From 887858c9aea0e735caf99c524e5eb2aa7c3de08b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Timo=20R=C3=B6hling?= Date: Sat, 4 May 2024 20:26:53 +0200 Subject: [PATCH] New upstream version 1.9.25+dfsg3 --- third_party/civetweb/build.cmd | 1732 +- third_party/civetweb/mingw.cmd | 1768 +- third_party/glslang/.appveyor.yml | 105 + third_party/glslang/.clang-format | 13 + third_party/glslang/.gitattributes | 17 + third_party/glslang/.gitignore | 20 + third_party/glslang/.gn | 39 + third_party/glslang/.travis.yml | 128 + third_party/glslang/Android.mk | 165 + third_party/glslang/BUILD.bazel | 293 + third_party/glslang/BUILD.gn | 324 + third_party/glslang/CHANGES.md | 34 + third_party/glslang/CMakeLists.txt | 371 + third_party/glslang/CODE_OF_CONDUCT.md | 1 + third_party/glslang/ChooseMSVCCRT.cmake | 138 + third_party/glslang/DEPS | 82 + third_party/glslang/External/CMakeLists.txt | 77 + third_party/glslang/LICENSE | 75 + third_party/glslang/LICENSE.txt | 384 + .../glslang/OGLCompilersDLL/CMakeLists.txt | 48 + .../glslang/OGLCompilersDLL/InitializeDll.cpp | 165 + .../glslang/OGLCompilersDLL/InitializeDll.h | 49 + .../OGLCompilersDLL/tnt/CMakeLists.txt | 14 + third_party/glslang/README-spirv-remap.txt | 137 + third_party/glslang/README.md | 470 + .../SPIRV/CInterface/spirv_c_interface.cpp | 110 + third_party/glslang/SPIRV/CMakeLists.txt | 138 + third_party/glslang/SPIRV/GLSL.ext.AMD.h | 108 + third_party/glslang/SPIRV/GLSL.ext.EXT.h | 41 + third_party/glslang/SPIRV/GLSL.ext.KHR.h | 54 + third_party/glslang/SPIRV/GLSL.ext.NV.h | 81 + third_party/glslang/SPIRV/GLSL.std.450.h | 131 + third_party/glslang/SPIRV/GlslangToSpv.cpp | 8824 ++++++ third_party/glslang/SPIRV/GlslangToSpv.h | 61 + third_party/glslang/SPIRV/InReadableOrder.cpp | 131 + third_party/glslang/SPIRV/Logger.cpp | 72 + third_party/glslang/SPIRV/Logger.h | 83 + .../glslang/SPIRV/NonSemanticDebugPrintf.h | 50 + third_party/glslang/SPIRV/SPVRemapper.cpp | 1498 + third_party/glslang/SPIRV/SPVRemapper.h | 304 + third_party/glslang/SPIRV/SpvBuilder.cpp | 3237 ++ third_party/glslang/SPIRV/SpvBuilder.h | 856 + third_party/glslang/SPIRV/SpvPostProcess.cpp | 450 + third_party/glslang/SPIRV/SpvTools.cpp | 241 + third_party/glslang/SPIRV/SpvTools.h | 93 + third_party/glslang/SPIRV/bitutils.h | 81 + third_party/glslang/SPIRV/disassemble.cpp | 746 + third_party/glslang/SPIRV/disassemble.h | 53 + third_party/glslang/SPIRV/doc.cpp | 2933 ++ third_party/glslang/SPIRV/doc.h | 259 + third_party/glslang/SPIRV/hex_float.h | 1078 + third_party/glslang/SPIRV/spirv.hpp | 2197 ++ third_party/glslang/SPIRV/spvIR.h | 508 + third_party/glslang/SPIRV/tnt/CMakeLists.txt | 76 + third_party/glslang/StandAlone/CMakeLists.txt | 106 + .../glslang/StandAlone/DirStackFileIncluder.h | 141 + .../glslang/StandAlone/ResourceLimits.cpp | 496 + .../glslang/StandAlone/ResourceLimits.h | 57 + third_party/glslang/StandAlone/StandAlone.cpp | 1771 ++ third_party/glslang/StandAlone/Worklist.h | 95 + .../glslang/StandAlone/resource_limits_c.cpp | 65 + .../glslang/StandAlone/resource_limits_c.h | 54 + .../glslang/StandAlone/spirv-remap.cpp | 341 + third_party/glslang/WORKSPACE | 27 + third_party/glslang/_config.yml | 1 + third_party/glslang/build_info.h.tmpl | 62 + third_party/glslang/build_info.py | 226 + third_party/glslang/build_overrides/build.gni | 39 + .../glslang/build_overrides/glslang.gni | 37 + .../glslang/build_overrides/spirv_tools.gni | 38 + .../CInterface/glslang_c_interface.cpp | 428 + third_party/glslang/glslang/CMakeLists.txt | 226 + .../glslang/GenericCodeGen/CodeGen.cpp | 76 + .../glslang/glslang/GenericCodeGen/Link.cpp | 91 + .../glslang/glslang/HLSL/hlslAttributes.cpp | 149 + .../glslang/glslang/HLSL/hlslAttributes.h | 59 + .../glslang/glslang/HLSL/hlslGrammar.cpp | 4190 +++ .../glslang/glslang/HLSL/hlslGrammar.h | 142 + .../glslang/glslang/HLSL/hlslOpMap.cpp | 173 + third_party/glslang/glslang/HLSL/hlslOpMap.h | 69 + .../glslang/glslang/HLSL/hlslParseHelper.cpp | 10165 +++++++ .../glslang/glslang/HLSL/hlslParseHelper.h | 514 + .../glslang/glslang/HLSL/hlslParseables.cpp | 1259 + .../glslang/glslang/HLSL/hlslParseables.h | 64 + .../glslang/glslang/HLSL/hlslScanContext.cpp | 903 + .../glslang/glslang/HLSL/hlslScanContext.h | 109 + .../glslang/glslang/HLSL/hlslTokenStream.cpp | 150 + .../glslang/glslang/HLSL/hlslTokenStream.h | 96 + third_party/glslang/glslang/HLSL/hlslTokens.h | 374 + third_party/glslang/glslang/HLSL/pch.h | 53 + .../glslang/glslang/Include/BaseTypes.h | 577 + third_party/glslang/glslang/Include/Common.h | 291 + .../glslang/glslang/Include/ConstantUnion.h | 974 + .../glslang/glslang/Include/InfoSink.h | 144 + .../glslang/Include/InitializeGlobals.h | 44 + .../glslang/glslang/Include/PoolAlloc.h | 316 + .../glslang/glslang/Include/ResourceLimits.h | 150 + .../glslang/glslang/Include/ShHandle.h | 176 + third_party/glslang/glslang/Include/Types.h | 2492 ++ third_party/glslang/glslang/Include/arrays.h | 341 + .../glslang/Include/glslang_c_interface.h | 249 + .../glslang/Include/glslang_c_shader_types.h | 185 + .../glslang/glslang/Include/intermediate.h | 1809 ++ .../glslang/MachineIndependent/Constant.cpp | 1428 + .../glslang/MachineIndependent/InfoSink.cpp | 113 + .../glslang/MachineIndependent/Initialize.cpp | 9425 ++++++ .../glslang/MachineIndependent/Initialize.h | 112 + .../MachineIndependent/IntermTraverse.cpp | 309 + .../MachineIndependent/Intermediate.cpp | 3986 +++ .../MachineIndependent/LiveTraverser.h | 168 + .../MachineIndependent/ParseContextBase.cpp | 663 + .../MachineIndependent/ParseHelper.cpp | 8676 ++++++ .../glslang/MachineIndependent/ParseHelper.h | 535 + .../glslang/MachineIndependent/PoolAlloc.cpp | 315 + .../glslang/MachineIndependent/RemoveTree.cpp | 118 + .../glslang/MachineIndependent/RemoveTree.h | 41 + .../glslang/MachineIndependent/Scan.cpp | 1917 ++ .../glslang/glslang/MachineIndependent/Scan.h | 276 + .../glslang/MachineIndependent/ScanContext.h | 93 + .../glslang/MachineIndependent/ShaderLang.cpp | 2146 ++ .../MachineIndependent/SymbolTable.cpp | 450 + .../glslang/MachineIndependent/SymbolTable.h | 899 + .../glslang/MachineIndependent/Versions.cpp | 1296 + .../glslang/MachineIndependent/Versions.h | 337 + .../glslang/MachineIndependent/attribute.cpp | 346 + .../glslang/MachineIndependent/attribute.h | 149 + .../glslang/MachineIndependent/gl_types.h | 218 + .../glslang/MachineIndependent/glslang.m4 | 4033 +++ .../glslang/MachineIndependent/glslang.y | 4033 +++ .../MachineIndependent/glslang_tab.cpp | 11188 +++++++ .../MachineIndependent/glslang_tab.cpp.h | 553 + .../glslang/MachineIndependent/intermOut.cpp | 1569 + .../glslang/MachineIndependent/iomapper.cpp | 1601 + .../glslang/MachineIndependent/iomapper.h | 305 + .../glslang/MachineIndependent/limits.cpp | 200 + .../MachineIndependent/linkValidate.cpp | 1781 ++ .../MachineIndependent/localintermediate.h | 1071 + .../glslang/MachineIndependent/parseConst.cpp | 214 + .../MachineIndependent/parseVersions.h | 245 + .../glslang/glslang/MachineIndependent/pch.h | 49 + .../MachineIndependent/preprocessor/Pp.cpp | 1346 + .../preprocessor/PpAtom.cpp | 181 + .../preprocessor/PpContext.cpp | 120 + .../preprocessor/PpContext.h | 703 + .../preprocessor/PpScanner.cpp | 1315 + .../preprocessor/PpTokens.cpp | 221 + .../preprocessor/PpTokens.h | 179 + .../propagateNoContraction.cpp | 870 + .../propagateNoContraction.h | 55 + .../glslang/MachineIndependent/reflection.cpp | 1272 + .../glslang/MachineIndependent/reflection.h | 223 + .../glslang/OSDependent/Unix/CMakeLists.txt | 59 + .../glslang/OSDependent/Unix/ossource.cpp | 207 + .../glslang/OSDependent/Web/CMakeLists.txt | 71 + .../glslang/OSDependent/Web/glslang.after.js | 26 + .../glslang/OSDependent/Web/glslang.js.cpp | 287 + .../glslang/OSDependent/Web/glslang.pre.js | 56 + .../OSDependent/Windows/CMakeLists.txt | 54 + .../glslang/OSDependent/Windows/main.cpp | 74 + .../glslang/OSDependent/Windows/ossource.cpp | 147 + .../glslang/glslang/OSDependent/osinclude.h | 63 + .../glslang/glslang/Public/ShaderLang.h | 948 + .../glslang/glslang/tnt/CMakeLists.txt | 117 + third_party/glslang/glslang/updateGrammar | 49 + third_party/glslang/gtests/AST.FromFile.cpp | 298 + .../gtests/BuiltInResource.FromFile.cpp | 57 + third_party/glslang/gtests/CMakeLists.txt | 108 + .../glslang/gtests/Config.FromFile.cpp | 108 + third_party/glslang/gtests/HexFloat.cpp | 1231 + third_party/glslang/gtests/Hlsl.FromFile.cpp | 511 + third_party/glslang/gtests/Initializer.h | 55 + .../glslang/gtests/Link.FromFile.Vk.cpp | 132 + third_party/glslang/gtests/Link.FromFile.cpp | 114 + third_party/glslang/gtests/Pp.FromFile.cpp | 76 + third_party/glslang/gtests/README.md | 26 + third_party/glslang/gtests/Remap.FromFile.cpp | 118 + third_party/glslang/gtests/Settings.cpp | 51 + third_party/glslang/gtests/Settings.h | 58 + third_party/glslang/gtests/Spv.FromFile.cpp | 737 + third_party/glslang/gtests/TestFixture.cpp | 180 + third_party/glslang/gtests/TestFixture.h | 715 + third_party/glslang/gtests/main.cpp | 79 + third_party/glslang/gtests/pch.h | 39 + third_party/glslang/hlsl/CMakeLists.txt | 59 + third_party/glslang/hlsl/stub.cpp | 41 + third_party/glslang/known_good.json | 18 + third_party/glslang/known_good_khr.json | 18 + .../kokoro/android-ndk-build/build-docker.sh | 48 + .../glslang/kokoro/android-ndk-build/build.sh | 47 + .../kokoro/android-ndk-build/continuous.cfg | 35 + .../kokoro/android-ndk-build/presubmit.cfg | 35 + .../kokoro/license-check/build-docker.sh | 39 + .../glslang/kokoro/license-check/build.sh | 47 + .../kokoro/license-check/continuous.cfg | 35 + .../kokoro/license-check/presubmit.cfg | 35 + .../kokoro/linux-clang-cmake/build-docker.sh | 50 + .../glslang/kokoro/linux-clang-cmake/build.sh | 48 + .../linux-clang-cmake/shared/continuous.cfg | 40 + .../linux-clang-cmake/shared/presubmit.cfg | 40 + .../linux-clang-cmake/static/continuous.cfg | 40 + .../linux-clang-cmake/static/presubmit.cfg | 40 + .../kokoro/linux-clang-gn/build-docker.sh | 55 + .../glslang/kokoro/linux-clang-gn/build.sh | 49 + .../kokoro/linux-clang-gn/continuous.cfg | 35 + .../kokoro/linux-clang-gn/presubmit.cfg | 35 + .../kokoro/linux-clang-release-bazel/build.sh | 60 + .../linux-clang-release-bazel/continuous.cfg | 35 + .../linux-clang-release-bazel/presubmit.cfg | 35 + .../kokoro/linux-gcc-cmake/build-docker.sh | 50 + .../glslang/kokoro/linux-gcc-cmake/build.sh | 48 + .../linux-gcc-cmake/shared/continuous.cfg | 40 + .../linux-gcc-cmake/shared/presubmit.cfg | 40 + .../linux-gcc-cmake/static/continuous.cfg | 40 + .../linux-gcc-cmake/static/presubmit.cfg | 40 + .../kokoro/macos-clang-release-bazel/build.sh | 60 + .../macos-clang-release-bazel/continuous.cfg | 35 + .../macos-clang-release-bazel/presubmit.cfg | 35 + .../windows-msvc-2015-release-bazel/build.bat | 75 + .../continuous.cfg | 35 + .../presubmit.cfg | 35 + third_party/glslang/license-checker.cfg | 51 + third_party/glslang/ndk_test/Android.mk | 45 + .../glslang/ndk_test/jni/Application.mk | 38 + third_party/glslang/ndk_test/test.cpp | 19 + third_party/glslang/standalone.gclient | 42 + third_party/glslang/tnt/CMakeLists.txt | 113 + third_party/glslang/tnt/README.md | 20 + third_party/glslang/update_glslang_sources.py | 155 + third_party/spirv-cross/.clang-format | 167 + third_party/spirv-cross/.gitignore | 20 + third_party/spirv-cross/.travis.yml | 72 + third_party/spirv-cross/CMakeLists.txt | 608 + third_party/spirv-cross/CODE_OF_CONDUCT.md | 1 + third_party/spirv-cross/GLSL.std.450.h | 131 + third_party/spirv-cross/LICENSE | 202 + third_party/spirv-cross/Makefile | 41 + third_party/spirv-cross/README.md | 515 + third_party/spirv-cross/appveyor.yml | 31 + .../spirv-cross/build_glslang_spirv_tools.sh | 26 + .../checkout_glslang_spirv_tools.sh | 55 + third_party/spirv-cross/cmake/gitversion.in.h | 6 + third_party/spirv-cross/format_all.sh | 8 + third_party/spirv-cross/gn/BUILD.gn | 63 + .../include/spirv_cross/barrier.hpp | 79 + .../include/spirv_cross/external_interface.h | 126 + .../spirv-cross/include/spirv_cross/image.hpp | 62 + .../spirv_cross/internal_interface.hpp | 603 + .../include/spirv_cross/sampler.hpp | 105 + .../include/spirv_cross/thread_group.hpp | 113 + third_party/spirv-cross/main.cpp | 1591 + .../pkg-config/spirv-cross-c-shared.pc.in | 13 + .../asm/comp/access-chain-invalidate.asm.comp | 19 + .../asm/comp/atomic-decrement.asm.comp | 22 + .../asm/comp/atomic-increment.asm.comp | 22 + .../asm/comp/bitcast_icmp.asm.comp | 28 + .../asm/comp/block-name-alias-global.asm.comp | 39 + .../asm/comp/control-flow-hints.asm.comp | 41 + .../comp/global-parameter-name-alias.asm.comp | 9 + .../asm/comp/nmin-max-clamp.asm.comp | 87 + .../asm/frag/cbuffer-stripped.asm.frag | 26 + .../asm/frag/combined-sampler-reuse.asm.frag | 30 + .../asm/frag/empty-struct.asm.frag | 8 + .../opt/shaders-hlsl/asm/frag/frem.asm.frag | 29 + .../asm/frag/function-overload-alias.asm.frag | 19 + .../asm/frag/image-extract-reuse.asm.frag | 31 + .../asm/frag/implicit-read-dep-phi.asm.frag | 57 + .../asm/frag/inf-nan-constant.asm.frag | 19 + .../asm/frag/line-directive.line.asm.frag | 86 + .../frag/lut-promotion-initializer.asm.frag | 57 + ...d-unpack-uint2.fxconly.nofxc.sm60.asm.frag | 33 + .../asm/frag/pass-by-value.asm.frag | 25 + .../asm/frag/sample-and-compare.asm.frag | 30 + .../frag/single-function-private-lut.asm.frag | 63 + .../opt/shaders-hlsl/asm/frag/srem.asm.frag | 29 + .../storage-class-output-initializer.asm.frag | 23 + .../asm/frag/texel-fetch-no-lod.asm.frag | 30 + .../asm/frag/texture-sampling-fp16.asm.frag | 29 + .../asm/frag/unknown-depth-state.asm.frag | 31 + .../asm/frag/unreachable.asm.frag | 40 + ...act-transposed-matrix-from-struct.asm.vert | 45 + .../vert/spec-constant-op-composite.asm.vert | 46 + .../vert/uint-vertex-id-instance-id.asm.vert | 28 + .../asm/vert/vertex-id-instance-id.asm.vert | 28 + .../comp/access-chain-load-composite.comp | 108 + .../opt/shaders-hlsl/comp/access-chains.comp | 23 + .../comp/access-chains.force-uav.comp | 23 + .../shaders-hlsl/comp/address-buffers.comp | 17 + .../opt/shaders-hlsl/comp/atomic.comp | 91 + .../opt/shaders-hlsl/comp/barriers.comp | 26 + .../opt/shaders-hlsl/comp/builtins.comp | 11 + .../comp/composite-array-initialization.comp | 50 + .../shaders-hlsl/comp/globallycoherent.comp | 18 + .../opt/shaders-hlsl/comp/image.comp | 66 + .../comp/image.nonwritable-uav-texture.comp | 66 + .../opt/shaders-hlsl/comp/inverse.comp | 124 + .../comp/num-workgroups-alone.comp | 19 + .../comp/num-workgroups-with-builtins.comp | 26 + .../opt/shaders-hlsl/comp/outer-product.comp | 50 + .../opt/shaders-hlsl/comp/rmw-matrix.comp | 22 + .../shaders-hlsl/comp/rwbuffer-matrix.comp | 93 + ...alar-std450-distance-length-normalize.comp | 16 + .../opt/shaders-hlsl/comp/shared.comp | 29 + .../comp/spec-constant-op-member-array.comp | 51 + .../comp/spec-constant-work-group-size.comp | 43 + .../shaders-hlsl/comp/ssbo-array-length.comp | 17 + .../opt/shaders-hlsl/comp/ssbo-array.comp | 11 + .../frag/array-lut-no-loop-variable.frag | 34 + .../frag/basic-color-3comp.sm30.frag | 26 + .../frag/basic-color-3comp.sm50.frag | 26 + .../opt/shaders-hlsl/frag/basic.frag | 32 + .../shaders-hlsl/frag/bit-conversions.frag | 26 + .../opt/shaders-hlsl/frag/boolean-mix.frag | 27 + .../opt/shaders-hlsl/frag/builtins.frag | 34 + .../shaders-hlsl/frag/bvec-operations.frag | 29 + .../shaders-hlsl/frag/clip-cull-distance.frag | 30 + .../combined-texture-sampler-parameter.frag | 24 + .../frag/combined-texture-sampler-shadow.frag | 23 + .../complex-expression-in-access-chain.frag | 38 + .../frag/constant-composites.frag | 38 + .../control-dependent-in-branch.desktop.frag | 55 + .../shaders-hlsl/frag/demote-to-helper.frag | 9 + .../shaders-hlsl/frag/depth-greater-than.frag | 19 + .../shaders-hlsl/frag/depth-less-than.frag | 19 + .../frag/dual-source-blending.frag | 23 + .../frag/early-fragment-test.frag | 9 + .../frag/for-loop-continue-control-flow.frag | 25 + .../opt/shaders-hlsl/frag/fp16-packing.frag | 44 + .../opt/shaders-hlsl/frag/front-facing.frag | 39 + .../frag/image-query-selective.frag | 8 + .../shaders-hlsl/frag/image-query-uav.frag | 8 + ...age-query-uav.nonwritable-uav-texture.frag | 8 + .../opt/shaders-hlsl/frag/image-query.frag | 8 + .../frag/input-attachment-ms.frag | 33 + .../shaders-hlsl/frag/input-attachment.frag | 30 + .../opt/shaders-hlsl/frag/io-block.frag | 28 + .../frag/legacy-tex-modifiers.sm30.frag | 32 + .../opt/shaders-hlsl/frag/lut-promotion.frag | 55 + .../opt/shaders-hlsl/frag/matrix-input.frag | 26 + .../reference/opt/shaders-hlsl/frag/mod.frag | 67 + .../reference/opt/shaders-hlsl/frag/mrt.frag | 31 + .../opt/shaders-hlsl/frag/no-return.frag | 8 + .../opt/shaders-hlsl/frag/no-return2.frag | 16 + ...orm-qualifier.nonuniformresource.sm51.frag | 49 + .../frag/partial-write-preserve.frag | 8 + .../pixel-interlock-ordered.sm51.fxconly.frag | 24 + .../shaders-hlsl/frag/point-coord-compat.frag | 19 + .../shaders-hlsl/frag/query-lod.desktop.frag | 31 + .../readonly-coherent-ssbo.force-uav.frag | 21 + .../frag/readonly-coherent-ssbo.frag | 21 + .../opt/shaders-hlsl/frag/resources.frag | 41 + .../frag/row-major-layout-in-struct.frag | 38 + .../frag/sample-cmp-level-zero.frag | 40 + .../frag/sample-mask-in-and-out.frag | 30 + .../opt/shaders-hlsl/frag/sample-mask-in.frag | 32 + .../shaders-hlsl/frag/sample-mask-out.frag | 23 + .../opt/shaders-hlsl/frag/sampler-array.frag | 31 + .../frag/sampler-image-arrays.frag | 39 + .../frag/scalar-refract-reflect.frag | 49 + .../separate-combined-fake-overload.sm30.frag | 22 + .../frag/spec-constant-block-size.frag | 37 + .../frag/spec-constant-ternary.frag | 26 + .../frag/switch-unsigned-case.frag | 38 + .../opt/shaders-hlsl/frag/swizzle-scalar.frag | 41 + .../shaders-hlsl/frag/tex-sampling-ms.frag | 34 + .../opt/shaders-hlsl/frag/tex-sampling.frag | 71 + .../shaders-hlsl/frag/tex-sampling.sm30.frag | 54 + .../shaders-hlsl/frag/texel-fetch-offset.frag | 32 + .../frag/texture-proj-shadow.frag | 51 + .../texture-size-combined-image-sampler.frag | 30 + .../opt/shaders-hlsl/frag/unary-enclose.frag | 29 + .../frag/unorm-snorm-packing.frag | 109 + .../shaders-hlsl/frag/various-glsl-ops.frag | 26 + .../opt/shaders-hlsl/vert/basic.vert | 39 + .../shaders-hlsl/vert/clip-cull-distance.vert | 28 + .../opt/shaders-hlsl/vert/instancing.vert | 28 + .../opt/shaders-hlsl/vert/locations.vert | 73 + .../shaders-hlsl/vert/matrix-attribute.vert | 35 + .../opt/shaders-hlsl/vert/matrix-output.vert | 23 + .../opt/shaders-hlsl/vert/no-input.vert | 18 + .../shaders-hlsl/vert/point-size-compat.vert | 20 + .../opt/shaders-hlsl/vert/qualifiers.vert | 50 + .../vert/read-from-row-major-array.vert | 36 + .../opt/shaders-hlsl/vert/return-array.vert | 29 + .../shaders-hlsl/vert/sampler-buffers.vert | 22 + .../vert/struct-composite-decl.vert | 44 + .../opt/shaders-hlsl/vert/texture_buffer.vert | 21 + .../amd/shader_trinary_minmax.msl21.comp | 11 + .../asm/comp/atomic-decrement.asm.comp | 27 + .../asm/comp/atomic-increment.asm.comp | 27 + .../asm/comp/bitcast_iadd.asm.comp | 29 + .../asm/comp/bitcast_icmp.asm.comp | 29 + .../shaders-msl/asm/comp/bitcast_sar.asm.comp | 31 + .../asm/comp/bitcast_sdiv.asm.comp | 31 + .../shaders-msl/asm/comp/bitcast_slr.asm.comp | 31 + .../asm/comp/block-name-alias-global.asm.comp | 46 + .../comp/buffer-write-relative-addr.asm.comp | 29 + .../asm/comp/buffer-write.asm.comp | 24 + .../comp/global-parameter-name-alias.asm.comp | 9 + .../image-load-store-short-vector.asm.comp | 10 + .../asm/comp/multiple-entry.asm.comp | 29 + .../shaders-msl/asm/comp/quantize.asm.comp | 21 + .../asm/comp/relaxed-block-layout.asm.comp | 22 + ...specialization-constant-workgroup.asm.comp | 21 + .../struct-resource-name-aliasing.asm.comp | 16 + .../asm/comp/variable-pointers-2.asm.comp | 51 + ...ariable-pointers-store-forwarding.asm.comp | 25 + .../vector-builtin-type-cast-func.asm.comp | 28 + .../comp/vector-builtin-type-cast.asm.comp | 28 + .../asm/frag/combined-sampler-reuse.asm.frag | 23 + .../asm/frag/default-member-names.asm.frag | 19 + .../frag/descriptor-array-unnamed.asm.frag | 48 + ...isable-renamed-output.frag-output.asm.frag | 35 + .../asm/frag/empty-struct.asm.frag | 9 + .../extract-packed-from-composite.asm.frag | 30 + .../opt/shaders-msl/asm/frag/frem.asm.frag | 23 + .../asm/frag/function-overload-alias.asm.frag | 17 + .../asm/frag/image-extract-reuse.asm.frag | 17 + .../asm/frag/implicit-read-dep-phi.asm.frag | 50 + .../asm/frag/inf-nan-constant.asm.frag | 17 + .../interpolation-qualifiers-struct.asm.frag | 47 + .../asm/frag/line-directive.line.asm.frag | 82 + .../asm/frag/locations-components.asm.frag | 35 + .../frag/lut-promotion-initializer.asm.frag | 94 + .../asm/frag/min-lod.msl22.asm.frag | 22 + .../asm/frag/op-constant-null.asm.frag | 17 + .../asm/frag/op-image-sampled-image.asm.frag | 22 + .../asm/frag/pass-by-value.asm.frag | 22 + .../asm/frag/phi-loop-variable.asm.frag | 9 + .../pull-model-interpolation.asm.msl23.frag | 181 + .../asm/frag/sample-and-compare.asm.frag | 22 + .../frag/single-function-private-lut.asm.frag | 79 + .../opt/shaders-msl/asm/frag/srem.asm.frag | 23 + .../storage-class-output-initializer.asm.frag | 65 + .../asm/frag/texel-fetch-no-lod.asm.frag | 17 + .../asm/frag/texture-atomics.asm.frag | 121 + ...re-atomics.asm.graphics-robust-access.frag | 122 + .../asm/frag/texture-sampling-fp16.asm.frag | 22 + .../asm/frag/undef-variable-store.asm.frag | 17 + .../asm/frag/unknown-depth-state.asm.frag | 22 + .../asm/frag/unord-relational-op.asm.frag | 22 + .../shaders-msl/asm/frag/unreachable.asm.frag | 36 + .../asm/frag/vector-shuffle-oom.asm.frag | 282 + .../asm/tesc/tess-level-overrun.asm.tesc | 23 + .../tess-level-overrun.multi-patch.asm.tesc | 24 + .../asm/tese/unnamed-builtin-array.asm.tese | 23 + .../clip-distance-plain-variable.asm.vert | 29 + ...ce-plain-variable.no-user-varying.asm.vert | 25 + .../asm/vert/copy-memory-interface.asm.vert | 25 + ...act-transposed-matrix-from-struct.asm.vert | 35 + .../asm/vert/fake-builtin-input.asm.vert | 22 + .../asm/vert/invariant.msl21.asm.vert | 17 + .../asm/vert/packed-bool-to-uint.asm.vert | 38 + .../packed-bool2-to-packed_uint2.asm.vert | 38 + .../asm/vert/packing-test.asm.vert | 9 + .../vert/spec-constant-op-composite.asm.vert | 35 + .../vert/uint-vertex-id-instance-id.asm.vert | 17 + .../access-private-workgroup-in-function.comp | 11 + ...ffers-discrete.msl2.argument.discrete.comp | 42 + ...rs-image-load-store.ios.msl2.argument.comp | 11 + ...uffers-image-load-store.msl2.argument.comp | 17 + .../opt/shaders-msl/comp/array-length.comp | 31 + .../array-length.msl2.argument.discrete.comp | 56 + .../opt/shaders-msl/comp/atomic.comp | 72 + .../opt/shaders-msl/comp/barriers.comp | 22 + .../reference/opt/shaders-msl/comp/basic.comp | 35 + .../shaders-msl/comp/basic.dispatchbase.comp | 38 + .../comp/basic.dispatchbase.msl11.comp | 36 + .../comp/basic.inline-block.msl2.comp | 53 + .../opt/shaders-msl/comp/builtins.comp | 11 + .../comp/cfg-preserve-parameter.comp | 9 + .../opt/shaders-msl/comp/coherent-block.comp | 17 + .../opt/shaders-msl/comp/coherent-image.comp | 17 + .../complex-composite-constant-array.comp | 59 + .../shaders-msl/comp/complex-type-alias.comp | 56 + .../comp/composite-array-initialization.comp | 79 + ...ray-initialization.force-native-array.comp | 148 + .../shaders-msl/comp/composite-construct.comp | 67 + .../comp/copy-array-of-arrays.comp | 20 + ...py-array-of-arrays.force-native-array.comp | 20 + .../opt/shaders-msl/comp/culling.comp | 35 + .../opt/shaders-msl/comp/defer-parens.comp | 24 + .../opt/shaders-msl/comp/dowhile.comp | 42 + .../comp/force-recompile-hooks.swizzle.comp | 73 + .../opt/shaders-msl/comp/functions.comp | 9 + ...vocation-id-writable-ssbo-in-function.comp | 26 + .../comp/global-invocation-id.comp | 26 + ...omic-automatic-bindings.argument.msl2.comp | 37 + .../comp/image-atomic-automatic-bindings.comp | 28 + .../comp/image-cube-array-load-store.comp | 12 + .../reference/opt/shaders-msl/comp/image.comp | 13 + .../opt/shaders-msl/comp/insert.comp | 28 + .../opt/shaders-msl/comp/inverse.comp | 130 + .../shaders-msl/comp/local-invocation-id.comp | 26 + .../comp/local-invocation-index.comp | 26 + .../reference/opt/shaders-msl/comp/mat3.comp | 17 + .../reference/opt/shaders-msl/comp/mod.comp | 33 + .../reference/opt/shaders-msl/comp/modf.comp | 24 + .../opt/shaders-msl/comp/outer-product.comp | 40 + .../opt/shaders-msl/comp/packing-test-1.comp | 28 + .../opt/shaders-msl/comp/packing-test-2.comp | 28 + .../opt/shaders-msl/comp/read-write-only.comp | 31 + .../opt/shaders-msl/comp/rmw-matrix.comp | 24 + .../opt/shaders-msl/comp/rmw-opt.comp | 27 + ...alar-std450-distance-length-normalize.comp | 25 + .../comp/shared-array-of-arrays.comp | 20 + .../opt/shaders-msl/comp/shared.comp | 25 + .../comp/spec-constant-op-member-array.comp | 49 + .../comp/spec-constant-work-group-size.comp | 75 + .../storage-buffer-std140-vector-array.comp | 91 + .../opt/shaders-msl/comp/struct-layout.comp | 27 + .../opt/shaders-msl/comp/struct-nested.comp | 27 + .../opt/shaders-msl/comp/struct-packing.comp | 152 + .../opt/shaders-msl/comp/torture-loop.comp | 44 + .../opt/shaders-msl/comp/type-alias.comp | 37 + .../reference/opt/shaders-msl/comp/udiv.comp | 22 + .../opt/shaders-msl/comp/writable-ssbo.comp | 26 + .../comp/extended-arithmetic.desktop.comp | 179 + .../desktop-only/frag/image-ms.desktop.frag | 11 + .../frag/query-levels.desktop.frag | 17 + .../frag/sampler-ms-query.desktop.frag | 17 + .../tesc/arrayed-output.desktop.sso.tesc | 85 + .../tesc/basic.desktop.sso.multi-patch.tesc | 39 + .../desktop-only/tesc/basic.desktop.sso.tesc | 39 + .../struct-copy.desktop.sso.multi-patch.tesc | 38 + .../tesc/struct-copy.desktop.sso.tesc | 40 + .../tese/triangle.desktop.sso.tese | 27 + .../desktop-only/vert/basic.desktop.sso.vert | 30 + ...ull-distance..no-user-varying.desktop.vert | 20 + .../vert/clip-cull-distance.desktop.vert | 24 + ...ader-draw-parameters.desktop.for-tess.vert | 20 + .../vert/shader-draw-parameters.desktop.vert | 17 + .../shaders-msl/flatten/basic.flatten.vert | 30 + .../flatten/multiindex.flatten.vert | 27 + .../flatten/push-constant.flatten.vert | 32 + .../shaders-msl/flatten/rowmajor.flatten.vert | 29 + .../shaders-msl/flatten/struct.flatten.vert | 40 + .../shaders-msl/flatten/swizzle.flatten.vert | 47 + .../shaders-msl/flatten/types.flatten.frag | 35 + .../frag/argument-buffers.msl2.argument.frag | 73 + .../frag/array-lut-no-loop-variable.frag | 66 + .../shaders-msl/frag/array-of-array-lut.frag | 68 + ...niform.msl2.argument.discrete.swizzle.frag | 105 + ...zzle-nonconstant-uniform.msl2.swizzle.frag | 96 + ...wizzle.msl2.argument.discrete.swizzle.frag | 97 + ...array-of-texture-swizzle.msl2.swizzle.frag | 86 + .../frag/barycentric-nv-nopersp.msl22.frag | 28 + .../frag/barycentric-nv.msl22.frag | 28 + .../reference/opt/shaders-msl/frag/basic.frag | 23 + .../binary-func-unpack-pack-arguments.frag | 28 + .../frag/binary-unpack-pack-arguments.frag | 28 + .../shaders-msl/frag/bitcasting.1d-as-2d.frag | 26 + .../opt/shaders-msl/frag/bitcasting.frag | 26 + .../shaders-msl/frag/buffer-read-write.frag | 27 + ...ead-write.texture-buffer-native.msl21.frag | 18 + .../opt/shaders-msl/frag/builtins.frag | 24 + .../frag/clip-distance-varying.frag | 67 + .../complex-expression-in-access-chain.frag | 29 + .../composite-extract-forced-temporary.frag | 24 + .../opt/shaders-msl/frag/constant-array.frag | 76 + .../shaders-msl/frag/constant-composites.frag | 74 + .../control-dependent-in-branch.desktop.frag | 45 + .../shaders-msl/frag/depth-greater-than.frag | 17 + .../opt/shaders-msl/frag/depth-less-than.frag | 17 + .../frag/disable-frag-output.frag-output.frag | 35 + .../frag/dual-source-blending.frag | 19 + .../frag/early-fragment-tests.frag | 17 + .../opt/shaders-msl/frag/false-loop-init.frag | 28 + .../opt/shaders-msl/frag/flush_params.frag | 17 + .../frag/for-loop-continue-control-flow.frag | 23 + .../opt/shaders-msl/frag/for-loop-init.frag | 71 + .../opt/shaders-msl/frag/fp16-packing.frag | 25 + ...agment-component-padding.pad-fragment.frag | 76 + .../opt/shaders-msl/frag/front-facing.frag | 30 + .../opt/shaders-msl/frag/gather-dref.frag | 22 + .../opt/shaders-msl/frag/gather-offset.frag | 17 + .../frag/helper-invocation.msl21.frag | 32 + ....device-argument-buffer.argument.msl2.frag | 44 + .../shaders-msl/frag/illegal-name-test-0.frag | 17 + .../frag/image-query-lod.msl22.frag | 70 + .../opt/shaders-msl/frag/in_block.frag | 32 + .../opt/shaders-msl/frag/in_mat.frag | 37 + ...t-attachment-ms.arrayed-subpass.msl21.frag | 17 + .../shaders-msl/frag/input-attachment-ms.frag | 17 + .../input-attachment-ms.multiview.msl21.frag | 18 + .../input-attachment.arrayed-subpass.frag | 17 + .../shaders-msl/frag/input-attachment.frag | 17 + .../frag/input-attachment.multiview.frag | 18 + .../frag/interpolation-qualifiers-block.frag | 47 + .../frag/interpolation-qualifiers.frag | 28 + .../opt/shaders-msl/frag/lut-promotion.frag | 92 + .../reference/opt/shaders-msl/frag/mix.frag | 28 + .../opt/shaders-msl/frag/mrt-array.frag | 82 + .../frag/nonuniform-qualifier.msl2.frag | 53 + .../packed-expression-vector-shuffle.frag | 23 + .../opt/shaders-msl/frag/packing-test-3.frag | 28 + ...pixel-interlock-ordered.msl2.argument.frag | 53 + .../frag/pixel-interlock-ordered.msl2.frag | 41 + .../reference/opt/shaders-msl/frag/pls.frag | 31 + .../frag/post-depth-coverage.ios.msl2.frag | 17 + .../frag/post-depth-coverage.msl23.frag | 17 + ...rivate-variable-prototype-declaration.frag | 17 + .../opt/shaders-msl/frag/readonly-ssbo.frag | 22 + ...e-depth-propagate-state-from-resource.frag | 23 + .../sample-depth-separate-image-sampler.frag | 17 + ...ple-mask-in-and-out.fixed-sample-mask.frag | 20 + ...ample-mask-not-used.fixed-sample-mask.frag | 19 + .../frag/sample-mask.fixed-sample-mask.frag | 20 + .../opt/shaders-msl/frag/sample-mask.frag | 19 + .../frag/sample-position-func.frag | 23 + .../opt/shaders-msl/frag/sample-position.frag | 18 + .../frag/sampler-1d-lod.1d-as-2d.frag | 22 + .../opt/shaders-msl/frag/sampler-1d-lod.frag | 22 + .../sampler-compare-bias.msl23.1d-as-2d.frag | 22 + .../sampler-compare-cascade-gradient.frag | 22 + .../sampler-compare-cascade-gradient.ios.frag | 22 + ...ampler-compare-cascade-gradient.msl23.frag | 22 + .../frag/sampler-image-arrays.msl2.frag | 28 + .../opt/shaders-msl/frag/sampler-ms.frag | 18 + .../opt/shaders-msl/frag/sampler.frag | 23 + .../frag/scalar-refract-reflect.frag | 49 + .../frag/separate-image-sampler-argument.frag | 17 + .../frag/shader-arithmetic-8bit.frag | 53 + .../frag/spec-constant-block-size.frag | 32 + .../frag/spec-constant-ternary.frag | 22 + .../frag/stencil-export.msl21.frag | 22 + .../frag/subgroup-builtins.msl22.frag | 18 + .../frag/switch-unsigned-case.frag | 35 + .../opt/shaders-msl/frag/swizzle.frag | 30 + .../frag/texel-fetch-offset.1d-as-2d.frag | 18 + .../shaders-msl/frag/texel-fetch-offset.frag | 19 + .../shaders-msl/frag/texture-cube-array.frag | 22 + ...ure-cube-array.ios.emulate-cube-array.frag | 58 + .../frag/texture-multisample-array.msl21.frag | 23 + .../shaders-msl/frag/texture-proj-shadow.frag | 29 + .../opt/shaders-msl/frag/ubo_layout.frag | 37 + .../opt/shaders-msl/frag/unary-enclose.frag | 22 + .../frag/vecsize-mismatch.shader-inputs.frag | 75 + .../frag/write-depth-in-function.frag | 19 + .../intel/shader-integer-functions2.asm.comp | 31 + .../legacy/vert/transpose.legacy.vert | 29 + .../shaders-msl/tesc/basic.multi-patch.tesc | 23 + .../reference/opt/shaders-msl/tesc/basic.tesc | 22 + ...rol-point-array-of-matrix.multi-patch.tesc | 68 + .../load-control-point-array-of-matrix.tesc | 73 + ...rol-point-array-of-struct.multi-patch.tesc | 78 + .../load-control-point-array-of-struct.tesc | 84 + .../load-control-point-array.multi-patch.tesc | 69 + .../tesc/load-control-point-array.tesc | 70 + .../tesc/matrix-output.multi-patch.tesc | 41 + .../tesc/reload-tess-level.multi-patch.tesc | 35 + .../shaders-msl/tesc/reload-tess-level.tesc | 35 + .../tesc/struct-output.multi-patch.tesc | 48 + .../tesc/water_tess.multi-patch.tesc | 91 + .../opt/shaders-msl/tesc/water_tess.tesc | 93 + .../opt/shaders-msl/tese/input-array.tese | 28 + .../opt/shaders-msl/tese/input-types.tese | 76 + .../load-control-point-array-of-matrix.tese | 85 + .../tese/load-control-point-array.tese | 78 + .../opt/shaders-msl/tese/quad.domain.tese | 24 + .../reference/opt/shaders-msl/tese/quad.tese | 23 + .../shaders-msl/tese/set-from-function.tese | 55 + .../shaders-msl/tese/triangle-tess-level.tese | 69 + .../opt/shaders-msl/tese/triangle.tese | 17 + .../opt/shaders-msl/tese/water_tess.tese | 45 + .../opt/shaders-msl/vert/basic.capture.vert | 29 + .../opt/shaders-msl/vert/basic.for-tess.vert | 31 + .../reference/opt/shaders-msl/vert/basic.vert | 30 + .../clip-distance-block.no-user-varying.vert | 25 + .../shaders-msl/vert/clip-distance-block.vert | 29 + .../opt/shaders-msl/vert/copy.flatten.vert | 45 + .../opt/shaders-msl/vert/dynamic.flatten.vert | 45 + .../vert/float-math.invariant-float-math.vert | 137 + .../opt/shaders-msl/vert/float-math.vert | 88 + .../opt/shaders-msl/vert/functions.vert | 122 + .../shaders-msl/vert/in_out_array_mat.vert | 108 + .../interface-block-block-composites.frag | 99 + .../interface-block-block-composites.vert | 105 + .../vert/interpolation-qualifiers-block.vert | 55 + .../vert/interpolation-qualifiers.vert | 36 + .../opt/shaders-msl/vert/invariant.msl21.vert | 26 + .../vert/leaf-function.capture.vert | 29 + .../vert/leaf-function.for-tess.vert | 31 + .../no-disable-vertex-out.frag-output.vert | 28 + .../vert/no_stage_out.for-tess.vert | 23 + .../opt/shaders-msl/vert/no_stage_out.vert | 20 + .../vert/no_stage_out.write_buff.vert | 37 + .../vert/no_stage_out.write_buff_atomic.vert | 30 + .../vert/no_stage_out.write_tex.vert | 27 + .../opt/shaders-msl/vert/out_block.vert | 41 + .../shaders-msl/vert/packed-bool-to-uint.vert | 38 + .../vert/packed-bool2-to-packed_uint2.vert | 38 + .../opt/shaders-msl/vert/packed_matrix.vert | 48 + .../opt/shaders-msl/vert/pointsize.vert | 33 + .../vert/read-from-row-major-array.vert | 29 + .../vert/resource-arrays-leaf.ios.vert | 42 + .../shaders-msl/vert/resource-arrays.ios.vert | 42 + .../vert/return-array.force-native-array.vert | 22 + .../opt/shaders-msl/vert/return-array.vert | 22 + .../shaders-msl/vert/set_builtin_in_func.vert | 19 + .../opt/shaders-msl/vert/sign-int-types.vert | 60 + .../signedness-mismatch.shader-inputs.vert | 74 + ...re_buffer.texture-buffer-native.msl21.vert | 17 + .../opt/shaders-msl/vert/texture_buffer.vert | 26 + .../opt/shaders-msl/vert/ubo.alignment.vert | 38 + .../reference/opt/shaders-msl/vert/ubo.vert | 30 + ...asic.multiview.no-layered.nocompat.vk.frag | 73 + .../frag/basic.multiview.nocompat.vk.frag | 73 + ...lper-forwarding.asm.vk.nocompat.msl23.frag | 22 + .../demote-to-helper.vk.nocompat.msl23.frag | 11 + ...emote-to-helper.vk.nocompat.msl23.ios.frag | 11 + .../vulkan/frag/push-constant.vk.frag | 28 + .../vulkan/frag/spec-constant.msl11.vk.frag | 26 + .../vulkan/frag/spec-constant.vk.frag | 22 + ...oup.multiview.viewfromdev.nocompat.vk.vert | 19 + .../vulkan/vert/device-group.nocompat.vk.vert | 18 + ...view.multiview.no-layered.nocompat.vk.vert | 28 + .../vert/multiview.multiview.nocompat.vk.vert | 31 + .../vulkan/vert/multiview.nocompat.vk.vert | 29 + .../vulkan/vert/small-storage.vk.vert | 48 + .../vulkan/vert/vulkan-vertex.vk.vert | 17 + .../asm/frag/depth-compare.asm.frag | 314 + .../asm/frag/global-constant-arrays.asm.frag | 1141 + .../padded-float-array-member-defef.asm.frag | 1190 + .../asm/frag/sample-mask-not-array.asm.frag | 503 + ...bpass-input.ios.framebuffer-fetch.asm.frag | 213 + ...ass-input.msl23.framebuffer-fetch.asm.frag | 213 + .../texture-atomics.asm.argument.msl2.frag | 84 + .../asm/frag/texture-atomics.asm.frag | 76 + ...re-atomics.asm.graphics-robust-access.frag | 77 + .../asm/tesc/hs-incorrect-base-type.asm.tesc | 399 + .../asm/tesc/hs-input-array-access.asm.tesc | 467 + .../asm/tesc/hs-texcoord-array.asm.tesc | 411 + .../tess-factor-must-be-threadgroup.asm.tesc | 178 + .../asm/tese/ds-double-gl-in-deref.asm.tese | 419 + .../asm/tese/ds-patch-input-fixes.asm.tese | 417 + .../asm/tese/ds-patch-inputs.asm.tese | 216 + .../asm/tese/ds-texcoord-array.asm.tese | 318 + .../asm/vert/array-missing-copies.asm.vert | 457 + .../asm/vert/texture-buffer.asm.vert | 387 + .../reference/opt/shaders/amd/gcn_shader.comp | 9 + .../opt/shaders/amd/shader_ballot.comp | 27 + .../opt/shaders/amd/shader_group_vote.comp | 14 + .../shaders/amd/shader_trinary_minmax.comp | 8 + .../asm/comp/atomic-decrement.asm.comp | 16 + .../asm/comp/atomic-increment.asm.comp | 16 + .../shaders/asm/comp/bitcast_iadd.asm.comp | 27 + .../shaders/asm/comp/bitcast_icmp.asm.comp | 27 + .../shaders/asm/comp/bitcast_iequal.asm.comp | 33 + .../opt/shaders/asm/comp/bitcast_sar.asm.comp | 29 + .../shaders/asm/comp/bitcast_sdiv.asm.comp | 29 + .../opt/shaders/asm/comp/bitcast_slr.asm.comp | 29 + .../asm/comp/block-name-alias-global.asm.comp | 37 + .../asm/comp/builtin-compute-bitcast.asm.comp | 13 + .../asm/comp/decoration-group.asm.comp | 38 + .../comp/global-parameter-name-alias.asm.comp | 7 + .../asm/comp/hlsl-functionality.asm.comp | 19 + .../opt/shaders/asm/comp/logical.asm.comp | 7 + .../shaders/asm/comp/multiple-entry.asm.comp | 27 + .../shaders/asm/comp/nmin-max-clamp.asm.comp | 47 + .../opt/shaders/asm/comp/op-phi-swap.asm.comp | 39 + .../opt/shaders/asm/comp/quantize.asm.comp | 19 + .../asm/comp/recompile-block-naming.asm.comp | 35 + ...specialization-constant-workgroup.asm.comp | 21 + .../asm/comp/switch-break-ladder.asm.comp | 71 + .../frag/combined-sampler-reuse.vk.asm.frag | 13 + .../combined-sampler-reuse.vk.asm.frag.vk | 14 + .../frag/complex-name-workarounds.asm.frag | 15 + ...osite-construct-struct-no-swizzle.asm.frag | 12 + .../asm/frag/default-member-names.asm.frag | 11 + .../frag/do-while-statement-fallback.asm.frag | 9 + .../shaders/asm/frag/empty-struct.asm.frag | 6 + .../frag/for-loop-phi-only-continue.asm.frag | 17 + .../opt/shaders/asm/frag/frem.asm.frag | 13 + .../asm/frag/function-overload-alias.asm.frag | 11 + .../hlsl-sample-cmp-level-zero-cube.asm.frag | 11 + .../frag/hlsl-sample-cmp-level-zero.asm.frag | 14 + .../asm/frag/image-extract-reuse.asm.frag | 11 + .../frag/image-fetch-no-sampler.asm.vk.frag | 13 + .../image-fetch-no-sampler.asm.vk.frag.vk | 14 + ...etch-no-sampler.no-samplerless.asm.vk.frag | 13 + ...h-no-sampler.no-samplerless.asm.vk.frag.vk | 14 + ...uery-no-sampler.no-samplerless.vk.asm.frag | 6 + ...y-no-sampler.no-samplerless.vk.asm.frag.vk | 6 + .../frag/image-query-no-sampler.vk.asm.frag | 6 + .../image-query-no-sampler.vk.asm.frag.vk | 6 + .../asm/frag/implicit-read-dep-phi.asm.frag | 40 + .../asm/frag/inf-nan-constant-double.asm.frag | 11 + .../asm/frag/inf-nan-constant.asm.frag | 11 + .../shaders/asm/frag/invalidation.asm.frag | 11 + .../asm/frag/line-directive.line.asm.frag | 71 + .../asm/frag/locations-components.asm.frag | 23 + ...op-body-dominator-continue-access.asm.frag | 70 + .../asm/frag/loop-header-to-continue.asm.frag | 45 + .../frag/lut-promotion-initializer.asm.frag | 42 + .../asm/frag/multi-for-loop-init.asm.frag | 22 + .../asm/frag/op-constant-null.asm.frag | 11 + .../frag/op-phi-swap-continue-block.asm.frag | 29 + .../asm/frag/pack-and-unpack-uint2.asm.frag | 11 + .../shaders/asm/frag/pass-by-value.asm.frag | 16 + .../asm/frag/phi-loop-variable.asm.frag | 6 + .../asm/frag/sample-and-compare.asm.frag | 13 + ...pler-buffer-array-without-sampler.asm.frag | 18 + .../sampler-buffer-without-sampler.asm.frag | 13 + .../frag/single-function-private-lut.asm.frag | 23 + .../opt/shaders/asm/frag/srem.asm.frag | 13 + .../storage-class-output-initializer.asm.frag | 11 + .../struct-composite-extract-swizzle.asm.frag | 21 + .../frag/switch-label-shared-block.asm.frag | 32 + .../asm/frag/temporary-name-alias.asm.frag | 6 + .../asm/frag/temporary-phi-hoisting.asm.frag | 27 + .../asm/frag/texel-fetch-no-lod.asm.frag | 11 + .../frag/texture-sampling-fp16.asm.vk.frag | 19 + .../frag/texture-sampling-fp16.asm.vk.frag.vk | 20 + .../asm/frag/undef-variable-store.asm.frag | 9 + .../asm/frag/unknown-depth-state.asm.vk.frag | 13 + .../frag/unknown-depth-state.asm.vk.frag.vk | 14 + .../opt/shaders/asm/frag/unreachable.asm.frag | 24 + .../asm/frag/vector-shuffle-oom.asm.frag | 277 + .../asm/geom/block-name-namespace.asm.geom | 32 + .../inout-split-access-chain-handle.asm.geom | 9 + .../geom/split-access-chain-input.asm.geom | 9 + .../asm/geom/unroll-glposition-load.asm.geom | 22 + .../asm/tese/unroll-input-array-load.asm.tese | 17 + .../opt/shaders/asm/vert/empty-io.asm.vert | 14 + ...act-transposed-matrix-from-struct.asm.vert | 30 + .../asm/vert/global-builtin.sso.asm.vert | 20 + .../shaders/asm/vert/invariant-block.asm.vert | 9 + .../asm/vert/invariant-block.sso.asm.vert | 17 + .../opt/shaders/asm/vert/invariant.asm.vert | 9 + .../shaders/asm/vert/invariant.sso.asm.vert | 14 + .../spec-constant-op-composite.asm.vk.vert | 30 + .../spec-constant-op-composite.asm.vk.vert.vk | 24 + .../vert/uint-vertex-id-instance-id.asm.vert | 16 + .../reference/opt/shaders/comp/atomic.comp | 49 + .../opt/shaders/comp/bake_gradient.comp | 26 + .../reference/opt/shaders/comp/barriers.comp | 22 + .../reference/opt/shaders/comp/basic.comp | 28 + .../reference/opt/shaders/comp/casts.comp | 18 + .../shaders/comp/cfg-preserve-parameter.comp | 7 + .../reference/opt/shaders/comp/cfg.comp | 44 + .../opt/shaders/comp/coherent-block.comp | 13 + .../opt/shaders/comp/coherent-image.comp | 15 + .../comp/composite-array-initialization.comp | 27 + .../opt/shaders/comp/composite-construct.comp | 18 + .../reference/opt/shaders/comp/culling.comp | 28 + .../opt/shaders/comp/defer-parens.comp | 20 + .../reference/opt/shaders/comp/dowhile.comp | 38 + .../opt/shaders/comp/generate_height.comp | 56 + .../reference/opt/shaders/comp/image.comp | 12 + .../reference/opt/shaders/comp/insert.comp | 24 + .../reference/opt/shaders/comp/mat3.comp | 13 + .../reference/opt/shaders/comp/mod.comp | 20 + .../reference/opt/shaders/comp/modf.comp | 20 + .../opt/shaders/comp/outer-product.comp | 36 + .../opt/shaders/comp/read-write-only.comp | 27 + .../opt/shaders/comp/rmw-matrix.comp | 20 + .../reference/opt/shaders/comp/rmw-opt.comp | 23 + ...alar-std450-distance-length-normalize.comp | 19 + .../reference/opt/shaders/comp/shared.comp | 22 + .../opt/shaders/comp/ssbo-array-length.comp | 14 + .../opt/shaders/comp/ssbo-array.comp | 13 + .../opt/shaders/comp/struct-layout.comp | 23 + .../opt/shaders/comp/struct-packing.comp | 104 + .../opt/shaders/comp/torture-loop.comp | 40 + .../opt/shaders/comp/type-alias.comp | 33 + .../reference/opt/shaders/comp/udiv.comp | 18 + .../desktop-only/comp/enhanced-layouts.comp | 40 + .../comp/extended-arithmetic.desktop.comp | 159 + .../desktop-only/comp/fp64.desktop.comp | 63 + .../image-formats.desktop.noeliminate.comp | 7 + .../desktop-only/comp/int64.desktop.comp | 52 + .../frag/clip-cull-distance.desktop.frag | 12 + .../control-dependent-in-branch.desktop.frag | 37 + .../frag/depth-greater-than.desktop.frag | 9 + .../frag/depth-less-than.desktop.frag | 9 + .../frag/dual-source-blending.desktop.frag | 11 + .../frag/hlsl-uav-block-alias.asm.frag | 19 + .../desktop-only/frag/image-ms.desktop.frag | 12 + .../frag/image-query.desktop.frag | 6 + .../shaders/desktop-only/frag/image-size.frag | 12 + .../image-size.no-qualifier-deduction.frag | 12 + .../frag/in-block-qualifiers.frag | 21 + .../frag/layout-component.desktop.frag | 16 + .../frag/query-levels.desktop.frag | 11 + .../desktop-only/frag/query-lod.desktop.frag | 12 + .../frag/sampler-ms-query.desktop.frag | 14 + .../frag/stencil-export.desktop.frag | 13 + .../frag/texture-proj-shadow.desktop.frag | 26 + .../desktop-only/geom/basic.desktop.sso.geom | 36 + .../geom/viewport-index.desktop.geom | 9 + .../desktop-only/tesc/basic.desktop.sso.tesc | 27 + .../tese/triangle.desktop.sso.tese | 18 + .../desktop-only/vert/basic.desktop.sso.vert | 22 + .../vert/clip-cull-distance.desktop.sso.vert | 20 + .../vert/clip-cull-distance.desktop.vert | 15 + .../vert/out-block-qualifiers.vert | 27 + ...shader-draw-parameters-450.desktop.vk.vert | 24 + ...der-draw-parameters-450.desktop.vk.vert.vk | 8 + .../shader-draw-parameters.desktop.vk.vert | 24 + .../shader-draw-parameters.desktop.vk.vert.vk | 7 + .../opt/shaders/flatten/array.flatten.vert | 10 + .../opt/shaders/flatten/basic.flatten.vert | 13 + .../opt/shaders/flatten/copy.flatten.vert | 27 + .../opt/shaders/flatten/dynamic.flatten.vert | 27 + .../flatten/matrix-conversion.flatten.frag | 14 + .../shaders/flatten/matrixindex.flatten.vert | 19 + .../shaders/flatten/multiindex.flatten.vert | 10 + .../flatten/push-constant.flatten.vert | 13 + .../opt/shaders/flatten/rowmajor.flatten.vert | 10 + .../opt/shaders/flatten/struct.flatten.vert | 22 + .../flatten/struct.rowmajor.flatten.vert | 21 + .../opt/shaders/flatten/swizzle.flatten.vert | 21 + .../opt/shaders/flatten/types.flatten.frag | 14 + .../frag/array-lut-no-loop-variable.frag | 19 + .../avoid-expression-lowering-to-loop.frag | 29 + .../opt/shaders/frag/barycentric-nv.frag | 19 + .../reference/opt/shaders/frag/basic.frag | 15 + .../complex-expression-in-access-chain.frag | 21 + .../composite-extract-forced-temporary.frag | 16 + .../opt/shaders/frag/constant-array.frag | 22 + .../opt/shaders/frag/constant-composites.frag | 23 + .../opt/shaders/frag/false-loop-init.frag | 18 + .../opt/shaders/frag/flush_params.frag | 11 + .../frag/for-loop-continue-control-flow.frag | 15 + .../opt/shaders/frag/for-loop-init.frag | 65 + .../opt/shaders/frag/frexp-modf.frag | 33 + .../opt/shaders/frag/front-facing.frag | 20 + .../opt/shaders/frag/gather-dref.frag | 14 + .../reference/opt/shaders/frag/ground.frag | 35 + .../opt/shaders/frag/helper-invocation.frag | 23 + ...temporary-use-continue-block-as-value.frag | 28 + .../frag/image-load-store-uint-coord.asm.frag | 17 + ...-loop-dominated-variable-preservation.frag | 11 + .../loop-dominator-and-switch-default.frag | 56 + .../opt/shaders/frag/lut-promotion.frag | 41 + .../reference/opt/shaders/frag/mix.frag | 18 + .../shaders/frag/partial-write-preserve.frag | 8 + .../shaders/frag/pixel-interlock-ordered.frag | 23 + .../frag/pixel-interlock-unordered.frag | 23 + .../reference/opt/shaders/frag/pls.frag | 21 + .../shaders/frag/post-depth-coverage-es.frag | 14 + .../opt/shaders/frag/post-depth-coverage.frag | 15 + .../opt/shaders/frag/round-even.frag | 12 + .../reference/opt/shaders/frag/round.frag | 12 + .../frag/sample-interlock-ordered.frag | 23 + .../frag/sample-interlock-unordered.frag | 23 + .../opt/shaders/frag/sample-parameter.frag | 13 + .../opt/shaders/frag/sampler-ms.frag | 14 + .../opt/shaders/frag/sampler-proj.frag | 16 + .../reference/opt/shaders/frag/sampler.frag | 15 + .../shaders/frag/scalar-refract-reflect.frag | 13 + .../frag/selection-block-dominator.frag | 19 + .../frag/struct-type-unrelated-alias.frag | 9 + .../shaders/frag/switch-unsigned-case.frag | 29 + .../reference/opt/shaders/frag/swizzle.frag | 22 + .../opt/shaders/frag/texel-fetch-offset.frag | 15 + .../frag/ubo-load-row-major-workaround.frag | 46 + .../opt/shaders/frag/ubo_layout.frag | 26 + .../opt/shaders/frag/unary-enclose.frag | 12 + .../reference/opt/shaders/geom/basic.geom | 27 + .../shaders/geom/geometry-passthrough.geom | 27 + .../opt/shaders/geom/lines-adjacency.geom | 26 + .../reference/opt/shaders/geom/lines.geom | 23 + .../opt/shaders/geom/multi-stream.geom | 14 + .../reference/opt/shaders/geom/points.geom | 26 + .../opt/shaders/geom/single-invocation.geom | 26 + .../geom/transform-feedback-streams.geom | 26 + .../opt/shaders/geom/triangles-adjacency.geom | 26 + .../reference/opt/shaders/geom/triangles.geom | 26 + .../legacy/fragment/explicit-lod.legacy.frag | 12 + .../legacy/fragment/explicit-lod.legacy.vert | 11 + .../shaders/legacy/fragment/fma.legacy.frag | 13 + .../legacy/fragment/io-blocks.legacy.frag | 12 + .../multiple-struct-flattening.legacy.frag | 36 + .../shaders/legacy/fragment/round.legacy.frag | 13 + .../fragment/struct-varying.legacy.frag | 18 + .../legacy/fragment/switch.legacy.frag | 77 + .../legacy/vert/implicit-lod.legacy.vert | 9 + .../shaders/legacy/vert/io-block.legacy.vert | 13 + .../struct-flatten-inner-array.legacy.vert | 17 + ...flatten-stores-multi-dimension.legacy.vert | 49 + .../legacy/vert/struct-varying.legacy.vert | 24 + .../legacy/vert/switch-nested.legacy.vert | 43 + .../shaders/legacy/vert/transpose.legacy.vert | 29 + .../reference/opt/shaders/tesc/basic.tesc | 17 + .../opt/shaders/tesc/water_tess.tesc | 79 + .../reference/opt/shaders/tese/ccw.tese | 9 + .../reference/opt/shaders/tese/cw.tese | 9 + .../reference/opt/shaders/tese/equal.tese | 9 + .../opt/shaders/tese/fractional_even.tese | 9 + .../opt/shaders/tese/fractional_odd.tese | 9 + .../opt/shaders/tese/input-array.tese | 11 + .../reference/opt/shaders/tese/line.tese | 9 + .../opt/shaders/tese/load-array-of-array.tese | 10 + .../opt/shaders/tese/patch-input-array.tese | 10 + .../reference/opt/shaders/tese/triangle.tese | 9 + .../opt/shaders/tese/water_tess.tese | 36 + .../reference/opt/shaders/vert/basic.vert | 17 + .../reference/opt/shaders/vert/ground.vert | 94 + .../reference/opt/shaders/vert/invariant.vert | 19 + .../reference/opt/shaders/vert/ocean.vert | 126 + .../vert/read-from-row-major-array.vert | 18 + .../opt/shaders/vert/return-array.vert | 9 + .../opt/shaders/vert/texture_buffer.vert | 11 + .../vert/transform-feedback-decorations.vert | 22 + .../reference/opt/shaders/vert/ubo.vert | 17 + ...ay-of-buffer-reference.nocompat.vk.comp.vk | 25 + ...ffer-reference-bitcast.nocompat.vk.comp.vk | 26 + .../comp/buffer-reference.nocompat.vk.comp.vk | 35 + .../spec-constant-op-member-array.vk.comp | 47 + .../spec-constant-op-member-array.vk.comp.vk | 35 + .../spec-constant-work-group-size.vk.comp | 34 + .../spec-constant-work-group-size.vk.comp.vk | 24 + .../combined-texture-sampler-shadow.vk.frag | 14 + ...combined-texture-sampler-shadow.vk.frag.vk | 15 + .../frag/combined-texture-sampler.vk.frag | 17 + .../frag/combined-texture-sampler.vk.frag.vk | 17 + ...-helper-forwarding.asm.vk.nocompat.frag.vk | 15 + .../frag/demote-to-helper.vk.nocompat.frag.vk | 9 + .../vulkan/frag/desktop-mediump.vk.frag | 12 + .../vulkan/frag/desktop-mediump.vk.frag.vk | 12 + .../vulkan/frag/input-attachment-ms.vk.frag | 12 + .../frag/input-attachment-ms.vk.frag.vk | 12 + .../vulkan/frag/input-attachment.vk.frag | 14 + .../vulkan/frag/input-attachment.vk.frag.vk | 14 + .../nonuniform-qualifier.vk.nocompat.frag.vk | 34 + .../push-constant-as-ubo.push-ubo.vk.frag | 14 + .../push-constant-as-ubo.push-ubo.vk.frag.vk | 14 + .../shaders/vulkan/frag/push-constant.vk.frag | 20 + .../vulkan/frag/push-constant.vk.frag.vk | 18 + .../separate-combined-fake-overload.vk.frag | 12 + ...separate-combined-fake-overload.vk.frag.vk | 13 + .../separate-sampler-texture-array.vk.frag | 19 + .../separate-sampler-texture-array.vk.frag.vk | 20 + .../frag/separate-sampler-texture.vk.frag | 19 + .../frag/separate-sampler-texture.vk.frag.vk | 20 + ...shader-arithmetic-8bit.nocompat.vk.frag.vk | 43 + .../frag/spec-constant-block-size.vk.frag | 22 + .../frag/spec-constant-block-size.vk.frag.vk | 19 + .../vulkan/frag/spec-constant-ternary.vk.frag | 16 + .../frag/spec-constant-ternary.vk.frag.vk | 13 + .../hit_attribute_block.nocompat.vk.rchit.vk | 24 + ...ute_block_in_function.nocompat.vk.rchit.vk | 24 + .../hit_attribute_plain.nocompat.vk.rchit.vk | 11 + .../hit_attribute_struct.nocompat.vk.rchit.vk | 17 + .../rchit/hit_kind.nocompat.vk.rchit.vk | 10 + .../vulkan/rchit/hit_t.nocompat.vk.rchit.vk | 10 + .../incoming_ray_flags.nocompat.vk.rchit.vk | 10 + .../instance_custom_id.nocompat.vk.rchit.vk | 10 + .../rchit/instance_id.nocompat.vk.rchit.vk | 10 + .../object_ray_direction.nocompat.vk.rchit.vk | 10 + .../object_ray_origin.nocompat.vk.rchit.vk | 10 + .../object_to_world.nocompat.vk.rchit.vk | 10 + .../rchit/payloads.nocompat.vk.rchit.vk | 15 + .../rchit/primitive_id.nocompat.vk.rchit.vk | 10 + .../rchit/ray_tmax.nocompat.vk.rchit.vk | 10 + .../rchit/ray_tmin.nocompat.vk.rchit.vk | 10 + .../rchit/ray_tracing.nocompat.vk.rchit.vk | 10 + .../world_ray_direction.nocompat.vk.rchit.vk | 10 + .../world_ray_origin.nocompat.vk.rchit.vk | 10 + .../world_to_object.nocompat.vk.rchit.vk | 10 + .../rgen/execute_callable.nocompat.vk.rgen.vk | 15 + .../vulkan/rgen/launch_id.nocompat.vk.rgen.vk | 10 + .../rgen/launch_size.nocompat.vk.rgen.vk | 10 + .../vulkan/rgen/payloads.nocompat.vk.rgen.vk | 31 + .../vulkan/rgen/pure_call.nocompat.vk.rgen.vk | 12 + .../rgen/ray_tracing.nocompat.vk.rgen.vk | 15 + .../shader_record_buffer.nocompat.vk.rgen.vk | 16 + .../rmiss/ray_tracing.nocompat.vk.rmiss.vk | 10 + .../vert/device-group.nocompat.vk.vert.vk | 8 + .../vulkan/vert/multiview.nocompat.vk.vert.vk | 15 + .../shaders/vulkan/vert/small-storage.vk.vert | 59 + .../vulkan/vert/small-storage.vk.vert.vk | 55 + .../shaders/vulkan/vert/vulkan-vertex.vk.vert | 16 + .../vulkan/vert/vulkan-vertex.vk.vert.vk | 7 + ...access-chain-load-store-composite.asm.comp | 75 + ...ased-struct-divergent-member-name.asm.comp | 22 + .../asm/comp/atomic-load-store.asm.comp | 18 + .../asm/comp/atomic-result-temporary.asm.comp | 24 + .../comp/bitfield-signed-operations.asm.comp | 105 + .../asm/comp/bitscan.asm.comp | 25 + ...onuniform.asm.sm51.nonuniformresource.comp | 21 + .../comp/constant-composite-undef.asm.comp | 14 + .../asm/comp/glsl-signed-operations.asm.comp | 45 + ....std450.frexp-modf-struct.fxconly.asm.comp | 31 + ...onuniform.asm.sm51.nonuniformresource.comp | 21 + ...lization-constant-workgroup.nofxc.asm.comp | 22 + ...torage-buffer-basic.invalid.nofxc.asm.comp | 32 + .../asm/frag/empty-struct-in-struct.asm.frag | 37 + .../asm/frag/image-fetch-uint-coord.asm.frag | 28 + ...andling-2.nonuniformresource.sm51.asm.frag | 37 + ...opagation.nonuniformresource.sm51.asm.frag | 52 + .../asm/frag/phi.zero-initialize.asm.frag | 45 + ...-interlock-callstack.sm51.fxconly.asm.frag | 33 + ...terlock-control-flow.sm51.fxconly.asm.frag | 43 + ...lock-split-functions.sm51.fxconly.asm.frag | 43 + ...element-array-matrix-rule.invalid.asm.frag | 42 + ...rithmetic-cast.invalid.nofxc.sm60.asm.frag | 30 + ...ch-block-case-fallthrough.asm.invalid.frag | 82 + .../asm/frag/unordered-compare.asm.frag | 51 + .../frag/vector-shuffle-undef-index.asm.frag | 28 + .../asm/temporary.zero-initialize.asm.frag | 44 + .../asm/vert/complex-link-by-name.asm.vert | 54 + .../asm/vert/empty-struct-composite.asm.vert | 15 + .../shaders-hlsl-no-opt/comp/bitfield.comp | 113 + .../comp/glsl.std450.fxconly.comp | 297 + .../comp/illegal-struct-name.asm.comp | 22 + .../comp/subgroups.invalid.nofxc.sm60.comp | 95 + .../frag/cbuffer-packing-straddle.frag | 56 + .../constant-buffer-array.invalid.sm51.frag | 44 + .../frag/fp16.invalid.desktop.frag | 179 + .../shaders-hlsl-no-opt/frag/frag-coord.frag | 27 + ...types.fxconly.nofxc.sm62.native-16bit.frag | 79 + ...terlock-simple-callstack.sm51.fxconly.frag | 33 + .../frag/spec-constant.frag | 142 + .../frag/ubo-offset-out-of-order.frag | 33 + .../frag/variables.zero-initialize.frag | 45 + ...rix-input.flatten-matrix-vertex-input.vert | 51 + .../vert/pass-array-by-value.vert | 48 + .../asm/comp/access-chain-invalidate.asm.comp | 19 + .../asm/comp/atomic-decrement.asm.comp | 24 + .../asm/comp/atomic-increment.asm.comp | 24 + .../asm/comp/bitcast_icmp.asm.comp | 28 + .../asm/comp/block-name-alias-global.asm.comp | 39 + .../asm/comp/control-flow-hints.asm.comp | 41 + .../comp/global-parameter-name-alias.asm.comp | 33 + .../asm/comp/nmin-max-clamp.asm.comp | 84 + .../asm/frag/cbuffer-stripped.asm.frag | 32 + .../asm/frag/combined-sampler-reuse.asm.frag | 30 + .../asm/frag/empty-struct.asm.frag | 27 + .../shaders-hlsl/asm/frag/frem.asm.frag | 29 + .../asm/frag/function-overload-alias.asm.frag | 47 + .../asm/frag/image-extract-reuse.asm.frag | 31 + .../asm/frag/implicit-read-dep-phi.asm.frag | 56 + .../asm/frag/inf-nan-constant.asm.frag | 19 + .../asm/frag/line-directive.line.asm.frag | 86 + .../frag/lut-promotion-initializer.asm.frag | 55 + ...d-unpack-uint2.fxconly.nofxc.sm60.asm.frag | 34 + .../asm/frag/pass-by-value.asm.frag | 30 + .../asm/frag/sample-and-compare.asm.frag | 30 + .../frag/single-function-private-lut.asm.frag | 66 + .../shaders-hlsl/asm/frag/srem.asm.frag | 29 + .../storage-class-output-initializer.asm.frag | 23 + .../asm/frag/texel-fetch-no-lod.asm.frag | 30 + .../asm/frag/texture-sampling-fp16.asm.frag | 29 + .../asm/frag/unknown-depth-state.asm.frag | 41 + .../asm/frag/unreachable.asm.frag | 44 + ...act-transposed-matrix-from-struct.asm.vert | 67 + .../vert/spec-constant-op-composite.asm.vert | 50 + .../vert/uint-vertex-id-instance-id.asm.vert | 37 + .../asm/vert/vertex-id-instance-id.asm.vert | 28 + .../comp/access-chain-load-composite.comp | 164 + .../shaders-hlsl/comp/access-chains.comp | 23 + .../comp/access-chains.force-uav.comp | 23 + .../shaders-hlsl/comp/address-buffers.comp | 17 + .../reference/shaders-hlsl/comp/atomic.comp | 91 + .../reference/shaders-hlsl/comp/barriers.comp | 81 + .../reference/shaders-hlsl/comp/builtins.comp | 32 + .../comp/composite-array-initialization.comp | 63 + .../shaders-hlsl/comp/globallycoherent.comp | 20 + .../reference/shaders-hlsl/comp/image.comp | 73 + .../comp/image.nonwritable-uav-texture.comp | 73 + .../reference/shaders-hlsl/comp/inverse.comp | 124 + .../comp/num-workgroups-alone.comp | 19 + .../comp/num-workgroups-with-builtins.comp | 26 + .../shaders-hlsl/comp/outer-product.comp | 50 + .../shaders-hlsl/comp/rmw-matrix.comp | 22 + .../shaders-hlsl/comp/rwbuffer-matrix.comp | 139 + ...alar-std450-distance-length-normalize.comp | 16 + .../reference/shaders-hlsl/comp/shared.comp | 31 + .../comp/spec-constant-op-member-array.comp | 51 + .../comp/spec-constant-work-group-size.comp | 43 + .../shaders-hlsl/comp/ssbo-array-length.comp | 17 + .../shaders-hlsl/comp/ssbo-array.comp | 13 + .../frag/array-lut-no-loop-variable.frag | 30 + .../frag/basic-color-3comp.sm30.frag | 26 + .../frag/basic-color-3comp.sm50.frag | 26 + .../reference/shaders-hlsl/frag/basic.frag | 32 + .../shaders-hlsl/frag/bit-conversions.frag | 27 + .../shaders-hlsl/frag/boolean-mix.frag | 27 + .../reference/shaders-hlsl/frag/builtins.frag | 34 + .../shaders-hlsl/frag/bvec-operations.frag | 29 + .../shaders-hlsl/frag/clip-cull-distance.frag | 30 + .../combined-texture-sampler-parameter.frag | 44 + .../frag/combined-texture-sampler-shadow.frag | 40 + .../complex-expression-in-access-chain.frag | 41 + .../frag/constant-composites.frag | 38 + .../control-dependent-in-branch.desktop.frag | 56 + .../shaders-hlsl/frag/demote-to-helper.frag | 9 + .../shaders-hlsl/frag/depth-greater-than.frag | 19 + .../shaders-hlsl/frag/depth-less-than.frag | 19 + .../frag/dual-source-blending.frag | 23 + .../frag/early-fragment-test.frag | 9 + .../frag/for-loop-continue-control-flow.frag | 44 + .../shaders-hlsl/frag/fp16-packing.frag | 44 + .../shaders-hlsl/frag/front-facing.frag | 39 + .../frag/image-query-selective.frag | 146 + .../shaders-hlsl/frag/image-query-uav.frag | 64 + ...age-query-uav.nonwritable-uav-texture.frag | 63 + .../shaders-hlsl/frag/image-query.frag | 132 + .../frag/input-attachment-ms.frag | 39 + .../shaders-hlsl/frag/input-attachment.frag | 35 + .../reference/shaders-hlsl/frag/io-block.frag | 28 + .../frag/legacy-tex-modifiers.sm30.frag | 32 + .../shaders-hlsl/frag/lut-promotion.frag | 55 + .../shaders-hlsl/frag/matrix-input.frag | 26 + .../reference/shaders-hlsl/frag/mod.frag | 71 + .../reference/shaders-hlsl/frag/mrt.frag | 31 + .../shaders-hlsl/frag/no-return.frag | 8 + .../shaders-hlsl/frag/no-return2.frag | 17 + ...orm-qualifier.nonuniformresource.sm51.frag | 52 + .../frag/partial-write-preserve.frag | 76 + .../pixel-interlock-ordered.sm51.fxconly.frag | 24 + .../shaders-hlsl/frag/point-coord-compat.frag | 19 + .../shaders-hlsl/frag/query-lod.desktop.frag | 31 + .../readonly-coherent-ssbo.force-uav.frag | 21 + .../frag/readonly-coherent-ssbo.frag | 21 + .../shaders-hlsl/frag/resources.frag | 44 + .../frag/row-major-layout-in-struct.frag | 47 + .../frag/sample-cmp-level-zero.frag | 51 + .../frag/sample-mask-in-and-out.frag | 30 + .../shaders-hlsl/frag/sample-mask-in.frag | 32 + .../shaders-hlsl/frag/sample-mask-out.frag | 23 + .../shaders-hlsl/frag/sampler-array.frag | 45 + .../frag/sampler-image-arrays.frag | 54 + .../frag/scalar-refract-reflect.frag | 49 + .../separate-combined-fake-overload.sm30.frag | 32 + .../frag/spec-constant-block-size.frag | 37 + .../frag/spec-constant-ternary.frag | 26 + .../frag/switch-unsigned-case.frag | 38 + .../shaders-hlsl/frag/swizzle-scalar.frag | 41 + .../shaders-hlsl/frag/tex-sampling-ms.frag | 33 + .../shaders-hlsl/frag/tex-sampling.frag | 106 + .../shaders-hlsl/frag/tex-sampling.sm30.frag | 83 + .../shaders-hlsl/frag/texel-fetch-offset.frag | 31 + .../frag/texture-proj-shadow.frag | 51 + .../texture-size-combined-image-sampler.frag | 30 + .../shaders-hlsl/frag/unary-enclose.frag | 32 + .../frag/unorm-snorm-packing.frag | 109 + .../shaders-hlsl/frag/various-glsl-ops.frag | 28 + .../reference/shaders-hlsl/vert/basic.vert | 39 + .../shaders-hlsl/vert/clip-cull-distance.vert | 28 + .../shaders-hlsl/vert/instancing.vert | 28 + .../shaders-hlsl/vert/locations.vert | 75 + .../shaders-hlsl/vert/matrix-attribute.vert | 35 + .../shaders-hlsl/vert/matrix-output.vert | 23 + .../reference/shaders-hlsl/vert/no-input.vert | 18 + .../shaders-hlsl/vert/point-size-compat.vert | 20 + .../shaders-hlsl/vert/qualifiers.vert | 50 + .../vert/read-from-row-major-array.vert | 65 + .../shaders-hlsl/vert/return-array.vert | 48 + .../shaders-hlsl/vert/sampler-buffers.vert | 27 + .../vert/struct-composite-decl.vert | 50 + .../shaders-hlsl/vert/texture_buffer.vert | 21 + ...ased-struct-divergent-member-name.asm.comp | 38 + .../comp/arithmetic-conversion-signs.asm.comp | 42 + .../asm/comp/atomic-load-store.asm.comp | 23 + .../asm/comp/atomic-result-temporary.asm.comp | 23 + .../asm/comp/bitcast-fp16-fp32.asm.comp | 21 + .../comp/bitfield-signed-operations.asm.comp | 29 + .../asm/comp/bitscan.asm.comp | 53 + ...composite-construct-buffer-struct.asm.comp | 22 + .../comp/constant-composite-undef.asm.comp | 17 + .../asm/comp/copy-logical-2.spv14.asm.comp | 60 + .../asm/comp/copy-logical.spv14.asm.comp | 47 + .../comp/device-array-load-temporary.asm.comp | 174 + ...load-temporary.force-native-array.asm.comp | 135 + .../device-constant-array-load-store.asm.comp | 179 + ...ray-load-store.force-native-array.asm.comp | 140 + .../asm/comp/glsl-signed-operations.asm.comp | 73 + .../glsl.std450.frexp-modf-struct.asm.comp | 35 + .../storage-buffer-basic.invalid.asm.comp | 22 + .../storage-buffer-pointer-argument.asm.comp | 28 + .../asm/comp/variable-pointers.asm.comp | 74 + ...omponent-insert-packed-expression.asm.frag | 34 + .../asm/frag/empty-struct-in-struct.asm.frag | 35 + .../asm/frag/image-fetch-uint-coord.asm.frag | 22 + .../asm/frag/image-gather.asm.frag | 22 + ...mage-type-normal-comparison-usage.asm.frag | 31 + ...nput-attachment-unused-frag-coord.asm.frag | 25 + ...-frexp-scalar-access-chain-output.asm.frag | 17 + .../asm/frag/phi.zero-initialize.asm.frag | 41 + .../pixel-interlock-callstack.msl2.asm.frag | 37 + ...pixel-interlock-control-flow.msl2.asm.frag | 52 + ...el-interlock-split-functions.msl2.asm.frag | 49 + .../private-initializer-direct-store.asm.frag | 19 + .../subgroup-arithmetic-cast.msl21.asm.frag | 30 + ...ch-block-case-fallthrough.asm.invalid.frag | 75 + .../asm/frag/texture-access.swizzle.asm.frag | 190 + .../frag/vector-shuffle-undef-index.asm.frag | 24 + .../composite-extract-row-major.asm.comp | 16 + .../load-packed-no-forwarding-2.asm.comp | 19 + .../load-packed-no-forwarding-3.asm.comp | 20 + .../load-packed-no-forwarding-4.asm.comp | 20 + .../load-packed-no-forwarding-5.asm.comp | 20 + .../load-packed-no-forwarding.asm.comp | 23 + .../packed-vector-extract-insert.asm.comp | 21 + .../row-major-split-access-chain.asm.comp | 16 + .../asm/packing/scalar-array-float2.asm.frag | 24 + .../scalar-array-float3-one-element.asm.frag | 23 + .../asm/packing/scalar-array-float3.asm.frag | 24 + ...scalar-float2x2-col-major.invalid.asm.frag | 24 + .../scalar-float2x2-row-major.asm.frag | 24 + ...scalar-float2x3-col-major.invalid.asm.frag | 24 + .../scalar-float2x3-row-major.asm.frag | 24 + ...scalar-float3x2-col-major.invalid.asm.frag | 24 + .../scalar-float3x2-row-major.asm.frag | 24 + ...scalar-float3x3-col-major.invalid.asm.frag | 24 + .../scalar-float3x3-row-major.asm.frag | 24 + .../asm/temporary.zero-initialize.asm.frag | 38 + ...input-array-builtin-array.invalid.asm.tesc | 141 + ...builtin-array.invalid.multi-patch.asm.tesc | 140 + ...omposite-extract-physical-type-id.asm.vert | 31 + .../asm/vert/empty-struct-composite.asm.vert | 13 + .../op-load-forced-temporary-array.asm.frag | 76 + .../comp/array-copy-threadgroup-memory.comp | 172 + .../basic.dynamic-buffer.msl2.invalid.comp | 90 + .../comp/bitcast-16bit-1.invalid.comp | 26 + .../comp/bitcast-16bit-2.invalid.comp | 31 + .../shaders-msl-no-opt/comp/bitfield.comp | 47 + .../shaders-msl-no-opt/comp/glsl.std450.comp | 289 + .../comp/illegal-struct-name.asm.comp | 29 + .../comp/int64.invalid.msl22.comp | 106 + .../shaders-msl-no-opt/comp/loop.comp | 91 + .../shaders-msl-no-opt/comp/return.comp | 39 + ...std140-array-load-composite-construct.comp | 18 + ...ct-packing-scalar.nocompat.invalid.vk.comp | 151 + .../subgroups.nocompat.invalid.vk.msl21.comp | 320 + ...bgroups.nocompat.invalid.vk.msl21.ios.comp | 151 + .../components/fragment-input-component.frag | 23 + .../components/fragment-output-component.frag | 25 + ...ragment-output-component.pad-fragment.frag | 22 + .../components/vertex-input-component.vert | 28 + .../components/vertex-output-component.vert | 26 + .../frag/16bit-constants.invalid.frag | 21 + ...e-to-helper.vk.nocompat.msl21.invalid.frag | 11 + .../frag/depth-image-gather.asm.frag | 22 + ....msl2.argument..force-active.discrete.frag | 31 + .../frag/fp16.desktop.invalid.frag | 185 + .../shaders-msl-no-opt/frag/image-gather.frag | 24 + .../frag/in_block_assign.frag | 31 + .../frag/min-max-clamp.invalid.asm.frag | 69 + ...pixel-interlock-simple-callstack.msl2.frag | 37 + ...layout-ubo-std430.vk.nocompat.invalid.frag | 36 + .../shadow-compare-global-alias.invalid.frag | 57 + .../subgroups.nocompat.invalid.vk.msl21.frag | 315 + ...-fallback.msl20.ios.framebuffer-fetch.frag | 17 + ...ndex-fallback.msl23.framebuffer-fetch.frag | 17 + ...nction-argument.framebuffer-fetch.ios.frag | 37 + ...tion-argument.framebuffer-fetch.msl23.frag | 37 + .../frag/texture-access-int.swizzle.frag | 148 + .../frag/texture-access-leaf.swizzle.frag | 197 + .../frag/texture-access-uint.swizzle.frag | 148 + .../frag/texture-access.swizzle.frag | 190 + ...le-structs-access-chain.argument.msl2.frag | 32 + ...o-array-multiple-structs-access-chain.frag | 33 + .../frag/ubo-offset-out-of-order.frag | 28 + .../frag/variables.zero-initialize.frag | 40 + .../packing/array-of-vec3.comp | 17 + .../packing/array-of-vec4.comp | 17 + .../packing/isolated-scalar-access.comp | 26 + .../packing/load-store-col-rows.comp | 76 + .../packing/matrix-2x2-scalar.comp | 86 + .../packing/matrix-2x2-std140.comp | 92 + .../packing/matrix-2x2-std430.comp | 86 + .../packing/matrix-2x3-scalar.comp | 92 + .../packing/matrix-2x3-std140.comp | 93 + .../packing/matrix-2x3-std430.comp | 87 + .../packing/matrix-2x4-scalar.comp | 88 + .../packing/matrix-2x4-std140.comp | 97 + .../packing/matrix-2x4-std430.comp | 88 + .../packing/matrix-3x2-scalar.comp | 91 + .../packing/matrix-3x2-std140.comp | 92 + .../packing/matrix-3x2-std430.comp | 86 + .../packing/matrix-3x3-scalar.comp | 102 + .../packing/matrix-3x3-std140.comp | 87 + .../packing/matrix-3x3-std430.comp | 87 + .../packing/matrix-3x4-scalar.comp | 99 + .../packing/matrix-3x4-std140.comp | 88 + .../packing/matrix-3x4-std430.comp | 88 + .../packing/matrix-4x2-scalar.comp | 86 + .../packing/matrix-4x2-std140.comp | 95 + .../packing/matrix-4x2-std430.comp | 86 + .../packing/matrix-4x3-scalar.comp | 98 + .../packing/matrix-4x3-std140.comp | 87 + .../packing/matrix-4x3-std430.comp | 87 + .../packing/matrix-4x4-scalar.comp | 88 + .../packing/matrix-4x4-std140.comp | 88 + .../packing/matrix-4x4-std430.comp | 88 + .../packing/matrix-multiply-row-major.comp | 21 + .../matrix-multiply-unpacked-col-major-2.comp | 25 + .../matrix-multiply-unpacked-col-major.comp | 23 + .../matrix-multiply-unpacked-row-major-2.comp | 25 + .../matrix-multiply-unpacked-row-major.comp | 23 + .../packing/member-padding.comp | 21 + .../packing/std140-array-of-vectors.comp | 42 + .../packing/struct-alignment.comp | 27 + .../struct-packing-array-of-scalar.comp | 22 + .../packing/struct-packing-recursive.comp | 33 + .../packing/struct-packing.comp | 29 + .../struct-size-padding-array-of-array.comp | 54 + .../packing/struct-size-padding.comp | 54 + .../vert/functions_nested.vert | 199 + .../vert/layer.msl11.invalid.vert | 24 + ...ass-array-by-value.force-native-array.vert | 157 + .../vert/pass-array-by-value.vert | 87 + .../vert/viewport-index.msl2.invalid.vert | 24 + .../texture-access-function.swizzle.vk.frag | 204 + .../amd/shader_trinary_minmax.msl21.comp | 14 + .../asm/comp/atomic-decrement.asm.comp | 29 + .../asm/comp/atomic-increment.asm.comp | 29 + .../asm/comp/bitcast_iadd.asm.comp | 29 + .../asm/comp/bitcast_icmp.asm.comp | 29 + .../shaders-msl/asm/comp/bitcast_sar.asm.comp | 31 + .../asm/comp/bitcast_sdiv.asm.comp | 31 + .../shaders-msl/asm/comp/bitcast_slr.asm.comp | 31 + .../asm/comp/block-name-alias-global.asm.comp | 46 + .../comp/buffer-write-relative-addr.asm.comp | 32 + .../asm/comp/buffer-write.asm.comp | 24 + .../comp/global-parameter-name-alias.asm.comp | 33 + .../image-load-store-short-vector.asm.comp | 22 + .../asm/comp/multiple-entry.asm.comp | 29 + .../shaders-msl/asm/comp/quantize.asm.comp | 21 + .../asm/comp/relaxed-block-layout.asm.comp | 22 + ...specialization-constant-workgroup.asm.comp | 21 + .../struct-resource-name-aliasing.asm.comp | 24 + .../asm/comp/variable-pointers-2.asm.comp | 63 + ...ariable-pointers-store-forwarding.asm.comp | 32 + .../vector-builtin-type-cast-func.asm.comp | 34 + .../comp/vector-builtin-type-cast.asm.comp | 24 + .../asm/frag/combined-sampler-reuse.asm.frag | 23 + .../asm/frag/default-member-names.asm.frag | 40 + .../frag/descriptor-array-unnamed.asm.frag | 48 + ...isable-renamed-output.frag-output.asm.frag | 35 + .../asm/frag/empty-struct.asm.frag | 29 + .../extract-packed-from-composite.asm.frag | 49 + .../shaders-msl/asm/frag/frem.asm.frag | 23 + .../asm/frag/function-overload-alias.asm.frag | 51 + .../asm/frag/image-extract-reuse.asm.frag | 17 + .../asm/frag/implicit-read-dep-phi.asm.frag | 49 + .../asm/frag/inf-nan-constant.asm.frag | 17 + .../interpolation-qualifiers-struct.asm.frag | 47 + .../asm/frag/line-directive.line.asm.frag | 85 + .../asm/frag/locations-components.asm.frag | 37 + .../frag/lut-promotion-initializer.asm.frag | 92 + .../asm/frag/min-lod.msl22.asm.frag | 22 + .../asm/frag/op-constant-null.asm.frag | 70 + .../asm/frag/op-image-sampled-image.asm.frag | 25 + .../asm/frag/pass-by-value.asm.frag | 30 + .../asm/frag/phi-loop-variable.asm.frag | 12 + .../pull-model-interpolation.asm.msl23.frag | 187 + .../asm/frag/sample-and-compare.asm.frag | 22 + .../frag/single-function-private-lut.asm.frag | 82 + .../shaders-msl/asm/frag/srem.asm.frag | 23 + .../storage-class-output-initializer.asm.frag | 65 + .../asm/frag/texel-fetch-no-lod.asm.frag | 17 + .../asm/frag/texture-atomics.asm.frag | 121 + ...re-atomics.asm.graphics-robust-access.frag | 121 + .../asm/frag/texture-sampling-fp16.asm.frag | 22 + .../asm/frag/undef-variable-store.asm.frag | 37 + .../asm/frag/unknown-depth-state.asm.frag | 36 + .../asm/frag/unord-relational-op.asm.frag | 58 + .../shaders-msl/asm/frag/unreachable.asm.frag | 40 + .../asm/frag/vector-shuffle-oom.asm.frag | 351 + .../asm/tesc/tess-level-overrun.asm.tesc | 23 + .../tess-level-overrun.multi-patch.asm.tesc | 24 + .../asm/tese/unnamed-builtin-array.asm.tese | 23 + .../clip-distance-plain-variable.asm.vert | 49 + ...ce-plain-variable.no-user-varying.asm.vert | 45 + .../asm/vert/copy-memory-interface.asm.vert | 25 + ...act-transposed-matrix-from-struct.asm.vert | 66 + .../asm/vert/fake-builtin-input.asm.vert | 22 + .../asm/vert/invariant.msl21.asm.vert | 26 + .../asm/vert/packed-bool-to-uint.asm.vert | 38 + .../packed-bool2-to-packed_uint2.asm.vert | 38 + .../asm/vert/packing-test.asm.vert | 19 + .../vert/spec-constant-op-composite.asm.vert | 37 + .../vert/uint-vertex-id-instance-id.asm.vert | 29 + .../access-private-workgroup-in-function.comp | 38 + ...ffers-discrete.msl2.argument.discrete.comp | 42 + ...rs-image-load-store.ios.msl2.argument.comp | 11 + ...uffers-image-load-store.msl2.argument.comp | 17 + .../shaders-msl/comp/array-length.comp | 39 + .../array-length.msl2.argument.discrete.comp | 68 + .../reference/shaders-msl/comp/atomic.comp | 72 + .../reference/shaders-msl/comp/barriers.comp | 90 + .../reference/shaders-msl/comp/basic.comp | 36 + .../shaders-msl/comp/basic.dispatchbase.comp | 41 + .../comp/basic.dispatchbase.msl11.comp | 39 + .../comp/basic.inline-block.msl2.comp | 54 + .../reference/shaders-msl/comp/builtins.comp | 17 + .../comp/cfg-preserve-parameter.comp | 82 + .../shaders-msl/comp/coherent-block.comp | 17 + .../shaders-msl/comp/coherent-image.comp | 17 + .../complex-composite-constant-array.comp | 65 + .../shaders-msl/comp/complex-type-alias.comp | 69 + .../comp/composite-array-initialization.comp | 89 + ...ray-initialization.force-native-array.comp | 158 + .../shaders-msl/comp/composite-construct.comp | 76 + .../comp/copy-array-of-arrays.comp | 79 + ...py-array-of-arrays.force-native-array.comp | 364 + .../reference/shaders-msl/comp/culling.comp | 36 + .../shaders-msl/comp/defer-parens.comp | 25 + .../reference/shaders-msl/comp/dowhile.comp | 31 + .../comp/force-recompile-hooks.swizzle.comp | 74 + .../reference/shaders-msl/comp/functions.comp | 19 + ...vocation-id-writable-ssbo-in-function.comp | 32 + .../comp/global-invocation-id.comp | 26 + ...omic-automatic-bindings.argument.msl2.comp | 38 + .../comp/image-atomic-automatic-bindings.comp | 29 + .../comp/image-cube-array-load-store.comp | 14 + .../reference/shaders-msl/comp/image.comp | 13 + .../reference/shaders-msl/comp/insert.comp | 23 + .../reference/shaders-msl/comp/inverse.comp | 130 + .../shaders-msl/comp/local-invocation-id.comp | 26 + .../comp/local-invocation-index.comp | 26 + .../reference/shaders-msl/comp/mat3.comp | 18 + .../reference/shaders-msl/comp/mod.comp | 37 + .../reference/shaders-msl/comp/modf.comp | 26 + .../shaders-msl/comp/outer-product.comp | 40 + .../shaders-msl/comp/packing-test-1.comp | 38 + .../shaders-msl/comp/packing-test-2.comp | 29 + .../shaders-msl/comp/read-write-only.comp | 31 + .../shaders-msl/comp/rmw-matrix.comp | 24 + .../reference/shaders-msl/comp/rmw-opt.comp | 31 + ...alar-std450-distance-length-normalize.comp | 25 + .../comp/shared-array-of-arrays.comp | 33 + .../reference/shaders-msl/comp/shared.comp | 27 + .../comp/spec-constant-op-member-array.comp | 49 + .../comp/spec-constant-work-group-size.comp | 75 + .../storage-buffer-std140-vector-array.comp | 96 + .../shaders-msl/comp/struct-layout.comp | 28 + .../shaders-msl/comp/struct-nested.comp | 39 + .../shaders-msl/comp/struct-packing.comp | 152 + .../shaders-msl/comp/torture-loop.comp | 53 + .../shaders-msl/comp/type-alias.comp | 67 + .../reference/shaders-msl/comp/udiv.comp | 22 + .../shaders-msl/comp/writable-ssbo.comp | 26 + .../comp/extended-arithmetic.desktop.comp | 179 + .../desktop-only/frag/image-ms.desktop.frag | 13 + .../frag/query-levels.desktop.frag | 17 + .../frag/sampler-ms-query.desktop.frag | 17 + .../tesc/arrayed-output.desktop.sso.tesc | 85 + .../tesc/basic.desktop.sso.multi-patch.tesc | 47 + .../desktop-only/tesc/basic.desktop.sso.tesc | 47 + .../struct-copy.desktop.sso.multi-patch.tesc | 38 + .../tesc/struct-copy.desktop.sso.tesc | 40 + .../tese/triangle.desktop.sso.tese | 27 + .../desktop-only/vert/basic.desktop.sso.vert | 30 + ...ull-distance..no-user-varying.desktop.vert | 20 + .../vert/clip-cull-distance.desktop.vert | 24 + ...ader-draw-parameters.desktop.for-tess.vert | 20 + .../vert/shader-draw-parameters.desktop.vert | 17 + .../shaders-msl/flatten/basic.flatten.vert | 30 + .../flatten/multiindex.flatten.vert | 27 + .../flatten/push-constant.flatten.vert | 32 + .../shaders-msl/flatten/rowmajor.flatten.vert | 30 + .../shaders-msl/flatten/struct.flatten.vert | 40 + .../shaders-msl/flatten/swizzle.flatten.vert | 47 + .../shaders-msl/flatten/types.flatten.frag | 35 + .../frag/argument-buffers.msl2.argument.frag | 96 + .../frag/array-lut-no-loop-variable.frag | 62 + .../shaders-msl/frag/array-of-array-lut.frag | 68 + ...niform.msl2.argument.discrete.swizzle.frag | 117 + ...zzle-nonconstant-uniform.msl2.swizzle.frag | 108 + ...wizzle.msl2.argument.discrete.swizzle.frag | 114 + ...array-of-texture-swizzle.msl2.swizzle.frag | 98 + .../frag/barycentric-nv-nopersp.msl22.frag | 31 + .../frag/barycentric-nv.msl22.frag | 31 + .../reference/shaders-msl/frag/basic.frag | 23 + .../binary-func-unpack-pack-arguments.frag | 28 + .../frag/binary-unpack-pack-arguments.frag | 28 + .../shaders-msl/frag/bitcasting.1d-as-2d.frag | 30 + .../shaders-msl/frag/bitcasting.frag | 30 + .../shaders-msl/frag/buffer-read-write.frag | 27 + ...ead-write.texture-buffer-native.msl21.frag | 18 + .../reference/shaders-msl/frag/builtins.frag | 24 + .../frag/clip-distance-varying.frag | 67 + .../complex-expression-in-access-chain.frag | 32 + .../composite-extract-forced-temporary.frag | 23 + .../shaders-msl/frag/constant-array.frag | 83 + .../shaders-msl/frag/constant-composites.frag | 74 + .../control-dependent-in-branch.desktop.frag | 45 + .../shaders-msl/frag/depth-greater-than.frag | 17 + .../shaders-msl/frag/depth-less-than.frag | 17 + .../frag/disable-frag-output.frag-output.frag | 35 + .../frag/dual-source-blending.frag | 19 + .../frag/early-fragment-tests.frag | 17 + .../shaders-msl/frag/false-loop-init.frag | 35 + .../shaders-msl/frag/flush_params.frag | 40 + .../frag/for-loop-continue-control-flow.frag | 42 + .../shaders-msl/frag/for-loop-init.frag | 58 + .../shaders-msl/frag/fp16-packing.frag | 25 + ...agment-component-padding.pad-fragment.frag | 82 + .../shaders-msl/frag/front-facing.frag | 30 + .../shaders-msl/frag/gather-dref.frag | 22 + .../shaders-msl/frag/gather-offset.frag | 17 + .../frag/helper-invocation.msl21.frag | 40 + ....device-argument-buffer.argument.msl2.frag | 58 + .../shaders-msl/frag/illegal-name-test-0.frag | 21 + .../frag/image-query-lod.msl22.frag | 78 + .../reference/shaders-msl/frag/in_block.frag | 32 + .../reference/shaders-msl/frag/in_mat.frag | 37 + ...t-attachment-ms.arrayed-subpass.msl21.frag | 26 + .../shaders-msl/frag/input-attachment-ms.frag | 26 + .../input-attachment-ms.multiview.msl21.frag | 27 + .../input-attachment.arrayed-subpass.frag | 25 + .../shaders-msl/frag/input-attachment.frag | 25 + .../frag/input-attachment.multiview.frag | 26 + .../frag/interpolation-qualifiers-block.frag | 47 + .../frag/interpolation-qualifiers.frag | 28 + .../shaders-msl/frag/lut-promotion.frag | 92 + .../reference/shaders-msl/frag/mix.frag | 30 + .../reference/shaders-msl/frag/mrt-array.frag | 94 + .../frag/nonuniform-qualifier.msl2.frag | 56 + .../packed-expression-vector-shuffle.frag | 25 + .../shaders-msl/frag/packing-test-3.frag | 55 + ...pixel-interlock-ordered.msl2.argument.frag | 53 + .../frag/pixel-interlock-ordered.msl2.frag | 41 + .../reference/shaders-msl/frag/pls.frag | 31 + .../frag/post-depth-coverage.ios.msl2.frag | 17 + .../frag/post-depth-coverage.msl23.frag | 17 + ...rivate-variable-prototype-declaration.frag | 41 + .../shaders-msl/frag/readonly-ssbo.frag | 30 + ...e-depth-propagate-state-from-resource.frag | 43 + .../sample-depth-separate-image-sampler.frag | 31 + ...ple-mask-in-and-out.fixed-sample-mask.frag | 20 + ...ample-mask-not-used.fixed-sample-mask.frag | 19 + .../frag/sample-mask.fixed-sample-mask.frag | 20 + .../shaders-msl/frag/sample-mask.frag | 19 + .../frag/sample-position-func.frag | 32 + .../shaders-msl/frag/sample-position.frag | 18 + .../frag/sampler-1d-lod.1d-as-2d.frag | 22 + .../shaders-msl/frag/sampler-1d-lod.frag | 22 + .../sampler-compare-bias.msl23.1d-as-2d.frag | 22 + .../sampler-compare-cascade-gradient.frag | 22 + .../sampler-compare-cascade-gradient.ios.frag | 22 + ...ampler-compare-cascade-gradient.msl23.frag | 22 + .../frag/sampler-image-arrays.msl2.frag | 48 + .../shaders-msl/frag/sampler-ms.frag | 18 + .../reference/shaders-msl/frag/sampler.frag | 32 + .../frag/scalar-refract-reflect.frag | 49 + .../frag/separate-image-sampler-argument.frag | 25 + .../frag/shader-arithmetic-8bit.frag | 102 + .../frag/spec-constant-block-size.frag | 32 + .../frag/spec-constant-ternary.frag | 22 + .../frag/stencil-export.msl21.frag | 30 + .../frag/subgroup-builtins.msl22.frag | 18 + .../frag/switch-unsigned-case.frag | 35 + .../reference/shaders-msl/frag/swizzle.frag | 28 + .../frag/texel-fetch-offset.1d-as-2d.frag | 18 + .../shaders-msl/frag/texel-fetch-offset.frag | 18 + .../shaders-msl/frag/texture-cube-array.frag | 25 + ...ure-cube-array.ios.emulate-cube-array.frag | 61 + .../frag/texture-multisample-array.msl21.frag | 23 + .../shaders-msl/frag/texture-proj-shadow.frag | 29 + .../shaders-msl/frag/ubo_layout.frag | 37 + .../shaders-msl/frag/unary-enclose.frag | 26 + .../frag/vecsize-mismatch.shader-inputs.frag | 75 + .../frag/write-depth-in-function.frag | 27 + .../intel/shader-integer-functions2.asm.comp | 31 + .../legacy/vert/transpose.legacy.vert | 33 + .../shaders-msl/tesc/basic.multi-patch.tesc | 23 + .../reference/shaders-msl/tesc/basic.tesc | 22 + ...rol-point-array-of-matrix.multi-patch.tesc | 68 + .../load-control-point-array-of-matrix.tesc | 73 + ...rol-point-array-of-struct.multi-patch.tesc | 80 + .../load-control-point-array-of-struct.tesc | 86 + .../load-control-point-array.multi-patch.tesc | 69 + .../tesc/load-control-point-array.tesc | 70 + .../tesc/matrix-output.multi-patch.tesc | 39 + .../tesc/reload-tess-level.multi-patch.tesc | 35 + .../shaders-msl/tesc/reload-tess-level.tesc | 35 + .../tesc/struct-output.multi-patch.tesc | 45 + .../tesc/water_tess.multi-patch.tesc | 135 + .../shaders-msl/tesc/water_tess.tesc | 137 + .../shaders-msl/tese/input-array.tese | 36 + .../shaders-msl/tese/input-types.tese | 87 + .../load-control-point-array-of-matrix.tese | 84 + .../tese/load-control-point-array.tese | 81 + .../shaders-msl/tese/quad.domain.tese | 24 + .../reference/shaders-msl/tese/quad.tese | 31 + .../shaders-msl/tese/set-from-function.tese | 63 + .../shaders-msl/tese/triangle-tess-level.tese | 69 + .../reference/shaders-msl/tese/triangle.tese | 17 + .../shaders-msl/tese/water_tess.tese | 75 + .../shaders-msl/vert/basic.capture.vert | 29 + .../shaders-msl/vert/basic.for-tess.vert | 31 + .../reference/shaders-msl/vert/basic.vert | 30 + .../clip-distance-block.no-user-varying.vert | 25 + .../shaders-msl/vert/clip-distance-block.vert | 29 + .../shaders-msl/vert/copy.flatten.vert | 54 + .../shaders-msl/vert/dynamic.flatten.vert | 43 + .../vert/float-math.invariant-float-math.vert | 136 + .../shaders-msl/vert/float-math.vert | 87 + .../reference/shaders-msl/vert/functions.vert | 122 + .../shaders-msl/vert/in_out_array_mat.vert | 119 + .../interface-block-block-composites.frag | 97 + .../interface-block-block-composites.vert | 105 + .../vert/interpolation-qualifiers-block.vert | 55 + .../vert/interpolation-qualifiers.vert | 36 + .../shaders-msl/vert/invariant.msl21.vert | 26 + .../vert/leaf-function.capture.vert | 37 + .../vert/leaf-function.for-tess.vert | 39 + .../no-disable-vertex-out.frag-output.vert | 28 + .../vert/no_stage_out.for-tess.vert | 23 + .../shaders-msl/vert/no_stage_out.vert | 20 + .../vert/no_stage_out.write_buff.vert | 35 + .../vert/no_stage_out.write_buff_atomic.vert | 31 + .../vert/no_stage_out.write_tex.vert | 25 + .../reference/shaders-msl/vert/out_block.vert | 41 + .../shaders-msl/vert/packed-bool-to-uint.vert | 38 + .../vert/packed-bool2-to-packed_uint2.vert | 38 + .../shaders-msl/vert/packed_matrix.vert | 55 + .../reference/shaders-msl/vert/pointsize.vert | 33 + .../vert/read-from-row-major-array.vert | 63 + .../vert/resource-arrays-leaf.ios.vert | 50 + .../shaders-msl/vert/resource-arrays.ios.vert | 42 + .../vert/return-array.force-native-array.vert | 154 + .../shaders-msl/vert/return-array.vert | 81 + .../shaders-msl/vert/set_builtin_in_func.vert | 27 + .../shaders-msl/vert/sign-int-types.vert | 60 + .../signedness-mismatch.shader-inputs.vert | 74 + ...re_buffer.texture-buffer-native.msl21.vert | 17 + .../shaders-msl/vert/texture_buffer.vert | 26 + .../shaders-msl/vert/ubo.alignment.vert | 38 + .../reference/shaders-msl/vert/ubo.vert | 30 + ...asic.multiview.no-layered.nocompat.vk.frag | 73 + .../frag/basic.multiview.nocompat.vk.frag | 73 + ...lper-forwarding.asm.vk.nocompat.msl23.frag | 22 + .../demote-to-helper.vk.nocompat.msl23.frag | 12 + ...emote-to-helper.vk.nocompat.msl23.ios.frag | 12 + .../vulkan/frag/push-constant.vk.frag | 28 + .../vulkan/frag/spec-constant.msl11.vk.frag | 126 + .../vulkan/frag/spec-constant.vk.frag | 110 + ...oup.multiview.viewfromdev.nocompat.vk.vert | 19 + .../vulkan/vert/device-group.nocompat.vk.vert | 18 + ...view.multiview.no-layered.nocompat.vk.vert | 28 + .../vert/multiview.multiview.nocompat.vk.vert | 31 + .../vulkan/vert/multiview.nocompat.vk.vert | 29 + .../vulkan/vert/small-storage.vk.vert | 48 + .../vulkan/vert/vulkan-vertex.vk.vert | 17 + ...ss-chain-dominator-in-loop-body-2.asm.comp | 30 + ...cess-chain-dominator-in-loop-body.asm.comp | 27 + ...ess-tracking-function-call-result.asm.comp | 24 + ...ased-struct-divergent-member-name.asm.comp | 25 + ...c-conversion-signs.asm.nocompat.vk.comp.vk | 46 + .../asm/comp/atomic-load-store.asm.comp | 16 + .../asm/comp/atomic-result-temporary.asm.comp | 18 + .../asm/comp/bitcast-fp16-fp32.asm.vk.comp | 24 + .../asm/comp/bitcast-fp16-fp32.asm.vk.comp.vk | 25 + .../comp/bitfield-signed-operations.asm.comp | 27 + .../shaders-no-opt/asm/comp/bitscan.asm.comp | 27 + ...-atomic-nonuniform.vk.nocompat.asm.comp.vk | 15 + ...thesized-pointer-2.asm.nocompat.vk.comp.vk | 21 + ...ynthesized-pointer.asm.nocompat.vk.comp.vk | 21 + .../comp/constant-composite-undef.asm.comp | 15 + .../asm/comp/copy-logical.spv14.asm.comp | 45 + .../extended-debug-extinst.invalid.asm.comp | 18 + .../asm/comp/glsl-signed-operations.asm.comp | 47 + .../glsl.std450.frexp-modf-struct.asm.comp | 33 + ...-atomic-nonuniform.vk.nocompat.asm.comp.vk | 12 + .../loop-variable-with-initializer.asm.comp | 12 + ...m-bracket-handling.vk.nocompat.asm.comp.vk | 55 + ...porary-copy-loop-variable.asm.invalid.comp | 20 + .../spec-constant-op-convert-sign.asm.comp | 33 + .../storage-buffer-basic.invalid.asm.comp | 28 + ...ge-sampler-dxc-min16float.asm.invalid.frag | 28 + ...pure-function-call.vk.nocompat.asm.frag.vk | 22 + .../discard-impure-function-call.asm.frag | 21 + .../do-while-continue-phi.asm.invalid.frag | 37 + .../frag/do-while-loop-inverted-test.asm.frag | 13 + .../early-conditional-return-switch.asm.frag | 67 + .../asm/frag/empty-struct-in-struct.asm.frag | 31 + ...ated-merge-block-inverted.asm.invalid.frag | 11 + ...-merge-block-non-inverted.asm.invalid.frag | 11 + .../asm/frag/for-loop-inverted-test.asm.frag | 11 + .../asm/frag/image-fetch-uint-coord.asm.frag | 12 + .../inliner-dominator-inside-loop.asm.frag | 230 + .../asm/frag/ldexp-uint-exponent.asm.frag | 11 + .../loop-merge-to-continue.asm.invalid.frag | 17 + ...bracket-handling-2.vk.nocompat.asm.frag.vk | 20 + ...lifier-propagation.vk.nocompat.asm.frag.vk | 37 + .../opaque-id-literal-alias.preserve.asm.frag | 20 + .../asm/frag/out-of-order-struct-id.asm.frag | 25 + .../asm/frag/phi.zero-initialize.asm.frag | 29 + .../frag/pixel-interlock-callstack.asm.frag | 39 + .../pixel-interlock-control-flow.asm.frag | 53 + .../pixel-interlock-split-functions.asm.frag | 49 + .../asm/frag/reserved-identifiers.asm.frag | 17 + ...lection-merge-to-continue.asm.invalid.frag | 21 + ...exture-feedback-uint-code.asm.desktop.frag | 23 + ...up-arithmetic-cast.nocompat.vk.asm.frag.vk | 24 + ...ch-block-case-fallthrough.asm.invalid.frag | 63 + .../switch-merge-to-continue.asm.invalid.frag | 30 + ...tch-single-case-multiple-exit-cfg.asm.frag | 26 + .../asm/frag/unordered-compare.asm.frag | 33 + ...tor-extract-dynamic-spec-constant.asm.frag | 27 + .../frag/vector-shuffle-undef-index.asm.frag | 12 + .../frag/while-loop-inverted-test.asm.frag | 13 + .../geom/store-uint-layer.invalid.asm.geom | 41 + .../loop-header-self-continue-break.asm.comp | 89 + .../asm/temporary.zero-initialize.asm.frag | 28 + ...input-array-builtin-array.invalid.asm.tesc | 79 + .../asm/vert/complex-link-by-name.asm.vert | 35 + ...by-name.force-flattened-io.legacy.asm.vert | 33 + .../asm/vert/empty-struct-composite.asm.vert | 13 + .../asm/vert/semantic-decoration.asm.vert | 25 + .../comp/bitcast-16bit-1.invalid.comp | 34 + .../comp/bitcast-16bit-2.invalid.comp | 39 + .../shaders-no-opt/comp/bitfield.comp | 19 + .../shaders-no-opt/comp/glsl.std450.comp | 112 + .../comp/illegal-struct-name.asm.comp | 22 + .../comp/inout-struct.invalid.comp | 65 + .../reference/shaders-no-opt/comp/loop.comp | 82 + .../reference/shaders-no-opt/comp/return.comp | 35 + ...ballot_nonuniform_invocations.invalid.comp | 11 + .../specialization-constant-evaluation.comp | 321 + ...packing-scalar.nocompat.invalid.vk.comp.vk | 105 + .../subgroups.nocompat.invalid.vk.comp.vk | 110 + .../comp/subgroups_basicvoteballot.vk.comp | 401 + .../comp/subgroups_basicvoteballot.vk.comp.vk | 45 + .../frag/16bit-constants.invalid.frag | 25 + .../frag/fp16.invalid.desktop.frag | 159 + ...h_subpassInput.vk.nocompat.invalid.frag.vk | 11 + .../shaders-no-opt/frag/fs.invalid.frag | 15 + .../shaders-no-opt/frag/image-gather.frag | 15 + ...mensional.desktop.invalid.flatten_dim.frag | 24 + .../pixel-interlock-simple-callstack.frag | 34 + ...out-ubo-std430.vk.nocompat.invalid.frag.vk | 24 + .../frag/sparse-texture-clamp.desktop.frag | 46 + .../frag/sparse-texture-feedback.desktop.frag | 105 + ...pass-input.framebuffer-fetch.nocompat.frag | 18 + ...put.framebuffer-fetch.nocompat.legacy.frag | 16 + .../frag/variables.zero-initialize.frag | 28 + ...gle-case-multiple-exit-cfg.legacy.asm.frag | 24 + .../vert/io-blocks.force-flattened-io.vert | 25 + .../vert/pass-array-by-value.vert | 27 + .../vulkan/frag/spec-constant.vk.frag | 131 + .../vulkan/frag/spec-constant.vk.frag.vk | 107 + ...bo-offset-out-of-order.vk.nocompat.frag.vk | 16 + .../aliased-entry-point-names.asm.multi.json | 55 + ...to-array-of-physical-pointer.asm.comp.json | 71 + .../asm/op-source-glsl-ssbo-1.asm.comp.json | 46 + .../asm/op-source-glsl-ssbo-2.asm.comp.json | 70 + .../asm/op-source-hlsl-uav-1.asm.comp.json | 46 + .../asm/op-source-hlsl-uav-2.asm.comp.json | 53 + .../asm/op-source-none-ssbo-1.asm.comp.json | 46 + .../asm/op-source-none-ssbo-2.asm.comp.json | 70 + .../asm/op-source-none-uav-1.asm.comp.json | 46 + .../asm/op-source-none-uav-2.asm.comp.json | 53 + .../comp/array-of-physical-pointer.comp.json | 66 + .../function-pointer.invalid.asm.comp.json | 18 + .../comp/physical-pointer.comp.json | 55 + .../comp/struct-layout.comp.json | 83 + .../comp/struct-packing.comp.json | 590 + .../workgroup-size-spec-constant.comp.json | 62 + ...mbined-texture-sampler-shadow.vk.frag.json | 37 + .../combined-texture-sampler.vk.frag.json | 50 + .../image-load-store-uint-coord.asm.frag.json | 47 + .../frag/input-attachment-ms.vk.frag.json | 31 + .../frag/input-attachment.vk.frag.json | 31 + .../frag/push-constant.vk.frag.json | 46 + ...eparate-sampler-texture-array.vk.frag.json | 85 + .../frag/spec-constant.vk.frag.json | 90 + .../rgen/acceleration_structure.vk.rgen.json | 16 + .../vert/array-size-reflection.vert.json | 78 + .../vert/read-from-row-major-array.vert.json | 67 + .../vert/stride-reflection.vert.json | 96 + .../vert/texture_buffer.vert.json | 40 + ...schain-invalid-expression.asm.invalid.frag | 369 + .../frag/array-copy-error.asm.invalid.frag | 353 + .../phi-variable-declaration.asm.invalid.frag | 353 + ...-accesschain-writethrough.asm.invalid.vert | 122 + .../asm/frag/depth-compare.asm.frag | 314 + .../asm/frag/global-constant-arrays.asm.frag | 1365 + .../padded-float-array-member-defef.asm.frag | 1414 + .../asm/frag/sample-mask-not-array.asm.frag | 503 + ...bpass-input.ios.framebuffer-fetch.asm.frag | 213 + ...ass-input.msl23.framebuffer-fetch.asm.frag | 213 + .../texture-atomics.asm.argument.msl2.frag | 130 + .../asm/frag/texture-atomics.asm.frag | 122 + ...re-atomics.asm.graphics-robust-access.frag | 122 + .../asm/tesc/hs-incorrect-base-type.asm.tesc | 399 + .../asm/tesc/hs-input-array-access.asm.tesc | 467 + .../asm/tesc/hs-texcoord-array.asm.tesc | 411 + .../tess-factor-must-be-threadgroup.asm.tesc | 178 + .../asm/tese/ds-double-gl-in-deref.asm.tese | 420 + .../asm/tese/ds-patch-input-fixes.asm.tese | 417 + .../asm/tese/ds-patch-inputs.asm.tese | 217 + .../asm/tese/ds-texcoord-array.asm.tese | 318 + .../asm/vert/array-missing-copies.asm.vert | 466 + .../asm/vert/texture-buffer.asm.vert | 387 + .../reference/shaders/amd/gcn_shader.comp | 12 + .../reference/shaders/amd/shader_ballot.comp | 32 + .../shaders/amd/shader_group_vote.comp | 18 + .../shaders/amd/shader_trinary_minmax.comp | 11 + .../asm/comp/atomic-decrement.asm.comp | 18 + .../asm/comp/atomic-increment.asm.comp | 18 + .../shaders/asm/comp/bitcast_iadd.asm.comp | 27 + .../shaders/asm/comp/bitcast_icmp.asm.comp | 27 + .../shaders/asm/comp/bitcast_iequal.asm.comp | 33 + .../shaders/asm/comp/bitcast_sar.asm.comp | 29 + .../shaders/asm/comp/bitcast_sdiv.asm.comp | 29 + .../shaders/asm/comp/bitcast_slr.asm.comp | 29 + .../asm/comp/block-name-alias-global.asm.comp | 37 + .../asm/comp/builtin-compute-bitcast.asm.comp | 13 + .../asm/comp/decoration-group.asm.comp | 38 + .../comp/global-parameter-name-alias.asm.comp | 27 + .../asm/comp/hlsl-functionality.asm.comp | 24 + .../shaders/asm/comp/logical.asm.comp | 56 + .../shaders/asm/comp/multiple-entry.asm.comp | 27 + .../shaders/asm/comp/nmin-max-clamp.asm.comp | 44 + .../shaders/asm/comp/op-phi-swap.asm.comp | 40 + .../shaders/asm/comp/quantize.asm.comp | 19 + .../asm/comp/recompile-block-naming.asm.comp | 36 + ...specialization-constant-workgroup.asm.comp | 21 + .../asm/comp/switch-break-ladder.asm.comp | 64 + .../frag/combined-sampler-reuse.vk.asm.frag | 13 + .../combined-sampler-reuse.vk.asm.frag.vk | 14 + .../frag/complex-name-workarounds.asm.frag | 28 + ...osite-construct-struct-no-swizzle.asm.frag | 19 + .../asm/frag/default-member-names.asm.frag | 32 + .../frag/do-while-statement-fallback.asm.frag | 58 + .../shaders/asm/frag/empty-struct.asm.frag | 25 + .../frag/for-loop-phi-only-continue.asm.frag | 18 + .../reference/shaders/asm/frag/frem.asm.frag | 13 + .../asm/frag/function-overload-alias.asm.frag | 39 + .../hlsl-sample-cmp-level-zero-cube.asm.frag | 17 + .../frag/hlsl-sample-cmp-level-zero.asm.frag | 27 + .../asm/frag/image-extract-reuse.asm.frag | 11 + .../frag/image-fetch-no-sampler.asm.vk.frag | 38 + .../image-fetch-no-sampler.asm.vk.frag.vk | 37 + ...etch-no-sampler.no-samplerless.asm.vk.frag | 38 + ...h-no-sampler.no-samplerless.asm.vk.frag.vk | 37 + ...uery-no-sampler.no-samplerless.vk.asm.frag | 13 + ...y-no-sampler.no-samplerless.vk.asm.frag.vk | 14 + .../frag/image-query-no-sampler.vk.asm.frag | 13 + .../image-query-no-sampler.vk.asm.frag.vk | 14 + .../asm/frag/implicit-read-dep-phi.asm.frag | 39 + .../asm/frag/inf-nan-constant-double.asm.frag | 11 + .../asm/frag/inf-nan-constant.asm.frag | 11 + .../shaders/asm/frag/invalidation.asm.frag | 15 + .../asm/frag/line-directive.line.asm.frag | 71 + .../asm/frag/locations-components.asm.frag | 25 + ...op-body-dominator-continue-access.asm.frag | 50 + .../asm/frag/loop-header-to-continue.asm.frag | 45 + .../frag/lut-promotion-initializer.asm.frag | 40 + .../asm/frag/multi-for-loop-init.asm.frag | 19 + .../asm/frag/op-constant-null.asm.frag | 23 + .../frag/op-phi-swap-continue-block.asm.frag | 24 + .../asm/frag/pack-and-unpack-uint2.asm.frag | 12 + .../shaders/asm/frag/pass-by-value.asm.frag | 21 + .../asm/frag/phi-loop-variable.asm.frag | 9 + .../asm/frag/sample-and-compare.asm.frag | 13 + ...pler-buffer-array-without-sampler.asm.frag | 28 + .../sampler-buffer-without-sampler.asm.frag | 20 + .../frag/single-function-private-lut.asm.frag | 26 + .../reference/shaders/asm/frag/srem.asm.frag | 13 + .../storage-class-output-initializer.asm.frag | 11 + .../struct-composite-extract-swizzle.asm.frag | 21 + .../frag/switch-label-shared-block.asm.frag | 32 + .../asm/frag/temporary-name-alias.asm.frag | 10 + .../asm/frag/temporary-phi-hoisting.asm.frag | 26 + .../asm/frag/texel-fetch-no-lod.asm.frag | 11 + .../frag/texture-sampling-fp16.asm.vk.frag | 19 + .../frag/texture-sampling-fp16.asm.vk.frag.vk | 20 + .../asm/frag/undef-variable-store.asm.frag | 29 + .../asm/frag/unknown-depth-state.asm.vk.frag | 23 + .../frag/unknown-depth-state.asm.vk.frag.vk | 24 + .../shaders/asm/frag/unreachable.asm.frag | 28 + .../asm/frag/vector-shuffle-oom.asm.frag | 316 + .../asm/geom/block-name-namespace.asm.geom | 33 + .../inout-split-access-chain-handle.asm.geom | 23 + .../geom/split-access-chain-input.asm.geom | 9 + .../asm/geom/unroll-glposition-load.asm.geom | 35 + .../asm/tese/unroll-input-array-load.asm.tese | 48 + .../shaders/asm/vert/empty-io.asm.vert | 34 + ...act-transposed-matrix-from-struct.asm.vert | 52 + .../asm/vert/global-builtin.sso.asm.vert | 35 + .../shaders/asm/vert/invariant-block.asm.vert | 9 + .../asm/vert/invariant-block.sso.asm.vert | 17 + .../shaders/asm/vert/invariant.asm.vert | 15 + .../shaders/asm/vert/invariant.sso.asm.vert | 20 + .../spec-constant-op-composite.asm.vk.vert | 34 + .../spec-constant-op-composite.asm.vk.vert.vk | 25 + .../vert/uint-vertex-id-instance-id.asm.vert | 25 + .../reference/shaders/comp/atomic.comp | 49 + .../reference/shaders/comp/bake_gradient.comp | 39 + .../reference/shaders/comp/barriers.comp | 77 + .../reference/shaders/comp/basic.comp | 29 + .../reference/shaders/comp/casts.comp | 19 + .../shaders/comp/cfg-preserve-parameter.comp | 74 + .../reference/shaders/comp/cfg.comp | 81 + .../shaders/comp/coherent-block.comp | 13 + .../shaders/comp/coherent-image.comp | 15 + .../comp/composite-array-initialization.comp | 38 + .../shaders/comp/composite-construct.comp | 40 + .../reference/shaders/comp/culling.comp | 29 + .../reference/shaders/comp/defer-parens.comp | 21 + .../reference/shaders/comp/dowhile.comp | 29 + .../shaders/comp/generate_height.comp | 95 + .../reference/shaders/comp/image.comp | 12 + .../reference/shaders/comp/insert.comp | 19 + .../reference/shaders/comp/mat3.comp | 14 + .../reference/shaders/comp/mod.comp | 24 + .../reference/shaders/comp/modf.comp | 22 + .../reference/shaders/comp/outer-product.comp | 36 + .../shaders/comp/read-write-only.comp | 27 + .../reference/shaders/comp/rmw-matrix.comp | 20 + .../reference/shaders/comp/rmw-opt.comp | 27 + ...alar-std450-distance-length-normalize.comp | 19 + .../reference/shaders/comp/shared.comp | 24 + .../shaders/comp/ssbo-array-length.comp | 14 + .../reference/shaders/comp/ssbo-array.comp | 14 + .../reference/shaders/comp/struct-layout.comp | 24 + .../shaders/comp/struct-packing.comp | 104 + .../reference/shaders/comp/torture-loop.comp | 49 + .../reference/shaders/comp/type-alias.comp | 49 + .../reference/shaders/comp/udiv.comp | 18 + .../desktop-only/comp/enhanced-layouts.comp | 40 + .../comp/extended-arithmetic.desktop.comp | 159 + .../desktop-only/comp/fp64.desktop.comp | 83 + .../image-formats.desktop.noeliminate.comp | 47 + .../desktop-only/comp/int64.desktop.comp | 52 + .../frag/clip-cull-distance.desktop.frag | 12 + .../control-dependent-in-branch.desktop.frag | 37 + .../frag/depth-greater-than.desktop.frag | 9 + .../frag/depth-less-than.desktop.frag | 9 + .../frag/dual-source-blending.desktop.frag | 11 + .../frag/hlsl-uav-block-alias.asm.frag | 24 + .../desktop-only/frag/image-ms.desktop.frag | 13 + .../frag/image-query.desktop.frag | 53 + .../shaders/desktop-only/frag/image-size.frag | 12 + .../image-size.no-qualifier-deduction.frag | 12 + .../frag/in-block-qualifiers.frag | 21 + .../frag/layout-component.desktop.frag | 16 + .../frag/query-levels.desktop.frag | 11 + .../desktop-only/frag/query-lod.desktop.frag | 12 + .../frag/sampler-ms-query.desktop.frag | 14 + .../frag/stencil-export.desktop.frag | 13 + .../frag/texture-proj-shadow.desktop.frag | 26 + .../desktop-only/geom/basic.desktop.sso.geom | 35 + .../geom/viewport-index.desktop.geom | 9 + .../desktop-only/tesc/basic.desktop.sso.tesc | 27 + .../tese/triangle.desktop.sso.tese | 18 + .../desktop-only/vert/basic.desktop.sso.vert | 22 + .../vert/clip-cull-distance.desktop.sso.vert | 20 + .../vert/clip-cull-distance.desktop.vert | 15 + .../vert/out-block-qualifiers.vert | 27 + ...shader-draw-parameters-450.desktop.vk.vert | 24 + ...der-draw-parameters-450.desktop.vk.vert.vk | 8 + .../shader-draw-parameters.desktop.vk.vert | 24 + .../shader-draw-parameters.desktop.vk.vert.vk | 7 + .../shaders/flatten/array.flatten.vert | 12 + .../shaders/flatten/basic.flatten.vert | 13 + .../shaders/flatten/copy.flatten.vert | 30 + .../shaders/flatten/dynamic.flatten.vert | 25 + .../flatten/matrix-conversion.flatten.frag | 14 + .../shaders/flatten/matrixindex.flatten.vert | 19 + .../shaders/flatten/multiindex.flatten.vert | 10 + .../flatten/push-constant.flatten.vert | 13 + .../shaders/flatten/rowmajor.flatten.vert | 11 + .../shaders/flatten/struct.flatten.vert | 22 + .../flatten/struct.rowmajor.flatten.vert | 26 + .../shaders/flatten/swizzle.flatten.vert | 21 + .../shaders/flatten/types.flatten.frag | 14 + .../frag/array-lut-no-loop-variable.frag | 15 + .../avoid-expression-lowering-to-loop.frag | 26 + .../shaders/frag/barycentric-nv.frag | 20 + .../reference/shaders/frag/basic.frag | 15 + .../complex-expression-in-access-chain.frag | 24 + .../composite-extract-forced-temporary.frag | 15 + .../shaders/frag/constant-array.frag | 29 + .../shaders/frag/constant-composites.frag | 23 + .../shaders/frag/false-loop-init.frag | 25 + .../reference/shaders/frag/flush_params.frag | 30 + .../frag/for-loop-continue-control-flow.frag | 34 + .../reference/shaders/frag/for-loop-init.frag | 52 + .../reference/shaders/frag/frexp-modf.frag | 43 + .../reference/shaders/frag/front-facing.frag | 20 + .../reference/shaders/frag/gather-dref.frag | 14 + .../reference/shaders/frag/ground.frag | 62 + .../shaders/frag/helper-invocation.frag | 28 + ...temporary-use-continue-block-as-value.frag | 31 + .../frag/image-load-store-uint-coord.asm.frag | 26 + ...-loop-dominated-variable-preservation.frag | 30 + .../loop-dominator-and-switch-default.frag | 50 + .../reference/shaders/frag/lut-promotion.frag | 40 + .../reference/shaders/frag/mix.frag | 20 + .../shaders/frag/partial-write-preserve.frag | 109 + .../shaders/frag/pixel-interlock-ordered.frag | 23 + .../frag/pixel-interlock-unordered.frag | 23 + .../reference/shaders/frag/pls.frag | 21 + .../shaders/frag/post-depth-coverage-es.frag | 14 + .../shaders/frag/post-depth-coverage.frag | 15 + .../reference/shaders/frag/round-even.frag | 12 + .../reference/shaders/frag/round.frag | 12 + .../frag/sample-interlock-ordered.frag | 23 + .../frag/sample-interlock-unordered.frag | 23 + .../shaders/frag/sample-parameter.frag | 13 + .../reference/shaders/frag/sampler-ms.frag | 14 + .../reference/shaders/frag/sampler-proj.frag | 16 + .../reference/shaders/frag/sampler.frag | 21 + .../shaders/frag/scalar-refract-reflect.frag | 13 + .../frag/selection-block-dominator.frag | 20 + .../frag/struct-type-unrelated-alias.frag | 18 + .../shaders/frag/switch-unsigned-case.frag | 29 + .../reference/shaders/frag/swizzle.frag | 20 + .../shaders/frag/texel-fetch-offset.frag | 14 + .../frag/ubo-load-row-major-workaround.frag | 48 + .../reference/shaders/frag/ubo_layout.frag | 26 + .../reference/shaders/frag/unary-enclose.frag | 16 + .../reference/shaders/geom/basic.geom | 26 + .../shaders/geom/geometry-passthrough.geom | 27 + .../shaders/geom/lines-adjacency.geom | 26 + .../reference/shaders/geom/lines.geom | 23 + .../reference/shaders/geom/multi-stream.geom | 14 + .../reference/shaders/geom/points.geom | 26 + .../shaders/geom/single-invocation.geom | 26 + .../geom/transform-feedback-streams.geom | 26 + .../shaders/geom/triangles-adjacency.geom | 26 + .../reference/shaders/geom/triangles.geom | 26 + .../legacy/fragment/explicit-lod.legacy.frag | 12 + .../legacy/fragment/explicit-lod.legacy.vert | 11 + .../shaders/legacy/fragment/fma.legacy.frag | 13 + .../legacy/fragment/io-blocks.legacy.frag | 12 + .../multiple-struct-flattening.legacy.frag | 39 + .../shaders/legacy/fragment/round.legacy.frag | 13 + .../fragment/struct-varying.legacy.frag | 22 + .../legacy/fragment/switch.legacy.frag | 78 + .../legacy/vert/implicit-lod.legacy.vert | 9 + .../shaders/legacy/vert/io-block.legacy.vert | 13 + .../struct-flatten-inner-array.legacy.vert | 18 + ...flatten-stores-multi-dimension.legacy.vert | 49 + .../legacy/vert/struct-varying.legacy.vert | 26 + .../legacy/vert/switch-nested.legacy.vert | 41 + .../shaders/legacy/vert/transpose.legacy.vert | 37 + .../reference/shaders/tesc/basic.tesc | 17 + .../reference/shaders/tesc/water_tess.tesc | 116 + .../reference/shaders/tese/ccw.tese | 9 + .../reference/shaders/tese/cw.tese | 9 + .../reference/shaders/tese/equal.tese | 9 + .../shaders/tese/fractional_even.tese | 9 + .../shaders/tese/fractional_odd.tese | 9 + .../reference/shaders/tese/input-array.tese | 11 + .../reference/shaders/tese/line.tese | 9 + .../shaders/tese/load-array-of-array.tese | 16 + .../shaders/tese/patch-input-array.tese | 10 + .../reference/shaders/tese/triangle.tese | 9 + .../reference/shaders/tese/water_tess.tese | 61 + .../reference/shaders/vert/basic.vert | 17 + .../reference/shaders/vert/ground.vert | 116 + .../reference/shaders/vert/invariant.vert | 19 + .../reference/shaders/vert/ocean.vert | 140 + .../vert/read-from-row-major-array.vert | 47 + .../reference/shaders/vert/return-array.vert | 23 + .../shaders/vert/texture_buffer.vert | 11 + .../vert/transform-feedback-decorations.vert | 22 + .../reference/shaders/vert/ubo.vert | 17 + ...ay-of-buffer-reference.nocompat.vk.comp.vk | 25 + ...ffer-reference-bitcast.nocompat.vk.comp.vk | 26 + .../comp/buffer-reference.nocompat.vk.comp.vk | 56 + .../spec-constant-op-member-array.vk.comp | 47 + .../spec-constant-op-member-array.vk.comp.vk | 35 + .../spec-constant-work-group-size.vk.comp | 34 + .../spec-constant-work-group-size.vk.comp.vk | 24 + .../combined-texture-sampler-shadow.vk.frag | 31 + ...combined-texture-sampler-shadow.vk.frag.vk | 32 + .../frag/combined-texture-sampler.vk.frag | 48 + .../frag/combined-texture-sampler.vk.frag.vk | 48 + ...-helper-forwarding.asm.vk.nocompat.frag.vk | 15 + .../frag/demote-to-helper.vk.nocompat.frag.vk | 10 + .../vulkan/frag/desktop-mediump.vk.frag | 12 + .../vulkan/frag/desktop-mediump.vk.frag.vk | 12 + .../vulkan/frag/input-attachment-ms.vk.frag | 12 + .../frag/input-attachment-ms.vk.frag.vk | 12 + .../vulkan/frag/input-attachment.vk.frag | 14 + .../vulkan/frag/input-attachment.vk.frag.vk | 14 + .../nonuniform-qualifier.vk.nocompat.frag.vk | 37 + .../push-constant-as-ubo.push-ubo.vk.frag | 14 + .../push-constant-as-ubo.push-ubo.vk.frag.vk | 14 + .../shaders/vulkan/frag/push-constant.vk.frag | 20 + .../vulkan/frag/push-constant.vk.frag.vk | 18 + .../separate-combined-fake-overload.vk.frag | 22 + ...separate-combined-fake-overload.vk.frag.vk | 23 + .../separate-sampler-texture-array.vk.frag | 44 + .../separate-sampler-texture-array.vk.frag.vk | 45 + .../frag/separate-sampler-texture.vk.frag | 37 + .../frag/separate-sampler-texture.vk.frag.vk | 38 + ...shader-arithmetic-8bit.nocompat.vk.frag.vk | 88 + .../frag/spec-constant-block-size.vk.frag | 22 + .../frag/spec-constant-block-size.vk.frag.vk | 19 + .../vulkan/frag/spec-constant-ternary.vk.frag | 16 + .../frag/spec-constant-ternary.vk.frag.vk | 13 + .../hit_attribute_block.nocompat.vk.rchit.vk | 24 + ...ute_block_in_function.nocompat.vk.rchit.vk | 29 + .../hit_attribute_plain.nocompat.vk.rchit.vk | 11 + .../hit_attribute_struct.nocompat.vk.rchit.vk | 17 + .../rchit/hit_kind.nocompat.vk.rchit.vk | 10 + .../vulkan/rchit/hit_t.nocompat.vk.rchit.vk | 10 + .../incoming_ray_flags.nocompat.vk.rchit.vk | 10 + .../instance_custom_id.nocompat.vk.rchit.vk | 10 + .../rchit/instance_id.nocompat.vk.rchit.vk | 10 + .../object_ray_direction.nocompat.vk.rchit.vk | 10 + .../object_ray_origin.nocompat.vk.rchit.vk | 10 + .../object_to_world.nocompat.vk.rchit.vk | 10 + .../rchit/payloads.nocompat.vk.rchit.vk | 20 + .../rchit/primitive_id.nocompat.vk.rchit.vk | 10 + .../rchit/ray_tmax.nocompat.vk.rchit.vk | 10 + .../rchit/ray_tmin.nocompat.vk.rchit.vk | 10 + .../rchit/ray_tracing.nocompat.vk.rchit.vk | 10 + .../world_ray_direction.nocompat.vk.rchit.vk | 10 + .../world_ray_origin.nocompat.vk.rchit.vk | 10 + .../world_to_object.nocompat.vk.rchit.vk | 10 + .../rgen/execute_callable.nocompat.vk.rgen.vk | 17 + .../vulkan/rgen/launch_id.nocompat.vk.rgen.vk | 10 + .../rgen/launch_size.nocompat.vk.rgen.vk | 10 + .../vulkan/rgen/payloads.nocompat.vk.rgen.vk | 47 + .../vulkan/rgen/pure_call.nocompat.vk.rgen.vk | 21 + .../rgen/ray_tracing.nocompat.vk.rgen.vk | 17 + .../shader_record_buffer.nocompat.vk.rgen.vk | 17 + .../rmiss/ray_tracing.nocompat.vk.rmiss.vk | 10 + .../vert/device-group.nocompat.vk.vert.vk | 8 + .../vulkan/vert/multiview.nocompat.vk.vert.vk | 15 + .../shaders/vulkan/vert/small-storage.vk.vert | 59 + .../vulkan/vert/small-storage.vk.vert.vk | 55 + .../shaders/vulkan/vert/vulkan-vertex.vk.vert | 16 + .../vulkan/vert/vulkan-vertex.vk.vert.vk | 7 + third_party/spirv-cross/samples/cpp/Makefile | 28 + .../spirv-cross/samples/cpp/atomics.comp | 29 + .../spirv-cross/samples/cpp/atomics.cpp | 90 + .../spirv-cross/samples/cpp/multiply.comp | 22 + .../spirv-cross/samples/cpp/multiply.cpp | 91 + .../spirv-cross/samples/cpp/shared.comp | 36 + .../spirv-cross/samples/cpp/shared.cpp | 89 + ...access-chain-load-store-composite.asm.comp | 118 + ...ased-struct-divergent-member-name.asm.comp | 77 + .../asm/comp/atomic-load-store.asm.comp | 48 + .../asm/comp/atomic-result-temporary.asm.comp | 59 + .../comp/bitfield-signed-operations.asm.comp | 97 + .../asm/comp/bitscan.asm.comp | 72 + ...onuniform.asm.sm51.nonuniformresource.comp | 53 + .../comp/constant-composite-undef.asm.comp | 40 + .../asm/comp/glsl-signed-operations.asm.comp | 123 + ....std450.frexp-modf-struct.fxconly.asm.comp | 55 + ...onuniform.asm.sm51.nonuniformresource.comp | 55 + ...lization-constant-workgroup.nofxc.asm.comp | 47 + ...torage-buffer-basic.invalid.nofxc.asm.comp | 57 + .../asm/frag/empty-struct-in-struct.asm.frag | 61 + .../asm/frag/image-fetch-uint-coord.asm.frag | 44 + ...andling-2.nonuniformresource.sm51.asm.frag | 96 + ...opagation.nonuniformresource.sm51.asm.frag | 159 + .../asm/frag/phi.zero-initialize.asm.frag | 69 + ...-interlock-callstack.sm51.fxconly.asm.frag | 89 + ...terlock-control-flow.sm51.fxconly.asm.frag | 121 + ...lock-split-functions.sm51.fxconly.asm.frag | 102 + ...element-array-matrix-rule.invalid.asm.frag | 77 + ...rithmetic-cast.invalid.nofxc.sm60.asm.frag | 65 + ...ch-block-case-fallthrough.asm.invalid.frag | 80 + .../asm/frag/unordered-compare.asm.frag | 177 + .../frag/vector-shuffle-undef-index.asm.frag | 42 + .../asm/temporary.zero-initialize.asm.frag | 93 + .../asm/vert/complex-link-by-name.asm.vert | 119 + .../asm/vert/empty-struct-composite.asm.vert | 36 + .../shaders-hlsl-no-opt/comp/bitfield.comp | 44 + .../comp/glsl.std450.fxconly.comp | 130 + .../comp/illegal-struct-name.asm.comp | 62 + .../comp/subgroups.invalid.nofxc.sm60.comp | 131 + .../frag/cbuffer-packing-straddle.frag | 50 + .../constant-buffer-array.invalid.sm51.frag | 32 + .../frag/fp16.invalid.desktop.frag | 156 + .../shaders-hlsl-no-opt/frag/frag-coord.frag | 8 + ...types.fxconly.nofxc.sm62.native-16bit.frag | 72 + ...terlock-simple-callstack.sm51.fxconly.frag | 31 + .../frag/spec-constant.frag | 80 + .../frag/ubo-offset-out-of-order.frag | 16 + .../frag/variables.zero-initialize.frag | 21 + ...rix-input.flatten-matrix-vertex-input.vert | 13 + .../vert/pass-array-by-value.vert | 26 + .../asm/comp/access-chain-invalidate.asm.comp | 61 + .../asm/comp/atomic-decrement.asm.comp | 71 + .../asm/comp/atomic-increment.asm.comp | 71 + .../asm/comp/bitcast_icmp.asm.comp | 101 + .../asm/comp/block-name-alias-global.asm.comp | 119 + .../asm/comp/control-flow-hints.asm.comp | 146 + .../comp/global-parameter-name-alias.asm.comp | 102 + .../asm/comp/nmin-max-clamp.asm.comp | 203 + .../asm/frag/cbuffer-stripped.asm.frag | 55 + .../asm/frag/combined-sampler-reuse.asm.frag | 57 + .../asm/frag/empty-struct.asm.frag | 55 + .../shaders-hlsl/asm/frag/frem.asm.frag | 41 + .../asm/frag/function-overload-alias.asm.frag | 153 + .../asm/frag/image-extract-reuse.asm.frag | 41 + .../asm/frag/implicit-read-dep-phi.asm.frag | 81 + .../asm/frag/inf-nan-constant.asm.frag | 29 + .../asm/frag/line-directive.line.asm.frag | 221 + .../frag/lut-promotion-initializer.asm.frag | 195 + ...d-unpack-uint2.fxconly.nofxc.sm60.asm.frag | 55 + .../asm/frag/pass-by-value.asm.frag | 51 + .../asm/frag/sample-and-compare.asm.frag | 61 + .../frag/single-function-private-lut.asm.frag | 86 + .../shaders-hlsl/asm/frag/srem.asm.frag | 43 + .../storage-class-output-initializer.asm.frag | 41 + .../asm/frag/texel-fetch-no-lod.asm.frag | 46 + .../asm/frag/texture-sampling-fp16.asm.frag | 47 + .../asm/frag/unknown-depth-state.asm.frag | 71 + .../asm/frag/unreachable.asm.frag | 61 + ...act-transposed-matrix-from-struct.asm.vert | 141 + .../vert/spec-constant-op-composite.asm.vert | 98 + .../vert/uint-vertex-id-instance-id.asm.vert | 65 + .../asm/vert/vertex-id-instance-id.asm.vert | 53 + .../comp/access-chain-load-composite.comp | 35 + .../shaders-hlsl/comp/access-chains.comp | 24 + .../comp/access-chains.force-uav.comp | 24 + .../shaders-hlsl/comp/address-buffers.comp | 23 + .../spirv-cross/shaders-hlsl/comp/atomic.comp | 66 + .../shaders-hlsl/comp/barriers.comp | 79 + .../shaders-hlsl/comp/builtins.comp | 11 + .../comp/composite-array-initialization.comp | 29 + .../shaders-hlsl/comp/globallycoherent.comp | 25 + .../spirv-cross/shaders-hlsl/comp/image.comp | 77 + .../comp/image.nonwritable-uav-texture.comp | 77 + .../shaders-hlsl/comp/inverse.comp | 23 + .../comp/num-workgroups-alone.comp | 13 + .../comp/num-workgroups-with-builtins.comp | 13 + .../shaders-hlsl/comp/outer-product.comp | 37 + .../shaders-hlsl/comp/rmw-matrix.comp | 20 + .../shaders-hlsl/comp/rwbuffer-matrix.comp | 104 + ...alar-std450-distance-length-normalize.comp | 18 + .../spirv-cross/shaders-hlsl/comp/shared.comp | 27 + .../comp/spec-constant-op-member-array.comp | 33 + .../comp/spec-constant-work-group-size.comp | 19 + .../shaders-hlsl/comp/ssbo-array-length.comp | 12 + .../shaders-hlsl/comp/ssbo-array.comp | 29 + .../frag/array-lut-no-loop-variable.frag | 13 + .../frag/basic-color-3comp.sm30.frag | 11 + .../frag/basic-color-3comp.sm50.frag | 11 + .../spirv-cross/shaders-hlsl/frag/basic.frag | 13 + .../shaders-hlsl/frag/bit-conversions.frag | 12 + .../shaders-hlsl/frag/boolean-mix.frag | 10 + .../shaders-hlsl/frag/builtins.frag | 11 + .../shaders-hlsl/frag/bvec-operations.frag | 13 + .../shaders-hlsl/frag/clip-cull-distance.frag | 12 + .../combined-texture-sampler-parameter.frag | 31 + .../frag/combined-texture-sampler-shadow.frag | 29 + .../complex-expression-in-access-chain.frag | 29 + .../frag/constant-composites.frag | 20 + .../control-dependent-in-branch.desktop.frag | 36 + .../shaders-hlsl/frag/demote-to-helper.frag | 7 + .../shaders-hlsl/frag/depth-greater-than.frag | 8 + .../shaders-hlsl/frag/depth-less-than.frag | 8 + .../frag/dual-source-blending.frag | 10 + .../frag/early-fragment-test.frag | 7 + .../frag/for-loop-continue-control-flow.frag | 11 + .../shaders-hlsl/frag/fp16-packing.frag | 12 + .../shaders-hlsl/frag/front-facing.frag | 14 + .../frag/image-query-selective.frag | 35 + .../shaders-hlsl/frag/image-query-uav.frag | 18 + ...age-query-uav.nonwritable-uav-texture.frag | 18 + .../shaders-hlsl/frag/image-query.frag | 33 + .../frag/input-attachment-ms.frag | 15 + .../shaders-hlsl/frag/input-attachment.frag | 16 + .../shaders-hlsl/frag/io-block.frag | 16 + .../frag/legacy-tex-modifiers.sm30.frag | 13 + .../shaders-hlsl/frag/lut-promotion.frag | 44 + .../shaders-hlsl/frag/matrix-input.frag | 9 + .../spirv-cross/shaders-hlsl/frag/mod.frag | 22 + .../spirv-cross/shaders-hlsl/frag/mrt.frag | 15 + .../shaders-hlsl/frag/no-return.frag | 5 + .../shaders-hlsl/frag/no-return2.frag | 9 + ...orm-qualifier.nonuniformresource.sm51.frag | 28 + .../frag/partial-write-preserve.frag | 64 + .../pixel-interlock-ordered.sm51.fxconly.frag | 36 + .../shaders-hlsl/frag/point-coord-compat.frag | 10 + .../shaders-hlsl/frag/query-lod.desktop.frag | 10 + .../readonly-coherent-ssbo.force-uav.frag | 12 + .../frag/readonly-coherent-ssbo.frag | 12 + .../shaders-hlsl/frag/resources.frag | 27 + .../frag/row-major-layout-in-struct.frag | 29 + .../frag/sample-cmp-level-zero.frag | 27 + .../frag/sample-mask-in-and-out.frag | 9 + .../shaders-hlsl/frag/sample-mask-in.frag | 11 + .../shaders-hlsl/frag/sample-mask-out.frag | 9 + .../shaders-hlsl/frag/sampler-array.frag | 28 + .../frag/sampler-image-arrays.frag | 33 + .../frag/scalar-refract-reflect.frag | 11 + .../separate-combined-fake-overload.sm30.frag | 21 + .../frag/spec-constant-block-size.frag | 17 + .../frag/spec-constant-ternary.frag | 9 + .../frag/switch-unsigned-case.frag | 26 + .../shaders-hlsl/frag/swizzle-scalar.frag | 16 + .../shaders-hlsl/frag/tex-sampling-ms.frag | 16 + .../shaders-hlsl/frag/tex-sampling.frag | 81 + .../shaders-hlsl/frag/texel-fetch-offset.frag | 10 + .../frag/texture-proj-shadow.frag | 21 + .../texture-size-combined-image-sampler.frag | 9 + .../shaders-hlsl/frag/unary-enclose.frag | 15 + .../frag/unorm-snorm-packing.frag | 24 + .../shaders-hlsl/frag/various-glsl-ops.frag | 17 + .../spirv-cross/shaders-hlsl/vert/basic.vert | 15 + .../shaders-hlsl/vert/clip-cull-distance.vert | 11 + .../shaders-hlsl/vert/instancing.vert | 6 + .../shaders-hlsl/vert/locations.vert | 51 + .../shaders-hlsl/vert/matrix-attribute.vert | 9 + .../shaders-hlsl/vert/matrix-output.vert | 9 + .../shaders-hlsl/vert/no-input.vert | 6 + .../shaders-hlsl/vert/point-size-compat.vert | 7 + .../shaders-hlsl/vert/qualifiers.vert | 27 + .../vert/read-from-row-major-array.vert | 20 + .../shaders-hlsl/vert/return-array.vert | 22 + .../shaders-hlsl/vert/sampler-buffers.vert | 17 + .../vert/struct-composite-decl.vert | 26 + .../shaders-hlsl/vert/texture_buffer.vert | 9 + ...ased-struct-divergent-member-name.asm.comp | 77 + .../comp/arithmetic-conversion-signs.asm.comp | 131 + .../asm/comp/atomic-load-store.asm.comp | 48 + .../asm/comp/atomic-result-temporary.asm.comp | 59 + .../asm/comp/bitcast-fp16-fp32.asm.comp | 63 + .../comp/bitfield-signed-operations.asm.comp | 97 + .../asm/comp/bitscan.asm.comp | 72 + ...composite-construct-buffer-struct.asm.comp | 54 + .../comp/constant-composite-undef.asm.comp | 40 + .../asm/comp/copy-logical-2.spv14.asm.comp | 81 + .../asm/comp/copy-logical.spv14.asm.comp | 69 + .../comp/device-array-load-temporary.asm.comp | 53 + ...load-temporary.force-native-array.asm.comp | 53 + .../device-constant-array-load-store.asm.comp | 81 + ...ray-load-store.force-native-array.asm.comp | 81 + .../asm/comp/glsl-signed-operations.asm.comp | 123 + .../glsl.std450.frexp-modf-struct.asm.comp | 55 + .../storage-buffer-basic.invalid.asm.comp | 58 + .../storage-buffer-pointer-argument.asm.comp | 63 + .../asm/comp/variable-pointers.asm.comp | 152 + ...omponent-insert-packed-expression.asm.frag | 70 + .../asm/frag/empty-struct-in-struct.asm.frag | 61 + .../asm/frag/image-fetch-uint-coord.asm.frag | 44 + .../asm/frag/image-gather.asm.frag | 74 + ...mage-type-normal-comparison-usage.asm.frag | 76 + ...nput-attachment-unused-frag-coord.asm.frag | 74 + ...-frexp-scalar-access-chain-output.asm.frag | 36 + .../asm/frag/phi.zero-initialize.asm.frag | 69 + .../pixel-interlock-callstack.msl2.asm.frag | 89 + ...pixel-interlock-control-flow.msl2.asm.frag | 121 + ...el-interlock-split-functions.msl2.asm.frag | 102 + .../private-initializer-direct-store.asm.frag | 32 + .../subgroup-arithmetic-cast.msl21.asm.frag | 65 + ...ch-block-case-fallthrough.asm.invalid.frag | 80 + .../asm/frag/texture-access.swizzle.asm.frag | 364 + .../frag/vector-shuffle-undef-index.asm.frag | 42 + .../composite-extract-row-major.asm.comp | 48 + .../load-packed-no-forwarding-2.asm.comp | 56 + .../load-packed-no-forwarding-3.asm.comp | 48 + .../load-packed-no-forwarding-4.asm.comp | 61 + .../load-packed-no-forwarding-5.asm.comp | 54 + .../load-packed-no-forwarding.asm.comp | 56 + .../packed-vector-extract-insert.asm.comp | 57 + .../row-major-split-access-chain.asm.comp | 48 + .../asm/packing/scalar-array-float2.asm.frag | 54 + .../scalar-array-float3-one-element.asm.frag | 51 + .../asm/packing/scalar-array-float3.asm.frag | 54 + ...scalar-float2x2-col-major.invalid.asm.frag | 56 + .../scalar-float2x2-row-major.asm.frag | 56 + ...scalar-float2x3-col-major.invalid.asm.frag | 56 + .../scalar-float2x3-row-major.asm.frag | 56 + ...scalar-float3x2-col-major.invalid.asm.frag | 56 + .../scalar-float3x2-row-major.asm.frag | 56 + ...scalar-float3x3-col-major.invalid.asm.frag | 56 + .../scalar-float3x3-row-major.asm.frag | 56 + .../asm/temporary.zero-initialize.asm.frag | 93 + ...input-array-builtin-array.invalid.asm.tesc | 248 + ...builtin-array.invalid.multi-patch.asm.tesc | 248 + ...omposite-extract-physical-type-id.asm.vert | 63 + .../asm/vert/empty-struct-composite.asm.vert | 36 + .../op-load-forced-temporary-array.asm.frag | 60 + .../comp/array-copy-threadgroup-memory.comp | 18 + .../basic.dynamic-buffer.msl2.invalid.comp | 27 + .../comp/bitcast-16bit-1.invalid.comp | 23 + .../comp/bitcast-16bit-2.invalid.comp | 26 + .../shaders-msl-no-opt/comp/bitfield.comp | 23 + .../shaders-msl-no-opt/comp/glsl.std450.comp | 129 + .../comp/illegal-struct-name.asm.comp | 62 + .../comp/int64.invalid.msl22.comp | 65 + .../shaders-msl-no-opt/comp/loop.comp | 98 + .../shaders-msl-no-opt/comp/return.comp | 33 + ...std140-array-load-composite-construct.comp | 13 + ...ct-packing-scalar.nocompat.invalid.vk.comp | 100 + .../subgroups.nocompat.invalid.vk.msl21.comp | 138 + ...bgroups.nocompat.invalid.vk.msl21.ios.comp | 49 + .../components/fragment-input-component.frag | 10 + .../components/fragment-output-component.frag | 12 + ...ragment-output-component.pad-fragment.frag | 10 + .../components/vertex-input-component.vert | 11 + .../components/vertex-output-component.vert | 12 + .../frag/16bit-constants.invalid.frag | 14 + ...e-to-helper.vk.nocompat.msl21.invalid.frag | 8 + .../frag/depth-image-gather.asm.frag | 72 + ....msl2.argument..force-active.discrete.frag | 15 + .../frag/fp16.desktop.invalid.frag | 151 + .../shaders-msl-no-opt/frag/image-gather.frag | 14 + .../frag/in_block_assign.frag | 16 + .../frag/min-max-clamp.invalid.asm.frag | 293 + ...pixel-interlock-simple-callstack.msl2.frag | 31 + ...layout-ubo-std430.vk.nocompat.invalid.frag | 23 + .../shadow-compare-global-alias.invalid.frag | 38 + .../subgroups.nocompat.invalid.vk.msl21.frag | 131 + ...-fallback.msl20.ios.framebuffer-fetch.frag | 9 + ...ndex-fallback.msl23.framebuffer-fetch.frag | 9 + ...nction-argument.framebuffer-fetch.ios.frag | 24 + ...tion-argument.framebuffer-fetch.msl23.frag | 24 + .../frag/texture-access-int.swizzle.frag | 53 + .../frag/texture-access-leaf.swizzle.frag | 86 + .../frag/texture-access-uint.swizzle.frag | 53 + .../frag/texture-access.swizzle.frag | 79 + ...le-structs-access-chain.argument.msl2.frag | 18 + ...o-array-multiple-structs-access-chain.frag | 18 + .../frag/ubo-offset-out-of-order.frag | 16 + .../frag/variables.zero-initialize.frag | 21 + .../packing/array-of-vec3.comp | 13 + .../packing/array-of-vec4.comp | 13 + .../packing/isolated-scalar-access.comp | 25 + .../packing/load-store-col-rows.comp | 59 + .../packing/matrix-2x2-scalar.comp | 86 + .../packing/matrix-2x2-std140.comp | 85 + .../packing/matrix-2x2-std430.comp | 85 + .../packing/matrix-2x3-scalar.comp | 86 + .../packing/matrix-2x3-std140.comp | 85 + .../packing/matrix-2x3-std430.comp | 85 + .../packing/matrix-2x4-scalar.comp | 86 + .../packing/matrix-2x4-std140.comp | 85 + .../packing/matrix-2x4-std430.comp | 85 + .../packing/matrix-3x2-scalar.comp | 86 + .../packing/matrix-3x2-std140.comp | 85 + .../packing/matrix-3x2-std430.comp | 85 + .../packing/matrix-3x3-scalar.comp | 86 + .../packing/matrix-3x3-std140.comp | 85 + .../packing/matrix-3x3-std430.comp | 85 + .../packing/matrix-3x4-scalar.comp | 86 + .../packing/matrix-3x4-std140.comp | 85 + .../packing/matrix-3x4-std430.comp | 85 + .../packing/matrix-4x2-scalar.comp | 86 + .../packing/matrix-4x2-std140.comp | 85 + .../packing/matrix-4x2-std430.comp | 85 + .../packing/matrix-4x3-scalar.comp | 86 + .../packing/matrix-4x3-std140.comp | 85 + .../packing/matrix-4x3-std430.comp | 85 + .../packing/matrix-4x4-scalar.comp | 86 + .../packing/matrix-4x4-std140.comp | 85 + .../packing/matrix-4x4-std430.comp | 85 + .../packing/matrix-multiply-row-major.comp | 16 + .../matrix-multiply-unpacked-col-major-2.comp | 19 + .../matrix-multiply-unpacked-col-major.comp | 18 + .../matrix-multiply-unpacked-row-major-2.comp | 19 + .../matrix-multiply-unpacked-row-major.comp | 18 + .../packing/member-padding.comp | 14 + .../packing/std140-array-of-vectors.comp | 47 + .../packing/struct-alignment.comp | 22 + .../struct-packing-array-of-scalar.comp | 18 + .../packing/struct-packing-recursive.comp | 29 + .../packing/struct-packing.comp | 27 + .../struct-size-padding-array-of-array.comp | 45 + .../packing/struct-size-padding.comp | 45 + .../vert/functions_nested.vert | 132 + .../vert/layer.msl11.invalid.vert | 10 + ...ass-array-by-value.force-native-array.vert | 26 + .../vert/pass-array-by-value.vert | 26 + .../vert/viewport-index.msl2.invalid.vert | 10 + .../texture-access-function.swizzle.vk.frag | 90 + .../amd/shader_trinary_minmax.msl21.comp | 11 + .../asm/comp/atomic-decrement.asm.comp | 71 + .../asm/comp/atomic-increment.asm.comp | 71 + .../asm/comp/bitcast_iadd.asm.comp | 79 + .../asm/comp/bitcast_icmp.asm.comp | 101 + .../shaders-msl/asm/comp/bitcast_sar.asm.comp | 77 + .../asm/comp/bitcast_sdiv.asm.comp | 77 + .../shaders-msl/asm/comp/bitcast_slr.asm.comp | 77 + .../asm/comp/block-name-alias-global.asm.comp | 119 + .../comp/buffer-write-relative-addr.asm.comp | 93 + .../asm/comp/buffer-write.asm.comp | 59 + .../comp/global-parameter-name-alias.asm.comp | 102 + .../image-load-store-short-vector.asm.comp | 75 + .../asm/comp/multiple-entry.asm.comp | 98 + .../shaders-msl/asm/comp/quantize.asm.comp | 67 + .../asm/comp/relaxed-block-layout.asm.comp | 108 + ...specialization-constant-workgroup.asm.comp | 47 + .../struct-resource-name-aliasing.asm.comp | 49 + .../asm/comp/variable-pointers-2.asm.comp | 117 + ...ariable-pointers-store-forwarding.asm.comp | 75 + .../vector-builtin-type-cast-func.asm.comp | 147 + .../comp/vector-builtin-type-cast.asm.comp | 128 + .../asm/frag/combined-sampler-reuse.asm.frag | 57 + .../asm/frag/default-member-names.asm.frag | 57 + .../frag/descriptor-array-unnamed.asm.frag | 63 + ...isable-renamed-output.frag-output.asm.frag | 83 + .../asm/frag/empty-struct.asm.frag | 55 + .../extract-packed-from-composite.asm.frag | 108 + .../shaders-msl/asm/frag/frem.asm.frag | 41 + .../asm/frag/function-overload-alias.asm.frag | 153 + .../asm/frag/image-extract-reuse.asm.frag | 41 + .../asm/frag/implicit-read-dep-phi.asm.frag | 81 + .../asm/frag/inf-nan-constant.asm.frag | 29 + .../interpolation-qualifiers-struct.asm.frag | 85 + .../asm/frag/line-directive.line.asm.frag | 221 + .../asm/frag/locations-components.asm.frag | 103 + .../frag/lut-promotion-initializer.asm.frag | 195 + .../asm/frag/min-lod.msl22.asm.frag | 42 + .../asm/frag/op-constant-null.asm.frag | 85 + .../asm/frag/op-image-sampled-image.asm.frag | 82 + .../asm/frag/pass-by-value.asm.frag | 51 + .../asm/frag/phi-loop-variable.asm.frag | 71 + .../pull-model-interpolation.asm.msl23.frag | 425 + .../asm/frag/sample-and-compare.asm.frag | 61 + .../frag/single-function-private-lut.asm.frag | 86 + .../shaders-msl/asm/frag/srem.asm.frag | 43 + .../storage-class-output-initializer.asm.frag | 41 + .../asm/frag/texel-fetch-no-lod.asm.frag | 46 + .../asm/frag/texture-sampling-fp16.asm.frag | 47 + .../asm/frag/undef-variable-store.asm.frag | 85 + .../asm/frag/unknown-depth-state.asm.frag | 71 + .../asm/frag/unord-relational-op.asm.frag | 205 + .../shaders-msl/asm/frag/unreachable.asm.frag | 61 + .../asm/frag/vector-shuffle-oom.asm.frag | 886 + .../asm/tesc/tess-level-overrun.asm.tesc | 102 + .../tess-level-overrun.multi-patch.asm.tesc | 102 + .../asm/tese/unnamed-builtin-array.asm.tese | 96 + .../clip-distance-plain-variable.asm.vert | 91 + ...ce-plain-variable.no-user-varying.asm.vert | 91 + .../asm/vert/copy-memory-interface.asm.vert | 33 + ...act-transposed-matrix-from-struct.asm.vert | 141 + .../asm/vert/fake-builtin-input.asm.vert | 55 + .../asm/vert/invariant.msl21.asm.vert | 34 + .../asm/vert/packed-bool-to-uint.asm.vert | 111 + .../packed-bool2-to-packed_uint2.asm.vert | 113 + .../asm/vert/packing-test.asm.vert | 43 + .../vert/spec-constant-op-composite.asm.vert | 98 + .../vert/uint-vertex-id-instance-id.asm.vert | 65 + .../access-private-workgroup-in-function.comp | 31 + ...ffers-discrete.msl2.argument.discrete.comp | 27 + ...rs-image-load-store.ios.msl2.argument.comp | 10 + ...uffers-image-load-store.msl2.argument.comp | 10 + .../shaders-msl/comp/array-length.comp | 22 + .../array-length.msl2.argument.discrete.comp | 38 + .../spirv-cross/shaders-msl/comp/atomic.comp | 56 + .../shaders-msl/comp/barriers.comp | 79 + .../spirv-cross/shaders-msl/comp/basic.comp | 28 + .../shaders-msl/comp/basic.dispatchbase.comp | 29 + .../comp/basic.dispatchbase.msl11.comp | 29 + .../comp/basic.inline-block.msl2.comp | 37 + .../shaders-msl/comp/builtins.comp | 12 + .../comp/cfg-preserve-parameter.comp | 54 + .../shaders-msl/comp/coherent-block.comp | 12 + .../shaders-msl/comp/coherent-image.comp | 14 + .../complex-composite-constant-array.comp | 19 + .../shaders-msl/comp/complex-type-alias.comp | 41 + .../comp/composite-array-initialization.comp | 28 + ...ray-initialization.force-native-array.comp | 28 + .../shaders-msl/comp/composite-construct.comp | 31 + .../comp/copy-array-of-arrays.comp | 21 + ...py-array-of-arrays.force-native-array.comp | 21 + .../spirv-cross/shaders-msl/comp/culling.comp | 26 + .../shaders-msl/comp/defer-parens.comp | 30 + .../spirv-cross/shaders-msl/comp/dowhile.comp | 31 + .../comp/force-recompile-hooks.swizzle.comp | 9 + .../shaders-msl/comp/functions.comp | 12 + ...vocation-id-writable-ssbo-in-function.comp | 12 + .../comp/global-invocation-id.comp | 9 + ...omic-automatic-bindings.argument.msl2.comp | 16 + .../comp/image-atomic-automatic-bindings.comp | 16 + .../comp/image-cube-array-load-store.comp | 13 + .../spirv-cross/shaders-msl/comp/image.comp | 12 + .../spirv-cross/shaders-msl/comp/insert.comp | 18 + .../spirv-cross/shaders-msl/comp/inverse.comp | 23 + .../shaders-msl/comp/local-invocation-id.comp | 9 + .../comp/local-invocation-index.comp | 9 + .../spirv-cross/shaders-msl/comp/mat3.comp | 14 + .../spirv-cross/shaders-msl/comp/mod.comp | 26 + .../spirv-cross/shaders-msl/comp/modf.comp | 23 + .../shaders-msl/comp/outer-product.comp | 37 + .../shaders-msl/comp/packing-test-1.comp | 18 + .../shaders-msl/comp/packing-test-2.comp | 16 + .../shaders-msl/comp/read-write-only.comp | 26 + .../shaders-msl/comp/rmw-matrix.comp | 20 + .../spirv-cross/shaders-msl/comp/rmw-opt.comp | 27 + ...alar-std450-distance-length-normalize.comp | 20 + .../comp/shared-array-of-arrays.comp | 29 + .../spirv-cross/shaders-msl/comp/shared.comp | 27 + .../comp/spec-constant-op-member-array.comp | 33 + .../comp/spec-constant-work-group-size.comp | 17 + .../storage-buffer-std140-vector-array.comp | 30 + .../shaders-msl/comp/struct-layout.comp | 24 + .../shaders-msl/comp/struct-nested.comp | 20 + .../shaders-msl/comp/struct-packing.comp | 77 + .../shaders-msl/comp/torture-loop.comp | 40 + .../shaders-msl/comp/type-alias.comp | 45 + .../spirv-cross/shaders-msl/comp/udiv.comp | 17 + .../shaders-msl/comp/writable-ssbo.comp | 9 + .../comp/extended-arithmetic.desktop.comp | 41 + .../desktop-only/frag/image-ms.desktop.frag | 13 + .../frag/query-levels.desktop.frag | 11 + .../frag/sampler-ms-query.desktop.frag | 12 + .../tesc/arrayed-output.desktop.sso.tesc | 27 + .../tesc/basic.desktop.sso.multi-patch.tesc | 32 + .../desktop-only/tesc/basic.desktop.sso.tesc | 32 + .../struct-copy.desktop.sso.multi-patch.tesc | 22 + .../tesc/struct-copy.desktop.sso.tesc | 22 + .../tese/triangle.desktop.sso.tese | 22 + .../desktop-only/vert/basic.desktop.sso.vert | 20 + ...ull-distance..no-user-varying.desktop.vert | 10 + .../vert/clip-cull-distance.desktop.vert | 10 + ...ader-draw-parameters.desktop.for-tess.vert | 11 + .../vert/shader-draw-parameters.desktop.vert | 11 + .../shaders-msl/flatten/basic.flatten.vert | 16 + .../flatten/multiindex.flatten.vert | 13 + .../flatten/push-constant.flatten.vert | 17 + .../shaders-msl/flatten/rowmajor.flatten.vert | 16 + .../shaders-msl/flatten/struct.flatten.vert | 30 + .../shaders-msl/flatten/swizzle.flatten.vert | 47 + .../shaders-msl/flatten/types.flatten.frag | 27 + .../frag/argument-buffers.msl2.argument.frag | 61 + .../frag/array-lut-no-loop-variable.frag | 13 + .../shaders-msl/frag/array-of-array-lut.frag | 12 + ...niform.msl2.argument.discrete.swizzle.frag | 34 + ...zzle-nonconstant-uniform.msl2.swizzle.frag | 34 + ...wizzle.msl2.argument.discrete.swizzle.frag | 31 + ...array-of-texture-swizzle.msl2.swizzle.frag | 23 + .../frag/barycentric-nv-nopersp.msl22.frag | 17 + .../frag/barycentric-nv.msl22.frag | 17 + .../spirv-cross/shaders-msl/frag/basic.frag | 13 + .../binary-func-unpack-pack-arguments.frag | 15 + .../frag/binary-unpack-pack-arguments.frag | 15 + .../shaders-msl/frag/bitcasting.1d-as-2d.frag | 23 + .../shaders-msl/frag/bitcasting.frag | 24 + .../shaders-msl/frag/buffer-read-write.frag | 12 + ...ead-write.texture-buffer-native.msl21.frag | 12 + .../shaders-msl/frag/builtins.frag | 11 + .../frag/clip-distance-varying.frag | 10 + .../complex-expression-in-access-chain.frag | 29 + .../composite-extract-forced-temporary.frag | 11 + .../shaders-msl/frag/constant-array.frag | 21 + .../shaders-msl/frag/constant-composites.frag | 20 + .../control-dependent-in-branch.desktop.frag | 34 + .../shaders-msl/frag/depth-greater-than.frag | 7 + .../shaders-msl/frag/depth-less-than.frag | 7 + .../frag/disable-frag-output.frag-output.frag | 25 + .../frag/dual-source-blending.frag | 10 + .../frag/early-fragment-tests.frag | 9 + .../shaders-msl/frag/false-loop-init.frag | 19 + .../shaders-msl/frag/flush_params.frag | 27 + .../frag/for-loop-continue-control-flow.frag | 11 + .../shaders-msl/frag/for-loop-init.frag | 52 + .../shaders-msl/frag/fp16-packing.frag | 12 + ...agment-component-padding.pad-fragment.frag | 18 + .../shaders-msl/frag/front-facing.frag | 14 + .../shaders-msl/frag/gather-dref.frag | 11 + .../shaders-msl/frag/gather-offset.frag | 9 + .../frag/helper-invocation.msl21.frag | 21 + ....device-argument-buffer.argument.msl2.frag | 26 + .../shaders-msl/frag/illegal-name-test-0.frag | 12 + .../frag/image-query-lod.msl22.frag | 33 + .../shaders-msl/frag/in_block.frag | 14 + .../spirv-cross/shaders-msl/frag/in_mat.frag | 19 + ...t-attachment-ms.arrayed-subpass.msl21.frag | 15 + .../shaders-msl/frag/input-attachment-ms.frag | 15 + .../input-attachment-ms.multiview.msl21.frag | 15 + .../input-attachment.arrayed-subpass.frag | 16 + .../shaders-msl/frag/input-attachment.frag | 16 + .../frag/input-attachment.multiview.frag | 16 + .../frag/interpolation-qualifiers-block.frag | 19 + .../frag/interpolation-qualifiers.frag | 15 + .../shaders-msl/frag/lut-promotion.frag | 44 + .../spirv-cross/shaders-msl/frag/mix.frag | 20 + .../shaders-msl/frag/mrt-array.frag | 24 + .../frag/nonuniform-qualifier.msl2.frag | 28 + .../packed-expression-vector-shuffle.frag | 15 + .../shaders-msl/frag/packing-test-3.frag | 36 + ...pixel-interlock-ordered.msl2.argument.frag | 36 + .../frag/pixel-interlock-ordered.msl2.frag | 36 + .../spirv-cross/shaders-msl/frag/pls.frag | 20 + .../frag/post-depth-coverage.ios.msl2.frag | 11 + .../frag/post-depth-coverage.msl23.frag | 11 + ...rivate-variable-prototype-declaration.frag | 20 + .../shaders-msl/frag/readonly-ssbo.frag | 16 + ...e-depth-propagate-state-from-resource.frag | 29 + .../sample-depth-separate-image-sampler.frag | 22 + ...ple-mask-in-and-out.fixed-sample-mask.frag | 10 + ...ample-mask-not-used.fixed-sample-mask.frag | 8 + .../frag/sample-mask.fixed-sample-mask.frag | 10 + .../shaders-msl/frag/sample-mask.frag | 10 + .../frag/sample-position-func.frag | 15 + .../shaders-msl/frag/sample-position.frag | 8 + .../frag/sampler-1d-lod.1d-as-2d.frag | 12 + .../shaders-msl/frag/sampler-1d-lod.frag | 12 + .../sampler-compare-bias.msl23.1d-as-2d.frag | 11 + .../sampler-compare-cascade-gradient.frag | 11 + .../sampler-compare-cascade-gradient.ios.frag | 11 + ...ampler-compare-cascade-gradient.msl23.frag | 11 + .../frag/sampler-image-arrays.msl2.frag | 33 + .../shaders-msl/frag/sampler-ms.frag | 16 + .../spirv-cross/shaders-msl/frag/sampler.frag | 18 + .../frag/scalar-refract-reflect.frag | 11 + .../frag/separate-image-sampler-argument.frag | 16 + .../frag/shader-arithmetic-8bit.frag | 88 + .../frag/spec-constant-block-size.frag | 17 + .../frag/spec-constant-ternary.frag | 9 + .../frag/stencil-export.msl21.frag | 17 + .../frag/subgroup-builtins.msl22.frag | 10 + .../frag/switch-unsigned-case.frag | 26 + .../spirv-cross/shaders-msl/frag/swizzle.frag | 17 + .../frag/texel-fetch-offset.1d-as-2d.frag | 10 + .../shaders-msl/frag/texel-fetch-offset.frag | 10 + .../shaders-msl/frag/texture-cube-array.frag | 16 + ...ure-cube-array.ios.emulate-cube-array.frag | 16 + .../frag/texture-multisample-array.msl21.frag | 10 + .../shaders-msl/frag/texture-proj-shadow.frag | 19 + .../shaders-msl/frag/ubo_layout.frag | 24 + .../shaders-msl/frag/unary-enclose.frag | 15 + .../frag/vecsize-mismatch.shader-inputs.frag | 17 + .../frag/write-depth-in-function.frag | 14 + .../intel/shader-integer-functions2.asm.comp | 137 + .../legacy/vert/transpose.legacy.vert | 20 + .../shaders-msl/tesc/basic.multi-patch.tesc | 17 + .../spirv-cross/shaders-msl/tesc/basic.tesc | 17 + ...rol-point-array-of-matrix.multi-patch.tesc | 12 + .../load-control-point-array-of-matrix.tesc | 12 + ...rol-point-array-of-struct.multi-patch.tesc | 21 + .../load-control-point-array-of-struct.tesc | 21 + .../load-control-point-array.multi-patch.tesc | 12 + .../tesc/load-control-point-array.tesc | 12 + .../tesc/matrix-output.multi-patch.tesc | 28 + .../tesc/reload-tess-level.multi-patch.tesc | 17 + .../shaders-msl/tesc/reload-tess-level.tesc | 17 + .../tesc/struct-output.multi-patch.tesc | 36 + .../tesc/water_tess.multi-patch.tesc | 115 + .../shaders-msl/tesc/water_tess.tesc | 115 + .../shaders-msl/tese/input-array.tese | 15 + .../shaders-msl/tese/input-types.tese | 75 + .../load-control-point-array-of-matrix.tese | 13 + .../tese/load-control-point-array.tese | 13 + .../shaders-msl/tese/quad.domain.tese | 12 + .../spirv-cross/shaders-msl/tese/quad.tese | 17 + .../shaders-msl/tese/set-from-function.tese | 36 + .../shaders-msl/tese/triangle-tess-level.tese | 13 + .../shaders-msl/tese/triangle.tese | 10 + .../shaders-msl/tese/water_tess.tese | 65 + .../shaders-msl/vert/basic.capture.vert | 17 + .../shaders-msl/vert/basic.for-tess.vert | 17 + .../spirv-cross/shaders-msl/vert/basic.vert | 17 + .../clip-distance-block.no-user-varying.vert | 15 + .../shaders-msl/vert/clip-distance-block.vert | 15 + .../shaders-msl/vert/copy.flatten.vert | 34 + .../shaders-msl/vert/dynamic.flatten.vert | 33 + .../vert/float-math.invariant-float-math.vert | 25 + .../shaders-msl/vert/float-math.vert | 25 + .../shaders-msl/vert/functions.vert | 28 + .../shaders-msl/vert/in_out_array_mat.vert | 41 + .../interface-block-block-composites.frag | 17 + .../interface-block-block-composites.vert | 22 + .../vert/interpolation-qualifiers-block.vert | 26 + .../vert/interpolation-qualifiers.vert | 22 + .../shaders-msl/vert/invariant.msl21.vert | 11 + .../vert/leaf-function.capture.vert | 22 + .../vert/leaf-function.for-tess.vert | 22 + .../no-disable-vertex-out.frag-output.vert | 16 + .../vert/no_stage_out.for-tess.vert | 14 + .../shaders-msl/vert/no_stage_out.vert | 14 + .../vert/no_stage_out.write_buff.vert | 23 + .../vert/no_stage_out.write_buff_atomic.vert | 15 + .../vert/no_stage_out.write_tex.vert | 16 + .../shaders-msl/vert/out_block.vert | 22 + .../shaders-msl/vert/packed-bool-to-uint.vert | 22 + .../vert/packed-bool2-to-packed_uint2.vert | 22 + .../shaders-msl/vert/packed_matrix.vert | 41 + .../shaders-msl/vert/pointsize.vert | 15 + .../vert/read-from-row-major-array.vert | 20 + .../vert/resource-arrays-leaf.ios.vert | 27 + .../shaders-msl/vert/resource-arrays.ios.vert | 23 + .../vert/return-array.force-native-array.vert | 22 + .../shaders-msl/vert/return-array.vert | 22 + .../shaders-msl/vert/set_builtin_in_func.vert | 12 + .../shaders-msl/vert/sign-int-types.vert | 38 + .../signedness-mismatch.shader-inputs.vert | 14 + ...re_buffer.texture-buffer-native.msl21.vert | 10 + .../shaders-msl/vert/texture_buffer.vert | 10 + .../shaders-msl/vert/ubo.alignment.vert | 23 + .../spirv-cross/shaders-msl/vert/ubo.vert | 16 + ...asic.multiview.no-layered.nocompat.vk.frag | 14 + .../frag/basic.multiview.nocompat.vk.frag | 14 + ...lper-forwarding.asm.vk.nocompat.msl23.frag | 41 + .../demote-to-helper.vk.nocompat.msl23.frag | 8 + ...emote-to-helper.vk.nocompat.msl23.ios.frag | 8 + .../vulkan/frag/push-constant.vk.frag | 16 + .../vulkan/frag/spec-constant.msl11.vk.frag | 67 + .../vulkan/frag/spec-constant.vk.frag | 67 + ...oup.multiview.viewfromdev.nocompat.vk.vert | 8 + .../vulkan/vert/device-group.nocompat.vk.vert | 7 + ...view.multiview.no-layered.nocompat.vk.vert | 14 + .../vert/multiview.multiview.nocompat.vk.vert | 14 + .../vulkan/vert/multiview.nocompat.vk.vert | 14 + .../vulkan/vert/small-storage.vk.vert | 38 + .../vulkan/vert/vulkan-vertex.vk.vert | 6 + ...ss-chain-dominator-in-loop-body-2.asm.comp | 55 + ...cess-chain-dominator-in-loop-body.asm.comp | 54 + ...ess-tracking-function-call-result.asm.comp | 54 + ...ased-struct-divergent-member-name.asm.comp | 77 + ...etic-conversion-signs.asm.nocompat.vk.comp | 143 + .../asm/comp/atomic-load-store.asm.comp | 48 + .../asm/comp/atomic-result-temporary.asm.comp | 59 + .../asm/comp/bitcast-fp16-fp32.asm.vk.comp | 63 + .../comp/bitfield-signed-operations.asm.comp | 97 + .../shaders-no-opt/asm/comp/bitscan.asm.comp | 72 + ...fer-atomic-nonuniform.vk.nocompat.asm.comp | 53 + ...synthesized-pointer-2.asm.nocompat.vk.comp | 44 + ...e-synthesized-pointer.asm.nocompat.vk.comp | 51 + .../comp/constant-composite-undef.asm.comp | 40 + .../asm/comp/copy-logical.spv14.asm.comp | 69 + .../extended-debug-extinst.invalid.asm.comp | 67 + .../asm/comp/glsl-signed-operations.asm.comp | 123 + .../glsl.std450.frexp-modf-struct.asm.comp | 55 + ...age-atomic-nonuniform.vk.nocompat.asm.comp | 55 + .../loop-variable-with-initializer.asm.comp | 31 + ...form-bracket-handling.vk.nocompat.asm.comp | 298 + ...porary-copy-loop-variable.asm.invalid.comp | 68 + .../spec-constant-op-convert-sign.asm.comp | 63 + .../storage-buffer-basic.invalid.asm.comp | 57 + ...ge-sampler-dxc-min16float.asm.invalid.frag | 95 + ...-impure-function-call.vk.nocompat.asm.frag | 63 + .../discard-impure-function-call.asm.frag | 59 + .../do-while-continue-phi.asm.invalid.frag | 64 + .../frag/do-while-loop-inverted-test.asm.frag | 51 + .../early-conditional-return-switch.asm.frag | 133 + .../asm/frag/empty-struct-in-struct.asm.frag | 61 + ...ated-merge-block-inverted.asm.invalid.frag | 37 + ...-merge-block-non-inverted.asm.invalid.frag | 37 + .../asm/frag/for-loop-inverted-test.asm.frag | 35 + .../asm/frag/image-fetch-uint-coord.asm.frag | 44 + .../inliner-dominator-inside-loop.asm.frag | 646 + .../asm/frag/ldexp-uint-exponent.asm.frag | 36 + .../loop-merge-to-continue.asm.invalid.frag | 85 + ...rm-bracket-handling-2.vk.nocompat.asm.frag | 96 + ...qualifier-propagation.vk.nocompat.asm.frag | 159 + .../opaque-id-literal-alias.preserve.asm.frag | 78 + .../asm/frag/out-of-order-struct-id.asm.frag | 54 + .../asm/frag/phi.zero-initialize.asm.frag | 69 + .../frag/pixel-interlock-callstack.asm.frag | 89 + .../pixel-interlock-control-flow.asm.frag | 121 + .../pixel-interlock-split-functions.asm.frag | 102 + .../asm/frag/reserved-identifiers.asm.frag | 51 + ...lection-merge-to-continue.asm.invalid.frag | 85 + ...exture-feedback-uint-code.asm.desktop.frag | 57 + ...group-arithmetic-cast.nocompat.vk.asm.frag | 65 + ...ch-block-case-fallthrough.asm.invalid.frag | 80 + .../switch-merge-to-continue.asm.invalid.frag | 85 + ...tch-single-case-multiple-exit-cfg.asm.frag | 57 + .../asm/frag/unordered-compare.asm.frag | 177 + ...tor-extract-dynamic-spec-constant.asm.frag | 49 + .../frag/vector-shuffle-undef-index.asm.frag | 42 + .../frag/while-loop-inverted-test.asm.frag | 53 + .../geom/store-uint-layer.invalid.asm.geom | 130 + .../loop-header-self-continue-break.asm.comp | 109 + .../asm/temporary.zero-initialize.asm.frag | 93 + ...input-array-builtin-array.invalid.asm.tesc | 248 + .../asm/vert/complex-link-by-name.asm.vert | 119 + ...by-name.force-flattened-io.legacy.asm.vert | 119 + .../asm/vert/empty-struct-composite.asm.vert | 36 + .../asm/vert/semantic-decoration.asm.vert | 68 + .../comp/bitcast-16bit-1.invalid.comp | 23 + .../comp/bitcast-16bit-2.invalid.comp | 26 + .../shaders-no-opt/comp/bitfield.comp | 21 + .../shaders-no-opt/comp/glsl.std450.comp | 129 + .../comp/illegal-struct-name.asm.comp | 62 + .../comp/inout-struct.invalid.comp | 55 + .../spirv-cross/shaders-no-opt/comp/loop.comp | 98 + .../shaders-no-opt/comp/return.comp | 33 + ...ballot_nonuniform_invocations.invalid.comp | 9 + .../specialization-constant-evaluation.comp | 123 + ...ct-packing-scalar.nocompat.invalid.vk.comp | 88 + .../comp/subgroups.nocompat.invalid.vk.comp | 125 + .../comp/subgroups_basicvoteballot.vk.comp | 49 + .../frag/16bit-constants.invalid.frag | 14 + .../frag/fp16.invalid.desktop.frag | 151 + ...etch_subpassInput.vk.nocompat.invalid.frag | 10 + .../shaders-no-opt/frag/fs.invalid.frag | 14 + .../shaders-no-opt/frag/image-gather.frag | 14 + ...mensional.desktop.invalid.flatten_dim.frag | 18 + .../pixel-interlock-simple-callstack.frag | 31 + ...layout-ubo-std430.vk.nocompat.invalid.frag | 23 + .../frag/sparse-texture-clamp.desktop.frag | 23 + .../frag/sparse-texture-feedback.desktop.frag | 31 + ...pass-input.framebuffer-fetch.nocompat.frag | 12 + ...put.framebuffer-fetch.nocompat.legacy.frag | 12 + .../frag/variables.zero-initialize.frag | 21 + ...gle-case-multiple-exit-cfg.legacy.asm.frag | 57 + .../vert/io-blocks.force-flattened-io.vert | 25 + .../vert/pass-array-by-value.vert | 26 + .../vulkan/frag/spec-constant.vk.frag | 77 + .../ubo-offset-out-of-order.vk.nocompat.frag | 16 + .../spirv-cross/shaders-other/README.md | 4 + .../aliased-entry-point-names.asm | 60 + .../asm/aliased-entry-point-names.asm.multi | 60 + ...nter-to-array-of-physical-pointer.asm.comp | 51 + .../asm/op-source-glsl-ssbo-1.asm.comp | 53 + .../asm/op-source-glsl-ssbo-2.asm.comp | 65 + .../asm/op-source-hlsl-uav-1.asm.comp | 48 + .../asm/op-source-hlsl-uav-2.asm.comp | 54 + .../asm/op-source-none-ssbo-1.asm.comp | 52 + .../asm/op-source-none-ssbo-2.asm.comp | 64 + .../asm/op-source-none-uav-1.asm.comp | 47 + .../asm/op-source-none-uav-2.asm.comp | 53 + .../comp/array-of-physical-pointer.comp | 15 + .../comp/function-pointer.invalid.asm.comp | 19 + .../comp/physical-pointer.comp | 15 + .../comp/struct-layout.comp | 24 + .../comp/struct-packing.comp | 87 + .../comp/workgroup-size-spec-constant.comp | 13 + .../combined-texture-sampler-shadow.vk.frag | 29 + .../frag/combined-texture-sampler.vk.frag | 47 + .../frag/image-load-store-uint-coord.asm.frag | 103 + .../frag/input-attachment-ms.vk.frag | 10 + .../frag/input-attachment.vk.frag | 11 + .../frag/push-constant.vk.frag | 16 + .../separate-sampler-texture-array.vk.frag | 42 + .../frag/spec-constant.vk.frag | 78 + .../rgen/acceleration_structure.vk.rgen | 9 + .../vert/array-size-reflection.vert | 13 + .../vert/read-from-row-major-array.vert | 20 + .../vert/stride-reflection.vert | 14 + .../vert/texture_buffer.vert | 10 + ...schain-invalid-expression.asm.invalid.frag | 1087 + .../frag/array-copy-error.asm.invalid.frag | 878 + .../phi-variable-declaration.asm.invalid.frag | 878 + ...-accesschain-writethrough.asm.invalid.vert | 259 + .../asm/frag/depth-compare.asm.frag | 961 + .../asm/frag/global-constant-arrays.asm.frag | 3556 +++ .../padded-float-array-member-defef.asm.frag | 3694 +++ .../asm/frag/sample-mask-not-array.asm.frag | 1230 + ...bpass-input.ios.framebuffer-fetch.asm.frag | 589 + ...ass-input.msl23.framebuffer-fetch.asm.frag | 589 + .../texture-atomics.asm.argument.msl2.frag | 242 + .../asm/frag/texture-atomics.asm.frag | 242 + ...re-atomics.asm.graphics-robust-access.frag | 242 + .../asm/tesc/hs-incorrect-base-type.asm.tesc | 1158 + .../asm/tesc/hs-input-array-access.asm.tesc | 1264 + .../asm/tesc/hs-texcoord-array.asm.tesc | 1144 + .../tess-factor-must-be-threadgroup.asm.tesc | 352 + .../asm/tese/ds-double-gl-in-deref.asm.tese | 1046 + .../asm/tese/ds-patch-input-fixes.asm.tese | 1175 + .../asm/tese/ds-patch-inputs.asm.tese | 547 + .../asm/tese/ds-texcoord-array.asm.tese | 715 + .../asm/vert/array-missing-copies.asm.vert | 1131 + .../asm/vert/texture-buffer.asm.vert | 1054 + .../spirv-cross/shaders/amd/gcn_shader.comp | 13 + .../shaders/amd/shader_ballot.comp | 33 + .../shaders/amd/shader_group_vote.comp | 18 + .../shaders/amd/shader_trinary_minmax.comp | 11 + .../asm/comp/atomic-decrement.asm.comp | 71 + .../asm/comp/atomic-increment.asm.comp | 71 + .../shaders/asm/comp/bitcast_iadd.asm.comp | 79 + .../shaders/asm/comp/bitcast_icmp.asm.comp | 101 + .../shaders/asm/comp/bitcast_iequal.asm.comp | 90 + .../shaders/asm/comp/bitcast_sar.asm.comp | 77 + .../shaders/asm/comp/bitcast_sdiv.asm.comp | 77 + .../shaders/asm/comp/bitcast_slr.asm.comp | 77 + .../asm/comp/block-name-alias-global.asm.comp | 119 + .../asm/comp/builtin-compute-bitcast.asm.comp | 50 + .../asm/comp/decoration-group.asm.comp | 99 + .../comp/global-parameter-name-alias.asm.comp | 102 + .../asm/comp/hlsl-functionality.asm.comp | 64 + .../shaders/asm/comp/logical.asm.comp | 191 + .../shaders/asm/comp/multiple-entry.asm.comp | 98 + .../shaders/asm/comp/nmin-max-clamp.asm.comp | 203 + .../shaders/asm/comp/op-phi-swap.asm.comp | 63 + .../shaders/asm/comp/quantize.asm.comp | 67 + .../asm/comp/recompile-block-naming.asm.comp | 140 + ...specialization-constant-workgroup.asm.comp | 47 + .../asm/comp/switch-break-ladder.asm.comp | 92 + .../frag/combined-sampler-reuse.vk.asm.frag | 57 + .../frag/complex-name-workarounds.asm.frag | 81 + ...osite-construct-struct-no-swizzle.asm.frag | 51 + .../asm/frag/default-member-names.asm.frag | 57 + .../frag/do-while-statement-fallback.asm.frag | 76 + .../shaders/asm/frag/empty-struct.asm.frag | 55 + .../frag/for-loop-phi-only-continue.asm.frag | 48 + .../shaders/asm/frag/frem.asm.frag | 41 + .../asm/frag/function-overload-alias.asm.frag | 153 + .../hlsl-sample-cmp-level-zero-cube.asm.frag | 60 + .../frag/hlsl-sample-cmp-level-zero.asm.frag | 115 + .../asm/frag/image-extract-reuse.asm.frag | 41 + .../frag/image-fetch-no-sampler.asm.vk.frag | 163 + ...etch-no-sampler.no-samplerless.asm.vk.frag | 163 + ...uery-no-sampler.no-samplerless.vk.asm.frag | 57 + .../frag/image-query-no-sampler.vk.asm.frag | 57 + .../asm/frag/implicit-read-dep-phi.asm.frag | 81 + .../asm/frag/inf-nan-constant-double.asm.frag | 41 + .../asm/frag/inf-nan-constant.asm.frag | 29 + .../shaders/asm/frag/invalidation.asm.frag | 46 + .../asm/frag/line-directive.line.asm.frag | 221 + .../asm/frag/locations-components.asm.frag | 103 + ...op-body-dominator-continue-access.asm.frag | 190 + .../asm/frag/loop-header-to-continue.asm.frag | 132 + .../frag/lut-promotion-initializer.asm.frag | 195 + .../asm/frag/multi-for-loop-init.asm.frag | 111 + .../asm/frag/op-constant-null.asm.frag | 85 + .../frag/op-phi-swap-continue-block.asm.frag | 69 + .../asm/frag/pack-and-unpack-uint2.asm.frag | 55 + .../shaders/asm/frag/pass-by-value.asm.frag | 51 + .../asm/frag/phi-loop-variable.asm.frag | 71 + .../asm/frag/sample-and-compare.asm.frag | 61 + ...pler-buffer-array-without-sampler.asm.frag | 86 + .../sampler-buffer-without-sampler.asm.frag | 62 + .../frag/single-function-private-lut.asm.frag | 86 + .../shaders/asm/frag/srem.asm.frag | 43 + .../storage-class-output-initializer.asm.frag | 41 + .../struct-composite-extract-swizzle.asm.frag | 55 + .../frag/switch-label-shared-block.asm.frag | 45 + .../asm/frag/temporary-name-alias.asm.frag | 47 + .../asm/frag/temporary-phi-hoisting.asm.frag | 76 + .../asm/frag/texel-fetch-no-lod.asm.frag | 46 + .../frag/texture-sampling-fp16.asm.vk.frag | 47 + .../asm/frag/undef-variable-store.asm.frag | 85 + .../asm/frag/unknown-depth-state.asm.vk.frag | 71 + .../shaders/asm/frag/unreachable.asm.frag | 61 + .../asm/frag/vector-shuffle-oom.asm.frag | 886 + .../asm/geom/block-name-namespace.asm.geom | 103 + .../inout-split-access-chain-handle.asm.geom | 90 + .../geom/split-access-chain-input.asm.geom | 52 + .../asm/geom/unroll-glposition-load.asm.geom | 102 + .../asm/tese/unroll-input-array-load.asm.tese | 131 + .../shaders/asm/vert/empty-io.asm.vert | 70 + ...act-transposed-matrix-from-struct.asm.vert | 141 + .../asm/vert/global-builtin.sso.asm.vert | 68 + .../shaders/asm/vert/invariant-block.asm.vert | 44 + .../asm/vert/invariant-block.sso.asm.vert | 44 + .../shaders/asm/vert/invariant.asm.vert | 34 + .../shaders/asm/vert/invariant.sso.asm.vert | 34 + .../spec-constant-op-composite.asm.vk.vert | 98 + .../vert/uint-vertex-id-instance-id.asm.vert | 65 + .../spirv-cross/shaders/comp/atomic.comp | 56 + .../shaders/comp/bake_gradient.comp | 55 + .../spirv-cross/shaders/comp/barriers.comp | 79 + .../spirv-cross/shaders/comp/basic.comp | 28 + .../spirv-cross/shaders/comp/casts.comp | 18 + .../shaders/comp/cfg-preserve-parameter.comp | 54 + third_party/spirv-cross/shaders/comp/cfg.comp | 91 + .../shaders/comp/coherent-block.comp | 12 + .../shaders/comp/coherent-image.comp | 14 + .../comp/composite-array-initialization.comp | 29 + .../shaders/comp/composite-construct.comp | 40 + .../spirv-cross/shaders/comp/culling.comp | 26 + .../shaders/comp/defer-parens.comp | 30 + .../spirv-cross/shaders/comp/dowhile.comp | 31 + .../shaders/comp/generate_height.comp | 97 + .../spirv-cross/shaders/comp/image.comp | 12 + .../spirv-cross/shaders/comp/insert.comp | 18 + .../spirv-cross/shaders/comp/mat3.comp | 14 + third_party/spirv-cross/shaders/comp/mod.comp | 26 + .../spirv-cross/shaders/comp/modf.comp | 23 + .../shaders/comp/outer-product.comp | 37 + .../shaders/comp/read-write-only.comp | 26 + .../spirv-cross/shaders/comp/rmw-matrix.comp | 20 + .../spirv-cross/shaders/comp/rmw-opt.comp | 27 + ...alar-std450-distance-length-normalize.comp | 18 + .../spirv-cross/shaders/comp/shared.comp | 27 + .../shaders/comp/ssbo-array-length.comp | 12 + .../spirv-cross/shaders/comp/ssbo-array.comp | 14 + .../shaders/comp/struct-layout.comp | 24 + .../shaders/comp/struct-packing.comp | 86 + .../shaders/comp/torture-loop.comp | 40 + .../spirv-cross/shaders/comp/type-alias.comp | 45 + .../spirv-cross/shaders/comp/udiv.comp | 17 + .../desktop-only/comp/enhanced-layouts.comp | 39 + .../comp/extended-arithmetic.desktop.comp | 41 + .../desktop-only/comp/fp64.desktop.comp | 91 + .../image-formats.desktop.noeliminate.comp | 48 + .../desktop-only/comp/int64.desktop.comp | 55 + .../frag/clip-cull-distance.desktop.frag | 12 + .../control-dependent-in-branch.desktop.frag | 36 + .../frag/depth-greater-than.desktop.frag | 8 + .../frag/depth-less-than.desktop.frag | 8 + .../frag/dual-source-blending.desktop.frag | 10 + .../frag/hlsl-uav-block-alias.asm.frag | 56 + .../desktop-only/frag/image-ms.desktop.frag | 12 + .../frag/image-query.desktop.frag | 56 + .../shaders/desktop-only/frag/image-size.frag | 10 + .../image-size.no-qualifier-deduction.frag | 10 + .../frag/in-block-qualifiers.frag | 20 + .../frag/layout-component.desktop.frag | 14 + .../frag/query-levels.desktop.frag | 9 + .../desktop-only/frag/query-lod.desktop.frag | 10 + .../frag/sampler-ms-query.desktop.frag | 17 + .../frag/stencil-export.desktop.frag | 11 + .../frag/texture-proj-shadow.desktop.frag | 21 + .../desktop-only/geom/basic.desktop.sso.geom | 37 + .../geom/viewport-index.desktop.geom | 11 + .../desktop-only/tesc/basic.desktop.sso.tesc | 28 + .../tese/triangle.desktop.sso.tese | 22 + .../desktop-only/vert/basic.desktop.sso.vert | 20 + .../vert/clip-cull-distance.desktop.sso.vert | 13 + .../vert/clip-cull-distance.desktop.vert | 13 + .../vert/out-block-qualifiers.vert | 26 + ...shader-draw-parameters-450.desktop.vk.vert | 12 + .../shader-draw-parameters.desktop.vk.vert | 11 + .../shaders/flatten/array.flatten.vert | 19 + .../shaders/flatten/basic.flatten.vert | 16 + .../shaders/flatten/copy.flatten.vert | 34 + .../shaders/flatten/dynamic.flatten.vert | 33 + .../flatten/matrix-conversion.flatten.frag | 14 + .../shaders/flatten/matrixindex.flatten.vert | 25 + .../shaders/flatten/multiindex.flatten.vert | 13 + .../flatten/push-constant.flatten.vert | 17 + .../shaders/flatten/rowmajor.flatten.vert | 16 + .../shaders/flatten/struct.flatten.vert | 30 + .../flatten/struct.rowmajor.flatten.vert | 26 + .../shaders/flatten/swizzle.flatten.vert | 47 + .../shaders/flatten/types.flatten.frag | 27 + .../frag/array-lut-no-loop-variable.frag | 13 + .../avoid-expression-lowering-to-loop.frag | 23 + .../shaders/frag/barycentric-nv.frag | 18 + .../spirv-cross/shaders/frag/basic.frag | 13 + .../complex-expression-in-access-chain.frag | 29 + .../composite-extract-forced-temporary.frag | 11 + .../shaders/frag/constant-array.frag | 21 + .../shaders/frag/constant-composites.frag | 20 + .../shaders/frag/false-loop-init.frag | 19 + .../shaders/frag/flush_params.frag | 27 + .../frag/for-loop-continue-control-flow.frag | 11 + .../shaders/frag/for-loop-init.frag | 52 + .../spirv-cross/shaders/frag/frexp-modf.frag | 24 + .../shaders/frag/front-facing.frag | 14 + .../spirv-cross/shaders/frag/gather-dref.frag | 11 + .../spirv-cross/shaders/frag/ground.frag | 162 + .../shaders/frag/helper-invocation.frag | 21 + ...temporary-use-continue-block-as-value.frag | 24 + .../frag/image-load-store-uint-coord.asm.frag | 103 + ...-loop-dominated-variable-preservation.frag | 24 + .../loop-dominator-and-switch-default.frag | 34 + .../shaders/frag/lut-promotion.frag | 44 + third_party/spirv-cross/shaders/frag/mix.frag | 20 + .../shaders/frag/partial-write-preserve.frag | 95 + .../shaders/frag/pixel-interlock-ordered.frag | 22 + .../frag/pixel-interlock-unordered.frag | 22 + third_party/spirv-cross/shaders/frag/pls.frag | 20 + .../shaders/frag/post-depth-coverage-es.frag | 13 + .../shaders/frag/post-depth-coverage.frag | 11 + .../spirv-cross/shaders/frag/round-even.frag | 11 + .../spirv-cross/shaders/frag/round.frag | 11 + .../frag/sample-interlock-ordered.frag | 22 + .../frag/sample-interlock-unordered.frag | 22 + .../shaders/frag/sample-parameter.frag | 11 + .../spirv-cross/shaders/frag/sampler-ms.frag | 16 + .../shaders/frag/sampler-proj.frag | 12 + .../spirv-cross/shaders/frag/sampler.frag | 18 + .../shaders/frag/scalar-refract-reflect.frag | 11 + .../frag/selection-block-dominator.frag | 18 + .../frag/struct-type-unrelated-alias.frag | 19 + .../shaders/frag/switch-unsigned-case.frag | 26 + .../spirv-cross/shaders/frag/swizzle.frag | 17 + .../shaders/frag/texel-fetch-offset.frag | 10 + .../frag/ubo-load-row-major-workaround.frag | 44 + .../spirv-cross/shaders/frag/ubo_layout.frag | 24 + .../shaders/frag/unary-enclose.frag | 15 + .../spirv-cross/shaders/geom/basic.geom | 28 + .../shaders/geom/geometry-passthrough.geom | 28 + .../shaders/geom/lines-adjacency.geom | 28 + .../spirv-cross/shaders/geom/lines.geom | 24 + .../shaders/geom/multi-stream.geom | 15 + .../spirv-cross/shaders/geom/points.geom | 28 + .../shaders/geom/single-invocation.geom | 28 + .../geom/transform-feedback-streams.geom | 24 + .../shaders/geom/triangles-adjacency.geom | 28 + .../spirv-cross/shaders/geom/triangles.geom | 28 + .../legacy/fragment/explicit-lod.legacy.frag | 12 + .../legacy/fragment/explicit-lod.legacy.vert | 12 + .../shaders/legacy/fragment/fma.legacy.frag | 11 + .../legacy/fragment/io-blocks.legacy.frag | 16 + .../multiple-struct-flattening.legacy.frag | 37 + .../shaders/legacy/fragment/round.legacy.frag | 11 + .../fragment/struct-varying.legacy.frag | 25 + .../legacy/fragment/switch.legacy.frag | 43 + .../legacy/vert/implicit-lod.legacy.vert | 8 + .../shaders/legacy/vert/io-block.legacy.vert | 17 + .../struct-flatten-inner-array.legacy.vert | 15 + ...flatten-stores-multi-dimension.legacy.vert | 40 + .../legacy/vert/struct-varying.legacy.vert | 33 + .../legacy/vert/switch-nested.legacy.vert | 28 + .../shaders/legacy/vert/transpose.legacy.vert | 32 + .../spirv-cross/shaders/tesc/basic.tesc | 17 + .../spirv-cross/shaders/tesc/water_tess.tesc | 115 + third_party/spirv-cross/shaders/tese/ccw.tese | 10 + third_party/spirv-cross/shaders/tese/cw.tese | 10 + .../spirv-cross/shaders/tese/equal.tese | 10 + .../shaders/tese/fractional_even.tese | 10 + .../shaders/tese/fractional_odd.tese | 10 + .../spirv-cross/shaders/tese/input-array.tese | 10 + .../spirv-cross/shaders/tese/line.tese | 10 + .../shaders/tese/load-array-of-array.tese | 10 + .../shaders/tese/patch-input-array.tese | 9 + .../spirv-cross/shaders/tese/triangle.tese | 10 + .../spirv-cross/shaders/tese/water_tess.tese | 65 + .../spirv-cross/shaders/vert/basic.vert | 16 + .../spirv-cross/shaders/vert/ground.vert | 202 + .../spirv-cross/shaders/vert/invariant.vert | 13 + .../spirv-cross/shaders/vert/ocean.vert | 200 + .../vert/read-from-row-major-array.vert | 20 + .../shaders/vert/return-array.vert | 22 + .../shaders/vert/texture_buffer.vert | 10 + .../vert/transform-feedback-decorations.vert | 20 + third_party/spirv-cross/shaders/vert/ubo.vert | 16 + ...array-of-buffer-reference.nocompat.vk.comp | 23 + .../buffer-reference-bitcast.nocompat.vk.comp | 22 + .../comp/buffer-reference.nocompat.vk.comp | 40 + .../spec-constant-op-member-array.vk.comp | 33 + .../spec-constant-work-group-size.vk.comp | 17 + .../combined-texture-sampler-shadow.vk.frag | 29 + .../frag/combined-texture-sampler.vk.frag | 47 + ...-to-helper-forwarding.asm.vk.nocompat.frag | 41 + .../frag/demote-to-helper.vk.nocompat.frag | 8 + .../vulkan/frag/desktop-mediump.vk.frag | 11 + .../vulkan/frag/input-attachment-ms.vk.frag | 10 + .../vulkan/frag/input-attachment.vk.frag | 11 + .../nonuniform-qualifier.vk.nocompat.frag | 28 + .../push-constant-as-ubo.push-ubo.vk.frag | 13 + .../shaders/vulkan/frag/push-constant.vk.frag | 16 + .../separate-combined-fake-overload.vk.frag | 21 + .../separate-sampler-texture-array.vk.frag | 42 + .../frag/separate-sampler-texture.vk.frag | 36 + .../shader-arithmetic-8bit.nocompat.vk.frag | 88 + .../frag/spec-constant-block-size.vk.frag | 17 + .../vulkan/frag/spec-constant-ternary.vk.frag | 9 + .../hit_attribute_block.nocompat.vk.rchit | 11 + ...ribute_block_in_function.nocompat.vk.rchit | 16 + .../hit_attribute_plain.nocompat.vk.rchit | 10 + .../hit_attribute_struct.nocompat.vk.rchit | 12 + .../vulkan/rchit/hit_kind.nocompat.vk.rchit | 9 + .../vulkan/rchit/hit_t.nocompat.vk.rchit | 9 + .../incoming_ray_flags.nocompat.vk.rchit | 9 + .../instance_custom_id.nocompat.vk.rchit | 9 + .../rchit/instance_id.nocompat.vk.rchit | 9 + .../object_ray_direction.nocompat.vk.rchit | 9 + .../rchit/object_ray_origin.nocompat.vk.rchit | 9 + .../rchit/object_to_world.nocompat.vk.rchit | 9 + .../vulkan/rchit/payloads.nocompat.vk.rchit | 19 + .../rchit/primitive_id.nocompat.vk.rchit | 9 + .../vulkan/rchit/ray_tmax.nocompat.vk.rchit | 9 + .../vulkan/rchit/ray_tmin.nocompat.vk.rchit | 9 + .../rchit/ray_tracing.nocompat.vk.rchit | 9 + .../world_ray_direction.nocompat.vk.rchit | 9 + .../rchit/world_ray_origin.nocompat.vk.rchit | 9 + .../rchit/world_to_object.nocompat.vk.rchit | 9 + .../rgen/execute_callable.nocompat.vk.rgen | 16 + .../vulkan/rgen/launch_id.nocompat.vk.rgen | 9 + .../vulkan/rgen/launch_size.nocompat.vk.rgen | 9 + .../vulkan/rgen/payloads.nocompat.vk.rgen | 49 + .../vulkan/rgen/pure_call.nocompat.vk.rgen | 18 + .../vulkan/rgen/ray_tracing.nocompat.vk.rgen | 16 + .../shader_record_buffer.nocompat.vk.rgen | 16 + .../rmiss/ray_tracing.nocompat.vk.rmiss | 9 + .../vulkan/vert/device-group.nocompat.vk.vert | 7 + .../vulkan/vert/multiview.nocompat.vk.vert | 14 + .../shaders/vulkan/vert/small-storage.vk.vert | 46 + .../shaders/vulkan/vert/vulkan-vertex.vk.vert | 6 + third_party/spirv-cross/spirv.h | 2104 ++ third_party/spirv-cross/spirv.hpp | 2114 ++ third_party/spirv-cross/spirv_cfg.cpp | 397 + third_party/spirv-cross/spirv_cfg.hpp | 156 + third_party/spirv-cross/spirv_common.hpp | 1798 ++ third_party/spirv-cross/spirv_cpp.cpp | 551 + third_party/spirv-cross/spirv_cpp.hpp | 86 + third_party/spirv-cross/spirv_cross.cpp | 4870 +++ third_party/spirv-cross/spirv_cross.hpp | 1075 + third_party/spirv-cross/spirv_cross_c.cpp | 2491 ++ third_party/spirv-cross/spirv_cross_c.h | 980 + .../spirv-cross/spirv_cross_containers.hpp | 740 + .../spirv_cross_error_handling.hpp | 87 + .../spirv-cross/spirv_cross_parsed_ir.cpp | 1044 + .../spirv-cross/spirv_cross_parsed_ir.hpp | 239 + third_party/spirv-cross/spirv_cross_util.cpp | 70 + third_party/spirv-cross/spirv_cross_util.hpp | 30 + third_party/spirv-cross/spirv_glsl.cpp | 15278 ++++++++++ third_party/spirv-cross/spirv_glsl.hpp | 896 + third_party/spirv-cross/spirv_hlsl.cpp | 5768 ++++ third_party/spirv-cross/spirv_hlsl.hpp | 367 + third_party/spirv-cross/spirv_msl.cpp | 14431 +++++++++ third_party/spirv-cross/spirv_msl.hpp | 1048 + third_party/spirv-cross/spirv_parser.cpp | 1176 + third_party/spirv-cross/spirv_parser.hpp | 94 + third_party/spirv-cross/spirv_reflect.cpp | 699 + third_party/spirv-cross/spirv_reflect.hpp | 84 + third_party/spirv-cross/test_shaders.py | 894 + third_party/spirv-cross/test_shaders.sh | 29 + .../spirv-cross/tests-other/c_api_test.c | 189 + .../spirv-cross/tests-other/c_api_test.spv | Bin 0 -> 1268 bytes .../tests-other/hlsl_resource_binding.spv | Bin 0 -> 796 bytes .../tests-other/hlsl_resource_bindings.cpp | 89 + .../tests-other/hlsl_wave_mask.cpp | 73 + .../tests-other/msl_constexpr_test.cpp | 133 + .../tests-other/msl_constexpr_test.spv | Bin 0 -> 1656 bytes .../tests-other/msl_resource_binding.spv | Bin 0 -> 1424 bytes .../tests-other/msl_resource_bindings.cpp | 86 + .../tests-other/msl_ycbcr_conversion_test.cpp | 103 + .../tests-other/msl_ycbcr_conversion_test.spv | Bin 0 -> 728 bytes .../msl_ycbcr_conversion_test_2.spv | Bin 0 -> 840 bytes .../spirv-cross/tests-other/small_vector.cpp | 226 + .../spirv-cross/tests-other/typed_id_test.cpp | 49 + third_party/spirv-cross/tnt/CMakeLists.txt | 94 + third_party/spirv-cross/tnt/README.md | 31 + .../spirv-cross/update_test_shaders.sh | 4 + third_party/spirv-tools/.appveyor.yml | 90 + third_party/spirv-tools/.clang-format | 6 + third_party/spirv-tools/.gitignore | 32 + third_party/spirv-tools/.gn | 20 + third_party/spirv-tools/Android.mk | 337 + third_party/spirv-tools/BUILD.bazel | 514 + third_party/spirv-tools/BUILD.gn | 1059 + third_party/spirv-tools/CHANGES | 1157 + third_party/spirv-tools/CMakeLists.txt | 404 + third_party/spirv-tools/CODE_OF_CONDUCT.md | 1 + third_party/spirv-tools/CONTRIBUTING.md | 192 + third_party/spirv-tools/DEPS | 26 + third_party/spirv-tools/FILAMENT_README.md | 30 + third_party/spirv-tools/LICENSE | 202 + third_party/spirv-tools/PRESUBMIT.py | 40 + third_party/spirv-tools/README.md | 709 + third_party/spirv-tools/WORKSPACE | 19 + .../spirv-tools/android_test/Android.mk | 12 + .../android_test/jni/Application.mk | 5 + third_party/spirv-tools/android_test/test.cpp | 22 + third_party/spirv-tools/build_defs.bzl | 291 + .../spirv-tools/build_overrides/build.gni | 46 + .../spirv-tools/build_overrides/gtest.gni | 25 + .../build_overrides/spirv_tools.gni | 25 + .../cmake/SPIRV-Tools-shared.pc.in | 12 + .../spirv-tools/cmake/SPIRV-Tools.pc.in | 12 + .../spirv-tools/cmake/write_pkg_config.cmake | 31 + third_party/spirv-tools/codereview.settings | 2 + third_party/spirv-tools/docs/downloads.md | 14 + third_party/spirv-tools/docs/projects.md | 82 + third_party/spirv-tools/docs/spirv-fuzz.md | 83 + third_party/spirv-tools/docs/syntax.md | 238 + .../spirv-tools/examples/CMakeLists.txt | 35 + .../examples/cpp-interface/CMakeLists.txt | 19 + .../examples/cpp-interface/main.cpp | 65 + .../spirv-tools/external/CMakeLists.txt | 181 + .../external/spirv-headers/.gitattributes | 7 + .../external/spirv-headers/.gitignore | 3 + .../external/spirv-headers/BUILD.bazel | 166 + .../external/spirv-headers/BUILD.gn | 44 + .../external/spirv-headers/CMakeLists.txt | 127 + .../external/spirv-headers/CODE_OF_CONDUCT.md | 1 + .../external/spirv-headers/LICENSE | 25 + .../external/spirv-headers/README.md | 215 + .../external/spirv-headers/WORKSPACE | 0 .../spirv-headers/cmake/Config.cmake.in | 4 + .../spirv-headers/example/CMakeLists.txt | 4 + .../spirv-headers/example/example.cpp | 37 + .../include/spirv/1.0/GLSL.std.450.h | 131 + .../include/spirv/1.0/OpenCL.std.h | 210 + .../1.0/extinst.glsl.std.450.grammar.json | 642 + .../1.0/extinst.opencl.std.100.grammar.json | 1279 + .../include/spirv/1.0/spirv.core.grammar.json | 5775 ++++ .../spirv-headers/include/spirv/1.0/spirv.cs | 993 + .../spirv-headers/include/spirv/1.0/spirv.h | 993 + .../spirv-headers/include/spirv/1.0/spirv.hpp | 1002 + .../include/spirv/1.0/spirv.hpp11 | 1002 + .../include/spirv/1.0/spirv.json | 1020 + .../spirv-headers/include/spirv/1.0/spirv.lua | 949 + .../spirv-headers/include/spirv/1.0/spirv.py | 949 + .../include/spirv/1.1/GLSL.std.450.h | 131 + .../include/spirv/1.1/OpenCL.std.h | 210 + .../1.1/extinst.glsl.std.450.grammar.json | 642 + .../1.1/extinst.opencl.std.100.grammar.json | 1279 + .../include/spirv/1.1/spirv.core.grammar.json | 5938 ++++ .../spirv-headers/include/spirv/1.1/spirv.cs | 1015 + .../spirv-headers/include/spirv/1.1/spirv.h | 1015 + .../spirv-headers/include/spirv/1.1/spirv.hpp | 1024 + .../include/spirv/1.1/spirv.hpp11 | 1024 + .../include/spirv/1.1/spirv.json | 1040 + .../spirv-headers/include/spirv/1.1/spirv.lua | 971 + .../spirv-headers/include/spirv/1.1/spirv.py | 971 + .../include/spirv/1.2/GLSL.std.450.h | 131 + .../include/spirv/1.2/OpenCL.std.h | 210 + .../1.2/extinst.glsl.std.450.grammar.json | 642 + .../1.2/extinst.opencl.std.100.grammar.json | 1279 + .../include/spirv/1.2/spirv.core.grammar.json | 5986 ++++ .../spirv-headers/include/spirv/1.2/spirv.cs | 1021 + .../spirv-headers/include/spirv/1.2/spirv.h | 1021 + .../spirv-headers/include/spirv/1.2/spirv.hpp | 1030 + .../include/spirv/1.2/spirv.hpp11 | 1030 + .../include/spirv/1.2/spirv.json | 1046 + .../spirv-headers/include/spirv/1.2/spirv.lua | 977 + .../spirv-headers/include/spirv/1.2/spirv.py | 977 + .../spirv-headers/include/spirv/spir-v.xml | 205 + .../include/spirv/unified1/AMD_gcn_shader.h | 52 + .../spirv/unified1/AMD_shader_ballot.h | 53 + .../AMD_shader_explicit_vertex_parameter.h | 50 + .../unified1/AMD_shader_trinary_minmax.h | 58 + .../include/spirv/unified1/DebugInfo.h | 143 + .../include/spirv/unified1/GLSL.std.450.h | 131 + .../unified1/NonSemanticClspvReflection.h | 73 + .../spirv/unified1/NonSemanticDebugPrintf.h | 50 + .../include/spirv/unified1/OpenCL.std.h | 401 + .../spirv/unified1/OpenCLDebugInfo100.h | 156 + .../unified1/extinst.debuginfo.grammar.json | 568 + .../extinst.glsl.std.450.grammar.json | 642 + ...t.nonsemantic.clspvreflection.grammar.json | 237 + ...tinst.nonsemantic.debugprintf.grammar.json | 13 + .../extinst.opencl.debuginfo.100.grammar.json | 632 + .../extinst.opencl.std.100.grammar.json | 1279 + .../extinst.spv-amd-gcn-shader.grammar.json | 26 + ...extinst.spv-amd-shader-ballot.grammar.json | 41 + ...der-explicit-vertex-parameter.grammar.json | 14 + ...spv-amd-shader-trinary-minmax.grammar.json | 95 + .../spirv/unified1/spirv.core.grammar.json | 11779 ++++++++ .../include/spirv/unified1/spirv.cs | 1622 + .../include/spirv/unified1/spirv.h | 2185 ++ .../include/spirv/unified1/spirv.hpp | 2196 ++ .../include/spirv/unified1/spirv.hpp11 | 2196 ++ .../include/spirv/unified1/spirv.json | 1604 + .../include/spirv/unified1/spirv.lua | 1572 + .../include/spirv/unified1/spirv.py | 1572 + .../include/spirv/unified1/spv.d | 1624 + .../tools/buildHeaders/CMakeLists.txt | 26 + .../bin/generate_language_headers.py | 242 + .../buildHeaders/bin/makeExtinstHeaders.py | 28 + .../tools/buildHeaders/bin/makeHeaders | 7 + .../tools/buildHeaders/header.cpp | 835 + .../spirv-headers/tools/buildHeaders/header.h | 54 + .../tools/buildHeaders/jsonToSpirv.cpp | 495 + .../tools/buildHeaders/jsonToSpirv.h | 294 + .../spirv-headers/tools/buildHeaders/main.cpp | 127 + .../filament-specific-changes.patch | 215 + .../include/spirv-tools/instrument.hpp | 251 + .../include/spirv-tools/libspirv.h | 858 + .../include/spirv-tools/libspirv.hpp | 356 + .../include/spirv-tools/linker.hpp | 97 + .../include/spirv-tools/optimizer.hpp | 880 + .../spirv-tools/kokoro/android/build.sh | 52 + .../spirv-tools/kokoro/android/continuous.cfg | 17 + .../spirv-tools/kokoro/android/presubmit.cfg | 16 + .../spirv-tools/kokoro/check-format/build.sh | 42 + .../check-format/presubmit_check_format.cfg | 16 + third_party/spirv-tools/kokoro/img/linux.png | Bin 0 -> 17369 bytes third_party/spirv-tools/kokoro/img/macos.png | Bin 0 -> 15464 bytes .../spirv-tools/kokoro/img/windows.png | Bin 0 -> 16653 bytes .../kokoro/linux-clang-asan/build.sh | 24 + .../kokoro/linux-clang-asan/continuous.cfg | 16 + .../kokoro/linux-clang-asan/presubmit.cfg | 16 + .../kokoro/linux-clang-debug/build.sh | 24 + .../kokoro/linux-clang-debug/continuous.cfg | 22 + .../kokoro/linux-clang-debug/presubmit.cfg | 16 + .../kokoro/linux-clang-release-bazel/build.sh | 43 + .../linux-clang-release-bazel/continuous.cfg | 16 + .../linux-clang-release-bazel/presubmit.cfg | 16 + .../kokoro/linux-clang-release/build.sh | 24 + .../kokoro/linux-clang-release/continuous.cfg | 22 + .../kokoro/linux-clang-release/presubmit.cfg | 16 + .../kokoro/linux-gcc-debug/build.sh | 24 + .../kokoro/linux-gcc-debug/continuous.cfg | 22 + .../kokoro/linux-gcc-debug/presubmit.cfg | 17 + .../kokoro/linux-gcc-release/build.sh | 24 + .../kokoro/linux-gcc-release/continuous.cfg | 22 + .../kokoro/linux-gcc-release/presubmit.cfg | 16 + .../kokoro/macos-clang-debug/build.sh | 25 + .../kokoro/macos-clang-debug/continuous.cfg | 22 + .../kokoro/macos-clang-debug/presubmit.cfg | 16 + .../kokoro/macos-clang-release-bazel/build.sh | 44 + .../macos-clang-release-bazel/continuous.cfg | 16 + .../macos-clang-release-bazel/presubmit.cfg | 16 + .../kokoro/macos-clang-release/build.sh | 25 + .../kokoro/macos-clang-release/continuous.cfg | 22 + .../kokoro/macos-clang-release/presubmit.cfg | 16 + .../spirv-tools/kokoro/ndk-build/build.sh | 57 + .../kokoro/ndk-build/continuous.cfg | 17 + .../kokoro/ndk-build/presubmit.cfg | 16 + .../spirv-tools/kokoro/scripts/linux/build.sh | 105 + .../spirv-tools/kokoro/scripts/macos/build.sh | 69 + .../kokoro/scripts/windows/build.bat | 109 + .../kokoro/shaderc-smoketest/build.sh | 71 + .../kokoro/shaderc-smoketest/continuous.cfg | 17 + .../kokoro/shaderc-smoketest/presubmit.cfg | 17 + .../windows-msvc-2013-release/build.bat | 24 + .../windows-msvc-2013-release/continuous.cfg | 16 + .../windows-msvc-2013-release/presubmit.cfg | 16 + .../windows-msvc-2015-release-bazel/build.bat | 60 + .../continuous.cfg | 16 + .../presubmit.cfg | 16 + .../windows-msvc-2015-release/build.bat | 24 + .../windows-msvc-2015-release/continuous.cfg | 16 + .../windows-msvc-2015-release/presubmit.cfg | 16 + .../kokoro/windows-msvc-2017-debug/build.bat | 23 + .../windows-msvc-2017-debug/continuous.cfg | 22 + .../windows-msvc-2017-debug/presubmit.cfg | 16 + .../windows-msvc-2017-release/build.bat | 24 + .../windows-msvc-2017-release/continuous.cfg | 22 + .../windows-msvc-2017-release/presubmit.cfg | 16 + third_party/spirv-tools/source/CMakeLists.txt | 434 + .../spirv-tools/source/assembly_grammar.cpp | 264 + .../spirv-tools/source/assembly_grammar.h | 138 + third_party/spirv-tools/source/binary.cpp | 829 + third_party/spirv-tools/source/binary.h | 36 + third_party/spirv-tools/source/cfa.h | 347 + third_party/spirv-tools/source/diagnostic.cpp | 193 + third_party/spirv-tools/source/diagnostic.h | 79 + .../spirv-tools/source/disassemble.cpp | 533 + third_party/spirv-tools/source/disassemble.h | 38 + third_party/spirv-tools/source/enum_set.h | 208 + .../source/enum_string_mapping.cpp | 29 + .../spirv-tools/source/enum_string_mapping.h | 36 + third_party/spirv-tools/source/ext_inst.cpp | 199 + third_party/spirv-tools/source/ext_inst.h | 46 + third_party/spirv-tools/source/extensions.cpp | 44 + third_party/spirv-tools/source/extensions.h | 40 + .../spirv-tools/source/fuzz/CMakeLists.txt | 473 + .../source/fuzz/added_function_reducer.cpp | 302 + .../source/fuzz/added_function_reducer.h | 193 + .../spirv-tools/source/fuzz/call_graph.cpp | 183 + .../spirv-tools/source/fuzz/call_graph.h | 110 + .../fuzz/comparator_deep_blocks_first.h | 53 + .../fuzz/counter_overflow_id_source.cpp | 36 + .../source/fuzz/counter_overflow_id_source.h | 49 + .../source/fuzz/data_descriptor.cpp | 69 + .../spirv-tools/source/fuzz/data_descriptor.h | 48 + .../source/fuzz/equivalence_relation.h | 247 + .../fact_manager/constant_uniform_facts.cpp | 235 + .../fact_manager/constant_uniform_facts.h | 90 + .../data_synonym_and_id_equation_facts.cpp | 932 + .../data_synonym_and_id_equation_facts.h | 185 + .../fuzz/fact_manager/dead_block_facts.cpp | 45 + .../fuzz/fact_manager/dead_block_facts.h | 53 + .../source/fuzz/fact_manager/fact_manager.cpp | 279 + .../source/fuzz/fact_manager/fact_manager.h | 225 + .../fact_manager/irrelevant_value_facts.cpp | 133 + .../fact_manager/irrelevant_value_facts.h | 81 + .../fact_manager/livesafe_function_facts.cpp | 46 + .../fact_manager/livesafe_function_facts.h | 50 + .../source/fuzz/force_render_red.cpp | 373 + .../source/fuzz/force_render_red.h | 49 + .../spirv-tools/source/fuzz/fuzzer.cpp | 409 + third_party/spirv-tools/source/fuzz/fuzzer.h | 187 + .../source/fuzz/fuzzer_context.cpp | 407 + .../spirv-tools/source/fuzz/fuzzer_context.h | 564 + .../spirv-tools/source/fuzz/fuzzer_pass.cpp | 754 + .../spirv-tools/source/fuzz/fuzzer_pass.h | 353 + .../fuzz/fuzzer_pass_add_access_chains.cpp | 192 + .../fuzz/fuzzer_pass_add_access_chains.h | 41 + ...zzer_pass_add_bit_instruction_synonyms.cpp | 92 + ...fuzzer_pass_add_bit_instruction_synonyms.h | 41 + .../fuzzer_pass_add_composite_extract.cpp | 167 + .../fuzz/fuzzer_pass_add_composite_extract.h | 40 + .../fuzzer_pass_add_composite_inserts.cpp | 234 + .../fuzz/fuzzer_pass_add_composite_inserts.h | 45 + .../fuzz/fuzzer_pass_add_composite_types.cpp | 144 + .../fuzz/fuzzer_pass_add_composite_types.h | 61 + .../fuzz/fuzzer_pass_add_copy_memory.cpp | 82 + .../source/fuzz/fuzzer_pass_add_copy_memory.h | 40 + .../fuzz/fuzzer_pass_add_dead_blocks.cpp | 67 + .../source/fuzz/fuzzer_pass_add_dead_blocks.h | 40 + .../fuzz/fuzzer_pass_add_dead_breaks.cpp | 127 + .../source/fuzz/fuzzer_pass_add_dead_breaks.h | 39 + .../fuzz/fuzzer_pass_add_dead_continues.cpp | 94 + .../fuzz/fuzzer_pass_add_dead_continues.h | 39 + .../fuzzer_pass_add_equation_instructions.cpp | 414 + .../fuzzer_pass_add_equation_instructions.h | 80 + .../fuzz/fuzzer_pass_add_function_calls.cpp | 203 + .../fuzz/fuzzer_pass_add_function_calls.h | 50 + .../fuzz/fuzzer_pass_add_global_variables.cpp | 95 + .../fuzz/fuzzer_pass_add_global_variables.h | 40 + ...ass_add_image_sample_unused_components.cpp | 200 + ..._pass_add_image_sample_unused_components.h | 41 + .../source/fuzz/fuzzer_pass_add_loads.cpp | 96 + .../source/fuzz/fuzzer_pass_add_loads.h | 39 + .../fuzz/fuzzer_pass_add_local_variables.cpp | 80 + .../fuzz/fuzzer_pass_add_local_variables.h | 40 + .../fuzz/fuzzer_pass_add_loop_preheaders.cpp | 66 + .../fuzz/fuzzer_pass_add_loop_preheaders.h | 43 + ..._loops_to_create_int_constant_synonyms.cpp | 258 + ...dd_loops_to_create_int_constant_synonyms.h | 53 + ...er_pass_add_no_contraction_decorations.cpp | 57 + ...zzer_pass_add_no_contraction_decorations.h | 39 + .../fuzz/fuzzer_pass_add_opphi_synonyms.cpp | 312 + .../fuzz/fuzzer_pass_add_opphi_synonyms.h | 73 + .../fuzz/fuzzer_pass_add_parameters.cpp | 145 + .../source/fuzz/fuzzer_pass_add_parameters.h | 47 + .../fuzzer_pass_add_relaxed_decorations.cpp | 55 + .../fuzzer_pass_add_relaxed_decorations.h | 39 + .../source/fuzz/fuzzer_pass_add_stores.cpp | 132 + .../source/fuzz/fuzzer_pass_add_stores.h | 41 + .../source/fuzz/fuzzer_pass_add_synonyms.cpp | 133 + .../source/fuzz/fuzzer_pass_add_synonyms.h | 40 + ...r_pass_add_vector_shuffle_instructions.cpp | 145 + ...zer_pass_add_vector_shuffle_instructions.h | 39 + .../fuzzer_pass_adjust_branch_weights.cpp | 48 + .../fuzz/fuzzer_pass_adjust_branch_weights.h | 41 + .../fuzzer_pass_adjust_function_controls.cpp | 71 + .../fuzzer_pass_adjust_function_controls.h | 39 + .../fuzz/fuzzer_pass_adjust_loop_controls.cpp | 118 + .../fuzz/fuzzer_pass_adjust_loop_controls.h | 39 + ...zzer_pass_adjust_memory_operands_masks.cpp | 109 + ...fuzzer_pass_adjust_memory_operands_masks.h | 40 + .../fuzzer_pass_adjust_selection_controls.cpp | 73 + .../fuzzer_pass_adjust_selection_controls.h | 39 + .../fuzz/fuzzer_pass_apply_id_synonyms.cpp | 207 + .../fuzz/fuzzer_pass_apply_id_synonyms.h | 51 + .../fuzz/fuzzer_pass_construct_composites.cpp | 388 + .../fuzz/fuzzer_pass_construct_composites.h | 80 + .../source/fuzz/fuzzer_pass_copy_objects.cpp | 89 + .../source/fuzz/fuzzer_pass_copy_objects.h | 39 + .../fuzz/fuzzer_pass_donate_modules.cpp | 1213 + .../source/fuzz/fuzzer_pass_donate_modules.h | 164 + ...pass_duplicate_regions_with_selections.cpp | 133 + ...r_pass_duplicate_regions_with_selections.h | 42 + .../fuzzer_pass_expand_vector_reductions.cpp | 67 + .../fuzzer_pass_expand_vector_reductions.h | 41 + ...zzer_pass_flatten_conditional_branches.cpp | 251 + ...fuzzer_pass_flatten_conditional_branches.h | 46 + .../fuzz/fuzzer_pass_inline_functions.cpp | 104 + .../fuzz/fuzzer_pass_inline_functions.h | 41 + ...rchange_signedness_of_integer_operands.cpp | 156 + ...terchange_signedness_of_integer_operands.h | 51 + ...r_pass_interchange_zero_like_constants.cpp | 116 + ...zer_pass_interchange_zero_like_constants.h | 53 + ...uzzer_pass_invert_comparison_operators.cpp | 52 + .../fuzzer_pass_invert_comparison_operators.h | 40 + ...er_pass_make_vector_operations_dynamic.cpp | 71 + ...zzer_pass_make_vector_operations_dynamic.h | 40 + .../source/fuzz/fuzzer_pass_merge_blocks.cpp | 63 + .../source/fuzz/fuzzer_pass_merge_blocks.h | 39 + .../fuzzer_pass_merge_function_returns.cpp | 337 + .../fuzz/fuzzer_pass_merge_function_returns.h | 69 + .../fuzz/fuzzer_pass_mutate_pointers.cpp | 74 + .../source/fuzz/fuzzer_pass_mutate_pointers.h | 39 + .../fuzz/fuzzer_pass_obfuscate_constants.cpp | 523 + .../fuzz/fuzzer_pass_obfuscate_constants.h | 112 + .../fuzz/fuzzer_pass_outline_functions.cpp | 196 + .../fuzz/fuzzer_pass_outline_functions.h | 65 + .../fuzz/fuzzer_pass_permute_blocks.cpp | 79 + .../source/fuzz/fuzzer_pass_permute_blocks.h | 40 + ...uzzer_pass_permute_function_parameters.cpp | 72 + .../fuzzer_pass_permute_function_parameters.h | 45 + .../fuzz/fuzzer_pass_permute_instructions.cpp | 64 + .../fuzz/fuzzer_pass_permute_instructions.h | 40 + .../fuzz/fuzzer_pass_permute_phi_operands.cpp | 65 + .../fuzz/fuzzer_pass_permute_phi_operands.h | 40 + ...uzzer_pass_propagate_instructions_down.cpp | 68 + .../fuzzer_pass_propagate_instructions_down.h | 39 + .../fuzzer_pass_propagate_instructions_up.cpp | 63 + .../fuzzer_pass_propagate_instructions_up.h | 40 + ...fuzzer_pass_push_ids_through_variables.cpp | 154 + .../fuzzer_pass_push_ids_through_variables.h | 41 + ..._adds_subs_muls_with_carrying_extended.cpp | 79 + ...ce_adds_subs_muls_with_carrying_extended.h | 42 + ...e_branches_from_dead_blocks_with_exits.cpp | 133 + ...ace_branches_from_dead_blocks_with_exits.h | 41 + ...eplace_copy_memories_with_loads_stores.cpp | 58 + ..._replace_copy_memories_with_loads_stores.h | 41 + ...replace_copy_objects_with_stores_loads.cpp | 89 + ...s_replace_copy_objects_with_stores_loads.h | 41 + .../fuzzer_pass_replace_irrelevant_ids.cpp | 183 + .../fuzz/fuzzer_pass_replace_irrelevant_ids.h | 39 + ...ss_replace_linear_algebra_instructions.cpp | 63 + ...pass_replace_linear_algebra_instructions.h | 40 + ...eplace_loads_stores_with_copy_memories.cpp | 105 + ..._replace_loads_stores_with_copy_memories.h | 41 + ...place_opphi_ids_from_dead_predecessors.cpp | 121 + ...replace_opphi_ids_from_dead_predecessors.h | 39 + ...ce_opselects_with_conditional_branches.cpp | 172 + ...lace_opselects_with_conditional_branches.h | 53 + ...zer_pass_replace_parameter_with_global.cpp | 89 + ...uzzer_pass_replace_parameter_with_global.h | 40 + ...fuzzer_pass_replace_params_with_struct.cpp | 113 + .../fuzzer_pass_replace_params_with_struct.h | 40 + .../source/fuzz/fuzzer_pass_split_blocks.cpp | 104 + .../source/fuzz/fuzzer_pass_split_blocks.h | 40 + .../fuzzer_pass_swap_commutable_operands.cpp | 51 + .../fuzzer_pass_swap_commutable_operands.h | 41 + ..._pass_swap_conditional_branch_operands.cpp | 60 + ...er_pass_swap_conditional_branch_operands.h | 40 + ...r_pass_toggle_access_chain_instruction.cpp | 55 + ...zer_pass_toggle_access_chain_instruction.h | 40 + ...fuzzer_pass_wrap_regions_in_selections.cpp | 140 + .../fuzzer_pass_wrap_regions_in_selections.h | 55 + .../spirv-tools/source/fuzz/fuzzer_util.cpp | 1832 ++ .../spirv-tools/source/fuzz/fuzzer_util.h | 595 + .../source/fuzz/id_use_descriptor.cpp | 62 + .../source/fuzz/id_use_descriptor.h | 47 + .../source/fuzz/instruction_descriptor.cpp | 127 + .../source/fuzz/instruction_descriptor.h | 53 + .../source/fuzz/instruction_message.cpp | 80 + .../source/fuzz/instruction_message.h | 47 + .../source/fuzz/overflow_id_source.cpp | 23 + .../source/fuzz/overflow_id_source.h | 111 + .../pass_management/repeated_pass_instances.h | 187 + .../pass_management/repeated_pass_manager.cpp | 27 + .../pass_management/repeated_pass_manager.h | 59 + ...ss_manager_looped_with_recommendations.cpp | 70 + ...pass_manager_looped_with_recommendations.h | 69 + ...ss_manager_random_with_recommendations.cpp | 62 + ...pass_manager_random_with_recommendations.h | 68 + .../repeated_pass_manager_simple.cpp | 33 + .../repeated_pass_manager_simple.h | 39 + .../repeated_pass_recommender.cpp | 23 + .../repeated_pass_recommender.h | 42 + .../repeated_pass_recommender_standard.cpp | 377 + .../repeated_pass_recommender_standard.h | 50 + .../fuzz/protobufs/spirvfuzz_protobufs.h | 54 + .../source/fuzz/protobufs/spvtoolsfuzz.proto | 2342 ++ .../source/fuzz/pseudo_random_generator.cpp | 51 + .../source/fuzz/pseudo_random_generator.h | 49 + .../source/fuzz/random_generator.cpp | 25 + .../source/fuzz/random_generator.h | 48 + .../spirv-tools/source/fuzz/replayer.cpp | 170 + .../spirv-tools/source/fuzz/replayer.h | 105 + .../spirv-tools/source/fuzz/shrinker.cpp | 316 + .../spirv-tools/source/fuzz/shrinker.h | 128 + .../source/fuzz/transformation.cpp | 399 + .../spirv-tools/source/fuzz/transformation.h | 108 + .../fuzz/transformation_access_chain.cpp | 420 + .../source/fuzz/transformation_access_chain.h | 106 + ...sformation_add_bit_instruction_synonym.cpp | 253 + ...ansformation_add_bit_instruction_synonym.h | 143 + .../transformation_add_constant_boolean.cpp | 72 + .../transformation_add_constant_boolean.h | 58 + .../transformation_add_constant_composite.cpp | 151 + .../transformation_add_constant_composite.h | 66 + .../fuzz/transformation_add_constant_null.cpp | 71 + .../fuzz/transformation_add_constant_null.h | 56 + .../transformation_add_constant_scalar.cpp | 99 + .../fuzz/transformation_add_constant_scalar.h | 61 + .../fuzz/transformation_add_copy_memory.cpp | 210 + .../fuzz/transformation_add_copy_memory.h | 78 + .../fuzz/transformation_add_dead_block.cpp | 197 + .../fuzz/transformation_add_dead_block.h | 67 + .../fuzz/transformation_add_dead_break.cpp | 220 + .../fuzz/transformation_add_dead_break.h | 89 + .../fuzz/transformation_add_dead_continue.cpp | 168 + .../fuzz/transformation_add_dead_continue.h | 86 + ...formation_add_early_terminator_wrapper.cpp | 110 + ...nsformation_add_early_terminator_wrapper.h | 63 + .../fuzz/transformation_add_function.cpp | 960 + .../source/fuzz/transformation_add_function.h | 128 + .../fuzz/transformation_add_global_undef.cpp | 66 + .../fuzz/transformation_add_global_undef.h | 55 + .../transformation_add_global_variable.cpp | 124 + .../fuzz/transformation_add_global_variable.h | 69 + ...ion_add_image_sample_unused_components.cpp | 122 + ...ation_add_image_sample_unused_components.h | 59 + .../transformation_add_local_variable.cpp | 97 + .../fuzz/transformation_add_local_variable.h | 64 + .../transformation_add_loop_preheader.cpp | 233 + .../fuzz/transformation_add_loop_preheader.h | 59 + ...dd_loop_to_create_int_constant_synonym.cpp | 453 + ..._add_loop_to_create_int_constant_synonym.h | 72 + ...ormation_add_no_contraction_decoration.cpp | 114 + ...sformation_add_no_contraction_decoration.h | 62 + .../fuzz/transformation_add_opphi_synonym.cpp | 204 + .../fuzz/transformation_add_opphi_synonym.h | 75 + .../fuzz/transformation_add_parameter.cpp | 225 + .../fuzz/transformation_add_parameter.h | 75 + .../transformation_add_relaxed_decoration.cpp | 151 + .../transformation_add_relaxed_decoration.h | 64 + .../transformation_add_spec_constant_op.cpp | 91 + .../transformation_add_spec_constant_op.h | 62 + .../fuzz/transformation_add_synonym.cpp | 328 + .../source/fuzz/transformation_add_synonym.h | 94 + .../fuzz/transformation_add_type_array.cpp | 92 + .../fuzz/transformation_add_type_array.h | 59 + .../fuzz/transformation_add_type_boolean.cpp | 65 + .../fuzz/transformation_add_type_boolean.h | 53 + .../fuzz/transformation_add_type_float.cpp | 86 + .../fuzz/transformation_add_type_float.h | 55 + .../fuzz/transformation_add_type_function.cpp | 89 + .../fuzz/transformation_add_type_function.h | 63 + .../fuzz/transformation_add_type_int.cpp | 96 + .../source/fuzz/transformation_add_type_int.h | 56 + .../fuzz/transformation_add_type_matrix.cpp | 75 + .../fuzz/transformation_add_type_matrix.h | 57 + .../fuzz/transformation_add_type_pointer.cpp | 70 + .../fuzz/transformation_add_type_pointer.h | 57 + .../fuzz/transformation_add_type_struct.cpp | 82 + .../fuzz/transformation_add_type_struct.h | 61 + .../fuzz/transformation_add_type_vector.cpp | 69 + .../fuzz/transformation_add_type_vector.h | 57 + .../transformation_adjust_branch_weights.cpp | 102 + .../transformation_adjust_branch_weights.h | 59 + .../transformation_composite_construct.cpp | 323 + .../fuzz/transformation_composite_construct.h | 104 + .../fuzz/transformation_composite_extract.cpp | 142 + .../fuzz/transformation_composite_extract.h | 76 + .../fuzz/transformation_composite_insert.cpp | 241 + .../fuzz/transformation_composite_insert.h | 79 + ...tion_compute_data_synonym_fact_closure.cpp | 57 + ...mation_compute_data_synonym_fact_closure.h | 55 + .../source/fuzz/transformation_context.cpp | 68 + .../source/fuzz/transformation_context.h | 74 + ...mation_duplicate_region_with_selection.cpp | 682 + ...ormation_duplicate_region_with_selection.h | 80 + .../transformation_equation_instruction.cpp | 296 + .../transformation_equation_instruction.h | 78 + ...transformation_expand_vector_reduction.cpp | 170 + .../transformation_expand_vector_reduction.h | 105 + ...nsformation_flatten_conditional_branch.cpp | 1020 + ...ransformation_flatten_conditional_branch.h | 157 + .../fuzz/transformation_function_call.cpp | 193 + .../fuzz/transformation_function_call.h | 69 + .../fuzz/transformation_inline_function.cpp | 365 + .../fuzz/transformation_inline_function.h | 78 + ...nsformation_invert_comparison_operator.cpp | 183 + ...ransformation_invert_comparison_operator.h | 65 + .../source/fuzz/transformation_load.cpp | 106 + .../source/fuzz/transformation_load.h | 63 + ...ormation_make_vector_operation_dynamic.cpp | 116 + ...sformation_make_vector_operation_dynamic.h | 65 + .../fuzz/transformation_merge_blocks.cpp | 86 + .../source/fuzz/transformation_merge_blocks.h | 58 + .../transformation_merge_function_returns.cpp | 831 + .../transformation_merge_function_returns.h | 115 + .../fuzz/transformation_move_block_down.cpp | 113 + .../fuzz/transformation_move_block_down.h | 58 + .../transformation_move_instruction_down.cpp | 734 + .../transformation_move_instruction_down.h | 107 + .../fuzz/transformation_mutate_pointer.cpp | 171 + .../fuzz/transformation_mutate_pointer.h | 79 + .../fuzz/transformation_outline_function.cpp | 1015 + .../fuzz/transformation_outline_function.h | 229 + ...sformation_permute_function_parameters.cpp | 170 + ...ansformation_permute_function_parameters.h | 65 + .../transformation_permute_phi_operands.cpp | 100 + .../transformation_permute_phi_operands.h | 58 + ...nsformation_propagate_instruction_down.cpp | 592 + ...ransformation_propagate_instruction_down.h | 184 + ...ransformation_propagate_instruction_up.cpp | 417 + .../transformation_propagate_instruction_up.h | 91 + ...ransformation_push_id_through_variable.cpp | 173 + .../transformation_push_id_through_variable.h | 70 + ...sformation_record_synonymous_constants.cpp | 137 + ...ansformation_record_synonymous_constants.h | 73 + ...ace_add_sub_mul_with_carrying_extended.cpp | 237 + ...place_add_sub_mul_with_carrying_extended.h | 71 + ..._boolean_constant_with_constant_binary.cpp | 327 + ...ce_boolean_constant_with_constant_binary.h | 81 + ...place_branch_from_dead_block_with_exit.cpp | 169 + ...replace_branch_from_dead_block_with_exit.h | 84 + ...ormation_replace_constant_with_uniform.cpp | 294 + ...sformation_replace_constant_with_uniform.h | 99 + ...on_replace_copy_memory_with_load_store.cpp | 132 + ...tion_replace_copy_memory_with_load_store.h | 59 + ...on_replace_copy_object_with_store_load.cpp | 156 + ...tion_replace_copy_object_with_store_load.h | 65 + ...transformation_replace_id_with_synonym.cpp | 168 + .../transformation_replace_id_with_synonym.h | 77 + .../transformation_replace_irrelevant_id.cpp | 135 + .../transformation_replace_irrelevant_id.h | 68 + ...ion_replace_linear_algebra_instruction.cpp | 1041 + ...ation_replace_linear_algebra_instruction.h | 93 + ...on_replace_load_store_with_copy_memory.cpp | 189 + ...tion_replace_load_store_with_copy_memory.h | 78 + ...replace_opphi_id_from_dead_predecessor.cpp | 115 + ...n_replace_opphi_id_from_dead_predecessor.h | 58 + ...place_opselect_with_conditional_branch.cpp | 209 + ...replace_opselect_with_conditional_branch.h | 63 + ...ormation_replace_parameter_with_global.cpp | 211 + ...sformation_replace_parameter_with_global.h | 70 + ...nsformation_replace_params_with_struct.cpp | 316 + ...ransformation_replace_params_with_struct.h | 91 + .../transformation_set_function_control.cpp | 105 + .../transformation_set_function_control.h | 63 + .../fuzz/transformation_set_loop_control.cpp | 221 + .../fuzz/transformation_set_loop_control.h | 83 + ...ransformation_set_memory_operands_mask.cpp | 228 + .../transformation_set_memory_operands_mask.h | 81 + .../transformation_set_selection_control.cpp | 65 + .../transformation_set_selection_control.h | 58 + .../fuzz/transformation_split_block.cpp | 151 + .../source/fuzz/transformation_split_block.h | 67 + .../source/fuzz/transformation_store.cpp | 133 + .../source/fuzz/transformation_store.h | 67 + ...ransformation_swap_commutable_operands.cpp | 69 + .../transformation_swap_commutable_operands.h | 55 + ...ation_swap_conditional_branch_operands.cpp | 110 + ...rmation_swap_conditional_branch_operands.h | 60 + ...mation_toggle_access_chain_instruction.cpp | 86 + ...ormation_toggle_access_chain_instruction.h | 55 + .../fuzz/transformation_vector_shuffle.cpp | 233 + .../fuzz/transformation_vector_shuffle.h | 95 + ...tion_wrap_early_terminator_in_function.cpp | 183 + ...mation_wrap_early_terminator_in_function.h | 73 + ...ransformation_wrap_region_in_selection.cpp | 166 + .../transformation_wrap_region_in_selection.h | 88 + .../uniform_buffer_element_descriptor.cpp | 118 + .../fuzz/uniform_buffer_element_descriptor.h | 52 + third_party/spirv-tools/source/instruction.h | 49 + .../latest_version_glsl_std_450_header.h | 20 + .../source/latest_version_opencl_std_header.h | 20 + .../source/latest_version_spirv_header.h | 20 + third_party/spirv-tools/source/libspirv.cpp | 133 + .../spirv-tools/source/link/CMakeLists.txt | 46 + .../spirv-tools/source/link/linker.cpp | 767 + third_party/spirv-tools/source/macro.h | 25 + .../spirv-tools/source/name_mapper.cpp | 332 + third_party/spirv-tools/source/name_mapper.h | 122 + third_party/spirv-tools/source/opcode.cpp | 750 + third_party/spirv-tools/source/opcode.h | 153 + third_party/spirv-tools/source/operand.cpp | 588 + third_party/spirv-tools/source/operand.h | 151 + .../spirv-tools/source/opt/CMakeLists.txt | 265 + .../opt/aggressive_dead_code_elim_pass.cpp | 1001 + .../opt/aggressive_dead_code_elim_pass.h | 197 + .../spirv-tools/source/opt/amd_ext_to_khr.cpp | 972 + .../spirv-tools/source/opt/amd_ext_to_khr.h | 51 + .../spirv-tools/source/opt/basic_block.cpp | 286 + .../spirv-tools/source/opt/basic_block.h | 342 + .../source/opt/block_merge_pass.cpp | 52 + .../spirv-tools/source/opt/block_merge_pass.h | 62 + .../source/opt/block_merge_util.cpp | 194 + .../spirv-tools/source/opt/block_merge_util.h | 44 + .../spirv-tools/source/opt/build_module.cpp | 79 + .../spirv-tools/source/opt/build_module.h | 46 + .../spirv-tools/source/opt/ccp_pass.cpp | 343 + third_party/spirv-tools/source/opt/ccp_pass.h | 117 + third_party/spirv-tools/source/opt/cfg.cpp | 338 + third_party/spirv-tools/source/opt/cfg.h | 181 + .../source/opt/cfg_cleanup_pass.cpp | 39 + .../spirv-tools/source/opt/cfg_cleanup_pass.h | 41 + .../spirv-tools/source/opt/code_sink.cpp | 320 + .../spirv-tools/source/opt/code_sink.h | 107 + .../source/opt/combine_access_chains.cpp | 290 + .../source/opt/combine_access_chains.h | 83 + .../source/opt/compact_ids_pass.cpp | 97 + .../spirv-tools/source/opt/compact_ids_pass.h | 42 + .../spirv-tools/source/opt/composite.cpp | 52 + .../spirv-tools/source/opt/composite.h | 51 + .../source/opt/const_folding_rules.cpp | 1277 + .../source/opt/const_folding_rules.h | 130 + .../spirv-tools/source/opt/constants.cpp | 428 + .../spirv-tools/source/opt/constants.h | 706 + .../source/opt/convert_to_half_pass.cpp | 471 + .../source/opt/convert_to_half_pass.h | 148 + .../source/opt/copy_prop_arrays.cpp | 859 + .../spirv-tools/source/opt/copy_prop_arrays.h | 235 + .../source/opt/dead_branch_elim_pass.cpp | 658 + .../source/opt/dead_branch_elim_pass.h | 176 + .../source/opt/dead_insert_elim_pass.cpp | 264 + .../source/opt/dead_insert_elim_pass.h | 90 + .../source/opt/dead_variable_elimination.cpp | 111 + .../source/opt/dead_variable_elimination.h | 56 + .../source/opt/debug_info_manager.cpp | 862 + .../source/opt/debug_info_manager.h | 273 + .../decompose_initialized_variables_pass.cpp | 112 + .../decompose_initialized_variables_pass.h | 57 + .../source/opt/decoration_manager.cpp | 618 + .../source/opt/decoration_manager.h | 198 + .../source/opt/def_use_manager.cpp | 299 + .../spirv-tools/source/opt/def_use_manager.h | 256 + .../spirv-tools/source/opt/desc_sroa.cpp | 448 + .../spirv-tools/source/opt/desc_sroa.h | 107 + .../source/opt/dominator_analysis.cpp | 81 + .../source/opt/dominator_analysis.h | 138 + .../spirv-tools/source/opt/dominator_tree.cpp | 398 + .../spirv-tools/source/opt/dominator_tree.h | 305 + .../opt/eliminate_dead_constant_pass.cpp | 104 + .../source/opt/eliminate_dead_constant_pass.h | 35 + .../opt/eliminate_dead_functions_pass.cpp | 52 + .../opt/eliminate_dead_functions_pass.h | 44 + .../opt/eliminate_dead_functions_util.cpp | 58 + .../opt/eliminate_dead_functions_util.h | 36 + .../opt/eliminate_dead_members_pass.cpp | 691 + .../source/opt/eliminate_dead_members_pass.h | 146 + .../spirv-tools/source/opt/empty_pass.h | 36 + .../source/opt/feature_manager.cpp | 113 + .../spirv-tools/source/opt/feature_manager.h | 100 + .../source/opt/fix_storage_class.cpp | 330 + .../source/opt/fix_storage_class.h | 93 + .../source/opt/flatten_decoration_pass.cpp | 165 + .../source/opt/flatten_decoration_pass.h | 35 + third_party/spirv-tools/source/opt/fold.cpp | 711 + third_party/spirv-tools/source/opt/fold.h | 187 + ...ld_spec_constant_op_and_composite_pass.cpp | 445 + ...fold_spec_constant_op_and_composite_pass.h | 82 + .../spirv-tools/source/opt/folding_rules.cpp | 2537 ++ .../spirv-tools/source/opt/folding_rules.h | 118 + .../opt/freeze_spec_constant_value_pass.cpp | 53 + .../opt/freeze_spec_constant_value_pass.h | 35 + .../spirv-tools/source/opt/function.cpp | 275 + third_party/spirv-tools/source/opt/function.h | 264 + .../opt/generate_webgpu_initializers_pass.cpp | 116 + .../opt/generate_webgpu_initializers_pass.h | 62 + .../opt/graphics_robust_access_pass.cpp | 1061 + .../source/opt/graphics_robust_access_pass.h | 156 + .../spirv-tools/source/opt/if_conversion.cpp | 286 + .../spirv-tools/source/opt/if_conversion.h | 89 + .../source/opt/inline_exhaustive_pass.cpp | 85 + .../source/opt/inline_exhaustive_pass.h | 53 + .../source/opt/inline_opaque_pass.cpp | 120 + .../source/opt/inline_opaque_pass.h | 60 + .../spirv-tools/source/opt/inline_pass.cpp | 796 + .../spirv-tools/source/opt/inline_pass.h | 243 + .../source/opt/inst_bindless_check_pass.cpp | 647 + .../source/opt/inst_bindless_check_pass.h | 198 + .../source/opt/inst_buff_addr_check_pass.cpp | 441 + .../source/opt/inst_buff_addr_check_pass.h | 129 + .../source/opt/inst_debug_printf_pass.cpp | 266 + .../source/opt/inst_debug_printf_pass.h | 95 + .../spirv-tools/source/opt/instruction.cpp | 965 + .../spirv-tools/source/opt/instruction.h | 867 + .../source/opt/instruction_list.cpp | 36 + .../spirv-tools/source/opt/instruction_list.h | 140 + .../source/opt/instrument_pass.cpp | 1128 + .../spirv-tools/source/opt/instrument_pass.h | 475 + .../spirv-tools/source/opt/ir_builder.h | 628 + .../spirv-tools/source/opt/ir_context.cpp | 1042 + .../spirv-tools/source/opt/ir_context.h | 1168 + .../spirv-tools/source/opt/ir_loader.cpp | 313 + .../spirv-tools/source/opt/ir_loader.h | 91 + third_party/spirv-tools/source/opt/iterator.h | 358 + .../opt/legalize_vector_shuffle_pass.cpp | 39 + .../source/opt/legalize_vector_shuffle_pass.h | 53 + .../spirv-tools/source/opt/licm_pass.cpp | 140 + .../spirv-tools/source/opt/licm_pass.h | 72 + .../opt/local_access_chain_convert_pass.cpp | 425 + .../opt/local_access_chain_convert_pass.h | 133 + .../opt/local_redundancy_elimination.cpp | 67 + .../source/opt/local_redundancy_elimination.h | 68 + .../opt/local_single_block_elim_pass.cpp | 277 + .../source/opt/local_single_block_elim_pass.h | 107 + .../opt/local_single_store_elim_pass.cpp | 311 + .../source/opt/local_single_store_elim_pass.h | 108 + third_party/spirv-tools/source/opt/log.h | 235 + .../source/opt/loop_dependence.cpp | 1675 ++ .../spirv-tools/source/opt/loop_dependence.h | 560 + .../source/opt/loop_dependence_helpers.cpp | 541 + .../source/opt/loop_descriptor.cpp | 1025 + .../spirv-tools/source/opt/loop_descriptor.h | 576 + .../spirv-tools/source/opt/loop_fission.cpp | 513 + .../spirv-tools/source/opt/loop_fission.h | 78 + .../spirv-tools/source/opt/loop_fusion.cpp | 730 + .../spirv-tools/source/opt/loop_fusion.h | 114 + .../source/opt/loop_fusion_pass.cpp | 69 + .../spirv-tools/source/opt/loop_fusion_pass.h | 51 + .../spirv-tools/source/opt/loop_peeling.cpp | 1086 + .../spirv-tools/source/opt/loop_peeling.h | 336 + .../spirv-tools/source/opt/loop_unroller.cpp | 1119 + .../spirv-tools/source/opt/loop_unroller.h | 49 + .../source/opt/loop_unswitch_pass.cpp | 620 + .../source/opt/loop_unswitch_pass.h | 43 + .../spirv-tools/source/opt/loop_utils.cpp | 694 + .../spirv-tools/source/opt/loop_utils.h | 182 + .../spirv-tools/source/opt/mem_pass.cpp | 510 + third_party/spirv-tools/source/opt/mem_pass.h | 164 + .../source/opt/merge_return_pass.cpp | 878 + .../source/opt/merge_return_pass.h | 337 + third_party/spirv-tools/source/opt/module.cpp | 251 + third_party/spirv-tools/source/opt/module.h | 536 + .../spirv-tools/source/opt/null_pass.h | 34 + .../spirv-tools/source/opt/optimizer.cpp | 977 + third_party/spirv-tools/source/opt/pass.cpp | 155 + third_party/spirv-tools/source/opt/pass.h | 164 + .../spirv-tools/source/opt/pass_manager.cpp | 85 + .../spirv-tools/source/opt/pass_manager.h | 158 + third_party/spirv-tools/source/opt/passes.h | 85 + .../spirv-tools/source/opt/pch_source_opt.cpp | 15 + .../spirv-tools/source/opt/pch_source_opt.h | 32 + .../source/opt/private_to_local_pass.cpp | 239 + .../source/opt/private_to_local_pass.h | 73 + .../spirv-tools/source/opt/propagator.cpp | 291 + .../spirv-tools/source/opt/propagator.h | 317 + .../source/opt/reduce_load_size.cpp | 183 + .../spirv-tools/source/opt/reduce_load_size.h | 65 + .../source/opt/redundancy_elimination.cpp | 56 + .../source/opt/redundancy_elimination.h | 56 + third_party/spirv-tools/source/opt/reflect.h | 70 + .../source/opt/register_pressure.cpp | 583 + .../source/opt/register_pressure.h | 196 + .../source/opt/relax_float_ops_pass.cpp | 178 + .../source/opt/relax_float_ops_pass.h | 80 + .../source/opt/remove_duplicates_pass.cpp | 213 + .../source/opt/remove_duplicates_pass.h | 61 + .../source/opt/replace_invalid_opc.cpp | 208 + .../source/opt/replace_invalid_opc.h | 67 + .../source/opt/scalar_analysis.cpp | 988 + .../spirv-tools/source/opt/scalar_analysis.h | 314 + .../source/opt/scalar_analysis_nodes.h | 347 + .../opt/scalar_analysis_simplification.cpp | 539 + .../source/opt/scalar_replacement_pass.cpp | 989 + .../source/opt/scalar_replacement_pass.h | 257 + .../set_spec_constant_default_value_pass.cpp | 368 + .../set_spec_constant_default_value_pass.h | 114 + .../source/opt/simplification_pass.cpp | 165 + .../source/opt/simplification_pass.h | 58 + .../opt/split_invalid_unreachable_pass.cpp | 95 + .../opt/split_invalid_unreachable_pass.h | 51 + .../source/opt/ssa_rewrite_pass.cpp | 769 + .../spirv-tools/source/opt/ssa_rewrite_pass.h | 315 + .../source/opt/strength_reduction_pass.cpp | 200 + .../source/opt/strength_reduction_pass.h | 65 + .../opt/strip_atomic_counter_memory_pass.cpp | 57 + .../opt/strip_atomic_counter_memory_pass.h | 51 + .../source/opt/strip_debug_info_pass.cpp | 109 + .../source/opt/strip_debug_info_pass.h | 35 + .../source/opt/strip_reflect_info_pass.cpp | 128 + .../source/opt/strip_reflect_info_pass.h | 44 + .../source/opt/struct_cfg_analysis.cpp | 250 + .../source/opt/struct_cfg_analysis.h | 160 + .../spirv-tools/source/opt/tree_iterator.h | 246 + .../spirv-tools/source/opt/type_manager.cpp | 1053 + .../spirv-tools/source/opt/type_manager.h | 285 + third_party/spirv-tools/source/opt/types.cpp | 703 + third_party/spirv-tools/source/opt/types.h | 672 + .../source/opt/unify_const_pass.cpp | 177 + .../spirv-tools/source/opt/unify_const_pass.h | 35 + .../source/opt/upgrade_memory_model.cpp | 770 + .../source/opt/upgrade_memory_model.h | 150 + .../source/opt/value_number_table.cpp | 240 + .../source/opt/value_number_table.h | 91 + .../spirv-tools/source/opt/vector_dce.cpp | 419 + .../spirv-tools/source/opt/vector_dce.h | 157 + .../spirv-tools/source/opt/workaround1209.cpp | 69 + .../spirv-tools/source/opt/workaround1209.h | 41 + .../spirv-tools/source/opt/wrap_opkill.cpp | 200 + .../spirv-tools/source/opt/wrap_opkill.h | 80 + .../spirv-tools/source/parsed_operand.cpp | 74 + .../spirv-tools/source/parsed_operand.h | 33 + third_party/spirv-tools/source/pch_source.cpp | 15 + third_party/spirv-tools/source/pch_source.h | 15 + third_party/spirv-tools/source/print.cpp | 126 + third_party/spirv-tools/source/print.h | 75 + .../spirv-tools/source/reduce/CMakeLists.txt | 112 + .../change_operand_reduction_opportunity.cpp | 32 + .../change_operand_reduction_opportunity.h | 54 + ...operand_to_undef_reduction_opportunity.cpp | 41 + ...e_operand_to_undef_reduction_opportunity.h | 53 + ..._conditional_branch_opportunity_finder.cpp | 87 + ...le_conditional_branch_opportunity_finder.h | 37 + ...nditional_branch_reduction_opportunity.cpp | 68 + ...conditional_branch_reduction_opportunity.h | 54 + .../merge_blocks_reduction_opportunity.cpp | 81 + .../merge_blocks_reduction_opportunity.h | 53 + ...ge_blocks_reduction_opportunity_finder.cpp | 46 + ...erge_blocks_reduction_opportunity_finder.h | 42 + ..._to_const_reduction_opportunity_finder.cpp | 81 + ...nd_to_const_reduction_opportunity_finder.h | 44 + ...nating_id_reduction_opportunity_finder.cpp | 114 + ...minating_id_reduction_opportunity_finder.h | 56 + ..._to_undef_reduction_opportunity_finder.cpp | 92 + ...nd_to_undef_reduction_opportunity_finder.h | 43 + .../source/reduce/pch_source_reduce.cpp | 15 + .../source/reduce/pch_source_reduce.h | 23 + .../spirv-tools/source/reduce/reducer.cpp | 246 + .../spirv-tools/source/reduce/reducer.h | 122 + .../source/reduce/reduction_opportunity.cpp | 27 + .../source/reduce/reduction_opportunity.h | 47 + .../reduce/reduction_opportunity_finder.cpp | 34 + .../reduce/reduction_opportunity_finder.h | 58 + .../source/reduce/reduction_pass.cpp | 86 + .../source/reduce/reduction_pass.h | 86 + .../source/reduce/reduction_util.cpp | 119 + .../source/reduce/reduction_util.h | 51 + .../remove_block_reduction_opportunity.cpp | 54 + .../remove_block_reduction_opportunity.h | 46 + ...ove_block_reduction_opportunity_finder.cpp | 95 + ...emove_block_reduction_opportunity_finder.h | 55 + .../remove_function_reduction_opportunity.cpp | 41 + .../remove_function_reduction_opportunity.h | 49 + ..._function_reduction_opportunity_finder.cpp | 50 + ...ve_function_reduction_opportunity_finder.h | 42 + ...move_instruction_reduction_opportunity.cpp | 41 + ...remove_instruction_reduction_opportunity.h | 44 + ...remove_selection_reduction_opportunity.cpp | 31 + .../remove_selection_reduction_opportunity.h | 47 + ...selection_reduction_opportunity_finder.cpp | 146 + ...e_selection_reduction_opportunity_finder.h | 49 + ...ve_struct_member_reduction_opportunity.cpp | 208 + ...move_struct_member_reduction_opportunity.h | 84 + ...struction_reduction_opportunity_finder.cpp | 176 + ...instruction_reduction_opportunity_finder.h | 61 + ...ct_member_reduction_opportunity_finder.cpp | 200 + ...ruct_member_reduction_opportunity_finder.h | 61 + ...al_branch_to_branch_opportunity_finder.cpp | 62 + ...onal_branch_to_branch_opportunity_finder.h | 37 + ...branch_to_branch_reduction_opportunity.cpp | 57 + ...l_branch_to_branch_reduction_opportunity.h | 45 + ...oop_to_selection_reduction_opportunity.cpp | 288 + ..._loop_to_selection_reduction_opportunity.h | 97 + ...selection_reduction_opportunity_finder.cpp | 102 + ...o_selection_reduction_opportunity_finder.h | 57 + .../spirv-tools/source/software_version.cpp | 27 + .../spirv-tools/source/spirv_constant.h | 100 + .../spirv-tools/source/spirv_definition.h | 33 + .../spirv-tools/source/spirv_endian.cpp | 77 + third_party/spirv-tools/source/spirv_endian.h | 37 + .../source/spirv_fuzzer_options.cpp | 68 + .../spirv-tools/source/spirv_fuzzer_options.h | 48 + .../source/spirv_optimizer_options.cpp | 51 + .../source/spirv_optimizer_options.h | 49 + .../source/spirv_reducer_options.cpp | 51 + .../source/spirv_reducer_options.h | 38 + .../spirv-tools/source/spirv_target_env.cpp | 380 + .../spirv-tools/source/spirv_target_env.h | 52 + .../source/spirv_validator_options.cpp | 117 + .../source/spirv_validator_options.h | 61 + third_party/spirv-tools/source/table.cpp | 67 + third_party/spirv-tools/source/table.h | 133 + third_party/spirv-tools/source/text.cpp | 841 + third_party/spirv-tools/source/text.h | 53 + .../spirv-tools/source/text_handler.cpp | 397 + third_party/spirv-tools/source/text_handler.h | 264 + .../spirv-tools/source/util/bit_vector.cpp | 82 + .../spirv-tools/source/util/bit_vector.h | 119 + .../spirv-tools/source/util/bitutils.h | 187 + .../spirv-tools/source/util/hex_float.h | 1150 + third_party/spirv-tools/source/util/ilist.h | 365 + .../spirv-tools/source/util/ilist_node.h | 265 + .../spirv-tools/source/util/make_unique.h | 30 + .../spirv-tools/source/util/parse_number.cpp | 217 + .../spirv-tools/source/util/parse_number.h | 252 + .../spirv-tools/source/util/small_vector.h | 466 + .../spirv-tools/source/util/string_utils.cpp | 58 + .../spirv-tools/source/util/string_utils.h | 92 + third_party/spirv-tools/source/util/timer.cpp | 102 + third_party/spirv-tools/source/util/timer.h | 392 + .../spirv-tools/source/val/basic_block.cpp | 143 + .../spirv-tools/source/val/basic_block.h | 244 + .../spirv-tools/source/val/construct.cpp | 229 + .../spirv-tools/source/val/construct.h | 170 + .../spirv-tools/source/val/decoration.h | 89 + .../spirv-tools/source/val/function.cpp | 416 + third_party/spirv-tools/source/val/function.h | 400 + .../spirv-tools/source/val/instruction.cpp | 45 + .../spirv-tools/source/val/instruction.h | 152 + .../spirv-tools/source/val/validate.cpp | 518 + third_party/spirv-tools/source/val/validate.h | 242 + .../source/val/validate_adjacency.cpp | 128 + .../source/val/validate_annotation.cpp | 480 + .../source/val/validate_arithmetics.cpp | 550 + .../source/val/validate_atomics.cpp | 348 + .../source/val/validate_barriers.cpp | 136 + .../source/val/validate_bitwise.cpp | 219 + .../source/val/validate_builtins.cpp | 3785 +++ .../source/val/validate_capability.cpp | 382 + .../spirv-tools/source/val/validate_cfg.cpp | 1180 + .../source/val/validate_composites.cpp | 619 + .../source/val/validate_constants.cpp | 468 + .../source/val/validate_conversion.cpp | 546 + .../spirv-tools/source/val/validate_debug.cpp | 74 + .../source/val/validate_decorations.cpp | 1634 + .../source/val/validate_derivatives.cpp | 110 + .../val/validate_execution_limitations.cpp | 72 + .../source/val/validate_extensions.cpp | 3160 ++ .../source/val/validate_function.cpp | 358 + .../spirv-tools/source/val/validate_id.cpp | 245 + .../spirv-tools/source/val/validate_image.cpp | 2016 ++ .../source/val/validate_instruction.cpp | 501 + .../source/val/validate_interfaces.cpp | 504 + .../source/val/validate_layout.cpp | 357 + .../source/val/validate_literals.cpp | 99 + .../source/val/validate_logicals.cpp | 286 + .../source/val/validate_memory.cpp | 1669 + .../source/val/validate_memory_semantics.cpp | 295 + .../source/val/validate_memory_semantics.h | 28 + .../spirv-tools/source/val/validate_misc.cpp | 163 + .../source/val/validate_mode_setting.cpp | 543 + .../source/val/validate_non_uniform.cpp | 77 + .../source/val/validate_primitives.cpp | 75 + .../source/val/validate_scopes.cpp | 325 + .../spirv-tools/source/val/validate_scopes.h | 33 + .../source/val/validate_small_type_uses.cpp | 57 + .../spirv-tools/source/val/validate_type.cpp | 673 + .../source/val/validation_state.cpp | 1513 + .../spirv-tools/source/val/validation_state.h | 890 + third_party/spirv-tools/test/CMakeLists.txt | 194 + .../test/assembly_context_test.cpp | 77 + .../spirv-tools/test/assembly_format_test.cpp | 50 + .../spirv-tools/test/binary_destroy_test.cpp | 44 + .../test/binary_endianness_test.cpp | 54 + .../test/binary_header_get_test.cpp | 117 + .../spirv-tools/test/binary_parse_test.cpp | 893 + .../test/binary_strnlen_s_test.cpp | 32 + .../test/binary_to_text.literal_test.cpp | 76 + .../spirv-tools/test/binary_to_text_test.cpp | 561 + .../spirv-tools/test/c_interface_test.cpp | 299 + third_party/spirv-tools/test/comment_test.cpp | 51 + .../spirv-tools/test/cpp_interface_test.cpp | 328 + .../spirv-tools/test/diagnostic_test.cpp | 150 + .../spirv-tools/test/enum_set_test.cpp | 290 + .../test/enum_string_mapping_test.cpp | 200 + .../test/ext_inst.cldebug100_test.cpp | 1070 + .../test/ext_inst.debuginfo_test.cpp | 815 + .../spirv-tools/test/ext_inst.glsl_test.cpp | 204 + .../test/ext_inst.non_semantic_test.cpp | 90 + .../spirv-tools/test/ext_inst.opencl_test.cpp | 374 + .../spirv-tools/test/fix_word_test.cpp | 64 + .../spirv-tools/test/fuzz/CMakeLists.txt | 138 + .../spirv-tools/test/fuzz/call_graph_test.cpp | 378 + .../comparator_deep_blocks_first_test.cpp | 133 + .../fuzz/data_synonym_transformation_test.cpp | 1250 + .../test/fuzz/equivalence_relation_test.cpp | 155 + .../constant_uniform_facts_test.cpp | 797 + ...ata_synonym_and_id_equation_facts_test.cpp | 691 + .../fact_manager/dead_block_facts_test.cpp | 74 + .../irrelevant_value_facts_test.cpp | 179 + .../spirv-tools/test/fuzz/fuzz_test_util.cpp | 188 + .../spirv-tools/test/fuzz/fuzz_test_util.h | 103 + .../fuzzer_pass_add_opphi_synonyms_test.cpp | 176 + .../fuzzer_pass_construct_composites_test.cpp | 188 + .../fuzz/fuzzer_pass_donate_modules_test.cpp | 2268 ++ .../fuzzer_pass_outline_functions_test.cpp | 597 + .../test/fuzz/fuzzer_pass_test.cpp | 103 + .../test/fuzz/fuzzer_replayer_test.cpp | 1750 ++ .../test/fuzz/fuzzer_shrinker_test.cpp | 1149 + .../test/fuzz/instruction_descriptor_test.cpp | 74 + .../spirv-tools/test/fuzz/replayer_test.cpp | 475 + .../spirv-tools/test/fuzz/shrinker_test.cpp | 383 + .../fuzz/transformation_access_chain_test.cpp | 713 + ...ation_add_bit_instruction_synonym_test.cpp | 942 + ...ansformation_add_constant_boolean_test.cpp | 190 + ...sformation_add_constant_composite_test.cpp | 289 + .../transformation_add_constant_null_test.cpp | 143 + ...ransformation_add_constant_scalar_test.cpp | 356 + .../transformation_add_copy_memory_test.cpp | 483 + .../transformation_add_dead_block_test.cpp | 387 + .../transformation_add_dead_break_test.cpp | 2874 ++ .../transformation_add_dead_continue_test.cpp | 1616 + ...tion_add_early_terminator_wrapper_test.cpp | 163 + .../fuzz/transformation_add_function_test.cpp | 3018 ++ .../transformation_add_global_undef_test.cpp | 125 + ...ransformation_add_global_variable_test.cpp | 407 + ...dd_image_sample_unused_components_test.cpp | 255 + ...transformation_add_local_variable_test.cpp | 228 + ...transformation_add_loop_preheader_test.cpp | 298 + ...op_to_create_int_constant_synonym_test.cpp | 1149 + ...ion_add_no_contraction_decoration_test.cpp | 202 + .../transformation_add_opphi_synonym_test.cpp | 488 + .../transformation_add_parameter_test.cpp | 1106 + ...sformation_add_relaxed_decoration_test.cpp | 147 + .../fuzz/transformation_add_synonym_test.cpp | 1472 + .../transformation_add_type_array_test.cpp | 143 + .../transformation_add_type_boolean_test.cpp | 86 + .../transformation_add_type_float_test.cpp | 145 + .../transformation_add_type_function_test.cpp | 141 + .../fuzz/transformation_add_type_int_test.cpp | 187 + .../transformation_add_type_matrix_test.cpp | 137 + .../transformation_add_type_pointer_test.cpp | 213 + .../transformation_add_type_struct_test.cpp | 158 + .../transformation_add_type_vector_test.cpp | 109 + ...nsformation_adjust_branch_weights_test.cpp | 348 + ...ransformation_composite_construct_test.cpp | 1648 + .../transformation_composite_extract_test.cpp | 640 + .../transformation_composite_insert_test.cpp | 920 + ...compute_data_synonym_fact_closure_test.cpp | 477 + ...n_duplicate_region_with_selection_test.cpp | 2280 ++ ...ansformation_equation_instruction_test.cpp | 1680 ++ ...formation_expand_vector_reduction_test.cpp | 284 + ...mation_flatten_conditional_branch_test.cpp | 2140 ++ .../transformation_function_call_test.cpp | 475 + .../transformation_inline_function_test.cpp | 1116 + ...mation_invert_comparison_operator_test.cpp | 136 + .../test/fuzz/transformation_load_test.cpp | 298 + ...ion_make_vector_operation_dynamic_test.cpp | 364 + .../fuzz/transformation_merge_blocks_test.cpp | 703 + ...sformation_merge_function_returns_test.cpp | 1886 ++ .../transformation_move_block_down_test.cpp | 726 + ...nsformation_move_instruction_down_test.cpp | 720 + .../transformation_mutate_pointer_test.cpp | 322 + .../transformation_outline_function_test.cpp | 3304 ++ ...ation_permute_function_parameters_test.cpp | 563 + ...ansformation_permute_phi_operands_test.cpp | 155 + ...mation_propagate_instruction_down_test.cpp | 1138 + ...ormation_propagate_instruction_up_test.cpp | 952 + ...ormation_push_id_through_variable_test.cpp | 763 + ...ation_record_synonymous_constants_test.cpp | 884 + ...dd_sub_mul_with_carrying_extended_test.cpp | 618 + ...ean_constant_with_constant_binary_test.cpp | 752 + ..._branch_from_dead_block_with_exit_test.cpp | 571 + ...ion_replace_constant_with_uniform_test.cpp | 1636 + ...place_copy_memory_with_load_store_test.cpp | 158 + ...place_copy_object_with_store_load_test.cpp | 267 + ...formation_replace_id_with_synonym_test.cpp | 1793 ++ ...nsformation_replace_irrelevant_id_test.cpp | 336 + ...eplace_linear_algebra_instruction_test.cpp | 2486 ++ ...place_load_store_with_copy_memory_test.cpp | 287 + ...ce_opphi_id_from_dead_predecessor_test.cpp | 212 + ..._opselect_with_conditional_branch_test.cpp | 276 + ...ion_replace_parameter_with_global_test.cpp | 364 + ...mation_replace_params_with_struct_test.cpp | 527 + ...ansformation_set_function_control_test.cpp | 263 + .../transformation_set_loop_control_test.cpp | 986 + ...ormation_set_memory_operands_mask_test.cpp | 520 + ...nsformation_set_selection_control_test.cpp | 231 + .../fuzz/transformation_split_block_test.cpp | 921 + .../test/fuzz/transformation_store_test.cpp | 429 + ...ormation_swap_commutable_operands_test.cpp | 456 + ..._swap_conditional_branch_operands_test.cpp | 146 + ...n_toggle_access_chain_instruction_test.cpp | 432 + .../transformation_vector_shuffle_test.cpp | 806 + ...wrap_early_terminator_in_function_test.cpp | 318 + ...ormation_wrap_region_in_selection_test.cpp | 266 + ...uniform_buffer_element_descriptor_test.cpp | 85 + third_party/spirv-tools/test/fuzzers/BUILD.gn | 215 + .../test/fuzzers/corpora/spv/simple.spv | Bin 0 -> 728 bytes .../test/fuzzers/spvtools_as_fuzzer.cpp | 72 + .../fuzzers/spvtools_binary_parser_fuzzer.cpp | 44 + .../test/fuzzers/spvtools_dis_fuzzer.cpp | 71 + .../spvtools_opt_legalization_fuzzer.cpp | 38 + .../spvtools_opt_performance_fuzzer.cpp | 38 + .../test/fuzzers/spvtools_opt_size_fuzzer.cpp | 38 + .../spvtools_opt_vulkantowebgpu_fuzzer.cpp | 38 + .../spvtools_opt_webgputovulkan_fuzzer.cpp | 38 + .../test/fuzzers/spvtools_val_fuzzer.cpp | 36 + .../fuzzers/spvtools_val_webgpu_fuzzer.cpp | 36 + .../test/generator_magic_number_test.cpp | 62 + .../spirv-tools/test/hex_float_test.cpp | 1331 + .../spirv-tools/test/immediate_int_test.cpp | 291 + .../spirv-tools/test/libspirv_macros_test.cpp | 25 + .../spirv-tools/test/link/CMakeLists.txt | 28 + .../test/link/binary_version_test.cpp | 60 + .../test/link/entry_points_test.cpp | 94 + .../test/link/global_values_amount_test.cpp | 153 + .../spirv-tools/test/link/ids_limit_test.cpp | 72 + .../spirv-tools/test/link/linker_fixture.h | 222 + .../link/matching_imports_to_exports_test.cpp | 476 + .../test/link/memory_model_test.cpp | 74 + .../test/link/partial_linkage_test.cpp | 89 + .../spirv-tools/test/link/type_match_test.cpp | 148 + .../spirv-tools/test/link/unique_ids_test.cpp | 142 + third_party/spirv-tools/test/log_test.cpp | 53 + .../spirv-tools/test/name_mapper_test.cpp | 348 + .../spirv-tools/test/named_id_test.cpp | 87 + .../spirv-tools/test/opcode_make_test.cpp | 44 + .../test/opcode_require_capabilities_test.cpp | 78 + .../spirv-tools/test/opcode_split_test.cpp | 30 + .../test/opcode_table_get_test.cpp | 39 + .../test/operand-class-test-coverage.csv | 43 + .../test/operand_capabilities_test.cpp | 755 + .../spirv-tools/test/operand_pattern_test.cpp | 270 + third_party/spirv-tools/test/operand_test.cpp | 123 + .../spirv-tools/test/opt/CMakeLists.txt | 108 + .../opt/aggressive_dead_code_elim_test.cpp | 7592 +++++ .../spirv-tools/test/opt/amd_ext_to_khr.cpp | 953 + .../spirv-tools/test/opt/assembly_builder.h | 266 + .../test/opt/assembly_builder_test.cpp | 283 + .../spirv-tools/test/opt/block_merge_test.cpp | 1046 + third_party/spirv-tools/test/opt/ccp_test.cpp | 1213 + .../spirv-tools/test/opt/cfg_cleanup_test.cpp | 456 + third_party/spirv-tools/test/opt/cfg_test.cpp | 205 + .../spirv-tools/test/opt/code_sink_test.cpp | 557 + .../test/opt/combine_access_chains_test.cpp | 773 + .../spirv-tools/test/opt/compact_ids_test.cpp | 315 + .../test/opt/constant_manager_test.cpp | 110 + .../spirv-tools/test/opt/constants_test.cpp | 167 + .../test/opt/convert_relaxed_to_half_test.cpp | 1336 + .../test/opt/copy_prop_array_test.cpp | 1819 ++ .../test/opt/dead_branch_elim_test.cpp | 3409 +++ .../test/opt/dead_insert_elim_test.cpp | 678 + .../test/opt/dead_variable_elim_test.cpp | 298 + .../test/opt/debug_info_manager_test.cpp | 615 + .../decompose_initialized_variables_test.cpp | 252 + .../test/opt/decoration_manager_test.cpp | 1510 + .../spirv-tools/test/opt/def_use_test.cpp | 1719 ++ .../spirv-tools/test/opt/desc_sroa_test.cpp | 734 + .../test/opt/dominator_tree/CMakeLists.txt | 31 + .../opt/dominator_tree/common_dominators.cpp | 151 + .../test/opt/dominator_tree/generated.cpp | 1020 + .../test/opt/dominator_tree/nested_ifs.cpp | 153 + .../opt/dominator_tree/nested_ifs_post.cpp | 156 + .../test/opt/dominator_tree/nested_loops.cpp | 433 + .../nested_loops_with_unreachables.cpp | 848 + .../opt/dominator_tree/pch_test_opt_dom.cpp | 15 + .../opt/dominator_tree/pch_test_opt_dom.h | 25 + .../test/opt/dominator_tree/post.cpp | 207 + .../test/opt/dominator_tree/simple.cpp | 177 + .../switch_case_fallthrough.cpp | 163 + .../opt/dominator_tree/unreachable_for.cpp | 121 + .../dominator_tree/unreachable_for_post.cpp | 118 + .../test/opt/eliminate_dead_const_test.cpp | 847 + .../opt/eliminate_dead_functions_test.cpp | 444 + .../test/opt/eliminate_dead_member_test.cpp | 1187 + .../test/opt/feature_manager_test.cpp | 142 + .../test/opt/fix_storage_class_test.cpp | 840 + .../test/opt/flatten_decoration_test.cpp | 239 + .../opt/fold_spec_const_op_composite_test.cpp | 1513 + .../spirv-tools/test/opt/fold_test.cpp | 7322 +++++ .../test/opt/freeze_spec_const_test.cpp | 133 + .../spirv-tools/test/opt/function_test.cpp | 301 + .../spirv-tools/test/opt/function_utils.h | 55 + .../opt/generate_webgpu_initializers_test.cpp | 347 + .../test/opt/graphics_robust_access_test.cpp | 1562 + .../test/opt/if_conversion_test.cpp | 562 + .../test/opt/inline_opaque_test.cpp | 414 + .../spirv-tools/test/opt/inline_test.cpp | 3945 +++ .../test/opt/insert_extract_elim_test.cpp | 900 + .../test/opt/inst_bindless_check_test.cpp | 8488 ++++++ .../test/opt/inst_buff_addr_check_test.cpp | 620 + .../test/opt/inst_debug_printf_test.cpp | 215 + .../test/opt/instruction_list_test.cpp | 115 + .../spirv-tools/test/opt/instruction_test.cpp | 1536 + .../spirv-tools/test/opt/ir_builder.cpp | 439 + .../spirv-tools/test/opt/ir_context_test.cpp | 1173 + .../spirv-tools/test/opt/ir_loader_test.cpp | 1413 + .../spirv-tools/test/opt/iterator_test.cpp | 267 + .../test/opt/legalize_vector_shuffle_test.cpp | 81 + .../test/opt/line_debug_info_test.cpp | 113 + .../opt/local_access_chain_convert_test.cpp | 1171 + .../opt/local_redundancy_elimination_test.cpp | 203 + .../test/opt/local_single_block_elim.cpp | 1464 + .../test/opt/local_single_store_elim_test.cpp | 1452 + .../test/opt/local_ssa_elim_test.cpp | 3943 +++ .../opt/loop_optimizations/CMakeLists.txt | 41 + .../dependence_analysis.cpp | 4205 +++ .../dependence_analysis_helpers.cpp | 3017 ++ .../fusion_compatibility.cpp | 1785 ++ .../opt/loop_optimizations/fusion_illegal.cpp | 1592 + .../opt/loop_optimizations/fusion_legal.cpp | 4580 +++ .../opt/loop_optimizations/fusion_pass.cpp | 717 + .../hoist_all_loop_types.cpp | 285 + .../hoist_double_nested_loops.cpp | 162 + .../hoist_from_independent_loops.cpp | 201 + .../loop_optimizations/hoist_simple_case.cpp | 126 + .../hoist_single_nested_loops.cpp | 209 + .../hoist_without_preheader.cpp | 197 + .../test/opt/loop_optimizations/lcssa.cpp | 607 + .../loop_optimizations/loop_descriptions.cpp | 421 + .../opt/loop_optimizations/loop_fission.cpp | 3491 +++ .../opt/loop_optimizations/nested_loops.cpp | 795 + .../loop_optimizations/pch_test_opt_loop.cpp | 15 + .../loop_optimizations/pch_test_opt_loop.h | 25 + .../test/opt/loop_optimizations/peeling.cpp | 1186 + .../opt/loop_optimizations/peeling_pass.cpp | 1099 + .../loop_optimizations/unroll_assumptions.cpp | 1515 + .../opt/loop_optimizations/unroll_simple.cpp | 3406 +++ .../test/opt/loop_optimizations/unswitch.cpp | 967 + .../spirv-tools/test/opt/module_test.cpp | 341 + .../spirv-tools/test/opt/module_utils.h | 34 + .../spirv-tools/test/opt/optimizer_test.cpp | 767 + .../spirv-tools/test/opt/pass_fixture.h | 295 + .../test/opt/pass_manager_test.cpp | 191 + .../test/opt/pass_merge_return_test.cpp | 2572 ++ .../test/opt/pass_remove_duplicates_test.cpp | 646 + .../spirv-tools/test/opt/pass_utils.cpp | 102 + third_party/spirv-tools/test/opt/pass_utils.h | 84 + .../spirv-tools/test/opt/pch_test_opt.cpp | 15 + .../spirv-tools/test/opt/pch_test_opt.h | 25 + .../test/opt/private_to_local_test.cpp | 501 + .../spirv-tools/test/opt/propagator_test.cpp | 219 + .../test/opt/reduce_load_size_test.cpp | 424 + .../test/opt/redundancy_elimination_test.cpp | 340 + .../test/opt/register_liveness.cpp | 1322 + .../test/opt/relax_float_ops_test.cpp | 142 + .../test/opt/replace_invalid_opc_test.cpp | 566 + .../spirv-tools/test/opt/scalar_analysis.cpp | 1221 + .../test/opt/scalar_replacement_test.cpp | 2084 ++ .../opt/set_spec_const_default_value_test.cpp | 1077 + .../test/opt/simplification_test.cpp | 365 + .../opt/split_invalid_unreachable_test.cpp | 155 + .../test/opt/strength_reduction_test.cpp | 438 + .../opt/strip_atomic_counter_memory_test.cpp | 406 + .../test/opt/strip_debug_info_test.cpp | 232 + .../test/opt/strip_reflect_info_test.cpp | 231 + .../test/opt/struct_cfg_analysis_test.cpp | 1531 + .../test/opt/type_manager_test.cpp | 1174 + .../spirv-tools/test/opt/types_test.cpp | 389 + .../spirv-tools/test/opt/unify_const_test.cpp | 996 + .../test/opt/upgrade_memory_model_test.cpp | 2272 ++ .../spirv-tools/test/opt/utils_test.cpp | 110 + .../spirv-tools/test/opt/value_table_test.cpp | 832 + .../spirv-tools/test/opt/vector_dce_test.cpp | 1356 + .../test/opt/workaround1209_test.cpp | 423 + .../spirv-tools/test/opt/wrap_opkill_test.cpp | 955 + .../spirv-tools/test/parse_number_test.cpp | 970 + third_party/spirv-tools/test/pch_test.cpp | 15 + third_party/spirv-tools/test/pch_test.h | 18 + .../test/preserve_numeric_ids_test.cpp | 159 + .../spirv-tools/test/reduce/CMakeLists.txt | 35 + ...anch_to_simple_conditional_branch_test.cpp | 501 + .../test/reduce/merge_blocks_test.cpp | 652 + .../test/reduce/operand_to_constant_test.cpp | 308 + .../reduce/operand_to_dominating_id_test.cpp | 198 + .../test/reduce/operand_to_undef_test.cpp | 230 + .../test/reduce/reduce_test_util.cpp | 112 + .../test/reduce/reduce_test_util.h | 75 + .../spirv-tools/test/reduce/reducer_test.cpp | 627 + .../test/reduce/remove_block_test.cpp | 358 + .../test/reduce/remove_function_test.cpp | 295 + .../test/reduce/remove_selection_test.cpp | 557 + .../reduce/remove_unused_instruction_test.cpp | 563 + .../remove_unused_struct_member_test.cpp | 238 + ...mple_conditional_branch_to_branch_test.cpp | 486 + .../structured_loop_to_selection_test.cpp | 3628 +++ .../validation_during_reduction_test.cpp | 589 + .../test/scripts/test_compact_ids.py | 100 + .../test/software_version_test.cpp | 67 + .../spirv-tools/test/string_utils_test.cpp | 191 + .../spirv-tools/test/target_env_test.cpp | 165 + third_party/spirv-tools/test/test_fixture.h | 198 + .../spirv-tools/test/text_advance_test.cpp | 134 + .../spirv-tools/test/text_destroy_test.cpp | 75 + .../spirv-tools/test/text_literal_test.cpp | 412 + .../test/text_start_new_inst_test.cpp | 75 + .../test/text_to_binary.annotation_test.cpp | 549 + .../test/text_to_binary.barrier_test.cpp | 170 + .../test/text_to_binary.composite_test.cpp | 49 + .../test/text_to_binary.constant_test.cpp | 841 + .../test/text_to_binary.control_flow_test.cpp | 427 + .../test/text_to_binary.debug_test.cpp | 215 + ...ext_to_binary.device_side_enqueue_test.cpp | 112 + .../test/text_to_binary.extension_test.cpp | 909 + .../test/text_to_binary.function_test.cpp | 81 + .../test/text_to_binary.group_test.cpp | 76 + .../test/text_to_binary.image_test.cpp | 276 + .../test/text_to_binary.literal_test.cpp | 125 + .../test/text_to_binary.memory_test.cpp | 426 + .../test/text_to_binary.misc_test.cpp | 58 + .../test/text_to_binary.mode_setting_test.cpp | 303 + .../test/text_to_binary.pipe_storage_test.cpp | 126 + .../text_to_binary.reserved_sampling_test.cpp | 63 + .../text_to_binary.subgroup_dispatch_test.cpp | 122 + .../text_to_binary.type_declaration_test.cpp | 293 + .../spirv-tools/test/text_to_binary_test.cpp | 269 + .../spirv-tools/test/text_word_get_test.cpp | 254 + third_party/spirv-tools/test/timer_test.cpp | 142 + .../spirv-tools/test/tools/CMakeLists.txt | 21 + third_party/spirv-tools/test/tools/expect.py | 708 + .../spirv-tools/test/tools/expect_unittest.py | 82 + .../spirv-tools/test/tools/opt/CMakeLists.txt | 25 + .../spirv-tools/test/tools/opt/flags.py | 406 + .../spirv-tools/test/tools/opt/oconfig.py | 73 + .../spirv-tools/test/tools/placeholder.py | 213 + .../test/tools/spirv_test_framework.py | 394 + .../tools/spirv_test_framework_unittest.py | 158 + third_party/spirv-tools/test/unit_spirv.cpp | 51 + third_party/spirv-tools/test/unit_spirv.h | 211 + .../spirv-tools/test/util/CMakeLists.txt | 21 + .../spirv-tools/test/util/bit_vector_test.cpp | 164 + .../spirv-tools/test/util/bitutils_test.cpp | 193 + .../spirv-tools/test/util/ilist_test.cpp | 325 + .../test/util/small_vector_test.cpp | 598 + .../spirv-tools/test/val/CMakeLists.txt | 96 + .../spirv-tools/test/val/pch_test_val.cpp | 15 + .../spirv-tools/test/val/pch_test_val.h | 19 + .../test/val/val_adjacency_test.cpp | 707 + .../test/val/val_arithmetics_test.cpp | 1422 + .../spirv-tools/test/val/val_atomics_test.cpp | 2276 ++ .../test/val/val_barriers_test.cpp | 1665 + .../spirv-tools/test/val/val_bitwise_test.cpp | 549 + .../test/val/val_builtins_test.cpp | 3903 +++ .../test/val/val_capability_test.cpp | 2915 ++ .../spirv-tools/test/val/val_cfg_test.cpp | 4603 +++ .../test/val/val_code_generator.cpp | 224 + .../spirv-tools/test/val/val_code_generator.h | 49 + .../test/val/val_composites_test.cpp | 1999 ++ .../test/val/val_constants_test.cpp | 483 + .../test/val/val_conversion_test.cpp | 1721 ++ .../spirv-tools/test/val/val_data_test.cpp | 949 + .../test/val/val_decoration_test.cpp | 7164 +++++ .../test/val/val_derivatives_test.cpp | 195 + .../spirv-tools/test/val/val_entry_point.cpp | 76 + .../test/val/val_explicit_reserved_test.cpp | 122 + .../test/val/val_ext_inst_test.cpp | 9337 ++++++ ...extension_spv_khr_terminate_invocation.cpp | 150 + .../test/val/val_extensions_test.cpp | 349 + .../spirv-tools/test/val/val_fixtures.h | 186 + .../test/val/val_function_test.cpp | 843 + .../spirv-tools/test/val/val_id_test.cpp | 6588 ++++ .../spirv-tools/test/val/val_image_test.cpp | 5178 ++++ .../test/val/val_interfaces_test.cpp | 1415 + .../spirv-tools/test/val/val_layout_test.cpp | 725 + .../spirv-tools/test/val/val_limits_test.cpp | 778 + .../test/val/val_literals_test.cpp | 152 + .../test/val/val_logicals_test.cpp | 1164 + .../spirv-tools/test/val/val_memory_test.cpp | 4485 +++ .../spirv-tools/test/val/val_misc_test.cpp | 231 + .../spirv-tools/test/val/val_modes_test.cpp | 1183 + .../test/val/val_non_semantic_test.cpp | 195 + .../test/val/val_non_uniform_test.cpp | 293 + .../spirv-tools/test/val/val_opencl_test.cpp | 275 + .../test/val/val_primitives_test.cpp | 321 + .../test/val/val_small_type_uses_test.cpp | 338 + .../spirv-tools/test/val/val_ssa_test.cpp | 1450 + .../spirv-tools/test/val/val_state_test.cpp | 190 + .../spirv-tools/test/val/val_storage_test.cpp | 319 + .../test/val/val_type_unique_test.cpp | 271 + .../test/val/val_validation_state_test.cpp | 361 + .../spirv-tools/test/val/val_version_test.cpp | 300 + .../spirv-tools/test/val/val_webgpu_test.cpp | 424 + third_party/spirv-tools/tools/CMakeLists.txt | 75 + third_party/spirv-tools/tools/as/as.cpp | 154 + .../spirv-tools/tools/cfg/bin_to_dot.cpp | 187 + .../spirv-tools/tools/cfg/bin_to_dot.h | 28 + third_party/spirv-tools/tools/cfg/cfg.cpp | 127 + third_party/spirv-tools/tools/dis/dis.cpp | 216 + .../spirv-tools/tools/emacs/50spirv-tools.el | 40 + .../spirv-tools/tools/emacs/CMakeLists.txt | 48 + third_party/spirv-tools/tools/fuzz/fuzz.cpp | 786 + third_party/spirv-tools/tools/io.h | 86 + .../spirv-tools/tools/lesspipe/CMakeLists.txt | 28 + .../tools/lesspipe/spirv-lesspipe.sh | 27 + third_party/spirv-tools/tools/link/linker.cpp | 160 + third_party/spirv-tools/tools/opt/opt.cpp | 926 + .../spirv-tools/tools/reduce/reduce.cpp | 368 + .../spirv-tools/tools/sva/.eslintrc.json | 25 + third_party/spirv-tools/tools/sva/.gitignore | 6 + third_party/spirv-tools/tools/sva/README.md | 41 + third_party/spirv-tools/tools/sva/bin/sva.js | 32 + third_party/spirv-tools/tools/sva/mocha.opts | 1 + .../spirv-tools/tools/sva/package.json | 25 + .../spirv-tools/tools/sva/rollup.config.js | 7 + .../spirv-tools/tools/sva/src/assembler.js | 98 + .../tools/sva/src/assembler_test.js | 165 + third_party/spirv-tools/tools/sva/src/ast.js | 141 + .../spirv-tools/tools/sva/src/lexer.js | 363 + .../spirv-tools/tools/sva/src/lexer_test.js | 191 + .../spirv-tools/tools/sva/src/parser.js | 283 + .../spirv-tools/tools/sva/src/parser_test.js | 489 + .../spirv-tools/tools/sva/src/spirv.data.js | 4567 +++ third_party/spirv-tools/tools/sva/src/sva.js | 40 + .../spirv-tools/tools/sva/src/token.js | 55 + .../tools/sva/tests/empty_main.spv_asm | 18 + .../spirv-tools/tools/sva/tests/index.html | 23 + .../tools/sva/tests/simple.spv_asm | 30 + .../tools/sva/tools/process_grammar.rb | 119 + third_party/spirv-tools/tools/sva/yarn.lock | 1778 ++ .../spirv-tools/tools/util/cli_consumer.cpp | 45 + .../spirv-tools/tools/util/cli_consumer.h | 31 + third_party/spirv-tools/tools/val/val.cpp | 195 + .../spirv-tools/utils/check_code_format.sh | 39 + .../spirv-tools/utils/check_copyright.py | 227 + .../spirv-tools/utils/check_symbol_exports.py | 98 + .../spirv-tools/utils/fixup_fuzz_result.py | 25 + .../utils/generate_grammar_tables.py | 874 + .../utils/generate_language_headers.py | 185 + .../utils/generate_registry_tables.py | 90 + .../spirv-tools/utils/generate_vim_syntax.py | 205 + third_party/spirv-tools/utils/git-sync-deps | 282 + third_party/spirv-tools/utils/roll_deps.sh | 46 + .../spirv-tools/utils/update_build_version.py | 148 + .../spirv-tools/utils/vscode/.gitignore | 1 + .../spirv-tools/utils/vscode/README.md | 17 + .../spirv-tools/utils/vscode/extension.js | 66 + .../spirv-tools/utils/vscode/install.bat | 30 + .../spirv-tools/utils/vscode/install.sh | 32 + .../spirv-tools/utils/vscode/package.json | 39 + .../spirv-tools/utils/vscode/spirv.json | 247 + .../spirv-tools/utils/vscode/spirv.json.tmpl | 72 + .../utils/vscode/src/grammar/grammar.go | 81 + .../spirv-tools/utils/vscode/src/langsvr.go | 527 + .../spirv-tools/utils/vscode/src/lsp/LICENSE | 202 + .../utils/vscode/src/lsp/README.md | 5 + .../utils/vscode/src/lsp/jsonrpc2/handler.go | 134 + .../utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go | 416 + .../utils/vscode/src/lsp/jsonrpc2/stream.go | 160 + .../utils/vscode/src/lsp/jsonrpc2/wire.go | 148 + .../utils/vscode/src/lsp/protocol/context.go | 29 + .../utils/vscode/src/lsp/protocol/doc.go | 26 + .../utils/vscode/src/lsp/protocol/enums.go | 256 + .../utils/vscode/src/lsp/protocol/log.go | 258 + .../utils/vscode/src/lsp/protocol/protocol.go | 86 + .../utils/vscode/src/lsp/protocol/span.go | 137 + .../utils/vscode/src/lsp/protocol/tsclient.go | 221 + .../vscode/src/lsp/protocol/tsprotocol.go | 4630 +++ .../utils/vscode/src/lsp/protocol/tsserver.go | 842 + .../utils/vscode/src/lsp/span/parse.go | 110 + .../utils/vscode/src/lsp/span/span.go | 295 + .../utils/vscode/src/lsp/span/token.go | 161 + .../utils/vscode/src/lsp/span/token111.go | 49 + .../utils/vscode/src/lsp/span/token112.go | 26 + .../utils/vscode/src/lsp/span/uri.go | 162 + .../utils/vscode/src/lsp/span/utf16.go | 104 + .../utils/vscode/src/parser/parser.go | 818 + .../utils/vscode/src/schema/schema.go | 25094 ++++++++++++++++ .../utils/vscode/src/schema/schema.go.tmpl | 156 + .../utils/vscode/src/tools/gen-grammar.go | 239 + 4868 files changed, 977176 insertions(+), 1750 deletions(-) create mode 100644 third_party/glslang/.appveyor.yml create mode 100644 third_party/glslang/.clang-format create mode 100755 third_party/glslang/.gitattributes create mode 100644 third_party/glslang/.gitignore create mode 100644 third_party/glslang/.gn create mode 100644 third_party/glslang/.travis.yml create mode 100644 third_party/glslang/Android.mk create mode 100644 third_party/glslang/BUILD.bazel create mode 100644 third_party/glslang/BUILD.gn create mode 100644 third_party/glslang/CHANGES.md create mode 100644 third_party/glslang/CMakeLists.txt create mode 100644 third_party/glslang/CODE_OF_CONDUCT.md create mode 100644 third_party/glslang/ChooseMSVCCRT.cmake create mode 100644 third_party/glslang/DEPS create mode 100644 third_party/glslang/External/CMakeLists.txt create mode 100644 third_party/glslang/LICENSE create mode 100644 third_party/glslang/LICENSE.txt create mode 100644 third_party/glslang/OGLCompilersDLL/CMakeLists.txt create mode 100644 third_party/glslang/OGLCompilersDLL/InitializeDll.cpp create mode 100644 third_party/glslang/OGLCompilersDLL/InitializeDll.h create mode 100644 third_party/glslang/OGLCompilersDLL/tnt/CMakeLists.txt create mode 100644 third_party/glslang/README-spirv-remap.txt create mode 100755 third_party/glslang/README.md create mode 100644 third_party/glslang/SPIRV/CInterface/spirv_c_interface.cpp create mode 100644 third_party/glslang/SPIRV/CMakeLists.txt create mode 100644 third_party/glslang/SPIRV/GLSL.ext.AMD.h create mode 100644 third_party/glslang/SPIRV/GLSL.ext.EXT.h create mode 100644 third_party/glslang/SPIRV/GLSL.ext.KHR.h create mode 100644 third_party/glslang/SPIRV/GLSL.ext.NV.h create mode 100644 third_party/glslang/SPIRV/GLSL.std.450.h create mode 100644 third_party/glslang/SPIRV/GlslangToSpv.cpp create mode 100755 third_party/glslang/SPIRV/GlslangToSpv.h create mode 100644 third_party/glslang/SPIRV/InReadableOrder.cpp create mode 100644 third_party/glslang/SPIRV/Logger.cpp create mode 100644 third_party/glslang/SPIRV/Logger.h create mode 100644 third_party/glslang/SPIRV/NonSemanticDebugPrintf.h create mode 100644 third_party/glslang/SPIRV/SPVRemapper.cpp create mode 100644 third_party/glslang/SPIRV/SPVRemapper.h create mode 100644 third_party/glslang/SPIRV/SpvBuilder.cpp create mode 100644 third_party/glslang/SPIRV/SpvBuilder.h create mode 100644 third_party/glslang/SPIRV/SpvPostProcess.cpp create mode 100644 third_party/glslang/SPIRV/SpvTools.cpp create mode 100644 third_party/glslang/SPIRV/SpvTools.h create mode 100644 third_party/glslang/SPIRV/bitutils.h create mode 100644 third_party/glslang/SPIRV/disassemble.cpp create mode 100644 third_party/glslang/SPIRV/disassemble.h create mode 100644 third_party/glslang/SPIRV/doc.cpp create mode 100644 third_party/glslang/SPIRV/doc.h create mode 100644 third_party/glslang/SPIRV/hex_float.h create mode 100644 third_party/glslang/SPIRV/spirv.hpp create mode 100755 third_party/glslang/SPIRV/spvIR.h create mode 100644 third_party/glslang/SPIRV/tnt/CMakeLists.txt create mode 100644 third_party/glslang/StandAlone/CMakeLists.txt create mode 100644 third_party/glslang/StandAlone/DirStackFileIncluder.h create mode 100644 third_party/glslang/StandAlone/ResourceLimits.cpp create mode 100644 third_party/glslang/StandAlone/ResourceLimits.h create mode 100644 third_party/glslang/StandAlone/StandAlone.cpp create mode 100644 third_party/glslang/StandAlone/Worklist.h create mode 100644 third_party/glslang/StandAlone/resource_limits_c.cpp create mode 100644 third_party/glslang/StandAlone/resource_limits_c.h create mode 100644 third_party/glslang/StandAlone/spirv-remap.cpp create mode 100644 third_party/glslang/WORKSPACE create mode 100644 third_party/glslang/_config.yml create mode 100644 third_party/glslang/build_info.h.tmpl create mode 100755 third_party/glslang/build_info.py create mode 100644 third_party/glslang/build_overrides/build.gni create mode 100644 third_party/glslang/build_overrides/glslang.gni create mode 100644 third_party/glslang/build_overrides/spirv_tools.gni create mode 100644 third_party/glslang/glslang/CInterface/glslang_c_interface.cpp create mode 100644 third_party/glslang/glslang/CMakeLists.txt create mode 100644 third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp create mode 100644 third_party/glslang/glslang/GenericCodeGen/Link.cpp create mode 100644 third_party/glslang/glslang/HLSL/hlslAttributes.cpp create mode 100644 third_party/glslang/glslang/HLSL/hlslAttributes.h create mode 100644 third_party/glslang/glslang/HLSL/hlslGrammar.cpp create mode 100644 third_party/glslang/glslang/HLSL/hlslGrammar.h create mode 100644 third_party/glslang/glslang/HLSL/hlslOpMap.cpp create mode 100644 third_party/glslang/glslang/HLSL/hlslOpMap.h create mode 100755 third_party/glslang/glslang/HLSL/hlslParseHelper.cpp create mode 100644 third_party/glslang/glslang/HLSL/hlslParseHelper.h create mode 100644 third_party/glslang/glslang/HLSL/hlslParseables.cpp create mode 100644 third_party/glslang/glslang/HLSL/hlslParseables.h create mode 100644 third_party/glslang/glslang/HLSL/hlslScanContext.cpp create mode 100644 third_party/glslang/glslang/HLSL/hlslScanContext.h create mode 100644 third_party/glslang/glslang/HLSL/hlslTokenStream.cpp create mode 100644 third_party/glslang/glslang/HLSL/hlslTokenStream.h create mode 100644 third_party/glslang/glslang/HLSL/hlslTokens.h create mode 100644 third_party/glslang/glslang/HLSL/pch.h create mode 100644 third_party/glslang/glslang/Include/BaseTypes.h create mode 100644 third_party/glslang/glslang/Include/Common.h create mode 100644 third_party/glslang/glslang/Include/ConstantUnion.h create mode 100644 third_party/glslang/glslang/Include/InfoSink.h create mode 100644 third_party/glslang/glslang/Include/InitializeGlobals.h create mode 100644 third_party/glslang/glslang/Include/PoolAlloc.h create mode 100644 third_party/glslang/glslang/Include/ResourceLimits.h create mode 100644 third_party/glslang/glslang/Include/ShHandle.h create mode 100644 third_party/glslang/glslang/Include/Types.h create mode 100644 third_party/glslang/glslang/Include/arrays.h create mode 100644 third_party/glslang/glslang/Include/glslang_c_interface.h create mode 100644 third_party/glslang/glslang/Include/glslang_c_shader_types.h create mode 100644 third_party/glslang/glslang/Include/intermediate.h create mode 100644 third_party/glslang/glslang/MachineIndependent/Constant.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/InfoSink.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/Initialize.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/Initialize.h create mode 100644 third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp create mode 100755 third_party/glslang/glslang/MachineIndependent/Intermediate.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/LiveTraverser.h create mode 100644 third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/ParseHelper.h create mode 100644 third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/RemoveTree.h create mode 100644 third_party/glslang/glslang/MachineIndependent/Scan.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/Scan.h create mode 100644 third_party/glslang/glslang/MachineIndependent/ScanContext.h create mode 100644 third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/SymbolTable.h create mode 100644 third_party/glslang/glslang/MachineIndependent/Versions.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/Versions.h create mode 100644 third_party/glslang/glslang/MachineIndependent/attribute.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/attribute.h create mode 100644 third_party/glslang/glslang/MachineIndependent/gl_types.h create mode 100644 third_party/glslang/glslang/MachineIndependent/glslang.m4 create mode 100644 third_party/glslang/glslang/MachineIndependent/glslang.y create mode 100644 third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp.h create mode 100644 third_party/glslang/glslang/MachineIndependent/intermOut.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/iomapper.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/iomapper.h create mode 100644 third_party/glslang/glslang/MachineIndependent/limits.cpp create mode 100755 third_party/glslang/glslang/MachineIndependent/linkValidate.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/localintermediate.h create mode 100644 third_party/glslang/glslang/MachineIndependent/parseConst.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/parseVersions.h create mode 100644 third_party/glslang/glslang/MachineIndependent/pch.h create mode 100644 third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.h create mode 100644 third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp create mode 100755 third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h create mode 100644 third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/propagateNoContraction.h create mode 100644 third_party/glslang/glslang/MachineIndependent/reflection.cpp create mode 100644 third_party/glslang/glslang/MachineIndependent/reflection.h create mode 100644 third_party/glslang/glslang/OSDependent/Unix/CMakeLists.txt create mode 100644 third_party/glslang/glslang/OSDependent/Unix/ossource.cpp create mode 100644 third_party/glslang/glslang/OSDependent/Web/CMakeLists.txt create mode 100644 third_party/glslang/glslang/OSDependent/Web/glslang.after.js create mode 100644 third_party/glslang/glslang/OSDependent/Web/glslang.js.cpp create mode 100644 third_party/glslang/glslang/OSDependent/Web/glslang.pre.js create mode 100644 third_party/glslang/glslang/OSDependent/Windows/CMakeLists.txt create mode 100644 third_party/glslang/glslang/OSDependent/Windows/main.cpp create mode 100644 third_party/glslang/glslang/OSDependent/Windows/ossource.cpp create mode 100644 third_party/glslang/glslang/OSDependent/osinclude.h create mode 100644 third_party/glslang/glslang/Public/ShaderLang.h create mode 100644 third_party/glslang/glslang/tnt/CMakeLists.txt create mode 100755 third_party/glslang/glslang/updateGrammar create mode 100644 third_party/glslang/gtests/AST.FromFile.cpp create mode 100644 third_party/glslang/gtests/BuiltInResource.FromFile.cpp create mode 100644 third_party/glslang/gtests/CMakeLists.txt create mode 100644 third_party/glslang/gtests/Config.FromFile.cpp create mode 100644 third_party/glslang/gtests/HexFloat.cpp create mode 100755 third_party/glslang/gtests/Hlsl.FromFile.cpp create mode 100644 third_party/glslang/gtests/Initializer.h create mode 100755 third_party/glslang/gtests/Link.FromFile.Vk.cpp create mode 100755 third_party/glslang/gtests/Link.FromFile.cpp create mode 100644 third_party/glslang/gtests/Pp.FromFile.cpp create mode 100644 third_party/glslang/gtests/README.md create mode 100644 third_party/glslang/gtests/Remap.FromFile.cpp create mode 100644 third_party/glslang/gtests/Settings.cpp create mode 100644 third_party/glslang/gtests/Settings.h create mode 100644 third_party/glslang/gtests/Spv.FromFile.cpp create mode 100644 third_party/glslang/gtests/TestFixture.cpp create mode 100755 third_party/glslang/gtests/TestFixture.h create mode 100644 third_party/glslang/gtests/main.cpp create mode 100644 third_party/glslang/gtests/pch.h create mode 100644 third_party/glslang/hlsl/CMakeLists.txt create mode 100644 third_party/glslang/hlsl/stub.cpp create mode 100644 third_party/glslang/known_good.json create mode 100644 third_party/glslang/known_good_khr.json create mode 100755 third_party/glslang/kokoro/android-ndk-build/build-docker.sh create mode 100755 third_party/glslang/kokoro/android-ndk-build/build.sh create mode 100644 third_party/glslang/kokoro/android-ndk-build/continuous.cfg create mode 100644 third_party/glslang/kokoro/android-ndk-build/presubmit.cfg create mode 100755 third_party/glslang/kokoro/license-check/build-docker.sh create mode 100755 third_party/glslang/kokoro/license-check/build.sh create mode 100644 third_party/glslang/kokoro/license-check/continuous.cfg create mode 100644 third_party/glslang/kokoro/license-check/presubmit.cfg create mode 100755 third_party/glslang/kokoro/linux-clang-cmake/build-docker.sh create mode 100755 third_party/glslang/kokoro/linux-clang-cmake/build.sh create mode 100644 third_party/glslang/kokoro/linux-clang-cmake/shared/continuous.cfg create mode 100644 third_party/glslang/kokoro/linux-clang-cmake/shared/presubmit.cfg create mode 100644 third_party/glslang/kokoro/linux-clang-cmake/static/continuous.cfg create mode 100644 third_party/glslang/kokoro/linux-clang-cmake/static/presubmit.cfg create mode 100755 third_party/glslang/kokoro/linux-clang-gn/build-docker.sh create mode 100755 third_party/glslang/kokoro/linux-clang-gn/build.sh create mode 100644 third_party/glslang/kokoro/linux-clang-gn/continuous.cfg create mode 100644 third_party/glslang/kokoro/linux-clang-gn/presubmit.cfg create mode 100644 third_party/glslang/kokoro/linux-clang-release-bazel/build.sh create mode 100644 third_party/glslang/kokoro/linux-clang-release-bazel/continuous.cfg create mode 100644 third_party/glslang/kokoro/linux-clang-release-bazel/presubmit.cfg create mode 100755 third_party/glslang/kokoro/linux-gcc-cmake/build-docker.sh create mode 100755 third_party/glslang/kokoro/linux-gcc-cmake/build.sh create mode 100644 third_party/glslang/kokoro/linux-gcc-cmake/shared/continuous.cfg create mode 100644 third_party/glslang/kokoro/linux-gcc-cmake/shared/presubmit.cfg create mode 100644 third_party/glslang/kokoro/linux-gcc-cmake/static/continuous.cfg create mode 100644 third_party/glslang/kokoro/linux-gcc-cmake/static/presubmit.cfg create mode 100644 third_party/glslang/kokoro/macos-clang-release-bazel/build.sh create mode 100644 third_party/glslang/kokoro/macos-clang-release-bazel/continuous.cfg create mode 100644 third_party/glslang/kokoro/macos-clang-release-bazel/presubmit.cfg create mode 100644 third_party/glslang/kokoro/windows-msvc-2015-release-bazel/build.bat create mode 100644 third_party/glslang/kokoro/windows-msvc-2015-release-bazel/continuous.cfg create mode 100644 third_party/glslang/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg create mode 100644 third_party/glslang/license-checker.cfg create mode 100644 third_party/glslang/ndk_test/Android.mk create mode 100644 third_party/glslang/ndk_test/jni/Application.mk create mode 100644 third_party/glslang/ndk_test/test.cpp create mode 100644 third_party/glslang/standalone.gclient create mode 100644 third_party/glslang/tnt/CMakeLists.txt create mode 100644 third_party/glslang/tnt/README.md create mode 100755 third_party/glslang/update_glslang_sources.py create mode 100755 third_party/spirv-cross/.clang-format create mode 100644 third_party/spirv-cross/.gitignore create mode 100644 third_party/spirv-cross/.travis.yml create mode 100644 third_party/spirv-cross/CMakeLists.txt create mode 100644 third_party/spirv-cross/CODE_OF_CONDUCT.md create mode 100644 third_party/spirv-cross/GLSL.std.450.h create mode 100644 third_party/spirv-cross/LICENSE create mode 100644 third_party/spirv-cross/Makefile create mode 100644 third_party/spirv-cross/README.md create mode 100644 third_party/spirv-cross/appveyor.yml create mode 100644 third_party/spirv-cross/build_glslang_spirv_tools.sh create mode 100755 third_party/spirv-cross/checkout_glslang_spirv_tools.sh create mode 100644 third_party/spirv-cross/cmake/gitversion.in.h create mode 100755 third_party/spirv-cross/format_all.sh create mode 100644 third_party/spirv-cross/gn/BUILD.gn create mode 100644 third_party/spirv-cross/include/spirv_cross/barrier.hpp create mode 100644 third_party/spirv-cross/include/spirv_cross/external_interface.h create mode 100644 third_party/spirv-cross/include/spirv_cross/image.hpp create mode 100644 third_party/spirv-cross/include/spirv_cross/internal_interface.hpp create mode 100644 third_party/spirv-cross/include/spirv_cross/sampler.hpp create mode 100644 third_party/spirv-cross/include/spirv_cross/thread_group.hpp create mode 100644 third_party/spirv-cross/main.cpp create mode 100644 third_party/spirv-cross/pkg-config/spirv-cross-c-shared.pc.in create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chain-load-composite.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chains.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chains.force-uav.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/address-buffers.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/atomic.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/barriers.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/builtins.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/globallycoherent.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/image.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/image.nonwritable-uav-texture.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/inverse.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/num-workgroups-alone.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/num-workgroups-with-builtins.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/outer-product.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/rwbuffer-matrix.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/shared.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/spec-constant-op-member-array.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/spec-constant-work-group-size.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/ssbo-array-length.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/comp/ssbo-array.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic-color-3comp.sm30.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic-color-3comp.sm50.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/bit-conversions.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/boolean-mix.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/builtins.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/bvec-operations.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/clip-cull-distance.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/combined-texture-sampler-parameter.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/combined-texture-sampler-shadow.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/demote-to-helper.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/depth-greater-than.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/depth-less-than.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/dual-source-blending.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/early-fragment-test.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/fp16-packing.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/front-facing.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-selective.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-uav.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/input-attachment-ms.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/input-attachment.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/io-block.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/matrix-input.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/mod.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/mrt.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/no-return.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/no-return2.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/partial-write-preserve.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/point-coord-compat.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/query-lod.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/readonly-coherent-ssbo.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/resources.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/row-major-layout-in-struct.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-cmp-level-zero.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-in-and-out.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-in.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-out.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sampler-array.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sampler-image-arrays.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/spec-constant-block-size.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/swizzle-scalar.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling-ms.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling.sm30.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texture-proj-shadow.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texture-size-combined-image-sampler.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/unorm-snorm-packing.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/frag/various-glsl-ops.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/basic.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/clip-cull-distance.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/instancing.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/locations.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/matrix-attribute.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/matrix-output.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/no-input.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/point-size-compat.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/qualifiers.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/return-array.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/sampler-buffers.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/struct-composite-decl.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-hlsl/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/amd/shader_trinary_minmax.msl21.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_iadd.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_sar.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_sdiv.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_slr.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/buffer-write.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/multiple-entry.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/quantize.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/relaxed-block-layout.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/variable-pointers-2.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/default-member-names.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/locations-components.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/min-lod.msl22.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/op-constant-null.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/op-image-sampled-image.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/phi-loop-variable.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-atomics.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-atomics.asm.graphics-robust-access.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/undef-variable-store.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unord-relational-op.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/copy-memory-interface.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/fake-builtin-input.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/invariant.msl21.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packing-test.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/access-private-workgroup-in-function.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/array-length.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/array-length.msl2.argument.discrete.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/atomic.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/barriers.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.dispatchbase.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.dispatchbase.msl11.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.inline-block.msl2.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/builtins.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/cfg-preserve-parameter.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/coherent-block.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/coherent-image.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/complex-composite-constant-array.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/complex-type-alias.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-array-initialization.force-native-array.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-construct.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/copy-array-of-arrays.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/culling.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/defer-parens.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/dowhile.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/force-recompile-hooks.swizzle.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/functions.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/global-invocation-id.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/image-atomic-automatic-bindings.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/image-cube-array-load-store.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/image.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/insert.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/inverse.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/local-invocation-id.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/local-invocation-index.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/mat3.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/mod.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/modf.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/outer-product.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/packing-test-1.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/packing-test-2.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/read-write-only.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/rmw-opt.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/shared-array-of-arrays.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/shared.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/spec-constant-op-member-array.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/spec-constant-work-group-size.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/storage-buffer-std140-vector-array.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-layout.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-nested.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-packing.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/torture-loop.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/type-alias.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/udiv.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/comp/writable-ssbo.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/image-ms.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/query-levels.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/basic.desktop.sso.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/flatten/basic.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/flatten/multiindex.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/flatten/push-constant.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/flatten/rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/flatten/struct.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/flatten/swizzle.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/flatten/types.flatten.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/argument-buffers.msl2.argument.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-array-lut.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/barycentric-nv.msl22.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/basic.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/binary-func-unpack-pack-arguments.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/binary-unpack-pack-arguments.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/bitcasting.1d-as-2d.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/bitcasting.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/buffer-read-write.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/builtins.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/clip-distance-varying.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/composite-extract-forced-temporary.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/constant-array.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/depth-greater-than.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/depth-less-than.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/disable-frag-output.frag-output.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/dual-source-blending.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/early-fragment-tests.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/false-loop-init.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/flush_params.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/for-loop-init.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/fp16-packing.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/fragment-component-padding.pad-fragment.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/front-facing.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/gather-dref.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/gather-offset.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/helper-invocation.msl21.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/illegal-name-test-0.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/image-query-lod.msl22.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/in_block.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/in_mat.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.arrayed-subpass.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.multiview.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/interpolation-qualifiers-block.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/interpolation-qualifiers.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/mix.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/mrt-array.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/nonuniform-qualifier.msl2.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/packed-expression-vector-shuffle.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/packing-test-3.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/pixel-interlock-ordered.msl2.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/pls.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/post-depth-coverage.ios.msl2.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/post-depth-coverage.msl23.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/private-variable-prototype-declaration.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/readonly-ssbo.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-depth-separate-image-sampler.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-position-func.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-position.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-1d-lod.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-image-arrays.msl2.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-ms.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/separate-image-sampler-argument.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/shader-arithmetic-8bit.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/spec-constant-block-size.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/spec-constant-ternary.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/stencil-export.msl21.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/subgroup-builtins.msl22.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/swizzle.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-cube-array.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-multisample-array.msl21.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-proj-shadow.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/ubo_layout.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/frag/write-depth-in-function.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/intel/shader-integer-functions2.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/legacy/vert/transpose.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/basic.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/basic.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-matrix.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-struct.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/matrix-output.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/reload-tess-level.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/reload-tess-level.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/struct-output.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/water_tess.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tesc/water_tess.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/input-array.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/input-types.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/load-control-point-array-of-matrix.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/load-control-point-array.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/quad.domain.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/quad.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/set-from-function.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/triangle-tess-level.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/triangle.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/tese/water_tess.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.capture.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.for-tess.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/clip-distance-block.no-user-varying.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/clip-distance-block.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/copy.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/dynamic.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/float-math.invariant-float-math.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/float-math.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/functions.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/in_out_array_mat.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/interface-block-block-composites.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/interface-block-block-composites.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/interpolation-qualifiers-block.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/interpolation-qualifiers.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/invariant.msl21.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/leaf-function.capture.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/leaf-function.for-tess.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/no-disable-vertex-out.frag-output.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.for-tess.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_buff.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_buff_atomic.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_tex.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/out_block.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/packed-bool-to-uint.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/packed-bool2-to-packed_uint2.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/packed_matrix.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/pointsize.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/resource-arrays-leaf.ios.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/resource-arrays.ios.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/return-array.force-native-array.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/return-array.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/set_builtin_in_func.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/sign-int-types.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/signedness-mismatch.shader-inputs.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/ubo.alignment.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vert/ubo.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/push-constant.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/spec-constant.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/small-storage.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/depth-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/global-constant-arrays.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/vert/array-missing-copies.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders-ue4/asm/vert/texture-buffer.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/amd/gcn_shader.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/amd/shader_ballot.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/amd/shader_group_vote.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/amd/shader_trinary_minmax.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_iadd.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_iequal.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_sar.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_sdiv.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_slr.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/builtin-compute-bitcast.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/decoration-group.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/hlsl-functionality.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/logical.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/multiple-entry.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/nmin-max-clamp.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/op-phi-swap.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/quantize.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/recompile-block-naming.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/specialization-constant-workgroup.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/comp/switch-break-ladder.asm.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/complex-name-workarounds.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/default-member-names.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/do-while-statement-fallback.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/for-loop-phi-only-continue.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.vk.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.vk.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/inf-nan-constant-double.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/invalidation.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/locations-components.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/loop-header-to-continue.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/multi-for-loop-init.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/op-constant-null.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/op-phi-swap-continue-block.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/pack-and-unpack-uint2.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/phi-loop-variable.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/temporary-name-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/temporary-phi-hoisting.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/undef-variable-store.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/frag/vector-shuffle-oom.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/geom/block-name-namespace.asm.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/geom/inout-split-access-chain-handle.asm.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/geom/split-access-chain-input.asm.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/geom/unroll-glposition-load.asm.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/tese/unroll-input-array-load.asm.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/empty-io.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/global-builtin.sso.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant-block.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant-block.sso.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant.sso.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/atomic.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/bake_gradient.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/barriers.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/basic.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/casts.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/cfg-preserve-parameter.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/cfg.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/coherent-block.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/coherent-image.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/composite-construct.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/culling.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/defer-parens.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/dowhile.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/generate_height.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/image.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/insert.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/mat3.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/mod.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/modf.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/outer-product.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/read-write-only.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/rmw-opt.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/shared.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/ssbo-array-length.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/ssbo-array.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/struct-layout.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/struct-packing.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/torture-loop.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/type-alias.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/comp/udiv.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/enhanced-layouts.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/extended-arithmetic.desktop.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/fp64.desktop.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/int64.desktop.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/clip-cull-distance.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/depth-greater-than.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/depth-less-than.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/dual-source-blending.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-ms.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-query.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-size.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/in-block-qualifiers.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/layout-component.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/query-levels.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/query-lod.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/sampler-ms-query.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/stencil-export.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/geom/basic.desktop.sso.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/geom/viewport-index.desktop.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/tesc/basic.desktop.sso.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/tese/triangle.desktop.sso.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/basic.desktop.sso.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/clip-cull-distance.desktop.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/out-block-qualifiers.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/array.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/basic.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/copy.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/dynamic.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/matrix-conversion.flatten.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/matrixindex.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/multiindex.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/push-constant.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/struct.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/struct.rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/swizzle.flatten.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/flatten/types.flatten.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/avoid-expression-lowering-to-loop.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/barycentric-nv.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/basic.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/composite-extract-forced-temporary.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/constant-array.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/false-loop-init.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/flush_params.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/for-loop-init.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/frexp-modf.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/front-facing.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/gather-dref.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/ground.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/helper-invocation.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/image-load-store-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/inside-loop-dominated-variable-preservation.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/loop-dominator-and-switch-default.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/mix.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/partial-write-preserve.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/pixel-interlock-ordered.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/pixel-interlock-unordered.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/pls.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/post-depth-coverage-es.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/post-depth-coverage.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/round-even.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/round.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/sample-interlock-ordered.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/sample-interlock-unordered.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/sample-parameter.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/sampler-ms.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/sampler-proj.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/sampler.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/selection-block-dominator.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/struct-type-unrelated-alias.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/swizzle.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/ubo-load-row-major-workaround.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/ubo_layout.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/basic.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/geometry-passthrough.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/lines-adjacency.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/lines.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/multi-stream.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/points.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/single-invocation.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/transform-feedback-streams.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/triangles-adjacency.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/geom/triangles.geom create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/fragment/explicit-lod.legacy.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/fragment/explicit-lod.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/fragment/fma.legacy.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/fragment/io-blocks.legacy.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/fragment/round.legacy.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/fragment/struct-varying.legacy.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/fragment/switch.legacy.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/vert/implicit-lod.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/vert/io-block.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-varying.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/vert/switch-nested.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/legacy/vert/transpose.legacy.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/tesc/basic.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders/tesc/water_tess.tesc create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/ccw.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/cw.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/equal.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/fractional_even.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/fractional_odd.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/input-array.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/line.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/load-array-of-array.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/patch-input-array.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/triangle.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/tese/water_tess.tese create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/basic.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/ground.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/invariant.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/ocean.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/return-array.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/transform-feedback-decorations.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vert/ubo.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/desktop-mediump.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/desktop-mediump.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment-ms.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-block-size.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-block-size.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/payloads.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/payloads.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/vert/device-group.nocompat.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/vert/multiview.nocompat.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/vert/small-storage.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/vert/small-storage.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/vert/vulkan-vertex.vk.vert create mode 100644 third_party/spirv-cross/reference/opt/shaders/vulkan/vert/vulkan-vertex.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/access-chain-load-store-composite.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/atomic-load-store.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/atomic-result-temporary.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/bitfield-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/bitscan.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/buffer-atomic-nonuniform.asm.sm51.nonuniformresource.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/constant-composite-undef.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/glsl-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.fxconly.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/image-atomic-nonuniform.asm.sm51.nonuniformresource.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/specialization-constant-workgroup.nofxc.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/storage-buffer-basic.invalid.nofxc.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/empty-struct-in-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/nonuniform-bracket-handling-2.nonuniformresource.sm51.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/nonuniform-qualifier-propagation.nonuniformresource.sm51.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/phi.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-callstack.sm51.fxconly.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-control-flow.sm51.fxconly.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-split-functions.sm51.fxconly.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/struct-packing-last-element-array-matrix-rule.invalid.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/subgroup-arithmetic-cast.invalid.nofxc.sm60.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/unordered-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/temporary.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/vert/empty-struct-composite.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/bitfield.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/glsl.std450.fxconly.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/illegal-struct-name.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/subgroups.invalid.nofxc.sm60.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/cbuffer-packing-straddle.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/constant-buffer-array.invalid.sm51.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/fp16.invalid.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/frag-coord.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/native-16bit-types.fxconly.nofxc.sm62.native-16bit.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/pixel-interlock-simple-callstack.sm51.fxconly.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/spec-constant.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/ubo-offset-out-of-order.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/variables.zero-initialize.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/vert/flatten-matrix-input.flatten-matrix-vertex-input.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl-no-opt/vert/pass-array-by-value.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/access-chain-load-composite.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/access-chains.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/access-chains.force-uav.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/address-buffers.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/atomic.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/barriers.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/builtins.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/globallycoherent.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/image.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/image.nonwritable-uav-texture.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/inverse.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/num-workgroups-alone.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/num-workgroups-with-builtins.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/outer-product.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/rwbuffer-matrix.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/shared.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/spec-constant-op-member-array.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/spec-constant-work-group-size.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/ssbo-array-length.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/comp/ssbo-array.comp create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/basic-color-3comp.sm30.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/basic-color-3comp.sm50.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/basic.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/bit-conversions.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/boolean-mix.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/builtins.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/bvec-operations.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/clip-cull-distance.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/combined-texture-sampler-parameter.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/combined-texture-sampler-shadow.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/demote-to-helper.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/depth-greater-than.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/depth-less-than.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/dual-source-blending.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/early-fragment-test.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/fp16-packing.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/front-facing.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-selective.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-uav.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/image-query.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/input-attachment-ms.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/input-attachment.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/io-block.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/matrix-input.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/mod.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/mrt.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/no-return.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/no-return2.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/partial-write-preserve.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/point-coord-compat.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/query-lod.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/readonly-coherent-ssbo.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/resources.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/row-major-layout-in-struct.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/sample-cmp-level-zero.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-in-and-out.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-in.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-out.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/sampler-array.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/sampler-image-arrays.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/spec-constant-block-size.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/swizzle-scalar.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling-ms.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling.sm30.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/texture-proj-shadow.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/texture-size-combined-image-sampler.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/unorm-snorm-packing.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/frag/various-glsl-ops.frag create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/basic.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/clip-cull-distance.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/instancing.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/locations.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/matrix-attribute.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/matrix-output.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/no-input.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/point-size-compat.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/qualifiers.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/return-array.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/sampler-buffers.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/struct-composite-decl.vert create mode 100644 third_party/spirv-cross/reference/shaders-hlsl/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/atomic-load-store.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/atomic-result-temporary.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitcast-fp16-fp32.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitfield-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitscan.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/composite-construct-buffer-struct.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/constant-composite-undef.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/copy-logical-2.spv14.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/copy-logical.spv14.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-array-load-temporary.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-array-load-temporary.force-native-array.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.force-native-array.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/glsl-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/storage-buffer-pointer-argument.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/component-insert-packed-expression.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/empty-struct-in-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-gather.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-type-normal-comparison-usage.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/input-attachment-unused-frag-coord.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/phi.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-callstack.msl2.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-control-flow.msl2.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-split-functions.msl2.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/private-initializer-direct-store.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/subgroup-arithmetic-cast.msl21.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/texture-access.swizzle.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/composite-extract-row-major.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-2.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-3.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-4.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-5.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/packed-vector-extract-insert.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/row-major-split-access-chain.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float2.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float3-one-element.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float3.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x2-col-major.invalid.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x2-row-major.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x3-col-major.invalid.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x3-row-major.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x2-col-major.invalid.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x2-row-major.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x3-col-major.invalid.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x3-row-major.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/temporary.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.multi-patch.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/composite-extract-physical-type-id.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/empty-struct-composite.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/array-copy-threadgroup-memory.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/basic.dynamic-buffer.msl2.invalid.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitcast-16bit-1.invalid.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitcast-16bit-2.invalid.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitfield.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/glsl.std450.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/illegal-struct-name.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/int64.invalid.msl22.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/loop.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/return.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/std140-array-load-composite-construct.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.ios.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-input-component.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-output-component.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-output-component.pad-fragment.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/components/vertex-input-component.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/components/vertex-output-component.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/16bit-constants.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/demote-to-helper.vk.nocompat.msl21.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/depth-image-gather.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/force-active-resources.msl2.argument..force-active.discrete.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/fp16.desktop.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/image-gather.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/in_block_assign.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/min-max-clamp.invalid.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/pixel-interlock-simple-callstack.msl2.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/shadow-compare-global-alias.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subgroups.nocompat.invalid.vk.msl21.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl20.ios.framebuffer-fetch.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl23.framebuffer-fetch.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.ios.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.msl23.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-int.swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-leaf.swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-uint.swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access.swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.argument.msl2.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-offset-out-of-order.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/frag/variables.zero-initialize.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/array-of-vec3.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/array-of-vec4.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/isolated-scalar-access.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/load-store-col-rows.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-std140.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-std430.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-row-major.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major-2.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major-2.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/member-padding.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/std140-array-of-vectors.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-alignment.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing-array-of-scalar.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing-recursive.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-size-padding-array-of-array.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-size-padding.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/vert/functions_nested.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/vert/layer.msl11.invalid.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/vert/pass-array-by-value.force-native-array.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/vert/pass-array-by-value.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/vert/viewport-index.msl2.invalid.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl-no-opt/vulkan/frag/texture-access-function.swizzle.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/amd/shader_trinary_minmax.msl21.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_iadd.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_sar.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_sdiv.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_slr.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/buffer-write.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/multiple-entry.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/quantize.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/relaxed-block-layout.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/variable-pointers-2.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/default-member-names.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/locations-components.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/min-lod.msl22.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/op-constant-null.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/op-image-sampled-image.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/phi-loop-variable.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-atomics.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-atomics.asm.graphics-robust-access.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/undef-variable-store.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/unord-relational-op.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/copy-memory-interface.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/fake-builtin-input.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/invariant.msl21.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/packing-test.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/access-private-workgroup-in-function.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/array-length.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/array-length.msl2.argument.discrete.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/atomic.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/barriers.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/basic.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/basic.dispatchbase.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/basic.dispatchbase.msl11.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/basic.inline-block.msl2.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/builtins.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/cfg-preserve-parameter.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/coherent-block.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/coherent-image.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/complex-composite-constant-array.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/complex-type-alias.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/composite-array-initialization.force-native-array.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/composite-construct.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/copy-array-of-arrays.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/culling.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/defer-parens.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/dowhile.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/force-recompile-hooks.swizzle.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/functions.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/global-invocation-id.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/image-atomic-automatic-bindings.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/image-cube-array-load-store.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/image.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/insert.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/inverse.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/local-invocation-id.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/local-invocation-index.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/mat3.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/mod.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/modf.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/outer-product.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/packing-test-1.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/packing-test-2.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/read-write-only.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/rmw-opt.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/shared-array-of-arrays.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/shared.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/spec-constant-op-member-array.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/spec-constant-work-group-size.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/storage-buffer-std140-vector-array.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/struct-layout.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/struct-nested.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/struct-packing.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/torture-loop.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/type-alias.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/udiv.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/comp/writable-ssbo.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/image-ms.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/query-levels.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/basic.desktop.sso.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/flatten/basic.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/flatten/multiindex.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/flatten/push-constant.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/flatten/rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/flatten/struct.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/flatten/swizzle.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/flatten/types.flatten.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/argument-buffers.msl2.argument.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/array-of-array-lut.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/barycentric-nv.msl22.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/basic.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/binary-func-unpack-pack-arguments.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/binary-unpack-pack-arguments.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/bitcasting.1d-as-2d.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/bitcasting.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/buffer-read-write.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/builtins.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/clip-distance-varying.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/composite-extract-forced-temporary.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/constant-array.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/depth-greater-than.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/depth-less-than.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/disable-frag-output.frag-output.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/dual-source-blending.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/early-fragment-tests.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/false-loop-init.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/flush_params.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/for-loop-init.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/fp16-packing.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/fragment-component-padding.pad-fragment.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/front-facing.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/gather-dref.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/gather-offset.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/helper-invocation.msl21.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/illegal-name-test-0.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/image-query-lod.msl22.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/in_block.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/in_mat.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.arrayed-subpass.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.multiview.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/interpolation-qualifiers-block.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/interpolation-qualifiers.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/mix.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/mrt-array.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/nonuniform-qualifier.msl2.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/packed-expression-vector-shuffle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/packing-test-3.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/pixel-interlock-ordered.msl2.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/pls.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/post-depth-coverage.ios.msl2.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/post-depth-coverage.msl23.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/private-variable-prototype-declaration.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/readonly-ssbo.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sample-depth-separate-image-sampler.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sample-mask.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sample-mask.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sample-position-func.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sample-position.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler-1d-lod.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler-image-arrays.msl2.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler-ms.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/sampler.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/separate-image-sampler-argument.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/shader-arithmetic-8bit.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/spec-constant-block-size.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/spec-constant-ternary.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/stencil-export.msl21.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/subgroup-builtins.msl22.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/texture-cube-array.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/texture-multisample-array.msl21.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/texture-proj-shadow.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/ubo_layout.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/frag/write-depth-in-function.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/intel/shader-integer-functions2.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-msl/legacy/vert/transpose.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/basic.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/basic.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-matrix.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-struct.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/matrix-output.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/reload-tess-level.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/reload-tess-level.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/struct-output.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/water_tess.multi-patch.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tesc/water_tess.tesc create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/input-array.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/input-types.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/load-control-point-array-of-matrix.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/load-control-point-array.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/quad.domain.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/quad.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/set-from-function.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/triangle-tess-level.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/triangle.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/tese/water_tess.tese create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/basic.capture.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/basic.for-tess.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/basic.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/clip-distance-block.no-user-varying.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/clip-distance-block.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/copy.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/dynamic.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/float-math.invariant-float-math.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/float-math.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/functions.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/in_out_array_mat.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/interface-block-block-composites.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/interface-block-block-composites.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/interpolation-qualifiers-block.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/interpolation-qualifiers.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/invariant.msl21.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/leaf-function.capture.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/leaf-function.for-tess.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/no-disable-vertex-out.frag-output.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.for-tess.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_buff.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_buff_atomic.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_tex.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/out_block.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/packed-bool-to-uint.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/packed-bool2-to-packed_uint2.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/packed_matrix.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/pointsize.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/resource-arrays-leaf.ios.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/resource-arrays.ios.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/return-array.force-native-array.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/return-array.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/set_builtin_in_func.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/sign-int-types.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/signedness-mismatch.shader-inputs.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/ubo.alignment.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vert/ubo.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/frag/push-constant.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/frag/spec-constant.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/vert/small-storage.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-tracking-function-call-result.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/atomic-load-store.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/atomic-result-temporary.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitfield-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitscan.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-atomic-nonuniform.vk.nocompat.asm.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer-2.asm.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer.asm.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/constant-composite-undef.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/copy-logical.spv14.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/extended-debug-extinst.invalid.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/glsl-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/image-atomic-nonuniform.vk.nocompat.asm.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/loop-variable-with-initializer.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/nonuniform-bracket-handling.vk.nocompat.asm.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/phi-temporary-copy-loop-variable.asm.invalid.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/combined-image-sampler-dxc-min16float.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/demote-impure-function-call.vk.nocompat.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/discard-impure-function-call.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/do-while-continue-phi.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/do-while-loop-inverted-test.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/early-conditional-return-switch.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/empty-struct-in-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-inverted.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-non-inverted.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-inverted-test.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/image-fetch-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/inliner-dominator-inside-loop.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/ldexp-uint-exponent.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/loop-merge-to-continue.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/nonuniform-bracket-handling-2.vk.nocompat.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/nonuniform-qualifier-propagation.vk.nocompat.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/opaque-id-literal-alias.preserve.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/out-of-order-struct-id.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/phi.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-callstack.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-control-flow.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-split-functions.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/reserved-identifiers.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/selection-merge-to-continue.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/sparse-texture-feedback-uint-code.asm.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/subgroup-arithmetic-cast.nocompat.vk.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-merge-to-continue.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-single-case-multiple-exit-cfg.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/unordered-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/vector-extract-dynamic-spec-constant.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/frag/while-loop-inverted-test.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/geom/store-uint-layer.invalid.asm.geom create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/loop-header-self-continue-break.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/temporary.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/vert/complex-link-by-name.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/vert/complex-link-by-name.force-flattened-io.legacy.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/vert/empty-struct-composite.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/asm/vert/semantic-decoration.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/bitcast-16bit-1.invalid.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/bitcast-16bit-2.invalid.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/bitfield.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/glsl.std450.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/illegal-struct-name.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/inout-struct.invalid.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/loop.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/return.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/shader_ballot_nonuniform_invocations.invalid.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/specialization-constant-evaluation.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups.nocompat.invalid.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/16bit-constants.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/fp16.invalid.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/fragmentMaskFetch_subpassInput.vk.nocompat.invalid.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/fs.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/image-gather.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/multi-dimensional.desktop.invalid.flatten_dim.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/pixel-interlock-simple-callstack.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/sparse-texture-clamp.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/sparse-texture-feedback.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.legacy.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/frag/variables.zero-initialize.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/vert/io-blocks.force-flattened-io.vert create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/vert/pass-array-by-value.vert create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/spec-constant.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/spec-constant.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/ubo-offset-out-of-order.vk.nocompat.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/comp/pointer-to-array-of-physical-pointer.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/op-source-glsl-ssbo-1.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/op-source-glsl-ssbo-2.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/op-source-hlsl-uav-1.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/op-source-hlsl-uav-2.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-ssbo-1.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-ssbo-2.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-uav-1.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-uav-2.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/comp/array-of-physical-pointer.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/comp/function-pointer.invalid.asm.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/comp/physical-pointer.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/comp/struct-layout.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/comp/struct-packing.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/comp/workgroup-size-spec-constant.comp.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/frag/input-attachment.vk.frag.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/frag/push-constant.vk.frag.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/frag/spec-constant.vk.frag.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/rgen/acceleration_structure.vk.rgen.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/vert/array-size-reflection.vert.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/vert/read-from-row-major-array.vert.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/vert/stride-reflection.vert.json create mode 100644 third_party/spirv-cross/reference/shaders-reflection/vert/texture_buffer.vert.json create mode 100644 third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/accesschain-invalid-expression.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/array-copy-error.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/phi-variable-declaration.asm.invalid.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/vert/loop-accesschain-writethrough.asm.invalid.vert create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/depth-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/global-constant-arrays.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/vert/array-missing-copies.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders-ue4/asm/vert/texture-buffer.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/amd/gcn_shader.comp create mode 100644 third_party/spirv-cross/reference/shaders/amd/shader_ballot.comp create mode 100644 third_party/spirv-cross/reference/shaders/amd/shader_group_vote.comp create mode 100644 third_party/spirv-cross/reference/shaders/amd/shader_trinary_minmax.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/bitcast_iadd.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/bitcast_iequal.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/bitcast_sar.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/bitcast_sdiv.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/bitcast_slr.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/builtin-compute-bitcast.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/decoration-group.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/hlsl-functionality.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/logical.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/multiple-entry.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/nmin-max-clamp.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/op-phi-swap.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/quantize.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/recompile-block-naming.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/specialization-constant-workgroup.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/comp/switch-break-ladder.asm.comp create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/complex-name-workarounds.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/default-member-names.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/do-while-statement-fallback.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/for-loop-phi-only-continue.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.vk.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.vk.asm.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/inf-nan-constant-double.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/invalidation.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/locations-components.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/loop-header-to-continue.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/multi-for-loop-init.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/op-constant-null.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/op-phi-swap-continue-block.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/pack-and-unpack-uint2.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/phi-loop-variable.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/temporary-name-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/temporary-phi-hoisting.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/undef-variable-store.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/frag/vector-shuffle-oom.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/asm/geom/block-name-namespace.asm.geom create mode 100644 third_party/spirv-cross/reference/shaders/asm/geom/inout-split-access-chain-handle.asm.geom create mode 100644 third_party/spirv-cross/reference/shaders/asm/geom/split-access-chain-input.asm.geom create mode 100644 third_party/spirv-cross/reference/shaders/asm/geom/unroll-glposition-load.asm.geom create mode 100644 third_party/spirv-cross/reference/shaders/asm/tese/unroll-input-array-load.asm.tese create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/empty-io.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/global-builtin.sso.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/invariant-block.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/invariant-block.sso.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/invariant.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/invariant.sso.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/reference/shaders/comp/atomic.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/bake_gradient.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/barriers.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/basic.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/casts.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/cfg-preserve-parameter.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/cfg.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/coherent-block.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/coherent-image.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/composite-construct.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/culling.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/defer-parens.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/dowhile.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/generate_height.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/image.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/insert.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/mat3.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/mod.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/modf.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/outer-product.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/read-write-only.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/rmw-opt.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/shared.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/ssbo-array-length.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/ssbo-array.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/struct-layout.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/struct-packing.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/torture-loop.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/type-alias.comp create mode 100644 third_party/spirv-cross/reference/shaders/comp/udiv.comp create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/comp/enhanced-layouts.comp create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/comp/extended-arithmetic.desktop.comp create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/comp/fp64.desktop.comp create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/comp/int64.desktop.comp create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/clip-cull-distance.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/depth-greater-than.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/depth-less-than.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/dual-source-blending.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/image-ms.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/image-query.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/image-size.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/in-block-qualifiers.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/layout-component.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/query-levels.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/query-lod.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/sampler-ms-query.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/stencil-export.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/geom/basic.desktop.sso.geom create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/geom/viewport-index.desktop.geom create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/tesc/basic.desktop.sso.tesc create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/tese/triangle.desktop.sso.tese create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/vert/basic.desktop.sso.vert create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/vert/clip-cull-distance.desktop.vert create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/vert/out-block-qualifiers.vert create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/shaders/flatten/array.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/basic.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/copy.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/dynamic.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/matrix-conversion.flatten.frag create mode 100644 third_party/spirv-cross/reference/shaders/flatten/matrixindex.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/multiindex.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/push-constant.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/struct.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/struct.rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/swizzle.flatten.vert create mode 100644 third_party/spirv-cross/reference/shaders/flatten/types.flatten.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/avoid-expression-lowering-to-loop.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/barycentric-nv.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/basic.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/composite-extract-forced-temporary.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/constant-array.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/false-loop-init.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/flush_params.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/for-loop-init.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/frexp-modf.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/front-facing.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/gather-dref.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/ground.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/helper-invocation.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/image-load-store-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/inside-loop-dominated-variable-preservation.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/loop-dominator-and-switch-default.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/mix.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/partial-write-preserve.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/pixel-interlock-ordered.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/pixel-interlock-unordered.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/pls.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/post-depth-coverage-es.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/post-depth-coverage.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/round-even.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/round.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/sample-interlock-ordered.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/sample-interlock-unordered.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/sample-parameter.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/sampler-ms.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/sampler-proj.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/sampler.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/selection-block-dominator.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/struct-type-unrelated-alias.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/swizzle.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/ubo-load-row-major-workaround.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/ubo_layout.frag create mode 100644 third_party/spirv-cross/reference/shaders/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/reference/shaders/geom/basic.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/geometry-passthrough.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/lines-adjacency.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/lines.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/multi-stream.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/points.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/single-invocation.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/transform-feedback-streams.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/triangles-adjacency.geom create mode 100644 third_party/spirv-cross/reference/shaders/geom/triangles.geom create mode 100644 third_party/spirv-cross/reference/shaders/legacy/fragment/explicit-lod.legacy.frag create mode 100644 third_party/spirv-cross/reference/shaders/legacy/fragment/explicit-lod.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders/legacy/fragment/fma.legacy.frag create mode 100644 third_party/spirv-cross/reference/shaders/legacy/fragment/io-blocks.legacy.frag create mode 100644 third_party/spirv-cross/reference/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag create mode 100644 third_party/spirv-cross/reference/shaders/legacy/fragment/round.legacy.frag create mode 100644 third_party/spirv-cross/reference/shaders/legacy/fragment/struct-varying.legacy.frag create mode 100644 third_party/spirv-cross/reference/shaders/legacy/fragment/switch.legacy.frag create mode 100644 third_party/spirv-cross/reference/shaders/legacy/vert/implicit-lod.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders/legacy/vert/io-block.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders/legacy/vert/struct-varying.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders/legacy/vert/switch-nested.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders/legacy/vert/transpose.legacy.vert create mode 100644 third_party/spirv-cross/reference/shaders/tesc/basic.tesc create mode 100644 third_party/spirv-cross/reference/shaders/tesc/water_tess.tesc create mode 100644 third_party/spirv-cross/reference/shaders/tese/ccw.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/cw.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/equal.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/fractional_even.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/fractional_odd.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/input-array.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/line.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/load-array-of-array.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/patch-input-array.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/triangle.tese create mode 100644 third_party/spirv-cross/reference/shaders/tese/water_tess.tese create mode 100644 third_party/spirv-cross/reference/shaders/vert/basic.vert create mode 100644 third_party/spirv-cross/reference/shaders/vert/ground.vert create mode 100644 third_party/spirv-cross/reference/shaders/vert/invariant.vert create mode 100644 third_party/spirv-cross/reference/shaders/vert/ocean.vert create mode 100644 third_party/spirv-cross/reference/shaders/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/reference/shaders/vert/return-array.vert create mode 100644 third_party/spirv-cross/reference/shaders/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/reference/shaders/vert/transform-feedback-decorations.vert create mode 100644 third_party/spirv-cross/reference/shaders/vert/ubo.vert create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/desktop-mediump.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/desktop-mediump.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-block-size.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-block-size.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/payloads.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rgen/payloads.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/vert/device-group.nocompat.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/vert/multiview.nocompat.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/vert/small-storage.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/vert/small-storage.vk.vert.vk create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/vert/vulkan-vertex.vk.vert create mode 100644 third_party/spirv-cross/reference/shaders/vulkan/vert/vulkan-vertex.vk.vert.vk create mode 100644 third_party/spirv-cross/samples/cpp/Makefile create mode 100644 third_party/spirv-cross/samples/cpp/atomics.comp create mode 100644 third_party/spirv-cross/samples/cpp/atomics.cpp create mode 100644 third_party/spirv-cross/samples/cpp/multiply.comp create mode 100644 third_party/spirv-cross/samples/cpp/multiply.cpp create mode 100644 third_party/spirv-cross/samples/cpp/shared.comp create mode 100644 third_party/spirv-cross/samples/cpp/shared.cpp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/access-chain-load-store-composite.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/atomic-load-store.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/atomic-result-temporary.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/bitfield-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/bitscan.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/buffer-atomic-nonuniform.asm.sm51.nonuniformresource.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/constant-composite-undef.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/glsl-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.fxconly.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/image-atomic-nonuniform.asm.sm51.nonuniformresource.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/specialization-constant-workgroup.nofxc.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/storage-buffer-basic.invalid.nofxc.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/empty-struct-in-struct.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/nonuniform-bracket-handling-2.nonuniformresource.sm51.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/nonuniform-qualifier-propagation.nonuniformresource.sm51.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/phi.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-callstack.sm51.fxconly.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-control-flow.sm51.fxconly.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-split-functions.sm51.fxconly.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/struct-packing-last-element-array-matrix-rule.invalid.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/subgroup-arithmetic-cast.invalid.nofxc.sm60.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/unordered-compare.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/temporary.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/asm/vert/empty-struct-composite.asm.vert create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/comp/bitfield.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/comp/glsl.std450.fxconly.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/comp/illegal-struct-name.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/comp/subgroups.invalid.nofxc.sm60.comp create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/cbuffer-packing-straddle.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/constant-buffer-array.invalid.sm51.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/fp16.invalid.desktop.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/frag-coord.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/native-16bit-types.fxconly.nofxc.sm62.native-16bit.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/pixel-interlock-simple-callstack.sm51.fxconly.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/spec-constant.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/ubo-offset-out-of-order.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/frag/variables.zero-initialize.frag create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/vert/flatten-matrix-input.flatten-matrix-vertex-input.vert create mode 100644 third_party/spirv-cross/shaders-hlsl-no-opt/vert/pass-array-by-value.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/comp/control-flow-hints.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/access-chain-load-composite.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/access-chains.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/access-chains.force-uav.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/address-buffers.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/atomic.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/barriers.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/builtins.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/globallycoherent.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/image.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/image.nonwritable-uav-texture.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/inverse.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/num-workgroups-alone.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/num-workgroups-with-builtins.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/outer-product.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/rwbuffer-matrix.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/shared.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/spec-constant-op-member-array.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/spec-constant-work-group-size.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/ssbo-array-length.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/comp/ssbo-array.comp create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/basic-color-3comp.sm30.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/basic-color-3comp.sm50.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/basic.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/bit-conversions.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/boolean-mix.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/builtins.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/bvec-operations.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/clip-cull-distance.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/combined-texture-sampler-parameter.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/combined-texture-sampler-shadow.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/demote-to-helper.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/depth-greater-than.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/depth-less-than.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/dual-source-blending.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/early-fragment-test.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/fp16-packing.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/front-facing.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/image-query-selective.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/image-query-uav.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/image-query.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/input-attachment-ms.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/input-attachment.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/io-block.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/matrix-input.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/mod.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/mrt.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/no-return.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/no-return2.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/partial-write-preserve.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/point-coord-compat.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/query-lod.desktop.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/readonly-coherent-ssbo.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/resources.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/row-major-layout-in-struct.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/sample-cmp-level-zero.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/sample-mask-in-and-out.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/sample-mask-in.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/sample-mask-out.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/sampler-array.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/sampler-image-arrays.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/spec-constant-block-size.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/spec-constant-ternary.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/swizzle-scalar.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/tex-sampling-ms.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/tex-sampling.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/texture-proj-shadow.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/texture-size-combined-image-sampler.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/unorm-snorm-packing.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/frag/various-glsl-ops.frag create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/basic.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/clip-cull-distance.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/instancing.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/locations.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/matrix-attribute.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/matrix-output.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/no-input.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/point-size-compat.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/qualifiers.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/return-array.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/sampler-buffers.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/struct-composite-decl.vert create mode 100644 third_party/spirv-cross/shaders-hlsl/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/atomic-load-store.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/atomic-result-temporary.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitcast-fp16-fp32.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitfield-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitscan.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/composite-construct-buffer-struct.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/constant-composite-undef.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/copy-logical-2.spv14.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/copy-logical.spv14.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-array-load-temporary.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-array-load-temporary.force-native-array.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.force-native-array.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/glsl-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/storage-buffer-pointer-argument.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/component-insert-packed-expression.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/empty-struct-in-struct.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-gather.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-type-normal-comparison-usage.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/input-attachment-unused-frag-coord.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/phi.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-callstack.msl2.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-control-flow.msl2.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-split-functions.msl2.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/private-initializer-direct-store.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/subgroup-arithmetic-cast.msl21.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/texture-access.swizzle.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/composite-extract-row-major.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-2.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-3.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-4.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-5.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/packed-vector-extract-insert.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/row-major-split-access-chain.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float2.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float3-one-element.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float3.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x2-col-major.invalid.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x2-row-major.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x3-col-major.invalid.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x3-row-major.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x2-col-major.invalid.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x2-row-major.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x3-col-major.invalid.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x3-row-major.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/temporary.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.multi-patch.asm.tesc create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/vert/composite-extract-physical-type-id.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/vert/empty-struct-composite.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/array-copy-threadgroup-memory.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/basic.dynamic-buffer.msl2.invalid.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/bitcast-16bit-1.invalid.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/bitcast-16bit-2.invalid.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/bitfield.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/glsl.std450.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/illegal-struct-name.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/int64.invalid.msl22.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/loop.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/return.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/std140-array-load-composite-construct.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.ios.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/components/fragment-input-component.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/components/fragment-output-component.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/components/fragment-output-component.pad-fragment.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/components/vertex-input-component.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/components/vertex-output-component.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/16bit-constants.invalid.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/demote-to-helper.vk.nocompat.msl21.invalid.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/depth-image-gather.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/force-active-resources.msl2.argument..force-active.discrete.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/fp16.desktop.invalid.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/image-gather.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/in_block_assign.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/min-max-clamp.invalid.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/pixel-interlock-simple-callstack.msl2.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/shadow-compare-global-alias.invalid.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/subgroups.nocompat.invalid.vk.msl21.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl20.ios.framebuffer-fetch.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl23.framebuffer-fetch.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.ios.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.msl23.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-int.swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-leaf.swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-uint.swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access.swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.argument.msl2.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-offset-out-of-order.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/frag/variables.zero-initialize.frag create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/array-of-vec3.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/array-of-vec4.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/isolated-scalar-access.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/load-store-col-rows.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-std140.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-std430.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-row-major.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major-2.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major-2.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/member-padding.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/std140-array-of-vectors.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/struct-alignment.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing-array-of-scalar.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing-recursive.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/struct-size-padding-array-of-array.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/packing/struct-size-padding.comp create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/vert/functions_nested.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/vert/layer.msl11.invalid.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/vert/pass-array-by-value.force-native-array.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/vert/pass-array-by-value.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/vert/viewport-index.msl2.invalid.vert create mode 100644 third_party/spirv-cross/shaders-msl-no-opt/vulkan/frag/texture-access-function.swizzle.vk.frag create mode 100644 third_party/spirv-cross/shaders-msl/amd/shader_trinary_minmax.msl21.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/bitcast_iadd.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/bitcast_sar.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/bitcast_sdiv.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/bitcast_slr.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/buffer-write.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/multiple-entry.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/quantize.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/relaxed-block-layout.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/variable-pointers-2.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/default-member-names.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/locations-components.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/min-lod.msl22.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/op-constant-null.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/op-image-sampled-image.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/phi-loop-variable.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/undef-variable-store.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/unknown-depth-state.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/unord-relational-op.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag create mode 100644 third_party/spirv-cross/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc create mode 100644 third_party/spirv-cross/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc create mode 100644 third_party/spirv-cross/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/copy-memory-interface.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/fake-builtin-input.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/invariant.msl21.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/packing-test.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/shaders-msl/comp/access-private-workgroup-in-function.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/array-length.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/array-length.msl2.argument.discrete.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/atomic.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/barriers.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/basic.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/basic.dispatchbase.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/basic.dispatchbase.msl11.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/basic.inline-block.msl2.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/builtins.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/cfg-preserve-parameter.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/coherent-block.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/coherent-image.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/complex-composite-constant-array.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/complex-type-alias.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/composite-array-initialization.force-native-array.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/composite-construct.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/copy-array-of-arrays.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/culling.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/defer-parens.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/dowhile.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/force-recompile-hooks.swizzle.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/functions.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/global-invocation-id.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/image-atomic-automatic-bindings.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/image-cube-array-load-store.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/image.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/insert.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/inverse.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/local-invocation-id.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/local-invocation-index.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/mat3.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/mod.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/modf.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/outer-product.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/packing-test-1.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/packing-test-2.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/read-write-only.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/rmw-opt.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/shared-array-of-arrays.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/shared.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/spec-constant-op-member-array.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/spec-constant-work-group-size.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/storage-buffer-std140-vector-array.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/struct-layout.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/struct-nested.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/struct-packing.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/torture-loop.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/type-alias.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/udiv.comp create mode 100644 third_party/spirv-cross/shaders-msl/comp/writable-ssbo.comp create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/frag/image-ms.desktop.frag create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/frag/query-levels.desktop.frag create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/vert/basic.desktop.sso.vert create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert create mode 100644 third_party/spirv-cross/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert create mode 100644 third_party/spirv-cross/shaders-msl/flatten/basic.flatten.vert create mode 100644 third_party/spirv-cross/shaders-msl/flatten/multiindex.flatten.vert create mode 100644 third_party/spirv-cross/shaders-msl/flatten/push-constant.flatten.vert create mode 100644 third_party/spirv-cross/shaders-msl/flatten/rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/shaders-msl/flatten/struct.flatten.vert create mode 100644 third_party/spirv-cross/shaders-msl/flatten/swizzle.flatten.vert create mode 100644 third_party/spirv-cross/shaders-msl/flatten/types.flatten.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/argument-buffers.msl2.argument.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/array-of-array-lut.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/barycentric-nv.msl22.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/basic.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/binary-func-unpack-pack-arguments.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/binary-unpack-pack-arguments.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/bitcasting.1d-as-2d.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/bitcasting.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/buffer-read-write.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/builtins.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/clip-distance-varying.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/composite-extract-forced-temporary.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/constant-array.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/depth-greater-than.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/depth-less-than.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/disable-frag-output.frag-output.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/dual-source-blending.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/early-fragment-tests.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/false-loop-init.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/flush_params.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/for-loop-init.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/fp16-packing.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/fragment-component-padding.pad-fragment.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/front-facing.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/gather-dref.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/gather-offset.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/helper-invocation.msl21.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/illegal-name-test-0.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/image-query-lod.msl22.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/in_block.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/in_mat.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/input-attachment.arrayed-subpass.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/input-attachment.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/input-attachment.multiview.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/interpolation-qualifiers-block.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/interpolation-qualifiers.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/mix.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/mrt-array.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/nonuniform-qualifier.msl2.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/packed-expression-vector-shuffle.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/packing-test-3.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/pixel-interlock-ordered.msl2.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/pls.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/post-depth-coverage.ios.msl2.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/post-depth-coverage.msl23.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/private-variable-prototype-declaration.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/readonly-ssbo.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sample-depth-separate-image-sampler.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sample-mask.fixed-sample-mask.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sample-mask.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sample-position-func.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sample-position.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler-1d-lod.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler-image-arrays.msl2.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler-ms.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/sampler.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/separate-image-sampler-argument.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/shader-arithmetic-8bit.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/spec-constant-block-size.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/spec-constant-ternary.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/stencil-export.msl21.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/subgroup-builtins.msl22.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/swizzle.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/texture-cube-array.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/texture-multisample-array.msl21.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/texture-proj-shadow.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/ubo_layout.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag create mode 100644 third_party/spirv-cross/shaders-msl/frag/write-depth-in-function.frag create mode 100644 third_party/spirv-cross/shaders-msl/intel/shader-integer-functions2.asm.comp create mode 100644 third_party/spirv-cross/shaders-msl/legacy/vert/transpose.legacy.vert create mode 100644 third_party/spirv-cross/shaders-msl/tesc/basic.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/basic.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-matrix.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-struct.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/load-control-point-array.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/load-control-point-array.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/matrix-output.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/reload-tess-level.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/reload-tess-level.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/struct-output.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/water_tess.multi-patch.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tesc/water_tess.tesc create mode 100644 third_party/spirv-cross/shaders-msl/tese/input-array.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/input-types.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/load-control-point-array-of-matrix.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/load-control-point-array.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/quad.domain.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/quad.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/set-from-function.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/triangle-tess-level.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/triangle.tese create mode 100644 third_party/spirv-cross/shaders-msl/tese/water_tess.tese create mode 100644 third_party/spirv-cross/shaders-msl/vert/basic.capture.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/basic.for-tess.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/basic.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/clip-distance-block.no-user-varying.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/clip-distance-block.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/copy.flatten.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/dynamic.flatten.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/float-math.invariant-float-math.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/float-math.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/functions.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/in_out_array_mat.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/interface-block-block-composites.frag create mode 100644 third_party/spirv-cross/shaders-msl/vert/interface-block-block-composites.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/interpolation-qualifiers-block.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/interpolation-qualifiers.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/invariant.msl21.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/leaf-function.capture.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/leaf-function.for-tess.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/no-disable-vertex-out.frag-output.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/no_stage_out.for-tess.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/no_stage_out.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_buff.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_buff_atomic.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_tex.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/out_block.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/packed-bool-to-uint.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/packed-bool2-to-packed_uint2.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/packed_matrix.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/pointsize.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/resource-arrays-leaf.ios.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/resource-arrays.ios.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/return-array.force-native-array.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/return-array.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/set_builtin_in_func.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/sign-int-types.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/signedness-mismatch.shader-inputs.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/ubo.alignment.vert create mode 100644 third_party/spirv-cross/shaders-msl/vert/ubo.vert create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/frag/push-constant.vk.frag create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/frag/spec-constant.vk.frag create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/vert/small-storage.vk.vert create mode 100644 third_party/spirv-cross/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/access-tracking-function-call-result.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/atomic-load-store.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/atomic-result-temporary.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/bitfield-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/bitscan.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-atomic-nonuniform.vk.nocompat.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer-2.asm.nocompat.vk.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer.asm.nocompat.vk.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/constant-composite-undef.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/copy-logical.spv14.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/extended-debug-extinst.invalid.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/glsl-signed-operations.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/image-atomic-nonuniform.vk.nocompat.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/loop-variable-with-initializer.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/nonuniform-bracket-handling.vk.nocompat.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/phi-temporary-copy-loop-variable.asm.invalid.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/combined-image-sampler-dxc-min16float.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/demote-impure-function-call.vk.nocompat.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/discard-impure-function-call.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/do-while-continue-phi.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/do-while-loop-inverted-test.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/early-conditional-return-switch.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/empty-struct-in-struct.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-inverted.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-non-inverted.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-inverted-test.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/image-fetch-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/inliner-dominator-inside-loop.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/ldexp-uint-exponent.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/loop-merge-to-continue.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/nonuniform-bracket-handling-2.vk.nocompat.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/nonuniform-qualifier-propagation.vk.nocompat.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/opaque-id-literal-alias.preserve.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/out-of-order-struct-id.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/phi.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-callstack.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-control-flow.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-split-functions.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/reserved-identifiers.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/selection-merge-to-continue.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/sparse-texture-feedback-uint-code.asm.desktop.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/subgroup-arithmetic-cast.nocompat.vk.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/switch-merge-to-continue.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/switch-single-case-multiple-exit-cfg.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/unordered-compare.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/vector-extract-dynamic-spec-constant.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/frag/while-loop-inverted-test.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/geom/store-uint-layer.invalid.asm.geom create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/loop-header-self-continue-break.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/temporary.zero-initialize.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/vert/complex-link-by-name.asm.vert create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/vert/complex-link-by-name.force-flattened-io.legacy.asm.vert create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/vert/empty-struct-composite.asm.vert create mode 100644 third_party/spirv-cross/shaders-no-opt/asm/vert/semantic-decoration.asm.vert create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/bitcast-16bit-1.invalid.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/bitcast-16bit-2.invalid.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/bitfield.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/glsl.std450.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/illegal-struct-name.asm.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/inout-struct.invalid.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/loop.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/return.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/shader_ballot_nonuniform_invocations.invalid.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/specialization-constant-evaluation.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/subgroups.nocompat.invalid.vk.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/16bit-constants.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/fp16.invalid.desktop.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/fragmentMaskFetch_subpassInput.vk.nocompat.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/fs.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/image-gather.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/multi-dimensional.desktop.invalid.flatten_dim.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/pixel-interlock-simple-callstack.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/sparse-texture-clamp.desktop.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/sparse-texture-feedback.desktop.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.legacy.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/frag/variables.zero-initialize.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/vert/io-blocks.force-flattened-io.vert create mode 100644 third_party/spirv-cross/shaders-no-opt/vert/pass-array-by-value.vert create mode 100644 third_party/spirv-cross/shaders-no-opt/vulkan/frag/spec-constant.vk.frag create mode 100644 third_party/spirv-cross/shaders-no-opt/vulkan/frag/ubo-offset-out-of-order.vk.nocompat.frag create mode 100644 third_party/spirv-cross/shaders-other/README.md create mode 100644 third_party/spirv-cross/shaders-other/aliased-entry-point-names.asm create mode 100644 third_party/spirv-cross/shaders-reflection/asm/aliased-entry-point-names.asm.multi create mode 100644 third_party/spirv-cross/shaders-reflection/asm/comp/pointer-to-array-of-physical-pointer.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/asm/op-source-glsl-ssbo-1.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/asm/op-source-glsl-ssbo-2.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/asm/op-source-hlsl-uav-1.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/asm/op-source-hlsl-uav-2.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/asm/op-source-none-ssbo-1.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/asm/op-source-none-ssbo-2.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/asm/op-source-none-uav-1.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/asm/op-source-none-uav-2.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/comp/array-of-physical-pointer.comp create mode 100644 third_party/spirv-cross/shaders-reflection/comp/function-pointer.invalid.asm.comp create mode 100644 third_party/spirv-cross/shaders-reflection/comp/physical-pointer.comp create mode 100644 third_party/spirv-cross/shaders-reflection/comp/struct-layout.comp create mode 100644 third_party/spirv-cross/shaders-reflection/comp/struct-packing.comp create mode 100644 third_party/spirv-cross/shaders-reflection/comp/workgroup-size-spec-constant.comp create mode 100644 third_party/spirv-cross/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag create mode 100644 third_party/spirv-cross/shaders-reflection/frag/combined-texture-sampler.vk.frag create mode 100644 third_party/spirv-cross/shaders-reflection/frag/image-load-store-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/shaders-reflection/frag/input-attachment-ms.vk.frag create mode 100644 third_party/spirv-cross/shaders-reflection/frag/input-attachment.vk.frag create mode 100644 third_party/spirv-cross/shaders-reflection/frag/push-constant.vk.frag create mode 100644 third_party/spirv-cross/shaders-reflection/frag/separate-sampler-texture-array.vk.frag create mode 100644 third_party/spirv-cross/shaders-reflection/frag/spec-constant.vk.frag create mode 100644 third_party/spirv-cross/shaders-reflection/rgen/acceleration_structure.vk.rgen create mode 100644 third_party/spirv-cross/shaders-reflection/vert/array-size-reflection.vert create mode 100644 third_party/spirv-cross/shaders-reflection/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/shaders-reflection/vert/stride-reflection.vert create mode 100644 third_party/spirv-cross/shaders-reflection/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/accesschain-invalid-expression.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/array-copy-error.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/phi-variable-declaration.asm.invalid.frag create mode 100644 third_party/spirv-cross/shaders-ue4-no-opt/asm/vert/loop-accesschain-writethrough.asm.invalid.vert create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/depth-compare.asm.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/global-constant-arrays.asm.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag create mode 100644 third_party/spirv-cross/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc create mode 100644 third_party/spirv-cross/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc create mode 100644 third_party/spirv-cross/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc create mode 100644 third_party/spirv-cross/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc create mode 100644 third_party/spirv-cross/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese create mode 100644 third_party/spirv-cross/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese create mode 100644 third_party/spirv-cross/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese create mode 100644 third_party/spirv-cross/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese create mode 100644 third_party/spirv-cross/shaders-ue4/asm/vert/array-missing-copies.asm.vert create mode 100644 third_party/spirv-cross/shaders-ue4/asm/vert/texture-buffer.asm.vert create mode 100644 third_party/spirv-cross/shaders/amd/gcn_shader.comp create mode 100644 third_party/spirv-cross/shaders/amd/shader_ballot.comp create mode 100644 third_party/spirv-cross/shaders/amd/shader_group_vote.comp create mode 100644 third_party/spirv-cross/shaders/amd/shader_trinary_minmax.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/atomic-decrement.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/atomic-increment.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/bitcast_iadd.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/bitcast_icmp.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/bitcast_iequal.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/bitcast_sar.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/bitcast_sdiv.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/bitcast_slr.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/block-name-alias-global.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/builtin-compute-bitcast.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/decoration-group.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/global-parameter-name-alias.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/hlsl-functionality.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/logical.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/multiple-entry.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/nmin-max-clamp.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/op-phi-swap.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/quantize.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/recompile-block-naming.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/specialization-constant-workgroup.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/comp/switch-break-ladder.asm.comp create mode 100644 third_party/spirv-cross/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/complex-name-workarounds.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/default-member-names.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/do-while-statement-fallback.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/empty-struct.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/for-loop-phi-only-continue.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/frem.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/function-overload-alias.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/image-extract-reuse.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/image-query-no-sampler.vk.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/implicit-read-dep-phi.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/inf-nan-constant-double.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/inf-nan-constant.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/invalidation.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/line-directive.line.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/locations-components.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/loop-header-to-continue.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/lut-promotion-initializer.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/multi-for-loop-init.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/op-constant-null.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/op-phi-swap-continue-block.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/pack-and-unpack-uint2.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/pass-by-value.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/phi-loop-variable.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/sample-and-compare.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/single-function-private-lut.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/srem.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/storage-class-output-initializer.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/switch-label-shared-block.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/temporary-name-alias.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/temporary-phi-hoisting.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/texel-fetch-no-lod.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/undef-variable-store.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/unknown-depth-state.asm.vk.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/unreachable.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/frag/vector-shuffle-oom.asm.frag create mode 100644 third_party/spirv-cross/shaders/asm/geom/block-name-namespace.asm.geom create mode 100644 third_party/spirv-cross/shaders/asm/geom/inout-split-access-chain-handle.asm.geom create mode 100644 third_party/spirv-cross/shaders/asm/geom/split-access-chain-input.asm.geom create mode 100644 third_party/spirv-cross/shaders/asm/geom/unroll-glposition-load.asm.geom create mode 100644 third_party/spirv-cross/shaders/asm/tese/unroll-input-array-load.asm.tese create mode 100644 third_party/spirv-cross/shaders/asm/vert/empty-io.asm.vert create mode 100644 third_party/spirv-cross/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert create mode 100644 third_party/spirv-cross/shaders/asm/vert/global-builtin.sso.asm.vert create mode 100644 third_party/spirv-cross/shaders/asm/vert/invariant-block.asm.vert create mode 100644 third_party/spirv-cross/shaders/asm/vert/invariant-block.sso.asm.vert create mode 100644 third_party/spirv-cross/shaders/asm/vert/invariant.asm.vert create mode 100644 third_party/spirv-cross/shaders/asm/vert/invariant.sso.asm.vert create mode 100644 third_party/spirv-cross/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert create mode 100644 third_party/spirv-cross/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert create mode 100644 third_party/spirv-cross/shaders/comp/atomic.comp create mode 100644 third_party/spirv-cross/shaders/comp/bake_gradient.comp create mode 100644 third_party/spirv-cross/shaders/comp/barriers.comp create mode 100644 third_party/spirv-cross/shaders/comp/basic.comp create mode 100644 third_party/spirv-cross/shaders/comp/casts.comp create mode 100644 third_party/spirv-cross/shaders/comp/cfg-preserve-parameter.comp create mode 100644 third_party/spirv-cross/shaders/comp/cfg.comp create mode 100644 third_party/spirv-cross/shaders/comp/coherent-block.comp create mode 100644 third_party/spirv-cross/shaders/comp/coherent-image.comp create mode 100644 third_party/spirv-cross/shaders/comp/composite-array-initialization.comp create mode 100644 third_party/spirv-cross/shaders/comp/composite-construct.comp create mode 100644 third_party/spirv-cross/shaders/comp/culling.comp create mode 100644 third_party/spirv-cross/shaders/comp/defer-parens.comp create mode 100644 third_party/spirv-cross/shaders/comp/dowhile.comp create mode 100644 third_party/spirv-cross/shaders/comp/generate_height.comp create mode 100644 third_party/spirv-cross/shaders/comp/image.comp create mode 100644 third_party/spirv-cross/shaders/comp/insert.comp create mode 100644 third_party/spirv-cross/shaders/comp/mat3.comp create mode 100644 third_party/spirv-cross/shaders/comp/mod.comp create mode 100644 third_party/spirv-cross/shaders/comp/modf.comp create mode 100644 third_party/spirv-cross/shaders/comp/outer-product.comp create mode 100644 third_party/spirv-cross/shaders/comp/read-write-only.comp create mode 100644 third_party/spirv-cross/shaders/comp/rmw-matrix.comp create mode 100644 third_party/spirv-cross/shaders/comp/rmw-opt.comp create mode 100644 third_party/spirv-cross/shaders/comp/scalar-std450-distance-length-normalize.comp create mode 100644 third_party/spirv-cross/shaders/comp/shared.comp create mode 100644 third_party/spirv-cross/shaders/comp/ssbo-array-length.comp create mode 100644 third_party/spirv-cross/shaders/comp/ssbo-array.comp create mode 100644 third_party/spirv-cross/shaders/comp/struct-layout.comp create mode 100644 third_party/spirv-cross/shaders/comp/struct-packing.comp create mode 100644 third_party/spirv-cross/shaders/comp/torture-loop.comp create mode 100644 third_party/spirv-cross/shaders/comp/type-alias.comp create mode 100644 third_party/spirv-cross/shaders/comp/udiv.comp create mode 100644 third_party/spirv-cross/shaders/desktop-only/comp/enhanced-layouts.comp create mode 100644 third_party/spirv-cross/shaders/desktop-only/comp/extended-arithmetic.desktop.comp create mode 100644 third_party/spirv-cross/shaders/desktop-only/comp/fp64.desktop.comp create mode 100644 third_party/spirv-cross/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp create mode 100644 third_party/spirv-cross/shaders/desktop-only/comp/int64.desktop.comp create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/clip-cull-distance.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/depth-greater-than.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/depth-less-than.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/dual-source-blending.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/image-ms.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/image-query.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/image-size.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/in-block-qualifiers.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/layout-component.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/query-levels.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/query-lod.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/sampler-ms-query.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/stencil-export.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag create mode 100644 third_party/spirv-cross/shaders/desktop-only/geom/basic.desktop.sso.geom create mode 100644 third_party/spirv-cross/shaders/desktop-only/geom/viewport-index.desktop.geom create mode 100644 third_party/spirv-cross/shaders/desktop-only/tesc/basic.desktop.sso.tesc create mode 100644 third_party/spirv-cross/shaders/desktop-only/tese/triangle.desktop.sso.tese create mode 100644 third_party/spirv-cross/shaders/desktop-only/vert/basic.desktop.sso.vert create mode 100644 third_party/spirv-cross/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert create mode 100644 third_party/spirv-cross/shaders/desktop-only/vert/clip-cull-distance.desktop.vert create mode 100644 third_party/spirv-cross/shaders/desktop-only/vert/out-block-qualifiers.vert create mode 100644 third_party/spirv-cross/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert create mode 100644 third_party/spirv-cross/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert create mode 100644 third_party/spirv-cross/shaders/flatten/array.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/basic.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/copy.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/dynamic.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/matrix-conversion.flatten.frag create mode 100644 third_party/spirv-cross/shaders/flatten/matrixindex.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/multiindex.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/push-constant.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/struct.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/struct.rowmajor.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/swizzle.flatten.vert create mode 100644 third_party/spirv-cross/shaders/flatten/types.flatten.frag create mode 100644 third_party/spirv-cross/shaders/frag/array-lut-no-loop-variable.frag create mode 100644 third_party/spirv-cross/shaders/frag/avoid-expression-lowering-to-loop.frag create mode 100644 third_party/spirv-cross/shaders/frag/barycentric-nv.frag create mode 100644 third_party/spirv-cross/shaders/frag/basic.frag create mode 100644 third_party/spirv-cross/shaders/frag/complex-expression-in-access-chain.frag create mode 100644 third_party/spirv-cross/shaders/frag/composite-extract-forced-temporary.frag create mode 100644 third_party/spirv-cross/shaders/frag/constant-array.frag create mode 100644 third_party/spirv-cross/shaders/frag/constant-composites.frag create mode 100644 third_party/spirv-cross/shaders/frag/false-loop-init.frag create mode 100644 third_party/spirv-cross/shaders/frag/flush_params.frag create mode 100644 third_party/spirv-cross/shaders/frag/for-loop-continue-control-flow.frag create mode 100644 third_party/spirv-cross/shaders/frag/for-loop-init.frag create mode 100644 third_party/spirv-cross/shaders/frag/frexp-modf.frag create mode 100644 third_party/spirv-cross/shaders/frag/front-facing.frag create mode 100644 third_party/spirv-cross/shaders/frag/gather-dref.frag create mode 100755 third_party/spirv-cross/shaders/frag/ground.frag create mode 100644 third_party/spirv-cross/shaders/frag/helper-invocation.frag create mode 100644 third_party/spirv-cross/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag create mode 100644 third_party/spirv-cross/shaders/frag/image-load-store-uint-coord.asm.frag create mode 100644 third_party/spirv-cross/shaders/frag/inside-loop-dominated-variable-preservation.frag create mode 100644 third_party/spirv-cross/shaders/frag/loop-dominator-and-switch-default.frag create mode 100644 third_party/spirv-cross/shaders/frag/lut-promotion.frag create mode 100644 third_party/spirv-cross/shaders/frag/mix.frag create mode 100644 third_party/spirv-cross/shaders/frag/partial-write-preserve.frag create mode 100644 third_party/spirv-cross/shaders/frag/pixel-interlock-ordered.frag create mode 100644 third_party/spirv-cross/shaders/frag/pixel-interlock-unordered.frag create mode 100644 third_party/spirv-cross/shaders/frag/pls.frag create mode 100644 third_party/spirv-cross/shaders/frag/post-depth-coverage-es.frag create mode 100644 third_party/spirv-cross/shaders/frag/post-depth-coverage.frag create mode 100644 third_party/spirv-cross/shaders/frag/round-even.frag create mode 100644 third_party/spirv-cross/shaders/frag/round.frag create mode 100644 third_party/spirv-cross/shaders/frag/sample-interlock-ordered.frag create mode 100644 third_party/spirv-cross/shaders/frag/sample-interlock-unordered.frag create mode 100644 third_party/spirv-cross/shaders/frag/sample-parameter.frag create mode 100644 third_party/spirv-cross/shaders/frag/sampler-ms.frag create mode 100644 third_party/spirv-cross/shaders/frag/sampler-proj.frag create mode 100644 third_party/spirv-cross/shaders/frag/sampler.frag create mode 100644 third_party/spirv-cross/shaders/frag/scalar-refract-reflect.frag create mode 100644 third_party/spirv-cross/shaders/frag/selection-block-dominator.frag create mode 100644 third_party/spirv-cross/shaders/frag/struct-type-unrelated-alias.frag create mode 100644 third_party/spirv-cross/shaders/frag/switch-unsigned-case.frag create mode 100644 third_party/spirv-cross/shaders/frag/swizzle.frag create mode 100644 third_party/spirv-cross/shaders/frag/texel-fetch-offset.frag create mode 100644 third_party/spirv-cross/shaders/frag/ubo-load-row-major-workaround.frag create mode 100644 third_party/spirv-cross/shaders/frag/ubo_layout.frag create mode 100644 third_party/spirv-cross/shaders/frag/unary-enclose.frag create mode 100644 third_party/spirv-cross/shaders/geom/basic.geom create mode 100644 third_party/spirv-cross/shaders/geom/geometry-passthrough.geom create mode 100644 third_party/spirv-cross/shaders/geom/lines-adjacency.geom create mode 100644 third_party/spirv-cross/shaders/geom/lines.geom create mode 100644 third_party/spirv-cross/shaders/geom/multi-stream.geom create mode 100644 third_party/spirv-cross/shaders/geom/points.geom create mode 100644 third_party/spirv-cross/shaders/geom/single-invocation.geom create mode 100644 third_party/spirv-cross/shaders/geom/transform-feedback-streams.geom create mode 100644 third_party/spirv-cross/shaders/geom/triangles-adjacency.geom create mode 100644 third_party/spirv-cross/shaders/geom/triangles.geom create mode 100644 third_party/spirv-cross/shaders/legacy/fragment/explicit-lod.legacy.frag create mode 100644 third_party/spirv-cross/shaders/legacy/fragment/explicit-lod.legacy.vert create mode 100644 third_party/spirv-cross/shaders/legacy/fragment/fma.legacy.frag create mode 100644 third_party/spirv-cross/shaders/legacy/fragment/io-blocks.legacy.frag create mode 100644 third_party/spirv-cross/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag create mode 100644 third_party/spirv-cross/shaders/legacy/fragment/round.legacy.frag create mode 100644 third_party/spirv-cross/shaders/legacy/fragment/struct-varying.legacy.frag create mode 100644 third_party/spirv-cross/shaders/legacy/fragment/switch.legacy.frag create mode 100644 third_party/spirv-cross/shaders/legacy/vert/implicit-lod.legacy.vert create mode 100644 third_party/spirv-cross/shaders/legacy/vert/io-block.legacy.vert create mode 100644 third_party/spirv-cross/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert create mode 100644 third_party/spirv-cross/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert create mode 100644 third_party/spirv-cross/shaders/legacy/vert/struct-varying.legacy.vert create mode 100644 third_party/spirv-cross/shaders/legacy/vert/switch-nested.legacy.vert create mode 100644 third_party/spirv-cross/shaders/legacy/vert/transpose.legacy.vert create mode 100644 third_party/spirv-cross/shaders/tesc/basic.tesc create mode 100644 third_party/spirv-cross/shaders/tesc/water_tess.tesc create mode 100644 third_party/spirv-cross/shaders/tese/ccw.tese create mode 100644 third_party/spirv-cross/shaders/tese/cw.tese create mode 100644 third_party/spirv-cross/shaders/tese/equal.tese create mode 100644 third_party/spirv-cross/shaders/tese/fractional_even.tese create mode 100644 third_party/spirv-cross/shaders/tese/fractional_odd.tese create mode 100644 third_party/spirv-cross/shaders/tese/input-array.tese create mode 100644 third_party/spirv-cross/shaders/tese/line.tese create mode 100644 third_party/spirv-cross/shaders/tese/load-array-of-array.tese create mode 100644 third_party/spirv-cross/shaders/tese/patch-input-array.tese create mode 100644 third_party/spirv-cross/shaders/tese/triangle.tese create mode 100644 third_party/spirv-cross/shaders/tese/water_tess.tese create mode 100644 third_party/spirv-cross/shaders/vert/basic.vert create mode 100755 third_party/spirv-cross/shaders/vert/ground.vert create mode 100644 third_party/spirv-cross/shaders/vert/invariant.vert create mode 100644 third_party/spirv-cross/shaders/vert/ocean.vert create mode 100644 third_party/spirv-cross/shaders/vert/read-from-row-major-array.vert create mode 100644 third_party/spirv-cross/shaders/vert/return-array.vert create mode 100644 third_party/spirv-cross/shaders/vert/texture_buffer.vert create mode 100644 third_party/spirv-cross/shaders/vert/transform-feedback-decorations.vert create mode 100644 third_party/spirv-cross/shaders/vert/ubo.vert create mode 100644 third_party/spirv-cross/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp create mode 100644 third_party/spirv-cross/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp create mode 100644 third_party/spirv-cross/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp create mode 100644 third_party/spirv-cross/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp create mode 100644 third_party/spirv-cross/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/combined-texture-sampler.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/desktop-mediump.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/input-attachment-ms.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/input-attachment.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/push-constant.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/separate-sampler-texture.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/spec-constant-block-size.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/frag/spec-constant-ternary.vk.frag create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/payloads.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit create mode 100644 third_party/spirv-cross/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen create mode 100644 third_party/spirv-cross/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen create mode 100644 third_party/spirv-cross/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen create mode 100644 third_party/spirv-cross/shaders/vulkan/rgen/payloads.nocompat.vk.rgen create mode 100644 third_party/spirv-cross/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen create mode 100644 third_party/spirv-cross/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen create mode 100644 third_party/spirv-cross/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen create mode 100644 third_party/spirv-cross/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss create mode 100644 third_party/spirv-cross/shaders/vulkan/vert/device-group.nocompat.vk.vert create mode 100644 third_party/spirv-cross/shaders/vulkan/vert/multiview.nocompat.vk.vert create mode 100644 third_party/spirv-cross/shaders/vulkan/vert/small-storage.vk.vert create mode 100644 third_party/spirv-cross/shaders/vulkan/vert/vulkan-vertex.vk.vert create mode 100644 third_party/spirv-cross/spirv.h create mode 100644 third_party/spirv-cross/spirv.hpp create mode 100644 third_party/spirv-cross/spirv_cfg.cpp create mode 100644 third_party/spirv-cross/spirv_cfg.hpp create mode 100644 third_party/spirv-cross/spirv_common.hpp create mode 100644 third_party/spirv-cross/spirv_cpp.cpp create mode 100644 third_party/spirv-cross/spirv_cpp.hpp create mode 100644 third_party/spirv-cross/spirv_cross.cpp create mode 100644 third_party/spirv-cross/spirv_cross.hpp create mode 100644 third_party/spirv-cross/spirv_cross_c.cpp create mode 100644 third_party/spirv-cross/spirv_cross_c.h create mode 100644 third_party/spirv-cross/spirv_cross_containers.hpp create mode 100644 third_party/spirv-cross/spirv_cross_error_handling.hpp create mode 100644 third_party/spirv-cross/spirv_cross_parsed_ir.cpp create mode 100644 third_party/spirv-cross/spirv_cross_parsed_ir.hpp create mode 100644 third_party/spirv-cross/spirv_cross_util.cpp create mode 100644 third_party/spirv-cross/spirv_cross_util.hpp create mode 100644 third_party/spirv-cross/spirv_glsl.cpp create mode 100644 third_party/spirv-cross/spirv_glsl.hpp create mode 100644 third_party/spirv-cross/spirv_hlsl.cpp create mode 100644 third_party/spirv-cross/spirv_hlsl.hpp create mode 100644 third_party/spirv-cross/spirv_msl.cpp create mode 100644 third_party/spirv-cross/spirv_msl.hpp create mode 100644 third_party/spirv-cross/spirv_parser.cpp create mode 100644 third_party/spirv-cross/spirv_parser.hpp create mode 100644 third_party/spirv-cross/spirv_reflect.cpp create mode 100644 third_party/spirv-cross/spirv_reflect.hpp create mode 100755 third_party/spirv-cross/test_shaders.py create mode 100755 third_party/spirv-cross/test_shaders.sh create mode 100644 third_party/spirv-cross/tests-other/c_api_test.c create mode 100644 third_party/spirv-cross/tests-other/c_api_test.spv create mode 100644 third_party/spirv-cross/tests-other/hlsl_resource_binding.spv create mode 100644 third_party/spirv-cross/tests-other/hlsl_resource_bindings.cpp create mode 100644 third_party/spirv-cross/tests-other/hlsl_wave_mask.cpp create mode 100644 third_party/spirv-cross/tests-other/msl_constexpr_test.cpp create mode 100644 third_party/spirv-cross/tests-other/msl_constexpr_test.spv create mode 100644 third_party/spirv-cross/tests-other/msl_resource_binding.spv create mode 100644 third_party/spirv-cross/tests-other/msl_resource_bindings.cpp create mode 100644 third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test.cpp create mode 100644 third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test.spv create mode 100644 third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test_2.spv create mode 100644 third_party/spirv-cross/tests-other/small_vector.cpp create mode 100644 third_party/spirv-cross/tests-other/typed_id_test.cpp create mode 100644 third_party/spirv-cross/tnt/CMakeLists.txt create mode 100644 third_party/spirv-cross/tnt/README.md create mode 100755 third_party/spirv-cross/update_test_shaders.sh create mode 100644 third_party/spirv-tools/.appveyor.yml create mode 100644 third_party/spirv-tools/.clang-format create mode 100644 third_party/spirv-tools/.gitignore create mode 100644 third_party/spirv-tools/.gn create mode 100644 third_party/spirv-tools/Android.mk create mode 100644 third_party/spirv-tools/BUILD.bazel create mode 100644 third_party/spirv-tools/BUILD.gn create mode 100644 third_party/spirv-tools/CHANGES create mode 100755 third_party/spirv-tools/CMakeLists.txt create mode 100644 third_party/spirv-tools/CODE_OF_CONDUCT.md create mode 100644 third_party/spirv-tools/CONTRIBUTING.md create mode 100644 third_party/spirv-tools/DEPS create mode 100644 third_party/spirv-tools/FILAMENT_README.md create mode 100644 third_party/spirv-tools/LICENSE create mode 100644 third_party/spirv-tools/PRESUBMIT.py create mode 100644 third_party/spirv-tools/README.md create mode 100644 third_party/spirv-tools/WORKSPACE create mode 100644 third_party/spirv-tools/android_test/Android.mk create mode 100644 third_party/spirv-tools/android_test/jni/Application.mk create mode 100644 third_party/spirv-tools/android_test/test.cpp create mode 100644 third_party/spirv-tools/build_defs.bzl create mode 100644 third_party/spirv-tools/build_overrides/build.gni create mode 100644 third_party/spirv-tools/build_overrides/gtest.gni create mode 100644 third_party/spirv-tools/build_overrides/spirv_tools.gni create mode 100644 third_party/spirv-tools/cmake/SPIRV-Tools-shared.pc.in create mode 100644 third_party/spirv-tools/cmake/SPIRV-Tools.pc.in create mode 100644 third_party/spirv-tools/cmake/write_pkg_config.cmake create mode 100644 third_party/spirv-tools/codereview.settings create mode 100644 third_party/spirv-tools/docs/downloads.md create mode 100644 third_party/spirv-tools/docs/projects.md create mode 100644 third_party/spirv-tools/docs/spirv-fuzz.md create mode 100644 third_party/spirv-tools/docs/syntax.md create mode 100644 third_party/spirv-tools/examples/CMakeLists.txt create mode 100644 third_party/spirv-tools/examples/cpp-interface/CMakeLists.txt create mode 100644 third_party/spirv-tools/examples/cpp-interface/main.cpp create mode 100644 third_party/spirv-tools/external/CMakeLists.txt create mode 100644 third_party/spirv-tools/external/spirv-headers/.gitattributes create mode 100644 third_party/spirv-tools/external/spirv-headers/.gitignore create mode 100644 third_party/spirv-tools/external/spirv-headers/BUILD.bazel create mode 100644 third_party/spirv-tools/external/spirv-headers/BUILD.gn create mode 100644 third_party/spirv-tools/external/spirv-headers/CMakeLists.txt create mode 100644 third_party/spirv-tools/external/spirv-headers/CODE_OF_CONDUCT.md create mode 100644 third_party/spirv-tools/external/spirv-headers/LICENSE create mode 100644 third_party/spirv-tools/external/spirv-headers/README.md create mode 100644 third_party/spirv-tools/external/spirv-headers/WORKSPACE create mode 100644 third_party/spirv-tools/external/spirv-headers/cmake/Config.cmake.in create mode 100644 third_party/spirv-tools/external/spirv-headers/example/CMakeLists.txt create mode 100644 third_party/spirv-tools/external/spirv-headers/example/example.cpp create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/GLSL.std.450.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/OpenCL.std.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.glsl.std.450.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.opencl.std.100.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.core.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.cs create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp11 create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.lua create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.py create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/GLSL.std.450.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/OpenCL.std.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.glsl.std.450.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.opencl.std.100.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.core.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.cs create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp11 create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.lua create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.py create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/GLSL.std.450.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/OpenCL.std.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.glsl.std.450.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.opencl.std.100.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.core.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.cs create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp11 create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.lua create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.py create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_gcn_shader.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_ballot.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_explicit_vertex_parameter.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_trinary_minmax.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/DebugInfo.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/GLSL.std.450.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugPrintf.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCL.std.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCLDebugInfo100.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.debuginfo.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugprintf.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.std.100.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-ballot.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-trinary-minmax.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py create mode 100644 third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d create mode 100644 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/CMakeLists.txt create mode 100755 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/generate_language_headers.py create mode 100755 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeExtinstHeaders.py create mode 100755 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeHeaders create mode 100644 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/header.cpp create mode 100644 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/header.h create mode 100644 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.cpp create mode 100644 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.h create mode 100644 third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/main.cpp create mode 100644 third_party/spirv-tools/filament-specific-changes.patch create mode 100644 third_party/spirv-tools/include/spirv-tools/instrument.hpp create mode 100644 third_party/spirv-tools/include/spirv-tools/libspirv.h create mode 100644 third_party/spirv-tools/include/spirv-tools/libspirv.hpp create mode 100644 third_party/spirv-tools/include/spirv-tools/linker.hpp create mode 100644 third_party/spirv-tools/include/spirv-tools/optimizer.hpp create mode 100644 third_party/spirv-tools/kokoro/android/build.sh create mode 100644 third_party/spirv-tools/kokoro/android/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/android/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/check-format/build.sh create mode 100644 third_party/spirv-tools/kokoro/check-format/presubmit_check_format.cfg create mode 100644 third_party/spirv-tools/kokoro/img/linux.png create mode 100644 third_party/spirv-tools/kokoro/img/macos.png create mode 100644 third_party/spirv-tools/kokoro/img/windows.png create mode 100644 third_party/spirv-tools/kokoro/linux-clang-asan/build.sh create mode 100644 third_party/spirv-tools/kokoro/linux-clang-asan/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-clang-asan/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-clang-debug/build.sh create mode 100644 third_party/spirv-tools/kokoro/linux-clang-debug/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-clang-debug/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-clang-release-bazel/build.sh create mode 100644 third_party/spirv-tools/kokoro/linux-clang-release-bazel/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-clang-release-bazel/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-clang-release/build.sh create mode 100644 third_party/spirv-tools/kokoro/linux-clang-release/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-clang-release/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-gcc-debug/build.sh create mode 100644 third_party/spirv-tools/kokoro/linux-gcc-debug/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-gcc-debug/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-gcc-release/build.sh create mode 100644 third_party/spirv-tools/kokoro/linux-gcc-release/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/linux-gcc-release/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/macos-clang-debug/build.sh create mode 100644 third_party/spirv-tools/kokoro/macos-clang-debug/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/macos-clang-debug/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/macos-clang-release-bazel/build.sh create mode 100644 third_party/spirv-tools/kokoro/macos-clang-release-bazel/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/macos-clang-release-bazel/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/macos-clang-release/build.sh create mode 100644 third_party/spirv-tools/kokoro/macos-clang-release/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/macos-clang-release/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/ndk-build/build.sh create mode 100644 third_party/spirv-tools/kokoro/ndk-build/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/ndk-build/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/scripts/linux/build.sh create mode 100644 third_party/spirv-tools/kokoro/scripts/macos/build.sh create mode 100644 third_party/spirv-tools/kokoro/scripts/windows/build.bat create mode 100644 third_party/spirv-tools/kokoro/shaderc-smoketest/build.sh create mode 100644 third_party/spirv-tools/kokoro/shaderc-smoketest/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/shaderc-smoketest/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2013-release/build.bat create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2013-release/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2013-release/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/build.bat create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2015-release/build.bat create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2015-release/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2015-release/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2017-debug/build.bat create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2017-debug/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2017-debug/presubmit.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2017-release/build.bat create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2017-release/continuous.cfg create mode 100644 third_party/spirv-tools/kokoro/windows-msvc-2017-release/presubmit.cfg create mode 100644 third_party/spirv-tools/source/CMakeLists.txt create mode 100644 third_party/spirv-tools/source/assembly_grammar.cpp create mode 100644 third_party/spirv-tools/source/assembly_grammar.h create mode 100644 third_party/spirv-tools/source/binary.cpp create mode 100644 third_party/spirv-tools/source/binary.h create mode 100644 third_party/spirv-tools/source/cfa.h create mode 100644 third_party/spirv-tools/source/diagnostic.cpp create mode 100644 third_party/spirv-tools/source/diagnostic.h create mode 100644 third_party/spirv-tools/source/disassemble.cpp create mode 100644 third_party/spirv-tools/source/disassemble.h create mode 100644 third_party/spirv-tools/source/enum_set.h create mode 100644 third_party/spirv-tools/source/enum_string_mapping.cpp create mode 100644 third_party/spirv-tools/source/enum_string_mapping.h create mode 100644 third_party/spirv-tools/source/ext_inst.cpp create mode 100644 third_party/spirv-tools/source/ext_inst.h create mode 100644 third_party/spirv-tools/source/extensions.cpp create mode 100644 third_party/spirv-tools/source/extensions.h create mode 100644 third_party/spirv-tools/source/fuzz/CMakeLists.txt create mode 100644 third_party/spirv-tools/source/fuzz/added_function_reducer.cpp create mode 100644 third_party/spirv-tools/source/fuzz/added_function_reducer.h create mode 100644 third_party/spirv-tools/source/fuzz/call_graph.cpp create mode 100644 third_party/spirv-tools/source/fuzz/call_graph.h create mode 100644 third_party/spirv-tools/source/fuzz/comparator_deep_blocks_first.h create mode 100644 third_party/spirv-tools/source/fuzz/counter_overflow_id_source.cpp create mode 100644 third_party/spirv-tools/source/fuzz/counter_overflow_id_source.h create mode 100644 third_party/spirv-tools/source/fuzz/data_descriptor.cpp create mode 100644 third_party/spirv-tools/source/fuzz/data_descriptor.h create mode 100644 third_party/spirv-tools/source/fuzz/equivalence_relation.h create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.h create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/dead_block_facts.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/dead_block_facts.h create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/fact_manager.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/fact_manager.h create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.h create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.h create mode 100644 third_party/spirv-tools/source/fuzz/force_render_red.cpp create mode 100644 third_party/spirv-tools/source/fuzz/force_render_red.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_context.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_context.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_util.cpp create mode 100644 third_party/spirv-tools/source/fuzz/fuzzer_util.h create mode 100644 third_party/spirv-tools/source/fuzz/id_use_descriptor.cpp create mode 100644 third_party/spirv-tools/source/fuzz/id_use_descriptor.h create mode 100644 third_party/spirv-tools/source/fuzz/instruction_descriptor.cpp create mode 100644 third_party/spirv-tools/source/fuzz/instruction_descriptor.h create mode 100644 third_party/spirv-tools/source/fuzz/instruction_message.cpp create mode 100644 third_party/spirv-tools/source/fuzz/instruction_message.h create mode 100644 third_party/spirv-tools/source/fuzz/overflow_id_source.cpp create mode 100644 third_party/spirv-tools/source/fuzz/overflow_id_source.h create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.cpp create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.h create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.cpp create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.h create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.cpp create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.h create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp create mode 100644 third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.h create mode 100644 third_party/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h create mode 100644 third_party/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto create mode 100644 third_party/spirv-tools/source/fuzz/pseudo_random_generator.cpp create mode 100644 third_party/spirv-tools/source/fuzz/pseudo_random_generator.h create mode 100644 third_party/spirv-tools/source/fuzz/random_generator.cpp create mode 100644 third_party/spirv-tools/source/fuzz/random_generator.h create mode 100644 third_party/spirv-tools/source/fuzz/replayer.cpp create mode 100644 third_party/spirv-tools/source/fuzz/replayer.h create mode 100644 third_party/spirv-tools/source/fuzz/shrinker.cpp create mode 100644 third_party/spirv-tools/source/fuzz/shrinker.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_access_chain.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_access_chain.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_constant_boolean.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_constant_composite.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_constant_null.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_constant_null.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_constant_scalar.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_copy_memory.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_dead_block.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_dead_block.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_dead_break.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_dead_break.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_dead_continue.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_function.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_function.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_global_undef.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_global_undef.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_global_variable.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_global_variable.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_local_variable.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_local_variable.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_loop_preheader.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_opphi_synonym.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_opphi_synonym.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_parameter.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_parameter.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_spec_constant_op.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_spec_constant_op.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_synonym.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_synonym.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_array.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_array.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_boolean.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_boolean.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_float.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_float.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_function.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_function.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_int.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_int.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_matrix.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_pointer.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_struct.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_struct.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_vector.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_add_type_vector.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_adjust_branch_weights.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_adjust_branch_weights.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_composite_construct.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_composite_construct.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_composite_extract.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_composite_extract.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_composite_insert.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_composite_insert.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_context.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_context.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_equation_instruction.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_equation_instruction.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_function_call.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_function_call.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_inline_function.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_inline_function.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_load.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_load.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_merge_blocks.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_merge_blocks.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_merge_function_returns.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_merge_function_returns.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_move_block_down.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_move_block_down.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_move_instruction_down.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_mutate_pointer.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_mutate_pointer.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_outline_function.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_outline_function.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_permute_function_parameters.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_permute_phi_operands.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_down.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_down.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_set_function_control.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_set_function_control.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_set_loop_control.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_set_loop_control.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_set_selection_control.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_set_selection_control.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_split_block.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_split_block.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_store.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_store.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_swap_commutable_operands.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.h create mode 100644 third_party/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.cpp create mode 100644 third_party/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.h create mode 100644 third_party/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp create mode 100644 third_party/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h create mode 100644 third_party/spirv-tools/source/instruction.h create mode 100644 third_party/spirv-tools/source/latest_version_glsl_std_450_header.h create mode 100644 third_party/spirv-tools/source/latest_version_opencl_std_header.h create mode 100644 third_party/spirv-tools/source/latest_version_spirv_header.h create mode 100644 third_party/spirv-tools/source/libspirv.cpp create mode 100644 third_party/spirv-tools/source/link/CMakeLists.txt create mode 100644 third_party/spirv-tools/source/link/linker.cpp create mode 100644 third_party/spirv-tools/source/macro.h create mode 100644 third_party/spirv-tools/source/name_mapper.cpp create mode 100644 third_party/spirv-tools/source/name_mapper.h create mode 100644 third_party/spirv-tools/source/opcode.cpp create mode 100644 third_party/spirv-tools/source/opcode.h create mode 100644 third_party/spirv-tools/source/operand.cpp create mode 100644 third_party/spirv-tools/source/operand.h create mode 100644 third_party/spirv-tools/source/opt/CMakeLists.txt create mode 100644 third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h create mode 100644 third_party/spirv-tools/source/opt/amd_ext_to_khr.cpp create mode 100644 third_party/spirv-tools/source/opt/amd_ext_to_khr.h create mode 100644 third_party/spirv-tools/source/opt/basic_block.cpp create mode 100644 third_party/spirv-tools/source/opt/basic_block.h create mode 100644 third_party/spirv-tools/source/opt/block_merge_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/block_merge_pass.h create mode 100644 third_party/spirv-tools/source/opt/block_merge_util.cpp create mode 100644 third_party/spirv-tools/source/opt/block_merge_util.h create mode 100644 third_party/spirv-tools/source/opt/build_module.cpp create mode 100644 third_party/spirv-tools/source/opt/build_module.h create mode 100644 third_party/spirv-tools/source/opt/ccp_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/ccp_pass.h create mode 100644 third_party/spirv-tools/source/opt/cfg.cpp create mode 100644 third_party/spirv-tools/source/opt/cfg.h create mode 100644 third_party/spirv-tools/source/opt/cfg_cleanup_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/cfg_cleanup_pass.h create mode 100644 third_party/spirv-tools/source/opt/code_sink.cpp create mode 100644 third_party/spirv-tools/source/opt/code_sink.h create mode 100644 third_party/spirv-tools/source/opt/combine_access_chains.cpp create mode 100644 third_party/spirv-tools/source/opt/combine_access_chains.h create mode 100644 third_party/spirv-tools/source/opt/compact_ids_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/compact_ids_pass.h create mode 100644 third_party/spirv-tools/source/opt/composite.cpp create mode 100644 third_party/spirv-tools/source/opt/composite.h create mode 100644 third_party/spirv-tools/source/opt/const_folding_rules.cpp create mode 100644 third_party/spirv-tools/source/opt/const_folding_rules.h create mode 100644 third_party/spirv-tools/source/opt/constants.cpp create mode 100644 third_party/spirv-tools/source/opt/constants.h create mode 100644 third_party/spirv-tools/source/opt/convert_to_half_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/convert_to_half_pass.h create mode 100644 third_party/spirv-tools/source/opt/copy_prop_arrays.cpp create mode 100644 third_party/spirv-tools/source/opt/copy_prop_arrays.h create mode 100644 third_party/spirv-tools/source/opt/dead_branch_elim_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/dead_branch_elim_pass.h create mode 100644 third_party/spirv-tools/source/opt/dead_insert_elim_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/dead_insert_elim_pass.h create mode 100644 third_party/spirv-tools/source/opt/dead_variable_elimination.cpp create mode 100644 third_party/spirv-tools/source/opt/dead_variable_elimination.h create mode 100644 third_party/spirv-tools/source/opt/debug_info_manager.cpp create mode 100644 third_party/spirv-tools/source/opt/debug_info_manager.h create mode 100644 third_party/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/decompose_initialized_variables_pass.h create mode 100644 third_party/spirv-tools/source/opt/decoration_manager.cpp create mode 100644 third_party/spirv-tools/source/opt/decoration_manager.h create mode 100644 third_party/spirv-tools/source/opt/def_use_manager.cpp create mode 100644 third_party/spirv-tools/source/opt/def_use_manager.h create mode 100644 third_party/spirv-tools/source/opt/desc_sroa.cpp create mode 100644 third_party/spirv-tools/source/opt/desc_sroa.h create mode 100644 third_party/spirv-tools/source/opt/dominator_analysis.cpp create mode 100644 third_party/spirv-tools/source/opt/dominator_analysis.h create mode 100644 third_party/spirv-tools/source/opt/dominator_tree.cpp create mode 100644 third_party/spirv-tools/source/opt/dominator_tree.h create mode 100644 third_party/spirv-tools/source/opt/eliminate_dead_constant_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/eliminate_dead_constant_pass.h create mode 100644 third_party/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/eliminate_dead_functions_pass.h create mode 100644 third_party/spirv-tools/source/opt/eliminate_dead_functions_util.cpp create mode 100644 third_party/spirv-tools/source/opt/eliminate_dead_functions_util.h create mode 100644 third_party/spirv-tools/source/opt/eliminate_dead_members_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/eliminate_dead_members_pass.h create mode 100644 third_party/spirv-tools/source/opt/empty_pass.h create mode 100644 third_party/spirv-tools/source/opt/feature_manager.cpp create mode 100644 third_party/spirv-tools/source/opt/feature_manager.h create mode 100644 third_party/spirv-tools/source/opt/fix_storage_class.cpp create mode 100644 third_party/spirv-tools/source/opt/fix_storage_class.h create mode 100644 third_party/spirv-tools/source/opt/flatten_decoration_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/flatten_decoration_pass.h create mode 100644 third_party/spirv-tools/source/opt/fold.cpp create mode 100644 third_party/spirv-tools/source/opt/fold.h create mode 100644 third_party/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h create mode 100644 third_party/spirv-tools/source/opt/folding_rules.cpp create mode 100644 third_party/spirv-tools/source/opt/folding_rules.h create mode 100644 third_party/spirv-tools/source/opt/freeze_spec_constant_value_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/freeze_spec_constant_value_pass.h create mode 100644 third_party/spirv-tools/source/opt/function.cpp create mode 100644 third_party/spirv-tools/source/opt/function.h create mode 100644 third_party/spirv-tools/source/opt/generate_webgpu_initializers_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/generate_webgpu_initializers_pass.h create mode 100644 third_party/spirv-tools/source/opt/graphics_robust_access_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/graphics_robust_access_pass.h create mode 100644 third_party/spirv-tools/source/opt/if_conversion.cpp create mode 100644 third_party/spirv-tools/source/opt/if_conversion.h create mode 100644 third_party/spirv-tools/source/opt/inline_exhaustive_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/inline_exhaustive_pass.h create mode 100644 third_party/spirv-tools/source/opt/inline_opaque_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/inline_opaque_pass.h create mode 100644 third_party/spirv-tools/source/opt/inline_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/inline_pass.h create mode 100644 third_party/spirv-tools/source/opt/inst_bindless_check_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/inst_bindless_check_pass.h create mode 100644 third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.h create mode 100644 third_party/spirv-tools/source/opt/inst_debug_printf_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/inst_debug_printf_pass.h create mode 100644 third_party/spirv-tools/source/opt/instruction.cpp create mode 100644 third_party/spirv-tools/source/opt/instruction.h create mode 100644 third_party/spirv-tools/source/opt/instruction_list.cpp create mode 100644 third_party/spirv-tools/source/opt/instruction_list.h create mode 100644 third_party/spirv-tools/source/opt/instrument_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/instrument_pass.h create mode 100644 third_party/spirv-tools/source/opt/ir_builder.h create mode 100644 third_party/spirv-tools/source/opt/ir_context.cpp create mode 100644 third_party/spirv-tools/source/opt/ir_context.h create mode 100644 third_party/spirv-tools/source/opt/ir_loader.cpp create mode 100644 third_party/spirv-tools/source/opt/ir_loader.h create mode 100644 third_party/spirv-tools/source/opt/iterator.h create mode 100644 third_party/spirv-tools/source/opt/legalize_vector_shuffle_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/legalize_vector_shuffle_pass.h create mode 100644 third_party/spirv-tools/source/opt/licm_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/licm_pass.h create mode 100644 third_party/spirv-tools/source/opt/local_access_chain_convert_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/local_access_chain_convert_pass.h create mode 100644 third_party/spirv-tools/source/opt/local_redundancy_elimination.cpp create mode 100644 third_party/spirv-tools/source/opt/local_redundancy_elimination.h create mode 100644 third_party/spirv-tools/source/opt/local_single_block_elim_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/local_single_block_elim_pass.h create mode 100644 third_party/spirv-tools/source/opt/local_single_store_elim_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/local_single_store_elim_pass.h create mode 100644 third_party/spirv-tools/source/opt/log.h create mode 100644 third_party/spirv-tools/source/opt/loop_dependence.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_dependence.h create mode 100644 third_party/spirv-tools/source/opt/loop_dependence_helpers.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_descriptor.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_descriptor.h create mode 100644 third_party/spirv-tools/source/opt/loop_fission.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_fission.h create mode 100644 third_party/spirv-tools/source/opt/loop_fusion.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_fusion.h create mode 100644 third_party/spirv-tools/source/opt/loop_fusion_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_fusion_pass.h create mode 100644 third_party/spirv-tools/source/opt/loop_peeling.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_peeling.h create mode 100644 third_party/spirv-tools/source/opt/loop_unroller.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_unroller.h create mode 100644 third_party/spirv-tools/source/opt/loop_unswitch_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_unswitch_pass.h create mode 100644 third_party/spirv-tools/source/opt/loop_utils.cpp create mode 100644 third_party/spirv-tools/source/opt/loop_utils.h create mode 100644 third_party/spirv-tools/source/opt/mem_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/mem_pass.h create mode 100644 third_party/spirv-tools/source/opt/merge_return_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/merge_return_pass.h create mode 100644 third_party/spirv-tools/source/opt/module.cpp create mode 100644 third_party/spirv-tools/source/opt/module.h create mode 100644 third_party/spirv-tools/source/opt/null_pass.h create mode 100644 third_party/spirv-tools/source/opt/optimizer.cpp create mode 100644 third_party/spirv-tools/source/opt/pass.cpp create mode 100644 third_party/spirv-tools/source/opt/pass.h create mode 100644 third_party/spirv-tools/source/opt/pass_manager.cpp create mode 100644 third_party/spirv-tools/source/opt/pass_manager.h create mode 100644 third_party/spirv-tools/source/opt/passes.h create mode 100644 third_party/spirv-tools/source/opt/pch_source_opt.cpp create mode 100644 third_party/spirv-tools/source/opt/pch_source_opt.h create mode 100644 third_party/spirv-tools/source/opt/private_to_local_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/private_to_local_pass.h create mode 100644 third_party/spirv-tools/source/opt/propagator.cpp create mode 100644 third_party/spirv-tools/source/opt/propagator.h create mode 100644 third_party/spirv-tools/source/opt/reduce_load_size.cpp create mode 100644 third_party/spirv-tools/source/opt/reduce_load_size.h create mode 100644 third_party/spirv-tools/source/opt/redundancy_elimination.cpp create mode 100644 third_party/spirv-tools/source/opt/redundancy_elimination.h create mode 100644 third_party/spirv-tools/source/opt/reflect.h create mode 100644 third_party/spirv-tools/source/opt/register_pressure.cpp create mode 100644 third_party/spirv-tools/source/opt/register_pressure.h create mode 100644 third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/relax_float_ops_pass.h create mode 100644 third_party/spirv-tools/source/opt/remove_duplicates_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/remove_duplicates_pass.h create mode 100644 third_party/spirv-tools/source/opt/replace_invalid_opc.cpp create mode 100644 third_party/spirv-tools/source/opt/replace_invalid_opc.h create mode 100644 third_party/spirv-tools/source/opt/scalar_analysis.cpp create mode 100644 third_party/spirv-tools/source/opt/scalar_analysis.h create mode 100644 third_party/spirv-tools/source/opt/scalar_analysis_nodes.h create mode 100644 third_party/spirv-tools/source/opt/scalar_analysis_simplification.cpp create mode 100644 third_party/spirv-tools/source/opt/scalar_replacement_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/scalar_replacement_pass.h create mode 100644 third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.h create mode 100644 third_party/spirv-tools/source/opt/simplification_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/simplification_pass.h create mode 100644 third_party/spirv-tools/source/opt/split_invalid_unreachable_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/split_invalid_unreachable_pass.h create mode 100644 third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/ssa_rewrite_pass.h create mode 100644 third_party/spirv-tools/source/opt/strength_reduction_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/strength_reduction_pass.h create mode 100644 third_party/spirv-tools/source/opt/strip_atomic_counter_memory_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/strip_atomic_counter_memory_pass.h create mode 100644 third_party/spirv-tools/source/opt/strip_debug_info_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/strip_debug_info_pass.h create mode 100644 third_party/spirv-tools/source/opt/strip_reflect_info_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/strip_reflect_info_pass.h create mode 100644 third_party/spirv-tools/source/opt/struct_cfg_analysis.cpp create mode 100644 third_party/spirv-tools/source/opt/struct_cfg_analysis.h create mode 100644 third_party/spirv-tools/source/opt/tree_iterator.h create mode 100644 third_party/spirv-tools/source/opt/type_manager.cpp create mode 100644 third_party/spirv-tools/source/opt/type_manager.h create mode 100644 third_party/spirv-tools/source/opt/types.cpp create mode 100644 third_party/spirv-tools/source/opt/types.h create mode 100644 third_party/spirv-tools/source/opt/unify_const_pass.cpp create mode 100644 third_party/spirv-tools/source/opt/unify_const_pass.h create mode 100644 third_party/spirv-tools/source/opt/upgrade_memory_model.cpp create mode 100644 third_party/spirv-tools/source/opt/upgrade_memory_model.h create mode 100644 third_party/spirv-tools/source/opt/value_number_table.cpp create mode 100644 third_party/spirv-tools/source/opt/value_number_table.h create mode 100644 third_party/spirv-tools/source/opt/vector_dce.cpp create mode 100644 third_party/spirv-tools/source/opt/vector_dce.h create mode 100644 third_party/spirv-tools/source/opt/workaround1209.cpp create mode 100644 third_party/spirv-tools/source/opt/workaround1209.h create mode 100644 third_party/spirv-tools/source/opt/wrap_opkill.cpp create mode 100644 third_party/spirv-tools/source/opt/wrap_opkill.h create mode 100644 third_party/spirv-tools/source/parsed_operand.cpp create mode 100644 third_party/spirv-tools/source/parsed_operand.h create mode 100644 third_party/spirv-tools/source/pch_source.cpp create mode 100644 third_party/spirv-tools/source/pch_source.h create mode 100644 third_party/spirv-tools/source/print.cpp create mode 100644 third_party/spirv-tools/source/print.h create mode 100644 third_party/spirv-tools/source/reduce/CMakeLists.txt create mode 100644 third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/pch_source_reduce.cpp create mode 100644 third_party/spirv-tools/source/reduce/pch_source_reduce.h create mode 100644 third_party/spirv-tools/source/reduce/reducer.cpp create mode 100644 third_party/spirv-tools/source/reduce/reducer.h create mode 100644 third_party/spirv-tools/source/reduce/reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/reduction_pass.cpp create mode 100644 third_party/spirv-tools/source/reduce/reduction_pass.h create mode 100644 third_party/spirv-tools/source/reduce/reduction_util.cpp create mode 100644 third_party/spirv-tools/source/reduce/reduction_util.h create mode 100644 third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp create mode 100644 third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h create mode 100644 third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp create mode 100644 third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h create mode 100644 third_party/spirv-tools/source/software_version.cpp create mode 100644 third_party/spirv-tools/source/spirv_constant.h create mode 100644 third_party/spirv-tools/source/spirv_definition.h create mode 100644 third_party/spirv-tools/source/spirv_endian.cpp create mode 100644 third_party/spirv-tools/source/spirv_endian.h create mode 100644 third_party/spirv-tools/source/spirv_fuzzer_options.cpp create mode 100644 third_party/spirv-tools/source/spirv_fuzzer_options.h create mode 100644 third_party/spirv-tools/source/spirv_optimizer_options.cpp create mode 100644 third_party/spirv-tools/source/spirv_optimizer_options.h create mode 100644 third_party/spirv-tools/source/spirv_reducer_options.cpp create mode 100644 third_party/spirv-tools/source/spirv_reducer_options.h create mode 100644 third_party/spirv-tools/source/spirv_target_env.cpp create mode 100644 third_party/spirv-tools/source/spirv_target_env.h create mode 100644 third_party/spirv-tools/source/spirv_validator_options.cpp create mode 100644 third_party/spirv-tools/source/spirv_validator_options.h create mode 100644 third_party/spirv-tools/source/table.cpp create mode 100644 third_party/spirv-tools/source/table.h create mode 100644 third_party/spirv-tools/source/text.cpp create mode 100644 third_party/spirv-tools/source/text.h create mode 100644 third_party/spirv-tools/source/text_handler.cpp create mode 100644 third_party/spirv-tools/source/text_handler.h create mode 100644 third_party/spirv-tools/source/util/bit_vector.cpp create mode 100644 third_party/spirv-tools/source/util/bit_vector.h create mode 100644 third_party/spirv-tools/source/util/bitutils.h create mode 100644 third_party/spirv-tools/source/util/hex_float.h create mode 100644 third_party/spirv-tools/source/util/ilist.h create mode 100644 third_party/spirv-tools/source/util/ilist_node.h create mode 100644 third_party/spirv-tools/source/util/make_unique.h create mode 100644 third_party/spirv-tools/source/util/parse_number.cpp create mode 100644 third_party/spirv-tools/source/util/parse_number.h create mode 100644 third_party/spirv-tools/source/util/small_vector.h create mode 100644 third_party/spirv-tools/source/util/string_utils.cpp create mode 100644 third_party/spirv-tools/source/util/string_utils.h create mode 100644 third_party/spirv-tools/source/util/timer.cpp create mode 100644 third_party/spirv-tools/source/util/timer.h create mode 100644 third_party/spirv-tools/source/val/basic_block.cpp create mode 100644 third_party/spirv-tools/source/val/basic_block.h create mode 100644 third_party/spirv-tools/source/val/construct.cpp create mode 100644 third_party/spirv-tools/source/val/construct.h create mode 100644 third_party/spirv-tools/source/val/decoration.h create mode 100644 third_party/spirv-tools/source/val/function.cpp create mode 100644 third_party/spirv-tools/source/val/function.h create mode 100644 third_party/spirv-tools/source/val/instruction.cpp create mode 100644 third_party/spirv-tools/source/val/instruction.h create mode 100644 third_party/spirv-tools/source/val/validate.cpp create mode 100644 third_party/spirv-tools/source/val/validate.h create mode 100644 third_party/spirv-tools/source/val/validate_adjacency.cpp create mode 100644 third_party/spirv-tools/source/val/validate_annotation.cpp create mode 100644 third_party/spirv-tools/source/val/validate_arithmetics.cpp create mode 100644 third_party/spirv-tools/source/val/validate_atomics.cpp create mode 100644 third_party/spirv-tools/source/val/validate_barriers.cpp create mode 100644 third_party/spirv-tools/source/val/validate_bitwise.cpp create mode 100644 third_party/spirv-tools/source/val/validate_builtins.cpp create mode 100644 third_party/spirv-tools/source/val/validate_capability.cpp create mode 100644 third_party/spirv-tools/source/val/validate_cfg.cpp create mode 100644 third_party/spirv-tools/source/val/validate_composites.cpp create mode 100644 third_party/spirv-tools/source/val/validate_constants.cpp create mode 100644 third_party/spirv-tools/source/val/validate_conversion.cpp create mode 100644 third_party/spirv-tools/source/val/validate_debug.cpp create mode 100644 third_party/spirv-tools/source/val/validate_decorations.cpp create mode 100644 third_party/spirv-tools/source/val/validate_derivatives.cpp create mode 100644 third_party/spirv-tools/source/val/validate_execution_limitations.cpp create mode 100644 third_party/spirv-tools/source/val/validate_extensions.cpp create mode 100644 third_party/spirv-tools/source/val/validate_function.cpp create mode 100644 third_party/spirv-tools/source/val/validate_id.cpp create mode 100644 third_party/spirv-tools/source/val/validate_image.cpp create mode 100644 third_party/spirv-tools/source/val/validate_instruction.cpp create mode 100644 third_party/spirv-tools/source/val/validate_interfaces.cpp create mode 100644 third_party/spirv-tools/source/val/validate_layout.cpp create mode 100644 third_party/spirv-tools/source/val/validate_literals.cpp create mode 100644 third_party/spirv-tools/source/val/validate_logicals.cpp create mode 100644 third_party/spirv-tools/source/val/validate_memory.cpp create mode 100644 third_party/spirv-tools/source/val/validate_memory_semantics.cpp create mode 100644 third_party/spirv-tools/source/val/validate_memory_semantics.h create mode 100644 third_party/spirv-tools/source/val/validate_misc.cpp create mode 100644 third_party/spirv-tools/source/val/validate_mode_setting.cpp create mode 100644 third_party/spirv-tools/source/val/validate_non_uniform.cpp create mode 100644 third_party/spirv-tools/source/val/validate_primitives.cpp create mode 100644 third_party/spirv-tools/source/val/validate_scopes.cpp create mode 100644 third_party/spirv-tools/source/val/validate_scopes.h create mode 100644 third_party/spirv-tools/source/val/validate_small_type_uses.cpp create mode 100644 third_party/spirv-tools/source/val/validate_type.cpp create mode 100644 third_party/spirv-tools/source/val/validation_state.cpp create mode 100644 third_party/spirv-tools/source/val/validation_state.h create mode 100644 third_party/spirv-tools/test/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/assembly_context_test.cpp create mode 100644 third_party/spirv-tools/test/assembly_format_test.cpp create mode 100644 third_party/spirv-tools/test/binary_destroy_test.cpp create mode 100644 third_party/spirv-tools/test/binary_endianness_test.cpp create mode 100644 third_party/spirv-tools/test/binary_header_get_test.cpp create mode 100644 third_party/spirv-tools/test/binary_parse_test.cpp create mode 100644 third_party/spirv-tools/test/binary_strnlen_s_test.cpp create mode 100644 third_party/spirv-tools/test/binary_to_text.literal_test.cpp create mode 100644 third_party/spirv-tools/test/binary_to_text_test.cpp create mode 100644 third_party/spirv-tools/test/c_interface_test.cpp create mode 100644 third_party/spirv-tools/test/comment_test.cpp create mode 100644 third_party/spirv-tools/test/cpp_interface_test.cpp create mode 100644 third_party/spirv-tools/test/diagnostic_test.cpp create mode 100644 third_party/spirv-tools/test/enum_set_test.cpp create mode 100644 third_party/spirv-tools/test/enum_string_mapping_test.cpp create mode 100644 third_party/spirv-tools/test/ext_inst.cldebug100_test.cpp create mode 100644 third_party/spirv-tools/test/ext_inst.debuginfo_test.cpp create mode 100644 third_party/spirv-tools/test/ext_inst.glsl_test.cpp create mode 100644 third_party/spirv-tools/test/ext_inst.non_semantic_test.cpp create mode 100644 third_party/spirv-tools/test/ext_inst.opencl_test.cpp create mode 100644 third_party/spirv-tools/test/fix_word_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/fuzz/call_graph_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/comparator_deep_blocks_first_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/data_synonym_transformation_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/equivalence_relation_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fact_manager/constant_uniform_facts_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fact_manager/data_synonym_and_id_equation_facts_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fact_manager/dead_block_facts_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fact_manager/irrelevant_value_facts_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fuzz_test_util.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fuzz_test_util.h create mode 100644 third_party/spirv-tools/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fuzzer_pass_outline_functions_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fuzzer_pass_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/instruction_descriptor_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/replayer_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/shrinker_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_access_chain_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_constant_boolean_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_constant_composite_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_constant_null_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_constant_scalar_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_copy_memory_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_dead_block_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_dead_break_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_early_terminator_wrapper_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_function_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_global_undef_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_global_variable_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_local_variable_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_loop_preheader_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_loop_to_create_int_constant_synonym_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_opphi_synonym_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_parameter_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_synonym_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_array_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_boolean_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_float_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_function_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_int_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_matrix_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_struct_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_add_type_vector_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_adjust_branch_weights_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_composite_construct_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_composite_extract_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_composite_insert_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_duplicate_region_with_selection_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_equation_instruction_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_expand_vector_reduction_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_flatten_conditional_branch_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_function_call_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_inline_function_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_invert_comparison_operator_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_load_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_make_vector_operation_dynamic_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_merge_blocks_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_merge_function_returns_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_move_block_down_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_move_instruction_down_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_mutate_pointer_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_outline_function_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_permute_function_parameters_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_permute_phi_operands_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_propagate_instruction_down_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_propagate_instruction_up_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_push_id_through_variable_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_record_synonymous_constants_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_add_sub_mul_with_carrying_extended_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_irrelevant_id_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_opphi_id_from_dead_predecessor_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_opselect_with_conditional_branch_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_replace_params_with_struct_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_split_block_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_store_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_swap_commutable_operands_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_vector_shuffle_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_wrap_early_terminator_in_function_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/transformation_wrap_region_in_selection_test.cpp create mode 100644 third_party/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/BUILD.gn create mode 100644 third_party/spirv-tools/test/fuzzers/corpora/spv/simple.spv create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_as_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_binary_parser_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_opt_performance_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_opt_size_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_val_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/fuzzers/spvtools_val_webgpu_fuzzer.cpp create mode 100644 third_party/spirv-tools/test/generator_magic_number_test.cpp create mode 100644 third_party/spirv-tools/test/hex_float_test.cpp create mode 100644 third_party/spirv-tools/test/immediate_int_test.cpp create mode 100644 third_party/spirv-tools/test/libspirv_macros_test.cpp create mode 100644 third_party/spirv-tools/test/link/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/link/binary_version_test.cpp create mode 100644 third_party/spirv-tools/test/link/entry_points_test.cpp create mode 100644 third_party/spirv-tools/test/link/global_values_amount_test.cpp create mode 100644 third_party/spirv-tools/test/link/ids_limit_test.cpp create mode 100644 third_party/spirv-tools/test/link/linker_fixture.h create mode 100644 third_party/spirv-tools/test/link/matching_imports_to_exports_test.cpp create mode 100644 third_party/spirv-tools/test/link/memory_model_test.cpp create mode 100644 third_party/spirv-tools/test/link/partial_linkage_test.cpp create mode 100644 third_party/spirv-tools/test/link/type_match_test.cpp create mode 100644 third_party/spirv-tools/test/link/unique_ids_test.cpp create mode 100644 third_party/spirv-tools/test/log_test.cpp create mode 100644 third_party/spirv-tools/test/name_mapper_test.cpp create mode 100644 third_party/spirv-tools/test/named_id_test.cpp create mode 100644 third_party/spirv-tools/test/opcode_make_test.cpp create mode 100644 third_party/spirv-tools/test/opcode_require_capabilities_test.cpp create mode 100644 third_party/spirv-tools/test/opcode_split_test.cpp create mode 100644 third_party/spirv-tools/test/opcode_table_get_test.cpp create mode 100644 third_party/spirv-tools/test/operand-class-test-coverage.csv create mode 100644 third_party/spirv-tools/test/operand_capabilities_test.cpp create mode 100644 third_party/spirv-tools/test/operand_pattern_test.cpp create mode 100644 third_party/spirv-tools/test/operand_test.cpp create mode 100644 third_party/spirv-tools/test/opt/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp create mode 100644 third_party/spirv-tools/test/opt/amd_ext_to_khr.cpp create mode 100644 third_party/spirv-tools/test/opt/assembly_builder.h create mode 100644 third_party/spirv-tools/test/opt/assembly_builder_test.cpp create mode 100644 third_party/spirv-tools/test/opt/block_merge_test.cpp create mode 100644 third_party/spirv-tools/test/opt/ccp_test.cpp create mode 100644 third_party/spirv-tools/test/opt/cfg_cleanup_test.cpp create mode 100644 third_party/spirv-tools/test/opt/cfg_test.cpp create mode 100644 third_party/spirv-tools/test/opt/code_sink_test.cpp create mode 100644 third_party/spirv-tools/test/opt/combine_access_chains_test.cpp create mode 100644 third_party/spirv-tools/test/opt/compact_ids_test.cpp create mode 100644 third_party/spirv-tools/test/opt/constant_manager_test.cpp create mode 100644 third_party/spirv-tools/test/opt/constants_test.cpp create mode 100644 third_party/spirv-tools/test/opt/convert_relaxed_to_half_test.cpp create mode 100644 third_party/spirv-tools/test/opt/copy_prop_array_test.cpp create mode 100644 third_party/spirv-tools/test/opt/dead_branch_elim_test.cpp create mode 100644 third_party/spirv-tools/test/opt/dead_insert_elim_test.cpp create mode 100644 third_party/spirv-tools/test/opt/dead_variable_elim_test.cpp create mode 100644 third_party/spirv-tools/test/opt/debug_info_manager_test.cpp create mode 100644 third_party/spirv-tools/test/opt/decompose_initialized_variables_test.cpp create mode 100644 third_party/spirv-tools/test/opt/decoration_manager_test.cpp create mode 100644 third_party/spirv-tools/test/opt/def_use_test.cpp create mode 100644 third_party/spirv-tools/test/opt/desc_sroa_test.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/common_dominators.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/generated.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/nested_ifs.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/nested_ifs_post.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/nested_loops.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/nested_loops_with_unreachables.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/pch_test_opt_dom.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/pch_test_opt_dom.h create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/post.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/simple.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/switch_case_fallthrough.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/unreachable_for.cpp create mode 100644 third_party/spirv-tools/test/opt/dominator_tree/unreachable_for_post.cpp create mode 100644 third_party/spirv-tools/test/opt/eliminate_dead_const_test.cpp create mode 100644 third_party/spirv-tools/test/opt/eliminate_dead_functions_test.cpp create mode 100644 third_party/spirv-tools/test/opt/eliminate_dead_member_test.cpp create mode 100644 third_party/spirv-tools/test/opt/feature_manager_test.cpp create mode 100644 third_party/spirv-tools/test/opt/fix_storage_class_test.cpp create mode 100644 third_party/spirv-tools/test/opt/flatten_decoration_test.cpp create mode 100644 third_party/spirv-tools/test/opt/fold_spec_const_op_composite_test.cpp create mode 100644 third_party/spirv-tools/test/opt/fold_test.cpp create mode 100644 third_party/spirv-tools/test/opt/freeze_spec_const_test.cpp create mode 100644 third_party/spirv-tools/test/opt/function_test.cpp create mode 100644 third_party/spirv-tools/test/opt/function_utils.h create mode 100644 third_party/spirv-tools/test/opt/generate_webgpu_initializers_test.cpp create mode 100644 third_party/spirv-tools/test/opt/graphics_robust_access_test.cpp create mode 100644 third_party/spirv-tools/test/opt/if_conversion_test.cpp create mode 100644 third_party/spirv-tools/test/opt/inline_opaque_test.cpp create mode 100644 third_party/spirv-tools/test/opt/inline_test.cpp create mode 100644 third_party/spirv-tools/test/opt/insert_extract_elim_test.cpp create mode 100644 third_party/spirv-tools/test/opt/inst_bindless_check_test.cpp create mode 100644 third_party/spirv-tools/test/opt/inst_buff_addr_check_test.cpp create mode 100644 third_party/spirv-tools/test/opt/inst_debug_printf_test.cpp create mode 100644 third_party/spirv-tools/test/opt/instruction_list_test.cpp create mode 100644 third_party/spirv-tools/test/opt/instruction_test.cpp create mode 100644 third_party/spirv-tools/test/opt/ir_builder.cpp create mode 100644 third_party/spirv-tools/test/opt/ir_context_test.cpp create mode 100644 third_party/spirv-tools/test/opt/ir_loader_test.cpp create mode 100644 third_party/spirv-tools/test/opt/iterator_test.cpp create mode 100644 third_party/spirv-tools/test/opt/legalize_vector_shuffle_test.cpp create mode 100644 third_party/spirv-tools/test/opt/line_debug_info_test.cpp create mode 100644 third_party/spirv-tools/test/opt/local_access_chain_convert_test.cpp create mode 100644 third_party/spirv-tools/test/opt/local_redundancy_elimination_test.cpp create mode 100644 third_party/spirv-tools/test/opt/local_single_block_elim.cpp create mode 100644 third_party/spirv-tools/test/opt/local_single_store_elim_test.cpp create mode 100644 third_party/spirv-tools/test/opt/local_ssa_elim_test.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/dependence_analysis.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/dependence_analysis_helpers.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/fusion_compatibility.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/fusion_illegal.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/fusion_pass.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/hoist_all_loop_types.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/hoist_double_nested_loops.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/hoist_from_independent_loops.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/hoist_simple_case.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/hoist_single_nested_loops.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/hoist_without_preheader.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/lcssa.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/loop_descriptions.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/loop_fission.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/nested_loops.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/pch_test_opt_loop.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/pch_test_opt_loop.h create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/peeling.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/peeling_pass.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/unroll_assumptions.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/unroll_simple.cpp create mode 100644 third_party/spirv-tools/test/opt/loop_optimizations/unswitch.cpp create mode 100644 third_party/spirv-tools/test/opt/module_test.cpp create mode 100644 third_party/spirv-tools/test/opt/module_utils.h create mode 100644 third_party/spirv-tools/test/opt/optimizer_test.cpp create mode 100644 third_party/spirv-tools/test/opt/pass_fixture.h create mode 100644 third_party/spirv-tools/test/opt/pass_manager_test.cpp create mode 100644 third_party/spirv-tools/test/opt/pass_merge_return_test.cpp create mode 100644 third_party/spirv-tools/test/opt/pass_remove_duplicates_test.cpp create mode 100644 third_party/spirv-tools/test/opt/pass_utils.cpp create mode 100644 third_party/spirv-tools/test/opt/pass_utils.h create mode 100644 third_party/spirv-tools/test/opt/pch_test_opt.cpp create mode 100644 third_party/spirv-tools/test/opt/pch_test_opt.h create mode 100644 third_party/spirv-tools/test/opt/private_to_local_test.cpp create mode 100644 third_party/spirv-tools/test/opt/propagator_test.cpp create mode 100644 third_party/spirv-tools/test/opt/reduce_load_size_test.cpp create mode 100644 third_party/spirv-tools/test/opt/redundancy_elimination_test.cpp create mode 100644 third_party/spirv-tools/test/opt/register_liveness.cpp create mode 100644 third_party/spirv-tools/test/opt/relax_float_ops_test.cpp create mode 100644 third_party/spirv-tools/test/opt/replace_invalid_opc_test.cpp create mode 100644 third_party/spirv-tools/test/opt/scalar_analysis.cpp create mode 100644 third_party/spirv-tools/test/opt/scalar_replacement_test.cpp create mode 100644 third_party/spirv-tools/test/opt/set_spec_const_default_value_test.cpp create mode 100644 third_party/spirv-tools/test/opt/simplification_test.cpp create mode 100644 third_party/spirv-tools/test/opt/split_invalid_unreachable_test.cpp create mode 100644 third_party/spirv-tools/test/opt/strength_reduction_test.cpp create mode 100644 third_party/spirv-tools/test/opt/strip_atomic_counter_memory_test.cpp create mode 100644 third_party/spirv-tools/test/opt/strip_debug_info_test.cpp create mode 100644 third_party/spirv-tools/test/opt/strip_reflect_info_test.cpp create mode 100644 third_party/spirv-tools/test/opt/struct_cfg_analysis_test.cpp create mode 100644 third_party/spirv-tools/test/opt/type_manager_test.cpp create mode 100644 third_party/spirv-tools/test/opt/types_test.cpp create mode 100644 third_party/spirv-tools/test/opt/unify_const_test.cpp create mode 100644 third_party/spirv-tools/test/opt/upgrade_memory_model_test.cpp create mode 100644 third_party/spirv-tools/test/opt/utils_test.cpp create mode 100644 third_party/spirv-tools/test/opt/value_table_test.cpp create mode 100644 third_party/spirv-tools/test/opt/vector_dce_test.cpp create mode 100644 third_party/spirv-tools/test/opt/workaround1209_test.cpp create mode 100644 third_party/spirv-tools/test/opt/wrap_opkill_test.cpp create mode 100644 third_party/spirv-tools/test/parse_number_test.cpp create mode 100644 third_party/spirv-tools/test/pch_test.cpp create mode 100644 third_party/spirv-tools/test/pch_test.h create mode 100644 third_party/spirv-tools/test/preserve_numeric_ids_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/merge_blocks_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/operand_to_constant_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/operand_to_dominating_id_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/operand_to_undef_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/reduce_test_util.cpp create mode 100644 third_party/spirv-tools/test/reduce/reduce_test_util.h create mode 100644 third_party/spirv-tools/test/reduce/reducer_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/remove_block_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/remove_function_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/remove_selection_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/remove_unused_instruction_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/remove_unused_struct_member_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp create mode 100644 third_party/spirv-tools/test/reduce/validation_during_reduction_test.cpp create mode 100644 third_party/spirv-tools/test/scripts/test_compact_ids.py create mode 100644 third_party/spirv-tools/test/software_version_test.cpp create mode 100644 third_party/spirv-tools/test/string_utils_test.cpp create mode 100644 third_party/spirv-tools/test/target_env_test.cpp create mode 100644 third_party/spirv-tools/test/test_fixture.h create mode 100644 third_party/spirv-tools/test/text_advance_test.cpp create mode 100644 third_party/spirv-tools/test/text_destroy_test.cpp create mode 100644 third_party/spirv-tools/test/text_literal_test.cpp create mode 100644 third_party/spirv-tools/test/text_start_new_inst_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.annotation_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.barrier_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.composite_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.constant_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.control_flow_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.debug_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.device_side_enqueue_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.extension_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.function_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.group_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.image_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.literal_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.memory_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.misc_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.mode_setting_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.pipe_storage_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.reserved_sampling_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.subgroup_dispatch_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary.type_declaration_test.cpp create mode 100644 third_party/spirv-tools/test/text_to_binary_test.cpp create mode 100644 third_party/spirv-tools/test/text_word_get_test.cpp create mode 100644 third_party/spirv-tools/test/timer_test.cpp create mode 100644 third_party/spirv-tools/test/tools/CMakeLists.txt create mode 100755 third_party/spirv-tools/test/tools/expect.py create mode 100644 third_party/spirv-tools/test/tools/expect_unittest.py create mode 100644 third_party/spirv-tools/test/tools/opt/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/tools/opt/flags.py create mode 100644 third_party/spirv-tools/test/tools/opt/oconfig.py create mode 100755 third_party/spirv-tools/test/tools/placeholder.py create mode 100755 third_party/spirv-tools/test/tools/spirv_test_framework.py create mode 100644 third_party/spirv-tools/test/tools/spirv_test_framework_unittest.py create mode 100644 third_party/spirv-tools/test/unit_spirv.cpp create mode 100644 third_party/spirv-tools/test/unit_spirv.h create mode 100644 third_party/spirv-tools/test/util/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/util/bit_vector_test.cpp create mode 100644 third_party/spirv-tools/test/util/bitutils_test.cpp create mode 100644 third_party/spirv-tools/test/util/ilist_test.cpp create mode 100644 third_party/spirv-tools/test/util/small_vector_test.cpp create mode 100644 third_party/spirv-tools/test/val/CMakeLists.txt create mode 100644 third_party/spirv-tools/test/val/pch_test_val.cpp create mode 100644 third_party/spirv-tools/test/val/pch_test_val.h create mode 100644 third_party/spirv-tools/test/val/val_adjacency_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_arithmetics_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_atomics_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_barriers_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_bitwise_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_builtins_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_capability_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_cfg_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_code_generator.cpp create mode 100644 third_party/spirv-tools/test/val/val_code_generator.h create mode 100644 third_party/spirv-tools/test/val/val_composites_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_constants_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_conversion_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_data_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_decoration_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_derivatives_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_entry_point.cpp create mode 100644 third_party/spirv-tools/test/val/val_explicit_reserved_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_ext_inst_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_extension_spv_khr_terminate_invocation.cpp create mode 100644 third_party/spirv-tools/test/val/val_extensions_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_fixtures.h create mode 100644 third_party/spirv-tools/test/val/val_function_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_id_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_image_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_interfaces_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_layout_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_limits_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_literals_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_logicals_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_memory_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_misc_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_modes_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_non_semantic_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_non_uniform_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_opencl_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_primitives_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_small_type_uses_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_ssa_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_state_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_storage_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_type_unique_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_validation_state_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_version_test.cpp create mode 100644 third_party/spirv-tools/test/val/val_webgpu_test.cpp create mode 100644 third_party/spirv-tools/tools/CMakeLists.txt create mode 100644 third_party/spirv-tools/tools/as/as.cpp create mode 100644 third_party/spirv-tools/tools/cfg/bin_to_dot.cpp create mode 100644 third_party/spirv-tools/tools/cfg/bin_to_dot.h create mode 100644 third_party/spirv-tools/tools/cfg/cfg.cpp create mode 100644 third_party/spirv-tools/tools/dis/dis.cpp create mode 100644 third_party/spirv-tools/tools/emacs/50spirv-tools.el create mode 100644 third_party/spirv-tools/tools/emacs/CMakeLists.txt create mode 100644 third_party/spirv-tools/tools/fuzz/fuzz.cpp create mode 100644 third_party/spirv-tools/tools/io.h create mode 100644 third_party/spirv-tools/tools/lesspipe/CMakeLists.txt create mode 100644 third_party/spirv-tools/tools/lesspipe/spirv-lesspipe.sh create mode 100644 third_party/spirv-tools/tools/link/linker.cpp create mode 100644 third_party/spirv-tools/tools/opt/opt.cpp create mode 100644 third_party/spirv-tools/tools/reduce/reduce.cpp create mode 100644 third_party/spirv-tools/tools/sva/.eslintrc.json create mode 100644 third_party/spirv-tools/tools/sva/.gitignore create mode 100644 third_party/spirv-tools/tools/sva/README.md create mode 100755 third_party/spirv-tools/tools/sva/bin/sva.js create mode 100644 third_party/spirv-tools/tools/sva/mocha.opts create mode 100644 third_party/spirv-tools/tools/sva/package.json create mode 100644 third_party/spirv-tools/tools/sva/rollup.config.js create mode 100644 third_party/spirv-tools/tools/sva/src/assembler.js create mode 100644 third_party/spirv-tools/tools/sva/src/assembler_test.js create mode 100644 third_party/spirv-tools/tools/sva/src/ast.js create mode 100644 third_party/spirv-tools/tools/sva/src/lexer.js create mode 100644 third_party/spirv-tools/tools/sva/src/lexer_test.js create mode 100644 third_party/spirv-tools/tools/sva/src/parser.js create mode 100644 third_party/spirv-tools/tools/sva/src/parser_test.js create mode 100644 third_party/spirv-tools/tools/sva/src/spirv.data.js create mode 100644 third_party/spirv-tools/tools/sva/src/sva.js create mode 100644 third_party/spirv-tools/tools/sva/src/token.js create mode 100644 third_party/spirv-tools/tools/sva/tests/empty_main.spv_asm create mode 100644 third_party/spirv-tools/tools/sva/tests/index.html create mode 100644 third_party/spirv-tools/tools/sva/tests/simple.spv_asm create mode 100755 third_party/spirv-tools/tools/sva/tools/process_grammar.rb create mode 100644 third_party/spirv-tools/tools/sva/yarn.lock create mode 100644 third_party/spirv-tools/tools/util/cli_consumer.cpp create mode 100644 third_party/spirv-tools/tools/util/cli_consumer.h create mode 100644 third_party/spirv-tools/tools/val/val.cpp create mode 100755 third_party/spirv-tools/utils/check_code_format.sh create mode 100755 third_party/spirv-tools/utils/check_copyright.py create mode 100755 third_party/spirv-tools/utils/check_symbol_exports.py create mode 100755 third_party/spirv-tools/utils/fixup_fuzz_result.py create mode 100755 third_party/spirv-tools/utils/generate_grammar_tables.py create mode 100755 third_party/spirv-tools/utils/generate_language_headers.py create mode 100755 third_party/spirv-tools/utils/generate_registry_tables.py create mode 100755 third_party/spirv-tools/utils/generate_vim_syntax.py create mode 100755 third_party/spirv-tools/utils/git-sync-deps create mode 100755 third_party/spirv-tools/utils/roll_deps.sh create mode 100755 third_party/spirv-tools/utils/update_build_version.py create mode 100644 third_party/spirv-tools/utils/vscode/.gitignore create mode 100644 third_party/spirv-tools/utils/vscode/README.md create mode 100644 third_party/spirv-tools/utils/vscode/extension.js create mode 100644 third_party/spirv-tools/utils/vscode/install.bat create mode 100755 third_party/spirv-tools/utils/vscode/install.sh create mode 100644 third_party/spirv-tools/utils/vscode/package.json create mode 100644 third_party/spirv-tools/utils/vscode/spirv.json create mode 100644 third_party/spirv-tools/utils/vscode/spirv.json.tmpl create mode 100644 third_party/spirv-tools/utils/vscode/src/grammar/grammar.go create mode 100644 third_party/spirv-tools/utils/vscode/src/langsvr.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/LICENSE create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/README.md create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/handler.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/stream.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/wire.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/context.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/doc.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/enums.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/log.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/protocol.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/span.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsclient.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsprotocol.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsserver.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/span/parse.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/span/span.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/span/token.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/span/token111.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/span/token112.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/span/uri.go create mode 100644 third_party/spirv-tools/utils/vscode/src/lsp/span/utf16.go create mode 100644 third_party/spirv-tools/utils/vscode/src/parser/parser.go create mode 100755 third_party/spirv-tools/utils/vscode/src/schema/schema.go create mode 100644 third_party/spirv-tools/utils/vscode/src/schema/schema.go.tmpl create mode 100644 third_party/spirv-tools/utils/vscode/src/tools/gen-grammar.go diff --git a/third_party/civetweb/build.cmd b/third_party/civetweb/build.cmd index 8ccf0e4..6c1df16 100644 --- a/third_party/civetweb/build.cmd +++ b/third_party/civetweb/build.cmd @@ -1,866 +1,866 @@ -:: Make sure the extensions are enabled -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - call :print_usage "Failed to enable extensions" - exit /b 1 -) - -::Change the code page to unicode -@chcp 65001 1>nul 2>nul -@if errorlevel 1 ( - call :print_usage "Failed to change the code page to unicode" - exit /b 1 -) - -:: Set up some global variables -@set project=civetweb -@set "script_name=%~nx0" -@set "script_folder=%~dp0" -@set "script_folder=%script_folder:~0,-1%" -@set "output_path=%script_folder%\output" -@set "build_path=%output_path%\build" -@set "install_path=%output_path%\install" -@set build_shared=OFF -@set build_type=Release -@set dependency_path=%TEMP%\%project%-build-dependencies - -:: Check the command line parameters -@set logging_level=1 -@set "options=%* " -@if not "!options!"=="!options:/? =!" set usage="Convenience script to build %project% with CMake" -@for %%a in (%options%) do @( - @set arg=%%~a - @set arg=!arg: =! - @set one=!arg:~0,1! - @set two=!arg:~0,2! - @if /i [!arg!] == [/q] set quiet=true - @if /i [!two!] == [/v] call :verbosity "!arg!" - @if /i [!arg!] == [/s] set build_shared=ON - @if /i [!arg!] == [/d] set build_type=Debug - @if /i not [!one!] == [/] ( - if not defined generator ( - set generator=!arg! - ) else ( - set usage="Too many generators: !method! !arg!" ^ - "There should only be one generator parameter" - ) - ) -) -@if defined quiet ( - set logging_level=0 -) -@if not defined generator ( - set generator=MSVC -) -@if /i not [%generator%] == [MinGW] ( - if /i not [%generator%] == [MSVC] ( - call :print_usage "Invalid argument: %generator%" - exit /b 1 - ) -) - -:: Set up the logging -@set log_folder=%output_path%\logs -@call :iso8601 timestamp -@set log_path=%log_folder%\%timestamp%.log -@set log_keep=10 - -:: Only keep a certain amount of logs -@set /a "log_keep=log_keep-1" -@if not exist %log_folder% @mkdir %log_folder% -@for /f "skip=%log_keep%" %%f in ('dir /b /o-D /tc %log_folder%') do @( - call :log 4 "Removing old log file %log_folder%\%%f" - del %log_folder%\%%f -) - -:: Set up some more global variables -@call :architecture arch -@call :windows_version win_ver win_ver_major win_ver_minor win_ver_rev -@call :script_source script_source -@if [%script_source%] == [explorer] ( - set /a "logging_level=logging_level+1" -) - -:: Print the usage or start the script -@set exit_code=0 -@if defined usage ( - call :print_usage %usage% -) else ( - call :main - @if errorlevel 1 ( - @call :log 0 "Failed to build the %project% project" - @set exit_code=1 - ) -) - -:: Tell the user where the built files are -@call :log 5 -@call :log 0 "The built files are available in %install_path%" - -:: Stop the script if the user double clicked -@if [%script_source%] == [explorer] ( - pause -) - -@exit /b %exit_code% -@endlocal -@goto :eof - -:: -------------------------- Functions start here ---------------------------- - -:main - Main function that performs the build -@setlocal -@call :log 6 -@call :log 2 "Welcome to the %project% build script" -@call :log 6 "------------------------------------" -@call :log 6 -@call :log 2 "This script builds the project using CMake" -@call :log 6 -@call :log 2 "Generating %generator%..." -@call :log 6 -@set methods=dependencies ^ - generate ^ - build ^ - install -@for %%m in (%methods%) do @( - call :log 3 "Excuting the '%%m' method" - call :log 8 - call :%%~m - if errorlevel 1 ( - call :log 0 "Failed to complete the '%%~m' dependency routine" - call :log 0 "View the log at %log_path%" - exit /b 1 - ) -) -@call :log 6 "------------------------------------" -@call :log 2 "Build complete" -@call :log 6 -@endlocal -@goto :eof - -:print_usage - Prints the usage of the script -:: %* - message to print, each argument on it's own line -@setlocal -@for %%a in (%*) do @echo.%%~a -@echo. -@echo.build [/?][/v[v...]^|/q][MinGW^|MSVC] -@echo. -@echo. [MinGW^|(MSVC)] -@echo. Builds the library with one of the compilers -@echo. /s Builds shared libraries -@echo. /d Builds a debug variant of the project -@echo. /v Sets the output to be more verbose -@echo. /v[v...] Extra verbosity, /vv, /vvv, etc -@echo. /q Quiets the output -@echo. /? Shows this usage message -@echo. -@endlocal -@goto :eof - -:dependencies - Installs any prerequisites for the build -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - call :log 0 "Failed to enable extensions" - exit /b 1 -) -@call :log 5 -@call :log 0 "Installing dependencies for %generator%" -@if /i [%generator%] == [MinGW] ( - call :mingw compiler_path - @if errorlevel 1 ( - @call :log 5 - @call :log 0 "Failed to find MinGW" - @exit /b 1 - ) - set "PATH=!compiler_path!;%PATH%" - @call :find_in_path gcc_executable gcc.exe - @if errorlevel 1 ( - @call :log 5 - @call :log 0 "Failed to find gcc.exe" - @exit /b 1 - ) -) -@if [%reboot_required%] equ [1] call :reboot -@endlocal & set "PATH=%PATH%" -@goto :eof - -:generate - Uses CMake to generate the build files -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - call :log 0 "Failed to enable extensions" - exit /b 1 -) -@call :log 5 -@call :log 0 "Generating CMake files for %generator%" -@call :cmake cmake_executable -@if errorlevel 1 ( - @call :log 5 - @call :log 0 "Need CMake to create the build files" - @exit /b 1 -) -@if /i [%generator%] == [MinGW] @( - @set "generator_var=-G "MinGW Makefiles^"" -) -@if /i [%generator%] == [MSVC] @( - rem We could figure out the correct MSVS generator here -) -@call :iso8601 iso8601 -@set output=%temp%\cmake-%iso8601%.log -@if not exist %build_path% mkdir %build_path% -@cd %build_path% -@"%cmake_executable%" ^ - !generator_var! ^ - -DCMAKE_BUILD_TYPE=!build_type! ^ - -DBUILD_SHARED_LIBS=!build_shared! ^ - "%script_folder%" > "%output%" -@if errorlevel 1 ( - @call :log 5 - @call :log 0 "Failed to generate build files with CMake" - @call :log_append "%output%" - @cd %script_folder% - @exit /b 1 -) -@cd %script_folder% -@endlocal -@goto :eof - -:build - Builds the library -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - call :log 0 "Failed to enable extensions" - exit /b 1 -) -@call :log 5 -@call :log 0 "Building %project% with %generator%" -@if /i [%generator%] == [MinGW] @( - @call :find_in_path mingw32_make_executable mingw32-make.exe - @if errorlevel 1 ( - @call :log 5 - @call :log 0 "Failed to find mingw32-make" - @exit /b 1 - ) - @set "build_command=^"!mingw32_make_executable!^" all test" -) -@if /i [%generator%] == [MSVC] @( - @call :msbuild msbuild_executable - @if errorlevel 1 ( - @call :log 5 - @call :log 0 "Failed to find MSBuild" - @exit /b 1 - ) - @set "build_command=^"!msbuild_executable!^" /m:4 /p:Configuration=%build_type% %project%.sln" -) -@if not defined build_command ( - @call :log 5 - @call :log 0 "No build command for %generator%" - @exit /b 1 -) -@cd %build_path% -@call :iso8601 iso8601 -@set output=%temp%\build-%iso8601%.log -@call :log 7 -@call :log 2 "Build command: %build_command:"=%" -@%build_command% > "%output%" -@if errorlevel 1 ( - @call :log_append "%output%" - @call :log 5 - @call :log 0 "Failed to complete the build" - @exit /b 1 -) -@call :log_append "%output%" -@cd %script_folder% -@endlocal -@goto :eof - -:install - Installs the built files -@setlocal -@call :log 5 -@call :log 0 "Installing built files" -@call :cmake cmake_executable -@if errorlevel 1 ( - @call :log 5 - @call :log 0 "Need CMake to install the built files" - @exit /b 1 -) -@call :iso8601 iso8601 -@set output=%temp%\install-%iso8601%.log -@"%cmake_executable%" ^ - "-DCMAKE_INSTALL_PREFIX=%install_path%" ^ - -P "%build_path%/cmake_install.cmake" ^ - > "%output%" -@if errorlevel 1 ( - @call :log_append "%output%" - @call :log 5 - @call :log 0 "Failed to install the files" - @exit /b 1 -) -@call :log_append "%output%" -@endlocal -@goto :eof - -:script_source - Determines if the script was ran from the cli or explorer -:: %1 - The return variable [cli|explorer] -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - call :log 0 "Failed to enable extensions" - exit /b 1 -) -@call :log 3 "Attempting to detect the script source" -@echo "The invocation command was: '%cmdcmdline%'" >> %log_path% -@for /f "tokens=1-3,*" %%a in ("%cmdcmdline%") do @( - set cmd=%%~a - set arg1=%%~b - set arg2=%%~c - set rest=%%~d -) -@set quote=" -@if "!arg2:~0,1!" equ "!quote!" ( - if "!arg2:~-1!" neq "!quote!" ( - set "arg2=!arg2:~1!" - ) -) -@call :log 4 "cmd = %cmd%" -@call :log 4 "arg1 = %arg1%" -@call :log 4 "arg2 = %arg2%" -@call :log 4 "rest = %rest%" -@call :log 4 "src = %~f0" -@if /i "%arg2%" == "call" ( - set script_source=cli -) else ( - @if /i "%arg1%" == "/c" ( - set script_source=explorer - ) else ( - set script_source=cli - ) -) -@call :log 3 "The script was invoked from %script_source%" -@endlocal & set "%~1=%script_source%" -@goto :eof - -:architecture - Finds the system architecture -:: %1 - The return variable [x86|x86_64] -@setlocal -@call :log 3 "Determining the processor architecture" -@set "key=HKLM\System\CurrentControlSet\Control\Session Manager\Environment" -@set "var=PROCESSOR_ARCHITECTURE" -@for /f "skip=2 tokens=2,*" %%a in ('reg query "%key%" /v "%var%"') do @set "arch=%%b" -@if "%arch%" == "AMD64" set arch=x86_64 -@call :log 4 "arch = %arch%" -@endlocal & set "%~1=%arch%" -@goto :eof - -:md5 - Gets the MD5 checksum for a file -:: %1 - The hash -:: %2 - The file path -@setlocal -@set var=%~1 -@set file_path=%~2 -@if [%var%] == [] exit /b 1 -@if "%file_path%" == "" exit /b 1 -@if not exist "%file_path%" exit /b 1 -@for /f "skip=3 tokens=1,*" %%a in ('powershell Get-FileHash -Algorithm MD5 "'%file_path%'"') do @set hash=%%b -@if not defined hash ( - call :log 6 - call :log 0 "Failed to get MD5 hash for %file_path%" - exit /b 1 -) -@endlocal & set "%var%=%hash: =%" -@goto :eof - -:windows_version - Checks the windows version -:: %1 - The windows version -:: %2 - The major version number return variable -:: %3 - The minor version number return variable -:: %4 - The revision version number return variable -@setlocal -@call :log 3 "Retrieving the Windows version" -@for /f "tokens=2 delims=[]" %%x in ('ver') do @set win_ver=%%x -@set win_ver=%win_ver:Version =% -@set win_ver_major=%win_ver:~0,1% -@set win_ver_minor=%win_ver:~2,1% -@set win_ver_rev=%win_ver:~4% -@call :log 4 "win_ver = %win_ver%" -@endlocal & set "%~1=%win_ver%" ^ - & set "%~2=%win_ver_major%" ^ - & set "%~3=%win_ver_minor%" ^ - & set "%~4=%win_ver_rev%" -@goto :eof - -:find_in_path - Finds a program of file in the PATH -@setlocal -@set var=%~1 -@set file=%~2 -@if [%var%] == [] exit /b 1 -@if [%file%] == [] exit /b 1 -@call :log 3 "Searching PATH for %file%" -@for %%x in ("%file%") do @set "file_path=%%~f$PATH:x" -@if not defined file_path exit /b 1 -@endlocal & set "%var%=%file_path%" -@goto :eof - -:administrator_check - Checks for administrator priviledges -@setlocal -@call :log 2 "Checking for administrator priviledges" -@set "key=HKLM\Software\VCA\Tool Chain\Admin Check" -@reg add "%key%" /v Elevated /t REG_DWORD /d 1 /f > nul 2>&1 -@if errorlevel 1 exit /b 1 -@reg delete "%key%" /va /f > nul 2>&1 -@endlocal -@goto :eof - -:log_append - Appends another file into the current logging file -:: %1 - the file_path to the file to concatenate -@setlocal -@set "file_path=%~1" -@if [%file_path%] == [] exit /b 1 -@call :log 3 "Appending to log: %file_path%" -@call :iso8601 iso8601 -@set "temp_log=%temp%\append-%iso8601%.log" -@call :log 4 "Using temp file %temp_log%" -@type "%log_path%" "%file_path%" > "%temp_log%" 2>nul -@move /y "%temp_log%" "%log_path%" 1>nul -@del "%file_path%" 2>nul -@del "%temp_log%" 2>nul -@endlocal -@goto :eof - -:iso8601 - Returns the current time in ISO8601 format -:: %1 - the return variable -:: %2 - format [extended|basic*] -:: iso8601 - contains the resulting timestamp -@setlocal -@wmic Alias /? >NUL 2>&1 || @exit /b 1 -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "format=%~2" -@if "%format%" == "" set format=basic -@for /F "skip=1 tokens=1-6" %%g IN ('wmic Path Win32_UTCTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') do @( - @if "%%~l"=="" goto :iso8601_done - @set "yyyy=%%l" - @set "mm=00%%j" - @set "dd=00%%g" - @set "hour=00%%h" - @set "minute=00%%i" - @set "seconds=00%%k" -) -:iso8601_done -@set mm=%mm:~-2% -@set dd=%dd:~-2% -@set hour=%hour:~-2% -@set minute=%minute:~-2% -@set seconds=%seconds:~-2% -@if /i [%format%] == [extended] ( - set iso8601=%yyyy%-%mm%-%dd%T%hour%:%minute%:%seconds%Z -) else ( - if /i [%format%] == [basic] ( - set iso8601=%yyyy%%mm%%dd%T%hour%%minute%%seconds%Z - ) else ( - @exit /b 1 - ) -) -@set iso8601=%iso8601: =0% -@endlocal & set %var%=%iso8601% -@goto :eof - -:verbosity - Processes the verbosity parameter '/v[v...] -:: %1 - verbosity given on the command line -:: logging_level - set to the number of v's -@setlocal -@set logging_level=0 -@set verbosity=%~1 -:verbosity_loop -@set verbosity=%verbosity:~1% -@if not [%verbosity%] == [] @( - set /a "logging_level=logging_level+1" - goto verbosity_loop -) -@endlocal & set logging_level=%logging_level% -@goto :eof - -:log - Logs a message, depending on verbosity -:: %1 - level -:: [0-4] for CLI logging -:: [5-9] for GUI logging -:: %2 - message to print -@setlocal -@set "level=%~1" -@set "msg=%~2" -@if "%log_folder%" == "" ( - echo Logging was used to early in the script, log_folder isn't set yet - goto :eof -) -@if "%log_path%" == "" ( - echo Logging was used to early in the script, log_path isn't set yet - goto :eof -) -@if not exist "%log_folder%" mkdir "%log_folder%" -@if not exist "%log_path%" echo. 1>nul 2>"%log_path%" -@echo.%msg% >> "%log_path%" -@if %level% geq 5 ( - @if [%script_source%] == [explorer] ( - set /a "level=level-5" - ) else ( - @goto :eof - ) -) -@if "%logging_level%" == "" ( - echo Logging was used to early in the script, logging_level isn't set yet - goto :eof -) -@if %logging_level% geq %level% echo.%msg% 1>&2 -@endlocal -@goto :eof - - -:start_browser - Opens the default browser to a URL -:: %1 - the url to open -@setlocal -@set url=%~1 -@call :log 4 "Opening default browser: %url%" -@start %url% -@endlocal -@goto :eof - -:find_cmake - Finds cmake on the command line or in the registry -:: %1 - the cmake file path -@setlocal -@set var=%~1 -@if [%var%] == [] exit /b 1 -@call :log 6 -@call :log 6 "Finding CMake" -@call :log 6 "--------------" -@call :find_in_path cmake_executable cmake.exe -@if not errorlevel 1 goto found_cmake -@for /l %%i in (5,-1,0) do @( -@for /l %%j in (9,-1,0) do @( -@for /l %%k in (9,-1,0) do @( -@for %%l in (HKCU HKLM) do @( -@for %%m in (SOFTWARE SOFTWARE\Wow6432Node) do @( - @reg query "%%l\%%m\Kitware\CMake %%i.%%j.%%k" /ve > nul 2>nul - @if not errorlevel 1 ( - @for /f "skip=2 tokens=2,*" %%a in ('reg query "%%l\%%m\Kitware\CMake %%i.%%j.%%k" /ve') do @( - @if exist "%%b\bin\cmake.exe" ( - @set "cmake_executable=%%b\bin\cmake.exe" - goto found_cmake - ) - ) - ) -))))) -@call :log 5 -@call :log 0 "Failed to find cmake" -@exit /b 1 -:found_cmake -@endlocal & set "%var%=%cmake_executable%" -@goto :eof - -:cmake - Finds cmake and installs it if necessary -:: %1 - the cmake file path -@setlocal -@set var=%~1 -@if [%var%] == [] exit /b 1 -@call :log 6 -@call :log 6 "Checking for CMake" -@call :log 6 "------------------" -@call :find_cmake cmake_executable cmake.exe -@if not errorlevel 1 goto got_cmake -@set checksum=C00267A3D3D9619A7A2E8FA4F46D7698 -@set version=3.2.2 -@call :install_nsis cmake http://www.cmake.org/files/v%version:~0,3%/cmake-%version%-win32-x86.exe %checksum% -@if errorlevel 1 ( - call :log 5 - call :log 0 "Failed to install cmake" - @exit /b 1 -) -@call :find_cmake cmake_executable cmake.exe -@if not errorlevel 1 goto got_cmake -@call :log 5 -@call :log 0 "Failed to check for cmake" -@exit /b 1 -:got_cmake -@endlocal & set "%var%=%cmake_executable%" -@goto :eof - -:mingw - Finds MinGW, installing it if needed -:: %1 - the compiler path that should be added to PATH -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :log 5 - @call :log 0 "Failed to enable extensions" - @exit /b 1 -) -@set var=%~1 -@if [%var%] == [] exit /b 1 -@call :log 6 -@call :log 6 "Checking for MinGW" -@call :log 6 "------------------" -@call :find_in_path gcc_executable gcc.exe -@if not errorlevel 1 ( - @for %%a in ("%gcc_executable%") do @set "compiler_path=%%~dpa" - goto got_mingw -) -@call :log 7 -@call :log 2 "Downloading MinGW" -@if %logging_level% leq 1 set "logging=/q" -@if %logging_level% gtr 1 set "logging=/v" -@set output_path= -@for /f %%a in ('call - "%script_folder%\mingw.cmd" - %logging% - /arch "%arch%" - "%dependency_path%"' -) do @set "compiler_path=%%a\" -@if not defined compiler_path ( - @call :log_append "%output%" - @call :log 5 - @call :log 0 "Failed to download MinGW" - @exit /b 1 -) -:got_mingw -@call :log 5 -@call :log 0 "Found MinGW: %compiler_path%gcc.exe" -@endlocal & set "%var%=%compiler_path%" -@goto :eof - -:msbuild - Finds MSBuild -:: %1 - the path to MSBuild executable -@setlocal -@set var=%~1 -@if [%var%] == [] exit /b 1 -@call :find_in_path msbuild_executable msbuild.exe -@if not errorlevel 1 goto got_msbuild -@for /l %%i in (20,-1,4) do @( -@for /l %%j in (9,-1,0) do @( -@for %%k in (HKCU HKLM) do @( -@for %%l in (SOFTWARE SOFTWARE\Wow6432Node) do @( - @reg query "%%k\%%l\Microsoft\MSBuild\%%i.%%j" /v MSBuildOverrideTasksPath > nul 2>nul - @if not errorlevel 1 ( - @for /f "skip=2 tokens=2,*" %%a in ('reg query "%%k\%%l\Microsoft\MSBuild\%%i.%%j" /v MSBuildOverrideTasksPath') do @( - @if exist "%%bmsbuild.exe" ( - @set "msbuild_executable=%%bmsbuild.exe" - goto got_msbuild - ) - ) - ) -)))) -@call :log 5 -@call :log 0 "Failed to check for MSBuild" -@exit /b 1 -:got_msbuild -@endlocal & set "%var%=%msbuild_executable%" -@goto :eof - -:download - Downloads a file from the internet -:: %1 - the url of the file to download -:: %2 - the file to download to -:: %3 - the MD5 checksum of the file (optional) -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - call :print_usage "Failed to enable extensions" - exit /b 1 -) -@set url=%~1 -@set file_path=%~2 -@set checksum=%~3 -@for %%a in (%file_path%) do @set dir_path=%%~dpa -@for %%a in (%file_path%) do @set file_name=%%~nxa -@if [%url%] == [] exit /b 1 -@if [%file_path%] == [] exit /b 1 -@if [%dir_path%] == [] exit /b 1 -@if [%file_name%] == [] exit /b 1 -@if not exist "%dir_path%" mkdir "%dir_path%" -@call :log 1 "Downloading %url%" -@call :iso8601 iso8601 -@set temp_path=%temp%\download-%iso8601%-%file_name% -@call :log 3 "Using temp file %temp_path%" -@powershell Invoke-WebRequest "%url%" -OutFile %temp_path% -@if errorlevel 1 ( - call :log 0 "Failed to download %url%" - exit /b 1 -) -@if [%checksum%] neq [] ( - @call :log 4 "Checking %checksum% against %temp_path%" - @call :md5 hash "%temp_path%" - if "!hash!" neq "%checksum%" ( - call :log 0 "Failed to match checksum: %temp_path%" - call :log 0 "Hash : !hash!" - call :log 0 "Checksum: %checksum%" - exit /b 1 - ) else ( - call :log 3 "Checksum matched: %temp_path%" - call :log 3 "Hash : !hash!" - call :log 3 "Checksum: %checksum%" - ) -) -@call :log 4 "Renaming %temp_path% to %file_path%" -@move /y "%temp_path%" "%file_path%" 1>nul -@endlocal -@goto :eof - -:install_msi - Installs a dependency from an Microsoft Installer package (.msi) -:: %1 - [string] name of the project to install -:: %2 - The location of the .msi, a url must start with 'http://' or file_path -:: %3 - The checksum of the msi (optional) -@setlocal -@set name=%~1 -@set file_path=%~2 -@set checksum=%~3 -@set msi=%~nx2 -@set msi_path=%dependency_path%\%msi% -@if [%name%] == [] exit /b 1 -@if [%file_path%] == [] exit /b 1 -@if [%msi%] == [] exit /b 1 -@if [%msi_path%] == [] exit /b 1 -@for %%x in (msiexec.exe) do @set "msiexec_path=%%~f$PATH:x" -@if "msiexec_path" == "" ( - call :log 0 "Failed to find the Microsoft package installer (msiexec.exe)" - call :log 6 - call :log 0 "Please install it from the Microsoft Download center" - call :log 6 - choice /C YN /T 60 /D N /M "Would you like to go there now?" - if !errorlevel! equ 1 call :start_browser ^ - "http://search.microsoft.com/DownloadResults.aspx?q=Windows+Installer" - exit /b 1 -) -@call :log 6 -@call :log 1 "Installing the '%name%' dependency" -@call :log 6 "-------------------------------------" -@call :administrator_check -@if errorlevel 1 ( - call :log 0 "You must run %~nx0 in elevated mode to install '%name%'" - call :log 5 "Right-Click and select 'Run as Administrator' - call :log 0 "Install the dependency manually by running %file_path%" - @exit /b 740 -) -@if [%file_path:~0,4%] == [http] ( - if not exist "%msi_path%" ( - call :download "%file_path%" "%msi_path%" %checksum% - if errorlevel 1 ( - call :log 0 "Failed to download the %name% dependency" - exit /b 1 - ) - ) -) else ( - call :log 2 "Copying MSI %file_path% to %msi_path%" - call :log 7 - if not exist "%msi_path%" ( - xcopy /q /y /z "%file_path%" "%msi_path%" 1>nul - if errorlevel 1 ( - call :log 0 "Failed to copy the Microsoft Installer" - exit /b 1 - ) - ) -) -@call :log 1 "Running the %msi%" -@call :log 6 -@set msi_log=%temp%\msiexec-%timestamp%.log -@call :log 3 "Logging to: %msi_log%" -@msiexec /i "%msi_path%" /passive /log "%msi_log%" ALLUSERS=1 -@set msi_errorlevel=%errorlevel% -@call :log_append "%msi_log%" -@if %msi_errorlevel% equ 0 goto install_msi_success -@if %msi_errorlevel% equ 3010 goto install_msi_success_reboot -@if %msi_errorlevel% equ 1641 goto install_msi_success_reboot -@if %msi_errorlevel% equ 3015 goto install_msi_in_progress_reboot -@if %msi_errorlevel% equ 1615 goto install_msi_in_progress_reboot -@call :log 0 "Microsoft Installer failed: %msi_errorlevel%" -@call :log 0 "Install the dependency manually by running %msi_path%" -@exit /b 1 -:install_msi_in_progress_reboot -@call :log 0 "The installation requires a reboot to continue" -@call :log 5 -@call :reboot -@exit /b 1 -:install_msi_success_reboot -@call :log 3 "The installation requires a reboot to be fully functional" -@set reboot_required=1 -:install_msi_success -@call :log 2 "Successfully installed %name%" -@call :log 7 -@endlocal & set reboot_required=%reboot_required% -@goto :eof - -:install_nsis - Installs a dependency from an Nullsoft Installer package (.exe) -:: %1 - [string] name of the project to install -:: %2 - The location of the .exe, a url must start with 'http://' or file_path -:: %3 - The checksum of the exe (optional) -@setlocal -@set name=%~1 -@set file_path=%~2 -@set checksum=%~3 -@set exe=%~nx2 -@set exe_path=%dependency_path%\%exe% -@if [%name%] == [] exit /b 1 -@if [%file_path%] == [] exit /b 1 -@if [%exe%] == [] exit /b 1 -@if [%exe_path%] == [] exit /b 1 -@call :log 6 -@call :log 1 "Installing the '%name%' dependency" -@call :log 6 "-------------------------------------" -@call :administrator_check -@if errorlevel 1 ( - call :log 0 "You must run %~nx0 in elevated mode to install '%name%'" - call :log 5 "Right-Click and select 'Run as Administrator' - call :log 0 "Install the dependency manually by running %file_path%" - @exit /b 740 -) -@if [%file_path:~0,4%] == [http] ( - if not exist "%exe_path%" ( - call :download "%file_path%" "%exe_path%" %checksum% - if errorlevel 1 ( - call :log 0 "Failed to download the %name% dependency" - exit /b 1 - ) - ) -) else ( - call :log 2 "Copying installer %file_path% to %exe_path%" - call :log 7 - if not exist "%exe_path%" ( - xcopy /q /y /z "%file_path%" "%exe_path%" 1>nul - if errorlevel 1 ( - call :log 0 "Failed to copy the Nullsoft Installer" - exit /b 1 - ) - ) -) -@call :log 1 "Running the %exe%" -@call :log 6 -@"%exe_path%" /S -@set nsis_errorlevel=%errorlevel% -@if %nsis_errorlevel% equ 0 goto install_nsis_success -@if %nsis_errorlevel% equ 3010 goto install_nsis_success_reboot -@if %nsis_errorlevel% equ 1641 goto install_nsis_success_reboot -@if %nsis_errorlevel% equ 3015 goto install_nsis_in_progress_reboot -@if %nsis_errorlevel% equ 1615 goto install_nsis_in_progress_reboot -@call :log 0 "Nullsoft Installer failed: %nsis_errorlevel%" -@call :log 0 "Install the dependency manually by running %exe_path%" -@exit /b 1 -:install_nsis_in_progress_reboot -@call :log 0 "The installation requires a reboot to continue" -@call :log 5 -@call :reboot -@exit /b 1 -:install_nsis_success_reboot -@call :log 3 "The installation requires a reboot to be fully functional" -@set reboot_required=1 -:install_nsis_success -@call :log 2 "Successfully installed %name%" -@call :log 7 -@endlocal & set reboot_required=%reboot_required% -@goto :eof - -:reboot - Asks the user if they would like to reboot then stops the script -@setlocal -@call :log 6 "-------------------------------------------" -@choice /C YN /T 60 /D N /M "The %method% requires a reboot, reboot now?" -@set ret=%errorlevel% -@call :log 6 -@if %ret% equ 1 ( - @shutdown /r -) else ( - @call :log 0 "You will need to reboot to complete the %method%" - @call :log 5 -) -@endlocal -@goto :eof +:: Make sure the extensions are enabled +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + call :print_usage "Failed to enable extensions" + exit /b 1 +) + +::Change the code page to unicode +@chcp 65001 1>nul 2>nul +@if errorlevel 1 ( + call :print_usage "Failed to change the code page to unicode" + exit /b 1 +) + +:: Set up some global variables +@set project=civetweb +@set "script_name=%~nx0" +@set "script_folder=%~dp0" +@set "script_folder=%script_folder:~0,-1%" +@set "output_path=%script_folder%\output" +@set "build_path=%output_path%\build" +@set "install_path=%output_path%\install" +@set build_shared=OFF +@set build_type=Release +@set dependency_path=%TEMP%\%project%-build-dependencies + +:: Check the command line parameters +@set logging_level=1 +@set "options=%* " +@if not "!options!"=="!options:/? =!" set usage="Convenience script to build %project% with CMake" +@for %%a in (%options%) do @( + @set arg=%%~a + @set arg=!arg: =! + @set one=!arg:~0,1! + @set two=!arg:~0,2! + @if /i [!arg!] == [/q] set quiet=true + @if /i [!two!] == [/v] call :verbosity "!arg!" + @if /i [!arg!] == [/s] set build_shared=ON + @if /i [!arg!] == [/d] set build_type=Debug + @if /i not [!one!] == [/] ( + if not defined generator ( + set generator=!arg! + ) else ( + set usage="Too many generators: !method! !arg!" ^ + "There should only be one generator parameter" + ) + ) +) +@if defined quiet ( + set logging_level=0 +) +@if not defined generator ( + set generator=MSVC +) +@if /i not [%generator%] == [MinGW] ( + if /i not [%generator%] == [MSVC] ( + call :print_usage "Invalid argument: %generator%" + exit /b 1 + ) +) + +:: Set up the logging +@set log_folder=%output_path%\logs +@call :iso8601 timestamp +@set log_path=%log_folder%\%timestamp%.log +@set log_keep=10 + +:: Only keep a certain amount of logs +@set /a "log_keep=log_keep-1" +@if not exist %log_folder% @mkdir %log_folder% +@for /f "skip=%log_keep%" %%f in ('dir /b /o-D /tc %log_folder%') do @( + call :log 4 "Removing old log file %log_folder%\%%f" + del %log_folder%\%%f +) + +:: Set up some more global variables +@call :architecture arch +@call :windows_version win_ver win_ver_major win_ver_minor win_ver_rev +@call :script_source script_source +@if [%script_source%] == [explorer] ( + set /a "logging_level=logging_level+1" +) + +:: Print the usage or start the script +@set exit_code=0 +@if defined usage ( + call :print_usage %usage% +) else ( + call :main + @if errorlevel 1 ( + @call :log 0 "Failed to build the %project% project" + @set exit_code=1 + ) +) + +:: Tell the user where the built files are +@call :log 5 +@call :log 0 "The built files are available in %install_path%" + +:: Stop the script if the user double clicked +@if [%script_source%] == [explorer] ( + pause +) + +@exit /b %exit_code% +@endlocal +@goto :eof + +:: -------------------------- Functions start here ---------------------------- + +:main - Main function that performs the build +@setlocal +@call :log 6 +@call :log 2 "Welcome to the %project% build script" +@call :log 6 "------------------------------------" +@call :log 6 +@call :log 2 "This script builds the project using CMake" +@call :log 6 +@call :log 2 "Generating %generator%..." +@call :log 6 +@set methods=dependencies ^ + generate ^ + build ^ + install +@for %%m in (%methods%) do @( + call :log 3 "Excuting the '%%m' method" + call :log 8 + call :%%~m + if errorlevel 1 ( + call :log 0 "Failed to complete the '%%~m' dependency routine" + call :log 0 "View the log at %log_path%" + exit /b 1 + ) +) +@call :log 6 "------------------------------------" +@call :log 2 "Build complete" +@call :log 6 +@endlocal +@goto :eof + +:print_usage - Prints the usage of the script +:: %* - message to print, each argument on it's own line +@setlocal +@for %%a in (%*) do @echo.%%~a +@echo. +@echo.build [/?][/v[v...]^|/q][MinGW^|MSVC] +@echo. +@echo. [MinGW^|(MSVC)] +@echo. Builds the library with one of the compilers +@echo. /s Builds shared libraries +@echo. /d Builds a debug variant of the project +@echo. /v Sets the output to be more verbose +@echo. /v[v...] Extra verbosity, /vv, /vvv, etc +@echo. /q Quiets the output +@echo. /? Shows this usage message +@echo. +@endlocal +@goto :eof + +:dependencies - Installs any prerequisites for the build +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + call :log 0 "Failed to enable extensions" + exit /b 1 +) +@call :log 5 +@call :log 0 "Installing dependencies for %generator%" +@if /i [%generator%] == [MinGW] ( + call :mingw compiler_path + @if errorlevel 1 ( + @call :log 5 + @call :log 0 "Failed to find MinGW" + @exit /b 1 + ) + set "PATH=!compiler_path!;%PATH%" + @call :find_in_path gcc_executable gcc.exe + @if errorlevel 1 ( + @call :log 5 + @call :log 0 "Failed to find gcc.exe" + @exit /b 1 + ) +) +@if [%reboot_required%] equ [1] call :reboot +@endlocal & set "PATH=%PATH%" +@goto :eof + +:generate - Uses CMake to generate the build files +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + call :log 0 "Failed to enable extensions" + exit /b 1 +) +@call :log 5 +@call :log 0 "Generating CMake files for %generator%" +@call :cmake cmake_executable +@if errorlevel 1 ( + @call :log 5 + @call :log 0 "Need CMake to create the build files" + @exit /b 1 +) +@if /i [%generator%] == [MinGW] @( + @set "generator_var=-G "MinGW Makefiles^"" +) +@if /i [%generator%] == [MSVC] @( + rem We could figure out the correct MSVS generator here +) +@call :iso8601 iso8601 +@set output=%temp%\cmake-%iso8601%.log +@if not exist %build_path% mkdir %build_path% +@cd %build_path% +@"%cmake_executable%" ^ + !generator_var! ^ + -DCMAKE_BUILD_TYPE=!build_type! ^ + -DBUILD_SHARED_LIBS=!build_shared! ^ + "%script_folder%" > "%output%" +@if errorlevel 1 ( + @call :log 5 + @call :log 0 "Failed to generate build files with CMake" + @call :log_append "%output%" + @cd %script_folder% + @exit /b 1 +) +@cd %script_folder% +@endlocal +@goto :eof + +:build - Builds the library +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + call :log 0 "Failed to enable extensions" + exit /b 1 +) +@call :log 5 +@call :log 0 "Building %project% with %generator%" +@if /i [%generator%] == [MinGW] @( + @call :find_in_path mingw32_make_executable mingw32-make.exe + @if errorlevel 1 ( + @call :log 5 + @call :log 0 "Failed to find mingw32-make" + @exit /b 1 + ) + @set "build_command=^"!mingw32_make_executable!^" all test" +) +@if /i [%generator%] == [MSVC] @( + @call :msbuild msbuild_executable + @if errorlevel 1 ( + @call :log 5 + @call :log 0 "Failed to find MSBuild" + @exit /b 1 + ) + @set "build_command=^"!msbuild_executable!^" /m:4 /p:Configuration=%build_type% %project%.sln" +) +@if not defined build_command ( + @call :log 5 + @call :log 0 "No build command for %generator%" + @exit /b 1 +) +@cd %build_path% +@call :iso8601 iso8601 +@set output=%temp%\build-%iso8601%.log +@call :log 7 +@call :log 2 "Build command: %build_command:"=%" +@%build_command% > "%output%" +@if errorlevel 1 ( + @call :log_append "%output%" + @call :log 5 + @call :log 0 "Failed to complete the build" + @exit /b 1 +) +@call :log_append "%output%" +@cd %script_folder% +@endlocal +@goto :eof + +:install - Installs the built files +@setlocal +@call :log 5 +@call :log 0 "Installing built files" +@call :cmake cmake_executable +@if errorlevel 1 ( + @call :log 5 + @call :log 0 "Need CMake to install the built files" + @exit /b 1 +) +@call :iso8601 iso8601 +@set output=%temp%\install-%iso8601%.log +@"%cmake_executable%" ^ + "-DCMAKE_INSTALL_PREFIX=%install_path%" ^ + -P "%build_path%/cmake_install.cmake" ^ + > "%output%" +@if errorlevel 1 ( + @call :log_append "%output%" + @call :log 5 + @call :log 0 "Failed to install the files" + @exit /b 1 +) +@call :log_append "%output%" +@endlocal +@goto :eof + +:script_source - Determines if the script was ran from the cli or explorer +:: %1 - The return variable [cli|explorer] +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + call :log 0 "Failed to enable extensions" + exit /b 1 +) +@call :log 3 "Attempting to detect the script source" +@echo "The invocation command was: '%cmdcmdline%'" >> %log_path% +@for /f "tokens=1-3,*" %%a in ("%cmdcmdline%") do @( + set cmd=%%~a + set arg1=%%~b + set arg2=%%~c + set rest=%%~d +) +@set quote=" +@if "!arg2:~0,1!" equ "!quote!" ( + if "!arg2:~-1!" neq "!quote!" ( + set "arg2=!arg2:~1!" + ) +) +@call :log 4 "cmd = %cmd%" +@call :log 4 "arg1 = %arg1%" +@call :log 4 "arg2 = %arg2%" +@call :log 4 "rest = %rest%" +@call :log 4 "src = %~f0" +@if /i "%arg2%" == "call" ( + set script_source=cli +) else ( + @if /i "%arg1%" == "/c" ( + set script_source=explorer + ) else ( + set script_source=cli + ) +) +@call :log 3 "The script was invoked from %script_source%" +@endlocal & set "%~1=%script_source%" +@goto :eof + +:architecture - Finds the system architecture +:: %1 - The return variable [x86|x86_64] +@setlocal +@call :log 3 "Determining the processor architecture" +@set "key=HKLM\System\CurrentControlSet\Control\Session Manager\Environment" +@set "var=PROCESSOR_ARCHITECTURE" +@for /f "skip=2 tokens=2,*" %%a in ('reg query "%key%" /v "%var%"') do @set "arch=%%b" +@if "%arch%" == "AMD64" set arch=x86_64 +@call :log 4 "arch = %arch%" +@endlocal & set "%~1=%arch%" +@goto :eof + +:md5 - Gets the MD5 checksum for a file +:: %1 - The hash +:: %2 - The file path +@setlocal +@set var=%~1 +@set file_path=%~2 +@if [%var%] == [] exit /b 1 +@if "%file_path%" == "" exit /b 1 +@if not exist "%file_path%" exit /b 1 +@for /f "skip=3 tokens=1,*" %%a in ('powershell Get-FileHash -Algorithm MD5 "'%file_path%'"') do @set hash=%%b +@if not defined hash ( + call :log 6 + call :log 0 "Failed to get MD5 hash for %file_path%" + exit /b 1 +) +@endlocal & set "%var%=%hash: =%" +@goto :eof + +:windows_version - Checks the windows version +:: %1 - The windows version +:: %2 - The major version number return variable +:: %3 - The minor version number return variable +:: %4 - The revision version number return variable +@setlocal +@call :log 3 "Retrieving the Windows version" +@for /f "tokens=2 delims=[]" %%x in ('ver') do @set win_ver=%%x +@set win_ver=%win_ver:Version =% +@set win_ver_major=%win_ver:~0,1% +@set win_ver_minor=%win_ver:~2,1% +@set win_ver_rev=%win_ver:~4% +@call :log 4 "win_ver = %win_ver%" +@endlocal & set "%~1=%win_ver%" ^ + & set "%~2=%win_ver_major%" ^ + & set "%~3=%win_ver_minor%" ^ + & set "%~4=%win_ver_rev%" +@goto :eof + +:find_in_path - Finds a program of file in the PATH +@setlocal +@set var=%~1 +@set file=%~2 +@if [%var%] == [] exit /b 1 +@if [%file%] == [] exit /b 1 +@call :log 3 "Searching PATH for %file%" +@for %%x in ("%file%") do @set "file_path=%%~f$PATH:x" +@if not defined file_path exit /b 1 +@endlocal & set "%var%=%file_path%" +@goto :eof + +:administrator_check - Checks for administrator priviledges +@setlocal +@call :log 2 "Checking for administrator priviledges" +@set "key=HKLM\Software\VCA\Tool Chain\Admin Check" +@reg add "%key%" /v Elevated /t REG_DWORD /d 1 /f > nul 2>&1 +@if errorlevel 1 exit /b 1 +@reg delete "%key%" /va /f > nul 2>&1 +@endlocal +@goto :eof + +:log_append - Appends another file into the current logging file +:: %1 - the file_path to the file to concatenate +@setlocal +@set "file_path=%~1" +@if [%file_path%] == [] exit /b 1 +@call :log 3 "Appending to log: %file_path%" +@call :iso8601 iso8601 +@set "temp_log=%temp%\append-%iso8601%.log" +@call :log 4 "Using temp file %temp_log%" +@type "%log_path%" "%file_path%" > "%temp_log%" 2>nul +@move /y "%temp_log%" "%log_path%" 1>nul +@del "%file_path%" 2>nul +@del "%temp_log%" 2>nul +@endlocal +@goto :eof + +:iso8601 - Returns the current time in ISO8601 format +:: %1 - the return variable +:: %2 - format [extended|basic*] +:: iso8601 - contains the resulting timestamp +@setlocal +@wmic Alias /? >NUL 2>&1 || @exit /b 1 +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "format=%~2" +@if "%format%" == "" set format=basic +@for /F "skip=1 tokens=1-6" %%g IN ('wmic Path Win32_UTCTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') do @( + @if "%%~l"=="" goto :iso8601_done + @set "yyyy=%%l" + @set "mm=00%%j" + @set "dd=00%%g" + @set "hour=00%%h" + @set "minute=00%%i" + @set "seconds=00%%k" +) +:iso8601_done +@set mm=%mm:~-2% +@set dd=%dd:~-2% +@set hour=%hour:~-2% +@set minute=%minute:~-2% +@set seconds=%seconds:~-2% +@if /i [%format%] == [extended] ( + set iso8601=%yyyy%-%mm%-%dd%T%hour%:%minute%:%seconds%Z +) else ( + if /i [%format%] == [basic] ( + set iso8601=%yyyy%%mm%%dd%T%hour%%minute%%seconds%Z + ) else ( + @exit /b 1 + ) +) +@set iso8601=%iso8601: =0% +@endlocal & set %var%=%iso8601% +@goto :eof + +:verbosity - Processes the verbosity parameter '/v[v...] +:: %1 - verbosity given on the command line +:: logging_level - set to the number of v's +@setlocal +@set logging_level=0 +@set verbosity=%~1 +:verbosity_loop +@set verbosity=%verbosity:~1% +@if not [%verbosity%] == [] @( + set /a "logging_level=logging_level+1" + goto verbosity_loop +) +@endlocal & set logging_level=%logging_level% +@goto :eof + +:log - Logs a message, depending on verbosity +:: %1 - level +:: [0-4] for CLI logging +:: [5-9] for GUI logging +:: %2 - message to print +@setlocal +@set "level=%~1" +@set "msg=%~2" +@if "%log_folder%" == "" ( + echo Logging was used to early in the script, log_folder isn't set yet + goto :eof +) +@if "%log_path%" == "" ( + echo Logging was used to early in the script, log_path isn't set yet + goto :eof +) +@if not exist "%log_folder%" mkdir "%log_folder%" +@if not exist "%log_path%" echo. 1>nul 2>"%log_path%" +@echo.%msg% >> "%log_path%" +@if %level% geq 5 ( + @if [%script_source%] == [explorer] ( + set /a "level=level-5" + ) else ( + @goto :eof + ) +) +@if "%logging_level%" == "" ( + echo Logging was used to early in the script, logging_level isn't set yet + goto :eof +) +@if %logging_level% geq %level% echo.%msg% 1>&2 +@endlocal +@goto :eof + + +:start_browser - Opens the default browser to a URL +:: %1 - the url to open +@setlocal +@set url=%~1 +@call :log 4 "Opening default browser: %url%" +@start %url% +@endlocal +@goto :eof + +:find_cmake - Finds cmake on the command line or in the registry +:: %1 - the cmake file path +@setlocal +@set var=%~1 +@if [%var%] == [] exit /b 1 +@call :log 6 +@call :log 6 "Finding CMake" +@call :log 6 "--------------" +@call :find_in_path cmake_executable cmake.exe +@if not errorlevel 1 goto found_cmake +@for /l %%i in (5,-1,0) do @( +@for /l %%j in (9,-1,0) do @( +@for /l %%k in (9,-1,0) do @( +@for %%l in (HKCU HKLM) do @( +@for %%m in (SOFTWARE SOFTWARE\Wow6432Node) do @( + @reg query "%%l\%%m\Kitware\CMake %%i.%%j.%%k" /ve > nul 2>nul + @if not errorlevel 1 ( + @for /f "skip=2 tokens=2,*" %%a in ('reg query "%%l\%%m\Kitware\CMake %%i.%%j.%%k" /ve') do @( + @if exist "%%b\bin\cmake.exe" ( + @set "cmake_executable=%%b\bin\cmake.exe" + goto found_cmake + ) + ) + ) +))))) +@call :log 5 +@call :log 0 "Failed to find cmake" +@exit /b 1 +:found_cmake +@endlocal & set "%var%=%cmake_executable%" +@goto :eof + +:cmake - Finds cmake and installs it if necessary +:: %1 - the cmake file path +@setlocal +@set var=%~1 +@if [%var%] == [] exit /b 1 +@call :log 6 +@call :log 6 "Checking for CMake" +@call :log 6 "------------------" +@call :find_cmake cmake_executable cmake.exe +@if not errorlevel 1 goto got_cmake +@set checksum=C00267A3D3D9619A7A2E8FA4F46D7698 +@set version=3.2.2 +@call :install_nsis cmake http://www.cmake.org/files/v%version:~0,3%/cmake-%version%-win32-x86.exe %checksum% +@if errorlevel 1 ( + call :log 5 + call :log 0 "Failed to install cmake" + @exit /b 1 +) +@call :find_cmake cmake_executable cmake.exe +@if not errorlevel 1 goto got_cmake +@call :log 5 +@call :log 0 "Failed to check for cmake" +@exit /b 1 +:got_cmake +@endlocal & set "%var%=%cmake_executable%" +@goto :eof + +:mingw - Finds MinGW, installing it if needed +:: %1 - the compiler path that should be added to PATH +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :log 5 + @call :log 0 "Failed to enable extensions" + @exit /b 1 +) +@set var=%~1 +@if [%var%] == [] exit /b 1 +@call :log 6 +@call :log 6 "Checking for MinGW" +@call :log 6 "------------------" +@call :find_in_path gcc_executable gcc.exe +@if not errorlevel 1 ( + @for %%a in ("%gcc_executable%") do @set "compiler_path=%%~dpa" + goto got_mingw +) +@call :log 7 +@call :log 2 "Downloading MinGW" +@if %logging_level% leq 1 set "logging=/q" +@if %logging_level% gtr 1 set "logging=/v" +@set output_path= +@for /f %%a in ('call + "%script_folder%\mingw.cmd" + %logging% + /arch "%arch%" + "%dependency_path%"' +) do @set "compiler_path=%%a\" +@if not defined compiler_path ( + @call :log_append "%output%" + @call :log 5 + @call :log 0 "Failed to download MinGW" + @exit /b 1 +) +:got_mingw +@call :log 5 +@call :log 0 "Found MinGW: %compiler_path%gcc.exe" +@endlocal & set "%var%=%compiler_path%" +@goto :eof + +:msbuild - Finds MSBuild +:: %1 - the path to MSBuild executable +@setlocal +@set var=%~1 +@if [%var%] == [] exit /b 1 +@call :find_in_path msbuild_executable msbuild.exe +@if not errorlevel 1 goto got_msbuild +@for /l %%i in (20,-1,4) do @( +@for /l %%j in (9,-1,0) do @( +@for %%k in (HKCU HKLM) do @( +@for %%l in (SOFTWARE SOFTWARE\Wow6432Node) do @( + @reg query "%%k\%%l\Microsoft\MSBuild\%%i.%%j" /v MSBuildOverrideTasksPath > nul 2>nul + @if not errorlevel 1 ( + @for /f "skip=2 tokens=2,*" %%a in ('reg query "%%k\%%l\Microsoft\MSBuild\%%i.%%j" /v MSBuildOverrideTasksPath') do @( + @if exist "%%bmsbuild.exe" ( + @set "msbuild_executable=%%bmsbuild.exe" + goto got_msbuild + ) + ) + ) +)))) +@call :log 5 +@call :log 0 "Failed to check for MSBuild" +@exit /b 1 +:got_msbuild +@endlocal & set "%var%=%msbuild_executable%" +@goto :eof + +:download - Downloads a file from the internet +:: %1 - the url of the file to download +:: %2 - the file to download to +:: %3 - the MD5 checksum of the file (optional) +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + call :print_usage "Failed to enable extensions" + exit /b 1 +) +@set url=%~1 +@set file_path=%~2 +@set checksum=%~3 +@for %%a in (%file_path%) do @set dir_path=%%~dpa +@for %%a in (%file_path%) do @set file_name=%%~nxa +@if [%url%] == [] exit /b 1 +@if [%file_path%] == [] exit /b 1 +@if [%dir_path%] == [] exit /b 1 +@if [%file_name%] == [] exit /b 1 +@if not exist "%dir_path%" mkdir "%dir_path%" +@call :log 1 "Downloading %url%" +@call :iso8601 iso8601 +@set temp_path=%temp%\download-%iso8601%-%file_name% +@call :log 3 "Using temp file %temp_path%" +@powershell Invoke-WebRequest "%url%" -OutFile %temp_path% +@if errorlevel 1 ( + call :log 0 "Failed to download %url%" + exit /b 1 +) +@if [%checksum%] neq [] ( + @call :log 4 "Checking %checksum% against %temp_path%" + @call :md5 hash "%temp_path%" + if "!hash!" neq "%checksum%" ( + call :log 0 "Failed to match checksum: %temp_path%" + call :log 0 "Hash : !hash!" + call :log 0 "Checksum: %checksum%" + exit /b 1 + ) else ( + call :log 3 "Checksum matched: %temp_path%" + call :log 3 "Hash : !hash!" + call :log 3 "Checksum: %checksum%" + ) +) +@call :log 4 "Renaming %temp_path% to %file_path%" +@move /y "%temp_path%" "%file_path%" 1>nul +@endlocal +@goto :eof + +:install_msi - Installs a dependency from an Microsoft Installer package (.msi) +:: %1 - [string] name of the project to install +:: %2 - The location of the .msi, a url must start with 'http://' or file_path +:: %3 - The checksum of the msi (optional) +@setlocal +@set name=%~1 +@set file_path=%~2 +@set checksum=%~3 +@set msi=%~nx2 +@set msi_path=%dependency_path%\%msi% +@if [%name%] == [] exit /b 1 +@if [%file_path%] == [] exit /b 1 +@if [%msi%] == [] exit /b 1 +@if [%msi_path%] == [] exit /b 1 +@for %%x in (msiexec.exe) do @set "msiexec_path=%%~f$PATH:x" +@if "msiexec_path" == "" ( + call :log 0 "Failed to find the Microsoft package installer (msiexec.exe)" + call :log 6 + call :log 0 "Please install it from the Microsoft Download center" + call :log 6 + choice /C YN /T 60 /D N /M "Would you like to go there now?" + if !errorlevel! equ 1 call :start_browser ^ + "http://search.microsoft.com/DownloadResults.aspx?q=Windows+Installer" + exit /b 1 +) +@call :log 6 +@call :log 1 "Installing the '%name%' dependency" +@call :log 6 "-------------------------------------" +@call :administrator_check +@if errorlevel 1 ( + call :log 0 "You must run %~nx0 in elevated mode to install '%name%'" + call :log 5 "Right-Click and select 'Run as Administrator' + call :log 0 "Install the dependency manually by running %file_path%" + @exit /b 740 +) +@if [%file_path:~0,4%] == [http] ( + if not exist "%msi_path%" ( + call :download "%file_path%" "%msi_path%" %checksum% + if errorlevel 1 ( + call :log 0 "Failed to download the %name% dependency" + exit /b 1 + ) + ) +) else ( + call :log 2 "Copying MSI %file_path% to %msi_path%" + call :log 7 + if not exist "%msi_path%" ( + xcopy /q /y /z "%file_path%" "%msi_path%" 1>nul + if errorlevel 1 ( + call :log 0 "Failed to copy the Microsoft Installer" + exit /b 1 + ) + ) +) +@call :log 1 "Running the %msi%" +@call :log 6 +@set msi_log=%temp%\msiexec-%timestamp%.log +@call :log 3 "Logging to: %msi_log%" +@msiexec /i "%msi_path%" /passive /log "%msi_log%" ALLUSERS=1 +@set msi_errorlevel=%errorlevel% +@call :log_append "%msi_log%" +@if %msi_errorlevel% equ 0 goto install_msi_success +@if %msi_errorlevel% equ 3010 goto install_msi_success_reboot +@if %msi_errorlevel% equ 1641 goto install_msi_success_reboot +@if %msi_errorlevel% equ 3015 goto install_msi_in_progress_reboot +@if %msi_errorlevel% equ 1615 goto install_msi_in_progress_reboot +@call :log 0 "Microsoft Installer failed: %msi_errorlevel%" +@call :log 0 "Install the dependency manually by running %msi_path%" +@exit /b 1 +:install_msi_in_progress_reboot +@call :log 0 "The installation requires a reboot to continue" +@call :log 5 +@call :reboot +@exit /b 1 +:install_msi_success_reboot +@call :log 3 "The installation requires a reboot to be fully functional" +@set reboot_required=1 +:install_msi_success +@call :log 2 "Successfully installed %name%" +@call :log 7 +@endlocal & set reboot_required=%reboot_required% +@goto :eof + +:install_nsis - Installs a dependency from an Nullsoft Installer package (.exe) +:: %1 - [string] name of the project to install +:: %2 - The location of the .exe, a url must start with 'http://' or file_path +:: %3 - The checksum of the exe (optional) +@setlocal +@set name=%~1 +@set file_path=%~2 +@set checksum=%~3 +@set exe=%~nx2 +@set exe_path=%dependency_path%\%exe% +@if [%name%] == [] exit /b 1 +@if [%file_path%] == [] exit /b 1 +@if [%exe%] == [] exit /b 1 +@if [%exe_path%] == [] exit /b 1 +@call :log 6 +@call :log 1 "Installing the '%name%' dependency" +@call :log 6 "-------------------------------------" +@call :administrator_check +@if errorlevel 1 ( + call :log 0 "You must run %~nx0 in elevated mode to install '%name%'" + call :log 5 "Right-Click and select 'Run as Administrator' + call :log 0 "Install the dependency manually by running %file_path%" + @exit /b 740 +) +@if [%file_path:~0,4%] == [http] ( + if not exist "%exe_path%" ( + call :download "%file_path%" "%exe_path%" %checksum% + if errorlevel 1 ( + call :log 0 "Failed to download the %name% dependency" + exit /b 1 + ) + ) +) else ( + call :log 2 "Copying installer %file_path% to %exe_path%" + call :log 7 + if not exist "%exe_path%" ( + xcopy /q /y /z "%file_path%" "%exe_path%" 1>nul + if errorlevel 1 ( + call :log 0 "Failed to copy the Nullsoft Installer" + exit /b 1 + ) + ) +) +@call :log 1 "Running the %exe%" +@call :log 6 +@"%exe_path%" /S +@set nsis_errorlevel=%errorlevel% +@if %nsis_errorlevel% equ 0 goto install_nsis_success +@if %nsis_errorlevel% equ 3010 goto install_nsis_success_reboot +@if %nsis_errorlevel% equ 1641 goto install_nsis_success_reboot +@if %nsis_errorlevel% equ 3015 goto install_nsis_in_progress_reboot +@if %nsis_errorlevel% equ 1615 goto install_nsis_in_progress_reboot +@call :log 0 "Nullsoft Installer failed: %nsis_errorlevel%" +@call :log 0 "Install the dependency manually by running %exe_path%" +@exit /b 1 +:install_nsis_in_progress_reboot +@call :log 0 "The installation requires a reboot to continue" +@call :log 5 +@call :reboot +@exit /b 1 +:install_nsis_success_reboot +@call :log 3 "The installation requires a reboot to be fully functional" +@set reboot_required=1 +:install_nsis_success +@call :log 2 "Successfully installed %name%" +@call :log 7 +@endlocal & set reboot_required=%reboot_required% +@goto :eof + +:reboot - Asks the user if they would like to reboot then stops the script +@setlocal +@call :log 6 "-------------------------------------------" +@choice /C YN /T 60 /D N /M "The %method% requires a reboot, reboot now?" +@set ret=%errorlevel% +@call :log 6 +@if %ret% equ 1 ( + @shutdown /r +) else ( + @call :log 0 "You will need to reboot to complete the %method%" + @call :log 5 +) +@endlocal +@goto :eof diff --git a/third_party/civetweb/mingw.cmd b/third_party/civetweb/mingw.cmd index 4b26215..5743733 100644 --- a/third_party/civetweb/mingw.cmd +++ b/third_party/civetweb/mingw.cmd @@ -1,884 +1,884 @@ -:: Make sure the extensions are enabled -@verify other 2>nul -@setlocal EnableExtensions EnableDelayedExpansion -@if errorlevel 1 ( - @call :print_usage "Failed to enable extensions" - @exit /b 1 -) - -::Change the code page to unicode -@chcp 65001 1>nul 2>nul -@if errorlevel 1 ( - @call :print_usage "Failed to change the code page to unicode" - @exit /b 1 -) - -:: Set up some global variables -@set "script_name=%~nx0" -@set "script_folder=%~dp0" -@set "script_folder=%script_folder:~0,-1%" -@set "dependency_path=%TEMP%\mingw-build-dependencies" - -:: Check the command line parameters -@set logging_level=1 -:options_loop -@if [%1] == [] goto :options_parsed -@set "arg=%~1" -@set one=%arg:~0,1% -@set two=%arg:~0,2% -@set three=%arg:~0,3% -@if /i [%arg%] == [/?] ( - @call :print_usage "Downloads a specific version of MinGW" - @exit /b 0 -) -@if /i [%arg%] == [/q] set quiet=true -@if /i [%two%] == [/v] @if /i not [%three%] == [/ve] @call :verbosity "!arg!" -@if /i [%arg%] == [/version] set "version=%~2" & shift -@if /i [%arg%] == [/arch] set "arch=%~2" & shift -@if /i [%arg%] == [/exceptions] set "exceptions=%~2" & shift -@if /i [%arg%] == [/threading] set "threading=%~2" & shift -@if /i [%arg%] == [/revision] set "revision=%~2" & shift -@if /i not [!one!] == [/] ( - if not defined output_path ( - set output_path=!arg! - ) else ( - @call :print_usage "Too many output locations: !output_path! !arg!" ^ - "There should only be one output location" - @exit /b 1 - ) -) -@shift -@goto :options_loop -:options_parsed -@if defined quiet set logging_level=0 -@if not defined output_path set "output_path=%script_folder%\mingw-builds" -@set "output_path=%output_path:/=\%" - -:: Set up the logging -@set "log_folder=%output_path%\logs" -@call :iso8601 timestamp -@set "log_path=%log_folder%\%timestamp%.log" -@set log_keep=10 - -:: Get default architecture -@if not defined arch @call :architecture arch - -:: Only keep a certain amount of logs -@set /a "log_keep=log_keep-1" -@if not exist %log_folder% @mkdir %log_folder% -@for /f "skip=%log_keep%" %%f in ('dir /b /o-D /tc %log_folder%') do @( - @call :log 4 "Removing old log file %log_folder%\%%f" - del %log_folder%\%%f -) - -:: Set up some more global variables -@call :windows_version win_ver win_ver_major win_ver_minor win_ver_rev -@call :script_source script_source -@if [%script_source%] == [explorer] ( - set /a "logging_level=logging_level+1" -) - -:: Execute the main function -@call :main "%arch%" "%version%" "%threading%" "%exceptions%" "%revision%" -@if errorlevel 1 ( - @call :log 0 "Failed to download MinGW" - @call :log 0 "View the log at %log_path%" - @exit /b 1 -) - -:: Stop the script if the user double clicked -@if [%script_source%] == [explorer] ( - pause -) - -@endlocal -@goto :eof - -:: -------------------------- Functions start here ---------------------------- - -:main - Main function that performs the download -:: %1 - Target architecture -:: %2 - Version of MinGW to get [optional] -:: %3 - Threading model [optional] -:: %4 - Exception model [optional] -:: %5 - Package revision [optional] -@setlocal -@call :log 6 -@call :log 2 "Welcome to the MinGW download script" -@call :log 6 "------------------------------------" -@call :log 6 -@call :log 2 "This script downloads a specific version of MinGW" -@set "arch=%~1" -@if "%arch%" == "" @exit /b 1 -@set "version=%~2" -@set "threading=%~3" -@set "exceptions=%~4" -@set "revision=%~5" -@call :log 3 "arch = %arch%" -@call :log 3 "version = %version%" -@call :log 3 "exceptions = %exceptions%" -@call :log 3 "threading = %threading%" -@call :log 3 "revision = %revision%" -@call :repository repo -@if errorlevel 1 ( - @call :log 0 "Failed to get the MinGW-builds repository information" - @exit /b 1 -) -@call :resolve slug url "%repo%" "%arch%" "%version%" "%threading%" "%exceptions%" "%revision%" -@if errorlevel 1 ( - @call :log 0 "Failed to resolve the correct URL of MinGW" - @exit /b 1 -) -@call :unpack compiler_path "%url%" "%output_path%\mingw\%slug%" -@if errorlevel 1 ( - @call :log 0 "Failed to unpack the MinGW archive" - @exit /b 1 -) -@rmdir /s /q "%dependency_path%" -@echo.%compiler_path% -@endlocal -@goto :eof - -:repository - Gets the MinGW-builds repository -:: %1 - The return variable for the repository file path -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :log 0 "Failed to enable extensions" - @exit /b 1 -) -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@call :log 7 -@call :log 2 "Getting MinGW repository information" -@set "url=http://downloads.sourceforge.net/project/mingw-w64/Toolchains targetting Win32/Personal Builds/mingw-builds/installer/repository.txt" -@call :log 6 -@call :log 1 "Downloading MinGW repository" -@set "file_path=%dependency_path%\mingw-repository.txt" -@call :download "%url%" "%file_path%" -@if errorlevel 1 ( - @call :log 0 "Failed to download the MinGW repository information" - @exit /b 1 -) -@set "repository_path=%dependency_path%\repository.txt" -@del "%repository_path%" 2>nul -@for /f "delims=| tokens=1-6,*" %%a in (%file_path%) do @( - @set "version=%%~a" - @set "version=!version: =!" - @set "arch=%%~b" - @set "arch=!arch: =!" - @set "threading=%%~c" - @set "threading=!threading: =!" - @set "exceptions=%%~d" - @set "exceptions=!exceptions: =!" - @set "revision=%%~e" - @set "revision=!revision: =!" - @set "revision=!revision:rev=!" - @set "url=%%~f" - @set "url=!url:%%20= !" - @for /l %%a in (1,1,32) do @if "!url:~-1!" == " " set url=!url:~0,-1! - @echo !arch!^|!version!^|!threading!^|!exceptions!^|!revision!^|!url!>> "%repository_path%" -) -@del "%file_path%" 2>nul -@endlocal & set "%var%=%repository_path%" -@goto :eof - -:resolve - Gets the MinGW-builds repository -:: %1 - The return variable for the MinGW slug -:: %2 - The return variable for the MinGW URL -:: %3 - The repository information to use -:: %4 - Target architecture -:: %5 - Version of MinGW to get [optional] -:: %6 - Threading model [optional] -:: %7 - Exception model [optional] -:: %8 - Package revision [optional] -@setlocal -@set "slug_var=%~1" -@if "%slug_var%" == "" @exit /b 1 -@set "url_var=%~2" -@if "%url_var%" == "" @exit /b 1 -@set "repository=%~3" -@if "%repository%" == "" @exit /b 1 -@set "arch=%~4" -@if "%arch%" == "" @exit /b 1 -@call :resolve_version version "%repository%" "%arch%" "%~5" -@if errorlevel 1 @exit /b 1 -@call :resolve_threading threading "%repository%" "%arch%" "%version%" "%~6" -@if errorlevel 1 @exit /b 1 -@call :resolve_exceptions exceptions "%repository%" "%arch%" "%version%" "%threading%" "%~7" -@if errorlevel 1 @exit /b 1 -@call :resolve_revision revision "%repository%" "%arch%" "%version%" "%threading%" "%exceptions%" "%~8" -@if errorlevel 1 @exit /b 1 -@call :log 3 "Finding URL" -@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( - @if "%arch%" == "%%a" ( - @if "%version%" == "%%b" ( - @if "%threading%" == "%%c" ( - @if "%exceptions%" == "%%d" ( - @if "%revision%" == "%%e" ( - @set "url=%%f" -) ) ) ) ) ) -@if "%url%" == "" ( - @call :log 0 "Failed to resolve URL" - @exit /b 1 -) -@set slug=gcc-%version%-%arch%-%threading%-%exceptions%-rev%revision% -@call :log 2 "Resolved slug: %slug%" -@call :log 2 "Resolved url: %url%" -@endlocal & set "%slug_var%=%slug%" & set "%url_var%=%url%" -@goto :eof - -:unpack - Unpacks the MinGW archive -:: %1 - The return variable name for the compiler path -:: %2 - The filepath or URL of the archive -:: %3 - The folder to unpack to -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :log 0 "Failed to enable extensions" - @exit /b 1 -) -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "archive_path=%~2" -@if "%archive_path%" == "" @exit /b 1 -@set "folder_path=%~3" -@if "%folder_path%" == "" @exit /b 1 -@set "compiler_path=%folder_path%\bin" -@if exist "%compiler_path%" goto :unpack_done -@call :log 7 -@call :log 2 "Unpacking MinGW archive" -@set "http=%archive_path:~0,4%" -@if "%http%" == "http" ( - @set "url=%archive_path%" - @for /f %%a in ("!url: =-!") do @set "file_name=%%~na" - @for /f %%a in ("!url: =-!") do @set "file_ext=%%~xa" - @set "archive_path=%dependency_path%\!file_name!!file_ext!" - @if not exist "!archive_path!" ( - @call :log 6 - @call :log 1 "Downloading MinGW archive" - @call :download "!url!" "!archive_path!" - @if errorlevel 1 ( - @del "!archive_path!" 2>nul - @call :log 0 "Failed to download: !file_name!!file_ext!" - @exit /b 1 - ) - ) -) -@if not exist "%archive_path%" ( - @call :log 0 "The archive did not exist to unpack: %archive_path%" - @exit /b 1 -) -@for /f %%a in ("%archive_path: =-%") do @set "file_name=%%~na" -@for /f %%a in ("%archive_path: =-%") do @set "file_ext=%%~xa" -@call :log 6 -@call :log 1 "Unpacking MinGW %file_name%%file_ext%" -@call :find_sevenzip sevenzip_executable -@if errorlevel 1 ( - @call :log 0 "Need 7zip to unpack the MinGW archive" - @exit /b 1 -) -@call :iso8601 iso8601 -@for /f %%a in ("%folder_path%") do @set "tmp_path=%%~dpatmp-%iso8601%" -@"%sevenzip_executable%" x -y "-o%tmp_path%" "%archive_path%" > nul -@if errorlevel 1 ( - @rmdir /s /q "%folder_path%" - @call :log 0 "Failed to unpack the MinGW archive" - @exit /b 1 -) -@set "expected_path=%tmp_path%\mingw64" -@if not exist "%expected_path%" ( - @set "expected_path=%tmp_path%\mingw32" -) -@move /y "%expected_path%" "%folder_path%" > nul -@if errorlevel 1 ( - @rmdir /s /q "%tmp_path%" 2>nul - @call :log 0 "Failed to move MinGW folder" - @call :log 0 "%expected_path%" - @call :log 0 "%folder_path%" - @exit /b 1 -) -@rmdir /s /q %tmp_path% -@set "compiler_path=%folder_path%\bin" -:unpack_done -@if not exist "%compiler_path%\gcc.exe" ( - @call :log 0 "Failed to find gcc: %compiler_path%" - @exit /b 1 -) -@endlocal & set "%var%=%compiler_path%" -@goto :eof - -:find_sevenzip - Finds (or downloads) the 7zip executable -:: %1 - The return variable for the 7zip executable path -@setlocal -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@call :log 2 "Finding 7zip" -@call :find_in_path sevenzip_executable 7z.exe -@if not errorlevel 1 goto :find_sevenzip_done -@call :find_in_path sevenzip_executable 7za.exe -@if not errorlevel 1 goto :find_sevenzip_done -@set checksum=2FAC454A90AE96021F4FFC607D4C00F8 -@set "url=http://7-zip.org/a/7za920.zip" -@for /f %%a in ("%url: =-%") do @set "file_name=%%~na" -@for /f %%a in ("%url: =-%") do @set "file_ext=%%~xa" -@set "archive_path=%dependency_path%\%file_name%%file_ext%" -@if not exist "%archive_path%" ( - @call :log 6 - @call :log 1 "Downloading 7zip archive" - @call :download "%url%" "%archive_path%" %checksum% - @if errorlevel 1 ( - @del "%archive_path%" 2>nul - @call :log 0 "Failed to download: %file_name%%file_ext%" - @exit /b 1 - ) -) -@set "sevenzip_path=%dependency_path%\sevenzip" -@if not exist "%sevenzip_path%" ( - @call :unzip "%archive_path%" "%sevenzip_path%" - @if errorlevel 1 ( - @call :log 0 "Failed to unzip the7zip archive" - @exit /b 1 - ) -) -@set "sevenzip_executable=%sevenzip_path%\7za.exe" -@if not exist "%sevenzip_executable%" ( - @call :log 0 "Failed to find unpacked 7zip: %sevenzip_executable%" - @exit /b 1 -) -:find_sevenzip_done -@call :log 2 "Found 7zip: %sevenzip_executable%" -@endlocal & set "%var%=%sevenzip_executable%" -@goto :eof - -:unzip - Unzips a .zip archive -:: %1 - The archive to unzip -:: %2 - The location to unzip to -@setlocal -@set "archive_path=%~1" -@if "%archive_path%" == "" @exit /b 1 -@set "folder_path=%~2" -@if "%folder_path%" == "" @exit /b 1 -@for /f %%a in ("%archive_path: =-%") do @set "file_name=%%~na" -@for /f %%a in ("%archive_path: =-%") do @set "file_ext=%%~xa" -@call :log 2 "Unzipping: %file_name%%file_ext%" -@call :iso8601 iso8601 -@set "log_path=%temp%\unzip-%iso8601%-%file_name%.log" -@powershell ^ - Add-Type -assembly "system.io.compression.filesystem"; ^ - [io.compression.zipfile]::ExtractToDirectory(^ - '%archive_path%', '%folder_path%') 2>"%log_path%" -@if errorlevel 1 ( - @call :log 0 "Failed to unzip: %file_name%%file_ext%" - @call :log_append "%log_path%" - @exit /b 1 -) -@endlocal -@goto :eof - -:resolve_version - Gets the version of the MinGW compiler -:: %1 - The return variable for the version -:: %2 - The repository information to use -:: %3 - The architecture of the compiler -:: %4 - Version of MinGW to get [optional] -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :log 0 "Failed to enable extensions" - @exit /b 1 -) -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "repository=%~2" -@if "%repository%" == "" @exit /b 1 -@set "arch=%~3" -@if "%arch%" == "" @exit /b 1 -@set "version=%~4" -@if not "%version%" == "" goto :resolve_version_done -:: Find the latest version -@call :log 3 "Finding latest version" -@set version=0.0.0 -@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( - @if "%arch%" == "%%a" ( - @call :version_compare result "%version%" "%%b" - @if errorlevel 1 ( - @call :log 0 "Failed to compare versions: %version% %%a" - @exit /b 1 - ) - @if !result! lss 0 set version=%%b - ) -) -:resolve_version_done -@if "%version%" == "" ( - @call :log 0 "Failed to resolve latest version number" - @exit /b 1 -) -@call :log 2 "Resolved version: %version%" -@endlocal & set "%var%=%version%" -@goto :eof - -:resolve_threading - Gets the threading model of the MinGW compiler -:: %1 - The return variable for the threading model -:: %2 - The repository information to use -:: %3 - The architecture of the compiler -:: %4 - The version of the compiler -:: %5 - threading model of MinGW to use [optional] -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :log 0 "Failed to enable extensions" - @exit /b 1 -) -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "repository=%~2" -@if "%repository%" == "" @exit /b 1 -@set "arch=%~3" -@if "%arch%" == "" @exit /b 1 -@set "version=%~4" -@if "%version%" == "" @exit /b 1 -@set "threading=%~5" -@if not "%threading%" == "" goto :resolve_threading_done -@call :log 3 "Finding best threading model" -@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( - @if "%arch%" == "%%a" ( - @if "%version%" == "%%b" ( - @if not defined threading ( - @set "threading=%%c" - ) - @if "%%c" == "posix" ( - @set "threading=%%c" -) ) ) ) -:resolve_threading_done -@if "%threading%" == "" ( - @call :log 0 "Failed to resolve the best threading model" - @exit /b 1 -) -@call :log 2 "Resolved threading model: %threading%" -@endlocal & set "%var%=%threading%" -@goto :eof - -:resolve_exceptions - Gets the exception model of the MinGW compiler -:: %1 - The return variable for the exception model -:: %2 - The repository information to use -:: %3 - The architecture of the compiler -:: %4 - The version of the compiler -:: %4 - The threading model of the compiler -:: %5 - exception model of MinGW to use [optional] -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :log 0 "Failed to enable extensions" - @exit /b 1 -) -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "repository=%~2" -@if "%repository%" == "" @exit /b 1 -@set "arch=%~3" -@if "%arch%" == "" @exit /b 1 -@set "version=%~4" -@if "%version%" == "" @exit /b 1 -@set "threading=%~5" -@if "%threading%" == "" @exit /b 1 -@set "exceptions=%~6" -@if not "%exceptions%" == "" goto :resolve_exceptions_done -@call :log 3 "Finding best exception model" -@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( - @if "%arch%" == "%%a" ( - @if "%version%" == "%%b" ( - @if "%threading%" == "%%c" ( - @if not defined exceptions ( - @set "exceptions=%%d" - ) - @if "%%d" == "dwarf" ( - @set "exceptions=%%d" - ) - @if "%%d" == "seh" ( - @set "exceptions=%%d" -) ) ) ) ) -:resolve_exceptions_done -@if "%exceptions%" == "" ( - @call :log 0 "Failed to resolve the best exception model" - @exit /b 1 -) -@call :log 2 "Resolved exception model: %exceptions%" -@endlocal & set "%var%=%exceptions%" -@goto :eof - -:resolve_revision - Gets the revision of the MinGW compiler -:: %1 - The return variable for the revision -:: %2 - The repository information to use -:: %3 - The architecture of the compiler -:: %4 - The version of the compiler -:: %4 - The threading model of the compiler -:: %4 - The exception model of the compiler -:: %5 - revision of the MinGW package to use [optional] -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :log 0 "Failed to enable extensions" - @exit /b 1 -) -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "repository=%~2" -@if "%repository%" == "" @exit /b 1 -@set "arch=%~3" -@if "%arch%" == "" @exit /b 1 -@set "version=%~4" -@if "%version%" == "" @exit /b 1 -@set "threading=%~5" -@if "%threading%" == "" @exit /b 1 -@set "exceptions=%~6" -@if "%exceptions%" == "" @exit /b 1 -@set "revision=%~7" -@if not "%revision%" == "" goto :resolve_revision_done -@call :log 3 "Finding latest revision" -@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( - @if "%arch%" == "%%a" ( - @if "%version%" == "%%b" ( - @if "%threading%" == "%%c" ( - @if "%exceptions%" == "%%d" ( - @if "%%e" gtr "%revision%" ( - @set "revision=%%e" -) ) ) ) ) ) -:resolve_revision_done -@if "%revision%" == "" ( - @call :log 0 "Failed to resolve latest revision" - @exit /b 1 -) -@call :log 2 "Resolved revision: %revision%" -@endlocal & set "%var%=%revision%" -@goto :eof - -:version_compare - Compares two semantic version numbers -:: %1 - The return variable: -:: - < 0 : if %2 < %3 -:: - 0 : if %2 == %3 -:: - > 0 : if %2 > %3 -:: %2 - The first version to compare -:: %3 - The second version to compare -@setlocal -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "lhs=%~2" -@if "%lhs%" == "" @exit /b 1 -@set "rhs=%~3" -@if "%lhs%" == "" @exit /b 1 -@set result=0 -@for /f "delims=. tokens=1-6" %%a in ("%lhs%.%rhs%") do @( - @if %%a lss %%d ( - set result=-1 - goto :version_compare_done - ) else ( - @if %%a gtr %%d ( - set result=1 - goto :version_compare_done - ) else ( - @if %%b lss %%e ( - set result=-1 - goto :version_compare_done - ) else ( - @if %%b gtr %%e ( - set result=1 - goto :version_compare_done - ) else ( - @if %%c lss %%f ( - set result=-1 - goto :version_compare_done - ) else ( - @if %%c gtr %%f ( - set result=1 - goto :version_compare_done - ) - ) - ) - ) - ) - ) -) -:version_compare_done -@endlocal & set "%var%=%result%" -@goto :eof - -:print_usage - Prints the usage of the script -:: %* - message to print, each argument on it's own line -@setlocal -@for %%a in (%*) do @echo.%%~a -@echo. -@echo.build [/?][/v[v...]^|/q][/version][/arch a][/threading t] -@echo. [/exceptions e][/revision r] location -@echo. -@echo. /version v The version of MinGW to download -@echo. /arch a The target architecture [i686^|x86_64] -@echo. /threading t -@echo. Threading model to use [posix^|win32] -@echo. /exceptions e -@echo. Exception model to use [sjlj^|seh^|dwarf] -@echo. /revision e Revision of the release to use -@echo. /v Sets the output to be more verbose -@echo. /v[v...] Extra verbosity, /vv, /vvv, etc -@echo. /q Quiets the output -@echo. /? Shows this usage message -@echo. -@endlocal -@goto :eof - -:script_source - Determines if the script was ran from the cli or explorer -:: %1 - The return variable [cli|explorer] -@verify other 2>nul -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :log 0 "Failed to enable extensions" - @exit /b 1 -) -@call :log 3 "Attempting to detect the script source" -@echo "The invocation command was: '%cmdcmdline%'" >> %log_path% -@for /f "tokens=1-3,*" %%a in ("%cmdcmdline%") do @( - set cmd=%%~a - set arg1=%%~b - set arg2=%%~c - set rest=%%~d -) -@set quote=" -@if "!arg2:~0,1!" equ "!quote!" ( - if "!arg2:~-1!" neq "!quote!" ( - set "arg2=!arg2:~1!" - ) -) -@call :log 4 "cmd = %cmd%" -@call :log 4 "arg1 = %arg1%" -@call :log 4 "arg2 = %arg2%" -@call :log 4 "rest = %rest%" -@call :log 4 "src = %~f0" -@if /i "%arg2%" == "call" ( - set script_source=cli -) else ( - @if /i "%arg1%" == "/c" ( - set script_source=explorer - ) else ( - set script_source=cli - ) -) -@call :log 3 "The script was invoked from %script_source%" -@endlocal & set "%~1=%script_source%" -@goto :eof - -:architecture - Finds the system architecture -:: %1 - The return variable [i686|x86_64] -@setlocal -@call :log 3 "Determining the processor architecture" -@set "key=HKLM\System\CurrentControlSet\Control\Session Manager\Environment" -@set "var=PROCESSOR_ARCHITECTURE" -@for /f "skip=2 tokens=2,*" %%a in ('reg query "%key%" /v "%var%"') do @set "arch=%%b" -@if "%arch%" == "AMD64" set arch=x86_64 -@if "%arch%" == "x64" set arch=i686 -@call :log 4 "arch = %arch%" -@endlocal & set "%~1=%arch%" -@goto :eof - -:md5 - Gets the MD5 checksum for a file -:: %1 - The hash -:: %2 - The file path -@setlocal -@set "var=%~1" -@set "file_path=%~2" -@if "%var%" == "" @exit /b 1 -@if "%file_path%" == "" @exit /b 1 -@if not exist "%file_path%" @exit /b 1 -@for /f "skip=3 tokens=1,*" %%a in ('powershell Get-FileHash -Algorithm MD5 "'%file_path%'"') do @set hash=%%b -@if not defined hash ( - @call :log 6 - @call :log 0 "Failed to get MD5 hash for %file_path%" - @exit /b 1 -) -@endlocal & set "%var%=%hash: =%" -@goto :eof - -:windows_version - Checks the windows version -:: %1 - The windows version -:: %2 - The major version number return variable -:: %3 - The minor version number return variable -:: %4 - The revision version number return variable -@setlocal -@call :log 3 "Retrieving the Windows version" -@for /f "tokens=2 delims=[]" %%x in ('ver') do @set win_ver=%%x -@set win_ver=%win_ver:Version =% -@set win_ver_major=%win_ver:~0,1% -@set win_ver_minor=%win_ver:~2,1% -@set win_ver_rev=%win_ver:~4% -@call :log 4 "win_ver = %win_ver%" -@endlocal & set "%~1=%win_ver%" ^ - & set "%~2=%win_ver_major%" ^ - & set "%~3=%win_ver_minor%" ^ - & set "%~4=%win_ver_rev%" -@goto :eof - -:find_in_path - Finds a program of file in the PATH -:: %1 - return variable of the file path -@setlocal -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "file=%~2" -@if "%file%" == "" @exit /b 1 -@call :log 3 "Searching PATH for %file%" -@for %%x in ("%file%") do @set "file_path=%%~f$PATH:x" -@if not defined file_path @exit /b 1 -@endlocal & set "%var%=%file_path%" -@goto :eof - -:log_append - Appends another file into the current logging file -:: %1 - the file_path to the file to concatenate -@setlocal -@set "file_path=%~1" -@if "%file_path%" == "" @exit /b 1 -@call :log 3 "Appending to log: %file_path%" -@call :iso8601 iso8601 -@set temp_log=%temp%\append-%iso8601%.log -@call :log 4 "Using temp file %temp_log%" -@type "%log_path%" "%file_path%" > "%temp_log%" 2>nul -@move /y "%temp_log%" "%log_path%" 1>nul -@del "%file_path% 2>nul -@del "%temp_log% 2>nul -@endlocal -@goto :eof - -:iso8601 - Returns the current time in ISO8601 format -:: %1 - the return variable -:: %2 - format [extended|basic*] -:: iso8601 - contains the resulting timestamp -@setlocal -@wmic Alias /? >NUL 2>&1 || @exit /b 1 -@set "var=%~1" -@if "%var%" == "" @exit /b 1 -@set "format=%~2" -@if "%format%" == "" set format=basic -@for /F "skip=1 tokens=1-6" %%g IN ('wmic Path Win32_UTCTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') do @( - @if "%%~l"=="" goto :iso8601_done - @set "yyyy=%%l" - @set "mm=00%%j" - @set "dd=00%%g" - @set "hour=00%%h" - @set "minute=00%%i" - @set "seconds=00%%k" -) -:iso8601_done -@set mm=%mm:~-2% -@set dd=%dd:~-2% -@set hour=%hour:~-2% -@set minute=%minute:~-2% -@set seconds=%seconds:~-2% -@if /i [%format%] == [extended] ( - set iso8601=%yyyy%-%mm%-%dd%T%hour%:%minute%:%seconds%Z -) else ( - if /i [%format%] == [basic] ( - set iso8601=%yyyy%%mm%%dd%T%hour%%minute%%seconds%Z - ) else ( - @exit /b 1 - ) -) -@set iso8601=%iso8601: =0% -@endlocal & set %var%=%iso8601% -@goto :eof - -:verbosity - Processes the verbosity parameter '/v[v...] -:: %1 - verbosity given on the command line -:: logging_level - set to the number of v's -@setlocal -@set logging_level=0 -@set verbosity=%~1 -:verbosity_loop -@set verbosity=%verbosity:~1% -@if not [%verbosity%] == [] @( - set /a "logging_level=logging_level+1" - goto verbosity_loop -) -@endlocal & set logging_level=%logging_level% -@goto :eof - -:log - Logs a message, depending on verbosity -:: %1 - level -:: [0-4] for CLI logging -:: [5-9] for GUI logging -:: %2 - message to print -@setlocal -@set "level=%~1" -@set "msg=%~2" -@if "%log_folder%" == "" ( - echo Logging was used to early in the script, log_folder isn't set yet - goto :eof -) -@if "%log_path%" == "" ( - echo Logging was used to early in the script, log_path isn't set yet - goto :eof -) -@if not exist "%log_folder%" mkdir "%log_folder%" -@if not exist "%log_path%" echo. 1>nul 2>"%log_path%" -@echo.%msg% >> "%log_path%" -@if %level% geq 5 ( - @if [%script_source%] == [explorer] ( - set /a "level=level-5" - ) else ( - @goto :eof - ) -) -@if "%logging_level%" == "" ( - echo Logging was used to early in the script, logging_level isn't set yet - goto :eof -) -@if %logging_level% geq %level% echo.%msg% 1>&2 -@endlocal -@goto :eof - -:download - Downloads a file from the internet -:: %1 - the url of the file to download -:: %2 - the file to download to -:: %3 - the MD5 checksum of the file (optional) -@setlocal EnableDelayedExpansion -@if errorlevel 1 ( - @call :print_usage "Failed to enable extensions" - @exit /b 1 -) -@set "url=%~1" -@set "file_path=%~2" -@set "checksum=%~3" -@for %%a in (%file_path%) do @set dir_path=%%~dpa -@for %%a in (%file_path%) do @set file_name=%%~nxa -@if "%url%" == "" @exit /b 1 -@if "%file_path%" == "" @exit /b 1 -@if "%dir_path%" == "" @exit /b 1 -@if "%file_name%" == "" @exit /b 1 -@if not exist "%dir_path%" mkdir "%dir_path%" -@call :log 2 "Downloading %url%" -@call :iso8601 iso8601 -@set "temp_path=%temp%\download-%iso8601%-%file_name%" -@set "log_path=%temp%\download-%iso8601%-log-%file_name%" -@call :log 4 "Using temp file %temp_path%" -@powershell Invoke-WebRequest "'%url%'" ^ - -OutFile "'%temp_path%'" ^ - -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::IE ^ - 1>nul 2>"%log_path%" -@if errorlevel 1 ( - @call :log 0 "Failed to download %url%" - @call :log_append "%log_path%" - @exit /b 1 -) -@if [%checksum%] neq [] ( - @call :log 4 "Checking %checksum% against %temp_path%" - @call :md5 hash "%temp_path%" - if "!hash!" neq "%checksum%" ( - @call :log 0 "Failed to match checksum: %temp_path%" - @call :log 0 "Hash : !hash!" - @call :log 0 "Checksum: %checksum%" - @exit /b 1 - ) else ( - @call :log 3 "Checksum matched: %temp_path%" - @call :log 3 "Hash : !hash!" - @call :log 3 "Checksum: %checksum%" - ) -) -@call :log 4 "Renaming %temp_path% to %file_path%" -@move /y "%temp_path%" "%file_path%" 1>nul -@endlocal -@goto :eof +:: Make sure the extensions are enabled +@verify other 2>nul +@setlocal EnableExtensions EnableDelayedExpansion +@if errorlevel 1 ( + @call :print_usage "Failed to enable extensions" + @exit /b 1 +) + +::Change the code page to unicode +@chcp 65001 1>nul 2>nul +@if errorlevel 1 ( + @call :print_usage "Failed to change the code page to unicode" + @exit /b 1 +) + +:: Set up some global variables +@set "script_name=%~nx0" +@set "script_folder=%~dp0" +@set "script_folder=%script_folder:~0,-1%" +@set "dependency_path=%TEMP%\mingw-build-dependencies" + +:: Check the command line parameters +@set logging_level=1 +:options_loop +@if [%1] == [] goto :options_parsed +@set "arg=%~1" +@set one=%arg:~0,1% +@set two=%arg:~0,2% +@set three=%arg:~0,3% +@if /i [%arg%] == [/?] ( + @call :print_usage "Downloads a specific version of MinGW" + @exit /b 0 +) +@if /i [%arg%] == [/q] set quiet=true +@if /i [%two%] == [/v] @if /i not [%three%] == [/ve] @call :verbosity "!arg!" +@if /i [%arg%] == [/version] set "version=%~2" & shift +@if /i [%arg%] == [/arch] set "arch=%~2" & shift +@if /i [%arg%] == [/exceptions] set "exceptions=%~2" & shift +@if /i [%arg%] == [/threading] set "threading=%~2" & shift +@if /i [%arg%] == [/revision] set "revision=%~2" & shift +@if /i not [!one!] == [/] ( + if not defined output_path ( + set output_path=!arg! + ) else ( + @call :print_usage "Too many output locations: !output_path! !arg!" ^ + "There should only be one output location" + @exit /b 1 + ) +) +@shift +@goto :options_loop +:options_parsed +@if defined quiet set logging_level=0 +@if not defined output_path set "output_path=%script_folder%\mingw-builds" +@set "output_path=%output_path:/=\%" + +:: Set up the logging +@set "log_folder=%output_path%\logs" +@call :iso8601 timestamp +@set "log_path=%log_folder%\%timestamp%.log" +@set log_keep=10 + +:: Get default architecture +@if not defined arch @call :architecture arch + +:: Only keep a certain amount of logs +@set /a "log_keep=log_keep-1" +@if not exist %log_folder% @mkdir %log_folder% +@for /f "skip=%log_keep%" %%f in ('dir /b /o-D /tc %log_folder%') do @( + @call :log 4 "Removing old log file %log_folder%\%%f" + del %log_folder%\%%f +) + +:: Set up some more global variables +@call :windows_version win_ver win_ver_major win_ver_minor win_ver_rev +@call :script_source script_source +@if [%script_source%] == [explorer] ( + set /a "logging_level=logging_level+1" +) + +:: Execute the main function +@call :main "%arch%" "%version%" "%threading%" "%exceptions%" "%revision%" +@if errorlevel 1 ( + @call :log 0 "Failed to download MinGW" + @call :log 0 "View the log at %log_path%" + @exit /b 1 +) + +:: Stop the script if the user double clicked +@if [%script_source%] == [explorer] ( + pause +) + +@endlocal +@goto :eof + +:: -------------------------- Functions start here ---------------------------- + +:main - Main function that performs the download +:: %1 - Target architecture +:: %2 - Version of MinGW to get [optional] +:: %3 - Threading model [optional] +:: %4 - Exception model [optional] +:: %5 - Package revision [optional] +@setlocal +@call :log 6 +@call :log 2 "Welcome to the MinGW download script" +@call :log 6 "------------------------------------" +@call :log 6 +@call :log 2 "This script downloads a specific version of MinGW" +@set "arch=%~1" +@if "%arch%" == "" @exit /b 1 +@set "version=%~2" +@set "threading=%~3" +@set "exceptions=%~4" +@set "revision=%~5" +@call :log 3 "arch = %arch%" +@call :log 3 "version = %version%" +@call :log 3 "exceptions = %exceptions%" +@call :log 3 "threading = %threading%" +@call :log 3 "revision = %revision%" +@call :repository repo +@if errorlevel 1 ( + @call :log 0 "Failed to get the MinGW-builds repository information" + @exit /b 1 +) +@call :resolve slug url "%repo%" "%arch%" "%version%" "%threading%" "%exceptions%" "%revision%" +@if errorlevel 1 ( + @call :log 0 "Failed to resolve the correct URL of MinGW" + @exit /b 1 +) +@call :unpack compiler_path "%url%" "%output_path%\mingw\%slug%" +@if errorlevel 1 ( + @call :log 0 "Failed to unpack the MinGW archive" + @exit /b 1 +) +@rmdir /s /q "%dependency_path%" +@echo.%compiler_path% +@endlocal +@goto :eof + +:repository - Gets the MinGW-builds repository +:: %1 - The return variable for the repository file path +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :log 0 "Failed to enable extensions" + @exit /b 1 +) +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@call :log 7 +@call :log 2 "Getting MinGW repository information" +@set "url=http://downloads.sourceforge.net/project/mingw-w64/Toolchains targetting Win32/Personal Builds/mingw-builds/installer/repository.txt" +@call :log 6 +@call :log 1 "Downloading MinGW repository" +@set "file_path=%dependency_path%\mingw-repository.txt" +@call :download "%url%" "%file_path%" +@if errorlevel 1 ( + @call :log 0 "Failed to download the MinGW repository information" + @exit /b 1 +) +@set "repository_path=%dependency_path%\repository.txt" +@del "%repository_path%" 2>nul +@for /f "delims=| tokens=1-6,*" %%a in (%file_path%) do @( + @set "version=%%~a" + @set "version=!version: =!" + @set "arch=%%~b" + @set "arch=!arch: =!" + @set "threading=%%~c" + @set "threading=!threading: =!" + @set "exceptions=%%~d" + @set "exceptions=!exceptions: =!" + @set "revision=%%~e" + @set "revision=!revision: =!" + @set "revision=!revision:rev=!" + @set "url=%%~f" + @set "url=!url:%%20= !" + @for /l %%a in (1,1,32) do @if "!url:~-1!" == " " set url=!url:~0,-1! + @echo !arch!^|!version!^|!threading!^|!exceptions!^|!revision!^|!url!>> "%repository_path%" +) +@del "%file_path%" 2>nul +@endlocal & set "%var%=%repository_path%" +@goto :eof + +:resolve - Gets the MinGW-builds repository +:: %1 - The return variable for the MinGW slug +:: %2 - The return variable for the MinGW URL +:: %3 - The repository information to use +:: %4 - Target architecture +:: %5 - Version of MinGW to get [optional] +:: %6 - Threading model [optional] +:: %7 - Exception model [optional] +:: %8 - Package revision [optional] +@setlocal +@set "slug_var=%~1" +@if "%slug_var%" == "" @exit /b 1 +@set "url_var=%~2" +@if "%url_var%" == "" @exit /b 1 +@set "repository=%~3" +@if "%repository%" == "" @exit /b 1 +@set "arch=%~4" +@if "%arch%" == "" @exit /b 1 +@call :resolve_version version "%repository%" "%arch%" "%~5" +@if errorlevel 1 @exit /b 1 +@call :resolve_threading threading "%repository%" "%arch%" "%version%" "%~6" +@if errorlevel 1 @exit /b 1 +@call :resolve_exceptions exceptions "%repository%" "%arch%" "%version%" "%threading%" "%~7" +@if errorlevel 1 @exit /b 1 +@call :resolve_revision revision "%repository%" "%arch%" "%version%" "%threading%" "%exceptions%" "%~8" +@if errorlevel 1 @exit /b 1 +@call :log 3 "Finding URL" +@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( + @if "%arch%" == "%%a" ( + @if "%version%" == "%%b" ( + @if "%threading%" == "%%c" ( + @if "%exceptions%" == "%%d" ( + @if "%revision%" == "%%e" ( + @set "url=%%f" +) ) ) ) ) ) +@if "%url%" == "" ( + @call :log 0 "Failed to resolve URL" + @exit /b 1 +) +@set slug=gcc-%version%-%arch%-%threading%-%exceptions%-rev%revision% +@call :log 2 "Resolved slug: %slug%" +@call :log 2 "Resolved url: %url%" +@endlocal & set "%slug_var%=%slug%" & set "%url_var%=%url%" +@goto :eof + +:unpack - Unpacks the MinGW archive +:: %1 - The return variable name for the compiler path +:: %2 - The filepath or URL of the archive +:: %3 - The folder to unpack to +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :log 0 "Failed to enable extensions" + @exit /b 1 +) +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "archive_path=%~2" +@if "%archive_path%" == "" @exit /b 1 +@set "folder_path=%~3" +@if "%folder_path%" == "" @exit /b 1 +@set "compiler_path=%folder_path%\bin" +@if exist "%compiler_path%" goto :unpack_done +@call :log 7 +@call :log 2 "Unpacking MinGW archive" +@set "http=%archive_path:~0,4%" +@if "%http%" == "http" ( + @set "url=%archive_path%" + @for /f %%a in ("!url: =-!") do @set "file_name=%%~na" + @for /f %%a in ("!url: =-!") do @set "file_ext=%%~xa" + @set "archive_path=%dependency_path%\!file_name!!file_ext!" + @if not exist "!archive_path!" ( + @call :log 6 + @call :log 1 "Downloading MinGW archive" + @call :download "!url!" "!archive_path!" + @if errorlevel 1 ( + @del "!archive_path!" 2>nul + @call :log 0 "Failed to download: !file_name!!file_ext!" + @exit /b 1 + ) + ) +) +@if not exist "%archive_path%" ( + @call :log 0 "The archive did not exist to unpack: %archive_path%" + @exit /b 1 +) +@for /f %%a in ("%archive_path: =-%") do @set "file_name=%%~na" +@for /f %%a in ("%archive_path: =-%") do @set "file_ext=%%~xa" +@call :log 6 +@call :log 1 "Unpacking MinGW %file_name%%file_ext%" +@call :find_sevenzip sevenzip_executable +@if errorlevel 1 ( + @call :log 0 "Need 7zip to unpack the MinGW archive" + @exit /b 1 +) +@call :iso8601 iso8601 +@for /f %%a in ("%folder_path%") do @set "tmp_path=%%~dpatmp-%iso8601%" +@"%sevenzip_executable%" x -y "-o%tmp_path%" "%archive_path%" > nul +@if errorlevel 1 ( + @rmdir /s /q "%folder_path%" + @call :log 0 "Failed to unpack the MinGW archive" + @exit /b 1 +) +@set "expected_path=%tmp_path%\mingw64" +@if not exist "%expected_path%" ( + @set "expected_path=%tmp_path%\mingw32" +) +@move /y "%expected_path%" "%folder_path%" > nul +@if errorlevel 1 ( + @rmdir /s /q "%tmp_path%" 2>nul + @call :log 0 "Failed to move MinGW folder" + @call :log 0 "%expected_path%" + @call :log 0 "%folder_path%" + @exit /b 1 +) +@rmdir /s /q %tmp_path% +@set "compiler_path=%folder_path%\bin" +:unpack_done +@if not exist "%compiler_path%\gcc.exe" ( + @call :log 0 "Failed to find gcc: %compiler_path%" + @exit /b 1 +) +@endlocal & set "%var%=%compiler_path%" +@goto :eof + +:find_sevenzip - Finds (or downloads) the 7zip executable +:: %1 - The return variable for the 7zip executable path +@setlocal +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@call :log 2 "Finding 7zip" +@call :find_in_path sevenzip_executable 7z.exe +@if not errorlevel 1 goto :find_sevenzip_done +@call :find_in_path sevenzip_executable 7za.exe +@if not errorlevel 1 goto :find_sevenzip_done +@set checksum=2FAC454A90AE96021F4FFC607D4C00F8 +@set "url=http://7-zip.org/a/7za920.zip" +@for /f %%a in ("%url: =-%") do @set "file_name=%%~na" +@for /f %%a in ("%url: =-%") do @set "file_ext=%%~xa" +@set "archive_path=%dependency_path%\%file_name%%file_ext%" +@if not exist "%archive_path%" ( + @call :log 6 + @call :log 1 "Downloading 7zip archive" + @call :download "%url%" "%archive_path%" %checksum% + @if errorlevel 1 ( + @del "%archive_path%" 2>nul + @call :log 0 "Failed to download: %file_name%%file_ext%" + @exit /b 1 + ) +) +@set "sevenzip_path=%dependency_path%\sevenzip" +@if not exist "%sevenzip_path%" ( + @call :unzip "%archive_path%" "%sevenzip_path%" + @if errorlevel 1 ( + @call :log 0 "Failed to unzip the7zip archive" + @exit /b 1 + ) +) +@set "sevenzip_executable=%sevenzip_path%\7za.exe" +@if not exist "%sevenzip_executable%" ( + @call :log 0 "Failed to find unpacked 7zip: %sevenzip_executable%" + @exit /b 1 +) +:find_sevenzip_done +@call :log 2 "Found 7zip: %sevenzip_executable%" +@endlocal & set "%var%=%sevenzip_executable%" +@goto :eof + +:unzip - Unzips a .zip archive +:: %1 - The archive to unzip +:: %2 - The location to unzip to +@setlocal +@set "archive_path=%~1" +@if "%archive_path%" == "" @exit /b 1 +@set "folder_path=%~2" +@if "%folder_path%" == "" @exit /b 1 +@for /f %%a in ("%archive_path: =-%") do @set "file_name=%%~na" +@for /f %%a in ("%archive_path: =-%") do @set "file_ext=%%~xa" +@call :log 2 "Unzipping: %file_name%%file_ext%" +@call :iso8601 iso8601 +@set "log_path=%temp%\unzip-%iso8601%-%file_name%.log" +@powershell ^ + Add-Type -assembly "system.io.compression.filesystem"; ^ + [io.compression.zipfile]::ExtractToDirectory(^ + '%archive_path%', '%folder_path%') 2>"%log_path%" +@if errorlevel 1 ( + @call :log 0 "Failed to unzip: %file_name%%file_ext%" + @call :log_append "%log_path%" + @exit /b 1 +) +@endlocal +@goto :eof + +:resolve_version - Gets the version of the MinGW compiler +:: %1 - The return variable for the version +:: %2 - The repository information to use +:: %3 - The architecture of the compiler +:: %4 - Version of MinGW to get [optional] +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :log 0 "Failed to enable extensions" + @exit /b 1 +) +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "repository=%~2" +@if "%repository%" == "" @exit /b 1 +@set "arch=%~3" +@if "%arch%" == "" @exit /b 1 +@set "version=%~4" +@if not "%version%" == "" goto :resolve_version_done +:: Find the latest version +@call :log 3 "Finding latest version" +@set version=0.0.0 +@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( + @if "%arch%" == "%%a" ( + @call :version_compare result "%version%" "%%b" + @if errorlevel 1 ( + @call :log 0 "Failed to compare versions: %version% %%a" + @exit /b 1 + ) + @if !result! lss 0 set version=%%b + ) +) +:resolve_version_done +@if "%version%" == "" ( + @call :log 0 "Failed to resolve latest version number" + @exit /b 1 +) +@call :log 2 "Resolved version: %version%" +@endlocal & set "%var%=%version%" +@goto :eof + +:resolve_threading - Gets the threading model of the MinGW compiler +:: %1 - The return variable for the threading model +:: %2 - The repository information to use +:: %3 - The architecture of the compiler +:: %4 - The version of the compiler +:: %5 - threading model of MinGW to use [optional] +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :log 0 "Failed to enable extensions" + @exit /b 1 +) +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "repository=%~2" +@if "%repository%" == "" @exit /b 1 +@set "arch=%~3" +@if "%arch%" == "" @exit /b 1 +@set "version=%~4" +@if "%version%" == "" @exit /b 1 +@set "threading=%~5" +@if not "%threading%" == "" goto :resolve_threading_done +@call :log 3 "Finding best threading model" +@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( + @if "%arch%" == "%%a" ( + @if "%version%" == "%%b" ( + @if not defined threading ( + @set "threading=%%c" + ) + @if "%%c" == "posix" ( + @set "threading=%%c" +) ) ) ) +:resolve_threading_done +@if "%threading%" == "" ( + @call :log 0 "Failed to resolve the best threading model" + @exit /b 1 +) +@call :log 2 "Resolved threading model: %threading%" +@endlocal & set "%var%=%threading%" +@goto :eof + +:resolve_exceptions - Gets the exception model of the MinGW compiler +:: %1 - The return variable for the exception model +:: %2 - The repository information to use +:: %3 - The architecture of the compiler +:: %4 - The version of the compiler +:: %4 - The threading model of the compiler +:: %5 - exception model of MinGW to use [optional] +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :log 0 "Failed to enable extensions" + @exit /b 1 +) +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "repository=%~2" +@if "%repository%" == "" @exit /b 1 +@set "arch=%~3" +@if "%arch%" == "" @exit /b 1 +@set "version=%~4" +@if "%version%" == "" @exit /b 1 +@set "threading=%~5" +@if "%threading%" == "" @exit /b 1 +@set "exceptions=%~6" +@if not "%exceptions%" == "" goto :resolve_exceptions_done +@call :log 3 "Finding best exception model" +@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( + @if "%arch%" == "%%a" ( + @if "%version%" == "%%b" ( + @if "%threading%" == "%%c" ( + @if not defined exceptions ( + @set "exceptions=%%d" + ) + @if "%%d" == "dwarf" ( + @set "exceptions=%%d" + ) + @if "%%d" == "seh" ( + @set "exceptions=%%d" +) ) ) ) ) +:resolve_exceptions_done +@if "%exceptions%" == "" ( + @call :log 0 "Failed to resolve the best exception model" + @exit /b 1 +) +@call :log 2 "Resolved exception model: %exceptions%" +@endlocal & set "%var%=%exceptions%" +@goto :eof + +:resolve_revision - Gets the revision of the MinGW compiler +:: %1 - The return variable for the revision +:: %2 - The repository information to use +:: %3 - The architecture of the compiler +:: %4 - The version of the compiler +:: %4 - The threading model of the compiler +:: %4 - The exception model of the compiler +:: %5 - revision of the MinGW package to use [optional] +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :log 0 "Failed to enable extensions" + @exit /b 1 +) +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "repository=%~2" +@if "%repository%" == "" @exit /b 1 +@set "arch=%~3" +@if "%arch%" == "" @exit /b 1 +@set "version=%~4" +@if "%version%" == "" @exit /b 1 +@set "threading=%~5" +@if "%threading%" == "" @exit /b 1 +@set "exceptions=%~6" +@if "%exceptions%" == "" @exit /b 1 +@set "revision=%~7" +@if not "%revision%" == "" goto :resolve_revision_done +@call :log 3 "Finding latest revision" +@for /f "delims=| tokens=1-6" %%a in (%repository%) do @( + @if "%arch%" == "%%a" ( + @if "%version%" == "%%b" ( + @if "%threading%" == "%%c" ( + @if "%exceptions%" == "%%d" ( + @if "%%e" gtr "%revision%" ( + @set "revision=%%e" +) ) ) ) ) ) +:resolve_revision_done +@if "%revision%" == "" ( + @call :log 0 "Failed to resolve latest revision" + @exit /b 1 +) +@call :log 2 "Resolved revision: %revision%" +@endlocal & set "%var%=%revision%" +@goto :eof + +:version_compare - Compares two semantic version numbers +:: %1 - The return variable: +:: - < 0 : if %2 < %3 +:: - 0 : if %2 == %3 +:: - > 0 : if %2 > %3 +:: %2 - The first version to compare +:: %3 - The second version to compare +@setlocal +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "lhs=%~2" +@if "%lhs%" == "" @exit /b 1 +@set "rhs=%~3" +@if "%lhs%" == "" @exit /b 1 +@set result=0 +@for /f "delims=. tokens=1-6" %%a in ("%lhs%.%rhs%") do @( + @if %%a lss %%d ( + set result=-1 + goto :version_compare_done + ) else ( + @if %%a gtr %%d ( + set result=1 + goto :version_compare_done + ) else ( + @if %%b lss %%e ( + set result=-1 + goto :version_compare_done + ) else ( + @if %%b gtr %%e ( + set result=1 + goto :version_compare_done + ) else ( + @if %%c lss %%f ( + set result=-1 + goto :version_compare_done + ) else ( + @if %%c gtr %%f ( + set result=1 + goto :version_compare_done + ) + ) + ) + ) + ) + ) +) +:version_compare_done +@endlocal & set "%var%=%result%" +@goto :eof + +:print_usage - Prints the usage of the script +:: %* - message to print, each argument on it's own line +@setlocal +@for %%a in (%*) do @echo.%%~a +@echo. +@echo.build [/?][/v[v...]^|/q][/version][/arch a][/threading t] +@echo. [/exceptions e][/revision r] location +@echo. +@echo. /version v The version of MinGW to download +@echo. /arch a The target architecture [i686^|x86_64] +@echo. /threading t +@echo. Threading model to use [posix^|win32] +@echo. /exceptions e +@echo. Exception model to use [sjlj^|seh^|dwarf] +@echo. /revision e Revision of the release to use +@echo. /v Sets the output to be more verbose +@echo. /v[v...] Extra verbosity, /vv, /vvv, etc +@echo. /q Quiets the output +@echo. /? Shows this usage message +@echo. +@endlocal +@goto :eof + +:script_source - Determines if the script was ran from the cli or explorer +:: %1 - The return variable [cli|explorer] +@verify other 2>nul +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :log 0 "Failed to enable extensions" + @exit /b 1 +) +@call :log 3 "Attempting to detect the script source" +@echo "The invocation command was: '%cmdcmdline%'" >> %log_path% +@for /f "tokens=1-3,*" %%a in ("%cmdcmdline%") do @( + set cmd=%%~a + set arg1=%%~b + set arg2=%%~c + set rest=%%~d +) +@set quote=" +@if "!arg2:~0,1!" equ "!quote!" ( + if "!arg2:~-1!" neq "!quote!" ( + set "arg2=!arg2:~1!" + ) +) +@call :log 4 "cmd = %cmd%" +@call :log 4 "arg1 = %arg1%" +@call :log 4 "arg2 = %arg2%" +@call :log 4 "rest = %rest%" +@call :log 4 "src = %~f0" +@if /i "%arg2%" == "call" ( + set script_source=cli +) else ( + @if /i "%arg1%" == "/c" ( + set script_source=explorer + ) else ( + set script_source=cli + ) +) +@call :log 3 "The script was invoked from %script_source%" +@endlocal & set "%~1=%script_source%" +@goto :eof + +:architecture - Finds the system architecture +:: %1 - The return variable [i686|x86_64] +@setlocal +@call :log 3 "Determining the processor architecture" +@set "key=HKLM\System\CurrentControlSet\Control\Session Manager\Environment" +@set "var=PROCESSOR_ARCHITECTURE" +@for /f "skip=2 tokens=2,*" %%a in ('reg query "%key%" /v "%var%"') do @set "arch=%%b" +@if "%arch%" == "AMD64" set arch=x86_64 +@if "%arch%" == "x64" set arch=i686 +@call :log 4 "arch = %arch%" +@endlocal & set "%~1=%arch%" +@goto :eof + +:md5 - Gets the MD5 checksum for a file +:: %1 - The hash +:: %2 - The file path +@setlocal +@set "var=%~1" +@set "file_path=%~2" +@if "%var%" == "" @exit /b 1 +@if "%file_path%" == "" @exit /b 1 +@if not exist "%file_path%" @exit /b 1 +@for /f "skip=3 tokens=1,*" %%a in ('powershell Get-FileHash -Algorithm MD5 "'%file_path%'"') do @set hash=%%b +@if not defined hash ( + @call :log 6 + @call :log 0 "Failed to get MD5 hash for %file_path%" + @exit /b 1 +) +@endlocal & set "%var%=%hash: =%" +@goto :eof + +:windows_version - Checks the windows version +:: %1 - The windows version +:: %2 - The major version number return variable +:: %3 - The minor version number return variable +:: %4 - The revision version number return variable +@setlocal +@call :log 3 "Retrieving the Windows version" +@for /f "tokens=2 delims=[]" %%x in ('ver') do @set win_ver=%%x +@set win_ver=%win_ver:Version =% +@set win_ver_major=%win_ver:~0,1% +@set win_ver_minor=%win_ver:~2,1% +@set win_ver_rev=%win_ver:~4% +@call :log 4 "win_ver = %win_ver%" +@endlocal & set "%~1=%win_ver%" ^ + & set "%~2=%win_ver_major%" ^ + & set "%~3=%win_ver_minor%" ^ + & set "%~4=%win_ver_rev%" +@goto :eof + +:find_in_path - Finds a program of file in the PATH +:: %1 - return variable of the file path +@setlocal +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "file=%~2" +@if "%file%" == "" @exit /b 1 +@call :log 3 "Searching PATH for %file%" +@for %%x in ("%file%") do @set "file_path=%%~f$PATH:x" +@if not defined file_path @exit /b 1 +@endlocal & set "%var%=%file_path%" +@goto :eof + +:log_append - Appends another file into the current logging file +:: %1 - the file_path to the file to concatenate +@setlocal +@set "file_path=%~1" +@if "%file_path%" == "" @exit /b 1 +@call :log 3 "Appending to log: %file_path%" +@call :iso8601 iso8601 +@set temp_log=%temp%\append-%iso8601%.log +@call :log 4 "Using temp file %temp_log%" +@type "%log_path%" "%file_path%" > "%temp_log%" 2>nul +@move /y "%temp_log%" "%log_path%" 1>nul +@del "%file_path% 2>nul +@del "%temp_log% 2>nul +@endlocal +@goto :eof + +:iso8601 - Returns the current time in ISO8601 format +:: %1 - the return variable +:: %2 - format [extended|basic*] +:: iso8601 - contains the resulting timestamp +@setlocal +@wmic Alias /? >NUL 2>&1 || @exit /b 1 +@set "var=%~1" +@if "%var%" == "" @exit /b 1 +@set "format=%~2" +@if "%format%" == "" set format=basic +@for /F "skip=1 tokens=1-6" %%g IN ('wmic Path Win32_UTCTime Get Day^,Hour^,Minute^,Month^,Second^,Year /Format:table') do @( + @if "%%~l"=="" goto :iso8601_done + @set "yyyy=%%l" + @set "mm=00%%j" + @set "dd=00%%g" + @set "hour=00%%h" + @set "minute=00%%i" + @set "seconds=00%%k" +) +:iso8601_done +@set mm=%mm:~-2% +@set dd=%dd:~-2% +@set hour=%hour:~-2% +@set minute=%minute:~-2% +@set seconds=%seconds:~-2% +@if /i [%format%] == [extended] ( + set iso8601=%yyyy%-%mm%-%dd%T%hour%:%minute%:%seconds%Z +) else ( + if /i [%format%] == [basic] ( + set iso8601=%yyyy%%mm%%dd%T%hour%%minute%%seconds%Z + ) else ( + @exit /b 1 + ) +) +@set iso8601=%iso8601: =0% +@endlocal & set %var%=%iso8601% +@goto :eof + +:verbosity - Processes the verbosity parameter '/v[v...] +:: %1 - verbosity given on the command line +:: logging_level - set to the number of v's +@setlocal +@set logging_level=0 +@set verbosity=%~1 +:verbosity_loop +@set verbosity=%verbosity:~1% +@if not [%verbosity%] == [] @( + set /a "logging_level=logging_level+1" + goto verbosity_loop +) +@endlocal & set logging_level=%logging_level% +@goto :eof + +:log - Logs a message, depending on verbosity +:: %1 - level +:: [0-4] for CLI logging +:: [5-9] for GUI logging +:: %2 - message to print +@setlocal +@set "level=%~1" +@set "msg=%~2" +@if "%log_folder%" == "" ( + echo Logging was used to early in the script, log_folder isn't set yet + goto :eof +) +@if "%log_path%" == "" ( + echo Logging was used to early in the script, log_path isn't set yet + goto :eof +) +@if not exist "%log_folder%" mkdir "%log_folder%" +@if not exist "%log_path%" echo. 1>nul 2>"%log_path%" +@echo.%msg% >> "%log_path%" +@if %level% geq 5 ( + @if [%script_source%] == [explorer] ( + set /a "level=level-5" + ) else ( + @goto :eof + ) +) +@if "%logging_level%" == "" ( + echo Logging was used to early in the script, logging_level isn't set yet + goto :eof +) +@if %logging_level% geq %level% echo.%msg% 1>&2 +@endlocal +@goto :eof + +:download - Downloads a file from the internet +:: %1 - the url of the file to download +:: %2 - the file to download to +:: %3 - the MD5 checksum of the file (optional) +@setlocal EnableDelayedExpansion +@if errorlevel 1 ( + @call :print_usage "Failed to enable extensions" + @exit /b 1 +) +@set "url=%~1" +@set "file_path=%~2" +@set "checksum=%~3" +@for %%a in (%file_path%) do @set dir_path=%%~dpa +@for %%a in (%file_path%) do @set file_name=%%~nxa +@if "%url%" == "" @exit /b 1 +@if "%file_path%" == "" @exit /b 1 +@if "%dir_path%" == "" @exit /b 1 +@if "%file_name%" == "" @exit /b 1 +@if not exist "%dir_path%" mkdir "%dir_path%" +@call :log 2 "Downloading %url%" +@call :iso8601 iso8601 +@set "temp_path=%temp%\download-%iso8601%-%file_name%" +@set "log_path=%temp%\download-%iso8601%-log-%file_name%" +@call :log 4 "Using temp file %temp_path%" +@powershell Invoke-WebRequest "'%url%'" ^ + -OutFile "'%temp_path%'" ^ + -UserAgent [Microsoft.PowerShell.Commands.PSUserAgent]::IE ^ + 1>nul 2>"%log_path%" +@if errorlevel 1 ( + @call :log 0 "Failed to download %url%" + @call :log_append "%log_path%" + @exit /b 1 +) +@if [%checksum%] neq [] ( + @call :log 4 "Checking %checksum% against %temp_path%" + @call :md5 hash "%temp_path%" + if "!hash!" neq "%checksum%" ( + @call :log 0 "Failed to match checksum: %temp_path%" + @call :log 0 "Hash : !hash!" + @call :log 0 "Checksum: %checksum%" + @exit /b 1 + ) else ( + @call :log 3 "Checksum matched: %temp_path%" + @call :log 3 "Hash : !hash!" + @call :log 3 "Checksum: %checksum%" + ) +) +@call :log 4 "Renaming %temp_path% to %file_path%" +@move /y "%temp_path%" "%file_path%" 1>nul +@endlocal +@goto :eof diff --git a/third_party/glslang/.appveyor.yml b/third_party/glslang/.appveyor.yml new file mode 100644 index 0000000..500d4bd --- /dev/null +++ b/third_party/glslang/.appveyor.yml @@ -0,0 +1,105 @@ +# Windows Build Configuration for AppVeyor +# http://www.appveyor.com/docs/appveyor-yml + +# build version format +version: "{build}" + +os: Visual Studio 2015 + +platform: + - x64 + +configuration: + - Debug + - Release + +branches: + only: + - master + +# changes to these files don't need to trigger testing +skip_commits: + files: + - README.md + - README-spirv-remap.txt + - LICENSE.txt + - CODE_OF_CONDUCT.md + - BUILD.* + - WORKSPACE + - kokoro/* + - make-revision + - Android.mk + - _config.yml + +# Travis advances the master-tot tag to current top of the tree after +# each push into the master branch, because it relies on that tag to +# upload build artifacts to the master-tot release. This will cause +# double testing for each push on Appveyor: one for the push, one for +# the tag advance. Disable testing tags. +skip_tags: true + +clone_depth: 5 + +matrix: + fast_finish: true # Show final status immediately if a test fails. + +# scripts that run after cloning repository +install: + - C:/Python27/python.exe update_glslang_sources.py + - set PATH=C:\ninja;C:\Python36;%PATH% + - git clone https://github.com/google/googletest.git External/googletest + +build: + parallel: true # enable MSBuild parallel builds + verbosity: minimal + +build_script: + - mkdir build && cd build + - cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_INSTALL_PREFIX=install .. + - cmake --build . --config %CONFIGURATION% --target install + +test_script: + - ctest -C %CONFIGURATION% --output-on-failure + - cd ../Test && bash runtests + - cd ../build + +after_test: + # For debug build, the generated dll has a postfix "d" in its name. + - ps: >- + If ($env:configuration -Match "Debug") { + $env:SUFFIX="d" + } Else { + $env:SUFFIX="" + } + - cd install + # Zip all glslang artifacts for uploading and deploying + - 7z a glslang-master-windows-"%PLATFORM%"-"%CONFIGURATION%".zip + bin\glslangValidator.exe + bin\spirv-remap.exe + include\glslang\* + lib\glslang%SUFFIX%.lib + lib\HLSL%SUFFIX%.lib + lib\OGLCompiler%SUFFIX%.lib + lib\OSDependent%SUFFIX%.lib + lib\SPIRV%SUFFIX%.lib + lib\SPVRemapper%SUFFIX%.lib + lib\SPIRV-Tools%SUFFIX%.lib + lib\SPIRV-Tools-opt%SUFFIX%.lib + +artifacts: + - path: build\install\*.zip + name: artifacts-zip + +deploy: + - provider: GitHub + auth_token: + secure: YglcSYdl0TylEa59H4K6lylBEDr586NAt2EMgZquSo+iuPrwgZQuJLPCoihSm9y6 + release: master-tot + description: "Continuous build of the latest master branch by Appveyor and Travis CI" + artifact: artifacts-zip + draft: false + prerelease: false + force_update: true + on: + branch: master + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 diff --git a/third_party/glslang/.clang-format b/third_party/glslang/.clang-format new file mode 100644 index 0000000..8c73a52 --- /dev/null +++ b/third_party/glslang/.clang-format @@ -0,0 +1,13 @@ +Language: Cpp +IndentWidth: 4 +PointerAlignment: Left +BreakBeforeBraces: Custom +BraceWrapping: { AfterFunction: true, AfterControlStatement: false } +IndentCaseLabels: false +ReflowComments: false +ColumnLimit: 120 +AccessModifierOffset: -4 +AlignTrailingComments: true +AllowShortBlocksOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false diff --git a/third_party/glslang/.gitattributes b/third_party/glslang/.gitattributes new file mode 100755 index 0000000..2929e83 --- /dev/null +++ b/third_party/glslang/.gitattributes @@ -0,0 +1,17 @@ +# test files have a mix of lf/crlf, and that's a good thing, for testing, don't mess with it +# bash scripts need lines ending with lf, and that's correct for Windows too, e.g., under Cygwin +# (scripts often don't have a suffix) +* -text +*.sh text eof=lf + +# txt files should be native and normalized +*.txt text + +# source code can be native and normalized, but simpler if lf everywhere; will try that way +*.h text eol=lf +*.c text eol=lf +*.cpp text eol=lf +*.y text eol=lf +*.out text eol=lf +*.conf text eol=lf +*.err text eol=lf diff --git a/third_party/glslang/.gitignore b/third_party/glslang/.gitignore new file mode 100644 index 0000000..ab25cec --- /dev/null +++ b/third_party/glslang/.gitignore @@ -0,0 +1,20 @@ +*.o +*.a +*.so +*.exe +.vscode/ +tags +TAGS +bazel-* +build/ +Test/localResults/ +External/googletest +External/spirv-tools +out/ + +# GN generated files +.cipd/ +*.gclient_entries +third_party/ +buildtools/ +tools/ diff --git a/third_party/glslang/.gn b/third_party/glslang/.gn new file mode 100644 index 0000000..5befb80 --- /dev/null +++ b/third_party/glslang/.gn @@ -0,0 +1,39 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +buildconfig = "//build/config/BUILDCONFIG.gn" + +default_args = { + clang_use_chrome_plugins = false + use_custom_libcxx = false +} diff --git a/third_party/glslang/.travis.yml b/third_party/glslang/.travis.yml new file mode 100644 index 0000000..1fa3fc0 --- /dev/null +++ b/third_party/glslang/.travis.yml @@ -0,0 +1,128 @@ +# Linux and Mac Build Configuration for Travis + +language: cpp + +os: + - linux + - osx + +# Use Ubuntu 14.04 LTS (Trusty) as the Linux testing environment. +sudo: false +dist: trusty + +env: + global: + - secure: aGFrgzyKp+84hKrGkxVWg8cHV61uqrKEHT38gfSQK6+WS4GfLOyH83p7WnsEBb7AMhzU7LMNFdvOFr6+NaMpVnqRvc40CEG1Q+lNg9Pq9mhIZLowvDrfqTL9kQ+8Nbw5Q6/dg6CTvY7fvRfpfCEmKIUZBRkoKUuHeuM1uy3IupFcdNuL5bSYn3Beo+apSJginh9DI4BLDXFUgBzTRSLLyCX5g3cpaeGGOCr8quJlYx75W6HRck5g9SZuLtUoH9GFEV3l+ZEWB8noErW+J56L03bwNwFuuAh321evw++oQk5KFa8rlDvar3SJ3b1RHB8u/eq5DBYMyaK/fS8+Q7QbGr8diF/wDe68bKO7U9IhpNfExXmczCpExjHomW5TQv4rYdGhygPMfW97aIsPRYyNKcl4fkmb7NDrM8w0Jscdq2g5c2Kz0ItyZoBri/NXLwFQQjaVCs7Pf97TjuMA7mK0GJmDTRzi6SrDYlWMt5BQL3y0CCojyfLIRcTh0CQjQI29s97bLfQrYAxt9GNNFR+HTXRLLrkaAlJkPGEPwUywlSfEThnvHLesNxYqemolAYpQT4ithoL4GehGIHmaxsW295aKVhuRf8K9eBODNqrfblvM42UHhjntT+92ZnQ/Gkq80GqaMxnxi4PO5FyPIxt0r981b54YBkWi8YA4P7w5pNI= + matrix: + - GLSLANG_BUILD_TYPE=Release + - GLSLANG_BUILD_TYPE=Debug + +compiler: + - clang + - gcc + +matrix: + fast_finish: true # Show final status immediately if a test fails. + exclude: + # Skip GCC builds on Mac OS X. + - os: osx + compiler: gcc + include: + # Additional build using Android NDK. + - env: BUILD_NDK=ON + +cache: + apt: true + +branches: + only: + - master + +addons: + apt: + packages: + - clang-3.6 + +install: + # Make sure that clang-3.6 is selected on Linux. + - if [[ "$TRAVIS_OS_NAME" == "linux" && "$CC" == "clang" ]]; then + export CC=clang-3.6 CXX=clang++-3.6; + fi + # Download a recent Android NDK and use its android.toolchain.cmake file. + - if [[ "$BUILD_NDK" == "ON" ]]; then + export ANDROID_NDK=$HOME/android-ndk; + git init $ANDROID_NDK; + pushd $ANDROID_NDK; + git remote add dneto0 https://github.com/dneto0/android-ndk.git; + git fetch --depth=1 dneto0 r17b-strip; + git checkout FETCH_HEAD; + popd; + export TOOLCHAIN_PATH=$ANDROID_NDK/build/cmake/android.toolchain.cmake; + fi + +before_script: + - git clone --depth=1 https://github.com/google/googletest.git External/googletest + - ./update_glslang_sources.py + +script: + - mkdir build && cd build + # For Android, do release building using NDK without testing. + # Use android-14, the oldest native API level supporeted by NDK r17b. + # We can use newer API levels if we want. + # For Linux and macOS, do debug/release building with testing. + - if [[ "$BUILD_NDK" == "ON" ]]; then + cmake -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_PATH} + -DANDROID_NATIVE_API_LEVEL=android-14 + -DCMAKE_BUILD_TYPE=Release + -DANDROID_ABI="armeabi-v7a with NEON" + -DBUILD_TESTING=OFF ..; + make -j4; + else + cmake -DCMAKE_BUILD_TYPE=${GLSLANG_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=`pwd`/install ..; + make -j4 install; + ctest --output-on-failure && + cd ../Test && ./runtests; + fi + +after_success: + # For debug build, the generated dll has a postfix "d" in its name. + - if [[ "${GLSLANG_BUILD_TYPE}" == "Debug" ]]; then + export SUFFIX="d"; + else + export SUFFIX=""; + fi + # Create tarball for deployment + - if [[ ${CC} == clang* && "${BUILD_NDK}" != "ON" ]]; then + cd ../build/install; + export TARBALL=glslang-master-${TRAVIS_OS_NAME}-${GLSLANG_BUILD_TYPE}.zip; + zip ${TARBALL} + bin/glslangValidator + include/glslang/* + lib/libglslang${SUFFIX}.a + lib/libHLSL${SUFFIX}.a + lib/libOGLCompiler${SUFFIX}.a + lib/libOSDependent${SUFFIX}.a + lib/libSPIRV${SUFFIX}.a + lib/libSPVRemapper${SUFFIX}.a + lib/libSPIRV-Tools${SUFFIX}.a + lib/libSPIRV-Tools-opt${SUFFIX}.a; + fi + +before_deploy: + # Tag the current top of the tree as "master-tot". + # Travis CI replies on the tag name to properly push to GitHub Releases. + - git config --global user.name "Travis CI" + - git config --global user.email "builds@travis-ci.org" + - git tag -f master-tot + - git push -q -f https://${glslangtoken}@github.com/KhronosGroup/glslang --tags + +deploy: + provider: releases + api_key: ${glslangtoken} + on: + branch: master + condition: ${CC} == clang* && ${BUILD_NDK} != ON + file: ${TARBALL} + skip_cleanup: true + overwrite: true diff --git a/third_party/glslang/Android.mk b/third_party/glslang/Android.mk new file mode 100644 index 0000000..3a74497 --- /dev/null +++ b/third_party/glslang/Android.mk @@ -0,0 +1,165 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +LOCAL_PATH := $(call my-dir) + +# Generate glslang/build_info.h +GLSLANG_GENERATED_INCLUDEDIR:=$(TARGET_OUT)/include +GLSLANG_BUILD_INFO_H:=$(GLSLANG_GENERATED_INCLUDEDIR)/glslang/build_info.h + +define gen_glslang_build_info_h +$(call generate-file-dir,$(GLSLANG_GENERATED_INCLUDEDIR)/dummy_filename) +$(GLSLANG_BUILD_INFO_H): \ + $(LOCAL_PATH)/build_info.py \ + $(LOCAL_PATH)/build_info.h.tmpl \ + $(LOCAL_PATH)/CHANGES.md + @$(HOST_PYTHON) $(LOCAL_PATH)/build_info.py \ + $(LOCAL_PATH) \ + -i $(LOCAL_PATH)/build_info.h.tmpl \ + -o $(GLSLANG_BUILD_INFO_H) + @echo "[$(TARGET_ARCH_ABI)] Generate : $(GLSLANG_BUILD_INFO_H) <= CHANGES.md" +endef +$(eval $(call gen_glslang_build_info_h)) + +GLSLANG_OS_FLAGS := -DGLSLANG_OSINCLUDE_UNIX +# AMD and NV extensions are turned on by default in upstream Glslang. +GLSLANG_DEFINES:= -DAMD_EXTENSIONS -DNV_EXTENSIONS -DENABLE_HLSL $(GLSLANG_OS_FLAGS) + +include $(CLEAR_VARS) +LOCAL_MODULE:=OSDependent +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti $(GLSLANG_DEFINES) +LOCAL_EXPORT_C_INCLUDES:=$(LOCAL_PATH) +LOCAL_SRC_FILES:=glslang/OSDependent/Unix/ossource.cpp +LOCAL_C_INCLUDES:=$(LOCAL_PATH) $(LOCAL_PATH)/glslang/OSDependent/Unix/ +LOCAL_EXPORT_C_INCLUDES:=$(LOCAL_PATH)/glslang/OSDependent/Unix/ +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE:=OGLCompiler +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti $(GLSLANG_DEFINES) +LOCAL_EXPORT_C_INCLUDES:=$(LOCAL_PATH) +LOCAL_SRC_FILES:=OGLCompilersDLL/InitializeDll.cpp +LOCAL_C_INCLUDES:=$(LOCAL_PATH)/OGLCompiler +LOCAL_STATIC_LIBRARIES:=OSDependent +include $(BUILD_STATIC_LIBRARY) + +# Build the stubbed HLSL library. +# The HLSL source is now directly referenced by the glslang static library +# instead. +include $(CLEAR_VARS) +LOCAL_MODULE:=HLSL +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti $(GLSLANG_DEFINES) +LOCAL_SRC_FILES:= \ + hlsl/stub.cpp +LOCAL_C_INCLUDES:=$(LOCAL_PATH) \ + $(LOCAL_PATH)/glslang/HLSL +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +GLSLANG_OUT_PATH=$(if $(call host-path-is-absolute,$(TARGET_OUT)),$(TARGET_OUT),$(abspath $(TARGET_OUT))) + +# ShaderLang.cpp depends on the generated build_info.h +$(LOCAL_PATH)/glslang/MachineIndependent/ShaderLang.cpp: \ + $(GLSLANG_BUILD_INFO_H) + +LOCAL_MODULE:=glslang +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti $(GLSLANG_DEFINES) +LOCAL_EXPORT_C_INCLUDES:=$(LOCAL_PATH) +LOCAL_SRC_FILES:= \ + glslang/GenericCodeGen/CodeGen.cpp \ + glslang/GenericCodeGen/Link.cpp \ + glslang/HLSL/hlslAttributes.cpp \ + glslang/HLSL/hlslGrammar.cpp \ + glslang/HLSL/hlslOpMap.cpp \ + glslang/HLSL/hlslParseables.cpp \ + glslang/HLSL/hlslParseHelper.cpp \ + glslang/HLSL/hlslScanContext.cpp \ + glslang/HLSL/hlslTokenStream.cpp \ + glslang/MachineIndependent/attribute.cpp \ + glslang/MachineIndependent/Constant.cpp \ + glslang/MachineIndependent/glslang_tab.cpp \ + glslang/MachineIndependent/InfoSink.cpp \ + glslang/MachineIndependent/Initialize.cpp \ + glslang/MachineIndependent/Intermediate.cpp \ + glslang/MachineIndependent/intermOut.cpp \ + glslang/MachineIndependent/IntermTraverse.cpp \ + glslang/MachineIndependent/iomapper.cpp \ + glslang/MachineIndependent/limits.cpp \ + glslang/MachineIndependent/linkValidate.cpp \ + glslang/MachineIndependent/parseConst.cpp \ + glslang/MachineIndependent/ParseContextBase.cpp \ + glslang/MachineIndependent/ParseHelper.cpp \ + glslang/MachineIndependent/PoolAlloc.cpp \ + glslang/MachineIndependent/propagateNoContraction.cpp \ + glslang/MachineIndependent/reflection.cpp \ + glslang/MachineIndependent/RemoveTree.cpp \ + glslang/MachineIndependent/Scan.cpp \ + glslang/MachineIndependent/ShaderLang.cpp \ + glslang/MachineIndependent/SymbolTable.cpp \ + glslang/MachineIndependent/Versions.cpp \ + glslang/MachineIndependent/preprocessor/PpAtom.cpp \ + glslang/MachineIndependent/preprocessor/PpContext.cpp \ + glslang/MachineIndependent/preprocessor/Pp.cpp \ + glslang/MachineIndependent/preprocessor/PpScanner.cpp \ + glslang/MachineIndependent/preprocessor/PpTokens.cpp +LOCAL_C_INCLUDES:=$(LOCAL_PATH) \ + $(LOCAL_PATH)/glslang/MachineIndependent \ + $(GLSLANG_GENERATED_INCLUDEDIR) \ + $(GLSLANG_OUT_PATH) +LOCAL_STATIC_LIBRARIES:=OSDependent OGLCompiler HLSL +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +# GlslangToSpv.cpp depends on the generated build_info.h +$(LOCAL_PATH)/SPIRV/GlslangToSpv.cpp: \ + $(GLSLANG_BUILD_INFO_H) + +LOCAL_MODULE:=SPIRV +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror $(GLSLANG_DEFINES) +LOCAL_SRC_FILES:= \ + SPIRV/GlslangToSpv.cpp \ + SPIRV/InReadableOrder.cpp \ + SPIRV/Logger.cpp \ + SPIRV/SPVRemapper.cpp \ + SPIRV/SpvBuilder.cpp \ + SPIRV/SpvPostProcess.cpp \ + SPIRV/SpvTools.cpp \ + SPIRV/disassemble.cpp \ + SPIRV/doc.cpp +LOCAL_C_INCLUDES:=$(LOCAL_PATH) \ + $(LOCAL_PATH)/glslang/SPIRV \ + $(GLSLANG_GENERATED_INCLUDEDIR) +LOCAL_EXPORT_C_INCLUDES:=$(LOCAL_PATH)/glslang/SPIRV +LOCAL_STATIC_LIBRARIES:=glslang +include $(BUILD_STATIC_LIBRARY) diff --git a/third_party/glslang/BUILD.bazel b/third_party/glslang/BUILD.bazel new file mode 100644 index 0000000..bfb7797 --- /dev/null +++ b/third_party/glslang/BUILD.bazel @@ -0,0 +1,293 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +package( + default_visibility = ["//visibility:public"], +) + +# Description: +# +# Khronos reference front-end for GLSL and ESSL, and sample SPIR-V generator. + +licenses(["notice"]) + +exports_files(["LICENSE"]) + +# Build information generation script +py_binary( + name = "build_info", + srcs = ["build_info.py"], +) + +genrule( + name = "gen_build_info_h", + srcs = ["CHANGES.md", "build_info.h.tmpl"], + outs = ["glslang/build_info.h"], + cmd = "$(location build_info) $$(dirname $(location CHANGES.md)) -i $(location build_info.h.tmpl) -o $(location glslang/build_info.h)", + tools = [":build_info"], +) + +COMMON_COPTS = select({ + "@bazel_tools//src/conditions:windows": [""], + "//conditions:default": [ + "-Wall", + "-Wuninitialized", + "-Wunused", + "-Wunused-local-typedefs", + "-Wunused-parameter", + "-Wunused-value", + "-Wunused-variable", + "-Wno-reorder", + "-std=c++11", + "-fvisibility=hidden", + "-fvisibility-inlines-hidden", + "-fno-exceptions", + "-fno-rtti", + ], +}) + +cc_library( + name = "glslang", + srcs = glob( + [ + "glslang/GenericCodeGen/*.cpp", + "glslang/HLSL/*.cpp", + "glslang/MachineIndependent/*.cpp", + "glslang/MachineIndependent/preprocessor/*.cpp", + ], + exclude = [ + "glslang/HLSL/pch.h", + "glslang/MachineIndependent/pch.h", + ], + ) + [ + "OGLCompilersDLL/InitializeDll.cpp", + ] + select({ + "@bazel_tools//src/conditions:windows": + ["glslang/OSDependent/Windows/ossource.cpp"], + "//conditions:default": + ["glslang/OSDependent/Unix/ossource.cpp"], + }), + hdrs = glob([ + "glslang/HLSL/*.h", + "glslang/Include/*.h", + "glslang/MachineIndependent/*.h", + "glslang/MachineIndependent/preprocessor/*.h", + ]) + [ + "OGLCompilersDLL/InitializeDll.h", + "StandAlone/DirStackFileIncluder.h", + "glslang/OSDependent/osinclude.h", + "glslang/Public/ShaderLang.h", + ":gen_build_info_h", + ], + copts = COMMON_COPTS, + defines = [ + "AMD_EXTENSIONS", + "ENABLE_HLSL=0", + "ENABLE_OPT=0", + "NV_EXTENSIONS", + ], + linkopts = select({ + "@bazel_tools//src/conditions:windows": [""], + "//conditions:default": ["-lm", "-lpthread"], + }), + linkstatic = 1, +) + +genrule( + name = "export_spirv_headers", + srcs = [ + "SPIRV/GLSL.ext.AMD.h", + "SPIRV/GLSL.ext.EXT.h", + "SPIRV/GLSL.ext.KHR.h", + "SPIRV/GLSL.ext.NV.h", + "SPIRV/GLSL.std.450.h", + "SPIRV/NonSemanticDebugPrintf.h", + "SPIRV/spirv.hpp", + ], + outs = [ + "include/SPIRV/GLSL.ext.AMD.h", + "include/SPIRV/GLSL.ext.EXT.h", + "include/SPIRV/GLSL.ext.KHR.h", + "include/SPIRV/GLSL.ext.NV.h", + "include/SPIRV/GLSL.std.450.h", + "include/SPIRV/NonSemanticDebugPrintf.h", + "include/SPIRV/spirv.hpp", + ], + cmd = "mkdir -p $(@D)/include/SPIRV && cp $(SRCS) $(@D)/include/SPIRV/", +) + +cc_library( + name = "SPIRV_headers", + hdrs = [":export_spirv_headers"], + copts = COMMON_COPTS, + includes = [ + "include", + "include/SPIRV", + ], + linkstatic = 1, +) + +cc_library( + name = "SPIRV", + srcs = glob( + ["SPIRV/*.cpp"], + exclude = [ + "SPIRV/SpvTools.cpp", + ], + ), + hdrs = [ + "SPIRV/GlslangToSpv.h", + "SPIRV/Logger.h", + "SPIRV/SPVRemapper.h", + "SPIRV/SpvBuilder.h", + "SPIRV/SpvTools.h", + "SPIRV/bitutils.h", + "SPIRV/disassemble.h", + "SPIRV/doc.h", + "SPIRV/hex_float.h", + "SPIRV/spvIR.h", + ], + copts = COMMON_COPTS, + includes = ["SPIRV"], + linkopts = select({ + "@bazel_tools//src/conditions:windows": [""], + "//conditions:default": ["-lm"], + }), + linkstatic = 1, + deps = [ + ":SPIRV_headers", + ":glslang", + ], +) + +cc_library( + name = "glslang-default-resource-limits", + srcs = ["StandAlone/ResourceLimits.cpp"], + hdrs = ["StandAlone/ResourceLimits.h"], + copts = COMMON_COPTS, + linkstatic = 1, + deps = [":glslang"], +) + +cc_binary( + name = "glslangValidator", + srcs = [ + "StandAlone/StandAlone.cpp", + "StandAlone/Worklist.h", + ], + copts = COMMON_COPTS, + deps = [ + ":SPIRV", + ":glslang", + ":glslang-default-resource-limits", + ], +) + +cc_binary( + name = "spirv-remap", + srcs = ["StandAlone/spirv-remap.cpp"], + copts = COMMON_COPTS, + deps = [ + ":SPIRV", + ":glslang", + ":glslang-default-resource-limits", + ], +) + +filegroup( + name = "test_files", + srcs = glob( + ["Test/**"], + exclude = [ + "Test/bump", + "Test/glslangValidator", + "Test/runtests", + ], + ), +) + +cc_library( + name = "glslang_test_lib", + testonly = 1, + srcs = [ + "gtests/HexFloat.cpp", + "gtests/Initializer.h", + "gtests/Settings.cpp", + "gtests/Settings.h", + "gtests/TestFixture.cpp", + "gtests/TestFixture.h", + "gtests/main.cpp", + ], + copts = COMMON_COPTS, + data = [":test_files"], + defines = select({ + # Unfortunately we can't use $(location) in cc_library at the moment. + # See https://github.com/bazelbuild/bazel/issues/1023 + # So we'll specify the path manually. + "@bazel_tools//src/conditions:windows": + ["GLSLANG_TEST_DIRECTORY='\"../../../../../Test\"'"], + "//conditions:default": + ["GLSLANG_TEST_DIRECTORY='\"Test\"'"], + }), + linkstatic = 1, + deps = [ + ":SPIRV", + ":glslang", + ":glslang-default-resource-limits", + "@com_google_googletest//:gtest", + ], +) + +GLSLANG_TESTS = glob( + ["gtests/*.FromFile.cpp"], + # Since we are not building the SPIRV-Tools dependency, the following tests + # cannot be performed. + exclude = [ + "gtests/Hlsl.FromFile.cpp", + "gtests/Spv.FromFile.cpp", + ], +) + +[cc_test( + name = test_file.replace("gtests/", "").replace(".FromFile.cpp", "") + "_test", + srcs = [test_file], + copts = COMMON_COPTS, + data = [ + ":test_files", + ], + deps = [ + ":SPIRV", + ":glslang", + ":glslang_test_lib", + ], +) for test_file in GLSLANG_TESTS] diff --git a/third_party/glslang/BUILD.gn b/third_party/glslang/BUILD.gn new file mode 100644 index 0000000..973ca98 --- /dev/null +++ b/third_party/glslang/BUILD.gn @@ -0,0 +1,324 @@ +# Copyright (C) 2018 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import("//build_overrides/glslang.gni") + +# Both Chromium and Fuchsia use by default a set of warning errors +# that is far too strict to compile this project. These are also +# typically appended after |cflags|, overriding target-specific +# definitions. To work around this, determine which configs to +# add and remove in order to succesfully build the project. +if (defined(is_fuchsia_tree) && is_fuchsia_tree) { + _configs_to_remove = [ "//build/config:default_warnings" ] + _configs_to_add = [] +} else { + _configs_to_remove = [ "//build/config/compiler:chromium_code" ] + _configs_to_add = [ "//build/config/compiler:no_chromium_code" ] +} + +action("glslang_build_info") { + script = "build_info.py" + + src_dir = "." + changes_file = "CHANGES.md" + template_file = "build_info.h.tmpl" + out_file = "${target_gen_dir}/include/glslang/build_info.h" + + inputs = [ + changes_file, + script, + template_file, + ] + outputs = [ out_file ] + args = [ + rebase_path(src_dir, root_build_dir), + "-i", + rebase_path(template_file, root_build_dir), + "-o", + rebase_path(out_file, root_build_dir), + ] +} + +spirv_tools_dir = glslang_spirv_tools_dir +if (!defined(glslang_angle)) { + glslang_angle = false +} + +config("glslang_public") { + include_dirs = [ "." ] + if (!is_win || is_clang) { + cflags = [ "-Wno-conversion" ] + } +} + +config("glslang_hlsl") { + defines = [ "ENABLE_HLSL=1" ] +} + +template("glslang_sources_common") { + source_set(target_name) { + public_configs = [ ":glslang_public" ] + + if (invoker.enable_hlsl) { + public_configs += [ ":glslang_hlsl" ] + } + + sources = [ + "OGLCompilersDLL/InitializeDll.cpp", + "OGLCompilersDLL/InitializeDll.h", + "SPIRV/GLSL.ext.AMD.h", + "SPIRV/GLSL.ext.EXT.h", + "SPIRV/GLSL.ext.KHR.h", + "SPIRV/GLSL.ext.NV.h", + "SPIRV/GLSL.std.450.h", + "SPIRV/GlslangToSpv.cpp", + "SPIRV/GlslangToSpv.h", + "SPIRV/InReadableOrder.cpp", + "SPIRV/Logger.cpp", + "SPIRV/Logger.h", + "SPIRV/NonSemanticDebugPrintf.h", + "SPIRV/SPVRemapper.cpp", + "SPIRV/SPVRemapper.h", + "SPIRV/SpvBuilder.cpp", + "SPIRV/SpvBuilder.h", + "SPIRV/SpvPostProcess.cpp", + "SPIRV/SpvTools.h", + "SPIRV/bitutils.h", + "SPIRV/disassemble.cpp", + "SPIRV/disassemble.h", + "SPIRV/doc.cpp", + "SPIRV/doc.h", + "SPIRV/hex_float.h", + "SPIRV/spirv.hpp", + "SPIRV/spvIR.h", + "glslang/GenericCodeGen/CodeGen.cpp", + "glslang/GenericCodeGen/Link.cpp", + "glslang/Include/BaseTypes.h", + "glslang/Include/Common.h", + "glslang/Include/ConstantUnion.h", + "glslang/Include/InfoSink.h", + "glslang/Include/InitializeGlobals.h", + "glslang/Include/PoolAlloc.h", + "glslang/Include/ResourceLimits.h", + "glslang/Include/ShHandle.h", + "glslang/Include/Types.h", + "glslang/Include/arrays.h", + "glslang/Include/intermediate.h", + "glslang/MachineIndependent/Constant.cpp", + "glslang/MachineIndependent/InfoSink.cpp", + "glslang/MachineIndependent/Initialize.cpp", + "glslang/MachineIndependent/Initialize.h", + "glslang/MachineIndependent/IntermTraverse.cpp", + "glslang/MachineIndependent/Intermediate.cpp", + "glslang/MachineIndependent/LiveTraverser.h", + "glslang/MachineIndependent/ParseContextBase.cpp", + "glslang/MachineIndependent/ParseHelper.cpp", + "glslang/MachineIndependent/ParseHelper.h", + "glslang/MachineIndependent/PoolAlloc.cpp", + "glslang/MachineIndependent/RemoveTree.cpp", + "glslang/MachineIndependent/RemoveTree.h", + "glslang/MachineIndependent/Scan.cpp", + "glslang/MachineIndependent/Scan.h", + "glslang/MachineIndependent/ScanContext.h", + "glslang/MachineIndependent/ShaderLang.cpp", + "glslang/MachineIndependent/SymbolTable.cpp", + "glslang/MachineIndependent/SymbolTable.h", + "glslang/MachineIndependent/Versions.cpp", + "glslang/MachineIndependent/Versions.h", + "glslang/MachineIndependent/attribute.cpp", + "glslang/MachineIndependent/attribute.h", + "glslang/MachineIndependent/gl_types.h", + "glslang/MachineIndependent/glslang_tab.cpp", + "glslang/MachineIndependent/glslang_tab.cpp.h", + "glslang/MachineIndependent/intermOut.cpp", + "glslang/MachineIndependent/iomapper.cpp", + "glslang/MachineIndependent/iomapper.h", + "glslang/MachineIndependent/limits.cpp", + "glslang/MachineIndependent/linkValidate.cpp", + "glslang/MachineIndependent/localintermediate.h", + "glslang/MachineIndependent/parseConst.cpp", + "glslang/MachineIndependent/parseVersions.h", + "glslang/MachineIndependent/preprocessor/Pp.cpp", + "glslang/MachineIndependent/preprocessor/PpAtom.cpp", + "glslang/MachineIndependent/preprocessor/PpContext.cpp", + "glslang/MachineIndependent/preprocessor/PpContext.h", + "glslang/MachineIndependent/preprocessor/PpScanner.cpp", + "glslang/MachineIndependent/preprocessor/PpTokens.cpp", + "glslang/MachineIndependent/preprocessor/PpTokens.h", + "glslang/MachineIndependent/propagateNoContraction.cpp", + "glslang/MachineIndependent/propagateNoContraction.h", + "glslang/MachineIndependent/reflection.cpp", + "glslang/MachineIndependent/reflection.h", + "glslang/OSDependent/osinclude.h", + "glslang/Public/ShaderLang.h", + ] + + # Workaround gn issue complaining about these not being allowed even though GLSLANG_HLSL is not + # defined. + sources += [ + "glslang/HLSL/hlslParseHelper.h", + "glslang/HLSL/hlslParseables.h", + "glslang/HLSL/hlslScanContext.h", + "glslang/HLSL/hlslTokens.h", + ] + + if (invoker.enable_hlsl) { + sources += [ + "glslang/HLSL/hlslAttributes.cpp", + "glslang/HLSL/hlslAttributes.h", + "glslang/HLSL/hlslGrammar.cpp", + "glslang/HLSL/hlslGrammar.h", + "glslang/HLSL/hlslOpMap.cpp", + "glslang/HLSL/hlslOpMap.h", + "glslang/HLSL/hlslParseHelper.cpp", + "glslang/HLSL/hlslParseables.cpp", + "glslang/HLSL/hlslScanContext.cpp", + "glslang/HLSL/hlslTokenStream.cpp", + "glslang/HLSL/hlslTokenStream.h", + ] + } + + defines = [] + if (invoker.enable_opt) { + sources += [ "SPIRV/SpvTools.cpp" ] + defines += [ "ENABLE_OPT=1" ] + } + if (invoker.is_angle) { + defines += [ "GLSLANG_ANGLE" ] + } + + if (is_win) { + sources += [ "glslang/OSDependent/Windows/ossource.cpp" ] + defines += [ "GLSLANG_OSINCLUDE_WIN32" ] + } else { + sources += [ "glslang/OSDependent/Unix/ossource.cpp" ] + defines += [ "GLSLANG_OSINCLUDE_UNIX" ] + } + + if (is_clang) { + cflags = [ + "-Wno-extra-semi", + "-Wno-ignored-qualifiers", + "-Wno-implicit-fallthrough", + "-Wno-inconsistent-missing-override", + "-Wno-missing-field-initializers", + "-Wno-newline-eof", + "-Wno-sign-compare", + "-Wno-suggest-destructor-override", + "-Wno-suggest-override", + "-Wno-unused-variable", + ] + } + if (is_win && !is_clang) { + cflags = [ + "/wd4018", # signed/unsigned mismatch + "/wd4189", # local variable is initialized but not referenced + ] + } + + include_dirs = [ "${target_gen_dir}/include" ] + + deps = [ ":glslang_build_info" ] + + if (invoker.enable_opt) { + deps += [ + "${spirv_tools_dir}:spvtools_opt", + "${spirv_tools_dir}:spvtools_val", + ] + include_dirs += [ "${spirv_tools_dir}/include" ] + } + + configs -= _configs_to_remove + configs += _configs_to_add + } +} + +glslang_sources_common("glslang_lib_sources") { + enable_opt = !glslang_angle + enable_hlsl = !glslang_angle + is_angle = glslang_angle +} + +glslang_sources_common("glslang_sources") { + enable_opt = true + enable_hlsl = true + is_angle = false +} + +source_set("glslang_default_resource_limits_sources") { + sources = [ + "StandAlone/ResourceLimits.cpp", + "StandAlone/ResourceLimits.h", + "glslang/Include/ResourceLimits.h", + ] + public_configs = [ ":glslang_public" ] + + configs -= _configs_to_remove + configs += _configs_to_add +} + +executable("glslang_validator") { + sources = [ + "StandAlone/DirStackFileIncluder.h", + "StandAlone/StandAlone.cpp", + ] + if (!is_win) { + cflags = [ "-Woverflow" ] + } + defines = [ "ENABLE_OPT=1" ] + deps = [ + ":glslang_build_info", + ":glslang_default_resource_limits_sources", + ":glslang_sources", + ] + public_configs = [ ":glslang_hlsl" ] + + include_dirs = [ + "${target_gen_dir}/include", + "${spirv_tools_dir}/include", + ] + + configs -= _configs_to_remove + configs += _configs_to_add +} + +executable("spirv-remap") { + sources = [ "StandAlone/spirv-remap.cpp" ] + defines = [ "ENABLE_OPT=1" ] + deps = [ ":glslang_sources" ] + + include_dirs = [ "${spirv_tools_dir}/include" ] + + configs -= _configs_to_remove + configs += _configs_to_add +} diff --git a/third_party/glslang/CHANGES.md b/third_party/glslang/CHANGES.md new file mode 100644 index 0000000..6328041 --- /dev/null +++ b/third_party/glslang/CHANGES.md @@ -0,0 +1,34 @@ +# Revision history for `glslang` + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](https://semver.org/). + +## 11.0.0 2020-07-20 + +### Breaking changes + +#### Visual Studio 2013 is no longer supported + +[As scheduled](https://github.com/KhronosGroup/glslang/blob/9eef54b2513ca6b40b47b07d24f453848b65c0df/README.md#planned-deprecationsremovals), +Microsoft Visual Studio 2013 is no longer officially supported. Please upgrade +to at least Visual Studio 2015. + +## 10.15.3847 2020-07-20 + +### Breaking changes + +* The following files have been removed: + * `glslang/include/revision.h` + * `glslang/include/revision.template` + +The `GLSLANG_MINOR_VERSION` and `GLSLANG_PATCH_LEVEL` defines have been removed +from the public headers. \ +Instead each build script now uses the new `build_info.py` +script along with the `build_info.h.tmpl` and this `CHANGES.md` file to generate +the glslang build-time generated header `glslang/build_info.h`. + +The new public API to obtain the `glslang` version is `glslang::GetVersion()`. + +### Other changes +* `glslang` shared objects produced by CMake are now `SONAME` versioned using + [Semantic Versioning 2.0.0](https://semver.org/). diff --git a/third_party/glslang/CMakeLists.txt b/third_party/glslang/CMakeLists.txt new file mode 100644 index 0000000..ada2b8f --- /dev/null +++ b/third_party/glslang/CMakeLists.txt @@ -0,0 +1,371 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +project(glslang + LANGUAGES CXX) + +# increase to 3.1 once all major distributions +# include a version of CMake >= 3.1 +cmake_minimum_required(VERSION 2.8.12) +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif() +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# Enable compile commands database +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Adhere to GNU filesystem layout conventions +include(GNUInstallDirs) + +# Needed for CMAKE_DEPENDENT_OPTION macro +include(CMakeDependentOption) + +option(BUILD_SHARED_LIBS "Build Shared Libraries" OFF) +option(BUILD_EXTERNAL "Build external dependencies in /External" ON) + +set(LIB_TYPE STATIC) + +if(BUILD_SHARED_LIBS) + set(LIB_TYPE SHARED) +endif() + +if ("${CMAKE_BUILD_TYPE}" STREQUAL "") + # This logic inside SPIRV-Tools, which can upset build target dependencies + # if changed after targets are already defined. To prevent these issues, + # ensure CMAKE_BUILD_TYPE is assigned early and at the glslang root scope. + message(STATUS "No build type selected, default to Debug") + set(CMAKE_BUILD_TYPE "Debug") +endif() + +option(SKIP_GLSLANG_INSTALL "Skip installation" ${SKIP_GLSLANG_INSTALL}) +if(NOT ${SKIP_GLSLANG_INSTALL}) + set(ENABLE_GLSLANG_INSTALL ON) +endif() +option(ENABLE_SPVREMAPPER "Enables building of SPVRemapper" ON) + +option(ENABLE_GLSLANG_BINARIES "Builds glslangValidator and spirv-remap" ON) + +option(ENABLE_GLSLANG_JS + "If using Emscripten, build glslang.js. Otherwise, builds a sample executable for binary-size testing." OFF) +CMAKE_DEPENDENT_OPTION(ENABLE_GLSLANG_WEBMIN + "Reduces glslang to minimum needed for web use" + OFF "ENABLE_GLSLANG_JS" + OFF) +CMAKE_DEPENDENT_OPTION(ENABLE_GLSLANG_WEBMIN_DEVEL + "For ENABLE_GLSLANG_WEBMIN builds, enables compilation error messages" + OFF "ENABLE_GLSLANG_WEBMIN" + OFF) +CMAKE_DEPENDENT_OPTION(ENABLE_EMSCRIPTEN_SINGLE_FILE + "If using Emscripten, enables SINGLE_FILE build" + OFF "ENABLE_GLSLANG_JS AND EMSCRIPTEN" + OFF) +CMAKE_DEPENDENT_OPTION(ENABLE_EMSCRIPTEN_ENVIRONMENT_NODE + "If using Emscripten, builds to run on Node instead of Web" + OFF "ENABLE_GLSLANG_JS AND EMSCRIPTEN" + OFF) + +CMAKE_DEPENDENT_OPTION(ENABLE_HLSL + "Enables HLSL input support" + ON "NOT ENABLE_GLSLANG_WEBMIN" + OFF) + +option(ENABLE_RTTI "Enables RTTI" OFF) +option(ENABLE_EXCEPTIONS "Enables Exceptions" OFF) +option(ENABLE_OPT "Enables spirv-opt capability if present" ON) +option(ENABLE_PCH "Enables Precompiled header" ON) +option(ENABLE_CTEST "Enables testing" ON) + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND WIN32) + set(CMAKE_INSTALL_PREFIX "install" CACHE STRING "..." FORCE) +endif() + +option(USE_CCACHE "Use ccache" OFF) +if(USE_CCACHE) + find_program(CCACHE_FOUND ccache) + if(CCACHE_FOUND) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + endif(CCACHE_FOUND) +endif() + +if(ENABLE_CTEST) + include(CTest) +endif() + +if(ENABLE_HLSL) + add_definitions(-DENABLE_HLSL) +endif(ENABLE_HLSL) + +if(ENABLE_GLSLANG_WEBMIN) + add_definitions(-DGLSLANG_WEB) + if(ENABLE_GLSLANG_WEBMIN_DEVEL) + add_definitions(-DGLSLANG_WEB_DEVEL) + endif(ENABLE_GLSLANG_WEBMIN_DEVEL) +endif(ENABLE_GLSLANG_WEBMIN) + +if(WIN32) + set(CMAKE_DEBUG_POSTFIX "d") + if(MSVC) + include(ChooseMSVCCRT.cmake) + endif(MSVC) + add_definitions(-DGLSLANG_OSINCLUDE_WIN32) +elseif(UNIX) + add_definitions(-DGLSLANG_OSINCLUDE_UNIX) +else(WIN32) + message("unknown platform") +endif(WIN32) + +if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + add_compile_options(-Wall -Wmaybe-uninitialized -Wuninitialized -Wunused -Wunused-local-typedefs + -Wunused-parameter -Wunused-value -Wunused-variable -Wunused-but-set-parameter -Wunused-but-set-variable -fno-exceptions) + add_compile_options(-Wno-reorder) # disable this from -Wall, since it happens all over. + if(NOT ENABLE_RTTI) + add_compile_options(-fno-rtti) + endif() + if(NOT ENABLE_EXCEPTIONS) + add_compile_options(-fno-exceptions) + endif() + if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0") + add_compile_options(-Werror=deprecated-copy) + endif() + + if(NOT CMAKE_VERSION VERSION_LESS "3.13") + # Error if there's symbols that are not found at link time. + # add_link_options() was added in CMake 3.13 - if using an earlier + # version don't set this - it should be caught by presubmits anyway. + add_link_options("-Wl,--no-undefined") + endif() +elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" AND NOT MSVC) + add_compile_options(-Wall -Wuninitialized -Wunused -Wunused-local-typedefs + -Wunused-parameter -Wunused-value -Wunused-variable) + add_compile_options(-Wno-reorder) # disable this from -Wall, since it happens all over. + if(NOT ENABLE_RTTI) + add_compile_options(-fno-rtti) + endif() + if(NOT ENABLE_EXCEPTIONS) + add_compile_options(-fno-exceptions) + endif() + + if(NOT CMAKE_VERSION VERSION_LESS "3.13") + # Error if there's symbols that are not found at link time. + # add_link_options() was added in CMake 3.13 - if using an earlier + # version don't set this - it should be caught by presubmits anyway. + add_link_options("-Wl,-undefined,error") + endif() +elseif(MSVC) + if(NOT ENABLE_RTTI) + string(FIND "${CMAKE_CXX_FLAGS}" "/GR" MSVC_HAS_GR) + if(MSVC_HAS_GR) + string(REGEX REPLACE /GR /GR- CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + else() + add_compile_options(/GR-) # Disable RTTI + endif() + endif() + if(ENABLE_EXCEPTIONS) + add_compile_options(/EHsc) # Enable Exceptions + endif() +endif() + +if(ENABLE_GLSLANG_JS) + if(MSVC) + add_compile_options(/Os /GR-) + else() + add_compile_options(-Os -fno-rtti -fno-exceptions) + if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" AND NOT MSVC) + add_compile_options(-Wno-unused-parameter) + add_compile_options(-Wno-unused-variable -Wno-unused-const-variable) + endif() + endif() +endif(ENABLE_GLSLANG_JS) + +# Request C++11 +if(${CMAKE_VERSION} VERSION_LESS 3.1) + # CMake versions before 3.1 do not understand CMAKE_CXX_STANDARD + # remove this block once CMake >=3.1 has fixated in the ecosystem + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +else() + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) +endif() + +function(glslang_set_link_args TARGET) + # For MinGW compiles, statically link against the GCC and C++ runtimes. + # This avoids the need to ship those runtimes as DLLs. + if(WIN32 AND ${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + set_target_properties(${TARGET} PROPERTIES + LINK_FLAGS "-static -static-libgcc -static-libstdc++") + endif() +endfunction(glslang_set_link_args) + +if(NOT COMMAND find_host_package) + macro(find_host_package) + find_package(${ARGN}) + endmacro() +endif() + +# CMake needs to find the right version of python, right from the beginning, +# otherwise, it will find the wrong version and fail later +find_host_package(PythonInterp 3 REQUIRED) + +# Root directory for build-time generated include files +set(GLSLANG_GENERATED_INCLUDEDIR "${CMAKE_BINARY_DIR}/include") + +################################################################################ +# Build version information generation +################################################################################ +set(GLSLANG_CHANGES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CHANGES.md") +set(GLSLANG_BUILD_INFO_PY "${CMAKE_CURRENT_SOURCE_DIR}/build_info.py") +set(GLSLANG_BUILD_INFO_H_TMPL "${CMAKE_CURRENT_SOURCE_DIR}/build_info.h.tmpl") +set(GLSLANG_BUILD_INFO_H "${GLSLANG_GENERATED_INCLUDEDIR}/glslang/build_info.h") + +# Command to build the build_info.h file +add_custom_command( + OUTPUT ${GLSLANG_BUILD_INFO_H} + COMMAND ${PYTHON_EXECUTABLE} "${GLSLANG_BUILD_INFO_PY}" + ${CMAKE_CURRENT_SOURCE_DIR} + "-i" ${GLSLANG_BUILD_INFO_H_TMPL} + "-o" ${GLSLANG_BUILD_INFO_H} + DEPENDS ${GLSLANG_BUILD_INFO_PY} + ${GLSLANG_CHANGES_FILE} + ${GLSLANG_BUILD_INFO_H_TMPL} + COMMENT "Generating ${GLSLANG_BUILD_INFO_H}") + +# Target to build the build_info.h file +add_custom_target(glslang-build-info DEPENDS ${GLSLANG_BUILD_INFO_H}) + +# Populate the CMake GLSLANG_VERSION* variables with the build version +# information. +execute_process( + COMMAND ${PYTHON_EXECUTABLE} "${GLSLANG_BUILD_INFO_PY}" + ${CMAKE_CURRENT_SOURCE_DIR} "..<-flavor>;;;;" + OUTPUT_VARIABLE "GLSLANG_VERSIONS" + OUTPUT_STRIP_TRAILING_WHITESPACE) +list(GET "GLSLANG_VERSIONS" 0 "GLSLANG_VERSION") +list(GET "GLSLANG_VERSIONS" 1 "GLSLANG_VERSION_MAJOR") +list(GET "GLSLANG_VERSIONS" 2 "GLSLANG_VERSION_MINOR") +list(GET "GLSLANG_VERSIONS" 3 "GLSLANG_VERSION_PATCH") +list(GET "GLSLANG_VERSIONS" 4 "GLSLANG_VERSION_FLAVOR") +configure_file(${GLSLANG_CHANGES_FILE} "${CMAKE_CURRENT_BINARY_DIR}/CHANGES.md") # Required to re-run cmake on version change + +# glslang_add_build_info_dependency() adds the glslang-build-info dependency and +# generated include directories to target. +function(glslang_add_build_info_dependency target) + target_include_directories(${target} PUBLIC $) + add_dependencies(${target} glslang-build-info) +endfunction() + +# glslang_only_export_explicit_symbols() makes the symbol visibility hidden by +# default for when building shared libraries, and sets the +# GLSLANG_IS_SHARED_LIBRARY define, and GLSLANG_EXPORTING to 1 when specifically +# building . +function(glslang_only_export_explicit_symbols target) + if(BUILD_SHARED_LIBS) + target_compile_definitions(${target} PUBLIC "GLSLANG_IS_SHARED_LIBRARY=1") + if(WIN32) + target_compile_definitions(${target} PRIVATE "GLSLANG_EXPORTING=1") + else() + target_compile_options(${target} PRIVATE "-fvisibility=hidden") + endif() + endif() +endfunction() + +# glslang_pch() adds precompiled header rules to for the pre-compiled +# header file . As target_precompile_headers() was added in CMake 3.16, +# this is a no-op if called on earlier versions of CMake. +if(NOT CMAKE_VERSION VERSION_LESS "3.16" AND ENABLE_PCH) + function(glslang_pch target pch) + target_precompile_headers(${target} PRIVATE ${pch}) + endfunction() +else() + function(glslang_pch target pch) + endfunction() + if(ENABLE_PCH) + message("Your CMake version is ${CMAKE_VERSION}. Update to at least 3.16 to enable precompiled headers to speed up incremental builds") + endif() +endif() + +if(BUILD_EXTERNAL AND IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/External) + # We depend on these for later projects, so they should come first. + add_subdirectory(External) +endif() + +if(NOT TARGET SPIRV-Tools-opt) + set(ENABLE_OPT OFF) +endif() + +if(ENABLE_OPT) + message(STATUS "optimizer enabled") + add_definitions(-DENABLE_OPT=1) +else() + if(ENABLE_HLSL) + message(STATUS "spirv-tools not linked - illegal SPIRV may be generated for HLSL") + endif() + add_definitions(-DENABLE_OPT=0) +endif() + +add_subdirectory(glslang) +add_subdirectory(OGLCompilersDLL) +if(ENABLE_GLSLANG_BINARIES) + add_subdirectory(StandAlone) +endif() +add_subdirectory(SPIRV) +if(ENABLE_HLSL) + add_subdirectory(hlsl) +endif(ENABLE_HLSL) +if(ENABLE_CTEST) + add_subdirectory(gtests) +endif() + +if(ENABLE_CTEST AND BUILD_TESTING) + # glslang-testsuite runs a bash script on Windows. + # Make sure to use '-o igncr' flag to ignore carriage returns (\r). + set(IGNORE_CR_FLAG "") + if(WIN32) + set(IGNORE_CR_FLAG -o igncr) + endif() + + if (CMAKE_CONFIGURATION_TYPES) + set(RESULTS_PATH ${CMAKE_CURRENT_BINARY_DIR}/$/localResults) + set(VALIDATOR_PATH ${CMAKE_CURRENT_BINARY_DIR}/StandAlone/$/glslangValidator) + set(REMAP_PATH ${CMAKE_CURRENT_BINARY_DIR}/StandAlone/$/spirv-remap) + else(CMAKE_CONFIGURATION_TYPES) + set(RESULTS_PATH ${CMAKE_CURRENT_BINARY_DIR}/localResults) + set(VALIDATOR_PATH ${CMAKE_CURRENT_BINARY_DIR}/StandAlone/glslangValidator) + set(REMAP_PATH ${CMAKE_CURRENT_BINARY_DIR}/StandAlone/spirv-remap) + endif(CMAKE_CONFIGURATION_TYPES) + + add_test(NAME glslang-testsuite + COMMAND bash ${IGNORE_CR_FLAG} runtests ${RESULTS_PATH} ${VALIDATOR_PATH} ${REMAP_PATH} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Test/) +endif() diff --git a/third_party/glslang/CODE_OF_CONDUCT.md b/third_party/glslang/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a11610b --- /dev/null +++ b/third_party/glslang/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +A reminder that this issue tracker is managed by the Khronos Group. Interactions here should follow the Khronos Code of Conduct (https://www.khronos.org/developers/code-of-conduct), which prohibits aggressive or derogatory language. Please keep the discussion friendly and civil. diff --git a/third_party/glslang/ChooseMSVCCRT.cmake b/third_party/glslang/ChooseMSVCCRT.cmake new file mode 100644 index 0000000..b156126 --- /dev/null +++ b/third_party/glslang/ChooseMSVCCRT.cmake @@ -0,0 +1,138 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# The macro choose_msvc_crt() takes a list of possible +# C runtimes to choose from, in the form of compiler flags, +# to present to the user. (MTd for /MTd, etc) +# +# The macro is invoked at the end of the file. +# +# CMake already sets CRT flags in the CMAKE_CXX_FLAGS_* and +# CMAKE_C_FLAGS_* variables by default. To let the user +# override that for each build type: +# 1. Detect which CRT is already selected, and reflect this in +# LLVM_USE_CRT_* so the user can have a better idea of what +# changes they're making. +# 2. Replace the flags in both variables with the new flag via a regex. +# 3. set() the variables back into the cache so the changes +# are user-visible. + +### Helper macros: ### +macro(make_crt_regex regex crts) + set(${regex} "") + foreach(crt ${${crts}}) + # Trying to match the beginning or end of the string with stuff + # like [ ^]+ didn't work, so use a bunch of parentheses instead. + set(${regex} "${${regex}}|(^| +)/${crt}($| +)") + endforeach(crt) + string(REGEX REPLACE "^\\|" "" ${regex} "${${regex}}") +endmacro(make_crt_regex) + +macro(get_current_crt crt_current regex flagsvar) + # Find the selected-by-CMake CRT for each build type, if any. + # Strip off the leading slash and any whitespace. + string(REGEX MATCH "${${regex}}" ${crt_current} "${${flagsvar}}") + string(REPLACE "/" " " ${crt_current} "${${crt_current}}") + string(STRIP "${${crt_current}}" ${crt_current}) +endmacro(get_current_crt) + +# Replaces or adds a flag to a variable. +# Expects 'flag' to be padded with spaces. +macro(set_flag_in_var flagsvar regex flag) + string(REGEX MATCH "${${regex}}" current_flag "${${flagsvar}}") + if("${current_flag}" STREQUAL "") + set(${flagsvar} "${${flagsvar}}${${flag}}") + else() + string(REGEX REPLACE "${${regex}}" "${${flag}}" ${flagsvar} "${${flagsvar}}") + endif() + string(STRIP "${${flagsvar}}" ${flagsvar}) + # Make sure this change gets reflected in the cache/gui. + # CMake requires the docstring parameter whenever set() touches the cache, + # so get the existing docstring and re-use that. + get_property(flagsvar_docs CACHE ${flagsvar} PROPERTY HELPSTRING) + set(${flagsvar} "${${flagsvar}}" CACHE STRING "${flagsvar_docs}" FORCE) +endmacro(set_flag_in_var) + + +macro(choose_msvc_crt MSVC_CRT) + if(LLVM_USE_CRT) + message(FATAL_ERROR + "LLVM_USE_CRT is deprecated. Use the CMAKE_BUILD_TYPE-specific +variables (LLVM_USE_CRT_DEBUG, etc) instead.") + endif() + + make_crt_regex(MSVC_CRT_REGEX ${MSVC_CRT}) + + foreach(build_type ${CMAKE_CONFIGURATION_TYPES} ${CMAKE_BUILD_TYPE}) + string(TOUPPER "${build_type}" build) + if (NOT LLVM_USE_CRT_${build}) + get_current_crt(LLVM_USE_CRT_${build} + MSVC_CRT_REGEX + CMAKE_CXX_FLAGS_${build}) + set(LLVM_USE_CRT_${build} + "${LLVM_USE_CRT_${build}}" + CACHE STRING "Specify VC++ CRT to use for ${build_type} configurations." + FORCE) + set_property(CACHE LLVM_USE_CRT_${build} + PROPERTY STRINGS ;${${MSVC_CRT}}) + endif(NOT LLVM_USE_CRT_${build}) + endforeach(build_type) + + foreach(build_type ${CMAKE_CONFIGURATION_TYPES} ${CMAKE_BUILD_TYPE}) + string(TOUPPER "${build_type}" build) + if ("${LLVM_USE_CRT_${build}}" STREQUAL "") + set(flag_string " ") + else() + set(flag_string " /${LLVM_USE_CRT_${build}} ") + list(FIND ${MSVC_CRT} ${LLVM_USE_CRT_${build}} idx) + if (idx LESS 0) + message(FATAL_ERROR + "Invalid value for LLVM_USE_CRT_${build}: ${LLVM_USE_CRT_${build}}. Valid options are one of: ${${MSVC_CRT}}") + endif (idx LESS 0) + message(STATUS "Using ${build_type} VC++ CRT: ${LLVM_USE_CRT_${build}}") + endif() + foreach(lang C CXX) + set_flag_in_var(CMAKE_${lang}_FLAGS_${build} MSVC_CRT_REGEX flag_string) + endforeach(lang) + endforeach(build_type) +endmacro(choose_msvc_crt MSVC_CRT) + + +# List of valid CRTs for MSVC +set(MSVC_CRT + MD + MDd + MT + MTd) + +choose_msvc_crt(MSVC_CRT) diff --git a/third_party/glslang/DEPS b/third_party/glslang/DEPS new file mode 100644 index 0000000..2f5fa8e --- /dev/null +++ b/third_party/glslang/DEPS @@ -0,0 +1,82 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +use_relative_paths = True + +gclient_gn_args_file = 'build/config/gclient_args.gni' + +vars = { + 'chromium_git': 'https://chromium.googlesource.com', + 'build_with_chromium': False, +} + +deps = { + + './build': { + 'url': '{chromium_git}/chromium/src/build.git@85ee3b7692e5284f08bd3c9459fb5685eed7b838', + 'condition': 'not build_with_chromium', + }, + + './buildtools': { + 'url': '{chromium_git}/chromium/src/buildtools.git@4be464e050b3d05060471788f926b34c641db9fd', + 'condition': 'not build_with_chromium', + }, + + './tools/clang': { + 'url': '{chromium_git}/chromium/src/tools/clang.git@3a982adabb720aa8f3e3885d40bf3fe506990157', + 'condition': 'not build_with_chromium', + }, + +} + +hooks = [ + { + 'name': 'sysroot_x64', + 'pattern': '.', + 'condition': 'checkout_linux and (checkout_x64 and not build_with_chromium)', + 'action': ['python', './build/linux/sysroot_scripts/install-sysroot.py', + '--arch=x64'], + }, + { + # Note: On Win, this should run after win_toolchain, as it may use it. + 'name': 'clang', + 'pattern': '.', + 'action': ['python', './tools/clang/scripts/update.py'], + 'condition': 'not build_with_chromium', + }, +] + +recursedeps = [ + # buildtools provides clang_format, libc++, and libc++abi + 'buildtools', +] diff --git a/third_party/glslang/External/CMakeLists.txt b/third_party/glslang/External/CMakeLists.txt new file mode 100644 index 0000000..cbabd2e --- /dev/null +++ b/third_party/glslang/External/CMakeLists.txt @@ -0,0 +1,77 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Suppress all warnings from external projects. +set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS -w) + +if(BUILD_TESTING) + if(TARGET gmock) + message(STATUS "Google Mock already configured - use it") + elseif(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/googletest) + # We need to make sure Google Test does not mess up with the + # global CRT settings on Windows. + if(WIN32) + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + endif(WIN32) + # EXCLUDE_FROM_ALL keeps the install target from installing GTEST files. + add_subdirectory(googletest EXCLUDE_FROM_ALL) + set(GTEST_TARGETS + gtest + gtest_main + gmock + gmock_main) + foreach(target ${GTEST_TARGETS}) + set_property(TARGET ${target} PROPERTY FOLDER gtest) + endforeach() + mark_as_advanced(gmock_build_tests + BUILD_GMOCK + BUILD_GTEST + BUILD_SHARED_LIBS + gtest_build_samples + gtest_build_tests + gtest_disable_pthreads + gtest_force_shared_crt + gtest_hide_internal_symbols) + else() + message(STATUS + "Google Mock was not found - tests based on that will not build") + endif() +endif() + +if(ENABLE_OPT AND NOT TARGET SPIRV-Tools-opt) + if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/spirv-tools) + set(SPIRV_SKIP_TESTS ON CACHE BOOL "Skip building SPIRV-Tools tests") + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/spirv-tools spirv-tools) + endif() +endif() + diff --git a/third_party/glslang/LICENSE b/third_party/glslang/LICENSE new file mode 100644 index 0000000..39d404f --- /dev/null +++ b/third_party/glslang/LICENSE @@ -0,0 +1,75 @@ +Copyright (C) 2015-2018 Google, Inc. +Copyright (C) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + Neither the name of 3Dlabs Inc. Ltd. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/glslang/LICENSE.txt b/third_party/glslang/LICENSE.txt new file mode 100644 index 0000000..5f58565 --- /dev/null +++ b/third_party/glslang/LICENSE.txt @@ -0,0 +1,384 @@ +Here, glslang proper means core GLSL parsing, HLSL parsing, and SPIR-V code +generation. Glslang proper requires use of a number of licenses, one that covers +preprocessing and others that covers non-preprocessing. + +Bison was removed long ago. You can build glslang from the source grammar, +using tools of your choice, without using bison or any bison files. + +Other parts, outside of glslang proper, include: + +- gl_types.h, only needed for OpenGL-like reflection, and can be left out of + a parse and codegen project. See it for its license. + +- update_glslang_sources.py, which is not part of the project proper and does + not need to be used. + +- the SPIR-V "remapper", which is optional, but has the same license as + glslang proper + +- Google tests and SPIR-V tools, and anything in the external subdirectory + are external and optional; see them for their respective licenses. + +-------------------------------------------------------------------------------- + +The core of glslang-proper, minus the preprocessor is licenced as follows: + +-------------------------------------------------------------------------------- +3-Clause BSD License +-------------------------------------------------------------------------------- + +// +// Copyright (C) 2015-2018 Google, Inc. +// Copyright (C) +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + + +-------------------------------------------------------------------------------- +2-Clause BSD License +-------------------------------------------------------------------------------- + +Copyright 2020 The Khronos Group Inc + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +-------------------------------------------------------------------------------- +The MIT License +-------------------------------------------------------------------------------- + +Copyright 2020 The Khronos Group Inc + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +-------------------------------------------------------------------------------- +APACHE LICENSE, VERSION 2.0 +-------------------------------------------------------------------------------- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +-------------------------------------------------------------------------------- +GPL 3 with special bison exception +-------------------------------------------------------------------------------- + + Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. + +-------------------------------------------------------------------------------- +================================================================================ +-------------------------------------------------------------------------------- + +The preprocessor has the core licenses stated above, plus an additional licence: + +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ diff --git a/third_party/glslang/OGLCompilersDLL/CMakeLists.txt b/third_party/glslang/OGLCompilersDLL/CMakeLists.txt new file mode 100644 index 0000000..0b007d4 --- /dev/null +++ b/third_party/glslang/OGLCompilersDLL/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set(SOURCES InitializeDll.cpp InitializeDll.h) + +add_library(OGLCompiler STATIC ${SOURCES}) +set_property(TARGET OGLCompiler PROPERTY FOLDER glslang) +set_property(TARGET OGLCompiler PROPERTY POSITION_INDEPENDENT_CODE ON) + +if(WIN32) + source_group("Source" FILES ${SOURCES}) +endif(WIN32) + +if(ENABLE_GLSLANG_INSTALL) + install(TARGETS OGLCompiler EXPORT OGLCompilerTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(EXPORT OGLCompilerTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/OGLCompilersDLL/InitializeDll.cpp b/third_party/glslang/OGLCompilersDLL/InitializeDll.cpp new file mode 100644 index 0000000..abea910 --- /dev/null +++ b/third_party/glslang/OGLCompilersDLL/InitializeDll.cpp @@ -0,0 +1,165 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#define SH_EXPORTING + +#include + +#include "InitializeDll.h" +#include "../glslang/Include/InitializeGlobals.h" +#include "../glslang/Public/ShaderLang.h" +#include "../glslang/Include/PoolAlloc.h" + +namespace glslang { + +OS_TLSIndex ThreadInitializeIndex = OS_INVALID_TLS_INDEX; + +// Per-process initialization. +// Needs to be called at least once before parsing, etc. is done. +// Will also do thread initialization for the calling thread; other +// threads will need to do that explicitly. +bool InitProcess() +{ + glslang::GetGlobalLock(); + + if (ThreadInitializeIndex != OS_INVALID_TLS_INDEX) { + // + // Function is re-entrant. + // + + glslang::ReleaseGlobalLock(); + return true; + } + + ThreadInitializeIndex = OS_AllocTLSIndex(); + + if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "InitProcess(): Failed to allocate TLS area for init flag"); + + glslang::ReleaseGlobalLock(); + return false; + } + + if (! InitializePoolIndex()) { + assert(0 && "InitProcess(): Failed to initialize global pool"); + + glslang::ReleaseGlobalLock(); + return false; + } + + if (! InitThread()) { + assert(0 && "InitProcess(): Failed to initialize thread"); + + glslang::ReleaseGlobalLock(); + return false; + } + + glslang::ReleaseGlobalLock(); + return true; +} + +// Per-thread scoped initialization. +// Must be called at least once by each new thread sharing the +// symbol tables, etc., needed to parse. +bool InitThread() +{ + // + // This function is re-entrant + // + if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "InitThread(): Process hasn't been initalised."); + return false; + } + + if (OS_GetTLSValue(ThreadInitializeIndex) != 0) + return true; + + if (! OS_SetTLSValue(ThreadInitializeIndex, (void *)1)) { + assert(0 && "InitThread(): Unable to set init flag."); + return false; + } + + glslang::SetThreadPoolAllocator(nullptr); + + return true; +} + +// Not necessary to call this: InitThread() is reentrant, and the need +// to do per thread tear down has been removed. +// +// This is kept, with memory management removed, to satisfy any exiting +// calls to it that rely on it. +bool DetachThread() +{ + bool success = true; + + if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) + return true; + + // + // Function is re-entrant and this thread may not have been initialized. + // + if (OS_GetTLSValue(ThreadInitializeIndex) != 0) { + if (!OS_SetTLSValue(ThreadInitializeIndex, (void *)0)) { + assert(0 && "DetachThread(): Unable to clear init flag."); + success = false; + } + } + + return success; +} + +// Not necessary to call this: InitProcess() is reentrant. +// +// This is kept, with memory management removed, to satisfy any exiting +// calls to it that rely on it. +// +// Users of glslang should call shFinalize() or glslang::FinalizeProcess() for +// process-scoped memory tear down. +bool DetachProcess() +{ + bool success = true; + + if (ThreadInitializeIndex == OS_INVALID_TLS_INDEX) + return true; + + success = DetachThread(); + + OS_FreeTLSIndex(ThreadInitializeIndex); + ThreadInitializeIndex = OS_INVALID_TLS_INDEX; + + return success; +} + +} // end namespace glslang diff --git a/third_party/glslang/OGLCompilersDLL/InitializeDll.h b/third_party/glslang/OGLCompilersDLL/InitializeDll.h new file mode 100644 index 0000000..661cee4 --- /dev/null +++ b/third_party/glslang/OGLCompilersDLL/InitializeDll.h @@ -0,0 +1,49 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef __INITIALIZEDLL_H +#define __INITIALIZEDLL_H + +#include "../glslang/OSDependent/osinclude.h" + +namespace glslang { + +bool InitProcess(); +bool InitThread(); +bool DetachThread(); // not called from standalone, perhaps other tools rely on parts of it +bool DetachProcess(); // not called from standalone, perhaps other tools rely on parts of it + +} // end namespace glslang + +#endif // __INITIALIZEDLL_H + diff --git a/third_party/glslang/OGLCompilersDLL/tnt/CMakeLists.txt b/third_party/glslang/OGLCompilersDLL/tnt/CMakeLists.txt new file mode 100644 index 0000000..322f50f --- /dev/null +++ b/third_party/glslang/OGLCompilersDLL/tnt/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SOURCES ../InitializeDll.cpp ../InitializeDll.h) + +add_library(OGLCompiler STATIC ${SOURCES}) +set_property(TARGET OGLCompiler PROPERTY FOLDER glslang) +set_property(TARGET OGLCompiler PROPERTY POSITION_INDEPENDENT_CODE ON) + +if(WIN32) + source_group("Source" FILES ${SOURCES}) +endif(WIN32) + +if(ENABLE_GLSLANG_INSTALL) + install(TARGETS OGLCompiler + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/README-spirv-remap.txt b/third_party/glslang/README-spirv-remap.txt new file mode 100644 index 0000000..3e5288a --- /dev/null +++ b/third_party/glslang/README-spirv-remap.txt @@ -0,0 +1,137 @@ + +VERSION +-------------------------------------------------------------------------------- +spirv-remap 0.97 + +INTRO: +-------------------------------------------------------------------------------- +spirv-remap is a utility to improve compression of SPIR-V binary files via +entropy reduction, plus optional stripping of debug information and +load/store optimization. It transforms SPIR-V to SPIR-V, remapping IDs. The +resulting modules have an increased ID range (IDs are not as tightly packed +around zero), but will compress better when multiple modules are compressed +together, since compressor's dictionary can find better cross module +commonality. + +Remapping is accomplished via canonicalization. Thus, modules can be +compressed one at a time with no loss of quality relative to operating on +many modules at once. The command line tool operates on multiple modules +only in the trivial repetition sense, for ease of use. The remapper API +only accepts a single module at a time. + +There are two modes of use: command line, and a C++11 API. Both are +described below. + +spirv-remap is currently in an alpha state. Although there are no known +remapping defects, it has only been exercised on one real world game shader +workload. + + +FEEDBACK +-------------------------------------------------------------------------------- +Report defects, enhancements requests, code improvements, etc to: + spvremapper@lunarg.com + + +COMMAND LINE USAGE: +-------------------------------------------------------------------------------- +Examples are given with a verbosity of one (-v), but more verbosity can be +had via -vv, -vvv, etc, or an integer parameter to --verbose, such as +"--verbose 4". With no verbosity, the command is silent and returns 0 on +success, and a positive integer error on failure. + +Pre-built binaries for several OSs are available. Examples presented are +for Linux. Command line arguments can be provided in any order. + +1. Basic ID remapping + +Perform ID remapping on all shaders in "*.spv", writing new files with +the same basenames to /tmp/out_dir. + + spirv-remap -v --map all --input *.spv --output /tmp/out_dir + +2. Perform all possible size reductions + + spirv-remap-linux-64 -v --do-everything --input *.spv --output /tmp/out_dir + +Note that --do-everything is a synonym for: + + --map all --dce all --opt all --strip all + +API USAGE: +-------------------------------------------------------------------------------- + +The public interface to the remapper is defined in SPIRV/SPVRemapper.h as follows: + +namespace spv { + +class spirvbin_t +{ +public: + enum Options { ... }; + spirvbin_t(int verbose = 0); // construct + + // remap an existing binary in memory + void remap(std::vector& spv, std::uint32_t opts = DO_EVERYTHING); + + // Type for error/log handler functions + typedef std::function errorfn_t; + typedef std::function logfn_t; + + // Register error/log handling functions (can be c/c++ fn, lambda fn, or functor) + static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; } + static void registerLogHandler(logfn_t handler) { logHandler = handler; } +}; + +} // namespace spv + +The class definition is in SPVRemapper.cpp. + +remap() accepts an std::vector of SPIR-V words, modifies them per the +request given in 'opts', and leaves the 'spv' container with the result. +It is safe to instantiate one spirvbin_t per thread and process a different +SPIR-V in each. + +The "opts" parameter to remap() accepts a bit mask of desired remapping +options. See REMAPPING AND OPTIMIZATION OPTIONS. + +On error, the function supplied to registerErrorHandler() will be invoked. +This can be a standard C/C++ function, a lambda function, or a functor. +The default handler simply calls exit(5); The error handler is a static +member, so need only be set up once, not once per spirvbin_t instance. + +Log messages are supplied to registerLogHandler(). By default, log +messages are eaten silently. The log handler is also a static member. + +BUILD DEPENDENCIES: +-------------------------------------------------------------------------------- + 1. C++11 compatible compiler + 2. cmake + 3. glslang + + +BUILDING +-------------------------------------------------------------------------------- +The standalone remapper is built along side glslangValidator through its +normal build process. + + +REMAPPING AND OPTIMIZATION OPTIONS +-------------------------------------------------------------------------------- +API: + These are bits defined under spv::spirvbin_t::, and can be + bitwise or-ed together as desired. + + MAP_TYPES = canonicalize type IDs + MAP_NAMES = canonicalize named data + MAP_FUNCS = canonicalize function bodies + DCE_FUNCS = remove dead functions + DCE_VARS = remove dead variables + DCE_TYPES = remove dead types + OPT_LOADSTORE = optimize unneeded load/stores + MAP_ALL = (MAP_TYPES | MAP_NAMES | MAP_FUNCS) + DCE_ALL = (DCE_FUNCS | DCE_VARS | DCE_TYPES) + OPT_ALL = (OPT_LOADSTORE) + ALL_BUT_STRIP = (MAP_ALL | DCE_ALL | OPT_ALL) + DO_EVERYTHING = (STRIP | ALL_BUT_STRIP) + diff --git a/third_party/glslang/README.md b/third_party/glslang/README.md new file mode 100755 index 0000000..7ad4ace --- /dev/null +++ b/third_party/glslang/README.md @@ -0,0 +1,470 @@ +# News + +1. Visual Studio 2013 is no longer supported + + [As scheduled](https://github.com/KhronosGroup/glslang/blob/9eef54b2513ca6b40b47b07d24f453848b65c0df/README.md#planned-deprecationsremovals), +Microsoft Visual Studio 2013 is no longer officially supported. \ + Please upgrade to at least Visual Studio 2015. + +2. The versioning scheme is being improved, and you might notice some differences. This is currently WIP, but will be coming soon. See, for example, PR #2277. + +3. If you get a new **compilation error due to a missing header**, it might be caused by this planned removal: + +**SPIRV Folder, 1-May, 2020.** Glslang, when installed through CMake, +will install a `SPIRV` folder into `${CMAKE_INSTALL_INCLUDEDIR}`. +This `SPIRV` folder is being moved to `glslang/SPIRV`. +During the transition the `SPIRV` folder will be installed into both locations. +The old install of `SPIRV/` will be removed as a CMake install target no sooner than May 1, 2020. +See issue #1964. + +If people are only using this location to get spirv.hpp, I recommend they get that from [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) instead. + +[![Build Status](https://travis-ci.org/KhronosGroup/glslang.svg?branch=master)](https://travis-ci.org/KhronosGroup/glslang) +[![Build status](https://ci.appveyor.com/api/projects/status/q6fi9cb0qnhkla68/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/glslang/branch/master) + +# Glslang Components and Status + +There are several components: + +### Reference Validator and GLSL/ESSL -> AST Front End + +An OpenGL GLSL and OpenGL|ES GLSL (ESSL) front-end for reference validation and translation of GLSL/ESSL into an internal abstract syntax tree (AST). + +**Status**: Virtually complete, with results carrying similar weight as the specifications. + +### HLSL -> AST Front End + +An HLSL front-end for translation of an approximation of HLSL to glslang's AST form. + +**Status**: Partially complete. Semantics are not reference quality and input is not validated. +This is in contrast to the [DXC project](https://github.com/Microsoft/DirectXShaderCompiler), which receives a much larger investment and attempts to have definitive/reference-level semantics. + +See [issue 362](https://github.com/KhronosGroup/glslang/issues/362) and [issue 701](https://github.com/KhronosGroup/glslang/issues/701) for current status. + +### AST -> SPIR-V Back End + +Translates glslang's AST to the Khronos-specified SPIR-V intermediate language. + +**Status**: Virtually complete. + +### Reflector + +An API for getting reflection information from the AST, reflection types/variables/etc. from the HLL source (not the SPIR-V). + +**Status**: There is a large amount of functionality present, but no specification/goal to measure completeness against. It is accurate for the input HLL and AST, but only approximate for what would later be emitted for SPIR-V. + +### Standalone Wrapper + +`glslangValidator` is command-line tool for accessing the functionality above. + +Status: Complete. + +Tasks waiting to be done are documented as GitHub issues. + +## Other References + +Also see the Khronos landing page for glslang as a reference front end: + +https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/ + +The above page, while not kept up to date, includes additional information regarding glslang as a reference validator. + +# How to Use Glslang + +## Execution of Standalone Wrapper + +To use the standalone binary form, execute `glslangValidator`, and it will print +a usage statement. Basic operation is to give it a file containing a shader, +and it will print out warnings/errors and optionally an AST. + +The applied stage-specific rules are based on the file extension: +* `.vert` for a vertex shader +* `.tesc` for a tessellation control shader +* `.tese` for a tessellation evaluation shader +* `.geom` for a geometry shader +* `.frag` for a fragment shader +* `.comp` for a compute shader + +There is also a non-shader extension +* `.conf` for a configuration file of limits, see usage statement for example + +## Building (CMake) + +Instead of building manually, you can also download the binaries for your +platform directly from the [master-tot release][master-tot-release] on GitHub. +Those binaries are automatically uploaded by the buildbots after successful +testing and they always reflect the current top of the tree of the master +branch. + +### Dependencies + +* A C++11 compiler. + (For MSVS: use 2015 or later.) +* [CMake][cmake]: for generating compilation targets. +* make: _Linux_, ninja is an alternative, if configured. +* [Python 3.x][python]: for executing SPIRV-Tools scripts. (Optional if not using SPIRV-Tools and the 'External' subdirectory does not exist.) +* [bison][bison]: _optional_, but needed when changing the grammar (glslang.y). +* [googletest][googletest]: _optional_, but should use if making any changes to glslang. + +### Build steps + +The following steps assume a Bash shell. On Windows, that could be the Git Bash +shell or some other shell of your choosing. + +#### 1) Check-Out this project + +```bash +cd +git clone https://github.com/KhronosGroup/glslang.git +``` + +#### 2) Check-Out External Projects + +```bash +cd +git clone https://github.com/google/googletest.git External/googletest +``` + +If you wish to assure that SPIR-V generated from HLSL is legal for Vulkan, +wish to invoke -Os to reduce SPIR-V size from HLSL or GLSL, or wish to run the +integrated test suite, install spirv-tools with this: + +```bash +./update_glslang_sources.py +``` + +#### 3) Configure + +Assume the source directory is `$SOURCE_DIR` and the build directory is +`$BUILD_DIR`. First ensure the build directory exists, then navigate to it: + +```bash +mkdir -p $BUILD_DIR +cd $BUILD_DIR +``` + +For building on Linux: + +```bash +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$(pwd)/install" $SOURCE_DIR +# "Release" (for CMAKE_BUILD_TYPE) could also be "Debug" or "RelWithDebInfo" +``` + +For building on Android: +```bash +cmake $SOURCE_DIR -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DANDROID_ABI=arm64-v8a -DCMAKE_BUILD_TYPE=Release -DANDROID_STL=c++_static -DANDROID_PLATFORM=android-24 -DCMAKE_SYSTEM_NAME=Android -DANDROID_TOOLCHAIN=clang -DANDROID_ARM_MODE=arm -DCMAKE_MAKE_PROGRAM=$ANDROID_NDK_ROOT/prebuilt/linux-x86_64/bin/make -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake +# If on Windows will be -DCMAKE_MAKE_PROGRAM=%ANDROID_NDK_ROOT%\prebuilt\windows-x86_64\bin\make.exe +# -G is needed for building on Windows +# -DANDROID_ABI can also be armeabi-v7a for 32 bit +``` + +For building on Windows: + +```bash +cmake $SOURCE_DIR -DCMAKE_INSTALL_PREFIX="$(pwd)/install" +# The CMAKE_INSTALL_PREFIX part is for testing (explained later). +``` + +The CMake GUI also works for Windows (version 3.4.1 tested). + +Also, consider using `git config --global core.fileMode false` (or with `--local`) on Windows +to prevent the addition of execution permission on files. + +#### 4) Build and Install + +```bash +# for Linux: +make -j4 install + +# for Windows: +cmake --build . --config Release --target install +# "Release" (for --config) could also be "Debug", "MinSizeRel", or "RelWithDebInfo" +``` + +If using MSVC, after running CMake to configure, use the +Configuration Manager to check the `INSTALL` project. + +### Building (GN) + +glslang can also be built with the [GN build system](https://gn.googlesource.com/gn/). + +#### 1) Install `depot_tools` + +Download [depot_tools.zip](https://storage.googleapis.com/chrome-infra/depot_tools.zip), +extract to a directory, and add this directory to your `PATH`. + +#### 2) Synchronize dependencies and generate build files + +This only needs to be done once after updating `glslang`. + +With the current directory set to your `glslang` checkout, type: + +```bash +./update_glslang_sources.py +gclient sync --gclientfile=standalone.gclient +gn gen out/Default +``` + +#### 3) Build + +With the current directory set to your `glslang` checkout, type: + +```bash +cd out/Default +ninja +``` + +### If you need to change the GLSL grammar + +The grammar in `glslang/MachineIndependent/glslang.y` has to be recompiled with +bison if it changes, the output files are committed to the repo to avoid every +developer needing to have bison configured to compile the project when grammar +changes are quite infrequent. For windows you can get binaries from +[GnuWin32][bison-gnu-win32]. + +The command to rebuild is: + +```bash +m4 -P MachineIndependent/glslang.m4 > MachineIndependent/glslang.y +bison --defines=MachineIndependent/glslang_tab.cpp.h + -t MachineIndependent/glslang.y + -o MachineIndependent/glslang_tab.cpp +``` + +The above commands are also available in the bash script in `updateGrammar`, +when executed from the glslang subdirectory of the glslang repository. +With no arguments it builds the full grammar, and with a "web" argument, +the web grammar subset (see more about the web subset in the next section). + +### Building to WASM for the Web and Node +### Building a standalone JS/WASM library for the Web and Node + +Use the steps in [Build Steps](#build-steps), with the following notes/exceptions: +* `emsdk` needs to be present in your executable search path, *PATH* for + Bash-like environments: + + [Instructions located here](https://emscripten.org/docs/getting_started/downloads.html#sdk-download-and-install) +* Wrap cmake call: `emcmake cmake` +* Set `-DBUILD_TESTING=OFF -DENABLE_OPT=OFF -DINSTALL_GTEST=OFF`. +* Set `-DENABLE_HLSL=OFF` if HLSL is not needed. +* For a standalone JS/WASM library, turn on `-DENABLE_GLSLANG_JS=ON`. +* For building a minimum-size web subset of core glslang: + + turn on `-DENABLE_GLSLANG_WEBMIN=ON` (disables HLSL) + + execute `updateGrammar web` from the glslang subdirectory + (or if using your own scripts, `m4` needs a `-DGLSLANG_WEB` argument) + + optionally, for GLSL compilation error messages, turn on + `-DENABLE_GLSLANG_WEBMIN_DEVEL=ON` +* To get a fully minimized build, make sure to use `brotli` to compress the .js + and .wasm files + +Example: + +```sh +emcmake cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GLSLANG_JS=ON \ + -DENABLE_HLSL=OFF -DBUILD_TESTING=OFF -DENABLE_OPT=OFF -DINSTALL_GTEST=OFF .. +``` + +## Building glslang - Using vcpkg + +You can download and install glslang using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + ./vcpkg install glslang + +The glslang port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +## Testing + +Right now, there are two test harnesses existing in glslang: one is [Google +Test](gtests/), one is the [`runtests` script](Test/runtests). The former +runs unit tests and single-shader single-threaded integration tests, while +the latter runs multiple-shader linking tests and multi-threaded tests. + +### Running tests + +The [`runtests` script](Test/runtests) requires compiled binaries to be +installed into `$BUILD_DIR/install`. Please make sure you have supplied the +correct configuration to CMake (using `-DCMAKE_INSTALL_PREFIX`) when building; +otherwise, you may want to modify the path in the `runtests` script. + +Running Google Test-backed tests: + +```bash +cd $BUILD_DIR + +# for Linux: +ctest + +# for Windows: +ctest -C {Debug|Release|RelWithDebInfo|MinSizeRel} + +# or, run the test binary directly +# (which gives more fine-grained control like filtering): +/glslangtests +``` + +Running `runtests` script-backed tests: + +```bash +cd $SOURCE_DIR/Test && ./runtests +``` + +If some tests fail with validation errors, there may be a mismatch between the +version of `spirv-val` on the system and the version of glslang. In this +case, it is necessary to run `update_glslang_sources.py`. See "Check-Out +External Projects" above for more details. + +### Contributing tests + +Test results should always be included with a pull request that modifies +functionality. + +If you are writing unit tests, please use the Google Test framework and +place the tests under the `gtests/` directory. + +Integration tests are placed in the `Test/` directory. It contains test input +and a subdirectory `baseResults/` that contains the expected results of the +tests. Both the tests and `baseResults/` are under source-code control. + +Google Test runs those integration tests by reading the test input, compiling +them, and then compare against the expected results in `baseResults/`. The +integration tests to run via Google Test is registered in various +`gtests/*.FromFile.cpp` source files. `glslangtests` provides a command-line +option `--update-mode`, which, if supplied, will overwrite the golden files +under the `baseResults/` directory with real output from that invocation. +For more information, please check `gtests/` directory's +[README](gtests/README.md). + +For the `runtests` script, it will generate current results in the +`localResults/` directory and `diff` them against the `baseResults/`. +When you want to update the tracked test results, they need to be +copied from `localResults/` to `baseResults/`. This can be done by +the `bump` shell script. + +You can add your own private list of tests, not tracked publicly, by using +`localtestlist` to list non-tracked tests. This is automatically read +by `runtests` and included in the `diff` and `bump` process. + +## Programmatic Interfaces + +Another piece of software can programmatically translate shaders to an AST +using one of two different interfaces: +* A new C++ class-oriented interface, or +* The original C functional interface + +The `main()` in `StandAlone/StandAlone.cpp` shows examples using both styles. + +### C++ Class Interface (new, preferred) + +This interface is in roughly the last 1/3 of `ShaderLang.h`. It is in the +glslang namespace and contains the following, here with suggested calls +for generating SPIR-V: + +```cxx +const char* GetEsslVersionString(); +const char* GetGlslVersionString(); +bool InitializeProcess(); +void FinalizeProcess(); + +class TShader + setStrings(...); + setEnvInput(EShSourceHlsl or EShSourceGlsl, stage, EShClientVulkan or EShClientOpenGL, 100); + setEnvClient(EShClientVulkan or EShClientOpenGL, EShTargetVulkan_1_0 or EShTargetVulkan_1_1 or EShTargetOpenGL_450); + setEnvTarget(EShTargetSpv, EShTargetSpv_1_0 or EShTargetSpv_1_3); + bool parse(...); + const char* getInfoLog(); + +class TProgram + void addShader(...); + bool link(...); + const char* getInfoLog(); + Reflection queries +``` + +For just validating (not generating code), substitute these calls: + +```cxx + setEnvInput(EShSourceHlsl or EShSourceGlsl, stage, EShClientNone, 0); + setEnvClient(EShClientNone, 0); + setEnvTarget(EShTargetNone, 0); +``` + +See `ShaderLang.h` and the usage of it in `StandAlone/StandAlone.cpp` for more +details. There is a block comment giving more detail above the calls for +`setEnvInput, setEnvClient, and setEnvTarget`. + +### C Functional Interface (original) + +This interface is in roughly the first 2/3 of `ShaderLang.h`, and referred to +as the `Sh*()` interface, as all the entry points start `Sh`. + +The `Sh*()` interface takes a "compiler" call-back object, which it calls after +building call back that is passed the AST and can then execute a back end on it. + +The following is a simplified resulting run-time call stack: + +```c +ShCompile(shader, compiler) -> compiler(AST) -> +``` + +In practice, `ShCompile()` takes shader strings, default version, and +warning/error and other options for controlling compilation. + +## Basic Internal Operation + +* Initial lexical analysis is done by the preprocessor in + `MachineIndependent/Preprocessor`, and then refined by a GLSL scanner + in `MachineIndependent/Scan.cpp`. There is currently no use of flex. + +* Code is parsed using bison on `MachineIndependent/glslang.y` with the + aid of a symbol table and an AST. The symbol table is not passed on to + the back-end; the intermediate representation stands on its own. + The tree is built by the grammar productions, many of which are + offloaded into `ParseHelper.cpp`, and by `Intermediate.cpp`. + +* The intermediate representation is very high-level, and represented + as an in-memory tree. This serves to lose no information from the + original program, and to have efficient transfer of the result from + parsing to the back-end. In the AST, constants are propagated and + folded, and a very small amount of dead code is eliminated. + + To aid linking and reflection, the last top-level branch in the AST + lists all global symbols. + +* The primary algorithm of the back-end compiler is to traverse the + tree (high-level intermediate representation), and create an internal + object code representation. There is an example of how to do this + in `MachineIndependent/intermOut.cpp`. + +* Reduction of the tree to a linear byte-code style low-level intermediate + representation is likely a good way to generate fully optimized code. + +* There is currently some dead old-style linker-type code still lying around. + +* Memory pool: parsing uses types derived from C++ `std` types, using a + custom allocator that puts them in a memory pool. This makes allocation + of individual container/contents just few cycles and deallocation free. + This pool is popped after the AST is made and processed. + + The use is simple: if you are going to call `new`, there are three cases: + + - the object comes from the pool (its base class has the macro + `POOL_ALLOCATOR_NEW_DELETE` in it) and you do not have to call `delete` + + - it is a `TString`, in which case call `NewPoolTString()`, which gets + it from the pool, and there is no corresponding `delete` + + - the object does not come from the pool, and you have to do normal + C++ memory management of what you `new` + +* Features can be protected by version/extension/stage/profile: + See the comment in `glslang/MachineIndependent/Versions.cpp`. + +[cmake]: https://cmake.org/ +[python]: https://www.python.org/ +[bison]: https://www.gnu.org/software/bison/ +[googletest]: https://github.com/google/googletest +[bison-gnu-win32]: http://gnuwin32.sourceforge.net/packages/bison.htm +[master-tot-release]: https://github.com/KhronosGroup/glslang/releases/tag/master-tot diff --git a/third_party/glslang/SPIRV/CInterface/spirv_c_interface.cpp b/third_party/glslang/SPIRV/CInterface/spirv_c_interface.cpp new file mode 100644 index 0000000..a0790f4 --- /dev/null +++ b/third_party/glslang/SPIRV/CInterface/spirv_c_interface.cpp @@ -0,0 +1,110 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include "glslang/Include/glslang_c_interface.h" + +#include "SPIRV/GlslangToSpv.h" +#include "SPIRV/Logger.h" +#include "SPIRV/SpvTools.h" + +typedef struct glslang_program_s { + glslang::TProgram* program; + std::vector spirv; + std::string loggerMessages; +} glslang_program_t; + +static EShLanguage c_shader_stage(glslang_stage_t stage) +{ + switch (stage) { + case GLSLANG_STAGE_VERTEX: + return EShLangVertex; + case GLSLANG_STAGE_TESSCONTROL: + return EShLangTessControl; + case GLSLANG_STAGE_TESSEVALUATION: + return EShLangTessEvaluation; + case GLSLANG_STAGE_GEOMETRY: + return EShLangGeometry; + case GLSLANG_STAGE_FRAGMENT: + return EShLangFragment; + case GLSLANG_STAGE_COMPUTE: + return EShLangCompute; + case GLSLANG_STAGE_RAYGEN_NV: + return EShLangRayGen; + case GLSLANG_STAGE_INTERSECT_NV: + return EShLangIntersect; + case GLSLANG_STAGE_ANYHIT_NV: + return EShLangAnyHit; + case GLSLANG_STAGE_CLOSESTHIT_NV: + return EShLangClosestHit; + case GLSLANG_STAGE_MISS_NV: + return EShLangMiss; + case GLSLANG_STAGE_CALLABLE_NV: + return EShLangCallable; + case GLSLANG_STAGE_TASK_NV: + return EShLangTaskNV; + case GLSLANG_STAGE_MESH_NV: + return EShLangMeshNV; + default: + break; + } + return EShLangCount; +} + +GLSLANG_EXPORT void glslang_program_SPIRV_generate(glslang_program_t* program, glslang_stage_t stage) +{ + spv::SpvBuildLogger logger; + glslang::SpvOptions spvOptions; + spvOptions.validate = true; + + const glslang::TIntermediate* intermediate = program->program->getIntermediate(c_shader_stage(stage)); + + glslang::GlslangToSpv(*intermediate, program->spirv, &logger, &spvOptions); + + program->loggerMessages = logger.getAllMessages(); +} + +GLSLANG_EXPORT size_t glslang_program_SPIRV_get_size(glslang_program_t* program) { return program->spirv.size(); } + +GLSLANG_EXPORT void glslang_program_SPIRV_get(glslang_program_t* program, unsigned int* out) +{ + memcpy(out, program->spirv.data(), program->spirv.size() * sizeof(unsigned int)); +} + +GLSLANG_EXPORT unsigned int* glslang_program_SPIRV_get_ptr(glslang_program_t* program) +{ + return program->spirv.data(); +} + +GLSLANG_EXPORT const char* glslang_program_SPIRV_get_messages(glslang_program_t* program) +{ + return program->loggerMessages.empty() ? nullptr : program->loggerMessages.c_str(); +} diff --git a/third_party/glslang/SPIRV/CMakeLists.txt b/third_party/glslang/SPIRV/CMakeLists.txt new file mode 100644 index 0000000..d699dad --- /dev/null +++ b/third_party/glslang/SPIRV/CMakeLists.txt @@ -0,0 +1,138 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set(SOURCES + GlslangToSpv.cpp + InReadableOrder.cpp + Logger.cpp + SpvBuilder.cpp + SpvPostProcess.cpp + doc.cpp + SpvTools.cpp + disassemble.cpp + CInterface/spirv_c_interface.cpp) + +set(SPVREMAP_SOURCES + SPVRemapper.cpp + doc.cpp) + +set(HEADERS + bitutils.h + spirv.hpp + GLSL.std.450.h + GLSL.ext.EXT.h + GLSL.ext.KHR.h + GlslangToSpv.h + hex_float.h + Logger.h + SpvBuilder.h + spvIR.h + doc.h + SpvTools.h + disassemble.h + GLSL.ext.AMD.h + GLSL.ext.NV.h + NonSemanticDebugPrintf.h) + +set(SPVREMAP_HEADERS + SPVRemapper.h + doc.h) + +add_library(SPIRV ${LIB_TYPE} ${SOURCES} ${HEADERS}) +set_property(TARGET SPIRV PROPERTY FOLDER glslang) +set_property(TARGET SPIRV PROPERTY POSITION_INDEPENDENT_CODE ON) +target_include_directories(SPIRV PUBLIC + $ + $) + +glslang_add_build_info_dependency(SPIRV) + +if (ENABLE_SPVREMAPPER) + add_library(SPVRemapper ${LIB_TYPE} ${SPVREMAP_SOURCES} ${SPVREMAP_HEADERS}) + set_property(TARGET SPVRemapper PROPERTY FOLDER glslang) + set_property(TARGET SPVRemapper PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +if(WIN32 AND BUILD_SHARED_LIBS) + set_target_properties(SPIRV PROPERTIES PREFIX "") + if (ENABLE_SPVREMAPPER) + set_target_properties(SPVRemapper PROPERTIES PREFIX "") + endif() +endif() + +if(ENABLE_OPT) + target_include_directories(SPIRV + PRIVATE ${spirv-tools_SOURCE_DIR}/include + PRIVATE ${spirv-tools_SOURCE_DIR}/source + ) + target_link_libraries(SPIRV PRIVATE MachineIndependent SPIRV-Tools-opt) + target_include_directories(SPIRV PUBLIC + $ + $) +else() + target_link_libraries(SPIRV PRIVATE MachineIndependent) +endif(ENABLE_OPT) + +if(WIN32) + source_group("Source" FILES ${SOURCES} ${HEADERS}) + source_group("Source" FILES ${SPVREMAP_SOURCES} ${SPVREMAP_HEADERS}) +endif(WIN32) + +if(ENABLE_GLSLANG_INSTALL) + if(BUILD_SHARED_LIBS) + if (ENABLE_SPVREMAPPER) + install(TARGETS SPVRemapper EXPORT SPVRemapperTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + install(TARGETS SPIRV EXPORT SPIRVTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else() + if (ENABLE_SPVREMAPPER) + install(TARGETS SPVRemapper EXPORT SPVRemapperTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + install(TARGETS SPIRV EXPORT SPIRVTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + if (ENABLE_SPVREMAPPER) + install(EXPORT SPVRemapperTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + endif() + + install(EXPORT SPIRVTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + + install(FILES ${HEADERS} ${SPVREMAP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/glslang/SPIRV/) +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/SPIRV/GLSL.ext.AMD.h b/third_party/glslang/SPIRV/GLSL.ext.AMD.h new file mode 100644 index 0000000..009d2f1 --- /dev/null +++ b/third_party/glslang/SPIRV/GLSL.ext.AMD.h @@ -0,0 +1,108 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextAMD_H +#define GLSLextAMD_H + +static const int GLSLextAMDVersion = 100; +static const int GLSLextAMDRevision = 7; + +// SPV_AMD_shader_ballot +static const char* const E_SPV_AMD_shader_ballot = "SPV_AMD_shader_ballot"; + +enum ShaderBallotAMD { + ShaderBallotBadAMD = 0, // Don't use + + SwizzleInvocationsAMD = 1, + SwizzleInvocationsMaskedAMD = 2, + WriteInvocationAMD = 3, + MbcntAMD = 4, + + ShaderBallotCountAMD +}; + +// SPV_AMD_shader_trinary_minmax +static const char* const E_SPV_AMD_shader_trinary_minmax = "SPV_AMD_shader_trinary_minmax"; + +enum ShaderTrinaryMinMaxAMD { + ShaderTrinaryMinMaxBadAMD = 0, // Don't use + + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9, + + ShaderTrinaryMinMaxCountAMD +}; + +// SPV_AMD_shader_explicit_vertex_parameter +static const char* const E_SPV_AMD_shader_explicit_vertex_parameter = "SPV_AMD_shader_explicit_vertex_parameter"; + +enum ShaderExplicitVertexParameterAMD { + ShaderExplicitVertexParameterBadAMD = 0, // Don't use + + InterpolateAtVertexAMD = 1, + + ShaderExplicitVertexParameterCountAMD +}; + +// SPV_AMD_gcn_shader +static const char* const E_SPV_AMD_gcn_shader = "SPV_AMD_gcn_shader"; + +enum GcnShaderAMD { + GcnShaderBadAMD = 0, // Don't use + + CubeFaceIndexAMD = 1, + CubeFaceCoordAMD = 2, + TimeAMD = 3, + + GcnShaderCountAMD +}; + +// SPV_AMD_gpu_shader_half_float +static const char* const E_SPV_AMD_gpu_shader_half_float = "SPV_AMD_gpu_shader_half_float"; + +// SPV_AMD_texture_gather_bias_lod +static const char* const E_SPV_AMD_texture_gather_bias_lod = "SPV_AMD_texture_gather_bias_lod"; + +// SPV_AMD_gpu_shader_int16 +static const char* const E_SPV_AMD_gpu_shader_int16 = "SPV_AMD_gpu_shader_int16"; + +// SPV_AMD_shader_image_load_store_lod +static const char* const E_SPV_AMD_shader_image_load_store_lod = "SPV_AMD_shader_image_load_store_lod"; + +// SPV_AMD_shader_fragment_mask +static const char* const E_SPV_AMD_shader_fragment_mask = "SPV_AMD_shader_fragment_mask"; + +// SPV_AMD_gpu_shader_half_float_fetch +static const char* const E_SPV_AMD_gpu_shader_half_float_fetch = "SPV_AMD_gpu_shader_half_float_fetch"; + +#endif // #ifndef GLSLextAMD_H diff --git a/third_party/glslang/SPIRV/GLSL.ext.EXT.h b/third_party/glslang/SPIRV/GLSL.ext.EXT.h new file mode 100644 index 0000000..20b9e54 --- /dev/null +++ b/third_party/glslang/SPIRV/GLSL.ext.EXT.h @@ -0,0 +1,41 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextEXT_H +#define GLSLextEXT_H + +static const int GLSLextEXTVersion = 100; +static const int GLSLextEXTRevision = 2; + +static const char* const E_SPV_EXT_shader_stencil_export = "SPV_EXT_shader_stencil_export"; +static const char* const E_SPV_EXT_shader_viewport_index_layer = "SPV_EXT_shader_viewport_index_layer"; +static const char* const E_SPV_EXT_fragment_fully_covered = "SPV_EXT_fragment_fully_covered"; +static const char* const E_SPV_EXT_fragment_invocation_density = "SPV_EXT_fragment_invocation_density"; +static const char* const E_SPV_EXT_demote_to_helper_invocation = "SPV_EXT_demote_to_helper_invocation"; +static const char* const E_SPV_EXT_shader_atomic_float_add = "SPV_EXT_shader_atomic_float_add"; +static const char* const E_SPV_EXT_shader_image_int64 = "SPV_EXT_shader_image_int64"; + +#endif // #ifndef GLSLextEXT_H diff --git a/third_party/glslang/SPIRV/GLSL.ext.KHR.h b/third_party/glslang/SPIRV/GLSL.ext.KHR.h new file mode 100644 index 0000000..9610c6e --- /dev/null +++ b/third_party/glslang/SPIRV/GLSL.ext.KHR.h @@ -0,0 +1,54 @@ +/* +** Copyright (c) 2014-2020 The Khronos Group Inc. +** Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextKHR_H +#define GLSLextKHR_H + +static const int GLSLextKHRVersion = 100; +static const int GLSLextKHRRevision = 2; + +static const char* const E_SPV_KHR_shader_ballot = "SPV_KHR_shader_ballot"; +static const char* const E_SPV_KHR_subgroup_vote = "SPV_KHR_subgroup_vote"; +static const char* const E_SPV_KHR_device_group = "SPV_KHR_device_group"; +static const char* const E_SPV_KHR_multiview = "SPV_KHR_multiview"; +static const char* const E_SPV_KHR_shader_draw_parameters = "SPV_KHR_shader_draw_parameters"; +static const char* const E_SPV_KHR_16bit_storage = "SPV_KHR_16bit_storage"; +static const char* const E_SPV_KHR_8bit_storage = "SPV_KHR_8bit_storage"; +static const char* const E_SPV_KHR_storage_buffer_storage_class = "SPV_KHR_storage_buffer_storage_class"; +static const char* const E_SPV_KHR_post_depth_coverage = "SPV_KHR_post_depth_coverage"; +static const char* const E_SPV_KHR_vulkan_memory_model = "SPV_KHR_vulkan_memory_model"; +static const char* const E_SPV_EXT_physical_storage_buffer = "SPV_EXT_physical_storage_buffer"; +static const char* const E_SPV_KHR_physical_storage_buffer = "SPV_KHR_physical_storage_buffer"; +static const char* const E_SPV_EXT_fragment_shader_interlock = "SPV_EXT_fragment_shader_interlock"; +static const char* const E_SPV_KHR_shader_clock = "SPV_KHR_shader_clock"; +static const char* const E_SPV_KHR_non_semantic_info = "SPV_KHR_non_semantic_info"; +static const char* const E_SPV_KHR_ray_tracing = "SPV_KHR_ray_tracing"; +static const char* const E_SPV_KHR_ray_query = "SPV_KHR_ray_query"; +static const char* const E_SPV_KHR_fragment_shading_rate = "SPV_KHR_fragment_shading_rate"; +static const char* const E_SPV_KHR_terminate_invocation = "SPV_KHR_terminate_invocation"; + +#endif // #ifndef GLSLextKHR_H diff --git a/third_party/glslang/SPIRV/GLSL.ext.NV.h b/third_party/glslang/SPIRV/GLSL.ext.NV.h new file mode 100644 index 0000000..50146da --- /dev/null +++ b/third_party/glslang/SPIRV/GLSL.ext.NV.h @@ -0,0 +1,81 @@ +/* +** Copyright (c) 2014-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLextNV_H +#define GLSLextNV_H + +enum BuiltIn; +enum Decoration; +enum Op; +enum Capability; + +static const int GLSLextNVVersion = 100; +static const int GLSLextNVRevision = 11; + +//SPV_NV_sample_mask_override_coverage +const char* const E_SPV_NV_sample_mask_override_coverage = "SPV_NV_sample_mask_override_coverage"; + +//SPV_NV_geometry_shader_passthrough +const char* const E_SPV_NV_geometry_shader_passthrough = "SPV_NV_geometry_shader_passthrough"; + +//SPV_NV_viewport_array2 +const char* const E_SPV_NV_viewport_array2 = "SPV_NV_viewport_array2"; +const char* const E_ARB_shader_viewport_layer_array = "SPV_ARB_shader_viewport_layer_array"; + +//SPV_NV_stereo_view_rendering +const char* const E_SPV_NV_stereo_view_rendering = "SPV_NV_stereo_view_rendering"; + +//SPV_NVX_multiview_per_view_attributes +const char* const E_SPV_NVX_multiview_per_view_attributes = "SPV_NVX_multiview_per_view_attributes"; + +//SPV_NV_shader_subgroup_partitioned +const char* const E_SPV_NV_shader_subgroup_partitioned = "SPV_NV_shader_subgroup_partitioned"; + +//SPV_NV_fragment_shader_barycentric +const char* const E_SPV_NV_fragment_shader_barycentric = "SPV_NV_fragment_shader_barycentric"; + +//SPV_NV_compute_shader_derivatives +const char* const E_SPV_NV_compute_shader_derivatives = "SPV_NV_compute_shader_derivatives"; + +//SPV_NV_shader_image_footprint +const char* const E_SPV_NV_shader_image_footprint = "SPV_NV_shader_image_footprint"; + +//SPV_NV_mesh_shader +const char* const E_SPV_NV_mesh_shader = "SPV_NV_mesh_shader"; + +//SPV_NV_raytracing +const char* const E_SPV_NV_ray_tracing = "SPV_NV_ray_tracing"; + +//SPV_NV_shading_rate +const char* const E_SPV_NV_shading_rate = "SPV_NV_shading_rate"; + +//SPV_NV_cooperative_matrix +const char* const E_SPV_NV_cooperative_matrix = "SPV_NV_cooperative_matrix"; + +//SPV_NV_shader_sm_builtins +const char* const E_SPV_NV_shader_sm_builtins = "SPV_NV_shader_sm_builtins"; + +#endif // #ifndef GLSLextNV_H diff --git a/third_party/glslang/SPIRV/GLSL.std.450.h b/third_party/glslang/SPIRV/GLSL.std.450.h new file mode 100644 index 0000000..df31092 --- /dev/null +++ b/third_party/glslang/SPIRV/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 1; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/third_party/glslang/SPIRV/GlslangToSpv.cpp b/third_party/glslang/SPIRV/GlslangToSpv.cpp new file mode 100644 index 0000000..e9c14df --- /dev/null +++ b/third_party/glslang/SPIRV/GlslangToSpv.cpp @@ -0,0 +1,8824 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// translate them to SPIR-V. +// + +#include "spirv.hpp" +#include "GlslangToSpv.h" +#include "SpvBuilder.h" +namespace spv { + #include "GLSL.std.450.h" + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + #include "NonSemanticDebugPrintf.h" +} + +// Glslang includes +#include "../glslang/MachineIndependent/localintermediate.h" +#include "../glslang/MachineIndependent/SymbolTable.h" +#include "../glslang/Include/Common.h" + +// Build-time generated includes +#include "glslang/build_info.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +namespace { +class SpecConstantOpModeGuard { +public: + SpecConstantOpModeGuard(spv::Builder* builder) + : builder_(builder) { + previous_flag_ = builder->isInSpecConstCodeGenMode(); + } + ~SpecConstantOpModeGuard() { + previous_flag_ ? builder_->setToSpecConstCodeGenMode() + : builder_->setToNormalCodeGenMode(); + } + void turnOnSpecConstantOpMode() { + builder_->setToSpecConstCodeGenMode(); + } + +private: + spv::Builder* builder_; + bool previous_flag_; +}; + +struct OpDecorations { + public: + OpDecorations(spv::Decoration precision, spv::Decoration noContraction, spv::Decoration nonUniform) : + precision(precision) +#ifndef GLSLANG_WEB + , + noContraction(noContraction), + nonUniform(nonUniform) +#endif + { } + + spv::Decoration precision; + +#ifdef GLSLANG_WEB + void addNoContraction(spv::Builder&, spv::Id) const { } + void addNonUniform(spv::Builder&, spv::Id) const { } +#else + void addNoContraction(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, noContraction); } + void addNonUniform(spv::Builder& builder, spv::Id t) { builder.addDecoration(t, nonUniform); } + protected: + spv::Decoration noContraction; + spv::Decoration nonUniform; +#endif + +}; + +} // namespace + +// +// The main holder of information for translating glslang to SPIR-V. +// +// Derives from the AST walking base class. +// +class TGlslangToSpvTraverser : public glslang::TIntermTraverser { +public: + TGlslangToSpvTraverser(unsigned int spvVersion, const glslang::TIntermediate*, spv::SpvBuildLogger* logger, + glslang::SpvOptions& options); + virtual ~TGlslangToSpvTraverser() { } + + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*); + bool visitBinary(glslang::TVisit, glslang::TIntermBinary*); + void visitConstantUnion(glslang::TIntermConstantUnion*); + bool visitSelection(glslang::TVisit, glslang::TIntermSelection*); + bool visitSwitch(glslang::TVisit, glslang::TIntermSwitch*); + void visitSymbol(glslang::TIntermSymbol* symbol); + bool visitUnary(glslang::TVisit, glslang::TIntermUnary*); + bool visitLoop(glslang::TVisit, glslang::TIntermLoop*); + bool visitBranch(glslang::TVisit visit, glslang::TIntermBranch*); + + void finishSpv(); + void dumpSpv(std::vector& out); + +protected: + TGlslangToSpvTraverser(TGlslangToSpvTraverser&); + TGlslangToSpvTraverser& operator=(TGlslangToSpvTraverser&); + + spv::Decoration TranslateInterpolationDecoration(const glslang::TQualifier& qualifier); + spv::Decoration TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier); + spv::Decoration TranslateNonUniformDecoration(const glslang::TQualifier& qualifier); + spv::Builder::AccessChain::CoherentFlags TranslateCoherent(const glslang::TType& type); + spv::MemoryAccessMask TranslateMemoryAccess(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::ImageOperandsMask TranslateImageOperands(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::Scope TranslateMemoryScope(const spv::Builder::AccessChain::CoherentFlags &coherentFlags); + spv::BuiltIn TranslateBuiltInDecoration(glslang::TBuiltInVariable, bool memberDeclaration); + spv::ImageFormat TranslateImageFormat(const glslang::TType& type); + spv::SelectionControlMask TranslateSelectionControl(const glslang::TIntermSelection&) const; + spv::SelectionControlMask TranslateSwitchControl(const glslang::TIntermSwitch&) const; + spv::LoopControlMask TranslateLoopControl(const glslang::TIntermLoop&, std::vector& operands) const; + spv::StorageClass TranslateStorageClass(const glslang::TType&); + void addIndirectionIndexCapabilities(const glslang::TType& baseType, const glslang::TType& indexType); + spv::Id createSpvVariable(const glslang::TIntermSymbol*, spv::Id forcedType); + spv::Id getSampledType(const glslang::TSampler&); + spv::Id getInvertedSwizzleType(const glslang::TIntermTyped&); + spv::Id createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped&, spv::Id parentResult); + void convertSwizzle(const glslang::TIntermAggregate&, std::vector& swizzle); + spv::Id convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly = false); + spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&, + bool lastBufferBlockMember, bool forwardReferenceOnly = false); + bool filterMember(const glslang::TType& member); + spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct, + glslang::TLayoutPacking, const glslang::TQualifier&); + void decorateStructType(const glslang::TType&, const glslang::TTypeList* glslangStruct, glslang::TLayoutPacking, + const glslang::TQualifier&, spv::Id); + spv::Id makeArraySizeId(const glslang::TArraySizes&, int dim); + spv::Id accessChainLoad(const glslang::TType& type); + void accessChainStore(const glslang::TType& type, spv::Id rvalue); + void multiTypeStore(const glslang::TType&, spv::Id rValue); + glslang::TLayoutPacking getExplicitLayout(const glslang::TType& type) const; + int getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking, glslang::TLayoutMatrix); + int getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking, glslang::TLayoutMatrix); + void updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, int& currentOffset, + int& nextOffset, glslang::TLayoutPacking, glslang::TLayoutMatrix); + void declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember); + + bool isShaderEntryPoint(const glslang::TIntermAggregate* node); + bool writableParam(glslang::TStorageQualifier) const; + bool originalParam(glslang::TStorageQualifier, const glslang::TType&, bool implicitThisParam); + void makeFunctions(const glslang::TIntermSequence&); + void makeGlobalInitializers(const glslang::TIntermSequence&); + void visitFunctions(const glslang::TIntermSequence&); + void handleFunctionEntry(const glslang::TIntermAggregate* node); + void translateArguments(const glslang::TIntermAggregate& node, std::vector& arguments, + spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); + void translateArguments(glslang::TIntermUnary& node, std::vector& arguments); + spv::Id createImageTextureFunctionCall(glslang::TIntermOperator* node); + spv::Id handleUserFunctionCall(const glslang::TIntermAggregate*); + + spv::Id createBinaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right, + glslang::TBasicType typeProxy, bool reduceComparison = true); + spv::Id createBinaryMatrixOperation(spv::Op, OpDecorations&, spv::Id typeId, spv::Id left, spv::Id right); + spv::Id createUnaryOperation(glslang::TOperator op, OpDecorations&, spv::Id typeId, spv::Id operand, + glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); + spv::Id createUnaryMatrixOperation(spv::Op op, OpDecorations&, spv::Id typeId, spv::Id operand, + glslang::TBasicType typeProxy); + spv::Id createConversion(glslang::TOperator op, OpDecorations&, spv::Id destTypeId, spv::Id operand, + glslang::TBasicType typeProxy); + spv::Id createIntWidthConversion(glslang::TOperator op, spv::Id operand, int vectorSize); + spv::Id makeSmearedConstant(spv::Id constant, int vectorSize); + spv::Id createAtomicOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags); + spv::Id createInvocationsOperation(glslang::TOperator op, spv::Id typeId, std::vector& operands, + glslang::TBasicType typeProxy); + spv::Id CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, + spv::Id typeId, std::vector& operands); + spv::Id createSubgroupOperation(glslang::TOperator op, spv::Id typeId, std::vector& operands, + glslang::TBasicType typeProxy); + spv::Id createMiscOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy); + spv::Id createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId); + spv::Id getSymbolId(const glslang::TIntermSymbol* node); + void addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier & qualifier); + spv::Id createSpvConstant(const glslang::TIntermTyped&); + spv::Id createSpvConstantFromConstUnionArray(const glslang::TType& type, const glslang::TConstUnionArray&, + int& nextConst, bool specConstant); + bool isTrivialLeaf(const glslang::TIntermTyped* node); + bool isTrivial(const glslang::TIntermTyped* node); + spv::Id createShortCircuit(glslang::TOperator, glslang::TIntermTyped& left, glslang::TIntermTyped& right); + spv::Id getExtBuiltins(const char* name); + std::pair getForcedType(glslang::TBuiltInVariable builtIn, const glslang::TType&); + spv::Id translateForcedType(spv::Id object); + spv::Id createCompositeConstruct(spv::Id typeId, std::vector constituents); + + glslang::SpvOptions& options; + spv::Function* shaderEntry; + spv::Function* currentFunction; + spv::Instruction* entryPoint; + int sequenceDepth; + + spv::SpvBuildLogger* logger; + + // There is a 1:1 mapping between a spv builder and a module; this is thread safe + spv::Builder builder; + bool inEntryPoint; + bool entryPointTerminated; + bool linkageOnly; // true when visiting the set of objects in the AST present only for + // establishing interface, whether or not they were statically used + std::set iOSet; // all input/output variables from either static use or declaration of interface + const glslang::TIntermediate* glslangIntermediate; + bool nanMinMaxClamp; // true if use NMin/NMax/NClamp instead of FMin/FMax/FClamp + spv::Id stdBuiltins; + spv::Id nonSemanticDebugPrintf; + std::unordered_map extBuiltinMap; + + std::unordered_map symbolValues; + std::unordered_set rValueParameters; // set of formal function parameters passed as rValues, + // rather than a pointer + std::unordered_map functionMap; + std::unordered_map structMap[glslang::ElpCount][glslang::ElmCount]; + // for mapping glslang block indices to spv indices (e.g., due to hidden members): + std::unordered_map> memberRemapper; + // for mapping glslang symbol struct to symbol Id + std::unordered_map glslangTypeToIdMap; + std::stack breakForLoop; // false means break for switch + std::unordered_map counterOriginator; + // Map pointee types for EbtReference to their forward pointers + std::map forwardPointers; + // Type forcing, for when SPIR-V wants a different type than the AST, + // requiring local translation to and from SPIR-V type on every access. + // Maps AST-required-type-id> + std::unordered_map forceType; +}; + +// +// Helper functions for translating glslang representations to SPIR-V enumerants. +// + +// Translate glslang profile to SPIR-V source language. +spv::SourceLanguage TranslateSourceLanguage(glslang::EShSource source, EProfile profile) +{ +#ifdef GLSLANG_WEB + return spv::SourceLanguageESSL; +#elif defined(GLSLANG_ANGLE) + return spv::SourceLanguageGLSL; +#endif + + switch (source) { + case glslang::EShSourceGlsl: + switch (profile) { + case ENoProfile: + case ECoreProfile: + case ECompatibilityProfile: + return spv::SourceLanguageGLSL; + case EEsProfile: + return spv::SourceLanguageESSL; + default: + return spv::SourceLanguageUnknown; + } + case glslang::EShSourceHlsl: + return spv::SourceLanguageHLSL; + default: + return spv::SourceLanguageUnknown; + } +} + +// Translate glslang language (stage) to SPIR-V execution model. +spv::ExecutionModel TranslateExecutionModel(EShLanguage stage) +{ + switch (stage) { + case EShLangVertex: return spv::ExecutionModelVertex; + case EShLangFragment: return spv::ExecutionModelFragment; + case EShLangCompute: return spv::ExecutionModelGLCompute; +#ifndef GLSLANG_WEB + case EShLangTessControl: return spv::ExecutionModelTessellationControl; + case EShLangTessEvaluation: return spv::ExecutionModelTessellationEvaluation; + case EShLangGeometry: return spv::ExecutionModelGeometry; + case EShLangRayGen: return spv::ExecutionModelRayGenerationKHR; + case EShLangIntersect: return spv::ExecutionModelIntersectionKHR; + case EShLangAnyHit: return spv::ExecutionModelAnyHitKHR; + case EShLangClosestHit: return spv::ExecutionModelClosestHitKHR; + case EShLangMiss: return spv::ExecutionModelMissKHR; + case EShLangCallable: return spv::ExecutionModelCallableKHR; + case EShLangTaskNV: return spv::ExecutionModelTaskNV; + case EShLangMeshNV: return spv::ExecutionModelMeshNV; +#endif + default: + assert(0); + return spv::ExecutionModelFragment; + } +} + +// Translate glslang sampler type to SPIR-V dimensionality. +spv::Dim TranslateDimensionality(const glslang::TSampler& sampler) +{ + switch (sampler.dim) { + case glslang::Esd1D: return spv::Dim1D; + case glslang::Esd2D: return spv::Dim2D; + case glslang::Esd3D: return spv::Dim3D; + case glslang::EsdCube: return spv::DimCube; + case glslang::EsdRect: return spv::DimRect; + case glslang::EsdBuffer: return spv::DimBuffer; + case glslang::EsdSubpass: return spv::DimSubpassData; + default: + assert(0); + return spv::Dim2D; + } +} + +// Translate glslang precision to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(glslang::TPrecisionQualifier glslangPrecision) +{ + switch (glslangPrecision) { + case glslang::EpqLow: return spv::DecorationRelaxedPrecision; + case glslang::EpqMedium: return spv::DecorationRelaxedPrecision; + default: + return spv::NoPrecision; + } +} + +// Translate glslang type to SPIR-V precision decorations. +spv::Decoration TranslatePrecisionDecoration(const glslang::TType& type) +{ + return TranslatePrecisionDecoration(type.getQualifier().precision); +} + +// Translate glslang type to SPIR-V block decorations. +spv::Decoration TranslateBlockDecoration(const glslang::TType& type, bool useStorageBuffer) +{ + if (type.getBasicType() == glslang::EbtBlock) { + switch (type.getQualifier().storage) { + case glslang::EvqUniform: return spv::DecorationBlock; + case glslang::EvqBuffer: return useStorageBuffer ? spv::DecorationBlock : spv::DecorationBufferBlock; + case glslang::EvqVaryingIn: return spv::DecorationBlock; + case glslang::EvqVaryingOut: return spv::DecorationBlock; +#ifndef GLSLANG_WEB + case glslang::EvqPayload: return spv::DecorationBlock; + case glslang::EvqPayloadIn: return spv::DecorationBlock; + case glslang::EvqHitAttr: return spv::DecorationBlock; + case glslang::EvqCallableData: return spv::DecorationBlock; + case glslang::EvqCallableDataIn: return spv::DecorationBlock; +#endif + default: + assert(0); + break; + } + } + + return spv::DecorationMax; +} + +// Translate glslang type to SPIR-V memory decorations. +void TranslateMemoryDecoration(const glslang::TQualifier& qualifier, std::vector& memory, + bool useVulkanMemoryModel) +{ + if (!useVulkanMemoryModel) { + if (qualifier.isCoherent()) + memory.push_back(spv::DecorationCoherent); + if (qualifier.isVolatile()) { + memory.push_back(spv::DecorationVolatile); + memory.push_back(spv::DecorationCoherent); + } + } + if (qualifier.isRestrict()) + memory.push_back(spv::DecorationRestrict); + if (qualifier.isReadOnly()) + memory.push_back(spv::DecorationNonWritable); + if (qualifier.isWriteOnly()) + memory.push_back(spv::DecorationNonReadable); +} + +// Translate glslang type to SPIR-V layout decorations. +spv::Decoration TranslateLayoutDecoration(const glslang::TType& type, glslang::TLayoutMatrix matrixLayout) +{ + if (type.isMatrix()) { + switch (matrixLayout) { + case glslang::ElmRowMajor: + return spv::DecorationRowMajor; + case glslang::ElmColumnMajor: + return spv::DecorationColMajor; + default: + // opaque layouts don't need a majorness + return spv::DecorationMax; + } + } else { + switch (type.getBasicType()) { + default: + return spv::DecorationMax; + break; + case glslang::EbtBlock: + switch (type.getQualifier().storage) { + case glslang::EvqUniform: + case glslang::EvqBuffer: + switch (type.getQualifier().layoutPacking) { + case glslang::ElpShared: return spv::DecorationGLSLShared; + case glslang::ElpPacked: return spv::DecorationGLSLPacked; + default: + return spv::DecorationMax; + } + case glslang::EvqVaryingIn: + case glslang::EvqVaryingOut: + if (type.getQualifier().isTaskMemory()) { + switch (type.getQualifier().layoutPacking) { + case glslang::ElpShared: return spv::DecorationGLSLShared; + case glslang::ElpPacked: return spv::DecorationGLSLPacked; + default: break; + } + } else { + assert(type.getQualifier().layoutPacking == glslang::ElpNone); + } + return spv::DecorationMax; +#ifndef GLSLANG_WEB + case glslang::EvqPayload: + case glslang::EvqPayloadIn: + case glslang::EvqHitAttr: + case glslang::EvqCallableData: + case glslang::EvqCallableDataIn: + return spv::DecorationMax; +#endif + default: + assert(0); + return spv::DecorationMax; + } + } + } +} + +// Translate glslang type to SPIR-V interpolation decorations. +// Returns spv::DecorationMax when no decoration +// should be applied. +spv::Decoration TGlslangToSpvTraverser::TranslateInterpolationDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.smooth) + // Smooth decoration doesn't exist in SPIR-V 1.0 + return spv::DecorationMax; + else if (qualifier.isNonPerspective()) + return spv::DecorationNoPerspective; + else if (qualifier.flat) + return spv::DecorationFlat; + else if (qualifier.isExplicitInterpolation()) { + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::DecorationExplicitInterpAMD; + } + else + return spv::DecorationMax; +} + +// Translate glslang type to SPIR-V auxiliary storage decorations. +// Returns spv::DecorationMax when no decoration +// should be applied. +spv::Decoration TGlslangToSpvTraverser::TranslateAuxiliaryStorageDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.centroid) + return spv::DecorationCentroid; +#ifndef GLSLANG_WEB + else if (qualifier.patch) + return spv::DecorationPatch; + else if (qualifier.sample) { + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::DecorationSample; + } +#endif + + return spv::DecorationMax; +} + +// If glslang type is invariant, return SPIR-V invariant decoration. +spv::Decoration TranslateInvariantDecoration(const glslang::TQualifier& qualifier) +{ + if (qualifier.invariant) + return spv::DecorationInvariant; + else + return spv::DecorationMax; +} + +// If glslang type is noContraction, return SPIR-V NoContraction decoration. +spv::Decoration TranslateNoContractionDecoration(const glslang::TQualifier& qualifier) +{ +#ifndef GLSLANG_WEB + if (qualifier.isNoContraction()) + return spv::DecorationNoContraction; + else +#endif + return spv::DecorationMax; +} + +// If glslang type is nonUniform, return SPIR-V NonUniform decoration. +spv::Decoration TGlslangToSpvTraverser::TranslateNonUniformDecoration(const glslang::TQualifier& qualifier) +{ +#ifndef GLSLANG_WEB + if (qualifier.isNonUniform()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderNonUniformEXT); + return spv::DecorationNonUniformEXT; + } else +#endif + return spv::DecorationMax; +} + +spv::MemoryAccessMask TGlslangToSpvTraverser::TranslateMemoryAccess( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::MemoryAccessMask mask = spv::MemoryAccessMaskNone; + +#ifndef GLSLANG_WEB + if (!glslangIntermediate->usingVulkanMemoryModel() || coherentFlags.isImage) + return mask; + + if (coherentFlags.isVolatile() || coherentFlags.anyCoherent()) { + mask = mask | spv::MemoryAccessMakePointerAvailableKHRMask | + spv::MemoryAccessMakePointerVisibleKHRMask; + } + + if (coherentFlags.nonprivate) { + mask = mask | spv::MemoryAccessNonPrivatePointerKHRMask; + } + if (coherentFlags.volatil) { + mask = mask | spv::MemoryAccessVolatileMask; + } + if (mask != spv::MemoryAccessMaskNone) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } +#endif + + return mask; +} + +spv::ImageOperandsMask TGlslangToSpvTraverser::TranslateImageOperands( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + +#ifndef GLSLANG_WEB + if (!glslangIntermediate->usingVulkanMemoryModel()) + return mask; + + if (coherentFlags.volatil || + coherentFlags.anyCoherent()) { + mask = mask | spv::ImageOperandsMakeTexelAvailableKHRMask | + spv::ImageOperandsMakeTexelVisibleKHRMask; + } + if (coherentFlags.nonprivate) { + mask = mask | spv::ImageOperandsNonPrivateTexelKHRMask; + } + if (coherentFlags.volatil) { + mask = mask | spv::ImageOperandsVolatileTexelKHRMask; + } + if (mask != spv::ImageOperandsMaskNone) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } +#endif + + return mask; +} + +spv::Builder::AccessChain::CoherentFlags TGlslangToSpvTraverser::TranslateCoherent(const glslang::TType& type) +{ + spv::Builder::AccessChain::CoherentFlags flags = {}; +#ifndef GLSLANG_WEB + flags.coherent = type.getQualifier().coherent; + flags.devicecoherent = type.getQualifier().devicecoherent; + flags.queuefamilycoherent = type.getQualifier().queuefamilycoherent; + // shared variables are implicitly workgroupcoherent in GLSL. + flags.workgroupcoherent = type.getQualifier().workgroupcoherent || + type.getQualifier().storage == glslang::EvqShared; + flags.subgroupcoherent = type.getQualifier().subgroupcoherent; + flags.shadercallcoherent = type.getQualifier().shadercallcoherent; + flags.volatil = type.getQualifier().volatil; + // *coherent variables are implicitly nonprivate in GLSL + flags.nonprivate = type.getQualifier().nonprivate || + flags.anyCoherent() || + flags.volatil; + flags.isImage = type.getBasicType() == glslang::EbtSampler; +#endif + return flags; +} + +spv::Scope TGlslangToSpvTraverser::TranslateMemoryScope( + const spv::Builder::AccessChain::CoherentFlags &coherentFlags) +{ + spv::Scope scope = spv::ScopeMax; + +#ifndef GLSLANG_WEB + if (coherentFlags.volatil || coherentFlags.coherent) { + // coherent defaults to Device scope in the old model, QueueFamilyKHR scope in the new model + scope = glslangIntermediate->usingVulkanMemoryModel() ? spv::ScopeQueueFamilyKHR : spv::ScopeDevice; + } else if (coherentFlags.devicecoherent) { + scope = spv::ScopeDevice; + } else if (coherentFlags.queuefamilycoherent) { + scope = spv::ScopeQueueFamilyKHR; + } else if (coherentFlags.workgroupcoherent) { + scope = spv::ScopeWorkgroup; + } else if (coherentFlags.subgroupcoherent) { + scope = spv::ScopeSubgroup; + } else if (coherentFlags.shadercallcoherent) { + scope = spv::ScopeShaderCallKHR; + } + if (glslangIntermediate->usingVulkanMemoryModel() && scope == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } +#endif + + return scope; +} + +// Translate a glslang built-in variable to a SPIR-V built in decoration. Also generate +// associated capabilities when required. For some built-in variables, a capability +// is generated only when using the variable in an executable instruction, but not when +// just declaring a struct member variable with it. This is true for PointSize, +// ClipDistance, and CullDistance. +spv::BuiltIn TGlslangToSpvTraverser::TranslateBuiltInDecoration(glslang::TBuiltInVariable builtIn, + bool memberDeclaration) +{ + switch (builtIn) { + case glslang::EbvPointSize: +#ifndef GLSLANG_WEB + // Defer adding the capability until the built-in is actually used. + if (! memberDeclaration) { + switch (glslangIntermediate->getStage()) { + case EShLangGeometry: + builder.addCapability(spv::CapabilityGeometryPointSize); + break; + case EShLangTessControl: + case EShLangTessEvaluation: + builder.addCapability(spv::CapabilityTessellationPointSize); + break; + default: + break; + } + } +#endif + return spv::BuiltInPointSize; + + case glslang::EbvPosition: return spv::BuiltInPosition; + case glslang::EbvVertexId: return spv::BuiltInVertexId; + case glslang::EbvInstanceId: return spv::BuiltInInstanceId; + case glslang::EbvVertexIndex: return spv::BuiltInVertexIndex; + case glslang::EbvInstanceIndex: return spv::BuiltInInstanceIndex; + + case glslang::EbvFragCoord: return spv::BuiltInFragCoord; + case glslang::EbvPointCoord: return spv::BuiltInPointCoord; + case glslang::EbvFace: return spv::BuiltInFrontFacing; + case glslang::EbvFragDepth: return spv::BuiltInFragDepth; + + case glslang::EbvNumWorkGroups: return spv::BuiltInNumWorkgroups; + case glslang::EbvWorkGroupSize: return spv::BuiltInWorkgroupSize; + case glslang::EbvWorkGroupId: return spv::BuiltInWorkgroupId; + case glslang::EbvLocalInvocationId: return spv::BuiltInLocalInvocationId; + case glslang::EbvLocalInvocationIndex: return spv::BuiltInLocalInvocationIndex; + case glslang::EbvGlobalInvocationId: return spv::BuiltInGlobalInvocationId; + +#ifndef GLSLANG_WEB + // These *Distance capabilities logically belong here, but if the member is declared and + // then never used, consumers of SPIR-V prefer the capability not be declared. + // They are now generated when used, rather than here when declared. + // Potentially, the specification should be more clear what the minimum + // use needed is to trigger the capability. + // + case glslang::EbvClipDistance: + if (!memberDeclaration) + builder.addCapability(spv::CapabilityClipDistance); + return spv::BuiltInClipDistance; + + case glslang::EbvCullDistance: + if (!memberDeclaration) + builder.addCapability(spv::CapabilityCullDistance); + return spv::BuiltInCullDistance; + + case glslang::EbvViewportIndex: + builder.addCapability(spv::CapabilityMultiViewport); + if (glslangIntermediate->getStage() == EShLangVertex || + glslangIntermediate->getStage() == EShLangTessControl || + glslangIntermediate->getStage() == EShLangTessEvaluation) { + + if (builder.getSpvVersion() < spv::Spv_1_5) { + builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); + } + else + builder.addCapability(spv::CapabilityShaderViewportIndex); + } + return spv::BuiltInViewportIndex; + + case glslang::EbvSampleId: + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::BuiltInSampleId; + + case glslang::EbvSamplePosition: + builder.addCapability(spv::CapabilitySampleRateShading); + return spv::BuiltInSamplePosition; + + case glslang::EbvSampleMask: + return spv::BuiltInSampleMask; + + case glslang::EbvLayer: + if (glslangIntermediate->getStage() == EShLangMeshNV) { + return spv::BuiltInLayer; + } + builder.addCapability(spv::CapabilityGeometry); + if (glslangIntermediate->getStage() == EShLangVertex || + glslangIntermediate->getStage() == EShLangTessControl || + glslangIntermediate->getStage() == EShLangTessEvaluation) { + + if (builder.getSpvVersion() < spv::Spv_1_5) { + builder.addIncorporatedExtension(spv::E_SPV_EXT_shader_viewport_index_layer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityShaderViewportIndexLayerEXT); + } else + builder.addCapability(spv::CapabilityShaderLayer); + } + return spv::BuiltInLayer; + + case glslang::EbvBaseVertex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInBaseVertex; + + case glslang::EbvBaseInstance: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInBaseInstance; + + case glslang::EbvDrawId: + builder.addIncorporatedExtension(spv::E_SPV_KHR_shader_draw_parameters, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDrawParameters); + return spv::BuiltInDrawIndex; + + case glslang::EbvPrimitiveId: + if (glslangIntermediate->getStage() == EShLangFragment) + builder.addCapability(spv::CapabilityGeometry); + return spv::BuiltInPrimitiveId; + + case glslang::EbvFragStencilRef: + builder.addExtension(spv::E_SPV_EXT_shader_stencil_export); + builder.addCapability(spv::CapabilityStencilExportEXT); + return spv::BuiltInFragStencilRefEXT; + + case glslang::EbvShadingRateKHR: + builder.addExtension(spv::E_SPV_KHR_fragment_shading_rate); + builder.addCapability(spv::CapabilityFragmentShadingRateKHR); + return spv::BuiltInShadingRateKHR; + + case glslang::EbvPrimitiveShadingRateKHR: + builder.addExtension(spv::E_SPV_KHR_fragment_shading_rate); + builder.addCapability(spv::CapabilityFragmentShadingRateKHR); + return spv::BuiltInPrimitiveShadingRateKHR; + + case glslang::EbvInvocationId: return spv::BuiltInInvocationId; + case glslang::EbvTessLevelInner: return spv::BuiltInTessLevelInner; + case glslang::EbvTessLevelOuter: return spv::BuiltInTessLevelOuter; + case glslang::EbvTessCoord: return spv::BuiltInTessCoord; + case glslang::EbvPatchVertices: return spv::BuiltInPatchVertices; + case glslang::EbvHelperInvocation: return spv::BuiltInHelperInvocation; + + case glslang::EbvSubGroupSize: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupSize; + + case glslang::EbvSubGroupInvocation: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLocalInvocationId; + + case glslang::EbvSubGroupEqMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupEqMask; + + case glslang::EbvSubGroupGeMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupGeMask; + + case glslang::EbvSubGroupGtMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupGtMask; + + case glslang::EbvSubGroupLeMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLeMask; + + case glslang::EbvSubGroupLtMask: + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + return spv::BuiltInSubgroupLtMask; + + case glslang::EbvNumSubgroups: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInNumSubgroups; + + case glslang::EbvSubgroupID: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupId; + + case glslang::EbvSubgroupSize2: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupSize; + + case glslang::EbvSubgroupInvocation2: + builder.addCapability(spv::CapabilityGroupNonUniform); + return spv::BuiltInSubgroupLocalInvocationId; + + case glslang::EbvSubgroupEqMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupEqMask; + + case glslang::EbvSubgroupGeMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupGeMask; + + case glslang::EbvSubgroupGtMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupGtMask; + + case glslang::EbvSubgroupLeMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupLeMask; + + case glslang::EbvSubgroupLtMask2: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + return spv::BuiltInSubgroupLtMask; + + case glslang::EbvBaryCoordNoPersp: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspAMD; + + case glslang::EbvBaryCoordNoPerspCentroid: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspCentroidAMD; + + case glslang::EbvBaryCoordNoPerspSample: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordNoPerspSampleAMD; + + case glslang::EbvBaryCoordSmooth: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothAMD; + + case glslang::EbvBaryCoordSmoothCentroid: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothCentroidAMD; + + case glslang::EbvBaryCoordSmoothSample: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordSmoothSampleAMD; + + case glslang::EbvBaryCoordPullModel: + builder.addExtension(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + return spv::BuiltInBaryCoordPullModelAMD; + + case glslang::EbvDeviceIndex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_device_group, spv::Spv_1_3); + builder.addCapability(spv::CapabilityDeviceGroup); + return spv::BuiltInDeviceIndex; + + case glslang::EbvViewIndex: + builder.addIncorporatedExtension(spv::E_SPV_KHR_multiview, spv::Spv_1_3); + builder.addCapability(spv::CapabilityMultiView); + return spv::BuiltInViewIndex; + + case glslang::EbvFragSizeEXT: + builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density); + builder.addCapability(spv::CapabilityFragmentDensityEXT); + return spv::BuiltInFragSizeEXT; + + case glslang::EbvFragInvocationCountEXT: + builder.addExtension(spv::E_SPV_EXT_fragment_invocation_density); + builder.addCapability(spv::CapabilityFragmentDensityEXT); + return spv::BuiltInFragInvocationCountEXT; + + case glslang::EbvViewportMaskNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_viewport_array2); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + } + return spv::BuiltInViewportMaskNV; + case glslang::EbvSecondaryPositionNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + } + return spv::BuiltInSecondaryPositionNV; + case glslang::EbvSecondaryViewportMaskNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + } + return spv::BuiltInSecondaryViewportMaskNV; + case glslang::EbvPositionPerViewNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); + builder.addCapability(spv::CapabilityPerViewAttributesNV); + } + return spv::BuiltInPositionPerViewNV; + case glslang::EbvViewportMaskPerViewNV: + if (!memberDeclaration) { + builder.addExtension(spv::E_SPV_NVX_multiview_per_view_attributes); + builder.addCapability(spv::CapabilityPerViewAttributesNV); + } + return spv::BuiltInViewportMaskPerViewNV; + case glslang::EbvFragFullyCoveredNV: + builder.addExtension(spv::E_SPV_EXT_fragment_fully_covered); + builder.addCapability(spv::CapabilityFragmentFullyCoveredEXT); + return spv::BuiltInFullyCoveredEXT; + case glslang::EbvFragmentSizeNV: + builder.addExtension(spv::E_SPV_NV_shading_rate); + builder.addCapability(spv::CapabilityShadingRateNV); + return spv::BuiltInFragmentSizeNV; + case glslang::EbvInvocationsPerPixelNV: + builder.addExtension(spv::E_SPV_NV_shading_rate); + builder.addCapability(spv::CapabilityShadingRateNV); + return spv::BuiltInInvocationsPerPixelNV; + + // ray tracing + case glslang::EbvLaunchId: + return spv::BuiltInLaunchIdKHR; + case glslang::EbvLaunchSize: + return spv::BuiltInLaunchSizeKHR; + case glslang::EbvWorldRayOrigin: + return spv::BuiltInWorldRayOriginKHR; + case glslang::EbvWorldRayDirection: + return spv::BuiltInWorldRayDirectionKHR; + case glslang::EbvObjectRayOrigin: + return spv::BuiltInObjectRayOriginKHR; + case glslang::EbvObjectRayDirection: + return spv::BuiltInObjectRayDirectionKHR; + case glslang::EbvRayTmin: + return spv::BuiltInRayTminKHR; + case glslang::EbvRayTmax: + return spv::BuiltInRayTmaxKHR; + case glslang::EbvInstanceCustomIndex: + return spv::BuiltInInstanceCustomIndexKHR; + case glslang::EbvHitT: + return spv::BuiltInHitTKHR; + case glslang::EbvHitKind: + return spv::BuiltInHitKindKHR; + case glslang::EbvObjectToWorld: + case glslang::EbvObjectToWorld3x4: + return spv::BuiltInObjectToWorldKHR; + case glslang::EbvWorldToObject: + case glslang::EbvWorldToObject3x4: + return spv::BuiltInWorldToObjectKHR; + case glslang::EbvIncomingRayFlags: + return spv::BuiltInIncomingRayFlagsKHR; + case glslang::EbvGeometryIndex: + return spv::BuiltInRayGeometryIndexKHR; + + // barycentrics + case glslang::EbvBaryCoordNV: + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + return spv::BuiltInBaryCoordNV; + case glslang::EbvBaryCoordNoPerspNV: + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + return spv::BuiltInBaryCoordNoPerspNV; + + // mesh shaders + case glslang::EbvTaskCountNV: + return spv::BuiltInTaskCountNV; + case glslang::EbvPrimitiveCountNV: + return spv::BuiltInPrimitiveCountNV; + case glslang::EbvPrimitiveIndicesNV: + return spv::BuiltInPrimitiveIndicesNV; + case glslang::EbvClipDistancePerViewNV: + return spv::BuiltInClipDistancePerViewNV; + case glslang::EbvCullDistancePerViewNV: + return spv::BuiltInCullDistancePerViewNV; + case glslang::EbvLayerPerViewNV: + return spv::BuiltInLayerPerViewNV; + case glslang::EbvMeshViewCountNV: + return spv::BuiltInMeshViewCountNV; + case glslang::EbvMeshViewIndicesNV: + return spv::BuiltInMeshViewIndicesNV; + + // sm builtins + case glslang::EbvWarpsPerSM: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInWarpsPerSMNV; + case glslang::EbvSMCount: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInSMCountNV; + case glslang::EbvWarpID: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInWarpIDNV; + case glslang::EbvSMID: + builder.addExtension(spv::E_SPV_NV_shader_sm_builtins); + builder.addCapability(spv::CapabilityShaderSMBuiltinsNV); + return spv::BuiltInSMIDNV; +#endif + + default: + return spv::BuiltInMax; + } +} + +// Translate glslang image layout format to SPIR-V image format. +spv::ImageFormat TGlslangToSpvTraverser::TranslateImageFormat(const glslang::TType& type) +{ + assert(type.getBasicType() == glslang::EbtSampler); + +#ifdef GLSLANG_WEB + return spv::ImageFormatUnknown; +#endif + + // Check for capabilities + switch (type.getQualifier().getFormat()) { + case glslang::ElfRg32f: + case glslang::ElfRg16f: + case glslang::ElfR11fG11fB10f: + case glslang::ElfR16f: + case glslang::ElfRgba16: + case glslang::ElfRgb10A2: + case glslang::ElfRg16: + case glslang::ElfRg8: + case glslang::ElfR16: + case glslang::ElfR8: + case glslang::ElfRgba16Snorm: + case glslang::ElfRg16Snorm: + case glslang::ElfRg8Snorm: + case glslang::ElfR16Snorm: + case glslang::ElfR8Snorm: + + case glslang::ElfRg32i: + case glslang::ElfRg16i: + case glslang::ElfRg8i: + case glslang::ElfR16i: + case glslang::ElfR8i: + + case glslang::ElfRgb10a2ui: + case glslang::ElfRg32ui: + case glslang::ElfRg16ui: + case glslang::ElfRg8ui: + case glslang::ElfR16ui: + case glslang::ElfR8ui: + builder.addCapability(spv::CapabilityStorageImageExtendedFormats); + break; + + case glslang::ElfR64ui: + case glslang::ElfR64i: + builder.addExtension(spv::E_SPV_EXT_shader_image_int64); + builder.addCapability(spv::CapabilityInt64ImageEXT); + default: + break; + } + + // do the translation + switch (type.getQualifier().getFormat()) { + case glslang::ElfNone: return spv::ImageFormatUnknown; + case glslang::ElfRgba32f: return spv::ImageFormatRgba32f; + case glslang::ElfRgba16f: return spv::ImageFormatRgba16f; + case glslang::ElfR32f: return spv::ImageFormatR32f; + case glslang::ElfRgba8: return spv::ImageFormatRgba8; + case glslang::ElfRgba8Snorm: return spv::ImageFormatRgba8Snorm; + case glslang::ElfRg32f: return spv::ImageFormatRg32f; + case glslang::ElfRg16f: return spv::ImageFormatRg16f; + case glslang::ElfR11fG11fB10f: return spv::ImageFormatR11fG11fB10f; + case glslang::ElfR16f: return spv::ImageFormatR16f; + case glslang::ElfRgba16: return spv::ImageFormatRgba16; + case glslang::ElfRgb10A2: return spv::ImageFormatRgb10A2; + case glslang::ElfRg16: return spv::ImageFormatRg16; + case glslang::ElfRg8: return spv::ImageFormatRg8; + case glslang::ElfR16: return spv::ImageFormatR16; + case glslang::ElfR8: return spv::ImageFormatR8; + case glslang::ElfRgba16Snorm: return spv::ImageFormatRgba16Snorm; + case glslang::ElfRg16Snorm: return spv::ImageFormatRg16Snorm; + case glslang::ElfRg8Snorm: return spv::ImageFormatRg8Snorm; + case glslang::ElfR16Snorm: return spv::ImageFormatR16Snorm; + case glslang::ElfR8Snorm: return spv::ImageFormatR8Snorm; + case glslang::ElfRgba32i: return spv::ImageFormatRgba32i; + case glslang::ElfRgba16i: return spv::ImageFormatRgba16i; + case glslang::ElfRgba8i: return spv::ImageFormatRgba8i; + case glslang::ElfR32i: return spv::ImageFormatR32i; + case glslang::ElfRg32i: return spv::ImageFormatRg32i; + case glslang::ElfRg16i: return spv::ImageFormatRg16i; + case glslang::ElfRg8i: return spv::ImageFormatRg8i; + case glslang::ElfR16i: return spv::ImageFormatR16i; + case glslang::ElfR8i: return spv::ImageFormatR8i; + case glslang::ElfRgba32ui: return spv::ImageFormatRgba32ui; + case glslang::ElfRgba16ui: return spv::ImageFormatRgba16ui; + case glslang::ElfRgba8ui: return spv::ImageFormatRgba8ui; + case glslang::ElfR32ui: return spv::ImageFormatR32ui; + case glslang::ElfRg32ui: return spv::ImageFormatRg32ui; + case glslang::ElfRg16ui: return spv::ImageFormatRg16ui; + case glslang::ElfRgb10a2ui: return spv::ImageFormatRgb10a2ui; + case glslang::ElfRg8ui: return spv::ImageFormatRg8ui; + case glslang::ElfR16ui: return spv::ImageFormatR16ui; + case glslang::ElfR8ui: return spv::ImageFormatR8ui; + case glslang::ElfR64ui: return spv::ImageFormatR64ui; + case glslang::ElfR64i: return spv::ImageFormatR64i; + default: return spv::ImageFormatMax; + } +} + +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSelectionControl( + const glslang::TIntermSelection& selectionNode) const +{ + if (selectionNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (selectionNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +spv::SelectionControlMask TGlslangToSpvTraverser::TranslateSwitchControl(const glslang::TIntermSwitch& switchNode) + const +{ + if (switchNode.getFlatten()) + return spv::SelectionControlFlattenMask; + if (switchNode.getDontFlatten()) + return spv::SelectionControlDontFlattenMask; + return spv::SelectionControlMaskNone; +} + +// return a non-0 dependency if the dependency argument must be set +spv::LoopControlMask TGlslangToSpvTraverser::TranslateLoopControl(const glslang::TIntermLoop& loopNode, + std::vector& operands) const +{ + spv::LoopControlMask control = spv::LoopControlMaskNone; + + if (loopNode.getDontUnroll()) + control = control | spv::LoopControlDontUnrollMask; + if (loopNode.getUnroll()) + control = control | spv::LoopControlUnrollMask; + if (unsigned(loopNode.getLoopDependency()) == glslang::TIntermLoop::dependencyInfinite) + control = control | spv::LoopControlDependencyInfiniteMask; + else if (loopNode.getLoopDependency() > 0) { + control = control | spv::LoopControlDependencyLengthMask; + operands.push_back((unsigned int)loopNode.getLoopDependency()); + } + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + if (loopNode.getMinIterations() > 0) { + control = control | spv::LoopControlMinIterationsMask; + operands.push_back(loopNode.getMinIterations()); + } + if (loopNode.getMaxIterations() < glslang::TIntermLoop::iterationsInfinite) { + control = control | spv::LoopControlMaxIterationsMask; + operands.push_back(loopNode.getMaxIterations()); + } + if (loopNode.getIterationMultiple() > 1) { + control = control | spv::LoopControlIterationMultipleMask; + operands.push_back(loopNode.getIterationMultiple()); + } + if (loopNode.getPeelCount() > 0) { + control = control | spv::LoopControlPeelCountMask; + operands.push_back(loopNode.getPeelCount()); + } + if (loopNode.getPartialCount() > 0) { + control = control | spv::LoopControlPartialCountMask; + operands.push_back(loopNode.getPartialCount()); + } + } + + return control; +} + +// Translate glslang type to SPIR-V storage class. +spv::StorageClass TGlslangToSpvTraverser::TranslateStorageClass(const glslang::TType& type) +{ + if (type.getBasicType() == glslang::EbtRayQuery) + return spv::StorageClassFunction; + if (type.getQualifier().isPipeInput()) + return spv::StorageClassInput; + if (type.getQualifier().isPipeOutput()) + return spv::StorageClassOutput; + + if (glslangIntermediate->getSource() != glslang::EShSourceHlsl || + type.getQualifier().storage == glslang::EvqUniform) { + if (type.isAtomic()) + return spv::StorageClassAtomicCounter; + if (type.containsOpaque()) + return spv::StorageClassUniformConstant; + } + + if (type.getQualifier().isUniformOrBuffer() && + type.getQualifier().isShaderRecord()) { + return spv::StorageClassShaderRecordBufferKHR; + } + + if (glslangIntermediate->usingStorageBuffer() && type.getQualifier().storage == glslang::EvqBuffer) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_storage_buffer_storage_class, spv::Spv_1_3); + return spv::StorageClassStorageBuffer; + } + + if (type.getQualifier().isUniformOrBuffer()) { + if (type.getQualifier().isPushConstant()) + return spv::StorageClassPushConstant; + if (type.getBasicType() == glslang::EbtBlock) + return spv::StorageClassUniform; + return spv::StorageClassUniformConstant; + } + + switch (type.getQualifier().storage) { + case glslang::EvqGlobal: return spv::StorageClassPrivate; + case glslang::EvqConstReadOnly: return spv::StorageClassFunction; + case glslang::EvqTemporary: return spv::StorageClassFunction; + case glslang::EvqShared: return spv::StorageClassWorkgroup; +#ifndef GLSLANG_WEB + case glslang::EvqPayload: return spv::StorageClassRayPayloadKHR; + case glslang::EvqPayloadIn: return spv::StorageClassIncomingRayPayloadKHR; + case glslang::EvqHitAttr: return spv::StorageClassHitAttributeKHR; + case glslang::EvqCallableData: return spv::StorageClassCallableDataKHR; + case glslang::EvqCallableDataIn: return spv::StorageClassIncomingCallableDataKHR; +#endif + default: + assert(0); + break; + } + + return spv::StorageClassFunction; +} + +// Add capabilities pertaining to how an array is indexed. +void TGlslangToSpvTraverser::addIndirectionIndexCapabilities(const glslang::TType& baseType, + const glslang::TType& indexType) +{ +#ifndef GLSLANG_WEB + if (indexType.getQualifier().isNonUniform()) { + // deal with an asserted non-uniform index + // SPV_EXT_descriptor_indexing already added in TranslateNonUniformDecoration + if (baseType.getBasicType() == glslang::EbtSampler) { + if (baseType.getQualifier().hasAttachment()) + builder.addCapability(spv::CapabilityInputAttachmentArrayNonUniformIndexingEXT); + else if (baseType.isImage() && baseType.getSampler().isBuffer()) + builder.addCapability(spv::CapabilityStorageTexelBufferArrayNonUniformIndexingEXT); + else if (baseType.isTexture() && baseType.getSampler().isBuffer()) + builder.addCapability(spv::CapabilityUniformTexelBufferArrayNonUniformIndexingEXT); + else if (baseType.isImage()) + builder.addCapability(spv::CapabilityStorageImageArrayNonUniformIndexingEXT); + else if (baseType.isTexture()) + builder.addCapability(spv::CapabilitySampledImageArrayNonUniformIndexingEXT); + } else if (baseType.getBasicType() == glslang::EbtBlock) { + if (baseType.getQualifier().storage == glslang::EvqBuffer) + builder.addCapability(spv::CapabilityStorageBufferArrayNonUniformIndexingEXT); + else if (baseType.getQualifier().storage == glslang::EvqUniform) + builder.addCapability(spv::CapabilityUniformBufferArrayNonUniformIndexingEXT); + } + } else { + // assume a dynamically uniform index + if (baseType.getBasicType() == glslang::EbtSampler) { + if (baseType.getQualifier().hasAttachment()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityInputAttachmentArrayDynamicIndexingEXT); + } else if (baseType.isImage() && baseType.getSampler().isBuffer()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityStorageTexelBufferArrayDynamicIndexingEXT); + } else if (baseType.isTexture() && baseType.getSampler().isBuffer()) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityUniformTexelBufferArrayDynamicIndexingEXT); + } + } + } +#endif +} + +// Return whether or not the given type is something that should be tied to a +// descriptor set. +bool IsDescriptorResource(const glslang::TType& type) +{ + // uniform and buffer blocks are included, unless it is a push_constant + if (type.getBasicType() == glslang::EbtBlock) + return type.getQualifier().isUniformOrBuffer() && + ! type.getQualifier().isShaderRecord() && + ! type.getQualifier().isPushConstant(); + + // non block... + // basically samplerXXX/subpass/sampler/texture are all included + // if they are the global-scope-class, not the function parameter + // (or local, if they ever exist) class. + if (type.getBasicType() == glslang::EbtSampler || + type.getBasicType() == glslang::EbtAccStruct) + return type.getQualifier().isUniformOrBuffer(); + + // None of the above. + return false; +} + +void InheritQualifiers(glslang::TQualifier& child, const glslang::TQualifier& parent) +{ + if (child.layoutMatrix == glslang::ElmNone) + child.layoutMatrix = parent.layoutMatrix; + + if (parent.invariant) + child.invariant = true; + if (parent.flat) + child.flat = true; + if (parent.centroid) + child.centroid = true; +#ifndef GLSLANG_WEB + if (parent.nopersp) + child.nopersp = true; + if (parent.explicitInterp) + child.explicitInterp = true; + if (parent.perPrimitiveNV) + child.perPrimitiveNV = true; + if (parent.perViewNV) + child.perViewNV = true; + if (parent.perTaskNV) + child.perTaskNV = true; + if (parent.patch) + child.patch = true; + if (parent.sample) + child.sample = true; + if (parent.coherent) + child.coherent = true; + if (parent.devicecoherent) + child.devicecoherent = true; + if (parent.queuefamilycoherent) + child.queuefamilycoherent = true; + if (parent.workgroupcoherent) + child.workgroupcoherent = true; + if (parent.subgroupcoherent) + child.subgroupcoherent = true; + if (parent.shadercallcoherent) + child.shadercallcoherent = true; + if (parent.nonprivate) + child.nonprivate = true; + if (parent.volatil) + child.volatil = true; + if (parent.restrict) + child.restrict = true; + if (parent.readonly) + child.readonly = true; + if (parent.writeonly) + child.writeonly = true; +#endif +} + +bool HasNonLayoutQualifiers(const glslang::TType& type, const glslang::TQualifier& qualifier) +{ + // This should list qualifiers that simultaneous satisfy: + // - struct members might inherit from a struct declaration + // (note that non-block structs don't explicitly inherit, + // only implicitly, meaning no decoration involved) + // - affect decorations on the struct members + // (note smooth does not, and expecting something like volatile + // to effect the whole object) + // - are not part of the offset/st430/etc or row/column-major layout + return qualifier.invariant || (qualifier.hasLocation() && type.getBasicType() == glslang::EbtBlock); +} + +// +// Implement the TGlslangToSpvTraverser class. +// + +TGlslangToSpvTraverser::TGlslangToSpvTraverser(unsigned int spvVersion, + const glslang::TIntermediate* glslangIntermediate, + spv::SpvBuildLogger* buildLogger, glslang::SpvOptions& options) : + TIntermTraverser(true, false, true), + options(options), + shaderEntry(nullptr), currentFunction(nullptr), + sequenceDepth(0), logger(buildLogger), + builder(spvVersion, (glslang::GetKhronosToolId() << 16) | glslang::GetSpirvGeneratorVersion(), logger), + inEntryPoint(false), entryPointTerminated(false), linkageOnly(false), + glslangIntermediate(glslangIntermediate), + nanMinMaxClamp(glslangIntermediate->getNanMinMaxClamp()), + nonSemanticDebugPrintf(0) +{ + spv::ExecutionModel executionModel = TranslateExecutionModel(glslangIntermediate->getStage()); + + builder.clearAccessChain(); + builder.setSource(TranslateSourceLanguage(glslangIntermediate->getSource(), glslangIntermediate->getProfile()), + glslangIntermediate->getVersion()); + + if (options.generateDebugInfo) { + builder.setEmitOpLines(); + builder.setSourceFile(glslangIntermediate->getSourceFile()); + + // Set the source shader's text. If for SPV version 1.0, include + // a preamble in comments stating the OpModuleProcessed instructions. + // Otherwise, emit those as actual instructions. + std::string text; + const std::vector& processes = glslangIntermediate->getProcesses(); + for (int p = 0; p < (int)processes.size(); ++p) { + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1) { + text.append("// OpModuleProcessed "); + text.append(processes[p]); + text.append("\n"); + } else + builder.addModuleProcessed(processes[p]); + } + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_1 && (int)processes.size() > 0) + text.append("#line 1\n"); + text.append(glslangIntermediate->getSourceText()); + builder.setSourceText(text); + // Pass name and text for all included files + const std::map& include_txt = glslangIntermediate->getIncludeText(); + for (auto iItr = include_txt.begin(); iItr != include_txt.end(); ++iItr) + builder.addInclude(iItr->first, iItr->second); + } + stdBuiltins = builder.import("GLSL.std.450"); + + spv::AddressingModel addressingModel = spv::AddressingModelLogical; + spv::MemoryModel memoryModel = spv::MemoryModelGLSL450; + + if (glslangIntermediate->usingPhysicalStorageBuffer()) { + addressingModel = spv::AddressingModelPhysicalStorageBuffer64EXT; + builder.addIncorporatedExtension(spv::E_SPV_EXT_physical_storage_buffer, spv::Spv_1_5); + builder.addCapability(spv::CapabilityPhysicalStorageBufferAddressesEXT); + } + if (glslangIntermediate->usingVulkanMemoryModel()) { + memoryModel = spv::MemoryModelVulkanKHR; + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + builder.addIncorporatedExtension(spv::E_SPV_KHR_vulkan_memory_model, spv::Spv_1_5); + } + builder.setMemoryModel(addressingModel, memoryModel); + + if (glslangIntermediate->usingVariablePointers()) { + builder.addCapability(spv::CapabilityVariablePointers); + } + + shaderEntry = builder.makeEntryPoint(glslangIntermediate->getEntryPointName().c_str()); + entryPoint = builder.addEntryPoint(executionModel, shaderEntry, glslangIntermediate->getEntryPointName().c_str()); + + // Add the source extensions + const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); + for (auto it = sourceExtensions.begin(); it != sourceExtensions.end(); ++it) + builder.addSourceExtension(it->c_str()); + + // Add the top-level modes for this shader. + + if (glslangIntermediate->getXfbMode()) { + builder.addCapability(spv::CapabilityTransformFeedback); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeXfb); + } + + if (glslangIntermediate->getLayoutPrimitiveCulling()) { + builder.addCapability(spv::CapabilityRayTraversalPrimitiveCullingProvisionalKHR); + } + + unsigned int mode; + switch (glslangIntermediate->getStage()) { + case EShLangVertex: + builder.addCapability(spv::CapabilityShader); + break; + + case EShLangFragment: + builder.addCapability(spv::CapabilityShader); + if (glslangIntermediate->getPixelCenterInteger()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModePixelCenterInteger); + + if (glslangIntermediate->getOriginUpperLeft()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginUpperLeft); + else + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOriginLowerLeft); + + if (glslangIntermediate->getEarlyFragmentTests()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeEarlyFragmentTests); + + if (glslangIntermediate->getPostDepthCoverage()) { + builder.addCapability(spv::CapabilitySampleMaskPostDepthCoverage); + builder.addExecutionMode(shaderEntry, spv::ExecutionModePostDepthCoverage); + builder.addExtension(spv::E_SPV_KHR_post_depth_coverage); + } + + if (glslangIntermediate->getDepth() != glslang::EldUnchanged && glslangIntermediate->isDepthReplacing()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDepthReplacing); + +#ifndef GLSLANG_WEB + + switch(glslangIntermediate->getDepth()) { + case glslang::EldGreater: mode = spv::ExecutionModeDepthGreater; break; + case glslang::EldLess: mode = spv::ExecutionModeDepthLess; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + switch (glslangIntermediate->getInterlockOrdering()) { + case glslang::EioPixelInterlockOrdered: mode = spv::ExecutionModePixelInterlockOrderedEXT; + break; + case glslang::EioPixelInterlockUnordered: mode = spv::ExecutionModePixelInterlockUnorderedEXT; + break; + case glslang::EioSampleInterlockOrdered: mode = spv::ExecutionModeSampleInterlockOrderedEXT; + break; + case glslang::EioSampleInterlockUnordered: mode = spv::ExecutionModeSampleInterlockUnorderedEXT; + break; + case glslang::EioShadingRateInterlockOrdered: mode = spv::ExecutionModeShadingRateInterlockOrderedEXT; + break; + case glslang::EioShadingRateInterlockUnordered: mode = spv::ExecutionModeShadingRateInterlockUnorderedEXT; + break; + default: mode = spv::ExecutionModeMax; + break; + } + if (mode != spv::ExecutionModeMax) { + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + if (mode == spv::ExecutionModeShadingRateInterlockOrderedEXT || + mode == spv::ExecutionModeShadingRateInterlockUnorderedEXT) { + builder.addCapability(spv::CapabilityFragmentShaderShadingRateInterlockEXT); + } else if (mode == spv::ExecutionModePixelInterlockOrderedEXT || + mode == spv::ExecutionModePixelInterlockUnorderedEXT) { + builder.addCapability(spv::CapabilityFragmentShaderPixelInterlockEXT); + } else { + builder.addCapability(spv::CapabilityFragmentShaderSampleInterlockEXT); + } + builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock); + } +#endif + break; + + case EShLangCompute: + builder.addCapability(spv::CapabilityShader); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), + glslangIntermediate->getLocalSize(1), + glslangIntermediate->getLocalSize(2)); + if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupQuads) { + builder.addCapability(spv::CapabilityComputeDerivativeGroupQuadsNV); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupQuadsNV); + builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives); + } else if (glslangIntermediate->getLayoutDerivativeModeNone() == glslang::LayoutDerivativeGroupLinear) { + builder.addCapability(spv::CapabilityComputeDerivativeGroupLinearNV); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeDerivativeGroupLinearNV); + builder.addExtension(spv::E_SPV_NV_compute_shader_derivatives); + } + break; +#ifndef GLSLANG_WEB + case EShLangTessEvaluation: + case EShLangTessControl: + builder.addCapability(spv::CapabilityTessellation); + + glslang::TLayoutGeometry primitive; + + if (glslangIntermediate->getStage() == EShLangTessControl) { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, + glslangIntermediate->getVertices()); + primitive = glslangIntermediate->getOutputPrimitive(); + } else { + primitive = glslangIntermediate->getInputPrimitive(); + } + + switch (primitive) { + case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; + case glslang::ElgQuads: mode = spv::ExecutionModeQuads; break; + case glslang::ElgIsolines: mode = spv::ExecutionModeIsolines; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + switch (glslangIntermediate->getVertexSpacing()) { + case glslang::EvsEqual: mode = spv::ExecutionModeSpacingEqual; break; + case glslang::EvsFractionalEven: mode = spv::ExecutionModeSpacingFractionalEven; break; + case glslang::EvsFractionalOdd: mode = spv::ExecutionModeSpacingFractionalOdd; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + switch (glslangIntermediate->getVertexOrder()) { + case glslang::EvoCw: mode = spv::ExecutionModeVertexOrderCw; break; + case glslang::EvoCcw: mode = spv::ExecutionModeVertexOrderCcw; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + if (glslangIntermediate->getPointMode()) + builder.addExecutionMode(shaderEntry, spv::ExecutionModePointMode); + break; + + case EShLangGeometry: + builder.addCapability(spv::CapabilityGeometry); + switch (glslangIntermediate->getInputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeInputPoints; break; + case glslang::ElgLines: mode = spv::ExecutionModeInputLines; break; + case glslang::ElgLinesAdjacency: mode = spv::ExecutionModeInputLinesAdjacency; break; + case glslang::ElgTriangles: mode = spv::ExecutionModeTriangles; break; + case glslang::ElgTrianglesAdjacency: mode = spv::ExecutionModeInputTrianglesAdjacency; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + + builder.addExecutionMode(shaderEntry, spv::ExecutionModeInvocations, glslangIntermediate->getInvocations()); + + switch (glslangIntermediate->getOutputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; + case glslang::ElgLineStrip: mode = spv::ExecutionModeOutputLineStrip; break; + case glslang::ElgTriangleStrip: mode = spv::ExecutionModeOutputTriangleStrip; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, glslangIntermediate->getVertices()); + break; + + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + { + auto& extensions = glslangIntermediate->getRequestedExtensions(); + if (extensions.find("GL_NV_ray_tracing") == extensions.end()) { + builder.addCapability(spv::CapabilityRayTracingProvisionalKHR); + builder.addExtension("SPV_KHR_ray_tracing"); + } + else { + builder.addCapability(spv::CapabilityRayTracingNV); + builder.addExtension("SPV_NV_ray_tracing"); + } + break; + } + case EShLangTaskNV: + case EShLangMeshNV: + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeLocalSize, glslangIntermediate->getLocalSize(0), + glslangIntermediate->getLocalSize(1), + glslangIntermediate->getLocalSize(2)); + if (glslangIntermediate->getStage() == EShLangMeshNV) { + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputVertices, + glslangIntermediate->getVertices()); + builder.addExecutionMode(shaderEntry, spv::ExecutionModeOutputPrimitivesNV, + glslangIntermediate->getPrimitives()); + + switch (glslangIntermediate->getOutputPrimitive()) { + case glslang::ElgPoints: mode = spv::ExecutionModeOutputPoints; break; + case glslang::ElgLines: mode = spv::ExecutionModeOutputLinesNV; break; + case glslang::ElgTriangles: mode = spv::ExecutionModeOutputTrianglesNV; break; + default: mode = spv::ExecutionModeMax; break; + } + if (mode != spv::ExecutionModeMax) + builder.addExecutionMode(shaderEntry, (spv::ExecutionMode)mode); + } + break; +#endif + + default: + break; + } +} + +// Finish creating SPV, after the traversal is complete. +void TGlslangToSpvTraverser::finishSpv() +{ + // Finish the entry point function + if (! entryPointTerminated) { + builder.setBuildPoint(shaderEntry->getLastBlock()); + builder.leaveFunction(); + } + + // finish off the entry-point SPV instruction by adding the Input/Output + for (auto it = iOSet.cbegin(); it != iOSet.cend(); ++it) + entryPoint->addIdOperand(*it); + + // Add capabilities, extensions, remove unneeded decorations, etc., + // based on the resulting SPIR-V. + // Note: WebGPU code generation must have the opportunity to aggressively + // prune unreachable merge blocks and continue targets. + builder.postProcess(); +} + +// Write the SPV into 'out'. +void TGlslangToSpvTraverser::dumpSpv(std::vector& out) +{ + builder.dump(out); +} + +// +// Implement the traversal functions. +// +// Return true from interior nodes to have the external traversal +// continue on to children. Return false if children were +// already processed. +// + +// +// Symbols can turn into +// - uniform/input reads +// - output writes +// - complex lvalue base setups: foo.bar[3].... , where we see foo and start up an access chain +// - something simple that degenerates into the last bullet +// +void TGlslangToSpvTraverser::visitSymbol(glslang::TIntermSymbol* symbol) +{ + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (symbol->getType().isStruct()) + glslangTypeToIdMap[symbol->getType().getStruct()] = symbol->getId(); + + if (symbol->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + +#ifdef ENABLE_HLSL + // Skip symbol handling if it is string-typed + if (symbol->getBasicType() == glslang::EbtString) + return; +#endif + + // getSymbolId() will set up all the IO decorations on the first call. + // Formal function parameters were mapped during makeFunctions(). + spv::Id id = getSymbolId(symbol); + + if (builder.isPointer(id)) { + if (!symbol->getType().getQualifier().isParamInput() && + !symbol->getType().getQualifier().isParamOutput()) { + // Include all "static use" and "linkage only" interface variables on the OpEntryPoint instruction + // Consider adding to the OpEntryPoint interface list. + // Only looking at structures if they have at least one member. + if (!symbol->getType().isStruct() || symbol->getType().getStruct()->size() > 0) { + spv::StorageClass sc = builder.getStorageClass(id); + // Before SPIR-V 1.4, we only want to include Input and Output. + // Starting with SPIR-V 1.4, we want all globals. + if ((glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4 && builder.isGlobalStorage(id)) || + (sc == spv::StorageClassInput || sc == spv::StorageClassOutput)) { + iOSet.insert(id); + } + } + } + + // If the SPIR-V type is required to be different than the AST type + // (for ex SubgroupMasks or 3x4 ObjectToWorld/WorldToObject matrices), + // translate now from the SPIR-V type to the AST type, for the consuming + // operation. + // Note this turns it from an l-value to an r-value. + // Currently, all symbols needing this are inputs; avoid the map lookup when non-input. + if (symbol->getType().getQualifier().storage == glslang::EvqVaryingIn) + id = translateForcedType(id); + } + + // Only process non-linkage-only nodes for generating actual static uses + if (! linkageOnly || symbol->getQualifier().isSpecConstant()) { + // Prepare to generate code for the access + + // L-value chains will be computed left to right. We're on the symbol now, + // which is the left-most part of the access chain, so now is "clear" time, + // followed by setting the base. + builder.clearAccessChain(); + + // For now, we consider all user variables as being in memory, so they are pointers, + // except for + // A) R-Value arguments to a function, which are an intermediate object. + // See comments in handleUserFunctionCall(). + // B) Specialization constants (normal constants don't even come in as a variable), + // These are also pure R-values. + // C) R-Values from type translation, see above call to translateForcedType() + glslang::TQualifier qualifier = symbol->getQualifier(); + if (qualifier.isSpecConstant() || rValueParameters.find(symbol->getId()) != rValueParameters.end() || + !builder.isPointerType(builder.getTypeId(id))) + builder.setAccessChainRValue(id); + else + builder.setAccessChainLValue(id); + } + +#ifdef ENABLE_HLSL + // Process linkage-only nodes for any special additional interface work. + if (linkageOnly) { + if (glslangIntermediate->getHlslFunctionality1()) { + // Map implicit counter buffers to their originating buffers, which should have been + // seen by now, given earlier pruning of unused counters, and preservation of order + // of declaration. + if (symbol->getType().getQualifier().isUniformOrBuffer()) { + if (!glslangIntermediate->hasCounterBufferName(symbol->getName())) { + // Save possible originating buffers for counter buffers, keyed by + // making the potential counter-buffer name. + std::string keyName = symbol->getName().c_str(); + keyName = glslangIntermediate->addCounterBufferName(keyName); + counterOriginator[keyName] = symbol; + } else { + // Handle a counter buffer, by finding the saved originating buffer. + std::string keyName = symbol->getName().c_str(); + auto it = counterOriginator.find(keyName); + if (it != counterOriginator.end()) { + id = getSymbolId(it->second); + if (id != spv::NoResult) { + spv::Id counterId = getSymbolId(symbol); + if (counterId != spv::NoResult) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addDecorationId(id, spv::DecorationHlslCounterBufferGOOGLE, counterId); + } + } + } + } + } + } + } +#endif +} + +bool TGlslangToSpvTraverser::visitBinary(glslang::TVisit /* visit */, glslang::TIntermBinary* node) +{ + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + if (node->getLeft()->getAsSymbolNode() != nullptr && node->getLeft()->getType().isStruct()) { + glslangTypeToIdMap[node->getLeft()->getType().getStruct()] = node->getLeft()->getAsSymbolNode()->getId(); + } + if (node->getRight()->getAsSymbolNode() != nullptr && node->getRight()->getType().isStruct()) { + glslangTypeToIdMap[node->getRight()->getType().getStruct()] = node->getRight()->getAsSymbolNode()->getId(); + } + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + // First, handle special cases + switch (node->getOp()) { + case glslang::EOpAssign: + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + case glslang::EOpAndAssign: + case glslang::EOpInclusiveOrAssign: + case glslang::EOpExclusiveOrAssign: + case glslang::EOpLeftShiftAssign: + case glslang::EOpRightShiftAssign: + // A bin-op assign "a += b" means the same thing as "a = a + b" + // where a is evaluated before b. For a simple assignment, GLSL + // says to evaluate the left before the right. So, always, left + // node then right node. + { + // get the left l-value, save it away + builder.clearAccessChain(); + node->getLeft()->traverse(this); + spv::Builder::AccessChain lValue = builder.getAccessChain(); + + // evaluate the right + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id rValue = accessChainLoad(node->getRight()->getType()); + + if (node->getOp() != glslang::EOpAssign) { + // the left is also an r-value + builder.setAccessChain(lValue); + spv::Id leftRValue = accessChainLoad(node->getLeft()->getType()); + + // do the operation + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + rValue = createBinaryOperation(node->getOp(), decorations, + convertGlslangToSpvType(node->getType()), leftRValue, rValue, + node->getType().getBasicType()); + + // these all need their counterparts in createBinaryOperation() + assert(rValue != spv::NoResult); + } + + // store the result + builder.setAccessChain(lValue); + multiTypeStore(node->getLeft()->getType(), rValue); + + // assignments are expressions having an rValue after they are evaluated... + builder.clearAccessChain(); + builder.setAccessChainRValue(rValue); + } + return false; + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + { + // Structure, array, matrix, or vector indirection with statically known index. + // Get the left part of the access chain. + node->getLeft()->traverse(this); + + // Add the next element in the chain + + const int glslangIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (! node->getLeft()->getType().isArray() && + node->getLeft()->getType().isVector() && + node->getOp() == glslang::EOpIndexDirect) { + // This is essentially a hard-coded vector swizzle of size 1, + // so short circuit the access-chain stuff with a swizzle. + std::vector swizzle; + swizzle.push_back(glslangIndex); + int dummySize; + builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()), + TranslateCoherent(node->getLeft()->getType()), + glslangIntermediate->getBaseAlignmentScalar( + node->getLeft()->getType(), dummySize)); + } else { + + // Load through a block reference is performed with a dot operator that + // is mapped to EOpIndexDirectStruct. When we get to the actual reference, + // do a load and reset the access chain. + if (node->getLeft()->isReference() && + !node->getLeft()->getType().isArray() && + node->getOp() == glslang::EOpIndexDirectStruct) + { + spv::Id left = accessChainLoad(node->getLeft()->getType()); + builder.clearAccessChain(); + builder.setAccessChainLValue(left); + } + + int spvIndex = glslangIndex; + if (node->getLeft()->getBasicType() == glslang::EbtBlock && + node->getOp() == glslang::EOpIndexDirectStruct) + { + // This may be, e.g., an anonymous block-member selection, which generally need + // index remapping due to hidden members in anonymous blocks. + int glslangId = glslangTypeToIdMap[node->getLeft()->getType().getStruct()]; + if (memberRemapper.find(glslangId) != memberRemapper.end()) { + std::vector& remapper = memberRemapper[glslangId]; + assert(remapper.size() > 0); + spvIndex = remapper[glslangIndex]; + } + } + + // normal case for indexing array or structure or block + builder.accessChainPush(builder.makeIntConstant(spvIndex), + TranslateCoherent(node->getLeft()->getType()), + node->getLeft()->getType().getBufferReferenceAlignment()); + + // Add capabilities here for accessing PointSize and clip/cull distance. + // We have deferred generation of associated capabilities until now. + if (node->getLeft()->getType().isStruct() && ! node->getLeft()->getType().isArray()) + declareUseOfStructMember(*(node->getLeft()->getType().getStruct()), glslangIndex); + } + } + return false; + case glslang::EOpIndexIndirect: + { + // Array, matrix, or vector indirection with variable index. + // Will use native SPIR-V access-chain for and array indirection; + // matrices are arrays of vectors, so will also work for a matrix. + // Will use the access chain's 'component' for variable index into a vector. + + // This adapter is building access chains left to right. + // Set up the access chain to the left. + node->getLeft()->traverse(this); + + // save it so that computing the right side doesn't trash it + spv::Builder::AccessChain partial = builder.getAccessChain(); + + // compute the next index in the chain + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id index = accessChainLoad(node->getRight()->getType()); + + addIndirectionIndexCapabilities(node->getLeft()->getType(), node->getRight()->getType()); + + // restore the saved access chain + builder.setAccessChain(partial); + + if (! node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) { + int dummySize; + builder.accessChainPushComponent(index, convertGlslangToSpvType(node->getLeft()->getType()), + TranslateCoherent(node->getLeft()->getType()), + glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), + dummySize)); + } else + builder.accessChainPush(index, TranslateCoherent(node->getLeft()->getType()), + node->getLeft()->getType().getBufferReferenceAlignment()); + } + return false; + case glslang::EOpVectorSwizzle: + { + node->getLeft()->traverse(this); + std::vector swizzle; + convertSwizzle(*node->getRight()->getAsAggregate(), swizzle); + int dummySize; + builder.accessChainPushSwizzle(swizzle, convertGlslangToSpvType(node->getLeft()->getType()), + TranslateCoherent(node->getLeft()->getType()), + glslangIntermediate->getBaseAlignmentScalar(node->getLeft()->getType(), + dummySize)); + } + return false; + case glslang::EOpMatrixSwizzle: + logger->missingFunctionality("matrix swizzle"); + return true; + case glslang::EOpLogicalOr: + case glslang::EOpLogicalAnd: + { + + // These may require short circuiting, but can sometimes be done as straight + // binary operations. The right operand must be short circuited if it has + // side effects, and should probably be if it is complex. + if (isTrivial(node->getRight()->getAsTyped())) + break; // handle below as a normal binary operation + // otherwise, we need to do dynamic short circuiting on the right operand + spv::Id result = createShortCircuit(node->getOp(), *node->getLeft()->getAsTyped(), + *node->getRight()->getAsTyped()); + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } + return false; + default: + break; + } + + // Assume generic binary op... + + // get right operand + builder.clearAccessChain(); + node->getLeft()->traverse(this); + spv::Id left = accessChainLoad(node->getLeft()->getType()); + + // get left operand + builder.clearAccessChain(); + node->getRight()->traverse(this); + spv::Id right = accessChainLoad(node->getRight()->getType()); + + // get result + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + spv::Id result = createBinaryOperation(node->getOp(), decorations, + convertGlslangToSpvType(node->getType()), left, right, + node->getLeft()->getType().getBasicType()); + + builder.clearAccessChain(); + if (! result) { + logger->missingFunctionality("unknown glslang binary operation"); + return true; // pick up a child as the place-holder result + } else { + builder.setAccessChainRValue(result); + return false; + } +} + +// Figure out what, if any, type changes are needed when accessing a specific built-in. +// Returns . +// Also see comment for 'forceType', regarding tracking SPIR-V-required types. +std::pair TGlslangToSpvTraverser::getForcedType(glslang::TBuiltInVariable glslangBuiltIn, + const glslang::TType& glslangType) +{ + switch(glslangBuiltIn) + { + case glslang::EbvSubGroupEqMask: + case glslang::EbvSubGroupGeMask: + case glslang::EbvSubGroupGtMask: + case glslang::EbvSubGroupLeMask: + case glslang::EbvSubGroupLtMask: { + // these require changing a 64-bit scaler -> a vector of 32-bit components + if (glslangType.isVector()) + break; + std::pair ret(builder.makeVectorType(builder.makeUintType(32), 4), + builder.makeUintType(64)); + return ret; + } + // There are no SPIR-V builtins defined for these and map onto original non-transposed + // builtins. During visitBinary we insert a transpose + case glslang::EbvWorldToObject3x4: + case glslang::EbvObjectToWorld3x4: { + spv::Id mat43 = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + spv::Id mat34 = builder.makeMatrixType(builder.makeFloatType(32), 3, 4); + std::pair ret(mat43, mat34); + return ret; + } + default: + break; + } + + std::pair ret(spv::NoType, spv::NoType); + return ret; +} + +// For an object previously identified (see getForcedType() and forceType) +// as needing type translations, do the translation needed for a load, turning +// an L-value into in R-value. +spv::Id TGlslangToSpvTraverser::translateForcedType(spv::Id object) +{ + const auto forceIt = forceType.find(object); + if (forceIt == forceType.end()) + return object; + + spv::Id desiredTypeId = forceIt->second; + spv::Id objectTypeId = builder.getTypeId(object); + assert(builder.isPointerType(objectTypeId)); + objectTypeId = builder.getContainedTypeId(objectTypeId); + if (builder.isVectorType(objectTypeId) && + builder.getScalarTypeWidth(builder.getContainedTypeId(objectTypeId)) == 32) { + if (builder.getScalarTypeWidth(desiredTypeId) == 64) { + // handle 32-bit v.xy* -> 64-bit + builder.clearAccessChain(); + builder.setAccessChainLValue(object); + object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, objectTypeId); + std::vector components; + components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 0)); + components.push_back(builder.createCompositeExtract(object, builder.getContainedTypeId(objectTypeId), 1)); + + spv::Id vecType = builder.makeVectorType(builder.getContainedTypeId(objectTypeId), 2); + return builder.createUnaryOp(spv::OpBitcast, desiredTypeId, + builder.createCompositeConstruct(vecType, components)); + } else { + logger->missingFunctionality("forcing 32-bit vector type to non 64-bit scalar"); + } + } else if (builder.isMatrixType(objectTypeId)) { + // There are no SPIR-V builtins defined for 3x4 variants of ObjectToWorld/WorldToObject + // and we insert a transpose after loading the original non-transposed builtins + builder.clearAccessChain(); + builder.setAccessChainLValue(object); + object = builder.accessChainLoad(spv::NoPrecision, spv::DecorationMax, objectTypeId); + return builder.createUnaryOp(spv::OpTranspose, desiredTypeId, object); + + } else { + logger->missingFunctionality("forcing non 32-bit vector type"); + } + + return object; +} + +bool TGlslangToSpvTraverser::visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) +{ + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + spv::Id result = spv::NoResult; + + // try texturing first + result = createImageTextureFunctionCall(node); + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; // done with this node + } + + // Non-texturing. + + if (node->getOp() == glslang::EOpArrayLength) { + // Quite special; won't want to evaluate the operand. + + // Currently, the front-end does not allow .length() on an array until it is sized, + // except for the last block membeor of an SSBO. + // TODO: If this changes, link-time sized arrays might show up here, and need their + // size extracted. + + // Normal .length() would have been constant folded by the front-end. + // So, this has to be block.lastMember.length(). + // SPV wants "block" and member number as the operands, go get them. + + spv::Id length; + if (node->getOperand()->getType().isCoopMat()) { + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + spv::Id typeId = convertGlslangToSpvType(node->getOperand()->getType()); + assert(builder.isCooperativeMatrixType(typeId)); + + length = builder.createCooperativeMatrixLength(typeId); + } else { + glslang::TIntermTyped* block = node->getOperand()->getAsBinaryNode()->getLeft(); + block->traverse(this); + unsigned int member = node->getOperand()->getAsBinaryNode()->getRight()->getAsConstantUnion() + ->getConstArray()[0].getUConst(); + length = builder.createArrayLength(builder.accessChainGetLValue(), member); + } + + // GLSL semantics say the result of .length() is an int, while SPIR-V says + // signedness must be 0. So, convert from SPIR-V unsigned back to GLSL's + // AST expectation of a signed result. + if (glslangIntermediate->getSource() == glslang::EShSourceGlsl) { + if (builder.isInSpecConstCodeGenMode()) { + length = builder.createBinOp(spv::OpIAdd, builder.makeIntType(32), length, builder.makeIntConstant(0)); + } else { + length = builder.createUnaryOp(spv::OpBitcast, builder.makeIntType(32), length); + } + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(length); + + return false; + } + + // Start by evaluating the operand + + // Does it need a swizzle inversion? If so, evaluation is inverted; + // operate first on the swizzle base, then apply the swizzle. + spv::Id invertedType = spv::NoType; + auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? + invertedType : convertGlslangToSpvType(node->getType()); }; + if (node->getOp() == glslang::EOpInterpolateAtCentroid) + invertedType = getInvertedSwizzleType(*node->getOperand()); + + builder.clearAccessChain(); + TIntermNode *operandNode; + if (invertedType != spv::NoType) + operandNode = node->getOperand()->getAsBinaryNode()->getLeft(); + else + operandNode = node->getOperand(); + + operandNode->traverse(this); + + spv::Id operand = spv::NoResult; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + +#ifndef GLSLANG_WEB + if (node->getOp() == glslang::EOpAtomicCounterIncrement || + node->getOp() == glslang::EOpAtomicCounterDecrement || + node->getOp() == glslang::EOpAtomicCounter || + node->getOp() == glslang::EOpInterpolateAtCentroid || + node->getOp() == glslang::EOpRayQueryProceed || + node->getOp() == glslang::EOpRayQueryGetRayTMin || + node->getOp() == glslang::EOpRayQueryGetRayFlags || + node->getOp() == glslang::EOpRayQueryGetWorldRayOrigin || + node->getOp() == glslang::EOpRayQueryGetWorldRayDirection || + node->getOp() == glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque || + node->getOp() == glslang::EOpRayQueryTerminate || + node->getOp() == glslang::EOpRayQueryConfirmIntersection) { + operand = builder.accessChainGetLValue(); // Special case l-value operands + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + lvalueCoherentFlags |= TranslateCoherent(operandNode->getAsTyped()->getType()); + } else +#endif + { + operand = accessChainLoad(node->getOperand()->getType()); + } + + OpDecorations decorations = { TranslatePrecisionDecoration(node->getOperationPrecision()), + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + + // it could be a conversion + if (! result) + result = createConversion(node->getOp(), decorations, resultType(), operand, + node->getOperand()->getBasicType()); + + // if not, then possibly an operation + if (! result) + result = createUnaryOperation(node->getOp(), decorations, resultType(), operand, + node->getOperand()->getBasicType(), lvalueCoherentFlags); + + if (result) { + if (invertedType) { + result = createInvertedSwizzle(decorations.precision, *node->getOperand(), result); + decorations.addNonUniform(builder, result); + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; // done with this node + } + + // it must be a special case, check... + switch (node->getOp()) { + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + { + // we need the integer value "1" or the floating point "1.0" to add/subtract + spv::Id one = 0; + if (node->getBasicType() == glslang::EbtFloat) + one = builder.makeFloatConstant(1.0F); +#ifndef GLSLANG_WEB + else if (node->getBasicType() == glslang::EbtDouble) + one = builder.makeDoubleConstant(1.0); + else if (node->getBasicType() == glslang::EbtFloat16) + one = builder.makeFloat16Constant(1.0F); + else if (node->getBasicType() == glslang::EbtInt8 || node->getBasicType() == glslang::EbtUint8) + one = builder.makeInt8Constant(1); + else if (node->getBasicType() == glslang::EbtInt16 || node->getBasicType() == glslang::EbtUint16) + one = builder.makeInt16Constant(1); + else if (node->getBasicType() == glslang::EbtInt64 || node->getBasicType() == glslang::EbtUint64) + one = builder.makeInt64Constant(1); +#endif + else + one = builder.makeIntConstant(1); + glslang::TOperator op; + if (node->getOp() == glslang::EOpPreIncrement || + node->getOp() == glslang::EOpPostIncrement) + op = glslang::EOpAdd; + else + op = glslang::EOpSub; + + spv::Id result = createBinaryOperation(op, decorations, + convertGlslangToSpvType(node->getType()), operand, one, + node->getType().getBasicType()); + assert(result != spv::NoResult); + + // The result of operation is always stored, but conditionally the + // consumed result. The consumed result is always an r-value. + builder.accessChainStore(result, + TranslateNonUniformDecoration(node->getOperand()->getType().getQualifier())); + builder.clearAccessChain(); + if (node->getOp() == glslang::EOpPreIncrement || + node->getOp() == glslang::EOpPreDecrement) + builder.setAccessChainRValue(result); + else + builder.setAccessChainRValue(operand); + } + + return false; + +#ifndef GLSLANG_WEB + case glslang::EOpEmitStreamVertex: + builder.createNoResultOp(spv::OpEmitStreamVertex, operand); + return false; + case glslang::EOpEndStreamPrimitive: + builder.createNoResultOp(spv::OpEndStreamPrimitive, operand); + return false; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR, operand); + return false; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR, operand); + return false; +#endif + + default: + logger->missingFunctionality("unknown glslang unary"); + return true; // pick up operand as placeholder result + } +} + +// Construct a composite object, recursively copying members if their types don't match +spv::Id TGlslangToSpvTraverser::createCompositeConstruct(spv::Id resultTypeId, std::vector constituents) +{ + for (int c = 0; c < (int)constituents.size(); ++c) { + spv::Id& constituent = constituents[c]; + spv::Id lType = builder.getContainedTypeId(resultTypeId, c); + spv::Id rType = builder.getTypeId(constituent); + if (lType != rType) { + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + constituent = builder.createUnaryOp(spv::OpCopyLogical, lType, constituent); + } else if (builder.isStructType(rType)) { + std::vector rTypeConstituents; + int numrTypeConstituents = builder.getNumTypeConstituents(rType); + for (int i = 0; i < numrTypeConstituents; ++i) { + rTypeConstituents.push_back(builder.createCompositeExtract(constituent, + builder.getContainedTypeId(rType, i), i)); + } + constituents[c] = createCompositeConstruct(lType, rTypeConstituents); + } else { + assert(builder.isArrayType(rType)); + std::vector rTypeConstituents; + int numrTypeConstituents = builder.getNumTypeConstituents(rType); + + spv::Id elementRType = builder.getContainedTypeId(rType); + for (int i = 0; i < numrTypeConstituents; ++i) { + rTypeConstituents.push_back(builder.createCompositeExtract(constituent, elementRType, i)); + } + constituents[c] = createCompositeConstruct(lType, rTypeConstituents); + } + } + } + return builder.createCompositeConstruct(resultTypeId, constituents); +} + +bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TIntermAggregate* node) +{ + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + + spv::Id result = spv::NoResult; + spv::Id invertedType = spv::NoType; // to use to override the natural type of the node + std::vector complexLvalues; // for holding swizzling l-values too complex for + // SPIR-V, for an out parameter + std::vector complexLValueQualifiers; + std::vector temporaryLvalues; // temporaries to pass, as proxies for complexLValues + + auto resultType = [&invertedType, &node, this](){ return invertedType != spv::NoType ? + invertedType : + convertGlslangToSpvType(node->getType()); }; + + // try texturing + result = createImageTextureFunctionCall(node); + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } +#ifndef GLSLANG_WEB + else if (node->getOp() == glslang::EOpImageStore || + node->getOp() == glslang::EOpImageStoreLod || + node->getOp() == glslang::EOpImageAtomicStore) { + // "imageStore" is a special case, which has no result + return false; + } +#endif + + glslang::TOperator binOp = glslang::EOpNull; + bool reduceComparison = true; + bool isMatrix = false; + bool noReturnValue = false; + bool atomic = false; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + + assert(node->getOp()); + + spv::Decoration precision = TranslatePrecisionDecoration(node->getOperationPrecision()); + + switch (node->getOp()) { + case glslang::EOpSequence: + { + if (preVisit) + ++sequenceDepth; + else + --sequenceDepth; + + if (sequenceDepth == 1) { + // If this is the parent node of all the functions, we want to see them + // early, so all call points have actual SPIR-V functions to reference. + // In all cases, still let the traverser visit the children for us. + makeFunctions(node->getAsAggregate()->getSequence()); + + // Also, we want all globals initializers to go into the beginning of the entry point, before + // anything else gets there, so visit out of order, doing them all now. + makeGlobalInitializers(node->getAsAggregate()->getSequence()); + + // Initializers are done, don't want to visit again, but functions and link objects need to be processed, + // so do them manually. + visitFunctions(node->getAsAggregate()->getSequence()); + + return false; + } + + return true; + } + case glslang::EOpLinkerObjects: + { + if (visit == glslang::EvPreVisit) + linkageOnly = true; + else + linkageOnly = false; + + return true; + } + case glslang::EOpComma: + { + // processing from left to right naturally leaves the right-most + // lying around in the access chain + glslang::TIntermSequence& glslangOperands = node->getSequence(); + for (int i = 0; i < (int)glslangOperands.size(); ++i) + glslangOperands[i]->traverse(this); + + return false; + } + case glslang::EOpFunction: + if (visit == glslang::EvPreVisit) { + if (isShaderEntryPoint(node)) { + inEntryPoint = true; + builder.setBuildPoint(shaderEntry->getLastBlock()); + currentFunction = shaderEntry; + } else { + handleFunctionEntry(node); + } + } else { + if (inEntryPoint) + entryPointTerminated = true; + builder.leaveFunction(); + inEntryPoint = false; + } + + return true; + case glslang::EOpParameters: + // Parameters will have been consumed by EOpFunction processing, but not + // the body, so we still visited the function node's children, making this + // child redundant. + return false; + case glslang::EOpFunctionCall: + { + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + if (node->isUserDefined()) + result = handleUserFunctionCall(node); + if (result) { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } else + logger->missingFunctionality("missing user function; linker needs to catch that"); + + return false; + } + case glslang::EOpConstructMat2x2: + case glslang::EOpConstructMat2x3: + case glslang::EOpConstructMat2x4: + case glslang::EOpConstructMat3x2: + case glslang::EOpConstructMat3x3: + case glslang::EOpConstructMat3x4: + case glslang::EOpConstructMat4x2: + case glslang::EOpConstructMat4x3: + case glslang::EOpConstructMat4x4: + case glslang::EOpConstructDMat2x2: + case glslang::EOpConstructDMat2x3: + case glslang::EOpConstructDMat2x4: + case glslang::EOpConstructDMat3x2: + case glslang::EOpConstructDMat3x3: + case glslang::EOpConstructDMat3x4: + case glslang::EOpConstructDMat4x2: + case glslang::EOpConstructDMat4x3: + case glslang::EOpConstructDMat4x4: + case glslang::EOpConstructIMat2x2: + case glslang::EOpConstructIMat2x3: + case glslang::EOpConstructIMat2x4: + case glslang::EOpConstructIMat3x2: + case glslang::EOpConstructIMat3x3: + case glslang::EOpConstructIMat3x4: + case glslang::EOpConstructIMat4x2: + case glslang::EOpConstructIMat4x3: + case glslang::EOpConstructIMat4x4: + case glslang::EOpConstructUMat2x2: + case glslang::EOpConstructUMat2x3: + case glslang::EOpConstructUMat2x4: + case glslang::EOpConstructUMat3x2: + case glslang::EOpConstructUMat3x3: + case glslang::EOpConstructUMat3x4: + case glslang::EOpConstructUMat4x2: + case glslang::EOpConstructUMat4x3: + case glslang::EOpConstructUMat4x4: + case glslang::EOpConstructBMat2x2: + case glslang::EOpConstructBMat2x3: + case glslang::EOpConstructBMat2x4: + case glslang::EOpConstructBMat3x2: + case glslang::EOpConstructBMat3x3: + case glslang::EOpConstructBMat3x4: + case glslang::EOpConstructBMat4x2: + case glslang::EOpConstructBMat4x3: + case glslang::EOpConstructBMat4x4: + case glslang::EOpConstructF16Mat2x2: + case glslang::EOpConstructF16Mat2x3: + case glslang::EOpConstructF16Mat2x4: + case glslang::EOpConstructF16Mat3x2: + case glslang::EOpConstructF16Mat3x3: + case glslang::EOpConstructF16Mat3x4: + case glslang::EOpConstructF16Mat4x2: + case glslang::EOpConstructF16Mat4x3: + case glslang::EOpConstructF16Mat4x4: + isMatrix = true; + // fall through + case glslang::EOpConstructFloat: + case glslang::EOpConstructVec2: + case glslang::EOpConstructVec3: + case glslang::EOpConstructVec4: + case glslang::EOpConstructDouble: + case glslang::EOpConstructDVec2: + case glslang::EOpConstructDVec3: + case glslang::EOpConstructDVec4: + case glslang::EOpConstructFloat16: + case glslang::EOpConstructF16Vec2: + case glslang::EOpConstructF16Vec3: + case glslang::EOpConstructF16Vec4: + case glslang::EOpConstructBool: + case glslang::EOpConstructBVec2: + case glslang::EOpConstructBVec3: + case glslang::EOpConstructBVec4: + case glslang::EOpConstructInt8: + case glslang::EOpConstructI8Vec2: + case glslang::EOpConstructI8Vec3: + case glslang::EOpConstructI8Vec4: + case glslang::EOpConstructUint8: + case glslang::EOpConstructU8Vec2: + case glslang::EOpConstructU8Vec3: + case glslang::EOpConstructU8Vec4: + case glslang::EOpConstructInt16: + case glslang::EOpConstructI16Vec2: + case glslang::EOpConstructI16Vec3: + case glslang::EOpConstructI16Vec4: + case glslang::EOpConstructUint16: + case glslang::EOpConstructU16Vec2: + case glslang::EOpConstructU16Vec3: + case glslang::EOpConstructU16Vec4: + case glslang::EOpConstructInt: + case glslang::EOpConstructIVec2: + case glslang::EOpConstructIVec3: + case glslang::EOpConstructIVec4: + case glslang::EOpConstructUint: + case glslang::EOpConstructUVec2: + case glslang::EOpConstructUVec3: + case glslang::EOpConstructUVec4: + case glslang::EOpConstructInt64: + case glslang::EOpConstructI64Vec2: + case glslang::EOpConstructI64Vec3: + case glslang::EOpConstructI64Vec4: + case glslang::EOpConstructUint64: + case glslang::EOpConstructU64Vec2: + case glslang::EOpConstructU64Vec3: + case glslang::EOpConstructU64Vec4: + case glslang::EOpConstructStruct: + case glslang::EOpConstructTextureSampler: + case glslang::EOpConstructReference: + case glslang::EOpConstructCooperativeMatrix: + { + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + std::vector arguments; + translateArguments(*node, arguments, lvalueCoherentFlags); + spv::Id constructed; + if (node->getOp() == glslang::EOpConstructTextureSampler) + constructed = builder.createOp(spv::OpSampledImage, resultType(), arguments); + else if (node->getOp() == glslang::EOpConstructStruct || + node->getOp() == glslang::EOpConstructCooperativeMatrix || + node->getType().isArray()) { + std::vector constituents; + for (int c = 0; c < (int)arguments.size(); ++c) + constituents.push_back(arguments[c]); + constructed = createCompositeConstruct(resultType(), constituents); + } else if (isMatrix) + constructed = builder.createMatrixConstructor(precision, arguments, resultType()); + else + constructed = builder.createConstructor(precision, arguments, resultType()); + + if (node->getType().getQualifier().isNonUniform()) { + builder.addDecoration(constructed, spv::DecorationNonUniformEXT); + } + + builder.clearAccessChain(); + builder.setAccessChainRValue(constructed); + + return false; + } + + // These six are component-wise compares with component-wise results. + // Forward on to createBinaryOperation(), requesting a vector result. + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpVectorEqual: + case glslang::EOpVectorNotEqual: + { + // Map the operation to a binary + binOp = node->getOp(); + reduceComparison = false; + switch (node->getOp()) { + case glslang::EOpVectorEqual: binOp = glslang::EOpVectorEqual; break; + case glslang::EOpVectorNotEqual: binOp = glslang::EOpVectorNotEqual; break; + default: binOp = node->getOp(); break; + } + + break; + } + case glslang::EOpMul: + // component-wise matrix multiply + binOp = glslang::EOpMul; + break; + case glslang::EOpOuterProduct: + // two vectors multiplied to make a matrix + binOp = glslang::EOpOuterProduct; + break; + case glslang::EOpDot: + { + // for scalar dot product, use multiply + glslang::TIntermSequence& glslangOperands = node->getSequence(); + if (glslangOperands[0]->getAsTyped()->getVectorSize() == 1) + binOp = glslang::EOpMul; + break; + } + case glslang::EOpMod: + // when an aggregate, this is the floating-point mod built-in function, + // which can be emitted by the one in createBinaryOperation() + binOp = glslang::EOpMod; + break; + + case glslang::EOpEmitVertex: + case glslang::EOpEndPrimitive: + case glslang::EOpBarrier: + case glslang::EOpMemoryBarrier: + case glslang::EOpMemoryBarrierAtomicCounter: + case glslang::EOpMemoryBarrierBuffer: + case glslang::EOpMemoryBarrierImage: + case glslang::EOpMemoryBarrierShared: + case glslang::EOpGroupMemoryBarrier: + case glslang::EOpDeviceMemoryBarrier: + case glslang::EOpAllMemoryBarrierWithGroupSync: + case glslang::EOpDeviceMemoryBarrierWithGroupSync: + case glslang::EOpWorkgroupMemoryBarrier: + case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: + case glslang::EOpSubgroupBarrier: + case glslang::EOpSubgroupMemoryBarrier: + case glslang::EOpSubgroupMemoryBarrierBuffer: + case glslang::EOpSubgroupMemoryBarrierImage: + case glslang::EOpSubgroupMemoryBarrierShared: + noReturnValue = true; + // These all have 0 operands and will naturally finish up in the code below for 0 operands + break; + + case glslang::EOpAtomicAdd: + case glslang::EOpAtomicMin: + case glslang::EOpAtomicMax: + case glslang::EOpAtomicAnd: + case glslang::EOpAtomicOr: + case glslang::EOpAtomicXor: + case glslang::EOpAtomicExchange: + case glslang::EOpAtomicCompSwap: + atomic = true; + break; + +#ifndef GLSLANG_WEB + case glslang::EOpAtomicStore: + noReturnValue = true; + // fallthrough + case glslang::EOpAtomicLoad: + atomic = true; + break; + + case glslang::EOpAtomicCounterAdd: + case glslang::EOpAtomicCounterSubtract: + case glslang::EOpAtomicCounterMin: + case glslang::EOpAtomicCounterMax: + case glslang::EOpAtomicCounterAnd: + case glslang::EOpAtomicCounterOr: + case glslang::EOpAtomicCounterXor: + case glslang::EOpAtomicCounterExchange: + case glslang::EOpAtomicCounterCompSwap: + builder.addExtension("SPV_KHR_shader_atomic_counter_ops"); + builder.addCapability(spv::CapabilityAtomicStorageOps); + atomic = true; + break; + + case glslang::EOpAbsDifference: + case glslang::EOpAddSaturate: + case glslang::EOpSubSaturate: + case glslang::EOpAverage: + case glslang::EOpAverageRounded: + case glslang::EOpMul32x16: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + binOp = node->getOp(); + break; + + case glslang::EOpIgnoreIntersection: + case glslang::EOpTerminateRay: + case glslang::EOpTrace: + case glslang::EOpExecuteCallable: + case glslang::EOpWritePackedPrimitiveIndices4x8NV: + noReturnValue = true; + break; + case glslang::EOpRayQueryInitialize: + case glslang::EOpRayQueryTerminate: + case glslang::EOpRayQueryGenerateIntersection: + case glslang::EOpRayQueryConfirmIntersection: + builder.addExtension("SPV_KHR_ray_query"); + builder.addCapability(spv::CapabilityRayQueryProvisionalKHR); + noReturnValue = true; + break; + case glslang::EOpRayQueryProceed: + case glslang::EOpRayQueryGetIntersectionType: + case glslang::EOpRayQueryGetRayTMin: + case glslang::EOpRayQueryGetRayFlags: + case glslang::EOpRayQueryGetIntersectionT: + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + case glslang::EOpRayQueryGetIntersectionInstanceId: + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + case glslang::EOpRayQueryGetIntersectionBarycentrics: + case glslang::EOpRayQueryGetIntersectionFrontFace: + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + case glslang::EOpRayQueryGetWorldRayDirection: + case glslang::EOpRayQueryGetWorldRayOrigin: + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + case glslang::EOpRayQueryGetIntersectionWorldToObject: + builder.addExtension("SPV_KHR_ray_query"); + builder.addCapability(spv::CapabilityRayQueryProvisionalKHR); + break; + case glslang::EOpCooperativeMatrixLoad: + case glslang::EOpCooperativeMatrixStore: + noReturnValue = true; + break; + case glslang::EOpBeginInvocationInterlock: + case glslang::EOpEndInvocationInterlock: + builder.addExtension(spv::E_SPV_EXT_fragment_shader_interlock); + noReturnValue = true; + break; +#endif + + case glslang::EOpDebugPrintf: + noReturnValue = true; + break; + + default: + break; + } + + // + // See if it maps to a regular operation. + // + if (binOp != glslang::EOpNull) { + glslang::TIntermTyped* left = node->getSequence()[0]->getAsTyped(); + glslang::TIntermTyped* right = node->getSequence()[1]->getAsTyped(); + assert(left && right); + + builder.clearAccessChain(); + left->traverse(this); + spv::Id leftId = accessChainLoad(left->getType()); + + builder.clearAccessChain(); + right->traverse(this); + spv::Id rightId = accessChainLoad(right->getType()); + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + OpDecorations decorations = { precision, + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + result = createBinaryOperation(binOp, decorations, + resultType(), leftId, rightId, + left->getType().getBasicType(), reduceComparison); + + // code above should only make binOp that exists in createBinaryOperation + assert(result != spv::NoResult); + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + + return false; + } + + // + // Create the list of operands. + // + glslang::TIntermSequence& glslangOperands = node->getSequence(); + std::vector operands; + std::vector memoryAccessOperands; + for (int arg = 0; arg < (int)glslangOperands.size(); ++arg) { + // special case l-value operands; there are just a few + bool lvalue = false; + switch (node->getOp()) { + case glslang::EOpModf: + if (arg == 1) + lvalue = true; + break; + + case glslang::EOpRayQueryInitialize: + case glslang::EOpRayQueryTerminate: + case glslang::EOpRayQueryConfirmIntersection: + case glslang::EOpRayQueryProceed: + case glslang::EOpRayQueryGenerateIntersection: + case glslang::EOpRayQueryGetIntersectionType: + case glslang::EOpRayQueryGetIntersectionT: + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + case glslang::EOpRayQueryGetIntersectionInstanceId: + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + case glslang::EOpRayQueryGetIntersectionBarycentrics: + case glslang::EOpRayQueryGetIntersectionFrontFace: + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + case glslang::EOpRayQueryGetIntersectionWorldToObject: + if (arg == 0) + lvalue = true; + break; + + case glslang::EOpAtomicAdd: + case glslang::EOpAtomicMin: + case glslang::EOpAtomicMax: + case glslang::EOpAtomicAnd: + case glslang::EOpAtomicOr: + case glslang::EOpAtomicXor: + case glslang::EOpAtomicExchange: + case glslang::EOpAtomicCompSwap: + if (arg == 0) + lvalue = true; + break; + +#ifndef GLSLANG_WEB + case glslang::EOpFrexp: + if (arg == 1) + lvalue = true; + break; + case glslang::EOpInterpolateAtSample: + case glslang::EOpInterpolateAtOffset: + case glslang::EOpInterpolateAtVertex: + if (arg == 0) { + lvalue = true; + + // Does it need a swizzle inversion? If so, evaluation is inverted; + // operate first on the swizzle base, then apply the swizzle. + // That is, we transform + // + // interpolate(v.zy) -> interpolate(v).zy + // + if (glslangOperands[0]->getAsOperator() && + glslangOperands[0]->getAsOperator()->getOp() == glslang::EOpVectorSwizzle) + invertedType = convertGlslangToSpvType( + glslangOperands[0]->getAsBinaryNode()->getLeft()->getType()); + } + break; + case glslang::EOpAtomicLoad: + case glslang::EOpAtomicStore: + case glslang::EOpAtomicCounterAdd: + case glslang::EOpAtomicCounterSubtract: + case glslang::EOpAtomicCounterMin: + case glslang::EOpAtomicCounterMax: + case glslang::EOpAtomicCounterAnd: + case glslang::EOpAtomicCounterOr: + case glslang::EOpAtomicCounterXor: + case glslang::EOpAtomicCounterExchange: + case glslang::EOpAtomicCounterCompSwap: + if (arg == 0) + lvalue = true; + break; + case glslang::EOpAddCarry: + case glslang::EOpSubBorrow: + if (arg == 2) + lvalue = true; + break; + case glslang::EOpUMulExtended: + case glslang::EOpIMulExtended: + if (arg >= 2) + lvalue = true; + break; + case glslang::EOpCooperativeMatrixLoad: + if (arg == 0 || arg == 1) + lvalue = true; + break; + case glslang::EOpCooperativeMatrixStore: + if (arg == 1) + lvalue = true; + break; +#endif + default: + break; + } + builder.clearAccessChain(); + if (invertedType != spv::NoType && arg == 0) + glslangOperands[0]->getAsBinaryNode()->getLeft()->traverse(this); + else + glslangOperands[arg]->traverse(this); + +#ifndef GLSLANG_WEB + if (node->getOp() == glslang::EOpCooperativeMatrixLoad || + node->getOp() == glslang::EOpCooperativeMatrixStore) { + + if (arg == 1) { + // fold "element" parameter into the access chain + spv::Builder::AccessChain save = builder.getAccessChain(); + builder.clearAccessChain(); + glslangOperands[2]->traverse(this); + + spv::Id elementId = accessChainLoad(glslangOperands[2]->getAsTyped()->getType()); + + builder.setAccessChain(save); + + // Point to the first element of the array. + builder.accessChainPush(elementId, + TranslateCoherent(glslangOperands[arg]->getAsTyped()->getType()), + glslangOperands[arg]->getAsTyped()->getType().getBufferReferenceAlignment()); + + spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags; + unsigned int alignment = builder.getAccessChain().alignment; + + int memoryAccess = TranslateMemoryAccess(coherentFlags); + if (node->getOp() == glslang::EOpCooperativeMatrixLoad) + memoryAccess &= ~spv::MemoryAccessMakePointerAvailableKHRMask; + if (node->getOp() == glslang::EOpCooperativeMatrixStore) + memoryAccess &= ~spv::MemoryAccessMakePointerVisibleKHRMask; + if (builder.getStorageClass(builder.getAccessChain().base) == + spv::StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + memoryAccessOperands.push_back(spv::IdImmediate(false, memoryAccess)); + + if (memoryAccess & spv::MemoryAccessAlignedMask) { + memoryAccessOperands.push_back(spv::IdImmediate(false, alignment)); + } + + if (memoryAccess & + (spv::MemoryAccessMakePointerAvailableKHRMask | spv::MemoryAccessMakePointerVisibleKHRMask)) { + memoryAccessOperands.push_back(spv::IdImmediate(true, + builder.makeUintConstant(TranslateMemoryScope(coherentFlags)))); + } + } else if (arg == 2) { + continue; + } + } +#endif + + // for l-values, pass the address, for r-values, pass the value + if (lvalue) { + if (invertedType == spv::NoType && !builder.isSpvLvalue()) { + // SPIR-V cannot represent an l-value containing a swizzle that doesn't + // reduce to a simple access chain. So, we need a temporary vector to + // receive the result, and must later swizzle that into the original + // l-value. + complexLvalues.push_back(builder.getAccessChain()); + complexLValueQualifiers.push_back(glslangOperands[arg]->getAsTyped()->getType().getQualifier()); + temporaryLvalues.push_back(builder.createVariable( + spv::NoPrecision, spv::StorageClassFunction, + builder.accessChainGetInferredType(), "swizzleTemp")); + operands.push_back(temporaryLvalues.back()); + } else { + operands.push_back(builder.accessChainGetLValue()); + } + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + lvalueCoherentFlags |= TranslateCoherent(glslangOperands[arg]->getAsTyped()->getType()); + } else { + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + glslang::TOperator glslangOp = node->getOp(); + if (arg == 1 && + (glslangOp == glslang::EOpRayQueryGetIntersectionType || + glslangOp == glslang::EOpRayQueryGetIntersectionT || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceCustomIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceId || + glslangOp == glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset || + glslangOp == glslang::EOpRayQueryGetIntersectionGeometryIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionPrimitiveIndex || + glslangOp == glslang::EOpRayQueryGetIntersectionBarycentrics || + glslangOp == glslang::EOpRayQueryGetIntersectionFrontFace || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectRayDirection || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectRayOrigin || + glslangOp == glslang::EOpRayQueryGetIntersectionObjectToWorld || + glslangOp == glslang::EOpRayQueryGetIntersectionWorldToObject + )) { + bool cond = glslangOperands[arg]->getAsConstantUnion()->getConstArray()[0].getBConst(); + operands.push_back(builder.makeIntConstant(cond ? 1 : 0)); + } + else { + operands.push_back(accessChainLoad(glslangOperands[arg]->getAsTyped()->getType())); + } + + } + } + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); +#ifndef GLSLANG_WEB + if (node->getOp() == glslang::EOpCooperativeMatrixLoad) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // colMajor + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); + // get the pointee type + spv::Id typeId = builder.getContainedTypeId(builder.getTypeId(operands[0])); + assert(builder.isCooperativeMatrixType(typeId)); + // do the op + spv::Id result = builder.createOp(spv::OpCooperativeMatrixLoadNV, typeId, idImmOps); + // store the result to the pointer (out param 'm') + builder.createStore(result, operands[0]); + result = 0; + } else if (node->getOp() == glslang::EOpCooperativeMatrixStore) { + std::vector idImmOps; + + idImmOps.push_back(spv::IdImmediate(true, operands[1])); // buf + idImmOps.push_back(spv::IdImmediate(true, operands[0])); // object + idImmOps.push_back(spv::IdImmediate(true, operands[2])); // stride + idImmOps.push_back(spv::IdImmediate(true, operands[3])); // colMajor + idImmOps.insert(idImmOps.end(), memoryAccessOperands.begin(), memoryAccessOperands.end()); + + builder.createNoResultOp(spv::OpCooperativeMatrixStoreNV, idImmOps); + result = 0; + } else +#endif + if (atomic) { + // Handle all atomics + result = createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType(), + lvalueCoherentFlags); + } else if (node->getOp() == glslang::EOpDebugPrintf) { + if (!nonSemanticDebugPrintf) { + nonSemanticDebugPrintf = builder.import("NonSemantic.DebugPrintf"); + } + result = builder.createBuiltinCall(builder.makeVoidType(), nonSemanticDebugPrintf, spv::NonSemanticDebugPrintfDebugPrintf, operands); + builder.addExtension(spv::E_SPV_KHR_non_semantic_info); + } else { + // Pass through to generic operations. + switch (glslangOperands.size()) { + case 0: + result = createNoArgOperation(node->getOp(), precision, resultType()); + break; + case 1: + { + OpDecorations decorations = { precision, + TranslateNoContractionDecoration(node->getType().getQualifier()), + TranslateNonUniformDecoration(node->getType().getQualifier()) }; + result = createUnaryOperation( + node->getOp(), decorations, + resultType(), operands.front(), + glslangOperands[0]->getAsTyped()->getBasicType(), lvalueCoherentFlags); + } + break; + default: + result = createMiscOperation(node->getOp(), precision, resultType(), operands, node->getBasicType()); + break; + } + + if (invertedType != spv::NoResult) + result = createInvertedSwizzle(precision, *glslangOperands[0]->getAsBinaryNode(), result); + + for (unsigned int i = 0; i < temporaryLvalues.size(); ++i) { + builder.setAccessChain(complexLvalues[i]); + builder.accessChainStore(builder.createLoad(temporaryLvalues[i], spv::NoPrecision), TranslateNonUniformDecoration(complexLValueQualifiers[i])); + } + } + + if (noReturnValue) + return false; + + if (! result) { + logger->missingFunctionality("unknown glslang aggregate"); + return true; // pick up a child as a placeholder operand + } else { + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + return false; + } +} + +// This path handles both if-then-else and ?: +// The if-then-else has a node type of void, while +// ?: has either a void or a non-void node type +// +// Leaving the result, when not void: +// GLSL only has r-values as the result of a :?, but +// if we have an l-value, that can be more efficient if it will +// become the base of a complex r-value expression, because the +// next layer copies r-values into memory to use the access-chain mechanism +bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node) +{ + // see if OpSelect can handle it + const auto isOpSelectable = [&]() { + if (node->getBasicType() == glslang::EbtVoid) + return false; + // OpSelect can do all other types starting with SPV 1.4 + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_4) { + // pre-1.4, only scalars and vectors can be handled + if ((!node->getType().isScalar() && !node->getType().isVector())) + return false; + } + return true; + }; + + // See if it simple and safe, or required, to execute both sides. + // Crucially, side effects must be either semantically required or avoided, + // and there are performance trade-offs. + // Return true if required or a good idea (and safe) to execute both sides, + // false otherwise. + const auto bothSidesPolicy = [&]() -> bool { + // do we have both sides? + if (node->getTrueBlock() == nullptr || + node->getFalseBlock() == nullptr) + return false; + + // required? (unless we write additional code to look for side effects + // and make performance trade-offs if none are present) + if (!node->getShortCircuit()) + return true; + + // if not required to execute both, decide based on performance/practicality... + + if (!isOpSelectable()) + return false; + + assert(node->getType() == node->getTrueBlock() ->getAsTyped()->getType() && + node->getType() == node->getFalseBlock()->getAsTyped()->getType()); + + // return true if a single operand to ? : is okay for OpSelect + const auto operandOkay = [](glslang::TIntermTyped* node) { + return node->getAsSymbolNode() || node->getType().getQualifier().isConstant(); + }; + + return operandOkay(node->getTrueBlock() ->getAsTyped()) && + operandOkay(node->getFalseBlock()->getAsTyped()); + }; + + spv::Id result = spv::NoResult; // upcoming result selecting between trueValue and falseValue + // emit the condition before doing anything with selection + node->getCondition()->traverse(this); + spv::Id condition = accessChainLoad(node->getCondition()->getType()); + + // Find a way of executing both sides and selecting the right result. + const auto executeBothSides = [&]() -> void { + // execute both sides + node->getTrueBlock()->traverse(this); + spv::Id trueValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + node->getFalseBlock()->traverse(this); + spv::Id falseValue = accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()); + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + + // done if void + if (node->getBasicType() == glslang::EbtVoid) + return; + + // emit code to select between trueValue and falseValue + + // see if OpSelect can handle it + if (isOpSelectable()) { + // Emit OpSelect for this selection. + + // smear condition to vector, if necessary (AST is always scalar) + // Before 1.4, smear like for mix(), starting with 1.4, keep it scalar + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_4 && builder.isVector(trueValue)) { + condition = builder.smearScalar(spv::NoPrecision, condition, + builder.makeVectorType(builder.makeBoolType(), + builder.getNumComponents(trueValue))); + } + + // OpSelect + result = builder.createTriOp(spv::OpSelect, + convertGlslangToSpvType(node->getType()), condition, + trueValue, falseValue); + + builder.clearAccessChain(); + builder.setAccessChainRValue(result); + } else { + // We need control flow to select the result. + // TODO: Once SPIR-V OpSelect allows arbitrary types, eliminate this path. + result = builder.createVariable(TranslatePrecisionDecoration(node->getType()), + spv::StorageClassFunction, convertGlslangToSpvType(node->getType())); + + // Selection control: + const spv::SelectionControlMask control = TranslateSelectionControl(*node); + + // make an "if" based on the value created by the condition + spv::Builder::If ifBuilder(condition, control, builder); + + // emit the "then" statement + builder.createStore(trueValue, result); + ifBuilder.makeBeginElse(); + // emit the "else" statement + builder.createStore(falseValue, result); + + // finish off the control flow + ifBuilder.makeEndIf(); + + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + } + }; + + // Execute the one side needed, as per the condition + const auto executeOneSide = [&]() { + // Always emit control flow. + if (node->getBasicType() != glslang::EbtVoid) { + result = builder.createVariable(TranslatePrecisionDecoration(node->getType()), spv::StorageClassFunction, + convertGlslangToSpvType(node->getType())); + } + + // Selection control: + const spv::SelectionControlMask control = TranslateSelectionControl(*node); + + // make an "if" based on the value created by the condition + spv::Builder::If ifBuilder(condition, control, builder); + + // emit the "then" statement + if (node->getTrueBlock() != nullptr) { + node->getTrueBlock()->traverse(this); + if (result != spv::NoResult) + builder.createStore(accessChainLoad(node->getTrueBlock()->getAsTyped()->getType()), result); + } + + if (node->getFalseBlock() != nullptr) { + ifBuilder.makeBeginElse(); + // emit the "else" statement + node->getFalseBlock()->traverse(this); + if (result != spv::NoResult) + builder.createStore(accessChainLoad(node->getFalseBlock()->getAsTyped()->getType()), result); + } + + // finish off the control flow + ifBuilder.makeEndIf(); + + if (result != spv::NoResult) { + builder.clearAccessChain(); + builder.setAccessChainLValue(result); + } + }; + + // Try for OpSelect (or a requirement to execute both sides) + if (bothSidesPolicy()) { + SpecConstantOpModeGuard spec_constant_op_mode_setter(&builder); + if (node->getType().getQualifier().isSpecConstant()) + spec_constant_op_mode_setter.turnOnSpecConstantOpMode(); + executeBothSides(); + } else + executeOneSide(); + + return false; +} + +bool TGlslangToSpvTraverser::visitSwitch(glslang::TVisit /* visit */, glslang::TIntermSwitch* node) +{ + // emit and get the condition before doing anything with switch + node->getCondition()->traverse(this); + spv::Id selector = accessChainLoad(node->getCondition()->getAsTyped()->getType()); + + // Selection control: + const spv::SelectionControlMask control = TranslateSwitchControl(*node); + + // browse the children to sort out code segments + int defaultSegment = -1; + std::vector codeSegments; + glslang::TIntermSequence& sequence = node->getBody()->getSequence(); + std::vector caseValues; + std::vector valueIndexToSegment(sequence.size()); // note: probably not all are used, it is an overestimate + for (glslang::TIntermSequence::iterator c = sequence.begin(); c != sequence.end(); ++c) { + TIntermNode* child = *c; + if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpDefault) + defaultSegment = (int)codeSegments.size(); + else if (child->getAsBranchNode() && child->getAsBranchNode()->getFlowOp() == glslang::EOpCase) { + valueIndexToSegment[caseValues.size()] = (int)codeSegments.size(); + caseValues.push_back(child->getAsBranchNode()->getExpression()->getAsConstantUnion() + ->getConstArray()[0].getIConst()); + } else + codeSegments.push_back(child); + } + + // handle the case where the last code segment is missing, due to no code + // statements between the last case and the end of the switch statement + if ((caseValues.size() && (int)codeSegments.size() == valueIndexToSegment[caseValues.size() - 1]) || + (int)codeSegments.size() == defaultSegment) + codeSegments.push_back(nullptr); + + // make the switch statement + std::vector segmentBlocks; // returned, as the blocks allocated in the call + builder.makeSwitch(selector, control, (int)codeSegments.size(), caseValues, valueIndexToSegment, defaultSegment, + segmentBlocks); + + // emit all the code in the segments + breakForLoop.push(false); + for (unsigned int s = 0; s < codeSegments.size(); ++s) { + builder.nextSwitchSegment(segmentBlocks, s); + if (codeSegments[s]) + codeSegments[s]->traverse(this); + else + builder.addSwitchBreak(); + } + breakForLoop.pop(); + + builder.endSwitch(segmentBlocks); + + return false; +} + +void TGlslangToSpvTraverser::visitConstantUnion(glslang::TIntermConstantUnion* node) +{ + int nextConst = 0; + spv::Id constant = createSpvConstantFromConstUnionArray(node->getType(), node->getConstArray(), nextConst, false); + + builder.clearAccessChain(); + builder.setAccessChainRValue(constant); +} + +bool TGlslangToSpvTraverser::visitLoop(glslang::TVisit /* visit */, glslang::TIntermLoop* node) +{ + auto blocks = builder.makeNewLoop(); + builder.createBranch(&blocks.head); + + // Loop control: + std::vector operands; + const spv::LoopControlMask control = TranslateLoopControl(*node, operands); + + // Spec requires back edges to target header blocks, and every header block + // must dominate its merge block. Make a header block first to ensure these + // conditions are met. By definition, it will contain OpLoopMerge, followed + // by a block-ending branch. But we don't want to put any other body/test + // instructions in it, since the body/test may have arbitrary instructions, + // including merges of its own. + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + builder.setBuildPoint(&blocks.head); + builder.createLoopMerge(&blocks.merge, &blocks.continue_target, control, operands); + if (node->testFirst() && node->getTest()) { + spv::Block& test = builder.makeNewBlock(); + builder.createBranch(&test); + + builder.setBuildPoint(&test); + node->getTest()->traverse(this); + spv::Id condition = accessChainLoad(node->getTest()->getType()); + builder.createConditionalBranch(condition, &blocks.body, &blocks.merge); + + builder.setBuildPoint(&blocks.body); + breakForLoop.push(true); + if (node->getBody()) + node->getBody()->traverse(this); + builder.createBranch(&blocks.continue_target); + breakForLoop.pop(); + + builder.setBuildPoint(&blocks.continue_target); + if (node->getTerminal()) + node->getTerminal()->traverse(this); + builder.createBranch(&blocks.head); + } else { + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + builder.createBranch(&blocks.body); + + breakForLoop.push(true); + builder.setBuildPoint(&blocks.body); + if (node->getBody()) + node->getBody()->traverse(this); + builder.createBranch(&blocks.continue_target); + breakForLoop.pop(); + + builder.setBuildPoint(&blocks.continue_target); + if (node->getTerminal()) + node->getTerminal()->traverse(this); + if (node->getTest()) { + node->getTest()->traverse(this); + spv::Id condition = + accessChainLoad(node->getTest()->getType()); + builder.createConditionalBranch(condition, &blocks.head, &blocks.merge); + } else { + // TODO: unless there was a break/return/discard instruction + // somewhere in the body, this is an infinite loop, so we should + // issue a warning. + builder.createBranch(&blocks.head); + } + } + builder.setBuildPoint(&blocks.merge); + builder.closeLoop(); + return false; +} + +bool TGlslangToSpvTraverser::visitBranch(glslang::TVisit /* visit */, glslang::TIntermBranch* node) +{ + if (node->getExpression()) + node->getExpression()->traverse(this); + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + + switch (node->getFlowOp()) { + case glslang::EOpKill: + builder.makeDiscard(); + break; + case glslang::EOpTerminateInvocation: + builder.addExtension(spv::E_SPV_KHR_terminate_invocation); + builder.makeTerminateInvocation(); + break; + case glslang::EOpBreak: + if (breakForLoop.top()) + builder.createLoopExit(); + else + builder.addSwitchBreak(); + break; + case glslang::EOpContinue: + builder.createLoopContinue(); + break; + case glslang::EOpReturn: + if (node->getExpression() != nullptr) { + const glslang::TType& glslangReturnType = node->getExpression()->getType(); + spv::Id returnId = accessChainLoad(glslangReturnType); + if (builder.getTypeId(returnId) != currentFunction->getReturnType() || + TranslatePrecisionDecoration(glslangReturnType) != currentFunction->getReturnPrecision()) { + builder.clearAccessChain(); + spv::Id copyId = builder.createVariable(currentFunction->getReturnPrecision(), + spv::StorageClassFunction, currentFunction->getReturnType()); + builder.setAccessChainLValue(copyId); + multiTypeStore(glslangReturnType, returnId); + returnId = builder.createLoad(copyId, currentFunction->getReturnPrecision()); + } + builder.makeReturn(false, returnId); + } else + builder.makeReturn(false); + + builder.clearAccessChain(); + break; + +#ifndef GLSLANG_WEB + case glslang::EOpDemote: + builder.createNoResultOp(spv::OpDemoteToHelperInvocationEXT); + builder.addExtension(spv::E_SPV_EXT_demote_to_helper_invocation); + builder.addCapability(spv::CapabilityDemoteToHelperInvocationEXT); + break; +#endif + + default: + assert(0); + break; + } + + return false; +} + +spv::Id TGlslangToSpvTraverser::createSpvVariable(const glslang::TIntermSymbol* node, spv::Id forcedType) +{ + // First, steer off constants, which are not SPIR-V variables, but + // can still have a mapping to a SPIR-V Id. + // This includes specialization constants. + if (node->getQualifier().isConstant()) { + spv::Id result = createSpvConstant(*node); + if (result != spv::NoResult) + return result; + } + + // Now, handle actual variables + spv::StorageClass storageClass = TranslateStorageClass(node->getType()); + spv::Id spvType = forcedType == spv::NoType ? convertGlslangToSpvType(node->getType()) + : forcedType; + + const bool contains16BitType = node->getType().contains16BitFloat() || + node->getType().contains16BitInt(); + if (contains16BitType) { + switch (storageClass) { + case spv::StorageClassInput: + case spv::StorageClassOutput: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStorageInputOutput16); + break; + case spv::StorageClassUniform: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + if (node->getType().getQualifier().storage == glslang::EvqBuffer) + builder.addCapability(spv::CapabilityStorageUniformBufferBlock16); + else + builder.addCapability(spv::CapabilityStorageUniform16); + break; +#ifndef GLSLANG_WEB + case spv::StorageClassPushConstant: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStoragePushConstant16); + break; + case spv::StorageClassStorageBuffer: + case spv::StorageClassPhysicalStorageBufferEXT: + builder.addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + builder.addCapability(spv::CapabilityStorageUniformBufferBlock16); + break; +#endif + default: + if (node->getType().contains16BitFloat()) + builder.addCapability(spv::CapabilityFloat16); + if (node->getType().contains16BitInt()) + builder.addCapability(spv::CapabilityInt16); + break; + } + } + + if (node->getType().contains8BitInt()) { + if (storageClass == spv::StorageClassPushConstant) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityStoragePushConstant8); + } else if (storageClass == spv::StorageClassUniform) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityUniformAndStorageBuffer8BitAccess); + } else if (storageClass == spv::StorageClassStorageBuffer) { + builder.addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + builder.addCapability(spv::CapabilityStorageBuffer8BitAccess); + } else { + builder.addCapability(spv::CapabilityInt8); + } + } + + const char* name = node->getName().c_str(); + if (glslang::IsAnonymous(name)) + name = ""; + + spv::Id initializer = spv::NoResult; + + if (node->getType().getQualifier().storage == glslang::EvqUniform && + !node->getConstArray().empty()) { + int nextConst = 0; + initializer = createSpvConstantFromConstUnionArray(node->getType(), + node->getConstArray(), + nextConst, + false /* specConst */); + } + + return builder.createVariable(spv::NoPrecision, storageClass, spvType, name, initializer); +} + +// Return type Id of the sampled type. +spv::Id TGlslangToSpvTraverser::getSampledType(const glslang::TSampler& sampler) +{ + switch (sampler.type) { + case glslang::EbtInt: return builder.makeIntType(32); + case glslang::EbtUint: return builder.makeUintType(32); + case glslang::EbtFloat: return builder.makeFloatType(32); +#ifndef GLSLANG_WEB + case glslang::EbtFloat16: + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float_fetch); + builder.addCapability(spv::CapabilityFloat16ImageAMD); + return builder.makeFloatType(16); + case glslang::EbtInt64: return builder.makeIntType(64); + builder.addExtension(spv::E_SPV_EXT_shader_image_int64); + builder.addCapability(spv::CapabilityFloat16ImageAMD); + case glslang::EbtUint64: return builder.makeUintType(64); + builder.addExtension(spv::E_SPV_EXT_shader_image_int64); + builder.addCapability(spv::CapabilityFloat16ImageAMD); +#endif + default: + assert(0); + return builder.makeFloatType(32); + } +} + +// If node is a swizzle operation, return the type that should be used if +// the swizzle base is first consumed by another operation, before the swizzle +// is applied. +spv::Id TGlslangToSpvTraverser::getInvertedSwizzleType(const glslang::TIntermTyped& node) +{ + if (node.getAsOperator() && + node.getAsOperator()->getOp() == glslang::EOpVectorSwizzle) + return convertGlslangToSpvType(node.getAsBinaryNode()->getLeft()->getType()); + else + return spv::NoType; +} + +// When inverting a swizzle with a parent op, this function +// will apply the swizzle operation to a completed parent operation. +spv::Id TGlslangToSpvTraverser::createInvertedSwizzle(spv::Decoration precision, const glslang::TIntermTyped& node, + spv::Id parentResult) +{ + std::vector swizzle; + convertSwizzle(*node.getAsBinaryNode()->getRight()->getAsAggregate(), swizzle); + return builder.createRvalueSwizzle(precision, convertGlslangToSpvType(node.getType()), parentResult, swizzle); +} + +// Convert a glslang AST swizzle node to a swizzle vector for building SPIR-V. +void TGlslangToSpvTraverser::convertSwizzle(const glslang::TIntermAggregate& node, std::vector& swizzle) +{ + const glslang::TIntermSequence& swizzleSequence = node.getSequence(); + for (int i = 0; i < (int)swizzleSequence.size(); ++i) + swizzle.push_back(swizzleSequence[i]->getAsConstantUnion()->getConstArray()[0].getIConst()); +} + +// Convert from a glslang type to an SPV type, by calling into a +// recursive version of this function. This establishes the inherited +// layout state rooted from the top-level type. +spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly) +{ + return convertGlslangToSpvType(type, getExplicitLayout(type), type.getQualifier(), false, forwardReferenceOnly); +} + +// Do full recursive conversion of an arbitrary glslang type to a SPIR-V Id. +// explicitLayout can be kept the same throughout the hierarchical recursive walk. +// Mutually recursive with convertGlslangStructToSpvType(). +spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& type, + glslang::TLayoutPacking explicitLayout, const glslang::TQualifier& qualifier, + bool lastBufferBlockMember, bool forwardReferenceOnly) +{ + spv::Id spvType = spv::NoResult; + + switch (type.getBasicType()) { + case glslang::EbtVoid: + spvType = builder.makeVoidType(); + assert (! type.isArray()); + break; + case glslang::EbtBool: + // "transparent" bool doesn't exist in SPIR-V. The GLSL convention is + // a 32-bit int where non-0 means true. + if (explicitLayout != glslang::ElpNone) + spvType = builder.makeUintType(32); + else + spvType = builder.makeBoolType(); + break; + case glslang::EbtInt: + spvType = builder.makeIntType(32); + break; + case glslang::EbtUint: + spvType = builder.makeUintType(32); + break; + case glslang::EbtFloat: + spvType = builder.makeFloatType(32); + break; +#ifndef GLSLANG_WEB + case glslang::EbtDouble: + spvType = builder.makeFloatType(64); + break; + case glslang::EbtFloat16: + spvType = builder.makeFloatType(16); + break; + case glslang::EbtInt8: + spvType = builder.makeIntType(8); + break; + case glslang::EbtUint8: + spvType = builder.makeUintType(8); + break; + case glslang::EbtInt16: + spvType = builder.makeIntType(16); + break; + case glslang::EbtUint16: + spvType = builder.makeUintType(16); + break; + case glslang::EbtInt64: + spvType = builder.makeIntType(64); + break; + case glslang::EbtUint64: + spvType = builder.makeUintType(64); + break; + case glslang::EbtAtomicUint: + builder.addCapability(spv::CapabilityAtomicStorage); + spvType = builder.makeUintType(32); + break; + case glslang::EbtAccStruct: + spvType = builder.makeAccelerationStructureType(); + break; + case glslang::EbtRayQuery: + spvType = builder.makeRayQueryType(); + break; + case glslang::EbtReference: + { + // Make the forward pointer, then recurse to convert the structure type, then + // patch up the forward pointer with a real pointer type. + if (forwardPointers.find(type.getReferentType()) == forwardPointers.end()) { + spv::Id forwardId = builder.makeForwardPointer(spv::StorageClassPhysicalStorageBufferEXT); + forwardPointers[type.getReferentType()] = forwardId; + } + spvType = forwardPointers[type.getReferentType()]; + if (!forwardReferenceOnly) { + spv::Id referentType = convertGlslangToSpvType(*type.getReferentType()); + builder.makePointerFromForwardPointer(spv::StorageClassPhysicalStorageBufferEXT, + forwardPointers[type.getReferentType()], + referentType); + } + } + break; +#endif + case glslang::EbtSampler: + { + const glslang::TSampler& sampler = type.getSampler(); + if (sampler.isPureSampler()) { + spvType = builder.makeSamplerType(); + } else { + // an image is present, make its type + spvType = builder.makeImageType(getSampledType(sampler), TranslateDimensionality(sampler), + sampler.isShadow(), sampler.isArrayed(), sampler.isMultiSample(), + sampler.isImageClass() ? 2 : 1, TranslateImageFormat(type)); + if (sampler.isCombined()) { + // already has both image and sampler, make the combined type + spvType = builder.makeSampledImageType(spvType); + } + } + } + break; + case glslang::EbtStruct: + case glslang::EbtBlock: + { + // If we've seen this struct type, return it + const glslang::TTypeList* glslangMembers = type.getStruct(); + + // Try to share structs for different layouts, but not yet for other + // kinds of qualification (primarily not yet including interpolant qualification). + if (! HasNonLayoutQualifiers(type, qualifier)) + spvType = structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers]; + if (spvType != spv::NoResult) + break; + + // else, we haven't seen it... + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangTypeToIdMap[glslangMembers]].resize(glslangMembers->size()); + spvType = convertGlslangStructToSpvType(type, glslangMembers, explicitLayout, qualifier); + } + break; + case glslang::EbtString: + // no type used for OpString + return 0; + default: + assert(0); + break; + } + + if (type.isMatrix()) + spvType = builder.makeMatrixType(spvType, type.getMatrixCols(), type.getMatrixRows()); + else { + // If this variable has a vector element count greater than 1, create a SPIR-V vector + if (type.getVectorSize() > 1) + spvType = builder.makeVectorType(spvType, type.getVectorSize()); + } + + if (type.isCoopMat()) { + builder.addCapability(spv::CapabilityCooperativeMatrixNV); + builder.addExtension(spv::E_SPV_NV_cooperative_matrix); + if (type.getBasicType() == glslang::EbtFloat16) + builder.addCapability(spv::CapabilityFloat16); + if (type.getBasicType() == glslang::EbtUint8 || + type.getBasicType() == glslang::EbtInt8) { + builder.addCapability(spv::CapabilityInt8); + } + + spv::Id scope = makeArraySizeId(*type.getTypeParameters(), 1); + spv::Id rows = makeArraySizeId(*type.getTypeParameters(), 2); + spv::Id cols = makeArraySizeId(*type.getTypeParameters(), 3); + + spvType = builder.makeCooperativeMatrixType(spvType, scope, rows, cols); + } + + if (type.isArray()) { + int stride = 0; // keep this 0 unless doing an explicit layout; 0 will mean no decoration, no stride + + // Do all but the outer dimension + if (type.getArraySizes()->getNumDims() > 1) { + // We need to decorate array strides for types needing explicit layout, except blocks. + if (explicitLayout != glslang::ElpNone && type.getBasicType() != glslang::EbtBlock) { + // Use a dummy glslang type for querying internal strides of + // arrays of arrays, but using just a one-dimensional array. + glslang::TType simpleArrayType(type, 0); // deference type of the array + while (simpleArrayType.getArraySizes()->getNumDims() > 1) + simpleArrayType.getArraySizes()->dereference(); + + // Will compute the higher-order strides here, rather than making a whole + // pile of types and doing repetitive recursion on their contents. + stride = getArrayStride(simpleArrayType, explicitLayout, qualifier.layoutMatrix); + } + + // make the arrays + for (int dim = type.getArraySizes()->getNumDims() - 1; dim > 0; --dim) { + spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), dim), stride); + if (stride > 0) + builder.addDecoration(spvType, spv::DecorationArrayStride, stride); + stride *= type.getArraySizes()->getDimSize(dim); + } + } else { + // single-dimensional array, and don't yet have stride + + // We need to decorate array strides for types needing explicit layout, except blocks. + if (explicitLayout != glslang::ElpNone && type.getBasicType() != glslang::EbtBlock) + stride = getArrayStride(type, explicitLayout, qualifier.layoutMatrix); + } + + // Do the outer dimension, which might not be known for a runtime-sized array. + // (Unsized arrays that survive through linking will be runtime-sized arrays) + if (type.isSizedArray()) + spvType = builder.makeArrayType(spvType, makeArraySizeId(*type.getArraySizes(), 0), stride); + else { +#ifndef GLSLANG_WEB + if (!lastBufferBlockMember) { + builder.addIncorporatedExtension("SPV_EXT_descriptor_indexing", spv::Spv_1_5); + builder.addCapability(spv::CapabilityRuntimeDescriptorArrayEXT); + } +#endif + spvType = builder.makeRuntimeArray(spvType); + } + if (stride > 0) + builder.addDecoration(spvType, spv::DecorationArrayStride, stride); + } + + return spvType; +} + +// TODO: this functionality should exist at a higher level, in creating the AST +// +// Identify interface members that don't have their required extension turned on. +// +bool TGlslangToSpvTraverser::filterMember(const glslang::TType& member) +{ +#ifndef GLSLANG_WEB + auto& extensions = glslangIntermediate->getRequestedExtensions(); + + if (member.getFieldName() == "gl_SecondaryViewportMaskNV" && + extensions.find("GL_NV_stereo_view_rendering") == extensions.end()) + return true; + if (member.getFieldName() == "gl_SecondaryPositionNV" && + extensions.find("GL_NV_stereo_view_rendering") == extensions.end()) + return true; + + if (glslangIntermediate->getStage() != EShLangMeshNV) { + if (member.getFieldName() == "gl_ViewportMask" && + extensions.find("GL_NV_viewport_array2") == extensions.end()) + return true; + if (member.getFieldName() == "gl_PositionPerViewNV" && + extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end()) + return true; + if (member.getFieldName() == "gl_ViewportMaskPerViewNV" && + extensions.find("GL_NVX_multiview_per_view_attributes") == extensions.end()) + return true; + } +#endif + + return false; +}; + +// Do full recursive conversion of a glslang structure (or block) type to a SPIR-V Id. +// explicitLayout can be kept the same throughout the hierarchical recursive walk. +// Mutually recursive with convertGlslangToSpvType(). +spv::Id TGlslangToSpvTraverser::convertGlslangStructToSpvType(const glslang::TType& type, + const glslang::TTypeList* glslangMembers, + glslang::TLayoutPacking explicitLayout, + const glslang::TQualifier& qualifier) +{ + // Create a vector of struct types for SPIR-V to consume + std::vector spvMembers; + int memberDelta = 0; // how much the member's index changes from glslang to SPIR-V, normally 0, + // except sometimes for blocks + std::vector > deferredForwardPointers; + for (int i = 0; i < (int)glslangMembers->size(); i++) { + glslang::TType& glslangMember = *(*glslangMembers)[i].type; + if (glslangMember.hiddenMember()) { + ++memberDelta; + if (type.getBasicType() == glslang::EbtBlock) + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = -1; + } else { + if (type.getBasicType() == glslang::EbtBlock) { + if (filterMember(glslangMember)) { + memberDelta++; + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = -1; + continue; + } + memberRemapper[glslangTypeToIdMap[glslangMembers]][i] = i - memberDelta; + } + // modify just this child's view of the qualifier + glslang::TQualifier memberQualifier = glslangMember.getQualifier(); + InheritQualifiers(memberQualifier, qualifier); + + // manually inherit location + if (! memberQualifier.hasLocation() && qualifier.hasLocation()) + memberQualifier.layoutLocation = qualifier.layoutLocation; + + // recurse + bool lastBufferBlockMember = qualifier.storage == glslang::EvqBuffer && + i == (int)glslangMembers->size() - 1; + + // Make forward pointers for any pointer members, and create a list of members to + // convert to spirv types after creating the struct. + if (glslangMember.isReference()) { + if (forwardPointers.find(glslangMember.getReferentType()) == forwardPointers.end()) { + deferredForwardPointers.push_back(std::make_pair(&glslangMember, memberQualifier)); + } + spvMembers.push_back( + convertGlslangToSpvType(glslangMember, explicitLayout, memberQualifier, lastBufferBlockMember, + true)); + } else { + spvMembers.push_back( + convertGlslangToSpvType(glslangMember, explicitLayout, memberQualifier, lastBufferBlockMember, + false)); + } + } + } + + // Make the SPIR-V type + spv::Id spvType = builder.makeStructType(spvMembers, type.getTypeName().c_str()); + if (! HasNonLayoutQualifiers(type, qualifier)) + structMap[explicitLayout][qualifier.layoutMatrix][glslangMembers] = spvType; + + // Decorate it + decorateStructType(type, glslangMembers, explicitLayout, qualifier, spvType); + + for (int i = 0; i < (int)deferredForwardPointers.size(); ++i) { + auto it = deferredForwardPointers[i]; + convertGlslangToSpvType(*it.first, explicitLayout, it.second, false); + } + + return spvType; +} + +void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type, + const glslang::TTypeList* glslangMembers, + glslang::TLayoutPacking explicitLayout, + const glslang::TQualifier& qualifier, + spv::Id spvType) +{ + // Name and decorate the non-hidden members + int offset = -1; + int locationOffset = 0; // for use within the members of this struct + bool memberLocationInvalid = type.isArrayOfArrays() || + (type.isArray() && (type.getQualifier().isArrayedIo(glslangIntermediate->getStage()) == false)); + for (int i = 0; i < (int)glslangMembers->size(); i++) { + glslang::TType& glslangMember = *(*glslangMembers)[i].type; + int member = i; + if (type.getBasicType() == glslang::EbtBlock) { + member = memberRemapper[glslangTypeToIdMap[glslangMembers]][i]; + if (filterMember(glslangMember)) + continue; + } + + // modify just this child's view of the qualifier + glslang::TQualifier memberQualifier = glslangMember.getQualifier(); + InheritQualifiers(memberQualifier, qualifier); + + // using -1 above to indicate a hidden member + if (member < 0) + continue; + + builder.addMemberName(spvType, member, glslangMember.getFieldName().c_str()); + builder.addMemberDecoration(spvType, member, + TranslateLayoutDecoration(glslangMember, memberQualifier.layoutMatrix)); + builder.addMemberDecoration(spvType, member, TranslatePrecisionDecoration(glslangMember)); + // Add interpolation and auxiliary storage decorations only to + // top-level members of Input and Output storage classes + if (type.getQualifier().storage == glslang::EvqVaryingIn || + type.getQualifier().storage == glslang::EvqVaryingOut) { + if (type.getBasicType() == glslang::EbtBlock || + glslangIntermediate->getSource() == glslang::EShSourceHlsl) { + builder.addMemberDecoration(spvType, member, TranslateInterpolationDecoration(memberQualifier)); + builder.addMemberDecoration(spvType, member, TranslateAuxiliaryStorageDecoration(memberQualifier)); +#ifndef GLSLANG_WEB + addMeshNVDecoration(spvType, member, memberQualifier); +#endif + } + } + builder.addMemberDecoration(spvType, member, TranslateInvariantDecoration(memberQualifier)); + +#ifndef GLSLANG_WEB + if (type.getBasicType() == glslang::EbtBlock && + qualifier.storage == glslang::EvqBuffer) { + // Add memory decorations only to top-level members of shader storage block + std::vector memory; + TranslateMemoryDecoration(memberQualifier, memory, glslangIntermediate->usingVulkanMemoryModel()); + for (unsigned int i = 0; i < memory.size(); ++i) + builder.addMemberDecoration(spvType, member, memory[i]); + } + +#endif + + // Location assignment was already completed correctly by the front end, + // just track whether a member needs to be decorated. + // Ignore member locations if the container is an array, as that's + // ill-specified and decisions have been made to not allow this. + if (!memberLocationInvalid && memberQualifier.hasLocation()) + builder.addMemberDecoration(spvType, member, spv::DecorationLocation, memberQualifier.layoutLocation); + + if (qualifier.hasLocation()) // track for upcoming inheritance + locationOffset += glslangIntermediate->computeTypeLocationSize( + glslangMember, glslangIntermediate->getStage()); + + // component, XFB, others + if (glslangMember.getQualifier().hasComponent()) + builder.addMemberDecoration(spvType, member, spv::DecorationComponent, + glslangMember.getQualifier().layoutComponent); + if (glslangMember.getQualifier().hasXfbOffset()) + builder.addMemberDecoration(spvType, member, spv::DecorationOffset, + glslangMember.getQualifier().layoutXfbOffset); + else if (explicitLayout != glslang::ElpNone) { + // figure out what to do with offset, which is accumulating + int nextOffset; + updateMemberOffset(type, glslangMember, offset, nextOffset, explicitLayout, memberQualifier.layoutMatrix); + if (offset >= 0) + builder.addMemberDecoration(spvType, member, spv::DecorationOffset, offset); + offset = nextOffset; + } + + if (glslangMember.isMatrix() && explicitLayout != glslang::ElpNone) + builder.addMemberDecoration(spvType, member, spv::DecorationMatrixStride, + getMatrixStride(glslangMember, explicitLayout, memberQualifier.layoutMatrix)); + + // built-in variable decorations + spv::BuiltIn builtIn = TranslateBuiltInDecoration(glslangMember.getQualifier().builtIn, true); + if (builtIn != spv::BuiltInMax) + builder.addMemberDecoration(spvType, member, spv::DecorationBuiltIn, (int)builtIn); + +#ifndef GLSLANG_WEB + // nonuniform + builder.addMemberDecoration(spvType, member, TranslateNonUniformDecoration(glslangMember.getQualifier())); + + if (glslangIntermediate->getHlslFunctionality1() && memberQualifier.semanticName != nullptr) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE, + memberQualifier.semanticName); + } + + if (builtIn == spv::BuiltInLayer) { + // SPV_NV_viewport_array2 extension + if (glslangMember.getQualifier().layoutViewportRelative){ + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationViewportRelativeNV); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + builder.addExtension(spv::E_SPV_NV_viewport_array2); + } + if (glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset != -2048){ + builder.addMemberDecoration(spvType, member, + (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV, + glslangMember.getQualifier().layoutSecondaryViewportRelativeOffset); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + } + } + if (glslangMember.getQualifier().layoutPassthrough) { + builder.addMemberDecoration(spvType, member, (spv::Decoration)spv::DecorationPassthroughNV); + builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } +#endif + } + + // Decorate the structure + builder.addDecoration(spvType, TranslateLayoutDecoration(type, qualifier.layoutMatrix)); + builder.addDecoration(spvType, TranslateBlockDecoration(type, glslangIntermediate->usingStorageBuffer())); +} + +// Turn the expression forming the array size into an id. +// This is not quite trivial, because of specialization constants. +// Sometimes, a raw constant is turned into an Id, and sometimes +// a specialization constant expression is. +spv::Id TGlslangToSpvTraverser::makeArraySizeId(const glslang::TArraySizes& arraySizes, int dim) +{ + // First, see if this is sized with a node, meaning a specialization constant: + glslang::TIntermTyped* specNode = arraySizes.getDimNode(dim); + if (specNode != nullptr) { + builder.clearAccessChain(); + specNode->traverse(this); + return accessChainLoad(specNode->getAsTyped()->getType()); + } + + // Otherwise, need a compile-time (front end) size, get it: + int size = arraySizes.getDimSize(dim); + assert(size > 0); + return builder.makeUintConstant(size); +} + +// Wrap the builder's accessChainLoad to: +// - localize handling of RelaxedPrecision +// - use the SPIR-V inferred type instead of another conversion of the glslang type +// (avoids unnecessary work and possible type punning for structures) +// - do conversion of concrete to abstract type +spv::Id TGlslangToSpvTraverser::accessChainLoad(const glslang::TType& type) +{ + spv::Id nominalTypeId = builder.accessChainGetInferredType(); + + spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags; + coherentFlags |= TranslateCoherent(type); + + unsigned int alignment = builder.getAccessChain().alignment; + alignment |= type.getBufferReferenceAlignment(); + + spv::Id loadedId = builder.accessChainLoad(TranslatePrecisionDecoration(type), + TranslateNonUniformDecoration(type.getQualifier()), + nominalTypeId, + spv::MemoryAccessMask(TranslateMemoryAccess(coherentFlags) & ~spv::MemoryAccessMakePointerAvailableKHRMask), + TranslateMemoryScope(coherentFlags), + alignment); + + // Need to convert to abstract types when necessary + if (type.getBasicType() == glslang::EbtBool) { + if (builder.isScalarType(nominalTypeId)) { + // Conversion for bool + spv::Id boolType = builder.makeBoolType(); + if (nominalTypeId != boolType) + loadedId = builder.createBinOp(spv::OpINotEqual, boolType, loadedId, builder.makeUintConstant(0)); + } else if (builder.isVectorType(nominalTypeId)) { + // Conversion for bvec + int vecSize = builder.getNumTypeComponents(nominalTypeId); + spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); + if (nominalTypeId != bvecType) + loadedId = builder.createBinOp(spv::OpINotEqual, bvecType, loadedId, + makeSmearedConstant(builder.makeUintConstant(0), vecSize)); + } + } + + return loadedId; +} + +// Wrap the builder's accessChainStore to: +// - do conversion of concrete to abstract type +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::accessChainStore(const glslang::TType& type, spv::Id rvalue) +{ + // Need to convert to abstract types when necessary + if (type.getBasicType() == glslang::EbtBool) { + spv::Id nominalTypeId = builder.accessChainGetInferredType(); + + if (builder.isScalarType(nominalTypeId)) { + // Conversion for bool + spv::Id boolType = builder.makeBoolType(); + if (nominalTypeId != boolType) { + // keep these outside arguments, for determinant order-of-evaluation + spv::Id one = builder.makeUintConstant(1); + spv::Id zero = builder.makeUintConstant(0); + rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero); + } else if (builder.getTypeId(rvalue) != boolType) + rvalue = builder.createBinOp(spv::OpINotEqual, boolType, rvalue, builder.makeUintConstant(0)); + } else if (builder.isVectorType(nominalTypeId)) { + // Conversion for bvec + int vecSize = builder.getNumTypeComponents(nominalTypeId); + spv::Id bvecType = builder.makeVectorType(builder.makeBoolType(), vecSize); + if (nominalTypeId != bvecType) { + // keep these outside arguments, for determinant order-of-evaluation + spv::Id one = makeSmearedConstant(builder.makeUintConstant(1), vecSize); + spv::Id zero = makeSmearedConstant(builder.makeUintConstant(0), vecSize); + rvalue = builder.createTriOp(spv::OpSelect, nominalTypeId, rvalue, one, zero); + } else if (builder.getTypeId(rvalue) != bvecType) + rvalue = builder.createBinOp(spv::OpINotEqual, bvecType, rvalue, + makeSmearedConstant(builder.makeUintConstant(0), vecSize)); + } + } + + spv::Builder::AccessChain::CoherentFlags coherentFlags = builder.getAccessChain().coherentFlags; + coherentFlags |= TranslateCoherent(type); + + unsigned int alignment = builder.getAccessChain().alignment; + alignment |= type.getBufferReferenceAlignment(); + + builder.accessChainStore(rvalue, TranslateNonUniformDecoration(type.getQualifier()), + spv::MemoryAccessMask(TranslateMemoryAccess(coherentFlags) & + ~spv::MemoryAccessMakePointerVisibleKHRMask), + TranslateMemoryScope(coherentFlags), alignment); +} + +// For storing when types match at the glslang level, but not might match at the +// SPIR-V level. +// +// This especially happens when a single glslang type expands to multiple +// SPIR-V types, like a struct that is used in a member-undecorated way as well +// as in a member-decorated way. +// +// NOTE: This function can handle any store request; if it's not special it +// simplifies to a simple OpStore. +// +// Implicitly uses the existing builder.accessChain as the storage target. +void TGlslangToSpvTraverser::multiTypeStore(const glslang::TType& type, spv::Id rValue) +{ + // we only do the complex path here if it's an aggregate + if (! type.isStruct() && ! type.isArray()) { + accessChainStore(type, rValue); + return; + } + + // and, it has to be a case of type aliasing + spv::Id rType = builder.getTypeId(rValue); + spv::Id lValue = builder.accessChainGetLValue(); + spv::Id lType = builder.getContainedTypeId(builder.getTypeId(lValue)); + if (lType == rType) { + accessChainStore(type, rValue); + return; + } + + // Recursively (as needed) copy an aggregate type to a different aggregate type, + // where the two types were the same type in GLSL. This requires member + // by member copy, recursively. + + // SPIR-V 1.4 added an instruction to do help do this. + if (glslangIntermediate->getSpv().spv >= glslang::EShTargetSpv_1_4) { + // However, bool in uniform space is changed to int, so + // OpCopyLogical does not work for that. + // TODO: It would be more robust to do a full recursive verification of the types satisfying SPIR-V rules. + bool rBool = builder.containsType(builder.getTypeId(rValue), spv::OpTypeBool, 0); + bool lBool = builder.containsType(lType, spv::OpTypeBool, 0); + if (lBool == rBool) { + spv::Id logicalCopy = builder.createUnaryOp(spv::OpCopyLogical, lType, rValue); + accessChainStore(type, logicalCopy); + return; + } + } + + // If an array, copy element by element. + if (type.isArray()) { + glslang::TType glslangElementType(type, 0); + spv::Id elementRType = builder.getContainedTypeId(rType); + for (int index = 0; index < type.getOuterArraySize(); ++index) { + // get the source member + spv::Id elementRValue = builder.createCompositeExtract(rValue, elementRType, index); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(index), TranslateCoherent(type), + type.getBufferReferenceAlignment()); + + // store the member + multiTypeStore(glslangElementType, elementRValue); + } + } else { + assert(type.isStruct()); + + // loop over structure members + const glslang::TTypeList& members = *type.getStruct(); + for (int m = 0; m < (int)members.size(); ++m) { + const glslang::TType& glslangMemberType = *members[m].type; + + // get the source member + spv::Id memberRType = builder.getContainedTypeId(rType, m); + spv::Id memberRValue = builder.createCompositeExtract(rValue, memberRType, m); + + // set up the target storage + builder.clearAccessChain(); + builder.setAccessChainLValue(lValue); + builder.accessChainPush(builder.makeIntConstant(m), TranslateCoherent(type), + type.getBufferReferenceAlignment()); + + // store the member + multiTypeStore(glslangMemberType, memberRValue); + } + } +} + +// Decide whether or not this type should be +// decorated with offsets and strides, and if so +// whether std140 or std430 rules should be applied. +glslang::TLayoutPacking TGlslangToSpvTraverser::getExplicitLayout(const glslang::TType& type) const +{ + // has to be a block + if (type.getBasicType() != glslang::EbtBlock) + return glslang::ElpNone; + + // has to be a uniform or buffer block or task in/out blocks + if (type.getQualifier().storage != glslang::EvqUniform && + type.getQualifier().storage != glslang::EvqBuffer && + !type.getQualifier().isTaskMemory()) + return glslang::ElpNone; + + // return the layout to use + switch (type.getQualifier().layoutPacking) { + case glslang::ElpStd140: + case glslang::ElpStd430: + case glslang::ElpScalar: + return type.getQualifier().layoutPacking; + default: + return glslang::ElpNone; + } +} + +// Given an array type, returns the integer stride required for that array +int TGlslangToSpvTraverser::getArrayStride(const glslang::TType& arrayType, glslang::TLayoutPacking explicitLayout, + glslang::TLayoutMatrix matrixLayout) +{ + int size; + int stride; + glslangIntermediate->getMemberAlignment(arrayType, size, stride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + return stride; +} + +// Given a matrix type, or array (of array) of matrixes type, returns the integer stride required for that matrix +// when used as a member of an interface block +int TGlslangToSpvTraverser::getMatrixStride(const glslang::TType& matrixType, glslang::TLayoutPacking explicitLayout, + glslang::TLayoutMatrix matrixLayout) +{ + glslang::TType elementType; + elementType.shallowCopy(matrixType); + elementType.clearArraySizes(); + + int size; + int stride; + glslangIntermediate->getMemberAlignment(elementType, size, stride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + return stride; +} + +// Given a member type of a struct, realign the current offset for it, and compute +// the next (not yet aligned) offset for the next member, which will get aligned +// on the next call. +// 'currentOffset' should be passed in already initialized, ready to modify, and reflecting +// the migration of data from nextOffset -> currentOffset. It should be -1 on the first call. +// -1 means a non-forced member offset (no decoration needed). +void TGlslangToSpvTraverser::updateMemberOffset(const glslang::TType& structType, const glslang::TType& memberType, + int& currentOffset, int& nextOffset, glslang::TLayoutPacking explicitLayout, glslang::TLayoutMatrix matrixLayout) +{ + // this will get a positive value when deemed necessary + nextOffset = -1; + + // override anything in currentOffset with user-set offset + if (memberType.getQualifier().hasOffset()) + currentOffset = memberType.getQualifier().layoutOffset; + + // It could be that current linker usage in glslang updated all the layoutOffset, + // in which case the following code does not matter. But, that's not quite right + // once cross-compilation unit GLSL validation is done, as the original user + // settings are needed in layoutOffset, and then the following will come into play. + + if (explicitLayout == glslang::ElpNone) { + if (! memberType.getQualifier().hasOffset()) + currentOffset = -1; + + return; + } + + // Getting this far means we need explicit offsets + if (currentOffset < 0) + currentOffset = 0; + + // Now, currentOffset is valid (either 0, or from a previous nextOffset), + // but possibly not yet correctly aligned. + + int memberSize; + int dummyStride; + int memberAlignment = glslangIntermediate->getMemberAlignment(memberType, memberSize, dummyStride, explicitLayout, + matrixLayout == glslang::ElmRowMajor); + + // Adjust alignment for HLSL rules + // TODO: make this consistent in early phases of code: + // adjusting this late means inconsistencies with earlier code, which for reflection is an issue + // Until reflection is brought in sync with these adjustments, don't apply to $Global, + // which is the most likely to rely on reflection, and least likely to rely implicit layouts + if (glslangIntermediate->usingHlslOffsets() && + ! memberType.isArray() && memberType.isVector() && structType.getTypeName().compare("$Global") != 0) { + int dummySize; + int componentAlignment = glslangIntermediate->getBaseAlignmentScalar(memberType, dummySize); + if (componentAlignment <= 4) + memberAlignment = componentAlignment; + } + + // Bump up to member alignment + glslang::RoundToPow2(currentOffset, memberAlignment); + + // Bump up to vec4 if there is a bad straddle + if (explicitLayout != glslang::ElpScalar && glslangIntermediate->improperStraddle(memberType, memberSize, + currentOffset)) + glslang::RoundToPow2(currentOffset, 16); + + nextOffset = currentOffset + memberSize; +} + +void TGlslangToSpvTraverser::declareUseOfStructMember(const glslang::TTypeList& members, int glslangMember) +{ + const glslang::TBuiltInVariable glslangBuiltIn = members[glslangMember].type->getQualifier().builtIn; + switch (glslangBuiltIn) + { + case glslang::EbvPointSize: +#ifndef GLSLANG_WEB + case glslang::EbvClipDistance: + case glslang::EbvCullDistance: + case glslang::EbvViewportMaskNV: + case glslang::EbvSecondaryPositionNV: + case glslang::EbvSecondaryViewportMaskNV: + case glslang::EbvPositionPerViewNV: + case glslang::EbvViewportMaskPerViewNV: + case glslang::EbvTaskCountNV: + case glslang::EbvPrimitiveCountNV: + case glslang::EbvPrimitiveIndicesNV: + case glslang::EbvClipDistancePerViewNV: + case glslang::EbvCullDistancePerViewNV: + case glslang::EbvLayerPerViewNV: + case glslang::EbvMeshViewCountNV: + case glslang::EbvMeshViewIndicesNV: +#endif + // Generate the associated capability. Delegate to TranslateBuiltInDecoration. + // Alternately, we could just call this for any glslang built-in, since the + // capability already guards against duplicates. + TranslateBuiltInDecoration(glslangBuiltIn, false); + break; + default: + // Capabilities were already generated when the struct was declared. + break; + } +} + +bool TGlslangToSpvTraverser::isShaderEntryPoint(const glslang::TIntermAggregate* node) +{ + return node->getName().compare(glslangIntermediate->getEntryPointMangledName().c_str()) == 0; +} + +// Does parameter need a place to keep writes, separate from the original? +// Assumes called after originalParam(), which filters out block/buffer/opaque-based +// qualifiers such that we should have only in/out/inout/constreadonly here. +bool TGlslangToSpvTraverser::writableParam(glslang::TStorageQualifier qualifier) const +{ + assert(qualifier == glslang::EvqIn || + qualifier == glslang::EvqOut || + qualifier == glslang::EvqInOut || + qualifier == glslang::EvqUniform || + qualifier == glslang::EvqConstReadOnly); + return qualifier != glslang::EvqConstReadOnly && + qualifier != glslang::EvqUniform; +} + +// Is parameter pass-by-original? +bool TGlslangToSpvTraverser::originalParam(glslang::TStorageQualifier qualifier, const glslang::TType& paramType, + bool implicitThisParam) +{ + if (implicitThisParam) // implicit this + return true; + if (glslangIntermediate->getSource() == glslang::EShSourceHlsl) + return paramType.getBasicType() == glslang::EbtBlock; + return paramType.containsOpaque() || // sampler, etc. + (paramType.getBasicType() == glslang::EbtBlock && qualifier == glslang::EvqBuffer); // SSBO +} + +// Make all the functions, skeletally, without actually visiting their bodies. +void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslFunctions) +{ + const auto getParamDecorations = [&](std::vector& decorations, const glslang::TType& type, + bool useVulkanMemoryModel) { + spv::Decoration paramPrecision = TranslatePrecisionDecoration(type); + if (paramPrecision != spv::NoPrecision) + decorations.push_back(paramPrecision); + TranslateMemoryDecoration(type.getQualifier(), decorations, useVulkanMemoryModel); + if (type.isReference()) { + // Original and non-writable params pass the pointer directly and + // use restrict/aliased, others are stored to a pointer in Function + // memory and use RestrictPointer/AliasedPointer. + if (originalParam(type.getQualifier().storage, type, false) || + !writableParam(type.getQualifier().storage)) { + decorations.push_back(type.getQualifier().isRestrict() ? spv::DecorationRestrict : + spv::DecorationAliased); + } else { + decorations.push_back(type.getQualifier().isRestrict() ? spv::DecorationRestrictPointerEXT : + spv::DecorationAliasedPointerEXT); + } + } + }; + + for (int f = 0; f < (int)glslFunctions.size(); ++f) { + glslang::TIntermAggregate* glslFunction = glslFunctions[f]->getAsAggregate(); + if (! glslFunction || glslFunction->getOp() != glslang::EOpFunction || isShaderEntryPoint(glslFunction)) + continue; + + // We're on a user function. Set up the basic interface for the function now, + // so that it's available to call. Translating the body will happen later. + // + // Typically (except for a "const in" parameter), an address will be passed to the + // function. What it is an address of varies: + // + // - "in" parameters not marked as "const" can be written to without modifying the calling + // argument so that write needs to be to a copy, hence the address of a copy works. + // + // - "const in" parameters can just be the r-value, as no writes need occur. + // + // - "out" and "inout" arguments can't be done as pointers to the calling argument, because + // GLSL has copy-in/copy-out semantics. They can be handled though with a pointer to a copy. + + std::vector paramTypes; + std::vector> paramDecorations; // list of decorations per parameter + glslang::TIntermSequence& parameters = glslFunction->getSequence()[0]->getAsAggregate()->getSequence(); + +#ifdef ENABLE_HLSL + bool implicitThis = (int)parameters.size() > 0 && parameters[0]->getAsSymbolNode()->getName() == + glslangIntermediate->implicitThisName; +#else + bool implicitThis = false; +#endif + + paramDecorations.resize(parameters.size()); + for (int p = 0; p < (int)parameters.size(); ++p) { + const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); + spv::Id typeId = convertGlslangToSpvType(paramType); + if (originalParam(paramType.getQualifier().storage, paramType, implicitThis && p == 0)) + typeId = builder.makePointer(TranslateStorageClass(paramType), typeId); + else if (writableParam(paramType.getQualifier().storage)) + typeId = builder.makePointer(spv::StorageClassFunction, typeId); + else + rValueParameters.insert(parameters[p]->getAsSymbolNode()->getId()); + getParamDecorations(paramDecorations[p], paramType, glslangIntermediate->usingVulkanMemoryModel()); + paramTypes.push_back(typeId); + } + + spv::Block* functionBlock; + spv::Function *function = builder.makeFunctionEntry(TranslatePrecisionDecoration(glslFunction->getType()), + convertGlslangToSpvType(glslFunction->getType()), + glslFunction->getName().c_str(), paramTypes, + paramDecorations, &functionBlock); + if (implicitThis) + function->setImplicitThis(); + + // Track function to emit/call later + functionMap[glslFunction->getName().c_str()] = function; + + // Set the parameter id's + for (int p = 0; p < (int)parameters.size(); ++p) { + symbolValues[parameters[p]->getAsSymbolNode()->getId()] = function->getParamId(p); + // give a name too + builder.addName(function->getParamId(p), parameters[p]->getAsSymbolNode()->getName().c_str()); + + const glslang::TType& paramType = parameters[p]->getAsTyped()->getType(); + if (paramType.contains8BitInt()) + builder.addCapability(spv::CapabilityInt8); + if (paramType.contains16BitInt()) + builder.addCapability(spv::CapabilityInt16); + if (paramType.contains16BitFloat()) + builder.addCapability(spv::CapabilityFloat16); + } + } +} + +// Process all the initializers, while skipping the functions and link objects +void TGlslangToSpvTraverser::makeGlobalInitializers(const glslang::TIntermSequence& initializers) +{ + builder.setBuildPoint(shaderEntry->getLastBlock()); + for (int i = 0; i < (int)initializers.size(); ++i) { + glslang::TIntermAggregate* initializer = initializers[i]->getAsAggregate(); + if (initializer && initializer->getOp() != glslang::EOpFunction && initializer->getOp() != + glslang::EOpLinkerObjects) { + + // We're on a top-level node that's not a function. Treat as an initializer, whose + // code goes into the beginning of the entry point. + initializer->traverse(this); + } + } +} + +// Process all the functions, while skipping initializers. +void TGlslangToSpvTraverser::visitFunctions(const glslang::TIntermSequence& glslFunctions) +{ + for (int f = 0; f < (int)glslFunctions.size(); ++f) { + glslang::TIntermAggregate* node = glslFunctions[f]->getAsAggregate(); + if (node && (node->getOp() == glslang::EOpFunction || node->getOp() == glslang::EOpLinkerObjects)) + node->traverse(this); + } +} + +void TGlslangToSpvTraverser::handleFunctionEntry(const glslang::TIntermAggregate* node) +{ + // SPIR-V functions should already be in the functionMap from the prepass + // that called makeFunctions(). + currentFunction = functionMap[node->getName().c_str()]; + spv::Block* functionBlock = currentFunction->getEntryBlock(); + builder.setBuildPoint(functionBlock); +} + +void TGlslangToSpvTraverser::translateArguments(const glslang::TIntermAggregate& node, std::vector& arguments, + spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags) +{ + const glslang::TIntermSequence& glslangArguments = node.getSequence(); + + glslang::TSampler sampler = {}; + bool cubeCompare = false; +#ifndef GLSLANG_WEB + bool f16ShadowCompare = false; +#endif + if (node.isTexture() || node.isImage()) { + sampler = glslangArguments[0]->getAsTyped()->getType().getSampler(); + cubeCompare = sampler.dim == glslang::EsdCube && sampler.arrayed && sampler.shadow; +#ifndef GLSLANG_WEB + f16ShadowCompare = sampler.shadow && + glslangArguments[1]->getAsTyped()->getType().getBasicType() == glslang::EbtFloat16; +#endif + } + + for (int i = 0; i < (int)glslangArguments.size(); ++i) { + builder.clearAccessChain(); + glslangArguments[i]->traverse(this); + +#ifndef GLSLANG_WEB + // Special case l-value operands + bool lvalue = false; + switch (node.getOp()) { + case glslang::EOpImageAtomicAdd: + case glslang::EOpImageAtomicMin: + case glslang::EOpImageAtomicMax: + case glslang::EOpImageAtomicAnd: + case glslang::EOpImageAtomicOr: + case glslang::EOpImageAtomicXor: + case glslang::EOpImageAtomicExchange: + case glslang::EOpImageAtomicCompSwap: + case glslang::EOpImageAtomicLoad: + case glslang::EOpImageAtomicStore: + if (i == 0) + lvalue = true; + break; + case glslang::EOpSparseImageLoad: + if ((sampler.ms && i == 3) || (! sampler.ms && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTexture: + if (((cubeCompare || f16ShadowCompare) && i == 3) || (! (cubeCompare || f16ShadowCompare) && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureClamp: + if (((cubeCompare || f16ShadowCompare) && i == 4) || (! (cubeCompare || f16ShadowCompare) && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureLod: + case glslang::EOpSparseTextureOffset: + if ((f16ShadowCompare && i == 4) || (! f16ShadowCompare && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureFetch: + if ((sampler.dim != glslang::EsdRect && i == 3) || (sampler.dim == glslang::EsdRect && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureFetchOffset: + if ((sampler.dim != glslang::EsdRect && i == 4) || (sampler.dim == glslang::EsdRect && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureLodOffset: + case glslang::EOpSparseTextureGrad: + case glslang::EOpSparseTextureOffsetClamp: + if ((f16ShadowCompare && i == 5) || (! f16ShadowCompare && i == 4)) + lvalue = true; + break; + case glslang::EOpSparseTextureGradOffset: + case glslang::EOpSparseTextureGradClamp: + if ((f16ShadowCompare && i == 6) || (! f16ShadowCompare && i == 5)) + lvalue = true; + break; + case glslang::EOpSparseTextureGradOffsetClamp: + if ((f16ShadowCompare && i == 7) || (! f16ShadowCompare && i == 6)) + lvalue = true; + break; + case glslang::EOpSparseTextureGather: + if ((sampler.shadow && i == 3) || (! sampler.shadow && i == 2)) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherOffset: + case glslang::EOpSparseTextureGatherOffsets: + if ((sampler.shadow && i == 4) || (! sampler.shadow && i == 3)) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherLod: + if (i == 3) + lvalue = true; + break; + case glslang::EOpSparseTextureGatherLodOffset: + case glslang::EOpSparseTextureGatherLodOffsets: + if (i == 4) + lvalue = true; + break; + case glslang::EOpSparseImageLoadLod: + if (i == 3) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintNV: + if (i == 4) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintClampNV: + case glslang::EOpImageSampleFootprintLodNV: + if (i == 5) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintGradNV: + if (i == 6) + lvalue = true; + break; + case glslang::EOpImageSampleFootprintGradClampNV: + if (i == 7) + lvalue = true; + break; + default: + break; + } + + if (lvalue) { + arguments.push_back(builder.accessChainGetLValue()); + lvalueCoherentFlags = builder.getAccessChain().coherentFlags; + lvalueCoherentFlags |= TranslateCoherent(glslangArguments[i]->getAsTyped()->getType()); + } else +#endif + arguments.push_back(accessChainLoad(glslangArguments[i]->getAsTyped()->getType())); + } +} + +void TGlslangToSpvTraverser::translateArguments(glslang::TIntermUnary& node, std::vector& arguments) +{ + builder.clearAccessChain(); + node.getOperand()->traverse(this); + arguments.push_back(accessChainLoad(node.getOperand()->getType())); +} + +spv::Id TGlslangToSpvTraverser::createImageTextureFunctionCall(glslang::TIntermOperator* node) +{ + if (! node->isImage() && ! node->isTexture()) + return spv::NoResult; + + builder.setLine(node->getLoc().line, node->getLoc().getFilename()); + + // Process a GLSL texturing op (will be SPV image) + + const glslang::TType &imageType = node->getAsAggregate() + ? node->getAsAggregate()->getSequence()[0]->getAsTyped()->getType() + : node->getAsUnaryNode()->getOperand()->getAsTyped()->getType(); + const glslang::TSampler sampler = imageType.getSampler(); +#ifdef GLSLANG_WEB + const bool f16ShadowCompare = false; +#else + bool f16ShadowCompare = (sampler.shadow && node->getAsAggregate()) + ? node->getAsAggregate()->getSequence()[1]->getAsTyped()->getType().getBasicType() == glslang::EbtFloat16 + : false; +#endif + + const auto signExtensionMask = [&]() { + if (builder.getSpvVersion() >= spv::Spv_1_4) { + if (sampler.type == glslang::EbtUint) + return spv::ImageOperandsZeroExtendMask; + else if (sampler.type == glslang::EbtInt) + return spv::ImageOperandsSignExtendMask; + } + return spv::ImageOperandsMaskNone; + }; + + spv::Builder::AccessChain::CoherentFlags lvalueCoherentFlags; + + std::vector arguments; + if (node->getAsAggregate()) + translateArguments(*node->getAsAggregate(), arguments, lvalueCoherentFlags); + else + translateArguments(*node->getAsUnaryNode(), arguments); + spv::Decoration precision = TranslatePrecisionDecoration(node->getType()); + + spv::Builder::TextureParameters params = { }; + params.sampler = arguments[0]; + + glslang::TCrackedTextureOp cracked; + node->crackTexture(sampler, cracked); + + const bool isUnsignedResult = node->getType().getBasicType() == glslang::EbtUint; + + if (builder.isSampledImage(params.sampler) && + ((cracked.query && node->getOp() != glslang::EOpTextureQueryLod) || cracked.fragMask || cracked.fetch)) { + params.sampler = builder.createUnaryOp(spv::OpImage, builder.getImageType(params.sampler), params.sampler); + if (imageType.getQualifier().isNonUniform()) { + builder.addDecoration(params.sampler, spv::DecorationNonUniformEXT); + } + } + // Check for queries + if (cracked.query) { + switch (node->getOp()) { + case glslang::EOpImageQuerySize: + case glslang::EOpTextureQuerySize: + if (arguments.size() > 1) { + params.lod = arguments[1]; + return builder.createTextureQueryCall(spv::OpImageQuerySizeLod, params, isUnsignedResult); + } else + return builder.createTextureQueryCall(spv::OpImageQuerySize, params, isUnsignedResult); +#ifndef GLSLANG_WEB + case glslang::EOpImageQuerySamples: + case glslang::EOpTextureQuerySamples: + return builder.createTextureQueryCall(spv::OpImageQuerySamples, params, isUnsignedResult); + case glslang::EOpTextureQueryLod: + params.coords = arguments[1]; + return builder.createTextureQueryCall(spv::OpImageQueryLod, params, isUnsignedResult); + case glslang::EOpTextureQueryLevels: + return builder.createTextureQueryCall(spv::OpImageQueryLevels, params, isUnsignedResult); + case glslang::EOpSparseTexelsResident: + return builder.createUnaryOp(spv::OpImageSparseTexelsResident, builder.makeBoolType(), arguments[0]); +#endif + default: + assert(0); + break; + } + } + + int components = node->getType().getVectorSize(); + + if (node->getOp() == glslang::EOpTextureFetch) { + // These must produce 4 components, per SPIR-V spec. We'll add a conversion constructor if needed. + // This will only happen through the HLSL path for operator[], so we do not have to handle e.g. + // the EOpTexture/Proj/Lod/etc family. It would be harmless to do so, but would need more logic + // here around e.g. which ones return scalars or other types. + components = 4; + } + + glslang::TType returnType(node->getType().getBasicType(), glslang::EvqTemporary, components); + + auto resultType = [&returnType,this]{ return convertGlslangToSpvType(returnType); }; + + // Check for image functions other than queries + if (node->isImage()) { + std::vector operands; + auto opIt = arguments.begin(); + spv::IdImmediate image = { true, *(opIt++) }; + operands.push_back(image); + + // Handle subpass operations + // TODO: GLSL should change to have the "MS" only on the type rather than the + // built-in function. + if (cracked.subpass) { + // add on the (0,0) coordinate + spv::Id zero = builder.makeIntConstant(0); + std::vector comps; + comps.push_back(zero); + comps.push_back(zero); + spv::IdImmediate coord = { true, + builder.makeCompositeConstant(builder.makeVectorType(builder.makeIntType(32), 2), comps) }; + operands.push_back(coord); + spv::IdImmediate imageOperands = { false, spv::ImageOperandsMaskNone }; + imageOperands.word = imageOperands.word | signExtensionMask(); + if (sampler.isMultiSample()) { + imageOperands.word = imageOperands.word | spv::ImageOperandsSampleMask; + } + if (imageOperands.word != spv::ImageOperandsMaskNone) { + operands.push_back(imageOperands); + if (sampler.isMultiSample()) { + spv::IdImmediate imageOperand = { true, *(opIt++) }; + operands.push_back(imageOperand); + } + } + spv::Id result = builder.createOp(spv::OpImageRead, resultType(), operands); + builder.setPrecision(result, precision); + return result; + } + + spv::IdImmediate coord = { true, *(opIt++) }; + operands.push_back(coord); + if (node->getOp() == glslang::EOpImageLoad || node->getOp() == glslang::EOpImageLoadLod) { + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelAvailableKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelVisibleKHRMask) { + spv::IdImmediate imageOperand = { true, + builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat); + + std::vector result(1, builder.createOp(spv::OpImageRead, resultType(), operands)); + builder.setPrecision(result[0], precision); + + // If needed, add a conversion constructor to the proper size. + if (components != node->getType().getVectorSize()) + result[0] = builder.createConstructor(precision, result, convertGlslangToSpvType(node->getType())); + + return result[0]; + } else if (node->getOp() == glslang::EOpImageStore || node->getOp() == glslang::EOpImageStoreLod) { + + // Push the texel value before the operands + if (sampler.isMultiSample() || cracked.lod) { + spv::IdImmediate texel = { true, *(opIt + 1) }; + operands.push_back(texel); + } else { + spv::IdImmediate texel = { true, *opIt }; + operands.push_back(texel); + } + + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelVisibleKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelAvailableKHRMask) { + spv::IdImmediate imageOperand = { true, + builder.makeUintConstant(TranslateMemoryScope(TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + builder.createNoResultOp(spv::OpImageWrite, operands); + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageWriteWithoutFormat); + return spv::NoResult; + } else if (node->getOp() == glslang::EOpSparseImageLoad || + node->getOp() == glslang::EOpSparseImageLoadLod) { + builder.addCapability(spv::CapabilitySparseResidency); + if (builder.getImageTypeFormat(builder.getImageType(operands.front().word)) == spv::ImageFormatUnknown) + builder.addCapability(spv::CapabilityStorageImageReadWithoutFormat); + + spv::ImageOperandsMask mask = spv::ImageOperandsMaskNone; + if (sampler.isMultiSample()) { + mask = mask | spv::ImageOperandsSampleMask; + } + if (cracked.lod) { + builder.addExtension(spv::E_SPV_AMD_shader_image_load_store_lod); + builder.addCapability(spv::CapabilityImageReadWriteLodAMD); + + mask = mask | spv::ImageOperandsLodMask; + } + mask = mask | TranslateImageOperands(TranslateCoherent(imageType)); + mask = (spv::ImageOperandsMask)(mask & ~spv::ImageOperandsMakeTexelAvailableKHRMask); + mask = mask | signExtensionMask(); + if (mask != spv::ImageOperandsMaskNone) { + spv::IdImmediate imageOperands = { false, (unsigned int)mask }; + operands.push_back(imageOperands); + } + if (mask & spv::ImageOperandsSampleMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsLodMask) { + spv::IdImmediate imageOperand = { true, *opIt++ }; + operands.push_back(imageOperand); + } + if (mask & spv::ImageOperandsMakeTexelVisibleKHRMask) { + spv::IdImmediate imageOperand = { true, builder.makeUintConstant(TranslateMemoryScope( + TranslateCoherent(imageType))) }; + operands.push_back(imageOperand); + } + + // Create the return type that was a special structure + spv::Id texelOut = *opIt; + spv::Id typeId0 = resultType(); + spv::Id typeId1 = builder.getDerefTypeId(texelOut); + spv::Id resultTypeId = builder.makeStructResultType(typeId0, typeId1); + + spv::Id resultId = builder.createOp(spv::OpImageSparseRead, resultTypeId, operands); + + // Decode the return type + builder.createStore(builder.createCompositeExtract(resultId, typeId1, 1), texelOut); + return builder.createCompositeExtract(resultId, typeId0, 0); + } else { + // Process image atomic operations + + // GLSL "IMAGE_PARAMS" will involve in constructing an image texel pointer and this pointer, + // as the first source operand, is required by SPIR-V atomic operations. + // For non-MS, the sample value should be 0 + spv::IdImmediate sample = { true, sampler.isMultiSample() ? *(opIt++) : builder.makeUintConstant(0) }; + operands.push_back(sample); + + spv::Id resultTypeId; + // imageAtomicStore has a void return type so base the pointer type on + // the type of the value operand. + if (node->getOp() == glslang::EOpImageAtomicStore) { + resultTypeId = builder.makePointer(spv::StorageClassImage, builder.getTypeId(*opIt)); + } else { + resultTypeId = builder.makePointer(spv::StorageClassImage, resultType()); + } + spv::Id pointer = builder.createOp(spv::OpImageTexelPointer, resultTypeId, operands); + if (imageType.getQualifier().nonUniform) { + builder.addDecoration(pointer, spv::DecorationNonUniformEXT); + } + + std::vector operands; + operands.push_back(pointer); + for (; opIt != arguments.end(); ++opIt) + operands.push_back(*opIt); + + return createAtomicOperation(node->getOp(), precision, resultType(), operands, node->getBasicType(), + lvalueCoherentFlags); + } + } + +#ifndef GLSLANG_WEB + // Check for fragment mask functions other than queries + if (cracked.fragMask) { + assert(sampler.ms); + + auto opIt = arguments.begin(); + std::vector operands; + + operands.push_back(params.sampler); + ++opIt; + + if (sampler.isSubpass()) { + // add on the (0,0) coordinate + spv::Id zero = builder.makeIntConstant(0); + std::vector comps; + comps.push_back(zero); + comps.push_back(zero); + operands.push_back(builder.makeCompositeConstant( + builder.makeVectorType(builder.makeIntType(32), 2), comps)); + } + + for (; opIt != arguments.end(); ++opIt) + operands.push_back(*opIt); + + spv::Op fragMaskOp = spv::OpNop; + if (node->getOp() == glslang::EOpFragmentMaskFetch) + fragMaskOp = spv::OpFragmentMaskFetchAMD; + else if (node->getOp() == glslang::EOpFragmentFetch) + fragMaskOp = spv::OpFragmentFetchAMD; + + builder.addExtension(spv::E_SPV_AMD_shader_fragment_mask); + builder.addCapability(spv::CapabilityFragmentMaskAMD); + return builder.createOp(fragMaskOp, resultType(), operands); + } +#endif + + // Check for texture functions other than queries + bool sparse = node->isSparseTexture(); + bool imageFootprint = node->isImageFootprint(); + bool cubeCompare = sampler.dim == glslang::EsdCube && sampler.isArrayed() && sampler.isShadow(); + + // check for bias argument + bool bias = false; + if (! cracked.lod && ! cracked.grad && ! cracked.fetch && ! cubeCompare) { + int nonBiasArgCount = 2; + if (cracked.gather) + ++nonBiasArgCount; // comp argument should be present when bias argument is present + + if (f16ShadowCompare) + ++nonBiasArgCount; + if (cracked.offset) + ++nonBiasArgCount; + else if (cracked.offsets) + ++nonBiasArgCount; + if (cracked.grad) + nonBiasArgCount += 2; + if (cracked.lodClamp) + ++nonBiasArgCount; + if (sparse) + ++nonBiasArgCount; + if (imageFootprint) + //Following three extra arguments + // int granularity, bool coarse, out gl_TextureFootprint2DNV footprint + nonBiasArgCount += 3; + if ((int)arguments.size() > nonBiasArgCount) + bias = true; + } + +#ifndef GLSLANG_WEB + if (cracked.gather) { + const auto& sourceExtensions = glslangIntermediate->getRequestedExtensions(); + if (bias || cracked.lod || + sourceExtensions.find(glslang::E_GL_AMD_texture_gather_bias_lod) != sourceExtensions.end()) { + builder.addExtension(spv::E_SPV_AMD_texture_gather_bias_lod); + builder.addCapability(spv::CapabilityImageGatherBiasLodAMD); + } + } +#endif + + // set the rest of the arguments + + params.coords = arguments[1]; + int extraArgs = 0; + bool noImplicitLod = false; + + // sort out where Dref is coming from + if (cubeCompare || f16ShadowCompare) { + params.Dref = arguments[2]; + ++extraArgs; + } else if (sampler.shadow && cracked.gather) { + params.Dref = arguments[2]; + ++extraArgs; + } else if (sampler.shadow) { + std::vector indexes; + int dRefComp; + if (cracked.proj) + dRefComp = 2; // "The resulting 3rd component of P in the shadow forms is used as Dref" + else + dRefComp = builder.getNumComponents(params.coords) - 1; + indexes.push_back(dRefComp); + params.Dref = builder.createCompositeExtract(params.coords, + builder.getScalarTypeId(builder.getTypeId(params.coords)), indexes); + } + + // lod + if (cracked.lod) { + params.lod = arguments[2 + extraArgs]; + ++extraArgs; + } else if (glslangIntermediate->getStage() != EShLangFragment && + !(glslangIntermediate->getStage() == EShLangCompute && + glslangIntermediate->hasLayoutDerivativeModeNone())) { + // we need to invent the default lod for an explicit lod instruction for a non-fragment stage + noImplicitLod = true; + } + + // multisample + if (sampler.isMultiSample()) { + params.sample = arguments[2 + extraArgs]; // For MS, "sample" should be specified + ++extraArgs; + } + + // gradient + if (cracked.grad) { + params.gradX = arguments[2 + extraArgs]; + params.gradY = arguments[3 + extraArgs]; + extraArgs += 2; + } + + // offset and offsets + if (cracked.offset) { + params.offset = arguments[2 + extraArgs]; + ++extraArgs; + } else if (cracked.offsets) { + params.offsets = arguments[2 + extraArgs]; + ++extraArgs; + } + +#ifndef GLSLANG_WEB + // lod clamp + if (cracked.lodClamp) { + params.lodClamp = arguments[2 + extraArgs]; + ++extraArgs; + } + // sparse + if (sparse) { + params.texelOut = arguments[2 + extraArgs]; + ++extraArgs; + } + // gather component + if (cracked.gather && ! sampler.shadow) { + // default component is 0, if missing, otherwise an argument + if (2 + extraArgs < (int)arguments.size()) { + params.component = arguments[2 + extraArgs]; + ++extraArgs; + } else + params.component = builder.makeIntConstant(0); + } + spv::Id resultStruct = spv::NoResult; + if (imageFootprint) { + //Following three extra arguments + // int granularity, bool coarse, out gl_TextureFootprint2DNV footprint + params.granularity = arguments[2 + extraArgs]; + params.coarse = arguments[3 + extraArgs]; + resultStruct = arguments[4 + extraArgs]; + extraArgs += 3; + } +#endif + // bias + if (bias) { + params.bias = arguments[2 + extraArgs]; + ++extraArgs; + } + +#ifndef GLSLANG_WEB + if (imageFootprint) { + builder.addExtension(spv::E_SPV_NV_shader_image_footprint); + builder.addCapability(spv::CapabilityImageFootprintNV); + + + //resultStructType(OpenGL type) contains 5 elements: + //struct gl_TextureFootprint2DNV { + // uvec2 anchor; + // uvec2 offset; + // uvec2 mask; + // uint lod; + // uint granularity; + //}; + //or + //struct gl_TextureFootprint3DNV { + // uvec3 anchor; + // uvec3 offset; + // uvec2 mask; + // uint lod; + // uint granularity; + //}; + spv::Id resultStructType = builder.getContainedTypeId(builder.getTypeId(resultStruct)); + assert(builder.isStructType(resultStructType)); + + //resType (SPIR-V type) contains 6 elements: + //Member 0 must be a Boolean type scalar(LOD), + //Member 1 must be a vector of integer type, whose Signedness operand is 0(anchor), + //Member 2 must be a vector of integer type, whose Signedness operand is 0(offset), + //Member 3 must be a vector of integer type, whose Signedness operand is 0(mask), + //Member 4 must be a scalar of integer type, whose Signedness operand is 0(lod), + //Member 5 must be a scalar of integer type, whose Signedness operand is 0(granularity). + std::vector members; + members.push_back(resultType()); + for (int i = 0; i < 5; i++) { + members.push_back(builder.getContainedTypeId(resultStructType, i)); + } + spv::Id resType = builder.makeStructType(members, "ResType"); + + //call ImageFootprintNV + spv::Id res = builder.createTextureCall(precision, resType, sparse, cracked.fetch, cracked.proj, + cracked.gather, noImplicitLod, params, signExtensionMask()); + + //copy resType (SPIR-V type) to resultStructType(OpenGL type) + for (int i = 0; i < 5; i++) { + builder.clearAccessChain(); + builder.setAccessChainLValue(resultStruct); + + //Accessing to a struct we created, no coherent flag is set + spv::Builder::AccessChain::CoherentFlags flags; + flags.clear(); + + builder.accessChainPush(builder.makeIntConstant(i), flags, 0); + builder.accessChainStore(builder.createCompositeExtract(res, builder.getContainedTypeId(resType, i+1), + i+1), TranslateNonUniformDecoration(imageType.getQualifier())); + } + return builder.createCompositeExtract(res, resultType(), 0); + } +#endif + + // projective component (might not to move) + // GLSL: "The texture coordinates consumed from P, not including the last component of P, + // are divided by the last component of P." + // SPIR-V: "... (u [, v] [, w], q)... It may be a vector larger than needed, but all + // unused components will appear after all used components." + if (cracked.proj) { + int projSourceComp = builder.getNumComponents(params.coords) - 1; + int projTargetComp; + switch (sampler.dim) { + case glslang::Esd1D: projTargetComp = 1; break; + case glslang::Esd2D: projTargetComp = 2; break; + case glslang::EsdRect: projTargetComp = 2; break; + default: projTargetComp = projSourceComp; break; + } + // copy the projective coordinate if we have to + if (projTargetComp != projSourceComp) { + spv::Id projComp = builder.createCompositeExtract(params.coords, + builder.getScalarTypeId(builder.getTypeId(params.coords)), projSourceComp); + params.coords = builder.createCompositeInsert(projComp, params.coords, + builder.getTypeId(params.coords), projTargetComp); + } + } + +#ifndef GLSLANG_WEB + // nonprivate + if (imageType.getQualifier().nonprivate) { + params.nonprivate = true; + } + + // volatile + if (imageType.getQualifier().volatil) { + params.volatil = true; + } +#endif + + std::vector result( 1, + builder.createTextureCall(precision, resultType(), sparse, cracked.fetch, cracked.proj, cracked.gather, + noImplicitLod, params, signExtensionMask()) + ); + + if (components != node->getType().getVectorSize()) + result[0] = builder.createConstructor(precision, result, convertGlslangToSpvType(node->getType())); + + return result[0]; +} + +spv::Id TGlslangToSpvTraverser::handleUserFunctionCall(const glslang::TIntermAggregate* node) +{ + // Grab the function's pointer from the previously created function + spv::Function* function = functionMap[node->getName().c_str()]; + if (! function) + return 0; + + const glslang::TIntermSequence& glslangArgs = node->getSequence(); + const glslang::TQualifierList& qualifiers = node->getQualifierList(); + + // See comments in makeFunctions() for details about the semantics for parameter passing. + // + // These imply we need a four step process: + // 1. Evaluate the arguments + // 2. Allocate and make copies of in, out, and inout arguments + // 3. Make the call + // 4. Copy back the results + + // 1. Evaluate the arguments and their types + std::vector lValues; + std::vector rValues; + std::vector argTypes; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + argTypes.push_back(&glslangArgs[a]->getAsTyped()->getType()); + // build l-value + builder.clearAccessChain(); + glslangArgs[a]->traverse(this); + // keep outputs and pass-by-originals as l-values, evaluate others as r-values + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0) || + writableParam(qualifiers[a])) { + // save l-value + lValues.push_back(builder.getAccessChain()); + } else { + // process r-value + rValues.push_back(accessChainLoad(*argTypes.back())); + } + } + + // 2. Allocate space for anything needing a copy, and if it's "in" or "inout" + // copy the original into that space. + // + // Also, build up the list of actual arguments to pass in for the call + int lValueCount = 0; + int rValueCount = 0; + std::vector spvArgs; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + spv::Id arg; + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0)) { + builder.setAccessChain(lValues[lValueCount]); + arg = builder.accessChainGetLValue(); + ++lValueCount; + } else if (writableParam(qualifiers[a])) { + // need space to hold the copy + arg = builder.createVariable(function->getParamPrecision(a), spv::StorageClassFunction, + builder.getContainedTypeId(function->getParamType(a)), "param"); + if (qualifiers[a] == glslang::EvqIn || qualifiers[a] == glslang::EvqInOut) { + // need to copy the input into output space + builder.setAccessChain(lValues[lValueCount]); + spv::Id copy = accessChainLoad(*argTypes[a]); + builder.clearAccessChain(); + builder.setAccessChainLValue(arg); + multiTypeStore(*argTypes[a], copy); + } + ++lValueCount; + } else { + // process r-value, which involves a copy for a type mismatch + if (function->getParamType(a) != convertGlslangToSpvType(*argTypes[a]) || + TranslatePrecisionDecoration(*argTypes[a]) != function->getParamPrecision(a)) + { + spv::Id argCopy = builder.createVariable(function->getParamPrecision(a), spv::StorageClassFunction, function->getParamType(a), "arg"); + builder.clearAccessChain(); + builder.setAccessChainLValue(argCopy); + multiTypeStore(*argTypes[a], rValues[rValueCount]); + arg = builder.createLoad(argCopy, function->getParamPrecision(a)); + } else + arg = rValues[rValueCount]; + ++rValueCount; + } + spvArgs.push_back(arg); + } + + // 3. Make the call. + spv::Id result = builder.createFunctionCall(function, spvArgs); + builder.setPrecision(result, TranslatePrecisionDecoration(node->getType())); + + // 4. Copy back out an "out" arguments. + lValueCount = 0; + for (int a = 0; a < (int)glslangArgs.size(); ++a) { + if (originalParam(qualifiers[a], *argTypes[a], function->hasImplicitThis() && a == 0)) + ++lValueCount; + else if (writableParam(qualifiers[a])) { + if (qualifiers[a] == glslang::EvqOut || qualifiers[a] == glslang::EvqInOut) { + spv::Id copy = builder.createLoad(spvArgs[a], spv::NoPrecision); + builder.setAccessChain(lValues[lValueCount]); + multiTypeStore(*argTypes[a], copy); + } + ++lValueCount; + } + } + + return result; +} + +// Translate AST operation to SPV operation, already having SPV-based operands/types. +spv::Id TGlslangToSpvTraverser::createBinaryOperation(glslang::TOperator op, OpDecorations& decorations, + spv::Id typeId, spv::Id left, spv::Id right, + glslang::TBasicType typeProxy, bool reduceComparison) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + bool isBool = typeProxy == glslang::EbtBool; + + spv::Op binOp = spv::OpNop; + bool needMatchingVectors = true; // for non-matrix ops, would a scalar need to smear to match a vector? + bool comparison = false; + + switch (op) { + case glslang::EOpAdd: + case glslang::EOpAddAssign: + if (isFloat) + binOp = spv::OpFAdd; + else + binOp = spv::OpIAdd; + break; + case glslang::EOpSub: + case glslang::EOpSubAssign: + if (isFloat) + binOp = spv::OpFSub; + else + binOp = spv::OpISub; + break; + case glslang::EOpMul: + case glslang::EOpMulAssign: + if (isFloat) + binOp = spv::OpFMul; + else + binOp = spv::OpIMul; + break; + case glslang::EOpVectorTimesScalar: + case glslang::EOpVectorTimesScalarAssign: + if (isFloat && (builder.isVector(left) || builder.isVector(right))) { + if (builder.isVector(right)) + std::swap(left, right); + assert(builder.isScalar(right)); + needMatchingVectors = false; + binOp = spv::OpVectorTimesScalar; + } else if (isFloat) + binOp = spv::OpFMul; + else + binOp = spv::OpIMul; + break; + case glslang::EOpVectorTimesMatrix: + case glslang::EOpVectorTimesMatrixAssign: + binOp = spv::OpVectorTimesMatrix; + break; + case glslang::EOpMatrixTimesVector: + binOp = spv::OpMatrixTimesVector; + break; + case glslang::EOpMatrixTimesScalar: + case glslang::EOpMatrixTimesScalarAssign: + binOp = spv::OpMatrixTimesScalar; + break; + case glslang::EOpMatrixTimesMatrix: + case glslang::EOpMatrixTimesMatrixAssign: + binOp = spv::OpMatrixTimesMatrix; + break; + case glslang::EOpOuterProduct: + binOp = spv::OpOuterProduct; + needMatchingVectors = false; + break; + + case glslang::EOpDiv: + case glslang::EOpDivAssign: + if (isFloat) + binOp = spv::OpFDiv; + else if (isUnsigned) + binOp = spv::OpUDiv; + else + binOp = spv::OpSDiv; + break; + case glslang::EOpMod: + case glslang::EOpModAssign: + if (isFloat) + binOp = spv::OpFMod; + else if (isUnsigned) + binOp = spv::OpUMod; + else + binOp = spv::OpSMod; + break; + case glslang::EOpRightShift: + case glslang::EOpRightShiftAssign: + if (isUnsigned) + binOp = spv::OpShiftRightLogical; + else + binOp = spv::OpShiftRightArithmetic; + break; + case glslang::EOpLeftShift: + case glslang::EOpLeftShiftAssign: + binOp = spv::OpShiftLeftLogical; + break; + case glslang::EOpAnd: + case glslang::EOpAndAssign: + binOp = spv::OpBitwiseAnd; + break; + case glslang::EOpLogicalAnd: + needMatchingVectors = false; + binOp = spv::OpLogicalAnd; + break; + case glslang::EOpInclusiveOr: + case glslang::EOpInclusiveOrAssign: + binOp = spv::OpBitwiseOr; + break; + case glslang::EOpLogicalOr: + needMatchingVectors = false; + binOp = spv::OpLogicalOr; + break; + case glslang::EOpExclusiveOr: + case glslang::EOpExclusiveOrAssign: + binOp = spv::OpBitwiseXor; + break; + case glslang::EOpLogicalXor: + needMatchingVectors = false; + binOp = spv::OpLogicalNotEqual; + break; + + case glslang::EOpAbsDifference: + binOp = isUnsigned ? spv::OpAbsUSubINTEL : spv::OpAbsISubINTEL; + break; + + case glslang::EOpAddSaturate: + binOp = isUnsigned ? spv::OpUAddSatINTEL : spv::OpIAddSatINTEL; + break; + + case glslang::EOpSubSaturate: + binOp = isUnsigned ? spv::OpUSubSatINTEL : spv::OpISubSatINTEL; + break; + + case glslang::EOpAverage: + binOp = isUnsigned ? spv::OpUAverageINTEL : spv::OpIAverageINTEL; + break; + + case glslang::EOpAverageRounded: + binOp = isUnsigned ? spv::OpUAverageRoundedINTEL : spv::OpIAverageRoundedINTEL; + break; + + case glslang::EOpMul32x16: + binOp = isUnsigned ? spv::OpUMul32x16INTEL : spv::OpIMul32x16INTEL; + break; + + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpEqual: + case glslang::EOpNotEqual: + case glslang::EOpVectorEqual: + case glslang::EOpVectorNotEqual: + comparison = true; + break; + default: + break; + } + + // handle mapped binary operations (should be non-comparison) + if (binOp != spv::OpNop) { + assert(comparison == false); + if (builder.isMatrix(left) || builder.isMatrix(right) || + builder.isCooperativeMatrix(left) || builder.isCooperativeMatrix(right)) + return createBinaryMatrixOperation(binOp, decorations, typeId, left, right); + + // No matrix involved; make both operands be the same number of components, if needed + if (needMatchingVectors) + builder.promoteScalar(decorations.precision, left, right); + + spv::Id result = builder.createBinOp(binOp, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + if (! comparison) + return 0; + + // Handle comparison instructions + + if (reduceComparison && (op == glslang::EOpEqual || op == glslang::EOpNotEqual) + && (builder.isVector(left) || builder.isMatrix(left) || builder.isAggregate(left))) { + spv::Id result = builder.createCompositeCompare(decorations.precision, left, right, op == glslang::EOpEqual); + decorations.addNonUniform(builder, result); + return result; + } + + switch (op) { + case glslang::EOpLessThan: + if (isFloat) + binOp = spv::OpFOrdLessThan; + else if (isUnsigned) + binOp = spv::OpULessThan; + else + binOp = spv::OpSLessThan; + break; + case glslang::EOpGreaterThan: + if (isFloat) + binOp = spv::OpFOrdGreaterThan; + else if (isUnsigned) + binOp = spv::OpUGreaterThan; + else + binOp = spv::OpSGreaterThan; + break; + case glslang::EOpLessThanEqual: + if (isFloat) + binOp = spv::OpFOrdLessThanEqual; + else if (isUnsigned) + binOp = spv::OpULessThanEqual; + else + binOp = spv::OpSLessThanEqual; + break; + case glslang::EOpGreaterThanEqual: + if (isFloat) + binOp = spv::OpFOrdGreaterThanEqual; + else if (isUnsigned) + binOp = spv::OpUGreaterThanEqual; + else + binOp = spv::OpSGreaterThanEqual; + break; + case glslang::EOpEqual: + case glslang::EOpVectorEqual: + if (isFloat) + binOp = spv::OpFOrdEqual; + else if (isBool) + binOp = spv::OpLogicalEqual; + else + binOp = spv::OpIEqual; + break; + case glslang::EOpNotEqual: + case glslang::EOpVectorNotEqual: + if (isFloat) + binOp = spv::OpFUnordNotEqual; + else if (isBool) + binOp = spv::OpLogicalNotEqual; + else + binOp = spv::OpINotEqual; + break; + default: + break; + } + + if (binOp != spv::OpNop) { + spv::Id result = builder.createBinOp(binOp, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + return 0; +} + +// +// Translate AST matrix operation to SPV operation, already having SPV-based operands/types. +// These can be any of: +// +// matrix * scalar +// scalar * matrix +// matrix * matrix linear algebraic +// matrix * vector +// vector * matrix +// matrix * matrix componentwise +// matrix op matrix op in {+, -, /} +// matrix op scalar op in {+, -, /} +// scalar op matrix op in {+, -, /} +// +spv::Id TGlslangToSpvTraverser::createBinaryMatrixOperation(spv::Op op, OpDecorations& decorations, spv::Id typeId, + spv::Id left, spv::Id right) +{ + bool firstClass = true; + + // First, handle first-class matrix operations (* and matrix/scalar) + switch (op) { + case spv::OpFDiv: + if (builder.isMatrix(left) && builder.isScalar(right)) { + // turn matrix / scalar into a multiply... + spv::Id resultType = builder.getTypeId(right); + right = builder.createBinOp(spv::OpFDiv, resultType, builder.makeFpConstant(resultType, 1.0), right); + op = spv::OpMatrixTimesScalar; + } else + firstClass = false; + break; + case spv::OpMatrixTimesScalar: + if (builder.isMatrix(right) || builder.isCooperativeMatrix(right)) + std::swap(left, right); + assert(builder.isScalar(right)); + break; + case spv::OpVectorTimesMatrix: + assert(builder.isVector(left)); + assert(builder.isMatrix(right)); + break; + case spv::OpMatrixTimesVector: + assert(builder.isMatrix(left)); + assert(builder.isVector(right)); + break; + case spv::OpMatrixTimesMatrix: + assert(builder.isMatrix(left)); + assert(builder.isMatrix(right)); + break; + default: + firstClass = false; + break; + } + + if (builder.isCooperativeMatrix(left) || builder.isCooperativeMatrix(right)) + firstClass = true; + + if (firstClass) { + spv::Id result = builder.createBinOp(op, typeId, left, right); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + return builder.setPrecision(result, decorations.precision); + } + + // Handle component-wise +, -, *, %, and / for all combinations of type. + // The result type of all of them is the same type as the (a) matrix operand. + // The algorithm is to: + // - break the matrix(es) into vectors + // - smear any scalar to a vector + // - do vector operations + // - make a matrix out the vector results + switch (op) { + case spv::OpFAdd: + case spv::OpFSub: + case spv::OpFDiv: + case spv::OpFMod: + case spv::OpFMul: + { + // one time set up... + bool leftMat = builder.isMatrix(left); + bool rightMat = builder.isMatrix(right); + unsigned int numCols = leftMat ? builder.getNumColumns(left) : builder.getNumColumns(right); + int numRows = leftMat ? builder.getNumRows(left) : builder.getNumRows(right); + spv::Id scalarType = builder.getScalarTypeId(typeId); + spv::Id vecType = builder.makeVectorType(scalarType, numRows); + std::vector results; + spv::Id smearVec = spv::NoResult; + if (builder.isScalar(left)) + smearVec = builder.smearScalar(decorations.precision, left, vecType); + else if (builder.isScalar(right)) + smearVec = builder.smearScalar(decorations.precision, right, vecType); + + // do each vector op + for (unsigned int c = 0; c < numCols; ++c) { + std::vector indexes; + indexes.push_back(c); + spv::Id leftVec = leftMat ? builder.createCompositeExtract( left, vecType, indexes) : smearVec; + spv::Id rightVec = rightMat ? builder.createCompositeExtract(right, vecType, indexes) : smearVec; + spv::Id result = builder.createBinOp(op, vecType, leftVec, rightVec); + decorations.addNoContraction(builder, result); + decorations.addNonUniform(builder, result); + results.push_back(builder.setPrecision(result, decorations.precision)); + } + + // put the pieces together + spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision); + decorations.addNonUniform(builder, result); + return result; + } + default: + assert(0); + return spv::NoResult; + } +} + +spv::Id TGlslangToSpvTraverser::createUnaryOperation(glslang::TOperator op, OpDecorations& decorations, spv::Id typeId, + spv::Id operand, glslang::TBasicType typeProxy, const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags) +{ + spv::Op unaryOp = spv::OpNop; + int extBuiltins = -1; + int libCall = -1; + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + switch (op) { + case glslang::EOpNegative: + if (isFloat) { + unaryOp = spv::OpFNegate; + if (builder.isMatrixType(typeId)) + return createUnaryMatrixOperation(unaryOp, decorations, typeId, operand, typeProxy); + } else + unaryOp = spv::OpSNegate; + break; + + case glslang::EOpLogicalNot: + case glslang::EOpVectorLogicalNot: + unaryOp = spv::OpLogicalNot; + break; + case glslang::EOpBitwiseNot: + unaryOp = spv::OpNot; + break; + + case glslang::EOpDeterminant: + libCall = spv::GLSLstd450Determinant; + break; + case glslang::EOpMatrixInverse: + libCall = spv::GLSLstd450MatrixInverse; + break; + case glslang::EOpTranspose: + unaryOp = spv::OpTranspose; + break; + + case glslang::EOpRadians: + libCall = spv::GLSLstd450Radians; + break; + case glslang::EOpDegrees: + libCall = spv::GLSLstd450Degrees; + break; + case glslang::EOpSin: + libCall = spv::GLSLstd450Sin; + break; + case glslang::EOpCos: + libCall = spv::GLSLstd450Cos; + break; + case glslang::EOpTan: + libCall = spv::GLSLstd450Tan; + break; + case glslang::EOpAcos: + libCall = spv::GLSLstd450Acos; + break; + case glslang::EOpAsin: + libCall = spv::GLSLstd450Asin; + break; + case glslang::EOpAtan: + libCall = spv::GLSLstd450Atan; + break; + + case glslang::EOpAcosh: + libCall = spv::GLSLstd450Acosh; + break; + case glslang::EOpAsinh: + libCall = spv::GLSLstd450Asinh; + break; + case glslang::EOpAtanh: + libCall = spv::GLSLstd450Atanh; + break; + case glslang::EOpTanh: + libCall = spv::GLSLstd450Tanh; + break; + case glslang::EOpCosh: + libCall = spv::GLSLstd450Cosh; + break; + case glslang::EOpSinh: + libCall = spv::GLSLstd450Sinh; + break; + + case glslang::EOpLength: + libCall = spv::GLSLstd450Length; + break; + case glslang::EOpNormalize: + libCall = spv::GLSLstd450Normalize; + break; + + case glslang::EOpExp: + libCall = spv::GLSLstd450Exp; + break; + case glslang::EOpLog: + libCall = spv::GLSLstd450Log; + break; + case glslang::EOpExp2: + libCall = spv::GLSLstd450Exp2; + break; + case glslang::EOpLog2: + libCall = spv::GLSLstd450Log2; + break; + case glslang::EOpSqrt: + libCall = spv::GLSLstd450Sqrt; + break; + case glslang::EOpInverseSqrt: + libCall = spv::GLSLstd450InverseSqrt; + break; + + case glslang::EOpFloor: + libCall = spv::GLSLstd450Floor; + break; + case glslang::EOpTrunc: + libCall = spv::GLSLstd450Trunc; + break; + case glslang::EOpRound: + libCall = spv::GLSLstd450Round; + break; + case glslang::EOpRoundEven: + libCall = spv::GLSLstd450RoundEven; + break; + case glslang::EOpCeil: + libCall = spv::GLSLstd450Ceil; + break; + case glslang::EOpFract: + libCall = spv::GLSLstd450Fract; + break; + + case glslang::EOpIsNan: + unaryOp = spv::OpIsNan; + break; + case glslang::EOpIsInf: + unaryOp = spv::OpIsInf; + break; + case glslang::EOpIsFinite: + unaryOp = spv::OpIsFinite; + break; + + case glslang::EOpFloatBitsToInt: + case glslang::EOpFloatBitsToUint: + case glslang::EOpIntBitsToFloat: + case glslang::EOpUintBitsToFloat: + case glslang::EOpDoubleBitsToInt64: + case glslang::EOpDoubleBitsToUint64: + case glslang::EOpInt64BitsToDouble: + case glslang::EOpUint64BitsToDouble: + case glslang::EOpFloat16BitsToInt16: + case glslang::EOpFloat16BitsToUint16: + case glslang::EOpInt16BitsToFloat16: + case glslang::EOpUint16BitsToFloat16: + unaryOp = spv::OpBitcast; + break; + + case glslang::EOpPackSnorm2x16: + libCall = spv::GLSLstd450PackSnorm2x16; + break; + case glslang::EOpUnpackSnorm2x16: + libCall = spv::GLSLstd450UnpackSnorm2x16; + break; + case glslang::EOpPackUnorm2x16: + libCall = spv::GLSLstd450PackUnorm2x16; + break; + case glslang::EOpUnpackUnorm2x16: + libCall = spv::GLSLstd450UnpackUnorm2x16; + break; + case glslang::EOpPackHalf2x16: + libCall = spv::GLSLstd450PackHalf2x16; + break; + case glslang::EOpUnpackHalf2x16: + libCall = spv::GLSLstd450UnpackHalf2x16; + break; +#ifndef GLSLANG_WEB + case glslang::EOpPackSnorm4x8: + libCall = spv::GLSLstd450PackSnorm4x8; + break; + case glslang::EOpUnpackSnorm4x8: + libCall = spv::GLSLstd450UnpackSnorm4x8; + break; + case glslang::EOpPackUnorm4x8: + libCall = spv::GLSLstd450PackUnorm4x8; + break; + case glslang::EOpUnpackUnorm4x8: + libCall = spv::GLSLstd450UnpackUnorm4x8; + break; + case glslang::EOpPackDouble2x32: + libCall = spv::GLSLstd450PackDouble2x32; + break; + case glslang::EOpUnpackDouble2x32: + libCall = spv::GLSLstd450UnpackDouble2x32; + break; +#endif + + case glslang::EOpPackInt2x32: + case glslang::EOpUnpackInt2x32: + case glslang::EOpPackUint2x32: + case glslang::EOpUnpackUint2x32: + case glslang::EOpPack16: + case glslang::EOpPack32: + case glslang::EOpPack64: + case glslang::EOpUnpack32: + case glslang::EOpUnpack16: + case glslang::EOpUnpack8: + case glslang::EOpPackInt2x16: + case glslang::EOpUnpackInt2x16: + case glslang::EOpPackUint2x16: + case glslang::EOpUnpackUint2x16: + case glslang::EOpPackInt4x16: + case glslang::EOpUnpackInt4x16: + case glslang::EOpPackUint4x16: + case glslang::EOpUnpackUint4x16: + case glslang::EOpPackFloat2x16: + case glslang::EOpUnpackFloat2x16: + unaryOp = spv::OpBitcast; + break; + + case glslang::EOpDPdx: + unaryOp = spv::OpDPdx; + break; + case glslang::EOpDPdy: + unaryOp = spv::OpDPdy; + break; + case glslang::EOpFwidth: + unaryOp = spv::OpFwidth; + break; + + case glslang::EOpAny: + unaryOp = spv::OpAny; + break; + case glslang::EOpAll: + unaryOp = spv::OpAll; + break; + + case glslang::EOpAbs: + if (isFloat) + libCall = spv::GLSLstd450FAbs; + else + libCall = spv::GLSLstd450SAbs; + break; + case glslang::EOpSign: + if (isFloat) + libCall = spv::GLSLstd450FSign; + else + libCall = spv::GLSLstd450SSign; + break; + +#ifndef GLSLANG_WEB + case glslang::EOpDPdxFine: + unaryOp = spv::OpDPdxFine; + break; + case glslang::EOpDPdyFine: + unaryOp = spv::OpDPdyFine; + break; + case glslang::EOpFwidthFine: + unaryOp = spv::OpFwidthFine; + break; + case glslang::EOpDPdxCoarse: + unaryOp = spv::OpDPdxCoarse; + break; + case glslang::EOpDPdyCoarse: + unaryOp = spv::OpDPdyCoarse; + break; + case glslang::EOpFwidthCoarse: + unaryOp = spv::OpFwidthCoarse; + break; + case glslang::EOpRayQueryProceed: + unaryOp = spv::OpRayQueryProceedKHR; + break; + case glslang::EOpRayQueryGetRayTMin: + unaryOp = spv::OpRayQueryGetRayTMinKHR; + break; + case glslang::EOpRayQueryGetRayFlags: + unaryOp = spv::OpRayQueryGetRayFlagsKHR; + break; + case glslang::EOpRayQueryGetWorldRayOrigin: + unaryOp = spv::OpRayQueryGetWorldRayOriginKHR; + break; + case glslang::EOpRayQueryGetWorldRayDirection: + unaryOp = spv::OpRayQueryGetWorldRayDirectionKHR; + break; + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + unaryOp = spv::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR; + break; + case glslang::EOpInterpolateAtCentroid: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtCentroid; + break; + case glslang::EOpAtomicCounterIncrement: + case glslang::EOpAtomicCounterDecrement: + case glslang::EOpAtomicCounter: + { + // Handle all of the atomics in one place, in createAtomicOperation() + std::vector operands; + operands.push_back(operand); + return createAtomicOperation(op, decorations.precision, typeId, operands, typeProxy, lvalueCoherentFlags); + } + + case glslang::EOpBitFieldReverse: + unaryOp = spv::OpBitReverse; + break; + case glslang::EOpBitCount: + unaryOp = spv::OpBitCount; + break; + case glslang::EOpFindLSB: + libCall = spv::GLSLstd450FindILsb; + break; + case glslang::EOpFindMSB: + if (isUnsigned) + libCall = spv::GLSLstd450FindUMsb; + else + libCall = spv::GLSLstd450FindSMsb; + break; + + case glslang::EOpCountLeadingZeros: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + unaryOp = spv::OpUCountLeadingZerosINTEL; + break; + + case glslang::EOpCountTrailingZeros: + builder.addCapability(spv::CapabilityIntegerFunctions2INTEL); + builder.addExtension("SPV_INTEL_shader_integer_functions2"); + unaryOp = spv::OpUCountTrailingZerosINTEL; + break; + + case glslang::EOpBallot: + case glslang::EOpReadFirstInvocation: + case glslang::EOpAnyInvocation: + case glslang::EOpAllInvocations: + case glslang::EOpAllInvocationsEqual: + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + { + std::vector operands; + operands.push_back(operand); + return createInvocationsOperation(op, typeId, operands, typeProxy); + } + case glslang::EOpSubgroupAll: + case glslang::EOpSubgroupAny: + case glslang::EOpSubgroupAllEqual: + case glslang::EOpSubgroupBroadcastFirst: + case glslang::EOpSubgroupBallot: + case glslang::EOpSubgroupInverseBallot: + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupBallotFindLSB: + case glslang::EOpSubgroupBallotFindMSB: + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: { + std::vector operands; + operands.push_back(operand); + return createSubgroupOperation(op, typeId, operands, typeProxy); + } + case glslang::EOpMbcnt: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::MbcntAMD; + break; + + case glslang::EOpCubeFaceIndex: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader); + libCall = spv::CubeFaceIndexAMD; + break; + + case glslang::EOpCubeFaceCoord: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_gcn_shader); + libCall = spv::CubeFaceCoordAMD; + break; + case glslang::EOpSubgroupPartition: + unaryOp = spv::OpGroupNonUniformPartitionNV; + break; + case glslang::EOpConstructReference: + unaryOp = spv::OpBitcast; + break; +#endif + + case glslang::EOpCopyObject: + unaryOp = spv::OpCopyObject; + break; + + default: + return 0; + } + + spv::Id id; + if (libCall >= 0) { + std::vector args; + args.push_back(operand); + id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, args); + } else { + id = builder.createUnaryOp(unaryOp, typeId, operand); + } + + decorations.addNoContraction(builder, id); + decorations.addNonUniform(builder, id); + return builder.setPrecision(id, decorations.precision); +} + +// Create a unary operation on a matrix +spv::Id TGlslangToSpvTraverser::createUnaryMatrixOperation(spv::Op op, OpDecorations& decorations, spv::Id typeId, + spv::Id operand, glslang::TBasicType /* typeProxy */) +{ + // Handle unary operations vector by vector. + // The result type is the same type as the original type. + // The algorithm is to: + // - break the matrix into vectors + // - apply the operation to each vector + // - make a matrix out the vector results + + // get the types sorted out + int numCols = builder.getNumColumns(operand); + int numRows = builder.getNumRows(operand); + spv::Id srcVecType = builder.makeVectorType(builder.getScalarTypeId(builder.getTypeId(operand)), numRows); + spv::Id destVecType = builder.makeVectorType(builder.getScalarTypeId(typeId), numRows); + std::vector results; + + // do each vector op + for (int c = 0; c < numCols; ++c) { + std::vector indexes; + indexes.push_back(c); + spv::Id srcVec = builder.createCompositeExtract(operand, srcVecType, indexes); + spv::Id destVec = builder.createUnaryOp(op, destVecType, srcVec); + decorations.addNoContraction(builder, destVec); + decorations.addNonUniform(builder, destVec); + results.push_back(builder.setPrecision(destVec, decorations.precision)); + } + + // put the pieces together + spv::Id result = builder.setPrecision(builder.createCompositeConstruct(typeId, results), decorations.precision); + decorations.addNonUniform(builder, result); + return result; +} + +// For converting integers where both the bitwidth and the signedness could +// change, but only do the width change here. The caller is still responsible +// for the signedness conversion. +spv::Id TGlslangToSpvTraverser::createIntWidthConversion(glslang::TOperator op, spv::Id operand, int vectorSize) +{ + // Get the result type width, based on the type to convert to. + int width = 32; + switch(op) { + case glslang::EOpConvInt16ToUint8: + case glslang::EOpConvIntToUint8: + case glslang::EOpConvInt64ToUint8: + case glslang::EOpConvUint16ToInt8: + case glslang::EOpConvUintToInt8: + case glslang::EOpConvUint64ToInt8: + width = 8; + break; + case glslang::EOpConvInt8ToUint16: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvInt64ToUint16: + case glslang::EOpConvUint8ToInt16: + case glslang::EOpConvUintToInt16: + case glslang::EOpConvUint64ToInt16: + width = 16; + break; + case glslang::EOpConvInt8ToUint: + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvInt64ToUint: + case glslang::EOpConvUint8ToInt: + case glslang::EOpConvUint16ToInt: + case glslang::EOpConvUint64ToInt: + width = 32; + break; + case glslang::EOpConvInt8ToUint64: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvIntToUint64: + case glslang::EOpConvUint8ToInt64: + case glslang::EOpConvUint16ToInt64: + case glslang::EOpConvUintToInt64: + width = 64; + break; + + default: + assert(false && "Default missing"); + break; + } + + // Get the conversion operation and result type, + // based on the target width, but the source type. + spv::Id type = spv::NoType; + spv::Op convOp = spv::OpNop; + switch(op) { + case glslang::EOpConvInt8ToUint16: + case glslang::EOpConvInt8ToUint: + case glslang::EOpConvInt8ToUint64: + case glslang::EOpConvInt16ToUint8: + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvIntToUint8: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvIntToUint64: + case glslang::EOpConvInt64ToUint8: + case glslang::EOpConvInt64ToUint16: + case glslang::EOpConvInt64ToUint: + convOp = spv::OpSConvert; + type = builder.makeIntType(width); + break; + default: + convOp = spv::OpUConvert; + type = builder.makeUintType(width); + break; + } + + if (vectorSize > 0) + type = builder.makeVectorType(type, vectorSize); + + return builder.createUnaryOp(convOp, type, operand); +} + +spv::Id TGlslangToSpvTraverser::createConversion(glslang::TOperator op, OpDecorations& decorations, spv::Id destType, + spv::Id operand, glslang::TBasicType typeProxy) +{ + spv::Op convOp = spv::OpNop; + spv::Id zero = 0; + spv::Id one = 0; + + int vectorSize = builder.isVectorType(destType) ? builder.getNumTypeComponents(destType) : 0; + + switch (op) { + case glslang::EOpConvIntToBool: + case glslang::EOpConvUintToBool: + zero = builder.makeUintConstant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + case glslang::EOpConvFloatToBool: + zero = builder.makeFloatConstant(0.0F); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFUnordNotEqual, destType, operand, zero); + case glslang::EOpConvBoolToFloat: + convOp = spv::OpSelect; + zero = builder.makeFloatConstant(0.0F); + one = builder.makeFloatConstant(1.0F); + break; + + case glslang::EOpConvBoolToInt: + case glslang::EOpConvBoolToInt64: +#ifndef GLSLANG_WEB + if (op == glslang::EOpConvBoolToInt64) { + zero = builder.makeInt64Constant(0); + one = builder.makeInt64Constant(1); + } else +#endif + { + zero = builder.makeIntConstant(0); + one = builder.makeIntConstant(1); + } + + convOp = spv::OpSelect; + break; + + case glslang::EOpConvBoolToUint: + case glslang::EOpConvBoolToUint64: +#ifndef GLSLANG_WEB + if (op == glslang::EOpConvBoolToUint64) { + zero = builder.makeUint64Constant(0); + one = builder.makeUint64Constant(1); + } else +#endif + { + zero = builder.makeUintConstant(0); + one = builder.makeUintConstant(1); + } + + convOp = spv::OpSelect; + break; + + case glslang::EOpConvInt8ToFloat16: + case glslang::EOpConvInt8ToFloat: + case glslang::EOpConvInt8ToDouble: + case glslang::EOpConvInt16ToFloat16: + case glslang::EOpConvInt16ToFloat: + case glslang::EOpConvInt16ToDouble: + case glslang::EOpConvIntToFloat16: + case glslang::EOpConvIntToFloat: + case glslang::EOpConvIntToDouble: + case glslang::EOpConvInt64ToFloat: + case glslang::EOpConvInt64ToDouble: + case glslang::EOpConvInt64ToFloat16: + convOp = spv::OpConvertSToF; + break; + + case glslang::EOpConvUint8ToFloat16: + case glslang::EOpConvUint8ToFloat: + case glslang::EOpConvUint8ToDouble: + case glslang::EOpConvUint16ToFloat16: + case glslang::EOpConvUint16ToFloat: + case glslang::EOpConvUint16ToDouble: + case glslang::EOpConvUintToFloat16: + case glslang::EOpConvUintToFloat: + case glslang::EOpConvUintToDouble: + case glslang::EOpConvUint64ToFloat: + case glslang::EOpConvUint64ToDouble: + case glslang::EOpConvUint64ToFloat16: + convOp = spv::OpConvertUToF; + break; + + case glslang::EOpConvFloat16ToInt8: + case glslang::EOpConvFloatToInt8: + case glslang::EOpConvDoubleToInt8: + case glslang::EOpConvFloat16ToInt16: + case glslang::EOpConvFloatToInt16: + case glslang::EOpConvDoubleToInt16: + case glslang::EOpConvFloat16ToInt: + case glslang::EOpConvFloatToInt: + case glslang::EOpConvDoubleToInt: + case glslang::EOpConvFloat16ToInt64: + case glslang::EOpConvFloatToInt64: + case glslang::EOpConvDoubleToInt64: + convOp = spv::OpConvertFToS; + break; + + case glslang::EOpConvUint8ToInt8: + case glslang::EOpConvInt8ToUint8: + case glslang::EOpConvUint16ToInt16: + case glslang::EOpConvInt16ToUint16: + case glslang::EOpConvUintToInt: + case glslang::EOpConvIntToUint: + case glslang::EOpConvUint64ToInt64: + case glslang::EOpConvInt64ToUint64: + if (builder.isInSpecConstCodeGenMode()) { + // Build zero scalar or vector for OpIAdd. +#ifndef GLSLANG_WEB + if(op == glslang::EOpConvUint8ToInt8 || op == glslang::EOpConvInt8ToUint8) { + zero = builder.makeUint8Constant(0); + } else if (op == glslang::EOpConvUint16ToInt16 || op == glslang::EOpConvInt16ToUint16) { + zero = builder.makeUint16Constant(0); + } else if (op == glslang::EOpConvUint64ToInt64 || op == glslang::EOpConvInt64ToUint64) { + zero = builder.makeUint64Constant(0); + } else +#endif + { + zero = builder.makeUintConstant(0); + } + zero = makeSmearedConstant(zero, vectorSize); + // Use OpIAdd, instead of OpBitcast to do the conversion when + // generating for OpSpecConstantOp instruction. + return builder.createBinOp(spv::OpIAdd, destType, operand, zero); + } + // For normal run-time conversion instruction, use OpBitcast. + convOp = spv::OpBitcast; + break; + + case glslang::EOpConvFloat16ToUint8: + case glslang::EOpConvFloatToUint8: + case glslang::EOpConvDoubleToUint8: + case glslang::EOpConvFloat16ToUint16: + case glslang::EOpConvFloatToUint16: + case glslang::EOpConvDoubleToUint16: + case glslang::EOpConvFloat16ToUint: + case glslang::EOpConvFloatToUint: + case glslang::EOpConvDoubleToUint: + case glslang::EOpConvFloatToUint64: + case glslang::EOpConvDoubleToUint64: + case glslang::EOpConvFloat16ToUint64: + convOp = spv::OpConvertFToU; + break; + +#ifndef GLSLANG_WEB + case glslang::EOpConvInt8ToBool: + case glslang::EOpConvUint8ToBool: + zero = builder.makeUint8Constant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + case glslang::EOpConvInt16ToBool: + case glslang::EOpConvUint16ToBool: + zero = builder.makeUint16Constant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + case glslang::EOpConvInt64ToBool: + case glslang::EOpConvUint64ToBool: + zero = builder.makeUint64Constant(0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpINotEqual, destType, operand, zero); + case glslang::EOpConvDoubleToBool: + zero = builder.makeDoubleConstant(0.0); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFUnordNotEqual, destType, operand, zero); + case glslang::EOpConvFloat16ToBool: + zero = builder.makeFloat16Constant(0.0F); + zero = makeSmearedConstant(zero, vectorSize); + return builder.createBinOp(spv::OpFUnordNotEqual, destType, operand, zero); + case glslang::EOpConvBoolToDouble: + convOp = spv::OpSelect; + zero = builder.makeDoubleConstant(0.0); + one = builder.makeDoubleConstant(1.0); + break; + case glslang::EOpConvBoolToFloat16: + convOp = spv::OpSelect; + zero = builder.makeFloat16Constant(0.0F); + one = builder.makeFloat16Constant(1.0F); + break; + case glslang::EOpConvBoolToInt8: + zero = builder.makeInt8Constant(0); + one = builder.makeInt8Constant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvBoolToUint8: + zero = builder.makeUint8Constant(0); + one = builder.makeUint8Constant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvBoolToInt16: + zero = builder.makeInt16Constant(0); + one = builder.makeInt16Constant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvBoolToUint16: + zero = builder.makeUint16Constant(0); + one = builder.makeUint16Constant(1); + convOp = spv::OpSelect; + break; + case glslang::EOpConvDoubleToFloat: + case glslang::EOpConvFloatToDouble: + case glslang::EOpConvDoubleToFloat16: + case glslang::EOpConvFloat16ToDouble: + case glslang::EOpConvFloatToFloat16: + case glslang::EOpConvFloat16ToFloat: + convOp = spv::OpFConvert; + if (builder.isMatrixType(destType)) + return createUnaryMatrixOperation(convOp, decorations, destType, operand, typeProxy); + break; + + case glslang::EOpConvInt8ToInt16: + case glslang::EOpConvInt8ToInt: + case glslang::EOpConvInt8ToInt64: + case glslang::EOpConvInt16ToInt8: + case glslang::EOpConvInt16ToInt: + case glslang::EOpConvInt16ToInt64: + case glslang::EOpConvIntToInt8: + case glslang::EOpConvIntToInt16: + case glslang::EOpConvIntToInt64: + case glslang::EOpConvInt64ToInt8: + case glslang::EOpConvInt64ToInt16: + case glslang::EOpConvInt64ToInt: + convOp = spv::OpSConvert; + break; + + case glslang::EOpConvUint8ToUint16: + case glslang::EOpConvUint8ToUint: + case glslang::EOpConvUint8ToUint64: + case glslang::EOpConvUint16ToUint8: + case glslang::EOpConvUint16ToUint: + case glslang::EOpConvUint16ToUint64: + case glslang::EOpConvUintToUint8: + case glslang::EOpConvUintToUint16: + case glslang::EOpConvUintToUint64: + case glslang::EOpConvUint64ToUint8: + case glslang::EOpConvUint64ToUint16: + case glslang::EOpConvUint64ToUint: + convOp = spv::OpUConvert; + break; + + case glslang::EOpConvInt8ToUint16: + case glslang::EOpConvInt8ToUint: + case glslang::EOpConvInt8ToUint64: + case glslang::EOpConvInt16ToUint8: + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvIntToUint8: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvIntToUint64: + case glslang::EOpConvInt64ToUint8: + case glslang::EOpConvInt64ToUint16: + case glslang::EOpConvInt64ToUint: + case glslang::EOpConvUint8ToInt16: + case glslang::EOpConvUint8ToInt: + case glslang::EOpConvUint8ToInt64: + case glslang::EOpConvUint16ToInt8: + case glslang::EOpConvUint16ToInt: + case glslang::EOpConvUint16ToInt64: + case glslang::EOpConvUintToInt8: + case glslang::EOpConvUintToInt16: + case glslang::EOpConvUintToInt64: + case glslang::EOpConvUint64ToInt8: + case glslang::EOpConvUint64ToInt16: + case glslang::EOpConvUint64ToInt: + // OpSConvert/OpUConvert + OpBitCast + operand = createIntWidthConversion(op, operand, vectorSize); + + if (builder.isInSpecConstCodeGenMode()) { + // Build zero scalar or vector for OpIAdd. + switch(op) { + case glslang::EOpConvInt16ToUint8: + case glslang::EOpConvIntToUint8: + case glslang::EOpConvInt64ToUint8: + case glslang::EOpConvUint16ToInt8: + case glslang::EOpConvUintToInt8: + case glslang::EOpConvUint64ToInt8: + zero = builder.makeUint8Constant(0); + break; + case glslang::EOpConvInt8ToUint16: + case glslang::EOpConvIntToUint16: + case glslang::EOpConvInt64ToUint16: + case glslang::EOpConvUint8ToInt16: + case glslang::EOpConvUintToInt16: + case glslang::EOpConvUint64ToInt16: + zero = builder.makeUint16Constant(0); + break; + case glslang::EOpConvInt8ToUint: + case glslang::EOpConvInt16ToUint: + case glslang::EOpConvInt64ToUint: + case glslang::EOpConvUint8ToInt: + case glslang::EOpConvUint16ToInt: + case glslang::EOpConvUint64ToInt: + zero = builder.makeUintConstant(0); + break; + case glslang::EOpConvInt8ToUint64: + case glslang::EOpConvInt16ToUint64: + case glslang::EOpConvIntToUint64: + case glslang::EOpConvUint8ToInt64: + case glslang::EOpConvUint16ToInt64: + case glslang::EOpConvUintToInt64: + zero = builder.makeUint64Constant(0); + break; + default: + assert(false && "Default missing"); + break; + } + zero = makeSmearedConstant(zero, vectorSize); + // Use OpIAdd, instead of OpBitcast to do the conversion when + // generating for OpSpecConstantOp instruction. + return builder.createBinOp(spv::OpIAdd, destType, operand, zero); + } + // For normal run-time conversion instruction, use OpBitcast. + convOp = spv::OpBitcast; + break; + case glslang::EOpConvUint64ToPtr: + convOp = spv::OpConvertUToPtr; + break; + case glslang::EOpConvPtrToUint64: + convOp = spv::OpConvertPtrToU; + break; + case glslang::EOpConvPtrToUvec2: + case glslang::EOpConvUvec2ToPtr: + if (builder.isVector(operand)) + builder.promoteIncorporatedExtension(spv::E_SPV_EXT_physical_storage_buffer, + spv::E_SPV_KHR_physical_storage_buffer, spv::Spv_1_5); + convOp = spv::OpBitcast; + break; +#endif + + default: + break; + } + + spv::Id result = 0; + if (convOp == spv::OpNop) + return result; + + if (convOp == spv::OpSelect) { + zero = makeSmearedConstant(zero, vectorSize); + one = makeSmearedConstant(one, vectorSize); + result = builder.createTriOp(convOp, destType, operand, one, zero); + } else + result = builder.createUnaryOp(convOp, destType, operand); + + result = builder.setPrecision(result, decorations.precision); + decorations.addNonUniform(builder, result); + return result; +} + +spv::Id TGlslangToSpvTraverser::makeSmearedConstant(spv::Id constant, int vectorSize) +{ + if (vectorSize == 0) + return constant; + + spv::Id vectorTypeId = builder.makeVectorType(builder.getTypeId(constant), vectorSize); + std::vector components; + for (int c = 0; c < vectorSize; ++c) + components.push_back(constant); + return builder.makeCompositeConstant(vectorTypeId, components); +} + +// For glslang ops that map to SPV atomic opCodes +spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv::Decoration /*precision*/, + spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy, + const spv::Builder::AccessChain::CoherentFlags &lvalueCoherentFlags) +{ + spv::Op opCode = spv::OpNop; + + switch (op) { + case glslang::EOpAtomicAdd: + case glslang::EOpImageAtomicAdd: + case glslang::EOpAtomicCounterAdd: + opCode = spv::OpAtomicIAdd; + if (typeProxy == glslang::EbtFloat || typeProxy == glslang::EbtDouble) { + opCode = spv::OpAtomicFAddEXT; + builder.addExtension(spv::E_SPV_EXT_shader_atomic_float_add); + if (typeProxy == glslang::EbtFloat) + builder.addCapability(spv::CapabilityAtomicFloat32AddEXT); + else + builder.addCapability(spv::CapabilityAtomicFloat64AddEXT); + } + break; + case glslang::EOpAtomicCounterSubtract: + opCode = spv::OpAtomicISub; + break; + case glslang::EOpAtomicMin: + case glslang::EOpImageAtomicMin: + case glslang::EOpAtomicCounterMin: + opCode = (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) ? + spv::OpAtomicUMin : spv::OpAtomicSMin; + break; + case glslang::EOpAtomicMax: + case glslang::EOpImageAtomicMax: + case glslang::EOpAtomicCounterMax: + opCode = (typeProxy == glslang::EbtUint || typeProxy == glslang::EbtUint64) ? + spv::OpAtomicUMax : spv::OpAtomicSMax; + break; + case glslang::EOpAtomicAnd: + case glslang::EOpImageAtomicAnd: + case glslang::EOpAtomicCounterAnd: + opCode = spv::OpAtomicAnd; + break; + case glslang::EOpAtomicOr: + case glslang::EOpImageAtomicOr: + case glslang::EOpAtomicCounterOr: + opCode = spv::OpAtomicOr; + break; + case glslang::EOpAtomicXor: + case glslang::EOpImageAtomicXor: + case glslang::EOpAtomicCounterXor: + opCode = spv::OpAtomicXor; + break; + case glslang::EOpAtomicExchange: + case glslang::EOpImageAtomicExchange: + case glslang::EOpAtomicCounterExchange: + opCode = spv::OpAtomicExchange; + break; + case glslang::EOpAtomicCompSwap: + case glslang::EOpImageAtomicCompSwap: + case glslang::EOpAtomicCounterCompSwap: + opCode = spv::OpAtomicCompareExchange; + break; + case glslang::EOpAtomicCounterIncrement: + opCode = spv::OpAtomicIIncrement; + break; + case glslang::EOpAtomicCounterDecrement: + opCode = spv::OpAtomicIDecrement; + break; + case glslang::EOpAtomicCounter: + case glslang::EOpImageAtomicLoad: + case glslang::EOpAtomicLoad: + opCode = spv::OpAtomicLoad; + break; + case glslang::EOpAtomicStore: + case glslang::EOpImageAtomicStore: + opCode = spv::OpAtomicStore; + break; + default: + assert(0); + break; + } + + if (typeProxy == glslang::EbtInt64 || typeProxy == glslang::EbtUint64) + builder.addCapability(spv::CapabilityInt64Atomics); + + // Sort out the operands + // - mapping from glslang -> SPV + // - there are extra SPV operands that are optional in glslang + // - compare-exchange swaps the value and comparator + // - compare-exchange has an extra memory semantics + // - EOpAtomicCounterDecrement needs a post decrement + spv::Id pointerId = 0, compareId = 0, valueId = 0; + // scope defaults to Device in the old model, QueueFamilyKHR in the new model + spv::Id scopeId; + if (glslangIntermediate->usingVulkanMemoryModel()) { + scopeId = builder.makeUintConstant(spv::ScopeQueueFamilyKHR); + } else { + scopeId = builder.makeUintConstant(spv::ScopeDevice); + } + // semantics default to relaxed + spv::Id semanticsId = builder.makeUintConstant(lvalueCoherentFlags.isVolatile() && + glslangIntermediate->usingVulkanMemoryModel() ? + spv::MemorySemanticsVolatileMask : + spv::MemorySemanticsMaskNone); + spv::Id semanticsId2 = semanticsId; + + pointerId = operands[0]; + if (opCode == spv::OpAtomicIIncrement || opCode == spv::OpAtomicIDecrement) { + // no additional operands + } else if (opCode == spv::OpAtomicCompareExchange) { + compareId = operands[1]; + valueId = operands[2]; + if (operands.size() > 3) { + scopeId = operands[3]; + semanticsId = builder.makeUintConstant( + builder.getConstantScalar(operands[4]) | builder.getConstantScalar(operands[5])); + semanticsId2 = builder.makeUintConstant( + builder.getConstantScalar(operands[6]) | builder.getConstantScalar(operands[7])); + } + } else if (opCode == spv::OpAtomicLoad) { + if (operands.size() > 1) { + scopeId = operands[1]; + semanticsId = builder.makeUintConstant( + builder.getConstantScalar(operands[2]) | builder.getConstantScalar(operands[3])); + } + } else { + // atomic store or RMW + valueId = operands[1]; + if (operands.size() > 2) { + scopeId = operands[2]; + semanticsId = builder.makeUintConstant + (builder.getConstantScalar(operands[3]) | builder.getConstantScalar(operands[4])); + } + } + + // Check for capabilities + unsigned semanticsImmediate = builder.getConstantScalar(semanticsId) | builder.getConstantScalar(semanticsId2); + if (semanticsImmediate & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + + if (glslangIntermediate->usingVulkanMemoryModel() && builder.getConstantScalar(scopeId) == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + + std::vector spvAtomicOperands; // hold the spv operands + spvAtomicOperands.push_back(pointerId); + spvAtomicOperands.push_back(scopeId); + spvAtomicOperands.push_back(semanticsId); + if (opCode == spv::OpAtomicCompareExchange) { + spvAtomicOperands.push_back(semanticsId2); + spvAtomicOperands.push_back(valueId); + spvAtomicOperands.push_back(compareId); + } else if (opCode != spv::OpAtomicLoad && opCode != spv::OpAtomicIIncrement && opCode != spv::OpAtomicIDecrement) { + spvAtomicOperands.push_back(valueId); + } + + if (opCode == spv::OpAtomicStore) { + builder.createNoResultOp(opCode, spvAtomicOperands); + return 0; + } else { + spv::Id resultId = builder.createOp(opCode, typeId, spvAtomicOperands); + + // GLSL and HLSL atomic-counter decrement return post-decrement value, + // while SPIR-V returns pre-decrement value. Translate between these semantics. + if (op == glslang::EOpAtomicCounterDecrement) + resultId = builder.createBinOp(spv::OpISub, typeId, resultId, builder.makeIntConstant(1)); + + return resultId; + } +} + +// Create group invocation operations. +spv::Id TGlslangToSpvTraverser::createInvocationsOperation(glslang::TOperator op, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + spv::Op opCode = spv::OpNop; + std::vector spvGroupOperands; + spv::GroupOperation groupOperation = spv::GroupOperationMax; + + if (op == glslang::EOpBallot || op == glslang::EOpReadFirstInvocation || + op == glslang::EOpReadInvocation) { + builder.addExtension(spv::E_SPV_KHR_shader_ballot); + builder.addCapability(spv::CapabilitySubgroupBallotKHR); + } else if (op == glslang::EOpAnyInvocation || + op == glslang::EOpAllInvocations || + op == glslang::EOpAllInvocationsEqual) { + builder.addExtension(spv::E_SPV_KHR_subgroup_vote); + builder.addCapability(spv::CapabilitySubgroupVoteKHR); + } else { + builder.addCapability(spv::CapabilityGroups); + if (op == glslang::EOpMinInvocationsNonUniform || + op == glslang::EOpMaxInvocationsNonUniform || + op == glslang::EOpAddInvocationsNonUniform || + op == glslang::EOpMinInvocationsInclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsInclusiveScanNonUniform || + op == glslang::EOpAddInvocationsInclusiveScanNonUniform || + op == glslang::EOpMinInvocationsExclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsExclusiveScanNonUniform || + op == glslang::EOpAddInvocationsExclusiveScanNonUniform) + builder.addExtension(spv::E_SPV_AMD_shader_ballot); + + switch (op) { + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + groupOperation = spv::GroupOperationReduce; + break; + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + groupOperation = spv::GroupOperationInclusiveScan; + break; + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + groupOperation = spv::GroupOperationExclusiveScan; + break; + default: + break; + } + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + if (groupOperation != spv::GroupOperationMax) { + spv::IdImmediate groupOp = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOp); + } + } + + for (auto opIt = operands.begin(); opIt != operands.end(); ++opIt) { + spv::IdImmediate op = { true, *opIt }; + spvGroupOperands.push_back(op); + } + + switch (op) { + case glslang::EOpAnyInvocation: + opCode = spv::OpSubgroupAnyKHR; + break; + case glslang::EOpAllInvocations: + opCode = spv::OpSubgroupAllKHR; + break; + case glslang::EOpAllInvocationsEqual: + opCode = spv::OpSubgroupAllEqualKHR; + break; + case glslang::EOpReadInvocation: + opCode = spv::OpSubgroupReadInvocationKHR; + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + break; + case glslang::EOpReadFirstInvocation: + opCode = spv::OpSubgroupFirstInvocationKHR; + break; + case glslang::EOpBallot: + { + // NOTE: According to the spec, the result type of "OpSubgroupBallotKHR" must be a 4 component vector of 32 + // bit integer types. The GLSL built-in function "ballotARB()" assumes the maximum number of invocations in + // a subgroup is 64. Thus, we have to convert uvec4.xy to uint64_t as follow: + // + // result = Bitcast(SubgroupBallotKHR(Predicate).xy) + // + spv::Id uintType = builder.makeUintType(32); + spv::Id uvec4Type = builder.makeVectorType(uintType, 4); + spv::Id result = builder.createOp(spv::OpSubgroupBallotKHR, uvec4Type, spvGroupOperands); + + std::vector components; + components.push_back(builder.createCompositeExtract(result, uintType, 0)); + components.push_back(builder.createCompositeExtract(result, uintType, 1)); + + spv::Id uvec2Type = builder.makeVectorType(uintType, 2); + return builder.createUnaryOp(spv::OpBitcast, typeId, + builder.createCompositeConstruct(uvec2Type, components)); + } + + case glslang::EOpMinInvocations: + case glslang::EOpMaxInvocations: + case glslang::EOpAddInvocations: + case glslang::EOpMinInvocationsInclusiveScan: + case glslang::EOpMaxInvocationsInclusiveScan: + case glslang::EOpAddInvocationsInclusiveScan: + case glslang::EOpMinInvocationsExclusiveScan: + case glslang::EOpMaxInvocationsExclusiveScan: + case glslang::EOpAddInvocationsExclusiveScan: + if (op == glslang::EOpMinInvocations || + op == glslang::EOpMinInvocationsInclusiveScan || + op == glslang::EOpMinInvocationsExclusiveScan) { + if (isFloat) + opCode = spv::OpGroupFMin; + else { + if (isUnsigned) + opCode = spv::OpGroupUMin; + else + opCode = spv::OpGroupSMin; + } + } else if (op == glslang::EOpMaxInvocations || + op == glslang::EOpMaxInvocationsInclusiveScan || + op == glslang::EOpMaxInvocationsExclusiveScan) { + if (isFloat) + opCode = spv::OpGroupFMax; + else { + if (isUnsigned) + opCode = spv::OpGroupUMax; + else + opCode = spv::OpGroupSMax; + } + } else { + if (isFloat) + opCode = spv::OpGroupFAdd; + else + opCode = spv::OpGroupIAdd; + } + + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + + break; + case glslang::EOpMinInvocationsNonUniform: + case glslang::EOpMaxInvocationsNonUniform: + case glslang::EOpAddInvocationsNonUniform: + case glslang::EOpMinInvocationsInclusiveScanNonUniform: + case glslang::EOpMaxInvocationsInclusiveScanNonUniform: + case glslang::EOpAddInvocationsInclusiveScanNonUniform: + case glslang::EOpMinInvocationsExclusiveScanNonUniform: + case glslang::EOpMaxInvocationsExclusiveScanNonUniform: + case glslang::EOpAddInvocationsExclusiveScanNonUniform: + if (op == glslang::EOpMinInvocationsNonUniform || + op == glslang::EOpMinInvocationsInclusiveScanNonUniform || + op == glslang::EOpMinInvocationsExclusiveScanNonUniform) { + if (isFloat) + opCode = spv::OpGroupFMinNonUniformAMD; + else { + if (isUnsigned) + opCode = spv::OpGroupUMinNonUniformAMD; + else + opCode = spv::OpGroupSMinNonUniformAMD; + } + } + else if (op == glslang::EOpMaxInvocationsNonUniform || + op == glslang::EOpMaxInvocationsInclusiveScanNonUniform || + op == glslang::EOpMaxInvocationsExclusiveScanNonUniform) { + if (isFloat) + opCode = spv::OpGroupFMaxNonUniformAMD; + else { + if (isUnsigned) + opCode = spv::OpGroupUMaxNonUniformAMD; + else + opCode = spv::OpGroupSMaxNonUniformAMD; + } + } + else { + if (isFloat) + opCode = spv::OpGroupFAddNonUniformAMD; + else + opCode = spv::OpGroupIAddNonUniformAMD; + } + + if (builder.isVectorType(typeId)) + return CreateInvocationsVectorOperation(opCode, groupOperation, typeId, operands); + + break; + default: + logger->missingFunctionality("invocation operation"); + return spv::NoResult; + } + + assert(opCode != spv::OpNop); + return builder.createOp(opCode, typeId, spvGroupOperands); +} + +// Create group invocation operations on a vector +spv::Id TGlslangToSpvTraverser::CreateInvocationsVectorOperation(spv::Op op, spv::GroupOperation groupOperation, + spv::Id typeId, std::vector& operands) +{ + assert(op == spv::OpGroupFMin || op == spv::OpGroupUMin || op == spv::OpGroupSMin || + op == spv::OpGroupFMax || op == spv::OpGroupUMax || op == spv::OpGroupSMax || + op == spv::OpGroupFAdd || op == spv::OpGroupIAdd || op == spv::OpGroupBroadcast || + op == spv::OpSubgroupReadInvocationKHR || + op == spv::OpGroupFMinNonUniformAMD || op == spv::OpGroupUMinNonUniformAMD || + op == spv::OpGroupSMinNonUniformAMD || + op == spv::OpGroupFMaxNonUniformAMD || op == spv::OpGroupUMaxNonUniformAMD || + op == spv::OpGroupSMaxNonUniformAMD || + op == spv::OpGroupFAddNonUniformAMD || op == spv::OpGroupIAddNonUniformAMD); + + // Handle group invocation operations scalar by scalar. + // The result type is the same type as the original type. + // The algorithm is to: + // - break the vector into scalars + // - apply the operation to each scalar + // - make a vector out the scalar results + + // get the types sorted out + int numComponents = builder.getNumComponents(operands[0]); + spv::Id scalarType = builder.getScalarTypeId(builder.getTypeId(operands[0])); + std::vector results; + + // do each scalar op + for (int comp = 0; comp < numComponents; ++comp) { + std::vector indexes; + indexes.push_back(comp); + spv::IdImmediate scalar = { true, builder.createCompositeExtract(operands[0], scalarType, indexes) }; + std::vector spvGroupOperands; + if (op == spv::OpSubgroupReadInvocationKHR) { + spvGroupOperands.push_back(scalar); + spv::IdImmediate operand = { true, operands[1] }; + spvGroupOperands.push_back(operand); + } else if (op == spv::OpGroupBroadcast) { + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + spvGroupOperands.push_back(scalar); + spv::IdImmediate operand = { true, operands[1] }; + spvGroupOperands.push_back(operand); + } else { + spv::IdImmediate scope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(scope); + spv::IdImmediate groupOp = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOp); + spvGroupOperands.push_back(scalar); + } + + results.push_back(builder.createOp(op, scalarType, spvGroupOperands)); + } + + // put the pieces together + return builder.createCompositeConstruct(typeId, results); +} + +// Create subgroup invocation operations. +spv::Id TGlslangToSpvTraverser::createSubgroupOperation(glslang::TOperator op, spv::Id typeId, + std::vector& operands, glslang::TBasicType typeProxy) +{ + // Add the required capabilities. + switch (op) { + case glslang::EOpSubgroupElect: + builder.addCapability(spv::CapabilityGroupNonUniform); + break; + case glslang::EOpSubgroupAll: + case glslang::EOpSubgroupAny: + case glslang::EOpSubgroupAllEqual: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformVote); + break; + case glslang::EOpSubgroupBroadcast: + case glslang::EOpSubgroupBroadcastFirst: + case glslang::EOpSubgroupBallot: + case glslang::EOpSubgroupInverseBallot: + case glslang::EOpSubgroupBallotBitExtract: + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupBallotFindLSB: + case glslang::EOpSubgroupBallotFindMSB: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformBallot); + break; + case glslang::EOpSubgroupShuffle: + case glslang::EOpSubgroupShuffleXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformShuffle); + break; + case glslang::EOpSubgroupShuffleUp: + case glslang::EOpSubgroupShuffleDown: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformShuffleRelative); + break; + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformArithmetic); + break; + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformClustered); + break; + case glslang::EOpSubgroupQuadBroadcast: + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: + builder.addCapability(spv::CapabilityGroupNonUniform); + builder.addCapability(spv::CapabilityGroupNonUniformQuad); + break; + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + builder.addExtension(spv::E_SPV_NV_shader_subgroup_partitioned); + builder.addCapability(spv::CapabilityGroupNonUniformPartitionedNV); + break; + default: assert(0 && "Unhandled subgroup operation!"); + } + + + const bool isUnsigned = isTypeUnsignedInt(typeProxy); + const bool isFloat = isTypeFloat(typeProxy); + const bool isBool = typeProxy == glslang::EbtBool; + + spv::Op opCode = spv::OpNop; + + // Figure out which opcode to use. + switch (op) { + case glslang::EOpSubgroupElect: opCode = spv::OpGroupNonUniformElect; break; + case glslang::EOpSubgroupAll: opCode = spv::OpGroupNonUniformAll; break; + case glslang::EOpSubgroupAny: opCode = spv::OpGroupNonUniformAny; break; + case glslang::EOpSubgroupAllEqual: opCode = spv::OpGroupNonUniformAllEqual; break; + case glslang::EOpSubgroupBroadcast: opCode = spv::OpGroupNonUniformBroadcast; break; + case glslang::EOpSubgroupBroadcastFirst: opCode = spv::OpGroupNonUniformBroadcastFirst; break; + case glslang::EOpSubgroupBallot: opCode = spv::OpGroupNonUniformBallot; break; + case glslang::EOpSubgroupInverseBallot: opCode = spv::OpGroupNonUniformInverseBallot; break; + case glslang::EOpSubgroupBallotBitExtract: opCode = spv::OpGroupNonUniformBallotBitExtract; break; + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupBallotExclusiveBitCount: opCode = spv::OpGroupNonUniformBallotBitCount; break; + case glslang::EOpSubgroupBallotFindLSB: opCode = spv::OpGroupNonUniformBallotFindLSB; break; + case glslang::EOpSubgroupBallotFindMSB: opCode = spv::OpGroupNonUniformBallotFindMSB; break; + case glslang::EOpSubgroupShuffle: opCode = spv::OpGroupNonUniformShuffle; break; + case glslang::EOpSubgroupShuffleXor: opCode = spv::OpGroupNonUniformShuffleXor; break; + case glslang::EOpSubgroupShuffleUp: opCode = spv::OpGroupNonUniformShuffleUp; break; + case glslang::EOpSubgroupShuffleDown: opCode = spv::OpGroupNonUniformShuffleDown; break; + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + if (isFloat) { + opCode = spv::OpGroupNonUniformFAdd; + } else { + opCode = spv::OpGroupNonUniformIAdd; + } + break; + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMul: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMul; + } else { + opCode = spv::OpGroupNonUniformIMul; + } + break; + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMin: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMin; + } else if (isUnsigned) { + opCode = spv::OpGroupNonUniformUMin; + } else { + opCode = spv::OpGroupNonUniformSMin; + } + break; + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveMax: + if (isFloat) { + opCode = spv::OpGroupNonUniformFMax; + } else if (isUnsigned) { + opCode = spv::OpGroupNonUniformUMax; + } else { + opCode = spv::OpGroupNonUniformSMax; + } + break; + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalAnd; + } else { + opCode = spv::OpGroupNonUniformBitwiseAnd; + } + break; + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveOr: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalOr; + } else { + opCode = spv::OpGroupNonUniformBitwiseOr; + } + break; + case glslang::EOpSubgroupXor: + case glslang::EOpSubgroupInclusiveXor: + case glslang::EOpSubgroupExclusiveXor: + case glslang::EOpSubgroupClusteredXor: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveXor: + if (isBool) { + opCode = spv::OpGroupNonUniformLogicalXor; + } else { + opCode = spv::OpGroupNonUniformBitwiseXor; + } + break; + case glslang::EOpSubgroupQuadBroadcast: opCode = spv::OpGroupNonUniformQuadBroadcast; break; + case glslang::EOpSubgroupQuadSwapHorizontal: + case glslang::EOpSubgroupQuadSwapVertical: + case glslang::EOpSubgroupQuadSwapDiagonal: opCode = spv::OpGroupNonUniformQuadSwap; break; + default: assert(0 && "Unhandled subgroup operation!"); + } + + // get the right Group Operation + spv::GroupOperation groupOperation = spv::GroupOperationMax; + switch (op) { + default: + break; + case glslang::EOpSubgroupBallotBitCount: + case glslang::EOpSubgroupAdd: + case glslang::EOpSubgroupMul: + case glslang::EOpSubgroupMin: + case glslang::EOpSubgroupMax: + case glslang::EOpSubgroupAnd: + case glslang::EOpSubgroupOr: + case glslang::EOpSubgroupXor: + groupOperation = spv::GroupOperationReduce; + break; + case glslang::EOpSubgroupBallotInclusiveBitCount: + case glslang::EOpSubgroupInclusiveAdd: + case glslang::EOpSubgroupInclusiveMul: + case glslang::EOpSubgroupInclusiveMin: + case glslang::EOpSubgroupInclusiveMax: + case glslang::EOpSubgroupInclusiveAnd: + case glslang::EOpSubgroupInclusiveOr: + case glslang::EOpSubgroupInclusiveXor: + groupOperation = spv::GroupOperationInclusiveScan; + break; + case glslang::EOpSubgroupBallotExclusiveBitCount: + case glslang::EOpSubgroupExclusiveAdd: + case glslang::EOpSubgroupExclusiveMul: + case glslang::EOpSubgroupExclusiveMin: + case glslang::EOpSubgroupExclusiveMax: + case glslang::EOpSubgroupExclusiveAnd: + case glslang::EOpSubgroupExclusiveOr: + case glslang::EOpSubgroupExclusiveXor: + groupOperation = spv::GroupOperationExclusiveScan; + break; + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + groupOperation = spv::GroupOperationClusteredReduce; + break; + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + groupOperation = spv::GroupOperationPartitionedReduceNV; + break; + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + groupOperation = spv::GroupOperationPartitionedInclusiveScanNV; + break; + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + groupOperation = spv::GroupOperationPartitionedExclusiveScanNV; + break; + } + + // build the instruction + std::vector spvGroupOperands; + + // Every operation begins with the Execution Scope operand. + spv::IdImmediate executionScope = { true, builder.makeUintConstant(spv::ScopeSubgroup) }; + spvGroupOperands.push_back(executionScope); + + // Next, for all operations that use a Group Operation, push that as an operand. + if (groupOperation != spv::GroupOperationMax) { + spv::IdImmediate groupOperand = { false, (unsigned)groupOperation }; + spvGroupOperands.push_back(groupOperand); + } + + // Push back the operands next. + for (auto opIt = operands.cbegin(); opIt != operands.cend(); ++opIt) { + spv::IdImmediate operand = { true, *opIt }; + spvGroupOperands.push_back(operand); + } + + // Some opcodes have additional operands. + spv::Id directionId = spv::NoResult; + switch (op) { + default: break; + case glslang::EOpSubgroupQuadSwapHorizontal: directionId = builder.makeUintConstant(0); break; + case glslang::EOpSubgroupQuadSwapVertical: directionId = builder.makeUintConstant(1); break; + case glslang::EOpSubgroupQuadSwapDiagonal: directionId = builder.makeUintConstant(2); break; + } + if (directionId != spv::NoResult) { + spv::IdImmediate direction = { true, directionId }; + spvGroupOperands.push_back(direction); + } + + return builder.createOp(opCode, typeId, spvGroupOperands); +} + +spv::Id TGlslangToSpvTraverser::createMiscOperation(glslang::TOperator op, spv::Decoration precision, + spv::Id typeId, std::vector& operands, glslang::TBasicType typeProxy) +{ + bool isUnsigned = isTypeUnsignedInt(typeProxy); + bool isFloat = isTypeFloat(typeProxy); + + spv::Op opCode = spv::OpNop; + int extBuiltins = -1; + int libCall = -1; + size_t consumedOperands = operands.size(); + spv::Id typeId0 = 0; + if (consumedOperands > 0) + typeId0 = builder.getTypeId(operands[0]); + spv::Id typeId1 = 0; + if (consumedOperands > 1) + typeId1 = builder.getTypeId(operands[1]); + spv::Id frexpIntType = 0; + + switch (op) { + case glslang::EOpMin: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NMin : spv::GLSLstd450FMin; + else if (isUnsigned) + libCall = spv::GLSLstd450UMin; + else + libCall = spv::GLSLstd450SMin; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpModf: + libCall = spv::GLSLstd450Modf; + break; + case glslang::EOpMax: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NMax : spv::GLSLstd450FMax; + else if (isUnsigned) + libCall = spv::GLSLstd450UMax; + else + libCall = spv::GLSLstd450SMax; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpPow: + libCall = spv::GLSLstd450Pow; + break; + case glslang::EOpDot: + opCode = spv::OpDot; + break; + case glslang::EOpAtan: + libCall = spv::GLSLstd450Atan2; + break; + + case glslang::EOpClamp: + if (isFloat) + libCall = nanMinMaxClamp ? spv::GLSLstd450NClamp : spv::GLSLstd450FClamp; + else if (isUnsigned) + libCall = spv::GLSLstd450UClamp; + else + libCall = spv::GLSLstd450SClamp; + builder.promoteScalar(precision, operands.front(), operands[1]); + builder.promoteScalar(precision, operands.front(), operands[2]); + break; + case glslang::EOpMix: + if (! builder.isBoolType(builder.getScalarTypeId(builder.getTypeId(operands.back())))) { + assert(isFloat); + libCall = spv::GLSLstd450FMix; + } else { + opCode = spv::OpSelect; + std::swap(operands.front(), operands.back()); + } + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpStep: + libCall = spv::GLSLstd450Step; + builder.promoteScalar(precision, operands.front(), operands.back()); + break; + case glslang::EOpSmoothStep: + libCall = spv::GLSLstd450SmoothStep; + builder.promoteScalar(precision, operands[0], operands[2]); + builder.promoteScalar(precision, operands[1], operands[2]); + break; + + case glslang::EOpDistance: + libCall = spv::GLSLstd450Distance; + break; + case glslang::EOpCross: + libCall = spv::GLSLstd450Cross; + break; + case glslang::EOpFaceForward: + libCall = spv::GLSLstd450FaceForward; + break; + case glslang::EOpReflect: + libCall = spv::GLSLstd450Reflect; + break; + case glslang::EOpRefract: + libCall = spv::GLSLstd450Refract; + break; + case glslang::EOpBarrier: + { + // This is for the extended controlBarrier function, with four operands. + // The unextended barrier() goes through createNoArgOperation. + assert(operands.size() == 4); + unsigned int executionScope = builder.getConstantScalar(operands[0]); + unsigned int memoryScope = builder.getConstantScalar(operands[1]); + unsigned int semantics = builder.getConstantScalar(operands[2]) | builder.getConstantScalar(operands[3]); + builder.createControlBarrier((spv::Scope)executionScope, (spv::Scope)memoryScope, + (spv::MemorySemanticsMask)semantics); + if (semantics & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + if (glslangIntermediate->usingVulkanMemoryModel() && (executionScope == spv::ScopeDevice || + memoryScope == spv::ScopeDevice)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + return 0; + } + break; + case glslang::EOpMemoryBarrier: + { + // This is for the extended memoryBarrier function, with three operands. + // The unextended memoryBarrier() goes through createNoArgOperation. + assert(operands.size() == 3); + unsigned int memoryScope = builder.getConstantScalar(operands[0]); + unsigned int semantics = builder.getConstantScalar(operands[1]) | builder.getConstantScalar(operands[2]); + builder.createMemoryBarrier((spv::Scope)memoryScope, (spv::MemorySemanticsMask)semantics); + if (semantics & (spv::MemorySemanticsMakeAvailableKHRMask | + spv::MemorySemanticsMakeVisibleKHRMask | + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsVolatileMask)) { + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } + if (glslangIntermediate->usingVulkanMemoryModel() && memoryScope == spv::ScopeDevice) { + builder.addCapability(spv::CapabilityVulkanMemoryModelDeviceScopeKHR); + } + return 0; + } + break; + +#ifndef GLSLANG_WEB + case glslang::EOpInterpolateAtSample: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtSample; + break; + case glslang::EOpInterpolateAtOffset: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + libCall = spv::GLSLstd450InterpolateAtOffset; + break; + case glslang::EOpAddCarry: + opCode = spv::OpIAddCarry; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpSubBorrow: + opCode = spv::OpISubBorrow; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpUMulExtended: + opCode = spv::OpUMulExtended; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpIMulExtended: + opCode = spv::OpSMulExtended; + typeId = builder.makeStructResultType(typeId0, typeId0); + consumedOperands = 2; + break; + case glslang::EOpBitfieldExtract: + if (isUnsigned) + opCode = spv::OpBitFieldUExtract; + else + opCode = spv::OpBitFieldSExtract; + break; + case glslang::EOpBitfieldInsert: + opCode = spv::OpBitFieldInsert; + break; + + case glslang::EOpFma: + libCall = spv::GLSLstd450Fma; + break; + case glslang::EOpFrexp: + { + libCall = spv::GLSLstd450FrexpStruct; + assert(builder.isPointerType(typeId1)); + typeId1 = builder.getContainedTypeId(typeId1); + int width = builder.getScalarTypeWidth(typeId1); + if (width == 16) + // Using 16-bit exp operand, enable extension SPV_AMD_gpu_shader_int16 + builder.addExtension(spv::E_SPV_AMD_gpu_shader_int16); + if (builder.getNumComponents(operands[0]) == 1) + frexpIntType = builder.makeIntegerType(width, true); + else + frexpIntType = builder.makeVectorType(builder.makeIntegerType(width, true), + builder.getNumComponents(operands[0])); + typeId = builder.makeStructResultType(typeId0, frexpIntType); + consumedOperands = 1; + } + break; + case glslang::EOpLdexp: + libCall = spv::GLSLstd450Ldexp; + break; + + case glslang::EOpReadInvocation: + return createInvocationsOperation(op, typeId, operands, typeProxy); + + case glslang::EOpSubgroupBroadcast: + case glslang::EOpSubgroupBallotBitExtract: + case glslang::EOpSubgroupShuffle: + case glslang::EOpSubgroupShuffleXor: + case glslang::EOpSubgroupShuffleUp: + case glslang::EOpSubgroupShuffleDown: + case glslang::EOpSubgroupClusteredAdd: + case glslang::EOpSubgroupClusteredMul: + case glslang::EOpSubgroupClusteredMin: + case glslang::EOpSubgroupClusteredMax: + case glslang::EOpSubgroupClusteredAnd: + case glslang::EOpSubgroupClusteredOr: + case glslang::EOpSubgroupClusteredXor: + case glslang::EOpSubgroupQuadBroadcast: + case glslang::EOpSubgroupPartitionedAdd: + case glslang::EOpSubgroupPartitionedMul: + case glslang::EOpSubgroupPartitionedMin: + case glslang::EOpSubgroupPartitionedMax: + case glslang::EOpSubgroupPartitionedAnd: + case glslang::EOpSubgroupPartitionedOr: + case glslang::EOpSubgroupPartitionedXor: + case glslang::EOpSubgroupPartitionedInclusiveAdd: + case glslang::EOpSubgroupPartitionedInclusiveMul: + case glslang::EOpSubgroupPartitionedInclusiveMin: + case glslang::EOpSubgroupPartitionedInclusiveMax: + case glslang::EOpSubgroupPartitionedInclusiveAnd: + case glslang::EOpSubgroupPartitionedInclusiveOr: + case glslang::EOpSubgroupPartitionedInclusiveXor: + case glslang::EOpSubgroupPartitionedExclusiveAdd: + case glslang::EOpSubgroupPartitionedExclusiveMul: + case glslang::EOpSubgroupPartitionedExclusiveMin: + case glslang::EOpSubgroupPartitionedExclusiveMax: + case glslang::EOpSubgroupPartitionedExclusiveAnd: + case glslang::EOpSubgroupPartitionedExclusiveOr: + case glslang::EOpSubgroupPartitionedExclusiveXor: + return createSubgroupOperation(op, typeId, operands, typeProxy); + + case glslang::EOpSwizzleInvocations: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::SwizzleInvocationsAMD; + break; + case glslang::EOpSwizzleInvocationsMasked: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::SwizzleInvocationsMaskedAMD; + break; + case glslang::EOpWriteInvocation: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_ballot); + libCall = spv::WriteInvocationAMD; + break; + + case glslang::EOpMin3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMin3AMD; + else { + if (isUnsigned) + libCall = spv::UMin3AMD; + else + libCall = spv::SMin3AMD; + } + break; + case glslang::EOpMax3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMax3AMD; + else { + if (isUnsigned) + libCall = spv::UMax3AMD; + else + libCall = spv::SMax3AMD; + } + break; + case glslang::EOpMid3: + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_trinary_minmax); + if (isFloat) + libCall = spv::FMid3AMD; + else { + if (isUnsigned) + libCall = spv::UMid3AMD; + else + libCall = spv::SMid3AMD; + } + break; + + case glslang::EOpInterpolateAtVertex: + if (typeProxy == glslang::EbtFloat16) + builder.addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + extBuiltins = getExtBuiltins(spv::E_SPV_AMD_shader_explicit_vertex_parameter); + libCall = spv::InterpolateAtVertexAMD; + break; + + case glslang::EOpReportIntersection: + typeId = builder.makeBoolType(); + opCode = spv::OpReportIntersectionKHR; + break; + case glslang::EOpTrace: + builder.createNoResultOp(spv::OpTraceRayKHR, operands); + return 0; + case glslang::EOpExecuteCallable: + builder.createNoResultOp(spv::OpExecuteCallableKHR, operands); + return 0; + + case glslang::EOpRayQueryInitialize: + builder.createNoResultOp(spv::OpRayQueryInitializeKHR, operands); + return 0; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR, operands); + return 0; + case glslang::EOpRayQueryGenerateIntersection: + builder.createNoResultOp(spv::OpRayQueryGenerateIntersectionKHR, operands); + return 0; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR, operands); + return 0; + case glslang::EOpRayQueryProceed: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryProceedKHR; + break; + case glslang::EOpRayQueryGetIntersectionType: + typeId = builder.makeUintType(32); + opCode = spv::OpRayQueryGetIntersectionTypeKHR; + break; + case glslang::EOpRayQueryGetRayTMin: + typeId = builder.makeFloatType(32); + opCode = spv::OpRayQueryGetRayTMinKHR; + break; + case glslang::EOpRayQueryGetRayFlags: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetRayFlagsKHR; + break; + case glslang::EOpRayQueryGetIntersectionT: + typeId = builder.makeFloatType(32); + opCode = spv::OpRayQueryGetIntersectionTKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceCustomIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceCustomIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceId: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceIdKHR; + break; + case glslang::EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR; + break; + case glslang::EOpRayQueryGetIntersectionGeometryIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionGeometryIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionPrimitiveIndex: + typeId = builder.makeIntType(32); + opCode = spv::OpRayQueryGetIntersectionPrimitiveIndexKHR; + break; + case glslang::EOpRayQueryGetIntersectionBarycentrics: + typeId = builder.makeVectorType(builder.makeFloatType(32), 2); + opCode = spv::OpRayQueryGetIntersectionBarycentricsKHR; + break; + case glslang::EOpRayQueryGetIntersectionFrontFace: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryGetIntersectionFrontFaceKHR; + break; + case glslang::EOpRayQueryGetIntersectionCandidateAABBOpaque: + typeId = builder.makeBoolType(); + opCode = spv::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectRayDirection: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetIntersectionObjectRayDirectionKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectRayOrigin: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetIntersectionObjectRayOriginKHR; + break; + case glslang::EOpRayQueryGetWorldRayDirection: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetWorldRayDirectionKHR; + break; + case glslang::EOpRayQueryGetWorldRayOrigin: + typeId = builder.makeVectorType(builder.makeFloatType(32), 3); + opCode = spv::OpRayQueryGetWorldRayOriginKHR; + break; + case glslang::EOpRayQueryGetIntersectionObjectToWorld: + typeId = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + opCode = spv::OpRayQueryGetIntersectionObjectToWorldKHR; + break; + case glslang::EOpRayQueryGetIntersectionWorldToObject: + typeId = builder.makeMatrixType(builder.makeFloatType(32), 4, 3); + opCode = spv::OpRayQueryGetIntersectionWorldToObjectKHR; + break; + case glslang::EOpWritePackedPrimitiveIndices4x8NV: + builder.createNoResultOp(spv::OpWritePackedPrimitiveIndices4x8NV, operands); + return 0; + case glslang::EOpCooperativeMatrixMulAdd: + opCode = spv::OpCooperativeMatrixMulAddNV; + break; +#endif // GLSLANG_WEB + default: + return 0; + } + + spv::Id id = 0; + if (libCall >= 0) { + // Use an extended instruction from the standard library. + // Construct the call arguments, without modifying the original operands vector. + // We might need the remaining arguments, e.g. in the EOpFrexp case. + std::vector callArguments(operands.begin(), operands.begin() + consumedOperands); + id = builder.createBuiltinCall(typeId, extBuiltins >= 0 ? extBuiltins : stdBuiltins, libCall, callArguments); + } else if (opCode == spv::OpDot && !isFloat) { + // int dot(int, int) + // NOTE: never called for scalar/vector1, this is turned into simple mul before this can be reached + const int componentCount = builder.getNumComponents(operands[0]); + spv::Id mulOp = builder.createBinOp(spv::OpIMul, builder.getTypeId(operands[0]), operands[0], operands[1]); + builder.setPrecision(mulOp, precision); + id = builder.createCompositeExtract(mulOp, typeId, 0); + for (int i = 1; i < componentCount; ++i) { + builder.setPrecision(id, precision); + id = builder.createBinOp(spv::OpIAdd, typeId, id, builder.createCompositeExtract(mulOp, typeId, i)); + } + } else { + switch (consumedOperands) { + case 0: + // should all be handled by visitAggregate and createNoArgOperation + assert(0); + return 0; + case 1: + // should all be handled by createUnaryOperation + assert(0); + return 0; + case 2: + id = builder.createBinOp(opCode, typeId, operands[0], operands[1]); + break; + default: + // anything 3 or over doesn't have l-value operands, so all should be consumed + assert(consumedOperands == operands.size()); + id = builder.createOp(opCode, typeId, operands); + break; + } + } + +#ifndef GLSLANG_WEB + // Decode the return types that were structures + switch (op) { + case glslang::EOpAddCarry: + case glslang::EOpSubBorrow: + builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]); + id = builder.createCompositeExtract(id, typeId0, 0); + break; + case glslang::EOpUMulExtended: + case glslang::EOpIMulExtended: + builder.createStore(builder.createCompositeExtract(id, typeId0, 0), operands[3]); + builder.createStore(builder.createCompositeExtract(id, typeId0, 1), operands[2]); + break; + case glslang::EOpFrexp: + { + assert(operands.size() == 2); + if (builder.isFloatType(builder.getScalarTypeId(typeId1))) { + // "exp" is floating-point type (from HLSL intrinsic) + spv::Id member1 = builder.createCompositeExtract(id, frexpIntType, 1); + member1 = builder.createUnaryOp(spv::OpConvertSToF, typeId1, member1); + builder.createStore(member1, operands[1]); + } else + // "exp" is integer type (from GLSL built-in function) + builder.createStore(builder.createCompositeExtract(id, frexpIntType, 1), operands[1]); + id = builder.createCompositeExtract(id, typeId0, 0); + } + break; + default: + break; + } +#endif + + return builder.setPrecision(id, precision); +} + +// Intrinsics with no arguments (or no return value, and no precision). +spv::Id TGlslangToSpvTraverser::createNoArgOperation(glslang::TOperator op, spv::Decoration precision, spv::Id typeId) +{ + // GLSL memory barriers use queuefamily scope in new model, device scope in old model + spv::Scope memoryBarrierScope = glslangIntermediate->usingVulkanMemoryModel() ? + spv::ScopeQueueFamilyKHR : spv::ScopeDevice; + + switch (op) { + case glslang::EOpBarrier: + if (glslangIntermediate->getStage() == EShLangTessControl) { + if (glslangIntermediate->usingVulkanMemoryModel()) { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsOutputMemoryKHRMask | + spv::MemorySemanticsAcquireReleaseMask); + builder.addCapability(spv::CapabilityVulkanMemoryModelKHR); + } else { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeInvocation, spv::MemorySemanticsMaskNone); + } + } else { + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + } + return 0; + case glslang::EOpMemoryBarrier: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierBuffer: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierShared: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpGroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; +#ifndef GLSLANG_WEB + case glslang::EOpMemoryBarrierAtomicCounter: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsAtomicCounterMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpMemoryBarrierImage: + builder.createMemoryBarrier(memoryBarrierScope, spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpAllMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, + spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpDeviceMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpDeviceMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeDevice, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpWorkgroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeWorkgroup, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpWorkgroupMemoryBarrierWithGroupSync: + builder.createControlBarrier(spv::ScopeWorkgroup, spv::ScopeWorkgroup, + spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return 0; + case glslang::EOpSubgroupBarrier: + builder.createControlBarrier(spv::ScopeSubgroup, spv::ScopeSubgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrier: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsAllMemory | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierBuffer: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsUniformMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierImage: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsImageMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + case glslang::EOpSubgroupMemoryBarrierShared: + builder.createMemoryBarrier(spv::ScopeSubgroup, spv::MemorySemanticsWorkgroupMemoryMask | + spv::MemorySemanticsAcquireReleaseMask); + return spv::NoResult; + + case glslang::EOpEmitVertex: + builder.createNoResultOp(spv::OpEmitVertex); + return 0; + case glslang::EOpEndPrimitive: + builder.createNoResultOp(spv::OpEndPrimitive); + return 0; + + case glslang::EOpSubgroupElect: { + std::vector operands; + return createSubgroupOperation(op, typeId, operands, glslang::EbtVoid); + } + case glslang::EOpTime: + { + std::vector args; // Dummy arguments + spv::Id id = builder.createBuiltinCall(typeId, getExtBuiltins(spv::E_SPV_AMD_gcn_shader), spv::TimeAMD, args); + return builder.setPrecision(id, precision); + } + case glslang::EOpIgnoreIntersection: + builder.createNoResultOp(spv::OpIgnoreIntersectionKHR); + return 0; + case glslang::EOpTerminateRay: + builder.createNoResultOp(spv::OpTerminateRayKHR); + return 0; + case glslang::EOpRayQueryInitialize: + builder.createNoResultOp(spv::OpRayQueryInitializeKHR); + return 0; + case glslang::EOpRayQueryTerminate: + builder.createNoResultOp(spv::OpRayQueryTerminateKHR); + return 0; + case glslang::EOpRayQueryGenerateIntersection: + builder.createNoResultOp(spv::OpRayQueryGenerateIntersectionKHR); + return 0; + case glslang::EOpRayQueryConfirmIntersection: + builder.createNoResultOp(spv::OpRayQueryConfirmIntersectionKHR); + return 0; + case glslang::EOpBeginInvocationInterlock: + builder.createNoResultOp(spv::OpBeginInvocationInterlockEXT); + return 0; + case glslang::EOpEndInvocationInterlock: + builder.createNoResultOp(spv::OpEndInvocationInterlockEXT); + return 0; + + case glslang::EOpIsHelperInvocation: + { + std::vector args; // Dummy arguments + builder.addExtension(spv::E_SPV_EXT_demote_to_helper_invocation); + builder.addCapability(spv::CapabilityDemoteToHelperInvocationEXT); + return builder.createOp(spv::OpIsHelperInvocationEXT, typeId, args); + } + + case glslang::EOpReadClockSubgroupKHR: { + std::vector args; + args.push_back(builder.makeUintConstant(spv::ScopeSubgroup)); + builder.addExtension(spv::E_SPV_KHR_shader_clock); + builder.addCapability(spv::CapabilityShaderClockKHR); + return builder.createOp(spv::OpReadClockKHR, typeId, args); + } + + case glslang::EOpReadClockDeviceKHR: { + std::vector args; + args.push_back(builder.makeUintConstant(spv::ScopeDevice)); + builder.addExtension(spv::E_SPV_KHR_shader_clock); + builder.addCapability(spv::CapabilityShaderClockKHR); + return builder.createOp(spv::OpReadClockKHR, typeId, args); + } +#endif + default: + break; + } + + logger->missingFunctionality("unknown operation with no arguments"); + + return 0; +} + +spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol) +{ + auto iter = symbolValues.find(symbol->getId()); + spv::Id id; + if (symbolValues.end() != iter) { + id = iter->second; + return id; + } + + // it was not found, create it + spv::BuiltIn builtIn = TranslateBuiltInDecoration(symbol->getQualifier().builtIn, false); + auto forcedType = getForcedType(symbol->getQualifier().builtIn, symbol->getType()); + id = createSpvVariable(symbol, forcedType.first); + symbolValues[symbol->getId()] = id; + if (forcedType.second != spv::NoType) + forceType[id] = forcedType.second; + + if (symbol->getBasicType() != glslang::EbtBlock) { + builder.addDecoration(id, TranslatePrecisionDecoration(symbol->getType())); + builder.addDecoration(id, TranslateInterpolationDecoration(symbol->getType().getQualifier())); + builder.addDecoration(id, TranslateAuxiliaryStorageDecoration(symbol->getType().getQualifier())); +#ifndef GLSLANG_WEB + addMeshNVDecoration(id, /*member*/ -1, symbol->getType().getQualifier()); + if (symbol->getQualifier().hasComponent()) + builder.addDecoration(id, spv::DecorationComponent, symbol->getQualifier().layoutComponent); + if (symbol->getQualifier().hasIndex()) + builder.addDecoration(id, spv::DecorationIndex, symbol->getQualifier().layoutIndex); +#endif + if (symbol->getType().getQualifier().hasSpecConstantId()) + builder.addDecoration(id, spv::DecorationSpecId, symbol->getType().getQualifier().layoutSpecConstantId); + // atomic counters use this: + if (symbol->getQualifier().hasOffset()) + builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutOffset); + } + + if (symbol->getQualifier().hasLocation()) + builder.addDecoration(id, spv::DecorationLocation, symbol->getQualifier().layoutLocation); + builder.addDecoration(id, TranslateInvariantDecoration(symbol->getType().getQualifier())); + if (symbol->getQualifier().hasStream() && glslangIntermediate->isMultiStream()) { + builder.addCapability(spv::CapabilityGeometryStreams); + builder.addDecoration(id, spv::DecorationStream, symbol->getQualifier().layoutStream); + } + if (symbol->getQualifier().hasSet()) + builder.addDecoration(id, spv::DecorationDescriptorSet, symbol->getQualifier().layoutSet); + else if (IsDescriptorResource(symbol->getType())) { + // default to 0 + builder.addDecoration(id, spv::DecorationDescriptorSet, 0); + } + if (symbol->getQualifier().hasBinding()) + builder.addDecoration(id, spv::DecorationBinding, symbol->getQualifier().layoutBinding); + else if (IsDescriptorResource(symbol->getType())) { + // default to 0 + builder.addDecoration(id, spv::DecorationBinding, 0); + } + if (symbol->getQualifier().hasAttachment()) + builder.addDecoration(id, spv::DecorationInputAttachmentIndex, symbol->getQualifier().layoutAttachment); + if (glslangIntermediate->getXfbMode()) { + builder.addCapability(spv::CapabilityTransformFeedback); + if (symbol->getQualifier().hasXfbBuffer()) { + builder.addDecoration(id, spv::DecorationXfbBuffer, symbol->getQualifier().layoutXfbBuffer); + unsigned stride = glslangIntermediate->getXfbStride(symbol->getQualifier().layoutXfbBuffer); + if (stride != glslang::TQualifier::layoutXfbStrideEnd) + builder.addDecoration(id, spv::DecorationXfbStride, stride); + } + if (symbol->getQualifier().hasXfbOffset()) + builder.addDecoration(id, spv::DecorationOffset, symbol->getQualifier().layoutXfbOffset); + } + + // add built-in variable decoration + if (builtIn != spv::BuiltInMax) { + builder.addDecoration(id, spv::DecorationBuiltIn, (int)builtIn); + } + +#ifndef GLSLANG_WEB + if (symbol->getType().isImage()) { + std::vector memory; + TranslateMemoryDecoration(symbol->getType().getQualifier(), memory, + glslangIntermediate->usingVulkanMemoryModel()); + for (unsigned int i = 0; i < memory.size(); ++i) + builder.addDecoration(id, memory[i]); + } + + // nonuniform + builder.addDecoration(id, TranslateNonUniformDecoration(symbol->getType().getQualifier())); + + if (builtIn == spv::BuiltInSampleMask) { + spv::Decoration decoration; + // GL_NV_sample_mask_override_coverage extension + if (glslangIntermediate->getLayoutOverrideCoverage()) + decoration = (spv::Decoration)spv::DecorationOverrideCoverageNV; + else + decoration = (spv::Decoration)spv::DecorationMax; + builder.addDecoration(id, decoration); + if (decoration != spv::DecorationMax) { + builder.addCapability(spv::CapabilitySampleMaskOverrideCoverageNV); + builder.addExtension(spv::E_SPV_NV_sample_mask_override_coverage); + } + } + else if (builtIn == spv::BuiltInLayer) { + // SPV_NV_viewport_array2 extension + if (symbol->getQualifier().layoutViewportRelative) { + builder.addDecoration(id, (spv::Decoration)spv::DecorationViewportRelativeNV); + builder.addCapability(spv::CapabilityShaderViewportMaskNV); + builder.addExtension(spv::E_SPV_NV_viewport_array2); + } + if (symbol->getQualifier().layoutSecondaryViewportRelativeOffset != -2048) { + builder.addDecoration(id, (spv::Decoration)spv::DecorationSecondaryViewportRelativeNV, + symbol->getQualifier().layoutSecondaryViewportRelativeOffset); + builder.addCapability(spv::CapabilityShaderStereoViewNV); + builder.addExtension(spv::E_SPV_NV_stereo_view_rendering); + } + } + + if (symbol->getQualifier().layoutPassthrough) { + builder.addDecoration(id, spv::DecorationPassthroughNV); + builder.addCapability(spv::CapabilityGeometryShaderPassthroughNV); + builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough); + } + if (symbol->getQualifier().pervertexNV) { + builder.addDecoration(id, spv::DecorationPerVertexNV); + builder.addCapability(spv::CapabilityFragmentBarycentricNV); + builder.addExtension(spv::E_SPV_NV_fragment_shader_barycentric); + } + + if (glslangIntermediate->getHlslFunctionality1() && symbol->getType().getQualifier().semanticName != nullptr) { + builder.addExtension("SPV_GOOGLE_hlsl_functionality1"); + builder.addDecoration(id, (spv::Decoration)spv::DecorationHlslSemanticGOOGLE, + symbol->getType().getQualifier().semanticName); + } + + if (symbol->isReference()) { + builder.addDecoration(id, symbol->getType().getQualifier().restrict ? + spv::DecorationRestrictPointerEXT : spv::DecorationAliasedPointerEXT); + } +#endif + + return id; +} + +#ifndef GLSLANG_WEB +// add per-primitive, per-view. per-task decorations to a struct member (member >= 0) or an object +void TGlslangToSpvTraverser::addMeshNVDecoration(spv::Id id, int member, const glslang::TQualifier& qualifier) +{ + if (member >= 0) { + if (qualifier.perPrimitiveNV) { + // Need to add capability/extension for fragment shader. + // Mesh shader already adds this by default. + if (glslangIntermediate->getStage() == EShLangFragment) { + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + } + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerPrimitiveNV); + } + if (qualifier.perViewNV) + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerViewNV); + if (qualifier.perTaskNV) + builder.addMemberDecoration(id, (unsigned)member, spv::DecorationPerTaskNV); + } else { + if (qualifier.perPrimitiveNV) { + // Need to add capability/extension for fragment shader. + // Mesh shader already adds this by default. + if (glslangIntermediate->getStage() == EShLangFragment) { + builder.addCapability(spv::CapabilityMeshShadingNV); + builder.addExtension(spv::E_SPV_NV_mesh_shader); + } + builder.addDecoration(id, spv::DecorationPerPrimitiveNV); + } + if (qualifier.perViewNV) + builder.addDecoration(id, spv::DecorationPerViewNV); + if (qualifier.perTaskNV) + builder.addDecoration(id, spv::DecorationPerTaskNV); + } +} +#endif + +// Make a full tree of instructions to build a SPIR-V specialization constant, +// or regular constant if possible. +// +// TBD: this is not yet done, nor verified to be the best design, it does do the leaf symbols though +// +// Recursively walk the nodes. The nodes form a tree whose leaves are +// regular constants, which themselves are trees that createSpvConstant() +// recursively walks. So, this function walks the "top" of the tree: +// - emit specialization constant-building instructions for specConstant +// - when running into a non-spec-constant, switch to createSpvConstant() +spv::Id TGlslangToSpvTraverser::createSpvConstant(const glslang::TIntermTyped& node) +{ + assert(node.getQualifier().isConstant()); + + // Handle front-end constants first (non-specialization constants). + if (! node.getQualifier().specConstant) { + // hand off to the non-spec-constant path + assert(node.getAsConstantUnion() != nullptr || node.getAsSymbolNode() != nullptr); + int nextConst = 0; + return createSpvConstantFromConstUnionArray(node.getType(), node.getAsConstantUnion() ? + node.getAsConstantUnion()->getConstArray() : node.getAsSymbolNode()->getConstArray(), + nextConst, false); + } + + // We now know we have a specialization constant to build + + // Extra capabilities may be needed. + if (node.getType().contains8BitInt()) + builder.addCapability(spv::CapabilityInt8); + if (node.getType().contains16BitFloat()) + builder.addCapability(spv::CapabilityFloat16); + if (node.getType().contains16BitInt()) + builder.addCapability(spv::CapabilityInt16); + if (node.getType().contains64BitInt()) + builder.addCapability(spv::CapabilityInt64); + if (node.getType().containsDouble()) + builder.addCapability(spv::CapabilityFloat64); + + // gl_WorkGroupSize is a special case until the front-end handles hierarchical specialization constants, + // even then, it's specialization ids are handled by special case syntax in GLSL: layout(local_size_x = ... + if (node.getType().getQualifier().builtIn == glslang::EbvWorkGroupSize) { + std::vector dimConstId; + for (int dim = 0; dim < 3; ++dim) { + bool specConst = (glslangIntermediate->getLocalSizeSpecId(dim) != glslang::TQualifier::layoutNotSet); + dimConstId.push_back(builder.makeUintConstant(glslangIntermediate->getLocalSize(dim), specConst)); + if (specConst) { + builder.addDecoration(dimConstId.back(), spv::DecorationSpecId, + glslangIntermediate->getLocalSizeSpecId(dim)); + } + } + return builder.makeCompositeConstant(builder.makeVectorType(builder.makeUintType(32), 3), dimConstId, true); + } + + // An AST node labelled as specialization constant should be a symbol node. + // Its initializer should either be a sub tree with constant nodes, or a constant union array. + if (auto* sn = node.getAsSymbolNode()) { + spv::Id result; + if (auto* sub_tree = sn->getConstSubtree()) { + // Traverse the constant constructor sub tree like generating normal run-time instructions. + // During the AST traversal, if the node is marked as 'specConstant', SpecConstantOpModeGuard + // will set the builder into spec constant op instruction generating mode. + sub_tree->traverse(this); + result = accessChainLoad(sub_tree->getType()); + } else if (auto* const_union_array = &sn->getConstArray()) { + int nextConst = 0; + result = createSpvConstantFromConstUnionArray(sn->getType(), *const_union_array, nextConst, true); + } else { + logger->missingFunctionality("Invalid initializer for spec onstant."); + return spv::NoResult; + } + builder.addName(result, sn->getName().c_str()); + return result; + } + + // Neither a front-end constant node, nor a specialization constant node with constant union array or + // constant sub tree as initializer. + logger->missingFunctionality("Neither a front-end constant nor a spec constant."); + return spv::NoResult; +} + +// Use 'consts' as the flattened glslang source of scalar constants to recursively +// build the aggregate SPIR-V constant. +// +// If there are not enough elements present in 'consts', 0 will be substituted; +// an empty 'consts' can be used to create a fully zeroed SPIR-V constant. +// +spv::Id TGlslangToSpvTraverser::createSpvConstantFromConstUnionArray(const glslang::TType& glslangType, + const glslang::TConstUnionArray& consts, int& nextConst, bool specConstant) +{ + // vector of constants for SPIR-V + std::vector spvConsts; + + // Type is used for struct and array constants + spv::Id typeId = convertGlslangToSpvType(glslangType); + + if (glslangType.isArray()) { + glslang::TType elementType(glslangType, 0); + for (int i = 0; i < glslangType.getOuterArraySize(); ++i) + spvConsts.push_back(createSpvConstantFromConstUnionArray(elementType, consts, nextConst, false)); + } else if (glslangType.isMatrix()) { + glslang::TType vectorType(glslangType, 0); + for (int col = 0; col < glslangType.getMatrixCols(); ++col) + spvConsts.push_back(createSpvConstantFromConstUnionArray(vectorType, consts, nextConst, false)); + } else if (glslangType.isCoopMat()) { + glslang::TType componentType(glslangType.getBasicType()); + spvConsts.push_back(createSpvConstantFromConstUnionArray(componentType, consts, nextConst, false)); + } else if (glslangType.isStruct()) { + glslang::TVector::const_iterator iter; + for (iter = glslangType.getStruct()->begin(); iter != glslangType.getStruct()->end(); ++iter) + spvConsts.push_back(createSpvConstantFromConstUnionArray(*iter->type, consts, nextConst, false)); + } else if (glslangType.getVectorSize() > 1) { + for (unsigned int i = 0; i < (unsigned int)glslangType.getVectorSize(); ++i) { + bool zero = nextConst >= consts.size(); + switch (glslangType.getBasicType()) { + case glslang::EbtInt: + spvConsts.push_back(builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst())); + break; + case glslang::EbtUint: + spvConsts.push_back(builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst())); + break; + case glslang::EbtFloat: + spvConsts.push_back(builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; + case glslang::EbtBool: + spvConsts.push_back(builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst())); + break; +#ifndef GLSLANG_WEB + case glslang::EbtInt8: + spvConsts.push_back(builder.makeInt8Constant(zero ? 0 : consts[nextConst].getI8Const())); + break; + case glslang::EbtUint8: + spvConsts.push_back(builder.makeUint8Constant(zero ? 0 : consts[nextConst].getU8Const())); + break; + case glslang::EbtInt16: + spvConsts.push_back(builder.makeInt16Constant(zero ? 0 : consts[nextConst].getI16Const())); + break; + case glslang::EbtUint16: + spvConsts.push_back(builder.makeUint16Constant(zero ? 0 : consts[nextConst].getU16Const())); + break; + case glslang::EbtInt64: + spvConsts.push_back(builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const())); + break; + case glslang::EbtUint64: + spvConsts.push_back(builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const())); + break; + case glslang::EbtDouble: + spvConsts.push_back(builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst())); + break; + case glslang::EbtFloat16: + spvConsts.push_back(builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst())); + break; +#endif + default: + assert(0); + break; + } + ++nextConst; + } + } else { + // we have a non-aggregate (scalar) constant + bool zero = nextConst >= consts.size(); + spv::Id scalar = 0; + switch (glslangType.getBasicType()) { + case glslang::EbtInt: + scalar = builder.makeIntConstant(zero ? 0 : consts[nextConst].getIConst(), specConstant); + break; + case glslang::EbtUint: + scalar = builder.makeUintConstant(zero ? 0 : consts[nextConst].getUConst(), specConstant); + break; + case glslang::EbtFloat: + scalar = builder.makeFloatConstant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtBool: + scalar = builder.makeBoolConstant(zero ? false : consts[nextConst].getBConst(), specConstant); + break; +#ifndef GLSLANG_WEB + case glslang::EbtInt8: + scalar = builder.makeInt8Constant(zero ? 0 : consts[nextConst].getI8Const(), specConstant); + break; + case glslang::EbtUint8: + scalar = builder.makeUint8Constant(zero ? 0 : consts[nextConst].getU8Const(), specConstant); + break; + case glslang::EbtInt16: + scalar = builder.makeInt16Constant(zero ? 0 : consts[nextConst].getI16Const(), specConstant); + break; + case glslang::EbtUint16: + scalar = builder.makeUint16Constant(zero ? 0 : consts[nextConst].getU16Const(), specConstant); + break; + case glslang::EbtInt64: + scalar = builder.makeInt64Constant(zero ? 0 : consts[nextConst].getI64Const(), specConstant); + break; + case glslang::EbtUint64: + scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant); + break; + case glslang::EbtDouble: + scalar = builder.makeDoubleConstant(zero ? 0.0 : consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtFloat16: + scalar = builder.makeFloat16Constant(zero ? 0.0F : (float)consts[nextConst].getDConst(), specConstant); + break; + case glslang::EbtReference: + scalar = builder.makeUint64Constant(zero ? 0 : consts[nextConst].getU64Const(), specConstant); + scalar = builder.createUnaryOp(spv::OpBitcast, typeId, scalar); + break; +#endif + case glslang::EbtString: + scalar = builder.getStringId(consts[nextConst].getSConst()->c_str()); + break; + default: + assert(0); + break; + } + ++nextConst; + return scalar; + } + + return builder.makeCompositeConstant(typeId, spvConsts); +} + +// Return true if the node is a constant or symbol whose reading has no +// non-trivial observable cost or effect. +bool TGlslangToSpvTraverser::isTrivialLeaf(const glslang::TIntermTyped* node) +{ + // don't know what this is + if (node == nullptr) + return false; + + // a constant is safe + if (node->getAsConstantUnion() != nullptr) + return true; + + // not a symbol means non-trivial + if (node->getAsSymbolNode() == nullptr) + return false; + + // a symbol, depends on what's being read + switch (node->getType().getQualifier().storage) { + case glslang::EvqTemporary: + case glslang::EvqGlobal: + case glslang::EvqIn: + case glslang::EvqInOut: + case glslang::EvqConst: + case glslang::EvqConstReadOnly: + case glslang::EvqUniform: + return true; + default: + return false; + } +} + +// A node is trivial if it is a single operation with no side effects. +// HLSL (and/or vectors) are always trivial, as it does not short circuit. +// Otherwise, error on the side of saying non-trivial. +// Return true if trivial. +bool TGlslangToSpvTraverser::isTrivial(const glslang::TIntermTyped* node) +{ + if (node == nullptr) + return false; + + // count non scalars as trivial, as well as anything coming from HLSL + if (! node->getType().isScalarOrVec1() || glslangIntermediate->getSource() == glslang::EShSourceHlsl) + return true; + + // symbols and constants are trivial + if (isTrivialLeaf(node)) + return true; + + // otherwise, it needs to be a simple operation or one or two leaf nodes + + // not a simple operation + const glslang::TIntermBinary* binaryNode = node->getAsBinaryNode(); + const glslang::TIntermUnary* unaryNode = node->getAsUnaryNode(); + if (binaryNode == nullptr && unaryNode == nullptr) + return false; + + // not on leaf nodes + if (binaryNode && (! isTrivialLeaf(binaryNode->getLeft()) || ! isTrivialLeaf(binaryNode->getRight()))) + return false; + + if (unaryNode && ! isTrivialLeaf(unaryNode->getOperand())) { + return false; + } + + switch (node->getAsOperator()->getOp()) { + case glslang::EOpLogicalNot: + case glslang::EOpConvIntToBool: + case glslang::EOpConvUintToBool: + case glslang::EOpConvFloatToBool: + case glslang::EOpConvDoubleToBool: + case glslang::EOpEqual: + case glslang::EOpNotEqual: + case glslang::EOpLessThan: + case glslang::EOpGreaterThan: + case glslang::EOpLessThanEqual: + case glslang::EOpGreaterThanEqual: + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + case glslang::EOpLogicalXor: + case glslang::EOpAny: + case glslang::EOpAll: + return true; + default: + return false; + } +} + +// Emit short-circuiting code, where 'right' is never evaluated unless +// the left side is true (for &&) or false (for ||). +spv::Id TGlslangToSpvTraverser::createShortCircuit(glslang::TOperator op, glslang::TIntermTyped& left, + glslang::TIntermTyped& right) +{ + spv::Id boolTypeId = builder.makeBoolType(); + + // emit left operand + builder.clearAccessChain(); + left.traverse(this); + spv::Id leftId = accessChainLoad(left.getType()); + + // Operands to accumulate OpPhi operands + std::vector phiOperands; + // accumulate left operand's phi information + phiOperands.push_back(leftId); + phiOperands.push_back(builder.getBuildPoint()->getId()); + + // Make the two kinds of operation symmetric with a "!" + // || => emit "if (! left) result = right" + // && => emit "if ( left) result = right" + // + // TODO: this runtime "not" for || could be avoided by adding functionality + // to 'builder' to have an "else" without an "then" + if (op == glslang::EOpLogicalOr) + leftId = builder.createUnaryOp(spv::OpLogicalNot, boolTypeId, leftId); + + // make an "if" based on the left value + spv::Builder::If ifBuilder(leftId, spv::SelectionControlMaskNone, builder); + + // emit right operand as the "then" part of the "if" + builder.clearAccessChain(); + right.traverse(this); + spv::Id rightId = accessChainLoad(right.getType()); + + // accumulate left operand's phi information + phiOperands.push_back(rightId); + phiOperands.push_back(builder.getBuildPoint()->getId()); + + // finish the "if" + ifBuilder.makeEndIf(); + + // phi together the two results + return builder.createOp(spv::OpPhi, boolTypeId, phiOperands); +} + +#ifndef GLSLANG_WEB +// Return type Id of the imported set of extended instructions corresponds to the name. +// Import this set if it has not been imported yet. +spv::Id TGlslangToSpvTraverser::getExtBuiltins(const char* name) +{ + if (extBuiltinMap.find(name) != extBuiltinMap.end()) + return extBuiltinMap[name]; + else { + builder.addExtension(name); + spv::Id extBuiltins = builder.import(name); + extBuiltinMap[name] = extBuiltins; + return extBuiltins; + } +} +#endif + +}; // end anonymous namespace + +namespace glslang { + +void GetSpirvVersion(std::string& version) +{ + const int bufSize = 100; + char buf[bufSize]; + snprintf(buf, bufSize, "0x%08x, Revision %d", spv::Version, spv::Revision); + version = buf; +} + +// For low-order part of the generator's magic number. Bump up +// when there is a change in the style (e.g., if SSA form changes, +// or a different instruction sequence to do something gets used). +int GetSpirvGeneratorVersion() +{ + // return 1; // start + // return 2; // EOpAtomicCounterDecrement gets a post decrement, to map between GLSL -> SPIR-V + // return 3; // change/correct barrier-instruction operands, to match memory model group decisions + // return 4; // some deeper access chains: for dynamic vector component, and local Boolean component + // return 5; // make OpArrayLength result type be an int with signedness of 0 + // return 6; // revert version 5 change, which makes a different (new) kind of incorrect code, + // versions 4 and 6 each generate OpArrayLength as it has long been done + // return 7; // GLSL volatile keyword maps to both SPIR-V decorations Volatile and Coherent + // return 8; // switch to new dead block eliminator; use OpUnreachable + // return 9; // don't include opaque function parameters in OpEntryPoint global's operand list + return 10; // Generate OpFUnordNotEqual for != comparisons +} + +// Write SPIR-V out to a binary file +void OutputSpvBin(const std::vector& spirv, const char* baseName) +{ + std::ofstream out; + out.open(baseName, std::ios::binary | std::ios::out); + if (out.fail()) + printf("ERROR: Failed to open file: %s\n", baseName); + for (int i = 0; i < (int)spirv.size(); ++i) { + unsigned int word = spirv[i]; + out.write((const char*)&word, 4); + } + out.close(); +} + +// Write SPIR-V out to a text file with 32-bit hexadecimal words +void OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName) +{ +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + std::ofstream out; + out.open(baseName, std::ios::binary | std::ios::out); + if (out.fail()) + printf("ERROR: Failed to open file: %s\n", baseName); + out << "\t// " << + GetSpirvGeneratorVersion() << + GLSLANG_VERSION_MAJOR << "." << GLSLANG_VERSION_MINOR << "." << GLSLANG_VERSION_PATCH << + GLSLANG_VERSION_FLAVOR << std::endl; + if (varName != nullptr) { + out << "\t #pragma once" << std::endl; + out << "const uint32_t " << varName << "[] = {" << std::endl; + } + const int WORDS_PER_LINE = 8; + for (int i = 0; i < (int)spirv.size(); i += WORDS_PER_LINE) { + out << "\t"; + for (int j = 0; j < WORDS_PER_LINE && i + j < (int)spirv.size(); ++j) { + const unsigned int word = spirv[i + j]; + out << "0x" << std::hex << std::setw(8) << std::setfill('0') << word; + if (i + j + 1 < (int)spirv.size()) { + out << ","; + } + } + out << std::endl; + } + if (varName != nullptr) { + out << "};"; + out << std::endl; + } + out.close(); +#endif +} + +// +// Set up the glslang traversal +// +void GlslangToSpv(const TIntermediate& intermediate, std::vector& spirv, SpvOptions* options) +{ + spv::SpvBuildLogger logger; + GlslangToSpv(intermediate, spirv, &logger, options); +} + +void GlslangToSpv(const TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options) +{ + TIntermNode* root = intermediate.getTreeRoot(); + + if (root == 0) + return; + + SpvOptions defaultOptions; + if (options == nullptr) + options = &defaultOptions; + + GetThreadPoolAllocator().push(); + + TGlslangToSpvTraverser it(intermediate.getSpv().spv, &intermediate, logger, *options); + root->traverse(&it); + it.finishSpv(); + it.dumpSpv(spirv); + +#if ENABLE_OPT + // If from HLSL, run spirv-opt to "legalize" the SPIR-V for Vulkan + // eg. forward and remove memory writes of opaque types. + bool prelegalization = intermediate.getSource() == EShSourceHlsl; + if ((prelegalization || options->optimizeSize) && !options->disableOptimizer) { + SpirvToolsTransform(intermediate, spirv, logger, options); + prelegalization = false; + } + else if (options->stripDebugInfo) { + // Strip debug info even if optimization is disabled. + SpirvToolsStripDebugInfo(intermediate, spirv, logger); + } + + if (options->validate) + SpirvToolsValidate(intermediate, spirv, logger, prelegalization); + + if (options->disassemble) + SpirvToolsDisassemble(std::cout, spirv); + +#endif + + GetThreadPoolAllocator().pop(); +} + +}; // end namespace glslang diff --git a/third_party/glslang/SPIRV/GlslangToSpv.h b/third_party/glslang/SPIRV/GlslangToSpv.h new file mode 100755 index 0000000..3907be4 --- /dev/null +++ b/third_party/glslang/SPIRV/GlslangToSpv.h @@ -0,0 +1,61 @@ +// +// Copyright (C) 2014 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#if defined(_MSC_VER) && _MSC_VER >= 1900 + #pragma warning(disable : 4464) // relative include path contains '..' +#endif + +#include "SpvTools.h" +#include "glslang/Include/intermediate.h" + +#include +#include + +#include "Logger.h" + +namespace glslang { + +void GetSpirvVersion(std::string&); +int GetSpirvGeneratorVersion(); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + SpvOptions* options = nullptr); +void GlslangToSpv(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, SpvOptions* options = nullptr); +void OutputSpvBin(const std::vector& spirv, const char* baseName); +void OutputSpvHex(const std::vector& spirv, const char* baseName, const char* varName); + +} diff --git a/third_party/glslang/SPIRV/InReadableOrder.cpp b/third_party/glslang/SPIRV/InReadableOrder.cpp new file mode 100644 index 0000000..9d9410b --- /dev/null +++ b/third_party/glslang/SPIRV/InReadableOrder.cpp @@ -0,0 +1,131 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// The SPIR-V spec requires code blocks to appear in an order satisfying the +// dominator-tree direction (ie, dominator before the dominated). This is, +// actually, easy to achieve: any pre-order CFG traversal algorithm will do it. +// Because such algorithms visit a block only after traversing some path to it +// from the root, they necessarily visit the block's idom first. +// +// But not every graph-traversal algorithm outputs blocks in an order that +// appears logical to human readers. The problem is that unrelated branches may +// be interspersed with each other, and merge blocks may come before some of the +// branches being merged. +// +// A good, human-readable order of blocks may be achieved by performing +// depth-first search but delaying merge nodes until after all their branches +// have been visited. This is implemented below by the inReadableOrder() +// function. + +#include "spvIR.h" + +#include +#include + +using spv::Block; +using spv::Id; + +namespace { +// Traverses CFG in a readable order, invoking a pre-set callback on each block. +// Use by calling visit() on the root block. +class ReadableOrderTraverser { +public: + ReadableOrderTraverser(std::function callback) + : callback_(callback) {} + // Visits the block if it hasn't been visited already and isn't currently + // being delayed. Invokes callback(block, why, header), then descends into its + // successors. Delays merge-block and continue-block processing until all + // the branches have been completed. If |block| is an unreachable merge block or + // an unreachable continue target, then |header| is the corresponding header block. + void visit(Block* block, spv::ReachReason why, Block* header) + { + assert(block); + if (why == spv::ReachViaControlFlow) { + reachableViaControlFlow_.insert(block); + } + if (visited_.count(block) || delayed_.count(block)) + return; + callback_(block, why, header); + visited_.insert(block); + Block* mergeBlock = nullptr; + Block* continueBlock = nullptr; + auto mergeInst = block->getMergeInstruction(); + if (mergeInst) { + Id mergeId = mergeInst->getIdOperand(0); + mergeBlock = block->getParent().getParent().getInstruction(mergeId)->getBlock(); + delayed_.insert(mergeBlock); + if (mergeInst->getOpCode() == spv::OpLoopMerge) { + Id continueId = mergeInst->getIdOperand(1); + continueBlock = + block->getParent().getParent().getInstruction(continueId)->getBlock(); + delayed_.insert(continueBlock); + } + } + if (why == spv::ReachViaControlFlow) { + const auto& successors = block->getSuccessors(); + for (auto it = successors.cbegin(); it != successors.cend(); ++it) + visit(*it, why, nullptr); + } + if (continueBlock) { + const spv::ReachReason continueWhy = + (reachableViaControlFlow_.count(continueBlock) > 0) + ? spv::ReachViaControlFlow + : spv::ReachDeadContinue; + delayed_.erase(continueBlock); + visit(continueBlock, continueWhy, block); + } + if (mergeBlock) { + const spv::ReachReason mergeWhy = + (reachableViaControlFlow_.count(mergeBlock) > 0) + ? spv::ReachViaControlFlow + : spv::ReachDeadMerge; + delayed_.erase(mergeBlock); + visit(mergeBlock, mergeWhy, block); + } + } + +private: + std::function callback_; + // Whether a block has already been visited or is being delayed. + std::unordered_set visited_, delayed_; + + // The set of blocks that actually are reached via control flow. + std::unordered_set reachableViaControlFlow_; +}; +} + +void spv::inReadableOrder(Block* root, std::function callback) +{ + ReadableOrderTraverser(callback).visit(root, spv::ReachViaControlFlow, nullptr); +} diff --git a/third_party/glslang/SPIRV/Logger.cpp b/third_party/glslang/SPIRV/Logger.cpp new file mode 100644 index 0000000..cdc8469 --- /dev/null +++ b/third_party/glslang/SPIRV/Logger.cpp @@ -0,0 +1,72 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_WEB + +#include "Logger.h" + +#include +#include +#include + +namespace spv { + +void SpvBuildLogger::tbdFunctionality(const std::string& f) +{ + if (std::find(std::begin(tbdFeatures), std::end(tbdFeatures), f) == std::end(tbdFeatures)) + tbdFeatures.push_back(f); +} + +void SpvBuildLogger::missingFunctionality(const std::string& f) +{ + if (std::find(std::begin(missingFeatures), std::end(missingFeatures), f) == std::end(missingFeatures)) + missingFeatures.push_back(f); +} + +std::string SpvBuildLogger::getAllMessages() const { + std::ostringstream messages; + for (auto it = tbdFeatures.cbegin(); it != tbdFeatures.cend(); ++it) + messages << "TBD functionality: " << *it << "\n"; + for (auto it = missingFeatures.cbegin(); it != missingFeatures.cend(); ++it) + messages << "Missing functionality: " << *it << "\n"; + for (auto it = warnings.cbegin(); it != warnings.cend(); ++it) + messages << "warning: " << *it << "\n"; + for (auto it = errors.cbegin(); it != errors.cend(); ++it) + messages << "error: " << *it << "\n"; + return messages.str(); +} + +} // end spv namespace + +#endif diff --git a/third_party/glslang/SPIRV/Logger.h b/third_party/glslang/SPIRV/Logger.h new file mode 100644 index 0000000..411367c --- /dev/null +++ b/third_party/glslang/SPIRV/Logger.h @@ -0,0 +1,83 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_SPIRV_LOGGER_H +#define GLSLANG_SPIRV_LOGGER_H + +#include +#include + +namespace spv { + +// A class for holding all SPIR-V build status messages, including +// missing/TBD functionalities, warnings, and errors. +class SpvBuildLogger { +public: + SpvBuildLogger() {} + +#ifdef GLSLANG_WEB + void tbdFunctionality(const std::string& f) { } + void missingFunctionality(const std::string& f) { } + void warning(const std::string& w) { } + void error(const std::string& e) { errors.push_back(e); } + std::string getAllMessages() { return ""; } +#else + + // Registers a TBD functionality. + void tbdFunctionality(const std::string& f); + // Registers a missing functionality. + void missingFunctionality(const std::string& f); + + // Logs a warning. + void warning(const std::string& w) { warnings.push_back(w); } + // Logs an error. + void error(const std::string& e) { errors.push_back(e); } + + // Returns all messages accumulated in the order of: + // TBD functionalities, missing functionalities, warnings, errors. + std::string getAllMessages() const; +#endif + +private: + SpvBuildLogger(const SpvBuildLogger&); + + std::vector tbdFeatures; + std::vector missingFeatures; + std::vector warnings; + std::vector errors; +}; + +} // end spv namespace + +#endif // GLSLANG_SPIRV_LOGGER_H diff --git a/third_party/glslang/SPIRV/NonSemanticDebugPrintf.h b/third_party/glslang/SPIRV/NonSemanticDebugPrintf.h new file mode 100644 index 0000000..83796d7 --- /dev/null +++ b/third_party/glslang/SPIRV/NonSemanticDebugPrintf.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ +#define SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticDebugPrintfRevision = 1, + NonSemanticDebugPrintfRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticDebugPrintfInstructions { + NonSemanticDebugPrintfDebugPrintf = 1, + NonSemanticDebugPrintfInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ diff --git a/third_party/glslang/SPIRV/SPVRemapper.cpp b/third_party/glslang/SPIRV/SPVRemapper.cpp new file mode 100644 index 0000000..56d7f43 --- /dev/null +++ b/third_party/glslang/SPIRV/SPVRemapper.cpp @@ -0,0 +1,1498 @@ +// +// Copyright (C) 2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "SPVRemapper.h" +#include "doc.h" + +#if !defined (use_cpp11) +// ... not supported before C++11 +#else // defined (use_cpp11) + +#include +#include +#include "../glslang/Include/Common.h" + +namespace spv { + + // By default, just abort on error. Can be overridden via RegisterErrorHandler + spirvbin_t::errorfn_t spirvbin_t::errorHandler = [](const std::string&) { exit(5); }; + // By default, eat log messages. Can be overridden via RegisterLogHandler + spirvbin_t::logfn_t spirvbin_t::logHandler = [](const std::string&) { }; + + // This can be overridden to provide other message behavior if needed + void spirvbin_t::msg(int minVerbosity, int indent, const std::string& txt) const + { + if (verbose >= minVerbosity) + logHandler(std::string(indent, ' ') + txt); + } + + // hash opcode, with special handling for OpExtInst + std::uint32_t spirvbin_t::asOpCodeHash(unsigned word) + { + const spv::Op opCode = asOpCode(word); + + std::uint32_t offset = 0; + + switch (opCode) { + case spv::OpExtInst: + offset += asId(word + 4); break; + default: + break; + } + + return opCode * 19 + offset; // 19 = small prime + } + + spirvbin_t::range_t spirvbin_t::literalRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + switch (opCode) { + case spv::OpTypeFloat: // fall through... + case spv::OpTypePointer: return range_t(2, 3); + case spv::OpTypeInt: return range_t(2, 4); + // TODO: case spv::OpTypeImage: + // TODO: case spv::OpTypeSampledImage: + case spv::OpTypeSampler: return range_t(3, 8); + case spv::OpTypeVector: // fall through + case spv::OpTypeMatrix: // ... + case spv::OpTypePipe: return range_t(3, 4); + case spv::OpConstant: return range_t(3, maxCount); + default: return range_t(0, 0); + } + } + + spirvbin_t::range_t spirvbin_t::typeRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + if (isConstOp(opCode)) + return range_t(1, 2); + + switch (opCode) { + case spv::OpTypeVector: // fall through + case spv::OpTypeMatrix: // ... + case spv::OpTypeSampler: // ... + case spv::OpTypeArray: // ... + case spv::OpTypeRuntimeArray: // ... + case spv::OpTypePipe: return range_t(2, 3); + case spv::OpTypeStruct: // fall through + case spv::OpTypeFunction: return range_t(2, maxCount); + case spv::OpTypePointer: return range_t(3, 4); + default: return range_t(0, 0); + } + } + + spirvbin_t::range_t spirvbin_t::constRange(spv::Op opCode) const + { + static const int maxCount = 1<<30; + + switch (opCode) { + case spv::OpTypeArray: // fall through... + case spv::OpTypeRuntimeArray: return range_t(3, 4); + case spv::OpConstantComposite: return range_t(3, maxCount); + default: return range_t(0, 0); + } + } + + // Return the size of a type in 32-bit words. This currently only + // handles ints and floats, and is only invoked by queries which must be + // integer types. If ever needed, it can be generalized. + unsigned spirvbin_t::typeSizeInWords(spv::Id id) const + { + const unsigned typeStart = idPos(id); + const spv::Op opCode = asOpCode(typeStart); + + if (errorLatch) + return 0; + + switch (opCode) { + case spv::OpTypeInt: // fall through... + case spv::OpTypeFloat: return (spv[typeStart+2]+31)/32; + default: + return 0; + } + } + + // Looks up the type of a given const or variable ID, and + // returns its size in 32-bit words. + unsigned spirvbin_t::idTypeSizeInWords(spv::Id id) const + { + const auto tid_it = idTypeSizeMap.find(id); + if (tid_it == idTypeSizeMap.end()) { + error("type size for ID not found"); + return 0; + } + + return tid_it->second; + } + + // Is this an opcode we should remove when using --strip? + bool spirvbin_t::isStripOp(spv::Op opCode) const + { + switch (opCode) { + case spv::OpSource: + case spv::OpSourceExtension: + case spv::OpName: + case spv::OpMemberName: + case spv::OpLine: return true; + default: return false; + } + } + + // Return true if this opcode is flow control + bool spirvbin_t::isFlowCtrl(spv::Op opCode) const + { + switch (opCode) { + case spv::OpBranchConditional: + case spv::OpBranch: + case spv::OpSwitch: + case spv::OpLoopMerge: + case spv::OpSelectionMerge: + case spv::OpLabel: + case spv::OpFunction: + case spv::OpFunctionEnd: return true; + default: return false; + } + } + + // Return true if this opcode defines a type + bool spirvbin_t::isTypeOp(spv::Op opCode) const + { + switch (opCode) { + case spv::OpTypeVoid: + case spv::OpTypeBool: + case spv::OpTypeInt: + case spv::OpTypeFloat: + case spv::OpTypeVector: + case spv::OpTypeMatrix: + case spv::OpTypeImage: + case spv::OpTypeSampler: + case spv::OpTypeArray: + case spv::OpTypeRuntimeArray: + case spv::OpTypeStruct: + case spv::OpTypeOpaque: + case spv::OpTypePointer: + case spv::OpTypeFunction: + case spv::OpTypeEvent: + case spv::OpTypeDeviceEvent: + case spv::OpTypeReserveId: + case spv::OpTypeQueue: + case spv::OpTypeSampledImage: + case spv::OpTypePipe: return true; + default: return false; + } + } + + // Return true if this opcode defines a constant + bool spirvbin_t::isConstOp(spv::Op opCode) const + { + switch (opCode) { + case spv::OpConstantSampler: + error("unimplemented constant type"); + return true; + + case spv::OpConstantNull: + case spv::OpConstantTrue: + case spv::OpConstantFalse: + case spv::OpConstantComposite: + case spv::OpConstant: + return true; + + default: + return false; + } + } + + const auto inst_fn_nop = [](spv::Op, unsigned) { return false; }; + const auto op_fn_nop = [](spv::Id&) { }; + + // g++ doesn't like these defined in the class proper in an anonymous namespace. + // Dunno why. Also MSVC doesn't like the constexpr keyword. Also dunno why. + // Defining them externally seems to please both compilers, so, here they are. + const spv::Id spirvbin_t::unmapped = spv::Id(-10000); + const spv::Id spirvbin_t::unused = spv::Id(-10001); + const int spirvbin_t::header_size = 5; + + spv::Id spirvbin_t::nextUnusedId(spv::Id id) + { + while (isNewIdMapped(id)) // search for an unused ID + ++id; + + return id; + } + + spv::Id spirvbin_t::localId(spv::Id id, spv::Id newId) + { + //assert(id != spv::NoResult && newId != spv::NoResult); + + if (id > bound()) { + error(std::string("ID out of range: ") + std::to_string(id)); + return spirvbin_t::unused; + } + + if (id >= idMapL.size()) + idMapL.resize(id+1, unused); + + if (newId != unmapped && newId != unused) { + if (isOldIdUnused(id)) { + error(std::string("ID unused in module: ") + std::to_string(id)); + return spirvbin_t::unused; + } + + if (!isOldIdUnmapped(id)) { + error(std::string("ID already mapped: ") + std::to_string(id) + " -> " + + std::to_string(localId(id))); + + return spirvbin_t::unused; + } + + if (isNewIdMapped(newId)) { + error(std::string("ID already used in module: ") + std::to_string(newId)); + return spirvbin_t::unused; + } + + msg(4, 4, std::string("map: ") + std::to_string(id) + " -> " + std::to_string(newId)); + setMapped(newId); + largestNewId = std::max(largestNewId, newId); + } + + return idMapL[id] = newId; + } + + // Parse a literal string from the SPIR binary and return it as an std::string + // Due to C++11 RValue references, this doesn't copy the result string. + std::string spirvbin_t::literalString(unsigned word) const + { + std::string literal; + + literal.reserve(16); + + const char* bytes = reinterpret_cast(spv.data() + word); + + while (bytes && *bytes) + literal += *bytes++; + + return literal; + } + + void spirvbin_t::applyMap() + { + msg(3, 2, std::string("Applying map: ")); + + // Map local IDs through the ID map + process(inst_fn_nop, // ignore instructions + [this](spv::Id& id) { + id = localId(id); + + if (errorLatch) + return; + + assert(id != unused && id != unmapped); + } + ); + } + + // Find free IDs for anything we haven't mapped + void spirvbin_t::mapRemainder() + { + msg(3, 2, std::string("Remapping remainder: ")); + + spv::Id unusedId = 1; // can't use 0: that's NoResult + spirword_t maxBound = 0; + + for (spv::Id id = 0; id < idMapL.size(); ++id) { + if (isOldIdUnused(id)) + continue; + + // Find a new mapping for any used but unmapped IDs + if (isOldIdUnmapped(id)) { + localId(id, unusedId = nextUnusedId(unusedId)); + if (errorLatch) + return; + } + + if (isOldIdUnmapped(id)) { + error(std::string("old ID not mapped: ") + std::to_string(id)); + return; + } + + // Track max bound + maxBound = std::max(maxBound, localId(id) + 1); + + if (errorLatch) + return; + } + + bound(maxBound); // reset header ID bound to as big as it now needs to be + } + + // Mark debug instructions for stripping + void spirvbin_t::stripDebug() + { + // Strip instructions in the stripOp set: debug info. + process( + [&](spv::Op opCode, unsigned start) { + // remember opcodes we want to strip later + if (isStripOp(opCode)) + stripInst(start); + return true; + }, + op_fn_nop); + } + + // Mark instructions that refer to now-removed IDs for stripping + void spirvbin_t::stripDeadRefs() + { + process( + [&](spv::Op opCode, unsigned start) { + // strip opcodes pointing to removed data + switch (opCode) { + case spv::OpName: + case spv::OpMemberName: + case spv::OpDecorate: + case spv::OpMemberDecorate: + if (idPosR.find(asId(start+1)) == idPosR.end()) + stripInst(start); + break; + default: + break; // leave it alone + } + + return true; + }, + op_fn_nop); + + strip(); + } + + // Update local maps of ID, type, etc positions + void spirvbin_t::buildLocalMaps() + { + msg(2, 2, std::string("build local maps: ")); + + mapped.clear(); + idMapL.clear(); +// preserve nameMap, so we don't clear that. + fnPos.clear(); + fnCalls.clear(); + typeConstPos.clear(); + idPosR.clear(); + entryPoint = spv::NoResult; + largestNewId = 0; + + idMapL.resize(bound(), unused); + + int fnStart = 0; + spv::Id fnRes = spv::NoResult; + + // build local Id and name maps + process( + [&](spv::Op opCode, unsigned start) { + unsigned word = start+1; + spv::Id typeId = spv::NoResult; + + if (spv::InstructionDesc[opCode].hasType()) + typeId = asId(word++); + + // If there's a result ID, remember the size of its type + if (spv::InstructionDesc[opCode].hasResult()) { + const spv::Id resultId = asId(word++); + idPosR[resultId] = start; + + if (typeId != spv::NoResult) { + const unsigned idTypeSize = typeSizeInWords(typeId); + + if (errorLatch) + return false; + + if (idTypeSize != 0) + idTypeSizeMap[resultId] = idTypeSize; + } + } + + if (opCode == spv::Op::OpName) { + const spv::Id target = asId(start+1); + const std::string name = literalString(start+2); + nameMap[name] = target; + + } else if (opCode == spv::Op::OpFunctionCall) { + ++fnCalls[asId(start + 3)]; + } else if (opCode == spv::Op::OpEntryPoint) { + entryPoint = asId(start + 2); + } else if (opCode == spv::Op::OpFunction) { + if (fnStart != 0) { + error("nested function found"); + return false; + } + + fnStart = start; + fnRes = asId(start + 2); + } else if (opCode == spv::Op::OpFunctionEnd) { + assert(fnRes != spv::NoResult); + if (fnStart == 0) { + error("function end without function start"); + return false; + } + + fnPos[fnRes] = range_t(fnStart, start + asWordCount(start)); + fnStart = 0; + } else if (isConstOp(opCode)) { + if (errorLatch) + return false; + + assert(asId(start + 2) != spv::NoResult); + typeConstPos.insert(start); + } else if (isTypeOp(opCode)) { + assert(asId(start + 1) != spv::NoResult); + typeConstPos.insert(start); + } + + return false; + }, + + [this](spv::Id& id) { localId(id, unmapped); } + ); + } + + // Validate the SPIR header + void spirvbin_t::validate() const + { + msg(2, 2, std::string("validating: ")); + + if (spv.size() < header_size) { + error("file too short: "); + return; + } + + if (magic() != spv::MagicNumber) { + error("bad magic number"); + return; + } + + // field 1 = version + // field 2 = generator magic + // field 3 = result bound + + if (schemaNum() != 0) { + error("bad schema, must be 0"); + return; + } + } + + int spirvbin_t::processInstruction(unsigned word, instfn_t instFn, idfn_t idFn) + { + const auto instructionStart = word; + const unsigned wordCount = asWordCount(instructionStart); + const int nextInst = word++ + wordCount; + spv::Op opCode = asOpCode(instructionStart); + + if (nextInst > int(spv.size())) { + error("spir instruction terminated too early"); + return -1; + } + + // Base for computing number of operands; will be updated as more is learned + unsigned numOperands = wordCount - 1; + + if (instFn(opCode, instructionStart)) + return nextInst; + + // Read type and result ID from instruction desc table + if (spv::InstructionDesc[opCode].hasType()) { + idFn(asId(word++)); + --numOperands; + } + + if (spv::InstructionDesc[opCode].hasResult()) { + idFn(asId(word++)); + --numOperands; + } + + // Extended instructions: currently, assume everything is an ID. + // TODO: add whatever data we need for exceptions to that + if (opCode == spv::OpExtInst) { + word += 2; // instruction set, and instruction from set + numOperands -= 2; + + for (unsigned op=0; op < numOperands; ++op) + idFn(asId(word++)); // ID + + return nextInst; + } + + // Circular buffer so we can look back at previous unmapped values during the mapping pass. + static const unsigned idBufferSize = 4; + spv::Id idBuffer[idBufferSize]; + unsigned idBufferPos = 0; + + // Store IDs from instruction in our map + for (int op = 0; numOperands > 0; ++op, --numOperands) { + // SpecConstantOp is special: it includes the operands of another opcode which is + // given as a literal in the 3rd word. We will switch over to pretending that the + // opcode being processed is the literal opcode value of the SpecConstantOp. See the + // SPIRV spec for details. This way we will handle IDs and literals as appropriate for + // the embedded op. + if (opCode == spv::OpSpecConstantOp) { + if (op == 0) { + opCode = asOpCode(word++); // this is the opcode embedded in the SpecConstantOp. + --numOperands; + } + } + + switch (spv::InstructionDesc[opCode].operands.getClass(op)) { + case spv::OperandId: + case spv::OperandScope: + case spv::OperandMemorySemantics: + idBuffer[idBufferPos] = asId(word); + idBufferPos = (idBufferPos + 1) % idBufferSize; + idFn(asId(word++)); + break; + + case spv::OperandVariableIds: + for (unsigned i = 0; i < numOperands; ++i) + idFn(asId(word++)); + return nextInst; + + case spv::OperandVariableLiterals: + // for clarity + // if (opCode == spv::OpDecorate && asDecoration(word - 1) == spv::DecorationBuiltIn) { + // ++word; + // --numOperands; + // } + // word += numOperands; + return nextInst; + + case spv::OperandVariableLiteralId: { + if (opCode == OpSwitch) { + // word-2 is the position of the selector ID. OpSwitch Literals match its type. + // In case the IDs are currently being remapped, we get the word[-2] ID from + // the circular idBuffer. + const unsigned literalSizePos = (idBufferPos+idBufferSize-2) % idBufferSize; + const unsigned literalSize = idTypeSizeInWords(idBuffer[literalSizePos]); + const unsigned numLiteralIdPairs = (nextInst-word) / (1+literalSize); + + if (errorLatch) + return -1; + + for (unsigned arg=0; arg instPos; + instPos.reserve(unsigned(spv.size()) / 16); // initial estimate; can grow if needed. + + // Build local table of instruction start positions + process( + [&](spv::Op, unsigned start) { instPos.push_back(start); return true; }, + op_fn_nop); + + if (errorLatch) + return; + + // Window size for context-sensitive canonicalization values + // Empirical best size from a single data set. TODO: Would be a good tunable. + // We essentially perform a little convolution around each instruction, + // to capture the flavor of nearby code, to hopefully match to similar + // code in other modules. + static const unsigned windowSize = 2; + + for (unsigned entry = 0; entry < unsigned(instPos.size()); ++entry) { + const unsigned start = instPos[entry]; + const spv::Op opCode = asOpCode(start); + + if (opCode == spv::OpFunction) + fnId = asId(start + 2); + + if (opCode == spv::OpFunctionEnd) + fnId = spv::NoResult; + + if (fnId != spv::NoResult) { // if inside a function + if (spv::InstructionDesc[opCode].hasResult()) { + const unsigned word = start + (spv::InstructionDesc[opCode].hasType() ? 2 : 1); + const spv::Id resId = asId(word); + std::uint32_t hashval = fnId * 17; // small prime + + for (unsigned i = entry-1; i >= entry-windowSize; --i) { + if (asOpCode(instPos[i]) == spv::OpFunction) + break; + hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime + } + + for (unsigned i = entry; i <= entry + windowSize; ++i) { + if (asOpCode(instPos[i]) == spv::OpFunctionEnd) + break; + hashval = hashval * 30103 + asOpCodeHash(instPos[i]); // 30103 = semiarbitrary prime + } + + if (isOldIdUnmapped(resId)) { + localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + if (errorLatch) + return; + } + + } + } + } + + spv::Op thisOpCode(spv::OpNop); + std::unordered_map opCounter; + int idCounter(0); + fnId = spv::NoResult; + + process( + [&](spv::Op opCode, unsigned start) { + switch (opCode) { + case spv::OpFunction: + // Reset counters at each function + idCounter = 0; + opCounter.clear(); + fnId = asId(start + 2); + break; + + case spv::OpImageSampleImplicitLod: + case spv::OpImageSampleExplicitLod: + case spv::OpImageSampleDrefImplicitLod: + case spv::OpImageSampleDrefExplicitLod: + case spv::OpImageSampleProjImplicitLod: + case spv::OpImageSampleProjExplicitLod: + case spv::OpImageSampleProjDrefImplicitLod: + case spv::OpImageSampleProjDrefExplicitLod: + case spv::OpDot: + case spv::OpCompositeExtract: + case spv::OpCompositeInsert: + case spv::OpVectorShuffle: + case spv::OpLabel: + case spv::OpVariable: + + case spv::OpAccessChain: + case spv::OpLoad: + case spv::OpStore: + case spv::OpCompositeConstruct: + case spv::OpFunctionCall: + ++opCounter[opCode]; + idCounter = 0; + thisOpCode = opCode; + break; + default: + thisOpCode = spv::OpNop; + } + + return false; + }, + + [&](spv::Id& id) { + if (thisOpCode != spv::OpNop) { + ++idCounter; + const std::uint32_t hashval = + // Explicitly cast operands to unsigned int to avoid integer + // promotion to signed int followed by integer overflow, + // which would result in undefined behavior. + static_cast(opCounter[thisOpCode]) + * thisOpCode + * 50047 + + idCounter + + static_cast(fnId) * 117; + + if (isOldIdUnmapped(id)) + localId(id, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + } + }); + } + + // EXPERIMENTAL: forward IO and uniform load/stores into operands + // This produces invalid Schema-0 SPIRV + void spirvbin_t::forwardLoadStores() + { + idset_t fnLocalVars; // set of function local vars + idmap_t idMap; // Map of load result IDs to what they load + + // EXPERIMENTAL: Forward input and access chain loads into consumptions + process( + [&](spv::Op opCode, unsigned start) { + // Add inputs and uniforms to the map + if ((opCode == spv::OpVariable && asWordCount(start) == 4) && + (spv[start+3] == spv::StorageClassUniform || + spv[start+3] == spv::StorageClassUniformConstant || + spv[start+3] == spv::StorageClassInput)) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpAccessChain && fnLocalVars.count(asId(start+3)) > 0) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) { + idMap[asId(start+2)] = asId(start+3); + stripInst(start); + } + + return false; + }, + + [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; } + ); + + if (errorLatch) + return; + + // EXPERIMENTAL: Implicit output stores + fnLocalVars.clear(); + idMap.clear(); + + process( + [&](spv::Op opCode, unsigned start) { + // Add inputs and uniforms to the map + if ((opCode == spv::OpVariable && asWordCount(start) == 4) && + (spv[start+3] == spv::StorageClassOutput)) + fnLocalVars.insert(asId(start+2)); + + if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) { + idMap[asId(start+2)] = asId(start+1); + stripInst(start); + } + + return false; + }, + op_fn_nop); + + if (errorLatch) + return; + + process( + inst_fn_nop, + [&](spv::Id& id) { if (idMap.find(id) != idMap.end()) id = idMap[id]; } + ); + + if (errorLatch) + return; + + strip(); // strip out data we decided to eliminate + } + + // optimize loads and stores + void spirvbin_t::optLoadStore() + { + idset_t fnLocalVars; // candidates for removal (only locals) + idmap_t idMap; // Map of load result IDs to what they load + blockmap_t blockMap; // Map of IDs to blocks they first appear in + int blockNum = 0; // block count, to avoid crossing flow control + + // Find all the function local pointers stored at most once, and not via access chains + process( + [&](spv::Op opCode, unsigned start) { + const int wordCount = asWordCount(start); + + // Count blocks, so we can avoid crossing flow control + if (isFlowCtrl(opCode)) + ++blockNum; + + // Add local variables to the map + if ((opCode == spv::OpVariable && spv[start+3] == spv::StorageClassFunction && asWordCount(start) == 4)) { + fnLocalVars.insert(asId(start+2)); + return true; + } + + // Ignore process vars referenced via access chain + if ((opCode == spv::OpAccessChain || opCode == spv::OpInBoundsAccessChain) && fnLocalVars.count(asId(start+3)) > 0) { + fnLocalVars.erase(asId(start+3)); + idMap.erase(asId(start+3)); + return true; + } + + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) { + const spv::Id varId = asId(start+3); + + // Avoid loads before stores + if (idMap.find(varId) == idMap.end()) { + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // don't do for volatile references + if (wordCount > 4 && (spv[start+4] & spv::MemoryAccessVolatileMask)) { + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // Handle flow control + if (blockMap.find(varId) == blockMap.end()) { + blockMap[varId] = blockNum; // track block we found it in. + } else if (blockMap[varId] != blockNum) { + fnLocalVars.erase(varId); // Ignore if crosses flow control + idMap.erase(varId); + } + + return true; + } + + if (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) { + const spv::Id varId = asId(start+1); + + if (idMap.find(varId) == idMap.end()) { + idMap[varId] = asId(start+2); + } else { + // Remove if it has more than one store to the same pointer + fnLocalVars.erase(varId); + idMap.erase(varId); + } + + // don't do for volatile references + if (wordCount > 3 && (spv[start+3] & spv::MemoryAccessVolatileMask)) { + fnLocalVars.erase(asId(start+3)); + idMap.erase(asId(start+3)); + } + + // Handle flow control + if (blockMap.find(varId) == blockMap.end()) { + blockMap[varId] = blockNum; // track block we found it in. + } else if (blockMap[varId] != blockNum) { + fnLocalVars.erase(varId); // Ignore if crosses flow control + idMap.erase(varId); + } + + return true; + } + + return false; + }, + + // If local var id used anywhere else, don't eliminate + [&](spv::Id& id) { + if (fnLocalVars.count(id) > 0) { + fnLocalVars.erase(id); + idMap.erase(id); + } + } + ); + + if (errorLatch) + return; + + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) + idMap[asId(start+2)] = idMap[asId(start+3)]; + return false; + }, + op_fn_nop); + + if (errorLatch) + return; + + // Chase replacements to their origins, in case there is a chain such as: + // 2 = store 1 + // 3 = load 2 + // 4 = store 3 + // 5 = load 4 + // We want to replace uses of 5 with 1. + for (const auto& idPair : idMap) { + spv::Id id = idPair.first; + while (idMap.find(id) != idMap.end()) // Chase to end of chain + id = idMap[id]; + + idMap[idPair.first] = id; // replace with final result + } + + // Remove the load/store/variables for the ones we've discovered + process( + [&](spv::Op opCode, unsigned start) { + if ((opCode == spv::OpLoad && fnLocalVars.count(asId(start+3)) > 0) || + (opCode == spv::OpStore && fnLocalVars.count(asId(start+1)) > 0) || + (opCode == spv::OpVariable && fnLocalVars.count(asId(start+2)) > 0)) { + + stripInst(start); + return true; + } + + return false; + }, + + [&](spv::Id& id) { + if (idMap.find(id) != idMap.end()) id = idMap[id]; + } + ); + + if (errorLatch) + return; + + strip(); // strip out data we decided to eliminate + } + + // remove bodies of uncalled functions + void spirvbin_t::dceFuncs() + { + msg(3, 2, std::string("Removing Dead Functions: ")); + + // TODO: There are more efficient ways to do this. + bool changed = true; + + while (changed) { + changed = false; + + for (auto fn = fnPos.begin(); fn != fnPos.end(); ) { + if (fn->first == entryPoint) { // don't DCE away the entry point! + ++fn; + continue; + } + + const auto call_it = fnCalls.find(fn->first); + + if (call_it == fnCalls.end() || call_it->second == 0) { + changed = true; + stripRange.push_back(fn->second); + + // decrease counts of called functions + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::Op::OpFunctionCall) { + const auto call_it = fnCalls.find(asId(start + 3)); + if (call_it != fnCalls.end()) { + if (--call_it->second <= 0) + fnCalls.erase(call_it); + } + } + + return true; + }, + op_fn_nop, + fn->second.first, + fn->second.second); + + if (errorLatch) + return; + + fn = fnPos.erase(fn); + } else ++fn; + } + } + } + + // remove unused function variables + decorations + void spirvbin_t::dceVars() + { + msg(3, 2, std::string("DCE Vars: ")); + + std::unordered_map varUseCount; + + // Count function variable use + process( + [&](spv::Op opCode, unsigned start) { + if (opCode == spv::OpVariable) { + ++varUseCount[asId(start+2)]; + return true; + } else if (opCode == spv::OpEntryPoint) { + const int wordCount = asWordCount(start); + for (int i = 4; i < wordCount; i++) { + ++varUseCount[asId(start+i)]; + } + return true; + } else + return false; + }, + + [&](spv::Id& id) { if (varUseCount[id]) ++varUseCount[id]; } + ); + + if (errorLatch) + return; + + // Remove single-use function variables + associated decorations and names + process( + [&](spv::Op opCode, unsigned start) { + spv::Id id = spv::NoResult; + if (opCode == spv::OpVariable) + id = asId(start+2); + if (opCode == spv::OpDecorate || opCode == spv::OpName) + id = asId(start+1); + + if (id != spv::NoResult && varUseCount[id] == 1) + stripInst(start); + + return true; + }, + op_fn_nop); + } + + // remove unused types + void spirvbin_t::dceTypes() + { + std::vector isType(bound(), false); + + // for speed, make O(1) way to get to type query (map is log(n)) + for (const auto typeStart : typeConstPos) + isType[asTypeConstId(typeStart)] = true; + + std::unordered_map typeUseCount; + + // This is not the most efficient algorithm, but this is an offline tool, and + // it's easy to write this way. Can be improved opportunistically if needed. + bool changed = true; + while (changed) { + changed = false; + strip(); + typeUseCount.clear(); + + // Count total type usage + process(inst_fn_nop, + [&](spv::Id& id) { if (isType[id]) ++typeUseCount[id]; } + ); + + if (errorLatch) + return; + + // Remove single reference types + for (const auto typeStart : typeConstPos) { + const spv::Id typeId = asTypeConstId(typeStart); + if (typeUseCount[typeId] == 1) { + changed = true; + --typeUseCount[typeId]; + stripInst(typeStart); + } + } + + if (errorLatch) + return; + } + } + +#ifdef NOTDEF + bool spirvbin_t::matchType(const spirvbin_t::globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const + { + // Find the local type id "lt" and global type id "gt" + const auto lt_it = typeConstPosR.find(lt); + if (lt_it == typeConstPosR.end()) + return false; + + const auto typeStart = lt_it->second; + + // Search for entry in global table + const auto gtype = globalTypes.find(gt); + if (gtype == globalTypes.end()) + return false; + + const auto& gdata = gtype->second; + + // local wordcount and opcode + const int wordCount = asWordCount(typeStart); + const spv::Op opCode = asOpCode(typeStart); + + // no type match if opcodes don't match, or operand count doesn't match + if (opCode != opOpCode(gdata[0]) || wordCount != opWordCount(gdata[0])) + return false; + + const unsigned numOperands = wordCount - 2; // all types have a result + + const auto cmpIdRange = [&](range_t range) { + for (int x=range.first; xsecond; + } + + // Hash types to canonical values. This can return ID collisions (it's a bit + // inevitable): it's up to the caller to handle that gracefully. + std::uint32_t spirvbin_t::hashType(unsigned typeStart) const + { + const unsigned wordCount = asWordCount(typeStart); + const spv::Op opCode = asOpCode(typeStart); + + switch (opCode) { + case spv::OpTypeVoid: return 0; + case spv::OpTypeBool: return 1; + case spv::OpTypeInt: return 3 + (spv[typeStart+3]); + case spv::OpTypeFloat: return 5; + case spv::OpTypeVector: + return 6 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + case spv::OpTypeMatrix: + return 30 + hashType(idPos(spv[typeStart+2])) * (spv[typeStart+3] - 1); + case spv::OpTypeImage: + return 120 + hashType(idPos(spv[typeStart+2])) + + spv[typeStart+3] + // dimensionality + spv[typeStart+4] * 8 * 16 + // depth + spv[typeStart+5] * 4 * 16 + // arrayed + spv[typeStart+6] * 2 * 16 + // multisampled + spv[typeStart+7] * 1 * 16; // format + case spv::OpTypeSampler: + return 500; + case spv::OpTypeSampledImage: + return 502; + case spv::OpTypeArray: + return 501 + hashType(idPos(spv[typeStart+2])) * spv[typeStart+3]; + case spv::OpTypeRuntimeArray: + return 5000 + hashType(idPos(spv[typeStart+2])); + case spv::OpTypeStruct: + { + std::uint32_t hash = 10000; + for (unsigned w=2; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + + case spv::OpTypeOpaque: return 6000 + spv[typeStart+2]; + case spv::OpTypePointer: return 100000 + hashType(idPos(spv[typeStart+3])); + case spv::OpTypeFunction: + { + std::uint32_t hash = 200000; + for (unsigned w=2; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + + case spv::OpTypeEvent: return 300000; + case spv::OpTypeDeviceEvent: return 300001; + case spv::OpTypeReserveId: return 300002; + case spv::OpTypeQueue: return 300003; + case spv::OpTypePipe: return 300004; + case spv::OpConstantTrue: return 300007; + case spv::OpConstantFalse: return 300008; + case spv::OpConstantComposite: + { + std::uint32_t hash = 300011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * hashType(idPos(spv[typeStart+w])); + return hash; + } + case spv::OpConstant: + { + std::uint32_t hash = 400011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * spv[typeStart+w]; + return hash; + } + case spv::OpConstantNull: + { + std::uint32_t hash = 500009 + hashType(idPos(spv[typeStart+1])); + return hash; + } + case spv::OpConstantSampler: + { + std::uint32_t hash = 600011 + hashType(idPos(spv[typeStart+1])); + for (unsigned w=3; w < wordCount; ++w) + hash += w * spv[typeStart+w]; + return hash; + } + + default: + error("unknown type opcode"); + return 0; + } + } + + void spirvbin_t::mapTypeConst() + { + globaltypes_t globalTypeMap; + + msg(3, 2, std::string("Remapping Consts & Types: ")); + + static const std::uint32_t softTypeIdLimit = 3011; // small prime. TODO: get from options + static const std::uint32_t firstMappedID = 8; // offset into ID space + + for (auto& typeStart : typeConstPos) { + const spv::Id resId = asTypeConstId(typeStart); + const std::uint32_t hashval = hashType(typeStart); + + if (errorLatch) + return; + + if (isOldIdUnmapped(resId)) { + localId(resId, nextUnusedId(hashval % softTypeIdLimit + firstMappedID)); + if (errorLatch) + return; + } + } + } + + // Strip a single binary by removing ranges given in stripRange + void spirvbin_t::strip() + { + if (stripRange.empty()) // nothing to do + return; + + // Sort strip ranges in order of traversal + std::sort(stripRange.begin(), stripRange.end()); + + // Allocate a new binary big enough to hold old binary + // We'll step this iterator through the strip ranges as we go through the binary + auto strip_it = stripRange.begin(); + + int strippedPos = 0; + for (unsigned word = 0; word < unsigned(spv.size()); ++word) { + while (strip_it != stripRange.end() && word >= strip_it->second) + ++strip_it; + + if (strip_it == stripRange.end() || word < strip_it->first || word >= strip_it->second) + spv[strippedPos++] = spv[word]; + } + + spv.resize(strippedPos); + stripRange.clear(); + + buildLocalMaps(); + } + + // Strip a single binary by removing ranges given in stripRange + void spirvbin_t::remap(std::uint32_t opts) + { + options = opts; + + // Set up opcode tables from SpvDoc + spv::Parameterize(); + + validate(); // validate header + buildLocalMaps(); // build ID maps + + msg(3, 4, std::string("ID bound: ") + std::to_string(bound())); + + if (options & STRIP) stripDebug(); + if (errorLatch) return; + + strip(); // strip out data we decided to eliminate + if (errorLatch) return; + + if (options & OPT_LOADSTORE) optLoadStore(); + if (errorLatch) return; + + if (options & OPT_FWD_LS) forwardLoadStores(); + if (errorLatch) return; + + if (options & DCE_FUNCS) dceFuncs(); + if (errorLatch) return; + + if (options & DCE_VARS) dceVars(); + if (errorLatch) return; + + if (options & DCE_TYPES) dceTypes(); + if (errorLatch) return; + + strip(); // strip out data we decided to eliminate + if (errorLatch) return; + + stripDeadRefs(); // remove references to things we DCEed + if (errorLatch) return; + + // after the last strip, we must clean any debug info referring to now-deleted data + + if (options & MAP_TYPES) mapTypeConst(); + if (errorLatch) return; + + if (options & MAP_NAMES) mapNames(); + if (errorLatch) return; + + if (options & MAP_FUNCS) mapFnBodies(); + if (errorLatch) return; + + if (options & MAP_ALL) { + mapRemainder(); // map any unmapped IDs + if (errorLatch) return; + + applyMap(); // Now remap each shader to the new IDs we've come up with + if (errorLatch) return; + } + } + + // remap from a memory image + void spirvbin_t::remap(std::vector& in_spv, std::uint32_t opts) + { + spv.swap(in_spv); + remap(opts); + spv.swap(in_spv); + } + +} // namespace SPV + +#endif // defined (use_cpp11) + diff --git a/third_party/glslang/SPIRV/SPVRemapper.h b/third_party/glslang/SPIRV/SPVRemapper.h new file mode 100644 index 0000000..d6b9c34 --- /dev/null +++ b/third_party/glslang/SPIRV/SPVRemapper.h @@ -0,0 +1,304 @@ +// +// Copyright (C) 2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef SPIRVREMAPPER_H +#define SPIRVREMAPPER_H + +#include +#include +#include +#include + +namespace spv { + +// MSVC defines __cplusplus as an older value, even when it supports almost all of 11. +// We handle that here by making our own symbol. +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1700) +# define use_cpp11 1 +#endif + +class spirvbin_base_t +{ +public: + enum Options { + NONE = 0, + STRIP = (1<<0), + MAP_TYPES = (1<<1), + MAP_NAMES = (1<<2), + MAP_FUNCS = (1<<3), + DCE_FUNCS = (1<<4), + DCE_VARS = (1<<5), + DCE_TYPES = (1<<6), + OPT_LOADSTORE = (1<<7), + OPT_FWD_LS = (1<<8), // EXPERIMENTAL: PRODUCES INVALID SCHEMA-0 SPIRV + MAP_ALL = (MAP_TYPES | MAP_NAMES | MAP_FUNCS), + DCE_ALL = (DCE_FUNCS | DCE_VARS | DCE_TYPES), + OPT_ALL = (OPT_LOADSTORE), + + ALL_BUT_STRIP = (MAP_ALL | DCE_ALL | OPT_ALL), + DO_EVERYTHING = (STRIP | ALL_BUT_STRIP) + }; +}; + +} // namespace SPV + +#if !defined (use_cpp11) +#include +#include + +namespace spv { +class spirvbin_t : public spirvbin_base_t +{ +public: + spirvbin_t(int /*verbose = 0*/) { } + + void remap(std::vector& /*spv*/, unsigned int /*opts = 0*/) + { + printf("Tool not compiled for C++11, which is required for SPIR-V remapping.\n"); + exit(5); + } +}; + +} // namespace SPV + +#else // defined (use_cpp11) + +#include +#include +#include +#include +#include +#include +#include + +#include "spirv.hpp" +#include "spvIR.h" + +namespace spv { + +// class to hold SPIR-V binary data for remapping, DCE, and debug stripping +class spirvbin_t : public spirvbin_base_t +{ +public: + spirvbin_t(int verbose = 0) : entryPoint(spv::NoResult), largestNewId(0), verbose(verbose), errorLatch(false) + { } + + virtual ~spirvbin_t() { } + + // remap on an existing binary in memory + void remap(std::vector& spv, std::uint32_t opts = DO_EVERYTHING); + + // Type for error/log handler functions + typedef std::function errorfn_t; + typedef std::function logfn_t; + + // Register error/log handling functions (can be lambda fn / functor / etc) + static void registerErrorHandler(errorfn_t handler) { errorHandler = handler; } + static void registerLogHandler(logfn_t handler) { logHandler = handler; } + +protected: + // This can be overridden to provide other message behavior if needed + virtual void msg(int minVerbosity, int indent, const std::string& txt) const; + +private: + // Local to global, or global to local ID map + typedef std::unordered_map idmap_t; + typedef std::unordered_set idset_t; + typedef std::unordered_map blockmap_t; + + void remap(std::uint32_t opts = DO_EVERYTHING); + + // Map of names to IDs + typedef std::unordered_map namemap_t; + + typedef std::uint32_t spirword_t; + + typedef std::pair range_t; + typedef std::function idfn_t; + typedef std::function instfn_t; + + // Special Values for ID map: + static const spv::Id unmapped; // unchanged from default value + static const spv::Id unused; // unused ID + static const int header_size; // SPIR header = 5 words + + class id_iterator_t; + + // For mapping type entries between different shaders + typedef std::vector typeentry_t; + typedef std::map globaltypes_t; + + // A set that preserves position order, and a reverse map + typedef std::set posmap_t; + typedef std::unordered_map posmap_rev_t; + + // Maps and ID to the size of its base type, if known. + typedef std::unordered_map typesize_map_t; + + // handle error + void error(const std::string& txt) const { errorLatch = true; errorHandler(txt); } + + bool isConstOp(spv::Op opCode) const; + bool isTypeOp(spv::Op opCode) const; + bool isStripOp(spv::Op opCode) const; + bool isFlowCtrl(spv::Op opCode) const; + range_t literalRange(spv::Op opCode) const; + range_t typeRange(spv::Op opCode) const; + range_t constRange(spv::Op opCode) const; + unsigned typeSizeInWords(spv::Id id) const; + unsigned idTypeSizeInWords(spv::Id id) const; + + spv::Id& asId(unsigned word) { return spv[word]; } + const spv::Id& asId(unsigned word) const { return spv[word]; } + spv::Op asOpCode(unsigned word) const { return opOpCode(spv[word]); } + std::uint32_t asOpCodeHash(unsigned word); + spv::Decoration asDecoration(unsigned word) const { return spv::Decoration(spv[word]); } + unsigned asWordCount(unsigned word) const { return opWordCount(spv[word]); } + spv::Id asTypeConstId(unsigned word) const { return asId(word + (isTypeOp(asOpCode(word)) ? 1 : 2)); } + unsigned idPos(spv::Id id) const; + + static unsigned opWordCount(spirword_t data) { return data >> spv::WordCountShift; } + static spv::Op opOpCode(spirword_t data) { return spv::Op(data & spv::OpCodeMask); } + + // Header access & set methods + spirword_t magic() const { return spv[0]; } // return magic number + spirword_t bound() const { return spv[3]; } // return Id bound from header + spirword_t bound(spirword_t b) { return spv[3] = b; } + spirword_t genmagic() const { return spv[2]; } // generator magic + spirword_t genmagic(spirword_t m) { return spv[2] = m; } + spirword_t schemaNum() const { return spv[4]; } // schema number from header + + // Mapping fns: get + spv::Id localId(spv::Id id) const { return idMapL[id]; } + + // Mapping fns: set + inline spv::Id localId(spv::Id id, spv::Id newId); + void countIds(spv::Id id); + + // Return next unused new local ID. + // NOTE: boost::dynamic_bitset would be more efficient due to find_next(), + // which std::vector doens't have. + inline spv::Id nextUnusedId(spv::Id id); + + void buildLocalMaps(); + std::string literalString(unsigned word) const; // Return literal as a std::string + int literalStringWords(const std::string& str) const { return (int(str.size())+4)/4; } + + bool isNewIdMapped(spv::Id newId) const { return isMapped(newId); } + bool isOldIdUnmapped(spv::Id oldId) const { return localId(oldId) == unmapped; } + bool isOldIdUnused(spv::Id oldId) const { return localId(oldId) == unused; } + bool isOldIdMapped(spv::Id oldId) const { return !isOldIdUnused(oldId) && !isOldIdUnmapped(oldId); } + bool isFunction(spv::Id oldId) const { return fnPos.find(oldId) != fnPos.end(); } + + // bool matchType(const globaltypes_t& globalTypes, spv::Id lt, spv::Id gt) const; + // spv::Id findType(const globaltypes_t& globalTypes, spv::Id lt) const; + std::uint32_t hashType(unsigned typeStart) const; + + spirvbin_t& process(instfn_t, idfn_t, unsigned begin = 0, unsigned end = 0); + int processInstruction(unsigned word, instfn_t, idfn_t); + + void validate() const; + void mapTypeConst(); + void mapFnBodies(); + void optLoadStore(); + void dceFuncs(); + void dceVars(); + void dceTypes(); + void mapNames(); + void foldIds(); // fold IDs to smallest space + void forwardLoadStores(); // load store forwarding (EXPERIMENTAL) + void offsetIds(); // create relative offset IDs + + void applyMap(); // remap per local name map + void mapRemainder(); // map any IDs we haven't touched yet + void stripDebug(); // strip all debug info + void stripDeadRefs(); // strips debug info for now-dead references after DCE + void strip(); // remove debug symbols + + std::vector spv; // SPIR words + + namemap_t nameMap; // ID names from OpName + + // Since we want to also do binary ops, we can't use std::vector. we could use + // boost::dynamic_bitset, but we're trying to avoid a boost dependency. + typedef std::uint64_t bits_t; + std::vector mapped; // which new IDs have been mapped + static const int mBits = sizeof(bits_t) * 4; + + bool isMapped(spv::Id id) const { return id < maxMappedId() && ((mapped[id/mBits] & (1LL<<(id%mBits))) != 0); } + void setMapped(spv::Id id) { resizeMapped(id); mapped[id/mBits] |= (1LL<<(id%mBits)); } + void resizeMapped(spv::Id id) { if (id >= maxMappedId()) mapped.resize(id/mBits+1, 0); } + size_t maxMappedId() const { return mapped.size() * mBits; } + + // Add a strip range for a given instruction starting at 'start' + // Note: avoiding brace initializers to please older versions os MSVC. + void stripInst(unsigned start) { stripRange.push_back(range_t(start, start + asWordCount(start))); } + + // Function start and end. use unordered_map because we'll have + // many fewer functions than IDs. + std::unordered_map fnPos; + + // Which functions are called, anywhere in the module, with a call count + std::unordered_map fnCalls; + + posmap_t typeConstPos; // word positions that define types & consts (ordered) + posmap_rev_t idPosR; // reverse map from IDs to positions + typesize_map_t idTypeSizeMap; // maps each ID to its type size, if known. + + std::vector idMapL; // ID {M}ap from {L}ocal to {G}lobal IDs + + spv::Id entryPoint; // module entry point + spv::Id largestNewId; // biggest new ID we have mapped anything to + + // Sections of the binary to strip, given as [begin,end) + std::vector stripRange; + + // processing options: + std::uint32_t options; + int verbose; // verbosity level + + // Error latch: this is set if the error handler is ever executed. It would be better to + // use a try/catch block and throw, but that's not desired for certain environments, so + // this is the alternative. + mutable bool errorLatch; + + static errorfn_t errorHandler; + static logfn_t logHandler; +}; + +} // namespace SPV + +#endif // defined (use_cpp11) +#endif // SPIRVREMAPPER_H diff --git a/third_party/glslang/SPIRV/SpvBuilder.cpp b/third_party/glslang/SPIRV/SpvBuilder.cpp new file mode 100644 index 0000000..85ffe90 --- /dev/null +++ b/third_party/glslang/SPIRV/SpvBuilder.cpp @@ -0,0 +1,3237 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Helper for making SPIR-V IR. Generally, this is documented in the header +// SpvBuilder.h. +// + +#include +#include + +#include +#include + +#include "SpvBuilder.h" + +#ifndef GLSLANG_WEB +#include "hex_float.h" +#endif + +#ifndef _WIN32 + #include +#endif + +namespace spv { + +Builder::Builder(unsigned int spvVersion, unsigned int magicNumber, SpvBuildLogger* buildLogger) : + spvVersion(spvVersion), + source(SourceLanguageUnknown), + sourceVersion(0), + sourceFileStringId(NoResult), + currentLine(0), + currentFile(nullptr), + emitOpLines(false), + addressModel(AddressingModelLogical), + memoryModel(MemoryModelGLSL450), + builderNumber(magicNumber), + buildPoint(0), + uniqueId(0), + entryPointFunction(0), + generatingOpCodeForSpecConst(false), + logger(buildLogger) +{ + clearAccessChain(); +} + +Builder::~Builder() +{ +} + +Id Builder::import(const char* name) +{ + Instruction* import = new Instruction(getUniqueId(), NoType, OpExtInstImport); + import->addStringOperand(name); + module.mapInstruction(import); + + imports.push_back(std::unique_ptr(import)); + return import->getResultId(); +} + +// Emit instruction for non-filename-based #line directives (ie. no filename +// seen yet): emit an OpLine if we've been asked to emit OpLines and the line +// number has changed since the last time, and is a valid line number. +void Builder::setLine(int lineNum) +{ + if (lineNum != 0 && lineNum != currentLine) { + currentLine = lineNum; + if (emitOpLines) + addLine(sourceFileStringId, currentLine, 0); + } +} + +// If no filename, do non-filename-based #line emit. Else do filename-based emit. +// Emit OpLine if we've been asked to emit OpLines and the line number or filename +// has changed since the last time, and line number is valid. +void Builder::setLine(int lineNum, const char* filename) +{ + if (filename == nullptr) { + setLine(lineNum); + return; + } + if ((lineNum != 0 && lineNum != currentLine) || currentFile == nullptr || + strncmp(filename, currentFile, strlen(currentFile) + 1) != 0) { + currentLine = lineNum; + currentFile = filename; + if (emitOpLines) { + spv::Id strId = getStringId(filename); + addLine(strId, currentLine, 0); + } + } +} + +void Builder::addLine(Id fileName, int lineNum, int column) +{ + Instruction* line = new Instruction(OpLine); + line->addIdOperand(fileName); + line->addImmediateOperand(lineNum); + line->addImmediateOperand(column); + buildPoint->addInstruction(std::unique_ptr(line)); +} + +// For creating new groupedTypes (will return old type if the requested one was already made). +Id Builder::makeVoidType() +{ + Instruction* type; + if (groupedTypes[OpTypeVoid].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeVoid); + groupedTypes[OpTypeVoid].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else + type = groupedTypes[OpTypeVoid].back(); + + return type->getResultId(); +} + +Id Builder::makeBoolType() +{ + Instruction* type; + if (groupedTypes[OpTypeBool].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeBool); + groupedTypes[OpTypeBool].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else + type = groupedTypes[OpTypeBool].back(); + + return type->getResultId(); +} + +Id Builder::makeSamplerType() +{ + Instruction* type; + if (groupedTypes[OpTypeSampler].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeSampler); + groupedTypes[OpTypeSampler].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else + type = groupedTypes[OpTypeSampler].back(); + + return type->getResultId(); +} + +Id Builder::makePointer(StorageClass storageClass, Id pointee) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)storageClass && + type->getIdOperand(1) == pointee) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypePointer); + type->addImmediateOperand(storageClass); + type->addIdOperand(pointee); + groupedTypes[OpTypePointer].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeForwardPointer(StorageClass storageClass) +{ + // Caching/uniquifying doesn't work here, because we don't know the + // pointee type and there can be multiple forward pointers of the same + // storage type. Somebody higher up in the stack must keep track. + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeForwardPointer); + type->addImmediateOperand(storageClass); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makePointerFromForwardPointer(StorageClass storageClass, Id forwardPointerType, Id pointee) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)storageClass && + type->getIdOperand(1) == pointee) + return type->getResultId(); + } + + type = new Instruction(forwardPointerType, NoType, OpTypePointer); + type->addImmediateOperand(storageClass); + type->addIdOperand(pointee); + groupedTypes[OpTypePointer].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeIntegerType(int width, bool hasSign) +{ +#ifdef GLSLANG_WEB + assert(width == 32); + width = 32; +#endif + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeInt].size(); ++t) { + type = groupedTypes[OpTypeInt][t]; + if (type->getImmediateOperand(0) == (unsigned)width && + type->getImmediateOperand(1) == (hasSign ? 1u : 0u)) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeInt); + type->addImmediateOperand(width); + type->addImmediateOperand(hasSign ? 1 : 0); + groupedTypes[OpTypeInt].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // deal with capabilities + switch (width) { + case 8: + case 16: + // these are currently handled by storage-type declarations and post processing + break; + case 64: + addCapability(CapabilityInt64); + break; + default: + break; + } + + return type->getResultId(); +} + +Id Builder::makeFloatType(int width) +{ +#ifdef GLSLANG_WEB + assert(width == 32); + width = 32; +#endif + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeFloat].size(); ++t) { + type = groupedTypes[OpTypeFloat][t]; + if (type->getImmediateOperand(0) == (unsigned)width) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeFloat); + type->addImmediateOperand(width); + groupedTypes[OpTypeFloat].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + // deal with capabilities + switch (width) { + case 16: + // currently handled by storage-type declarations and post processing + break; + case 64: + addCapability(CapabilityFloat64); + break; + default: + break; + } + + return type->getResultId(); +} + +// Make a struct without checking for duplication. +// See makeStructResultType() for non-decorated structs +// needed as the result of some instructions, which does +// check for duplicates. +Id Builder::makeStructType(const std::vector& members, const char* name) +{ + // Don't look for previous one, because in the general case, + // structs can be duplicated except for decorations. + + // not found, make it + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeStruct); + for (int op = 0; op < (int)members.size(); ++op) + type->addIdOperand(members[op]); + groupedTypes[OpTypeStruct].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + addName(type->getResultId(), name); + + return type->getResultId(); +} + +// Make a struct for the simple results of several instructions, +// checking for duplication. +Id Builder::makeStructResultType(Id type0, Id type1) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeStruct].size(); ++t) { + type = groupedTypes[OpTypeStruct][t]; + if (type->getNumOperands() != 2) + continue; + if (type->getIdOperand(0) != type0 || + type->getIdOperand(1) != type1) + continue; + return type->getResultId(); + } + + // not found, make it + std::vector members; + members.push_back(type0); + members.push_back(type1); + + return makeStructType(members, "ResType"); +} + +Id Builder::makeVectorType(Id component, int size) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeVector].size(); ++t) { + type = groupedTypes[OpTypeVector][t]; + if (type->getIdOperand(0) == component && + type->getImmediateOperand(1) == (unsigned)size) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeVector); + type->addIdOperand(component); + type->addImmediateOperand(size); + groupedTypes[OpTypeVector].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeMatrixType(Id component, int cols, int rows) +{ + assert(cols <= maxMatrixSize && rows <= maxMatrixSize); + + Id column = makeVectorType(component, rows); + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeMatrix].size(); ++t) { + type = groupedTypes[OpTypeMatrix][t]; + if (type->getIdOperand(0) == column && + type->getImmediateOperand(1) == (unsigned)cols) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeMatrix); + type->addIdOperand(column); + type->addImmediateOperand(cols); + groupedTypes[OpTypeMatrix].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeCooperativeMatrixNV].size(); ++t) { + type = groupedTypes[OpTypeCooperativeMatrixNV][t]; + if (type->getIdOperand(0) == component && + type->getIdOperand(1) == scope && + type->getIdOperand(2) == rows && + type->getIdOperand(3) == cols) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeCooperativeMatrixNV); + type->addIdOperand(component); + type->addIdOperand(scope); + type->addIdOperand(rows); + type->addIdOperand(cols); + groupedTypes[OpTypeCooperativeMatrixNV].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + + +// TODO: performance: track arrays per stride +// If a stride is supplied (non-zero) make an array. +// If no stride (0), reuse previous array types. +// 'size' is an Id of a constant or specialization constant of the array size +Id Builder::makeArrayType(Id element, Id sizeId, int stride) +{ + Instruction* type; + if (stride == 0) { + // try to find existing type + for (int t = 0; t < (int)groupedTypes[OpTypeArray].size(); ++t) { + type = groupedTypes[OpTypeArray][t]; + if (type->getIdOperand(0) == element && + type->getIdOperand(1) == sizeId) + return type->getResultId(); + } + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeArray); + type->addIdOperand(element); + type->addIdOperand(sizeId); + groupedTypes[OpTypeArray].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeRuntimeArray(Id element) +{ + Instruction* type = new Instruction(getUniqueId(), NoType, OpTypeRuntimeArray); + type->addIdOperand(element); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeFunctionType(Id returnType, const std::vector& paramTypes) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeFunction].size(); ++t) { + type = groupedTypes[OpTypeFunction][t]; + if (type->getIdOperand(0) != returnType || (int)paramTypes.size() != type->getNumOperands() - 1) + continue; + bool mismatch = false; + for (int p = 0; p < (int)paramTypes.size(); ++p) { + if (paramTypes[p] != type->getIdOperand(p + 1)) { + mismatch = true; + break; + } + } + if (! mismatch) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeFunction); + type->addIdOperand(returnType); + for (int p = 0; p < (int)paramTypes.size(); ++p) + type->addIdOperand(paramTypes[p]); + groupedTypes[OpTypeFunction].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +Id Builder::makeImageType(Id sampledType, Dim dim, bool depth, bool arrayed, bool ms, unsigned sampled, + ImageFormat format) +{ + assert(sampled == 1 || sampled == 2); + + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeImage].size(); ++t) { + type = groupedTypes[OpTypeImage][t]; + if (type->getIdOperand(0) == sampledType && + type->getImmediateOperand(1) == (unsigned int)dim && + type->getImmediateOperand(2) == ( depth ? 1u : 0u) && + type->getImmediateOperand(3) == (arrayed ? 1u : 0u) && + type->getImmediateOperand(4) == ( ms ? 1u : 0u) && + type->getImmediateOperand(5) == sampled && + type->getImmediateOperand(6) == (unsigned int)format) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeImage); + type->addIdOperand(sampledType); + type->addImmediateOperand( dim); + type->addImmediateOperand( depth ? 1 : 0); + type->addImmediateOperand(arrayed ? 1 : 0); + type->addImmediateOperand( ms ? 1 : 0); + type->addImmediateOperand(sampled); + type->addImmediateOperand((unsigned int)format); + + groupedTypes[OpTypeImage].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + +#ifndef GLSLANG_WEB + // deal with capabilities + switch (dim) { + case DimBuffer: + if (sampled == 1) + addCapability(CapabilitySampledBuffer); + else + addCapability(CapabilityImageBuffer); + break; + case Dim1D: + if (sampled == 1) + addCapability(CapabilitySampled1D); + else + addCapability(CapabilityImage1D); + break; + case DimCube: + if (arrayed) { + if (sampled == 1) + addCapability(CapabilitySampledCubeArray); + else + addCapability(CapabilityImageCubeArray); + } + break; + case DimRect: + if (sampled == 1) + addCapability(CapabilitySampledRect); + else + addCapability(CapabilityImageRect); + break; + case DimSubpassData: + addCapability(CapabilityInputAttachment); + break; + default: + break; + } + + if (ms) { + if (sampled == 2) { + // Images used with subpass data are not storage + // images, so don't require the capability for them. + if (dim != Dim::DimSubpassData) + addCapability(CapabilityStorageImageMultisample); + if (arrayed) + addCapability(CapabilityImageMSArray); + } + } +#endif + + return type->getResultId(); +} + +Id Builder::makeSampledImageType(Id imageType) +{ + // try to find it + Instruction* type; + for (int t = 0; t < (int)groupedTypes[OpTypeSampledImage].size(); ++t) { + type = groupedTypes[OpTypeSampledImage][t]; + if (type->getIdOperand(0) == imageType) + return type->getResultId(); + } + + // not found, make it + type = new Instruction(getUniqueId(), NoType, OpTypeSampledImage); + type->addIdOperand(imageType); + + groupedTypes[OpTypeSampledImage].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + + return type->getResultId(); +} + +#ifndef GLSLANG_WEB +Id Builder::makeAccelerationStructureType() +{ + Instruction *type; + if (groupedTypes[OpTypeAccelerationStructureKHR].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeAccelerationStructureKHR); + groupedTypes[OpTypeAccelerationStructureKHR].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else { + type = groupedTypes[OpTypeAccelerationStructureKHR].back(); + } + + return type->getResultId(); +} + +Id Builder::makeRayQueryType() +{ + Instruction *type; + if (groupedTypes[OpTypeRayQueryProvisionalKHR].size() == 0) { + type = new Instruction(getUniqueId(), NoType, OpTypeRayQueryProvisionalKHR); + groupedTypes[OpTypeRayQueryProvisionalKHR].push_back(type); + constantsTypesGlobals.push_back(std::unique_ptr(type)); + module.mapInstruction(type); + } else { + type = groupedTypes[OpTypeRayQueryProvisionalKHR].back(); + } + + return type->getResultId(); +} +#endif + +Id Builder::getDerefTypeId(Id resultId) const +{ + Id typeId = getTypeId(resultId); + assert(isPointerType(typeId)); + + return module.getInstruction(typeId)->getIdOperand(1); +} + +Op Builder::getMostBasicTypeClass(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + return getMostBasicTypeClass(instr->getIdOperand(0)); + case OpTypePointer: + return getMostBasicTypeClass(instr->getIdOperand(1)); + default: + return typeClass; + } +} + +int Builder::getNumTypeConstituents(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + switch (instr->getOpCode()) + { + case OpTypeBool: + case OpTypeInt: + case OpTypeFloat: + case OpTypePointer: + return 1; + case OpTypeVector: + case OpTypeMatrix: + return instr->getImmediateOperand(1); + case OpTypeArray: + { + Id lengthId = instr->getIdOperand(1); + return module.getInstruction(lengthId)->getImmediateOperand(0); + } + case OpTypeStruct: + return instr->getNumOperands(); + case OpTypeCooperativeMatrixNV: + // has only one constituent when used with OpCompositeConstruct. + return 1; + default: + assert(0); + return 1; + } +} + +// Return the lowest-level type of scalar that an homogeneous composite is made out of. +// Typically, this is just to find out if something is made out of ints or floats. +// However, it includes returning a structure, if say, it is an array of structure. +Id Builder::getScalarTypeId(Id typeId) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVoid: + case OpTypeBool: + case OpTypeInt: + case OpTypeFloat: + case OpTypeStruct: + return instr->getResultId(); + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + case OpTypePointer: + return getScalarTypeId(getContainedTypeId(typeId)); + default: + assert(0); + return NoResult; + } +} + +// Return the type of 'member' of a composite. +Id Builder::getContainedTypeId(Id typeId, int member) const +{ + Instruction* instr = module.getInstruction(typeId); + + Op typeClass = instr->getOpCode(); + switch (typeClass) + { + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + case OpTypeCooperativeMatrixNV: + return instr->getIdOperand(0); + case OpTypePointer: + return instr->getIdOperand(1); + case OpTypeStruct: + return instr->getIdOperand(member); + default: + assert(0); + return NoResult; + } +} + +// Return the immediately contained type of a given composite type. +Id Builder::getContainedTypeId(Id typeId) const +{ + return getContainedTypeId(typeId, 0); +} + +// Returns true if 'typeId' is or contains a scalar type declared with 'typeOp' +// of width 'width'. The 'width' is only consumed for int and float types. +// Returns false otherwise. +bool Builder::containsType(Id typeId, spv::Op typeOp, unsigned int width) const +{ + const Instruction& instr = *module.getInstruction(typeId); + + Op typeClass = instr.getOpCode(); + switch (typeClass) + { + case OpTypeInt: + case OpTypeFloat: + return typeClass == typeOp && instr.getImmediateOperand(0) == width; + case OpTypeStruct: + for (int m = 0; m < instr.getNumOperands(); ++m) { + if (containsType(instr.getIdOperand(m), typeOp, width)) + return true; + } + return false; + case OpTypePointer: + return false; + case OpTypeVector: + case OpTypeMatrix: + case OpTypeArray: + case OpTypeRuntimeArray: + return containsType(getContainedTypeId(typeId), typeOp, width); + default: + return typeClass == typeOp; + } +} + +// return true if the type is a pointer to PhysicalStorageBufferEXT or an +// array of such pointers. These require restrict/aliased decorations. +bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const +{ + const Instruction& instr = *module.getInstruction(typeId); + + Op typeClass = instr.getOpCode(); + switch (typeClass) + { + case OpTypePointer: + return getTypeStorageClass(typeId) == StorageClassPhysicalStorageBufferEXT; + case OpTypeArray: + return containsPhysicalStorageBufferOrArray(getContainedTypeId(typeId)); + default: + return false; + } +} + +// See if a scalar constant of this type has already been created, so it +// can be reused rather than duplicated. (Required by the specification). +Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value) +{ + Instruction* constant; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + if (constant->getOpCode() == opcode && + constant->getTypeId() == typeId && + constant->getImmediateOperand(0) == value) + return constant->getResultId(); + } + + return 0; +} + +// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64'). +Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2) +{ + Instruction* constant; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + if (constant->getOpCode() == opcode && + constant->getTypeId() == typeId && + constant->getImmediateOperand(0) == v1 && + constant->getImmediateOperand(1) == v2) + return constant->getResultId(); + } + + return 0; +} + +// Return true if consuming 'opcode' means consuming a constant. +// "constant" here means after final transform to executable code, +// the value consumed will be a constant, so includes specialization. +bool Builder::isConstantOpCode(Op opcode) const +{ + switch (opcode) { + case OpUndef: + case OpConstantTrue: + case OpConstantFalse: + case OpConstant: + case OpConstantComposite: + case OpConstantSampler: + case OpConstantNull: + case OpSpecConstantTrue: + case OpSpecConstantFalse: + case OpSpecConstant: + case OpSpecConstantComposite: + case OpSpecConstantOp: + return true; + default: + return false; + } +} + +// Return true if consuming 'opcode' means consuming a specialization constant. +bool Builder::isSpecConstantOpCode(Op opcode) const +{ + switch (opcode) { + case OpSpecConstantTrue: + case OpSpecConstantFalse: + case OpSpecConstant: + case OpSpecConstantComposite: + case OpSpecConstantOp: + return true; + default: + return false; + } +} + +Id Builder::makeBoolConstant(bool b, bool specConstant) +{ + Id typeId = makeBoolType(); + Instruction* constant; + Op opcode = specConstant ? (b ? OpSpecConstantTrue : OpSpecConstantFalse) : (b ? OpConstantTrue : OpConstantFalse); + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = 0; + for (int i = 0; i < (int)groupedConstants[OpTypeBool].size(); ++i) { + constant = groupedConstants[OpTypeBool][i]; + if (constant->getTypeId() == typeId && constant->getOpCode() == opcode) + existing = constant->getResultId(); + } + + if (existing) + return existing; + } + + // Make it + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeBool].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeInt, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeInt].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + + unsigned op1 = value & 0xFFFFFFFF; + unsigned op2 = value >> 32; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeInt, opcode, typeId, op1, op2); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(op1); + c->addImmediateOperand(op2); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeInt].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeFloatConstant(float f, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(32); + union { float fl; unsigned int ui; } u; + u.fl = f; + unsigned value = u.ui; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Id Builder::makeDoubleConstant(double d, bool specConstant) +{ +#ifdef GLSLANG_WEB + assert(0); + return NoResult; +#else + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(64); + union { double db; unsigned long long ull; } u; + u.db = d; + unsigned long long value = u.ull; + unsigned op1 = value & 0xFFFFFFFF; + unsigned op2 = value >> 32; + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (! specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, op1, op2); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(op1); + c->addImmediateOperand(op2); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +#endif +} + +Id Builder::makeFloat16Constant(float f16, bool specConstant) +{ +#ifdef GLSLANG_WEB + assert(0); + return NoResult; +#else + Op opcode = specConstant ? OpSpecConstant : OpConstant; + Id typeId = makeFloatType(16); + + spvutils::HexFloat> fVal(f16); + spvutils::HexFloat> f16Val(0); + fVal.castTo(f16Val, spvutils::kRoundToZero); + + unsigned value = f16Val.value().getAsFloat().get_value(); + + // See if we already made it. Applies only to regular constants, because specialization constants + // must remain distinct for the purpose of applying a SpecId decoration. + if (!specConstant) { + Id existing = findScalarConstant(OpTypeFloat, opcode, typeId, value); + if (existing) + return existing; + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + c->addImmediateOperand(value); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + groupedConstants[OpTypeFloat].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +#endif +} + +Id Builder::makeFpConstant(Id type, double d, bool specConstant) +{ +#ifdef GLSLANG_WEB + const int width = 32; + assert(width == getScalarTypeWidth(type)); +#else + const int width = getScalarTypeWidth(type); +#endif + + assert(isFloatType(type)); + + switch (width) { + case 16: + return makeFloat16Constant((float)d, specConstant); + case 32: + return makeFloatConstant((float)d, specConstant); + case 64: + return makeDoubleConstant(d, specConstant); + default: + break; + } + + assert(false); + return NoResult; +} + +Id Builder::findCompositeConstant(Op typeClass, Id typeId, const std::vector& comps) +{ + Instruction* constant = 0; + bool found = false; + for (int i = 0; i < (int)groupedConstants[typeClass].size(); ++i) { + constant = groupedConstants[typeClass][i]; + + if (constant->getTypeId() != typeId) + continue; + + // same contents? + bool mismatch = false; + for (int op = 0; op < constant->getNumOperands(); ++op) { + if (constant->getIdOperand(op) != comps[op]) { + mismatch = true; + break; + } + } + if (! mismatch) { + found = true; + break; + } + } + + return found ? constant->getResultId() : NoResult; +} + +Id Builder::findStructConstant(Id typeId, const std::vector& comps) +{ + Instruction* constant = 0; + bool found = false; + for (int i = 0; i < (int)groupedStructConstants[typeId].size(); ++i) { + constant = groupedStructConstants[typeId][i]; + + // same contents? + bool mismatch = false; + for (int op = 0; op < constant->getNumOperands(); ++op) { + if (constant->getIdOperand(op) != comps[op]) { + mismatch = true; + break; + } + } + if (! mismatch) { + found = true; + break; + } + } + + return found ? constant->getResultId() : NoResult; +} + +// Comments in header +Id Builder::makeCompositeConstant(Id typeId, const std::vector& members, bool specConstant) +{ + Op opcode = specConstant ? OpSpecConstantComposite : OpConstantComposite; + assert(typeId); + Op typeClass = getTypeClass(typeId); + + switch (typeClass) { + case OpTypeVector: + case OpTypeArray: + case OpTypeMatrix: + case OpTypeCooperativeMatrixNV: + if (! specConstant) { + Id existing = findCompositeConstant(typeClass, typeId, members); + if (existing) + return existing; + } + break; + case OpTypeStruct: + if (! specConstant) { + Id existing = findStructConstant(typeId, members); + if (existing) + return existing; + } + break; + default: + assert(0); + return makeFloatConstant(0.0); + } + + Instruction* c = new Instruction(getUniqueId(), typeId, opcode); + for (int op = 0; op < (int)members.size(); ++op) + c->addIdOperand(members[op]); + constantsTypesGlobals.push_back(std::unique_ptr(c)); + if (typeClass == OpTypeStruct) + groupedStructConstants[typeId].push_back(c); + else + groupedConstants[typeClass].push_back(c); + module.mapInstruction(c); + + return c->getResultId(); +} + +Instruction* Builder::addEntryPoint(ExecutionModel model, Function* function, const char* name) +{ + Instruction* entryPoint = new Instruction(OpEntryPoint); + entryPoint->addImmediateOperand(model); + entryPoint->addIdOperand(function->getId()); + entryPoint->addStringOperand(name); + + entryPoints.push_back(std::unique_ptr(entryPoint)); + + return entryPoint; +} + +// Currently relying on the fact that all 'value' of interest are small non-negative values. +void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, int value1, int value2, int value3) +{ + Instruction* instr = new Instruction(OpExecutionMode); + instr->addIdOperand(entryPoint->getId()); + instr->addImmediateOperand(mode); + if (value1 >= 0) + instr->addImmediateOperand(value1); + if (value2 >= 0) + instr->addImmediateOperand(value2); + if (value3 >= 0) + instr->addImmediateOperand(value3); + + executionModes.push_back(std::unique_ptr(instr)); +} + +void Builder::addExecutionMode(Function* entryPoint, ExecutionMode mode, const std::vector& literals) +{ + Instruction* instr = new Instruction(OpExecutionMode); + instr->addIdOperand(entryPoint->getId()); + instr->addImmediateOperand(mode); + for (auto literal : literals) + instr->addImmediateOperand(literal); + + executionModes.push_back(std::unique_ptr(instr)); +} + +void Builder::addExecutionModeId(Function* entryPoint, ExecutionMode mode, const std::vector& operandIds) +{ + Instruction* instr = new Instruction(OpExecutionModeId); + instr->addIdOperand(entryPoint->getId()); + instr->addImmediateOperand(mode); + for (auto operandId : operandIds) + instr->addIdOperand(operandId); + + executionModes.push_back(std::unique_ptr(instr)); +} + +void Builder::addName(Id id, const char* string) +{ + Instruction* name = new Instruction(OpName); + name->addIdOperand(id); + name->addStringOperand(string); + + names.push_back(std::unique_ptr(name)); +} + +void Builder::addMemberName(Id id, int memberNumber, const char* string) +{ + Instruction* name = new Instruction(OpMemberName); + name->addIdOperand(id); + name->addImmediateOperand(memberNumber); + name->addStringOperand(string); + + names.push_back(std::unique_ptr(name)); +} + +void Builder::addDecoration(Id id, Decoration decoration, int num) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorate); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + if (num >= 0) + dec->addImmediateOperand(num); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addDecoration(Id id, Decoration decoration, const char* s) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateString); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + dec->addStringOperand(s); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addDecoration(Id id, Decoration decoration, const std::vector& literals) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorate); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + for (auto literal : literals) + dec->addImmediateOperand(literal); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addDecoration(Id id, Decoration decoration, const std::vector& strings) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateString); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + for (auto string : strings) + dec->addStringOperand(string); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addDecorationId(Id id, Decoration decoration, Id idDecoration) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateId); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + dec->addIdOperand(idDecoration); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addDecorationId(Id id, Decoration decoration, const std::vector& operandIds) +{ + if(decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpDecorateId); + dec->addIdOperand(id); + dec->addImmediateOperand(decoration); + + for (auto operandId : operandIds) + dec->addIdOperand(operandId); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, int num) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorate); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + if (num >= 0) + dec->addImmediateOperand(num); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const char *s) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorateStringGOOGLE); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + dec->addStringOperand(s); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector& literals) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorate); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + for (auto literal : literals) + dec->addImmediateOperand(literal); + + decorations.push_back(std::unique_ptr(dec)); +} + +void Builder::addMemberDecoration(Id id, unsigned int member, Decoration decoration, const std::vector& strings) +{ + if (decoration == spv::DecorationMax) + return; + + Instruction* dec = new Instruction(OpMemberDecorateString); + dec->addIdOperand(id); + dec->addImmediateOperand(member); + dec->addImmediateOperand(decoration); + for (auto string : strings) + dec->addStringOperand(string); + + decorations.push_back(std::unique_ptr(dec)); +} + +// Comments in header +Function* Builder::makeEntryPoint(const char* entryPoint) +{ + assert(! entryPointFunction); + + Block* entry; + std::vector params; + std::vector> decorations; + + entryPointFunction = makeFunctionEntry(NoPrecision, makeVoidType(), entryPoint, params, decorations, &entry); + + return entryPointFunction; +} + +// Comments in header +Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, + const std::vector& paramTypes, + const std::vector>& decorations, Block **entry) +{ + // Make the function and initial instructions in it + Id typeId = makeFunctionType(returnType, paramTypes); + Id firstParamId = paramTypes.size() == 0 ? 0 : getUniqueIds((int)paramTypes.size()); + Function* function = new Function(getUniqueId(), returnType, typeId, firstParamId, module); + + // Set up the precisions + setPrecision(function->getId(), precision); + function->setReturnPrecision(precision); + for (unsigned p = 0; p < (unsigned)decorations.size(); ++p) { + for (int d = 0; d < (int)decorations[p].size(); ++d) { + addDecoration(firstParamId + p, decorations[p][d]); + function->addParamPrecision(p, decorations[p][d]); + } + } + + // CFG + if (entry) { + *entry = new Block(getUniqueId(), *function); + function->addBlock(*entry); + setBuildPoint(*entry); + } + + if (name) + addName(function->getId(), name); + + functions.push_back(std::unique_ptr(function)); + + return function; +} + +// Comments in header +void Builder::makeReturn(bool implicit, Id retVal) +{ + if (retVal) { + Instruction* inst = new Instruction(NoResult, NoType, OpReturnValue); + inst->addIdOperand(retVal); + buildPoint->addInstruction(std::unique_ptr(inst)); + } else + buildPoint->addInstruction(std::unique_ptr(new Instruction(NoResult, NoType, OpReturn))); + + if (! implicit) + createAndSetNoPredecessorBlock("post-return"); +} + +// Comments in header +void Builder::leaveFunction() +{ + Block* block = buildPoint; + Function& function = buildPoint->getParent(); + assert(block); + + // If our function did not contain a return, add a return void now. + if (! block->isTerminated()) { + if (function.getReturnType() == makeVoidType()) + makeReturn(true); + else { + makeReturn(true, createUndefined(function.getReturnType())); + } + } +} + +// Comments in header +void Builder::makeDiscard() +{ + buildPoint->addInstruction(std::unique_ptr(new Instruction(OpKill))); + createAndSetNoPredecessorBlock("post-discard"); +} + +// Comments in header +void Builder::makeTerminateInvocation() +{ + buildPoint->addInstruction(std::unique_ptr(new Instruction(OpTerminateInvocation))); + createAndSetNoPredecessorBlock("post-terminate-invocation"); +} + +// Comments in header +Id Builder::createVariable(Decoration precision, StorageClass storageClass, Id type, const char* name, Id initializer) +{ + Id pointerType = makePointer(storageClass, type); + Instruction* inst = new Instruction(getUniqueId(), pointerType, OpVariable); + inst->addImmediateOperand(storageClass); + if (initializer != NoResult) + inst->addIdOperand(initializer); + + switch (storageClass) { + case StorageClassFunction: + // Validation rules require the declaration in the entry block + buildPoint->getParent().addLocalVariable(std::unique_ptr(inst)); + break; + + default: + constantsTypesGlobals.push_back(std::unique_ptr(inst)); + module.mapInstruction(inst); + break; + } + + if (name) + addName(inst->getResultId(), name); + setPrecision(inst->getResultId(), precision); + + return inst->getResultId(); +} + +// Comments in header +Id Builder::createUndefined(Id type) +{ + Instruction* inst = new Instruction(getUniqueId(), type, OpUndef); + buildPoint->addInstruction(std::unique_ptr(inst)); + return inst->getResultId(); +} + +// av/vis/nonprivate are unnecessary and illegal for some storage classes. +spv::MemoryAccessMask Builder::sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) + const +{ + switch (sc) { + case spv::StorageClassUniform: + case spv::StorageClassWorkgroup: + case spv::StorageClassStorageBuffer: + case spv::StorageClassPhysicalStorageBufferEXT: + break; + default: + memoryAccess = spv::MemoryAccessMask(memoryAccess & + ~(spv::MemoryAccessMakePointerAvailableKHRMask | + spv::MemoryAccessMakePointerVisibleKHRMask | + spv::MemoryAccessNonPrivatePointerKHRMask)); + break; + } + return memoryAccess; +} + +// Comments in header +void Builder::createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess, spv::Scope scope, + unsigned int alignment) +{ + Instruction* store = new Instruction(OpStore); + store->addIdOperand(lValue); + store->addIdOperand(rValue); + + memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); + + if (memoryAccess != MemoryAccessMaskNone) { + store->addImmediateOperand(memoryAccess); + if (memoryAccess & spv::MemoryAccessAlignedMask) { + store->addImmediateOperand(alignment); + } + if (memoryAccess & spv::MemoryAccessMakePointerAvailableKHRMask) { + store->addIdOperand(makeUintConstant(scope)); + } + } + + buildPoint->addInstruction(std::unique_ptr(store)); +} + +// Comments in header +Id Builder::createLoad(Id lValue, spv::Decoration precision, spv::MemoryAccessMask memoryAccess, + spv::Scope scope, unsigned int alignment) +{ + Instruction* load = new Instruction(getUniqueId(), getDerefTypeId(lValue), OpLoad); + load->addIdOperand(lValue); + + memoryAccess = sanitizeMemoryAccessForStorageClass(memoryAccess, getStorageClass(lValue)); + + if (memoryAccess != MemoryAccessMaskNone) { + load->addImmediateOperand(memoryAccess); + if (memoryAccess & spv::MemoryAccessAlignedMask) { + load->addImmediateOperand(alignment); + } + if (memoryAccess & spv::MemoryAccessMakePointerVisibleKHRMask) { + load->addIdOperand(makeUintConstant(scope)); + } + } + + buildPoint->addInstruction(std::unique_ptr(load)); + setPrecision(load->getResultId(), precision); + + return load->getResultId(); +} + +// Comments in header +Id Builder::createAccessChain(StorageClass storageClass, Id base, const std::vector& offsets) +{ + // Figure out the final resulting type. + spv::Id typeId = getTypeId(base); + assert(isPointerType(typeId) && offsets.size() > 0); + typeId = getContainedTypeId(typeId); + for (int i = 0; i < (int)offsets.size(); ++i) { + if (isStructType(typeId)) { + assert(isConstantScalar(offsets[i])); + typeId = getContainedTypeId(typeId, getConstantScalar(offsets[i])); + } else + typeId = getContainedTypeId(typeId, offsets[i]); + } + typeId = makePointer(storageClass, typeId); + + // Make the instruction + Instruction* chain = new Instruction(getUniqueId(), typeId, OpAccessChain); + chain->addIdOperand(base); + for (int i = 0; i < (int)offsets.size(); ++i) + chain->addIdOperand(offsets[i]); + buildPoint->addInstruction(std::unique_ptr(chain)); + + return chain->getResultId(); +} + +Id Builder::createArrayLength(Id base, unsigned int member) +{ + spv::Id intType = makeUintType(32); + Instruction* length = new Instruction(getUniqueId(), intType, OpArrayLength); + length->addIdOperand(base); + length->addImmediateOperand(member); + buildPoint->addInstruction(std::unique_ptr(length)); + + return length->getResultId(); +} + +Id Builder::createCooperativeMatrixLength(Id type) +{ + spv::Id intType = makeUintType(32); + + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCooperativeMatrixLengthNV, intType, std::vector(1, type), std::vector()); + } + + Instruction* length = new Instruction(getUniqueId(), intType, OpCooperativeMatrixLengthNV); + length->addIdOperand(type); + buildPoint->addInstruction(std::unique_ptr(length)); + + return length->getResultId(); +} + +Id Builder::createCompositeExtract(Id composite, Id typeId, unsigned index) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCompositeExtract, typeId, std::vector(1, composite), + std::vector(1, index)); + } + Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); + extract->addIdOperand(composite); + extract->addImmediateOperand(index); + buildPoint->addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createCompositeExtract(Id composite, Id typeId, const std::vector& indexes) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(OpCompositeExtract, typeId, std::vector(1, composite), indexes); + } + Instruction* extract = new Instruction(getUniqueId(), typeId, OpCompositeExtract); + extract->addIdOperand(composite); + for (int i = 0; i < (int)indexes.size(); ++i) + extract->addImmediateOperand(indexes[i]); + buildPoint->addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, unsigned index) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); + insert->addIdOperand(object); + insert->addIdOperand(composite); + insert->addImmediateOperand(index); + buildPoint->addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +Id Builder::createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpCompositeInsert); + insert->addIdOperand(object); + insert->addIdOperand(composite); + for (int i = 0; i < (int)indexes.size(); ++i) + insert->addImmediateOperand(indexes[i]); + buildPoint->addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +Id Builder::createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex) +{ + Instruction* extract = new Instruction(getUniqueId(), typeId, OpVectorExtractDynamic); + extract->addIdOperand(vector); + extract->addIdOperand(componentIndex); + buildPoint->addInstruction(std::unique_ptr(extract)); + + return extract->getResultId(); +} + +Id Builder::createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex) +{ + Instruction* insert = new Instruction(getUniqueId(), typeId, OpVectorInsertDynamic); + insert->addIdOperand(vector); + insert->addIdOperand(component); + insert->addIdOperand(componentIndex); + buildPoint->addInstruction(std::unique_ptr(insert)); + + return insert->getResultId(); +} + +// An opcode that has no operands, no result id, and no type +void Builder::createNoResultOp(Op opCode) +{ + Instruction* op = new Instruction(opCode); + buildPoint->addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one id operand, no result id, and no type +void Builder::createNoResultOp(Op opCode, Id operand) +{ + Instruction* op = new Instruction(opCode); + op->addIdOperand(operand); + buildPoint->addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one or more operands, no result id, and no type +void Builder::createNoResultOp(Op opCode, const std::vector& operands) +{ + Instruction* op = new Instruction(opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) { + op->addIdOperand(*it); + } + buildPoint->addInstruction(std::unique_ptr(op)); +} + +// An opcode that has multiple operands, no result id, and no type +void Builder::createNoResultOp(Op opCode, const std::vector& operands) +{ + Instruction* op = new Instruction(opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) { + if (it->isId) + op->addIdOperand(it->word); + else + op->addImmediateOperand(it->word); + } + buildPoint->addInstruction(std::unique_ptr(op)); +} + +void Builder::createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask semantics) +{ + Instruction* op = new Instruction(OpControlBarrier); + op->addIdOperand(makeUintConstant(execution)); + op->addIdOperand(makeUintConstant(memory)); + op->addIdOperand(makeUintConstant(semantics)); + buildPoint->addInstruction(std::unique_ptr(op)); +} + +void Builder::createMemoryBarrier(unsigned executionScope, unsigned memorySemantics) +{ + Instruction* op = new Instruction(OpMemoryBarrier); + op->addIdOperand(makeUintConstant(executionScope)); + op->addIdOperand(makeUintConstant(memorySemantics)); + buildPoint->addInstruction(std::unique_ptr(op)); +} + +// An opcode that has one operands, a result id, and a type +Id Builder::createUnaryOp(Op opCode, Id typeId, Id operand) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + return createSpecConstantOp(opCode, typeId, std::vector(1, operand), std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->addIdOperand(operand); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createBinOp(Op opCode, Id typeId, Id left, Id right) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + std::vector operands(2); + operands[0] = left; operands[1] = right; + return createSpecConstantOp(opCode, typeId, operands, std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->addIdOperand(left); + op->addIdOperand(right); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createTriOp(Op opCode, Id typeId, Id op1, Id op2, Id op3) +{ + // Generate code for spec constants if in spec constant operation + // generation mode. + if (generatingOpCodeForSpecConst) { + std::vector operands(3); + operands[0] = op1; + operands[1] = op2; + operands[2] = op3; + return createSpecConstantOp( + opCode, typeId, operands, std::vector()); + } + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + op->addIdOperand(op1); + op->addIdOperand(op2); + op->addIdOperand(op3); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createOp(Op opCode, Id typeId, const std::vector& operands) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) + op->addIdOperand(*it); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createOp(Op opCode, Id typeId, const std::vector& operands) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) { + if (it->isId) + op->addIdOperand(it->word); + else + op->addImmediateOperand(it->word); + } + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createSpecConstantOp(Op opCode, Id typeId, const std::vector& operands, + const std::vector& literals) +{ + Instruction* op = new Instruction(getUniqueId(), typeId, OpSpecConstantOp); + op->addImmediateOperand((unsigned) opCode); + for (auto it = operands.cbegin(); it != operands.cend(); ++it) + op->addIdOperand(*it); + for (auto it = literals.cbegin(); it != literals.cend(); ++it) + op->addImmediateOperand(*it); + module.mapInstruction(op); + constantsTypesGlobals.push_back(std::unique_ptr(op)); + + return op->getResultId(); +} + +Id Builder::createFunctionCall(spv::Function* function, const std::vector& args) +{ + Instruction* op = new Instruction(getUniqueId(), function->getReturnType(), OpFunctionCall); + op->addIdOperand(function->getId()); + for (int a = 0; a < (int)args.size(); ++a) + op->addIdOperand(args[a]); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// Comments in header +Id Builder::createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector& channels) +{ + if (channels.size() == 1) + return setPrecision(createCompositeExtract(source, typeId, channels.front()), precision); + + if (generatingOpCodeForSpecConst) { + std::vector operands(2); + operands[0] = operands[1] = source; + return setPrecision(createSpecConstantOp(OpVectorShuffle, typeId, operands, channels), precision); + } + Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); + assert(isVector(source)); + swizzle->addIdOperand(source); + swizzle->addIdOperand(source); + for (int i = 0; i < (int)channels.size(); ++i) + swizzle->addImmediateOperand(channels[i]); + buildPoint->addInstruction(std::unique_ptr(swizzle)); + + return setPrecision(swizzle->getResultId(), precision); +} + +// Comments in header +Id Builder::createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector& channels) +{ + if (channels.size() == 1 && getNumComponents(source) == 1) + return createCompositeInsert(source, target, typeId, channels.front()); + + Instruction* swizzle = new Instruction(getUniqueId(), typeId, OpVectorShuffle); + + assert(isVector(target)); + swizzle->addIdOperand(target); + + assert(getNumComponents(source) == (int)channels.size()); + assert(isVector(source)); + swizzle->addIdOperand(source); + + // Set up an identity shuffle from the base value to the result value + unsigned int components[4]; + int numTargetComponents = getNumComponents(target); + for (int i = 0; i < numTargetComponents; ++i) + components[i] = i; + + // Punch in the l-value swizzle + for (int i = 0; i < (int)channels.size(); ++i) + components[channels[i]] = numTargetComponents + i; + + // finish the instruction with these components selectors + for (int i = 0; i < numTargetComponents; ++i) + swizzle->addImmediateOperand(components[i]); + buildPoint->addInstruction(std::unique_ptr(swizzle)); + + return swizzle->getResultId(); +} + +// Comments in header +void Builder::promoteScalar(Decoration precision, Id& left, Id& right) +{ + int direction = getNumComponents(right) - getNumComponents(left); + + if (direction > 0) + left = smearScalar(precision, left, makeVectorType(getTypeId(left), getNumComponents(right))); + else if (direction < 0) + right = smearScalar(precision, right, makeVectorType(getTypeId(right), getNumComponents(left))); + + return; +} + +// Comments in header +Id Builder::smearScalar(Decoration precision, Id scalar, Id vectorType) +{ + assert(getNumComponents(scalar) == 1); + assert(getTypeId(scalar) == getScalarTypeId(vectorType)); + + int numComponents = getNumTypeComponents(vectorType); + if (numComponents == 1) + return scalar; + + Instruction* smear = nullptr; + if (generatingOpCodeForSpecConst) { + auto members = std::vector(numComponents, scalar); + // Sometime even in spec-constant-op mode, the temporary vector created by + // promoting a scalar might not be a spec constant. This should depend on + // the scalar. + // e.g.: + // const vec2 spec_const_result = a_spec_const_vec2 + a_front_end_const_scalar; + // In such cases, the temporary vector created from a_front_end_const_scalar + // is not a spec constant vector, even though the binary operation node is marked + // as 'specConstant' and we are in spec-constant-op mode. + auto result_id = makeCompositeConstant(vectorType, members, isSpecConstant(scalar)); + smear = module.getInstruction(result_id); + } else { + smear = new Instruction(getUniqueId(), vectorType, OpCompositeConstruct); + for (int c = 0; c < numComponents; ++c) + smear->addIdOperand(scalar); + buildPoint->addInstruction(std::unique_ptr(smear)); + } + + return setPrecision(smear->getResultId(), precision); +} + +// Comments in header +Id Builder::createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector& args) +{ + Instruction* inst = new Instruction(getUniqueId(), resultType, OpExtInst); + inst->addIdOperand(builtins); + inst->addImmediateOperand(entryPoint); + for (int arg = 0; arg < (int)args.size(); ++arg) + inst->addIdOperand(args[arg]); + + buildPoint->addInstruction(std::unique_ptr(inst)); + + return inst->getResultId(); +} + +// Accept all parameters needed to create a texture instruction. +// Create the correct instruction based on the inputs, and make the call. +Id Builder::createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, + bool noImplicitLod, const TextureParameters& parameters, ImageOperandsMask signExtensionMask) +{ + static const int maxTextureArgs = 10; + Id texArgs[maxTextureArgs] = {}; + + // + // Set up the fixed arguments + // + int numArgs = 0; + bool explicitLod = false; + texArgs[numArgs++] = parameters.sampler; + texArgs[numArgs++] = parameters.coords; + if (parameters.Dref != NoResult) + texArgs[numArgs++] = parameters.Dref; + if (parameters.component != NoResult) + texArgs[numArgs++] = parameters.component; + +#ifndef GLSLANG_WEB + if (parameters.granularity != NoResult) + texArgs[numArgs++] = parameters.granularity; + if (parameters.coarse != NoResult) + texArgs[numArgs++] = parameters.coarse; +#endif + + // + // Set up the optional arguments + // + int optArgNum = numArgs; // track which operand, if it exists, is the mask of optional arguments + ++numArgs; // speculatively make room for the mask operand + ImageOperandsMask mask = ImageOperandsMaskNone; // the mask operand + if (parameters.bias) { + mask = (ImageOperandsMask)(mask | ImageOperandsBiasMask); + texArgs[numArgs++] = parameters.bias; + } + if (parameters.lod) { + mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); + texArgs[numArgs++] = parameters.lod; + explicitLod = true; + } else if (parameters.gradX) { + mask = (ImageOperandsMask)(mask | ImageOperandsGradMask); + texArgs[numArgs++] = parameters.gradX; + texArgs[numArgs++] = parameters.gradY; + explicitLod = true; + } else if (noImplicitLod && ! fetch && ! gather) { + // have to explicitly use lod of 0 if not allowed to have them be implicit, and + // we would otherwise be about to issue an implicit instruction + mask = (ImageOperandsMask)(mask | ImageOperandsLodMask); + texArgs[numArgs++] = makeFloatConstant(0.0); + explicitLod = true; + } + if (parameters.offset) { + if (isConstant(parameters.offset)) + mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetMask); + else { + addCapability(CapabilityImageGatherExtended); + mask = (ImageOperandsMask)(mask | ImageOperandsOffsetMask); + } + texArgs[numArgs++] = parameters.offset; + } + if (parameters.offsets) { + addCapability(CapabilityImageGatherExtended); + mask = (ImageOperandsMask)(mask | ImageOperandsConstOffsetsMask); + texArgs[numArgs++] = parameters.offsets; + } +#ifndef GLSLANG_WEB + if (parameters.sample) { + mask = (ImageOperandsMask)(mask | ImageOperandsSampleMask); + texArgs[numArgs++] = parameters.sample; + } + if (parameters.lodClamp) { + // capability if this bit is used + addCapability(CapabilityMinLod); + + mask = (ImageOperandsMask)(mask | ImageOperandsMinLodMask); + texArgs[numArgs++] = parameters.lodClamp; + } + if (parameters.nonprivate) { + mask = mask | ImageOperandsNonPrivateTexelKHRMask; + } + if (parameters.volatil) { + mask = mask | ImageOperandsVolatileTexelKHRMask; + } +#endif + mask = mask | signExtensionMask; + if (mask == ImageOperandsMaskNone) + --numArgs; // undo speculative reservation for the mask argument + else + texArgs[optArgNum] = mask; + + // + // Set up the instruction + // + Op opCode = OpNop; // All paths below need to set this + if (fetch) { + if (sparse) + opCode = OpImageSparseFetch; + else + opCode = OpImageFetch; +#ifndef GLSLANG_WEB + } else if (parameters.granularity && parameters.coarse) { + opCode = OpImageSampleFootprintNV; + } else if (gather) { + if (parameters.Dref) + if (sparse) + opCode = OpImageSparseDrefGather; + else + opCode = OpImageDrefGather; + else + if (sparse) + opCode = OpImageSparseGather; + else + opCode = OpImageGather; +#endif + } else if (explicitLod) { + if (parameters.Dref) { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjDrefExplicitLod; + else + opCode = OpImageSampleProjDrefExplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleDrefExplicitLod; + else + opCode = OpImageSampleDrefExplicitLod; + } else { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjExplicitLod; + else + opCode = OpImageSampleProjExplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleExplicitLod; + else + opCode = OpImageSampleExplicitLod; + } + } else { + if (parameters.Dref) { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjDrefImplicitLod; + else + opCode = OpImageSampleProjDrefImplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleDrefImplicitLod; + else + opCode = OpImageSampleDrefImplicitLod; + } else { + if (proj) + if (sparse) + opCode = OpImageSparseSampleProjImplicitLod; + else + opCode = OpImageSampleProjImplicitLod; + else + if (sparse) + opCode = OpImageSparseSampleImplicitLod; + else + opCode = OpImageSampleImplicitLod; + } + } + + // See if the result type is expecting a smeared result. + // This happens when a legacy shadow*() call is made, which + // gets a vec4 back instead of a float. + Id smearedType = resultType; + if (! isScalarType(resultType)) { + switch (opCode) { + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + resultType = getScalarTypeId(resultType); + break; + default: + break; + } + } + + Id typeId0 = 0; + Id typeId1 = 0; + + if (sparse) { + typeId0 = resultType; + typeId1 = getDerefTypeId(parameters.texelOut); + resultType = makeStructResultType(typeId0, typeId1); + } + + // Build the SPIR-V instruction + Instruction* textureInst = new Instruction(getUniqueId(), resultType, opCode); + for (int op = 0; op < optArgNum; ++op) + textureInst->addIdOperand(texArgs[op]); + if (optArgNum < numArgs) + textureInst->addImmediateOperand(texArgs[optArgNum]); + for (int op = optArgNum + 1; op < numArgs; ++op) + textureInst->addIdOperand(texArgs[op]); + setPrecision(textureInst->getResultId(), precision); + buildPoint->addInstruction(std::unique_ptr(textureInst)); + + Id resultId = textureInst->getResultId(); + + if (sparse) { + // set capability + addCapability(CapabilitySparseResidency); + + // Decode the return type that was a special structure + createStore(createCompositeExtract(resultId, typeId1, 1), parameters.texelOut); + resultId = createCompositeExtract(resultId, typeId0, 0); + setPrecision(resultId, precision); + } else { + // When a smear is needed, do it, as per what was computed + // above when resultType was changed to a scalar type. + if (resultType != smearedType) + resultId = smearScalar(precision, resultId, smearedType); + } + + return resultId; +} + +// Comments in header +Id Builder::createTextureQueryCall(Op opCode, const TextureParameters& parameters, bool isUnsignedResult) +{ + // Figure out the result type + Id resultType = 0; + switch (opCode) { + case OpImageQuerySize: + case OpImageQuerySizeLod: + { + int numComponents = 0; + switch (getTypeDimensionality(getImageType(parameters.sampler))) { + case Dim1D: + case DimBuffer: + numComponents = 1; + break; + case Dim2D: + case DimCube: + case DimRect: + case DimSubpassData: + numComponents = 2; + break; + case Dim3D: + numComponents = 3; + break; + + default: + assert(0); + break; + } + if (isArrayedImageType(getImageType(parameters.sampler))) + ++numComponents; + + Id intType = isUnsignedResult ? makeUintType(32) : makeIntType(32); + if (numComponents == 1) + resultType = intType; + else + resultType = makeVectorType(intType, numComponents); + + break; + } + case OpImageQueryLod: + resultType = makeVectorType(getScalarTypeId(getTypeId(parameters.coords)), 2); + break; + case OpImageQueryLevels: + case OpImageQuerySamples: + resultType = isUnsignedResult ? makeUintType(32) : makeIntType(32); + break; + default: + assert(0); + break; + } + + Instruction* query = new Instruction(getUniqueId(), resultType, opCode); + query->addIdOperand(parameters.sampler); + if (parameters.coords) + query->addIdOperand(parameters.coords); + if (parameters.lod) + query->addIdOperand(parameters.lod); + buildPoint->addInstruction(std::unique_ptr(query)); + addCapability(CapabilityImageQuery); + + return query->getResultId(); +} + +// External comments in header. +// Operates recursively to visit the composite's hierarchy. +Id Builder::createCompositeCompare(Decoration precision, Id value1, Id value2, bool equal) +{ + Id boolType = makeBoolType(); + Id valueType = getTypeId(value1); + + Id resultId = NoResult; + + int numConstituents = getNumTypeConstituents(valueType); + + // Scalars and Vectors + + if (isScalarType(valueType) || isVectorType(valueType)) { + assert(valueType == getTypeId(value2)); + // These just need a single comparison, just have + // to figure out what it is. + Op op; + switch (getMostBasicTypeClass(valueType)) { + case OpTypeFloat: + op = equal ? OpFOrdEqual : OpFUnordNotEqual; + break; + case OpTypeInt: + default: + op = equal ? OpIEqual : OpINotEqual; + break; + case OpTypeBool: + op = equal ? OpLogicalEqual : OpLogicalNotEqual; + precision = NoPrecision; + break; + } + + if (isScalarType(valueType)) { + // scalar + resultId = createBinOp(op, boolType, value1, value2); + } else { + // vector + resultId = createBinOp(op, makeVectorType(boolType, numConstituents), value1, value2); + setPrecision(resultId, precision); + // reduce vector compares... + resultId = createUnaryOp(equal ? OpAll : OpAny, boolType, resultId); + } + + return setPrecision(resultId, precision); + } + + // Only structs, arrays, and matrices should be left. + // They share in common the reduction operation across their constituents. + assert(isAggregateType(valueType) || isMatrixType(valueType)); + + // Compare each pair of constituents + for (int constituent = 0; constituent < numConstituents; ++constituent) { + std::vector indexes(1, constituent); + Id constituentType1 = getContainedTypeId(getTypeId(value1), constituent); + Id constituentType2 = getContainedTypeId(getTypeId(value2), constituent); + Id constituent1 = createCompositeExtract(value1, constituentType1, indexes); + Id constituent2 = createCompositeExtract(value2, constituentType2, indexes); + + Id subResultId = createCompositeCompare(precision, constituent1, constituent2, equal); + + if (constituent == 0) + resultId = subResultId; + else + resultId = setPrecision(createBinOp(equal ? OpLogicalAnd : OpLogicalOr, boolType, resultId, subResultId), + precision); + } + + return resultId; +} + +// OpCompositeConstruct +Id Builder::createCompositeConstruct(Id typeId, const std::vector& constituents) +{ + assert(isAggregateType(typeId) || (getNumTypeConstituents(typeId) > 1 && + getNumTypeConstituents(typeId) == (int)constituents.size())); + + if (generatingOpCodeForSpecConst) { + // Sometime, even in spec-constant-op mode, the constant composite to be + // constructed may not be a specialization constant. + // e.g.: + // const mat2 m2 = mat2(a_spec_const, a_front_end_const, another_front_end_const, third_front_end_const); + // The first column vector should be a spec constant one, as a_spec_const is a spec constant. + // The second column vector should NOT be spec constant, as it does not contain any spec constants. + // To handle such cases, we check the constituents of the constant vector to determine whether this + // vector should be created as a spec constant. + return makeCompositeConstant(typeId, constituents, + std::any_of(constituents.begin(), constituents.end(), + [&](spv::Id id) { return isSpecConstant(id); })); + } + + Instruction* op = new Instruction(getUniqueId(), typeId, OpCompositeConstruct); + for (int c = 0; c < (int)constituents.size(); ++c) + op->addIdOperand(constituents[c]); + buildPoint->addInstruction(std::unique_ptr(op)); + + return op->getResultId(); +} + +// Vector or scalar constructor +Id Builder::createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId) +{ + Id result = NoResult; + unsigned int numTargetComponents = getNumTypeComponents(resultTypeId); + unsigned int targetComponent = 0; + + // Special case: when calling a vector constructor with a single scalar + // argument, smear the scalar + if (sources.size() == 1 && isScalar(sources[0]) && numTargetComponents > 1) + return smearScalar(precision, sources[0], resultTypeId); + + // accumulate the arguments for OpCompositeConstruct + std::vector constituents; + Id scalarTypeId = getScalarTypeId(resultTypeId); + + // lambda to store the result of visiting an argument component + const auto latchResult = [&](Id comp) { + if (numTargetComponents > 1) + constituents.push_back(comp); + else + result = comp; + ++targetComponent; + }; + + // lambda to visit a vector argument's components + const auto accumulateVectorConstituents = [&](Id sourceArg) { + unsigned int sourceSize = getNumComponents(sourceArg); + unsigned int sourcesToUse = sourceSize; + if (sourcesToUse + targetComponent > numTargetComponents) + sourcesToUse = numTargetComponents - targetComponent; + + for (unsigned int s = 0; s < sourcesToUse; ++s) { + std::vector swiz; + swiz.push_back(s); + latchResult(createRvalueSwizzle(precision, scalarTypeId, sourceArg, swiz)); + } + }; + + // lambda to visit a matrix argument's components + const auto accumulateMatrixConstituents = [&](Id sourceArg) { + unsigned int sourceSize = getNumColumns(sourceArg) * getNumRows(sourceArg); + unsigned int sourcesToUse = sourceSize; + if (sourcesToUse + targetComponent > numTargetComponents) + sourcesToUse = numTargetComponents - targetComponent; + + int col = 0; + int row = 0; + for (unsigned int s = 0; s < sourcesToUse; ++s) { + if (row >= getNumRows(sourceArg)) { + row = 0; + col++; + } + std::vector indexes; + indexes.push_back(col); + indexes.push_back(row); + latchResult(createCompositeExtract(sourceArg, scalarTypeId, indexes)); + row++; + } + }; + + // Go through the source arguments, each one could have either + // a single or multiple components to contribute. + for (unsigned int i = 0; i < sources.size(); ++i) { + + if (isScalar(sources[i]) || isPointer(sources[i])) + latchResult(sources[i]); + else if (isVector(sources[i])) + accumulateVectorConstituents(sources[i]); + else if (isMatrix(sources[i])) + accumulateMatrixConstituents(sources[i]); + else + assert(0); + + if (targetComponent >= numTargetComponents) + break; + } + + // If the result is a vector, make it from the gathered constituents. + if (constituents.size() > 0) + result = createCompositeConstruct(resultTypeId, constituents); + + return setPrecision(result, precision); +} + +// Comments in header +Id Builder::createMatrixConstructor(Decoration precision, const std::vector& sources, Id resultTypeId) +{ + Id componentTypeId = getScalarTypeId(resultTypeId); + int numCols = getTypeNumColumns(resultTypeId); + int numRows = getTypeNumRows(resultTypeId); + + Instruction* instr = module.getInstruction(componentTypeId); +#ifdef GLSLANG_WEB + const unsigned bitCount = 32; + assert(bitCount == instr->getImmediateOperand(0)); +#else + const unsigned bitCount = instr->getImmediateOperand(0); +#endif + + // Optimize matrix constructed from a bigger matrix + if (isMatrix(sources[0]) && getNumColumns(sources[0]) >= numCols && getNumRows(sources[0]) >= numRows) { + // To truncate the matrix to a smaller number of rows/columns, we need to: + // 1. For each column, extract the column and truncate it to the required size using shuffle + // 2. Assemble the resulting matrix from all columns + Id matrix = sources[0]; + Id columnTypeId = getContainedTypeId(resultTypeId); + Id sourceColumnTypeId = getContainedTypeId(getTypeId(matrix)); + + std::vector channels; + for (int row = 0; row < numRows; ++row) + channels.push_back(row); + + std::vector matrixColumns; + for (int col = 0; col < numCols; ++col) { + std::vector indexes; + indexes.push_back(col); + Id colv = createCompositeExtract(matrix, sourceColumnTypeId, indexes); + setPrecision(colv, precision); + + if (numRows != getNumRows(matrix)) { + matrixColumns.push_back(createRvalueSwizzle(precision, columnTypeId, colv, channels)); + } else { + matrixColumns.push_back(colv); + } + } + + return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); + } + + // Otherwise, will use a two step process + // 1. make a compile-time 2D array of values + // 2. construct a matrix from that array + + // Step 1. + + // initialize the array to the identity matrix + Id ids[maxMatrixSize][maxMatrixSize]; + Id one = (bitCount == 64 ? makeDoubleConstant(1.0) : makeFloatConstant(1.0)); + Id zero = (bitCount == 64 ? makeDoubleConstant(0.0) : makeFloatConstant(0.0)); + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + if (col == row) + ids[col][row] = one; + else + ids[col][row] = zero; + } + } + + // modify components as dictated by the arguments + if (sources.size() == 1 && isScalar(sources[0])) { + // a single scalar; resets the diagonals + for (int col = 0; col < 4; ++col) + ids[col][col] = sources[0]; + } else if (isMatrix(sources[0])) { + // constructing from another matrix; copy over the parts that exist in both the argument and constructee + Id matrix = sources[0]; + int minCols = std::min(numCols, getNumColumns(matrix)); + int minRows = std::min(numRows, getNumRows(matrix)); + for (int col = 0; col < minCols; ++col) { + std::vector indexes; + indexes.push_back(col); + for (int row = 0; row < minRows; ++row) { + indexes.push_back(row); + ids[col][row] = createCompositeExtract(matrix, componentTypeId, indexes); + indexes.pop_back(); + setPrecision(ids[col][row], precision); + } + } + } else { + // fill in the matrix in column-major order with whatever argument components are available + int row = 0; + int col = 0; + + for (int arg = 0; arg < (int)sources.size(); ++arg) { + Id argComp = sources[arg]; + for (int comp = 0; comp < getNumComponents(sources[arg]); ++comp) { + if (getNumComponents(sources[arg]) > 1) { + argComp = createCompositeExtract(sources[arg], componentTypeId, comp); + setPrecision(argComp, precision); + } + ids[col][row++] = argComp; + if (row == numRows) { + row = 0; + col++; + } + } + } + } + + // Step 2: Construct a matrix from that array. + // First make the column vectors, then make the matrix. + + // make the column vectors + Id columnTypeId = getContainedTypeId(resultTypeId); + std::vector matrixColumns; + for (int col = 0; col < numCols; ++col) { + std::vector vectorComponents; + for (int row = 0; row < numRows; ++row) + vectorComponents.push_back(ids[col][row]); + Id column = createCompositeConstruct(columnTypeId, vectorComponents); + setPrecision(column, precision); + matrixColumns.push_back(column); + } + + // make the matrix + return setPrecision(createCompositeConstruct(resultTypeId, matrixColumns), precision); +} + +// Comments in header +Builder::If::If(Id cond, unsigned int ctrl, Builder& gb) : + builder(gb), + condition(cond), + control(ctrl), + elseBlock(0) +{ + function = &builder.getBuildPoint()->getParent(); + + // make the blocks, but only put the then-block into the function, + // the else-block and merge-block will be added later, in order, after + // earlier code is emitted + thenBlock = new Block(builder.getUniqueId(), *function); + mergeBlock = new Block(builder.getUniqueId(), *function); + + // Save the current block, so that we can add in the flow control split when + // makeEndIf is called. + headerBlock = builder.getBuildPoint(); + + function->addBlock(thenBlock); + builder.setBuildPoint(thenBlock); +} + +// Comments in header +void Builder::If::makeBeginElse() +{ + // Close out the "then" by having it jump to the mergeBlock + builder.createBranch(mergeBlock); + + // Make the first else block and add it to the function + elseBlock = new Block(builder.getUniqueId(), *function); + function->addBlock(elseBlock); + + // Start building the else block + builder.setBuildPoint(elseBlock); +} + +// Comments in header +void Builder::If::makeEndIf() +{ + // jump to the merge block + builder.createBranch(mergeBlock); + + // Go back to the headerBlock and make the flow control split + builder.setBuildPoint(headerBlock); + builder.createSelectionMerge(mergeBlock, control); + if (elseBlock) + builder.createConditionalBranch(condition, thenBlock, elseBlock); + else + builder.createConditionalBranch(condition, thenBlock, mergeBlock); + + // add the merge block to the function + function->addBlock(mergeBlock); + builder.setBuildPoint(mergeBlock); +} + +// Comments in header +void Builder::makeSwitch(Id selector, unsigned int control, int numSegments, const std::vector& caseValues, + const std::vector& valueIndexToSegment, int defaultSegment, + std::vector& segmentBlocks) +{ + Function& function = buildPoint->getParent(); + + // make all the blocks + for (int s = 0; s < numSegments; ++s) + segmentBlocks.push_back(new Block(getUniqueId(), function)); + + Block* mergeBlock = new Block(getUniqueId(), function); + + // make and insert the switch's selection-merge instruction + createSelectionMerge(mergeBlock, control); + + // make the switch instruction + Instruction* switchInst = new Instruction(NoResult, NoType, OpSwitch); + switchInst->addIdOperand(selector); + auto defaultOrMerge = (defaultSegment >= 0) ? segmentBlocks[defaultSegment] : mergeBlock; + switchInst->addIdOperand(defaultOrMerge->getId()); + defaultOrMerge->addPredecessor(buildPoint); + for (int i = 0; i < (int)caseValues.size(); ++i) { + switchInst->addImmediateOperand(caseValues[i]); + switchInst->addIdOperand(segmentBlocks[valueIndexToSegment[i]]->getId()); + segmentBlocks[valueIndexToSegment[i]]->addPredecessor(buildPoint); + } + buildPoint->addInstruction(std::unique_ptr(switchInst)); + + // push the merge block + switchMerges.push(mergeBlock); +} + +// Comments in header +void Builder::addSwitchBreak() +{ + // branch to the top of the merge block stack + createBranch(switchMerges.top()); + createAndSetNoPredecessorBlock("post-switch-break"); +} + +// Comments in header +void Builder::nextSwitchSegment(std::vector& segmentBlock, int nextSegment) +{ + int lastSegment = nextSegment - 1; + if (lastSegment >= 0) { + // Close out previous segment by jumping, if necessary, to next segment + if (! buildPoint->isTerminated()) + createBranch(segmentBlock[nextSegment]); + } + Block* block = segmentBlock[nextSegment]; + block->getParent().addBlock(block); + setBuildPoint(block); +} + +// Comments in header +void Builder::endSwitch(std::vector& /*segmentBlock*/) +{ + // Close out previous segment by jumping, if necessary, to next segment + if (! buildPoint->isTerminated()) + addSwitchBreak(); + + switchMerges.top()->getParent().addBlock(switchMerges.top()); + setBuildPoint(switchMerges.top()); + + switchMerges.pop(); +} + +Block& Builder::makeNewBlock() +{ + Function& function = buildPoint->getParent(); + auto block = new Block(getUniqueId(), function); + function.addBlock(block); + return *block; +} + +Builder::LoopBlocks& Builder::makeNewLoop() +{ + // This verbosity is needed to simultaneously get the same behavior + // everywhere (id's in the same order), have a syntax that works + // across lots of versions of C++, have no warnings from pedantic + // compilation modes, and leave the rest of the code alone. + Block& head = makeNewBlock(); + Block& body = makeNewBlock(); + Block& merge = makeNewBlock(); + Block& continue_target = makeNewBlock(); + LoopBlocks blocks(head, body, merge, continue_target); + loops.push(blocks); + return loops.top(); +} + +void Builder::createLoopContinue() +{ + createBranch(&loops.top().continue_target); + // Set up a block for dead code. + createAndSetNoPredecessorBlock("post-loop-continue"); +} + +void Builder::createLoopExit() +{ + createBranch(&loops.top().merge); + // Set up a block for dead code. + createAndSetNoPredecessorBlock("post-loop-break"); +} + +void Builder::closeLoop() +{ + loops.pop(); +} + +void Builder::clearAccessChain() +{ + accessChain.base = NoResult; + accessChain.indexChain.clear(); + accessChain.instr = NoResult; + accessChain.swizzle.clear(); + accessChain.component = NoResult; + accessChain.preSwizzleBaseType = NoType; + accessChain.isRValue = false; + accessChain.coherentFlags.clear(); + accessChain.alignment = 0; +} + +// Comments in header +void Builder::accessChainPushSwizzle(std::vector& swizzle, Id preSwizzleBaseType, + AccessChain::CoherentFlags coherentFlags, unsigned int alignment) +{ + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + + // swizzles can be stacked in GLSL, but simplified to a single + // one here; the base type doesn't change + if (accessChain.preSwizzleBaseType == NoType) + accessChain.preSwizzleBaseType = preSwizzleBaseType; + + // if needed, propagate the swizzle for the current access chain + if (accessChain.swizzle.size() > 0) { + std::vector oldSwizzle = accessChain.swizzle; + accessChain.swizzle.resize(0); + for (unsigned int i = 0; i < swizzle.size(); ++i) { + assert(swizzle[i] < oldSwizzle.size()); + accessChain.swizzle.push_back(oldSwizzle[swizzle[i]]); + } + } else + accessChain.swizzle = swizzle; + + // determine if we need to track this swizzle anymore + simplifyAccessChainSwizzle(); +} + +// Comments in header +void Builder::accessChainStore(Id rvalue, Decoration nonUniform, spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) +{ + assert(accessChain.isRValue == false); + + transferAccessChainSwizzle(true); + Id base = collapseAccessChain(); + addDecoration(base, nonUniform); + + Id source = rvalue; + + // dynamic component should be gone + assert(accessChain.component == NoResult); + + // If swizzle still exists, it is out-of-order or not full, we must load the target vector, + // extract and insert elements to perform writeMask and/or swizzle. + if (accessChain.swizzle.size() > 0) { + Id tempBaseId = createLoad(base, spv::NoPrecision); + source = createLvalueSwizzle(getTypeId(tempBaseId), tempBaseId, source, accessChain.swizzle); + } + + // take LSB of alignment + alignment = alignment & ~(alignment & (alignment-1)); + if (getStorageClass(base) == StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + createStore(source, base, memoryAccess, scope, alignment); +} + +// Comments in header +Id Builder::accessChainLoad(Decoration precision, Decoration nonUniform, Id resultType, + spv::MemoryAccessMask memoryAccess, spv::Scope scope, unsigned int alignment) +{ + Id id; + + if (accessChain.isRValue) { + // transfer access chain, but try to stay in registers + transferAccessChainSwizzle(false); + if (accessChain.indexChain.size() > 0) { + Id swizzleBase = accessChain.preSwizzleBaseType != NoType ? accessChain.preSwizzleBaseType : resultType; + + // if all the accesses are constants, we can use OpCompositeExtract + std::vector indexes; + bool constant = true; + for (int i = 0; i < (int)accessChain.indexChain.size(); ++i) { + if (isConstantScalar(accessChain.indexChain[i])) + indexes.push_back(getConstantScalar(accessChain.indexChain[i])); + else { + constant = false; + break; + } + } + + if (constant) { + id = createCompositeExtract(accessChain.base, swizzleBase, indexes); + setPrecision(id, precision); + } else { + Id lValue = NoResult; + if (spvVersion >= Spv_1_4 && isValidInitializer(accessChain.base)) { + // make a new function variable for this r-value, using an initializer, + // and mark it as NonWritable so that downstream it can be detected as a lookup + // table + lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base), + "indexable", accessChain.base); + addDecoration(lValue, DecorationNonWritable); + } else { + lValue = createVariable(NoPrecision, StorageClassFunction, getTypeId(accessChain.base), + "indexable"); + // store into it + createStore(accessChain.base, lValue); + } + // move base to the new variable + accessChain.base = lValue; + accessChain.isRValue = false; + + // load through the access chain + id = createLoad(collapseAccessChain(), precision); + } + } else + id = accessChain.base; // no precision, it was set when this was defined + } else { + transferAccessChainSwizzle(true); + + // take LSB of alignment + alignment = alignment & ~(alignment & (alignment-1)); + if (getStorageClass(accessChain.base) == StorageClassPhysicalStorageBufferEXT) { + memoryAccess = (spv::MemoryAccessMask)(memoryAccess | spv::MemoryAccessAlignedMask); + } + + // load through the access chain + id = collapseAccessChain(); + // Apply nonuniform both to the access chain and the loaded value. + // Buffer accesses need the access chain decorated, and this is where + // loaded image types get decorated. TODO: This should maybe move to + // createImageTextureFunctionCall. + addDecoration(id, nonUniform); + id = createLoad(id, precision, memoryAccess, scope, alignment); + addDecoration(id, nonUniform); + } + + // Done, unless there are swizzles to do + if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) + return id; + + // Do remaining swizzling + + // Do the basic swizzle + if (accessChain.swizzle.size() > 0) { + Id swizzledType = getScalarTypeId(getTypeId(id)); + if (accessChain.swizzle.size() > 1) + swizzledType = makeVectorType(swizzledType, (int)accessChain.swizzle.size()); + id = createRvalueSwizzle(precision, swizzledType, id, accessChain.swizzle); + } + + // Do the dynamic component + if (accessChain.component != NoResult) + id = setPrecision(createVectorExtractDynamic(id, resultType, accessChain.component), precision); + + addDecoration(id, nonUniform); + return id; +} + +Id Builder::accessChainGetLValue() +{ + assert(accessChain.isRValue == false); + + transferAccessChainSwizzle(true); + Id lvalue = collapseAccessChain(); + + // If swizzle exists, it is out-of-order or not full, we must load the target vector, + // extract and insert elements to perform writeMask and/or swizzle. This does not + // go with getting a direct l-value pointer. + assert(accessChain.swizzle.size() == 0); + assert(accessChain.component == NoResult); + + return lvalue; +} + +// comment in header +Id Builder::accessChainGetInferredType() +{ + // anything to operate on? + if (accessChain.base == NoResult) + return NoType; + Id type = getTypeId(accessChain.base); + + // do initial dereference + if (! accessChain.isRValue) + type = getContainedTypeId(type); + + // dereference each index + for (auto it = accessChain.indexChain.cbegin(); it != accessChain.indexChain.cend(); ++it) { + if (isStructType(type)) + type = getContainedTypeId(type, getConstantScalar(*it)); + else + type = getContainedTypeId(type); + } + + // dereference swizzle + if (accessChain.swizzle.size() == 1) + type = getContainedTypeId(type); + else if (accessChain.swizzle.size() > 1) + type = makeVectorType(getContainedTypeId(type), (int)accessChain.swizzle.size()); + + // dereference component selection + if (accessChain.component) + type = getContainedTypeId(type); + + return type; +} + +void Builder::dump(std::vector& out) const +{ + // Header, before first instructions: + out.push_back(MagicNumber); + out.push_back(spvVersion); + out.push_back(builderNumber); + out.push_back(uniqueId + 1); + out.push_back(0); + + // Capabilities + for (auto it = capabilities.cbegin(); it != capabilities.cend(); ++it) { + Instruction capInst(0, 0, OpCapability); + capInst.addImmediateOperand(*it); + capInst.dump(out); + } + + for (auto it = extensions.cbegin(); it != extensions.cend(); ++it) { + Instruction extInst(0, 0, OpExtension); + extInst.addStringOperand(it->c_str()); + extInst.dump(out); + } + + dumpInstructions(out, imports); + Instruction memInst(0, 0, OpMemoryModel); + memInst.addImmediateOperand(addressModel); + memInst.addImmediateOperand(memoryModel); + memInst.dump(out); + + // Instructions saved up while building: + dumpInstructions(out, entryPoints); + dumpInstructions(out, executionModes); + + // Debug instructions + dumpInstructions(out, strings); + dumpSourceInstructions(out); + for (int e = 0; e < (int)sourceExtensions.size(); ++e) { + Instruction sourceExtInst(0, 0, OpSourceExtension); + sourceExtInst.addStringOperand(sourceExtensions[e]); + sourceExtInst.dump(out); + } + dumpInstructions(out, names); + dumpModuleProcesses(out); + + // Annotation instructions + dumpInstructions(out, decorations); + + dumpInstructions(out, constantsTypesGlobals); + dumpInstructions(out, externals); + + // The functions + module.dump(out); +} + +// +// Protected methods. +// + +// Turn the described access chain in 'accessChain' into an instruction(s) +// computing its address. This *cannot* include complex swizzles, which must +// be handled after this is called. +// +// Can generate code. +Id Builder::collapseAccessChain() +{ + assert(accessChain.isRValue == false); + + // did we already emit an access chain for this? + if (accessChain.instr != NoResult) + return accessChain.instr; + + // If we have a dynamic component, we can still transfer + // that into a final operand to the access chain. We need to remap the + // dynamic component through the swizzle to get a new dynamic component to + // update. + // + // This was not done in transferAccessChainSwizzle() because it might + // generate code. + remapDynamicSwizzle(); + if (accessChain.component != NoResult) { + // transfer the dynamic component to the access chain + accessChain.indexChain.push_back(accessChain.component); + accessChain.component = NoResult; + } + + // note that non-trivial swizzling is left pending + + // do we have an access chain? + if (accessChain.indexChain.size() == 0) + return accessChain.base; + + // emit the access chain + StorageClass storageClass = (StorageClass)module.getStorageClass(getTypeId(accessChain.base)); + accessChain.instr = createAccessChain(storageClass, accessChain.base, accessChain.indexChain); + + return accessChain.instr; +} + +// For a dynamic component selection of a swizzle. +// +// Turn the swizzle and dynamic component into just a dynamic component. +// +// Generates code. +void Builder::remapDynamicSwizzle() +{ + // do we have a swizzle to remap a dynamic component through? + if (accessChain.component != NoResult && accessChain.swizzle.size() > 1) { + // build a vector of the swizzle for the component to map into + std::vector components; + for (int c = 0; c < (int)accessChain.swizzle.size(); ++c) + components.push_back(makeUintConstant(accessChain.swizzle[c])); + Id mapType = makeVectorType(makeUintType(32), (int)accessChain.swizzle.size()); + Id map = makeCompositeConstant(mapType, components); + + // use it + accessChain.component = createVectorExtractDynamic(map, makeUintType(32), accessChain.component); + accessChain.swizzle.clear(); + } +} + +// clear out swizzle if it is redundant, that is reselecting the same components +// that would be present without the swizzle. +void Builder::simplifyAccessChainSwizzle() +{ + // If the swizzle has fewer components than the vector, it is subsetting, and must stay + // to preserve that fact. + if (getNumTypeComponents(accessChain.preSwizzleBaseType) > (int)accessChain.swizzle.size()) + return; + + // if components are out of order, it is a swizzle + for (unsigned int i = 0; i < accessChain.swizzle.size(); ++i) { + if (i != accessChain.swizzle[i]) + return; + } + + // otherwise, there is no need to track this swizzle + accessChain.swizzle.clear(); + if (accessChain.component == NoResult) + accessChain.preSwizzleBaseType = NoType; +} + +// To the extent any swizzling can become part of the chain +// of accesses instead of a post operation, make it so. +// If 'dynamic' is true, include transferring the dynamic component, +// otherwise, leave it pending. +// +// Does not generate code. just updates the access chain. +void Builder::transferAccessChainSwizzle(bool dynamic) +{ + // non existent? + if (accessChain.swizzle.size() == 0 && accessChain.component == NoResult) + return; + + // too complex? + // (this requires either a swizzle, or generating code for a dynamic component) + if (accessChain.swizzle.size() > 1) + return; + + // single component, either in the swizzle and/or dynamic component + if (accessChain.swizzle.size() == 1) { + assert(accessChain.component == NoResult); + // handle static component selection + accessChain.indexChain.push_back(makeUintConstant(accessChain.swizzle.front())); + accessChain.swizzle.clear(); + accessChain.preSwizzleBaseType = NoType; + } else if (dynamic && accessChain.component != NoResult) { + assert(accessChain.swizzle.size() == 0); + // handle dynamic component + accessChain.indexChain.push_back(accessChain.component); + accessChain.preSwizzleBaseType = NoType; + accessChain.component = NoResult; + } +} + +// Utility method for creating a new block and setting the insert point to +// be in it. This is useful for flow-control operations that need a "dummy" +// block proceeding them (e.g. instructions after a discard, etc). +void Builder::createAndSetNoPredecessorBlock(const char* /*name*/) +{ + Block* block = new Block(getUniqueId(), buildPoint->getParent()); + block->setUnreachable(); + buildPoint->getParent().addBlock(block); + setBuildPoint(block); + + // if (name) + // addName(block->getId(), name); +} + +// Comments in header +void Builder::createBranch(Block* block) +{ + Instruction* branch = new Instruction(OpBranch); + branch->addIdOperand(block->getId()); + buildPoint->addInstruction(std::unique_ptr(branch)); + block->addPredecessor(buildPoint); +} + +void Builder::createSelectionMerge(Block* mergeBlock, unsigned int control) +{ + Instruction* merge = new Instruction(OpSelectionMerge); + merge->addIdOperand(mergeBlock->getId()); + merge->addImmediateOperand(control); + buildPoint->addInstruction(std::unique_ptr(merge)); +} + +void Builder::createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, + const std::vector& operands) +{ + Instruction* merge = new Instruction(OpLoopMerge); + merge->addIdOperand(mergeBlock->getId()); + merge->addIdOperand(continueBlock->getId()); + merge->addImmediateOperand(control); + for (int op = 0; op < (int)operands.size(); ++op) + merge->addImmediateOperand(operands[op]); + buildPoint->addInstruction(std::unique_ptr(merge)); +} + +void Builder::createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock) +{ + Instruction* branch = new Instruction(OpBranchConditional); + branch->addIdOperand(condition); + branch->addIdOperand(thenBlock->getId()); + branch->addIdOperand(elseBlock->getId()); + buildPoint->addInstruction(std::unique_ptr(branch)); + thenBlock->addPredecessor(buildPoint); + elseBlock->addPredecessor(buildPoint); +} + +// OpSource +// [OpSourceContinued] +// ... +void Builder::dumpSourceInstructions(const spv::Id fileId, const std::string& text, + std::vector& out) const +{ + const int maxWordCount = 0xFFFF; + const int opSourceWordCount = 4; + const int nonNullBytesPerInstruction = 4 * (maxWordCount - opSourceWordCount) - 1; + + if (source != SourceLanguageUnknown) { + // OpSource Language Version File Source + Instruction sourceInst(NoResult, NoType, OpSource); + sourceInst.addImmediateOperand(source); + sourceInst.addImmediateOperand(sourceVersion); + // File operand + if (fileId != NoResult) { + sourceInst.addIdOperand(fileId); + // Source operand + if (text.size() > 0) { + int nextByte = 0; + std::string subString; + while ((int)text.size() - nextByte > 0) { + subString = text.substr(nextByte, nonNullBytesPerInstruction); + if (nextByte == 0) { + // OpSource + sourceInst.addStringOperand(subString.c_str()); + sourceInst.dump(out); + } else { + // OpSourcContinued + Instruction sourceContinuedInst(OpSourceContinued); + sourceContinuedInst.addStringOperand(subString.c_str()); + sourceContinuedInst.dump(out); + } + nextByte += nonNullBytesPerInstruction; + } + } else + sourceInst.dump(out); + } else + sourceInst.dump(out); + } +} + +// Dump an OpSource[Continued] sequence for the source and every include file +void Builder::dumpSourceInstructions(std::vector& out) const +{ + dumpSourceInstructions(sourceFileStringId, sourceText, out); + for (auto iItr = includeFiles.begin(); iItr != includeFiles.end(); ++iItr) + dumpSourceInstructions(iItr->first, *iItr->second, out); +} + +void Builder::dumpInstructions(std::vector& out, + const std::vector >& instructions) const +{ + for (int i = 0; i < (int)instructions.size(); ++i) { + instructions[i]->dump(out); + } +} + +void Builder::dumpModuleProcesses(std::vector& out) const +{ + for (int i = 0; i < (int)moduleProcesses.size(); ++i) { + Instruction moduleProcessed(OpModuleProcessed); + moduleProcessed.addStringOperand(moduleProcesses[i]); + moduleProcessed.dump(out); + } +} + +}; // end spv namespace diff --git a/third_party/glslang/SPIRV/SpvBuilder.h b/third_party/glslang/SPIRV/SpvBuilder.h new file mode 100644 index 0000000..873c0cd --- /dev/null +++ b/third_party/glslang/SPIRV/SpvBuilder.h @@ -0,0 +1,856 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// "Builder" is an interface to fully build SPIR-V IR. Allocate one of +// these to build (a thread safe) internal SPIR-V representation (IR), +// and then dump it as a binary stream according to the SPIR-V specification. +// +// A Builder has a 1:1 relationship with a SPIR-V module. +// + +#pragma once +#ifndef SpvBuilder_H +#define SpvBuilder_H + +#include "Logger.h" +#include "spirv.hpp" +#include "spvIR.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spv { + +typedef enum { + Spv_1_0 = (1 << 16), + Spv_1_1 = (1 << 16) | (1 << 8), + Spv_1_2 = (1 << 16) | (2 << 8), + Spv_1_3 = (1 << 16) | (3 << 8), + Spv_1_4 = (1 << 16) | (4 << 8), + Spv_1_5 = (1 << 16) | (5 << 8), +} SpvVersion; + +class Builder { +public: + Builder(unsigned int spvVersion, unsigned int userNumber, SpvBuildLogger* logger); + virtual ~Builder(); + + static const int maxMatrixSize = 4; + + unsigned int getSpvVersion() const { return spvVersion; } + + void setSource(spv::SourceLanguage lang, int version) + { + source = lang; + sourceVersion = version; + } + spv::Id getStringId(const std::string& str) + { + auto sItr = stringIds.find(str); + if (sItr != stringIds.end()) + return sItr->second; + spv::Id strId = getUniqueId(); + Instruction* fileString = new Instruction(strId, NoType, OpString); + const char* file_c_str = str.c_str(); + fileString->addStringOperand(file_c_str); + strings.push_back(std::unique_ptr(fileString)); + module.mapInstruction(fileString); + stringIds[file_c_str] = strId; + return strId; + } + void setSourceFile(const std::string& file) + { + sourceFileStringId = getStringId(file); + } + void setSourceText(const std::string& text) { sourceText = text; } + void addSourceExtension(const char* ext) { sourceExtensions.push_back(ext); } + void addModuleProcessed(const std::string& p) { moduleProcesses.push_back(p.c_str()); } + void setEmitOpLines() { emitOpLines = true; } + void addExtension(const char* ext) { extensions.insert(ext); } + void removeExtension(const char* ext) + { + extensions.erase(ext); + } + void addIncorporatedExtension(const char* ext, SpvVersion incorporatedVersion) + { + if (getSpvVersion() < static_cast(incorporatedVersion)) + addExtension(ext); + } + void promoteIncorporatedExtension(const char* baseExt, const char* promoExt, SpvVersion incorporatedVersion) + { + removeExtension(baseExt); + addIncorporatedExtension(promoExt, incorporatedVersion); + } + void addInclude(const std::string& name, const std::string& text) + { + spv::Id incId = getStringId(name); + includeFiles[incId] = &text; + } + Id import(const char*); + void setMemoryModel(spv::AddressingModel addr, spv::MemoryModel mem) + { + addressModel = addr; + memoryModel = mem; + } + + void addCapability(spv::Capability cap) { capabilities.insert(cap); } + + // To get a new for anything needing a new one. + Id getUniqueId() { return ++uniqueId; } + + // To get a set of new s, e.g., for a set of function parameters + Id getUniqueIds(int numIds) + { + Id id = uniqueId + 1; + uniqueId += numIds; + return id; + } + + // Generate OpLine for non-filename-based #line directives (ie no filename + // seen yet): Log the current line, and if different than the last one, + // issue a new OpLine using the new line and current source file name. + void setLine(int line); + + // If filename null, generate OpLine for non-filename-based line directives, + // else do filename-based: Log the current line and file, and if different + // than the last one, issue a new OpLine using the new line and file + // name. + void setLine(int line, const char* filename); + // Low-level OpLine. See setLine() for a layered helper. + void addLine(Id fileName, int line, int column); + + // For creating new types (will return old type if the requested one was already made). + Id makeVoidType(); + Id makeBoolType(); + Id makePointer(StorageClass, Id pointee); + Id makeForwardPointer(StorageClass); + Id makePointerFromForwardPointer(StorageClass, Id forwardPointerType, Id pointee); + Id makeIntegerType(int width, bool hasSign); // generic + Id makeIntType(int width) { return makeIntegerType(width, true); } + Id makeUintType(int width) { return makeIntegerType(width, false); } + Id makeFloatType(int width); + Id makeStructType(const std::vector& members, const char*); + Id makeStructResultType(Id type0, Id type1); + Id makeVectorType(Id component, int size); + Id makeMatrixType(Id component, int cols, int rows); + Id makeArrayType(Id element, Id sizeId, int stride); // 0 stride means no stride decoration + Id makeRuntimeArray(Id element); + Id makeFunctionType(Id returnType, const std::vector& paramTypes); + Id makeImageType(Id sampledType, Dim, bool depth, bool arrayed, bool ms, unsigned sampled, ImageFormat format); + Id makeSamplerType(); + Id makeSampledImageType(Id imageType); + Id makeCooperativeMatrixType(Id component, Id scope, Id rows, Id cols); + + // accelerationStructureNV type + Id makeAccelerationStructureType(); + // rayQueryEXT type + Id makeRayQueryType(); + + // For querying about types. + Id getTypeId(Id resultId) const { return module.getTypeId(resultId); } + Id getDerefTypeId(Id resultId) const; + Op getOpCode(Id id) const { return module.getInstruction(id)->getOpCode(); } + Op getTypeClass(Id typeId) const { return getOpCode(typeId); } + Op getMostBasicTypeClass(Id typeId) const; + int getNumComponents(Id resultId) const { return getNumTypeComponents(getTypeId(resultId)); } + int getNumTypeConstituents(Id typeId) const; + int getNumTypeComponents(Id typeId) const { return getNumTypeConstituents(typeId); } + Id getScalarTypeId(Id typeId) const; + Id getContainedTypeId(Id typeId) const; + Id getContainedTypeId(Id typeId, int) const; + StorageClass getTypeStorageClass(Id typeId) const { return module.getStorageClass(typeId); } + ImageFormat getImageTypeFormat(Id typeId) const + { return (ImageFormat)module.getInstruction(typeId)->getImmediateOperand(6); } + + bool isPointer(Id resultId) const { return isPointerType(getTypeId(resultId)); } + bool isScalar(Id resultId) const { return isScalarType(getTypeId(resultId)); } + bool isVector(Id resultId) const { return isVectorType(getTypeId(resultId)); } + bool isMatrix(Id resultId) const { return isMatrixType(getTypeId(resultId)); } + bool isCooperativeMatrix(Id resultId)const { return isCooperativeMatrixType(getTypeId(resultId)); } + bool isAggregate(Id resultId) const { return isAggregateType(getTypeId(resultId)); } + bool isSampledImage(Id resultId) const { return isSampledImageType(getTypeId(resultId)); } + + bool isBoolType(Id typeId) + { return groupedTypes[OpTypeBool].size() > 0 && typeId == groupedTypes[OpTypeBool].back()->getResultId(); } + bool isIntType(Id typeId) const + { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) != 0; } + bool isUintType(Id typeId) const + { return getTypeClass(typeId) == OpTypeInt && module.getInstruction(typeId)->getImmediateOperand(1) == 0; } + bool isFloatType(Id typeId) const { return getTypeClass(typeId) == OpTypeFloat; } + bool isPointerType(Id typeId) const { return getTypeClass(typeId) == OpTypePointer; } + bool isScalarType(Id typeId) const + { return getTypeClass(typeId) == OpTypeFloat || getTypeClass(typeId) == OpTypeInt || + getTypeClass(typeId) == OpTypeBool; } + bool isVectorType(Id typeId) const { return getTypeClass(typeId) == OpTypeVector; } + bool isMatrixType(Id typeId) const { return getTypeClass(typeId) == OpTypeMatrix; } + bool isStructType(Id typeId) const { return getTypeClass(typeId) == OpTypeStruct; } + bool isArrayType(Id typeId) const { return getTypeClass(typeId) == OpTypeArray; } +#ifdef GLSLANG_WEB + bool isCooperativeMatrixType(Id typeId)const { return false; } +#else + bool isCooperativeMatrixType(Id typeId)const { return getTypeClass(typeId) == OpTypeCooperativeMatrixNV; } +#endif + bool isAggregateType(Id typeId) const + { return isArrayType(typeId) || isStructType(typeId) || isCooperativeMatrixType(typeId); } + bool isImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeImage; } + bool isSamplerType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampler; } + bool isSampledImageType(Id typeId) const { return getTypeClass(typeId) == OpTypeSampledImage; } + bool containsType(Id typeId, Op typeOp, unsigned int width) const; + bool containsPhysicalStorageBufferOrArray(Id typeId) const; + + bool isConstantOpCode(Op opcode) const; + bool isSpecConstantOpCode(Op opcode) const; + bool isConstant(Id resultId) const { return isConstantOpCode(getOpCode(resultId)); } + bool isConstantScalar(Id resultId) const { return getOpCode(resultId) == OpConstant; } + bool isSpecConstant(Id resultId) const { return isSpecConstantOpCode(getOpCode(resultId)); } + unsigned int getConstantScalar(Id resultId) const + { return module.getInstruction(resultId)->getImmediateOperand(0); } + StorageClass getStorageClass(Id resultId) const { return getTypeStorageClass(getTypeId(resultId)); } + + bool isVariableOpCode(Op opcode) const { return opcode == OpVariable; } + bool isVariable(Id resultId) const { return isVariableOpCode(getOpCode(resultId)); } + bool isGlobalStorage(Id resultId) const { return getStorageClass(resultId) != StorageClassFunction; } + bool isGlobalVariable(Id resultId) const { return isVariable(resultId) && isGlobalStorage(resultId); } + // See if a resultId is valid for use as an initializer. + bool isValidInitializer(Id resultId) const { return isConstant(resultId) || isGlobalVariable(resultId); } + + int getScalarTypeWidth(Id typeId) const + { + Id scalarTypeId = getScalarTypeId(typeId); + assert(getTypeClass(scalarTypeId) == OpTypeInt || getTypeClass(scalarTypeId) == OpTypeFloat); + return module.getInstruction(scalarTypeId)->getImmediateOperand(0); + } + + int getTypeNumColumns(Id typeId) const + { + assert(isMatrixType(typeId)); + return getNumTypeConstituents(typeId); + } + int getNumColumns(Id resultId) const { return getTypeNumColumns(getTypeId(resultId)); } + int getTypeNumRows(Id typeId) const + { + assert(isMatrixType(typeId)); + return getNumTypeComponents(getContainedTypeId(typeId)); + } + int getNumRows(Id resultId) const { return getTypeNumRows(getTypeId(resultId)); } + + Dim getTypeDimensionality(Id typeId) const + { + assert(isImageType(typeId)); + return (Dim)module.getInstruction(typeId)->getImmediateOperand(1); + } + Id getImageType(Id resultId) const + { + Id typeId = getTypeId(resultId); + assert(isImageType(typeId) || isSampledImageType(typeId)); + return isSampledImageType(typeId) ? module.getInstruction(typeId)->getIdOperand(0) : typeId; + } + bool isArrayedImageType(Id typeId) const + { + assert(isImageType(typeId)); + return module.getInstruction(typeId)->getImmediateOperand(3) != 0; + } + + // For making new constants (will return old constant if the requested one was already made). + Id makeBoolConstant(bool b, bool specConstant = false); + Id makeInt8Constant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(8), (unsigned)i, specConstant); } + Id makeUint8Constant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(8), u, specConstant); } + Id makeInt16Constant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(16), (unsigned)i, specConstant); } + Id makeUint16Constant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(16), u, specConstant); } + Id makeIntConstant(int i, bool specConstant = false) + { return makeIntConstant(makeIntType(32), (unsigned)i, specConstant); } + Id makeUintConstant(unsigned u, bool specConstant = false) + { return makeIntConstant(makeUintType(32), u, specConstant); } + Id makeInt64Constant(long long i, bool specConstant = false) + { return makeInt64Constant(makeIntType(64), (unsigned long long)i, specConstant); } + Id makeUint64Constant(unsigned long long u, bool specConstant = false) + { return makeInt64Constant(makeUintType(64), u, specConstant); } + Id makeFloatConstant(float f, bool specConstant = false); + Id makeDoubleConstant(double d, bool specConstant = false); + Id makeFloat16Constant(float f16, bool specConstant = false); + Id makeFpConstant(Id type, double d, bool specConstant = false); + + // Turn the array of constants into a proper spv constant of the requested type. + Id makeCompositeConstant(Id type, const std::vector& comps, bool specConst = false); + + // Methods for adding information outside the CFG. + Instruction* addEntryPoint(ExecutionModel, Function*, const char* name); + void addExecutionMode(Function*, ExecutionMode mode, int value1 = -1, int value2 = -1, int value3 = -1); + void addExecutionMode(Function*, ExecutionMode mode, const std::vector& literals); + void addExecutionModeId(Function*, ExecutionMode mode, const std::vector& operandIds); + void addName(Id, const char* name); + void addMemberName(Id, int member, const char* name); + void addDecoration(Id, Decoration, int num = -1); + void addDecoration(Id, Decoration, const char*); + void addDecoration(Id, Decoration, const std::vector& literals); + void addDecoration(Id, Decoration, const std::vector& strings); + void addDecorationId(Id id, Decoration, Id idDecoration); + void addDecorationId(Id id, Decoration, const std::vector& operandIds); + void addMemberDecoration(Id, unsigned int member, Decoration, int num = -1); + void addMemberDecoration(Id, unsigned int member, Decoration, const char*); + void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector& literals); + void addMemberDecoration(Id, unsigned int member, Decoration, const std::vector& strings); + + // At the end of what block do the next create*() instructions go? + void setBuildPoint(Block* bp) { buildPoint = bp; } + Block* getBuildPoint() const { return buildPoint; } + + // Make the entry-point function. The returned pointer is only valid + // for the lifetime of this builder. + Function* makeEntryPoint(const char*); + + // Make a shader-style function, and create its entry block if entry is non-zero. + // Return the function, pass back the entry. + // The returned pointer is only valid for the lifetime of this builder. + Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, + const std::vector& paramTypes, const std::vector>& precisions, Block **entry = 0); + + // Create a return. An 'implicit' return is one not appearing in the source + // code. In the case of an implicit return, no post-return block is inserted. + void makeReturn(bool implicit, Id retVal = 0); + + // Generate all the code needed to finish up a function. + void leaveFunction(); + + // Create a discard or terminate-invocation. + void makeDiscard(); + void makeTerminateInvocation(); + + // Create a global or function local or IO variable. + Id createVariable(Decoration precision, StorageClass, Id type, const char* name = nullptr, + Id initializer = NoResult); + + // Create an intermediate with an undefined value. + Id createUndefined(Id type); + + // Store into an Id and return the l-value + void createStore(Id rValue, Id lValue, spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // Load from an Id and return it + Id createLoad(Id lValue, spv::Decoration precision, + spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // Create an OpAccessChain instruction + Id createAccessChain(StorageClass, Id base, const std::vector& offsets); + + // Create an OpArrayLength instruction + Id createArrayLength(Id base, unsigned int member); + + // Create an OpCooperativeMatrixLengthNV instruction + Id createCooperativeMatrixLength(Id type); + + // Create an OpCompositeExtract instruction + Id createCompositeExtract(Id composite, Id typeId, unsigned index); + Id createCompositeExtract(Id composite, Id typeId, const std::vector& indexes); + Id createCompositeInsert(Id object, Id composite, Id typeId, unsigned index); + Id createCompositeInsert(Id object, Id composite, Id typeId, const std::vector& indexes); + + Id createVectorExtractDynamic(Id vector, Id typeId, Id componentIndex); + Id createVectorInsertDynamic(Id vector, Id typeId, Id component, Id componentIndex); + + void createNoResultOp(Op); + void createNoResultOp(Op, Id operand); + void createNoResultOp(Op, const std::vector& operands); + void createNoResultOp(Op, const std::vector& operands); + void createControlBarrier(Scope execution, Scope memory, MemorySemanticsMask); + void createMemoryBarrier(unsigned executionScope, unsigned memorySemantics); + Id createUnaryOp(Op, Id typeId, Id operand); + Id createBinOp(Op, Id typeId, Id operand1, Id operand2); + Id createTriOp(Op, Id typeId, Id operand1, Id operand2, Id operand3); + Id createOp(Op, Id typeId, const std::vector& operands); + Id createOp(Op, Id typeId, const std::vector& operands); + Id createFunctionCall(spv::Function*, const std::vector&); + Id createSpecConstantOp(Op, Id typeId, const std::vector& operands, const std::vector& literals); + + // Take an rvalue (source) and a set of channels to extract from it to + // make a new rvalue, which is returned. + Id createRvalueSwizzle(Decoration precision, Id typeId, Id source, const std::vector& channels); + + // Take a copy of an lvalue (target) and a source of components, and set the + // source components into the lvalue where the 'channels' say to put them. + // An updated version of the target is returned. + // (No true lvalue or stores are used.) + Id createLvalueSwizzle(Id typeId, Id target, Id source, const std::vector& channels); + + // If both the id and precision are valid, the id + // gets tagged with the requested precision. + // The passed in id is always the returned id, to simplify use patterns. + Id setPrecision(Id id, Decoration precision) + { + if (precision != NoPrecision && id != NoResult) + addDecoration(id, precision); + + return id; + } + + // Can smear a scalar to a vector for the following forms: + // - promoteScalar(scalar, vector) // smear scalar to width of vector + // - promoteScalar(vector, scalar) // smear scalar to width of vector + // - promoteScalar(pointer, scalar) // smear scalar to width of what pointer points to + // - promoteScalar(scalar, scalar) // do nothing + // Other forms are not allowed. + // + // Generally, the type of 'scalar' does not need to be the same type as the components in 'vector'. + // The type of the created vector is a vector of components of the same type as the scalar. + // + // Note: One of the arguments will change, with the result coming back that way rather than + // through the return value. + void promoteScalar(Decoration precision, Id& left, Id& right); + + // Make a value by smearing the scalar to fill the type. + // vectorType should be the correct type for making a vector of scalarVal. + // (No conversions are done.) + Id smearScalar(Decoration precision, Id scalarVal, Id vectorType); + + // Create a call to a built-in function. + Id createBuiltinCall(Id resultType, Id builtins, int entryPoint, const std::vector& args); + + // List of parameters used to create a texture operation + struct TextureParameters { + Id sampler; + Id coords; + Id bias; + Id lod; + Id Dref; + Id offset; + Id offsets; + Id gradX; + Id gradY; + Id sample; + Id component; + Id texelOut; + Id lodClamp; + Id granularity; + Id coarse; + bool nonprivate; + bool volatil; + }; + + // Select the correct texture operation based on all inputs, and emit the correct instruction + Id createTextureCall(Decoration precision, Id resultType, bool sparse, bool fetch, bool proj, bool gather, + bool noImplicit, const TextureParameters&, ImageOperandsMask); + + // Emit the OpTextureQuery* instruction that was passed in. + // Figure out the right return value and type, and return it. + Id createTextureQueryCall(Op, const TextureParameters&, bool isUnsignedResult); + + Id createSamplePositionCall(Decoration precision, Id, Id); + + Id createBitFieldExtractCall(Decoration precision, Id, Id, Id, bool isSigned); + Id createBitFieldInsertCall(Decoration precision, Id, Id, Id, Id); + + // Reduction comparison for composites: For equal and not-equal resulting in a scalar. + Id createCompositeCompare(Decoration precision, Id, Id, bool /* true if for equal, false if for not-equal */); + + // OpCompositeConstruct + Id createCompositeConstruct(Id typeId, const std::vector& constituents); + + // vector or scalar constructor + Id createConstructor(Decoration precision, const std::vector& sources, Id resultTypeId); + + // matrix constructor + Id createMatrixConstructor(Decoration precision, const std::vector& sources, Id constructee); + + // Helper to use for building nested control flow with if-then-else. + class If { + public: + If(Id condition, unsigned int ctrl, Builder& builder); + ~If() {} + + void makeBeginElse(); + void makeEndIf(); + + private: + If(const If&); + If& operator=(If&); + + Builder& builder; + Id condition; + unsigned int control; + Function* function; + Block* headerBlock; + Block* thenBlock; + Block* elseBlock; + Block* mergeBlock; + }; + + // Make a switch statement. A switch has 'numSegments' of pieces of code, not containing + // any case/default labels, all separated by one or more case/default labels. Each possible + // case value v is a jump to the caseValues[v] segment. The defaultSegment is also in this + // number space. How to compute the value is given by 'condition', as in switch(condition). + // + // The SPIR-V Builder will maintain the stack of post-switch merge blocks for nested switches. + // + // Use a defaultSegment < 0 if there is no default segment (to branch to post switch). + // + // Returns the right set of basic blocks to start each code segment with, so that the caller's + // recursion stack can hold the memory for it. + // + void makeSwitch(Id condition, unsigned int control, int numSegments, const std::vector& caseValues, + const std::vector& valueToSegment, int defaultSegment, std::vector& segmentBB); + + // Add a branch to the innermost switch's merge block. + void addSwitchBreak(); + + // Move to the next code segment, passing in the return argument in makeSwitch() + void nextSwitchSegment(std::vector& segmentBB, int segment); + + // Finish off the innermost switch. + void endSwitch(std::vector& segmentBB); + + struct LoopBlocks { + LoopBlocks(Block& head, Block& body, Block& merge, Block& continue_target) : + head(head), body(body), merge(merge), continue_target(continue_target) { } + Block &head, &body, &merge, &continue_target; + private: + LoopBlocks(); + LoopBlocks& operator=(const LoopBlocks&) = delete; + }; + + // Start a new loop and prepare the builder to generate code for it. Until + // closeLoop() is called for this loop, createLoopContinue() and + // createLoopExit() will target its corresponding blocks. + LoopBlocks& makeNewLoop(); + + // Create a new block in the function containing the build point. Memory is + // owned by the function object. + Block& makeNewBlock(); + + // Add a branch to the continue_target of the current (innermost) loop. + void createLoopContinue(); + + // Add an exit (e.g. "break") from the innermost loop that we're currently + // in. + void createLoopExit(); + + // Close the innermost loop that you're in + void closeLoop(); + + // + // Access chain design for an R-Value vs. L-Value: + // + // There is a single access chain the builder is building at + // any particular time. Such a chain can be used to either to a load or + // a store, when desired. + // + // Expressions can be r-values, l-values, or both, or only r-values: + // a[b.c].d = .... // l-value + // ... = a[b.c].d; // r-value, that also looks like an l-value + // ++a[b.c].d; // r-value and l-value + // (x + y)[2]; // r-value only, can't possibly be l-value + // + // Computing an r-value means generating code. Hence, + // r-values should only be computed when they are needed, not speculatively. + // + // Computing an l-value means saving away information for later use in the compiler, + // no code is generated until the l-value is later dereferenced. It is okay + // to speculatively generate an l-value, just not okay to speculatively dereference it. + // + // The base of the access chain (the left-most variable or expression + // from which everything is based) can be set either as an l-value + // or as an r-value. Most efficient would be to set an l-value if one + // is available. If an expression was evaluated, the resulting r-value + // can be set as the chain base. + // + // The users of this single access chain can save and restore if they + // want to nest or manage multiple chains. + // + + struct AccessChain { + Id base; // for l-values, pointer to the base object, for r-values, the base object + std::vector indexChain; + Id instr; // cache the instruction that generates this access chain + std::vector swizzle; // each std::vector element selects the next GLSL component number + Id component; // a dynamic component index, can coexist with a swizzle, + // done after the swizzle, NoResult if not present + Id preSwizzleBaseType; // dereferenced type, before swizzle or component is applied; + // NoType unless a swizzle or component is present + bool isRValue; // true if 'base' is an r-value, otherwise, base is an l-value + unsigned int alignment; // bitwise OR of alignment values passed in. Accumulates worst alignment. + // Only tracks base and (optional) component selection alignment. + + // Accumulate whether anything in the chain of structures has coherent decorations. + struct CoherentFlags { + CoherentFlags() { clear(); } +#ifdef GLSLANG_WEB + void clear() { } + bool isVolatile() const { return false; } + CoherentFlags operator |=(const CoherentFlags &other) { return *this; } +#else + bool isVolatile() const { return volatil; } + bool anyCoherent() const { + return coherent || devicecoherent || queuefamilycoherent || workgroupcoherent || + subgroupcoherent || shadercallcoherent; + } + + unsigned coherent : 1; + unsigned devicecoherent : 1; + unsigned queuefamilycoherent : 1; + unsigned workgroupcoherent : 1; + unsigned subgroupcoherent : 1; + unsigned shadercallcoherent : 1; + unsigned nonprivate : 1; + unsigned volatil : 1; + unsigned isImage : 1; + + void clear() { + coherent = 0; + devicecoherent = 0; + queuefamilycoherent = 0; + workgroupcoherent = 0; + subgroupcoherent = 0; + shadercallcoherent = 0; + nonprivate = 0; + volatil = 0; + isImage = 0; + } + + CoherentFlags operator |=(const CoherentFlags &other) { + coherent |= other.coherent; + devicecoherent |= other.devicecoherent; + queuefamilycoherent |= other.queuefamilycoherent; + workgroupcoherent |= other.workgroupcoherent; + subgroupcoherent |= other.subgroupcoherent; + shadercallcoherent |= other.shadercallcoherent; + nonprivate |= other.nonprivate; + volatil |= other.volatil; + isImage |= other.isImage; + return *this; + } +#endif + }; + CoherentFlags coherentFlags; + }; + + // + // the SPIR-V builder maintains a single active chain that + // the following methods operate on + // + + // for external save and restore + AccessChain getAccessChain() { return accessChain; } + void setAccessChain(AccessChain newChain) { accessChain = newChain; } + + // clear accessChain + void clearAccessChain(); + + // set new base as an l-value base + void setAccessChainLValue(Id lValue) + { + assert(isPointer(lValue)); + accessChain.base = lValue; + } + + // set new base value as an r-value + void setAccessChainRValue(Id rValue) + { + accessChain.isRValue = true; + accessChain.base = rValue; + } + + // push offset onto the end of the chain + void accessChainPush(Id offset, AccessChain::CoherentFlags coherentFlags, unsigned int alignment) + { + accessChain.indexChain.push_back(offset); + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + } + + // push new swizzle onto the end of any existing swizzle, merging into a single swizzle + void accessChainPushSwizzle(std::vector& swizzle, Id preSwizzleBaseType, + AccessChain::CoherentFlags coherentFlags, unsigned int alignment); + + // push a dynamic component selection onto the access chain, only applicable with a + // non-trivial swizzle or no swizzle + void accessChainPushComponent(Id component, Id preSwizzleBaseType, AccessChain::CoherentFlags coherentFlags, + unsigned int alignment) + { + if (accessChain.swizzle.size() != 1) { + accessChain.component = component; + if (accessChain.preSwizzleBaseType == NoType) + accessChain.preSwizzleBaseType = preSwizzleBaseType; + } + accessChain.coherentFlags |= coherentFlags; + accessChain.alignment |= alignment; + } + + // use accessChain and swizzle to store value + void accessChainStore(Id rvalue, Decoration nonUniform, + spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, + spv::Scope scope = spv::ScopeMax, unsigned int alignment = 0); + + // use accessChain and swizzle to load an r-value + Id accessChainLoad(Decoration precision, Decoration nonUniform, Id ResultType, + spv::MemoryAccessMask memoryAccess = spv::MemoryAccessMaskNone, spv::Scope scope = spv::ScopeMax, + unsigned int alignment = 0); + + // Return whether or not the access chain can be represented in SPIR-V + // as an l-value. + // E.g., a[3].yx cannot be, while a[3].y and a[3].y[x] can be. + bool isSpvLvalue() const { return accessChain.swizzle.size() <= 1; } + + // get the direct pointer for an l-value + Id accessChainGetLValue(); + + // Get the inferred SPIR-V type of the result of the current access chain, + // based on the type of the base and the chain of dereferences. + Id accessChainGetInferredType(); + + // Add capabilities, extensions, remove unneeded decorations, etc., + // based on the resulting SPIR-V. + void postProcess(); + + // Prune unreachable blocks in the CFG and remove unneeded decorations. + void postProcessCFG(); + +#ifndef GLSLANG_WEB + // Add capabilities, extensions based on instructions in the module. + void postProcessFeatures(); + // Hook to visit each instruction in a block in a function + void postProcess(Instruction&); + // Hook to visit each non-32-bit sized float/int operation in a block. + void postProcessType(const Instruction&, spv::Id typeId); +#endif + + void dump(std::vector&) const; + + void createBranch(Block* block); + void createConditionalBranch(Id condition, Block* thenBlock, Block* elseBlock); + void createLoopMerge(Block* mergeBlock, Block* continueBlock, unsigned int control, + const std::vector& operands); + + // Sets to generate opcode for specialization constants. + void setToSpecConstCodeGenMode() { generatingOpCodeForSpecConst = true; } + // Sets to generate opcode for non-specialization constants (normal mode). + void setToNormalCodeGenMode() { generatingOpCodeForSpecConst = false; } + // Check if the builder is generating code for spec constants. + bool isInSpecConstCodeGenMode() { return generatingOpCodeForSpecConst; } + + protected: + Id makeIntConstant(Id typeId, unsigned value, bool specConstant); + Id makeInt64Constant(Id typeId, unsigned long long value, bool specConstant); + Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value); + Id findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2); + Id findCompositeConstant(Op typeClass, Id typeId, const std::vector& comps); + Id findStructConstant(Id typeId, const std::vector& comps); + Id collapseAccessChain(); + void remapDynamicSwizzle(); + void transferAccessChainSwizzle(bool dynamic); + void simplifyAccessChainSwizzle(); + void createAndSetNoPredecessorBlock(const char*); + void createSelectionMerge(Block* mergeBlock, unsigned int control); + void dumpSourceInstructions(std::vector&) const; + void dumpSourceInstructions(const spv::Id fileId, const std::string& text, std::vector&) const; + void dumpInstructions(std::vector&, const std::vector >&) const; + void dumpModuleProcesses(std::vector&) const; + spv::MemoryAccessMask sanitizeMemoryAccessForStorageClass(spv::MemoryAccessMask memoryAccess, StorageClass sc) + const; + + unsigned int spvVersion; // the version of SPIR-V to emit in the header + SourceLanguage source; + int sourceVersion; + spv::Id sourceFileStringId; + std::string sourceText; + int currentLine; + const char* currentFile; + bool emitOpLines; + std::set extensions; + std::vector sourceExtensions; + std::vector moduleProcesses; + AddressingModel addressModel; + MemoryModel memoryModel; + std::set capabilities; + int builderNumber; + Module module; + Block* buildPoint; + Id uniqueId; + Function* entryPointFunction; + bool generatingOpCodeForSpecConst; + AccessChain accessChain; + + // special blocks of instructions for output + std::vector > strings; + std::vector > imports; + std::vector > entryPoints; + std::vector > executionModes; + std::vector > names; + std::vector > decorations; + std::vector > constantsTypesGlobals; + std::vector > externals; + std::vector > functions; + + // not output, internally used for quick & dirty canonical (unique) creation + + // map type opcodes to constant inst. + std::unordered_map> groupedConstants; + // map struct-id to constant instructions + std::unordered_map> groupedStructConstants; + // map type opcodes to type instructions + std::unordered_map> groupedTypes; + + // stack of switches + std::stack switchMerges; + + // Our loop stack. + std::stack loops; + + // map from strings to their string ids + std::unordered_map stringIds; + + // map from include file name ids to their contents + std::map includeFiles; + + // The stream for outputting warnings and errors. + SpvBuildLogger* logger; +}; // end Builder class + +}; // end spv namespace + +#endif // SpvBuilder_H diff --git a/third_party/glslang/SPIRV/SpvPostProcess.cpp b/third_party/glslang/SPIRV/SpvPostProcess.cpp new file mode 100644 index 0000000..d40174d --- /dev/null +++ b/third_party/glslang/SPIRV/SpvPostProcess.cpp @@ -0,0 +1,450 @@ +// +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Post-processing for SPIR-V IR, in internal form, not standard binary form. +// + +#include +#include + +#include +#include +#include + +#include "SpvBuilder.h" + +#include "spirv.hpp" +#include "GlslangToSpv.h" +#include "SpvBuilder.h" +namespace spv { + #include "GLSL.std.450.h" + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" +} + +namespace spv { + +#ifndef GLSLANG_WEB +// Hook to visit each operand type and result type of an instruction. +// Will be called multiple times for one instruction, once for each typed +// operand and the result. +void Builder::postProcessType(const Instruction& inst, Id typeId) +{ + // Characterize the type being questioned + Id basicTypeOp = getMostBasicTypeClass(typeId); + int width = 0; + if (basicTypeOp == OpTypeFloat || basicTypeOp == OpTypeInt) + width = getScalarTypeWidth(typeId); + + // Do opcode-specific checks + switch (inst.getOpCode()) { + case OpLoad: + case OpStore: + if (basicTypeOp == OpTypeStruct) { + if (containsType(typeId, OpTypeInt, 8)) + addCapability(CapabilityInt8); + if (containsType(typeId, OpTypeInt, 16)) + addCapability(CapabilityInt16); + if (containsType(typeId, OpTypeFloat, 16)) + addCapability(CapabilityFloat16); + } else { + StorageClass storageClass = getStorageClass(inst.getIdOperand(0)); + if (width == 8) { + switch (storageClass) { + case StorageClassPhysicalStorageBufferEXT: + case StorageClassUniform: + case StorageClassStorageBuffer: + case StorageClassPushConstant: + break; + default: + addCapability(CapabilityInt8); + break; + } + } else if (width == 16) { + switch (storageClass) { + case StorageClassPhysicalStorageBufferEXT: + case StorageClassUniform: + case StorageClassStorageBuffer: + case StorageClassPushConstant: + case StorageClassInput: + case StorageClassOutput: + break; + default: + if (basicTypeOp == OpTypeInt) + addCapability(CapabilityInt16); + if (basicTypeOp == OpTypeFloat) + addCapability(CapabilityFloat16); + break; + } + } + } + break; + case OpAccessChain: + case OpPtrAccessChain: + case OpCopyObject: + break; + case OpFConvert: + case OpSConvert: + case OpUConvert: + // Look for any 8/16-bit storage capabilities. If there are none, assume that + // the convert instruction requires the Float16/Int8/16 capability. + if (containsType(typeId, OpTypeFloat, 16) || containsType(typeId, OpTypeInt, 16)) { + bool foundStorage = false; + for (auto it = capabilities.begin(); it != capabilities.end(); ++it) { + spv::Capability cap = *it; + if (cap == spv::CapabilityStorageInputOutput16 || + cap == spv::CapabilityStoragePushConstant16 || + cap == spv::CapabilityStorageUniformBufferBlock16 || + cap == spv::CapabilityStorageUniform16) { + foundStorage = true; + break; + } + } + if (!foundStorage) { + if (containsType(typeId, OpTypeFloat, 16)) + addCapability(CapabilityFloat16); + if (containsType(typeId, OpTypeInt, 16)) + addCapability(CapabilityInt16); + } + } + if (containsType(typeId, OpTypeInt, 8)) { + bool foundStorage = false; + for (auto it = capabilities.begin(); it != capabilities.end(); ++it) { + spv::Capability cap = *it; + if (cap == spv::CapabilityStoragePushConstant8 || + cap == spv::CapabilityUniformAndStorageBuffer8BitAccess || + cap == spv::CapabilityStorageBuffer8BitAccess) { + foundStorage = true; + break; + } + } + if (!foundStorage) { + addCapability(CapabilityInt8); + } + } + break; + case OpExtInst: + switch (inst.getImmediateOperand(1)) { + case GLSLstd450Frexp: + case GLSLstd450FrexpStruct: + if (getSpvVersion() < glslang::EShTargetSpv_1_3 && containsType(typeId, OpTypeInt, 16)) + addExtension(spv::E_SPV_AMD_gpu_shader_int16); + break; + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + if (getSpvVersion() < glslang::EShTargetSpv_1_3 && containsType(typeId, OpTypeFloat, 16)) + addExtension(spv::E_SPV_AMD_gpu_shader_half_float); + break; + default: + break; + } + break; + default: + if (basicTypeOp == OpTypeFloat && width == 16) + addCapability(CapabilityFloat16); + if (basicTypeOp == OpTypeInt && width == 16) + addCapability(CapabilityInt16); + if (basicTypeOp == OpTypeInt && width == 8) + addCapability(CapabilityInt8); + break; + } +} + +// Called for each instruction that resides in a block. +void Builder::postProcess(Instruction& inst) +{ + // Add capabilities based simply on the opcode. + switch (inst.getOpCode()) { + case OpExtInst: + switch (inst.getImmediateOperand(1)) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + addCapability(CapabilityInterpolationFunction); + break; + default: + break; + } + break; + case OpDPdxFine: + case OpDPdyFine: + case OpFwidthFine: + case OpDPdxCoarse: + case OpDPdyCoarse: + case OpFwidthCoarse: + addCapability(CapabilityDerivativeControl); + break; + + case OpImageQueryLod: + case OpImageQuerySize: + case OpImageQuerySizeLod: + case OpImageQuerySamples: + case OpImageQueryLevels: + addCapability(CapabilityImageQuery); + break; + + case OpGroupNonUniformPartitionNV: + addExtension(E_SPV_NV_shader_subgroup_partitioned); + addCapability(CapabilityGroupNonUniformPartitionedNV); + break; + + case OpLoad: + case OpStore: + { + // For any load/store to a PhysicalStorageBufferEXT, walk the accesschain + // index list to compute the misalignment. The pre-existing alignment value + // (set via Builder::AccessChain::alignment) only accounts for the base of + // the reference type and any scalar component selection in the accesschain, + // and this function computes the rest from the SPIR-V Offset decorations. + Instruction *accessChain = module.getInstruction(inst.getIdOperand(0)); + if (accessChain->getOpCode() == OpAccessChain) { + Instruction *base = module.getInstruction(accessChain->getIdOperand(0)); + // Get the type of the base of the access chain. It must be a pointer type. + Id typeId = base->getTypeId(); + Instruction *type = module.getInstruction(typeId); + assert(type->getOpCode() == OpTypePointer); + if (type->getImmediateOperand(0) != StorageClassPhysicalStorageBufferEXT) { + break; + } + // Get the pointee type. + typeId = type->getIdOperand(1); + type = module.getInstruction(typeId); + // Walk the index list for the access chain. For each index, find any + // misalignment that can apply when accessing the member/element via + // Offset/ArrayStride/MatrixStride decorations, and bitwise OR them all + // together. + int alignment = 0; + for (int i = 1; i < accessChain->getNumOperands(); ++i) { + Instruction *idx = module.getInstruction(accessChain->getIdOperand(i)); + if (type->getOpCode() == OpTypeStruct) { + assert(idx->getOpCode() == OpConstant); + unsigned int c = idx->getImmediateOperand(0); + + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getOpCode() == OpMemberDecorate && + decoration.get()->getIdOperand(0) == typeId && + decoration.get()->getImmediateOperand(1) == c && + (decoration.get()->getImmediateOperand(2) == DecorationOffset || + decoration.get()->getImmediateOperand(2) == DecorationMatrixStride)) { + alignment |= decoration.get()->getImmediateOperand(3); + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + // get the next member type + typeId = type->getIdOperand(c); + type = module.getInstruction(typeId); + } else if (type->getOpCode() == OpTypeArray || + type->getOpCode() == OpTypeRuntimeArray) { + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getOpCode() == OpDecorate && + decoration.get()->getIdOperand(0) == typeId && + decoration.get()->getImmediateOperand(1) == DecorationArrayStride) { + alignment |= decoration.get()->getImmediateOperand(2); + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + // Get the element type + typeId = type->getIdOperand(0); + type = module.getInstruction(typeId); + } else { + // Once we get to any non-aggregate type, we're done. + break; + } + } + assert(inst.getNumOperands() >= 3); + unsigned int memoryAccess = inst.getImmediateOperand((inst.getOpCode() == OpStore) ? 2 : 1); + assert(memoryAccess & MemoryAccessAlignedMask); + static_cast(memoryAccess); + // Compute the index of the alignment operand. + int alignmentIdx = 2; + if (inst.getOpCode() == OpStore) + alignmentIdx++; + // Merge new and old (mis)alignment + alignment |= inst.getImmediateOperand(alignmentIdx); + // Pick the LSB + alignment = alignment & ~(alignment & (alignment-1)); + // update the Aligned operand + inst.setImmediateOperand(alignmentIdx, alignment); + } + break; + } + + default: + break; + } + + // Checks based on type + if (inst.getTypeId() != NoType) + postProcessType(inst, inst.getTypeId()); + for (int op = 0; op < inst.getNumOperands(); ++op) { + if (inst.isIdOperand(op)) { + // In blocks, these are always result ids, but we are relying on + // getTypeId() to return NoType for things like OpLabel. + if (getTypeId(inst.getIdOperand(op)) != NoType) + postProcessType(inst, getTypeId(inst.getIdOperand(op))); + } + } +} +#endif + +// comment in header +void Builder::postProcessCFG() +{ + // reachableBlocks is the set of blockss reached via control flow, or which are + // unreachable continue targert or unreachable merge. + std::unordered_set reachableBlocks; + std::unordered_map headerForUnreachableContinue; + std::unordered_set unreachableMerges; + std::unordered_set unreachableDefinitions; + // Collect IDs defined in unreachable blocks. For each function, label the + // reachable blocks first. Then for each unreachable block, collect the + // result IDs of the instructions in it. + for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + Block* entry = f->getEntryBlock(); + inReadableOrder(entry, + [&reachableBlocks, &unreachableMerges, &headerForUnreachableContinue] + (Block* b, ReachReason why, Block* header) { + reachableBlocks.insert(b); + if (why == ReachDeadContinue) headerForUnreachableContinue[b] = header; + if (why == ReachDeadMerge) unreachableMerges.insert(b); + }); + for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + if (unreachableMerges.count(b) != 0 || headerForUnreachableContinue.count(b) != 0) { + auto ii = b->getInstructions().cbegin(); + ++ii; // Keep potential decorations on the label. + for (; ii != b->getInstructions().cend(); ++ii) + unreachableDefinitions.insert(ii->get()->getResultId()); + } else if (reachableBlocks.count(b) == 0) { + // The normal case for unreachable code. All definitions are considered dead. + for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ++ii) + unreachableDefinitions.insert(ii->get()->getResultId()); + } + } + } + + // Modify unreachable merge blocks and unreachable continue targets. + // Delete their contents. + for (auto mergeIter = unreachableMerges.begin(); mergeIter != unreachableMerges.end(); ++mergeIter) { + (*mergeIter)->rewriteAsCanonicalUnreachableMerge(); + } + for (auto continueIter = headerForUnreachableContinue.begin(); + continueIter != headerForUnreachableContinue.end(); + ++continueIter) { + Block* continue_target = continueIter->first; + Block* header = continueIter->second; + continue_target->rewriteAsCanonicalUnreachableContinue(header); + } + + // Remove unneeded decorations, for unreachable instructions + decorations.erase(std::remove_if(decorations.begin(), decorations.end(), + [&unreachableDefinitions](std::unique_ptr& I) -> bool { + Id decoration_id = I.get()->getIdOperand(0); + return unreachableDefinitions.count(decoration_id) != 0; + }), + decorations.end()); +} + +#ifndef GLSLANG_WEB +// comment in header +void Builder::postProcessFeatures() { + // Add per-instruction capabilities, extensions, etc., + + // Look for any 8/16 bit type in physical storage buffer class, and set the + // appropriate capability. This happens in createSpvVariable for other storage + // classes, but there isn't always a variable for physical storage buffer. + for (int t = 0; t < (int)groupedTypes[OpTypePointer].size(); ++t) { + Instruction* type = groupedTypes[OpTypePointer][t]; + if (type->getImmediateOperand(0) == (unsigned)StorageClassPhysicalStorageBufferEXT) { + if (containsType(type->getIdOperand(1), OpTypeInt, 8)) { + addIncorporatedExtension(spv::E_SPV_KHR_8bit_storage, spv::Spv_1_5); + addCapability(spv::CapabilityStorageBuffer8BitAccess); + } + if (containsType(type->getIdOperand(1), OpTypeInt, 16) || + containsType(type->getIdOperand(1), OpTypeFloat, 16)) { + addIncorporatedExtension(spv::E_SPV_KHR_16bit_storage, spv::Spv_1_3); + addCapability(spv::CapabilityStorageBuffer16BitAccess); + } + } + } + + // process all block-contained instructions + for (auto fi = module.getFunctions().cbegin(); fi != module.getFunctions().cend(); fi++) { + Function* f = *fi; + for (auto bi = f->getBlocks().cbegin(); bi != f->getBlocks().cend(); bi++) { + Block* b = *bi; + for (auto ii = b->getInstructions().cbegin(); ii != b->getInstructions().cend(); ii++) + postProcess(*ii->get()); + + // For all local variables that contain pointers to PhysicalStorageBufferEXT, check whether + // there is an existing restrict/aliased decoration. If we don't find one, add Aliased as the + // default. + for (auto vi = b->getLocalVariables().cbegin(); vi != b->getLocalVariables().cend(); vi++) { + const Instruction& inst = *vi->get(); + Id resultId = inst.getResultId(); + if (containsPhysicalStorageBufferOrArray(getDerefTypeId(resultId))) { + bool foundDecoration = false; + const auto function = [&](const std::unique_ptr& decoration) { + if (decoration.get()->getIdOperand(0) == resultId && + decoration.get()->getOpCode() == OpDecorate && + (decoration.get()->getImmediateOperand(1) == spv::DecorationAliasedPointerEXT || + decoration.get()->getImmediateOperand(1) == spv::DecorationRestrictPointerEXT)) { + foundDecoration = true; + } + }; + std::for_each(decorations.begin(), decorations.end(), function); + if (!foundDecoration) { + addDecoration(resultId, spv::DecorationAliasedPointerEXT); + } + } + } + } + } +} +#endif + +// comment in header +void Builder::postProcess() { + postProcessCFG(); +#ifndef GLSLANG_WEB + postProcessFeatures(); +#endif +} + +}; // end spv namespace diff --git a/third_party/glslang/SPIRV/SpvTools.cpp b/third_party/glslang/SPIRV/SpvTools.cpp new file mode 100644 index 0000000..16d051a --- /dev/null +++ b/third_party/glslang/SPIRV/SpvTools.cpp @@ -0,0 +1,241 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2018-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Call into SPIRV-Tools to disassemble, validate, and optimize. +// + +#if ENABLE_OPT + +#include +#include + +#include "SpvTools.h" +#include "spirv-tools/optimizer.hpp" + +namespace glslang { + +// Translate glslang's view of target versioning to what SPIRV-Tools uses. +spv_target_env MapToSpirvToolsEnv(const SpvVersion& spvVersion, spv::SpvBuildLogger* logger) +{ + switch (spvVersion.vulkan) { + case glslang::EShTargetVulkan_1_0: + return spv_target_env::SPV_ENV_VULKAN_1_0; + case glslang::EShTargetVulkan_1_1: + switch (spvVersion.spv) { + case EShTargetSpv_1_0: + case EShTargetSpv_1_1: + case EShTargetSpv_1_2: + case EShTargetSpv_1_3: + return spv_target_env::SPV_ENV_VULKAN_1_1; + case EShTargetSpv_1_4: + return spv_target_env::SPV_ENV_VULKAN_1_1_SPIRV_1_4; + default: + logger->missingFunctionality("Target version for SPIRV-Tools validator"); + return spv_target_env::SPV_ENV_VULKAN_1_1; + } + case glslang::EShTargetVulkan_1_2: + return spv_target_env::SPV_ENV_VULKAN_1_2; + default: + break; + } + + if (spvVersion.openGl > 0) + return spv_target_env::SPV_ENV_OPENGL_4_5; + + logger->missingFunctionality("Target version for SPIRV-Tools validator"); + return spv_target_env::SPV_ENV_UNIVERSAL_1_0; +} + +// Callback passed to spvtools::Optimizer::SetMessageConsumer +void OptimizerMesssageConsumer(spv_message_level_t level, const char *source, + const spv_position_t &position, const char *message) +{ + auto &out = std::cerr; + switch (level) + { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + out << "error: "; + break; + case SPV_MSG_WARNING: + out << "warning: "; + break; + case SPV_MSG_INFO: + case SPV_MSG_DEBUG: + out << "info: "; + break; + default: + break; + } + if (source) + { + out << source << ":"; + } + out << position.line << ":" << position.column << ":" << position.index << ":"; + if (message) + { + out << " " << message; + } + out << std::endl; +} + +// Use the SPIRV-Tools disassembler to print SPIR-V using a SPV_ENV_UNIVERSAL_1_3 environment. +void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv) +{ + SpirvToolsDisassemble(out, spirv, spv_target_env::SPV_ENV_UNIVERSAL_1_3); +} + +// Use the SPIRV-Tools disassembler to print SPIR-V with a provided SPIR-V environment. +void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv, + spv_target_env requested_context) +{ + // disassemble + spv_context context = spvContextCreate(requested_context); + spv_text text; + spv_diagnostic diagnostic = nullptr; + spvBinaryToText(context, spirv.data(), spirv.size(), + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES | SPV_BINARY_TO_TEXT_OPTION_INDENT, + &text, &diagnostic); + + // dump + if (diagnostic == nullptr) + out << text->str; + else + spvDiagnosticPrint(diagnostic); + + // teardown + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); +} + +// Apply the SPIRV-Tools validator to generated SPIR-V. +void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, bool prelegalization) +{ + // validate + spv_context context = spvContextCreate(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); + spv_const_binary_t binary = { spirv.data(), spirv.size() }; + spv_diagnostic diagnostic = nullptr; + spv_validator_options options = spvValidatorOptionsCreate(); + spvValidatorOptionsSetRelaxBlockLayout(options, intermediate.usingHlslOffsets()); + spvValidatorOptionsSetBeforeHlslLegalization(options, prelegalization); + spvValidateWithOptions(context, options, &binary, &diagnostic); + + // report + if (diagnostic != nullptr) { + logger->error("SPIRV-Tools Validation Errors"); + logger->error(diagnostic->error); + } + + // tear down + spvValidatorOptionsDestroy(options); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); +} + +// Apply the SPIRV-Tools optimizer to generated SPIR-V. HLSL SPIR-V is legalized in the process. +void SpirvToolsTransform(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger* logger, const SpvOptions* options) +{ + spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger); + + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer(OptimizerMesssageConsumer); + + // If debug (specifically source line info) is being generated, propagate + // line information into all SPIR-V instructions. This avoids loss of + // information when instructions are deleted or moved. Later, remove + // redundant information to minimize final SPRIR-V size. + if (options->stripDebugInfo) { + optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass()); + } + optimizer.RegisterPass(spvtools::CreateWrapOpKillPass()); + optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); + optimizer.RegisterPass(spvtools::CreateMergeReturnPass()); + optimizer.RegisterPass(spvtools::CreateInlineExhaustivePass()); + optimizer.RegisterPass(spvtools::CreateEliminateDeadFunctionsPass()); + optimizer.RegisterPass(spvtools::CreateScalarReplacementPass()); + optimizer.RegisterPass(spvtools::CreateLocalAccessChainConvertPass()); + optimizer.RegisterPass(spvtools::CreateLocalSingleBlockLoadStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateSimplificationPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateVectorDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); + optimizer.RegisterPass(spvtools::CreateBlockMergePass()); + optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass()); + optimizer.RegisterPass(spvtools::CreateIfConversionPass()); + optimizer.RegisterPass(spvtools::CreateSimplificationPass()); + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateVectorDCEPass()); + optimizer.RegisterPass(spvtools::CreateDeadInsertElimPass()); + if (options->optimizeSize) { + optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass()); + } + optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); + optimizer.RegisterPass(spvtools::CreateCFGCleanupPass()); + + spvtools::OptimizerOptions spvOptOptions; + optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); + spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on + optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); +} + +// Apply the SPIRV-Tools optimizer to strip debug info from SPIR-V. This is implicitly done by +// SpirvToolsTransform if spvOptions->stripDebugInfo is set, but can be called separately if +// optimization is disabled. +void SpirvToolsStripDebugInfo(const glslang::TIntermediate& intermediate, + std::vector& spirv, spv::SpvBuildLogger* logger) +{ + spv_target_env target_env = MapToSpirvToolsEnv(intermediate.getSpv(), logger); + + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer(OptimizerMesssageConsumer); + + optimizer.RegisterPass(spvtools::CreateStripDebugInfoPass()); + + spvtools::OptimizerOptions spvOptOptions; + optimizer.SetTargetEnv(MapToSpirvToolsEnv(intermediate.getSpv(), logger)); + spvOptOptions.set_run_validator(false); // The validator may run as a separate step later on + optimizer.Run(spirv.data(), spirv.size(), &spirv, spvOptOptions); +} + +}; // end namespace glslang + +#endif diff --git a/third_party/glslang/SPIRV/SpvTools.h b/third_party/glslang/SPIRV/SpvTools.h new file mode 100644 index 0000000..3fb3cba --- /dev/null +++ b/third_party/glslang/SPIRV/SpvTools.h @@ -0,0 +1,93 @@ +// +// Copyright (C) 2014-2016 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Call into SPIRV-Tools to disassemble, validate, and optimize. +// + +#pragma once +#ifndef GLSLANG_SPV_TOOLS_H +#define GLSLANG_SPV_TOOLS_H + +#if ENABLE_OPT +#include +#include +#include "spirv-tools/libspirv.h" +#endif + +#include "glslang/MachineIndependent/localintermediate.h" +#include "Logger.h" + +namespace glslang { + +struct SpvOptions { + SpvOptions() : generateDebugInfo(false), stripDebugInfo(false), disableOptimizer(true), + optimizeSize(false), disassemble(false), validate(false) { } + bool generateDebugInfo; + bool stripDebugInfo; + bool disableOptimizer; + bool optimizeSize; + bool disassemble; + bool validate; +}; + +#if ENABLE_OPT + +// Use the SPIRV-Tools disassembler to print SPIR-V using a SPV_ENV_UNIVERSAL_1_3 environment. +void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv); + +// Use the SPIRV-Tools disassembler to print SPIR-V with a provided SPIR-V environment. +void SpirvToolsDisassemble(std::ostream& out, const std::vector& spirv, + spv_target_env requested_context); + +// Apply the SPIRV-Tools validator to generated SPIR-V. +void SpirvToolsValidate(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger*, bool prelegalization); + +// Apply the SPIRV-Tools optimizer to generated SPIR-V. HLSL SPIR-V is legalized in the process. +void SpirvToolsTransform(const glslang::TIntermediate& intermediate, std::vector& spirv, + spv::SpvBuildLogger*, const SpvOptions*); + +// Apply the SPIRV-Tools optimizer to strip debug info from SPIR-V. This is implicitly done by +// SpirvToolsTransform if spvOptions->stripDebugInfo is set, but can be called separately if +// optimization is disabled. +void SpirvToolsStripDebugInfo(const glslang::TIntermediate& intermediate, + std::vector& spirv, spv::SpvBuildLogger*); + +#endif + +} // end namespace glslang + +#endif // GLSLANG_SPV_TOOLS_H diff --git a/third_party/glslang/SPIRV/bitutils.h b/third_party/glslang/SPIRV/bitutils.h new file mode 100644 index 0000000..22e44ce --- /dev/null +++ b/third_party/glslang/SPIRV/bitutils.h @@ -0,0 +1,81 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_UTIL_BITUTILS_H_ +#define LIBSPIRV_UTIL_BITUTILS_H_ + +#include +#include + +namespace spvutils { + +// Performs a bitwise copy of source to the destination type Dest. +template +Dest BitwiseCast(Src source) { + Dest dest; + static_assert(sizeof(source) == sizeof(dest), + "BitwiseCast: Source and destination must have the same size"); + std::memcpy(static_cast(&dest), &source, sizeof(dest)); + return dest; +} + +// SetBits returns an integer of type with bits set +// for position through , counting from the least +// significant bit. In particular when Num == 0, no positions are set to 1. +// A static assert will be triggered if First + Num > sizeof(T) * 8, that is, +// a bit that will not fit in the underlying type is set. +template +struct SetBits { + static_assert(First < sizeof(T) * 8, + "Tried to set a bit that is shifted too far."); + const static T get = (T(1) << First) | SetBits::get; +}; + +template +struct SetBits { + const static T get = T(0); +}; + +// This is all compile-time so we can put our tests right here. +static_assert(SetBits::get == uint32_t(0x00000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000001), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x80000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000006), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xc0000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x7FFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFF0000), + "SetBits failed"); + +static_assert(SetBits::get == uint64_t(0x0000000000000001LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x8000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0xc000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x0000000080000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x00000000FFFF0000LL), + "SetBits failed"); + +} // namespace spvutils + +#endif // LIBSPIRV_UTIL_BITUTILS_H_ diff --git a/third_party/glslang/SPIRV/disassemble.cpp b/third_party/glslang/SPIRV/disassemble.cpp new file mode 100644 index 0000000..73c988c --- /dev/null +++ b/third_party/glslang/SPIRV/disassemble.cpp @@ -0,0 +1,746 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Disassembler for SPIR-V. +// + +#include +#include +#include +#include +#include +#include +#include + +#include "disassemble.h" +#include "doc.h" + +namespace spv { + extern "C" { + // Include C-based headers that don't have a namespace + #include "GLSL.std.450.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + } +} +const char* GlslStd450DebugNames[spv::GLSLstd450Count]; + +namespace spv { + +static const char* GLSLextAMDGetDebugNames(const char*, unsigned); +static const char* GLSLextNVGetDebugNames(const char*, unsigned); + +static void Kill(std::ostream& out, const char* message) +{ + out << std::endl << "Disassembly failed: " << message << std::endl; + exit(1); +} + +// used to identify the extended instruction library imported when printing +enum ExtInstSet { + GLSL450Inst, + GLSLextAMDInst, + GLSLextNVInst, + OpenCLExtInst, + NonSemanticDebugPrintfExtInst, +}; + +// Container class for a single instance of a SPIR-V stream, with methods for disassembly. +class SpirvStream { +public: + SpirvStream(std::ostream& out, const std::vector& stream) : out(out), stream(stream), word(0), nextNestedControl(0) { } + virtual ~SpirvStream() { } + + void validate(); + void processInstructions(); + +protected: + SpirvStream(const SpirvStream&); + SpirvStream& operator=(const SpirvStream&); + Op getOpCode(int id) const { return idInstruction[id] ? (Op)(stream[idInstruction[id]] & OpCodeMask) : OpNop; } + + // Output methods + void outputIndent(); + void formatId(Id id, std::stringstream&); + void outputResultId(Id id); + void outputTypeId(Id id); + void outputId(Id id); + void outputMask(OperandClass operandClass, unsigned mask); + void disassembleImmediates(int numOperands); + void disassembleIds(int numOperands); + int disassembleString(); + void disassembleInstruction(Id resultId, Id typeId, Op opCode, int numOperands); + + // Data + std::ostream& out; // where to write the disassembly + const std::vector& stream; // the actual word stream + int size; // the size of the word stream + int word; // the next word of the stream to read + + // map each to the instruction that created it + Id bound; + std::vector idInstruction; // the word offset into the stream where the instruction for result [id] starts; 0 if not yet seen (forward reference or function parameter) + + std::vector idDescriptor; // the best text string known for explaining the + + // schema + unsigned int schema; + + // stack of structured-merge points + std::stack nestedControl; + Id nextNestedControl; // need a slight delay for when we are nested +}; + +void SpirvStream::validate() +{ + size = (int)stream.size(); + if (size < 4) + Kill(out, "stream is too short"); + + // Magic number + if (stream[word++] != MagicNumber) { + out << "Bad magic number"; + return; + } + + // Version + out << "// Module Version " << std::hex << stream[word++] << std::endl; + + // Generator's magic number + out << "// Generated by (magic number): " << std::hex << stream[word++] << std::dec << std::endl; + + // Result bound + bound = stream[word++]; + idInstruction.resize(bound); + idDescriptor.resize(bound); + out << "// Id's are bound by " << bound << std::endl; + out << std::endl; + + // Reserved schema, must be 0 for now + schema = stream[word++]; + if (schema != 0) + Kill(out, "bad schema, must be 0"); +} + +// Loop over all the instructions, in order, processing each. +// Boiler plate for each is handled here directly, the rest is dispatched. +void SpirvStream::processInstructions() +{ + // Instructions + while (word < size) { + int instructionStart = word; + + // Instruction wordCount and opcode + unsigned int firstWord = stream[word]; + unsigned wordCount = firstWord >> WordCountShift; + Op opCode = (Op)(firstWord & OpCodeMask); + int nextInst = word + wordCount; + ++word; + + // Presence of full instruction + if (nextInst > size) + Kill(out, "stream instruction terminated too early"); + + // Base for computing number of operands; will be updated as more is learned + unsigned numOperands = wordCount - 1; + + // Type + Id typeId = 0; + if (InstructionDesc[opCode].hasType()) { + typeId = stream[word++]; + --numOperands; + } + + // Result + Id resultId = 0; + if (InstructionDesc[opCode].hasResult()) { + resultId = stream[word++]; + --numOperands; + + // save instruction for future reference + idInstruction[resultId] = instructionStart; + } + + outputResultId(resultId); + outputTypeId(typeId); + outputIndent(); + + // Hand off the Op and all its operands + disassembleInstruction(resultId, typeId, opCode, numOperands); + if (word != nextInst) { + out << " ERROR, incorrect number of operands consumed. At " << word << " instead of " << nextInst << " instruction start was " << instructionStart; + word = nextInst; + } + out << std::endl; + } +} + +void SpirvStream::outputIndent() +{ + for (int i = 0; i < (int)nestedControl.size(); ++i) + out << " "; +} + +void SpirvStream::formatId(Id id, std::stringstream& idStream) +{ + if (id != 0) { + // On instructions with no IDs, this is called with "0", which does not + // have to be within ID bounds on null shaders. + if (id >= bound) + Kill(out, "Bad "); + + idStream << id; + if (idDescriptor[id].size() > 0) + idStream << "(" << idDescriptor[id] << ")"; + } +} + +void SpirvStream::outputResultId(Id id) +{ + const int width = 16; + std::stringstream idStream; + formatId(id, idStream); + out << std::setw(width) << std::right << idStream.str(); + if (id != 0) + out << ":"; + else + out << " "; + + if (nestedControl.size() && id == nestedControl.top()) + nestedControl.pop(); +} + +void SpirvStream::outputTypeId(Id id) +{ + const int width = 12; + std::stringstream idStream; + formatId(id, idStream); + out << std::setw(width) << std::right << idStream.str() << " "; +} + +void SpirvStream::outputId(Id id) +{ + if (id >= bound) + Kill(out, "Bad "); + + out << id; + if (idDescriptor[id].size() > 0) + out << "(" << idDescriptor[id] << ")"; +} + +void SpirvStream::outputMask(OperandClass operandClass, unsigned mask) +{ + if (mask == 0) + out << "None"; + else { + for (int m = 0; m < OperandClassParams[operandClass].ceiling; ++m) { + if (mask & (1 << m)) + out << OperandClassParams[operandClass].getName(m) << " "; + } + } +} + +void SpirvStream::disassembleImmediates(int numOperands) +{ + for (int i = 0; i < numOperands; ++i) { + out << stream[word++]; + if (i < numOperands - 1) + out << " "; + } +} + +void SpirvStream::disassembleIds(int numOperands) +{ + for (int i = 0; i < numOperands; ++i) { + outputId(stream[word++]); + if (i < numOperands - 1) + out << " "; + } +} + +// return the number of operands consumed by the string +int SpirvStream::disassembleString() +{ + int startWord = word; + + out << " \""; + + const char* wordString; + bool done = false; + do { + unsigned int content = stream[word]; + wordString = (const char*)&content; + for (int charCount = 0; charCount < 4; ++charCount) { + if (*wordString == 0) { + done = true; + break; + } + out << *(wordString++); + } + ++word; + } while (! done); + + out << "\""; + + return word - startWord; +} + +void SpirvStream::disassembleInstruction(Id resultId, Id /*typeId*/, Op opCode, int numOperands) +{ + // Process the opcode + + out << (OpcodeString(opCode) + 2); // leave out the "Op" + + if (opCode == OpLoopMerge || opCode == OpSelectionMerge) + nextNestedControl = stream[word]; + else if (opCode == OpBranchConditional || opCode == OpSwitch) { + if (nextNestedControl) { + nestedControl.push(nextNestedControl); + nextNestedControl = 0; + } + } else if (opCode == OpExtInstImport) { + idDescriptor[resultId] = (const char*)(&stream[word]); + } + else { + if (resultId != 0 && idDescriptor[resultId].size() == 0) { + switch (opCode) { + case OpTypeInt: + switch (stream[word]) { + case 8: idDescriptor[resultId] = "int8_t"; break; + case 16: idDescriptor[resultId] = "int16_t"; break; + default: assert(0); // fallthrough + case 32: idDescriptor[resultId] = "int"; break; + case 64: idDescriptor[resultId] = "int64_t"; break; + } + break; + case OpTypeFloat: + switch (stream[word]) { + case 16: idDescriptor[resultId] = "float16_t"; break; + default: assert(0); // fallthrough + case 32: idDescriptor[resultId] = "float"; break; + case 64: idDescriptor[resultId] = "float64_t"; break; + } + break; + case OpTypeBool: + idDescriptor[resultId] = "bool"; + break; + case OpTypeStruct: + idDescriptor[resultId] = "struct"; + break; + case OpTypePointer: + idDescriptor[resultId] = "ptr"; + break; + case OpTypeVector: + if (idDescriptor[stream[word]].size() > 0) { + idDescriptor[resultId].append(idDescriptor[stream[word]].begin(), idDescriptor[stream[word]].begin() + 1); + if (strstr(idDescriptor[stream[word]].c_str(), "8")) { + idDescriptor[resultId].append("8"); + } + if (strstr(idDescriptor[stream[word]].c_str(), "16")) { + idDescriptor[resultId].append("16"); + } + if (strstr(idDescriptor[stream[word]].c_str(), "64")) { + idDescriptor[resultId].append("64"); + } + } + idDescriptor[resultId].append("vec"); + switch (stream[word + 1]) { + case 2: idDescriptor[resultId].append("2"); break; + case 3: idDescriptor[resultId].append("3"); break; + case 4: idDescriptor[resultId].append("4"); break; + case 8: idDescriptor[resultId].append("8"); break; + case 16: idDescriptor[resultId].append("16"); break; + case 32: idDescriptor[resultId].append("32"); break; + default: break; + } + break; + default: + break; + } + } + } + + // Process the operands. Note, a new context-dependent set could be + // swapped in mid-traversal. + + // Handle images specially, so can put out helpful strings. + if (opCode == OpTypeImage) { + out << " "; + disassembleIds(1); + out << " " << DimensionString((Dim)stream[word++]); + out << (stream[word++] != 0 ? " depth" : ""); + out << (stream[word++] != 0 ? " array" : ""); + out << (stream[word++] != 0 ? " multi-sampled" : ""); + switch (stream[word++]) { + case 0: out << " runtime"; break; + case 1: out << " sampled"; break; + case 2: out << " nonsampled"; break; + } + out << " format:" << ImageFormatString((ImageFormat)stream[word++]); + + if (numOperands == 8) { + out << " " << AccessQualifierString(stream[word++]); + } + return; + } + + // Handle all the parameterized operands + for (int op = 0; op < InstructionDesc[opCode].operands.getNum() && numOperands > 0; ++op) { + out << " "; + OperandClass operandClass = InstructionDesc[opCode].operands.getClass(op); + switch (operandClass) { + case OperandId: + case OperandScope: + case OperandMemorySemantics: + disassembleIds(1); + --numOperands; + // Get names for printing "(XXX)" for readability, *after* this id + if (opCode == OpName) + idDescriptor[stream[word - 1]] = (const char*)(&stream[word]); + break; + case OperandVariableIds: + disassembleIds(numOperands); + return; + case OperandImageOperands: + outputMask(OperandImageOperands, stream[word++]); + --numOperands; + disassembleIds(numOperands); + return; + case OperandOptionalLiteral: + case OperandVariableLiterals: + if ((opCode == OpDecorate && stream[word - 1] == DecorationBuiltIn) || + (opCode == OpMemberDecorate && stream[word - 1] == DecorationBuiltIn)) { + out << BuiltInString(stream[word++]); + --numOperands; + ++op; + } + disassembleImmediates(numOperands); + return; + case OperandVariableIdLiteral: + while (numOperands > 0) { + out << std::endl; + outputResultId(0); + outputTypeId(0); + outputIndent(); + out << " Type "; + disassembleIds(1); + out << ", member "; + disassembleImmediates(1); + numOperands -= 2; + } + return; + case OperandVariableLiteralId: + while (numOperands > 0) { + out << std::endl; + outputResultId(0); + outputTypeId(0); + outputIndent(); + out << " case "; + disassembleImmediates(1); + out << ": "; + disassembleIds(1); + numOperands -= 2; + } + return; + case OperandLiteralNumber: + disassembleImmediates(1); + --numOperands; + if (opCode == OpExtInst) { + ExtInstSet extInstSet = GLSL450Inst; + const char* name = idDescriptor[stream[word - 2]].c_str(); + if (strcmp("OpenCL.std", name) == 0) { + extInstSet = OpenCLExtInst; + } else if (strcmp("OpenCL.DebugInfo.100", name) == 0) { + extInstSet = OpenCLExtInst; + } else if (strcmp("NonSemantic.DebugPrintf", name) == 0) { + extInstSet = NonSemanticDebugPrintfExtInst; + } else if (strcmp(spv::E_SPV_AMD_shader_ballot, name) == 0 || + strcmp(spv::E_SPV_AMD_shader_trinary_minmax, name) == 0 || + strcmp(spv::E_SPV_AMD_shader_explicit_vertex_parameter, name) == 0 || + strcmp(spv::E_SPV_AMD_gcn_shader, name) == 0) { + extInstSet = GLSLextAMDInst; + } else if (strcmp(spv::E_SPV_NV_sample_mask_override_coverage, name) == 0 || + strcmp(spv::E_SPV_NV_geometry_shader_passthrough, name) == 0 || + strcmp(spv::E_SPV_NV_viewport_array2, name) == 0 || + strcmp(spv::E_SPV_NVX_multiview_per_view_attributes, name) == 0 || + strcmp(spv::E_SPV_NV_fragment_shader_barycentric, name) == 0 || + strcmp(spv::E_SPV_NV_mesh_shader, name) == 0) { + extInstSet = GLSLextNVInst; + } + unsigned entrypoint = stream[word - 1]; + if (extInstSet == GLSL450Inst) { + if (entrypoint < GLSLstd450Count) { + out << "(" << GlslStd450DebugNames[entrypoint] << ")"; + } + } else if (extInstSet == GLSLextAMDInst) { + out << "(" << GLSLextAMDGetDebugNames(name, entrypoint) << ")"; + } + else if (extInstSet == GLSLextNVInst) { + out << "(" << GLSLextNVGetDebugNames(name, entrypoint) << ")"; + } else if (extInstSet == NonSemanticDebugPrintfExtInst) { + out << "(DebugPrintf)"; + } + } + break; + case OperandOptionalLiteralString: + case OperandLiteralString: + numOperands -= disassembleString(); + break; + case OperandVariableLiteralStrings: + while (numOperands > 0) + numOperands -= disassembleString(); + return; + case OperandMemoryAccess: + outputMask(OperandMemoryAccess, stream[word++]); + --numOperands; + // Aligned is the only memory access operand that uses an immediate + // value, and it is also the first operand that uses a value at all. + if (stream[word-1] & MemoryAccessAlignedMask) { + disassembleImmediates(1); + numOperands--; + if (numOperands) + out << " "; + } + disassembleIds(numOperands); + return; + default: + assert(operandClass >= OperandSource && operandClass < OperandOpcode); + + if (OperandClassParams[operandClass].bitmask) + outputMask(operandClass, stream[word++]); + else + out << OperandClassParams[operandClass].getName(stream[word++]); + --numOperands; + + break; + } + } + + return; +} + +static void GLSLstd450GetDebugNames(const char** names) +{ + for (int i = 0; i < GLSLstd450Count; ++i) + names[i] = "Unknown"; + + names[GLSLstd450Round] = "Round"; + names[GLSLstd450RoundEven] = "RoundEven"; + names[GLSLstd450Trunc] = "Trunc"; + names[GLSLstd450FAbs] = "FAbs"; + names[GLSLstd450SAbs] = "SAbs"; + names[GLSLstd450FSign] = "FSign"; + names[GLSLstd450SSign] = "SSign"; + names[GLSLstd450Floor] = "Floor"; + names[GLSLstd450Ceil] = "Ceil"; + names[GLSLstd450Fract] = "Fract"; + names[GLSLstd450Radians] = "Radians"; + names[GLSLstd450Degrees] = "Degrees"; + names[GLSLstd450Sin] = "Sin"; + names[GLSLstd450Cos] = "Cos"; + names[GLSLstd450Tan] = "Tan"; + names[GLSLstd450Asin] = "Asin"; + names[GLSLstd450Acos] = "Acos"; + names[GLSLstd450Atan] = "Atan"; + names[GLSLstd450Sinh] = "Sinh"; + names[GLSLstd450Cosh] = "Cosh"; + names[GLSLstd450Tanh] = "Tanh"; + names[GLSLstd450Asinh] = "Asinh"; + names[GLSLstd450Acosh] = "Acosh"; + names[GLSLstd450Atanh] = "Atanh"; + names[GLSLstd450Atan2] = "Atan2"; + names[GLSLstd450Pow] = "Pow"; + names[GLSLstd450Exp] = "Exp"; + names[GLSLstd450Log] = "Log"; + names[GLSLstd450Exp2] = "Exp2"; + names[GLSLstd450Log2] = "Log2"; + names[GLSLstd450Sqrt] = "Sqrt"; + names[GLSLstd450InverseSqrt] = "InverseSqrt"; + names[GLSLstd450Determinant] = "Determinant"; + names[GLSLstd450MatrixInverse] = "MatrixInverse"; + names[GLSLstd450Modf] = "Modf"; + names[GLSLstd450ModfStruct] = "ModfStruct"; + names[GLSLstd450FMin] = "FMin"; + names[GLSLstd450SMin] = "SMin"; + names[GLSLstd450UMin] = "UMin"; + names[GLSLstd450FMax] = "FMax"; + names[GLSLstd450SMax] = "SMax"; + names[GLSLstd450UMax] = "UMax"; + names[GLSLstd450FClamp] = "FClamp"; + names[GLSLstd450SClamp] = "SClamp"; + names[GLSLstd450UClamp] = "UClamp"; + names[GLSLstd450FMix] = "FMix"; + names[GLSLstd450Step] = "Step"; + names[GLSLstd450SmoothStep] = "SmoothStep"; + names[GLSLstd450Fma] = "Fma"; + names[GLSLstd450Frexp] = "Frexp"; + names[GLSLstd450FrexpStruct] = "FrexpStruct"; + names[GLSLstd450Ldexp] = "Ldexp"; + names[GLSLstd450PackSnorm4x8] = "PackSnorm4x8"; + names[GLSLstd450PackUnorm4x8] = "PackUnorm4x8"; + names[GLSLstd450PackSnorm2x16] = "PackSnorm2x16"; + names[GLSLstd450PackUnorm2x16] = "PackUnorm2x16"; + names[GLSLstd450PackHalf2x16] = "PackHalf2x16"; + names[GLSLstd450PackDouble2x32] = "PackDouble2x32"; + names[GLSLstd450UnpackSnorm2x16] = "UnpackSnorm2x16"; + names[GLSLstd450UnpackUnorm2x16] = "UnpackUnorm2x16"; + names[GLSLstd450UnpackHalf2x16] = "UnpackHalf2x16"; + names[GLSLstd450UnpackSnorm4x8] = "UnpackSnorm4x8"; + names[GLSLstd450UnpackUnorm4x8] = "UnpackUnorm4x8"; + names[GLSLstd450UnpackDouble2x32] = "UnpackDouble2x32"; + names[GLSLstd450Length] = "Length"; + names[GLSLstd450Distance] = "Distance"; + names[GLSLstd450Cross] = "Cross"; + names[GLSLstd450Normalize] = "Normalize"; + names[GLSLstd450FaceForward] = "FaceForward"; + names[GLSLstd450Reflect] = "Reflect"; + names[GLSLstd450Refract] = "Refract"; + names[GLSLstd450FindILsb] = "FindILsb"; + names[GLSLstd450FindSMsb] = "FindSMsb"; + names[GLSLstd450FindUMsb] = "FindUMsb"; + names[GLSLstd450InterpolateAtCentroid] = "InterpolateAtCentroid"; + names[GLSLstd450InterpolateAtSample] = "InterpolateAtSample"; + names[GLSLstd450InterpolateAtOffset] = "InterpolateAtOffset"; + names[GLSLstd450NMin] = "NMin"; + names[GLSLstd450NMax] = "NMax"; + names[GLSLstd450NClamp] = "NClamp"; +} + +static const char* GLSLextAMDGetDebugNames(const char* name, unsigned entrypoint) +{ + if (strcmp(name, spv::E_SPV_AMD_shader_ballot) == 0) { + switch (entrypoint) { + case SwizzleInvocationsAMD: return "SwizzleInvocationsAMD"; + case SwizzleInvocationsMaskedAMD: return "SwizzleInvocationsMaskedAMD"; + case WriteInvocationAMD: return "WriteInvocationAMD"; + case MbcntAMD: return "MbcntAMD"; + default: return "Bad"; + } + } else if (strcmp(name, spv::E_SPV_AMD_shader_trinary_minmax) == 0) { + switch (entrypoint) { + case FMin3AMD: return "FMin3AMD"; + case UMin3AMD: return "UMin3AMD"; + case SMin3AMD: return "SMin3AMD"; + case FMax3AMD: return "FMax3AMD"; + case UMax3AMD: return "UMax3AMD"; + case SMax3AMD: return "SMax3AMD"; + case FMid3AMD: return "FMid3AMD"; + case UMid3AMD: return "UMid3AMD"; + case SMid3AMD: return "SMid3AMD"; + default: return "Bad"; + } + } else if (strcmp(name, spv::E_SPV_AMD_shader_explicit_vertex_parameter) == 0) { + switch (entrypoint) { + case InterpolateAtVertexAMD: return "InterpolateAtVertexAMD"; + default: return "Bad"; + } + } + else if (strcmp(name, spv::E_SPV_AMD_gcn_shader) == 0) { + switch (entrypoint) { + case CubeFaceIndexAMD: return "CubeFaceIndexAMD"; + case CubeFaceCoordAMD: return "CubeFaceCoordAMD"; + case TimeAMD: return "TimeAMD"; + default: + break; + } + } + + return "Bad"; +} + +static const char* GLSLextNVGetDebugNames(const char* name, unsigned entrypoint) +{ + if (strcmp(name, spv::E_SPV_NV_sample_mask_override_coverage) == 0 || + strcmp(name, spv::E_SPV_NV_geometry_shader_passthrough) == 0 || + strcmp(name, spv::E_ARB_shader_viewport_layer_array) == 0 || + strcmp(name, spv::E_SPV_NV_viewport_array2) == 0 || + strcmp(name, spv::E_SPV_NVX_multiview_per_view_attributes) == 0 || + strcmp(name, spv::E_SPV_NV_fragment_shader_barycentric) == 0 || + strcmp(name, spv::E_SPV_NV_mesh_shader) == 0 || + strcmp(name, spv::E_SPV_NV_shader_image_footprint) == 0) { + switch (entrypoint) { + // NV builtins + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case BuiltInBaryCoordNV: return "BaryCoordNV"; + case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV"; + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + + // NV Capabilities + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; + case CapabilitySampleMaskOverrideCoverageNV:return "SampleMaskOverrideCoverageNV"; + + // NV Decorations + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerVertexNV: return "PerVertexNV"; + case DecorationPerPrimitiveNV: return "PerPrimitiveNV"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + + default: return "Bad"; + } + } + return "Bad"; +} + +void Disassemble(std::ostream& out, const std::vector& stream) +{ + SpirvStream SpirvStream(out, stream); + spv::Parameterize(); + GLSLstd450GetDebugNames(GlslStd450DebugNames); + SpirvStream.validate(); + SpirvStream.processInstructions(); +} + +}; // end namespace spv diff --git a/third_party/glslang/SPIRV/disassemble.h b/third_party/glslang/SPIRV/disassemble.h new file mode 100644 index 0000000..b6a4635 --- /dev/null +++ b/third_party/glslang/SPIRV/disassemble.h @@ -0,0 +1,53 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Disassembler for SPIR-V. +// + +#pragma once +#ifndef disassembler_H +#define disassembler_H + +#include +#include + +namespace spv { + + // disassemble with glslang custom disassembler + void Disassemble(std::ostream& out, const std::vector&); + +} // end namespace spv + +#endif // disassembler_H diff --git a/third_party/glslang/SPIRV/doc.cpp b/third_party/glslang/SPIRV/doc.cpp new file mode 100644 index 0000000..3d08204 --- /dev/null +++ b/third_party/glslang/SPIRV/doc.cpp @@ -0,0 +1,2933 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// 1) Programmatically fill in instruction/operand information. +// This can be used for disassembly, printing documentation, etc. +// +// 2) Print documentation from this parameterization. +// + +#include "doc.h" + +#include +#include +#include + +namespace spv { + extern "C" { + // Include C-based headers that don't have a namespace + #include "GLSL.ext.KHR.h" + #include "GLSL.ext.EXT.h" + #include "GLSL.ext.AMD.h" + #include "GLSL.ext.NV.h" + } +} + +namespace spv { + +// +// Whole set of functions that translate enumerants to their text strings for +// the specification (or their sanitized versions for auto-generating the +// spirv headers. +// +// Also, for masks the ceilings are declared next to these, to help keep them in sync. +// Ceilings should be +// - one more than the maximum value an enumerant takes on, for non-mask enumerants +// (for non-sparse enums, this is the number of enumerants) +// - the number of bits consumed by the set of masks +// (for non-sparse mask enums, this is the number of enumerants) +// + +const char* SourceString(int source) +{ + switch (source) { + case 0: return "Unknown"; + case 1: return "ESSL"; + case 2: return "GLSL"; + case 3: return "OpenCL_C"; + case 4: return "OpenCL_CPP"; + case 5: return "HLSL"; + + default: return "Bad"; + } +} + +const char* ExecutionModelString(int model) +{ + switch (model) { + case 0: return "Vertex"; + case 1: return "TessellationControl"; + case 2: return "TessellationEvaluation"; + case 3: return "Geometry"; + case 4: return "Fragment"; + case 5: return "GLCompute"; + case 6: return "Kernel"; + case ExecutionModelTaskNV: return "TaskNV"; + case ExecutionModelMeshNV: return "MeshNV"; + + default: return "Bad"; + + case ExecutionModelRayGenerationKHR: return "RayGenerationKHR"; + case ExecutionModelIntersectionKHR: return "IntersectionKHR"; + case ExecutionModelAnyHitKHR: return "AnyHitKHR"; + case ExecutionModelClosestHitKHR: return "ClosestHitKHR"; + case ExecutionModelMissKHR: return "MissKHR"; + case ExecutionModelCallableKHR: return "CallableKHR"; + } +} + +const char* AddressingString(int addr) +{ + switch (addr) { + case 0: return "Logical"; + case 1: return "Physical32"; + case 2: return "Physical64"; + + case AddressingModelPhysicalStorageBuffer64EXT: return "PhysicalStorageBuffer64EXT"; + + default: return "Bad"; + } +} + +const char* MemoryString(int mem) +{ + switch (mem) { + case MemoryModelSimple: return "Simple"; + case MemoryModelGLSL450: return "GLSL450"; + case MemoryModelOpenCL: return "OpenCL"; + case MemoryModelVulkanKHR: return "VulkanKHR"; + + default: return "Bad"; + } +} + +const int ExecutionModeCeiling = 40; + +const char* ExecutionModeString(int mode) +{ + switch (mode) { + case 0: return "Invocations"; + case 1: return "SpacingEqual"; + case 2: return "SpacingFractionalEven"; + case 3: return "SpacingFractionalOdd"; + case 4: return "VertexOrderCw"; + case 5: return "VertexOrderCcw"; + case 6: return "PixelCenterInteger"; + case 7: return "OriginUpperLeft"; + case 8: return "OriginLowerLeft"; + case 9: return "EarlyFragmentTests"; + case 10: return "PointMode"; + case 11: return "Xfb"; + case 12: return "DepthReplacing"; + case 13: return "Bad"; + case 14: return "DepthGreater"; + case 15: return "DepthLess"; + case 16: return "DepthUnchanged"; + case 17: return "LocalSize"; + case 18: return "LocalSizeHint"; + case 19: return "InputPoints"; + case 20: return "InputLines"; + case 21: return "InputLinesAdjacency"; + case 22: return "Triangles"; + case 23: return "InputTrianglesAdjacency"; + case 24: return "Quads"; + case 25: return "Isolines"; + case 26: return "OutputVertices"; + case 27: return "OutputPoints"; + case 28: return "OutputLineStrip"; + case 29: return "OutputTriangleStrip"; + case 30: return "VecTypeHint"; + case 31: return "ContractionOff"; + case 32: return "Bad"; + + case ExecutionModeInitializer: return "Initializer"; + case ExecutionModeFinalizer: return "Finalizer"; + case ExecutionModeSubgroupSize: return "SubgroupSize"; + case ExecutionModeSubgroupsPerWorkgroup: return "SubgroupsPerWorkgroup"; + case ExecutionModeSubgroupsPerWorkgroupId: return "SubgroupsPerWorkgroupId"; + case ExecutionModeLocalSizeId: return "LocalSizeId"; + case ExecutionModeLocalSizeHintId: return "LocalSizeHintId"; + + case ExecutionModePostDepthCoverage: return "PostDepthCoverage"; + case ExecutionModeDenormPreserve: return "DenormPreserve"; + case ExecutionModeDenormFlushToZero: return "DenormFlushToZero"; + case ExecutionModeSignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case ExecutionModeRoundingModeRTE: return "RoundingModeRTE"; + case ExecutionModeRoundingModeRTZ: return "RoundingModeRTZ"; + case ExecutionModeStencilRefReplacingEXT: return "StencilRefReplacingEXT"; + + case ExecutionModeOutputLinesNV: return "OutputLinesNV"; + case ExecutionModeOutputPrimitivesNV: return "OutputPrimitivesNV"; + case ExecutionModeOutputTrianglesNV: return "OutputTrianglesNV"; + case ExecutionModeDerivativeGroupQuadsNV: return "DerivativeGroupQuadsNV"; + case ExecutionModeDerivativeGroupLinearNV: return "DerivativeGroupLinearNV"; + + case ExecutionModePixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case ExecutionModePixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case ExecutionModeSampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case ExecutionModeSampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case ExecutionModeShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case ExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + + case ExecutionModeMaxWorkgroupSizeINTEL: return "MaxWorkgroupSizeINTEL"; + case ExecutionModeMaxWorkDimINTEL: return "MaxWorkDimINTEL"; + case ExecutionModeNoGlobalOffsetINTEL: return "NoGlobalOffsetINTEL"; + case ExecutionModeNumSIMDWorkitemsINTEL: return "NumSIMDWorkitemsINTEL"; + + case ExecutionModeCeiling: + default: return "Bad"; + } +} + +const char* StorageClassString(int StorageClass) +{ + switch (StorageClass) { + case 0: return "UniformConstant"; + case 1: return "Input"; + case 2: return "Uniform"; + case 3: return "Output"; + case 4: return "Workgroup"; + case 5: return "CrossWorkgroup"; + case 6: return "Private"; + case 7: return "Function"; + case 8: return "Generic"; + case 9: return "PushConstant"; + case 10: return "AtomicCounter"; + case 11: return "Image"; + case 12: return "StorageBuffer"; + + case StorageClassRayPayloadKHR: return "RayPayloadKHR"; + case StorageClassHitAttributeKHR: return "HitAttributeKHR"; + case StorageClassIncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case StorageClassShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case StorageClassCallableDataKHR: return "CallableDataKHR"; + case StorageClassIncomingCallableDataKHR: return "IncomingCallableDataKHR"; + + case StorageClassPhysicalStorageBufferEXT: return "PhysicalStorageBufferEXT"; + + default: return "Bad"; + } +} + +const int DecorationCeiling = 45; + +const char* DecorationString(int decoration) +{ + switch (decoration) { + case 0: return "RelaxedPrecision"; + case 1: return "SpecId"; + case 2: return "Block"; + case 3: return "BufferBlock"; + case 4: return "RowMajor"; + case 5: return "ColMajor"; + case 6: return "ArrayStride"; + case 7: return "MatrixStride"; + case 8: return "GLSLShared"; + case 9: return "GLSLPacked"; + case 10: return "CPacked"; + case 11: return "BuiltIn"; + case 12: return "Bad"; + case 13: return "NoPerspective"; + case 14: return "Flat"; + case 15: return "Patch"; + case 16: return "Centroid"; + case 17: return "Sample"; + case 18: return "Invariant"; + case 19: return "Restrict"; + case 20: return "Aliased"; + case 21: return "Volatile"; + case 22: return "Constant"; + case 23: return "Coherent"; + case 24: return "NonWritable"; + case 25: return "NonReadable"; + case 26: return "Uniform"; + case 27: return "Bad"; + case 28: return "SaturatedConversion"; + case 29: return "Stream"; + case 30: return "Location"; + case 31: return "Component"; + case 32: return "Index"; + case 33: return "Binding"; + case 34: return "DescriptorSet"; + case 35: return "Offset"; + case 36: return "XfbBuffer"; + case 37: return "XfbStride"; + case 38: return "FuncParamAttr"; + case 39: return "FP Rounding Mode"; + case 40: return "FP Fast Math Mode"; + case 41: return "Linkage Attributes"; + case 42: return "NoContraction"; + case 43: return "InputAttachmentIndex"; + case 44: return "Alignment"; + + case DecorationCeiling: + default: return "Bad"; + + case DecorationExplicitInterpAMD: return "ExplicitInterpAMD"; + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerPrimitiveNV: return "PerPrimitiveNV"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + case DecorationPerVertexNV: return "PerVertexNV"; + + case DecorationNonUniformEXT: return "DecorationNonUniformEXT"; + case DecorationHlslCounterBufferGOOGLE: return "DecorationHlslCounterBufferGOOGLE"; + case DecorationHlslSemanticGOOGLE: return "DecorationHlslSemanticGOOGLE"; + case DecorationRestrictPointerEXT: return "DecorationRestrictPointerEXT"; + case DecorationAliasedPointerEXT: return "DecorationAliasedPointerEXT"; + } +} + +const char* BuiltInString(int builtIn) +{ + switch (builtIn) { + case 0: return "Position"; + case 1: return "PointSize"; + case 2: return "Bad"; + case 3: return "ClipDistance"; + case 4: return "CullDistance"; + case 5: return "VertexId"; + case 6: return "InstanceId"; + case 7: return "PrimitiveId"; + case 8: return "InvocationId"; + case 9: return "Layer"; + case 10: return "ViewportIndex"; + case 11: return "TessLevelOuter"; + case 12: return "TessLevelInner"; + case 13: return "TessCoord"; + case 14: return "PatchVertices"; + case 15: return "FragCoord"; + case 16: return "PointCoord"; + case 17: return "FrontFacing"; + case 18: return "SampleId"; + case 19: return "SamplePosition"; + case 20: return "SampleMask"; + case 21: return "Bad"; + case 22: return "FragDepth"; + case 23: return "HelperInvocation"; + case 24: return "NumWorkgroups"; + case 25: return "WorkgroupSize"; + case 26: return "WorkgroupId"; + case 27: return "LocalInvocationId"; + case 28: return "GlobalInvocationId"; + case 29: return "LocalInvocationIndex"; + case 30: return "WorkDim"; + case 31: return "GlobalSize"; + case 32: return "EnqueuedWorkgroupSize"; + case 33: return "GlobalOffset"; + case 34: return "GlobalLinearId"; + case 35: return "Bad"; + case 36: return "SubgroupSize"; + case 37: return "SubgroupMaxSize"; + case 38: return "NumSubgroups"; + case 39: return "NumEnqueuedSubgroups"; + case 40: return "SubgroupId"; + case 41: return "SubgroupLocalInvocationId"; + case 42: return "VertexIndex"; // TBD: put next to VertexId? + case 43: return "InstanceIndex"; // TBD: put next to InstanceId? + + case 4416: return "SubgroupEqMaskKHR"; + case 4417: return "SubgroupGeMaskKHR"; + case 4418: return "SubgroupGtMaskKHR"; + case 4419: return "SubgroupLeMaskKHR"; + case 4420: return "SubgroupLtMaskKHR"; + case 4438: return "DeviceIndex"; + case 4440: return "ViewIndex"; + case 4424: return "BaseVertex"; + case 4425: return "BaseInstance"; + case 4426: return "DrawIndex"; + case 4432: return "PrimitiveShadingRateKHR"; + case 4444: return "ShadingRateKHR"; + case 5014: return "FragStencilRefEXT"; + + case 4992: return "BaryCoordNoPerspAMD"; + case 4993: return "BaryCoordNoPerspCentroidAMD"; + case 4994: return "BaryCoordNoPerspSampleAMD"; + case 4995: return "BaryCoordSmoothAMD"; + case 4996: return "BaryCoordSmoothCentroidAMD"; + case 4997: return "BaryCoordSmoothSampleAMD"; + case 4998: return "BaryCoordPullModelAMD"; + case BuiltInLaunchIdKHR: return "LaunchIdKHR"; + case BuiltInLaunchSizeKHR: return "LaunchSizeKHR"; + case BuiltInWorldRayOriginKHR: return "WorldRayOriginKHR"; + case BuiltInWorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case BuiltInObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case BuiltInObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case BuiltInRayTminKHR: return "RayTminKHR"; + case BuiltInRayTmaxKHR: return "RayTmaxKHR"; + case BuiltInInstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case BuiltInRayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case BuiltInObjectToWorldKHR: return "ObjectToWorldKHR"; + case BuiltInWorldToObjectKHR: return "WorldToObjectKHR"; + case BuiltInHitTKHR: return "HitTKHR"; + case BuiltInHitKindKHR: return "HitKindKHR"; + case BuiltInIncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; +// case BuiltInFragmentSizeNV: return "FragmentSizeNV"; // superseded by BuiltInFragSizeEXT +// case BuiltInInvocationsPerPixelNV: return "InvocationsPerPixelNV"; // superseded by BuiltInFragInvocationCountEXT + case BuiltInBaryCoordNV: return "BaryCoordNV"; + case BuiltInBaryCoordNoPerspNV: return "BaryCoordNoPerspNV"; + + case BuiltInFragSizeEXT: return "FragSizeEXT"; + case BuiltInFragInvocationCountEXT: return "FragInvocationCountEXT"; + + case 5264: return "FullyCoveredEXT"; + + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + case BuiltInWarpsPerSMNV: return "WarpsPerSMNV"; + case BuiltInSMCountNV: return "SMCountNV"; + case BuiltInWarpIDNV: return "WarpIDNV"; + case BuiltInSMIDNV: return "SMIDNV"; + + default: return "Bad"; + } +} + +const char* DimensionString(int dim) +{ + switch (dim) { + case 0: return "1D"; + case 1: return "2D"; + case 2: return "3D"; + case 3: return "Cube"; + case 4: return "Rect"; + case 5: return "Buffer"; + case 6: return "SubpassData"; + + default: return "Bad"; + } +} + +const char* SamplerAddressingModeString(int mode) +{ + switch (mode) { + case 0: return "None"; + case 1: return "ClampToEdge"; + case 2: return "Clamp"; + case 3: return "Repeat"; + case 4: return "RepeatMirrored"; + + default: return "Bad"; + } +} + +const char* SamplerFilterModeString(int mode) +{ + switch (mode) { + case 0: return "Nearest"; + case 1: return "Linear"; + + default: return "Bad"; + } +} + +const char* ImageFormatString(int format) +{ + switch (format) { + case 0: return "Unknown"; + + // ES/Desktop float + case 1: return "Rgba32f"; + case 2: return "Rgba16f"; + case 3: return "R32f"; + case 4: return "Rgba8"; + case 5: return "Rgba8Snorm"; + + // Desktop float + case 6: return "Rg32f"; + case 7: return "Rg16f"; + case 8: return "R11fG11fB10f"; + case 9: return "R16f"; + case 10: return "Rgba16"; + case 11: return "Rgb10A2"; + case 12: return "Rg16"; + case 13: return "Rg8"; + case 14: return "R16"; + case 15: return "R8"; + case 16: return "Rgba16Snorm"; + case 17: return "Rg16Snorm"; + case 18: return "Rg8Snorm"; + case 19: return "R16Snorm"; + case 20: return "R8Snorm"; + + // ES/Desktop int + case 21: return "Rgba32i"; + case 22: return "Rgba16i"; + case 23: return "Rgba8i"; + case 24: return "R32i"; + + // Desktop int + case 25: return "Rg32i"; + case 26: return "Rg16i"; + case 27: return "Rg8i"; + case 28: return "R16i"; + case 29: return "R8i"; + + // ES/Desktop uint + case 30: return "Rgba32ui"; + case 31: return "Rgba16ui"; + case 32: return "Rgba8ui"; + case 33: return "R32ui"; + + // Desktop uint + case 34: return "Rgb10a2ui"; + case 35: return "Rg32ui"; + case 36: return "Rg16ui"; + case 37: return "Rg8ui"; + case 38: return "R16ui"; + case 39: return "R8ui"; + case 40: return "R64ui"; + case 41: return "R64i"; + + default: + return "Bad"; + } +} + +const char* ImageChannelOrderString(int format) +{ + switch (format) { + case 0: return "R"; + case 1: return "A"; + case 2: return "RG"; + case 3: return "RA"; + case 4: return "RGB"; + case 5: return "RGBA"; + case 6: return "BGRA"; + case 7: return "ARGB"; + case 8: return "Intensity"; + case 9: return "Luminance"; + case 10: return "Rx"; + case 11: return "RGx"; + case 12: return "RGBx"; + case 13: return "Depth"; + case 14: return "DepthStencil"; + case 15: return "sRGB"; + case 16: return "sRGBx"; + case 17: return "sRGBA"; + case 18: return "sBGRA"; + + default: + return "Bad"; + } +} + +const char* ImageChannelDataTypeString(int type) +{ + switch (type) + { + case 0: return "SnormInt8"; + case 1: return "SnormInt16"; + case 2: return "UnormInt8"; + case 3: return "UnormInt16"; + case 4: return "UnormShort565"; + case 5: return "UnormShort555"; + case 6: return "UnormInt101010"; + case 7: return "SignedInt8"; + case 8: return "SignedInt16"; + case 9: return "SignedInt32"; + case 10: return "UnsignedInt8"; + case 11: return "UnsignedInt16"; + case 12: return "UnsignedInt32"; + case 13: return "HalfFloat"; + case 14: return "Float"; + case 15: return "UnormInt24"; + case 16: return "UnormInt101010_2"; + + default: + return "Bad"; + } +} + +const int ImageOperandsCeiling = 14; + +const char* ImageOperandsString(int format) +{ + switch (format) { + case ImageOperandsBiasShift: return "Bias"; + case ImageOperandsLodShift: return "Lod"; + case ImageOperandsGradShift: return "Grad"; + case ImageOperandsConstOffsetShift: return "ConstOffset"; + case ImageOperandsOffsetShift: return "Offset"; + case ImageOperandsConstOffsetsShift: return "ConstOffsets"; + case ImageOperandsSampleShift: return "Sample"; + case ImageOperandsMinLodShift: return "MinLod"; + case ImageOperandsMakeTexelAvailableKHRShift: return "MakeTexelAvailableKHR"; + case ImageOperandsMakeTexelVisibleKHRShift: return "MakeTexelVisibleKHR"; + case ImageOperandsNonPrivateTexelKHRShift: return "NonPrivateTexelKHR"; + case ImageOperandsVolatileTexelKHRShift: return "VolatileTexelKHR"; + case ImageOperandsSignExtendShift: return "SignExtend"; + case ImageOperandsZeroExtendShift: return "ZeroExtend"; + + case ImageOperandsCeiling: + default: + return "Bad"; + } +} + +const char* FPFastMathString(int mode) +{ + switch (mode) { + case 0: return "NotNaN"; + case 1: return "NotInf"; + case 2: return "NSZ"; + case 3: return "AllowRecip"; + case 4: return "Fast"; + + default: return "Bad"; + } +} + +const char* FPRoundingModeString(int mode) +{ + switch (mode) { + case 0: return "RTE"; + case 1: return "RTZ"; + case 2: return "RTP"; + case 3: return "RTN"; + + default: return "Bad"; + } +} + +const char* LinkageTypeString(int type) +{ + switch (type) { + case 0: return "Export"; + case 1: return "Import"; + + default: return "Bad"; + } +} + +const char* FuncParamAttrString(int attr) +{ + switch (attr) { + case 0: return "Zext"; + case 1: return "Sext"; + case 2: return "ByVal"; + case 3: return "Sret"; + case 4: return "NoAlias"; + case 5: return "NoCapture"; + case 6: return "NoWrite"; + case 7: return "NoReadWrite"; + + default: return "Bad"; + } +} + +const char* AccessQualifierString(int attr) +{ + switch (attr) { + case 0: return "ReadOnly"; + case 1: return "WriteOnly"; + case 2: return "ReadWrite"; + + default: return "Bad"; + } +} + +const int SelectControlCeiling = 2; + +const char* SelectControlString(int cont) +{ + switch (cont) { + case 0: return "Flatten"; + case 1: return "DontFlatten"; + + case SelectControlCeiling: + default: return "Bad"; + } +} + +const int LoopControlCeiling = LoopControlPartialCountShift + 1; + +const char* LoopControlString(int cont) +{ + switch (cont) { + case LoopControlUnrollShift: return "Unroll"; + case LoopControlDontUnrollShift: return "DontUnroll"; + case LoopControlDependencyInfiniteShift: return "DependencyInfinite"; + case LoopControlDependencyLengthShift: return "DependencyLength"; + case LoopControlMinIterationsShift: return "MinIterations"; + case LoopControlMaxIterationsShift: return "MaxIterations"; + case LoopControlIterationMultipleShift: return "IterationMultiple"; + case LoopControlPeelCountShift: return "PeelCount"; + case LoopControlPartialCountShift: return "PartialCount"; + + case LoopControlCeiling: + default: return "Bad"; + } +} + +const int FunctionControlCeiling = 4; + +const char* FunctionControlString(int cont) +{ + switch (cont) { + case 0: return "Inline"; + case 1: return "DontInline"; + case 2: return "Pure"; + case 3: return "Const"; + + case FunctionControlCeiling: + default: return "Bad"; + } +} + +const char* MemorySemanticsString(int mem) +{ + // Note: No bits set (None) means "Relaxed" + switch (mem) { + case 0: return "Bad"; // Note: this is a placeholder for 'Consume' + case 1: return "Acquire"; + case 2: return "Release"; + case 3: return "AcquireRelease"; + case 4: return "SequentiallyConsistent"; + case 5: return "Bad"; // Note: reserved for future expansion + case 6: return "UniformMemory"; + case 7: return "SubgroupMemory"; + case 8: return "WorkgroupMemory"; + case 9: return "CrossWorkgroupMemory"; + case 10: return "AtomicCounterMemory"; + case 11: return "ImageMemory"; + + default: return "Bad"; + } +} + +const int MemoryAccessCeiling = 6; + +const char* MemoryAccessString(int mem) +{ + switch (mem) { + case MemoryAccessVolatileShift: return "Volatile"; + case MemoryAccessAlignedShift: return "Aligned"; + case MemoryAccessNontemporalShift: return "Nontemporal"; + case MemoryAccessMakePointerAvailableKHRShift: return "MakePointerAvailableKHR"; + case MemoryAccessMakePointerVisibleKHRShift: return "MakePointerVisibleKHR"; + case MemoryAccessNonPrivatePointerKHRShift: return "NonPrivatePointerKHR"; + + default: return "Bad"; + } +} + +const char* ScopeString(int mem) +{ + switch (mem) { + case 0: return "CrossDevice"; + case 1: return "Device"; + case 2: return "Workgroup"; + case 3: return "Subgroup"; + case 4: return "Invocation"; + + default: return "Bad"; + } +} + +const char* GroupOperationString(int gop) +{ + + switch (gop) + { + case GroupOperationReduce: return "Reduce"; + case GroupOperationInclusiveScan: return "InclusiveScan"; + case GroupOperationExclusiveScan: return "ExclusiveScan"; + case GroupOperationClusteredReduce: return "ClusteredReduce"; + case GroupOperationPartitionedReduceNV: return "PartitionedReduceNV"; + case GroupOperationPartitionedInclusiveScanNV: return "PartitionedInclusiveScanNV"; + case GroupOperationPartitionedExclusiveScanNV: return "PartitionedExclusiveScanNV"; + + default: return "Bad"; + } +} + +const char* KernelEnqueueFlagsString(int flag) +{ + switch (flag) + { + case 0: return "NoWait"; + case 1: return "WaitKernel"; + case 2: return "WaitWorkGroup"; + + default: return "Bad"; + } +} + +const char* KernelProfilingInfoString(int info) +{ + switch (info) + { + case 0: return "CmdExecTime"; + + default: return "Bad"; + } +} + +const char* CapabilityString(int info) +{ + switch (info) + { + case 0: return "Matrix"; + case 1: return "Shader"; + case 2: return "Geometry"; + case 3: return "Tessellation"; + case 4: return "Addresses"; + case 5: return "Linkage"; + case 6: return "Kernel"; + case 7: return "Vector16"; + case 8: return "Float16Buffer"; + case 9: return "Float16"; + case 10: return "Float64"; + case 11: return "Int64"; + case 12: return "Int64Atomics"; + case 13: return "ImageBasic"; + case 14: return "ImageReadWrite"; + case 15: return "ImageMipmap"; + case 16: return "Bad"; + case 17: return "Pipes"; + case 18: return "Groups"; + case 19: return "DeviceEnqueue"; + case 20: return "LiteralSampler"; + case 21: return "AtomicStorage"; + case 22: return "Int16"; + case 23: return "TessellationPointSize"; + case 24: return "GeometryPointSize"; + case 25: return "ImageGatherExtended"; + case 26: return "Bad"; + case 27: return "StorageImageMultisample"; + case 28: return "UniformBufferArrayDynamicIndexing"; + case 29: return "SampledImageArrayDynamicIndexing"; + case 30: return "StorageBufferArrayDynamicIndexing"; + case 31: return "StorageImageArrayDynamicIndexing"; + case 32: return "ClipDistance"; + case 33: return "CullDistance"; + case 34: return "ImageCubeArray"; + case 35: return "SampleRateShading"; + case 36: return "ImageRect"; + case 37: return "SampledRect"; + case 38: return "GenericPointer"; + case 39: return "Int8"; + case 40: return "InputAttachment"; + case 41: return "SparseResidency"; + case 42: return "MinLod"; + case 43: return "Sampled1D"; + case 44: return "Image1D"; + case 45: return "SampledCubeArray"; + case 46: return "SampledBuffer"; + case 47: return "ImageBuffer"; + case 48: return "ImageMSArray"; + case 49: return "StorageImageExtendedFormats"; + case 50: return "ImageQuery"; + case 51: return "DerivativeControl"; + case 52: return "InterpolationFunction"; + case 53: return "TransformFeedback"; + case 54: return "GeometryStreams"; + case 55: return "StorageImageReadWithoutFormat"; + case 56: return "StorageImageWriteWithoutFormat"; + case 57: return "MultiViewport"; + case 61: return "GroupNonUniform"; + case 62: return "GroupNonUniformVote"; + case 63: return "GroupNonUniformArithmetic"; + case 64: return "GroupNonUniformBallot"; + case 65: return "GroupNonUniformShuffle"; + case 66: return "GroupNonUniformShuffleRelative"; + case 67: return "GroupNonUniformClustered"; + case 68: return "GroupNonUniformQuad"; + + case CapabilitySubgroupBallotKHR: return "SubgroupBallotKHR"; + case CapabilityDrawParameters: return "DrawParameters"; + case CapabilitySubgroupVoteKHR: return "SubgroupVoteKHR"; + + case CapabilityStorageUniformBufferBlock16: return "StorageUniformBufferBlock16"; + case CapabilityStorageUniform16: return "StorageUniform16"; + case CapabilityStoragePushConstant16: return "StoragePushConstant16"; + case CapabilityStorageInputOutput16: return "StorageInputOutput16"; + + case CapabilityStorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case CapabilityUniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case CapabilityStoragePushConstant8: return "StoragePushConstant8"; + + case CapabilityDeviceGroup: return "DeviceGroup"; + case CapabilityMultiView: return "MultiView"; + + case CapabilityStencilExportEXT: return "StencilExportEXT"; + + case CapabilityFloat16ImageAMD: return "Float16ImageAMD"; + case CapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case CapabilityFragmentMaskAMD: return "FragmentMaskAMD"; + case CapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + + case CapabilityAtomicStorageOps: return "AtomicStorageOps"; + + case CapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportIndexLayerNV: return "ShaderViewportIndexLayerNV"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityGroupNonUniformPartitionedNV: return "GroupNonUniformPartitionedNV"; + case CapabilityRayTracingNV: return "RayTracingNV"; + case CapabilityRayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case CapabilityRayQueryProvisionalKHR: return "RayQueryProvisionalKHR"; + case CapabilityRayTraversalPrimitiveCullingProvisionalKHR: return "RayTraversalPrimitiveCullingProvisionalKHR"; + case CapabilityComputeDerivativeGroupQuadsNV: return "ComputeDerivativeGroupQuadsNV"; + case CapabilityComputeDerivativeGroupLinearNV: return "ComputeDerivativeGroupLinearNV"; + case CapabilityFragmentBarycentricNV: return "FragmentBarycentricNV"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; +// case CapabilityShadingRateNV: return "ShadingRateNV"; // superseded by FragmentDensityEXT + case CapabilitySampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case CapabilityFragmentDensityEXT: return "FragmentDensityEXT"; + + case CapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + + case CapabilityShaderNonUniformEXT: return "ShaderNonUniformEXT"; + case CapabilityRuntimeDescriptorArrayEXT: return "RuntimeDescriptorArrayEXT"; + case CapabilityInputAttachmentArrayDynamicIndexingEXT: return "InputAttachmentArrayDynamicIndexingEXT"; + case CapabilityUniformTexelBufferArrayDynamicIndexingEXT: return "UniformTexelBufferArrayDynamicIndexingEXT"; + case CapabilityStorageTexelBufferArrayDynamicIndexingEXT: return "StorageTexelBufferArrayDynamicIndexingEXT"; + case CapabilityUniformBufferArrayNonUniformIndexingEXT: return "UniformBufferArrayNonUniformIndexingEXT"; + case CapabilitySampledImageArrayNonUniformIndexingEXT: return "SampledImageArrayNonUniformIndexingEXT"; + case CapabilityStorageBufferArrayNonUniformIndexingEXT: return "StorageBufferArrayNonUniformIndexingEXT"; + case CapabilityStorageImageArrayNonUniformIndexingEXT: return "StorageImageArrayNonUniformIndexingEXT"; + case CapabilityInputAttachmentArrayNonUniformIndexingEXT: return "InputAttachmentArrayNonUniformIndexingEXT"; + case CapabilityUniformTexelBufferArrayNonUniformIndexingEXT: return "UniformTexelBufferArrayNonUniformIndexingEXT"; + case CapabilityStorageTexelBufferArrayNonUniformIndexingEXT: return "StorageTexelBufferArrayNonUniformIndexingEXT"; + + case CapabilityVulkanMemoryModelKHR: return "VulkanMemoryModelKHR"; + case CapabilityVulkanMemoryModelDeviceScopeKHR: return "VulkanMemoryModelDeviceScopeKHR"; + + case CapabilityPhysicalStorageBufferAddressesEXT: return "PhysicalStorageBufferAddressesEXT"; + + case CapabilityVariablePointers: return "VariablePointers"; + + case CapabilityCooperativeMatrixNV: return "CooperativeMatrixNV"; + case CapabilityShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + + case CapabilityFragmentShaderSampleInterlockEXT: return "CapabilityFragmentShaderSampleInterlockEXT"; + case CapabilityFragmentShaderPixelInterlockEXT: return "CapabilityFragmentShaderPixelInterlockEXT"; + case CapabilityFragmentShaderShadingRateInterlockEXT: return "CapabilityFragmentShaderShadingRateInterlockEXT"; + + case CapabilityFragmentShadingRateKHR: return "FragmentShadingRateKHR"; + + case CapabilityDemoteToHelperInvocationEXT: return "DemoteToHelperInvocationEXT"; + case CapabilityShaderClockKHR: return "ShaderClockKHR"; + case CapabilityInt64ImageEXT: return "Int64ImageEXT"; + + case CapabilityIntegerFunctions2INTEL: return "CapabilityIntegerFunctions2INTEL"; + + case CapabilityAtomicFloat32AddEXT: return "AtomicFloat32AddEXT"; + case CapabilityAtomicFloat64AddEXT: return "AtomicFloat64AddEXT"; + + default: return "Bad"; + } +} + +const char* OpcodeString(int op) +{ + switch (op) { + case 0: return "OpNop"; + case 1: return "OpUndef"; + case 2: return "OpSourceContinued"; + case 3: return "OpSource"; + case 4: return "OpSourceExtension"; + case 5: return "OpName"; + case 6: return "OpMemberName"; + case 7: return "OpString"; + case 8: return "OpLine"; + case 9: return "Bad"; + case 10: return "OpExtension"; + case 11: return "OpExtInstImport"; + case 12: return "OpExtInst"; + case 13: return "Bad"; + case 14: return "OpMemoryModel"; + case 15: return "OpEntryPoint"; + case 16: return "OpExecutionMode"; + case 17: return "OpCapability"; + case 18: return "Bad"; + case 19: return "OpTypeVoid"; + case 20: return "OpTypeBool"; + case 21: return "OpTypeInt"; + case 22: return "OpTypeFloat"; + case 23: return "OpTypeVector"; + case 24: return "OpTypeMatrix"; + case 25: return "OpTypeImage"; + case 26: return "OpTypeSampler"; + case 27: return "OpTypeSampledImage"; + case 28: return "OpTypeArray"; + case 29: return "OpTypeRuntimeArray"; + case 30: return "OpTypeStruct"; + case 31: return "OpTypeOpaque"; + case 32: return "OpTypePointer"; + case 33: return "OpTypeFunction"; + case 34: return "OpTypeEvent"; + case 35: return "OpTypeDeviceEvent"; + case 36: return "OpTypeReserveId"; + case 37: return "OpTypeQueue"; + case 38: return "OpTypePipe"; + case 39: return "OpTypeForwardPointer"; + case 40: return "Bad"; + case 41: return "OpConstantTrue"; + case 42: return "OpConstantFalse"; + case 43: return "OpConstant"; + case 44: return "OpConstantComposite"; + case 45: return "OpConstantSampler"; + case 46: return "OpConstantNull"; + case 47: return "Bad"; + case 48: return "OpSpecConstantTrue"; + case 49: return "OpSpecConstantFalse"; + case 50: return "OpSpecConstant"; + case 51: return "OpSpecConstantComposite"; + case 52: return "OpSpecConstantOp"; + case 53: return "Bad"; + case 54: return "OpFunction"; + case 55: return "OpFunctionParameter"; + case 56: return "OpFunctionEnd"; + case 57: return "OpFunctionCall"; + case 58: return "Bad"; + case 59: return "OpVariable"; + case 60: return "OpImageTexelPointer"; + case 61: return "OpLoad"; + case 62: return "OpStore"; + case 63: return "OpCopyMemory"; + case 64: return "OpCopyMemorySized"; + case 65: return "OpAccessChain"; + case 66: return "OpInBoundsAccessChain"; + case 67: return "OpPtrAccessChain"; + case 68: return "OpArrayLength"; + case 69: return "OpGenericPtrMemSemantics"; + case 70: return "OpInBoundsPtrAccessChain"; + case 71: return "OpDecorate"; + case 72: return "OpMemberDecorate"; + case 73: return "OpDecorationGroup"; + case 74: return "OpGroupDecorate"; + case 75: return "OpGroupMemberDecorate"; + case 76: return "Bad"; + case 77: return "OpVectorExtractDynamic"; + case 78: return "OpVectorInsertDynamic"; + case 79: return "OpVectorShuffle"; + case 80: return "OpCompositeConstruct"; + case 81: return "OpCompositeExtract"; + case 82: return "OpCompositeInsert"; + case 83: return "OpCopyObject"; + case 84: return "OpTranspose"; + case OpCopyLogical: return "OpCopyLogical"; + case 85: return "Bad"; + case 86: return "OpSampledImage"; + case 87: return "OpImageSampleImplicitLod"; + case 88: return "OpImageSampleExplicitLod"; + case 89: return "OpImageSampleDrefImplicitLod"; + case 90: return "OpImageSampleDrefExplicitLod"; + case 91: return "OpImageSampleProjImplicitLod"; + case 92: return "OpImageSampleProjExplicitLod"; + case 93: return "OpImageSampleProjDrefImplicitLod"; + case 94: return "OpImageSampleProjDrefExplicitLod"; + case 95: return "OpImageFetch"; + case 96: return "OpImageGather"; + case 97: return "OpImageDrefGather"; + case 98: return "OpImageRead"; + case 99: return "OpImageWrite"; + case 100: return "OpImage"; + case 101: return "OpImageQueryFormat"; + case 102: return "OpImageQueryOrder"; + case 103: return "OpImageQuerySizeLod"; + case 104: return "OpImageQuerySize"; + case 105: return "OpImageQueryLod"; + case 106: return "OpImageQueryLevels"; + case 107: return "OpImageQuerySamples"; + case 108: return "Bad"; + case 109: return "OpConvertFToU"; + case 110: return "OpConvertFToS"; + case 111: return "OpConvertSToF"; + case 112: return "OpConvertUToF"; + case 113: return "OpUConvert"; + case 114: return "OpSConvert"; + case 115: return "OpFConvert"; + case 116: return "OpQuantizeToF16"; + case 117: return "OpConvertPtrToU"; + case 118: return "OpSatConvertSToU"; + case 119: return "OpSatConvertUToS"; + case 120: return "OpConvertUToPtr"; + case 121: return "OpPtrCastToGeneric"; + case 122: return "OpGenericCastToPtr"; + case 123: return "OpGenericCastToPtrExplicit"; + case 124: return "OpBitcast"; + case 125: return "Bad"; + case 126: return "OpSNegate"; + case 127: return "OpFNegate"; + case 128: return "OpIAdd"; + case 129: return "OpFAdd"; + case 130: return "OpISub"; + case 131: return "OpFSub"; + case 132: return "OpIMul"; + case 133: return "OpFMul"; + case 134: return "OpUDiv"; + case 135: return "OpSDiv"; + case 136: return "OpFDiv"; + case 137: return "OpUMod"; + case 138: return "OpSRem"; + case 139: return "OpSMod"; + case 140: return "OpFRem"; + case 141: return "OpFMod"; + case 142: return "OpVectorTimesScalar"; + case 143: return "OpMatrixTimesScalar"; + case 144: return "OpVectorTimesMatrix"; + case 145: return "OpMatrixTimesVector"; + case 146: return "OpMatrixTimesMatrix"; + case 147: return "OpOuterProduct"; + case 148: return "OpDot"; + case 149: return "OpIAddCarry"; + case 150: return "OpISubBorrow"; + case 151: return "OpUMulExtended"; + case 152: return "OpSMulExtended"; + case 153: return "Bad"; + case 154: return "OpAny"; + case 155: return "OpAll"; + case 156: return "OpIsNan"; + case 157: return "OpIsInf"; + case 158: return "OpIsFinite"; + case 159: return "OpIsNormal"; + case 160: return "OpSignBitSet"; + case 161: return "OpLessOrGreater"; + case 162: return "OpOrdered"; + case 163: return "OpUnordered"; + case 164: return "OpLogicalEqual"; + case 165: return "OpLogicalNotEqual"; + case 166: return "OpLogicalOr"; + case 167: return "OpLogicalAnd"; + case 168: return "OpLogicalNot"; + case 169: return "OpSelect"; + case 170: return "OpIEqual"; + case 171: return "OpINotEqual"; + case 172: return "OpUGreaterThan"; + case 173: return "OpSGreaterThan"; + case 174: return "OpUGreaterThanEqual"; + case 175: return "OpSGreaterThanEqual"; + case 176: return "OpULessThan"; + case 177: return "OpSLessThan"; + case 178: return "OpULessThanEqual"; + case 179: return "OpSLessThanEqual"; + case 180: return "OpFOrdEqual"; + case 181: return "OpFUnordEqual"; + case 182: return "OpFOrdNotEqual"; + case 183: return "OpFUnordNotEqual"; + case 184: return "OpFOrdLessThan"; + case 185: return "OpFUnordLessThan"; + case 186: return "OpFOrdGreaterThan"; + case 187: return "OpFUnordGreaterThan"; + case 188: return "OpFOrdLessThanEqual"; + case 189: return "OpFUnordLessThanEqual"; + case 190: return "OpFOrdGreaterThanEqual"; + case 191: return "OpFUnordGreaterThanEqual"; + case 192: return "Bad"; + case 193: return "Bad"; + case 194: return "OpShiftRightLogical"; + case 195: return "OpShiftRightArithmetic"; + case 196: return "OpShiftLeftLogical"; + case 197: return "OpBitwiseOr"; + case 198: return "OpBitwiseXor"; + case 199: return "OpBitwiseAnd"; + case 200: return "OpNot"; + case 201: return "OpBitFieldInsert"; + case 202: return "OpBitFieldSExtract"; + case 203: return "OpBitFieldUExtract"; + case 204: return "OpBitReverse"; + case 205: return "OpBitCount"; + case 206: return "Bad"; + case 207: return "OpDPdx"; + case 208: return "OpDPdy"; + case 209: return "OpFwidth"; + case 210: return "OpDPdxFine"; + case 211: return "OpDPdyFine"; + case 212: return "OpFwidthFine"; + case 213: return "OpDPdxCoarse"; + case 214: return "OpDPdyCoarse"; + case 215: return "OpFwidthCoarse"; + case 216: return "Bad"; + case 217: return "Bad"; + case 218: return "OpEmitVertex"; + case 219: return "OpEndPrimitive"; + case 220: return "OpEmitStreamVertex"; + case 221: return "OpEndStreamPrimitive"; + case 222: return "Bad"; + case 223: return "Bad"; + case 224: return "OpControlBarrier"; + case 225: return "OpMemoryBarrier"; + case 226: return "Bad"; + case 227: return "OpAtomicLoad"; + case 228: return "OpAtomicStore"; + case 229: return "OpAtomicExchange"; + case 230: return "OpAtomicCompareExchange"; + case 231: return "OpAtomicCompareExchangeWeak"; + case 232: return "OpAtomicIIncrement"; + case 233: return "OpAtomicIDecrement"; + case 234: return "OpAtomicIAdd"; + case 235: return "OpAtomicISub"; + case 236: return "OpAtomicSMin"; + case 237: return "OpAtomicUMin"; + case 238: return "OpAtomicSMax"; + case 239: return "OpAtomicUMax"; + case 240: return "OpAtomicAnd"; + case 241: return "OpAtomicOr"; + case 242: return "OpAtomicXor"; + case 243: return "Bad"; + case 244: return "Bad"; + case 245: return "OpPhi"; + case 246: return "OpLoopMerge"; + case 247: return "OpSelectionMerge"; + case 248: return "OpLabel"; + case 249: return "OpBranch"; + case 250: return "OpBranchConditional"; + case 251: return "OpSwitch"; + case 252: return "OpKill"; + case 253: return "OpReturn"; + case 254: return "OpReturnValue"; + case 255: return "OpUnreachable"; + case 256: return "OpLifetimeStart"; + case 257: return "OpLifetimeStop"; + case 258: return "Bad"; + case 259: return "OpGroupAsyncCopy"; + case 260: return "OpGroupWaitEvents"; + case 261: return "OpGroupAll"; + case 262: return "OpGroupAny"; + case 263: return "OpGroupBroadcast"; + case 264: return "OpGroupIAdd"; + case 265: return "OpGroupFAdd"; + case 266: return "OpGroupFMin"; + case 267: return "OpGroupUMin"; + case 268: return "OpGroupSMin"; + case 269: return "OpGroupFMax"; + case 270: return "OpGroupUMax"; + case 271: return "OpGroupSMax"; + case 272: return "Bad"; + case 273: return "Bad"; + case 274: return "OpReadPipe"; + case 275: return "OpWritePipe"; + case 276: return "OpReservedReadPipe"; + case 277: return "OpReservedWritePipe"; + case 278: return "OpReserveReadPipePackets"; + case 279: return "OpReserveWritePipePackets"; + case 280: return "OpCommitReadPipe"; + case 281: return "OpCommitWritePipe"; + case 282: return "OpIsValidReserveId"; + case 283: return "OpGetNumPipePackets"; + case 284: return "OpGetMaxPipePackets"; + case 285: return "OpGroupReserveReadPipePackets"; + case 286: return "OpGroupReserveWritePipePackets"; + case 287: return "OpGroupCommitReadPipe"; + case 288: return "OpGroupCommitWritePipe"; + case 289: return "Bad"; + case 290: return "Bad"; + case 291: return "OpEnqueueMarker"; + case 292: return "OpEnqueueKernel"; + case 293: return "OpGetKernelNDrangeSubGroupCount"; + case 294: return "OpGetKernelNDrangeMaxSubGroupSize"; + case 295: return "OpGetKernelWorkGroupSize"; + case 296: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case 297: return "OpRetainEvent"; + case 298: return "OpReleaseEvent"; + case 299: return "OpCreateUserEvent"; + case 300: return "OpIsValidEvent"; + case 301: return "OpSetUserEventStatus"; + case 302: return "OpCaptureEventProfilingInfo"; + case 303: return "OpGetDefaultQueue"; + case 304: return "OpBuildNDRange"; + case 305: return "OpImageSparseSampleImplicitLod"; + case 306: return "OpImageSparseSampleExplicitLod"; + case 307: return "OpImageSparseSampleDrefImplicitLod"; + case 308: return "OpImageSparseSampleDrefExplicitLod"; + case 309: return "OpImageSparseSampleProjImplicitLod"; + case 310: return "OpImageSparseSampleProjExplicitLod"; + case 311: return "OpImageSparseSampleProjDrefImplicitLod"; + case 312: return "OpImageSparseSampleProjDrefExplicitLod"; + case 313: return "OpImageSparseFetch"; + case 314: return "OpImageSparseGather"; + case 315: return "OpImageSparseDrefGather"; + case 316: return "OpImageSparseTexelsResident"; + case 317: return "OpNoLine"; + case 318: return "OpAtomicFlagTestAndSet"; + case 319: return "OpAtomicFlagClear"; + case 320: return "OpImageSparseRead"; + + case OpModuleProcessed: return "OpModuleProcessed"; + case OpExecutionModeId: return "OpExecutionModeId"; + case OpDecorateId: return "OpDecorateId"; + + case 333: return "OpGroupNonUniformElect"; + case 334: return "OpGroupNonUniformAll"; + case 335: return "OpGroupNonUniformAny"; + case 336: return "OpGroupNonUniformAllEqual"; + case 337: return "OpGroupNonUniformBroadcast"; + case 338: return "OpGroupNonUniformBroadcastFirst"; + case 339: return "OpGroupNonUniformBallot"; + case 340: return "OpGroupNonUniformInverseBallot"; + case 341: return "OpGroupNonUniformBallotBitExtract"; + case 342: return "OpGroupNonUniformBallotBitCount"; + case 343: return "OpGroupNonUniformBallotFindLSB"; + case 344: return "OpGroupNonUniformBallotFindMSB"; + case 345: return "OpGroupNonUniformShuffle"; + case 346: return "OpGroupNonUniformShuffleXor"; + case 347: return "OpGroupNonUniformShuffleUp"; + case 348: return "OpGroupNonUniformShuffleDown"; + case 349: return "OpGroupNonUniformIAdd"; + case 350: return "OpGroupNonUniformFAdd"; + case 351: return "OpGroupNonUniformIMul"; + case 352: return "OpGroupNonUniformFMul"; + case 353: return "OpGroupNonUniformSMin"; + case 354: return "OpGroupNonUniformUMin"; + case 355: return "OpGroupNonUniformFMin"; + case 356: return "OpGroupNonUniformSMax"; + case 357: return "OpGroupNonUniformUMax"; + case 358: return "OpGroupNonUniformFMax"; + case 359: return "OpGroupNonUniformBitwiseAnd"; + case 360: return "OpGroupNonUniformBitwiseOr"; + case 361: return "OpGroupNonUniformBitwiseXor"; + case 362: return "OpGroupNonUniformLogicalAnd"; + case 363: return "OpGroupNonUniformLogicalOr"; + case 364: return "OpGroupNonUniformLogicalXor"; + case 365: return "OpGroupNonUniformQuadBroadcast"; + case 366: return "OpGroupNonUniformQuadSwap"; + + case OpTerminateInvocation: return "OpTerminateInvocation"; + + case 4421: return "OpSubgroupBallotKHR"; + case 4422: return "OpSubgroupFirstInvocationKHR"; + case 4428: return "OpSubgroupAllKHR"; + case 4429: return "OpSubgroupAnyKHR"; + case 4430: return "OpSubgroupAllEqualKHR"; + case 4432: return "OpSubgroupReadInvocationKHR"; + + case OpAtomicFAddEXT: return "OpAtomicFAddEXT"; + + case 5000: return "OpGroupIAddNonUniformAMD"; + case 5001: return "OpGroupFAddNonUniformAMD"; + case 5002: return "OpGroupFMinNonUniformAMD"; + case 5003: return "OpGroupUMinNonUniformAMD"; + case 5004: return "OpGroupSMinNonUniformAMD"; + case 5005: return "OpGroupFMaxNonUniformAMD"; + case 5006: return "OpGroupUMaxNonUniformAMD"; + case 5007: return "OpGroupSMaxNonUniformAMD"; + + case 5011: return "OpFragmentMaskFetchAMD"; + case 5012: return "OpFragmentFetchAMD"; + + case OpReadClockKHR: return "OpReadClockKHR"; + + case OpDecorateStringGOOGLE: return "OpDecorateStringGOOGLE"; + case OpMemberDecorateStringGOOGLE: return "OpMemberDecorateStringGOOGLE"; + + case OpGroupNonUniformPartitionNV: return "OpGroupNonUniformPartitionNV"; + case OpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case OpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case OpTerminateRayKHR: return "OpTerminateRayKHR"; + case OpTraceRayKHR: return "OpTraceRayKHR"; + case OpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case OpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case OpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case OpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + + case OpTypeRayQueryProvisionalKHR: return "OpTypeRayQueryProvisionalKHR"; + case OpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case OpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case OpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case OpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case OpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case OpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case OpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case OpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case OpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case OpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case OpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case OpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case OpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case OpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case OpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case OpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case OpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case OpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + + case OpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case OpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case OpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case OpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case OpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case OpDemoteToHelperInvocationEXT: return "OpDemoteToHelperInvocationEXT"; + case OpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + + case OpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case OpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + + default: + return "Bad"; + } +} + +// The set of objects that hold all the instruction/operand +// parameterization information. +InstructionParameters InstructionDesc[OpCodeMask + 1]; +OperandParameters ExecutionModeOperands[ExecutionModeCeiling]; +OperandParameters DecorationOperands[DecorationCeiling]; + +EnumDefinition OperandClassParams[OperandCount]; +EnumParameters ExecutionModeParams[ExecutionModeCeiling]; +EnumParameters ImageOperandsParams[ImageOperandsCeiling]; +EnumParameters DecorationParams[DecorationCeiling]; +EnumParameters LoopControlParams[FunctionControlCeiling]; +EnumParameters SelectionControlParams[SelectControlCeiling]; +EnumParameters FunctionControlParams[FunctionControlCeiling]; +EnumParameters MemoryAccessParams[MemoryAccessCeiling]; + +// Set up all the parameterizing descriptions of the opcodes, operands, etc. +void Parameterize() +{ + // only do this once. + static bool initialized = false; + if (initialized) + return; + initialized = true; + + // Exceptions to having a result and a resulting type . + // (Everything is initialized to have both). + + InstructionDesc[OpNop].setResultAndType(false, false); + InstructionDesc[OpSource].setResultAndType(false, false); + InstructionDesc[OpSourceContinued].setResultAndType(false, false); + InstructionDesc[OpSourceExtension].setResultAndType(false, false); + InstructionDesc[OpExtension].setResultAndType(false, false); + InstructionDesc[OpExtInstImport].setResultAndType(true, false); + InstructionDesc[OpCapability].setResultAndType(false, false); + InstructionDesc[OpMemoryModel].setResultAndType(false, false); + InstructionDesc[OpEntryPoint].setResultAndType(false, false); + InstructionDesc[OpExecutionMode].setResultAndType(false, false); + InstructionDesc[OpExecutionModeId].setResultAndType(false, false); + InstructionDesc[OpTypeVoid].setResultAndType(true, false); + InstructionDesc[OpTypeBool].setResultAndType(true, false); + InstructionDesc[OpTypeInt].setResultAndType(true, false); + InstructionDesc[OpTypeFloat].setResultAndType(true, false); + InstructionDesc[OpTypeVector].setResultAndType(true, false); + InstructionDesc[OpTypeMatrix].setResultAndType(true, false); + InstructionDesc[OpTypeImage].setResultAndType(true, false); + InstructionDesc[OpTypeSampler].setResultAndType(true, false); + InstructionDesc[OpTypeSampledImage].setResultAndType(true, false); + InstructionDesc[OpTypeArray].setResultAndType(true, false); + InstructionDesc[OpTypeRuntimeArray].setResultAndType(true, false); + InstructionDesc[OpTypeStruct].setResultAndType(true, false); + InstructionDesc[OpTypeOpaque].setResultAndType(true, false); + InstructionDesc[OpTypePointer].setResultAndType(true, false); + InstructionDesc[OpTypeForwardPointer].setResultAndType(false, false); + InstructionDesc[OpTypeFunction].setResultAndType(true, false); + InstructionDesc[OpTypeEvent].setResultAndType(true, false); + InstructionDesc[OpTypeDeviceEvent].setResultAndType(true, false); + InstructionDesc[OpTypeReserveId].setResultAndType(true, false); + InstructionDesc[OpTypeQueue].setResultAndType(true, false); + InstructionDesc[OpTypePipe].setResultAndType(true, false); + InstructionDesc[OpFunctionEnd].setResultAndType(false, false); + InstructionDesc[OpStore].setResultAndType(false, false); + InstructionDesc[OpImageWrite].setResultAndType(false, false); + InstructionDesc[OpDecorationGroup].setResultAndType(true, false); + InstructionDesc[OpDecorate].setResultAndType(false, false); + InstructionDesc[OpDecorateId].setResultAndType(false, false); + InstructionDesc[OpDecorateStringGOOGLE].setResultAndType(false, false); + InstructionDesc[OpMemberDecorate].setResultAndType(false, false); + InstructionDesc[OpMemberDecorateStringGOOGLE].setResultAndType(false, false); + InstructionDesc[OpGroupDecorate].setResultAndType(false, false); + InstructionDesc[OpGroupMemberDecorate].setResultAndType(false, false); + InstructionDesc[OpName].setResultAndType(false, false); + InstructionDesc[OpMemberName].setResultAndType(false, false); + InstructionDesc[OpString].setResultAndType(true, false); + InstructionDesc[OpLine].setResultAndType(false, false); + InstructionDesc[OpNoLine].setResultAndType(false, false); + InstructionDesc[OpCopyMemory].setResultAndType(false, false); + InstructionDesc[OpCopyMemorySized].setResultAndType(false, false); + InstructionDesc[OpEmitVertex].setResultAndType(false, false); + InstructionDesc[OpEndPrimitive].setResultAndType(false, false); + InstructionDesc[OpEmitStreamVertex].setResultAndType(false, false); + InstructionDesc[OpEndStreamPrimitive].setResultAndType(false, false); + InstructionDesc[OpControlBarrier].setResultAndType(false, false); + InstructionDesc[OpMemoryBarrier].setResultAndType(false, false); + InstructionDesc[OpAtomicStore].setResultAndType(false, false); + InstructionDesc[OpLoopMerge].setResultAndType(false, false); + InstructionDesc[OpSelectionMerge].setResultAndType(false, false); + InstructionDesc[OpLabel].setResultAndType(true, false); + InstructionDesc[OpBranch].setResultAndType(false, false); + InstructionDesc[OpBranchConditional].setResultAndType(false, false); + InstructionDesc[OpSwitch].setResultAndType(false, false); + InstructionDesc[OpKill].setResultAndType(false, false); + InstructionDesc[OpTerminateInvocation].setResultAndType(false, false); + InstructionDesc[OpReturn].setResultAndType(false, false); + InstructionDesc[OpReturnValue].setResultAndType(false, false); + InstructionDesc[OpUnreachable].setResultAndType(false, false); + InstructionDesc[OpLifetimeStart].setResultAndType(false, false); + InstructionDesc[OpLifetimeStop].setResultAndType(false, false); + InstructionDesc[OpCommitReadPipe].setResultAndType(false, false); + InstructionDesc[OpCommitWritePipe].setResultAndType(false, false); + InstructionDesc[OpGroupCommitWritePipe].setResultAndType(false, false); + InstructionDesc[OpGroupCommitReadPipe].setResultAndType(false, false); + InstructionDesc[OpCaptureEventProfilingInfo].setResultAndType(false, false); + InstructionDesc[OpSetUserEventStatus].setResultAndType(false, false); + InstructionDesc[OpRetainEvent].setResultAndType(false, false); + InstructionDesc[OpReleaseEvent].setResultAndType(false, false); + InstructionDesc[OpGroupWaitEvents].setResultAndType(false, false); + InstructionDesc[OpAtomicFlagClear].setResultAndType(false, false); + InstructionDesc[OpModuleProcessed].setResultAndType(false, false); + InstructionDesc[OpTypeCooperativeMatrixNV].setResultAndType(true, false); + InstructionDesc[OpCooperativeMatrixStoreNV].setResultAndType(false, false); + InstructionDesc[OpBeginInvocationInterlockEXT].setResultAndType(false, false); + InstructionDesc[OpEndInvocationInterlockEXT].setResultAndType(false, false); + + // Specific additional context-dependent operands + + ExecutionModeOperands[ExecutionModeInvocations].push(OperandLiteralNumber, "'Number of <>'"); + + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'x size'"); + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'y size'"); + ExecutionModeOperands[ExecutionModeLocalSize].push(OperandLiteralNumber, "'z size'"); + + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'x size'"); + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'y size'"); + ExecutionModeOperands[ExecutionModeLocalSizeHint].push(OperandLiteralNumber, "'z size'"); + + ExecutionModeOperands[ExecutionModeOutputVertices].push(OperandLiteralNumber, "'Vertex count'"); + ExecutionModeOperands[ExecutionModeVecTypeHint].push(OperandLiteralNumber, "'Vector type'"); + + DecorationOperands[DecorationStream].push(OperandLiteralNumber, "'Stream Number'"); + DecorationOperands[DecorationLocation].push(OperandLiteralNumber, "'Location'"); + DecorationOperands[DecorationComponent].push(OperandLiteralNumber, "'Component'"); + DecorationOperands[DecorationIndex].push(OperandLiteralNumber, "'Index'"); + DecorationOperands[DecorationBinding].push(OperandLiteralNumber, "'Binding Point'"); + DecorationOperands[DecorationDescriptorSet].push(OperandLiteralNumber, "'Descriptor Set'"); + DecorationOperands[DecorationOffset].push(OperandLiteralNumber, "'Byte Offset'"); + DecorationOperands[DecorationXfbBuffer].push(OperandLiteralNumber, "'XFB Buffer Number'"); + DecorationOperands[DecorationXfbStride].push(OperandLiteralNumber, "'XFB Stride'"); + DecorationOperands[DecorationArrayStride].push(OperandLiteralNumber, "'Array Stride'"); + DecorationOperands[DecorationMatrixStride].push(OperandLiteralNumber, "'Matrix Stride'"); + DecorationOperands[DecorationBuiltIn].push(OperandLiteralNumber, "See <>"); + DecorationOperands[DecorationFPRoundingMode].push(OperandFPRoundingMode, "'Floating-Point Rounding Mode'"); + DecorationOperands[DecorationFPFastMathMode].push(OperandFPFastMath, "'Fast-Math Mode'"); + DecorationOperands[DecorationLinkageAttributes].push(OperandLiteralString, "'Name'"); + DecorationOperands[DecorationLinkageAttributes].push(OperandLinkageType, "'Linkage Type'"); + DecorationOperands[DecorationFuncParamAttr].push(OperandFuncParamAttr, "'Function Parameter Attribute'"); + DecorationOperands[DecorationSpecId].push(OperandLiteralNumber, "'Specialization Constant ID'"); + DecorationOperands[DecorationInputAttachmentIndex].push(OperandLiteralNumber, "'Attachment Index'"); + DecorationOperands[DecorationAlignment].push(OperandLiteralNumber, "'Alignment'"); + + OperandClassParams[OperandSource].set(0, SourceString, 0); + OperandClassParams[OperandExecutionModel].set(0, ExecutionModelString, nullptr); + OperandClassParams[OperandAddressing].set(0, AddressingString, nullptr); + OperandClassParams[OperandMemory].set(0, MemoryString, nullptr); + OperandClassParams[OperandExecutionMode].set(ExecutionModeCeiling, ExecutionModeString, ExecutionModeParams); + OperandClassParams[OperandExecutionMode].setOperands(ExecutionModeOperands); + OperandClassParams[OperandStorage].set(0, StorageClassString, nullptr); + OperandClassParams[OperandDimensionality].set(0, DimensionString, nullptr); + OperandClassParams[OperandSamplerAddressingMode].set(0, SamplerAddressingModeString, nullptr); + OperandClassParams[OperandSamplerFilterMode].set(0, SamplerFilterModeString, nullptr); + OperandClassParams[OperandSamplerImageFormat].set(0, ImageFormatString, nullptr); + OperandClassParams[OperandImageChannelOrder].set(0, ImageChannelOrderString, nullptr); + OperandClassParams[OperandImageChannelDataType].set(0, ImageChannelDataTypeString, nullptr); + OperandClassParams[OperandImageOperands].set(ImageOperandsCeiling, ImageOperandsString, ImageOperandsParams, true); + OperandClassParams[OperandFPFastMath].set(0, FPFastMathString, nullptr, true); + OperandClassParams[OperandFPRoundingMode].set(0, FPRoundingModeString, nullptr); + OperandClassParams[OperandLinkageType].set(0, LinkageTypeString, nullptr); + OperandClassParams[OperandFuncParamAttr].set(0, FuncParamAttrString, nullptr); + OperandClassParams[OperandAccessQualifier].set(0, AccessQualifierString, nullptr); + OperandClassParams[OperandDecoration].set(DecorationCeiling, DecorationString, DecorationParams); + OperandClassParams[OperandDecoration].setOperands(DecorationOperands); + OperandClassParams[OperandBuiltIn].set(0, BuiltInString, nullptr); + OperandClassParams[OperandSelect].set(SelectControlCeiling, SelectControlString, SelectionControlParams, true); + OperandClassParams[OperandLoop].set(LoopControlCeiling, LoopControlString, LoopControlParams, true); + OperandClassParams[OperandFunction].set(FunctionControlCeiling, FunctionControlString, FunctionControlParams, true); + OperandClassParams[OperandMemorySemantics].set(0, MemorySemanticsString, nullptr, true); + OperandClassParams[OperandMemoryAccess].set(MemoryAccessCeiling, MemoryAccessString, MemoryAccessParams, true); + OperandClassParams[OperandScope].set(0, ScopeString, nullptr); + OperandClassParams[OperandGroupOperation].set(0, GroupOperationString, nullptr); + OperandClassParams[OperandKernelEnqueueFlags].set(0, KernelEnqueueFlagsString, nullptr); + OperandClassParams[OperandKernelProfilingInfo].set(0, KernelProfilingInfoString, nullptr, true); + OperandClassParams[OperandCapability].set(0, CapabilityString, nullptr); + OperandClassParams[OperandOpcode].set(OpCodeMask + 1, OpcodeString, 0); + + // set name of operator, an initial set of style operands, and the description + + InstructionDesc[OpSource].operands.push(OperandSource, ""); + InstructionDesc[OpSource].operands.push(OperandLiteralNumber, "'Version'"); + InstructionDesc[OpSource].operands.push(OperandId, "'File'", true); + InstructionDesc[OpSource].operands.push(OperandLiteralString, "'Source'", true); + + InstructionDesc[OpSourceContinued].operands.push(OperandLiteralString, "'Continued Source'"); + + InstructionDesc[OpSourceExtension].operands.push(OperandLiteralString, "'Extension'"); + + InstructionDesc[OpName].operands.push(OperandId, "'Target'"); + InstructionDesc[OpName].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpMemberName].operands.push(OperandId, "'Type'"); + InstructionDesc[OpMemberName].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberName].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpString].operands.push(OperandLiteralString, "'String'"); + + InstructionDesc[OpLine].operands.push(OperandId, "'File'"); + InstructionDesc[OpLine].operands.push(OperandLiteralNumber, "'Line'"); + InstructionDesc[OpLine].operands.push(OperandLiteralNumber, "'Column'"); + + InstructionDesc[OpExtension].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpExtInstImport].operands.push(OperandLiteralString, "'Name'"); + + InstructionDesc[OpCapability].operands.push(OperandCapability, "'Capability'"); + + InstructionDesc[OpMemoryModel].operands.push(OperandAddressing, ""); + InstructionDesc[OpMemoryModel].operands.push(OperandMemory, ""); + + InstructionDesc[OpEntryPoint].operands.push(OperandExecutionModel, ""); + InstructionDesc[OpEntryPoint].operands.push(OperandId, "'Entry Point'"); + InstructionDesc[OpEntryPoint].operands.push(OperandLiteralString, "'Name'"); + InstructionDesc[OpEntryPoint].operands.push(OperandVariableIds, "'Interface'"); + + InstructionDesc[OpExecutionMode].operands.push(OperandId, "'Entry Point'"); + InstructionDesc[OpExecutionMode].operands.push(OperandExecutionMode, "'Mode'"); + InstructionDesc[OpExecutionMode].operands.push(OperandOptionalLiteral, "See <>"); + + InstructionDesc[OpExecutionModeId].operands.push(OperandId, "'Entry Point'"); + InstructionDesc[OpExecutionModeId].operands.push(OperandExecutionMode, "'Mode'"); + InstructionDesc[OpExecutionModeId].operands.push(OperandVariableIds, "See <>"); + + InstructionDesc[OpTypeInt].operands.push(OperandLiteralNumber, "'Width'"); + InstructionDesc[OpTypeInt].operands.push(OperandLiteralNumber, "'Signedness'"); + + InstructionDesc[OpTypeFloat].operands.push(OperandLiteralNumber, "'Width'"); + + InstructionDesc[OpTypeVector].operands.push(OperandId, "'Component Type'"); + InstructionDesc[OpTypeVector].operands.push(OperandLiteralNumber, "'Component Count'"); + + InstructionDesc[OpTypeMatrix].operands.push(OperandId, "'Column Type'"); + InstructionDesc[OpTypeMatrix].operands.push(OperandLiteralNumber, "'Column Count'"); + + InstructionDesc[OpTypeImage].operands.push(OperandId, "'Sampled Type'"); + InstructionDesc[OpTypeImage].operands.push(OperandDimensionality, ""); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Depth'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Arrayed'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'MS'"); + InstructionDesc[OpTypeImage].operands.push(OperandLiteralNumber, "'Sampled'"); + InstructionDesc[OpTypeImage].operands.push(OperandSamplerImageFormat, ""); + InstructionDesc[OpTypeImage].operands.push(OperandAccessQualifier, "", true); + + InstructionDesc[OpTypeSampledImage].operands.push(OperandId, "'Image Type'"); + + InstructionDesc[OpTypeArray].operands.push(OperandId, "'Element Type'"); + InstructionDesc[OpTypeArray].operands.push(OperandId, "'Length'"); + + InstructionDesc[OpTypeRuntimeArray].operands.push(OperandId, "'Element Type'"); + + InstructionDesc[OpTypeStruct].operands.push(OperandVariableIds, "'Member 0 type', +\n'member 1 type', +\n..."); + + InstructionDesc[OpTypeOpaque].operands.push(OperandLiteralString, "The name of the opaque type."); + + InstructionDesc[OpTypePointer].operands.push(OperandStorage, ""); + InstructionDesc[OpTypePointer].operands.push(OperandId, "'Type'"); + + InstructionDesc[OpTypeForwardPointer].operands.push(OperandId, "'Pointer Type'"); + InstructionDesc[OpTypeForwardPointer].operands.push(OperandStorage, ""); + + InstructionDesc[OpTypePipe].operands.push(OperandAccessQualifier, "'Qualifier'"); + + InstructionDesc[OpTypeFunction].operands.push(OperandId, "'Return Type'"); + InstructionDesc[OpTypeFunction].operands.push(OperandVariableIds, "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..."); + + InstructionDesc[OpConstant].operands.push(OperandVariableLiterals, "'Value'"); + + InstructionDesc[OpConstantComposite].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpConstantSampler].operands.push(OperandSamplerAddressingMode, ""); + InstructionDesc[OpConstantSampler].operands.push(OperandLiteralNumber, "'Param'"); + InstructionDesc[OpConstantSampler].operands.push(OperandSamplerFilterMode, ""); + + InstructionDesc[OpSpecConstant].operands.push(OperandVariableLiterals, "'Value'"); + + InstructionDesc[OpSpecConstantComposite].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpSpecConstantOp].operands.push(OperandLiteralNumber, "'Opcode'"); + InstructionDesc[OpSpecConstantOp].operands.push(OperandVariableIds, "'Operands'"); + + InstructionDesc[OpVariable].operands.push(OperandStorage, ""); + InstructionDesc[OpVariable].operands.push(OperandId, "'Initializer'", true); + + InstructionDesc[OpFunction].operands.push(OperandFunction, ""); + InstructionDesc[OpFunction].operands.push(OperandId, "'Function Type'"); + + InstructionDesc[OpFunctionCall].operands.push(OperandId, "'Function'"); + InstructionDesc[OpFunctionCall].operands.push(OperandVariableIds, "'Argument 0', +\n'Argument 1', +\n..."); + + InstructionDesc[OpExtInst].operands.push(OperandId, "'Set'"); + InstructionDesc[OpExtInst].operands.push(OperandLiteralNumber, "'Instruction'"); + InstructionDesc[OpExtInst].operands.push(OperandVariableIds, "'Operand 1', +\n'Operand 2', +\n..."); + + InstructionDesc[OpLoad].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLoad].operands.push(OperandMemoryAccess, "", true); + InstructionDesc[OpLoad].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpLoad].operands.push(OperandId, "", true); + + InstructionDesc[OpStore].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpStore].operands.push(OperandId, "'Object'"); + InstructionDesc[OpStore].operands.push(OperandMemoryAccess, "", true); + InstructionDesc[OpStore].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpStore].operands.push(OperandId, "", true); + + InstructionDesc[OpPhi].operands.push(OperandVariableIds, "'Variable, Parent, ...'"); + + InstructionDesc[OpDecorate].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorate].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorate].operands.push(OperandVariableLiterals, "See <>."); + + InstructionDesc[OpDecorateId].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorateId].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorateId].operands.push(OperandVariableIds, "See <>."); + + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandId, "'Target'"); + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandDecoration, ""); + InstructionDesc[OpDecorateStringGOOGLE].operands.push(OperandVariableLiteralStrings, "'Literal Strings'"); + + InstructionDesc[OpMemberDecorate].operands.push(OperandId, "'Structure Type'"); + InstructionDesc[OpMemberDecorate].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberDecorate].operands.push(OperandDecoration, ""); + InstructionDesc[OpMemberDecorate].operands.push(OperandVariableLiterals, "See <>."); + + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandId, "'Structure Type'"); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandLiteralNumber, "'Member'"); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandDecoration, ""); + InstructionDesc[OpMemberDecorateStringGOOGLE].operands.push(OperandVariableLiteralStrings, "'Literal Strings'"); + + InstructionDesc[OpGroupDecorate].operands.push(OperandId, "'Decoration Group'"); + InstructionDesc[OpGroupDecorate].operands.push(OperandVariableIds, "'Targets'"); + + InstructionDesc[OpGroupMemberDecorate].operands.push(OperandId, "'Decoration Group'"); + InstructionDesc[OpGroupMemberDecorate].operands.push(OperandVariableIdLiteral, "'Targets'"); + + InstructionDesc[OpVectorExtractDynamic].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorExtractDynamic].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Component'"); + InstructionDesc[OpVectorInsertDynamic].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpVectorShuffle].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpVectorShuffle].operands.push(OperandId, "'Vector 2'"); + InstructionDesc[OpVectorShuffle].operands.push(OperandVariableLiterals, "'Components'"); + + InstructionDesc[OpCompositeConstruct].operands.push(OperandVariableIds, "'Constituents'"); + + InstructionDesc[OpCompositeExtract].operands.push(OperandId, "'Composite'"); + InstructionDesc[OpCompositeExtract].operands.push(OperandVariableLiterals, "'Indexes'"); + + InstructionDesc[OpCompositeInsert].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCompositeInsert].operands.push(OperandId, "'Composite'"); + InstructionDesc[OpCompositeInsert].operands.push(OperandVariableLiterals, "'Indexes'"); + + InstructionDesc[OpCopyObject].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpCopyMemory].operands.push(OperandId, "'Target'"); + InstructionDesc[OpCopyMemory].operands.push(OperandId, "'Source'"); + InstructionDesc[OpCopyMemory].operands.push(OperandMemoryAccess, "", true); + + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Target'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Source'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandId, "'Size'"); + InstructionDesc[OpCopyMemorySized].operands.push(OperandMemoryAccess, "", true); + + InstructionDesc[OpSampledImage].operands.push(OperandId, "'Image'"); + InstructionDesc[OpSampledImage].operands.push(OperandId, "'Sampler'"); + + InstructionDesc[OpImage].operands.push(OperandId, "'Sampled Image'"); + + InstructionDesc[OpImageRead].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageRead].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageRead].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageRead].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageWrite].operands.push(OperandId, "'Texel'"); + InstructionDesc[OpImageWrite].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageWrite].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleProjDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageFetch].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageFetch].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageFetch].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageFetch].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageGather].operands.push(OperandId, "'Component'"); + InstructionDesc[OpImageGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageDrefGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageDrefGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjDrefImplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseSampleProjDrefExplicitLod].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseFetch].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageSparseFetch].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseFetch].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseFetch].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandId, "'Component'"); + InstructionDesc[OpImageSparseGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandId, "'D~ref~'"); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseDrefGather].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseRead].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageSparseRead].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSparseRead].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSparseRead].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpImageSparseTexelsResident].operands.push(OperandId, "'Resident Code'"); + + InstructionDesc[OpImageQuerySizeLod].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageQuerySizeLod].operands.push(OperandId, "'Level of Detail'"); + + InstructionDesc[OpImageQuerySize].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryLod].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageQueryLod].operands.push(OperandId, "'Coordinate'"); + + InstructionDesc[OpImageQueryLevels].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQuerySamples].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryFormat].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpImageQueryOrder].operands.push(OperandId, "'Image'"); + + InstructionDesc[OpAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpInBoundsAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpInBoundsAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpPtrAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpPtrAccessChain].operands.push(OperandId, "'Element'"); + InstructionDesc[OpPtrAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandId, "'Base'"); + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandId, "'Element'"); + InstructionDesc[OpInBoundsPtrAccessChain].operands.push(OperandVariableIds, "'Indexes'"); + + InstructionDesc[OpSNegate].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpFNegate].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpNot].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpAny].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpAll].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpConvertFToU].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpConvertFToS].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpConvertSToF].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpConvertUToF].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpUConvert].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpSConvert].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpFConvert].operands.push(OperandId, "'Float Value'"); + + InstructionDesc[OpSatConvertSToU].operands.push(OperandId, "'Signed Value'"); + + InstructionDesc[OpSatConvertUToS].operands.push(OperandId, "'Unsigned Value'"); + + InstructionDesc[OpConvertPtrToU].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpConvertUToPtr].operands.push(OperandId, "'Integer Value'"); + + InstructionDesc[OpPtrCastToGeneric].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpGenericCastToPtr].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpGenericCastToPtrExplicit].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpGenericCastToPtrExplicit].operands.push(OperandStorage, "'Storage'"); + + InstructionDesc[OpGenericPtrMemSemantics].operands.push(OperandId, "'Pointer'"); + + InstructionDesc[OpBitcast].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpQuantizeToF16].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpTranspose].operands.push(OperandId, "'Matrix'"); + + InstructionDesc[OpCopyLogical].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpIsNan].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsInf].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsFinite].operands.push(OperandId, "'x'"); + + InstructionDesc[OpIsNormal].operands.push(OperandId, "'x'"); + + InstructionDesc[OpSignBitSet].operands.push(OperandId, "'x'"); + + InstructionDesc[OpLessOrGreater].operands.push(OperandId, "'x'"); + InstructionDesc[OpLessOrGreater].operands.push(OperandId, "'y'"); + + InstructionDesc[OpOrdered].operands.push(OperandId, "'x'"); + InstructionDesc[OpOrdered].operands.push(OperandId, "'y'"); + + InstructionDesc[OpUnordered].operands.push(OperandId, "'x'"); + InstructionDesc[OpUnordered].operands.push(OperandId, "'y'"); + + InstructionDesc[OpArrayLength].operands.push(OperandId, "'Structure'"); + InstructionDesc[OpArrayLength].operands.push(OperandLiteralNumber, "'Array member'"); + + InstructionDesc[OpIAdd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIAdd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFAdd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFAdd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpISub].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpISub].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFSub].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFSub].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpIMul].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIMul].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFMul].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFMul].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFDiv].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFDiv].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSRem].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSRem].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFRem].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFRem].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFMod].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFMod].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpVectorTimesScalar].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorTimesScalar].operands.push(OperandId, "'Scalar'"); + + InstructionDesc[OpMatrixTimesScalar].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpMatrixTimesScalar].operands.push(OperandId, "'Scalar'"); + + InstructionDesc[OpVectorTimesMatrix].operands.push(OperandId, "'Vector'"); + InstructionDesc[OpVectorTimesMatrix].operands.push(OperandId, "'Matrix'"); + + InstructionDesc[OpMatrixTimesVector].operands.push(OperandId, "'Matrix'"); + InstructionDesc[OpMatrixTimesVector].operands.push(OperandId, "'Vector'"); + + InstructionDesc[OpMatrixTimesMatrix].operands.push(OperandId, "'LeftMatrix'"); + InstructionDesc[OpMatrixTimesMatrix].operands.push(OperandId, "'RightMatrix'"); + + InstructionDesc[OpOuterProduct].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpOuterProduct].operands.push(OperandId, "'Vector 2'"); + + InstructionDesc[OpDot].operands.push(OperandId, "'Vector 1'"); + InstructionDesc[OpDot].operands.push(OperandId, "'Vector 2'"); + + InstructionDesc[OpIAddCarry].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIAddCarry].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpISubBorrow].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpISubBorrow].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUMulExtended].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUMulExtended].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSMulExtended].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSMulExtended].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpShiftRightLogical].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftRightLogical].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpShiftRightArithmetic].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftRightArithmetic].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpShiftLeftLogical].operands.push(OperandId, "'Base'"); + InstructionDesc[OpShiftLeftLogical].operands.push(OperandId, "'Shift'"); + + InstructionDesc[OpLogicalOr].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalOr].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalAnd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalAnd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpLogicalNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpLogicalNot].operands.push(OperandId, "'Operand'"); + + InstructionDesc[OpBitwiseOr].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseOr].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitwiseXor].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseXor].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitwiseAnd].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpBitwiseAnd].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Insert'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldInsert].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldSExtract].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Base'"); + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Offset'"); + InstructionDesc[OpBitFieldUExtract].operands.push(OperandId, "'Count'"); + + InstructionDesc[OpBitReverse].operands.push(OperandId, "'Base'"); + + InstructionDesc[OpBitCount].operands.push(OperandId, "'Base'"); + + InstructionDesc[OpSelect].operands.push(OperandId, "'Condition'"); + InstructionDesc[OpSelect].operands.push(OperandId, "'Object 1'"); + InstructionDesc[OpSelect].operands.push(OperandId, "'Object 2'"); + + InstructionDesc[OpIEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpIEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpINotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpINotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordNotEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordNotEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpULessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpULessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordLessThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordLessThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordGreaterThan].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordGreaterThan].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpULessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpULessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordLessThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordLessThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpUGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpUGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpSGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpSGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFOrdGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFOrdGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpFUnordGreaterThanEqual].operands.push(OperandId, "'Operand 1'"); + InstructionDesc[OpFUnordGreaterThanEqual].operands.push(OperandId, "'Operand 2'"); + + InstructionDesc[OpDPdx].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdy].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidth].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdxFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdyFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidthFine].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdxCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpDPdyCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpFwidthCoarse].operands.push(OperandId, "'P'"); + + InstructionDesc[OpEmitStreamVertex].operands.push(OperandId, "'Stream'"); + + InstructionDesc[OpEndStreamPrimitive].operands.push(OperandId, "'Stream'"); + + InstructionDesc[OpControlBarrier].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpControlBarrier].operands.push(OperandScope, "'Memory'"); + InstructionDesc[OpControlBarrier].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpMemoryBarrier].operands.push(OperandScope, "'Memory'"); + InstructionDesc[OpMemoryBarrier].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Image'"); + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageTexelPointer].operands.push(OperandId, "'Sample'"); + + InstructionDesc[OpAtomicLoad].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicLoad].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicLoad].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicStore].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicStore].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicStore].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicStore].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicExchange].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicExchange].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandMemorySemantics, "'Equal'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandMemorySemantics, "'Unequal'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Value'"); + InstructionDesc[OpAtomicCompareExchange].operands.push(OperandId, "'Comparator'"); + + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandMemorySemantics, "'Equal'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandMemorySemantics, "'Unequal'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Value'"); + InstructionDesc[OpAtomicCompareExchangeWeak].operands.push(OperandId, "'Comparator'"); + + InstructionDesc[OpAtomicIIncrement].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIIncrement].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIIncrement].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicIDecrement].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIDecrement].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIDecrement].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicIAdd].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicIAdd].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicFAddEXT].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFAddEXT].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFAddEXT].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicFAddEXT].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicISub].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicISub].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicISub].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicISub].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicUMin].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicUMin].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicUMax].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicUMax].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicSMin].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicSMin].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicSMax].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicSMax].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicAnd].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicAnd].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicOr].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicOr].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicOr].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicOr].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicXor].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicXor].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicXor].operands.push(OperandMemorySemantics, "'Semantics'"); + InstructionDesc[OpAtomicXor].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFlagTestAndSet].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpAtomicFlagClear].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpAtomicFlagClear].operands.push(OperandScope, "'Scope'"); + InstructionDesc[OpAtomicFlagClear].operands.push(OperandMemorySemantics, "'Semantics'"); + + InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Merge Block'"); + InstructionDesc[OpLoopMerge].operands.push(OperandId, "'Continue Target'"); + InstructionDesc[OpLoopMerge].operands.push(OperandLoop, ""); + InstructionDesc[OpLoopMerge].operands.push(OperandOptionalLiteral, ""); + + InstructionDesc[OpSelectionMerge].operands.push(OperandId, "'Merge Block'"); + InstructionDesc[OpSelectionMerge].operands.push(OperandSelect, ""); + + InstructionDesc[OpBranch].operands.push(OperandId, "'Target Label'"); + + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'Condition'"); + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'True Label'"); + InstructionDesc[OpBranchConditional].operands.push(OperandId, "'False Label'"); + InstructionDesc[OpBranchConditional].operands.push(OperandVariableLiterals, "'Branch weights'"); + + InstructionDesc[OpSwitch].operands.push(OperandId, "'Selector'"); + InstructionDesc[OpSwitch].operands.push(OperandId, "'Default'"); + InstructionDesc[OpSwitch].operands.push(OperandVariableLiteralId, "'Target'"); + + + InstructionDesc[OpReturnValue].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpLifetimeStart].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLifetimeStart].operands.push(OperandLiteralNumber, "'Size'"); + + InstructionDesc[OpLifetimeStop].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpLifetimeStop].operands.push(OperandLiteralNumber, "'Size'"); + + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Destination'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Source'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Num Elements'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpGroupAsyncCopy].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpGroupWaitEvents].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupWaitEvents].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpGroupWaitEvents].operands.push(OperandId, "'Events List'"); + + InstructionDesc[OpGroupAll].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAll].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpGroupAny].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupAny].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpGroupBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupBroadcast].operands.push(OperandId, "'Value'"); + InstructionDesc[OpGroupBroadcast].operands.push(OperandId, "'LocalId'"); + + InstructionDesc[OpGroupIAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupIAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupIAdd].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupFAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFAdd].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupUMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMin].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupSMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMin].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMin].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupUMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMax].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupSMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMax].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMax].operands.push(OperandId, "X"); + + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Index'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReservedReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Index'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReservedWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReserveReadPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpReserveWritePipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpCommitReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpCommitWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpIsValidReserveId].operands.push(OperandId, "'Reserve Id'"); + + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGetNumPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGetMaxPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupReserveReadPipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Num Packets'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupReserveWritePipePackets].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupCommitReadPipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Pipe'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Reserve Id'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Packet Size'"); + InstructionDesc[OpGroupCommitWritePipe].operands.push(OperandId, "'Packet Alignment'"); + + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'GlobalWorkSize'"); + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'LocalWorkSize'"); + InstructionDesc[OpBuildNDRange].operands.push(OperandId, "'GlobalWorkOffset'"); + + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Event'"); + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Profiling Info'"); + InstructionDesc[OpCaptureEventProfilingInfo].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpSetUserEventStatus].operands.push(OperandId, "'Event'"); + InstructionDesc[OpSetUserEventStatus].operands.push(OperandId, "'Status'"); + + InstructionDesc[OpIsValidEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpRetainEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpReleaseEvent].operands.push(OperandId, "'Event'"); + + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelWorkGroupSize].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelPreferredWorkGroupSizeMultiple].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelNDrangeSubGroupCount].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpGetKernelNDrangeMaxSubGroupSize].operands.push(OperandId, "'Param Align'"); + + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Queue'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Flags'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'ND Range'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Wait Events'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Ret Event'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Invoke'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param Size'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandId, "'Param Align'"); + InstructionDesc[OpEnqueueKernel].operands.push(OperandVariableIds, "'Local Size'"); + + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Queue'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Num Events'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Wait Events'"); + InstructionDesc[OpEnqueueMarker].operands.push(OperandId, "'Ret Event'"); + + InstructionDesc[OpGroupNonUniformElect].operands.push(OperandScope, "'Execution'"); + + InstructionDesc[OpGroupNonUniformAll].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAll].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformAny].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAny].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformAllEqual].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformAllEqual].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBroadcast].operands.push(OperandId, "ID"); + + InstructionDesc[OpGroupNonUniformBroadcastFirst].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBroadcastFirst].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallot].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallot].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformInverseBallot].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformInverseBallot].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBallotBitExtract].operands.push(OperandId, "Bit"); + + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBallotBitCount].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotFindLSB].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotFindLSB].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformBallotFindMSB].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBallotFindMSB].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffle].operands.push(OperandId, "'Id'"); + + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleXor].operands.push(OperandId, "Mask"); + + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleUp].operands.push(OperandId, "Offset"); + + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformShuffleDown].operands.push(OperandId, "Offset"); + + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformIAdd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFAdd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformIMul].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMul].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformSMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformUMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMin].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformSMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformUMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformFMax].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseAnd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseOr].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformBitwiseXor].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalAnd].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalOr].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformLogicalXor].operands.push(OperandId, "'ClusterSize'", true); + + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformQuadBroadcast].operands.push(OperandId, "'Id'"); + + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandId, "X"); + InstructionDesc[OpGroupNonUniformQuadSwap].operands.push(OperandLiteralNumber, "'Direction'"); + + InstructionDesc[OpSubgroupBallotKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupFirstInvocationKHR].operands.push(OperandId, "'Value'"); + + InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAnyKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupAllKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAllKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpSubgroupAllEqualKHR].operands.push(OperandId, "'Predicate'"); + + InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Value'"); + InstructionDesc[OpSubgroupReadInvocationKHR].operands.push(OperandId, "'Index'"); + + InstructionDesc[OpModuleProcessed].operands.push(OperandLiteralString, "'process'"); + + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupIAddNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFAddNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMinNonUniformAMD].operands.push(OperandId, "'X'"); + + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMinNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMinNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupUMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupSMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandScope, "'Execution'"); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandGroupOperation, "'Operation'"); + InstructionDesc[OpGroupFMaxNonUniformAMD].operands.push(OperandId, "X"); + + InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Image'"); + InstructionDesc[OpFragmentMaskFetchAMD].operands.push(OperandId, "'Coordinate'"); + + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Image'"); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpFragmentFetchAMD].operands.push(OperandId, "'Fragment Index'"); + + InstructionDesc[OpGroupNonUniformPartitionNV].operands.push(OperandId, "X"); + + InstructionDesc[OpTypeAccelerationStructureKHR].setResultAndType(true, false); + + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'NV Acceleration Structure'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Flags'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Cull Mask'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'SBT Record Offset'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'SBT Record Stride'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Miss Index'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Origin'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'TMin'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Ray Direction'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'TMax'"); + InstructionDesc[OpTraceRayKHR].operands.push(OperandId, "'Payload'"); + InstructionDesc[OpTraceRayKHR].setResultAndType(false, false); + + InstructionDesc[OpReportIntersectionKHR].operands.push(OperandId, "'Hit Parameter'"); + InstructionDesc[OpReportIntersectionKHR].operands.push(OperandId, "'Hit Kind'"); + + InstructionDesc[OpIgnoreIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpTerminateRayKHR].setResultAndType(false, false); + + InstructionDesc[OpExecuteCallableKHR].operands.push(OperandId, "SBT Record Index"); + InstructionDesc[OpExecuteCallableKHR].operands.push(OperandId, "CallableData ID"); + InstructionDesc[OpExecuteCallableKHR].setResultAndType(false, false); + + // Ray Query + InstructionDesc[OpTypeAccelerationStructureKHR].setResultAndType(true, false); + InstructionDesc[OpTypeRayQueryProvisionalKHR].setResultAndType(true, false); + + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'AccelerationS'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'RayFlags'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'CullMask'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Origin'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Tmin'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Direction'"); + InstructionDesc[OpRayQueryInitializeKHR].operands.push(OperandId, "'Tmax'"); + InstructionDesc[OpRayQueryInitializeKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryTerminateKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryTerminateKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryGenerateIntersectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGenerateIntersectionKHR].operands.push(OperandId, "'THit'"); + InstructionDesc[OpRayQueryGenerateIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryConfirmIntersectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryConfirmIntersectionKHR].setResultAndType(false, false); + + InstructionDesc[OpRayQueryProceedKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryProceedKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionTypeKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetRayTMinKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetRayTMinKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetRayFlagsKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetRayFlagsKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionTKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionTKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionTKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceCustomIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceIdKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionGeometryIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionPrimitiveIndexKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionBarycentricsKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionFrontFaceKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionCandidateAABBOpaqueKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionCandidateAABBOpaqueKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayDirectionKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectRayOriginKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetWorldRayDirectionKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetWorldRayDirectionKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetWorldRayOriginKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetWorldRayOriginKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionObjectToWorldKHR].setResultAndType(true, true); + + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].operands.push(OperandId, "'RayQuery'"); + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].operands.push(OperandId, "'Committed'"); + InstructionDesc[OpRayQueryGetIntersectionWorldToObjectKHR].setResultAndType(true, true); + + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Sampled Image'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Coordinate'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Granularity'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandId, "'Coarse'"); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandImageOperands, "", true); + InstructionDesc[OpImageSampleFootprintNV].operands.push(OperandVariableIds, "", true); + + InstructionDesc[OpWritePackedPrimitiveIndices4x8NV].operands.push(OperandId, "'Index Offset'"); + InstructionDesc[OpWritePackedPrimitiveIndices4x8NV].operands.push(OperandId, "'Packed Indices'"); + + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Component Type'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Scope'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Rows'"); + InstructionDesc[OpTypeCooperativeMatrixNV].operands.push(OperandId, "'Columns'"); + + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "'Column Major'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeMatrixLoadNV].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Pointer'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Object'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Stride'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "'Column Major'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandMemoryAccess, "'Memory Access'"); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandLiteralNumber, "", true); + InstructionDesc[OpCooperativeMatrixStoreNV].operands.push(OperandId, "", true); + + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'A'"); + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'B'"); + InstructionDesc[OpCooperativeMatrixMulAddNV].operands.push(OperandId, "'C'"); + + InstructionDesc[OpCooperativeMatrixLengthNV].operands.push(OperandId, "'Type'"); + + InstructionDesc[OpDemoteToHelperInvocationEXT].setResultAndType(false, false); + + InstructionDesc[OpReadClockKHR].operands.push(OperandScope, "'Scope'"); +} + +}; // end spv namespace diff --git a/third_party/glslang/SPIRV/doc.h b/third_party/glslang/SPIRV/doc.h new file mode 100644 index 0000000..2a0b28c --- /dev/null +++ b/third_party/glslang/SPIRV/doc.h @@ -0,0 +1,259 @@ +// +// Copyright (C) 2014-2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Parameterize the SPIR-V enumerants. +// + +#pragma once + +#include "spirv.hpp" + +#include + +namespace spv { + +// Fill in all the parameters +void Parameterize(); + +// Return the English names of all the enums. +const char* SourceString(int); +const char* AddressingString(int); +const char* MemoryString(int); +const char* ExecutionModelString(int); +const char* ExecutionModeString(int); +const char* StorageClassString(int); +const char* DecorationString(int); +const char* BuiltInString(int); +const char* DimensionString(int); +const char* SelectControlString(int); +const char* LoopControlString(int); +const char* FunctionControlString(int); +const char* SamplerAddressingModeString(int); +const char* SamplerFilterModeString(int); +const char* ImageFormatString(int); +const char* ImageChannelOrderString(int); +const char* ImageChannelTypeString(int); +const char* ImageChannelDataTypeString(int type); +const char* ImageOperandsString(int format); +const char* ImageOperands(int); +const char* FPFastMathString(int); +const char* FPRoundingModeString(int); +const char* LinkageTypeString(int); +const char* FuncParamAttrString(int); +const char* AccessQualifierString(int); +const char* MemorySemanticsString(int); +const char* MemoryAccessString(int); +const char* ExecutionScopeString(int); +const char* GroupOperationString(int); +const char* KernelEnqueueFlagsString(int); +const char* KernelProfilingInfoString(int); +const char* CapabilityString(int); +const char* OpcodeString(int); +const char* ScopeString(int mem); + +// For grouping opcodes into subsections +enum OpcodeClass { + OpClassMisc, + OpClassDebug, + OpClassAnnotate, + OpClassExtension, + OpClassMode, + OpClassType, + OpClassConstant, + OpClassMemory, + OpClassFunction, + OpClassImage, + OpClassConvert, + OpClassComposite, + OpClassArithmetic, + OpClassBit, + OpClassRelationalLogical, + OpClassDerivative, + OpClassFlowControl, + OpClassAtomic, + OpClassPrimitive, + OpClassBarrier, + OpClassGroup, + OpClassDeviceSideEnqueue, + OpClassPipe, + + OpClassCount, + OpClassMissing // all instructions start out as missing +}; + +// For parameterizing operands. +enum OperandClass { + OperandNone, + OperandId, + OperandVariableIds, + OperandOptionalLiteral, + OperandOptionalLiteralString, + OperandVariableLiterals, + OperandVariableIdLiteral, + OperandVariableLiteralId, + OperandLiteralNumber, + OperandLiteralString, + OperandVariableLiteralStrings, + OperandSource, + OperandExecutionModel, + OperandAddressing, + OperandMemory, + OperandExecutionMode, + OperandStorage, + OperandDimensionality, + OperandSamplerAddressingMode, + OperandSamplerFilterMode, + OperandSamplerImageFormat, + OperandImageChannelOrder, + OperandImageChannelDataType, + OperandImageOperands, + OperandFPFastMath, + OperandFPRoundingMode, + OperandLinkageType, + OperandAccessQualifier, + OperandFuncParamAttr, + OperandDecoration, + OperandBuiltIn, + OperandSelect, + OperandLoop, + OperandFunction, + OperandMemorySemantics, + OperandMemoryAccess, + OperandScope, + OperandGroupOperation, + OperandKernelEnqueueFlags, + OperandKernelProfilingInfo, + OperandCapability, + + OperandOpcode, + + OperandCount +}; + +// Any specific enum can have a set of capabilities that allow it: +typedef std::vector EnumCaps; + +// Parameterize a set of operands with their OperandClass(es) and descriptions. +class OperandParameters { +public: + OperandParameters() { } + void push(OperandClass oc, const char* d, bool opt = false) + { + opClass.push_back(oc); + desc.push_back(d); + optional.push_back(opt); + } + void setOptional(); + OperandClass getClass(int op) const { return opClass[op]; } + const char* getDesc(int op) const { return desc[op]; } + bool isOptional(int op) const { return optional[op]; } + int getNum() const { return (int)opClass.size(); } + +protected: + std::vector opClass; + std::vector desc; + std::vector optional; +}; + +// Parameterize an enumerant +class EnumParameters { +public: + EnumParameters() : desc(0) { } + const char* desc; +}; + +// Parameterize a set of enumerants that form an enum +class EnumDefinition : public EnumParameters { +public: + EnumDefinition() : + ceiling(0), bitmask(false), getName(0), enumParams(0), operandParams(0) { } + void set(int ceil, const char* (*name)(int), EnumParameters* ep, bool mask = false) + { + ceiling = ceil; + getName = name; + bitmask = mask; + enumParams = ep; + } + void setOperands(OperandParameters* op) { operandParams = op; } + int ceiling; // ceiling of enumerants + bool bitmask; // true if these enumerants combine into a bitmask + const char* (*getName)(int); // a function that returns the name for each enumerant value (or shift) + EnumParameters* enumParams; // parameters for each individual enumerant + OperandParameters* operandParams; // sets of operands +}; + +// Parameterize an instruction's logical format, including its known set of operands, +// per OperandParameters above. +class InstructionParameters { +public: + InstructionParameters() : + opDesc("TBD"), + opClass(OpClassMissing), + typePresent(true), // most normal, only exceptions have to be spelled out + resultPresent(true) // most normal, only exceptions have to be spelled out + { } + + void setResultAndType(bool r, bool t) + { + resultPresent = r; + typePresent = t; + } + + bool hasResult() const { return resultPresent != 0; } + bool hasType() const { return typePresent != 0; } + + const char* opDesc; + OpcodeClass opClass; + OperandParameters operands; + +protected: + int typePresent : 1; + int resultPresent : 1; +}; + +// The set of objects that hold all the instruction/operand +// parameterization information. +extern InstructionParameters InstructionDesc[]; + +// These hold definitions of the enumerants used for operands +extern EnumDefinition OperandClassParams[]; + +const char* GetOperandDesc(OperandClass operand); +void PrintImmediateRow(int imm, const char* name, const EnumParameters* enumParams, bool caps, bool hex = false); +const char* AccessQualifierString(int attr); + +void PrintOperands(const OperandParameters& operands, int reservedOperands); + +} // end namespace spv diff --git a/third_party/glslang/SPIRV/hex_float.h b/third_party/glslang/SPIRV/hex_float.h new file mode 100644 index 0000000..8be8e9f --- /dev/null +++ b/third_party/glslang/SPIRV/hex_float.h @@ -0,0 +1,1078 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_UTIL_HEX_FLOAT_H_ +#define LIBSPIRV_UTIL_HEX_FLOAT_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1800 +namespace std { +bool isnan(double f) +{ + return ::_isnan(f) != 0; +} +bool isinf(double f) +{ + return ::_finite(f) == 0; +} +} +#endif + +#include "bitutils.h" + +namespace spvutils { + +class Float16 { + public: + Float16(uint16_t v) : val(v) {} + Float16() {} + static bool isNan(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) != 0); + } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) == 0); + } + Float16(const Float16& other) { val = other.val; } + uint16_t get_value() const { return val; } + + // Returns the maximum normal value. + static Float16 max() { return Float16(0x7bff); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16(0xfbff); } + + private: + uint16_t val; +}; + +// To specialize this type, you must override uint_type to define +// an unsigned integer that can fit your floating point type. +// You must also add a isNan function that returns true if +// a value is Nan. +template +struct FloatProxyTraits { + typedef void uint_type; +}; + +template <> +struct FloatProxyTraits { + typedef uint32_t uint_type; + static bool isNan(float f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(float f) { return std::isinf(f); } + // Returns the maximum normal value. + static float max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static float lowest() { return std::numeric_limits::lowest(); } +}; + +template <> +struct FloatProxyTraits { + typedef uint64_t uint_type; + static bool isNan(double f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(double f) { return std::isinf(f); } + // Returns the maximum normal value. + static double max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static double lowest() { return std::numeric_limits::lowest(); } +}; + +template <> +struct FloatProxyTraits { + typedef uint16_t uint_type; + static bool isNan(Float16 f) { return Float16::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(Float16 f) { return Float16::isInfinity(f); } + // Returns the maximum normal value. + static Float16 max() { return Float16::max(); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16::lowest(); } +}; + +// Since copying a floating point number (especially if it is NaN) +// does not guarantee that bits are preserved, this class lets us +// store the type and use it as a float when necessary. +template +class FloatProxy { + public: + typedef typename FloatProxyTraits::uint_type uint_type; + + // Since this is to act similar to the normal floats, + // do not initialize the data by default. + FloatProxy() {} + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(T val) { data_ = BitwiseCast(val); } + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(uint_type val) { data_ = val; } + + // This is helpful to have and is guaranteed not to stomp bits. + FloatProxy operator-() const { + return static_cast(data_ ^ + (uint_type(0x1) << (sizeof(T) * 8 - 1))); + } + + // Returns the data as a floating point value. + T getAsFloat() const { return BitwiseCast(data_); } + + // Returns the raw data. + uint_type data() const { return data_; } + + // Returns true if the value represents any type of NaN. + bool isNan() { return FloatProxyTraits::isNan(getAsFloat()); } + // Returns true if the value represents any type of infinity. + bool isInfinity() { return FloatProxyTraits::isInfinity(getAsFloat()); } + + // Returns the maximum normal value. + static FloatProxy max() { + return FloatProxy(FloatProxyTraits::max()); + } + // Returns the lowest normal value. + static FloatProxy lowest() { + return FloatProxy(FloatProxyTraits::lowest()); + } + + private: + uint_type data_; +}; + +template +bool operator==(const FloatProxy& first, const FloatProxy& second) { + return first.data() == second.data(); +} + +// Reads a FloatProxy value as a normal float from a stream. +template +std::istream& operator>>(std::istream& is, FloatProxy& value) { + T float_val; + is >> float_val; + value = FloatProxy(float_val); + return is; +} + +// This is an example traits. It is not meant to be used in practice, but will +// be the default for any non-specialized type. +template +struct HexFloatTraits { + // Integer type that can store this hex-float. + typedef void uint_type; + // Signed integer type that can store this hex-float. + typedef void int_type; + // The numerical type that this HexFloat represents. + typedef void underlying_type; + // The type needed to construct the underlying type. + typedef void native_type; + // The number of bits that are actually relevant in the uint_type. + // This allows us to deal with, for example, 24-bit values in a 32-bit + // integer. + static const uint32_t num_used_bits = 0; + // Number of bits that represent the exponent. + static const uint32_t num_exponent_bits = 0; + // Number of bits that represent the fractional part. + static const uint32_t num_fraction_bits = 0; + // The bias of the exponent. (How much we need to subtract from the stored + // value to get the correct value.) + static const uint32_t exponent_bias = 0; +}; + +// Traits for IEEE float. +// 1 sign bit, 8 exponent bits, 23 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint32_t uint_type; + typedef int32_t int_type; + typedef FloatProxy underlying_type; + typedef float native_type; + static const uint_type num_used_bits = 32; + static const uint_type num_exponent_bits = 8; + static const uint_type num_fraction_bits = 23; + static const uint_type exponent_bias = 127; +}; + +// Traits for IEEE double. +// 1 sign bit, 11 exponent bits, 52 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint64_t uint_type; + typedef int64_t int_type; + typedef FloatProxy underlying_type; + typedef double native_type; + static const uint_type num_used_bits = 64; + static const uint_type num_exponent_bits = 11; + static const uint_type num_fraction_bits = 52; + static const uint_type exponent_bias = 1023; +}; + +// Traits for IEEE half. +// 1 sign bit, 5 exponent bits, 10 fractional bits. +template <> +struct HexFloatTraits> { + typedef uint16_t uint_type; + typedef int16_t int_type; + typedef uint16_t underlying_type; + typedef uint16_t native_type; + static const uint_type num_used_bits = 16; + static const uint_type num_exponent_bits = 5; + static const uint_type num_fraction_bits = 10; + static const uint_type exponent_bias = 15; +}; + +enum round_direction { + kRoundToZero, + kRoundToNearestEven, + kRoundToPositiveInfinity, + kRoundToNegativeInfinity +}; + +// Template class that houses a floating pointer number. +// It exposes a number of constants based on the provided traits to +// assist in interpreting the bits of the value. +template > +class HexFloat { + public: + typedef typename Traits::uint_type uint_type; + typedef typename Traits::int_type int_type; + typedef typename Traits::underlying_type underlying_type; + typedef typename Traits::native_type native_type; + + explicit HexFloat(T f) : value_(f) {} + + T value() const { return value_; } + void set_value(T f) { value_ = f; } + + // These are all written like this because it is convenient to have + // compile-time constants for all of these values. + + // Pass-through values to save typing. + static const uint32_t num_used_bits = Traits::num_used_bits; + static const uint32_t exponent_bias = Traits::exponent_bias; + static const uint32_t num_exponent_bits = Traits::num_exponent_bits; + static const uint32_t num_fraction_bits = Traits::num_fraction_bits; + + // Number of bits to shift left to set the highest relevant bit. + static const uint32_t top_bit_left_shift = num_used_bits - 1; + // How many nibbles (hex characters) the fractional part takes up. + static const uint32_t fraction_nibbles = (num_fraction_bits + 3) / 4; + // If the fractional part does not fit evenly into a hex character (4-bits) + // then we have to left-shift to get rid of leading 0s. This is the amount + // we have to shift (might be 0). + static const uint32_t num_overflow_bits = + fraction_nibbles * 4 - num_fraction_bits; + + // The representation of the fraction, not the actual bits. This + // includes the leading bit that is usually implicit. + static const uint_type fraction_represent_mask = + spvutils::SetBits::get; + + // The topmost bit in the nibble-aligned fraction. + static const uint_type fraction_top_bit = + uint_type(1) << (num_fraction_bits + num_overflow_bits - 1); + + // The least significant bit in the exponent, which is also the bit + // immediately to the left of the significand. + static const uint_type first_exponent_bit = uint_type(1) + << (num_fraction_bits); + + // The mask for the encoded fraction. It does not include the + // implicit bit. + static const uint_type fraction_encode_mask = + spvutils::SetBits::get; + + // The bit that is used as a sign. + static const uint_type sign_mask = uint_type(1) << top_bit_left_shift; + + // The bits that represent the exponent. + static const uint_type exponent_mask = + spvutils::SetBits::get; + + // How far left the exponent is shifted. + static const uint32_t exponent_left_shift = num_fraction_bits; + + // How far from the right edge the fraction is shifted. + static const uint32_t fraction_right_shift = + static_cast(sizeof(uint_type) * 8) - num_fraction_bits; + + // The maximum representable unbiased exponent. + static const int_type max_exponent = + (exponent_mask >> num_fraction_bits) - exponent_bias; + // The minimum representable exponent for normalized numbers. + static const int_type min_exponent = -static_cast(exponent_bias); + + // Returns the bits associated with the value. + uint_type getBits() const { return spvutils::BitwiseCast(value_); } + + // Returns the bits associated with the value, without the leading sign bit. + uint_type getUnsignedBits() const { + return static_cast(spvutils::BitwiseCast(value_) & + ~sign_mask); + } + + // Returns the bits associated with the exponent, shifted to start at the + // lsb of the type. + const uint_type getExponentBits() const { + return static_cast((getBits() & exponent_mask) >> + num_fraction_bits); + } + + // Returns the exponent in unbiased form. This is the exponent in the + // human-friendly form. + const int_type getUnbiasedExponent() const { + return static_cast(getExponentBits() - exponent_bias); + } + + // Returns just the significand bits from the value. + const uint_type getSignificandBits() const { + return getBits() & fraction_encode_mask; + } + + // If the number was normalized, returns the unbiased exponent. + // If the number was denormal, normalize the exponent first. + const int_type getUnbiasedNormalizedExponent() const { + if ((getBits() & ~sign_mask) == 0) { // special case if everything is 0 + return 0; + } + int_type exp = getUnbiasedExponent(); + if (exp == min_exponent) { // We are in denorm land. + uint_type significand_bits = getSignificandBits(); + while ((significand_bits & (first_exponent_bit >> 1)) == 0) { + significand_bits = static_cast(significand_bits << 1); + exp = static_cast(exp - 1); + } + significand_bits &= fraction_encode_mask; + } + return exp; + } + + // Returns the signficand after it has been normalized. + const uint_type getNormalizedSignificand() const { + int_type unbiased_exponent = getUnbiasedNormalizedExponent(); + uint_type significand = getSignificandBits(); + for (int_type i = unbiased_exponent; i <= min_exponent; ++i) { + significand = static_cast(significand << 1); + } + significand &= fraction_encode_mask; + return significand; + } + + // Returns true if this number represents a negative value. + bool isNegative() const { return (getBits() & sign_mask) != 0; } + + // Sets this HexFloat from the individual components. + // Note this assumes EVERY significand is normalized, and has an implicit + // leading one. This means that the only way that this method will set 0, + // is if you set a number so denormalized that it underflows. + // Do not use this method with raw bits extracted from a subnormal number, + // since subnormals do not have an implicit leading 1 in the significand. + // The significand is also expected to be in the + // lowest-most num_fraction_bits of the uint_type. + // The exponent is expected to be unbiased, meaning an exponent of + // 0 actually means 0. + // If underflow_round_up is set, then on underflow, if a number is non-0 + // and would underflow, we round up to the smallest denorm. + void setFromSignUnbiasedExponentAndNormalizedSignificand( + bool negative, int_type exponent, uint_type significand, + bool round_denorm_up) { + bool significand_is_zero = significand == 0; + + if (exponent <= min_exponent) { + // If this was denormalized, then we have to shift the bit on, meaning + // the significand is not zero. + significand_is_zero = false; + significand |= first_exponent_bit; + significand = static_cast(significand >> 1); + } + + while (exponent < min_exponent) { + significand = static_cast(significand >> 1); + ++exponent; + } + + if (exponent == min_exponent) { + if (significand == 0 && !significand_is_zero && round_denorm_up) { + significand = static_cast(0x1); + } + } + + uint_type new_value = 0; + if (negative) { + new_value = static_cast(new_value | sign_mask); + } + exponent = static_cast(exponent + exponent_bias); + assert(exponent >= 0); + + // put it all together + exponent = static_cast((exponent << exponent_left_shift) & + exponent_mask); + significand = static_cast(significand & fraction_encode_mask); + new_value = static_cast(new_value | (exponent | significand)); + value_ = BitwiseCast(new_value); + } + + // Increments the significand of this number by the given amount. + // If this would spill the significand into the implicit bit, + // carry is set to true and the significand is shifted to fit into + // the correct location, otherwise carry is set to false. + // All significands and to_increment are assumed to be within the bounds + // for a valid significand. + static uint_type incrementSignificand(uint_type significand, + uint_type to_increment, bool* carry) { + significand = static_cast(significand + to_increment); + *carry = false; + if (significand & first_exponent_bit) { + *carry = true; + // The implicit 1-bit will have carried, so we should zero-out the + // top bit and shift back. + significand = static_cast(significand & ~first_exponent_bit); + significand = static_cast(significand >> 1); + } + return significand; + } + + // These exist because MSVC throws warnings on negative right-shifts + // even if they are not going to be executed. Eg: + // constant_number < 0? 0: constant_number + // These convert the negative left-shifts into right shifts. + + template + uint_type negatable_left_shift(int_type N, uint_type val) + { + if(N >= 0) + return val << N; + + return val >> -N; + } + + template + uint_type negatable_right_shift(int_type N, uint_type val) + { + if(N >= 0) + return val >> N; + + return val << -N; + } + + // Returns the significand, rounded to fit in a significand in + // other_T. This is shifted so that the most significant + // bit of the rounded number lines up with the most significant bit + // of the returned significand. + template + typename other_T::uint_type getRoundedNormalizedSignificand( + round_direction dir, bool* carry_bit) { + typedef typename other_T::uint_type other_uint_type; + static const int_type num_throwaway_bits = + static_cast(num_fraction_bits) - + static_cast(other_T::num_fraction_bits); + + static const uint_type last_significant_bit = + (num_throwaway_bits < 0) + ? 0 + : negatable_left_shift(num_throwaway_bits, 1u); + static const uint_type first_rounded_bit = + (num_throwaway_bits < 1) + ? 0 + : negatable_left_shift(num_throwaway_bits - 1, 1u); + + static const uint_type throwaway_mask_bits = + num_throwaway_bits > 0 ? num_throwaway_bits : 0; + static const uint_type throwaway_mask = + spvutils::SetBits::get; + + *carry_bit = false; + other_uint_type out_val = 0; + uint_type significand = getNormalizedSignificand(); + // If we are up-casting, then we just have to shift to the right location. + if (num_throwaway_bits <= 0) { + out_val = static_cast(significand); + uint_type shift_amount = static_cast(-num_throwaway_bits); + out_val = static_cast(out_val << shift_amount); + return out_val; + } + + // If every non-representable bit is 0, then we don't have any casting to + // do. + if ((significand & throwaway_mask) == 0) { + return static_cast( + negatable_right_shift(num_throwaway_bits, significand)); + } + + bool round_away_from_zero = false; + // We actually have to narrow the significand here, so we have to follow the + // rounding rules. + switch (dir) { + case kRoundToZero: + break; + case kRoundToPositiveInfinity: + round_away_from_zero = !isNegative(); + break; + case kRoundToNegativeInfinity: + round_away_from_zero = isNegative(); + break; + case kRoundToNearestEven: + // Have to round down, round bit is 0 + if ((first_rounded_bit & significand) == 0) { + break; + } + if (((significand & throwaway_mask) & ~first_rounded_bit) != 0) { + // If any subsequent bit of the rounded portion is non-0 then we round + // up. + round_away_from_zero = true; + break; + } + // We are exactly half-way between 2 numbers, pick even. + if ((significand & last_significant_bit) != 0) { + // 1 for our last bit, round up. + round_away_from_zero = true; + break; + } + break; + } + + if (round_away_from_zero) { + return static_cast( + negatable_right_shift(num_throwaway_bits, incrementSignificand( + significand, last_significant_bit, carry_bit))); + } else { + return static_cast( + negatable_right_shift(num_throwaway_bits, significand)); + } + } + + // Casts this value to another HexFloat. If the cast is widening, + // then round_dir is ignored. If the cast is narrowing, then + // the result is rounded in the direction specified. + // This number will retain Nan and Inf values. + // It will also saturate to Inf if the number overflows, and + // underflow to (0 or min depending on rounding) if the number underflows. + template + void castTo(other_T& other, round_direction round_dir) { + other = other_T(static_cast(0)); + bool negate = isNegative(); + if (getUnsignedBits() == 0) { + if (negate) { + other.set_value(-other.value()); + } + return; + } + uint_type significand = getSignificandBits(); + bool carried = false; + typename other_T::uint_type rounded_significand = + getRoundedNormalizedSignificand(round_dir, &carried); + + int_type exponent = getUnbiasedExponent(); + if (exponent == min_exponent) { + // If we are denormal, normalize the exponent, so that we can encode + // easily. + exponent = static_cast(exponent + 1); + for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0; + check_bit = static_cast(check_bit >> 1)) { + exponent = static_cast(exponent - 1); + if (check_bit & significand) break; + } + } + + bool is_nan = + (getBits() & exponent_mask) == exponent_mask && significand != 0; + bool is_inf = + !is_nan && + ((exponent + carried) > static_cast(other_T::exponent_bias) || + (significand == 0 && (getBits() & exponent_mask) == exponent_mask)); + + // If we are Nan or Inf we should pass that through. + if (is_inf) { + other.set_value(BitwiseCast( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask))); + return; + } + if (is_nan) { + typename other_T::uint_type shifted_significand; + shifted_significand = static_cast( + negatable_left_shift( + static_cast(other_T::num_fraction_bits) - + static_cast(num_fraction_bits), significand)); + + // We are some sort of Nan. We try to keep the bit-pattern of the Nan + // as close as possible. If we had to shift off bits so we are 0, then we + // just set the last bit. + other.set_value(BitwiseCast( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask | + (shifted_significand == 0 ? 0x1 : shifted_significand)))); + return; + } + + bool round_underflow_up = + isNegative() ? round_dir == kRoundToNegativeInfinity + : round_dir == kRoundToPositiveInfinity; + typedef typename other_T::int_type other_int_type; + // setFromSignUnbiasedExponentAndNormalizedSignificand will + // zero out any underflowing value (but retain the sign). + other.setFromSignUnbiasedExponentAndNormalizedSignificand( + negate, static_cast(exponent), rounded_significand, + round_underflow_up); + return; + } + + private: + T value_; + + static_assert(num_used_bits == + Traits::num_exponent_bits + Traits::num_fraction_bits + 1, + "The number of bits do not fit"); + static_assert(sizeof(T) == sizeof(uint_type), "The type sizes do not match"); +}; + +// Returns 4 bits represented by the hex character. +inline uint8_t get_nibble_from_character(int character) { + const char* dec = "0123456789"; + const char* lower = "abcdef"; + const char* upper = "ABCDEF"; + const char* p = nullptr; + if ((p = strchr(dec, character))) { + return static_cast(p - dec); + } else if ((p = strchr(lower, character))) { + return static_cast(p - lower + 0xa); + } else if ((p = strchr(upper, character))) { + return static_cast(p - upper + 0xa); + } + + assert(false && "This was called with a non-hex character"); + return 0; +} + +// Outputs the given HexFloat to the stream. +template +std::ostream& operator<<(std::ostream& os, const HexFloat& value) { + typedef HexFloat HF; + typedef typename HF::uint_type uint_type; + typedef typename HF::int_type int_type; + + static_assert(HF::num_used_bits != 0, + "num_used_bits must be non-zero for a valid float"); + static_assert(HF::num_exponent_bits != 0, + "num_exponent_bits must be non-zero for a valid float"); + static_assert(HF::num_fraction_bits != 0, + "num_fractin_bits must be non-zero for a valid float"); + + const uint_type bits = spvutils::BitwiseCast(value.value()); + const char* const sign = (bits & HF::sign_mask) ? "-" : ""; + const uint_type exponent = static_cast( + (bits & HF::exponent_mask) >> HF::num_fraction_bits); + + uint_type fraction = static_cast((bits & HF::fraction_encode_mask) + << HF::num_overflow_bits); + + const bool is_zero = exponent == 0 && fraction == 0; + const bool is_denorm = exponent == 0 && !is_zero; + + // exponent contains the biased exponent we have to convert it back into + // the normal range. + int_type int_exponent = static_cast(exponent - HF::exponent_bias); + // If the number is all zeros, then we actually have to NOT shift the + // exponent. + int_exponent = is_zero ? 0 : int_exponent; + + // If we are denorm, then start shifting, and decreasing the exponent until + // our leading bit is 1. + + if (is_denorm) { + while ((fraction & HF::fraction_top_bit) == 0) { + fraction = static_cast(fraction << 1); + int_exponent = static_cast(int_exponent - 1); + } + // Since this is denormalized, we have to consume the leading 1 since it + // will end up being implicit. + fraction = static_cast(fraction << 1); // eat the leading 1 + fraction &= HF::fraction_represent_mask; + } + + uint_type fraction_nibbles = HF::fraction_nibbles; + // We do not have to display any trailing 0s, since this represents the + // fractional part. + while (fraction_nibbles > 0 && (fraction & 0xF) == 0) { + // Shift off any trailing values; + fraction = static_cast(fraction >> 4); + --fraction_nibbles; + } + + const auto saved_flags = os.flags(); + const auto saved_fill = os.fill(); + + os << sign << "0x" << (is_zero ? '0' : '1'); + if (fraction_nibbles) { + // Make sure to keep the leading 0s in place, since this is the fractional + // part. + os << "." << std::setw(static_cast(fraction_nibbles)) + << std::setfill('0') << std::hex << fraction; + } + os << "p" << std::dec << (int_exponent >= 0 ? "+" : "") << int_exponent; + + os.flags(saved_flags); + os.fill(saved_fill); + + return os; +} + +// Returns true if negate_value is true and the next character on the +// input stream is a plus or minus sign. In that case we also set the fail bit +// on the stream and set the value to the zero value for its type. +template +inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value, + HexFloat& value) { + if (negate_value) { + auto next_char = is.peek(); + if (next_char == '-' || next_char == '+') { + // Fail the parse. Emulate standard behaviour by setting the value to + // the zero value, and set the fail bit on the stream. + value = HexFloat(typename HexFloat::uint_type(0)); + is.setstate(std::ios_base::failbit); + return true; + } + } + return false; +} + +// Parses a floating point number from the given stream and stores it into the +// value parameter. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template +inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value, + HexFloat& value) { + if (RejectParseDueToLeadingSign(is, negate_value, value)) { + return is; + } + T val; + is >> val; + if (negate_value) { + val = -val; + } + value.set_value(val); + // In the failure case, map -0.0 to 0.0. + if (is.fail() && value.getUnsignedBits() == 0u) { + value = HexFloat(typename HexFloat::uint_type(0)); + } + if (val.isInfinity()) { + // Fail the parse. Emulate standard behaviour by setting the value to + // the closest normal value, and set the fail bit on the stream. + value.set_value((value.isNegative() || negate_value) ? T::lowest() + : T::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Specialization of ParseNormalFloat for FloatProxy values. +// This will parse the float as it were a 32-bit floating point number, +// and then round it down to fit into a Float16 value. +// The number is rounded towards zero. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template <> +inline std::istream& +ParseNormalFloat, HexFloatTraits>>( + std::istream& is, bool negate_value, + HexFloat, HexFloatTraits>>& value) { + // First parse as a 32-bit float. + HexFloat> float_val(0.0f); + ParseNormalFloat(is, negate_value, float_val); + + // Then convert to 16-bit float, saturating at infinities, and + // rounding toward zero. + float_val.castTo(value, kRoundToZero); + + // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the + // fail bit and set the lowest or highest value. + if (Float16::isInfinity(value.value().getAsFloat())) { + value.set_value(value.isNegative() ? Float16::lowest() : Float16::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Reads a HexFloat from the given stream. +// If the float is not encoded as a hex-float then it will be parsed +// as a regular float. +// This may fail if your stream does not support at least one unget. +// Nan values can be encoded with "0x1.p+exponent_bias". +// This would normally overflow a float and round to +// infinity but this special pattern is the exact representation for a NaN, +// and therefore is actually encoded as the correct NaN. To encode inf, +// either 0x0p+exponent_bias can be specified or any exponent greater than +// exponent_bias. +// Examples using IEEE 32-bit float encoding. +// 0x1.0p+128 (+inf) +// -0x1.0p-128 (-inf) +// +// 0x1.1p+128 (+Nan) +// -0x1.1p+128 (-Nan) +// +// 0x1p+129 (+inf) +// -0x1p+129 (-inf) +template +std::istream& operator>>(std::istream& is, HexFloat& value) { + using HF = HexFloat; + using uint_type = typename HF::uint_type; + using int_type = typename HF::int_type; + + value.set_value(static_cast(0.f)); + + if (is.flags() & std::ios::skipws) { + // If the user wants to skip whitespace , then we should obey that. + while (std::isspace(is.peek())) { + is.get(); + } + } + + auto next_char = is.peek(); + bool negate_value = false; + + if (next_char != '-' && next_char != '0') { + return ParseNormalFloat(is, negate_value, value); + } + + if (next_char == '-') { + negate_value = true; + is.get(); + next_char = is.peek(); + } + + if (next_char == '0') { + is.get(); // We may have to unget this. + auto maybe_hex_start = is.peek(); + if (maybe_hex_start != 'x' && maybe_hex_start != 'X') { + is.unget(); + return ParseNormalFloat(is, negate_value, value); + } else { + is.get(); // Throw away the 'x'; + } + } else { + return ParseNormalFloat(is, negate_value, value); + } + + // This "looks" like a hex-float so treat it as one. + bool seen_p = false; + bool seen_dot = false; + uint_type fraction_index = 0; + + uint_type fraction = 0; + int_type exponent = HF::exponent_bias; + + // Strip off leading zeros so we don't have to special-case them later. + while ((next_char = is.peek()) == '0') { + is.get(); + } + + bool is_denorm = + true; // Assume denorm "representation" until we hear otherwise. + // NB: This does not mean the value is actually denorm, + // it just means that it was written 0. + bool bits_written = false; // Stays false until we write a bit. + while (!seen_p && !seen_dot) { + // Handle characters that are left of the fractional part. + if (next_char == '.') { + seen_dot = true; + } else if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + // We know this is not denormalized since we have stripped all leading + // zeroes and we are not a ".". + is_denorm = false; + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x1 : 0x0; + if (bits_written) { + // If we are here the bits represented belong in the fractional + // part of the float, and we have to adjust the exponent accordingly. + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + exponent = static_cast(exponent + 1); + } + bits_written |= write_bit != 0; + } + } else { + // We have not found our exponent yet, so we have to fail. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + bits_written = false; + while (seen_dot && !seen_p) { + // Handle only fractional parts now. + if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x01 : 0x00; + bits_written |= write_bit != 0; + if (is_denorm && !bits_written) { + // Handle modifying the exponent here this way we can handle + // an arbitrary number of hex values without overflowing our + // integer. + exponent = static_cast(exponent - 1); + } else { + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + } + } + } else { + // We still have not found our 'p' exponent yet, so this is not a valid + // hex-float. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + + bool seen_sign = false; + int8_t exponent_sign = 1; + int_type written_exponent = 0; + while (true) { + if ((next_char == '-' || next_char == '+')) { + if (seen_sign) { + is.setstate(std::ios::failbit); + return is; + } + seen_sign = true; + exponent_sign = (next_char == '-') ? -1 : 1; + } else if (::isdigit(next_char)) { + // Hex-floats express their exponent as decimal. + written_exponent = static_cast(written_exponent * 10); + written_exponent = + static_cast(written_exponent + (next_char - '0')); + } else { + break; + } + is.get(); + next_char = is.peek(); + } + + written_exponent = static_cast(written_exponent * exponent_sign); + exponent = static_cast(exponent + written_exponent); + + bool is_zero = is_denorm && (fraction == 0); + if (is_denorm && !is_zero) { + fraction = static_cast(fraction << 1); + exponent = static_cast(exponent - 1); + } else if (is_zero) { + exponent = 0; + } + + if (exponent <= 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + fraction |= static_cast(1) << HF::top_bit_left_shift; + } + + fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask; + + const int_type max_exponent = + SetBits::get; + + // Handle actual denorm numbers + while (exponent < 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + exponent = static_cast(exponent + 1); + + fraction &= HF::fraction_encode_mask; + if (fraction == 0) { + // We have underflowed our fraction. We should clamp to zero. + is_zero = true; + exponent = 0; + } + } + + // We have overflowed so we should be inf/-inf. + if (exponent > max_exponent) { + exponent = max_exponent; + fraction = 0; + } + + uint_type output_bits = static_cast( + static_cast(negate_value ? 1 : 0) << HF::top_bit_left_shift); + output_bits |= fraction; + + uint_type shifted_exponent = static_cast( + static_cast(exponent << HF::exponent_left_shift) & + HF::exponent_mask); + output_bits |= shifted_exponent; + + T output_float = spvutils::BitwiseCast(output_bits); + value.set_value(output_float); + + return is; +} + +// Writes a FloatProxy value to a stream. +// Zero and normal numbers are printed in the usual notation, but with +// enough digits to fully reproduce the value. Other values (subnormal, +// NaN, and infinity) are printed as a hex float. +template +std::ostream& operator<<(std::ostream& os, const FloatProxy& value) { + auto float_val = value.getAsFloat(); + switch (std::fpclassify(float_val)) { + case FP_ZERO: + case FP_NORMAL: { + auto saved_precision = os.precision(); + os.precision(std::numeric_limits::digits10); + os << float_val; + os.precision(saved_precision); + } break; + default: + os << HexFloat>(value); + break; + } + return os; +} + +template <> +inline std::ostream& operator<<(std::ostream& os, + const FloatProxy& value) { + os << HexFloat>(value); + return os; +} +} + +#endif // LIBSPIRV_UTIL_HEX_FLOAT_H_ diff --git a/third_party/glslang/SPIRV/spirv.hpp b/third_party/glslang/SPIRV/spirv.hpp new file mode 100644 index 0000000..ef5670e --- /dev/null +++ b/third_party/glslang/SPIRV/spirv.hpp @@ -0,0 +1,2197 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10500 +#define SPV_REVISION 3 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010500; +static const unsigned int Revision = 3; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelTaskNV = 5267, + ExecutionModelMeshNV = 5268, + ExecutionModelRayGenerationKHR = 5313, + ExecutionModelRayGenerationNV = 5313, + ExecutionModelIntersectionKHR = 5314, + ExecutionModelIntersectionNV = 5314, + ExecutionModelAnyHitKHR = 5315, + ExecutionModelAnyHitNV = 5315, + ExecutionModelClosestHitKHR = 5316, + ExecutionModelClosestHitNV = 5316, + ExecutionModelMissKHR = 5317, + ExecutionModelMissNV = 5317, + ExecutionModelCallableKHR = 5318, + ExecutionModelCallableNV = 5318, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelPhysicalStorageBuffer64 = 5348, + AddressingModelPhysicalStorageBuffer64EXT = 5348, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelVulkan = 3, + MemoryModelVulkanKHR = 3, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeDenormPreserve = 4459, + ExecutionModeDenormFlushToZero = 4460, + ExecutionModeSignedZeroInfNanPreserve = 4461, + ExecutionModeRoundingModeRTE = 4462, + ExecutionModeRoundingModeRTZ = 4463, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeOutputLinesNV = 5269, + ExecutionModeOutputPrimitivesNV = 5270, + ExecutionModeDerivativeGroupQuadsNV = 5289, + ExecutionModeDerivativeGroupLinearNV = 5290, + ExecutionModeOutputTrianglesNV = 5298, + ExecutionModePixelInterlockOrderedEXT = 5366, + ExecutionModePixelInterlockUnorderedEXT = 5367, + ExecutionModeSampleInterlockOrderedEXT = 5368, + ExecutionModeSampleInterlockUnorderedEXT = 5369, + ExecutionModeShadingRateInterlockOrderedEXT = 5370, + ExecutionModeShadingRateInterlockUnorderedEXT = 5371, + ExecutionModeMaxWorkgroupSizeINTEL = 5893, + ExecutionModeMaxWorkDimINTEL = 5894, + ExecutionModeNoGlobalOffsetINTEL = 5895, + ExecutionModeNumSIMDWorkitemsINTEL = 5896, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassCallableDataKHR = 5328, + StorageClassCallableDataNV = 5328, + StorageClassIncomingCallableDataKHR = 5329, + StorageClassIncomingCallableDataNV = 5329, + StorageClassRayPayloadKHR = 5338, + StorageClassRayPayloadNV = 5338, + StorageClassHitAttributeKHR = 5339, + StorageClassHitAttributeNV = 5339, + StorageClassIncomingRayPayloadKHR = 5342, + StorageClassIncomingRayPayloadNV = 5342, + StorageClassShaderRecordBufferKHR = 5343, + StorageClassShaderRecordBufferNV = 5343, + StorageClassPhysicalStorageBuffer = 5349, + StorageClassPhysicalStorageBufferEXT = 5349, + StorageClassCodeSectionINTEL = 5605, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatR64ui = 40, + ImageFormatR64i = 41, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMakeTexelAvailableShift = 8, + ImageOperandsMakeTexelAvailableKHRShift = 8, + ImageOperandsMakeTexelVisibleShift = 9, + ImageOperandsMakeTexelVisibleKHRShift = 9, + ImageOperandsNonPrivateTexelShift = 10, + ImageOperandsNonPrivateTexelKHRShift = 10, + ImageOperandsVolatileTexelShift = 11, + ImageOperandsVolatileTexelKHRShift = 11, + ImageOperandsSignExtendShift = 12, + ImageOperandsZeroExtendShift = 13, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, + ImageOperandsMakeTexelAvailableMask = 0x00000100, + ImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + ImageOperandsMakeTexelVisibleMask = 0x00000200, + ImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + ImageOperandsNonPrivateTexelMask = 0x00000400, + ImageOperandsNonPrivateTexelKHRMask = 0x00000400, + ImageOperandsVolatileTexelMask = 0x00000800, + ImageOperandsVolatileTexelKHRMask = 0x00000800, + ImageOperandsSignExtendMask = 0x00001000, + ImageOperandsZeroExtendMask = 0x00002000, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationUniformId = 27, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationNoSignedWrap = 4469, + DecorationNoUnsignedWrap = 4470, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationPerPrimitiveNV = 5271, + DecorationPerViewNV = 5272, + DecorationPerTaskNV = 5273, + DecorationPerVertexNV = 5285, + DecorationNonUniform = 5300, + DecorationNonUniformEXT = 5300, + DecorationRestrictPointer = 5355, + DecorationRestrictPointerEXT = 5355, + DecorationAliasedPointer = 5356, + DecorationAliasedPointerEXT = 5356, + DecorationReferencedIndirectlyINTEL = 5602, + DecorationCounterBuffer = 5634, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationUserSemantic = 5635, + DecorationUserTypeGOOGLE = 5636, + DecorationRegisterINTEL = 5825, + DecorationMemoryINTEL = 5826, + DecorationNumbanksINTEL = 5827, + DecorationBankwidthINTEL = 5828, + DecorationMaxPrivateCopiesINTEL = 5829, + DecorationSinglepumpINTEL = 5830, + DecorationDoublepumpINTEL = 5831, + DecorationMaxReplicatesINTEL = 5832, + DecorationSimpleDualPortINTEL = 5833, + DecorationMergeINTEL = 5834, + DecorationBankBitsINTEL = 5835, + DecorationForcePow2DepthINTEL = 5836, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMask = 4416, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMask = 4417, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMask = 4418, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMask = 4419, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMask = 4420, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInPrimitiveShadingRateKHR = 4432, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInShadingRateKHR = 4444, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInFullyCoveredEXT = 5264, + BuiltInTaskCountNV = 5274, + BuiltInPrimitiveCountNV = 5275, + BuiltInPrimitiveIndicesNV = 5276, + BuiltInClipDistancePerViewNV = 5277, + BuiltInCullDistancePerViewNV = 5278, + BuiltInLayerPerViewNV = 5279, + BuiltInMeshViewCountNV = 5280, + BuiltInMeshViewIndicesNV = 5281, + BuiltInBaryCoordNV = 5286, + BuiltInBaryCoordNoPerspNV = 5287, + BuiltInFragSizeEXT = 5292, + BuiltInFragmentSizeNV = 5292, + BuiltInFragInvocationCountEXT = 5293, + BuiltInInvocationsPerPixelNV = 5293, + BuiltInLaunchIdKHR = 5319, + BuiltInLaunchIdNV = 5319, + BuiltInLaunchSizeKHR = 5320, + BuiltInLaunchSizeNV = 5320, + BuiltInWorldRayOriginKHR = 5321, + BuiltInWorldRayOriginNV = 5321, + BuiltInWorldRayDirectionKHR = 5322, + BuiltInWorldRayDirectionNV = 5322, + BuiltInObjectRayOriginKHR = 5323, + BuiltInObjectRayOriginNV = 5323, + BuiltInObjectRayDirectionKHR = 5324, + BuiltInObjectRayDirectionNV = 5324, + BuiltInRayTminKHR = 5325, + BuiltInRayTminNV = 5325, + BuiltInRayTmaxKHR = 5326, + BuiltInRayTmaxNV = 5326, + BuiltInInstanceCustomIndexKHR = 5327, + BuiltInInstanceCustomIndexNV = 5327, + BuiltInObjectToWorldKHR = 5330, + BuiltInObjectToWorldNV = 5330, + BuiltInWorldToObjectKHR = 5331, + BuiltInWorldToObjectNV = 5331, + BuiltInHitTKHR = 5332, + BuiltInHitTNV = 5332, + BuiltInHitKindKHR = 5333, + BuiltInHitKindNV = 5333, + BuiltInIncomingRayFlagsKHR = 5351, + BuiltInIncomingRayFlagsNV = 5351, + BuiltInRayGeometryIndexKHR = 5352, + BuiltInWarpsPerSMNV = 5374, + BuiltInSMCountNV = 5375, + BuiltInWarpIDNV = 5376, + BuiltInSMIDNV = 5377, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMinIterationsShift = 4, + LoopControlMaxIterationsShift = 5, + LoopControlIterationMultipleShift = 6, + LoopControlPeelCountShift = 7, + LoopControlPartialCountShift = 8, + LoopControlInitiationIntervalINTELShift = 16, + LoopControlMaxConcurrencyINTELShift = 17, + LoopControlDependencyArrayINTELShift = 18, + LoopControlPipelineEnableINTELShift = 19, + LoopControlLoopCoalesceINTELShift = 20, + LoopControlMaxInterleavingINTELShift = 21, + LoopControlSpeculatedIterationsINTELShift = 22, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, + LoopControlMinIterationsMask = 0x00000010, + LoopControlMaxIterationsMask = 0x00000020, + LoopControlIterationMultipleMask = 0x00000040, + LoopControlPeelCountMask = 0x00000080, + LoopControlPartialCountMask = 0x00000100, + LoopControlInitiationIntervalINTELMask = 0x00010000, + LoopControlMaxConcurrencyINTELMask = 0x00020000, + LoopControlDependencyArrayINTELMask = 0x00040000, + LoopControlPipelineEnableINTELMask = 0x00080000, + LoopControlLoopCoalesceINTELMask = 0x00100000, + LoopControlMaxInterleavingINTELMask = 0x00200000, + LoopControlSpeculatedIterationsINTELMask = 0x00400000, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsOutputMemoryShift = 12, + MemorySemanticsOutputMemoryKHRShift = 12, + MemorySemanticsMakeAvailableShift = 13, + MemorySemanticsMakeAvailableKHRShift = 13, + MemorySemanticsMakeVisibleShift = 14, + MemorySemanticsMakeVisibleKHRShift = 14, + MemorySemanticsVolatileShift = 15, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, + MemorySemanticsOutputMemoryMask = 0x00001000, + MemorySemanticsOutputMemoryKHRMask = 0x00001000, + MemorySemanticsMakeAvailableMask = 0x00002000, + MemorySemanticsMakeAvailableKHRMask = 0x00002000, + MemorySemanticsMakeVisibleMask = 0x00004000, + MemorySemanticsMakeVisibleKHRMask = 0x00004000, + MemorySemanticsVolatileMask = 0x00008000, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMakePointerAvailableShift = 3, + MemoryAccessMakePointerAvailableKHRShift = 3, + MemoryAccessMakePointerVisibleShift = 4, + MemoryAccessMakePointerVisibleKHRShift = 4, + MemoryAccessNonPrivatePointerShift = 5, + MemoryAccessNonPrivatePointerKHRShift = 5, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, + MemoryAccessMakePointerAvailableMask = 0x00000008, + MemoryAccessMakePointerAvailableKHRMask = 0x00000008, + MemoryAccessMakePointerVisibleMask = 0x00000010, + MemoryAccessMakePointerVisibleKHRMask = 0x00000010, + MemoryAccessNonPrivatePointerMask = 0x00000020, + MemoryAccessNonPrivatePointerKHRMask = 0x00000020, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeQueueFamily = 5, + ScopeQueueFamilyKHR = 5, + ScopeShaderCallKHR = 6, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationClusteredReduce = 3, + GroupOperationPartitionedReduceNV = 6, + GroupOperationPartitionedInclusiveScanNV = 7, + GroupOperationPartitionedExclusiveScanNV = 8, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilityGroupNonUniform = 61, + CapabilityGroupNonUniformVote = 62, + CapabilityGroupNonUniformArithmetic = 63, + CapabilityGroupNonUniformBallot = 64, + CapabilityGroupNonUniformShuffle = 65, + CapabilityGroupNonUniformShuffleRelative = 66, + CapabilityGroupNonUniformClustered = 67, + CapabilityGroupNonUniformQuad = 68, + CapabilityShaderLayer = 69, + CapabilityShaderViewportIndex = 70, + CapabilityFragmentShadingRateKHR = 4422, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityStorageBuffer8BitAccess = 4448, + CapabilityUniformAndStorageBuffer8BitAccess = 4449, + CapabilityStoragePushConstant8 = 4450, + CapabilityDenormPreserve = 4464, + CapabilityDenormFlushToZero = 4465, + CapabilitySignedZeroInfNanPreserve = 4466, + CapabilityRoundingModeRTE = 4467, + CapabilityRoundingModeRTZ = 4468, + CapabilityRayQueryProvisionalKHR = 4471, + CapabilityRayTraversalPrimitiveCullingProvisionalKHR = 4478, + CapabilityFloat16ImageAMD = 5008, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilityInt64ImageEXT = 5016, + CapabilityShaderClockKHR = 5055, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilityFragmentFullyCoveredEXT = 5265, + CapabilityMeshShadingNV = 5266, + CapabilityImageFootprintNV = 5282, + CapabilityFragmentBarycentricNV = 5284, + CapabilityComputeDerivativeGroupQuadsNV = 5288, + CapabilityFragmentDensityEXT = 5291, + CapabilityShadingRateNV = 5291, + CapabilityGroupNonUniformPartitionedNV = 5297, + CapabilityShaderNonUniform = 5301, + CapabilityShaderNonUniformEXT = 5301, + CapabilityRuntimeDescriptorArray = 5302, + CapabilityRuntimeDescriptorArrayEXT = 5302, + CapabilityInputAttachmentArrayDynamicIndexing = 5303, + CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + CapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + CapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + CapabilityUniformBufferArrayNonUniformIndexing = 5306, + CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + CapabilitySampledImageArrayNonUniformIndexing = 5307, + CapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + CapabilityStorageBufferArrayNonUniformIndexing = 5308, + CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + CapabilityStorageImageArrayNonUniformIndexing = 5309, + CapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + CapabilityInputAttachmentArrayNonUniformIndexing = 5310, + CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + CapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + CapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + CapabilityRayTracingNV = 5340, + CapabilityVulkanMemoryModel = 5345, + CapabilityVulkanMemoryModelKHR = 5345, + CapabilityVulkanMemoryModelDeviceScope = 5346, + CapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + CapabilityPhysicalStorageBufferAddresses = 5347, + CapabilityPhysicalStorageBufferAddressesEXT = 5347, + CapabilityComputeDerivativeGroupLinearNV = 5350, + CapabilityRayTracingProvisionalKHR = 5353, + CapabilityCooperativeMatrixNV = 5357, + CapabilityFragmentShaderSampleInterlockEXT = 5363, + CapabilityFragmentShaderShadingRateInterlockEXT = 5372, + CapabilityShaderSMBuiltinsNV = 5373, + CapabilityFragmentShaderPixelInterlockEXT = 5378, + CapabilityDemoteToHelperInvocationEXT = 5379, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilitySubgroupImageMediaBlockIOINTEL = 5579, + CapabilityIntegerFunctions2INTEL = 5584, + CapabilityFunctionPointersINTEL = 5603, + CapabilityIndirectReferencesINTEL = 5604, + CapabilitySubgroupAvcMotionEstimationINTEL = 5696, + CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + CapabilityFPGAMemoryAttributesINTEL = 5824, + CapabilityUnstructuredLoopControlsINTEL = 5886, + CapabilityFPGALoopControlsINTEL = 5888, + CapabilityKernelAttributesINTEL = 5892, + CapabilityFPGAKernelAttributesINTEL = 5897, + CapabilityBlockingPipesINTEL = 5945, + CapabilityFPGARegINTEL = 5948, + CapabilityAtomicFloat32AddEXT = 6033, + CapabilityAtomicFloat64AddEXT = 6034, + CapabilityMax = 0x7fffffff, +}; + +enum RayFlagsShift { + RayFlagsOpaqueKHRShift = 0, + RayFlagsNoOpaqueKHRShift = 1, + RayFlagsTerminateOnFirstHitKHRShift = 2, + RayFlagsSkipClosestHitShaderKHRShift = 3, + RayFlagsCullBackFacingTrianglesKHRShift = 4, + RayFlagsCullFrontFacingTrianglesKHRShift = 5, + RayFlagsCullOpaqueKHRShift = 6, + RayFlagsCullNoOpaqueKHRShift = 7, + RayFlagsSkipTrianglesKHRShift = 8, + RayFlagsSkipAABBsKHRShift = 9, + RayFlagsMax = 0x7fffffff, +}; + +enum RayFlagsMask { + RayFlagsMaskNone = 0, + RayFlagsOpaqueKHRMask = 0x00000001, + RayFlagsNoOpaqueKHRMask = 0x00000002, + RayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + RayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + RayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + RayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + RayFlagsCullOpaqueKHRMask = 0x00000040, + RayFlagsCullNoOpaqueKHRMask = 0x00000080, + RayFlagsSkipTrianglesKHRMask = 0x00000100, + RayFlagsSkipAABBsKHRMask = 0x00000200, +}; + +enum RayQueryIntersection { + RayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + RayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + RayQueryIntersectionMax = 0x7fffffff, +}; + +enum RayQueryCommittedIntersectionType { + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + RayQueryCommittedIntersectionTypeMax = 0x7fffffff, +}; + +enum RayQueryCandidateIntersectionType { + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + RayQueryCandidateIntersectionTypeMax = 0x7fffffff, +}; + +enum FragmentShadingRateShift { + FragmentShadingRateVertical2PixelsShift = 0, + FragmentShadingRateVertical4PixelsShift = 1, + FragmentShadingRateHorizontal2PixelsShift = 2, + FragmentShadingRateHorizontal4PixelsShift = 3, + FragmentShadingRateMax = 0x7fffffff, +}; + +enum FragmentShadingRateMask { + FragmentShadingRateMaskNone = 0, + FragmentShadingRateVertical2PixelsMask = 0x00000001, + FragmentShadingRateVertical4PixelsMask = 0x00000002, + FragmentShadingRateHorizontal2PixelsMask = 0x00000004, + FragmentShadingRateHorizontal4PixelsMask = 0x00000008, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpTerminateInvocation = 4416, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpLoopControlINTEL = 5887, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpMax = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case OpNop: *hasResult = false; *hasResultType = false; break; + case OpUndef: *hasResult = true; *hasResultType = true; break; + case OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case OpSource: *hasResult = false; *hasResultType = false; break; + case OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case OpName: *hasResult = false; *hasResultType = false; break; + case OpMemberName: *hasResult = false; *hasResultType = false; break; + case OpString: *hasResult = true; *hasResultType = false; break; + case OpLine: *hasResult = false; *hasResultType = false; break; + case OpExtension: *hasResult = false; *hasResultType = false; break; + case OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case OpExtInst: *hasResult = true; *hasResultType = true; break; + case OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case OpCapability: *hasResult = false; *hasResultType = false; break; + case OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case OpTypeBool: *hasResult = true; *hasResultType = false; break; + case OpTypeInt: *hasResult = true; *hasResultType = false; break; + case OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case OpTypeVector: *hasResult = true; *hasResultType = false; break; + case OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case OpTypeImage: *hasResult = true; *hasResultType = false; break; + case OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case OpTypeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case OpTypePointer: *hasResult = true; *hasResultType = false; break; + case OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case OpTypePipe: *hasResult = true; *hasResultType = false; break; + case OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpConstant: *hasResult = true; *hasResultType = true; break; + case OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case OpConstantNull: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case OpFunction: *hasResult = true; *hasResultType = true; break; + case OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case OpVariable: *hasResult = true; *hasResultType = true; break; + case OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case OpLoad: *hasResult = true; *hasResultType = true; break; + case OpStore: *hasResult = false; *hasResultType = false; break; + case OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case OpAccessChain: *hasResult = true; *hasResultType = true; break; + case OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpArrayLength: *hasResult = true; *hasResultType = true; break; + case OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpDecorate: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case OpCopyObject: *hasResult = true; *hasResultType = true; break; + case OpTranspose: *hasResult = true; *hasResultType = true; break; + case OpSampledImage: *hasResult = true; *hasResultType = true; break; + case OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageFetch: *hasResult = true; *hasResultType = true; break; + case OpImageGather: *hasResult = true; *hasResultType = true; break; + case OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageRead: *hasResult = true; *hasResultType = true; break; + case OpImageWrite: *hasResult = false; *hasResultType = false; break; + case OpImage: *hasResult = true; *hasResultType = true; break; + case OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case OpUConvert: *hasResult = true; *hasResultType = true; break; + case OpSConvert: *hasResult = true; *hasResultType = true; break; + case OpFConvert: *hasResult = true; *hasResultType = true; break; + case OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case OpBitcast: *hasResult = true; *hasResultType = true; break; + case OpSNegate: *hasResult = true; *hasResultType = true; break; + case OpFNegate: *hasResult = true; *hasResultType = true; break; + case OpIAdd: *hasResult = true; *hasResultType = true; break; + case OpFAdd: *hasResult = true; *hasResultType = true; break; + case OpISub: *hasResult = true; *hasResultType = true; break; + case OpFSub: *hasResult = true; *hasResultType = true; break; + case OpIMul: *hasResult = true; *hasResultType = true; break; + case OpFMul: *hasResult = true; *hasResultType = true; break; + case OpUDiv: *hasResult = true; *hasResultType = true; break; + case OpSDiv: *hasResult = true; *hasResultType = true; break; + case OpFDiv: *hasResult = true; *hasResultType = true; break; + case OpUMod: *hasResult = true; *hasResultType = true; break; + case OpSRem: *hasResult = true; *hasResultType = true; break; + case OpSMod: *hasResult = true; *hasResultType = true; break; + case OpFRem: *hasResult = true; *hasResultType = true; break; + case OpFMod: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case OpDot: *hasResult = true; *hasResultType = true; break; + case OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case OpAny: *hasResult = true; *hasResultType = true; break; + case OpAll: *hasResult = true; *hasResultType = true; break; + case OpIsNan: *hasResult = true; *hasResultType = true; break; + case OpIsInf: *hasResult = true; *hasResultType = true; break; + case OpIsFinite: *hasResult = true; *hasResultType = true; break; + case OpIsNormal: *hasResult = true; *hasResultType = true; break; + case OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case OpOrdered: *hasResult = true; *hasResultType = true; break; + case OpUnordered: *hasResult = true; *hasResultType = true; break; + case OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case OpSelect: *hasResult = true; *hasResultType = true; break; + case OpIEqual: *hasResult = true; *hasResultType = true; break; + case OpINotEqual: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpULessThan: *hasResult = true; *hasResultType = true; break; + case OpSLessThan: *hasResult = true; *hasResultType = true; break; + case OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpNot: *hasResult = true; *hasResultType = true; break; + case OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case OpBitReverse: *hasResult = true; *hasResultType = true; break; + case OpBitCount: *hasResult = true; *hasResultType = true; break; + case OpDPdx: *hasResult = true; *hasResultType = true; break; + case OpDPdy: *hasResult = true; *hasResultType = true; break; + case OpFwidth: *hasResult = true; *hasResultType = true; break; + case OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case OpPhi: *hasResult = true; *hasResultType = true; break; + case OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case OpLabel: *hasResult = true; *hasResultType = false; break; + case OpBranch: *hasResult = false; *hasResultType = false; break; + case OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case OpSwitch: *hasResult = false; *hasResultType = false; break; + case OpKill: *hasResult = false; *hasResultType = false; break; + case OpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case OpReturn: *hasResult = false; *hasResultType = false; break; + case OpReturnValue: *hasResult = false; *hasResultType = false; break; + case OpUnreachable: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case OpGroupAll: *hasResult = true; *hasResultType = true; break; + case OpGroupAny: *hasResult = true; *hasResultType = true; break; + case OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case OpReadPipe: *hasResult = true; *hasResultType = true; break; + case OpWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case OpNoLine: *hasResult = false; *hasResultType = false; break; + case OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case OpSizeOf: *hasResult = true; *hasResultType = true; break; + case OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case OpDecorateId: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case OpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case OpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case OpTraceNV: *hasResult = false; *hasResultType = false; break; + case OpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; + case OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case OpDecorateString: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case OpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case OpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case OpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case OpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case OpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } +inline FragmentShadingRateMask operator|(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/glslang/SPIRV/spvIR.h b/third_party/glslang/SPIRV/spvIR.h new file mode 100755 index 0000000..486e80d --- /dev/null +++ b/third_party/glslang/SPIRV/spvIR.h @@ -0,0 +1,508 @@ +// +// Copyright (C) 2014 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// SPIRV-IR +// +// Simple in-memory representation (IR) of SPIRV. Just for holding +// Each function's CFG of blocks. Has this hierarchy: +// - Module, which is a list of +// - Function, which is a list of +// - Block, which is a list of +// - Instruction +// + +#pragma once +#ifndef spvIR_H +#define spvIR_H + +#include "spirv.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace spv { + +class Block; +class Function; +class Module; + +const Id NoResult = 0; +const Id NoType = 0; + +const Decoration NoPrecision = DecorationMax; + +#ifdef __GNUC__ +# define POTENTIALLY_UNUSED __attribute__((unused)) +#else +# define POTENTIALLY_UNUSED +#endif + +POTENTIALLY_UNUSED +const MemorySemanticsMask MemorySemanticsAllMemory = + (MemorySemanticsMask)(MemorySemanticsUniformMemoryMask | + MemorySemanticsWorkgroupMemoryMask | + MemorySemanticsAtomicCounterMemoryMask | + MemorySemanticsImageMemoryMask); + +struct IdImmediate { + bool isId; // true if word is an Id, false if word is an immediate + unsigned word; + IdImmediate(bool i, unsigned w) : isId(i), word(w) {} +}; + +// +// SPIR-V IR instruction. +// + +class Instruction { +public: + Instruction(Id resultId, Id typeId, Op opCode) : resultId(resultId), typeId(typeId), opCode(opCode), block(nullptr) { } + explicit Instruction(Op opCode) : resultId(NoResult), typeId(NoType), opCode(opCode), block(nullptr) { } + virtual ~Instruction() {} + void addIdOperand(Id id) { + operands.push_back(id); + idOperand.push_back(true); + } + void addImmediateOperand(unsigned int immediate) { + operands.push_back(immediate); + idOperand.push_back(false); + } + void setImmediateOperand(unsigned idx, unsigned int immediate) { + assert(!idOperand[idx]); + operands[idx] = immediate; + } + + void addStringOperand(const char* str) + { + unsigned int word; + char* wordString = (char*)&word; + char* wordPtr = wordString; + int charCount = 0; + char c; + do { + c = *(str++); + *(wordPtr++) = c; + ++charCount; + if (charCount == 4) { + addImmediateOperand(word); + wordPtr = wordString; + charCount = 0; + } + } while (c != 0); + + // deal with partial last word + if (charCount > 0) { + // pad with 0s + for (; charCount < 4; ++charCount) + *(wordPtr++) = 0; + addImmediateOperand(word); + } + } + bool isIdOperand(int op) const { return idOperand[op]; } + void setBlock(Block* b) { block = b; } + Block* getBlock() const { return block; } + Op getOpCode() const { return opCode; } + int getNumOperands() const + { + assert(operands.size() == idOperand.size()); + return (int)operands.size(); + } + Id getResultId() const { return resultId; } + Id getTypeId() const { return typeId; } + Id getIdOperand(int op) const { + assert(idOperand[op]); + return operands[op]; + } + unsigned int getImmediateOperand(int op) const { + assert(!idOperand[op]); + return operands[op]; + } + + // Write out the binary form. + void dump(std::vector& out) const + { + // Compute the wordCount + unsigned int wordCount = 1; + if (typeId) + ++wordCount; + if (resultId) + ++wordCount; + wordCount += (unsigned int)operands.size(); + + // Write out the beginning of the instruction + out.push_back(((wordCount) << WordCountShift) | opCode); + if (typeId) + out.push_back(typeId); + if (resultId) + out.push_back(resultId); + + // Write out the operands + for (int op = 0; op < (int)operands.size(); ++op) + out.push_back(operands[op]); + } + +protected: + Instruction(const Instruction&); + Id resultId; + Id typeId; + Op opCode; + std::vector operands; // operands, both and immediates (both are unsigned int) + std::vector idOperand; // true for operands that are , false for immediates + Block* block; +}; + +// +// SPIR-V IR block. +// + +class Block { +public: + Block(Id id, Function& parent); + virtual ~Block() + { + } + + Id getId() { return instructions.front()->getResultId(); } + + Function& getParent() const { return parent; } + void addInstruction(std::unique_ptr inst); + void addPredecessor(Block* pred) { predecessors.push_back(pred); pred->successors.push_back(this);} + void addLocalVariable(std::unique_ptr inst) { localVariables.push_back(std::move(inst)); } + const std::vector& getPredecessors() const { return predecessors; } + const std::vector& getSuccessors() const { return successors; } + const std::vector >& getInstructions() const { + return instructions; + } + const std::vector >& getLocalVariables() const { return localVariables; } + void setUnreachable() { unreachable = true; } + bool isUnreachable() const { return unreachable; } + // Returns the block's merge instruction, if one exists (otherwise null). + const Instruction* getMergeInstruction() const { + if (instructions.size() < 2) return nullptr; + const Instruction* nextToLast = (instructions.cend() - 2)->get(); + switch (nextToLast->getOpCode()) { + case OpSelectionMerge: + case OpLoopMerge: + return nextToLast; + default: + return nullptr; + } + return nullptr; + } + + // Change this block into a canonical dead merge block. Delete instructions + // as necessary. A canonical dead merge block has only an OpLabel and an + // OpUnreachable. + void rewriteAsCanonicalUnreachableMerge() { + assert(localVariables.empty()); + // Delete all instructions except for the label. + assert(instructions.size() > 0); + instructions.resize(1); + successors.clear(); + addInstruction(std::unique_ptr(new Instruction(OpUnreachable))); + } + // Change this block into a canonical dead continue target branching to the + // given header ID. Delete instructions as necessary. A canonical dead continue + // target has only an OpLabel and an unconditional branch back to the corresponding + // header. + void rewriteAsCanonicalUnreachableContinue(Block* header) { + assert(localVariables.empty()); + // Delete all instructions except for the label. + assert(instructions.size() > 0); + instructions.resize(1); + successors.clear(); + // Add OpBranch back to the header. + assert(header != nullptr); + Instruction* branch = new Instruction(OpBranch); + branch->addIdOperand(header->getId()); + addInstruction(std::unique_ptr(branch)); + successors.push_back(header); + } + + bool isTerminated() const + { + switch (instructions.back()->getOpCode()) { + case OpBranch: + case OpBranchConditional: + case OpSwitch: + case OpKill: + case OpTerminateInvocation: + case OpReturn: + case OpReturnValue: + case OpUnreachable: + return true; + default: + return false; + } + } + + void dump(std::vector& out) const + { + instructions[0]->dump(out); + for (int i = 0; i < (int)localVariables.size(); ++i) + localVariables[i]->dump(out); + for (int i = 1; i < (int)instructions.size(); ++i) + instructions[i]->dump(out); + } + +protected: + Block(const Block&); + Block& operator=(Block&); + + // To enforce keeping parent and ownership in sync: + friend Function; + + std::vector > instructions; + std::vector predecessors, successors; + std::vector > localVariables; + Function& parent; + + // track whether this block is known to be uncreachable (not necessarily + // true for all unreachable blocks, but should be set at least + // for the extraneous ones introduced by the builder). + bool unreachable; +}; + +// The different reasons for reaching a block in the inReadableOrder traversal. +enum ReachReason { + // Reachable from the entry block via transfers of control, i.e. branches. + ReachViaControlFlow = 0, + // A continue target that is not reachable via control flow. + ReachDeadContinue, + // A merge block that is not reachable via control flow. + ReachDeadMerge +}; + +// Traverses the control-flow graph rooted at root in an order suited for +// readable code generation. Invokes callback at every node in the traversal +// order. The callback arguments are: +// - the block, +// - the reason we reached the block, +// - if the reason was that block is an unreachable continue or unreachable merge block +// then the last parameter is the corresponding header block. +void inReadableOrder(Block* root, std::function callback); + +// +// SPIR-V IR Function. +// + +class Function { +public: + Function(Id id, Id resultType, Id functionType, Id firstParam, Module& parent); + virtual ~Function() + { + for (int i = 0; i < (int)parameterInstructions.size(); ++i) + delete parameterInstructions[i]; + + for (int i = 0; i < (int)blocks.size(); ++i) + delete blocks[i]; + } + Id getId() const { return functionInstruction.getResultId(); } + Id getParamId(int p) const { return parameterInstructions[p]->getResultId(); } + Id getParamType(int p) const { return parameterInstructions[p]->getTypeId(); } + + void addBlock(Block* block) { blocks.push_back(block); } + void removeBlock(Block* block) + { + auto found = find(blocks.begin(), blocks.end(), block); + assert(found != blocks.end()); + blocks.erase(found); + delete block; + } + + Module& getParent() const { return parent; } + Block* getEntryBlock() const { return blocks.front(); } + Block* getLastBlock() const { return blocks.back(); } + const std::vector& getBlocks() const { return blocks; } + void addLocalVariable(std::unique_ptr inst); + Id getReturnType() const { return functionInstruction.getTypeId(); } + void setReturnPrecision(Decoration precision) + { + if (precision == DecorationRelaxedPrecision) + reducedPrecisionReturn = true; + } + Decoration getReturnPrecision() const + { return reducedPrecisionReturn ? DecorationRelaxedPrecision : NoPrecision; } + + void setImplicitThis() { implicitThis = true; } + bool hasImplicitThis() const { return implicitThis; } + + void addParamPrecision(unsigned param, Decoration precision) + { + if (precision == DecorationRelaxedPrecision) + reducedPrecisionParams.insert(param); + } + Decoration getParamPrecision(unsigned param) const + { + return reducedPrecisionParams.find(param) != reducedPrecisionParams.end() ? + DecorationRelaxedPrecision : NoPrecision; + } + + void dump(std::vector& out) const + { + // OpFunction + functionInstruction.dump(out); + + // OpFunctionParameter + for (int p = 0; p < (int)parameterInstructions.size(); ++p) + parameterInstructions[p]->dump(out); + + // Blocks + inReadableOrder(blocks[0], [&out](const Block* b, ReachReason, Block*) { b->dump(out); }); + Instruction end(0, 0, OpFunctionEnd); + end.dump(out); + } + +protected: + Function(const Function&); + Function& operator=(Function&); + + Module& parent; + Instruction functionInstruction; + std::vector parameterInstructions; + std::vector blocks; + bool implicitThis; // true if this is a member function expecting to be passed a 'this' as the first argument + bool reducedPrecisionReturn; + std::set reducedPrecisionParams; // list of parameter indexes that need a relaxed precision arg +}; + +// +// SPIR-V IR Module. +// + +class Module { +public: + Module() {} + virtual ~Module() + { + // TODO delete things + } + + void addFunction(Function *fun) { functions.push_back(fun); } + + void mapInstruction(Instruction *instruction) + { + spv::Id resultId = instruction->getResultId(); + // map the instruction's result id + if (resultId >= idToInstruction.size()) + idToInstruction.resize(resultId + 16); + idToInstruction[resultId] = instruction; + } + + Instruction* getInstruction(Id id) const { return idToInstruction[id]; } + const std::vector& getFunctions() const { return functions; } + spv::Id getTypeId(Id resultId) const { + return idToInstruction[resultId] == nullptr ? NoType : idToInstruction[resultId]->getTypeId(); + } + StorageClass getStorageClass(Id typeId) const + { + assert(idToInstruction[typeId]->getOpCode() == spv::OpTypePointer); + return (StorageClass)idToInstruction[typeId]->getImmediateOperand(0); + } + + void dump(std::vector& out) const + { + for (int f = 0; f < (int)functions.size(); ++f) + functions[f]->dump(out); + } + +protected: + Module(const Module&); + std::vector functions; + + // map from result id to instruction having that result id + std::vector idToInstruction; + + // map from a result id to its type id +}; + +// +// Implementation (it's here due to circular type definitions). +// + +// Add both +// - the OpFunction instruction +// - all the OpFunctionParameter instructions +__inline Function::Function(Id id, Id resultType, Id functionType, Id firstParamId, Module& parent) + : parent(parent), functionInstruction(id, resultType, OpFunction), implicitThis(false), + reducedPrecisionReturn(false) +{ + // OpFunction + functionInstruction.addImmediateOperand(FunctionControlMaskNone); + functionInstruction.addIdOperand(functionType); + parent.mapInstruction(&functionInstruction); + parent.addFunction(this); + + // OpFunctionParameter + Instruction* typeInst = parent.getInstruction(functionType); + int numParams = typeInst->getNumOperands() - 1; + for (int p = 0; p < numParams; ++p) { + Instruction* param = new Instruction(firstParamId + p, typeInst->getIdOperand(p + 1), OpFunctionParameter); + parent.mapInstruction(param); + parameterInstructions.push_back(param); + } +} + +__inline void Function::addLocalVariable(std::unique_ptr inst) +{ + Instruction* raw_instruction = inst.get(); + blocks[0]->addLocalVariable(std::move(inst)); + parent.mapInstruction(raw_instruction); +} + +__inline Block::Block(Id id, Function& parent) : parent(parent), unreachable(false) +{ + instructions.push_back(std::unique_ptr(new Instruction(id, NoType, OpLabel))); + instructions.back()->setBlock(this); + parent.getParent().mapInstruction(instructions.back().get()); +} + +__inline void Block::addInstruction(std::unique_ptr inst) +{ + Instruction* raw_instruction = inst.get(); + instructions.push_back(std::move(inst)); + raw_instruction->setBlock(this); + if (raw_instruction->getResultId()) + parent.getParent().mapInstruction(raw_instruction); +} + +} // end spv namespace + +#endif // spvIR_H diff --git a/third_party/glslang/SPIRV/tnt/CMakeLists.txt b/third_party/glslang/SPIRV/tnt/CMakeLists.txt new file mode 100644 index 0000000..9b274cb --- /dev/null +++ b/third_party/glslang/SPIRV/tnt/CMakeLists.txt @@ -0,0 +1,76 @@ +set(SOURCES + ../GlslangToSpv.cpp + ../InReadableOrder.cpp + ../Logger.cpp + ../SpvBuilder.cpp + ../SpvPostProcess.cpp + ../doc.cpp + ../disassemble.cpp) + +set(SPVREMAP_SOURCES + ../SPVRemapper.cpp + ../doc.cpp) + +set(HEADERS + ../bitutils.h + ../spirv.hpp + ../GLSL.std.450.h + ../GLSL.ext.EXT.h + ../GLSL.ext.KHR.h + ../GlslangToSpv.h + ../hex_float.h + ../Logger.h + ../SpvBuilder.h + ../spvIR.h + ../doc.h + ../disassemble.h) + +set(SPVREMAP_HEADERS + ../SPVRemapper.h + ../doc.h) + +if(ENABLE_AMD_EXTENSIONS) + list(APPEND + HEADERS + ../GLSL.ext.AMD.h) +endif(ENABLE_AMD_EXTENSIONS) + +if(ENABLE_NV_EXTENSIONS) + list(APPEND + HEADERS + ../GLSL.ext.NV.h) +endif(ENABLE_NV_EXTENSIONS) + +add_library(SPIRV STATIC ${SOURCES} ${HEADERS}) +target_include_directories(SPIRV PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..) +target_include_directories(SPIRV PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../..) +set_property(TARGET SPIRV PROPERTY FOLDER glslang) +set_property(TARGET SPIRV PROPERTY POSITION_INDEPENDENT_CODE ON) + +glslang_add_build_info_dependency(SPIRV) + +add_library(SPVRemapper STATIC ${SPVREMAP_SOURCES} ${SPVREMAP_HEADERS}) +set_property(TARGET SPVRemapper PROPERTY FOLDER glslang) +set_property(TARGET SPVRemapper PROPERTY POSITION_INDEPENDENT_CODE ON) + +if(ENABLE_OPT) + target_include_directories(SPIRV + PRIVATE ${spirv-tools_SOURCE_DIR}/include + PRIVATE ${spirv-tools_SOURCE_DIR}/source + ) + target_link_libraries(SPIRV glslang SPIRV-Tools-opt SPVRemapper) +else() + target_link_libraries(SPIRV glslang) +endif(ENABLE_OPT) + +if(WIN32) + source_group("Source" FILES ${SOURCES} ${HEADERS}) + source_group("Source" FILES ${SPVREMAP_SOURCES} ${SPVREMAP_HEADERS}) +endif(WIN32) + +if(ENABLE_GLSLANG_INSTALL) + install(TARGETS SPIRV SPVRemapper + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + install(FILES ${HEADERS} ${SPVREMAP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/SPIRV/) +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/StandAlone/CMakeLists.txt b/third_party/glslang/StandAlone/CMakeLists.txt new file mode 100644 index 0000000..bff9ab6 --- /dev/null +++ b/third_party/glslang/StandAlone/CMakeLists.txt @@ -0,0 +1,106 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +add_library(glslang-default-resource-limits + ${CMAKE_CURRENT_SOURCE_DIR}/ResourceLimits.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/resource_limits_c.cpp) +set_property(TARGET glslang-default-resource-limits PROPERTY FOLDER glslang) +set_property(TARGET glslang-default-resource-limits PROPERTY POSITION_INDEPENDENT_CODE ON) + +target_include_directories(glslang-default-resource-limits + PUBLIC $ + PUBLIC $) + +set(SOURCES StandAlone.cpp DirStackFileIncluder.h) + +add_executable(glslangValidator ${SOURCES}) +set_property(TARGET glslangValidator PROPERTY FOLDER tools) +glslang_set_link_args(glslangValidator) + +set(LIBRARIES + glslang + SPIRV + glslang-default-resource-limits) + +if(ENABLE_SPVREMAPPER) + set(LIBRARIES ${LIBRARIES} SPVRemapper) +endif() + +if(WIN32) + set(LIBRARIES ${LIBRARIES} psapi) +elseif(UNIX) + if(NOT ANDROID) + set(LIBRARIES ${LIBRARIES} pthread) + endif() +endif(WIN32) + +target_link_libraries(glslangValidator ${LIBRARIES}) +target_include_directories(glslangValidator PUBLIC + $ + $) + +if(ENABLE_OPT) + target_include_directories(glslangValidator + PRIVATE ${spirv-tools_SOURCE_DIR}/include + ) +endif(ENABLE_OPT) + +if(ENABLE_SPVREMAPPER) + set(REMAPPER_SOURCES spirv-remap.cpp) + add_executable(spirv-remap ${REMAPPER_SOURCES}) + set_property(TARGET spirv-remap PROPERTY FOLDER tools) + glslang_set_link_args(spirv-remap) + target_link_libraries(spirv-remap ${LIBRARIES}) +endif() + +if(WIN32) + source_group("Source" FILES ${SOURCES}) +endif(WIN32) + +if(ENABLE_GLSLANG_INSTALL) + install(TARGETS glslangValidator EXPORT glslangValidatorTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(EXPORT glslangValidatorTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + + if(ENABLE_SPVREMAPPER) + install(TARGETS spirv-remap EXPORT spirv-remapTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(EXPORT spirv-remapTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + endif() + + if(BUILD_SHARED_LIBS) + install(TARGETS glslang-default-resource-limits EXPORT glslang-default-resource-limitsTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(EXPORT glslang-default-resource-limitsTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + endif() +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/StandAlone/DirStackFileIncluder.h b/third_party/glslang/StandAlone/DirStackFileIncluder.h new file mode 100644 index 0000000..1873413 --- /dev/null +++ b/third_party/glslang/StandAlone/DirStackFileIncluder.h @@ -0,0 +1,141 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2017 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#include +#include +#include +#include + +#include "./../glslang/Public/ShaderLang.h" + +// Default include class for normal include convention of search backward +// through the stack of active include paths (for nested includes). +// Can be overridden to customize. +class DirStackFileIncluder : public glslang::TShader::Includer { +public: + DirStackFileIncluder() : externalLocalDirectoryCount(0) { } + + virtual IncludeResult* includeLocal(const char* headerName, + const char* includerName, + size_t inclusionDepth) override + { + return readLocalPath(headerName, includerName, (int)inclusionDepth); + } + + virtual IncludeResult* includeSystem(const char* headerName, + const char* /*includerName*/, + size_t /*inclusionDepth*/) override + { + return readSystemPath(headerName); + } + + // Externally set directories. E.g., from a command-line -I. + // - Most-recently pushed are checked first. + // - All these are checked after the parse-time stack of local directories + // is checked. + // - This only applies to the "local" form of #include. + // - Makes its own copy of the path. + virtual void pushExternalLocalDirectory(const std::string& dir) + { + directoryStack.push_back(dir); + externalLocalDirectoryCount = (int)directoryStack.size(); + } + + virtual void releaseInclude(IncludeResult* result) override + { + if (result != nullptr) { + delete [] static_cast(result->userData); + delete result; + } + } + + virtual ~DirStackFileIncluder() override { } + +protected: + typedef char tUserDataElement; + std::vector directoryStack; + int externalLocalDirectoryCount; + + // Search for a valid "local" path based on combining the stack of include + // directories and the nominal name of the header. + virtual IncludeResult* readLocalPath(const char* headerName, const char* includerName, int depth) + { + // Discard popped include directories, and + // initialize when at parse-time first level. + directoryStack.resize(depth + externalLocalDirectoryCount); + if (depth == 1) + directoryStack.back() = getDirectory(includerName); + + // Find a directory that works, using a reverse search of the include stack. + for (auto it = directoryStack.rbegin(); it != directoryStack.rend(); ++it) { + std::string path = *it + '/' + headerName; + std::replace(path.begin(), path.end(), '\\', '/'); + std::ifstream file(path, std::ios_base::binary | std::ios_base::ate); + if (file) { + directoryStack.push_back(getDirectory(path)); + return newIncludeResult(path, file, (int)file.tellg()); + } + } + + return nullptr; + } + + // Search for a valid path. + // Not implemented yet; returning nullptr signals failure to find. + virtual IncludeResult* readSystemPath(const char* /*headerName*/) const + { + return nullptr; + } + + // Do actual reading of the file, filling in a new include result. + virtual IncludeResult* newIncludeResult(const std::string& path, std::ifstream& file, int length) const + { + char* content = new tUserDataElement [length]; + file.seekg(0, file.beg); + file.read(content, length); + return new IncludeResult(path, content, length, content); + } + + // If no path markers, return current working directory. + // Otherwise, strip file name and return path leading up to it. + virtual std::string getDirectory(const std::string path) const + { + size_t last = path.find_last_of("/\\"); + return last == std::string::npos ? "." : path.substr(0, last); + } +}; diff --git a/third_party/glslang/StandAlone/ResourceLimits.cpp b/third_party/glslang/StandAlone/ResourceLimits.cpp new file mode 100644 index 0000000..7c7f4c4 --- /dev/null +++ b/third_party/glslang/StandAlone/ResourceLimits.cpp @@ -0,0 +1,496 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +#include "ResourceLimits.h" + +namespace glslang { + +const TBuiltInResource DefaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + }}; + +std::string GetDefaultTBuiltInResourceString() +{ + std::ostringstream ostream; + + ostream << "MaxLights " << DefaultTBuiltInResource.maxLights << "\n" + << "MaxClipPlanes " << DefaultTBuiltInResource.maxClipPlanes << "\n" + << "MaxTextureUnits " << DefaultTBuiltInResource.maxTextureUnits << "\n" + << "MaxTextureCoords " << DefaultTBuiltInResource.maxTextureCoords << "\n" + << "MaxVertexAttribs " << DefaultTBuiltInResource.maxVertexAttribs << "\n" + << "MaxVertexUniformComponents " << DefaultTBuiltInResource.maxVertexUniformComponents << "\n" + << "MaxVaryingFloats " << DefaultTBuiltInResource.maxVaryingFloats << "\n" + << "MaxVertexTextureImageUnits " << DefaultTBuiltInResource.maxVertexTextureImageUnits << "\n" + << "MaxCombinedTextureImageUnits " << DefaultTBuiltInResource.maxCombinedTextureImageUnits << "\n" + << "MaxTextureImageUnits " << DefaultTBuiltInResource.maxTextureImageUnits << "\n" + << "MaxFragmentUniformComponents " << DefaultTBuiltInResource.maxFragmentUniformComponents << "\n" + << "MaxDrawBuffers " << DefaultTBuiltInResource.maxDrawBuffers << "\n" + << "MaxVertexUniformVectors " << DefaultTBuiltInResource.maxVertexUniformVectors << "\n" + << "MaxVaryingVectors " << DefaultTBuiltInResource.maxVaryingVectors << "\n" + << "MaxFragmentUniformVectors " << DefaultTBuiltInResource.maxFragmentUniformVectors << "\n" + << "MaxVertexOutputVectors " << DefaultTBuiltInResource.maxVertexOutputVectors << "\n" + << "MaxFragmentInputVectors " << DefaultTBuiltInResource.maxFragmentInputVectors << "\n" + << "MinProgramTexelOffset " << DefaultTBuiltInResource.minProgramTexelOffset << "\n" + << "MaxProgramTexelOffset " << DefaultTBuiltInResource.maxProgramTexelOffset << "\n" + << "MaxClipDistances " << DefaultTBuiltInResource.maxClipDistances << "\n" + << "MaxComputeWorkGroupCountX " << DefaultTBuiltInResource.maxComputeWorkGroupCountX << "\n" + << "MaxComputeWorkGroupCountY " << DefaultTBuiltInResource.maxComputeWorkGroupCountY << "\n" + << "MaxComputeWorkGroupCountZ " << DefaultTBuiltInResource.maxComputeWorkGroupCountZ << "\n" + << "MaxComputeWorkGroupSizeX " << DefaultTBuiltInResource.maxComputeWorkGroupSizeX << "\n" + << "MaxComputeWorkGroupSizeY " << DefaultTBuiltInResource.maxComputeWorkGroupSizeY << "\n" + << "MaxComputeWorkGroupSizeZ " << DefaultTBuiltInResource.maxComputeWorkGroupSizeZ << "\n" + << "MaxComputeUniformComponents " << DefaultTBuiltInResource.maxComputeUniformComponents << "\n" + << "MaxComputeTextureImageUnits " << DefaultTBuiltInResource.maxComputeTextureImageUnits << "\n" + << "MaxComputeImageUniforms " << DefaultTBuiltInResource.maxComputeImageUniforms << "\n" + << "MaxComputeAtomicCounters " << DefaultTBuiltInResource.maxComputeAtomicCounters << "\n" + << "MaxComputeAtomicCounterBuffers " << DefaultTBuiltInResource.maxComputeAtomicCounterBuffers << "\n" + << "MaxVaryingComponents " << DefaultTBuiltInResource.maxVaryingComponents << "\n" + << "MaxVertexOutputComponents " << DefaultTBuiltInResource.maxVertexOutputComponents << "\n" + << "MaxGeometryInputComponents " << DefaultTBuiltInResource.maxGeometryInputComponents << "\n" + << "MaxGeometryOutputComponents " << DefaultTBuiltInResource.maxGeometryOutputComponents << "\n" + << "MaxFragmentInputComponents " << DefaultTBuiltInResource.maxFragmentInputComponents << "\n" + << "MaxImageUnits " << DefaultTBuiltInResource.maxImageUnits << "\n" + << "MaxCombinedImageUnitsAndFragmentOutputs " << DefaultTBuiltInResource.maxCombinedImageUnitsAndFragmentOutputs << "\n" + << "MaxCombinedShaderOutputResources " << DefaultTBuiltInResource.maxCombinedShaderOutputResources << "\n" + << "MaxImageSamples " << DefaultTBuiltInResource.maxImageSamples << "\n" + << "MaxVertexImageUniforms " << DefaultTBuiltInResource.maxVertexImageUniforms << "\n" + << "MaxTessControlImageUniforms " << DefaultTBuiltInResource.maxTessControlImageUniforms << "\n" + << "MaxTessEvaluationImageUniforms " << DefaultTBuiltInResource.maxTessEvaluationImageUniforms << "\n" + << "MaxGeometryImageUniforms " << DefaultTBuiltInResource.maxGeometryImageUniforms << "\n" + << "MaxFragmentImageUniforms " << DefaultTBuiltInResource.maxFragmentImageUniforms << "\n" + << "MaxCombinedImageUniforms " << DefaultTBuiltInResource.maxCombinedImageUniforms << "\n" + << "MaxGeometryTextureImageUnits " << DefaultTBuiltInResource.maxGeometryTextureImageUnits << "\n" + << "MaxGeometryOutputVertices " << DefaultTBuiltInResource.maxGeometryOutputVertices << "\n" + << "MaxGeometryTotalOutputComponents " << DefaultTBuiltInResource.maxGeometryTotalOutputComponents << "\n" + << "MaxGeometryUniformComponents " << DefaultTBuiltInResource.maxGeometryUniformComponents << "\n" + << "MaxGeometryVaryingComponents " << DefaultTBuiltInResource.maxGeometryVaryingComponents << "\n" + << "MaxTessControlInputComponents " << DefaultTBuiltInResource.maxTessControlInputComponents << "\n" + << "MaxTessControlOutputComponents " << DefaultTBuiltInResource.maxTessControlOutputComponents << "\n" + << "MaxTessControlTextureImageUnits " << DefaultTBuiltInResource.maxTessControlTextureImageUnits << "\n" + << "MaxTessControlUniformComponents " << DefaultTBuiltInResource.maxTessControlUniformComponents << "\n" + << "MaxTessControlTotalOutputComponents " << DefaultTBuiltInResource.maxTessControlTotalOutputComponents << "\n" + << "MaxTessEvaluationInputComponents " << DefaultTBuiltInResource.maxTessEvaluationInputComponents << "\n" + << "MaxTessEvaluationOutputComponents " << DefaultTBuiltInResource.maxTessEvaluationOutputComponents << "\n" + << "MaxTessEvaluationTextureImageUnits " << DefaultTBuiltInResource.maxTessEvaluationTextureImageUnits << "\n" + << "MaxTessEvaluationUniformComponents " << DefaultTBuiltInResource.maxTessEvaluationUniformComponents << "\n" + << "MaxTessPatchComponents " << DefaultTBuiltInResource.maxTessPatchComponents << "\n" + << "MaxPatchVertices " << DefaultTBuiltInResource.maxPatchVertices << "\n" + << "MaxTessGenLevel " << DefaultTBuiltInResource.maxTessGenLevel << "\n" + << "MaxViewports " << DefaultTBuiltInResource.maxViewports << "\n" + << "MaxVertexAtomicCounters " << DefaultTBuiltInResource.maxVertexAtomicCounters << "\n" + << "MaxTessControlAtomicCounters " << DefaultTBuiltInResource.maxTessControlAtomicCounters << "\n" + << "MaxTessEvaluationAtomicCounters " << DefaultTBuiltInResource.maxTessEvaluationAtomicCounters << "\n" + << "MaxGeometryAtomicCounters " << DefaultTBuiltInResource.maxGeometryAtomicCounters << "\n" + << "MaxFragmentAtomicCounters " << DefaultTBuiltInResource.maxFragmentAtomicCounters << "\n" + << "MaxCombinedAtomicCounters " << DefaultTBuiltInResource.maxCombinedAtomicCounters << "\n" + << "MaxAtomicCounterBindings " << DefaultTBuiltInResource.maxAtomicCounterBindings << "\n" + << "MaxVertexAtomicCounterBuffers " << DefaultTBuiltInResource.maxVertexAtomicCounterBuffers << "\n" + << "MaxTessControlAtomicCounterBuffers " << DefaultTBuiltInResource.maxTessControlAtomicCounterBuffers << "\n" + << "MaxTessEvaluationAtomicCounterBuffers " << DefaultTBuiltInResource.maxTessEvaluationAtomicCounterBuffers << "\n" + << "MaxGeometryAtomicCounterBuffers " << DefaultTBuiltInResource.maxGeometryAtomicCounterBuffers << "\n" + << "MaxFragmentAtomicCounterBuffers " << DefaultTBuiltInResource.maxFragmentAtomicCounterBuffers << "\n" + << "MaxCombinedAtomicCounterBuffers " << DefaultTBuiltInResource.maxCombinedAtomicCounterBuffers << "\n" + << "MaxAtomicCounterBufferSize " << DefaultTBuiltInResource.maxAtomicCounterBufferSize << "\n" + << "MaxTransformFeedbackBuffers " << DefaultTBuiltInResource.maxTransformFeedbackBuffers << "\n" + << "MaxTransformFeedbackInterleavedComponents " << DefaultTBuiltInResource.maxTransformFeedbackInterleavedComponents << "\n" + << "MaxCullDistances " << DefaultTBuiltInResource.maxCullDistances << "\n" + << "MaxCombinedClipAndCullDistances " << DefaultTBuiltInResource.maxCombinedClipAndCullDistances << "\n" + << "MaxSamples " << DefaultTBuiltInResource.maxSamples << "\n" + << "MaxMeshOutputVerticesNV " << DefaultTBuiltInResource.maxMeshOutputVerticesNV << "\n" + << "MaxMeshOutputPrimitivesNV " << DefaultTBuiltInResource.maxMeshOutputPrimitivesNV << "\n" + << "MaxMeshWorkGroupSizeX_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeX_NV << "\n" + << "MaxMeshWorkGroupSizeY_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeY_NV << "\n" + << "MaxMeshWorkGroupSizeZ_NV " << DefaultTBuiltInResource.maxMeshWorkGroupSizeZ_NV << "\n" + << "MaxTaskWorkGroupSizeX_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeX_NV << "\n" + << "MaxTaskWorkGroupSizeY_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeY_NV << "\n" + << "MaxTaskWorkGroupSizeZ_NV " << DefaultTBuiltInResource.maxTaskWorkGroupSizeZ_NV << "\n" + << "MaxMeshViewCountNV " << DefaultTBuiltInResource.maxMeshViewCountNV << "\n" + << "MaxDualSourceDrawBuffersEXT " << DefaultTBuiltInResource.maxDualSourceDrawBuffersEXT << "\n" + << "nonInductiveForLoops " << DefaultTBuiltInResource.limits.nonInductiveForLoops << "\n" + << "whileLoops " << DefaultTBuiltInResource.limits.whileLoops << "\n" + << "doWhileLoops " << DefaultTBuiltInResource.limits.doWhileLoops << "\n" + << "generalUniformIndexing " << DefaultTBuiltInResource.limits.generalUniformIndexing << "\n" + << "generalAttributeMatrixVectorIndexing " << DefaultTBuiltInResource.limits.generalAttributeMatrixVectorIndexing << "\n" + << "generalVaryingIndexing " << DefaultTBuiltInResource.limits.generalVaryingIndexing << "\n" + << "generalSamplerIndexing " << DefaultTBuiltInResource.limits.generalSamplerIndexing << "\n" + << "generalVariableIndexing " << DefaultTBuiltInResource.limits.generalVariableIndexing << "\n" + << "generalConstantMatrixVectorIndexing " << DefaultTBuiltInResource.limits.generalConstantMatrixVectorIndexing << "\n" + ; + + return ostream.str(); +} + +void DecodeResourceLimits(TBuiltInResource* resources, char* config) +{ + static const char* delims = " \t\n\r"; + + size_t pos = 0; + std::string configStr(config); + + while ((pos = configStr.find_first_not_of(delims, pos)) != std::string::npos) { + const size_t token_s = pos; + const size_t token_e = configStr.find_first_of(delims, token_s); + const size_t value_s = configStr.find_first_not_of(delims, token_e); + const size_t value_e = configStr.find_first_of(delims, value_s); + pos = value_e; + + // Faster to use compare(), but prefering readability. + const std::string tokenStr = configStr.substr(token_s, token_e-token_s); + const std::string valueStr = configStr.substr(value_s, value_e-value_s); + + if (value_s == std::string::npos || ! (valueStr[0] == '-' || isdigit(valueStr[0]))) { + printf("Error: '%s' bad .conf file. Each name must be followed by one number.\n", + valueStr.c_str()); + return; + } + + const int value = std::atoi(valueStr.c_str()); + + if (tokenStr == "MaxLights") + resources->maxLights = value; + else if (tokenStr == "MaxClipPlanes") + resources->maxClipPlanes = value; + else if (tokenStr == "MaxTextureUnits") + resources->maxTextureUnits = value; + else if (tokenStr == "MaxTextureCoords") + resources->maxTextureCoords = value; + else if (tokenStr == "MaxVertexAttribs") + resources->maxVertexAttribs = value; + else if (tokenStr == "MaxVertexUniformComponents") + resources->maxVertexUniformComponents = value; + else if (tokenStr == "MaxVaryingFloats") + resources->maxVaryingFloats = value; + else if (tokenStr == "MaxVertexTextureImageUnits") + resources->maxVertexTextureImageUnits = value; + else if (tokenStr == "MaxCombinedTextureImageUnits") + resources->maxCombinedTextureImageUnits = value; + else if (tokenStr == "MaxTextureImageUnits") + resources->maxTextureImageUnits = value; + else if (tokenStr == "MaxFragmentUniformComponents") + resources->maxFragmentUniformComponents = value; + else if (tokenStr == "MaxDrawBuffers") + resources->maxDrawBuffers = value; + else if (tokenStr == "MaxVertexUniformVectors") + resources->maxVertexUniformVectors = value; + else if (tokenStr == "MaxVaryingVectors") + resources->maxVaryingVectors = value; + else if (tokenStr == "MaxFragmentUniformVectors") + resources->maxFragmentUniformVectors = value; + else if (tokenStr == "MaxVertexOutputVectors") + resources->maxVertexOutputVectors = value; + else if (tokenStr == "MaxFragmentInputVectors") + resources->maxFragmentInputVectors = value; + else if (tokenStr == "MinProgramTexelOffset") + resources->minProgramTexelOffset = value; + else if (tokenStr == "MaxProgramTexelOffset") + resources->maxProgramTexelOffset = value; + else if (tokenStr == "MaxClipDistances") + resources->maxClipDistances = value; + else if (tokenStr == "MaxComputeWorkGroupCountX") + resources->maxComputeWorkGroupCountX = value; + else if (tokenStr == "MaxComputeWorkGroupCountY") + resources->maxComputeWorkGroupCountY = value; + else if (tokenStr == "MaxComputeWorkGroupCountZ") + resources->maxComputeWorkGroupCountZ = value; + else if (tokenStr == "MaxComputeWorkGroupSizeX") + resources->maxComputeWorkGroupSizeX = value; + else if (tokenStr == "MaxComputeWorkGroupSizeY") + resources->maxComputeWorkGroupSizeY = value; + else if (tokenStr == "MaxComputeWorkGroupSizeZ") + resources->maxComputeWorkGroupSizeZ = value; + else if (tokenStr == "MaxComputeUniformComponents") + resources->maxComputeUniformComponents = value; + else if (tokenStr == "MaxComputeTextureImageUnits") + resources->maxComputeTextureImageUnits = value; + else if (tokenStr == "MaxComputeImageUniforms") + resources->maxComputeImageUniforms = value; + else if (tokenStr == "MaxComputeAtomicCounters") + resources->maxComputeAtomicCounters = value; + else if (tokenStr == "MaxComputeAtomicCounterBuffers") + resources->maxComputeAtomicCounterBuffers = value; + else if (tokenStr == "MaxVaryingComponents") + resources->maxVaryingComponents = value; + else if (tokenStr == "MaxVertexOutputComponents") + resources->maxVertexOutputComponents = value; + else if (tokenStr == "MaxGeometryInputComponents") + resources->maxGeometryInputComponents = value; + else if (tokenStr == "MaxGeometryOutputComponents") + resources->maxGeometryOutputComponents = value; + else if (tokenStr == "MaxFragmentInputComponents") + resources->maxFragmentInputComponents = value; + else if (tokenStr == "MaxImageUnits") + resources->maxImageUnits = value; + else if (tokenStr == "MaxCombinedImageUnitsAndFragmentOutputs") + resources->maxCombinedImageUnitsAndFragmentOutputs = value; + else if (tokenStr == "MaxCombinedShaderOutputResources") + resources->maxCombinedShaderOutputResources = value; + else if (tokenStr == "MaxImageSamples") + resources->maxImageSamples = value; + else if (tokenStr == "MaxVertexImageUniforms") + resources->maxVertexImageUniforms = value; + else if (tokenStr == "MaxTessControlImageUniforms") + resources->maxTessControlImageUniforms = value; + else if (tokenStr == "MaxTessEvaluationImageUniforms") + resources->maxTessEvaluationImageUniforms = value; + else if (tokenStr == "MaxGeometryImageUniforms") + resources->maxGeometryImageUniforms = value; + else if (tokenStr == "MaxFragmentImageUniforms") + resources->maxFragmentImageUniforms = value; + else if (tokenStr == "MaxCombinedImageUniforms") + resources->maxCombinedImageUniforms = value; + else if (tokenStr == "MaxGeometryTextureImageUnits") + resources->maxGeometryTextureImageUnits = value; + else if (tokenStr == "MaxGeometryOutputVertices") + resources->maxGeometryOutputVertices = value; + else if (tokenStr == "MaxGeometryTotalOutputComponents") + resources->maxGeometryTotalOutputComponents = value; + else if (tokenStr == "MaxGeometryUniformComponents") + resources->maxGeometryUniformComponents = value; + else if (tokenStr == "MaxGeometryVaryingComponents") + resources->maxGeometryVaryingComponents = value; + else if (tokenStr == "MaxTessControlInputComponents") + resources->maxTessControlInputComponents = value; + else if (tokenStr == "MaxTessControlOutputComponents") + resources->maxTessControlOutputComponents = value; + else if (tokenStr == "MaxTessControlTextureImageUnits") + resources->maxTessControlTextureImageUnits = value; + else if (tokenStr == "MaxTessControlUniformComponents") + resources->maxTessControlUniformComponents = value; + else if (tokenStr == "MaxTessControlTotalOutputComponents") + resources->maxTessControlTotalOutputComponents = value; + else if (tokenStr == "MaxTessEvaluationInputComponents") + resources->maxTessEvaluationInputComponents = value; + else if (tokenStr == "MaxTessEvaluationOutputComponents") + resources->maxTessEvaluationOutputComponents = value; + else if (tokenStr == "MaxTessEvaluationTextureImageUnits") + resources->maxTessEvaluationTextureImageUnits = value; + else if (tokenStr == "MaxTessEvaluationUniformComponents") + resources->maxTessEvaluationUniformComponents = value; + else if (tokenStr == "MaxTessPatchComponents") + resources->maxTessPatchComponents = value; + else if (tokenStr == "MaxPatchVertices") + resources->maxPatchVertices = value; + else if (tokenStr == "MaxTessGenLevel") + resources->maxTessGenLevel = value; + else if (tokenStr == "MaxViewports") + resources->maxViewports = value; + else if (tokenStr == "MaxVertexAtomicCounters") + resources->maxVertexAtomicCounters = value; + else if (tokenStr == "MaxTessControlAtomicCounters") + resources->maxTessControlAtomicCounters = value; + else if (tokenStr == "MaxTessEvaluationAtomicCounters") + resources->maxTessEvaluationAtomicCounters = value; + else if (tokenStr == "MaxGeometryAtomicCounters") + resources->maxGeometryAtomicCounters = value; + else if (tokenStr == "MaxFragmentAtomicCounters") + resources->maxFragmentAtomicCounters = value; + else if (tokenStr == "MaxCombinedAtomicCounters") + resources->maxCombinedAtomicCounters = value; + else if (tokenStr == "MaxAtomicCounterBindings") + resources->maxAtomicCounterBindings = value; + else if (tokenStr == "MaxVertexAtomicCounterBuffers") + resources->maxVertexAtomicCounterBuffers = value; + else if (tokenStr == "MaxTessControlAtomicCounterBuffers") + resources->maxTessControlAtomicCounterBuffers = value; + else if (tokenStr == "MaxTessEvaluationAtomicCounterBuffers") + resources->maxTessEvaluationAtomicCounterBuffers = value; + else if (tokenStr == "MaxGeometryAtomicCounterBuffers") + resources->maxGeometryAtomicCounterBuffers = value; + else if (tokenStr == "MaxFragmentAtomicCounterBuffers") + resources->maxFragmentAtomicCounterBuffers = value; + else if (tokenStr == "MaxCombinedAtomicCounterBuffers") + resources->maxCombinedAtomicCounterBuffers = value; + else if (tokenStr == "MaxAtomicCounterBufferSize") + resources->maxAtomicCounterBufferSize = value; + else if (tokenStr == "MaxTransformFeedbackBuffers") + resources->maxTransformFeedbackBuffers = value; + else if (tokenStr == "MaxTransformFeedbackInterleavedComponents") + resources->maxTransformFeedbackInterleavedComponents = value; + else if (tokenStr == "MaxCullDistances") + resources->maxCullDistances = value; + else if (tokenStr == "MaxCombinedClipAndCullDistances") + resources->maxCombinedClipAndCullDistances = value; + else if (tokenStr == "MaxSamples") + resources->maxSamples = value; + else if (tokenStr == "MaxMeshOutputVerticesNV") + resources->maxMeshOutputVerticesNV = value; + else if (tokenStr == "MaxMeshOutputPrimitivesNV") + resources->maxMeshOutputPrimitivesNV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeX_NV") + resources->maxMeshWorkGroupSizeX_NV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeY_NV") + resources->maxMeshWorkGroupSizeY_NV = value; + else if (tokenStr == "MaxMeshWorkGroupSizeZ_NV") + resources->maxMeshWorkGroupSizeZ_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeX_NV") + resources->maxTaskWorkGroupSizeX_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeY_NV") + resources->maxTaskWorkGroupSizeY_NV = value; + else if (tokenStr == "MaxTaskWorkGroupSizeZ_NV") + resources->maxTaskWorkGroupSizeZ_NV = value; + else if (tokenStr == "MaxMeshViewCountNV") + resources->maxMeshViewCountNV = value; + else if (tokenStr == "nonInductiveForLoops") + resources->limits.nonInductiveForLoops = (value != 0); + else if (tokenStr == "whileLoops") + resources->limits.whileLoops = (value != 0); + else if (tokenStr == "doWhileLoops") + resources->limits.doWhileLoops = (value != 0); + else if (tokenStr == "generalUniformIndexing") + resources->limits.generalUniformIndexing = (value != 0); + else if (tokenStr == "generalAttributeMatrixVectorIndexing") + resources->limits.generalAttributeMatrixVectorIndexing = (value != 0); + else if (tokenStr == "generalVaryingIndexing") + resources->limits.generalVaryingIndexing = (value != 0); + else if (tokenStr == "generalSamplerIndexing") + resources->limits.generalSamplerIndexing = (value != 0); + else if (tokenStr == "generalVariableIndexing") + resources->limits.generalVariableIndexing = (value != 0); + else if (tokenStr == "generalConstantMatrixVectorIndexing") + resources->limits.generalConstantMatrixVectorIndexing = (value != 0); + else + printf("Warning: unrecognized limit (%s) in configuration file.\n", tokenStr.c_str()); + + } +} + +} // end namespace glslang diff --git a/third_party/glslang/StandAlone/ResourceLimits.h b/third_party/glslang/StandAlone/ResourceLimits.h new file mode 100644 index 0000000..736248e --- /dev/null +++ b/third_party/glslang/StandAlone/ResourceLimits.h @@ -0,0 +1,57 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ +#define _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ + +#include + +#include "../glslang/Include/ResourceLimits.h" + +namespace glslang { + +// These are the default resources for TBuiltInResources, used for both +// - parsing this string for the case where the user didn't supply one, +// - dumping out a template for user construction of a config file. +extern const TBuiltInResource DefaultTBuiltInResource; + +// Returns the DefaultTBuiltInResource as a human-readable string. +std::string GetDefaultTBuiltInResourceString(); + +// Decodes the resource limits from |config| to |resources|. +void DecodeResourceLimits(TBuiltInResource* resources, char* config); + +} // end namespace glslang + +#endif // _STAND_ALONE_RESOURCE_LIMITS_INCLUDED_ diff --git a/third_party/glslang/StandAlone/StandAlone.cpp b/third_party/glslang/StandAlone/StandAlone.cpp new file mode 100644 index 0000000..1f294b0 --- /dev/null +++ b/third_party/glslang/StandAlone/StandAlone.cpp @@ -0,0 +1,1771 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2016-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// this only applies to the standalone wrapper, not the front end in general +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "ResourceLimits.h" +#include "Worklist.h" +#include "DirStackFileIncluder.h" +#include "./../glslang/Include/ShHandle.h" +#include "./../glslang/Public/ShaderLang.h" +#include "../SPIRV/GlslangToSpv.h" +#include "../SPIRV/GLSL.std.450.h" +#include "../SPIRV/doc.h" +#include "../SPIRV/disassemble.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../glslang/OSDependent/osinclude.h" + +// Build-time generated includes +#include "glslang/build_info.h" + +extern "C" { + GLSLANG_EXPORT void ShOutputHtml(); +} + +// Command-line options +enum TOptions { + EOptionNone = 0, + EOptionIntermediate = (1 << 0), + EOptionSuppressInfolog = (1 << 1), + EOptionMemoryLeakMode = (1 << 2), + EOptionRelaxedErrors = (1 << 3), + EOptionGiveWarnings = (1 << 4), + EOptionLinkProgram = (1 << 5), + EOptionMultiThreaded = (1 << 6), + EOptionDumpConfig = (1 << 7), + EOptionDumpReflection = (1 << 8), + EOptionSuppressWarnings = (1 << 9), + EOptionDumpVersions = (1 << 10), + EOptionSpv = (1 << 11), + EOptionHumanReadableSpv = (1 << 12), + EOptionVulkanRules = (1 << 13), + EOptionDefaultDesktop = (1 << 14), + EOptionOutputPreprocessed = (1 << 15), + EOptionOutputHexadecimal = (1 << 16), + EOptionReadHlsl = (1 << 17), + EOptionCascadingErrors = (1 << 18), + EOptionAutoMapBindings = (1 << 19), + EOptionFlattenUniformArrays = (1 << 20), + EOptionNoStorageFormat = (1 << 21), + EOptionKeepUncalled = (1 << 22), + EOptionHlslOffsets = (1 << 23), + EOptionHlslIoMapping = (1 << 24), + EOptionAutoMapLocations = (1 << 25), + EOptionDebug = (1 << 26), + EOptionStdin = (1 << 27), + EOptionOptimizeDisable = (1 << 28), + EOptionOptimizeSize = (1 << 29), + EOptionInvertY = (1 << 30), + EOptionDumpBareVersion = (1 << 31), +}; +bool targetHlslFunctionality1 = false; +bool SpvToolsDisassembler = false; +bool SpvToolsValidate = false; +bool NaNClamp = false; +bool stripDebugInfo = false; +bool beQuiet = false; + +// +// Return codes from main/exit(). +// +enum TFailCode { + ESuccess = 0, + EFailUsage, + EFailCompile, + EFailLink, + EFailCompilerCreate, + EFailThreadCreate, + EFailLinkerCreate +}; + +// +// Forward declarations. +// +EShLanguage FindLanguage(const std::string& name, bool parseSuffix=true); +void CompileFile(const char* fileName, ShHandle); +void usage(); +char* ReadFileData(const char* fileName); +void FreeFileData(char* data); +void InfoLogMsg(const char* msg, const char* name, const int num); + +// Globally track if any compile or link failure. +bool CompileFailed = false; +bool LinkFailed = false; + +// array of unique places to leave the shader names and infologs for the asynchronous compiles +std::vector> WorkItems; + +TBuiltInResource Resources; +std::string ConfigFile; + +// +// Parse either a .conf file provided by the user or the default from glslang::DefaultTBuiltInResource +// +void ProcessConfigFile() +{ + if (ConfigFile.size() == 0) + Resources = glslang::DefaultTBuiltInResource; +#ifndef GLSLANG_WEB + else { + char* configString = ReadFileData(ConfigFile.c_str()); + glslang::DecodeResourceLimits(&Resources, configString); + FreeFileData(configString); + } +#endif +} + +int ReflectOptions = EShReflectionDefault; +int Options = 0; +const char* ExecutableName = nullptr; +const char* binaryFileName = nullptr; +const char* entryPointName = nullptr; +const char* sourceEntryPointName = nullptr; +const char* shaderStageName = nullptr; +const char* variableName = nullptr; +bool HlslEnable16BitTypes = false; +bool HlslDX9compatible = false; +bool DumpBuiltinSymbols = false; +std::vector IncludeDirectoryList; + +// Source environment +// (source 'Client' is currently the same as target 'Client') +int ClientInputSemanticsVersion = 100; + +// Target environment +glslang::EShClient Client = glslang::EShClientNone; // will stay EShClientNone if only validating +glslang::EShTargetClientVersion ClientVersion; // not valid until Client is set +glslang::EShTargetLanguage TargetLanguage = glslang::EShTargetNone; +glslang::EShTargetLanguageVersion TargetVersion; // not valid until TargetLanguage is set + +std::vector Processes; // what should be recorded by OpModuleProcessed, or equivalent + +// Per descriptor-set binding base data +typedef std::map TPerSetBaseBinding; + +std::vector> uniformLocationOverrides; +int uniformBase = 0; + +std::array, glslang::EResCount> baseBinding; +std::array, glslang::EResCount> baseBindingForSet; +std::array, EShLangCount> baseResourceSetBinding; + +// Add things like "#define ..." to a preamble to use in the beginning of the shader. +class TPreamble { +public: + TPreamble() { } + + bool isSet() const { return text.size() > 0; } + const char* get() const { return text.c_str(); } + + // #define... + void addDef(std::string def) + { + text.append("#define "); + fixLine(def); + + Processes.push_back("define-macro "); + Processes.back().append(def); + + // The first "=" needs to turn into a space + const size_t equal = def.find_first_of("="); + if (equal != def.npos) + def[equal] = ' '; + + text.append(def); + text.append("\n"); + } + + // #undef... + void addUndef(std::string undef) + { + text.append("#undef "); + fixLine(undef); + + Processes.push_back("undef-macro "); + Processes.back().append(undef); + + text.append(undef); + text.append("\n"); + } + +protected: + void fixLine(std::string& line) + { + // Can't go past a newline in the line + const size_t end = line.find_first_of("\n"); + if (end != line.npos) + line = line.substr(0, end); + } + + std::string text; // contents of preamble +}; + +// Track the user's #define and #undef from the command line. +TPreamble UserPreamble; + +// +// Create the default name for saving a binary if -o is not provided. +// +const char* GetBinaryName(EShLanguage stage) +{ + const char* name; + if (binaryFileName == nullptr) { + switch (stage) { + case EShLangVertex: name = "vert.spv"; break; + case EShLangTessControl: name = "tesc.spv"; break; + case EShLangTessEvaluation: name = "tese.spv"; break; + case EShLangGeometry: name = "geom.spv"; break; + case EShLangFragment: name = "frag.spv"; break; + case EShLangCompute: name = "comp.spv"; break; + case EShLangRayGen: name = "rgen.spv"; break; + case EShLangIntersect: name = "rint.spv"; break; + case EShLangAnyHit: name = "rahit.spv"; break; + case EShLangClosestHit: name = "rchit.spv"; break; + case EShLangMiss: name = "rmiss.spv"; break; + case EShLangCallable: name = "rcall.spv"; break; + case EShLangMeshNV: name = "mesh.spv"; break; + case EShLangTaskNV: name = "task.spv"; break; + default: name = "unknown"; break; + } + } else + name = binaryFileName; + + return name; +} + +// +// *.conf => this is a config file that can set limits/resources +// +bool SetConfigFile(const std::string& name) +{ + if (name.size() < 5) + return false; + + if (name.compare(name.size() - 5, 5, ".conf") == 0) { + ConfigFile = name; + return true; + } + + return false; +} + +// +// Give error and exit with failure code. +// +void Error(const char* message, const char* detail = nullptr) +{ + fprintf(stderr, "%s: Error: ", ExecutableName); + if (detail != nullptr) + fprintf(stderr, "%s: ", detail); + fprintf(stderr, "%s (use -h for usage)\n", message); + exit(EFailUsage); +} + +// +// Process an optional binding base of one the forms: +// --argname [stage] base // base for stage (if given) or all stages (if not) +// --argname [stage] [base set]... // set/base pairs: set the base for given binding set. + +// Where stage is one of the forms accepted by FindLanguage, and base is an integer +// +void ProcessBindingBase(int& argc, char**& argv, glslang::TResourceType res) +{ + if (argc < 2) + usage(); + + EShLanguage lang = EShLangCount; + int singleBase = 0; + TPerSetBaseBinding perSetBase; + int arg = 1; + + // Parse stage, if given + if (!isdigit(argv[arg][0])) { + if (argc < 3) // this form needs one more argument + usage(); + + lang = FindLanguage(argv[arg++], false); + } + + if ((argc - arg) > 2 && isdigit(argv[arg+0][0]) && isdigit(argv[arg+1][0])) { + // Parse a per-set binding base + while ((argc - arg) > 2 && isdigit(argv[arg+0][0]) && isdigit(argv[arg+1][0])) { + const int baseNum = atoi(argv[arg++]); + const int setNum = atoi(argv[arg++]); + perSetBase[setNum] = baseNum; + } + } else { + // Parse single binding base + singleBase = atoi(argv[arg++]); + } + + argc -= (arg-1); + argv += (arg-1); + + // Set one or all languages + const int langMin = (lang < EShLangCount) ? lang+0 : 0; + const int langMax = (lang < EShLangCount) ? lang+1 : EShLangCount; + + for (int lang = langMin; lang < langMax; ++lang) { + if (!perSetBase.empty()) + baseBindingForSet[res][lang].insert(perSetBase.begin(), perSetBase.end()); + else + baseBinding[res][lang] = singleBase; + } +} + +void ProcessResourceSetBindingBase(int& argc, char**& argv, std::array, EShLangCount>& base) +{ + if (argc < 2) + usage(); + + if (!isdigit(argv[1][0])) { + if (argc < 3) // this form needs one more argument + usage(); + + // Parse form: --argname stage [regname set base...], or: + // --argname stage set + const EShLanguage lang = FindLanguage(argv[1], false); + + argc--; + argv++; + + while (argc > 1 && argv[1] != nullptr && argv[1][0] != '-') { + base[lang].push_back(argv[1]); + + argc--; + argv++; + } + + // Must have one arg, or a multiple of three (for [regname set binding] triples) + if (base[lang].size() != 1 && (base[lang].size() % 3) != 0) + usage(); + + } else { + // Parse form: --argname set + for (int lang=0; lang>& workItems, int argc, char* argv[]) +{ + for (int res = 0; res < glslang::EResCount; ++res) + baseBinding[res].fill(0); + + ExecutableName = argv[0]; + workItems.reserve(argc); + + const auto bumpArg = [&]() { + if (argc > 0) { + argc--; + argv++; + } + }; + + // read a string directly attached to a single-letter option + const auto getStringOperand = [&](const char* desc) { + if (argv[0][2] == 0) { + printf("%s must immediately follow option (no spaces)\n", desc); + exit(EFailUsage); + } + return argv[0] + 2; + }; + + // read a number attached to a single-letter option + const auto getAttachedNumber = [&](const char* desc) { + int num = atoi(argv[0] + 2); + if (num == 0) { + printf("%s: expected attached non-0 number\n", desc); + exit(EFailUsage); + } + return num; + }; + + // minimum needed (without overriding something else) to target Vulkan SPIR-V + const auto setVulkanSpv = []() { + if (Client == glslang::EShClientNone) + ClientVersion = glslang::EShTargetVulkan_1_0; + Client = glslang::EShClientVulkan; + Options |= EOptionSpv; + Options |= EOptionVulkanRules; + Options |= EOptionLinkProgram; + }; + + // minimum needed (without overriding something else) to target OpenGL SPIR-V + const auto setOpenGlSpv = []() { + if (Client == glslang::EShClientNone) + ClientVersion = glslang::EShTargetOpenGL_450; + Client = glslang::EShClientOpenGL; + Options |= EOptionSpv; + Options |= EOptionLinkProgram; + // undo a -H default to Vulkan + Options &= ~EOptionVulkanRules; + }; + + const auto getUniformOverride = [getStringOperand]() { + const char *arg = getStringOperand("-u:"); + const char *split = strchr(arg, ':'); + if (split == NULL) { + printf("%s: missing location\n", arg); + exit(EFailUsage); + } + errno = 0; + int location = ::strtol(split + 1, NULL, 10); + if (errno) { + printf("%s: invalid location\n", arg); + exit(EFailUsage); + } + return std::make_pair(std::string(arg, split - arg), location); + }; + + for (bumpArg(); argc >= 1; bumpArg()) { + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case '-': + { + std::string lowerword(argv[0]+2); + std::transform(lowerword.begin(), lowerword.end(), lowerword.begin(), ::tolower); + + // handle --word style options + if (lowerword == "auto-map-bindings" || // synonyms + lowerword == "auto-map-binding" || + lowerword == "amb") { + Options |= EOptionAutoMapBindings; + } else if (lowerword == "auto-map-locations" || // synonyms + lowerword == "aml") { + Options |= EOptionAutoMapLocations; + } else if (lowerword == "uniform-base") { + if (argc <= 1) + Error("no provided", lowerword.c_str()); + uniformBase = ::strtol(argv[1], NULL, 10); + bumpArg(); + break; + } else if (lowerword == "client") { + if (argc > 1) { + if (strcmp(argv[1], "vulkan100") == 0) + setVulkanSpv(); + else if (strcmp(argv[1], "opengl100") == 0) + setOpenGlSpv(); + else + Error("expects vulkan100 or opengl100", lowerword.c_str()); + } else + Error("expects vulkan100 or opengl100", lowerword.c_str()); + bumpArg(); + } else if (lowerword == "define-macro" || + lowerword == "d") { + if (argc > 1) + UserPreamble.addDef(argv[1]); + else + Error("expects ", argv[0]); + bumpArg(); + } else if (lowerword == "dump-builtin-symbols") { + DumpBuiltinSymbols = true; + } else if (lowerword == "entry-point") { + entryPointName = argv[1]; + if (argc <= 1) + Error("no provided", lowerword.c_str()); + bumpArg(); + } else if (lowerword == "flatten-uniform-arrays" || // synonyms + lowerword == "flatten-uniform-array" || + lowerword == "fua") { + Options |= EOptionFlattenUniformArrays; + } else if (lowerword == "hlsl-offsets") { + Options |= EOptionHlslOffsets; + } else if (lowerword == "hlsl-iomap" || + lowerword == "hlsl-iomapper" || + lowerword == "hlsl-iomapping") { + Options |= EOptionHlslIoMapping; + } else if (lowerword == "hlsl-enable-16bit-types") { + HlslEnable16BitTypes = true; + } else if (lowerword == "hlsl-dx9-compatible") { + HlslDX9compatible = true; + } else if (lowerword == "invert-y" || // synonyms + lowerword == "iy") { + Options |= EOptionInvertY; + } else if (lowerword == "keep-uncalled" || // synonyms + lowerword == "ku") { + Options |= EOptionKeepUncalled; + } else if (lowerword == "nan-clamp") { + NaNClamp = true; + } else if (lowerword == "no-storage-format" || // synonyms + lowerword == "nsf") { + Options |= EOptionNoStorageFormat; + } else if (lowerword == "relaxed-errors") { + Options |= EOptionRelaxedErrors; + } else if (lowerword == "reflect-strict-array-suffix") { + ReflectOptions |= EShReflectionStrictArraySuffix; + } else if (lowerword == "reflect-basic-array-suffix") { + ReflectOptions |= EShReflectionBasicArraySuffix; + } else if (lowerword == "reflect-intermediate-io") { + ReflectOptions |= EShReflectionIntermediateIO; + } else if (lowerword == "reflect-separate-buffers") { + ReflectOptions |= EShReflectionSeparateBuffers; + } else if (lowerword == "reflect-all-block-variables") { + ReflectOptions |= EShReflectionAllBlockVariables; + } else if (lowerword == "reflect-unwrap-io-blocks") { + ReflectOptions |= EShReflectionUnwrapIOBlocks; + } else if (lowerword == "reflect-all-io-variables") { + ReflectOptions |= EShReflectionAllIOVariables; + } else if (lowerword == "reflect-shared-std140-ubo") { + ReflectOptions |= EShReflectionSharedStd140UBO; + } else if (lowerword == "reflect-shared-std140-ssbo") { + ReflectOptions |= EShReflectionSharedStd140SSBO; + } else if (lowerword == "resource-set-bindings" || // synonyms + lowerword == "resource-set-binding" || + lowerword == "rsb") { + ProcessResourceSetBindingBase(argc, argv, baseResourceSetBinding); + } else if (lowerword == "shift-image-bindings" || // synonyms + lowerword == "shift-image-binding" || + lowerword == "sib") { + ProcessBindingBase(argc, argv, glslang::EResImage); + } else if (lowerword == "shift-sampler-bindings" || // synonyms + lowerword == "shift-sampler-binding" || + lowerword == "ssb") { + ProcessBindingBase(argc, argv, glslang::EResSampler); + } else if (lowerword == "shift-uav-bindings" || // synonyms + lowerword == "shift-uav-binding" || + lowerword == "suavb") { + ProcessBindingBase(argc, argv, glslang::EResUav); + } else if (lowerword == "shift-texture-bindings" || // synonyms + lowerword == "shift-texture-binding" || + lowerword == "stb") { + ProcessBindingBase(argc, argv, glslang::EResTexture); + } else if (lowerword == "shift-ubo-bindings" || // synonyms + lowerword == "shift-ubo-binding" || + lowerword == "shift-cbuffer-bindings" || + lowerword == "shift-cbuffer-binding" || + lowerword == "sub" || + lowerword == "scb") { + ProcessBindingBase(argc, argv, glslang::EResUbo); + } else if (lowerword == "shift-ssbo-bindings" || // synonyms + lowerword == "shift-ssbo-binding" || + lowerword == "sbb") { + ProcessBindingBase(argc, argv, glslang::EResSsbo); + } else if (lowerword == "source-entrypoint" || // synonyms + lowerword == "sep") { + if (argc <= 1) + Error("no provided", lowerword.c_str()); + sourceEntryPointName = argv[1]; + bumpArg(); + break; + } else if (lowerword == "spirv-dis") { + SpvToolsDisassembler = true; + } else if (lowerword == "spirv-val") { + SpvToolsValidate = true; + } else if (lowerword == "stdin") { + Options |= EOptionStdin; + shaderStageName = argv[1]; + } else if (lowerword == "suppress-warnings") { + Options |= EOptionSuppressWarnings; + } else if (lowerword == "target-env") { + if (argc > 1) { + if (strcmp(argv[1], "vulkan1.0") == 0) { + setVulkanSpv(); + ClientVersion = glslang::EShTargetVulkan_1_0; + } else if (strcmp(argv[1], "vulkan1.1") == 0) { + setVulkanSpv(); + ClientVersion = glslang::EShTargetVulkan_1_1; + } else if (strcmp(argv[1], "vulkan1.2") == 0) { + setVulkanSpv(); + ClientVersion = glslang::EShTargetVulkan_1_2; + } else if (strcmp(argv[1], "opengl") == 0) { + setOpenGlSpv(); + ClientVersion = glslang::EShTargetOpenGL_450; + } else if (strcmp(argv[1], "spirv1.0") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_0; + } else if (strcmp(argv[1], "spirv1.1") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_1; + } else if (strcmp(argv[1], "spirv1.2") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_2; + } else if (strcmp(argv[1], "spirv1.3") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_3; + } else if (strcmp(argv[1], "spirv1.4") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_4; + } else if (strcmp(argv[1], "spirv1.5") == 0) { + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_5; + } else + Error("--target-env expected one of: vulkan1.0, vulkan1.1, vulkan1.2, opengl,\n" + "spirv1.0, spirv1.1, spirv1.2, spirv1.3, spirv1.4, or spirv1.5"); + } + bumpArg(); + } else if (lowerword == "undef-macro" || + lowerword == "u") { + if (argc > 1) + UserPreamble.addUndef(argv[1]); + else + Error("expects ", argv[0]); + bumpArg(); + } else if (lowerword == "variable-name" || // synonyms + lowerword == "vn") { + Options |= EOptionOutputHexadecimal; + if (argc <= 1) + Error("no provided", lowerword.c_str()); + variableName = argv[1]; + bumpArg(); + break; + } else if (lowerword == "quiet") { + beQuiet = true; + } else if (lowerword == "version") { + Options |= EOptionDumpVersions; + } else if (lowerword == "help") { + usage(); + break; + } else { + Error("unrecognized command-line option", argv[0]); + } + } + break; + case 'C': + Options |= EOptionCascadingErrors; + break; + case 'D': + if (argv[0][2] == 0) + Options |= EOptionReadHlsl; + else + UserPreamble.addDef(getStringOperand("-D")); + break; + case 'u': + uniformLocationOverrides.push_back(getUniformOverride()); + break; + case 'E': + Options |= EOptionOutputPreprocessed; + break; + case 'G': + // OpenGL client + setOpenGlSpv(); + if (argv[0][2] != 0) + ClientInputSemanticsVersion = getAttachedNumber("-G client input semantics"); + if (ClientInputSemanticsVersion != 100) + Error("unknown client version for -G, should be 100"); + break; + case 'H': + Options |= EOptionHumanReadableSpv; + if ((Options & EOptionSpv) == 0) { + // default to Vulkan + setVulkanSpv(); + } + break; + case 'I': + IncludeDirectoryList.push_back(getStringOperand("-I include path")); + break; + case 'O': + if (argv[0][2] == 'd') + Options |= EOptionOptimizeDisable; + else if (argv[0][2] == 's') +#if ENABLE_OPT + Options |= EOptionOptimizeSize; +#else + Error("-Os not available; optimizer not linked"); +#endif + else + Error("unknown -O option"); + break; + case 'S': + if (argc <= 1) + Error("no specified for -S"); + shaderStageName = argv[1]; + bumpArg(); + break; + case 'U': + UserPreamble.addUndef(getStringOperand("-U")); + break; + case 'V': + setVulkanSpv(); + if (argv[0][2] != 0) + ClientInputSemanticsVersion = getAttachedNumber("-V client input semantics"); + if (ClientInputSemanticsVersion != 100) + Error("unknown client version for -V, should be 100"); + break; + case 'c': + Options |= EOptionDumpConfig; + break; + case 'd': + if (strncmp(&argv[0][1], "dumpversion", strlen(&argv[0][1]) + 1) == 0 || + strncmp(&argv[0][1], "dumpfullversion", strlen(&argv[0][1]) + 1) == 0) + Options |= EOptionDumpBareVersion; + else + Options |= EOptionDefaultDesktop; + break; + case 'e': + entryPointName = argv[1]; + if (argc <= 1) + Error("no provided for -e"); + bumpArg(); + break; + case 'f': + if (strcmp(&argv[0][2], "hlsl_functionality1") == 0) + targetHlslFunctionality1 = true; + else + Error("-f: expected hlsl_functionality1"); + break; + case 'g': + // Override previous -g or -g0 argument + stripDebugInfo = false; + Options &= ~EOptionDebug; + if (argv[0][2] == '0') + stripDebugInfo = true; + else + Options |= EOptionDebug; + break; + case 'h': + usage(); + break; + case 'i': + Options |= EOptionIntermediate; + break; + case 'l': + Options |= EOptionLinkProgram; + break; + case 'm': + Options |= EOptionMemoryLeakMode; + break; + case 'o': + if (argc <= 1) + Error("no provided for -o"); + binaryFileName = argv[1]; + bumpArg(); + break; + case 'q': + Options |= EOptionDumpReflection; + break; + case 'r': + Options |= EOptionRelaxedErrors; + break; + case 's': + Options |= EOptionSuppressInfolog; + break; + case 't': + Options |= EOptionMultiThreaded; + break; + case 'v': + Options |= EOptionDumpVersions; + break; + case 'w': + Options |= EOptionSuppressWarnings; + break; + case 'x': + Options |= EOptionOutputHexadecimal; + break; + default: + Error("unrecognized command-line option", argv[0]); + break; + } + } else { + std::string name(argv[0]); + if (! SetConfigFile(name)) { + workItems.push_back(std::unique_ptr(new glslang::TWorkItem(name))); + } + } + } + + // Make sure that -S is always specified if --stdin is specified + if ((Options & EOptionStdin) && shaderStageName == nullptr) + Error("must provide -S when --stdin is given"); + + // Make sure that -E is not specified alongside linking (which includes SPV generation) + // Or things that require linking + if (Options & EOptionOutputPreprocessed) { + if (Options & EOptionLinkProgram) + Error("can't use -E when linking is selected"); + if (Options & EOptionDumpReflection) + Error("reflection requires linking, which can't be used when -E when is selected"); + } + + // reflection requires linking + if ((Options & EOptionDumpReflection) && !(Options & EOptionLinkProgram)) + Error("reflection requires -l for linking"); + + // -o or -x makes no sense if there is no target binary + if (binaryFileName && (Options & EOptionSpv) == 0) + Error("no binary generation requested (e.g., -V)"); + + if ((Options & EOptionFlattenUniformArrays) != 0 && + (Options & EOptionReadHlsl) == 0) + Error("uniform array flattening only valid when compiling HLSL source."); + + // rationalize client and target language + if (TargetLanguage == glslang::EShTargetNone) { + switch (ClientVersion) { + case glslang::EShTargetVulkan_1_0: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_0; + break; + case glslang::EShTargetVulkan_1_1: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_3; + break; + case glslang::EShTargetVulkan_1_2: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_5; + break; + case glslang::EShTargetOpenGL_450: + TargetLanguage = glslang::EShTargetSpv; + TargetVersion = glslang::EShTargetSpv_1_0; + break; + default: + break; + } + } + if (TargetLanguage != glslang::EShTargetNone && Client == glslang::EShClientNone) + Error("To generate SPIR-V, also specify client semantics. See -G and -V."); +} + +// +// Translate the meaningful subset of command-line options to parser-behavior options. +// +void SetMessageOptions(EShMessages& messages) +{ + if (Options & EOptionRelaxedErrors) + messages = (EShMessages)(messages | EShMsgRelaxedErrors); + if (Options & EOptionIntermediate) + messages = (EShMessages)(messages | EShMsgAST); + if (Options & EOptionSuppressWarnings) + messages = (EShMessages)(messages | EShMsgSuppressWarnings); + if (Options & EOptionSpv) + messages = (EShMessages)(messages | EShMsgSpvRules); + if (Options & EOptionVulkanRules) + messages = (EShMessages)(messages | EShMsgVulkanRules); + if (Options & EOptionOutputPreprocessed) + messages = (EShMessages)(messages | EShMsgOnlyPreprocessor); + if (Options & EOptionReadHlsl) + messages = (EShMessages)(messages | EShMsgReadHlsl); + if (Options & EOptionCascadingErrors) + messages = (EShMessages)(messages | EShMsgCascadingErrors); + if (Options & EOptionKeepUncalled) + messages = (EShMessages)(messages | EShMsgKeepUncalled); + if (Options & EOptionHlslOffsets) + messages = (EShMessages)(messages | EShMsgHlslOffsets); + if (Options & EOptionDebug) + messages = (EShMessages)(messages | EShMsgDebugInfo); + if (HlslEnable16BitTypes) + messages = (EShMessages)(messages | EShMsgHlslEnable16BitTypes); + if ((Options & EOptionOptimizeDisable) || !ENABLE_OPT) + messages = (EShMessages)(messages | EShMsgHlslLegalization); + if (HlslDX9compatible) + messages = (EShMessages)(messages | EShMsgHlslDX9Compatible); + if (DumpBuiltinSymbols) + messages = (EShMessages)(messages | EShMsgBuiltinSymbolTable); +} + +// +// Thread entry point, for non-linking asynchronous mode. +// +void CompileShaders(glslang::TWorklist& worklist) +{ + if (Options & EOptionDebug) + Error("cannot generate debug information unless linking to generate code"); + + glslang::TWorkItem* workItem; + if (Options & EOptionStdin) { + if (worklist.remove(workItem)) { + ShHandle compiler = ShConstructCompiler(FindLanguage("stdin"), Options); + if (compiler == nullptr) + return; + + CompileFile("stdin", compiler); + + if (! (Options & EOptionSuppressInfolog)) + workItem->results = ShGetInfoLog(compiler); + + ShDestruct(compiler); + } + } else { + while (worklist.remove(workItem)) { + ShHandle compiler = ShConstructCompiler(FindLanguage(workItem->name), Options); + if (compiler == 0) + return; + + CompileFile(workItem->name.c_str(), compiler); + + if (! (Options & EOptionSuppressInfolog)) + workItem->results = ShGetInfoLog(compiler); + + ShDestruct(compiler); + } + } +} + +// Outputs the given string, but only if it is non-null and non-empty. +// This prevents erroneous newlines from appearing. +void PutsIfNonEmpty(const char* str) +{ + if (str && str[0]) { + puts(str); + } +} + +// Outputs the given string to stderr, but only if it is non-null and non-empty. +// This prevents erroneous newlines from appearing. +void StderrIfNonEmpty(const char* str) +{ + if (str && str[0]) + fprintf(stderr, "%s\n", str); +} + +// Simple bundling of what makes a compilation unit for ease in passing around, +// and separation of handling file IO versus API (programmatic) compilation. +struct ShaderCompUnit { + EShLanguage stage; + static const int maxCount = 1; + int count; // live number of strings/names + const char* text[maxCount]; // memory owned/managed externally + std::string fileName[maxCount]; // hold's the memory, but... + const char* fileNameList[maxCount]; // downstream interface wants pointers + + ShaderCompUnit(EShLanguage stage) : stage(stage), count(0) { } + + ShaderCompUnit(const ShaderCompUnit& rhs) + { + stage = rhs.stage; + count = rhs.count; + for (int i = 0; i < count; ++i) { + fileName[i] = rhs.fileName[i]; + text[i] = rhs.text[i]; + fileNameList[i] = rhs.fileName[i].c_str(); + } + } + + void addString(std::string& ifileName, const char* itext) + { + assert(count < maxCount); + fileName[count] = ifileName; + text[count] = itext; + fileNameList[count] = fileName[count].c_str(); + ++count; + } +}; + +// +// For linking mode: Will independently parse each compilation unit, but then put them +// in the same program and link them together, making at most one linked module per +// pipeline stage. +// +// Uses the new C++ interface instead of the old handle-based interface. +// + +void CompileAndLinkShaderUnits(std::vector compUnits) +{ + // keep track of what to free + std::list shaders; + + EShMessages messages = EShMsgDefault; + SetMessageOptions(messages); + + // + // Per-shader processing... + // + + glslang::TProgram& program = *new glslang::TProgram; + for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) { + const auto &compUnit = *it; + glslang::TShader* shader = new glslang::TShader(compUnit.stage); + shader->setStringsWithLengthsAndNames(compUnit.text, NULL, compUnit.fileNameList, compUnit.count); + if (entryPointName) + shader->setEntryPoint(entryPointName); + if (sourceEntryPointName) { + if (entryPointName == nullptr) + printf("Warning: Changing source entry point name without setting an entry-point name.\n" + "Use '-e '.\n"); + shader->setSourceEntryPoint(sourceEntryPointName); + } + if (UserPreamble.isSet()) + shader->setPreamble(UserPreamble.get()); + shader->addProcesses(Processes); + +#ifndef GLSLANG_WEB + // Set IO mapper binding shift values + for (int r = 0; r < glslang::EResCount; ++r) { + const glslang::TResourceType res = glslang::TResourceType(r); + + // Set base bindings + shader->setShiftBinding(res, baseBinding[res][compUnit.stage]); + + // Set bindings for particular resource sets + // TODO: use a range based for loop here, when available in all environments. + for (auto i = baseBindingForSet[res][compUnit.stage].begin(); + i != baseBindingForSet[res][compUnit.stage].end(); ++i) + shader->setShiftBindingForSet(res, i->second, i->first); + } + shader->setNoStorageFormat((Options & EOptionNoStorageFormat) != 0); + shader->setResourceSetBinding(baseResourceSetBinding[compUnit.stage]); + + if (Options & EOptionAutoMapBindings) + shader->setAutoMapBindings(true); + + if (Options & EOptionAutoMapLocations) + shader->setAutoMapLocations(true); + + for (auto& uniOverride : uniformLocationOverrides) { + shader->addUniformLocationOverride(uniOverride.first.c_str(), + uniOverride.second); + } + + shader->setUniformLocationBase(uniformBase); +#endif + + shader->setNanMinMaxClamp(NaNClamp); + +#ifdef ENABLE_HLSL + shader->setFlattenUniformArrays((Options & EOptionFlattenUniformArrays) != 0); + if (Options & EOptionHlslIoMapping) + shader->setHlslIoMapping(true); +#endif + + if (Options & EOptionInvertY) + shader->setInvertY(true); + + // Set up the environment, some subsettings take precedence over earlier + // ways of setting things. + if (Options & EOptionSpv) { + shader->setEnvInput((Options & EOptionReadHlsl) ? glslang::EShSourceHlsl + : glslang::EShSourceGlsl, + compUnit.stage, Client, ClientInputSemanticsVersion); + shader->setEnvClient(Client, ClientVersion); + shader->setEnvTarget(TargetLanguage, TargetVersion); +#ifdef ENABLE_HLSL + if (targetHlslFunctionality1) + shader->setEnvTargetHlslFunctionality1(); +#endif + } + + shaders.push_back(shader); + + const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100; + + DirStackFileIncluder includer; + std::for_each(IncludeDirectoryList.rbegin(), IncludeDirectoryList.rend(), [&includer](const std::string& dir) { + includer.pushExternalLocalDirectory(dir); }); +#ifndef GLSLANG_WEB + if (Options & EOptionOutputPreprocessed) { + std::string str; + if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, messages, &str, includer)) { + PutsIfNonEmpty(str.c_str()); + } else { + CompileFailed = true; + } + StderrIfNonEmpty(shader->getInfoLog()); + StderrIfNonEmpty(shader->getInfoDebugLog()); + continue; + } +#endif + + if (! shader->parse(&Resources, defaultVersion, false, messages, includer)) + CompileFailed = true; + + program.addShader(shader); + + if (! (Options & EOptionSuppressInfolog) && + ! (Options & EOptionMemoryLeakMode)) { + if (!beQuiet) + PutsIfNonEmpty(compUnit.fileName[0].c_str()); + PutsIfNonEmpty(shader->getInfoLog()); + PutsIfNonEmpty(shader->getInfoDebugLog()); + } + } + + // + // Program-level processing... + // + + // Link + if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages)) + LinkFailed = true; + +#ifndef GLSLANG_WEB + // Map IO + if (Options & EOptionSpv) { + if (!program.mapIO()) + LinkFailed = true; + } +#endif + + // Report + if (! (Options & EOptionSuppressInfolog) && + ! (Options & EOptionMemoryLeakMode)) { + PutsIfNonEmpty(program.getInfoLog()); + PutsIfNonEmpty(program.getInfoDebugLog()); + } + +#ifndef GLSLANG_WEB + // Reflect + if (Options & EOptionDumpReflection) { + program.buildReflection(ReflectOptions); + program.dumpReflection(); + } +#endif + + // Dump SPIR-V + if (Options & EOptionSpv) { + if (CompileFailed || LinkFailed) + printf("SPIR-V is not generated for failed compile or link\n"); + else { + for (int stage = 0; stage < EShLangCount; ++stage) { + if (program.getIntermediate((EShLanguage)stage)) { + std::vector spirv; + spv::SpvBuildLogger logger; + glslang::SpvOptions spvOptions; + if (Options & EOptionDebug) + spvOptions.generateDebugInfo = true; + else if (stripDebugInfo) + spvOptions.stripDebugInfo = true; + spvOptions.disableOptimizer = (Options & EOptionOptimizeDisable) != 0; + spvOptions.optimizeSize = (Options & EOptionOptimizeSize) != 0; + spvOptions.disassemble = SpvToolsDisassembler; + spvOptions.validate = SpvToolsValidate; + glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger, &spvOptions); + + // Dump the spv to a file or stdout, etc., but only if not doing + // memory/perf testing, as it's not internal to programmatic use. + if (! (Options & EOptionMemoryLeakMode)) { + printf("%s", logger.getAllMessages().c_str()); + if (Options & EOptionOutputHexadecimal) { + glslang::OutputSpvHex(spirv, GetBinaryName((EShLanguage)stage), variableName); + } else { + glslang::OutputSpvBin(spirv, GetBinaryName((EShLanguage)stage)); + } +#ifndef GLSLANG_WEB + if (!SpvToolsDisassembler && (Options & EOptionHumanReadableSpv)) + spv::Disassemble(std::cout, spirv); +#endif + } + } + } + } + } + + // Free everything up, program has to go before the shaders + // because it might have merged stuff from the shaders, and + // the stuff from the shaders has to have its destructors called + // before the pools holding the memory in the shaders is freed. + delete &program; + while (shaders.size() > 0) { + delete shaders.back(); + shaders.pop_back(); + } +} + +// +// Do file IO part of compile and link, handing off the pure +// API/programmatic mode to CompileAndLinkShaderUnits(), which can +// be put in a loop for testing memory footprint and performance. +// +// This is just for linking mode: meaning all the shaders will be put into the +// the same program linked together. +// +// This means there are a limited number of work items (not multi-threading mode) +// and that the point is testing at the linking level. Hence, to enable +// performance and memory testing, the actual compile/link can be put in +// a loop, independent of processing the work items and file IO. +// +void CompileAndLinkShaderFiles(glslang::TWorklist& Worklist) +{ + std::vector compUnits; + + // If this is using stdin, we can't really detect multiple different file + // units by input type. We need to assume that we're just being given one + // file of a certain type. + if ((Options & EOptionStdin) != 0) { + ShaderCompUnit compUnit(FindLanguage("stdin")); + std::istreambuf_iterator begin(std::cin), end; + std::string tempString(begin, end); + char* fileText = strdup(tempString.c_str()); + std::string fileName = "stdin"; + compUnit.addString(fileName, fileText); + compUnits.push_back(compUnit); + } else { + // Transfer all the work items from to a simple list of + // of compilation units. (We don't care about the thread + // work-item distribution properties in this path, which + // is okay due to the limited number of shaders, know since + // they are all getting linked together.) + glslang::TWorkItem* workItem; + while (Worklist.remove(workItem)) { + ShaderCompUnit compUnit(FindLanguage(workItem->name)); + char* fileText = ReadFileData(workItem->name.c_str()); + if (fileText == nullptr) + usage(); + compUnit.addString(workItem->name, fileText); + compUnits.push_back(compUnit); + } + } + + // Actual call to programmatic processing of compile and link, + // in a loop for testing memory and performance. This part contains + // all the perf/memory that a programmatic consumer will care about. + for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { + for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) + CompileAndLinkShaderUnits(compUnits); + + if (Options & EOptionMemoryLeakMode) + glslang::OS_DumpMemoryCounters(); + } + + // free memory from ReadFileData, which got stored in a const char* + // as the first string above + for (auto it = compUnits.begin(); it != compUnits.end(); ++it) + FreeFileData(const_cast(it->text[0])); +} + +int singleMain() +{ + glslang::TWorklist workList; + std::for_each(WorkItems.begin(), WorkItems.end(), [&workList](std::unique_ptr& item) { + assert(item); + workList.add(item.get()); + }); + +#ifndef GLSLANG_WEB + if (Options & EOptionDumpConfig) { + printf("%s", glslang::GetDefaultTBuiltInResourceString().c_str()); + if (workList.empty()) + return ESuccess; + } +#endif + + if (Options & EOptionDumpBareVersion) { + printf("%d:%d.%d.%d%s\n", glslang::GetSpirvGeneratorVersion(), GLSLANG_VERSION_MAJOR, GLSLANG_VERSION_MINOR, + GLSLANG_VERSION_PATCH, GLSLANG_VERSION_FLAVOR); + if (workList.empty()) + return ESuccess; + } else if (Options & EOptionDumpVersions) { + printf("Glslang Version: %d:%d.%d.%d%s\n", glslang::GetSpirvGeneratorVersion(), GLSLANG_VERSION_MAJOR, + GLSLANG_VERSION_MINOR, GLSLANG_VERSION_PATCH, GLSLANG_VERSION_FLAVOR); + printf("ESSL Version: %s\n", glslang::GetEsslVersionString()); + printf("GLSL Version: %s\n", glslang::GetGlslVersionString()); + std::string spirvVersion; + glslang::GetSpirvVersion(spirvVersion); + printf("SPIR-V Version %s\n", spirvVersion.c_str()); + printf("GLSL.std.450 Version %d, Revision %d\n", GLSLstd450Version, GLSLstd450Revision); + printf("Khronos Tool ID %d\n", glslang::GetKhronosToolId()); + printf("SPIR-V Generator Version %d\n", glslang::GetSpirvGeneratorVersion()); + printf("GL_KHR_vulkan_glsl version %d\n", 100); + printf("ARB_GL_gl_spirv version %d\n", 100); + if (workList.empty()) + return ESuccess; + } + + if (workList.empty() && ((Options & EOptionStdin) == 0)) { + usage(); + } + + if (Options & EOptionStdin) { + WorkItems.push_back(std::unique_ptr{new glslang::TWorkItem("stdin")}); + workList.add(WorkItems.back().get()); + } + + ProcessConfigFile(); + + if ((Options & EOptionReadHlsl) && !((Options & EOptionOutputPreprocessed) || (Options & EOptionSpv))) + Error("HLSL requires SPIR-V code generation (or preprocessing only)"); + + // + // Two modes: + // 1) linking all arguments together, single-threaded, new C++ interface + // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety, using the old handle interface + // + if (Options & (EOptionLinkProgram | EOptionOutputPreprocessed)) { + glslang::InitializeProcess(); + glslang::InitializeProcess(); // also test reference counting of users + glslang::InitializeProcess(); // also test reference counting of users + glslang::FinalizeProcess(); // also test reference counting of users + glslang::FinalizeProcess(); // also test reference counting of users + CompileAndLinkShaderFiles(workList); + glslang::FinalizeProcess(); + } else { + ShInitialize(); + ShInitialize(); // also test reference counting of users + ShFinalize(); // also test reference counting of users + + bool printShaderNames = workList.size() > 1; + + if (Options & EOptionMultiThreaded) { + std::array threads; + for (unsigned int t = 0; t < threads.size(); ++t) { + threads[t] = std::thread(CompileShaders, std::ref(workList)); + if (threads[t].get_id() == std::thread::id()) { + fprintf(stderr, "Failed to create thread\n"); + return EFailThreadCreate; + } + } + + std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); + } else + CompileShaders(workList); + + // Print out all the resulting infologs + for (size_t w = 0; w < WorkItems.size(); ++w) { + if (WorkItems[w]) { + if (printShaderNames || WorkItems[w]->results.size() > 0) + PutsIfNonEmpty(WorkItems[w]->name.c_str()); + PutsIfNonEmpty(WorkItems[w]->results.c_str()); + } + } + + ShFinalize(); + } + + if (CompileFailed) + return EFailCompile; + if (LinkFailed) + return EFailLink; + + return 0; +} + +int C_DECL main(int argc, char* argv[]) +{ + ProcessArguments(WorkItems, argc, argv); + + int ret = 0; + + // Loop over the entire init/finalize cycle to watch memory changes + const int iterations = 1; + if (iterations > 1) + glslang::OS_DumpMemoryCounters(); + for (int i = 0; i < iterations; ++i) { + ret = singleMain(); + if (iterations > 1) + glslang::OS_DumpMemoryCounters(); + } + + return ret; +} + +// +// Deduce the language from the filename. Files must end in one of the +// following extensions: +// +// .vert = vertex +// .tesc = tessellation control +// .tese = tessellation evaluation +// .geom = geometry +// .frag = fragment +// .comp = compute +// .rgen = ray generation +// .rint = ray intersection +// .rahit = ray any hit +// .rchit = ray closest hit +// .rmiss = ray miss +// .rcall = ray callable +// .mesh = mesh +// .task = task +// Additionally, the file names may end in ..glsl and ..hlsl +// where is one of the stages listed above. +// +EShLanguage FindLanguage(const std::string& name, bool parseStageName) +{ + std::string stageName; + if (shaderStageName) + stageName = shaderStageName; + else if (parseStageName) { + // Note: "first" extension means "first from the end", i.e. + // if the file is named foo.vert.glsl, then "glsl" is first, + // "vert" is second. + size_t firstExtStart = name.find_last_of("."); + bool hasFirstExt = firstExtStart != std::string::npos; + size_t secondExtStart = hasFirstExt ? name.find_last_of(".", firstExtStart - 1) : std::string::npos; + bool hasSecondExt = secondExtStart != std::string::npos; + std::string firstExt = name.substr(firstExtStart + 1, std::string::npos); + bool usesUnifiedExt = hasFirstExt && (firstExt == "glsl" || firstExt == "hlsl"); + if (usesUnifiedExt && firstExt == "hlsl") + Options |= EOptionReadHlsl; + if (hasFirstExt && !usesUnifiedExt) + stageName = firstExt; + else if (usesUnifiedExt && hasSecondExt) + stageName = name.substr(secondExtStart + 1, firstExtStart - secondExtStart - 1); + else { + usage(); + return EShLangVertex; + } + } else + stageName = name; + + if (stageName == "vert") + return EShLangVertex; + else if (stageName == "tesc") + return EShLangTessControl; + else if (stageName == "tese") + return EShLangTessEvaluation; + else if (stageName == "geom") + return EShLangGeometry; + else if (stageName == "frag") + return EShLangFragment; + else if (stageName == "comp") + return EShLangCompute; + else if (stageName == "rgen") + return EShLangRayGen; + else if (stageName == "rint") + return EShLangIntersect; + else if (stageName == "rahit") + return EShLangAnyHit; + else if (stageName == "rchit") + return EShLangClosestHit; + else if (stageName == "rmiss") + return EShLangMiss; + else if (stageName == "rcall") + return EShLangCallable; + else if (stageName == "mesh") + return EShLangMeshNV; + else if (stageName == "task") + return EShLangTaskNV; + + usage(); + return EShLangVertex; +} + +// +// Read a file's data into a string, and compile it using the old interface ShCompile, +// for non-linkable results. +// +void CompileFile(const char* fileName, ShHandle compiler) +{ + int ret = 0; + char* shaderString; + if ((Options & EOptionStdin) != 0) { + std::istreambuf_iterator begin(std::cin), end; + std::string tempString(begin, end); + shaderString = strdup(tempString.c_str()); + } else { + shaderString = ReadFileData(fileName); + } + + // move to length-based strings, rather than null-terminated strings + int* lengths = new int[1]; + lengths[0] = (int)strlen(shaderString); + + EShMessages messages = EShMsgDefault; + SetMessageOptions(messages); + + if (UserPreamble.isSet()) + Error("-D and -U options require -l (linking)\n"); + + for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { + for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) { + // ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + ret = ShCompile(compiler, &shaderString, 1, nullptr, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + // const char* multi[12] = { "# ve", "rsion", " 300 e", "s", "\n#err", + // "or should be l", "ine 1", "string 5\n", "float glo", "bal", + // ";\n#error should be line 2\n void main() {", "global = 2.3;}" }; + // const char* multi[7] = { "/", "/", "\\", "\n", "\n", "#", "version 300 es" }; + // ret = ShCompile(compiler, multi, 7, nullptr, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + } + + if (Options & EOptionMemoryLeakMode) + glslang::OS_DumpMemoryCounters(); + } + + delete [] lengths; + FreeFileData(shaderString); + + if (ret == 0) + CompileFailed = true; +} + +// +// print usage to stdout +// +void usage() +{ + printf("Usage: glslangValidator [option]... [file]...\n" + "\n" + "'file' can end in . for auto-stage classification, where is:\n" + " .conf to provide a config file that replaces the default configuration\n" + " (see -c option below for generating a template)\n" + " .vert for a vertex shader\n" + " .tesc for a tessellation control shader\n" + " .tese for a tessellation evaluation shader\n" + " .geom for a geometry shader\n" + " .frag for a fragment shader\n" + " .comp for a compute shader\n" + " .mesh for a mesh shader\n" + " .task for a task shader\n" + " .rgen for a ray generation shader\n" + " .rint for a ray intersection shader\n" + " .rahit for a ray any hit shader\n" + " .rchit for a ray closest hit shader\n" + " .rmiss for a ray miss shader\n" + " .rcall for a ray callable shader\n" + " .glsl for .vert.glsl, .tesc.glsl, ..., .comp.glsl compound suffixes\n" + " .hlsl for .vert.hlsl, .tesc.hlsl, ..., .comp.hlsl compound suffixes\n" + "\n" + "Options:\n" + " -C cascading errors; risk crash from accumulation of error recoveries\n" + " -D input is HLSL (this is the default when any suffix is .hlsl)\n" + " -D | --define-macro | --D \n" + " define a pre-processor macro\n" + " -E print pre-processed GLSL; cannot be used with -l;\n" + " errors will appear on stderr\n" + " -G[ver] create SPIR-V binary, under OpenGL semantics; turns on -l;\n" + " default file name is .spv (-o overrides this);\n" + " 'ver', when present, is the version of the input semantics,\n" + " which will appear in #define GL_SPIRV ver;\n" + " '--client opengl100' is the same as -G100;\n" + " a '--target-env' for OpenGL will also imply '-G'\n" + " -H print human readable form of SPIR-V; turns on -V\n" + " -I add dir to the include search path; includer's directory\n" + " is searched first, followed by left-to-right order of -I\n" + " -Od disables optimization; may cause illegal SPIR-V for HLSL\n" + " -Os optimizes SPIR-V to minimize size\n" + " -S uses specified stage rather than parsing the file extension\n" + " choices for are vert, tesc, tese, geom, frag, or comp\n" + " -U | --undef-macro | --U \n" + " undefine a pre-processor macro\n" + " -V[ver] create SPIR-V binary, under Vulkan semantics; turns on -l;\n" + " default file name is .spv (-o overrides this)\n" + " 'ver', when present, is the version of the input semantics,\n" + " which will appear in #define VULKAN ver\n" + " '--client vulkan100' is the same as -V100\n" + " a '--target-env' for Vulkan will also imply '-V'\n" + " -c configuration dump;\n" + " creates the default configuration file (redirect to a .conf file)\n" + " -d default to desktop (#version 110) when there is no shader #version\n" + " (default is ES version 100)\n" + " -e | --entry-point \n" + " specify as the entry-point function name\n" + " -f{hlsl_functionality1}\n" + " 'hlsl_functionality1' enables use of the\n" + " SPV_GOOGLE_hlsl_functionality1 extension\n" + " -g generate debug information\n" + " -g0 strip debug information\n" + " -h print this usage message\n" + " -i intermediate tree (glslang AST) is printed out\n" + " -l link all input files together to form a single module\n" + " -m memory leak mode\n" + " -o save binary to , requires a binary option (e.g., -V)\n" + " -q dump reflection query database; requires -l for linking\n" + " -r | --relaxed-errors" + " relaxed GLSL semantic error-checking mode\n" + " -s silence syntax and semantic error reporting\n" + " -t multi-threaded mode\n" + " -v | --version\n" + " print version strings\n" + " -w | --suppress-warnings\n" + " suppress GLSL warnings, except as required by \"#extension : warn\"\n" + " -x save binary output as text-based 32-bit hexadecimal numbers\n" + " -u: specify a uniform location override for --aml\n" + " --uniform-base set a base to use for generated uniform locations\n" + " --auto-map-bindings | --amb automatically bind uniform variables\n" + " without explicit bindings\n" + " --auto-map-locations | --aml automatically locate input/output lacking\n" + " 'location' (fragile, not cross stage)\n" + " --client {vulkan|opengl} see -V and -G\n" + " --dump-builtin-symbols prints builtin symbol table prior each compile\n" + " -dumpfullversion | -dumpversion print bare major.minor.patchlevel\n" + " --flatten-uniform-arrays | --fua flatten uniform texture/sampler arrays to\n" + " scalars\n" + " --hlsl-offsets allow block offsets to follow HLSL rules\n" + " works independently of source language\n" + " --hlsl-iomap perform IO mapping in HLSL register space\n" + " --hlsl-enable-16bit-types allow 16-bit types in SPIR-V for HLSL\n" + " --hlsl-dx9-compatible interprets sampler declarations as a\n" + " texture/sampler combo like DirectX9 would,\n" + " and recognizes DirectX9-specific semantics\n" + " --invert-y | --iy invert position.Y output in vertex shader\n" + " --keep-uncalled | --ku don't eliminate uncalled functions\n" + " --nan-clamp favor non-NaN operand in min, max, and clamp\n" + " --no-storage-format | --nsf use Unknown image format\n" + " --quiet do not print anything to stdout, unless\n" + " requested by another option\n" + " --reflect-strict-array-suffix use strict array suffix rules when\n" + " reflecting\n" + " --reflect-basic-array-suffix arrays of basic types will have trailing [0]\n" + " --reflect-intermediate-io reflection includes inputs/outputs of linked\n" + " shaders rather than just vertex/fragment\n" + " --reflect-separate-buffers reflect buffer variables and blocks\n" + " separately to uniforms\n" + " --reflect-all-block-variables reflect all variables in blocks, whether\n" + " inactive or active\n" + " --reflect-unwrap-io-blocks unwrap input/output blocks the same as\n" + " uniform blocks\n" + " --resource-set-binding [stage] name set binding\n" + " set descriptor set and binding for\n" + " individual resources\n" + " --resource-set-binding [stage] set\n" + " set descriptor set for all resources\n" + " --rsb synonym for --resource-set-binding\n" + " --shift-image-binding [stage] num\n" + " base binding number for images (uav)\n" + " --shift-image-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --sib synonym for --shift-image-binding\n" + " --shift-sampler-binding [stage] num\n" + " base binding number for samplers\n" + " --shift-sampler-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --ssb synonym for --shift-sampler-binding\n" + " --shift-ssbo-binding [stage] num base binding number for SSBOs\n" + " --shift-ssbo-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --sbb synonym for --shift-ssbo-binding\n" + " --shift-texture-binding [stage] num\n" + " base binding number for textures\n" + " --shift-texture-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --stb synonym for --shift-texture-binding\n" + " --shift-uav-binding [stage] num base binding number for UAVs\n" + " --shift-uav-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --suavb synonym for --shift-uav-binding\n" + " --shift-UBO-binding [stage] num base binding number for UBOs\n" + " --shift-UBO-binding [stage] [num set]...\n" + " per-descriptor-set shift values\n" + " --sub synonym for --shift-UBO-binding\n" + " --shift-cbuffer-binding | --scb synonyms for --shift-UBO-binding\n" + " --spirv-dis output standard-form disassembly; works only\n" + " when a SPIR-V generation option is also used\n" + " --spirv-val execute the SPIRV-Tools validator\n" + " --source-entrypoint the given shader source function is\n" + " renamed to be the given in -e\n" + " --sep synonym for --source-entrypoint\n" + " --stdin read from stdin instead of from a file;\n" + " requires providing the shader stage using -S\n" + " --target-env {vulkan1.0 | vulkan1.1 | vulkan1.2 | opengl | \n" + " spirv1.0 | spirv1.1 | spirv1.2 | spirv1.3 | spirv1.4 | spirv1.5}\n" + " Set the execution environment that the\n" + " generated code will be executed in.\n" + " Defaults to:\n" + " * vulkan1.0 under --client vulkan\n" + " * opengl under --client opengl\n" + " * spirv1.0 under --target-env vulkan1.0\n" + " * spirv1.3 under --target-env vulkan1.1\n" + " * spirv1.5 under --target-env vulkan1.2\n" + " Multiple --target-env can be specified.\n" + " --variable-name \n" + " --vn creates a C header file that contains a\n" + " uint32_t array named \n" + " initialized with the shader binary code\n" + ); + + exit(EFailUsage); +} + +#if !defined _MSC_VER && !defined MINGW_HAS_SECURE_API + +#include + +int fopen_s( + FILE** pFile, + const char* filename, + const char* mode +) +{ + if (!pFile || !filename || !mode) { + return EINVAL; + } + + FILE* f = fopen(filename, mode); + if (! f) { + if (errno != 0) { + return errno; + } else { + return ENOENT; + } + } + *pFile = f; + + return 0; +} + +#endif + +// +// Malloc a string of sufficient size and read a string into it. +// +char* ReadFileData(const char* fileName) +{ + FILE *in = nullptr; + int errorCode = fopen_s(&in, fileName, "r"); + if (errorCode || in == nullptr) + Error("unable to open input file"); + + int count = 0; + while (fgetc(in) != EOF) + count++; + + fseek(in, 0, SEEK_SET); + + char* return_data = (char*)malloc(count + 1); // freed in FreeFileData() + if ((int)fread(return_data, 1, count, in) != count) { + free(return_data); + Error("can't read input file"); + } + + return_data[count] = '\0'; + fclose(in); + + return return_data; +} + +void FreeFileData(char* data) +{ + free(data); +} + +void InfoLogMsg(const char* msg, const char* name, const int num) +{ + if (num >= 0 ) + printf("#### %s %s %d INFO LOG ####\n", msg, name, num); + else + printf("#### %s %s INFO LOG ####\n", msg, name); +} diff --git a/third_party/glslang/StandAlone/Worklist.h b/third_party/glslang/StandAlone/Worklist.h new file mode 100644 index 0000000..91b6f51 --- /dev/null +++ b/third_party/glslang/StandAlone/Worklist.h @@ -0,0 +1,95 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef WORKLIST_H_INCLUDED +#define WORKLIST_H_INCLUDED + +#include "../glslang/OSDependent/osinclude.h" +#include +#include +#include + +namespace glslang { + + class TWorkItem { + public: + TWorkItem() { } + explicit TWorkItem(const std::string& s) : + name(s) { } + std::string name; + std::string results; + std::string resultsIndex; + }; + + class TWorklist { + public: + TWorklist() { } + virtual ~TWorklist() { } + + void add(TWorkItem* item) + { + std::lock_guard guard(mutex); + worklist.push_back(item); + } + + bool remove(TWorkItem*& item) + { + std::lock_guard guard(mutex); + + if (worklist.empty()) + return false; + item = worklist.front(); + worklist.pop_front(); + + return true; + } + + int size() + { + return (int)worklist.size(); + } + + bool empty() + { + return worklist.empty(); + } + + protected: + std::mutex mutex; + std::list worklist; + }; + +} // end namespace glslang + +#endif // WORKLIST_H_INCLUDED diff --git a/third_party/glslang/StandAlone/resource_limits_c.cpp b/third_party/glslang/StandAlone/resource_limits_c.cpp new file mode 100644 index 0000000..a1f681c --- /dev/null +++ b/third_party/glslang/StandAlone/resource_limits_c.cpp @@ -0,0 +1,65 @@ +/** +BSD 2-Clause License + +Copyright (c) 2020, Travis Fort +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include "resource_limits_c.h" +#include "ResourceLimits.h" +#include +#include +#include + +const glslang_resource_t* glslang_default_resource(void) +{ + return reinterpret_cast(&glslang::DefaultTBuiltInResource); +} + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +const char* glslang_default_resource_string() +{ + std::string cpp_str = glslang::GetDefaultTBuiltInResourceString(); + char* c_str = (char*)malloc(cpp_str.length() + 1); + strcpy(c_str, cpp_str.c_str()); + return c_str; +} + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +void glslang_decode_resource_limits(glslang_resource_t* resources, char* config) +{ + glslang::DecodeResourceLimits(reinterpret_cast(resources), config); +} diff --git a/third_party/glslang/StandAlone/resource_limits_c.h b/third_party/glslang/StandAlone/resource_limits_c.h new file mode 100644 index 0000000..108fd5e --- /dev/null +++ b/third_party/glslang/StandAlone/resource_limits_c.h @@ -0,0 +1,54 @@ +/** +BSD 2-Clause License + +Copyright (c) 2020, Travis Fort +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#ifndef _STAND_ALONE_RESOURCE_LIMITS_C_INCLUDED_ +#define _STAND_ALONE_RESOURCE_LIMITS_C_INCLUDED_ + +#include "../glslang/Include/glslang_c_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// These are the default resources for TBuiltInResources, used for both +// - parsing this string for the case where the user didn't supply one, +// - dumping out a template for user construction of a config file. +const glslang_resource_t* glslang_default_resource(void); + +// Returns the DefaultTBuiltInResource as a human-readable string. +// NOTE: User is responsible for freeing this string. +const char* glslang_default_resource_string(); + +// Decodes the resource limits from |config| to |resources|. +void glslang_decode_resource_limits(glslang_resource_t* resources, char* config); + +#ifdef __cplusplus +} +#endif + +#endif // _STAND_ALONE_RESOURCE_LIMITS_C_INCLUDED_ diff --git a/third_party/glslang/StandAlone/spirv-remap.cpp b/third_party/glslang/StandAlone/spirv-remap.cpp new file mode 100644 index 0000000..48878c3 --- /dev/null +++ b/third_party/glslang/StandAlone/spirv-remap.cpp @@ -0,0 +1,341 @@ +// +// Copyright (C) 2015 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include +#include + +#include "../SPIRV/SPVRemapper.h" + +namespace { + + typedef unsigned int SpvWord; + + // Poor man's basename: given a complete path, return file portion. + // E.g: + // Linux: /foo/bar/test -> test + // Win: c:\foo\bar\test -> test + // It's not very efficient, but that doesn't matter for our minimal-duty use. + // Using boost::filesystem would be better in many ways, but want to avoid that dependency. + + // OS dependent path separator (avoiding boost::filesystem dependency) +#if defined(_WIN32) + char path_sep_char() { return '\\'; } +#else + char path_sep_char() { return '/'; } +#endif + + std::string basename(const std::string filename) + { + const size_t sepLoc = filename.find_last_of(path_sep_char()); + + return (sepLoc == filename.npos) ? filename : filename.substr(sepLoc+1); + } + + void errHandler(const std::string& str) { + std::cout << str << std::endl; + exit(5); + } + + void logHandler(const std::string& str) { + std::cout << str << std::endl; + } + + // Read word stream from disk + void read(std::vector& spv, const std::string& inFilename, int verbosity) + { + std::ifstream fp; + + if (verbosity > 0) + logHandler(std::string(" reading: ") + inFilename); + + spv.clear(); + fp.open(inFilename, std::fstream::in | std::fstream::binary); + + if (fp.fail()) + errHandler("error opening file for read: "); + + // Reserve space (for efficiency, not for correctness) + fp.seekg(0, fp.end); + spv.reserve(size_t(fp.tellg()) / sizeof(SpvWord)); + fp.seekg(0, fp.beg); + + while (!fp.eof()) { + SpvWord inWord; + fp.read((char *)&inWord, sizeof(inWord)); + + if (!fp.eof()) { + spv.push_back(inWord); + if (fp.fail()) + errHandler(std::string("error reading file: ") + inFilename); + } + } + } + + void write(std::vector& spv, const std::string& outFile, int verbosity) + { + if (outFile.empty()) + errHandler("missing output filename."); + + std::ofstream fp; + + if (verbosity > 0) + logHandler(std::string(" writing: ") + outFile); + + fp.open(outFile, std::fstream::out | std::fstream::binary); + + if (fp.fail()) + errHandler(std::string("error opening file for write: ") + outFile); + + for (auto it = spv.cbegin(); it != spv.cend(); ++it) { + SpvWord word = *it; + fp.write((char *)&word, sizeof(word)); + if (fp.fail()) + errHandler(std::string("error writing file: ") + outFile); + } + + // file is closed by destructor + } + + // Print helpful usage message to stdout, and exit + void usage(const char* const name, const char* const msg = 0) + { + if (msg) + std::cout << msg << std::endl << std::endl; + + std::cout << "Usage: " << std::endl; + + std::cout << " " << basename(name) + << " [-v[v[...]] | --verbose [int]]" + << " [--map (all|types|names|funcs)]" + << " [--dce (all|types|funcs)]" + << " [--opt (all|loadstore)]" + << " [--strip-all | --strip all | -s]" + << " [--do-everything]" + << " --input | -i file1 [file2...] --output|-o DESTDIR" + << std::endl; + + std::cout << " " << basename(name) << " [--version | -V]" << std::endl; + std::cout << " " << basename(name) << " [--help | -?]" << std::endl; + + exit(5); + } + + // grind through each SPIR in turn + void execute(const std::vector& inputFile, const std::string& outputDir, + int opts, int verbosity) + { + for (auto it = inputFile.cbegin(); it != inputFile.cend(); ++it) { + const std::string &filename = *it; + std::vector spv; + read(spv, filename, verbosity); + spv::spirvbin_t(verbosity).remap(spv, opts); + + const std::string outfile = outputDir + path_sep_char() + basename(filename); + + write(spv, outfile, verbosity); + } + + if (verbosity > 0) + std::cout << "Done: " << inputFile.size() << " file(s) processed" << std::endl; + } + + // Parse command line options + void parseCmdLine(int argc, char** argv, std::vector& inputFile, + std::string& outputDir, + int& options, + int& verbosity) + { + if (argc < 2) + usage(argv[0]); + + verbosity = 0; + options = spv::spirvbin_t::NONE; + + // Parse command line. + // boost::program_options would be quite a bit nicer, but we don't want to + // introduce a dependency on boost. + for (int a=1; a= argc) + usage(argv[0], "--output requires an argument"); + if (!outputDir.empty()) + usage(argv[0], "--output can be provided only once"); + + outputDir = argv[a++]; + + // Remove trailing directory separator characters + while (!outputDir.empty() && outputDir.back() == path_sep_char()) + outputDir.pop_back(); + + } + else if (arg == "-vv") { verbosity = 2; ++a; } // verbosity shortcuts + else if (arg == "-vvv") { verbosity = 3; ++a; } // ... + else if (arg == "-vvvv") { verbosity = 4; ++a; } // ... + else if (arg == "-vvvvv") { verbosity = 5; ++a; } // ... + + else if (arg == "--verbose" || arg == "-v") { + ++a; + verbosity = 1; + + if (a < argc) { + char* end_ptr = 0; + int verb = ::strtol(argv[a], &end_ptr, 10); + // If we have not read to the end of the string or + // the string contained no elements, then we do not want to + // store the value. + if (*end_ptr == '\0' && end_ptr != argv[a]) { + verbosity = verb; + ++a; + } + } + } + else if (arg == "--version" || arg == "-V") { + std::cout << basename(argv[0]) << " version 0.97" << std::endl; + exit(0); + } else if (arg == "--input" || arg == "-i") { + // Collect input files + for (++a; a < argc && argv[a][0] != '-'; ++a) + inputFile.push_back(argv[a]); + } else if (arg == "--do-everything") { + ++a; + options = options | spv::spirvbin_t::DO_EVERYTHING; + } else if (arg == "--strip-all" || arg == "-s") { + ++a; + options = options | spv::spirvbin_t::STRIP; + } else if (arg == "--strip") { + ++a; + if (strncmp(argv[a], "all", 3) == 0) { + options = options | spv::spirvbin_t::STRIP; + ++a; + } + } else if (arg == "--dce") { + // Parse comma (or colon, etc) separated list of things to dce + ++a; + for (const char* c = argv[a]; *c; ++c) { + if (strncmp(c, "all", 3) == 0) { + options = (options | spv::spirvbin_t::DCE_ALL); + c += 3; + } else if (strncmp(c, "*", 1) == 0) { + options = (options | spv::spirvbin_t::DCE_ALL); + c += 1; + } else if (strncmp(c, "funcs", 5) == 0) { + options = (options | spv::spirvbin_t::DCE_FUNCS); + c += 5; + } else if (strncmp(c, "types", 5) == 0) { + options = (options | spv::spirvbin_t::DCE_TYPES); + c += 5; + } + } + ++a; + } else if (arg == "--map") { + // Parse comma (or colon, etc) separated list of things to map + ++a; + for (const char* c = argv[a]; *c; ++c) { + if (strncmp(c, "all", 3) == 0) { + options = (options | spv::spirvbin_t::MAP_ALL); + c += 3; + } else if (strncmp(c, "*", 1) == 0) { + options = (options | spv::spirvbin_t::MAP_ALL); + c += 1; + } else if (strncmp(c, "types", 5) == 0) { + options = (options | spv::spirvbin_t::MAP_TYPES); + c += 5; + } else if (strncmp(c, "names", 5) == 0) { + options = (options | spv::spirvbin_t::MAP_NAMES); + c += 5; + } else if (strncmp(c, "funcs", 5) == 0) { + options = (options | spv::spirvbin_t::MAP_FUNCS); + c += 5; + } + } + ++a; + } else if (arg == "--opt") { + ++a; + for (const char* c = argv[a]; *c; ++c) { + if (strncmp(c, "all", 3) == 0) { + options = (options | spv::spirvbin_t::OPT_ALL); + c += 3; + } else if (strncmp(c, "*", 1) == 0) { + options = (options | spv::spirvbin_t::OPT_ALL); + c += 1; + } else if (strncmp(c, "loadstore", 9) == 0) { + options = (options | spv::spirvbin_t::OPT_LOADSTORE); + c += 9; + } + } + ++a; + } else if (arg == "--help" || arg == "-?") { + usage(argv[0]); + } else { + usage(argv[0], "Unknown command line option"); + } + } + } + +} // namespace + +int main(int argc, char** argv) +{ + std::vector inputFile; + std::string outputDir; + int opts; + int verbosity; + +#ifdef use_cpp11 + // handle errors by exiting + spv::spirvbin_t::registerErrorHandler(errHandler); + + // Log messages to std::cout + spv::spirvbin_t::registerLogHandler(logHandler); +#endif + + if (argc < 2) + usage(argv[0]); + + parseCmdLine(argc, argv, inputFile, outputDir, opts, verbosity); + + if (outputDir.empty()) + usage(argv[0], "Output directory required"); + + // Main operations: read, remap, and write. + execute(inputFile, outputDir, opts, verbosity); + + // If we get here, everything went OK! Nothing more to be done. +} diff --git a/third_party/glslang/WORKSPACE b/third_party/glslang/WORKSPACE new file mode 100644 index 0000000..488546c --- /dev/null +++ b/third_party/glslang/WORKSPACE @@ -0,0 +1,27 @@ +workspace(name = "org_khronos_glslang") +load( + "@bazel_tools//tools/build_defs/repo:http.bzl", + "http_archive", +) + +http_archive( + name = "com_google_googletest", + sha256 = "94c634d499558a76fa649edb13721dce6e98fb1e7018dfaeba3cd7a083945e91", + strip_prefix = "googletest-release-1.10.0", + urls = ["https://github.com/google/googletest/archive/release-1.10.0.zip"], # 3-Oct-2019 +) + +http_archive( + name = "com_googlesource_code_re2", + sha256 = "b885bb965ab4b6cf8718bbb8154d8f6474cd00331481b6d3e390babb3532263e", + strip_prefix = "re2-e860767c86e577b87deadf24cc4567ea83c4f162/", + urls = ["https://github.com/google/re2/archive/e860767c86e577b87deadf24cc4567ea83c4f162.zip"], +) + +http_archive( + name = "com_google_effcee", + build_file = "BUILD.effcee.bazel", + sha256 = "b0c21a01995fdf9792510566d78d5e7fe6f83cb4ba986eba691f4926f127cb34", + strip_prefix = "effcee-8f0a61dc95e0df18c18e0ac56d83b3fa9d2fe90b/", + urls = ["https://github.com/google/effcee/archive/8f0a61dc95e0df18c18e0ac56d83b3fa9d2fe90b.zip"], +) diff --git a/third_party/glslang/_config.yml b/third_party/glslang/_config.yml new file mode 100644 index 0000000..e8b995b --- /dev/null +++ b/third_party/glslang/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-merlot diff --git a/third_party/glslang/build_info.h.tmpl b/third_party/glslang/build_info.h.tmpl new file mode 100644 index 0000000..eacecb0 --- /dev/null +++ b/third_party/glslang/build_info.h.tmpl @@ -0,0 +1,62 @@ +// Copyright (C) 2020 The Khronos Group Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of The Khronos Group Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_BUILD_INFO +#define GLSLANG_BUILD_INFO + +#define GLSLANG_VERSION_MAJOR +#define GLSLANG_VERSION_MINOR +#define GLSLANG_VERSION_PATCH +#define GLSLANG_VERSION_FLAVOR "" + +#define GLSLANG_VERSION_GREATER_THAN(major, minor, patch) \ + (((major) > GLSLANG_VERSION_MAJOR) || ((major) == GLSLANG_VERSION_MAJOR && \ + (((minor) > GLSLANG_VERSION_MINOR) || ((minor) == GLSLANG_VERSION_MINOR && \ + ((patch) > GLSLANG_VERSION_PATCH))))) + +#define GLSLANG_VERSION_GREATER_OR_EQUAL_TO(major, minor, patch) \ + (((major) > GLSLANG_VERSION_MAJOR) || ((major) == GLSLANG_VERSION_MAJOR && \ + (((minor) > GLSLANG_VERSION_MINOR) || ((minor) == GLSLANG_VERSION_MINOR && \ + ((patch) >= GLSLANG_VERSION_PATCH))))) + +#define GLSLANG_VERSION_LESS_THAN(major, minor, patch) \ + (((major) < GLSLANG_VERSION_MAJOR) || ((major) == GLSLANG_VERSION_MAJOR && \ + (((minor) < GLSLANG_VERSION_MINOR) || ((minor) == GLSLANG_VERSION_MINOR && \ + ((patch) < GLSLANG_VERSION_PATCH))))) + +#define GLSLANG_VERSION_LESS_OR_EQUAL_TO(major, minor, patch) \ + (((major) < GLSLANG_VERSION_MAJOR) || ((major) == GLSLANG_VERSION_MAJOR && \ + (((minor) < GLSLANG_VERSION_MINOR) || ((minor) == GLSLANG_VERSION_MINOR && \ + ((patch) <= GLSLANG_VERSION_PATCH))))) + +#endif // GLSLANG_BUILD_INFO diff --git a/third_party/glslang/build_info.py b/third_party/glslang/build_info.py new file mode 100755 index 0000000..2ac864b --- /dev/null +++ b/third_party/glslang/build_info.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python + +# Copyright (c) 2020 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +import errno +import os +import os.path +import re +import subprocess +import sys +import time + +usage = """{} emits a string to stdout or file with project version information. + +args: [] [-i ] [-o ] + +Either or -i needs to be provided. + +The tool will output the provided string or file content with the following +tokens substituted: + + - The major version point parsed from the CHANGES.md file. + - The minor version point parsed from the CHANGES.md file. + - The point version point parsed from the CHANGES.md file. + - The optional dash suffix parsed from the CHANGES.md file (excluding + dash prefix). + <-flavor> - The optional dash suffix parsed from the CHANGES.md file (including + dash prefix). + - The optional date of the release in the form YYYY-MM-DD + - The git commit information for the directory taken from + "git describe" if that succeeds, or "git rev-parse HEAD" + if that succeeds, or otherwise a message containing the phrase + "unknown hash". + +-o is an optional flag for writing the output string to the given file. If + ommitted then the string is printed to stdout. +""" + +def mkdir_p(directory): + """Make the directory, and all its ancestors as required. Any of the + directories are allowed to already exist.""" + + if directory == "": + # We're being asked to make the current directory. + return + + try: + os.makedirs(directory) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(directory): + pass + else: + raise + + +def command_output(cmd, directory): + """Runs a command in a directory and returns its standard output stream. + + Captures the standard error stream. + + Raises a RuntimeError if the command fails to launch or otherwise fails. + """ + p = subprocess.Popen(cmd, + cwd=directory, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (stdout, _) = p.communicate() + if p.returncode != 0: + raise RuntimeError('Failed to run %s in %s' % (cmd, directory)) + return stdout + + +def deduce_software_version(directory): + """Returns a software version number parsed from the CHANGES.md file + in the given directory. + + The CHANGES.md file describes most recent versions first. + """ + + # Match the first well-formed version-and-date line. + # Allow trailing whitespace in the checked-out source code has + # unexpected carriage returns on a linefeed-only system such as + # Linux. + pattern = re.compile(r'^#* +(\d+)\.(\d+)\.(\d+)(-\w+)? (\d\d\d\d-\d\d-\d\d)? *$') + changes_file = os.path.join(directory, 'CHANGES.md') + with open(changes_file, mode='r') as f: + for line in f.readlines(): + match = pattern.match(line) + if match: + flavor = match.group(4) + if flavor == None: + flavor = "" + return { + "major": match.group(1), + "minor": match.group(2), + "patch": match.group(3), + "flavor": flavor.lstrip("-"), + "-flavor": flavor, + "date": match.group(5), + } + raise Exception('No version number found in {}'.format(changes_file)) + + +def describe(directory): + """Returns a string describing the current Git HEAD version as descriptively + as possible. + + Runs 'git describe', or alternately 'git rev-parse HEAD', in directory. If + successful, returns the output; otherwise returns 'unknown hash, '.""" + try: + # decode() is needed here for Python3 compatibility. In Python2, + # str and bytes are the same type, but not in Python3. + # Popen.communicate() returns a bytes instance, which needs to be + # decoded into text data first in Python3. And this decode() won't + # hurt Python2. + return command_output(['git', 'describe'], directory).rstrip().decode() + except: + try: + return command_output( + ['git', 'rev-parse', 'HEAD'], directory).rstrip().decode() + except: + # This is the fallback case where git gives us no information, + # e.g. because the source tree might not be in a git tree. + # In this case, usually use a timestamp. However, to ensure + # reproducible builds, allow the builder to override the wall + # clock time with environment variable SOURCE_DATE_EPOCH + # containing a (presumably) fixed timestamp. + timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) + formatted = datetime.datetime.utcfromtimestamp(timestamp).isoformat() + return 'unknown hash, {}'.format(formatted) + +def parse_args(): + directory = None + input_string = None + input_file = None + output_file = None + + if len(sys.argv) < 2: + raise Exception("Invalid number of arguments") + + directory = sys.argv[1] + i = 2 + + if not sys.argv[i].startswith("-"): + input_string = sys.argv[i] + i = i + 1 + + while i < len(sys.argv): + opt = sys.argv[i] + i = i + 1 + + if opt == "-i" or opt == "-o": + if i == len(sys.argv): + raise Exception("Expected path after {}".format(opt)) + val = sys.argv[i] + i = i + 1 + if (opt == "-i"): + input_file = val + elif (opt == "-o"): + output_file = val + else: + raise Exception("Unknown flag {}".format(opt)) + + return { + "directory": directory, + "input_string": input_string, + "input_file": input_file, + "output_file": output_file, + } + +def main(): + args = None + try: + args = parse_args() + except Exception as e: + print(e) + print("\nUsage:\n") + print(usage.format(sys.argv[0])) + sys.exit(1) + + directory = args["directory"] + template = args["input_string"] + if template == None: + with open(args["input_file"], 'r') as f: + template = f.read() + output_file = args["output_file"] + + software_version = deduce_software_version(directory) + commit = describe(directory) + output = template \ + .replace("", software_version["major"]) \ + .replace("", software_version["minor"]) \ + .replace("", software_version["patch"]) \ + .replace("", software_version["flavor"]) \ + .replace("<-flavor>", software_version["-flavor"]) \ + .replace("", software_version["date"]) \ + .replace("", commit) + + if output_file is None: + print(output) + else: + mkdir_p(os.path.dirname(output_file)) + + if os.path.isfile(output_file): + with open(output_file, 'r') as f: + if output == f.read(): + return + + with open(output_file, 'w') as f: + f.write(output) + +if __name__ == '__main__': + main() diff --git a/third_party/glslang/build_overrides/build.gni b/third_party/glslang/build_overrides/build.gni new file mode 100644 index 0000000..3101a86 --- /dev/null +++ b/third_party/glslang/build_overrides/build.gni @@ -0,0 +1,39 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +declare_args() { + build_with_chromium = false + linux_use_bundled_binutils_override = true + ignore_elf32_limitations = true + use_system_xcode = true +} diff --git a/third_party/glslang/build_overrides/glslang.gni b/third_party/glslang/build_overrides/glslang.gni new file mode 100644 index 0000000..003c78f --- /dev/null +++ b/third_party/glslang/build_overrides/glslang.gni @@ -0,0 +1,37 @@ +# Copyright (C) 2018 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# These are variables that are overridable by projects that include glslang. + +# The path to glslang dependencies. +glslang_spirv_tools_dir = "//External/spirv-tools" diff --git a/third_party/glslang/build_overrides/spirv_tools.gni b/third_party/glslang/build_overrides/spirv_tools.gni new file mode 100644 index 0000000..7cf7005 --- /dev/null +++ b/third_party/glslang/build_overrides/spirv_tools.gni @@ -0,0 +1,38 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# We are building inside glslang +spirv_tools_standalone = false + +# Paths to SPIRV-Tools dependencies +spirv_tools_spirv_headers_dir = "//External/spirv-tools/external/spirv-headers" diff --git a/third_party/glslang/glslang/CInterface/glslang_c_interface.cpp b/third_party/glslang/glslang/CInterface/glslang_c_interface.cpp new file mode 100644 index 0000000..2e04f53 --- /dev/null +++ b/third_party/glslang/glslang/CInterface/glslang_c_interface.cpp @@ -0,0 +1,428 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#include "glslang/Include/glslang_c_interface.h" + +#include "StandAlone/DirStackFileIncluder.h" +#include "StandAlone/ResourceLimits.h" +#include "glslang/Include/ShHandle.h" + +#include "glslang/Include/ResourceLimits.h" +#include "glslang/MachineIndependent/Versions.h" + +static_assert(int(GLSLANG_STAGE_COUNT) == EShLangCount, ""); +static_assert(int(GLSLANG_STAGE_MASK_COUNT) == EShLanguageMaskCount, ""); +static_assert(int(GLSLANG_SOURCE_COUNT) == glslang::EShSourceCount, ""); +static_assert(int(GLSLANG_CLIENT_COUNT) == glslang::EShClientCount, ""); +static_assert(int(GLSLANG_TARGET_COUNT) == glslang::EShTargetCount, ""); +static_assert(int(GLSLANG_TARGET_CLIENT_VERSION_COUNT) == glslang::EShTargetClientVersionCount, ""); +static_assert(int(GLSLANG_TARGET_LANGUAGE_VERSION_COUNT) == glslang::EShTargetLanguageVersionCount, ""); +static_assert(int(GLSLANG_OPT_LEVEL_COUNT) == EshOptLevelCount, ""); +static_assert(int(GLSLANG_TEX_SAMP_TRANS_COUNT) == EShTexSampTransCount, ""); +static_assert(int(GLSLANG_MSG_COUNT) == EShMsgCount, ""); +static_assert(int(GLSLANG_REFLECTION_COUNT) == EShReflectionCount, ""); +static_assert(int(GLSLANG_PROFILE_COUNT) == EProfileCount, ""); +static_assert(sizeof(glslang_limits_t) == sizeof(TLimits), ""); +static_assert(sizeof(glslang_resource_t) == sizeof(TBuiltInResource), ""); + +typedef struct glslang_shader_s { + glslang::TShader* shader; + std::string preprocessedGLSL; +} glslang_shader_t; + +typedef struct glslang_program_s { + glslang::TProgram* program; + std::vector spirv; + std::string loggerMessages; +} glslang_program_t; + +/* Wrapper/Adapter for C glsl_include_callbacks_t functions + + This class contains a 'glsl_include_callbacks_t' structure + with C include_local/include_system callback pointers. + + This class implement TShader::Includer interface + by redirecting C++ virtual methods to C callbacks. + + The 'IncludeResult' instances produced by this Includer + contain a reference to glsl_include_result_t C structure + to allow its lifetime management by another C callback + (CallbackIncluder::callbacks::free_include_result) +*/ +class CallbackIncluder : public glslang::TShader::Includer { +public: + /* Wrapper of IncludeResult which stores a glsl_include_result object internally */ + class CallbackIncludeResult : public glslang::TShader::Includer::IncludeResult { + public: + CallbackIncludeResult(const std::string& headerName, const char* const headerData, const size_t headerLength, + void* userData, glsl_include_result_t* includeResult) + : glslang::TShader::Includer::IncludeResult(headerName, headerData, headerLength, userData), + includeResult(includeResult) + { + } + + virtual ~CallbackIncludeResult() {} + + protected: + friend class CallbackIncluder; + + glsl_include_result_t* includeResult; + }; + +public: + CallbackIncluder(glsl_include_callbacks_t _callbacks, void* _context) : callbacks(_callbacks), context(_context) {} + + virtual ~CallbackIncluder() {} + + virtual IncludeResult* includeSystem(const char* headerName, const char* includerName, + size_t inclusionDepth) override + { + if (this->callbacks.include_system) { + glsl_include_result_t* result = + this->callbacks.include_system(this->context, headerName, includerName, inclusionDepth); + + return new CallbackIncludeResult(std::string(headerName), result->header_data, result->header_length, + nullptr, result); + } + + return glslang::TShader::Includer::includeSystem(headerName, includerName, inclusionDepth); + } + + virtual IncludeResult* includeLocal(const char* headerName, const char* includerName, + size_t inclusionDepth) override + { + if (this->callbacks.include_local) { + glsl_include_result_t* result = + this->callbacks.include_local(this->context, headerName, includerName, inclusionDepth); + + return new CallbackIncludeResult(std::string(headerName), result->header_data, result->header_length, + nullptr, result); + } + + return glslang::TShader::Includer::includeLocal(headerName, includerName, inclusionDepth); + } + + /* This function only calls free_include_result callback + when the IncludeResult instance is allocated by a C function */ + virtual void releaseInclude(IncludeResult* result) override + { + if (result == nullptr) + return; + + if (this->callbacks.free_include_result && (result->userData == nullptr)) { + CallbackIncludeResult* innerResult = static_cast(result); + /* use internal free() function */ + this->callbacks.free_include_result(this->context, innerResult->includeResult); + /* ignore internal fields of TShader::Includer::IncludeResult */ + delete result; + return; + } + + delete[] static_cast(result->userData); + delete result; + } + +private: + CallbackIncluder() {} + + /* C callback pointers */ + glsl_include_callbacks_t callbacks; + /* User-defined context */ + void* context; +}; + +GLSLANG_EXPORT int glslang_initialize_process() { return static_cast(glslang::InitializeProcess()); } + +GLSLANG_EXPORT void glslang_finalize_process() { glslang::FinalizeProcess(); } + +static EShLanguage c_shader_stage(glslang_stage_t stage) +{ + switch (stage) { + case GLSLANG_STAGE_VERTEX: + return EShLangVertex; + case GLSLANG_STAGE_TESSCONTROL: + return EShLangTessControl; + case GLSLANG_STAGE_TESSEVALUATION: + return EShLangTessEvaluation; + case GLSLANG_STAGE_GEOMETRY: + return EShLangGeometry; + case GLSLANG_STAGE_FRAGMENT: + return EShLangFragment; + case GLSLANG_STAGE_COMPUTE: + return EShLangCompute; + case GLSLANG_STAGE_RAYGEN_NV: + return EShLangRayGen; + case GLSLANG_STAGE_INTERSECT_NV: + return EShLangIntersect; + case GLSLANG_STAGE_ANYHIT_NV: + return EShLangAnyHit; + case GLSLANG_STAGE_CLOSESTHIT_NV: + return EShLangClosestHit; + case GLSLANG_STAGE_MISS_NV: + return EShLangMiss; + case GLSLANG_STAGE_CALLABLE_NV: + return EShLangCallable; + case GLSLANG_STAGE_TASK_NV: + return EShLangTaskNV; + case GLSLANG_STAGE_MESH_NV: + return EShLangMeshNV; + default: + break; + } + return EShLangCount; +} + +static int c_shader_messages(glslang_messages_t messages) +{ +#define CONVERT_MSG(in, out) \ + if ((messages & in) == in) \ + res |= out; + + int res = 0; + + CONVERT_MSG(GLSLANG_MSG_RELAXED_ERRORS_BIT, EShMsgRelaxedErrors); + CONVERT_MSG(GLSLANG_MSG_SUPPRESS_WARNINGS_BIT, EShMsgSuppressWarnings); + CONVERT_MSG(GLSLANG_MSG_AST_BIT, EShMsgAST); + CONVERT_MSG(GLSLANG_MSG_SPV_RULES_BIT, EShMsgSpvRules); + CONVERT_MSG(GLSLANG_MSG_VULKAN_RULES_BIT, EShMsgVulkanRules); + CONVERT_MSG(GLSLANG_MSG_ONLY_PREPROCESSOR_BIT, EShMsgOnlyPreprocessor); + CONVERT_MSG(GLSLANG_MSG_READ_HLSL_BIT, EShMsgReadHlsl); + CONVERT_MSG(GLSLANG_MSG_CASCADING_ERRORS_BIT, EShMsgCascadingErrors); + CONVERT_MSG(GLSLANG_MSG_KEEP_UNCALLED_BIT, EShMsgKeepUncalled); + CONVERT_MSG(GLSLANG_MSG_HLSL_OFFSETS_BIT, EShMsgHlslOffsets); + CONVERT_MSG(GLSLANG_MSG_DEBUG_INFO_BIT, EShMsgDebugInfo); + CONVERT_MSG(GLSLANG_MSG_HLSL_ENABLE_16BIT_TYPES_BIT, EShMsgHlslEnable16BitTypes); + CONVERT_MSG(GLSLANG_MSG_HLSL_LEGALIZATION_BIT, EShMsgHlslLegalization); + CONVERT_MSG(GLSLANG_MSG_HLSL_DX9_COMPATIBLE_BIT, EShMsgHlslDX9Compatible); + CONVERT_MSG(GLSLANG_MSG_BUILTIN_SYMBOL_TABLE_BIT, EShMsgBuiltinSymbolTable); + return res; +#undef CONVERT_MSG +} + +static glslang::EShTargetLanguageVersion +c_shader_target_language_version(glslang_target_language_version_t target_language_version) +{ + switch (target_language_version) { + case GLSLANG_TARGET_SPV_1_0: + return glslang::EShTargetSpv_1_0; + case GLSLANG_TARGET_SPV_1_1: + return glslang::EShTargetSpv_1_1; + case GLSLANG_TARGET_SPV_1_2: + return glslang::EShTargetSpv_1_2; + case GLSLANG_TARGET_SPV_1_3: + return glslang::EShTargetSpv_1_3; + case GLSLANG_TARGET_SPV_1_4: + return glslang::EShTargetSpv_1_4; + case GLSLANG_TARGET_SPV_1_5: + return glslang::EShTargetSpv_1_5; + default: + break; + } + return glslang::EShTargetSpv_1_0; +} + +static glslang::EShClient c_shader_client(glslang_client_t client) +{ + switch (client) { + case GLSLANG_CLIENT_VULKAN: + return glslang::EShClientVulkan; + case GLSLANG_CLIENT_OPENGL: + return glslang::EShClientOpenGL; + default: + break; + } + + return glslang::EShClientNone; +} + +static glslang::EShTargetClientVersion c_shader_client_version(glslang_target_client_version_t client_version) +{ + switch (client_version) { + case GLSLANG_TARGET_VULKAN_1_1: + return glslang::EShTargetVulkan_1_1; + case GLSLANG_TARGET_OPENGL_450: + return glslang::EShTargetOpenGL_450; + default: + break; + } + + return glslang::EShTargetVulkan_1_0; +} + +static glslang::EShTargetLanguage c_shader_target_language(glslang_target_language_t target_language) +{ + if (target_language == GLSLANG_TARGET_NONE) + return glslang::EShTargetNone; + + return glslang::EShTargetSpv; +} + +static glslang::EShSource c_shader_source(glslang_source_t source) +{ + switch (source) { + case GLSLANG_SOURCE_GLSL: + return glslang::EShSourceGlsl; + case GLSLANG_SOURCE_HLSL: + return glslang::EShSourceHlsl; + default: + break; + } + + return glslang::EShSourceNone; +} + +static EProfile c_shader_profile(glslang_profile_t profile) +{ + switch (profile) { + case GLSLANG_BAD_PROFILE: + return EBadProfile; + case GLSLANG_NO_PROFILE: + return ENoProfile; + case GLSLANG_CORE_PROFILE: + return ECoreProfile; + case GLSLANG_COMPATIBILITY_PROFILE: + return ECompatibilityProfile; + case GLSLANG_ES_PROFILE: + return EEsProfile; + case GLSLANG_PROFILE_COUNT: // Should not use this + break; + } + + return EProfile(); +} + +GLSLANG_EXPORT glslang_shader_t* glslang_shader_create(const glslang_input_t* input) +{ + if (!input || !input->code) { + printf("Error creating shader: null input(%p)/input->code\n", input); + + if (input) + printf("input->code = %p\n", input->code); + + return nullptr; + } + + glslang_shader_t* shader = new glslang_shader_t(); + + shader->shader = new glslang::TShader(c_shader_stage(input->stage)); + shader->shader->setStrings(&input->code, 1); + shader->shader->setEnvInput(c_shader_source(input->language), c_shader_stage(input->stage), + c_shader_client(input->client), input->default_version); + shader->shader->setEnvClient(c_shader_client(input->client), c_shader_client_version(input->client_version)); + shader->shader->setEnvTarget(c_shader_target_language(input->target_language), + c_shader_target_language_version(input->target_language_version)); + + return shader; +} + +GLSLANG_EXPORT const char* glslang_shader_get_preprocessed_code(glslang_shader_t* shader) +{ + return shader->preprocessedGLSL.c_str(); +} + +GLSLANG_EXPORT int glslang_shader_preprocess(glslang_shader_t* shader, const glslang_input_t* input) +{ + DirStackFileIncluder Includer; + /* TODO: use custom callbacks if they are available in 'i->callbacks' */ + return shader->shader->preprocess( + reinterpret_cast(input->resource), + input->default_version, + c_shader_profile(input->default_profile), + input->force_default_version_and_profile != 0, + input->forward_compatible != 0, + (EShMessages)c_shader_messages(input->messages), + &shader->preprocessedGLSL, + Includer + ); +} + +GLSLANG_EXPORT int glslang_shader_parse(glslang_shader_t* shader, const glslang_input_t* input) +{ + const char* preprocessedCStr = shader->preprocessedGLSL.c_str(); + shader->shader->setStrings(&preprocessedCStr, 1); + + return shader->shader->parse( + reinterpret_cast(input->resource), + input->default_version, + input->forward_compatible != 0, + (EShMessages)c_shader_messages(input->messages) + ); +} + +GLSLANG_EXPORT const char* glslang_shader_get_info_log(glslang_shader_t* shader) { return shader->shader->getInfoLog(); } + +GLSLANG_EXPORT const char* glslang_shader_get_info_debug_log(glslang_shader_t* shader) { return shader->shader->getInfoDebugLog(); } + +GLSLANG_EXPORT void glslang_shader_delete(glslang_shader_t* shader) +{ + if (!shader) + return; + + delete (shader->shader); + delete (shader); +} + +GLSLANG_EXPORT glslang_program_t* glslang_program_create() +{ + glslang_program_t* p = new glslang_program_t(); + p->program = new glslang::TProgram(); + return p; +} + +GLSLANG_EXPORT void glslang_program_delete(glslang_program_t* program) +{ + if (!program) + return; + + delete (program->program); + delete (program); +} + +GLSLANG_EXPORT void glslang_program_add_shader(glslang_program_t* program, glslang_shader_t* shader) +{ + program->program->addShader(shader->shader); +} + +GLSLANG_EXPORT int glslang_program_link(glslang_program_t* program, int messages) +{ + return (int)program->program->link((EShMessages)messages); +} + +GLSLANG_EXPORT const char* glslang_program_get_info_log(glslang_program_t* program) +{ + return program->program->getInfoLog(); +} + +GLSLANG_EXPORT const char* glslang_program_get_info_debug_log(glslang_program_t* program) +{ + return program->program->getInfoDebugLog(); +} diff --git a/third_party/glslang/glslang/CMakeLists.txt b/third_party/glslang/glslang/CMakeLists.txt new file mode 100644 index 0000000..1c7d22a --- /dev/null +++ b/third_party/glslang/glslang/CMakeLists.txt @@ -0,0 +1,226 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +if(WIN32) + add_subdirectory(OSDependent/Windows) +elseif(UNIX) + add_subdirectory(OSDependent/Unix) +else(WIN32) + message("unknown platform") +endif(WIN32) + +if(EMSCRIPTEN OR ENABLE_GLSLANG_JS) + # May be enabled on non-Emscripten builds for binary-size testing. + add_subdirectory(OSDependent/Web) +endif(EMSCRIPTEN OR ENABLE_GLSLANG_JS) + +################################################################################ +# GenericCodeGen +################################################################################ +add_library(GenericCodeGen STATIC + GenericCodeGen/CodeGen.cpp + GenericCodeGen/Link.cpp) +set_property(TARGET GenericCodeGen PROPERTY POSITION_INDEPENDENT_CODE ON) +set_property(TARGET GenericCodeGen PROPERTY FOLDER glslang) + +################################################################################ +# MachineIndependent +################################################################################ +set(MACHINEINDEPENDENT_SOURCES + MachineIndependent/glslang.m4 + MachineIndependent/glslang.y + MachineIndependent/glslang_tab.cpp + MachineIndependent/attribute.cpp + MachineIndependent/Constant.cpp + MachineIndependent/iomapper.cpp + MachineIndependent/InfoSink.cpp + MachineIndependent/Initialize.cpp + MachineIndependent/IntermTraverse.cpp + MachineIndependent/Intermediate.cpp + MachineIndependent/ParseContextBase.cpp + MachineIndependent/ParseHelper.cpp + MachineIndependent/PoolAlloc.cpp + MachineIndependent/RemoveTree.cpp + MachineIndependent/Scan.cpp + MachineIndependent/ShaderLang.cpp + MachineIndependent/SymbolTable.cpp + MachineIndependent/Versions.cpp + MachineIndependent/intermOut.cpp + MachineIndependent/limits.cpp + MachineIndependent/linkValidate.cpp + MachineIndependent/parseConst.cpp + MachineIndependent/reflection.cpp + MachineIndependent/preprocessor/Pp.cpp + MachineIndependent/preprocessor/PpAtom.cpp + MachineIndependent/preprocessor/PpContext.cpp + MachineIndependent/preprocessor/PpScanner.cpp + MachineIndependent/preprocessor/PpTokens.cpp + MachineIndependent/propagateNoContraction.cpp +) + +set(MACHINEINDEPENDENT_HEADERS + MachineIndependent/attribute.h + MachineIndependent/glslang_tab.cpp.h + MachineIndependent/gl_types.h + MachineIndependent/Initialize.h + MachineIndependent/iomapper.h + MachineIndependent/LiveTraverser.h + MachineIndependent/localintermediate.h + MachineIndependent/ParseHelper.h + MachineIndependent/reflection.h + MachineIndependent/RemoveTree.h + MachineIndependent/Scan.h + MachineIndependent/ScanContext.h + MachineIndependent/SymbolTable.h + MachineIndependent/Versions.h + MachineIndependent/parseVersions.h + MachineIndependent/propagateNoContraction.h + MachineIndependent/preprocessor/PpContext.h + MachineIndependent/preprocessor/PpTokens.h +) + +if(ENABLE_HLSL) + list(APPEND MACHINEINDEPENDENT_SOURCES + HLSL/hlslAttributes.cpp + HLSL/hlslParseHelper.cpp + HLSL/hlslScanContext.cpp + HLSL/hlslOpMap.cpp + HLSL/hlslTokenStream.cpp + HLSL/hlslGrammar.cpp + HLSL/hlslParseables.cpp) + + list(APPEND MACHINEINDEPENDENT_HEADERS + HLSL/hlslAttributes.h + HLSL/hlslParseHelper.h + HLSL/hlslTokens.h + HLSL/hlslScanContext.h + HLSL/hlslOpMap.h + HLSL/hlslTokenStream.h + HLSL/hlslGrammar.h + HLSL/hlslParseables.h) +endif(ENABLE_HLSL) + +add_library(MachineIndependent STATIC ${MACHINEINDEPENDENT_SOURCES} ${MACHINEINDEPENDENT_HEADERS}) +set_property(TARGET MachineIndependent PROPERTY POSITION_INDEPENDENT_CODE ON) +set_property(TARGET MachineIndependent PROPERTY FOLDER glslang) + +glslang_add_build_info_dependency(MachineIndependent) + +glslang_pch(MachineIndependent MachineIndependent/pch.h) + +target_link_libraries(MachineIndependent PRIVATE OGLCompiler OSDependent GenericCodeGen) + +################################################################################ +# glslang +################################################################################ +set(GLSLANG_SOURCES + CInterface/glslang_c_interface.cpp) + +set(GLSLANG_HEADERS + Public/ShaderLang.h + Include/arrays.h + Include/BaseTypes.h + Include/Common.h + Include/ConstantUnion.h + Include/glslang_c_interface.h + Include/glslang_c_shader_types.h + Include/InfoSink.h + Include/InitializeGlobals.h + Include/intermediate.h + Include/PoolAlloc.h + Include/ResourceLimits.h + Include/ShHandle.h + Include/Types.h) + +add_library(glslang ${LIB_TYPE} ${BISON_GLSLParser_OUTPUT_SOURCE} ${GLSLANG_SOURCES} ${GLSLANG_HEADERS}) +set_target_properties(glslang PROPERTIES + FOLDER glslang + POSITION_INDEPENDENT_CODE ON + VERSION "${GLSLANG_VERSION}" + SOVERSION "${GLSLANG_VERSION_MAJOR}") +target_link_libraries(glslang PRIVATE OGLCompiler OSDependent MachineIndependent) +target_include_directories(glslang PUBLIC + $ + $) + +glslang_add_build_info_dependency(glslang) + +glslang_only_export_explicit_symbols(glslang) + +if(WIN32 AND BUILD_SHARED_LIBS) + set_target_properties(glslang PROPERTIES PREFIX "") +endif() + +################################################################################ +# source_groups +################################################################################ +if(WIN32) + source_group("Public" REGULAR_EXPRESSION "Public/*") + source_group("MachineIndependent" REGULAR_EXPRESSION "MachineIndependent/[^/]*") + source_group("Include" REGULAR_EXPRESSION "Include/[^/]*") + source_group("GenericCodeGen" REGULAR_EXPRESSION "GenericCodeGen/*") + source_group("MachineIndependent\\Preprocessor" REGULAR_EXPRESSION "MachineIndependent/preprocessor/*") + source_group("HLSL" REGULAR_EXPRESSION "HLSL/*") + source_group("CInterface" REGULAR_EXPRESSION "CInterface/*") +endif(WIN32) + +################################################################################ +# install +################################################################################ +if(ENABLE_GLSLANG_INSTALL) + if(BUILD_SHARED_LIBS) + install(TARGETS glslang + EXPORT glslangTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else() + install(TARGETS glslang MachineIndependent GenericCodeGen + EXPORT glslangTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + + install(EXPORT glslangTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + + set(ALL_HEADERS + ${GLSLANG_HEADERS} + ${MACHINEINDEPENDENT_HEADERS}) + + foreach(file ${ALL_HEADERS}) + get_filename_component(dir ${file} DIRECTORY) + install(FILES ${file} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/glslang/${dir}) + endforeach() + + install(FILES ${GLSLANG_BUILD_INFO_H} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/glslang) + +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp b/third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp new file mode 100644 index 0000000..b3c7226 --- /dev/null +++ b/third_party/glslang/glslang/GenericCodeGen/CodeGen.cpp @@ -0,0 +1,76 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "../Include/ShHandle.h" +#include "../MachineIndependent/Versions.h" + +// +// Here is where real machine specific high-level data would be defined. +// +class TGenericCompiler : public TCompiler { +public: + TGenericCompiler(EShLanguage l, int dOptions) : TCompiler(l, infoSink), debugOptions(dOptions) { } + virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile); + TInfoSink infoSink; + int debugOptions; +}; + +// +// This function must be provided to create the actual +// compile object used by higher level code. It returns +// a subclass of TCompiler. +// +TCompiler* ConstructCompiler(EShLanguage language, int debugOptions) +{ + return new TGenericCompiler(language, debugOptions); +} + +// +// Delete the compiler made by ConstructCompiler +// +void DeleteCompiler(TCompiler* compiler) +{ + delete compiler; +} + +// +// Generate code from the given parse tree +// +bool TGenericCompiler::compile(TIntermNode* /*root*/, int /*version*/, EProfile /*profile*/) +{ + haveValidObjectCode = true; + + return haveValidObjectCode; +} diff --git a/third_party/glslang/glslang/GenericCodeGen/Link.cpp b/third_party/glslang/glslang/GenericCodeGen/Link.cpp new file mode 100644 index 0000000..c38db0f --- /dev/null +++ b/third_party/glslang/glslang/GenericCodeGen/Link.cpp @@ -0,0 +1,91 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// The top level algorithms for linking multiple +// shaders together. +// +#include "../Include/Common.h" +#include "../Include/ShHandle.h" + +// +// Actual link object, derived from the shader handle base classes. +// +class TGenericLinker : public TLinker { +public: + TGenericLinker(EShExecutable e, int dOptions) : TLinker(e, infoSink), debugOptions(dOptions) { } + bool link(TCompilerList&, TUniformMap*) { return true; } + void getAttributeBindings(ShBindingTable const **) const { } + TInfoSink infoSink; + int debugOptions; +}; + +// +// The internal view of a uniform/float object exchanged with the driver. +// +class TUniformLinkedMap : public TUniformMap { +public: + TUniformLinkedMap() { } + virtual int getLocation(const char*) { return 0; } +}; + +TShHandleBase* ConstructLinker(EShExecutable executable, int debugOptions) +{ + return new TGenericLinker(executable, debugOptions); +} + +void DeleteLinker(TShHandleBase* linker) +{ + delete linker; +} + +TUniformMap* ConstructUniformMap() +{ + return new TUniformLinkedMap(); +} + +void DeleteUniformMap(TUniformMap* map) +{ + delete map; +} + +TShHandleBase* ConstructBindings() +{ + return 0; +} + +void DeleteBindingList(TShHandleBase* bindingList) +{ + delete bindingList; +} diff --git a/third_party/glslang/glslang/HLSL/hlslAttributes.cpp b/third_party/glslang/glslang/HLSL/hlslAttributes.cpp new file mode 100644 index 0000000..0cc0d3f --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslAttributes.cpp @@ -0,0 +1,149 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "hlslAttributes.h" +#include "hlslParseHelper.h" + +namespace glslang { + // Map the given string to an attribute enum from TAttributeType, + // or EatNone if invalid. + TAttributeType HlslParseContext::attributeFromName(const TString& nameSpace, const TString& name) const + { + // handle names within a namespace + + if (nameSpace == "vk") { + if (name == "input_attachment_index") + return EatInputAttachment; + else if (name == "location") + return EatLocation; + else if (name == "binding") + return EatBinding; + else if (name == "global_cbuffer_binding") + return EatGlobalBinding; + else if (name == "builtin") + return EatBuiltIn; + else if (name == "constant_id") + return EatConstantId; + else if (name == "push_constant") + return EatPushConstant; + } else if (nameSpace == "spv") { + if (name == "format_rgba32f") return EatFormatRgba32f; + if (name == "format_rgba16f") return EatFormatRgba16f; + if (name == "format_r32f") return EatFormatR32f; + if (name == "format_rgba8") return EatFormatRgba8; + if (name == "format_rgba8snorm") return EatFormatRgba8Snorm; + if (name == "format_rg32f") return EatFormatRg32f; + if (name == "format_rg16f") return EatFormatRg16f; + if (name == "format_r11fg11fb10f") return EatFormatR11fG11fB10f; + if (name == "format_r16f") return EatFormatR16f; + if (name == "format_rgba16") return EatFormatRgba16; + if (name == "format_rgb10a2") return EatFormatRgb10A2; + if (name == "format_rg16") return EatFormatRg16; + if (name == "format_rg8") return EatFormatRg8; + if (name == "format_r16") return EatFormatR16; + if (name == "format_r8") return EatFormatR8; + if (name == "format_rgba16snorm") return EatFormatRgba16Snorm; + if (name == "format_rg16snorm") return EatFormatRg16Snorm; + if (name == "format_rg8snorm") return EatFormatRg8Snorm; + if (name == "format_r16snorm") return EatFormatR16Snorm; + if (name == "format_r8snorm") return EatFormatR8Snorm; + if (name == "format_rgba32i") return EatFormatRgba32i; + if (name == "format_rgba16i") return EatFormatRgba16i; + if (name == "format_rgba8i") return EatFormatRgba8i; + if (name == "format_r32i") return EatFormatR32i; + if (name == "format_rg32i") return EatFormatRg32i; + if (name == "format_rg16i") return EatFormatRg16i; + if (name == "format_rg8i") return EatFormatRg8i; + if (name == "format_r16i") return EatFormatR16i; + if (name == "format_r8i") return EatFormatR8i; + if (name == "format_rgba32ui") return EatFormatRgba32ui; + if (name == "format_rgba16ui") return EatFormatRgba16ui; + if (name == "format_rgba8ui") return EatFormatRgba8ui; + if (name == "format_r32ui") return EatFormatR32ui; + if (name == "format_rgb10a2ui") return EatFormatRgb10a2ui; + if (name == "format_rg32ui") return EatFormatRg32ui; + if (name == "format_rg16ui") return EatFormatRg16ui; + if (name == "format_rg8ui") return EatFormatRg8ui; + if (name == "format_r16ui") return EatFormatR16ui; + if (name == "format_r8ui") return EatFormatR8ui; + + if (name == "nonwritable") return EatNonWritable; + if (name == "nonreadable") return EatNonReadable; + } else if (nameSpace.size() > 0) + return EatNone; + + // handle names with no namespace + + if (name == "allow_uav_condition") + return EatAllow_uav_condition; + else if (name == "branch") + return EatBranch; + else if (name == "call") + return EatCall; + else if (name == "domain") + return EatDomain; + else if (name == "earlydepthstencil") + return EatEarlyDepthStencil; + else if (name == "fastopt") + return EatFastOpt; + else if (name == "flatten") + return EatFlatten; + else if (name == "forcecase") + return EatForceCase; + else if (name == "instance") + return EatInstance; + else if (name == "maxtessfactor") + return EatMaxTessFactor; + else if (name == "maxvertexcount") + return EatMaxVertexCount; + else if (name == "numthreads") + return EatNumThreads; + else if (name == "outputcontrolpoints") + return EatOutputControlPoints; + else if (name == "outputtopology") + return EatOutputTopology; + else if (name == "partitioning") + return EatPartitioning; + else if (name == "patchconstantfunc") + return EatPatchConstantFunc; + else if (name == "unroll") + return EatUnroll; + else if (name == "loop") + return EatLoop; + else + return EatNone; + } + +} // end namespace glslang diff --git a/third_party/glslang/glslang/HLSL/hlslAttributes.h b/third_party/glslang/glslang/HLSL/hlslAttributes.h new file mode 100644 index 0000000..62faa5b --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslAttributes.h @@ -0,0 +1,59 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef HLSLATTRIBUTES_H_ +#define HLSLATTRIBUTES_H_ + +#include +#include + +#include "../MachineIndependent/attribute.h" +#include "../MachineIndependent/SymbolTable.h" +#include "hlslScanContext.h" + +namespace glslang { + + class TFunctionDeclarator { + public: + TFunctionDeclarator() : function(nullptr), body(nullptr) { } + TSourceLoc loc; + TFunction* function; + TAttributes attributes; + TVector* body; + }; + +} // end namespace glslang + +#endif // HLSLATTRIBUTES_H_ diff --git a/third_party/glslang/glslang/HLSL/hlslGrammar.cpp b/third_party/glslang/glslang/HLSL/hlslGrammar.cpp new file mode 100644 index 0000000..f30c640 --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslGrammar.cpp @@ -0,0 +1,4190 @@ +// +// Copyright (C) 2016-2018 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This is a set of mutually recursive methods implementing the HLSL grammar. +// Generally, each returns +// - through an argument: a type specifically appropriate to which rule it +// recognized +// - through the return value: true/false to indicate whether or not it +// recognized its rule +// +// As much as possible, only grammar recognition should happen in this file, +// with all other work being farmed out to hlslParseHelper.cpp, which in turn +// will build the AST. +// +// The next token, yet to be "accepted" is always sitting in 'token'. +// When a method says it accepts a rule, that means all tokens involved +// in the rule will have been consumed, and none left in 'token'. +// + +#include "hlslTokens.h" +#include "hlslGrammar.h" +#include "hlslAttributes.h" + +namespace glslang { + +// Root entry point to this recursive decent parser. +// Return true if compilation unit was successfully accepted. +bool HlslGrammar::parse() +{ + advanceToken(); + return acceptCompilationUnit(); +} + +void HlslGrammar::expected(const char* syntax) +{ + parseContext.error(token.loc, "Expected", syntax, ""); +} + +void HlslGrammar::unimplemented(const char* error) +{ + parseContext.error(token.loc, "Unimplemented", error, ""); +} + +// IDENTIFIER +// THIS +// type that can be used as IDENTIFIER +// +// Only process the next token if it is an identifier. +// Return true if it was an identifier. +bool HlslGrammar::acceptIdentifier(HlslToken& idToken) +{ + // IDENTIFIER + if (peekTokenClass(EHTokIdentifier)) { + idToken = token; + advanceToken(); + return true; + } + + // THIS + // -> maps to the IDENTIFIER spelled with the internal special name for 'this' + if (peekTokenClass(EHTokThis)) { + idToken = token; + advanceToken(); + idToken.tokenClass = EHTokIdentifier; + idToken.string = NewPoolTString(intermediate.implicitThisName); + return true; + } + + // type that can be used as IDENTIFIER + + // Even though "sample", "bool", "float", etc keywords (for types, interpolation modifiers), + // they ARE still accepted as identifiers. This is not a dense space: e.g, "void" is not a + // valid identifier, nor is "linear". This code special cases the known instances of this, so + // e.g, "int sample;" or "float float;" is accepted. Other cases can be added here if needed. + + const char* idString = getTypeString(peek()); + if (idString == nullptr) + return false; + + token.string = NewPoolTString(idString); + token.tokenClass = EHTokIdentifier; + idToken = token; + typeIdentifiers = true; + + advanceToken(); + + return true; +} + +// compilationUnit +// : declaration_list EOF +// +bool HlslGrammar::acceptCompilationUnit() +{ + if (! acceptDeclarationList(unitNode)) + return false; + + if (! peekTokenClass(EHTokNone)) + return false; + + // set root of AST + if (unitNode && !unitNode->getAsAggregate()) + unitNode = intermediate.growAggregate(nullptr, unitNode); + intermediate.setTreeRoot(unitNode); + + return true; +} + +// Recognize the following, but with the extra condition that it can be +// successfully terminated by EOF or '}'. +// +// declaration_list +// : list of declaration_or_semicolon followed by EOF or RIGHT_BRACE +// +// declaration_or_semicolon +// : declaration +// : SEMICOLON +// +bool HlslGrammar::acceptDeclarationList(TIntermNode*& nodeList) +{ + do { + // HLSL allows extra semicolons between global declarations + do { } while (acceptTokenClass(EHTokSemicolon)); + + // EOF or RIGHT_BRACE + if (peekTokenClass(EHTokNone) || peekTokenClass(EHTokRightBrace)) + return true; + + // declaration + if (! acceptDeclaration(nodeList)) + return false; + } while (true); + + return true; +} + +// sampler_state +// : LEFT_BRACE [sampler_state_assignment ... ] RIGHT_BRACE +// +// sampler_state_assignment +// : sampler_state_identifier EQUAL value SEMICOLON +// +// sampler_state_identifier +// : ADDRESSU +// | ADDRESSV +// | ADDRESSW +// | BORDERCOLOR +// | FILTER +// | MAXANISOTROPY +// | MAXLOD +// | MINLOD +// | MIPLODBIAS +// +bool HlslGrammar::acceptSamplerState() +{ + // TODO: this should be genericized to accept a list of valid tokens and + // return token/value pairs. Presently it is specific to texture values. + + if (! acceptTokenClass(EHTokLeftBrace)) + return true; + + parseContext.warn(token.loc, "unimplemented", "immediate sampler state", ""); + + do { + // read state name + HlslToken state; + if (! acceptIdentifier(state)) + break; // end of list + + // FXC accepts any case + TString stateName = *state.string; + std::transform(stateName.begin(), stateName.end(), stateName.begin(), ::tolower); + + if (! acceptTokenClass(EHTokAssign)) { + expected("assign"); + return false; + } + + if (stateName == "minlod" || stateName == "maxlod") { + if (! peekTokenClass(EHTokIntConstant)) { + expected("integer"); + return false; + } + + TIntermTyped* lod = nullptr; + if (! acceptLiteral(lod)) // should never fail, since we just looked for an integer + return false; + } else if (stateName == "maxanisotropy") { + if (! peekTokenClass(EHTokIntConstant)) { + expected("integer"); + return false; + } + + TIntermTyped* maxAnisotropy = nullptr; + if (! acceptLiteral(maxAnisotropy)) // should never fail, since we just looked for an integer + return false; + } else if (stateName == "filter") { + HlslToken filterMode; + if (! acceptIdentifier(filterMode)) { + expected("filter mode"); + return false; + } + } else if (stateName == "addressu" || stateName == "addressv" || stateName == "addressw") { + HlslToken addrMode; + if (! acceptIdentifier(addrMode)) { + expected("texture address mode"); + return false; + } + } else if (stateName == "miplodbias") { + TIntermTyped* lodBias = nullptr; + if (! acceptLiteral(lodBias)) { + expected("lod bias"); + return false; + } + } else if (stateName == "bordercolor") { + return false; + } else { + expected("texture state"); + return false; + } + + // SEMICOLON + if (! acceptTokenClass(EHTokSemicolon)) { + expected("semicolon"); + return false; + } + } while (true); + + if (! acceptTokenClass(EHTokRightBrace)) + return false; + + return true; +} + +// sampler_declaration_dx9 +// : SAMPLER identifier EQUAL sampler_type sampler_state +// +bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/) +{ + if (! acceptTokenClass(EHTokSampler)) + return false; + + // TODO: remove this when DX9 style declarations are implemented. + unimplemented("Direct3D 9 sampler declaration"); + + // read sampler name + HlslToken name; + if (! acceptIdentifier(name)) { + expected("sampler name"); + return false; + } + + if (! acceptTokenClass(EHTokAssign)) { + expected("="); + return false; + } + + return false; +} + +// declaration +// : attributes attributed_declaration +// | NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE +// +// attributed_declaration +// : sampler_declaration_dx9 post_decls SEMICOLON +// | fully_specified_type // for cbuffer/tbuffer +// | fully_specified_type declarator_list SEMICOLON // for non cbuffer/tbuffer +// | fully_specified_type identifier function_parameters post_decls compound_statement // function definition +// | fully_specified_type identifier sampler_state post_decls compound_statement // sampler definition +// | typedef declaration +// +// declarator_list +// : declarator COMMA declarator COMMA declarator... // zero or more declarators +// +// declarator +// : identifier array_specifier post_decls +// | identifier array_specifier post_decls EQUAL assignment_expression +// | identifier function_parameters post_decls // function prototype +// +// Parsing has to go pretty far in to know whether it's a variable, prototype, or +// function definition, so the implementation below doesn't perfectly divide up the grammar +// as above. (The 'identifier' in the first item in init_declarator list is the +// same as 'identifier' for function declarations.) +// +// This can generate more than one subtree, one per initializer or a function body. +// All initializer subtrees are put in their own aggregate node, making one top-level +// node for all the initializers. Each function created is a top-level node to grow +// into the passed-in nodeList. +// +// If 'nodeList' is passed in as non-null, it must be an aggregate to extend for +// each top-level node the declaration creates. Otherwise, if only one top-level +// node in generated here, that is want is returned in nodeList. +// +bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList) +{ + // NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE + if (acceptTokenClass(EHTokNamespace)) { + HlslToken namespaceToken; + if (!acceptIdentifier(namespaceToken)) { + expected("namespace name"); + return false; + } + parseContext.pushNamespace(*namespaceToken.string); + if (!acceptTokenClass(EHTokLeftBrace)) { + expected("{"); + return false; + } + if (!acceptDeclarationList(nodeList)) { + expected("declaration list"); + return false; + } + if (!acceptTokenClass(EHTokRightBrace)) { + expected("}"); + return false; + } + parseContext.popNamespace(); + return true; + } + + bool declarator_list = false; // true when processing comma separation + + // attributes + TFunctionDeclarator declarator; + acceptAttributes(declarator.attributes); + + // typedef + bool typedefDecl = acceptTokenClass(EHTokTypedef); + + TType declaredType; + + // DX9 sampler declaration use a different syntax + // DX9 shaders need to run through HLSL compiler (fxc) via a back compat mode, it isn't going to + // be possible to simultaneously compile D3D10+ style shaders and DX9 shaders. If we want to compile DX9 + // HLSL shaders, this will have to be a master level switch + // As such, the sampler keyword in D3D10+ turns into an automatic sampler type, and is commonly used + // For that reason, this line is commented out + // if (acceptSamplerDeclarationDX9(declaredType)) + // return true; + + bool forbidDeclarators = (peekTokenClass(EHTokCBuffer) || peekTokenClass(EHTokTBuffer)); + // fully_specified_type + if (! acceptFullySpecifiedType(declaredType, nodeList, declarator.attributes, forbidDeclarators)) + return false; + + // cbuffer and tbuffer end with the closing '}'. + // No semicolon is included. + if (forbidDeclarators) + return true; + + // Check if there are invalid in/out qualifiers + switch (declaredType.getQualifier().storage) { + case EvqIn: + case EvqOut: + case EvqInOut: + parseContext.error(token.loc, "in/out qualifiers are only valid on parameters", token.string->c_str(), ""); + default: + break; + } + + // declarator_list + // : declarator + // : identifier + HlslToken idToken; + TIntermAggregate* initializers = nullptr; + while (acceptIdentifier(idToken)) { + TString *fullName = idToken.string; + if (parseContext.symbolTable.atGlobalLevel()) + parseContext.getFullNamespaceName(fullName); + if (peekTokenClass(EHTokLeftParen)) { + // looks like function parameters + + // merge in the attributes into the return type + parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType, true); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction(fullName); + + // function_parameters + declarator.function = new TFunction(fullName, declaredType); + if (!acceptFunctionParameters(*declarator.function)) { + expected("function parameter list"); + return false; + } + + // post_decls + acceptPostDecls(declarator.function->getWritableType().getQualifier()); + + // compound_statement (function body definition) or just a prototype? + declarator.loc = token.loc; + if (peekTokenClass(EHTokLeftBrace)) { + if (declarator_list) + parseContext.error(idToken.loc, "function body can't be in a declarator list", "{", ""); + if (typedefDecl) + parseContext.error(idToken.loc, "function body can't be in a typedef", "{", ""); + return acceptFunctionDefinition(declarator, nodeList, nullptr); + } else { + if (typedefDecl) + parseContext.error(idToken.loc, "function typedefs not implemented", "{", ""); + parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, true); + } + } else { + // A variable declaration. + + // merge in the attributes, the first time around, into the shared type + if (! declarator_list) + parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType); + + // Fix the storage qualifier if it's a global. + if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel()) + declaredType.getQualifier().storage = EvqUniform; + + // recognize array_specifier + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + + // We can handle multiple variables per type declaration, so + // the number of types can expand when arrayness is different. + TType variableType; + variableType.shallowCopy(declaredType); + + // In the most general case, arrayness is potentially coming both from the + // declared type and from the variable: "int[] a[];" or just one or the other. + // Merge it all to the variableType, so all arrayness is part of the variableType. + variableType.transferArraySizes(arraySizes); + variableType.copyArrayInnerSizes(declaredType.getArraySizes()); + + // samplers accept immediate sampler state + if (variableType.getBasicType() == EbtSampler) { + if (! acceptSamplerState()) + return false; + } + + // post_decls + acceptPostDecls(variableType.getQualifier()); + + // EQUAL assignment_expression + TIntermTyped* expressionNode = nullptr; + if (acceptTokenClass(EHTokAssign)) { + if (typedefDecl) + parseContext.error(idToken.loc, "can't have an initializer", "typedef", ""); + if (! acceptAssignmentExpression(expressionNode)) { + expected("initializer"); + return false; + } + } + + // TODO: things scoped within an annotation need their own name space; + // TODO: non-constant strings are not yet handled. + if (!(variableType.getBasicType() == EbtString && !variableType.getQualifier().isConstant()) && + parseContext.getAnnotationNestingLevel() == 0) { + if (typedefDecl) + parseContext.declareTypedef(idToken.loc, *fullName, variableType); + else if (variableType.getBasicType() == EbtBlock) { + if (expressionNode) + parseContext.error(idToken.loc, "buffer aliasing not yet supported", "block initializer", ""); + parseContext.declareBlock(idToken.loc, variableType, fullName); + parseContext.declareStructBufferCounter(idToken.loc, variableType, *fullName); + } else { + if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) { + // this isn't really an individual variable, but a member of the $Global buffer + parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName); + } else { + // Declare the variable and add any initializer code to the AST. + // The top-level node is always made into an aggregate, as that's + // historically how the AST has been. + initializers = intermediate.growAggregate(initializers, + parseContext.declareVariable(idToken.loc, *fullName, variableType, expressionNode), + idToken.loc); + } + } + } + } + + // COMMA + if (acceptTokenClass(EHTokComma)) + declarator_list = true; + } + + // The top-level initializer node is a sequence. + if (initializers != nullptr) + initializers->setOperator(EOpSequence); + + // if we have a locally scoped static, it needs a globally scoped initializer + if (declaredType.getQualifier().storage == EvqGlobal && !parseContext.symbolTable.atGlobalLevel()) { + unitNode = intermediate.growAggregate(unitNode, initializers, idToken.loc); + } else { + // Add the initializers' aggregate to the nodeList we were handed. + if (nodeList) + nodeList = intermediate.growAggregate(nodeList, initializers); + else + nodeList = initializers; + } + + // SEMICOLON + if (! acceptTokenClass(EHTokSemicolon)) { + // This may have been a false detection of what appeared to be a declaration, but + // was actually an assignment such as "float = 4", where "float" is an identifier. + // We put the token back to let further parsing happen for cases where that may + // happen. This errors on the side of caution, and mostly triggers the error. + if (peek() == EHTokAssign || peek() == EHTokLeftBracket || peek() == EHTokDot || peek() == EHTokComma) + recedeToken(); + else + expected(";"); + return false; + } + + return true; +} + +// control_declaration +// : fully_specified_type identifier EQUAL expression +// +bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node) +{ + node = nullptr; + TAttributes attributes; + + // fully_specified_type + TType type; + if (! acceptFullySpecifiedType(type, attributes)) + return false; + + if (attributes.size() > 0) + parseContext.warn(token.loc, "attributes don't apply to control declaration", "", ""); + + // filter out type casts + if (peekTokenClass(EHTokLeftParen)) { + recedeToken(); + return false; + } + + // identifier + HlslToken idToken; + if (! acceptIdentifier(idToken)) { + expected("identifier"); + return false; + } + + // EQUAL + TIntermTyped* expressionNode = nullptr; + if (! acceptTokenClass(EHTokAssign)) { + expected("="); + return false; + } + + // expression + if (! acceptExpression(expressionNode)) { + expected("initializer"); + return false; + } + + node = parseContext.declareVariable(idToken.loc, *idToken.string, type, expressionNode); + + return true; +} + +// fully_specified_type +// : type_specifier +// | type_qualifier type_specifier +// +bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributes& attributes) +{ + TIntermNode* nodeList = nullptr; + return acceptFullySpecifiedType(type, nodeList, attributes); +} +bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributes& attributes, bool forbidDeclarators) +{ + // type_qualifier + TQualifier qualifier; + qualifier.clear(); + if (! acceptQualifier(qualifier)) + return false; + TSourceLoc loc = token.loc; + + // type_specifier + if (! acceptType(type, nodeList)) { + // If this is not a type, we may have inadvertently gone down a wrong path + // by parsing "sample", which can be treated like either an identifier or a + // qualifier. Back it out, if we did. + if (qualifier.sample) + recedeToken(); + + return false; + } + + if (type.getBasicType() == EbtBlock) { + // the type was a block, which set some parts of the qualifier + parseContext.mergeQualifiers(type.getQualifier(), qualifier); + + // merge in the attributes + parseContext.transferTypeAttributes(token.loc, attributes, type); + + // further, it can create an anonymous instance of the block + // (cbuffer and tbuffer don't consume the next identifier, and + // should set forbidDeclarators) + if (forbidDeclarators || peek() != EHTokIdentifier) + parseContext.declareBlock(loc, type); + } else { + // Some qualifiers are set when parsing the type. Merge those with + // whatever comes from acceptQualifier. + assert(qualifier.layoutFormat == ElfNone); + + qualifier.layoutFormat = type.getQualifier().layoutFormat; + qualifier.precision = type.getQualifier().precision; + + if (type.getQualifier().storage == EvqOut || + type.getQualifier().storage == EvqBuffer) { + qualifier.storage = type.getQualifier().storage; + qualifier.readonly = type.getQualifier().readonly; + } + + if (type.isBuiltIn()) + qualifier.builtIn = type.getQualifier().builtIn; + + type.getQualifier() = qualifier; + } + + return true; +} + +// type_qualifier +// : qualifier qualifier ... +// +// Zero or more of these, so this can't return false. +// +bool HlslGrammar::acceptQualifier(TQualifier& qualifier) +{ + do { + switch (peek()) { + case EHTokStatic: + qualifier.storage = EvqGlobal; + break; + case EHTokExtern: + // TODO: no meaning in glslang? + break; + case EHTokShared: + // TODO: hint + break; + case EHTokGroupShared: + qualifier.storage = EvqShared; + break; + case EHTokUniform: + qualifier.storage = EvqUniform; + break; + case EHTokConst: + qualifier.storage = EvqConst; + break; + case EHTokVolatile: + qualifier.volatil = true; + break; + case EHTokLinear: + qualifier.smooth = true; + break; + case EHTokCentroid: + qualifier.centroid = true; + break; + case EHTokNointerpolation: + qualifier.flat = true; + break; + case EHTokNoperspective: + qualifier.nopersp = true; + break; + case EHTokSample: + qualifier.sample = true; + break; + case EHTokRowMajor: + qualifier.layoutMatrix = ElmColumnMajor; + break; + case EHTokColumnMajor: + qualifier.layoutMatrix = ElmRowMajor; + break; + case EHTokPrecise: + qualifier.noContraction = true; + break; + case EHTokIn: + if (qualifier.storage != EvqUniform) { + qualifier.storage = (qualifier.storage == EvqOut) ? EvqInOut : EvqIn; + } + break; + case EHTokOut: + qualifier.storage = (qualifier.storage == EvqIn) ? EvqInOut : EvqOut; + break; + case EHTokInOut: + qualifier.storage = EvqInOut; + break; + case EHTokLayout: + if (! acceptLayoutQualifierList(qualifier)) + return false; + continue; + case EHTokGloballyCoherent: + qualifier.coherent = true; + break; + case EHTokInline: + // TODO: map this to SPIR-V function control + break; + + // GS geometries: these are specified on stage input variables, and are an error (not verified here) + // for output variables. + case EHTokPoint: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgPoints)) + return false; + break; + case EHTokLine: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgLines)) + return false; + break; + case EHTokTriangle: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgTriangles)) + return false; + break; + case EHTokLineAdj: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgLinesAdjacency)) + return false; + break; + case EHTokTriangleAdj: + qualifier.storage = EvqIn; + if (!parseContext.handleInputGeometry(token.loc, ElgTrianglesAdjacency)) + return false; + break; + + default: + return true; + } + advanceToken(); + } while (true); +} + +// layout_qualifier_list +// : LAYOUT LEFT_PAREN layout_qualifier COMMA layout_qualifier ... RIGHT_PAREN +// +// layout_qualifier +// : identifier +// | identifier EQUAL expression +// +// Zero or more of these, so this can't return false. +// +bool HlslGrammar::acceptLayoutQualifierList(TQualifier& qualifier) +{ + if (! acceptTokenClass(EHTokLayout)) + return false; + + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + return false; + + do { + // identifier + HlslToken idToken; + if (! acceptIdentifier(idToken)) + break; + + // EQUAL expression + if (acceptTokenClass(EHTokAssign)) { + TIntermTyped* expr; + if (! acceptConditionalExpression(expr)) { + expected("expression"); + return false; + } + parseContext.setLayoutQualifier(idToken.loc, qualifier, *idToken.string, expr); + } else + parseContext.setLayoutQualifier(idToken.loc, qualifier, *idToken.string); + + // COMMA + if (! acceptTokenClass(EHTokComma)) + break; + } while (true); + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + return true; +} + +// template_type +// : FLOAT +// | DOUBLE +// | INT +// | DWORD +// | UINT +// | BOOL +// +bool HlslGrammar::acceptTemplateVecMatBasicType(TBasicType& basicType) +{ + switch (peek()) { + case EHTokFloat: + basicType = EbtFloat; + break; + case EHTokDouble: + basicType = EbtDouble; + break; + case EHTokInt: + case EHTokDword: + basicType = EbtInt; + break; + case EHTokUint: + basicType = EbtUint; + break; + case EHTokBool: + basicType = EbtBool; + break; + default: + return false; + } + + advanceToken(); + + return true; +} + +// vector_template_type +// : VECTOR +// | VECTOR LEFT_ANGLE template_type COMMA integer_literal RIGHT_ANGLE +// +bool HlslGrammar::acceptVectorTemplateType(TType& type) +{ + if (! acceptTokenClass(EHTokVector)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) { + // in HLSL, 'vector' alone means float4. + new(&type) TType(EbtFloat, EvqTemporary, 4); + return true; + } + + TBasicType basicType; + if (! acceptTemplateVecMatBasicType(basicType)) { + expected("scalar type"); + return false; + } + + // COMMA + if (! acceptTokenClass(EHTokComma)) { + expected(","); + return false; + } + + // integer + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + + TIntermTyped* vecSize; + if (! acceptLiteral(vecSize)) + return false; + + const int vecSizeI = vecSize->getAsConstantUnion()->getConstArray()[0].getIConst(); + + new(&type) TType(basicType, EvqTemporary, vecSizeI); + + if (vecSizeI == 1) + type.makeVector(); + + if (!acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + return true; +} + +// matrix_template_type +// : MATRIX +// | MATRIX LEFT_ANGLE template_type COMMA integer_literal COMMA integer_literal RIGHT_ANGLE +// +bool HlslGrammar::acceptMatrixTemplateType(TType& type) +{ + if (! acceptTokenClass(EHTokMatrix)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) { + // in HLSL, 'matrix' alone means float4x4. + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4); + return true; + } + + TBasicType basicType; + if (! acceptTemplateVecMatBasicType(basicType)) { + expected("scalar type"); + return false; + } + + // COMMA + if (! acceptTokenClass(EHTokComma)) { + expected(","); + return false; + } + + // integer rows + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + + TIntermTyped* rows; + if (! acceptLiteral(rows)) + return false; + + // COMMA + if (! acceptTokenClass(EHTokComma)) { + expected(","); + return false; + } + + // integer cols + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + + TIntermTyped* cols; + if (! acceptLiteral(cols)) + return false; + + new(&type) TType(basicType, EvqTemporary, 0, + rows->getAsConstantUnion()->getConstArray()[0].getIConst(), + cols->getAsConstantUnion()->getConstArray()[0].getIConst()); + + if (!acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + return true; +} + +// layout_geometry +// : LINESTREAM +// | POINTSTREAM +// | TRIANGLESTREAM +// +bool HlslGrammar::acceptOutputPrimitiveGeometry(TLayoutGeometry& geometry) +{ + // read geometry type + const EHlslTokenClass geometryType = peek(); + + switch (geometryType) { + case EHTokPointStream: geometry = ElgPoints; break; + case EHTokLineStream: geometry = ElgLineStrip; break; + case EHTokTriangleStream: geometry = ElgTriangleStrip; break; + default: + return false; // not a layout geometry + } + + advanceToken(); // consume the layout keyword + return true; +} + +// tessellation_decl_type +// : INPUTPATCH +// | OUTPUTPATCH +// +bool HlslGrammar::acceptTessellationDeclType(TBuiltInVariable& patchType) +{ + // read geometry type + const EHlslTokenClass tessType = peek(); + + switch (tessType) { + case EHTokInputPatch: patchType = EbvInputPatch; break; + case EHTokOutputPatch: patchType = EbvOutputPatch; break; + default: + return false; // not a tessellation decl + } + + advanceToken(); // consume the keyword + return true; +} + +// tessellation_patch_template_type +// : tessellation_decl_type LEFT_ANGLE type comma integer_literal RIGHT_ANGLE +// +bool HlslGrammar::acceptTessellationPatchTemplateType(TType& type) +{ + TBuiltInVariable patchType; + + if (! acceptTessellationDeclType(patchType)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) + return false; + + if (! acceptType(type)) { + expected("tessellation patch type"); + return false; + } + + if (! acceptTokenClass(EHTokComma)) + return false; + + // integer size + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + + TIntermTyped* size; + if (! acceptLiteral(size)) + return false; + + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(size->getAsConstantUnion()->getConstArray()[0].getIConst()); + type.transferArraySizes(arraySizes); + type.getQualifier().builtIn = patchType; + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + return true; +} + +// stream_out_template_type +// : output_primitive_geometry_type LEFT_ANGLE type RIGHT_ANGLE +// +bool HlslGrammar::acceptStreamOutTemplateType(TType& type, TLayoutGeometry& geometry) +{ + geometry = ElgNone; + + if (! acceptOutputPrimitiveGeometry(geometry)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) + return false; + + if (! acceptType(type)) { + expected("stream output type"); + return false; + } + + type.getQualifier().storage = EvqOut; + type.getQualifier().builtIn = EbvGsOutputStream; + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + return true; +} + +// annotations +// : LEFT_ANGLE declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE +// +bool HlslGrammar::acceptAnnotations(TQualifier&) +{ + if (! acceptTokenClass(EHTokLeftAngle)) + return false; + + // note that we are nesting a name space + parseContext.nestAnnotations(); + + // declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE + do { + // eat any extra SEMI_COLON; don't know if the grammar calls for this or not + while (acceptTokenClass(EHTokSemicolon)) + ; + + if (acceptTokenClass(EHTokRightAngle)) + break; + + // declaration + TIntermNode* node = nullptr; + if (! acceptDeclaration(node)) { + expected("declaration in annotation"); + return false; + } + } while (true); + + parseContext.unnestAnnotations(); + return true; +} + +// subpass input type +// : SUBPASSINPUT +// | SUBPASSINPUT VECTOR LEFT_ANGLE template_type RIGHT_ANGLE +// | SUBPASSINPUTMS +// | SUBPASSINPUTMS VECTOR LEFT_ANGLE template_type RIGHT_ANGLE +bool HlslGrammar::acceptSubpassInputType(TType& type) +{ + // read subpass type + const EHlslTokenClass subpassInputType = peek(); + + bool multisample; + + switch (subpassInputType) { + case EHTokSubpassInput: multisample = false; break; + case EHTokSubpassInputMS: multisample = true; break; + default: + return false; // not a subpass input declaration + } + + advanceToken(); // consume the sampler type keyword + + TType subpassType(EbtFloat, EvqUniform, 4); // default type is float4 + + if (acceptTokenClass(EHTokLeftAngle)) { + if (! acceptType(subpassType)) { + expected("scalar or vector type"); + return false; + } + + const TBasicType basicRetType = subpassType.getBasicType() ; + + switch (basicRetType) { + case EbtFloat: + case EbtUint: + case EbtInt: + case EbtStruct: + break; + default: + unimplemented("basic type in subpass input"); + return false; + } + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + } + + const TBasicType subpassBasicType = subpassType.isStruct() ? (*subpassType.getStruct())[0].type->getBasicType() + : subpassType.getBasicType(); + + TSampler sampler; + sampler.setSubpass(subpassBasicType, multisample); + + // Remember the declared return type. Function returns false on error. + if (!parseContext.setTextureReturnType(sampler, subpassType, token.loc)) + return false; + + type.shallowCopy(TType(sampler, EvqUniform)); + + return true; +} + +// sampler_type for DX9 compatibility +// : SAMPLER +// | SAMPLER1D +// | SAMPLER2D +// | SAMPLER3D +// | SAMPLERCUBE +bool HlslGrammar::acceptSamplerTypeDX9(TType &type) +{ + // read sampler type + const EHlslTokenClass samplerType = peek(); + + TSamplerDim dim = EsdNone; + TType txType(EbtFloat, EvqUniform, 4); // default type is float4 + + bool isShadow = false; + + switch (samplerType) + { + case EHTokSampler: dim = Esd2D; break; + case EHTokSampler1d: dim = Esd1D; break; + case EHTokSampler2d: dim = Esd2D; break; + case EHTokSampler3d: dim = Esd3D; break; + case EHTokSamplerCube: dim = EsdCube; break; + default: + return false; // not a dx9 sampler declaration + } + + advanceToken(); // consume the sampler type keyword + + TArraySizes *arraySizes = nullptr; // TODO: array + + TSampler sampler; + sampler.set(txType.getBasicType(), dim, false, isShadow, false); + + if (!parseContext.setTextureReturnType(sampler, txType, token.loc)) + return false; + + type.shallowCopy(TType(sampler, EvqUniform, arraySizes)); + type.getQualifier().layoutFormat = ElfNone; + + return true; +} + +// sampler_type +// : SAMPLER +// | SAMPLER1D +// | SAMPLER2D +// | SAMPLER3D +// | SAMPLERCUBE +// | SAMPLERSTATE +// | SAMPLERCOMPARISONSTATE +bool HlslGrammar::acceptSamplerType(TType& type) +{ + // read sampler type + const EHlslTokenClass samplerType = peek(); + + // TODO: for DX9 + // TSamplerDim dim = EsdNone; + + bool isShadow = false; + + switch (samplerType) { + case EHTokSampler: break; + case EHTokSampler1d: /*dim = Esd1D*/; break; + case EHTokSampler2d: /*dim = Esd2D*/; break; + case EHTokSampler3d: /*dim = Esd3D*/; break; + case EHTokSamplerCube: /*dim = EsdCube*/; break; + case EHTokSamplerState: break; + case EHTokSamplerComparisonState: isShadow = true; break; + default: + return false; // not a sampler declaration + } + + advanceToken(); // consume the sampler type keyword + + TArraySizes* arraySizes = nullptr; // TODO: array + + TSampler sampler; + sampler.setPureSampler(isShadow); + + type.shallowCopy(TType(sampler, EvqUniform, arraySizes)); + + return true; +} + +// texture_type +// | BUFFER +// | TEXTURE1D +// | TEXTURE1DARRAY +// | TEXTURE2D +// | TEXTURE2DARRAY +// | TEXTURE3D +// | TEXTURECUBE +// | TEXTURECUBEARRAY +// | TEXTURE2DMS +// | TEXTURE2DMSARRAY +// | RWBUFFER +// | RWTEXTURE1D +// | RWTEXTURE1DARRAY +// | RWTEXTURE2D +// | RWTEXTURE2DARRAY +// | RWTEXTURE3D + +bool HlslGrammar::acceptTextureType(TType& type) +{ + const EHlslTokenClass textureType = peek(); + + TSamplerDim dim = EsdNone; + bool array = false; + bool ms = false; + bool image = false; + bool combined = true; + + switch (textureType) { + case EHTokBuffer: dim = EsdBuffer; combined = false; break; + case EHTokTexture1d: dim = Esd1D; break; + case EHTokTexture1darray: dim = Esd1D; array = true; break; + case EHTokTexture2d: dim = Esd2D; break; + case EHTokTexture2darray: dim = Esd2D; array = true; break; + case EHTokTexture3d: dim = Esd3D; break; + case EHTokTextureCube: dim = EsdCube; break; + case EHTokTextureCubearray: dim = EsdCube; array = true; break; + case EHTokTexture2DMS: dim = Esd2D; ms = true; break; + case EHTokTexture2DMSarray: dim = Esd2D; array = true; ms = true; break; + case EHTokRWBuffer: dim = EsdBuffer; image=true; break; + case EHTokRWTexture1d: dim = Esd1D; array=false; image=true; break; + case EHTokRWTexture1darray: dim = Esd1D; array=true; image=true; break; + case EHTokRWTexture2d: dim = Esd2D; array=false; image=true; break; + case EHTokRWTexture2darray: dim = Esd2D; array=true; image=true; break; + case EHTokRWTexture3d: dim = Esd3D; array=false; image=true; break; + default: + return false; // not a texture declaration + } + + advanceToken(); // consume the texture object keyword + + TType txType(EbtFloat, EvqUniform, 4); // default type is float4 + + TIntermTyped* msCount = nullptr; + + // texture type: required for multisample types and RWBuffer/RWTextures! + if (acceptTokenClass(EHTokLeftAngle)) { + if (! acceptType(txType)) { + expected("scalar or vector type"); + return false; + } + + const TBasicType basicRetType = txType.getBasicType() ; + + switch (basicRetType) { + case EbtFloat: + case EbtUint: + case EbtInt: + case EbtStruct: + break; + default: + unimplemented("basic type in texture"); + return false; + } + + // Buffers can handle small mats if they fit in 4 components + if (dim == EsdBuffer && txType.isMatrix()) { + if ((txType.getMatrixCols() * txType.getMatrixRows()) > 4) { + expected("components < 4 in matrix buffer type"); + return false; + } + + // TODO: except we don't handle it yet... + unimplemented("matrix type in buffer"); + return false; + } + + if (!txType.isScalar() && !txType.isVector() && !txType.isStruct()) { + expected("scalar, vector, or struct type"); + return false; + } + + if (ms && acceptTokenClass(EHTokComma)) { + // read sample count for multisample types, if given + if (! peekTokenClass(EHTokIntConstant)) { + expected("multisample count"); + return false; + } + + if (! acceptLiteral(msCount)) // should never fail, since we just found an integer + return false; + } + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + } else if (ms) { + expected("texture type for multisample"); + return false; + } else if (image) { + expected("type for RWTexture/RWBuffer"); + return false; + } + + TArraySizes* arraySizes = nullptr; + const bool shadow = false; // declared on the sampler + + TSampler sampler; + TLayoutFormat format = ElfNone; + + // Buffer, RWBuffer and RWTexture (images) require a TLayoutFormat. We handle only a limit set. + if (image || dim == EsdBuffer) + format = parseContext.getLayoutFromTxType(token.loc, txType); + + const TBasicType txBasicType = txType.isStruct() ? (*txType.getStruct())[0].type->getBasicType() + : txType.getBasicType(); + + // Non-image Buffers are combined + if (dim == EsdBuffer && !image) { + sampler.set(txType.getBasicType(), dim, array); + } else { + // DX10 textures are separated. TODO: DX9. + if (image) { + sampler.setImage(txBasicType, dim, array, shadow, ms); + } else { + sampler.setTexture(txBasicType, dim, array, shadow, ms); + } + } + + // Remember the declared return type. Function returns false on error. + if (!parseContext.setTextureReturnType(sampler, txType, token.loc)) + return false; + + // Force uncombined, if necessary + if (!combined) + sampler.combined = false; + + type.shallowCopy(TType(sampler, EvqUniform, arraySizes)); + type.getQualifier().layoutFormat = format; + + return true; +} + +// If token is for a type, update 'type' with the type information, +// and return true and advance. +// Otherwise, return false, and don't advance +bool HlslGrammar::acceptType(TType& type) +{ + TIntermNode* nodeList = nullptr; + return acceptType(type, nodeList); +} +bool HlslGrammar::acceptType(TType& type, TIntermNode*& nodeList) +{ + // Basic types for min* types, use native halfs if the option allows them. + bool enable16BitTypes = parseContext.hlslEnable16BitTypes(); + + const TBasicType min16float_bt = enable16BitTypes ? EbtFloat16 : EbtFloat; + const TBasicType min10float_bt = enable16BitTypes ? EbtFloat16 : EbtFloat; + const TBasicType half_bt = enable16BitTypes ? EbtFloat16 : EbtFloat; + const TBasicType min16int_bt = enable16BitTypes ? EbtInt16 : EbtInt; + const TBasicType min12int_bt = enable16BitTypes ? EbtInt16 : EbtInt; + const TBasicType min16uint_bt = enable16BitTypes ? EbtUint16 : EbtUint; + + // Some types might have turned into identifiers. Take the hit for checking + // when this has happened. + if (typeIdentifiers) { + const char* identifierString = getTypeString(peek()); + if (identifierString != nullptr) { + TString name = identifierString; + // if it's an identifier, it's not a type + if (parseContext.symbolTable.find(name) != nullptr) + return false; + } + } + + bool isUnorm = false; + bool isSnorm = false; + + // Accept snorm and unorm. Presently, this is ignored, save for an error check below. + switch (peek()) { + case EHTokUnorm: + isUnorm = true; + advanceToken(); // eat the token + break; + case EHTokSNorm: + isSnorm = true; + advanceToken(); // eat the token + break; + default: + break; + } + + switch (peek()) { + case EHTokVector: + return acceptVectorTemplateType(type); + break; + + case EHTokMatrix: + return acceptMatrixTemplateType(type); + break; + + case EHTokPointStream: // fall through + case EHTokLineStream: // ... + case EHTokTriangleStream: // ... + { + TLayoutGeometry geometry; + if (! acceptStreamOutTemplateType(type, geometry)) + return false; + + if (! parseContext.handleOutputGeometry(token.loc, geometry)) + return false; + + return true; + } + + case EHTokInputPatch: // fall through + case EHTokOutputPatch: // ... + { + if (! acceptTessellationPatchTemplateType(type)) + return false; + + return true; + } + + case EHTokSampler: // fall through + case EHTokSampler1d: // ... + case EHTokSampler2d: // ... + case EHTokSampler3d: // ... + case EHTokSamplerCube: // ... + if (parseContext.hlslDX9Compatible()) + return acceptSamplerTypeDX9(type); + else + return acceptSamplerType(type); + break; + + case EHTokSamplerState: // fall through + case EHTokSamplerComparisonState: // ... + return acceptSamplerType(type); + break; + + case EHTokSubpassInput: // fall through + case EHTokSubpassInputMS: // ... + return acceptSubpassInputType(type); + break; + + case EHTokBuffer: // fall through + case EHTokTexture1d: // ... + case EHTokTexture1darray: // ... + case EHTokTexture2d: // ... + case EHTokTexture2darray: // ... + case EHTokTexture3d: // ... + case EHTokTextureCube: // ... + case EHTokTextureCubearray: // ... + case EHTokTexture2DMS: // ... + case EHTokTexture2DMSarray: // ... + case EHTokRWTexture1d: // ... + case EHTokRWTexture1darray: // ... + case EHTokRWTexture2d: // ... + case EHTokRWTexture2darray: // ... + case EHTokRWTexture3d: // ... + case EHTokRWBuffer: // ... + return acceptTextureType(type); + break; + + case EHTokAppendStructuredBuffer: + case EHTokByteAddressBuffer: + case EHTokConsumeStructuredBuffer: + case EHTokRWByteAddressBuffer: + case EHTokRWStructuredBuffer: + case EHTokStructuredBuffer: + return acceptStructBufferType(type); + break; + + case EHTokTextureBuffer: + return acceptTextureBufferType(type); + break; + + case EHTokConstantBuffer: + return acceptConstantBufferType(type); + + case EHTokClass: + case EHTokStruct: + case EHTokCBuffer: + case EHTokTBuffer: + return acceptStruct(type, nodeList); + + case EHTokIdentifier: + // An identifier could be for a user-defined type. + // Note we cache the symbol table lookup, to save for a later rule + // when this is not a type. + if (parseContext.lookupUserType(*token.string, type) != nullptr) { + advanceToken(); + return true; + } else + return false; + + case EHTokVoid: + new(&type) TType(EbtVoid); + break; + + case EHTokString: + new(&type) TType(EbtString); + break; + + case EHTokFloat: + new(&type) TType(EbtFloat); + break; + case EHTokFloat1: + new(&type) TType(EbtFloat); + type.makeVector(); + break; + case EHTokFloat2: + new(&type) TType(EbtFloat, EvqTemporary, 2); + break; + case EHTokFloat3: + new(&type) TType(EbtFloat, EvqTemporary, 3); + break; + case EHTokFloat4: + new(&type) TType(EbtFloat, EvqTemporary, 4); + break; + + case EHTokDouble: + new(&type) TType(EbtDouble); + break; + case EHTokDouble1: + new(&type) TType(EbtDouble); + type.makeVector(); + break; + case EHTokDouble2: + new(&type) TType(EbtDouble, EvqTemporary, 2); + break; + case EHTokDouble3: + new(&type) TType(EbtDouble, EvqTemporary, 3); + break; + case EHTokDouble4: + new(&type) TType(EbtDouble, EvqTemporary, 4); + break; + + case EHTokInt: + case EHTokDword: + new(&type) TType(EbtInt); + break; + case EHTokInt1: + new(&type) TType(EbtInt); + type.makeVector(); + break; + case EHTokInt2: + new(&type) TType(EbtInt, EvqTemporary, 2); + break; + case EHTokInt3: + new(&type) TType(EbtInt, EvqTemporary, 3); + break; + case EHTokInt4: + new(&type) TType(EbtInt, EvqTemporary, 4); + break; + + case EHTokUint: + new(&type) TType(EbtUint); + break; + case EHTokUint1: + new(&type) TType(EbtUint); + type.makeVector(); + break; + case EHTokUint2: + new(&type) TType(EbtUint, EvqTemporary, 2); + break; + case EHTokUint3: + new(&type) TType(EbtUint, EvqTemporary, 3); + break; + case EHTokUint4: + new(&type) TType(EbtUint, EvqTemporary, 4); + break; + + case EHTokUint64: + new(&type) TType(EbtUint64); + break; + + case EHTokBool: + new(&type) TType(EbtBool); + break; + case EHTokBool1: + new(&type) TType(EbtBool); + type.makeVector(); + break; + case EHTokBool2: + new(&type) TType(EbtBool, EvqTemporary, 2); + break; + case EHTokBool3: + new(&type) TType(EbtBool, EvqTemporary, 3); + break; + case EHTokBool4: + new(&type) TType(EbtBool, EvqTemporary, 4); + break; + + case EHTokHalf: + new(&type) TType(half_bt, EvqTemporary); + break; + case EHTokHalf1: + new(&type) TType(half_bt, EvqTemporary); + type.makeVector(); + break; + case EHTokHalf2: + new(&type) TType(half_bt, EvqTemporary, 2); + break; + case EHTokHalf3: + new(&type) TType(half_bt, EvqTemporary, 3); + break; + case EHTokHalf4: + new(&type) TType(half_bt, EvqTemporary, 4); + break; + + case EHTokMin16float: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin16float1: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin16float2: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin16float3: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin16float4: + new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokMin10float: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin10float1: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin10float2: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin10float3: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin10float4: + new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokMin16int: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin16int1: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin16int2: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin16int3: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin16int4: + new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokMin12int: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin12int1: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin12int2: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin12int3: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin12int4: + new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokMin16uint: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium); + break; + case EHTokMin16uint1: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium); + type.makeVector(); + break; + case EHTokMin16uint2: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 2); + break; + case EHTokMin16uint3: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 3); + break; + case EHTokMin16uint4: + new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 4); + break; + + case EHTokInt1x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 1); + break; + case EHTokInt1x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 2); + break; + case EHTokInt1x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 3); + break; + case EHTokInt1x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 4); + break; + case EHTokInt2x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 1); + break; + case EHTokInt2x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 2); + break; + case EHTokInt2x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 3); + break; + case EHTokInt2x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 4); + break; + case EHTokInt3x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 1); + break; + case EHTokInt3x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 2); + break; + case EHTokInt3x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 3); + break; + case EHTokInt3x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 4); + break; + case EHTokInt4x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 1); + break; + case EHTokInt4x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 2); + break; + case EHTokInt4x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 3); + break; + case EHTokInt4x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 4); + break; + + case EHTokUint1x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 1); + break; + case EHTokUint1x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 2); + break; + case EHTokUint1x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 3); + break; + case EHTokUint1x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 4); + break; + case EHTokUint2x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 1); + break; + case EHTokUint2x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 2); + break; + case EHTokUint2x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 3); + break; + case EHTokUint2x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 4); + break; + case EHTokUint3x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 1); + break; + case EHTokUint3x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 2); + break; + case EHTokUint3x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 3); + break; + case EHTokUint3x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 4); + break; + case EHTokUint4x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 1); + break; + case EHTokUint4x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 2); + break; + case EHTokUint4x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 3); + break; + case EHTokUint4x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 4); + break; + + case EHTokBool1x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 1); + break; + case EHTokBool1x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 2); + break; + case EHTokBool1x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 3); + break; + case EHTokBool1x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 4); + break; + case EHTokBool2x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 1); + break; + case EHTokBool2x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 2); + break; + case EHTokBool2x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 3); + break; + case EHTokBool2x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 4); + break; + case EHTokBool3x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 1); + break; + case EHTokBool3x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 2); + break; + case EHTokBool3x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 3); + break; + case EHTokBool3x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 4); + break; + case EHTokBool4x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 1); + break; + case EHTokBool4x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 2); + break; + case EHTokBool4x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 3); + break; + case EHTokBool4x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 4); + break; + + case EHTokFloat1x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 1); + break; + case EHTokFloat1x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 2); + break; + case EHTokFloat1x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 3); + break; + case EHTokFloat1x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 4); + break; + case EHTokFloat2x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 1); + break; + case EHTokFloat2x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 2); + break; + case EHTokFloat2x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 3); + break; + case EHTokFloat2x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 4); + break; + case EHTokFloat3x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 1); + break; + case EHTokFloat3x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 2); + break; + case EHTokFloat3x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 3); + break; + case EHTokFloat3x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 4); + break; + case EHTokFloat4x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 1); + break; + case EHTokFloat4x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 2); + break; + case EHTokFloat4x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 3); + break; + case EHTokFloat4x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4); + break; + + case EHTokHalf1x1: + new(&type) TType(half_bt, EvqTemporary, 0, 1, 1); + break; + case EHTokHalf1x2: + new(&type) TType(half_bt, EvqTemporary, 0, 1, 2); + break; + case EHTokHalf1x3: + new(&type) TType(half_bt, EvqTemporary, 0, 1, 3); + break; + case EHTokHalf1x4: + new(&type) TType(half_bt, EvqTemporary, 0, 1, 4); + break; + case EHTokHalf2x1: + new(&type) TType(half_bt, EvqTemporary, 0, 2, 1); + break; + case EHTokHalf2x2: + new(&type) TType(half_bt, EvqTemporary, 0, 2, 2); + break; + case EHTokHalf2x3: + new(&type) TType(half_bt, EvqTemporary, 0, 2, 3); + break; + case EHTokHalf2x4: + new(&type) TType(half_bt, EvqTemporary, 0, 2, 4); + break; + case EHTokHalf3x1: + new(&type) TType(half_bt, EvqTemporary, 0, 3, 1); + break; + case EHTokHalf3x2: + new(&type) TType(half_bt, EvqTemporary, 0, 3, 2); + break; + case EHTokHalf3x3: + new(&type) TType(half_bt, EvqTemporary, 0, 3, 3); + break; + case EHTokHalf3x4: + new(&type) TType(half_bt, EvqTemporary, 0, 3, 4); + break; + case EHTokHalf4x1: + new(&type) TType(half_bt, EvqTemporary, 0, 4, 1); + break; + case EHTokHalf4x2: + new(&type) TType(half_bt, EvqTemporary, 0, 4, 2); + break; + case EHTokHalf4x3: + new(&type) TType(half_bt, EvqTemporary, 0, 4, 3); + break; + case EHTokHalf4x4: + new(&type) TType(half_bt, EvqTemporary, 0, 4, 4); + break; + + case EHTokDouble1x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 1); + break; + case EHTokDouble1x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 2); + break; + case EHTokDouble1x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 3); + break; + case EHTokDouble1x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 4); + break; + case EHTokDouble2x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 1); + break; + case EHTokDouble2x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 2); + break; + case EHTokDouble2x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 3); + break; + case EHTokDouble2x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 4); + break; + case EHTokDouble3x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 1); + break; + case EHTokDouble3x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 2); + break; + case EHTokDouble3x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 3); + break; + case EHTokDouble3x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 4); + break; + case EHTokDouble4x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 1); + break; + case EHTokDouble4x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 2); + break; + case EHTokDouble4x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 3); + break; + case EHTokDouble4x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 4); + break; + + default: + return false; + } + + advanceToken(); + + if ((isUnorm || isSnorm) && !type.isFloatingDomain()) { + parseContext.error(token.loc, "unorm and snorm only valid in floating point domain", "", ""); + return false; + } + + return true; +} + +// struct +// : struct_type IDENTIFIER post_decls LEFT_BRACE struct_declaration_list RIGHT_BRACE +// | struct_type post_decls LEFT_BRACE struct_declaration_list RIGHT_BRACE +// | struct_type IDENTIFIER // use of previously declared struct type +// +// struct_type +// : STRUCT +// | CLASS +// | CBUFFER +// | TBUFFER +// +bool HlslGrammar::acceptStruct(TType& type, TIntermNode*& nodeList) +{ + // This storage qualifier will tell us whether it's an AST + // block type or just a generic structure type. + TStorageQualifier storageQualifier = EvqTemporary; + bool readonly = false; + + if (acceptTokenClass(EHTokCBuffer)) { + // CBUFFER + storageQualifier = EvqUniform; + } else if (acceptTokenClass(EHTokTBuffer)) { + // TBUFFER + storageQualifier = EvqBuffer; + readonly = true; + } else if (! acceptTokenClass(EHTokClass) && ! acceptTokenClass(EHTokStruct)) { + // Neither CLASS nor STRUCT + return false; + } + + // Now known to be one of CBUFFER, TBUFFER, CLASS, or STRUCT + + + // IDENTIFIER. It might also be a keyword which can double as an identifier. + // For example: 'cbuffer ConstantBuffer' or 'struct ConstantBuffer' is legal. + // 'cbuffer int' is also legal, and 'struct int' appears rejected only because + // it attempts to redefine the 'int' type. + const char* idString = getTypeString(peek()); + TString structName = ""; + if (peekTokenClass(EHTokIdentifier) || idString != nullptr) { + if (idString != nullptr) + structName = *idString; + else + structName = *token.string; + advanceToken(); + } + + // post_decls + TQualifier postDeclQualifier; + postDeclQualifier.clear(); + bool postDeclsFound = acceptPostDecls(postDeclQualifier); + + // LEFT_BRACE, or + // struct_type IDENTIFIER + if (! acceptTokenClass(EHTokLeftBrace)) { + if (structName.size() > 0 && !postDeclsFound && parseContext.lookupUserType(structName, type) != nullptr) { + // struct_type IDENTIFIER + return true; + } else { + expected("{"); + return false; + } + } + + + // struct_declaration_list + TTypeList* typeList; + // Save each member function so they can be processed after we have a fully formed 'this'. + TVector functionDeclarators; + + parseContext.pushNamespace(structName); + bool acceptedList = acceptStructDeclarationList(typeList, nodeList, functionDeclarators); + parseContext.popNamespace(); + + if (! acceptedList) { + expected("struct member declarations"); + return false; + } + + // RIGHT_BRACE + if (! acceptTokenClass(EHTokRightBrace)) { + expected("}"); + return false; + } + + // create the user-defined type + if (storageQualifier == EvqTemporary) + new(&type) TType(typeList, structName); + else { + postDeclQualifier.storage = storageQualifier; + postDeclQualifier.readonly = readonly; + new(&type) TType(typeList, structName, postDeclQualifier); // sets EbtBlock + } + + parseContext.declareStruct(token.loc, structName, type); + + // For member functions: now that we know the type of 'this', go back and + // - add their implicit argument with 'this' (not to the mangling, just the argument list) + // - parse the functions, their tokens were saved for deferred parsing (now) + for (int b = 0; b < (int)functionDeclarators.size(); ++b) { + // update signature + if (functionDeclarators[b].function->hasImplicitThis()) + functionDeclarators[b].function->addThisParameter(type, intermediate.implicitThisName); + } + + // All member functions get parsed inside the class/struct namespace and with the + // class/struct members in a symbol-table level. + parseContext.pushNamespace(structName); + parseContext.pushThisScope(type, functionDeclarators); + bool deferredSuccess = true; + for (int b = 0; b < (int)functionDeclarators.size() && deferredSuccess; ++b) { + // parse body + pushTokenStream(functionDeclarators[b].body); + if (! acceptFunctionBody(functionDeclarators[b], nodeList)) + deferredSuccess = false; + popTokenStream(); + } + parseContext.popThisScope(); + parseContext.popNamespace(); + + return deferredSuccess; +} + +// constantbuffer +// : CONSTANTBUFFER LEFT_ANGLE type RIGHT_ANGLE +bool HlslGrammar::acceptConstantBufferType(TType& type) +{ + if (! acceptTokenClass(EHTokConstantBuffer)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) { + expected("left angle bracket"); + return false; + } + + TType templateType; + if (! acceptType(templateType)) { + expected("type"); + return false; + } + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + TQualifier postDeclQualifier; + postDeclQualifier.clear(); + postDeclQualifier.storage = EvqUniform; + + if (templateType.isStruct()) { + // Make a block from the type parsed as the template argument + TTypeList* typeList = templateType.getWritableStruct(); + new(&type) TType(typeList, "", postDeclQualifier); // sets EbtBlock + + type.getQualifier().storage = EvqUniform; + + return true; + } else { + parseContext.error(token.loc, "non-structure type in ConstantBuffer", "", ""); + return false; + } +} + +// texture_buffer +// : TEXTUREBUFFER LEFT_ANGLE type RIGHT_ANGLE +bool HlslGrammar::acceptTextureBufferType(TType& type) +{ + if (! acceptTokenClass(EHTokTextureBuffer)) + return false; + + if (! acceptTokenClass(EHTokLeftAngle)) { + expected("left angle bracket"); + return false; + } + + TType templateType; + if (! acceptType(templateType)) { + expected("type"); + return false; + } + + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + + templateType.getQualifier().storage = EvqBuffer; + templateType.getQualifier().readonly = true; + + TType blockType(templateType.getWritableStruct(), "", templateType.getQualifier()); + + blockType.getQualifier().storage = EvqBuffer; + blockType.getQualifier().readonly = true; + + type.shallowCopy(blockType); + + return true; +} + + +// struct_buffer +// : APPENDSTRUCTUREDBUFFER +// | BYTEADDRESSBUFFER +// | CONSUMESTRUCTUREDBUFFER +// | RWBYTEADDRESSBUFFER +// | RWSTRUCTUREDBUFFER +// | STRUCTUREDBUFFER +bool HlslGrammar::acceptStructBufferType(TType& type) +{ + const EHlslTokenClass structBuffType = peek(); + + // TODO: globallycoherent + bool hasTemplateType = true; + bool readonly = false; + + TStorageQualifier storage = EvqBuffer; + TBuiltInVariable builtinType = EbvNone; + + switch (structBuffType) { + case EHTokAppendStructuredBuffer: + builtinType = EbvAppendConsume; + break; + case EHTokByteAddressBuffer: + hasTemplateType = false; + readonly = true; + builtinType = EbvByteAddressBuffer; + break; + case EHTokConsumeStructuredBuffer: + builtinType = EbvAppendConsume; + break; + case EHTokRWByteAddressBuffer: + hasTemplateType = false; + builtinType = EbvRWByteAddressBuffer; + break; + case EHTokRWStructuredBuffer: + builtinType = EbvRWStructuredBuffer; + break; + case EHTokStructuredBuffer: + builtinType = EbvStructuredBuffer; + readonly = true; + break; + default: + return false; // not a structure buffer type + } + + advanceToken(); // consume the structure keyword + + // type on which this StructedBuffer is templatized. E.g, StructedBuffer ==> MyStruct + TType* templateType = new TType; + + if (hasTemplateType) { + if (! acceptTokenClass(EHTokLeftAngle)) { + expected("left angle bracket"); + return false; + } + + if (! acceptType(*templateType)) { + expected("type"); + return false; + } + if (! acceptTokenClass(EHTokRightAngle)) { + expected("right angle bracket"); + return false; + } + } else { + // byte address buffers have no explicit type. + TType uintType(EbtUint, storage); + templateType->shallowCopy(uintType); + } + + // Create an unsized array out of that type. + // TODO: does this work if it's already an array type? + TArraySizes* unsizedArray = new TArraySizes; + unsizedArray->addInnerSize(UnsizedArraySize); + templateType->transferArraySizes(unsizedArray); + templateType->getQualifier().storage = storage; + + // field name is canonical for all structbuffers + templateType->setFieldName("@data"); + + TTypeList* blockStruct = new TTypeList; + TTypeLoc member = { templateType, token.loc }; + blockStruct->push_back(member); + + // This is the type of the buffer block (SSBO) + TType blockType(blockStruct, "", templateType->getQualifier()); + + blockType.getQualifier().storage = storage; + blockType.getQualifier().readonly = readonly; + blockType.getQualifier().builtIn = builtinType; + + // We may have created an equivalent type before, in which case we should use its + // deep structure. + parseContext.shareStructBufferType(blockType); + + type.shallowCopy(blockType); + + return true; +} + +// struct_declaration_list +// : struct_declaration SEMI_COLON struct_declaration SEMI_COLON ... +// +// struct_declaration +// : attributes fully_specified_type struct_declarator COMMA struct_declarator ... +// | attributes fully_specified_type IDENTIFIER function_parameters post_decls compound_statement // member-function definition +// +// struct_declarator +// : IDENTIFIER post_decls +// | IDENTIFIER array_specifier post_decls +// | IDENTIFIER function_parameters post_decls // member-function prototype +// +bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*& nodeList, + TVector& declarators) +{ + typeList = new TTypeList(); + HlslToken idToken; + + do { + // success on seeing the RIGHT_BRACE coming up + if (peekTokenClass(EHTokRightBrace)) + break; + + // struct_declaration + + // attributes + TAttributes attributes; + acceptAttributes(attributes); + + bool declarator_list = false; + + // fully_specified_type + TType memberType; + if (! acceptFullySpecifiedType(memberType, nodeList, attributes)) { + expected("member type"); + return false; + } + + // merge in the attributes + parseContext.transferTypeAttributes(token.loc, attributes, memberType); + + // struct_declarator COMMA struct_declarator ... + bool functionDefinitionAccepted = false; + do { + if (! acceptIdentifier(idToken)) { + expected("member name"); + return false; + } + + if (peekTokenClass(EHTokLeftParen)) { + // function_parameters + if (!declarator_list) { + declarators.resize(declarators.size() + 1); + // request a token stream for deferred processing + functionDefinitionAccepted = acceptMemberFunctionDefinition(nodeList, memberType, *idToken.string, + declarators.back()); + if (functionDefinitionAccepted) + break; + } + expected("member-function definition"); + return false; + } else { + // add it to the list of members + TTypeLoc member = { new TType(EbtVoid), token.loc }; + member.type->shallowCopy(memberType); + member.type->setFieldName(*idToken.string); + typeList->push_back(member); + + // array_specifier + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + if (arraySizes) + typeList->back().type->transferArraySizes(arraySizes); + + acceptPostDecls(member.type->getQualifier()); + + // EQUAL assignment_expression + if (acceptTokenClass(EHTokAssign)) { + parseContext.warn(idToken.loc, "struct-member initializers ignored", "typedef", ""); + TIntermTyped* expressionNode = nullptr; + if (! acceptAssignmentExpression(expressionNode)) { + expected("initializer"); + return false; + } + } + } + // success on seeing the SEMICOLON coming up + if (peekTokenClass(EHTokSemicolon)) + break; + + // COMMA + if (acceptTokenClass(EHTokComma)) + declarator_list = true; + else { + expected(","); + return false; + } + + } while (true); + + // SEMI_COLON + if (! functionDefinitionAccepted && ! acceptTokenClass(EHTokSemicolon)) { + expected(";"); + return false; + } + + } while (true); + + return true; +} + +// member_function_definition +// | function_parameters post_decls compound_statement +// +// Expects type to have EvqGlobal for a static member and +// EvqTemporary for non-static member. +bool HlslGrammar::acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TType& type, TString& memberName, + TFunctionDeclarator& declarator) +{ + bool accepted = false; + + TString* functionName = &memberName; + parseContext.getFullNamespaceName(functionName); + declarator.function = new TFunction(functionName, type); + if (type.getQualifier().storage == EvqTemporary) + declarator.function->setImplicitThis(); + else + declarator.function->setIllegalImplicitThis(); + + // function_parameters + if (acceptFunctionParameters(*declarator.function)) { + // post_decls + acceptPostDecls(declarator.function->getWritableType().getQualifier()); + + // compound_statement (function body definition) + if (peekTokenClass(EHTokLeftBrace)) { + declarator.loc = token.loc; + declarator.body = new TVector; + accepted = acceptFunctionDefinition(declarator, nodeList, declarator.body); + } + } else + expected("function parameter list"); + + return accepted; +} + +// function_parameters +// : LEFT_PAREN parameter_declaration COMMA parameter_declaration ... RIGHT_PAREN +// | LEFT_PAREN VOID RIGHT_PAREN +// +bool HlslGrammar::acceptFunctionParameters(TFunction& function) +{ + parseContext.beginParameterParsing(function); + + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + return false; + + // VOID RIGHT_PAREN + if (! acceptTokenClass(EHTokVoid)) { + do { + // parameter_declaration + if (! acceptParameterDeclaration(function)) + break; + + // COMMA + if (! acceptTokenClass(EHTokComma)) + break; + } while (true); + } + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + return true; +} + +// default_parameter_declaration +// : EQUAL conditional_expression +// : EQUAL initializer +bool HlslGrammar::acceptDefaultParameterDeclaration(const TType& type, TIntermTyped*& node) +{ + node = nullptr; + + // Valid not to have a default_parameter_declaration + if (!acceptTokenClass(EHTokAssign)) + return true; + + if (!acceptConditionalExpression(node)) { + if (!acceptInitializer(node)) + return false; + + // For initializer lists, we have to const-fold into a constructor for the type, so build + // that. + TFunction* constructor = parseContext.makeConstructorCall(token.loc, type); + if (constructor == nullptr) // cannot construct + return false; + + TIntermTyped* arguments = nullptr; + for (int i = 0; i < int(node->getAsAggregate()->getSequence().size()); i++) + parseContext.handleFunctionArgument(constructor, arguments, node->getAsAggregate()->getSequence()[i]->getAsTyped()); + + node = parseContext.handleFunctionCall(token.loc, constructor, node); + } + + if (node == nullptr) + return false; + + // If this is simply a constant, we can use it directly. + if (node->getAsConstantUnion()) + return true; + + // Otherwise, it has to be const-foldable. + TIntermTyped* origNode = node; + + node = intermediate.fold(node->getAsAggregate()); + + if (node != nullptr && origNode != node) + return true; + + parseContext.error(token.loc, "invalid default parameter value", "", ""); + + return false; +} + +// parameter_declaration +// : attributes attributed_declaration +// +// attributed_declaration +// : fully_specified_type post_decls [ = default_parameter_declaration ] +// | fully_specified_type identifier array_specifier post_decls [ = default_parameter_declaration ] +// +bool HlslGrammar::acceptParameterDeclaration(TFunction& function) +{ + // attributes + TAttributes attributes; + acceptAttributes(attributes); + + // fully_specified_type + TType* type = new TType; + if (! acceptFullySpecifiedType(*type, attributes)) + return false; + + // merge in the attributes + parseContext.transferTypeAttributes(token.loc, attributes, *type); + + // identifier + HlslToken idToken; + acceptIdentifier(idToken); + + // array_specifier + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + if (arraySizes) { + if (arraySizes->hasUnsized()) { + parseContext.error(token.loc, "function parameter requires array size", "[]", ""); + return false; + } + + type->transferArraySizes(arraySizes); + } + + // post_decls + acceptPostDecls(type->getQualifier()); + + TIntermTyped* defaultValue; + if (!acceptDefaultParameterDeclaration(*type, defaultValue)) + return false; + + parseContext.paramFix(*type); + + // If any prior parameters have default values, all the parameters after that must as well. + if (defaultValue == nullptr && function.getDefaultParamCount() > 0) { + parseContext.error(idToken.loc, "invalid parameter after default value parameters", idToken.string->c_str(), ""); + return false; + } + + TParameter param = { idToken.string, type, defaultValue }; + function.addParameter(param); + + return true; +} + +// Do the work to create the function definition in addition to +// parsing the body (compound_statement). +// +// If 'deferredTokens' are passed in, just get the token stream, +// don't process. +// +bool HlslGrammar::acceptFunctionDefinition(TFunctionDeclarator& declarator, TIntermNode*& nodeList, + TVector* deferredTokens) +{ + parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, false /* not prototype */); + + if (deferredTokens) + return captureBlockTokens(*deferredTokens); + else + return acceptFunctionBody(declarator, nodeList); +} + +bool HlslGrammar::acceptFunctionBody(TFunctionDeclarator& declarator, TIntermNode*& nodeList) +{ + // we might get back an entry-point + TIntermNode* entryPointNode = nullptr; + + // This does a pushScope() + TIntermNode* functionNode = parseContext.handleFunctionDefinition(declarator.loc, *declarator.function, + declarator.attributes, entryPointNode); + + // compound_statement + TIntermNode* functionBody = nullptr; + if (! acceptCompoundStatement(functionBody)) + return false; + + // this does a popScope() + parseContext.handleFunctionBody(declarator.loc, *declarator.function, functionBody, functionNode); + + // Hook up the 1 or 2 function definitions. + nodeList = intermediate.growAggregate(nodeList, functionNode); + nodeList = intermediate.growAggregate(nodeList, entryPointNode); + + return true; +} + +// Accept an expression with parenthesis around it, where +// the parenthesis ARE NOT expression parenthesis, but the +// syntactically required ones like in "if ( expression )". +// +// Also accepts a declaration expression; "if (int a = expression)". +// +// Note this one is not set up to be speculative; as it gives +// errors if not found. +// +bool HlslGrammar::acceptParenExpression(TIntermTyped*& expression) +{ + expression = nullptr; + + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + expected("("); + + bool decl = false; + TIntermNode* declNode = nullptr; + decl = acceptControlDeclaration(declNode); + if (decl) { + if (declNode == nullptr || declNode->getAsTyped() == nullptr) { + expected("initialized declaration"); + return false; + } else + expression = declNode->getAsTyped(); + } else { + // no declaration + if (! acceptExpression(expression)) { + expected("expression"); + return false; + } + } + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + + return true; +} + +// The top-level full expression recognizer. +// +// expression +// : assignment_expression COMMA assignment_expression COMMA assignment_expression ... +// +bool HlslGrammar::acceptExpression(TIntermTyped*& node) +{ + node = nullptr; + + // assignment_expression + if (! acceptAssignmentExpression(node)) + return false; + + if (! peekTokenClass(EHTokComma)) + return true; + + do { + // ... COMMA + TSourceLoc loc = token.loc; + advanceToken(); + + // ... assignment_expression + TIntermTyped* rightNode = nullptr; + if (! acceptAssignmentExpression(rightNode)) { + expected("assignment expression"); + return false; + } + + node = intermediate.addComma(node, rightNode, loc); + + if (! peekTokenClass(EHTokComma)) + return true; + } while (true); +} + +// initializer +// : LEFT_BRACE RIGHT_BRACE +// | LEFT_BRACE initializer_list RIGHT_BRACE +// +// initializer_list +// : assignment_expression COMMA assignment_expression COMMA ... +// +bool HlslGrammar::acceptInitializer(TIntermTyped*& node) +{ + // LEFT_BRACE + if (! acceptTokenClass(EHTokLeftBrace)) + return false; + + // RIGHT_BRACE + TSourceLoc loc = token.loc; + if (acceptTokenClass(EHTokRightBrace)) { + // a zero-length initializer list + node = intermediate.makeAggregate(loc); + return true; + } + + // initializer_list + node = nullptr; + do { + // assignment_expression + TIntermTyped* expr; + if (! acceptAssignmentExpression(expr)) { + expected("assignment expression in initializer list"); + return false; + } + + const bool firstNode = (node == nullptr); + + node = intermediate.growAggregate(node, expr, loc); + + // If every sub-node in the list has qualifier EvqConst, the returned node becomes + // EvqConst. Otherwise, it becomes EvqTemporary. That doesn't happen with e.g. + // EvqIn or EvqPosition, since the collection isn't EvqPosition if all the members are. + if (firstNode && expr->getQualifier().storage == EvqConst) + node->getQualifier().storage = EvqConst; + else if (expr->getQualifier().storage != EvqConst) + node->getQualifier().storage = EvqTemporary; + + // COMMA + if (acceptTokenClass(EHTokComma)) { + if (acceptTokenClass(EHTokRightBrace)) // allow trailing comma + return true; + continue; + } + + // RIGHT_BRACE + if (acceptTokenClass(EHTokRightBrace)) + return true; + + expected(", or }"); + return false; + } while (true); +} + +// Accept an assignment expression, where assignment operations +// associate right-to-left. That is, it is implicit, for example +// +// a op (b op (c op d)) +// +// assigment_expression +// : initializer +// | conditional_expression +// | conditional_expression assign_op conditional_expression assign_op conditional_expression ... +// +bool HlslGrammar::acceptAssignmentExpression(TIntermTyped*& node) +{ + // initializer + if (peekTokenClass(EHTokLeftBrace)) { + if (acceptInitializer(node)) + return true; + + expected("initializer"); + return false; + } + + // conditional_expression + if (! acceptConditionalExpression(node)) + return false; + + // assignment operation? + TOperator assignOp = HlslOpMap::assignment(peek()); + if (assignOp == EOpNull) + return true; + + // assign_op + TSourceLoc loc = token.loc; + advanceToken(); + + // conditional_expression assign_op conditional_expression ... + // Done by recursing this function, which automatically + // gets the right-to-left associativity. + TIntermTyped* rightNode = nullptr; + if (! acceptAssignmentExpression(rightNode)) { + expected("assignment expression"); + return false; + } + + node = parseContext.handleAssign(loc, assignOp, node, rightNode); + node = parseContext.handleLvalue(loc, "assign", node); + + if (node == nullptr) { + parseContext.error(loc, "could not create assignment", "", ""); + return false; + } + + if (! peekTokenClass(EHTokComma)) + return true; + + return true; +} + +// Accept a conditional expression, which associates right-to-left, +// accomplished by the "true" expression calling down to lower +// precedence levels than this level. +// +// conditional_expression +// : binary_expression +// | binary_expression QUESTION expression COLON assignment_expression +// +bool HlslGrammar::acceptConditionalExpression(TIntermTyped*& node) +{ + // binary_expression + if (! acceptBinaryExpression(node, PlLogicalOr)) + return false; + + if (! acceptTokenClass(EHTokQuestion)) + return true; + + node = parseContext.convertConditionalExpression(token.loc, node, false); + if (node == nullptr) + return false; + + ++parseContext.controlFlowNestingLevel; // this only needs to work right if no errors + + TIntermTyped* trueNode = nullptr; + if (! acceptExpression(trueNode)) { + expected("expression after ?"); + return false; + } + TSourceLoc loc = token.loc; + + if (! acceptTokenClass(EHTokColon)) { + expected(":"); + return false; + } + + TIntermTyped* falseNode = nullptr; + if (! acceptAssignmentExpression(falseNode)) { + expected("expression after :"); + return false; + } + + --parseContext.controlFlowNestingLevel; + + node = intermediate.addSelection(node, trueNode, falseNode, loc); + + return true; +} + +// Accept a binary expression, for binary operations that +// associate left-to-right. This is, it is implicit, for example +// +// ((a op b) op c) op d +// +// binary_expression +// : expression op expression op expression ... +// +// where 'expression' is the next higher level in precedence. +// +bool HlslGrammar::acceptBinaryExpression(TIntermTyped*& node, PrecedenceLevel precedenceLevel) +{ + if (precedenceLevel > PlMul) + return acceptUnaryExpression(node); + + // assignment_expression + if (! acceptBinaryExpression(node, (PrecedenceLevel)(precedenceLevel + 1))) + return false; + + do { + TOperator op = HlslOpMap::binary(peek()); + PrecedenceLevel tokenLevel = HlslOpMap::precedenceLevel(op); + if (tokenLevel < precedenceLevel) + return true; + + // ... op + TSourceLoc loc = token.loc; + advanceToken(); + + // ... expression + TIntermTyped* rightNode = nullptr; + if (! acceptBinaryExpression(rightNode, (PrecedenceLevel)(precedenceLevel + 1))) { + expected("expression"); + return false; + } + + node = intermediate.addBinaryMath(op, node, rightNode, loc); + if (node == nullptr) { + parseContext.error(loc, "Could not perform requested binary operation", "", ""); + return false; + } + } while (true); +} + +// unary_expression +// : (type) unary_expression +// | + unary_expression +// | - unary_expression +// | ! unary_expression +// | ~ unary_expression +// | ++ unary_expression +// | -- unary_expression +// | postfix_expression +// +bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node) +{ + // (type) unary_expression + // Have to look two steps ahead, because this could be, e.g., a + // postfix_expression instead, since that also starts with at "(". + if (acceptTokenClass(EHTokLeftParen)) { + TType castType; + if (acceptType(castType)) { + // recognize any array_specifier as part of the type + TArraySizes* arraySizes = nullptr; + acceptArraySpecifier(arraySizes); + if (arraySizes != nullptr) + castType.transferArraySizes(arraySizes); + TSourceLoc loc = token.loc; + if (acceptTokenClass(EHTokRightParen)) { + // We've matched "(type)" now, get the expression to cast + if (! acceptUnaryExpression(node)) + return false; + + // Hook it up like a constructor + TFunction* constructorFunction = parseContext.makeConstructorCall(loc, castType); + if (constructorFunction == nullptr) { + expected("type that can be constructed"); + return false; + } + TIntermTyped* arguments = nullptr; + parseContext.handleFunctionArgument(constructorFunction, arguments, node); + node = parseContext.handleFunctionCall(loc, constructorFunction, arguments); + + return node != nullptr; + } else { + // This could be a parenthesized constructor, ala (int(3)), and we just accepted + // the '(int' part. We must back up twice. + recedeToken(); + recedeToken(); + + // Note, there are no array constructors like + // (float[2](...)) + if (arraySizes != nullptr) + parseContext.error(loc, "parenthesized array constructor not allowed", "([]())", "", ""); + } + } else { + // This isn't a type cast, but it still started "(", so if it is a + // unary expression, it can only be a postfix_expression, so try that. + // Back it up first. + recedeToken(); + return acceptPostfixExpression(node); + } + } + + // peek for "op unary_expression" + TOperator unaryOp = HlslOpMap::preUnary(peek()); + + // postfix_expression (if no unary operator) + if (unaryOp == EOpNull) + return acceptPostfixExpression(node); + + // op unary_expression + TSourceLoc loc = token.loc; + advanceToken(); + if (! acceptUnaryExpression(node)) + return false; + + // + is a no-op + if (unaryOp == EOpAdd) + return true; + + node = intermediate.addUnaryMath(unaryOp, node, loc); + + // These unary ops require lvalues + if (unaryOp == EOpPreIncrement || unaryOp == EOpPreDecrement) + node = parseContext.handleLvalue(loc, "unary operator", node); + + return node != nullptr; +} + +// postfix_expression +// : LEFT_PAREN expression RIGHT_PAREN +// | literal +// | constructor +// | IDENTIFIER [ COLONCOLON IDENTIFIER [ COLONCOLON IDENTIFIER ... ] ] +// | function_call +// | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET +// | postfix_expression DOT IDENTIFIER +// | postfix_expression DOT IDENTIFIER arguments +// | postfix_expression arguments +// | postfix_expression INC_OP +// | postfix_expression DEC_OP +// +bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) +{ + // Not implemented as self-recursive: + // The logical "right recursion" is done with a loop at the end + + // idToken will pick up either a variable or a function name in a function call + HlslToken idToken; + + // Find something before the postfix operations, as they can't operate + // on nothing. So, no "return true", they fall through, only "return false". + if (acceptTokenClass(EHTokLeftParen)) { + // LEFT_PAREN expression RIGHT_PAREN + if (! acceptExpression(node)) { + expected("expression"); + return false; + } + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + } else if (acceptLiteral(node)) { + // literal (nothing else to do yet) + } else if (acceptConstructor(node)) { + // constructor (nothing else to do yet) + } else if (acceptIdentifier(idToken)) { + // user-type, namespace name, variable, or function name + TString* fullName = idToken.string; + while (acceptTokenClass(EHTokColonColon)) { + // user-type or namespace name + fullName = NewPoolTString(fullName->c_str()); + fullName->append(parseContext.scopeMangler); + if (acceptIdentifier(idToken)) + fullName->append(*idToken.string); + else { + expected("identifier after ::"); + return false; + } + } + if (! peekTokenClass(EHTokLeftParen)) { + node = parseContext.handleVariable(idToken.loc, fullName); + if (node == nullptr) + return false; + } else if (acceptFunctionCall(idToken.loc, *fullName, node, nullptr)) { + // function_call (nothing else to do yet) + } else { + expected("function call arguments"); + return false; + } + } else { + // nothing found, can't post operate + return false; + } + + // Something was found, chain as many postfix operations as exist. + do { + TSourceLoc loc = token.loc; + TOperator postOp = HlslOpMap::postUnary(peek()); + + // Consume only a valid post-unary operator, otherwise we are done. + switch (postOp) { + case EOpIndexDirectStruct: + case EOpIndexIndirect: + case EOpPostIncrement: + case EOpPostDecrement: + case EOpScoping: + advanceToken(); + break; + default: + return true; + } + + // We have a valid post-unary operator, process it. + switch (postOp) { + case EOpScoping: + case EOpIndexDirectStruct: + { + // DOT IDENTIFIER + // includes swizzles, member variables, and member functions + HlslToken field; + if (! acceptIdentifier(field)) { + expected("swizzle or member"); + return false; + } + + if (peekTokenClass(EHTokLeftParen)) { + // member function + TIntermTyped* thisNode = node; + + // arguments + if (! acceptFunctionCall(field.loc, *field.string, node, thisNode)) { + expected("function parameters"); + return false; + } + } else + node = parseContext.handleDotDereference(field.loc, node, *field.string); + + break; + } + case EOpIndexIndirect: + { + // LEFT_BRACKET integer_expression RIGHT_BRACKET + TIntermTyped* indexNode = nullptr; + if (! acceptExpression(indexNode) || + ! peekTokenClass(EHTokRightBracket)) { + expected("expression followed by ']'"); + return false; + } + advanceToken(); + node = parseContext.handleBracketDereference(indexNode->getLoc(), node, indexNode); + if (node == nullptr) + return false; + break; + } + case EOpPostIncrement: + // INC_OP + // fall through + case EOpPostDecrement: + // DEC_OP + node = intermediate.addUnaryMath(postOp, node, loc); + node = parseContext.handleLvalue(loc, "unary operator", node); + break; + default: + assert(0); + break; + } + } while (true); +} + +// constructor +// : type argument_list +// +bool HlslGrammar::acceptConstructor(TIntermTyped*& node) +{ + // type + TType type; + if (acceptType(type)) { + TFunction* constructorFunction = parseContext.makeConstructorCall(token.loc, type); + if (constructorFunction == nullptr) + return false; + + // arguments + TIntermTyped* arguments = nullptr; + if (! acceptArguments(constructorFunction, arguments)) { + // It's possible this is a type keyword used as an identifier. Put the token back + // for later use. + recedeToken(); + return false; + } + + if (arguments == nullptr) { + expected("one or more arguments"); + return false; + } + + // hook it up + node = parseContext.handleFunctionCall(arguments->getLoc(), constructorFunction, arguments); + + return node != nullptr; + } + + return false; +} + +// The function_call identifier was already recognized, and passed in as idToken. +// +// function_call +// : [idToken] arguments +// +bool HlslGrammar::acceptFunctionCall(const TSourceLoc& loc, TString& name, TIntermTyped*& node, TIntermTyped* baseObject) +{ + // name + TString* functionName = nullptr; + if (baseObject == nullptr) { + functionName = &name; + } else if (parseContext.isBuiltInMethod(loc, baseObject, name)) { + // Built-in methods are not in the symbol table as methods, but as global functions + // taking an explicit 'this' as the first argument. + functionName = NewPoolTString(BUILTIN_PREFIX); + functionName->append(name); + } else { + if (! baseObject->getType().isStruct()) { + expected("structure"); + return false; + } + functionName = NewPoolTString(""); + functionName->append(baseObject->getType().getTypeName()); + parseContext.addScopeMangler(*functionName); + functionName->append(name); + } + + // function + TFunction* function = new TFunction(functionName, TType(EbtVoid)); + + // arguments + TIntermTyped* arguments = nullptr; + if (baseObject != nullptr) { + // Non-static member functions have an implicit first argument of the base object. + parseContext.handleFunctionArgument(function, arguments, baseObject); + } + if (! acceptArguments(function, arguments)) + return false; + + // call + node = parseContext.handleFunctionCall(loc, function, arguments); + + return node != nullptr; +} + +// arguments +// : LEFT_PAREN expression COMMA expression COMMA ... RIGHT_PAREN +// +// The arguments are pushed onto the 'function' argument list and +// onto the 'arguments' aggregate. +// +bool HlslGrammar::acceptArguments(TFunction* function, TIntermTyped*& arguments) +{ + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + return false; + + // RIGHT_PAREN + if (acceptTokenClass(EHTokRightParen)) + return true; + + // must now be at least one expression... + do { + // expression + TIntermTyped* arg; + if (! acceptAssignmentExpression(arg)) + return false; + + // hook it up + parseContext.handleFunctionArgument(function, arguments, arg); + + // COMMA + if (! acceptTokenClass(EHTokComma)) + break; + } while (true); + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + return true; +} + +bool HlslGrammar::acceptLiteral(TIntermTyped*& node) +{ + switch (token.tokenClass) { + case EHTokIntConstant: + node = intermediate.addConstantUnion(token.i, token.loc, true); + break; + case EHTokUintConstant: + node = intermediate.addConstantUnion(token.u, token.loc, true); + break; + case EHTokFloat16Constant: + node = intermediate.addConstantUnion(token.d, EbtFloat16, token.loc, true); + break; + case EHTokFloatConstant: + node = intermediate.addConstantUnion(token.d, EbtFloat, token.loc, true); + break; + case EHTokDoubleConstant: + node = intermediate.addConstantUnion(token.d, EbtDouble, token.loc, true); + break; + case EHTokBoolConstant: + node = intermediate.addConstantUnion(token.b, token.loc, true); + break; + case EHTokStringConstant: + node = intermediate.addConstantUnion(token.string, token.loc, true); + break; + + default: + return false; + } + + advanceToken(); + + return true; +} + +// simple_statement +// : SEMICOLON +// | declaration_statement +// | expression SEMICOLON +// +bool HlslGrammar::acceptSimpleStatement(TIntermNode*& statement) +{ + // SEMICOLON + if (acceptTokenClass(EHTokSemicolon)) + return true; + + // declaration + if (acceptDeclaration(statement)) + return true; + + // expression + TIntermTyped* node; + if (acceptExpression(node)) + statement = node; + else + return false; + + // SEMICOLON (following an expression) + if (acceptTokenClass(EHTokSemicolon)) + return true; + else { + expected(";"); + return false; + } +} + +// compound_statement +// : LEFT_CURLY statement statement ... RIGHT_CURLY +// +bool HlslGrammar::acceptCompoundStatement(TIntermNode*& retStatement) +{ + TIntermAggregate* compoundStatement = nullptr; + + // LEFT_CURLY + if (! acceptTokenClass(EHTokLeftBrace)) + return false; + + // statement statement ... + TIntermNode* statement = nullptr; + while (acceptStatement(statement)) { + TIntermBranch* branch = statement ? statement->getAsBranchNode() : nullptr; + if (branch != nullptr && (branch->getFlowOp() == EOpCase || + branch->getFlowOp() == EOpDefault)) { + // hook up individual subsequences within a switch statement + parseContext.wrapupSwitchSubsequence(compoundStatement, statement); + compoundStatement = nullptr; + } else { + // hook it up to the growing compound statement + compoundStatement = intermediate.growAggregate(compoundStatement, statement); + } + } + if (compoundStatement) + compoundStatement->setOperator(EOpSequence); + + retStatement = compoundStatement; + + // RIGHT_CURLY + return acceptTokenClass(EHTokRightBrace); +} + +bool HlslGrammar::acceptScopedStatement(TIntermNode*& statement) +{ + parseContext.pushScope(); + bool result = acceptStatement(statement); + parseContext.popScope(); + + return result; +} + +bool HlslGrammar::acceptScopedCompoundStatement(TIntermNode*& statement) +{ + parseContext.pushScope(); + bool result = acceptCompoundStatement(statement); + parseContext.popScope(); + + return result; +} + +// statement +// : attributes attributed_statement +// +// attributed_statement +// : compound_statement +// | simple_statement +// | selection_statement +// | switch_statement +// | case_label +// | default_label +// | iteration_statement +// | jump_statement +// +bool HlslGrammar::acceptStatement(TIntermNode*& statement) +{ + statement = nullptr; + + // attributes + TAttributes attributes; + acceptAttributes(attributes); + + // attributed_statement + switch (peek()) { + case EHTokLeftBrace: + return acceptScopedCompoundStatement(statement); + + case EHTokIf: + return acceptSelectionStatement(statement, attributes); + + case EHTokSwitch: + return acceptSwitchStatement(statement, attributes); + + case EHTokFor: + case EHTokDo: + case EHTokWhile: + return acceptIterationStatement(statement, attributes); + + case EHTokContinue: + case EHTokBreak: + case EHTokDiscard: + case EHTokReturn: + return acceptJumpStatement(statement); + + case EHTokCase: + return acceptCaseLabel(statement); + case EHTokDefault: + return acceptDefaultLabel(statement); + + case EHTokRightBrace: + // Performance: not strictly necessary, but stops a bunch of hunting early, + // and is how sequences of statements end. + return false; + + default: + return acceptSimpleStatement(statement); + } + + return true; +} + +// attributes +// : [zero or more:] bracketed-attribute +// +// bracketed-attribute: +// : LEFT_BRACKET scoped-attribute RIGHT_BRACKET +// : LEFT_BRACKET LEFT_BRACKET scoped-attribute RIGHT_BRACKET RIGHT_BRACKET +// +// scoped-attribute: +// : attribute +// | namespace COLON COLON attribute +// +// attribute: +// : UNROLL +// | UNROLL LEFT_PAREN literal RIGHT_PAREN +// | FASTOPT +// | ALLOW_UAV_CONDITION +// | BRANCH +// | FLATTEN +// | FORCECASE +// | CALL +// | DOMAIN +// | EARLYDEPTHSTENCIL +// | INSTANCE +// | MAXTESSFACTOR +// | OUTPUTCONTROLPOINTS +// | OUTPUTTOPOLOGY +// | PARTITIONING +// | PATCHCONSTANTFUNC +// | NUMTHREADS LEFT_PAREN x_size, y_size,z z_size RIGHT_PAREN +// +void HlslGrammar::acceptAttributes(TAttributes& attributes) +{ + // For now, accept the [ XXX(X) ] syntax, but drop all but + // numthreads, which is used to set the CS local size. + // TODO: subset to correct set? Pass on? + do { + HlslToken attributeToken; + + // LEFT_BRACKET? + if (! acceptTokenClass(EHTokLeftBracket)) + return; + // another LEFT_BRACKET? + bool doubleBrackets = false; + if (acceptTokenClass(EHTokLeftBracket)) + doubleBrackets = true; + + // attribute? (could be namespace; will adjust later) + if (!acceptIdentifier(attributeToken)) { + if (!peekTokenClass(EHTokRightBracket)) { + expected("namespace or attribute identifier"); + advanceToken(); + } + } + + TString nameSpace; + if (acceptTokenClass(EHTokColonColon)) { + // namespace COLON COLON + nameSpace = *attributeToken.string; + // attribute + if (!acceptIdentifier(attributeToken)) { + expected("attribute identifier"); + return; + } + } + + TIntermAggregate* expressions = nullptr; + + // (x, ...) + if (acceptTokenClass(EHTokLeftParen)) { + expressions = new TIntermAggregate; + + TIntermTyped* node; + bool expectingExpression = false; + + while (acceptAssignmentExpression(node)) { + expectingExpression = false; + expressions->getSequence().push_back(node); + if (acceptTokenClass(EHTokComma)) + expectingExpression = true; + } + + // 'expressions' is an aggregate with the expressions in it + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + + // Error for partial or missing expression + if (expectingExpression || expressions->getSequence().empty()) + expected("expression"); + } + + // RIGHT_BRACKET + if (!acceptTokenClass(EHTokRightBracket)) { + expected("]"); + return; + } + // another RIGHT_BRACKET? + if (doubleBrackets && !acceptTokenClass(EHTokRightBracket)) { + expected("]]"); + return; + } + + // Add any values we found into the attribute map. + if (attributeToken.string != nullptr) { + TAttributeType attributeType = parseContext.attributeFromName(nameSpace, *attributeToken.string); + if (attributeType == EatNone) + parseContext.warn(attributeToken.loc, "unrecognized attribute", attributeToken.string->c_str(), ""); + else { + TAttributeArgs attributeArgs = { attributeType, expressions }; + attributes.push_back(attributeArgs); + } + } + } while (true); +} + +// selection_statement +// : IF LEFT_PAREN expression RIGHT_PAREN statement +// : IF LEFT_PAREN expression RIGHT_PAREN statement ELSE statement +// +bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement, const TAttributes& attributes) +{ + TSourceLoc loc = token.loc; + + // IF + if (! acceptTokenClass(EHTokIf)) + return false; + + // so that something declared in the condition is scoped to the lifetimes + // of the then-else statements + parseContext.pushScope(); + + // LEFT_PAREN expression RIGHT_PAREN + TIntermTyped* condition; + if (! acceptParenExpression(condition)) + return false; + condition = parseContext.convertConditionalExpression(loc, condition); + if (condition == nullptr) + return false; + + // create the child statements + TIntermNodePair thenElse = { nullptr, nullptr }; + + ++parseContext.controlFlowNestingLevel; // this only needs to work right if no errors + + // then statement + if (! acceptScopedStatement(thenElse.node1)) { + expected("then statement"); + return false; + } + + // ELSE + if (acceptTokenClass(EHTokElse)) { + // else statement + if (! acceptScopedStatement(thenElse.node2)) { + expected("else statement"); + return false; + } + } + + // Put the pieces together + statement = intermediate.addSelection(condition, thenElse, loc); + parseContext.handleSelectionAttributes(loc, statement->getAsSelectionNode(), attributes); + + parseContext.popScope(); + --parseContext.controlFlowNestingLevel; + + return true; +} + +// switch_statement +// : SWITCH LEFT_PAREN expression RIGHT_PAREN compound_statement +// +bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributes& attributes) +{ + // SWITCH + TSourceLoc loc = token.loc; + + if (! acceptTokenClass(EHTokSwitch)) + return false; + + // LEFT_PAREN expression RIGHT_PAREN + parseContext.pushScope(); + TIntermTyped* switchExpression; + if (! acceptParenExpression(switchExpression)) { + parseContext.popScope(); + return false; + } + + // compound_statement + parseContext.pushSwitchSequence(new TIntermSequence); + + ++parseContext.controlFlowNestingLevel; + bool statementOkay = acceptCompoundStatement(statement); + --parseContext.controlFlowNestingLevel; + + if (statementOkay) + statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr, + attributes); + + parseContext.popSwitchSequence(); + parseContext.popScope(); + + return statementOkay; +} + +// iteration_statement +// : WHILE LEFT_PAREN condition RIGHT_PAREN statement +// | DO LEFT_BRACE statement RIGHT_BRACE WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON +// | FOR LEFT_PAREN for_init_statement for_rest_statement RIGHT_PAREN statement +// +// Non-speculative, only call if it needs to be found; WHILE or DO or FOR already seen. +bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement, const TAttributes& attributes) +{ + TSourceLoc loc = token.loc; + TIntermTyped* condition = nullptr; + + EHlslTokenClass loop = peek(); + assert(loop == EHTokDo || loop == EHTokFor || loop == EHTokWhile); + + // WHILE or DO or FOR + advanceToken(); + + TIntermLoop* loopNode = nullptr; + switch (loop) { + case EHTokWhile: + // so that something declared in the condition is scoped to the lifetime + // of the while sub-statement + parseContext.pushScope(); // this only needs to work right if no errors + parseContext.nestLooping(); + ++parseContext.controlFlowNestingLevel; + + // LEFT_PAREN condition RIGHT_PAREN + if (! acceptParenExpression(condition)) + return false; + condition = parseContext.convertConditionalExpression(loc, condition); + if (condition == nullptr) + return false; + + // statement + if (! acceptScopedStatement(statement)) { + expected("while sub-statement"); + return false; + } + + parseContext.unnestLooping(); + parseContext.popScope(); + --parseContext.controlFlowNestingLevel; + + loopNode = intermediate.addLoop(statement, condition, nullptr, true, loc); + statement = loopNode; + break; + + case EHTokDo: + parseContext.nestLooping(); // this only needs to work right if no errors + ++parseContext.controlFlowNestingLevel; + + // statement + if (! acceptScopedStatement(statement)) { + expected("do sub-statement"); + return false; + } + + // WHILE + if (! acceptTokenClass(EHTokWhile)) { + expected("while"); + return false; + } + + // LEFT_PAREN condition RIGHT_PAREN + if (! acceptParenExpression(condition)) + return false; + condition = parseContext.convertConditionalExpression(loc, condition); + if (condition == nullptr) + return false; + + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + + parseContext.unnestLooping(); + --parseContext.controlFlowNestingLevel; + + loopNode = intermediate.addLoop(statement, condition, 0, false, loc); + statement = loopNode; + break; + + case EHTokFor: + { + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + expected("("); + + // so that something declared in the condition is scoped to the lifetime + // of the for sub-statement + parseContext.pushScope(); + + // initializer + TIntermNode* initNode = nullptr; + if (! acceptSimpleStatement(initNode)) + expected("for-loop initializer statement"); + + parseContext.nestLooping(); // this only needs to work right if no errors + ++parseContext.controlFlowNestingLevel; + + // condition SEMI_COLON + acceptExpression(condition); + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + if (condition != nullptr) { + condition = parseContext.convertConditionalExpression(loc, condition); + if (condition == nullptr) + return false; + } + + // iterator SEMI_COLON + TIntermTyped* iterator = nullptr; + acceptExpression(iterator); + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + + // statement + if (! acceptScopedStatement(statement)) { + expected("for sub-statement"); + return false; + } + + statement = intermediate.addForLoop(statement, initNode, condition, iterator, true, loc, loopNode); + + parseContext.popScope(); + parseContext.unnestLooping(); + --parseContext.controlFlowNestingLevel; + + break; + } + + default: + return false; + } + + parseContext.handleLoopAttributes(loc, loopNode, attributes); + return true; +} + +// jump_statement +// : CONTINUE SEMICOLON +// | BREAK SEMICOLON +// | DISCARD SEMICOLON +// | RETURN SEMICOLON +// | RETURN expression SEMICOLON +// +bool HlslGrammar::acceptJumpStatement(TIntermNode*& statement) +{ + EHlslTokenClass jump = peek(); + switch (jump) { + case EHTokContinue: + case EHTokBreak: + case EHTokDiscard: + case EHTokReturn: + advanceToken(); + break; + default: + // not something we handle in this function + return false; + } + + switch (jump) { + case EHTokContinue: + statement = intermediate.addBranch(EOpContinue, token.loc); + if (parseContext.loopNestingLevel == 0) { + expected("loop"); + return false; + } + break; + case EHTokBreak: + statement = intermediate.addBranch(EOpBreak, token.loc); + if (parseContext.loopNestingLevel == 0 && parseContext.switchSequenceStack.size() == 0) { + expected("loop or switch"); + return false; + } + break; + case EHTokDiscard: + statement = intermediate.addBranch(EOpKill, token.loc); + break; + + case EHTokReturn: + { + // expression + TIntermTyped* node; + if (acceptExpression(node)) { + // hook it up + statement = parseContext.handleReturnValue(token.loc, node); + } else + statement = intermediate.addBranch(EOpReturn, token.loc); + break; + } + + default: + assert(0); + return false; + } + + // SEMICOLON + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + + return true; +} + +// case_label +// : CASE expression COLON +// +bool HlslGrammar::acceptCaseLabel(TIntermNode*& statement) +{ + TSourceLoc loc = token.loc; + if (! acceptTokenClass(EHTokCase)) + return false; + + TIntermTyped* expression; + if (! acceptExpression(expression)) { + expected("case expression"); + return false; + } + + if (! acceptTokenClass(EHTokColon)) { + expected(":"); + return false; + } + + statement = parseContext.intermediate.addBranch(EOpCase, expression, loc); + + return true; +} + +// default_label +// : DEFAULT COLON +// +bool HlslGrammar::acceptDefaultLabel(TIntermNode*& statement) +{ + TSourceLoc loc = token.loc; + if (! acceptTokenClass(EHTokDefault)) + return false; + + if (! acceptTokenClass(EHTokColon)) { + expected(":"); + return false; + } + + statement = parseContext.intermediate.addBranch(EOpDefault, loc); + + return true; +} + +// array_specifier +// : LEFT_BRACKET integer_expression RGHT_BRACKET ... // optional +// : LEFT_BRACKET RGHT_BRACKET // optional +// +void HlslGrammar::acceptArraySpecifier(TArraySizes*& arraySizes) +{ + arraySizes = nullptr; + + // Early-out if there aren't any array dimensions + if (!peekTokenClass(EHTokLeftBracket)) + return; + + // If we get here, we have at least one array dimension. This will track the sizes we find. + arraySizes = new TArraySizes; + + // Collect each array dimension. + while (acceptTokenClass(EHTokLeftBracket)) { + TSourceLoc loc = token.loc; + TIntermTyped* sizeExpr = nullptr; + + // Array sizing expression is optional. If omitted, array will be later sized by initializer list. + const bool hasArraySize = acceptAssignmentExpression(sizeExpr); + + if (! acceptTokenClass(EHTokRightBracket)) { + expected("]"); + return; + } + + if (hasArraySize) { + TArraySize arraySize; + parseContext.arraySizeCheck(loc, sizeExpr, arraySize); + arraySizes->addInnerSize(arraySize); + } else { + arraySizes->addInnerSize(0); // sized by initializers. + } + } +} + +// post_decls +// : COLON semantic // optional +// COLON PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN // optional +// COLON REGISTER LEFT_PAREN [shader_profile,] Type#[subcomp]opt (COMMA SPACEN)opt RIGHT_PAREN // optional +// COLON LAYOUT layout_qualifier_list +// annotations // optional +// +// Return true if any tokens were accepted. That is, +// false can be returned on successfully recognizing nothing, +// not necessarily meaning bad syntax. +// +bool HlslGrammar::acceptPostDecls(TQualifier& qualifier) +{ + bool found = false; + + do { + // COLON + if (acceptTokenClass(EHTokColon)) { + found = true; + HlslToken idToken; + if (peekTokenClass(EHTokLayout)) + acceptLayoutQualifierList(qualifier); + else if (acceptTokenClass(EHTokPackOffset)) { + // PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) { + expected("("); + return false; + } + HlslToken locationToken; + if (! acceptIdentifier(locationToken)) { + expected("c[subcomponent][.component]"); + return false; + } + HlslToken componentToken; + if (acceptTokenClass(EHTokDot)) { + if (! acceptIdentifier(componentToken)) { + expected("component"); + return false; + } + } + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + break; + } + parseContext.handlePackOffset(locationToken.loc, qualifier, *locationToken.string, componentToken.string); + } else if (! acceptIdentifier(idToken)) { + expected("layout, semantic, packoffset, or register"); + return false; + } else if (*idToken.string == "register") { + // REGISTER LEFT_PAREN [shader_profile,] Type#[subcomp]opt (COMMA SPACEN)opt RIGHT_PAREN + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) { + expected("("); + return false; + } + HlslToken registerDesc; // for Type# + HlslToken profile; + if (! acceptIdentifier(registerDesc)) { + expected("register number description"); + return false; + } + if (registerDesc.string->size() > 1 && !isdigit((*registerDesc.string)[1]) && + acceptTokenClass(EHTokComma)) { + // Then we didn't really see the registerDesc yet, it was + // actually the profile. Adjust... + profile = registerDesc; + if (! acceptIdentifier(registerDesc)) { + expected("register number description"); + return false; + } + } + int subComponent = 0; + if (acceptTokenClass(EHTokLeftBracket)) { + // LEFT_BRACKET subcomponent RIGHT_BRACKET + if (! peekTokenClass(EHTokIntConstant)) { + expected("literal integer"); + return false; + } + subComponent = token.i; + advanceToken(); + if (! acceptTokenClass(EHTokRightBracket)) { + expected("]"); + break; + } + } + // (COMMA SPACEN)opt + HlslToken spaceDesc; + if (acceptTokenClass(EHTokComma)) { + if (! acceptIdentifier(spaceDesc)) { + expected ("space identifier"); + return false; + } + } + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + break; + } + parseContext.handleRegister(registerDesc.loc, qualifier, profile.string, *registerDesc.string, subComponent, spaceDesc.string); + } else { + // semantic, in idToken.string + TString semanticUpperCase = *idToken.string; + std::transform(semanticUpperCase.begin(), semanticUpperCase.end(), semanticUpperCase.begin(), ::toupper); + parseContext.handleSemantic(idToken.loc, qualifier, mapSemantic(semanticUpperCase.c_str()), semanticUpperCase); + } + } else if (peekTokenClass(EHTokLeftAngle)) { + found = true; + acceptAnnotations(qualifier); + } else + break; + + } while (true); + + return found; +} + +// +// Get the stream of tokens from the scanner, but skip all syntactic/semantic +// processing. +// +bool HlslGrammar::captureBlockTokens(TVector& tokens) +{ + if (! peekTokenClass(EHTokLeftBrace)) + return false; + + int braceCount = 0; + + do { + switch (peek()) { + case EHTokLeftBrace: + ++braceCount; + break; + case EHTokRightBrace: + --braceCount; + break; + case EHTokNone: + // End of input before balance { } is bad... + return false; + default: + break; + } + + tokens.push_back(token); + advanceToken(); + } while (braceCount > 0); + + return true; +} + +// Return a string for just the types that can also be declared as an identifier. +const char* HlslGrammar::getTypeString(EHlslTokenClass tokenClass) const +{ + switch (tokenClass) { + case EHTokSample: return "sample"; + case EHTokHalf: return "half"; + case EHTokHalf1x1: return "half1x1"; + case EHTokHalf1x2: return "half1x2"; + case EHTokHalf1x3: return "half1x3"; + case EHTokHalf1x4: return "half1x4"; + case EHTokHalf2x1: return "half2x1"; + case EHTokHalf2x2: return "half2x2"; + case EHTokHalf2x3: return "half2x3"; + case EHTokHalf2x4: return "half2x4"; + case EHTokHalf3x1: return "half3x1"; + case EHTokHalf3x2: return "half3x2"; + case EHTokHalf3x3: return "half3x3"; + case EHTokHalf3x4: return "half3x4"; + case EHTokHalf4x1: return "half4x1"; + case EHTokHalf4x2: return "half4x2"; + case EHTokHalf4x3: return "half4x3"; + case EHTokHalf4x4: return "half4x4"; + case EHTokBool: return "bool"; + case EHTokFloat: return "float"; + case EHTokDouble: return "double"; + case EHTokInt: return "int"; + case EHTokUint: return "uint"; + case EHTokMin16float: return "min16float"; + case EHTokMin10float: return "min10float"; + case EHTokMin16int: return "min16int"; + case EHTokMin12int: return "min12int"; + case EHTokConstantBuffer: return "ConstantBuffer"; + case EHTokLayout: return "layout"; + default: + return nullptr; + } +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/HLSL/hlslGrammar.h b/third_party/glslang/glslang/HLSL/hlslGrammar.h new file mode 100644 index 0000000..27706b2 --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslGrammar.h @@ -0,0 +1,142 @@ +// +// Copyright (C) 2016-2018 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef HLSLGRAMMAR_H_ +#define HLSLGRAMMAR_H_ + +#include "hlslParseHelper.h" +#include "hlslOpMap.h" +#include "hlslTokenStream.h" + +namespace glslang { + + class TFunctionDeclarator; + + // Should just be the grammar aspect of HLSL. + // Described in more detail in hlslGrammar.cpp. + + class HlslGrammar : public HlslTokenStream { + public: + HlslGrammar(HlslScanContext& scanner, HlslParseContext& parseContext) + : HlslTokenStream(scanner), parseContext(parseContext), intermediate(parseContext.intermediate), + typeIdentifiers(false), unitNode(nullptr) { } + virtual ~HlslGrammar() { } + + bool parse(); + + protected: + HlslGrammar(); + HlslGrammar& operator=(const HlslGrammar&); + + void expected(const char*); + void unimplemented(const char*); + bool acceptIdentifier(HlslToken&); + bool acceptCompilationUnit(); + bool acceptDeclarationList(TIntermNode*&); + bool acceptDeclaration(TIntermNode*&); + bool acceptControlDeclaration(TIntermNode*& node); + bool acceptSamplerDeclarationDX9(TType&); + bool acceptSamplerState(); + bool acceptFullySpecifiedType(TType&, const TAttributes&); + bool acceptFullySpecifiedType(TType&, TIntermNode*& nodeList, const TAttributes&, bool forbidDeclarators = false); + bool acceptQualifier(TQualifier&); + bool acceptLayoutQualifierList(TQualifier&); + bool acceptType(TType&); + bool acceptType(TType&, TIntermNode*& nodeList); + bool acceptTemplateVecMatBasicType(TBasicType&); + bool acceptVectorTemplateType(TType&); + bool acceptMatrixTemplateType(TType&); + bool acceptTessellationDeclType(TBuiltInVariable&); + bool acceptTessellationPatchTemplateType(TType&); + bool acceptStreamOutTemplateType(TType&, TLayoutGeometry&); + bool acceptOutputPrimitiveGeometry(TLayoutGeometry&); + bool acceptAnnotations(TQualifier&); + bool acceptSamplerTypeDX9(TType &); + bool acceptSamplerType(TType&); + bool acceptTextureType(TType&); + bool acceptSubpassInputType(TType&); + bool acceptStructBufferType(TType&); + bool acceptTextureBufferType(TType&); + bool acceptConstantBufferType(TType&); + bool acceptStruct(TType&, TIntermNode*& nodeList); + bool acceptStructDeclarationList(TTypeList*&, TIntermNode*& nodeList, TVector&); + bool acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TType&, TString& memberName, + TFunctionDeclarator&); + bool acceptFunctionParameters(TFunction&); + bool acceptParameterDeclaration(TFunction&); + bool acceptFunctionDefinition(TFunctionDeclarator&, TIntermNode*& nodeList, TVector* deferredTokens); + bool acceptFunctionBody(TFunctionDeclarator& declarator, TIntermNode*& nodeList); + bool acceptParenExpression(TIntermTyped*&); + bool acceptExpression(TIntermTyped*&); + bool acceptInitializer(TIntermTyped*&); + bool acceptAssignmentExpression(TIntermTyped*&); + bool acceptConditionalExpression(TIntermTyped*&); + bool acceptBinaryExpression(TIntermTyped*&, PrecedenceLevel); + bool acceptUnaryExpression(TIntermTyped*&); + bool acceptPostfixExpression(TIntermTyped*&); + bool acceptConstructor(TIntermTyped*&); + bool acceptFunctionCall(const TSourceLoc&, TString& name, TIntermTyped*&, TIntermTyped* objectBase); + bool acceptArguments(TFunction*, TIntermTyped*&); + bool acceptLiteral(TIntermTyped*&); + bool acceptSimpleStatement(TIntermNode*&); + bool acceptCompoundStatement(TIntermNode*&); + bool acceptScopedStatement(TIntermNode*&); + bool acceptScopedCompoundStatement(TIntermNode*&); + bool acceptStatement(TIntermNode*&); + bool acceptNestedStatement(TIntermNode*&); + void acceptAttributes(TAttributes&); + bool acceptSelectionStatement(TIntermNode*&, const TAttributes&); + bool acceptSwitchStatement(TIntermNode*&, const TAttributes&); + bool acceptIterationStatement(TIntermNode*&, const TAttributes&); + bool acceptJumpStatement(TIntermNode*&); + bool acceptCaseLabel(TIntermNode*&); + bool acceptDefaultLabel(TIntermNode*&); + void acceptArraySpecifier(TArraySizes*&); + bool acceptPostDecls(TQualifier&); + bool acceptDefaultParameterDeclaration(const TType&, TIntermTyped*&); + + bool captureBlockTokens(TVector& tokens); + const char* getTypeString(EHlslTokenClass tokenClass) const; + + HlslParseContext& parseContext; // state of parsing and helper functions for building the intermediate + TIntermediate& intermediate; // the final product, the intermediate representation, includes the AST + bool typeIdentifiers; // shader uses some types as identifiers + TIntermNode* unitNode; + }; + +} // end namespace glslang + +#endif // HLSLGRAMMAR_H_ diff --git a/third_party/glslang/glslang/HLSL/hlslOpMap.cpp b/third_party/glslang/glslang/HLSL/hlslOpMap.cpp new file mode 100644 index 0000000..ebe6fbd --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslOpMap.cpp @@ -0,0 +1,173 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// Map from physical token form (e.g. '-') to logical operator +// form (e.g., binary subtract or unary negate). + +#include "hlslOpMap.h" + +namespace glslang { + +// Map parsing tokens that could be assignments into assignment operators. +TOperator HlslOpMap::assignment(EHlslTokenClass op) +{ + switch (op) { + case EHTokAssign: return EOpAssign; + case EHTokMulAssign: return EOpMulAssign; + case EHTokDivAssign: return EOpDivAssign; + case EHTokAddAssign: return EOpAddAssign; + case EHTokModAssign: return EOpModAssign; + case EHTokLeftAssign: return EOpLeftShiftAssign; + case EHTokRightAssign: return EOpRightShiftAssign; + case EHTokAndAssign: return EOpAndAssign; + case EHTokXorAssign: return EOpExclusiveOrAssign; + case EHTokOrAssign: return EOpInclusiveOrAssign; + case EHTokSubAssign: return EOpSubAssign; + + default: + return EOpNull; + } +} + +// Map parsing tokens that could be binary operations into binary operators. +TOperator HlslOpMap::binary(EHlslTokenClass op) +{ + switch (op) { + case EHTokPlus: return EOpAdd; + case EHTokDash: return EOpSub; + case EHTokStar: return EOpMul; + case EHTokSlash: return EOpDiv; + case EHTokPercent: return EOpMod; + case EHTokRightOp: return EOpRightShift; + case EHTokLeftOp: return EOpLeftShift; + case EHTokAmpersand: return EOpAnd; + case EHTokVerticalBar: return EOpInclusiveOr; + case EHTokCaret: return EOpExclusiveOr; + case EHTokEqOp: return EOpEqual; + case EHTokNeOp: return EOpNotEqual; + case EHTokLeftAngle: return EOpLessThan; + case EHTokRightAngle: return EOpGreaterThan; + case EHTokLeOp: return EOpLessThanEqual; + case EHTokGeOp: return EOpGreaterThanEqual; + case EHTokOrOp: return EOpLogicalOr; + case EHTokXorOp: return EOpLogicalXor; + case EHTokAndOp: return EOpLogicalAnd; + + default: + return EOpNull; + } +} + +// Map parsing tokens that could be unary operations into unary operators. +// These are just the ones that can appear in front of its operand. +TOperator HlslOpMap::preUnary(EHlslTokenClass op) +{ + switch (op) { + case EHTokPlus: return EOpAdd; // means no-op, but still a unary op was present + case EHTokDash: return EOpNegative; + case EHTokBang: return EOpLogicalNot; + case EHTokTilde: return EOpBitwiseNot; + + case EHTokIncOp: return EOpPreIncrement; + case EHTokDecOp: return EOpPreDecrement; + + default: return EOpNull; // means not a pre-unary op + } +} + +// Map parsing tokens that could be unary operations into unary operators. +// These are just the ones that can appear behind its operand. +TOperator HlslOpMap::postUnary(EHlslTokenClass op) +{ + switch (op) { + case EHTokDot: return EOpIndexDirectStruct; + case EHTokLeftBracket: return EOpIndexIndirect; + + case EHTokIncOp: return EOpPostIncrement; + case EHTokDecOp: return EOpPostDecrement; + + case EHTokColonColon: return EOpScoping; + + default: return EOpNull; // means not a post-unary op + } +} + +// Map operators into their level of precedence. +PrecedenceLevel HlslOpMap::precedenceLevel(TOperator op) +{ + switch (op) { + case EOpLogicalOr: + return PlLogicalOr; + case EOpLogicalXor: + return PlLogicalXor; + case EOpLogicalAnd: + return PlLogicalAnd; + + case EOpInclusiveOr: + return PlBitwiseOr; + case EOpExclusiveOr: + return PlBitwiseXor; + case EOpAnd: + return PlBitwiseAnd; + + case EOpEqual: + case EOpNotEqual: + return PlEquality; + + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + return PlRelational; + + case EOpRightShift: + case EOpLeftShift: + return PlShift; + + case EOpAdd: + case EOpSub: + return PlAdd; + + case EOpMul: + case EOpDiv: + case EOpMod: + return PlMul; + + default: + return PlBad; + } +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/HLSL/hlslOpMap.h b/third_party/glslang/glslang/HLSL/hlslOpMap.h new file mode 100644 index 0000000..4e783f3 --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslOpMap.h @@ -0,0 +1,69 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef HLSLOPMAP_H_ +#define HLSLOPMAP_H_ + +#include "hlslScanContext.h" + +namespace glslang { + + enum PrecedenceLevel { + PlBad, + PlLogicalOr, + PlLogicalXor, + PlLogicalAnd, + PlBitwiseOr, + PlBitwiseXor, + PlBitwiseAnd, + PlEquality, + PlRelational, + PlShift, + PlAdd, + PlMul + }; + + class HlslOpMap { + public: + static TOperator assignment(EHlslTokenClass op); + static TOperator binary(EHlslTokenClass op); + static TOperator preUnary(EHlslTokenClass op); + static TOperator postUnary(EHlslTokenClass op); + static PrecedenceLevel precedenceLevel(TOperator); + }; + +} // end namespace glslang + +#endif // HLSLOPMAP_H_ diff --git a/third_party/glslang/glslang/HLSL/hlslParseHelper.cpp b/third_party/glslang/glslang/HLSL/hlslParseHelper.cpp new file mode 100755 index 0000000..ea31837 --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslParseHelper.cpp @@ -0,0 +1,10165 @@ +// +// Copyright (C) 2017-2018 Google, Inc. +// Copyright (C) 2017 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "hlslParseHelper.h" +#include "hlslScanContext.h" +#include "hlslGrammar.h" +#include "hlslAttributes.h" + +#include "../Include/Common.h" +#include "../MachineIndependent/Scan.h" +#include "../MachineIndependent/preprocessor/PpContext.h" + +#include "../OSDependent/osinclude.h" + +#include +#include +#include +#include +#include + +namespace glslang { + +HlslParseContext::HlslParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, + int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + TInfoSink& infoSink, + const TString sourceEntryPointName, + bool forwardCompatible, EShMessages messages) : + TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, infoSink, + forwardCompatible, messages, &sourceEntryPointName), + annotationNestingLevel(0), + inputPatch(nullptr), + nextInLocation(0), nextOutLocation(0), + entryPointFunction(nullptr), + entryPointFunctionBody(nullptr), + gsStreamOutput(nullptr), + clipDistanceOutput(nullptr), + cullDistanceOutput(nullptr), + clipDistanceInput(nullptr), + cullDistanceInput(nullptr), + parsingEntrypointParameters(false) +{ + globalUniformDefaults.clear(); + globalUniformDefaults.layoutMatrix = ElmRowMajor; + globalUniformDefaults.layoutPacking = ElpStd140; + + globalBufferDefaults.clear(); + globalBufferDefaults.layoutMatrix = ElmRowMajor; + globalBufferDefaults.layoutPacking = ElpStd430; + + globalInputDefaults.clear(); + globalOutputDefaults.clear(); + + clipSemanticNSizeIn.fill(0); + cullSemanticNSizeIn.fill(0); + clipSemanticNSizeOut.fill(0); + cullSemanticNSizeOut.fill(0); + + // "Shaders in the transform + // feedback capturing mode have an initial global default of + // layout(xfb_buffer = 0) out;" + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry) + globalOutputDefaults.layoutXfbBuffer = 0; + + if (language == EShLangGeometry) + globalOutputDefaults.layoutStream = 0; +} + +HlslParseContext::~HlslParseContext() +{ +} + +void HlslParseContext::initializeExtensionBehavior() +{ + TParseContextBase::initializeExtensionBehavior(); + + // HLSL allows #line by default. + extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhEnable; +} + +void HlslParseContext::setLimits(const TBuiltInResource& r) +{ + resources = r; + intermediate.setLimits(resources); +} + +// +// Parse an array of strings using the parser in HlslRules. +// +// Returns true for successful acceptance of the shader, false if any errors. +// +bool HlslParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) +{ + currentScanner = &input; + ppContext.setInput(input, versionWillBeError); + + HlslScanContext scanContext(*this, ppContext); + HlslGrammar grammar(scanContext, *this); + if (!grammar.parse()) { + // Print a message formated such that if you click on the message it will take you right to + // the line through most UIs. + const glslang::TSourceLoc& sourceLoc = input.getSourceLoc(); + infoSink.info << sourceLoc.getFilenameStr() << "(" << sourceLoc.line << "): error at column " << sourceLoc.column + << ", HLSL parsing failed.\n"; + ++numErrors; + return false; + } + + finish(); + + return numErrors == 0; +} + +// +// Return true if this l-value node should be converted in some manner. +// For instance: turning a load aggregate into a store in an l-value. +// +bool HlslParseContext::shouldConvertLValue(const TIntermNode* node) const +{ + if (node == nullptr || node->getAsTyped() == nullptr) + return false; + + const TIntermAggregate* lhsAsAggregate = node->getAsAggregate(); + const TIntermBinary* lhsAsBinary = node->getAsBinaryNode(); + + // If it's a swizzled/indexed aggregate, look at the left node instead. + if (lhsAsBinary != nullptr && + (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) + lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate(); + if (lhsAsAggregate != nullptr && lhsAsAggregate->getOp() == EOpImageLoad) + return true; + + return false; +} + +void HlslParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, + TTypeList* newTypeList) +{ + newTypeList = nullptr; + correctUniform(memberType.getQualifier()); + if (memberType.isStruct()) { + auto it = ioTypeMap.find(memberType.getStruct()); + if (it != ioTypeMap.end() && it->second.uniform) + newTypeList = it->second.uniform; + } + TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, newTypeList); +} + +// +// Return a TLayoutFormat corresponding to the given texture type. +// +TLayoutFormat HlslParseContext::getLayoutFromTxType(const TSourceLoc& loc, const TType& txType) +{ + if (txType.isStruct()) { + // TODO: implement. + error(loc, "unimplemented: structure type in image or buffer", "", ""); + return ElfNone; + } + + const int components = txType.getVectorSize(); + const TBasicType txBasicType = txType.getBasicType(); + + const auto selectFormat = [this,&components](TLayoutFormat v1, TLayoutFormat v2, TLayoutFormat v4) -> TLayoutFormat { + if (intermediate.getNoStorageFormat()) + return ElfNone; + + return components == 1 ? v1 : + components == 2 ? v2 : v4; + }; + + switch (txBasicType) { + case EbtFloat: return selectFormat(ElfR32f, ElfRg32f, ElfRgba32f); + case EbtInt: return selectFormat(ElfR32i, ElfRg32i, ElfRgba32i); + case EbtUint: return selectFormat(ElfR32ui, ElfRg32ui, ElfRgba32ui); + default: + error(loc, "unknown basic type in image format", "", ""); + return ElfNone; + } +} + +// +// Both test and if necessary, spit out an error, to see if the node is really +// an l-value that can be operated on this way. +// +// Returns true if there was an error. +// +bool HlslParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + if (shouldConvertLValue(node)) { + // if we're writing to a texture, it must be an RW form. + + TIntermAggregate* lhsAsAggregate = node->getAsAggregate(); + TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped(); + + if (!object->getType().getSampler().isImage()) { + error(loc, "operator[] on a non-RW texture must be an r-value", "", ""); + return true; + } + } + + // We tolerate samplers as l-values, even though they are nominally + // illegal, because we expect a later optimization to eliminate them. + if (node->getType().getBasicType() == EbtSampler) { + intermediate.setNeedsLegalization(); + return false; + } + + // Let the base class check errors + return TParseContextBase::lValueErrorCheck(loc, op, node); +} + +// +// This function handles l-value conversions and verifications. It uses, but is not synonymous +// with lValueErrorCheck. That function accepts an l-value directly, while this one must be +// given the surrounding tree - e.g, with an assignment, so we can convert the assign into a +// series of other image operations. +// +// Most things are passed through unmodified, except for error checking. +// +TIntermTyped* HlslParseContext::handleLvalue(const TSourceLoc& loc, const char* op, TIntermTyped*& node) +{ + if (node == nullptr) + return nullptr; + + TIntermBinary* nodeAsBinary = node->getAsBinaryNode(); + TIntermUnary* nodeAsUnary = node->getAsUnaryNode(); + TIntermAggregate* sequence = nullptr; + + TIntermTyped* lhs = nodeAsUnary ? nodeAsUnary->getOperand() : + nodeAsBinary ? nodeAsBinary->getLeft() : + nullptr; + + // Early bail out if there is no conversion to apply + if (!shouldConvertLValue(lhs)) { + if (lhs != nullptr) + if (lValueErrorCheck(loc, op, lhs)) + return nullptr; + return node; + } + + // *** If we get here, we're going to apply some conversion to an l-value. + + // Helper to create a load. + const auto makeLoad = [&](TIntermSymbol* rhsTmp, TIntermTyped* object, TIntermTyped* coord, const TType& derefType) { + TIntermAggregate* loadOp = new TIntermAggregate(EOpImageLoad); + loadOp->setLoc(loc); + loadOp->getSequence().push_back(object); + loadOp->getSequence().push_back(intermediate.addSymbol(*coord->getAsSymbolNode())); + loadOp->setType(derefType); + + sequence = intermediate.growAggregate(sequence, + intermediate.addAssign(EOpAssign, rhsTmp, loadOp, loc), + loc); + }; + + // Helper to create a store. + const auto makeStore = [&](TIntermTyped* object, TIntermTyped* coord, TIntermSymbol* rhsTmp) { + TIntermAggregate* storeOp = new TIntermAggregate(EOpImageStore); + storeOp->getSequence().push_back(object); + storeOp->getSequence().push_back(coord); + storeOp->getSequence().push_back(intermediate.addSymbol(*rhsTmp)); + storeOp->setLoc(loc); + storeOp->setType(TType(EbtVoid)); + + sequence = intermediate.growAggregate(sequence, storeOp); + }; + + // Helper to create an assign. + const auto makeBinary = [&](TOperator op, TIntermTyped* lhs, TIntermTyped* rhs) { + sequence = intermediate.growAggregate(sequence, + intermediate.addBinaryNode(op, lhs, rhs, loc, lhs->getType()), + loc); + }; + + // Helper to complete sequence by adding trailing variable, so we evaluate to the right value. + const auto finishSequence = [&](TIntermSymbol* rhsTmp, const TType& derefType) -> TIntermAggregate* { + // Add a trailing use of the temp, so the sequence returns the proper value. + sequence = intermediate.growAggregate(sequence, intermediate.addSymbol(*rhsTmp)); + sequence->setOperator(EOpSequence); + sequence->setLoc(loc); + sequence->setType(derefType); + + return sequence; + }; + + // Helper to add unary op + const auto makeUnary = [&](TOperator op, TIntermSymbol* rhsTmp) { + sequence = intermediate.growAggregate(sequence, + intermediate.addUnaryNode(op, intermediate.addSymbol(*rhsTmp), loc, + rhsTmp->getType()), + loc); + }; + + // Return true if swizzle or index writes all components of the given variable. + const auto writesAllComponents = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> bool { + if (swizzle == nullptr) // not a swizzle or index + return true; + + // Track which components are being set. + std::array compIsSet; + compIsSet.fill(false); + + const TIntermConstantUnion* asConst = swizzle->getRight()->getAsConstantUnion(); + const TIntermAggregate* asAggregate = swizzle->getRight()->getAsAggregate(); + + // This could be either a direct index, or a swizzle. + if (asConst) { + compIsSet[asConst->getConstArray()[0].getIConst()] = true; + } else if (asAggregate) { + const TIntermSequence& seq = asAggregate->getSequence(); + for (int comp=0; compgetAsConstantUnion()->getConstArray()[0].getIConst()] = true; + } else { + assert(0); + } + + // Return true if all components are being set by the index or swizzle + return std::all_of(compIsSet.begin(), compIsSet.begin() + var->getType().getVectorSize(), + [](bool isSet) { return isSet; } ); + }; + + // Create swizzle matching input swizzle + const auto addSwizzle = [&](TIntermSymbol* var, TIntermBinary* swizzle) -> TIntermTyped* { + if (swizzle) + return intermediate.addBinaryNode(swizzle->getOp(), var, swizzle->getRight(), loc, swizzle->getType()); + else + return var; + }; + + TIntermBinary* lhsAsBinary = lhs->getAsBinaryNode(); + TIntermAggregate* lhsAsAggregate = lhs->getAsAggregate(); + bool lhsIsSwizzle = false; + + // If it's a swizzled L-value, remember the swizzle, and use the LHS. + if (lhsAsBinary != nullptr && (lhsAsBinary->getOp() == EOpVectorSwizzle || lhsAsBinary->getOp() == EOpIndexDirect)) { + lhsAsAggregate = lhsAsBinary->getLeft()->getAsAggregate(); + lhsIsSwizzle = true; + } + + TIntermTyped* object = lhsAsAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* coord = lhsAsAggregate->getSequence()[1]->getAsTyped(); + + const TSampler& texSampler = object->getType().getSampler(); + + TType objDerefType; + getTextureReturnType(texSampler, objDerefType); + + if (nodeAsBinary) { + TIntermTyped* rhs = nodeAsBinary->getRight(); + const TOperator assignOp = nodeAsBinary->getOp(); + + bool isModifyOp = false; + + switch (assignOp) { + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpVectorTimesMatrixAssign: + case EOpVectorTimesScalarAssign: + case EOpMatrixTimesScalarAssign: + case EOpMatrixTimesMatrixAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + isModifyOp = true; + // fall through... + case EOpAssign: + { + // Since this is an lvalue, we'll convert an image load to a sequence like this + // (to still provide the value): + // OpSequence + // OpImageStore(object, lhs, rhs) + // rhs + // But if it's not a simple symbol RHS (say, a fn call), we don't want to duplicate the RHS, + // so we'll convert instead to this: + // OpSequence + // rhsTmp = rhs + // OpImageStore(object, coord, rhsTmp) + // rhsTmp + // If this is a read-modify-write op, like +=, we issue: + // OpSequence + // coordtmp = load's param1 + // rhsTmp = OpImageLoad(object, coordTmp) + // rhsTmp op= rhs + // OpImageStore(object, coordTmp, rhsTmp) + // rhsTmp + // + // If the lvalue is swizzled, we apply that when writing the temp variable, like so: + // ... + // rhsTmp.some_swizzle = ... + // For partial writes, an error is generated. + + TIntermSymbol* rhsTmp = rhs->getAsSymbolNode(); + TIntermTyped* coordTmp = coord; + + if (rhsTmp == nullptr || isModifyOp || lhsIsSwizzle) { + rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType); + + // Partial updates not yet supported + if (!writesAllComponents(rhsTmp, lhsAsBinary)) { + error(loc, "unimplemented: partial image updates", "", ""); + } + + // Assign storeTemp = rhs + if (isModifyOp) { + // We have to make a temp var for the coordinate, to avoid evaluating it twice. + coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); + makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] + makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp) + } + + // rhsTmp op= rhs. + makeBinary(assignOp, addSwizzle(intermediate.addSymbol(*rhsTmp), lhsAsBinary), rhs); + } + + makeStore(object, coordTmp, rhsTmp); // add a store + return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence + } + + default: + break; + } + } + + if (nodeAsUnary) { + const TOperator assignOp = nodeAsUnary->getOp(); + + switch (assignOp) { + case EOpPreIncrement: + case EOpPreDecrement: + { + // We turn this into: + // OpSequence + // coordtmp = load's param1 + // rhsTmp = OpImageLoad(object, coordTmp) + // rhsTmp op + // OpImageStore(object, coordTmp, rhsTmp) + // rhsTmp + + TIntermSymbol* rhsTmp = makeInternalVariableNode(loc, "storeTemp", objDerefType); + TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); + + makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] + makeLoad(rhsTmp, object, coordTmp, objDerefType); // rhsTmp = OpImageLoad(object, coordTmp) + makeUnary(assignOp, rhsTmp); // op rhsTmp + makeStore(object, coordTmp, rhsTmp); // OpImageStore(object, coordTmp, rhsTmp) + return finishSequence(rhsTmp, objDerefType); // return rhsTmp from sequence + } + + case EOpPostIncrement: + case EOpPostDecrement: + { + // We turn this into: + // OpSequence + // coordtmp = load's param1 + // rhsTmp1 = OpImageLoad(object, coordTmp) + // rhsTmp2 = rhsTmp1 + // rhsTmp2 op + // OpImageStore(object, coordTmp, rhsTmp2) + // rhsTmp1 (pre-op value) + TIntermSymbol* rhsTmp1 = makeInternalVariableNode(loc, "storeTempPre", objDerefType); + TIntermSymbol* rhsTmp2 = makeInternalVariableNode(loc, "storeTempPost", objDerefType); + TIntermTyped* coordTmp = makeInternalVariableNode(loc, "coordTemp", coord->getType()); + + makeBinary(EOpAssign, coordTmp, coord); // coordtmp = load[param1] + makeLoad(rhsTmp1, object, coordTmp, objDerefType); // rhsTmp1 = OpImageLoad(object, coordTmp) + makeBinary(EOpAssign, rhsTmp2, rhsTmp1); // rhsTmp2 = rhsTmp1 + makeUnary(assignOp, rhsTmp2); // rhsTmp op + makeStore(object, coordTmp, rhsTmp2); // OpImageStore(object, coordTmp, rhsTmp2) + return finishSequence(rhsTmp1, objDerefType); // return rhsTmp from sequence + } + + default: + break; + } + } + + if (lhs) + if (lValueErrorCheck(loc, op, lhs)) + return nullptr; + + return node; +} + +void HlslParseContext::handlePragma(const TSourceLoc& loc, const TVector& tokens) +{ + if (pragmaCallback) + pragmaCallback(loc.line, tokens); + + if (tokens.size() == 0) + return; + + // These pragmas are case insensitive in HLSL, so we'll compare in lower case. + TVector lowerTokens = tokens; + + for (auto it = lowerTokens.begin(); it != lowerTokens.end(); ++it) + std::transform(it->begin(), it->end(), it->begin(), ::tolower); + + // Handle pack_matrix + if (tokens.size() == 4 && lowerTokens[0] == "pack_matrix" && tokens[1] == "(" && tokens[3] == ")") { + // Note that HLSL semantic order is Mrc, not Mcr like SPIR-V, so we reverse the sense. + // Row major becomes column major and vice versa. + + if (lowerTokens[2] == "row_major") { + globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmColumnMajor; + } else if (lowerTokens[2] == "column_major") { + globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor; + } else { + // unknown majorness strings are treated as (HLSL column major)==(SPIR-V row major) + warn(loc, "unknown pack_matrix pragma value", tokens[2].c_str(), ""); + globalUniformDefaults.layoutMatrix = globalBufferDefaults.layoutMatrix = ElmRowMajor; + } + return; + } + + // Handle once + if (lowerTokens[0] == "once") { + warn(loc, "not implemented", "#pragma once", ""); + return; + } +} + +// +// Look at a '.' matrix selector string and change it into components +// for a matrix. There are two types: +// +// _21 second row, first column (one based) +// _m21 third row, second column (zero based) +// +// Returns true if there is no error. +// +bool HlslParseContext::parseMatrixSwizzleSelector(const TSourceLoc& loc, const TString& fields, int cols, int rows, + TSwizzleSelectors& components) +{ + int startPos[MaxSwizzleSelectors]; + int numComps = 0; + TString compString = fields; + + // Find where each component starts, + // recording the first character position after the '_'. + for (size_t c = 0; c < compString.size(); ++c) { + if (compString[c] == '_') { + if (numComps >= MaxSwizzleSelectors) { + error(loc, "matrix component swizzle has too many components", compString.c_str(), ""); + return false; + } + if (c > compString.size() - 3 || + ((compString[c+1] == 'm' || compString[c+1] == 'M') && c > compString.size() - 4)) { + error(loc, "matrix component swizzle missing", compString.c_str(), ""); + return false; + } + startPos[numComps++] = (int)c + 1; + } + } + + // Process each component + for (int i = 0; i < numComps; ++i) { + int pos = startPos[i]; + int bias = -1; + if (compString[pos] == 'm' || compString[pos] == 'M') { + bias = 0; + ++pos; + } + TMatrixSelector comp; + comp.coord1 = compString[pos+0] - '0' + bias; + comp.coord2 = compString[pos+1] - '0' + bias; + if (comp.coord1 < 0 || comp.coord1 >= cols) { + error(loc, "matrix row component out of range", compString.c_str(), ""); + return false; + } + if (comp.coord2 < 0 || comp.coord2 >= rows) { + error(loc, "matrix column component out of range", compString.c_str(), ""); + return false; + } + components.push_back(comp); + } + + return true; +} + +// If the 'comps' express a column of a matrix, +// return the column. Column means the first coords all match. +// +// Otherwise, return -1. +// +int HlslParseContext::getMatrixComponentsColumn(int rows, const TSwizzleSelectors& selector) +{ + int col = -1; + + // right number of comps? + if (selector.size() != rows) + return -1; + + // all comps in the same column? + // rows in order? + col = selector[0].coord1; + for (int i = 0; i < rows; ++i) { + if (col != selector[i].coord1) + return -1; + if (i != selector[i].coord2) + return -1; + } + + return col; +} + +// +// Handle seeing a variable identifier in the grammar. +// +TIntermTyped* HlslParseContext::handleVariable(const TSourceLoc& loc, const TString* string) +{ + int thisDepth; + TSymbol* symbol = symbolTable.find(*string, thisDepth); + if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) { + error(loc, "expected symbol, not user-defined type", string->c_str(), ""); + return nullptr; + } + + const TVariable* variable = nullptr; + const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr; + TIntermTyped* node = nullptr; + if (anon) { + // It was a member of an anonymous container, which could be a 'this' structure. + + // Create a subtree for its dereference. + if (thisDepth > 0) { + variable = getImplicitThis(thisDepth); + if (variable == nullptr) + error(loc, "cannot access member variables (static member function?)", "this", ""); + } + if (variable == nullptr) + variable = anon->getAnonContainer().getAsVariable(); + + TIntermTyped* container = intermediate.addSymbol(*variable, loc); + TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc); + node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc); + + node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); + if (node->getType().hiddenMember()) + error(loc, "member of nameless block was not redeclared", string->c_str(), ""); + } else { + // Not a member of an anonymous container. + + // The symbol table search was done in the lexical phase. + // See if it was a variable. + variable = symbol ? symbol->getAsVariable() : nullptr; + if (variable) { + if ((variable->getType().getBasicType() == EbtBlock || + variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) { + error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), ""); + variable = nullptr; + } + } else { + if (symbol) + error(loc, "variable name expected", string->c_str(), ""); + } + + // Recovery, if it wasn't found or was not a variable. + if (variable == nullptr) { + error(loc, "unknown variable", string->c_str(), ""); + variable = new TVariable(string, TType(EbtVoid)); + } + + if (variable->getType().getQualifier().isFrontEndConstant()) + node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); + else + node = intermediate.addSymbol(*variable, loc); + } + + if (variable->getType().getQualifier().isIo()) + intermediate.addIoAccessed(*string); + + return node; +} + +// +// Handle operator[] on any objects it applies to. Currently: +// Textures +// Buffers +// +TIntermTyped* HlslParseContext::handleBracketOperator(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) +{ + // handle r-value operator[] on textures and images. l-values will be processed later. + if (base->getType().getBasicType() == EbtSampler && !base->isArray()) { + const TSampler& sampler = base->getType().getSampler(); + if (sampler.isImage() || sampler.isTexture()) { + if (! mipsOperatorMipArg.empty() && mipsOperatorMipArg.back().mipLevel == nullptr) { + // The first operator[] to a .mips[] sequence is the mip level. We'll remember it. + mipsOperatorMipArg.back().mipLevel = index; + return base; // next [] index is to the same base. + } else { + TIntermAggregate* load = new TIntermAggregate(sampler.isImage() ? EOpImageLoad : EOpTextureFetch); + + TType sampReturnType; + getTextureReturnType(sampler, sampReturnType); + + load->setType(sampReturnType); + load->setLoc(loc); + load->getSequence().push_back(base); + load->getSequence().push_back(index); + + // Textures need a MIP. If we saw one go by, use it. Otherwise, use zero. + if (sampler.isTexture()) { + if (! mipsOperatorMipArg.empty()) { + load->getSequence().push_back(mipsOperatorMipArg.back().mipLevel); + mipsOperatorMipArg.pop_back(); + } else { + load->getSequence().push_back(intermediate.addConstantUnion(0, loc, true)); + } + } + + return load; + } + } + } + + // Handle operator[] on structured buffers: this indexes into the array element of the buffer. + // indexStructBufferContent returns nullptr if it isn't a structuredbuffer (SSBO). + TIntermTyped* sbArray = indexStructBufferContent(loc, base); + if (sbArray != nullptr) { + // Now we'll apply the [] index to that array + const TOperator idxOp = (index->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; + + TIntermTyped* element = intermediate.addIndex(idxOp, sbArray, index, loc); + const TType derefType(sbArray->getType(), 0); + element->setType(derefType); + return element; + } + + return nullptr; +} + +// +// Cast index value to a uint if it isn't already (for operator[], load indexes, etc) +TIntermTyped* HlslParseContext::makeIntegerIndex(TIntermTyped* index) +{ + const TBasicType indexBasicType = index->getType().getBasicType(); + const int vecSize = index->getType().getVectorSize(); + + // We can use int types directly as the index + if (indexBasicType == EbtInt || indexBasicType == EbtUint || + indexBasicType == EbtInt64 || indexBasicType == EbtUint64) + return index; + + // Cast index to unsigned integer if it isn't one. + return intermediate.addConversion(EOpConstructUint, TType(EbtUint, EvqTemporary, vecSize), index); +} + +// +// Handle seeing a base[index] dereference in the grammar. +// +TIntermTyped* HlslParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) +{ + index = makeIntegerIndex(index); + + if (index == nullptr) { + error(loc, " unknown index type ", "", ""); + return nullptr; + } + + TIntermTyped* result = handleBracketOperator(loc, base, index); + + if (result != nullptr) + return result; // it was handled as an operator[] + + bool flattened = false; + int indexValue = 0; + if (index->getQualifier().isFrontEndConstant()) + indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); + + variableCheck(base); + if (! base->isArray() && ! base->isMatrix() && ! base->isVector()) { + if (base->getAsSymbolNode()) + error(loc, " left of '[' is not of type array, matrix, or vector ", + base->getAsSymbolNode()->getName().c_str(), ""); + else + error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); + } else if (base->getType().getQualifier().isFrontEndConstant() && + index->getQualifier().isFrontEndConstant()) { + // both base and index are front-end constants + checkIndex(loc, base->getType(), indexValue); + return intermediate.foldDereference(base, indexValue, loc); + } else { + // at least one of base and index is variable... + + if (index->getQualifier().isFrontEndConstant()) + checkIndex(loc, base->getType(), indexValue); + + if (base->getType().isScalarOrVec1()) + result = base; + else if (base->getAsSymbolNode() && wasFlattened(base)) { + if (index->getQualifier().storage != EvqConst) + error(loc, "Invalid variable index to flattened array", base->getAsSymbolNode()->getName().c_str(), ""); + + result = flattenAccess(base, indexValue); + flattened = (result != base); + } else { + if (index->getQualifier().isFrontEndConstant()) { + if (base->getType().isUnsizedArray()) + base->getWritableType().updateImplicitArraySize(indexValue + 1); + else + checkIndex(loc, base->getType(), indexValue); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + } else + result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); + } + } + + if (result == nullptr) { + // Insert dummy error-recovery result + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + } else { + // If the array reference was flattened, it has the correct type. E.g, if it was + // a uniform array, it was flattened INTO a set of scalar uniforms, not scalar temps. + // In that case, we preserve the qualifiers. + if (!flattened) { + // Insert valid dereferenced result + TType newType(base->getType(), 0); // dereferenced type + if (base->getType().getQualifier().storage == EvqConst && index->getQualifier().storage == EvqConst) + newType.getQualifier().storage = EvqConst; + else + newType.getQualifier().storage = EvqTemporary; + result->setType(newType); + } + } + + return result; +} + +// Handle seeing a binary node with a math operation. +TIntermTyped* HlslParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, + TIntermTyped* left, TIntermTyped* right) +{ + TIntermTyped* result = intermediate.addBinaryMath(op, left, right, loc); + if (result == nullptr) + binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString()); + + return result; +} + +// Handle seeing a unary node with a math operation. +TIntermTyped* HlslParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, + TIntermTyped* childNode) +{ + TIntermTyped* result = intermediate.addUnaryMath(op, childNode, loc); + + if (result) + return result; + else + unaryOpError(loc, str, childNode->getCompleteString()); + + return childNode; +} +// +// Return true if the name is a struct buffer method +// +bool HlslParseContext::isStructBufferMethod(const TString& name) const +{ + return + name == "GetDimensions" || + name == "Load" || + name == "Load2" || + name == "Load3" || + name == "Load4" || + name == "Store" || + name == "Store2" || + name == "Store3" || + name == "Store4" || + name == "InterlockedAdd" || + name == "InterlockedAnd" || + name == "InterlockedCompareExchange" || + name == "InterlockedCompareStore" || + name == "InterlockedExchange" || + name == "InterlockedMax" || + name == "InterlockedMin" || + name == "InterlockedOr" || + name == "InterlockedXor" || + name == "IncrementCounter" || + name == "DecrementCounter" || + name == "Append" || + name == "Consume"; +} + +// +// Handle seeing a base.field dereference in the grammar, where 'field' is a +// swizzle or member variable. +// +TIntermTyped* HlslParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) +{ + variableCheck(base); + + if (base->isArray()) { + error(loc, "cannot apply to an array:", ".", field.c_str()); + return base; + } + + TIntermTyped* result = base; + + if (base->getType().getBasicType() == EbtSampler) { + // Handle .mips[mipid][pos] operation on textures + const TSampler& sampler = base->getType().getSampler(); + if (sampler.isTexture() && field == "mips") { + // Push a null to signify that we expect a mip level under operator[] next. + mipsOperatorMipArg.push_back(tMipsOperatorData(loc, nullptr)); + // Keep 'result' pointing to 'base', since we expect an operator[] to go by next. + } else { + if (field == "mips") + error(loc, "unexpected texture type for .mips[][] operator:", + base->getType().getCompleteString().c_str(), ""); + else + error(loc, "unexpected operator on texture type:", field.c_str(), + base->getType().getCompleteString().c_str()); + } + } else if (base->isVector() || base->isScalar()) { + TSwizzleSelectors selectors; + parseSwizzleSelector(loc, field, base->getVectorSize(), selectors); + + if (base->isScalar()) { + if (selectors.size() == 1) + return result; + else { + TType type(base->getBasicType(), EvqTemporary, selectors.size()); + return addConstructor(loc, base, type); + } + } + if (base->getVectorSize() == 1) { + TType scalarType(base->getBasicType(), EvqTemporary, 1); + if (selectors.size() == 1) + return addConstructor(loc, base, scalarType); + else { + TType vectorType(base->getBasicType(), EvqTemporary, selectors.size()); + return addConstructor(loc, addConstructor(loc, base, scalarType), vectorType); + } + } + + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldSwizzle(base, selectors, loc); + else { + if (selectors.size() == 1) { + TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary)); + } else { + TIntermTyped* index = intermediate.addSwizzle(selectors, loc); + result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, + selectors.size())); + } + } + } else if (base->isMatrix()) { + TSwizzleSelectors selectors; + if (! parseMatrixSwizzleSelector(loc, field, base->getMatrixCols(), base->getMatrixRows(), selectors)) + return result; + + if (selectors.size() == 1) { + // Representable by m[c][r] + if (base->getType().getQualifier().isFrontEndConstant()) { + result = intermediate.foldDereference(base, selectors[0].coord1, loc); + result = intermediate.foldDereference(result, selectors[0].coord2, loc); + } else { + result = intermediate.addIndex(EOpIndexDirect, base, + intermediate.addConstantUnion(selectors[0].coord1, loc), + loc); + TType dereferencedCol(base->getType(), 0); + result->setType(dereferencedCol); + result = intermediate.addIndex(EOpIndexDirect, result, + intermediate.addConstantUnion(selectors[0].coord2, loc), + loc); + TType dereferenced(dereferencedCol, 0); + result->setType(dereferenced); + } + } else { + int column = getMatrixComponentsColumn(base->getMatrixRows(), selectors); + if (column >= 0) { + // Representable by m[c] + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldDereference(base, column, loc); + else { + result = intermediate.addIndex(EOpIndexDirect, base, intermediate.addConstantUnion(column, loc), + loc); + TType dereferenced(base->getType(), 0); + result->setType(dereferenced); + } + } else { + // general case, not a column, not a single component + TIntermTyped* index = intermediate.addSwizzle(selectors, loc); + result = intermediate.addIndex(EOpMatrixSwizzle, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, + selectors.size())); + } + } + } else if (base->getBasicType() == EbtStruct || base->getBasicType() == EbtBlock) { + const TTypeList* fields = base->getType().getStruct(); + bool fieldFound = false; + int member; + for (member = 0; member < (int)fields->size(); ++member) { + if ((*fields)[member].type->getFieldName() == field) { + fieldFound = true; + break; + } + } + if (fieldFound) { + if (base->getAsSymbolNode() && wasFlattened(base)) { + result = flattenAccess(base, member); + } else { + if (base->getType().getQualifier().storage == EvqConst) + result = intermediate.foldDereference(base, member, loc); + else { + TIntermTyped* index = intermediate.addConstantUnion(member, loc); + result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc); + result->setType(*(*fields)[member].type); + } + } + } else + error(loc, "no such field in structure", field.c_str(), ""); + } else + error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str()); + + return result; +} + +// +// Return true if the field should be treated as a built-in method. +// Return false otherwise. +// +bool HlslParseContext::isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field) +{ + if (base == nullptr) + return false; + + variableCheck(base); + + if (base->getType().getBasicType() == EbtSampler) { + return true; + } else if (isStructBufferType(base->getType()) && isStructBufferMethod(field)) { + return true; + } else if (field == "Append" || + field == "RestartStrip") { + // We cannot check the type here: it may be sanitized if we're not compiling a geometry shader, but + // the code is around in the shader source. + return true; + } else + return false; +} + +// Independently establish a built-in that is a member of a structure. +// 'arraySizes' are what's desired for the independent built-in, whatever +// the higher-level source/expression of them was. +void HlslParseContext::splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes* arraySizes, + const TQualifier& outerQualifier) +{ + // Because of arrays of structs, we might be asked more than once, + // but the arraySizes passed in should have captured the whole thing + // the first time. + // However, clip/cull rely on multiple updates. + if (!isClipOrCullDistance(memberType)) + if (splitBuiltIns.find(tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)) != + splitBuiltIns.end()) + return; + + TVariable* ioVar = makeInternalVariable(baseName + "." + memberType.getFieldName(), memberType); + + if (arraySizes != nullptr && !memberType.isArray()) + ioVar->getWritableType().copyArraySizes(*arraySizes); + + splitBuiltIns[tInterstageIoData(memberType.getQualifier().builtIn, outerQualifier.storage)] = ioVar; + if (!isClipOrCullDistance(ioVar->getType())) + trackLinkage(*ioVar); + + // Merge qualifier from the user structure + mergeQualifiers(ioVar->getWritableType().getQualifier(), outerQualifier); + + // Fix the builtin type if needed (e.g, some types require fixed array sizes, no matter how the + // shader declared them). This is done after mergeQualifiers(), in case fixBuiltInIoType looks + // at the qualifier to determine e.g, in or out qualifications. + fixBuiltInIoType(ioVar->getWritableType()); + + // But, not location, we're losing that + ioVar->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd; +} + +// Split a type into +// 1. a struct of non-I/O members +// 2. a collection of independent I/O variables +void HlslParseContext::split(const TVariable& variable) +{ + // Create a new variable: + const TType& clonedType = *variable.getType().clone(); + const TType& splitType = split(clonedType, variable.getName(), clonedType.getQualifier()); + splitNonIoVars[variable.getUniqueId()] = makeInternalVariable(variable.getName(), splitType); +} + +// Recursive implementation of split(). +// Returns reference to the modified type. +const TType& HlslParseContext::split(const TType& type, const TString& name, const TQualifier& outerQualifier) +{ + if (type.isStruct()) { + TTypeList* userStructure = type.getWritableStruct(); + for (auto ioType = userStructure->begin(); ioType != userStructure->end(); ) { + if (ioType->type->isBuiltIn()) { + // move out the built-in + splitBuiltIn(name, *ioType->type, type.getArraySizes(), outerQualifier); + ioType = userStructure->erase(ioType); + } else { + split(*ioType->type, name + "." + ioType->type->getFieldName(), outerQualifier); + ++ioType; + } + } + } + + return type; +} + +// Is this an aggregate that should be flattened? +// Can be applied to intermediate levels of type in a hierarchy. +// Some things like flattening uniform arrays are only about the top level +// of the aggregate, triggered on 'topLevel'. +bool HlslParseContext::shouldFlatten(const TType& type, TStorageQualifier qualifier, bool topLevel) const +{ + switch (qualifier) { + case EvqVaryingIn: + case EvqVaryingOut: + return type.isStruct() || type.isArray(); + case EvqUniform: + return (type.isArray() && intermediate.getFlattenUniformArrays() && topLevel) || + (type.isStruct() && type.containsOpaque()); + default: + return false; + }; +} + +// Top level variable flattening: construct data +void HlslParseContext::flatten(const TVariable& variable, bool linkage, bool arrayed) +{ + const TType& type = variable.getType(); + + // If it's a standalone built-in, there is nothing to flatten + if (type.isBuiltIn() && !type.isStruct()) + return; + + auto entry = flattenMap.insert(std::make_pair(variable.getUniqueId(), + TFlattenData(type.getQualifier().layoutBinding, + type.getQualifier().layoutLocation))); + + // if flattening arrayed io struct, array each member of dereferenced type + if (arrayed) { + const TType dereferencedType(type, 0); + flatten(variable, dereferencedType, entry.first->second, variable.getName(), linkage, + type.getQualifier(), type.getArraySizes()); + } else { + flatten(variable, type, entry.first->second, variable.getName(), linkage, + type.getQualifier(), nullptr); + } +} + +// Recursively flatten the given variable at the provided type, building the flattenData as we go. +// +// This is mutually recursive with flattenStruct and flattenArray. +// We are going to flatten an arbitrarily nested composite structure into a linear sequence of +// members, and later on, we want to turn a path through the tree structure into a final +// location in this linear sequence. +// +// If the tree was N-ary, that can be directly calculated. However, we are dealing with +// arbitrary numbers - perhaps a struct of 7 members containing an array of 3. Thus, we must +// build a data structure to allow the sequence of bracket and dot operators on arrays and +// structs to arrive at the proper member. +// +// To avoid storing a tree with pointers, we are going to flatten the tree into a vector of integers. +// The leaves are the indexes into the flattened member array. +// Each level will have the next location for the Nth item stored sequentially, so for instance: +// +// struct { float2 a[2]; int b; float4 c[3] }; +// +// This will produce the following flattened tree: +// Pos: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 +// (3, 7, 8, 5, 6, 0, 1, 2, 11, 12, 13, 3, 4, 5} +// +// Given a reference to mystruct.c[1], the access chain is (2,1), so we traverse: +// (0+2) = 8 --> (8+1) = 12 --> 12 = 4 +// +// so the 4th flattened member in traversal order is ours. +// +int HlslParseContext::flatten(const TVariable& variable, const TType& type, + TFlattenData& flattenData, TString name, bool linkage, + const TQualifier& outerQualifier, + const TArraySizes* builtInArraySizes) +{ + // If something is an arrayed struct, the array flattener will recursively call flatten() + // to then flatten the struct, so this is an "if else": we don't do both. + if (type.isArray()) + return flattenArray(variable, type, flattenData, name, linkage, outerQualifier); + else if (type.isStruct()) + return flattenStruct(variable, type, flattenData, name, linkage, outerQualifier, builtInArraySizes); + else { + assert(0); // should never happen + return -1; + } +} + +// Add a single flattened member to the flattened data being tracked for the composite +// Returns true for the final flattening level. +int HlslParseContext::addFlattenedMember(const TVariable& variable, const TType& type, TFlattenData& flattenData, + const TString& memberName, bool linkage, + const TQualifier& outerQualifier, + const TArraySizes* builtInArraySizes) +{ + if (!shouldFlatten(type, outerQualifier.storage, false)) { + // This is as far as we flatten. Insert the variable. + TVariable* memberVariable = makeInternalVariable(memberName, type); + mergeQualifiers(memberVariable->getWritableType().getQualifier(), variable.getType().getQualifier()); + + if (flattenData.nextBinding != TQualifier::layoutBindingEnd) + memberVariable->getWritableType().getQualifier().layoutBinding = flattenData.nextBinding++; + + if (memberVariable->getType().isBuiltIn()) { + // inherited locations are nonsensical for built-ins (TODO: what if semantic had a number) + memberVariable->getWritableType().getQualifier().layoutLocation = TQualifier::layoutLocationEnd; + } else { + // inherited locations must be auto bumped, not replicated + if (flattenData.nextLocation != TQualifier::layoutLocationEnd) { + memberVariable->getWritableType().getQualifier().layoutLocation = flattenData.nextLocation; + flattenData.nextLocation += intermediate.computeTypeLocationSize(memberVariable->getType(), language); + nextOutLocation = std::max(nextOutLocation, flattenData.nextLocation); + } + } + + // Only propagate arraysizes here for arrayed io + if (variable.getType().getQualifier().isArrayedIo(language) && builtInArraySizes != nullptr) + memberVariable->getWritableType().copyArraySizes(*builtInArraySizes); + + flattenData.offsets.push_back(static_cast(flattenData.members.size())); + flattenData.members.push_back(memberVariable); + + if (linkage) + trackLinkage(*memberVariable); + + return static_cast(flattenData.offsets.size()) - 1; // location of the member reference + } else { + // Further recursion required + return flatten(variable, type, flattenData, memberName, linkage, outerQualifier, builtInArraySizes); + } +} + +// Figure out the mapping between an aggregate's top members and an +// equivalent set of individual variables. +// +// Assumes shouldFlatten() or equivalent was called first. +int HlslParseContext::flattenStruct(const TVariable& variable, const TType& type, + TFlattenData& flattenData, TString name, bool linkage, + const TQualifier& outerQualifier, + const TArraySizes* builtInArraySizes) +{ + assert(type.isStruct()); + + auto members = *type.getStruct(); + + // Reserve space for this tree level. + int start = static_cast(flattenData.offsets.size()); + int pos = start; + flattenData.offsets.resize(int(pos + members.size()), -1); + + for (int member = 0; member < (int)members.size(); ++member) { + TType& dereferencedType = *members[member].type; + if (dereferencedType.isBuiltIn()) + splitBuiltIn(variable.getName(), dereferencedType, builtInArraySizes, outerQualifier); + else { + const int mpos = addFlattenedMember(variable, dereferencedType, flattenData, + name + "." + dereferencedType.getFieldName(), + linkage, outerQualifier, + builtInArraySizes == nullptr && dereferencedType.isArray() + ? dereferencedType.getArraySizes() + : builtInArraySizes); + flattenData.offsets[pos++] = mpos; + } + } + + return start; +} + +// Figure out mapping between an array's members and an +// equivalent set of individual variables. +// +// Assumes shouldFlatten() or equivalent was called first. +int HlslParseContext::flattenArray(const TVariable& variable, const TType& type, + TFlattenData& flattenData, TString name, bool linkage, + const TQualifier& outerQualifier) +{ + assert(type.isSizedArray()); + + const int size = type.getOuterArraySize(); + const TType dereferencedType(type, 0); + + if (name.empty()) + name = variable.getName(); + + // Reserve space for this tree level. + int start = static_cast(flattenData.offsets.size()); + int pos = start; + flattenData.offsets.resize(int(pos + size), -1); + + for (int element=0; element < size; ++element) { + char elementNumBuf[20]; // sufficient for MAXINT + snprintf(elementNumBuf, sizeof(elementNumBuf)-1, "[%d]", element); + const int mpos = addFlattenedMember(variable, dereferencedType, flattenData, + name + elementNumBuf, linkage, outerQualifier, + type.getArraySizes()); + + flattenData.offsets[pos++] = mpos; + } + + return start; +} + +// Return true if we have flattened this node. +bool HlslParseContext::wasFlattened(const TIntermTyped* node) const +{ + return node != nullptr && node->getAsSymbolNode() != nullptr && + wasFlattened(node->getAsSymbolNode()->getId()); +} + +// Return true if we have split this structure +bool HlslParseContext::wasSplit(const TIntermTyped* node) const +{ + return node != nullptr && node->getAsSymbolNode() != nullptr && + wasSplit(node->getAsSymbolNode()->getId()); +} + +// Turn an access into an aggregate that was flattened to instead be +// an access to the individual variable the member was flattened to. +// Assumes wasFlattened() or equivalent was called first. +TIntermTyped* HlslParseContext::flattenAccess(TIntermTyped* base, int member) +{ + const TType dereferencedType(base->getType(), member); // dereferenced type + const TIntermSymbol& symbolNode = *base->getAsSymbolNode(); + TIntermTyped* flattened = flattenAccess(symbolNode.getId(), member, base->getQualifier().storage, + dereferencedType, symbolNode.getFlattenSubset()); + + return flattened ? flattened : base; +} +TIntermTyped* HlslParseContext::flattenAccess(int uniqueId, int member, TStorageQualifier outerStorage, + const TType& dereferencedType, int subset) +{ + const auto flattenData = flattenMap.find(uniqueId); + + if (flattenData == flattenMap.end()) + return nullptr; + + // Calculate new cumulative offset from the packed tree + int newSubset = flattenData->second.offsets[subset >= 0 ? subset + member : member]; + + TIntermSymbol* subsetSymbol; + if (!shouldFlatten(dereferencedType, outerStorage, false)) { + // Finished flattening: create symbol for variable + member = flattenData->second.offsets[newSubset]; + const TVariable* memberVariable = flattenData->second.members[member]; + subsetSymbol = intermediate.addSymbol(*memberVariable); + subsetSymbol->setFlattenSubset(-1); + } else { + + // If this is not the final flattening, accumulate the position and return + // an object of the partially dereferenced type. + subsetSymbol = new TIntermSymbol(uniqueId, "flattenShadow", dereferencedType); + subsetSymbol->setFlattenSubset(newSubset); + } + + return subsetSymbol; +} + +// For finding where the first leaf is in a subtree of a multi-level aggregate +// that is just getting a subset assigned. Follows the same logic as flattenAccess, +// but logically going down the "left-most" tree branch each step of the way. +// +// Returns the offset into the first leaf of the subset. +int HlslParseContext::findSubtreeOffset(const TIntermNode& node) const +{ + const TIntermSymbol* sym = node.getAsSymbolNode(); + if (sym == nullptr) + return 0; + if (!sym->isArray() && !sym->isStruct()) + return 0; + int subset = sym->getFlattenSubset(); + if (subset == -1) + return 0; + + // Getting this far means a partial aggregate is identified by the flatten subset. + // Find the first leaf of the subset. + + const auto flattenData = flattenMap.find(sym->getId()); + if (flattenData == flattenMap.end()) + return 0; + + return findSubtreeOffset(sym->getType(), subset, flattenData->second.offsets); + + do { + subset = flattenData->second.offsets[subset]; + } while (true); +} +// Recursively do the desent +int HlslParseContext::findSubtreeOffset(const TType& type, int subset, const TVector& offsets) const +{ + if (!type.isArray() && !type.isStruct()) + return offsets[subset]; + TType derefType(type, 0); + return findSubtreeOffset(derefType, offsets[subset], offsets); +}; + +// Find and return the split IO TVariable for id, or nullptr if none. +TVariable* HlslParseContext::getSplitNonIoVar(int id) const +{ + const auto splitNonIoVar = splitNonIoVars.find(id); + if (splitNonIoVar == splitNonIoVars.end()) + return nullptr; + + return splitNonIoVar->second; +} + +// Pass through to base class after remembering built-in mappings. +void HlslParseContext::trackLinkage(TSymbol& symbol) +{ + TBuiltInVariable biType = symbol.getType().getQualifier().builtIn; + + if (biType != EbvNone) + builtInTessLinkageSymbols[biType] = symbol.clone(); + + TParseContextBase::trackLinkage(symbol); +} + + +// Returns true if the built-in is a clip or cull distance variable. +bool HlslParseContext::isClipOrCullDistance(TBuiltInVariable builtIn) +{ + return builtIn == EbvClipDistance || builtIn == EbvCullDistance; +} + +// Some types require fixed array sizes in SPIR-V, but can be scalars or +// arrays of sizes SPIR-V doesn't allow. For example, tessellation factors. +// This creates the right size. A conversion is performed when the internal +// type is copied to or from the external type. This corrects the externally +// facing input or output type to abide downstream semantics. +void HlslParseContext::fixBuiltInIoType(TType& type) +{ + int requiredArraySize = 0; + int requiredVectorSize = 0; + + switch (type.getQualifier().builtIn) { + case EbvTessLevelOuter: requiredArraySize = 4; break; + case EbvTessLevelInner: requiredArraySize = 2; break; + + case EbvSampleMask: + { + // Promote scalar to array of size 1. Leave existing arrays alone. + if (!type.isArray()) + requiredArraySize = 1; + break; + } + + case EbvWorkGroupId: requiredVectorSize = 3; break; + case EbvGlobalInvocationId: requiredVectorSize = 3; break; + case EbvLocalInvocationId: requiredVectorSize = 3; break; + case EbvTessCoord: requiredVectorSize = 3; break; + + default: + if (isClipOrCullDistance(type)) { + const int loc = type.getQualifier().layoutLocation; + + if (type.getQualifier().builtIn == EbvClipDistance) { + if (type.getQualifier().storage == EvqVaryingIn) + clipSemanticNSizeIn[loc] = type.getVectorSize(); + else + clipSemanticNSizeOut[loc] = type.getVectorSize(); + } else { + if (type.getQualifier().storage == EvqVaryingIn) + cullSemanticNSizeIn[loc] = type.getVectorSize(); + else + cullSemanticNSizeOut[loc] = type.getVectorSize(); + } + } + + return; + } + + // Alter or set vector size as needed. + if (requiredVectorSize > 0) { + TType newType(type.getBasicType(), type.getQualifier().storage, requiredVectorSize); + newType.getQualifier() = type.getQualifier(); + + type.shallowCopy(newType); + } + + // Alter or set array size as needed. + if (requiredArraySize > 0) { + if (!type.isArray() || type.getOuterArraySize() != requiredArraySize) { + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(requiredArraySize); + type.transferArraySizes(arraySizes); + } + } +} + +// Variables that correspond to the user-interface in and out of a stage +// (not the built-in interface) are +// - assigned locations +// - registered as a linkage node (part of the stage's external interface). +// Assumes it is called in the order in which locations should be assigned. +void HlslParseContext::assignToInterface(TVariable& variable) +{ + const auto assignLocation = [&](TVariable& variable) { + TType& type = variable.getWritableType(); + if (!type.isStruct() || type.getStruct()->size() > 0) { + TQualifier& qualifier = type.getQualifier(); + if (qualifier.storage == EvqVaryingIn || qualifier.storage == EvqVaryingOut) { + if (qualifier.builtIn == EbvNone && !qualifier.hasLocation()) { + // Strip off the outer array dimension for those having an extra one. + int size; + if (type.isArray() && qualifier.isArrayedIo(language)) { + TType elementType(type, 0); + size = intermediate.computeTypeLocationSize(elementType, language); + } else + size = intermediate.computeTypeLocationSize(type, language); + + if (qualifier.storage == EvqVaryingIn) { + variable.getWritableType().getQualifier().layoutLocation = nextInLocation; + nextInLocation += size; + } else { + variable.getWritableType().getQualifier().layoutLocation = nextOutLocation; + nextOutLocation += size; + } + } + trackLinkage(variable); + } + } + }; + + if (wasFlattened(variable.getUniqueId())) { + auto& memberList = flattenMap[variable.getUniqueId()].members; + for (auto member = memberList.begin(); member != memberList.end(); ++member) + assignLocation(**member); + } else if (wasSplit(variable.getUniqueId())) { + TVariable* splitIoVar = getSplitNonIoVar(variable.getUniqueId()); + assignLocation(*splitIoVar); + } else { + assignLocation(variable); + } +} + +// +// Handle seeing a function declarator in the grammar. This is the precursor +// to recognizing a function prototype or function definition. +// +void HlslParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) +{ + // + // Multiple declarations of the same function name are allowed. + // + // If this is a definition, the definition production code will check for redefinitions + // (we don't know at this point if it's a definition or not). + // + bool builtIn; + TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn); + const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; + + if (prototype) { + // All built-in functions are defined, even though they don't have a body. + // Count their prototype as a definition instead. + if (symbolTable.atBuiltInLevel()) + function.setDefined(); + else { + if (prevDec && ! builtIn) + symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const + function.setPrototyped(); + } + } + + // This insert won't actually insert it if it's a duplicate signature, but it will still check for + // other forms of name collisions. + if (! symbolTable.insert(function)) + error(loc, "function name is redeclaration of existing name", function.getName().c_str(), ""); +} + +// For struct buffers with counters, we must pass the counter buffer as hidden parameter. +// This adds the hidden parameter to the parameter list in 'paramNodes' if needed. +// Otherwise, it's a no-op +void HlslParseContext::addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter& param, + TIntermAggregate*& paramNodes) +{ + if (! hasStructBuffCounter(*param.type)) + return; + + const TString counterBlockName(intermediate.addCounterBufferName(*param.name)); + + TType counterType; + counterBufferType(loc, counterType); + TVariable *variable = makeInternalVariable(counterBlockName, counterType); + + if (! symbolTable.insert(*variable)) + error(loc, "redefinition", variable->getName().c_str(), ""); + + paramNodes = intermediate.growAggregate(paramNodes, + intermediate.addSymbol(*variable, loc), + loc); +} + +// +// Handle seeing the function prototype in front of a function definition in the grammar. +// The body is handled after this function returns. +// +// Returns an aggregate of parameter-symbol nodes. +// +TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function, + const TAttributes& attributes, + TIntermNode*& entryPointTree) +{ + currentCaller = function.getMangledName(); + TSymbol* symbol = symbolTable.find(function.getMangledName()); + TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; + + if (prevDec == nullptr) + error(loc, "can't find function", function.getName().c_str(), ""); + // Note: 'prevDec' could be 'function' if this is the first time we've seen function + // as it would have just been put in the symbol table. Otherwise, we're looking up + // an earlier occurrence. + + if (prevDec && prevDec->isDefined()) { + // Then this function already has a body. + error(loc, "function already has a body", function.getName().c_str(), ""); + } + if (prevDec && ! prevDec->isDefined()) { + prevDec->setDefined(); + + // Remember the return type for later checking for RETURN statements. + currentFunctionType = &(prevDec->getType()); + } else + currentFunctionType = new TType(EbtVoid); + functionReturnsValue = false; + + // Entry points need different I/O and other handling, transform it so the + // rest of this function doesn't care. + entryPointTree = transformEntryPoint(loc, function, attributes); + + // + // New symbol table scope for body of function plus its arguments + // + pushScope(); + + // + // Insert parameters into the symbol table. + // If the parameter has no name, it's not an error, just don't insert it + // (could be used for unused args). + // + // Also, accumulate the list of parameters into the AST, so lower level code + // knows where to find parameters. + // + TIntermAggregate* paramNodes = new TIntermAggregate; + for (int i = 0; i < function.getParamCount(); i++) { + TParameter& param = function[i]; + if (param.name != nullptr) { + TVariable *variable = new TVariable(param.name, *param.type); + + if (i == 0 && function.hasImplicitThis()) { + // Anonymous 'this' members are already in a symbol-table level, + // and we need to know what function parameter to map them to. + symbolTable.makeInternalVariable(*variable); + pushImplicitThis(variable); + } + + // Insert the parameters with name in the symbol table. + if (! symbolTable.insert(*variable)) + error(loc, "redefinition", variable->getName().c_str(), ""); + + // Add parameters to the AST list. + if (shouldFlatten(variable->getType(), variable->getType().getQualifier().storage, true)) { + // Expand the AST parameter nodes (but not the name mangling or symbol table view) + // for structures that need to be flattened. + flatten(*variable, false); + const TTypeList* structure = variable->getType().getStruct(); + for (int mem = 0; mem < (int)structure->size(); ++mem) { + paramNodes = intermediate.growAggregate(paramNodes, + flattenAccess(variable->getUniqueId(), mem, + variable->getType().getQualifier().storage, + *(*structure)[mem].type), + loc); + } + } else { + // Add the parameter to the AST + paramNodes = intermediate.growAggregate(paramNodes, + intermediate.addSymbol(*variable, loc), + loc); + } + + // Add hidden AST parameter for struct buffer counters, if needed. + addStructBufferHiddenCounterParam(loc, param, paramNodes); + } else + paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); + } + if (function.hasIllegalImplicitThis()) + pushImplicitThis(nullptr); + + intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); + loopNestingLevel = 0; + controlFlowNestingLevel = 0; + postEntryPointReturn = false; + + return paramNodes; +} + +// Handle all [attrib] attribute for the shader entry point +void HlslParseContext::handleEntryPointAttributes(const TSourceLoc& loc, const TAttributes& attributes) +{ + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatNumThreads: + { + const TIntermSequence& sequence = it->args->getSequence(); + for (int lid = 0; lid < int(sequence.size()); ++lid) + intermediate.setLocalSize(lid, sequence[lid]->getAsConstantUnion()->getConstArray()[0].getIConst()); + break; + } + case EatMaxVertexCount: + { + int maxVertexCount; + + if (! it->getInt(maxVertexCount)) { + error(loc, "invalid maxvertexcount", "", ""); + } else { + if (! intermediate.setVertices(maxVertexCount)) + error(loc, "cannot change previously set maxvertexcount attribute", "", ""); + } + break; + } + case EatPatchConstantFunc: + { + TString pcfName; + if (! it->getString(pcfName, 0, false)) { + error(loc, "invalid patch constant function", "", ""); + } else { + patchConstantFunctionName = pcfName; + } + break; + } + case EatDomain: + { + // Handle [domain("...")] + TString domainStr; + if (! it->getString(domainStr)) { + error(loc, "invalid domain", "", ""); + } else { + TLayoutGeometry domain = ElgNone; + + if (domainStr == "tri") { + domain = ElgTriangles; + } else if (domainStr == "quad") { + domain = ElgQuads; + } else if (domainStr == "isoline") { + domain = ElgIsolines; + } else { + error(loc, "unsupported domain type", domainStr.c_str(), ""); + } + + if (language == EShLangTessEvaluation) { + if (! intermediate.setInputPrimitive(domain)) + error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); + } else { + if (! intermediate.setOutputPrimitive(domain)) + error(loc, "cannot change previously set domain", TQualifier::getGeometryString(domain), ""); + } + } + break; + } + case EatOutputTopology: + { + // Handle [outputtopology("...")] + TString topologyStr; + if (! it->getString(topologyStr)) { + error(loc, "invalid outputtopology", "", ""); + } else { + TVertexOrder vertexOrder = EvoNone; + TLayoutGeometry primitive = ElgNone; + + if (topologyStr == "point") { + intermediate.setPointMode(); + } else if (topologyStr == "line") { + primitive = ElgIsolines; + } else if (topologyStr == "triangle_cw") { + vertexOrder = EvoCw; + primitive = ElgTriangles; + } else if (topologyStr == "triangle_ccw") { + vertexOrder = EvoCcw; + primitive = ElgTriangles; + } else { + error(loc, "unsupported outputtopology type", topologyStr.c_str(), ""); + } + + if (vertexOrder != EvoNone) { + if (! intermediate.setVertexOrder(vertexOrder)) { + error(loc, "cannot change previously set outputtopology", + TQualifier::getVertexOrderString(vertexOrder), ""); + } + } + if (primitive != ElgNone) + intermediate.setOutputPrimitive(primitive); + } + break; + } + case EatPartitioning: + { + // Handle [partitioning("...")] + TString partitionStr; + if (! it->getString(partitionStr)) { + error(loc, "invalid partitioning", "", ""); + } else { + TVertexSpacing partitioning = EvsNone; + + if (partitionStr == "integer") { + partitioning = EvsEqual; + } else if (partitionStr == "fractional_even") { + partitioning = EvsFractionalEven; + } else if (partitionStr == "fractional_odd") { + partitioning = EvsFractionalOdd; + //} else if (partition == "pow2") { // TODO: currently nothing to map this to. + } else { + error(loc, "unsupported partitioning type", partitionStr.c_str(), ""); + } + + if (! intermediate.setVertexSpacing(partitioning)) + error(loc, "cannot change previously set partitioning", + TQualifier::getVertexSpacingString(partitioning), ""); + } + break; + } + case EatOutputControlPoints: + { + // Handle [outputcontrolpoints("...")] + int ctrlPoints; + if (! it->getInt(ctrlPoints)) { + error(loc, "invalid outputcontrolpoints", "", ""); + } else { + if (! intermediate.setVertices(ctrlPoints)) { + error(loc, "cannot change previously set outputcontrolpoints attribute", "", ""); + } + } + break; + } + case EatEarlyDepthStencil: + intermediate.setEarlyFragmentTests(); + break; + case EatBuiltIn: + case EatLocation: + // tolerate these because of dual use of entrypoint and type attributes + break; + default: + warn(loc, "attribute does not apply to entry point", "", ""); + break; + } + } +} + +// Update the given type with any type-like attribute information in the +// attributes. +void HlslParseContext::transferTypeAttributes(const TSourceLoc& loc, const TAttributes& attributes, TType& type, + bool allowEntry) +{ + if (attributes.size() == 0) + return; + + int value; + TString builtInString; + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatLocation: + // location + if (it->getInt(value)) + type.getQualifier().layoutLocation = value; + else + error(loc, "needs a literal integer", "location", ""); + break; + case EatBinding: + // binding + if (it->getInt(value)) { + type.getQualifier().layoutBinding = value; + type.getQualifier().layoutSet = 0; + } else + error(loc, "needs a literal integer", "binding", ""); + // set + if (it->getInt(value, 1)) + type.getQualifier().layoutSet = value; + break; + case EatGlobalBinding: + // global cbuffer binding + if (it->getInt(value)) + globalUniformBinding = value; + else + error(loc, "needs a literal integer", "global binding", ""); + // global cbuffer set + if (it->getInt(value, 1)) + globalUniformSet = value; + break; + case EatInputAttachment: + // input attachment + if (it->getInt(value)) + type.getQualifier().layoutAttachment = value; + else + error(loc, "needs a literal integer", "input attachment", ""); + break; + case EatBuiltIn: + // PointSize built-in + if (it->getString(builtInString, 0, false)) { + if (builtInString == "PointSize") + type.getQualifier().builtIn = EbvPointSize; + } + break; + case EatPushConstant: + // push_constant + type.getQualifier().layoutPushConstant = true; + break; + case EatConstantId: + // specialization constant + if (type.getQualifier().storage != EvqConst) { + error(loc, "needs a const type", "constant_id", ""); + break; + } + if (it->getInt(value)) { + TSourceLoc loc; + loc.init(); + setSpecConstantId(loc, type.getQualifier(), value); + } + break; + + // image formats + case EatFormatRgba32f: type.getQualifier().layoutFormat = ElfRgba32f; break; + case EatFormatRgba16f: type.getQualifier().layoutFormat = ElfRgba16f; break; + case EatFormatR32f: type.getQualifier().layoutFormat = ElfR32f; break; + case EatFormatRgba8: type.getQualifier().layoutFormat = ElfRgba8; break; + case EatFormatRgba8Snorm: type.getQualifier().layoutFormat = ElfRgba8Snorm; break; + case EatFormatRg32f: type.getQualifier().layoutFormat = ElfRg32f; break; + case EatFormatRg16f: type.getQualifier().layoutFormat = ElfRg16f; break; + case EatFormatR11fG11fB10f: type.getQualifier().layoutFormat = ElfR11fG11fB10f; break; + case EatFormatR16f: type.getQualifier().layoutFormat = ElfR16f; break; + case EatFormatRgba16: type.getQualifier().layoutFormat = ElfRgba16; break; + case EatFormatRgb10A2: type.getQualifier().layoutFormat = ElfRgb10A2; break; + case EatFormatRg16: type.getQualifier().layoutFormat = ElfRg16; break; + case EatFormatRg8: type.getQualifier().layoutFormat = ElfRg8; break; + case EatFormatR16: type.getQualifier().layoutFormat = ElfR16; break; + case EatFormatR8: type.getQualifier().layoutFormat = ElfR8; break; + case EatFormatRgba16Snorm: type.getQualifier().layoutFormat = ElfRgba16Snorm; break; + case EatFormatRg16Snorm: type.getQualifier().layoutFormat = ElfRg16Snorm; break; + case EatFormatRg8Snorm: type.getQualifier().layoutFormat = ElfRg8Snorm; break; + case EatFormatR16Snorm: type.getQualifier().layoutFormat = ElfR16Snorm; break; + case EatFormatR8Snorm: type.getQualifier().layoutFormat = ElfR8Snorm; break; + case EatFormatRgba32i: type.getQualifier().layoutFormat = ElfRgba32i; break; + case EatFormatRgba16i: type.getQualifier().layoutFormat = ElfRgba16i; break; + case EatFormatRgba8i: type.getQualifier().layoutFormat = ElfRgba8i; break; + case EatFormatR32i: type.getQualifier().layoutFormat = ElfR32i; break; + case EatFormatRg32i: type.getQualifier().layoutFormat = ElfRg32i; break; + case EatFormatRg16i: type.getQualifier().layoutFormat = ElfRg16i; break; + case EatFormatRg8i: type.getQualifier().layoutFormat = ElfRg8i; break; + case EatFormatR16i: type.getQualifier().layoutFormat = ElfR16i; break; + case EatFormatR8i: type.getQualifier().layoutFormat = ElfR8i; break; + case EatFormatRgba32ui: type.getQualifier().layoutFormat = ElfRgba32ui; break; + case EatFormatRgba16ui: type.getQualifier().layoutFormat = ElfRgba16ui; break; + case EatFormatRgba8ui: type.getQualifier().layoutFormat = ElfRgba8ui; break; + case EatFormatR32ui: type.getQualifier().layoutFormat = ElfR32ui; break; + case EatFormatRgb10a2ui: type.getQualifier().layoutFormat = ElfRgb10a2ui; break; + case EatFormatRg32ui: type.getQualifier().layoutFormat = ElfRg32ui; break; + case EatFormatRg16ui: type.getQualifier().layoutFormat = ElfRg16ui; break; + case EatFormatRg8ui: type.getQualifier().layoutFormat = ElfRg8ui; break; + case EatFormatR16ui: type.getQualifier().layoutFormat = ElfR16ui; break; + case EatFormatR8ui: type.getQualifier().layoutFormat = ElfR8ui; break; + case EatFormatUnknown: type.getQualifier().layoutFormat = ElfNone; break; + + case EatNonWritable: type.getQualifier().readonly = true; break; + case EatNonReadable: type.getQualifier().writeonly = true; break; + + default: + if (! allowEntry) + warn(loc, "attribute does not apply to a type", "", ""); + break; + } + } +} + +// +// Do all special handling for the entry point, including wrapping +// the shader's entry point with the official entry point that will call it. +// +// The following: +// +// retType shaderEntryPoint(args...) // shader declared entry point +// { body } +// +// Becomes +// +// out retType ret; +// in iargs...; +// out oargs ...; +// +// void shaderEntryPoint() // synthesized, but official, entry point +// { +// args = iargs...; +// ret = @shaderEntryPoint(args...); +// oargs = args...; +// } +// retType @shaderEntryPoint(args...) +// { body } +// +// The symbol table will still map the original entry point name to the +// the modified function and its new name: +// +// symbol table: shaderEntryPoint -> @shaderEntryPoint +// +// Returns nullptr if no entry-point tree was built, otherwise, returns +// a subtree that creates the entry point. +// +TIntermNode* HlslParseContext::transformEntryPoint(const TSourceLoc& loc, TFunction& userFunction, + const TAttributes& attributes) +{ + // Return true if this is a tessellation patch constant function input to a domain shader. + const auto isDsPcfInput = [this](const TType& type) { + return language == EShLangTessEvaluation && + type.contains([](const TType* t) { + return t->getQualifier().builtIn == EbvTessLevelOuter || + t->getQualifier().builtIn == EbvTessLevelInner; + }); + }; + + // if we aren't in the entry point, fix the IO as such and exit + if (! isEntrypointName(userFunction.getName())) { + remapNonEntryPointIO(userFunction); + return nullptr; + } + + entryPointFunction = &userFunction; // needed in finish() + + // Handle entry point attributes + handleEntryPointAttributes(loc, attributes); + + // entry point logic... + + // Move parameters and return value to shader in/out + TVariable* entryPointOutput; // gets created in remapEntryPointIO + TVector inputs; + TVector outputs; + remapEntryPointIO(userFunction, entryPointOutput, inputs, outputs); + + // Further this return/in/out transform by flattening, splitting, and assigning locations + const auto makeVariableInOut = [&](TVariable& variable) { + if (variable.getType().isStruct()) { + bool arrayed = variable.getType().getQualifier().isArrayedIo(language); + flatten(variable, false /* don't track linkage here, it will be tracked in assignToInterface() */, arrayed); + } + // TODO: flatten arrays too + // TODO: flatten everything in I/O + // TODO: replace all split with flatten, make all paths can create flattened I/O, then split code can be removed + + // For clip and cull distance, multiple output variables potentially get merged + // into one in assignClipCullDistance. That code in assignClipCullDistance + // handles the interface logic, so we avoid it here in that case. + if (!isClipOrCullDistance(variable.getType())) + assignToInterface(variable); + }; + if (entryPointOutput != nullptr) + makeVariableInOut(*entryPointOutput); + for (auto it = inputs.begin(); it != inputs.end(); ++it) + if (!isDsPcfInput((*it)->getType())) // wait until the end for PCF input (see comment below) + makeVariableInOut(*(*it)); + for (auto it = outputs.begin(); it != outputs.end(); ++it) + makeVariableInOut(*(*it)); + + // In the domain shader, PCF input must be at the end of the linkage. That's because in the + // hull shader there is no ordering: the output comes from the separate PCF, which does not + // participate in the argument list. That is always put at the end of the HS linkage, so the + // input side of the DS must match. The argument may be in any position in the DS argument list + // however, so this ensures the linkage is built in the correct order regardless of argument order. + if (language == EShLangTessEvaluation) { + for (auto it = inputs.begin(); it != inputs.end(); ++it) + if (isDsPcfInput((*it)->getType())) + makeVariableInOut(*(*it)); + } + + // Add uniform parameters to the $Global uniform block. + TVector opaque_uniforms; + for (int i = 0; i < userFunction.getParamCount(); i++) { + TType& paramType = *userFunction[i].type; + TString& paramName = *userFunction[i].name; + if (paramType.getQualifier().storage == EvqUniform) { + if (!paramType.containsOpaque()) { + // Add it to the global uniform block. + growGlobalUniformBlock(loc, paramType, paramName); + } else { + // Declare it as a separate variable. + TVariable *var = makeInternalVariable(paramName.c_str(), paramType); + opaque_uniforms.push_back(var); + } + } + } + + // Synthesize the call + + pushScope(); // matches the one in handleFunctionBody() + + // new signature + TType voidType(EbtVoid); + TFunction synthEntryPoint(&userFunction.getName(), voidType); + TIntermAggregate* synthParams = new TIntermAggregate(); + intermediate.setAggregateOperator(synthParams, EOpParameters, voidType, loc); + intermediate.setEntryPointMangledName(synthEntryPoint.getMangledName().c_str()); + intermediate.incrementEntryPointCount(); + TFunction callee(&userFunction.getName(), voidType); // call based on old name, which is still in the symbol table + + // change original name + userFunction.addPrefix("@"); // change the name in the function, but not in the symbol table + + // Copy inputs (shader-in -> calling arg), while building up the call node + TVector argVars; + TIntermAggregate* synthBody = new TIntermAggregate(); + auto inputIt = inputs.begin(); + auto opaqueUniformIt = opaque_uniforms.begin(); + TIntermTyped* callingArgs = nullptr; + + for (int i = 0; i < userFunction.getParamCount(); i++) { + TParameter& param = userFunction[i]; + argVars.push_back(makeInternalVariable(*param.name, *param.type)); + argVars.back()->getWritableType().getQualifier().makeTemporary(); + + // Track the input patch, which is the only non-builtin supported by hull shader PCF. + if (param.getDeclaredBuiltIn() == EbvInputPatch) + inputPatch = argVars.back(); + + TIntermSymbol* arg = intermediate.addSymbol(*argVars.back()); + handleFunctionArgument(&callee, callingArgs, arg); + if (param.type->getQualifier().isParamInput()) { + intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, + intermediate.addSymbol(**inputIt))); + inputIt++; + } + if (param.type->getQualifier().storage == EvqUniform) { + if (!param.type->containsOpaque()) { + // Look it up in the $Global uniform block. + intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, + handleVariable(loc, param.name))); + } else { + intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, arg, + intermediate.addSymbol(**opaqueUniformIt))); + ++opaqueUniformIt; + } + } + } + + // Call + currentCaller = synthEntryPoint.getMangledName(); + TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs); + currentCaller = userFunction.getMangledName(); + + // Return value + if (entryPointOutput) { + TIntermTyped* returnAssign; + + // For hull shaders, the wrapped entry point return value is written to + // an array element as indexed by invocation ID, which we might have to make up. + // This is required to match SPIR-V semantics. + if (language == EShLangTessControl) { + TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId); + + // If there is no user declared invocation ID, we must make one. + if (invocationIdSym == nullptr) { + TType invocationIdType(EbtUint, EvqIn, 1); + TString* invocationIdName = NewPoolTString("InvocationId"); + invocationIdType.getQualifier().builtIn = EbvInvocationId; + + TVariable* variable = makeInternalVariable(*invocationIdName, invocationIdType); + + globalQualifierFix(loc, variable->getWritableType().getQualifier()); + trackLinkage(*variable); + + invocationIdSym = intermediate.addSymbol(*variable); + } + + TIntermTyped* element = intermediate.addIndex(EOpIndexIndirect, intermediate.addSymbol(*entryPointOutput), + invocationIdSym, loc); + + // Set the type of the array element being dereferenced + const TType derefElementType(entryPointOutput->getType(), 0); + element->setType(derefElementType); + + returnAssign = handleAssign(loc, EOpAssign, element, callReturn); + } else { + returnAssign = handleAssign(loc, EOpAssign, intermediate.addSymbol(*entryPointOutput), callReturn); + } + intermediate.growAggregate(synthBody, returnAssign); + } else + intermediate.growAggregate(synthBody, callReturn); + + // Output copies + auto outputIt = outputs.begin(); + for (int i = 0; i < userFunction.getParamCount(); i++) { + TParameter& param = userFunction[i]; + + // GS outputs are via emit, so we do not copy them here. + if (param.type->getQualifier().isParamOutput()) { + if (param.getDeclaredBuiltIn() == EbvGsOutputStream) { + // GS output stream does not assign outputs here: it's the Append() method + // which writes to the output, probably multiple times separated by Emit. + // We merely remember the output to use, here. + gsStreamOutput = *outputIt; + } else { + intermediate.growAggregate(synthBody, handleAssign(loc, EOpAssign, + intermediate.addSymbol(**outputIt), + intermediate.addSymbol(*argVars[i]))); + } + + outputIt++; + } + } + + // Put the pieces together to form a full function subtree + // for the synthesized entry point. + synthBody->setOperator(EOpSequence); + TIntermNode* synthFunctionDef = synthParams; + handleFunctionBody(loc, synthEntryPoint, synthBody, synthFunctionDef); + + entryPointFunctionBody = synthBody; + + return synthFunctionDef; +} + +void HlslParseContext::handleFunctionBody(const TSourceLoc& loc, TFunction& function, TIntermNode* functionBody, + TIntermNode*& node) +{ + node = intermediate.growAggregate(node, functionBody); + intermediate.setAggregateOperator(node, EOpFunction, function.getType(), loc); + node->getAsAggregate()->setName(function.getMangledName().c_str()); + + popScope(); + if (function.hasImplicitThis()) + popImplicitThis(); + + if (function.getType().getBasicType() != EbtVoid && ! functionReturnsValue) + error(loc, "function does not return a value:", "", function.getName().c_str()); +} + +// AST I/O is done through shader globals declared in the 'in' or 'out' +// storage class. An HLSL entry point has a return value, input parameters +// and output parameters. These need to get remapped to the AST I/O. +void HlslParseContext::remapEntryPointIO(TFunction& function, TVariable*& returnValue, + TVector& inputs, TVector& outputs) +{ + // We might have in input structure type with no decorations that caused it + // to look like an input type, yet it has (e.g.) interpolation types that + // must be modified that turn it into an input type. + // Hence, a missing ioTypeMap for 'input' might need to be synthesized. + const auto synthesizeEditedInput = [this](TType& type) { + // True if a type needs to be 'flat' + const auto needsFlat = [](const TType& type) { + return type.containsBasicType(EbtInt) || + type.containsBasicType(EbtUint) || + type.containsBasicType(EbtInt64) || + type.containsBasicType(EbtUint64) || + type.containsBasicType(EbtBool) || + type.containsBasicType(EbtDouble); + }; + + if (language == EShLangFragment && needsFlat(type)) { + if (type.isStruct()) { + TTypeList* finalList = nullptr; + auto it = ioTypeMap.find(type.getStruct()); + if (it == ioTypeMap.end() || it->second.input == nullptr) { + // Getting here means we have no input struct, but we need one. + auto list = new TTypeList; + for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { + TType* newType = new TType; + newType->shallowCopy(*member->type); + TTypeLoc typeLoc = { newType, member->loc }; + list->push_back(typeLoc); + } + // install the new input type + if (it == ioTypeMap.end()) { + tIoKinds newLists = { list, nullptr, nullptr }; + ioTypeMap[type.getStruct()] = newLists; + } else + it->second.input = list; + finalList = list; + } else + finalList = it->second.input; + // edit for 'flat' + for (auto member = finalList->begin(); member != finalList->end(); ++member) { + if (needsFlat(*member->type)) { + member->type->getQualifier().clearInterpolation(); + member->type->getQualifier().flat = true; + } + } + } else { + type.getQualifier().clearInterpolation(); + type.getQualifier().flat = true; + } + } + }; + + // Do the actual work to make a type be a shader input or output variable, + // and clear the original to be non-IO (for use as a normal function parameter/return). + const auto makeIoVariable = [this](const char* name, TType& type, TStorageQualifier storage) -> TVariable* { + TVariable* ioVariable = makeInternalVariable(name, type); + clearUniformInputOutput(type.getQualifier()); + if (type.isStruct()) { + auto newLists = ioTypeMap.find(ioVariable->getType().getStruct()); + if (newLists != ioTypeMap.end()) { + if (storage == EvqVaryingIn && newLists->second.input) + ioVariable->getWritableType().setStruct(newLists->second.input); + else if (storage == EvqVaryingOut && newLists->second.output) + ioVariable->getWritableType().setStruct(newLists->second.output); + } + } + if (storage == EvqVaryingIn) { + correctInput(ioVariable->getWritableType().getQualifier()); + if (language == EShLangTessEvaluation) + if (!ioVariable->getType().isArray()) + ioVariable->getWritableType().getQualifier().patch = true; + } else { + correctOutput(ioVariable->getWritableType().getQualifier()); + } + ioVariable->getWritableType().getQualifier().storage = storage; + + fixBuiltInIoType(ioVariable->getWritableType()); + + return ioVariable; + }; + + // return value is actually a shader-scoped output (out) + if (function.getType().getBasicType() == EbtVoid) { + returnValue = nullptr; + } else { + if (language == EShLangTessControl) { + // tessellation evaluation in HLSL writes a per-ctrl-pt value, but it needs to be an + // array in SPIR-V semantics. We'll write to it indexed by invocation ID. + + returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut); + + TType outputType; + outputType.shallowCopy(function.getType()); + + // vertices has necessarily already been set when handling entry point attributes. + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(intermediate.getVertices()); + outputType.transferArraySizes(arraySizes); + + clearUniformInputOutput(function.getWritableType().getQualifier()); + returnValue = makeIoVariable("@entryPointOutput", outputType, EvqVaryingOut); + } else { + returnValue = makeIoVariable("@entryPointOutput", function.getWritableType(), EvqVaryingOut); + } + } + + // parameters are actually shader-scoped inputs and outputs (in or out) + for (int i = 0; i < function.getParamCount(); i++) { + TType& paramType = *function[i].type; + if (paramType.getQualifier().isParamInput()) { + synthesizeEditedInput(paramType); + TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingIn); + inputs.push_back(argAsGlobal); + } + if (paramType.getQualifier().isParamOutput()) { + TVariable* argAsGlobal = makeIoVariable(function[i].name->c_str(), paramType, EvqVaryingOut); + outputs.push_back(argAsGlobal); + } + } +} + +// An HLSL function that looks like an entry point, but is not, +// declares entry point IO built-ins, but these have to be undone. +void HlslParseContext::remapNonEntryPointIO(TFunction& function) +{ + // return value + if (function.getType().getBasicType() != EbtVoid) + clearUniformInputOutput(function.getWritableType().getQualifier()); + + // parameters. + // References to structuredbuffer types are left unmodified + for (int i = 0; i < function.getParamCount(); i++) + if (!isReference(*function[i].type)) + clearUniformInputOutput(function[i].type->getQualifier()); +} + +// Handle function returns, including type conversions to the function return type +// if necessary. +TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) +{ + functionReturnsValue = true; + + if (currentFunctionType->getBasicType() == EbtVoid) { + error(loc, "void function cannot return a value", "return", ""); + return intermediate.addBranch(EOpReturn, loc); + } else if (*currentFunctionType != value->getType()) { + value = intermediate.addConversion(EOpReturn, *currentFunctionType, value); + if (value && *currentFunctionType != value->getType()) + value = intermediate.addUniShapeConversion(EOpReturn, *currentFunctionType, value); + if (value == nullptr || *currentFunctionType != value->getType()) { + error(loc, "type does not match, or is not convertible to, the function's return type", "return", ""); + return value; + } + } + + return intermediate.addBranch(EOpReturn, value, loc); +} + +void HlslParseContext::handleFunctionArgument(TFunction* function, + TIntermTyped*& arguments, TIntermTyped* newArg) +{ + TParameter param = { 0, new TType, nullptr }; + param.type->shallowCopy(newArg->getType()); + + function->addParameter(param); + if (arguments) + arguments = intermediate.growAggregate(arguments, newArg); + else + arguments = newArg; +} + +// Position may require special handling: we can optionally invert Y. +// See: https://github.com/KhronosGroup/glslang/issues/1173 +// https://github.com/KhronosGroup/glslang/issues/494 +TIntermTyped* HlslParseContext::assignPosition(const TSourceLoc& loc, TOperator op, + TIntermTyped* left, TIntermTyped* right) +{ + // If we are not asked for Y inversion, use a plain old assign. + if (!intermediate.getInvertY()) + return intermediate.addAssign(op, left, right, loc); + + // If we get here, we should invert Y. + TIntermAggregate* assignList = nullptr; + + // If this is a complex rvalue, we don't want to dereference it many times. Create a temporary. + TVariable* rhsTempVar = nullptr; + rhsTempVar = makeInternalVariable("@position", right->getType()); + rhsTempVar->getWritableType().getQualifier().makeTemporary(); + + { + TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc); + assignList = intermediate.growAggregate(assignList, + intermediate.addAssign(EOpAssign, rhsTempSym, right, loc), loc); + } + + // pos.y = -pos.y + { + const int Y = 1; + + TIntermTyped* tempSymL = intermediate.addSymbol(*rhsTempVar, loc); + TIntermTyped* tempSymR = intermediate.addSymbol(*rhsTempVar, loc); + TIntermTyped* index = intermediate.addConstantUnion(Y, loc); + + TIntermTyped* lhsElement = intermediate.addIndex(EOpIndexDirect, tempSymL, index, loc); + TIntermTyped* rhsElement = intermediate.addIndex(EOpIndexDirect, tempSymR, index, loc); + + const TType derefType(right->getType(), 0); + + lhsElement->setType(derefType); + rhsElement->setType(derefType); + + TIntermTyped* yNeg = intermediate.addUnaryMath(EOpNegative, rhsElement, loc); + + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(EOpAssign, lhsElement, yNeg, loc)); + } + + // Assign the rhs temp (now with Y inversion) to the final output + { + TIntermTyped* rhsTempSym = intermediate.addSymbol(*rhsTempVar, loc); + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, rhsTempSym, loc)); + } + + assert(assignList != nullptr); + assignList->setOperator(EOpSequence); + + return assignList; +} + +// Clip and cull distance require special handling due to a semantic mismatch. In HLSL, +// these can be float scalar, float vector, or arrays of float scalar or float vector. +// In SPIR-V, they are arrays of scalar floats in all cases. We must copy individual components +// (e.g, both x and y components of a float2) out into the destination float array. +// +// The values are assigned to sequential members of the output array. The inner dimension +// is vector components. The outer dimension is array elements. +TIntermAggregate* HlslParseContext::assignClipCullDistance(const TSourceLoc& loc, TOperator op, int semanticId, + TIntermTyped* left, TIntermTyped* right) +{ + switch (language) { + case EShLangFragment: + case EShLangVertex: + case EShLangGeometry: + break; + default: + error(loc, "unimplemented: clip/cull not currently implemented for this stage", "", ""); + return nullptr; + } + + TVariable** clipCullVar = nullptr; + + // Figure out if we are assigning to, or from, clip or cull distance. + const bool isOutput = isClipOrCullDistance(left->getType()); + + // This is the rvalue or lvalue holding the clip or cull distance. + TIntermTyped* clipCullNode = isOutput ? left : right; + // This is the value going into or out of the clip or cull distance. + TIntermTyped* internalNode = isOutput ? right : left; + + const TBuiltInVariable builtInType = clipCullNode->getQualifier().builtIn; + + decltype(clipSemanticNSizeIn)* semanticNSize = nullptr; + + // Refer to either the clip or the cull distance, depending on semantic. + switch (builtInType) { + case EbvClipDistance: + clipCullVar = isOutput ? &clipDistanceOutput : &clipDistanceInput; + semanticNSize = isOutput ? &clipSemanticNSizeOut : &clipSemanticNSizeIn; + break; + case EbvCullDistance: + clipCullVar = isOutput ? &cullDistanceOutput : &cullDistanceInput; + semanticNSize = isOutput ? &cullSemanticNSizeOut : &cullSemanticNSizeIn; + break; + + // called invalidly: we expected a clip or a cull distance. + // static compile time problem: should not happen. + default: assert(0); return nullptr; + } + + // This is the offset in the destination array of a given semantic's data + std::array semanticOffset; + + // Calculate offset of variable of semantic N in destination array + int arrayLoc = 0; + int vecItems = 0; + + for (int x = 0; x < maxClipCullRegs; ++x) { + // See if we overflowed the vec4 packing + if ((vecItems + (*semanticNSize)[x]) > 4) { + arrayLoc = (arrayLoc + 3) & (~0x3); // round up to next multiple of 4 + vecItems = 0; + } + + semanticOffset[x] = arrayLoc; + vecItems += (*semanticNSize)[x]; + arrayLoc += (*semanticNSize)[x]; + } + + + // It can have up to 2 array dimensions (in the case of geometry shader inputs) + const TArraySizes* const internalArraySizes = internalNode->getType().getArraySizes(); + const int internalArrayDims = internalNode->getType().isArray() ? internalArraySizes->getNumDims() : 0; + // vector sizes: + const int internalVectorSize = internalNode->getType().getVectorSize(); + // array sizes, or 1 if it's not an array: + const int internalInnerArraySize = (internalArrayDims > 0 ? internalArraySizes->getDimSize(internalArrayDims-1) : 1); + const int internalOuterArraySize = (internalArrayDims > 1 ? internalArraySizes->getDimSize(0) : 1); + + // The created type may be an array of arrays, e.g, for geometry shader inputs. + const bool isImplicitlyArrayed = (language == EShLangGeometry && !isOutput); + + // If we haven't created the output already, create it now. + if (*clipCullVar == nullptr) { + // ClipDistance and CullDistance are handled specially in the entry point input/output copy + // algorithm, because they may need to be unpacked from components of vectors (or a scalar) + // into a float array, or vice versa. Here, we make the array the right size and type, + // which depends on the incoming data, which has several potential dimensions: + // * Semantic ID + // * vector size + // * array size + // Of those, semantic ID and array size cannot appear simultaneously. + // + // Also to note: for implicitly arrayed forms (e.g, geometry shader inputs), we need to create two + // array dimensions. The shader's declaration may have one or two array dimensions. One is always + // the geometry's dimension. + + const bool useInnerSize = internalArrayDims > 1 || !isImplicitlyArrayed; + + const int requiredInnerArraySize = arrayLoc * (useInnerSize ? internalInnerArraySize : 1); + const int requiredOuterArraySize = (internalArrayDims > 0) ? internalArraySizes->getDimSize(0) : 1; + + TType clipCullType(EbtFloat, clipCullNode->getType().getQualifier().storage, 1); + clipCullType.getQualifier() = clipCullNode->getType().getQualifier(); + + // Create required array dimension + TArraySizes* arraySizes = new TArraySizes; + if (isImplicitlyArrayed) + arraySizes->addInnerSize(requiredOuterArraySize); + arraySizes->addInnerSize(requiredInnerArraySize); + clipCullType.transferArraySizes(arraySizes); + + // Obtain symbol name: we'll use that for the symbol we introduce. + TIntermSymbol* sym = clipCullNode->getAsSymbolNode(); + assert(sym != nullptr); + + // We are moving the semantic ID from the layout location, so it is no longer needed or + // desired there. + clipCullType.getQualifier().layoutLocation = TQualifier::layoutLocationEnd; + + // Create variable and track its linkage + *clipCullVar = makeInternalVariable(sym->getName().c_str(), clipCullType); + + trackLinkage(**clipCullVar); + } + + // Create symbol for the clip or cull variable. + TIntermSymbol* clipCullSym = intermediate.addSymbol(**clipCullVar); + + // vector sizes: + const int clipCullVectorSize = clipCullSym->getType().getVectorSize(); + + // array sizes, or 1 if it's not an array: + const TArraySizes* const clipCullArraySizes = clipCullSym->getType().getArraySizes(); + const int clipCullOuterArraySize = isImplicitlyArrayed ? clipCullArraySizes->getDimSize(0) : 1; + const int clipCullInnerArraySize = clipCullArraySizes->getDimSize(isImplicitlyArrayed ? 1 : 0); + + // clipCullSym has got to be an array of scalar floats, per SPIR-V semantics. + // fixBuiltInIoType() should have handled that upstream. + assert(clipCullSym->getType().isArray()); + assert(clipCullSym->getType().getVectorSize() == 1); + assert(clipCullSym->getType().getBasicType() == EbtFloat); + + // We may be creating multiple sub-assignments. This is an aggregate to hold them. + // TODO: it would be possible to be clever sometimes and avoid the sequence node if not needed. + TIntermAggregate* assignList = nullptr; + + // Holds individual component assignments as we make them. + TIntermTyped* clipCullAssign = nullptr; + + // If the types are homomorphic, use a simple assign. No need to mess about with + // individual components. + if (clipCullSym->getType().isArray() == internalNode->getType().isArray() && + clipCullInnerArraySize == internalInnerArraySize && + clipCullOuterArraySize == internalOuterArraySize && + clipCullVectorSize == internalVectorSize) { + + if (isOutput) + clipCullAssign = intermediate.addAssign(op, clipCullSym, internalNode, loc); + else + clipCullAssign = intermediate.addAssign(op, internalNode, clipCullSym, loc); + + assignList = intermediate.growAggregate(assignList, clipCullAssign); + assignList->setOperator(EOpSequence); + + return assignList; + } + + // We are going to copy each component of the internal (per array element if indicated) to sequential + // array elements of the clipCullSym. This tracks the lhs element we're writing to as we go along. + // We may be starting in the middle - e.g, for a non-zero semantic ID calculated above. + int clipCullInnerArrayPos = semanticOffset[semanticId]; + int clipCullOuterArrayPos = 0; + + // Lambda to add an index to a node, set the type of the result, and return the new node. + const auto addIndex = [this, &loc](TIntermTyped* node, int pos) -> TIntermTyped* { + const TType derefType(node->getType(), 0); + node = intermediate.addIndex(EOpIndexDirect, node, intermediate.addConstantUnion(pos, loc), loc); + node->setType(derefType); + return node; + }; + + // Loop through every component of every element of the internal, and copy to or from the matching external. + for (int internalOuterArrayPos = 0; internalOuterArrayPos < internalOuterArraySize; ++internalOuterArrayPos) { + for (int internalInnerArrayPos = 0; internalInnerArrayPos < internalInnerArraySize; ++internalInnerArrayPos) { + for (int internalComponent = 0; internalComponent < internalVectorSize; ++internalComponent) { + // clip/cull array member to read from / write to: + TIntermTyped* clipCullMember = clipCullSym; + + // If implicitly arrayed, there is an outer array dimension involved + if (isImplicitlyArrayed) + clipCullMember = addIndex(clipCullMember, clipCullOuterArrayPos); + + // Index into proper array position for clip cull member + clipCullMember = addIndex(clipCullMember, clipCullInnerArrayPos++); + + // if needed, start over with next outer array slice. + if (isImplicitlyArrayed && clipCullInnerArrayPos >= clipCullInnerArraySize) { + clipCullInnerArrayPos = semanticOffset[semanticId]; + ++clipCullOuterArrayPos; + } + + // internal member to read from / write to: + TIntermTyped* internalMember = internalNode; + + // If internal node has outer array dimension, index appropriately. + if (internalArrayDims > 1) + internalMember = addIndex(internalMember, internalOuterArrayPos); + + // If internal node has inner array dimension, index appropriately. + if (internalArrayDims > 0) + internalMember = addIndex(internalMember, internalInnerArrayPos); + + // If internal node is a vector, extract the component of interest. + if (internalNode->getType().isVector()) + internalMember = addIndex(internalMember, internalComponent); + + // Create an assignment: output from internal to clip cull, or input from clip cull to internal. + if (isOutput) + clipCullAssign = intermediate.addAssign(op, clipCullMember, internalMember, loc); + else + clipCullAssign = intermediate.addAssign(op, internalMember, clipCullMember, loc); + + // Track assignment in the sequence. + assignList = intermediate.growAggregate(assignList, clipCullAssign); + } + } + } + + assert(assignList != nullptr); + assignList->setOperator(EOpSequence); + + return assignList; +} + +// Some simple source assignments need to be flattened to a sequence +// of AST assignments. Catch these and flatten, otherwise, pass through +// to intermediate.addAssign(). +// +// Also, assignment to matrix swizzles requires multiple component assignments, +// intercept those as well. +TIntermTyped* HlslParseContext::handleAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, + TIntermTyped* right) +{ + if (left == nullptr || right == nullptr) + return nullptr; + + // writing to opaques will require fixing transforms + if (left->getType().containsOpaque()) + intermediate.setNeedsLegalization(); + + if (left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle) + return handleAssignToMatrixSwizzle(loc, op, left, right); + + // Return true if the given node is an index operation into a split variable. + const auto indexesSplit = [this](const TIntermTyped* node) -> bool { + const TIntermBinary* binaryNode = node->getAsBinaryNode(); + + if (binaryNode == nullptr) + return false; + + return (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect) && + wasSplit(binaryNode->getLeft()); + }; + + // Return symbol if node is symbol or index ref + const auto getSymbol = [](const TIntermTyped* node) -> const TIntermSymbol* { + const TIntermSymbol* symbolNode = node->getAsSymbolNode(); + if (symbolNode != nullptr) + return symbolNode; + + const TIntermBinary* binaryNode = node->getAsBinaryNode(); + if (binaryNode != nullptr && (binaryNode->getOp() == EOpIndexDirect || binaryNode->getOp() == EOpIndexIndirect)) + return binaryNode->getLeft()->getAsSymbolNode(); + + return nullptr; + }; + + // Return true if this stage assigns clip position with potentially inverted Y + const auto assignsClipPos = [this](const TIntermTyped* node) -> bool { + return node->getType().getQualifier().builtIn == EbvPosition && + (language == EShLangVertex || language == EShLangGeometry || language == EShLangTessEvaluation); + }; + + const TIntermSymbol* leftSymbol = getSymbol(left); + const TIntermSymbol* rightSymbol = getSymbol(right); + + const bool isSplitLeft = wasSplit(left) || indexesSplit(left); + const bool isSplitRight = wasSplit(right) || indexesSplit(right); + + const bool isFlattenLeft = wasFlattened(leftSymbol); + const bool isFlattenRight = wasFlattened(rightSymbol); + + // OK to do a single assign if neither side is split or flattened. Otherwise, + // fall through to a member-wise copy. + if (!isFlattenLeft && !isFlattenRight && !isSplitLeft && !isSplitRight) { + // Clip and cull distance requires more processing. See comment above assignClipCullDistance. + if (isClipOrCullDistance(left->getType()) || isClipOrCullDistance(right->getType())) { + const bool isOutput = isClipOrCullDistance(left->getType()); + + const int semanticId = (isOutput ? left : right)->getType().getQualifier().layoutLocation; + return assignClipCullDistance(loc, op, semanticId, left, right); + } else if (assignsClipPos(left)) { + // Position can require special handling: see comment above assignPosition + return assignPosition(loc, op, left, right); + } else if (left->getQualifier().builtIn == EbvSampleMask) { + // Certain builtins are required to be arrayed outputs in SPIR-V, but may internally be scalars + // in the shader. Copy the scalar RHS into the LHS array element zero, if that happens. + if (left->isArray() && !right->isArray()) { + const TType derefType(left->getType(), 0); + left = intermediate.addIndex(EOpIndexDirect, left, intermediate.addConstantUnion(0, loc), loc); + left->setType(derefType); + // Fall through to add assign. + } + } + + return intermediate.addAssign(op, left, right, loc); + } + + TIntermAggregate* assignList = nullptr; + const TVector* leftVariables = nullptr; + const TVector* rightVariables = nullptr; + + // A temporary to store the right node's value, so we don't keep indirecting into it + // if it's not a simple symbol. + TVariable* rhsTempVar = nullptr; + + // If the RHS is a simple symbol node, we'll copy it for each member. + TIntermSymbol* cloneSymNode = nullptr; + + int memberCount = 0; + + // Track how many items there are to copy. + if (left->getType().isStruct()) + memberCount = (int)left->getType().getStruct()->size(); + if (left->getType().isArray()) + memberCount = left->getType().getCumulativeArraySize(); + + if (isFlattenLeft) + leftVariables = &flattenMap.find(leftSymbol->getId())->second.members; + + if (isFlattenRight) { + rightVariables = &flattenMap.find(rightSymbol->getId())->second.members; + } else { + // The RHS is not flattened. There are several cases: + // 1. 1 item to copy: Use the RHS directly. + // 2. >1 item, simple symbol RHS: we'll create a new TIntermSymbol node for each, but no assign to temp. + // 3. >1 item, complex RHS: assign it to a new temp variable, and create a TIntermSymbol for each member. + + if (memberCount <= 1) { + // case 1: we'll use the symbol directly below. Nothing to do. + } else { + if (right->getAsSymbolNode() != nullptr) { + // case 2: we'll copy the symbol per iteration below. + cloneSymNode = right->getAsSymbolNode(); + } else { + // case 3: assign to a temp, and indirect into that. + rhsTempVar = makeInternalVariable("flattenTemp", right->getType()); + rhsTempVar->getWritableType().getQualifier().makeTemporary(); + TIntermTyped* noFlattenRHS = intermediate.addSymbol(*rhsTempVar, loc); + + // Add this to the aggregate being built. + assignList = intermediate.growAggregate(assignList, + intermediate.addAssign(op, noFlattenRHS, right, loc), loc); + } + } + } + + // When dealing with split arrayed structures of built-ins, the arrayness is moved to the extracted built-in + // variables, which is awkward when copying between split and unsplit structures. This variable tracks + // array indirections so they can be percolated from outer structs to inner variables. + std::vector arrayElement; + + TStorageQualifier leftStorage = left->getType().getQualifier().storage; + TStorageQualifier rightStorage = right->getType().getQualifier().storage; + + int leftOffsetStart = findSubtreeOffset(*left); + int rightOffsetStart = findSubtreeOffset(*right); + int leftOffset = leftOffsetStart; + int rightOffset = rightOffsetStart; + + const auto getMember = [&](bool isLeft, const TType& type, int member, TIntermTyped* splitNode, int splitMember, + bool flattened) + -> TIntermTyped * { + const bool split = isLeft ? isSplitLeft : isSplitRight; + + TIntermTyped* subTree; + const TType derefType(type, member); + const TVariable* builtInVar = nullptr; + if ((flattened || split) && derefType.isBuiltIn()) { + auto splitPair = splitBuiltIns.find(HlslParseContext::tInterstageIoData( + derefType.getQualifier().builtIn, + isLeft ? leftStorage : rightStorage)); + if (splitPair != splitBuiltIns.end()) + builtInVar = splitPair->second; + } + if (builtInVar != nullptr) { + // copy from interstage IO built-in if needed + subTree = intermediate.addSymbol(*builtInVar); + + if (subTree->getType().isArray()) { + // Arrayness of builtIn symbols isn't handled by the normal recursion: + // it's been extracted and moved to the built-in. + if (!arrayElement.empty()) { + const TType splitDerefType(subTree->getType(), arrayElement.back()); + subTree = intermediate.addIndex(EOpIndexDirect, subTree, + intermediate.addConstantUnion(arrayElement.back(), loc), loc); + subTree->setType(splitDerefType); + } else if (splitNode->getAsOperator() != nullptr && (splitNode->getAsOperator()->getOp() == EOpIndexIndirect)) { + // This might also be a stage with arrayed outputs, in which case there's an index + // operation we should transfer to the output builtin. + + const TType splitDerefType(subTree->getType(), 0); + subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree, + splitNode->getAsBinaryNode()->getRight(), loc); + subTree->setType(splitDerefType); + } + } + } else if (flattened && !shouldFlatten(derefType, isLeft ? leftStorage : rightStorage, false)) { + if (isLeft) { + // offset will cycle through variables for arrayed io + if (leftOffset >= static_cast(leftVariables->size())) + leftOffset = leftOffsetStart; + subTree = intermediate.addSymbol(*(*leftVariables)[leftOffset++]); + } else { + // offset will cycle through variables for arrayed io + if (rightOffset >= static_cast(rightVariables->size())) + rightOffset = rightOffsetStart; + subTree = intermediate.addSymbol(*(*rightVariables)[rightOffset++]); + } + + // arrayed io + if (subTree->getType().isArray()) { + if (!arrayElement.empty()) { + const TType derefType(subTree->getType(), arrayElement.front()); + subTree = intermediate.addIndex(EOpIndexDirect, subTree, + intermediate.addConstantUnion(arrayElement.front(), loc), loc); + subTree->setType(derefType); + } else { + // There's an index operation we should transfer to the output builtin. + assert(splitNode->getAsOperator() != nullptr && + splitNode->getAsOperator()->getOp() == EOpIndexIndirect); + const TType splitDerefType(subTree->getType(), 0); + subTree = intermediate.addIndex(splitNode->getAsOperator()->getOp(), subTree, + splitNode->getAsBinaryNode()->getRight(), loc); + subTree->setType(splitDerefType); + } + } + } else { + // Index operator if it's an aggregate, else EOpNull + const TOperator accessOp = type.isArray() ? EOpIndexDirect + : type.isStruct() ? EOpIndexDirectStruct + : EOpNull; + if (accessOp == EOpNull) { + subTree = splitNode; + } else { + subTree = intermediate.addIndex(accessOp, splitNode, intermediate.addConstantUnion(splitMember, loc), + loc); + const TType splitDerefType(splitNode->getType(), splitMember); + subTree->setType(splitDerefType); + } + } + + return subTree; + }; + + // Use the proper RHS node: a new symbol from a TVariable, copy + // of an TIntermSymbol node, or sometimes the right node directly. + right = rhsTempVar != nullptr ? intermediate.addSymbol(*rhsTempVar, loc) : + cloneSymNode != nullptr ? intermediate.addSymbol(*cloneSymNode) : + right; + + // Cannot use auto here, because this is recursive, and auto can't work out the type without seeing the + // whole thing. So, we'll resort to an explicit type via std::function. + const std::function + traverse = [&](TIntermTyped* left, TIntermTyped* right, TIntermTyped* splitLeft, TIntermTyped* splitRight, + bool topLevel) -> void { + // If we get here, we are assigning to or from a whole array or struct that must be + // flattened, so have to do member-by-member assignment: + + bool shouldFlattenSubsetLeft = isFlattenLeft && shouldFlatten(left->getType(), leftStorage, topLevel); + bool shouldFlattenSubsetRight = isFlattenRight && shouldFlatten(right->getType(), rightStorage, topLevel); + + if ((left->getType().isArray() || right->getType().isArray()) && + (shouldFlattenSubsetLeft || isSplitLeft || + shouldFlattenSubsetRight || isSplitRight)) { + const int elementsL = left->getType().isArray() ? left->getType().getOuterArraySize() : 1; + const int elementsR = right->getType().isArray() ? right->getType().getOuterArraySize() : 1; + + // The arrays might not be the same size, + // e.g., if the size has been forced for EbvTessLevelInner/Outer. + const int elementsToCopy = std::min(elementsL, elementsR); + + // array case + for (int element = 0; element < elementsToCopy; ++element) { + arrayElement.push_back(element); + + // Add a new AST symbol node if we have a temp variable holding a complex RHS. + TIntermTyped* subLeft = getMember(true, left->getType(), element, left, element, + shouldFlattenSubsetLeft); + TIntermTyped* subRight = getMember(false, right->getType(), element, right, element, + shouldFlattenSubsetRight); + + TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left->getType(), element, splitLeft, + element, shouldFlattenSubsetLeft) + : subLeft; + TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), element, splitRight, + element, shouldFlattenSubsetRight) + : subRight; + + traverse(subLeft, subRight, subSplitLeft, subSplitRight, false); + + arrayElement.pop_back(); + } + } else if (left->getType().isStruct() && (shouldFlattenSubsetLeft || isSplitLeft || + shouldFlattenSubsetRight || isSplitRight)) { + // struct case + const auto& membersL = *left->getType().getStruct(); + const auto& membersR = *right->getType().getStruct(); + + // These track the members in the split structures corresponding to the same in the unsplit structures, + // which we traverse in parallel. + int memberL = 0; + int memberR = 0; + + // Handle empty structure assignment + if (int(membersL.size()) == 0 && int(membersR.size()) == 0) + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc); + + for (int member = 0; member < int(membersL.size()); ++member) { + const TType& typeL = *membersL[member].type; + const TType& typeR = *membersR[member].type; + + TIntermTyped* subLeft = getMember(true, left->getType(), member, left, member, + shouldFlattenSubsetLeft); + TIntermTyped* subRight = getMember(false, right->getType(), member, right, member, + shouldFlattenSubsetRight); + + // If there is no splitting, use the same values to avoid inefficiency. + TIntermTyped* subSplitLeft = isSplitLeft ? getMember(true, left->getType(), member, splitLeft, + memberL, shouldFlattenSubsetLeft) + : subLeft; + TIntermTyped* subSplitRight = isSplitRight ? getMember(false, right->getType(), member, splitRight, + memberR, shouldFlattenSubsetRight) + : subRight; + + if (isClipOrCullDistance(subSplitLeft->getType()) || isClipOrCullDistance(subSplitRight->getType())) { + // Clip and cull distance built-in assignment is complex in its own right, and is handled in + // a separate function dedicated to that task. See comment above assignClipCullDistance; + + const bool isOutput = isClipOrCullDistance(subSplitLeft->getType()); + + // Since all clip/cull semantics boil down to the same built-in type, we need to get the + // semantic ID from the dereferenced type's layout location, to avoid an N-1 mapping. + const TType derefType((isOutput ? left : right)->getType(), member); + const int semanticId = derefType.getQualifier().layoutLocation; + + TIntermAggregate* clipCullAssign = assignClipCullDistance(loc, op, semanticId, + subSplitLeft, subSplitRight); + + assignList = intermediate.growAggregate(assignList, clipCullAssign, loc); + } else if (assignsClipPos(subSplitLeft)) { + // Position can require special handling: see comment above assignPosition + TIntermTyped* positionAssign = assignPosition(loc, op, subSplitLeft, subSplitRight); + assignList = intermediate.growAggregate(assignList, positionAssign, loc); + } else if (!shouldFlattenSubsetLeft && !shouldFlattenSubsetRight && + !typeL.containsBuiltIn() && !typeR.containsBuiltIn()) { + // If this is the final flattening (no nested types below to flatten) + // we'll copy the member, else recurse into the type hierarchy. + // However, if splitting the struct, that means we can copy a whole + // subtree here IFF it does not itself contain any interstage built-in + // IO variables, so we only have to recurse into it if there's something + // for splitting to do. That can save a lot of AST verbosity for + // a bunch of memberwise copies. + + assignList = intermediate.growAggregate(assignList, + intermediate.addAssign(op, subSplitLeft, subSplitRight, loc), + loc); + } else { + traverse(subLeft, subRight, subSplitLeft, subSplitRight, false); + } + + memberL += (typeL.isBuiltIn() ? 0 : 1); + memberR += (typeR.isBuiltIn() ? 0 : 1); + } + } else { + // Member copy + assignList = intermediate.growAggregate(assignList, intermediate.addAssign(op, left, right, loc), loc); + } + + }; + + TIntermTyped* splitLeft = left; + TIntermTyped* splitRight = right; + + // If either left or right was a split structure, we must read or write it, but still have to + // parallel-recurse through the unsplit structure to identify the built-in IO vars. + // The left can be either a symbol, or an index into a symbol (e.g, array reference) + if (isSplitLeft) { + if (indexesSplit(left)) { + // Index case: Refer to the indexed symbol, if the left is an index operator. + const TIntermSymbol* symNode = left->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + + TIntermTyped* splitLeftNonIo = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc); + + splitLeft = intermediate.addIndex(left->getAsBinaryNode()->getOp(), splitLeftNonIo, + left->getAsBinaryNode()->getRight(), loc); + + const TType derefType(splitLeftNonIo->getType(), 0); + splitLeft->setType(derefType); + } else { + // Symbol case: otherwise, if not indexed, we have the symbol directly. + const TIntermSymbol* symNode = left->getAsSymbolNode(); + splitLeft = intermediate.addSymbol(*getSplitNonIoVar(symNode->getId()), loc); + } + } + + if (isSplitRight) + splitRight = intermediate.addSymbol(*getSplitNonIoVar(right->getAsSymbolNode()->getId()), loc); + + // This makes the whole assignment, recursing through subtypes as needed. + traverse(left, right, splitLeft, splitRight, true); + + assert(assignList != nullptr); + assignList->setOperator(EOpSequence); + + return assignList; +} + +// An assignment to matrix swizzle must be decomposed into individual assignments. +// These must be selected component-wise from the RHS and stored component-wise +// into the LHS. +TIntermTyped* HlslParseContext::handleAssignToMatrixSwizzle(const TSourceLoc& loc, TOperator op, TIntermTyped* left, + TIntermTyped* right) +{ + assert(left->getAsOperator() && left->getAsOperator()->getOp() == EOpMatrixSwizzle); + + if (op != EOpAssign) + error(loc, "only simple assignment to non-simple matrix swizzle is supported", "assign", ""); + + // isolate the matrix and swizzle nodes + TIntermTyped* matrix = left->getAsBinaryNode()->getLeft()->getAsTyped(); + const TIntermSequence& swizzle = left->getAsBinaryNode()->getRight()->getAsAggregate()->getSequence(); + + // if the RHS isn't already a simple vector, let's store into one + TIntermSymbol* vector = right->getAsSymbolNode(); + TIntermTyped* vectorAssign = nullptr; + if (vector == nullptr) { + // create a new intermediate vector variable to assign to + TType vectorType(matrix->getBasicType(), EvqTemporary, matrix->getQualifier().precision, (int)swizzle.size()/2); + vector = intermediate.addSymbol(*makeInternalVariable("intermVec", vectorType), loc); + + // assign the right to the new vector + vectorAssign = handleAssign(loc, op, vector, right); + } + + // Assign the vector components to the matrix components. + // Store this as a sequence, so a single aggregate node represents this + // entire operation. + TIntermAggregate* result = intermediate.makeAggregate(vectorAssign); + TType columnType(matrix->getType(), 0); + TType componentType(columnType, 0); + TType indexType(EbtInt); + for (int i = 0; i < (int)swizzle.size(); i += 2) { + // the right component, single index into the RHS vector + TIntermTyped* rightComp = intermediate.addIndex(EOpIndexDirect, vector, + intermediate.addConstantUnion(i/2, loc), loc); + + // the left component, double index into the LHS matrix + TIntermTyped* leftComp = intermediate.addIndex(EOpIndexDirect, matrix, + intermediate.addConstantUnion(swizzle[i]->getAsConstantUnion()->getConstArray(), + indexType, loc), + loc); + leftComp->setType(columnType); + leftComp = intermediate.addIndex(EOpIndexDirect, leftComp, + intermediate.addConstantUnion(swizzle[i+1]->getAsConstantUnion()->getConstArray(), + indexType, loc), + loc); + leftComp->setType(componentType); + + // Add the assignment to the aggregate + result = intermediate.growAggregate(result, intermediate.addAssign(op, leftComp, rightComp, loc)); + } + + result->setOp(EOpSequence); + + return result; +} + +// +// HLSL atomic operations have slightly different arguments than +// GLSL/AST/SPIRV. The semantics are converted below in decomposeIntrinsic. +// This provides the post-decomposition equivalent opcode. +// +TOperator HlslParseContext::mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage) +{ + switch (op) { + case EOpInterlockedAdd: return isImage ? EOpImageAtomicAdd : EOpAtomicAdd; + case EOpInterlockedAnd: return isImage ? EOpImageAtomicAnd : EOpAtomicAnd; + case EOpInterlockedCompareExchange: return isImage ? EOpImageAtomicCompSwap : EOpAtomicCompSwap; + case EOpInterlockedMax: return isImage ? EOpImageAtomicMax : EOpAtomicMax; + case EOpInterlockedMin: return isImage ? EOpImageAtomicMin : EOpAtomicMin; + case EOpInterlockedOr: return isImage ? EOpImageAtomicOr : EOpAtomicOr; + case EOpInterlockedXor: return isImage ? EOpImageAtomicXor : EOpAtomicXor; + case EOpInterlockedExchange: return isImage ? EOpImageAtomicExchange : EOpAtomicExchange; + case EOpInterlockedCompareStore: // TODO: ... + default: + error(loc, "unknown atomic operation", "unknown op", ""); + return EOpNull; + } +} + +// +// Create a combined sampler/texture from separate sampler and texture. +// +TIntermAggregate* HlslParseContext::handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, + TIntermTyped* argSampler) +{ + TIntermAggregate* txcombine = new TIntermAggregate(EOpConstructTextureSampler); + + txcombine->getSequence().push_back(argTex); + txcombine->getSequence().push_back(argSampler); + + TSampler samplerType = argTex->getType().getSampler(); + samplerType.combined = true; + + // TODO: + // This block exists until the spec no longer requires shadow modes on texture objects. + // It can be deleted after that, along with the shadowTextureVariant member. + { + const bool shadowMode = argSampler->getType().getSampler().shadow; + + TIntermSymbol* texSymbol = argTex->getAsSymbolNode(); + + if (texSymbol == nullptr) + texSymbol = argTex->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + + if (texSymbol == nullptr) { + error(loc, "unable to find texture symbol", "", ""); + return nullptr; + } + + // This forces the texture's shadow state to be the sampler's + // shadow state. This depends on downstream optimization to + // DCE one variant in [shadow, nonshadow] if both are present, + // or the SPIR-V module would be invalid. + int newId = texSymbol->getId(); + + // Check to see if this texture has been given a shadow mode already. + // If so, look up the one we already have. + const auto textureShadowEntry = textureShadowVariant.find(texSymbol->getId()); + + if (textureShadowEntry != textureShadowVariant.end()) + newId = textureShadowEntry->second->get(shadowMode); + else + textureShadowVariant[texSymbol->getId()] = NewPoolObject(tShadowTextureSymbols(), 1); + + // Sometimes we have to create another symbol (if this texture has been seen before, + // and we haven't created the form for this shadow mode). + if (newId == -1) { + TType texType; + texType.shallowCopy(argTex->getType()); + texType.getSampler().shadow = shadowMode; // set appropriate shadow mode. + globalQualifierFix(loc, texType.getQualifier()); + + TVariable* newTexture = makeInternalVariable(texSymbol->getName(), texType); + + trackLinkage(*newTexture); + + newId = newTexture->getUniqueId(); + } + + assert(newId != -1); + + if (textureShadowVariant.find(newId) == textureShadowVariant.end()) + textureShadowVariant[newId] = textureShadowVariant[texSymbol->getId()]; + + textureShadowVariant[newId]->set(shadowMode, newId); + + // Remember this shadow mode in the texture and the merged type. + argTex->getWritableType().getSampler().shadow = shadowMode; + samplerType.shadow = shadowMode; + + texSymbol->switchId(newId); + } + + txcombine->setType(TType(samplerType, EvqTemporary)); + txcombine->setLoc(loc); + + return txcombine; +} + +// Return true if this a buffer type that has an associated counter buffer. +bool HlslParseContext::hasStructBuffCounter(const TType& type) const +{ + switch (type.getQualifier().declaredBuiltIn) { + case EbvAppendConsume: // fall through... + case EbvRWStructuredBuffer: // ... + return true; + default: + return false; // the other structuredbuffer types do not have a counter. + } +} + +void HlslParseContext::counterBufferType(const TSourceLoc& loc, TType& type) +{ + // Counter type + TType* counterType = new TType(EbtUint, EvqBuffer); + counterType->setFieldName(intermediate.implicitCounterName); + + TTypeList* blockStruct = new TTypeList; + TTypeLoc member = { counterType, loc }; + blockStruct->push_back(member); + + TType blockType(blockStruct, "", counterType->getQualifier()); + blockType.getQualifier().storage = EvqBuffer; + + type.shallowCopy(blockType); + shareStructBufferType(type); +} + +// declare counter for a structured buffer type +void HlslParseContext::declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name) +{ + // Bail out if not a struct buffer + if (! isStructBufferType(bufferType)) + return; + + if (! hasStructBuffCounter(bufferType)) + return; + + TType blockType; + counterBufferType(loc, blockType); + + TString* blockName = NewPoolTString(intermediate.addCounterBufferName(name).c_str()); + + // Counter buffer is not yet in use + structBufferCounter[*blockName] = false; + + shareStructBufferType(blockType); + declareBlock(loc, blockType, blockName); +} + +// return the counter that goes with a given structuredbuffer +TIntermTyped* HlslParseContext::getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer) +{ + // Bail out if not a struct buffer + if (buffer == nullptr || ! isStructBufferType(buffer->getType())) + return nullptr; + + const TString counterBlockName(intermediate.addCounterBufferName(buffer->getAsSymbolNode()->getName())); + + // Mark the counter as being used + structBufferCounter[counterBlockName] = true; + + TIntermTyped* counterVar = handleVariable(loc, &counterBlockName); // find the block structure + TIntermTyped* index = intermediate.addConstantUnion(0, loc); // index to counter inside block struct + + TIntermTyped* counterMember = intermediate.addIndex(EOpIndexDirectStruct, counterVar, index, loc); + counterMember->setType(TType(EbtUint)); + return counterMember; +} + +// +// Decompose structure buffer methods into AST +// +void HlslParseContext::decomposeStructBufferMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) +{ + if (node == nullptr || node->getAsOperator() == nullptr || arguments == nullptr) + return; + + const TOperator op = node->getAsOperator()->getOp(); + TIntermAggregate* argAggregate = arguments->getAsAggregate(); + + // Buffer is the object upon which method is called, so always arg 0 + TIntermTyped* bufferObj = nullptr; + + // The parameters can be an aggregate, or just a the object as a symbol if there are no fn params. + if (argAggregate) { + if (argAggregate->getSequence().empty()) + return; + if (argAggregate->getSequence()[0]) + bufferObj = argAggregate->getSequence()[0]->getAsTyped(); + } else { + bufferObj = arguments->getAsSymbolNode(); + } + + if (bufferObj == nullptr || bufferObj->getAsSymbolNode() == nullptr) + return; + + // Some methods require a hidden internal counter, obtained via getStructBufferCounter(). + // This lambda adds something to it and returns the old value. + const auto incDecCounter = [&](int incval) -> TIntermTyped* { + TIntermTyped* incrementValue = intermediate.addConstantUnion(static_cast(incval), loc, true); + TIntermTyped* counter = getStructBufferCounter(loc, bufferObj); // obtain the counter member + + if (counter == nullptr) + return nullptr; + + TIntermAggregate* counterIncrement = new TIntermAggregate(EOpAtomicAdd); + counterIncrement->setType(TType(EbtUint, EvqTemporary)); + counterIncrement->setLoc(loc); + counterIncrement->getSequence().push_back(counter); + counterIncrement->getSequence().push_back(incrementValue); + + return counterIncrement; + }; + + // Index to obtain the runtime sized array out of the buffer. + TIntermTyped* argArray = indexStructBufferContent(loc, bufferObj); + if (argArray == nullptr) + return; // It might not be a struct buffer method. + + switch (op) { + case EOpMethodLoad: + { + TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index + + const TType& bufferType = bufferObj->getType(); + + const TBuiltInVariable builtInType = bufferType.getQualifier().declaredBuiltIn; + + // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address + // buffer then, but that's what it calls itself. + const bool isByteAddressBuffer = (builtInType == EbvByteAddressBuffer || + builtInType == EbvRWByteAddressBuffer); + + + if (isByteAddressBuffer) + argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, + intermediate.addConstantUnion(2, loc, true), + loc, TType(EbtInt)); + + // Index into the array to find the item being loaded. + const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; + + node = intermediate.addIndex(idxOp, argArray, argIndex, loc); + + const TType derefType(argArray->getType(), 0); + node->setType(derefType); + } + + break; + + case EOpMethodLoad2: + case EOpMethodLoad3: + case EOpMethodLoad4: + { + TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index + + TOperator constructOp = EOpNull; + int size = 0; + + switch (op) { + case EOpMethodLoad2: size = 2; constructOp = EOpConstructVec2; break; + case EOpMethodLoad3: size = 3; constructOp = EOpConstructVec3; break; + case EOpMethodLoad4: size = 4; constructOp = EOpConstructVec4; break; + default: assert(0); + } + + TIntermTyped* body = nullptr; + + // First, we'll store the address in a variable to avoid multiple shifts + // (we must convert the byte address to an item address) + TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex, + intermediate.addConstantUnion(2, loc, true), + loc, TType(EbtInt)); + + TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary)); + TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc); + + body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc)); + + TIntermTyped* vec = nullptr; + + // These are only valid on (rw)byteaddressbuffers, so we can always perform the >>2 + // address conversion. + for (int idx=0; idxgetQualifier().storage == EvqConst) ? EOpIndexDirect + : EOpIndexIndirect; + + TIntermTyped* indexVal = intermediate.addIndex(idxOp, argArray, offsetIdx, loc); + + TType derefType(argArray->getType(), 0); + derefType.getQualifier().makeTemporary(); + indexVal->setType(derefType); + + vec = intermediate.growAggregate(vec, indexVal); + } + + vec->setType(TType(argArray->getBasicType(), EvqTemporary, size)); + vec->getAsAggregate()->setOperator(constructOp); + + body = intermediate.growAggregate(body, vec); + body->setType(vec->getType()); + body->getAsAggregate()->setOperator(EOpSequence); + + node = body; + } + + break; + + case EOpMethodStore: + case EOpMethodStore2: + case EOpMethodStore3: + case EOpMethodStore4: + { + TIntermTyped* argIndex = makeIntegerIndex(argAggregate->getSequence()[1]->getAsTyped()); // index + TIntermTyped* argValue = argAggregate->getSequence()[2]->getAsTyped(); // value + + // Index into the array to find the item being loaded. + // Byte address buffers index in bytes (only multiples of 4 permitted... not so much a byte address + // buffer then, but that's what it calls itself). + + int size = 0; + + switch (op) { + case EOpMethodStore: size = 1; break; + case EOpMethodStore2: size = 2; break; + case EOpMethodStore3: size = 3; break; + case EOpMethodStore4: size = 4; break; + default: assert(0); + } + + TIntermAggregate* body = nullptr; + + // First, we'll store the address in a variable to avoid multiple shifts + // (we must convert the byte address to an item address) + TIntermTyped* byteAddrIdx = intermediate.addBinaryNode(EOpRightShift, argIndex, + intermediate.addConstantUnion(2, loc, true), loc, TType(EbtInt)); + + TVariable* byteAddrSym = makeInternalVariable("byteAddrTemp", TType(EbtInt, EvqTemporary)); + TIntermTyped* byteAddrIdxVar = intermediate.addSymbol(*byteAddrSym, loc); + + body = intermediate.growAggregate(body, intermediate.addAssign(EOpAssign, byteAddrIdxVar, byteAddrIdx, loc)); + + for (int idx=0; idxgetQualifier().storage == EvqConst) ? EOpIndexDirect + : EOpIndexIndirect; + + TIntermTyped* lValue = intermediate.addIndex(idxOp, argArray, offsetIdx, loc); + const TType derefType(argArray->getType(), 0); + lValue->setType(derefType); + + TIntermTyped* rValue; + if (size == 1) { + rValue = argValue; + } else { + rValue = intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc); + const TType indexType(argValue->getType(), 0); + rValue->setType(indexType); + } + + TIntermTyped* assign = intermediate.addAssign(EOpAssign, lValue, rValue, loc); + + body = intermediate.growAggregate(body, assign); + } + + body->setOperator(EOpSequence); + node = body; + } + + break; + + case EOpMethodGetDimensions: + { + const int numArgs = (int)argAggregate->getSequence().size(); + TIntermTyped* argNumItems = argAggregate->getSequence()[1]->getAsTyped(); // out num items + TIntermTyped* argStride = numArgs > 2 ? argAggregate->getSequence()[2]->getAsTyped() : nullptr; // out stride + + TIntermAggregate* body = nullptr; + + // Length output: + if (argArray->getType().isSizedArray()) { + const int length = argArray->getType().getOuterArraySize(); + TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, + intermediate.addConstantUnion(length, loc, true), loc); + body = intermediate.growAggregate(body, assign, loc); + } else { + TIntermTyped* lengthCall = intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, argArray, + argNumItems->getType()); + TIntermTyped* assign = intermediate.addAssign(EOpAssign, argNumItems, lengthCall, loc); + body = intermediate.growAggregate(body, assign, loc); + } + + // Stride output: + if (argStride != nullptr) { + int size; + int stride; + intermediate.getMemberAlignment(argArray->getType(), size, stride, argArray->getType().getQualifier().layoutPacking, + argArray->getType().getQualifier().layoutMatrix == ElmRowMajor); + + TIntermTyped* assign = intermediate.addAssign(EOpAssign, argStride, + intermediate.addConstantUnion(stride, loc, true), loc); + + body = intermediate.growAggregate(body, assign); + } + + body->setOperator(EOpSequence); + node = body; + } + + break; + + case EOpInterlockedAdd: + case EOpInterlockedAnd: + case EOpInterlockedExchange: + case EOpInterlockedMax: + case EOpInterlockedMin: + case EOpInterlockedOr: + case EOpInterlockedXor: + case EOpInterlockedCompareExchange: + case EOpInterlockedCompareStore: + { + // We'll replace the first argument with the block dereference, and let + // downstream decomposition handle the rest. + + TIntermSequence& sequence = argAggregate->getSequence(); + + TIntermTyped* argIndex = makeIntegerIndex(sequence[1]->getAsTyped()); // index + argIndex = intermediate.addBinaryNode(EOpRightShift, argIndex, intermediate.addConstantUnion(2, loc, true), + loc, TType(EbtInt)); + + const TOperator idxOp = (argIndex->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; + TIntermTyped* element = intermediate.addIndex(idxOp, argArray, argIndex, loc); + + const TType derefType(argArray->getType(), 0); + element->setType(derefType); + + // Replace the numeric byte offset parameter with array reference. + sequence[1] = element; + sequence.erase(sequence.begin(), sequence.begin()+1); + } + break; + + case EOpMethodIncrementCounter: + { + node = incDecCounter(1); + break; + } + + case EOpMethodDecrementCounter: + { + TIntermTyped* preIncValue = incDecCounter(-1); // result is original value + node = intermediate.addBinaryNode(EOpAdd, preIncValue, intermediate.addConstantUnion(-1, loc, true), loc, + preIncValue->getType()); + break; + } + + case EOpMethodAppend: + { + TIntermTyped* oldCounter = incDecCounter(1); + + TIntermTyped* lValue = intermediate.addIndex(EOpIndexIndirect, argArray, oldCounter, loc); + TIntermTyped* rValue = argAggregate->getSequence()[1]->getAsTyped(); + + const TType derefType(argArray->getType(), 0); + lValue->setType(derefType); + + node = intermediate.addAssign(EOpAssign, lValue, rValue, loc); + + break; + } + + case EOpMethodConsume: + { + TIntermTyped* oldCounter = incDecCounter(-1); + + TIntermTyped* newCounter = intermediate.addBinaryNode(EOpAdd, oldCounter, + intermediate.addConstantUnion(-1, loc, true), loc, + oldCounter->getType()); + + node = intermediate.addIndex(EOpIndexIndirect, argArray, newCounter, loc); + + const TType derefType(argArray->getType(), 0); + node->setType(derefType); + + break; + } + + default: + break; // most pass through unchanged + } +} + +// Create array of standard sample positions for given sample count. +// TODO: remove when a real method to query sample pos exists in SPIR-V. +TIntermConstantUnion* HlslParseContext::getSamplePosArray(int count) +{ + struct tSamplePos { float x, y; }; + + static const tSamplePos pos1[] = { + { 0.0/16.0, 0.0/16.0 }, + }; + + // standard sample positions for 2, 4, 8, and 16 samples. + static const tSamplePos pos2[] = { + { 4.0/16.0, 4.0/16.0 }, {-4.0/16.0, -4.0/16.0 }, + }; + + static const tSamplePos pos4[] = { + {-2.0/16.0, -6.0/16.0 }, { 6.0/16.0, -2.0/16.0 }, {-6.0/16.0, 2.0/16.0 }, { 2.0/16.0, 6.0/16.0 }, + }; + + static const tSamplePos pos8[] = { + { 1.0/16.0, -3.0/16.0 }, {-1.0/16.0, 3.0/16.0 }, { 5.0/16.0, 1.0/16.0 }, {-3.0/16.0, -5.0/16.0 }, + {-5.0/16.0, 5.0/16.0 }, {-7.0/16.0, -1.0/16.0 }, { 3.0/16.0, 7.0/16.0 }, { 7.0/16.0, -7.0/16.0 }, + }; + + static const tSamplePos pos16[] = { + { 1.0/16.0, 1.0/16.0 }, {-1.0/16.0, -3.0/16.0 }, {-3.0/16.0, 2.0/16.0 }, { 4.0/16.0, -1.0/16.0 }, + {-5.0/16.0, -2.0/16.0 }, { 2.0/16.0, 5.0/16.0 }, { 5.0/16.0, 3.0/16.0 }, { 3.0/16.0, -5.0/16.0 }, + {-2.0/16.0, 6.0/16.0 }, { 0.0/16.0, -7.0/16.0 }, {-4.0/16.0, -6.0/16.0 }, {-6.0/16.0, 4.0/16.0 }, + {-8.0/16.0, 0.0/16.0 }, { 7.0/16.0, -4.0/16.0 }, { 6.0/16.0, 7.0/16.0 }, {-7.0/16.0, -8.0/16.0 }, + }; + + const tSamplePos* sampleLoc = nullptr; + int numSamples = count; + + switch (count) { + case 2: sampleLoc = pos2; break; + case 4: sampleLoc = pos4; break; + case 8: sampleLoc = pos8; break; + case 16: sampleLoc = pos16; break; + default: + sampleLoc = pos1; + numSamples = 1; + } + + TConstUnionArray* values = new TConstUnionArray(numSamples*2); + + for (int pos=0; posaddInnerSize(numSamples); + retType.transferArraySizes(arraySizes); + } + + return new TIntermConstantUnion(*values, retType); +} + +// +// Decompose DX9 and DX10 sample intrinsics & object methods into AST +// +void HlslParseContext::decomposeSampleMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) +{ + if (node == nullptr || !node->getAsOperator()) + return; + + // Sampler return must always be a vec4, but we can construct a shorter vector or a structure from it. + const auto convertReturn = [&loc, &node, this](TIntermTyped* result, const TSampler& sampler) -> TIntermTyped* { + result->setType(TType(node->getType().getBasicType(), EvqTemporary, node->getVectorSize())); + + TIntermTyped* convertedResult = nullptr; + + TType retType; + getTextureReturnType(sampler, retType); + + if (retType.isStruct()) { + // For type convenience, conversionAggregate points to the convertedResult (we know it's an aggregate here) + TIntermAggregate* conversionAggregate = new TIntermAggregate; + convertedResult = conversionAggregate; + + // Convert vector output to return structure. We will need a temp symbol to copy the results to. + TVariable* structVar = makeInternalVariable("@sampleStructTemp", retType); + + // We also need a temp symbol to hold the result of the texture. We don't want to re-fetch the + // sample each time we'll index into the result, so we'll copy to this, and index into the copy. + TVariable* sampleShadow = makeInternalVariable("@sampleResultShadow", result->getType()); + + // Initial copy from texture to our sample result shadow. + TIntermTyped* shadowCopy = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*sampleShadow, loc), + result, loc); + + conversionAggregate->getSequence().push_back(shadowCopy); + + unsigned vec4Pos = 0; + + for (unsigned m = 0; m < unsigned(retType.getStruct()->size()); ++m) { + const TType memberType(retType, m); // dereferenced type of the member we're about to assign. + + // Check for bad struct members. This should have been caught upstream. Complain, because + // wwe don't know what to do with it. This algorithm could be generalized to handle + // other things, e.g, sub-structures, but HLSL doesn't allow them. + if (!memberType.isVector() && !memberType.isScalar()) { + error(loc, "expected: scalar or vector type in texture structure", "", ""); + return nullptr; + } + + // Index into the struct variable to find the member to assign. + TIntermTyped* structMember = intermediate.addIndex(EOpIndexDirectStruct, + intermediate.addSymbol(*structVar, loc), + intermediate.addConstantUnion(m, loc), loc); + + structMember->setType(memberType); + + // Assign each component of (possible) vector in struct member. + for (int component = 0; component < memberType.getVectorSize(); ++component) { + TIntermTyped* vec4Member = intermediate.addIndex(EOpIndexDirect, + intermediate.addSymbol(*sampleShadow, loc), + intermediate.addConstantUnion(vec4Pos++, loc), loc); + vec4Member->setType(TType(memberType.getBasicType(), EvqTemporary, 1)); + + TIntermTyped* memberAssign = nullptr; + + if (memberType.isVector()) { + // Vector member: we need to create an access chain to the vector component. + + TIntermTyped* structVecComponent = intermediate.addIndex(EOpIndexDirect, structMember, + intermediate.addConstantUnion(component, loc), loc); + + memberAssign = intermediate.addAssign(EOpAssign, structVecComponent, vec4Member, loc); + } else { + // Scalar member: we can assign to it directly. + memberAssign = intermediate.addAssign(EOpAssign, structMember, vec4Member, loc); + } + + + conversionAggregate->getSequence().push_back(memberAssign); + } + } + + // Add completed variable so the expression results in the whole struct value we just built. + conversionAggregate->getSequence().push_back(intermediate.addSymbol(*structVar, loc)); + + // Make it a sequence. + intermediate.setAggregateOperator(conversionAggregate, EOpSequence, retType, loc); + } else { + // vector clamp the output if template vector type is smaller than sample result. + if (retType.getVectorSize() < node->getVectorSize()) { + // Too many components. Construct shorter vector from it. + const TOperator op = intermediate.mapTypeToConstructorOp(retType); + + convertedResult = constructBuiltIn(retType, op, result, loc, false); + } else { + // Enough components. Use directly. + convertedResult = result; + } + } + + convertedResult->setLoc(loc); + return convertedResult; + }; + + const TOperator op = node->getAsOperator()->getOp(); + const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; + + // Bail out if not a sampler method. + // Note though this is odd to do before checking the op, because the op + // could be something that takes the arguments, and the function in question + // takes the result of the op. So, this is not the final word. + if (arguments != nullptr) { + if (argAggregate == nullptr) { + if (arguments->getAsTyped()->getBasicType() != EbtSampler) + return; + } else { + if (argAggregate->getSequence().size() == 0 || + argAggregate->getSequence()[0] == nullptr || + argAggregate->getSequence()[0]->getAsTyped()->getBasicType() != EbtSampler) + return; + } + } + + switch (op) { + // **** DX9 intrinsics: **** + case EOpTexture: + { + // Texture with ddx & ddy is really gradient form in HLSL + if (argAggregate->getSequence().size() == 4) + node->getAsAggregate()->setOperator(EOpTextureGrad); + + break; + } + case EOpTextureLod: //is almost EOpTextureBias (only args & operations are different) + { + TIntermTyped *argSamp = argAggregate->getSequence()[0]->getAsTyped(); // sampler + TIntermTyped *argCoord = argAggregate->getSequence()[1]->getAsTyped(); // coord + + assert(argCoord->getVectorSize() == 4); + TIntermTyped *w = intermediate.addConstantUnion(3, loc, true); + TIntermTyped *argLod = intermediate.addIndex(EOpIndexDirect, argCoord, w, loc); + + TOperator constructOp = EOpNull; + const TSampler &sampler = argSamp->getType().getSampler(); + int coordSize = 0; + + switch (sampler.dim) + { + case Esd1D: constructOp = EOpConstructFloat; coordSize = 1; break; // 1D + case Esd2D: constructOp = EOpConstructVec2; coordSize = 2; break; // 2D + case Esd3D: constructOp = EOpConstructVec3; coordSize = 3; break; // 3D + case EsdCube: constructOp = EOpConstructVec3; coordSize = 3; break; // also 3D + default: + error(loc, "unhandled DX9 texture LoD dimension", "", ""); + break; + } + + TIntermAggregate *constructCoord = new TIntermAggregate(constructOp); + constructCoord->getSequence().push_back(argCoord); + constructCoord->setLoc(loc); + constructCoord->setType(TType(argCoord->getBasicType(), EvqTemporary, coordSize)); + + TIntermAggregate *tex = new TIntermAggregate(EOpTextureLod); + tex->getSequence().push_back(argSamp); // sampler + tex->getSequence().push_back(constructCoord); // coordinate + tex->getSequence().push_back(argLod); // lod + + node = convertReturn(tex, sampler); + + break; + } + + case EOpTextureBias: + { + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // sampler + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // coord + + // HLSL puts bias in W component of coordinate. We extract it and add it to + // the argument list, instead + TIntermTyped* w = intermediate.addConstantUnion(3, loc, true); + TIntermTyped* bias = intermediate.addIndex(EOpIndexDirect, arg1, w, loc); + + TOperator constructOp = EOpNull; + const TSampler& sampler = arg0->getType().getSampler(); + + switch (sampler.dim) { + case Esd1D: constructOp = EOpConstructFloat; break; // 1D + case Esd2D: constructOp = EOpConstructVec2; break; // 2D + case Esd3D: constructOp = EOpConstructVec3; break; // 3D + case EsdCube: constructOp = EOpConstructVec3; break; // also 3D + default: + error(loc, "unhandled DX9 texture bias dimension", "", ""); + break; + } + + TIntermAggregate* constructCoord = new TIntermAggregate(constructOp); + constructCoord->getSequence().push_back(arg1); + constructCoord->setLoc(loc); + + // The input vector should never be less than 2, since there's always a bias. + // The max is for safety, and should be a no-op. + constructCoord->setType(TType(arg1->getBasicType(), EvqTemporary, std::max(arg1->getVectorSize() - 1, 0))); + + TIntermAggregate* tex = new TIntermAggregate(EOpTexture); + tex->getSequence().push_back(arg0); // sampler + tex->getSequence().push_back(constructCoord); // coordinate + tex->getSequence().push_back(bias); // bias + + node = convertReturn(tex, sampler); + + break; + } + + // **** DX10 methods: **** + case EOpMethodSample: // fall through + case EOpMethodSampleBias: // ... + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argBias = nullptr; + TIntermTyped* argOffset = nullptr; + const TSampler& sampler = argTex->getType().getSampler(); + + int nextArg = 3; + + if (op == EOpMethodSampleBias) // SampleBias has a bias arg + argBias = argAggregate->getSequence()[nextArg++]->getAsTyped(); + + TOperator textureOp = EOpTexture; + + if ((int)argAggregate->getSequence().size() == (nextArg+1)) { // last parameter is offset form + textureOp = EOpTextureOffset; + argOffset = argAggregate->getSequence()[nextArg++]->getAsTyped(); + } + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + TIntermAggregate* txsample = new TIntermAggregate(textureOp); + txsample->getSequence().push_back(txcombine); + txsample->getSequence().push_back(argCoord); + + if (argBias != nullptr) + txsample->getSequence().push_back(argBias); + + if (argOffset != nullptr) + txsample->getSequence().push_back(argOffset); + + node = convertReturn(txsample, sampler); + + break; + } + + case EOpMethodSampleGrad: // ... + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argDDX = argAggregate->getSequence()[3]->getAsTyped(); + TIntermTyped* argDDY = argAggregate->getSequence()[4]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + const TSampler& sampler = argTex->getType().getSampler(); + + TOperator textureOp = EOpTextureGrad; + + if (argAggregate->getSequence().size() == 6) { // last parameter is offset form + textureOp = EOpTextureGradOffset; + argOffset = argAggregate->getSequence()[5]->getAsTyped(); + } + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + TIntermAggregate* txsample = new TIntermAggregate(textureOp); + txsample->getSequence().push_back(txcombine); + txsample->getSequence().push_back(argCoord); + txsample->getSequence().push_back(argDDX); + txsample->getSequence().push_back(argDDY); + + if (argOffset != nullptr) + txsample->getSequence().push_back(argOffset); + + node = convertReturn(txsample, sampler); + + break; + } + + case EOpMethodGetDimensions: + { + // AST returns a vector of results, which we break apart component-wise into + // separate values to assign to the HLSL method's outputs, ala: + // tx . GetDimensions(width, height); + // float2 sizeQueryTemp = EOpTextureQuerySize + // width = sizeQueryTemp.X; + // height = sizeQueryTemp.Y; + + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + const TType& texType = argTex->getType(); + + assert(texType.getBasicType() == EbtSampler); + + const TSampler& sampler = texType.getSampler(); + const TSamplerDim dim = sampler.dim; + const bool isImage = sampler.isImage(); + const bool isMs = sampler.isMultiSample(); + const int numArgs = (int)argAggregate->getSequence().size(); + + int numDims = 0; + + switch (dim) { + case Esd1D: numDims = 1; break; // W + case Esd2D: numDims = 2; break; // W, H + case Esd3D: numDims = 3; break; // W, H, D + case EsdCube: numDims = 2; break; // W, H (cube) + case EsdBuffer: numDims = 1; break; // W (buffers) + case EsdRect: numDims = 2; break; // W, H (rect) + default: + error(loc, "unhandled DX10 MethodGet dimension", "", ""); + break; + } + + // Arrayed adds another dimension for the number of array elements + if (sampler.isArrayed()) + ++numDims; + + // Establish whether the method itself is querying mip levels. This can be false even + // if the underlying query requires a MIP level, due to the available HLSL method overloads. + const bool mipQuery = (numArgs > (numDims + 1 + (isMs ? 1 : 0))); + + // Establish whether we must use the LOD form of query (even if the method did not supply a mip level to query). + // True if: + // 1. 1D/2D/3D/Cube AND multisample==0 AND NOT image (those can be sent to the non-LOD query) + // or, + // 2. There is a LOD (because the non-LOD query cannot be used in that case, per spec) + const bool mipRequired = + ((dim == Esd1D || dim == Esd2D || dim == Esd3D || dim == EsdCube) && !isMs && !isImage) || // 1... + mipQuery; // 2... + + // AST assumes integer return. Will be converted to float if required. + TIntermAggregate* sizeQuery = new TIntermAggregate(isImage ? EOpImageQuerySize : EOpTextureQuerySize); + sizeQuery->getSequence().push_back(argTex); + + // If we're building an LOD query, add the LOD. + if (mipRequired) { + // If the base HLSL query had no MIP level given, use level 0. + TIntermTyped* queryLod = mipQuery ? argAggregate->getSequence()[1]->getAsTyped() : + intermediate.addConstantUnion(0, loc, true); + sizeQuery->getSequence().push_back(queryLod); + } + + sizeQuery->setType(TType(EbtUint, EvqTemporary, numDims)); + sizeQuery->setLoc(loc); + + // Return value from size query + TVariable* tempArg = makeInternalVariable("sizeQueryTemp", sizeQuery->getType()); + tempArg->getWritableType().getQualifier().makeTemporary(); + TIntermTyped* sizeQueryAssign = intermediate.addAssign(EOpAssign, + intermediate.addSymbol(*tempArg, loc), + sizeQuery, loc); + + // Compound statement for assigning outputs + TIntermAggregate* compoundStatement = intermediate.makeAggregate(sizeQueryAssign, loc); + // Index of first output parameter + const int outParamBase = mipQuery ? 2 : 1; + + for (int compNum = 0; compNum < numDims; ++compNum) { + TIntermTyped* indexedOut = nullptr; + TIntermSymbol* sizeQueryReturn = intermediate.addSymbol(*tempArg, loc); + + if (numDims > 1) { + TIntermTyped* component = intermediate.addConstantUnion(compNum, loc, true); + indexedOut = intermediate.addIndex(EOpIndexDirect, sizeQueryReturn, component, loc); + indexedOut->setType(TType(EbtUint, EvqTemporary, 1)); + indexedOut->setLoc(loc); + } else { + indexedOut = sizeQueryReturn; + } + + TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + compNum]->getAsTyped(); + TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, indexedOut, loc); + + compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); + } + + // handle mip level parameter + if (mipQuery) { + TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped(); + + TIntermAggregate* levelsQuery = new TIntermAggregate(EOpTextureQueryLevels); + levelsQuery->getSequence().push_back(argTex); + levelsQuery->setType(TType(EbtUint, EvqTemporary, 1)); + levelsQuery->setLoc(loc); + + TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, levelsQuery, loc); + compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); + } + + // 2DMS formats query # samples, which needs a different query op + if (sampler.isMultiSample()) { + TIntermTyped* outParam = argAggregate->getSequence()[outParamBase + numDims]->getAsTyped(); + + TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples); + samplesQuery->getSequence().push_back(argTex); + samplesQuery->setType(TType(EbtUint, EvqTemporary, 1)); + samplesQuery->setLoc(loc); + + TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, outParam, samplesQuery, loc); + compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); + } + + compoundStatement->setOperator(EOpSequence); + compoundStatement->setLoc(loc); + compoundStatement->setType(TType(EbtVoid)); + + node = compoundStatement; + + break; + } + + case EOpMethodSampleCmp: // fall through... + case EOpMethodSampleCmpLevelZero: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argCmpVal = argAggregate->getSequence()[3]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + + // Sampler argument should be a sampler. + if (argSamp->getType().getBasicType() != EbtSampler) { + error(loc, "expected: sampler type", "", ""); + return; + } + + // Sampler should be a SamplerComparisonState + if (! argSamp->getType().getSampler().isShadow()) { + error(loc, "expected: SamplerComparisonState", "", ""); + return; + } + + // optional offset value + if (argAggregate->getSequence().size() > 4) + argOffset = argAggregate->getSequence()[4]->getAsTyped(); + + const int coordDimWithCmpVal = argCoord->getType().getVectorSize() + 1; // +1 for cmp + + // AST wants comparison value as one of the texture coordinates + TOperator constructOp = EOpNull; + switch (coordDimWithCmpVal) { + // 1D can't happen: there's always at least 1 coordinate dimension + 1 cmp val + case 2: constructOp = EOpConstructVec2; break; + case 3: constructOp = EOpConstructVec3; break; + case 4: constructOp = EOpConstructVec4; break; + case 5: constructOp = EOpConstructVec4; break; // cubeArrayShadow, cmp value is separate arg. + default: + error(loc, "unhandled DX10 MethodSample dimension", "", ""); + break; + } + + TIntermAggregate* coordWithCmp = new TIntermAggregate(constructOp); + coordWithCmp->getSequence().push_back(argCoord); + if (coordDimWithCmpVal != 5) // cube array shadow is special. + coordWithCmp->getSequence().push_back(argCmpVal); + coordWithCmp->setLoc(loc); + coordWithCmp->setType(TType(argCoord->getBasicType(), EvqTemporary, std::min(coordDimWithCmpVal, 4))); + + TOperator textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLod : EOpTexture); + if (argOffset != nullptr) + textureOp = (op == EOpMethodSampleCmpLevelZero ? EOpTextureLodOffset : EOpTextureOffset); + + // Create combined sampler & texture op + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + TIntermAggregate* txsample = new TIntermAggregate(textureOp); + txsample->getSequence().push_back(txcombine); + txsample->getSequence().push_back(coordWithCmp); + + if (coordDimWithCmpVal == 5) // cube array shadow is special: cmp val follows coord. + txsample->getSequence().push_back(argCmpVal); + + // the LevelZero form uses 0 as an explicit LOD + if (op == EOpMethodSampleCmpLevelZero) + txsample->getSequence().push_back(intermediate.addConstantUnion(0.0, EbtFloat, loc, true)); + + // Add offset if present + if (argOffset != nullptr) + txsample->getSequence().push_back(argOffset); + + txsample->setType(node->getType()); + txsample->setLoc(loc); + node = txsample; + + break; + } + + case EOpMethodLoad: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + TIntermTyped* lodComponent = nullptr; + TIntermTyped* coordSwizzle = nullptr; + + const TSampler& sampler = argTex->getType().getSampler(); + const bool isMS = sampler.isMultiSample(); + const bool isBuffer = sampler.dim == EsdBuffer; + const bool isImage = sampler.isImage(); + const TBasicType coordBaseType = argCoord->getType().getBasicType(); + + // Last component of coordinate is the mip level, for non-MS. we separate them here: + if (isMS || isBuffer || isImage) { + // MS, Buffer, and Image have no LOD + coordSwizzle = argCoord; + } else { + // Extract coordinate + int swizzleSize = argCoord->getType().getVectorSize() - (isMS ? 0 : 1); + TSwizzleSelectors coordFields; + for (int i = 0; i < swizzleSize; ++i) + coordFields.push_back(i); + TIntermTyped* coordIdx = intermediate.addSwizzle(coordFields, loc); + coordSwizzle = intermediate.addIndex(EOpVectorSwizzle, argCoord, coordIdx, loc); + coordSwizzle->setType(TType(coordBaseType, EvqTemporary, coordFields.size())); + + // Extract LOD + TIntermTyped* lodIdx = intermediate.addConstantUnion(coordFields.size(), loc, true); + lodComponent = intermediate.addIndex(EOpIndexDirect, argCoord, lodIdx, loc); + lodComponent->setType(TType(coordBaseType, EvqTemporary, 1)); + } + + const int numArgs = (int)argAggregate->getSequence().size(); + const bool hasOffset = ((!isMS && numArgs == 3) || (isMS && numArgs == 4)); + + // Create texel fetch + const TOperator fetchOp = (isImage ? EOpImageLoad : + hasOffset ? EOpTextureFetchOffset : + EOpTextureFetch); + TIntermAggregate* txfetch = new TIntermAggregate(fetchOp); + + // Build up the fetch + txfetch->getSequence().push_back(argTex); + txfetch->getSequence().push_back(coordSwizzle); + + if (isMS) { + // add 2DMS sample index + TIntermTyped* argSampleIdx = argAggregate->getSequence()[2]->getAsTyped(); + txfetch->getSequence().push_back(argSampleIdx); + } else if (isBuffer) { + // Nothing else to do for buffers. + } else if (isImage) { + // Nothing else to do for images. + } else { + // 2DMS and buffer have no LOD, but everything else does. + txfetch->getSequence().push_back(lodComponent); + } + + // Obtain offset arg, if there is one. + if (hasOffset) { + const int offsetPos = (isMS ? 3 : 2); + argOffset = argAggregate->getSequence()[offsetPos]->getAsTyped(); + txfetch->getSequence().push_back(argOffset); + } + + node = convertReturn(txfetch, sampler); + + break; + } + + case EOpMethodSampleLevel: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argLod = argAggregate->getSequence()[3]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + const TSampler& sampler = argTex->getType().getSampler(); + + const int numArgs = (int)argAggregate->getSequence().size(); + + if (numArgs == 5) // offset, if present + argOffset = argAggregate->getSequence()[4]->getAsTyped(); + + const TOperator textureOp = (argOffset == nullptr ? EOpTextureLod : EOpTextureLodOffset); + TIntermAggregate* txsample = new TIntermAggregate(textureOp); + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + txsample->getSequence().push_back(txcombine); + txsample->getSequence().push_back(argCoord); + txsample->getSequence().push_back(argLod); + + if (argOffset != nullptr) + txsample->getSequence().push_back(argOffset); + + node = convertReturn(txsample, sampler); + + break; + } + + case EOpMethodGather: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + + // Offset is optional + if (argAggregate->getSequence().size() > 3) + argOffset = argAggregate->getSequence()[3]->getAsTyped(); + + const TOperator textureOp = (argOffset == nullptr ? EOpTextureGather : EOpTextureGatherOffset); + TIntermAggregate* txgather = new TIntermAggregate(textureOp); + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + txgather->getSequence().push_back(txcombine); + txgather->getSequence().push_back(argCoord); + // Offset if not given is implicitly channel 0 (red) + + if (argOffset != nullptr) + txgather->getSequence().push_back(argOffset); + + txgather->setType(node->getType()); + txgather->setLoc(loc); + node = txgather; + + break; + } + + case EOpMethodGatherRed: // fall through... + case EOpMethodGatherGreen: // ... + case EOpMethodGatherBlue: // ... + case EOpMethodGatherAlpha: // ... + case EOpMethodGatherCmpRed: // ... + case EOpMethodGatherCmpGreen: // ... + case EOpMethodGatherCmpBlue: // ... + case EOpMethodGatherCmpAlpha: // ... + { + int channel = 0; // the channel we are gathering + int cmpValues = 0; // 1 if there is a compare value (handier than a bool below) + + switch (op) { + case EOpMethodGatherCmpRed: cmpValues = 1; // fall through + case EOpMethodGatherRed: channel = 0; break; + case EOpMethodGatherCmpGreen: cmpValues = 1; // fall through + case EOpMethodGatherGreen: channel = 1; break; + case EOpMethodGatherCmpBlue: cmpValues = 1; // fall through + case EOpMethodGatherBlue: channel = 2; break; + case EOpMethodGatherCmpAlpha: cmpValues = 1; // fall through + case EOpMethodGatherAlpha: channel = 3; break; + default: assert(0); break; + } + + // For now, we have nothing to map the component-wise comparison forms + // to, because neither GLSL nor SPIR-V has such an opcode. Issue an + // unimplemented error instead. Most of the machinery is here if that + // should ever become available. However, red can be passed through + // to OpImageDrefGather. G/B/A cannot, because that opcode does not + // accept a component. + if (cmpValues != 0 && op != EOpMethodGatherCmpRed) { + error(loc, "unimplemented: component-level gather compare", "", ""); + return; + } + + int arg = 0; + + TIntermTyped* argTex = argAggregate->getSequence()[arg++]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[arg++]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[arg++]->getAsTyped(); + TIntermTyped* argOffset = nullptr; + TIntermTyped* argOffsets[4] = { nullptr, nullptr, nullptr, nullptr }; + // TIntermTyped* argStatus = nullptr; // TODO: residency + TIntermTyped* argCmp = nullptr; + + const TSamplerDim dim = argTex->getType().getSampler().dim; + + const int argSize = (int)argAggregate->getSequence().size(); + bool hasStatus = (argSize == (5+cmpValues) || argSize == (8+cmpValues)); + bool hasOffset1 = false; + bool hasOffset4 = false; + + // Sampler argument should be a sampler. + if (argSamp->getType().getBasicType() != EbtSampler) { + error(loc, "expected: sampler type", "", ""); + return; + } + + // Cmp forms require SamplerComparisonState + if (cmpValues > 0 && ! argSamp->getType().getSampler().isShadow()) { + error(loc, "expected: SamplerComparisonState", "", ""); + return; + } + + // Only 2D forms can have offsets. Discover if we have 0, 1 or 4 offsets. + if (dim == Esd2D) { + hasOffset1 = (argSize == (4+cmpValues) || argSize == (5+cmpValues)); + hasOffset4 = (argSize == (7+cmpValues) || argSize == (8+cmpValues)); + } + + assert(!(hasOffset1 && hasOffset4)); + + TOperator textureOp = EOpTextureGather; + + // Compare forms have compare value + if (cmpValues != 0) + argCmp = argOffset = argAggregate->getSequence()[arg++]->getAsTyped(); + + // Some forms have single offset + if (hasOffset1) { + textureOp = EOpTextureGatherOffset; // single offset form + argOffset = argAggregate->getSequence()[arg++]->getAsTyped(); + } + + // Some forms have 4 gather offsets + if (hasOffset4) { + textureOp = EOpTextureGatherOffsets; // note plural, for 4 offset form + for (int offsetNum = 0; offsetNum < 4; ++offsetNum) + argOffsets[offsetNum] = argAggregate->getSequence()[arg++]->getAsTyped(); + } + + // Residency status + if (hasStatus) { + // argStatus = argAggregate->getSequence()[arg++]->getAsTyped(); + error(loc, "unimplemented: residency status", "", ""); + return; + } + + TIntermAggregate* txgather = new TIntermAggregate(textureOp); + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + + TIntermTyped* argChannel = intermediate.addConstantUnion(channel, loc, true); + + txgather->getSequence().push_back(txcombine); + txgather->getSequence().push_back(argCoord); + + // AST wants an array of 4 offsets, where HLSL has separate args. Here + // we construct an array from the separate args. + if (hasOffset4) { + TType arrayType(EbtInt, EvqTemporary, 2); + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(4); + arrayType.transferArraySizes(arraySizes); + + TIntermAggregate* initList = new TIntermAggregate(EOpNull); + + for (int offsetNum = 0; offsetNum < 4; ++offsetNum) + initList->getSequence().push_back(argOffsets[offsetNum]); + + argOffset = addConstructor(loc, initList, arrayType); + } + + // Add comparison value if we have one + if (argCmp != nullptr) + txgather->getSequence().push_back(argCmp); + + // Add offset (either 1, or an array of 4) if we have one + if (argOffset != nullptr) + txgather->getSequence().push_back(argOffset); + + // Add channel value if the sampler is not shadow + if (! argSamp->getType().getSampler().isShadow()) + txgather->getSequence().push_back(argChannel); + + txgather->setType(node->getType()); + txgather->setLoc(loc); + node = txgather; + + break; + } + + case EOpMethodCalculateLevelOfDetail: + case EOpMethodCalculateLevelOfDetailUnclamped: + { + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSamp = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* argCoord = argAggregate->getSequence()[2]->getAsTyped(); + + TIntermAggregate* txquerylod = new TIntermAggregate(EOpTextureQueryLod); + + TIntermAggregate* txcombine = handleSamplerTextureCombine(loc, argTex, argSamp); + txquerylod->getSequence().push_back(txcombine); + txquerylod->getSequence().push_back(argCoord); + + TIntermTyped* lodComponent = intermediate.addConstantUnion( + op == EOpMethodCalculateLevelOfDetail ? 0 : 1, + loc, true); + TIntermTyped* lodComponentIdx = intermediate.addIndex(EOpIndexDirect, txquerylod, lodComponent, loc); + lodComponentIdx->setType(TType(EbtFloat, EvqTemporary, 1)); + node = lodComponentIdx; + + break; + } + + case EOpMethodGetSamplePosition: + { + // TODO: this entire decomposition exists because there is not yet a way to query + // the sample position directly through SPIR-V. Instead, we return fixed sample + // positions for common cases. *** If the sample positions are set differently, + // this will be wrong. *** + + TIntermTyped* argTex = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* argSampIdx = argAggregate->getSequence()[1]->getAsTyped(); + + TIntermAggregate* samplesQuery = new TIntermAggregate(EOpImageQuerySamples); + samplesQuery->getSequence().push_back(argTex); + samplesQuery->setType(TType(EbtUint, EvqTemporary, 1)); + samplesQuery->setLoc(loc); + + TIntermAggregate* compoundStatement = nullptr; + + TVariable* outSampleCount = makeInternalVariable("@sampleCount", TType(EbtUint)); + outSampleCount->getWritableType().getQualifier().makeTemporary(); + TIntermTyped* compAssign = intermediate.addAssign(EOpAssign, intermediate.addSymbol(*outSampleCount, loc), + samplesQuery, loc); + compoundStatement = intermediate.growAggregate(compoundStatement, compAssign); + + TIntermTyped* idxtest[4]; + + // Create tests against 2, 4, 8, and 16 sample values + int count = 0; + for (int val = 2; val <= 16; val *= 2) + idxtest[count++] = + intermediate.addBinaryNode(EOpEqual, + intermediate.addSymbol(*outSampleCount, loc), + intermediate.addConstantUnion(val, loc), + loc, TType(EbtBool)); + + const TOperator idxOp = (argSampIdx->getQualifier().storage == EvqConst) ? EOpIndexDirect : EOpIndexIndirect; + + // Create index ops into position arrays given sample index. + // TODO: should it be clamped? + TIntermTyped* index[4]; + count = 0; + for (int val = 2; val <= 16; val *= 2) { + index[count] = intermediate.addIndex(idxOp, getSamplePosArray(val), argSampIdx, loc); + index[count++]->setType(TType(EbtFloat, EvqTemporary, 2)); + } + + // Create expression as: + // (sampleCount == 2) ? pos2[idx] : + // (sampleCount == 4) ? pos4[idx] : + // (sampleCount == 8) ? pos8[idx] : + // (sampleCount == 16) ? pos16[idx] : float2(0,0); + TIntermTyped* test = + intermediate.addSelection(idxtest[0], index[0], + intermediate.addSelection(idxtest[1], index[1], + intermediate.addSelection(idxtest[2], index[2], + intermediate.addSelection(idxtest[3], index[3], + getSamplePosArray(1), loc), loc), loc), loc); + + compoundStatement = intermediate.growAggregate(compoundStatement, test); + compoundStatement->setOperator(EOpSequence); + compoundStatement->setLoc(loc); + compoundStatement->setType(TType(EbtFloat, EvqTemporary, 2)); + + node = compoundStatement; + + break; + } + + case EOpSubpassLoad: + { + const TIntermTyped* argSubpass = + argAggregate ? argAggregate->getSequence()[0]->getAsTyped() : + arguments->getAsTyped(); + + const TSampler& sampler = argSubpass->getType().getSampler(); + + // subpass load: the multisample form is overloaded. Here, we convert that to + // the EOpSubpassLoadMS opcode. + if (argAggregate != nullptr && argAggregate->getSequence().size() > 1) + node->getAsOperator()->setOp(EOpSubpassLoadMS); + + node = convertReturn(node, sampler); + + break; + } + + + default: + break; // most pass through unchanged + } +} + +// +// Decompose geometry shader methods +// +void HlslParseContext::decomposeGeometryMethods(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) +{ + if (node == nullptr || !node->getAsOperator()) + return; + + const TOperator op = node->getAsOperator()->getOp(); + const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; + + switch (op) { + case EOpMethodAppend: + if (argAggregate) { + // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol. + if (language != EShLangGeometry) { + node = nullptr; + return; + } + + TIntermAggregate* sequence = nullptr; + TIntermAggregate* emit = new TIntermAggregate(EOpEmitVertex); + + emit->setLoc(loc); + emit->setType(TType(EbtVoid)); + + TIntermTyped* data = argAggregate->getSequence()[1]->getAsTyped(); + + // This will be patched in finalization during finalizeAppendMethods() + sequence = intermediate.growAggregate(sequence, data, loc); + sequence = intermediate.growAggregate(sequence, emit); + + sequence->setOperator(EOpSequence); + sequence->setLoc(loc); + sequence->setType(TType(EbtVoid)); + + gsAppends.push_back({sequence, loc}); + + node = sequence; + } + break; + + case EOpMethodRestartStrip: + { + // Don't emit these for non-GS stage, since we won't have the gsStreamOutput symbol. + if (language != EShLangGeometry) { + node = nullptr; + return; + } + + TIntermAggregate* cut = new TIntermAggregate(EOpEndPrimitive); + cut->setLoc(loc); + cut->setType(TType(EbtVoid)); + node = cut; + } + break; + + default: + break; // most pass through unchanged + } +} + +// +// Optionally decompose intrinsics to AST opcodes. +// +void HlslParseContext::decomposeIntrinsic(const TSourceLoc& loc, TIntermTyped*& node, TIntermNode* arguments) +{ + // Helper to find image data for image atomics: + // OpImageLoad(image[idx]) + // We take the image load apart and add its params to the atomic op aggregate node + const auto imageAtomicParams = [this, &loc, &node](TIntermAggregate* atomic, TIntermTyped* load) { + TIntermAggregate* loadOp = load->getAsAggregate(); + if (loadOp == nullptr) { + error(loc, "unknown image type in atomic operation", "", ""); + node = nullptr; + return; + } + + atomic->getSequence().push_back(loadOp->getSequence()[0]); + atomic->getSequence().push_back(loadOp->getSequence()[1]); + }; + + // Return true if this is an imageLoad, which we will change to an image atomic. + const auto isImageParam = [](TIntermTyped* image) -> bool { + TIntermAggregate* imageAggregate = image->getAsAggregate(); + return imageAggregate != nullptr && imageAggregate->getOp() == EOpImageLoad; + }; + + const auto lookupBuiltinVariable = [&](const char* name, TBuiltInVariable builtin, TType& type) -> TIntermTyped* { + TSymbol* symbol = symbolTable.find(name); + if (nullptr == symbol) { + type.getQualifier().builtIn = builtin; + + TVariable* variable = new TVariable(NewPoolTString(name), type); + + symbolTable.insert(*variable); + + symbol = symbolTable.find(name); + assert(symbol && "Inserted symbol could not be found!"); + } + + return intermediate.addSymbol(*(symbol->getAsVariable()), loc); + }; + + // HLSL intrinsics can be pass through to native AST opcodes, or decomposed here to existing AST + // opcodes for compatibility with existing software stacks. + static const bool decomposeHlslIntrinsics = true; + + if (!decomposeHlslIntrinsics || !node || !node->getAsOperator()) + return; + + const TIntermAggregate* argAggregate = arguments ? arguments->getAsAggregate() : nullptr; + TIntermUnary* fnUnary = node->getAsUnaryNode(); + const TOperator op = node->getAsOperator()->getOp(); + + switch (op) { + case EOpGenMul: + { + // mul(a,b) -> MatrixTimesMatrix, MatrixTimesVector, MatrixTimesScalar, VectorTimesScalar, Dot, Mul + // Since we are treating HLSL rows like GLSL columns (the first matrix indirection), + // we must reverse the operand order here. Hence, arg0 gets sequence[1], etc. + TIntermTyped* arg0 = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[0]->getAsTyped(); + + if (arg0->isVector() && arg1->isVector()) { // vec * vec + node->getAsAggregate()->setOperator(EOpDot); + } else { + node = handleBinaryMath(loc, "mul", EOpMul, arg0, arg1); + } + + break; + } + + case EOpRcp: + { + // rcp(a) -> 1 / a + TIntermTyped* arg0 = fnUnary->getOperand(); + TBasicType type0 = arg0->getBasicType(); + TIntermTyped* one = intermediate.addConstantUnion(1, type0, loc, true); + node = handleBinaryMath(loc, "rcp", EOpDiv, one, arg0); + + break; + } + + case EOpAny: // fall through + case EOpAll: + { + TIntermTyped* typedArg = arguments->getAsTyped(); + + // HLSL allows float/etc types here, and the SPIR-V opcode requires a bool. + // We'll convert here. Note that for efficiency, we could add a smarter + // decomposition for some type cases, e.g, maybe by decomposing a dot product. + if (typedArg->getType().getBasicType() != EbtBool) { + const TType boolType(EbtBool, EvqTemporary, + typedArg->getVectorSize(), + typedArg->getMatrixCols(), + typedArg->getMatrixRows(), + typedArg->isVector()); + + typedArg = intermediate.addConversion(EOpConstructBool, boolType, typedArg); + node->getAsUnaryNode()->setOperand(typedArg); + } + + break; + } + + case EOpSaturate: + { + // saturate(a) -> clamp(a,0,1) + TIntermTyped* arg0 = fnUnary->getOperand(); + TBasicType type0 = arg0->getBasicType(); + TIntermAggregate* clamp = new TIntermAggregate(EOpClamp); + + clamp->getSequence().push_back(arg0); + clamp->getSequence().push_back(intermediate.addConstantUnion(0, type0, loc, true)); + clamp->getSequence().push_back(intermediate.addConstantUnion(1, type0, loc, true)); + clamp->setLoc(loc); + clamp->setType(node->getType()); + clamp->getWritableType().getQualifier().makeTemporary(); + node = clamp; + + break; + } + + case EOpSinCos: + { + // sincos(a,b,c) -> b = sin(a), c = cos(a) + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); + + TIntermTyped* sinStatement = handleUnaryMath(loc, "sin", EOpSin, arg0); + TIntermTyped* cosStatement = handleUnaryMath(loc, "cos", EOpCos, arg0); + TIntermTyped* sinAssign = intermediate.addAssign(EOpAssign, arg1, sinStatement, loc); + TIntermTyped* cosAssign = intermediate.addAssign(EOpAssign, arg2, cosStatement, loc); + + TIntermAggregate* compoundStatement = intermediate.makeAggregate(sinAssign, loc); + compoundStatement = intermediate.growAggregate(compoundStatement, cosAssign); + compoundStatement->setOperator(EOpSequence); + compoundStatement->setLoc(loc); + compoundStatement->setType(TType(EbtVoid)); + + node = compoundStatement; + + break; + } + + case EOpClip: + { + // clip(a) -> if (any(a<0)) discard; + TIntermTyped* arg0 = fnUnary->getOperand(); + TBasicType type0 = arg0->getBasicType(); + TIntermTyped* compareNode = nullptr; + + // For non-scalars: per experiment with FXC compiler, discard if any component < 0. + if (!arg0->isScalar()) { + // component-wise compare: a < 0 + TIntermAggregate* less = new TIntermAggregate(EOpLessThan); + less->getSequence().push_back(arg0); + less->setLoc(loc); + + // make vec or mat of bool matching dimensions of input + less->setType(TType(EbtBool, EvqTemporary, + arg0->getType().getVectorSize(), + arg0->getType().getMatrixCols(), + arg0->getType().getMatrixRows(), + arg0->getType().isVector())); + + // calculate # of components for comparison const + const int constComponentCount = + std::max(arg0->getType().getVectorSize(), 1) * + std::max(arg0->getType().getMatrixCols(), 1) * + std::max(arg0->getType().getMatrixRows(), 1); + + TConstUnion zero; + if (arg0->getType().isIntegerDomain()) + zero.setDConst(0); + else + zero.setDConst(0.0); + TConstUnionArray zeros(constComponentCount, zero); + + less->getSequence().push_back(intermediate.addConstantUnion(zeros, arg0->getType(), loc, true)); + + compareNode = intermediate.addBuiltInFunctionCall(loc, EOpAny, true, less, TType(EbtBool)); + } else { + TIntermTyped* zero; + if (arg0->getType().isIntegerDomain()) + zero = intermediate.addConstantUnion(0, loc, true); + else + zero = intermediate.addConstantUnion(0.0, type0, loc, true); + compareNode = handleBinaryMath(loc, "clip", EOpLessThan, arg0, zero); + } + + TIntermBranch* killNode = intermediate.addBranch(EOpKill, loc); + + node = new TIntermSelection(compareNode, killNode, nullptr); + node->setLoc(loc); + + break; + } + + case EOpLog10: + { + // log10(a) -> log2(a) * 0.301029995663981 (== 1/log2(10)) + TIntermTyped* arg0 = fnUnary->getOperand(); + TIntermTyped* log2 = handleUnaryMath(loc, "log2", EOpLog2, arg0); + TIntermTyped* base = intermediate.addConstantUnion(0.301029995663981f, EbtFloat, loc, true); + + node = handleBinaryMath(loc, "mul", EOpMul, log2, base); + + break; + } + + case EOpDst: + { + // dest.x = 1; + // dest.y = src0.y * src1.y; + // dest.z = src0.z; + // dest.w = src1.w; + + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); + + TIntermTyped* y = intermediate.addConstantUnion(1, loc, true); + TIntermTyped* z = intermediate.addConstantUnion(2, loc, true); + TIntermTyped* w = intermediate.addConstantUnion(3, loc, true); + + TIntermTyped* src0y = intermediate.addIndex(EOpIndexDirect, arg0, y, loc); + TIntermTyped* src1y = intermediate.addIndex(EOpIndexDirect, arg1, y, loc); + TIntermTyped* src0z = intermediate.addIndex(EOpIndexDirect, arg0, z, loc); + TIntermTyped* src1w = intermediate.addIndex(EOpIndexDirect, arg1, w, loc); + + TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); + + dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); + dst->getSequence().push_back(handleBinaryMath(loc, "mul", EOpMul, src0y, src1y)); + dst->getSequence().push_back(src0z); + dst->getSequence().push_back(src1w); + dst->setType(TType(EbtFloat, EvqTemporary, 4)); + dst->setLoc(loc); + node = dst; + + break; + } + + case EOpInterlockedAdd: // optional last argument (if present) is assigned from return value + case EOpInterlockedMin: // ... + case EOpInterlockedMax: // ... + case EOpInterlockedAnd: // ... + case EOpInterlockedOr: // ... + case EOpInterlockedXor: // ... + case EOpInterlockedExchange: // always has output arg + { + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // value + TIntermTyped* arg2 = nullptr; + + if (argAggregate->getSequence().size() > 2) + arg2 = argAggregate->getSequence()[2]->getAsTyped(); + + const bool isImage = isImageParam(arg0); + const TOperator atomicOp = mapAtomicOp(loc, op, isImage); + TIntermAggregate* atomic = new TIntermAggregate(atomicOp); + atomic->setType(arg0->getType()); + atomic->getWritableType().getQualifier().makeTemporary(); + atomic->setLoc(loc); + + if (isImage) { + // orig_value = imageAtomicOp(image, loc, data) + imageAtomicParams(atomic, arg0); + atomic->getSequence().push_back(arg1); + + if (argAggregate->getSequence().size() > 2) { + node = intermediate.addAssign(EOpAssign, arg2, atomic, loc); + } else { + node = atomic; // no assignment needed, as there was no out var. + } + } else { + // Normal memory variable: + // arg0 = mem, arg1 = data, arg2(optional,out) = orig_value + if (argAggregate->getSequence().size() > 2) { + // optional output param is present. return value goes to arg2. + atomic->getSequence().push_back(arg0); + atomic->getSequence().push_back(arg1); + + node = intermediate.addAssign(EOpAssign, arg2, atomic, loc); + } else { + // Set the matching operator. Since output is absent, this is all we need to do. + node->getAsAggregate()->setOperator(atomicOp); + node->setType(atomic->getType()); + } + } + + break; + } + + case EOpInterlockedCompareExchange: + { + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // dest + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // cmp + TIntermTyped* arg2 = argAggregate->getSequence()[2]->getAsTyped(); // value + TIntermTyped* arg3 = argAggregate->getSequence()[3]->getAsTyped(); // orig + + const bool isImage = isImageParam(arg0); + TIntermAggregate* atomic = new TIntermAggregate(mapAtomicOp(loc, op, isImage)); + atomic->setLoc(loc); + atomic->setType(arg2->getType()); + atomic->getWritableType().getQualifier().makeTemporary(); + + if (isImage) { + imageAtomicParams(atomic, arg0); + } else { + atomic->getSequence().push_back(arg0); + } + + atomic->getSequence().push_back(arg1); + atomic->getSequence().push_back(arg2); + node = intermediate.addAssign(EOpAssign, arg3, atomic, loc); + + break; + } + + case EOpEvaluateAttributeSnapped: + { + // SPIR-V InterpolateAtOffset uses float vec2 offset in pixels + // HLSL uses int2 offset on a 16x16 grid in [-8..7] on x & y: + // iU = (iU<<28)>>28 + // fU = ((float)iU)/16 + // Targets might handle this natively, in which case they can disable + // decompositions. + + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); // value + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); // offset + + TIntermTyped* i28 = intermediate.addConstantUnion(28, loc, true); + TIntermTyped* iU = handleBinaryMath(loc, ">>", EOpRightShift, + handleBinaryMath(loc, "<<", EOpLeftShift, arg1, i28), + i28); + + TIntermTyped* recip16 = intermediate.addConstantUnion((1.0/16.0), EbtFloat, loc, true); + TIntermTyped* floatOffset = handleBinaryMath(loc, "mul", EOpMul, + intermediate.addConversion(EOpConstructFloat, + TType(EbtFloat, EvqTemporary, 2), iU), + recip16); + + TIntermAggregate* interp = new TIntermAggregate(EOpInterpolateAtOffset); + interp->getSequence().push_back(arg0); + interp->getSequence().push_back(floatOffset); + interp->setLoc(loc); + interp->setType(arg0->getType()); + interp->getWritableType().getQualifier().makeTemporary(); + + node = interp; + + break; + } + + case EOpLit: + { + TIntermTyped* n_dot_l = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* n_dot_h = argAggregate->getSequence()[1]->getAsTyped(); + TIntermTyped* m = argAggregate->getSequence()[2]->getAsTyped(); + + TIntermAggregate* dst = new TIntermAggregate(EOpConstructVec4); + + // Ambient + dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); + + // Diffuse: + TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true); + TIntermAggregate* diffuse = new TIntermAggregate(EOpMax); + diffuse->getSequence().push_back(n_dot_l); + diffuse->getSequence().push_back(zero); + diffuse->setLoc(loc); + diffuse->setType(TType(EbtFloat)); + dst->getSequence().push_back(diffuse); + + // Specular: + TIntermAggregate* min_ndot = new TIntermAggregate(EOpMin); + min_ndot->getSequence().push_back(n_dot_l); + min_ndot->getSequence().push_back(n_dot_h); + min_ndot->setLoc(loc); + min_ndot->setType(TType(EbtFloat)); + + TIntermTyped* compare = handleBinaryMath(loc, "<", EOpLessThan, min_ndot, zero); + TIntermTyped* n_dot_h_m = handleBinaryMath(loc, "mul", EOpMul, n_dot_h, m); // n_dot_h * m + + dst->getSequence().push_back(intermediate.addSelection(compare, zero, n_dot_h_m, loc)); + + // One: + dst->getSequence().push_back(intermediate.addConstantUnion(1.0, EbtFloat, loc, true)); + + dst->setLoc(loc); + dst->setType(TType(EbtFloat, EvqTemporary, 4)); + node = dst; + break; + } + + case EOpAsDouble: + { + // asdouble accepts two 32 bit ints. we can use EOpUint64BitsToDouble, but must + // first construct a uint64. + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); + + if (arg0->getType().isVector()) { // TODO: ... + error(loc, "double2 conversion not implemented", "asdouble", ""); + break; + } + + TIntermAggregate* uint64 = new TIntermAggregate(EOpConstructUVec2); + + uint64->getSequence().push_back(arg0); + uint64->getSequence().push_back(arg1); + uint64->setType(TType(EbtUint, EvqTemporary, 2)); // convert 2 uints to a uint2 + uint64->setLoc(loc); + + // bitcast uint2 to a double + TIntermTyped* convert = new TIntermUnary(EOpUint64BitsToDouble); + convert->getAsUnaryNode()->setOperand(uint64); + convert->setLoc(loc); + convert->setType(TType(EbtDouble, EvqTemporary)); + node = convert; + + break; + } + + case EOpF16tof32: + { + // input uvecN with low 16 bits of each component holding a float16. convert to float32. + TIntermTyped* argValue = node->getAsUnaryNode()->getOperand(); + TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true); + const int vecSize = argValue->getType().getVectorSize(); + + TOperator constructOp = EOpNull; + switch (vecSize) { + case 1: constructOp = EOpNull; break; // direct use, no construct needed + case 2: constructOp = EOpConstructVec2; break; + case 3: constructOp = EOpConstructVec3; break; + case 4: constructOp = EOpConstructVec4; break; + default: assert(0); break; + } + + // For scalar case, we don't need to construct another type. + TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr; + + if (result) { + result->setType(TType(EbtFloat, EvqTemporary, vecSize)); + result->setLoc(loc); + } + + for (int idx = 0; idx < vecSize; ++idx) { + TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true); + TIntermTyped* component = argValue->getType().isVector() ? + intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue; + + if (component != argValue) + component->setType(TType(argValue->getBasicType(), EvqTemporary)); + + TIntermTyped* unpackOp = new TIntermUnary(EOpUnpackHalf2x16); + unpackOp->setType(TType(EbtFloat, EvqTemporary, 2)); + unpackOp->getAsUnaryNode()->setOperand(component); + unpackOp->setLoc(loc); + + TIntermTyped* lowOrder = intermediate.addIndex(EOpIndexDirect, unpackOp, zero, loc); + + if (result != nullptr) { + result->getSequence().push_back(lowOrder); + node = result; + } else { + node = lowOrder; + } + } + + break; + } + + case EOpF32tof16: + { + // input floatN converted to 16 bit float in low order bits of each component of uintN + TIntermTyped* argValue = node->getAsUnaryNode()->getOperand(); + + TIntermTyped* zero = intermediate.addConstantUnion(0.0, EbtFloat, loc, true); + const int vecSize = argValue->getType().getVectorSize(); + + TOperator constructOp = EOpNull; + switch (vecSize) { + case 1: constructOp = EOpNull; break; // direct use, no construct needed + case 2: constructOp = EOpConstructUVec2; break; + case 3: constructOp = EOpConstructUVec3; break; + case 4: constructOp = EOpConstructUVec4; break; + default: assert(0); break; + } + + // For scalar case, we don't need to construct another type. + TIntermAggregate* result = (vecSize > 1) ? new TIntermAggregate(constructOp) : nullptr; + + if (result) { + result->setType(TType(EbtUint, EvqTemporary, vecSize)); + result->setLoc(loc); + } + + for (int idx = 0; idx < vecSize; ++idx) { + TIntermTyped* idxConst = intermediate.addConstantUnion(idx, loc, true); + TIntermTyped* component = argValue->getType().isVector() ? + intermediate.addIndex(EOpIndexDirect, argValue, idxConst, loc) : argValue; + + if (component != argValue) + component->setType(TType(argValue->getBasicType(), EvqTemporary)); + + TIntermAggregate* vec2ComponentAndZero = new TIntermAggregate(EOpConstructVec2); + vec2ComponentAndZero->getSequence().push_back(component); + vec2ComponentAndZero->getSequence().push_back(zero); + vec2ComponentAndZero->setType(TType(EbtFloat, EvqTemporary, 2)); + vec2ComponentAndZero->setLoc(loc); + + TIntermTyped* packOp = new TIntermUnary(EOpPackHalf2x16); + packOp->getAsUnaryNode()->setOperand(vec2ComponentAndZero); + packOp->setLoc(loc); + packOp->setType(TType(EbtUint, EvqTemporary)); + + if (result != nullptr) { + result->getSequence().push_back(packOp); + node = result; + } else { + node = packOp; + } + } + + break; + } + + case EOpD3DCOLORtoUBYTE4: + { + // ivec4 ( x.zyxw * 255.001953 ); + TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand(); + TSwizzleSelectors selectors; + selectors.push_back(2); + selectors.push_back(1); + selectors.push_back(0); + selectors.push_back(3); + TIntermTyped* swizzleIdx = intermediate.addSwizzle(selectors, loc); + TIntermTyped* swizzled = intermediate.addIndex(EOpVectorSwizzle, arg0, swizzleIdx, loc); + swizzled->setType(arg0->getType()); + swizzled->getWritableType().getQualifier().makeTemporary(); + + TIntermTyped* conversion = intermediate.addConstantUnion(255.001953f, EbtFloat, loc, true); + TIntermTyped* rangeConverted = handleBinaryMath(loc, "mul", EOpMul, conversion, swizzled); + rangeConverted->setType(arg0->getType()); + rangeConverted->getWritableType().getQualifier().makeTemporary(); + + node = intermediate.addConversion(EOpConstructInt, TType(EbtInt, EvqTemporary, 4), rangeConverted); + node->setLoc(loc); + node->setType(TType(EbtInt, EvqTemporary, 4)); + break; + } + + case EOpIsFinite: + { + // Since OPIsFinite in SPIR-V is only supported with the Kernel capability, we translate + // it to !isnan && !isinf + + TIntermTyped* arg0 = node->getAsUnaryNode()->getOperand(); + + // We'll make a temporary in case the RHS is cmoplex + TVariable* tempArg = makeInternalVariable("@finitetmp", arg0->getType()); + tempArg->getWritableType().getQualifier().makeTemporary(); + + TIntermTyped* tmpArgAssign = intermediate.addAssign(EOpAssign, + intermediate.addSymbol(*tempArg, loc), + arg0, loc); + + TIntermAggregate* compoundStatement = intermediate.makeAggregate(tmpArgAssign, loc); + + const TType boolType(EbtBool, EvqTemporary, arg0->getVectorSize(), arg0->getMatrixCols(), + arg0->getMatrixRows()); + + TIntermTyped* isnan = handleUnaryMath(loc, "isnan", EOpIsNan, intermediate.addSymbol(*tempArg, loc)); + isnan->setType(boolType); + + TIntermTyped* notnan = handleUnaryMath(loc, "!", EOpLogicalNot, isnan); + notnan->setType(boolType); + + TIntermTyped* isinf = handleUnaryMath(loc, "isinf", EOpIsInf, intermediate.addSymbol(*tempArg, loc)); + isinf->setType(boolType); + + TIntermTyped* notinf = handleUnaryMath(loc, "!", EOpLogicalNot, isinf); + notinf->setType(boolType); + + TIntermTyped* andNode = handleBinaryMath(loc, "and", EOpLogicalAnd, notnan, notinf); + andNode->setType(boolType); + + compoundStatement = intermediate.growAggregate(compoundStatement, andNode); + compoundStatement->setOperator(EOpSequence); + compoundStatement->setLoc(loc); + compoundStatement->setType(boolType); + + node = compoundStatement; + + break; + } + case EOpWaveGetLaneCount: + { + // Mapped to gl_SubgroupSize builtin (We preprend @ to the symbol + // so that it inhabits the symbol table, but has a user-invalid name + // in-case some source HLSL defined the symbol also). + TType type(EbtUint, EvqVaryingIn); + node = lookupBuiltinVariable("@gl_SubgroupSize", EbvSubgroupSize2, type); + break; + } + case EOpWaveGetLaneIndex: + { + // Mapped to gl_SubgroupInvocationID builtin (We preprend @ to the + // symbol so that it inhabits the symbol table, but has a + // user-invalid name in-case some source HLSL defined the symbol + // also). + TType type(EbtUint, EvqVaryingIn); + node = lookupBuiltinVariable("@gl_SubgroupInvocationID", EbvSubgroupInvocation2, type); + break; + } + case EOpWaveActiveCountBits: + { + // Mapped to subgroupBallotBitCount(subgroupBallot()) builtin + + // uvec4 type. + TType uvec4Type(EbtUint, EvqTemporary, 4); + + // Get the uvec4 return from subgroupBallot(). + TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc, + EOpSubgroupBallot, true, arguments, uvec4Type); + + // uint type. + TType uintType(EbtUint, EvqTemporary); + + node = intermediate.addBuiltInFunctionCall(loc, + EOpSubgroupBallotBitCount, true, res, uintType); + + break; + } + case EOpWavePrefixCountBits: + { + // Mapped to subgroupBallotInclusiveBitCount(subgroupBallot()) + // builtin + + // uvec4 type. + TType uvec4Type(EbtUint, EvqTemporary, 4); + + // Get the uvec4 return from subgroupBallot(). + TIntermTyped* res = intermediate.addBuiltInFunctionCall(loc, + EOpSubgroupBallot, true, arguments, uvec4Type); + + // uint type. + TType uintType(EbtUint, EvqTemporary); + + node = intermediate.addBuiltInFunctionCall(loc, + EOpSubgroupBallotInclusiveBitCount, true, res, uintType); + + break; + } + + default: + break; // most pass through unchanged + } +} + +// +// Handle seeing function call syntax in the grammar, which could be any of +// - .length() method +// - constructor +// - a call to a built-in function mapped to an operator +// - a call to a built-in function that will remain a function call (e.g., texturing) +// - user function +// - subroutine call (not implemented yet) +// +TIntermTyped* HlslParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermTyped* arguments) +{ + TIntermTyped* result = nullptr; + + TOperator op = function->getBuiltInOp(); + if (op != EOpNull) { + // + // Then this should be a constructor. + // Don't go through the symbol table for constructors. + // Their parameters will be verified algorithmically. + // + TType type(EbtVoid); // use this to get the type back + if (! constructorError(loc, arguments, *function, op, type)) { + // + // It's a constructor, of type 'type'. + // + result = handleConstructor(loc, arguments, type); + if (result == nullptr) { + error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), ""); + return nullptr; + } + } + } else { + // + // Find it in the symbol table. + // + const TFunction* fnCandidate = nullptr; + bool builtIn = false; + int thisDepth = 0; + + // For mat mul, the situation is unusual: we have to compare vector sizes to mat row or col sizes, + // and clamp the opposite arg. Since that's complex, we farm it off to a separate method. + // It doesn't naturally fall out of processing an argument at a time in isolation. + if (function->getName() == "mul") + addGenMulArgumentConversion(loc, *function, arguments); + + TIntermAggregate* aggregate = arguments ? arguments->getAsAggregate() : nullptr; + + // TODO: this needs improvement: there's no way at present to look up a signature in + // the symbol table for an arbitrary type. This is a temporary hack until that ability exists. + // It will have false positives, since it doesn't check arg counts or types. + if (arguments) { + // Check if first argument is struct buffer type. It may be an aggregate or a symbol, so we + // look for either case. + + TIntermTyped* arg0 = nullptr; + + if (aggregate && aggregate->getSequence().size() > 0 && aggregate->getSequence()[0]) + arg0 = aggregate->getSequence()[0]->getAsTyped(); + else if (arguments->getAsSymbolNode()) + arg0 = arguments->getAsSymbolNode(); + + if (arg0 != nullptr && isStructBufferType(arg0->getType())) { + static const int methodPrefixSize = sizeof(BUILTIN_PREFIX)-1; + + if (function->getName().length() > methodPrefixSize && + isStructBufferMethod(function->getName().substr(methodPrefixSize))) { + const TString mangle = function->getName() + "("; + TSymbol* symbol = symbolTable.find(mangle, &builtIn); + + if (symbol) + fnCandidate = symbol->getAsFunction(); + } + } + } + + if (fnCandidate == nullptr) + fnCandidate = findFunction(loc, *function, builtIn, thisDepth, arguments); + + if (fnCandidate) { + // This is a declared function that might map to + // - a built-in operator, + // - a built-in function not mapped to an operator, or + // - a user function. + + // turn an implicit member-function resolution into an explicit call + TString callerName; + if (thisDepth == 0) + callerName = fnCandidate->getMangledName(); + else { + // get the explicit (full) name of the function + callerName = currentTypePrefix[currentTypePrefix.size() - thisDepth]; + callerName += fnCandidate->getMangledName(); + // insert the implicit calling argument + pushFrontArguments(intermediate.addSymbol(*getImplicitThis(thisDepth)), arguments); + } + + // Convert 'in' arguments, so that types match. + // However, skip those that need expansion, that is covered next. + if (arguments) + addInputArgumentConversions(*fnCandidate, arguments); + + // Expand arguments. Some arguments must physically expand to a different set + // than what the shader declared and passes. + if (arguments && !builtIn) + expandArguments(loc, *fnCandidate, arguments); + + // Expansion may have changed the form of arguments + aggregate = arguments ? arguments->getAsAggregate() : nullptr; + + op = fnCandidate->getBuiltInOp(); + if (builtIn && op != EOpNull) { + // A function call mapped to a built-in operation. + result = intermediate.addBuiltInFunctionCall(loc, op, fnCandidate->getParamCount() == 1, arguments, + fnCandidate->getType()); + if (result == nullptr) { + error(arguments->getLoc(), " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", + static_cast(arguments)->getCompleteString().c_str()); + } else if (result->getAsOperator()) { + builtInOpCheck(loc, *fnCandidate, *result->getAsOperator()); + } + } else { + // This is a function call not mapped to built-in operator. + // It could still be a built-in function, but only if PureOperatorBuiltins == false. + result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc); + TIntermAggregate* call = result->getAsAggregate(); + call->setName(callerName); + + // this is how we know whether the given function is a built-in function or a user-defined function + // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also + // if builtIn == true, it's definitely a built-in function with EOpNull + if (! builtIn) { + call->setUserDefined(); + intermediate.addToCallGraph(infoSink, currentCaller, callerName); + } + } + + // for decompositions, since we want to operate on the function node, not the aggregate holding + // output conversions. + const TIntermTyped* fnNode = result; + + decomposeStructBufferMethods(loc, result, arguments); // HLSL->AST struct buffer method decompositions + decomposeIntrinsic(loc, result, arguments); // HLSL->AST intrinsic decompositions + decomposeSampleMethods(loc, result, arguments); // HLSL->AST sample method decompositions + decomposeGeometryMethods(loc, result, arguments); // HLSL->AST geometry method decompositions + + // Create the qualifier list, carried in the AST for the call. + // Because some arguments expand to multiple arguments, the qualifier list will + // be longer than the formal parameter list. + if (result == fnNode && result->getAsAggregate()) { + TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage; + if (hasStructBuffCounter(*(*fnCandidate)[i].type)) { + // add buffer and counter buffer argument qualifier + qualifierList.push_back(qual); + qualifierList.push_back(qual); + } else if (shouldFlatten(*(*fnCandidate)[i].type, (*fnCandidate)[i].type->getQualifier().storage, + true)) { + // add structure member expansion + for (int memb = 0; memb < (int)(*fnCandidate)[i].type->getStruct()->size(); ++memb) + qualifierList.push_back(qual); + } else { + // Normal 1:1 case + qualifierList.push_back(qual); + } + } + } + + // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore. + // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output. + // Also, build the qualifier list for user function calls, which are always called with an aggregate. + // We don't do this is if there has been a decomposition, which will have added its own conversions + // for output parameters. + if (result == fnNode && result->getAsAggregate()) + result = addOutputArgumentConversions(*fnCandidate, *result->getAsOperator()); + } + } + + // generic error recovery + // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to + // reduce cascades + if (result == nullptr) + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + + return result; +} + +// An initial argument list is difficult: it can be null, or a single node, +// or an aggregate if more than one argument. Add one to the front, maintaining +// this lack of uniformity. +void HlslParseContext::pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments) +{ + if (arguments == nullptr) + arguments = front; + else if (arguments->getAsAggregate() != nullptr) + arguments->getAsAggregate()->getSequence().insert(arguments->getAsAggregate()->getSequence().begin(), front); + else + arguments = intermediate.growAggregate(front, arguments); +} + +// +// HLSL allows mismatched dimensions on vec*mat, mat*vec, vec*vec, and mat*mat. This is a +// situation not well suited to resolution in intrinsic selection, but we can do so here, since we +// can look at both arguments insert explicit shape changes if required. +// +void HlslParseContext::addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args) +{ + TIntermAggregate* argAggregate = args ? args->getAsAggregate() : nullptr; + + if (argAggregate == nullptr || argAggregate->getSequence().size() != 2) { + // It really ought to have two arguments. + error(loc, "expected: mul arguments", "", ""); + return; + } + + TIntermTyped* arg0 = argAggregate->getSequence()[0]->getAsTyped(); + TIntermTyped* arg1 = argAggregate->getSequence()[1]->getAsTyped(); + + if (arg0->isVector() && arg1->isVector()) { + // For: + // vec * vec: it's handled during intrinsic selection, so while we could do it here, + // we can also ignore it, which is easier. + } else if (arg0->isVector() && arg1->isMatrix()) { + // vec * mat: we clamp the vec if the mat col is smaller, else clamp the mat col. + if (arg0->getVectorSize() < arg1->getMatrixCols()) { + // vec is smaller, so truncate larger mat dimension + const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision, + 0, arg0->getVectorSize(), arg1->getMatrixRows()); + arg1 = addConstructor(loc, arg1, truncType); + } else if (arg0->getVectorSize() > arg1->getMatrixCols()) { + // vec is larger, so truncate vec to mat size + const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision, + arg1->getMatrixCols()); + arg0 = addConstructor(loc, arg0, truncType); + } + } else if (arg0->isMatrix() && arg1->isVector()) { + // mat * vec: we clamp the vec if the mat col is smaller, else clamp the mat col. + if (arg1->getVectorSize() < arg0->getMatrixRows()) { + // vec is smaller, so truncate larger mat dimension + const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision, + 0, arg0->getMatrixCols(), arg1->getVectorSize()); + arg0 = addConstructor(loc, arg0, truncType); + } else if (arg1->getVectorSize() > arg0->getMatrixRows()) { + // vec is larger, so truncate vec to mat size + const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision, + arg0->getMatrixRows()); + arg1 = addConstructor(loc, arg1, truncType); + } + } else if (arg0->isMatrix() && arg1->isMatrix()) { + // mat * mat: we clamp the smaller inner dimension to match the other matrix size. + // Remember, HLSL Mrc = GLSL/SPIRV Mcr. + if (arg0->getMatrixRows() > arg1->getMatrixCols()) { + const TType truncType(arg0->getBasicType(), arg0->getQualifier().storage, arg0->getQualifier().precision, + 0, arg0->getMatrixCols(), arg1->getMatrixCols()); + arg0 = addConstructor(loc, arg0, truncType); + } else if (arg0->getMatrixRows() < arg1->getMatrixCols()) { + const TType truncType(arg1->getBasicType(), arg1->getQualifier().storage, arg1->getQualifier().precision, + 0, arg0->getMatrixRows(), arg1->getMatrixRows()); + arg1 = addConstructor(loc, arg1, truncType); + } + } else { + // It's something with scalars: we'll just leave it alone. Function selection will handle it + // downstream. + } + + // Warn if we altered one of the arguments + if (arg0 != argAggregate->getSequence()[0] || arg1 != argAggregate->getSequence()[1]) + warn(loc, "mul() matrix size mismatch", "", ""); + + // Put arguments back. (They might be unchanged, in which case this is harmless). + argAggregate->getSequence()[0] = arg0; + argAggregate->getSequence()[1] = arg1; + + call[0].type = &arg0->getWritableType(); + call[1].type = &arg1->getWritableType(); +} + +// +// Add any needed implicit conversions for function-call arguments to input parameters. +// +void HlslParseContext::addInputArgumentConversions(const TFunction& function, TIntermTyped*& arguments) +{ + TIntermAggregate* aggregate = arguments->getAsAggregate(); + + // Replace a single argument with a single argument. + const auto setArg = [&](int paramNum, TIntermTyped* arg) { + if (function.getParamCount() == 1) + arguments = arg; + else { + if (aggregate == nullptr) + arguments = arg; + else + aggregate->getSequence()[paramNum] = arg; + } + }; + + // Process each argument's conversion + for (int param = 0; param < function.getParamCount(); ++param) { + if (! function[param].type->getQualifier().isParamInput()) + continue; + + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermTyped* arg = function.getParamCount() == 1 + ? arguments->getAsTyped() + : (aggregate ? + aggregate->getSequence()[param]->getAsTyped() : + arguments->getAsTyped()); + if (*function[param].type != arg->getType()) { + // In-qualified arguments just need an extra node added above the argument to + // convert to the correct type. + TIntermTyped* convArg = intermediate.addConversion(EOpFunctionCall, *function[param].type, arg); + if (convArg != nullptr) + convArg = intermediate.addUniShapeConversion(EOpFunctionCall, *function[param].type, convArg); + if (convArg != nullptr) + setArg(param, convArg); + else + error(arg->getLoc(), "cannot convert input argument, argument", "", "%d", param); + } else { + if (wasFlattened(arg)) { + // If both formal and calling arg are to be flattened, leave that to argument + // expansion, not conversion. + if (!shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) { + // Will make a two-level subtree. + // The deepest will copy member-by-member to build the structure to pass. + // The level above that will be a two-operand EOpComma sequence that follows the copy by the + // object itself. + TVariable* internalAggregate = makeInternalVariable("aggShadow", *function[param].type); + internalAggregate->getWritableType().getQualifier().makeTemporary(); + TIntermSymbol* internalSymbolNode = new TIntermSymbol(internalAggregate->getUniqueId(), + internalAggregate->getName(), + internalAggregate->getType()); + internalSymbolNode->setLoc(arg->getLoc()); + // This makes the deepest level, the member-wise copy + TIntermAggregate* assignAgg = handleAssign(arg->getLoc(), EOpAssign, + internalSymbolNode, arg)->getAsAggregate(); + + // Now, pair that with the resulting aggregate. + assignAgg = intermediate.growAggregate(assignAgg, internalSymbolNode, arg->getLoc()); + assignAgg->setOperator(EOpComma); + assignAgg->setType(internalAggregate->getType()); + setArg(param, assignAgg); + } + } + } + } +} + +// +// Add any needed implicit expansion of calling arguments from what the shader listed to what's +// internally needed for the AST (given the constraints downstream). +// +void HlslParseContext::expandArguments(const TSourceLoc& loc, const TFunction& function, TIntermTyped*& arguments) +{ + TIntermAggregate* aggregate = arguments->getAsAggregate(); + int functionParamNumberOffset = 0; + + // Replace a single argument with a single argument. + const auto setArg = [&](int paramNum, TIntermTyped* arg) { + if (function.getParamCount() + functionParamNumberOffset == 1) + arguments = arg; + else { + if (aggregate == nullptr) + arguments = arg; + else + aggregate->getSequence()[paramNum] = arg; + } + }; + + // Replace a single argument with a list of arguments + const auto setArgList = [&](int paramNum, const TVector& args) { + if (args.size() == 1) + setArg(paramNum, args.front()); + else if (args.size() > 1) { + if (function.getParamCount() + functionParamNumberOffset == 1) { + arguments = intermediate.makeAggregate(args.front()); + std::for_each(args.begin() + 1, args.end(), + [&](TIntermTyped* arg) { + arguments = intermediate.growAggregate(arguments, arg); + }); + } else { + auto it = aggregate->getSequence().erase(aggregate->getSequence().begin() + paramNum); + aggregate->getSequence().insert(it, args.begin(), args.end()); + } + functionParamNumberOffset += (int)(args.size() - 1); + } + }; + + // Process each argument's conversion + for (int param = 0; param < function.getParamCount(); ++param) { + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermTyped* arg = function.getParamCount() == 1 + ? arguments->getAsTyped() + : (aggregate ? + aggregate->getSequence()[param + functionParamNumberOffset]->getAsTyped() : + arguments->getAsTyped()); + + if (wasFlattened(arg) && shouldFlatten(*function[param].type, function[param].type->getQualifier().storage, true)) { + // Need to pass the structure members instead of the structure. + TVector memberArgs; + for (int memb = 0; memb < (int)arg->getType().getStruct()->size(); ++memb) + memberArgs.push_back(flattenAccess(arg, memb)); + setArgList(param + functionParamNumberOffset, memberArgs); + } + } + + // TODO: if we need both hidden counter args (below) and struct expansion (above) + // the two algorithms need to be merged: Each assumes the list starts out 1:1 between + // parameters and arguments. + + // If any argument is a pass-by-reference struct buffer with an associated counter + // buffer, we have to add another hidden parameter for that counter. + if (aggregate) + addStructBuffArguments(loc, aggregate); +} + +// +// Add any needed implicit output conversions for function-call arguments. This +// can require a new tree topology, complicated further by whether the function +// has a return value. +// +// Returns a node of a subtree that evaluates to the return value of the function. +// +TIntermTyped* HlslParseContext::addOutputArgumentConversions(const TFunction& function, TIntermOperator& intermNode) +{ + assert (intermNode.getAsAggregate() != nullptr || intermNode.getAsUnaryNode() != nullptr); + + const TSourceLoc& loc = intermNode.getLoc(); + + TIntermSequence argSequence; // temp sequence for unary node args + + if (intermNode.getAsUnaryNode()) + argSequence.push_back(intermNode.getAsUnaryNode()->getOperand()); + + TIntermSequence& arguments = argSequence.empty() ? intermNode.getAsAggregate()->getSequence() : argSequence; + + const auto needsConversion = [&](int argNum) { + return function[argNum].type->getQualifier().isParamOutput() && + (*function[argNum].type != arguments[argNum]->getAsTyped()->getType() || + shouldConvertLValue(arguments[argNum]) || + wasFlattened(arguments[argNum]->getAsTyped())); + }; + + // Will there be any output conversions? + bool outputConversions = false; + for (int i = 0; i < function.getParamCount(); ++i) { + if (needsConversion(i)) { + outputConversions = true; + break; + } + } + + if (! outputConversions) + return &intermNode; + + // Setup for the new tree, if needed: + // + // Output conversions need a different tree topology. + // Out-qualified arguments need a temporary of the correct type, with the call + // followed by an assignment of the temporary to the original argument: + // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...) + // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet) + // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment. + TIntermTyped* conversionTree = nullptr; + TVariable* tempRet = nullptr; + if (intermNode.getBasicType() != EbtVoid) { + // do the "tempRet = function(...), " bit from above + tempRet = makeInternalVariable("tempReturn", intermNode.getType()); + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc); + conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, loc); + } else + conversionTree = &intermNode; + + conversionTree = intermediate.makeAggregate(conversionTree); + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + if (needsConversion(i)) { + // Out-qualified arguments needing conversion need to use the topology setup above. + // Do the " ...(tempArg, ...), arg = tempArg" bit from above. + + // Make a temporary for what the function expects the argument to look like. + TVariable* tempArg = makeInternalVariable("tempArg", *function[i].type); + tempArg->getWritableType().getQualifier().makeTemporary(); + TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, loc); + + // This makes the deepest level, the member-wise copy + TIntermTyped* tempAssign = handleAssign(arguments[i]->getLoc(), EOpAssign, arguments[i]->getAsTyped(), + tempArgNode); + tempAssign = handleLvalue(arguments[i]->getLoc(), "assign", tempAssign); + conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc()); + + // replace the argument with another node for the same tempArg variable + arguments[i] = intermediate.addSymbol(*tempArg, loc); + } + } + + // Finalize the tree topology (see bigger comment above). + if (tempRet) { + // do the "..., tempRet" bit from above + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, loc); + conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, loc); + } + + conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), loc); + + return conversionTree; +} + +// +// Add any needed "hidden" counter buffer arguments for function calls. +// +// Modifies the 'aggregate' argument if needed. Otherwise, is no-op. +// +void HlslParseContext::addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*& aggregate) +{ + // See if there are any SB types with counters. + const bool hasStructBuffArg = + std::any_of(aggregate->getSequence().begin(), + aggregate->getSequence().end(), + [this](const TIntermNode* node) { + return (node && node->getAsTyped() != nullptr) && hasStructBuffCounter(node->getAsTyped()->getType()); + }); + + // Nothing to do, if we didn't find one. + if (! hasStructBuffArg) + return; + + TIntermSequence argsWithCounterBuffers; + + for (int param = 0; param < int(aggregate->getSequence().size()); ++param) { + argsWithCounterBuffers.push_back(aggregate->getSequence()[param]); + + if (hasStructBuffCounter(aggregate->getSequence()[param]->getAsTyped()->getType())) { + const TIntermSymbol* blockSym = aggregate->getSequence()[param]->getAsSymbolNode(); + if (blockSym != nullptr) { + TType counterType; + counterBufferType(loc, counterType); + + const TString counterBlockName(intermediate.addCounterBufferName(blockSym->getName())); + + TVariable* variable = makeInternalVariable(counterBlockName, counterType); + + // Mark this buffer's counter block as being in use + structBufferCounter[counterBlockName] = true; + + TIntermSymbol* sym = intermediate.addSymbol(*variable, loc); + argsWithCounterBuffers.push_back(sym); + } + } + } + + // Swap with the temp list we've built up. + aggregate->getSequence().swap(argsWithCounterBuffers); +} + + +// +// Do additional checking of built-in function calls that is not caught +// by normal semantic checks on argument type, extension tagging, etc. +// +// Assumes there has been a semantically correct match to a built-in function prototype. +// +void HlslParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) +{ + // Set up convenience accessors to the argument(s). There is almost always + // multiple arguments for the cases below, but when there might be one, + // check the unaryArg first. + const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference + const TIntermTyped* unaryArg = nullptr; + const TIntermTyped* arg0 = nullptr; + if (callNode.getAsAggregate()) { + argp = &callNode.getAsAggregate()->getSequence(); + if (argp->size() > 0) + arg0 = (*argp)[0]->getAsTyped(); + } else { + assert(callNode.getAsUnaryNode()); + unaryArg = callNode.getAsUnaryNode()->getOperand(); + arg0 = unaryArg; + } + const TIntermSequence& aggArgs = *argp; // only valid when unaryArg is nullptr + + switch (callNode.getOp()) { + case EOpTextureGather: + case EOpTextureGatherOffset: + case EOpTextureGatherOffsets: + { + // Figure out which variants are allowed by what extensions, + // and what arguments must be constant for which situations. + + TString featureString = fnCandidate.getName() + "(...)"; + const char* feature = featureString.c_str(); + int compArg = -1; // track which argument, if any, is the constant component argument + switch (callNode.getOp()) { + case EOpTextureGather: + // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, + // otherwise, need GL_ARB_texture_gather. + if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || + fnCandidate[0].type->getSampler().shadow) { + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 2; + } + break; + case EOpTextureGatherOffset: + // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + break; + case EOpTextureGatherOffsets: + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + break; + default: + break; + } + + if (compArg > 0 && compArg < fnCandidate.getParamCount()) { + if (aggArgs[compArg]->getAsConstantUnion()) { + int value = aggArgs[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (value < 0 || value > 3) + error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); + } else + error(loc, "must be a compile-time constant:", feature, "component argument"); + } + + break; + } + + case EOpTextureOffset: + case EOpTextureFetchOffset: + case EOpTextureProjOffset: + case EOpTextureLodOffset: + case EOpTextureProjLodOffset: + case EOpTextureGradOffset: + case EOpTextureProjGradOffset: + { + // Handle texture-offset limits checking + // Pick which argument has to hold constant offsets + int arg = -1; + switch (callNode.getOp()) { + case EOpTextureOffset: arg = 2; break; + case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break; + case EOpTextureProjOffset: arg = 2; break; + case EOpTextureLodOffset: arg = 3; break; + case EOpTextureProjLodOffset: arg = 3; break; + case EOpTextureGradOffset: arg = 4; break; + case EOpTextureProjGradOffset: arg = 4; break; + default: + assert(0); + break; + } + + if (arg > 0) { + if (aggArgs[arg]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "texel offset", ""); + else { + const TType& type = aggArgs[arg]->getAsTyped()->getType(); + for (int c = 0; c < type.getVectorSize(); ++c) { + int offset = aggArgs[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); + if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) + error(loc, "value is out of range:", "texel offset", + "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); + } + } + } + + break; + } + + case EOpTextureQuerySamples: + case EOpImageQuerySamples: + break; + + case EOpImageAtomicAdd: + case EOpImageAtomicMin: + case EOpImageAtomicMax: + case EOpImageAtomicAnd: + case EOpImageAtomicOr: + case EOpImageAtomicXor: + case EOpImageAtomicExchange: + case EOpImageAtomicCompSwap: + break; + + case EOpInterpolateAtCentroid: + case EOpInterpolateAtSample: + case EOpInterpolateAtOffset: + // Make sure the first argument is an interpolant, or an array element of an interpolant + if (arg0->getType().getQualifier().storage != EvqVaryingIn) { + // It might still be an array element. + // + // We could check more, but the semantics of the first argument are already met; the + // only way to turn an array into a float/vec* is array dereference and swizzle. + // + // ES and desktop 4.3 and earlier: swizzles may not be used + // desktop 4.4 and later: swizzles may be used + const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true); + if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn) + error(loc, "first argument must be an interpolant, or interpolant-array element", + fnCandidate.getName().c_str(), ""); + } + break; + + default: + break; + } +} + +// +// Handle seeing something in a grammar production that can be done by calling +// a constructor. +// +// The constructor still must be "handled" by handleFunctionCall(), which will +// then call handleConstructor(). +// +TFunction* HlslParseContext::makeConstructorCall(const TSourceLoc& loc, const TType& type) +{ + TOperator op = intermediate.mapTypeToConstructorOp(type); + + if (op == EOpNull) { + error(loc, "cannot construct this type", type.getBasicString(), ""); + return nullptr; + } + + TString empty(""); + + return new TFunction(&empty, type, op); +} + +// +// Handle seeing a "COLON semantic" at the end of a type declaration, +// by updating the type according to the semantic. +// +void HlslParseContext::handleSemantic(TSourceLoc loc, TQualifier& qualifier, TBuiltInVariable builtIn, + const TString& upperCase) +{ + // Parse and return semantic number. If limit is 0, it will be ignored. Otherwise, if the parsed + // semantic number is >= limit, errorMsg is issued and 0 is returned. + // TODO: it would be nicer if limit and errorMsg had default parameters, but some compilers don't yet + // accept those in lambda functions. + const auto getSemanticNumber = [this, loc](const TString& semantic, unsigned int limit, const char* errorMsg) -> unsigned int { + size_t pos = semantic.find_last_not_of("0123456789"); + if (pos == std::string::npos) + return 0u; + + unsigned int semanticNum = (unsigned int)atoi(semantic.c_str() + pos + 1); + + if (limit != 0 && semanticNum >= limit) { + error(loc, errorMsg, semantic.c_str(), ""); + return 0u; + } + + return semanticNum; + }; + + if (builtIn == EbvNone && hlslDX9Compatible()) { + if (language == EShLangVertex) { + if (qualifier.isParamOutput()) { + if (upperCase == "POSITION") { + builtIn = EbvPosition; + } + if (upperCase == "PSIZE") { + builtIn = EbvPointSize; + } + } + } else if (language == EShLangFragment) { + if (qualifier.isParamInput() && upperCase == "VPOS") { + builtIn = EbvFragCoord; + } + if (qualifier.isParamOutput()) { + if (upperCase.compare(0, 5, "COLOR") == 0) { + qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr); + nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u); + } + if (upperCase == "DEPTH") { + builtIn = EbvFragDepth; + } + } + } + } + + switch(builtIn) { + case EbvNone: + // Get location numbers from fragment outputs, instead of + // auto-assigning them. + if (language == EShLangFragment && upperCase.compare(0, 9, "SV_TARGET") == 0) { + qualifier.layoutLocation = getSemanticNumber(upperCase, 0, nullptr); + nextOutLocation = std::max(nextOutLocation, qualifier.layoutLocation + 1u); + } else if (upperCase.compare(0, 15, "SV_CLIPDISTANCE") == 0) { + builtIn = EbvClipDistance; + qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid clip semantic"); + } else if (upperCase.compare(0, 15, "SV_CULLDISTANCE") == 0) { + builtIn = EbvCullDistance; + qualifier.layoutLocation = getSemanticNumber(upperCase, maxClipCullRegs, "invalid cull semantic"); + } + break; + case EbvPosition: + // adjust for stage in/out + if (language == EShLangFragment) + builtIn = EbvFragCoord; + break; + case EbvFragStencilRef: + error(loc, "unimplemented; need ARB_shader_stencil_export", "SV_STENCILREF", ""); + break; + case EbvTessLevelInner: + case EbvTessLevelOuter: + qualifier.patch = true; + break; + default: + break; + } + + if (qualifier.builtIn == EbvNone) + qualifier.builtIn = builtIn; + qualifier.semanticName = intermediate.addSemanticName(upperCase); +} + +// +// Handle seeing something like "PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN" +// +// 'location' has the "c[Subcomponent]" part. +// 'component' points to the "component" part, or nullptr if not present. +// +void HlslParseContext::handlePackOffset(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString& location, + const glslang::TString* component) +{ + if (location.size() == 0 || location[0] != 'c') { + error(loc, "expected 'c'", "packoffset", ""); + return; + } + if (location.size() == 1) + return; + if (! isdigit(location[1])) { + error(loc, "expected number after 'c'", "packoffset", ""); + return; + } + + qualifier.layoutOffset = 16 * atoi(location.substr(1, location.size()).c_str()); + if (component != nullptr) { + int componentOffset = 0; + switch ((*component)[0]) { + case 'x': componentOffset = 0; break; + case 'y': componentOffset = 4; break; + case 'z': componentOffset = 8; break; + case 'w': componentOffset = 12; break; + default: + componentOffset = -1; + break; + } + if (componentOffset < 0 || component->size() > 1) { + error(loc, "expected {x, y, z, w} for component", "packoffset", ""); + return; + } + qualifier.layoutOffset += componentOffset; + } +} + +// +// Handle seeing something like "REGISTER LEFT_PAREN [shader_profile,] Type# RIGHT_PAREN" +// +// 'profile' points to the shader_profile part, or nullptr if not present. +// 'desc' is the type# part. +// +void HlslParseContext::handleRegister(const TSourceLoc& loc, TQualifier& qualifier, const glslang::TString* profile, + const glslang::TString& desc, int subComponent, const glslang::TString* spaceDesc) +{ + if (profile != nullptr) + warn(loc, "ignoring shader_profile", "register", ""); + + if (desc.size() < 1) { + error(loc, "expected register type", "register", ""); + return; + } + + int regNumber = 0; + if (desc.size() > 1) { + if (isdigit(desc[1])) + regNumber = atoi(desc.substr(1, desc.size()).c_str()); + else { + error(loc, "expected register number after register type", "register", ""); + return; + } + } + + // more information about register types see + // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-variable-register + const std::vector& resourceInfo = intermediate.getResourceSetBinding(); + switch (std::tolower(desc[0])) { + case 'c': + // c register is the register slot in the global const buffer + // each slot is a vector of 4 32 bit components + qualifier.layoutOffset = regNumber * 4 * 4; + break; + // const buffer register slot + case 'b': + // textrues and structured buffers + case 't': + // samplers + case 's': + // uav resources + case 'u': + // if nothing else has set the binding, do so now + // (other mechanisms override this one) + if (!qualifier.hasBinding()) + qualifier.layoutBinding = regNumber + subComponent; + + // This handles per-register layout sets numbers. For the global mode which sets + // every symbol to the same value, see setLinkageLayoutSets(). + if ((resourceInfo.size() % 3) == 0) { + // Apply per-symbol resource set and binding. + for (auto it = resourceInfo.cbegin(); it != resourceInfo.cend(); it = it + 3) { + if (strcmp(desc.c_str(), it[0].c_str()) == 0) { + qualifier.layoutSet = atoi(it[1].c_str()); + qualifier.layoutBinding = atoi(it[2].c_str()) + subComponent; + break; + } + } + } + break; + default: + warn(loc, "ignoring unrecognized register type", "register", "%c", desc[0]); + break; + } + + // space + unsigned int setNumber; + const auto crackSpace = [&]() -> bool { + const int spaceLen = 5; + if (spaceDesc->size() < spaceLen + 1) + return false; + if (spaceDesc->compare(0, spaceLen, "space") != 0) + return false; + if (! isdigit((*spaceDesc)[spaceLen])) + return false; + setNumber = atoi(spaceDesc->substr(spaceLen, spaceDesc->size()).c_str()); + return true; + }; + + // if nothing else has set the set, do so now + // (other mechanisms override this one) + if (spaceDesc && !qualifier.hasSet()) { + if (! crackSpace()) { + error(loc, "expected spaceN", "register", ""); + return; + } + qualifier.layoutSet = setNumber; + } +} + +// Convert to a scalar boolean, or if not allowed by HLSL semantics, +// report an error and return nullptr. +TIntermTyped* HlslParseContext::convertConditionalExpression(const TSourceLoc& loc, TIntermTyped* condition, + bool mustBeScalar) +{ + if (mustBeScalar && !condition->getType().isScalarOrVec1()) { + error(loc, "requires a scalar", "conditional expression", ""); + return nullptr; + } + + return intermediate.addConversion(EOpConstructBool, TType(EbtBool, EvqTemporary, condition->getVectorSize()), + condition); +} + +// +// Same error message for all places assignments don't work. +// +void HlslParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, "", op, "cannot convert from '%s' to '%s'", + right.c_str(), left.c_str()); +} + +// +// Same error message for all places unary operations don't work. +// +void HlslParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) +{ + error(loc, " wrong operand type", op, + "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)", + op, operand.c_str()); +} + +// +// Same error message for all binary operations don't work. +// +void HlslParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, " wrong operand types:", op, + "no operation '%s' exists that takes a left-hand operand of type '%s' and " + "a right operand of type '%s' (or there is no acceptable conversion)", + op, left.c_str(), right.c_str()); +} + +// +// A basic type of EbtVoid is a key that the name string was seen in the source, but +// it was not found as a variable in the symbol table. If so, give the error +// message and insert a dummy variable in the symbol table to prevent future errors. +// +void HlslParseContext::variableCheck(TIntermTyped*& nodePtr) +{ + TIntermSymbol* symbol = nodePtr->getAsSymbolNode(); + if (! symbol) + return; + + if (symbol->getType().getBasicType() == EbtVoid) { + error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), ""); + + // Add to symbol table to prevent future error messages on the same name + if (symbol->getName().size() > 0) { + TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat)); + symbolTable.insert(*fakeVariable); + + // substitute a symbol node for this new variable + nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc()); + } + } +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// a constant. +// +void HlslParseContext::constantValueCheck(TIntermTyped* node, const char* token) +{ + if (node->getQualifier().storage != EvqConst) + error(node->getLoc(), "constant expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// an integer. +// +void HlslParseContext::integerCheck(const TIntermTyped* node, const char* token) +{ + if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar()) + return; + + error(node->getLoc(), "scalar integer expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if we are currently +// globally scoped. +// +void HlslParseContext::globalCheck(const TSourceLoc& loc, const char* token) +{ + if (! symbolTable.atGlobalLevel()) + error(loc, "not allowed in nested scope", token, ""); +} + +bool HlslParseContext::builtInName(const TString& /*identifier*/) +{ + return false; +} + +// +// Make sure there is enough data and not too many arguments provided to the +// constructor to build something of the type of the constructor. Also returns +// the type of the constructor. +// +// Returns true if there was an error in construction. +// +bool HlslParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, + TOperator op, TType& type) +{ + type.shallowCopy(function.getType()); + + bool constructingMatrix = false; + switch (op) { + case EOpConstructTextureSampler: + error(loc, "unhandled texture constructor", "constructor", ""); + return true; + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructIMat2x2: + case EOpConstructIMat2x3: + case EOpConstructIMat2x4: + case EOpConstructIMat3x2: + case EOpConstructIMat3x3: + case EOpConstructIMat3x4: + case EOpConstructIMat4x2: + case EOpConstructIMat4x3: + case EOpConstructIMat4x4: + case EOpConstructUMat2x2: + case EOpConstructUMat2x3: + case EOpConstructUMat2x4: + case EOpConstructUMat3x2: + case EOpConstructUMat3x3: + case EOpConstructUMat3x4: + case EOpConstructUMat4x2: + case EOpConstructUMat4x3: + case EOpConstructUMat4x4: + case EOpConstructBMat2x2: + case EOpConstructBMat2x3: + case EOpConstructBMat2x4: + case EOpConstructBMat3x2: + case EOpConstructBMat3x3: + case EOpConstructBMat3x4: + case EOpConstructBMat4x2: + case EOpConstructBMat4x3: + case EOpConstructBMat4x4: + constructingMatrix = true; + break; + default: + break; + } + + // + // Walk the arguments for first-pass checks and collection of information. + // + + int size = 0; + bool constType = true; + bool full = false; + bool overFull = false; + bool matrixInMatrix = false; + bool arrayArg = false; + for (int arg = 0; arg < function.getParamCount(); ++arg) { + if (function[arg].type->isArray()) { + if (function[arg].type->isUnsizedArray()) { + // Can't construct from an unsized array. + error(loc, "array argument must be sized", "constructor", ""); + return true; + } + arrayArg = true; + } + if (constructingMatrix && function[arg].type->isMatrix()) + matrixInMatrix = true; + + // 'full' will go to true when enough args have been seen. If we loop + // again, there is an extra argument. + if (full) { + // For vectors and matrices, it's okay to have too many components + // available, but not okay to have unused arguments. + overFull = true; + } + + size += function[arg].type->computeNumComponents(); + if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents()) + full = true; + + if (function[arg].type->getQualifier().storage != EvqConst) + constType = false; + } + + if (constType) + type.getQualifier().storage = EvqConst; + + if (type.isArray()) { + if (function.getParamCount() == 0) { + error(loc, "array constructor must have at least one argument", "constructor", ""); + return true; + } + + if (type.isUnsizedArray()) { + // auto adapt the constructor type to the number of arguments + type.changeOuterArraySize(function.getParamCount()); + } else if (type.getOuterArraySize() != function.getParamCount() && type.computeNumComponents() > size) { + error(loc, "array constructor needs one argument per array element", "constructor", ""); + return true; + } + + if (type.isArrayOfArrays()) { + // Types have to match, but we're still making the type. + // Finish making the type, and the comparison is done later + // when checking for conversion. + TArraySizes& arraySizes = *type.getArraySizes(); + + // At least the dimensionalities have to match. + if (! function[0].type->isArray() || + arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) { + error(loc, "array constructor argument not correct type to construct array element", "constructor", ""); + return true; + } + + if (arraySizes.isInnerUnsized()) { + // "Arrays of arrays ..., and the size for any dimension is optional" + // That means we need to adopt (from the first argument) the other array sizes into the type. + for (int d = 1; d < arraySizes.getNumDims(); ++d) { + if (arraySizes.getDimSize(d) == UnsizedArraySize) { + arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1)); + } + } + } + } + } + + // Some array -> array type casts are okay + if (arrayArg && function.getParamCount() == 1 && op != EOpConstructStruct && type.isArray() && + !type.isArrayOfArrays() && !function[0].type->isArrayOfArrays() && + type.getVectorSize() >= 1 && function[0].type->getVectorSize() >= 1) + return false; + + if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) { + error(loc, "constructing non-array constituent from array argument", "constructor", ""); + return true; + } + + if (matrixInMatrix && ! type.isArray()) { + return false; + } + + if (overFull) { + error(loc, "too many arguments", "constructor", ""); + return true; + } + + if (op == EOpConstructStruct && ! type.isArray()) { + if (isScalarConstructor(node)) + return false; + + // Self-type construction: e.g, we can construct a struct from a single identically typed object. + if (function.getParamCount() == 1 && type == *function[0].type) + return false; + + if ((int)type.getStruct()->size() != function.getParamCount()) { + error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", ""); + return true; + } + } + + if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) || + (op == EOpConstructStruct && size < type.computeNumComponents())) { + error(loc, "not enough data provided for construction", "constructor", ""); + return true; + } + + return false; +} + +// See if 'node', in the context of constructing aggregates, is a scalar argument +// to a constructor. +// +bool HlslParseContext::isScalarConstructor(const TIntermNode* node) +{ + // Obviously, it must be a scalar, but an aggregate node might not be fully + // completed yet: holding a sequence of initializers under an aggregate + // would not yet be typed, so don't check it's type. This corresponds to + // the aggregate operator also not being set yet. (An aggregate operation + // that legitimately yields a scalar will have a getOp() of that operator, + // not EOpNull.) + + return node->getAsTyped() != nullptr && + node->getAsTyped()->isScalar() && + (node->getAsAggregate() == nullptr || node->getAsAggregate()->getOp() != EOpNull); +} + +// Checks to see if a void variable has been declared and raise an error message for such a case +// +// returns true in case of an error +// +bool HlslParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) +{ + if (basicType == EbtVoid) { + error(loc, "illegal use of type 'void'", identifier.c_str(), ""); + return true; + } + + return false; +} + +// +// Fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. +// +void HlslParseContext::globalQualifierFix(const TSourceLoc&, TQualifier& qualifier) +{ + // move from parameter/unknown qualifiers to pipeline in/out qualifiers + switch (qualifier.storage) { + case EvqIn: + qualifier.storage = EvqVaryingIn; + break; + case EvqOut: + qualifier.storage = EvqVaryingOut; + break; + default: + break; + } +} + +// +// Merge characteristics of the 'src' qualifier into the 'dst'. +// If there is duplication, issue error messages, unless 'force' +// is specified, which means to just override default settings. +// +// Also, when force is false, it will be assumed that 'src' follows +// 'dst', for the purpose of error checking order for versions +// that require specific orderings of qualifiers. +// +void HlslParseContext::mergeQualifiers(TQualifier& dst, const TQualifier& src) +{ + // Storage qualification + if (dst.storage == EvqTemporary || dst.storage == EvqGlobal) + dst.storage = src.storage; + else if ((dst.storage == EvqIn && src.storage == EvqOut) || + (dst.storage == EvqOut && src.storage == EvqIn)) + dst.storage = EvqInOut; + else if ((dst.storage == EvqIn && src.storage == EvqConst) || + (dst.storage == EvqConst && src.storage == EvqIn)) + dst.storage = EvqConstReadOnly; + + // Layout qualifiers + mergeObjectLayoutQualifiers(dst, src, false); + + // individual qualifiers + bool repeated = false; +#define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field; + MERGE_SINGLETON(invariant); + MERGE_SINGLETON(noContraction); + MERGE_SINGLETON(centroid); + MERGE_SINGLETON(smooth); + MERGE_SINGLETON(flat); + MERGE_SINGLETON(nopersp); + MERGE_SINGLETON(patch); + MERGE_SINGLETON(sample); + MERGE_SINGLETON(coherent); + MERGE_SINGLETON(volatil); + MERGE_SINGLETON(restrict); + MERGE_SINGLETON(readonly); + MERGE_SINGLETON(writeonly); + MERGE_SINGLETON(specConstant); + MERGE_SINGLETON(nonUniform); +} + +// used to flatten the sampler type space into a single dimension +// correlates with the declaration of defaultSamplerPrecision[] +int HlslParseContext::computeSamplerTypeIndex(TSampler& sampler) +{ + int arrayIndex = sampler.arrayed ? 1 : 0; + int shadowIndex = sampler.shadow ? 1 : 0; + int externalIndex = sampler.external ? 1 : 0; + + return EsdNumDims * + (EbtNumTypes * (2 * (2 * arrayIndex + shadowIndex) + externalIndex) + sampler.type) + sampler.dim; +} + +// +// Do size checking for an array type's size. +// +void HlslParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair) +{ + bool isConst = false; + sizePair.size = 1; + sizePair.node = nullptr; + + TIntermConstantUnion* constant = expr->getAsConstantUnion(); + if (constant) { + // handle true (non-specialization) constant + sizePair.size = constant->getConstArray()[0].getIConst(); + isConst = true; + } else { + // see if it's a specialization constant instead + if (expr->getQualifier().isSpecConstant()) { + isConst = true; + sizePair.node = expr; + TIntermSymbol* symbol = expr->getAsSymbolNode(); + if (symbol && symbol->getConstArray().size() > 0) + sizePair.size = symbol->getConstArray()[0].getIConst(); + } + } + + if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) { + error(loc, "array size must be a constant integer expression", "", ""); + return; + } + + if (sizePair.size <= 0) { + error(loc, "array size must be a positive integer", "", ""); + return; + } +} + +// +// Require array to be completely sized +// +void HlslParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) +{ + if (arraySizes.hasUnsized()) + error(loc, "array size required", "", ""); +} + +void HlslParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) +{ + const TTypeList& structure = *type.getStruct(); + for (int m = 0; m < (int)structure.size(); ++m) { + const TType& member = *structure[m].type; + if (member.isArray()) + arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes()); + } +} + +// +// Do all the semantic checking for declaring or redeclaring an array, with and +// without a size, and make the right changes to the symbol table. +// +void HlslParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, + TSymbol*& symbol, bool track) +{ + if (symbol == nullptr) { + bool currentScope; + symbol = symbolTable.find(identifier, nullptr, ¤tScope); + + if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) { + // bad shader (errors already reported) trying to redeclare a built-in name as an array + return; + } + if (symbol == nullptr || ! currentScope) { + // + // Successfully process a new definition. + // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations) + // + symbol = new TVariable(&identifier, type); + symbolTable.insert(*symbol); + if (track && symbolTable.atGlobalLevel()) + trackLinkage(*symbol); + + return; + } + if (symbol->getAsAnonMember()) { + error(loc, "cannot redeclare a user-block member array", identifier.c_str(), ""); + symbol = nullptr; + return; + } + } + + // + // Process a redeclaration. + // + + if (symbol == nullptr) { + error(loc, "array variable name expected", identifier.c_str(), ""); + return; + } + + // redeclareBuiltinVariable() should have already done the copyUp() + TType& existingType = symbol->getWritableType(); + + if (existingType.isSizedArray()) { + // be more lenient for input arrays to geometry shaders and tessellation control outputs, + // where the redeclaration is the same size + return; + } + + existingType.updateArraySizes(type); +} + +// +// Enforce non-initializer type/qualifier rules. +// +void HlslParseContext::fixConstInit(const TSourceLoc& loc, const TString& identifier, TType& type, + TIntermTyped*& initializer) +{ + // + // Make the qualifier make sense, given that there is an initializer. + // + if (initializer == nullptr) { + if (type.getQualifier().storage == EvqConst || + type.getQualifier().storage == EvqConstReadOnly) { + initializer = intermediate.makeAggregate(loc); + warn(loc, "variable with qualifier 'const' not initialized; zero initializing", identifier.c_str(), ""); + } + } +} + +// +// See if the identifier is a built-in symbol that can be redeclared, and if so, +// copy the symbol table's read-only built-in variable to the current +// global level, where it can be modified based on the passed in type. +// +// Returns nullptr if no redeclaration took place; meaning a normal declaration still +// needs to occur for it, not necessarily an error. +// +// Returns a redeclared and type-modified variable if a redeclared occurred. +// +TSymbol* HlslParseContext::redeclareBuiltinVariable(const TSourceLoc& /*loc*/, const TString& identifier, + const TQualifier& /*qualifier*/, + const TShaderQualifiers& /*publicType*/) +{ + if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel()) + return nullptr; + + return nullptr; +} + +// +// Generate index to the array element in a structure buffer (SSBO) +// +TIntermTyped* HlslParseContext::indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const +{ + // Bail out if not a struct buffer + if (buffer == nullptr || ! isStructBufferType(buffer->getType())) + return nullptr; + + // Runtime sized array is always the last element. + const TTypeList* bufferStruct = buffer->getType().getStruct(); + TIntermTyped* arrayPosition = intermediate.addConstantUnion(unsigned(bufferStruct->size()-1), loc); + + TIntermTyped* argArray = intermediate.addIndex(EOpIndexDirectStruct, buffer, arrayPosition, loc); + argArray->setType(*(*bufferStruct)[bufferStruct->size()-1].type); + + return argArray; +} + +// +// IFF type is a structuredbuffer/byteaddressbuffer type, return the content +// (template) type. E.g, StructuredBuffer -> MyType. Else return nullptr. +// +TType* HlslParseContext::getStructBufferContentType(const TType& type) const +{ + if (type.getBasicType() != EbtBlock || type.getQualifier().storage != EvqBuffer) + return nullptr; + + const int memberCount = (int)type.getStruct()->size(); + assert(memberCount > 0); + + TType* contentType = (*type.getStruct())[memberCount-1].type; + + return contentType->isUnsizedArray() ? contentType : nullptr; +} + +// +// If an existing struct buffer has a sharable type, then share it. +// +void HlslParseContext::shareStructBufferType(TType& type) +{ + // PackOffset must be equivalent to share types on a per-member basis. + // Note: cannot use auto type due to recursion. Thus, this is a std::function. + const std::function + compareQualifiers = [&](TType& lhs, TType& rhs) -> bool { + if (lhs.getQualifier().layoutOffset != rhs.getQualifier().layoutOffset) + return false; + + if (lhs.isStruct() != rhs.isStruct()) + return false; + + if (lhs.isStruct() && rhs.isStruct()) { + if (lhs.getStruct()->size() != rhs.getStruct()->size()) + return false; + + for (int i = 0; i < int(lhs.getStruct()->size()); ++i) + if (!compareQualifiers(*(*lhs.getStruct())[i].type, *(*rhs.getStruct())[i].type)) + return false; + } + + return true; + }; + + // We need to compare certain qualifiers in addition to the type. + const auto typeEqual = [compareQualifiers](TType& lhs, TType& rhs) -> bool { + if (lhs.getQualifier().readonly != rhs.getQualifier().readonly) + return false; + + // If both are structures, recursively look for packOffset equality + // as well as type equality. + return compareQualifiers(lhs, rhs) && lhs == rhs; + }; + + // This is an exhaustive O(N) search, but real world shaders have + // only a small number of these. + for (int idx = 0; idx < int(structBufferTypes.size()); ++idx) { + // If the deep structure matches, modulo qualifiers, use it + if (typeEqual(*structBufferTypes[idx], type)) { + type.shallowCopy(*structBufferTypes[idx]); + return; + } + } + + // Otherwise, remember it: + TType* typeCopy = new TType; + typeCopy->shallowCopy(type); + structBufferTypes.push_back(typeCopy); +} + +void HlslParseContext::paramFix(TType& type) +{ + switch (type.getQualifier().storage) { + case EvqConst: + type.getQualifier().storage = EvqConstReadOnly; + break; + case EvqGlobal: + case EvqTemporary: + type.getQualifier().storage = EvqIn; + break; + case EvqBuffer: + { + // SSBO parameter. These do not go through the declareBlock path since they are fn parameters. + correctUniform(type.getQualifier()); + TQualifier bufferQualifier = globalBufferDefaults; + mergeObjectLayoutQualifiers(bufferQualifier, type.getQualifier(), true); + bufferQualifier.storage = type.getQualifier().storage; + bufferQualifier.readonly = type.getQualifier().readonly; + bufferQualifier.coherent = type.getQualifier().coherent; + bufferQualifier.declaredBuiltIn = type.getQualifier().declaredBuiltIn; + type.getQualifier() = bufferQualifier; + break; + } + default: + break; + } +} + +void HlslParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (type.containsSpecializationSize()) + error(loc, "can't use with types containing arrays sized with a specialization constant", op, ""); +} + +// +// Layout qualifier stuff. +// + +// Put the id's layout qualification into the public type, for qualifiers not having a number set. +// This is before we know any type information for error checking. +void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id) +{ + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) { + qualifier.layoutMatrix = ElmRowMajor; + return; + } + if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) { + qualifier.layoutMatrix = ElmColumnMajor; + return; + } + if (id == "push_constant") { + requireVulkan(loc, "push_constant"); + qualifier.layoutPushConstant = true; + return; + } + if (language == EShLangGeometry || language == EShLangTessEvaluation) { + if (id == TQualifier::getGeometryString(ElgTriangles)) { + // publicType.shaderQualifiers.geometry = ElgTriangles; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (language == EShLangGeometry) { + if (id == TQualifier::getGeometryString(ElgPoints)) { + // publicType.shaderQualifiers.geometry = ElgPoints; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgLineStrip)) { + // publicType.shaderQualifiers.geometry = ElgLineStrip; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgLines)) { + // publicType.shaderQualifiers.geometry = ElgLines; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) { + // publicType.shaderQualifiers.geometry = ElgLinesAdjacency; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) { + // publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgTriangleStrip)) { + // publicType.shaderQualifiers.geometry = ElgTriangleStrip; + warn(loc, "ignored", id.c_str(), ""); + return; + } + } else { + assert(language == EShLangTessEvaluation); + + // input primitive + if (id == TQualifier::getGeometryString(ElgTriangles)) { + // publicType.shaderQualifiers.geometry = ElgTriangles; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgQuads)) { + // publicType.shaderQualifiers.geometry = ElgQuads; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getGeometryString(ElgIsolines)) { + // publicType.shaderQualifiers.geometry = ElgIsolines; + warn(loc, "ignored", id.c_str(), ""); + return; + } + + // vertex spacing + if (id == TQualifier::getVertexSpacingString(EvsEqual)) { + // publicType.shaderQualifiers.spacing = EvsEqual; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) { + // publicType.shaderQualifiers.spacing = EvsFractionalEven; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) { + // publicType.shaderQualifiers.spacing = EvsFractionalOdd; + warn(loc, "ignored", id.c_str(), ""); + return; + } + + // triangle order + if (id == TQualifier::getVertexOrderString(EvoCw)) { + // publicType.shaderQualifiers.order = EvoCw; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == TQualifier::getVertexOrderString(EvoCcw)) { + // publicType.shaderQualifiers.order = EvoCcw; + warn(loc, "ignored", id.c_str(), ""); + return; + } + + // point mode + if (id == "point_mode") { + // publicType.shaderQualifiers.pointMode = true; + warn(loc, "ignored", id.c_str(), ""); + return; + } + } + } + if (language == EShLangFragment) { + if (id == "origin_upper_left") { + // publicType.shaderQualifiers.originUpperLeft = true; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "pixel_center_integer") { + // publicType.shaderQualifiers.pixelCenterInteger = true; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "early_fragment_tests") { + // publicType.shaderQualifiers.earlyFragmentTests = true; + warn(loc, "ignored", id.c_str(), ""); + return; + } + for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth + 1)) { + if (id == TQualifier::getLayoutDepthString(depth)) { + // publicType.shaderQualifiers.layoutDepth = depth; + warn(loc, "ignored", id.c_str(), ""); + return; + } + } + if (id.compare(0, 13, "blend_support") == 0) { + bool found = false; + for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { + if (id == TQualifier::getBlendEquationString(be)) { + requireExtensions(loc, 1, &E_GL_KHR_blend_equation_advanced, "blend equation"); + intermediate.addBlendEquation(be); + // publicType.shaderQualifiers.blendEquation = true; + warn(loc, "ignored", id.c_str(), ""); + found = true; + break; + } + } + if (! found) + error(loc, "unknown blend equation", "blend_support", ""); + return; + } + } + error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), ""); +} + +// Put the id's layout qualifier value into the public type, for qualifiers having a number set. +// This is before we know any type information for error checking. +void HlslParseContext::setLayoutQualifier(const TSourceLoc& loc, TQualifier& qualifier, TString& id, + const TIntermTyped* node) +{ + const char* feature = "layout-id value"; + // const char* nonLiteralFeature = "non-literal layout-id value"; + + integerCheck(node, feature); + const TIntermConstantUnion* constUnion = node->getAsConstantUnion(); + int value = 0; + if (constUnion) { + value = constUnion->getConstArray()[0].getIConst(); + } + + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == "offset") { + qualifier.layoutOffset = value; + return; + } else if (id == "align") { + // "The specified alignment must be a power of 2, or a compile-time error results." + if (! IsPow2(value)) + error(loc, "must be a power of 2", "align", ""); + else + qualifier.layoutAlign = value; + return; + } else if (id == "location") { + if ((unsigned int)value >= TQualifier::layoutLocationEnd) + error(loc, "location is too large", id.c_str(), ""); + else + qualifier.layoutLocation = value; + return; + } else if (id == "set") { + if ((unsigned int)value >= TQualifier::layoutSetEnd) + error(loc, "set is too large", id.c_str(), ""); + else + qualifier.layoutSet = value; + return; + } else if (id == "binding") { + if ((unsigned int)value >= TQualifier::layoutBindingEnd) + error(loc, "binding is too large", id.c_str(), ""); + else + qualifier.layoutBinding = value; + return; + } else if (id == "component") { + if ((unsigned)value >= TQualifier::layoutComponentEnd) + error(loc, "component is too large", id.c_str(), ""); + else + qualifier.layoutComponent = value; + return; + } else if (id.compare(0, 4, "xfb_") == 0) { + // "Any shader making any static use (after preprocessing) of any of these + // *xfb_* qualifiers will cause the shader to be in a transform feedback + // capturing mode and hence responsible for describing the transform feedback + // setup." + intermediate.setXfbMode(); + if (id == "xfb_buffer") { + // "It is a compile-time error to specify an *xfb_buffer* that is greater than + // the implementation-dependent constant gl_MaxTransformFeedbackBuffers." + if (value >= resources.maxTransformFeedbackBuffers) + error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", + resources.maxTransformFeedbackBuffers); + if (value >= (int)TQualifier::layoutXfbBufferEnd) + error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd - 1); + else + qualifier.layoutXfbBuffer = value; + return; + } else if (id == "xfb_offset") { + if (value >= (int)TQualifier::layoutXfbOffsetEnd) + error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd - 1); + else + qualifier.layoutXfbOffset = value; + return; + } else if (id == "xfb_stride") { + // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the + // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." + if (value > 4 * resources.maxTransformFeedbackInterleavedComponents) + error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", + resources.maxTransformFeedbackInterleavedComponents); + else if (value >= (int)TQualifier::layoutXfbStrideEnd) + error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd - 1); + if (value < (int)TQualifier::layoutXfbStrideEnd) + qualifier.layoutXfbStride = value; + return; + } + } + + if (id == "input_attachment_index") { + requireVulkan(loc, "input_attachment_index"); + if (value >= (int)TQualifier::layoutAttachmentEnd) + error(loc, "attachment index is too large", id.c_str(), ""); + else + qualifier.layoutAttachment = value; + return; + } + if (id == "constant_id") { + setSpecConstantId(loc, qualifier, value); + return; + } + + switch (language) { + case EShLangVertex: + break; + + case EShLangTessControl: + if (id == "vertices") { + if (value == 0) + error(loc, "must be greater than 0", "vertices", ""); + else + // publicType.shaderQualifiers.vertices = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + break; + + case EShLangTessEvaluation: + break; + + case EShLangGeometry: + if (id == "invocations") { + if (value == 0) + error(loc, "must be at least 1", "invocations", ""); + else + // publicType.shaderQualifiers.invocations = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "max_vertices") { + // publicType.shaderQualifiers.vertices = value; + warn(loc, "ignored", id.c_str(), ""); + if (value > resources.maxGeometryOutputVertices) + error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", ""); + return; + } + if (id == "stream") { + qualifier.layoutStream = value; + return; + } + break; + + case EShLangFragment: + if (id == "index") { + qualifier.layoutIndex = value; + return; + } + break; + + case EShLangCompute: + if (id.compare(0, 11, "local_size_") == 0) { + if (id == "local_size_x") { + // publicType.shaderQualifiers.localSize[0] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "local_size_y") { + // publicType.shaderQualifiers.localSize[1] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "local_size_z") { + // publicType.shaderQualifiers.localSize[2] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (spvVersion.spv != 0) { + if (id == "local_size_x_id") { + // publicType.shaderQualifiers.localSizeSpecId[0] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "local_size_y_id") { + // publicType.shaderQualifiers.localSizeSpecId[1] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + if (id == "local_size_z_id") { + // publicType.shaderQualifiers.localSizeSpecId[2] = value; + warn(loc, "ignored", id.c_str(), ""); + return; + } + } + } + break; + + default: + break; + } + + error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), ""); +} + +void HlslParseContext::setSpecConstantId(const TSourceLoc& loc, TQualifier& qualifier, int value) +{ + if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { + error(loc, "specialization-constant id is too large", "constant_id", ""); + } else { + qualifier.layoutSpecConstantId = value; + qualifier.specConstant = true; + if (! intermediate.addUsedConstantId(value)) + error(loc, "specialization-constant id already used", "constant_id", ""); + } + return; +} + +// Merge any layout qualifier information from src into dst, leaving everything else in dst alone +// +// "More than one layout qualifier may appear in a single declaration. +// Additionally, the same layout-qualifier-name can occur multiple times +// within a layout qualifier or across multiple layout qualifiers in the +// same declaration. When the same layout-qualifier-name occurs +// multiple times, in a single declaration, the last occurrence overrides +// the former occurrence(s). Further, if such a layout-qualifier-name +// will effect subsequent declarations or other observable behavior, it +// is only the last occurrence that will have any effect, behaving as if +// the earlier occurrence(s) within the declaration are not present. +// This is also true for overriding layout-qualifier-names, where one +// overrides the other (e.g., row_major vs. column_major); only the last +// occurrence has any effect." +// +void HlslParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) +{ + if (src.hasMatrix()) + dst.layoutMatrix = src.layoutMatrix; + if (src.hasPacking()) + dst.layoutPacking = src.layoutPacking; + + if (src.hasStream()) + dst.layoutStream = src.layoutStream; + + if (src.hasFormat()) + dst.layoutFormat = src.layoutFormat; + + if (src.hasXfbBuffer()) + dst.layoutXfbBuffer = src.layoutXfbBuffer; + + if (src.hasAlign()) + dst.layoutAlign = src.layoutAlign; + + if (! inheritOnly) { + if (src.hasLocation()) + dst.layoutLocation = src.layoutLocation; + if (src.hasComponent()) + dst.layoutComponent = src.layoutComponent; + if (src.hasIndex()) + dst.layoutIndex = src.layoutIndex; + + if (src.hasOffset()) + dst.layoutOffset = src.layoutOffset; + + if (src.hasSet()) + dst.layoutSet = src.layoutSet; + if (src.layoutBinding != TQualifier::layoutBindingEnd) + dst.layoutBinding = src.layoutBinding; + + if (src.hasXfbStride()) + dst.layoutXfbStride = src.layoutXfbStride; + if (src.hasXfbOffset()) + dst.layoutXfbOffset = src.layoutXfbOffset; + if (src.hasAttachment()) + dst.layoutAttachment = src.layoutAttachment; + if (src.hasSpecConstantId()) + dst.layoutSpecConstantId = src.layoutSpecConstantId; + + if (src.layoutPushConstant) + dst.layoutPushConstant = true; + } +} + + +// +// Look up a function name in the symbol table, and make sure it is a function. +// +// First, look for an exact match. If there is none, use the generic selector +// TParseContextBase::selectFunction() to find one, parameterized by the +// convertible() and better() predicates defined below. +// +// Return the function symbol if found, otherwise nullptr. +// +const TFunction* HlslParseContext::findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, + TIntermTyped*& args) +{ + if (symbolTable.isFunctionNameVariable(call.getName())) { + error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); + return nullptr; + } + + // first, look for an exact match + bool dummyScope; + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn, &dummyScope, &thisDepth); + if (symbol) + return symbol->getAsFunction(); + + // no exact match, use the generic selector, parameterized by the GLSL rules + + // create list of candidates to send + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + // These built-in ops can accept any type, so we bypass the argument selection + if (candidateList.size() == 1 && builtIn && + (candidateList[0]->getBuiltInOp() == EOpMethodAppend || + candidateList[0]->getBuiltInOp() == EOpMethodRestartStrip || + candidateList[0]->getBuiltInOp() == EOpMethodIncrementCounter || + candidateList[0]->getBuiltInOp() == EOpMethodDecrementCounter || + candidateList[0]->getBuiltInOp() == EOpMethodAppend || + candidateList[0]->getBuiltInOp() == EOpMethodConsume)) { + return candidateList[0]; + } + + bool allowOnlyUpConversions = true; + + // can 'from' convert to 'to'? + const auto convertible = [&](const TType& from, const TType& to, TOperator op, int arg) -> bool { + if (from == to) + return true; + + // no aggregate conversions + if (from.isArray() || to.isArray() || + from.isStruct() || to.isStruct()) + return false; + + switch (op) { + case EOpInterlockedAdd: + case EOpInterlockedAnd: + case EOpInterlockedCompareExchange: + case EOpInterlockedCompareStore: + case EOpInterlockedExchange: + case EOpInterlockedMax: + case EOpInterlockedMin: + case EOpInterlockedOr: + case EOpInterlockedXor: + // We do not promote the texture or image type for these ocodes. Normally that would not + // be an issue because it's a buffer, but we haven't decomposed the opcode yet, and at this + // stage it's merely e.g, a basic integer type. + // + // Instead, we want to promote other arguments, but stay within the same family. In other + // words, InterlockedAdd(RWBuffer, ...) will always use the int flavor, never the uint flavor, + // but it is allowed to promote its other arguments. + if (arg == 0) + return false; + break; + case EOpMethodSample: + case EOpMethodSampleBias: + case EOpMethodSampleCmp: + case EOpMethodSampleCmpLevelZero: + case EOpMethodSampleGrad: + case EOpMethodSampleLevel: + case EOpMethodLoad: + case EOpMethodGetDimensions: + case EOpMethodGetSamplePosition: + case EOpMethodGather: + case EOpMethodCalculateLevelOfDetail: + case EOpMethodCalculateLevelOfDetailUnclamped: + case EOpMethodGatherRed: + case EOpMethodGatherGreen: + case EOpMethodGatherBlue: + case EOpMethodGatherAlpha: + case EOpMethodGatherCmp: + case EOpMethodGatherCmpRed: + case EOpMethodGatherCmpGreen: + case EOpMethodGatherCmpBlue: + case EOpMethodGatherCmpAlpha: + case EOpMethodAppend: + case EOpMethodRestartStrip: + // those are method calls, the object type can not be changed + // they are equal if the dim and type match (is dim sufficient?) + if (arg == 0) + return from.getSampler().type == to.getSampler().type && + from.getSampler().arrayed == to.getSampler().arrayed && + from.getSampler().shadow == to.getSampler().shadow && + from.getSampler().ms == to.getSampler().ms && + from.getSampler().dim == to.getSampler().dim; + break; + default: + break; + } + + // basic types have to be convertible + if (allowOnlyUpConversions) + if (! intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType(), EOpFunctionCall)) + return false; + + // shapes have to be convertible + if ((from.isScalarOrVec1() && to.isScalarOrVec1()) || + (from.isScalarOrVec1() && to.isVector()) || + (from.isScalarOrVec1() && to.isMatrix()) || + (from.isVector() && to.isVector() && from.getVectorSize() >= to.getVectorSize())) + return true; + + // TODO: what are the matrix rules? they go here + + return false; + }; + + // Is 'to2' a better conversion than 'to1'? + // Ties should not be considered as better. + // Assumes 'convertible' already said true. + const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool { + // exact match is always better than mismatch + if (from == to2) + return from != to1; + if (from == to1) + return false; + + // shape changes are always worse + if (from.isScalar() || from.isVector()) { + if (from.getVectorSize() == to2.getVectorSize() && + from.getVectorSize() != to1.getVectorSize()) + return true; + if (from.getVectorSize() == to1.getVectorSize() && + from.getVectorSize() != to2.getVectorSize()) + return false; + } + + // Handle sampler betterness: An exact sampler match beats a non-exact match. + // (If we just looked at basic type, all EbtSamplers would look the same). + // If any type is not a sampler, just use the linearize function below. + if (from.getBasicType() == EbtSampler && to1.getBasicType() == EbtSampler && to2.getBasicType() == EbtSampler) { + // We can ignore the vector size in the comparison. + TSampler to1Sampler = to1.getSampler(); + TSampler to2Sampler = to2.getSampler(); + + to1Sampler.vectorSize = to2Sampler.vectorSize = from.getSampler().vectorSize; + + if (from.getSampler() == to2Sampler) + return from.getSampler() != to1Sampler; + if (from.getSampler() == to1Sampler) + return false; + } + + // Might or might not be changing shape, which means basic type might + // or might not match, so within that, the question is how big a + // basic-type conversion is being done. + // + // Use a hierarchy of domains, translated to order of magnitude + // in a linearized view: + // - floating-point vs. integer + // - 32 vs. 64 bit (or width in general) + // - bool vs. non bool + // - signed vs. not signed + const auto linearize = [](const TBasicType& basicType) -> int { + switch (basicType) { + case EbtBool: return 1; + case EbtInt: return 10; + case EbtUint: return 11; + case EbtInt64: return 20; + case EbtUint64: return 21; + case EbtFloat: return 100; + case EbtDouble: return 110; + default: return 0; + } + }; + + return abs(linearize(to2.getBasicType()) - linearize(from.getBasicType())) < + abs(linearize(to1.getBasicType()) - linearize(from.getBasicType())); + }; + + // for ambiguity reporting + bool tie = false; + + // send to the generic selector + const TFunction* bestMatch = nullptr; + + // printf has var args and is in the symbol table as "printf()", + // mangled to "printf(" + if (call.getName() == "printf") { + TSymbol* symbol = symbolTable.find("printf(", &builtIn); + if (symbol) + return symbol->getAsFunction(); + } + + bestMatch = selectFunction(candidateList, call, convertible, better, tie); + + if (bestMatch == nullptr) { + // If there is nothing selected by allowing only up-conversions (to a larger linearize() value), + // we instead try down-conversions, which are valid in HLSL, but not preferred if there are any + // upconversions possible. + allowOnlyUpConversions = false; + bestMatch = selectFunction(candidateList, call, convertible, better, tie); + } + + if (bestMatch == nullptr) { + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + return nullptr; + } + + // For built-ins, we can convert across the arguments. This will happen in several steps: + // Step 1: If there's an exact match, use it. + // Step 2a: Otherwise, get the operator from the best match and promote arguments: + // Step 2b: reconstruct the TFunction based on the new arg types + // Step 3: Re-select after type promotion is applied, to find proper candidate. + if (builtIn) { + // Step 1: If there's an exact match, use it. + if (call.getMangledName() == bestMatch->getMangledName()) + return bestMatch; + + // Step 2a: Otherwise, get the operator from the best match and promote arguments as if we + // are that kind of operator. + if (args != nullptr) { + // The arg list can be a unary node, or an aggregate. We have to handle both. + // We will use the normal promote() facilities, which require an interm node. + TIntermOperator* promote = nullptr; + + if (call.getParamCount() == 1) { + promote = new TIntermUnary(bestMatch->getBuiltInOp()); + promote->getAsUnaryNode()->setOperand(args->getAsTyped()); + } else { + promote = new TIntermAggregate(bestMatch->getBuiltInOp()); + promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence()); + } + + if (! intermediate.promote(promote)) + return nullptr; + + // Obtain the promoted arg list. + if (call.getParamCount() == 1) { + args = promote->getAsUnaryNode()->getOperand(); + } else { + promote->getAsAggregate()->getSequence().swap(args->getAsAggregate()->getSequence()); + } + } + + // Step 2b: reconstruct the TFunction based on the new arg types + TFunction convertedCall(&call.getName(), call.getType(), call.getBuiltInOp()); + + if (args->getAsAggregate()) { + // Handle aggregates: put all args into the new function call + for (int arg = 0; arg < int(args->getAsAggregate()->getSequence().size()); ++arg) { + // TODO: But for constness, we could avoid the new & shallowCopy, and use the pointer directly. + TParameter param = { 0, new TType, nullptr }; + param.type->shallowCopy(args->getAsAggregate()->getSequence()[arg]->getAsTyped()->getType()); + convertedCall.addParameter(param); + } + } else if (args->getAsUnaryNode()) { + // Handle unaries: put all args into the new function call + TParameter param = { 0, new TType, nullptr }; + param.type->shallowCopy(args->getAsUnaryNode()->getOperand()->getAsTyped()->getType()); + convertedCall.addParameter(param); + } else if (args->getAsTyped()) { + // Handle bare e.g, floats, not in an aggregate. + TParameter param = { 0, new TType, nullptr }; + param.type->shallowCopy(args->getAsTyped()->getType()); + convertedCall.addParameter(param); + } else { + assert(0); // unknown argument list. + return nullptr; + } + + // Step 3: Re-select after type promotion, to find proper candidate + // send to the generic selector + bestMatch = selectFunction(candidateList, convertedCall, convertible, better, tie); + + // At this point, there should be no tie. + } + + if (tie) + error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); + + // Append default parameter values if needed + if (!tie && bestMatch != nullptr) { + for (int defParam = call.getParamCount(); defParam < bestMatch->getParamCount(); ++defParam) { + handleFunctionArgument(&call, args, (*bestMatch)[defParam].defaultValue); + } + } + + return bestMatch; +} + +// +// Do everything necessary to handle a typedef declaration, for a single symbol. +// +// 'parseType' is the type part of the declaration (to the left) +// 'arraySizes' is the arrayness tagged on the identifier (to the right) +// +void HlslParseContext::declareTypedef(const TSourceLoc& loc, const TString& identifier, const TType& parseType) +{ + TVariable* typeSymbol = new TVariable(&identifier, parseType, true); + if (! symbolTable.insert(*typeSymbol)) + error(loc, "name already defined", "typedef", identifier.c_str()); +} + +// Do everything necessary to handle a struct declaration, including +// making IO aliases because HLSL allows mixed IO in a struct that specializes +// based on the usage (input, output, uniform, none). +void HlslParseContext::declareStruct(const TSourceLoc& loc, TString& structName, TType& type) +{ + // If it was named, which means the type can be reused later, add + // it to the symbol table. (Unless it's a block, in which + // case the name is not a type.) + if (type.getBasicType() == EbtBlock || structName.size() == 0) + return; + + TVariable* userTypeDef = new TVariable(&structName, type, true); + if (! symbolTable.insert(*userTypeDef)) { + error(loc, "redefinition", structName.c_str(), "struct"); + return; + } + + // See if we need IO aliases for the structure typeList + + const auto condAlloc = [](bool pred, TTypeList*& list) { + if (pred && list == nullptr) + list = new TTypeList; + }; + + tIoKinds newLists = { nullptr, nullptr, nullptr }; // allocate for each kind found + for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { + condAlloc(hasUniform(member->type->getQualifier()), newLists.uniform); + condAlloc( hasInput(member->type->getQualifier()), newLists.input); + condAlloc( hasOutput(member->type->getQualifier()), newLists.output); + + if (member->type->isStruct()) { + auto it = ioTypeMap.find(member->type->getStruct()); + if (it != ioTypeMap.end()) { + condAlloc(it->second.uniform != nullptr, newLists.uniform); + condAlloc(it->second.input != nullptr, newLists.input); + condAlloc(it->second.output != nullptr, newLists.output); + } + } + } + if (newLists.uniform == nullptr && + newLists.input == nullptr && + newLists.output == nullptr) { + // Won't do any IO caching, clear up the type and get out now. + for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) + clearUniformInputOutput(member->type->getQualifier()); + return; + } + + // We have IO involved. + + // Make a pure typeList for the symbol table, and cache side copies of IO versions. + for (auto member = type.getStruct()->begin(); member != type.getStruct()->end(); ++member) { + const auto inheritStruct = [&](TTypeList* s, TTypeLoc& ioMember) { + if (s != nullptr) { + ioMember.type = new TType; + ioMember.type->shallowCopy(*member->type); + ioMember.type->setStruct(s); + } + }; + const auto newMember = [&](TTypeLoc& m) { + if (m.type == nullptr) { + m.type = new TType; + m.type->shallowCopy(*member->type); + } + }; + + TTypeLoc newUniformMember = { nullptr, member->loc }; + TTypeLoc newInputMember = { nullptr, member->loc }; + TTypeLoc newOutputMember = { nullptr, member->loc }; + if (member->type->isStruct()) { + // swap in an IO child if there is one + auto it = ioTypeMap.find(member->type->getStruct()); + if (it != ioTypeMap.end()) { + inheritStruct(it->second.uniform, newUniformMember); + inheritStruct(it->second.input, newInputMember); + inheritStruct(it->second.output, newOutputMember); + } + } + if (newLists.uniform) { + newMember(newUniformMember); + + // inherit default matrix layout (changeable via #pragma pack_matrix), if none given. + if (member->type->isMatrix() && member->type->getQualifier().layoutMatrix == ElmNone) + newUniformMember.type->getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix; + + correctUniform(newUniformMember.type->getQualifier()); + newLists.uniform->push_back(newUniformMember); + } + if (newLists.input) { + newMember(newInputMember); + correctInput(newInputMember.type->getQualifier()); + newLists.input->push_back(newInputMember); + } + if (newLists.output) { + newMember(newOutputMember); + correctOutput(newOutputMember.type->getQualifier()); + newLists.output->push_back(newOutputMember); + } + + // make original pure + clearUniformInputOutput(member->type->getQualifier()); + } + ioTypeMap[type.getStruct()] = newLists; +} + +// Lookup a user-type by name. +// If found, fill in the type and return the defining symbol. +// If not found, return nullptr. +TSymbol* HlslParseContext::lookupUserType(const TString& typeName, TType& type) +{ + TSymbol* symbol = symbolTable.find(typeName); + if (symbol && symbol->getAsVariable() && symbol->getAsVariable()->isUserType()) { + type.shallowCopy(symbol->getType()); + return symbol; + } else + return nullptr; +} + +// +// Do everything necessary to handle a variable (non-block) declaration. +// Either redeclaring a variable, or making a new one, updating the symbol +// table, and all error checking. +// +// Returns a subtree node that computes an initializer, if needed. +// Returns nullptr if there is no code to execute for initialization. +// +// 'parseType' is the type part of the declaration (to the left) +// 'arraySizes' is the arrayness tagged on the identifier (to the right) +// +TIntermNode* HlslParseContext::declareVariable(const TSourceLoc& loc, const TString& identifier, TType& type, + TIntermTyped* initializer) +{ + if (voidErrorCheck(loc, identifier, type.getBasicType())) + return nullptr; + + // Global consts with initializers that are non-const act like EvqGlobal in HLSL. + // This test is implicitly recursive, because initializers propagate constness + // up the aggregate node tree during creation. E.g, for: + // { { 1, 2 }, { 3, 4 } } + // the initializer list is marked EvqConst at the top node, and remains so here. However: + // { 1, { myvar, 2 }, 3 } + // is not a const intializer, and still becomes EvqGlobal here. + + const bool nonConstInitializer = (initializer != nullptr && initializer->getQualifier().storage != EvqConst); + + if (type.getQualifier().storage == EvqConst && symbolTable.atGlobalLevel() && nonConstInitializer) { + // Force to global + type.getQualifier().storage = EvqGlobal; + } + + // make const and initialization consistent + fixConstInit(loc, identifier, type, initializer); + + // Check for redeclaration of built-ins and/or attempting to declare a reserved name + TSymbol* symbol = nullptr; + + inheritGlobalDefaults(type.getQualifier()); + + const bool flattenVar = shouldFlatten(type, type.getQualifier().storage, true); + + // correct IO in the type + switch (type.getQualifier().storage) { + case EvqGlobal: + case EvqTemporary: + clearUniformInputOutput(type.getQualifier()); + break; + case EvqUniform: + case EvqBuffer: + correctUniform(type.getQualifier()); + if (type.isStruct()) { + auto it = ioTypeMap.find(type.getStruct()); + if (it != ioTypeMap.end()) + type.setStruct(it->second.uniform); + } + + break; + default: + break; + } + + // Declare the variable + if (type.isArray()) { + // array case + declareArray(loc, identifier, type, symbol, !flattenVar); + } else { + // non-array case + if (symbol == nullptr) + symbol = declareNonArray(loc, identifier, type, !flattenVar); + else if (type != symbol->getType()) + error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str()); + } + + if (symbol == nullptr) + return nullptr; + + if (flattenVar) + flatten(*symbol->getAsVariable(), symbolTable.atGlobalLevel()); + + if (initializer == nullptr) + return nullptr; + + // Deal with initializer + TVariable* variable = symbol->getAsVariable(); + if (variable == nullptr) { + error(loc, "initializer requires a variable, not a member", identifier.c_str(), ""); + return nullptr; + } + return executeInitializer(loc, initializer, variable); +} + +// Pick up global defaults from the provide global defaults into dst. +void HlslParseContext::inheritGlobalDefaults(TQualifier& dst) const +{ + if (dst.storage == EvqVaryingOut) { + if (! dst.hasStream() && language == EShLangGeometry) + dst.layoutStream = globalOutputDefaults.layoutStream; + if (! dst.hasXfbBuffer()) + dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + } +} + +// +// Make an internal-only variable whose name is for debug purposes only +// and won't be searched for. Callers will only use the return value to use +// the variable, not the name to look it up. It is okay if the name +// is the same as other names; there won't be any conflict. +// +TVariable* HlslParseContext::makeInternalVariable(const char* name, const TType& type) const +{ + TString* nameString = NewPoolTString(name); + TVariable* variable = new TVariable(nameString, type); + symbolTable.makeInternalVariable(*variable); + + return variable; +} + +// Make a symbol node holding a new internal temporary variable. +TIntermSymbol* HlslParseContext::makeInternalVariableNode(const TSourceLoc& loc, const char* name, + const TType& type) const +{ + TVariable* tmpVar = makeInternalVariable(name, type); + tmpVar->getWritableType().getQualifier().makeTemporary(); + + return intermediate.addSymbol(*tmpVar, loc); +} + +// +// Declare a non-array variable, the main point being there is no redeclaration +// for resizing allowed. +// +// Return the successfully declared variable. +// +TVariable* HlslParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type, + bool track) +{ + // make a new variable + TVariable* variable = new TVariable(&identifier, type); + + // add variable to symbol table + if (symbolTable.insert(*variable)) { + if (track && symbolTable.atGlobalLevel()) + trackLinkage(*variable); + return variable; + } + + error(loc, "redefinition", variable->getName().c_str(), ""); + return nullptr; +} + +// +// Handle all types of initializers from the grammar. +// +// Returning nullptr just means there is no code to execute to handle the +// initializer, which will, for example, be the case for constant initializers. +// +// Returns a subtree that accomplished the initialization. +// +TIntermNode* HlslParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable) +{ + // + // Identifier must be of type constant, a global, or a temporary, and + // starting at version 120, desktop allows uniforms to have initializers. + // + TStorageQualifier qualifier = variable->getType().getQualifier().storage; + + // + // If the initializer was from braces { ... }, we convert the whole subtree to a + // constructor-style subtree, allowing the rest of the code to operate + // identically for both kinds of initializers. + // + // + // Type can't be deduced from the initializer list, so a skeletal type to + // follow has to be passed in. Constness and specialization-constness + // should be deduced bottom up, not dictated by the skeletal type. + // + TType skeletalType; + skeletalType.shallowCopy(variable->getType()); + skeletalType.getQualifier().makeTemporary(); + if (initializer->getAsAggregate() && initializer->getAsAggregate()->getOp() == EOpNull) + initializer = convertInitializerList(loc, skeletalType, initializer, nullptr); + if (initializer == nullptr) { + // error recovery; don't leave const without constant values + if (qualifier == EvqConst) + variable->getWritableType().getQualifier().storage = EvqTemporary; + return nullptr; + } + + // Fix outer arrayness if variable is unsized, getting size from the initializer + if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray()) + variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize()); + + // Inner arrayness can also get set by an initializer + if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() && + initializer->getType().getArraySizes()->getNumDims() == + variable->getType().getArraySizes()->getNumDims()) { + // adopt unsized sizes from the initializer's sizes + for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) { + if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) { + variable->getWritableType().getArraySizes()->setDimSize(d, + initializer->getType().getArraySizes()->getDimSize(d)); + } + } + } + + // Uniform and global consts require a constant initializer + if (qualifier == EvqUniform && initializer->getType().getQualifier().storage != EvqConst) { + error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); + variable->getWritableType().getQualifier().storage = EvqTemporary; + return nullptr; + } + + // Const variables require a constant initializer + if (qualifier == EvqConst) { + if (initializer->getType().getQualifier().storage != EvqConst) { + variable->getWritableType().getQualifier().storage = EvqConstReadOnly; + qualifier = EvqConstReadOnly; + } + } + + if (qualifier == EvqConst || qualifier == EvqUniform) { + // Compile-time tagging of the variable with its constant value... + + initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer); + if (initializer != nullptr && variable->getType() != initializer->getType()) + initializer = intermediate.addUniShapeConversion(EOpAssign, variable->getType(), initializer); + if (initializer == nullptr || !initializer->getAsConstantUnion() || + variable->getType() != initializer->getType()) { + error(loc, "non-matching or non-convertible constant type for const initializer", + variable->getType().getStorageQualifierString(), ""); + variable->getWritableType().getQualifier().storage = EvqTemporary; + return nullptr; + } + + variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); + } else { + // normal assigning of a value to a variable... + specializationCheck(loc, initializer->getType(), "initializer"); + TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); + TIntermNode* initNode = handleAssign(loc, EOpAssign, intermSymbol, initializer); + if (initNode == nullptr) + assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); + return initNode; + } + + return nullptr; +} + +// +// Reprocess any initializer-list { ... } parts of the initializer. +// Need to hierarchically assign correct types and implicit +// conversions. Will do this mimicking the same process used for +// creating a constructor-style initializer, ensuring we get the +// same form. +// +// Returns a node representing an expression for the initializer list expressed +// as the correct type. +// +// Returns nullptr if there is an error. +// +TIntermTyped* HlslParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, + TIntermTyped* initializer, TIntermTyped* scalarInit) +{ + // Will operate recursively. Once a subtree is found that is constructor style, + // everything below it is already good: Only the "top part" of the initializer + // can be an initializer list, where "top part" can extend for several (or all) levels. + + // see if we have bottomed out in the tree within the initializer-list part + TIntermAggregate* initList = initializer->getAsAggregate(); + if (initList == nullptr || initList->getOp() != EOpNull) { + // We don't have a list, but if it's a scalar and the 'type' is a + // composite, we need to lengthen below to make it useful. + // Otherwise, this is an already formed object to initialize with. + if (type.isScalar() || !initializer->getType().isScalar()) + return initializer; + else + initList = intermediate.makeAggregate(initializer); + } + + // Of the initializer-list set of nodes, need to process bottom up, + // so recurse deep, then process on the way up. + + // Go down the tree here... + if (type.isArray()) { + // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate. + // Later on, initializer execution code will deal with array size logic. + TType arrayType; + arrayType.shallowCopy(type); // sharing struct stuff is fine + arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below + + // edit array sizes to fill in unsized dimensions + if (type.isUnsizedArray()) + arrayType.changeOuterArraySize((int)initList->getSequence().size()); + + // set unsized array dimensions that can be derived from the initializer's first element + if (arrayType.isArrayOfArrays() && initList->getSequence().size() > 0) { + TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped(); + if (firstInit->getType().isArray() && + arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) { + for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) { + if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize) + arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1)); + } + } + } + + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), arrayType.getOuterArraySize(), scalarInit); + + // recursively process each element + TType elementType(arrayType, 0); // dereferenced type + for (int i = 0; i < arrayType.getOuterArraySize(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, elementType, + initList->getSequence()[i]->getAsTyped(), scalarInit); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + + return addConstructor(loc, initList, arrayType); + } else if (type.isStruct()) { + // do we have implicit assignments to opaques? + for (size_t i = initList->getSequence().size(); i < type.getStruct()->size(); ++i) { + if ((*type.getStruct())[i].type->containsOpaque()) { + error(loc, "cannot implicitly initialize opaque members", "initializer list", ""); + return nullptr; + } + } + + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), static_cast(type.getStruct()->size()), scalarInit); + + if (type.getStruct()->size() != initList->getSequence().size()) { + error(loc, "wrong number of structure members", "initializer list", ""); + return nullptr; + } + for (size_t i = 0; i < type.getStruct()->size(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, + initList->getSequence()[i]->getAsTyped(), scalarInit); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } else if (type.isMatrix()) { + if (type.computeNumComponents() == (int)initList->getSequence().size()) { + // This means the matrix is initialized component-wise, rather than as + // a series of rows and columns. We can just use the list directly as + // a constructor; no further processing needed. + } else { + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), type.getMatrixCols(), scalarInit); + + if (type.getMatrixCols() != (int)initList->getSequence().size()) { + error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + TType vectorType(type, 0); // dereferenced type + for (int i = 0; i < type.getMatrixCols(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, vectorType, + initList->getSequence()[i]->getAsTyped(), scalarInit); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } + } else if (type.isVector()) { + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), type.getVectorSize(), scalarInit); + + // error check; we're at bottom, so work is finished below + if (type.getVectorSize() != (int)initList->getSequence().size()) { + error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", + type.getCompleteString().c_str()); + return nullptr; + } + } else if (type.isScalar()) { + // lengthen list to be long enough + lengthenList(loc, initList->getSequence(), 1, scalarInit); + + if ((int)initList->getSequence().size() != 1) { + error(loc, "scalar expected one element:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + } else { + error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + + // Now that the subtree is processed, process this node as if the + // initializer list is a set of arguments to a constructor. + TIntermTyped* emulatedConstructorArguments; + if (initList->getSequence().size() == 1) + emulatedConstructorArguments = initList->getSequence()[0]->getAsTyped(); + else + emulatedConstructorArguments = initList; + + return addConstructor(loc, emulatedConstructorArguments, type); +} + +// Lengthen list to be long enough to cover any gap from the current list size +// to 'size'. If the list is longer, do nothing. +// The value to lengthen with is the default for short lists. +// +// By default, lists that are too short due to lack of initializers initialize to zero. +// Alternatively, it could be a scalar initializer for a structure. Both cases are handled, +// based on whether something is passed in as 'scalarInit'. +// +// 'scalarInit' must be safe to use each time this is called (no side effects replication). +// +void HlslParseContext::lengthenList(const TSourceLoc& loc, TIntermSequence& list, int size, TIntermTyped* scalarInit) +{ + for (int c = (int)list.size(); c < size; ++c) { + if (scalarInit == nullptr) + list.push_back(intermediate.addConstantUnion(0, loc)); + else + list.push_back(scalarInit); + } +} + +// +// Test for the correctness of the parameters passed to various constructor functions +// and also convert them to the right data type, if allowed and required. +// +// Returns nullptr for an error or the constructed node (aggregate or typed) for no error. +// +TIntermTyped* HlslParseContext::handleConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) +{ + if (node == nullptr) + return nullptr; + + // Construct identical type + if (type == node->getType()) + return node; + + // Handle the idiom "(struct type)" + if (type.isStruct() && isScalarConstructor(node)) { + // 'node' will almost always get used multiple times, so should not be used directly, + // it would create a DAG instead of a tree, which might be okay (would + // like to formalize that for constants and symbols), but if it has + // side effects, they would get executed multiple times, which is not okay. + if (node->getAsConstantUnion() == nullptr && node->getAsSymbolNode() == nullptr) { + TIntermAggregate* seq = intermediate.makeAggregate(loc); + TIntermSymbol* copy = makeInternalVariableNode(loc, "scalarCopy", node->getType()); + seq = intermediate.growAggregate(seq, intermediate.addBinaryNode(EOpAssign, copy, node, loc)); + seq = intermediate.growAggregate(seq, convertInitializerList(loc, type, intermediate.makeAggregate(loc), copy)); + seq->setOp(EOpComma); + seq->setType(type); + return seq; + } else + return convertInitializerList(loc, type, intermediate.makeAggregate(loc), node); + } + + return addConstructor(loc, node, type); +} + +// Add a constructor, either from the grammar, or other programmatic reasons. +// +// 'node' is what to construct from. +// 'type' is what type to construct. +// +// Returns the constructed object. +// Return nullptr if it can't be done. +// +TIntermTyped* HlslParseContext::addConstructor(const TSourceLoc& loc, TIntermTyped* node, const TType& type) +{ + TIntermAggregate* aggrNode = node->getAsAggregate(); + TOperator op = intermediate.mapTypeToConstructorOp(type); + + if (op == EOpConstructTextureSampler) + return intermediate.setAggregateOperator(aggrNode, op, type, loc); + + TTypeList::const_iterator memberTypes; + if (op == EOpConstructStruct) + memberTypes = type.getStruct()->begin(); + + TType elementType; + if (type.isArray()) { + TType dereferenced(type, 0); + elementType.shallowCopy(dereferenced); + } else + elementType.shallowCopy(type); + + bool singleArg; + if (aggrNode != nullptr) { + if (aggrNode->getOp() != EOpNull) + singleArg = true; + else + singleArg = false; + } else + singleArg = true; + + TIntermTyped *newNode; + if (singleArg) { + // Handle array -> array conversion + // Constructing an array of one type from an array of another type is allowed, + // assuming there are enough components available (semantic-checked earlier). + if (type.isArray() && node->isArray()) + newNode = convertArray(node, type); + + // If structure constructor or array constructor is being called + // for only one parameter inside the aggregate, we need to call constructAggregate function once. + else if (type.isArray()) + newNode = constructAggregate(node, elementType, 1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc()); + else { + // shape conversion for matrix constructor from scalar. HLSL semantics are: scalar + // is replicated into every element of the matrix (not just the diagnonal), so + // that is handled specially here. + if (type.isMatrix() && node->getType().isScalarOrVec1()) + node = intermediate.addShapeConversion(type, node); + + newNode = constructBuiltIn(type, op, node, node->getLoc(), false); + } + + if (newNode && (type.isArray() || op == EOpConstructStruct)) + newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc); + + return newNode; + } + + // + // Handle list of arguments. + // + TIntermSequence& sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor + // if the structure constructor contains more than one parameter, then construct + // each parameter + + int paramCount = 0; // keeps a track of the constructor parameter number being checked + + // for each parameter to the constructor call, check to see if the right type is passed or convert them + // to the right type if possible (and allowed). + // for structure constructors, just check if the right type is passed, no conversion is allowed. + + for (TIntermSequence::iterator p = sequenceVector.begin(); + p != sequenceVector.end(); p++, paramCount++) { + if (type.isArray()) + newNode = constructAggregate(*p, elementType, paramCount + 1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount + 1, node->getLoc()); + else + newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true); + + if (newNode) + *p = newNode; + else + return nullptr; + } + + TIntermTyped* constructor = intermediate.setAggregateOperator(aggrNode, op, type, loc); + + return constructor; +} + +// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value +// for the parameter to the constructor (passed to this function). Essentially, it converts +// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a +// float, then float is converted to int. +// +// Returns nullptr for an error or the constructed node. +// +TIntermTyped* HlslParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, + const TSourceLoc& loc, bool subset) +{ + TIntermTyped* newNode; + TOperator basicOp; + + // + // First, convert types as needed. + // + switch (op) { + case EOpConstructF16Vec2: + case EOpConstructF16Vec3: + case EOpConstructF16Vec4: + case EOpConstructF16Mat2x2: + case EOpConstructF16Mat2x3: + case EOpConstructF16Mat2x4: + case EOpConstructF16Mat3x2: + case EOpConstructF16Mat3x3: + case EOpConstructF16Mat3x4: + case EOpConstructF16Mat4x2: + case EOpConstructF16Mat4x3: + case EOpConstructF16Mat4x4: + case EOpConstructFloat16: + basicOp = EOpConstructFloat16; + break; + + case EOpConstructVec2: + case EOpConstructVec3: + case EOpConstructVec4: + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: + case EOpConstructFloat: + basicOp = EOpConstructFloat; + break; + + case EOpConstructDVec2: + case EOpConstructDVec3: + case EOpConstructDVec4: + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructDouble: + basicOp = EOpConstructDouble; + break; + + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + case EOpConstructInt16: + basicOp = EOpConstructInt16; + break; + + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructIMat2x2: + case EOpConstructIMat2x3: + case EOpConstructIMat2x4: + case EOpConstructIMat3x2: + case EOpConstructIMat3x3: + case EOpConstructIMat3x4: + case EOpConstructIMat4x2: + case EOpConstructIMat4x3: + case EOpConstructIMat4x4: + case EOpConstructInt: + basicOp = EOpConstructInt; + break; + + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructUint16: + basicOp = EOpConstructUint16; + break; + + case EOpConstructUVec2: + case EOpConstructUVec3: + case EOpConstructUVec4: + case EOpConstructUMat2x2: + case EOpConstructUMat2x3: + case EOpConstructUMat2x4: + case EOpConstructUMat3x2: + case EOpConstructUMat3x3: + case EOpConstructUMat3x4: + case EOpConstructUMat4x2: + case EOpConstructUMat4x3: + case EOpConstructUMat4x4: + case EOpConstructUint: + basicOp = EOpConstructUint; + break; + + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructBMat2x2: + case EOpConstructBMat2x3: + case EOpConstructBMat2x4: + case EOpConstructBMat3x2: + case EOpConstructBMat3x3: + case EOpConstructBMat3x4: + case EOpConstructBMat4x2: + case EOpConstructBMat4x3: + case EOpConstructBMat4x4: + case EOpConstructBool: + basicOp = EOpConstructBool; + break; + + default: + error(loc, "unsupported construction", "", ""); + + return nullptr; + } + newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc()); + if (newNode == nullptr) { + error(loc, "can't convert", "constructor", ""); + return nullptr; + } + + // + // Now, if there still isn't an operation to do the construction, and we need one, add one. + // + + // Otherwise, skip out early. + if (subset || (newNode != node && newNode->getType() == type)) + return newNode; + + // setAggregateOperator will insert a new node for the constructor, as needed. + return intermediate.setAggregateOperator(newNode, op, type, loc); +} + +// Convert the array in node to the requested type, which is also an array. +// Returns nullptr on failure, otherwise returns aggregate holding the list of +// elements needed to construct the array. +TIntermTyped* HlslParseContext::convertArray(TIntermTyped* node, const TType& type) +{ + assert(node->isArray() && type.isArray()); + if (node->getType().computeNumComponents() < type.computeNumComponents()) + return nullptr; + + // TODO: write an argument replicator, for the case the argument should not be + // executed multiple times, yet multiple copies are needed. + + TIntermTyped* constructee = node->getAsTyped(); + // track where we are in consuming the argument + int constructeeElement = 0; + int constructeeComponent = 0; + + // bump up to the next component to consume + const auto getNextComponent = [&]() { + TIntermTyped* component; + component = handleBracketDereference(node->getLoc(), constructee, + intermediate.addConstantUnion(constructeeElement, node->getLoc())); + if (component->isVector()) + component = handleBracketDereference(node->getLoc(), component, + intermediate.addConstantUnion(constructeeComponent, node->getLoc())); + // bump component pointer up + ++constructeeComponent; + if (constructeeComponent == constructee->getVectorSize()) { + constructeeComponent = 0; + ++constructeeElement; + } + return component; + }; + + // make one subnode per constructed array element + TIntermAggregate* constructor = nullptr; + TType derefType(type, 0); + TType speculativeComponentType(derefType, 0); + TType* componentType = derefType.isVector() ? &speculativeComponentType : &derefType; + TOperator componentOp = intermediate.mapTypeToConstructorOp(*componentType); + TType crossType(node->getBasicType(), EvqTemporary, type.getVectorSize()); + for (int e = 0; e < type.getOuterArraySize(); ++e) { + // construct an element + TIntermTyped* elementArg; + if (type.getVectorSize() == constructee->getVectorSize()) { + // same element shape + elementArg = handleBracketDereference(node->getLoc(), constructee, + intermediate.addConstantUnion(e, node->getLoc())); + } else { + // mismatched element shapes + if (type.getVectorSize() == 1) + elementArg = getNextComponent(); + else { + // make a vector + TIntermAggregate* elementConstructee = nullptr; + for (int c = 0; c < type.getVectorSize(); ++c) + elementConstructee = intermediate.growAggregate(elementConstructee, getNextComponent()); + elementArg = addConstructor(node->getLoc(), elementConstructee, crossType); + } + } + // convert basic types + elementArg = intermediate.addConversion(componentOp, derefType, elementArg); + if (elementArg == nullptr) + return nullptr; + // combine with top-level constructor + constructor = intermediate.growAggregate(constructor, elementArg); + } + + return constructor; +} + +// This function tests for the type of the parameters to the structure or array constructor. Raises +// an error message if the expected type does not match the parameter passed to the constructor. +// +// Returns nullptr for an error or the input node itself if the expected and the given parameter types match. +// +TIntermTyped* HlslParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, + const TSourceLoc& loc) +{ + // Handle cases that map more 1:1 between constructor arguments and constructed. + TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped()); + if (converted == nullptr || converted->getType() != type) { + error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount, + node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str()); + + return nullptr; + } + + return converted; +} + +// +// Do everything needed to add an interface block. +// +void HlslParseContext::declareBlock(const TSourceLoc& loc, TType& type, const TString* instanceName) +{ + assert(type.getWritableStruct() != nullptr); + + // Clean up top-level decorations that don't belong. + switch (type.getQualifier().storage) { + case EvqUniform: + case EvqBuffer: + correctUniform(type.getQualifier()); + break; + case EvqVaryingIn: + correctInput(type.getQualifier()); + break; + case EvqVaryingOut: + correctOutput(type.getQualifier()); + break; + default: + break; + } + + TTypeList& typeList = *type.getWritableStruct(); + // fix and check for member storage qualifiers and types that don't belong within a block + for (unsigned int member = 0; member < typeList.size(); ++member) { + TType& memberType = *typeList[member].type; + TQualifier& memberQualifier = memberType.getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + globalQualifierFix(memberLoc, memberQualifier); + memberQualifier.storage = type.getQualifier().storage; + + if (memberType.isStruct()) { + // clean up and pick up the right set of decorations + auto it = ioTypeMap.find(memberType.getStruct()); + switch (type.getQualifier().storage) { + case EvqUniform: + case EvqBuffer: + correctUniform(type.getQualifier()); + if (it != ioTypeMap.end() && it->second.uniform) + memberType.setStruct(it->second.uniform); + break; + case EvqVaryingIn: + correctInput(type.getQualifier()); + if (it != ioTypeMap.end() && it->second.input) + memberType.setStruct(it->second.input); + break; + case EvqVaryingOut: + correctOutput(type.getQualifier()); + if (it != ioTypeMap.end() && it->second.output) + memberType.setStruct(it->second.output); + break; + default: + break; + } + } + } + + // Make default block qualification, and adjust the member qualifications + + TQualifier defaultQualification; + switch (type.getQualifier().storage) { + case EvqUniform: defaultQualification = globalUniformDefaults; break; + case EvqBuffer: defaultQualification = globalBufferDefaults; break; + case EvqVaryingIn: defaultQualification = globalInputDefaults; break; + case EvqVaryingOut: defaultQualification = globalOutputDefaults; break; + default: defaultQualification.clear(); break; + } + + // Special case for "push_constant uniform", which has a default of std430, + // contrary to normal uniform defaults, and can't have a default tracked for it. + if (type.getQualifier().layoutPushConstant && ! type.getQualifier().hasPacking()) + type.getQualifier().layoutPacking = ElpStd430; + + // fix and check for member layout qualifiers + + mergeObjectLayoutQualifiers(defaultQualification, type.getQualifier(), true); + + bool memberWithLocation = false; + bool memberWithoutLocation = false; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (memberQualifier.hasStream()) { + if (defaultQualification.layoutStream != memberQualifier.layoutStream) + error(memberLoc, "member cannot contradict block", "stream", ""); + } + + // "This includes a block's inheritance of the + // current global default buffer, a block member's inheritance of the block's + // buffer, and the requirement that any *xfb_buffer* declared on a block + // member must match the buffer inherited from the block." + if (memberQualifier.hasXfbBuffer()) { + if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); + } + + if (memberQualifier.hasLocation()) { + switch (type.getQualifier().storage) { + case EvqVaryingIn: + case EvqVaryingOut: + memberWithLocation = true; + break; + default: + break; + } + } else + memberWithoutLocation = true; + + TQualifier newMemberQualification = defaultQualification; + mergeQualifiers(newMemberQualification, memberQualifier); + memberQualifier = newMemberQualification; + } + + // Process the members + fixBlockLocations(loc, type.getQualifier(), typeList, memberWithLocation, memberWithoutLocation); + fixXfbOffsets(type.getQualifier(), typeList); + fixBlockUniformOffsets(type.getQualifier(), typeList); + + // reverse merge, so that currentBlockQualifier now has all layout information + // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers) + mergeObjectLayoutQualifiers(type.getQualifier(), defaultQualification, true); + + // + // Build and add the interface block as a new type named 'blockName' + // + + // Use the instance name as the interface name if one exists, else the block name. + const TString& interfaceName = (instanceName && !instanceName->empty()) ? *instanceName : type.getTypeName(); + + TType blockType(&typeList, interfaceName, type.getQualifier()); + if (type.isArray()) + blockType.transferArraySizes(type.getArraySizes()); + + // Add the variable, as anonymous or named instanceName. + // Make an anonymous variable if no name was provided. + if (instanceName == nullptr) + instanceName = NewPoolTString(""); + + TVariable& variable = *new TVariable(instanceName, blockType); + if (! symbolTable.insert(variable)) { + if (*instanceName == "") + error(loc, "nameless block contains a member that already has a name at global scope", + "" /* blockName->c_str() */, ""); + else + error(loc, "block instance name redefinition", variable.getName().c_str(), ""); + + return; + } + + // Save it in the AST for linker use. + if (symbolTable.atGlobalLevel()) + trackLinkage(variable); +} + +// +// "For a block, this process applies to the entire block, or until the first member +// is reached that has a location layout qualifier. When a block member is declared with a location +// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level +// declaration. Subsequent members are again assigned consecutive locations, based on the newest location, +// until the next member declared with a location qualifier. The values used for locations do not have to be +// declared in increasing order." +void HlslParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) +{ + // "If a block has no block-level location layout qualifier, it is required that either all or none of its members + // have a location layout qualifier, or a compile-time error results." + if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation) + error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", ""); + else { + if (memberWithLocation) { + // remove any block-level location and make it per *every* member + int nextLocation = 0; // by the rule above, initial value is not relevant + if (qualifier.hasAnyLocation()) { + nextLocation = qualifier.layoutLocation; + qualifier.layoutLocation = TQualifier::layoutLocationEnd; + if (qualifier.hasComponent()) { + // "It is a compile-time error to apply the *component* qualifier to a ... block" + error(loc, "cannot apply to a block", "component", ""); + } + if (qualifier.hasIndex()) { + error(loc, "cannot apply to a block", "index", ""); + } + } + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (! memberQualifier.hasLocation()) { + if (nextLocation >= (int)TQualifier::layoutLocationEnd) + error(memberLoc, "location is too large", "location", ""); + memberQualifier.layoutLocation = nextLocation; + memberQualifier.layoutComponent = 0; + } + nextLocation = memberQualifier.layoutLocation + + intermediate.computeTypeLocationSize(*typeList[member].type, language); + } + } + } +} + +void HlslParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) +{ + // "If a block is qualified with xfb_offset, all its + // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any + // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer + // offsets." + + if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset()) + return; + + int nextOffset = qualifier.layoutXfbOffset; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + bool contains64BitType = false; + bool contains32BitType = false; + bool contains16BitType = false; + int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType); + // see if we need to auto-assign an offset to this member + if (! memberQualifier.hasXfbOffset()) { + // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8" + if (contains64BitType) + RoundToPow2(nextOffset, 8); + else if (contains32BitType) + RoundToPow2(nextOffset, 4); + // "if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2" + else if (contains16BitType) + RoundToPow2(nextOffset, 2); + memberQualifier.layoutXfbOffset = nextOffset; + } else + nextOffset = memberQualifier.layoutXfbOffset; + nextOffset += memberSize; + } + + // The above gave all block members an offset, so we can take it off the block now, + // which will avoid double counting the offset usage. + qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd; +} + +// Calculate and save the offset of each block member, using the recursively +// defined block offset rules and the user-provided offset and align. +// +// Also, compute and save the total size of the block. For the block's size, arrayness +// is not taken into account, as each element is backed by a separate buffer. +// +void HlslParseContext::fixBlockUniformOffsets(const TQualifier& qualifier, TTypeList& typeList) +{ + if (! qualifier.isUniformOrBuffer()) + return; + if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar) + return; + + int offset = 0; + int memberSize; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + + // "When align is applied to an array, it effects only the start of the array, not the array's internal stride." + + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix; + int dummyStride; + int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride, + qualifier.layoutPacking, + subMatrixLayout != ElmNone + ? subMatrixLayout == ElmRowMajor + : qualifier.layoutMatrix == ElmRowMajor); + if (memberQualifier.hasOffset()) { + // "The specified offset must be a multiple + // of the base alignment of the type of the block member it qualifies, or a compile-time error results." + if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment)) + error(memberLoc, "must be a multiple of the member's alignment", "offset", ""); + + // "The offset qualifier forces the qualified member to start at or after the specified + // integral-constant expression, which will be its byte offset from the beginning of the buffer. + // "The actual offset of a member is computed as + // follows: If offset was declared, start with that offset, otherwise start with the next available offset." + offset = std::max(offset, memberQualifier.layoutOffset); + } + + // "The actual alignment of a member will be the greater of the specified align alignment and the standard + // (e.g., std140) base alignment for the member's type." + if (memberQualifier.hasAlign()) + memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign); + + // "If the resulting offset is not a multiple of the actual alignment, + // increase it to the first offset that is a multiple of + // the actual alignment." + RoundToPow2(offset, memberAlignment); + typeList[member].type->getQualifier().layoutOffset = offset; + offset += memberSize; + } +} + +// For an identifier that is already declared, add more qualification to it. +void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) +{ + TSymbol* symbol = symbolTable.find(identifier); + if (symbol == nullptr) { + error(loc, "identifier not previously declared", identifier.c_str(), ""); + return; + } + if (symbol->getAsFunction()) { + error(loc, "cannot re-qualify a function name", identifier.c_str(), ""); + return; + } + + if (qualifier.isAuxiliary() || + qualifier.isMemory() || + qualifier.isInterpolation() || + qualifier.hasLayout() || + qualifier.storage != EvqTemporary || + qualifier.precision != EpqNone) { + error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), ""); + return; + } + + // For read-only built-ins, add a new symbol for holding the modified qualifier. + // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block) + if (symbol->isReadOnly()) + symbol = symbolTable.copyUp(symbol); + + if (qualifier.invariant) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "invariant", ""); + symbol->getWritableType().getQualifier().invariant = true; + } else if (qualifier.noContraction) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "precise", ""); + symbol->getWritableType().getQualifier().noContraction = true; + } else if (qualifier.specConstant) { + symbol->getWritableType().getQualifier().makeSpecConstant(); + if (qualifier.hasSpecConstantId()) + symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId; + } else + warn(loc, "unknown requalification", "", ""); +} + +void HlslParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) +{ + for (unsigned int i = 0; i < identifiers.size(); ++i) + addQualifierToExisting(loc, qualifier, *identifiers[i]); +} + +// +// Update the intermediate for the given input geometry +// +bool HlslParseContext::handleInputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry) +{ + // these can be declared on non-entry-points, in which case they lose their meaning + if (! parsingEntrypointParameters) + return true; + + switch (geometry) { + case ElgPoints: // fall through + case ElgLines: // ... + case ElgTriangles: // ... + case ElgLinesAdjacency: // ... + case ElgTrianglesAdjacency: // ... + if (! intermediate.setInputPrimitive(geometry)) { + error(loc, "input primitive geometry redefinition", TQualifier::getGeometryString(geometry), ""); + return false; + } + break; + + default: + error(loc, "cannot apply to 'in'", TQualifier::getGeometryString(geometry), ""); + return false; + } + + return true; +} + +// +// Update the intermediate for the given output geometry +// +bool HlslParseContext::handleOutputGeometry(const TSourceLoc& loc, const TLayoutGeometry& geometry) +{ + // If this is not a geometry shader, ignore. It might be a mixed shader including several stages. + // Since that's an OK situation, return true for success. + if (language != EShLangGeometry) + return true; + + // these can be declared on non-entry-points, in which case they lose their meaning + if (! parsingEntrypointParameters) + return true; + + switch (geometry) { + case ElgPoints: + case ElgLineStrip: + case ElgTriangleStrip: + if (! intermediate.setOutputPrimitive(geometry)) { + error(loc, "output primitive geometry redefinition", TQualifier::getGeometryString(geometry), ""); + return false; + } + break; + default: + error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(geometry), ""); + return false; + } + + return true; +} + +// +// Selection attributes +// +void HlslParseContext::handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection* selection, + const TAttributes& attributes) +{ + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(loc, "attribute does not apply to a selection", "", ""); + break; + } + } +} + +// +// Switch attributes +// +void HlslParseContext::handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch* selection, + const TAttributes& attributes) +{ + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(loc, "attribute does not apply to a switch", "", ""); + break; + } + } +} + +// +// Loop attributes +// +void HlslParseContext::handleLoopAttributes(const TSourceLoc& loc, TIntermLoop* loop, + const TAttributes& attributes) +{ + if (loop == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + switch (it->name) { + case EatUnroll: + loop->setUnroll(); + break; + case EatLoop: + loop->setDontUnroll(); + break; + default: + warn(loc, "attribute does not apply to a loop", "", ""); + break; + } + } +} + +// +// Updating default qualifier for the case of a declaration with just a qualifier, +// no type, block, or identifier. +// +void HlslParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) +{ + if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) { + assert(language == EShLangTessControl || language == EShLangGeometry); + // const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices"; + } + if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) { + if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations)) + error(loc, "cannot change previously set layout value", "invocations", ""); + } + if (publicType.shaderQualifiers.geometry != ElgNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + switch (publicType.shaderQualifiers.geometry) { + case ElgPoints: + case ElgLines: + case ElgLinesAdjacency: + case ElgTriangles: + case ElgTrianglesAdjacency: + case ElgQuads: + case ElgIsolines: + break; + default: + error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), + ""); + } + } else if (publicType.qualifier.storage == EvqVaryingOut) { + handleOutputGeometry(loc, publicType.shaderQualifiers.geometry); + } else + error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), + GetStorageQualifierString(publicType.qualifier.storage)); + } + if (publicType.shaderQualifiers.spacing != EvsNone) + intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing); + if (publicType.shaderQualifiers.order != EvoNone) + intermediate.setVertexOrder(publicType.shaderQualifiers.order); + if (publicType.shaderQualifiers.pointMode) + intermediate.setPointMode(); + for (int i = 0; i < 3; ++i) { + if (publicType.shaderQualifiers.localSize[i] > 1) { + int max = 0; + switch (i) { + case 0: max = resources.maxComputeWorkGroupSizeX; break; + case 1: max = resources.maxComputeWorkGroupSizeY; break; + case 2: max = resources.maxComputeWorkGroupSizeZ; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", ""); + + // Fix the existing constant gl_WorkGroupSize with this new information. + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i)); + } + if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) { + intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i]); + // Set the workgroup built-in variable as a specialization constant + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + workGroupSize->getWritableType().getQualifier().specConstant = true; + } + } + if (publicType.shaderQualifiers.earlyFragmentTests) + intermediate.setEarlyFragmentTests(); + + const TQualifier& qualifier = publicType.qualifier; + + switch (qualifier.storage) { + case EvqUniform: + if (qualifier.hasMatrix()) + globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalUniformDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqBuffer: + if (qualifier.hasMatrix()) + globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalBufferDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqVaryingIn: + break; + case EvqVaryingOut: + if (qualifier.hasStream()) + globalOutputDefaults.layoutStream = qualifier.layoutStream; + if (qualifier.hasXfbBuffer()) + globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer; + if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) { + if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride)) + error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", + qualifier.layoutXfbBuffer); + } + break; + default: + error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); + return; + } +} + +// +// Take the sequence of statements that has been built up since the last case/default, +// put it on the list of top-level nodes for the current (inner-most) switch statement, +// and follow that by the case/default we are on now. (See switch topology comment on +// TIntermSwitch.) +// +void HlslParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) +{ + TIntermSequence* switchSequence = switchSequenceStack.back(); + + if (statements) { + statements->setOperator(EOpSequence); + switchSequence->push_back(statements); + } + if (branchNode) { + // check all previous cases for the same label (or both are 'default') + for (unsigned int s = 0; s < switchSequence->size(); ++s) { + TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode(); + if (prevBranch) { + TIntermTyped* prevExpression = prevBranch->getExpression(); + TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression(); + if (prevExpression == nullptr && newExpression == nullptr) + error(branchNode->getLoc(), "duplicate label", "default", ""); + else if (prevExpression != nullptr && + newExpression != nullptr && + prevExpression->getAsConstantUnion() && + newExpression->getAsConstantUnion() && + prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() == + newExpression->getAsConstantUnion()->getConstArray()[0].getIConst()) + error(branchNode->getLoc(), "duplicated value", "case", ""); + } + } + switchSequence->push_back(branchNode); + } +} + +// +// Turn the top-level node sequence built up of wrapupSwitchSubsequence +// into a switch node. +// +TIntermNode* HlslParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, + TIntermAggregate* lastStatements, const TAttributes& attributes) +{ + wrapupSwitchSubsequence(lastStatements, nullptr); + + if (expression == nullptr || + (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) || + expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) + error(loc, "condition must be a scalar integer expression", "switch", ""); + + // If there is nothing to do, drop the switch but still execute the expression + TIntermSequence* switchSequence = switchSequenceStack.back(); + if (switchSequence->size() == 0) + return expression; + + if (lastStatements == nullptr) { + // emulate a break for error recovery + lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc)); + lastStatements->setOperator(EOpSequence); + switchSequence->push_back(lastStatements); + } + + TIntermAggregate* body = new TIntermAggregate(EOpSequence); + body->getSequence() = *switchSequenceStack.back(); + body->setLoc(loc); + + TIntermSwitch* switchNode = new TIntermSwitch(expression, body); + switchNode->setLoc(loc); + handleSwitchAttributes(loc, switchNode, attributes); + + return switchNode; +} + +// Make a new symbol-table level that is made out of the members of a structure. +// This should be done as an anonymous struct (name is "") so that the symbol table +// finds the members with no explicit reference to a 'this' variable. +void HlslParseContext::pushThisScope(const TType& thisStruct, const TVector& functionDeclarators) +{ + // member variables + TVariable& thisVariable = *new TVariable(NewPoolTString(""), thisStruct); + symbolTable.pushThis(thisVariable); + + // member functions + for (auto it = functionDeclarators.begin(); it != functionDeclarators.end(); ++it) { + // member should have a prefix matching currentTypePrefix.back() + // but, symbol lookup within the class scope will just use the + // unprefixed name. Hence, there are two: one fully prefixed and + // one with no prefix. + TFunction& member = *it->function->clone(); + member.removePrefix(currentTypePrefix.back()); + symbolTable.insert(member); + } +} + +// Track levels of class/struct/namespace nesting with a prefix string using +// the type names separated by the scoping operator. E.g., two levels +// would look like: +// +// outer::inner +// +// The string is empty when at normal global level. +// +void HlslParseContext::pushNamespace(const TString& typeName) +{ + // make new type prefix + TString newPrefix; + if (currentTypePrefix.size() > 0) + newPrefix = currentTypePrefix.back(); + newPrefix.append(typeName); + newPrefix.append(scopeMangler); + currentTypePrefix.push_back(newPrefix); +} + +// Opposite of pushNamespace(), see above +void HlslParseContext::popNamespace() +{ + currentTypePrefix.pop_back(); +} + +// Use the class/struct nesting string to create a global name for +// a member of a class/struct. +void HlslParseContext::getFullNamespaceName(TString*& name) const +{ + if (currentTypePrefix.size() == 0) + return; + + TString* fullName = NewPoolTString(currentTypePrefix.back().c_str()); + fullName->append(*name); + name = fullName; +} + +// Helper function to add the namespace scope mangling syntax to a string. +void HlslParseContext::addScopeMangler(TString& name) +{ + name.append(scopeMangler); +} + +// Return true if this has uniform-interface like decorations. +bool HlslParseContext::hasUniform(const TQualifier& qualifier) const +{ + return qualifier.hasUniformLayout() || + qualifier.layoutPushConstant; +} + +// Potentially not the opposite of hasUniform(), as if some characteristic is +// ever used for more than one thing (e.g., uniform or input), hasUniform() should +// say it exists, but clearUniform() should leave it in place. +void HlslParseContext::clearUniform(TQualifier& qualifier) +{ + qualifier.clearUniformLayout(); + qualifier.layoutPushConstant = false; +} + +// Return false if builtIn by itself doesn't force this qualifier to be an input qualifier. +bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const +{ + switch (qualifier.builtIn) { + case EbvPosition: + case EbvPointSize: + return language != EShLangVertex && language != EShLangCompute && language != EShLangFragment; + case EbvClipDistance: + case EbvCullDistance: + return language != EShLangVertex && language != EShLangCompute; + case EbvFragCoord: + case EbvFace: + case EbvHelperInvocation: + case EbvLayer: + case EbvPointCoord: + case EbvSampleId: + case EbvSampleMask: + case EbvSamplePosition: + case EbvViewportIndex: + return language == EShLangFragment; + case EbvGlobalInvocationId: + case EbvLocalInvocationIndex: + case EbvLocalInvocationId: + case EbvNumWorkGroups: + case EbvWorkGroupId: + case EbvWorkGroupSize: + return language == EShLangCompute; + case EbvInvocationId: + return language == EShLangTessControl || language == EShLangTessEvaluation || language == EShLangGeometry; + case EbvPatchVertices: + return language == EShLangTessControl || language == EShLangTessEvaluation; + case EbvInstanceId: + case EbvInstanceIndex: + case EbvVertexId: + case EbvVertexIndex: + return language == EShLangVertex; + case EbvPrimitiveId: + return language == EShLangGeometry || language == EShLangFragment || language == EShLangTessControl; + case EbvTessLevelInner: + case EbvTessLevelOuter: + return language == EShLangTessEvaluation; + case EbvTessCoord: + return language == EShLangTessEvaluation; + default: + return false; + } +} + +// Return true if there are decorations to preserve for input-like storage. +bool HlslParseContext::hasInput(const TQualifier& qualifier) const +{ + if (qualifier.hasAnyLocation()) + return true; + + if (language == EShLangFragment && (qualifier.isInterpolation() || qualifier.centroid || qualifier.sample)) + return true; + + if (language == EShLangTessEvaluation && qualifier.patch) + return true; + + if (isInputBuiltIn(qualifier)) + return true; + + return false; +} + +// Return false if builtIn by itself doesn't force this qualifier to be an output qualifier. +bool HlslParseContext::isOutputBuiltIn(const TQualifier& qualifier) const +{ + switch (qualifier.builtIn) { + case EbvPosition: + case EbvPointSize: + case EbvClipVertex: + case EbvClipDistance: + case EbvCullDistance: + return language != EShLangFragment && language != EShLangCompute; + case EbvFragDepth: + case EbvFragDepthGreater: + case EbvFragDepthLesser: + case EbvSampleMask: + return language == EShLangFragment; + case EbvLayer: + case EbvViewportIndex: + return language == EShLangGeometry || language == EShLangVertex; + case EbvPrimitiveId: + return language == EShLangGeometry; + case EbvTessLevelInner: + case EbvTessLevelOuter: + return language == EShLangTessControl; + default: + return false; + } +} + +// Return true if there are decorations to preserve for output-like storage. +bool HlslParseContext::hasOutput(const TQualifier& qualifier) const +{ + if (qualifier.hasAnyLocation()) + return true; + + if (language != EShLangFragment && language != EShLangCompute && qualifier.hasXfb()) + return true; + + if (language == EShLangTessControl && qualifier.patch) + return true; + + if (language == EShLangGeometry && qualifier.hasStream()) + return true; + + if (isOutputBuiltIn(qualifier)) + return true; + + return false; +} + +// Make the IO decorations etc. be appropriate only for an input interface. +void HlslParseContext::correctInput(TQualifier& qualifier) +{ + clearUniform(qualifier); + if (language == EShLangVertex) + qualifier.clearInterstage(); + if (language != EShLangTessEvaluation) + qualifier.patch = false; + if (language != EShLangFragment) { + qualifier.clearInterpolation(); + qualifier.sample = false; + } + + qualifier.clearStreamLayout(); + qualifier.clearXfbLayout(); + + if (! isInputBuiltIn(qualifier)) + qualifier.builtIn = EbvNone; +} + +// Make the IO decorations etc. be appropriate only for an output interface. +void HlslParseContext::correctOutput(TQualifier& qualifier) +{ + clearUniform(qualifier); + if (language == EShLangFragment) + qualifier.clearInterstage(); + if (language != EShLangGeometry) + qualifier.clearStreamLayout(); + if (language == EShLangFragment) + qualifier.clearXfbLayout(); + if (language != EShLangTessControl) + qualifier.patch = false; + + switch (qualifier.builtIn) { + case EbvFragDepth: + intermediate.setDepthReplacing(); + intermediate.setDepth(EldAny); + break; + case EbvFragDepthGreater: + intermediate.setDepthReplacing(); + intermediate.setDepth(EldGreater); + qualifier.builtIn = EbvFragDepth; + break; + case EbvFragDepthLesser: + intermediate.setDepthReplacing(); + intermediate.setDepth(EldLess); + qualifier.builtIn = EbvFragDepth; + break; + default: + break; + } + + if (! isOutputBuiltIn(qualifier)) + qualifier.builtIn = EbvNone; +} + +// Make the IO decorations etc. be appropriate only for uniform type interfaces. +void HlslParseContext::correctUniform(TQualifier& qualifier) +{ + if (qualifier.declaredBuiltIn == EbvNone) + qualifier.declaredBuiltIn = qualifier.builtIn; + + qualifier.builtIn = EbvNone; + qualifier.clearInterstage(); + qualifier.clearInterstageLayout(); +} + +// Clear out all IO/Uniform stuff, so this has nothing to do with being an IO interface. +void HlslParseContext::clearUniformInputOutput(TQualifier& qualifier) +{ + clearUniform(qualifier); + correctUniform(qualifier); +} + + +// Set texture return type. Returns success (not all types are valid). +bool HlslParseContext::setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc) +{ + // Seed the output with an invalid index. We will set it to a valid one if we can. + sampler.structReturnIndex = TSampler::noReturnStruct; + + // Arrays aren't supported. + if (retType.isArray()) { + error(loc, "Arrays not supported in texture template types", "", ""); + return false; + } + + // If return type is a vector, remember the vector size in the sampler, and return. + if (retType.isVector() || retType.isScalar()) { + sampler.vectorSize = retType.getVectorSize(); + return true; + } + + // If it wasn't a vector, it must be a struct meeting certain requirements. The requirements + // are checked below: just check for struct-ness here. + if (!retType.isStruct()) { + error(loc, "Invalid texture template type", "", ""); + return false; + } + + // TODO: Subpass doesn't handle struct returns, due to some oddities with fn overloading. + if (sampler.isSubpass()) { + error(loc, "Unimplemented: structure template type in subpass input", "", ""); + return false; + } + + TTypeList* members = retType.getWritableStruct(); + + // Check for too many or not enough structure members. + if (members->size() > 4 || members->size() == 0) { + error(loc, "Invalid member count in texture template structure", "", ""); + return false; + } + + // Error checking: We must have <= 4 total components, all of the same basic type. + unsigned totalComponents = 0; + for (unsigned m = 0; m < members->size(); ++m) { + // Check for bad member types + if (!(*members)[m].type->isScalar() && !(*members)[m].type->isVector()) { + error(loc, "Invalid texture template struct member type", "", ""); + return false; + } + + const unsigned memberVectorSize = (*members)[m].type->getVectorSize(); + totalComponents += memberVectorSize; + + // too many total member components + if (totalComponents > 4) { + error(loc, "Too many components in texture template structure type", "", ""); + return false; + } + + // All members must be of a common basic type + if ((*members)[m].type->getBasicType() != (*members)[0].type->getBasicType()) { + error(loc, "Texture template structure members must same basic type", "", ""); + return false; + } + } + + // If the structure in the return type already exists in the table, we'll use it. Otherwise, we'll make + // a new entry. This is a linear search, but it hardly ever happens, and the list cannot be very large. + for (unsigned int idx = 0; idx < textureReturnStruct.size(); ++idx) { + if (textureReturnStruct[idx] == members) { + sampler.structReturnIndex = idx; + return true; + } + } + + // It wasn't found as an existing entry. See if we have room for a new one. + if (textureReturnStruct.size() >= TSampler::structReturnSlots) { + error(loc, "Texture template struct return slots exceeded", "", ""); + return false; + } + + // Insert it in the vector that tracks struct return types. + sampler.structReturnIndex = unsigned(textureReturnStruct.size()); + textureReturnStruct.push_back(members); + + // Success! + return true; +} + +// Return the sampler return type in retType. +void HlslParseContext::getTextureReturnType(const TSampler& sampler, TType& retType) const +{ + if (sampler.hasReturnStruct()) { + assert(textureReturnStruct.size() >= sampler.structReturnIndex); + + // We land here if the texture return is a structure. + TTypeList* blockStruct = textureReturnStruct[sampler.structReturnIndex]; + + const TType resultType(blockStruct, ""); + retType.shallowCopy(resultType); + } else { + // We land here if the texture return is a vector or scalar. + const TType resultType(sampler.type, EvqTemporary, sampler.getVectorSize()); + retType.shallowCopy(resultType); + } +} + + +// Return a symbol for the tessellation linkage variable of the given TBuiltInVariable type +TIntermSymbol* HlslParseContext::findTessLinkageSymbol(TBuiltInVariable biType) const +{ + const auto it = builtInTessLinkageSymbols.find(biType); + if (it == builtInTessLinkageSymbols.end()) // if it wasn't declared by the user, return nullptr + return nullptr; + + return intermediate.addSymbol(*it->second->getAsVariable()); +} + +// Find the patch constant function (issues error, returns nullptr if not found) +const TFunction* HlslParseContext::findPatchConstantFunction(const TSourceLoc& loc) +{ + if (symbolTable.isFunctionNameVariable(patchConstantFunctionName)) { + error(loc, "can't use variable in patch constant function", patchConstantFunctionName.c_str(), ""); + return nullptr; + } + + const TString mangledName = patchConstantFunctionName + "("; + + // create list of PCF candidates + TVector candidateList; + bool builtIn; + symbolTable.findFunctionNameList(mangledName, candidateList, builtIn); + + // We have to have one and only one, or we don't know which to pick: the patchconstantfunc does not + // allow any disambiguation of overloads. + if (candidateList.empty()) { + error(loc, "patch constant function not found", patchConstantFunctionName.c_str(), ""); + return nullptr; + } + + // Based on directed experiments, it appears that if there are overloaded patchconstantfunctions, + // HLSL picks the last one in shader source order. Since that isn't yet implemented here, error + // out if there is more than one candidate. + if (candidateList.size() > 1) { + error(loc, "ambiguous patch constant function", patchConstantFunctionName.c_str(), ""); + return nullptr; + } + + return candidateList[0]; +} + +// Finalization step: Add patch constant function invocation +void HlslParseContext::addPatchConstantInvocation() +{ + TSourceLoc loc; + loc.init(); + + // If there's no patch constant function, or we're not a HS, do nothing. + if (patchConstantFunctionName.empty() || language != EShLangTessControl) + return; + + // Look for built-in variables in a function's parameter list. + const auto findBuiltIns = [&](const TFunction& function, std::set& builtIns) { + for (int p=0; pgetQualifier().storage; + + if (storage == EvqConstReadOnly) // treated identically to input + storage = EvqIn; + + if (function[p].getDeclaredBuiltIn() != EbvNone) + builtIns.insert(HlslParseContext::tInterstageIoData(function[p].getDeclaredBuiltIn(), storage)); + else + builtIns.insert(HlslParseContext::tInterstageIoData(function[p].type->getQualifier().builtIn, storage)); + } + }; + + // If we synthesize a built-in interface variable, we must add it to the linkage. + const auto addToLinkage = [&](const TType& type, const TString* name, TIntermSymbol** symbolNode) { + if (name == nullptr) { + error(loc, "unable to locate patch function parameter name", "", ""); + return; + } else { + TVariable& variable = *new TVariable(name, type); + if (! symbolTable.insert(variable)) { + error(loc, "unable to declare patch constant function interface variable", name->c_str(), ""); + return; + } + + globalQualifierFix(loc, variable.getWritableType().getQualifier()); + + if (symbolNode != nullptr) + *symbolNode = intermediate.addSymbol(variable); + + trackLinkage(variable); + } + }; + + const auto isOutputPatch = [](TFunction& patchConstantFunction, int param) { + const TType& type = *patchConstantFunction[param].type; + const TBuiltInVariable biType = patchConstantFunction[param].getDeclaredBuiltIn(); + + return type.isSizedArray() && biType == EbvOutputPatch; + }; + + // We will perform these steps. Each is in a scoped block for separation: they could + // become separate functions to make addPatchConstantInvocation shorter. + // + // 1. Union the interfaces, and create built-ins for anything present in the PCF and + // declared as a built-in variable that isn't present in the entry point's signature. + // + // 2. Synthesizes a call to the patchconstfunction using built-in variables from either main, + // or the ones we created. Matching is based on built-in type. We may use synthesized + // variables from (1) above. + // + // 2B: Synthesize per control point invocations of wrapped entry point if the PCF requires them. + // + // 3. Create a return sequence: copy the return value (if any) from the PCF to a + // (non-sanitized) output variable. In case this may involve multiple copies, such as for + // an arrayed variable, a temporary copy of the PCF output is created to avoid multiple + // indirections into a complex R-value coming from the call to the PCF. + // + // 4. Create a barrier. + // + // 5/5B. Call the PCF inside an if test for (invocation id == 0). + + TFunction* patchConstantFunctionPtr = const_cast(findPatchConstantFunction(loc)); + + if (patchConstantFunctionPtr == nullptr) + return; + + TFunction& patchConstantFunction = *patchConstantFunctionPtr; + + const int pcfParamCount = patchConstantFunction.getParamCount(); + TIntermSymbol* invocationIdSym = findTessLinkageSymbol(EbvInvocationId); + TIntermSequence& epBodySeq = entryPointFunctionBody->getAsAggregate()->getSequence(); + + int outPatchParam = -1; // -1 means there isn't one. + + // ================ Step 1A: Union Interfaces ================ + // Our patch constant function. + { + std::set pcfBuiltIns; // patch constant function built-ins + std::set epfBuiltIns; // entry point function built-ins + + assert(entryPointFunction); + assert(entryPointFunctionBody); + + findBuiltIns(patchConstantFunction, pcfBuiltIns); + findBuiltIns(*entryPointFunction, epfBuiltIns); + + // Find the set of built-ins in the PCF that are not present in the entry point. + std::set notInEntryPoint; + + notInEntryPoint = pcfBuiltIns; + + // std::set_difference not usable on unordered containers + for (auto bi = epfBuiltIns.begin(); bi != epfBuiltIns.end(); ++bi) + notInEntryPoint.erase(*bi); + + // Now we'll add those to the entry and to the linkage. + for (int p=0; pgetQualifier().storage; + + // Track whether there is an output patch param + if (isOutputPatch(patchConstantFunction, p)) { + if (outPatchParam >= 0) { + // Presently we only support one per ctrl pt input. + error(loc, "unimplemented: multiple output patches in patch constant function", "", ""); + return; + } + outPatchParam = p; + } + + if (biType != EbvNone) { + TType* paramType = patchConstantFunction[p].type->clone(); + + if (storage == EvqConstReadOnly) // treated identically to input + storage = EvqIn; + + // Presently, the only non-built-in we support is InputPatch, which is treated as + // a pseudo-built-in. + if (biType == EbvInputPatch) { + builtInTessLinkageSymbols[biType] = inputPatch; + } else if (biType == EbvOutputPatch) { + // Nothing... + } else { + // Use the original declaration type for the linkage + paramType->getQualifier().builtIn = biType; + if (biType == EbvTessLevelInner || biType == EbvTessLevelInner) + paramType->getQualifier().patch = true; + + if (notInEntryPoint.count(tInterstageIoData(biType, storage)) == 1) + addToLinkage(*paramType, patchConstantFunction[p].name, nullptr); + } + } + } + + // If we didn't find it because the shader made one, add our own. + if (invocationIdSym == nullptr) { + TType invocationIdType(EbtUint, EvqIn, 1); + TString* invocationIdName = NewPoolTString("InvocationId"); + invocationIdType.getQualifier().builtIn = EbvInvocationId; + addToLinkage(invocationIdType, invocationIdName, &invocationIdSym); + } + + assert(invocationIdSym); + } + + TIntermTyped* pcfArguments = nullptr; + TVariable* perCtrlPtVar = nullptr; + + // ================ Step 1B: Argument synthesis ================ + // Create pcfArguments for synthesis of patchconstantfunction invocation + { + for (int p=0; pgetWritableType().getQualifier().makeTemporary(); + } + inputArg = intermediate.addSymbol(*perCtrlPtVar, loc); + } else { + // find which built-in it is + const TBuiltInVariable biType = patchConstantFunction[p].getDeclaredBuiltIn(); + + if (biType == EbvInputPatch && inputPatch == nullptr) { + error(loc, "unimplemented: PCF input patch without entry point input patch parameter", "", ""); + return; + } + + inputArg = findTessLinkageSymbol(biType); + + if (inputArg == nullptr) { + error(loc, "unable to find patch constant function built-in variable", "", ""); + return; + } + } + + if (pcfParamCount == 1) + pcfArguments = inputArg; + else + pcfArguments = intermediate.growAggregate(pcfArguments, inputArg); + } + } + + // ================ Step 2: Synthesize call to PCF ================ + TIntermAggregate* pcfCallSequence = nullptr; + TIntermTyped* pcfCall = nullptr; + + { + // Create a function call to the patchconstantfunction + if (pcfArguments) + addInputArgumentConversions(patchConstantFunction, pcfArguments); + + // Synthetic call. + pcfCall = intermediate.setAggregateOperator(pcfArguments, EOpFunctionCall, patchConstantFunction.getType(), loc); + pcfCall->getAsAggregate()->setUserDefined(); + pcfCall->getAsAggregate()->setName(patchConstantFunction.getMangledName()); + intermediate.addToCallGraph(infoSink, intermediate.getEntryPointMangledName().c_str(), + patchConstantFunction.getMangledName()); + + if (pcfCall->getAsAggregate()) { + TQualifierList& qualifierList = pcfCall->getAsAggregate()->getQualifierList(); + for (int i = 0; i < patchConstantFunction.getParamCount(); ++i) { + TStorageQualifier qual = patchConstantFunction[i].type->getQualifier().storage; + qualifierList.push_back(qual); + } + pcfCall = addOutputArgumentConversions(patchConstantFunction, *pcfCall->getAsOperator()); + } + } + + // ================ Step 2B: Per Control Point synthesis ================ + // If there is per control point data, we must either emulate that with multiple + // invocations of the entry point to build up an array, or (TODO:) use a yet + // unavailable extension to look across the SIMD lanes. This is the former + // as a placeholder for the latter. + if (outPatchParam >= 0) { + // We must introduce a local temp variable of the type wanted by the PCF input. + const int arraySize = patchConstantFunction[outPatchParam].type->getOuterArraySize(); + + if (entryPointFunction->getType().getBasicType() == EbtVoid) { + error(loc, "entry point must return a value for use with patch constant function", "", ""); + return; + } + + // Create calls to wrapped main to fill in the array. We will substitute fixed values + // of invocation ID when calling the wrapped main. + + // This is the type of the each member of the per ctrl point array. + const TType derefType(perCtrlPtVar->getType(), 0); + + for (int cpt = 0; cpt < arraySize; ++cpt) { + // TODO: improve. substr(1) here is to avoid the '@' that was grafted on but isn't in the symtab + // for this function. + const TString origName = entryPointFunction->getName().substr(1); + TFunction callee(&origName, TType(EbtVoid)); + TIntermTyped* callingArgs = nullptr; + + for (int i = 0; i < entryPointFunction->getParamCount(); i++) { + TParameter& param = (*entryPointFunction)[i]; + TType& paramType = *param.type; + + if (paramType.getQualifier().isParamOutput()) { + error(loc, "unimplemented: entry point outputs in patch constant function invocation", "", ""); + return; + } + + if (paramType.getQualifier().isParamInput()) { + TIntermTyped* arg = nullptr; + if ((*entryPointFunction)[i].getDeclaredBuiltIn() == EbvInvocationId) { + // substitute invocation ID with the array element ID + arg = intermediate.addConstantUnion(cpt, loc); + } else { + TVariable* argVar = makeInternalVariable(*param.name, *param.type); + argVar->getWritableType().getQualifier().makeTemporary(); + arg = intermediate.addSymbol(*argVar); + } + + handleFunctionArgument(&callee, callingArgs, arg); + } + } + + // Call and assign to per ctrl point variable + currentCaller = intermediate.getEntryPointMangledName().c_str(); + TIntermTyped* callReturn = handleFunctionCall(loc, &callee, callingArgs); + TIntermTyped* index = intermediate.addConstantUnion(cpt, loc); + TIntermSymbol* perCtrlPtSym = intermediate.addSymbol(*perCtrlPtVar, loc); + TIntermTyped* element = intermediate.addIndex(EOpIndexDirect, perCtrlPtSym, index, loc); + element->setType(derefType); + element->setLoc(loc); + + pcfCallSequence = intermediate.growAggregate(pcfCallSequence, + handleAssign(loc, EOpAssign, element, callReturn)); + } + } + + // ================ Step 3: Create return Sequence ================ + // Return sequence: copy PCF result to a temporary, then to shader output variable. + if (pcfCall->getBasicType() != EbtVoid) { + const TType* retType = &patchConstantFunction.getType(); // return type from the PCF + TType outType; // output type that goes with the return type. + outType.shallowCopy(*retType); + + // substitute the output type + const auto newLists = ioTypeMap.find(retType->getStruct()); + if (newLists != ioTypeMap.end()) + outType.setStruct(newLists->second.output); + + // Substitute the top level type's built-in type + if (patchConstantFunction.getDeclaredBuiltInType() != EbvNone) + outType.getQualifier().builtIn = patchConstantFunction.getDeclaredBuiltInType(); + + outType.getQualifier().patch = true; // make it a per-patch variable + + TVariable* pcfOutput = makeInternalVariable("@patchConstantOutput", outType); + pcfOutput->getWritableType().getQualifier().storage = EvqVaryingOut; + + if (pcfOutput->getType().isStruct()) + flatten(*pcfOutput, false); + + assignToInterface(*pcfOutput); + + TIntermSymbol* pcfOutputSym = intermediate.addSymbol(*pcfOutput, loc); + + // The call to the PCF is a complex R-value: we want to store it in a temp to avoid + // repeated calls to the PCF: + TVariable* pcfCallResult = makeInternalVariable("@patchConstantResult", *retType); + pcfCallResult->getWritableType().getQualifier().makeTemporary(); + + TIntermSymbol* pcfResultVar = intermediate.addSymbol(*pcfCallResult, loc); + TIntermNode* pcfResultAssign = handleAssign(loc, EOpAssign, pcfResultVar, pcfCall); + TIntermNode* pcfResultToOut = handleAssign(loc, EOpAssign, pcfOutputSym, + intermediate.addSymbol(*pcfCallResult, loc)); + + pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultAssign); + pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfResultToOut); + } else { + pcfCallSequence = intermediate.growAggregate(pcfCallSequence, pcfCall); + } + + // ================ Step 4: Barrier ================ + TIntermTyped* barrier = new TIntermAggregate(EOpBarrier); + barrier->setLoc(loc); + barrier->setType(TType(EbtVoid)); + epBodySeq.insert(epBodySeq.end(), barrier); + + // ================ Step 5: Test on invocation ID ================ + TIntermTyped* zero = intermediate.addConstantUnion(0, loc, true); + TIntermTyped* cmp = intermediate.addBinaryNode(EOpEqual, invocationIdSym, zero, loc, TType(EbtBool)); + + + // ================ Step 5B: Create if statement on Invocation ID == 0 ================ + intermediate.setAggregateOperator(pcfCallSequence, EOpSequence, TType(EbtVoid), loc); + TIntermTyped* invocationIdTest = new TIntermSelection(cmp, pcfCallSequence, nullptr); + invocationIdTest->setLoc(loc); + + // add our test sequence before the return. + epBodySeq.insert(epBodySeq.end(), invocationIdTest); +} + +// Finalization step: remove unused buffer blocks from linkage (we don't know until the +// shader is entirely compiled). +// Preserve order of remaining symbols. +void HlslParseContext::removeUnusedStructBufferCounters() +{ + const auto endIt = std::remove_if(linkageSymbols.begin(), linkageSymbols.end(), + [this](const TSymbol* sym) { + const auto sbcIt = structBufferCounter.find(sym->getName()); + return sbcIt != structBufferCounter.end() && !sbcIt->second; + }); + + linkageSymbols.erase(endIt, linkageSymbols.end()); +} + +// Finalization step: patch texture shadow modes to match samplers they were combined with +void HlslParseContext::fixTextureShadowModes() +{ + for (auto symbol = linkageSymbols.begin(); symbol != linkageSymbols.end(); ++symbol) { + TSampler& sampler = (*symbol)->getWritableType().getSampler(); + + if (sampler.isTexture()) { + const auto shadowMode = textureShadowVariant.find((*symbol)->getUniqueId()); + if (shadowMode != textureShadowVariant.end()) { + + if (shadowMode->second->overloaded()) + // Texture needs legalization if it's been seen with both shadow and non-shadow modes. + intermediate.setNeedsLegalization(); + + sampler.shadow = shadowMode->second->isShadowId((*symbol)->getUniqueId()); + } + } + } +} + +// Finalization step: patch append methods to use proper stream output, which isn't known until +// main is parsed, which could happen after the append method is parsed. +void HlslParseContext::finalizeAppendMethods() +{ + TSourceLoc loc; + loc.init(); + + // Nothing to do: bypass test for valid stream output. + if (gsAppends.empty()) + return; + + if (gsStreamOutput == nullptr) { + error(loc, "unable to find output symbol for Append()", "", ""); + return; + } + + // Patch append sequences, now that we know the stream output symbol. + for (auto append = gsAppends.begin(); append != gsAppends.end(); ++append) { + append->node->getSequence()[0] = + handleAssign(append->loc, EOpAssign, + intermediate.addSymbol(*gsStreamOutput, append->loc), + append->node->getSequence()[0]->getAsTyped()); + } +} + +// post-processing +void HlslParseContext::finish() +{ + // Error check: There was a dangling .mips operator. These are not nested constructs in the grammar, so + // cannot be detected there. This is not strictly needed in a non-validating parser; it's just helpful. + if (! mipsOperatorMipArg.empty()) { + error(mipsOperatorMipArg.back().loc, "unterminated mips operator:", "", ""); + } + + removeUnusedStructBufferCounters(); + addPatchConstantInvocation(); + fixTextureShadowModes(); + finalizeAppendMethods(); + + // Communicate out (esp. for command line) that we formed AST that will make + // illegal AST SPIR-V and it needs transforms to legalize it. + if (intermediate.needsLegalization() && (messages & EShMsgHlslLegalization)) + infoSink.info << "WARNING: AST will form illegal SPIR-V; need to transform to legalize"; + + TParseContextBase::finish(); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/HLSL/hlslParseHelper.h b/third_party/glslang/glslang/HLSL/hlslParseHelper.h new file mode 100644 index 0000000..b92856a --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslParseHelper.h @@ -0,0 +1,514 @@ +// +// Copyright (C) 2016-2018 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef HLSL_PARSE_INCLUDED_ +#define HLSL_PARSE_INCLUDED_ + +#include "../MachineIndependent/parseVersions.h" +#include "../MachineIndependent/ParseHelper.h" +#include "../MachineIndependent/attribute.h" + +#include + +namespace glslang { + +class TFunctionDeclarator; + +class HlslParseContext : public TParseContextBase { +public: + HlslParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, + int version, EProfile, const SpvVersion& spvVersion, EShLanguage, TInfoSink&, + const TString sourceEntryPointName, + bool forwardCompatible = false, EShMessages messages = EShMsgDefault); + virtual ~HlslParseContext(); + void initializeExtensionBehavior() override; + + void setLimits(const TBuiltInResource&) override; + bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override; + virtual const char* getGlobalUniformBlockName() const override { return "$Global"; } + virtual void setUniformBlockDefaults(TType& block) const override + { + block.getQualifier().layoutPacking = globalUniformDefaults.layoutPacking; + block.getQualifier().layoutMatrix = globalUniformDefaults.layoutMatrix; + } + + void reservedPpErrorCheck(const TSourceLoc&, const char* /*name*/, const char* /*op*/) override { } + bool lineContinuationCheck(const TSourceLoc&, bool /*endOfComment*/) override { return true; } + bool lineDirectiveShouldSetNextLine() const override { return true; } + bool builtInName(const TString&); + + void handlePragma(const TSourceLoc&, const TVector&) override; + TIntermTyped* handleVariable(const TSourceLoc&, const TString* string); + TIntermTyped* handleBracketDereference(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + TIntermTyped* handleBracketOperator(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + + TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode); + TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field); + bool isBuiltInMethod(const TSourceLoc&, TIntermTyped* base, const TString& field); + void assignToInterface(TVariable& variable); + void handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype); + TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&, const TAttributes&, TIntermNode*& entryPointTree); + TIntermNode* transformEntryPoint(const TSourceLoc&, TFunction&, const TAttributes&); + void handleEntryPointAttributes(const TSourceLoc&, const TAttributes&); + void transferTypeAttributes(const TSourceLoc&, const TAttributes&, TType&, bool allowEntry = false); + void handleFunctionBody(const TSourceLoc&, TFunction&, TIntermNode* functionBody, TIntermNode*& node); + void remapEntryPointIO(TFunction& function, TVariable*& returnValue, TVector& inputs, TVector& outputs); + void remapNonEntryPointIO(TFunction& function); + TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*); + void handleFunctionArgument(TFunction*, TIntermTyped*& arguments, TIntermTyped* newArg); + TIntermTyped* handleAssign(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleAssignToMatrixSwizzle(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermTyped*); + TIntermAggregate* assignClipCullDistance(const TSourceLoc&, TOperator, int semanticId, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* assignPosition(const TSourceLoc&, TOperator, TIntermTyped* left, TIntermTyped* right); + void decomposeIntrinsic(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void decomposeSampleMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void decomposeStructBufferMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void decomposeGeometryMethods(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments); + void pushFrontArguments(TIntermTyped* front, TIntermTyped*& arguments); + void addInputArgumentConversions(const TFunction&, TIntermTyped*&); + void expandArguments(const TSourceLoc&, const TFunction&, TIntermTyped*&); + TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermOperator&); + void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&); + TFunction* makeConstructorCall(const TSourceLoc&, const TType&); + void handleSemantic(TSourceLoc, TQualifier&, TBuiltInVariable, const TString& upperCase); + void handlePackOffset(const TSourceLoc&, TQualifier&, const glslang::TString& location, + const glslang::TString* component); + void handleRegister(const TSourceLoc&, TQualifier&, const glslang::TString* profile, const glslang::TString& desc, + int subComponent, const glslang::TString*); + TIntermTyped* convertConditionalExpression(const TSourceLoc&, TIntermTyped*, bool mustBeScalar = true); + TIntermAggregate* handleSamplerTextureCombine(const TSourceLoc& loc, TIntermTyped* argTex, TIntermTyped* argSampler); + + bool parseMatrixSwizzleSelector(const TSourceLoc&, const TString&, int cols, int rows, TSwizzleSelectors&); + int getMatrixComponentsColumn(int rows, const TSwizzleSelectors&); + void assignError(const TSourceLoc&, const char* op, TString left, TString right); + void unaryOpError(const TSourceLoc&, const char* op, TString operand); + void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right); + void variableCheck(TIntermTyped*& nodePtr); + void constantValueCheck(TIntermTyped* node, const char* token); + void integerCheck(const TIntermTyped* node, const char* token); + void globalCheck(const TSourceLoc&, const char* token); + bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&); + void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&); + void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&); + void structArrayCheck(const TSourceLoc&, const TType& structure); + bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType); + void globalQualifierFix(const TSourceLoc&, TQualifier&); + bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType); + void mergeQualifiers(TQualifier& dst, const TQualifier& src); + int computeSamplerTypeIndex(TSampler&); + TSymbol* redeclareBuiltinVariable(const TSourceLoc&, const TString&, const TQualifier&, const TShaderQualifiers&); + void paramFix(TType& type); + void specializationCheck(const TSourceLoc&, const TType&, const char* op); + + void setLayoutQualifier(const TSourceLoc&, TQualifier&, TString&); + void setLayoutQualifier(const TSourceLoc&, TQualifier&, TString&, const TIntermTyped*); + void setSpecConstantId(const TSourceLoc&, TQualifier&, int value); + void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly); + void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&); + + const TFunction* findFunction(const TSourceLoc& loc, TFunction& call, bool& builtIn, int& thisDepth, TIntermTyped*& args); + void addGenMulArgumentConversion(const TSourceLoc& loc, TFunction& call, TIntermTyped*& args); + void declareTypedef(const TSourceLoc&, const TString& identifier, const TType&); + void declareStruct(const TSourceLoc&, TString& structName, TType&); + TSymbol* lookupUserType(const TString&, TType&); + TIntermNode* declareVariable(const TSourceLoc&, const TString& identifier, TType&, TIntermTyped* initializer = 0); + void lengthenList(const TSourceLoc&, TIntermSequence& list, int size, TIntermTyped* scalarInit); + TIntermTyped* handleConstructor(const TSourceLoc&, TIntermTyped*, const TType&); + TIntermTyped* addConstructor(const TSourceLoc&, TIntermTyped*, const TType&); + TIntermTyped* convertArray(TIntermTyped*, const TType&); + TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&); + TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); + void declareBlock(const TSourceLoc&, TType&, const TString* instanceName = 0); + void declareStructBufferCounter(const TSourceLoc& loc, const TType& bufferType, const TString& name); + void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation); + void fixXfbOffsets(TQualifier&, TTypeList&); + void fixBlockUniformOffsets(const TQualifier&, TTypeList&); + void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier); + void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&); + void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&); + void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode); + TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body, const TAttributes&); + + void nestLooping() { ++loopNestingLevel; } + void unnestLooping() { --loopNestingLevel; } + void nestAnnotations() { ++annotationNestingLevel; } + void unnestAnnotations() { --annotationNestingLevel; } + int getAnnotationNestingLevel() { return annotationNestingLevel; } + void pushScope() { symbolTable.push(); } + void popScope() { symbolTable.pop(0); } + + void pushThisScope(const TType&, const TVector&); + void popThisScope() { symbolTable.pop(0); } + + void pushImplicitThis(TVariable* thisParameter) { implicitThisStack.push_back(thisParameter); } + void popImplicitThis() { implicitThisStack.pop_back(); } + TVariable* getImplicitThis(int thisDepth) const { return implicitThisStack[implicitThisStack.size() - thisDepth]; } + + void pushNamespace(const TString& name); + void popNamespace(); + void getFullNamespaceName(TString*&) const; + void addScopeMangler(TString&); + + void beginParameterParsing(TFunction& function) + { + parsingEntrypointParameters = isEntrypointName(function.getName()); + } + + void pushSwitchSequence(TIntermSequence* sequence) { switchSequenceStack.push_back(sequence); } + void popSwitchSequence() { switchSequenceStack.pop_back(); } + + virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, + TTypeList* typeList = nullptr) override; + + // Apply L-value conversions. E.g, turning a write to a RWTexture into an ImageStore. + TIntermTyped* handleLvalue(const TSourceLoc&, const char* op, TIntermTyped*& node); + bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; + + TLayoutFormat getLayoutFromTxType(const TSourceLoc&, const TType&); + + bool handleOutputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry); + bool handleInputGeometry(const TSourceLoc&, const TLayoutGeometry& geometry); + + // Determine selection control from attributes + void handleSelectionAttributes(const TSourceLoc& loc, TIntermSelection*, const TAttributes& attributes); + void handleSwitchAttributes(const TSourceLoc& loc, TIntermSwitch*, const TAttributes& attributes); + + // Determine loop control from attributes + void handleLoopAttributes(const TSourceLoc& loc, TIntermLoop*, const TAttributes& attributes); + + // Share struct buffer deep types + void shareStructBufferType(TType&); + + // Set texture return type of the given sampler. Returns success (not all types are valid). + bool setTextureReturnType(TSampler& sampler, const TType& retType, const TSourceLoc& loc); + + // Obtain the sampler return type of the given sampler in retType. + void getTextureReturnType(const TSampler& sampler, TType& retType) const; + + TAttributeType attributeFromName(const TString& nameSpace, const TString& name) const; + +protected: + struct TFlattenData { + TFlattenData() : nextBinding(TQualifier::layoutBindingEnd), + nextLocation(TQualifier::layoutLocationEnd) { } + TFlattenData(int nb, int nl) : nextBinding(nb), nextLocation(nl) { } + + TVector members; // individual flattened variables + TVector offsets; // offset to next tree level + unsigned int nextBinding; // next binding to use. + unsigned int nextLocation; // next location to use + }; + + void fixConstInit(const TSourceLoc&, const TString& identifier, TType& type, TIntermTyped*& initializer); + void inheritGlobalDefaults(TQualifier& dst) const; + TVariable* makeInternalVariable(const char* name, const TType&) const; + TVariable* makeInternalVariable(const TString& name, const TType& type) const { + return makeInternalVariable(name.c_str(), type); + } + TIntermSymbol* makeInternalVariableNode(const TSourceLoc&, const char* name, const TType&) const; + TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&, bool track); + void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&, bool track); + TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); + TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer, TIntermTyped* scalarInit); + bool isScalarConstructor(const TIntermNode*); + TOperator mapAtomicOp(const TSourceLoc& loc, TOperator op, bool isImage); + bool isEntrypointName(const TString& name) { return name.compare(intermediate.getEntryPointName().c_str()) == 0; } + + // Return true if this node requires L-value conversion (e.g, to an imageStore). + bool shouldConvertLValue(const TIntermNode*) const; + + // Array and struct flattening + TIntermTyped* flattenAccess(TIntermTyped* base, int member); + TIntermTyped* flattenAccess(int uniqueId, int member, TStorageQualifier outerStorage, const TType&, int subset = -1); + int findSubtreeOffset(const TIntermNode&) const; + int findSubtreeOffset(const TType&, int subset, const TVector& offsets) const; + bool shouldFlatten(const TType&, TStorageQualifier, bool topLevel) const; + bool wasFlattened(const TIntermTyped* node) const; + bool wasFlattened(int id) const { return flattenMap.find(id) != flattenMap.end(); } + int addFlattenedMember(const TVariable&, const TType&, TFlattenData&, const TString& name, bool linkage, + const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes); + + // Structure splitting (splits interstage built-in types into its own struct) + void split(const TVariable&); + void splitBuiltIn(const TString& baseName, const TType& memberType, const TArraySizes*, const TQualifier&); + const TType& split(const TType& type, const TString& name, const TQualifier&); + bool wasSplit(const TIntermTyped* node) const; + bool wasSplit(int id) const { return splitNonIoVars.find(id) != splitNonIoVars.end(); } + TVariable* getSplitNonIoVar(int id) const; + void addPatchConstantInvocation(); + void fixTextureShadowModes(); + void finalizeAppendMethods(); + TIntermTyped* makeIntegerIndex(TIntermTyped*); + + void fixBuiltInIoType(TType&); + + void flatten(const TVariable& variable, bool linkage, bool arrayed = false); + int flatten(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage, + const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes); + int flattenStruct(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage, + const TQualifier& outerQualifier, const TArraySizes* builtInArraySizes); + int flattenArray(const TVariable& variable, const TType&, TFlattenData&, TString name, bool linkage, + const TQualifier& outerQualifier); + + bool hasUniform(const TQualifier& qualifier) const; + void clearUniform(TQualifier& qualifier); + bool isInputBuiltIn(const TQualifier& qualifier) const; + bool hasInput(const TQualifier& qualifier) const; + void correctOutput(TQualifier& qualifier); + bool isOutputBuiltIn(const TQualifier& qualifier) const; + bool hasOutput(const TQualifier& qualifier) const; + void correctInput(TQualifier& qualifier); + void correctUniform(TQualifier& qualifier); + void clearUniformInputOutput(TQualifier& qualifier); + + // Test method names + bool isStructBufferMethod(const TString& name) const; + void counterBufferType(const TSourceLoc& loc, TType& type); + + // Return standard sample position array + TIntermConstantUnion* getSamplePosArray(int count); + + TType* getStructBufferContentType(const TType& type) const; + bool isStructBufferType(const TType& type) const { return getStructBufferContentType(type) != nullptr; } + TIntermTyped* indexStructBufferContent(const TSourceLoc& loc, TIntermTyped* buffer) const; + TIntermTyped* getStructBufferCounter(const TSourceLoc& loc, TIntermTyped* buffer); + TString getStructBuffCounterName(const TString&) const; + void addStructBuffArguments(const TSourceLoc& loc, TIntermAggregate*&); + void addStructBufferHiddenCounterParam(const TSourceLoc& loc, TParameter&, TIntermAggregate*&); + + // Return true if this type is a reference. This is not currently a type method in case that's + // a language specific answer. + bool isReference(const TType& type) const { return isStructBufferType(type); } + + // Return true if this a buffer type that has an associated counter buffer. + bool hasStructBuffCounter(const TType&) const; + + // Finalization step: remove unused buffer blocks from linkage (we don't know until the + // shader is entirely compiled) + void removeUnusedStructBufferCounters(); + + static bool isClipOrCullDistance(TBuiltInVariable); + static bool isClipOrCullDistance(const TQualifier& qual) { return isClipOrCullDistance(qual.builtIn); } + static bool isClipOrCullDistance(const TType& type) { return isClipOrCullDistance(type.getQualifier()); } + + // Find the patch constant function (issues error, returns nullptr if not found) + const TFunction* findPatchConstantFunction(const TSourceLoc& loc); + + // Pass through to base class after remembering built-in mappings. + using TParseContextBase::trackLinkage; + void trackLinkage(TSymbol& variable) override; + + void finish() override; // post-processing + + // Linkage symbol helpers + TIntermSymbol* findTessLinkageSymbol(TBuiltInVariable biType) const; + + // Current state of parsing + int annotationNestingLevel; // 0 if outside all annotations + + HlslParseContext(HlslParseContext&); + HlslParseContext& operator=(HlslParseContext&); + + static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2 * 2)); // see computeSamplerTypeIndex() + TQualifier globalBufferDefaults; + TQualifier globalUniformDefaults; + TQualifier globalInputDefaults; + TQualifier globalOutputDefaults; + TString currentCaller; // name of last function body entered (not valid when at global scope) + TIdSetType inductiveLoopIds; + TVector needsIndexLimitationChecking; + + // + // Geometry shader input arrays: + // - array sizing is based on input primitive and/or explicit size + // + // Tessellation control output arrays: + // - array sizing is based on output layout(vertices=...) and/or explicit size + // + // Both: + // - array sizing is retroactive + // - built-in block redeclarations interact with this + // + // Design: + // - use a per-context "resize-list", a list of symbols whose array sizes + // can be fixed + // + // - the resize-list starts empty at beginning of user-shader compilation, it does + // not have built-ins in it + // + // - on built-in array use: copyUp() symbol and add it to the resize-list + // + // - on user array declaration: add it to the resize-list + // + // - on block redeclaration: copyUp() symbol and add it to the resize-list + // * note, that appropriately gives an error if redeclaring a block that + // was already used and hence already copied-up + // + // - on seeing a layout declaration that sizes the array, fix everything in the + // resize-list, giving errors for mismatch + // + // - on seeing an array size declaration, give errors on mismatch between it and previous + // array-sizing declarations + // + TVector ioArraySymbolResizeList; + + TMap flattenMap; + + // IO-type map. Maps a pure symbol-table form of a structure-member list into + // each of the (up to) three kinds of IO, as each as different allowed decorations, + // but HLSL allows mixing all in the same structure. + struct tIoKinds { + TTypeList* input; + TTypeList* output; + TTypeList* uniform; + }; + TMap ioTypeMap; + + // Structure splitting data: + TMap splitNonIoVars; // variables with the built-in interstage IO removed, indexed by unique ID. + + // Structuredbuffer shared types. Typically there are only a few. + TVector structBufferTypes; + + // This tracks texture sample user structure return types. Only a limited number are supported, as + // may fit in TSampler::structReturnIndex. + TVector textureReturnStruct; + + TMap structBufferCounter; // true if counter buffer is in use + + // The built-in interstage IO map considers e.g, EvqPosition on input and output separately, so that we + // can build the linkage correctly if position appears on both sides. Otherwise, multiple positions + // are considered identical. + struct tInterstageIoData { + tInterstageIoData(TBuiltInVariable bi, TStorageQualifier q) : + builtIn(bi), storage(q) { } + + TBuiltInVariable builtIn; + TStorageQualifier storage; + + // ordering for maps + bool operator<(const tInterstageIoData d) const { + return (builtIn != d.builtIn) ? (builtIn < d.builtIn) : (storage < d.storage); + } + }; + + TMap splitBuiltIns; // split built-ins, indexed by built-in type. + TVariable* inputPatch; // input patch is special for PCF: it's the only non-builtin PCF input, + // and is handled as a pseudo-builtin. + + unsigned int nextInLocation; + unsigned int nextOutLocation; + + TFunction* entryPointFunction; + TIntermNode* entryPointFunctionBody; + + TString patchConstantFunctionName; // hull shader patch constant function name, from function level attribute. + TMap builtInTessLinkageSymbols; // used for tessellation, finding declared built-ins + + TVector currentTypePrefix; // current scoping prefix for nested structures + TVector implicitThisStack; // currently active 'this' variables for nested structures + + TVariable* gsStreamOutput; // geometry shader stream outputs, for emit (Append method) + + TVariable* clipDistanceOutput; // synthesized clip distance out variable (shader might have >1) + TVariable* cullDistanceOutput; // synthesized cull distance out variable (shader might have >1) + TVariable* clipDistanceInput; // synthesized clip distance in variable (shader might have >1) + TVariable* cullDistanceInput; // synthesized cull distance in variable (shader might have >1) + + static const int maxClipCullRegs = 2; + std::array clipSemanticNSizeIn; // vector, indexed by clip semantic ID + std::array cullSemanticNSizeIn; // vector, indexed by cull semantic ID + std::array clipSemanticNSizeOut; // vector, indexed by clip semantic ID + std::array cullSemanticNSizeOut; // vector, indexed by cull semantic ID + + // This tracks the first (mip level) argument to the .mips[][] operator. Since this can be nested as + // in tx.mips[tx.mips[0][1].x][2], we need a stack. We also track the TSourceLoc for error reporting + // purposes. + struct tMipsOperatorData { + tMipsOperatorData(TSourceLoc l, TIntermTyped* m) : loc(l), mipLevel(m) { } + TSourceLoc loc; + TIntermTyped* mipLevel; + }; + + TVector mipsOperatorMipArg; + + // The geometry output stream is not copied out from the entry point as a typical output variable + // is. It's written via EmitVertex (hlsl=Append), which may happen in arbitrary control flow. + // For this we need the real output symbol. Since it may not be known at the time and Append() + // method is parsed, the sequence will be patched during finalization. + struct tGsAppendData { + TIntermAggregate* node; + TSourceLoc loc; + }; + + TVector gsAppends; + + // A texture object may be used with shadow and non-shadow samplers, but both may not be + // alive post-DCE in the same shader. We do not know at compilation time which are alive: that's + // only known post-DCE. If a texture is used both ways, we create two textures, and + // leave the elimiation of one to the optimizer. This maps the shader variant to + // the shadow variant. + // + // This can be removed if and when the texture shadow code in + // HlslParseContext::handleSamplerTextureCombine is removed. + struct tShadowTextureSymbols { + tShadowTextureSymbols() { symId.fill(-1); } + + void set(bool shadow, int id) { symId[int(shadow)] = id; } + int get(bool shadow) const { return symId[int(shadow)]; } + + // True if this texture has been seen with both shadow and non-shadow modes + bool overloaded() const { return symId[0] != -1 && symId[1] != -1; } + bool isShadowId(int id) const { return symId[1] == id; } + + private: + std::array symId; + }; + + TMap textureShadowVariant; + bool parsingEntrypointParameters; +}; + +// This is the prefix we use for built-in methods to avoid namespace collisions with +// global scope user functions. +// TODO: this would be better as a nonparseable character, but that would +// require changing the scanner. +#define BUILTIN_PREFIX "__BI_" + +} // end namespace glslang + +#endif // HLSL_PARSE_INCLUDED_ diff --git a/third_party/glslang/glslang/HLSL/hlslParseables.cpp b/third_party/glslang/glslang/HLSL/hlslParseables.cpp new file mode 100644 index 0000000..025cb5e --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslParseables.cpp @@ -0,0 +1,1259 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Create strings that declare built-in definitions, add built-ins programmatically +// that cannot be expressed in the strings, and establish mappings between +// built-in functions and operators. +// +// Where to put a built-in: +// TBuiltInParseablesHlsl::initialize(version,profile) context-independent textual built-ins; add them to the right string +// TBuiltInParseablesHlsl::initialize(resources,...) context-dependent textual built-ins; add them to the right string +// TBuiltInParseablesHlsl::identifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table, +// including identifying what extensions are needed if a version does not allow a symbol +// TBuiltInParseablesHlsl::identifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the +// symbol table, including identifying what extensions are needed if a version does +// not allow a symbol +// + +#include "hlslParseables.h" +#include "hlslParseHelper.h" +#include +#include +#include + +namespace { // anonymous namespace functions + +// arg order queries +bool IsSamplerType(const char argType) { return argType == 'S' || argType == 's'; } +bool IsArrayed(const char argOrder) { return argOrder == '@' || argOrder == '&' || argOrder == '#'; } +bool IsTextureNonMS(const char argOrder) { return argOrder == '%'; } +bool IsSubpassInput(const char argOrder) { return argOrder == '[' || argOrder == ']'; } +bool IsArrayedTexture(const char argOrder) { return argOrder == '@'; } +bool IsTextureMS(const char argOrder) { return argOrder == '$' || argOrder == '&'; } +bool IsMS(const char argOrder) { return IsTextureMS(argOrder) || argOrder == ']'; } +bool IsBuffer(const char argOrder) { return argOrder == '*' || argOrder == '~'; } +bool IsImage(const char argOrder) { return argOrder == '!' || argOrder == '#' || argOrder == '~'; } + +bool IsTextureType(const char argOrder) +{ + return IsTextureNonMS(argOrder) || IsArrayedTexture(argOrder) || + IsTextureMS(argOrder) || IsBuffer(argOrder) || IsImage(argOrder); +} + +// Reject certain combinations that are illegal sample methods. For example, +// 3D arrays. +bool IsIllegalSample(const glslang::TString& name, const char* argOrder, int dim0) +{ + const bool isArrayed = IsArrayed(*argOrder); + const bool isMS = IsTextureMS(*argOrder); + const bool isBuffer = IsBuffer(*argOrder); + + // there are no 3D arrayed textures, or 3D SampleCmp(LevelZero) + if (dim0 == 3 && (isArrayed || name == "SampleCmp" || name == "SampleCmpLevelZero")) + return true; + + const int numArgs = int(std::count(argOrder, argOrder + strlen(argOrder), ',')) + 1; + + // Reject invalid offset forms with cubemaps + if (dim0 == 4) { + if ((name == "Sample" && numArgs >= 4) || + (name == "SampleBias" && numArgs >= 5) || + (name == "SampleCmp" && numArgs >= 5) || + (name == "SampleCmpLevelZero" && numArgs >= 5) || + (name == "SampleGrad" && numArgs >= 6) || + (name == "SampleLevel" && numArgs >= 5)) + return true; + } + + const bool isGather = + (name == "Gather" || + name == "GatherRed" || + name == "GatherGreen" || + name == "GatherBlue" || + name == "GatherAlpha"); + + const bool isGatherCmp = + (name == "GatherCmp" || + name == "GatherCmpRed" || + name == "GatherCmpGreen" || + name == "GatherCmpBlue" || + name == "GatherCmpAlpha"); + + // Reject invalid Gathers + if (isGather || isGatherCmp) { + if (dim0 == 1 || dim0 == 3) // there are no 1D or 3D gathers + return true; + + // no offset on cube or cube array gathers + if (dim0 == 4) { + if ((isGather && numArgs > 3) || (isGatherCmp && numArgs > 4)) + return true; + } + } + + // Reject invalid Loads + if (name == "Load" && dim0 == 4) + return true; // Load does not support any cubemaps, arrayed or not. + + // Multisample formats are only 2D and 2Darray + if (isMS && dim0 != 2) + return true; + + // Buffer are only 1D + if (isBuffer && dim0 != 1) + return true; + + return false; +} + +// Return the number of the coordinate arg, if any +int CoordinateArgPos(const glslang::TString& name, bool isTexture) +{ + if (!isTexture || (name == "GetDimensions")) + return -1; // has none + else if (name == "Load") + return 1; + else + return 2; // other texture methods are 2 +} + +// Some texture methods use an addition coordinate dimension for the mip +bool HasMipInCoord(const glslang::TString& name, bool isMS, bool isBuffer, bool isImage) +{ + return name == "Load" && !isMS && !isBuffer && !isImage; +} + +// LOD calculations don't pass the array level in the coordinate. +bool NoArrayCoord(const glslang::TString& name) +{ + return name == "CalculateLevelOfDetail" || name == "CalculateLevelOfDetailUnclamped"; +} + +// Handle IO params marked with > or < +const char* IoParam(glslang::TString& s, const char* nthArgOrder) +{ + if (*nthArgOrder == '>') { // output params + ++nthArgOrder; + s.append("out "); + } else if (*nthArgOrder == '<') { // input params + ++nthArgOrder; + s.append("in "); + } + + return nthArgOrder; +} + +// Handle repeated args +void HandleRepeatArg(const char*& arg, const char*& prev, const char* current) +{ + if (*arg == ',' || *arg == '\0') + arg = prev; + else + prev = current; +} + +// Return true for the end of a single argument key, which can be the end of the string, or +// the comma separator. +inline bool IsEndOfArg(const char* arg) +{ + return arg == nullptr || *arg == '\0' || *arg == ','; +} + +// If this is a fixed vector size, such as V3, return the size. Else return 0. +int FixedVecSize(const char* arg) +{ + while (!IsEndOfArg(arg)) { + if (isdigit(*arg)) + return *arg - '0'; + ++arg; + } + + return 0; // none found. +} + +// Create and return a type name, using HLSL type conventions. +// +// order: S = scalar, V = vector, M = matrix +// argType: F = float, D = double, I = int, U = uint, B = bool, S = sampler +// dim0 = vector dimension, or matrix 1st dimension +// dim1 = matrix 2nd dimension +glslang::TString& AppendTypeName(glslang::TString& s, const char* argOrder, const char* argType, int dim0, int dim1) +{ + const bool isTranspose = (argOrder[0] == '^'); + const bool isTexture = IsTextureType(argOrder[0]); + const bool isArrayed = IsArrayed(argOrder[0]); + const bool isSampler = IsSamplerType(argType[0]); + const bool isMS = IsMS(argOrder[0]); + const bool isBuffer = IsBuffer(argOrder[0]); + const bool isImage = IsImage(argOrder[0]); + const bool isSubpass = IsSubpassInput(argOrder[0]); + + char type = *argType; + + if (isTranspose) { // Take transpose of matrix dimensions + std::swap(dim0, dim1); + } else if (isTexture || isSubpass) { + if (type == 'F') // map base type to texture of that type. + type = 'T'; // e.g, int -> itexture, uint -> utexture, etc. + else if (type == 'I') + type = 'i'; + else if (type == 'U') + type = 'u'; + } + + if (isTranspose) + ++argOrder; + + char order = *argOrder; + + switch (type) { + case '-': s += "void"; break; + case 'F': s += "float"; break; + case 'D': s += "double"; break; + case 'I': s += "int"; break; + case 'U': s += "uint"; break; + case 'L': s += "int64_t"; break; + case 'M': s += "uint64_t"; break; + case 'B': s += "bool"; break; + case 'S': s += "sampler"; break; + case 's': s += "SamplerComparisonState"; break; + case 'T': s += ((isBuffer && isImage) ? "RWBuffer" : + isSubpass ? "SubpassInput" : + isBuffer ? "Buffer" : + isImage ? "RWTexture" : "Texture"); break; + case 'i': s += ((isBuffer && isImage) ? "RWBuffer" : + isSubpass ? "SubpassInput" : + isBuffer ? "Buffer" : + isImage ? "RWTexture" : "Texture"); break; + case 'u': s += ((isBuffer && isImage) ? "RWBuffer" : + isSubpass ? "SubpassInput" : + isBuffer ? "Buffer" : + isImage ? "RWTexture" : "Texture"); break; + default: s += "UNKNOWN_TYPE"; break; + } + + if (isSubpass && isMS) + s += "MS"; + + // handle fixed vector sizes, such as float3, and only ever 3. + const int fixedVecSize = FixedVecSize(argOrder); + if (fixedVecSize != 0) + dim0 = dim1 = fixedVecSize; + + const char dim0Char = ('0' + char(dim0)); + const char dim1Char = ('0' + char(dim1)); + + // Add sampler dimensions + if (isSampler || isTexture) { + if ((order == 'V' || isTexture) && !isBuffer) { + switch (dim0) { + case 1: s += "1D"; break; + case 2: s += (isMS ? "2DMS" : "2D"); break; + case 3: s += "3D"; break; + case 4: s += (type == 'S'? "CUBE" : "Cube"); break; + default: s += "UNKNOWN_SAMPLER"; break; + } + } + } else { + // Non-sampler type: + // verify dimensions + if (((order == 'V' || order == 'M') && (dim0 < 1 || dim0 > 4)) || + (order == 'M' && (dim1 < 1 || dim1 > 4))) { + s += "UNKNOWN_DIMENSION"; + return s; + } + + switch (order) { + case '-': break; // no dimensions for voids + case 'S': break; // no dimensions on scalars + case 'V': + s += dim0Char; + break; + case 'M': + s += dim0Char; + s += 'x'; + s += dim1Char; + break; + default: + break; + } + } + + // handle arrayed textures + if (isArrayed) + s += "Array"; + + switch (type) { + case 'i': s += " 0) // handle fixed sized vectors + dim0Min = dim0Max = fixedVecSize; +} + +} // end anonymous namespace + +namespace glslang { + +TBuiltInParseablesHlsl::TBuiltInParseablesHlsl() +{ +} + +// +// Handle creation of mat*mat specially, since it doesn't fall conveniently out of +// the generic prototype creation code below. +// +void TBuiltInParseablesHlsl::createMatTimesMat() +{ + TString& s = commonBuiltins; + + for (int xRows = 1; xRows <=4; xRows++) { + for (int xCols = 1; xCols <=4; xCols++) { + const int yRows = xCols; + for (int yCols = 1; yCols <=4; yCols++) { + const int retRows = xRows; + const int retCols = yCols; + + // Create a mat * mat of the appropriate dimensions + AppendTypeName(s, "M", "F", retRows, retCols); // add return type + s.append(" "); // space between type and name + s.append("mul"); // intrinsic name + s.append("("); // open paren + + AppendTypeName(s, "M", "F", xRows, xCols); // add X input + s.append(", "); + AppendTypeName(s, "M", "F", yRows, yCols); // add Y input + + s.append(");\n"); // close paren + } + + // Create M*V + AppendTypeName(s, "V", "F", xRows, 1); // add return type + s.append(" "); // space between type and name + s.append("mul"); // intrinsic name + s.append("("); // open paren + + AppendTypeName(s, "M", "F", xRows, xCols); // add X input + s.append(", "); + AppendTypeName(s, "V", "F", xCols, 1); // add Y input + + s.append(");\n"); // close paren + + // Create V*M + AppendTypeName(s, "V", "F", xCols, 1); // add return type + s.append(" "); // space between type and name + s.append("mul"); // intrinsic name + s.append("("); // open paren + + AppendTypeName(s, "V", "F", xRows, 1); // add Y input + s.append(", "); + AppendTypeName(s, "M", "F", xRows, xCols); // add X input + + s.append(");\n"); // close paren + } + } +} + +// +// Add all context-independent built-in functions and variables that are present +// for the given version and profile. Share common ones across stages, otherwise +// make stage-specific entries. +// +// Most built-ins variables can be added as simple text strings. Some need to +// be added programmatically, which is done later in IdentifyBuiltIns() below. +// +void TBuiltInParseablesHlsl::initialize(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/) +{ + static const EShLanguageMask EShLangAll = EShLanguageMask(EShLangCount - 1); + + // These are the actual stage masks defined in the documentation, in case they are + // needed for future validation. For now, they are commented out, and set below + // to EShLangAll, to allow any intrinsic to be used in any shader, which is legal + // if it is not called. + // + // static const EShLanguageMask EShLangPSCS = EShLanguageMask(EShLangFragmentMask | EShLangComputeMask); + // static const EShLanguageMask EShLangVSPSGS = EShLanguageMask(EShLangVertexMask | EShLangFragmentMask | EShLangGeometryMask); + // static const EShLanguageMask EShLangCS = EShLangComputeMask; + // static const EShLanguageMask EShLangPS = EShLangFragmentMask; + // static const EShLanguageMask EShLangHS = EShLangTessControlMask; + + // This set uses EShLangAll for everything. + static const EShLanguageMask EShLangPSCS = EShLangAll; + static const EShLanguageMask EShLangVSPSGS = EShLangAll; + static const EShLanguageMask EShLangCS = EShLangAll; + static const EShLanguageMask EShLangPS = EShLangAll; + static const EShLanguageMask EShLangHS = EShLangAll; + static const EShLanguageMask EShLangGS = EShLangAll; + + // This structure encodes the prototype information for each HLSL intrinsic. + // Because explicit enumeration would be cumbersome, it's procedurally generated. + // orderKey can be: + // S = scalar, V = vector, M = matrix, - = void + // typekey can be: + // D = double, F = float, U = uint, I = int, B = bool, S = sampler, s = shadowSampler, M = uint64_t, L = int64_t + // An empty order or type key repeats the first one. E.g: SVM,, means 3 args each of SVM. + // '>' as first letter of order creates an output parameter + // '<' as first letter of order creates an input parameter + // '^' as first letter of order takes transpose dimensions + // '%' as first letter of order creates texture of given F/I/U type (texture, itexture, etc) + // '@' as first letter of order creates arrayed texture of given type + // '$' / '&' as first letter of order creates 2DMS / 2DMSArray textures + // '*' as first letter of order creates buffer object + // '!' as first letter of order creates image object + // '#' as first letter of order creates arrayed image object + // '~' as first letter of order creates an image buffer object + // '[' / ']' as first letter of order creates a SubpassInput/SubpassInputMS object + + static const struct { + const char* name; // intrinsic name + const char* retOrder; // return type key: empty matches order of 1st argument + const char* retType; // return type key: empty matches type of 1st argument + const char* argOrder; // argument order key + const char* argType; // argument type key + unsigned int stage; // stage mask + bool method; // true if it's a method. + } hlslIntrinsics[] = { + // name retOrd retType argOrder argType stage mask method + // ---------------------------------------------------------------------------------------------------------------- + { "abort", nullptr, nullptr, "-", "-", EShLangAll, false }, + { "abs", nullptr, nullptr, "SVM", "DFUI", EShLangAll, false }, + { "acos", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "all", "S", "B", "SVM", "BFIU", EShLangAll, false }, + { "AllMemoryBarrier", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "AllMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "any", "S", "B", "SVM", "BFIU", EShLangAll, false }, + { "asdouble", "S", "D", "S,", "UI,", EShLangAll, false }, + { "asdouble", "V2", "D", "V2,", "UI,", EShLangAll, false }, + { "asfloat", nullptr, "F", "SVM", "BFIU", EShLangAll, false }, + { "asin", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "asint", nullptr, "I", "SVM", "FIU", EShLangAll, false }, + { "asuint", nullptr, "U", "SVM", "FIU", EShLangAll, false }, + { "atan", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "atan2", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "ceil", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "CheckAccessFullyMapped", "S", "B" , "S", "U", EShLangPSCS, false }, + { "clamp", nullptr, nullptr, "SVM,,", "FUI,,", EShLangAll, false }, + { "clip", "-", "-", "SVM", "FUI", EShLangPS, false }, + { "cos", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "cosh", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "countbits", nullptr, nullptr, "SV", "UI", EShLangAll, false }, + { "cross", nullptr, nullptr, "V3,", "F,", EShLangAll, false }, + { "D3DCOLORtoUBYTE4", "V4", "I", "V4", "F", EShLangAll, false }, + { "ddx", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddx_coarse", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddx_fine", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddy", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddy_coarse", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "ddy_fine", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "degrees", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "determinant", "S", "F", "M", "F", EShLangAll, false }, + { "DeviceMemoryBarrier", nullptr, nullptr, "-", "-", EShLangPSCS, false }, + { "DeviceMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "distance", "S", "F", "SV,", "F,", EShLangAll, false }, + { "dot", "S", nullptr, "SV,", "FI,", EShLangAll, false }, + { "dst", nullptr, nullptr, "V4,", "F,", EShLangAll, false }, + // { "errorf", "-", "-", "", "", EShLangAll, false }, TODO: varargs + { "EvaluateAttributeAtCentroid", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "EvaluateAttributeAtSample", nullptr, nullptr, "SVM,S", "F,U", EShLangPS, false }, + { "EvaluateAttributeSnapped", nullptr, nullptr, "SVM,V2", "F,I", EShLangPS, false }, + { "exp", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "exp2", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "f16tof32", nullptr, "F", "SV", "U", EShLangAll, false }, + { "f32tof16", nullptr, "U", "SV", "F", EShLangAll, false }, + { "faceforward", nullptr, nullptr, "V,,", "F,,", EShLangAll, false }, + { "firstbithigh", nullptr, nullptr, "SV", "UI", EShLangAll, false }, + { "firstbitlow", nullptr, nullptr, "SV", "UI", EShLangAll, false }, + { "floor", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "fma", nullptr, nullptr, "SVM,,", "D,,", EShLangAll, false }, + { "fmod", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "frac", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "frexp", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "fwidth", nullptr, nullptr, "SVM", "F", EShLangPS, false }, + { "GetRenderTargetSampleCount", "S", "U", "-", "-", EShLangAll, false }, + { "GetRenderTargetSamplePosition", "V2", "F", "V1", "I", EShLangAll, false }, + { "GroupMemoryBarrier", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "GroupMemoryBarrierWithGroupSync", nullptr, nullptr, "-", "-", EShLangCS, false }, + { "InterlockedAdd", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedAdd", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedAnd", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedAnd", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedCompareExchange", "-", "-", "SVM,,,>", "UI,,,", EShLangPSCS, false }, + { "InterlockedCompareStore", "-", "-", "SVM,,", "UI,,", EShLangPSCS, false }, + { "InterlockedExchange", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedMax", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedMax", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedMin", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedMin", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedOr", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedOr", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "InterlockedXor", "-", "-", "SVM,,>", "UI,,", EShLangPSCS, false }, + { "InterlockedXor", "-", "-", "SVM,", "UI,", EShLangPSCS, false }, + { "isfinite", nullptr, "B" , "SVM", "F", EShLangAll, false }, + { "isinf", nullptr, "B" , "SVM", "F", EShLangAll, false }, + { "isnan", nullptr, "B" , "SVM", "F", EShLangAll, false }, + { "ldexp", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "length", "S", "F", "SV", "F", EShLangAll, false }, + { "lerp", nullptr, nullptr, "VM,,", "F,,", EShLangAll, false }, + { "lerp", nullptr, nullptr, "SVM,,S", "F,,", EShLangAll, false }, + { "lit", "V4", "F", "S,,", "F,,", EShLangAll, false }, + { "log", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "log10", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "log2", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "mad", nullptr, nullptr, "SVM,,", "DFUI,,", EShLangAll, false }, + { "max", nullptr, nullptr, "SVM,", "FIU,", EShLangAll, false }, + { "min", nullptr, nullptr, "SVM,", "FIU,", EShLangAll, false }, + { "modf", nullptr, nullptr, "SVM,>", "FIU,", EShLangAll, false }, + { "msad4", "V4", "U", "S,V2,V4", "U,,", EShLangAll, false }, + { "mul", "S", nullptr, "S,S", "FI,", EShLangAll, false }, + { "mul", "V", nullptr, "S,V", "FI,", EShLangAll, false }, + { "mul", "M", nullptr, "S,M", "FI,", EShLangAll, false }, + { "mul", "V", nullptr, "V,S", "FI,", EShLangAll, false }, + { "mul", "S", nullptr, "V,V", "FI,", EShLangAll, false }, + { "mul", "M", nullptr, "M,S", "FI,", EShLangAll, false }, + // mat*mat form of mul is handled in createMatTimesMat() + { "noise", "S", "F", "V", "F", EShLangPS, false }, + { "normalize", nullptr, nullptr, "V", "F", EShLangAll, false }, + { "pow", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "printf", nullptr, nullptr, "-", "-", EShLangAll, false }, + { "Process2DQuadTessFactorsAvg", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "Process2DQuadTessFactorsMax", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "Process2DQuadTessFactorsMin", "-", "-", "V4,V2,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "ProcessIsolineTessFactors", "-", "-", "S,,>,>", "F,,,", EShLangHS, false }, + { "ProcessQuadTessFactorsAvg", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "ProcessQuadTessFactorsMax", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "ProcessQuadTessFactorsMin", "-", "-", "V4,S,>V4,>V2,", "F,,,,", EShLangHS, false }, + { "ProcessTriTessFactorsAvg", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false }, + { "ProcessTriTessFactorsMax", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false }, + { "ProcessTriTessFactorsMin", "-", "-", "V3,S,>V3,>S,", "F,,,,", EShLangHS, false }, + { "radians", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "rcp", nullptr, nullptr, "SVM", "FD", EShLangAll, false }, + { "reflect", nullptr, nullptr, "V,", "F,", EShLangAll, false }, + { "refract", nullptr, nullptr, "V,V,S", "F,,", EShLangAll, false }, + { "reversebits", nullptr, nullptr, "SV", "UI", EShLangAll, false }, + { "round", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "rsqrt", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "saturate", nullptr, nullptr , "SVM", "F", EShLangAll, false }, + { "sign", nullptr, nullptr, "SVM", "FI", EShLangAll, false }, + { "sin", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "sincos", "-", "-", "SVM,>,>", "F,,", EShLangAll, false }, + { "sinh", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "smoothstep", nullptr, nullptr, "SVM,,", "F,,", EShLangAll, false }, + { "sqrt", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "step", nullptr, nullptr, "SVM,", "F,", EShLangAll, false }, + { "tan", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "tanh", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + { "tex1D", "V4", "F", "S,S", "S,F", EShLangPS, false }, + { "tex1D", "V4", "F", "S,S,V1,", "S,F,,", EShLangPS, false }, + { "tex1Dbias", "V4", "F", "S,V4", "S,F", EShLangPS, false }, + { "tex1Dgrad", "V4", "F", "S,,,", "S,F,,", EShLangPS, false }, + { "tex1Dlod", "V4", "F", "S,V4", "S,F", EShLangPS, false }, + { "tex1Dproj", "V4", "F", "S,V4", "S,F", EShLangPS, false }, + { "tex2D", "V4", "F", "V2,", "S,F", EShLangPS, false }, + { "tex2D", "V4", "F", "V2,,,", "S,F,,", EShLangPS, false }, + { "tex2Dbias", "V4", "F", "V2,V4", "S,F", EShLangPS, false }, + { "tex2Dgrad", "V4", "F", "V2,,,", "S,F,,", EShLangPS, false }, + { "tex2Dlod", "V4", "F", "V2,V4", "S,F", EShLangAll, false }, + { "tex2Dproj", "V4", "F", "V2,V4", "S,F", EShLangPS, false }, + { "tex3D", "V4", "F", "V3,", "S,F", EShLangPS, false }, + { "tex3D", "V4", "F", "V3,,,", "S,F,,", EShLangPS, false }, + { "tex3Dbias", "V4", "F", "V3,V4", "S,F", EShLangPS, false }, + { "tex3Dgrad", "V4", "F", "V3,,,", "S,F,,", EShLangPS, false }, + { "tex3Dlod", "V4", "F", "V3,V4", "S,F", EShLangPS, false }, + { "tex3Dproj", "V4", "F", "V3,V4", "S,F", EShLangPS, false }, + { "texCUBE", "V4", "F", "V4,V3", "S,F", EShLangPS, false }, + { "texCUBE", "V4", "F", "V4,V3,,", "S,F,,", EShLangPS, false }, + { "texCUBEbias", "V4", "F", "V4,", "S,F", EShLangPS, false }, + { "texCUBEgrad", "V4", "F", "V4,V3,,", "S,F,,", EShLangPS, false }, + { "texCUBElod", "V4", "F", "V4,", "S,F", EShLangPS, false }, + { "texCUBEproj", "V4", "F", "V4,", "S,F", EShLangPS, false }, + { "transpose", "^M", nullptr, "M", "FUIB", EShLangAll, false }, + { "trunc", nullptr, nullptr, "SVM", "F", EShLangAll, false }, + + // Texture object methods. Return type can be overridden by shader declaration. + // !O = no offset, O = offset + { "Sample", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangPS, true }, + { "Sample", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangPS, true }, + + { "SampleBias", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,S,F,", EShLangPS, true }, + { "SampleBias", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,S,F,,I", EShLangPS, true }, + + // TODO: FXC accepts int/uint samplers here. unclear what that means. + { "SampleCmp", /*!O*/ "S", "F", "%@,S,V,S", "FIU,s,F,", EShLangPS, true }, + { "SampleCmp", /* O*/ "S", "F", "%@,S,V,S,V", "FIU,s,F,,I", EShLangPS, true }, + + // TODO: FXC accepts int/uint samplers here. unclear what that means. + { "SampleCmpLevelZero", /*!O*/ "S", "F", "%@,S,V,S", "FIU,s,F,F", EShLangPS, true }, + { "SampleCmpLevelZero", /* O*/ "S", "F", "%@,S,V,S,V", "FIU,s,F,F,I", EShLangPS, true }, + + { "SampleGrad", /*!O*/ "V4", nullptr, "%@,S,V,,", "FIU,S,F,,", EShLangAll, true }, + { "SampleGrad", /* O*/ "V4", nullptr, "%@,S,V,,,", "FIU,S,F,,,I", EShLangAll, true }, + + { "SampleLevel", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,S,F,", EShLangAll, true }, + { "SampleLevel", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,S,F,,I", EShLangAll, true }, + + { "Load", /*!O*/ "V4", nullptr, "%@,V", "FIU,I", EShLangAll, true }, + { "Load", /* O*/ "V4", nullptr, "%@,V,V", "FIU,I,I", EShLangAll, true }, + { "Load", /* +sampleidex*/ "V4", nullptr, "$&,V,S", "FIU,I,I", EShLangAll, true }, + { "Load", /* +samplindex, offset*/ "V4", nullptr, "$&,V,S,V", "FIU,I,I,I", EShLangAll, true }, + + // RWTexture loads + { "Load", "V4", nullptr, "!#,V", "FIU,I", EShLangAll, true }, + // (RW)Buffer loads + { "Load", "V4", nullptr, "~*1,V", "FIU,I", EShLangAll, true }, + + { "Gather", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "Gather", /* O*/ "V4", nullptr, "%@,S,V,V", "FIU,S,F,I", EShLangAll, true }, + + { "CalculateLevelOfDetail", "S", "F", "%@,S,V", "FUI,S,F", EShLangPS, true }, + { "CalculateLevelOfDetailUnclamped", "S", "F", "%@,S,V", "FUI,S,F", EShLangPS, true }, + + { "GetSamplePosition", "V2", "F", "$&2,S", "FUI,I", EShLangVSPSGS,true }, + + // + // UINT Width + // UINT MipLevel, UINT Width, UINT NumberOfLevels + { "GetDimensions", /* 1D */ "-", "-", "%!~1,>S", "FUI,U", EShLangAll, true }, + { "GetDimensions", /* 1D */ "-", "-", "%!~1,>S", "FUI,F", EShLangAll, true }, + { "GetDimensions", /* 1D */ "-", "-", "%1,S,>S,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 1D */ "-", "-", "%1,S,>S,", "FUI,U,F,", EShLangAll, true }, + + // UINT Width, UINT Elements + // UINT MipLevel, UINT Width, UINT Elements, UINT NumberOfLevels + { "GetDimensions", /* 1DArray */ "-", "-", "@#1,>S,", "FUI,U,", EShLangAll, true }, + { "GetDimensions", /* 1DArray */ "-", "-", "@#1,>S,", "FUI,F,", EShLangAll, true }, + { "GetDimensions", /* 1DArray */ "-", "-", "@1,S,>S,,", "FUI,U,,,", EShLangAll, true }, + { "GetDimensions", /* 1DArray */ "-", "-", "@1,S,>S,,", "FUI,U,F,,", EShLangAll, true }, + + // UINT Width, UINT Height + // UINT MipLevel, UINT Width, UINT Height, UINT NumberOfLevels + { "GetDimensions", /* 2D */ "-", "-", "%!2,>S,", "FUI,U,", EShLangAll, true }, + { "GetDimensions", /* 2D */ "-", "-", "%!2,>S,", "FUI,F,", EShLangAll, true }, + { "GetDimensions", /* 2D */ "-", "-", "%2,S,>S,,", "FUI,U,,,", EShLangAll, true }, + { "GetDimensions", /* 2D */ "-", "-", "%2,S,>S,,", "FUI,U,F,,", EShLangAll, true }, + + // UINT Width, UINT Height, UINT Elements + // UINT MipLevel, UINT Width, UINT Height, UINT Elements, UINT NumberOfLevels + { "GetDimensions", /* 2DArray */ "-", "-", "@#2,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 2DArray */ "-", "-", "@#2,>S,,", "FUI,F,F,F", EShLangAll, true }, + { "GetDimensions", /* 2DArray */ "-", "-", "@2,S,>S,,,", "FUI,U,,,,", EShLangAll, true }, + { "GetDimensions", /* 2DArray */ "-", "-", "@2,S,>S,,,", "FUI,U,F,,,", EShLangAll, true }, + + // UINT Width, UINT Height, UINT Depth + // UINT MipLevel, UINT Width, UINT Height, UINT Depth, UINT NumberOfLevels + { "GetDimensions", /* 3D */ "-", "-", "%!3,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 3D */ "-", "-", "%!3,>S,,", "FUI,F,,", EShLangAll, true }, + { "GetDimensions", /* 3D */ "-", "-", "%3,S,>S,,,", "FUI,U,,,,", EShLangAll, true }, + { "GetDimensions", /* 3D */ "-", "-", "%3,S,>S,,,", "FUI,U,F,,,", EShLangAll, true }, + + // UINT Width, UINT Height + // UINT MipLevel, UINT Width, UINT Height, UINT NumberOfLevels + { "GetDimensions", /* Cube */ "-", "-", "%4,>S,", "FUI,U,", EShLangAll, true }, + { "GetDimensions", /* Cube */ "-", "-", "%4,>S,", "FUI,F,", EShLangAll, true }, + { "GetDimensions", /* Cube */ "-", "-", "%4,S,>S,,", "FUI,U,,,", EShLangAll, true }, + { "GetDimensions", /* Cube */ "-", "-", "%4,S,>S,,", "FUI,U,F,,", EShLangAll, true }, + + // UINT Width, UINT Height, UINT Elements + // UINT MipLevel, UINT Width, UINT Height, UINT Elements, UINT NumberOfLevels + { "GetDimensions", /* CubeArray */ "-", "-", "@4,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* CubeArray */ "-", "-", "@4,>S,,", "FUI,F,,", EShLangAll, true }, + { "GetDimensions", /* CubeArray */ "-", "-", "@4,S,>S,,,", "FUI,U,,,,", EShLangAll, true }, + { "GetDimensions", /* CubeArray */ "-", "-", "@4,S,>S,,,", "FUI,U,F,,,", EShLangAll, true }, + + // UINT Width, UINT Height, UINT Samples + // UINT Width, UINT Height, UINT Elements, UINT Samples + { "GetDimensions", /* 2DMS */ "-", "-", "$2,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 2DMS */ "-", "-", "$2,>S,,", "FUI,U,,", EShLangAll, true }, + { "GetDimensions", /* 2DMSArray */ "-", "-", "&2,>S,,,", "FUI,U,,,", EShLangAll, true }, + { "GetDimensions", /* 2DMSArray */ "-", "-", "&2,>S,,,", "FUI,U,,,", EShLangAll, true }, + + // SM5 texture methods + { "GatherRed", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "GatherRed", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true }, + { "GatherRed", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true }, + { "GatherRed", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true }, + { "GatherRed", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true }, + + { "GatherGreen", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "GatherGreen", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true }, + { "GatherGreen", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true }, + { "GatherGreen", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true }, + { "GatherGreen", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true }, + + { "GatherBlue", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "GatherBlue", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true }, + { "GatherBlue", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true }, + { "GatherBlue", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true }, + { "GatherBlue", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true }, + + { "GatherAlpha", /*!O*/ "V4", nullptr, "%@,S,V", "FIU,S,F", EShLangAll, true }, + { "GatherAlpha", /* O*/ "V4", nullptr, "%@,S,V,", "FIU,S,F,I", EShLangAll, true }, + { "GatherAlpha", /* O, status*/ "V4", nullptr, "%@,S,V,,>S", "FIU,S,F,I,U", EShLangAll, true }, + { "GatherAlpha", /* O-4 */ "V4", nullptr, "%@,S,V,,,,", "FIU,S,F,I,,,", EShLangAll, true }, + { "GatherAlpha", /* O-4, status */"V4", nullptr, "%@,S,V,,,,,S", "FIU,S,F,I,,,,U", EShLangAll, true }, + + { "GatherCmp", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmp", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmp", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmp", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmp", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,V,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + { "GatherCmpRed", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmpRed", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmpRed", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmpRed", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmpRed", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,V,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + { "GatherCmpGreen", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmpGreen", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmpGreen", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmpGreen", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmpGreen", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + { "GatherCmpBlue", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmpBlue", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmpBlue", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmpBlue", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmpBlue", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + { "GatherCmpAlpha", /*!O*/ "V4", nullptr, "%@,S,V,S", "FIU,s,F,", EShLangAll, true }, + { "GatherCmpAlpha", /* O*/ "V4", nullptr, "%@,S,V,S,V", "FIU,s,F,,I", EShLangAll, true }, + { "GatherCmpAlpha", /* O, status*/ "V4", nullptr, "%@,S,V,S,V,>S", "FIU,s,F,,I,U", EShLangAll, true }, + { "GatherCmpAlpha", /* O-4 */ "V4", nullptr, "%@,S,V,S,V,,,", "FIU,s,F,,I,,,", EShLangAll, true }, + { "GatherCmpAlpha", /* O-4, status */"V4", nullptr, "%@,S,V,S,V,,,,S","FIU,s,F,,I,,,,U",EShLangAll, true }, + + // geometry methods + { "Append", "-", "-", "-", "-", EShLangGS , true }, + { "RestartStrip", "-", "-", "-", "-", EShLangGS , true }, + + // Methods for structurebuffers. TODO: wildcard type matching. + { "Load", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Load2", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Load3", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Load4", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Store", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Store2", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Store3", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Store4", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "GetDimensions", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedAdd", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedAnd", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedCompareExchange", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedCompareStore", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedExchange", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedMax", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedMin", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedOr", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "InterlockedXor", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "IncrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "DecrementCounter", nullptr, nullptr, "-", "-", EShLangAll, true }, + { "Consume", nullptr, nullptr, "-", "-", EShLangAll, true }, + + // SM 6.0 + + { "WaveIsFirstLane", "S", "B", "-", "-", EShLangPSCS, false}, + { "WaveGetLaneCount", "S", "U", "-", "-", EShLangPSCS, false}, + { "WaveGetLaneIndex", "S", "U", "-", "-", EShLangPSCS, false}, + { "WaveActiveAnyTrue", "S", "B", "S", "B", EShLangPSCS, false}, + { "WaveActiveAllTrue", "S", "B", "S", "B", EShLangPSCS, false}, + { "WaveActiveBallot", "V4", "U", "S", "B", EShLangPSCS, false}, + { "WaveReadLaneAt", nullptr, nullptr, "SV,S", "DFUI,U", EShLangPSCS, false}, + { "WaveReadLaneFirst", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveAllEqual", "S", "B", "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveAllEqualBool", "S", "B", "S", "B", EShLangPSCS, false}, + { "WaveActiveCountBits", "S", "U", "S", "B", EShLangPSCS, false}, + + { "WaveActiveSum", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveProduct", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveBitAnd", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveBitOr", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveBitXor", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveMin", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WaveActiveMax", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WavePrefixSum", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WavePrefixProduct", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "WavePrefixCountBits", "S", "U", "S", "B", EShLangPSCS, false}, + { "QuadReadAcrossX", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "QuadReadAcrossY", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "QuadReadAcrossDiagonal", nullptr, nullptr, "SV", "DFUI", EShLangPSCS, false}, + { "QuadReadLaneAt", nullptr, nullptr, "SV,S", "DFUI,U", EShLangPSCS, false}, + + // Methods for subpass input objects + { "SubpassLoad", "V4", nullptr, "[", "FIU", EShLangPS, true }, + { "SubpassLoad", "V4", nullptr, "],S", "FIU,I", EShLangPS, true }, + + // Mark end of list, since we want to avoid a range-based for, as some compilers don't handle it yet. + { nullptr, nullptr, nullptr, nullptr, nullptr, 0, false }, + }; + + // Create prototypes for the intrinsics. TODO: Avoid ranged based for until all compilers can handle it. + for (int icount = 0; hlslIntrinsics[icount].name; ++icount) { + const auto& intrinsic = hlslIntrinsics[icount]; + + for (int stage = 0; stage < EShLangCount; ++stage) { // for each stage... + if ((intrinsic.stage & (1< 0 ? std::min(dim0, 3) : dim0; + + s.append(arg > 0 ? ", ": ""); // comma separator if needed + + const char* orderBegin = nthArgOrder; + nthArgOrder = IoParam(s, nthArgOrder); + + // Comma means use the previous argument order and type. + HandleRepeatArg(nthArgOrder, prevArgOrder, orderBegin); + HandleRepeatArg(nthArgType, prevArgType, nthArgType); + + // In case the repeated arg has its own I/O marker + nthArgOrder = IoParam(s, nthArgOrder); + + // arrayed textures have one extra coordinate dimension, except for + // the CalculateLevelOfDetail family. + if (isArrayed && arg == coordArg && !NoArrayCoord(intrinsic.name)) + argDim0++; + + // Some texture methods use an addition arg dimension to hold mip + if (arg == coordArg && mipInCoord) + argDim0++; + + // For textures, the 1D case isn't a 1-vector, but a scalar. + if (isTexture && argDim0 == 1 && arg > 0 && *nthArgOrder == 'V') + nthArgOrder = "S"; + + AppendTypeName(s, nthArgOrder, nthArgType, argDim0, dim1); // Add arguments + } + + s.append(");\n"); // close paren and trailing semicolon + } // dim 1 loop + } // dim 0 loop + } // arg type loop + + // skip over special characters + if (isTexture && isalpha(argOrder[1])) + ++argOrder; + if (isdigit(argOrder[1])) + ++argOrder; + } // arg order loop + + if (intrinsic.stage == EShLangAll) // common builtins are only added once. + break; + } + } + + createMatTimesMat(); // handle this case separately, for convenience + + // printf("Common:\n%s\n", getCommonString().c_str()); + // printf("Frag:\n%s\n", getStageString(EShLangFragment).c_str()); + // printf("Vertex:\n%s\n", getStageString(EShLangVertex).c_str()); + // printf("Geo:\n%s\n", getStageString(EShLangGeometry).c_str()); + // printf("TessCtrl:\n%s\n", getStageString(EShLangTessControl).c_str()); + // printf("TessEval:\n%s\n", getStageString(EShLangTessEvaluation).c_str()); + // printf("Compute:\n%s\n", getStageString(EShLangCompute).c_str()); +} + +// +// Add context-dependent built-in functions and variables that are present +// for the given version and profile. All the results are put into just the +// commonBuiltins, because it is called for just a specific stage. So, +// add stage-specific entries to the commonBuiltins, and only if that stage +// was requested. +// +void TBuiltInParseablesHlsl::initialize(const TBuiltInResource& /*resources*/, int /*version*/, EProfile /*profile*/, + const SpvVersion& /*spvVersion*/, EShLanguage /*language*/) +{ +} + +// +// Finish adding/processing context-independent built-in symbols. +// 1) Programmatically add symbols that could not be added by simple text strings above. +// 2) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 3) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/, EShLanguage /*language*/, + TSymbolTable& symbolTable) +{ + // symbolTable.relateToOperator("abort", EOpAbort); + symbolTable.relateToOperator("abs", EOpAbs); + symbolTable.relateToOperator("acos", EOpAcos); + symbolTable.relateToOperator("all", EOpAll); + symbolTable.relateToOperator("AllMemoryBarrier", EOpMemoryBarrier); + symbolTable.relateToOperator("AllMemoryBarrierWithGroupSync", EOpAllMemoryBarrierWithGroupSync); + symbolTable.relateToOperator("any", EOpAny); + symbolTable.relateToOperator("asdouble", EOpAsDouble); + symbolTable.relateToOperator("asfloat", EOpIntBitsToFloat); + symbolTable.relateToOperator("asin", EOpAsin); + symbolTable.relateToOperator("asint", EOpFloatBitsToInt); + symbolTable.relateToOperator("asuint", EOpFloatBitsToUint); + symbolTable.relateToOperator("atan", EOpAtan); + symbolTable.relateToOperator("atan2", EOpAtan); + symbolTable.relateToOperator("ceil", EOpCeil); + // symbolTable.relateToOperator("CheckAccessFullyMapped"); + symbolTable.relateToOperator("clamp", EOpClamp); + symbolTable.relateToOperator("clip", EOpClip); + symbolTable.relateToOperator("cos", EOpCos); + symbolTable.relateToOperator("cosh", EOpCosh); + symbolTable.relateToOperator("countbits", EOpBitCount); + symbolTable.relateToOperator("cross", EOpCross); + symbolTable.relateToOperator("D3DCOLORtoUBYTE4", EOpD3DCOLORtoUBYTE4); + symbolTable.relateToOperator("ddx", EOpDPdx); + symbolTable.relateToOperator("ddx_coarse", EOpDPdxCoarse); + symbolTable.relateToOperator("ddx_fine", EOpDPdxFine); + symbolTable.relateToOperator("ddy", EOpDPdy); + symbolTable.relateToOperator("ddy_coarse", EOpDPdyCoarse); + symbolTable.relateToOperator("ddy_fine", EOpDPdyFine); + symbolTable.relateToOperator("degrees", EOpDegrees); + symbolTable.relateToOperator("determinant", EOpDeterminant); + symbolTable.relateToOperator("DeviceMemoryBarrier", EOpDeviceMemoryBarrier); + symbolTable.relateToOperator("DeviceMemoryBarrierWithGroupSync", EOpDeviceMemoryBarrierWithGroupSync); + symbolTable.relateToOperator("distance", EOpDistance); + symbolTable.relateToOperator("dot", EOpDot); + symbolTable.relateToOperator("dst", EOpDst); + // symbolTable.relateToOperator("errorf", EOpErrorf); + symbolTable.relateToOperator("EvaluateAttributeAtCentroid", EOpInterpolateAtCentroid); + symbolTable.relateToOperator("EvaluateAttributeAtSample", EOpInterpolateAtSample); + symbolTable.relateToOperator("EvaluateAttributeSnapped", EOpEvaluateAttributeSnapped); + symbolTable.relateToOperator("exp", EOpExp); + symbolTable.relateToOperator("exp2", EOpExp2); + symbolTable.relateToOperator("f16tof32", EOpF16tof32); + symbolTable.relateToOperator("f32tof16", EOpF32tof16); + symbolTable.relateToOperator("faceforward", EOpFaceForward); + symbolTable.relateToOperator("firstbithigh", EOpFindMSB); + symbolTable.relateToOperator("firstbitlow", EOpFindLSB); + symbolTable.relateToOperator("floor", EOpFloor); + symbolTable.relateToOperator("fma", EOpFma); + symbolTable.relateToOperator("fmod", EOpMod); + symbolTable.relateToOperator("frac", EOpFract); + symbolTable.relateToOperator("frexp", EOpFrexp); + symbolTable.relateToOperator("fwidth", EOpFwidth); + // symbolTable.relateToOperator("GetRenderTargetSampleCount"); + // symbolTable.relateToOperator("GetRenderTargetSamplePosition"); + symbolTable.relateToOperator("GroupMemoryBarrier", EOpWorkgroupMemoryBarrier); + symbolTable.relateToOperator("GroupMemoryBarrierWithGroupSync", EOpWorkgroupMemoryBarrierWithGroupSync); + symbolTable.relateToOperator("InterlockedAdd", EOpInterlockedAdd); + symbolTable.relateToOperator("InterlockedAnd", EOpInterlockedAnd); + symbolTable.relateToOperator("InterlockedCompareExchange", EOpInterlockedCompareExchange); + symbolTable.relateToOperator("InterlockedCompareStore", EOpInterlockedCompareStore); + symbolTable.relateToOperator("InterlockedExchange", EOpInterlockedExchange); + symbolTable.relateToOperator("InterlockedMax", EOpInterlockedMax); + symbolTable.relateToOperator("InterlockedMin", EOpInterlockedMin); + symbolTable.relateToOperator("InterlockedOr", EOpInterlockedOr); + symbolTable.relateToOperator("InterlockedXor", EOpInterlockedXor); + symbolTable.relateToOperator("isfinite", EOpIsFinite); + symbolTable.relateToOperator("isinf", EOpIsInf); + symbolTable.relateToOperator("isnan", EOpIsNan); + symbolTable.relateToOperator("ldexp", EOpLdexp); + symbolTable.relateToOperator("length", EOpLength); + symbolTable.relateToOperator("lerp", EOpMix); + symbolTable.relateToOperator("lit", EOpLit); + symbolTable.relateToOperator("log", EOpLog); + symbolTable.relateToOperator("log10", EOpLog10); + symbolTable.relateToOperator("log2", EOpLog2); + symbolTable.relateToOperator("mad", EOpFma); + symbolTable.relateToOperator("max", EOpMax); + symbolTable.relateToOperator("min", EOpMin); + symbolTable.relateToOperator("modf", EOpModf); + // symbolTable.relateToOperator("msad4", EOpMsad4); + symbolTable.relateToOperator("mul", EOpGenMul); + // symbolTable.relateToOperator("noise", EOpNoise); // TODO: check return type + symbolTable.relateToOperator("normalize", EOpNormalize); + symbolTable.relateToOperator("pow", EOpPow); + symbolTable.relateToOperator("printf", EOpDebugPrintf); + // symbolTable.relateToOperator("Process2DQuadTessFactorsAvg"); + // symbolTable.relateToOperator("Process2DQuadTessFactorsMax"); + // symbolTable.relateToOperator("Process2DQuadTessFactorsMin"); + // symbolTable.relateToOperator("ProcessIsolineTessFactors"); + // symbolTable.relateToOperator("ProcessQuadTessFactorsAvg"); + // symbolTable.relateToOperator("ProcessQuadTessFactorsMax"); + // symbolTable.relateToOperator("ProcessQuadTessFactorsMin"); + // symbolTable.relateToOperator("ProcessTriTessFactorsAvg"); + // symbolTable.relateToOperator("ProcessTriTessFactorsMax"); + // symbolTable.relateToOperator("ProcessTriTessFactorsMin"); + symbolTable.relateToOperator("radians", EOpRadians); + symbolTable.relateToOperator("rcp", EOpRcp); + symbolTable.relateToOperator("reflect", EOpReflect); + symbolTable.relateToOperator("refract", EOpRefract); + symbolTable.relateToOperator("reversebits", EOpBitFieldReverse); + symbolTable.relateToOperator("round", EOpRoundEven); + symbolTable.relateToOperator("rsqrt", EOpInverseSqrt); + symbolTable.relateToOperator("saturate", EOpSaturate); + symbolTable.relateToOperator("sign", EOpSign); + symbolTable.relateToOperator("sin", EOpSin); + symbolTable.relateToOperator("sincos", EOpSinCos); + symbolTable.relateToOperator("sinh", EOpSinh); + symbolTable.relateToOperator("smoothstep", EOpSmoothStep); + symbolTable.relateToOperator("sqrt", EOpSqrt); + symbolTable.relateToOperator("step", EOpStep); + symbolTable.relateToOperator("tan", EOpTan); + symbolTable.relateToOperator("tanh", EOpTanh); + symbolTable.relateToOperator("tex1D", EOpTexture); + symbolTable.relateToOperator("tex1Dbias", EOpTextureBias); + symbolTable.relateToOperator("tex1Dgrad", EOpTextureGrad); + symbolTable.relateToOperator("tex1Dlod", EOpTextureLod); + symbolTable.relateToOperator("tex1Dproj", EOpTextureProj); + symbolTable.relateToOperator("tex2D", EOpTexture); + symbolTable.relateToOperator("tex2Dbias", EOpTextureBias); + symbolTable.relateToOperator("tex2Dgrad", EOpTextureGrad); + symbolTable.relateToOperator("tex2Dlod", EOpTextureLod); + symbolTable.relateToOperator("tex2Dproj", EOpTextureProj); + symbolTable.relateToOperator("tex3D", EOpTexture); + symbolTable.relateToOperator("tex3Dbias", EOpTextureBias); + symbolTable.relateToOperator("tex3Dgrad", EOpTextureGrad); + symbolTable.relateToOperator("tex3Dlod", EOpTextureLod); + symbolTable.relateToOperator("tex3Dproj", EOpTextureProj); + symbolTable.relateToOperator("texCUBE", EOpTexture); + symbolTable.relateToOperator("texCUBEbias", EOpTextureBias); + symbolTable.relateToOperator("texCUBEgrad", EOpTextureGrad); + symbolTable.relateToOperator("texCUBElod", EOpTextureLod); + symbolTable.relateToOperator("texCUBEproj", EOpTextureProj); + symbolTable.relateToOperator("transpose", EOpTranspose); + symbolTable.relateToOperator("trunc", EOpTrunc); + + // Texture methods + symbolTable.relateToOperator(BUILTIN_PREFIX "Sample", EOpMethodSample); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleBias", EOpMethodSampleBias); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleCmp", EOpMethodSampleCmp); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleCmpLevelZero", EOpMethodSampleCmpLevelZero); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleGrad", EOpMethodSampleGrad); + symbolTable.relateToOperator(BUILTIN_PREFIX "SampleLevel", EOpMethodSampleLevel); + symbolTable.relateToOperator(BUILTIN_PREFIX "Load", EOpMethodLoad); + symbolTable.relateToOperator(BUILTIN_PREFIX "GetDimensions", EOpMethodGetDimensions); + symbolTable.relateToOperator(BUILTIN_PREFIX "GetSamplePosition", EOpMethodGetSamplePosition); + symbolTable.relateToOperator(BUILTIN_PREFIX "Gather", EOpMethodGather); + symbolTable.relateToOperator(BUILTIN_PREFIX "CalculateLevelOfDetail", EOpMethodCalculateLevelOfDetail); + symbolTable.relateToOperator(BUILTIN_PREFIX "CalculateLevelOfDetailUnclamped", EOpMethodCalculateLevelOfDetailUnclamped); + + // Structure buffer methods (excluding associations already made above for texture methods w/ same name) + symbolTable.relateToOperator(BUILTIN_PREFIX "Load2", EOpMethodLoad2); + symbolTable.relateToOperator(BUILTIN_PREFIX "Load3", EOpMethodLoad3); + symbolTable.relateToOperator(BUILTIN_PREFIX "Load4", EOpMethodLoad4); + symbolTable.relateToOperator(BUILTIN_PREFIX "Store", EOpMethodStore); + symbolTable.relateToOperator(BUILTIN_PREFIX "Store2", EOpMethodStore2); + symbolTable.relateToOperator(BUILTIN_PREFIX "Store3", EOpMethodStore3); + symbolTable.relateToOperator(BUILTIN_PREFIX "Store4", EOpMethodStore4); + symbolTable.relateToOperator(BUILTIN_PREFIX "IncrementCounter", EOpMethodIncrementCounter); + symbolTable.relateToOperator(BUILTIN_PREFIX "DecrementCounter", EOpMethodDecrementCounter); + // Append is also a GS method: we don't add it twice + symbolTable.relateToOperator(BUILTIN_PREFIX "Consume", EOpMethodConsume); + + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAdd", EOpInterlockedAdd); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedAnd", EOpInterlockedAnd); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedCompareExchange", EOpInterlockedCompareExchange); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedCompareStore", EOpInterlockedCompareStore); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedExchange", EOpInterlockedExchange); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedMax", EOpInterlockedMax); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedMin", EOpInterlockedMin); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedOr", EOpInterlockedOr); + symbolTable.relateToOperator(BUILTIN_PREFIX "InterlockedXor", EOpInterlockedXor); + + // SM5 Texture methods + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherRed", EOpMethodGatherRed); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherGreen", EOpMethodGatherGreen); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherBlue", EOpMethodGatherBlue); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherAlpha", EOpMethodGatherAlpha); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmp", EOpMethodGatherCmpRed); // alias + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpRed", EOpMethodGatherCmpRed); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpGreen", EOpMethodGatherCmpGreen); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpBlue", EOpMethodGatherCmpBlue); + symbolTable.relateToOperator(BUILTIN_PREFIX "GatherCmpAlpha", EOpMethodGatherCmpAlpha); + + // GS methods + symbolTable.relateToOperator(BUILTIN_PREFIX "Append", EOpMethodAppend); + symbolTable.relateToOperator(BUILTIN_PREFIX "RestartStrip", EOpMethodRestartStrip); + + // Wave ops + symbolTable.relateToOperator("WaveIsFirstLane", EOpSubgroupElect); + symbolTable.relateToOperator("WaveGetLaneCount", EOpWaveGetLaneCount); + symbolTable.relateToOperator("WaveGetLaneIndex", EOpWaveGetLaneIndex); + symbolTable.relateToOperator("WaveActiveAnyTrue", EOpSubgroupAny); + symbolTable.relateToOperator("WaveActiveAllTrue", EOpSubgroupAll); + symbolTable.relateToOperator("WaveActiveBallot", EOpSubgroupBallot); + symbolTable.relateToOperator("WaveReadLaneFirst", EOpSubgroupBroadcastFirst); + symbolTable.relateToOperator("WaveReadLaneAt", EOpSubgroupShuffle); + symbolTable.relateToOperator("WaveActiveAllEqual", EOpSubgroupAllEqual); + symbolTable.relateToOperator("WaveActiveAllEqualBool", EOpSubgroupAllEqual); + symbolTable.relateToOperator("WaveActiveCountBits", EOpWaveActiveCountBits); + symbolTable.relateToOperator("WaveActiveSum", EOpSubgroupAdd); + symbolTable.relateToOperator("WaveActiveProduct", EOpSubgroupMul); + symbolTable.relateToOperator("WaveActiveBitAnd", EOpSubgroupAnd); + symbolTable.relateToOperator("WaveActiveBitOr", EOpSubgroupOr); + symbolTable.relateToOperator("WaveActiveBitXor", EOpSubgroupXor); + symbolTable.relateToOperator("WaveActiveMin", EOpSubgroupMin); + symbolTable.relateToOperator("WaveActiveMax", EOpSubgroupMax); + symbolTable.relateToOperator("WavePrefixSum", EOpSubgroupInclusiveAdd); + symbolTable.relateToOperator("WavePrefixProduct", EOpSubgroupInclusiveMul); + symbolTable.relateToOperator("WavePrefixCountBits", EOpWavePrefixCountBits); + symbolTable.relateToOperator("QuadReadAcrossX", EOpSubgroupQuadSwapHorizontal); + symbolTable.relateToOperator("QuadReadAcrossY", EOpSubgroupQuadSwapVertical); + symbolTable.relateToOperator("QuadReadAcrossDiagonal", EOpSubgroupQuadSwapDiagonal); + symbolTable.relateToOperator("QuadReadLaneAt", EOpSubgroupQuadBroadcast); + + // Subpass input methods + symbolTable.relateToOperator(BUILTIN_PREFIX "SubpassLoad", EOpSubpassLoad); + symbolTable.relateToOperator(BUILTIN_PREFIX "SubpassLoadMS", EOpSubpassLoadMS); +} + +// +// Add context-dependent (resource-specific) built-ins not handled by the above. These +// would be ones that need to be programmatically added because they cannot +// be added by simple text strings. For these, also +// 1) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 2) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltInParseablesHlsl::identifyBuiltIns(int /*version*/, EProfile /*profile*/, const SpvVersion& /*spvVersion*/, EShLanguage /*language*/, + TSymbolTable& /*symbolTable*/, const TBuiltInResource& /*resources*/) +{ +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/HLSL/hlslParseables.h b/third_party/glslang/glslang/HLSL/hlslParseables.h new file mode 100644 index 0000000..a4aef6c --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslParseables.h @@ -0,0 +1,64 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _HLSLPARSEABLES_INCLUDED_ +#define _HLSLPARSEABLES_INCLUDED_ + +#include "../MachineIndependent/Initialize.h" + +namespace glslang { + +// +// This is an HLSL specific derivation of TBuiltInParseables. See comment +// above TBuiltInParseables for details. +// +class TBuiltInParseablesHlsl : public TBuiltInParseables { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TBuiltInParseablesHlsl(); + void initialize(int version, EProfile, const SpvVersion& spvVersion); + void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage); + + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable); + + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources); + +private: + void createMatTimesMat(); +}; + +} // end namespace glslang + +#endif // _HLSLPARSEABLES_INCLUDED_ diff --git a/third_party/glslang/glslang/HLSL/hlslScanContext.cpp b/third_party/glslang/glslang/HLSL/hlslScanContext.cpp new file mode 100644 index 0000000..fc62672 --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslScanContext.cpp @@ -0,0 +1,903 @@ +// +// Copyright (C) 2016 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// HLSL scanning, leveraging the scanning done by the preprocessor. +// + +#include +#include +#include + +#include "../Include/Types.h" +#include "../MachineIndependent/SymbolTable.h" +#include "../MachineIndependent/ParseHelper.h" +#include "hlslScanContext.h" +#include "hlslTokens.h" + +// preprocessor includes +#include "../MachineIndependent/preprocessor/PpContext.h" +#include "../MachineIndependent/preprocessor/PpTokens.h" + +namespace { + +struct str_eq +{ + bool operator()(const char* lhs, const char* rhs) const + { + return strcmp(lhs, rhs) == 0; + } +}; + +struct str_hash +{ + size_t operator()(const char* str) const + { + // djb2 + unsigned long hash = 5381; + int c; + + while ((c = *str++) != 0) + hash = ((hash << 5) + hash) + c; + + return hash; + } +}; + +// A single global usable by all threads, by all versions, by all languages. +// After a single process-level initialization, this is read only and thread safe +std::unordered_map* KeywordMap = nullptr; +std::unordered_set* ReservedSet = nullptr; +std::unordered_map* SemanticMap = nullptr; + +}; + +namespace glslang { + +void HlslScanContext::fillInKeywordMap() +{ + if (KeywordMap != nullptr) { + // this is really an error, as this should called only once per process + // but, the only risk is if two threads called simultaneously + return; + } + KeywordMap = new std::unordered_map; + + (*KeywordMap)["static"] = EHTokStatic; + (*KeywordMap)["const"] = EHTokConst; + (*KeywordMap)["unorm"] = EHTokUnorm; + (*KeywordMap)["snorm"] = EHTokSNorm; + (*KeywordMap)["extern"] = EHTokExtern; + (*KeywordMap)["uniform"] = EHTokUniform; + (*KeywordMap)["volatile"] = EHTokVolatile; + (*KeywordMap)["precise"] = EHTokPrecise; + (*KeywordMap)["shared"] = EHTokShared; + (*KeywordMap)["groupshared"] = EHTokGroupShared; + (*KeywordMap)["linear"] = EHTokLinear; + (*KeywordMap)["centroid"] = EHTokCentroid; + (*KeywordMap)["nointerpolation"] = EHTokNointerpolation; + (*KeywordMap)["noperspective"] = EHTokNoperspective; + (*KeywordMap)["sample"] = EHTokSample; + (*KeywordMap)["row_major"] = EHTokRowMajor; + (*KeywordMap)["column_major"] = EHTokColumnMajor; + (*KeywordMap)["packoffset"] = EHTokPackOffset; + (*KeywordMap)["in"] = EHTokIn; + (*KeywordMap)["out"] = EHTokOut; + (*KeywordMap)["inout"] = EHTokInOut; + (*KeywordMap)["layout"] = EHTokLayout; + (*KeywordMap)["globallycoherent"] = EHTokGloballyCoherent; + (*KeywordMap)["inline"] = EHTokInline; + + (*KeywordMap)["point"] = EHTokPoint; + (*KeywordMap)["line"] = EHTokLine; + (*KeywordMap)["triangle"] = EHTokTriangle; + (*KeywordMap)["lineadj"] = EHTokLineAdj; + (*KeywordMap)["triangleadj"] = EHTokTriangleAdj; + + (*KeywordMap)["PointStream"] = EHTokPointStream; + (*KeywordMap)["LineStream"] = EHTokLineStream; + (*KeywordMap)["TriangleStream"] = EHTokTriangleStream; + + (*KeywordMap)["InputPatch"] = EHTokInputPatch; + (*KeywordMap)["OutputPatch"] = EHTokOutputPatch; + + (*KeywordMap)["Buffer"] = EHTokBuffer; + (*KeywordMap)["vector"] = EHTokVector; + (*KeywordMap)["matrix"] = EHTokMatrix; + + (*KeywordMap)["void"] = EHTokVoid; + (*KeywordMap)["string"] = EHTokString; + (*KeywordMap)["bool"] = EHTokBool; + (*KeywordMap)["int"] = EHTokInt; + (*KeywordMap)["uint"] = EHTokUint; + (*KeywordMap)["uint64_t"] = EHTokUint64; + (*KeywordMap)["dword"] = EHTokDword; + (*KeywordMap)["half"] = EHTokHalf; + (*KeywordMap)["float"] = EHTokFloat; + (*KeywordMap)["double"] = EHTokDouble; + (*KeywordMap)["min16float"] = EHTokMin16float; + (*KeywordMap)["min10float"] = EHTokMin10float; + (*KeywordMap)["min16int"] = EHTokMin16int; + (*KeywordMap)["min12int"] = EHTokMin12int; + (*KeywordMap)["min16uint"] = EHTokMin16uint; + + (*KeywordMap)["bool1"] = EHTokBool1; + (*KeywordMap)["bool2"] = EHTokBool2; + (*KeywordMap)["bool3"] = EHTokBool3; + (*KeywordMap)["bool4"] = EHTokBool4; + (*KeywordMap)["float1"] = EHTokFloat1; + (*KeywordMap)["float2"] = EHTokFloat2; + (*KeywordMap)["float3"] = EHTokFloat3; + (*KeywordMap)["float4"] = EHTokFloat4; + (*KeywordMap)["int1"] = EHTokInt1; + (*KeywordMap)["int2"] = EHTokInt2; + (*KeywordMap)["int3"] = EHTokInt3; + (*KeywordMap)["int4"] = EHTokInt4; + (*KeywordMap)["double1"] = EHTokDouble1; + (*KeywordMap)["double2"] = EHTokDouble2; + (*KeywordMap)["double3"] = EHTokDouble3; + (*KeywordMap)["double4"] = EHTokDouble4; + (*KeywordMap)["uint1"] = EHTokUint1; + (*KeywordMap)["uint2"] = EHTokUint2; + (*KeywordMap)["uint3"] = EHTokUint3; + (*KeywordMap)["uint4"] = EHTokUint4; + + (*KeywordMap)["half1"] = EHTokHalf1; + (*KeywordMap)["half2"] = EHTokHalf2; + (*KeywordMap)["half3"] = EHTokHalf3; + (*KeywordMap)["half4"] = EHTokHalf4; + (*KeywordMap)["min16float1"] = EHTokMin16float1; + (*KeywordMap)["min16float2"] = EHTokMin16float2; + (*KeywordMap)["min16float3"] = EHTokMin16float3; + (*KeywordMap)["min16float4"] = EHTokMin16float4; + (*KeywordMap)["min10float1"] = EHTokMin10float1; + (*KeywordMap)["min10float2"] = EHTokMin10float2; + (*KeywordMap)["min10float3"] = EHTokMin10float3; + (*KeywordMap)["min10float4"] = EHTokMin10float4; + (*KeywordMap)["min16int1"] = EHTokMin16int1; + (*KeywordMap)["min16int2"] = EHTokMin16int2; + (*KeywordMap)["min16int3"] = EHTokMin16int3; + (*KeywordMap)["min16int4"] = EHTokMin16int4; + (*KeywordMap)["min12int1"] = EHTokMin12int1; + (*KeywordMap)["min12int2"] = EHTokMin12int2; + (*KeywordMap)["min12int3"] = EHTokMin12int3; + (*KeywordMap)["min12int4"] = EHTokMin12int4; + (*KeywordMap)["min16uint1"] = EHTokMin16uint1; + (*KeywordMap)["min16uint2"] = EHTokMin16uint2; + (*KeywordMap)["min16uint3"] = EHTokMin16uint3; + (*KeywordMap)["min16uint4"] = EHTokMin16uint4; + + (*KeywordMap)["bool1x1"] = EHTokBool1x1; + (*KeywordMap)["bool1x2"] = EHTokBool1x2; + (*KeywordMap)["bool1x3"] = EHTokBool1x3; + (*KeywordMap)["bool1x4"] = EHTokBool1x4; + (*KeywordMap)["bool2x1"] = EHTokBool2x1; + (*KeywordMap)["bool2x2"] = EHTokBool2x2; + (*KeywordMap)["bool2x3"] = EHTokBool2x3; + (*KeywordMap)["bool2x4"] = EHTokBool2x4; + (*KeywordMap)["bool3x1"] = EHTokBool3x1; + (*KeywordMap)["bool3x2"] = EHTokBool3x2; + (*KeywordMap)["bool3x3"] = EHTokBool3x3; + (*KeywordMap)["bool3x4"] = EHTokBool3x4; + (*KeywordMap)["bool4x1"] = EHTokBool4x1; + (*KeywordMap)["bool4x2"] = EHTokBool4x2; + (*KeywordMap)["bool4x3"] = EHTokBool4x3; + (*KeywordMap)["bool4x4"] = EHTokBool4x4; + (*KeywordMap)["int1x1"] = EHTokInt1x1; + (*KeywordMap)["int1x2"] = EHTokInt1x2; + (*KeywordMap)["int1x3"] = EHTokInt1x3; + (*KeywordMap)["int1x4"] = EHTokInt1x4; + (*KeywordMap)["int2x1"] = EHTokInt2x1; + (*KeywordMap)["int2x2"] = EHTokInt2x2; + (*KeywordMap)["int2x3"] = EHTokInt2x3; + (*KeywordMap)["int2x4"] = EHTokInt2x4; + (*KeywordMap)["int3x1"] = EHTokInt3x1; + (*KeywordMap)["int3x2"] = EHTokInt3x2; + (*KeywordMap)["int3x3"] = EHTokInt3x3; + (*KeywordMap)["int3x4"] = EHTokInt3x4; + (*KeywordMap)["int4x1"] = EHTokInt4x1; + (*KeywordMap)["int4x2"] = EHTokInt4x2; + (*KeywordMap)["int4x3"] = EHTokInt4x3; + (*KeywordMap)["int4x4"] = EHTokInt4x4; + (*KeywordMap)["uint1x1"] = EHTokUint1x1; + (*KeywordMap)["uint1x2"] = EHTokUint1x2; + (*KeywordMap)["uint1x3"] = EHTokUint1x3; + (*KeywordMap)["uint1x4"] = EHTokUint1x4; + (*KeywordMap)["uint2x1"] = EHTokUint2x1; + (*KeywordMap)["uint2x2"] = EHTokUint2x2; + (*KeywordMap)["uint2x3"] = EHTokUint2x3; + (*KeywordMap)["uint2x4"] = EHTokUint2x4; + (*KeywordMap)["uint3x1"] = EHTokUint3x1; + (*KeywordMap)["uint3x2"] = EHTokUint3x2; + (*KeywordMap)["uint3x3"] = EHTokUint3x3; + (*KeywordMap)["uint3x4"] = EHTokUint3x4; + (*KeywordMap)["uint4x1"] = EHTokUint4x1; + (*KeywordMap)["uint4x2"] = EHTokUint4x2; + (*KeywordMap)["uint4x3"] = EHTokUint4x3; + (*KeywordMap)["uint4x4"] = EHTokUint4x4; + (*KeywordMap)["bool1x1"] = EHTokBool1x1; + (*KeywordMap)["bool1x2"] = EHTokBool1x2; + (*KeywordMap)["bool1x3"] = EHTokBool1x3; + (*KeywordMap)["bool1x4"] = EHTokBool1x4; + (*KeywordMap)["bool2x1"] = EHTokBool2x1; + (*KeywordMap)["bool2x2"] = EHTokBool2x2; + (*KeywordMap)["bool2x3"] = EHTokBool2x3; + (*KeywordMap)["bool2x4"] = EHTokBool2x4; + (*KeywordMap)["bool3x1"] = EHTokBool3x1; + (*KeywordMap)["bool3x2"] = EHTokBool3x2; + (*KeywordMap)["bool3x3"] = EHTokBool3x3; + (*KeywordMap)["bool3x4"] = EHTokBool3x4; + (*KeywordMap)["bool4x1"] = EHTokBool4x1; + (*KeywordMap)["bool4x2"] = EHTokBool4x2; + (*KeywordMap)["bool4x3"] = EHTokBool4x3; + (*KeywordMap)["bool4x4"] = EHTokBool4x4; + (*KeywordMap)["float1x1"] = EHTokFloat1x1; + (*KeywordMap)["float1x2"] = EHTokFloat1x2; + (*KeywordMap)["float1x3"] = EHTokFloat1x3; + (*KeywordMap)["float1x4"] = EHTokFloat1x4; + (*KeywordMap)["float2x1"] = EHTokFloat2x1; + (*KeywordMap)["float2x2"] = EHTokFloat2x2; + (*KeywordMap)["float2x3"] = EHTokFloat2x3; + (*KeywordMap)["float2x4"] = EHTokFloat2x4; + (*KeywordMap)["float3x1"] = EHTokFloat3x1; + (*KeywordMap)["float3x2"] = EHTokFloat3x2; + (*KeywordMap)["float3x3"] = EHTokFloat3x3; + (*KeywordMap)["float3x4"] = EHTokFloat3x4; + (*KeywordMap)["float4x1"] = EHTokFloat4x1; + (*KeywordMap)["float4x2"] = EHTokFloat4x2; + (*KeywordMap)["float4x3"] = EHTokFloat4x3; + (*KeywordMap)["float4x4"] = EHTokFloat4x4; + (*KeywordMap)["half1x1"] = EHTokHalf1x1; + (*KeywordMap)["half1x2"] = EHTokHalf1x2; + (*KeywordMap)["half1x3"] = EHTokHalf1x3; + (*KeywordMap)["half1x4"] = EHTokHalf1x4; + (*KeywordMap)["half2x1"] = EHTokHalf2x1; + (*KeywordMap)["half2x2"] = EHTokHalf2x2; + (*KeywordMap)["half2x3"] = EHTokHalf2x3; + (*KeywordMap)["half2x4"] = EHTokHalf2x4; + (*KeywordMap)["half3x1"] = EHTokHalf3x1; + (*KeywordMap)["half3x2"] = EHTokHalf3x2; + (*KeywordMap)["half3x3"] = EHTokHalf3x3; + (*KeywordMap)["half3x4"] = EHTokHalf3x4; + (*KeywordMap)["half4x1"] = EHTokHalf4x1; + (*KeywordMap)["half4x2"] = EHTokHalf4x2; + (*KeywordMap)["half4x3"] = EHTokHalf4x3; + (*KeywordMap)["half4x4"] = EHTokHalf4x4; + (*KeywordMap)["double1x1"] = EHTokDouble1x1; + (*KeywordMap)["double1x2"] = EHTokDouble1x2; + (*KeywordMap)["double1x3"] = EHTokDouble1x3; + (*KeywordMap)["double1x4"] = EHTokDouble1x4; + (*KeywordMap)["double2x1"] = EHTokDouble2x1; + (*KeywordMap)["double2x2"] = EHTokDouble2x2; + (*KeywordMap)["double2x3"] = EHTokDouble2x3; + (*KeywordMap)["double2x4"] = EHTokDouble2x4; + (*KeywordMap)["double3x1"] = EHTokDouble3x1; + (*KeywordMap)["double3x2"] = EHTokDouble3x2; + (*KeywordMap)["double3x3"] = EHTokDouble3x3; + (*KeywordMap)["double3x4"] = EHTokDouble3x4; + (*KeywordMap)["double4x1"] = EHTokDouble4x1; + (*KeywordMap)["double4x2"] = EHTokDouble4x2; + (*KeywordMap)["double4x3"] = EHTokDouble4x3; + (*KeywordMap)["double4x4"] = EHTokDouble4x4; + + (*KeywordMap)["sampler"] = EHTokSampler; + (*KeywordMap)["sampler1D"] = EHTokSampler1d; + (*KeywordMap)["sampler2D"] = EHTokSampler2d; + (*KeywordMap)["sampler3D"] = EHTokSampler3d; + (*KeywordMap)["samplerCUBE"] = EHTokSamplerCube; + (*KeywordMap)["sampler_state"] = EHTokSamplerState; + (*KeywordMap)["SamplerState"] = EHTokSamplerState; + (*KeywordMap)["SamplerComparisonState"] = EHTokSamplerComparisonState; + (*KeywordMap)["texture"] = EHTokTexture; + (*KeywordMap)["Texture1D"] = EHTokTexture1d; + (*KeywordMap)["Texture1DArray"] = EHTokTexture1darray; + (*KeywordMap)["Texture2D"] = EHTokTexture2d; + (*KeywordMap)["Texture2DArray"] = EHTokTexture2darray; + (*KeywordMap)["Texture3D"] = EHTokTexture3d; + (*KeywordMap)["TextureCube"] = EHTokTextureCube; + (*KeywordMap)["TextureCubeArray"] = EHTokTextureCubearray; + (*KeywordMap)["Texture2DMS"] = EHTokTexture2DMS; + (*KeywordMap)["Texture2DMSArray"] = EHTokTexture2DMSarray; + (*KeywordMap)["RWTexture1D"] = EHTokRWTexture1d; + (*KeywordMap)["RWTexture1DArray"] = EHTokRWTexture1darray; + (*KeywordMap)["RWTexture2D"] = EHTokRWTexture2d; + (*KeywordMap)["RWTexture2DArray"] = EHTokRWTexture2darray; + (*KeywordMap)["RWTexture3D"] = EHTokRWTexture3d; + (*KeywordMap)["RWBuffer"] = EHTokRWBuffer; + (*KeywordMap)["SubpassInput"] = EHTokSubpassInput; + (*KeywordMap)["SubpassInputMS"] = EHTokSubpassInputMS; + + (*KeywordMap)["AppendStructuredBuffer"] = EHTokAppendStructuredBuffer; + (*KeywordMap)["ByteAddressBuffer"] = EHTokByteAddressBuffer; + (*KeywordMap)["ConsumeStructuredBuffer"] = EHTokConsumeStructuredBuffer; + (*KeywordMap)["RWByteAddressBuffer"] = EHTokRWByteAddressBuffer; + (*KeywordMap)["RWStructuredBuffer"] = EHTokRWStructuredBuffer; + (*KeywordMap)["StructuredBuffer"] = EHTokStructuredBuffer; + (*KeywordMap)["TextureBuffer"] = EHTokTextureBuffer; + + (*KeywordMap)["class"] = EHTokClass; + (*KeywordMap)["struct"] = EHTokStruct; + (*KeywordMap)["cbuffer"] = EHTokCBuffer; + (*KeywordMap)["ConstantBuffer"] = EHTokConstantBuffer; + (*KeywordMap)["tbuffer"] = EHTokTBuffer; + (*KeywordMap)["typedef"] = EHTokTypedef; + (*KeywordMap)["this"] = EHTokThis; + (*KeywordMap)["namespace"] = EHTokNamespace; + + (*KeywordMap)["true"] = EHTokBoolConstant; + (*KeywordMap)["false"] = EHTokBoolConstant; + + (*KeywordMap)["for"] = EHTokFor; + (*KeywordMap)["do"] = EHTokDo; + (*KeywordMap)["while"] = EHTokWhile; + (*KeywordMap)["break"] = EHTokBreak; + (*KeywordMap)["continue"] = EHTokContinue; + (*KeywordMap)["if"] = EHTokIf; + (*KeywordMap)["else"] = EHTokElse; + (*KeywordMap)["discard"] = EHTokDiscard; + (*KeywordMap)["return"] = EHTokReturn; + (*KeywordMap)["switch"] = EHTokSwitch; + (*KeywordMap)["case"] = EHTokCase; + (*KeywordMap)["default"] = EHTokDefault; + + // TODO: get correct set here + ReservedSet = new std::unordered_set; + + ReservedSet->insert("auto"); + ReservedSet->insert("catch"); + ReservedSet->insert("char"); + ReservedSet->insert("const_cast"); + ReservedSet->insert("enum"); + ReservedSet->insert("explicit"); + ReservedSet->insert("friend"); + ReservedSet->insert("goto"); + ReservedSet->insert("long"); + ReservedSet->insert("mutable"); + ReservedSet->insert("new"); + ReservedSet->insert("operator"); + ReservedSet->insert("private"); + ReservedSet->insert("protected"); + ReservedSet->insert("public"); + ReservedSet->insert("reinterpret_cast"); + ReservedSet->insert("short"); + ReservedSet->insert("signed"); + ReservedSet->insert("sizeof"); + ReservedSet->insert("static_cast"); + ReservedSet->insert("template"); + ReservedSet->insert("throw"); + ReservedSet->insert("try"); + ReservedSet->insert("typename"); + ReservedSet->insert("union"); + ReservedSet->insert("unsigned"); + ReservedSet->insert("using"); + ReservedSet->insert("virtual"); + + SemanticMap = new std::unordered_map; + + // in DX9, all outputs had to have a semantic associated with them, that was either consumed + // by the system or was a specific register assignment + // in DX10+, only semantics with the SV_ prefix have any meaning beyond decoration + // Fxc will only accept DX9 style semantics in compat mode + // Also, in DX10 if a SV value is present as the input of a stage, but isn't appropriate for that + // stage, it would just be ignored as it is likely there as part of an output struct from one stage + // to the next + bool bParseDX9 = false; + if (bParseDX9) { + (*SemanticMap)["PSIZE"] = EbvPointSize; + (*SemanticMap)["FOG"] = EbvFogFragCoord; + (*SemanticMap)["DEPTH"] = EbvFragDepth; + (*SemanticMap)["VFACE"] = EbvFace; + (*SemanticMap)["VPOS"] = EbvFragCoord; + } + + (*SemanticMap)["SV_POSITION"] = EbvPosition; + (*SemanticMap)["SV_VERTEXID"] = EbvVertexIndex; + (*SemanticMap)["SV_VIEWPORTARRAYINDEX"] = EbvViewportIndex; + (*SemanticMap)["SV_TESSFACTOR"] = EbvTessLevelOuter; + (*SemanticMap)["SV_SAMPLEINDEX"] = EbvSampleId; + (*SemanticMap)["SV_RENDERTARGETARRAYINDEX"] = EbvLayer; + (*SemanticMap)["SV_PRIMITIVEID"] = EbvPrimitiveId; + (*SemanticMap)["SV_OUTPUTCONTROLPOINTID"] = EbvInvocationId; + (*SemanticMap)["SV_ISFRONTFACE"] = EbvFace; + (*SemanticMap)["SV_INSTANCEID"] = EbvInstanceIndex; + (*SemanticMap)["SV_INSIDETESSFACTOR"] = EbvTessLevelInner; + (*SemanticMap)["SV_GSINSTANCEID"] = EbvInvocationId; + (*SemanticMap)["SV_DISPATCHTHREADID"] = EbvGlobalInvocationId; + (*SemanticMap)["SV_GROUPTHREADID"] = EbvLocalInvocationId; + (*SemanticMap)["SV_GROUPINDEX"] = EbvLocalInvocationIndex; + (*SemanticMap)["SV_GROUPID"] = EbvWorkGroupId; + (*SemanticMap)["SV_DOMAINLOCATION"] = EbvTessCoord; + (*SemanticMap)["SV_DEPTH"] = EbvFragDepth; + (*SemanticMap)["SV_COVERAGE"] = EbvSampleMask; + (*SemanticMap)["SV_DEPTHGREATEREQUAL"] = EbvFragDepthGreater; + (*SemanticMap)["SV_DEPTHLESSEQUAL"] = EbvFragDepthLesser; + (*SemanticMap)["SV_STENCILREF"] = EbvFragStencilRef; +} + +void HlslScanContext::deleteKeywordMap() +{ + delete KeywordMap; + KeywordMap = nullptr; + delete ReservedSet; + ReservedSet = nullptr; + delete SemanticMap; + SemanticMap = nullptr; +} + +// Wrapper for tokenizeClass() to get everything inside the token. +void HlslScanContext::tokenize(HlslToken& token) +{ + EHlslTokenClass tokenClass = tokenizeClass(token); + token.tokenClass = tokenClass; +} + +glslang::TBuiltInVariable HlslScanContext::mapSemantic(const char* upperCase) +{ + auto it = SemanticMap->find(upperCase); + if (it != SemanticMap->end()) + return it->second; + else + return glslang::EbvNone; +} + +// +// Fill in token information for the next token, except for the token class. +// Returns the enum value of the token class of the next token found. +// Return 0 (EndOfTokens) on end of input. +// +EHlslTokenClass HlslScanContext::tokenizeClass(HlslToken& token) +{ + do { + parserToken = &token; + TPpToken ppToken; + int token = ppContext.tokenize(ppToken); + if (token == EndOfInput) + return EHTokNone; + + tokenText = ppToken.name; + loc = ppToken.loc; + parserToken->loc = loc; + switch (token) { + case ';': return EHTokSemicolon; + case ',': return EHTokComma; + case ':': return EHTokColon; + case '=': return EHTokAssign; + case '(': return EHTokLeftParen; + case ')': return EHTokRightParen; + case '.': return EHTokDot; + case '!': return EHTokBang; + case '-': return EHTokDash; + case '~': return EHTokTilde; + case '+': return EHTokPlus; + case '*': return EHTokStar; + case '/': return EHTokSlash; + case '%': return EHTokPercent; + case '<': return EHTokLeftAngle; + case '>': return EHTokRightAngle; + case '|': return EHTokVerticalBar; + case '^': return EHTokCaret; + case '&': return EHTokAmpersand; + case '?': return EHTokQuestion; + case '[': return EHTokLeftBracket; + case ']': return EHTokRightBracket; + case '{': return EHTokLeftBrace; + case '}': return EHTokRightBrace; + case '\\': + parseContext.error(loc, "illegal use of escape character", "\\", ""); + break; + + case PPAtomAddAssign: return EHTokAddAssign; + case PPAtomSubAssign: return EHTokSubAssign; + case PPAtomMulAssign: return EHTokMulAssign; + case PPAtomDivAssign: return EHTokDivAssign; + case PPAtomModAssign: return EHTokModAssign; + + case PpAtomRight: return EHTokRightOp; + case PpAtomLeft: return EHTokLeftOp; + + case PpAtomRightAssign: return EHTokRightAssign; + case PpAtomLeftAssign: return EHTokLeftAssign; + case PpAtomAndAssign: return EHTokAndAssign; + case PpAtomOrAssign: return EHTokOrAssign; + case PpAtomXorAssign: return EHTokXorAssign; + + case PpAtomAnd: return EHTokAndOp; + case PpAtomOr: return EHTokOrOp; + case PpAtomXor: return EHTokXorOp; + + case PpAtomEQ: return EHTokEqOp; + case PpAtomGE: return EHTokGeOp; + case PpAtomNE: return EHTokNeOp; + case PpAtomLE: return EHTokLeOp; + + case PpAtomDecrement: return EHTokDecOp; + case PpAtomIncrement: return EHTokIncOp; + + case PpAtomColonColon: return EHTokColonColon; + + case PpAtomConstInt: parserToken->i = ppToken.ival; return EHTokIntConstant; + case PpAtomConstUint: parserToken->i = ppToken.ival; return EHTokUintConstant; + case PpAtomConstFloat16: parserToken->d = ppToken.dval; return EHTokFloat16Constant; + case PpAtomConstFloat: parserToken->d = ppToken.dval; return EHTokFloatConstant; + case PpAtomConstDouble: parserToken->d = ppToken.dval; return EHTokDoubleConstant; + case PpAtomIdentifier: + { + EHlslTokenClass token = tokenizeIdentifier(); + return token; + } + + case PpAtomConstString: { + parserToken->string = NewPoolTString(tokenText); + return EHTokStringConstant; + } + + case EndOfInput: return EHTokNone; + + default: + if (token < PpAtomMaxSingle) { + char buf[2]; + buf[0] = (char)token; + buf[1] = 0; + parseContext.error(loc, "unexpected token", buf, ""); + } else if (tokenText[0] != 0) + parseContext.error(loc, "unexpected token", tokenText, ""); + else + parseContext.error(loc, "unexpected token", "", ""); + break; + } + } while (true); +} + +EHlslTokenClass HlslScanContext::tokenizeIdentifier() +{ + if (ReservedSet->find(tokenText) != ReservedSet->end()) + return reservedWord(); + + auto it = KeywordMap->find(tokenText); + if (it == KeywordMap->end()) { + // Should have an identifier of some sort + return identifierOrType(); + } + keyword = it->second; + + switch (keyword) { + + // qualifiers + case EHTokStatic: + case EHTokConst: + case EHTokSNorm: + case EHTokUnorm: + case EHTokExtern: + case EHTokUniform: + case EHTokVolatile: + case EHTokShared: + case EHTokGroupShared: + case EHTokLinear: + case EHTokCentroid: + case EHTokNointerpolation: + case EHTokNoperspective: + case EHTokSample: + case EHTokRowMajor: + case EHTokColumnMajor: + case EHTokPackOffset: + case EHTokIn: + case EHTokOut: + case EHTokInOut: + case EHTokPrecise: + case EHTokLayout: + case EHTokGloballyCoherent: + case EHTokInline: + return keyword; + + // primitive types + case EHTokPoint: + case EHTokLine: + case EHTokTriangle: + case EHTokLineAdj: + case EHTokTriangleAdj: + return keyword; + + // stream out types + case EHTokPointStream: + case EHTokLineStream: + case EHTokTriangleStream: + return keyword; + + // Tessellation patches + case EHTokInputPatch: + case EHTokOutputPatch: + return keyword; + + case EHTokBuffer: + case EHTokVector: + case EHTokMatrix: + return keyword; + + // scalar types + case EHTokVoid: + case EHTokString: + case EHTokBool: + case EHTokInt: + case EHTokUint: + case EHTokUint64: + case EHTokDword: + case EHTokHalf: + case EHTokFloat: + case EHTokDouble: + case EHTokMin16float: + case EHTokMin10float: + case EHTokMin16int: + case EHTokMin12int: + case EHTokMin16uint: + + // vector types + case EHTokBool1: + case EHTokBool2: + case EHTokBool3: + case EHTokBool4: + case EHTokFloat1: + case EHTokFloat2: + case EHTokFloat3: + case EHTokFloat4: + case EHTokInt1: + case EHTokInt2: + case EHTokInt3: + case EHTokInt4: + case EHTokDouble1: + case EHTokDouble2: + case EHTokDouble3: + case EHTokDouble4: + case EHTokUint1: + case EHTokUint2: + case EHTokUint3: + case EHTokUint4: + case EHTokHalf1: + case EHTokHalf2: + case EHTokHalf3: + case EHTokHalf4: + case EHTokMin16float1: + case EHTokMin16float2: + case EHTokMin16float3: + case EHTokMin16float4: + case EHTokMin10float1: + case EHTokMin10float2: + case EHTokMin10float3: + case EHTokMin10float4: + case EHTokMin16int1: + case EHTokMin16int2: + case EHTokMin16int3: + case EHTokMin16int4: + case EHTokMin12int1: + case EHTokMin12int2: + case EHTokMin12int3: + case EHTokMin12int4: + case EHTokMin16uint1: + case EHTokMin16uint2: + case EHTokMin16uint3: + case EHTokMin16uint4: + + // matrix types + case EHTokBool1x1: + case EHTokBool1x2: + case EHTokBool1x3: + case EHTokBool1x4: + case EHTokBool2x1: + case EHTokBool2x2: + case EHTokBool2x3: + case EHTokBool2x4: + case EHTokBool3x1: + case EHTokBool3x2: + case EHTokBool3x3: + case EHTokBool3x4: + case EHTokBool4x1: + case EHTokBool4x2: + case EHTokBool4x3: + case EHTokBool4x4: + case EHTokInt1x1: + case EHTokInt1x2: + case EHTokInt1x3: + case EHTokInt1x4: + case EHTokInt2x1: + case EHTokInt2x2: + case EHTokInt2x3: + case EHTokInt2x4: + case EHTokInt3x1: + case EHTokInt3x2: + case EHTokInt3x3: + case EHTokInt3x4: + case EHTokInt4x1: + case EHTokInt4x2: + case EHTokInt4x3: + case EHTokInt4x4: + case EHTokUint1x1: + case EHTokUint1x2: + case EHTokUint1x3: + case EHTokUint1x4: + case EHTokUint2x1: + case EHTokUint2x2: + case EHTokUint2x3: + case EHTokUint2x4: + case EHTokUint3x1: + case EHTokUint3x2: + case EHTokUint3x3: + case EHTokUint3x4: + case EHTokUint4x1: + case EHTokUint4x2: + case EHTokUint4x3: + case EHTokUint4x4: + case EHTokFloat1x1: + case EHTokFloat1x2: + case EHTokFloat1x3: + case EHTokFloat1x4: + case EHTokFloat2x1: + case EHTokFloat2x2: + case EHTokFloat2x3: + case EHTokFloat2x4: + case EHTokFloat3x1: + case EHTokFloat3x2: + case EHTokFloat3x3: + case EHTokFloat3x4: + case EHTokFloat4x1: + case EHTokFloat4x2: + case EHTokFloat4x3: + case EHTokFloat4x4: + case EHTokHalf1x1: + case EHTokHalf1x2: + case EHTokHalf1x3: + case EHTokHalf1x4: + case EHTokHalf2x1: + case EHTokHalf2x2: + case EHTokHalf2x3: + case EHTokHalf2x4: + case EHTokHalf3x1: + case EHTokHalf3x2: + case EHTokHalf3x3: + case EHTokHalf3x4: + case EHTokHalf4x1: + case EHTokHalf4x2: + case EHTokHalf4x3: + case EHTokHalf4x4: + case EHTokDouble1x1: + case EHTokDouble1x2: + case EHTokDouble1x3: + case EHTokDouble1x4: + case EHTokDouble2x1: + case EHTokDouble2x2: + case EHTokDouble2x3: + case EHTokDouble2x4: + case EHTokDouble3x1: + case EHTokDouble3x2: + case EHTokDouble3x3: + case EHTokDouble3x4: + case EHTokDouble4x1: + case EHTokDouble4x2: + case EHTokDouble4x3: + case EHTokDouble4x4: + return keyword; + + // texturing types + case EHTokSampler: + case EHTokSampler1d: + case EHTokSampler2d: + case EHTokSampler3d: + case EHTokSamplerCube: + case EHTokSamplerState: + case EHTokSamplerComparisonState: + case EHTokTexture: + case EHTokTexture1d: + case EHTokTexture1darray: + case EHTokTexture2d: + case EHTokTexture2darray: + case EHTokTexture3d: + case EHTokTextureCube: + case EHTokTextureCubearray: + case EHTokTexture2DMS: + case EHTokTexture2DMSarray: + case EHTokRWTexture1d: + case EHTokRWTexture1darray: + case EHTokRWTexture2d: + case EHTokRWTexture2darray: + case EHTokRWTexture3d: + case EHTokRWBuffer: + case EHTokAppendStructuredBuffer: + case EHTokByteAddressBuffer: + case EHTokConsumeStructuredBuffer: + case EHTokRWByteAddressBuffer: + case EHTokRWStructuredBuffer: + case EHTokStructuredBuffer: + case EHTokTextureBuffer: + case EHTokSubpassInput: + case EHTokSubpassInputMS: + return keyword; + + // variable, user type, ... + case EHTokClass: + case EHTokStruct: + case EHTokTypedef: + case EHTokCBuffer: + case EHTokConstantBuffer: + case EHTokTBuffer: + case EHTokThis: + case EHTokNamespace: + return keyword; + + case EHTokBoolConstant: + if (strcmp("true", tokenText) == 0) + parserToken->b = true; + else + parserToken->b = false; + return keyword; + + // control flow + case EHTokFor: + case EHTokDo: + case EHTokWhile: + case EHTokBreak: + case EHTokContinue: + case EHTokIf: + case EHTokElse: + case EHTokDiscard: + case EHTokReturn: + case EHTokCase: + case EHTokSwitch: + case EHTokDefault: + return keyword; + + default: + parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc); + return EHTokNone; + } +} + +EHlslTokenClass HlslScanContext::identifierOrType() +{ + parserToken->string = NewPoolTString(tokenText); + + return EHTokIdentifier; +} + +// Give an error for use of a reserved symbol. +// However, allow built-in declarations to use reserved words, to allow +// extension support before the extension is enabled. +EHlslTokenClass HlslScanContext::reservedWord() +{ + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.error(loc, "Reserved word.", tokenText, "", ""); + + return EHTokNone; +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/HLSL/hlslScanContext.h b/third_party/glslang/glslang/HLSL/hlslScanContext.h new file mode 100644 index 0000000..3b191e4 --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslScanContext.h @@ -0,0 +1,109 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This holds context specific to the HLSL scanner, which +// sits between the preprocessor scanner and HLSL parser. +// + +#ifndef HLSLSCANCONTEXT_H_ +#define HLSLSCANCONTEXT_H_ + +#include "../MachineIndependent/ParseHelper.h" +#include "hlslTokens.h" + +namespace glslang { + +class TPpContext; +class TPpToken; + + +// +// Everything needed to fully describe a token. +// +struct HlslToken { + HlslToken() : string(nullptr) { loc.init(); } + TSourceLoc loc; // location of token in the source + EHlslTokenClass tokenClass; // what kind of token it is + union { // what data the token holds + glslang::TString *string; // for identifiers + int i; // for literals + unsigned int u; + bool b; + double d; + }; +}; + +// +// The state of scanning and translating raw tokens to slightly richer +// semantics, like knowing if an identifier is an existing symbol, or +// user-defined type. +// +class HlslScanContext { +public: + HlslScanContext(TParseContextBase& parseContext, TPpContext& ppContext) + : parseContext(parseContext), ppContext(ppContext) { } + virtual ~HlslScanContext() { } + + static void fillInKeywordMap(); + static void deleteKeywordMap(); + + void tokenize(HlslToken&); + glslang::TBuiltInVariable mapSemantic(const char*); + +protected: + HlslScanContext(HlslScanContext&); + HlslScanContext& operator=(HlslScanContext&); + + EHlslTokenClass tokenizeClass(HlslToken&); + EHlslTokenClass tokenizeIdentifier(); + EHlslTokenClass identifierOrType(); + EHlslTokenClass reservedWord(); + EHlslTokenClass identifierOrReserved(bool reserved); + EHlslTokenClass nonreservedKeyword(int version); + + TParseContextBase& parseContext; + TPpContext& ppContext; + TSourceLoc loc; + TPpToken* ppToken; + HlslToken* parserToken; + + const char* tokenText; + EHlslTokenClass keyword; +}; + +} // end namespace glslang + +#endif // HLSLSCANCONTEXT_H_ diff --git a/third_party/glslang/glslang/HLSL/hlslTokenStream.cpp b/third_party/glslang/glslang/HLSL/hlslTokenStream.cpp new file mode 100644 index 0000000..5d9311c --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslTokenStream.cpp @@ -0,0 +1,150 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "hlslTokenStream.h" + +namespace glslang { + +void HlslTokenStream::pushPreToken(const HlslToken& tok) +{ + assert(preTokenStackSize < tokenBufferSize); + preTokenStack[preTokenStackSize++] = tok; +} + +HlslToken HlslTokenStream::popPreToken() +{ + assert(preTokenStackSize > 0); + + return preTokenStack[--preTokenStackSize]; +} + +void HlslTokenStream::pushTokenBuffer(const HlslToken& tok) +{ + tokenBuffer[tokenBufferPos] = tok; + tokenBufferPos = (tokenBufferPos+1) % tokenBufferSize; +} + +HlslToken HlslTokenStream::popTokenBuffer() +{ + // Back up + tokenBufferPos = (tokenBufferPos+tokenBufferSize-1) % tokenBufferSize; + + return tokenBuffer[tokenBufferPos]; +} + +// +// Make a new source of tokens, not from the source, but from an +// already pre-processed token stream. +// +// This interrupts current token processing which must be restored +// later. Some simplifying assumptions are made (and asserted). +// +void HlslTokenStream::pushTokenStream(const TVector* tokens) +{ + // not yet setup to interrupt a stream that has been receded + // and not yet reconsumed + assert(preTokenStackSize == 0); + + // save current state + currentTokenStack.push_back(token); + + // set up new token stream + tokenStreamStack.push_back(tokens); + + // start position at first token: + token = (*tokens)[0]; + tokenPosition.push_back(0); +} + +// Undo pushTokenStream(), see above +void HlslTokenStream::popTokenStream() +{ + tokenStreamStack.pop_back(); + tokenPosition.pop_back(); + token = currentTokenStack.back(); + currentTokenStack.pop_back(); +} + +// Load 'token' with the next token in the stream of tokens. +void HlslTokenStream::advanceToken() +{ + pushTokenBuffer(token); + if (preTokenStackSize > 0) + token = popPreToken(); + else { + if (tokenStreamStack.size() == 0) + scanner.tokenize(token); + else { + ++tokenPosition.back(); + if (tokenPosition.back() >= (int)tokenStreamStack.back()->size()) + token.tokenClass = EHTokNone; + else + token = (*tokenStreamStack.back())[tokenPosition.back()]; + } + } +} + +void HlslTokenStream::recedeToken() +{ + pushPreToken(token); + token = popTokenBuffer(); +} + +// Return the current token class. +EHlslTokenClass HlslTokenStream::peek() const +{ + return token.tokenClass; +} + +// Return true, without advancing to the next token, if the current token is +// the expected (passed in) token class. +bool HlslTokenStream::peekTokenClass(EHlslTokenClass tokenClass) const +{ + return peek() == tokenClass; +} + +// Return true and advance to the next token if the current token is the +// expected (passed in) token class. +bool HlslTokenStream::acceptTokenClass(EHlslTokenClass tokenClass) +{ + if (peekTokenClass(tokenClass)) { + advanceToken(); + return true; + } + + return false; +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/HLSL/hlslTokenStream.h b/third_party/glslang/glslang/HLSL/hlslTokenStream.h new file mode 100644 index 0000000..cb6c9e7 --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslTokenStream.h @@ -0,0 +1,96 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef HLSLTOKENSTREAM_H_ +#define HLSLTOKENSTREAM_H_ + +#include "hlslScanContext.h" + +namespace glslang { + + class HlslTokenStream { + public: + explicit HlslTokenStream(HlslScanContext& scanner) + : scanner(scanner), preTokenStackSize(0), tokenBufferPos(0) { } + virtual ~HlslTokenStream() { } + + public: + void advanceToken(); + void recedeToken(); + bool acceptTokenClass(EHlslTokenClass); + EHlslTokenClass peek() const; + bool peekTokenClass(EHlslTokenClass) const; + glslang::TBuiltInVariable mapSemantic(const char* upperCase) { return scanner.mapSemantic(upperCase); } + + void pushTokenStream(const TVector* tokens); + void popTokenStream(); + + protected: + HlslToken token; // the token we are currently looking at, but have not yet accepted + + private: + HlslTokenStream(); + HlslTokenStream& operator=(const HlslTokenStream&); + + HlslScanContext& scanner; // lexical scanner, to get next token from source file + TVector*> tokenStreamStack; // for getting the next token from an existing vector of tokens + TVector tokenPosition; + TVector currentTokenStack; + + // This is the number of tokens we can recedeToken() over. + static const int tokenBufferSize = 2; + + // Previously scanned tokens, returned for future advances, + // so logically in front of the token stream. + // Is logically a stack; needs last in last out semantics. + // Currently implemented as a stack of size 2. + HlslToken preTokenStack[tokenBufferSize]; + int preTokenStackSize; + void pushPreToken(const HlslToken&); + HlslToken popPreToken(); + + // Previously scanned tokens, not yet returned for future advances, + // but available for that. + // Is logically a fifo for normal advances, and a stack for recession. + // Currently implemented with an intrinsic size of 2. + HlslToken tokenBuffer[tokenBufferSize]; + int tokenBufferPos; + void pushTokenBuffer(const HlslToken&); + HlslToken popTokenBuffer(); + }; + +} // end namespace glslang + +#endif // HLSLTOKENSTREAM_H_ diff --git a/third_party/glslang/glslang/HLSL/hlslTokens.h b/third_party/glslang/glslang/HLSL/hlslTokens.h new file mode 100644 index 0000000..4426bcc --- /dev/null +++ b/third_party/glslang/glslang/HLSL/hlslTokens.h @@ -0,0 +1,374 @@ +// +// Copyright (C) 2016 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef EHLSLTOKENS_H_ +#define EHLSLTOKENS_H_ + +namespace glslang { + +enum EHlslTokenClass { + EHTokNone = 0, + + // qualifiers + EHTokStatic, + EHTokConst, + EHTokSNorm, + EHTokUnorm, + EHTokExtern, + EHTokUniform, + EHTokVolatile, + EHTokPrecise, + EHTokShared, + EHTokGroupShared, + EHTokLinear, + EHTokCentroid, + EHTokNointerpolation, + EHTokNoperspective, + EHTokSample, + EHTokRowMajor, + EHTokColumnMajor, + EHTokPackOffset, + EHTokIn, + EHTokOut, + EHTokInOut, + EHTokLayout, + EHTokGloballyCoherent, + EHTokInline, + + // primitive types + EHTokPoint, + EHTokLine, + EHTokTriangle, + EHTokLineAdj, + EHTokTriangleAdj, + + // stream out types + EHTokPointStream, + EHTokLineStream, + EHTokTriangleStream, + + // Tessellation patches + EHTokInputPatch, + EHTokOutputPatch, + + // template types + EHTokBuffer, + EHTokVector, + EHTokMatrix, + + // scalar types + EHTokVoid, + EHTokString, + EHTokBool, + EHTokInt, + EHTokUint, + EHTokUint64, + EHTokDword, + EHTokHalf, + EHTokFloat, + EHTokDouble, + EHTokMin16float, + EHTokMin10float, + EHTokMin16int, + EHTokMin12int, + EHTokMin16uint, + + // vector types + EHTokBool1, + EHTokBool2, + EHTokBool3, + EHTokBool4, + EHTokFloat1, + EHTokFloat2, + EHTokFloat3, + EHTokFloat4, + EHTokInt1, + EHTokInt2, + EHTokInt3, + EHTokInt4, + EHTokDouble1, + EHTokDouble2, + EHTokDouble3, + EHTokDouble4, + EHTokUint1, + EHTokUint2, + EHTokUint3, + EHTokUint4, + EHTokHalf1, + EHTokHalf2, + EHTokHalf3, + EHTokHalf4, + EHTokMin16float1, + EHTokMin16float2, + EHTokMin16float3, + EHTokMin16float4, + EHTokMin10float1, + EHTokMin10float2, + EHTokMin10float3, + EHTokMin10float4, + EHTokMin16int1, + EHTokMin16int2, + EHTokMin16int3, + EHTokMin16int4, + EHTokMin12int1, + EHTokMin12int2, + EHTokMin12int3, + EHTokMin12int4, + EHTokMin16uint1, + EHTokMin16uint2, + EHTokMin16uint3, + EHTokMin16uint4, + + // matrix types + EHTokInt1x1, + EHTokInt1x2, + EHTokInt1x3, + EHTokInt1x4, + EHTokInt2x1, + EHTokInt2x2, + EHTokInt2x3, + EHTokInt2x4, + EHTokInt3x1, + EHTokInt3x2, + EHTokInt3x3, + EHTokInt3x4, + EHTokInt4x1, + EHTokInt4x2, + EHTokInt4x3, + EHTokInt4x4, + EHTokUint1x1, + EHTokUint1x2, + EHTokUint1x3, + EHTokUint1x4, + EHTokUint2x1, + EHTokUint2x2, + EHTokUint2x3, + EHTokUint2x4, + EHTokUint3x1, + EHTokUint3x2, + EHTokUint3x3, + EHTokUint3x4, + EHTokUint4x1, + EHTokUint4x2, + EHTokUint4x3, + EHTokUint4x4, + EHTokBool1x1, + EHTokBool1x2, + EHTokBool1x3, + EHTokBool1x4, + EHTokBool2x1, + EHTokBool2x2, + EHTokBool2x3, + EHTokBool2x4, + EHTokBool3x1, + EHTokBool3x2, + EHTokBool3x3, + EHTokBool3x4, + EHTokBool4x1, + EHTokBool4x2, + EHTokBool4x3, + EHTokBool4x4, + EHTokFloat1x1, + EHTokFloat1x2, + EHTokFloat1x3, + EHTokFloat1x4, + EHTokFloat2x1, + EHTokFloat2x2, + EHTokFloat2x3, + EHTokFloat2x4, + EHTokFloat3x1, + EHTokFloat3x2, + EHTokFloat3x3, + EHTokFloat3x4, + EHTokFloat4x1, + EHTokFloat4x2, + EHTokFloat4x3, + EHTokFloat4x4, + EHTokHalf1x1, + EHTokHalf1x2, + EHTokHalf1x3, + EHTokHalf1x4, + EHTokHalf2x1, + EHTokHalf2x2, + EHTokHalf2x3, + EHTokHalf2x4, + EHTokHalf3x1, + EHTokHalf3x2, + EHTokHalf3x3, + EHTokHalf3x4, + EHTokHalf4x1, + EHTokHalf4x2, + EHTokHalf4x3, + EHTokHalf4x4, + EHTokDouble1x1, + EHTokDouble1x2, + EHTokDouble1x3, + EHTokDouble1x4, + EHTokDouble2x1, + EHTokDouble2x2, + EHTokDouble2x3, + EHTokDouble2x4, + EHTokDouble3x1, + EHTokDouble3x2, + EHTokDouble3x3, + EHTokDouble3x4, + EHTokDouble4x1, + EHTokDouble4x2, + EHTokDouble4x3, + EHTokDouble4x4, + + // texturing types + EHTokSampler, + EHTokSampler1d, + EHTokSampler2d, + EHTokSampler3d, + EHTokSamplerCube, + EHTokSamplerState, + EHTokSamplerComparisonState, + EHTokTexture, + EHTokTexture1d, + EHTokTexture1darray, + EHTokTexture2d, + EHTokTexture2darray, + EHTokTexture3d, + EHTokTextureCube, + EHTokTextureCubearray, + EHTokTexture2DMS, + EHTokTexture2DMSarray, + EHTokRWTexture1d, + EHTokRWTexture1darray, + EHTokRWTexture2d, + EHTokRWTexture2darray, + EHTokRWTexture3d, + EHTokRWBuffer, + EHTokSubpassInput, + EHTokSubpassInputMS, + + // Structure buffer variants + EHTokAppendStructuredBuffer, + EHTokByteAddressBuffer, + EHTokConsumeStructuredBuffer, + EHTokRWByteAddressBuffer, + EHTokRWStructuredBuffer, + EHTokStructuredBuffer, + EHTokTextureBuffer, + + // variable, user type, ... + EHTokIdentifier, + EHTokClass, + EHTokStruct, + EHTokCBuffer, + EHTokTBuffer, + EHTokTypedef, + EHTokThis, + EHTokNamespace, + EHTokConstantBuffer, + + // constant + EHTokFloat16Constant, + EHTokFloatConstant, + EHTokDoubleConstant, + EHTokIntConstant, + EHTokUintConstant, + EHTokBoolConstant, + EHTokStringConstant, + + // control flow + EHTokFor, + EHTokDo, + EHTokWhile, + EHTokBreak, + EHTokContinue, + EHTokIf, + EHTokElse, + EHTokDiscard, + EHTokReturn, + EHTokSwitch, + EHTokCase, + EHTokDefault, + + // expressions + EHTokLeftOp, + EHTokRightOp, + EHTokIncOp, + EHTokDecOp, + EHTokLeOp, + EHTokGeOp, + EHTokEqOp, + EHTokNeOp, + EHTokAndOp, + EHTokOrOp, + EHTokXorOp, + EHTokAssign, + EHTokMulAssign, + EHTokDivAssign, + EHTokAddAssign, + EHTokModAssign, + EHTokLeftAssign, + EHTokRightAssign, + EHTokAndAssign, + EHTokXorAssign, + EHTokOrAssign, + EHTokSubAssign, + EHTokLeftParen, + EHTokRightParen, + EHTokLeftBracket, + EHTokRightBracket, + EHTokLeftBrace, + EHTokRightBrace, + EHTokDot, + EHTokComma, + EHTokColon, + EHTokColonColon, + EHTokSemicolon, + EHTokBang, + EHTokDash, + EHTokTilde, + EHTokPlus, + EHTokStar, + EHTokSlash, + EHTokPercent, + EHTokLeftAngle, + EHTokRightAngle, + EHTokVerticalBar, + EHTokCaret, + EHTokAmpersand, + EHTokQuestion, +}; + +} // end namespace glslang + +#endif // EHLSLTOKENS_H_ diff --git a/third_party/glslang/glslang/HLSL/pch.h b/third_party/glslang/glslang/HLSL/pch.h new file mode 100644 index 0000000..465e7c1 --- /dev/null +++ b/third_party/glslang/glslang/HLSL/pch.h @@ -0,0 +1,53 @@ +#ifndef _PCH_H +#define _PCH_H +// +// Copyright (C) 2018 The Khronos Group Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "hlslAttributes.h" +#include "hlslGrammar.h" +#include "hlslParseHelper.h" +#include "hlslScanContext.h" + +#include "../MachineIndependent/Scan.h" +#include "../MachineIndependent/preprocessor/PpContext.h" + +#include "../OSDependent/osinclude.h" + +#include +#include +#include +#include +#include + +#endif /* _PCH_H */ diff --git a/third_party/glslang/glslang/Include/BaseTypes.h b/third_party/glslang/glslang/Include/BaseTypes.h new file mode 100644 index 0000000..55bdd25 --- /dev/null +++ b/third_party/glslang/glslang/Include/BaseTypes.h @@ -0,0 +1,577 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _BASICTYPES_INCLUDED_ +#define _BASICTYPES_INCLUDED_ + +namespace glslang { + +// +// Basic type. Arrays, vectors, sampler details, etc., are orthogonal to this. +// +enum TBasicType { + EbtVoid, + EbtFloat, + EbtDouble, + EbtFloat16, + EbtInt8, + EbtUint8, + EbtInt16, + EbtUint16, + EbtInt, + EbtUint, + EbtInt64, + EbtUint64, + EbtBool, + EbtAtomicUint, + EbtSampler, + EbtStruct, + EbtBlock, + EbtAccStruct, + EbtReference, + EbtRayQuery, + + // HLSL types that live only temporarily. + EbtString, + + EbtNumTypes +}; + +// +// Storage qualifiers. Should align with different kinds of storage or +// resource or GLSL storage qualifier. Expansion is deprecated. +// +// N.B.: You probably DON'T want to add anything here, but rather just add it +// to the built-in variables. See the comment above TBuiltInVariable. +// +// A new built-in variable will normally be an existing qualifier, like 'in', 'out', etc. +// DO NOT follow the design pattern of, say EvqInstanceId, etc. +// +enum TStorageQualifier { + EvqTemporary, // For temporaries (within a function), read/write + EvqGlobal, // For globals read/write + EvqConst, // User-defined constant values, will be semantically constant and constant folded + EvqVaryingIn, // pipeline input, read only, also supercategory for all built-ins not included in this enum (see TBuiltInVariable) + EvqVaryingOut, // pipeline output, read/write, also supercategory for all built-ins not included in this enum (see TBuiltInVariable) + EvqUniform, // read only, shared with app + EvqBuffer, // read/write, shared with app + EvqShared, // compute shader's read/write 'shared' qualifier + + EvqPayload, + EvqPayloadIn, + EvqHitAttr, + EvqCallableData, + EvqCallableDataIn, + + // parameters + EvqIn, // also, for 'in' in the grammar before we know if it's a pipeline input or an 'in' parameter + EvqOut, // also, for 'out' in the grammar before we know if it's a pipeline output or an 'out' parameter + EvqInOut, + EvqConstReadOnly, // input; also other read-only types having neither a constant value nor constant-value semantics + + // built-ins read by vertex shader + EvqVertexId, + EvqInstanceId, + + // built-ins written by vertex shader + EvqPosition, + EvqPointSize, + EvqClipVertex, + + // built-ins read by fragment shader + EvqFace, + EvqFragCoord, + EvqPointCoord, + + // built-ins written by fragment shader + EvqFragColor, + EvqFragDepth, + + // end of list + EvqLast +}; + +// +// Subcategories of the TStorageQualifier, simply to give a direct mapping +// between built-in variable names and an numerical value (the enum). +// +// For backward compatibility, there is some redundancy between the +// TStorageQualifier and these. Existing members should both be maintained accurately. +// However, any new built-in variable (and any existing non-redundant one) +// must follow the pattern that the specific built-in is here, and only its +// general qualifier is in TStorageQualifier. +// +// Something like gl_Position, which is sometimes 'in' and sometimes 'out' +// shows up as two different built-in variables in a single stage, but +// only has a single enum in TBuiltInVariable, so both the +// TStorageQualifier and the TBuitinVariable are needed to distinguish +// between them. +// +enum TBuiltInVariable { + EbvNone, + EbvNumWorkGroups, + EbvWorkGroupSize, + EbvWorkGroupId, + EbvLocalInvocationId, + EbvGlobalInvocationId, + EbvLocalInvocationIndex, + EbvNumSubgroups, + EbvSubgroupID, + EbvSubGroupSize, + EbvSubGroupInvocation, + EbvSubGroupEqMask, + EbvSubGroupGeMask, + EbvSubGroupGtMask, + EbvSubGroupLeMask, + EbvSubGroupLtMask, + EbvSubgroupSize2, + EbvSubgroupInvocation2, + EbvSubgroupEqMask2, + EbvSubgroupGeMask2, + EbvSubgroupGtMask2, + EbvSubgroupLeMask2, + EbvSubgroupLtMask2, + EbvVertexId, + EbvInstanceId, + EbvVertexIndex, + EbvInstanceIndex, + EbvBaseVertex, + EbvBaseInstance, + EbvDrawId, + EbvPosition, + EbvPointSize, + EbvClipVertex, + EbvClipDistance, + EbvCullDistance, + EbvNormal, + EbvVertex, + EbvMultiTexCoord0, + EbvMultiTexCoord1, + EbvMultiTexCoord2, + EbvMultiTexCoord3, + EbvMultiTexCoord4, + EbvMultiTexCoord5, + EbvMultiTexCoord6, + EbvMultiTexCoord7, + EbvFrontColor, + EbvBackColor, + EbvFrontSecondaryColor, + EbvBackSecondaryColor, + EbvTexCoord, + EbvFogFragCoord, + EbvInvocationId, + EbvPrimitiveId, + EbvLayer, + EbvViewportIndex, + EbvPatchVertices, + EbvTessLevelOuter, + EbvTessLevelInner, + EbvBoundingBox, + EbvTessCoord, + EbvColor, + EbvSecondaryColor, + EbvFace, + EbvFragCoord, + EbvPointCoord, + EbvFragColor, + EbvFragData, + EbvFragDepth, + EbvFragStencilRef, + EbvSampleId, + EbvSamplePosition, + EbvSampleMask, + EbvHelperInvocation, + + EbvBaryCoordNoPersp, + EbvBaryCoordNoPerspCentroid, + EbvBaryCoordNoPerspSample, + EbvBaryCoordSmooth, + EbvBaryCoordSmoothCentroid, + EbvBaryCoordSmoothSample, + EbvBaryCoordPullModel, + + EbvViewIndex, + EbvDeviceIndex, + + EbvShadingRateKHR, + EbvPrimitiveShadingRateKHR, + + EbvFragSizeEXT, + EbvFragInvocationCountEXT, + + EbvSecondaryFragDataEXT, + EbvSecondaryFragColorEXT, + + EbvViewportMaskNV, + EbvSecondaryPositionNV, + EbvSecondaryViewportMaskNV, + EbvPositionPerViewNV, + EbvViewportMaskPerViewNV, + EbvFragFullyCoveredNV, + EbvFragmentSizeNV, + EbvInvocationsPerPixelNV, + // ray tracing + EbvLaunchId, + EbvLaunchSize, + EbvInstanceCustomIndex, + EbvGeometryIndex, + EbvWorldRayOrigin, + EbvWorldRayDirection, + EbvObjectRayOrigin, + EbvObjectRayDirection, + EbvRayTmin, + EbvRayTmax, + EbvHitT, + EbvHitKind, + EbvObjectToWorld, + EbvObjectToWorld3x4, + EbvWorldToObject, + EbvWorldToObject3x4, + EbvIncomingRayFlags, + // barycentrics + EbvBaryCoordNV, + EbvBaryCoordNoPerspNV, + // mesh shaders + EbvTaskCountNV, + EbvPrimitiveCountNV, + EbvPrimitiveIndicesNV, + EbvClipDistancePerViewNV, + EbvCullDistancePerViewNV, + EbvLayerPerViewNV, + EbvMeshViewCountNV, + EbvMeshViewIndicesNV, + + // sm builtins + EbvWarpsPerSM, + EbvSMCount, + EbvWarpID, + EbvSMID, + + // HLSL built-ins that live only temporarily, until they get remapped + // to one of the above. + EbvFragDepthGreater, + EbvFragDepthLesser, + EbvGsOutputStream, + EbvOutputPatch, + EbvInputPatch, + + // structbuffer types + EbvAppendConsume, // no need to differentiate append and consume + EbvRWStructuredBuffer, + EbvStructuredBuffer, + EbvByteAddressBuffer, + EbvRWByteAddressBuffer, + + EbvLast +}; + +// In this enum, order matters; users can assume higher precision is a bigger value +// and EpqNone is 0. +enum TPrecisionQualifier { + EpqNone = 0, + EpqLow, + EpqMedium, + EpqHigh +}; + +#ifdef GLSLANG_WEB +__inline const char* GetStorageQualifierString(TStorageQualifier q) { return ""; } +__inline const char* GetPrecisionQualifierString(TPrecisionQualifier p) { return ""; } +#else +// These will show up in error messages +__inline const char* GetStorageQualifierString(TStorageQualifier q) +{ + switch (q) { + case EvqTemporary: return "temp"; break; + case EvqGlobal: return "global"; break; + case EvqConst: return "const"; break; + case EvqConstReadOnly: return "const (read only)"; break; + case EvqVaryingIn: return "in"; break; + case EvqVaryingOut: return "out"; break; + case EvqUniform: return "uniform"; break; + case EvqBuffer: return "buffer"; break; + case EvqShared: return "shared"; break; + case EvqIn: return "in"; break; + case EvqOut: return "out"; break; + case EvqInOut: return "inout"; break; + case EvqVertexId: return "gl_VertexId"; break; + case EvqInstanceId: return "gl_InstanceId"; break; + case EvqPosition: return "gl_Position"; break; + case EvqPointSize: return "gl_PointSize"; break; + case EvqClipVertex: return "gl_ClipVertex"; break; + case EvqFace: return "gl_FrontFacing"; break; + case EvqFragCoord: return "gl_FragCoord"; break; + case EvqPointCoord: return "gl_PointCoord"; break; + case EvqFragColor: return "fragColor"; break; + case EvqFragDepth: return "gl_FragDepth"; break; + case EvqPayload: return "rayPayloadNV"; break; + case EvqPayloadIn: return "rayPayloadInNV"; break; + case EvqHitAttr: return "hitAttributeNV"; break; + case EvqCallableData: return "callableDataNV"; break; + case EvqCallableDataIn: return "callableDataInNV"; break; + default: return "unknown qualifier"; + } +} + +__inline const char* GetBuiltInVariableString(TBuiltInVariable v) +{ + switch (v) { + case EbvNone: return ""; + case EbvNumWorkGroups: return "NumWorkGroups"; + case EbvWorkGroupSize: return "WorkGroupSize"; + case EbvWorkGroupId: return "WorkGroupID"; + case EbvLocalInvocationId: return "LocalInvocationID"; + case EbvGlobalInvocationId: return "GlobalInvocationID"; + case EbvLocalInvocationIndex: return "LocalInvocationIndex"; + case EbvNumSubgroups: return "NumSubgroups"; + case EbvSubgroupID: return "SubgroupID"; + case EbvSubGroupSize: return "SubGroupSize"; + case EbvSubGroupInvocation: return "SubGroupInvocation"; + case EbvSubGroupEqMask: return "SubGroupEqMask"; + case EbvSubGroupGeMask: return "SubGroupGeMask"; + case EbvSubGroupGtMask: return "SubGroupGtMask"; + case EbvSubGroupLeMask: return "SubGroupLeMask"; + case EbvSubGroupLtMask: return "SubGroupLtMask"; + case EbvSubgroupSize2: return "SubgroupSize"; + case EbvSubgroupInvocation2: return "SubgroupInvocationID"; + case EbvSubgroupEqMask2: return "SubgroupEqMask"; + case EbvSubgroupGeMask2: return "SubgroupGeMask"; + case EbvSubgroupGtMask2: return "SubgroupGtMask"; + case EbvSubgroupLeMask2: return "SubgroupLeMask"; + case EbvSubgroupLtMask2: return "SubgroupLtMask"; + case EbvVertexId: return "VertexId"; + case EbvInstanceId: return "InstanceId"; + case EbvVertexIndex: return "VertexIndex"; + case EbvInstanceIndex: return "InstanceIndex"; + case EbvBaseVertex: return "BaseVertex"; + case EbvBaseInstance: return "BaseInstance"; + case EbvDrawId: return "DrawId"; + case EbvPosition: return "Position"; + case EbvPointSize: return "PointSize"; + case EbvClipVertex: return "ClipVertex"; + case EbvClipDistance: return "ClipDistance"; + case EbvCullDistance: return "CullDistance"; + case EbvNormal: return "Normal"; + case EbvVertex: return "Vertex"; + case EbvMultiTexCoord0: return "MultiTexCoord0"; + case EbvMultiTexCoord1: return "MultiTexCoord1"; + case EbvMultiTexCoord2: return "MultiTexCoord2"; + case EbvMultiTexCoord3: return "MultiTexCoord3"; + case EbvMultiTexCoord4: return "MultiTexCoord4"; + case EbvMultiTexCoord5: return "MultiTexCoord5"; + case EbvMultiTexCoord6: return "MultiTexCoord6"; + case EbvMultiTexCoord7: return "MultiTexCoord7"; + case EbvFrontColor: return "FrontColor"; + case EbvBackColor: return "BackColor"; + case EbvFrontSecondaryColor: return "FrontSecondaryColor"; + case EbvBackSecondaryColor: return "BackSecondaryColor"; + case EbvTexCoord: return "TexCoord"; + case EbvFogFragCoord: return "FogFragCoord"; + case EbvInvocationId: return "InvocationID"; + case EbvPrimitiveId: return "PrimitiveID"; + case EbvLayer: return "Layer"; + case EbvViewportIndex: return "ViewportIndex"; + case EbvPatchVertices: return "PatchVertices"; + case EbvTessLevelOuter: return "TessLevelOuter"; + case EbvTessLevelInner: return "TessLevelInner"; + case EbvBoundingBox: return "BoundingBox"; + case EbvTessCoord: return "TessCoord"; + case EbvColor: return "Color"; + case EbvSecondaryColor: return "SecondaryColor"; + case EbvFace: return "Face"; + case EbvFragCoord: return "FragCoord"; + case EbvPointCoord: return "PointCoord"; + case EbvFragColor: return "FragColor"; + case EbvFragData: return "FragData"; + case EbvFragDepth: return "FragDepth"; + case EbvFragStencilRef: return "FragStencilRef"; + case EbvSampleId: return "SampleId"; + case EbvSamplePosition: return "SamplePosition"; + case EbvSampleMask: return "SampleMaskIn"; + case EbvHelperInvocation: return "HelperInvocation"; + + case EbvBaryCoordNoPersp: return "BaryCoordNoPersp"; + case EbvBaryCoordNoPerspCentroid: return "BaryCoordNoPerspCentroid"; + case EbvBaryCoordNoPerspSample: return "BaryCoordNoPerspSample"; + case EbvBaryCoordSmooth: return "BaryCoordSmooth"; + case EbvBaryCoordSmoothCentroid: return "BaryCoordSmoothCentroid"; + case EbvBaryCoordSmoothSample: return "BaryCoordSmoothSample"; + case EbvBaryCoordPullModel: return "BaryCoordPullModel"; + + case EbvViewIndex: return "ViewIndex"; + case EbvDeviceIndex: return "DeviceIndex"; + + case EbvFragSizeEXT: return "FragSizeEXT"; + case EbvFragInvocationCountEXT: return "FragInvocationCountEXT"; + + case EbvSecondaryFragDataEXT: return "SecondaryFragDataEXT"; + case EbvSecondaryFragColorEXT: return "SecondaryFragColorEXT"; + + case EbvViewportMaskNV: return "ViewportMaskNV"; + case EbvSecondaryPositionNV: return "SecondaryPositionNV"; + case EbvSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case EbvPositionPerViewNV: return "PositionPerViewNV"; + case EbvViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case EbvFragFullyCoveredNV: return "FragFullyCoveredNV"; + case EbvFragmentSizeNV: return "FragmentSizeNV"; + case EbvInvocationsPerPixelNV: return "InvocationsPerPixelNV"; + case EbvLaunchId: return "LaunchIdNV"; + case EbvLaunchSize: return "LaunchSizeNV"; + case EbvInstanceCustomIndex: return "InstanceCustomIndexNV"; + case EbvGeometryIndex: return "GeometryIndexEXT"; + case EbvWorldRayOrigin: return "WorldRayOriginNV"; + case EbvWorldRayDirection: return "WorldRayDirectionNV"; + case EbvObjectRayOrigin: return "ObjectRayOriginNV"; + case EbvObjectRayDirection: return "ObjectRayDirectionNV"; + case EbvRayTmin: return "ObjectRayTminNV"; + case EbvRayTmax: return "ObjectRayTmaxNV"; + case EbvHitT: return "HitTNV"; + case EbvHitKind: return "HitKindNV"; + case EbvIncomingRayFlags: return "IncomingRayFlagsNV"; + case EbvObjectToWorld: return "ObjectToWorldNV"; + case EbvWorldToObject: return "WorldToObjectNV"; + + case EbvBaryCoordNV: return "BaryCoordNV"; + case EbvBaryCoordNoPerspNV: return "BaryCoordNoPerspNV"; + + case EbvTaskCountNV: return "TaskCountNV"; + case EbvPrimitiveCountNV: return "PrimitiveCountNV"; + case EbvPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case EbvClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case EbvCullDistancePerViewNV: return "CullDistancePerViewNV"; + case EbvLayerPerViewNV: return "LayerPerViewNV"; + case EbvMeshViewCountNV: return "MeshViewCountNV"; + case EbvMeshViewIndicesNV: return "MeshViewIndicesNV"; + + case EbvWarpsPerSM: return "WarpsPerSMNV"; + case EbvSMCount: return "SMCountNV"; + case EbvWarpID: return "WarpIDNV"; + case EbvSMID: return "SMIDNV"; + + case EbvShadingRateKHR: return "ShadingRateKHR"; + case EbvPrimitiveShadingRateKHR: return "PrimitiveShadingRateKHR"; + + default: return "unknown built-in variable"; + } +} + +__inline const char* GetPrecisionQualifierString(TPrecisionQualifier p) +{ + switch (p) { + case EpqNone: return ""; break; + case EpqLow: return "lowp"; break; + case EpqMedium: return "mediump"; break; + case EpqHigh: return "highp"; break; + default: return "unknown precision qualifier"; + } +} +#endif + +__inline bool isTypeSignedInt(TBasicType type) +{ + switch (type) { + case EbtInt8: + case EbtInt16: + case EbtInt: + case EbtInt64: + return true; + default: + return false; + } +} + +__inline bool isTypeUnsignedInt(TBasicType type) +{ + switch (type) { + case EbtUint8: + case EbtUint16: + case EbtUint: + case EbtUint64: + return true; + default: + return false; + } +} + +__inline bool isTypeInt(TBasicType type) +{ + return isTypeSignedInt(type) || isTypeUnsignedInt(type); +} + +__inline bool isTypeFloat(TBasicType type) +{ + switch (type) { + case EbtFloat: + case EbtDouble: + case EbtFloat16: + return true; + default: + return false; + } +} + +__inline int getTypeRank(TBasicType type) +{ + int res = -1; + switch(type) { + case EbtInt8: + case EbtUint8: + res = 0; + break; + case EbtInt16: + case EbtUint16: + res = 1; + break; + case EbtInt: + case EbtUint: + res = 2; + break; + case EbtInt64: + case EbtUint64: + res = 3; + break; + default: + assert(false); + break; + } + return res; +} + +} // end namespace glslang + +#endif // _BASICTYPES_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/Common.h b/third_party/glslang/glslang/Include/Common.h new file mode 100644 index 0000000..b628cdc --- /dev/null +++ b/third_party/glslang/glslang/Include/Common.h @@ -0,0 +1,291 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _COMMON_INCLUDED_ +#define _COMMON_INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__ANDROID__) || (defined(_MSC_VER) && _MSC_VER < 1700) +#include +namespace std { +template +std::string to_string(const T& val) { + std::ostringstream os; + os << val; + return os.str(); +} +} +#endif + +#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) || defined MINGW_HAS_SECURE_API + #include + #ifndef snprintf + #define snprintf sprintf_s + #endif + #define safe_vsprintf(buf,max,format,args) vsnprintf_s((buf), (max), (max), (format), (args)) +#elif defined (solaris) + #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args)) + #include + #define UINT_PTR uintptr_t +#else + #define safe_vsprintf(buf,max,format,args) vsnprintf((buf), (max), (format), (args)) + #include + #define UINT_PTR uintptr_t +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1800 + #include + inline long long int strtoll (const char* str, char** endptr, int base) + { + return _strtoi64(str, endptr, base); + } + inline unsigned long long int strtoull (const char* str, char** endptr, int base) + { + return _strtoui64(str, endptr, base); + } + inline long long int atoll (const char* str) + { + return strtoll(str, NULL, 10); + } +#endif + +#if defined(_MSC_VER) +#define strdup _strdup +#endif + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4786) // Don't warn about too long identifiers + #pragma warning(disable : 4514) // unused inline method + #pragma warning(disable : 4201) // nameless union +#endif + +#include "PoolAlloc.h" + +// +// Put POOL_ALLOCATOR_NEW_DELETE in base classes to make them use this scheme. +// +#define POOL_ALLOCATOR_NEW_DELETE(A) \ + void* operator new(size_t s) { return (A).allocate(s); } \ + void* operator new(size_t, void *_Where) { return (_Where); } \ + void operator delete(void*) { } \ + void operator delete(void *, void *) { } \ + void* operator new[](size_t s) { return (A).allocate(s); } \ + void* operator new[](size_t, void *_Where) { return (_Where); } \ + void operator delete[](void*) { } \ + void operator delete[](void *, void *) { } + +namespace glslang { + + // + // Pool version of string. + // + typedef pool_allocator TStringAllocator; + typedef std::basic_string , TStringAllocator> TString; + +} // end namespace glslang + +// Repackage the std::hash for use by unordered map/set with a TString key. +namespace std { + + template<> struct hash { + std::size_t operator()(const glslang::TString& s) const + { + const unsigned _FNV_offset_basis = 2166136261U; + const unsigned _FNV_prime = 16777619U; + unsigned _Val = _FNV_offset_basis; + size_t _Count = s.size(); + const char* _First = s.c_str(); + for (size_t _Next = 0; _Next < _Count; ++_Next) + { + _Val ^= (unsigned)_First[_Next]; + _Val *= _FNV_prime; + } + + return _Val; + } + }; +} + +namespace glslang { + +inline TString* NewPoolTString(const char* s) +{ + void* memory = GetThreadPoolAllocator().allocate(sizeof(TString)); + return new(memory) TString(s); +} + +template inline T* NewPoolObject(T*) +{ + return new(GetThreadPoolAllocator().allocate(sizeof(T))) T; +} + +template inline T* NewPoolObject(T, int instances) +{ + return new(GetThreadPoolAllocator().allocate(instances * sizeof(T))) T[instances]; +} + +// +// Pool allocator versions of vectors, lists, and maps +// +template class TVector : public std::vector > { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + typedef typename std::vector >::size_type size_type; + TVector() : std::vector >() {} + TVector(const pool_allocator& a) : std::vector >(a) {} + TVector(size_type i) : std::vector >(i) {} + TVector(size_type i, const T& val) : std::vector >(i, val) {} +}; + +template class TList : public std::list > { +}; + +template > +class TMap : public std::map > > { +}; + +template , class PRED = std::equal_to > +class TUnorderedMap : public std::unordered_map > > { +}; + +// +// Persistent string memory. Should only be used for strings that survive +// across compiles/links. +// +typedef std::basic_string TPersistString; + +// +// templatized min and max functions. +// +template T Min(const T a, const T b) { return a < b ? a : b; } +template T Max(const T a, const T b) { return a > b ? a : b; } + +// +// Create a TString object from an integer. +// +#if defined _MSC_VER || defined MINGW_HAS_SECURE_API +inline const TString String(const int i, const int base = 10) +{ + char text[16]; // 32 bit ints are at most 10 digits in base 10 + _itoa_s(i, text, sizeof(text), base); + return text; +} +#else +inline const TString String(const int i, const int /*base*/ = 10) +{ + char text[16]; // 32 bit ints are at most 10 digits in base 10 + + // we assume base 10 for all cases + snprintf(text, sizeof(text), "%d", i); + + return text; +} +#endif + +struct TSourceLoc { + void init() + { + name = nullptr; string = 0; line = 0; column = 0; + } + void init(int stringNum) { init(); string = stringNum; } + // Returns the name if it exists. Otherwise, returns the string number. + std::string getStringNameOrNum(bool quoteStringName = true) const + { + if (name != nullptr) { + TString qstr = quoteStringName ? ("\"" + *name + "\"") : *name; + std::string ret_str(qstr.c_str()); + return ret_str; + } + return std::to_string((long long)string); + } + const char* getFilename() const + { + if (name == nullptr) + return nullptr; + return name->c_str(); + } + const char* getFilenameStr() const { return name == nullptr ? "" : name->c_str(); } + TString* name; // descriptive name for this string, when a textual name is available, otherwise nullptr + int string; + int line; + int column; +}; + +class TPragmaTable : public TMap { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) +}; + +const int MaxTokenLength = 1024; + +template bool IsPow2(T powerOf2) +{ + if (powerOf2 <= 0) + return false; + + return (powerOf2 & (powerOf2 - 1)) == 0; +} + +// Round number up to a multiple of the given powerOf2, which is not +// a power, just a number that must be a power of 2. +template void RoundToPow2(T& number, int powerOf2) +{ + assert(IsPow2(powerOf2)); + number = (number + powerOf2 - 1) & ~(powerOf2 - 1); +} + +template bool IsMultipleOfPow2(T number, int powerOf2) +{ + assert(IsPow2(powerOf2)); + return ! (number & (powerOf2 - 1)); +} + +} // end namespace glslang + +#endif // _COMMON_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/ConstantUnion.h b/third_party/glslang/glslang/Include/ConstantUnion.h new file mode 100644 index 0000000..c4ffb85 --- /dev/null +++ b/third_party/glslang/glslang/Include/ConstantUnion.h @@ -0,0 +1,974 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _CONSTANT_UNION_INCLUDED_ +#define _CONSTANT_UNION_INCLUDED_ + +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" + +namespace glslang { + +class TConstUnion { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TConstUnion() : iConst(0), type(EbtInt) { } + + void setI8Const(signed char i) + { + i8Const = i; + type = EbtInt8; + } + + void setU8Const(unsigned char u) + { + u8Const = u; + type = EbtUint8; + } + + void setI16Const(signed short i) + { + i16Const = i; + type = EbtInt16; + } + + void setU16Const(unsigned short u) + { + u16Const = u; + type = EbtUint16; + } + + void setIConst(int i) + { + iConst = i; + type = EbtInt; + } + + void setUConst(unsigned int u) + { + uConst = u; + type = EbtUint; + } + + void setI64Const(long long i64) + { + i64Const = i64; + type = EbtInt64; + } + + void setU64Const(unsigned long long u64) + { + u64Const = u64; + type = EbtUint64; + } + + void setDConst(double d) + { + dConst = d; + type = EbtDouble; + } + + void setBConst(bool b) + { + bConst = b; + type = EbtBool; + } + + void setSConst(const TString* s) + { + sConst = s; + type = EbtString; + } + + signed char getI8Const() const { return i8Const; } + unsigned char getU8Const() const { return u8Const; } + signed short getI16Const() const { return i16Const; } + unsigned short getU16Const() const { return u16Const; } + int getIConst() const { return iConst; } + unsigned int getUConst() const { return uConst; } + long long getI64Const() const { return i64Const; } + unsigned long long getU64Const() const { return u64Const; } + double getDConst() const { return dConst; } + bool getBConst() const { return bConst; } + const TString* getSConst() const { return sConst; } + + bool operator==(const signed char i) const + { + if (i == i8Const) + return true; + + return false; + } + + bool operator==(const unsigned char u) const + { + if (u == u8Const) + return true; + + return false; + } + + bool operator==(const signed short i) const + { + if (i == i16Const) + return true; + + return false; + } + + bool operator==(const unsigned short u) const + { + if (u == u16Const) + return true; + + return false; + } + + bool operator==(const int i) const + { + if (i == iConst) + return true; + + return false; + } + + bool operator==(const unsigned int u) const + { + if (u == uConst) + return true; + + return false; + } + + bool operator==(const long long i64) const + { + if (i64 == i64Const) + return true; + + return false; + } + + bool operator==(const unsigned long long u64) const + { + if (u64 == u64Const) + return true; + + return false; + } + + bool operator==(const double d) const + { + if (d == dConst) + return true; + + return false; + } + + bool operator==(const bool b) const + { + if (b == bConst) + return true; + + return false; + } + + bool operator==(const TConstUnion& constant) const + { + if (constant.type != type) + return false; + + switch (type) { + case EbtInt: + if (constant.iConst == iConst) + return true; + + break; + case EbtUint: + if (constant.uConst == uConst) + return true; + + break; + case EbtBool: + if (constant.bConst == bConst) + return true; + + break; + case EbtDouble: + if (constant.dConst == dConst) + return true; + + break; + +#ifndef GLSLANG_WEB + case EbtInt16: + if (constant.i16Const == i16Const) + return true; + + break; + case EbtUint16: + if (constant.u16Const == u16Const) + return true; + + break; + case EbtInt8: + if (constant.i8Const == i8Const) + return true; + + break; + case EbtUint8: + if (constant.u8Const == u8Const) + return true; + + break; + case EbtInt64: + if (constant.i64Const == i64Const) + return true; + + break; + case EbtUint64: + if (constant.u64Const == u64Const) + return true; + + break; +#endif + default: + assert(false && "Default missing"); + } + + return false; + } + + bool operator!=(const signed char i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned char u) const + { + return !operator==(u); + } + + bool operator!=(const signed short i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned short u) const + { + return !operator==(u); + } + + bool operator!=(const int i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned int u) const + { + return !operator==(u); + } + + bool operator!=(const long long i) const + { + return !operator==(i); + } + + bool operator!=(const unsigned long long u) const + { + return !operator==(u); + } + + bool operator!=(const float f) const + { + return !operator==(f); + } + + bool operator!=(const bool b) const + { + return !operator==(b); + } + + bool operator!=(const TConstUnion& constant) const + { + return !operator==(constant); + } + + bool operator>(const TConstUnion& constant) const + { + assert(type == constant.type); + switch (type) { + case EbtInt: + if (iConst > constant.iConst) + return true; + + return false; + case EbtUint: + if (uConst > constant.uConst) + return true; + + return false; + case EbtDouble: + if (dConst > constant.dConst) + return true; + + return false; +#ifndef GLSLANG_WEB + case EbtInt8: + if (i8Const > constant.i8Const) + return true; + + return false; + case EbtUint8: + if (u8Const > constant.u8Const) + return true; + + return false; + case EbtInt16: + if (i16Const > constant.i16Const) + return true; + + return false; + case EbtUint16: + if (u16Const > constant.u16Const) + return true; + + return false; + case EbtInt64: + if (i64Const > constant.i64Const) + return true; + + return false; + case EbtUint64: + if (u64Const > constant.u64Const) + return true; + + return false; +#endif + default: + assert(false && "Default missing"); + return false; + } + } + + bool operator<(const TConstUnion& constant) const + { + assert(type == constant.type); + switch (type) { +#ifndef GLSLANG_WEB + case EbtInt8: + if (i8Const < constant.i8Const) + return true; + + return false; + case EbtUint8: + if (u8Const < constant.u8Const) + return true; + + return false; + case EbtInt16: + if (i16Const < constant.i16Const) + return true; + + return false; + case EbtUint16: + if (u16Const < constant.u16Const) + return true; + return false; + case EbtInt64: + if (i64Const < constant.i64Const) + return true; + + return false; + case EbtUint64: + if (u64Const < constant.u64Const) + return true; + + return false; +#endif + case EbtDouble: + if (dConst < constant.dConst) + return true; + + return false; + case EbtInt: + if (iConst < constant.iConst) + return true; + + return false; + case EbtUint: + if (uConst < constant.uConst) + return true; + + return false; + default: + assert(false && "Default missing"); + return false; + } + } + + TConstUnion operator+(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst + constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst + constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst + constant.dConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const + constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const + constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const + constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const + constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const + constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const + constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator-(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst - constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst - constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst - constant.dConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const - constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const - constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const - constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const - constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const - constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const - constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator*(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst * constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst * constant.uConst); break; + case EbtDouble: returnValue.setDConst(dConst * constant.dConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const * constant.i8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const * constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const * constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const * constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const * constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const * constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator%(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst % constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst % constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const % constant.i8Const); break; + case EbtInt16: returnValue.setI8Const(i8Const % constant.i16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const % constant.i64Const); break; + case EbtUint8: returnValue.setU8Const(u8Const % constant.u8Const); break; + case EbtUint16: returnValue.setU16Const(u16Const % constant.u16Const); break; + case EbtUint64: returnValue.setU64Const(u64Const % constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator>>(const TConstUnion& constant) const + { + TConstUnion returnValue; + switch (type) { +#ifndef GLSLANG_WEB + case EbtInt8: + switch (constant.type) { + case EbtInt8: returnValue.setI8Const(i8Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI8Const(i8Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI8Const(i8Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI8Const(i8Const >> constant.u16Const); break; + case EbtInt: returnValue.setI8Const(i8Const >> constant.iConst); break; + case EbtUint: returnValue.setI8Const(i8Const >> constant.uConst); break; + case EbtInt64: returnValue.setI8Const(i8Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI8Const(i8Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint8: + switch (constant.type) { + case EbtInt8: returnValue.setU8Const(u8Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU8Const(u8Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU8Const(u8Const >> constant.u16Const); break; + case EbtInt: returnValue.setU8Const(u8Const >> constant.iConst); break; + case EbtUint: returnValue.setU8Const(u8Const >> constant.uConst); break; + case EbtInt64: returnValue.setU8Const(u8Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU8Const(u8Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt16: + switch (constant.type) { + case EbtInt8: returnValue.setI16Const(i16Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI16Const(i16Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI16Const(i16Const >> constant.u16Const); break; + case EbtInt: returnValue.setI16Const(i16Const >> constant.iConst); break; + case EbtUint: returnValue.setI16Const(i16Const >> constant.uConst); break; + case EbtInt64: returnValue.setI16Const(i16Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI16Const(i16Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint16: + switch (constant.type) { + case EbtInt8: returnValue.setU16Const(u16Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU16Const(u16Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU16Const(u16Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const >> constant.u16Const); break; + case EbtInt: returnValue.setU16Const(u16Const >> constant.iConst); break; + case EbtUint: returnValue.setU16Const(u16Const >> constant.uConst); break; + case EbtInt64: returnValue.setU16Const(u16Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU16Const(u16Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; +#endif + case EbtInt: + switch (constant.type) { + case EbtInt: returnValue.setIConst(iConst >> constant.iConst); break; + case EbtUint: returnValue.setIConst(iConst >> constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setIConst(iConst >> constant.i8Const); break; + case EbtUint8: returnValue.setIConst(iConst >> constant.u8Const); break; + case EbtInt16: returnValue.setIConst(iConst >> constant.i16Const); break; + case EbtUint16: returnValue.setIConst(iConst >> constant.u16Const); break; + case EbtInt64: returnValue.setIConst(iConst >> constant.i64Const); break; + case EbtUint64: returnValue.setIConst(iConst >> constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + break; + case EbtUint: + switch (constant.type) { + case EbtInt: returnValue.setUConst(uConst >> constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst >> constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setUConst(uConst >> constant.i8Const); break; + case EbtUint8: returnValue.setUConst(uConst >> constant.u8Const); break; + case EbtInt16: returnValue.setUConst(uConst >> constant.i16Const); break; + case EbtUint16: returnValue.setUConst(uConst >> constant.u16Const); break; + case EbtInt64: returnValue.setUConst(uConst >> constant.i64Const); break; + case EbtUint64: returnValue.setUConst(uConst >> constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + break; +#ifndef GLSLANG_WEB + case EbtInt64: + switch (constant.type) { + case EbtInt8: returnValue.setI64Const(i64Const >> constant.i8Const); break; + case EbtUint8: returnValue.setI64Const(i64Const >> constant.u8Const); break; + case EbtInt16: returnValue.setI64Const(i64Const >> constant.i16Const); break; + case EbtUint16: returnValue.setI64Const(i64Const >> constant.u16Const); break; + case EbtInt: returnValue.setI64Const(i64Const >> constant.iConst); break; + case EbtUint: returnValue.setI64Const(i64Const >> constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const >> constant.i64Const); break; + case EbtUint64: returnValue.setI64Const(i64Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint64: + switch (constant.type) { + case EbtInt8: returnValue.setU64Const(u64Const >> constant.i8Const); break; + case EbtUint8: returnValue.setU64Const(u64Const >> constant.u8Const); break; + case EbtInt16: returnValue.setU64Const(u64Const >> constant.i16Const); break; + case EbtUint16: returnValue.setU64Const(u64Const >> constant.u16Const); break; + case EbtInt: returnValue.setU64Const(u64Const >> constant.iConst); break; + case EbtUint: returnValue.setU64Const(u64Const >> constant.uConst); break; + case EbtInt64: returnValue.setU64Const(u64Const >> constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const >> constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator<<(const TConstUnion& constant) const + { + TConstUnion returnValue; + switch (type) { +#ifndef GLSLANG_WEB + case EbtInt8: + switch (constant.type) { + case EbtInt8: returnValue.setI8Const(i8Const << constant.i8Const); break; + case EbtUint8: returnValue.setI8Const(i8Const << constant.u8Const); break; + case EbtInt16: returnValue.setI8Const(i8Const << constant.i16Const); break; + case EbtUint16: returnValue.setI8Const(i8Const << constant.u16Const); break; + case EbtInt: returnValue.setI8Const(i8Const << constant.iConst); break; + case EbtUint: returnValue.setI8Const(i8Const << constant.uConst); break; + case EbtInt64: returnValue.setI8Const(i8Const << constant.i64Const); break; + case EbtUint64: returnValue.setI8Const(i8Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint8: + switch (constant.type) { + case EbtInt8: returnValue.setU8Const(u8Const << constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const << constant.u8Const); break; + case EbtInt16: returnValue.setU8Const(u8Const << constant.i16Const); break; + case EbtUint16: returnValue.setU8Const(u8Const << constant.u16Const); break; + case EbtInt: returnValue.setU8Const(u8Const << constant.iConst); break; + case EbtUint: returnValue.setU8Const(u8Const << constant.uConst); break; + case EbtInt64: returnValue.setU8Const(u8Const << constant.i64Const); break; + case EbtUint64: returnValue.setU8Const(u8Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt16: + switch (constant.type) { + case EbtInt8: returnValue.setI16Const(i16Const << constant.i8Const); break; + case EbtUint8: returnValue.setI16Const(i16Const << constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const << constant.i16Const); break; + case EbtUint16: returnValue.setI16Const(i16Const << constant.u16Const); break; + case EbtInt: returnValue.setI16Const(i16Const << constant.iConst); break; + case EbtUint: returnValue.setI16Const(i16Const << constant.uConst); break; + case EbtInt64: returnValue.setI16Const(i16Const << constant.i64Const); break; + case EbtUint64: returnValue.setI16Const(i16Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint16: + switch (constant.type) { + case EbtInt8: returnValue.setU16Const(u16Const << constant.i8Const); break; + case EbtUint8: returnValue.setU16Const(u16Const << constant.u8Const); break; + case EbtInt16: returnValue.setU16Const(u16Const << constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const << constant.u16Const); break; + case EbtInt: returnValue.setU16Const(u16Const << constant.iConst); break; + case EbtUint: returnValue.setU16Const(u16Const << constant.uConst); break; + case EbtInt64: returnValue.setU16Const(u16Const << constant.i64Const); break; + case EbtUint64: returnValue.setU16Const(u16Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtInt64: + switch (constant.type) { + case EbtInt8: returnValue.setI64Const(i64Const << constant.i8Const); break; + case EbtUint8: returnValue.setI64Const(i64Const << constant.u8Const); break; + case EbtInt16: returnValue.setI64Const(i64Const << constant.i16Const); break; + case EbtUint16: returnValue.setI64Const(i64Const << constant.u16Const); break; + case EbtInt: returnValue.setI64Const(i64Const << constant.iConst); break; + case EbtUint: returnValue.setI64Const(i64Const << constant.uConst); break; + case EbtInt64: returnValue.setI64Const(i64Const << constant.i64Const); break; + case EbtUint64: returnValue.setI64Const(i64Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; + case EbtUint64: + switch (constant.type) { + case EbtInt8: returnValue.setU64Const(u64Const << constant.i8Const); break; + case EbtUint8: returnValue.setU64Const(u64Const << constant.u8Const); break; + case EbtInt16: returnValue.setU64Const(u64Const << constant.i16Const); break; + case EbtUint16: returnValue.setU64Const(u64Const << constant.u16Const); break; + case EbtInt: returnValue.setU64Const(u64Const << constant.iConst); break; + case EbtUint: returnValue.setU64Const(u64Const << constant.uConst); break; + case EbtInt64: returnValue.setU64Const(u64Const << constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const << constant.u64Const); break; + default: assert(false && "Default missing"); + } + break; +#endif + case EbtInt: + switch (constant.type) { + case EbtInt: returnValue.setIConst(iConst << constant.iConst); break; + case EbtUint: returnValue.setIConst(iConst << constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setIConst(iConst << constant.i8Const); break; + case EbtUint8: returnValue.setIConst(iConst << constant.u8Const); break; + case EbtInt16: returnValue.setIConst(iConst << constant.i16Const); break; + case EbtUint16: returnValue.setIConst(iConst << constant.u16Const); break; + case EbtInt64: returnValue.setIConst(iConst << constant.i64Const); break; + case EbtUint64: returnValue.setIConst(iConst << constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + break; + case EbtUint: + switch (constant.type) { + case EbtInt: returnValue.setUConst(uConst << constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst << constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setUConst(uConst << constant.i8Const); break; + case EbtUint8: returnValue.setUConst(uConst << constant.u8Const); break; + case EbtInt16: returnValue.setUConst(uConst << constant.i16Const); break; + case EbtUint16: returnValue.setUConst(uConst << constant.u16Const); break; + case EbtInt64: returnValue.setUConst(uConst << constant.i64Const); break; + case EbtUint64: returnValue.setUConst(uConst << constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator&(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst & constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst & constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const & constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const & constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const & constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const & constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const & constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const & constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator|(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst | constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst | constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const | constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const | constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const | constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const | constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const | constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const | constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator^(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtInt: returnValue.setIConst(iConst ^ constant.iConst); break; + case EbtUint: returnValue.setUConst(uConst ^ constant.uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(i8Const ^ constant.i8Const); break; + case EbtUint8: returnValue.setU8Const(u8Const ^ constant.u8Const); break; + case EbtInt16: returnValue.setI16Const(i16Const ^ constant.i16Const); break; + case EbtUint16: returnValue.setU16Const(u16Const ^ constant.u16Const); break; + case EbtInt64: returnValue.setI64Const(i64Const ^ constant.i64Const); break; + case EbtUint64: returnValue.setU64Const(u64Const ^ constant.u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator~() const + { + TConstUnion returnValue; + switch (type) { + case EbtInt: returnValue.setIConst(~iConst); break; + case EbtUint: returnValue.setUConst(~uConst); break; +#ifndef GLSLANG_WEB + case EbtInt8: returnValue.setI8Const(~i8Const); break; + case EbtUint8: returnValue.setU8Const(~u8Const); break; + case EbtInt16: returnValue.setI16Const(~i16Const); break; + case EbtUint16: returnValue.setU16Const(~u16Const); break; + case EbtInt64: returnValue.setI64Const(~i64Const); break; + case EbtUint64: returnValue.setU64Const(~u64Const); break; +#endif + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator&&(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtBool: returnValue.setBConst(bConst && constant.bConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TConstUnion operator||(const TConstUnion& constant) const + { + TConstUnion returnValue; + assert(type == constant.type); + switch (type) { + case EbtBool: returnValue.setBConst(bConst || constant.bConst); break; + default: assert(false && "Default missing"); + } + + return returnValue; + } + + TBasicType getType() const { return type; } + +private: + union { + signed char i8Const; // used for i8vec, scalar int8s + unsigned char u8Const; // used for u8vec, scalar uint8s + signed short i16Const; // used for i16vec, scalar int16s + unsigned short u16Const; // used for u16vec, scalar uint16s + int iConst; // used for ivec, scalar ints + unsigned int uConst; // used for uvec, scalar uints + long long i64Const; // used for i64vec, scalar int64s + unsigned long long u64Const; // used for u64vec, scalar uint64s + bool bConst; // used for bvec, scalar bools + double dConst; // used for vec, dvec, mat, dmat, scalar floats and doubles + const TString* sConst; // string constant + }; + + TBasicType type; +}; + +// Encapsulate having a pointer to an array of TConstUnion, +// which only needs to be allocated if its size is going to be +// bigger than 0. +// +// One convenience is being able to use [] to go inside the array, instead +// of C++ assuming it as an array of pointers to vectors. +// +// General usage is that the size is known up front, and it is +// created once with the proper size. +// +class TConstUnionArray { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TConstUnionArray() : unionArray(nullptr) { } + virtual ~TConstUnionArray() { } + + explicit TConstUnionArray(int size) + { + if (size == 0) + unionArray = nullptr; + else + unionArray = new TConstUnionVector(size); + } + TConstUnionArray(const TConstUnionArray& a) = default; + TConstUnionArray(const TConstUnionArray& a, int start, int size) + { + unionArray = new TConstUnionVector(size); + for (int i = 0; i < size; ++i) + (*unionArray)[i] = a[start + i]; + } + + // Use this constructor for a smear operation + TConstUnionArray(int size, const TConstUnion& val) + { + unionArray = new TConstUnionVector(size, val); + } + + int size() const { return unionArray ? (int)unionArray->size() : 0; } + TConstUnion& operator[](size_t index) { return (*unionArray)[index]; } + const TConstUnion& operator[](size_t index) const { return (*unionArray)[index]; } + bool operator==(const TConstUnionArray& rhs) const + { + // this includes the case that both are unallocated + if (unionArray == rhs.unionArray) + return true; + + if (! unionArray || ! rhs.unionArray) + return false; + + return *unionArray == *rhs.unionArray; + } + bool operator!=(const TConstUnionArray& rhs) const { return ! operator==(rhs); } + + double dot(const TConstUnionArray& rhs) + { + assert(rhs.unionArray->size() == unionArray->size()); + double sum = 0.0; + + for (size_t comp = 0; comp < unionArray->size(); ++comp) + sum += (*this)[comp].getDConst() * rhs[comp].getDConst(); + + return sum; + } + + bool empty() const { return unionArray == nullptr; } + +protected: + typedef TVector TConstUnionVector; + TConstUnionVector* unionArray; +}; + +} // end namespace glslang + +#endif // _CONSTANT_UNION_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/InfoSink.h b/third_party/glslang/glslang/Include/InfoSink.h new file mode 100644 index 0000000..dceb603 --- /dev/null +++ b/third_party/glslang/glslang/Include/InfoSink.h @@ -0,0 +1,144 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _INFOSINK_INCLUDED_ +#define _INFOSINK_INCLUDED_ + +#include "../Include/Common.h" +#include + +namespace glslang { + +// +// TPrefixType is used to centralize how info log messages start. +// See below. +// +enum TPrefixType { + EPrefixNone, + EPrefixWarning, + EPrefixError, + EPrefixInternalError, + EPrefixUnimplemented, + EPrefixNote +}; + +enum TOutputStream { + ENull = 0, + EDebugger = 0x01, + EStdOut = 0x02, + EString = 0x04, +}; +// +// Encapsulate info logs for all objects that have them. +// +// The methods are a general set of tools for getting a variety of +// messages and types inserted into the log. +// +class TInfoSinkBase { +public: + TInfoSinkBase() : outputStream(4) {} + void erase() { sink.erase(); } + TInfoSinkBase& operator<<(const TPersistString& t) { append(t); return *this; } + TInfoSinkBase& operator<<(char c) { append(1, c); return *this; } + TInfoSinkBase& operator<<(const char* s) { append(s); return *this; } + TInfoSinkBase& operator<<(int n) { append(String(n)); return *this; } + TInfoSinkBase& operator<<(unsigned int n) { append(String(n)); return *this; } + TInfoSinkBase& operator<<(float n) { const int size = 40; char buf[size]; + snprintf(buf, size, (fabs(n) > 1e-8 && fabs(n) < 1e8) || n == 0.0f ? "%f" : "%g", n); + append(buf); + return *this; } + TInfoSinkBase& operator+(const TPersistString& t) { append(t); return *this; } + TInfoSinkBase& operator+(const TString& t) { append(t); return *this; } + TInfoSinkBase& operator<<(const TString& t) { append(t); return *this; } + TInfoSinkBase& operator+(const char* s) { append(s); return *this; } + const char* c_str() const { return sink.c_str(); } + void prefix(TPrefixType message) { + switch(message) { + case EPrefixNone: break; + case EPrefixWarning: append("WARNING: "); break; + case EPrefixError: append("ERROR: "); break; + case EPrefixInternalError: append("INTERNAL ERROR: "); break; + case EPrefixUnimplemented: append("UNIMPLEMENTED: "); break; + case EPrefixNote: append("NOTE: "); break; + default: append("UNKNOWN ERROR: "); break; + } + } + void location(const TSourceLoc& loc) { + const int maxSize = 24; + char locText[maxSize]; + snprintf(locText, maxSize, ":%d", loc.line); + append(loc.getStringNameOrNum(false).c_str()); + append(locText); + append(": "); + } + void message(TPrefixType message, const char* s) { + prefix(message); + append(s); + append("\n"); + } + void message(TPrefixType message, const char* s, const TSourceLoc& loc) { + prefix(message); + location(loc); + append(s); + append("\n"); + } + + void setOutputStream(int output = 4) + { + outputStream = output; + } + +protected: + void append(const char* s); + + void append(int count, char c); + void append(const TPersistString& t); + void append(const TString& t); + + void checkMem(size_t growth) { if (sink.capacity() < sink.size() + growth + 2) + sink.reserve(sink.capacity() + sink.capacity() / 2); } + void appendToStream(const char* s); + TPersistString sink; + int outputStream; +}; + +} // end namespace glslang + +class TInfoSink { +public: + glslang::TInfoSinkBase info; + glslang::TInfoSinkBase debug; +}; + +#endif // _INFOSINK_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/InitializeGlobals.h b/third_party/glslang/glslang/Include/InitializeGlobals.h new file mode 100644 index 0000000..95d0a40 --- /dev/null +++ b/third_party/glslang/glslang/Include/InitializeGlobals.h @@ -0,0 +1,44 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef __INITIALIZE_GLOBALS_INCLUDED_ +#define __INITIALIZE_GLOBALS_INCLUDED_ + +namespace glslang { + +bool InitializePoolIndex(); + +} // end namespace glslang + +#endif // __INITIALIZE_GLOBALS_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/PoolAlloc.h b/third_party/glslang/glslang/Include/PoolAlloc.h new file mode 100644 index 0000000..b8eccb8 --- /dev/null +++ b/third_party/glslang/glslang/Include/PoolAlloc.h @@ -0,0 +1,316 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _POOLALLOC_INCLUDED_ +#define _POOLALLOC_INCLUDED_ + +#ifdef _DEBUG +# define GUARD_BLOCKS // define to enable guard block sanity checking +#endif + +// +// This header defines an allocator that can be used to efficiently +// allocate a large number of small requests for heap memory, with the +// intention that they are not individually deallocated, but rather +// collectively deallocated at one time. +// +// This simultaneously +// +// * Makes each individual allocation much more efficient; the +// typical allocation is trivial. +// * Completely avoids the cost of doing individual deallocation. +// * Saves the trouble of tracking down and plugging a large class of leaks. +// +// Individual classes can use this allocator by supplying their own +// new and delete methods. +// +// STL containers can use this allocator by using the pool_allocator +// class as the allocator (second) template argument. +// + +#include +#include +#include + +namespace glslang { + +// If we are using guard blocks, we must track each individual +// allocation. If we aren't using guard blocks, these +// never get instantiated, so won't have any impact. +// + +class TAllocation { +public: + TAllocation(size_t size, unsigned char* mem, TAllocation* prev = 0) : + size(size), mem(mem), prevAlloc(prev) { + // Allocations are bracketed: + // [allocationHeader][initialGuardBlock][userData][finalGuardBlock] + // This would be cleaner with if (guardBlockSize)..., but that + // makes the compiler print warnings about 0 length memsets, + // even with the if() protecting them. +# ifdef GUARD_BLOCKS + memset(preGuard(), guardBlockBeginVal, guardBlockSize); + memset(data(), userDataFill, size); + memset(postGuard(), guardBlockEndVal, guardBlockSize); +# endif + } + + void check() const { + checkGuardBlock(preGuard(), guardBlockBeginVal, "before"); + checkGuardBlock(postGuard(), guardBlockEndVal, "after"); + } + + void checkAllocList() const; + + // Return total size needed to accommodate user buffer of 'size', + // plus our tracking data. + inline static size_t allocationSize(size_t size) { + return size + 2 * guardBlockSize + headerSize(); + } + + // Offset from surrounding buffer to get to user data buffer. + inline static unsigned char* offsetAllocation(unsigned char* m) { + return m + guardBlockSize + headerSize(); + } + +private: + void checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const; + + // Find offsets to pre and post guard blocks, and user data buffer + unsigned char* preGuard() const { return mem + headerSize(); } + unsigned char* data() const { return preGuard() + guardBlockSize; } + unsigned char* postGuard() const { return data() + size; } + + size_t size; // size of the user data area + unsigned char* mem; // beginning of our allocation (pts to header) + TAllocation* prevAlloc; // prior allocation in the chain + + const static unsigned char guardBlockBeginVal; + const static unsigned char guardBlockEndVal; + const static unsigned char userDataFill; + + const static size_t guardBlockSize; +# ifdef GUARD_BLOCKS + inline static size_t headerSize() { return sizeof(TAllocation); } +# else + inline static size_t headerSize() { return 0; } +# endif +}; + +// +// There are several stacks. One is to track the pushing and popping +// of the user, and not yet implemented. The others are simply a +// repositories of free pages or used pages. +// +// Page stacks are linked together with a simple header at the beginning +// of each allocation obtained from the underlying OS. Multi-page allocations +// are returned to the OS. Individual page allocations are kept for future +// re-use. +// +// The "page size" used is not, nor must it match, the underlying OS +// page size. But, having it be about that size or equal to a set of +// pages is likely most optimal. +// +class TPoolAllocator { +public: + TPoolAllocator(int growthIncrement = 8*1024, int allocationAlignment = 16); + + // + // Don't call the destructor just to free up the memory, call pop() + // + ~TPoolAllocator(); + + // + // Call push() to establish a new place to pop memory too. Does not + // have to be called to get things started. + // + void push(); + + // + // Call pop() to free all memory allocated since the last call to push(), + // or if no last call to push, frees all memory since first allocation. + // + void pop(); + + // + // Call popAll() to free all memory allocated. + // + void popAll(); + + // + // Call allocate() to actually acquire memory. Returns 0 if no memory + // available, otherwise a properly aligned pointer to 'numBytes' of memory. + // + void* allocate(size_t numBytes); + + // + // There is no deallocate. The point of this class is that + // deallocation can be skipped by the user of it, as the model + // of use is to simultaneously deallocate everything at once + // by calling pop(), and to not have to solve memory leak problems. + // + +protected: + friend struct tHeader; + + struct tHeader { + tHeader(tHeader* nextPage, size_t pageCount) : +#ifdef GUARD_BLOCKS + lastAllocation(0), +#endif + nextPage(nextPage), pageCount(pageCount) { } + + ~tHeader() { +#ifdef GUARD_BLOCKS + if (lastAllocation) + lastAllocation->checkAllocList(); +#endif + } + +#ifdef GUARD_BLOCKS + TAllocation* lastAllocation; +#endif + tHeader* nextPage; + size_t pageCount; + }; + + struct tAllocState { + size_t offset; + tHeader* page; + }; + typedef std::vector tAllocStack; + + // Track allocations if and only if we're using guard blocks +#ifndef GUARD_BLOCKS + void* initializeAllocation(tHeader*, unsigned char* memory, size_t) { +#else + void* initializeAllocation(tHeader* block, unsigned char* memory, size_t numBytes) { + new(memory) TAllocation(numBytes, memory, block->lastAllocation); + block->lastAllocation = reinterpret_cast(memory); +#endif + + // This is optimized entirely away if GUARD_BLOCKS is not defined. + return TAllocation::offsetAllocation(memory); + } + + size_t pageSize; // granularity of allocation from the OS + size_t alignment; // all returned allocations will be aligned at + // this granularity, which will be a power of 2 + size_t alignmentMask; + size_t headerSkip; // amount of memory to skip to make room for the + // header (basically, size of header, rounded + // up to make it aligned + size_t currentPageOffset; // next offset in top of inUseList to allocate from + tHeader* freeList; // list of popped memory + tHeader* inUseList; // list of all memory currently being used + tAllocStack stack; // stack of where to allocate from, to partition pool + + int numCalls; // just an interesting statistic + size_t totalBytes; // just an interesting statistic +private: + TPoolAllocator& operator=(const TPoolAllocator&); // don't allow assignment operator + TPoolAllocator(const TPoolAllocator&); // don't allow default copy constructor +}; + +// +// There could potentially be many pools with pops happening at +// different times. But a simple use is to have a global pop +// with everyone using the same global allocator. +// +extern TPoolAllocator& GetThreadPoolAllocator(); +void SetThreadPoolAllocator(TPoolAllocator* poolAllocator); + +// +// This STL compatible allocator is intended to be used as the allocator +// parameter to templatized STL containers, like vector and map. +// +// It will use the pools for allocation, and not +// do any deallocation, but will still do destruction. +// +template +class pool_allocator { +public: + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef T *pointer; + typedef const T *const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef T value_type; + template + struct rebind { + typedef pool_allocator other; + }; + pointer address(reference x) const { return &x; } + const_pointer address(const_reference x) const { return &x; } + + pool_allocator() : allocator(GetThreadPoolAllocator()) { } + pool_allocator(TPoolAllocator& a) : allocator(a) { } + pool_allocator(const pool_allocator& p) : allocator(p.allocator) { } + + template + pool_allocator(const pool_allocator& p) : allocator(p.getAllocator()) { } + + pointer allocate(size_type n) { + return reinterpret_cast(getAllocator().allocate(n * sizeof(T))); } + pointer allocate(size_type n, const void*) { + return reinterpret_cast(getAllocator().allocate(n * sizeof(T))); } + + void deallocate(void*, size_type) { } + void deallocate(pointer, size_type) { } + + pointer _Charalloc(size_t n) { + return reinterpret_cast(getAllocator().allocate(n)); } + + void construct(pointer p, const T& val) { new ((void *)p) T(val); } + void destroy(pointer p) { p->T::~T(); } + + bool operator==(const pool_allocator& rhs) const { return &getAllocator() == &rhs.getAllocator(); } + bool operator!=(const pool_allocator& rhs) const { return &getAllocator() != &rhs.getAllocator(); } + + size_type max_size() const { return static_cast(-1) / sizeof(T); } + size_type max_size(int size) const { return static_cast(-1) / size; } + + TPoolAllocator& getAllocator() const { return allocator; } + +protected: + pool_allocator& operator=(const pool_allocator&) { return *this; } + TPoolAllocator& allocator; +}; + +} // end namespace glslang + +#endif // _POOLALLOC_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/ResourceLimits.h b/third_party/glslang/glslang/Include/ResourceLimits.h new file mode 100644 index 0000000..b670cf1 --- /dev/null +++ b/third_party/glslang/glslang/Include/ResourceLimits.h @@ -0,0 +1,150 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _RESOURCE_LIMITS_INCLUDED_ +#define _RESOURCE_LIMITS_INCLUDED_ + +struct TLimits { + bool nonInductiveForLoops; + bool whileLoops; + bool doWhileLoops; + bool generalUniformIndexing; + bool generalAttributeMatrixVectorIndexing; + bool generalVaryingIndexing; + bool generalSamplerIndexing; + bool generalVariableIndexing; + bool generalConstantMatrixVectorIndexing; +}; + +struct TBuiltInResource { + int maxLights; + int maxClipPlanes; + int maxTextureUnits; + int maxTextureCoords; + int maxVertexAttribs; + int maxVertexUniformComponents; + int maxVaryingFloats; + int maxVertexTextureImageUnits; + int maxCombinedTextureImageUnits; + int maxTextureImageUnits; + int maxFragmentUniformComponents; + int maxDrawBuffers; + int maxVertexUniformVectors; + int maxVaryingVectors; + int maxFragmentUniformVectors; + int maxVertexOutputVectors; + int maxFragmentInputVectors; + int minProgramTexelOffset; + int maxProgramTexelOffset; + int maxClipDistances; + int maxComputeWorkGroupCountX; + int maxComputeWorkGroupCountY; + int maxComputeWorkGroupCountZ; + int maxComputeWorkGroupSizeX; + int maxComputeWorkGroupSizeY; + int maxComputeWorkGroupSizeZ; + int maxComputeUniformComponents; + int maxComputeTextureImageUnits; + int maxComputeImageUniforms; + int maxComputeAtomicCounters; + int maxComputeAtomicCounterBuffers; + int maxVaryingComponents; + int maxVertexOutputComponents; + int maxGeometryInputComponents; + int maxGeometryOutputComponents; + int maxFragmentInputComponents; + int maxImageUnits; + int maxCombinedImageUnitsAndFragmentOutputs; + int maxCombinedShaderOutputResources; + int maxImageSamples; + int maxVertexImageUniforms; + int maxTessControlImageUniforms; + int maxTessEvaluationImageUniforms; + int maxGeometryImageUniforms; + int maxFragmentImageUniforms; + int maxCombinedImageUniforms; + int maxGeometryTextureImageUnits; + int maxGeometryOutputVertices; + int maxGeometryTotalOutputComponents; + int maxGeometryUniformComponents; + int maxGeometryVaryingComponents; + int maxTessControlInputComponents; + int maxTessControlOutputComponents; + int maxTessControlTextureImageUnits; + int maxTessControlUniformComponents; + int maxTessControlTotalOutputComponents; + int maxTessEvaluationInputComponents; + int maxTessEvaluationOutputComponents; + int maxTessEvaluationTextureImageUnits; + int maxTessEvaluationUniformComponents; + int maxTessPatchComponents; + int maxPatchVertices; + int maxTessGenLevel; + int maxViewports; + int maxVertexAtomicCounters; + int maxTessControlAtomicCounters; + int maxTessEvaluationAtomicCounters; + int maxGeometryAtomicCounters; + int maxFragmentAtomicCounters; + int maxCombinedAtomicCounters; + int maxAtomicCounterBindings; + int maxVertexAtomicCounterBuffers; + int maxTessControlAtomicCounterBuffers; + int maxTessEvaluationAtomicCounterBuffers; + int maxGeometryAtomicCounterBuffers; + int maxFragmentAtomicCounterBuffers; + int maxCombinedAtomicCounterBuffers; + int maxAtomicCounterBufferSize; + int maxTransformFeedbackBuffers; + int maxTransformFeedbackInterleavedComponents; + int maxCullDistances; + int maxCombinedClipAndCullDistances; + int maxSamples; + int maxMeshOutputVerticesNV; + int maxMeshOutputPrimitivesNV; + int maxMeshWorkGroupSizeX_NV; + int maxMeshWorkGroupSizeY_NV; + int maxMeshWorkGroupSizeZ_NV; + int maxTaskWorkGroupSizeX_NV; + int maxTaskWorkGroupSizeY_NV; + int maxTaskWorkGroupSizeZ_NV; + int maxMeshViewCountNV; + int maxDualSourceDrawBuffersEXT; + + TLimits limits; +}; + +#endif // _RESOURCE_LIMITS_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/ShHandle.h b/third_party/glslang/glslang/Include/ShHandle.h new file mode 100644 index 0000000..df07bd8 --- /dev/null +++ b/third_party/glslang/glslang/Include/ShHandle.h @@ -0,0 +1,176 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _SHHANDLE_INCLUDED_ +#define _SHHANDLE_INCLUDED_ + +// +// Machine independent part of the compiler private objects +// sent as ShHandle to the driver. +// +// This should not be included by driver code. +// + +#define SH_EXPORTING +#include "../Public/ShaderLang.h" +#include "../MachineIndependent/Versions.h" +#include "InfoSink.h" + +class TCompiler; +class TLinker; +class TUniformMap; + +// +// The base class used to back handles returned to the driver. +// +class TShHandleBase { +public: + TShHandleBase() { pool = new glslang::TPoolAllocator; } + virtual ~TShHandleBase() { delete pool; } + virtual TCompiler* getAsCompiler() { return 0; } + virtual TLinker* getAsLinker() { return 0; } + virtual TUniformMap* getAsUniformMap() { return 0; } + virtual glslang::TPoolAllocator* getPool() const { return pool; } +private: + glslang::TPoolAllocator* pool; +}; + +// +// The base class for the machine dependent linker to derive from +// for managing where uniforms live. +// +class TUniformMap : public TShHandleBase { +public: + TUniformMap() { } + virtual ~TUniformMap() { } + virtual TUniformMap* getAsUniformMap() { return this; } + virtual int getLocation(const char* name) = 0; + virtual TInfoSink& getInfoSink() { return infoSink; } + TInfoSink infoSink; +}; + +class TIntermNode; + +// +// The base class for the machine dependent compiler to derive from +// for managing object code from the compile. +// +class TCompiler : public TShHandleBase { +public: + TCompiler(EShLanguage l, TInfoSink& sink) : infoSink(sink) , language(l), haveValidObjectCode(false) { } + virtual ~TCompiler() { } + EShLanguage getLanguage() { return language; } + virtual TInfoSink& getInfoSink() { return infoSink; } + + virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile) = 0; + + virtual TCompiler* getAsCompiler() { return this; } + virtual bool linkable() { return haveValidObjectCode; } + + TInfoSink& infoSink; +protected: + TCompiler& operator=(TCompiler&); + + EShLanguage language; + bool haveValidObjectCode; +}; + +// +// Link operations are based on a list of compile results... +// +typedef glslang::TVector TCompilerList; +typedef glslang::TVector THandleList; + +// +// The base class for the machine dependent linker to derive from +// to manage the resulting executable. +// + +class TLinker : public TShHandleBase { +public: + TLinker(EShExecutable e, TInfoSink& iSink) : + infoSink(iSink), + executable(e), + haveReturnableObjectCode(false), + appAttributeBindings(0), + fixedAttributeBindings(0), + excludedAttributes(0), + excludedCount(0), + uniformBindings(0) { } + virtual TLinker* getAsLinker() { return this; } + virtual ~TLinker() { } + virtual bool link(TCompilerList&, TUniformMap*) = 0; + virtual bool link(THandleList&) { return false; } + virtual void setAppAttributeBindings(const ShBindingTable* t) { appAttributeBindings = t; } + virtual void setFixedAttributeBindings(const ShBindingTable* t) { fixedAttributeBindings = t; } + virtual void getAttributeBindings(ShBindingTable const **t) const = 0; + virtual void setExcludedAttributes(const int* attributes, int count) { excludedAttributes = attributes; excludedCount = count; } + virtual ShBindingTable* getUniformBindings() const { return uniformBindings; } + virtual const void* getObjectCode() const { return 0; } // a real compiler would be returning object code here + virtual TInfoSink& getInfoSink() { return infoSink; } + TInfoSink& infoSink; +protected: + TLinker& operator=(TLinker&); + EShExecutable executable; + bool haveReturnableObjectCode; // true when objectCode is acceptable to send to driver + + const ShBindingTable* appAttributeBindings; + const ShBindingTable* fixedAttributeBindings; + const int* excludedAttributes; + int excludedCount; + ShBindingTable* uniformBindings; // created by the linker +}; + +// +// This is the interface between the machine independent code +// and the machine dependent code. +// +// The machine dependent code should derive from the classes +// above. Then Construct*() and Delete*() will create and +// destroy the machine dependent objects, which contain the +// above machine independent information. +// +TCompiler* ConstructCompiler(EShLanguage, int); + +TShHandleBase* ConstructLinker(EShExecutable, int); +TShHandleBase* ConstructBindings(); +void DeleteLinker(TShHandleBase*); +void DeleteBindingList(TShHandleBase* bindingList); + +TUniformMap* ConstructUniformMap(); +void DeleteCompiler(TCompiler*); + +void DeleteUniformMap(TUniformMap*); + +#endif // _SHHANDLE_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/Types.h b/third_party/glslang/glslang/Include/Types.h new file mode 100644 index 0000000..a3043f8 --- /dev/null +++ b/third_party/glslang/glslang/Include/Types.h @@ -0,0 +1,2492 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2015-2016 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _TYPES_INCLUDED +#define _TYPES_INCLUDED + +#include "../Include/Common.h" +#include "../Include/BaseTypes.h" +#include "../Public/ShaderLang.h" +#include "arrays.h" + +#include + +namespace glslang { + +const int GlslangMaxTypeLength = 200; // TODO: need to print block/struct one member per line, so this can stay bounded + +const char* const AnonymousPrefix = "anon@"; // for something like a block whose members can be directly accessed +inline bool IsAnonymous(const TString& name) +{ + return name.compare(0, 5, AnonymousPrefix) == 0; +} + +// +// Details within a sampler type +// +enum TSamplerDim { + EsdNone, + Esd1D, + Esd2D, + Esd3D, + EsdCube, + EsdRect, + EsdBuffer, + EsdSubpass, // goes only with non-sampled image (image is true) + EsdNumDims +}; + +struct TSampler { // misnomer now; includes images, textures without sampler, and textures with sampler + TBasicType type : 8; // type returned by sampler + TSamplerDim dim : 8; + bool arrayed : 1; + bool shadow : 1; + bool ms : 1; + bool image : 1; // image, combined should be false + bool combined : 1; // true means texture is combined with a sampler, false means texture with no sampler + bool sampler : 1; // true means a pure sampler, other fields should be clear() + +#ifdef GLSLANG_WEB + bool is1D() const { return false; } + bool isBuffer() const { return false; } + bool isRect() const { return false; } + bool isSubpass() const { return false; } + bool isCombined() const { return true; } + bool isImage() const { return false; } + bool isImageClass() const { return false; } + bool isMultiSample() const { return false; } + bool isExternal() const { return false; } + void setExternal(bool e) { } + bool isYuv() const { return false; } +#else + unsigned int vectorSize : 3; // vector return type size. + // Some languages support structures as sample results. Storing the whole structure in the + // TSampler is too large, so there is an index to a separate table. + static const unsigned structReturnIndexBits = 4; // number of index bits to use. + static const unsigned structReturnSlots = (1< TTypeList; + +typedef TVector TIdentifierList; + +// +// Following are a series of helper enums for managing layouts and qualifiers, +// used for TPublicType, TType, others. +// + +enum TLayoutPacking { + ElpNone, + ElpShared, // default, but different than saying nothing + ElpStd140, + ElpStd430, + ElpPacked, + ElpScalar, + ElpCount // If expanding, see bitfield width below +}; + +enum TLayoutMatrix { + ElmNone, + ElmRowMajor, + ElmColumnMajor, // default, but different than saying nothing + ElmCount // If expanding, see bitfield width below +}; + +// Union of geometry shader and tessellation shader geometry types. +// They don't go into TType, but rather have current state per shader or +// active parser type (TPublicType). +enum TLayoutGeometry { + ElgNone, + ElgPoints, + ElgLines, + ElgLinesAdjacency, + ElgLineStrip, + ElgTriangles, + ElgTrianglesAdjacency, + ElgTriangleStrip, + ElgQuads, + ElgIsolines, +}; + +enum TVertexSpacing { + EvsNone, + EvsEqual, + EvsFractionalEven, + EvsFractionalOdd +}; + +enum TVertexOrder { + EvoNone, + EvoCw, + EvoCcw +}; + +// Note: order matters, as type of format is done by comparison. +enum TLayoutFormat { + ElfNone, + + // Float image + ElfRgba32f, + ElfRgba16f, + ElfR32f, + ElfRgba8, + ElfRgba8Snorm, + + ElfEsFloatGuard, // to help with comparisons + + ElfRg32f, + ElfRg16f, + ElfR11fG11fB10f, + ElfR16f, + ElfRgba16, + ElfRgb10A2, + ElfRg16, + ElfRg8, + ElfR16, + ElfR8, + ElfRgba16Snorm, + ElfRg16Snorm, + ElfRg8Snorm, + ElfR16Snorm, + ElfR8Snorm, + + ElfFloatGuard, // to help with comparisons + + // Int image + ElfRgba32i, + ElfRgba16i, + ElfRgba8i, + ElfR32i, + + ElfEsIntGuard, // to help with comparisons + + ElfRg32i, + ElfRg16i, + ElfRg8i, + ElfR16i, + ElfR8i, + ElfR64i, + + ElfIntGuard, // to help with comparisons + + // Uint image + ElfRgba32ui, + ElfRgba16ui, + ElfRgba8ui, + ElfR32ui, + + ElfEsUintGuard, // to help with comparisons + + ElfRg32ui, + ElfRg16ui, + ElfRgb10a2ui, + ElfRg8ui, + ElfR16ui, + ElfR8ui, + ElfR64ui, + + ElfCount +}; + +enum TLayoutDepth { + EldNone, + EldAny, + EldGreater, + EldLess, + EldUnchanged, + + EldCount +}; + +enum TBlendEquationShift { + // No 'EBlendNone': + // These are used as bit-shift amounts. A mask of such shifts will have type 'int', + // and in that space, 0 means no bits set, or none. In this enum, 0 means (1 << 0), a bit is set. + EBlendMultiply, + EBlendScreen, + EBlendOverlay, + EBlendDarken, + EBlendLighten, + EBlendColordodge, + EBlendColorburn, + EBlendHardlight, + EBlendSoftlight, + EBlendDifference, + EBlendExclusion, + EBlendHslHue, + EBlendHslSaturation, + EBlendHslColor, + EBlendHslLuminosity, + EBlendAllEquations, + + EBlendCount +}; + +enum TInterlockOrdering { + EioNone, + EioPixelInterlockOrdered, + EioPixelInterlockUnordered, + EioSampleInterlockOrdered, + EioSampleInterlockUnordered, + EioShadingRateInterlockOrdered, + EioShadingRateInterlockUnordered, + + EioCount, +}; + +enum TShaderInterface +{ + // Includes both uniform blocks and buffer blocks + EsiUniform = 0, + EsiInput, + EsiOutput, + EsiNone, + + EsiCount +}; + + +class TQualifier { +public: + static const int layoutNotSet = -1; + + void clear() + { + precision = EpqNone; + invariant = false; + makeTemporary(); + declaredBuiltIn = EbvNone; +#ifndef GLSLANG_WEB + noContraction = false; +#endif + } + + // drop qualifiers that don't belong in a temporary variable + void makeTemporary() + { + semanticName = nullptr; + storage = EvqTemporary; + builtIn = EbvNone; + clearInterstage(); + clearMemory(); + specConstant = false; + nonUniform = false; + clearLayout(); + } + + void clearInterstage() + { + clearInterpolation(); +#ifndef GLSLANG_WEB + patch = false; + sample = false; +#endif + } + + void clearInterpolation() + { + centroid = false; + smooth = false; + flat = false; +#ifndef GLSLANG_WEB + nopersp = false; + explicitInterp = false; + pervertexNV = false; + perPrimitiveNV = false; + perViewNV = false; + perTaskNV = false; +#endif + } + + void clearMemory() + { +#ifndef GLSLANG_WEB + coherent = false; + devicecoherent = false; + queuefamilycoherent = false; + workgroupcoherent = false; + subgroupcoherent = false; + shadercallcoherent = false; + nonprivate = false; + volatil = false; + restrict = false; + readonly = false; + writeonly = false; +#endif + } + + const char* semanticName; + TStorageQualifier storage : 6; + TBuiltInVariable builtIn : 9; + TBuiltInVariable declaredBuiltIn : 9; + static_assert(EbvLast < 256, "need to increase size of TBuiltInVariable bitfields!"); + TPrecisionQualifier precision : 3; + bool invariant : 1; // require canonical treatment for cross-shader invariance + bool centroid : 1; + bool smooth : 1; + bool flat : 1; + // having a constant_id is not sufficient: expressions have no id, but are still specConstant + bool specConstant : 1; + bool nonUniform : 1; + bool explicitOffset : 1; + +#ifdef GLSLANG_WEB + bool isWriteOnly() const { return false; } + bool isReadOnly() const { return false; } + bool isRestrict() const { return false; } + bool isCoherent() const { return false; } + bool isVolatile() const { return false; } + bool isSample() const { return false; } + bool isMemory() const { return false; } + bool isMemoryQualifierImageAndSSBOOnly() const { return false; } + bool bufferReferenceNeedsVulkanMemoryModel() const { return false; } + bool isInterpolation() const { return flat || smooth; } + bool isExplicitInterpolation() const { return false; } + bool isAuxiliary() const { return centroid; } + bool isPatch() const { return false; } + bool isNoContraction() const { return false; } + void setNoContraction() { } + bool isPervertexNV() const { return false; } +#else + bool noContraction: 1; // prevent contraction and reassociation, e.g., for 'precise' keyword, and expressions it affects + bool nopersp : 1; + bool explicitInterp : 1; + bool pervertexNV : 1; + bool perPrimitiveNV : 1; + bool perViewNV : 1; + bool perTaskNV : 1; + bool patch : 1; + bool sample : 1; + bool restrict : 1; + bool readonly : 1; + bool writeonly : 1; + bool coherent : 1; + bool volatil : 1; + bool devicecoherent : 1; + bool queuefamilycoherent : 1; + bool workgroupcoherent : 1; + bool subgroupcoherent : 1; + bool shadercallcoherent : 1; + bool nonprivate : 1; + bool isWriteOnly() const { return writeonly; } + bool isReadOnly() const { return readonly; } + bool isRestrict() const { return restrict; } + bool isCoherent() const { return coherent; } + bool isVolatile() const { return volatil; } + bool isSample() const { return sample; } + bool isMemory() const + { + return shadercallcoherent || subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || restrict || readonly || writeonly || nonprivate; + } + bool isMemoryQualifierImageAndSSBOOnly() const + { + return shadercallcoherent || subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || volatil || restrict || readonly || writeonly; + } + bool bufferReferenceNeedsVulkanMemoryModel() const + { + // include qualifiers that map to load/store availability/visibility/nonprivate memory access operands + return subgroupcoherent || workgroupcoherent || queuefamilycoherent || devicecoherent || coherent || nonprivate; + } + bool isInterpolation() const + { + return flat || smooth || nopersp || explicitInterp; + } + bool isExplicitInterpolation() const + { + return explicitInterp; + } + bool isAuxiliary() const + { + return centroid || patch || sample || pervertexNV; + } + bool isPatch() const { return patch; } + bool isNoContraction() const { return noContraction; } + void setNoContraction() { noContraction = true; } + bool isPervertexNV() const { return pervertexNV; } +#endif + + bool isPipeInput() const + { + switch (storage) { + case EvqVaryingIn: + case EvqFragCoord: + case EvqPointCoord: + case EvqFace: + case EvqVertexId: + case EvqInstanceId: + return true; + default: + return false; + } + } + + bool isPipeOutput() const + { + switch (storage) { + case EvqPosition: + case EvqPointSize: + case EvqClipVertex: + case EvqVaryingOut: + case EvqFragColor: + case EvqFragDepth: + return true; + default: + return false; + } + } + + bool isParamInput() const + { + switch (storage) { + case EvqIn: + case EvqInOut: + case EvqConstReadOnly: + return true; + default: + return false; + } + } + + bool isParamOutput() const + { + switch (storage) { + case EvqOut: + case EvqInOut: + return true; + default: + return false; + } + } + + bool isUniformOrBuffer() const + { + switch (storage) { + case EvqUniform: + case EvqBuffer: + return true; + default: + return false; + } + } + + bool isIo() const + { + switch (storage) { + case EvqUniform: + case EvqBuffer: + case EvqVaryingIn: + case EvqFragCoord: + case EvqPointCoord: + case EvqFace: + case EvqVertexId: + case EvqInstanceId: + case EvqPosition: + case EvqPointSize: + case EvqClipVertex: + case EvqVaryingOut: + case EvqFragColor: + case EvqFragDepth: + return true; + default: + return false; + } + } + + // non-built-in symbols that might link between compilation units + bool isLinkable() const + { + switch (storage) { + case EvqGlobal: + case EvqVaryingIn: + case EvqVaryingOut: + case EvqUniform: + case EvqBuffer: + case EvqShared: + return true; + default: + return false; + } + } + +#ifdef GLSLANG_WEB + bool isPerView() const { return false; } + bool isTaskMemory() const { return false; } + bool isArrayedIo(EShLanguage language) const { return false; } +#else + bool isPerPrimitive() const { return perPrimitiveNV; } + bool isPerView() const { return perViewNV; } + bool isTaskMemory() const { return perTaskNV; } + + // True if this type of IO is supposed to be arrayed with extra level for per-vertex data + bool isArrayedIo(EShLanguage language) const + { + switch (language) { + case EShLangGeometry: + return isPipeInput(); + case EShLangTessControl: + return ! patch && (isPipeInput() || isPipeOutput()); + case EShLangTessEvaluation: + return ! patch && isPipeInput(); + case EShLangFragment: + return pervertexNV && isPipeInput(); + case EShLangMeshNV: + return ! perTaskNV && isPipeOutput(); + + default: + return false; + } + } +#endif + + // Implementing an embedded layout-qualifier class here, since C++ can't have a real class bitfield + void clearLayout() // all layout + { + clearUniformLayout(); + +#ifndef GLSLANG_WEB + layoutPushConstant = false; + layoutBufferReference = false; + layoutPassthrough = false; + layoutViewportRelative = false; + // -2048 as the default value indicating layoutSecondaryViewportRelative is not set + layoutSecondaryViewportRelativeOffset = -2048; + layoutShaderRecord = false; + layoutBufferReferenceAlign = layoutBufferReferenceAlignEnd; + layoutFormat = ElfNone; +#endif + + clearInterstageLayout(); + + layoutSpecConstantId = layoutSpecConstantIdEnd; + } + void clearInterstageLayout() + { + layoutLocation = layoutLocationEnd; + layoutComponent = layoutComponentEnd; +#ifndef GLSLANG_WEB + layoutIndex = layoutIndexEnd; + clearStreamLayout(); + clearXfbLayout(); +#endif + } + +#ifndef GLSLANG_WEB + void clearStreamLayout() + { + layoutStream = layoutStreamEnd; + } + void clearXfbLayout() + { + layoutXfbBuffer = layoutXfbBufferEnd; + layoutXfbStride = layoutXfbStrideEnd; + layoutXfbOffset = layoutXfbOffsetEnd; + } +#endif + + bool hasNonXfbLayout() const + { + return hasUniformLayout() || + hasAnyLocation() || + hasStream() || + hasFormat() || + isShaderRecord() || + isPushConstant() || + hasBufferReference(); + } + bool hasLayout() const + { + return hasNonXfbLayout() || + hasXfb(); + } + TLayoutMatrix layoutMatrix : 3; + TLayoutPacking layoutPacking : 4; + int layoutOffset; + int layoutAlign; + + unsigned int layoutLocation : 12; + static const unsigned int layoutLocationEnd = 0xFFF; + + unsigned int layoutComponent : 3; + static const unsigned int layoutComponentEnd = 4; + + unsigned int layoutSet : 7; + static const unsigned int layoutSetEnd = 0x3F; + + unsigned int layoutBinding : 16; + static const unsigned int layoutBindingEnd = 0xFFFF; + + unsigned int layoutIndex : 8; + static const unsigned int layoutIndexEnd = 0xFF; + + unsigned int layoutStream : 8; + static const unsigned int layoutStreamEnd = 0xFF; + + unsigned int layoutXfbBuffer : 4; + static const unsigned int layoutXfbBufferEnd = 0xF; + + unsigned int layoutXfbStride : 14; + static const unsigned int layoutXfbStrideEnd = 0x3FFF; + + unsigned int layoutXfbOffset : 13; + static const unsigned int layoutXfbOffsetEnd = 0x1FFF; + + unsigned int layoutAttachment : 8; // for input_attachment_index + static const unsigned int layoutAttachmentEnd = 0XFF; + + unsigned int layoutSpecConstantId : 11; + static const unsigned int layoutSpecConstantIdEnd = 0x7FF; + +#ifndef GLSLANG_WEB + // stored as log2 of the actual alignment value + unsigned int layoutBufferReferenceAlign : 6; + static const unsigned int layoutBufferReferenceAlignEnd = 0x3F; + + TLayoutFormat layoutFormat : 8; + + bool layoutPushConstant; + bool layoutBufferReference; + bool layoutPassthrough; + bool layoutViewportRelative; + int layoutSecondaryViewportRelativeOffset; + bool layoutShaderRecord; +#endif + + bool hasUniformLayout() const + { + return hasMatrix() || + hasPacking() || + hasOffset() || + hasBinding() || + hasSet() || + hasAlign(); + } + void clearUniformLayout() // only uniform specific + { + layoutMatrix = ElmNone; + layoutPacking = ElpNone; + layoutOffset = layoutNotSet; + layoutAlign = layoutNotSet; + + layoutSet = layoutSetEnd; + layoutBinding = layoutBindingEnd; +#ifndef GLSLANG_WEB + layoutAttachment = layoutAttachmentEnd; +#endif + } + + bool hasMatrix() const + { + return layoutMatrix != ElmNone; + } + bool hasPacking() const + { + return layoutPacking != ElpNone; + } + bool hasAlign() const + { + return layoutAlign != layoutNotSet; + } + bool hasAnyLocation() const + { + return hasLocation() || + hasComponent() || + hasIndex(); + } + bool hasLocation() const + { + return layoutLocation != layoutLocationEnd; + } + bool hasSet() const + { + return layoutSet != layoutSetEnd; + } + bool hasBinding() const + { + return layoutBinding != layoutBindingEnd; + } +#ifdef GLSLANG_WEB + bool hasOffset() const { return false; } + bool isNonPerspective() const { return false; } + bool hasIndex() const { return false; } + unsigned getIndex() const { return 0; } + bool hasComponent() const { return false; } + bool hasStream() const { return false; } + bool hasFormat() const { return false; } + bool hasXfb() const { return false; } + bool hasXfbBuffer() const { return false; } + bool hasXfbStride() const { return false; } + bool hasXfbOffset() const { return false; } + bool hasAttachment() const { return false; } + TLayoutFormat getFormat() const { return ElfNone; } + bool isPushConstant() const { return false; } + bool isShaderRecord() const { return false; } + bool hasBufferReference() const { return false; } + bool hasBufferReferenceAlign() const { return false; } + bool isNonUniform() const { return false; } +#else + bool hasOffset() const + { + return layoutOffset != layoutNotSet; + } + bool isNonPerspective() const { return nopersp; } + bool hasIndex() const + { + return layoutIndex != layoutIndexEnd; + } + unsigned getIndex() const { return layoutIndex; } + bool hasComponent() const + { + return layoutComponent != layoutComponentEnd; + } + bool hasStream() const + { + return layoutStream != layoutStreamEnd; + } + bool hasFormat() const + { + return layoutFormat != ElfNone; + } + bool hasXfb() const + { + return hasXfbBuffer() || + hasXfbStride() || + hasXfbOffset(); + } + bool hasXfbBuffer() const + { + return layoutXfbBuffer != layoutXfbBufferEnd; + } + bool hasXfbStride() const + { + return layoutXfbStride != layoutXfbStrideEnd; + } + bool hasXfbOffset() const + { + return layoutXfbOffset != layoutXfbOffsetEnd; + } + bool hasAttachment() const + { + return layoutAttachment != layoutAttachmentEnd; + } + TLayoutFormat getFormat() const { return layoutFormat; } + bool isPushConstant() const { return layoutPushConstant; } + bool isShaderRecord() const { return layoutShaderRecord; } + bool hasBufferReference() const { return layoutBufferReference; } + bool hasBufferReferenceAlign() const + { + return layoutBufferReferenceAlign != layoutBufferReferenceAlignEnd; + } + bool isNonUniform() const + { + return nonUniform; + } +#endif + bool hasSpecConstantId() const + { + // Not the same thing as being a specialization constant, this + // is just whether or not it was declared with an ID. + return layoutSpecConstantId != layoutSpecConstantIdEnd; + } + bool isSpecConstant() const + { + // True if type is a specialization constant, whether or not it + // had a specialization-constant ID, and false if it is not a + // true front-end constant. + return specConstant; + } + bool isFrontEndConstant() const + { + // True if the front-end knows the final constant value. + // This allows front-end constant folding. + return storage == EvqConst && ! specConstant; + } + bool isConstant() const + { + // True if is either kind of constant; specialization or regular. + return isFrontEndConstant() || isSpecConstant(); + } + void makeSpecConstant() + { + storage = EvqConst; + specConstant = true; + } + static const char* getLayoutPackingString(TLayoutPacking packing) + { + switch (packing) { + case ElpStd140: return "std140"; +#ifndef GLSLANG_WEB + case ElpPacked: return "packed"; + case ElpShared: return "shared"; + case ElpStd430: return "std430"; + case ElpScalar: return "scalar"; +#endif + default: return "none"; + } + } + static const char* getLayoutMatrixString(TLayoutMatrix m) + { + switch (m) { + case ElmColumnMajor: return "column_major"; + case ElmRowMajor: return "row_major"; + default: return "none"; + } + } +#ifdef GLSLANG_WEB + static const char* getLayoutFormatString(TLayoutFormat f) { return "none"; } +#else + static const char* getLayoutFormatString(TLayoutFormat f) + { + switch (f) { + case ElfRgba32f: return "rgba32f"; + case ElfRgba16f: return "rgba16f"; + case ElfRg32f: return "rg32f"; + case ElfRg16f: return "rg16f"; + case ElfR11fG11fB10f: return "r11f_g11f_b10f"; + case ElfR32f: return "r32f"; + case ElfR16f: return "r16f"; + case ElfRgba16: return "rgba16"; + case ElfRgb10A2: return "rgb10_a2"; + case ElfRgba8: return "rgba8"; + case ElfRg16: return "rg16"; + case ElfRg8: return "rg8"; + case ElfR16: return "r16"; + case ElfR8: return "r8"; + case ElfRgba16Snorm: return "rgba16_snorm"; + case ElfRgba8Snorm: return "rgba8_snorm"; + case ElfRg16Snorm: return "rg16_snorm"; + case ElfRg8Snorm: return "rg8_snorm"; + case ElfR16Snorm: return "r16_snorm"; + case ElfR8Snorm: return "r8_snorm"; + + case ElfRgba32i: return "rgba32i"; + case ElfRgba16i: return "rgba16i"; + case ElfRgba8i: return "rgba8i"; + case ElfRg32i: return "rg32i"; + case ElfRg16i: return "rg16i"; + case ElfRg8i: return "rg8i"; + case ElfR32i: return "r32i"; + case ElfR16i: return "r16i"; + case ElfR8i: return "r8i"; + + case ElfRgba32ui: return "rgba32ui"; + case ElfRgba16ui: return "rgba16ui"; + case ElfRgba8ui: return "rgba8ui"; + case ElfRg32ui: return "rg32ui"; + case ElfRg16ui: return "rg16ui"; + case ElfRgb10a2ui: return "rgb10_a2ui"; + case ElfRg8ui: return "rg8ui"; + case ElfR32ui: return "r32ui"; + case ElfR16ui: return "r16ui"; + case ElfR8ui: return "r8ui"; + case ElfR64ui: return "r64ui"; + case ElfR64i: return "r64i"; + default: return "none"; + } + } + static const char* getLayoutDepthString(TLayoutDepth d) + { + switch (d) { + case EldAny: return "depth_any"; + case EldGreater: return "depth_greater"; + case EldLess: return "depth_less"; + case EldUnchanged: return "depth_unchanged"; + default: return "none"; + } + } + static const char* getBlendEquationString(TBlendEquationShift e) + { + switch (e) { + case EBlendMultiply: return "blend_support_multiply"; + case EBlendScreen: return "blend_support_screen"; + case EBlendOverlay: return "blend_support_overlay"; + case EBlendDarken: return "blend_support_darken"; + case EBlendLighten: return "blend_support_lighten"; + case EBlendColordodge: return "blend_support_colordodge"; + case EBlendColorburn: return "blend_support_colorburn"; + case EBlendHardlight: return "blend_support_hardlight"; + case EBlendSoftlight: return "blend_support_softlight"; + case EBlendDifference: return "blend_support_difference"; + case EBlendExclusion: return "blend_support_exclusion"; + case EBlendHslHue: return "blend_support_hsl_hue"; + case EBlendHslSaturation: return "blend_support_hsl_saturation"; + case EBlendHslColor: return "blend_support_hsl_color"; + case EBlendHslLuminosity: return "blend_support_hsl_luminosity"; + case EBlendAllEquations: return "blend_support_all_equations"; + default: return "unknown"; + } + } + static const char* getGeometryString(TLayoutGeometry geometry) + { + switch (geometry) { + case ElgPoints: return "points"; + case ElgLines: return "lines"; + case ElgLinesAdjacency: return "lines_adjacency"; + case ElgLineStrip: return "line_strip"; + case ElgTriangles: return "triangles"; + case ElgTrianglesAdjacency: return "triangles_adjacency"; + case ElgTriangleStrip: return "triangle_strip"; + case ElgQuads: return "quads"; + case ElgIsolines: return "isolines"; + default: return "none"; + } + } + static const char* getVertexSpacingString(TVertexSpacing spacing) + { + switch (spacing) { + case EvsEqual: return "equal_spacing"; + case EvsFractionalEven: return "fractional_even_spacing"; + case EvsFractionalOdd: return "fractional_odd_spacing"; + default: return "none"; + } + } + static const char* getVertexOrderString(TVertexOrder order) + { + switch (order) { + case EvoCw: return "cw"; + case EvoCcw: return "ccw"; + default: return "none"; + } + } + static int mapGeometryToSize(TLayoutGeometry geometry) + { + switch (geometry) { + case ElgPoints: return 1; + case ElgLines: return 2; + case ElgLinesAdjacency: return 4; + case ElgTriangles: return 3; + case ElgTrianglesAdjacency: return 6; + default: return 0; + } + } + static const char* getInterlockOrderingString(TInterlockOrdering order) + { + switch (order) { + case EioPixelInterlockOrdered: return "pixel_interlock_ordered"; + case EioPixelInterlockUnordered: return "pixel_interlock_unordered"; + case EioSampleInterlockOrdered: return "sample_interlock_ordered"; + case EioSampleInterlockUnordered: return "sample_interlock_unordered"; + case EioShadingRateInterlockOrdered: return "shading_rate_interlock_ordered"; + case EioShadingRateInterlockUnordered: return "shading_rate_interlock_unordered"; + default: return "none"; + } + } +#endif +}; + +// Qualifiers that don't need to be keep per object. They have shader scope, not object scope. +// So, they will not be part of TType, TQualifier, etc. +struct TShaderQualifiers { + TLayoutGeometry geometry; // geometry/tessellation shader in/out primitives + bool pixelCenterInteger; // fragment shader + bool originUpperLeft; // fragment shader + int invocations; + int vertices; // for tessellation "vertices", geometry & mesh "max_vertices" + TVertexSpacing spacing; + TVertexOrder order; + bool pointMode; + int localSize[3]; // compute shader + bool localSizeNotDefault[3]; // compute shader + int localSizeSpecId[3]; // compute shader specialization id for gl_WorkGroupSize +#ifndef GLSLANG_WEB + bool earlyFragmentTests; // fragment input + bool postDepthCoverage; // fragment input + TLayoutDepth layoutDepth; + bool blendEquation; // true if any blend equation was specified + int numViews; // multiview extenstions + TInterlockOrdering interlockOrdering; + bool layoutOverrideCoverage; // true if layout override_coverage set + bool layoutDerivativeGroupQuads; // true if layout derivative_group_quadsNV set + bool layoutDerivativeGroupLinear; // true if layout derivative_group_linearNV set + int primitives; // mesh shader "max_primitives"DerivativeGroupLinear; // true if layout derivative_group_linearNV set + bool layoutPrimitiveCulling; // true if layout primitive_culling set + TLayoutDepth getDepth() const { return layoutDepth; } +#else + TLayoutDepth getDepth() const { return EldNone; } +#endif + + void init() + { + geometry = ElgNone; + originUpperLeft = false; + pixelCenterInteger = false; + invocations = TQualifier::layoutNotSet; + vertices = TQualifier::layoutNotSet; + spacing = EvsNone; + order = EvoNone; + pointMode = false; + localSize[0] = 1; + localSize[1] = 1; + localSize[2] = 1; + localSizeNotDefault[0] = false; + localSizeNotDefault[1] = false; + localSizeNotDefault[2] = false; + localSizeSpecId[0] = TQualifier::layoutNotSet; + localSizeSpecId[1] = TQualifier::layoutNotSet; + localSizeSpecId[2] = TQualifier::layoutNotSet; +#ifndef GLSLANG_WEB + earlyFragmentTests = false; + postDepthCoverage = false; + layoutDepth = EldNone; + blendEquation = false; + numViews = TQualifier::layoutNotSet; + layoutOverrideCoverage = false; + layoutDerivativeGroupQuads = false; + layoutDerivativeGroupLinear = false; + layoutPrimitiveCulling = false; + primitives = TQualifier::layoutNotSet; + interlockOrdering = EioNone; +#endif + } + +#ifdef GLSLANG_WEB + bool hasBlendEquation() const { return false; } +#else + bool hasBlendEquation() const { return blendEquation; } +#endif + + // Merge in characteristics from the 'src' qualifier. They can override when + // set, but never erase when not set. + void merge(const TShaderQualifiers& src) + { + if (src.geometry != ElgNone) + geometry = src.geometry; + if (src.pixelCenterInteger) + pixelCenterInteger = src.pixelCenterInteger; + if (src.originUpperLeft) + originUpperLeft = src.originUpperLeft; + if (src.invocations != TQualifier::layoutNotSet) + invocations = src.invocations; + if (src.vertices != TQualifier::layoutNotSet) + vertices = src.vertices; + if (src.spacing != EvsNone) + spacing = src.spacing; + if (src.order != EvoNone) + order = src.order; + if (src.pointMode) + pointMode = true; + for (int i = 0; i < 3; ++i) { + if (src.localSize[i] > 1) + localSize[i] = src.localSize[i]; + } + for (int i = 0; i < 3; ++i) { + localSizeNotDefault[i] = src.localSizeNotDefault[i] || localSizeNotDefault[i]; + } + for (int i = 0; i < 3; ++i) { + if (src.localSizeSpecId[i] != TQualifier::layoutNotSet) + localSizeSpecId[i] = src.localSizeSpecId[i]; + } +#ifndef GLSLANG_WEB + if (src.earlyFragmentTests) + earlyFragmentTests = true; + if (src.postDepthCoverage) + postDepthCoverage = true; + if (src.layoutDepth) + layoutDepth = src.layoutDepth; + if (src.blendEquation) + blendEquation = src.blendEquation; + if (src.numViews != TQualifier::layoutNotSet) + numViews = src.numViews; + if (src.layoutOverrideCoverage) + layoutOverrideCoverage = src.layoutOverrideCoverage; + if (src.layoutDerivativeGroupQuads) + layoutDerivativeGroupQuads = src.layoutDerivativeGroupQuads; + if (src.layoutDerivativeGroupLinear) + layoutDerivativeGroupLinear = src.layoutDerivativeGroupLinear; + if (src.primitives != TQualifier::layoutNotSet) + primitives = src.primitives; + if (src.interlockOrdering != EioNone) + interlockOrdering = src.interlockOrdering; + if (src.layoutPrimitiveCulling) + layoutPrimitiveCulling = src.layoutPrimitiveCulling; +#endif + } +}; + +// +// TPublicType is just temporarily used while parsing and not quite the same +// information kept per node in TType. Due to the bison stack, it can't have +// types that it thinks have non-trivial constructors. It should +// just be used while recognizing the grammar, not anything else. +// Once enough is known about the situation, the proper information +// moved into a TType, or the parse context, etc. +// +class TPublicType { +public: + TBasicType basicType; + TSampler sampler; + TQualifier qualifier; + TShaderQualifiers shaderQualifiers; + int vectorSize : 4; + int matrixCols : 4; + int matrixRows : 4; + bool coopmat : 1; + TArraySizes* arraySizes; + const TType* userDef; + TSourceLoc loc; + TArraySizes* typeParameters; + +#ifdef GLSLANG_WEB + bool isCoopmat() const { return false; } +#else + bool isCoopmat() const { return coopmat; } +#endif + + void initType(const TSourceLoc& l) + { + basicType = EbtVoid; + vectorSize = 1; + matrixRows = 0; + matrixCols = 0; + arraySizes = nullptr; + userDef = nullptr; + loc = l; + typeParameters = nullptr; + coopmat = false; + } + + void initQualifiers(bool global = false) + { + qualifier.clear(); + if (global) + qualifier.storage = EvqGlobal; + } + + void init(const TSourceLoc& l, bool global = false) + { + initType(l); + sampler.clear(); + initQualifiers(global); + shaderQualifiers.init(); + } + + void setVector(int s) + { + matrixRows = 0; + matrixCols = 0; + vectorSize = s; + } + + void setMatrix(int c, int r) + { + matrixRows = r; + matrixCols = c; + vectorSize = 0; + } + + bool isScalar() const + { + return matrixCols == 0 && vectorSize == 1 && arraySizes == nullptr && userDef == nullptr; + } + + // "Image" is a superset of "Subpass" + bool isImage() const { return basicType == EbtSampler && sampler.isImage(); } + bool isSubpass() const { return basicType == EbtSampler && sampler.isSubpass(); } +}; + +// +// Base class for things that have a type. +// +class TType { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + // for "empty" type (no args) or simple scalar/vector/matrix + explicit TType(TBasicType t = EbtVoid, TStorageQualifier q = EvqTemporary, int vs = 1, int mc = 0, int mr = 0, + bool isVector = false) : + basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), vector1(isVector && vs == 1), coopmat(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr) + { + sampler.clear(); + qualifier.clear(); + qualifier.storage = q; + assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices + } + // for explicit precision qualifier + TType(TBasicType t, TStorageQualifier q, TPrecisionQualifier p, int vs = 1, int mc = 0, int mr = 0, + bool isVector = false) : + basicType(t), vectorSize(vs), matrixCols(mc), matrixRows(mr), vector1(isVector && vs == 1), coopmat(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(nullptr) + { + sampler.clear(); + qualifier.clear(); + qualifier.storage = q; + qualifier.precision = p; + assert(p >= EpqNone && p <= EpqHigh); + assert(!(isMatrix() && vectorSize != 0)); // prevent vectorSize != 0 on matrices + } + // for turning a TPublicType into a TType, using a shallow copy + explicit TType(const TPublicType& p) : + basicType(p.basicType), + vectorSize(p.vectorSize), matrixCols(p.matrixCols), matrixRows(p.matrixRows), vector1(false), coopmat(p.coopmat), + arraySizes(p.arraySizes), structure(nullptr), fieldName(nullptr), typeName(nullptr), typeParameters(p.typeParameters) + { + if (basicType == EbtSampler) + sampler = p.sampler; + else + sampler.clear(); + qualifier = p.qualifier; + if (p.userDef) { + if (p.userDef->basicType == EbtReference) { + basicType = EbtReference; + referentType = p.userDef->referentType; + } else { + structure = p.userDef->getWritableStruct(); // public type is short-lived; there are no sharing issues + } + typeName = NewPoolTString(p.userDef->getTypeName().c_str()); + } + if (p.isCoopmat() && p.typeParameters && p.typeParameters->getNumDims() > 0) { + int numBits = p.typeParameters->getDimSize(0); + if (p.basicType == EbtFloat && numBits == 16) { + basicType = EbtFloat16; + qualifier.precision = EpqNone; + } else if (p.basicType == EbtUint && numBits == 8) { + basicType = EbtUint8; + qualifier.precision = EpqNone; + } else if (p.basicType == EbtInt && numBits == 8) { + basicType = EbtInt8; + qualifier.precision = EpqNone; + } + } + } + // for construction of sampler types + TType(const TSampler& sampler, TStorageQualifier q = EvqUniform, TArraySizes* as = nullptr) : + basicType(EbtSampler), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false), + arraySizes(as), structure(nullptr), fieldName(nullptr), typeName(nullptr), + sampler(sampler), typeParameters(nullptr) + { + qualifier.clear(); + qualifier.storage = q; + } + // to efficiently make a dereferenced type + // without ever duplicating the outer structure that will be thrown away + // and using only shallow copy + TType(const TType& type, int derefIndex, bool rowMajor = false) + { + if (type.isArray()) { + shallowCopy(type); + if (type.getArraySizes()->getNumDims() == 1) { + arraySizes = nullptr; + } else { + // want our own copy of the array, so we can edit it + arraySizes = new TArraySizes; + arraySizes->copyDereferenced(*type.arraySizes); + } + } else if (type.basicType == EbtStruct || type.basicType == EbtBlock) { + // do a structure dereference + const TTypeList& memberList = *type.getStruct(); + shallowCopy(*memberList[derefIndex].type); + return; + } else { + // do a vector/matrix dereference + shallowCopy(type); + if (matrixCols > 0) { + // dereference from matrix to vector + if (rowMajor) + vectorSize = matrixCols; + else + vectorSize = matrixRows; + matrixCols = 0; + matrixRows = 0; + if (vectorSize == 1) + vector1 = true; + } else if (isVector()) { + // dereference from vector to scalar + vectorSize = 1; + vector1 = false; + } else if (isCoopMat()) { + coopmat = false; + typeParameters = nullptr; + } + } + } + // for making structures, ... + TType(TTypeList* userDef, const TString& n) : + basicType(EbtStruct), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false), + arraySizes(nullptr), structure(userDef), fieldName(nullptr), typeParameters(nullptr) + { + sampler.clear(); + qualifier.clear(); + typeName = NewPoolTString(n.c_str()); + } + // For interface blocks + TType(TTypeList* userDef, const TString& n, const TQualifier& q) : + basicType(EbtBlock), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), coopmat(false), + qualifier(q), arraySizes(nullptr), structure(userDef), fieldName(nullptr), typeParameters(nullptr) + { + sampler.clear(); + typeName = NewPoolTString(n.c_str()); + } + // for block reference (first parameter must be EbtReference) + explicit TType(TBasicType t, const TType &p, const TString& n) : + basicType(t), vectorSize(1), matrixCols(0), matrixRows(0), vector1(false), + arraySizes(nullptr), structure(nullptr), fieldName(nullptr), typeName(nullptr) + { + assert(t == EbtReference); + typeName = NewPoolTString(n.c_str()); + qualifier.clear(); + qualifier.storage = p.qualifier.storage; + referentType = p.clone(); + } + virtual ~TType() {} + + // Not for use across pool pops; it will cause multiple instances of TType to point to the same information. + // This only works if that information (like a structure's list of types) does not change and + // the instances are sharing the same pool. + void shallowCopy(const TType& copyOf) + { + basicType = copyOf.basicType; + sampler = copyOf.sampler; + qualifier = copyOf.qualifier; + vectorSize = copyOf.vectorSize; + matrixCols = copyOf.matrixCols; + matrixRows = copyOf.matrixRows; + vector1 = copyOf.vector1; + arraySizes = copyOf.arraySizes; // copying the pointer only, not the contents + fieldName = copyOf.fieldName; + typeName = copyOf.typeName; + if (isStruct()) { + structure = copyOf.structure; + } else { + referentType = copyOf.referentType; + } + typeParameters = copyOf.typeParameters; + coopmat = copyOf.isCoopMat(); + } + + // Make complete copy of the whole type graph rooted at 'copyOf'. + void deepCopy(const TType& copyOf) + { + TMap copied; // to enable copying a type graph as a graph, not a tree + deepCopy(copyOf, copied); + } + + // Recursively make temporary + void makeTemporary() + { + getQualifier().makeTemporary(); + + if (isStruct()) + for (unsigned int i = 0; i < structure->size(); ++i) + (*structure)[i].type->makeTemporary(); + } + + TType* clone() const + { + TType *newType = new TType(); + newType->deepCopy(*this); + + return newType; + } + + void makeVector() { vector1 = true; } + + virtual void hideMember() { basicType = EbtVoid; vectorSize = 1; } + virtual bool hiddenMember() const { return basicType == EbtVoid; } + + virtual void setFieldName(const TString& n) { fieldName = NewPoolTString(n.c_str()); } + virtual const TString& getTypeName() const + { + assert(typeName); + return *typeName; + } + + virtual const TString& getFieldName() const + { + assert(fieldName); + return *fieldName; + } + TShaderInterface getShaderInterface() const + { + if (basicType != EbtBlock) + return EsiNone; + + switch (qualifier.storage) { + default: + return EsiNone; + case EvqVaryingIn: + return EsiInput; + case EvqVaryingOut: + return EsiOutput; + case EvqUniform: + case EvqBuffer: + return EsiUniform; + } + } + + virtual TBasicType getBasicType() const { return basicType; } + virtual const TSampler& getSampler() const { return sampler; } + virtual TSampler& getSampler() { return sampler; } + + virtual TQualifier& getQualifier() { return qualifier; } + virtual const TQualifier& getQualifier() const { return qualifier; } + + virtual int getVectorSize() const { return vectorSize; } // returns 1 for either scalar or vector of size 1, valid for both + virtual int getMatrixCols() const { return matrixCols; } + virtual int getMatrixRows() const { return matrixRows; } + virtual int getOuterArraySize() const { return arraySizes->getOuterSize(); } + virtual TIntermTyped* getOuterArrayNode() const { return arraySizes->getOuterNode(); } + virtual int getCumulativeArraySize() const { return arraySizes->getCumulativeSize(); } +#ifdef GLSLANG_WEB + bool isArrayOfArrays() const { return false; } +#else + bool isArrayOfArrays() const { return arraySizes != nullptr && arraySizes->getNumDims() > 1; } +#endif + virtual int getImplicitArraySize() const { return arraySizes->getImplicitSize(); } + virtual const TArraySizes* getArraySizes() const { return arraySizes; } + virtual TArraySizes* getArraySizes() { return arraySizes; } + virtual TType* getReferentType() const { return referentType; } + virtual const TArraySizes* getTypeParameters() const { return typeParameters; } + virtual TArraySizes* getTypeParameters() { return typeParameters; } + + virtual bool isScalar() const { return ! isVector() && ! isMatrix() && ! isStruct() && ! isArray(); } + virtual bool isScalarOrVec1() const { return isScalar() || vector1; } + virtual bool isVector() const { return vectorSize > 1 || vector1; } + virtual bool isMatrix() const { return matrixCols ? true : false; } + virtual bool isArray() const { return arraySizes != nullptr; } + virtual bool isSizedArray() const { return isArray() && arraySizes->isSized(); } + virtual bool isUnsizedArray() const { return isArray() && !arraySizes->isSized(); } + virtual bool isArrayVariablyIndexed() const { assert(isArray()); return arraySizes->isVariablyIndexed(); } + virtual void setArrayVariablyIndexed() { assert(isArray()); arraySizes->setVariablyIndexed(); } + virtual void updateImplicitArraySize(int size) { assert(isArray()); arraySizes->updateImplicitSize(size); } + virtual bool isStruct() const { return basicType == EbtStruct || basicType == EbtBlock; } + virtual bool isFloatingDomain() const { return basicType == EbtFloat || basicType == EbtDouble || basicType == EbtFloat16; } + virtual bool isIntegerDomain() const + { + switch (basicType) { + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtAtomicUint: + return true; + default: + break; + } + return false; + } + virtual bool isOpaque() const { return basicType == EbtSampler +#ifndef GLSLANG_WEB + || basicType == EbtAtomicUint || basicType == EbtAccStruct || basicType == EbtRayQuery +#endif + ; } + virtual bool isBuiltIn() const { return getQualifier().builtIn != EbvNone; } + + // "Image" is a superset of "Subpass" + virtual bool isImage() const { return basicType == EbtSampler && getSampler().isImage(); } + virtual bool isSubpass() const { return basicType == EbtSampler && getSampler().isSubpass(); } + virtual bool isTexture() const { return basicType == EbtSampler && getSampler().isTexture(); } + // Check the block-name convention of creating a block without populating it's members: + virtual bool isUnusableName() const { return isStruct() && structure == nullptr; } + virtual bool isParameterized() const { return typeParameters != nullptr; } +#ifdef GLSLANG_WEB + bool isAtomic() const { return false; } + bool isCoopMat() const { return false; } + bool isReference() const { return false; } +#else + bool isAtomic() const { return basicType == EbtAtomicUint; } + bool isCoopMat() const { return coopmat; } + bool isReference() const { return getBasicType() == EbtReference; } +#endif + + // return true if this type contains any subtype which satisfies the given predicate. + template + bool contains(P predicate) const + { + if (predicate(this)) + return true; + + const auto hasa = [predicate](const TTypeLoc& tl) { return tl.type->contains(predicate); }; + + return isStruct() && std::any_of(structure->begin(), structure->end(), hasa); + } + + // Recursively checks if the type contains the given basic type + virtual bool containsBasicType(TBasicType checkType) const + { + return contains([checkType](const TType* t) { return t->basicType == checkType; } ); + } + + // Recursively check the structure for any arrays, needed for some error checks + virtual bool containsArray() const + { + return contains([](const TType* t) { return t->isArray(); } ); + } + + // Check the structure for any structures, needed for some error checks + virtual bool containsStructure() const + { + return contains([this](const TType* t) { return t != this && t->isStruct(); } ); + } + + // Recursively check the structure for any unsized arrays, needed for triggering a copyUp(). + virtual bool containsUnsizedArray() const + { + return contains([](const TType* t) { return t->isUnsizedArray(); } ); + } + + virtual bool containsOpaque() const + { + return contains([](const TType* t) { return t->isOpaque(); } ); + } + + // Recursively checks if the type contains a built-in variable + virtual bool containsBuiltIn() const + { + return contains([](const TType* t) { return t->isBuiltIn(); } ); + } + + virtual bool containsNonOpaque() const + { + const auto nonOpaque = [](const TType* t) { + switch (t->basicType) { + case EbtVoid: + case EbtFloat: + case EbtDouble: + case EbtFloat16: + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtBool: + case EbtReference: + return true; + default: + return false; + } + }; + + return contains(nonOpaque); + } + + virtual bool containsSpecializationSize() const + { + return contains([](const TType* t) { return t->isArray() && t->arraySizes->isOuterSpecialization(); } ); + } + +#ifdef GLSLANG_WEB + bool containsDouble() const { return false; } + bool contains16BitFloat() const { return false; } + bool contains64BitInt() const { return false; } + bool contains16BitInt() const { return false; } + bool contains8BitInt() const { return false; } + bool containsCoopMat() const { return false; } + bool containsReference() const { return false; } +#else + bool containsDouble() const + { + return containsBasicType(EbtDouble); + } + bool contains16BitFloat() const + { + return containsBasicType(EbtFloat16); + } + bool contains64BitInt() const + { + return containsBasicType(EbtInt64) || containsBasicType(EbtUint64); + } + bool contains16BitInt() const + { + return containsBasicType(EbtInt16) || containsBasicType(EbtUint16); + } + bool contains8BitInt() const + { + return containsBasicType(EbtInt8) || containsBasicType(EbtUint8); + } + bool containsCoopMat() const + { + return contains([](const TType* t) { return t->coopmat; } ); + } + bool containsReference() const + { + return containsBasicType(EbtReference); + } +#endif + + // Array editing methods. Array descriptors can be shared across + // type instances. This allows all uses of the same array + // to be updated at once. E.g., all nodes can be explicitly sized + // by tracking and correcting one implicit size. Or, all nodes + // can get the explicit size on a redeclaration that gives size. + // + // N.B.: Don't share with the shared symbol tables (symbols are + // marked as isReadOnly(). Such symbols with arrays that will be + // edited need to copyUp() on first use, so that + // A) the edits don't effect the shared symbol table, and + // B) the edits are shared across all users. + void updateArraySizes(const TType& type) + { + // For when we may already be sharing existing array descriptors, + // keeping the pointers the same, just updating the contents. + assert(arraySizes != nullptr); + assert(type.arraySizes != nullptr); + *arraySizes = *type.arraySizes; + } + void copyArraySizes(const TArraySizes& s) + { + // For setting a fresh new set of array sizes, not yet worrying about sharing. + arraySizes = new TArraySizes; + *arraySizes = s; + } + void transferArraySizes(TArraySizes* s) + { + // For setting an already allocated set of sizes that this type can use + // (no copy made). + arraySizes = s; + } + void clearArraySizes() + { + arraySizes = nullptr; + } + + // Add inner array sizes, to any existing sizes, via copy; the + // sizes passed in can still be reused for other purposes. + void copyArrayInnerSizes(const TArraySizes* s) + { + if (s != nullptr) { + if (arraySizes == nullptr) + copyArraySizes(*s); + else + arraySizes->addInnerSizes(*s); + } + } + void changeOuterArraySize(int s) { arraySizes->changeOuterSize(s); } + + // Recursively make the implicit array size the explicit array size. + // Expicit arrays are compile-time or link-time sized, never run-time sized. + // Sometimes, policy calls for an array to be run-time sized even if it was + // never variably indexed: Don't turn a 'skipNonvariablyIndexed' array into + // an explicit array. + void adoptImplicitArraySizes(bool skipNonvariablyIndexed) + { + if (isUnsizedArray() && !(skipNonvariablyIndexed || isArrayVariablyIndexed())) + changeOuterArraySize(getImplicitArraySize()); + // For multi-dim per-view arrays, set unsized inner dimension size to 1 + if (qualifier.isPerView() && arraySizes && arraySizes->isInnerUnsized()) + arraySizes->clearInnerUnsized(); + if (isStruct() && structure->size() > 0) { + int lastMember = (int)structure->size() - 1; + for (int i = 0; i < lastMember; ++i) + (*structure)[i].type->adoptImplicitArraySizes(false); + // implement the "last member of an SSBO" policy + (*structure)[lastMember].type->adoptImplicitArraySizes(getQualifier().storage == EvqBuffer); + } + } + + + void updateTypeParameters(const TType& type) + { + // For when we may already be sharing existing array descriptors, + // keeping the pointers the same, just updating the contents. + assert(typeParameters != nullptr); + assert(type.typeParameters != nullptr); + *typeParameters = *type.typeParameters; + } + void copyTypeParameters(const TArraySizes& s) + { + // For setting a fresh new set of type parameters, not yet worrying about sharing. + typeParameters = new TArraySizes; + *typeParameters = s; + } + void transferTypeParameters(TArraySizes* s) + { + // For setting an already allocated set of sizes that this type can use + // (no copy made). + typeParameters = s; + } + void clearTypeParameters() + { + typeParameters = nullptr; + } + + // Add inner array sizes, to any existing sizes, via copy; the + // sizes passed in can still be reused for other purposes. + void copyTypeParametersInnerSizes(const TArraySizes* s) + { + if (s != nullptr) { + if (typeParameters == nullptr) + copyTypeParameters(*s); + else + typeParameters->addInnerSizes(*s); + } + } + + + + const char* getBasicString() const + { + return TType::getBasicString(basicType); + } + + static const char* getBasicString(TBasicType t) + { + switch (t) { + case EbtFloat: return "float"; + case EbtInt: return "int"; + case EbtUint: return "uint"; + case EbtSampler: return "sampler/image"; +#ifndef GLSLANG_WEB + case EbtVoid: return "void"; + case EbtDouble: return "double"; + case EbtFloat16: return "float16_t"; + case EbtInt8: return "int8_t"; + case EbtUint8: return "uint8_t"; + case EbtInt16: return "int16_t"; + case EbtUint16: return "uint16_t"; + case EbtInt64: return "int64_t"; + case EbtUint64: return "uint64_t"; + case EbtBool: return "bool"; + case EbtAtomicUint: return "atomic_uint"; + case EbtStruct: return "structure"; + case EbtBlock: return "block"; + case EbtAccStruct: return "accelerationStructureNV"; + case EbtRayQuery: return "rayQueryEXT"; + case EbtReference: return "reference"; + case EbtString: return "string"; +#endif + default: return "unknown type"; + } + } + +#ifdef GLSLANG_WEB + TString getCompleteString() const { return ""; } + const char* getStorageQualifierString() const { return ""; } + const char* getBuiltInVariableString() const { return ""; } + const char* getPrecisionQualifierString() const { return ""; } + TString getBasicTypeString() const { return ""; } +#else + TString getCompleteString() const + { + TString typeString; + + const auto appendStr = [&](const char* s) { typeString.append(s); }; + const auto appendUint = [&](unsigned int u) { typeString.append(std::to_string(u).c_str()); }; + const auto appendInt = [&](int i) { typeString.append(std::to_string(i).c_str()); }; + + if (qualifier.hasLayout()) { + // To reduce noise, skip this if the only layout is an xfb_buffer + // with no triggering xfb_offset. + TQualifier noXfbBuffer = qualifier; + noXfbBuffer.layoutXfbBuffer = TQualifier::layoutXfbBufferEnd; + if (noXfbBuffer.hasLayout()) { + appendStr("layout("); + if (qualifier.hasAnyLocation()) { + appendStr(" location="); + appendUint(qualifier.layoutLocation); + if (qualifier.hasComponent()) { + appendStr(" component="); + appendUint(qualifier.layoutComponent); + } + if (qualifier.hasIndex()) { + appendStr(" index="); + appendUint(qualifier.layoutIndex); + } + } + if (qualifier.hasSet()) { + appendStr(" set="); + appendUint(qualifier.layoutSet); + } + if (qualifier.hasBinding()) { + appendStr(" binding="); + appendUint(qualifier.layoutBinding); + } + if (qualifier.hasStream()) { + appendStr(" stream="); + appendUint(qualifier.layoutStream); + } + if (qualifier.hasMatrix()) { + appendStr(" "); + appendStr(TQualifier::getLayoutMatrixString(qualifier.layoutMatrix)); + } + if (qualifier.hasPacking()) { + appendStr(" "); + appendStr(TQualifier::getLayoutPackingString(qualifier.layoutPacking)); + } + if (qualifier.hasOffset()) { + appendStr(" offset="); + appendInt(qualifier.layoutOffset); + } + if (qualifier.hasAlign()) { + appendStr(" align="); + appendInt(qualifier.layoutAlign); + } + if (qualifier.hasFormat()) { + appendStr(" "); + appendStr(TQualifier::getLayoutFormatString(qualifier.layoutFormat)); + } + if (qualifier.hasXfbBuffer() && qualifier.hasXfbOffset()) { + appendStr(" xfb_buffer="); + appendUint(qualifier.layoutXfbBuffer); + } + if (qualifier.hasXfbOffset()) { + appendStr(" xfb_offset="); + appendUint(qualifier.layoutXfbOffset); + } + if (qualifier.hasXfbStride()) { + appendStr(" xfb_stride="); + appendUint(qualifier.layoutXfbStride); + } + if (qualifier.hasAttachment()) { + appendStr(" input_attachment_index="); + appendUint(qualifier.layoutAttachment); + } + if (qualifier.hasSpecConstantId()) { + appendStr(" constant_id="); + appendUint(qualifier.layoutSpecConstantId); + } + if (qualifier.layoutPushConstant) + appendStr(" push_constant"); + if (qualifier.layoutBufferReference) + appendStr(" buffer_reference"); + if (qualifier.hasBufferReferenceAlign()) { + appendStr(" buffer_reference_align="); + appendUint(1u << qualifier.layoutBufferReferenceAlign); + } + + if (qualifier.layoutPassthrough) + appendStr(" passthrough"); + if (qualifier.layoutViewportRelative) + appendStr(" layoutViewportRelative"); + if (qualifier.layoutSecondaryViewportRelativeOffset != -2048) { + appendStr(" layoutSecondaryViewportRelativeOffset="); + appendInt(qualifier.layoutSecondaryViewportRelativeOffset); + } + if (qualifier.layoutShaderRecord) + appendStr(" shaderRecordNV"); + + appendStr(")"); + } + } + + if (qualifier.invariant) + appendStr(" invariant"); + if (qualifier.noContraction) + appendStr(" noContraction"); + if (qualifier.centroid) + appendStr(" centroid"); + if (qualifier.smooth) + appendStr(" smooth"); + if (qualifier.flat) + appendStr(" flat"); + if (qualifier.nopersp) + appendStr(" noperspective"); + if (qualifier.explicitInterp) + appendStr(" __explicitInterpAMD"); + if (qualifier.pervertexNV) + appendStr(" pervertexNV"); + if (qualifier.perPrimitiveNV) + appendStr(" perprimitiveNV"); + if (qualifier.perViewNV) + appendStr(" perviewNV"); + if (qualifier.perTaskNV) + appendStr(" taskNV"); + if (qualifier.patch) + appendStr(" patch"); + if (qualifier.sample) + appendStr(" sample"); + if (qualifier.coherent) + appendStr(" coherent"); + if (qualifier.devicecoherent) + appendStr(" devicecoherent"); + if (qualifier.queuefamilycoherent) + appendStr(" queuefamilycoherent"); + if (qualifier.workgroupcoherent) + appendStr(" workgroupcoherent"); + if (qualifier.subgroupcoherent) + appendStr(" subgroupcoherent"); + if (qualifier.shadercallcoherent) + appendStr(" shadercallcoherent"); + if (qualifier.nonprivate) + appendStr(" nonprivate"); + if (qualifier.volatil) + appendStr(" volatile"); + if (qualifier.restrict) + appendStr(" restrict"); + if (qualifier.readonly) + appendStr(" readonly"); + if (qualifier.writeonly) + appendStr(" writeonly"); + if (qualifier.specConstant) + appendStr(" specialization-constant"); + if (qualifier.nonUniform) + appendStr(" nonuniform"); + appendStr(" "); + appendStr(getStorageQualifierString()); + if (isArray()) { + for(int i = 0; i < (int)arraySizes->getNumDims(); ++i) { + int size = arraySizes->getDimSize(i); + if (size == UnsizedArraySize && i == 0 && arraySizes->isVariablyIndexed()) + appendStr(" runtime-sized array of"); + else { + if (size == UnsizedArraySize) { + appendStr(" unsized"); + if (i == 0) { + appendStr(" "); + appendInt(arraySizes->getImplicitSize()); + } + } else { + appendStr(" "); + appendInt(arraySizes->getDimSize(i)); + } + appendStr("-element array of"); + } + } + } + if (isParameterized()) { + appendStr("<"); + for(int i = 0; i < (int)typeParameters->getNumDims(); ++i) { + appendInt(typeParameters->getDimSize(i)); + if (i != (int)typeParameters->getNumDims() - 1) + appendStr(", "); + } + appendStr(">"); + } + if (qualifier.precision != EpqNone) { + appendStr(" "); + appendStr(getPrecisionQualifierString()); + } + if (isMatrix()) { + appendStr(" "); + appendInt(matrixCols); + appendStr("X"); + appendInt(matrixRows); + appendStr(" matrix of"); + } else if (isVector()) { + appendStr(" "); + appendInt(vectorSize); + appendStr("-component vector of"); + } + + appendStr(" "); + typeString.append(getBasicTypeString()); + + if (qualifier.builtIn != EbvNone) { + appendStr(" "); + appendStr(getBuiltInVariableString()); + } + + // Add struct/block members + if (isStruct() && structure) { + appendStr("{"); + bool hasHiddenMember = true; + for (size_t i = 0; i < structure->size(); ++i) { + if (! (*structure)[i].type->hiddenMember()) { + if (!hasHiddenMember) + appendStr(", "); + typeString.append((*structure)[i].type->getCompleteString()); + typeString.append(" "); + typeString.append((*structure)[i].type->getFieldName()); + hasHiddenMember = false; + } + } + appendStr("}"); + } + + return typeString; + } + + TString getBasicTypeString() const + { + if (basicType == EbtSampler) + return sampler.getString(); + else + return getBasicString(); + } + + const char* getStorageQualifierString() const { return GetStorageQualifierString(qualifier.storage); } + const char* getBuiltInVariableString() const { return GetBuiltInVariableString(qualifier.builtIn); } + const char* getPrecisionQualifierString() const { return GetPrecisionQualifierString(qualifier.precision); } +#endif + + const TTypeList* getStruct() const { assert(isStruct()); return structure; } + void setStruct(TTypeList* s) { assert(isStruct()); structure = s; } + TTypeList* getWritableStruct() const { assert(isStruct()); return structure; } // This should only be used when known to not be sharing with other threads + void setBasicType(const TBasicType& t) { basicType = t; } + + int computeNumComponents() const + { + int components = 0; + + if (getBasicType() == EbtStruct || getBasicType() == EbtBlock) { + for (TTypeList::const_iterator tl = getStruct()->begin(); tl != getStruct()->end(); tl++) + components += ((*tl).type)->computeNumComponents(); + } else if (matrixCols) + components = matrixCols * matrixRows; + else + components = vectorSize; + + if (arraySizes != nullptr) { + components *= arraySizes->getCumulativeSize(); + } + + return components; + } + + // append this type's mangled name to the passed in 'name' + void appendMangledName(TString& name) const + { + buildMangledName(name); + name += ';' ; + } + + // Do two structure types match? They could be declared independently, + // in different places, but still might satisfy the definition of matching. + // From the spec: + // + // "Structures must have the same name, sequence of type names, and + // type definitions, and member names to be considered the same type. + // This rule applies recursively for nested or embedded types." + // + bool sameStructType(const TType& right) const + { + // Most commonly, they are both nullptr, or the same pointer to the same actual structure + if ((!isStruct() && !right.isStruct()) || + (isStruct() && right.isStruct() && structure == right.structure)) + return true; + + // Both being nullptr was caught above, now they both have to be structures of the same number of elements + if (!isStruct() || !right.isStruct() || + structure->size() != right.structure->size()) + return false; + + // Structure names have to match + if (*typeName != *right.typeName) + return false; + + // Compare the names and types of all the members, which have to match + for (unsigned int i = 0; i < structure->size(); ++i) { + if ((*structure)[i].type->getFieldName() != (*right.structure)[i].type->getFieldName()) + return false; + + if (*(*structure)[i].type != *(*right.structure)[i].type) + return false; + } + + return true; + } + + bool sameReferenceType(const TType& right) const + { + if (isReference() != right.isReference()) + return false; + + if (!isReference() && !right.isReference()) + return true; + + assert(referentType != nullptr); + assert(right.referentType != nullptr); + + if (referentType == right.referentType) + return true; + + return *referentType == *right.referentType; + } + + // See if two types match, in all aspects except arrayness + bool sameElementType(const TType& right) const + { + return basicType == right.basicType && sameElementShape(right); + } + + // See if two type's arrayness match + bool sameArrayness(const TType& right) const + { + return ((arraySizes == nullptr && right.arraySizes == nullptr) || + (arraySizes != nullptr && right.arraySizes != nullptr && *arraySizes == *right.arraySizes)); + } + + // See if two type's arrayness match in everything except their outer dimension + bool sameInnerArrayness(const TType& right) const + { + assert(arraySizes != nullptr && right.arraySizes != nullptr); + return arraySizes->sameInnerArrayness(*right.arraySizes); + } + + // See if two type's parameters match + bool sameTypeParameters(const TType& right) const + { + return ((typeParameters == nullptr && right.typeParameters == nullptr) || + (typeParameters != nullptr && right.typeParameters != nullptr && *typeParameters == *right.typeParameters)); + } + + // See if two type's elements match in all ways except basic type + bool sameElementShape(const TType& right) const + { + return sampler == right.sampler && + vectorSize == right.vectorSize && + matrixCols == right.matrixCols && + matrixRows == right.matrixRows && + vector1 == right.vector1 && + isCoopMat() == right.isCoopMat() && + sameStructType(right) && + sameReferenceType(right); + } + + // See if a cooperative matrix type parameter with unspecified parameters is + // an OK function parameter + bool coopMatParameterOK(const TType& right) const + { + return isCoopMat() && right.isCoopMat() && (getBasicType() == right.getBasicType()) && + typeParameters == nullptr && right.typeParameters != nullptr; + } + + bool sameCoopMatBaseType(const TType &right) const { + bool rv = coopmat && right.coopmat; + if (getBasicType() == EbtFloat || getBasicType() == EbtFloat16) + rv = right.getBasicType() == EbtFloat || right.getBasicType() == EbtFloat16; + else if (getBasicType() == EbtUint || getBasicType() == EbtUint8) + rv = right.getBasicType() == EbtUint || right.getBasicType() == EbtUint8; + else if (getBasicType() == EbtInt || getBasicType() == EbtInt8) + rv = right.getBasicType() == EbtInt || right.getBasicType() == EbtInt8; + else + rv = false; + return rv; + } + + + // See if two types match in all ways (just the actual type, not qualification) + bool operator==(const TType& right) const + { + return sameElementType(right) && sameArrayness(right) && sameTypeParameters(right); + } + + bool operator!=(const TType& right) const + { + return ! operator==(right); + } + + unsigned int getBufferReferenceAlignment() const + { +#ifndef GLSLANG_WEB + if (getBasicType() == glslang::EbtReference) { + return getReferentType()->getQualifier().hasBufferReferenceAlign() ? + (1u << getReferentType()->getQualifier().layoutBufferReferenceAlign) : 16u; + } +#endif + return 0; + } + +protected: + // Require consumer to pick between deep copy and shallow copy. + TType(const TType& type); + TType& operator=(const TType& type); + + // Recursively copy a type graph, while preserving the graph-like + // quality. That is, don't make more than one copy of a structure that + // gets reused multiple times in the type graph. + void deepCopy(const TType& copyOf, TMap& copiedMap) + { + shallowCopy(copyOf); + + if (copyOf.arraySizes) { + arraySizes = new TArraySizes; + *arraySizes = *copyOf.arraySizes; + } + + if (copyOf.typeParameters) { + typeParameters = new TArraySizes; + *typeParameters = *copyOf.typeParameters; + } + + if (copyOf.isStruct() && copyOf.structure) { + auto prevCopy = copiedMap.find(copyOf.structure); + if (prevCopy != copiedMap.end()) + structure = prevCopy->second; + else { + structure = new TTypeList; + copiedMap[copyOf.structure] = structure; + for (unsigned int i = 0; i < copyOf.structure->size(); ++i) { + TTypeLoc typeLoc; + typeLoc.loc = (*copyOf.structure)[i].loc; + typeLoc.type = new TType(); + typeLoc.type->deepCopy(*(*copyOf.structure)[i].type, copiedMap); + structure->push_back(typeLoc); + } + } + } + + if (copyOf.fieldName) + fieldName = NewPoolTString(copyOf.fieldName->c_str()); + if (copyOf.typeName) + typeName = NewPoolTString(copyOf.typeName->c_str()); + } + + + void buildMangledName(TString&) const; + + TBasicType basicType : 8; + int vectorSize : 4; // 1 means either scalar or 1-component vector; see vector1 to disambiguate. + int matrixCols : 4; + int matrixRows : 4; + bool vector1 : 1; // Backward-compatible tracking of a 1-component vector distinguished from a scalar. + // GLSL 4.5 never has a 1-component vector; so this will always be false until such + // functionality is added. + // HLSL does have a 1-component vectors, so this will be true to disambiguate + // from a scalar. + bool coopmat : 1; + TQualifier qualifier; + + TArraySizes* arraySizes; // nullptr unless an array; can be shared across types + // A type can't be both a structure (EbtStruct/EbtBlock) and a reference (EbtReference), so + // conserve space by making these a union + union { + TTypeList* structure; // invalid unless this is a struct; can be shared across types + TType *referentType; // invalid unless this is an EbtReference + }; + TString *fieldName; // for structure field names + TString *typeName; // for structure type name + TSampler sampler; + TArraySizes* typeParameters;// nullptr unless a parameterized type; can be shared across types +}; + +} // end namespace glslang + +#endif // _TYPES_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/arrays.h b/third_party/glslang/glslang/Include/arrays.h new file mode 100644 index 0000000..7f047d9 --- /dev/null +++ b/third_party/glslang/glslang/Include/arrays.h @@ -0,0 +1,341 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Implement types for tracking GLSL arrays, arrays of arrays, etc. +// + +#ifndef _ARRAYS_INCLUDED +#define _ARRAYS_INCLUDED + +#include + +namespace glslang { + +// This is used to mean there is no size yet (unsized), it is waiting to get a size from somewhere else. +const int UnsizedArraySize = 0; + +class TIntermTyped; +extern bool SameSpecializationConstants(TIntermTyped*, TIntermTyped*); + +// Specialization constants need both a nominal size and a node that defines +// the specialization constant being used. Array types are the same when their +// size and specialization constant nodes are the same. +struct TArraySize { + unsigned int size; + TIntermTyped* node; // nullptr means no specialization constant node + bool operator==(const TArraySize& rhs) const + { + if (size != rhs.size) + return false; + if (node == nullptr || rhs.node == nullptr) + return node == rhs.node; + + return SameSpecializationConstants(node, rhs.node); + } +}; + +// +// TSmallArrayVector is used as the container for the set of sizes in TArraySizes. +// It has generic-container semantics, while TArraySizes has array-of-array semantics. +// That is, TSmallArrayVector should be more focused on mechanism and TArraySizes on policy. +// +struct TSmallArrayVector { + // + // TODO: memory: TSmallArrayVector is intended to be smaller. + // Almost all arrays could be handled by two sizes each fitting + // in 16 bits, needing a real vector only in the cases where there + // are more than 3 sizes or a size needing more than 16 bits. + // + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TSmallArrayVector() : sizes(nullptr) { } + virtual ~TSmallArrayVector() { dealloc(); } + + // For breaking into two non-shared copies, independently modifiable. + TSmallArrayVector& operator=(const TSmallArrayVector& from) + { + if (from.sizes == nullptr) + sizes = nullptr; + else { + alloc(); + *sizes = *from.sizes; + } + + return *this; + } + + int size() const + { + if (sizes == nullptr) + return 0; + return (int)sizes->size(); + } + + unsigned int frontSize() const + { + assert(sizes != nullptr && sizes->size() > 0); + return sizes->front().size; + } + + TIntermTyped* frontNode() const + { + assert(sizes != nullptr && sizes->size() > 0); + return sizes->front().node; + } + + void changeFront(unsigned int s) + { + assert(sizes != nullptr); + // this should only happen for implicitly sized arrays, not specialization constants + assert(sizes->front().node == nullptr); + sizes->front().size = s; + } + + void push_back(unsigned int e, TIntermTyped* n) + { + alloc(); + TArraySize pair = { e, n }; + sizes->push_back(pair); + } + + void push_back(const TSmallArrayVector& newDims) + { + alloc(); + sizes->insert(sizes->end(), newDims.sizes->begin(), newDims.sizes->end()); + } + + void pop_front() + { + assert(sizes != nullptr && sizes->size() > 0); + if (sizes->size() == 1) + dealloc(); + else + sizes->erase(sizes->begin()); + } + + // 'this' should currently not be holding anything, and copyNonFront + // will make it hold a copy of all but the first element of rhs. + // (This would be useful for making a type that is dereferenced by + // one dimension.) + void copyNonFront(const TSmallArrayVector& rhs) + { + assert(sizes == nullptr); + if (rhs.size() > 1) { + alloc(); + sizes->insert(sizes->begin(), rhs.sizes->begin() + 1, rhs.sizes->end()); + } + } + + unsigned int getDimSize(int i) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + return (*sizes)[i].size; + } + + void setDimSize(int i, unsigned int size) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + assert((*sizes)[i].node == nullptr); + (*sizes)[i].size = size; + } + + TIntermTyped* getDimNode(int i) const + { + assert(sizes != nullptr && (int)sizes->size() > i); + return (*sizes)[i].node; + } + + bool operator==(const TSmallArrayVector& rhs) const + { + if (sizes == nullptr && rhs.sizes == nullptr) + return true; + if (sizes == nullptr || rhs.sizes == nullptr) + return false; + return *sizes == *rhs.sizes; + } + bool operator!=(const TSmallArrayVector& rhs) const { return ! operator==(rhs); } + +protected: + TSmallArrayVector(const TSmallArrayVector&); + + void alloc() + { + if (sizes == nullptr) + sizes = new TVector; + } + void dealloc() + { + delete sizes; + sizes = nullptr; + } + + TVector* sizes; // will either hold such a pointer, or in the future, hold the two array sizes +}; + +// +// Represent an array, or array of arrays, to arbitrary depth. This is not +// done through a hierarchy of types in a type tree, rather all contiguous arrayness +// in the type hierarchy is localized into this single cumulative object. +// +// The arrayness in TTtype is a pointer, so that it can be non-allocated and zero +// for the vast majority of types that are non-array types. +// +// Order Policy: these are all identical: +// - left to right order within a contiguous set of ...[..][..][..]... in the source language +// - index order 0, 1, 2, ... within the 'sizes' member below +// - outer-most to inner-most +// +struct TArraySizes { + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + + TArraySizes() : implicitArraySize(1), variablyIndexed(false) { } + + // For breaking into two non-shared copies, independently modifiable. + TArraySizes& operator=(const TArraySizes& from) + { + implicitArraySize = from.implicitArraySize; + variablyIndexed = from.variablyIndexed; + sizes = from.sizes; + + return *this; + } + + // translate from array-of-array semantics to container semantics + int getNumDims() const { return sizes.size(); } + int getDimSize(int dim) const { return sizes.getDimSize(dim); } + TIntermTyped* getDimNode(int dim) const { return sizes.getDimNode(dim); } + void setDimSize(int dim, int size) { sizes.setDimSize(dim, size); } + int getOuterSize() const { return sizes.frontSize(); } + TIntermTyped* getOuterNode() const { return sizes.frontNode(); } + int getCumulativeSize() const + { + int size = 1; + for (int d = 0; d < sizes.size(); ++d) { + // this only makes sense in paths that have a known array size + assert(sizes.getDimSize(d) != UnsizedArraySize); + size *= sizes.getDimSize(d); + } + return size; + } + void addInnerSize() { addInnerSize((unsigned)UnsizedArraySize); } + void addInnerSize(int s) { addInnerSize((unsigned)s, nullptr); } + void addInnerSize(int s, TIntermTyped* n) { sizes.push_back((unsigned)s, n); } + void addInnerSize(TArraySize pair) { + sizes.push_back(pair.size, pair.node); + } + void addInnerSizes(const TArraySizes& s) { sizes.push_back(s.sizes); } + void changeOuterSize(int s) { sizes.changeFront((unsigned)s); } + int getImplicitSize() const { return implicitArraySize; } + void updateImplicitSize(int s) { implicitArraySize = std::max(implicitArraySize, s); } + bool isInnerUnsized() const + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize) + return true; + } + + return false; + } + bool clearInnerUnsized() + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) == (unsigned)UnsizedArraySize) + setDimSize(d, 1); + } + + return false; + } + bool isInnerSpecialization() const + { + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimNode(d) != nullptr) + return true; + } + + return false; + } + bool isOuterSpecialization() + { + return sizes.getDimNode(0) != nullptr; + } + + bool hasUnsized() const { return getOuterSize() == UnsizedArraySize || isInnerUnsized(); } + bool isSized() const { return getOuterSize() != UnsizedArraySize; } + void dereference() { sizes.pop_front(); } + void copyDereferenced(const TArraySizes& rhs) + { + assert(sizes.size() == 0); + if (rhs.sizes.size() > 1) + sizes.copyNonFront(rhs.sizes); + } + + bool sameInnerArrayness(const TArraySizes& rhs) const + { + if (sizes.size() != rhs.sizes.size()) + return false; + + for (int d = 1; d < sizes.size(); ++d) { + if (sizes.getDimSize(d) != rhs.sizes.getDimSize(d) || + sizes.getDimNode(d) != rhs.sizes.getDimNode(d)) + return false; + } + + return true; + } + + void setVariablyIndexed() { variablyIndexed = true; } + bool isVariablyIndexed() const { return variablyIndexed; } + + bool operator==(const TArraySizes& rhs) const { return sizes == rhs.sizes; } + bool operator!=(const TArraySizes& rhs) const { return sizes != rhs.sizes; } + +protected: + TSmallArrayVector sizes; + + TArraySizes(const TArraySizes&); + + // For tracking maximum referenced compile-time constant index. + // Applies only to the outer-most dimension. Potentially becomes + // the implicit size of the array, if not variably indexed and + // otherwise legal. + int implicitArraySize; + bool variablyIndexed; // true if array is indexed with a non compile-time constant +}; + +} // end namespace glslang + +#endif // _ARRAYS_INCLUDED_ diff --git a/third_party/glslang/glslang/Include/glslang_c_interface.h b/third_party/glslang/glslang/Include/glslang_c_interface.h new file mode 100644 index 0000000..4b32e2b --- /dev/null +++ b/third_party/glslang/glslang/Include/glslang_c_interface.h @@ -0,0 +1,249 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#ifndef GLSLANG_C_IFACE_H_INCLUDED +#define GLSLANG_C_IFACE_H_INCLUDED + +#include +#include + +#include "glslang_c_shader_types.h" + +typedef struct glslang_shader_s glslang_shader_t; +typedef struct glslang_program_s glslang_program_t; + +/* TLimits counterpart */ +typedef struct glslang_limits_s { + bool non_inductive_for_loops; + bool while_loops; + bool do_while_loops; + bool general_uniform_indexing; + bool general_attribute_matrix_vector_indexing; + bool general_varying_indexing; + bool general_sampler_indexing; + bool general_variable_indexing; + bool general_constant_matrix_vector_indexing; +} glslang_limits_t; + +/* TBuiltInResource counterpart */ +typedef struct glslang_resource_s { + int max_lights; + int max_clip_planes; + int max_texture_units; + int max_texture_coords; + int max_vertex_attribs; + int max_vertex_uniform_components; + int max_varying_floats; + int max_vertex_texture_image_units; + int max_combined_texture_image_units; + int max_texture_image_units; + int max_fragment_uniform_components; + int max_draw_buffers; + int max_vertex_uniform_vectors; + int max_varying_vectors; + int max_fragment_uniform_vectors; + int max_vertex_output_vectors; + int max_fragment_input_vectors; + int min_program_texel_offset; + int max_program_texel_offset; + int max_clip_distances; + int max_compute_work_group_count_x; + int max_compute_work_group_count_y; + int max_compute_work_group_count_z; + int max_compute_work_group_size_x; + int max_compute_work_group_size_y; + int max_compute_work_group_size_z; + int max_compute_uniform_components; + int max_compute_texture_image_units; + int max_compute_image_uniforms; + int max_compute_atomic_counters; + int max_compute_atomic_counter_buffers; + int max_varying_components; + int max_vertex_output_components; + int max_geometry_input_components; + int max_geometry_output_components; + int max_fragment_input_components; + int max_image_units; + int max_combined_image_units_and_fragment_outputs; + int max_combined_shader_output_resources; + int max_image_samples; + int max_vertex_image_uniforms; + int max_tess_control_image_uniforms; + int max_tess_evaluation_image_uniforms; + int max_geometry_image_uniforms; + int max_fragment_image_uniforms; + int max_combined_image_uniforms; + int max_geometry_texture_image_units; + int max_geometry_output_vertices; + int max_geometry_total_output_components; + int max_geometry_uniform_components; + int max_geometry_varying_components; + int max_tess_control_input_components; + int max_tess_control_output_components; + int max_tess_control_texture_image_units; + int max_tess_control_uniform_components; + int max_tess_control_total_output_components; + int max_tess_evaluation_input_components; + int max_tess_evaluation_output_components; + int max_tess_evaluation_texture_image_units; + int max_tess_evaluation_uniform_components; + int max_tess_patch_components; + int max_patch_vertices; + int max_tess_gen_level; + int max_viewports; + int max_vertex_atomic_counters; + int max_tess_control_atomic_counters; + int max_tess_evaluation_atomic_counters; + int max_geometry_atomic_counters; + int max_fragment_atomic_counters; + int max_combined_atomic_counters; + int max_atomic_counter_bindings; + int max_vertex_atomic_counter_buffers; + int max_tess_control_atomic_counter_buffers; + int max_tess_evaluation_atomic_counter_buffers; + int max_geometry_atomic_counter_buffers; + int max_fragment_atomic_counter_buffers; + int max_combined_atomic_counter_buffers; + int max_atomic_counter_buffer_size; + int max_transform_feedback_buffers; + int max_transform_feedback_interleaved_components; + int max_cull_distances; + int max_combined_clip_and_cull_distances; + int max_samples; + int max_mesh_output_vertices_nv; + int max_mesh_output_primitives_nv; + int max_mesh_work_group_size_x_nv; + int max_mesh_work_group_size_y_nv; + int max_mesh_work_group_size_z_nv; + int max_task_work_group_size_x_nv; + int max_task_work_group_size_y_nv; + int max_task_work_group_size_z_nv; + int max_mesh_view_count_nv; + int maxDualSourceDrawBuffersEXT; + + glslang_limits_t limits; +} glslang_resource_t; + +typedef struct glslang_input_s { + glslang_source_t language; + glslang_stage_t stage; + glslang_client_t client; + glslang_target_client_version_t client_version; + glslang_target_language_t target_language; + glslang_target_language_version_t target_language_version; + /** Shader source code */ + const char* code; + int default_version; + glslang_profile_t default_profile; + int force_default_version_and_profile; + int forward_compatible; + glslang_messages_t messages; + const glslang_resource_t* resource; +} glslang_input_t; + +/* Inclusion result structure allocated by C include_local/include_system callbacks */ +typedef struct glsl_include_result_s { + /* Header file name or NULL if inclusion failed */ + const char* header_name; + + /* Header contents or NULL */ + const char* header_data; + size_t header_length; + +} glsl_include_result_t; + +/* Callback for local file inclusion */ +typedef glsl_include_result_t* (*glsl_include_local_func)(void* ctx, const char* header_name, const char* includer_name, + size_t include_depth); + +/* Callback for system file inclusion */ +typedef glsl_include_result_t* (*glsl_include_system_func)(void* ctx, const char* header_name, + const char* includer_name, size_t include_depth); + +/* Callback for include result destruction */ +typedef int (*glsl_free_include_result_func)(void* ctx, glsl_include_result_t* result); + +/* Collection of callbacks for GLSL preprocessor */ +typedef struct glsl_include_callbacks_s { + glsl_include_system_func include_system; + glsl_include_local_func include_local; + glsl_free_include_result_func free_include_result; +} glsl_include_callbacks_t; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef GLSLANG_IS_SHARED_LIBRARY + #ifdef _WIN32 + #ifdef GLSLANG_EXPORTING + #define GLSLANG_EXPORT __declspec(dllexport) + #else + #define GLSLANG_EXPORT __declspec(dllimport) + #endif + #elif __GNUC__ >= 4 + #define GLSLANG_EXPORT __attribute__((visibility("default"))) + #endif +#endif // GLSLANG_IS_SHARED_LIBRARY + +#ifndef GLSLANG_EXPORT +#define GLSLANG_EXPORT +#endif + +GLSLANG_EXPORT int glslang_initialize_process(); +GLSLANG_EXPORT void glslang_finalize_process(); + +GLSLANG_EXPORT glslang_shader_t* glslang_shader_create(const glslang_input_t* input); +GLSLANG_EXPORT void glslang_shader_delete(glslang_shader_t* shader); +GLSLANG_EXPORT int glslang_shader_preprocess(glslang_shader_t* shader, const glslang_input_t* input); +GLSLANG_EXPORT int glslang_shader_parse(glslang_shader_t* shader, const glslang_input_t* input); +GLSLANG_EXPORT const char* glslang_shader_get_preprocessed_code(glslang_shader_t* shader); +GLSLANG_EXPORT const char* glslang_shader_get_info_log(glslang_shader_t* shader); +GLSLANG_EXPORT const char* glslang_shader_get_info_debug_log(glslang_shader_t* shader); + +GLSLANG_EXPORT glslang_program_t* glslang_program_create(); +GLSLANG_EXPORT void glslang_program_delete(glslang_program_t* program); +GLSLANG_EXPORT void glslang_program_add_shader(glslang_program_t* program, glslang_shader_t* shader); +GLSLANG_EXPORT int glslang_program_link(glslang_program_t* program, int messages); // glslang_messages_t +GLSLANG_EXPORT void glslang_program_SPIRV_generate(glslang_program_t* program, glslang_stage_t stage); +GLSLANG_EXPORT size_t glslang_program_SPIRV_get_size(glslang_program_t* program); +GLSLANG_EXPORT void glslang_program_SPIRV_get(glslang_program_t* program, unsigned int*); +GLSLANG_EXPORT unsigned int* glslang_program_SPIRV_get_ptr(glslang_program_t* program); +GLSLANG_EXPORT const char* glslang_program_SPIRV_get_messages(glslang_program_t* program); +GLSLANG_EXPORT const char* glslang_program_get_info_log(glslang_program_t* program); +GLSLANG_EXPORT const char* glslang_program_get_info_debug_log(glslang_program_t* program); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifdef GLSLANG_C_IFACE_INCLUDED */ diff --git a/third_party/glslang/glslang/Include/glslang_c_shader_types.h b/third_party/glslang/glslang/Include/glslang_c_shader_types.h new file mode 100644 index 0000000..d01a115 --- /dev/null +++ b/third_party/glslang/glslang/Include/glslang_c_shader_types.h @@ -0,0 +1,185 @@ +/** + This code is based on the glslang_c_interface implementation by Viktor Latypov +**/ + +/** +BSD 2-Clause License + +Copyright (c) 2019, Viktor Latypov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +#ifndef C_SHADER_TYPES_H_INCLUDED +#define C_SHADER_TYPES_H_INCLUDED + +#define LAST_ELEMENT_MARKER(x) x + +/* EShLanguage counterpart */ +typedef enum { + GLSLANG_STAGE_VERTEX, + GLSLANG_STAGE_TESSCONTROL, + GLSLANG_STAGE_TESSEVALUATION, + GLSLANG_STAGE_GEOMETRY, + GLSLANG_STAGE_FRAGMENT, + GLSLANG_STAGE_COMPUTE, + GLSLANG_STAGE_RAYGEN_NV, + GLSLANG_STAGE_INTERSECT_NV, + GLSLANG_STAGE_ANYHIT_NV, + GLSLANG_STAGE_CLOSESTHIT_NV, + GLSLANG_STAGE_MISS_NV, + GLSLANG_STAGE_CALLABLE_NV, + GLSLANG_STAGE_TASK_NV, + GLSLANG_STAGE_MESH_NV, + LAST_ELEMENT_MARKER(GLSLANG_STAGE_COUNT), +} glslang_stage_t; // would be better as stage, but this is ancient now + +/* EShLanguageMask counterpart */ +typedef enum { + GLSLANG_STAGE_VERTEX_MASK = (1 << GLSLANG_STAGE_VERTEX), + GLSLANG_STAGE_TESSCONTROL_MASK = (1 << GLSLANG_STAGE_TESSCONTROL), + GLSLANG_STAGE_TESSEVALUATION_MASK = (1 << GLSLANG_STAGE_TESSEVALUATION), + GLSLANG_STAGE_GEOMETRY_MASK = (1 << GLSLANG_STAGE_GEOMETRY), + GLSLANG_STAGE_FRAGMENT_MASK = (1 << GLSLANG_STAGE_FRAGMENT), + GLSLANG_STAGE_COMPUTE_MASK = (1 << GLSLANG_STAGE_COMPUTE), + GLSLANG_STAGE_RAYGEN_NV_MASK = (1 << GLSLANG_STAGE_RAYGEN_NV), + GLSLANG_STAGE_INTERSECT_NV_MASK = (1 << GLSLANG_STAGE_INTERSECT_NV), + GLSLANG_STAGE_ANYHIT_NV_MASK = (1 << GLSLANG_STAGE_ANYHIT_NV), + GLSLANG_STAGE_CLOSESTHIT_NV_MASK = (1 << GLSLANG_STAGE_CLOSESTHIT_NV), + GLSLANG_STAGE_MISS_NV_MASK = (1 << GLSLANG_STAGE_MISS_NV), + GLSLANG_STAGE_CALLABLE_NV_MASK = (1 << GLSLANG_STAGE_CALLABLE_NV), + GLSLANG_STAGE_TASK_NV_MASK = (1 << GLSLANG_STAGE_TASK_NV), + GLSLANG_STAGE_MESH_NV_MASK = (1 << GLSLANG_STAGE_MESH_NV), + LAST_ELEMENT_MARKER(GLSLANG_STAGE_MASK_COUNT), +} glslang_stage_mask_t; + +/* EShSource counterpart */ +typedef enum { + GLSLANG_SOURCE_NONE, + GLSLANG_SOURCE_GLSL, + GLSLANG_SOURCE_HLSL, + LAST_ELEMENT_MARKER(GLSLANG_SOURCE_COUNT), +} glslang_source_t; + +/* EShClient counterpart */ +typedef enum { + GLSLANG_CLIENT_NONE, + GLSLANG_CLIENT_VULKAN, + GLSLANG_CLIENT_OPENGL, + LAST_ELEMENT_MARKER(GLSLANG_CLIENT_COUNT), +} glslang_client_t; + +/* EShTargetLanguage counterpart */ +typedef enum { + GLSLANG_TARGET_NONE, + GLSLANG_TARGET_SPV, + LAST_ELEMENT_MARKER(GLSLANG_TARGET_COUNT), +} glslang_target_language_t; + +/* SH_TARGET_ClientVersion counterpart */ +typedef enum { + GLSLANG_TARGET_VULKAN_1_0 = (1 << 22), + GLSLANG_TARGET_VULKAN_1_1 = (1 << 22) | (1 << 12), + GLSLANG_TARGET_OPENGL_450 = 450, + LAST_ELEMENT_MARKER(GLSLANG_TARGET_CLIENT_VERSION_COUNT), +} glslang_target_client_version_t; + +/* SH_TARGET_LanguageVersion counterpart */ +typedef enum { + GLSLANG_TARGET_SPV_1_0 = (1 << 16), + GLSLANG_TARGET_SPV_1_1 = (1 << 16) | (1 << 8), + GLSLANG_TARGET_SPV_1_2 = (1 << 16) | (2 << 8), + GLSLANG_TARGET_SPV_1_3 = (1 << 16) | (3 << 8), + GLSLANG_TARGET_SPV_1_4 = (1 << 16) | (4 << 8), + GLSLANG_TARGET_SPV_1_5 = (1 << 16) | (5 << 8), + LAST_ELEMENT_MARKER(GLSLANG_TARGET_LANGUAGE_VERSION_COUNT), +} glslang_target_language_version_t; + +/* EShExecutable counterpart */ +typedef enum { GLSLANG_EX_VERTEX_FRAGMENT, GLSLANG_EX_FRAGMENT } glslang_executable_t; + +/* EShOptimizationLevel counterpart */ +typedef enum { + GLSLANG_OPT_NO_GENERATION, + GLSLANG_OPT_NONE, + GLSLANG_OPT_SIMPLE, + GLSLANG_OPT_FULL, + LAST_ELEMENT_MARKER(GLSLANG_OPT_LEVEL_COUNT), +} glslang_optimization_level_t; + +/* EShTextureSamplerTransformMode counterpart */ +typedef enum { + GLSLANG_TEX_SAMP_TRANS_KEEP, + GLSLANG_TEX_SAMP_TRANS_UPGRADE_TEXTURE_REMOVE_SAMPLER, + LAST_ELEMENT_MARKER(GLSLANG_TEX_SAMP_TRANS_COUNT), +} glslang_texture_sampler_transform_mode_t; + +/* EShMessages counterpart */ +typedef enum { + GLSLANG_MSG_DEFAULT_BIT = 0, + GLSLANG_MSG_RELAXED_ERRORS_BIT = (1 << 0), + GLSLANG_MSG_SUPPRESS_WARNINGS_BIT = (1 << 1), + GLSLANG_MSG_AST_BIT = (1 << 2), + GLSLANG_MSG_SPV_RULES_BIT = (1 << 3), + GLSLANG_MSG_VULKAN_RULES_BIT = (1 << 4), + GLSLANG_MSG_ONLY_PREPROCESSOR_BIT = (1 << 5), + GLSLANG_MSG_READ_HLSL_BIT = (1 << 6), + GLSLANG_MSG_CASCADING_ERRORS_BIT = (1 << 7), + GLSLANG_MSG_KEEP_UNCALLED_BIT = (1 << 8), + GLSLANG_MSG_HLSL_OFFSETS_BIT = (1 << 9), + GLSLANG_MSG_DEBUG_INFO_BIT = (1 << 10), + GLSLANG_MSG_HLSL_ENABLE_16BIT_TYPES_BIT = (1 << 11), + GLSLANG_MSG_HLSL_LEGALIZATION_BIT = (1 << 12), + GLSLANG_MSG_HLSL_DX9_COMPATIBLE_BIT = (1 << 13), + GLSLANG_MSG_BUILTIN_SYMBOL_TABLE_BIT = (1 << 14), + LAST_ELEMENT_MARKER(GLSLANG_MSG_COUNT), +} glslang_messages_t; + +/* EShReflectionOptions counterpart */ +typedef enum { + GLSLANG_REFLECTION_DEFAULT_BIT = 0, + GLSLANG_REFLECTION_STRICT_ARRAY_SUFFIX_BIT = (1 << 0), + GLSLANG_REFLECTION_BASIC_ARRAY_SUFFIX_BIT = (1 << 1), + GLSLANG_REFLECTION_INTERMEDIATE_IOO_BIT = (1 << 2), + GLSLANG_REFLECTION_SEPARATE_BUFFERS_BIT = (1 << 3), + GLSLANG_REFLECTION_ALL_BLOCK_VARIABLES_BIT = (1 << 4), + GLSLANG_REFLECTION_UNWRAP_IO_BLOCKS_BIT = (1 << 5), + GLSLANG_REFLECTION_ALL_IO_VARIABLES_BIT = (1 << 6), + GLSLANG_REFLECTION_SHARED_STD140_SSBO_BIT = (1 << 7), + GLSLANG_REFLECTION_SHARED_STD140_UBO_BIT = (1 << 8), + LAST_ELEMENT_MARKER(GLSLANG_REFLECTION_COUNT), +} glslang_reflection_options_t; + +/* EProfile counterpart (from Versions.h) */ +typedef enum { + GLSLANG_BAD_PROFILE = 0, + GLSLANG_NO_PROFILE = (1 << 0), + GLSLANG_CORE_PROFILE = (1 << 1), + GLSLANG_COMPATIBILITY_PROFILE = (1 << 2), + GLSLANG_ES_PROFILE = (1 << 3), + LAST_ELEMENT_MARKER(GLSLANG_PROFILE_COUNT), +} glslang_profile_t; + +#undef LAST_ELEMENT_MARKER + +#endif diff --git a/third_party/glslang/glslang/Include/intermediate.h b/third_party/glslang/glslang/Include/intermediate.h new file mode 100644 index 0000000..f0411eb --- /dev/null +++ b/third_party/glslang/glslang/Include/intermediate.h @@ -0,0 +1,1809 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Definition of the in-memory high-level intermediate representation +// of shaders. This is a tree that parser creates. +// +// Nodes in the tree are defined as a hierarchy of classes derived from +// TIntermNode. Each is a node in a tree. There is no preset branching factor; +// each node can have it's own type of list of children. +// + +#ifndef __INTERMEDIATE_H +#define __INTERMEDIATE_H + +#if defined(_MSC_VER) && _MSC_VER >= 1900 + #pragma warning(disable : 4464) // relative include path contains '..' + #pragma warning(disable : 5026) // 'glslang::TIntermUnary': move constructor was implicitly defined as deleted +#endif + +#include "../Include/Common.h" +#include "../Include/Types.h" +#include "../Include/ConstantUnion.h" + +namespace glslang { + +class TIntermediate; + +// +// Operators used by the high-level (parse tree) representation. +// +enum TOperator { + EOpNull, // if in a node, should only mean a node is still being built + EOpSequence, // denotes a list of statements, or parameters, etc. + EOpLinkerObjects, // for aggregate node of objects the linker may need, if not reference by the rest of the AST + EOpFunctionCall, + EOpFunction, // For function definition + EOpParameters, // an aggregate listing the parameters to a function + + // + // Unary operators + // + + EOpNegative, + EOpLogicalNot, + EOpVectorLogicalNot, + EOpBitwiseNot, + + EOpPostIncrement, + EOpPostDecrement, + EOpPreIncrement, + EOpPreDecrement, + + EOpCopyObject, + + // (u)int* -> bool + EOpConvInt8ToBool, + EOpConvUint8ToBool, + EOpConvInt16ToBool, + EOpConvUint16ToBool, + EOpConvIntToBool, + EOpConvUintToBool, + EOpConvInt64ToBool, + EOpConvUint64ToBool, + + // float* -> bool + EOpConvFloat16ToBool, + EOpConvFloatToBool, + EOpConvDoubleToBool, + + // bool -> (u)int* + EOpConvBoolToInt8, + EOpConvBoolToUint8, + EOpConvBoolToInt16, + EOpConvBoolToUint16, + EOpConvBoolToInt, + EOpConvBoolToUint, + EOpConvBoolToInt64, + EOpConvBoolToUint64, + + // bool -> float* + EOpConvBoolToFloat16, + EOpConvBoolToFloat, + EOpConvBoolToDouble, + + // int8_t -> (u)int* + EOpConvInt8ToInt16, + EOpConvInt8ToInt, + EOpConvInt8ToInt64, + EOpConvInt8ToUint8, + EOpConvInt8ToUint16, + EOpConvInt8ToUint, + EOpConvInt8ToUint64, + + // uint8_t -> (u)int* + EOpConvUint8ToInt8, + EOpConvUint8ToInt16, + EOpConvUint8ToInt, + EOpConvUint8ToInt64, + EOpConvUint8ToUint16, + EOpConvUint8ToUint, + EOpConvUint8ToUint64, + + // int8_t -> float* + EOpConvInt8ToFloat16, + EOpConvInt8ToFloat, + EOpConvInt8ToDouble, + + // uint8_t -> float* + EOpConvUint8ToFloat16, + EOpConvUint8ToFloat, + EOpConvUint8ToDouble, + + // int16_t -> (u)int* + EOpConvInt16ToInt8, + EOpConvInt16ToInt, + EOpConvInt16ToInt64, + EOpConvInt16ToUint8, + EOpConvInt16ToUint16, + EOpConvInt16ToUint, + EOpConvInt16ToUint64, + + // uint16_t -> (u)int* + EOpConvUint16ToInt8, + EOpConvUint16ToInt16, + EOpConvUint16ToInt, + EOpConvUint16ToInt64, + EOpConvUint16ToUint8, + EOpConvUint16ToUint, + EOpConvUint16ToUint64, + + // int16_t -> float* + EOpConvInt16ToFloat16, + EOpConvInt16ToFloat, + EOpConvInt16ToDouble, + + // uint16_t -> float* + EOpConvUint16ToFloat16, + EOpConvUint16ToFloat, + EOpConvUint16ToDouble, + + // int32_t -> (u)int* + EOpConvIntToInt8, + EOpConvIntToInt16, + EOpConvIntToInt64, + EOpConvIntToUint8, + EOpConvIntToUint16, + EOpConvIntToUint, + EOpConvIntToUint64, + + // uint32_t -> (u)int* + EOpConvUintToInt8, + EOpConvUintToInt16, + EOpConvUintToInt, + EOpConvUintToInt64, + EOpConvUintToUint8, + EOpConvUintToUint16, + EOpConvUintToUint64, + + // int32_t -> float* + EOpConvIntToFloat16, + EOpConvIntToFloat, + EOpConvIntToDouble, + + // uint32_t -> float* + EOpConvUintToFloat16, + EOpConvUintToFloat, + EOpConvUintToDouble, + + // int64_t -> (u)int* + EOpConvInt64ToInt8, + EOpConvInt64ToInt16, + EOpConvInt64ToInt, + EOpConvInt64ToUint8, + EOpConvInt64ToUint16, + EOpConvInt64ToUint, + EOpConvInt64ToUint64, + + // uint64_t -> (u)int* + EOpConvUint64ToInt8, + EOpConvUint64ToInt16, + EOpConvUint64ToInt, + EOpConvUint64ToInt64, + EOpConvUint64ToUint8, + EOpConvUint64ToUint16, + EOpConvUint64ToUint, + + // int64_t -> float* + EOpConvInt64ToFloat16, + EOpConvInt64ToFloat, + EOpConvInt64ToDouble, + + // uint64_t -> float* + EOpConvUint64ToFloat16, + EOpConvUint64ToFloat, + EOpConvUint64ToDouble, + + // float16_t -> (u)int* + EOpConvFloat16ToInt8, + EOpConvFloat16ToInt16, + EOpConvFloat16ToInt, + EOpConvFloat16ToInt64, + EOpConvFloat16ToUint8, + EOpConvFloat16ToUint16, + EOpConvFloat16ToUint, + EOpConvFloat16ToUint64, + + // float16_t -> float* + EOpConvFloat16ToFloat, + EOpConvFloat16ToDouble, + + // float -> (u)int* + EOpConvFloatToInt8, + EOpConvFloatToInt16, + EOpConvFloatToInt, + EOpConvFloatToInt64, + EOpConvFloatToUint8, + EOpConvFloatToUint16, + EOpConvFloatToUint, + EOpConvFloatToUint64, + + // float -> float* + EOpConvFloatToFloat16, + EOpConvFloatToDouble, + + // float64 _t-> (u)int* + EOpConvDoubleToInt8, + EOpConvDoubleToInt16, + EOpConvDoubleToInt, + EOpConvDoubleToInt64, + EOpConvDoubleToUint8, + EOpConvDoubleToUint16, + EOpConvDoubleToUint, + EOpConvDoubleToUint64, + + // float64_t -> float* + EOpConvDoubleToFloat16, + EOpConvDoubleToFloat, + + // uint64_t <-> pointer + EOpConvUint64ToPtr, + EOpConvPtrToUint64, + + // uvec2 <-> pointer + EOpConvUvec2ToPtr, + EOpConvPtrToUvec2, + + // + // binary operations + // + + EOpAdd, + EOpSub, + EOpMul, + EOpDiv, + EOpMod, + EOpRightShift, + EOpLeftShift, + EOpAnd, + EOpInclusiveOr, + EOpExclusiveOr, + EOpEqual, + EOpNotEqual, + EOpVectorEqual, + EOpVectorNotEqual, + EOpLessThan, + EOpGreaterThan, + EOpLessThanEqual, + EOpGreaterThanEqual, + EOpComma, + + EOpVectorTimesScalar, + EOpVectorTimesMatrix, + EOpMatrixTimesVector, + EOpMatrixTimesScalar, + + EOpLogicalOr, + EOpLogicalXor, + EOpLogicalAnd, + + EOpIndexDirect, + EOpIndexIndirect, + EOpIndexDirectStruct, + + EOpVectorSwizzle, + + EOpMethod, + EOpScoping, + + // + // Built-in functions mapped to operators + // + + EOpRadians, + EOpDegrees, + EOpSin, + EOpCos, + EOpTan, + EOpAsin, + EOpAcos, + EOpAtan, + EOpSinh, + EOpCosh, + EOpTanh, + EOpAsinh, + EOpAcosh, + EOpAtanh, + + EOpPow, + EOpExp, + EOpLog, + EOpExp2, + EOpLog2, + EOpSqrt, + EOpInverseSqrt, + + EOpAbs, + EOpSign, + EOpFloor, + EOpTrunc, + EOpRound, + EOpRoundEven, + EOpCeil, + EOpFract, + EOpModf, + EOpMin, + EOpMax, + EOpClamp, + EOpMix, + EOpStep, + EOpSmoothStep, + + EOpIsNan, + EOpIsInf, + + EOpFma, + + EOpFrexp, + EOpLdexp, + + EOpFloatBitsToInt, + EOpFloatBitsToUint, + EOpIntBitsToFloat, + EOpUintBitsToFloat, + EOpDoubleBitsToInt64, + EOpDoubleBitsToUint64, + EOpInt64BitsToDouble, + EOpUint64BitsToDouble, + EOpFloat16BitsToInt16, + EOpFloat16BitsToUint16, + EOpInt16BitsToFloat16, + EOpUint16BitsToFloat16, + EOpPackSnorm2x16, + EOpUnpackSnorm2x16, + EOpPackUnorm2x16, + EOpUnpackUnorm2x16, + EOpPackSnorm4x8, + EOpUnpackSnorm4x8, + EOpPackUnorm4x8, + EOpUnpackUnorm4x8, + EOpPackHalf2x16, + EOpUnpackHalf2x16, + EOpPackDouble2x32, + EOpUnpackDouble2x32, + EOpPackInt2x32, + EOpUnpackInt2x32, + EOpPackUint2x32, + EOpUnpackUint2x32, + EOpPackFloat2x16, + EOpUnpackFloat2x16, + EOpPackInt2x16, + EOpUnpackInt2x16, + EOpPackUint2x16, + EOpUnpackUint2x16, + EOpPackInt4x16, + EOpUnpackInt4x16, + EOpPackUint4x16, + EOpUnpackUint4x16, + EOpPack16, + EOpPack32, + EOpPack64, + EOpUnpack32, + EOpUnpack16, + EOpUnpack8, + + EOpLength, + EOpDistance, + EOpDot, + EOpCross, + EOpNormalize, + EOpFaceForward, + EOpReflect, + EOpRefract, + + EOpMin3, + EOpMax3, + EOpMid3, + + EOpDPdx, // Fragment only + EOpDPdy, // Fragment only + EOpFwidth, // Fragment only + EOpDPdxFine, // Fragment only + EOpDPdyFine, // Fragment only + EOpFwidthFine, // Fragment only + EOpDPdxCoarse, // Fragment only + EOpDPdyCoarse, // Fragment only + EOpFwidthCoarse, // Fragment only + + EOpInterpolateAtCentroid, // Fragment only + EOpInterpolateAtSample, // Fragment only + EOpInterpolateAtOffset, // Fragment only + EOpInterpolateAtVertex, + + EOpMatrixTimesMatrix, + EOpOuterProduct, + EOpDeterminant, + EOpMatrixInverse, + EOpTranspose, + + EOpFtransform, + + EOpNoise, + + EOpEmitVertex, // geometry only + EOpEndPrimitive, // geometry only + EOpEmitStreamVertex, // geometry only + EOpEndStreamPrimitive, // geometry only + + EOpBarrier, + EOpMemoryBarrier, + EOpMemoryBarrierAtomicCounter, + EOpMemoryBarrierBuffer, + EOpMemoryBarrierImage, + EOpMemoryBarrierShared, // compute only + EOpGroupMemoryBarrier, // compute only + + EOpBallot, + EOpReadInvocation, + EOpReadFirstInvocation, + + EOpAnyInvocation, + EOpAllInvocations, + EOpAllInvocationsEqual, + + EOpSubgroupGuardStart, + EOpSubgroupBarrier, + EOpSubgroupMemoryBarrier, + EOpSubgroupMemoryBarrierBuffer, + EOpSubgroupMemoryBarrierImage, + EOpSubgroupMemoryBarrierShared, // compute only + EOpSubgroupElect, + EOpSubgroupAll, + EOpSubgroupAny, + EOpSubgroupAllEqual, + EOpSubgroupBroadcast, + EOpSubgroupBroadcastFirst, + EOpSubgroupBallot, + EOpSubgroupInverseBallot, + EOpSubgroupBallotBitExtract, + EOpSubgroupBallotBitCount, + EOpSubgroupBallotInclusiveBitCount, + EOpSubgroupBallotExclusiveBitCount, + EOpSubgroupBallotFindLSB, + EOpSubgroupBallotFindMSB, + EOpSubgroupShuffle, + EOpSubgroupShuffleXor, + EOpSubgroupShuffleUp, + EOpSubgroupShuffleDown, + EOpSubgroupAdd, + EOpSubgroupMul, + EOpSubgroupMin, + EOpSubgroupMax, + EOpSubgroupAnd, + EOpSubgroupOr, + EOpSubgroupXor, + EOpSubgroupInclusiveAdd, + EOpSubgroupInclusiveMul, + EOpSubgroupInclusiveMin, + EOpSubgroupInclusiveMax, + EOpSubgroupInclusiveAnd, + EOpSubgroupInclusiveOr, + EOpSubgroupInclusiveXor, + EOpSubgroupExclusiveAdd, + EOpSubgroupExclusiveMul, + EOpSubgroupExclusiveMin, + EOpSubgroupExclusiveMax, + EOpSubgroupExclusiveAnd, + EOpSubgroupExclusiveOr, + EOpSubgroupExclusiveXor, + EOpSubgroupClusteredAdd, + EOpSubgroupClusteredMul, + EOpSubgroupClusteredMin, + EOpSubgroupClusteredMax, + EOpSubgroupClusteredAnd, + EOpSubgroupClusteredOr, + EOpSubgroupClusteredXor, + EOpSubgroupQuadBroadcast, + EOpSubgroupQuadSwapHorizontal, + EOpSubgroupQuadSwapVertical, + EOpSubgroupQuadSwapDiagonal, + + EOpSubgroupPartition, + EOpSubgroupPartitionedAdd, + EOpSubgroupPartitionedMul, + EOpSubgroupPartitionedMin, + EOpSubgroupPartitionedMax, + EOpSubgroupPartitionedAnd, + EOpSubgroupPartitionedOr, + EOpSubgroupPartitionedXor, + EOpSubgroupPartitionedInclusiveAdd, + EOpSubgroupPartitionedInclusiveMul, + EOpSubgroupPartitionedInclusiveMin, + EOpSubgroupPartitionedInclusiveMax, + EOpSubgroupPartitionedInclusiveAnd, + EOpSubgroupPartitionedInclusiveOr, + EOpSubgroupPartitionedInclusiveXor, + EOpSubgroupPartitionedExclusiveAdd, + EOpSubgroupPartitionedExclusiveMul, + EOpSubgroupPartitionedExclusiveMin, + EOpSubgroupPartitionedExclusiveMax, + EOpSubgroupPartitionedExclusiveAnd, + EOpSubgroupPartitionedExclusiveOr, + EOpSubgroupPartitionedExclusiveXor, + + EOpSubgroupGuardStop, + + EOpMinInvocations, + EOpMaxInvocations, + EOpAddInvocations, + EOpMinInvocationsNonUniform, + EOpMaxInvocationsNonUniform, + EOpAddInvocationsNonUniform, + EOpMinInvocationsInclusiveScan, + EOpMaxInvocationsInclusiveScan, + EOpAddInvocationsInclusiveScan, + EOpMinInvocationsInclusiveScanNonUniform, + EOpMaxInvocationsInclusiveScanNonUniform, + EOpAddInvocationsInclusiveScanNonUniform, + EOpMinInvocationsExclusiveScan, + EOpMaxInvocationsExclusiveScan, + EOpAddInvocationsExclusiveScan, + EOpMinInvocationsExclusiveScanNonUniform, + EOpMaxInvocationsExclusiveScanNonUniform, + EOpAddInvocationsExclusiveScanNonUniform, + EOpSwizzleInvocations, + EOpSwizzleInvocationsMasked, + EOpWriteInvocation, + EOpMbcnt, + + EOpCubeFaceIndex, + EOpCubeFaceCoord, + EOpTime, + + EOpAtomicAdd, + EOpAtomicMin, + EOpAtomicMax, + EOpAtomicAnd, + EOpAtomicOr, + EOpAtomicXor, + EOpAtomicExchange, + EOpAtomicCompSwap, + EOpAtomicLoad, + EOpAtomicStore, + + EOpAtomicCounterIncrement, // results in pre-increment value + EOpAtomicCounterDecrement, // results in post-decrement value + EOpAtomicCounter, + EOpAtomicCounterAdd, + EOpAtomicCounterSubtract, + EOpAtomicCounterMin, + EOpAtomicCounterMax, + EOpAtomicCounterAnd, + EOpAtomicCounterOr, + EOpAtomicCounterXor, + EOpAtomicCounterExchange, + EOpAtomicCounterCompSwap, + + EOpAny, + EOpAll, + + EOpCooperativeMatrixLoad, + EOpCooperativeMatrixStore, + EOpCooperativeMatrixMulAdd, + + EOpBeginInvocationInterlock, // Fragment only + EOpEndInvocationInterlock, // Fragment only + + EOpIsHelperInvocation, + + EOpDebugPrintf, + + // + // Branch + // + + EOpKill, // Fragment only + EOpTerminateInvocation, // Fragment only + EOpDemote, // Fragment only + EOpReturn, + EOpBreak, + EOpContinue, + EOpCase, + EOpDefault, + + // + // Constructors + // + + EOpConstructGuardStart, + EOpConstructInt, // these first scalar forms also identify what implicit conversion is needed + EOpConstructUint, + EOpConstructInt8, + EOpConstructUint8, + EOpConstructInt16, + EOpConstructUint16, + EOpConstructInt64, + EOpConstructUint64, + EOpConstructBool, + EOpConstructFloat, + EOpConstructDouble, + // Keep vector and matrix constructors in a consistent relative order for + // TParseContext::constructBuiltIn, which converts between 8/16/32 bit + // vector constructors + EOpConstructVec2, + EOpConstructVec3, + EOpConstructVec4, + EOpConstructMat2x2, + EOpConstructMat2x3, + EOpConstructMat2x4, + EOpConstructMat3x2, + EOpConstructMat3x3, + EOpConstructMat3x4, + EOpConstructMat4x2, + EOpConstructMat4x3, + EOpConstructMat4x4, + EOpConstructDVec2, + EOpConstructDVec3, + EOpConstructDVec4, + EOpConstructBVec2, + EOpConstructBVec3, + EOpConstructBVec4, + EOpConstructI8Vec2, + EOpConstructI8Vec3, + EOpConstructI8Vec4, + EOpConstructU8Vec2, + EOpConstructU8Vec3, + EOpConstructU8Vec4, + EOpConstructI16Vec2, + EOpConstructI16Vec3, + EOpConstructI16Vec4, + EOpConstructU16Vec2, + EOpConstructU16Vec3, + EOpConstructU16Vec4, + EOpConstructIVec2, + EOpConstructIVec3, + EOpConstructIVec4, + EOpConstructUVec2, + EOpConstructUVec3, + EOpConstructUVec4, + EOpConstructI64Vec2, + EOpConstructI64Vec3, + EOpConstructI64Vec4, + EOpConstructU64Vec2, + EOpConstructU64Vec3, + EOpConstructU64Vec4, + EOpConstructDMat2x2, + EOpConstructDMat2x3, + EOpConstructDMat2x4, + EOpConstructDMat3x2, + EOpConstructDMat3x3, + EOpConstructDMat3x4, + EOpConstructDMat4x2, + EOpConstructDMat4x3, + EOpConstructDMat4x4, + EOpConstructIMat2x2, + EOpConstructIMat2x3, + EOpConstructIMat2x4, + EOpConstructIMat3x2, + EOpConstructIMat3x3, + EOpConstructIMat3x4, + EOpConstructIMat4x2, + EOpConstructIMat4x3, + EOpConstructIMat4x4, + EOpConstructUMat2x2, + EOpConstructUMat2x3, + EOpConstructUMat2x4, + EOpConstructUMat3x2, + EOpConstructUMat3x3, + EOpConstructUMat3x4, + EOpConstructUMat4x2, + EOpConstructUMat4x3, + EOpConstructUMat4x4, + EOpConstructBMat2x2, + EOpConstructBMat2x3, + EOpConstructBMat2x4, + EOpConstructBMat3x2, + EOpConstructBMat3x3, + EOpConstructBMat3x4, + EOpConstructBMat4x2, + EOpConstructBMat4x3, + EOpConstructBMat4x4, + EOpConstructFloat16, + EOpConstructF16Vec2, + EOpConstructF16Vec3, + EOpConstructF16Vec4, + EOpConstructF16Mat2x2, + EOpConstructF16Mat2x3, + EOpConstructF16Mat2x4, + EOpConstructF16Mat3x2, + EOpConstructF16Mat3x3, + EOpConstructF16Mat3x4, + EOpConstructF16Mat4x2, + EOpConstructF16Mat4x3, + EOpConstructF16Mat4x4, + EOpConstructStruct, + EOpConstructTextureSampler, + EOpConstructNonuniform, // expected to be transformed away, not present in final AST + EOpConstructReference, + EOpConstructCooperativeMatrix, + EOpConstructGuardEnd, + + // + // moves + // + + EOpAssign, + EOpAddAssign, + EOpSubAssign, + EOpMulAssign, + EOpVectorTimesMatrixAssign, + EOpVectorTimesScalarAssign, + EOpMatrixTimesScalarAssign, + EOpMatrixTimesMatrixAssign, + EOpDivAssign, + EOpModAssign, + EOpAndAssign, + EOpInclusiveOrAssign, + EOpExclusiveOrAssign, + EOpLeftShiftAssign, + EOpRightShiftAssign, + + // + // Array operators + // + + // Can apply to arrays, vectors, or matrices. + // Can be decomposed to a constant at compile time, but this does not always happen, + // due to link-time effects. So, consumer can expect either a link-time sized or + // run-time sized array. + EOpArrayLength, + + // + // Image operations + // + + EOpImageGuardBegin, + + EOpImageQuerySize, + EOpImageQuerySamples, + EOpImageLoad, + EOpImageStore, + EOpImageLoadLod, + EOpImageStoreLod, + EOpImageAtomicAdd, + EOpImageAtomicMin, + EOpImageAtomicMax, + EOpImageAtomicAnd, + EOpImageAtomicOr, + EOpImageAtomicXor, + EOpImageAtomicExchange, + EOpImageAtomicCompSwap, + EOpImageAtomicLoad, + EOpImageAtomicStore, + + EOpSubpassLoad, + EOpSubpassLoadMS, + EOpSparseImageLoad, + EOpSparseImageLoadLod, + + EOpImageGuardEnd, + + // + // Texture operations + // + + EOpTextureGuardBegin, + + EOpTextureQuerySize, + EOpTextureQueryLod, + EOpTextureQueryLevels, + EOpTextureQuerySamples, + + EOpSamplingGuardBegin, + + EOpTexture, + EOpTextureProj, + EOpTextureLod, + EOpTextureOffset, + EOpTextureFetch, + EOpTextureFetchOffset, + EOpTextureProjOffset, + EOpTextureLodOffset, + EOpTextureProjLod, + EOpTextureProjLodOffset, + EOpTextureGrad, + EOpTextureGradOffset, + EOpTextureProjGrad, + EOpTextureProjGradOffset, + EOpTextureGather, + EOpTextureGatherOffset, + EOpTextureGatherOffsets, + EOpTextureClamp, + EOpTextureOffsetClamp, + EOpTextureGradClamp, + EOpTextureGradOffsetClamp, + EOpTextureGatherLod, + EOpTextureGatherLodOffset, + EOpTextureGatherLodOffsets, + EOpFragmentMaskFetch, + EOpFragmentFetch, + + EOpSparseTextureGuardBegin, + + EOpSparseTexture, + EOpSparseTextureLod, + EOpSparseTextureOffset, + EOpSparseTextureFetch, + EOpSparseTextureFetchOffset, + EOpSparseTextureLodOffset, + EOpSparseTextureGrad, + EOpSparseTextureGradOffset, + EOpSparseTextureGather, + EOpSparseTextureGatherOffset, + EOpSparseTextureGatherOffsets, + EOpSparseTexelsResident, + EOpSparseTextureClamp, + EOpSparseTextureOffsetClamp, + EOpSparseTextureGradClamp, + EOpSparseTextureGradOffsetClamp, + EOpSparseTextureGatherLod, + EOpSparseTextureGatherLodOffset, + EOpSparseTextureGatherLodOffsets, + + EOpSparseTextureGuardEnd, + + EOpImageFootprintGuardBegin, + EOpImageSampleFootprintNV, + EOpImageSampleFootprintClampNV, + EOpImageSampleFootprintLodNV, + EOpImageSampleFootprintGradNV, + EOpImageSampleFootprintGradClampNV, + EOpImageFootprintGuardEnd, + EOpSamplingGuardEnd, + EOpTextureGuardEnd, + + // + // Integer operations + // + + EOpAddCarry, + EOpSubBorrow, + EOpUMulExtended, + EOpIMulExtended, + EOpBitfieldExtract, + EOpBitfieldInsert, + EOpBitFieldReverse, + EOpBitCount, + EOpFindLSB, + EOpFindMSB, + + EOpCountLeadingZeros, + EOpCountTrailingZeros, + EOpAbsDifference, + EOpAddSaturate, + EOpSubSaturate, + EOpAverage, + EOpAverageRounded, + EOpMul32x16, + + EOpTrace, + EOpReportIntersection, + EOpIgnoreIntersection, + EOpTerminateRay, + EOpExecuteCallable, + EOpWritePackedPrimitiveIndices4x8NV, + + // + // GL_EXT_ray_query operations + // + + EOpRayQueryInitialize, + EOpRayQueryTerminate, + EOpRayQueryGenerateIntersection, + EOpRayQueryConfirmIntersection, + EOpRayQueryProceed, + EOpRayQueryGetIntersectionType, + EOpRayQueryGetRayTMin, + EOpRayQueryGetRayFlags, + EOpRayQueryGetIntersectionT, + EOpRayQueryGetIntersectionInstanceCustomIndex, + EOpRayQueryGetIntersectionInstanceId, + EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset, + EOpRayQueryGetIntersectionGeometryIndex, + EOpRayQueryGetIntersectionPrimitiveIndex, + EOpRayQueryGetIntersectionBarycentrics, + EOpRayQueryGetIntersectionFrontFace, + EOpRayQueryGetIntersectionCandidateAABBOpaque, + EOpRayQueryGetIntersectionObjectRayDirection, + EOpRayQueryGetIntersectionObjectRayOrigin, + EOpRayQueryGetWorldRayDirection, + EOpRayQueryGetWorldRayOrigin, + EOpRayQueryGetIntersectionObjectToWorld, + EOpRayQueryGetIntersectionWorldToObject, + + // + // HLSL operations + // + + EOpClip, // discard if input value < 0 + EOpIsFinite, + EOpLog10, // base 10 log + EOpRcp, // 1/x + EOpSaturate, // clamp from 0 to 1 + EOpSinCos, // sin and cos in out parameters + EOpGenMul, // mul(x,y) on any of mat/vec/scalars + EOpDst, // x = 1, y=src0.y * src1.y, z=src0.z, w=src1.w + EOpInterlockedAdd, // atomic ops, but uses [optional] out arg instead of return + EOpInterlockedAnd, // ... + EOpInterlockedCompareExchange, // ... + EOpInterlockedCompareStore, // ... + EOpInterlockedExchange, // ... + EOpInterlockedMax, // ... + EOpInterlockedMin, // ... + EOpInterlockedOr, // ... + EOpInterlockedXor, // ... + EOpAllMemoryBarrierWithGroupSync, // memory barriers without non-hlsl AST equivalents + EOpDeviceMemoryBarrier, // ... + EOpDeviceMemoryBarrierWithGroupSync, // ... + EOpWorkgroupMemoryBarrier, // ... + EOpWorkgroupMemoryBarrierWithGroupSync, // ... + EOpEvaluateAttributeSnapped, // InterpolateAtOffset with int position on 16x16 grid + EOpF32tof16, // HLSL conversion: half of a PackHalf2x16 + EOpF16tof32, // HLSL conversion: half of an UnpackHalf2x16 + EOpLit, // HLSL lighting coefficient vector + EOpTextureBias, // HLSL texture bias: will be lowered to EOpTexture + EOpAsDouble, // slightly different from EOpUint64BitsToDouble + EOpD3DCOLORtoUBYTE4, // convert and swizzle 4-component color to UBYTE4 range + + EOpMethodSample, // Texture object methods. These are translated to existing + EOpMethodSampleBias, // AST methods, and exist to represent HLSL semantics until that + EOpMethodSampleCmp, // translation is performed. See HlslParseContext::decomposeSampleMethods(). + EOpMethodSampleCmpLevelZero, // ... + EOpMethodSampleGrad, // ... + EOpMethodSampleLevel, // ... + EOpMethodLoad, // ... + EOpMethodGetDimensions, // ... + EOpMethodGetSamplePosition, // ... + EOpMethodGather, // ... + EOpMethodCalculateLevelOfDetail, // ... + EOpMethodCalculateLevelOfDetailUnclamped, // ... + + // Load already defined above for textures + EOpMethodLoad2, // Structure buffer object methods. These are translated to existing + EOpMethodLoad3, // AST methods, and exist to represent HLSL semantics until that + EOpMethodLoad4, // translation is performed. See HlslParseContext::decomposeSampleMethods(). + EOpMethodStore, // ... + EOpMethodStore2, // ... + EOpMethodStore3, // ... + EOpMethodStore4, // ... + EOpMethodIncrementCounter, // ... + EOpMethodDecrementCounter, // ... + // EOpMethodAppend is defined for geo shaders below + EOpMethodConsume, + + // SM5 texture methods + EOpMethodGatherRed, // These are covered under the above EOpMethodSample comment about + EOpMethodGatherGreen, // translation to existing AST opcodes. They exist temporarily + EOpMethodGatherBlue, // because HLSL arguments are slightly different. + EOpMethodGatherAlpha, // ... + EOpMethodGatherCmp, // ... + EOpMethodGatherCmpRed, // ... + EOpMethodGatherCmpGreen, // ... + EOpMethodGatherCmpBlue, // ... + EOpMethodGatherCmpAlpha, // ... + + // geometry methods + EOpMethodAppend, // Geometry shader methods + EOpMethodRestartStrip, // ... + + // matrix + EOpMatrixSwizzle, // select multiple matrix components (non-column) + + // SM6 wave ops + EOpWaveGetLaneCount, // Will decompose to gl_SubgroupSize. + EOpWaveGetLaneIndex, // Will decompose to gl_SubgroupInvocationID. + EOpWaveActiveCountBits, // Will decompose to subgroupBallotBitCount(subgroupBallot()). + EOpWavePrefixCountBits, // Will decompose to subgroupBallotInclusiveBitCount(subgroupBallot()). + + // Shader Clock Ops + EOpReadClockSubgroupKHR, + EOpReadClockDeviceKHR, +}; + +class TIntermTraverser; +class TIntermOperator; +class TIntermAggregate; +class TIntermUnary; +class TIntermBinary; +class TIntermConstantUnion; +class TIntermSelection; +class TIntermSwitch; +class TIntermBranch; +class TIntermTyped; +class TIntermMethod; +class TIntermSymbol; +class TIntermLoop; + +} // end namespace glslang + +// +// Base class for the tree nodes +// +// (Put outside the glslang namespace, as it's used as part of the external interface.) +// +class TIntermNode { +public: + POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator()) + + TIntermNode() { loc.init(); } + virtual const glslang::TSourceLoc& getLoc() const { return loc; } + virtual void setLoc(const glslang::TSourceLoc& l) { loc = l; } + virtual void traverse(glslang::TIntermTraverser*) = 0; + virtual glslang::TIntermTyped* getAsTyped() { return 0; } + virtual glslang::TIntermOperator* getAsOperator() { return 0; } + virtual glslang::TIntermConstantUnion* getAsConstantUnion() { return 0; } + virtual glslang::TIntermAggregate* getAsAggregate() { return 0; } + virtual glslang::TIntermUnary* getAsUnaryNode() { return 0; } + virtual glslang::TIntermBinary* getAsBinaryNode() { return 0; } + virtual glslang::TIntermSelection* getAsSelectionNode() { return 0; } + virtual glslang::TIntermSwitch* getAsSwitchNode() { return 0; } + virtual glslang::TIntermMethod* getAsMethodNode() { return 0; } + virtual glslang::TIntermSymbol* getAsSymbolNode() { return 0; } + virtual glslang::TIntermBranch* getAsBranchNode() { return 0; } + virtual glslang::TIntermLoop* getAsLoopNode() { return 0; } + + virtual const glslang::TIntermTyped* getAsTyped() const { return 0; } + virtual const glslang::TIntermOperator* getAsOperator() const { return 0; } + virtual const glslang::TIntermConstantUnion* getAsConstantUnion() const { return 0; } + virtual const glslang::TIntermAggregate* getAsAggregate() const { return 0; } + virtual const glslang::TIntermUnary* getAsUnaryNode() const { return 0; } + virtual const glslang::TIntermBinary* getAsBinaryNode() const { return 0; } + virtual const glslang::TIntermSelection* getAsSelectionNode() const { return 0; } + virtual const glslang::TIntermSwitch* getAsSwitchNode() const { return 0; } + virtual const glslang::TIntermMethod* getAsMethodNode() const { return 0; } + virtual const glslang::TIntermSymbol* getAsSymbolNode() const { return 0; } + virtual const glslang::TIntermBranch* getAsBranchNode() const { return 0; } + virtual const glslang::TIntermLoop* getAsLoopNode() const { return 0; } + virtual ~TIntermNode() { } + +protected: + TIntermNode(const TIntermNode&); + TIntermNode& operator=(const TIntermNode&); + glslang::TSourceLoc loc; +}; + +namespace glslang { + +// +// This is just to help yacc. +// +struct TIntermNodePair { + TIntermNode* node1; + TIntermNode* node2; +}; + +// +// Intermediate class for nodes that have a type. +// +class TIntermTyped : public TIntermNode { +public: + TIntermTyped(const TType& t) { type.shallowCopy(t); } + TIntermTyped(TBasicType basicType) { TType bt(basicType); type.shallowCopy(bt); } + virtual TIntermTyped* getAsTyped() { return this; } + virtual const TIntermTyped* getAsTyped() const { return this; } + virtual void setType(const TType& t) { type.shallowCopy(t); } + virtual const TType& getType() const { return type; } + virtual TType& getWritableType() { return type; } + + virtual TBasicType getBasicType() const { return type.getBasicType(); } + virtual TQualifier& getQualifier() { return type.getQualifier(); } + virtual const TQualifier& getQualifier() const { return type.getQualifier(); } + virtual void propagatePrecision(TPrecisionQualifier); + virtual int getVectorSize() const { return type.getVectorSize(); } + virtual int getMatrixCols() const { return type.getMatrixCols(); } + virtual int getMatrixRows() const { return type.getMatrixRows(); } + virtual bool isMatrix() const { return type.isMatrix(); } + virtual bool isArray() const { return type.isArray(); } + virtual bool isVector() const { return type.isVector(); } + virtual bool isScalar() const { return type.isScalar(); } + virtual bool isStruct() const { return type.isStruct(); } + virtual bool isFloatingDomain() const { return type.isFloatingDomain(); } + virtual bool isIntegerDomain() const { return type.isIntegerDomain(); } + bool isAtomic() const { return type.isAtomic(); } + bool isReference() const { return type.isReference(); } + TString getCompleteString() const { return type.getCompleteString(); } + +protected: + TIntermTyped& operator=(const TIntermTyped&); + TType type; +}; + +// +// Handle for, do-while, and while loops. +// +class TIntermLoop : public TIntermNode { +public: + TIntermLoop(TIntermNode* aBody, TIntermTyped* aTest, TIntermTyped* aTerminal, bool testFirst) : + body(aBody), + test(aTest), + terminal(aTerminal), + first(testFirst), + unroll(false), + dontUnroll(false), + dependency(0), + minIterations(0), + maxIterations(iterationsInfinite), + iterationMultiple(1), + peelCount(0), + partialCount(0) + { } + + virtual TIntermLoop* getAsLoopNode() { return this; } + virtual const TIntermLoop* getAsLoopNode() const { return this; } + virtual void traverse(TIntermTraverser*); + TIntermNode* getBody() const { return body; } + TIntermTyped* getTest() const { return test; } + TIntermTyped* getTerminal() const { return terminal; } + bool testFirst() const { return first; } + + void setUnroll() { unroll = true; } + void setDontUnroll() { + dontUnroll = true; + peelCount = 0; + partialCount = 0; + } + bool getUnroll() const { return unroll; } + bool getDontUnroll() const { return dontUnroll; } + + static const unsigned int dependencyInfinite = 0xFFFFFFFF; + static const unsigned int iterationsInfinite = 0xFFFFFFFF; + void setLoopDependency(int d) { dependency = d; } + int getLoopDependency() const { return dependency; } + + void setMinIterations(unsigned int v) { minIterations = v; } + unsigned int getMinIterations() const { return minIterations; } + void setMaxIterations(unsigned int v) { maxIterations = v; } + unsigned int getMaxIterations() const { return maxIterations; } + void setIterationMultiple(unsigned int v) { iterationMultiple = v; } + unsigned int getIterationMultiple() const { return iterationMultiple; } + void setPeelCount(unsigned int v) { + peelCount = v; + dontUnroll = false; + } + unsigned int getPeelCount() const { return peelCount; } + void setPartialCount(unsigned int v) { + partialCount = v; + dontUnroll = false; + } + unsigned int getPartialCount() const { return partialCount; } + +protected: + TIntermNode* body; // code to loop over + TIntermTyped* test; // exit condition associated with loop, could be 0 for 'for' loops + TIntermTyped* terminal; // exists for for-loops + bool first; // true for while and for, not for do-while + bool unroll; // true if unroll requested + bool dontUnroll; // true if request to not unroll + unsigned int dependency; // loop dependency hint; 0 means not set or unknown + unsigned int minIterations; // as per the SPIR-V specification + unsigned int maxIterations; // as per the SPIR-V specification + unsigned int iterationMultiple; // as per the SPIR-V specification + unsigned int peelCount; // as per the SPIR-V specification + unsigned int partialCount; // as per the SPIR-V specification +}; + +// +// Handle case, break, continue, return, and kill. +// +class TIntermBranch : public TIntermNode { +public: + TIntermBranch(TOperator op, TIntermTyped* e) : + flowOp(op), + expression(e) { } + virtual TIntermBranch* getAsBranchNode() { return this; } + virtual const TIntermBranch* getAsBranchNode() const { return this; } + virtual void traverse(TIntermTraverser*); + TOperator getFlowOp() const { return flowOp; } + TIntermTyped* getExpression() const { return expression; } + void setExpression(TIntermTyped* pExpression) { expression = pExpression; } + void updatePrecision(TPrecisionQualifier parentPrecision); +protected: + TOperator flowOp; + TIntermTyped* expression; +}; + +// +// Represent method names before seeing their calling signature +// or resolving them to operations. Just an expression as the base object +// and a textural name. +// +class TIntermMethod : public TIntermTyped { +public: + TIntermMethod(TIntermTyped* o, const TType& t, const TString& m) : TIntermTyped(t), object(o), method(m) { } + virtual TIntermMethod* getAsMethodNode() { return this; } + virtual const TIntermMethod* getAsMethodNode() const { return this; } + virtual const TString& getMethodName() const { return method; } + virtual TIntermTyped* getObject() const { return object; } + virtual void traverse(TIntermTraverser*); +protected: + TIntermTyped* object; + TString method; +}; + +// +// Nodes that correspond to symbols or constants in the source code. +// +class TIntermSymbol : public TIntermTyped { +public: + // if symbol is initialized as symbol(sym), the memory comes from the pool allocator of sym. If sym comes from + // per process threadPoolAllocator, then it causes increased memory usage per compile + // it is essential to use "symbol = sym" to assign to symbol + TIntermSymbol(int i, const TString& n, const TType& t) + : TIntermTyped(t), id(i), +#ifndef GLSLANG_WEB + flattenSubset(-1), +#endif + constSubtree(nullptr) + { name = n; } + virtual int getId() const { return id; } + virtual void changeId(int i) { id = i; } + virtual const TString& getName() const { return name; } + virtual void traverse(TIntermTraverser*); + virtual TIntermSymbol* getAsSymbolNode() { return this; } + virtual const TIntermSymbol* getAsSymbolNode() const { return this; } + void setConstArray(const TConstUnionArray& c) { constArray = c; } + const TConstUnionArray& getConstArray() const { return constArray; } + void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + TIntermTyped* getConstSubtree() const { return constSubtree; } +#ifndef GLSLANG_WEB + void setFlattenSubset(int subset) { flattenSubset = subset; } + virtual const TString& getAccessName() const; + + int getFlattenSubset() const { return flattenSubset; } // -1 means full object +#endif + + // This is meant for cases where a node has already been constructed, and + // later on, it becomes necessary to switch to a different symbol. + virtual void switchId(int newId) { id = newId; } + +protected: + int id; // the unique id of the symbol this node represents +#ifndef GLSLANG_WEB + int flattenSubset; // how deeply the flattened object rooted at id has been dereferenced +#endif + TString name; // the name of the symbol this node represents + TConstUnionArray constArray; // if the symbol is a front-end compile-time constant, this is its value + TIntermTyped* constSubtree; +}; + +class TIntermConstantUnion : public TIntermTyped { +public: + TIntermConstantUnion(const TConstUnionArray& ua, const TType& t) : TIntermTyped(t), constArray(ua), literal(false) { } + const TConstUnionArray& getConstArray() const { return constArray; } + virtual TIntermConstantUnion* getAsConstantUnion() { return this; } + virtual const TIntermConstantUnion* getAsConstantUnion() const { return this; } + virtual void traverse(TIntermTraverser*); + virtual TIntermTyped* fold(TOperator, const TIntermTyped*) const; + virtual TIntermTyped* fold(TOperator, const TType&) const; + void setLiteral() { literal = true; } + void setExpression() { literal = false; } + bool isLiteral() const { return literal; } + +protected: + TIntermConstantUnion& operator=(const TIntermConstantUnion&); + + const TConstUnionArray constArray; + bool literal; // true if node represents a literal in the source code +}; + +// Represent the independent aspects of a texturing TOperator +struct TCrackedTextureOp { + bool query; + bool proj; + bool lod; + bool fetch; + bool offset; + bool offsets; + bool gather; + bool grad; + bool subpass; + bool lodClamp; + bool fragMask; +}; + +// +// Intermediate class for node types that hold operators. +// +class TIntermOperator : public TIntermTyped { +public: + virtual TIntermOperator* getAsOperator() { return this; } + virtual const TIntermOperator* getAsOperator() const { return this; } + TOperator getOp() const { return op; } + void setOp(TOperator newOp) { op = newOp; } + bool modifiesState() const; + bool isConstructor() const; + bool isTexture() const { return op > EOpTextureGuardBegin && op < EOpTextureGuardEnd; } + bool isSampling() const { return op > EOpSamplingGuardBegin && op < EOpSamplingGuardEnd; } +#ifdef GLSLANG_WEB + bool isImage() const { return false; } + bool isSparseTexture() const { return false; } + bool isImageFootprint() const { return false; } + bool isSparseImage() const { return false; } + bool isSubgroup() const { return false; } +#else + bool isImage() const { return op > EOpImageGuardBegin && op < EOpImageGuardEnd; } + bool isSparseTexture() const { return op > EOpSparseTextureGuardBegin && op < EOpSparseTextureGuardEnd; } + bool isImageFootprint() const { return op > EOpImageFootprintGuardBegin && op < EOpImageFootprintGuardEnd; } + bool isSparseImage() const { return op == EOpSparseImageLoad; } + bool isSubgroup() const { return op > EOpSubgroupGuardStart && op < EOpSubgroupGuardStop; } +#endif + + void setOperationPrecision(TPrecisionQualifier p) { operationPrecision = p; } + TPrecisionQualifier getOperationPrecision() const { return operationPrecision != EpqNone ? + operationPrecision : + type.getQualifier().precision; } + TString getCompleteString() const + { + TString cs = type.getCompleteString(); + if (getOperationPrecision() != type.getQualifier().precision) { + cs += ", operation at "; + cs += GetPrecisionQualifierString(getOperationPrecision()); + } + + return cs; + } + + // Crack the op into the individual dimensions of texturing operation. + void crackTexture(TSampler sampler, TCrackedTextureOp& cracked) const + { + cracked.query = false; + cracked.proj = false; + cracked.lod = false; + cracked.fetch = false; + cracked.offset = false; + cracked.offsets = false; + cracked.gather = false; + cracked.grad = false; + cracked.subpass = false; + cracked.lodClamp = false; + cracked.fragMask = false; + + switch (op) { + case EOpImageQuerySize: + case EOpImageQuerySamples: + case EOpTextureQuerySize: + case EOpTextureQueryLod: + case EOpTextureQueryLevels: + case EOpTextureQuerySamples: + case EOpSparseTexelsResident: + cracked.query = true; + break; + case EOpTexture: + case EOpSparseTexture: + break; + case EOpTextureProj: + cracked.proj = true; + break; + case EOpTextureLod: + case EOpSparseTextureLod: + cracked.lod = true; + break; + case EOpTextureOffset: + case EOpSparseTextureOffset: + cracked.offset = true; + break; + case EOpTextureFetch: + case EOpSparseTextureFetch: + cracked.fetch = true; + if (sampler.is1D() || (sampler.dim == Esd2D && ! sampler.isMultiSample()) || sampler.dim == Esd3D) + cracked.lod = true; + break; + case EOpTextureFetchOffset: + case EOpSparseTextureFetchOffset: + cracked.fetch = true; + cracked.offset = true; + if (sampler.is1D() || (sampler.dim == Esd2D && ! sampler.isMultiSample()) || sampler.dim == Esd3D) + cracked.lod = true; + break; + case EOpTextureProjOffset: + cracked.offset = true; + cracked.proj = true; + break; + case EOpTextureLodOffset: + case EOpSparseTextureLodOffset: + cracked.offset = true; + cracked.lod = true; + break; + case EOpTextureProjLod: + cracked.lod = true; + cracked.proj = true; + break; + case EOpTextureProjLodOffset: + cracked.offset = true; + cracked.lod = true; + cracked.proj = true; + break; + case EOpTextureGrad: + case EOpSparseTextureGrad: + cracked.grad = true; + break; + case EOpTextureGradOffset: + case EOpSparseTextureGradOffset: + cracked.grad = true; + cracked.offset = true; + break; + case EOpTextureProjGrad: + cracked.grad = true; + cracked.proj = true; + break; + case EOpTextureProjGradOffset: + cracked.grad = true; + cracked.offset = true; + cracked.proj = true; + break; +#ifndef GLSLANG_WEB + case EOpTextureClamp: + case EOpSparseTextureClamp: + cracked.lodClamp = true; + break; + case EOpTextureOffsetClamp: + case EOpSparseTextureOffsetClamp: + cracked.offset = true; + cracked.lodClamp = true; + break; + case EOpTextureGradClamp: + case EOpSparseTextureGradClamp: + cracked.grad = true; + cracked.lodClamp = true; + break; + case EOpTextureGradOffsetClamp: + case EOpSparseTextureGradOffsetClamp: + cracked.grad = true; + cracked.offset = true; + cracked.lodClamp = true; + break; + case EOpTextureGather: + case EOpSparseTextureGather: + cracked.gather = true; + break; + case EOpTextureGatherOffset: + case EOpSparseTextureGatherOffset: + cracked.gather = true; + cracked.offset = true; + break; + case EOpTextureGatherOffsets: + case EOpSparseTextureGatherOffsets: + cracked.gather = true; + cracked.offsets = true; + break; + case EOpTextureGatherLod: + case EOpSparseTextureGatherLod: + cracked.gather = true; + cracked.lod = true; + break; + case EOpTextureGatherLodOffset: + case EOpSparseTextureGatherLodOffset: + cracked.gather = true; + cracked.offset = true; + cracked.lod = true; + break; + case EOpTextureGatherLodOffsets: + case EOpSparseTextureGatherLodOffsets: + cracked.gather = true; + cracked.offsets = true; + cracked.lod = true; + break; + case EOpImageLoadLod: + case EOpImageStoreLod: + case EOpSparseImageLoadLod: + cracked.lod = true; + break; + case EOpFragmentMaskFetch: + cracked.subpass = sampler.dim == EsdSubpass; + cracked.fragMask = true; + break; + case EOpFragmentFetch: + cracked.subpass = sampler.dim == EsdSubpass; + cracked.fragMask = true; + break; + case EOpImageSampleFootprintNV: + break; + case EOpImageSampleFootprintClampNV: + cracked.lodClamp = true; + break; + case EOpImageSampleFootprintLodNV: + cracked.lod = true; + break; + case EOpImageSampleFootprintGradNV: + cracked.grad = true; + break; + case EOpImageSampleFootprintGradClampNV: + cracked.lodClamp = true; + cracked.grad = true; + break; + case EOpSubpassLoad: + case EOpSubpassLoadMS: + cracked.subpass = true; + break; +#endif + default: + break; + } + } + +protected: + TIntermOperator(TOperator o) : TIntermTyped(EbtFloat), op(o), operationPrecision(EpqNone) {} + TIntermOperator(TOperator o, TType& t) : TIntermTyped(t), op(o), operationPrecision(EpqNone) {} + TOperator op; + // The result precision is in the inherited TType, and is usually meant to be both + // the operation precision and the result precision. However, some more complex things, + // like built-in function calls, distinguish between the two, in which case non-EqpNone + // 'operationPrecision' overrides the result precision as far as operation precision + // is concerned. + TPrecisionQualifier operationPrecision; +}; + +// +// Nodes for all the basic binary math operators. +// +class TIntermBinary : public TIntermOperator { +public: + TIntermBinary(TOperator o) : TIntermOperator(o) {} + virtual void traverse(TIntermTraverser*); + virtual void setLeft(TIntermTyped* n) { left = n; } + virtual void setRight(TIntermTyped* n) { right = n; } + virtual TIntermTyped* getLeft() const { return left; } + virtual TIntermTyped* getRight() const { return right; } + virtual TIntermBinary* getAsBinaryNode() { return this; } + virtual const TIntermBinary* getAsBinaryNode() const { return this; } + virtual void updatePrecision(); +protected: + TIntermTyped* left; + TIntermTyped* right; +}; + +// +// Nodes for unary math operators. +// +class TIntermUnary : public TIntermOperator { +public: + TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0) {} + TIntermUnary(TOperator o) : TIntermOperator(o), operand(0) {} + virtual void traverse(TIntermTraverser*); + virtual void setOperand(TIntermTyped* o) { operand = o; } + virtual TIntermTyped* getOperand() { return operand; } + virtual const TIntermTyped* getOperand() const { return operand; } + virtual TIntermUnary* getAsUnaryNode() { return this; } + virtual const TIntermUnary* getAsUnaryNode() const { return this; } + virtual void updatePrecision(); +protected: + TIntermTyped* operand; +}; + +typedef TVector TIntermSequence; +typedef TVector TQualifierList; +// +// Nodes that operate on an arbitrary sized set of children. +// +class TIntermAggregate : public TIntermOperator { +public: + TIntermAggregate() : TIntermOperator(EOpNull), userDefined(false), pragmaTable(nullptr) { } + TIntermAggregate(TOperator o) : TIntermOperator(o), pragmaTable(nullptr) { } + ~TIntermAggregate() { delete pragmaTable; } + virtual TIntermAggregate* getAsAggregate() { return this; } + virtual const TIntermAggregate* getAsAggregate() const { return this; } + virtual void setOperator(TOperator o) { op = o; } + virtual TIntermSequence& getSequence() { return sequence; } + virtual const TIntermSequence& getSequence() const { return sequence; } + virtual void setName(const TString& n) { name = n; } + virtual const TString& getName() const { return name; } + virtual void traverse(TIntermTraverser*); + virtual void setUserDefined() { userDefined = true; } + virtual bool isUserDefined() { return userDefined; } + virtual TQualifierList& getQualifierList() { return qualifier; } + virtual const TQualifierList& getQualifierList() const { return qualifier; } + void setOptimize(bool o) { optimize = o; } + void setDebug(bool d) { debug = d; } + bool getOptimize() const { return optimize; } + bool getDebug() const { return debug; } + void setPragmaTable(const TPragmaTable& pTable); + const TPragmaTable& getPragmaTable() const { return *pragmaTable; } +protected: + TIntermAggregate(const TIntermAggregate&); // disallow copy constructor + TIntermAggregate& operator=(const TIntermAggregate&); // disallow assignment operator + TIntermSequence sequence; + TQualifierList qualifier; + TString name; + bool userDefined; // used for user defined function names + bool optimize; + bool debug; + TPragmaTable* pragmaTable; +}; + +// +// For if tests. +// +class TIntermSelection : public TIntermTyped { +public: + TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB) : + TIntermTyped(EbtVoid), condition(cond), trueBlock(trueB), falseBlock(falseB), + shortCircuit(true), + flatten(false), dontFlatten(false) {} + TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) : + TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB), + shortCircuit(true), + flatten(false), dontFlatten(false) {} + virtual void traverse(TIntermTraverser*); + virtual TIntermTyped* getCondition() const { return condition; } + virtual TIntermNode* getTrueBlock() const { return trueBlock; } + virtual TIntermNode* getFalseBlock() const { return falseBlock; } + virtual TIntermSelection* getAsSelectionNode() { return this; } + virtual const TIntermSelection* getAsSelectionNode() const { return this; } + + void setNoShortCircuit() { shortCircuit = false; } + bool getShortCircuit() const { return shortCircuit; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + +protected: + TIntermTyped* condition; + TIntermNode* trueBlock; + TIntermNode* falseBlock; + bool shortCircuit; // normally all if-then-else and all GLSL ?: short-circuit, but HLSL ?: does not + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten +}; + +// +// For switch statements. Designed use is that a switch will have sequence of nodes +// that are either case/default nodes or a *single* node that represents all the code +// in between (if any) consecutive case/defaults. So, a traversal need only deal with +// 0 or 1 nodes per case/default statement. +// +class TIntermSwitch : public TIntermNode { +public: + TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b), + flatten(false), dontFlatten(false) {} + virtual void traverse(TIntermTraverser*); + virtual TIntermNode* getCondition() const { return condition; } + virtual TIntermAggregate* getBody() const { return body; } + virtual TIntermSwitch* getAsSwitchNode() { return this; } + virtual const TIntermSwitch* getAsSwitchNode() const { return this; } + + void setFlatten() { flatten = true; } + void setDontFlatten() { dontFlatten = true; } + bool getFlatten() const { return flatten; } + bool getDontFlatten() const { return dontFlatten; } + +protected: + TIntermTyped* condition; + TIntermAggregate* body; + bool flatten; // true if flatten requested + bool dontFlatten; // true if requested to not flatten +}; + +enum TVisit +{ + EvPreVisit, + EvInVisit, + EvPostVisit +}; + +// +// For traversing the tree. User should derive from this, +// put their traversal specific data in it, and then pass +// it to a Traverse method. +// +// When using this, just fill in the methods for nodes you want visited. +// Return false from a pre-visit to skip visiting that node's subtree. +// +// Explicitly set postVisit to true if you want post visiting, otherwise, +// filled in methods will only be called at pre-visit time (before processing +// the subtree). Similarly for inVisit for in-order visiting of nodes with +// multiple children. +// +// If you only want post-visits, explicitly turn off preVisit (and inVisit) +// and turn on postVisit. +// +// In general, for the visit*() methods, return true from interior nodes +// to have the traversal continue on to children. +// +// If you process children yourself, or don't want them processed, return false. +// +class TIntermTraverser { +public: + POOL_ALLOCATOR_NEW_DELETE(glslang::GetThreadPoolAllocator()) + TIntermTraverser(bool preVisit = true, bool inVisit = false, bool postVisit = false, bool rightToLeft = false) : + preVisit(preVisit), + inVisit(inVisit), + postVisit(postVisit), + rightToLeft(rightToLeft), + depth(0), + maxDepth(0) { } + virtual ~TIntermTraverser() { } + + virtual void visitSymbol(TIntermSymbol*) { } + virtual void visitConstantUnion(TIntermConstantUnion*) { } + virtual bool visitBinary(TVisit, TIntermBinary*) { return true; } + virtual bool visitUnary(TVisit, TIntermUnary*) { return true; } + virtual bool visitSelection(TVisit, TIntermSelection*) { return true; } + virtual bool visitAggregate(TVisit, TIntermAggregate*) { return true; } + virtual bool visitLoop(TVisit, TIntermLoop*) { return true; } + virtual bool visitBranch(TVisit, TIntermBranch*) { return true; } + virtual bool visitSwitch(TVisit, TIntermSwitch*) { return true; } + + int getMaxDepth() const { return maxDepth; } + + void incrementDepth(TIntermNode *current) + { + depth++; + maxDepth = (std::max)(maxDepth, depth); + path.push_back(current); + } + + void decrementDepth() + { + depth--; + path.pop_back(); + } + + TIntermNode *getParentNode() + { + return path.size() == 0 ? NULL : path.back(); + } + + const bool preVisit; + const bool inVisit; + const bool postVisit; + const bool rightToLeft; + +protected: + TIntermTraverser& operator=(TIntermTraverser&); + + int depth; + int maxDepth; + + // All the nodes from root to the current node's parent during traversing. + TVector path; +}; + +// KHR_vulkan_glsl says "Two arrays sized with specialization constants are the same type only if +// sized with the same symbol, involving no operations" +inline bool SameSpecializationConstants(TIntermTyped* node1, TIntermTyped* node2) +{ + return node1->getAsSymbolNode() && node2->getAsSymbolNode() && + node1->getAsSymbolNode()->getId() == node2->getAsSymbolNode()->getId(); +} + +} // end namespace glslang + +#endif // __INTERMEDIATE_H diff --git a/third_party/glslang/glslang/MachineIndependent/Constant.cpp b/third_party/glslang/glslang/MachineIndependent/Constant.cpp new file mode 100644 index 0000000..e21cf42 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/Constant.cpp @@ -0,0 +1,1428 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2018-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "localintermediate.h" +#include +#include +#include +#include + +namespace { + +using namespace glslang; + +typedef union { + double d; + int i[2]; +} DoubleIntUnion; + +// Some helper functions + +bool isNan(double x) +{ + DoubleIntUnion u; + // tough to find a platform independent library function, do it directly + u.d = x; + int bitPatternL = u.i[0]; + int bitPatternH = u.i[1]; + return (bitPatternH & 0x7ff80000) == 0x7ff80000 && + ((bitPatternH & 0xFFFFF) != 0 || bitPatternL != 0); +} + +bool isInf(double x) +{ + DoubleIntUnion u; + // tough to find a platform independent library function, do it directly + u.d = x; + int bitPatternL = u.i[0]; + int bitPatternH = u.i[1]; + return (bitPatternH & 0x7ff00000) == 0x7ff00000 && + (bitPatternH & 0xFFFFF) == 0 && bitPatternL == 0; +} + +const double pi = 3.1415926535897932384626433832795; + +} // end anonymous namespace + + +namespace glslang { + +// +// The fold functions see if an operation on a constant can be done in place, +// without generating run-time code. +// +// Returns the node to keep using, which may or may not be the node passed in. +// +// Note: As of version 1.2, all constant operations must be folded. It is +// not opportunistic, but rather a semantic requirement. +// + +// +// Do folding between a pair of nodes. +// 'this' is the left-hand operand and 'rightConstantNode' is the right-hand operand. +// +// Returns a new node representing the result. +// +TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TIntermTyped* rightConstantNode) const +{ + // For most cases, the return type matches the argument type, so set that + // up and just code to exceptions below. + TType returnType; + returnType.shallowCopy(getType()); + + // + // A pair of nodes is to be folded together + // + + const TIntermConstantUnion *rightNode = rightConstantNode->getAsConstantUnion(); + TConstUnionArray leftUnionArray = getConstArray(); + TConstUnionArray rightUnionArray = rightNode->getConstArray(); + + // Figure out the size of the result + int newComps; + int constComps; + switch(op) { + case EOpMatrixTimesMatrix: + newComps = rightNode->getMatrixCols() * getMatrixRows(); + break; + case EOpMatrixTimesVector: + newComps = getMatrixRows(); + break; + case EOpVectorTimesMatrix: + newComps = rightNode->getMatrixCols(); + break; + default: + newComps = getType().computeNumComponents(); + constComps = rightConstantNode->getType().computeNumComponents(); + if (constComps == 1 && newComps > 1) { + // for a case like vec4 f = vec4(2,3,4,5) + 1.2; + TConstUnionArray smearedArray(newComps, rightNode->getConstArray()[0]); + rightUnionArray = smearedArray; + } else if (constComps > 1 && newComps == 1) { + // for a case like vec4 f = 1.2 + vec4(2,3,4,5); + newComps = constComps; + rightUnionArray = rightNode->getConstArray(); + TConstUnionArray smearedArray(newComps, getConstArray()[0]); + leftUnionArray = smearedArray; + returnType.shallowCopy(rightNode->getType()); + } + break; + } + + TConstUnionArray newConstArray(newComps); + TType constBool(EbtBool, EvqConst); + + switch(op) { + case EOpAdd: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] + rightUnionArray[i]; + break; + case EOpSub: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] - rightUnionArray[i]; + break; + + case EOpMul: + case EOpVectorTimesScalar: + case EOpMatrixTimesScalar: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] * rightUnionArray[i]; + break; + case EOpMatrixTimesMatrix: + for (int row = 0; row < getMatrixRows(); row++) { + for (int column = 0; column < rightNode->getMatrixCols(); column++) { + double sum = 0.0f; + for (int i = 0; i < rightNode->getMatrixRows(); i++) + sum += leftUnionArray[i * getMatrixRows() + row].getDConst() * rightUnionArray[column * rightNode->getMatrixRows() + i].getDConst(); + newConstArray[column * getMatrixRows() + row].setDConst(sum); + } + } + returnType.shallowCopy(TType(getType().getBasicType(), EvqConst, 0, rightNode->getMatrixCols(), getMatrixRows())); + break; + case EOpDiv: + for (int i = 0; i < newComps; i++) { + switch (getType().getBasicType()) { + case EbtDouble: + case EbtFloat: + case EbtFloat16: + if (rightUnionArray[i].getDConst() != 0.0) + newConstArray[i].setDConst(leftUnionArray[i].getDConst() / rightUnionArray[i].getDConst()); + else if (leftUnionArray[i].getDConst() > 0.0) + newConstArray[i].setDConst((double)INFINITY); + else if (leftUnionArray[i].getDConst() < 0.0) + newConstArray[i].setDConst(-(double)INFINITY); + else + newConstArray[i].setDConst((double)NAN); + break; + + case EbtInt: + if (rightUnionArray[i] == 0) + newConstArray[i].setIConst(0x7FFFFFFF); + else if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == (int)-0x80000000ll) + newConstArray[i].setIConst((int)-0x80000000ll); + else + newConstArray[i].setIConst(leftUnionArray[i].getIConst() / rightUnionArray[i].getIConst()); + break; + + case EbtUint: + if (rightUnionArray[i] == 0u) + newConstArray[i].setUConst(0xFFFFFFFFu); + else + newConstArray[i].setUConst(leftUnionArray[i].getUConst() / rightUnionArray[i].getUConst()); + break; + +#ifndef GLSLANG_WEB + case EbtInt8: + if (rightUnionArray[i] == (signed char)0) + newConstArray[i].setI8Const((signed char)0x7F); + else if (rightUnionArray[i].getI8Const() == (signed char)-1 && leftUnionArray[i].getI8Const() == (signed char)-0x80) + newConstArray[i].setI8Const((signed char)-0x80); + else + newConstArray[i].setI8Const(leftUnionArray[i].getI8Const() / rightUnionArray[i].getI8Const()); + break; + + case EbtUint8: + if (rightUnionArray[i] == (unsigned char)0u) + newConstArray[i].setU8Const((unsigned char)0xFFu); + else + newConstArray[i].setU8Const(leftUnionArray[i].getU8Const() / rightUnionArray[i].getU8Const()); + break; + + case EbtInt16: + if (rightUnionArray[i] == (signed short)0) + newConstArray[i].setI16Const((signed short)0x7FFF); + else if (rightUnionArray[i].getI16Const() == (signed short)-1 && leftUnionArray[i].getI16Const() == (signed short)-0x8000) + newConstArray[i].setI16Const((signed short)-0x8000); + else + newConstArray[i].setI16Const(leftUnionArray[i].getI16Const() / rightUnionArray[i].getI16Const()); + break; + + case EbtUint16: + if (rightUnionArray[i] == (unsigned short)0u) + newConstArray[i].setU16Const((unsigned short)0xFFFFu); + else + newConstArray[i].setU16Const(leftUnionArray[i].getU16Const() / rightUnionArray[i].getU16Const()); + break; + + case EbtInt64: + if (rightUnionArray[i] == 0ll) + newConstArray[i].setI64Const(0x7FFFFFFFFFFFFFFFll); + else if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == (long long)-0x8000000000000000ll) + newConstArray[i].setI64Const((long long)-0x8000000000000000ll); + else + newConstArray[i].setI64Const(leftUnionArray[i].getI64Const() / rightUnionArray[i].getI64Const()); + break; + + case EbtUint64: + if (rightUnionArray[i] == 0ull) + newConstArray[i].setU64Const(0xFFFFFFFFFFFFFFFFull); + else + newConstArray[i].setU64Const(leftUnionArray[i].getU64Const() / rightUnionArray[i].getU64Const()); + break; + default: + return 0; +#endif + } + } + break; + + case EOpMatrixTimesVector: + for (int i = 0; i < getMatrixRows(); i++) { + double sum = 0.0f; + for (int j = 0; j < rightNode->getVectorSize(); j++) { + sum += leftUnionArray[j*getMatrixRows() + i].getDConst() * rightUnionArray[j].getDConst(); + } + newConstArray[i].setDConst(sum); + } + + returnType.shallowCopy(TType(getBasicType(), EvqConst, getMatrixRows())); + break; + + case EOpVectorTimesMatrix: + for (int i = 0; i < rightNode->getMatrixCols(); i++) { + double sum = 0.0f; + for (int j = 0; j < getVectorSize(); j++) + sum += leftUnionArray[j].getDConst() * rightUnionArray[i*rightNode->getMatrixRows() + j].getDConst(); + newConstArray[i].setDConst(sum); + } + + returnType.shallowCopy(TType(getBasicType(), EvqConst, rightNode->getMatrixCols())); + break; + + case EOpMod: + for (int i = 0; i < newComps; i++) { + if (rightUnionArray[i] == 0) + newConstArray[i] = leftUnionArray[i]; + else { + switch (getType().getBasicType()) { + case EbtInt: + if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == INT_MIN) { + newConstArray[i].setIConst(0); + break; + } else goto modulo_default; +#ifndef GLSLANG_WEB + case EbtInt64: + if (rightUnionArray[i].getI64Const() == -1 && leftUnionArray[i].getI64Const() == LLONG_MIN) { + newConstArray[i].setI64Const(0); + break; + } else goto modulo_default; + case EbtInt16: + if (rightUnionArray[i].getIConst() == -1 && leftUnionArray[i].getIConst() == SHRT_MIN) { + newConstArray[i].setIConst(0); + break; + } else goto modulo_default; +#endif + default: + modulo_default: + newConstArray[i] = leftUnionArray[i] % rightUnionArray[i]; + } + } + } + break; + + case EOpRightShift: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] >> rightUnionArray[i]; + break; + + case EOpLeftShift: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] << rightUnionArray[i]; + break; + + case EOpAnd: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] & rightUnionArray[i]; + break; + case EOpInclusiveOr: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] | rightUnionArray[i]; + break; + case EOpExclusiveOr: + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] ^ rightUnionArray[i]; + break; + + case EOpLogicalAnd: // this code is written for possible future use, will not get executed currently + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] && rightUnionArray[i]; + break; + + case EOpLogicalOr: // this code is written for possible future use, will not get executed currently + for (int i = 0; i < newComps; i++) + newConstArray[i] = leftUnionArray[i] || rightUnionArray[i]; + break; + + case EOpLogicalXor: + for (int i = 0; i < newComps; i++) { + switch (getType().getBasicType()) { + case EbtBool: newConstArray[i].setBConst((leftUnionArray[i] == rightUnionArray[i]) ? false : true); break; + default: assert(false && "Default missing"); + } + } + break; + + case EOpLessThan: + newConstArray[0].setBConst(leftUnionArray[0] < rightUnionArray[0]); + returnType.shallowCopy(constBool); + break; + case EOpGreaterThan: + newConstArray[0].setBConst(leftUnionArray[0] > rightUnionArray[0]); + returnType.shallowCopy(constBool); + break; + case EOpLessThanEqual: + newConstArray[0].setBConst(! (leftUnionArray[0] > rightUnionArray[0])); + returnType.shallowCopy(constBool); + break; + case EOpGreaterThanEqual: + newConstArray[0].setBConst(! (leftUnionArray[0] < rightUnionArray[0])); + returnType.shallowCopy(constBool); + break; + case EOpEqual: + newConstArray[0].setBConst(rightNode->getConstArray() == leftUnionArray); + returnType.shallowCopy(constBool); + break; + case EOpNotEqual: + newConstArray[0].setBConst(rightNode->getConstArray() != leftUnionArray); + returnType.shallowCopy(constBool); + break; + + default: + return 0; + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, returnType); + newNode->setLoc(getLoc()); + + return newNode; +} + +// +// Do single unary node folding +// +// Returns a new node representing the result. +// +TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TType& returnType) const +{ + // First, size the result, which is mostly the same as the argument's size, + // but not always, and classify what is componentwise. + // Also, eliminate cases that can't be compile-time constant. + int resultSize; + bool componentWise = true; + + int objectSize = getType().computeNumComponents(); + switch (op) { + case EOpDeterminant: + case EOpAny: + case EOpAll: + case EOpLength: + componentWise = false; + resultSize = 1; + break; + + case EOpEmitStreamVertex: + case EOpEndStreamPrimitive: + // These don't fold + return nullptr; + + case EOpPackSnorm2x16: + case EOpPackUnorm2x16: + case EOpPackHalf2x16: + componentWise = false; + resultSize = 1; + break; + + case EOpUnpackSnorm2x16: + case EOpUnpackUnorm2x16: + case EOpUnpackHalf2x16: + componentWise = false; + resultSize = 2; + break; + + case EOpPack16: + case EOpPack32: + case EOpPack64: + case EOpUnpack32: + case EOpUnpack16: + case EOpUnpack8: + case EOpNormalize: + componentWise = false; + resultSize = objectSize; + break; + + default: + resultSize = objectSize; + break; + } + + // Set up for processing + TConstUnionArray newConstArray(resultSize); + const TConstUnionArray& unionArray = getConstArray(); + + // Process non-component-wise operations + switch (op) { + case EOpLength: + case EOpNormalize: + { + double sum = 0; + for (int i = 0; i < objectSize; i++) + sum += unionArray[i].getDConst() * unionArray[i].getDConst(); + double length = sqrt(sum); + if (op == EOpLength) + newConstArray[0].setDConst(length); + else { + for (int i = 0; i < objectSize; i++) + newConstArray[i].setDConst(unionArray[i].getDConst() / length); + } + break; + } + + case EOpAny: + { + bool result = false; + for (int i = 0; i < objectSize; i++) { + if (unionArray[i].getBConst()) + result = true; + } + newConstArray[0].setBConst(result); + break; + } + case EOpAll: + { + bool result = true; + for (int i = 0; i < objectSize; i++) { + if (! unionArray[i].getBConst()) + result = false; + } + newConstArray[0].setBConst(result); + break; + } + + case EOpPackSnorm2x16: + case EOpPackUnorm2x16: + case EOpPackHalf2x16: + case EOpPack16: + case EOpPack32: + case EOpPack64: + case EOpUnpack32: + case EOpUnpack16: + case EOpUnpack8: + + case EOpUnpackSnorm2x16: + case EOpUnpackUnorm2x16: + case EOpUnpackHalf2x16: + + case EOpDeterminant: + case EOpMatrixInverse: + case EOpTranspose: + return nullptr; + + default: + assert(componentWise); + break; + } + + // Turn off the componentwise loop + if (! componentWise) + objectSize = 0; + + // Process component-wise operations + for (int i = 0; i < objectSize; i++) { + switch (op) { + case EOpNegative: + switch (getType().getBasicType()) { + case EbtDouble: + case EbtFloat16: + case EbtFloat: newConstArray[i].setDConst(-unionArray[i].getDConst()); break; + case EbtInt: newConstArray[i].setIConst(-unionArray[i].getIConst()); break; + case EbtUint: newConstArray[i].setUConst(static_cast(-static_cast(unionArray[i].getUConst()))); break; +#ifndef GLSLANG_WEB + case EbtInt8: newConstArray[i].setI8Const(-unionArray[i].getI8Const()); break; + case EbtUint8: newConstArray[i].setU8Const(static_cast(-static_cast(unionArray[i].getU8Const()))); break; + case EbtInt16: newConstArray[i].setI16Const(-unionArray[i].getI16Const()); break; + case EbtUint16:newConstArray[i].setU16Const(static_cast(-static_cast(unionArray[i].getU16Const()))); break; + case EbtInt64: newConstArray[i].setI64Const(-unionArray[i].getI64Const()); break; + case EbtUint64: newConstArray[i].setU64Const(static_cast(-static_cast(unionArray[i].getU64Const()))); break; +#endif + default: + return nullptr; + } + break; + case EOpLogicalNot: + case EOpVectorLogicalNot: + switch (getType().getBasicType()) { + case EbtBool: newConstArray[i].setBConst(!unionArray[i].getBConst()); break; + default: + return nullptr; + } + break; + case EOpBitwiseNot: + newConstArray[i] = ~unionArray[i]; + break; + case EOpRadians: + newConstArray[i].setDConst(unionArray[i].getDConst() * pi / 180.0); + break; + case EOpDegrees: + newConstArray[i].setDConst(unionArray[i].getDConst() * 180.0 / pi); + break; + case EOpSin: + newConstArray[i].setDConst(sin(unionArray[i].getDConst())); + break; + case EOpCos: + newConstArray[i].setDConst(cos(unionArray[i].getDConst())); + break; + case EOpTan: + newConstArray[i].setDConst(tan(unionArray[i].getDConst())); + break; + case EOpAsin: + newConstArray[i].setDConst(asin(unionArray[i].getDConst())); + break; + case EOpAcos: + newConstArray[i].setDConst(acos(unionArray[i].getDConst())); + break; + case EOpAtan: + newConstArray[i].setDConst(atan(unionArray[i].getDConst())); + break; + + case EOpDPdx: + case EOpDPdy: + case EOpFwidth: + case EOpDPdxFine: + case EOpDPdyFine: + case EOpFwidthFine: + case EOpDPdxCoarse: + case EOpDPdyCoarse: + case EOpFwidthCoarse: + // The derivatives are all mandated to create a constant 0. + newConstArray[i].setDConst(0.0); + break; + + case EOpExp: + newConstArray[i].setDConst(exp(unionArray[i].getDConst())); + break; + case EOpLog: + newConstArray[i].setDConst(log(unionArray[i].getDConst())); + break; + case EOpExp2: + { + const double inv_log2_e = 0.69314718055994530941723212145818; + newConstArray[i].setDConst(exp(unionArray[i].getDConst() * inv_log2_e)); + break; + } + case EOpLog2: + { + const double log2_e = 1.4426950408889634073599246810019; + newConstArray[i].setDConst(log2_e * log(unionArray[i].getDConst())); + break; + } + case EOpSqrt: + newConstArray[i].setDConst(sqrt(unionArray[i].getDConst())); + break; + case EOpInverseSqrt: + newConstArray[i].setDConst(1.0 / sqrt(unionArray[i].getDConst())); + break; + + case EOpAbs: + if (unionArray[i].getType() == EbtDouble) + newConstArray[i].setDConst(fabs(unionArray[i].getDConst())); + else if (unionArray[i].getType() == EbtInt) + newConstArray[i].setIConst(abs(unionArray[i].getIConst())); + else + newConstArray[i] = unionArray[i]; + break; + case EOpSign: + #define SIGN(X) (X == 0 ? 0 : (X < 0 ? -1 : 1)) + if (unionArray[i].getType() == EbtDouble) + newConstArray[i].setDConst(SIGN(unionArray[i].getDConst())); + else + newConstArray[i].setIConst(SIGN(unionArray[i].getIConst())); + break; + case EOpFloor: + newConstArray[i].setDConst(floor(unionArray[i].getDConst())); + break; + case EOpTrunc: + if (unionArray[i].getDConst() > 0) + newConstArray[i].setDConst(floor(unionArray[i].getDConst())); + else + newConstArray[i].setDConst(ceil(unionArray[i].getDConst())); + break; + case EOpRound: + newConstArray[i].setDConst(floor(0.5 + unionArray[i].getDConst())); + break; + case EOpRoundEven: + { + double flr = floor(unionArray[i].getDConst()); + bool even = flr / 2.0 == floor(flr / 2.0); + double rounded = even ? ceil(unionArray[i].getDConst() - 0.5) : floor(unionArray[i].getDConst() + 0.5); + newConstArray[i].setDConst(rounded); + break; + } + case EOpCeil: + newConstArray[i].setDConst(ceil(unionArray[i].getDConst())); + break; + case EOpFract: + { + double x = unionArray[i].getDConst(); + newConstArray[i].setDConst(x - floor(x)); + break; + } + + case EOpIsNan: + { + newConstArray[i].setBConst(isNan(unionArray[i].getDConst())); + break; + } + case EOpIsInf: + { + newConstArray[i].setBConst(isInf(unionArray[i].getDConst())); + break; + } + + case EOpConvIntToBool: + newConstArray[i].setBConst(unionArray[i].getIConst() != 0); break; + case EOpConvUintToBool: + newConstArray[i].setBConst(unionArray[i].getUConst() != 0); break; + case EOpConvBoolToInt: + newConstArray[i].setIConst(unionArray[i].getBConst()); break; + case EOpConvBoolToUint: + newConstArray[i].setUConst(unionArray[i].getBConst()); break; + case EOpConvIntToUint: + newConstArray[i].setUConst(unionArray[i].getIConst()); break; + case EOpConvUintToInt: + newConstArray[i].setIConst(unionArray[i].getUConst()); break; + + case EOpConvFloatToBool: + case EOpConvDoubleToBool: + newConstArray[i].setBConst(unionArray[i].getDConst() != 0); break; + + case EOpConvBoolToFloat: + case EOpConvBoolToDouble: + newConstArray[i].setDConst(unionArray[i].getBConst()); break; + + case EOpConvIntToFloat: + case EOpConvIntToDouble: + newConstArray[i].setDConst(unionArray[i].getIConst()); break; + + case EOpConvUintToFloat: + case EOpConvUintToDouble: + newConstArray[i].setDConst(unionArray[i].getUConst()); break; + + case EOpConvDoubleToFloat: + case EOpConvFloatToDouble: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + + case EOpConvFloatToUint: + case EOpConvDoubleToUint: + newConstArray[i].setUConst(static_cast(unionArray[i].getDConst())); break; + + case EOpConvFloatToInt: + case EOpConvDoubleToInt: + newConstArray[i].setIConst(static_cast(unionArray[i].getDConst())); break; + +#ifndef GLSLANG_WEB + case EOpConvInt8ToBool: + newConstArray[i].setBConst(unionArray[i].getI8Const() != 0); break; + case EOpConvUint8ToBool: + newConstArray[i].setBConst(unionArray[i].getU8Const() != 0); break; + case EOpConvInt16ToBool: + newConstArray[i].setBConst(unionArray[i].getI16Const() != 0); break; + case EOpConvUint16ToBool: + newConstArray[i].setBConst(unionArray[i].getU16Const() != 0); break; + case EOpConvInt64ToBool: + newConstArray[i].setBConst(unionArray[i].getI64Const() != 0); break; + case EOpConvUint64ToBool: + newConstArray[i].setBConst(unionArray[i].getI64Const() != 0); break; + case EOpConvFloat16ToBool: + newConstArray[i].setBConst(unionArray[i].getDConst() != 0); break; + + case EOpConvBoolToInt8: + newConstArray[i].setI8Const(unionArray[i].getBConst()); break; + case EOpConvBoolToUint8: + newConstArray[i].setU8Const(unionArray[i].getBConst()); break; + case EOpConvBoolToInt16: + newConstArray[i].setI16Const(unionArray[i].getBConst()); break; + case EOpConvBoolToUint16: + newConstArray[i].setU16Const(unionArray[i].getBConst()); break; + case EOpConvBoolToInt64: + newConstArray[i].setI64Const(unionArray[i].getBConst()); break; + case EOpConvBoolToUint64: + newConstArray[i].setU64Const(unionArray[i].getBConst()); break; + case EOpConvBoolToFloat16: + newConstArray[i].setDConst(unionArray[i].getBConst()); break; + + case EOpConvInt8ToInt16: + newConstArray[i].setI16Const(unionArray[i].getI8Const()); break; + case EOpConvInt8ToInt: + newConstArray[i].setIConst(unionArray[i].getI8Const()); break; + case EOpConvInt8ToInt64: + newConstArray[i].setI64Const(unionArray[i].getI8Const()); break; + case EOpConvInt8ToUint8: + newConstArray[i].setU8Const(unionArray[i].getI8Const()); break; + case EOpConvInt8ToUint16: + newConstArray[i].setU16Const(unionArray[i].getI8Const()); break; + case EOpConvInt8ToUint: + newConstArray[i].setUConst(unionArray[i].getI8Const()); break; + case EOpConvInt8ToUint64: + newConstArray[i].setU64Const(unionArray[i].getI8Const()); break; + case EOpConvUint8ToInt8: + newConstArray[i].setI8Const(unionArray[i].getU8Const()); break; + case EOpConvUint8ToInt16: + newConstArray[i].setI16Const(unionArray[i].getU8Const()); break; + case EOpConvUint8ToInt: + newConstArray[i].setIConst(unionArray[i].getU8Const()); break; + case EOpConvUint8ToInt64: + newConstArray[i].setI64Const(unionArray[i].getU8Const()); break; + case EOpConvUint8ToUint16: + newConstArray[i].setU16Const(unionArray[i].getU8Const()); break; + case EOpConvUint8ToUint: + newConstArray[i].setUConst(unionArray[i].getU8Const()); break; + case EOpConvUint8ToUint64: + newConstArray[i].setU64Const(unionArray[i].getU8Const()); break; + case EOpConvInt8ToFloat16: + newConstArray[i].setDConst(unionArray[i].getI8Const()); break; + case EOpConvInt8ToFloat: + newConstArray[i].setDConst(unionArray[i].getI8Const()); break; + case EOpConvInt8ToDouble: + newConstArray[i].setDConst(unionArray[i].getI8Const()); break; + case EOpConvUint8ToFloat16: + newConstArray[i].setDConst(unionArray[i].getU8Const()); break; + case EOpConvUint8ToFloat: + newConstArray[i].setDConst(unionArray[i].getU8Const()); break; + case EOpConvUint8ToDouble: + newConstArray[i].setDConst(unionArray[i].getU8Const()); break; + + case EOpConvInt16ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getI16Const())); break; + case EOpConvInt16ToInt: + newConstArray[i].setIConst(unionArray[i].getI16Const()); break; + case EOpConvInt16ToInt64: + newConstArray[i].setI64Const(unionArray[i].getI16Const()); break; + case EOpConvInt16ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getI16Const())); break; + case EOpConvInt16ToUint16: + newConstArray[i].setU16Const(unionArray[i].getI16Const()); break; + case EOpConvInt16ToUint: + newConstArray[i].setUConst(unionArray[i].getI16Const()); break; + case EOpConvInt16ToUint64: + newConstArray[i].setU64Const(unionArray[i].getI16Const()); break; + case EOpConvUint16ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getU16Const())); break; + case EOpConvUint16ToInt16: + newConstArray[i].setI16Const(unionArray[i].getU16Const()); break; + case EOpConvUint16ToInt: + newConstArray[i].setIConst(unionArray[i].getU16Const()); break; + case EOpConvUint16ToInt64: + newConstArray[i].setI64Const(unionArray[i].getU16Const()); break; + case EOpConvUint16ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getU16Const())); break; + + case EOpConvUint16ToUint: + newConstArray[i].setUConst(unionArray[i].getU16Const()); break; + case EOpConvUint16ToUint64: + newConstArray[i].setU64Const(unionArray[i].getU16Const()); break; + case EOpConvInt16ToFloat16: + newConstArray[i].setDConst(unionArray[i].getI16Const()); break; + case EOpConvInt16ToFloat: + newConstArray[i].setDConst(unionArray[i].getI16Const()); break; + case EOpConvInt16ToDouble: + newConstArray[i].setDConst(unionArray[i].getI16Const()); break; + case EOpConvUint16ToFloat16: + newConstArray[i].setDConst(unionArray[i].getU16Const()); break; + case EOpConvUint16ToFloat: + newConstArray[i].setDConst(unionArray[i].getU16Const()); break; + case EOpConvUint16ToDouble: + newConstArray[i].setDConst(unionArray[i].getU16Const()); break; + + case EOpConvIntToInt8: + newConstArray[i].setI8Const((signed char)unionArray[i].getIConst()); break; + case EOpConvIntToInt16: + newConstArray[i].setI16Const((signed short)unionArray[i].getIConst()); break; + case EOpConvIntToInt64: + newConstArray[i].setI64Const(unionArray[i].getIConst()); break; + case EOpConvIntToUint8: + newConstArray[i].setU8Const((unsigned char)unionArray[i].getIConst()); break; + case EOpConvIntToUint16: + newConstArray[i].setU16Const((unsigned char)unionArray[i].getIConst()); break; + case EOpConvIntToUint64: + newConstArray[i].setU64Const(unionArray[i].getIConst()); break; + + case EOpConvUintToInt8: + newConstArray[i].setI8Const((signed char)unionArray[i].getUConst()); break; + case EOpConvUintToInt16: + newConstArray[i].setI16Const((signed short)unionArray[i].getUConst()); break; + case EOpConvUintToInt64: + newConstArray[i].setI64Const(unionArray[i].getUConst()); break; + case EOpConvUintToUint8: + newConstArray[i].setU8Const((unsigned char)unionArray[i].getUConst()); break; + case EOpConvUintToUint16: + newConstArray[i].setU16Const((unsigned short)unionArray[i].getUConst()); break; + case EOpConvUintToUint64: + newConstArray[i].setU64Const(unionArray[i].getUConst()); break; + case EOpConvIntToFloat16: + newConstArray[i].setDConst(unionArray[i].getIConst()); break; + case EOpConvUintToFloat16: + newConstArray[i].setDConst(unionArray[i].getUConst()); break; + case EOpConvInt64ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToInt: + newConstArray[i].setIConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToUint: + newConstArray[i].setUConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToUint64: + newConstArray[i].setU64Const(unionArray[i].getI64Const()); break; + case EOpConvUint64ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToInt: + newConstArray[i].setIConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToInt64: + newConstArray[i].setI64Const(unionArray[i].getU64Const()); break; + case EOpConvUint64ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToUint: + newConstArray[i].setUConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvInt64ToFloat16: + newConstArray[i].setDConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToFloat: + newConstArray[i].setDConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvInt64ToDouble: + newConstArray[i].setDConst(static_cast(unionArray[i].getI64Const())); break; + case EOpConvUint64ToFloat16: + newConstArray[i].setDConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToFloat: + newConstArray[i].setDConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvUint64ToDouble: + newConstArray[i].setDConst(static_cast(unionArray[i].getU64Const())); break; + case EOpConvFloat16ToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToInt: + newConstArray[i].setIConst(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToInt64: + newConstArray[i].setI64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToUint: + newConstArray[i].setUConst(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToUint64: + newConstArray[i].setU64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloat16ToFloat: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + case EOpConvFloat16ToDouble: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + case EOpConvFloatToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToInt64: + newConstArray[i].setI64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToUint64: + newConstArray[i].setU64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvFloatToFloat16: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + case EOpConvDoubleToInt8: + newConstArray[i].setI8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToInt16: + newConstArray[i].setI16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToInt64: + newConstArray[i].setI64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToUint8: + newConstArray[i].setU8Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToUint16: + newConstArray[i].setU16Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToUint64: + newConstArray[i].setU64Const(static_cast(unionArray[i].getDConst())); break; + case EOpConvDoubleToFloat16: + newConstArray[i].setDConst(unionArray[i].getDConst()); break; + case EOpConvPtrToUint64: + case EOpConvUint64ToPtr: + case EOpConstructReference: + newConstArray[i].setU64Const(unionArray[i].getU64Const()); break; +#endif + + // TODO: 3.0 Functionality: unary constant folding: the rest of the ops have to be fleshed out + + case EOpSinh: + case EOpCosh: + case EOpTanh: + case EOpAsinh: + case EOpAcosh: + case EOpAtanh: + + case EOpFloatBitsToInt: + case EOpFloatBitsToUint: + case EOpIntBitsToFloat: + case EOpUintBitsToFloat: + case EOpDoubleBitsToInt64: + case EOpDoubleBitsToUint64: + case EOpInt64BitsToDouble: + case EOpUint64BitsToDouble: + case EOpFloat16BitsToInt16: + case EOpFloat16BitsToUint16: + case EOpInt16BitsToFloat16: + case EOpUint16BitsToFloat16: + default: + return nullptr; + } + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, returnType); + newNode->getWritableType().getQualifier().storage = EvqConst; + newNode->setLoc(getLoc()); + + return newNode; +} + +// +// Do constant folding for an aggregate node that has all its children +// as constants and an operator that requires constant folding. +// +TIntermTyped* TIntermediate::fold(TIntermAggregate* aggrNode) +{ + if (aggrNode == nullptr) + return aggrNode; + + if (! areAllChildConst(aggrNode)) + return aggrNode; + + if (aggrNode->isConstructor()) + return foldConstructor(aggrNode); + + TIntermSequence& children = aggrNode->getSequence(); + + // First, see if this is an operation to constant fold, kick out if not, + // see what size the result is if so. + + bool componentwise = false; // will also say componentwise if a scalar argument gets repeated to make per-component results + int objectSize; + switch (aggrNode->getOp()) { + case EOpAtan: + case EOpPow: + case EOpMin: + case EOpMax: + case EOpMix: + case EOpMod: + case EOpClamp: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + componentwise = true; + objectSize = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + break; + case EOpCross: + case EOpReflect: + case EOpRefract: + case EOpFaceForward: + objectSize = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + break; + case EOpDistance: + case EOpDot: + objectSize = 1; + break; + case EOpOuterProduct: + objectSize = children[0]->getAsTyped()->getType().getVectorSize() * + children[1]->getAsTyped()->getType().getVectorSize(); + break; + case EOpStep: + componentwise = true; + objectSize = std::max(children[0]->getAsTyped()->getType().getVectorSize(), + children[1]->getAsTyped()->getType().getVectorSize()); + break; + case EOpSmoothStep: + componentwise = true; + objectSize = std::max(children[0]->getAsTyped()->getType().getVectorSize(), + children[2]->getAsTyped()->getType().getVectorSize()); + break; + default: + return aggrNode; + } + TConstUnionArray newConstArray(objectSize); + + TVector childConstUnions; + for (unsigned int arg = 0; arg < children.size(); ++arg) + childConstUnions.push_back(children[arg]->getAsConstantUnion()->getConstArray()); + + if (componentwise) { + for (int comp = 0; comp < objectSize; comp++) { + + // some arguments are scalars instead of matching vectors; simulate a smear + int arg0comp = std::min(comp, children[0]->getAsTyped()->getType().getVectorSize() - 1); + int arg1comp = 0; + if (children.size() > 1) + arg1comp = std::min(comp, children[1]->getAsTyped()->getType().getVectorSize() - 1); + int arg2comp = 0; + if (children.size() > 2) + arg2comp = std::min(comp, children[2]->getAsTyped()->getType().getVectorSize() - 1); + + switch (aggrNode->getOp()) { + case EOpAtan: + newConstArray[comp].setDConst(atan2(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EOpPow: + newConstArray[comp].setDConst(pow(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EOpMod: + { + double arg0 = childConstUnions[0][arg0comp].getDConst(); + double arg1 = childConstUnions[1][arg1comp].getDConst(); + double result = arg0 - arg1 * floor(arg0 / arg1); + newConstArray[comp].setDConst(result); + break; + } + case EOpMin: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::min(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::min(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::min(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst())); + break; +#ifndef GLSLANG_WEB + case EbtInt8: + newConstArray[comp].setI8Const(std::min(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::min(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::min(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::min(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::min(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::min(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const())); + break; +#endif + default: assert(false && "Default missing"); + } + break; + case EOpMax: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::max(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::max(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::max(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst())); + break; +#ifndef GLSLANG_WEB + case EbtInt8: + newConstArray[comp].setI8Const(std::max(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::max(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::max(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::max(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::max(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::max(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const())); + break; +#endif + default: assert(false && "Default missing"); + } + break; + case EOpClamp: + switch(children[0]->getAsTyped()->getBasicType()) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + newConstArray[comp].setDConst(std::min(std::max(childConstUnions[0][arg0comp].getDConst(), childConstUnions[1][arg1comp].getDConst()), + childConstUnions[2][arg2comp].getDConst())); + break; + case EbtUint: + newConstArray[comp].setUConst(std::min(std::max(childConstUnions[0][arg0comp].getUConst(), childConstUnions[1][arg1comp].getUConst()), + childConstUnions[2][arg2comp].getUConst())); + break; +#ifndef GLSLANG_WEB + case EbtInt8: + newConstArray[comp].setI8Const(std::min(std::max(childConstUnions[0][arg0comp].getI8Const(), childConstUnions[1][arg1comp].getI8Const()), + childConstUnions[2][arg2comp].getI8Const())); + break; + case EbtUint8: + newConstArray[comp].setU8Const(std::min(std::max(childConstUnions[0][arg0comp].getU8Const(), childConstUnions[1][arg1comp].getU8Const()), + childConstUnions[2][arg2comp].getU8Const())); + break; + case EbtInt16: + newConstArray[comp].setI16Const(std::min(std::max(childConstUnions[0][arg0comp].getI16Const(), childConstUnions[1][arg1comp].getI16Const()), + childConstUnions[2][arg2comp].getI16Const())); + break; + case EbtUint16: + newConstArray[comp].setU16Const(std::min(std::max(childConstUnions[0][arg0comp].getU16Const(), childConstUnions[1][arg1comp].getU16Const()), + childConstUnions[2][arg2comp].getU16Const())); + break; + case EbtInt: + newConstArray[comp].setIConst(std::min(std::max(childConstUnions[0][arg0comp].getIConst(), childConstUnions[1][arg1comp].getIConst()), + childConstUnions[2][arg2comp].getIConst())); + break; + case EbtInt64: + newConstArray[comp].setI64Const(std::min(std::max(childConstUnions[0][arg0comp].getI64Const(), childConstUnions[1][arg1comp].getI64Const()), + childConstUnions[2][arg2comp].getI64Const())); + break; + case EbtUint64: + newConstArray[comp].setU64Const(std::min(std::max(childConstUnions[0][arg0comp].getU64Const(), childConstUnions[1][arg1comp].getU64Const()), + childConstUnions[2][arg2comp].getU64Const())); + break; +#endif + default: assert(false && "Default missing"); + } + break; + case EOpLessThan: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] < childConstUnions[1][arg1comp]); + break; + case EOpGreaterThan: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] > childConstUnions[1][arg1comp]); + break; + case EOpLessThanEqual: + newConstArray[comp].setBConst(! (childConstUnions[0][arg0comp] > childConstUnions[1][arg1comp])); + break; + case EOpGreaterThanEqual: + newConstArray[comp].setBConst(! (childConstUnions[0][arg0comp] < childConstUnions[1][arg1comp])); + break; + case EOpVectorEqual: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] == childConstUnions[1][arg1comp]); + break; + case EOpVectorNotEqual: + newConstArray[comp].setBConst(childConstUnions[0][arg0comp] != childConstUnions[1][arg1comp]); + break; + case EOpMix: + if (!children[0]->getAsTyped()->isFloatingDomain()) + return aggrNode; + if (children[2]->getAsTyped()->getBasicType() == EbtBool) { + newConstArray[comp].setDConst(childConstUnions[2][arg2comp].getBConst() + ? childConstUnions[1][arg1comp].getDConst() + : childConstUnions[0][arg0comp].getDConst()); + } else { + newConstArray[comp].setDConst( + childConstUnions[0][arg0comp].getDConst() * (1.0 - childConstUnions[2][arg2comp].getDConst()) + + childConstUnions[1][arg1comp].getDConst() * childConstUnions[2][arg2comp].getDConst()); + } + break; + case EOpStep: + newConstArray[comp].setDConst(childConstUnions[1][arg1comp].getDConst() < childConstUnions[0][arg0comp].getDConst() ? 0.0 : 1.0); + break; + case EOpSmoothStep: + { + double t = (childConstUnions[2][arg2comp].getDConst() - childConstUnions[0][arg0comp].getDConst()) / + (childConstUnions[1][arg1comp].getDConst() - childConstUnions[0][arg0comp].getDConst()); + if (t < 0.0) + t = 0.0; + if (t > 1.0) + t = 1.0; + newConstArray[comp].setDConst(t * t * (3.0 - 2.0 * t)); + break; + } + default: + return aggrNode; + } + } + } else { + // Non-componentwise... + + int numComps = children[0]->getAsConstantUnion()->getType().computeNumComponents(); + double dot; + + switch (aggrNode->getOp()) { + case EOpDistance: + { + double sum = 0.0; + for (int comp = 0; comp < numComps; ++comp) { + double diff = childConstUnions[1][comp].getDConst() - childConstUnions[0][comp].getDConst(); + sum += diff * diff; + } + newConstArray[0].setDConst(sqrt(sum)); + break; + } + case EOpDot: + newConstArray[0].setDConst(childConstUnions[0].dot(childConstUnions[1])); + break; + case EOpCross: + newConstArray[0] = childConstUnions[0][1] * childConstUnions[1][2] - childConstUnions[0][2] * childConstUnions[1][1]; + newConstArray[1] = childConstUnions[0][2] * childConstUnions[1][0] - childConstUnions[0][0] * childConstUnions[1][2]; + newConstArray[2] = childConstUnions[0][0] * childConstUnions[1][1] - childConstUnions[0][1] * childConstUnions[1][0]; + break; + case EOpFaceForward: + // If dot(Nref, I) < 0 return N, otherwise return -N: Arguments are (N, I, Nref). + dot = childConstUnions[1].dot(childConstUnions[2]); + for (int comp = 0; comp < numComps; ++comp) { + if (dot < 0.0) + newConstArray[comp] = childConstUnions[0][comp]; + else + newConstArray[comp].setDConst(-childConstUnions[0][comp].getDConst()); + } + break; + case EOpReflect: + // I - 2 * dot(N, I) * N: Arguments are (I, N). + dot = childConstUnions[0].dot(childConstUnions[1]); + dot *= 2.0; + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(childConstUnions[0][comp].getDConst() - dot * childConstUnions[1][comp].getDConst()); + break; + case EOpRefract: + { + // Arguments are (I, N, eta). + // k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I)) + // if (k < 0.0) + // return dvec(0.0) + // else + // return eta * I - (eta * dot(N, I) + sqrt(k)) * N + dot = childConstUnions[0].dot(childConstUnions[1]); + double eta = childConstUnions[2][0].getDConst(); + double k = 1.0 - eta * eta * (1.0 - dot * dot); + if (k < 0.0) { + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(0.0); + } else { + for (int comp = 0; comp < numComps; ++comp) + newConstArray[comp].setDConst(eta * childConstUnions[0][comp].getDConst() - (eta * dot + sqrt(k)) * childConstUnions[1][comp].getDConst()); + } + break; + } + case EOpOuterProduct: + { + int numRows = numComps; + int numCols = children[1]->getAsConstantUnion()->getType().computeNumComponents(); + for (int row = 0; row < numRows; ++row) + for (int col = 0; col < numCols; ++col) + newConstArray[col * numRows + row] = childConstUnions[0][row] * childConstUnions[1][col]; + break; + } + default: + return aggrNode; + } + } + + TIntermConstantUnion *newNode = new TIntermConstantUnion(newConstArray, aggrNode->getType()); + newNode->getWritableType().getQualifier().storage = EvqConst; + newNode->setLoc(aggrNode->getLoc()); + + return newNode; +} + +bool TIntermediate::areAllChildConst(TIntermAggregate* aggrNode) +{ + bool allConstant = true; + + // check if all the child nodes are constants so that they can be inserted into + // the parent node + if (aggrNode) { + TIntermSequence& childSequenceVector = aggrNode->getSequence(); + for (TIntermSequence::iterator p = childSequenceVector.begin(); + p != childSequenceVector.end(); p++) { + if (!(*p)->getAsTyped()->getAsConstantUnion()) + return false; + } + } + + return allConstant; +} + +TIntermTyped* TIntermediate::foldConstructor(TIntermAggregate* aggrNode) +{ + bool error = false; + + TConstUnionArray unionArray(aggrNode->getType().computeNumComponents()); + if (aggrNode->getSequence().size() == 1) + error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType(), true); + else + error = parseConstTree(aggrNode, unionArray, aggrNode->getOp(), aggrNode->getType()); + + if (error) + return aggrNode; + + return addConstantUnion(unionArray, aggrNode->getType(), aggrNode->getLoc()); +} + +// +// Constant folding of a bracket (array-style) dereference or struct-like dot +// dereference. Can handle anything except a multi-character swizzle, though +// all swizzles may go to foldSwizzle(). +// +TIntermTyped* TIntermediate::foldDereference(TIntermTyped* node, int index, const TSourceLoc& loc) +{ + TType dereferencedType(node->getType(), index); + dereferencedType.getQualifier().storage = EvqConst; + TIntermTyped* result = 0; + int size = dereferencedType.computeNumComponents(); + + // arrays, vectors, matrices, all use simple multiplicative math + // while structures need to add up heterogeneous members + int start; + if (node->getType().isCoopMat()) + start = 0; + else if (node->isArray() || ! node->isStruct()) + start = size * index; + else { + // it is a structure + assert(node->isStruct()); + start = 0; + for (int i = 0; i < index; ++i) + start += (*node->getType().getStruct())[i].type->computeNumComponents(); + } + + result = addConstantUnion(TConstUnionArray(node->getAsConstantUnion()->getConstArray(), start, size), node->getType(), loc); + + if (result == 0) + result = node; + else + result->setType(dereferencedType); + + return result; +} + +// +// Make a constant vector node or constant scalar node, representing a given +// constant vector and constant swizzle into it. +// +TIntermTyped* TIntermediate::foldSwizzle(TIntermTyped* node, TSwizzleSelectors& selectors, const TSourceLoc& loc) +{ + const TConstUnionArray& unionArray = node->getAsConstantUnion()->getConstArray(); + TConstUnionArray constArray(selectors.size()); + + for (int i = 0; i < selectors.size(); i++) + constArray[i] = unionArray[selectors[i]]; + + TIntermTyped* result = addConstantUnion(constArray, node->getType(), loc); + + if (result == 0) + result = node; + else + result->setType(TType(node->getBasicType(), EvqConst, selectors.size())); + + return result; +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/InfoSink.cpp b/third_party/glslang/glslang/MachineIndependent/InfoSink.cpp new file mode 100644 index 0000000..d00c422 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/InfoSink.cpp @@ -0,0 +1,113 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/InfoSink.h" + +#include + +namespace glslang { + +void TInfoSinkBase::append(const char* s) +{ + if (outputStream & EString) { + if (s == nullptr) + sink.append("(null)"); + else { + checkMem(strlen(s)); + sink.append(s); + } + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(s); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", s); +} + +void TInfoSinkBase::append(int count, char c) +{ + if (outputStream & EString) { + checkMem(count); + sink.append(count, c); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) { +// char str[2]; +// str[0] = c; +// str[1] = '\0'; +// OutputDebugString(str); +// } +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%c", c); +} + +void TInfoSinkBase::append(const TPersistString& t) +{ + if (outputStream & EString) { + checkMem(t.size()); + sink.append(t); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(t.c_str()); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", t.c_str()); +} + +void TInfoSinkBase::append(const TString& t) +{ + if (outputStream & EString) { + checkMem(t.size()); + sink.append(t.c_str()); + } + +//#ifdef _WIN32 +// if (outputStream & EDebugger) +// OutputDebugString(t.c_str()); +//#endif + + if (outputStream & EStdOut) + fprintf(stdout, "%s", t.c_str()); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/Initialize.cpp b/third_party/glslang/glslang/MachineIndependent/Initialize.cpp new file mode 100644 index 0000000..c765199 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/Initialize.cpp @@ -0,0 +1,9425 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Create strings that declare built-in definitions, add built-ins programmatically +// that cannot be expressed in the strings, and establish mappings between +// built-in functions and operators. +// +// Where to put a built-in: +// TBuiltIns::initialize(version,profile) context-independent textual built-ins; add them to the right string +// TBuiltIns::initialize(resources,...) context-dependent textual built-ins; add them to the right string +// TBuiltIns::identifyBuiltIns(...,symbolTable) context-independent programmatic additions/mappings to the symbol table, +// including identifying what extensions are needed if a version does not allow a symbol +// TBuiltIns::identifyBuiltIns(...,symbolTable, resources) context-dependent programmatic additions/mappings to the symbol table, +// including identifying what extensions are needed if a version does not allow a symbol +// + +#include "../Include/intermediate.h" +#include "Initialize.h" + +namespace glslang { + +// TODO: ARB_Compatability: do full extension support +const bool ARBCompatibility = true; + +const bool ForwardCompatibility = false; + +// change this back to false if depending on textual spellings of texturing calls when consuming the AST +// Using PureOperatorBuiltins=false is deprecated. +bool PureOperatorBuiltins = true; + +namespace { + +// +// A set of definitions for tabling of the built-in functions. +// + +// Order matters here, as does correlation with the subsequent +// "const int ..." declarations and the ArgType enumerants. +const char* TypeString[] = { + "bool", "bvec2", "bvec3", "bvec4", + "float", "vec2", "vec3", "vec4", + "int", "ivec2", "ivec3", "ivec4", + "uint", "uvec2", "uvec3", "uvec4", +}; +const int TypeStringCount = sizeof(TypeString) / sizeof(char*); // number of entries in 'TypeString' +const int TypeStringRowShift = 2; // shift amount to go downe one row in 'TypeString' +const int TypeStringColumnMask = (1 << TypeStringRowShift) - 1; // reduce type to its column number in 'TypeString' +const int TypeStringScalarMask = ~TypeStringColumnMask; // take type to its scalar column in 'TypeString' + +enum ArgType { + // numbers hardcoded to correspond to 'TypeString'; order and value matter + TypeB = 1 << 0, // Boolean + TypeF = 1 << 1, // float 32 + TypeI = 1 << 2, // int 32 + TypeU = 1 << 3, // uint 32 + TypeF16 = 1 << 4, // float 16 + TypeF64 = 1 << 5, // float 64 + TypeI8 = 1 << 6, // int 8 + TypeI16 = 1 << 7, // int 16 + TypeI64 = 1 << 8, // int 64 + TypeU8 = 1 << 9, // uint 8 + TypeU16 = 1 << 10, // uint 16 + TypeU64 = 1 << 11, // uint 64 +}; +// Mixtures of the above, to help the function tables +const ArgType TypeFI = static_cast(TypeF | TypeI); +const ArgType TypeFIB = static_cast(TypeF | TypeI | TypeB); +const ArgType TypeIU = static_cast(TypeI | TypeU); + +// The relationships between arguments and return type, whether anything is +// output, or other unusual situations. +enum ArgClass { + ClassRegular = 0, // nothing special, just all vector widths with matching return type; traditional arithmetic + ClassLS = 1 << 0, // the last argument is also held fixed as a (type-matched) scalar while the others cycle + ClassXLS = 1 << 1, // the last argument is exclusively a (type-matched) scalar while the others cycle + ClassLS2 = 1 << 2, // the last two arguments are held fixed as a (type-matched) scalar while the others cycle + ClassFS = 1 << 3, // the first argument is held fixed as a (type-matched) scalar while the others cycle + ClassFS2 = 1 << 4, // the first two arguments are held fixed as a (type-matched) scalar while the others cycle + ClassLO = 1 << 5, // the last argument is an output + ClassB = 1 << 6, // return type cycles through only bool/bvec, matching vector width of args + ClassLB = 1 << 7, // last argument cycles through only bool/bvec, matching vector width of args + ClassV1 = 1 << 8, // scalar only + ClassFIO = 1 << 9, // first argument is inout + ClassRS = 1 << 10, // the return is held scalar as the arguments cycle + ClassNS = 1 << 11, // no scalar prototype + ClassCV = 1 << 12, // first argument is 'coherent volatile' + ClassFO = 1 << 13, // first argument is output + ClassV3 = 1 << 14, // vec3 only +}; +// Mixtures of the above, to help the function tables +const ArgClass ClassV1FIOCV = (ArgClass)(ClassV1 | ClassFIO | ClassCV); +const ArgClass ClassBNS = (ArgClass)(ClassB | ClassNS); +const ArgClass ClassRSNS = (ArgClass)(ClassRS | ClassNS); + +// A descriptor, for a single profile, of when something is available. +// If the current profile does not match 'profile' mask below, the other fields +// do not apply (nor validate). +// profiles == EBadProfile is the end of an array of these +struct Versioning { + EProfile profiles; // the profile(s) (mask) that the following fields are valid for + int minExtendedVersion; // earliest version when extensions are enabled; ignored if numExtensions is 0 + int minCoreVersion; // earliest version function is in core; 0 means never + int numExtensions; // how many extensions are in the 'extensions' list + const char** extensions; // list of extension names enabling the function +}; + +EProfile EDesktopProfile = static_cast(ENoProfile | ECoreProfile | ECompatibilityProfile); + +// Declare pointers to put into the table for versioning. +#ifdef GLSLANG_WEB + const Versioning* Es300Desktop130 = nullptr; + const Versioning* Es310Desktop420 = nullptr; +#elif defined(GLSLANG_ANGLE) + const Versioning* Es300Desktop130 = nullptr; + const Versioning* Es310Desktop420 = nullptr; + const Versioning* Es310Desktop450 = nullptr; +#else + const Versioning Es300Desktop130Version[] = { { EEsProfile, 0, 300, 0, nullptr }, + { EDesktopProfile, 0, 130, 0, nullptr }, + { EBadProfile } }; + const Versioning* Es300Desktop130 = &Es300Desktop130Version[0]; + + const Versioning Es310Desktop420Version[] = { { EEsProfile, 0, 310, 0, nullptr }, + { EDesktopProfile, 0, 420, 0, nullptr }, + { EBadProfile } }; + const Versioning* Es310Desktop420 = &Es310Desktop420Version[0]; + + const Versioning Es310Desktop450Version[] = { { EEsProfile, 0, 310, 0, nullptr }, + { EDesktopProfile, 0, 450, 0, nullptr }, + { EBadProfile } }; + const Versioning* Es310Desktop450 = &Es310Desktop450Version[0]; +#endif + +// The main descriptor of what a set of function prototypes can look like, and +// a pointer to extra versioning information, when needed. +struct BuiltInFunction { + TOperator op; // operator to map the name to + const char* name; // function name + int numArguments; // number of arguments (overloads with varying arguments need different entries) + ArgType types; // ArgType mask + ArgClass classes; // the ways this particular function entry manifests + const Versioning* versioning; // nullptr means always a valid version +}; + +// The tables can have the same built-in function name more than one time, +// but the exact same prototype must be indicated at most once. +// The prototypes that get declared are the union of all those indicated. +// This is important when different releases add new prototypes for the same name. +// It also also congnitively simpler tiling of the prototype space. +// In practice, most names can be fully represented with one entry. +// +// Table is terminated by an OpNull TOperator. + +const BuiltInFunction BaseFunctions[] = { +// TOperator, name, arg-count, ArgType, ArgClass, versioning +// --------- ---- --------- ------- -------- ---------- + { EOpRadians, "radians", 1, TypeF, ClassRegular, nullptr }, + { EOpDegrees, "degrees", 1, TypeF, ClassRegular, nullptr }, + { EOpSin, "sin", 1, TypeF, ClassRegular, nullptr }, + { EOpCos, "cos", 1, TypeF, ClassRegular, nullptr }, + { EOpTan, "tan", 1, TypeF, ClassRegular, nullptr }, + { EOpAsin, "asin", 1, TypeF, ClassRegular, nullptr }, + { EOpAcos, "acos", 1, TypeF, ClassRegular, nullptr }, + { EOpAtan, "atan", 2, TypeF, ClassRegular, nullptr }, + { EOpAtan, "atan", 1, TypeF, ClassRegular, nullptr }, + { EOpPow, "pow", 2, TypeF, ClassRegular, nullptr }, + { EOpExp, "exp", 1, TypeF, ClassRegular, nullptr }, + { EOpLog, "log", 1, TypeF, ClassRegular, nullptr }, + { EOpExp2, "exp2", 1, TypeF, ClassRegular, nullptr }, + { EOpLog2, "log2", 1, TypeF, ClassRegular, nullptr }, + { EOpSqrt, "sqrt", 1, TypeF, ClassRegular, nullptr }, + { EOpInverseSqrt, "inversesqrt", 1, TypeF, ClassRegular, nullptr }, + { EOpAbs, "abs", 1, TypeF, ClassRegular, nullptr }, + { EOpSign, "sign", 1, TypeF, ClassRegular, nullptr }, + { EOpFloor, "floor", 1, TypeF, ClassRegular, nullptr }, + { EOpCeil, "ceil", 1, TypeF, ClassRegular, nullptr }, + { EOpFract, "fract", 1, TypeF, ClassRegular, nullptr }, + { EOpMod, "mod", 2, TypeF, ClassLS, nullptr }, + { EOpMin, "min", 2, TypeF, ClassLS, nullptr }, + { EOpMax, "max", 2, TypeF, ClassLS, nullptr }, + { EOpClamp, "clamp", 3, TypeF, ClassLS2, nullptr }, + { EOpMix, "mix", 3, TypeF, ClassLS, nullptr }, + { EOpStep, "step", 2, TypeF, ClassFS, nullptr }, + { EOpSmoothStep, "smoothstep", 3, TypeF, ClassFS2, nullptr }, + { EOpNormalize, "normalize", 1, TypeF, ClassRegular, nullptr }, + { EOpFaceForward, "faceforward", 3, TypeF, ClassRegular, nullptr }, + { EOpReflect, "reflect", 2, TypeF, ClassRegular, nullptr }, + { EOpRefract, "refract", 3, TypeF, ClassXLS, nullptr }, + { EOpLength, "length", 1, TypeF, ClassRS, nullptr }, + { EOpDistance, "distance", 2, TypeF, ClassRS, nullptr }, + { EOpDot, "dot", 2, TypeF, ClassRS, nullptr }, + { EOpCross, "cross", 2, TypeF, ClassV3, nullptr }, + { EOpLessThan, "lessThan", 2, TypeFI, ClassBNS, nullptr }, + { EOpLessThanEqual, "lessThanEqual", 2, TypeFI, ClassBNS, nullptr }, + { EOpGreaterThan, "greaterThan", 2, TypeFI, ClassBNS, nullptr }, + { EOpGreaterThanEqual, "greaterThanEqual", 2, TypeFI, ClassBNS, nullptr }, + { EOpVectorEqual, "equal", 2, TypeFIB, ClassBNS, nullptr }, + { EOpVectorNotEqual, "notEqual", 2, TypeFIB, ClassBNS, nullptr }, + { EOpAny, "any", 1, TypeB, ClassRSNS, nullptr }, + { EOpAll, "all", 1, TypeB, ClassRSNS, nullptr }, + { EOpVectorLogicalNot, "not", 1, TypeB, ClassNS, nullptr }, + { EOpSinh, "sinh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpCosh, "cosh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpTanh, "tanh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpAsinh, "asinh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpAcosh, "acosh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpAtanh, "atanh", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpAbs, "abs", 1, TypeI, ClassRegular, Es300Desktop130 }, + { EOpSign, "sign", 1, TypeI, ClassRegular, Es300Desktop130 }, + { EOpTrunc, "trunc", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpRound, "round", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpRoundEven, "roundEven", 1, TypeF, ClassRegular, Es300Desktop130 }, + { EOpModf, "modf", 2, TypeF, ClassLO, Es300Desktop130 }, + { EOpMin, "min", 2, TypeIU, ClassLS, Es300Desktop130 }, + { EOpMax, "max", 2, TypeIU, ClassLS, Es300Desktop130 }, + { EOpClamp, "clamp", 3, TypeIU, ClassLS2, Es300Desktop130 }, + { EOpMix, "mix", 3, TypeF, ClassLB, Es300Desktop130 }, + { EOpIsInf, "isinf", 1, TypeF, ClassB, Es300Desktop130 }, + { EOpIsNan, "isnan", 1, TypeF, ClassB, Es300Desktop130 }, + { EOpLessThan, "lessThan", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpLessThanEqual, "lessThanEqual", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpGreaterThan, "greaterThan", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpGreaterThanEqual, "greaterThanEqual", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpVectorEqual, "equal", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpVectorNotEqual, "notEqual", 2, TypeU, ClassBNS, Es300Desktop130 }, + { EOpAtomicAdd, "atomicAdd", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicMin, "atomicMin", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicMax, "atomicMax", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicAnd, "atomicAnd", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicOr, "atomicOr", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicXor, "atomicXor", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicExchange, "atomicExchange", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 }, + { EOpAtomicCompSwap, "atomicCompSwap", 3, TypeIU, ClassV1FIOCV, Es310Desktop420 }, +#ifndef GLSLANG_WEB + { EOpMix, "mix", 3, TypeB, ClassRegular, Es310Desktop450 }, + { EOpMix, "mix", 3, TypeIU, ClassLB, Es310Desktop450 }, +#endif + + { EOpNull } +}; + +const BuiltInFunction DerivativeFunctions[] = { + { EOpDPdx, "dFdx", 1, TypeF, ClassRegular, nullptr }, + { EOpDPdy, "dFdy", 1, TypeF, ClassRegular, nullptr }, + { EOpFwidth, "fwidth", 1, TypeF, ClassRegular, nullptr }, + { EOpNull } +}; + +// For functions declared some other way, but still use the table to relate to operator. +struct CustomFunction { + TOperator op; // operator to map the name to + const char* name; // function name + const Versioning* versioning; // nullptr means always a valid version +}; + +const CustomFunction CustomFunctions[] = { + { EOpBarrier, "barrier", nullptr }, + { EOpMemoryBarrierShared, "memoryBarrierShared", nullptr }, + { EOpGroupMemoryBarrier, "groupMemoryBarrier", nullptr }, + { EOpMemoryBarrier, "memoryBarrier", nullptr }, + { EOpMemoryBarrierBuffer, "memoryBarrierBuffer", nullptr }, + + { EOpPackSnorm2x16, "packSnorm2x16", nullptr }, + { EOpUnpackSnorm2x16, "unpackSnorm2x16", nullptr }, + { EOpPackUnorm2x16, "packUnorm2x16", nullptr }, + { EOpUnpackUnorm2x16, "unpackUnorm2x16", nullptr }, + { EOpPackHalf2x16, "packHalf2x16", nullptr }, + { EOpUnpackHalf2x16, "unpackHalf2x16", nullptr }, + + { EOpMul, "matrixCompMult", nullptr }, + { EOpOuterProduct, "outerProduct", nullptr }, + { EOpTranspose, "transpose", nullptr }, + { EOpDeterminant, "determinant", nullptr }, + { EOpMatrixInverse, "inverse", nullptr }, + { EOpFloatBitsToInt, "floatBitsToInt", nullptr }, + { EOpFloatBitsToUint, "floatBitsToUint", nullptr }, + { EOpIntBitsToFloat, "intBitsToFloat", nullptr }, + { EOpUintBitsToFloat, "uintBitsToFloat", nullptr }, + + { EOpTextureQuerySize, "textureSize", nullptr }, + { EOpTextureQueryLod, "textureQueryLod", nullptr }, + { EOpTextureQueryLevels, "textureQueryLevels", nullptr }, + { EOpTextureQuerySamples, "textureSamples", nullptr }, + { EOpTexture, "texture", nullptr }, + { EOpTextureProj, "textureProj", nullptr }, + { EOpTextureLod, "textureLod", nullptr }, + { EOpTextureOffset, "textureOffset", nullptr }, + { EOpTextureFetch, "texelFetch", nullptr }, + { EOpTextureFetchOffset, "texelFetchOffset", nullptr }, + { EOpTextureProjOffset, "textureProjOffset", nullptr }, + { EOpTextureLodOffset, "textureLodOffset", nullptr }, + { EOpTextureProjLod, "textureProjLod", nullptr }, + { EOpTextureProjLodOffset, "textureProjLodOffset", nullptr }, + { EOpTextureGrad, "textureGrad", nullptr }, + { EOpTextureGradOffset, "textureGradOffset", nullptr }, + { EOpTextureProjGrad, "textureProjGrad", nullptr }, + { EOpTextureProjGradOffset, "textureProjGradOffset", nullptr }, + + { EOpNull } +}; + +// For the given table of functions, add all the indicated prototypes for each +// one, to be returned in the passed in decls. +void AddTabledBuiltin(TString& decls, const BuiltInFunction& function) +{ + const auto isScalarType = [](int type) { return (type & TypeStringColumnMask) == 0; }; + + // loop across these two: + // 0: the varying arg set, and + // 1: the fixed scalar args + const ArgClass ClassFixed = (ArgClass)(ClassLS | ClassXLS | ClassLS2 | ClassFS | ClassFS2); + for (int fixed = 0; fixed < ((function.classes & ClassFixed) > 0 ? 2 : 1); ++fixed) { + + if (fixed == 0 && (function.classes & ClassXLS)) + continue; + + // walk the type strings in TypeString[] + for (int type = 0; type < TypeStringCount; ++type) { + // skip types not selected: go from type to row number to type bit + if ((function.types & (1 << (type >> TypeStringRowShift))) == 0) + continue; + + // if we aren't on a scalar, and should be, skip + if ((function.classes & ClassV1) && !isScalarType(type)) + continue; + + // if we aren't on a 3-vector, and should be, skip + if ((function.classes & ClassV3) && (type & TypeStringColumnMask) != 2) + continue; + + // skip replication of all arg scalars between the varying arg set and the fixed args + if (fixed == 1 && type == (type & TypeStringScalarMask) && (function.classes & ClassXLS) == 0) + continue; + + // skip scalars when we are told to + if ((function.classes & ClassNS) && isScalarType(type)) + continue; + + // return type + if (function.classes & ClassB) + decls.append(TypeString[type & TypeStringColumnMask]); + else if (function.classes & ClassRS) + decls.append(TypeString[type & TypeStringScalarMask]); + else + decls.append(TypeString[type]); + decls.append(" "); + decls.append(function.name); + decls.append("("); + + // arguments + for (int arg = 0; arg < function.numArguments; ++arg) { + if (arg == function.numArguments - 1 && (function.classes & ClassLO)) + decls.append("out "); + if (arg == 0) { +#ifndef GLSLANG_WEB + if (function.classes & ClassCV) + decls.append("coherent volatile "); +#endif + if (function.classes & ClassFIO) + decls.append("inout "); + if (function.classes & ClassFO) + decls.append("out "); + } + if ((function.classes & ClassLB) && arg == function.numArguments - 1) + decls.append(TypeString[type & TypeStringColumnMask]); + else if (fixed && ((arg == function.numArguments - 1 && (function.classes & (ClassLS | ClassXLS | + ClassLS2))) || + (arg == function.numArguments - 2 && (function.classes & ClassLS2)) || + (arg == 0 && (function.classes & (ClassFS | ClassFS2))) || + (arg == 1 && (function.classes & ClassFS2)))) + decls.append(TypeString[type & TypeStringScalarMask]); + else + decls.append(TypeString[type]); + if (arg < function.numArguments - 1) + decls.append(","); + } + decls.append(");\n"); + } + } +} + +// See if the tabled versioning information allows the current version. +bool ValidVersion(const BuiltInFunction& function, int version, EProfile profile, const SpvVersion& /* spVersion */) +{ +#if defined(GLSLANG_WEB) || defined(GLSLANG_ANGLE) + // all entries in table are valid + return true; +#endif + + // nullptr means always valid + if (function.versioning == nullptr) + return true; + + // check for what is said about our current profile + for (const Versioning* v = function.versioning; v->profiles != EBadProfile; ++v) { + if ((v->profiles & profile) != 0) { + if (v->minCoreVersion <= version || (v->numExtensions > 0 && v->minExtendedVersion <= version)) + return true; + } + } + + return false; +} + +// Relate a single table of built-ins to their AST operator. +// This can get called redundantly (especially for the common built-ins, when +// called once per stage). This is a performance issue only, not a correctness +// concern. It is done for quality arising from simplicity, as there are subtleties +// to get correct if instead trying to do it surgically. +template +void RelateTabledBuiltins(const FunctionT* functions, TSymbolTable& symbolTable) +{ + while (functions->op != EOpNull) { + symbolTable.relateToOperator(functions->name, functions->op); + ++functions; + } +} + +} // end anonymous namespace + +// Add declarations for all tables of built-in functions. +void TBuiltIns::addTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion) +{ + const auto forEachFunction = [&](TString& decls, const BuiltInFunction* function) { + while (function->op != EOpNull) { + if (ValidVersion(*function, version, profile, spvVersion)) + AddTabledBuiltin(decls, *function); + ++function; + } + }; + + forEachFunction(commonBuiltins, BaseFunctions); + forEachFunction(stageBuiltins[EShLangFragment], DerivativeFunctions); + + if ((profile == EEsProfile && version >= 320) || (profile != EEsProfile && version >= 450)) + forEachFunction(stageBuiltins[EShLangCompute], DerivativeFunctions); +} + +// Relate all tables of built-ins to the AST operators. +void TBuiltIns::relateTabledBuiltins(int /* version */, EProfile /* profile */, const SpvVersion& /* spvVersion */, EShLanguage /* stage */, TSymbolTable& symbolTable) +{ + RelateTabledBuiltins(BaseFunctions, symbolTable); + RelateTabledBuiltins(DerivativeFunctions, symbolTable); + RelateTabledBuiltins(CustomFunctions, symbolTable); +} + +inline bool IncludeLegacy(int version, EProfile profile, const SpvVersion& spvVersion) +{ + return profile != EEsProfile && (version <= 130 || (spvVersion.spv == 0 && ARBCompatibility) || profile == ECompatibilityProfile); +} + +// Construct TBuiltInParseables base class. This can be used for language-common constructs. +TBuiltInParseables::TBuiltInParseables() +{ +} + +// Destroy TBuiltInParseables. +TBuiltInParseables::~TBuiltInParseables() +{ +} + +TBuiltIns::TBuiltIns() +{ + // Set up textual representations for making all the permutations + // of texturing/imaging functions. + prefixes[EbtFloat] = ""; + prefixes[EbtInt] = "i"; + prefixes[EbtUint] = "u"; +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + prefixes[EbtFloat16] = "f16"; + prefixes[EbtInt8] = "i8"; + prefixes[EbtUint8] = "u8"; + prefixes[EbtInt16] = "i16"; + prefixes[EbtUint16] = "u16"; + prefixes[EbtInt64] = "i64"; + prefixes[EbtUint64] = "u64"; +#endif + + postfixes[2] = "2"; + postfixes[3] = "3"; + postfixes[4] = "4"; + + // Map from symbolic class of texturing dimension to numeric dimensions. + dimMap[Esd2D] = 2; + dimMap[Esd3D] = 3; + dimMap[EsdCube] = 3; +#ifndef GLSLANG_WEB +#ifndef GLSLANG_ANGLE + dimMap[Esd1D] = 1; +#endif + dimMap[EsdRect] = 2; + dimMap[EsdBuffer] = 1; + dimMap[EsdSubpass] = 2; // potentially unused for now +#endif +} + +TBuiltIns::~TBuiltIns() +{ +} + + +// +// Add all context-independent built-in functions and variables that are present +// for the given version and profile. Share common ones across stages, otherwise +// make stage-specific entries. +// +// Most built-ins variables can be added as simple text strings. Some need to +// be added programmatically, which is done later in IdentifyBuiltIns() below. +// +void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvVersion) +{ +#ifdef GLSLANG_WEB + version = 310; + profile = EEsProfile; +#elif defined(GLSLANG_ANGLE) + version = 450; + profile = ECoreProfile; +#endif + addTabledBuiltins(version, profile, spvVersion); + + //============================================================================ + // + // Prototypes for built-in functions used repeatly by different shaders + // + //============================================================================ + +#ifndef GLSLANG_WEB + // + // Derivatives Functions. + // + TString derivativeControls ( + "float dFdxFine(float p);" + "vec2 dFdxFine(vec2 p);" + "vec3 dFdxFine(vec3 p);" + "vec4 dFdxFine(vec4 p);" + + "float dFdyFine(float p);" + "vec2 dFdyFine(vec2 p);" + "vec3 dFdyFine(vec3 p);" + "vec4 dFdyFine(vec4 p);" + + "float fwidthFine(float p);" + "vec2 fwidthFine(vec2 p);" + "vec3 fwidthFine(vec3 p);" + "vec4 fwidthFine(vec4 p);" + + "float dFdxCoarse(float p);" + "vec2 dFdxCoarse(vec2 p);" + "vec3 dFdxCoarse(vec3 p);" + "vec4 dFdxCoarse(vec4 p);" + + "float dFdyCoarse(float p);" + "vec2 dFdyCoarse(vec2 p);" + "vec3 dFdyCoarse(vec3 p);" + "vec4 dFdyCoarse(vec4 p);" + + "float fwidthCoarse(float p);" + "vec2 fwidthCoarse(vec2 p);" + "vec3 fwidthCoarse(vec3 p);" + "vec4 fwidthCoarse(vec4 p);" + ); + +#ifndef GLSLANG_ANGLE + TString derivativesAndControl16bits ( + "float16_t dFdx(float16_t);" + "f16vec2 dFdx(f16vec2);" + "f16vec3 dFdx(f16vec3);" + "f16vec4 dFdx(f16vec4);" + + "float16_t dFdy(float16_t);" + "f16vec2 dFdy(f16vec2);" + "f16vec3 dFdy(f16vec3);" + "f16vec4 dFdy(f16vec4);" + + "float16_t dFdxFine(float16_t);" + "f16vec2 dFdxFine(f16vec2);" + "f16vec3 dFdxFine(f16vec3);" + "f16vec4 dFdxFine(f16vec4);" + + "float16_t dFdyFine(float16_t);" + "f16vec2 dFdyFine(f16vec2);" + "f16vec3 dFdyFine(f16vec3);" + "f16vec4 dFdyFine(f16vec4);" + + "float16_t dFdxCoarse(float16_t);" + "f16vec2 dFdxCoarse(f16vec2);" + "f16vec3 dFdxCoarse(f16vec3);" + "f16vec4 dFdxCoarse(f16vec4);" + + "float16_t dFdyCoarse(float16_t);" + "f16vec2 dFdyCoarse(f16vec2);" + "f16vec3 dFdyCoarse(f16vec3);" + "f16vec4 dFdyCoarse(f16vec4);" + + "float16_t fwidth(float16_t);" + "f16vec2 fwidth(f16vec2);" + "f16vec3 fwidth(f16vec3);" + "f16vec4 fwidth(f16vec4);" + + "float16_t fwidthFine(float16_t);" + "f16vec2 fwidthFine(f16vec2);" + "f16vec3 fwidthFine(f16vec3);" + "f16vec4 fwidthFine(f16vec4);" + + "float16_t fwidthCoarse(float16_t);" + "f16vec2 fwidthCoarse(f16vec2);" + "f16vec3 fwidthCoarse(f16vec3);" + "f16vec4 fwidthCoarse(f16vec4);" + ); + + TString derivativesAndControl64bits ( + "float64_t dFdx(float64_t);" + "f64vec2 dFdx(f64vec2);" + "f64vec3 dFdx(f64vec3);" + "f64vec4 dFdx(f64vec4);" + + "float64_t dFdy(float64_t);" + "f64vec2 dFdy(f64vec2);" + "f64vec3 dFdy(f64vec3);" + "f64vec4 dFdy(f64vec4);" + + "float64_t dFdxFine(float64_t);" + "f64vec2 dFdxFine(f64vec2);" + "f64vec3 dFdxFine(f64vec3);" + "f64vec4 dFdxFine(f64vec4);" + + "float64_t dFdyFine(float64_t);" + "f64vec2 dFdyFine(f64vec2);" + "f64vec3 dFdyFine(f64vec3);" + "f64vec4 dFdyFine(f64vec4);" + + "float64_t dFdxCoarse(float64_t);" + "f64vec2 dFdxCoarse(f64vec2);" + "f64vec3 dFdxCoarse(f64vec3);" + "f64vec4 dFdxCoarse(f64vec4);" + + "float64_t dFdyCoarse(float64_t);" + "f64vec2 dFdyCoarse(f64vec2);" + "f64vec3 dFdyCoarse(f64vec3);" + "f64vec4 dFdyCoarse(f64vec4);" + + "float64_t fwidth(float64_t);" + "f64vec2 fwidth(f64vec2);" + "f64vec3 fwidth(f64vec3);" + "f64vec4 fwidth(f64vec4);" + + "float64_t fwidthFine(float64_t);" + "f64vec2 fwidthFine(f64vec2);" + "f64vec3 fwidthFine(f64vec3);" + "f64vec4 fwidthFine(f64vec4);" + + "float64_t fwidthCoarse(float64_t);" + "f64vec2 fwidthCoarse(f64vec2);" + "f64vec3 fwidthCoarse(f64vec3);" + "f64vec4 fwidthCoarse(f64vec4);" + ); + + //============================================================================ + // + // Prototypes for built-in functions seen by both vertex and fragment shaders. + // + //============================================================================ + + // + // double functions added to desktop 4.00, but not fma, frexp, ldexp, or pack/unpack + // + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + + "double sqrt(double);" + "dvec2 sqrt(dvec2);" + "dvec3 sqrt(dvec3);" + "dvec4 sqrt(dvec4);" + + "double inversesqrt(double);" + "dvec2 inversesqrt(dvec2);" + "dvec3 inversesqrt(dvec3);" + "dvec4 inversesqrt(dvec4);" + + "double abs(double);" + "dvec2 abs(dvec2);" + "dvec3 abs(dvec3);" + "dvec4 abs(dvec4);" + + "double sign(double);" + "dvec2 sign(dvec2);" + "dvec3 sign(dvec3);" + "dvec4 sign(dvec4);" + + "double floor(double);" + "dvec2 floor(dvec2);" + "dvec3 floor(dvec3);" + "dvec4 floor(dvec4);" + + "double trunc(double);" + "dvec2 trunc(dvec2);" + "dvec3 trunc(dvec3);" + "dvec4 trunc(dvec4);" + + "double round(double);" + "dvec2 round(dvec2);" + "dvec3 round(dvec3);" + "dvec4 round(dvec4);" + + "double roundEven(double);" + "dvec2 roundEven(dvec2);" + "dvec3 roundEven(dvec3);" + "dvec4 roundEven(dvec4);" + + "double ceil(double);" + "dvec2 ceil(dvec2);" + "dvec3 ceil(dvec3);" + "dvec4 ceil(dvec4);" + + "double fract(double);" + "dvec2 fract(dvec2);" + "dvec3 fract(dvec3);" + "dvec4 fract(dvec4);" + + "double mod(double, double);" + "dvec2 mod(dvec2 , double);" + "dvec3 mod(dvec3 , double);" + "dvec4 mod(dvec4 , double);" + "dvec2 mod(dvec2 , dvec2);" + "dvec3 mod(dvec3 , dvec3);" + "dvec4 mod(dvec4 , dvec4);" + + "double modf(double, out double);" + "dvec2 modf(dvec2, out dvec2);" + "dvec3 modf(dvec3, out dvec3);" + "dvec4 modf(dvec4, out dvec4);" + + "double min(double, double);" + "dvec2 min(dvec2, double);" + "dvec3 min(dvec3, double);" + "dvec4 min(dvec4, double);" + "dvec2 min(dvec2, dvec2);" + "dvec3 min(dvec3, dvec3);" + "dvec4 min(dvec4, dvec4);" + + "double max(double, double);" + "dvec2 max(dvec2 , double);" + "dvec3 max(dvec3 , double);" + "dvec4 max(dvec4 , double);" + "dvec2 max(dvec2 , dvec2);" + "dvec3 max(dvec3 , dvec3);" + "dvec4 max(dvec4 , dvec4);" + + "double clamp(double, double, double);" + "dvec2 clamp(dvec2 , double, double);" + "dvec3 clamp(dvec3 , double, double);" + "dvec4 clamp(dvec4 , double, double);" + "dvec2 clamp(dvec2 , dvec2 , dvec2);" + "dvec3 clamp(dvec3 , dvec3 , dvec3);" + "dvec4 clamp(dvec4 , dvec4 , dvec4);" + + "double mix(double, double, double);" + "dvec2 mix(dvec2, dvec2, double);" + "dvec3 mix(dvec3, dvec3, double);" + "dvec4 mix(dvec4, dvec4, double);" + "dvec2 mix(dvec2, dvec2, dvec2);" + "dvec3 mix(dvec3, dvec3, dvec3);" + "dvec4 mix(dvec4, dvec4, dvec4);" + "double mix(double, double, bool);" + "dvec2 mix(dvec2, dvec2, bvec2);" + "dvec3 mix(dvec3, dvec3, bvec3);" + "dvec4 mix(dvec4, dvec4, bvec4);" + + "double step(double, double);" + "dvec2 step(dvec2 , dvec2);" + "dvec3 step(dvec3 , dvec3);" + "dvec4 step(dvec4 , dvec4);" + "dvec2 step(double, dvec2);" + "dvec3 step(double, dvec3);" + "dvec4 step(double, dvec4);" + + "double smoothstep(double, double, double);" + "dvec2 smoothstep(dvec2 , dvec2 , dvec2);" + "dvec3 smoothstep(dvec3 , dvec3 , dvec3);" + "dvec4 smoothstep(dvec4 , dvec4 , dvec4);" + "dvec2 smoothstep(double, double, dvec2);" + "dvec3 smoothstep(double, double, dvec3);" + "dvec4 smoothstep(double, double, dvec4);" + + "bool isnan(double);" + "bvec2 isnan(dvec2);" + "bvec3 isnan(dvec3);" + "bvec4 isnan(dvec4);" + + "bool isinf(double);" + "bvec2 isinf(dvec2);" + "bvec3 isinf(dvec3);" + "bvec4 isinf(dvec4);" + + "double length(double);" + "double length(dvec2);" + "double length(dvec3);" + "double length(dvec4);" + + "double distance(double, double);" + "double distance(dvec2 , dvec2);" + "double distance(dvec3 , dvec3);" + "double distance(dvec4 , dvec4);" + + "double dot(double, double);" + "double dot(dvec2 , dvec2);" + "double dot(dvec3 , dvec3);" + "double dot(dvec4 , dvec4);" + + "dvec3 cross(dvec3, dvec3);" + + "double normalize(double);" + "dvec2 normalize(dvec2);" + "dvec3 normalize(dvec3);" + "dvec4 normalize(dvec4);" + + "double faceforward(double, double, double);" + "dvec2 faceforward(dvec2, dvec2, dvec2);" + "dvec3 faceforward(dvec3, dvec3, dvec3);" + "dvec4 faceforward(dvec4, dvec4, dvec4);" + + "double reflect(double, double);" + "dvec2 reflect(dvec2 , dvec2 );" + "dvec3 reflect(dvec3 , dvec3 );" + "dvec4 reflect(dvec4 , dvec4 );" + + "double refract(double, double, double);" + "dvec2 refract(dvec2 , dvec2 , double);" + "dvec3 refract(dvec3 , dvec3 , double);" + "dvec4 refract(dvec4 , dvec4 , double);" + + "dmat2 matrixCompMult(dmat2, dmat2);" + "dmat3 matrixCompMult(dmat3, dmat3);" + "dmat4 matrixCompMult(dmat4, dmat4);" + "dmat2x3 matrixCompMult(dmat2x3, dmat2x3);" + "dmat2x4 matrixCompMult(dmat2x4, dmat2x4);" + "dmat3x2 matrixCompMult(dmat3x2, dmat3x2);" + "dmat3x4 matrixCompMult(dmat3x4, dmat3x4);" + "dmat4x2 matrixCompMult(dmat4x2, dmat4x2);" + "dmat4x3 matrixCompMult(dmat4x3, dmat4x3);" + + "dmat2 outerProduct(dvec2, dvec2);" + "dmat3 outerProduct(dvec3, dvec3);" + "dmat4 outerProduct(dvec4, dvec4);" + "dmat2x3 outerProduct(dvec3, dvec2);" + "dmat3x2 outerProduct(dvec2, dvec3);" + "dmat2x4 outerProduct(dvec4, dvec2);" + "dmat4x2 outerProduct(dvec2, dvec4);" + "dmat3x4 outerProduct(dvec4, dvec3);" + "dmat4x3 outerProduct(dvec3, dvec4);" + + "dmat2 transpose(dmat2);" + "dmat3 transpose(dmat3);" + "dmat4 transpose(dmat4);" + "dmat2x3 transpose(dmat3x2);" + "dmat3x2 transpose(dmat2x3);" + "dmat2x4 transpose(dmat4x2);" + "dmat4x2 transpose(dmat2x4);" + "dmat3x4 transpose(dmat4x3);" + "dmat4x3 transpose(dmat3x4);" + + "double determinant(dmat2);" + "double determinant(dmat3);" + "double determinant(dmat4);" + + "dmat2 inverse(dmat2);" + "dmat3 inverse(dmat3);" + "dmat4 inverse(dmat4);" + + "bvec2 lessThan(dvec2, dvec2);" + "bvec3 lessThan(dvec3, dvec3);" + "bvec4 lessThan(dvec4, dvec4);" + + "bvec2 lessThanEqual(dvec2, dvec2);" + "bvec3 lessThanEqual(dvec3, dvec3);" + "bvec4 lessThanEqual(dvec4, dvec4);" + + "bvec2 greaterThan(dvec2, dvec2);" + "bvec3 greaterThan(dvec3, dvec3);" + "bvec4 greaterThan(dvec4, dvec4);" + + "bvec2 greaterThanEqual(dvec2, dvec2);" + "bvec3 greaterThanEqual(dvec3, dvec3);" + "bvec4 greaterThanEqual(dvec4, dvec4);" + + "bvec2 equal(dvec2, dvec2);" + "bvec3 equal(dvec3, dvec3);" + "bvec4 equal(dvec4, dvec4);" + + "bvec2 notEqual(dvec2, dvec2);" + "bvec3 notEqual(dvec3, dvec3);" + "bvec4 notEqual(dvec4, dvec4);" + + "\n"); + } + + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + + "int64_t abs(int64_t);" + "i64vec2 abs(i64vec2);" + "i64vec3 abs(i64vec3);" + "i64vec4 abs(i64vec4);" + + "int64_t sign(int64_t);" + "i64vec2 sign(i64vec2);" + "i64vec3 sign(i64vec3);" + "i64vec4 sign(i64vec4);" + + "int64_t min(int64_t, int64_t);" + "i64vec2 min(i64vec2, int64_t);" + "i64vec3 min(i64vec3, int64_t);" + "i64vec4 min(i64vec4, int64_t);" + "i64vec2 min(i64vec2, i64vec2);" + "i64vec3 min(i64vec3, i64vec3);" + "i64vec4 min(i64vec4, i64vec4);" + "uint64_t min(uint64_t, uint64_t);" + "u64vec2 min(u64vec2, uint64_t);" + "u64vec3 min(u64vec3, uint64_t);" + "u64vec4 min(u64vec4, uint64_t);" + "u64vec2 min(u64vec2, u64vec2);" + "u64vec3 min(u64vec3, u64vec3);" + "u64vec4 min(u64vec4, u64vec4);" + + "int64_t max(int64_t, int64_t);" + "i64vec2 max(i64vec2, int64_t);" + "i64vec3 max(i64vec3, int64_t);" + "i64vec4 max(i64vec4, int64_t);" + "i64vec2 max(i64vec2, i64vec2);" + "i64vec3 max(i64vec3, i64vec3);" + "i64vec4 max(i64vec4, i64vec4);" + "uint64_t max(uint64_t, uint64_t);" + "u64vec2 max(u64vec2, uint64_t);" + "u64vec3 max(u64vec3, uint64_t);" + "u64vec4 max(u64vec4, uint64_t);" + "u64vec2 max(u64vec2, u64vec2);" + "u64vec3 max(u64vec3, u64vec3);" + "u64vec4 max(u64vec4, u64vec4);" + + "int64_t clamp(int64_t, int64_t, int64_t);" + "i64vec2 clamp(i64vec2, int64_t, int64_t);" + "i64vec3 clamp(i64vec3, int64_t, int64_t);" + "i64vec4 clamp(i64vec4, int64_t, int64_t);" + "i64vec2 clamp(i64vec2, i64vec2, i64vec2);" + "i64vec3 clamp(i64vec3, i64vec3, i64vec3);" + "i64vec4 clamp(i64vec4, i64vec4, i64vec4);" + "uint64_t clamp(uint64_t, uint64_t, uint64_t);" + "u64vec2 clamp(u64vec2, uint64_t, uint64_t);" + "u64vec3 clamp(u64vec3, uint64_t, uint64_t);" + "u64vec4 clamp(u64vec4, uint64_t, uint64_t);" + "u64vec2 clamp(u64vec2, u64vec2, u64vec2);" + "u64vec3 clamp(u64vec3, u64vec3, u64vec3);" + "u64vec4 clamp(u64vec4, u64vec4, u64vec4);" + + "int64_t mix(int64_t, int64_t, bool);" + "i64vec2 mix(i64vec2, i64vec2, bvec2);" + "i64vec3 mix(i64vec3, i64vec3, bvec3);" + "i64vec4 mix(i64vec4, i64vec4, bvec4);" + "uint64_t mix(uint64_t, uint64_t, bool);" + "u64vec2 mix(u64vec2, u64vec2, bvec2);" + "u64vec3 mix(u64vec3, u64vec3, bvec3);" + "u64vec4 mix(u64vec4, u64vec4, bvec4);" + + "int64_t doubleBitsToInt64(double);" + "i64vec2 doubleBitsToInt64(dvec2);" + "i64vec3 doubleBitsToInt64(dvec3);" + "i64vec4 doubleBitsToInt64(dvec4);" + + "uint64_t doubleBitsToUint64(double);" + "u64vec2 doubleBitsToUint64(dvec2);" + "u64vec3 doubleBitsToUint64(dvec3);" + "u64vec4 doubleBitsToUint64(dvec4);" + + "double int64BitsToDouble(int64_t);" + "dvec2 int64BitsToDouble(i64vec2);" + "dvec3 int64BitsToDouble(i64vec3);" + "dvec4 int64BitsToDouble(i64vec4);" + + "double uint64BitsToDouble(uint64_t);" + "dvec2 uint64BitsToDouble(u64vec2);" + "dvec3 uint64BitsToDouble(u64vec3);" + "dvec4 uint64BitsToDouble(u64vec4);" + + "int64_t packInt2x32(ivec2);" + "uint64_t packUint2x32(uvec2);" + "ivec2 unpackInt2x32(int64_t);" + "uvec2 unpackUint2x32(uint64_t);" + + "bvec2 lessThan(i64vec2, i64vec2);" + "bvec3 lessThan(i64vec3, i64vec3);" + "bvec4 lessThan(i64vec4, i64vec4);" + "bvec2 lessThan(u64vec2, u64vec2);" + "bvec3 lessThan(u64vec3, u64vec3);" + "bvec4 lessThan(u64vec4, u64vec4);" + + "bvec2 lessThanEqual(i64vec2, i64vec2);" + "bvec3 lessThanEqual(i64vec3, i64vec3);" + "bvec4 lessThanEqual(i64vec4, i64vec4);" + "bvec2 lessThanEqual(u64vec2, u64vec2);" + "bvec3 lessThanEqual(u64vec3, u64vec3);" + "bvec4 lessThanEqual(u64vec4, u64vec4);" + + "bvec2 greaterThan(i64vec2, i64vec2);" + "bvec3 greaterThan(i64vec3, i64vec3);" + "bvec4 greaterThan(i64vec4, i64vec4);" + "bvec2 greaterThan(u64vec2, u64vec2);" + "bvec3 greaterThan(u64vec3, u64vec3);" + "bvec4 greaterThan(u64vec4, u64vec4);" + + "bvec2 greaterThanEqual(i64vec2, i64vec2);" + "bvec3 greaterThanEqual(i64vec3, i64vec3);" + "bvec4 greaterThanEqual(i64vec4, i64vec4);" + "bvec2 greaterThanEqual(u64vec2, u64vec2);" + "bvec3 greaterThanEqual(u64vec3, u64vec3);" + "bvec4 greaterThanEqual(u64vec4, u64vec4);" + + "bvec2 equal(i64vec2, i64vec2);" + "bvec3 equal(i64vec3, i64vec3);" + "bvec4 equal(i64vec4, i64vec4);" + "bvec2 equal(u64vec2, u64vec2);" + "bvec3 equal(u64vec3, u64vec3);" + "bvec4 equal(u64vec4, u64vec4);" + + "bvec2 notEqual(i64vec2, i64vec2);" + "bvec3 notEqual(i64vec3, i64vec3);" + "bvec4 notEqual(i64vec4, i64vec4);" + "bvec2 notEqual(u64vec2, u64vec2);" + "bvec3 notEqual(u64vec3, u64vec3);" + "bvec4 notEqual(u64vec4, u64vec4);" + + "int64_t findLSB(int64_t);" + "i64vec2 findLSB(i64vec2);" + "i64vec3 findLSB(i64vec3);" + "i64vec4 findLSB(i64vec4);" + + "int64_t findLSB(uint64_t);" + "i64vec2 findLSB(u64vec2);" + "i64vec3 findLSB(u64vec3);" + "i64vec4 findLSB(u64vec4);" + + "int64_t findMSB(int64_t);" + "i64vec2 findMSB(i64vec2);" + "i64vec3 findMSB(i64vec3);" + "i64vec4 findMSB(i64vec4);" + + "int64_t findMSB(uint64_t);" + "i64vec2 findMSB(u64vec2);" + "i64vec3 findMSB(u64vec3);" + "i64vec4 findMSB(u64vec4);" + + "\n" + ); + } + + // GL_AMD_shader_trinary_minmax + if (profile != EEsProfile && version >= 430) { + commonBuiltins.append( + "float min3(float, float, float);" + "vec2 min3(vec2, vec2, vec2);" + "vec3 min3(vec3, vec3, vec3);" + "vec4 min3(vec4, vec4, vec4);" + + "int min3(int, int, int);" + "ivec2 min3(ivec2, ivec2, ivec2);" + "ivec3 min3(ivec3, ivec3, ivec3);" + "ivec4 min3(ivec4, ivec4, ivec4);" + + "uint min3(uint, uint, uint);" + "uvec2 min3(uvec2, uvec2, uvec2);" + "uvec3 min3(uvec3, uvec3, uvec3);" + "uvec4 min3(uvec4, uvec4, uvec4);" + + "float max3(float, float, float);" + "vec2 max3(vec2, vec2, vec2);" + "vec3 max3(vec3, vec3, vec3);" + "vec4 max3(vec4, vec4, vec4);" + + "int max3(int, int, int);" + "ivec2 max3(ivec2, ivec2, ivec2);" + "ivec3 max3(ivec3, ivec3, ivec3);" + "ivec4 max3(ivec4, ivec4, ivec4);" + + "uint max3(uint, uint, uint);" + "uvec2 max3(uvec2, uvec2, uvec2);" + "uvec3 max3(uvec3, uvec3, uvec3);" + "uvec4 max3(uvec4, uvec4, uvec4);" + + "float mid3(float, float, float);" + "vec2 mid3(vec2, vec2, vec2);" + "vec3 mid3(vec3, vec3, vec3);" + "vec4 mid3(vec4, vec4, vec4);" + + "int mid3(int, int, int);" + "ivec2 mid3(ivec2, ivec2, ivec2);" + "ivec3 mid3(ivec3, ivec3, ivec3);" + "ivec4 mid3(ivec4, ivec4, ivec4);" + + "uint mid3(uint, uint, uint);" + "uvec2 mid3(uvec2, uvec2, uvec2);" + "uvec3 mid3(uvec3, uvec3, uvec3);" + "uvec4 mid3(uvec4, uvec4, uvec4);" + + "float16_t min3(float16_t, float16_t, float16_t);" + "f16vec2 min3(f16vec2, f16vec2, f16vec2);" + "f16vec3 min3(f16vec3, f16vec3, f16vec3);" + "f16vec4 min3(f16vec4, f16vec4, f16vec4);" + + "float16_t max3(float16_t, float16_t, float16_t);" + "f16vec2 max3(f16vec2, f16vec2, f16vec2);" + "f16vec3 max3(f16vec3, f16vec3, f16vec3);" + "f16vec4 max3(f16vec4, f16vec4, f16vec4);" + + "float16_t mid3(float16_t, float16_t, float16_t);" + "f16vec2 mid3(f16vec2, f16vec2, f16vec2);" + "f16vec3 mid3(f16vec3, f16vec3, f16vec3);" + "f16vec4 mid3(f16vec4, f16vec4, f16vec4);" + + "int16_t min3(int16_t, int16_t, int16_t);" + "i16vec2 min3(i16vec2, i16vec2, i16vec2);" + "i16vec3 min3(i16vec3, i16vec3, i16vec3);" + "i16vec4 min3(i16vec4, i16vec4, i16vec4);" + + "int16_t max3(int16_t, int16_t, int16_t);" + "i16vec2 max3(i16vec2, i16vec2, i16vec2);" + "i16vec3 max3(i16vec3, i16vec3, i16vec3);" + "i16vec4 max3(i16vec4, i16vec4, i16vec4);" + + "int16_t mid3(int16_t, int16_t, int16_t);" + "i16vec2 mid3(i16vec2, i16vec2, i16vec2);" + "i16vec3 mid3(i16vec3, i16vec3, i16vec3);" + "i16vec4 mid3(i16vec4, i16vec4, i16vec4);" + + "uint16_t min3(uint16_t, uint16_t, uint16_t);" + "u16vec2 min3(u16vec2, u16vec2, u16vec2);" + "u16vec3 min3(u16vec3, u16vec3, u16vec3);" + "u16vec4 min3(u16vec4, u16vec4, u16vec4);" + + "uint16_t max3(uint16_t, uint16_t, uint16_t);" + "u16vec2 max3(u16vec2, u16vec2, u16vec2);" + "u16vec3 max3(u16vec3, u16vec3, u16vec3);" + "u16vec4 max3(u16vec4, u16vec4, u16vec4);" + + "uint16_t mid3(uint16_t, uint16_t, uint16_t);" + "u16vec2 mid3(u16vec2, u16vec2, u16vec2);" + "u16vec3 mid3(u16vec3, u16vec3, u16vec3);" + "u16vec4 mid3(u16vec4, u16vec4, u16vec4);" + + "\n" + ); + } +#endif // !GLSLANG_ANGLE + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 430)) { + commonBuiltins.append( + "uint atomicAdd(coherent volatile inout uint, uint, int, int, int);" + " int atomicAdd(coherent volatile inout int, int, int, int, int);" + + "uint atomicMin(coherent volatile inout uint, uint, int, int, int);" + " int atomicMin(coherent volatile inout int, int, int, int, int);" + + "uint atomicMax(coherent volatile inout uint, uint, int, int, int);" + " int atomicMax(coherent volatile inout int, int, int, int, int);" + + "uint atomicAnd(coherent volatile inout uint, uint, int, int, int);" + " int atomicAnd(coherent volatile inout int, int, int, int, int);" + + "uint atomicOr (coherent volatile inout uint, uint, int, int, int);" + " int atomicOr (coherent volatile inout int, int, int, int, int);" + + "uint atomicXor(coherent volatile inout uint, uint, int, int, int);" + " int atomicXor(coherent volatile inout int, int, int, int, int);" + + "uint atomicExchange(coherent volatile inout uint, uint, int, int, int);" + " int atomicExchange(coherent volatile inout int, int, int, int, int);" + + "uint atomicCompSwap(coherent volatile inout uint, uint, uint, int, int, int, int, int);" + " int atomicCompSwap(coherent volatile inout int, int, int, int, int, int, int, int);" + + "uint atomicLoad(coherent volatile in uint, int, int, int);" + " int atomicLoad(coherent volatile in int, int, int, int);" + + "void atomicStore(coherent volatile out uint, uint, int, int, int);" + "void atomicStore(coherent volatile out int, int, int, int, int);" + + "\n"); + } + +#ifndef GLSLANG_ANGLE + if (profile != EEsProfile && version >= 440) { + commonBuiltins.append( + "uint64_t atomicMin(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicMin(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicMin(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicMin(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicMax(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicMax(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicMax(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicMax(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicAnd(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicAnd(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicAnd(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicAnd(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicOr (coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicOr (coherent volatile inout int64_t, int64_t);" + "uint64_t atomicOr (coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicOr (coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicXor(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicXor(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicXor(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicXor(coherent volatile inout int64_t, int64_t, int, int, int);" + + "uint64_t atomicAdd(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicAdd(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicAdd(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicAdd(coherent volatile inout int64_t, int64_t, int, int, int);" + " float atomicAdd(coherent volatile inout float, float);" + " float atomicAdd(coherent volatile inout float, float, int, int, int);" + " double atomicAdd(coherent volatile inout double, double);" + " double atomicAdd(coherent volatile inout double, double, int, int, int);" + + "uint64_t atomicExchange(coherent volatile inout uint64_t, uint64_t);" + " int64_t atomicExchange(coherent volatile inout int64_t, int64_t);" + "uint64_t atomicExchange(coherent volatile inout uint64_t, uint64_t, int, int, int);" + " int64_t atomicExchange(coherent volatile inout int64_t, int64_t, int, int, int);" + " float atomicExchange(coherent volatile inout float, float);" + " float atomicExchange(coherent volatile inout float, float, int, int, int);" + " double atomicExchange(coherent volatile inout double, double);" + " double atomicExchange(coherent volatile inout double, double, int, int, int);" + + "uint64_t atomicCompSwap(coherent volatile inout uint64_t, uint64_t, uint64_t);" + " int64_t atomicCompSwap(coherent volatile inout int64_t, int64_t, int64_t);" + "uint64_t atomicCompSwap(coherent volatile inout uint64_t, uint64_t, uint64_t, int, int, int, int, int);" + " int64_t atomicCompSwap(coherent volatile inout int64_t, int64_t, int64_t, int, int, int, int, int);" + + "uint64_t atomicLoad(coherent volatile in uint64_t, int, int, int);" + " int64_t atomicLoad(coherent volatile in int64_t, int, int, int);" + " float atomicLoad(coherent volatile in float, int, int, int);" + " double atomicLoad(coherent volatile in double, int, int, int);" + + "void atomicStore(coherent volatile out uint64_t, uint64_t, int, int, int);" + "void atomicStore(coherent volatile out int64_t, int64_t, int, int, int);" + "void atomicStore(coherent volatile out float, float, int, int, int);" + "void atomicStore(coherent volatile out double, double, int, int, int);" + "\n"); + } +#endif // !GLSLANG_ANGLE +#endif // !GLSLANG_WEB + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { // GL_ARB_shader_bit_encoding + commonBuiltins.append( + "int floatBitsToInt(highp float value);" + "ivec2 floatBitsToInt(highp vec2 value);" + "ivec3 floatBitsToInt(highp vec3 value);" + "ivec4 floatBitsToInt(highp vec4 value);" + + "uint floatBitsToUint(highp float value);" + "uvec2 floatBitsToUint(highp vec2 value);" + "uvec3 floatBitsToUint(highp vec3 value);" + "uvec4 floatBitsToUint(highp vec4 value);" + + "float intBitsToFloat(highp int value);" + "vec2 intBitsToFloat(highp ivec2 value);" + "vec3 intBitsToFloat(highp ivec3 value);" + "vec4 intBitsToFloat(highp ivec4 value);" + + "float uintBitsToFloat(highp uint value);" + "vec2 uintBitsToFloat(highp uvec2 value);" + "vec3 uintBitsToFloat(highp uvec3 value);" + "vec4 uintBitsToFloat(highp uvec4 value);" + + "\n"); + } + +#ifndef GLSLANG_WEB + if ((profile != EEsProfile && version >= 400) || + (profile == EEsProfile && version >= 310)) { // GL_OES_gpu_shader5 + + commonBuiltins.append( + "float fma(float, float, float );" + "vec2 fma(vec2, vec2, vec2 );" + "vec3 fma(vec3, vec3, vec3 );" + "vec4 fma(vec4, vec4, vec4 );" + "\n"); + } + +#ifndef GLSLANG_ANGLE + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + "double fma(double, double, double);" + "dvec2 fma(dvec2, dvec2, dvec2 );" + "dvec3 fma(dvec3, dvec3, dvec3 );" + "dvec4 fma(dvec4, dvec4, dvec4 );" + "\n"); + } +#endif + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + "float frexp(highp float, out highp int);" + "vec2 frexp(highp vec2, out highp ivec2);" + "vec3 frexp(highp vec3, out highp ivec3);" + "vec4 frexp(highp vec4, out highp ivec4);" + + "float ldexp(highp float, highp int);" + "vec2 ldexp(highp vec2, highp ivec2);" + "vec3 ldexp(highp vec3, highp ivec3);" + "vec4 ldexp(highp vec4, highp ivec4);" + + "\n"); + } + +#ifndef GLSLANG_ANGLE + if (profile != EEsProfile && version >= 150) { // ARB_gpu_shader_fp64 + commonBuiltins.append( + "double frexp(double, out int);" + "dvec2 frexp( dvec2, out ivec2);" + "dvec3 frexp( dvec3, out ivec3);" + "dvec4 frexp( dvec4, out ivec4);" + + "double ldexp(double, int);" + "dvec2 ldexp( dvec2, ivec2);" + "dvec3 ldexp( dvec3, ivec3);" + "dvec4 ldexp( dvec4, ivec4);" + + "double packDouble2x32(uvec2);" + "uvec2 unpackDouble2x32(double);" + + "\n"); + } +#endif +#endif + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packUnorm2x16(vec2);" + "vec2 unpackUnorm2x16(highp uint);" + "\n"); + } + + if ((profile == EEsProfile && version >= 300) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packSnorm2x16(vec2);" + " vec2 unpackSnorm2x16(highp uint);" + "highp uint packHalf2x16(vec2);" + "\n"); + } + + if (profile == EEsProfile && version >= 300) { + commonBuiltins.append( + "mediump vec2 unpackHalf2x16(highp uint);" + "\n"); + } else if (profile != EEsProfile && version >= 150) { + commonBuiltins.append( + " vec2 unpackHalf2x16(highp uint);" + "\n"); + } + +#ifndef GLSLANG_WEB + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 150)) { + commonBuiltins.append( + "highp uint packSnorm4x8(vec4);" + "highp uint packUnorm4x8(vec4);" + "\n"); + } + + if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "mediump vec4 unpackSnorm4x8(highp uint);" + "mediump vec4 unpackUnorm4x8(highp uint);" + "\n"); + } else if (profile != EEsProfile && version >= 150) { + commonBuiltins.append( + "vec4 unpackSnorm4x8(highp uint);" + "vec4 unpackUnorm4x8(highp uint);" + "\n"); + } +#endif + + // + // Matrix Functions. + // + commonBuiltins.append( + "mat2 matrixCompMult(mat2 x, mat2 y);" + "mat3 matrixCompMult(mat3 x, mat3 y);" + "mat4 matrixCompMult(mat4 x, mat4 y);" + + "\n"); + + // 120 is correct for both ES and desktop + if (version >= 120) { + commonBuiltins.append( + "mat2 outerProduct(vec2 c, vec2 r);" + "mat3 outerProduct(vec3 c, vec3 r);" + "mat4 outerProduct(vec4 c, vec4 r);" + "mat2x3 outerProduct(vec3 c, vec2 r);" + "mat3x2 outerProduct(vec2 c, vec3 r);" + "mat2x4 outerProduct(vec4 c, vec2 r);" + "mat4x2 outerProduct(vec2 c, vec4 r);" + "mat3x4 outerProduct(vec4 c, vec3 r);" + "mat4x3 outerProduct(vec3 c, vec4 r);" + + "mat2 transpose(mat2 m);" + "mat3 transpose(mat3 m);" + "mat4 transpose(mat4 m);" + "mat2x3 transpose(mat3x2 m);" + "mat3x2 transpose(mat2x3 m);" + "mat2x4 transpose(mat4x2 m);" + "mat4x2 transpose(mat2x4 m);" + "mat3x4 transpose(mat4x3 m);" + "mat4x3 transpose(mat3x4 m);" + + "mat2x3 matrixCompMult(mat2x3, mat2x3);" + "mat2x4 matrixCompMult(mat2x4, mat2x4);" + "mat3x2 matrixCompMult(mat3x2, mat3x2);" + "mat3x4 matrixCompMult(mat3x4, mat3x4);" + "mat4x2 matrixCompMult(mat4x2, mat4x2);" + "mat4x3 matrixCompMult(mat4x3, mat4x3);" + + "\n"); + + // 150 is correct for both ES and desktop + if (version >= 150) { + commonBuiltins.append( + "float determinant(mat2 m);" + "float determinant(mat3 m);" + "float determinant(mat4 m);" + + "mat2 inverse(mat2 m);" + "mat3 inverse(mat3 m);" + "mat4 inverse(mat4 m);" + + "\n"); + } + } + +#ifndef GLSLANG_WEB +#ifndef GLSLANG_ANGLE + // + // Original-style texture functions existing in all stages. + // (Per-stage functions below.) + // + if ((profile == EEsProfile && version == 100) || + profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + commonBuiltins.append( + "vec4 texture2D(sampler2D, vec2);" + + "vec4 texture2DProj(sampler2D, vec3);" + "vec4 texture2DProj(sampler2D, vec4);" + + "vec4 texture3D(sampler3D, vec3);" // OES_texture_3D, but caught by keyword check + "vec4 texture3DProj(sampler3D, vec4);" // OES_texture_3D, but caught by keyword check + + "vec4 textureCube(samplerCube, vec3);" + + "\n"); + } + } + + if ( profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + commonBuiltins.append( + "vec4 texture1D(sampler1D, float);" + + "vec4 texture1DProj(sampler1D, vec2);" + "vec4 texture1DProj(sampler1D, vec4);" + + "vec4 shadow1D(sampler1DShadow, vec3);" + "vec4 shadow2D(sampler2DShadow, vec3);" + "vec4 shadow1DProj(sampler1DShadow, vec4);" + "vec4 shadow2DProj(sampler2DShadow, vec4);" + + "vec4 texture2DRect(sampler2DRect, vec2);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 texture2DRectProj(sampler2DRect, vec3);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 texture2DRectProj(sampler2DRect, vec4);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 shadow2DRect(sampler2DRectShadow, vec3);" // GL_ARB_texture_rectangle, caught by keyword check + "vec4 shadow2DRectProj(sampler2DRectShadow, vec4);" // GL_ARB_texture_rectangle, caught by keyword check + + "\n"); + } + } + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + if (version < 300) { + commonBuiltins.append( + "vec4 texture2D(samplerExternalOES, vec2 coord);" // GL_OES_EGL_image_external + "vec4 texture2DProj(samplerExternalOES, vec3);" // GL_OES_EGL_image_external + "vec4 texture2DProj(samplerExternalOES, vec4);" // GL_OES_EGL_image_external + "\n"); + } else { + commonBuiltins.append( + "highp ivec2 textureSize(samplerExternalOES, int lod);" // GL_OES_EGL_image_external_essl3 + "vec4 texture(samplerExternalOES, vec2);" // GL_OES_EGL_image_external_essl3 + "vec4 texture(samplerExternalOES, vec2, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec3);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec3, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec4);" // GL_OES_EGL_image_external_essl3 + "vec4 textureProj(samplerExternalOES, vec4, float bias);" // GL_OES_EGL_image_external_essl3 + "vec4 texelFetch(samplerExternalOES, ivec2, int lod);" // GL_OES_EGL_image_external_essl3 + "\n"); + } + commonBuiltins.append( + "highp ivec2 textureSize(__samplerExternal2DY2YEXT, int lod);" // GL_EXT_YUV_target + "vec4 texture(__samplerExternal2DY2YEXT, vec2);" // GL_EXT_YUV_target + "vec4 texture(__samplerExternal2DY2YEXT, vec2, float bias);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec3);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec3, float bias);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec4);" // GL_EXT_YUV_target + "vec4 textureProj(__samplerExternal2DY2YEXT, vec4, float bias);" // GL_EXT_YUV_target + "vec4 texelFetch(__samplerExternal2DY2YEXT sampler, ivec2, int lod);" // GL_EXT_YUV_target + "\n"); + commonBuiltins.append( + "vec4 texture2DGradEXT(sampler2D, vec2, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjGradEXT(sampler2D, vec3, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjGradEXT(sampler2D, vec4, vec2, vec2);" // GL_EXT_shader_texture_lod + "vec4 textureCubeGradEXT(samplerCube, vec3, vec3, vec3);" // GL_EXT_shader_texture_lod + + "float shadow2DEXT(sampler2DShadow, vec3);" // GL_EXT_shadow_samplers + "float shadow2DProjEXT(sampler2DShadow, vec4);" // GL_EXT_shadow_samplers + + "\n"); + } + } + + // + // Noise functions. + // + if (spvVersion.spv == 0 && profile != EEsProfile) { + commonBuiltins.append( + "float noise1(float x);" + "float noise1(vec2 x);" + "float noise1(vec3 x);" + "float noise1(vec4 x);" + + "vec2 noise2(float x);" + "vec2 noise2(vec2 x);" + "vec2 noise2(vec3 x);" + "vec2 noise2(vec4 x);" + + "vec3 noise3(float x);" + "vec3 noise3(vec2 x);" + "vec3 noise3(vec3 x);" + "vec3 noise3(vec4 x);" + + "vec4 noise4(float x);" + "vec4 noise4(vec2 x);" + "vec4 noise4(vec3 x);" + "vec4 noise4(vec4 x);" + + "\n"); + } + + if (spvVersion.vulkan == 0) { + // + // Atomic counter functions. + // + if ((profile != EEsProfile && version >= 300) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append( + "uint atomicCounterIncrement(atomic_uint);" + "uint atomicCounterDecrement(atomic_uint);" + "uint atomicCounter(atomic_uint);" + + "\n"); + } + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append( + "uint atomicCounterAdd(atomic_uint, uint);" + "uint atomicCounterSubtract(atomic_uint, uint);" + "uint atomicCounterMin(atomic_uint, uint);" + "uint atomicCounterMax(atomic_uint, uint);" + "uint atomicCounterAnd(atomic_uint, uint);" + "uint atomicCounterOr(atomic_uint, uint);" + "uint atomicCounterXor(atomic_uint, uint);" + "uint atomicCounterExchange(atomic_uint, uint);" + "uint atomicCounterCompSwap(atomic_uint, uint, uint);" + + "\n"); + } + } +#endif // !GLSLANG_ANGLE + + // Bitfield + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + " int bitfieldExtract( int, int, int);" + "ivec2 bitfieldExtract(ivec2, int, int);" + "ivec3 bitfieldExtract(ivec3, int, int);" + "ivec4 bitfieldExtract(ivec4, int, int);" + + " uint bitfieldExtract( uint, int, int);" + "uvec2 bitfieldExtract(uvec2, int, int);" + "uvec3 bitfieldExtract(uvec3, int, int);" + "uvec4 bitfieldExtract(uvec4, int, int);" + + " int bitfieldInsert( int base, int, int, int);" + "ivec2 bitfieldInsert(ivec2 base, ivec2, int, int);" + "ivec3 bitfieldInsert(ivec3 base, ivec3, int, int);" + "ivec4 bitfieldInsert(ivec4 base, ivec4, int, int);" + + " uint bitfieldInsert( uint base, uint, int, int);" + "uvec2 bitfieldInsert(uvec2 base, uvec2, int, int);" + "uvec3 bitfieldInsert(uvec3 base, uvec3, int, int);" + "uvec4 bitfieldInsert(uvec4 base, uvec4, int, int);" + + "\n"); + } + + if (profile != EEsProfile && version >= 400) { + commonBuiltins.append( + " int findLSB( int);" + "ivec2 findLSB(ivec2);" + "ivec3 findLSB(ivec3);" + "ivec4 findLSB(ivec4);" + + " int findLSB( uint);" + "ivec2 findLSB(uvec2);" + "ivec3 findLSB(uvec3);" + "ivec4 findLSB(uvec4);" + + "\n"); + } else if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "lowp int findLSB( int);" + "lowp ivec2 findLSB(ivec2);" + "lowp ivec3 findLSB(ivec3);" + "lowp ivec4 findLSB(ivec4);" + + "lowp int findLSB( uint);" + "lowp ivec2 findLSB(uvec2);" + "lowp ivec3 findLSB(uvec3);" + "lowp ivec4 findLSB(uvec4);" + + "\n"); + } + + if (profile != EEsProfile && version >= 400) { + commonBuiltins.append( + " int bitCount( int);" + "ivec2 bitCount(ivec2);" + "ivec3 bitCount(ivec3);" + "ivec4 bitCount(ivec4);" + + " int bitCount( uint);" + "ivec2 bitCount(uvec2);" + "ivec3 bitCount(uvec3);" + "ivec4 bitCount(uvec4);" + + " int findMSB(highp int);" + "ivec2 findMSB(highp ivec2);" + "ivec3 findMSB(highp ivec3);" + "ivec4 findMSB(highp ivec4);" + + " int findMSB(highp uint);" + "ivec2 findMSB(highp uvec2);" + "ivec3 findMSB(highp uvec3);" + "ivec4 findMSB(highp uvec4);" + + "\n"); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + commonBuiltins.append( + " uint uaddCarry(highp uint, highp uint, out lowp uint carry);" + "uvec2 uaddCarry(highp uvec2, highp uvec2, out lowp uvec2 carry);" + "uvec3 uaddCarry(highp uvec3, highp uvec3, out lowp uvec3 carry);" + "uvec4 uaddCarry(highp uvec4, highp uvec4, out lowp uvec4 carry);" + + " uint usubBorrow(highp uint, highp uint, out lowp uint borrow);" + "uvec2 usubBorrow(highp uvec2, highp uvec2, out lowp uvec2 borrow);" + "uvec3 usubBorrow(highp uvec3, highp uvec3, out lowp uvec3 borrow);" + "uvec4 usubBorrow(highp uvec4, highp uvec4, out lowp uvec4 borrow);" + + "void umulExtended(highp uint, highp uint, out highp uint, out highp uint lsb);" + "void umulExtended(highp uvec2, highp uvec2, out highp uvec2, out highp uvec2 lsb);" + "void umulExtended(highp uvec3, highp uvec3, out highp uvec3, out highp uvec3 lsb);" + "void umulExtended(highp uvec4, highp uvec4, out highp uvec4, out highp uvec4 lsb);" + + "void imulExtended(highp int, highp int, out highp int, out highp int lsb);" + "void imulExtended(highp ivec2, highp ivec2, out highp ivec2, out highp ivec2 lsb);" + "void imulExtended(highp ivec3, highp ivec3, out highp ivec3, out highp ivec3 lsb);" + "void imulExtended(highp ivec4, highp ivec4, out highp ivec4, out highp ivec4 lsb);" + + " int bitfieldReverse(highp int);" + "ivec2 bitfieldReverse(highp ivec2);" + "ivec3 bitfieldReverse(highp ivec3);" + "ivec4 bitfieldReverse(highp ivec4);" + + " uint bitfieldReverse(highp uint);" + "uvec2 bitfieldReverse(highp uvec2);" + "uvec3 bitfieldReverse(highp uvec3);" + "uvec4 bitfieldReverse(highp uvec4);" + + "\n"); + } + + if (profile == EEsProfile && version >= 310) { + commonBuiltins.append( + "lowp int bitCount( int);" + "lowp ivec2 bitCount(ivec2);" + "lowp ivec3 bitCount(ivec3);" + "lowp ivec4 bitCount(ivec4);" + + "lowp int bitCount( uint);" + "lowp ivec2 bitCount(uvec2);" + "lowp ivec3 bitCount(uvec3);" + "lowp ivec4 bitCount(uvec4);" + + "lowp int findMSB(highp int);" + "lowp ivec2 findMSB(highp ivec2);" + "lowp ivec3 findMSB(highp ivec3);" + "lowp ivec4 findMSB(highp ivec4);" + + "lowp int findMSB(highp uint);" + "lowp ivec2 findMSB(highp uvec2);" + "lowp ivec3 findMSB(highp uvec3);" + "lowp ivec4 findMSB(highp uvec4);" + + "\n"); + } + +#ifndef GLSLANG_ANGLE + // GL_ARB_shader_ballot + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uint64_t ballotARB(bool);" + + "float readInvocationARB(float, uint);" + "vec2 readInvocationARB(vec2, uint);" + "vec3 readInvocationARB(vec3, uint);" + "vec4 readInvocationARB(vec4, uint);" + + "int readInvocationARB(int, uint);" + "ivec2 readInvocationARB(ivec2, uint);" + "ivec3 readInvocationARB(ivec3, uint);" + "ivec4 readInvocationARB(ivec4, uint);" + + "uint readInvocationARB(uint, uint);" + "uvec2 readInvocationARB(uvec2, uint);" + "uvec3 readInvocationARB(uvec3, uint);" + "uvec4 readInvocationARB(uvec4, uint);" + + "float readFirstInvocationARB(float);" + "vec2 readFirstInvocationARB(vec2);" + "vec3 readFirstInvocationARB(vec3);" + "vec4 readFirstInvocationARB(vec4);" + + "int readFirstInvocationARB(int);" + "ivec2 readFirstInvocationARB(ivec2);" + "ivec3 readFirstInvocationARB(ivec3);" + "ivec4 readFirstInvocationARB(ivec4);" + + "uint readFirstInvocationARB(uint);" + "uvec2 readFirstInvocationARB(uvec2);" + "uvec3 readFirstInvocationARB(uvec3);" + "uvec4 readFirstInvocationARB(uvec4);" + + "\n"); + } + + // GL_ARB_shader_group_vote + if (profile != EEsProfile && version >= 430) { + commonBuiltins.append( + "bool anyInvocationARB(bool);" + "bool allInvocationsARB(bool);" + "bool allInvocationsEqualARB(bool);" + + "\n"); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + commonBuiltins.append( + "void subgroupBarrier();" + "void subgroupMemoryBarrier();" + "void subgroupMemoryBarrierBuffer();" + "void subgroupMemoryBarrierImage();" + "bool subgroupElect();" + + "bool subgroupAll(bool);\n" + "bool subgroupAny(bool);\n" + "uvec4 subgroupBallot(bool);\n" + "bool subgroupInverseBallot(uvec4);\n" + "bool subgroupBallotBitExtract(uvec4, uint);\n" + "uint subgroupBallotBitCount(uvec4);\n" + "uint subgroupBallotInclusiveBitCount(uvec4);\n" + "uint subgroupBallotExclusiveBitCount(uvec4);\n" + "uint subgroupBallotFindLSB(uvec4);\n" + "uint subgroupBallotFindMSB(uvec4);\n" + ); + + // Generate all flavors of subgroup ops. + static const char *subgroupOps[] = + { + "bool subgroupAllEqual(%s);\n", + "%s subgroupBroadcast(%s, uint);\n", + "%s subgroupBroadcastFirst(%s);\n", + "%s subgroupShuffle(%s, uint);\n", + "%s subgroupShuffleXor(%s, uint);\n", + "%s subgroupShuffleUp(%s, uint delta);\n", + "%s subgroupShuffleDown(%s, uint delta);\n", + "%s subgroupAdd(%s);\n", + "%s subgroupMul(%s);\n", + "%s subgroupMin(%s);\n", + "%s subgroupMax(%s);\n", + "%s subgroupAnd(%s);\n", + "%s subgroupOr(%s);\n", + "%s subgroupXor(%s);\n", + "%s subgroupInclusiveAdd(%s);\n", + "%s subgroupInclusiveMul(%s);\n", + "%s subgroupInclusiveMin(%s);\n", + "%s subgroupInclusiveMax(%s);\n", + "%s subgroupInclusiveAnd(%s);\n", + "%s subgroupInclusiveOr(%s);\n", + "%s subgroupInclusiveXor(%s);\n", + "%s subgroupExclusiveAdd(%s);\n", + "%s subgroupExclusiveMul(%s);\n", + "%s subgroupExclusiveMin(%s);\n", + "%s subgroupExclusiveMax(%s);\n", + "%s subgroupExclusiveAnd(%s);\n", + "%s subgroupExclusiveOr(%s);\n", + "%s subgroupExclusiveXor(%s);\n", + "%s subgroupClusteredAdd(%s, uint);\n", + "%s subgroupClusteredMul(%s, uint);\n", + "%s subgroupClusteredMin(%s, uint);\n", + "%s subgroupClusteredMax(%s, uint);\n", + "%s subgroupClusteredAnd(%s, uint);\n", + "%s subgroupClusteredOr(%s, uint);\n", + "%s subgroupClusteredXor(%s, uint);\n", + "%s subgroupQuadBroadcast(%s, uint);\n", + "%s subgroupQuadSwapHorizontal(%s);\n", + "%s subgroupQuadSwapVertical(%s);\n", + "%s subgroupQuadSwapDiagonal(%s);\n", + "uvec4 subgroupPartitionNV(%s);\n", + "%s subgroupPartitionedAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedXorNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedInclusiveXorNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveAddNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMulNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMinNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveMaxNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveAndNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveOrNV(%s, uvec4 ballot);\n", + "%s subgroupPartitionedExclusiveXorNV(%s, uvec4 ballot);\n", + }; + + static const char *floatTypes[] = { + "float", "vec2", "vec3", "vec4", + "float16_t", "f16vec2", "f16vec3", "f16vec4", + }; + static const char *doubleTypes[] = { + "double", "dvec2", "dvec3", "dvec4", + }; + static const char *intTypes[] = { + "int8_t", "i8vec2", "i8vec3", "i8vec4", + "int16_t", "i16vec2", "i16vec3", "i16vec4", + "int", "ivec2", "ivec3", "ivec4", + "int64_t", "i64vec2", "i64vec3", "i64vec4", + "uint8_t", "u8vec2", "u8vec3", "u8vec4", + "uint16_t", "u16vec2", "u16vec3", "u16vec4", + "uint", "uvec2", "uvec3", "uvec4", + "uint64_t", "u64vec2", "u64vec3", "u64vec4", + }; + static const char *boolTypes[] = { + "bool", "bvec2", "bvec3", "bvec4", + }; + + for (size_t i = 0; i < sizeof(subgroupOps)/sizeof(subgroupOps[0]); ++i) { + const char *op = subgroupOps[i]; + + // Logical operations don't support float + bool logicalOp = strstr(op, "Or") || strstr(op, "And") || + (strstr(op, "Xor") && !strstr(op, "ShuffleXor")); + // Math operations don't support bool + bool mathOp = strstr(op, "Add") || strstr(op, "Mul") || strstr(op, "Min") || strstr(op, "Max"); + + const int bufSize = 256; + char buf[bufSize]; + + if (!logicalOp) { + for (size_t j = 0; j < sizeof(floatTypes)/sizeof(floatTypes[0]); ++j) { + snprintf(buf, bufSize, op, floatTypes[j], floatTypes[j]); + commonBuiltins.append(buf); + } + if (profile != EEsProfile && version >= 400) { + for (size_t j = 0; j < sizeof(doubleTypes)/sizeof(doubleTypes[0]); ++j) { + snprintf(buf, bufSize, op, doubleTypes[j], doubleTypes[j]); + commonBuiltins.append(buf); + } + } + } + if (!mathOp) { + for (size_t j = 0; j < sizeof(boolTypes)/sizeof(boolTypes[0]); ++j) { + snprintf(buf, bufSize, op, boolTypes[j], boolTypes[j]); + commonBuiltins.append(buf); + } + } + for (size_t j = 0; j < sizeof(intTypes)/sizeof(intTypes[0]); ++j) { + snprintf(buf, bufSize, op, intTypes[j], intTypes[j]); + commonBuiltins.append(buf); + } + } + + stageBuiltins[EShLangCompute].append( + "void subgroupMemoryBarrierShared();" + + "\n" + ); + stageBuiltins[EShLangMeshNV].append( + "void subgroupMemoryBarrierShared();" + "\n" + ); + stageBuiltins[EShLangTaskNV].append( + "void subgroupMemoryBarrierShared();" + "\n" + ); + } + + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append( + "bool anyInvocation(bool);" + "bool allInvocations(bool);" + "bool allInvocationsEqual(bool);" + + "\n"); + } + + // GL_AMD_shader_ballot + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "float minInvocationsAMD(float);" + "vec2 minInvocationsAMD(vec2);" + "vec3 minInvocationsAMD(vec3);" + "vec4 minInvocationsAMD(vec4);" + + "int minInvocationsAMD(int);" + "ivec2 minInvocationsAMD(ivec2);" + "ivec3 minInvocationsAMD(ivec3);" + "ivec4 minInvocationsAMD(ivec4);" + + "uint minInvocationsAMD(uint);" + "uvec2 minInvocationsAMD(uvec2);" + "uvec3 minInvocationsAMD(uvec3);" + "uvec4 minInvocationsAMD(uvec4);" + + "double minInvocationsAMD(double);" + "dvec2 minInvocationsAMD(dvec2);" + "dvec3 minInvocationsAMD(dvec3);" + "dvec4 minInvocationsAMD(dvec4);" + + "int64_t minInvocationsAMD(int64_t);" + "i64vec2 minInvocationsAMD(i64vec2);" + "i64vec3 minInvocationsAMD(i64vec3);" + "i64vec4 minInvocationsAMD(i64vec4);" + + "uint64_t minInvocationsAMD(uint64_t);" + "u64vec2 minInvocationsAMD(u64vec2);" + "u64vec3 minInvocationsAMD(u64vec3);" + "u64vec4 minInvocationsAMD(u64vec4);" + + "float16_t minInvocationsAMD(float16_t);" + "f16vec2 minInvocationsAMD(f16vec2);" + "f16vec3 minInvocationsAMD(f16vec3);" + "f16vec4 minInvocationsAMD(f16vec4);" + + "int16_t minInvocationsAMD(int16_t);" + "i16vec2 minInvocationsAMD(i16vec2);" + "i16vec3 minInvocationsAMD(i16vec3);" + "i16vec4 minInvocationsAMD(i16vec4);" + + "uint16_t minInvocationsAMD(uint16_t);" + "u16vec2 minInvocationsAMD(u16vec2);" + "u16vec3 minInvocationsAMD(u16vec3);" + "u16vec4 minInvocationsAMD(u16vec4);" + + "float minInvocationsInclusiveScanAMD(float);" + "vec2 minInvocationsInclusiveScanAMD(vec2);" + "vec3 minInvocationsInclusiveScanAMD(vec3);" + "vec4 minInvocationsInclusiveScanAMD(vec4);" + + "int minInvocationsInclusiveScanAMD(int);" + "ivec2 minInvocationsInclusiveScanAMD(ivec2);" + "ivec3 minInvocationsInclusiveScanAMD(ivec3);" + "ivec4 minInvocationsInclusiveScanAMD(ivec4);" + + "uint minInvocationsInclusiveScanAMD(uint);" + "uvec2 minInvocationsInclusiveScanAMD(uvec2);" + "uvec3 minInvocationsInclusiveScanAMD(uvec3);" + "uvec4 minInvocationsInclusiveScanAMD(uvec4);" + + "double minInvocationsInclusiveScanAMD(double);" + "dvec2 minInvocationsInclusiveScanAMD(dvec2);" + "dvec3 minInvocationsInclusiveScanAMD(dvec3);" + "dvec4 minInvocationsInclusiveScanAMD(dvec4);" + + "int64_t minInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 minInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 minInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 minInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t minInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 minInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 minInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 minInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t minInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 minInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 minInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 minInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t minInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 minInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 minInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 minInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t minInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 minInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 minInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 minInvocationsInclusiveScanAMD(u16vec4);" + + "float minInvocationsExclusiveScanAMD(float);" + "vec2 minInvocationsExclusiveScanAMD(vec2);" + "vec3 minInvocationsExclusiveScanAMD(vec3);" + "vec4 minInvocationsExclusiveScanAMD(vec4);" + + "int minInvocationsExclusiveScanAMD(int);" + "ivec2 minInvocationsExclusiveScanAMD(ivec2);" + "ivec3 minInvocationsExclusiveScanAMD(ivec3);" + "ivec4 minInvocationsExclusiveScanAMD(ivec4);" + + "uint minInvocationsExclusiveScanAMD(uint);" + "uvec2 minInvocationsExclusiveScanAMD(uvec2);" + "uvec3 minInvocationsExclusiveScanAMD(uvec3);" + "uvec4 minInvocationsExclusiveScanAMD(uvec4);" + + "double minInvocationsExclusiveScanAMD(double);" + "dvec2 minInvocationsExclusiveScanAMD(dvec2);" + "dvec3 minInvocationsExclusiveScanAMD(dvec3);" + "dvec4 minInvocationsExclusiveScanAMD(dvec4);" + + "int64_t minInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 minInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 minInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 minInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t minInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 minInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 minInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 minInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t minInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 minInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 minInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 minInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t minInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 minInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 minInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 minInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t minInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 minInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 minInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 minInvocationsExclusiveScanAMD(u16vec4);" + + "float maxInvocationsAMD(float);" + "vec2 maxInvocationsAMD(vec2);" + "vec3 maxInvocationsAMD(vec3);" + "vec4 maxInvocationsAMD(vec4);" + + "int maxInvocationsAMD(int);" + "ivec2 maxInvocationsAMD(ivec2);" + "ivec3 maxInvocationsAMD(ivec3);" + "ivec4 maxInvocationsAMD(ivec4);" + + "uint maxInvocationsAMD(uint);" + "uvec2 maxInvocationsAMD(uvec2);" + "uvec3 maxInvocationsAMD(uvec3);" + "uvec4 maxInvocationsAMD(uvec4);" + + "double maxInvocationsAMD(double);" + "dvec2 maxInvocationsAMD(dvec2);" + "dvec3 maxInvocationsAMD(dvec3);" + "dvec4 maxInvocationsAMD(dvec4);" + + "int64_t maxInvocationsAMD(int64_t);" + "i64vec2 maxInvocationsAMD(i64vec2);" + "i64vec3 maxInvocationsAMD(i64vec3);" + "i64vec4 maxInvocationsAMD(i64vec4);" + + "uint64_t maxInvocationsAMD(uint64_t);" + "u64vec2 maxInvocationsAMD(u64vec2);" + "u64vec3 maxInvocationsAMD(u64vec3);" + "u64vec4 maxInvocationsAMD(u64vec4);" + + "float16_t maxInvocationsAMD(float16_t);" + "f16vec2 maxInvocationsAMD(f16vec2);" + "f16vec3 maxInvocationsAMD(f16vec3);" + "f16vec4 maxInvocationsAMD(f16vec4);" + + "int16_t maxInvocationsAMD(int16_t);" + "i16vec2 maxInvocationsAMD(i16vec2);" + "i16vec3 maxInvocationsAMD(i16vec3);" + "i16vec4 maxInvocationsAMD(i16vec4);" + + "uint16_t maxInvocationsAMD(uint16_t);" + "u16vec2 maxInvocationsAMD(u16vec2);" + "u16vec3 maxInvocationsAMD(u16vec3);" + "u16vec4 maxInvocationsAMD(u16vec4);" + + "float maxInvocationsInclusiveScanAMD(float);" + "vec2 maxInvocationsInclusiveScanAMD(vec2);" + "vec3 maxInvocationsInclusiveScanAMD(vec3);" + "vec4 maxInvocationsInclusiveScanAMD(vec4);" + + "int maxInvocationsInclusiveScanAMD(int);" + "ivec2 maxInvocationsInclusiveScanAMD(ivec2);" + "ivec3 maxInvocationsInclusiveScanAMD(ivec3);" + "ivec4 maxInvocationsInclusiveScanAMD(ivec4);" + + "uint maxInvocationsInclusiveScanAMD(uint);" + "uvec2 maxInvocationsInclusiveScanAMD(uvec2);" + "uvec3 maxInvocationsInclusiveScanAMD(uvec3);" + "uvec4 maxInvocationsInclusiveScanAMD(uvec4);" + + "double maxInvocationsInclusiveScanAMD(double);" + "dvec2 maxInvocationsInclusiveScanAMD(dvec2);" + "dvec3 maxInvocationsInclusiveScanAMD(dvec3);" + "dvec4 maxInvocationsInclusiveScanAMD(dvec4);" + + "int64_t maxInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 maxInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 maxInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 maxInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t maxInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 maxInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 maxInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 maxInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t maxInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 maxInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 maxInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 maxInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t maxInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 maxInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 maxInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 maxInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t maxInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 maxInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 maxInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 maxInvocationsInclusiveScanAMD(u16vec4);" + + "float maxInvocationsExclusiveScanAMD(float);" + "vec2 maxInvocationsExclusiveScanAMD(vec2);" + "vec3 maxInvocationsExclusiveScanAMD(vec3);" + "vec4 maxInvocationsExclusiveScanAMD(vec4);" + + "int maxInvocationsExclusiveScanAMD(int);" + "ivec2 maxInvocationsExclusiveScanAMD(ivec2);" + "ivec3 maxInvocationsExclusiveScanAMD(ivec3);" + "ivec4 maxInvocationsExclusiveScanAMD(ivec4);" + + "uint maxInvocationsExclusiveScanAMD(uint);" + "uvec2 maxInvocationsExclusiveScanAMD(uvec2);" + "uvec3 maxInvocationsExclusiveScanAMD(uvec3);" + "uvec4 maxInvocationsExclusiveScanAMD(uvec4);" + + "double maxInvocationsExclusiveScanAMD(double);" + "dvec2 maxInvocationsExclusiveScanAMD(dvec2);" + "dvec3 maxInvocationsExclusiveScanAMD(dvec3);" + "dvec4 maxInvocationsExclusiveScanAMD(dvec4);" + + "int64_t maxInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 maxInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 maxInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 maxInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t maxInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 maxInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 maxInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 maxInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t maxInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 maxInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 maxInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 maxInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t maxInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 maxInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 maxInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 maxInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t maxInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 maxInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 maxInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 maxInvocationsExclusiveScanAMD(u16vec4);" + + "float addInvocationsAMD(float);" + "vec2 addInvocationsAMD(vec2);" + "vec3 addInvocationsAMD(vec3);" + "vec4 addInvocationsAMD(vec4);" + + "int addInvocationsAMD(int);" + "ivec2 addInvocationsAMD(ivec2);" + "ivec3 addInvocationsAMD(ivec3);" + "ivec4 addInvocationsAMD(ivec4);" + + "uint addInvocationsAMD(uint);" + "uvec2 addInvocationsAMD(uvec2);" + "uvec3 addInvocationsAMD(uvec3);" + "uvec4 addInvocationsAMD(uvec4);" + + "double addInvocationsAMD(double);" + "dvec2 addInvocationsAMD(dvec2);" + "dvec3 addInvocationsAMD(dvec3);" + "dvec4 addInvocationsAMD(dvec4);" + + "int64_t addInvocationsAMD(int64_t);" + "i64vec2 addInvocationsAMD(i64vec2);" + "i64vec3 addInvocationsAMD(i64vec3);" + "i64vec4 addInvocationsAMD(i64vec4);" + + "uint64_t addInvocationsAMD(uint64_t);" + "u64vec2 addInvocationsAMD(u64vec2);" + "u64vec3 addInvocationsAMD(u64vec3);" + "u64vec4 addInvocationsAMD(u64vec4);" + + "float16_t addInvocationsAMD(float16_t);" + "f16vec2 addInvocationsAMD(f16vec2);" + "f16vec3 addInvocationsAMD(f16vec3);" + "f16vec4 addInvocationsAMD(f16vec4);" + + "int16_t addInvocationsAMD(int16_t);" + "i16vec2 addInvocationsAMD(i16vec2);" + "i16vec3 addInvocationsAMD(i16vec3);" + "i16vec4 addInvocationsAMD(i16vec4);" + + "uint16_t addInvocationsAMD(uint16_t);" + "u16vec2 addInvocationsAMD(u16vec2);" + "u16vec3 addInvocationsAMD(u16vec3);" + "u16vec4 addInvocationsAMD(u16vec4);" + + "float addInvocationsInclusiveScanAMD(float);" + "vec2 addInvocationsInclusiveScanAMD(vec2);" + "vec3 addInvocationsInclusiveScanAMD(vec3);" + "vec4 addInvocationsInclusiveScanAMD(vec4);" + + "int addInvocationsInclusiveScanAMD(int);" + "ivec2 addInvocationsInclusiveScanAMD(ivec2);" + "ivec3 addInvocationsInclusiveScanAMD(ivec3);" + "ivec4 addInvocationsInclusiveScanAMD(ivec4);" + + "uint addInvocationsInclusiveScanAMD(uint);" + "uvec2 addInvocationsInclusiveScanAMD(uvec2);" + "uvec3 addInvocationsInclusiveScanAMD(uvec3);" + "uvec4 addInvocationsInclusiveScanAMD(uvec4);" + + "double addInvocationsInclusiveScanAMD(double);" + "dvec2 addInvocationsInclusiveScanAMD(dvec2);" + "dvec3 addInvocationsInclusiveScanAMD(dvec3);" + "dvec4 addInvocationsInclusiveScanAMD(dvec4);" + + "int64_t addInvocationsInclusiveScanAMD(int64_t);" + "i64vec2 addInvocationsInclusiveScanAMD(i64vec2);" + "i64vec3 addInvocationsInclusiveScanAMD(i64vec3);" + "i64vec4 addInvocationsInclusiveScanAMD(i64vec4);" + + "uint64_t addInvocationsInclusiveScanAMD(uint64_t);" + "u64vec2 addInvocationsInclusiveScanAMD(u64vec2);" + "u64vec3 addInvocationsInclusiveScanAMD(u64vec3);" + "u64vec4 addInvocationsInclusiveScanAMD(u64vec4);" + + "float16_t addInvocationsInclusiveScanAMD(float16_t);" + "f16vec2 addInvocationsInclusiveScanAMD(f16vec2);" + "f16vec3 addInvocationsInclusiveScanAMD(f16vec3);" + "f16vec4 addInvocationsInclusiveScanAMD(f16vec4);" + + "int16_t addInvocationsInclusiveScanAMD(int16_t);" + "i16vec2 addInvocationsInclusiveScanAMD(i16vec2);" + "i16vec3 addInvocationsInclusiveScanAMD(i16vec3);" + "i16vec4 addInvocationsInclusiveScanAMD(i16vec4);" + + "uint16_t addInvocationsInclusiveScanAMD(uint16_t);" + "u16vec2 addInvocationsInclusiveScanAMD(u16vec2);" + "u16vec3 addInvocationsInclusiveScanAMD(u16vec3);" + "u16vec4 addInvocationsInclusiveScanAMD(u16vec4);" + + "float addInvocationsExclusiveScanAMD(float);" + "vec2 addInvocationsExclusiveScanAMD(vec2);" + "vec3 addInvocationsExclusiveScanAMD(vec3);" + "vec4 addInvocationsExclusiveScanAMD(vec4);" + + "int addInvocationsExclusiveScanAMD(int);" + "ivec2 addInvocationsExclusiveScanAMD(ivec2);" + "ivec3 addInvocationsExclusiveScanAMD(ivec3);" + "ivec4 addInvocationsExclusiveScanAMD(ivec4);" + + "uint addInvocationsExclusiveScanAMD(uint);" + "uvec2 addInvocationsExclusiveScanAMD(uvec2);" + "uvec3 addInvocationsExclusiveScanAMD(uvec3);" + "uvec4 addInvocationsExclusiveScanAMD(uvec4);" + + "double addInvocationsExclusiveScanAMD(double);" + "dvec2 addInvocationsExclusiveScanAMD(dvec2);" + "dvec3 addInvocationsExclusiveScanAMD(dvec3);" + "dvec4 addInvocationsExclusiveScanAMD(dvec4);" + + "int64_t addInvocationsExclusiveScanAMD(int64_t);" + "i64vec2 addInvocationsExclusiveScanAMD(i64vec2);" + "i64vec3 addInvocationsExclusiveScanAMD(i64vec3);" + "i64vec4 addInvocationsExclusiveScanAMD(i64vec4);" + + "uint64_t addInvocationsExclusiveScanAMD(uint64_t);" + "u64vec2 addInvocationsExclusiveScanAMD(u64vec2);" + "u64vec3 addInvocationsExclusiveScanAMD(u64vec3);" + "u64vec4 addInvocationsExclusiveScanAMD(u64vec4);" + + "float16_t addInvocationsExclusiveScanAMD(float16_t);" + "f16vec2 addInvocationsExclusiveScanAMD(f16vec2);" + "f16vec3 addInvocationsExclusiveScanAMD(f16vec3);" + "f16vec4 addInvocationsExclusiveScanAMD(f16vec4);" + + "int16_t addInvocationsExclusiveScanAMD(int16_t);" + "i16vec2 addInvocationsExclusiveScanAMD(i16vec2);" + "i16vec3 addInvocationsExclusiveScanAMD(i16vec3);" + "i16vec4 addInvocationsExclusiveScanAMD(i16vec4);" + + "uint16_t addInvocationsExclusiveScanAMD(uint16_t);" + "u16vec2 addInvocationsExclusiveScanAMD(u16vec2);" + "u16vec3 addInvocationsExclusiveScanAMD(u16vec3);" + "u16vec4 addInvocationsExclusiveScanAMD(u16vec4);" + + "float minInvocationsNonUniformAMD(float);" + "vec2 minInvocationsNonUniformAMD(vec2);" + "vec3 minInvocationsNonUniformAMD(vec3);" + "vec4 minInvocationsNonUniformAMD(vec4);" + + "int minInvocationsNonUniformAMD(int);" + "ivec2 minInvocationsNonUniformAMD(ivec2);" + "ivec3 minInvocationsNonUniformAMD(ivec3);" + "ivec4 minInvocationsNonUniformAMD(ivec4);" + + "uint minInvocationsNonUniformAMD(uint);" + "uvec2 minInvocationsNonUniformAMD(uvec2);" + "uvec3 minInvocationsNonUniformAMD(uvec3);" + "uvec4 minInvocationsNonUniformAMD(uvec4);" + + "double minInvocationsNonUniformAMD(double);" + "dvec2 minInvocationsNonUniformAMD(dvec2);" + "dvec3 minInvocationsNonUniformAMD(dvec3);" + "dvec4 minInvocationsNonUniformAMD(dvec4);" + + "int64_t minInvocationsNonUniformAMD(int64_t);" + "i64vec2 minInvocationsNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsNonUniformAMD(u64vec4);" + + "float16_t minInvocationsNonUniformAMD(float16_t);" + "f16vec2 minInvocationsNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsNonUniformAMD(f16vec4);" + + "int16_t minInvocationsNonUniformAMD(int16_t);" + "i16vec2 minInvocationsNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsNonUniformAMD(u16vec4);" + + "float minInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 minInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 minInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 minInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int minInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 minInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 minInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 minInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint minInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 minInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 minInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 minInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double minInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 minInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 minInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 minInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t minInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 minInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t minInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 minInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t minInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 minInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float minInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 minInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 minInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 minInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int minInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 minInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 minInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 minInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint minInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 minInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 minInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 minInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double minInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 minInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 minInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 minInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t minInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 minInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 minInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 minInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t minInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 minInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 minInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 minInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t minInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 minInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 minInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 minInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t minInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 minInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 minInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 minInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t minInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 minInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 minInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 minInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float maxInvocationsNonUniformAMD(float);" + "vec2 maxInvocationsNonUniformAMD(vec2);" + "vec3 maxInvocationsNonUniformAMD(vec3);" + "vec4 maxInvocationsNonUniformAMD(vec4);" + + "int maxInvocationsNonUniformAMD(int);" + "ivec2 maxInvocationsNonUniformAMD(ivec2);" + "ivec3 maxInvocationsNonUniformAMD(ivec3);" + "ivec4 maxInvocationsNonUniformAMD(ivec4);" + + "uint maxInvocationsNonUniformAMD(uint);" + "uvec2 maxInvocationsNonUniformAMD(uvec2);" + "uvec3 maxInvocationsNonUniformAMD(uvec3);" + "uvec4 maxInvocationsNonUniformAMD(uvec4);" + + "double maxInvocationsNonUniformAMD(double);" + "dvec2 maxInvocationsNonUniformAMD(dvec2);" + "dvec3 maxInvocationsNonUniformAMD(dvec3);" + "dvec4 maxInvocationsNonUniformAMD(dvec4);" + + "int64_t maxInvocationsNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsNonUniformAMD(u16vec4);" + + "float maxInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 maxInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 maxInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 maxInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int maxInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 maxInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 maxInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 maxInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint maxInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 maxInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 maxInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 maxInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double maxInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 maxInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 maxInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 maxInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t maxInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float maxInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 maxInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 maxInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 maxInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int maxInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 maxInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 maxInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 maxInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint maxInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 maxInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 maxInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 maxInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double maxInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 maxInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 maxInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 maxInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t maxInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 maxInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 maxInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 maxInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t maxInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 maxInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 maxInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 maxInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t maxInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 maxInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 maxInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 maxInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t maxInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 maxInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 maxInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 maxInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t maxInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 maxInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 maxInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 maxInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float addInvocationsNonUniformAMD(float);" + "vec2 addInvocationsNonUniformAMD(vec2);" + "vec3 addInvocationsNonUniformAMD(vec3);" + "vec4 addInvocationsNonUniformAMD(vec4);" + + "int addInvocationsNonUniformAMD(int);" + "ivec2 addInvocationsNonUniformAMD(ivec2);" + "ivec3 addInvocationsNonUniformAMD(ivec3);" + "ivec4 addInvocationsNonUniformAMD(ivec4);" + + "uint addInvocationsNonUniformAMD(uint);" + "uvec2 addInvocationsNonUniformAMD(uvec2);" + "uvec3 addInvocationsNonUniformAMD(uvec3);" + "uvec4 addInvocationsNonUniformAMD(uvec4);" + + "double addInvocationsNonUniformAMD(double);" + "dvec2 addInvocationsNonUniformAMD(dvec2);" + "dvec3 addInvocationsNonUniformAMD(dvec3);" + "dvec4 addInvocationsNonUniformAMD(dvec4);" + + "int64_t addInvocationsNonUniformAMD(int64_t);" + "i64vec2 addInvocationsNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsNonUniformAMD(u64vec4);" + + "float16_t addInvocationsNonUniformAMD(float16_t);" + "f16vec2 addInvocationsNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsNonUniformAMD(f16vec4);" + + "int16_t addInvocationsNonUniformAMD(int16_t);" + "i16vec2 addInvocationsNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsNonUniformAMD(u16vec4);" + + "float addInvocationsInclusiveScanNonUniformAMD(float);" + "vec2 addInvocationsInclusiveScanNonUniformAMD(vec2);" + "vec3 addInvocationsInclusiveScanNonUniformAMD(vec3);" + "vec4 addInvocationsInclusiveScanNonUniformAMD(vec4);" + + "int addInvocationsInclusiveScanNonUniformAMD(int);" + "ivec2 addInvocationsInclusiveScanNonUniformAMD(ivec2);" + "ivec3 addInvocationsInclusiveScanNonUniformAMD(ivec3);" + "ivec4 addInvocationsInclusiveScanNonUniformAMD(ivec4);" + + "uint addInvocationsInclusiveScanNonUniformAMD(uint);" + "uvec2 addInvocationsInclusiveScanNonUniformAMD(uvec2);" + "uvec3 addInvocationsInclusiveScanNonUniformAMD(uvec3);" + "uvec4 addInvocationsInclusiveScanNonUniformAMD(uvec4);" + + "double addInvocationsInclusiveScanNonUniformAMD(double);" + "dvec2 addInvocationsInclusiveScanNonUniformAMD(dvec2);" + "dvec3 addInvocationsInclusiveScanNonUniformAMD(dvec3);" + "dvec4 addInvocationsInclusiveScanNonUniformAMD(dvec4);" + + "int64_t addInvocationsInclusiveScanNonUniformAMD(int64_t);" + "i64vec2 addInvocationsInclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsInclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsInclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsInclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsInclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsInclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsInclusiveScanNonUniformAMD(u64vec4);" + + "float16_t addInvocationsInclusiveScanNonUniformAMD(float16_t);" + "f16vec2 addInvocationsInclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsInclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsInclusiveScanNonUniformAMD(f16vec4);" + + "int16_t addInvocationsInclusiveScanNonUniformAMD(int16_t);" + "i16vec2 addInvocationsInclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsInclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsInclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsInclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsInclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsInclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsInclusiveScanNonUniformAMD(u16vec4);" + + "float addInvocationsExclusiveScanNonUniformAMD(float);" + "vec2 addInvocationsExclusiveScanNonUniformAMD(vec2);" + "vec3 addInvocationsExclusiveScanNonUniformAMD(vec3);" + "vec4 addInvocationsExclusiveScanNonUniformAMD(vec4);" + + "int addInvocationsExclusiveScanNonUniformAMD(int);" + "ivec2 addInvocationsExclusiveScanNonUniformAMD(ivec2);" + "ivec3 addInvocationsExclusiveScanNonUniformAMD(ivec3);" + "ivec4 addInvocationsExclusiveScanNonUniformAMD(ivec4);" + + "uint addInvocationsExclusiveScanNonUniformAMD(uint);" + "uvec2 addInvocationsExclusiveScanNonUniformAMD(uvec2);" + "uvec3 addInvocationsExclusiveScanNonUniformAMD(uvec3);" + "uvec4 addInvocationsExclusiveScanNonUniformAMD(uvec4);" + + "double addInvocationsExclusiveScanNonUniformAMD(double);" + "dvec2 addInvocationsExclusiveScanNonUniformAMD(dvec2);" + "dvec3 addInvocationsExclusiveScanNonUniformAMD(dvec3);" + "dvec4 addInvocationsExclusiveScanNonUniformAMD(dvec4);" + + "int64_t addInvocationsExclusiveScanNonUniformAMD(int64_t);" + "i64vec2 addInvocationsExclusiveScanNonUniformAMD(i64vec2);" + "i64vec3 addInvocationsExclusiveScanNonUniformAMD(i64vec3);" + "i64vec4 addInvocationsExclusiveScanNonUniformAMD(i64vec4);" + + "uint64_t addInvocationsExclusiveScanNonUniformAMD(uint64_t);" + "u64vec2 addInvocationsExclusiveScanNonUniformAMD(u64vec2);" + "u64vec3 addInvocationsExclusiveScanNonUniformAMD(u64vec3);" + "u64vec4 addInvocationsExclusiveScanNonUniformAMD(u64vec4);" + + "float16_t addInvocationsExclusiveScanNonUniformAMD(float16_t);" + "f16vec2 addInvocationsExclusiveScanNonUniformAMD(f16vec2);" + "f16vec3 addInvocationsExclusiveScanNonUniformAMD(f16vec3);" + "f16vec4 addInvocationsExclusiveScanNonUniformAMD(f16vec4);" + + "int16_t addInvocationsExclusiveScanNonUniformAMD(int16_t);" + "i16vec2 addInvocationsExclusiveScanNonUniformAMD(i16vec2);" + "i16vec3 addInvocationsExclusiveScanNonUniformAMD(i16vec3);" + "i16vec4 addInvocationsExclusiveScanNonUniformAMD(i16vec4);" + + "uint16_t addInvocationsExclusiveScanNonUniformAMD(uint16_t);" + "u16vec2 addInvocationsExclusiveScanNonUniformAMD(u16vec2);" + "u16vec3 addInvocationsExclusiveScanNonUniformAMD(u16vec3);" + "u16vec4 addInvocationsExclusiveScanNonUniformAMD(u16vec4);" + + "float swizzleInvocationsAMD(float, uvec4);" + "vec2 swizzleInvocationsAMD(vec2, uvec4);" + "vec3 swizzleInvocationsAMD(vec3, uvec4);" + "vec4 swizzleInvocationsAMD(vec4, uvec4);" + + "int swizzleInvocationsAMD(int, uvec4);" + "ivec2 swizzleInvocationsAMD(ivec2, uvec4);" + "ivec3 swizzleInvocationsAMD(ivec3, uvec4);" + "ivec4 swizzleInvocationsAMD(ivec4, uvec4);" + + "uint swizzleInvocationsAMD(uint, uvec4);" + "uvec2 swizzleInvocationsAMD(uvec2, uvec4);" + "uvec3 swizzleInvocationsAMD(uvec3, uvec4);" + "uvec4 swizzleInvocationsAMD(uvec4, uvec4);" + + "float swizzleInvocationsMaskedAMD(float, uvec3);" + "vec2 swizzleInvocationsMaskedAMD(vec2, uvec3);" + "vec3 swizzleInvocationsMaskedAMD(vec3, uvec3);" + "vec4 swizzleInvocationsMaskedAMD(vec4, uvec3);" + + "int swizzleInvocationsMaskedAMD(int, uvec3);" + "ivec2 swizzleInvocationsMaskedAMD(ivec2, uvec3);" + "ivec3 swizzleInvocationsMaskedAMD(ivec3, uvec3);" + "ivec4 swizzleInvocationsMaskedAMD(ivec4, uvec3);" + + "uint swizzleInvocationsMaskedAMD(uint, uvec3);" + "uvec2 swizzleInvocationsMaskedAMD(uvec2, uvec3);" + "uvec3 swizzleInvocationsMaskedAMD(uvec3, uvec3);" + "uvec4 swizzleInvocationsMaskedAMD(uvec4, uvec3);" + + "float writeInvocationAMD(float, float, uint);" + "vec2 writeInvocationAMD(vec2, vec2, uint);" + "vec3 writeInvocationAMD(vec3, vec3, uint);" + "vec4 writeInvocationAMD(vec4, vec4, uint);" + + "int writeInvocationAMD(int, int, uint);" + "ivec2 writeInvocationAMD(ivec2, ivec2, uint);" + "ivec3 writeInvocationAMD(ivec3, ivec3, uint);" + "ivec4 writeInvocationAMD(ivec4, ivec4, uint);" + + "uint writeInvocationAMD(uint, uint, uint);" + "uvec2 writeInvocationAMD(uvec2, uvec2, uint);" + "uvec3 writeInvocationAMD(uvec3, uvec3, uint);" + "uvec4 writeInvocationAMD(uvec4, uvec4, uint);" + + "uint mbcntAMD(uint64_t);" + + "\n"); + } + + // GL_AMD_gcn_shader + if (profile != EEsProfile && version >= 440) { + commonBuiltins.append( + "float cubeFaceIndexAMD(vec3);" + "vec2 cubeFaceCoordAMD(vec3);" + "uint64_t timeAMD();" + + "in int gl_SIMDGroupSizeAMD;" + "\n"); + } + + // GL_AMD_shader_fragment_mask + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uint fragmentMaskFetchAMD(sampler2DMS, ivec2);" + "uint fragmentMaskFetchAMD(isampler2DMS, ivec2);" + "uint fragmentMaskFetchAMD(usampler2DMS, ivec2);" + + "uint fragmentMaskFetchAMD(sampler2DMSArray, ivec3);" + "uint fragmentMaskFetchAMD(isampler2DMSArray, ivec3);" + "uint fragmentMaskFetchAMD(usampler2DMSArray, ivec3);" + + "vec4 fragmentFetchAMD(sampler2DMS, ivec2, uint);" + "ivec4 fragmentFetchAMD(isampler2DMS, ivec2, uint);" + "uvec4 fragmentFetchAMD(usampler2DMS, ivec2, uint);" + + "vec4 fragmentFetchAMD(sampler2DMSArray, ivec3, uint);" + "ivec4 fragmentFetchAMD(isampler2DMSArray, ivec3, uint);" + "uvec4 fragmentFetchAMD(usampler2DMSArray, ivec3, uint);" + + "\n"); + } + + if ((profile != EEsProfile && version >= 130) || + (profile == EEsProfile && version >= 300)) { + commonBuiltins.append( + "uint countLeadingZeros(uint);" + "uvec2 countLeadingZeros(uvec2);" + "uvec3 countLeadingZeros(uvec3);" + "uvec4 countLeadingZeros(uvec4);" + + "uint countTrailingZeros(uint);" + "uvec2 countTrailingZeros(uvec2);" + "uvec3 countTrailingZeros(uvec3);" + "uvec4 countTrailingZeros(uvec4);" + + "uint absoluteDifference(int, int);" + "uvec2 absoluteDifference(ivec2, ivec2);" + "uvec3 absoluteDifference(ivec3, ivec3);" + "uvec4 absoluteDifference(ivec4, ivec4);" + + "uint16_t absoluteDifference(int16_t, int16_t);" + "u16vec2 absoluteDifference(i16vec2, i16vec2);" + "u16vec3 absoluteDifference(i16vec3, i16vec3);" + "u16vec4 absoluteDifference(i16vec4, i16vec4);" + + "uint64_t absoluteDifference(int64_t, int64_t);" + "u64vec2 absoluteDifference(i64vec2, i64vec2);" + "u64vec3 absoluteDifference(i64vec3, i64vec3);" + "u64vec4 absoluteDifference(i64vec4, i64vec4);" + + "uint absoluteDifference(uint, uint);" + "uvec2 absoluteDifference(uvec2, uvec2);" + "uvec3 absoluteDifference(uvec3, uvec3);" + "uvec4 absoluteDifference(uvec4, uvec4);" + + "uint16_t absoluteDifference(uint16_t, uint16_t);" + "u16vec2 absoluteDifference(u16vec2, u16vec2);" + "u16vec3 absoluteDifference(u16vec3, u16vec3);" + "u16vec4 absoluteDifference(u16vec4, u16vec4);" + + "uint64_t absoluteDifference(uint64_t, uint64_t);" + "u64vec2 absoluteDifference(u64vec2, u64vec2);" + "u64vec3 absoluteDifference(u64vec3, u64vec3);" + "u64vec4 absoluteDifference(u64vec4, u64vec4);" + + "int addSaturate(int, int);" + "ivec2 addSaturate(ivec2, ivec2);" + "ivec3 addSaturate(ivec3, ivec3);" + "ivec4 addSaturate(ivec4, ivec4);" + + "int16_t addSaturate(int16_t, int16_t);" + "i16vec2 addSaturate(i16vec2, i16vec2);" + "i16vec3 addSaturate(i16vec3, i16vec3);" + "i16vec4 addSaturate(i16vec4, i16vec4);" + + "int64_t addSaturate(int64_t, int64_t);" + "i64vec2 addSaturate(i64vec2, i64vec2);" + "i64vec3 addSaturate(i64vec3, i64vec3);" + "i64vec4 addSaturate(i64vec4, i64vec4);" + + "uint addSaturate(uint, uint);" + "uvec2 addSaturate(uvec2, uvec2);" + "uvec3 addSaturate(uvec3, uvec3);" + "uvec4 addSaturate(uvec4, uvec4);" + + "uint16_t addSaturate(uint16_t, uint16_t);" + "u16vec2 addSaturate(u16vec2, u16vec2);" + "u16vec3 addSaturate(u16vec3, u16vec3);" + "u16vec4 addSaturate(u16vec4, u16vec4);" + + "uint64_t addSaturate(uint64_t, uint64_t);" + "u64vec2 addSaturate(u64vec2, u64vec2);" + "u64vec3 addSaturate(u64vec3, u64vec3);" + "u64vec4 addSaturate(u64vec4, u64vec4);" + + "int subtractSaturate(int, int);" + "ivec2 subtractSaturate(ivec2, ivec2);" + "ivec3 subtractSaturate(ivec3, ivec3);" + "ivec4 subtractSaturate(ivec4, ivec4);" + + "int16_t subtractSaturate(int16_t, int16_t);" + "i16vec2 subtractSaturate(i16vec2, i16vec2);" + "i16vec3 subtractSaturate(i16vec3, i16vec3);" + "i16vec4 subtractSaturate(i16vec4, i16vec4);" + + "int64_t subtractSaturate(int64_t, int64_t);" + "i64vec2 subtractSaturate(i64vec2, i64vec2);" + "i64vec3 subtractSaturate(i64vec3, i64vec3);" + "i64vec4 subtractSaturate(i64vec4, i64vec4);" + + "uint subtractSaturate(uint, uint);" + "uvec2 subtractSaturate(uvec2, uvec2);" + "uvec3 subtractSaturate(uvec3, uvec3);" + "uvec4 subtractSaturate(uvec4, uvec4);" + + "uint16_t subtractSaturate(uint16_t, uint16_t);" + "u16vec2 subtractSaturate(u16vec2, u16vec2);" + "u16vec3 subtractSaturate(u16vec3, u16vec3);" + "u16vec4 subtractSaturate(u16vec4, u16vec4);" + + "uint64_t subtractSaturate(uint64_t, uint64_t);" + "u64vec2 subtractSaturate(u64vec2, u64vec2);" + "u64vec3 subtractSaturate(u64vec3, u64vec3);" + "u64vec4 subtractSaturate(u64vec4, u64vec4);" + + "int average(int, int);" + "ivec2 average(ivec2, ivec2);" + "ivec3 average(ivec3, ivec3);" + "ivec4 average(ivec4, ivec4);" + + "int16_t average(int16_t, int16_t);" + "i16vec2 average(i16vec2, i16vec2);" + "i16vec3 average(i16vec3, i16vec3);" + "i16vec4 average(i16vec4, i16vec4);" + + "int64_t average(int64_t, int64_t);" + "i64vec2 average(i64vec2, i64vec2);" + "i64vec3 average(i64vec3, i64vec3);" + "i64vec4 average(i64vec4, i64vec4);" + + "uint average(uint, uint);" + "uvec2 average(uvec2, uvec2);" + "uvec3 average(uvec3, uvec3);" + "uvec4 average(uvec4, uvec4);" + + "uint16_t average(uint16_t, uint16_t);" + "u16vec2 average(u16vec2, u16vec2);" + "u16vec3 average(u16vec3, u16vec3);" + "u16vec4 average(u16vec4, u16vec4);" + + "uint64_t average(uint64_t, uint64_t);" + "u64vec2 average(u64vec2, u64vec2);" + "u64vec3 average(u64vec3, u64vec3);" + "u64vec4 average(u64vec4, u64vec4);" + + "int averageRounded(int, int);" + "ivec2 averageRounded(ivec2, ivec2);" + "ivec3 averageRounded(ivec3, ivec3);" + "ivec4 averageRounded(ivec4, ivec4);" + + "int16_t averageRounded(int16_t, int16_t);" + "i16vec2 averageRounded(i16vec2, i16vec2);" + "i16vec3 averageRounded(i16vec3, i16vec3);" + "i16vec4 averageRounded(i16vec4, i16vec4);" + + "int64_t averageRounded(int64_t, int64_t);" + "i64vec2 averageRounded(i64vec2, i64vec2);" + "i64vec3 averageRounded(i64vec3, i64vec3);" + "i64vec4 averageRounded(i64vec4, i64vec4);" + + "uint averageRounded(uint, uint);" + "uvec2 averageRounded(uvec2, uvec2);" + "uvec3 averageRounded(uvec3, uvec3);" + "uvec4 averageRounded(uvec4, uvec4);" + + "uint16_t averageRounded(uint16_t, uint16_t);" + "u16vec2 averageRounded(u16vec2, u16vec2);" + "u16vec3 averageRounded(u16vec3, u16vec3);" + "u16vec4 averageRounded(u16vec4, u16vec4);" + + "uint64_t averageRounded(uint64_t, uint64_t);" + "u64vec2 averageRounded(u64vec2, u64vec2);" + "u64vec3 averageRounded(u64vec3, u64vec3);" + "u64vec4 averageRounded(u64vec4, u64vec4);" + + "int multiply32x16(int, int);" + "ivec2 multiply32x16(ivec2, ivec2);" + "ivec3 multiply32x16(ivec3, ivec3);" + "ivec4 multiply32x16(ivec4, ivec4);" + + "uint multiply32x16(uint, uint);" + "uvec2 multiply32x16(uvec2, uvec2);" + "uvec3 multiply32x16(uvec3, uvec3);" + "uvec4 multiply32x16(uvec4, uvec4);" + "\n"); + } + + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + commonBuiltins.append( + "struct gl_TextureFootprint2DNV {" + "uvec2 anchor;" + "uvec2 offset;" + "uvec2 mask;" + "uint lod;" + "uint granularity;" + "};" + + "struct gl_TextureFootprint3DNV {" + "uvec3 anchor;" + "uvec3 offset;" + "uvec2 mask;" + "uint lod;" + "uint granularity;" + "};" + "bool textureFootprintNV(sampler2D, vec2, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintNV(sampler3D, vec3, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintNV(sampler2D, vec2, int, bool, out gl_TextureFootprint2DNV, float);" + "bool textureFootprintNV(sampler3D, vec3, int, bool, out gl_TextureFootprint3DNV, float);" + "bool textureFootprintClampNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintClampNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintClampNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV, float);" + "bool textureFootprintClampNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV, float);" + "bool textureFootprintLodNV(sampler2D, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintLodNV(sampler3D, vec3, float, int, bool, out gl_TextureFootprint3DNV);" + "bool textureFootprintGradNV(sampler2D, vec2, vec2, vec2, int, bool, out gl_TextureFootprint2DNV);" + "bool textureFootprintGradClampNV(sampler2D, vec2, vec2, vec2, float, int, bool, out gl_TextureFootprint2DNV);" + "\n"); + } +#endif // !GLSLANG_ANGLE + + if ((profile == EEsProfile && version >= 300 && version < 310) || + (profile != EEsProfile && version >= 150 && version < 450)) { // GL_EXT_shader_integer_mix + commonBuiltins.append("int mix(int, int, bool);" + "ivec2 mix(ivec2, ivec2, bvec2);" + "ivec3 mix(ivec3, ivec3, bvec3);" + "ivec4 mix(ivec4, ivec4, bvec4);" + "uint mix(uint, uint, bool );" + "uvec2 mix(uvec2, uvec2, bvec2);" + "uvec3 mix(uvec3, uvec3, bvec3);" + "uvec4 mix(uvec4, uvec4, bvec4);" + "bool mix(bool, bool, bool );" + "bvec2 mix(bvec2, bvec2, bvec2);" + "bvec3 mix(bvec3, bvec3, bvec3);" + "bvec4 mix(bvec4, bvec4, bvec4);" + + "\n"); + } + +#ifndef GLSLANG_ANGLE + // GL_AMD_gpu_shader_half_float/Explicit types + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "float16_t radians(float16_t);" + "f16vec2 radians(f16vec2);" + "f16vec3 radians(f16vec3);" + "f16vec4 radians(f16vec4);" + + "float16_t degrees(float16_t);" + "f16vec2 degrees(f16vec2);" + "f16vec3 degrees(f16vec3);" + "f16vec4 degrees(f16vec4);" + + "float16_t sin(float16_t);" + "f16vec2 sin(f16vec2);" + "f16vec3 sin(f16vec3);" + "f16vec4 sin(f16vec4);" + + "float16_t cos(float16_t);" + "f16vec2 cos(f16vec2);" + "f16vec3 cos(f16vec3);" + "f16vec4 cos(f16vec4);" + + "float16_t tan(float16_t);" + "f16vec2 tan(f16vec2);" + "f16vec3 tan(f16vec3);" + "f16vec4 tan(f16vec4);" + + "float16_t asin(float16_t);" + "f16vec2 asin(f16vec2);" + "f16vec3 asin(f16vec3);" + "f16vec4 asin(f16vec4);" + + "float16_t acos(float16_t);" + "f16vec2 acos(f16vec2);" + "f16vec3 acos(f16vec3);" + "f16vec4 acos(f16vec4);" + + "float16_t atan(float16_t, float16_t);" + "f16vec2 atan(f16vec2, f16vec2);" + "f16vec3 atan(f16vec3, f16vec3);" + "f16vec4 atan(f16vec4, f16vec4);" + + "float16_t atan(float16_t);" + "f16vec2 atan(f16vec2);" + "f16vec3 atan(f16vec3);" + "f16vec4 atan(f16vec4);" + + "float16_t sinh(float16_t);" + "f16vec2 sinh(f16vec2);" + "f16vec3 sinh(f16vec3);" + "f16vec4 sinh(f16vec4);" + + "float16_t cosh(float16_t);" + "f16vec2 cosh(f16vec2);" + "f16vec3 cosh(f16vec3);" + "f16vec4 cosh(f16vec4);" + + "float16_t tanh(float16_t);" + "f16vec2 tanh(f16vec2);" + "f16vec3 tanh(f16vec3);" + "f16vec4 tanh(f16vec4);" + + "float16_t asinh(float16_t);" + "f16vec2 asinh(f16vec2);" + "f16vec3 asinh(f16vec3);" + "f16vec4 asinh(f16vec4);" + + "float16_t acosh(float16_t);" + "f16vec2 acosh(f16vec2);" + "f16vec3 acosh(f16vec3);" + "f16vec4 acosh(f16vec4);" + + "float16_t atanh(float16_t);" + "f16vec2 atanh(f16vec2);" + "f16vec3 atanh(f16vec3);" + "f16vec4 atanh(f16vec4);" + + "float16_t pow(float16_t, float16_t);" + "f16vec2 pow(f16vec2, f16vec2);" + "f16vec3 pow(f16vec3, f16vec3);" + "f16vec4 pow(f16vec4, f16vec4);" + + "float16_t exp(float16_t);" + "f16vec2 exp(f16vec2);" + "f16vec3 exp(f16vec3);" + "f16vec4 exp(f16vec4);" + + "float16_t log(float16_t);" + "f16vec2 log(f16vec2);" + "f16vec3 log(f16vec3);" + "f16vec4 log(f16vec4);" + + "float16_t exp2(float16_t);" + "f16vec2 exp2(f16vec2);" + "f16vec3 exp2(f16vec3);" + "f16vec4 exp2(f16vec4);" + + "float16_t log2(float16_t);" + "f16vec2 log2(f16vec2);" + "f16vec3 log2(f16vec3);" + "f16vec4 log2(f16vec4);" + + "float16_t sqrt(float16_t);" + "f16vec2 sqrt(f16vec2);" + "f16vec3 sqrt(f16vec3);" + "f16vec4 sqrt(f16vec4);" + + "float16_t inversesqrt(float16_t);" + "f16vec2 inversesqrt(f16vec2);" + "f16vec3 inversesqrt(f16vec3);" + "f16vec4 inversesqrt(f16vec4);" + + "float16_t abs(float16_t);" + "f16vec2 abs(f16vec2);" + "f16vec3 abs(f16vec3);" + "f16vec4 abs(f16vec4);" + + "float16_t sign(float16_t);" + "f16vec2 sign(f16vec2);" + "f16vec3 sign(f16vec3);" + "f16vec4 sign(f16vec4);" + + "float16_t floor(float16_t);" + "f16vec2 floor(f16vec2);" + "f16vec3 floor(f16vec3);" + "f16vec4 floor(f16vec4);" + + "float16_t trunc(float16_t);" + "f16vec2 trunc(f16vec2);" + "f16vec3 trunc(f16vec3);" + "f16vec4 trunc(f16vec4);" + + "float16_t round(float16_t);" + "f16vec2 round(f16vec2);" + "f16vec3 round(f16vec3);" + "f16vec4 round(f16vec4);" + + "float16_t roundEven(float16_t);" + "f16vec2 roundEven(f16vec2);" + "f16vec3 roundEven(f16vec3);" + "f16vec4 roundEven(f16vec4);" + + "float16_t ceil(float16_t);" + "f16vec2 ceil(f16vec2);" + "f16vec3 ceil(f16vec3);" + "f16vec4 ceil(f16vec4);" + + "float16_t fract(float16_t);" + "f16vec2 fract(f16vec2);" + "f16vec3 fract(f16vec3);" + "f16vec4 fract(f16vec4);" + + "float16_t mod(float16_t, float16_t);" + "f16vec2 mod(f16vec2, float16_t);" + "f16vec3 mod(f16vec3, float16_t);" + "f16vec4 mod(f16vec4, float16_t);" + "f16vec2 mod(f16vec2, f16vec2);" + "f16vec3 mod(f16vec3, f16vec3);" + "f16vec4 mod(f16vec4, f16vec4);" + + "float16_t modf(float16_t, out float16_t);" + "f16vec2 modf(f16vec2, out f16vec2);" + "f16vec3 modf(f16vec3, out f16vec3);" + "f16vec4 modf(f16vec4, out f16vec4);" + + "float16_t min(float16_t, float16_t);" + "f16vec2 min(f16vec2, float16_t);" + "f16vec3 min(f16vec3, float16_t);" + "f16vec4 min(f16vec4, float16_t);" + "f16vec2 min(f16vec2, f16vec2);" + "f16vec3 min(f16vec3, f16vec3);" + "f16vec4 min(f16vec4, f16vec4);" + + "float16_t max(float16_t, float16_t);" + "f16vec2 max(f16vec2, float16_t);" + "f16vec3 max(f16vec3, float16_t);" + "f16vec4 max(f16vec4, float16_t);" + "f16vec2 max(f16vec2, f16vec2);" + "f16vec3 max(f16vec3, f16vec3);" + "f16vec4 max(f16vec4, f16vec4);" + + "float16_t clamp(float16_t, float16_t, float16_t);" + "f16vec2 clamp(f16vec2, float16_t, float16_t);" + "f16vec3 clamp(f16vec3, float16_t, float16_t);" + "f16vec4 clamp(f16vec4, float16_t, float16_t);" + "f16vec2 clamp(f16vec2, f16vec2, f16vec2);" + "f16vec3 clamp(f16vec3, f16vec3, f16vec3);" + "f16vec4 clamp(f16vec4, f16vec4, f16vec4);" + + "float16_t mix(float16_t, float16_t, float16_t);" + "f16vec2 mix(f16vec2, f16vec2, float16_t);" + "f16vec3 mix(f16vec3, f16vec3, float16_t);" + "f16vec4 mix(f16vec4, f16vec4, float16_t);" + "f16vec2 mix(f16vec2, f16vec2, f16vec2);" + "f16vec3 mix(f16vec3, f16vec3, f16vec3);" + "f16vec4 mix(f16vec4, f16vec4, f16vec4);" + "float16_t mix(float16_t, float16_t, bool);" + "f16vec2 mix(f16vec2, f16vec2, bvec2);" + "f16vec3 mix(f16vec3, f16vec3, bvec3);" + "f16vec4 mix(f16vec4, f16vec4, bvec4);" + + "float16_t step(float16_t, float16_t);" + "f16vec2 step(f16vec2, f16vec2);" + "f16vec3 step(f16vec3, f16vec3);" + "f16vec4 step(f16vec4, f16vec4);" + "f16vec2 step(float16_t, f16vec2);" + "f16vec3 step(float16_t, f16vec3);" + "f16vec4 step(float16_t, f16vec4);" + + "float16_t smoothstep(float16_t, float16_t, float16_t);" + "f16vec2 smoothstep(f16vec2, f16vec2, f16vec2);" + "f16vec3 smoothstep(f16vec3, f16vec3, f16vec3);" + "f16vec4 smoothstep(f16vec4, f16vec4, f16vec4);" + "f16vec2 smoothstep(float16_t, float16_t, f16vec2);" + "f16vec3 smoothstep(float16_t, float16_t, f16vec3);" + "f16vec4 smoothstep(float16_t, float16_t, f16vec4);" + + "bool isnan(float16_t);" + "bvec2 isnan(f16vec2);" + "bvec3 isnan(f16vec3);" + "bvec4 isnan(f16vec4);" + + "bool isinf(float16_t);" + "bvec2 isinf(f16vec2);" + "bvec3 isinf(f16vec3);" + "bvec4 isinf(f16vec4);" + + "float16_t fma(float16_t, float16_t, float16_t);" + "f16vec2 fma(f16vec2, f16vec2, f16vec2);" + "f16vec3 fma(f16vec3, f16vec3, f16vec3);" + "f16vec4 fma(f16vec4, f16vec4, f16vec4);" + + "float16_t frexp(float16_t, out int);" + "f16vec2 frexp(f16vec2, out ivec2);" + "f16vec3 frexp(f16vec3, out ivec3);" + "f16vec4 frexp(f16vec4, out ivec4);" + + "float16_t ldexp(float16_t, in int);" + "f16vec2 ldexp(f16vec2, in ivec2);" + "f16vec3 ldexp(f16vec3, in ivec3);" + "f16vec4 ldexp(f16vec4, in ivec4);" + + "uint packFloat2x16(f16vec2);" + "f16vec2 unpackFloat2x16(uint);" + + "float16_t length(float16_t);" + "float16_t length(f16vec2);" + "float16_t length(f16vec3);" + "float16_t length(f16vec4);" + + "float16_t distance(float16_t, float16_t);" + "float16_t distance(f16vec2, f16vec2);" + "float16_t distance(f16vec3, f16vec3);" + "float16_t distance(f16vec4, f16vec4);" + + "float16_t dot(float16_t, float16_t);" + "float16_t dot(f16vec2, f16vec2);" + "float16_t dot(f16vec3, f16vec3);" + "float16_t dot(f16vec4, f16vec4);" + + "f16vec3 cross(f16vec3, f16vec3);" + + "float16_t normalize(float16_t);" + "f16vec2 normalize(f16vec2);" + "f16vec3 normalize(f16vec3);" + "f16vec4 normalize(f16vec4);" + + "float16_t faceforward(float16_t, float16_t, float16_t);" + "f16vec2 faceforward(f16vec2, f16vec2, f16vec2);" + "f16vec3 faceforward(f16vec3, f16vec3, f16vec3);" + "f16vec4 faceforward(f16vec4, f16vec4, f16vec4);" + + "float16_t reflect(float16_t, float16_t);" + "f16vec2 reflect(f16vec2, f16vec2);" + "f16vec3 reflect(f16vec3, f16vec3);" + "f16vec4 reflect(f16vec4, f16vec4);" + + "float16_t refract(float16_t, float16_t, float16_t);" + "f16vec2 refract(f16vec2, f16vec2, float16_t);" + "f16vec3 refract(f16vec3, f16vec3, float16_t);" + "f16vec4 refract(f16vec4, f16vec4, float16_t);" + + "f16mat2 matrixCompMult(f16mat2, f16mat2);" + "f16mat3 matrixCompMult(f16mat3, f16mat3);" + "f16mat4 matrixCompMult(f16mat4, f16mat4);" + "f16mat2x3 matrixCompMult(f16mat2x3, f16mat2x3);" + "f16mat2x4 matrixCompMult(f16mat2x4, f16mat2x4);" + "f16mat3x2 matrixCompMult(f16mat3x2, f16mat3x2);" + "f16mat3x4 matrixCompMult(f16mat3x4, f16mat3x4);" + "f16mat4x2 matrixCompMult(f16mat4x2, f16mat4x2);" + "f16mat4x3 matrixCompMult(f16mat4x3, f16mat4x3);" + + "f16mat2 outerProduct(f16vec2, f16vec2);" + "f16mat3 outerProduct(f16vec3, f16vec3);" + "f16mat4 outerProduct(f16vec4, f16vec4);" + "f16mat2x3 outerProduct(f16vec3, f16vec2);" + "f16mat3x2 outerProduct(f16vec2, f16vec3);" + "f16mat2x4 outerProduct(f16vec4, f16vec2);" + "f16mat4x2 outerProduct(f16vec2, f16vec4);" + "f16mat3x4 outerProduct(f16vec4, f16vec3);" + "f16mat4x3 outerProduct(f16vec3, f16vec4);" + + "f16mat2 transpose(f16mat2);" + "f16mat3 transpose(f16mat3);" + "f16mat4 transpose(f16mat4);" + "f16mat2x3 transpose(f16mat3x2);" + "f16mat3x2 transpose(f16mat2x3);" + "f16mat2x4 transpose(f16mat4x2);" + "f16mat4x2 transpose(f16mat2x4);" + "f16mat3x4 transpose(f16mat4x3);" + "f16mat4x3 transpose(f16mat3x4);" + + "float16_t determinant(f16mat2);" + "float16_t determinant(f16mat3);" + "float16_t determinant(f16mat4);" + + "f16mat2 inverse(f16mat2);" + "f16mat3 inverse(f16mat3);" + "f16mat4 inverse(f16mat4);" + + "bvec2 lessThan(f16vec2, f16vec2);" + "bvec3 lessThan(f16vec3, f16vec3);" + "bvec4 lessThan(f16vec4, f16vec4);" + + "bvec2 lessThanEqual(f16vec2, f16vec2);" + "bvec3 lessThanEqual(f16vec3, f16vec3);" + "bvec4 lessThanEqual(f16vec4, f16vec4);" + + "bvec2 greaterThan(f16vec2, f16vec2);" + "bvec3 greaterThan(f16vec3, f16vec3);" + "bvec4 greaterThan(f16vec4, f16vec4);" + + "bvec2 greaterThanEqual(f16vec2, f16vec2);" + "bvec3 greaterThanEqual(f16vec3, f16vec3);" + "bvec4 greaterThanEqual(f16vec4, f16vec4);" + + "bvec2 equal(f16vec2, f16vec2);" + "bvec3 equal(f16vec3, f16vec3);" + "bvec4 equal(f16vec4, f16vec4);" + + "bvec2 notEqual(f16vec2, f16vec2);" + "bvec3 notEqual(f16vec3, f16vec3);" + "bvec4 notEqual(f16vec4, f16vec4);" + + "\n"); + } + + // Explicit types + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "int8_t abs(int8_t);" + "i8vec2 abs(i8vec2);" + "i8vec3 abs(i8vec3);" + "i8vec4 abs(i8vec4);" + + "int8_t sign(int8_t);" + "i8vec2 sign(i8vec2);" + "i8vec3 sign(i8vec3);" + "i8vec4 sign(i8vec4);" + + "int8_t min(int8_t x, int8_t y);" + "i8vec2 min(i8vec2 x, int8_t y);" + "i8vec3 min(i8vec3 x, int8_t y);" + "i8vec4 min(i8vec4 x, int8_t y);" + "i8vec2 min(i8vec2 x, i8vec2 y);" + "i8vec3 min(i8vec3 x, i8vec3 y);" + "i8vec4 min(i8vec4 x, i8vec4 y);" + + "uint8_t min(uint8_t x, uint8_t y);" + "u8vec2 min(u8vec2 x, uint8_t y);" + "u8vec3 min(u8vec3 x, uint8_t y);" + "u8vec4 min(u8vec4 x, uint8_t y);" + "u8vec2 min(u8vec2 x, u8vec2 y);" + "u8vec3 min(u8vec3 x, u8vec3 y);" + "u8vec4 min(u8vec4 x, u8vec4 y);" + + "int8_t max(int8_t x, int8_t y);" + "i8vec2 max(i8vec2 x, int8_t y);" + "i8vec3 max(i8vec3 x, int8_t y);" + "i8vec4 max(i8vec4 x, int8_t y);" + "i8vec2 max(i8vec2 x, i8vec2 y);" + "i8vec3 max(i8vec3 x, i8vec3 y);" + "i8vec4 max(i8vec4 x, i8vec4 y);" + + "uint8_t max(uint8_t x, uint8_t y);" + "u8vec2 max(u8vec2 x, uint8_t y);" + "u8vec3 max(u8vec3 x, uint8_t y);" + "u8vec4 max(u8vec4 x, uint8_t y);" + "u8vec2 max(u8vec2 x, u8vec2 y);" + "u8vec3 max(u8vec3 x, u8vec3 y);" + "u8vec4 max(u8vec4 x, u8vec4 y);" + + "int8_t clamp(int8_t x, int8_t minVal, int8_t maxVal);" + "i8vec2 clamp(i8vec2 x, int8_t minVal, int8_t maxVal);" + "i8vec3 clamp(i8vec3 x, int8_t minVal, int8_t maxVal);" + "i8vec4 clamp(i8vec4 x, int8_t minVal, int8_t maxVal);" + "i8vec2 clamp(i8vec2 x, i8vec2 minVal, i8vec2 maxVal);" + "i8vec3 clamp(i8vec3 x, i8vec3 minVal, i8vec3 maxVal);" + "i8vec4 clamp(i8vec4 x, i8vec4 minVal, i8vec4 maxVal);" + + "uint8_t clamp(uint8_t x, uint8_t minVal, uint8_t maxVal);" + "u8vec2 clamp(u8vec2 x, uint8_t minVal, uint8_t maxVal);" + "u8vec3 clamp(u8vec3 x, uint8_t minVal, uint8_t maxVal);" + "u8vec4 clamp(u8vec4 x, uint8_t minVal, uint8_t maxVal);" + "u8vec2 clamp(u8vec2 x, u8vec2 minVal, u8vec2 maxVal);" + "u8vec3 clamp(u8vec3 x, u8vec3 minVal, u8vec3 maxVal);" + "u8vec4 clamp(u8vec4 x, u8vec4 minVal, u8vec4 maxVal);" + + "int8_t mix(int8_t, int8_t, bool);" + "i8vec2 mix(i8vec2, i8vec2, bvec2);" + "i8vec3 mix(i8vec3, i8vec3, bvec3);" + "i8vec4 mix(i8vec4, i8vec4, bvec4);" + "uint8_t mix(uint8_t, uint8_t, bool);" + "u8vec2 mix(u8vec2, u8vec2, bvec2);" + "u8vec3 mix(u8vec3, u8vec3, bvec3);" + "u8vec4 mix(u8vec4, u8vec4, bvec4);" + + "bvec2 lessThan(i8vec2, i8vec2);" + "bvec3 lessThan(i8vec3, i8vec3);" + "bvec4 lessThan(i8vec4, i8vec4);" + "bvec2 lessThan(u8vec2, u8vec2);" + "bvec3 lessThan(u8vec3, u8vec3);" + "bvec4 lessThan(u8vec4, u8vec4);" + + "bvec2 lessThanEqual(i8vec2, i8vec2);" + "bvec3 lessThanEqual(i8vec3, i8vec3);" + "bvec4 lessThanEqual(i8vec4, i8vec4);" + "bvec2 lessThanEqual(u8vec2, u8vec2);" + "bvec3 lessThanEqual(u8vec3, u8vec3);" + "bvec4 lessThanEqual(u8vec4, u8vec4);" + + "bvec2 greaterThan(i8vec2, i8vec2);" + "bvec3 greaterThan(i8vec3, i8vec3);" + "bvec4 greaterThan(i8vec4, i8vec4);" + "bvec2 greaterThan(u8vec2, u8vec2);" + "bvec3 greaterThan(u8vec3, u8vec3);" + "bvec4 greaterThan(u8vec4, u8vec4);" + + "bvec2 greaterThanEqual(i8vec2, i8vec2);" + "bvec3 greaterThanEqual(i8vec3, i8vec3);" + "bvec4 greaterThanEqual(i8vec4, i8vec4);" + "bvec2 greaterThanEqual(u8vec2, u8vec2);" + "bvec3 greaterThanEqual(u8vec3, u8vec3);" + "bvec4 greaterThanEqual(u8vec4, u8vec4);" + + "bvec2 equal(i8vec2, i8vec2);" + "bvec3 equal(i8vec3, i8vec3);" + "bvec4 equal(i8vec4, i8vec4);" + "bvec2 equal(u8vec2, u8vec2);" + "bvec3 equal(u8vec3, u8vec3);" + "bvec4 equal(u8vec4, u8vec4);" + + "bvec2 notEqual(i8vec2, i8vec2);" + "bvec3 notEqual(i8vec3, i8vec3);" + "bvec4 notEqual(i8vec4, i8vec4);" + "bvec2 notEqual(u8vec2, u8vec2);" + "bvec3 notEqual(u8vec3, u8vec3);" + "bvec4 notEqual(u8vec4, u8vec4);" + + " int8_t bitfieldExtract( int8_t, int8_t, int8_t);" + "i8vec2 bitfieldExtract(i8vec2, int8_t, int8_t);" + "i8vec3 bitfieldExtract(i8vec3, int8_t, int8_t);" + "i8vec4 bitfieldExtract(i8vec4, int8_t, int8_t);" + + " uint8_t bitfieldExtract( uint8_t, int8_t, int8_t);" + "u8vec2 bitfieldExtract(u8vec2, int8_t, int8_t);" + "u8vec3 bitfieldExtract(u8vec3, int8_t, int8_t);" + "u8vec4 bitfieldExtract(u8vec4, int8_t, int8_t);" + + " int8_t bitfieldInsert( int8_t base, int8_t, int8_t, int8_t);" + "i8vec2 bitfieldInsert(i8vec2 base, i8vec2, int8_t, int8_t);" + "i8vec3 bitfieldInsert(i8vec3 base, i8vec3, int8_t, int8_t);" + "i8vec4 bitfieldInsert(i8vec4 base, i8vec4, int8_t, int8_t);" + + " uint8_t bitfieldInsert( uint8_t base, uint8_t, int8_t, int8_t);" + "u8vec2 bitfieldInsert(u8vec2 base, u8vec2, int8_t, int8_t);" + "u8vec3 bitfieldInsert(u8vec3 base, u8vec3, int8_t, int8_t);" + "u8vec4 bitfieldInsert(u8vec4 base, u8vec4, int8_t, int8_t);" + + " int8_t bitCount( int8_t);" + "i8vec2 bitCount(i8vec2);" + "i8vec3 bitCount(i8vec3);" + "i8vec4 bitCount(i8vec4);" + + " int8_t bitCount( uint8_t);" + "i8vec2 bitCount(u8vec2);" + "i8vec3 bitCount(u8vec3);" + "i8vec4 bitCount(u8vec4);" + + " int8_t findLSB( int8_t);" + "i8vec2 findLSB(i8vec2);" + "i8vec3 findLSB(i8vec3);" + "i8vec4 findLSB(i8vec4);" + + " int8_t findLSB( uint8_t);" + "i8vec2 findLSB(u8vec2);" + "i8vec3 findLSB(u8vec3);" + "i8vec4 findLSB(u8vec4);" + + " int8_t findMSB( int8_t);" + "i8vec2 findMSB(i8vec2);" + "i8vec3 findMSB(i8vec3);" + "i8vec4 findMSB(i8vec4);" + + " int8_t findMSB( uint8_t);" + "i8vec2 findMSB(u8vec2);" + "i8vec3 findMSB(u8vec3);" + "i8vec4 findMSB(u8vec4);" + + "int16_t abs(int16_t);" + "i16vec2 abs(i16vec2);" + "i16vec3 abs(i16vec3);" + "i16vec4 abs(i16vec4);" + + "int16_t sign(int16_t);" + "i16vec2 sign(i16vec2);" + "i16vec3 sign(i16vec3);" + "i16vec4 sign(i16vec4);" + + "int16_t min(int16_t x, int16_t y);" + "i16vec2 min(i16vec2 x, int16_t y);" + "i16vec3 min(i16vec3 x, int16_t y);" + "i16vec4 min(i16vec4 x, int16_t y);" + "i16vec2 min(i16vec2 x, i16vec2 y);" + "i16vec3 min(i16vec3 x, i16vec3 y);" + "i16vec4 min(i16vec4 x, i16vec4 y);" + + "uint16_t min(uint16_t x, uint16_t y);" + "u16vec2 min(u16vec2 x, uint16_t y);" + "u16vec3 min(u16vec3 x, uint16_t y);" + "u16vec4 min(u16vec4 x, uint16_t y);" + "u16vec2 min(u16vec2 x, u16vec2 y);" + "u16vec3 min(u16vec3 x, u16vec3 y);" + "u16vec4 min(u16vec4 x, u16vec4 y);" + + "int16_t max(int16_t x, int16_t y);" + "i16vec2 max(i16vec2 x, int16_t y);" + "i16vec3 max(i16vec3 x, int16_t y);" + "i16vec4 max(i16vec4 x, int16_t y);" + "i16vec2 max(i16vec2 x, i16vec2 y);" + "i16vec3 max(i16vec3 x, i16vec3 y);" + "i16vec4 max(i16vec4 x, i16vec4 y);" + + "uint16_t max(uint16_t x, uint16_t y);" + "u16vec2 max(u16vec2 x, uint16_t y);" + "u16vec3 max(u16vec3 x, uint16_t y);" + "u16vec4 max(u16vec4 x, uint16_t y);" + "u16vec2 max(u16vec2 x, u16vec2 y);" + "u16vec3 max(u16vec3 x, u16vec3 y);" + "u16vec4 max(u16vec4 x, u16vec4 y);" + + "int16_t clamp(int16_t x, int16_t minVal, int16_t maxVal);" + "i16vec2 clamp(i16vec2 x, int16_t minVal, int16_t maxVal);" + "i16vec3 clamp(i16vec3 x, int16_t minVal, int16_t maxVal);" + "i16vec4 clamp(i16vec4 x, int16_t minVal, int16_t maxVal);" + "i16vec2 clamp(i16vec2 x, i16vec2 minVal, i16vec2 maxVal);" + "i16vec3 clamp(i16vec3 x, i16vec3 minVal, i16vec3 maxVal);" + "i16vec4 clamp(i16vec4 x, i16vec4 minVal, i16vec4 maxVal);" + + "uint16_t clamp(uint16_t x, uint16_t minVal, uint16_t maxVal);" + "u16vec2 clamp(u16vec2 x, uint16_t minVal, uint16_t maxVal);" + "u16vec3 clamp(u16vec3 x, uint16_t minVal, uint16_t maxVal);" + "u16vec4 clamp(u16vec4 x, uint16_t minVal, uint16_t maxVal);" + "u16vec2 clamp(u16vec2 x, u16vec2 minVal, u16vec2 maxVal);" + "u16vec3 clamp(u16vec3 x, u16vec3 minVal, u16vec3 maxVal);" + "u16vec4 clamp(u16vec4 x, u16vec4 minVal, u16vec4 maxVal);" + + "int16_t mix(int16_t, int16_t, bool);" + "i16vec2 mix(i16vec2, i16vec2, bvec2);" + "i16vec3 mix(i16vec3, i16vec3, bvec3);" + "i16vec4 mix(i16vec4, i16vec4, bvec4);" + "uint16_t mix(uint16_t, uint16_t, bool);" + "u16vec2 mix(u16vec2, u16vec2, bvec2);" + "u16vec3 mix(u16vec3, u16vec3, bvec3);" + "u16vec4 mix(u16vec4, u16vec4, bvec4);" + + "float16_t frexp(float16_t, out int16_t);" + "f16vec2 frexp(f16vec2, out i16vec2);" + "f16vec3 frexp(f16vec3, out i16vec3);" + "f16vec4 frexp(f16vec4, out i16vec4);" + + "float16_t ldexp(float16_t, int16_t);" + "f16vec2 ldexp(f16vec2, i16vec2);" + "f16vec3 ldexp(f16vec3, i16vec3);" + "f16vec4 ldexp(f16vec4, i16vec4);" + + "int16_t halfBitsToInt16(float16_t);" + "i16vec2 halfBitsToInt16(f16vec2);" + "i16vec3 halhBitsToInt16(f16vec3);" + "i16vec4 halfBitsToInt16(f16vec4);" + + "uint16_t halfBitsToUint16(float16_t);" + "u16vec2 halfBitsToUint16(f16vec2);" + "u16vec3 halfBitsToUint16(f16vec3);" + "u16vec4 halfBitsToUint16(f16vec4);" + + "int16_t float16BitsToInt16(float16_t);" + "i16vec2 float16BitsToInt16(f16vec2);" + "i16vec3 float16BitsToInt16(f16vec3);" + "i16vec4 float16BitsToInt16(f16vec4);" + + "uint16_t float16BitsToUint16(float16_t);" + "u16vec2 float16BitsToUint16(f16vec2);" + "u16vec3 float16BitsToUint16(f16vec3);" + "u16vec4 float16BitsToUint16(f16vec4);" + + "float16_t int16BitsToFloat16(int16_t);" + "f16vec2 int16BitsToFloat16(i16vec2);" + "f16vec3 int16BitsToFloat16(i16vec3);" + "f16vec4 int16BitsToFloat16(i16vec4);" + + "float16_t uint16BitsToFloat16(uint16_t);" + "f16vec2 uint16BitsToFloat16(u16vec2);" + "f16vec3 uint16BitsToFloat16(u16vec3);" + "f16vec4 uint16BitsToFloat16(u16vec4);" + + "float16_t int16BitsToHalf(int16_t);" + "f16vec2 int16BitsToHalf(i16vec2);" + "f16vec3 int16BitsToHalf(i16vec3);" + "f16vec4 int16BitsToHalf(i16vec4);" + + "float16_t uint16BitsToHalf(uint16_t);" + "f16vec2 uint16BitsToHalf(u16vec2);" + "f16vec3 uint16BitsToHalf(u16vec3);" + "f16vec4 uint16BitsToHalf(u16vec4);" + + "int packInt2x16(i16vec2);" + "uint packUint2x16(u16vec2);" + "int64_t packInt4x16(i16vec4);" + "uint64_t packUint4x16(u16vec4);" + "i16vec2 unpackInt2x16(int);" + "u16vec2 unpackUint2x16(uint);" + "i16vec4 unpackInt4x16(int64_t);" + "u16vec4 unpackUint4x16(uint64_t);" + + "bvec2 lessThan(i16vec2, i16vec2);" + "bvec3 lessThan(i16vec3, i16vec3);" + "bvec4 lessThan(i16vec4, i16vec4);" + "bvec2 lessThan(u16vec2, u16vec2);" + "bvec3 lessThan(u16vec3, u16vec3);" + "bvec4 lessThan(u16vec4, u16vec4);" + + "bvec2 lessThanEqual(i16vec2, i16vec2);" + "bvec3 lessThanEqual(i16vec3, i16vec3);" + "bvec4 lessThanEqual(i16vec4, i16vec4);" + "bvec2 lessThanEqual(u16vec2, u16vec2);" + "bvec3 lessThanEqual(u16vec3, u16vec3);" + "bvec4 lessThanEqual(u16vec4, u16vec4);" + + "bvec2 greaterThan(i16vec2, i16vec2);" + "bvec3 greaterThan(i16vec3, i16vec3);" + "bvec4 greaterThan(i16vec4, i16vec4);" + "bvec2 greaterThan(u16vec2, u16vec2);" + "bvec3 greaterThan(u16vec3, u16vec3);" + "bvec4 greaterThan(u16vec4, u16vec4);" + + "bvec2 greaterThanEqual(i16vec2, i16vec2);" + "bvec3 greaterThanEqual(i16vec3, i16vec3);" + "bvec4 greaterThanEqual(i16vec4, i16vec4);" + "bvec2 greaterThanEqual(u16vec2, u16vec2);" + "bvec3 greaterThanEqual(u16vec3, u16vec3);" + "bvec4 greaterThanEqual(u16vec4, u16vec4);" + + "bvec2 equal(i16vec2, i16vec2);" + "bvec3 equal(i16vec3, i16vec3);" + "bvec4 equal(i16vec4, i16vec4);" + "bvec2 equal(u16vec2, u16vec2);" + "bvec3 equal(u16vec3, u16vec3);" + "bvec4 equal(u16vec4, u16vec4);" + + "bvec2 notEqual(i16vec2, i16vec2);" + "bvec3 notEqual(i16vec3, i16vec3);" + "bvec4 notEqual(i16vec4, i16vec4);" + "bvec2 notEqual(u16vec2, u16vec2);" + "bvec3 notEqual(u16vec3, u16vec3);" + "bvec4 notEqual(u16vec4, u16vec4);" + + " int16_t bitfieldExtract( int16_t, int16_t, int16_t);" + "i16vec2 bitfieldExtract(i16vec2, int16_t, int16_t);" + "i16vec3 bitfieldExtract(i16vec3, int16_t, int16_t);" + "i16vec4 bitfieldExtract(i16vec4, int16_t, int16_t);" + + " uint16_t bitfieldExtract( uint16_t, int16_t, int16_t);" + "u16vec2 bitfieldExtract(u16vec2, int16_t, int16_t);" + "u16vec3 bitfieldExtract(u16vec3, int16_t, int16_t);" + "u16vec4 bitfieldExtract(u16vec4, int16_t, int16_t);" + + " int16_t bitfieldInsert( int16_t base, int16_t, int16_t, int16_t);" + "i16vec2 bitfieldInsert(i16vec2 base, i16vec2, int16_t, int16_t);" + "i16vec3 bitfieldInsert(i16vec3 base, i16vec3, int16_t, int16_t);" + "i16vec4 bitfieldInsert(i16vec4 base, i16vec4, int16_t, int16_t);" + + " uint16_t bitfieldInsert( uint16_t base, uint16_t, int16_t, int16_t);" + "u16vec2 bitfieldInsert(u16vec2 base, u16vec2, int16_t, int16_t);" + "u16vec3 bitfieldInsert(u16vec3 base, u16vec3, int16_t, int16_t);" + "u16vec4 bitfieldInsert(u16vec4 base, u16vec4, int16_t, int16_t);" + + " int16_t bitCount( int16_t);" + "i16vec2 bitCount(i16vec2);" + "i16vec3 bitCount(i16vec3);" + "i16vec4 bitCount(i16vec4);" + + " int16_t bitCount( uint16_t);" + "i16vec2 bitCount(u16vec2);" + "i16vec3 bitCount(u16vec3);" + "i16vec4 bitCount(u16vec4);" + + " int16_t findLSB( int16_t);" + "i16vec2 findLSB(i16vec2);" + "i16vec3 findLSB(i16vec3);" + "i16vec4 findLSB(i16vec4);" + + " int16_t findLSB( uint16_t);" + "i16vec2 findLSB(u16vec2);" + "i16vec3 findLSB(u16vec3);" + "i16vec4 findLSB(u16vec4);" + + " int16_t findMSB( int16_t);" + "i16vec2 findMSB(i16vec2);" + "i16vec3 findMSB(i16vec3);" + "i16vec4 findMSB(i16vec4);" + + " int16_t findMSB( uint16_t);" + "i16vec2 findMSB(u16vec2);" + "i16vec3 findMSB(u16vec3);" + "i16vec4 findMSB(u16vec4);" + + "int16_t pack16(i8vec2);" + "uint16_t pack16(u8vec2);" + "int32_t pack32(i8vec4);" + "uint32_t pack32(u8vec4);" + "int32_t pack32(i16vec2);" + "uint32_t pack32(u16vec2);" + "int64_t pack64(i16vec4);" + "uint64_t pack64(u16vec4);" + "int64_t pack64(i32vec2);" + "uint64_t pack64(u32vec2);" + + "i8vec2 unpack8(int16_t);" + "u8vec2 unpack8(uint16_t);" + "i8vec4 unpack8(int32_t);" + "u8vec4 unpack8(uint32_t);" + "i16vec2 unpack16(int32_t);" + "u16vec2 unpack16(uint32_t);" + "i16vec4 unpack16(int64_t);" + "u16vec4 unpack16(uint64_t);" + "i32vec2 unpack32(int64_t);" + "u32vec2 unpack32(uint64_t);" + + "float64_t radians(float64_t);" + "f64vec2 radians(f64vec2);" + "f64vec3 radians(f64vec3);" + "f64vec4 radians(f64vec4);" + + "float64_t degrees(float64_t);" + "f64vec2 degrees(f64vec2);" + "f64vec3 degrees(f64vec3);" + "f64vec4 degrees(f64vec4);" + + "float64_t sin(float64_t);" + "f64vec2 sin(f64vec2);" + "f64vec3 sin(f64vec3);" + "f64vec4 sin(f64vec4);" + + "float64_t cos(float64_t);" + "f64vec2 cos(f64vec2);" + "f64vec3 cos(f64vec3);" + "f64vec4 cos(f64vec4);" + + "float64_t tan(float64_t);" + "f64vec2 tan(f64vec2);" + "f64vec3 tan(f64vec3);" + "f64vec4 tan(f64vec4);" + + "float64_t asin(float64_t);" + "f64vec2 asin(f64vec2);" + "f64vec3 asin(f64vec3);" + "f64vec4 asin(f64vec4);" + + "float64_t acos(float64_t);" + "f64vec2 acos(f64vec2);" + "f64vec3 acos(f64vec3);" + "f64vec4 acos(f64vec4);" + + "float64_t atan(float64_t, float64_t);" + "f64vec2 atan(f64vec2, f64vec2);" + "f64vec3 atan(f64vec3, f64vec3);" + "f64vec4 atan(f64vec4, f64vec4);" + + "float64_t atan(float64_t);" + "f64vec2 atan(f64vec2);" + "f64vec3 atan(f64vec3);" + "f64vec4 atan(f64vec4);" + + "float64_t sinh(float64_t);" + "f64vec2 sinh(f64vec2);" + "f64vec3 sinh(f64vec3);" + "f64vec4 sinh(f64vec4);" + + "float64_t cosh(float64_t);" + "f64vec2 cosh(f64vec2);" + "f64vec3 cosh(f64vec3);" + "f64vec4 cosh(f64vec4);" + + "float64_t tanh(float64_t);" + "f64vec2 tanh(f64vec2);" + "f64vec3 tanh(f64vec3);" + "f64vec4 tanh(f64vec4);" + + "float64_t asinh(float64_t);" + "f64vec2 asinh(f64vec2);" + "f64vec3 asinh(f64vec3);" + "f64vec4 asinh(f64vec4);" + + "float64_t acosh(float64_t);" + "f64vec2 acosh(f64vec2);" + "f64vec3 acosh(f64vec3);" + "f64vec4 acosh(f64vec4);" + + "float64_t atanh(float64_t);" + "f64vec2 atanh(f64vec2);" + "f64vec3 atanh(f64vec3);" + "f64vec4 atanh(f64vec4);" + + "float64_t pow(float64_t, float64_t);" + "f64vec2 pow(f64vec2, f64vec2);" + "f64vec3 pow(f64vec3, f64vec3);" + "f64vec4 pow(f64vec4, f64vec4);" + + "float64_t exp(float64_t);" + "f64vec2 exp(f64vec2);" + "f64vec3 exp(f64vec3);" + "f64vec4 exp(f64vec4);" + + "float64_t log(float64_t);" + "f64vec2 log(f64vec2);" + "f64vec3 log(f64vec3);" + "f64vec4 log(f64vec4);" + + "float64_t exp2(float64_t);" + "f64vec2 exp2(f64vec2);" + "f64vec3 exp2(f64vec3);" + "f64vec4 exp2(f64vec4);" + + "float64_t log2(float64_t);" + "f64vec2 log2(f64vec2);" + "f64vec3 log2(f64vec3);" + "f64vec4 log2(f64vec4);" + "\n"); + } + + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append(derivativesAndControl64bits); + stageBuiltins[EShLangFragment].append( + "float64_t interpolateAtCentroid(float64_t);" + "f64vec2 interpolateAtCentroid(f64vec2);" + "f64vec3 interpolateAtCentroid(f64vec3);" + "f64vec4 interpolateAtCentroid(f64vec4);" + + "float64_t interpolateAtSample(float64_t, int);" + "f64vec2 interpolateAtSample(f64vec2, int);" + "f64vec3 interpolateAtSample(f64vec3, int);" + "f64vec4 interpolateAtSample(f64vec4, int);" + + "float64_t interpolateAtOffset(float64_t, f64vec2);" + "f64vec2 interpolateAtOffset(f64vec2, f64vec2);" + "f64vec3 interpolateAtOffset(f64vec3, f64vec2);" + "f64vec4 interpolateAtOffset(f64vec4, f64vec2);" + + "\n"); + + } +#endif // !GLSLANG_ANGLE + + //============================================================================ + // + // Prototypes for built-in functions seen by vertex shaders only. + // (Except legacy lod functions, where it depends which release they are + // vertex only.) + // + //============================================================================ + + // + // Geometric Functions. + // + if (spvVersion.vulkan == 0 && IncludeLegacy(version, profile, spvVersion)) + stageBuiltins[EShLangVertex].append("vec4 ftransform();"); + +#ifndef GLSLANG_ANGLE + // + // Original-style texture Functions with lod. + // + TString* s; + if (version == 100) + s = &stageBuiltins[EShLangVertex]; + else + s = &commonBuiltins; + if ((profile == EEsProfile && version == 100) || + profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + s->append( + "vec4 texture2DLod(sampler2D, vec2, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjLod(sampler2D, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjLod(sampler2D, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 texture3DLod(sampler3D, vec3, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check + "vec4 texture3DProjLod(sampler3D, vec4, float);" // GL_ARB_shader_texture_lod // OES_texture_3D, but caught by keyword check + "vec4 textureCubeLod(samplerCube, vec3, float);" // GL_ARB_shader_texture_lod + + "\n"); + } + } + if ( profile == ECompatibilityProfile || + (profile == ECoreProfile && version < 420) || + profile == ENoProfile) { + if (spvVersion.spv == 0) { + s->append( + "vec4 texture1DLod(sampler1D, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjLod(sampler1D, vec2, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjLod(sampler1D, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DLod(sampler1DShadow, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DLod(sampler2DShadow, vec3, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DProjLod(sampler1DShadow, vec4, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DProjLod(sampler2DShadow, vec4, float);" // GL_ARB_shader_texture_lod + + "vec4 texture1DGradARB(sampler1D, float, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjGradARB(sampler1D, vec2, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture1DProjGradARB(sampler1D, vec4, float, float);" // GL_ARB_shader_texture_lod + "vec4 texture2DGradARB(sampler2D, vec2, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjGradARB(sampler2D, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DProjGradARB(sampler2D, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture3DGradARB(sampler3D, vec3, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 texture3DProjGradARB(sampler3D, vec4, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 textureCubeGradARB(samplerCube, vec3, vec3, vec3);" // GL_ARB_shader_texture_lod + "vec4 shadow1DGradARB(sampler1DShadow, vec3, float, float);" // GL_ARB_shader_texture_lod + "vec4 shadow1DProjGradARB( sampler1DShadow, vec4, float, float);" // GL_ARB_shader_texture_lod + "vec4 shadow2DGradARB(sampler2DShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DProjGradARB( sampler2DShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectGradARB(sampler2DRect, vec2, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectProjGradARB( sampler2DRect, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 texture2DRectProjGradARB( sampler2DRect, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DRectGradARB( sampler2DRectShadow, vec3, vec2, vec2);" // GL_ARB_shader_texture_lod + "vec4 shadow2DRectProjGradARB(sampler2DRectShadow, vec4, vec2, vec2);" // GL_ARB_shader_texture_lod + + "\n"); + } + } +#endif // !GLSLANG_ANGLE + + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) { + //============================================================================ + // + // Prototypes for built-in functions seen by geometry shaders only. + // + //============================================================================ + + if (profile != EEsProfile && version >= 400) { + stageBuiltins[EShLangGeometry].append( + "void EmitStreamVertex(int);" + "void EndStreamPrimitive(int);" + ); + } + stageBuiltins[EShLangGeometry].append( + "void EmitVertex();" + "void EndPrimitive();" + "\n"); + } +#endif // !GLSLANG_WEB + + //============================================================================ + // + // Prototypes for all control functions. + // + //============================================================================ + bool esBarrier = (profile == EEsProfile && version >= 310); + if ((profile != EEsProfile && version >= 150) || esBarrier) + stageBuiltins[EShLangTessControl].append( + "void barrier();" + ); + if ((profile != EEsProfile && version >= 420) || esBarrier) + stageBuiltins[EShLangCompute].append( + "void barrier();" + ); + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMeshNV].append( + "void barrier();" + ); + stageBuiltins[EShLangTaskNV].append( + "void barrier();" + ); + } + if ((profile != EEsProfile && version >= 130) || esBarrier) + commonBuiltins.append( + "void memoryBarrier();" + ); + if ((profile != EEsProfile && version >= 420) || esBarrier) { + commonBuiltins.append( + "void memoryBarrierBuffer();" + ); + stageBuiltins[EShLangCompute].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + } +#ifndef GLSLANG_WEB + if ((profile != EEsProfile && version >= 420) || esBarrier) { + if (spvVersion.vulkan == 0) { + commonBuiltins.append("void memoryBarrierAtomicCounter();"); + } + commonBuiltins.append("void memoryBarrierImage();"); + } + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMeshNV].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + stageBuiltins[EShLangTaskNV].append( + "void memoryBarrierShared();" + "void groupMemoryBarrier();" + ); + } + + commonBuiltins.append("void controlBarrier(int, int, int, int);\n" + "void memoryBarrier(int, int, int);\n"); + + commonBuiltins.append("void debugPrintfEXT();\n"); + +#ifndef GLSLANG_ANGLE + if (profile != EEsProfile && version >= 450) { + // coopMatStoreNV perhaps ought to have "out" on the buf parameter, but + // adding it introduces undesirable tempArgs on the stack. What we want + // is more like "buf" thought of as a pointer value being an in parameter. + stageBuiltins[EShLangCompute].append( + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent float16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent float[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out fcoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(fcoopmatNV m, volatile coherent float16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent float[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent float64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(fcoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "fcoopmatNV coopMatMulAddNV(fcoopmatNV A, fcoopmatNV B, fcoopmatNV C);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out icoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatLoadNV(out ucoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(icoopmatNV m, volatile coherent int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(icoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "void coopMatStoreNV(ucoopmatNV m, volatile coherent int8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent int16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent int[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent int64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent ivec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent ivec4[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uint8_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uint16_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uint[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uint64_t[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uvec2[] buf, uint element, uint stride, bool colMajor);\n" + "void coopMatStoreNV(ucoopmatNV m, volatile coherent uvec4[] buf, uint element, uint stride, bool colMajor);\n" + + "icoopmatNV coopMatMulAddNV(icoopmatNV A, icoopmatNV B, icoopmatNV C);\n" + "ucoopmatNV coopMatMulAddNV(ucoopmatNV A, ucoopmatNV B, ucoopmatNV C);\n" + ); + } + + //============================================================================ + // + // Prototypes for built-in functions seen by fragment shaders only. + // + //============================================================================ + + // + // Original-style texture Functions with bias. + // + if (spvVersion.spv == 0 && (profile != EEsProfile || version == 100)) { + stageBuiltins[EShLangFragment].append( + "vec4 texture2D(sampler2D, vec2, float);" + "vec4 texture2DProj(sampler2D, vec3, float);" + "vec4 texture2DProj(sampler2D, vec4, float);" + "vec4 texture3D(sampler3D, vec3, float);" // OES_texture_3D + "vec4 texture3DProj(sampler3D, vec4, float);" // OES_texture_3D + "vec4 textureCube(samplerCube, vec3, float);" + + "\n"); + } + if (spvVersion.spv == 0 && (profile != EEsProfile && version > 100)) { + stageBuiltins[EShLangFragment].append( + "vec4 texture1D(sampler1D, float, float);" + "vec4 texture1DProj(sampler1D, vec2, float);" + "vec4 texture1DProj(sampler1D, vec4, float);" + "vec4 shadow1D(sampler1DShadow, vec3, float);" + "vec4 shadow2D(sampler2DShadow, vec3, float);" + "vec4 shadow1DProj(sampler1DShadow, vec4, float);" + "vec4 shadow2DProj(sampler2DShadow, vec4, float);" + + "\n"); + } + if (spvVersion.spv == 0 && profile == EEsProfile) { + stageBuiltins[EShLangFragment].append( + "vec4 texture2DLodEXT(sampler2D, vec2, float);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjLodEXT(sampler2D, vec3, float);" // GL_EXT_shader_texture_lod + "vec4 texture2DProjLodEXT(sampler2D, vec4, float);" // GL_EXT_shader_texture_lod + "vec4 textureCubeLodEXT(samplerCube, vec3, float);" // GL_EXT_shader_texture_lod + + "\n"); + } +#endif // !GLSLANG_ANGLE + + // GL_ARB_derivative_control + if (profile != EEsProfile && version >= 400) { + stageBuiltins[EShLangFragment].append(derivativeControls); + stageBuiltins[EShLangFragment].append("\n"); + } + + // GL_OES_shader_multisample_interpolation + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 400)) { + stageBuiltins[EShLangFragment].append( + "float interpolateAtCentroid(float);" + "vec2 interpolateAtCentroid(vec2);" + "vec3 interpolateAtCentroid(vec3);" + "vec4 interpolateAtCentroid(vec4);" + + "float interpolateAtSample(float, int);" + "vec2 interpolateAtSample(vec2, int);" + "vec3 interpolateAtSample(vec3, int);" + "vec4 interpolateAtSample(vec4, int);" + + "float interpolateAtOffset(float, vec2);" + "vec2 interpolateAtOffset(vec2, vec2);" + "vec3 interpolateAtOffset(vec3, vec2);" + "vec4 interpolateAtOffset(vec4, vec2);" + + "\n"); + } + + stageBuiltins[EShLangFragment].append( + "void beginInvocationInterlockARB(void);" + "void endInvocationInterlockARB(void);"); + + stageBuiltins[EShLangFragment].append( + "bool helperInvocationEXT();" + "\n"); + +#ifndef GLSLANG_ANGLE + // GL_AMD_shader_explicit_vertex_parameter + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append( + "float interpolateAtVertexAMD(float, uint);" + "vec2 interpolateAtVertexAMD(vec2, uint);" + "vec3 interpolateAtVertexAMD(vec3, uint);" + "vec4 interpolateAtVertexAMD(vec4, uint);" + + "int interpolateAtVertexAMD(int, uint);" + "ivec2 interpolateAtVertexAMD(ivec2, uint);" + "ivec3 interpolateAtVertexAMD(ivec3, uint);" + "ivec4 interpolateAtVertexAMD(ivec4, uint);" + + "uint interpolateAtVertexAMD(uint, uint);" + "uvec2 interpolateAtVertexAMD(uvec2, uint);" + "uvec3 interpolateAtVertexAMD(uvec3, uint);" + "uvec4 interpolateAtVertexAMD(uvec4, uint);" + + "float16_t interpolateAtVertexAMD(float16_t, uint);" + "f16vec2 interpolateAtVertexAMD(f16vec2, uint);" + "f16vec3 interpolateAtVertexAMD(f16vec3, uint);" + "f16vec4 interpolateAtVertexAMD(f16vec4, uint);" + + "\n"); + } + + // GL_AMD_gpu_shader_half_float + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangFragment].append(derivativesAndControl16bits); + stageBuiltins[EShLangFragment].append("\n"); + + stageBuiltins[EShLangFragment].append( + "float16_t interpolateAtCentroid(float16_t);" + "f16vec2 interpolateAtCentroid(f16vec2);" + "f16vec3 interpolateAtCentroid(f16vec3);" + "f16vec4 interpolateAtCentroid(f16vec4);" + + "float16_t interpolateAtSample(float16_t, int);" + "f16vec2 interpolateAtSample(f16vec2, int);" + "f16vec3 interpolateAtSample(f16vec3, int);" + "f16vec4 interpolateAtSample(f16vec4, int);" + + "float16_t interpolateAtOffset(float16_t, f16vec2);" + "f16vec2 interpolateAtOffset(f16vec2, f16vec2);" + "f16vec3 interpolateAtOffset(f16vec3, f16vec2);" + "f16vec4 interpolateAtOffset(f16vec4, f16vec2);" + + "\n"); + } + + // GL_ARB_shader_clock & GL_EXT_shader_realtime_clock + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append( + "uvec2 clock2x32ARB();" + "uint64_t clockARB();" + "uvec2 clockRealtime2x32EXT();" + "uint64_t clockRealtimeEXT();" + "\n"); + } + + // GL_AMD_shader_fragment_mask + if (profile != EEsProfile && version >= 450 && spvVersion.vulkan > 0) { + stageBuiltins[EShLangFragment].append( + "uint fragmentMaskFetchAMD(subpassInputMS);" + "uint fragmentMaskFetchAMD(isubpassInputMS);" + "uint fragmentMaskFetchAMD(usubpassInputMS);" + + "vec4 fragmentFetchAMD(subpassInputMS, uint);" + "ivec4 fragmentFetchAMD(isubpassInputMS, uint);" + "uvec4 fragmentFetchAMD(usubpassInputMS, uint);" + + "\n"); + } + + // Builtins for GL_NV_ray_tracing/GL_EXT_ray_tracing/GL_EXT_ray_query + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append("void rayQueryInitializeEXT(rayQueryEXT, accelerationStructureEXT, uint, uint, vec3, float, vec3, float);" + "void rayQueryTerminateEXT(rayQueryEXT);" + "void rayQueryGenerateIntersectionEXT(rayQueryEXT, float);" + "void rayQueryConfirmIntersectionEXT(rayQueryEXT);" + "bool rayQueryProceedEXT(rayQueryEXT);" + "uint rayQueryGetIntersectionTypeEXT(rayQueryEXT, bool);" + "float rayQueryGetRayTMinEXT(rayQueryEXT);" + "uint rayQueryGetRayFlagsEXT(rayQueryEXT);" + "vec3 rayQueryGetWorldRayOriginEXT(rayQueryEXT);" + "vec3 rayQueryGetWorldRayDirectionEXT(rayQueryEXT);" + "float rayQueryGetIntersectionTEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionInstanceCustomIndexEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionInstanceIdEXT(rayQueryEXT, bool);" + "uint rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionGeometryIndexEXT(rayQueryEXT, bool);" + "int rayQueryGetIntersectionPrimitiveIndexEXT(rayQueryEXT, bool);" + "vec2 rayQueryGetIntersectionBarycentricsEXT(rayQueryEXT, bool);" + "bool rayQueryGetIntersectionFrontFaceEXT(rayQueryEXT, bool);" + "bool rayQueryGetIntersectionCandidateAABBOpaqueEXT(rayQueryEXT);" + "vec3 rayQueryGetIntersectionObjectRayDirectionEXT(rayQueryEXT, bool);" + "vec3 rayQueryGetIntersectionObjectRayOriginEXT(rayQueryEXT, bool);" + "mat4x3 rayQueryGetIntersectionObjectToWorldEXT(rayQueryEXT, bool);" + "mat4x3 rayQueryGetIntersectionWorldToObjectEXT(rayQueryEXT, bool);" + "\n"); + + stageBuiltins[EShLangRayGen].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + stageBuiltins[EShLangIntersect].append( + "bool reportIntersectionNV(float, uint);" + "bool reportIntersectionEXT(float, uint);" + "\n"); + stageBuiltins[EShLangAnyHit].append( + "void ignoreIntersectionNV();" + "void ignoreIntersectionEXT();" + "void terminateRayNV();" + "void terminateRayEXT();" + "\n"); + stageBuiltins[EShLangClosestHit].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + stageBuiltins[EShLangMiss].append( + "void traceNV(accelerationStructureNV,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void traceRayEXT(accelerationStructureEXT,uint,uint,uint,uint,uint,vec3,float,vec3,float,int);" + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + stageBuiltins[EShLangCallable].append( + "void executeCallableNV(uint, int);" + "void executeCallableEXT(uint, int);" + "\n"); + } +#endif // !GLSLANG_ANGLE + + //E_SPV_NV_compute_shader_derivatives + if ((profile == EEsProfile && version >= 320) || (profile != EEsProfile && version >= 450)) { + stageBuiltins[EShLangCompute].append(derivativeControls); + stageBuiltins[EShLangCompute].append("\n"); + } +#ifndef GLSLANG_ANGLE + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangCompute].append(derivativesAndControl16bits); + stageBuiltins[EShLangCompute].append(derivativesAndControl64bits); + stageBuiltins[EShLangCompute].append("\n"); + } + + // Builtins for GL_NV_mesh_shader + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + stageBuiltins[EShLangMeshNV].append( + "void writePackedPrimitiveIndices4x8NV(uint, uint);" + "\n"); + } +#endif // !GLSLANG_ANGLE +#endif // !GLSLANG_WEB + + //============================================================================ + // + // Standard Uniforms + // + //============================================================================ + + // + // Depth range in window coordinates, p. 33 + // + if (spvVersion.spv == 0) { + commonBuiltins.append( + "struct gl_DepthRangeParameters {" + ); + if (profile == EEsProfile) { + commonBuiltins.append( + "highp float near;" // n + "highp float far;" // f + "highp float diff;" // f - n + ); + } else { +#ifndef GLSLANG_WEB + commonBuiltins.append( + "float near;" // n + "float far;" // f + "float diff;" // f - n + ); +#endif + } + + commonBuiltins.append( + "};" + "uniform gl_DepthRangeParameters gl_DepthRange;" + "\n"); + } + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) { + // + // Matrix state. p. 31, 32, 37, 39, 40. + // + commonBuiltins.append( + "uniform mat4 gl_ModelViewMatrix;" + "uniform mat4 gl_ProjectionMatrix;" + "uniform mat4 gl_ModelViewProjectionMatrix;" + + // + // Derived matrix state that provides inverse and transposed versions + // of the matrices above. + // + "uniform mat3 gl_NormalMatrix;" + + "uniform mat4 gl_ModelViewMatrixInverse;" + "uniform mat4 gl_ProjectionMatrixInverse;" + "uniform mat4 gl_ModelViewProjectionMatrixInverse;" + + "uniform mat4 gl_ModelViewMatrixTranspose;" + "uniform mat4 gl_ProjectionMatrixTranspose;" + "uniform mat4 gl_ModelViewProjectionMatrixTranspose;" + + "uniform mat4 gl_ModelViewMatrixInverseTranspose;" + "uniform mat4 gl_ProjectionMatrixInverseTranspose;" + "uniform mat4 gl_ModelViewProjectionMatrixInverseTranspose;" + + // + // Normal scaling p. 39. + // + "uniform float gl_NormalScale;" + + // + // Point Size, p. 66, 67. + // + "struct gl_PointParameters {" + "float size;" + "float sizeMin;" + "float sizeMax;" + "float fadeThresholdSize;" + "float distanceConstantAttenuation;" + "float distanceLinearAttenuation;" + "float distanceQuadraticAttenuation;" + "};" + + "uniform gl_PointParameters gl_Point;" + + // + // Material State p. 50, 55. + // + "struct gl_MaterialParameters {" + "vec4 emission;" // Ecm + "vec4 ambient;" // Acm + "vec4 diffuse;" // Dcm + "vec4 specular;" // Scm + "float shininess;" // Srm + "};" + "uniform gl_MaterialParameters gl_FrontMaterial;" + "uniform gl_MaterialParameters gl_BackMaterial;" + + // + // Light State p 50, 53, 55. + // + "struct gl_LightSourceParameters {" + "vec4 ambient;" // Acli + "vec4 diffuse;" // Dcli + "vec4 specular;" // Scli + "vec4 position;" // Ppli + "vec4 halfVector;" // Derived: Hi + "vec3 spotDirection;" // Sdli + "float spotExponent;" // Srli + "float spotCutoff;" // Crli + // (range: [0.0,90.0], 180.0) + "float spotCosCutoff;" // Derived: cos(Crli) + // (range: [1.0,0.0],-1.0) + "float constantAttenuation;" // K0 + "float linearAttenuation;" // K1 + "float quadraticAttenuation;"// K2 + "};" + + "struct gl_LightModelParameters {" + "vec4 ambient;" // Acs + "};" + + "uniform gl_LightModelParameters gl_LightModel;" + + // + // Derived state from products of light and material. + // + "struct gl_LightModelProducts {" + "vec4 sceneColor;" // Derived. Ecm + Acm * Acs + "};" + + "uniform gl_LightModelProducts gl_FrontLightModelProduct;" + "uniform gl_LightModelProducts gl_BackLightModelProduct;" + + "struct gl_LightProducts {" + "vec4 ambient;" // Acm * Acli + "vec4 diffuse;" // Dcm * Dcli + "vec4 specular;" // Scm * Scli + "};" + + // + // Fog p. 161 + // + "struct gl_FogParameters {" + "vec4 color;" + "float density;" + "float start;" + "float end;" + "float scale;" // 1 / (gl_FogEnd - gl_FogStart) + "};" + + "uniform gl_FogParameters gl_Fog;" + + "\n"); + } +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE + + //============================================================================ + // + // Define the interface to the compute shader. + // + //============================================================================ + + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangCompute].append( + "in highp uvec3 gl_NumWorkGroups;" + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + + "\n"); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangCompute].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "\n"); + } + +#ifndef GLSLANG_WEB +#ifndef GLSLANG_ANGLE + //============================================================================ + // + // Define the interface to the mesh/task shader. + // + //============================================================================ + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + // per-vertex attributes + stageBuiltins[EShLangMeshNV].append( + "out gl_MeshPerVertexNV {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + "float gl_CullDistance[];" + "perviewNV vec4 gl_PositionPerViewNV[];" + "perviewNV float gl_ClipDistancePerViewNV[][];" + "perviewNV float gl_CullDistancePerViewNV[][];" + "} gl_MeshVerticesNV[];" + ); + + // per-primitive attributes + stageBuiltins[EShLangMeshNV].append( + "perprimitiveNV out gl_MeshPerPrimitiveNV {" + "int gl_PrimitiveID;" + "int gl_Layer;" + "int gl_ViewportIndex;" + "int gl_ViewportMask[];" + "perviewNV int gl_LayerPerViewNV[];" + "perviewNV int gl_ViewportMaskPerViewNV[][];" + "} gl_MeshPrimitivesNV[];" + ); + + stageBuiltins[EShLangMeshNV].append( + "out uint gl_PrimitiveCountNV;" + "out uint gl_PrimitiveIndicesNV[];" + + "in uint gl_MeshViewCountNV;" + "in uint gl_MeshViewIndicesNV[4];" + + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + + "\n"); + + stageBuiltins[EShLangTaskNV].append( + "out uint gl_TaskCountNV;" + + "const highp uvec3 gl_WorkGroupSize = uvec3(1,1,1);" + + "in highp uvec3 gl_WorkGroupID;" + "in highp uvec3 gl_LocalInvocationID;" + + "in highp uvec3 gl_GlobalInvocationID;" + "in highp uint gl_LocalInvocationIndex;" + + "in uint gl_MeshViewCountNV;" + "in uint gl_MeshViewIndicesNV[4];" + + "\n"); + } + + if (profile != EEsProfile && version >= 450) { + stageBuiltins[EShLangMeshNV].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in int gl_DrawIDARB;" // GL_ARB_shader_draw_parameters + "\n"); + + stageBuiltins[EShLangTaskNV].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in int gl_DrawIDARB;" // GL_ARB_shader_draw_parameters + "\n"); + + if (version >= 460) { + stageBuiltins[EShLangMeshNV].append( + "in int gl_DrawID;" + "\n"); + + stageBuiltins[EShLangTaskNV].append( + "in int gl_DrawID;" + "\n"); + } + } +#endif // !GLSLANG_ANGLE + + //============================================================================ + // + // Define the interface to the vertex shader. + // + //============================================================================ + + if (profile != EEsProfile) { + if (version < 130) { + stageBuiltins[EShLangVertex].append( + "attribute vec4 gl_Color;" + "attribute vec4 gl_SecondaryColor;" + "attribute vec3 gl_Normal;" + "attribute vec4 gl_Vertex;" + "attribute vec4 gl_MultiTexCoord0;" + "attribute vec4 gl_MultiTexCoord1;" + "attribute vec4 gl_MultiTexCoord2;" + "attribute vec4 gl_MultiTexCoord3;" + "attribute vec4 gl_MultiTexCoord4;" + "attribute vec4 gl_MultiTexCoord5;" + "attribute vec4 gl_MultiTexCoord6;" + "attribute vec4 gl_MultiTexCoord7;" + "attribute float gl_FogCoord;" + "\n"); + } else if (IncludeLegacy(version, profile, spvVersion)) { + stageBuiltins[EShLangVertex].append( + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + "in vec3 gl_Normal;" + "in vec4 gl_Vertex;" + "in vec4 gl_MultiTexCoord0;" + "in vec4 gl_MultiTexCoord1;" + "in vec4 gl_MultiTexCoord2;" + "in vec4 gl_MultiTexCoord3;" + "in vec4 gl_MultiTexCoord4;" + "in vec4 gl_MultiTexCoord5;" + "in vec4 gl_MultiTexCoord6;" + "in vec4 gl_MultiTexCoord7;" + "in float gl_FogCoord;" + "\n"); + } + + if (version < 150) { + if (version < 130) { + stageBuiltins[EShLangVertex].append( + " vec4 gl_ClipVertex;" // needs qualifier fixed later + "varying vec4 gl_FrontColor;" + "varying vec4 gl_BackColor;" + "varying vec4 gl_FrontSecondaryColor;" + "varying vec4 gl_BackSecondaryColor;" + "varying vec4 gl_TexCoord[];" + "varying float gl_FogFragCoord;" + "\n"); + } else if (IncludeLegacy(version, profile, spvVersion)) { + stageBuiltins[EShLangVertex].append( + " vec4 gl_ClipVertex;" // needs qualifier fixed later + "out vec4 gl_FrontColor;" + "out vec4 gl_BackColor;" + "out vec4 gl_FrontSecondaryColor;" + "out vec4 gl_BackSecondaryColor;" + "out vec4 gl_TexCoord[];" + "out float gl_FogFragCoord;" + "\n"); + } + stageBuiltins[EShLangVertex].append( + "vec4 gl_Position;" // needs qualifier fixed later + "float gl_PointSize;" // needs qualifier fixed later + ); + + if (version == 130 || version == 140) + stageBuiltins[EShLangVertex].append( + "out float gl_ClipDistance[];" + ); + } else { + // version >= 150 + stageBuiltins[EShLangVertex].append( + "out gl_PerVertex {" + "vec4 gl_Position;" // needs qualifier fixed later + "float gl_PointSize;" // needs qualifier fixed later + "float gl_ClipDistance[];" + ); + if (IncludeLegacy(version, profile, spvVersion)) + stageBuiltins[EShLangVertex].append( + "vec4 gl_ClipVertex;" // needs qualifier fixed later + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangVertex].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangVertex].append( + "};" + "\n"); + } + if (version >= 130 && spvVersion.vulkan == 0) + stageBuiltins[EShLangVertex].append( + "int gl_VertexID;" // needs qualifier fixed later + ); + if (version >= 140 && spvVersion.vulkan == 0) + stageBuiltins[EShLangVertex].append( + "int gl_InstanceID;" // needs qualifier fixed later + ); + if (spvVersion.vulkan > 0 && version >= 140) + stageBuiltins[EShLangVertex].append( + "in int gl_VertexIndex;" + "in int gl_InstanceIndex;" + ); + if (version >= 440) { + stageBuiltins[EShLangVertex].append( + "in int gl_BaseVertexARB;" + "in int gl_BaseInstanceARB;" + "in int gl_DrawIDARB;" + ); + } + if (version >= 410) { + stageBuiltins[EShLangVertex].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + ); + } + if (version >= 460) { + stageBuiltins[EShLangVertex].append( + "in int gl_BaseVertex;" + "in int gl_BaseInstance;" + "in int gl_DrawID;" + ); + } + + if (version >= 450) + stageBuiltins[EShLangVertex].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + } else { + // ES profile + if (version == 100) { + stageBuiltins[EShLangVertex].append( + "highp vec4 gl_Position;" // needs qualifier fixed later + "mediump float gl_PointSize;" // needs qualifier fixed later + ); + } else { + if (spvVersion.vulkan == 0) + stageBuiltins[EShLangVertex].append( + "in highp int gl_VertexID;" // needs qualifier fixed later + "in highp int gl_InstanceID;" // needs qualifier fixed later + ); + if (spvVersion.vulkan > 0) +#endif + stageBuiltins[EShLangVertex].append( + "in highp int gl_VertexIndex;" + "in highp int gl_InstanceIndex;" + ); +#ifndef GLSLANG_WEB + if (version < 310) +#endif + stageBuiltins[EShLangVertex].append( + "highp vec4 gl_Position;" // needs qualifier fixed later + "highp float gl_PointSize;" // needs qualifier fixed later + ); +#ifndef GLSLANG_WEB + else + stageBuiltins[EShLangVertex].append( + "out gl_PerVertex {" + "highp vec4 gl_Position;" // needs qualifier fixed later + "highp float gl_PointSize;" // needs qualifier fixed later + "};" + ); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangVertex].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + if (version >= 300 /* both ES and non-ES */) { + stageBuiltins[EShLangVertex].append( + "in highp uint gl_ViewID_OVR;" // GL_OVR_multiview, GL_OVR_multiview2 + "\n"); + } + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangVertex].append( + "out highp int gl_PrimitiveShadingRateEXT;" // GL_EXT_fragment_shading_rate + "\n"); + } + + //============================================================================ + // + // Define the interface to the geometry shader. + // + //============================================================================ + + if (profile == ECoreProfile || profile == ECompatibilityProfile) { + stageBuiltins[EShLangGeometry].append( + "in gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + stageBuiltins[EShLangGeometry].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "float gl_CullDistance[];" + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + stageBuiltins[EShLangGeometry].append( + "} gl_in[];" + + "in int gl_PrimitiveIDIn;" + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + "\n"); + if (profile == ECompatibilityProfile && version >= 400) + stageBuiltins[EShLangGeometry].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangGeometry].append( + "};" + + "out int gl_PrimitiveID;" + "out int gl_Layer;"); + + if (version >= 150) + stageBuiltins[EShLangGeometry].append( + "out int gl_ViewportIndex;" + ); + + if (profile == ECompatibilityProfile && version < 400) + stageBuiltins[EShLangGeometry].append( + "out vec4 gl_ClipVertex;" + ); + + if (version >= 400) + stageBuiltins[EShLangGeometry].append( + "in int gl_InvocationID;" + ); + + if (version >= 450) + stageBuiltins[EShLangGeometry].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + + stageBuiltins[EShLangGeometry].append("\n"); + } else if (profile == EEsProfile && version >= 310) { + stageBuiltins[EShLangGeometry].append( + "in gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "} gl_in[];" + "\n" + "in highp int gl_PrimitiveIDIn;" + "in highp int gl_InvocationID;" + "\n" + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "};" + "\n" + "out highp int gl_PrimitiveID;" + "out highp int gl_Layer;" + "\n" + ); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangGeometry].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangGeometry].append( + "out highp int gl_PrimitiveShadingRateEXT;" // GL_EXT_fragment_shading_rate + "\n"); + } + + //============================================================================ + // + // Define the interface to the tessellation control shader. + // + //============================================================================ + + if (profile != EEsProfile && version >= 150) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessControl].append( + "in int gl_PatchVerticesIn;" + "in int gl_PrimitiveID;" + "in int gl_InvocationID;" + + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + stageBuiltins[EShLangTessControl].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangTessControl].append( + "float gl_CullDistance[];" + "int gl_ViewportMask[];" // GL_NV_viewport_array2 + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + stageBuiltins[EShLangTessControl].append( + "} gl_out[];" + + "patch out float gl_TessLevelOuter[4];" + "patch out float gl_TessLevelInner[2];" + "\n"); + + if (version >= 410) + stageBuiltins[EShLangTessControl].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + "\n"); + + } else { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessControl].append( + "in highp int gl_PatchVerticesIn;" + "in highp int gl_PrimitiveID;" + "in highp int gl_InvocationID;" + + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + ); + stageBuiltins[EShLangTessControl].append( + "} gl_out[];" + + "patch out highp float gl_TessLevelOuter[4];" + "patch out highp float gl_TessLevelInner[2];" + "patch out highp vec4 gl_BoundingBoxOES[2];" + "patch out highp vec4 gl_BoundingBoxEXT[2];" + "\n"); + if (profile == EEsProfile && version >= 320) { + stageBuiltins[EShLangTessControl].append( + "patch out highp vec4 gl_BoundingBox[2];" + "\n" + ); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangTessControl].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + //============================================================================ + // + // Define the interface to the tessellation evaluation shader. + // + //============================================================================ + + if (profile != EEsProfile && version >= 150) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessEvaluation].append( + "in int gl_PatchVerticesIn;" + "in int gl_PrimitiveID;" + "in vec3 gl_TessCoord;" + + "patch in float gl_TessLevelOuter[4];" + "patch in float gl_TessLevelInner[2];" + + "out gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (version >= 400 && profile == ECompatibilityProfile) + stageBuiltins[EShLangTessEvaluation].append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (version >= 450) + stageBuiltins[EShLangTessEvaluation].append( + "float gl_CullDistance[];" + ); + stageBuiltins[EShLangTessEvaluation].append( + "};" + "\n"); + + if (version >= 410) + stageBuiltins[EShLangTessEvaluation].append( + "out int gl_ViewportIndex;" + "out int gl_Layer;" + "\n"); + + if (version >= 450) + stageBuiltins[EShLangTessEvaluation].append( + "out int gl_ViewportMask[];" // GL_NV_viewport_array2 + "out vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "out int gl_SecondaryViewportMaskNV[];" // GL_NV_stereo_view_rendering + "out vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "out int gl_ViewportMaskPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + + } else if (profile == EEsProfile && version >= 310) { + // Note: "in gl_PerVertex {...} gl_in[gl_MaxPatchVertices];" is declared in initialize() below, + // as it depends on the resource sizing of gl_MaxPatchVertices. + + stageBuiltins[EShLangTessEvaluation].append( + "in highp int gl_PatchVerticesIn;" + "in highp int gl_PrimitiveID;" + "in highp vec3 gl_TessCoord;" + + "patch in highp float gl_TessLevelOuter[4];" + "patch in highp float gl_TessLevelInner[2];" + + "out gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + ); + stageBuiltins[EShLangTessEvaluation].append( + "};" + "\n"); + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangTessEvaluation].append( + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + + //============================================================================ + // + // Define the interface to the fragment shader. + // + //============================================================================ + + if (profile != EEsProfile) { + + stageBuiltins[EShLangFragment].append( + "vec4 gl_FragCoord;" // needs qualifier fixed later + "bool gl_FrontFacing;" // needs qualifier fixed later + "float gl_FragDepth;" // needs qualifier fixed later + ); + if (version >= 120) + stageBuiltins[EShLangFragment].append( + "vec2 gl_PointCoord;" // needs qualifier fixed later + ); + if (version >= 140) + stageBuiltins[EShLangFragment].append( + "out int gl_FragStencilRefARB;" + ); + if (IncludeLegacy(version, profile, spvVersion) || (! ForwardCompatibility && version < 420)) + stageBuiltins[EShLangFragment].append( + "vec4 gl_FragColor;" // needs qualifier fixed later + ); + + if (version < 130) { + stageBuiltins[EShLangFragment].append( + "varying vec4 gl_Color;" + "varying vec4 gl_SecondaryColor;" + "varying vec4 gl_TexCoord[];" + "varying float gl_FogFragCoord;" + ); + } else { + stageBuiltins[EShLangFragment].append( + "in float gl_ClipDistance[];" + ); + + if (IncludeLegacy(version, profile, spvVersion)) { + if (version < 150) + stageBuiltins[EShLangFragment].append( + "in float gl_FogFragCoord;" + "in vec4 gl_TexCoord[];" + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + ); + else + stageBuiltins[EShLangFragment].append( + "in gl_PerFragment {" + "in float gl_FogFragCoord;" + "in vec4 gl_TexCoord[];" + "in vec4 gl_Color;" + "in vec4 gl_SecondaryColor;" + "};" + ); + } + } + + if (version >= 150) + stageBuiltins[EShLangFragment].append( + "flat in int gl_PrimitiveID;" + ); + + if (version >= 130) { // ARB_sample_shading + stageBuiltins[EShLangFragment].append( + "flat in int gl_SampleID;" + " in vec2 gl_SamplePosition;" + " out int gl_SampleMask[];" + ); + + if (spvVersion.spv == 0) { + stageBuiltins[EShLangFragment].append( + "uniform int gl_NumSamples;" + ); + } + } + + if (version >= 400) + stageBuiltins[EShLangFragment].append( + "flat in int gl_SampleMaskIn[];" + ); + + if (version >= 430) + stageBuiltins[EShLangFragment].append( + "flat in int gl_Layer;" + "flat in int gl_ViewportIndex;" + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "in float gl_CullDistance[];" + "bool gl_HelperInvocation;" // needs qualifier fixed later + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( // GL_EXT_fragment_invocation_density + "flat in ivec2 gl_FragSizeEXT;" + "flat in int gl_FragInvocationCountEXT;" + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "in vec2 gl_BaryCoordNoPerspAMD;" + "in vec2 gl_BaryCoordNoPerspCentroidAMD;" + "in vec2 gl_BaryCoordNoPerspSampleAMD;" + "in vec2 gl_BaryCoordSmoothAMD;" + "in vec2 gl_BaryCoordSmoothCentroidAMD;" + "in vec2 gl_BaryCoordSmoothSampleAMD;" + "in vec3 gl_BaryCoordPullModelAMD;" + ); + + if (version >= 430) + stageBuiltins[EShLangFragment].append( + "in bool gl_FragFullyCoveredNV;" + ); + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "flat in ivec2 gl_FragmentSizeNV;" // GL_NV_shading_rate_image + "flat in int gl_InvocationsPerPixelNV;" + "in vec3 gl_BaryCoordNV;" // GL_NV_fragment_shader_barycentric + "in vec3 gl_BaryCoordNoPerspNV;" + ); + + if (version >= 450) + stageBuiltins[EShLangFragment].append( + "flat in int gl_ShadingRateEXT;" // GL_EXT_fragment_shading_rate + ); + + } else { + // ES profile + + if (version == 100) { + stageBuiltins[EShLangFragment].append( + "mediump vec4 gl_FragCoord;" // needs qualifier fixed later + " bool gl_FrontFacing;" // needs qualifier fixed later + "mediump vec4 gl_FragColor;" // needs qualifier fixed later + "mediump vec2 gl_PointCoord;" // needs qualifier fixed later + ); + } +#endif + if (version >= 300) { + stageBuiltins[EShLangFragment].append( + "highp vec4 gl_FragCoord;" // needs qualifier fixed later + " bool gl_FrontFacing;" // needs qualifier fixed later + "mediump vec2 gl_PointCoord;" // needs qualifier fixed later + "highp float gl_FragDepth;" // needs qualifier fixed later + ); + } +#ifndef GLSLANG_WEB + if (version >= 310) { + stageBuiltins[EShLangFragment].append( + "bool gl_HelperInvocation;" // needs qualifier fixed later + "flat in highp int gl_PrimitiveID;" // needs qualifier fixed later + "flat in highp int gl_Layer;" // needs qualifier fixed later + ); + + stageBuiltins[EShLangFragment].append( // GL_OES_sample_variables + "flat in lowp int gl_SampleID;" + " in mediump vec2 gl_SamplePosition;" + "flat in highp int gl_SampleMaskIn[];" + " out highp int gl_SampleMask[];" + ); + if (spvVersion.spv == 0) + stageBuiltins[EShLangFragment].append( // GL_OES_sample_variables + "uniform lowp int gl_NumSamples;" + ); + } + stageBuiltins[EShLangFragment].append( + "highp float gl_FragDepthEXT;" // GL_EXT_frag_depth + ); + + if (version >= 310) + stageBuiltins[EShLangFragment].append( // GL_EXT_fragment_invocation_density + "flat in ivec2 gl_FragSizeEXT;" + "flat in int gl_FragInvocationCountEXT;" + ); + if (version >= 320) + stageBuiltins[EShLangFragment].append( // GL_NV_shading_rate_image + "flat in ivec2 gl_FragmentSizeNV;" + "flat in int gl_InvocationsPerPixelNV;" + ); + if (version >= 320) + stageBuiltins[EShLangFragment].append( + "in vec3 gl_BaryCoordNV;" + "in vec3 gl_BaryCoordNoPerspNV;" + ); + if (version >= 310) + stageBuiltins[EShLangFragment].append( + "flat in highp int gl_ShadingRateEXT;" // GL_EXT_fragment_shading_rate + ); + } +#endif + + stageBuiltins[EShLangFragment].append("\n"); + + if (version >= 130) + add2ndGenerationSamplingImaging(version, profile, spvVersion); + +#ifndef GLSLANG_WEB + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + stageBuiltins[EShLangFragment].append( + "flat in highp int gl_DeviceIndex;" // GL_EXT_device_group + "flat in highp int gl_ViewIndex;" // GL_EXT_multiview + "\n"); + } + +#ifndef GLSLANG_ANGLE + // GL_ARB_shader_ballot + if (profile != EEsProfile && version >= 450) { + const char* ballotDecls = + "uniform uint gl_SubGroupSizeARB;" + "in uint gl_SubGroupInvocationARB;" + "in uint64_t gl_SubGroupEqMaskARB;" + "in uint64_t gl_SubGroupGeMaskARB;" + "in uint64_t gl_SubGroupGtMaskARB;" + "in uint64_t gl_SubGroupLeMaskARB;" + "in uint64_t gl_SubGroupLtMaskARB;" + "\n"; + const char* fragmentBallotDecls = + "uniform uint gl_SubGroupSizeARB;" + "flat in uint gl_SubGroupInvocationARB;" + "flat in uint64_t gl_SubGroupEqMaskARB;" + "flat in uint64_t gl_SubGroupGeMaskARB;" + "flat in uint64_t gl_SubGroupGtMaskARB;" + "flat in uint64_t gl_SubGroupLeMaskARB;" + "flat in uint64_t gl_SubGroupLtMaskARB;" + "\n"; + stageBuiltins[EShLangVertex] .append(ballotDecls); + stageBuiltins[EShLangTessControl] .append(ballotDecls); + stageBuiltins[EShLangTessEvaluation].append(ballotDecls); + stageBuiltins[EShLangGeometry] .append(ballotDecls); + stageBuiltins[EShLangCompute] .append(ballotDecls); + stageBuiltins[EShLangFragment] .append(fragmentBallotDecls); + stageBuiltins[EShLangMeshNV] .append(ballotDecls); + stageBuiltins[EShLangTaskNV] .append(ballotDecls); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + const char* subgroupDecls = + "in mediump uint gl_SubgroupSize;" + "in mediump uint gl_SubgroupInvocationID;" + "in highp uvec4 gl_SubgroupEqMask;" + "in highp uvec4 gl_SubgroupGeMask;" + "in highp uvec4 gl_SubgroupGtMask;" + "in highp uvec4 gl_SubgroupLeMask;" + "in highp uvec4 gl_SubgroupLtMask;" + // GL_NV_shader_sm_builtins + "in highp uint gl_WarpsPerSMNV;" + "in highp uint gl_SMCountNV;" + "in highp uint gl_WarpIDNV;" + "in highp uint gl_SMIDNV;" + "\n"; + const char* fragmentSubgroupDecls = + "flat in mediump uint gl_SubgroupSize;" + "flat in mediump uint gl_SubgroupInvocationID;" + "flat in highp uvec4 gl_SubgroupEqMask;" + "flat in highp uvec4 gl_SubgroupGeMask;" + "flat in highp uvec4 gl_SubgroupGtMask;" + "flat in highp uvec4 gl_SubgroupLeMask;" + "flat in highp uvec4 gl_SubgroupLtMask;" + // GL_NV_shader_sm_builtins + "flat in highp uint gl_WarpsPerSMNV;" + "flat in highp uint gl_SMCountNV;" + "flat in highp uint gl_WarpIDNV;" + "flat in highp uint gl_SMIDNV;" + "\n"; + const char* computeSubgroupDecls = + "in highp uint gl_NumSubgroups;" + "in highp uint gl_SubgroupID;" + "\n"; + + stageBuiltins[EShLangVertex] .append(subgroupDecls); + stageBuiltins[EShLangTessControl] .append(subgroupDecls); + stageBuiltins[EShLangTessEvaluation].append(subgroupDecls); + stageBuiltins[EShLangGeometry] .append(subgroupDecls); + stageBuiltins[EShLangCompute] .append(subgroupDecls); + stageBuiltins[EShLangCompute] .append(computeSubgroupDecls); + stageBuiltins[EShLangFragment] .append(fragmentSubgroupDecls); + stageBuiltins[EShLangMeshNV] .append(subgroupDecls); + stageBuiltins[EShLangMeshNV] .append(computeSubgroupDecls); + stageBuiltins[EShLangTaskNV] .append(subgroupDecls); + stageBuiltins[EShLangTaskNV] .append(computeSubgroupDecls); + stageBuiltins[EShLangRayGen] .append(subgroupDecls); + stageBuiltins[EShLangIntersect] .append(subgroupDecls); + stageBuiltins[EShLangAnyHit] .append(subgroupDecls); + stageBuiltins[EShLangClosestHit] .append(subgroupDecls); + stageBuiltins[EShLangMiss] .append(subgroupDecls); + stageBuiltins[EShLangCallable] .append(subgroupDecls); + } + + // GL_NV_ray_tracing/GL_EXT_ray_tracing + if (profile != EEsProfile && version >= 460) { + + const char *constRayFlags = + "const uint gl_RayFlagsNoneNV = 0U;" + "const uint gl_RayFlagsNoneEXT = 0U;" + "const uint gl_RayFlagsOpaqueNV = 1U;" + "const uint gl_RayFlagsOpaqueEXT = 1U;" + "const uint gl_RayFlagsNoOpaqueNV = 2U;" + "const uint gl_RayFlagsNoOpaqueEXT = 2U;" + "const uint gl_RayFlagsTerminateOnFirstHitNV = 4U;" + "const uint gl_RayFlagsTerminateOnFirstHitEXT = 4U;" + "const uint gl_RayFlagsSkipClosestHitShaderNV = 8U;" + "const uint gl_RayFlagsSkipClosestHitShaderEXT = 8U;" + "const uint gl_RayFlagsCullBackFacingTrianglesNV = 16U;" + "const uint gl_RayFlagsCullBackFacingTrianglesEXT = 16U;" + "const uint gl_RayFlagsCullFrontFacingTrianglesNV = 32U;" + "const uint gl_RayFlagsCullFrontFacingTrianglesEXT = 32U;" + "const uint gl_RayFlagsCullOpaqueNV = 64U;" + "const uint gl_RayFlagsCullOpaqueEXT = 64U;" + "const uint gl_RayFlagsCullNoOpaqueNV = 128U;" + "const uint gl_RayFlagsCullNoOpaqueEXT = 128U;" + "const uint gl_RayFlagsSkipTrianglesEXT = 256U;" + "const uint gl_RayFlagsSkipAABBEXT = 512U;" + "const uint gl_HitKindFrontFacingTriangleEXT = 254U;" + "const uint gl_HitKindBackFacingTriangleEXT = 255U;" + "\n"; + + const char *constRayQueryIntersection = + "const uint gl_RayQueryCandidateIntersectionEXT = 0U;" + "const uint gl_RayQueryCommittedIntersectionEXT = 1U;" + "const uint gl_RayQueryCommittedIntersectionNoneEXT = 0U;" + "const uint gl_RayQueryCommittedIntersectionTriangleEXT = 1U;" + "const uint gl_RayQueryCommittedIntersectionGeneratedEXT = 2U;" + "const uint gl_RayQueryCandidateIntersectionTriangleEXT = 0U;" + "const uint gl_RayQueryCandidateIntersectionAABBEXT = 1U;" + "\n"; + + const char *rayGenDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "\n"; + const char *intersectDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in int gl_PrimitiveID;" + "in int gl_InstanceID;" + "in int gl_InstanceCustomIndexNV;" + "in int gl_InstanceCustomIndexEXT;" + "in int gl_GeometryIndexEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayOriginEXT;" + "in vec3 gl_ObjectRayDirectionNV;" + "in vec3 gl_ObjectRayDirectionEXT;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in float gl_RayTmaxEXT;" + "in mat4x3 gl_ObjectToWorldNV;" + "in mat4x3 gl_ObjectToWorldEXT;" + "in mat3x4 gl_ObjectToWorld3x4EXT;" + "in mat4x3 gl_WorldToObjectNV;" + "in mat4x3 gl_WorldToObjectEXT;" + "in mat3x4 gl_WorldToObject3x4EXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "\n"; + const char *hitDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in int gl_PrimitiveID;" + "in int gl_InstanceID;" + "in int gl_InstanceCustomIndexNV;" + "in int gl_InstanceCustomIndexEXT;" + "in int gl_GeometryIndexEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayOriginEXT;" + "in vec3 gl_ObjectRayDirectionNV;" + "in vec3 gl_ObjectRayDirectionEXT;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in float gl_RayTmaxEXT;" + "in float gl_HitTNV;" + "in float gl_HitTEXT;" + "in uint gl_HitKindNV;" + "in uint gl_HitKindEXT;" + "in mat4x3 gl_ObjectToWorldNV;" + "in mat4x3 gl_ObjectToWorldEXT;" + "in mat3x4 gl_ObjectToWorld3x4EXT;" + "in mat4x3 gl_WorldToObjectNV;" + "in mat4x3 gl_WorldToObjectEXT;" + "in mat3x4 gl_WorldToObject3x4EXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "\n"; + const char *missDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "in vec3 gl_WorldRayOriginNV;" + "in vec3 gl_WorldRayOriginEXT;" + "in vec3 gl_WorldRayDirectionNV;" + "in vec3 gl_WorldRayDirectionEXT;" + "in vec3 gl_ObjectRayOriginNV;" + "in vec3 gl_ObjectRayDirectionNV;" + "in float gl_RayTminNV;" + "in float gl_RayTminEXT;" + "in float gl_RayTmaxNV;" + "in float gl_RayTmaxEXT;" + "in uint gl_IncomingRayFlagsNV;" + "in uint gl_IncomingRayFlagsEXT;" + "\n"; + + const char *callableDecls = + "in uvec3 gl_LaunchIDNV;" + "in uvec3 gl_LaunchIDEXT;" + "in uvec3 gl_LaunchSizeNV;" + "in uvec3 gl_LaunchSizeEXT;" + "\n"; + + + commonBuiltins.append(constRayQueryIntersection); + commonBuiltins.append(constRayFlags); + + stageBuiltins[EShLangRayGen].append(rayGenDecls); + stageBuiltins[EShLangIntersect].append(intersectDecls); + stageBuiltins[EShLangAnyHit].append(hitDecls); + stageBuiltins[EShLangClosestHit].append(hitDecls); + stageBuiltins[EShLangMiss].append(missDecls); + stageBuiltins[EShLangCallable].append(callableDecls); + + } + + if ((profile != EEsProfile && version >= 140)) { + const char *deviceIndex = + "in highp int gl_DeviceIndex;" // GL_EXT_device_group + "\n"; + + stageBuiltins[EShLangRayGen].append(deviceIndex); + stageBuiltins[EShLangIntersect].append(deviceIndex); + stageBuiltins[EShLangAnyHit].append(deviceIndex); + stageBuiltins[EShLangClosestHit].append(deviceIndex); + stageBuiltins[EShLangMiss].append(deviceIndex); + } + + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append("const int gl_ScopeDevice = 1;\n"); + commonBuiltins.append("const int gl_ScopeWorkgroup = 2;\n"); + commonBuiltins.append("const int gl_ScopeSubgroup = 3;\n"); + commonBuiltins.append("const int gl_ScopeInvocation = 4;\n"); + commonBuiltins.append("const int gl_ScopeQueueFamily = 5;\n"); + commonBuiltins.append("const int gl_ScopeShaderCallEXT = 6;\n"); + + commonBuiltins.append("const int gl_SemanticsRelaxed = 0x0;\n"); + commonBuiltins.append("const int gl_SemanticsAcquire = 0x2;\n"); + commonBuiltins.append("const int gl_SemanticsRelease = 0x4;\n"); + commonBuiltins.append("const int gl_SemanticsAcquireRelease = 0x8;\n"); + commonBuiltins.append("const int gl_SemanticsMakeAvailable = 0x2000;\n"); + commonBuiltins.append("const int gl_SemanticsMakeVisible = 0x4000;\n"); + commonBuiltins.append("const int gl_SemanticsVolatile = 0x8000;\n"); + + commonBuiltins.append("const int gl_StorageSemanticsNone = 0x0;\n"); + commonBuiltins.append("const int gl_StorageSemanticsBuffer = 0x40;\n"); + commonBuiltins.append("const int gl_StorageSemanticsShared = 0x100;\n"); + commonBuiltins.append("const int gl_StorageSemanticsImage = 0x800;\n"); + commonBuiltins.append("const int gl_StorageSemanticsOutput = 0x1000;\n"); + } + +#endif // !GLSLANG_ANGLE + + if (version >= 300 /* both ES and non-ES */) { + stageBuiltins[EShLangFragment].append( + "flat in highp uint gl_ViewID_OVR;" // GL_OVR_multiview, GL_OVR_multiview2 + "\n"); + } + + // Adding these to common built-ins triggers an assert due to a memory corruption in related code when testing + // So instead add to each stage individually, avoiding the GLSLang bug + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 310)) { + for (int stage=EShLangVertex; stage(stage)].append("const highp int gl_ShadingRateFlag2VerticalPixelsEXT = 1;\n"); + stageBuiltins[static_cast(stage)].append("const highp int gl_ShadingRateFlag4VerticalPixelsEXT = 2;\n"); + stageBuiltins[static_cast(stage)].append("const highp int gl_ShadingRateFlag2HorizontalPixelsEXT = 4;\n"); + stageBuiltins[static_cast(stage)].append("const highp int gl_ShadingRateFlag4HorizontalPixelsEXT = 8;\n"); + } + } + + // GL_EXT_shader_image_int64 + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) { + + const TBasicType bTypes[] = { EbtInt64, EbtUint64 }; + for (int ms = 0; ms <= 1; ++ms) { // loop over "bool" multisample or not + for (int arrayed = 0; arrayed <= 1; ++arrayed) { // loop over "bool" arrayed or not + for (int dim = Esd1D; dim < EsdSubpass; ++dim) { // 1D, ..., buffer + if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile) + continue; + + if ((dim == Esd3D || dim == EsdRect || dim == EsdBuffer) && arrayed) + continue; + + if (dim != Esd2D && ms) + continue; + + // Loop over the bTypes + for (size_t bType = 0; bType < sizeof(bTypes)/sizeof(TBasicType); ++bType) { + // + // Now, make all the function prototypes for the type we just built... + // + TSampler sampler; + + sampler.setImage(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false, + false, + ms ? true : false); + + TString typeName = sampler.getString(); + + addQueryFunctions(sampler, typeName, version, profile); + addImageFunctions(sampler, typeName, version, profile); + } + } + } + } + } + +#endif // !GLSLANG_WEB + + // printf("%s\n", commonBuiltins.c_str()); + // printf("%s\n", stageBuiltins[EShLangFragment].c_str()); +} + +// +// Helper function for initialize(), to add the second set of names for texturing, +// when adding context-independent built-in functions. +// +void TBuiltIns::add2ndGenerationSamplingImaging(int version, EProfile profile, const SpvVersion& spvVersion) +{ + // + // In this function proper, enumerate the types, then calls the next set of functions + // to enumerate all the uses for that type. + // + + // enumerate all the types + const TBasicType bTypes[] = { EbtFloat, EbtInt, EbtUint, +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + EbtFloat16 +#endif + }; +#ifdef GLSLANG_WEB + bool skipBuffer = true; + bool skipCubeArrayed = true; + const int image = 0; +#else + bool skipBuffer = (profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 140); + bool skipCubeArrayed = (profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 130); + for (int image = 0; image <= 1; ++image) // loop over "bool" image vs sampler +#endif + { + for (int shadow = 0; shadow <= 1; ++shadow) { // loop over "bool" shadow or not +#ifdef GLSLANG_WEB + const int ms = 0; +#else + for (int ms = 0; ms <= 1; ++ms) // loop over "bool" multisample or not +#endif + { + if ((ms || image) && shadow) + continue; + if (ms && profile != EEsProfile && version < 150) + continue; + if (ms && image && profile == EEsProfile) + continue; + if (ms && profile == EEsProfile && version < 310) + continue; + + for (int arrayed = 0; arrayed <= 1; ++arrayed) { // loop over "bool" arrayed or not +#ifdef GLSLANG_WEB + for (int dim = Esd2D; dim <= EsdCube; ++dim) { // 2D, 3D, and Cube +#else +#if defined(GLSLANG_ANGLE) + for (int dim = Esd2D; dim < EsdNumDims; ++dim) { // 2D, ..., buffer, subpass +#else + for (int dim = Esd1D; dim < EsdNumDims; ++dim) { // 1D, ..., buffer, subpass +#endif + if (dim == EsdSubpass && spvVersion.vulkan == 0) + continue; + if (dim == EsdSubpass && (image || shadow || arrayed)) + continue; + if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile) + continue; + if (dim == EsdSubpass && spvVersion.vulkan == 0) + continue; + if (dim == EsdSubpass && (image || shadow || arrayed)) + continue; + if ((dim == Esd1D || dim == EsdRect) && profile == EEsProfile) + continue; + if (dim != Esd2D && dim != EsdSubpass && ms) + continue; + if (dim == EsdBuffer && skipBuffer) + continue; + if (dim == EsdBuffer && (shadow || arrayed || ms)) + continue; + if (ms && arrayed && profile == EEsProfile && version < 310) + continue; +#endif + if (dim == Esd3D && shadow) + continue; + if (dim == EsdCube && arrayed && skipCubeArrayed) + continue; + if ((dim == Esd3D || dim == EsdRect) && arrayed) + continue; + + // Loop over the bTypes + for (size_t bType = 0; bType < sizeof(bTypes)/sizeof(TBasicType); ++bType) { +#ifndef GLSLANG_WEB + if (bTypes[bType] == EbtFloat16 && (profile == EEsProfile || version < 450)) + continue; + if (dim == EsdRect && version < 140 && bType > 0) + continue; +#endif + if (shadow && (bTypes[bType] == EbtInt || bTypes[bType] == EbtUint)) + continue; + // + // Now, make all the function prototypes for the type we just built... + // + TSampler sampler; +#ifndef GLSLANG_WEB + if (dim == EsdSubpass) { + sampler.setSubpass(bTypes[bType], ms ? true : false); + } else +#endif + if (image) { + sampler.setImage(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false, + shadow ? true : false, + ms ? true : false); + } else { + sampler.set(bTypes[bType], (TSamplerDim)dim, arrayed ? true : false, + shadow ? true : false, + ms ? true : false); + } + + TString typeName = sampler.getString(); + +#ifndef GLSLANG_WEB + if (dim == EsdSubpass) { + addSubpassSampling(sampler, typeName, version, profile); + continue; + } +#endif + + addQueryFunctions(sampler, typeName, version, profile); + + if (image) + addImageFunctions(sampler, typeName, version, profile); + else { + addSamplingFunctions(sampler, typeName, version, profile); +#ifndef GLSLANG_WEB + addGatherFunctions(sampler, typeName, version, profile); + if (spvVersion.vulkan > 0 && sampler.isCombined() && !sampler.shadow) { + // Base Vulkan allows texelFetch() for + // textureBuffer (i.e. without sampler). + // + // GL_EXT_samplerless_texture_functions + // allows texelFetch() and query functions + // (other than textureQueryLod()) for all + // texture types. + sampler.setTexture(sampler.type, sampler.dim, sampler.arrayed, sampler.shadow, + sampler.ms); + TString textureTypeName = sampler.getString(); + addSamplingFunctions(sampler, textureTypeName, version, profile); + addQueryFunctions(sampler, textureTypeName, version, profile); + } +#endif + } + } + } + } + } + } + } + + // + // sparseTexelsResidentARB() + // + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append("bool sparseTexelsResidentARB(int code);\n"); + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the query functions for the given type. +// +void TBuiltIns::addQueryFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ + // + // textureSize() and imageSize() + // + + int sizeDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0) - (sampler.dim == EsdCube ? 1 : 0); + +#ifdef GLSLANG_WEB + commonBuiltins.append("highp "); + commonBuiltins.append("ivec"); + commonBuiltins.append(postfixes[sizeDims]); + commonBuiltins.append(" textureSize("); + commonBuiltins.append(typeName); + commonBuiltins.append(",int);\n"); + return; +#endif + + if (sampler.isImage() && ((profile == EEsProfile && version < 310) || (profile != EEsProfile && version < 420))) + return; + + if (profile == EEsProfile) + commonBuiltins.append("highp "); + if (sizeDims == 1) + commonBuiltins.append("int"); + else { + commonBuiltins.append("ivec"); + commonBuiltins.append(postfixes[sizeDims]); + } + if (sampler.isImage()) + commonBuiltins.append(" imageSize(readonly writeonly volatile coherent "); + else + commonBuiltins.append(" textureSize("); + commonBuiltins.append(typeName); + if (! sampler.isImage() && ! sampler.isRect() && ! sampler.isBuffer() && ! sampler.isMultiSample()) + commonBuiltins.append(",int);\n"); + else + commonBuiltins.append(");\n"); + + // + // textureSamples() and imageSamples() + // + + // GL_ARB_shader_texture_image_samples + // TODO: spec issue? there are no memory qualifiers; how to query a writeonly/readonly image, etc? + if (profile != EEsProfile && version >= 430 && sampler.isMultiSample()) { + commonBuiltins.append("int "); + if (sampler.isImage()) + commonBuiltins.append("imageSamples(readonly writeonly volatile coherent "); + else + commonBuiltins.append("textureSamples("); + commonBuiltins.append(typeName); + commonBuiltins.append(");\n"); + } + + // + // textureQueryLod(), fragment stage only + // Also enabled with extension GL_ARB_texture_query_lod + + if (profile != EEsProfile && version >= 150 && sampler.isCombined() && sampler.dim != EsdRect && + ! sampler.isMultiSample() && ! sampler.isBuffer()) { + for (int f16TexAddr = 0; f16TexAddr < 2; ++f16TexAddr) { + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + stageBuiltins[EShLangFragment].append("vec2 textureQueryLod("); + stageBuiltins[EShLangFragment].append(typeName); + if (dimMap[sampler.dim] == 1) + if (f16TexAddr) + stageBuiltins[EShLangFragment].append(", float16_t"); + else + stageBuiltins[EShLangFragment].append(", float"); + else { + if (f16TexAddr) + stageBuiltins[EShLangFragment].append(", f16vec"); + else + stageBuiltins[EShLangFragment].append(", vec"); + stageBuiltins[EShLangFragment].append(postfixes[dimMap[sampler.dim]]); + } + stageBuiltins[EShLangFragment].append(");\n"); + } + + stageBuiltins[EShLangCompute].append("vec2 textureQueryLod("); + stageBuiltins[EShLangCompute].append(typeName); + if (dimMap[sampler.dim] == 1) + stageBuiltins[EShLangCompute].append(", float"); + else { + stageBuiltins[EShLangCompute].append(", vec"); + stageBuiltins[EShLangCompute].append(postfixes[dimMap[sampler.dim]]); + } + stageBuiltins[EShLangCompute].append(");\n"); + } + + // + // textureQueryLevels() + // + + if (profile != EEsProfile && version >= 430 && ! sampler.isImage() && sampler.dim != EsdRect && + ! sampler.isMultiSample() && ! sampler.isBuffer()) { + commonBuiltins.append("int textureQueryLevels("); + commonBuiltins.append(typeName); + commonBuiltins.append(");\n"); + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the image access functions for the given type. +// +void TBuiltIns::addImageFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ + int dims = dimMap[sampler.dim]; + // most things with an array add a dimension, except for cubemaps + if (sampler.arrayed && sampler.dim != EsdCube) + ++dims; + + TString imageParams = typeName; + if (dims == 1) + imageParams.append(", int"); + else { + imageParams.append(", ivec"); + imageParams.append(postfixes[dims]); + } + if (sampler.isMultiSample()) + imageParams.append(", int"); + + if (profile == EEsProfile) + commonBuiltins.append("highp "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4 imageLoad(readonly volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(");\n"); + + commonBuiltins.append("void imageStore(writeonly volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4);\n"); + + if (! sampler.is1D() && ! sampler.isBuffer() && profile != EEsProfile && version >= 450) { + commonBuiltins.append("int sparseImageLoadARB(readonly volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", out "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4"); + commonBuiltins.append(");\n"); + } + + if ( profile != EEsProfile || + (profile == EEsProfile && version >= 310)) { + if (sampler.type == EbtInt || sampler.type == EbtUint || sampler.type == EbtInt64 || sampler.type == EbtUint64 ) { + + const char* dataType; + switch (sampler.type) { + case(EbtInt): dataType = "highp int"; break; + case(EbtUint): dataType = "highp uint"; break; + case(EbtInt64): dataType = "highp int64_t"; break; + case(EbtUint64): dataType = "highp uint64_t"; break; + default: dataType = ""; + } + + const int numBuiltins = 7; + + static const char* atomicFunc[numBuiltins] = { + " imageAtomicAdd(volatile coherent ", + " imageAtomicMin(volatile coherent ", + " imageAtomicMax(volatile coherent ", + " imageAtomicAnd(volatile coherent ", + " imageAtomicOr(volatile coherent ", + " imageAtomicXor(volatile coherent ", + " imageAtomicExchange(volatile coherent " + }; + + // Loop twice to add prototypes with/without scope/semantics + for (int j = 0; j < 2; ++j) { + for (size_t i = 0; i < numBuiltins; ++i) { + commonBuiltins.append(dataType); + commonBuiltins.append(atomicFunc[i]); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + if (j == 1) { + commonBuiltins.append(", int, int, int"); + } + commonBuiltins.append(");\n"); + } + + commonBuiltins.append(dataType); + commonBuiltins.append(" imageAtomicCompSwap(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + if (j == 1) { + commonBuiltins.append(", int, int, int, int, int"); + } + commonBuiltins.append(");\n"); + } + + commonBuiltins.append(dataType); + commonBuiltins.append(" imageAtomicLoad(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("void imageAtomicStore(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", "); + commonBuiltins.append(dataType); + commonBuiltins.append(", int, int, int);\n"); + + } else { + // not int or uint + // GL_ARB_ES3_1_compatibility + // TODO: spec issue: are there restrictions on the kind of layout() that can be used? what about dropping memory qualifiers? + if (profile == EEsProfile && version >= 310) { + commonBuiltins.append("float imageAtomicExchange(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + } + if (profile != EEsProfile && version >= 450) { + commonBuiltins.append("float imageAtomicAdd(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + + commonBuiltins.append("float imageAtomicAdd(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float"); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("float imageAtomicExchange(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float);\n"); + + commonBuiltins.append("float imageAtomicExchange(volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float"); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("float imageAtomicLoad(readonly volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", int, int, int);\n"); + + commonBuiltins.append("void imageAtomicStore(writeonly volatile coherent "); + commonBuiltins.append(imageParams); + commonBuiltins.append(", float"); + commonBuiltins.append(", int, int, int);\n"); + } + } + } + + if (sampler.dim == EsdRect || sampler.dim == EsdBuffer || sampler.shadow || sampler.isMultiSample()) + return; + + if (profile == EEsProfile || version < 450) + return; + + TString imageLodParams = typeName; + if (dims == 1) + imageLodParams.append(", int"); + else { + imageLodParams.append(", ivec"); + imageLodParams.append(postfixes[dims]); + } + imageLodParams.append(", int"); + + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4 imageLoadLodAMD(readonly volatile coherent "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(");\n"); + + commonBuiltins.append("void imageStoreLodAMD(writeonly volatile coherent "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(", "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4);\n"); + + if (! sampler.is1D()) { + commonBuiltins.append("int sparseImageLoadLodAMD(readonly volatile coherent "); + commonBuiltins.append(imageLodParams); + commonBuiltins.append(", out "); + commonBuiltins.append(prefixes[sampler.type]); + commonBuiltins.append("vec4"); + commonBuiltins.append(");\n"); + } +} + +// +// Helper function for initialize(), +// when adding context-independent built-in functions. +// +// Add all the subpass access functions for the given type. +// +void TBuiltIns::addSubpassSampling(TSampler sampler, const TString& typeName, int /*version*/, EProfile /*profile*/) +{ + stageBuiltins[EShLangFragment].append(prefixes[sampler.type]); + stageBuiltins[EShLangFragment].append("vec4 subpassLoad"); + stageBuiltins[EShLangFragment].append("("); + stageBuiltins[EShLangFragment].append(typeName.c_str()); + if (sampler.isMultiSample()) + stageBuiltins[EShLangFragment].append(", int"); + stageBuiltins[EShLangFragment].append(");\n"); +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the texture lookup functions for the given type. +// +void TBuiltIns::addSamplingFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#elif defined(GLSLANG_ANGLE) + profile = ECoreProfile; + version = 450; +#endif + + // + // texturing + // + for (int proj = 0; proj <= 1; ++proj) { // loop over "bool" projective or not + + if (proj && (sampler.dim == EsdCube || sampler.isBuffer() || sampler.arrayed || sampler.isMultiSample() + || !sampler.isCombined())) + continue; + + for (int lod = 0; lod <= 1; ++lod) { + + if (lod && (sampler.isBuffer() || sampler.isRect() || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (lod && sampler.dim == Esd2D && sampler.arrayed && sampler.shadow) + continue; + if (lod && sampler.dim == EsdCube && sampler.shadow) + continue; + + for (int bias = 0; bias <= 1; ++bias) { + + if (bias && (lod || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (bias && (sampler.dim == Esd2D || sampler.dim == EsdCube) && sampler.shadow && sampler.arrayed) + continue; + if (bias && (sampler.isRect() || sampler.isBuffer())) + continue; + + for (int offset = 0; offset <= 1; ++offset) { // loop over "bool" offset or not + + if (proj + offset + bias + lod > 3) + continue; + if (offset && (sampler.dim == EsdCube || sampler.isBuffer() || sampler.isMultiSample())) + continue; + + for (int fetch = 0; fetch <= 1; ++fetch) { // loop over "bool" fetch or not + + if (proj + offset + fetch + bias + lod > 3) + continue; + if (fetch && (lod || bias)) + continue; + if (fetch && (sampler.shadow || sampler.dim == EsdCube)) + continue; + if (fetch == 0 && (sampler.isMultiSample() || sampler.isBuffer() + || !sampler.isCombined())) + continue; + + for (int grad = 0; grad <= 1; ++grad) { // loop over "bool" grad or not + + if (grad && (lod || bias || sampler.isMultiSample() || !sampler.isCombined())) + continue; + if (grad && sampler.isBuffer()) + continue; + if (proj + offset + fetch + grad + bias + lod > 3) + continue; + + for (int extraProj = 0; extraProj <= 1; ++extraProj) { + bool compare = false; + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + // skip dummy unused second component for 1D non-array shadows + if (sampler.shadow && totalDims < 2) + totalDims = 2; + totalDims += (sampler.shadow ? 1 : 0) + proj; + if (totalDims > 4 && sampler.shadow) { + compare = true; + totalDims = 4; + } + assert(totalDims <= 4); + + if (extraProj && ! proj) + continue; + if (extraProj && (sampler.dim == Esd3D || sampler.shadow || !sampler.isCombined())) + continue; + + // loop over 16-bit floating-point texel addressing +#if defined(GLSLANG_WEB) || defined(GLSLANG_ANGLE) + const int f16TexAddr = 0; +#else + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) +#endif + { + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + if (f16TexAddr && sampler.shadow && ! compare) { + compare = true; // compare argument is always present + totalDims--; + } + // loop over "bool" lod clamp +#if defined(GLSLANG_WEB) || defined(GLSLANG_ANGLE) + const int lodClamp = 0; +#else + for (int lodClamp = 0; lodClamp <= 1 ;++lodClamp) +#endif + { + if (lodClamp && (profile == EEsProfile || version < 450)) + continue; + if (lodClamp && (proj || lod || fetch)) + continue; + + // loop over "bool" sparse or not +#if defined(GLSLANG_WEB) || defined(GLSLANG_ANGLE) + const int sparse = 0; +#else + for (int sparse = 0; sparse <= 1; ++sparse) +#endif + { + if (sparse && (profile == EEsProfile || version < 450)) + continue; + // Sparse sampling is not for 1D/1D array texture, buffer texture, and + // projective texture + if (sparse && (sampler.is1D() || sampler.isBuffer() || proj)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + if (sampler.shadow) + if (sampler.type == EbtFloat16) + s.append("float16_t "); + else + s.append("float "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + } + + // name + if (sparse) { + if (fetch) + s.append("sparseTexel"); + else + s.append("sparseTexture"); + } + else { + if (fetch) + s.append("texel"); + else + s.append("texture"); + } + if (proj) + s.append("Proj"); + if (lod) + s.append("Lod"); + if (grad) + s.append("Grad"); + if (fetch) + s.append("Fetch"); + if (offset) + s.append("Offset"); + if (lodClamp) + s.append("Clamp"); + if (lodClamp != 0 || sparse) + s.append("ARB"); + s.append("("); + + // sampler type + s.append(typeName); + // P coordinate + if (extraProj) { + if (f16TexAddr) + s.append(",f16vec4"); + else + s.append(",vec4"); + } else { + s.append(","); + TBasicType t = fetch ? EbtInt : (f16TexAddr ? EbtFloat16 : EbtFloat); + if (totalDims == 1) + s.append(TType::getBasicString(t)); + else { + s.append(prefixes[t]); + s.append("vec"); + s.append(postfixes[totalDims]); + } + } + // non-optional compare + if (compare) + s.append(",float"); + + // non-optional lod argument (lod that's not driven by lod loop) or sample + if ((fetch && !sampler.isBuffer() && + !sampler.isRect() && !sampler.isMultiSample()) + || (sampler.isMultiSample() && fetch)) + s.append(",int"); + // non-optional lod + if (lod) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + // gradient arguments + if (grad) { + if (dimMap[sampler.dim] == 1) { + if (f16TexAddr) + s.append(",float16_t,float16_t"); + else + s.append(",float,float"); + } else { + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + s.append(postfixes[dimMap[sampler.dim]]); + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + s.append(postfixes[dimMap[sampler.dim]]); + } + } + // offset + if (offset) { + if (dimMap[sampler.dim] == 1) + s.append(",int"); + else { + s.append(",ivec"); + s.append(postfixes[dimMap[sampler.dim]]); + } + } + + // lod clamp + if (lodClamp) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + if (sampler.shadow) + if (sampler.type == EbtFloat16) + s.append("float16_t"); + else + s.append("float"); + else { + s.append(prefixes[sampler.type]); + s.append("vec4"); + } + } + // optional bias + if (bias) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + s.append(");\n"); + + // Add to the per-language set of built-ins + if (bias || lodClamp != 0) { + stageBuiltins[EShLangFragment].append(s); + stageBuiltins[EShLangCompute].append(s); + } else + commonBuiltins.append(s); + + } + } + } + } + } + } + } + } + } + } +} + +// +// Helper function for add2ndGenerationSamplingImaging(), +// when adding context-independent built-in functions. +// +// Add all the texture gather functions for the given type. +// +void TBuiltIns::addGatherFunctions(TSampler sampler, const TString& typeName, int version, EProfile profile) +{ +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#elif defined(GLSLANG_ANGLE) + profile = ECoreProfile; + version = 450; +#endif + + switch (sampler.dim) { + case Esd2D: + case EsdRect: + case EsdCube: + break; + default: + return; + } + + if (sampler.isMultiSample()) + return; + + if (version < 140 && sampler.dim == EsdRect && sampler.type != EbtFloat) + return; + + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing + + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + for (int offset = 0; offset < 3; ++offset) { // loop over three forms of offset in the call name: none, Offset, and Offsets + + for (int comp = 0; comp < 2; ++comp) { // loop over presence of comp argument + + if (comp > 0 && sampler.shadow) + continue; + + if (offset > 0 && sampler.dim == EsdCube) + continue; + + for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not + if (sparse && (profile == EEsProfile || version < 450)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // name + if (sparse) + s.append("sparseTextureGather"); + else + s.append("textureGather"); + switch (offset) { + case 1: + s.append("Offset"); + break; + case 2: + s.append("Offsets"); + break; + default: + break; + } + if (sparse) + s.append("ARB"); + s.append("("); + + // sampler type argument + s.append(typeName); + + // P coordinate argument + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + s.append(postfixes[totalDims]); + + // refZ argument + if (sampler.shadow) + s.append(",float"); + + // offset argument + if (offset > 0) { + s.append(",ivec2"); + if (offset == 2) + s.append("[4]"); + } + + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // comp argument + if (comp) + s.append(",int"); + + s.append(");\n"); + commonBuiltins.append(s); + } + } + } + } + + if (sampler.dim == EsdRect || sampler.shadow) + return; + + if (profile == EEsProfile || version < 450) + return; + + for (int bias = 0; bias < 2; ++bias) { // loop over presence of bias argument + + for (int lod = 0; lod < 2; ++lod) { // loop over presence of lod argument + + if ((lod && bias) || (lod == 0 && bias == 0)) + continue; + + for (int f16TexAddr = 0; f16TexAddr <= 1; ++f16TexAddr) { // loop over 16-bit floating-point texel addressing + + if (f16TexAddr && sampler.type != EbtFloat16) + continue; + + for (int offset = 0; offset < 3; ++offset) { // loop over three forms of offset in the call name: none, Offset, and Offsets + + for (int comp = 0; comp < 2; ++comp) { // loop over presence of comp argument + + if (comp == 0 && bias) + continue; + + if (offset > 0 && sampler.dim == EsdCube) + continue; + + for (int sparse = 0; sparse <= 1; ++sparse) { // loop over "bool" sparse or not + if (sparse && (profile == EEsProfile || version < 450)) + continue; + + TString s; + + // return type + if (sparse) + s.append("int "); + else { + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // name + if (sparse) + s.append("sparseTextureGather"); + else + s.append("textureGather"); + + if (lod) + s.append("Lod"); + + switch (offset) { + case 1: + s.append("Offset"); + break; + case 2: + s.append("Offsets"); + break; + default: + break; + } + + if (lod) + s.append("AMD"); + else if (sparse) + s.append("ARB"); + + s.append("("); + + // sampler type argument + s.append(typeName); + + // P coordinate argument + if (f16TexAddr) + s.append(",f16vec"); + else + s.append(",vec"); + int totalDims = dimMap[sampler.dim] + (sampler.arrayed ? 1 : 0); + s.append(postfixes[totalDims]); + + // lod argument + if (lod) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + // offset argument + if (offset > 0) { + s.append(",ivec2"); + if (offset == 2) + s.append("[4]"); + } + + // texel out (for sparse texture) + if (sparse) { + s.append(",out "); + s.append(prefixes[sampler.type]); + s.append("vec4 "); + } + + // comp argument + if (comp) + s.append(",int"); + + // bias argument + if (bias) { + if (f16TexAddr) + s.append(",float16_t"); + else + s.append(",float"); + } + + s.append(");\n"); + if (bias) + stageBuiltins[EShLangFragment].append(s); + else + commonBuiltins.append(s); + } + } + } + } + } + } +} + +// +// Add context-dependent built-in functions and variables that are present +// for the given version and profile. All the results are put into just the +// commonBuiltins, because it is called for just a specific stage. So, +// add stage-specific entries to the commonBuiltins, and only if that stage +// was requested. +// +void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language) +{ +#ifdef GLSLANG_WEB + version = 310; + profile = EEsProfile; +#elif defined(GLSLANG_ANGLE) + version = 450; + profile = ECoreProfile; +#endif + + // + // Initialize the context-dependent (resource-dependent) built-in strings for parsing. + // + + //============================================================================ + // + // Standard Uniforms + // + //============================================================================ + + TString& s = commonBuiltins; + const int maxSize = 200; + char builtInConstant[maxSize]; + + // + // Build string of implementation dependent constants. + // + + if (profile == EEsProfile) { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexUniformVectors = %d;", resources.maxVertexUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxFragmentUniformVectors = %d;", resources.maxFragmentUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers); + s.append(builtInConstant); + + if (version == 100) { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVaryingVectors = %d;", resources.maxVaryingVectors); + s.append(builtInConstant); + } else { + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxVertexOutputVectors = %d;", resources.maxVertexOutputVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxFragmentInputVectors = %d;", resources.maxFragmentInputVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MinProgramTexelOffset = %d;", resources.minProgramTexelOffset); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxProgramTexelOffset = %d;", resources.maxProgramTexelOffset); + s.append(builtInConstant); + } + +#ifndef GLSLANG_WEB + if (version >= 310) { + // geometry + + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryInputComponents = %d;", resources.maxGeometryInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputComponents = %d;", resources.maxGeometryOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryImageUniforms = %d;", resources.maxGeometryImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTextureImageUnits = %d;", resources.maxGeometryTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputVertices = %d;", resources.maxGeometryOutputVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTotalOutputComponents = %d;", resources.maxGeometryTotalOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryUniformComponents = %d;", resources.maxGeometryUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounters = %d;", resources.maxGeometryAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounterBuffers = %d;", resources.maxGeometryAtomicCounterBuffers); + s.append(builtInConstant); + + // tessellation + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlInputComponents = %d;", resources.maxTessControlInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlOutputComponents = %d;", resources.maxTessControlOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTextureImageUnits = %d;", resources.maxTessControlTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlUniformComponents = %d;", resources.maxTessControlUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTotalOutputComponents = %d;", resources.maxTessControlTotalOutputComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationInputComponents = %d;", resources.maxTessEvaluationInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationOutputComponents = %d;", resources.maxTessEvaluationOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationTextureImageUnits = %d;", resources.maxTessEvaluationTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationUniformComponents = %d;", resources.maxTessEvaluationUniformComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessPatchComponents = %d;", resources.maxTessPatchComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxPatchVertices = %d;", resources.maxPatchVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessGenLevel = %d;", resources.maxTessGenLevel); + s.append(builtInConstant); + + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxPatchVertices + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + s.append( + "in gl_PerVertex {" + "highp vec4 gl_Position;" + "highp float gl_PointSize;" + "highp vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "highp vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + "} gl_in[gl_MaxPatchVertices];" + "\n"); + } + } + + if (version >= 320) { + // tessellation + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlImageUniforms = %d;", resources.maxTessControlImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationImageUniforms = %d;", resources.maxTessEvaluationImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounters = %d;", resources.maxTessControlAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounters = %d;", resources.maxTessEvaluationAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounterBuffers = %d;", resources.maxTessControlAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounterBuffers = %d;", resources.maxTessEvaluationAtomicCounterBuffers); + s.append(builtInConstant); + } + + if (version >= 100) { + // GL_EXT_blend_func_extended + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxDualSourceDrawBuffersEXT = %d;", resources.maxDualSourceDrawBuffersEXT); + s.append(builtInConstant); + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxDualSourceDrawBuffersEXT + if (language == EShLangFragment) { + s.append( + "mediump vec4 gl_SecondaryFragColorEXT;" + "mediump vec4 gl_SecondaryFragDataEXT[gl_MaxDualSourceDrawBuffersEXT];" + "\n"); + } + } + } else { + // non-ES profile + + if (version > 400) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexUniformVectors = %d;", resources.maxVertexUniformVectors); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentUniformVectors = %d;", resources.maxFragmentUniformVectors); + s.append(builtInConstant); + } + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAttribs = %d;", resources.maxVertexAttribs); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexTextureImageUnits = %d;", resources.maxVertexTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedTextureImageUnits = %d;", resources.maxCombinedTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureImageUnits = %d;", resources.maxTextureImageUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxDrawBuffers = %d;", resources.maxDrawBuffers); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxLights = %d;", resources.maxLights); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxClipPlanes = %d;", resources.maxClipPlanes); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureUnits = %d;", resources.maxTextureUnits); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTextureCoords = %d;", resources.maxTextureCoords); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexUniformComponents = %d;", resources.maxVertexUniformComponents); + s.append(builtInConstant); + + if (version < 150 || ARBCompatibility) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingFloats = %d;", resources.maxVaryingFloats); + s.append(builtInConstant); + } + + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentUniformComponents = %d;", resources.maxFragmentUniformComponents); + s.append(builtInConstant); + + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) { + // + // OpenGL'uniform' state. Page numbers are in reference to version + // 1.4 of the OpenGL specification. + // + + // + // Matrix state. p. 31, 32, 37, 39, 40. + // + s.append("uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords];" + + // + // Derived matrix state that provides inverse and transposed versions + // of the matrices above. + // + "uniform mat4 gl_TextureMatrixInverse[gl_MaxTextureCoords];" + + "uniform mat4 gl_TextureMatrixTranspose[gl_MaxTextureCoords];" + + "uniform mat4 gl_TextureMatrixInverseTranspose[gl_MaxTextureCoords];" + + // + // Clip planes p. 42. + // + "uniform vec4 gl_ClipPlane[gl_MaxClipPlanes];" + + // + // Light State p 50, 53, 55. + // + "uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];" + + // + // Derived state from products of light. + // + "uniform gl_LightProducts gl_FrontLightProduct[gl_MaxLights];" + "uniform gl_LightProducts gl_BackLightProduct[gl_MaxLights];" + + // + // Texture Environment and Generation, p. 152, p. 40-42. + // + "uniform vec4 gl_TextureEnvColor[gl_MaxTextureImageUnits];" + "uniform vec4 gl_EyePlaneS[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneT[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneR[gl_MaxTextureCoords];" + "uniform vec4 gl_EyePlaneQ[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneS[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneT[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneR[gl_MaxTextureCoords];" + "uniform vec4 gl_ObjectPlaneQ[gl_MaxTextureCoords];"); + } + + if (version >= 130) { + snprintf(builtInConstant, maxSize, "const int gl_MaxClipDistances = %d;", resources.maxClipDistances); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVaryingComponents = %d;", resources.maxVaryingComponents); + s.append(builtInConstant); + + // GL_ARB_shading_language_420pack + snprintf(builtInConstant, maxSize, "const mediump int gl_MinProgramTexelOffset = %d;", resources.minProgramTexelOffset); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const mediump int gl_MaxProgramTexelOffset = %d;", resources.maxProgramTexelOffset); + s.append(builtInConstant); + } + + // geometry + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryInputComponents = %d;", resources.maxGeometryInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputComponents = %d;", resources.maxGeometryOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTextureImageUnits = %d;", resources.maxGeometryTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryOutputVertices = %d;", resources.maxGeometryOutputVertices); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryTotalOutputComponents = %d;", resources.maxGeometryTotalOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryUniformComponents = %d;", resources.maxGeometryUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryVaryingComponents = %d;", resources.maxGeometryVaryingComponents); + s.append(builtInConstant); + + } + + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexOutputComponents = %d;", resources.maxVertexOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentInputComponents = %d;", resources.maxFragmentInputComponents); + s.append(builtInConstant); + } + + // tessellation + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlInputComponents = %d;", resources.maxTessControlInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlOutputComponents = %d;", resources.maxTessControlOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTextureImageUnits = %d;", resources.maxTessControlTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlUniformComponents = %d;", resources.maxTessControlUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlTotalOutputComponents = %d;", resources.maxTessControlTotalOutputComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationInputComponents = %d;", resources.maxTessEvaluationInputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationOutputComponents = %d;", resources.maxTessEvaluationOutputComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationTextureImageUnits = %d;", resources.maxTessEvaluationTextureImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationUniformComponents = %d;", resources.maxTessEvaluationUniformComponents); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxTessPatchComponents = %d;", resources.maxTessPatchComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessGenLevel = %d;", resources.maxTessGenLevel); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxPatchVertices = %d;", resources.maxPatchVertices); + s.append(builtInConstant); + + // this is here instead of with the others in initialize(version, profile) due to the dependence on gl_MaxPatchVertices + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + s.append( + "in gl_PerVertex {" + "vec4 gl_Position;" + "float gl_PointSize;" + "float gl_ClipDistance[];" + ); + if (profile == ECompatibilityProfile) + s.append( + "vec4 gl_ClipVertex;" + "vec4 gl_FrontColor;" + "vec4 gl_BackColor;" + "vec4 gl_FrontSecondaryColor;" + "vec4 gl_BackSecondaryColor;" + "vec4 gl_TexCoord[];" + "float gl_FogFragCoord;" + ); + if (profile != EEsProfile && version >= 450) + s.append( + "float gl_CullDistance[];" + "vec4 gl_SecondaryPositionNV;" // GL_NV_stereo_view_rendering + "vec4 gl_PositionPerViewNV[];" // GL_NVX_multiview_per_view_attributes + ); + s.append( + "} gl_in[gl_MaxPatchVertices];" + "\n"); + } + } + + if (version >= 150) { + snprintf(builtInConstant, maxSize, "const int gl_MaxViewports = %d;", resources.maxViewports); + s.append(builtInConstant); + } + + // images + if (version >= 130) { + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedImageUnitsAndFragmentOutputs = %d;", resources.maxCombinedImageUnitsAndFragmentOutputs); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxImageSamples = %d;", resources.maxImageSamples); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlImageUniforms = %d;", resources.maxTessControlImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationImageUniforms = %d;", resources.maxTessEvaluationImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryImageUniforms = %d;", resources.maxGeometryImageUniforms); + s.append(builtInConstant); + } + + // enhanced layouts + if (version >= 430) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTransformFeedbackBuffers = %d;", resources.maxTransformFeedbackBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTransformFeedbackInterleavedComponents = %d;", resources.maxTransformFeedbackInterleavedComponents); + s.append(builtInConstant); + } +#endif + } + + // compute + if ((profile == EEsProfile && version >= 310) || (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxComputeWorkGroupCount = ivec3(%d,%d,%d);", resources.maxComputeWorkGroupCountX, + resources.maxComputeWorkGroupCountY, + resources.maxComputeWorkGroupCountZ); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxComputeWorkGroupSize = ivec3(%d,%d,%d);", resources.maxComputeWorkGroupSizeX, + resources.maxComputeWorkGroupSizeY, + resources.maxComputeWorkGroupSizeZ); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeUniformComponents = %d;", resources.maxComputeUniformComponents); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeTextureImageUnits = %d;", resources.maxComputeTextureImageUnits); + s.append(builtInConstant); + + s.append("\n"); + } + +#ifndef GLSLANG_WEB + // images (some in compute below) + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 130)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxImageUnits = %d;", resources.maxImageUnits); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedShaderOutputResources = %d;", resources.maxCombinedShaderOutputResources); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexImageUniforms = %d;", resources.maxVertexImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentImageUniforms = %d;", resources.maxFragmentImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedImageUniforms = %d;", resources.maxCombinedImageUniforms); + s.append(builtInConstant); + } + + // compute + if ((profile == EEsProfile && version >= 310) || (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeImageUniforms = %d;", resources.maxComputeImageUniforms); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeAtomicCounters = %d;", resources.maxComputeAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxComputeAtomicCounterBuffers = %d;", resources.maxComputeAtomicCounterBuffers); + s.append(builtInConstant); + + s.append("\n"); + } + +#ifndef GLSLANG_ANGLE + // atomic counters (some in compute below) + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 420)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAtomicCounters = %d;", resources. maxVertexAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentAtomicCounters = %d;", resources. maxFragmentAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedAtomicCounters = %d;", resources. maxCombinedAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxAtomicCounterBindings = %d;", resources. maxAtomicCounterBindings); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxVertexAtomicCounterBuffers = %d;", resources. maxVertexAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxFragmentAtomicCounterBuffers = %d;", resources. maxFragmentAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedAtomicCounterBuffers = %d;", resources. maxCombinedAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxAtomicCounterBufferSize = %d;", resources. maxAtomicCounterBufferSize); + s.append(builtInConstant); + } + if (profile != EEsProfile && version >= 420) { + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounters = %d;", resources. maxTessControlAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounters = %d;", resources. maxTessEvaluationAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounters = %d;", resources. maxGeometryAtomicCounters); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessControlAtomicCounterBuffers = %d;", resources. maxTessControlAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxTessEvaluationAtomicCounterBuffers = %d;", resources. maxTessEvaluationAtomicCounterBuffers); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxGeometryAtomicCounterBuffers = %d;", resources. maxGeometryAtomicCounterBuffers); + s.append(builtInConstant); + + s.append("\n"); + } +#endif // !GLSLANG_ANGLE + + // GL_ARB_cull_distance + if (profile != EEsProfile && version >= 450) { + snprintf(builtInConstant, maxSize, "const int gl_MaxCullDistances = %d;", resources.maxCullDistances); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const int gl_MaxCombinedClipAndCullDistances = %d;", resources.maxCombinedClipAndCullDistances); + s.append(builtInConstant); + } + + // GL_ARB_ES3_1_compatibility + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 310)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxSamples = %d;", resources.maxSamples); + s.append(builtInConstant); + } + +#ifndef GLSLANG_ANGLE + // SPV_NV_mesh_shader + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshOutputVerticesNV = %d;", resources.maxMeshOutputVerticesNV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshOutputPrimitivesNV = %d;", resources.maxMeshOutputPrimitivesNV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxMeshWorkGroupSizeNV = ivec3(%d,%d,%d);", resources.maxMeshWorkGroupSizeX_NV, + resources.maxMeshWorkGroupSizeY_NV, + resources.maxMeshWorkGroupSizeZ_NV); + s.append(builtInConstant); + snprintf(builtInConstant, maxSize, "const ivec3 gl_MaxTaskWorkGroupSizeNV = ivec3(%d,%d,%d);", resources.maxTaskWorkGroupSizeX_NV, + resources.maxTaskWorkGroupSizeY_NV, + resources.maxTaskWorkGroupSizeZ_NV); + s.append(builtInConstant); + + snprintf(builtInConstant, maxSize, "const int gl_MaxMeshViewCountNV = %d;", resources.maxMeshViewCountNV); + s.append(builtInConstant); + + s.append("\n"); + } +#endif +#endif + + s.append("\n"); +} + +// +// To support special built-ins that have a special qualifier that cannot be declared textually +// in a shader, like gl_Position. +// +// This lets the type of the built-in be declared textually, and then have just its qualifier be +// updated afterward. +// +// Safe to call even if name is not present. +// +// Only use this for built-in variables that have a special qualifier in TStorageQualifier. +// New built-in variables should use a generic (textually declarable) qualifier in +// TStoraregQualifier and only call BuiltInVariable(). +// +static void SpecialQualifier(const char* name, TStorageQualifier qualifier, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol == nullptr) + return; + + TQualifier& symQualifier = symbol->getWritableType().getQualifier(); + symQualifier.storage = qualifier; + symQualifier.builtIn = builtIn; +} + +// +// To tag built-in variables with their TBuiltInVariable enum. Use this when the +// normal declaration text already gets the qualifier right, and all that's needed +// is setting the builtIn field. This should be the normal way for all new +// built-in variables. +// +// If SpecialQualifier() was called, this does not need to be called. +// +// Safe to call even if name is not present. +// +static void BuiltInVariable(const char* name, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol == nullptr) + return; + + TQualifier& symQualifier = symbol->getWritableType().getQualifier(); + symQualifier.builtIn = builtIn; +} + +// +// For built-in variables inside a named block. +// SpecialQualifier() won't ever go inside a block; their member's qualifier come +// from the qualification of the block. +// +// See comments above for other detail. +// +static void BuiltInVariable(const char* blockName, const char* name, TBuiltInVariable builtIn, TSymbolTable& symbolTable) +{ + TSymbol* symbol = symbolTable.find(blockName); + if (symbol == nullptr) + return; + + TTypeList& structure = *symbol->getWritableType().getWritableStruct(); + for (int i = 0; i < (int)structure.size(); ++i) { + if (structure[i].type->getFieldName().compare(name) == 0) { + structure[i].type->getQualifier().builtIn = builtIn; + return; + } + } +} + +// +// Finish adding/processing context-independent built-in symbols. +// 1) Programmatically add symbols that could not be added by simple text strings above. +// 2) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 3) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable) +{ +#ifdef GLSLANG_WEB + version = 310; + profile = EEsProfile; +#elif defined(GLSLANG_ANGLE) + version = 450; + profile = ECoreProfile; +#endif + + // + // Tag built-in variables and functions with additional qualifier and extension information + // that cannot be declared with the text strings. + // + + // N.B.: a symbol should only be tagged once, and this function is called multiple times, once + // per stage that's used for this profile. So + // - generally, stick common ones in the fragment stage to ensure they are tagged exactly once + // - for ES, which has different precisions for different stages, the coarsest-grained tagging + // for a built-in used in many stages needs to be once for the fragment stage and once for + // the vertex stage + + switch(language) { + case EShLangVertex: + if (spvVersion.vulkan > 0) { + BuiltInVariable("gl_VertexIndex", EbvVertexIndex, symbolTable); + BuiltInVariable("gl_InstanceIndex", EbvInstanceIndex, symbolTable); + } + +#ifndef GLSLANG_WEB + if (spvVersion.vulkan == 0) { + SpecialQualifier("gl_VertexID", EvqVertexId, EbvVertexId, symbolTable); + SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable); + } + + if (profile != EEsProfile) { + if (version >= 440) { + symbolTable.setVariableExtensions("gl_BaseVertexARB", 1, &E_GL_ARB_shader_draw_parameters); + symbolTable.setVariableExtensions("gl_BaseInstanceARB", 1, &E_GL_ARB_shader_draw_parameters); + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_BaseVertexARB", EbvBaseVertex, symbolTable); + BuiltInVariable("gl_BaseInstanceARB", EbvBaseInstance, symbolTable); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + } + if (version >= 460) { + BuiltInVariable("gl_BaseVertex", EbvBaseVertex, symbolTable); + BuiltInVariable("gl_BaseInstance", EbvBaseInstance, symbolTable); + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + symbolTable.setFunctionExtensions("ballotARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setFunctionExtensions("readInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setFunctionExtensions("readFirstInvocationARB", 1, &E_GL_ARB_shader_ballot); + + if (version >= 430) { + symbolTable.setFunctionExtensions("anyInvocationARB", 1, &E_GL_ARB_shader_group_vote); + symbolTable.setFunctionExtensions("allInvocationsARB", 1, &E_GL_ARB_shader_group_vote); + symbolTable.setFunctionExtensions("allInvocationsEqualARB", 1, &E_GL_ARB_shader_group_vote); + } + } + + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("minInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("swizzleInvocationsAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("swizzleInvocationsWithPatternAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("writeInvocationAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("mbcntAMD", 1, &E_GL_AMD_shader_ballot); + + symbolTable.setFunctionExtensions("minInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsInclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsInclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsExclusiveScanAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("minInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("maxInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + symbolTable.setFunctionExtensions("addInvocationsExclusiveScanNonUniformAMD", 1, &E_GL_AMD_shader_ballot); + } + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("min3", 1, &E_GL_AMD_shader_trinary_minmax); + symbolTable.setFunctionExtensions("max3", 1, &E_GL_AMD_shader_trinary_minmax); + symbolTable.setFunctionExtensions("mid3", 1, &E_GL_AMD_shader_trinary_minmax); + } + + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SIMDGroupSizeAMD", 1, &E_GL_AMD_gcn_shader); + SpecialQualifier("gl_SIMDGroupSizeAMD", EvqVaryingIn, EbvSubGroupSize, symbolTable); + + symbolTable.setFunctionExtensions("cubeFaceIndexAMD", 1, &E_GL_AMD_gcn_shader); + symbolTable.setFunctionExtensions("cubeFaceCoordAMD", 1, &E_GL_AMD_gcn_shader); + symbolTable.setFunctionExtensions("timeAMD", 1, &E_GL_AMD_gcn_shader); + } + + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("fragmentMaskFetchAMD", 1, &E_GL_AMD_shader_fragment_mask); + symbolTable.setFunctionExtensions("fragmentFetchAMD", 1, &E_GL_AMD_shader_fragment_mask); + } + + symbolTable.setFunctionExtensions("countLeadingZeros", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("countTrailingZeros", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("absoluteDifference", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("addSaturate", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("subtractSaturate", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("average", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("averageRounded", 1, &E_GL_INTEL_shader_integer_functions2); + symbolTable.setFunctionExtensions("multiply32x16", 1, &E_GL_INTEL_shader_integer_functions2); + + symbolTable.setFunctionExtensions("textureFootprintNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintClampNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintLodNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintGradNV", 1, &E_GL_NV_shader_texture_footprint); + symbolTable.setFunctionExtensions("textureFootprintGradClampNV", 1, &E_GL_NV_shader_texture_footprint); + // Compatibility variables, vertex only + if (spvVersion.spv == 0) { + BuiltInVariable("gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_SecondaryColor", EbvSecondaryColor, symbolTable); + BuiltInVariable("gl_Normal", EbvNormal, symbolTable); + BuiltInVariable("gl_Vertex", EbvVertex, symbolTable); + BuiltInVariable("gl_MultiTexCoord0", EbvMultiTexCoord0, symbolTable); + BuiltInVariable("gl_MultiTexCoord1", EbvMultiTexCoord1, symbolTable); + BuiltInVariable("gl_MultiTexCoord2", EbvMultiTexCoord2, symbolTable); + BuiltInVariable("gl_MultiTexCoord3", EbvMultiTexCoord3, symbolTable); + BuiltInVariable("gl_MultiTexCoord4", EbvMultiTexCoord4, symbolTable); + BuiltInVariable("gl_MultiTexCoord5", EbvMultiTexCoord5, symbolTable); + BuiltInVariable("gl_MultiTexCoord6", EbvMultiTexCoord6, symbolTable); + BuiltInVariable("gl_MultiTexCoord7", EbvMultiTexCoord7, symbolTable); + BuiltInVariable("gl_FogCoord", EbvFogFragCoord, symbolTable); + } + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture2DGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradEXT", 1, &E_GL_EXT_shader_texture_lod); + if (version == 310) + symbolTable.setFunctionExtensions("textureGatherOffsets", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + if (version == 310) + symbolTable.setFunctionExtensions("fma", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + + if (profile == EEsProfile && version < 320) { + symbolTable.setFunctionExtensions("imageAtomicAdd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMin", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMax", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicAnd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicOr", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicXor", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicExchange", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic); + } + + if (version >= 300 /* both ES and non-ES */) { + symbolTable.setVariableExtensions("gl_ViewID_OVR", Num_OVR_multiview_EXTs, OVR_multiview_EXTs); + BuiltInVariable("gl_ViewID_OVR", EbvViewIndex, symbolTable); + } + + if (profile == EEsProfile) { + symbolTable.setFunctionExtensions("shadow2DEXT", 1, &E_GL_EXT_shadow_samplers); + symbolTable.setFunctionExtensions("shadow2DProjEXT", 1, &E_GL_EXT_shadow_samplers); + } + // Fall through + + case EShLangTessControl: + if (profile == EEsProfile && version >= 310) { + BuiltInVariable("gl_BoundingBoxEXT", EbvBoundingBox, symbolTable); + symbolTable.setVariableExtensions("gl_BoundingBoxEXT", 1, + &E_GL_EXT_primitive_bounding_box); + BuiltInVariable("gl_BoundingBoxOES", EbvBoundingBox, symbolTable); + symbolTable.setVariableExtensions("gl_BoundingBoxOES", 1, + &E_GL_OES_primitive_bounding_box); + + if (version >= 320) { + BuiltInVariable("gl_BoundingBox", EbvBoundingBox, symbolTable); + } + } + // Fall through + + case EShLangTessEvaluation: + case EShLangGeometry: +#endif // !GLSLANG_WEB + SpecialQualifier("gl_Position", EvqPosition, EbvPosition, symbolTable); + SpecialQualifier("gl_PointSize", EvqPointSize, EbvPointSize, symbolTable); + + BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable); + + BuiltInVariable("gl_out", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_out", "gl_PointSize", EbvPointSize, symbolTable); + +#ifndef GLSLANG_WEB + SpecialQualifier("gl_ClipVertex", EvqClipVertex, EbvClipVertex, symbolTable); + + BuiltInVariable("gl_in", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_in", "gl_CullDistance", EbvCullDistance, symbolTable); + + BuiltInVariable("gl_out", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_out", "gl_CullDistance", EbvCullDistance, symbolTable); + + BuiltInVariable("gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_CullDistance", EbvCullDistance, symbolTable); + BuiltInVariable("gl_PrimitiveIDIn", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_InvocationID", EbvInvocationId, symbolTable); + BuiltInVariable("gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_ViewportIndex", EbvViewportIndex, symbolTable); + + if (language != EShLangGeometry) { + symbolTable.setVariableExtensions("gl_Layer", Num_viewportEXTs, viewportEXTs); + symbolTable.setVariableExtensions("gl_ViewportIndex", Num_viewportEXTs, viewportEXTs); + } + symbolTable.setVariableExtensions("gl_ViewportMask", 1, &E_GL_NV_viewport_array2); + symbolTable.setVariableExtensions("gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + symbolTable.setVariableExtensions("gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_ViewportMask", EbvViewportMaskNV, symbolTable); + BuiltInVariable("gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable); + BuiltInVariable("gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + if (language == EShLangVertex || language == EShLangGeometry) { + symbolTable.setVariableExtensions("gl_in", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_in", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_in", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + } + symbolTable.setVariableExtensions("gl_out", "gl_ViewportMask", 1, &E_GL_NV_viewport_array2); + symbolTable.setVariableExtensions("gl_out", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_out", "gl_SecondaryViewportMaskNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_out", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + symbolTable.setVariableExtensions("gl_out", "gl_ViewportMaskPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_out", "gl_ViewportMask", EbvViewportMaskNV, symbolTable); + BuiltInVariable("gl_out", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_out", "gl_SecondaryViewportMaskNV", EbvSecondaryViewportMaskNV, symbolTable); + BuiltInVariable("gl_out", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_out", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + BuiltInVariable("gl_PatchVerticesIn", EbvPatchVertices, symbolTable); + BuiltInVariable("gl_TessLevelOuter", EbvTessLevelOuter, symbolTable); + BuiltInVariable("gl_TessLevelInner", EbvTessLevelInner, symbolTable); + BuiltInVariable("gl_TessCoord", EbvTessCoord, symbolTable); + + if (version < 410) + symbolTable.setVariableExtensions("gl_ViewportIndex", 1, &E_GL_ARB_viewport_array); + + // Compatibility variables + + BuiltInVariable("gl_in", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_in", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_in", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + BuiltInVariable("gl_out", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_out", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_out", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_out", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_out", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_out", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_out", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + BuiltInVariable("gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + // gl_PointSize, when it needs to be tied to an extension, is always a member of a block. + // (Sometimes with an instance name, sometimes anonymous). + if (profile == EEsProfile) { + if (language == EShLangGeometry) { + symbolTable.setVariableExtensions("gl_PointSize", Num_AEP_geometry_point_size, AEP_geometry_point_size); + symbolTable.setVariableExtensions("gl_in", "gl_PointSize", Num_AEP_geometry_point_size, AEP_geometry_point_size); + } else if (language == EShLangTessEvaluation || language == EShLangTessControl) { + // gl_in tessellation settings of gl_PointSize are in the context-dependent paths + symbolTable.setVariableExtensions("gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + symbolTable.setVariableExtensions("gl_out", "gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + } + } + + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + } + + if (profile != EEsProfile) { + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + + if (language == EShLangGeometry || language == EShLangVertex) { + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_PrimitiveShadingRateEXT", 1, &E_GL_EXT_fragment_shading_rate); + BuiltInVariable("gl_PrimitiveShadingRateEXT", EbvPrimitiveShadingRateKHR, symbolTable); + + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + } + +#endif // !GLSLANG_WEB + break; + + case EShLangFragment: + SpecialQualifier("gl_FrontFacing", EvqFace, EbvFace, symbolTable); + SpecialQualifier("gl_FragCoord", EvqFragCoord, EbvFragCoord, symbolTable); + SpecialQualifier("gl_PointCoord", EvqPointCoord, EbvPointCoord, symbolTable); + if (spvVersion.spv == 0) + SpecialQualifier("gl_FragColor", EvqFragColor, EbvFragColor, symbolTable); + else { + TSymbol* symbol = symbolTable.find("gl_FragColor"); + if (symbol) { + symbol->getWritableType().getQualifier().storage = EvqVaryingOut; + symbol->getWritableType().getQualifier().layoutLocation = 0; + } + } + SpecialQualifier("gl_FragDepth", EvqFragDepth, EbvFragDepth, symbolTable); +#ifndef GLSLANG_WEB + SpecialQualifier("gl_FragDepthEXT", EvqFragDepth, EbvFragDepth, symbolTable); + SpecialQualifier("gl_HelperInvocation", EvqVaryingIn, EbvHelperInvocation, symbolTable); + + BuiltInVariable("gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_CullDistance", EbvCullDistance, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + + if (profile != EEsProfile && version >= 140) { + symbolTable.setVariableExtensions("gl_FragStencilRefARB", 1, &E_GL_ARB_shader_stencil_export); + BuiltInVariable("gl_FragStencilRefARB", EbvFragStencilRef, symbolTable); + } + + if (profile != EEsProfile && version < 400) { + symbolTable.setFunctionExtensions("textureQueryLod", 1, &E_GL_ARB_texture_query_lod); + } + + if (profile != EEsProfile && version >= 460) { + symbolTable.setFunctionExtensions("rayQueryInitializeEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryTerminateEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGenerateIntersectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryConfirmIntersectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryProceedEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionTypeEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionTEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetRayFlagsEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetRayTMinEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceCustomIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceIdEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionGeometryIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionPrimitiveIndexEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionBarycentricsEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionFrontFaceEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionCandidateAABBOpaqueEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectRayDirectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectRayOriginEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionObjectToWorldEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetIntersectionWorldToObjectEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetWorldRayOriginEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setFunctionExtensions("rayQueryGetWorldRayDirectionEXT", 1, &E_GL_EXT_ray_query); + symbolTable.setVariableExtensions("gl_RayFlagsSkipAABBEXT", 1, &E_GL_EXT_ray_flags_primitive_culling); + symbolTable.setVariableExtensions("gl_RayFlagsSkipTrianglesEXT", 1, &E_GL_EXT_ray_flags_primitive_culling); + } + + if ((profile != EEsProfile && version >= 130) || + (profile == EEsProfile && version >= 310)) { + BuiltInVariable("gl_SampleID", EbvSampleId, symbolTable); + BuiltInVariable("gl_SamplePosition", EbvSamplePosition, symbolTable); + BuiltInVariable("gl_SampleMask", EbvSampleMask, symbolTable); + + if (profile != EEsProfile && version < 400) { + BuiltInVariable("gl_NumSamples", EbvSampleMask, symbolTable); + + symbolTable.setVariableExtensions("gl_SampleMask", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_SampleID", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_SamplePosition", 1, &E_GL_ARB_sample_shading); + symbolTable.setVariableExtensions("gl_NumSamples", 1, &E_GL_ARB_sample_shading); + } else { + BuiltInVariable("gl_SampleMaskIn", EbvSampleMask, symbolTable); + + if (profile == EEsProfile && version < 320) { + symbolTable.setVariableExtensions("gl_SampleID", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SamplePosition", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SampleMaskIn", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_SampleMask", 1, &E_GL_OES_sample_variables); + symbolTable.setVariableExtensions("gl_NumSamples", 1, &E_GL_OES_sample_variables); + } + } + } + + BuiltInVariable("gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_ViewportIndex", EbvViewportIndex, symbolTable); + + // Compatibility variables + + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_in", "gl_SecondaryColor", EbvSecondaryColor, symbolTable); + + BuiltInVariable("gl_FogFragCoord", EbvFogFragCoord, symbolTable); + BuiltInVariable("gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_Color", EbvColor, symbolTable); + BuiltInVariable("gl_SecondaryColor", EbvSecondaryColor, symbolTable); + + // built-in functions + + if (profile == EEsProfile) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture2DLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeLodEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradEXT", 1, &E_GL_EXT_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradEXT", 1, &E_GL_EXT_shader_texture_lod); + if (version < 320) + symbolTable.setFunctionExtensions("textureGatherOffsets", Num_AEP_gpu_shader5, AEP_gpu_shader5); + } + if (version == 100) { + symbolTable.setFunctionExtensions("dFdx", 1, &E_GL_OES_standard_derivatives); + symbolTable.setFunctionExtensions("dFdy", 1, &E_GL_OES_standard_derivatives); + symbolTable.setFunctionExtensions("fwidth", 1, &E_GL_OES_standard_derivatives); + } + if (version == 310) { + symbolTable.setFunctionExtensions("fma", Num_AEP_gpu_shader5, AEP_gpu_shader5); + symbolTable.setFunctionExtensions("interpolateAtCentroid", 1, &E_GL_OES_shader_multisample_interpolation); + symbolTable.setFunctionExtensions("interpolateAtSample", 1, &E_GL_OES_shader_multisample_interpolation); + symbolTable.setFunctionExtensions("interpolateAtOffset", 1, &E_GL_OES_shader_multisample_interpolation); + } + } else if (version < 130) { + if (spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture1DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture1DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DProjLod", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DProjLod", 1, &E_GL_ARB_shader_texture_lod); + } + } + + // E_GL_ARB_shader_texture_lod functions usable only with the extension enabled + if (profile != EEsProfile && spvVersion.spv == 0) { + symbolTable.setFunctionExtensions("texture1DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture1DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture3DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("textureCubeGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow1DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DRectGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("texture2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DRectGradARB", 1, &E_GL_ARB_shader_texture_lod); + symbolTable.setFunctionExtensions("shadow2DRectProjGradARB", 1, &E_GL_ARB_shader_texture_lod); + } + + // E_GL_ARB_shader_image_load_store + if (profile != EEsProfile && version < 420) + symbolTable.setFunctionExtensions("memoryBarrier", 1, &E_GL_ARB_shader_image_load_store); + // All the image access functions are protected by checks on the type of the first argument. + + // E_GL_ARB_shader_atomic_counters + if (profile != EEsProfile && version < 420) { + symbolTable.setFunctionExtensions("atomicCounterIncrement", 1, &E_GL_ARB_shader_atomic_counters); + symbolTable.setFunctionExtensions("atomicCounterDecrement", 1, &E_GL_ARB_shader_atomic_counters); + symbolTable.setFunctionExtensions("atomicCounter" , 1, &E_GL_ARB_shader_atomic_counters); + } + + // E_GL_ARB_derivative_control + if (profile != EEsProfile && version < 450) { + symbolTable.setFunctionExtensions("dFdxFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdyFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("fwidthFine", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdxCoarse", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("dFdyCoarse", 1, &E_GL_ARB_derivative_control); + symbolTable.setFunctionExtensions("fwidthCoarse", 1, &E_GL_ARB_derivative_control); + } + + // E_GL_ARB_sparse_texture2 + if (profile != EEsProfile) + { + symbolTable.setFunctionExtensions("sparseTextureARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureLodARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelFetchARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelFetchOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureLodOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGradARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGradOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherOffsetARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTextureGatherOffsetsARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseImageLoadARB", 1, &E_GL_ARB_sparse_texture2); + symbolTable.setFunctionExtensions("sparseTexelsResident", 1, &E_GL_ARB_sparse_texture2); + } + + // E_GL_ARB_sparse_texture_clamp + if (profile != EEsProfile) + { + symbolTable.setFunctionExtensions("sparseTextureClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureGradClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("sparseTextureGradOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureGradClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + symbolTable.setFunctionExtensions("textureGradOffsetClampARB", 1, &E_GL_ARB_sparse_texture_clamp); + } + + // E_GL_AMD_shader_explicit_vertex_parameter + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspCentroidAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspSampleAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothCentroidAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordSmoothSampleAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + symbolTable.setVariableExtensions("gl_BaryCoordPullModelAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + + symbolTable.setFunctionExtensions("interpolateAtVertexAMD", 1, &E_GL_AMD_shader_explicit_vertex_parameter); + + BuiltInVariable("gl_BaryCoordNoPerspAMD", EbvBaryCoordNoPersp, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspCentroidAMD", EbvBaryCoordNoPerspCentroid, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspSampleAMD", EbvBaryCoordNoPerspSample, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothAMD", EbvBaryCoordSmooth, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothCentroidAMD", EbvBaryCoordSmoothCentroid, symbolTable); + BuiltInVariable("gl_BaryCoordSmoothSampleAMD", EbvBaryCoordSmoothSample, symbolTable); + BuiltInVariable("gl_BaryCoordPullModelAMD", EbvBaryCoordPullModel, symbolTable); + } + + // E_GL_AMD_texture_gather_bias_lod + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("textureGatherLodAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("textureGatherLodOffsetAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("textureGatherLodOffsetsAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + symbolTable.setFunctionExtensions("sparseTextureGatherLodOffsetsAMD", 1, &E_GL_AMD_texture_gather_bias_lod); + } + + // E_GL_AMD_shader_image_load_store_lod + if (profile != EEsProfile) { + symbolTable.setFunctionExtensions("imageLoadLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + symbolTable.setFunctionExtensions("imageStoreLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + symbolTable.setFunctionExtensions("sparseImageLoadLodAMD", 1, &E_GL_AMD_shader_image_load_store_lod); + } + if (profile != EEsProfile && version >= 430) { + symbolTable.setVariableExtensions("gl_FragFullyCoveredNV", 1, &E_GL_NV_conservative_raster_underestimation); + BuiltInVariable("gl_FragFullyCoveredNV", EbvFragFullyCoveredNV, symbolTable); + } + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + symbolTable.setVariableExtensions("gl_FragmentSizeNV", 1, &E_GL_NV_shading_rate_image); + symbolTable.setVariableExtensions("gl_InvocationsPerPixelNV", 1, &E_GL_NV_shading_rate_image); + BuiltInVariable("gl_FragmentSizeNV", EbvFragmentSizeNV, symbolTable); + BuiltInVariable("gl_InvocationsPerPixelNV", EbvInvocationsPerPixelNV, symbolTable); + symbolTable.setVariableExtensions("gl_BaryCoordNV", 1, &E_GL_NV_fragment_shader_barycentric); + symbolTable.setVariableExtensions("gl_BaryCoordNoPerspNV", 1, &E_GL_NV_fragment_shader_barycentric); + BuiltInVariable("gl_BaryCoordNV", EbvBaryCoordNV, symbolTable); + BuiltInVariable("gl_BaryCoordNoPerspNV", EbvBaryCoordNoPerspNV, symbolTable); + } + + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_FragSizeEXT", 1, &E_GL_EXT_fragment_invocation_density); + symbolTable.setVariableExtensions("gl_FragInvocationCountEXT", 1, &E_GL_EXT_fragment_invocation_density); + BuiltInVariable("gl_FragSizeEXT", EbvFragSizeEXT, symbolTable); + BuiltInVariable("gl_FragInvocationCountEXT", EbvFragInvocationCountEXT, symbolTable); + } + + symbolTable.setVariableExtensions("gl_FragDepthEXT", 1, &E_GL_EXT_frag_depth); + + symbolTable.setFunctionExtensions("clockARB", 1, &E_GL_ARB_shader_clock); + symbolTable.setFunctionExtensions("clock2x32ARB", 1, &E_GL_ARB_shader_clock); + + symbolTable.setFunctionExtensions("clockRealtimeEXT", 1, &E_GL_EXT_shader_realtime_clock); + symbolTable.setFunctionExtensions("clockRealtime2x32EXT", 1, &E_GL_EXT_shader_realtime_clock); + + if (profile == EEsProfile && version < 320) { + symbolTable.setVariableExtensions("gl_PrimitiveID", Num_AEP_geometry_shader, AEP_geometry_shader); + symbolTable.setVariableExtensions("gl_Layer", Num_AEP_geometry_shader, AEP_geometry_shader); + } + + if (profile == EEsProfile && version < 320) { + symbolTable.setFunctionExtensions("imageAtomicAdd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMin", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicMax", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicAnd", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicOr", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicXor", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicExchange", 1, &E_GL_OES_shader_image_atomic); + symbolTable.setFunctionExtensions("imageAtomicCompSwap", 1, &E_GL_OES_shader_image_atomic); + } + + if (profile != EEsProfile && version < 330 ) { + symbolTable.setFunctionExtensions("floatBitsToInt", 1, &E_GL_ARB_shader_bit_encoding); + symbolTable.setFunctionExtensions("floatBitsToUint", 1, &E_GL_ARB_shader_bit_encoding); + symbolTable.setFunctionExtensions("intBitsToFloat", 1, &E_GL_ARB_shader_bit_encoding); + symbolTable.setFunctionExtensions("uintBitsToFloat", 1, &E_GL_ARB_shader_bit_encoding); + } + + if (profile != EEsProfile && version < 430 ) { + symbolTable.setFunctionExtensions("imageSize", 1, &E_GL_ARB_shader_image_size); + } + + // GL_ARB_shader_storage_buffer_object + if (profile != EEsProfile && version < 430 ) { + symbolTable.setFunctionExtensions("atomicAdd", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicMin", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicMax", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicAnd", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicOr", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicXor", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicExchange", 1, &E_GL_ARB_shader_storage_buffer_object); + symbolTable.setFunctionExtensions("atomicCompSwap", 1, &E_GL_ARB_shader_storage_buffer_object); + } + + // GL_ARB_shading_language_packing + if (profile != EEsProfile && version < 400 ) { + symbolTable.setFunctionExtensions("packUnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackUnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packSnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packUnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackSnorm4x8", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackUnorm4x8", 1, &E_GL_ARB_shading_language_packing); + } + if (profile != EEsProfile && version < 420 ) { + symbolTable.setFunctionExtensions("packSnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackSnorm2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("unpackHalf2x16", 1, &E_GL_ARB_shading_language_packing); + symbolTable.setFunctionExtensions("packHalf2x16", 1, &E_GL_ARB_shading_language_packing); + } + + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + if (version >= 300 /* both ES and non-ES */) { + symbolTable.setVariableExtensions("gl_ViewID_OVR", Num_OVR_multiview_EXTs, OVR_multiview_EXTs); + BuiltInVariable("gl_ViewID_OVR", EbvViewIndex, symbolTable); + } + + // GL_ARB_shader_ballot + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupBarrier", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrier", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrierBuffer", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupMemoryBarrierImage", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupElect", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setFunctionExtensions("subgroupAll", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupAny", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupAllEqual", 1, &E_GL_KHR_shader_subgroup_vote); + symbolTable.setFunctionExtensions("subgroupBroadcast", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBroadcastFirst", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallot", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupInverseBallot", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotBitExtract", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotInclusiveBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotExclusiveBitCount", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotFindLSB", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupBallotFindMSB", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setFunctionExtensions("subgroupShuffle", 1, &E_GL_KHR_shader_subgroup_shuffle); + symbolTable.setFunctionExtensions("subgroupShuffleXor", 1, &E_GL_KHR_shader_subgroup_shuffle); + symbolTable.setFunctionExtensions("subgroupShuffleUp", 1, &E_GL_KHR_shader_subgroup_shuffle_relative); + symbolTable.setFunctionExtensions("subgroupShuffleDown", 1, &E_GL_KHR_shader_subgroup_shuffle_relative); + symbolTable.setFunctionExtensions("subgroupAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupInclusiveXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveAdd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMul", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMin", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveMax", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveAnd", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveOr", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupExclusiveXor", 1, &E_GL_KHR_shader_subgroup_arithmetic); + symbolTable.setFunctionExtensions("subgroupClusteredAdd", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMul", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMin", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredMax", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredAnd", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredOr", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupClusteredXor", 1, &E_GL_KHR_shader_subgroup_clustered); + symbolTable.setFunctionExtensions("subgroupQuadBroadcast", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapHorizontal", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapVertical", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupQuadSwapDiagonal", 1, &E_GL_KHR_shader_subgroup_quad); + symbolTable.setFunctionExtensions("subgroupPartitionNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedInclusiveXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveAddNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMulNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMinNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveMaxNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveAndNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveOrNV", 1, &E_GL_NV_shader_subgroup_partitioned); + symbolTable.setFunctionExtensions("subgroupPartitionedExclusiveXorNV", 1, &E_GL_NV_shader_subgroup_partitioned); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + + if (profile == EEsProfile) { + symbolTable.setFunctionExtensions("shadow2DEXT", 1, &E_GL_EXT_shadow_samplers); + symbolTable.setFunctionExtensions("shadow2DProjEXT", 1, &E_GL_EXT_shadow_samplers); + } + + if (spvVersion.vulkan > 0) { + symbolTable.setVariableExtensions("gl_ScopeDevice", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeWorkgroup", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeSubgroup", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_ScopeInvocation", 1, &E_GL_KHR_memory_scope_semantics); + + symbolTable.setVariableExtensions("gl_SemanticsRelaxed", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsAcquire", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsRelease", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsAcquireRelease", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsMakeAvailable", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsMakeVisible", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_SemanticsVolatile", 1, &E_GL_KHR_memory_scope_semantics); + + symbolTable.setVariableExtensions("gl_StorageSemanticsNone", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsBuffer", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsShared", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsImage", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setVariableExtensions("gl_StorageSemanticsOutput", 1, &E_GL_KHR_memory_scope_semantics); + } + + symbolTable.setFunctionExtensions("helperInvocationEXT", 1, &E_GL_EXT_demote_to_helper_invocation); + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateEXT", 1, &E_GL_EXT_fragment_shading_rate); + BuiltInVariable("gl_ShadingRateEXT", EbvShadingRateKHR, symbolTable); + + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } +#endif // !GLSLANG_WEB + break; + + case EShLangCompute: + BuiltInVariable("gl_NumWorkGroups", EbvNumWorkGroups, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + BuiltInVariable("gl_ViewIndex", EbvViewIndex, symbolTable); + +#ifndef GLSLANG_WEB + if ((profile != EEsProfile && version >= 140) || + (profile == EEsProfile && version >= 310)) { + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + symbolTable.setVariableExtensions("gl_ViewIndex", 1, &E_GL_EXT_multiview); + } + + if (profile != EEsProfile && version < 430) { + symbolTable.setVariableExtensions("gl_NumWorkGroups", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_ARB_compute_shader); + + symbolTable.setVariableExtensions("gl_MaxComputeWorkGroupCount", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeWorkGroupSize", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeUniformComponents", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeTextureImageUnits", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeImageUniforms", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeAtomicCounters", 1, &E_GL_ARB_compute_shader); + symbolTable.setVariableExtensions("gl_MaxComputeAtomicCounterBuffers", 1, &E_GL_ARB_compute_shader); + + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierAtomicCounter", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierBuffer", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierImage", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_ARB_compute_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_ARB_compute_shader); + } + + + symbolTable.setFunctionExtensions("controlBarrier", 1, &E_GL_KHR_memory_scope_semantics); + symbolTable.setFunctionExtensions("debugPrintfEXT", 1, &E_GL_EXT_debug_printf); + + // GL_ARB_shader_ballot + if (profile != EEsProfile) { + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + } + + { + const char *coopExt[2] = { E_GL_NV_cooperative_matrix, E_GL_NV_integer_cooperative_matrix }; + symbolTable.setFunctionExtensions("coopMatLoadNV", 2, coopExt); + symbolTable.setFunctionExtensions("coopMatStoreNV", 2, coopExt); + symbolTable.setFunctionExtensions("coopMatMulAddNV", 2, coopExt); + } + + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.setFunctionExtensions("dFdx", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdy", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidth", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdxFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdyFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidthFine", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdxCoarse", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("dFdyCoarse", 1, &E_GL_NV_compute_shader_derivatives); + symbolTable.setFunctionExtensions("fwidthCoarse", 1, &E_GL_NV_compute_shader_derivatives); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } +#endif // !GLSLANG_WEB + break; + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (profile != EEsProfile && version >= 460) { + const char *rtexts[] = { E_GL_NV_ray_tracing, E_GL_EXT_ray_tracing }; + symbolTable.setVariableExtensions("gl_LaunchIDNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchIDEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchSizeNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_LaunchSizeEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_PrimitiveID", 2, rtexts); + symbolTable.setVariableExtensions("gl_InstanceID", 2, rtexts); + symbolTable.setVariableExtensions("gl_InstanceCustomIndexNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_InstanceCustomIndexEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_GeometryIndexEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayOriginNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayOriginEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayDirectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldRayDirectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayOriginNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayOriginEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayDirectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectRayDirectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTminNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTminEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTmaxNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_RayTmaxEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_HitTNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_HitTEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_HitKindNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_HitKindEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorldNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorldEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_ObjectToWorld3x4EXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObjectNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObjectEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_WorldToObject3x4EXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setVariableExtensions("gl_IncomingRayFlagsNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setVariableExtensions("gl_IncomingRayFlagsEXT", 1, &E_GL_EXT_ray_tracing); + + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + + + symbolTable.setFunctionExtensions("traceNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("traceRayEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("reportIntersectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("reportIntersectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("ignoreIntersectionNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("ignoreIntersectionEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("terminateRayNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("terminateRayEXT", 1, &E_GL_EXT_ray_tracing); + symbolTable.setFunctionExtensions("executeCallableNV", 1, &E_GL_NV_ray_tracing); + symbolTable.setFunctionExtensions("executeCallableEXT", 1, &E_GL_EXT_ray_tracing); + + + BuiltInVariable("gl_LaunchIDNV", EbvLaunchId, symbolTable); + BuiltInVariable("gl_LaunchIDEXT", EbvLaunchId, symbolTable); + BuiltInVariable("gl_LaunchSizeNV", EbvLaunchSize, symbolTable); + BuiltInVariable("gl_LaunchSizeEXT", EbvLaunchSize, symbolTable); + BuiltInVariable("gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_InstanceID", EbvInstanceId, symbolTable); + BuiltInVariable("gl_InstanceCustomIndexNV", EbvInstanceCustomIndex,symbolTable); + BuiltInVariable("gl_InstanceCustomIndexEXT", EbvInstanceCustomIndex,symbolTable); + BuiltInVariable("gl_GeometryIndexEXT", EbvGeometryIndex, symbolTable); + BuiltInVariable("gl_WorldRayOriginNV", EbvWorldRayOrigin, symbolTable); + BuiltInVariable("gl_WorldRayOriginEXT", EbvWorldRayOrigin, symbolTable); + BuiltInVariable("gl_WorldRayDirectionNV", EbvWorldRayDirection, symbolTable); + BuiltInVariable("gl_WorldRayDirectionEXT", EbvWorldRayDirection, symbolTable); + BuiltInVariable("gl_ObjectRayOriginNV", EbvObjectRayOrigin, symbolTable); + BuiltInVariable("gl_ObjectRayOriginEXT", EbvObjectRayOrigin, symbolTable); + BuiltInVariable("gl_ObjectRayDirectionNV", EbvObjectRayDirection, symbolTable); + BuiltInVariable("gl_ObjectRayDirectionEXT", EbvObjectRayDirection, symbolTable); + BuiltInVariable("gl_RayTminNV", EbvRayTmin, symbolTable); + BuiltInVariable("gl_RayTminEXT", EbvRayTmin, symbolTable); + BuiltInVariable("gl_RayTmaxNV", EbvRayTmax, symbolTable); + BuiltInVariable("gl_RayTmaxEXT", EbvRayTmax, symbolTable); + BuiltInVariable("gl_HitTNV", EbvHitT, symbolTable); + BuiltInVariable("gl_HitTEXT", EbvHitT, symbolTable); + BuiltInVariable("gl_HitKindNV", EbvHitKind, symbolTable); + BuiltInVariable("gl_HitKindEXT", EbvHitKind, symbolTable); + BuiltInVariable("gl_ObjectToWorldNV", EbvObjectToWorld, symbolTable); + BuiltInVariable("gl_ObjectToWorldEXT", EbvObjectToWorld, symbolTable); + BuiltInVariable("gl_ObjectToWorld3x4EXT", EbvObjectToWorld3x4, symbolTable); + BuiltInVariable("gl_WorldToObjectNV", EbvWorldToObject, symbolTable); + BuiltInVariable("gl_WorldToObjectEXT", EbvWorldToObject, symbolTable); + BuiltInVariable("gl_WorldToObject3x4EXT", EbvWorldToObject3x4, symbolTable); + BuiltInVariable("gl_IncomingRayFlagsNV", EbvIncomingRayFlags, symbolTable); + BuiltInVariable("gl_IncomingRayFlagsEXT", EbvIncomingRayFlags, symbolTable); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + + // GL_KHR_shader_subgroup + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + break; + + case EShLangMeshNV: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + // per-vertex builtins + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_Position", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_PointSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_ClipDistance", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_CullDistance", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshVerticesNV", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_PointSize", EbvPointSize, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_CullDistance", EbvCullDistance, symbolTable); + + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_PositionPerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_ClipDistancePerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshVerticesNV", "gl_CullDistancePerViewNV", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshVerticesNV", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_ClipDistancePerViewNV", EbvClipDistancePerViewNV, symbolTable); + BuiltInVariable("gl_MeshVerticesNV", "gl_CullDistancePerViewNV", EbvCullDistancePerViewNV, symbolTable); + + // per-primitive builtins + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_PrimitiveID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_Layer", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportIndex", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportMask", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshPrimitivesNV", "gl_PrimitiveID", EbvPrimitiveId, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_Layer", EbvLayer, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportIndex", EbvViewportIndex, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportMask", EbvViewportMaskNV, symbolTable); + + // per-view per-primitive builtins + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_LayerPerViewNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshPrimitivesNV", "gl_ViewportMaskPerViewNV", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_MeshPrimitivesNV", "gl_LayerPerViewNV", EbvLayerPerViewNV, symbolTable); + BuiltInVariable("gl_MeshPrimitivesNV", "gl_ViewportMaskPerViewNV", EbvViewportMaskPerViewNV, symbolTable); + + // other builtins + symbolTable.setVariableExtensions("gl_PrimitiveCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_PrimitiveIndicesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewIndicesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_PrimitiveCountNV", EbvPrimitiveCountNV, symbolTable); + BuiltInVariable("gl_PrimitiveIndicesNV", EbvPrimitiveIndicesNV, symbolTable); + BuiltInVariable("gl_MeshViewCountNV", EbvMeshViewCountNV, symbolTable); + BuiltInVariable("gl_MeshViewIndicesNV", EbvMeshViewIndicesNV, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + + // builtin constants + symbolTable.setVariableExtensions("gl_MaxMeshOutputVerticesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshOutputPrimitivesNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshWorkGroupSizeNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshViewCountNV", 1, &E_GL_NV_mesh_shader); + + // builtin functions + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_NV_mesh_shader); + } + + if (profile != EEsProfile && version >= 450) { + // GL_EXT_device_group + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + + // GL_ARB_shader_draw_parameters + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + if (version >= 460) { + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + break; + + case EShLangTaskNV: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.setVariableExtensions("gl_TaskCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupSize", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_WorkGroupID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_GlobalInvocationID", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_LocalInvocationIndex", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewCountNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MeshViewIndicesNV", 1, &E_GL_NV_mesh_shader); + + BuiltInVariable("gl_TaskCountNV", EbvTaskCountNV, symbolTable); + BuiltInVariable("gl_WorkGroupSize", EbvWorkGroupSize, symbolTable); + BuiltInVariable("gl_WorkGroupID", EbvWorkGroupId, symbolTable); + BuiltInVariable("gl_LocalInvocationID", EbvLocalInvocationId, symbolTable); + BuiltInVariable("gl_GlobalInvocationID", EbvGlobalInvocationId, symbolTable); + BuiltInVariable("gl_LocalInvocationIndex", EbvLocalInvocationIndex, symbolTable); + BuiltInVariable("gl_MeshViewCountNV", EbvMeshViewCountNV, symbolTable); + BuiltInVariable("gl_MeshViewIndicesNV", EbvMeshViewIndicesNV, symbolTable); + + symbolTable.setVariableExtensions("gl_MaxTaskWorkGroupSizeNV", 1, &E_GL_NV_mesh_shader); + symbolTable.setVariableExtensions("gl_MaxMeshViewCountNV", 1, &E_GL_NV_mesh_shader); + + symbolTable.setFunctionExtensions("barrier", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("memoryBarrierShared", 1, &E_GL_NV_mesh_shader); + symbolTable.setFunctionExtensions("groupMemoryBarrier", 1, &E_GL_NV_mesh_shader); + } + + if (profile != EEsProfile && version >= 450) { + // GL_EXT_device_group + symbolTable.setVariableExtensions("gl_DeviceIndex", 1, &E_GL_EXT_device_group); + BuiltInVariable("gl_DeviceIndex", EbvDeviceIndex, symbolTable); + + // GL_ARB_shader_draw_parameters + symbolTable.setVariableExtensions("gl_DrawIDARB", 1, &E_GL_ARB_shader_draw_parameters); + BuiltInVariable("gl_DrawIDARB", EbvDrawId, symbolTable); + if (version >= 460) { + BuiltInVariable("gl_DrawID", EbvDrawId, symbolTable); + } + + // GL_ARB_shader_ballot + symbolTable.setVariableExtensions("gl_SubGroupSizeARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupInvocationARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupEqMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupGtMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLeMaskARB", 1, &E_GL_ARB_shader_ballot); + symbolTable.setVariableExtensions("gl_SubGroupLtMaskARB", 1, &E_GL_ARB_shader_ballot); + + BuiltInVariable("gl_SubGroupInvocationARB", EbvSubGroupInvocation, symbolTable); + BuiltInVariable("gl_SubGroupEqMaskARB", EbvSubGroupEqMask, symbolTable); + BuiltInVariable("gl_SubGroupGeMaskARB", EbvSubGroupGeMask, symbolTable); + BuiltInVariable("gl_SubGroupGtMaskARB", EbvSubGroupGtMask, symbolTable); + BuiltInVariable("gl_SubGroupLeMaskARB", EbvSubGroupLeMask, symbolTable); + BuiltInVariable("gl_SubGroupLtMaskARB", EbvSubGroupLtMask, symbolTable); + + if (spvVersion.vulkan > 0) + // Treat "gl_SubGroupSizeARB" as shader input instead of uniform for Vulkan + SpecialQualifier("gl_SubGroupSizeARB", EvqVaryingIn, EbvSubGroupSize, symbolTable); + else + BuiltInVariable("gl_SubGroupSizeARB", EbvSubGroupSize, symbolTable); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.setVariableExtensions("gl_NumSubgroups", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupSize", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupInvocationID", 1, &E_GL_KHR_shader_subgroup_basic); + symbolTable.setVariableExtensions("gl_SubgroupEqMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupGtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLeMask", 1, &E_GL_KHR_shader_subgroup_ballot); + symbolTable.setVariableExtensions("gl_SubgroupLtMask", 1, &E_GL_KHR_shader_subgroup_ballot); + + BuiltInVariable("gl_NumSubgroups", EbvNumSubgroups, symbolTable); + BuiltInVariable("gl_SubgroupID", EbvSubgroupID, symbolTable); + BuiltInVariable("gl_SubgroupSize", EbvSubgroupSize2, symbolTable); + BuiltInVariable("gl_SubgroupInvocationID", EbvSubgroupInvocation2, symbolTable); + BuiltInVariable("gl_SubgroupEqMask", EbvSubgroupEqMask2, symbolTable); + BuiltInVariable("gl_SubgroupGeMask", EbvSubgroupGeMask2, symbolTable); + BuiltInVariable("gl_SubgroupGtMask", EbvSubgroupGtMask2, symbolTable); + BuiltInVariable("gl_SubgroupLeMask", EbvSubgroupLeMask2, symbolTable); + BuiltInVariable("gl_SubgroupLtMask", EbvSubgroupLtMask2, symbolTable); + + symbolTable.setFunctionExtensions("subgroupMemoryBarrierShared", 1, &E_GL_KHR_shader_subgroup_basic); + + // GL_NV_shader_sm_builtins + symbolTable.setVariableExtensions("gl_WarpsPerSMNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMCountNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_WarpIDNV", 1, &E_GL_NV_shader_sm_builtins); + symbolTable.setVariableExtensions("gl_SMIDNV", 1, &E_GL_NV_shader_sm_builtins); + BuiltInVariable("gl_WarpsPerSMNV", EbvWarpsPerSM, symbolTable); + BuiltInVariable("gl_SMCountNV", EbvSMCount, symbolTable); + BuiltInVariable("gl_WarpIDNV", EbvWarpID, symbolTable); + BuiltInVariable("gl_SMIDNV", EbvSMID, symbolTable); + } + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 450)) { + symbolTable.setVariableExtensions("gl_ShadingRateFlag2VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4VerticalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag2HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + symbolTable.setVariableExtensions("gl_ShadingRateFlag4HorizontalPixelsEXT", 1, &E_GL_EXT_fragment_shading_rate); + } + break; +#endif + + default: + assert(false && "Language not supported"); + break; + } + + // + // Next, identify which built-ins have a mapping to an operator. + // If PureOperatorBuiltins is false, those that are not identified as such are + // expected to be resolved through a library of functions, versus as + // operations. + // + + relateTabledBuiltins(version, profile, spvVersion, language, symbolTable); + +#ifndef GLSLANG_WEB + symbolTable.relateToOperator("doubleBitsToInt64", EOpDoubleBitsToInt64); + symbolTable.relateToOperator("doubleBitsToUint64", EOpDoubleBitsToUint64); + symbolTable.relateToOperator("int64BitsToDouble", EOpInt64BitsToDouble); + symbolTable.relateToOperator("uint64BitsToDouble", EOpUint64BitsToDouble); + symbolTable.relateToOperator("halfBitsToInt16", EOpFloat16BitsToInt16); + symbolTable.relateToOperator("halfBitsToUint16", EOpFloat16BitsToUint16); + symbolTable.relateToOperator("float16BitsToInt16", EOpFloat16BitsToInt16); + symbolTable.relateToOperator("float16BitsToUint16", EOpFloat16BitsToUint16); + symbolTable.relateToOperator("int16BitsToFloat16", EOpInt16BitsToFloat16); + symbolTable.relateToOperator("uint16BitsToFloat16", EOpUint16BitsToFloat16); + + symbolTable.relateToOperator("int16BitsToHalf", EOpInt16BitsToFloat16); + symbolTable.relateToOperator("uint16BitsToHalf", EOpUint16BitsToFloat16); + + symbolTable.relateToOperator("packSnorm4x8", EOpPackSnorm4x8); + symbolTable.relateToOperator("unpackSnorm4x8", EOpUnpackSnorm4x8); + symbolTable.relateToOperator("packUnorm4x8", EOpPackUnorm4x8); + symbolTable.relateToOperator("unpackUnorm4x8", EOpUnpackUnorm4x8); + + symbolTable.relateToOperator("packDouble2x32", EOpPackDouble2x32); + symbolTable.relateToOperator("unpackDouble2x32", EOpUnpackDouble2x32); + + symbolTable.relateToOperator("packInt2x32", EOpPackInt2x32); + symbolTable.relateToOperator("unpackInt2x32", EOpUnpackInt2x32); + symbolTable.relateToOperator("packUint2x32", EOpPackUint2x32); + symbolTable.relateToOperator("unpackUint2x32", EOpUnpackUint2x32); + + symbolTable.relateToOperator("packInt2x16", EOpPackInt2x16); + symbolTable.relateToOperator("unpackInt2x16", EOpUnpackInt2x16); + symbolTable.relateToOperator("packUint2x16", EOpPackUint2x16); + symbolTable.relateToOperator("unpackUint2x16", EOpUnpackUint2x16); + + symbolTable.relateToOperator("packInt4x16", EOpPackInt4x16); + symbolTable.relateToOperator("unpackInt4x16", EOpUnpackInt4x16); + symbolTable.relateToOperator("packUint4x16", EOpPackUint4x16); + symbolTable.relateToOperator("unpackUint4x16", EOpUnpackUint4x16); + symbolTable.relateToOperator("packFloat2x16", EOpPackFloat2x16); + symbolTable.relateToOperator("unpackFloat2x16", EOpUnpackFloat2x16); + + symbolTable.relateToOperator("pack16", EOpPack16); + symbolTable.relateToOperator("pack32", EOpPack32); + symbolTable.relateToOperator("pack64", EOpPack64); + + symbolTable.relateToOperator("unpack32", EOpUnpack32); + symbolTable.relateToOperator("unpack16", EOpUnpack16); + symbolTable.relateToOperator("unpack8", EOpUnpack8); + + symbolTable.relateToOperator("controlBarrier", EOpBarrier); + symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierAtomicCounter); + symbolTable.relateToOperator("memoryBarrierImage", EOpMemoryBarrierImage); + + symbolTable.relateToOperator("atomicLoad", EOpAtomicLoad); + symbolTable.relateToOperator("atomicStore", EOpAtomicStore); + + symbolTable.relateToOperator("atomicCounterIncrement", EOpAtomicCounterIncrement); + symbolTable.relateToOperator("atomicCounterDecrement", EOpAtomicCounterDecrement); + symbolTable.relateToOperator("atomicCounter", EOpAtomicCounter); + + symbolTable.relateToOperator("clockARB", EOpReadClockSubgroupKHR); + symbolTable.relateToOperator("clock2x32ARB", EOpReadClockSubgroupKHR); + + symbolTable.relateToOperator("clockRealtimeEXT", EOpReadClockDeviceKHR); + symbolTable.relateToOperator("clockRealtime2x32EXT", EOpReadClockDeviceKHR); + + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("atomicCounterAdd", EOpAtomicCounterAdd); + symbolTable.relateToOperator("atomicCounterSubtract", EOpAtomicCounterSubtract); + symbolTable.relateToOperator("atomicCounterMin", EOpAtomicCounterMin); + symbolTable.relateToOperator("atomicCounterMax", EOpAtomicCounterMax); + symbolTable.relateToOperator("atomicCounterAnd", EOpAtomicCounterAnd); + symbolTable.relateToOperator("atomicCounterOr", EOpAtomicCounterOr); + symbolTable.relateToOperator("atomicCounterXor", EOpAtomicCounterXor); + symbolTable.relateToOperator("atomicCounterExchange", EOpAtomicCounterExchange); + symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCounterCompSwap); + } + + symbolTable.relateToOperator("fma", EOpFma); + symbolTable.relateToOperator("frexp", EOpFrexp); + symbolTable.relateToOperator("ldexp", EOpLdexp); + symbolTable.relateToOperator("uaddCarry", EOpAddCarry); + symbolTable.relateToOperator("usubBorrow", EOpSubBorrow); + symbolTable.relateToOperator("umulExtended", EOpUMulExtended); + symbolTable.relateToOperator("imulExtended", EOpIMulExtended); + symbolTable.relateToOperator("bitfieldExtract", EOpBitfieldExtract); + symbolTable.relateToOperator("bitfieldInsert", EOpBitfieldInsert); + symbolTable.relateToOperator("bitfieldReverse", EOpBitFieldReverse); + symbolTable.relateToOperator("bitCount", EOpBitCount); + symbolTable.relateToOperator("findLSB", EOpFindLSB); + symbolTable.relateToOperator("findMSB", EOpFindMSB); + + symbolTable.relateToOperator("helperInvocationEXT", EOpIsHelperInvocation); + + symbolTable.relateToOperator("countLeadingZeros", EOpCountLeadingZeros); + symbolTable.relateToOperator("countTrailingZeros", EOpCountTrailingZeros); + symbolTable.relateToOperator("absoluteDifference", EOpAbsDifference); + symbolTable.relateToOperator("addSaturate", EOpAddSaturate); + symbolTable.relateToOperator("subtractSaturate", EOpSubSaturate); + symbolTable.relateToOperator("average", EOpAverage); + symbolTable.relateToOperator("averageRounded", EOpAverageRounded); + symbolTable.relateToOperator("multiply32x16", EOpMul32x16); + symbolTable.relateToOperator("debugPrintfEXT", EOpDebugPrintf); + + + if (PureOperatorBuiltins) { + symbolTable.relateToOperator("imageSize", EOpImageQuerySize); + symbolTable.relateToOperator("imageSamples", EOpImageQuerySamples); + symbolTable.relateToOperator("imageLoad", EOpImageLoad); + symbolTable.relateToOperator("imageStore", EOpImageStore); + symbolTable.relateToOperator("imageAtomicAdd", EOpImageAtomicAdd); + symbolTable.relateToOperator("imageAtomicMin", EOpImageAtomicMin); + symbolTable.relateToOperator("imageAtomicMax", EOpImageAtomicMax); + symbolTable.relateToOperator("imageAtomicAnd", EOpImageAtomicAnd); + symbolTable.relateToOperator("imageAtomicOr", EOpImageAtomicOr); + symbolTable.relateToOperator("imageAtomicXor", EOpImageAtomicXor); + symbolTable.relateToOperator("imageAtomicExchange", EOpImageAtomicExchange); + symbolTable.relateToOperator("imageAtomicCompSwap", EOpImageAtomicCompSwap); + symbolTable.relateToOperator("imageAtomicLoad", EOpImageAtomicLoad); + symbolTable.relateToOperator("imageAtomicStore", EOpImageAtomicStore); + + symbolTable.relateToOperator("subpassLoad", EOpSubpassLoad); + symbolTable.relateToOperator("subpassLoadMS", EOpSubpassLoadMS); + + symbolTable.relateToOperator("textureGather", EOpTextureGather); + symbolTable.relateToOperator("textureGatherOffset", EOpTextureGatherOffset); + symbolTable.relateToOperator("textureGatherOffsets", EOpTextureGatherOffsets); + + symbolTable.relateToOperator("noise1", EOpNoise); + symbolTable.relateToOperator("noise2", EOpNoise); + symbolTable.relateToOperator("noise3", EOpNoise); + symbolTable.relateToOperator("noise4", EOpNoise); + + symbolTable.relateToOperator("textureFootprintNV", EOpImageSampleFootprintNV); + symbolTable.relateToOperator("textureFootprintClampNV", EOpImageSampleFootprintClampNV); + symbolTable.relateToOperator("textureFootprintLodNV", EOpImageSampleFootprintLodNV); + symbolTable.relateToOperator("textureFootprintGradNV", EOpImageSampleFootprintGradNV); + symbolTable.relateToOperator("textureFootprintGradClampNV", EOpImageSampleFootprintGradClampNV); + + if (spvVersion.spv == 0 && IncludeLegacy(version, profile, spvVersion)) + symbolTable.relateToOperator("ftransform", EOpFtransform); + + if (spvVersion.spv == 0 && (IncludeLegacy(version, profile, spvVersion) || + (profile == EEsProfile && version == 100))) { + + symbolTable.relateToOperator("texture1D", EOpTexture); + symbolTable.relateToOperator("texture1DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture1DProj", EOpTextureProj); + symbolTable.relateToOperator("texture1DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture1DLod", EOpTextureLod); + symbolTable.relateToOperator("texture1DProjLod", EOpTextureProjLod); + + symbolTable.relateToOperator("texture2DRect", EOpTexture); + symbolTable.relateToOperator("texture2DRectProj", EOpTextureProj); + symbolTable.relateToOperator("texture2DRectGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture2DRectProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow2DRect", EOpTexture); + symbolTable.relateToOperator("shadow2DRectProj", EOpTextureProj); + symbolTable.relateToOperator("shadow2DRectGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow2DRectProjGradARB", EOpTextureProjGrad); + + symbolTable.relateToOperator("texture2D", EOpTexture); + symbolTable.relateToOperator("texture2DProj", EOpTextureProj); + symbolTable.relateToOperator("texture2DGradEXT", EOpTextureGrad); + symbolTable.relateToOperator("texture2DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture2DProjGradEXT", EOpTextureProjGrad); + symbolTable.relateToOperator("texture2DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture2DLod", EOpTextureLod); + symbolTable.relateToOperator("texture2DLodEXT", EOpTextureLod); + symbolTable.relateToOperator("texture2DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("texture2DProjLodEXT", EOpTextureProjLod); + + symbolTable.relateToOperator("texture3D", EOpTexture); + symbolTable.relateToOperator("texture3DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("texture3DProj", EOpTextureProj); + symbolTable.relateToOperator("texture3DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("texture3DLod", EOpTextureLod); + symbolTable.relateToOperator("texture3DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("textureCube", EOpTexture); + symbolTable.relateToOperator("textureCubeGradEXT", EOpTextureGrad); + symbolTable.relateToOperator("textureCubeGradARB", EOpTextureGrad); + symbolTable.relateToOperator("textureCubeLod", EOpTextureLod); + symbolTable.relateToOperator("textureCubeLodEXT", EOpTextureLod); + symbolTable.relateToOperator("shadow1D", EOpTexture); + symbolTable.relateToOperator("shadow1DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow2D", EOpTexture); + symbolTable.relateToOperator("shadow2DGradARB", EOpTextureGrad); + symbolTable.relateToOperator("shadow1DProj", EOpTextureProj); + symbolTable.relateToOperator("shadow2DProj", EOpTextureProj); + symbolTable.relateToOperator("shadow1DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow2DProjGradARB", EOpTextureProjGrad); + symbolTable.relateToOperator("shadow1DLod", EOpTextureLod); + symbolTable.relateToOperator("shadow2DLod", EOpTextureLod); + symbolTable.relateToOperator("shadow1DProjLod", EOpTextureProjLod); + symbolTable.relateToOperator("shadow2DProjLod", EOpTextureProjLod); + } + + if (profile != EEsProfile) { + symbolTable.relateToOperator("sparseTextureARB", EOpSparseTexture); + symbolTable.relateToOperator("sparseTextureLodARB", EOpSparseTextureLod); + symbolTable.relateToOperator("sparseTextureOffsetARB", EOpSparseTextureOffset); + symbolTable.relateToOperator("sparseTexelFetchARB", EOpSparseTextureFetch); + symbolTable.relateToOperator("sparseTexelFetchOffsetARB", EOpSparseTextureFetchOffset); + symbolTable.relateToOperator("sparseTextureLodOffsetARB", EOpSparseTextureLodOffset); + symbolTable.relateToOperator("sparseTextureGradARB", EOpSparseTextureGrad); + symbolTable.relateToOperator("sparseTextureGradOffsetARB", EOpSparseTextureGradOffset); + symbolTable.relateToOperator("sparseTextureGatherARB", EOpSparseTextureGather); + symbolTable.relateToOperator("sparseTextureGatherOffsetARB", EOpSparseTextureGatherOffset); + symbolTable.relateToOperator("sparseTextureGatherOffsetsARB", EOpSparseTextureGatherOffsets); + symbolTable.relateToOperator("sparseImageLoadARB", EOpSparseImageLoad); + symbolTable.relateToOperator("sparseTexelsResidentARB", EOpSparseTexelsResident); + + symbolTable.relateToOperator("sparseTextureClampARB", EOpSparseTextureClamp); + symbolTable.relateToOperator("sparseTextureOffsetClampARB", EOpSparseTextureOffsetClamp); + symbolTable.relateToOperator("sparseTextureGradClampARB", EOpSparseTextureGradClamp); + symbolTable.relateToOperator("sparseTextureGradOffsetClampARB", EOpSparseTextureGradOffsetClamp); + symbolTable.relateToOperator("textureClampARB", EOpTextureClamp); + symbolTable.relateToOperator("textureOffsetClampARB", EOpTextureOffsetClamp); + symbolTable.relateToOperator("textureGradClampARB", EOpTextureGradClamp); + symbolTable.relateToOperator("textureGradOffsetClampARB", EOpTextureGradOffsetClamp); + + symbolTable.relateToOperator("ballotARB", EOpBallot); + symbolTable.relateToOperator("readInvocationARB", EOpReadInvocation); + symbolTable.relateToOperator("readFirstInvocationARB", EOpReadFirstInvocation); + + if (version >= 430) { + symbolTable.relateToOperator("anyInvocationARB", EOpAnyInvocation); + symbolTable.relateToOperator("allInvocationsARB", EOpAllInvocations); + symbolTable.relateToOperator("allInvocationsEqualARB", EOpAllInvocationsEqual); + } + if (version >= 460) { + symbolTable.relateToOperator("anyInvocation", EOpAnyInvocation); + symbolTable.relateToOperator("allInvocations", EOpAllInvocations); + symbolTable.relateToOperator("allInvocationsEqual", EOpAllInvocationsEqual); + } + symbolTable.relateToOperator("minInvocationsAMD", EOpMinInvocations); + symbolTable.relateToOperator("maxInvocationsAMD", EOpMaxInvocations); + symbolTable.relateToOperator("addInvocationsAMD", EOpAddInvocations); + symbolTable.relateToOperator("minInvocationsNonUniformAMD", EOpMinInvocationsNonUniform); + symbolTable.relateToOperator("maxInvocationsNonUniformAMD", EOpMaxInvocationsNonUniform); + symbolTable.relateToOperator("addInvocationsNonUniformAMD", EOpAddInvocationsNonUniform); + symbolTable.relateToOperator("minInvocationsInclusiveScanAMD", EOpMinInvocationsInclusiveScan); + symbolTable.relateToOperator("maxInvocationsInclusiveScanAMD", EOpMaxInvocationsInclusiveScan); + symbolTable.relateToOperator("addInvocationsInclusiveScanAMD", EOpAddInvocationsInclusiveScan); + symbolTable.relateToOperator("minInvocationsInclusiveScanNonUniformAMD", EOpMinInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("maxInvocationsInclusiveScanNonUniformAMD", EOpMaxInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("addInvocationsInclusiveScanNonUniformAMD", EOpAddInvocationsInclusiveScanNonUniform); + symbolTable.relateToOperator("minInvocationsExclusiveScanAMD", EOpMinInvocationsExclusiveScan); + symbolTable.relateToOperator("maxInvocationsExclusiveScanAMD", EOpMaxInvocationsExclusiveScan); + symbolTable.relateToOperator("addInvocationsExclusiveScanAMD", EOpAddInvocationsExclusiveScan); + symbolTable.relateToOperator("minInvocationsExclusiveScanNonUniformAMD", EOpMinInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("maxInvocationsExclusiveScanNonUniformAMD", EOpMaxInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("addInvocationsExclusiveScanNonUniformAMD", EOpAddInvocationsExclusiveScanNonUniform); + symbolTable.relateToOperator("swizzleInvocationsAMD", EOpSwizzleInvocations); + symbolTable.relateToOperator("swizzleInvocationsMaskedAMD", EOpSwizzleInvocationsMasked); + symbolTable.relateToOperator("writeInvocationAMD", EOpWriteInvocation); + symbolTable.relateToOperator("mbcntAMD", EOpMbcnt); + + symbolTable.relateToOperator("min3", EOpMin3); + symbolTable.relateToOperator("max3", EOpMax3); + symbolTable.relateToOperator("mid3", EOpMid3); + + symbolTable.relateToOperator("cubeFaceIndexAMD", EOpCubeFaceIndex); + symbolTable.relateToOperator("cubeFaceCoordAMD", EOpCubeFaceCoord); + symbolTable.relateToOperator("timeAMD", EOpTime); + + symbolTable.relateToOperator("textureGatherLodAMD", EOpTextureGatherLod); + symbolTable.relateToOperator("textureGatherLodOffsetAMD", EOpTextureGatherLodOffset); + symbolTable.relateToOperator("textureGatherLodOffsetsAMD", EOpTextureGatherLodOffsets); + symbolTable.relateToOperator("sparseTextureGatherLodAMD", EOpSparseTextureGatherLod); + symbolTable.relateToOperator("sparseTextureGatherLodOffsetAMD", EOpSparseTextureGatherLodOffset); + symbolTable.relateToOperator("sparseTextureGatherLodOffsetsAMD", EOpSparseTextureGatherLodOffsets); + + symbolTable.relateToOperator("imageLoadLodAMD", EOpImageLoadLod); + symbolTable.relateToOperator("imageStoreLodAMD", EOpImageStoreLod); + symbolTable.relateToOperator("sparseImageLoadLodAMD", EOpSparseImageLoadLod); + + symbolTable.relateToOperator("fragmentMaskFetchAMD", EOpFragmentMaskFetch); + symbolTable.relateToOperator("fragmentFetchAMD", EOpFragmentFetch); + } + + // GL_KHR_shader_subgroup + if ((profile == EEsProfile && version >= 310) || + (profile != EEsProfile && version >= 140)) { + symbolTable.relateToOperator("subgroupBarrier", EOpSubgroupBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrier", EOpSubgroupMemoryBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrierBuffer", EOpSubgroupMemoryBarrierBuffer); + symbolTable.relateToOperator("subgroupMemoryBarrierImage", EOpSubgroupMemoryBarrierImage); + symbolTable.relateToOperator("subgroupElect", EOpSubgroupElect); + symbolTable.relateToOperator("subgroupAll", EOpSubgroupAll); + symbolTable.relateToOperator("subgroupAny", EOpSubgroupAny); + symbolTable.relateToOperator("subgroupAllEqual", EOpSubgroupAllEqual); + symbolTable.relateToOperator("subgroupBroadcast", EOpSubgroupBroadcast); + symbolTable.relateToOperator("subgroupBroadcastFirst", EOpSubgroupBroadcastFirst); + symbolTable.relateToOperator("subgroupBallot", EOpSubgroupBallot); + symbolTable.relateToOperator("subgroupInverseBallot", EOpSubgroupInverseBallot); + symbolTable.relateToOperator("subgroupBallotBitExtract", EOpSubgroupBallotBitExtract); + symbolTable.relateToOperator("subgroupBallotBitCount", EOpSubgroupBallotBitCount); + symbolTable.relateToOperator("subgroupBallotInclusiveBitCount", EOpSubgroupBallotInclusiveBitCount); + symbolTable.relateToOperator("subgroupBallotExclusiveBitCount", EOpSubgroupBallotExclusiveBitCount); + symbolTable.relateToOperator("subgroupBallotFindLSB", EOpSubgroupBallotFindLSB); + symbolTable.relateToOperator("subgroupBallotFindMSB", EOpSubgroupBallotFindMSB); + symbolTable.relateToOperator("subgroupShuffle", EOpSubgroupShuffle); + symbolTable.relateToOperator("subgroupShuffleXor", EOpSubgroupShuffleXor); + symbolTable.relateToOperator("subgroupShuffleUp", EOpSubgroupShuffleUp); + symbolTable.relateToOperator("subgroupShuffleDown", EOpSubgroupShuffleDown); + symbolTable.relateToOperator("subgroupAdd", EOpSubgroupAdd); + symbolTable.relateToOperator("subgroupMul", EOpSubgroupMul); + symbolTable.relateToOperator("subgroupMin", EOpSubgroupMin); + symbolTable.relateToOperator("subgroupMax", EOpSubgroupMax); + symbolTable.relateToOperator("subgroupAnd", EOpSubgroupAnd); + symbolTable.relateToOperator("subgroupOr", EOpSubgroupOr); + symbolTable.relateToOperator("subgroupXor", EOpSubgroupXor); + symbolTable.relateToOperator("subgroupInclusiveAdd", EOpSubgroupInclusiveAdd); + symbolTable.relateToOperator("subgroupInclusiveMul", EOpSubgroupInclusiveMul); + symbolTable.relateToOperator("subgroupInclusiveMin", EOpSubgroupInclusiveMin); + symbolTable.relateToOperator("subgroupInclusiveMax", EOpSubgroupInclusiveMax); + symbolTable.relateToOperator("subgroupInclusiveAnd", EOpSubgroupInclusiveAnd); + symbolTable.relateToOperator("subgroupInclusiveOr", EOpSubgroupInclusiveOr); + symbolTable.relateToOperator("subgroupInclusiveXor", EOpSubgroupInclusiveXor); + symbolTable.relateToOperator("subgroupExclusiveAdd", EOpSubgroupExclusiveAdd); + symbolTable.relateToOperator("subgroupExclusiveMul", EOpSubgroupExclusiveMul); + symbolTable.relateToOperator("subgroupExclusiveMin", EOpSubgroupExclusiveMin); + symbolTable.relateToOperator("subgroupExclusiveMax", EOpSubgroupExclusiveMax); + symbolTable.relateToOperator("subgroupExclusiveAnd", EOpSubgroupExclusiveAnd); + symbolTable.relateToOperator("subgroupExclusiveOr", EOpSubgroupExclusiveOr); + symbolTable.relateToOperator("subgroupExclusiveXor", EOpSubgroupExclusiveXor); + symbolTable.relateToOperator("subgroupClusteredAdd", EOpSubgroupClusteredAdd); + symbolTable.relateToOperator("subgroupClusteredMul", EOpSubgroupClusteredMul); + symbolTable.relateToOperator("subgroupClusteredMin", EOpSubgroupClusteredMin); + symbolTable.relateToOperator("subgroupClusteredMax", EOpSubgroupClusteredMax); + symbolTable.relateToOperator("subgroupClusteredAnd", EOpSubgroupClusteredAnd); + symbolTable.relateToOperator("subgroupClusteredOr", EOpSubgroupClusteredOr); + symbolTable.relateToOperator("subgroupClusteredXor", EOpSubgroupClusteredXor); + symbolTable.relateToOperator("subgroupQuadBroadcast", EOpSubgroupQuadBroadcast); + symbolTable.relateToOperator("subgroupQuadSwapHorizontal", EOpSubgroupQuadSwapHorizontal); + symbolTable.relateToOperator("subgroupQuadSwapVertical", EOpSubgroupQuadSwapVertical); + symbolTable.relateToOperator("subgroupQuadSwapDiagonal", EOpSubgroupQuadSwapDiagonal); + + symbolTable.relateToOperator("subgroupPartitionNV", EOpSubgroupPartition); + symbolTable.relateToOperator("subgroupPartitionedAddNV", EOpSubgroupPartitionedAdd); + symbolTable.relateToOperator("subgroupPartitionedMulNV", EOpSubgroupPartitionedMul); + symbolTable.relateToOperator("subgroupPartitionedMinNV", EOpSubgroupPartitionedMin); + symbolTable.relateToOperator("subgroupPartitionedMaxNV", EOpSubgroupPartitionedMax); + symbolTable.relateToOperator("subgroupPartitionedAndNV", EOpSubgroupPartitionedAnd); + symbolTable.relateToOperator("subgroupPartitionedOrNV", EOpSubgroupPartitionedOr); + symbolTable.relateToOperator("subgroupPartitionedXorNV", EOpSubgroupPartitionedXor); + symbolTable.relateToOperator("subgroupPartitionedInclusiveAddNV", EOpSubgroupPartitionedInclusiveAdd); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMulNV", EOpSubgroupPartitionedInclusiveMul); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMinNV", EOpSubgroupPartitionedInclusiveMin); + symbolTable.relateToOperator("subgroupPartitionedInclusiveMaxNV", EOpSubgroupPartitionedInclusiveMax); + symbolTable.relateToOperator("subgroupPartitionedInclusiveAndNV", EOpSubgroupPartitionedInclusiveAnd); + symbolTable.relateToOperator("subgroupPartitionedInclusiveOrNV", EOpSubgroupPartitionedInclusiveOr); + symbolTable.relateToOperator("subgroupPartitionedInclusiveXorNV", EOpSubgroupPartitionedInclusiveXor); + symbolTable.relateToOperator("subgroupPartitionedExclusiveAddNV", EOpSubgroupPartitionedExclusiveAdd); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMulNV", EOpSubgroupPartitionedExclusiveMul); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMinNV", EOpSubgroupPartitionedExclusiveMin); + symbolTable.relateToOperator("subgroupPartitionedExclusiveMaxNV", EOpSubgroupPartitionedExclusiveMax); + symbolTable.relateToOperator("subgroupPartitionedExclusiveAndNV", EOpSubgroupPartitionedExclusiveAnd); + symbolTable.relateToOperator("subgroupPartitionedExclusiveOrNV", EOpSubgroupPartitionedExclusiveOr); + symbolTable.relateToOperator("subgroupPartitionedExclusiveXorNV", EOpSubgroupPartitionedExclusiveXor); + } + + if (profile == EEsProfile) { + symbolTable.relateToOperator("shadow2DEXT", EOpTexture); + symbolTable.relateToOperator("shadow2DProjEXT", EOpTextureProj); + } + } + + switch(language) { + case EShLangVertex: + break; + + case EShLangTessControl: + case EShLangTessEvaluation: + break; + + case EShLangGeometry: + symbolTable.relateToOperator("EmitStreamVertex", EOpEmitStreamVertex); + symbolTable.relateToOperator("EndStreamPrimitive", EOpEndStreamPrimitive); + symbolTable.relateToOperator("EmitVertex", EOpEmitVertex); + symbolTable.relateToOperator("EndPrimitive", EOpEndPrimitive); + break; + + case EShLangFragment: + if (profile != EEsProfile && version >= 400) { + symbolTable.relateToOperator("dFdxFine", EOpDPdxFine); + symbolTable.relateToOperator("dFdyFine", EOpDPdyFine); + symbolTable.relateToOperator("fwidthFine", EOpFwidthFine); + symbolTable.relateToOperator("dFdxCoarse", EOpDPdxCoarse); + symbolTable.relateToOperator("dFdyCoarse", EOpDPdyCoarse); + symbolTable.relateToOperator("fwidthCoarse", EOpFwidthCoarse); + } + + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("rayQueryInitializeEXT", EOpRayQueryInitialize); + symbolTable.relateToOperator("rayQueryTerminateEXT", EOpRayQueryTerminate); + symbolTable.relateToOperator("rayQueryGenerateIntersectionEXT", EOpRayQueryGenerateIntersection); + symbolTable.relateToOperator("rayQueryConfirmIntersectionEXT", EOpRayQueryConfirmIntersection); + symbolTable.relateToOperator("rayQueryProceedEXT", EOpRayQueryProceed); + symbolTable.relateToOperator("rayQueryGetIntersectionTypeEXT", EOpRayQueryGetIntersectionType); + symbolTable.relateToOperator("rayQueryGetRayTMinEXT", EOpRayQueryGetRayTMin); + symbolTable.relateToOperator("rayQueryGetRayFlagsEXT", EOpRayQueryGetRayFlags); + symbolTable.relateToOperator("rayQueryGetIntersectionTEXT", EOpRayQueryGetIntersectionT); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceCustomIndexEXT", EOpRayQueryGetIntersectionInstanceCustomIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceIdEXT", EOpRayQueryGetIntersectionInstanceId); + symbolTable.relateToOperator("rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT", EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset); + symbolTable.relateToOperator("rayQueryGetIntersectionGeometryIndexEXT", EOpRayQueryGetIntersectionGeometryIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionPrimitiveIndexEXT", EOpRayQueryGetIntersectionPrimitiveIndex); + symbolTable.relateToOperator("rayQueryGetIntersectionBarycentricsEXT", EOpRayQueryGetIntersectionBarycentrics); + symbolTable.relateToOperator("rayQueryGetIntersectionFrontFaceEXT", EOpRayQueryGetIntersectionFrontFace); + symbolTable.relateToOperator("rayQueryGetIntersectionCandidateAABBOpaqueEXT", EOpRayQueryGetIntersectionCandidateAABBOpaque); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectRayDirectionEXT", EOpRayQueryGetIntersectionObjectRayDirection); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectRayOriginEXT", EOpRayQueryGetIntersectionObjectRayOrigin); + symbolTable.relateToOperator("rayQueryGetWorldRayDirectionEXT", EOpRayQueryGetWorldRayDirection); + symbolTable.relateToOperator("rayQueryGetWorldRayOriginEXT", EOpRayQueryGetWorldRayOrigin); + symbolTable.relateToOperator("rayQueryGetIntersectionObjectToWorldEXT", EOpRayQueryGetIntersectionObjectToWorld); + symbolTable.relateToOperator("rayQueryGetIntersectionWorldToObjectEXT", EOpRayQueryGetIntersectionWorldToObject); + } + + symbolTable.relateToOperator("interpolateAtCentroid", EOpInterpolateAtCentroid); + symbolTable.relateToOperator("interpolateAtSample", EOpInterpolateAtSample); + symbolTable.relateToOperator("interpolateAtOffset", EOpInterpolateAtOffset); + + if (profile != EEsProfile) + symbolTable.relateToOperator("interpolateAtVertexAMD", EOpInterpolateAtVertex); + + symbolTable.relateToOperator("beginInvocationInterlockARB", EOpBeginInvocationInterlock); + symbolTable.relateToOperator("endInvocationInterlockARB", EOpEndInvocationInterlock); + + break; + + case EShLangCompute: + symbolTable.relateToOperator("subgroupMemoryBarrierShared", EOpSubgroupMemoryBarrierShared); + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("dFdx", EOpDPdx); + symbolTable.relateToOperator("dFdy", EOpDPdy); + symbolTable.relateToOperator("fwidth", EOpFwidth); + symbolTable.relateToOperator("dFdxFine", EOpDPdxFine); + symbolTable.relateToOperator("dFdyFine", EOpDPdyFine); + symbolTable.relateToOperator("fwidthFine", EOpFwidthFine); + symbolTable.relateToOperator("dFdxCoarse", EOpDPdxCoarse); + symbolTable.relateToOperator("dFdyCoarse", EOpDPdyCoarse); + symbolTable.relateToOperator("fwidthCoarse",EOpFwidthCoarse); + } + symbolTable.relateToOperator("coopMatLoadNV", EOpCooperativeMatrixLoad); + symbolTable.relateToOperator("coopMatStoreNV", EOpCooperativeMatrixStore); + symbolTable.relateToOperator("coopMatMulAddNV", EOpCooperativeMatrixMulAdd); + break; + + case EShLangRayGen: + case EShLangClosestHit: + case EShLangMiss: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("traceNV", EOpTrace); + symbolTable.relateToOperator("traceRayEXT", EOpTrace); + symbolTable.relateToOperator("executeCallableNV", EOpExecuteCallable); + symbolTable.relateToOperator("executeCallableEXT", EOpExecuteCallable); + } + break; + case EShLangIntersect: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("reportIntersectionNV", EOpReportIntersection); + symbolTable.relateToOperator("reportIntersectionEXT", EOpReportIntersection); + } + break; + case EShLangAnyHit: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("ignoreIntersectionNV", EOpIgnoreIntersection); + symbolTable.relateToOperator("ignoreIntersectionEXT", EOpIgnoreIntersection); + symbolTable.relateToOperator("terminateRayNV", EOpTerminateRay); + symbolTable.relateToOperator("terminateRayEXT", EOpTerminateRay); + } + break; + case EShLangCallable: + if (profile != EEsProfile && version >= 460) { + symbolTable.relateToOperator("executeCallableNV", EOpExecuteCallable); + symbolTable.relateToOperator("executeCallableEXT", EOpExecuteCallable); + } + break; + case EShLangMeshNV: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("writePackedPrimitiveIndices4x8NV", EOpWritePackedPrimitiveIndices4x8NV); + } + // fall through + case EShLangTaskNV: + if ((profile != EEsProfile && version >= 450) || (profile == EEsProfile && version >= 320)) { + symbolTable.relateToOperator("memoryBarrierShared", EOpMemoryBarrierShared); + symbolTable.relateToOperator("groupMemoryBarrier", EOpGroupMemoryBarrier); + symbolTable.relateToOperator("subgroupMemoryBarrierShared", EOpSubgroupMemoryBarrierShared); + } + break; + + default: + assert(false && "Language not supported"); + } +#endif // !GLSLANG_WEB +} + +// +// Add context-dependent (resource-specific) built-ins not handled by the above. These +// would be ones that need to be programmatically added because they cannot +// be added by simple text strings. For these, also +// 1) Map built-in functions to operators, for those that will turn into an operation node +// instead of remaining a function call. +// 2) Tag extension-related symbols added to their base version with their extensions, so +// that if an early version has the extension turned off, there is an error reported on use. +// +void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources) +{ +#ifndef GLSLANG_WEB +#if defined(GLSLANG_ANGLE) + profile = ECoreProfile; + version = 450; +#endif + if (profile != EEsProfile && version >= 430 && version < 440) { + symbolTable.setVariableExtensions("gl_MaxTransformFeedbackBuffers", 1, &E_GL_ARB_enhanced_layouts); + symbolTable.setVariableExtensions("gl_MaxTransformFeedbackInterleavedComponents", 1, &E_GL_ARB_enhanced_layouts); + } + if (profile != EEsProfile && version >= 130 && version < 420) { + symbolTable.setVariableExtensions("gl_MinProgramTexelOffset", 1, &E_GL_ARB_shading_language_420pack); + symbolTable.setVariableExtensions("gl_MaxProgramTexelOffset", 1, &E_GL_ARB_shading_language_420pack); + } + if (profile != EEsProfile && version >= 150 && version < 410) + symbolTable.setVariableExtensions("gl_MaxViewports", 1, &E_GL_ARB_viewport_array); + + switch(language) { + case EShLangFragment: + // Set up gl_FragData based on current array size. + if (version == 100 || IncludeLegacy(version, profile, spvVersion) || (! ForwardCompatibility && profile != EEsProfile && version < 420)) { + TPrecisionQualifier pq = profile == EEsProfile ? EpqMedium : EpqNone; + TType fragData(EbtFloat, EvqFragColor, pq, 4); + TArraySizes* arraySizes = new TArraySizes; + arraySizes->addInnerSize(resources.maxDrawBuffers); + fragData.transferArraySizes(arraySizes); + symbolTable.insert(*new TVariable(NewPoolTString("gl_FragData"), fragData)); + SpecialQualifier("gl_FragData", EvqFragColor, EbvFragData, symbolTable); + } + + // GL_EXT_blend_func_extended + if (profile == EEsProfile && version >= 100) { + symbolTable.setVariableExtensions("gl_MaxDualSourceDrawBuffersEXT", 1, &E_GL_EXT_blend_func_extended); + symbolTable.setVariableExtensions("gl_SecondaryFragColorEXT", 1, &E_GL_EXT_blend_func_extended); + symbolTable.setVariableExtensions("gl_SecondaryFragDataEXT", 1, &E_GL_EXT_blend_func_extended); + SpecialQualifier("gl_SecondaryFragColorEXT", EvqVaryingOut, EbvSecondaryFragColorEXT, symbolTable); + SpecialQualifier("gl_SecondaryFragDataEXT", EvqVaryingOut, EbvSecondaryFragDataEXT, symbolTable); + } + + break; + + case EShLangTessControl: + case EShLangTessEvaluation: + // Because of the context-dependent array size (gl_MaxPatchVertices), + // these variables were added later than the others and need to be mapped now. + + // standard members + BuiltInVariable("gl_in", "gl_Position", EbvPosition, symbolTable); + BuiltInVariable("gl_in", "gl_PointSize", EbvPointSize, symbolTable); + BuiltInVariable("gl_in", "gl_ClipDistance", EbvClipDistance, symbolTable); + BuiltInVariable("gl_in", "gl_CullDistance", EbvCullDistance, symbolTable); + + // compatibility members + BuiltInVariable("gl_in", "gl_ClipVertex", EbvClipVertex, symbolTable); + BuiltInVariable("gl_in", "gl_FrontColor", EbvFrontColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackColor", EbvBackColor, symbolTable); + BuiltInVariable("gl_in", "gl_FrontSecondaryColor", EbvFrontSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_BackSecondaryColor", EbvBackSecondaryColor, symbolTable); + BuiltInVariable("gl_in", "gl_TexCoord", EbvTexCoord, symbolTable); + BuiltInVariable("gl_in", "gl_FogFragCoord", EbvFogFragCoord, symbolTable); + + symbolTable.setVariableExtensions("gl_in", "gl_SecondaryPositionNV", 1, &E_GL_NV_stereo_view_rendering); + symbolTable.setVariableExtensions("gl_in", "gl_PositionPerViewNV", 1, &E_GL_NVX_multiview_per_view_attributes); + + BuiltInVariable("gl_in", "gl_SecondaryPositionNV", EbvSecondaryPositionNV, symbolTable); + BuiltInVariable("gl_in", "gl_PositionPerViewNV", EbvPositionPerViewNV, symbolTable); + + // extension requirements + if (profile == EEsProfile) { + symbolTable.setVariableExtensions("gl_in", "gl_PointSize", Num_AEP_tessellation_point_size, AEP_tessellation_point_size); + } + + break; + + default: + break; + } +#endif +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/Initialize.h b/third_party/glslang/glslang/MachineIndependent/Initialize.h new file mode 100644 index 0000000..ac8ec33 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/Initialize.h @@ -0,0 +1,112 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _INITIALIZE_INCLUDED_ +#define _INITIALIZE_INCLUDED_ + +#include "../Include/ResourceLimits.h" +#include "../Include/Common.h" +#include "../Include/ShHandle.h" +#include "SymbolTable.h" +#include "Versions.h" + +namespace glslang { + +// +// This is made to hold parseable strings for almost all the built-in +// functions and variables for one specific combination of version +// and profile. (Some still need to be added programmatically.) +// This is a base class for language-specific derivations, which +// can be used for language independent builtins. +// +// The strings are organized by +// commonBuiltins: intersection of all stages' built-ins, processed just once +// stageBuiltins[]: anything a stage needs that's not in commonBuiltins +// +class TBuiltInParseables { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TBuiltInParseables(); + virtual ~TBuiltInParseables(); + virtual void initialize(int version, EProfile, const SpvVersion& spvVersion) = 0; + virtual void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage) = 0; + virtual const TString& getCommonString() const { return commonBuiltins; } + virtual const TString& getStageString(EShLanguage language) const { return stageBuiltins[language]; } + + virtual void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable) = 0; + virtual void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources) = 0; + +protected: + TString commonBuiltins; + TString stageBuiltins[EShLangCount]; +}; + +// +// This is a GLSL specific derivation of TBuiltInParseables. To present a stable +// interface and match other similar code, it is called TBuiltIns, rather +// than TBuiltInParseablesGlsl. +// +class TBuiltIns : public TBuiltInParseables { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TBuiltIns(); + virtual ~TBuiltIns(); + void initialize(int version, EProfile, const SpvVersion& spvVersion); + void initialize(const TBuiltInResource& resources, int version, EProfile, const SpvVersion& spvVersion, EShLanguage); + + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable); + void identifyBuiltIns(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, TSymbolTable& symbolTable, const TBuiltInResource &resources); + +protected: + void addTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion); + void relateTabledBuiltins(int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage, TSymbolTable&); + void add2ndGenerationSamplingImaging(int version, EProfile profile, const SpvVersion& spvVersion); + void addSubpassSampling(TSampler, const TString& typeName, int version, EProfile profile); + void addQueryFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addImageFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addSamplingFunctions(TSampler, const TString& typeName, int version, EProfile profile); + void addGatherFunctions(TSampler, const TString& typeName, int version, EProfile profile); + + // Helpers for making textual representations of the permutations + // of texturing/imaging functions. + const char* postfixes[5]; + const char* prefixes[EbtNumTypes]; + int dimMap[EsdNumDims]; +}; + +} // end namespace glslang + +#endif // _INITIALIZE_INCLUDED_ diff --git a/third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp b/third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp new file mode 100644 index 0000000..553b1b5 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/IntermTraverse.cpp @@ -0,0 +1,309 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (c) 2002-2010 The ANGLE Project Authors. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/intermediate.h" + +namespace glslang { + +// +// Traverse the intermediate representation tree, and +// call a node type specific function for each node. +// Done recursively through the member function Traverse(). +// Node types can be skipped if their function to call is 0, +// but their subtree will still be traversed. +// Nodes with children can have their whole subtree skipped +// if preVisit is turned on and the type specific function +// returns false. +// +// preVisit, postVisit, and rightToLeft control what order +// nodes are visited in. +// + +// +// Traversal functions for terminals are straightforward.... +// +void TIntermMethod::traverse(TIntermTraverser*) +{ + // Tree should always resolve all methods as a non-method. +} + +void TIntermSymbol::traverse(TIntermTraverser *it) +{ + it->visitSymbol(this); +} + +void TIntermConstantUnion::traverse(TIntermTraverser *it) +{ + it->visitConstantUnion(this); +} + +const TString& TIntermSymbol::getAccessName() const { + if (getBasicType() == EbtBlock) + return getType().getTypeName(); + else + return getName(); +} + +// +// Traverse a binary node. +// +void TIntermBinary::traverse(TIntermTraverser *it) +{ + bool visit = true; + + // + // visit the node before children if pre-visiting. + // + if (it->preVisit) + visit = it->visitBinary(EvPreVisit, this); + + // + // Visit the children, in the right order. + // + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + if (right) + right->traverse(it); + + if (it->inVisit) + visit = it->visitBinary(EvInVisit, this); + + if (visit && left) + left->traverse(it); + } else { + if (left) + left->traverse(it); + + if (it->inVisit) + visit = it->visitBinary(EvInVisit, this); + + if (visit && right) + right->traverse(it); + } + + it->decrementDepth(); + } + + // + // Visit the node after the children, if requested and the traversal + // hasn't been canceled yet. + // + if (visit && it->postVisit) + it->visitBinary(EvPostVisit, this); +} + +// +// Traverse a unary node. Same comments in binary node apply here. +// +void TIntermUnary::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitUnary(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + operand->traverse(it); + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitUnary(EvPostVisit, this); +} + +// +// Traverse an aggregate node. Same comments in binary node apply here. +// +void TIntermAggregate::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitAggregate(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + for (TIntermSequence::reverse_iterator sit = sequence.rbegin(); sit != sequence.rend(); sit++) { + (*sit)->traverse(it); + + if (visit && it->inVisit) { + if (*sit != sequence.front()) + visit = it->visitAggregate(EvInVisit, this); + } + } + } else { + for (TIntermSequence::iterator sit = sequence.begin(); sit != sequence.end(); sit++) { + (*sit)->traverse(it); + + if (visit && it->inVisit) { + if (*sit != sequence.back()) + visit = it->visitAggregate(EvInVisit, this); + } + } + } + + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitAggregate(EvPostVisit, this); +} + +// +// Traverse a selection node. Same comments in binary node apply here. +// +void TIntermSelection::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitSelection(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + if (it->rightToLeft) { + if (falseBlock) + falseBlock->traverse(it); + if (trueBlock) + trueBlock->traverse(it); + condition->traverse(it); + } else { + condition->traverse(it); + if (trueBlock) + trueBlock->traverse(it); + if (falseBlock) + falseBlock->traverse(it); + } + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitSelection(EvPostVisit, this); +} + +// +// Traverse a loop node. Same comments in binary node apply here. +// +void TIntermLoop::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitLoop(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + + if (it->rightToLeft) { + if (terminal) + terminal->traverse(it); + + if (body) + body->traverse(it); + + if (test) + test->traverse(it); + } else { + if (test) + test->traverse(it); + + if (body) + body->traverse(it); + + if (terminal) + terminal->traverse(it); + } + + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitLoop(EvPostVisit, this); +} + +// +// Traverse a branch node. Same comments in binary node apply here. +// +void TIntermBranch::traverse(TIntermTraverser *it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitBranch(EvPreVisit, this); + + if (visit && expression) { + it->incrementDepth(this); + expression->traverse(it); + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitBranch(EvPostVisit, this); +} + +// +// Traverse a switch node. +// +void TIntermSwitch::traverse(TIntermTraverser* it) +{ + bool visit = true; + + if (it->preVisit) + visit = it->visitSwitch(EvPreVisit, this); + + if (visit) { + it->incrementDepth(this); + if (it->rightToLeft) { + body->traverse(it); + condition->traverse(it); + } else { + condition->traverse(it); + body->traverse(it); + } + it->decrementDepth(); + } + + if (visit && it->postVisit) + it->visitSwitch(EvPostVisit, this); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/Intermediate.cpp b/third_party/glslang/glslang/MachineIndependent/Intermediate.cpp new file mode 100755 index 0000000..b8c220d --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/Intermediate.cpp @@ -0,0 +1,3986 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2015 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Build the intermediate representation. +// + +#include "localintermediate.h" +#include "RemoveTree.h" +#include "SymbolTable.h" +#include "propagateNoContraction.h" + +#include +#include +#include + +namespace glslang { + +//////////////////////////////////////////////////////////////////////////// +// +// First set of functions are to help build the intermediate representation. +// These functions are not member functions of the nodes. +// They are called from parser productions. +// +///////////////////////////////////////////////////////////////////////////// + +// +// Add a terminal node for an identifier in an expression. +// +// Returns the added node. +// + +TIntermSymbol* TIntermediate::addSymbol(int id, const TString& name, const TType& type, const TConstUnionArray& constArray, + TIntermTyped* constSubtree, const TSourceLoc& loc) +{ + TIntermSymbol* node = new TIntermSymbol(id, name, type); + node->setLoc(loc); + node->setConstArray(constArray); + node->setConstSubtree(constSubtree); + + return node; +} + +TIntermSymbol* TIntermediate::addSymbol(const TIntermSymbol& intermSymbol) +{ + return addSymbol(intermSymbol.getId(), + intermSymbol.getName(), + intermSymbol.getType(), + intermSymbol.getConstArray(), + intermSymbol.getConstSubtree(), + intermSymbol.getLoc()); +} + +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable) +{ + glslang::TSourceLoc loc; // just a null location + loc.init(); + + return addSymbol(variable, loc); +} + +TIntermSymbol* TIntermediate::addSymbol(const TVariable& variable, const TSourceLoc& loc) +{ + return addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), variable.getConstArray(), variable.getConstSubtree(), loc); +} + +TIntermSymbol* TIntermediate::addSymbol(const TType& type, const TSourceLoc& loc) +{ + TConstUnionArray unionArray; // just a null constant + + return addSymbol(0, "", type, unionArray, nullptr, loc); +} + +// +// Connect two nodes with a new parent that does a binary operation on the nodes. +// +// Returns the added node. +// +// Returns nullptr if the working conversions and promotions could not be found. +// +TIntermTyped* TIntermediate::addBinaryMath(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) +{ + // No operations work on blocks + if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock) + return nullptr; + + // Convert "reference +/- int" and "reference - reference" to integer math + if (op == EOpAdd || op == EOpSub) { + + // No addressing math on struct with unsized array. + if ((left->isReference() && left->getType().getReferentType()->containsUnsizedArray()) || + (right->isReference() && right->getType().getReferentType()->containsUnsizedArray())) { + return nullptr; + } + + if (left->isReference() && isTypeInt(right->getBasicType())) { + const TType& referenceType = left->getType(); + TIntermConstantUnion* size = addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(left->getType()), loc, true); + left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64)); + + right = createConversion(EbtInt64, right); + right = addBinaryMath(EOpMul, right, size, loc); + + TIntermTyped *node = addBinaryMath(op, left, right, loc); + node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType); + return node; + } + } + + if (op == EOpAdd && right->isReference() && isTypeInt(left->getBasicType())) { + const TType& referenceType = right->getType(); + TIntermConstantUnion* size = + addConstantUnion((unsigned long long)computeBufferReferenceTypeSize(right->getType()), loc, true); + right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64)); + + left = createConversion(EbtInt64, left); + left = addBinaryMath(EOpMul, left, size, loc); + + TIntermTyped *node = addBinaryMath(op, left, right, loc); + node = addBuiltInFunctionCall(loc, EOpConvUint64ToPtr, true, node, referenceType); + return node; + } + + if (op == EOpSub && left->isReference() && right->isReference()) { + TIntermConstantUnion* size = + addConstantUnion((long long)computeBufferReferenceTypeSize(left->getType()), loc, true); + + left = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, left, TType(EbtUint64)); + right = addBuiltInFunctionCall(loc, EOpConvPtrToUint64, true, right, TType(EbtUint64)); + + left = addBuiltInFunctionCall(loc, EOpConvUint64ToInt64, true, left, TType(EbtInt64)); + right = addBuiltInFunctionCall(loc, EOpConvUint64ToInt64, true, right, TType(EbtInt64)); + + left = addBinaryMath(EOpSub, left, right, loc); + + TIntermTyped *node = addBinaryMath(EOpDiv, left, size, loc); + return node; + } + + // No other math operators supported on references + if (left->isReference() || right->isReference()) + return nullptr; + + // Try converting the children's base types to compatible types. + auto children = addPairConversion(op, left, right); + left = std::get<0>(children); + right = std::get<1>(children); + + if (left == nullptr || right == nullptr) + return nullptr; + + // Convert the children's type shape to be compatible. + addBiShapeConversion(op, left, right); + if (left == nullptr || right == nullptr) + return nullptr; + + // + // Need a new node holding things together. Make + // one and promote it to the right type. + // + TIntermBinary* node = addBinaryNode(op, left, right, loc); + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + // + // If they are both (non-specialization) constants, they must be folded. + // (Unless it's the sequence (comma) operator, but that's handled in addComma().) + // + TIntermConstantUnion *leftTempConstant = node->getLeft()->getAsConstantUnion(); + TIntermConstantUnion *rightTempConstant = node->getRight()->getAsConstantUnion(); + if (leftTempConstant && rightTempConstant) { + TIntermTyped* folded = leftTempConstant->fold(node->getOp(), rightTempConstant); + if (folded) + return folded; + } + + // If can propagate spec-constantness and if the operation is an allowed + // specialization-constant operation, make a spec-constant. + if (specConstantPropagates(*node->getLeft(), *node->getRight()) && isSpecializationOperation(*node)) + node->getWritableType().getQualifier().makeSpecConstant(); + + // If must propagate nonuniform, make a nonuniform. + if ((node->getLeft()->getQualifier().isNonUniform() || node->getRight()->getQualifier().isNonUniform()) && + isNonuniformPropagating(node->getOp())) + node->getWritableType().getQualifier().nonUniform = true; + + return node; +} + +// +// Low level: add binary node (no promotions or other argument modifications) +// +TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, + const TSourceLoc& loc) const +{ + // build the node + TIntermBinary* node = new TIntermBinary(op); + node->setLoc(loc.line != 0 ? loc : left->getLoc()); + node->setLeft(left); + node->setRight(right); + + return node; +} + +// +// like non-type form, but sets node's type. +// +TIntermBinary* TIntermediate::addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, + const TSourceLoc& loc, const TType& type) const +{ + TIntermBinary* node = addBinaryNode(op, left, right, loc); + node->setType(type); + return node; +} + +// +// Low level: add unary node (no promotions or other argument modifications) +// +TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc) const +{ + TIntermUnary* node = new TIntermUnary(op); + node->setLoc(loc.line != 0 ? loc : child->getLoc()); + node->setOperand(child); + + return node; +} + +// +// like non-type form, but sets node's type. +// +TIntermUnary* TIntermediate::addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc& loc, const TType& type) + const +{ + TIntermUnary* node = addUnaryNode(op, child, loc); + node->setType(type); + return node; +} + +// +// Connect two nodes through an assignment. +// +// Returns the added node. +// +// Returns nullptr if the 'right' type could not be converted to match the 'left' type, +// or the resulting operation cannot be properly promoted. +// +TIntermTyped* TIntermediate::addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, + const TSourceLoc& loc) +{ + // No block assignment + if (left->getType().getBasicType() == EbtBlock || right->getType().getBasicType() == EbtBlock) + return nullptr; + + // Convert "reference += int" to "reference = reference + int". We need this because the + // "reference + int" calculation involves a cast back to the original type, which makes it + // not an lvalue. + if ((op == EOpAddAssign || op == EOpSubAssign) && left->isReference()) { + if (!(right->getType().isScalar() && right->getType().isIntegerDomain())) + return nullptr; + + TIntermTyped* node = addBinaryMath(op == EOpAddAssign ? EOpAdd : EOpSub, left, right, loc); + if (!node) + return nullptr; + + TIntermSymbol* symbol = left->getAsSymbolNode(); + left = addSymbol(*symbol); + + node = addAssign(EOpAssign, left, node, loc); + return node; + } + + // + // Like adding binary math, except the conversion can only go + // from right to left. + // + + // convert base types, nullptr return means not possible + right = addConversion(op, left->getType(), right); + if (right == nullptr) + return nullptr; + + // convert shape + right = addUniShapeConversion(op, left->getType(), right); + + // build the node + TIntermBinary* node = addBinaryNode(op, left, right, loc); + + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + return node; +} + +// +// Connect two nodes through an index operator, where the left node is the base +// of an array or struct, and the right node is a direct or indirect offset. +// +// Returns the added node. +// The caller should set the type of the returned node. +// +TIntermTyped* TIntermediate::addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, + const TSourceLoc& loc) +{ + // caller should set the type + return addBinaryNode(op, base, index, loc); +} + +// +// Add one node as the parent of another that it operates on. +// +// Returns the added node. +// +TIntermTyped* TIntermediate::addUnaryMath(TOperator op, TIntermTyped* child, + const TSourceLoc& loc) +{ + if (child == 0) + return nullptr; + + if (child->getType().getBasicType() == EbtBlock) + return nullptr; + + switch (op) { + case EOpLogicalNot: + if (getSource() == EShSourceHlsl) { + break; // HLSL can promote logical not + } + + if (child->getType().getBasicType() != EbtBool || child->getType().isMatrix() || child->getType().isArray() || child->getType().isVector()) { + return nullptr; + } + break; + + case EOpPostIncrement: + case EOpPreIncrement: + case EOpPostDecrement: + case EOpPreDecrement: + case EOpNegative: + if (child->getType().getBasicType() == EbtStruct || child->getType().isArray()) + return nullptr; + default: break; // some compilers want this + } + + // + // Do we need to promote the operand? + // + TBasicType newType = EbtVoid; + switch (op) { + case EOpConstructBool: newType = EbtBool; break; + case EOpConstructFloat: newType = EbtFloat; break; + case EOpConstructInt: newType = EbtInt; break; + case EOpConstructUint: newType = EbtUint; break; +#ifndef GLSLANG_WEB + case EOpConstructInt8: newType = EbtInt8; break; + case EOpConstructUint8: newType = EbtUint8; break; + case EOpConstructInt16: newType = EbtInt16; break; + case EOpConstructUint16: newType = EbtUint16; break; + case EOpConstructInt64: newType = EbtInt64; break; + case EOpConstructUint64: newType = EbtUint64; break; + case EOpConstructDouble: newType = EbtDouble; break; + case EOpConstructFloat16: newType = EbtFloat16; break; +#endif + default: break; // some compilers want this + } + + if (newType != EbtVoid) { + child = addConversion(op, TType(newType, EvqTemporary, child->getVectorSize(), + child->getMatrixCols(), + child->getMatrixRows(), + child->isVector()), + child); + if (child == nullptr) + return nullptr; + } + + // + // For constructors, we are now done, it was all in the conversion. + // TODO: but, did this bypass constant folding? + // + switch (op) { + case EOpConstructInt8: + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt: + case EOpConstructUint: + case EOpConstructInt64: + case EOpConstructUint64: + case EOpConstructBool: + case EOpConstructFloat: + case EOpConstructDouble: + case EOpConstructFloat16: + return child; + default: break; // some compilers want this + } + + // + // Make a new node for the operator. + // + TIntermUnary* node = addUnaryNode(op, child, loc); + + if (! promote(node)) + return nullptr; + + node->updatePrecision(); + + // If it's a (non-specialization) constant, it must be folded. + if (node->getOperand()->getAsConstantUnion()) + return node->getOperand()->getAsConstantUnion()->fold(op, node->getType()); + + // If it's a specialization constant, the result is too, + // if the operation is allowed for specialization constants. + if (node->getOperand()->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*node)) + node->getWritableType().getQualifier().makeSpecConstant(); + + // If must propagate nonuniform, make a nonuniform. + if (node->getOperand()->getQualifier().isNonUniform() && isNonuniformPropagating(node->getOp())) + node->getWritableType().getQualifier().nonUniform = true; + + return node; +} + +TIntermTyped* TIntermediate::addBuiltInFunctionCall(const TSourceLoc& loc, TOperator op, bool unary, + TIntermNode* childNode, const TType& returnType) +{ + if (unary) { + // + // Treat it like a unary operator. + // addUnaryMath() should get the type correct on its own; + // including constness (which would differ from the prototype). + // + TIntermTyped* child = childNode->getAsTyped(); + if (child == nullptr) + return nullptr; + + if (child->getAsConstantUnion()) { + TIntermTyped* folded = child->getAsConstantUnion()->fold(op, returnType); + if (folded) + return folded; + } + + return addUnaryNode(op, child, child->getLoc(), returnType); + } else { + // setAggregateOperater() calls fold() for constant folding + TIntermTyped* node = setAggregateOperator(childNode, op, returnType, loc); + + return node; + } +} + +// +// This is the safe way to change the operator on an aggregate, as it +// does lots of error checking and fixing. Especially for establishing +// a function call's operation on its set of parameters. Sequences +// of instructions are also aggregates, but they just directly set +// their operator to EOpSequence. +// +// Returns an aggregate node, which could be the one passed in if +// it was already an aggregate. +// +TIntermTyped* TIntermediate::setAggregateOperator(TIntermNode* node, TOperator op, const TType& type, + const TSourceLoc& loc) +{ + TIntermAggregate* aggNode; + + // + // Make sure we have an aggregate. If not turn it into one. + // + if (node != nullptr) { + aggNode = node->getAsAggregate(); + if (aggNode == nullptr || aggNode->getOp() != EOpNull) { + // + // Make an aggregate containing this node. + // + aggNode = new TIntermAggregate(); + aggNode->getSequence().push_back(node); + } + } else + aggNode = new TIntermAggregate(); + + // + // Set the operator. + // + aggNode->setOperator(op); + if (loc.line != 0 || node != nullptr) + aggNode->setLoc(loc.line != 0 ? loc : node->getLoc()); + + aggNode->setType(type); + + return fold(aggNode); +} + +bool TIntermediate::isConversionAllowed(TOperator op, TIntermTyped* node) const +{ + // + // Does the base type even allow the operation? + // + switch (node->getBasicType()) { + case EbtVoid: + return false; + case EbtAtomicUint: + case EbtSampler: + case EbtAccStruct: + // opaque types can be passed to functions + if (op == EOpFunction) + break; + + // HLSL can assign samplers directly (no constructor) + if (getSource() == EShSourceHlsl && node->getBasicType() == EbtSampler) + break; + + // samplers can get assigned via a sampler constructor + // (well, not yet, but code in the rest of this function is ready for it) + if (node->getBasicType() == EbtSampler && op == EOpAssign && + node->getAsOperator() != nullptr && node->getAsOperator()->getOp() == EOpConstructTextureSampler) + break; + + // otherwise, opaque types can't even be operated on, let alone converted + return false; + default: + break; + } + + return true; +} + +bool TIntermediate::buildConvertOp(TBasicType dst, TBasicType src, TOperator& newOp) const +{ + switch (dst) { +#ifndef GLSLANG_WEB + case EbtDouble: + switch (src) { + case EbtUint: newOp = EOpConvUintToDouble; break; + case EbtBool: newOp = EOpConvBoolToDouble; break; + case EbtFloat: newOp = EOpConvFloatToDouble; break; + case EbtInt: newOp = EOpConvIntToDouble; break; + case EbtInt8: newOp = EOpConvInt8ToDouble; break; + case EbtUint8: newOp = EOpConvUint8ToDouble; break; + case EbtInt16: newOp = EOpConvInt16ToDouble; break; + case EbtUint16: newOp = EOpConvUint16ToDouble; break; + case EbtFloat16: newOp = EOpConvFloat16ToDouble; break; + case EbtInt64: newOp = EOpConvInt64ToDouble; break; + case EbtUint64: newOp = EOpConvUint64ToDouble; break; + default: + return false; + } + break; +#endif + case EbtFloat: + switch (src) { + case EbtInt: newOp = EOpConvIntToFloat; break; + case EbtUint: newOp = EOpConvUintToFloat; break; + case EbtBool: newOp = EOpConvBoolToFloat; break; +#ifndef GLSLANG_WEB + case EbtDouble: newOp = EOpConvDoubleToFloat; break; + case EbtInt8: newOp = EOpConvInt8ToFloat; break; + case EbtUint8: newOp = EOpConvUint8ToFloat; break; + case EbtInt16: newOp = EOpConvInt16ToFloat; break; + case EbtUint16: newOp = EOpConvUint16ToFloat; break; + case EbtFloat16: newOp = EOpConvFloat16ToFloat; break; + case EbtInt64: newOp = EOpConvInt64ToFloat; break; + case EbtUint64: newOp = EOpConvUint64ToFloat; break; +#endif + default: + return false; + } + break; +#ifndef GLSLANG_WEB + case EbtFloat16: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToFloat16; break; + case EbtUint8: newOp = EOpConvUint8ToFloat16; break; + case EbtInt16: newOp = EOpConvInt16ToFloat16; break; + case EbtUint16: newOp = EOpConvUint16ToFloat16; break; + case EbtInt: newOp = EOpConvIntToFloat16; break; + case EbtUint: newOp = EOpConvUintToFloat16; break; + case EbtBool: newOp = EOpConvBoolToFloat16; break; + case EbtFloat: newOp = EOpConvFloatToFloat16; break; + case EbtDouble: newOp = EOpConvDoubleToFloat16; break; + case EbtInt64: newOp = EOpConvInt64ToFloat16; break; + case EbtUint64: newOp = EOpConvUint64ToFloat16; break; + default: + return false; + } + break; +#endif + case EbtBool: + switch (src) { + case EbtInt: newOp = EOpConvIntToBool; break; + case EbtUint: newOp = EOpConvUintToBool; break; + case EbtFloat: newOp = EOpConvFloatToBool; break; +#ifndef GLSLANG_WEB + case EbtDouble: newOp = EOpConvDoubleToBool; break; + case EbtInt8: newOp = EOpConvInt8ToBool; break; + case EbtUint8: newOp = EOpConvUint8ToBool; break; + case EbtInt16: newOp = EOpConvInt16ToBool; break; + case EbtUint16: newOp = EOpConvUint16ToBool; break; + case EbtFloat16: newOp = EOpConvFloat16ToBool; break; + case EbtInt64: newOp = EOpConvInt64ToBool; break; + case EbtUint64: newOp = EOpConvUint64ToBool; break; +#endif + default: + return false; + } + break; +#ifndef GLSLANG_WEB + case EbtInt8: + switch (src) { + case EbtUint8: newOp = EOpConvUint8ToInt8; break; + case EbtInt16: newOp = EOpConvInt16ToInt8; break; + case EbtUint16: newOp = EOpConvUint16ToInt8; break; + case EbtInt: newOp = EOpConvIntToInt8; break; + case EbtUint: newOp = EOpConvUintToInt8; break; + case EbtInt64: newOp = EOpConvInt64ToInt8; break; + case EbtUint64: newOp = EOpConvUint64ToInt8; break; + case EbtBool: newOp = EOpConvBoolToInt8; break; + case EbtFloat: newOp = EOpConvFloatToInt8; break; + case EbtDouble: newOp = EOpConvDoubleToInt8; break; + case EbtFloat16: newOp = EOpConvFloat16ToInt8; break; + default: + return false; + } + break; + case EbtUint8: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToUint8; break; + case EbtInt16: newOp = EOpConvInt16ToUint8; break; + case EbtUint16: newOp = EOpConvUint16ToUint8; break; + case EbtInt: newOp = EOpConvIntToUint8; break; + case EbtUint: newOp = EOpConvUintToUint8; break; + case EbtInt64: newOp = EOpConvInt64ToUint8; break; + case EbtUint64: newOp = EOpConvUint64ToUint8; break; + case EbtBool: newOp = EOpConvBoolToUint8; break; + case EbtFloat: newOp = EOpConvFloatToUint8; break; + case EbtDouble: newOp = EOpConvDoubleToUint8; break; + case EbtFloat16: newOp = EOpConvFloat16ToUint8; break; + default: + return false; + } + break; + + case EbtInt16: + switch (src) { + case EbtUint8: newOp = EOpConvUint8ToInt16; break; + case EbtInt8: newOp = EOpConvInt8ToInt16; break; + case EbtUint16: newOp = EOpConvUint16ToInt16; break; + case EbtInt: newOp = EOpConvIntToInt16; break; + case EbtUint: newOp = EOpConvUintToInt16; break; + case EbtInt64: newOp = EOpConvInt64ToInt16; break; + case EbtUint64: newOp = EOpConvUint64ToInt16; break; + case EbtBool: newOp = EOpConvBoolToInt16; break; + case EbtFloat: newOp = EOpConvFloatToInt16; break; + case EbtDouble: newOp = EOpConvDoubleToInt16; break; + case EbtFloat16: newOp = EOpConvFloat16ToInt16; break; + default: + return false; + } + break; + case EbtUint16: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToUint16; break; + case EbtUint8: newOp = EOpConvUint8ToUint16; break; + case EbtInt16: newOp = EOpConvInt16ToUint16; break; + case EbtInt: newOp = EOpConvIntToUint16; break; + case EbtUint: newOp = EOpConvUintToUint16; break; + case EbtInt64: newOp = EOpConvInt64ToUint16; break; + case EbtUint64: newOp = EOpConvUint64ToUint16; break; + case EbtBool: newOp = EOpConvBoolToUint16; break; + case EbtFloat: newOp = EOpConvFloatToUint16; break; + case EbtDouble: newOp = EOpConvDoubleToUint16; break; + case EbtFloat16: newOp = EOpConvFloat16ToUint16; break; + default: + return false; + } + break; +#endif + + case EbtInt: + switch (src) { + case EbtUint: newOp = EOpConvUintToInt; break; + case EbtBool: newOp = EOpConvBoolToInt; break; + case EbtFloat: newOp = EOpConvFloatToInt; break; +#ifndef GLSLANG_WEB + case EbtInt8: newOp = EOpConvInt8ToInt; break; + case EbtUint8: newOp = EOpConvUint8ToInt; break; + case EbtInt16: newOp = EOpConvInt16ToInt; break; + case EbtUint16: newOp = EOpConvUint16ToInt; break; + case EbtDouble: newOp = EOpConvDoubleToInt; break; + case EbtFloat16: newOp = EOpConvFloat16ToInt; break; + case EbtInt64: newOp = EOpConvInt64ToInt; break; + case EbtUint64: newOp = EOpConvUint64ToInt; break; +#endif + default: + return false; + } + break; + case EbtUint: + switch (src) { + case EbtInt: newOp = EOpConvIntToUint; break; + case EbtBool: newOp = EOpConvBoolToUint; break; + case EbtFloat: newOp = EOpConvFloatToUint; break; +#ifndef GLSLANG_WEB + case EbtInt8: newOp = EOpConvInt8ToUint; break; + case EbtUint8: newOp = EOpConvUint8ToUint; break; + case EbtInt16: newOp = EOpConvInt16ToUint; break; + case EbtUint16: newOp = EOpConvUint16ToUint; break; + case EbtDouble: newOp = EOpConvDoubleToUint; break; + case EbtFloat16: newOp = EOpConvFloat16ToUint; break; + case EbtInt64: newOp = EOpConvInt64ToUint; break; + case EbtUint64: newOp = EOpConvUint64ToUint; break; +#endif + default: + return false; + } + break; +#ifndef GLSLANG_WEB + case EbtInt64: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToInt64; break; + case EbtUint8: newOp = EOpConvUint8ToInt64; break; + case EbtInt16: newOp = EOpConvInt16ToInt64; break; + case EbtUint16: newOp = EOpConvUint16ToInt64; break; + case EbtInt: newOp = EOpConvIntToInt64; break; + case EbtUint: newOp = EOpConvUintToInt64; break; + case EbtBool: newOp = EOpConvBoolToInt64; break; + case EbtFloat: newOp = EOpConvFloatToInt64; break; + case EbtDouble: newOp = EOpConvDoubleToInt64; break; + case EbtFloat16: newOp = EOpConvFloat16ToInt64; break; + case EbtUint64: newOp = EOpConvUint64ToInt64; break; + default: + return false; + } + break; + case EbtUint64: + switch (src) { + case EbtInt8: newOp = EOpConvInt8ToUint64; break; + case EbtUint8: newOp = EOpConvUint8ToUint64; break; + case EbtInt16: newOp = EOpConvInt16ToUint64; break; + case EbtUint16: newOp = EOpConvUint16ToUint64; break; + case EbtInt: newOp = EOpConvIntToUint64; break; + case EbtUint: newOp = EOpConvUintToUint64; break; + case EbtBool: newOp = EOpConvBoolToUint64; break; + case EbtFloat: newOp = EOpConvFloatToUint64; break; + case EbtDouble: newOp = EOpConvDoubleToUint64; break; + case EbtFloat16: newOp = EOpConvFloat16ToUint64; break; + case EbtInt64: newOp = EOpConvInt64ToUint64; break; + default: + return false; + } + break; +#endif + default: + return false; + } + return true; +} + +// This is 'mechanism' here, it does any conversion told. +// It is about basic type, not about shape. +// The policy comes from the shader or the calling code. +TIntermTyped* TIntermediate::createConversion(TBasicType convertTo, TIntermTyped* node) const +{ + // + // Add a new newNode for the conversion. + // + +#ifndef GLSLANG_WEB + bool convertToIntTypes = (convertTo == EbtInt8 || convertTo == EbtUint8 || + convertTo == EbtInt16 || convertTo == EbtUint16 || + convertTo == EbtInt || convertTo == EbtUint || + convertTo == EbtInt64 || convertTo == EbtUint64); + + bool convertFromIntTypes = (node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8 || + node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16 || + node->getBasicType() == EbtInt || node->getBasicType() == EbtUint || + node->getBasicType() == EbtInt64 || node->getBasicType() == EbtUint64); + + bool convertToFloatTypes = (convertTo == EbtFloat16 || convertTo == EbtFloat || convertTo == EbtDouble); + + bool convertFromFloatTypes = (node->getBasicType() == EbtFloat16 || + node->getBasicType() == EbtFloat || + node->getBasicType() == EbtDouble); + + if (((convertTo == EbtInt8 || convertTo == EbtUint8) && ! convertFromIntTypes) || + ((node->getBasicType() == EbtInt8 || node->getBasicType() == EbtUint8) && ! convertToIntTypes)) { + if (! getArithemeticInt8Enabled()) { + return nullptr; + } + } + + if (((convertTo == EbtInt16 || convertTo == EbtUint16) && ! convertFromIntTypes) || + ((node->getBasicType() == EbtInt16 || node->getBasicType() == EbtUint16) && ! convertToIntTypes)) { + if (! getArithemeticInt16Enabled()) { + return nullptr; + } + } + + if ((convertTo == EbtFloat16 && ! convertFromFloatTypes) || + (node->getBasicType() == EbtFloat16 && ! convertToFloatTypes)) { + if (! getArithemeticFloat16Enabled()) { + return nullptr; + } + } +#endif + + TIntermUnary* newNode = nullptr; + TOperator newOp = EOpNull; + if (!buildConvertOp(convertTo, node->getBasicType(), newOp)) { + return nullptr; + } + + TType newType(convertTo, EvqTemporary, node->getVectorSize(), node->getMatrixCols(), node->getMatrixRows()); + newNode = addUnaryNode(newOp, node, node->getLoc(), newType); + + if (node->getAsConstantUnion()) { +#ifndef GLSLANG_WEB + // 8/16-bit storage extensions don't support 8/16-bit constants, so don't fold conversions + // to those types + if ((getArithemeticInt8Enabled() || !(convertTo == EbtInt8 || convertTo == EbtUint8)) && + (getArithemeticInt16Enabled() || !(convertTo == EbtInt16 || convertTo == EbtUint16)) && + (getArithemeticFloat16Enabled() || !(convertTo == EbtFloat16))) +#endif + { + TIntermTyped* folded = node->getAsConstantUnion()->fold(newOp, newType); + if (folded) + return folded; + } + } + + // Propagate specialization-constant-ness, if allowed + if (node->getType().getQualifier().isSpecConstant() && isSpecializationOperation(*newNode)) + newNode->getWritableType().getQualifier().makeSpecConstant(); + + return newNode; +} + +TIntermTyped* TIntermediate::addConversion(TBasicType convertTo, TIntermTyped* node) const +{ + return createConversion(convertTo, node); +} + +// For converting a pair of operands to a binary operation to compatible +// types with each other, relative to the operation in 'op'. +// This does not cover assignment operations, which is asymmetric in that the +// left type is not changeable. +// See addConversion(op, type, node) for assignments and unary operation +// conversions. +// +// Generally, this is focused on basic type conversion, not shape conversion. +// See addShapeConversion() for shape conversions. +// +// Returns the converted pair of nodes. +// Returns when there is no conversion. +std::tuple +TIntermediate::addPairConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1) +{ + if (!isConversionAllowed(op, node0) || !isConversionAllowed(op, node1)) + return std::make_tuple(nullptr, nullptr); + + if (node0->getType() != node1->getType()) { + // If differing structure, then no conversions. + if (node0->isStruct() || node1->isStruct()) + return std::make_tuple(nullptr, nullptr); + + // If differing arrays, then no conversions. + if (node0->getType().isArray() || node1->getType().isArray()) + return std::make_tuple(nullptr, nullptr); + + // No implicit conversions for operations involving cooperative matrices + if (node0->getType().isCoopMat() || node1->getType().isCoopMat()) + return std::make_tuple(node0, node1); + } + + auto promoteTo = std::make_tuple(EbtNumTypes, EbtNumTypes); + + switch (op) { + // + // List all the binary ops that can implicitly convert one operand to the other's type; + // This implements the 'policy' for implicit type conversion. + // + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpEqual: + case EOpNotEqual: + + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpDiv: + case EOpMod: + + case EOpVectorTimesScalar: + case EOpVectorTimesMatrix: + case EOpMatrixTimesVector: + case EOpMatrixTimesScalar: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpSequence: // used by ?: + + if (node0->getBasicType() == node1->getBasicType()) + return std::make_tuple(node0, node1); + + promoteTo = getConversionDestinationType(node0->getBasicType(), node1->getBasicType(), op); + if (std::get<0>(promoteTo) == EbtNumTypes || std::get<1>(promoteTo) == EbtNumTypes) + return std::make_tuple(nullptr, nullptr); + + break; + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + if (getSource() == EShSourceHlsl) + promoteTo = std::make_tuple(EbtBool, EbtBool); + else + return std::make_tuple(node0, node1); + break; + + // There are no conversions needed for GLSL; the shift amount just needs to be an + // integer type, as does the base. + // HLSL can promote bools to ints to make this work. + case EOpLeftShift: + case EOpRightShift: + if (getSource() == EShSourceHlsl) { + TBasicType node0BasicType = node0->getBasicType(); + if (node0BasicType == EbtBool) + node0BasicType = EbtInt; + if (node1->getBasicType() == EbtBool) + promoteTo = std::make_tuple(node0BasicType, EbtInt); + else + promoteTo = std::make_tuple(node0BasicType, node1->getBasicType()); + } else { + if (isTypeInt(node0->getBasicType()) && isTypeInt(node1->getBasicType())) + return std::make_tuple(node0, node1); + else + return std::make_tuple(nullptr, nullptr); + } + break; + + default: + if (node0->getType() == node1->getType()) + return std::make_tuple(node0, node1); + + return std::make_tuple(nullptr, nullptr); + } + + TIntermTyped* newNode0; + TIntermTyped* newNode1; + + if (std::get<0>(promoteTo) != node0->getType().getBasicType()) { + if (node0->getAsConstantUnion()) + newNode0 = promoteConstantUnion(std::get<0>(promoteTo), node0->getAsConstantUnion()); + else + newNode0 = createConversion(std::get<0>(promoteTo), node0); + } else + newNode0 = node0; + + if (std::get<1>(promoteTo) != node1->getType().getBasicType()) { + if (node1->getAsConstantUnion()) + newNode1 = promoteConstantUnion(std::get<1>(promoteTo), node1->getAsConstantUnion()); + else + newNode1 = createConversion(std::get<1>(promoteTo), node1); + } else + newNode1 = node1; + + return std::make_tuple(newNode0, newNode1); +} + +// +// Convert the node's type to the given type, as allowed by the operation involved: 'op'. +// For implicit conversions, 'op' is not the requested conversion, it is the explicit +// operation requiring the implicit conversion. +// +// Binary operation conversions should be handled by addConversion(op, node, node), not here. +// +// Returns a node representing the conversion, which could be the same +// node passed in if no conversion was needed. +// +// Generally, this is focused on basic type conversion, not shape conversion. +// See addShapeConversion() for shape conversions. +// +// Return nullptr if a conversion can't be done. +// +TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TIntermTyped* node) +{ + if (!isConversionAllowed(op, node)) + return nullptr; + + // Otherwise, if types are identical, no problem + if (type == node->getType()) + return node; + + // If one's a structure, then no conversions. + if (type.isStruct() || node->isStruct()) + return nullptr; + + // If one's an array, then no conversions. + if (type.isArray() || node->getType().isArray()) + return nullptr; + + // Note: callers are responsible for other aspects of shape, + // like vector and matrix sizes. + + switch (op) { + // + // Explicit conversions (unary operations) + // + case EOpConstructBool: + case EOpConstructFloat: + case EOpConstructInt: + case EOpConstructUint: +#ifndef GLSLANG_WEB + case EOpConstructDouble: + case EOpConstructFloat16: + case EOpConstructInt8: + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt64: + case EOpConstructUint64: + break; + +#endif + + // + // Implicit conversions + // + case EOpLogicalNot: + + case EOpFunctionCall: + + case EOpReturn: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpVectorTimesScalarAssign: + case EOpMatrixTimesScalarAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + case EOpAtan: + case EOpClamp: + case EOpCross: + case EOpDistance: + case EOpDot: + case EOpDst: + case EOpFaceForward: + case EOpFma: + case EOpFrexp: + case EOpLdexp: + case EOpMix: + case EOpLit: + case EOpMax: + case EOpMin: + case EOpMod: + case EOpModf: + case EOpPow: + case EOpReflect: + case EOpRefract: + case EOpSmoothStep: + case EOpStep: + + case EOpSequence: + case EOpConstructStruct: + case EOpConstructCooperativeMatrix: + + if (type.isReference() || node->getType().isReference()) { + // types must match to assign a reference + if (type == node->getType()) + return node; + else + return nullptr; + } + + if (type.getBasicType() == node->getType().getBasicType()) + return node; + + if (! canImplicitlyPromote(node->getBasicType(), type.getBasicType(), op)) + return nullptr; + break; + + // For GLSL, there are no conversions needed; the shift amount just needs to be an + // integer type, as do the base/result. + // HLSL can convert the shift from a bool to an int. + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + { + if (!(getSource() == EShSourceHlsl && node->getType().getBasicType() == EbtBool)) { + if (isTypeInt(type.getBasicType()) && isTypeInt(node->getBasicType())) + return node; + else + return nullptr; + } + break; + } + + default: + // default is to require a match; all exceptions should have case statements above + + if (type.getBasicType() == node->getType().getBasicType()) + return node; + else + return nullptr; + } + + bool canPromoteConstant = true; +#ifndef GLSLANG_WEB + // GL_EXT_shader_16bit_storage can't do OpConstantComposite with + // 16-bit types, so disable promotion for those types. + // Many issues with this, from JohnK: + // - this isn't really right to discuss SPIR-V here + // - this could easily be entirely about scalars, so is overstepping + // - we should be looking at what the shader asked for, and saying whether or + // not it can be done, in the parser, by calling requireExtensions(), not + // changing language sementics on the fly by asking what extensions are in use + // - at the time of this writing (14-Aug-2020), no test results are changed by this. + switch (op) { + case EOpConstructFloat16: + canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16); + break; + case EOpConstructInt8: + case EOpConstructUint8: + canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8); + break; + case EOpConstructInt16: + case EOpConstructUint16: + canPromoteConstant = numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16); + break; + default: + break; + } +#endif + + if (canPromoteConstant && node->getAsConstantUnion()) + return promoteConstantUnion(type.getBasicType(), node->getAsConstantUnion()); + + // + // Add a new newNode for the conversion. + // + TIntermTyped* newNode = createConversion(type.getBasicType(), node); + + return newNode; +} + +// Convert the node's shape of type for the given type, as allowed by the +// operation involved: 'op'. This is for situations where there is only one +// direction to consider doing the shape conversion. +// +// This implements policy, it call addShapeConversion() for the mechanism. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +// Return 'node' if no conversion was done. Promotion handles final shape +// checking. +// +TIntermTyped* TIntermediate::addUniShapeConversion(TOperator op, const TType& type, TIntermTyped* node) +{ + // some source languages don't do this + switch (getSource()) { + case EShSourceHlsl: + break; + case EShSourceGlsl: + default: + return node; + } + + // some operations don't do this + switch (op) { + case EOpFunctionCall: + case EOpReturn: + break; + + case EOpMulAssign: + // want to support vector *= scalar native ops in AST and lower, not smear, similarly for + // matrix *= scalar, etc. + + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + if (node->getVectorSize() == 1) + return node; + break; + + case EOpAssign: + break; + + case EOpMix: + break; + + default: + return node; + } + + return addShapeConversion(type, node); +} + +// Convert the nodes' shapes to be compatible for the operation 'op'. +// +// This implements policy, it call addShapeConversion() for the mechanism. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +void TIntermediate::addBiShapeConversion(TOperator op, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode) +{ + // some source languages don't do this + switch (getSource()) { + case EShSourceHlsl: + break; + case EShSourceGlsl: + default: + return; + } + + // some operations don't do this + // 'break' will mean attempt bidirectional conversion + switch (op) { + case EOpMulAssign: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + // switch to unidirectional conversion (the lhs can't change) + rhsNode = addUniShapeConversion(op, lhsNode->getType(), rhsNode); + return; + + case EOpMul: + // matrix multiply does not change shapes + if (lhsNode->isMatrix() && rhsNode->isMatrix()) + return; + case EOpAdd: + case EOpSub: + case EOpDiv: + // want to support vector * scalar native ops in AST and lower, not smear, similarly for + // matrix * vector, etc. + if (lhsNode->getVectorSize() == 1 || rhsNode->getVectorSize() == 1) + return; + break; + + case EOpRightShift: + case EOpLeftShift: + // can natively support the right operand being a scalar and the left a vector, + // but not the reverse + if (rhsNode->getVectorSize() == 1) + return; + break; + + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpEqual: + case EOpNotEqual: + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpMix: + break; + + default: + return; + } + + // Do bidirectional conversions + if (lhsNode->getType().isScalarOrVec1() || rhsNode->getType().isScalarOrVec1()) { + if (lhsNode->getType().isScalarOrVec1()) + lhsNode = addShapeConversion(rhsNode->getType(), lhsNode); + else + rhsNode = addShapeConversion(lhsNode->getType(), rhsNode); + } + lhsNode = addShapeConversion(rhsNode->getType(), lhsNode); + rhsNode = addShapeConversion(lhsNode->getType(), rhsNode); +} + +// Convert the node's shape of type for the given type, as allowed by the +// operation involved: 'op'. +// +// Generally, the AST represents allowed GLSL shapes, so this isn't needed +// for GLSL. Bad shapes are caught in conversion or promotion. +// +// Return 'node' if no conversion was done. Promotion handles final shape +// checking. +// +TIntermTyped* TIntermediate::addShapeConversion(const TType& type, TIntermTyped* node) +{ + // no conversion needed + if (node->getType() == type) + return node; + + // structures and arrays don't change shape, either to or from + if (node->getType().isStruct() || node->getType().isArray() || + type.isStruct() || type.isArray()) + return node; + + // The new node that handles the conversion + TOperator constructorOp = mapTypeToConstructorOp(type); + + if (getSource() == EShSourceHlsl) { + // HLSL rules for scalar, vector and matrix conversions: + // 1) scalar can become anything, initializing every component with its value + // 2) vector and matrix can become scalar, first element is used (warning: truncation) + // 3) matrix can become matrix with less rows and/or columns (warning: truncation) + // 4) vector can become vector with less rows size (warning: truncation) + // 5a) vector 4 can become 2x2 matrix (special case) (same packing layout, its a reinterpret) + // 5b) 2x2 matrix can become vector 4 (special case) (same packing layout, its a reinterpret) + + const TType &sourceType = node->getType(); + + // rule 1 for scalar to matrix is special + if (sourceType.isScalarOrVec1() && type.isMatrix()) { + + // HLSL semantics: the scalar (or vec1) is replicated to every component of the matrix. Left to its + // own devices, the constructor from a scalar would populate the diagonal. This forces replication + // to every matrix element. + + // Note that if the node is complex (e.g, a function call), we don't want to duplicate it here + // repeatedly, so we copy it to a temp, then use the temp. + const int matSize = type.computeNumComponents(); + TIntermAggregate* rhsAggregate = new TIntermAggregate(); + + const bool isSimple = (node->getAsSymbolNode() != nullptr) || (node->getAsConstantUnion() != nullptr); + + if (!isSimple) { + assert(0); // TODO: use node replicator service when available. + } + + for (int x = 0; x < matSize; ++x) + rhsAggregate->getSequence().push_back(node); + + return setAggregateOperator(rhsAggregate, constructorOp, type, node->getLoc()); + } + + // rule 1 and 2 + if ((sourceType.isScalar() && !type.isScalar()) || (!sourceType.isScalar() && type.isScalar())) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + + // rule 3 and 5b + if (sourceType.isMatrix()) { + // rule 3 + if (type.isMatrix()) { + if ((sourceType.getMatrixCols() != type.getMatrixCols() || sourceType.getMatrixRows() != type.getMatrixRows()) && + sourceType.getMatrixCols() >= type.getMatrixCols() && sourceType.getMatrixRows() >= type.getMatrixRows()) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + // rule 5b + } else if (type.isVector()) { + if (type.getVectorSize() == 4 && sourceType.getMatrixCols() == 2 && sourceType.getMatrixRows() == 2) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + } + } + + // rule 4 and 5a + if (sourceType.isVector()) { + // rule 4 + if (type.isVector()) + { + if (sourceType.getVectorSize() > type.getVectorSize()) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + // rule 5a + } else if (type.isMatrix()) { + if (sourceType.getVectorSize() == 4 && type.getMatrixCols() == 2 && type.getMatrixRows() == 2) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + } + } + } + + // scalar -> vector or vec1 -> vector or + // vector -> scalar or + // bigger vector -> smaller vector + if ((node->getType().isScalarOrVec1() && type.isVector()) || + (node->getType().isVector() && type.isScalar()) || + (node->isVector() && type.isVector() && node->getVectorSize() > type.getVectorSize())) + return setAggregateOperator(makeAggregate(node), constructorOp, type, node->getLoc()); + + return node; +} + +bool TIntermediate::isIntegralPromotion(TBasicType from, TBasicType to) const +{ + // integral promotions + if (to == EbtInt) { + switch(from) { + case EbtInt8: + case EbtInt16: + case EbtUint8: + case EbtUint16: + return true; + default: + break; + } + } + return false; +} + +bool TIntermediate::isFPPromotion(TBasicType from, TBasicType to) const +{ + // floating-point promotions + if (to == EbtDouble) { + switch(from) { + case EbtFloat16: + case EbtFloat: + return true; + default: + break; + } + } + return false; +} + +bool TIntermediate::isIntegralConversion(TBasicType from, TBasicType to) const +{ +#ifdef GLSLANG_WEB + return false; +#endif + + switch (from) { + case EbtInt: + switch(to) { + case EbtUint: + return version >= 400 || getSource() == EShSourceHlsl; + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint: + switch(to) { + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt8: + switch (to) { + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint8: + switch (to) { + case EbtInt16: + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt16: + switch(to) { + case EbtUint16: + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtUint16: + switch(to) { + case EbtUint: + case EbtInt64: + case EbtUint64: + return true; + default: + break; + } + break; + case EbtInt64: + if (to == EbtUint64) { + return true; + } + break; + default: + break; + } + return false; +} + +bool TIntermediate::isFPConversion(TBasicType from, TBasicType to) const +{ +#ifdef GLSLANG_WEB + return false; +#endif + + if (to == EbtFloat && from == EbtFloat16) { + return true; + } else { + return false; + } +} + +bool TIntermediate::isFPIntegralConversion(TBasicType from, TBasicType to) const +{ + switch (from) { + case EbtInt: + case EbtUint: + switch(to) { + case EbtFloat: + case EbtDouble: + return true; + default: + break; + } + break; +#ifndef GLSLANG_WEB + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + switch (to) { + case EbtFloat16: + case EbtFloat: + case EbtDouble: + return true; + default: + break; + } + break; + case EbtInt64: + case EbtUint64: + if (to == EbtDouble) { + return true; + } + break; +#endif + default: + break; + } + return false; +} + +// +// See if the 'from' type is allowed to be implicitly converted to the +// 'to' type. This is not about vector/array/struct, only about basic type. +// +bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op) const +{ + if ((isEsProfile() && version < 310 ) || version == 110) + return false; + + if (from == to) + return true; + + // TODO: Move more policies into language-specific handlers. + // Some languages allow more general (or potentially, more specific) conversions under some conditions. + if (getSource() == EShSourceHlsl) { + const bool fromConvertable = (from == EbtFloat || from == EbtDouble || from == EbtInt || from == EbtUint || from == EbtBool); + const bool toConvertable = (to == EbtFloat || to == EbtDouble || to == EbtInt || to == EbtUint || to == EbtBool); + + if (fromConvertable && toConvertable) { + switch (op) { + case EOpAndAssign: // assignments can perform arbitrary conversions + case EOpInclusiveOrAssign: // ... + case EOpExclusiveOrAssign: // ... + case EOpAssign: // ... + case EOpAddAssign: // ... + case EOpSubAssign: // ... + case EOpMulAssign: // ... + case EOpVectorTimesScalarAssign: // ... + case EOpMatrixTimesScalarAssign: // ... + case EOpDivAssign: // ... + case EOpModAssign: // ... + case EOpReturn: // function returns can also perform arbitrary conversions + case EOpFunctionCall: // conversion of a calling parameter + case EOpLogicalNot: + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + case EOpConstructStruct: + return true; + default: + break; + } + } + } + + if (getSource() == EShSourceHlsl) { + // HLSL + if (from == EbtBool && (to == EbtInt || to == EbtUint || to == EbtFloat)) + return true; + } else { + // GLSL + if (isIntegralPromotion(from, to) || + isFPPromotion(from, to) || + isIntegralConversion(from, to) || + isFPConversion(from, to) || + isFPIntegralConversion(from, to)) { + + if (numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int32) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int64) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float32) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float64)) { + return true; + } + } + } + + if (isEsProfile()) { + switch (to) { + case EbtFloat: + switch (from) { + case EbtInt: + case EbtUint: + return numericFeatures.contains(TNumericFeatures::shader_implicit_conversions); + default: + return false; + } + case EbtUint: + switch (from) { + case EbtInt: + return numericFeatures.contains(TNumericFeatures::shader_implicit_conversions); + default: + return false; + } + default: + return false; + } + } else { + switch (to) { + case EbtDouble: + switch (from) { + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtFloat: + return version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64); + case EbtInt16: + case EbtUint16: + return (version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64)) && + numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + case EbtFloat16: + return (version >= 400 || numericFeatures.contains(TNumericFeatures::gpu_shader_fp64)) && + numericFeatures.contains(TNumericFeatures::gpu_shader_half_float); + default: + return false; + } + case EbtFloat: + switch (from) { + case EbtInt: + case EbtUint: + return true; + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + case EbtUint16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + case EbtFloat16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_half_float) || + getSource() == EShSourceHlsl; + default: + return false; + } + case EbtUint: + switch (from) { + case EbtInt: + return version >= 400 || getSource() == EShSourceHlsl; + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + case EbtUint16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + return false; + } + case EbtInt: + switch (from) { + case EbtBool: + return getSource() == EShSourceHlsl; + case EbtInt16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + return false; + } + case EbtUint64: + switch (from) { + case EbtInt: + case EbtUint: + case EbtInt64: + return true; + case EbtInt16: + case EbtUint16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + return false; + } + case EbtInt64: + switch (from) { + case EbtInt: + return true; + case EbtInt16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + return false; + } + case EbtFloat16: + switch (from) { + case EbtInt16: + case EbtUint16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + break; + } + return false; + case EbtUint16: + switch (from) { + case EbtInt16: + return numericFeatures.contains(TNumericFeatures::gpu_shader_int16); + default: + break; + } + return false; + default: + return false; + } + } + + return false; +} + +static bool canSignedIntTypeRepresentAllUnsignedValues(TBasicType sintType, TBasicType uintType) +{ +#ifdef GLSLANG_WEB + return false; +#endif + + switch(sintType) { + case EbtInt8: + switch(uintType) { + case EbtUint8: + case EbtUint16: + case EbtUint: + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt16: + switch(uintType) { + case EbtUint8: + return true; + case EbtUint16: + case EbtUint: + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt: + switch(uintType) { + case EbtUint8: + case EbtUint16: + return true; + case EbtUint: + return false; + default: + assert(false); + return false; + } + break; + case EbtInt64: + switch(uintType) { + case EbtUint8: + case EbtUint16: + case EbtUint: + return true; + case EbtUint64: + return false; + default: + assert(false); + return false; + } + break; + default: + assert(false); + return false; + } +} + + +static TBasicType getCorrespondingUnsignedType(TBasicType type) +{ +#ifdef GLSLANG_WEB + assert(type == EbtInt); + return EbtUint; +#endif + + switch(type) { + case EbtInt8: + return EbtUint8; + case EbtInt16: + return EbtUint16; + case EbtInt: + return EbtUint; + case EbtInt64: + return EbtUint64; + default: + assert(false); + return EbtNumTypes; + } +} + +// Implements the following rules +// - If either operand has type float64_t or derived from float64_t, +// the other shall be converted to float64_t or derived type. +// - Otherwise, if either operand has type float32_t or derived from +// float32_t, the other shall be converted to float32_t or derived type. +// - Otherwise, if either operand has type float16_t or derived from +// float16_t, the other shall be converted to float16_t or derived type. +// - Otherwise, if both operands have integer types the following rules +// shall be applied to the operands: +// - If both operands have the same type, no further conversion +// is needed. +// - Otherwise, if both operands have signed integer types or both +// have unsigned integer types, the operand with the type of lesser +// integer conversion rank shall be converted to the type of the +// operand with greater rank. +// - Otherwise, if the operand that has unsigned integer type has rank +// greater than or equal to the rank of the type of the other +// operand, the operand with signed integer type shall be converted +// to the type of the operand with unsigned integer type. +// - Otherwise, if the type of the operand with signed integer type can +// represent all of the values of the type of the operand with +// unsigned integer type, the operand with unsigned integer type +// shall be converted to the type of the operand with signed +// integer type. +// - Otherwise, both operands shall be converted to the unsigned +// integer type corresponding to the type of the operand with signed +// integer type. + +std::tuple TIntermediate::getConversionDestinationType(TBasicType type0, TBasicType type1, TOperator op) const +{ + TBasicType res0 = EbtNumTypes; + TBasicType res1 = EbtNumTypes; + + if ((isEsProfile() && + (version < 310 || !numericFeatures.contains(TNumericFeatures::shader_implicit_conversions))) || + version == 110) + return std::make_tuple(res0, res1); + + if (getSource() == EShSourceHlsl) { + if (canImplicitlyPromote(type1, type0, op)) { + res0 = type0; + res1 = type0; + } else if (canImplicitlyPromote(type0, type1, op)) { + res0 = type1; + res1 = type1; + } + return std::make_tuple(res0, res1); + } + + if ((type0 == EbtDouble && canImplicitlyPromote(type1, EbtDouble, op)) || + (type1 == EbtDouble && canImplicitlyPromote(type0, EbtDouble, op)) ) { + res0 = EbtDouble; + res1 = EbtDouble; + } else if ((type0 == EbtFloat && canImplicitlyPromote(type1, EbtFloat, op)) || + (type1 == EbtFloat && canImplicitlyPromote(type0, EbtFloat, op)) ) { + res0 = EbtFloat; + res1 = EbtFloat; + } else if ((type0 == EbtFloat16 && canImplicitlyPromote(type1, EbtFloat16, op)) || + (type1 == EbtFloat16 && canImplicitlyPromote(type0, EbtFloat16, op)) ) { + res0 = EbtFloat16; + res1 = EbtFloat16; + } else if (isTypeInt(type0) && isTypeInt(type1) && + (canImplicitlyPromote(type0, type1, op) || canImplicitlyPromote(type1, type0, op))) { + if ((isTypeSignedInt(type0) && isTypeSignedInt(type1)) || + (isTypeUnsignedInt(type0) && isTypeUnsignedInt(type1))) { + if (getTypeRank(type0) < getTypeRank(type1)) { + res0 = type1; + res1 = type1; + } else { + res0 = type0; + res1 = type0; + } + } else if (isTypeUnsignedInt(type0) && (getTypeRank(type0) > getTypeRank(type1))) { + res0 = type0; + res1 = type0; + } else if (isTypeUnsignedInt(type1) && (getTypeRank(type1) > getTypeRank(type0))) { + res0 = type1; + res1 = type1; + } else if (isTypeSignedInt(type0)) { + if (canSignedIntTypeRepresentAllUnsignedValues(type0, type1)) { + res0 = type0; + res1 = type0; + } else { + res0 = getCorrespondingUnsignedType(type0); + res1 = getCorrespondingUnsignedType(type0); + } + } else if (isTypeSignedInt(type1)) { + if (canSignedIntTypeRepresentAllUnsignedValues(type1, type0)) { + res0 = type1; + res1 = type1; + } else { + res0 = getCorrespondingUnsignedType(type1); + res1 = getCorrespondingUnsignedType(type1); + } + } + } + + return std::make_tuple(res0, res1); +} + +// +// Given a type, find what operation would fully construct it. +// +TOperator TIntermediate::mapTypeToConstructorOp(const TType& type) const +{ + TOperator op = EOpNull; + + if (type.getQualifier().isNonUniform()) + return EOpConstructNonuniform; + + if (type.isCoopMat()) + return EOpConstructCooperativeMatrix; + + switch (type.getBasicType()) { + case EbtStruct: + op = EOpConstructStruct; + break; + case EbtSampler: + if (type.getSampler().isCombined()) + op = EOpConstructTextureSampler; + break; + case EbtFloat: + if (type.isMatrix()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat2x2; break; + case 3: op = EOpConstructMat2x3; break; + case 4: op = EOpConstructMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat3x2; break; + case 3: op = EOpConstructMat3x3; break; + case 4: op = EOpConstructMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructMat4x2; break; + case 3: op = EOpConstructMat4x3; break; + case 4: op = EOpConstructMat4x4; break; + default: break; // some compilers want this + } + break; + default: break; // some compilers want this + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructFloat; break; + case 2: op = EOpConstructVec2; break; + case 3: op = EOpConstructVec3; break; + case 4: op = EOpConstructVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtInt: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat2x2; break; + case 3: op = EOpConstructIMat2x3; break; + case 4: op = EOpConstructIMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat3x2; break; + case 3: op = EOpConstructIMat3x3; break; + case 4: op = EOpConstructIMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructIMat4x2; break; + case 3: op = EOpConstructIMat4x3; break; + case 4: op = EOpConstructIMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt; break; + case 2: op = EOpConstructIVec2; break; + case 3: op = EOpConstructIVec3; break; + case 4: op = EOpConstructIVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtUint: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat2x2; break; + case 3: op = EOpConstructUMat2x3; break; + case 4: op = EOpConstructUMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat3x2; break; + case 3: op = EOpConstructUMat3x3; break; + case 4: op = EOpConstructUMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructUMat4x2; break; + case 3: op = EOpConstructUMat4x3; break; + case 4: op = EOpConstructUMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint; break; + case 2: op = EOpConstructUVec2; break; + case 3: op = EOpConstructUVec3; break; + case 4: op = EOpConstructUVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtBool: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat2x2; break; + case 3: op = EOpConstructBMat2x3; break; + case 4: op = EOpConstructBMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat3x2; break; + case 3: op = EOpConstructBMat3x3; break; + case 4: op = EOpConstructBMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructBMat4x2; break; + case 3: op = EOpConstructBMat4x3; break; + case 4: op = EOpConstructBMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructBool; break; + case 2: op = EOpConstructBVec2; break; + case 3: op = EOpConstructBVec3; break; + case 4: op = EOpConstructBVec4; break; + default: break; // some compilers want this + } + } + break; +#ifndef GLSLANG_WEB + case EbtDouble: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat2x2; break; + case 3: op = EOpConstructDMat2x3; break; + case 4: op = EOpConstructDMat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat3x2; break; + case 3: op = EOpConstructDMat3x3; break; + case 4: op = EOpConstructDMat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructDMat4x2; break; + case 3: op = EOpConstructDMat4x3; break; + case 4: op = EOpConstructDMat4x4; break; + default: break; // some compilers want this + } + break; + } + } else { + switch(type.getVectorSize()) { + case 1: op = EOpConstructDouble; break; + case 2: op = EOpConstructDVec2; break; + case 3: op = EOpConstructDVec3; break; + case 4: op = EOpConstructDVec4; break; + default: break; // some compilers want this + } + } + break; + case EbtFloat16: + if (type.getMatrixCols()) { + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat2x2; break; + case 3: op = EOpConstructF16Mat2x3; break; + case 4: op = EOpConstructF16Mat2x4; break; + default: break; // some compilers want this + } + break; + case 3: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat3x2; break; + case 3: op = EOpConstructF16Mat3x3; break; + case 4: op = EOpConstructF16Mat3x4; break; + default: break; // some compilers want this + } + break; + case 4: + switch (type.getMatrixRows()) { + case 2: op = EOpConstructF16Mat4x2; break; + case 3: op = EOpConstructF16Mat4x3; break; + case 4: op = EOpConstructF16Mat4x4; break; + default: break; // some compilers want this + } + break; + } + } + else { + switch (type.getVectorSize()) { + case 1: op = EOpConstructFloat16; break; + case 2: op = EOpConstructF16Vec2; break; + case 3: op = EOpConstructF16Vec3; break; + case 4: op = EOpConstructF16Vec4; break; + default: break; // some compilers want this + } + } + break; + case EbtInt8: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt8; break; + case 2: op = EOpConstructI8Vec2; break; + case 3: op = EOpConstructI8Vec3; break; + case 4: op = EOpConstructI8Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint8: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint8; break; + case 2: op = EOpConstructU8Vec2; break; + case 3: op = EOpConstructU8Vec3; break; + case 4: op = EOpConstructU8Vec4; break; + default: break; // some compilers want this + } + break; + case EbtInt16: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt16; break; + case 2: op = EOpConstructI16Vec2; break; + case 3: op = EOpConstructI16Vec3; break; + case 4: op = EOpConstructI16Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint16: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint16; break; + case 2: op = EOpConstructU16Vec2; break; + case 3: op = EOpConstructU16Vec3; break; + case 4: op = EOpConstructU16Vec4; break; + default: break; // some compilers want this + } + break; + case EbtInt64: + switch(type.getVectorSize()) { + case 1: op = EOpConstructInt64; break; + case 2: op = EOpConstructI64Vec2; break; + case 3: op = EOpConstructI64Vec3; break; + case 4: op = EOpConstructI64Vec4; break; + default: break; // some compilers want this + } + break; + case EbtUint64: + switch(type.getVectorSize()) { + case 1: op = EOpConstructUint64; break; + case 2: op = EOpConstructU64Vec2; break; + case 3: op = EOpConstructU64Vec3; break; + case 4: op = EOpConstructU64Vec4; break; + default: break; // some compilers want this + } + break; + case EbtReference: + op = EOpConstructReference; + break; +#endif + default: + break; + } + + return op; +} + +// +// Safe way to combine two nodes into an aggregate. Works with null pointers, +// a node that's not a aggregate yet, etc. +// +// Returns the resulting aggregate, unless nullptr was passed in for +// both existing nodes. +// +TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right) +{ + if (left == nullptr && right == nullptr) + return nullptr; + + TIntermAggregate* aggNode = nullptr; + if (left != nullptr) + aggNode = left->getAsAggregate(); + if (aggNode == nullptr || aggNode->getOp() != EOpNull) { + aggNode = new TIntermAggregate; + if (left != nullptr) + aggNode->getSequence().push_back(left); + } + + if (right != nullptr) + aggNode->getSequence().push_back(right); + + return aggNode; +} + +TIntermAggregate* TIntermediate::growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc& loc) +{ + TIntermAggregate* aggNode = growAggregate(left, right); + if (aggNode) + aggNode->setLoc(loc); + + return aggNode; +} + +// +// Turn an existing node into an aggregate. +// +// Returns an aggregate, unless nullptr was passed in for the existing node. +// +TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node) +{ + if (node == nullptr) + return nullptr; + + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->getSequence().push_back(node); + aggNode->setLoc(node->getLoc()); + + return aggNode; +} + +TIntermAggregate* TIntermediate::makeAggregate(TIntermNode* node, const TSourceLoc& loc) +{ + if (node == nullptr) + return nullptr; + + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->getSequence().push_back(node); + aggNode->setLoc(loc); + + return aggNode; +} + +// +// Make an aggregate with an empty sequence. +// +TIntermAggregate* TIntermediate::makeAggregate(const TSourceLoc& loc) +{ + TIntermAggregate* aggNode = new TIntermAggregate; + aggNode->setLoc(loc); + + return aggNode; +} + +// +// For "if" test nodes. There are three children; a condition, +// a true path, and a false path. The two paths are in the +// nodePair. +// +// Returns the selection node created. +// +TIntermSelection* TIntermediate::addSelection(TIntermTyped* cond, TIntermNodePair nodePair, const TSourceLoc& loc) +{ + // + // Don't prune the false path for compile-time constants; it's needed + // for static access analysis. + // + + TIntermSelection* node = new TIntermSelection(cond, nodePair.node1, nodePair.node2); + node->setLoc(loc); + + return node; +} + +TIntermTyped* TIntermediate::addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc& loc) +{ + // However, the lowest precedence operators of the sequence operator ( , ) and the assignment operators + // ... are not included in the operators that can create a constant expression. + // + // if (left->getType().getQualifier().storage == EvqConst && + // right->getType().getQualifier().storage == EvqConst) { + + // return right; + //} + + TIntermTyped *commaAggregate = growAggregate(left, right, loc); + commaAggregate->getAsAggregate()->setOperator(EOpComma); + commaAggregate->setType(right->getType()); + commaAggregate->getWritableType().getQualifier().makeTemporary(); + + return commaAggregate; +} + +TIntermTyped* TIntermediate::addMethod(TIntermTyped* object, const TType& type, const TString* name, const TSourceLoc& loc) +{ + TIntermMethod* method = new TIntermMethod(object, type, *name); + method->setLoc(loc); + + return method; +} + +// +// For "?:" test nodes. There are three children; a condition, +// a true path, and a false path. The two paths are specified +// as separate parameters. For vector 'cond', the true and false +// are not paths, but vectors to mix. +// +// Specialization constant operations include +// - The ternary operator ( ? : ) +// +// Returns the selection node created, or nullptr if one could not be. +// +TIntermTyped* TIntermediate::addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, + const TSourceLoc& loc) +{ + // If it's void, go to the if-then-else selection() + if (trueBlock->getBasicType() == EbtVoid && falseBlock->getBasicType() == EbtVoid) { + TIntermNodePair pair = { trueBlock, falseBlock }; + TIntermSelection* selection = addSelection(cond, pair, loc); + if (getSource() == EShSourceHlsl) + selection->setNoShortCircuit(); + + return selection; + } + + // + // Get compatible types. + // + auto children = addPairConversion(EOpSequence, trueBlock, falseBlock); + trueBlock = std::get<0>(children); + falseBlock = std::get<1>(children); + + if (trueBlock == nullptr || falseBlock == nullptr) + return nullptr; + + // Handle a vector condition as a mix + if (!cond->getType().isScalarOrVec1()) { + TType targetVectorType(trueBlock->getType().getBasicType(), EvqTemporary, + cond->getType().getVectorSize()); + // smear true/false operands as needed + trueBlock = addUniShapeConversion(EOpMix, targetVectorType, trueBlock); + falseBlock = addUniShapeConversion(EOpMix, targetVectorType, falseBlock); + + // After conversion, types have to match. + if (falseBlock->getType() != trueBlock->getType()) + return nullptr; + + // make the mix operation + TIntermAggregate* mix = makeAggregate(loc); + mix = growAggregate(mix, falseBlock); + mix = growAggregate(mix, trueBlock); + mix = growAggregate(mix, cond); + mix->setType(targetVectorType); + mix->setOp(EOpMix); + + return mix; + } + + // Now have a scalar condition... + + // Convert true and false expressions to matching types + addBiShapeConversion(EOpMix, trueBlock, falseBlock); + + // After conversion, types have to match. + if (falseBlock->getType() != trueBlock->getType()) + return nullptr; + + // Eliminate the selection when the condition is a scalar and all operands are constant. + if (cond->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion()) { + if (cond->getAsConstantUnion()->getConstArray()[0].getBConst()) + return trueBlock; + else + return falseBlock; + } + + // + // Make a selection node. + // + TIntermSelection* node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); + node->setLoc(loc); + node->getQualifier().precision = std::max(trueBlock->getQualifier().precision, falseBlock->getQualifier().precision); + + if ((cond->getQualifier().isConstant() && specConstantPropagates(*trueBlock, *falseBlock)) || + (cond->getQualifier().isSpecConstant() && trueBlock->getQualifier().isConstant() && + falseBlock->getQualifier().isConstant())) + node->getQualifier().makeSpecConstant(); + else + node->getQualifier().makeTemporary(); + + if (getSource() == EShSourceHlsl) + node->setNoShortCircuit(); + + return node; +} + +// +// Constant terminal nodes. Has a union that contains bool, float or int constants +// +// Returns the constant union node created. +// + +TIntermConstantUnion* TIntermediate::addConstantUnion(const TConstUnionArray& unionArray, const TType& t, const TSourceLoc& loc, bool literal) const +{ + TIntermConstantUnion* node = new TIntermConstantUnion(unionArray, t); + node->getQualifier().storage = EvqConst; + node->setLoc(loc); + if (literal) + node->setLiteral(); + + return node; +} +TIntermConstantUnion* TIntermediate::addConstantUnion(signed char i8, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI8Const(i8); + + return addConstantUnion(unionArray, TType(EbtInt8, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned char u8, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setUConst(u8); + + return addConstantUnion(unionArray, TType(EbtUint8, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(signed short i16, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI16Const(i16); + + return addConstantUnion(unionArray, TType(EbtInt16, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned short u16, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setU16Const(u16); + + return addConstantUnion(unionArray, TType(EbtUint16, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(int i, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setIConst(i); + + return addConstantUnion(unionArray, TType(EbtInt, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned int u, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setUConst(u); + + return addConstantUnion(unionArray, TType(EbtUint, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(long long i64, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setI64Const(i64); + + return addConstantUnion(unionArray, TType(EbtInt64, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(unsigned long long u64, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setU64Const(u64); + + return addConstantUnion(unionArray, TType(EbtUint64, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(bool b, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setBConst(b); + + return addConstantUnion(unionArray, TType(EbtBool, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseType, const TSourceLoc& loc, bool literal) const +{ + assert(baseType == EbtFloat || baseType == EbtDouble || baseType == EbtFloat16); + + TConstUnionArray unionArray(1); + unionArray[0].setDConst(d); + + return addConstantUnion(unionArray, TType(baseType, EvqConst), loc, literal); +} + +TIntermConstantUnion* TIntermediate::addConstantUnion(const TString* s, const TSourceLoc& loc, bool literal) const +{ + TConstUnionArray unionArray(1); + unionArray[0].setSConst(s); + + return addConstantUnion(unionArray, TType(EbtString, EvqConst), loc, literal); +} + +// Put vector swizzle selectors onto the given sequence +void TIntermediate::pushSelector(TIntermSequence& sequence, const TVectorSelector& selector, const TSourceLoc& loc) +{ + TIntermConstantUnion* constIntNode = addConstantUnion(selector, loc); + sequence.push_back(constIntNode); +} + +// Put matrix swizzle selectors onto the given sequence +void TIntermediate::pushSelector(TIntermSequence& sequence, const TMatrixSelector& selector, const TSourceLoc& loc) +{ + TIntermConstantUnion* constIntNode = addConstantUnion(selector.coord1, loc); + sequence.push_back(constIntNode); + constIntNode = addConstantUnion(selector.coord2, loc); + sequence.push_back(constIntNode); +} + +// Make an aggregate node that has a sequence of all selectors. +template TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc); +template TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc); +template +TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors& selector, const TSourceLoc& loc) +{ + TIntermAggregate* node = new TIntermAggregate(EOpSequence); + + node->setLoc(loc); + TIntermSequence &sequenceVector = node->getSequence(); + + for (int i = 0; i < selector.size(); i++) + pushSelector(sequenceVector, selector[i], loc); + + return node; +} + +// +// Follow the left branches down to the root of an l-value +// expression (just "." and []). +// +// Return the base of the l-value (where following indexing quits working). +// Return nullptr if a chain following dereferences cannot be followed. +// +// 'swizzleOkay' says whether or not it is okay to consider a swizzle +// a valid part of the dereference chain. +// +const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool swizzleOkay) +{ + do { + const TIntermBinary* binary = node->getAsBinaryNode(); + if (binary == nullptr) + return node; + TOperator op = binary->getOp(); + if (op != EOpIndexDirect && op != EOpIndexIndirect && op != EOpIndexDirectStruct && op != EOpVectorSwizzle && op != EOpMatrixSwizzle) + return nullptr; + if (! swizzleOkay) { + if (op == EOpVectorSwizzle || op == EOpMatrixSwizzle) + return nullptr; + if ((op == EOpIndexDirect || op == EOpIndexIndirect) && + (binary->getLeft()->getType().isVector() || binary->getLeft()->getType().isScalar()) && + ! binary->getLeft()->getType().isArray()) + return nullptr; + } + node = node->getAsBinaryNode()->getLeft(); + } while (true); +} + +// +// Create while and do-while loop nodes. +// +TIntermLoop* TIntermediate::addLoop(TIntermNode* body, TIntermTyped* test, TIntermTyped* terminal, bool testFirst, + const TSourceLoc& loc) +{ + TIntermLoop* node = new TIntermLoop(body, test, terminal, testFirst); + node->setLoc(loc); + + return node; +} + +// +// Create a for-loop sequence. +// +TIntermAggregate* TIntermediate::addForLoop(TIntermNode* body, TIntermNode* initializer, TIntermTyped* test, + TIntermTyped* terminal, bool testFirst, const TSourceLoc& loc, TIntermLoop*& node) +{ + node = new TIntermLoop(body, test, terminal, testFirst); + node->setLoc(loc); + + // make a sequence of the initializer and statement, but try to reuse the + // aggregate already created for whatever is in the initializer, if there is one + TIntermAggregate* loopSequence = (initializer == nullptr || + initializer->getAsAggregate() == nullptr) ? makeAggregate(initializer, loc) + : initializer->getAsAggregate(); + if (loopSequence != nullptr && loopSequence->getOp() == EOpSequence) + loopSequence->setOp(EOpNull); + loopSequence = growAggregate(loopSequence, node); + loopSequence->setOperator(EOpSequence); + + return loopSequence; +} + +// +// Add branches. +// +TIntermBranch* TIntermediate::addBranch(TOperator branchOp, const TSourceLoc& loc) +{ + return addBranch(branchOp, nullptr, loc); +} + +TIntermBranch* TIntermediate::addBranch(TOperator branchOp, TIntermTyped* expression, const TSourceLoc& loc) +{ + TIntermBranch* node = new TIntermBranch(branchOp, expression); + node->setLoc(loc); + + return node; +} + +// Propagate precision from formal function return type to actual return type, +// and on to its subtree. +void TIntermBranch::updatePrecision(TPrecisionQualifier parentPrecision) +{ + TIntermTyped* exp = getExpression(); + if (exp == nullptr) + return; + + if (exp->getBasicType() == EbtInt || exp->getBasicType() == EbtUint || + exp->getBasicType() == EbtFloat || exp->getBasicType() == EbtFloat16) { + if (parentPrecision != EpqNone && exp->getQualifier().precision == EpqNone) { + exp->propagatePrecision(parentPrecision); + } + } +} + +// +// This is to be executed after the final root is put on top by the parsing +// process. +// +bool TIntermediate::postProcess(TIntermNode* root, EShLanguage /*language*/) +{ + if (root == nullptr) + return true; + + // Finish off the top-level sequence + TIntermAggregate* aggRoot = root->getAsAggregate(); + if (aggRoot && aggRoot->getOp() == EOpNull) + aggRoot->setOperator(EOpSequence); + +#ifndef GLSLANG_WEB + // Propagate 'noContraction' label in backward from 'precise' variables. + glslang::PropagateNoContraction(*this); + + switch (textureSamplerTransformMode) { + case EShTexSampTransKeep: + break; + case EShTexSampTransUpgradeTextureRemoveSampler: + performTextureUpgradeAndSamplerRemovalTransformation(root); + break; + case EShTexSampTransCount: + assert(0); + break; + } +#endif + + return true; +} + +void TIntermediate::addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage language, TSymbolTable& symbolTable) +{ + // Add top-level nodes for declarations that must be checked cross + // compilation unit by a linker, yet might not have been referenced + // by the AST. + // + // Almost entirely, translation of symbols is driven by what's present + // in the AST traversal, not by translating the symbol table. + // + // However, there are some special cases: + // - From the specification: "Special built-in inputs gl_VertexID and + // gl_InstanceID are also considered active vertex attributes." + // - Linker-based type mismatch error reporting needs to see all + // uniforms/ins/outs variables and blocks. + // - ftransform() can make gl_Vertex and gl_ModelViewProjectionMatrix active. + // + + // if (ftransformUsed) { + // TODO: 1.1 lowering functionality: track ftransform() usage + // addSymbolLinkageNode(root, symbolTable, "gl_Vertex"); + // addSymbolLinkageNode(root, symbolTable, "gl_ModelViewProjectionMatrix"); + //} + + if (language == EShLangVertex) { + // the names won't be found in the symbol table unless the versions are right, + // so version logic does not need to be repeated here + addSymbolLinkageNode(linkage, symbolTable, "gl_VertexID"); + addSymbolLinkageNode(linkage, symbolTable, "gl_InstanceID"); + } + + // Add a child to the root node for the linker objects + linkage->setOperator(EOpLinkerObjects); + treeRoot = growAggregate(treeRoot, linkage); +} + +// +// Add the given name or symbol to the list of nodes at the end of the tree used +// for link-time checking and external linkage. +// + +void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable& symbolTable, const TString& name) +{ + TSymbol* symbol = symbolTable.find(name); + if (symbol) + addSymbolLinkageNode(linkage, *symbol->getAsVariable()); +} + +void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol& symbol) +{ + const TVariable* variable = symbol.getAsVariable(); + if (! variable) { + // This must be a member of an anonymous block, and we need to add the whole block + const TAnonMember* anon = symbol.getAsAnonMember(); + variable = &anon->getAnonContainer(); + } + TIntermSymbol* node = addSymbol(*variable); + linkage = growAggregate(linkage, node); +} + +// +// Add a caller->callee relationship to the call graph. +// Assumes the strings are unique per signature. +// +void TIntermediate::addToCallGraph(TInfoSink& /*infoSink*/, const TString& caller, const TString& callee) +{ + // Duplicates are okay, but faster to not keep them, and they come grouped by caller, + // as long as new ones are push on the same end we check on for duplicates + for (TGraph::const_iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->caller != caller) + break; + if (call->callee == callee) + return; + } + + callGraph.push_front(TCall(caller, callee)); +} + +// +// This deletes the tree. +// +void TIntermediate::removeTree() +{ + if (treeRoot) + RemoveAllTreeNodes(treeRoot); +} + +// +// Implement the part of KHR_vulkan_glsl that lists the set of operations +// that can result in a specialization constant operation. +// +// "5.x Specialization Constant Operations" +// +// Only some operations discussed in this section may be applied to a +// specialization constant and still yield a result that is as +// specialization constant. The operations allowed are listed below. +// When a specialization constant is operated on with one of these +// operators and with another constant or specialization constant, the +// result is implicitly a specialization constant. +// +// - int(), uint(), and bool() constructors for type conversions +// from any of the following types to any of the following types: +// * int +// * uint +// * bool +// - vector versions of the above conversion constructors +// - allowed implicit conversions of the above +// - swizzles (e.g., foo.yx) +// - The following when applied to integer or unsigned integer types: +// * unary negative ( - ) +// * binary operations ( + , - , * , / , % ) +// * shift ( <<, >> ) +// * bitwise operations ( & , | , ^ ) +// - The following when applied to integer or unsigned integer scalar types: +// * comparison ( == , != , > , >= , < , <= ) +// - The following when applied to the Boolean scalar type: +// * not ( ! ) +// * logical operations ( && , || , ^^ ) +// * comparison ( == , != )" +// +// This function just handles binary and unary nodes. Construction +// rules are handled in construction paths that are not covered by the unary +// and binary paths, while required conversions will still show up here +// as unary converters in the from a construction operator. +// +bool TIntermediate::isSpecializationOperation(const TIntermOperator& node) const +{ + // The operations resulting in floating point are quite limited + // (However, some floating-point operations result in bool, like ">", + // so are handled later.) + if (node.getType().isFloatingDomain()) { + switch (node.getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + case EOpConvFloatToDouble: + case EOpConvDoubleToFloat: + case EOpConvFloat16ToFloat: + case EOpConvFloatToFloat16: + case EOpConvFloat16ToDouble: + case EOpConvDoubleToFloat16: + return true; + default: + return false; + } + } + + // Check for floating-point arguments + if (const TIntermBinary* bin = node.getAsBinaryNode()) + if (bin->getLeft() ->getType().isFloatingDomain() || + bin->getRight()->getType().isFloatingDomain()) + return false; + + // So, for now, we can assume everything left is non-floating-point... + + // Now check for integer/bool-based operations + switch (node.getOp()) { + + // dereference/swizzle + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + + // (u)int* -> bool + case EOpConvInt8ToBool: + case EOpConvInt16ToBool: + case EOpConvIntToBool: + case EOpConvInt64ToBool: + case EOpConvUint8ToBool: + case EOpConvUint16ToBool: + case EOpConvUintToBool: + case EOpConvUint64ToBool: + + // bool -> (u)int* + case EOpConvBoolToInt8: + case EOpConvBoolToInt16: + case EOpConvBoolToInt: + case EOpConvBoolToInt64: + case EOpConvBoolToUint8: + case EOpConvBoolToUint16: + case EOpConvBoolToUint: + case EOpConvBoolToUint64: + + // int8_t -> (u)int* + case EOpConvInt8ToInt16: + case EOpConvInt8ToInt: + case EOpConvInt8ToInt64: + case EOpConvInt8ToUint8: + case EOpConvInt8ToUint16: + case EOpConvInt8ToUint: + case EOpConvInt8ToUint64: + + // int16_t -> (u)int* + case EOpConvInt16ToInt8: + case EOpConvInt16ToInt: + case EOpConvInt16ToInt64: + case EOpConvInt16ToUint8: + case EOpConvInt16ToUint16: + case EOpConvInt16ToUint: + case EOpConvInt16ToUint64: + + // int32_t -> (u)int* + case EOpConvIntToInt8: + case EOpConvIntToInt16: + case EOpConvIntToInt64: + case EOpConvIntToUint8: + case EOpConvIntToUint16: + case EOpConvIntToUint: + case EOpConvIntToUint64: + + // int64_t -> (u)int* + case EOpConvInt64ToInt8: + case EOpConvInt64ToInt16: + case EOpConvInt64ToInt: + case EOpConvInt64ToUint8: + case EOpConvInt64ToUint16: + case EOpConvInt64ToUint: + case EOpConvInt64ToUint64: + + // uint8_t -> (u)int* + case EOpConvUint8ToInt8: + case EOpConvUint8ToInt16: + case EOpConvUint8ToInt: + case EOpConvUint8ToInt64: + case EOpConvUint8ToUint16: + case EOpConvUint8ToUint: + case EOpConvUint8ToUint64: + + // uint16_t -> (u)int* + case EOpConvUint16ToInt8: + case EOpConvUint16ToInt16: + case EOpConvUint16ToInt: + case EOpConvUint16ToInt64: + case EOpConvUint16ToUint8: + case EOpConvUint16ToUint: + case EOpConvUint16ToUint64: + + // uint32_t -> (u)int* + case EOpConvUintToInt8: + case EOpConvUintToInt16: + case EOpConvUintToInt: + case EOpConvUintToInt64: + case EOpConvUintToUint8: + case EOpConvUintToUint16: + case EOpConvUintToUint64: + + // uint64_t -> (u)int* + case EOpConvUint64ToInt8: + case EOpConvUint64ToInt16: + case EOpConvUint64ToInt: + case EOpConvUint64ToInt64: + case EOpConvUint64ToUint8: + case EOpConvUint64ToUint16: + case EOpConvUint64ToUint: + + // unary operations + case EOpNegative: + case EOpLogicalNot: + case EOpBitwiseNot: + + // binary operations + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpVectorTimesScalar: + case EOpDiv: + case EOpMod: + case EOpRightShift: + case EOpLeftShift: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpLogicalOr: + case EOpLogicalXor: + case EOpLogicalAnd: + case EOpEqual: + case EOpNotEqual: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + return true; + default: + return false; + } +} + +// Is the operation one that must propagate nonuniform? +bool TIntermediate::isNonuniformPropagating(TOperator op) const +{ + // "* All Operators in Section 5.1 (Operators), except for assignment, + // arithmetic assignment, and sequence + // * Component selection in Section 5.5 + // * Matrix components in Section 5.6 + // * Structure and Array Operations in Section 5.7, except for the length + // method." + switch (op) { + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + + case EOpNegative: + case EOpLogicalNot: + case EOpVectorLogicalNot: + case EOpBitwiseNot: + + case EOpAdd: + case EOpSub: + case EOpMul: + case EOpDiv: + case EOpMod: + case EOpRightShift: + case EOpLeftShift: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpEqual: + case EOpNotEqual: + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + case EOpVectorTimesScalar: + case EOpVectorTimesMatrix: + case EOpMatrixTimesVector: + case EOpMatrixTimesScalar: + + case EOpLogicalOr: + case EOpLogicalXor: + case EOpLogicalAnd: + + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + return true; + + default: + break; + } + + return false; +} + +//////////////////////////////////////////////////////////////// +// +// Member functions of the nodes used for building the tree. +// +//////////////////////////////////////////////////////////////// + +// +// Say whether or not an operation node changes the value of a variable. +// +// Returns true if state is modified. +// +bool TIntermOperator::modifiesState() const +{ + switch (op) { + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpVectorTimesMatrixAssign: + case EOpVectorTimesScalarAssign: + case EOpMatrixTimesScalarAssign: + case EOpMatrixTimesMatrixAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + return true; + default: + return false; + } +} + +// +// returns true if the operator is for one of the constructors +// +bool TIntermOperator::isConstructor() const +{ + return op > EOpConstructGuardStart && op < EOpConstructGuardEnd; +} + +// +// Make sure the type of an operator is appropriate for its +// combination of operation and operand type. This will invoke +// promoteUnary, promoteBinary, etc as needed. +// +// Returns false if nothing makes sense. +// +bool TIntermediate::promote(TIntermOperator* node) +{ + if (node == nullptr) + return false; + + if (node->getAsUnaryNode()) + return promoteUnary(*node->getAsUnaryNode()); + + if (node->getAsBinaryNode()) + return promoteBinary(*node->getAsBinaryNode()); + + if (node->getAsAggregate()) + return promoteAggregate(*node->getAsAggregate()); + + return false; +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteUnary(TIntermUnary& node) +{ + const TOperator op = node.getOp(); + TIntermTyped* operand = node.getOperand(); + + switch (op) { + case EOpLogicalNot: + // Convert operand to a boolean type + if (operand->getBasicType() != EbtBool) { + // Add constructor to boolean type. If that fails, we can't do it, so return false. + TIntermTyped* converted = addConversion(op, TType(EbtBool), operand); + if (converted == nullptr) + return false; + + // Use the result of converting the node to a bool. + node.setOperand(operand = converted); // also updates stack variable + } + break; + case EOpBitwiseNot: + if (!isTypeInt(operand->getBasicType())) + return false; + break; + case EOpNegative: + case EOpPostIncrement: + case EOpPostDecrement: + case EOpPreIncrement: + case EOpPreDecrement: + if (!isTypeInt(operand->getBasicType()) && + operand->getBasicType() != EbtFloat && + operand->getBasicType() != EbtFloat16 && + operand->getBasicType() != EbtDouble) + + return false; + break; + default: + // HLSL uses this path for initial function signature finding for built-ins + // taking a single argument, which generally don't participate in + // operator-based type promotion (type conversion will occur later). + // For now, scalar argument cases are relying on the setType() call below. + if (getSource() == EShSourceHlsl) + break; + + // GLSL only allows integer arguments for the cases identified above in the + // case statements. + if (operand->getBasicType() != EbtFloat) + return false; + } + + node.setType(operand->getType()); + node.getWritableType().getQualifier().makeTemporary(); + + return true; +} + +// Propagate precision qualifiers *up* from children to parent. +void TIntermUnary::updatePrecision() +{ + if (getBasicType() == EbtInt || getBasicType() == EbtUint || + getBasicType() == EbtFloat || getBasicType() == EbtFloat16) { + if (operand->getQualifier().precision > getQualifier().precision) + getQualifier().precision = operand->getQualifier().precision; + } +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteBinary(TIntermBinary& node) +{ + TOperator op = node.getOp(); + TIntermTyped* left = node.getLeft(); + TIntermTyped* right = node.getRight(); + + // Arrays and structures have to be exact matches. + if ((left->isArray() || right->isArray() || left->getBasicType() == EbtStruct || right->getBasicType() == EbtStruct) + && left->getType() != right->getType()) + return false; + + // Base assumption: just make the type the same as the left + // operand. Only deviations from this will be coded. + node.setType(left->getType()); + node.getWritableType().getQualifier().clear(); + + // Composite and opaque types don't having pending operator changes, e.g., + // array, structure, and samplers. Just establish final type and correctness. + if (left->isArray() || left->getBasicType() == EbtStruct || left->getBasicType() == EbtSampler) { + switch (op) { + case EOpEqual: + case EOpNotEqual: + if (left->getBasicType() == EbtSampler) { + // can't compare samplers + return false; + } else { + // Promote to conditional + node.setType(TType(EbtBool)); + } + + return true; + + case EOpAssign: + // Keep type from above + + return true; + + default: + return false; + } + } + + // + // We now have only scalars, vectors, and matrices to worry about. + // + + // HLSL implicitly promotes bool -> int for numeric operations. + // (Implicit conversions to make the operands match each other's types were already done.) + if (getSource() == EShSourceHlsl && + (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool)) { + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpRightShift: + case EOpLeftShift: + + case EOpMod: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMul: + if (left->getBasicType() == EbtBool) + left = createConversion(EbtInt, left); + if (right->getBasicType() == EbtBool) + right = createConversion(EbtInt, right); + if (left == nullptr || right == nullptr) + return false; + node.setLeft(left); + node.setRight(right); + + // Update the original base assumption on result type.. + node.setType(left->getType()); + node.getWritableType().getQualifier().clear(); + + break; + + default: + break; + } + } + + // Do general type checks against individual operands (comparing left and right is coming up, checking mixed shapes after that) + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + // Relational comparisons need numeric types and will promote to scalar Boolean. + if (left->getBasicType() == EbtBool) + return false; + + node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize())); + break; + + case EOpEqual: + case EOpNotEqual: + if (getSource() == EShSourceHlsl) { + const int resultWidth = std::max(left->getVectorSize(), right->getVectorSize()); + + // In HLSL, == or != on vectors means component-wise comparison. + if (resultWidth > 1) { + op = (op == EOpEqual) ? EOpVectorEqual : EOpVectorNotEqual; + node.setOp(op); + } + + node.setType(TType(EbtBool, EvqTemporary, resultWidth)); + } else { + // All the above comparisons result in a bool (but not the vector compares) + node.setType(TType(EbtBool)); + } + break; + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + // logical ops operate only on Booleans or vectors of Booleans. + if (left->getBasicType() != EbtBool || left->isMatrix()) + return false; + + if (getSource() == EShSourceGlsl) { + // logical ops operate only on scalar Booleans and will promote to scalar Boolean. + if (left->isVector()) + return false; + } + + node.setType(TType(EbtBool, EvqTemporary, left->getVectorSize())); + break; + + case EOpRightShift: + case EOpLeftShift: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + + case EOpMod: + case EOpModAssign: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + if (getSource() == EShSourceHlsl) + break; + + // Check for integer-only operands. + if (!isTypeInt(left->getBasicType()) && !isTypeInt(right->getBasicType())) + return false; + if (left->isMatrix() || right->isMatrix()) + return false; + + break; + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMul: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpDivAssign: + // check for non-Boolean operands + if (left->getBasicType() == EbtBool || right->getBasicType() == EbtBool) + return false; + + default: + break; + } + + // Compare left and right, and finish with the cases where the operand types must match + switch (op) { + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + + case EOpEqual: + case EOpNotEqual: + case EOpVectorEqual: + case EOpVectorNotEqual: + + case EOpLogicalAnd: + case EOpLogicalOr: + case EOpLogicalXor: + return left->getType() == right->getType(); + + case EOpMod: + case EOpModAssign: + + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + case EOpAdd: + case EOpSub: + case EOpDiv: + + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + // Quick out in case the types do match + if (left->getType() == right->getType()) + return true; + + // Fall through + + case EOpMul: + case EOpMulAssign: + // At least the basic type has to match + if (left->getBasicType() != right->getBasicType()) + return false; + + default: + break; + } + + if (left->getType().isCoopMat() || right->getType().isCoopMat()) { + if (left->getType().isCoopMat() && right->getType().isCoopMat() && + *left->getType().getTypeParameters() != *right->getType().getTypeParameters()) { + return false; + } + switch (op) { + case EOpMul: + case EOpMulAssign: + if (left->getType().isCoopMat() && right->getType().isCoopMat()) { + return false; + } + if (op == EOpMulAssign && right->getType().isCoopMat()) { + return false; + } + node.setOp(op == EOpMulAssign ? EOpMatrixTimesScalarAssign : EOpMatrixTimesScalar); + if (right->getType().isCoopMat()) { + node.setType(right->getType()); + } + return true; + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpAssign: + // These require both to be cooperative matrices + if (!left->getType().isCoopMat() || !right->getType().isCoopMat()) { + return false; + } + return true; + default: + break; + } + return false; + } + + // Finish handling the case, for all ops, where both operands are scalars. + if (left->isScalar() && right->isScalar()) + return true; + + // Finish handling the case, for all ops, where there are two vectors of different sizes + if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize() && right->getVectorSize() > 1) + return false; + + // + // We now have a mix of scalars, vectors, or matrices, for non-relational operations. + // + + // Can these two operands be combined, what is the resulting type? + TBasicType basicType = left->getBasicType(); + switch (op) { + case EOpMul: + if (!left->isMatrix() && right->isMatrix()) { + if (left->isVector()) { + if (left->getVectorSize() != right->getMatrixRows()) + return false; + node.setOp(op = EOpVectorTimesMatrix); + node.setType(TType(basicType, EvqTemporary, right->getMatrixCols())); + } else { + node.setOp(op = EOpMatrixTimesScalar); + node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), right->getMatrixRows())); + } + } else if (left->isMatrix() && !right->isMatrix()) { + if (right->isVector()) { + if (left->getMatrixCols() != right->getVectorSize()) + return false; + node.setOp(op = EOpMatrixTimesVector); + node.setType(TType(basicType, EvqTemporary, left->getMatrixRows())); + } else { + node.setOp(op = EOpMatrixTimesScalar); + } + } else if (left->isMatrix() && right->isMatrix()) { + if (left->getMatrixCols() != right->getMatrixRows()) + return false; + node.setOp(op = EOpMatrixTimesMatrix); + node.setType(TType(basicType, EvqTemporary, 0, right->getMatrixCols(), left->getMatrixRows())); + } else if (! left->isMatrix() && ! right->isMatrix()) { + if (left->isVector() && right->isVector()) { + ; // leave as component product + } else if (left->isVector() || right->isVector()) { + node.setOp(op = EOpVectorTimesScalar); + if (right->isVector()) + node.setType(TType(basicType, EvqTemporary, right->getVectorSize())); + } + } else { + return false; + } + break; + case EOpMulAssign: + if (! left->isMatrix() && right->isMatrix()) { + if (left->isVector()) { + if (left->getVectorSize() != right->getMatrixRows() || left->getVectorSize() != right->getMatrixCols()) + return false; + node.setOp(op = EOpVectorTimesMatrixAssign); + } else { + return false; + } + } else if (left->isMatrix() && !right->isMatrix()) { + if (right->isVector()) { + return false; + } else { + node.setOp(op = EOpMatrixTimesScalarAssign); + } + } else if (left->isMatrix() && right->isMatrix()) { + if (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixCols() != right->getMatrixRows()) + return false; + node.setOp(op = EOpMatrixTimesMatrixAssign); + } else if (!left->isMatrix() && !right->isMatrix()) { + if (left->isVector() && right->isVector()) { + // leave as component product + } else if (left->isVector() || right->isVector()) { + if (! left->isVector()) + return false; + node.setOp(op = EOpVectorTimesScalarAssign); + } + } else { + return false; + } + break; + + case EOpRightShift: + case EOpLeftShift: + case EOpRightShiftAssign: + case EOpLeftShiftAssign: + if (right->isVector() && (! left->isVector() || right->getVectorSize() != left->getVectorSize())) + return false; + break; + + case EOpAssign: + if (left->getVectorSize() != right->getVectorSize() || left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows()) + return false; + // fall through + + case EOpAdd: + case EOpSub: + case EOpDiv: + case EOpMod: + case EOpAnd: + case EOpInclusiveOr: + case EOpExclusiveOr: + case EOpAddAssign: + case EOpSubAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + + if ((left->isMatrix() && right->isVector()) || + (left->isVector() && right->isMatrix()) || + left->getBasicType() != right->getBasicType()) + return false; + if (left->isMatrix() && right->isMatrix() && (left->getMatrixCols() != right->getMatrixCols() || left->getMatrixRows() != right->getMatrixRows())) + return false; + if (left->isVector() && right->isVector() && left->getVectorSize() != right->getVectorSize()) + return false; + if (right->isVector() || right->isMatrix()) { + node.getWritableType().shallowCopy(right->getType()); + node.getWritableType().getQualifier().makeTemporary(); + } + break; + + default: + return false; + } + + // + // One more check for assignment. + // + switch (op) { + // The resulting type has to match the left operand. + case EOpAssign: + case EOpAddAssign: + case EOpSubAssign: + case EOpMulAssign: + case EOpDivAssign: + case EOpModAssign: + case EOpAndAssign: + case EOpInclusiveOrAssign: + case EOpExclusiveOrAssign: + case EOpLeftShiftAssign: + case EOpRightShiftAssign: + if (node.getType() != left->getType()) + return false; + break; + default: + break; + } + + return true; +} + +// +// See TIntermediate::promote +// +bool TIntermediate::promoteAggregate(TIntermAggregate& node) +{ + TOperator op = node.getOp(); + TIntermSequence& args = node.getSequence(); + const int numArgs = static_cast(args.size()); + + // Presently, only hlsl does intrinsic promotions. + if (getSource() != EShSourceHlsl) + return true; + + // set of opcodes that can be promoted in this manner. + switch (op) { + case EOpAtan: + case EOpClamp: + case EOpCross: + case EOpDistance: + case EOpDot: + case EOpDst: + case EOpFaceForward: + // case EOpFindMSB: TODO: + // case EOpFindLSB: TODO: + case EOpFma: + case EOpMod: + case EOpFrexp: + case EOpLdexp: + case EOpMix: + case EOpLit: + case EOpMax: + case EOpMin: + case EOpModf: + // case EOpGenMul: TODO: + case EOpPow: + case EOpReflect: + case EOpRefract: + // case EOpSinCos: TODO: + case EOpSmoothStep: + case EOpStep: + break; + default: + return true; + } + + // TODO: array and struct behavior + + // Try converting all nodes to the given node's type + TIntermSequence convertedArgs(numArgs, nullptr); + + // Try to convert all types to the nonConvArg type. + for (int nonConvArg = 0; nonConvArg < numArgs; ++nonConvArg) { + // Try converting all args to this arg's type + for (int convArg = 0; convArg < numArgs; ++convArg) { + convertedArgs[convArg] = addConversion(op, args[nonConvArg]->getAsTyped()->getType(), + args[convArg]->getAsTyped()); + } + + // If we successfully converted all the args, use the result. + if (std::all_of(convertedArgs.begin(), convertedArgs.end(), + [](const TIntermNode* node) { return node != nullptr; })) { + + std::swap(args, convertedArgs); + return true; + } + } + + return false; +} + +// Propagate precision qualifiers *up* from children to parent, and then +// back *down* again to the children's subtrees. +void TIntermBinary::updatePrecision() +{ + if (getBasicType() == EbtInt || getBasicType() == EbtUint || + getBasicType() == EbtFloat || getBasicType() == EbtFloat16) { + getQualifier().precision = std::max(right->getQualifier().precision, left->getQualifier().precision); + if (getQualifier().precision != EpqNone) { + left->propagatePrecision(getQualifier().precision); + right->propagatePrecision(getQualifier().precision); + } + } +} + +// Recursively propagate precision qualifiers *down* the subtree of the current node, +// until reaching a node that already has a precision qualifier or otherwise does +// not participate in precision propagation. +void TIntermTyped::propagatePrecision(TPrecisionQualifier newPrecision) +{ + if (getQualifier().precision != EpqNone || + (getBasicType() != EbtInt && getBasicType() != EbtUint && + getBasicType() != EbtFloat && getBasicType() != EbtFloat16)) + return; + + getQualifier().precision = newPrecision; + + TIntermBinary* binaryNode = getAsBinaryNode(); + if (binaryNode) { + binaryNode->getLeft()->propagatePrecision(newPrecision); + binaryNode->getRight()->propagatePrecision(newPrecision); + + return; + } + + TIntermUnary* unaryNode = getAsUnaryNode(); + if (unaryNode) { + unaryNode->getOperand()->propagatePrecision(newPrecision); + + return; + } + + TIntermAggregate* aggregateNode = getAsAggregate(); + if (aggregateNode) { + TIntermSequence operands = aggregateNode->getSequence(); + for (unsigned int i = 0; i < operands.size(); ++i) { + TIntermTyped* typedNode = operands[i]->getAsTyped(); + if (! typedNode) + break; + typedNode->propagatePrecision(newPrecision); + } + + return; + } + + TIntermSelection* selectionNode = getAsSelectionNode(); + if (selectionNode) { + TIntermTyped* typedNode = selectionNode->getTrueBlock()->getAsTyped(); + if (typedNode) { + typedNode->propagatePrecision(newPrecision); + typedNode = selectionNode->getFalseBlock()->getAsTyped(); + if (typedNode) + typedNode->propagatePrecision(newPrecision); + } + + return; + } +} + +TIntermTyped* TIntermediate::promoteConstantUnion(TBasicType promoteTo, TIntermConstantUnion* node) const +{ + const TConstUnionArray& rightUnionArray = node->getConstArray(); + int size = node->getType().computeNumComponents(); + + TConstUnionArray leftUnionArray(size); + + for (int i=0; i < size; i++) { + +#define PROMOTE(Set, CType, Get) leftUnionArray[i].Set(static_cast(rightUnionArray[i].Get())) +#define PROMOTE_TO_BOOL(Get) leftUnionArray[i].setBConst(rightUnionArray[i].Get() != 0) + +#ifdef GLSLANG_WEB +#define TO_ALL(Get) \ + switch (promoteTo) { \ + case EbtFloat: PROMOTE(setDConst, double, Get); break; \ + case EbtInt: PROMOTE(setIConst, int, Get); break; \ + case EbtUint: PROMOTE(setUConst, unsigned int, Get); break; \ + case EbtBool: PROMOTE_TO_BOOL(Get); break; \ + default: return node; \ + } +#else +#define TO_ALL(Get) \ + switch (promoteTo) { \ + case EbtFloat16: PROMOTE(setDConst, double, Get); break; \ + case EbtFloat: PROMOTE(setDConst, double, Get); break; \ + case EbtDouble: PROMOTE(setDConst, double, Get); break; \ + case EbtInt8: PROMOTE(setI8Const, char, Get); break; \ + case EbtInt16: PROMOTE(setI16Const, short, Get); break; \ + case EbtInt: PROMOTE(setIConst, int, Get); break; \ + case EbtInt64: PROMOTE(setI64Const, long long, Get); break; \ + case EbtUint8: PROMOTE(setU8Const, unsigned char, Get); break; \ + case EbtUint16: PROMOTE(setU16Const, unsigned short, Get); break; \ + case EbtUint: PROMOTE(setUConst, unsigned int, Get); break; \ + case EbtUint64: PROMOTE(setU64Const, unsigned long long, Get); break; \ + case EbtBool: PROMOTE_TO_BOOL(Get); break; \ + default: return node; \ + } +#endif + + switch (node->getType().getBasicType()) { + case EbtFloat: TO_ALL(getDConst); break; + case EbtInt: TO_ALL(getIConst); break; + case EbtUint: TO_ALL(getUConst); break; + case EbtBool: TO_ALL(getBConst); break; +#ifndef GLSLANG_WEB + case EbtFloat16: TO_ALL(getDConst); break; + case EbtDouble: TO_ALL(getDConst); break; + case EbtInt8: TO_ALL(getI8Const); break; + case EbtInt16: TO_ALL(getI16Const); break; + case EbtInt64: TO_ALL(getI64Const); break; + case EbtUint8: TO_ALL(getU8Const); break; + case EbtUint16: TO_ALL(getU16Const); break; + case EbtUint64: TO_ALL(getU64Const); break; +#endif + default: return node; + } + } + + const TType& t = node->getType(); + + return addConstantUnion(leftUnionArray, TType(promoteTo, t.getQualifier().storage, t.getVectorSize(), t.getMatrixCols(), t.getMatrixRows()), + node->getLoc()); +} + +void TIntermAggregate::setPragmaTable(const TPragmaTable& pTable) +{ + assert(pragmaTable == nullptr); + pragmaTable = new TPragmaTable; + *pragmaTable = pTable; +} + +// If either node is a specialization constant, while the other is +// a constant (or specialization constant), the result is still +// a specialization constant. +bool TIntermediate::specConstantPropagates(const TIntermTyped& node1, const TIntermTyped& node2) +{ + return (node1.getType().getQualifier().isSpecConstant() && node2.getType().getQualifier().isConstant()) || + (node2.getType().getQualifier().isSpecConstant() && node1.getType().getQualifier().isConstant()); +} + +struct TextureUpgradeAndSamplerRemovalTransform : public TIntermTraverser { + void visitSymbol(TIntermSymbol* symbol) override { + if (symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isTexture()) { + symbol->getWritableType().getSampler().setCombined(true); + } + } + bool visitAggregate(TVisit, TIntermAggregate* ag) override { + using namespace std; + TIntermSequence& seq = ag->getSequence(); + TQualifierList& qual = ag->getQualifierList(); + + // qual and seq are indexed using the same indices, so we have to modify both in lock-step + assert(seq.size() == qual.size() || qual.empty()); + + size_t write = 0; + for (size_t i = 0; i < seq.size(); ++i) { + TIntermSymbol* symbol = seq[i]->getAsSymbolNode(); + if (symbol && symbol->getBasicType() == EbtSampler && symbol->getType().getSampler().isPureSampler()) { + // remove pure sampler variables + continue; + } + + TIntermNode* result = seq[i]; + + // replace constructors with sampler/textures + TIntermAggregate *constructor = seq[i]->getAsAggregate(); + if (constructor && constructor->getOp() == EOpConstructTextureSampler) { + if (!constructor->getSequence().empty()) + result = constructor->getSequence()[0]; + } + + // write new node & qualifier + seq[write] = result; + if (!qual.empty()) + qual[write] = qual[i]; + write++; + } + + seq.resize(write); + if (!qual.empty()) + qual.resize(write); + + return true; + } +}; + +void TIntermediate::performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root) +{ + TextureUpgradeAndSamplerRemovalTransform transform; + root->traverse(&transform); +} + +const char* TIntermediate::getResourceName(TResourceType res) +{ + switch (res) { + case EResSampler: return "shift-sampler-binding"; + case EResTexture: return "shift-texture-binding"; + case EResImage: return "shift-image-binding"; + case EResUbo: return "shift-UBO-binding"; + case EResSsbo: return "shift-ssbo-binding"; + case EResUav: return "shift-uav-binding"; + default: + assert(0); // internal error: should only be called with valid resource types. + return nullptr; + } +} + + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/LiveTraverser.h b/third_party/glslang/glslang/MachineIndependent/LiveTraverser.h new file mode 100644 index 0000000..9b39b59 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/LiveTraverser.h @@ -0,0 +1,168 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +#include "../Include/Common.h" +#include "reflection.h" +#include "localintermediate.h" + +#include "gl_types.h" + +#include +#include + +namespace glslang { + +// +// The traverser: mostly pass through, except +// - processing function-call nodes to push live functions onto the stack of functions to process +// - processing selection nodes to trim semantically dead code +// +// This is in the glslang namespace directly so it can be a friend of TReflection. +// This can be derived from to implement reflection database traversers or +// binding mappers: anything that wants to traverse the live subset of the tree. +// + +class TLiveTraverser : public TIntermTraverser { +public: + TLiveTraverser(const TIntermediate& i, bool traverseAll = false, + bool preVisit = true, bool inVisit = false, bool postVisit = false) : + TIntermTraverser(preVisit, inVisit, postVisit), + intermediate(i), traverseAll(traverseAll) + { } + + // + // Given a function name, find its subroot in the tree, and push it onto the stack of + // functions left to process. + // + void pushFunction(const TString& name) + { + TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence(); + for (unsigned int f = 0; f < globals.size(); ++f) { + TIntermAggregate* candidate = globals[f]->getAsAggregate(); + if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) { + destinations.push_back(candidate); + break; + } + } + } + + void pushGlobalReference(const TString& name) + { + TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence(); + for (unsigned int f = 0; f < globals.size(); ++f) { + TIntermAggregate* candidate = globals[f]->getAsAggregate(); + if (candidate && candidate->getOp() == EOpSequence && + candidate->getSequence().size() == 1 && + candidate->getSequence()[0]->getAsBinaryNode()) { + TIntermBinary* binary = candidate->getSequence()[0]->getAsBinaryNode(); + TIntermSymbol* symbol = binary->getLeft()->getAsSymbolNode(); + if (symbol && symbol->getQualifier().storage == EvqGlobal && + symbol->getName() == name) { + destinations.push_back(candidate); + break; + } + } + } + } + + typedef std::list TDestinationStack; + TDestinationStack destinations; + +protected: + // To catch which function calls are not dead, and hence which functions must be visited. + virtual bool visitAggregate(TVisit, TIntermAggregate* node) + { + if (!traverseAll) + if (node->getOp() == EOpFunctionCall) + addFunctionCall(node); + + return true; // traverse this subtree + } + + // To prune semantically dead paths. + virtual bool visitSelection(TVisit /* visit */, TIntermSelection* node) + { + if (traverseAll) + return true; // traverse all code + + TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion(); + if (constant) { + // cull the path that is dead + if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock()) + node->getTrueBlock()->traverse(this); + if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock()) + node->getFalseBlock()->traverse(this); + + return false; // don't traverse any more, we did it all above + } else + return true; // traverse the whole subtree + } + + // Track live functions as well as uniforms, so that we don't visit dead functions + // and only visit each function once. + void addFunctionCall(TIntermAggregate* call) + { + // just use the map to ensure we process each function at most once + if (liveFunctions.find(call->getName()) == liveFunctions.end()) { + liveFunctions.insert(call->getName()); + pushFunction(call->getName()); + } + } + + void addGlobalReference(const TString& name) + { + // just use the map to ensure we process each global at most once + if (liveGlobals.find(name) == liveGlobals.end()) { + liveGlobals.insert(name); + pushGlobalReference(name); + } + } + + const TIntermediate& intermediate; + typedef std::unordered_set TLiveFunctions; + TLiveFunctions liveFunctions; + typedef std::unordered_set TLiveGlobals; + TLiveGlobals liveGlobals; + bool traverseAll; + +private: + // prevent copy & copy construct + TLiveTraverser(TLiveTraverser&); + TLiveTraverser& operator=(TLiveTraverser&); +}; + +} // namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp b/third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp new file mode 100644 index 0000000..3efa27a --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/ParseContextBase.cpp @@ -0,0 +1,663 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// Implement the TParseContextBase class. + +#include + +#include "ParseHelper.h" + +extern int yyparse(glslang::TParseContext*); + +namespace glslang { + +// +// Used to output syntax, parsing, and semantic errors. +// + +void TParseContextBase::outputMessage(const TSourceLoc& loc, const char* szReason, + const char* szToken, + const char* szExtraInfoFormat, + TPrefixType prefix, va_list args) +{ + const int maxSize = MaxTokenLength + 200; + char szExtraInfo[maxSize]; + + safe_vsprintf(szExtraInfo, maxSize, szExtraInfoFormat, args); + + infoSink.info.prefix(prefix); + infoSink.info.location(loc); + infoSink.info << "'" << szToken << "' : " << szReason << " " << szExtraInfo << "\n"; + + if (prefix == EPrefixError) { + ++numErrors; + } +} + +#if !defined(GLSLANG_WEB) || defined(GLSLANG_WEB_DEVEL) + +void C_DECL TParseContextBase::error(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + if (messages & EShMsgOnlyPreprocessor) + return; + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); + va_end(args); + + if ((messages & EShMsgCascadingErrors) == 0) + currentScanner->setEndOfInput(); +} + +void C_DECL TParseContextBase::warn(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + if (suppressWarnings()) + return; + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); + va_end(args); +} + +void C_DECL TParseContextBase::ppError(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args); + va_end(args); + + if ((messages & EShMsgCascadingErrors) == 0) + currentScanner->setEndOfInput(); +} + +void C_DECL TParseContextBase::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) +{ + va_list args; + va_start(args, szExtraInfoFormat); + outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args); + va_end(args); +} + +#endif + +// +// Both test and if necessary, spit out an error, to see if the node is really +// an l-value that can be operated on this way. +// +// Returns true if there was an error. +// +bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + TIntermBinary* binaryNode = node->getAsBinaryNode(); + + const char* symbol = nullptr; + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (symNode != nullptr) + symbol = symNode->getName().c_str(); + + const char* message = nullptr; + switch (node->getQualifier().storage) { + case EvqConst: message = "can't modify a const"; break; + case EvqConstReadOnly: message = "can't modify a const"; break; + case EvqUniform: message = "can't modify a uniform"; break; +#ifndef GLSLANG_WEB + case EvqBuffer: + if (node->getQualifier().isReadOnly()) + message = "can't modify a readonly buffer"; + if (node->getQualifier().isShaderRecord()) + message = "can't modify a shaderrecordnv qualified buffer"; + break; + case EvqHitAttr: + if (language != EShLangIntersect) + message = "cannot modify hitAttributeNV in this stage"; + break; +#endif + + default: + // + // Type that can't be written to? + // + switch (node->getBasicType()) { + case EbtSampler: + message = "can't modify a sampler"; + break; + case EbtVoid: + message = "can't modify void"; + break; +#ifndef GLSLANG_WEB + case EbtAtomicUint: + message = "can't modify an atomic_uint"; + break; + case EbtAccStruct: + message = "can't modify accelerationStructureNV"; + break; + case EbtRayQuery: + message = "can't modify rayQueryEXT"; + break; +#endif + default: + break; + } + } + + if (message == nullptr && binaryNode == nullptr && symNode == nullptr) { + error(loc, " l-value required", op, "", ""); + + return true; + } + + // + // Everything else is okay, no error. + // + if (message == nullptr) + { + if (binaryNode) { + switch (binaryNode->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: // fall through + case EOpIndexDirectStruct: // fall through + case EOpVectorSwizzle: + case EOpMatrixSwizzle: + return lValueErrorCheck(loc, op, binaryNode->getLeft()); + default: + break; + } + error(loc, " l-value required", op, "", ""); + + return true; + } + return false; + } + + // + // If we get here, we have an error and a message. + // + const TIntermTyped* leftMostTypeNode = TIntermediate::findLValueBase(node, true); + + if (symNode) + error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message); + else + if (binaryNode && binaryNode->getAsOperator()->getOp() == EOpIndexDirectStruct) + if(IsAnonymous(leftMostTypeNode->getAsSymbolNode()->getName())) + error(loc, " l-value required", op, "\"%s\" (%s)", leftMostTypeNode->getAsSymbolNode()->getAccessName().c_str(), message); + else + error(loc, " l-value required", op, "\"%s\" (%s)", leftMostTypeNode->getAsSymbolNode()->getName().c_str(), message); + else + error(loc, " l-value required", op, "(%s)", message); + + return true; +} + +// Test for and give an error if the node can't be read from. +void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + TIntermBinary* binaryNode = node->getAsBinaryNode(); + const TIntermSymbol* symNode = node->getAsSymbolNode(); + + if (! node) + return; + + if (node->getQualifier().isWriteOnly()) { + const TIntermTyped* leftMostTypeNode = TIntermediate::findLValueBase(node, true); + + if (symNode != nullptr) + error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str()); + else if (binaryNode && + (binaryNode->getAsOperator()->getOp() == EOpIndexDirectStruct || + binaryNode->getAsOperator()->getOp() == EOpIndexDirect)) + if(IsAnonymous(leftMostTypeNode->getAsSymbolNode()->getName())) + error(loc, "can't read from writeonly object: ", op, leftMostTypeNode->getAsSymbolNode()->getAccessName().c_str()); + else + error(loc, "can't read from writeonly object: ", op, leftMostTypeNode->getAsSymbolNode()->getName().c_str()); + else + error(loc, "can't read from writeonly object: ", op, ""); + + } else { + if (binaryNode) { + switch (binaryNode->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + case EOpVectorSwizzle: + case EOpMatrixSwizzle: + rValueErrorCheck(loc, op, binaryNode->getLeft()); + default: + break; + } + } + } +} + +// Add 'symbol' to the list of deferred linkage symbols, which +// are later processed in finish(), at which point the symbol +// must still be valid. +// It is okay if the symbol's type will be subsequently edited; +// the modifications will be tracked. +// Order is preserved, to avoid creating novel forward references. +void TParseContextBase::trackLinkage(TSymbol& symbol) +{ + if (!parsingBuiltins) + linkageSymbols.push_back(&symbol); +} + +// Ensure index is in bounds, correct if necessary. +// Give an error if not. +void TParseContextBase::checkIndex(const TSourceLoc& loc, const TType& type, int& index) +{ + const auto sizeIsSpecializationExpression = [&type]() { + return type.containsSpecializationSize() && + type.getArraySizes()->getOuterNode() != nullptr && + type.getArraySizes()->getOuterNode()->getAsSymbolNode() == nullptr; }; + + if (index < 0) { + error(loc, "", "[", "index out of range '%d'", index); + index = 0; + } else if (type.isArray()) { + if (type.isSizedArray() && !sizeIsSpecializationExpression() && + index >= type.getOuterArraySize()) { + error(loc, "", "[", "array index out of range '%d'", index); + index = type.getOuterArraySize() - 1; + } + } else if (type.isVector()) { + if (index >= type.getVectorSize()) { + error(loc, "", "[", "vector index out of range '%d'", index); + index = type.getVectorSize() - 1; + } + } else if (type.isMatrix()) { + if (index >= type.getMatrixCols()) { + error(loc, "", "[", "matrix index out of range '%d'", index); + index = type.getMatrixCols() - 1; + } + } +} + +// Make a shared symbol have a non-shared version that can be edited by the current +// compile, such that editing its type will not change the shared version and will +// effect all nodes already sharing it (non-shallow type), +// or adopting its full type after being edited (shallow type). +void TParseContextBase::makeEditable(TSymbol*& symbol) +{ + // copyUp() does a deep copy of the type. + symbol = symbolTable.copyUp(symbol); + + // Save it (deferred, so it can be edited first) in the AST for linker use. + if (symbol) + trackLinkage(*symbol); +} + +// Return a writable version of the variable 'name'. +// +// Return nullptr if 'name' is not found. This should mean +// something is seriously wrong (e.g., compiler asking self for +// built-in that doesn't exist). +TVariable* TParseContextBase::getEditableVariable(const char* name) +{ + bool builtIn; + TSymbol* symbol = symbolTable.find(name, &builtIn); + + assert(symbol != nullptr); + if (symbol == nullptr) + return nullptr; + + if (builtIn) + makeEditable(symbol); + + return symbol->getAsVariable(); +} + +// Select the best matching function for 'call' from 'candidateList'. +// +// Assumptions +// +// There is no exact match, so a selection algorithm needs to run. That is, the +// language-specific handler should check for exact match first, to +// decide what to do, before calling this selector. +// +// Input +// +// * list of candidate signatures to select from +// * the call +// * a predicate function convertible(from, to) that says whether or not type +// 'from' can implicitly convert to type 'to' (it includes the case of what +// the calling language would consider a matching type with no conversion +// needed) +// * a predicate function better(from1, from2, to1, to2) that says whether or +// not a conversion from <-> to2 is considered better than a conversion +// from <-> to1 (both in and out directions need testing, as declared by the +// formal parameter) +// +// Output +// +// * best matching candidate (or none, if no viable candidates found) +// * whether there was a tie for the best match (ambiguous overload selection, +// caller's choice for how to report) +// +const TFunction* TParseContextBase::selectFunction( + const TVector candidateList, + const TFunction& call, + std::function convertible, + std::function better, + /* output */ bool& tie) +{ +// +// Operation +// +// 1. Prune the input list of candidates down to a list of viable candidates, +// where each viable candidate has +// +// * at least as many parameters as there are calling arguments, with any +// remaining parameters being optional or having default values +// * each parameter is true under convertible(A, B), where A is the calling +// type for in and B is the formal type, and in addition, for out B is the +// calling type and A is the formal type +// +// 2. If there are no viable candidates, return with no match. +// +// 3. If there is only one viable candidate, it is the best match. +// +// 4. If there are multiple viable candidates, select the first viable candidate +// as the incumbent. Compare the incumbent to the next viable candidate, and if +// that candidate is better (bullets below), make it the incumbent. Repeat, with +// a linear walk through the viable candidate list. The final incumbent will be +// returned as the best match. A viable candidate is better than the incumbent if +// +// * it has a function argument with a better(...) conversion than the incumbent, +// for all directions needed by in and out +// * the incumbent has no argument with a better(...) conversion then the +// candidate, for either in or out (as needed) +// +// 5. Check for ambiguity by comparing the best match against all other viable +// candidates. If any other viable candidate has a function argument with a +// better(...) conversion than the best candidate (for either in or out +// directions), return that there was a tie for best. +// + + tie = false; + + // 1. prune to viable... + TVector viableCandidates; + for (auto it = candidateList.begin(); it != candidateList.end(); ++it) { + const TFunction& candidate = *(*it); + + // to even be a potential match, number of arguments must be >= the number of + // fixed (non-default) parameters, and <= the total (including parameter with defaults). + if (call.getParamCount() < candidate.getFixedParamCount() || + call.getParamCount() > candidate.getParamCount()) + continue; + + // see if arguments are convertible + bool viable = true; + + // The call can have fewer parameters than the candidate, if some have defaults. + const int paramCount = std::min(call.getParamCount(), candidate.getParamCount()); + for (int param = 0; param < paramCount; ++param) { + if (candidate[param].type->getQualifier().isParamInput()) { + if (! convertible(*call[param].type, *candidate[param].type, candidate.getBuiltInOp(), param)) { + viable = false; + break; + } + } + if (candidate[param].type->getQualifier().isParamOutput()) { + if (! convertible(*candidate[param].type, *call[param].type, candidate.getBuiltInOp(), param)) { + viable = false; + break; + } + } + } + + if (viable) + viableCandidates.push_back(&candidate); + } + + // 2. none viable... + if (viableCandidates.size() == 0) + return nullptr; + + // 3. only one viable... + if (viableCandidates.size() == 1) + return viableCandidates.front(); + + // 4. find best... + const auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { + // is call -> can2 better than call -> can1 for any parameter + bool hasBetterParam = false; + for (int param = 0; param < call.getParamCount(); ++param) { + if (better(*call[param].type, *can1[param].type, *can2[param].type)) { + hasBetterParam = true; + break; + } + } + return hasBetterParam; + }; + + const auto equivalentParams = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool { + // is call -> can2 equivalent to call -> can1 for all the call parameters? + for (int param = 0; param < call.getParamCount(); ++param) { + if (better(*call[param].type, *can1[param].type, *can2[param].type) || + better(*call[param].type, *can2[param].type, *can1[param].type)) + return false; + } + return true; + }; + + const TFunction* incumbent = viableCandidates.front(); + for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) { + const TFunction& candidate = *(*it); + if (betterParam(*incumbent, candidate) && ! betterParam(candidate, *incumbent)) + incumbent = &candidate; + } + + // 5. ambiguity... + for (auto it = viableCandidates.begin(); it != viableCandidates.end(); ++it) { + if (incumbent == *it) + continue; + const TFunction& candidate = *(*it); + + // In the case of default parameters, it may have an identical initial set, which is + // also ambiguous + if (betterParam(*incumbent, candidate) || equivalentParams(*incumbent, candidate)) + tie = true; + } + + return incumbent; +} + +// +// Look at a '.' field selector string and change it into numerical selectors +// for a vector or scalar. +// +// Always return some form of swizzle, so the result is always usable. +// +void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize, + TSwizzleSelectors& selector) +{ + // Too long? + if (compString.size() > MaxSwizzleSelectors) + error(loc, "vector swizzle too long", compString.c_str(), ""); + + // Use this to test that all swizzle characters are from the same swizzle-namespace-set + enum { + exyzw, + ergba, + estpq, + } fieldSet[MaxSwizzleSelectors]; + + // Decode the swizzle string. + int size = std::min(MaxSwizzleSelectors, (int)compString.size()); + for (int i = 0; i < size; ++i) { + switch (compString[i]) { + case 'x': + selector.push_back(0); + fieldSet[i] = exyzw; + break; + case 'r': + selector.push_back(0); + fieldSet[i] = ergba; + break; + case 's': + selector.push_back(0); + fieldSet[i] = estpq; + break; + + case 'y': + selector.push_back(1); + fieldSet[i] = exyzw; + break; + case 'g': + selector.push_back(1); + fieldSet[i] = ergba; + break; + case 't': + selector.push_back(1); + fieldSet[i] = estpq; + break; + + case 'z': + selector.push_back(2); + fieldSet[i] = exyzw; + break; + case 'b': + selector.push_back(2); + fieldSet[i] = ergba; + break; + case 'p': + selector.push_back(2); + fieldSet[i] = estpq; + break; + + case 'w': + selector.push_back(3); + fieldSet[i] = exyzw; + break; + case 'a': + selector.push_back(3); + fieldSet[i] = ergba; + break; + case 'q': + selector.push_back(3); + fieldSet[i] = estpq; + break; + + default: + error(loc, "unknown swizzle selection", compString.c_str(), ""); + break; + } + } + + // Additional error checking. + for (int i = 0; i < selector.size(); ++i) { + if (selector[i] >= vecSize) { + error(loc, "vector swizzle selection out of range", compString.c_str(), ""); + selector.resize(i); + break; + } + + if (i > 0 && fieldSet[i] != fieldSet[i-1]) { + error(loc, "vector swizzle selectors not from the same set", compString.c_str(), ""); + selector.resize(i); + break; + } + } + + // Ensure it is valid. + if (selector.size() == 0) + selector.push_back(0); +} + +#ifdef ENABLE_HLSL +// +// Make the passed-in variable information become a member of the +// global uniform block. If this doesn't exist yet, make it. +// +void TParseContextBase::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) +{ + // Make the global block, if not yet made. + if (globalUniformBlock == nullptr) { + TQualifier blockQualifier; + blockQualifier.clear(); + blockQualifier.storage = EvqUniform; + TType blockType(new TTypeList, *NewPoolTString(getGlobalUniformBlockName()), blockQualifier); + setUniformBlockDefaults(blockType); + globalUniformBlock = new TVariable(NewPoolTString(""), blockType, true); + firstNewMember = 0; + } + + // Update with binding and set + globalUniformBlock->getWritableType().getQualifier().layoutBinding = globalUniformBinding; + globalUniformBlock->getWritableType().getQualifier().layoutSet = globalUniformSet; + + // Add the requested member as a member to the global block. + TType* type = new TType; + type->shallowCopy(memberType); + type->setFieldName(memberName); + if (typeList) + type->setStruct(typeList); + TTypeLoc typeLoc = {type, loc}; + globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc); + + // Insert into the symbol table. + if (firstNewMember == 0) { + // This is the first request; we need a normal symbol table insert + if (symbolTable.insert(*globalUniformBlock)) + trackLinkage(*globalUniformBlock); + else + error(loc, "failed to insert the global constant buffer", "uniform", ""); + } else { + // This is a follow-on request; we need to amend the first insert + symbolTable.amend(*globalUniformBlock, firstNewMember); + } + + ++firstNewMember; +} +#endif + +void TParseContextBase::finish() +{ + if (parsingBuiltins) + return; + + // Transfer the linkage symbols to AST nodes, preserving order. + TIntermAggregate* linkage = new TIntermAggregate; + for (auto i = linkageSymbols.begin(); i != linkageSymbols.end(); ++i) + intermediate.addSymbolLinkageNode(linkage, **i); + intermediate.addSymbolLinkageNodes(linkage, getLanguage(), symbolTable); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp b/third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp new file mode 100644 index 0000000..63fb957 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/ParseHelper.cpp @@ -0,0 +1,8676 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2015 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// Copyright (C) 2017, 2019 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "ParseHelper.h" +#include "Scan.h" + +#include "../OSDependent/osinclude.h" +#include + +#include "preprocessor/PpContext.h" + +extern int yyparse(glslang::TParseContext*); + +namespace glslang { + +TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, + int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + TInfoSink& infoSink, bool forwardCompatible, EShMessages messages, + const TString* entryPoint) : + TParseContextBase(symbolTable, interm, parsingBuiltins, version, profile, spvVersion, language, + infoSink, forwardCompatible, messages, entryPoint), + inMain(false), + blockName(nullptr), + limits(resources.limits) +#ifndef GLSLANG_WEB + , + atomicUintOffsets(nullptr), anyIndexLimits(false) +#endif +{ + // decide whether precision qualifiers should be ignored or respected + if (isEsProfile() || spvVersion.vulkan > 0) { + precisionManager.respectPrecisionQualifiers(); + if (! parsingBuiltins && language == EShLangFragment && !isEsProfile() && spvVersion.vulkan > 0) + precisionManager.warnAboutDefaults(); + } + + setPrecisionDefaults(); + + globalUniformDefaults.clear(); + globalUniformDefaults.layoutMatrix = ElmColumnMajor; + globalUniformDefaults.layoutPacking = spvVersion.spv != 0 ? ElpStd140 : ElpShared; + + globalBufferDefaults.clear(); + globalBufferDefaults.layoutMatrix = ElmColumnMajor; + globalBufferDefaults.layoutPacking = spvVersion.spv != 0 ? ElpStd430 : ElpShared; + + // use storage buffer on SPIR-V 1.3 and up + if (spvVersion.spv >= EShTargetSpv_1_3) + intermediate.setUseStorageBuffer(); + + globalInputDefaults.clear(); + globalOutputDefaults.clear(); + +#ifndef GLSLANG_WEB + // "Shaders in the transform + // feedback capturing mode have an initial global default of + // layout(xfb_buffer = 0) out;" + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry) + globalOutputDefaults.layoutXfbBuffer = 0; + + if (language == EShLangGeometry) + globalOutputDefaults.layoutStream = 0; +#endif + + if (entryPoint != nullptr && entryPoint->size() > 0 && *entryPoint != "main") + infoSink.info.message(EPrefixError, "Source entry point must be \"main\""); +} + +TParseContext::~TParseContext() +{ +#ifndef GLSLANG_WEB + delete [] atomicUintOffsets; +#endif +} + +// Set up all default precisions as needed by the current environment. +// Intended just as a TParseContext constructor helper. +void TParseContext::setPrecisionDefaults() +{ + // Set all precision defaults to EpqNone, which is correct for all types + // when not obeying precision qualifiers, and correct for types that don't + // have defaults (thus getting an error on use) when obeying precision + // qualifiers. + + for (int type = 0; type < EbtNumTypes; ++type) + defaultPrecision[type] = EpqNone; + + for (int type = 0; type < maxSamplerIndex; ++type) + defaultSamplerPrecision[type] = EpqNone; + + // replace with real precision defaults for those that have them + if (obeyPrecisionQualifiers()) { + if (isEsProfile()) { + // Most don't have defaults, a few default to lowp. + TSampler sampler; + sampler.set(EbtFloat, Esd2D); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + sampler.set(EbtFloat, EsdCube); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + sampler.set(EbtFloat, Esd2D); + sampler.setExternal(true); + defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; + } + + // If we are parsing built-in computational variables/functions, it is meaningful to record + // whether the built-in has no precision qualifier, as that ambiguity + // is used to resolve the precision from the supplied arguments/operands instead. + // So, we don't actually want to replace EpqNone with a default precision for built-ins. + if (! parsingBuiltins) { + if (isEsProfile() && language == EShLangFragment) { + defaultPrecision[EbtInt] = EpqMedium; + defaultPrecision[EbtUint] = EpqMedium; + } else { + defaultPrecision[EbtInt] = EpqHigh; + defaultPrecision[EbtUint] = EpqHigh; + defaultPrecision[EbtFloat] = EpqHigh; + } + + if (!isEsProfile()) { + // Non-ES profile + // All sampler precisions default to highp. + for (int type = 0; type < maxSamplerIndex; ++type) + defaultSamplerPrecision[type] = EpqHigh; + } + } + + defaultPrecision[EbtSampler] = EpqLow; + defaultPrecision[EbtAtomicUint] = EpqHigh; + } +} + +void TParseContext::setLimits(const TBuiltInResource& r) +{ + resources = r; + intermediate.setLimits(r); + +#ifndef GLSLANG_WEB + anyIndexLimits = ! limits.generalAttributeMatrixVectorIndexing || + ! limits.generalConstantMatrixVectorIndexing || + ! limits.generalSamplerIndexing || + ! limits.generalUniformIndexing || + ! limits.generalVariableIndexing || + ! limits.generalVaryingIndexing; + + + // "Each binding point tracks its own current default offset for + // inheritance of subsequent variables using the same binding. The initial state of compilation is that all + // binding points have an offset of 0." + atomicUintOffsets = new int[resources.maxAtomicCounterBindings]; + for (int b = 0; b < resources.maxAtomicCounterBindings; ++b) + atomicUintOffsets[b] = 0; +#endif +} + +// +// Parse an array of strings using yyparse, going through the +// preprocessor to tokenize the shader strings, then through +// the GLSL scanner. +// +// Returns true for successful acceptance of the shader, false if any errors. +// +bool TParseContext::parseShaderStrings(TPpContext& ppContext, TInputScanner& input, bool versionWillBeError) +{ + currentScanner = &input; + ppContext.setInput(input, versionWillBeError); + yyparse(this); + + finish(); + + return numErrors == 0; +} + +// This is called from bison when it has a parse (syntax) error +// Note though that to stop cascading errors, we set EOF, which +// will usually cause a syntax error, so be more accurate that +// compilation is terminating. +void TParseContext::parserError(const char* s) +{ + if (! getScanner()->atEndOfInput() || numErrors == 0) + error(getCurrentLoc(), "", "", s, ""); + else + error(getCurrentLoc(), "compilation terminated", "", ""); +} + +void TParseContext::handlePragma(const TSourceLoc& loc, const TVector& tokens) +{ +#ifndef GLSLANG_WEB + if (pragmaCallback) + pragmaCallback(loc.line, tokens); + + if (tokens.size() == 0) + return; + + if (tokens[0].compare("optimize") == 0) { + if (tokens.size() != 4) { + error(loc, "optimize pragma syntax is incorrect", "#pragma", ""); + return; + } + + if (tokens[1].compare("(") != 0) { + error(loc, "\"(\" expected after 'optimize' keyword", "#pragma", ""); + return; + } + + if (tokens[2].compare("on") == 0) + contextPragma.optimize = true; + else if (tokens[2].compare("off") == 0) + contextPragma.optimize = false; + else { + if(relaxedErrors()) + // If an implementation does not recognize the tokens following #pragma, then it will ignore that pragma. + warn(loc, "\"on\" or \"off\" expected after '(' for 'optimize' pragma", "#pragma", ""); + return; + } + + if (tokens[3].compare(")") != 0) { + error(loc, "\")\" expected to end 'optimize' pragma", "#pragma", ""); + return; + } + } else if (tokens[0].compare("debug") == 0) { + if (tokens.size() != 4) { + error(loc, "debug pragma syntax is incorrect", "#pragma", ""); + return; + } + + if (tokens[1].compare("(") != 0) { + error(loc, "\"(\" expected after 'debug' keyword", "#pragma", ""); + return; + } + + if (tokens[2].compare("on") == 0) + contextPragma.debug = true; + else if (tokens[2].compare("off") == 0) + contextPragma.debug = false; + else { + if(relaxedErrors()) + // If an implementation does not recognize the tokens following #pragma, then it will ignore that pragma. + warn(loc, "\"on\" or \"off\" expected after '(' for 'debug' pragma", "#pragma", ""); + return; + } + + if (tokens[3].compare(")") != 0) { + error(loc, "\")\" expected to end 'debug' pragma", "#pragma", ""); + return; + } + } else if (spvVersion.spv > 0 && tokens[0].compare("use_storage_buffer") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + intermediate.setUseStorageBuffer(); + } else if (spvVersion.spv > 0 && tokens[0].compare("use_vulkan_memory_model") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + intermediate.setUseVulkanMemoryModel(); + } else if (spvVersion.spv > 0 && tokens[0].compare("use_variable_pointers") == 0) { + if (tokens.size() != 1) + error(loc, "extra tokens", "#pragma", ""); + if (spvVersion.spv < glslang::EShTargetSpv_1_3) + error(loc, "requires SPIR-V 1.3", "#pragma use_variable_pointers", ""); + intermediate.setUseVariablePointers(); + } else if (tokens[0].compare("once") == 0) { + warn(loc, "not implemented", "#pragma once", ""); + } else if (tokens[0].compare("glslang_binary_double_output") == 0) + intermediate.setBinaryDoubleOutput(); +#endif +} + +// +// Handle seeing a variable identifier in the grammar. +// +TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symbol, const TString* string) +{ + TIntermTyped* node = nullptr; + + // Error check for requiring specific extensions present. + if (symbol && symbol->getNumExtensions()) + requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str()); + +#ifndef GLSLANG_WEB + if (symbol && symbol->isReadOnly()) { + // All shared things containing an unsized array must be copied up + // on first use, so that all future references will share its array structure, + // so that editing the implicit size will effect all nodes consuming it, + // and so that editing the implicit size won't change the shared one. + // + // If this is a variable or a block, check it and all it contains, but if this + // is a member of an anonymous block, check the whole block, as the whole block + // will need to be copied up if it contains an unsized array. + // + // This check is being done before the block-name check further down, so guard + // for that too. + if (!symbol->getType().isUnusableName()) { + if (symbol->getType().containsUnsizedArray() || + (symbol->getAsAnonMember() && + symbol->getAsAnonMember()->getAnonContainer().getType().containsUnsizedArray())) + makeEditable(symbol); + } + } +#endif + + const TVariable* variable; + const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr; + if (anon) { + // It was a member of an anonymous container. + + // Create a subtree for its dereference. + variable = anon->getAnonContainer().getAsVariable(); + TIntermTyped* container = intermediate.addSymbol(*variable, loc); + TIntermTyped* constNode = intermediate.addConstantUnion(anon->getMemberNumber(), loc); + node = intermediate.addIndex(EOpIndexDirectStruct, container, constNode, loc); + + node->setType(*(*variable->getType().getStruct())[anon->getMemberNumber()].type); + if (node->getType().hiddenMember()) + error(loc, "member of nameless block was not redeclared", string->c_str(), ""); + } else { + // Not a member of an anonymous container. + + // The symbol table search was done in the lexical phase. + // See if it was a variable. + variable = symbol ? symbol->getAsVariable() : nullptr; + if (variable) { + if (variable->getType().isUnusableName()) { + error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), ""); + variable = nullptr; + } + } else { + if (symbol) + error(loc, "variable name expected", string->c_str(), ""); + } + + // Recovery, if it wasn't found or was not a variable. + if (! variable) + variable = new TVariable(string, TType(EbtVoid)); + + if (variable->getType().getQualifier().isFrontEndConstant()) + node = intermediate.addConstantUnion(variable->getConstArray(), variable->getType(), loc); + else + node = intermediate.addSymbol(*variable, loc); + } + + if (variable->getType().getQualifier().isIo()) + intermediate.addIoAccessed(*string); + + if (variable->getType().isReference() && + variable->getType().getQualifier().bufferReferenceNeedsVulkanMemoryModel()) { + intermediate.setUseVulkanMemoryModel(); + } + + return node; +} + +// +// Handle seeing a base[index] dereference in the grammar. +// +TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIntermTyped* base, TIntermTyped* index) +{ + int indexValue = 0; + if (index->getQualifier().isFrontEndConstant()) + indexValue = index->getAsConstantUnion()->getConstArray()[0].getIConst(); + + // basic type checks... + variableCheck(base); + + if (! base->isArray() && ! base->isMatrix() && ! base->isVector() && ! base->getType().isCoopMat() && + ! base->isReference()) { + if (base->getAsSymbolNode()) + error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), ""); + else + error(loc, " left of '[' is not of type array, matrix, or vector ", "expression", ""); + + // Insert dummy error-recovery result + return intermediate.addConstantUnion(0.0, EbtFloat, loc); + } + + if (!base->isArray() && base->isVector()) { + if (base->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, "[", "does not operate on types containing float16"); + if (base->getType().contains16BitInt()) + requireInt16Arithmetic(loc, "[", "does not operate on types containing (u)int16"); + if (base->getType().contains8BitInt()) + requireInt8Arithmetic(loc, "[", "does not operate on types containing (u)int8"); + } + + // check for constant folding + if (base->getType().getQualifier().isFrontEndConstant() && index->getQualifier().isFrontEndConstant()) { + // both base and index are front-end constants + checkIndex(loc, base->getType(), indexValue); + return intermediate.foldDereference(base, indexValue, loc); + } + + // at least one of base and index is not a front-end constant variable... + TIntermTyped* result = nullptr; + +#ifndef GLSLANG_WEB + if (base->isReference() && ! base->isArray()) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference2, "buffer reference indexing"); + if (base->getType().getReferentType()->containsUnsizedArray()) { + error(loc, "cannot index reference to buffer containing an unsized array", "", ""); + result = nullptr; + } else { + result = intermediate.addBinaryMath(EOpAdd, base, index, loc); + if (result != nullptr) + result->setType(base->getType()); + } + if (result == nullptr) { + error(loc, "cannot index buffer reference", "", ""); + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + } + return result; + } + if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) + handleIoResizeArrayAccess(loc, base); +#endif + + if (index->getQualifier().isFrontEndConstant()) + checkIndex(loc, base->getType(), indexValue); + + if (index->getQualifier().isFrontEndConstant()) { +#ifndef GLSLANG_WEB + if (base->getType().isUnsizedArray()) { + base->getWritableType().updateImplicitArraySize(indexValue + 1); + // For 2D per-view builtin arrays, update the inner dimension size in parent type + if (base->getQualifier().isPerView() && base->getQualifier().builtIn != EbvNone) { + TIntermBinary* binaryNode = base->getAsBinaryNode(); + if (binaryNode) { + TType& leftType = binaryNode->getLeft()->getWritableType(); + TArraySizes& arraySizes = *leftType.getArraySizes(); + assert(arraySizes.getNumDims() == 2); + arraySizes.setDimSize(1, std::max(arraySizes.getDimSize(1), indexValue + 1)); + } + } + } else +#endif + checkIndex(loc, base->getType(), indexValue); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + } else { +#ifndef GLSLANG_WEB + if (base->getType().isUnsizedArray()) { + // we have a variable index into an unsized array, which is okay, + // depending on the situation + if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) + error(loc, "", "[", "array must be sized by a redeclaration or layout qualifier before being indexed with a variable"); + else { + // it is okay for a run-time sized array + checkRuntimeSizable(loc, *base); + } + base->getWritableType().setArrayVariablyIndexed(); + } +#endif + if (base->getBasicType() == EbtBlock) { + if (base->getQualifier().storage == EvqBuffer) + requireProfile(base->getLoc(), ~EEsProfile, "variable indexing buffer block array"); + else if (base->getQualifier().storage == EvqUniform) + profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "variable indexing uniform block array"); + else { + // input/output blocks either don't exist or can't be variably indexed + } + } else if (language == EShLangFragment && base->getQualifier().isPipeOutput()) + requireProfile(base->getLoc(), ~EEsProfile, "variable indexing fragment shader output array"); + else if (base->getBasicType() == EbtSampler && version >= 130) { + const char* explanation = "variable indexing sampler array"; + requireProfile(base->getLoc(), EEsProfile | ECoreProfile | ECompatibilityProfile, explanation); + profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, explanation); + profileRequires(base->getLoc(), ECoreProfile | ECompatibilityProfile, 400, nullptr, explanation); + } + + result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); + } + + // Insert valid dereferenced result type + TType newType(base->getType(), 0); + if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) { + newType.getQualifier().storage = EvqConst; + // If base or index is a specialization constant, the result should also be a specialization constant. + if (base->getType().getQualifier().isSpecConstant() || index->getQualifier().isSpecConstant()) { + newType.getQualifier().makeSpecConstant(); + } + } else { + newType.getQualifier().storage = EvqTemporary; + newType.getQualifier().specConstant = false; + } + result->setType(newType); + +#ifndef GLSLANG_WEB + inheritMemoryQualifiers(base->getQualifier(), result->getWritableType().getQualifier()); + + // Propagate nonuniform + if (base->getQualifier().isNonUniform() || index->getQualifier().isNonUniform()) + result->getWritableType().getQualifier().nonUniform = true; + + if (anyIndexLimits) + handleIndexLimits(loc, base, index); +#endif + + return result; +} + +#ifndef GLSLANG_WEB + +// for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms +void TParseContext::handleIndexLimits(const TSourceLoc& /*loc*/, TIntermTyped* base, TIntermTyped* index) +{ + if ((! limits.generalSamplerIndexing && base->getBasicType() == EbtSampler) || + (! limits.generalUniformIndexing && base->getQualifier().isUniformOrBuffer() && language != EShLangVertex) || + (! limits.generalAttributeMatrixVectorIndexing && base->getQualifier().isPipeInput() && language == EShLangVertex && (base->getType().isMatrix() || base->getType().isVector())) || + (! limits.generalConstantMatrixVectorIndexing && base->getAsConstantUnion()) || + (! limits.generalVariableIndexing && ! base->getType().getQualifier().isUniformOrBuffer() && + ! base->getType().getQualifier().isPipeInput() && + ! base->getType().getQualifier().isPipeOutput() && + ! base->getType().getQualifier().isConstant()) || + (! limits.generalVaryingIndexing && (base->getType().getQualifier().isPipeInput() || + base->getType().getQualifier().isPipeOutput()))) { + // it's too early to know what the inductive variables are, save it for post processing + needsIndexLimitationChecking.push_back(index); + } +} + +// Make a shared symbol have a non-shared version that can be edited by the current +// compile, such that editing its type will not change the shared version and will +// effect all nodes sharing it. +void TParseContext::makeEditable(TSymbol*& symbol) +{ + TParseContextBase::makeEditable(symbol); + + // See if it's tied to IO resizing + if (isIoResizeArray(symbol->getType())) + ioArraySymbolResizeList.push_back(symbol); +} + +// Return true if this is a geometry shader input array or tessellation control output array +// or mesh shader output array. +bool TParseContext::isIoResizeArray(const TType& type) const +{ + return type.isArray() && + ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) || + (language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut && + ! type.getQualifier().patch) || + (language == EShLangFragment && type.getQualifier().storage == EvqVaryingIn && + type.getQualifier().pervertexNV) || + (language == EShLangMeshNV && type.getQualifier().storage == EvqVaryingOut && + !type.getQualifier().perTaskNV)); +} + +// If an array is not isIoResizeArray() but is an io array, make sure it has the right size +void TParseContext::fixIoArraySize(const TSourceLoc& loc, TType& type) +{ + if (! type.isArray() || type.getQualifier().patch || symbolTable.atBuiltInLevel()) + return; + + assert(! isIoResizeArray(type)); + + if (type.getQualifier().storage != EvqVaryingIn || type.getQualifier().patch) + return; + + if (language == EShLangTessControl || language == EShLangTessEvaluation) { + if (type.getOuterArraySize() != resources.maxPatchVertices) { + if (type.isSizedArray()) + error(loc, "tessellation input array size must be gl_MaxPatchVertices or implicitly sized", "[]", ""); + type.changeOuterArraySize(resources.maxPatchVertices); + } + } +} + +// Issue any errors if the non-array object is missing arrayness WRT +// shader I/O that has array requirements. +// All arrayness checking is handled in array paths, this is for +void TParseContext::ioArrayCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (! type.isArray() && ! symbolTable.atBuiltInLevel()) { + if (type.getQualifier().isArrayedIo(language) && !type.getQualifier().layoutPassthrough) + error(loc, "type must be an array:", type.getStorageQualifierString(), identifier.c_str()); + } +} + +// Handle a dereference of a geometry shader input array or tessellation control output array. +// See ioArraySymbolResizeList comment in ParseHelper.h. +// +void TParseContext::handleIoResizeArrayAccess(const TSourceLoc& /*loc*/, TIntermTyped* base) +{ + TIntermSymbol* symbolNode = base->getAsSymbolNode(); + assert(symbolNode); + if (! symbolNode) + return; + + // fix array size, if it can be fixed and needs to be fixed (will allow variable indexing) + if (symbolNode->getType().isUnsizedArray()) { + int newSize = getIoArrayImplicitSize(symbolNode->getType().getQualifier()); + if (newSize > 0) + symbolNode->getWritableType().changeOuterArraySize(newSize); + } +} + +// If there has been an input primitive declaration (geometry shader) or an output +// number of vertices declaration(tessellation shader), make sure all input array types +// match it in size. Types come either from nodes in the AST or symbols in the +// symbol table. +// +// Types without an array size will be given one. +// Types already having a size that is wrong will get an error. +// +void TParseContext::checkIoArraysConsistency(const TSourceLoc &loc, bool tailOnly) +{ + int requiredSize = 0; + TString featureString; + size_t listSize = ioArraySymbolResizeList.size(); + size_t i = 0; + + // If tailOnly = true, only check the last array symbol in the list. + if (tailOnly) { + i = listSize - 1; + } + for (bool firstIteration = true; i < listSize; ++i) { + TType &type = ioArraySymbolResizeList[i]->getWritableType(); + + // As I/O array sizes don't change, fetch requiredSize only once, + // except for mesh shaders which could have different I/O array sizes based on type qualifiers. + if (firstIteration || (language == EShLangMeshNV)) { + requiredSize = getIoArrayImplicitSize(type.getQualifier(), &featureString); + if (requiredSize == 0) + break; + firstIteration = false; + } + + checkIoArrayConsistency(loc, requiredSize, featureString.c_str(), type, + ioArraySymbolResizeList[i]->getName()); + } +} + +int TParseContext::getIoArrayImplicitSize(const TQualifier &qualifier, TString *featureString) const +{ + int expectedSize = 0; + TString str = "unknown"; + unsigned int maxVertices = intermediate.getVertices() != TQualifier::layoutNotSet ? intermediate.getVertices() : 0; + + if (language == EShLangGeometry) { + expectedSize = TQualifier::mapGeometryToSize(intermediate.getInputPrimitive()); + str = TQualifier::getGeometryString(intermediate.getInputPrimitive()); + } + else if (language == EShLangTessControl) { + expectedSize = maxVertices; + str = "vertices"; + } else if (language == EShLangFragment) { + // Number of vertices for Fragment shader is always three. + expectedSize = 3; + str = "vertices"; + } else if (language == EShLangMeshNV) { + unsigned int maxPrimitives = + intermediate.getPrimitives() != TQualifier::layoutNotSet ? intermediate.getPrimitives() : 0; + if (qualifier.builtIn == EbvPrimitiveIndicesNV) { + expectedSize = maxPrimitives * TQualifier::mapGeometryToSize(intermediate.getOutputPrimitive()); + str = "max_primitives*"; + str += TQualifier::getGeometryString(intermediate.getOutputPrimitive()); + } + else if (qualifier.isPerPrimitive()) { + expectedSize = maxPrimitives; + str = "max_primitives"; + } + else { + expectedSize = maxVertices; + str = "max_vertices"; + } + } + if (featureString) + *featureString = str; + return expectedSize; +} + +void TParseContext::checkIoArrayConsistency(const TSourceLoc& loc, int requiredSize, const char* feature, TType& type, const TString& name) +{ + if (type.isUnsizedArray()) + type.changeOuterArraySize(requiredSize); + else if (type.getOuterArraySize() != requiredSize) { + if (language == EShLangGeometry) + error(loc, "inconsistent input primitive for array size of", feature, name.c_str()); + else if (language == EShLangTessControl) + error(loc, "inconsistent output number of vertices for array size of", feature, name.c_str()); + else if (language == EShLangFragment) { + if (type.getOuterArraySize() > requiredSize) + error(loc, " cannot be greater than 3 for pervertexNV", feature, name.c_str()); + } + else if (language == EShLangMeshNV) + error(loc, "inconsistent output array size of", feature, name.c_str()); + else + assert(0); + } +} + +#endif // GLSLANG_WEB + +// Handle seeing a binary node with a math operation. +// Returns nullptr if not semantically allowed. +TIntermTyped* TParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right) +{ + rValueErrorCheck(loc, str, left->getAsTyped()); + rValueErrorCheck(loc, str, right->getAsTyped()); + + bool allowed = true; + switch (op) { + // TODO: Bring more source language-specific checks up from intermediate.cpp + // to the specific parse helpers for that source language. + case EOpLessThan: + case EOpGreaterThan: + case EOpLessThanEqual: + case EOpGreaterThanEqual: + if (! left->isScalar() || ! right->isScalar()) + allowed = false; + break; + default: + break; + } + + if (((left->getType().contains16BitFloat() || right->getType().contains16BitFloat()) && !float16Arithmetic()) || + ((left->getType().contains16BitInt() || right->getType().contains16BitInt()) && !int16Arithmetic()) || + ((left->getType().contains8BitInt() || right->getType().contains8BitInt()) && !int8Arithmetic())) { + allowed = false; + } + + TIntermTyped* result = nullptr; + if (allowed) { + if ((left->isReference() || right->isReference())) + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference2, "buffer reference math"); + result = intermediate.addBinaryMath(op, left, right, loc); + } + + if (result == nullptr) + binaryOpError(loc, str, left->getCompleteString(), right->getCompleteString()); + + return result; +} + +// Handle seeing a unary node with a math operation. +TIntermTyped* TParseContext::handleUnaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* childNode) +{ + rValueErrorCheck(loc, str, childNode); + + bool allowed = true; + if ((childNode->getType().contains16BitFloat() && !float16Arithmetic()) || + (childNode->getType().contains16BitInt() && !int16Arithmetic()) || + (childNode->getType().contains8BitInt() && !int8Arithmetic())) { + allowed = false; + } + + TIntermTyped* result = nullptr; + if (allowed) + result = intermediate.addUnaryMath(op, childNode, loc); + + if (result) + return result; + else + unaryOpError(loc, str, childNode->getCompleteString()); + + return childNode; +} + +// +// Handle seeing a base.field dereference in the grammar. +// +TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TIntermTyped* base, const TString& field) +{ + variableCheck(base); + + // + // .length() can't be resolved until we later see the function-calling syntax. + // Save away the name in the AST for now. Processing is completed in + // handleLengthMethod(). + // + if (field == "length") { + if (base->isArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, ".length"); + profileRequires(loc, EEsProfile, 300, nullptr, ".length"); + } else if (base->isVector() || base->isMatrix()) { + const char* feature = ".length() on vectors and matrices"; + requireProfile(loc, ~EEsProfile, feature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, feature); + } else if (!base->getType().isCoopMat()) { + error(loc, "does not operate on this type:", field.c_str(), base->getType().getCompleteString().c_str()); + + return base; + } + + return intermediate.addMethod(base, TType(EbtInt), &field, loc); + } + + // It's not .length() if we get to here. + + if (base->isArray()) { + error(loc, "cannot apply to an array:", ".", field.c_str()); + + return base; + } + + if (base->getType().isCoopMat()) { + error(loc, "cannot apply to a cooperative matrix type:", ".", field.c_str()); + return base; + } + + // It's neither an array nor .length() if we get here, + // leaving swizzles and struct/block dereferences. + + TIntermTyped* result = base; + if ((base->isVector() || base->isScalar()) && + (base->isFloatingDomain() || base->isIntegerDomain() || base->getBasicType() == EbtBool)) { + result = handleDotSwizzle(loc, base, field); + } else if (base->isStruct() || base->isReference()) { + const TTypeList* fields = base->isReference() ? + base->getType().getReferentType()->getStruct() : + base->getType().getStruct(); + bool fieldFound = false; + int member; + for (member = 0; member < (int)fields->size(); ++member) { + if ((*fields)[member].type->getFieldName() == field) { + fieldFound = true; + break; + } + } + if (fieldFound) { + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldDereference(base, member, loc); + else { + blockMemberExtensionCheck(loc, base, member, field); + TIntermTyped* index = intermediate.addConstantUnion(member, loc); + result = intermediate.addIndex(EOpIndexDirectStruct, base, index, loc); + result->setType(*(*fields)[member].type); + if ((*fields)[member].type->getQualifier().isIo()) + intermediate.addIoAccessed(field); + } + inheritMemoryQualifiers(base->getQualifier(), result->getWritableType().getQualifier()); + } else + error(loc, "no such field in structure", field.c_str(), ""); + } else + error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str()); + + // Propagate noContraction up the dereference chain + if (base->getQualifier().isNoContraction()) + result->getWritableType().getQualifier().setNoContraction(); + + // Propagate nonuniform + if (base->getQualifier().isNonUniform()) + result->getWritableType().getQualifier().nonUniform = true; + + return result; +} + +// +// Handle seeing a base.swizzle, a subset of base.identifier in the grammar. +// +TIntermTyped* TParseContext::handleDotSwizzle(const TSourceLoc& loc, TIntermTyped* base, const TString& field) +{ + TIntermTyped* result = base; + if (base->isScalar()) { + const char* dotFeature = "scalar swizzle"; + requireProfile(loc, ~EEsProfile, dotFeature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, dotFeature); + } + + TSwizzleSelectors selectors; + parseSwizzleSelector(loc, field, base->getVectorSize(), selectors); + + if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, ".", "can't swizzle types containing float16"); + if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitInt()) + requireInt16Arithmetic(loc, ".", "can't swizzle types containing (u)int16"); + if (base->isVector() && selectors.size() != 1 && base->getType().contains8BitInt()) + requireInt8Arithmetic(loc, ".", "can't swizzle types containing (u)int8"); + + if (base->isScalar()) { + if (selectors.size() == 1) + return result; + else { + TType type(base->getBasicType(), EvqTemporary, selectors.size()); + // Swizzle operations propagate specialization-constantness + if (base->getQualifier().isSpecConstant()) + type.getQualifier().makeSpecConstant(); + return addConstructor(loc, base, type); + } + } + + if (base->getType().getQualifier().isFrontEndConstant()) + result = intermediate.foldSwizzle(base, selectors, loc); + else { + if (selectors.size() == 1) { + TIntermTyped* index = intermediate.addConstantUnion(selectors[0], loc); + result = intermediate.addIndex(EOpIndexDirect, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision)); + } else { + TIntermTyped* index = intermediate.addSwizzle(selectors, loc); + result = intermediate.addIndex(EOpVectorSwizzle, base, index, loc); + result->setType(TType(base->getBasicType(), EvqTemporary, base->getType().getQualifier().precision, selectors.size())); + } + // Swizzle operations propagate specialization-constantness + if (base->getType().getQualifier().isSpecConstant()) + result->getWritableType().getQualifier().makeSpecConstant(); + } + + return result; +} + +void TParseContext::blockMemberExtensionCheck(const TSourceLoc& loc, const TIntermTyped* base, int member, const TString& memberName) +{ + // a block that needs extension checking is either 'base', or if arrayed, + // one level removed to the left + const TIntermSymbol* baseSymbol = nullptr; + if (base->getAsBinaryNode() == nullptr) + baseSymbol = base->getAsSymbolNode(); + else + baseSymbol = base->getAsBinaryNode()->getLeft()->getAsSymbolNode(); + if (baseSymbol == nullptr) + return; + const TSymbol* symbol = symbolTable.find(baseSymbol->getName()); + if (symbol == nullptr) + return; + const TVariable* variable = symbol->getAsVariable(); + if (variable == nullptr) + return; + if (!variable->hasMemberExtensions()) + return; + + // We now have a variable that is the base of a dot reference + // with members that need extension checking. + if (variable->getNumMemberExtensions(member) > 0) + requireExtensions(loc, variable->getNumMemberExtensions(member), variable->getMemberExtensions(member), memberName.c_str()); +} + +// +// Handle seeing a function declarator in the grammar. This is the precursor +// to recognizing a function prototype or function definition. +// +TFunction* TParseContext::handleFunctionDeclarator(const TSourceLoc& loc, TFunction& function, bool prototype) +{ + // ES can't declare prototypes inside functions + if (! symbolTable.atGlobalLevel()) + requireProfile(loc, ~EEsProfile, "local function declaration"); + + // + // Multiple declarations of the same function name are allowed. + // + // If this is a definition, the definition production code will check for redefinitions + // (we don't know at this point if it's a definition or not). + // + // Redeclarations (full signature match) are allowed. But, return types and parameter qualifiers must also match. + // - except ES 100, which only allows a single prototype + // + // ES 100 does not allow redefining, but does allow overloading of built-in functions. + // ES 300 does not allow redefining or overloading of built-in functions. + // + bool builtIn; + TSymbol* symbol = symbolTable.find(function.getMangledName(), &builtIn); + if (symbol && symbol->getAsFunction() && builtIn) + requireProfile(loc, ~EEsProfile, "redefinition of built-in function"); + const TFunction* prevDec = symbol ? symbol->getAsFunction() : 0; + if (prevDec) { + if (prevDec->isPrototyped() && prototype) + profileRequires(loc, EEsProfile, 300, nullptr, "multiple prototypes for same function"); + if (prevDec->getType() != function.getType()) + error(loc, "overloaded functions must have the same return type", function.getName().c_str(), ""); + for (int i = 0; i < prevDec->getParamCount(); ++i) { + if ((*prevDec)[i].type->getQualifier().storage != function[i].type->getQualifier().storage) + error(loc, "overloaded functions must have the same parameter storage qualifiers for argument", function[i].type->getStorageQualifierString(), "%d", i+1); + + if ((*prevDec)[i].type->getQualifier().precision != function[i].type->getQualifier().precision) + error(loc, "overloaded functions must have the same parameter precision qualifiers for argument", function[i].type->getPrecisionQualifierString(), "%d", i+1); + } + } + + arrayObjectCheck(loc, function.getType(), "array in function return type"); + + if (prototype) { + // All built-in functions are defined, even though they don't have a body. + // Count their prototype as a definition instead. + if (symbolTable.atBuiltInLevel()) + function.setDefined(); + else { + if (prevDec && ! builtIn) + symbol->getAsFunction()->setPrototyped(); // need a writable one, but like having prevDec as a const + function.setPrototyped(); + } + } + + // This insert won't actually insert it if it's a duplicate signature, but it will still check for + // other forms of name collisions. + if (! symbolTable.insert(function)) + error(loc, "function name is redeclaration of existing name", function.getName().c_str(), ""); + + // + // If this is a redeclaration, it could also be a definition, + // in which case, we need to use the parameter names from this one, and not the one that's + // being redeclared. So, pass back this declaration, not the one in the symbol table. + // + return &function; +} + +// +// Handle seeing the function prototype in front of a function definition in the grammar. +// The body is handled after this function returns. +// +TIntermAggregate* TParseContext::handleFunctionDefinition(const TSourceLoc& loc, TFunction& function) +{ + currentCaller = function.getMangledName(); + TSymbol* symbol = symbolTable.find(function.getMangledName()); + TFunction* prevDec = symbol ? symbol->getAsFunction() : nullptr; + + if (! prevDec) + error(loc, "can't find function", function.getName().c_str(), ""); + // Note: 'prevDec' could be 'function' if this is the first time we've seen function + // as it would have just been put in the symbol table. Otherwise, we're looking up + // an earlier occurrence. + + if (prevDec && prevDec->isDefined()) { + // Then this function already has a body. + error(loc, "function already has a body", function.getName().c_str(), ""); + } + if (prevDec && ! prevDec->isDefined()) { + prevDec->setDefined(); + + // Remember the return type for later checking for RETURN statements. + currentFunctionType = &(prevDec->getType()); + } else + currentFunctionType = new TType(EbtVoid); + functionReturnsValue = false; + + // Check for entry point + if (function.getName().compare(intermediate.getEntryPointName().c_str()) == 0) { + intermediate.setEntryPointMangledName(function.getMangledName().c_str()); + intermediate.incrementEntryPointCount(); + inMain = true; + } else + inMain = false; + + // + // Raise error message if main function takes any parameters or returns anything other than void + // + if (inMain) { + if (function.getParamCount() > 0) + error(loc, "function cannot take any parameter(s)", function.getName().c_str(), ""); + if (function.getType().getBasicType() != EbtVoid) + error(loc, "", function.getType().getBasicTypeString().c_str(), "entry point cannot return a value"); + } + + // + // New symbol table scope for body of function plus its arguments + // + symbolTable.push(); + + // + // Insert parameters into the symbol table. + // If the parameter has no name, it's not an error, just don't insert it + // (could be used for unused args). + // + // Also, accumulate the list of parameters into the HIL, so lower level code + // knows where to find parameters. + // + TIntermAggregate* paramNodes = new TIntermAggregate; + for (int i = 0; i < function.getParamCount(); i++) { + TParameter& param = function[i]; + if (param.name != nullptr) { + TVariable *variable = new TVariable(param.name, *param.type); + + // Insert the parameters with name in the symbol table. + if (! symbolTable.insert(*variable)) + error(loc, "redefinition", variable->getName().c_str(), ""); + else { + // Transfer ownership of name pointer to symbol table. + param.name = nullptr; + + // Add the parameter to the HIL + paramNodes = intermediate.growAggregate(paramNodes, + intermediate.addSymbol(*variable, loc), + loc); + } + } else + paramNodes = intermediate.growAggregate(paramNodes, intermediate.addSymbol(*param.type, loc), loc); + } + intermediate.setAggregateOperator(paramNodes, EOpParameters, TType(EbtVoid), loc); + loopNestingLevel = 0; + statementNestingLevel = 0; + controlFlowNestingLevel = 0; + postEntryPointReturn = false; + + return paramNodes; +} + +// +// Handle seeing function call syntax in the grammar, which could be any of +// - .length() method +// - constructor +// - a call to a built-in function mapped to an operator +// - a call to a built-in function that will remain a function call (e.g., texturing) +// - user function +// - subroutine call (not implemented yet) +// +TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments) +{ + TIntermTyped* result = nullptr; + + if (function->getBuiltInOp() == EOpArrayLength) + result = handleLengthMethod(loc, function, arguments); + else if (function->getBuiltInOp() != EOpNull) { + // + // Then this should be a constructor. + // Don't go through the symbol table for constructors. + // Their parameters will be verified algorithmically. + // + TType type(EbtVoid); // use this to get the type back + if (! constructorError(loc, arguments, *function, function->getBuiltInOp(), type)) { + // + // It's a constructor, of type 'type'. + // + result = addConstructor(loc, arguments, type); + if (result == nullptr) + error(loc, "cannot construct with these arguments", type.getCompleteString().c_str(), ""); + } + } else { + // + // Find it in the symbol table. + // + const TFunction* fnCandidate; + bool builtIn; + fnCandidate = findFunction(loc, *function, builtIn); + if (fnCandidate) { + // This is a declared function that might map to + // - a built-in operator, + // - a built-in function not mapped to an operator, or + // - a user function. + + // Error check for a function requiring specific extensions present. + if (builtIn && fnCandidate->getNumExtensions()) + requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str()); + + if (builtIn && fnCandidate->getType().contains16BitFloat()) + requireFloat16Arithmetic(loc, "built-in function", "float16 types can only be in uniform block or buffer storage"); + if (builtIn && fnCandidate->getType().contains16BitInt()) + requireInt16Arithmetic(loc, "built-in function", "(u)int16 types can only be in uniform block or buffer storage"); + if (builtIn && fnCandidate->getType().contains8BitInt()) + requireInt8Arithmetic(loc, "built-in function", "(u)int8 types can only be in uniform block or buffer storage"); + + if (arguments != nullptr) { + // Make sure qualifications work for these arguments. + TIntermAggregate* aggregate = arguments->getAsAggregate(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermNode* arg = fnCandidate->getParamCount() == 1 ? arguments : (aggregate ? aggregate->getSequence()[i] : arguments); + TQualifier& formalQualifier = (*fnCandidate)[i].type->getQualifier(); + if (formalQualifier.isParamOutput()) { + if (lValueErrorCheck(arguments->getLoc(), "assign", arg->getAsTyped())) + error(arguments->getLoc(), "Non-L-value cannot be passed for 'out' or 'inout' parameters.", "out", ""); + } + const TType& argType = arg->getAsTyped()->getType(); + const TQualifier& argQualifier = argType.getQualifier(); + if (argQualifier.isMemory() && (argType.containsOpaque() || argType.isReference())) { + const char* message = "argument cannot drop memory qualifier when passed to formal parameter"; +#ifndef GLSLANG_WEB + if (argQualifier.volatil && ! formalQualifier.volatil) + error(arguments->getLoc(), message, "volatile", ""); + if (argQualifier.coherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "coherent", ""); + if (argQualifier.devicecoherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "devicecoherent", ""); + if (argQualifier.queuefamilycoherent && ! (formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "queuefamilycoherent", ""); + if (argQualifier.workgroupcoherent && ! (formalQualifier.workgroupcoherent || formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "workgroupcoherent", ""); + if (argQualifier.subgroupcoherent && ! (formalQualifier.subgroupcoherent || formalQualifier.workgroupcoherent || formalQualifier.queuefamilycoherent || formalQualifier.devicecoherent || formalQualifier.coherent)) + error(arguments->getLoc(), message, "subgroupcoherent", ""); + if (argQualifier.readonly && ! formalQualifier.readonly) + error(arguments->getLoc(), message, "readonly", ""); + if (argQualifier.writeonly && ! formalQualifier.writeonly) + error(arguments->getLoc(), message, "writeonly", ""); + // Don't check 'restrict', it is different than the rest: + // "...but only restrict can be taken away from a calling argument, by a formal parameter that + // lacks the restrict qualifier..." +#endif + } + if (!builtIn && argQualifier.getFormat() != formalQualifier.getFormat()) { + // we have mismatched formats, which should only be allowed if writeonly + // and at least one format is unknown + if (!formalQualifier.isWriteOnly() || (formalQualifier.getFormat() != ElfNone && + argQualifier.getFormat() != ElfNone)) + error(arguments->getLoc(), "image formats must match", "format", ""); + } + if (builtIn && arg->getAsTyped()->getType().contains16BitFloat()) + requireFloat16Arithmetic(arguments->getLoc(), "built-in function", "float16 types can only be in uniform block or buffer storage"); + if (builtIn && arg->getAsTyped()->getType().contains16BitInt()) + requireInt16Arithmetic(arguments->getLoc(), "built-in function", "(u)int16 types can only be in uniform block or buffer storage"); + if (builtIn && arg->getAsTyped()->getType().contains8BitInt()) + requireInt8Arithmetic(arguments->getLoc(), "built-in function", "(u)int8 types can only be in uniform block or buffer storage"); + + // TODO 4.5 functionality: A shader will fail to compile + // if the value passed to the memargument of an atomic memory function does not correspond to a buffer or + // shared variable. It is acceptable to pass an element of an array or a single component of a vector to the + // memargument of an atomic memory function, as long as the underlying array or vector is a buffer or + // shared variable. + } + + // Convert 'in' arguments + addInputArgumentConversions(*fnCandidate, arguments); // arguments may be modified if it's just a single argument node + } + + if (builtIn && fnCandidate->getBuiltInOp() != EOpNull) { + // A function call mapped to a built-in operation. + result = handleBuiltInFunctionCall(loc, arguments, *fnCandidate); + } else { + // This is a function call not mapped to built-in operator. + // It could still be a built-in function, but only if PureOperatorBuiltins == false. + result = intermediate.setAggregateOperator(arguments, EOpFunctionCall, fnCandidate->getType(), loc); + TIntermAggregate* call = result->getAsAggregate(); + call->setName(fnCandidate->getMangledName()); + + // this is how we know whether the given function is a built-in function or a user-defined function + // if builtIn == false, it's a userDefined -> could be an overloaded built-in function also + // if builtIn == true, it's definitely a built-in function with EOpNull + if (! builtIn) { + call->setUserDefined(); + if (symbolTable.atGlobalLevel()) { + requireProfile(loc, ~EEsProfile, "calling user function from global scope"); + intermediate.addToCallGraph(infoSink, "main(", fnCandidate->getMangledName()); + } else + intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName()); + } + +#ifndef GLSLANG_WEB + if (builtIn) + nonOpBuiltInCheck(loc, *fnCandidate, *call); + else +#endif + userFunctionCallCheck(loc, *call); + } + + // Convert 'out' arguments. If it was a constant folded built-in, it won't be an aggregate anymore. + // Built-ins with a single argument aren't called with an aggregate, but they also don't have an output. + // Also, build the qualifier list for user function calls, which are always called with an aggregate. + if (result->getAsAggregate()) { + TQualifierList& qualifierList = result->getAsAggregate()->getQualifierList(); + for (int i = 0; i < fnCandidate->getParamCount(); ++i) { + TStorageQualifier qual = (*fnCandidate)[i].type->getQualifier().storage; + qualifierList.push_back(qual); + } + result = addOutputArgumentConversions(*fnCandidate, *result->getAsAggregate()); + } + + if (result->getAsTyped()->getType().isCoopMat() && + !result->getAsTyped()->getType().isParameterized()) { + assert(fnCandidate->getBuiltInOp() == EOpCooperativeMatrixMulAdd); + + result->setType(result->getAsAggregate()->getSequence()[2]->getAsTyped()->getType()); + } + } + } + + // generic error recovery + // TODO: simplification: localize all the error recoveries that look like this, and taking type into account to reduce cascades + if (result == nullptr) + result = intermediate.addConstantUnion(0.0, EbtFloat, loc); + + return result; +} + +TIntermTyped* TParseContext::handleBuiltInFunctionCall(TSourceLoc loc, TIntermNode* arguments, + const TFunction& function) +{ + checkLocation(loc, function.getBuiltInOp()); + TIntermTyped *result = intermediate.addBuiltInFunctionCall(loc, function.getBuiltInOp(), + function.getParamCount() == 1, + arguments, function.getType()); + if (result != nullptr && obeyPrecisionQualifiers()) + computeBuiltinPrecisions(*result, function); + + if (result == nullptr) { + if (arguments == nullptr) + error(loc, " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", ""); + else + error(arguments->getLoc(), " wrong operand type", "Internal Error", + "built in unary operator function. Type: %s", + static_cast(arguments)->getCompleteString().c_str()); + } else if (result->getAsOperator()) + builtInOpCheck(loc, function, *result->getAsOperator()); + + return result; +} + +// "The operation of a built-in function can have a different precision +// qualification than the precision qualification of the resulting value. +// These two precision qualifications are established as follows. +// +// The precision qualification of the operation of a built-in function is +// based on the precision qualification of its input arguments and formal +// parameters: When a formal parameter specifies a precision qualifier, +// that is used, otherwise, the precision qualification of the calling +// argument is used. The highest precision of these will be the precision +// qualification of the operation of the built-in function. Generally, +// this is applied across all arguments to a built-in function, with the +// exceptions being: +// - bitfieldExtract and bitfieldInsert ignore the 'offset' and 'bits' +// arguments. +// - interpolateAt* functions only look at the 'interpolant' argument. +// +// The precision qualification of the result of a built-in function is +// determined in one of the following ways: +// +// - For the texture sampling, image load, and image store functions, +// the precision of the return type matches the precision of the +// sampler type +// +// Otherwise: +// +// - For prototypes that do not specify a resulting precision qualifier, +// the precision will be the same as the precision of the operation. +// +// - For prototypes that do specify a resulting precision qualifier, +// the specified precision qualifier is the precision qualification of +// the result." +// +void TParseContext::computeBuiltinPrecisions(TIntermTyped& node, const TFunction& function) +{ + TPrecisionQualifier operationPrecision = EpqNone; + TPrecisionQualifier resultPrecision = EpqNone; + + TIntermOperator* opNode = node.getAsOperator(); + if (opNode == nullptr) + return; + + if (TIntermUnary* unaryNode = node.getAsUnaryNode()) { + operationPrecision = std::max(function[0].type->getQualifier().precision, + unaryNode->getOperand()->getType().getQualifier().precision); + if (function.getType().getBasicType() != EbtBool) + resultPrecision = function.getType().getQualifier().precision == EpqNone ? + operationPrecision : + function.getType().getQualifier().precision; + } else if (TIntermAggregate* agg = node.getAsAggregate()) { + TIntermSequence& sequence = agg->getSequence(); + unsigned int numArgs = (unsigned int)sequence.size(); + switch (agg->getOp()) { + case EOpBitfieldExtract: + numArgs = 1; + break; + case EOpBitfieldInsert: + numArgs = 2; + break; + case EOpInterpolateAtCentroid: + case EOpInterpolateAtOffset: + case EOpInterpolateAtSample: + numArgs = 1; + break; + case EOpDebugPrintf: + numArgs = 0; + break; + default: + break; + } + // find the maximum precision from the arguments and parameters + for (unsigned int arg = 0; arg < numArgs; ++arg) { + operationPrecision = std::max(operationPrecision, sequence[arg]->getAsTyped()->getQualifier().precision); + operationPrecision = std::max(operationPrecision, function[arg].type->getQualifier().precision); + } + // compute the result precision + if (agg->isSampling() || + agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore || + agg->getOp() == EOpImageLoadLod || agg->getOp() == EOpImageStoreLod) + resultPrecision = sequence[0]->getAsTyped()->getQualifier().precision; + else if (function.getType().getBasicType() != EbtBool) + resultPrecision = function.getType().getQualifier().precision == EpqNone ? + operationPrecision : + function.getType().getQualifier().precision; + } + + // Propagate precision through this node and its children. That algorithm stops + // when a precision is found, so start by clearing this subroot precision + opNode->getQualifier().precision = EpqNone; + if (operationPrecision != EpqNone) { + opNode->propagatePrecision(operationPrecision); + opNode->setOperationPrecision(operationPrecision); + } + // Now, set the result precision, which might not match + opNode->getQualifier().precision = resultPrecision; +} + +TIntermNode* TParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) +{ +#ifndef GLSLANG_WEB + storage16BitAssignmentCheck(loc, value->getType(), "return"); +#endif + + functionReturnsValue = true; + TIntermBranch* branch = nullptr; + if (currentFunctionType->getBasicType() == EbtVoid) { + error(loc, "void function cannot return a value", "return", ""); + branch = intermediate.addBranch(EOpReturn, loc); + } else if (*currentFunctionType != value->getType()) { + TIntermTyped* converted = intermediate.addConversion(EOpReturn, *currentFunctionType, value); + if (converted) { + if (*currentFunctionType != converted->getType()) + error(loc, "cannot convert return value to function return type", "return", ""); + if (version < 420) + warn(loc, "type conversion on return values was not explicitly allowed until version 420", + "return", ""); + branch = intermediate.addBranch(EOpReturn, converted, loc); + } else { + error(loc, "type does not match, or is not convertible to, the function's return type", "return", ""); + branch = intermediate.addBranch(EOpReturn, value, loc); + } + } else + branch = intermediate.addBranch(EOpReturn, value, loc); + + branch->updatePrecision(currentFunctionType->getQualifier().precision); + return branch; +} + +// See if the operation is being done in an illegal location. +void TParseContext::checkLocation(const TSourceLoc& loc, TOperator op) +{ +#ifndef GLSLANG_WEB + switch (op) { + case EOpBarrier: + if (language == EShLangTessControl) { + if (controlFlowNestingLevel > 0) + error(loc, "tessellation control barrier() cannot be placed within flow control", "", ""); + if (! inMain) + error(loc, "tessellation control barrier() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "tessellation control barrier() cannot be placed after a return from main()", "", ""); + } + break; + case EOpBeginInvocationInterlock: + if (language != EShLangFragment) + error(loc, "beginInvocationInterlockARB() must be in a fragment shader", "", ""); + if (! inMain) + error(loc, "beginInvocationInterlockARB() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "beginInvocationInterlockARB() cannot be placed after a return from main()", "", ""); + if (controlFlowNestingLevel > 0) + error(loc, "beginInvocationInterlockARB() cannot be placed within flow control", "", ""); + + if (beginInvocationInterlockCount > 0) + error(loc, "beginInvocationInterlockARB() must only be called once", "", ""); + if (endInvocationInterlockCount > 0) + error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", ""); + + beginInvocationInterlockCount++; + + // default to pixel_interlock_ordered + if (intermediate.getInterlockOrdering() == EioNone) + intermediate.setInterlockOrdering(EioPixelInterlockOrdered); + break; + case EOpEndInvocationInterlock: + if (language != EShLangFragment) + error(loc, "endInvocationInterlockARB() must be in a fragment shader", "", ""); + if (! inMain) + error(loc, "endInvocationInterlockARB() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "endInvocationInterlockARB() cannot be placed after a return from main()", "", ""); + if (controlFlowNestingLevel > 0) + error(loc, "endInvocationInterlockARB() cannot be placed within flow control", "", ""); + + if (endInvocationInterlockCount > 0) + error(loc, "endInvocationInterlockARB() must only be called once", "", ""); + if (beginInvocationInterlockCount == 0) + error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", ""); + + endInvocationInterlockCount++; + break; + default: + break; + } +#endif +} + +// Finish processing object.length(). This started earlier in handleDotDereference(), where +// the ".length" part was recognized and semantically checked, and finished here where the +// function syntax "()" is recognized. +// +// Return resulting tree node. +TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction* function, TIntermNode* intermNode) +{ + int length = 0; + + if (function->getParamCount() > 0) + error(loc, "method does not accept any arguments", function->getName().c_str(), ""); + else { + const TType& type = intermNode->getAsTyped()->getType(); + if (type.isArray()) { + if (type.isUnsizedArray()) { +#ifndef GLSLANG_WEB + if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) { + // We could be between a layout declaration that gives a built-in io array implicit size and + // a user redeclaration of that array, meaning we have to substitute its implicit size here + // without actually redeclaring the array. (It is an error to use a member before the + // redeclaration, but not an error to use the array name itself.) + const TString& name = intermNode->getAsSymbolNode()->getName(); + if (name == "gl_in" || name == "gl_out" || name == "gl_MeshVerticesNV" || + name == "gl_MeshPrimitivesNV") { + length = getIoArrayImplicitSize(type.getQualifier()); + } + } +#endif + if (length == 0) { +#ifndef GLSLANG_WEB + if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) + error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); + else if (isRuntimeLength(*intermNode->getAsTyped())) { + // Create a unary op and let the back end handle it + return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); + } else +#endif + error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method"); + } + } else if (type.getOuterArrayNode()) { + // If the array's outer size is specified by an intermediate node, it means the array's length + // was specified by a specialization constant. In such a case, we should return the node of the + // specialization constants to represent the length. + return type.getOuterArrayNode(); + } else + length = type.getOuterArraySize(); + } else if (type.isMatrix()) + length = type.getMatrixCols(); + else if (type.isVector()) + length = type.getVectorSize(); + else if (type.isCoopMat()) + return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); + else { + // we should not get here, because earlier semantic checking should have prevented this path + error(loc, ".length()", "unexpected use of .length()", ""); + } + } + + if (length == 0) + length = 1; + + return intermediate.addConstantUnion(length, loc); +} + +// +// Add any needed implicit conversions for function-call arguments to input parameters. +// +void TParseContext::addInputArgumentConversions(const TFunction& function, TIntermNode*& arguments) const +{ +#ifndef GLSLANG_WEB + TIntermAggregate* aggregate = arguments->getAsAggregate(); + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + // At this early point there is a slight ambiguity between whether an aggregate 'arguments' + // is the single argument itself or its children are the arguments. Only one argument + // means take 'arguments' itself as the one argument. + TIntermTyped* arg = function.getParamCount() == 1 ? arguments->getAsTyped() : (aggregate ? aggregate->getSequence()[i]->getAsTyped() : arguments->getAsTyped()); + if (*function[i].type != arg->getType()) { + if (function[i].type->getQualifier().isParamInput() && + !function[i].type->isCoopMat()) { + // In-qualified arguments just need an extra node added above the argument to + // convert to the correct type. + arg = intermediate.addConversion(EOpFunctionCall, *function[i].type, arg); + if (arg) { + if (function.getParamCount() == 1) + arguments = arg; + else { + if (aggregate) + aggregate->getSequence()[i] = arg; + else + arguments = arg; + } + } + } + } + } +#endif +} + +// +// Add any needed implicit output conversions for function-call arguments. This +// can require a new tree topology, complicated further by whether the function +// has a return value. +// +// Returns a node of a subtree that evaluates to the return value of the function. +// +TIntermTyped* TParseContext::addOutputArgumentConversions(const TFunction& function, TIntermAggregate& intermNode) const +{ +#ifdef GLSLANG_WEB + return &intermNode; +#else + TIntermSequence& arguments = intermNode.getSequence(); + + // Will there be any output conversions? + bool outputConversions = false; + for (int i = 0; i < function.getParamCount(); ++i) { + if (*function[i].type != arguments[i]->getAsTyped()->getType() && function[i].type->getQualifier().isParamOutput()) { + outputConversions = true; + break; + } + } + + if (! outputConversions) + return &intermNode; + + // Setup for the new tree, if needed: + // + // Output conversions need a different tree topology. + // Out-qualified arguments need a temporary of the correct type, with the call + // followed by an assignment of the temporary to the original argument: + // void: function(arg, ...) -> ( function(tempArg, ...), arg = tempArg, ...) + // ret = function(arg, ...) -> ret = (tempRet = function(tempArg, ...), arg = tempArg, ..., tempRet) + // Where the "tempArg" type needs no conversion as an argument, but will convert on assignment. + TIntermTyped* conversionTree = nullptr; + TVariable* tempRet = nullptr; + if (intermNode.getBasicType() != EbtVoid) { + // do the "tempRet = function(...), " bit from above + tempRet = makeInternalVariable("tempReturn", intermNode.getType()); + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); + conversionTree = intermediate.addAssign(EOpAssign, tempRetNode, &intermNode, intermNode.getLoc()); + } else + conversionTree = &intermNode; + + conversionTree = intermediate.makeAggregate(conversionTree); + + // Process each argument's conversion + for (int i = 0; i < function.getParamCount(); ++i) { + if (*function[i].type != arguments[i]->getAsTyped()->getType()) { + if (function[i].type->getQualifier().isParamOutput()) { + // Out-qualified arguments need to use the topology set up above. + // do the " ...(tempArg, ...), arg = tempArg" bit from above + TType paramType; + paramType.shallowCopy(*function[i].type); + if (arguments[i]->getAsTyped()->getType().isParameterized() && + !paramType.isParameterized()) { + paramType.shallowCopy(arguments[i]->getAsTyped()->getType()); + paramType.copyTypeParameters(*arguments[i]->getAsTyped()->getType().getTypeParameters()); + } + TVariable* tempArg = makeInternalVariable("tempArg", paramType); + tempArg->getWritableType().getQualifier().makeTemporary(); + TIntermSymbol* tempArgNode = intermediate.addSymbol(*tempArg, intermNode.getLoc()); + TIntermTyped* tempAssign = intermediate.addAssign(EOpAssign, arguments[i]->getAsTyped(), tempArgNode, arguments[i]->getLoc()); + conversionTree = intermediate.growAggregate(conversionTree, tempAssign, arguments[i]->getLoc()); + // replace the argument with another node for the same tempArg variable + arguments[i] = intermediate.addSymbol(*tempArg, intermNode.getLoc()); + } + } + } + + // Finalize the tree topology (see bigger comment above). + if (tempRet) { + // do the "..., tempRet" bit from above + TIntermSymbol* tempRetNode = intermediate.addSymbol(*tempRet, intermNode.getLoc()); + conversionTree = intermediate.growAggregate(conversionTree, tempRetNode, intermNode.getLoc()); + } + conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), intermNode.getLoc()); + + return conversionTree; +#endif +} + +TIntermTyped* TParseContext::addAssign(const TSourceLoc& loc, TOperator op, TIntermTyped* left, TIntermTyped* right) +{ + if ((op == EOpAddAssign || op == EOpSubAssign) && left->isReference()) + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference2, "+= and -= on a buffer reference"); + + return intermediate.addAssign(op, left, right, loc); +} + +void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& fnCandidate, const TIntermOperator& callNode) +{ + const TIntermSequence* argp = &callNode.getAsAggregate()->getSequence(); + + //const int gl_SemanticsRelaxed = 0x0; + const int gl_SemanticsAcquire = 0x2; + const int gl_SemanticsRelease = 0x4; + const int gl_SemanticsAcquireRelease = 0x8; + const int gl_SemanticsMakeAvailable = 0x2000; + const int gl_SemanticsMakeVisible = 0x4000; + const int gl_SemanticsVolatile = 0x8000; + + //const int gl_StorageSemanticsNone = 0x0; + const int gl_StorageSemanticsBuffer = 0x40; + const int gl_StorageSemanticsShared = 0x100; + const int gl_StorageSemanticsImage = 0x800; + const int gl_StorageSemanticsOutput = 0x1000; + + + unsigned int semantics = 0, storageClassSemantics = 0; + unsigned int semantics2 = 0, storageClassSemantics2 = 0; + + const TIntermTyped* arg0 = (*argp)[0]->getAsTyped(); + const bool isMS = arg0->getBasicType() == EbtSampler && arg0->getType().getSampler().isMultiSample(); + + // Grab the semantics and storage class semantics from the operands, based on opcode + switch (callNode.getOp()) { + case EOpAtomicAdd: + case EOpAtomicMin: + case EOpAtomicMax: + case EOpAtomicAnd: + case EOpAtomicOr: + case EOpAtomicXor: + case EOpAtomicExchange: + case EOpAtomicStore: + storageClassSemantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpAtomicLoad: + storageClassSemantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpAtomicCompSwap: + storageClassSemantics = (*argp)[4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + storageClassSemantics2 = (*argp)[6]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics2 = (*argp)[7]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + + case EOpImageAtomicAdd: + case EOpImageAtomicMin: + case EOpImageAtomicMax: + case EOpImageAtomicAnd: + case EOpImageAtomicOr: + case EOpImageAtomicXor: + case EOpImageAtomicExchange: + case EOpImageAtomicStore: + storageClassSemantics = (*argp)[isMS ? 5 : 4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 6 : 5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpImageAtomicLoad: + storageClassSemantics = (*argp)[isMS ? 4 : 3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 5 : 4]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpImageAtomicCompSwap: + storageClassSemantics = (*argp)[isMS ? 6 : 5]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[isMS ? 7 : 6]->getAsConstantUnion()->getConstArray()[0].getIConst(); + storageClassSemantics2 = (*argp)[isMS ? 8 : 7]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics2 = (*argp)[isMS ? 9 : 8]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + + case EOpBarrier: + storageClassSemantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[3]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + case EOpMemoryBarrier: + storageClassSemantics = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getIConst(); + semantics = (*argp)[2]->getAsConstantUnion()->getConstArray()[0].getIConst(); + break; + default: + break; + } + + if ((semantics & gl_SemanticsAcquire) && + (callNode.getOp() == EOpAtomicStore || callNode.getOp() == EOpImageAtomicStore)) { + error(loc, "gl_SemanticsAcquire must not be used with (image) atomic store", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsRelease) && + (callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpImageAtomicLoad)) { + error(loc, "gl_SemanticsRelease must not be used with (image) atomic load", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsAcquireRelease) && + (callNode.getOp() == EOpAtomicStore || callNode.getOp() == EOpImageAtomicStore || + callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpImageAtomicLoad)) { + error(loc, "gl_SemanticsAcquireRelease must not be used with (image) atomic load/store", + fnCandidate.getName().c_str(), ""); + } + if (((semantics | semantics2) & ~(gl_SemanticsAcquire | + gl_SemanticsRelease | + gl_SemanticsAcquireRelease | + gl_SemanticsMakeAvailable | + gl_SemanticsMakeVisible | + gl_SemanticsVolatile))) { + error(loc, "Invalid semantics value", fnCandidate.getName().c_str(), ""); + } + if (((storageClassSemantics | storageClassSemantics2) & ~(gl_StorageSemanticsBuffer | + gl_StorageSemanticsShared | + gl_StorageSemanticsImage | + gl_StorageSemanticsOutput))) { + error(loc, "Invalid storage class semantics value", fnCandidate.getName().c_str(), ""); + } + + if (callNode.getOp() == EOpMemoryBarrier) { + if (!IsPow2(semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "Semantics must include exactly one of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } else { + if (semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease)) { + if (!IsPow2(semantics & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "Semantics must not include multiple of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } + if (semantics2 & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease)) { + if (!IsPow2(semantics2 & (gl_SemanticsAcquire | gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "semUnequal must not include multiple of gl_SemanticsRelease, gl_SemanticsAcquire, or " + "gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); + } + } + } + if (callNode.getOp() == EOpMemoryBarrier) { + if (storageClassSemantics == 0) { + error(loc, "Storage class semantics must not be zero", fnCandidate.getName().c_str(), ""); + } + } + if (callNode.getOp() == EOpBarrier && semantics != 0 && storageClassSemantics == 0) { + error(loc, "Storage class semantics must not be zero", fnCandidate.getName().c_str(), ""); + } + if ((callNode.getOp() == EOpAtomicCompSwap || callNode.getOp() == EOpImageAtomicCompSwap) && + (semantics2 & (gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "semUnequal must not be gl_SemanticsRelease or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsMakeAvailable) && + !(semantics & (gl_SemanticsRelease | gl_SemanticsAcquireRelease))) { + error(loc, "gl_SemanticsMakeAvailable requires gl_SemanticsRelease or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsMakeVisible) && + !(semantics & (gl_SemanticsAcquire | gl_SemanticsAcquireRelease))) { + error(loc, "gl_SemanticsMakeVisible requires gl_SemanticsAcquire or gl_SemanticsAcquireRelease", + fnCandidate.getName().c_str(), ""); + } + if ((semantics & gl_SemanticsVolatile) && + (callNode.getOp() == EOpMemoryBarrier || callNode.getOp() == EOpBarrier)) { + error(loc, "gl_SemanticsVolatile must not be used with memoryBarrier or controlBarrier", + fnCandidate.getName().c_str(), ""); + } + if ((callNode.getOp() == EOpAtomicCompSwap || callNode.getOp() == EOpImageAtomicCompSwap) && + ((semantics ^ semantics2) & gl_SemanticsVolatile)) { + error(loc, "semEqual and semUnequal must either both include gl_SemanticsVolatile or neither", + fnCandidate.getName().c_str(), ""); + } +} + +// +// Do additional checking of built-in function calls that is not caught +// by normal semantic checks on argument type, extension tagging, etc. +// +// Assumes there has been a semantically correct match to a built-in function prototype. +// +void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermOperator& callNode) +{ + // Set up convenience accessors to the argument(s). There is almost always + // multiple arguments for the cases below, but when there might be one, + // check the unaryArg first. + const TIntermSequence* argp = nullptr; // confusing to use [] syntax on a pointer, so this is to help get a reference + const TIntermTyped* unaryArg = nullptr; + const TIntermTyped* arg0 = nullptr; + if (callNode.getAsAggregate()) { + argp = &callNode.getAsAggregate()->getSequence(); + if (argp->size() > 0) + arg0 = (*argp)[0]->getAsTyped(); + } else { + assert(callNode.getAsUnaryNode()); + unaryArg = callNode.getAsUnaryNode()->getOperand(); + arg0 = unaryArg; + } + + TString featureString; + const char* feature = nullptr; + switch (callNode.getOp()) { +#ifndef GLSLANG_WEB + case EOpTextureGather: + case EOpTextureGatherOffset: + case EOpTextureGatherOffsets: + { + // Figure out which variants are allowed by what extensions, + // and what arguments must be constant for which situations. + + featureString = fnCandidate.getName(); + featureString += "(...)"; + feature = featureString.c_str(); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + int compArg = -1; // track which argument, if any, is the constant component argument + switch (callNode.getOp()) { + case EOpTextureGather: + // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, + // otherwise, need GL_ARB_texture_gather. + if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 2; + } else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + break; + case EOpTextureGatherOffset: + // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument + if (fnCandidate[0].type->getSampler().dim == Esd2D && ! fnCandidate[0].type->getSampler().shadow && fnCandidate.getParamCount() == 3) + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! (*argp)[fnCandidate[0].type->getSampler().shadow ? 3 : 2]->getAsConstantUnion()) + profileRequires(loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "non-constant offset argument"); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + break; + case EOpTextureGatherOffsets: + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + // check for constant offsets + if (! (*argp)[fnCandidate[0].type->getSampler().shadow ? 3 : 2]->getAsConstantUnion()) + error(loc, "must be a compile-time constant:", feature, "offsets argument"); + break; + default: + break; + } + + if (compArg > 0 && compArg < fnCandidate.getParamCount()) { + if ((*argp)[compArg]->getAsConstantUnion()) { + int value = (*argp)[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (value < 0 || value > 3) + error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); + } else + error(loc, "must be a compile-time constant:", feature, "component argument"); + } + + bool bias = false; + if (callNode.getOp() == EOpTextureGather) + bias = fnCandidate.getParamCount() > 3; + else if (callNode.getOp() == EOpTextureGatherOffset || + callNode.getOp() == EOpTextureGatherOffsets) + bias = fnCandidate.getParamCount() > 4; + + if (bias) { + featureString = fnCandidate.getName(); + featureString += "with bias argument"; + feature = featureString.c_str(); + profileRequires(loc, ~EEsProfile, 450, nullptr, feature); + requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature); + } + break; + } + case EOpSparseTextureGather: + case EOpSparseTextureGatherOffset: + case EOpSparseTextureGatherOffsets: + { + bool bias = false; + if (callNode.getOp() == EOpSparseTextureGather) + bias = fnCandidate.getParamCount() > 4; + else if (callNode.getOp() == EOpSparseTextureGatherOffset || + callNode.getOp() == EOpSparseTextureGatherOffsets) + bias = fnCandidate.getParamCount() > 5; + + if (bias) { + featureString = fnCandidate.getName(); + featureString += "with bias argument"; + feature = featureString.c_str(); + profileRequires(loc, ~EEsProfile, 450, nullptr, feature); + requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature); + } + + break; + } + + case EOpSparseTextureGatherLod: + case EOpSparseTextureGatherLodOffset: + case EOpSparseTextureGatherLodOffsets: + { + requireExtensions(loc, 1, &E_GL_ARB_sparse_texture2, fnCandidate.getName().c_str()); + break; + } + + case EOpSwizzleInvocations: + { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "offset", ""); + else { + unsigned offset[4] = {}; + offset[0] = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + offset[1] = (*argp)[1]->getAsConstantUnion()->getConstArray()[1].getUConst(); + offset[2] = (*argp)[1]->getAsConstantUnion()->getConstArray()[2].getUConst(); + offset[3] = (*argp)[1]->getAsConstantUnion()->getConstArray()[3].getUConst(); + if (offset[0] > 3 || offset[1] > 3 || offset[2] > 3 || offset[3] > 3) + error(loc, "components must be in the range [0, 3]", "offset", ""); + } + + break; + } + + case EOpSwizzleInvocationsMasked: + { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "mask", ""); + else { + unsigned mask[3] = {}; + mask[0] = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + mask[1] = (*argp)[1]->getAsConstantUnion()->getConstArray()[1].getUConst(); + mask[2] = (*argp)[1]->getAsConstantUnion()->getConstArray()[2].getUConst(); + if (mask[0] > 31 || mask[1] > 31 || mask[2] > 31) + error(loc, "components must be in the range [0, 31]", "mask", ""); + } + + break; + } +#endif + + case EOpTextureOffset: + case EOpTextureFetchOffset: + case EOpTextureProjOffset: + case EOpTextureLodOffset: + case EOpTextureProjLodOffset: + case EOpTextureGradOffset: + case EOpTextureProjGradOffset: + { + // Handle texture-offset limits checking + // Pick which argument has to hold constant offsets + int arg = -1; + switch (callNode.getOp()) { + case EOpTextureOffset: arg = 2; break; + case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().isRect()) ? 2 : 3; break; + case EOpTextureProjOffset: arg = 2; break; + case EOpTextureLodOffset: arg = 3; break; + case EOpTextureProjLodOffset: arg = 3; break; + case EOpTextureGradOffset: arg = 4; break; + case EOpTextureProjGradOffset: arg = 4; break; + default: + assert(0); + break; + } + + if (arg > 0) { + +#ifndef GLSLANG_WEB + bool f16ShadowCompare = (*argp)[1]->getAsTyped()->getBasicType() == EbtFloat16 && + arg0->getType().getSampler().shadow; + if (f16ShadowCompare) + ++arg; +#endif + if (! (*argp)[arg]->getAsTyped()->getQualifier().isConstant()) + error(loc, "argument must be compile-time constant", "texel offset", ""); + else if ((*argp)[arg]->getAsConstantUnion()) { + const TType& type = (*argp)[arg]->getAsTyped()->getType(); + for (int c = 0; c < type.getVectorSize(); ++c) { + int offset = (*argp)[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); + if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) + error(loc, "value is out of range:", "texel offset", + "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); + } + } + } + + break; + } + +#ifndef GLSLANG_WEB + case EOpTrace: + if (!(*argp)[10]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "payload number", ""); + break; + case EOpExecuteCallable: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "callable data number", ""); + break; + + case EOpRayQueryGetIntersectionType: + case EOpRayQueryGetIntersectionT: + case EOpRayQueryGetIntersectionInstanceCustomIndex: + case EOpRayQueryGetIntersectionInstanceId: + case EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: + case EOpRayQueryGetIntersectionGeometryIndex: + case EOpRayQueryGetIntersectionPrimitiveIndex: + case EOpRayQueryGetIntersectionBarycentrics: + case EOpRayQueryGetIntersectionFrontFace: + case EOpRayQueryGetIntersectionObjectRayDirection: + case EOpRayQueryGetIntersectionObjectRayOrigin: + case EOpRayQueryGetIntersectionObjectToWorld: + case EOpRayQueryGetIntersectionWorldToObject: + if (!(*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "committed", ""); + break; + + case EOpTextureQuerySamples: + case EOpImageQuerySamples: + // GL_ARB_shader_texture_image_samples + profileRequires(loc, ~EEsProfile, 450, E_GL_ARB_shader_texture_image_samples, "textureSamples and imageSamples"); + break; + + case EOpImageAtomicAdd: + case EOpImageAtomicMin: + case EOpImageAtomicMax: + case EOpImageAtomicAnd: + case EOpImageAtomicOr: + case EOpImageAtomicXor: + case EOpImageAtomicExchange: + case EOpImageAtomicCompSwap: + case EOpImageAtomicLoad: + case EOpImageAtomicStore: + { + // Make sure the image types have the correct layout() format and correct argument types + const TType& imageType = arg0->getType(); + if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint || + imageType.getSampler().type == EbtInt64 || imageType.getSampler().type == EbtUint64) { + if (imageType.getQualifier().getFormat() != ElfR32i && imageType.getQualifier().getFormat() != ElfR32ui && + imageType.getQualifier().getFormat() != ElfR64i && imageType.getQualifier().getFormat() != ElfR64ui) + error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), ""); + if (callNode.getType().getBasicType() == EbtInt64 && imageType.getQualifier().getFormat() != ElfR64i) + error(loc, "only supported on image with format r64i", fnCandidate.getName().c_str(), ""); + else if (callNode.getType().getBasicType() == EbtUint64 && imageType.getQualifier().getFormat() != ElfR64ui) + error(loc, "only supported on image with format r64ui", fnCandidate.getName().c_str(), ""); + } else { + bool isImageAtomicOnFloatAllowed = ((fnCandidate.getName().compare(0, 14, "imageAtomicAdd") == 0) || + (fnCandidate.getName().compare(0, 15, "imageAtomicLoad") == 0) || + (fnCandidate.getName().compare(0, 16, "imageAtomicStore") == 0) || + (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") == 0)); + if (imageType.getSampler().type == EbtFloat && isImageAtomicOnFloatAllowed && + (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0)) // imageAtomicExchange doesn't require GL_EXT_shader_atomic_float + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float, fnCandidate.getName().c_str()); + if (!isImageAtomicOnFloatAllowed) + error(loc, "only supported on integer images", fnCandidate.getName().c_str(), ""); + else if (imageType.getQualifier().getFormat() != ElfR32f && isEsProfile()) + error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), ""); + } + + const size_t maxArgs = imageType.getSampler().isMultiSample() ? 5 : 4; + if (argp->size() > maxArgs) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + } + + break; + } + + case EOpAtomicAdd: + case EOpAtomicMin: + case EOpAtomicMax: + case EOpAtomicAnd: + case EOpAtomicOr: + case EOpAtomicXor: + case EOpAtomicExchange: + case EOpAtomicCompSwap: + case EOpAtomicLoad: + case EOpAtomicStore: + { + if (argp->size() > 3) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + if ((callNode.getOp() == EOpAtomicAdd || callNode.getOp() == EOpAtomicExchange || + callNode.getOp() == EOpAtomicLoad || callNode.getOp() == EOpAtomicStore) && + (arg0->getType().isFloatingDomain())) { + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float, fnCandidate.getName().c_str()); + } + } else if (arg0->getType().getBasicType() == EbtInt64 || arg0->getType().getBasicType() == EbtUint64) { + const char* const extensions[2] = { E_GL_NV_shader_atomic_int64, + E_GL_EXT_shader_atomic_int64 }; + requireExtensions(loc, 2, extensions, fnCandidate.getName().c_str()); + } else if ((callNode.getOp() == EOpAtomicAdd || callNode.getOp() == EOpAtomicExchange) && + (arg0->getType().isFloatingDomain())) { + requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float, fnCandidate.getName().c_str()); + } + break; + } + + case EOpInterpolateAtCentroid: + case EOpInterpolateAtSample: + case EOpInterpolateAtOffset: + case EOpInterpolateAtVertex: + // Make sure the first argument is an interpolant, or an array element of an interpolant + if (arg0->getType().getQualifier().storage != EvqVaryingIn) { + // It might still be an array element. + // + // We could check more, but the semantics of the first argument are already met; the + // only way to turn an array into a float/vec* is array dereference and swizzle. + // + // ES and desktop 4.3 and earlier: swizzles may not be used + // desktop 4.4 and later: swizzles may be used + bool swizzleOkay = (!isEsProfile()) && (version >= 440); + const TIntermTyped* base = TIntermediate::findLValueBase(arg0, swizzleOkay); + if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn) + error(loc, "first argument must be an interpolant, or interpolant-array element", fnCandidate.getName().c_str(), ""); + } + + if (callNode.getOp() == EOpInterpolateAtVertex) { + if (!arg0->getType().getQualifier().isExplicitInterpolation()) + error(loc, "argument must be qualified as __explicitInterpAMD in", "interpolant", ""); + else { + if (! (*argp)[1]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "vertex index", ""); + else { + unsigned vertexIdx = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getUConst(); + if (vertexIdx > 2) + error(loc, "must be in the range [0, 2]", "vertex index", ""); + } + } + } + break; + + case EOpEmitStreamVertex: + case EOpEndStreamPrimitive: + intermediate.setMultiStream(); + break; + + case EOpSubgroupClusteredAdd: + case EOpSubgroupClusteredMul: + case EOpSubgroupClusteredMin: + case EOpSubgroupClusteredMax: + case EOpSubgroupClusteredAnd: + case EOpSubgroupClusteredOr: + case EOpSubgroupClusteredXor: + // The as used in the subgroupClustered() operations must be: + // - An integral constant expression. + // - At least 1. + // - A power of 2. + if ((*argp)[1]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "cluster size", ""); + else { + int size = (*argp)[1]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (size < 1) + error(loc, "argument must be at least 1", "cluster size", ""); + else if (!IsPow2(size)) + error(loc, "argument must be a power of 2", "cluster size", ""); + } + break; + + case EOpSubgroupBroadcast: + case EOpSubgroupQuadBroadcast: + if (spvVersion.spv < EShTargetSpv_1_5) { + // must be an integral constant expression. + if ((*argp)[1]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "id", ""); + } + break; + + case EOpBarrier: + case EOpMemoryBarrier: + if (argp->size() > 0) { + requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); + memorySemanticsCheck(loc, fnCandidate, callNode); + } + break; + + case EOpMix: + if (profile == EEsProfile && version < 310) { + // Look for specific signatures + if ((*argp)[0]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[1]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[2]->getAsTyped()->getBasicType() == EbtBool) { + requireExtensions(loc, 1, &E_GL_EXT_shader_integer_mix, "specific signature of builtin mix"); + } + } + + if (profile != EEsProfile && version < 450) { + if ((*argp)[0]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[0]->getAsTyped()->getBasicType() != EbtDouble && + (*argp)[1]->getAsTyped()->getBasicType() != EbtFloat && + (*argp)[1]->getAsTyped()->getBasicType() != EbtDouble && + (*argp)[2]->getAsTyped()->getBasicType() == EbtBool) { + requireExtensions(loc, 1, &E_GL_EXT_shader_integer_mix, fnCandidate.getName().c_str()); + } + } + + break; +#endif + + default: + break; + } + + // Texture operations on texture objects (aside from texelFetch on a + // textureBuffer) require EXT_samplerless_texture_functions. + switch (callNode.getOp()) { + case EOpTextureQuerySize: + case EOpTextureQueryLevels: + case EOpTextureQuerySamples: + case EOpTextureFetch: + case EOpTextureFetchOffset: + { + const TSampler& sampler = fnCandidate[0].type->getSampler(); + + const bool isTexture = sampler.isTexture() && !sampler.isCombined(); + const bool isBuffer = sampler.isBuffer(); + const bool isFetch = callNode.getOp() == EOpTextureFetch || callNode.getOp() == EOpTextureFetchOffset; + + if (isTexture && (!isBuffer || !isFetch)) + requireExtensions(loc, 1, &E_GL_EXT_samplerless_texture_functions, fnCandidate.getName().c_str()); + + break; + } + + default: + break; + } + + if (callNode.isSubgroup()) { + // these require SPIR-V 1.3 + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_3) + error(loc, "requires SPIR-V 1.3", "subgroup op", ""); + + // Check that if extended types are being used that the correct extensions are enabled. + if (arg0 != nullptr) { + const TType& type = arg0->getType(); + switch (type.getBasicType()) { + default: + break; + case EbtInt8: + case EbtUint8: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int8, type.getCompleteString().c_str()); + break; + case EbtInt16: + case EbtUint16: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int16, type.getCompleteString().c_str()); + break; + case EbtInt64: + case EbtUint64: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int64, type.getCompleteString().c_str()); + break; + case EbtFloat16: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_float16, type.getCompleteString().c_str()); + break; + } + } + } +} + +#ifndef GLSLANG_WEB + +extern bool PureOperatorBuiltins; + +// Deprecated! Use PureOperatorBuiltins == true instead, in which case this +// functionality is handled in builtInOpCheck() instead of here. +// +// Do additional checking of built-in function calls that were not mapped +// to built-in operations (e.g., texturing functions). +// +// Assumes there has been a semantically correct match to a built-in function. +// +void TParseContext::nonOpBuiltInCheck(const TSourceLoc& loc, const TFunction& fnCandidate, TIntermAggregate& callNode) +{ + // Further maintenance of this function is deprecated, because the "correct" + // future-oriented design is to not have to do string compares on function names. + + // If PureOperatorBuiltins == true, then all built-ins should be mapped + // to a TOperator, and this function would then never get called. + + assert(PureOperatorBuiltins == false); + + // built-in texturing functions get their return value precision from the precision of the sampler + if (fnCandidate.getType().getQualifier().precision == EpqNone && + fnCandidate.getParamCount() > 0 && fnCandidate[0].type->getBasicType() == EbtSampler) + callNode.getQualifier().precision = callNode.getSequence()[0]->getAsTyped()->getQualifier().precision; + + if (fnCandidate.getName().compare(0, 7, "texture") == 0) { + if (fnCandidate.getName().compare(0, 13, "textureGather") == 0) { + TString featureString = fnCandidate.getName() + "(...)"; + const char* feature = featureString.c_str(); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + + int compArg = -1; // track which argument, if any, is the constant component argument + if (fnCandidate.getName().compare("textureGatherOffset") == 0) { + // GL_ARB_texture_gather is good enough for 2D non-shadow textures with no component argument + if (fnCandidate[0].type->getSampler().dim == Esd2D && ! fnCandidate[0].type->getSampler().shadow && fnCandidate.getParamCount() == 3) + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + int offsetArg = fnCandidate[0].type->getSampler().shadow ? 3 : 2; + if (! callNode.getSequence()[offsetArg]->getAsConstantUnion()) + profileRequires(loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, + "non-constant offset argument"); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + } else if (fnCandidate.getName().compare("textureGatherOffsets") == 0) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 3; + // check for constant offsets + int offsetArg = fnCandidate[0].type->getSampler().shadow ? 3 : 2; + if (! callNode.getSequence()[offsetArg]->getAsConstantUnion()) + error(loc, "must be a compile-time constant:", feature, "offsets argument"); + } else if (fnCandidate.getName().compare("textureGather") == 0) { + // More than two arguments needs gpu_shader5, and rectangular or shadow needs gpu_shader5, + // otherwise, need GL_ARB_texture_gather. + if (fnCandidate.getParamCount() > 2 || fnCandidate[0].type->getSampler().dim == EsdRect || fnCandidate[0].type->getSampler().shadow) { + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_gpu_shader5, feature); + if (! fnCandidate[0].type->getSampler().shadow) + compArg = 2; + } else + profileRequires(loc, ~EEsProfile, 400, E_GL_ARB_texture_gather, feature); + } + + if (compArg > 0 && compArg < fnCandidate.getParamCount()) { + if (callNode.getSequence()[compArg]->getAsConstantUnion()) { + int value = callNode.getSequence()[compArg]->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (value < 0 || value > 3) + error(loc, "must be 0, 1, 2, or 3:", feature, "component argument"); + } else + error(loc, "must be a compile-time constant:", feature, "component argument"); + } + } else { + // this is only for functions not starting "textureGather"... + if (fnCandidate.getName().find("Offset") != TString::npos) { + + // Handle texture-offset limits checking + int arg = -1; + if (fnCandidate.getName().compare("textureOffset") == 0) + arg = 2; + else if (fnCandidate.getName().compare("texelFetchOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureProjOffset") == 0) + arg = 2; + else if (fnCandidate.getName().compare("textureLodOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureProjLodOffset") == 0) + arg = 3; + else if (fnCandidate.getName().compare("textureGradOffset") == 0) + arg = 4; + else if (fnCandidate.getName().compare("textureProjGradOffset") == 0) + arg = 4; + + if (arg > 0) { + if (! callNode.getSequence()[arg]->getAsConstantUnion()) + error(loc, "argument must be compile-time constant", "texel offset", ""); + else { + const TType& type = callNode.getSequence()[arg]->getAsTyped()->getType(); + for (int c = 0; c < type.getVectorSize(); ++c) { + int offset = callNode.getSequence()[arg]->getAsConstantUnion()->getConstArray()[c].getIConst(); + if (offset > resources.maxProgramTexelOffset || offset < resources.minProgramTexelOffset) + error(loc, "value is out of range:", "texel offset", "[gl_MinProgramTexelOffset, gl_MaxProgramTexelOffset]"); + } + } + } + } + } + } + + // GL_ARB_shader_texture_image_samples + if (fnCandidate.getName().compare(0, 14, "textureSamples") == 0 || fnCandidate.getName().compare(0, 12, "imageSamples") == 0) + profileRequires(loc, ~EEsProfile, 450, E_GL_ARB_shader_texture_image_samples, "textureSamples and imageSamples"); + + if (fnCandidate.getName().compare(0, 11, "imageAtomic") == 0) { + const TType& imageType = callNode.getSequence()[0]->getAsTyped()->getType(); + if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint) { + if (imageType.getQualifier().getFormat() != ElfR32i && imageType.getQualifier().getFormat() != ElfR32ui) + error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), ""); + } else { + if (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0) + error(loc, "only supported on integer images", fnCandidate.getName().c_str(), ""); + else if (imageType.getQualifier().getFormat() != ElfR32f && isEsProfile()) + error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), ""); + } + } +} + +#endif + +// +// Do any extra checking for a user function call. +// +void TParseContext::userFunctionCallCheck(const TSourceLoc& loc, TIntermAggregate& callNode) +{ + TIntermSequence& arguments = callNode.getSequence(); + + for (int i = 0; i < (int)arguments.size(); ++i) + samplerConstructorLocationCheck(loc, "call argument", arguments[i]); +} + +// +// Emit an error if this is a sampler constructor +// +void TParseContext::samplerConstructorLocationCheck(const TSourceLoc& loc, const char* token, TIntermNode* node) +{ + if (node->getAsOperator() && node->getAsOperator()->getOp() == EOpConstructTextureSampler) + error(loc, "sampler constructor must appear at point of use", token, ""); +} + +// +// Handle seeing a built-in constructor in a grammar production. +// +TFunction* TParseContext::handleConstructorCall(const TSourceLoc& loc, const TPublicType& publicType) +{ + TType type(publicType); + type.getQualifier().precision = EpqNone; + + if (type.isArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed constructor"); + profileRequires(loc, EEsProfile, 300, nullptr, "arrayed constructor"); + } + + TOperator op = intermediate.mapTypeToConstructorOp(type); + + if (op == EOpNull) { + error(loc, "cannot construct this type", type.getBasicString(), ""); + op = EOpConstructFloat; + TType errorType(EbtFloat); + type.shallowCopy(errorType); + } + + TString empty(""); + + return new TFunction(&empty, type, op); +} + +// Handle seeing a precision qualifier in the grammar. +void TParseContext::handlePrecisionQualifier(const TSourceLoc& /*loc*/, TQualifier& qualifier, TPrecisionQualifier precision) +{ + if (obeyPrecisionQualifiers()) + qualifier.precision = precision; +} + +// Check for messages to give on seeing a precision qualifier used in a +// declaration in the grammar. +void TParseContext::checkPrecisionQualifier(const TSourceLoc& loc, TPrecisionQualifier) +{ + if (precisionManager.shouldWarnAboutDefaults()) { + warn(loc, "all default precisions are highp; use precision statements to quiet warning, e.g.:\n" + " \"precision mediump int; precision highp float;\"", "", ""); + precisionManager.defaultWarningGiven(); + } +} + +// +// Same error message for all places assignments don't work. +// +void TParseContext::assignError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, "", op, "cannot convert from '%s' to '%s'", + right.c_str(), left.c_str()); +} + +// +// Same error message for all places unary operations don't work. +// +void TParseContext::unaryOpError(const TSourceLoc& loc, const char* op, TString operand) +{ + error(loc, " wrong operand type", op, + "no operation '%s' exists that takes an operand of type %s (or there is no acceptable conversion)", + op, operand.c_str()); +} + +// +// Same error message for all binary operations don't work. +// +void TParseContext::binaryOpError(const TSourceLoc& loc, const char* op, TString left, TString right) +{ + error(loc, " wrong operand types:", op, + "no operation '%s' exists that takes a left-hand operand of type '%s' and " + "a right operand of type '%s' (or there is no acceptable conversion)", + op, left.c_str(), right.c_str()); +} + +// +// A basic type of EbtVoid is a key that the name string was seen in the source, but +// it was not found as a variable in the symbol table. If so, give the error +// message and insert a dummy variable in the symbol table to prevent future errors. +// +void TParseContext::variableCheck(TIntermTyped*& nodePtr) +{ + TIntermSymbol* symbol = nodePtr->getAsSymbolNode(); + if (! symbol) + return; + + if (symbol->getType().getBasicType() == EbtVoid) { + const char *extraInfoFormat = ""; + if (spvVersion.vulkan != 0 && symbol->getName() == "gl_VertexID") { + extraInfoFormat = "(Did you mean gl_VertexIndex?)"; + } else if (spvVersion.vulkan != 0 && symbol->getName() == "gl_InstanceID") { + extraInfoFormat = "(Did you mean gl_InstanceIndex?)"; + } + error(symbol->getLoc(), "undeclared identifier", symbol->getName().c_str(), extraInfoFormat); + + // Add to symbol table to prevent future error messages on the same name + if (symbol->getName().size() > 0) { + TVariable* fakeVariable = new TVariable(&symbol->getName(), TType(EbtFloat)); + symbolTable.insert(*fakeVariable); + + // substitute a symbol node for this new variable + nodePtr = intermediate.addSymbol(*fakeVariable, symbol->getLoc()); + } + } else { + switch (symbol->getQualifier().storage) { + case EvqPointCoord: + profileRequires(symbol->getLoc(), ENoProfile, 120, nullptr, "gl_PointCoord"); + break; + default: break; // some compilers want this + } + } +} + +// +// Both test and if necessary, spit out an error, to see if the node is really +// an l-value that can be operated on this way. +// +// Returns true if there was an error. +// +bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + TIntermBinary* binaryNode = node->getAsBinaryNode(); + + if (binaryNode) { + bool errorReturn = false; + + switch(binaryNode->getOp()) { +#ifndef GLSLANG_WEB + case EOpIndexDirect: + case EOpIndexIndirect: + // ... tessellation control shader ... + // If a per-vertex output variable is used as an l-value, it is a + // compile-time or link-time error if the expression indicating the + // vertex index is not the identifier gl_InvocationID. + if (language == EShLangTessControl) { + const TType& leftType = binaryNode->getLeft()->getType(); + if (leftType.getQualifier().storage == EvqVaryingOut && ! leftType.getQualifier().patch && binaryNode->getLeft()->getAsSymbolNode()) { + // we have a per-vertex output + const TIntermSymbol* rightSymbol = binaryNode->getRight()->getAsSymbolNode(); + if (! rightSymbol || rightSymbol->getQualifier().builtIn != EbvInvocationId) + error(loc, "tessellation-control per-vertex output l-value must be indexed with gl_InvocationID", "[]", ""); + } + } + break; // left node is checked by base class +#endif + case EOpVectorSwizzle: + errorReturn = lValueErrorCheck(loc, op, binaryNode->getLeft()); + if (!errorReturn) { + int offset[4] = {0,0,0,0}; + + TIntermTyped* rightNode = binaryNode->getRight(); + TIntermAggregate *aggrNode = rightNode->getAsAggregate(); + + for (TIntermSequence::iterator p = aggrNode->getSequence().begin(); + p != aggrNode->getSequence().end(); p++) { + int value = (*p)->getAsTyped()->getAsConstantUnion()->getConstArray()[0].getIConst(); + offset[value]++; + if (offset[value] > 1) { + error(loc, " l-value of swizzle cannot have duplicate components", op, "", ""); + + return true; + } + } + } + + return errorReturn; + default: + break; + } + + if (errorReturn) { + error(loc, " l-value required", op, "", ""); + return true; + } + } + + if (binaryNode && binaryNode->getOp() == EOpIndexDirectStruct && binaryNode->getLeft()->isReference()) + return false; + + // Let the base class check errors + if (TParseContextBase::lValueErrorCheck(loc, op, node)) + return true; + + const char* symbol = nullptr; + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (symNode != nullptr) + symbol = symNode->getName().c_str(); + + const char* message = nullptr; + switch (node->getQualifier().storage) { + case EvqVaryingIn: message = "can't modify shader input"; break; + case EvqInstanceId: message = "can't modify gl_InstanceID"; break; + case EvqVertexId: message = "can't modify gl_VertexID"; break; + case EvqFace: message = "can't modify gl_FrontFace"; break; + case EvqFragCoord: message = "can't modify gl_FragCoord"; break; + case EvqPointCoord: message = "can't modify gl_PointCoord"; break; + case EvqFragDepth: + intermediate.setDepthReplacing(); + // "In addition, it is an error to statically write to gl_FragDepth in the fragment shader." + if (isEsProfile() && intermediate.getEarlyFragmentTests()) + message = "can't modify gl_FragDepth if using early_fragment_tests"; + break; + + default: + break; + } + + if (message == nullptr && binaryNode == nullptr && symNode == nullptr) { + error(loc, " l-value required", op, "", ""); + + return true; + } + + // + // Everything else is okay, no error. + // + if (message == nullptr) + return false; + + // + // If we get here, we have an error and a message. + // + if (symNode) + error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message); + else + error(loc, " l-value required", op, "(%s)", message); + + return true; +} + +// Test for and give an error if the node can't be read from. +void TParseContext::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node) +{ + // Let the base class check errors + TParseContextBase::rValueErrorCheck(loc, op, node); + + TIntermSymbol* symNode = node->getAsSymbolNode(); + if (!(symNode && symNode->getQualifier().isWriteOnly())) // base class checks + if (symNode && symNode->getQualifier().isExplicitInterpolation()) + error(loc, "can't read from explicitly-interpolated object: ", op, symNode->getName().c_str()); +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// a constant. +// +void TParseContext::constantValueCheck(TIntermTyped* node, const char* token) +{ + if (! node->getQualifier().isConstant()) + error(node->getLoc(), "constant expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if the node is really +// an integer. +// +void TParseContext::integerCheck(const TIntermTyped* node, const char* token) +{ + if ((node->getBasicType() == EbtInt || node->getBasicType() == EbtUint) && node->isScalar()) + return; + + error(node->getLoc(), "scalar integer expression required", token, ""); +} + +// +// Both test, and if necessary spit out an error, to see if we are currently +// globally scoped. +// +void TParseContext::globalCheck(const TSourceLoc& loc, const char* token) +{ + if (! symbolTable.atGlobalLevel()) + error(loc, "not allowed in nested scope", token, ""); +} + +// +// Reserved errors for GLSL. +// +void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& identifier) +{ + // "Identifiers starting with "gl_" are reserved for use by OpenGL, and may not be + // declared in a shader; this results in a compile-time error." + if (! symbolTable.atBuiltInLevel()) { + if (builtInName(identifier)) + error(loc, "identifiers starting with \"gl_\" are reserved", identifier.c_str(), ""); + + // "__" are not supposed to be an error. ES 300 (and desktop) added the clarification: + // "In addition, all identifiers containing two consecutive underscores (__) are + // reserved; using such a name does not itself result in an error, but may result + // in undefined behavior." + // however, before that, ES tests required an error. + if (identifier.find("__") != TString::npos) { + if (isEsProfile() && version < 300) + error(loc, "identifiers containing consecutive underscores (\"__\") are reserved, and an error if version < 300", identifier.c_str(), ""); + else + warn(loc, "identifiers containing consecutive underscores (\"__\") are reserved", identifier.c_str(), ""); + } + } +} + +// +// Reserved errors for the preprocessor. +// +void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* identifier, const char* op) +{ + // "__" are not supposed to be an error. ES 300 (and desktop) added the clarification: + // "All macro names containing two consecutive underscores ( __ ) are reserved; + // defining such a name does not itself result in an error, but may result in + // undefined behavior. All macro names prefixed with "GL_" ("GL" followed by a + // single underscore) are also reserved, and defining such a name results in a + // compile-time error." + // however, before that, ES tests required an error. + if (strncmp(identifier, "GL_", 3) == 0) + ppError(loc, "names beginning with \"GL_\" can't be (un)defined:", op, identifier); + else if (strncmp(identifier, "defined", 8) == 0) + if (relaxedErrors()) + ppWarn(loc, "\"defined\" is (un)defined:", op, identifier); + else + ppError(loc, "\"defined\" can't be (un)defined:", op, identifier); + else if (strstr(identifier, "__") != 0) { + if (isEsProfile() && version >= 300 && + (strcmp(identifier, "__LINE__") == 0 || + strcmp(identifier, "__FILE__") == 0 || + strcmp(identifier, "__VERSION__") == 0)) + ppError(loc, "predefined names can't be (un)defined:", op, identifier); + else { + if (isEsProfile() && version < 300 && !relaxedErrors()) + ppError(loc, "names containing consecutive underscores are reserved, and an error if version < 300:", op, identifier); + else + ppWarn(loc, "names containing consecutive underscores are reserved:", op, identifier); + } + } +} + +// +// See if this version/profile allows use of the line-continuation character '\'. +// +// Returns true if a line continuation should be done. +// +bool TParseContext::lineContinuationCheck(const TSourceLoc& loc, bool endOfComment) +{ +#ifdef GLSLANG_WEB + return true; +#endif + + const char* message = "line continuation"; + + bool lineContinuationAllowed = (isEsProfile() && version >= 300) || + (!isEsProfile() && (version >= 420 || extensionTurnedOn(E_GL_ARB_shading_language_420pack))); + + if (endOfComment) { + if (lineContinuationAllowed) + warn(loc, "used at end of comment; the following line is still part of the comment", message, ""); + else + warn(loc, "used at end of comment, but this version does not provide line continuation", message, ""); + + return lineContinuationAllowed; + } + + if (relaxedErrors()) { + if (! lineContinuationAllowed) + warn(loc, "not allowed in this version", message, ""); + return true; + } else { + profileRequires(loc, EEsProfile, 300, nullptr, message); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, message); + } + + return lineContinuationAllowed; +} + +bool TParseContext::builtInName(const TString& identifier) +{ + return identifier.compare(0, 3, "gl_") == 0; +} + +// +// Make sure there is enough data and not too many arguments provided to the +// constructor to build something of the type of the constructor. Also returns +// the type of the constructor. +// +// Part of establishing type is establishing specialization-constness. +// We don't yet know "top down" whether type is a specialization constant, +// but a const constructor can becomes a specialization constant if any of +// its children are, subject to KHR_vulkan_glsl rules: +// +// - int(), uint(), and bool() constructors for type conversions +// from any of the following types to any of the following types: +// * int +// * uint +// * bool +// - vector versions of the above conversion constructors +// +// Returns true if there was an error in construction. +// +bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, TOperator op, TType& type) +{ + // See if the constructor does not establish the main type, only requalifies + // it, in which case the type comes from the argument instead of from the + // constructor function. + switch (op) { +#ifndef GLSLANG_WEB + case EOpConstructNonuniform: + if (node != nullptr && node->getAsTyped() != nullptr) { + type.shallowCopy(node->getAsTyped()->getType()); + type.getQualifier().makeTemporary(); + type.getQualifier().nonUniform = true; + } + break; +#endif + default: + type.shallowCopy(function.getType()); + break; + } + + // See if it's a matrix + bool constructingMatrix = false; + switch (op) { + case EOpConstructTextureSampler: + return constructorTextureSamplerError(loc, function); + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: +#ifndef GLSLANG_WEB + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructF16Mat2x2: + case EOpConstructF16Mat2x3: + case EOpConstructF16Mat2x4: + case EOpConstructF16Mat3x2: + case EOpConstructF16Mat3x3: + case EOpConstructF16Mat3x4: + case EOpConstructF16Mat4x2: + case EOpConstructF16Mat4x3: + case EOpConstructF16Mat4x4: +#endif + constructingMatrix = true; + break; + default: + break; + } + + // + // Walk the arguments for first-pass checks and collection of information. + // + + int size = 0; + bool constType = true; + bool specConstType = false; // value is only valid if constType is true + bool full = false; + bool overFull = false; + bool matrixInMatrix = false; + bool arrayArg = false; + bool floatArgument = false; + for (int arg = 0; arg < function.getParamCount(); ++arg) { + if (function[arg].type->isArray()) { + if (function[arg].type->isUnsizedArray()) { + // Can't construct from an unsized array. + error(loc, "array argument must be sized", "constructor", ""); + return true; + } + arrayArg = true; + } + if (constructingMatrix && function[arg].type->isMatrix()) + matrixInMatrix = true; + + // 'full' will go to true when enough args have been seen. If we loop + // again, there is an extra argument. + if (full) { + // For vectors and matrices, it's okay to have too many components + // available, but not okay to have unused arguments. + overFull = true; + } + + size += function[arg].type->computeNumComponents(); + if (op != EOpConstructStruct && ! type.isArray() && size >= type.computeNumComponents()) + full = true; + + if (! function[arg].type->getQualifier().isConstant()) + constType = false; + if (function[arg].type->getQualifier().isSpecConstant()) + specConstType = true; + if (function[arg].type->isFloatingDomain()) + floatArgument = true; + if (type.isStruct()) { + if (function[arg].type->contains16BitFloat()) { + requireFloat16Arithmetic(loc, "constructor", "can't construct structure containing 16-bit type"); + } + if (function[arg].type->contains16BitInt()) { + requireInt16Arithmetic(loc, "constructor", "can't construct structure containing 16-bit type"); + } + if (function[arg].type->contains8BitInt()) { + requireInt8Arithmetic(loc, "constructor", "can't construct structure containing 8-bit type"); + } + } + } + if (op == EOpConstructNonuniform) + constType = false; + +#ifndef GLSLANG_WEB + switch (op) { + case EOpConstructFloat16: + case EOpConstructF16Vec2: + case EOpConstructF16Vec3: + case EOpConstructF16Vec4: + if (type.isArray()) + requireFloat16Arithmetic(loc, "constructor", "16-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireFloat16Arithmetic(loc, "constructor", "16-bit vectors only take vector types"); + break; + case EOpConstructUint16: + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructInt16: + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + if (type.isArray()) + requireInt16Arithmetic(loc, "constructor", "16-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireInt16Arithmetic(loc, "constructor", "16-bit vectors only take vector types"); + break; + case EOpConstructUint8: + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructInt8: + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + if (type.isArray()) + requireInt8Arithmetic(loc, "constructor", "8-bit arrays not supported"); + if (type.isVector() && function.getParamCount() != 1) + requireInt8Arithmetic(loc, "constructor", "8-bit vectors only take vector types"); + break; + default: + break; + } +#endif + + // inherit constness from children + if (constType) { + bool makeSpecConst; + // Finish pinning down spec-const semantics + if (specConstType) { + switch (op) { + case EOpConstructInt8: + case EOpConstructInt: + case EOpConstructUint: + case EOpConstructBool: + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructUVec2: + case EOpConstructUVec3: + case EOpConstructUVec4: +#ifndef GLSLANG_WEB + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt64: + case EOpConstructUint64: + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructI64Vec2: + case EOpConstructI64Vec3: + case EOpConstructI64Vec4: + case EOpConstructU64Vec2: + case EOpConstructU64Vec3: + case EOpConstructU64Vec4: +#endif + // This was the list of valid ones, if they aren't converting from float + // and aren't making an array. + makeSpecConst = ! floatArgument && ! type.isArray(); + break; + default: + // anything else wasn't white-listed in the spec as a conversion + makeSpecConst = false; + break; + } + } else + makeSpecConst = false; + + if (makeSpecConst) + type.getQualifier().makeSpecConstant(); + else if (specConstType) + type.getQualifier().makeTemporary(); + else + type.getQualifier().storage = EvqConst; + } + + if (type.isArray()) { + if (function.getParamCount() == 0) { + error(loc, "array constructor must have at least one argument", "constructor", ""); + return true; + } + + if (type.isUnsizedArray()) { + // auto adapt the constructor type to the number of arguments + type.changeOuterArraySize(function.getParamCount()); + } else if (type.getOuterArraySize() != function.getParamCount()) { + error(loc, "array constructor needs one argument per array element", "constructor", ""); + return true; + } + + if (type.isArrayOfArrays()) { + // Types have to match, but we're still making the type. + // Finish making the type, and the comparison is done later + // when checking for conversion. + TArraySizes& arraySizes = *type.getArraySizes(); + + // At least the dimensionalities have to match. + if (! function[0].type->isArray() || + arraySizes.getNumDims() != function[0].type->getArraySizes()->getNumDims() + 1) { + error(loc, "array constructor argument not correct type to construct array element", "constructor", ""); + return true; + } + + if (arraySizes.isInnerUnsized()) { + // "Arrays of arrays ..., and the size for any dimension is optional" + // That means we need to adopt (from the first argument) the other array sizes into the type. + for (int d = 1; d < arraySizes.getNumDims(); ++d) { + if (arraySizes.getDimSize(d) == UnsizedArraySize) { + arraySizes.setDimSize(d, function[0].type->getArraySizes()->getDimSize(d - 1)); + } + } + } + } + } + + if (arrayArg && op != EOpConstructStruct && ! type.isArrayOfArrays()) { + error(loc, "constructing non-array constituent from array argument", "constructor", ""); + return true; + } + + if (matrixInMatrix && ! type.isArray()) { + profileRequires(loc, ENoProfile, 120, nullptr, "constructing matrix from matrix"); + + // "If a matrix argument is given to a matrix constructor, + // it is a compile-time error to have any other arguments." + if (function.getParamCount() != 1) + error(loc, "matrix constructed from matrix can only have one argument", "constructor", ""); + return false; + } + + if (overFull) { + error(loc, "too many arguments", "constructor", ""); + return true; + } + + if (op == EOpConstructStruct && ! type.isArray() && (int)type.getStruct()->size() != function.getParamCount()) { + error(loc, "Number of constructor parameters does not match the number of structure fields", "constructor", ""); + return true; + } + + if ((op != EOpConstructStruct && size != 1 && size < type.computeNumComponents()) || + (op == EOpConstructStruct && size < type.computeNumComponents())) { + error(loc, "not enough data provided for construction", "constructor", ""); + return true; + } + + if (type.isCoopMat() && function.getParamCount() != 1) { + error(loc, "wrong number of arguments", "constructor", ""); + return true; + } + if (type.isCoopMat() && + !(function[0].type->isScalar() || function[0].type->isCoopMat())) { + error(loc, "Cooperative matrix constructor argument must be scalar or cooperative matrix", "constructor", ""); + return true; + } + + TIntermTyped* typed = node->getAsTyped(); + if (typed == nullptr) { + error(loc, "constructor argument does not have a type", "constructor", ""); + return true; + } + if (op != EOpConstructStruct && op != EOpConstructNonuniform && typed->getBasicType() == EbtSampler) { + error(loc, "cannot convert a sampler", "constructor", ""); + return true; + } + if (op != EOpConstructStruct && typed->isAtomic()) { + error(loc, "cannot convert an atomic_uint", "constructor", ""); + return true; + } + if (typed->getBasicType() == EbtVoid) { + error(loc, "cannot convert a void", "constructor", ""); + return true; + } + + return false; +} + +// Verify all the correct semantics for constructing a combined texture/sampler. +// Return true if the semantics are incorrect. +bool TParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const TFunction& function) +{ + TString constructorName = function.getType().getBasicTypeString(); // TODO: performance: should not be making copy; interface needs to change + const char* token = constructorName.c_str(); + + // exactly two arguments needed + if (function.getParamCount() != 2) { + error(loc, "sampler-constructor requires two arguments", token, ""); + return true; + } + + // For now, not allowing arrayed constructors, the rest of this function + // is set up to allow them, if this test is removed: + if (function.getType().isArray()) { + error(loc, "sampler-constructor cannot make an array of samplers", token, ""); + return true; + } + + // first argument + // * the constructor's first argument must be a texture type + // * the dimensionality (1D, 2D, 3D, Cube, Rect, Buffer, MS, and Array) + // of the texture type must match that of the constructed sampler type + // (that is, the suffixes of the type of the first argument and the + // type of the constructor will be spelled the same way) + if (function[0].type->getBasicType() != EbtSampler || + ! function[0].type->getSampler().isTexture() || + function[0].type->isArray()) { + error(loc, "sampler-constructor first argument must be a scalar *texture* type", token, ""); + return true; + } + // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=() + TSampler texture = function.getType().getSampler(); + texture.setCombined(false); + texture.shadow = false; + if (texture != function[0].type->getSampler()) { + error(loc, "sampler-constructor first argument must be a *texture* type" + " matching the dimensionality and sampled type of the constructor", token, ""); + return true; + } + + // second argument + // * the constructor's second argument must be a scalar of type + // *sampler* or *samplerShadow* + if ( function[1].type->getBasicType() != EbtSampler || + ! function[1].type->getSampler().isPureSampler() || + function[1].type->isArray()) { + error(loc, "sampler-constructor second argument must be a scalar sampler or samplerShadow", token, ""); + return true; + } + + return false; +} + +// Checks to see if a void variable has been declared and raise an error message for such a case +// +// returns true in case of an error +// +bool TParseContext::voidErrorCheck(const TSourceLoc& loc, const TString& identifier, const TBasicType basicType) +{ + if (basicType == EbtVoid) { + error(loc, "illegal use of type 'void'", identifier.c_str(), ""); + return true; + } + + return false; +} + +// Checks to see if the node (for the expression) contains a scalar boolean expression or not +void TParseContext::boolCheck(const TSourceLoc& loc, const TIntermTyped* type) +{ + if (type->getBasicType() != EbtBool || type->isArray() || type->isMatrix() || type->isVector()) + error(loc, "boolean expression expected", "", ""); +} + +// This function checks to see if the node (for the expression) contains a scalar boolean expression or not +void TParseContext::boolCheck(const TSourceLoc& loc, const TPublicType& pType) +{ + if (pType.basicType != EbtBool || pType.arraySizes || pType.matrixCols > 1 || (pType.vectorSize > 1)) + error(loc, "boolean expression expected", "", ""); +} + +void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const TString& identifier, TIntermTyped* /*initializer*/) +{ + // Check that the appropriate extension is enabled if external sampler is used. + // There are two extensions. The correct one must be used based on GLSL version. + if (type.getBasicType() == EbtSampler && type.getSampler().isExternal()) { + if (version < 300) { + requireExtensions(loc, 1, &E_GL_OES_EGL_image_external, "samplerExternalOES"); + } else { + requireExtensions(loc, 1, &E_GL_OES_EGL_image_external_essl3, "samplerExternalOES"); + } + } + if (type.getSampler().isYuv()) { + requireExtensions(loc, 1, &E_GL_EXT_YUV_target, "__samplerExternal2DY2YEXT"); + } + + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtSampler)) + error(loc, "non-uniform struct contains a sampler or image:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getBasicType() == EbtSampler && type.getQualifier().storage != EvqUniform) { + // non-uniform sampler + // not yet: okay if it has an initializer + // if (! initializer) + error(loc, "sampler/image types can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str()); + } +} + +#ifndef GLSLANG_WEB + +void TParseContext::atomicUintCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtAtomicUint)) + error(loc, "non-uniform struct contains an atomic_uint:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getBasicType() == EbtAtomicUint && type.getQualifier().storage != EvqUniform) + error(loc, "atomic_uints can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str()); +} + +void TParseContext::accStructCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (type.getQualifier().storage == EvqUniform) + return; + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtAccStruct)) + error(loc, "non-uniform struct contains an accelerationStructureNV:", type.getBasicTypeString().c_str(), identifier.c_str()); + else if (type.getBasicType() == EbtAccStruct && type.getQualifier().storage != EvqUniform) + error(loc, "accelerationStructureNV can only be used in uniform variables or function parameters:", + type.getBasicTypeString().c_str(), identifier.c_str()); + +} + +#endif // GLSLANG_WEB + +void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) +{ + if (parsingBuiltins) + return; + + if (type.getQualifier().storage != EvqUniform) + return; + + if (type.containsNonOpaque()) { + // Vulkan doesn't allow transparent uniforms outside of blocks + if (spvVersion.vulkan > 0) + vulkanRemoved(loc, "non-opaque uniforms outside a block"); + // OpenGL wants locations on these (unless they are getting automapped) + if (spvVersion.openGl > 0 && !type.getQualifier().hasLocation() && !intermediate.getAutoMapLocations()) + error(loc, "non-opaque uniform variables need a layout(location=L)", identifier.c_str(), ""); + } +} + +// +// Qualifier checks knowing the qualifier and that it is a member of a struct/block. +// +void TParseContext::memberQualifierCheck(glslang::TPublicType& publicType) +{ + globalQualifierFixCheck(publicType.loc, publicType.qualifier, true); + checkNoShaderLayouts(publicType.loc, publicType.shaderQualifiers); + if (publicType.qualifier.isNonUniform()) { + error(publicType.loc, "not allowed on block or structure members", "nonuniformEXT", ""); + publicType.qualifier.nonUniform = false; + } +} + +// +// Check/fix just a full qualifier (no variables or types yet, but qualifier is complete) at global level. +// +void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& qualifier, bool isMemberCheck) +{ + bool nonuniformOkay = false; + + // move from parameter/unknown qualifiers to pipeline in/out qualifiers + switch (qualifier.storage) { + case EvqIn: + profileRequires(loc, ENoProfile, 130, nullptr, "in for stage inputs"); + profileRequires(loc, EEsProfile, 300, nullptr, "in for stage inputs"); + qualifier.storage = EvqVaryingIn; + nonuniformOkay = true; + break; + case EvqOut: + profileRequires(loc, ENoProfile, 130, nullptr, "out for stage outputs"); + profileRequires(loc, EEsProfile, 300, nullptr, "out for stage outputs"); + qualifier.storage = EvqVaryingOut; + break; + case EvqInOut: + qualifier.storage = EvqVaryingIn; + error(loc, "cannot use 'inout' at global scope", "", ""); + break; + case EvqGlobal: + case EvqTemporary: + nonuniformOkay = true; + break; + case EvqUniform: + // According to GLSL spec: The std430 qualifier is supported only for shader storage blocks; a shader using + // the std430 qualifier on a uniform block will fail to compile. + // Only check the global declaration: layout(std430) uniform; + if (blockName == nullptr && + qualifier.layoutPacking == ElpStd430) + { + error(loc, "it is invalid to declare std430 qualifier on uniform", "", ""); + } + break; + default: + break; + } + + if (!nonuniformOkay && qualifier.isNonUniform()) + error(loc, "for non-parameter, can only apply to 'in' or no storage qualifier", "nonuniformEXT", ""); + + // Storage qualifier isn't ready for memberQualifierCheck, we should skip invariantCheck for it. + if (!isMemberCheck || structNestingLevel > 0) + invariantCheck(loc, qualifier); +} + +// +// Check a full qualifier and type (no variable yet) at global level. +// +void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQualifier& qualifier, const TPublicType& publicType) +{ + if (! symbolTable.atGlobalLevel()) + return; + + if (!(publicType.userDef && publicType.userDef->isReference())) { + if (qualifier.isMemoryQualifierImageAndSSBOOnly() && ! publicType.isImage() && publicType.qualifier.storage != EvqBuffer) { + error(loc, "memory qualifiers cannot be used on this type", "", ""); + } else if (qualifier.isMemory() && (publicType.basicType != EbtSampler) && !publicType.qualifier.isUniformOrBuffer()) { + error(loc, "memory qualifiers cannot be used on this type", "", ""); + } + } + + if (qualifier.storage == EvqBuffer && + publicType.basicType != EbtBlock && + !qualifier.hasBufferReference()) + error(loc, "buffers can be declared only as blocks", "buffer", ""); + + if (qualifier.storage != EvqVaryingIn && publicType.basicType == EbtDouble && + extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit) && language == EShLangVertex && + version < 400) { + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 410, E_GL_ARB_gpu_shader_fp64, "vertex-shader `double` type"); + } + if (qualifier.storage != EvqVaryingIn && qualifier.storage != EvqVaryingOut) + return; + + if (publicType.shaderQualifiers.hasBlendEquation()) + error(loc, "can only be applied to a standalone 'out'", "blend equation", ""); + + // now, knowing it is a shader in/out, do all the in/out semantic checks + + if (publicType.basicType == EbtBool && !parsingBuiltins) { + error(loc, "cannot be bool", GetStorageQualifierString(qualifier.storage), ""); + return; + } + + if (isTypeInt(publicType.basicType) || publicType.basicType == EbtDouble) + profileRequires(loc, EEsProfile, 300, nullptr, "shader input/output"); + + if (!qualifier.flat && !qualifier.isExplicitInterpolation() && !qualifier.isPervertexNV()) { + if (isTypeInt(publicType.basicType) || + publicType.basicType == EbtDouble || + (publicType.userDef && ( publicType.userDef->containsBasicType(EbtInt) + || publicType.userDef->containsBasicType(EbtUint) + || publicType.userDef->contains16BitInt() + || publicType.userDef->contains8BitInt() + || publicType.userDef->contains64BitInt() + || publicType.userDef->containsDouble()))) { + if (qualifier.storage == EvqVaryingIn && language == EShLangFragment) + error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage)); + else if (qualifier.storage == EvqVaryingOut && language == EShLangVertex && version == 300) + error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage)); + } + } + + if (qualifier.isPatch() && qualifier.isInterpolation()) + error(loc, "cannot use interpolation qualifiers with patch", "patch", ""); + + if (qualifier.isTaskMemory() && publicType.basicType != EbtBlock) + error(loc, "taskNV variables can be declared only as blocks", "taskNV", ""); + + if (qualifier.storage == EvqVaryingIn) { + switch (language) { + case EShLangVertex: + if (publicType.basicType == EbtStruct) { + error(loc, "cannot be a structure or array", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (publicType.arraySizes) { + requireProfile(loc, ~EEsProfile, "vertex input arrays"); + profileRequires(loc, ENoProfile, 150, nullptr, "vertex input arrays"); + } + if (publicType.basicType == EbtDouble) + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_vertex_attrib_64bit, "vertex-shader `double` type input"); + if (qualifier.isAuxiliary() || qualifier.isInterpolation() || qualifier.isMemory() || qualifier.invariant) + error(loc, "vertex input cannot be further qualified", "", ""); + break; + case EShLangFragment: + if (publicType.userDef) { + profileRequires(loc, EEsProfile, 300, nullptr, "fragment-shader struct input"); + profileRequires(loc, ~EEsProfile, 150, nullptr, "fragment-shader struct input"); + if (publicType.userDef->containsStructure()) + requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing structure"); + if (publicType.userDef->containsArray()) + requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing an array"); + } + break; + case EShLangCompute: + if (! symbolTable.atBuiltInLevel()) + error(loc, "global storage input qualifier cannot be used in a compute shader", "in", ""); + break; +#ifndef GLSLANG_WEB + case EShLangTessControl: + if (qualifier.patch) + error(loc, "can only use on output in tessellation-control shader", "patch", ""); + break; +#endif + default: + break; + } + } else { + // qualifier.storage == EvqVaryingOut + switch (language) { + case EShLangVertex: + if (publicType.userDef) { + profileRequires(loc, EEsProfile, 300, nullptr, "vertex-shader struct output"); + profileRequires(loc, ~EEsProfile, 150, nullptr, "vertex-shader struct output"); + if (publicType.userDef->containsStructure()) + requireProfile(loc, ~EEsProfile, "vertex-shader struct output containing structure"); + if (publicType.userDef->containsArray()) + requireProfile(loc, ~EEsProfile, "vertex-shader struct output containing an array"); + } + + break; + case EShLangFragment: + profileRequires(loc, EEsProfile, 300, nullptr, "fragment shader output"); + if (publicType.basicType == EbtStruct) { + error(loc, "cannot be a structure", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (publicType.matrixRows > 0) { + error(loc, "cannot be a matrix", GetStorageQualifierString(qualifier.storage), ""); + return; + } + if (qualifier.isAuxiliary()) + error(loc, "can't use auxiliary qualifier on a fragment output", "centroid/sample/patch", ""); + if (qualifier.isInterpolation()) + error(loc, "can't use interpolation qualifier on a fragment output", "flat/smooth/noperspective", ""); + if (publicType.basicType == EbtDouble || publicType.basicType == EbtInt64 || publicType.basicType == EbtUint64) + error(loc, "cannot contain a double, int64, or uint64", GetStorageQualifierString(qualifier.storage), ""); + break; + + case EShLangCompute: + error(loc, "global storage output qualifier cannot be used in a compute shader", "out", ""); + break; +#ifndef GLSLANG_WEB + case EShLangTessEvaluation: + if (qualifier.patch) + error(loc, "can only use on input in tessellation-evaluation shader", "patch", ""); + break; +#endif + default: + break; + } + } +} + +// +// Merge characteristics of the 'src' qualifier into the 'dst'. +// If there is duplication, issue error messages, unless 'force' +// is specified, which means to just override default settings. +// +// Also, when force is false, it will be assumed that 'src' follows +// 'dst', for the purpose of error checking order for versions +// that require specific orderings of qualifiers. +// +void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, const TQualifier& src, bool force) +{ + // Multiple auxiliary qualifiers (mostly done later by 'individual qualifiers') + if (src.isAuxiliary() && dst.isAuxiliary()) + error(loc, "can only have one auxiliary qualifier (centroid, patch, and sample)", "", ""); + + // Multiple interpolation qualifiers (mostly done later by 'individual qualifiers') + if (src.isInterpolation() && dst.isInterpolation()) + error(loc, "can only have one interpolation qualifier (flat, smooth, noperspective, __explicitInterpAMD)", "", ""); + + // Ordering + if (! force && ((!isEsProfile() && version < 420) || + (isEsProfile() && version < 310)) + && ! extensionTurnedOn(E_GL_ARB_shading_language_420pack)) { + // non-function parameters + if (src.isNoContraction() && (dst.invariant || dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "precise qualifier must appear first", "", ""); + if (src.invariant && (dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "invariant qualifier must appear before interpolation, storage, and precision qualifiers ", "", ""); + else if (src.isInterpolation() && (dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "interpolation qualifiers must appear before storage and precision qualifiers", "", ""); + else if (src.isAuxiliary() && (dst.storage != EvqTemporary || dst.precision != EpqNone)) + error(loc, "Auxiliary qualifiers (centroid, patch, and sample) must appear before storage and precision qualifiers", "", ""); + else if (src.storage != EvqTemporary && (dst.precision != EpqNone)) + error(loc, "precision qualifier must appear as last qualifier", "", ""); + + // function parameters + if (src.isNoContraction() && (dst.storage == EvqConst || dst.storage == EvqIn || dst.storage == EvqOut)) + error(loc, "precise qualifier must appear first", "", ""); + if (src.storage == EvqConst && (dst.storage == EvqIn || dst.storage == EvqOut)) + error(loc, "in/out must appear before const", "", ""); + } + + // Storage qualification + if (dst.storage == EvqTemporary || dst.storage == EvqGlobal) + dst.storage = src.storage; + else if ((dst.storage == EvqIn && src.storage == EvqOut) || + (dst.storage == EvqOut && src.storage == EvqIn)) + dst.storage = EvqInOut; + else if ((dst.storage == EvqIn && src.storage == EvqConst) || + (dst.storage == EvqConst && src.storage == EvqIn)) + dst.storage = EvqConstReadOnly; + else if (src.storage != EvqTemporary && + src.storage != EvqGlobal) + error(loc, "too many storage qualifiers", GetStorageQualifierString(src.storage), ""); + + // Precision qualifiers + if (! force && src.precision != EpqNone && dst.precision != EpqNone) + error(loc, "only one precision qualifier allowed", GetPrecisionQualifierString(src.precision), ""); + if (dst.precision == EpqNone || (force && src.precision != EpqNone)) + dst.precision = src.precision; + +#ifndef GLSLANG_WEB + if (!force && ((src.coherent && (dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.devicecoherent && (dst.coherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.queuefamilycoherent && (dst.coherent || dst.devicecoherent || dst.workgroupcoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.workgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.subgroupcoherent || dst.shadercallcoherent)) || + (src.subgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.shadercallcoherent)) || + (src.shadercallcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent)))) { + error(loc, "only one coherent/devicecoherent/queuefamilycoherent/workgroupcoherent/subgroupcoherent/shadercallcoherent qualifier allowed", + GetPrecisionQualifierString(src.precision), ""); + } +#endif + // Layout qualifiers + mergeObjectLayoutQualifiers(dst, src, false); + + // individual qualifiers + bool repeated = false; + #define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field; + MERGE_SINGLETON(invariant); + MERGE_SINGLETON(centroid); + MERGE_SINGLETON(smooth); + MERGE_SINGLETON(flat); + MERGE_SINGLETON(specConstant); +#ifndef GLSLANG_WEB + MERGE_SINGLETON(noContraction); + MERGE_SINGLETON(nopersp); + MERGE_SINGLETON(explicitInterp); + MERGE_SINGLETON(perPrimitiveNV); + MERGE_SINGLETON(perViewNV); + MERGE_SINGLETON(perTaskNV); + MERGE_SINGLETON(patch); + MERGE_SINGLETON(sample); + MERGE_SINGLETON(coherent); + MERGE_SINGLETON(devicecoherent); + MERGE_SINGLETON(queuefamilycoherent); + MERGE_SINGLETON(workgroupcoherent); + MERGE_SINGLETON(subgroupcoherent); + MERGE_SINGLETON(shadercallcoherent); + MERGE_SINGLETON(nonprivate); + MERGE_SINGLETON(volatil); + MERGE_SINGLETON(restrict); + MERGE_SINGLETON(readonly); + MERGE_SINGLETON(writeonly); + MERGE_SINGLETON(nonUniform); +#endif + + if (repeated) + error(loc, "replicated qualifiers", "", ""); +} + +void TParseContext::setDefaultPrecision(const TSourceLoc& loc, TPublicType& publicType, TPrecisionQualifier qualifier) +{ + TBasicType basicType = publicType.basicType; + + if (basicType == EbtSampler) { + defaultSamplerPrecision[computeSamplerTypeIndex(publicType.sampler)] = qualifier; + + return; // all is well + } + + if (basicType == EbtInt || basicType == EbtFloat) { + if (publicType.isScalar()) { + defaultPrecision[basicType] = qualifier; + if (basicType == EbtInt) { + defaultPrecision[EbtUint] = qualifier; + precisionManager.explicitIntDefaultSeen(); + } else + precisionManager.explicitFloatDefaultSeen(); + + return; // all is well + } + } + + if (basicType == EbtAtomicUint) { + if (qualifier != EpqHigh) + error(loc, "can only apply highp to atomic_uint", "precision", ""); + + return; + } + + error(loc, "cannot apply precision statement to this type; use 'float', 'int' or a sampler type", TType::getBasicString(basicType), ""); +} + +// used to flatten the sampler type space into a single dimension +// correlates with the declaration of defaultSamplerPrecision[] +int TParseContext::computeSamplerTypeIndex(TSampler& sampler) +{ + int arrayIndex = sampler.arrayed ? 1 : 0; + int shadowIndex = sampler.shadow ? 1 : 0; + int externalIndex = sampler.isExternal() ? 1 : 0; + int imageIndex = sampler.isImageClass() ? 1 : 0; + int msIndex = sampler.isMultiSample() ? 1 : 0; + + int flattened = EsdNumDims * (EbtNumTypes * (2 * (2 * (2 * (2 * arrayIndex + msIndex) + imageIndex) + shadowIndex) + + externalIndex) + sampler.type) + sampler.dim; + assert(flattened < maxSamplerIndex); + + return flattened; +} + +TPrecisionQualifier TParseContext::getDefaultPrecision(TPublicType& publicType) +{ + if (publicType.basicType == EbtSampler) + return defaultSamplerPrecision[computeSamplerTypeIndex(publicType.sampler)]; + else + return defaultPrecision[publicType.basicType]; +} + +void TParseContext::precisionQualifierCheck(const TSourceLoc& loc, TBasicType baseType, TQualifier& qualifier) +{ + // Built-in symbols are allowed some ambiguous precisions, to be pinned down + // later by context. + if (! obeyPrecisionQualifiers() || parsingBuiltins) + return; + +#ifndef GLSLANG_WEB + if (baseType == EbtAtomicUint && qualifier.precision != EpqNone && qualifier.precision != EpqHigh) + error(loc, "atomic counters can only be highp", "atomic_uint", ""); +#endif + + if (baseType == EbtFloat || baseType == EbtUint || baseType == EbtInt || baseType == EbtSampler || baseType == EbtAtomicUint) { + if (qualifier.precision == EpqNone) { + if (relaxedErrors()) + warn(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), "substituting 'mediump'"); + else + error(loc, "type requires declaration of default precision qualifier", TType::getBasicString(baseType), ""); + qualifier.precision = EpqMedium; + defaultPrecision[baseType] = EpqMedium; + } + } else if (qualifier.precision != EpqNone) + error(loc, "type cannot have precision qualifier", TType::getBasicString(baseType), ""); +} + +void TParseContext::parameterTypeCheck(const TSourceLoc& loc, TStorageQualifier qualifier, const TType& type) +{ + if ((qualifier == EvqOut || qualifier == EvqInOut) && type.isOpaque()) + error(loc, "samplers and atomic_uints cannot be output parameters", type.getBasicTypeString().c_str(), ""); + if (!parsingBuiltins && type.contains16BitFloat()) + requireFloat16Arithmetic(loc, type.getBasicTypeString().c_str(), "float16 types can only be in uniform block or buffer storage"); + if (!parsingBuiltins && type.contains16BitInt()) + requireInt16Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int16 types can only be in uniform block or buffer storage"); + if (!parsingBuiltins && type.contains8BitInt()) + requireInt8Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int8 types can only be in uniform block or buffer storage"); +} + +bool TParseContext::containsFieldWithBasicType(const TType& type, TBasicType basicType) +{ + if (type.getBasicType() == basicType) + return true; + + if (type.getBasicType() == EbtStruct) { + const TTypeList& structure = *type.getStruct(); + for (unsigned int i = 0; i < structure.size(); ++i) { + if (containsFieldWithBasicType(*structure[i].type, basicType)) + return true; + } + } + + return false; +} + +// +// Do size checking for an array type's size. +// +void TParseContext::arraySizeCheck(const TSourceLoc& loc, TIntermTyped* expr, TArraySize& sizePair, const char *sizeType) +{ + bool isConst = false; + sizePair.node = nullptr; + + int size = 1; + + TIntermConstantUnion* constant = expr->getAsConstantUnion(); + if (constant) { + // handle true (non-specialization) constant + size = constant->getConstArray()[0].getIConst(); + isConst = true; + } else { + // see if it's a specialization constant instead + if (expr->getQualifier().isSpecConstant()) { + isConst = true; + sizePair.node = expr; + TIntermSymbol* symbol = expr->getAsSymbolNode(); + if (symbol && symbol->getConstArray().size() > 0) + size = symbol->getConstArray()[0].getIConst(); + } else if (expr->getAsUnaryNode() && + expr->getAsUnaryNode()->getOp() == glslang::EOpArrayLength && + expr->getAsUnaryNode()->getOperand()->getType().isCoopMat()) { + isConst = true; + size = 1; + sizePair.node = expr->getAsUnaryNode(); + } + } + + sizePair.size = size; + + if (! isConst || (expr->getBasicType() != EbtInt && expr->getBasicType() != EbtUint)) { + error(loc, sizeType, "", "must be a constant integer expression"); + return; + } + + if (size <= 0) { + error(loc, sizeType, "", "must be a positive integer"); + return; + } +} + +// +// See if this qualifier can be an array. +// +// Returns true if there is an error. +// +bool TParseContext::arrayQualifierError(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (qualifier.storage == EvqConst) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "const array"); + profileRequires(loc, EEsProfile, 300, nullptr, "const array"); + } + + if (qualifier.storage == EvqVaryingIn && language == EShLangVertex) { + requireProfile(loc, ~EEsProfile, "vertex input arrays"); + profileRequires(loc, ENoProfile, 150, nullptr, "vertex input arrays"); + } + + return false; +} + +// +// See if this qualifier and type combination can be an array. +// Assumes arrayQualifierError() was also called to catch the type-invariant tests. +// +// Returns true if there is an error. +// +bool TParseContext::arrayError(const TSourceLoc& loc, const TType& type) +{ + if (type.getQualifier().storage == EvqVaryingOut && language == EShLangVertex) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "vertex-shader array-of-array output"); + else if (type.isStruct()) + requireProfile(loc, ~EEsProfile, "vertex-shader array-of-struct output"); + } + if (type.getQualifier().storage == EvqVaryingIn && language == EShLangFragment) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-array input"); + else if (type.isStruct()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-struct input"); + } + if (type.getQualifier().storage == EvqVaryingOut && language == EShLangFragment) { + if (type.isArrayOfArrays()) + requireProfile(loc, ~EEsProfile, "fragment-shader array-of-array output"); + } + + return false; +} + +// +// Require array to be completely sized +// +void TParseContext::arraySizeRequiredCheck(const TSourceLoc& loc, const TArraySizes& arraySizes) +{ + if (!parsingBuiltins && arraySizes.hasUnsized()) + error(loc, "array size required", "", ""); +} + +void TParseContext::structArrayCheck(const TSourceLoc& /*loc*/, const TType& type) +{ + const TTypeList& structure = *type.getStruct(); + for (int m = 0; m < (int)structure.size(); ++m) { + const TType& member = *structure[m].type; + if (member.isArray()) + arraySizeRequiredCheck(structure[m].loc, *member.getArraySizes()); + } +} + +void TParseContext::arraySizesCheck(const TSourceLoc& loc, const TQualifier& qualifier, TArraySizes* arraySizes, + const TIntermTyped* initializer, bool lastMember) +{ + assert(arraySizes); + + // always allow special built-in ins/outs sized to topologies + if (parsingBuiltins) + return; + + // initializer must be a sized array, in which case + // allow the initializer to set any unknown array sizes + if (initializer != nullptr) { + if (initializer->getType().isUnsizedArray()) + error(loc, "array initializer must be sized", "[]", ""); + return; + } + + // No environment allows any non-outer-dimension to be implicitly sized + if (arraySizes->isInnerUnsized()) { + error(loc, "only outermost dimension of an array of arrays can be implicitly sized", "[]", ""); + arraySizes->clearInnerUnsized(); + } + + if (arraySizes->isInnerSpecialization() && + (qualifier.storage != EvqTemporary && qualifier.storage != EvqGlobal && qualifier.storage != EvqShared && qualifier.storage != EvqConst)) + error(loc, "only outermost dimension of an array of arrays can be a specialization constant", "[]", ""); + +#ifndef GLSLANG_WEB + + // desktop always allows outer-dimension-unsized variable arrays, + if (!isEsProfile()) + return; + + // for ES, if size isn't coming from an initializer, it has to be explicitly declared now, + // with very few exceptions + + // implicitly-sized io exceptions: + switch (language) { + case EShLangGeometry: + if (qualifier.storage == EvqVaryingIn) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_geometry_shader, AEP_geometry_shader)) + return; + break; + case EShLangTessControl: + if ( qualifier.storage == EvqVaryingIn || + (qualifier.storage == EvqVaryingOut && ! qualifier.isPatch())) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader)) + return; + break; + case EShLangTessEvaluation: + if ((qualifier.storage == EvqVaryingIn && ! qualifier.isPatch()) || + qualifier.storage == EvqVaryingOut) + if ((isEsProfile() && version >= 320) || + extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader)) + return; + break; + case EShLangMeshNV: + if (qualifier.storage == EvqVaryingOut) + if ((isEsProfile() && version >= 320) || + extensionTurnedOn(E_GL_NV_mesh_shader)) + return; + break; + default: + break; + } + +#endif + + // last member of ssbo block exception: + if (qualifier.storage == EvqBuffer && lastMember) + return; + + arraySizeRequiredCheck(loc, *arraySizes); +} + +void TParseContext::arrayOfArrayVersionCheck(const TSourceLoc& loc, const TArraySizes* sizes) +{ + if (sizes == nullptr || sizes->getNumDims() == 1) + return; + + const char* feature = "arrays of arrays"; + + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, feature); +} + +// +// Do all the semantic checking for declaring or redeclaring an array, with and +// without a size, and make the right changes to the symbol table. +// +void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifier, const TType& type, TSymbol*& symbol) +{ + if (symbol == nullptr) { + bool currentScope; + symbol = symbolTable.find(identifier, nullptr, ¤tScope); + + if (symbol && builtInName(identifier) && ! symbolTable.atBuiltInLevel()) { + // bad shader (errors already reported) trying to redeclare a built-in name as an array + symbol = nullptr; + return; + } + if (symbol == nullptr || ! currentScope) { + // + // Successfully process a new definition. + // (Redeclarations have to take place at the same scope; otherwise they are hiding declarations) + // + symbol = new TVariable(&identifier, type); + symbolTable.insert(*symbol); + if (symbolTable.atGlobalLevel()) + trackLinkage(*symbol); + +#ifndef GLSLANG_WEB + if (! symbolTable.atBuiltInLevel()) { + if (isIoResizeArray(type)) { + ioArraySymbolResizeList.push_back(symbol); + checkIoArraysConsistency(loc, true); + } else + fixIoArraySize(loc, symbol->getWritableType()); + } +#endif + + return; + } + if (symbol->getAsAnonMember()) { + error(loc, "cannot redeclare a user-block member array", identifier.c_str(), ""); + symbol = nullptr; + return; + } + } + + // + // Process a redeclaration. + // + + if (symbol == nullptr) { + error(loc, "array variable name expected", identifier.c_str(), ""); + return; + } + + // redeclareBuiltinVariable() should have already done the copyUp() + TType& existingType = symbol->getWritableType(); + + if (! existingType.isArray()) { + error(loc, "redeclaring non-array as array", identifier.c_str(), ""); + return; + } + + if (! existingType.sameElementType(type)) { + error(loc, "redeclaration of array with a different element type", identifier.c_str(), ""); + return; + } + + if (! existingType.sameInnerArrayness(type)) { + error(loc, "redeclaration of array with a different array dimensions or sizes", identifier.c_str(), ""); + return; + } + +#ifndef GLSLANG_WEB + if (existingType.isSizedArray()) { + // be more leniant for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size + if (! (isIoResizeArray(type) && existingType.getOuterArraySize() == type.getOuterArraySize())) + error(loc, "redeclaration of array with size", identifier.c_str(), ""); + return; + } + + arrayLimitCheck(loc, identifier, type.getOuterArraySize()); + + existingType.updateArraySizes(type); + + if (isIoResizeArray(type)) + checkIoArraysConsistency(loc); +#endif +} + +#ifndef GLSLANG_WEB + +// Policy and error check for needing a runtime sized array. +void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermTyped& base) +{ + // runtime length implies runtime sizeable, so no problem + if (isRuntimeLength(base)) + return; + + if (base.getType().getQualifier().builtIn == EbvSampleMask) + return; + + // Check for last member of a bufferreference type, which is runtime sizeable + // but doesn't support runtime length + if (base.getType().getQualifier().storage == EvqBuffer) { + const TIntermBinary* binary = base.getAsBinaryNode(); + if (binary != nullptr && + binary->getOp() == EOpIndexDirectStruct && + binary->getLeft()->isReference()) { + + const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + const int memberCount = (int)binary->getLeft()->getType().getReferentType()->getStruct()->size(); + if (index == memberCount - 1) + return; + } + } + + // check for additional things allowed by GL_EXT_nonuniform_qualifier + if (base.getBasicType() == EbtSampler || base.getBasicType() == EbtAccStruct || base.getBasicType() == EbtRayQuery || + (base.getBasicType() == EbtBlock && base.getType().getQualifier().isUniformOrBuffer())) + requireExtensions(loc, 1, &E_GL_EXT_nonuniform_qualifier, "variable index"); + else + error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); +} + +// Policy decision for whether a run-time .length() is allowed. +bool TParseContext::isRuntimeLength(const TIntermTyped& base) const +{ + if (base.getType().getQualifier().storage == EvqBuffer) { + // in a buffer block + const TIntermBinary* binary = base.getAsBinaryNode(); + if (binary != nullptr && binary->getOp() == EOpIndexDirectStruct) { + // is it the last member? + const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + + if (binary->getLeft()->isReference()) + return false; + + const int memberCount = (int)binary->getLeft()->getType().getStruct()->size(); + if (index == memberCount - 1) + return true; + } + } + + return false; +} + +// Check if mesh perviewNV attributes have a view dimension +// and resize it to gl_MaxMeshViewCountNV when implicitly sized. +void TParseContext::checkAndResizeMeshViewDim(const TSourceLoc& loc, TType& type, bool isBlockMember) +{ + // see if member is a per-view attribute + if (!type.getQualifier().isPerView()) + return; + + if ((isBlockMember && type.isArray()) || (!isBlockMember && type.isArrayOfArrays())) { + // since we don't have the maxMeshViewCountNV set during parsing builtins, we hardcode the value. + int maxViewCount = parsingBuiltins ? 4 : resources.maxMeshViewCountNV; + // For block members, outermost array dimension is the view dimension. + // For non-block members, outermost array dimension is the vertex/primitive dimension + // and 2nd outermost is the view dimension. + int viewDim = isBlockMember ? 0 : 1; + int viewDimSize = type.getArraySizes()->getDimSize(viewDim); + + if (viewDimSize != UnsizedArraySize && viewDimSize != maxViewCount) + error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", ""); + else if (viewDimSize == UnsizedArraySize) + type.getArraySizes()->setDimSize(viewDim, maxViewCount); + } + else { + error(loc, "requires a view array dimension", "perviewNV", ""); + } +} + +#endif // GLSLANG_WEB + +// Returns true if the first argument to the #line directive is the line number for the next line. +// +// Desktop, pre-version 3.30: "After processing this directive +// (including its new-line), the implementation will behave as if it is compiling at line number line+1 and +// source string number source-string-number." +// +// Desktop, version 3.30 and later, and ES: "After processing this directive +// (including its new-line), the implementation will behave as if it is compiling at line number line and +// source string number source-string-number. +bool TParseContext::lineDirectiveShouldSetNextLine() const +{ + return isEsProfile() || version >= 330; +} + +// +// Enforce non-initializer type/qualifier rules. +// +void TParseContext::nonInitConstCheck(const TSourceLoc& loc, TString& identifier, TType& type) +{ + // + // Make the qualifier make sense, given that there is not an initializer. + // + if (type.getQualifier().storage == EvqConst || + type.getQualifier().storage == EvqConstReadOnly) { + type.getQualifier().makeTemporary(); + error(loc, "variables with qualifier 'const' must be initialized", identifier.c_str(), ""); + } +} + +// +// See if the identifier is a built-in symbol that can be redeclared, and if so, +// copy the symbol table's read-only built-in variable to the current +// global level, where it can be modified based on the passed in type. +// +// Returns nullptr if no redeclaration took place; meaning a normal declaration still +// needs to occur for it, not necessarily an error. +// +// Returns a redeclared and type-modified variable if a redeclarated occurred. +// +TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TString& identifier, + const TQualifier& qualifier, const TShaderQualifiers& publicType) +{ +#ifndef GLSLANG_WEB + if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel()) + return nullptr; + + bool nonEsRedecls = (!isEsProfile() && (version >= 130 || identifier == "gl_TexCoord")); + bool esRedecls = (isEsProfile() && + (version >= 320 || extensionsTurnedOn(Num_AEP_shader_io_blocks, AEP_shader_io_blocks))); + if (! esRedecls && ! nonEsRedecls) + return nullptr; + + // Special case when using GL_ARB_separate_shader_objects + bool ssoPre150 = false; // means the only reason this variable is redeclared is due to this combination + if (!isEsProfile() && version <= 140 && extensionTurnedOn(E_GL_ARB_separate_shader_objects)) { + if (identifier == "gl_Position" || + identifier == "gl_PointSize" || + identifier == "gl_ClipVertex" || + identifier == "gl_FogFragCoord") + ssoPre150 = true; + } + + // Potentially redeclaring a built-in variable... + + if (ssoPre150 || + (identifier == "gl_FragDepth" && ((nonEsRedecls && version >= 420) || esRedecls)) || + (identifier == "gl_FragCoord" && ((nonEsRedecls && version >= 150) || esRedecls)) || + identifier == "gl_ClipDistance" || + identifier == "gl_CullDistance" || + identifier == "gl_ShadingRateEXT" || + identifier == "gl_PrimitiveShadingRateEXT" || + identifier == "gl_FrontColor" || + identifier == "gl_BackColor" || + identifier == "gl_FrontSecondaryColor" || + identifier == "gl_BackSecondaryColor" || + identifier == "gl_SecondaryColor" || + (identifier == "gl_Color" && language == EShLangFragment) || + (identifier == "gl_FragStencilRefARB" && (nonEsRedecls && version >= 140) + && language == EShLangFragment) || + identifier == "gl_SampleMask" || + identifier == "gl_Layer" || + identifier == "gl_PrimitiveIndicesNV" || + identifier == "gl_TexCoord") { + + // Find the existing symbol, if any. + bool builtIn; + TSymbol* symbol = symbolTable.find(identifier, &builtIn); + + // If the symbol was not found, this must be a version/profile/stage + // that doesn't have it. + if (! symbol) + return nullptr; + + // If it wasn't at a built-in level, then it's already been redeclared; + // that is, this is a redeclaration of a redeclaration; reuse that initial + // redeclaration. Otherwise, make the new one. + if (builtIn) + makeEditable(symbol); + + // Now, modify the type of the copy, as per the type of the current redeclaration. + + TQualifier& symbolQualifier = symbol->getWritableType().getQualifier(); + if (ssoPre150) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot redeclare after use", identifier.c_str(), ""); + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.isMemory() || qualifier.isAuxiliary() || (language == EShLangVertex && qualifier.storage != EvqVaryingOut) || + (language == EShLangFragment && qualifier.storage != EvqVaryingIn)) + error(loc, "cannot change storage, memory, or auxiliary qualification of", "redeclaration", symbol->getName().c_str()); + if (! qualifier.smooth) + error(loc, "cannot change interpolation qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_FrontColor" || + identifier == "gl_BackColor" || + identifier == "gl_FrontSecondaryColor" || + identifier == "gl_BackSecondaryColor" || + identifier == "gl_SecondaryColor" || + identifier == "gl_Color") { + symbolQualifier.flat = qualifier.flat; + symbolQualifier.smooth = qualifier.smooth; + symbolQualifier.nopersp = qualifier.nopersp; + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.isMemory() || qualifier.isAuxiliary() || symbol->getType().getQualifier().storage != qualifier.storage) + error(loc, "cannot change storage, memory, or auxiliary qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_TexCoord" || + identifier == "gl_ClipDistance" || + identifier == "gl_CullDistance") { + if (qualifier.hasLayout() || qualifier.isMemory() || qualifier.isAuxiliary() || + qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + symbolQualifier.storage != qualifier.storage) + error(loc, "cannot change qualification of", "redeclaration", symbol->getName().c_str()); + } else if (identifier == "gl_FragCoord") { + if (intermediate.inIoAccessed("gl_FragCoord")) + error(loc, "cannot redeclare after use", "gl_FragCoord", ""); + if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + qualifier.isMemory() || qualifier.isAuxiliary()) + error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingIn) + error(loc, "cannot change input storage qualification of", "redeclaration", symbol->getName().c_str()); + if (! builtIn && (publicType.pixelCenterInteger != intermediate.getPixelCenterInteger() || + publicType.originUpperLeft != intermediate.getOriginUpperLeft())) + error(loc, "cannot redeclare with different qualification:", "redeclaration", symbol->getName().c_str()); + if (publicType.pixelCenterInteger) + intermediate.setPixelCenterInteger(); + if (publicType.originUpperLeft) + intermediate.setOriginUpperLeft(); + } else if (identifier == "gl_FragDepth") { + if (qualifier.nopersp != symbolQualifier.nopersp || qualifier.flat != symbolQualifier.flat || + qualifier.isMemory() || qualifier.isAuxiliary()) + error(loc, "can only change layout qualification of", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingOut) + error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str()); + if (publicType.layoutDepth != EldNone) { + if (intermediate.inIoAccessed("gl_FragDepth")) + error(loc, "cannot redeclare after use", "gl_FragDepth", ""); + if (! intermediate.setDepth(publicType.layoutDepth)) + error(loc, "all redeclarations must use the same depth layout on", "redeclaration", symbol->getName().c_str()); + } + } + else if ( + identifier == "gl_PrimitiveIndicesNV" || + identifier == "gl_FragStencilRefARB") { + if (qualifier.hasLayout()) + error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); + if (qualifier.storage != EvqVaryingOut) + error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str()); + } + else if (identifier == "gl_SampleMask") { + if (!publicType.layoutOverrideCoverage) { + error(loc, "redeclaration only allowed for override_coverage layout", "redeclaration", symbol->getName().c_str()); + } + intermediate.setLayoutOverrideCoverage(); + } + else if (identifier == "gl_Layer") { + if (!qualifier.layoutViewportRelative && qualifier.layoutSecondaryViewportRelativeOffset == -2048) + error(loc, "redeclaration only allowed for viewport_relative or secondary_view_offset layout", "redeclaration", symbol->getName().c_str()); + symbolQualifier.layoutViewportRelative = qualifier.layoutViewportRelative; + symbolQualifier.layoutSecondaryViewportRelativeOffset = qualifier.layoutSecondaryViewportRelativeOffset; + } + + // TODO: semantics quality: separate smooth from nothing declared, then use IsInterpolation for several tests above + + return symbol; + } +#endif + + return nullptr; +} + +// +// Either redeclare the requested block, or give an error message why it can't be done. +// +// TODO: functionality: explicitly sizing members of redeclared blocks is not giving them an explicit size +void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName, + const TString* instanceName, TArraySizes* arraySizes) +{ +#ifndef GLSLANG_WEB + const char* feature = "built-in block redeclaration"; + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature); + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + + if (blockName != "gl_PerVertex" && blockName != "gl_PerFragment" && + blockName != "gl_MeshPerVertexNV" && blockName != "gl_MeshPerPrimitiveNV") { + error(loc, "cannot redeclare block: ", "block declaration", blockName.c_str()); + return; + } + + // Redeclaring a built-in block... + + if (instanceName && ! builtInName(*instanceName)) { + error(loc, "cannot redeclare a built-in block with a user name", instanceName->c_str(), ""); + return; + } + + // Blocks with instance names are easy to find, lookup the instance name, + // Anonymous blocks need to be found via a member. + bool builtIn; + TSymbol* block; + if (instanceName) + block = symbolTable.find(*instanceName, &builtIn); + else + block = symbolTable.find(newTypeList.front().type->getFieldName(), &builtIn); + + // If the block was not found, this must be a version/profile/stage + // that doesn't have it, or the instance name is wrong. + const char* errorName = instanceName ? instanceName->c_str() : newTypeList.front().type->getFieldName().c_str(); + if (! block) { + error(loc, "no declaration found for redeclaration", errorName, ""); + return; + } + // Built-in blocks cannot be redeclared more than once, which if happened, + // we'd be finding the already redeclared one here, rather than the built in. + if (! builtIn) { + error(loc, "can only redeclare a built-in block once, and before any use", blockName.c_str(), ""); + return; + } + + // Copy the block to make a writable version, to insert into the block table after editing. + block = symbolTable.copyUpDeferredInsert(block); + + if (block->getType().getBasicType() != EbtBlock) { + error(loc, "cannot redeclare a non block as a block", errorName, ""); + return; + } + + // Fix XFB stuff up, it applies to the order of the redeclaration, not + // the order of the original members. + if (currentBlockQualifier.storage == EvqVaryingOut && globalOutputDefaults.hasXfbBuffer()) { + if (!currentBlockQualifier.hasXfbBuffer()) + currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + if (!currentBlockQualifier.hasStream()) + currentBlockQualifier.layoutStream = globalOutputDefaults.layoutStream; + fixXfbOffsets(currentBlockQualifier, newTypeList); + } + + // Edit and error check the container against the redeclaration + // - remove unused members + // - ensure remaining qualifiers/types match + + TType& type = block->getWritableType(); + + // if gl_PerVertex is redeclared for the purpose of passing through "gl_Position" + // for passthrough purpose, the redeclared block should have the same qualifers as + // the current one + if (currentBlockQualifier.layoutPassthrough) { + type.getQualifier().layoutPassthrough = currentBlockQualifier.layoutPassthrough; + type.getQualifier().storage = currentBlockQualifier.storage; + type.getQualifier().layoutStream = currentBlockQualifier.layoutStream; + type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + } + + TTypeList::iterator member = type.getWritableStruct()->begin(); + size_t numOriginalMembersFound = 0; + while (member != type.getStruct()->end()) { + // look for match + bool found = false; + TTypeList::const_iterator newMember; + TSourceLoc memberLoc; + memberLoc.init(); + for (newMember = newTypeList.begin(); newMember != newTypeList.end(); ++newMember) { + if (member->type->getFieldName() == newMember->type->getFieldName()) { + found = true; + memberLoc = newMember->loc; + break; + } + } + + if (found) { + ++numOriginalMembersFound; + // - ensure match between redeclared members' types + // - check for things that can't be changed + // - update things that can be changed + TType& oldType = *member->type; + const TType& newType = *newMember->type; + if (! newType.sameElementType(oldType)) + error(memberLoc, "cannot redeclare block member with a different type", member->type->getFieldName().c_str(), ""); + if (oldType.isArray() != newType.isArray()) + error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && ! oldType.sameArrayness(newType) && oldType.isSizedArray()) + error(memberLoc, "cannot change array size of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && newType.isArray()) + arrayLimitCheck(loc, member->type->getFieldName(), newType.getOuterArraySize()); + if (oldType.getQualifier().isPerView() && ! newType.getQualifier().isPerView()) + error(memberLoc, "missing perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerView() && newType.getQualifier().isPerView()) + error(memberLoc, "cannot add perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (newType.getQualifier().isPerView()) { + if (oldType.getArraySizes()->getNumDims() != newType.getArraySizes()->getNumDims()) + error(memberLoc, "cannot change arrayness of redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! newType.isUnsizedArray() && newType.getOuterArraySize() != resources.maxMeshViewCountNV) + error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", ""); + else if (newType.getArraySizes()->getNumDims() == 2) { + int innerDimSize = newType.getArraySizes()->getDimSize(1); + arrayLimitCheck(memberLoc, member->type->getFieldName(), innerDimSize); + oldType.getArraySizes()->setDimSize(1, innerDimSize); + } + } + if (oldType.getQualifier().isPerPrimitive() && ! newType.getQualifier().isPerPrimitive()) + error(memberLoc, "missing perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + else if (! oldType.getQualifier().isPerPrimitive() && newType.getQualifier().isPerPrimitive()) + error(memberLoc, "cannot add perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().isMemory()) + error(memberLoc, "cannot add memory qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().hasNonXfbLayout()) + error(memberLoc, "cannot add non-XFB layout to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().patch) + error(memberLoc, "cannot add patch to redeclared block member", member->type->getFieldName().c_str(), ""); + if (newType.getQualifier().hasXfbBuffer() && + newType.getQualifier().layoutXfbBuffer != currentBlockQualifier.layoutXfbBuffer) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); + if (newType.getQualifier().hasStream() && + newType.getQualifier().layoutStream != currentBlockQualifier.layoutStream) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_stream", ""); + oldType.getQualifier().centroid = newType.getQualifier().centroid; + oldType.getQualifier().sample = newType.getQualifier().sample; + oldType.getQualifier().invariant = newType.getQualifier().invariant; + oldType.getQualifier().noContraction = newType.getQualifier().noContraction; + oldType.getQualifier().smooth = newType.getQualifier().smooth; + oldType.getQualifier().flat = newType.getQualifier().flat; + oldType.getQualifier().nopersp = newType.getQualifier().nopersp; + oldType.getQualifier().layoutXfbOffset = newType.getQualifier().layoutXfbOffset; + oldType.getQualifier().layoutXfbBuffer = newType.getQualifier().layoutXfbBuffer; + oldType.getQualifier().layoutXfbStride = newType.getQualifier().layoutXfbStride; + if (oldType.getQualifier().layoutXfbOffset != TQualifier::layoutXfbBufferEnd) { + // If any member has an xfb_offset, then the block's xfb_buffer inherents current xfb_buffer, + // and for xfb processing, the member needs it as well, along with xfb_stride. + type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + oldType.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; + } + if (oldType.isUnsizedArray() && newType.isSizedArray()) + oldType.changeOuterArraySize(newType.getOuterArraySize()); + + // check and process the member's type, which will include managing xfb information + layoutTypeCheck(loc, oldType); + + // go to next member + ++member; + } else { + // For missing members of anonymous blocks that have been redeclared, + // hide the original (shared) declaration. + // Instance-named blocks can just have the member removed. + if (instanceName) + member = type.getWritableStruct()->erase(member); + else { + member->type->hideMember(); + ++member; + } + } + } + + if (spvVersion.vulkan > 0) { + // ...then streams apply to built-in blocks, instead of them being only on stream 0 + type.getQualifier().layoutStream = currentBlockQualifier.layoutStream; + } + + if (numOriginalMembersFound < newTypeList.size()) + error(loc, "block redeclaration has extra members", blockName.c_str(), ""); + if (type.isArray() != (arraySizes != nullptr) || + (type.isArray() && arraySizes != nullptr && type.getArraySizes()->getNumDims() != arraySizes->getNumDims())) + error(loc, "cannot change arrayness of redeclared block", blockName.c_str(), ""); + else if (type.isArray()) { + // At this point, we know both are arrays and both have the same number of dimensions. + + // It is okay for a built-in block redeclaration to be unsized, and keep the size of the + // original block declaration. + if (!arraySizes->isSized() && type.isSizedArray()) + arraySizes->changeOuterSize(type.getOuterArraySize()); + + // And, okay to be giving a size to the array, by the redeclaration + if (!type.isSizedArray() && arraySizes->isSized()) + type.changeOuterArraySize(arraySizes->getOuterSize()); + + // Now, they must match in all dimensions. + if (type.isSizedArray() && *type.getArraySizes() != *arraySizes) + error(loc, "cannot change array size of redeclared block", blockName.c_str(), ""); + } + + symbolTable.insert(*block); + + // Check for general layout qualifier errors + layoutObjectCheck(loc, *block); + + // Tracking for implicit sizing of array + if (isIoResizeArray(block->getType())) { + ioArraySymbolResizeList.push_back(block); + checkIoArraysConsistency(loc, true); + } else if (block->getType().isArray()) + fixIoArraySize(loc, block->getWritableType()); + + // Save it in the AST for linker use. + trackLinkage(*block); +#endif // GLSLANG_WEB +} + +void TParseContext::paramCheckFixStorage(const TSourceLoc& loc, const TStorageQualifier& qualifier, TType& type) +{ + switch (qualifier) { + case EvqConst: + case EvqConstReadOnly: + type.getQualifier().storage = EvqConstReadOnly; + break; + case EvqIn: + case EvqOut: + case EvqInOut: + type.getQualifier().storage = qualifier; + break; + case EvqGlobal: + case EvqTemporary: + type.getQualifier().storage = EvqIn; + break; + default: + type.getQualifier().storage = EvqIn; + error(loc, "storage qualifier not allowed on function parameter", GetStorageQualifierString(qualifier), ""); + break; + } +} + +void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& qualifier, TType& type) +{ +#ifndef GLSLANG_WEB + if (qualifier.isMemory()) { + type.getQualifier().volatil = qualifier.volatil; + type.getQualifier().coherent = qualifier.coherent; + type.getQualifier().devicecoherent = qualifier.devicecoherent ; + type.getQualifier().queuefamilycoherent = qualifier.queuefamilycoherent; + type.getQualifier().workgroupcoherent = qualifier.workgroupcoherent; + type.getQualifier().subgroupcoherent = qualifier.subgroupcoherent; + type.getQualifier().shadercallcoherent = qualifier.shadercallcoherent; + type.getQualifier().nonprivate = qualifier.nonprivate; + type.getQualifier().readonly = qualifier.readonly; + type.getQualifier().writeonly = qualifier.writeonly; + type.getQualifier().restrict = qualifier.restrict; + } +#endif + + if (qualifier.isAuxiliary() || + qualifier.isInterpolation()) + error(loc, "cannot use auxiliary or interpolation qualifiers on a function parameter", "", ""); + if (qualifier.hasLayout()) + error(loc, "cannot use layout qualifiers on a function parameter", "", ""); + if (qualifier.invariant) + error(loc, "cannot use invariant qualifier on a function parameter", "", ""); + if (qualifier.isNoContraction()) { + if (qualifier.isParamOutput()) + type.getQualifier().setNoContraction(); + else + warn(loc, "qualifier has no effect on non-output parameters", "precise", ""); + } + if (qualifier.isNonUniform()) + type.getQualifier().nonUniform = qualifier.nonUniform; + + paramCheckFixStorage(loc, qualifier.storage, type); +} + +void TParseContext::nestedBlockCheck(const TSourceLoc& loc) +{ + if (structNestingLevel > 0 || blockNestingLevel > 0) + error(loc, "cannot nest a block definition inside a structure or block", "", ""); + ++blockNestingLevel; +} + +void TParseContext::nestedStructCheck(const TSourceLoc& loc) +{ + if (structNestingLevel > 0 || blockNestingLevel > 0) + error(loc, "cannot nest a structure definition inside a structure or block", "", ""); + ++structNestingLevel; +} + +void TParseContext::arrayObjectCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + // Some versions don't allow comparing arrays or structures containing arrays + if (type.containsArray()) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, op); + profileRequires(loc, EEsProfile, 300, nullptr, op); + } +} + +void TParseContext::opaqueCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (containsFieldWithBasicType(type, EbtSampler)) + error(loc, "can't use with samplers or structs containing samplers", op, ""); +} + +void TParseContext::referenceCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ +#ifndef GLSLANG_WEB + if (containsFieldWithBasicType(type, EbtReference)) + error(loc, "can't use with reference types", op, ""); +#endif +} + +void TParseContext::storage16BitAssignmentCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ +#ifndef GLSLANG_WEB + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtFloat16)) + requireFloat16Arithmetic(loc, op, "can't use with structs containing float16"); + + if (type.isArray() && type.getBasicType() == EbtFloat16) + requireFloat16Arithmetic(loc, op, "can't use with arrays containing float16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtInt16)) + requireInt16Arithmetic(loc, op, "can't use with structs containing int16"); + + if (type.isArray() && type.getBasicType() == EbtInt16) + requireInt16Arithmetic(loc, op, "can't use with arrays containing int16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtUint16)) + requireInt16Arithmetic(loc, op, "can't use with structs containing uint16"); + + if (type.isArray() && type.getBasicType() == EbtUint16) + requireInt16Arithmetic(loc, op, "can't use with arrays containing uint16"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtInt8)) + requireInt8Arithmetic(loc, op, "can't use with structs containing int8"); + + if (type.isArray() && type.getBasicType() == EbtInt8) + requireInt8Arithmetic(loc, op, "can't use with arrays containing int8"); + + if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtUint8)) + requireInt8Arithmetic(loc, op, "can't use with structs containing uint8"); + + if (type.isArray() && type.getBasicType() == EbtUint8) + requireInt8Arithmetic(loc, op, "can't use with arrays containing uint8"); +#endif +} + +void TParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) +{ + if (type.containsSpecializationSize()) + error(loc, "can't use with types containing arrays sized with a specialization constant", op, ""); +} + +void TParseContext::structTypeCheck(const TSourceLoc& /*loc*/, TPublicType& publicType) +{ + const TTypeList& typeList = *publicType.userDef->getStruct(); + + // fix and check for member storage qualifiers and types that don't belong within a structure + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (memberQualifier.isAuxiliary() || + memberQualifier.isInterpolation() || + (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal)) + error(memberLoc, "cannot use storage or interpolation qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.isMemory()) + error(memberLoc, "cannot use memory qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.hasLayout()) { + error(memberLoc, "cannot use layout qualifiers on structure members", typeList[member].type->getFieldName().c_str(), ""); + memberQualifier.clearLayout(); + } + if (memberQualifier.invariant) + error(memberLoc, "cannot use invariant qualifier on structure members", typeList[member].type->getFieldName().c_str(), ""); + } +} + +// +// See if this loop satisfies the limitations for ES 2.0 (version 100) for loops in Appendex A: +// +// "The loop index has type int or float. +// +// "The for statement has the form: +// for ( init-declaration ; condition ; expression ) +// init-declaration has the form: type-specifier identifier = constant-expression +// condition has the form: loop-index relational_operator constant-expression +// where relational_operator is one of: > >= < <= == or != +// expression [sic] has one of the following forms: +// loop-index++ +// loop-index-- +// loop-index += constant-expression +// loop-index -= constant-expression +// +// The body is handled in an AST traversal. +// +void TParseContext::inductiveLoopCheck(const TSourceLoc& loc, TIntermNode* init, TIntermLoop* loop) +{ +#ifndef GLSLANG_WEB + // loop index init must exist and be a declaration, which shows up in the AST as an aggregate of size 1 of the declaration + bool badInit = false; + if (! init || ! init->getAsAggregate() || init->getAsAggregate()->getSequence().size() != 1) + badInit = true; + TIntermBinary* binaryInit = 0; + if (! badInit) { + // get the declaration assignment + binaryInit = init->getAsAggregate()->getSequence()[0]->getAsBinaryNode(); + if (! binaryInit) + badInit = true; + } + if (badInit) { + error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", ""); + return; + } + + // loop index must be type int or float + if (! binaryInit->getType().isScalar() || (binaryInit->getBasicType() != EbtInt && binaryInit->getBasicType() != EbtFloat)) { + error(loc, "inductive loop requires a scalar 'int' or 'float' loop index", "limitations", ""); + return; + } + + // init is the form "loop-index = constant" + if (binaryInit->getOp() != EOpAssign || ! binaryInit->getLeft()->getAsSymbolNode() || ! binaryInit->getRight()->getAsConstantUnion()) { + error(loc, "inductive-loop init-declaration requires the form \"type-specifier loop-index = constant-expression\"", "limitations", ""); + return; + } + + // get the unique id of the loop index + int loopIndex = binaryInit->getLeft()->getAsSymbolNode()->getId(); + inductiveLoopIds.insert(loopIndex); + + // condition's form must be "loop-index relational-operator constant-expression" + bool badCond = ! loop->getTest(); + if (! badCond) { + TIntermBinary* binaryCond = loop->getTest()->getAsBinaryNode(); + badCond = ! binaryCond; + if (! badCond) { + switch (binaryCond->getOp()) { + case EOpGreaterThan: + case EOpGreaterThanEqual: + case EOpLessThan: + case EOpLessThanEqual: + case EOpEqual: + case EOpNotEqual: + break; + default: + badCond = true; + } + } + if (binaryCond && (! binaryCond->getLeft()->getAsSymbolNode() || + binaryCond->getLeft()->getAsSymbolNode()->getId() != loopIndex || + ! binaryCond->getRight()->getAsConstantUnion())) + badCond = true; + } + if (badCond) { + error(loc, "inductive-loop condition requires the form \"loop-index constant-expression\"", "limitations", ""); + return; + } + + // loop-index++ + // loop-index-- + // loop-index += constant-expression + // loop-index -= constant-expression + bool badTerminal = ! loop->getTerminal(); + if (! badTerminal) { + TIntermUnary* unaryTerminal = loop->getTerminal()->getAsUnaryNode(); + TIntermBinary* binaryTerminal = loop->getTerminal()->getAsBinaryNode(); + if (unaryTerminal || binaryTerminal) { + switch(loop->getTerminal()->getAsOperator()->getOp()) { + case EOpPostDecrement: + case EOpPostIncrement: + case EOpAddAssign: + case EOpSubAssign: + break; + default: + badTerminal = true; + } + } else + badTerminal = true; + if (binaryTerminal && (! binaryTerminal->getLeft()->getAsSymbolNode() || + binaryTerminal->getLeft()->getAsSymbolNode()->getId() != loopIndex || + ! binaryTerminal->getRight()->getAsConstantUnion())) + badTerminal = true; + if (unaryTerminal && (! unaryTerminal->getOperand()->getAsSymbolNode() || + unaryTerminal->getOperand()->getAsSymbolNode()->getId() != loopIndex)) + badTerminal = true; + } + if (badTerminal) { + error(loc, "inductive-loop termination requires the form \"loop-index++, loop-index--, loop-index += constant-expression, or loop-index -= constant-expression\"", "limitations", ""); + return; + } + + // the body + inductiveLoopBodyCheck(loop->getBody(), loopIndex, symbolTable); +#endif +} + +#ifndef GLSLANG_WEB +// Do limit checks for built-in arrays. +void TParseContext::arrayLimitCheck(const TSourceLoc& loc, const TString& identifier, int size) +{ + if (identifier.compare("gl_TexCoord") == 0) + limitCheck(loc, size, "gl_MaxTextureCoords", "gl_TexCoord array size"); + else if (identifier.compare("gl_ClipDistance") == 0) + limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistance array size"); + else if (identifier.compare("gl_CullDistance") == 0) + limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistance array size"); + else if (identifier.compare("gl_ClipDistancePerViewNV") == 0) + limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistancePerViewNV array size"); + else if (identifier.compare("gl_CullDistancePerViewNV") == 0) + limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistancePerViewNV array size"); +} +#endif // GLSLANG_WEB + +// See if the provided value is less than or equal to the symbol indicated by limit, +// which should be a constant in the symbol table. +void TParseContext::limitCheck(const TSourceLoc& loc, int value, const char* limit, const char* feature) +{ + TSymbol* symbol = symbolTable.find(limit); + assert(symbol->getAsVariable()); + const TConstUnionArray& constArray = symbol->getAsVariable()->getConstArray(); + assert(! constArray.empty()); + if (value > constArray[0].getIConst()) + error(loc, "must be less than or equal to", feature, "%s (%d)", limit, constArray[0].getIConst()); +} + +#ifndef GLSLANG_WEB + +// +// Do any additional error checking, etc., once we know the parsing is done. +// +void TParseContext::finish() +{ + TParseContextBase::finish(); + + if (parsingBuiltins) + return; + + // Check on array indexes for ES 2.0 (version 100) limitations. + for (size_t i = 0; i < needsIndexLimitationChecking.size(); ++i) + constantIndexExpressionCheck(needsIndexLimitationChecking[i]); + + // Check for stages that are enabled by extension. + // Can't do this at the beginning, it is chicken and egg to add a stage by + // extension. + // Stage-specific features were correctly tested for already, this is just + // about the stage itself. + switch (language) { + case EShLangGeometry: + if (isEsProfile() && version == 310) + requireExtensions(getCurrentLoc(), Num_AEP_geometry_shader, AEP_geometry_shader, "geometry shaders"); + break; + case EShLangTessControl: + case EShLangTessEvaluation: + if (isEsProfile() && version == 310) + requireExtensions(getCurrentLoc(), Num_AEP_tessellation_shader, AEP_tessellation_shader, "tessellation shaders"); + else if (!isEsProfile() && version < 400) + requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_tessellation_shader, "tessellation shaders"); + break; + case EShLangCompute: + if (!isEsProfile() && version < 430) + requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_compute_shader, "compute shaders"); + break; + case EShLangTaskNV: + requireExtensions(getCurrentLoc(), 1, &E_GL_NV_mesh_shader, "task shaders"); + break; + case EShLangMeshNV: + requireExtensions(getCurrentLoc(), 1, &E_GL_NV_mesh_shader, "mesh shaders"); + break; + default: + break; + } + + // Set default outputs for GL_NV_geometry_shader_passthrough + if (language == EShLangGeometry && extensionTurnedOn(E_SPV_NV_geometry_shader_passthrough)) { + if (intermediate.getOutputPrimitive() == ElgNone) { + switch (intermediate.getInputPrimitive()) { + case ElgPoints: intermediate.setOutputPrimitive(ElgPoints); break; + case ElgLines: intermediate.setOutputPrimitive(ElgLineStrip); break; + case ElgTriangles: intermediate.setOutputPrimitive(ElgTriangleStrip); break; + default: break; + } + } + if (intermediate.getVertices() == TQualifier::layoutNotSet) { + switch (intermediate.getInputPrimitive()) { + case ElgPoints: intermediate.setVertices(1); break; + case ElgLines: intermediate.setVertices(2); break; + case ElgTriangles: intermediate.setVertices(3); break; + default: break; + } + } + } +} +#endif // GLSLANG_WEB + +// +// Layout qualifier stuff. +// + +// Put the id's layout qualification into the public type, for qualifiers not having a number set. +// This is before we know any type information for error checking. +void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id) +{ + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == TQualifier::getLayoutMatrixString(ElmColumnMajor)) { + publicType.qualifier.layoutMatrix = ElmColumnMajor; + return; + } + if (id == TQualifier::getLayoutMatrixString(ElmRowMajor)) { + publicType.qualifier.layoutMatrix = ElmRowMajor; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpPacked)) { + if (spvVersion.spv != 0) + spvRemoved(loc, "packed"); + publicType.qualifier.layoutPacking = ElpPacked; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpShared)) { + if (spvVersion.spv != 0) + spvRemoved(loc, "shared"); + publicType.qualifier.layoutPacking = ElpShared; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpStd140)) { + publicType.qualifier.layoutPacking = ElpStd140; + return; + } +#ifndef GLSLANG_WEB + if (id == TQualifier::getLayoutPackingString(ElpStd430)) { + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "std430"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_shader_storage_buffer_object, "std430"); + profileRequires(loc, EEsProfile, 310, nullptr, "std430"); + publicType.qualifier.layoutPacking = ElpStd430; + return; + } + if (id == TQualifier::getLayoutPackingString(ElpScalar)) { + requireVulkan(loc, "scalar"); + requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "scalar block layout"); + publicType.qualifier.layoutPacking = ElpScalar; + return; + } + // TODO: compile-time performance: may need to stop doing linear searches + for (TLayoutFormat format = (TLayoutFormat)(ElfNone + 1); format < ElfCount; format = (TLayoutFormat)(format + 1)) { + if (id == TQualifier::getLayoutFormatString(format)) { + if ((format > ElfEsFloatGuard && format < ElfFloatGuard) || + (format > ElfEsIntGuard && format < ElfIntGuard) || + (format > ElfEsUintGuard && format < ElfCount)) + requireProfile(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, "image load-store format"); + profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_ARB_shader_image_load_store, "image load store"); + profileRequires(loc, EEsProfile, 310, E_GL_ARB_shader_image_load_store, "image load store"); + publicType.qualifier.layoutFormat = format; + return; + } + } + if (id == "push_constant") { + requireVulkan(loc, "push_constant"); + publicType.qualifier.layoutPushConstant = true; + return; + } + if (id == "buffer_reference") { + requireVulkan(loc, "buffer_reference"); + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference"); + publicType.qualifier.layoutBufferReference = true; + intermediate.setUseStorageBuffer(); + intermediate.setUsePhysicalStorageBuffer(); + return; + } + if (language == EShLangGeometry || language == EShLangTessEvaluation || language == EShLangMeshNV) { + if (id == TQualifier::getGeometryString(ElgTriangles)) { + publicType.shaderQualifiers.geometry = ElgTriangles; + return; + } + if (language == EShLangGeometry || language == EShLangMeshNV) { + if (id == TQualifier::getGeometryString(ElgPoints)) { + publicType.shaderQualifiers.geometry = ElgPoints; + return; + } + if (id == TQualifier::getGeometryString(ElgLines)) { + publicType.shaderQualifiers.geometry = ElgLines; + return; + } + if (language == EShLangGeometry) { + if (id == TQualifier::getGeometryString(ElgLineStrip)) { + publicType.shaderQualifiers.geometry = ElgLineStrip; + return; + } + if (id == TQualifier::getGeometryString(ElgLinesAdjacency)) { + publicType.shaderQualifiers.geometry = ElgLinesAdjacency; + return; + } + if (id == TQualifier::getGeometryString(ElgTrianglesAdjacency)) { + publicType.shaderQualifiers.geometry = ElgTrianglesAdjacency; + return; + } + if (id == TQualifier::getGeometryString(ElgTriangleStrip)) { + publicType.shaderQualifiers.geometry = ElgTriangleStrip; + return; + } + if (id == "passthrough") { + requireExtensions(loc, 1, &E_SPV_NV_geometry_shader_passthrough, "geometry shader passthrough"); + publicType.qualifier.layoutPassthrough = true; + intermediate.setGeoPassthroughEXT(); + return; + } + } + } else { + assert(language == EShLangTessEvaluation); + + // input primitive + if (id == TQualifier::getGeometryString(ElgTriangles)) { + publicType.shaderQualifiers.geometry = ElgTriangles; + return; + } + if (id == TQualifier::getGeometryString(ElgQuads)) { + publicType.shaderQualifiers.geometry = ElgQuads; + return; + } + if (id == TQualifier::getGeometryString(ElgIsolines)) { + publicType.shaderQualifiers.geometry = ElgIsolines; + return; + } + + // vertex spacing + if (id == TQualifier::getVertexSpacingString(EvsEqual)) { + publicType.shaderQualifiers.spacing = EvsEqual; + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalEven)) { + publicType.shaderQualifiers.spacing = EvsFractionalEven; + return; + } + if (id == TQualifier::getVertexSpacingString(EvsFractionalOdd)) { + publicType.shaderQualifiers.spacing = EvsFractionalOdd; + return; + } + + // triangle order + if (id == TQualifier::getVertexOrderString(EvoCw)) { + publicType.shaderQualifiers.order = EvoCw; + return; + } + if (id == TQualifier::getVertexOrderString(EvoCcw)) { + publicType.shaderQualifiers.order = EvoCcw; + return; + } + + // point mode + if (id == "point_mode") { + publicType.shaderQualifiers.pointMode = true; + return; + } + } + } + if (language == EShLangFragment) { + if (id == "origin_upper_left") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "origin_upper_left"); + publicType.shaderQualifiers.originUpperLeft = true; + return; + } + if (id == "pixel_center_integer") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "pixel_center_integer"); + publicType.shaderQualifiers.pixelCenterInteger = true; + return; + } + if (id == "early_fragment_tests") { + profileRequires(loc, ENoProfile | ECoreProfile | ECompatibilityProfile, 420, E_GL_ARB_shader_image_load_store, "early_fragment_tests"); + profileRequires(loc, EEsProfile, 310, nullptr, "early_fragment_tests"); + publicType.shaderQualifiers.earlyFragmentTests = true; + return; + } + if (id == "post_depth_coverage") { + requireExtensions(loc, Num_post_depth_coverageEXTs, post_depth_coverageEXTs, "post depth coverage"); + if (extensionTurnedOn(E_GL_ARB_post_depth_coverage)) { + publicType.shaderQualifiers.earlyFragmentTests = true; + } + publicType.shaderQualifiers.postDepthCoverage = true; + return; + } + for (TLayoutDepth depth = (TLayoutDepth)(EldNone + 1); depth < EldCount; depth = (TLayoutDepth)(depth+1)) { + if (id == TQualifier::getLayoutDepthString(depth)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "depth layout qualifier"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, nullptr, "depth layout qualifier"); + publicType.shaderQualifiers.layoutDepth = depth; + return; + } + } + for (TInterlockOrdering order = (TInterlockOrdering)(EioNone + 1); order < EioCount; order = (TInterlockOrdering)(order+1)) { + if (id == TQualifier::getInterlockOrderingString(order)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "fragment shader interlock layout qualifier"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 450, nullptr, "fragment shader interlock layout qualifier"); + requireExtensions(loc, 1, &E_GL_ARB_fragment_shader_interlock, TQualifier::getInterlockOrderingString(order)); + if (order == EioShadingRateInterlockOrdered || order == EioShadingRateInterlockUnordered) + requireExtensions(loc, 1, &E_GL_NV_shading_rate_image, TQualifier::getInterlockOrderingString(order)); + publicType.shaderQualifiers.interlockOrdering = order; + return; + } + } + if (id.compare(0, 13, "blend_support") == 0) { + bool found = false; + for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { + if (id == TQualifier::getBlendEquationString(be)) { + profileRequires(loc, EEsProfile, 320, E_GL_KHR_blend_equation_advanced, "blend equation"); + profileRequires(loc, ~EEsProfile, 0, E_GL_KHR_blend_equation_advanced, "blend equation"); + intermediate.addBlendEquation(be); + publicType.shaderQualifiers.blendEquation = true; + found = true; + break; + } + } + if (! found) + error(loc, "unknown blend equation", "blend_support", ""); + return; + } + if (id == "override_coverage") { + requireExtensions(loc, 1, &E_GL_NV_sample_mask_override_coverage, "sample mask override coverage"); + publicType.shaderQualifiers.layoutOverrideCoverage = true; + return; + } + } + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry ) { + if (id == "viewport_relative") { + requireExtensions(loc, 1, &E_GL_NV_viewport_array2, "view port array2"); + publicType.qualifier.layoutViewportRelative = true; + return; + } + } else { + if (language == EShLangRayGen || language == EShLangIntersect || + language == EShLangAnyHit || language == EShLangClosestHit || + language == EShLangMiss || language == EShLangCallable) { + if (id == "shaderrecordnv" || id == "shaderrecordext") { + if (id == "shaderrecordnv") { + requireExtensions(loc, 1, &E_GL_NV_ray_tracing, "shader record NV"); + } else { + requireExtensions(loc, 1, &E_GL_EXT_ray_tracing, "shader record EXT"); + } + publicType.qualifier.layoutShaderRecord = true; + return; + } + + } + } + if (language == EShLangCompute) { + if (id.compare(0, 17, "derivative_group_") == 0) { + requireExtensions(loc, 1, &E_GL_NV_compute_shader_derivatives, "compute shader derivatives"); + if (id == "derivative_group_quadsnv") { + publicType.shaderQualifiers.layoutDerivativeGroupQuads = true; + return; + } else if (id == "derivative_group_linearnv") { + publicType.shaderQualifiers.layoutDerivativeGroupLinear = true; + return; + } + } + } + + if (id == "primitive_culling") { + requireExtensions(loc, 1, &E_GL_EXT_ray_flags_primitive_culling, "primitive culling"); + publicType.shaderQualifiers.layoutPrimitiveCulling = true; + return; + } +#endif + + error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), ""); +} + +// Put the id's layout qualifier value into the public type, for qualifiers having a number set. +// This is before we know any type information for error checking. +void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publicType, TString& id, const TIntermTyped* node) +{ + const char* feature = "layout-id value"; + const char* nonLiteralFeature = "non-literal layout-id value"; + + integerCheck(node, feature); + const TIntermConstantUnion* constUnion = node->getAsConstantUnion(); + int value; + bool nonLiteral = false; + if (constUnion) { + value = constUnion->getConstArray()[0].getIConst(); + if (! constUnion->isLiteral()) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, nonLiteralFeature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, nonLiteralFeature); + } + } else { + // grammar should have give out the error message + value = 0; + nonLiteral = true; + } + + if (value < 0) { + error(loc, "cannot be negative", feature, ""); + return; + } + + std::transform(id.begin(), id.end(), id.begin(), ::tolower); + + if (id == "offset") { + // "offset" can be for either + // - uniform offsets + // - atomic_uint offsets + const char* feature = "offset"; + if (spvVersion.spv == 0) { + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature); + const char* exts[2] = { E_GL_ARB_enhanced_layouts, E_GL_ARB_shader_atomic_counters }; + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 420, 2, exts, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + publicType.qualifier.layoutOffset = value; + publicType.qualifier.explicitOffset = true; + if (nonLiteral) + error(loc, "needs a literal integer", "offset", ""); + return; + } else if (id == "align") { + const char* feature = "uniform buffer-member align"; + if (spvVersion.spv == 0) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + } + // "The specified alignment must be a power of 2, or a compile-time error results." + if (! IsPow2(value)) + error(loc, "must be a power of 2", "align", ""); + else + publicType.qualifier.layoutAlign = value; + if (nonLiteral) + error(loc, "needs a literal integer", "align", ""); + return; + } else if (id == "location") { + profileRequires(loc, EEsProfile, 300, nullptr, "location"); + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + // GL_ARB_explicit_uniform_location requires 330 or GL_ARB_explicit_attrib_location we do not need to add it here + profileRequires(loc, ~EEsProfile, 330, 2, exts, "location"); + if ((unsigned int)value >= TQualifier::layoutLocationEnd) + error(loc, "location is too large", id.c_str(), ""); + else + publicType.qualifier.layoutLocation = value; + if (nonLiteral) + error(loc, "needs a literal integer", "location", ""); + return; + } else if (id == "set") { + if ((unsigned int)value >= TQualifier::layoutSetEnd) + error(loc, "set is too large", id.c_str(), ""); + else + publicType.qualifier.layoutSet = value; + if (value != 0) + requireVulkan(loc, "descriptor set"); + if (nonLiteral) + error(loc, "needs a literal integer", "set", ""); + return; + } else if (id == "binding") { +#ifndef GLSLANG_WEB + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, "binding"); + profileRequires(loc, EEsProfile, 310, nullptr, "binding"); +#endif + if ((unsigned int)value >= TQualifier::layoutBindingEnd) + error(loc, "binding is too large", id.c_str(), ""); + else + publicType.qualifier.layoutBinding = value; + if (nonLiteral) + error(loc, "needs a literal integer", "binding", ""); + return; + } + if (id == "constant_id") { + requireSpv(loc, "constant_id"); + if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { + error(loc, "specialization-constant id is too large", id.c_str(), ""); + } else { + publicType.qualifier.layoutSpecConstantId = value; + publicType.qualifier.specConstant = true; + if (! intermediate.addUsedConstantId(value)) + error(loc, "specialization-constant id already used", id.c_str(), ""); + } + if (nonLiteral) + error(loc, "needs a literal integer", "constant_id", ""); + return; + } +#ifndef GLSLANG_WEB + if (id == "component") { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "component"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "component"); + if ((unsigned)value >= TQualifier::layoutComponentEnd) + error(loc, "component is too large", id.c_str(), ""); + else + publicType.qualifier.layoutComponent = value; + if (nonLiteral) + error(loc, "needs a literal integer", "component", ""); + return; + } + if (id.compare(0, 4, "xfb_") == 0) { + // "Any shader making any static use (after preprocessing) of any of these + // *xfb_* qualifiers will cause the shader to be in a transform feedback + // capturing mode and hence responsible for describing the transform feedback + // setup." + intermediate.setXfbMode(); + const char* feature = "transform feedback qualifier"; + requireStage(loc, (EShLanguageMask)(EShLangVertexMask | EShLangGeometryMask | EShLangTessControlMask | EShLangTessEvaluationMask), feature); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, feature); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + if (id == "xfb_buffer") { + // "It is a compile-time error to specify an *xfb_buffer* that is greater than + // the implementation-dependent constant gl_MaxTransformFeedbackBuffers." + if (value >= resources.maxTransformFeedbackBuffers) + error(loc, "buffer is too large:", id.c_str(), "gl_MaxTransformFeedbackBuffers is %d", resources.maxTransformFeedbackBuffers); + if (value >= (int)TQualifier::layoutXfbBufferEnd) + error(loc, "buffer is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbBufferEnd-1); + else + publicType.qualifier.layoutXfbBuffer = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_buffer", ""); + return; + } else if (id == "xfb_offset") { + if (value >= (int)TQualifier::layoutXfbOffsetEnd) + error(loc, "offset is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbOffsetEnd-1); + else + publicType.qualifier.layoutXfbOffset = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_offset", ""); + return; + } else if (id == "xfb_stride") { + // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the + // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." + if (value > 4 * resources.maxTransformFeedbackInterleavedComponents) { + error(loc, "1/4 stride is too large:", id.c_str(), "gl_MaxTransformFeedbackInterleavedComponents is %d", + resources.maxTransformFeedbackInterleavedComponents); + } + if (value >= (int)TQualifier::layoutXfbStrideEnd) + error(loc, "stride is too large:", id.c_str(), "internal max is %d", TQualifier::layoutXfbStrideEnd-1); + else + publicType.qualifier.layoutXfbStride = value; + if (nonLiteral) + error(loc, "needs a literal integer", "xfb_stride", ""); + return; + } + } + if (id == "input_attachment_index") { + requireVulkan(loc, "input_attachment_index"); + if (value >= (int)TQualifier::layoutAttachmentEnd) + error(loc, "attachment index is too large", id.c_str(), ""); + else + publicType.qualifier.layoutAttachment = value; + if (nonLiteral) + error(loc, "needs a literal integer", "input_attachment_index", ""); + return; + } + if (id == "num_views") { + requireExtensions(loc, Num_OVR_multiview_EXTs, OVR_multiview_EXTs, "num_views"); + publicType.shaderQualifiers.numViews = value; + if (nonLiteral) + error(loc, "needs a literal integer", "num_views", ""); + return; + } + if (language == EShLangVertex || + language == EShLangTessControl || + language == EShLangTessEvaluation || + language == EShLangGeometry) { + if (id == "secondary_view_offset") { + requireExtensions(loc, 1, &E_GL_NV_stereo_view_rendering, "stereo view rendering"); + publicType.qualifier.layoutSecondaryViewportRelativeOffset = value; + if (nonLiteral) + error(loc, "needs a literal integer", "secondary_view_offset", ""); + return; + } + } + + if (id == "buffer_reference_align") { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference_align"); + if (! IsPow2(value)) + error(loc, "must be a power of 2", "buffer_reference_align", ""); + else + publicType.qualifier.layoutBufferReferenceAlign = (unsigned int)std::log2(value); + if (nonLiteral) + error(loc, "needs a literal integer", "buffer_reference_align", ""); + return; + } +#endif + + switch (language) { +#ifndef GLSLANG_WEB + case EShLangTessControl: + if (id == "vertices") { + if (value == 0) + error(loc, "must be greater than 0", "vertices", ""); + else + publicType.shaderQualifiers.vertices = value; + if (nonLiteral) + error(loc, "needs a literal integer", "vertices", ""); + return; + } + break; + + case EShLangGeometry: + if (id == "invocations") { + profileRequires(loc, ECompatibilityProfile | ECoreProfile, 400, nullptr, "invocations"); + if (value == 0) + error(loc, "must be at least 1", "invocations", ""); + else + publicType.shaderQualifiers.invocations = value; + if (nonLiteral) + error(loc, "needs a literal integer", "invocations", ""); + return; + } + if (id == "max_vertices") { + publicType.shaderQualifiers.vertices = value; + if (value > resources.maxGeometryOutputVertices) + error(loc, "too large, must be less than gl_MaxGeometryOutputVertices", "max_vertices", ""); + if (nonLiteral) + error(loc, "needs a literal integer", "max_vertices", ""); + return; + } + if (id == "stream") { + requireProfile(loc, ~EEsProfile, "selecting output stream"); + publicType.qualifier.layoutStream = value; + if (value > 0) + intermediate.setMultiStream(); + if (nonLiteral) + error(loc, "needs a literal integer", "stream", ""); + return; + } + break; + + case EShLangFragment: + if (id == "index") { + requireProfile(loc, ECompatibilityProfile | ECoreProfile | EEsProfile, "index layout qualifier on fragment output"); + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ECompatibilityProfile | ECoreProfile, 330, 2, exts, "index layout qualifier on fragment output"); + profileRequires(loc, EEsProfile ,310, E_GL_EXT_blend_func_extended, "index layout qualifier on fragment output"); + // "It is also a compile-time error if a fragment shader sets a layout index to less than 0 or greater than 1." + if (value < 0 || value > 1) { + value = 0; + error(loc, "value must be 0 or 1", "index", ""); + } + + publicType.qualifier.layoutIndex = value; + if (nonLiteral) + error(loc, "needs a literal integer", "index", ""); + return; + } + break; + + case EShLangMeshNV: + if (id == "max_vertices") { + requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "max_vertices"); + publicType.shaderQualifiers.vertices = value; + if (value > resources.maxMeshOutputVerticesNV) + error(loc, "too large, must be less than gl_MaxMeshOutputVerticesNV", "max_vertices", ""); + if (nonLiteral) + error(loc, "needs a literal integer", "max_vertices", ""); + return; + } + if (id == "max_primitives") { + requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "max_primitives"); + publicType.shaderQualifiers.primitives = value; + if (value > resources.maxMeshOutputPrimitivesNV) + error(loc, "too large, must be less than gl_MaxMeshOutputPrimitivesNV", "max_primitives", ""); + if (nonLiteral) + error(loc, "needs a literal integer", "max_primitives", ""); + return; + } + // Fall through + + case EShLangTaskNV: + // Fall through +#endif + case EShLangCompute: + if (id.compare(0, 11, "local_size_") == 0) { +#ifndef GLSLANG_WEB + if (language == EShLangMeshNV || language == EShLangTaskNV) { + requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "gl_WorkGroupSize"); + } else { + profileRequires(loc, EEsProfile, 310, 0, "gl_WorkGroupSize"); + profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_compute_shader, "gl_WorkGroupSize"); + } +#endif + if (nonLiteral) + error(loc, "needs a literal integer", "local_size", ""); + if (id.size() == 12 && value == 0) { + error(loc, "must be at least 1", id.c_str(), ""); + return; + } + if (id == "local_size_x") { + publicType.shaderQualifiers.localSize[0] = value; + publicType.shaderQualifiers.localSizeNotDefault[0] = true; + return; + } + if (id == "local_size_y") { + publicType.shaderQualifiers.localSize[1] = value; + publicType.shaderQualifiers.localSizeNotDefault[1] = true; + return; + } + if (id == "local_size_z") { + publicType.shaderQualifiers.localSize[2] = value; + publicType.shaderQualifiers.localSizeNotDefault[2] = true; + return; + } + if (spvVersion.spv != 0) { + if (id == "local_size_x_id") { + publicType.shaderQualifiers.localSizeSpecId[0] = value; + return; + } + if (id == "local_size_y_id") { + publicType.shaderQualifiers.localSizeSpecId[1] = value; + return; + } + if (id == "local_size_z_id") { + publicType.shaderQualifiers.localSizeSpecId[2] = value; + return; + } + } + } + break; + + default: + break; + } + + error(loc, "there is no such layout identifier for this stage taking an assigned value", id.c_str(), ""); +} + +// Merge any layout qualifier information from src into dst, leaving everything else in dst alone +// +// "More than one layout qualifier may appear in a single declaration. +// Additionally, the same layout-qualifier-name can occur multiple times +// within a layout qualifier or across multiple layout qualifiers in the +// same declaration. When the same layout-qualifier-name occurs +// multiple times, in a single declaration, the last occurrence overrides +// the former occurrence(s). Further, if such a layout-qualifier-name +// will effect subsequent declarations or other observable behavior, it +// is only the last occurrence that will have any effect, behaving as if +// the earlier occurrence(s) within the declaration are not present. +// This is also true for overriding layout-qualifier-names, where one +// overrides the other (e.g., row_major vs. column_major); only the last +// occurrence has any effect." +void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifier& src, bool inheritOnly) +{ + if (src.hasMatrix()) + dst.layoutMatrix = src.layoutMatrix; + if (src.hasPacking()) + dst.layoutPacking = src.layoutPacking; + +#ifndef GLSLANG_WEB + if (src.hasStream()) + dst.layoutStream = src.layoutStream; + if (src.hasFormat()) + dst.layoutFormat = src.layoutFormat; + if (src.hasXfbBuffer()) + dst.layoutXfbBuffer = src.layoutXfbBuffer; + if (src.hasBufferReferenceAlign()) + dst.layoutBufferReferenceAlign = src.layoutBufferReferenceAlign; +#endif + + if (src.hasAlign()) + dst.layoutAlign = src.layoutAlign; + + if (! inheritOnly) { + if (src.hasLocation()) + dst.layoutLocation = src.layoutLocation; + if (src.hasOffset()) + dst.layoutOffset = src.layoutOffset; + if (src.hasSet()) + dst.layoutSet = src.layoutSet; + if (src.layoutBinding != TQualifier::layoutBindingEnd) + dst.layoutBinding = src.layoutBinding; + + if (src.hasSpecConstantId()) + dst.layoutSpecConstantId = src.layoutSpecConstantId; + +#ifndef GLSLANG_WEB + if (src.hasComponent()) + dst.layoutComponent = src.layoutComponent; + if (src.hasIndex()) + dst.layoutIndex = src.layoutIndex; + if (src.hasXfbStride()) + dst.layoutXfbStride = src.layoutXfbStride; + if (src.hasXfbOffset()) + dst.layoutXfbOffset = src.layoutXfbOffset; + if (src.hasAttachment()) + dst.layoutAttachment = src.layoutAttachment; + if (src.layoutPushConstant) + dst.layoutPushConstant = true; + + if (src.layoutBufferReference) + dst.layoutBufferReference = true; + + if (src.layoutPassthrough) + dst.layoutPassthrough = true; + if (src.layoutViewportRelative) + dst.layoutViewportRelative = true; + if (src.layoutSecondaryViewportRelativeOffset != -2048) + dst.layoutSecondaryViewportRelativeOffset = src.layoutSecondaryViewportRelativeOffset; + if (src.layoutShaderRecord) + dst.layoutShaderRecord = true; + if (src.pervertexNV) + dst.pervertexNV = true; +#endif + } +} + +// Do error layout error checking given a full variable/block declaration. +void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symbol) +{ + const TType& type = symbol.getType(); + const TQualifier& qualifier = type.getQualifier(); + + // first, cross check WRT to just the type + layoutTypeCheck(loc, type); + + // now, any remaining error checking based on the object itself + + if (qualifier.hasAnyLocation()) { + switch (qualifier.storage) { + case EvqUniform: + case EvqBuffer: + if (symbol.getAsVariable() == nullptr) + error(loc, "can only be used on variable declaration", "location", ""); + break; + default: + break; + } + } + + // user-variable location check, which are required for SPIR-V in/out: + // - variables have it directly, + // - blocks have it on each member (already enforced), so check first one + if (spvVersion.spv > 0 && !parsingBuiltins && qualifier.builtIn == EbvNone && + !qualifier.hasLocation() && !intermediate.getAutoMapLocations()) { + + switch (qualifier.storage) { + case EvqVaryingIn: + case EvqVaryingOut: + if (!type.getQualifier().isTaskMemory() && + (type.getBasicType() != EbtBlock || + (!(*type.getStruct())[0].type->getQualifier().hasLocation() && + (*type.getStruct())[0].type->getQualifier().builtIn == EbvNone))) + error(loc, "SPIR-V requires location for user input/output", "location", ""); + break; + default: + break; + } + } + + // Check packing and matrix + if (qualifier.hasUniformLayout()) { + switch (qualifier.storage) { + case EvqUniform: + case EvqBuffer: + if (type.getBasicType() != EbtBlock) { + if (qualifier.hasMatrix()) + error(loc, "cannot specify matrix layout on a variable declaration", "layout", ""); + if (qualifier.hasPacking()) + error(loc, "cannot specify packing on a variable declaration", "layout", ""); + // "The offset qualifier can only be used on block members of blocks..." + if (qualifier.hasOffset() && !type.isAtomic()) + error(loc, "cannot specify on a variable declaration", "offset", ""); + // "The align qualifier can only be used on blocks or block members..." + if (qualifier.hasAlign()) + error(loc, "cannot specify on a variable declaration", "align", ""); + if (qualifier.isPushConstant()) + error(loc, "can only specify on a uniform block", "push_constant", ""); + if (qualifier.isShaderRecord()) + error(loc, "can only specify on a buffer block", "shaderRecordNV", ""); + } + break; + default: + // these were already filtered by layoutTypeCheck() (or its callees) + break; + } + } +} + +// "For some blocks declared as arrays, the location can only be applied at the block level: +// When a block is declared as an array where additional locations are needed for each member +// for each block array element, it is a compile-time error to specify locations on the block +// members. That is, when locations would be under specified by applying them on block members, +// they are not allowed on block members. For arrayed interfaces (those generally having an +// extra level of arrayness due to interface expansion), the outer array is stripped before +// applying this rule." +void TParseContext::layoutMemberLocationArrayCheck(const TSourceLoc& loc, bool memberWithLocation, + TArraySizes* arraySizes) +{ + if (memberWithLocation && arraySizes != nullptr) { + if (arraySizes->getNumDims() > (currentBlockQualifier.isArrayedIo(language) ? 1 : 0)) + error(loc, "cannot use in a block array where new locations are needed for each block element", + "location", ""); + } +} + +// Do layout error checking with respect to a type. +void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) +{ + const TQualifier& qualifier = type.getQualifier(); + + // first, intra-layout qualifier-only error checking + layoutQualifierCheck(loc, qualifier); + + // now, error checking combining type and qualifier + + if (qualifier.hasAnyLocation()) { + if (qualifier.hasLocation()) { + if (qualifier.storage == EvqVaryingOut && language == EShLangFragment) { + if (qualifier.layoutLocation >= (unsigned int)resources.maxDrawBuffers) + error(loc, "too large for fragment output", "location", ""); + } + } + if (qualifier.hasComponent()) { + // "It is a compile-time error if this sequence of components gets larger than 3." + if (qualifier.layoutComponent + type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1) > 4) + error(loc, "type overflows the available 4 components", "component", ""); + + // "It is a compile-time error to apply the component qualifier to a matrix, a structure, a block, or an array containing any of these." + if (type.isMatrix() || type.getBasicType() == EbtBlock || type.getBasicType() == EbtStruct) + error(loc, "cannot apply to a matrix, structure, or block", "component", ""); + + // " It is a compile-time error to use component 1 or 3 as the beginning of a double or dvec2." + if (type.getBasicType() == EbtDouble) + if (qualifier.layoutComponent & 1) + error(loc, "doubles cannot start on an odd-numbered component", "component", ""); + } + + switch (qualifier.storage) { + case EvqVaryingIn: + case EvqVaryingOut: + if (type.getBasicType() == EbtBlock) + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "location qualifier on in/out block"); + if (type.getQualifier().isTaskMemory()) + error(loc, "cannot apply to taskNV in/out blocks", "location", ""); + break; + case EvqUniform: + case EvqBuffer: + if (type.getBasicType() == EbtBlock) + error(loc, "cannot apply to uniform or buffer block", "location", ""); + break; +#ifndef GLSLANG_WEB + case EvqPayload: + case EvqPayloadIn: + case EvqHitAttr: + case EvqCallableData: + case EvqCallableDataIn: + break; +#endif + default: + error(loc, "can only apply to uniform, buffer, in, or out storage qualifiers", "location", ""); + break; + } + + bool typeCollision; + int repeated = intermediate.addUsedLocation(qualifier, type, typeCollision); + if (repeated >= 0 && ! typeCollision) + error(loc, "overlapping use of location", "location", "%d", repeated); + // "fragment-shader outputs ... if two variables are placed within the same + // location, they must have the same underlying type (floating-point or integer)" + if (typeCollision && language == EShLangFragment && qualifier.isPipeOutput()) + error(loc, "fragment outputs sharing the same location must be the same basic type", "location", "%d", repeated); + } + +#ifndef GLSLANG_WEB + if (qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()) { + int repeated = intermediate.addXfbBufferOffset(type); + if (repeated >= 0) + error(loc, "overlapping offsets at", "xfb_offset", "offset %d in buffer %d", repeated, qualifier.layoutXfbBuffer); + if (type.isUnsizedArray()) + error(loc, "unsized array", "xfb_offset", "in buffer %d", qualifier.layoutXfbBuffer); + + // "The offset must be a multiple of the size of the first component of the first + // qualified variable or block member, or a compile-time error results. Further, if applied to an aggregate + // containing a double or 64-bit integer, the offset must also be a multiple of 8..." + if ((type.containsBasicType(EbtDouble) || type.containsBasicType(EbtInt64) || type.containsBasicType(EbtUint64)) && + ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 8)) + error(loc, "type contains double or 64-bit integer; xfb_offset must be a multiple of 8", "xfb_offset", ""); + else if ((type.containsBasicType(EbtBool) || type.containsBasicType(EbtFloat) || + type.containsBasicType(EbtInt) || type.containsBasicType(EbtUint)) && + ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 4)) + error(loc, "must be a multiple of size of first component", "xfb_offset", ""); + // ..., if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2..." + else if ((type.contains16BitFloat() || type.containsBasicType(EbtInt16) || type.containsBasicType(EbtUint16)) && + !IsMultipleOfPow2(qualifier.layoutXfbOffset, 2)) + error(loc, "type contains half float or 16-bit integer; xfb_offset must be a multiple of 2", "xfb_offset", ""); + } + if (qualifier.hasXfbStride() && qualifier.hasXfbBuffer()) { + if (! intermediate.setXfbBufferStride(qualifier.layoutXfbBuffer, qualifier.layoutXfbStride)) + error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); + } +#endif + + if (qualifier.hasBinding()) { + // Binding checking, from the spec: + // + // "If the binding point for any uniform or shader storage block instance is less than zero, or greater than or + // equal to the implementation-dependent maximum number of uniform buffer bindings, a compile-time + // error will occur. When the binding identifier is used with a uniform or shader storage block instanced as + // an array of size N, all elements of the array from binding through binding + N - 1 must be within this + // range." + // + if (! type.isOpaque() && type.getBasicType() != EbtBlock) + error(loc, "requires block, or sampler/image, or atomic-counter type", "binding", ""); + if (type.getBasicType() == EbtSampler) { + int lastBinding = qualifier.layoutBinding; + if (type.isArray()) { + if (spvVersion.vulkan > 0) + lastBinding += 1; + else { + if (type.isSizedArray()) + lastBinding += type.getCumulativeArraySize(); + else { + lastBinding += 1; +#ifndef GLSLANG_WEB + if (spvVersion.vulkan == 0) + warn(loc, "assuming binding count of one for compile-time checking of binding numbers for unsized array", "[]", ""); +#endif + } + } + } +#ifndef GLSLANG_WEB + if (spvVersion.vulkan == 0 && lastBinding >= resources.maxCombinedTextureImageUnits) + error(loc, "sampler binding not less than gl_MaxCombinedTextureImageUnits", "binding", type.isArray() ? "(using array)" : ""); +#endif + } + if (type.isAtomic()) { + if (qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { + error(loc, "atomic_uint binding is too large; see gl_MaxAtomicCounterBindings", "binding", ""); + return; + } + } + } else if (!intermediate.getAutoMapBindings()) { + // some types require bindings + + // atomic_uint + if (type.isAtomic()) + error(loc, "layout(binding=X) is required", "atomic_uint", ""); + + // SPIR-V + if (spvVersion.spv > 0) { + if (qualifier.isUniformOrBuffer()) { + if (type.getBasicType() == EbtBlock && !qualifier.isPushConstant() && + !qualifier.isShaderRecord() && + !qualifier.hasAttachment() && + !qualifier.hasBufferReference()) + error(loc, "uniform/buffer blocks require layout(binding=X)", "binding", ""); + else if (spvVersion.vulkan > 0 && type.getBasicType() == EbtSampler) + error(loc, "sampler/texture/image requires layout(binding=X)", "binding", ""); + } + } + } + + // some things can't have arrays of arrays + if (type.isArrayOfArrays()) { + if (spvVersion.vulkan > 0) { + if (type.isOpaque() || (type.getQualifier().isUniformOrBuffer() && type.getBasicType() == EbtBlock)) + warn(loc, "Generating SPIR-V array-of-arrays, but Vulkan only supports single array level for this resource", "[][]", ""); + } + } + + // "The offset qualifier can only be used on block members of blocks..." + if (qualifier.hasOffset()) { + if (type.getBasicType() == EbtBlock) + error(loc, "only applies to block members, not blocks", "offset", ""); + } + + // Image format + if (qualifier.hasFormat()) { + if (! type.isImage()) + error(loc, "only apply to images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + else { + if (type.getSampler().type == EbtFloat && qualifier.getFormat() > ElfFloatGuard) + error(loc, "does not apply to floating point images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + if (type.getSampler().type == EbtInt && (qualifier.getFormat() < ElfFloatGuard || qualifier.getFormat() > ElfIntGuard)) + error(loc, "does not apply to signed integer images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + if (type.getSampler().type == EbtUint && qualifier.getFormat() < ElfIntGuard) + error(loc, "does not apply to unsigned integer images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + + if (isEsProfile()) { + // "Except for image variables qualified with the format qualifiers r32f, r32i, and r32ui, image variables must + // specify either memory qualifier readonly or the memory qualifier writeonly." + if (! (qualifier.getFormat() == ElfR32f || qualifier.getFormat() == ElfR32i || qualifier.getFormat() == ElfR32ui)) { + if (! qualifier.isReadOnly() && ! qualifier.isWriteOnly()) + error(loc, "format requires readonly or writeonly memory qualifier", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + } + } + } + } else if (type.isImage() && ! qualifier.isWriteOnly()) { + const char *explanation = "image variables not declared 'writeonly' and without a format layout qualifier"; + requireProfile(loc, ECoreProfile | ECompatibilityProfile, explanation); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shader_image_load_formatted, explanation); + } + + if (qualifier.isPushConstant() && type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "push_constant", ""); + + if (qualifier.hasBufferReference() && type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "buffer_reference", ""); + + if (qualifier.isShaderRecord() && type.getBasicType() != EbtBlock) + error(loc, "can only be used with a block", "shaderRecordNV", ""); + + // input attachment + if (type.isSubpass()) { + if (! qualifier.hasAttachment()) + error(loc, "requires an input_attachment_index layout qualifier", "subpass", ""); + } else { + if (qualifier.hasAttachment()) + error(loc, "can only be used with a subpass", "input_attachment_index", ""); + } + + // specialization-constant id + if (qualifier.hasSpecConstantId()) { + if (type.getQualifier().storage != EvqConst) + error(loc, "can only be applied to 'const'-qualified scalar", "constant_id", ""); + if (! type.isScalar()) + error(loc, "can only be applied to a scalar", "constant_id", ""); + switch (type.getBasicType()) + { + case EbtInt8: + case EbtUint8: + case EbtInt16: + case EbtUint16: + case EbtInt: + case EbtUint: + case EbtInt64: + case EbtUint64: + case EbtBool: + case EbtFloat: + case EbtDouble: + case EbtFloat16: + break; + default: + error(loc, "cannot be applied to this type", "constant_id", ""); + break; + } + } +} + +// Do layout error checking that can be done within a layout qualifier proper, not needing to know +// if there are blocks, atomic counters, variables, etc. +void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (qualifier.storage == EvqShared && qualifier.hasLayout()) + error(loc, "cannot apply layout qualifiers to a shared variable", "shared", ""); + + // "It is a compile-time error to use *component* without also specifying the location qualifier (order does not matter)." + if (qualifier.hasComponent() && ! qualifier.hasLocation()) + error(loc, "must specify 'location' to use 'component'", "component", ""); + + if (qualifier.hasAnyLocation()) { + + // "As with input layout qualifiers, all shaders except compute shaders + // allow *location* layout qualifiers on output variable declarations, + // output block declarations, and output block member declarations." + + switch (qualifier.storage) { +#ifndef GLSLANG_WEB + case EvqVaryingIn: + { + const char* feature = "location qualifier on input"; + if (isEsProfile() && version < 310) + requireStage(loc, EShLangVertex, feature); + else + requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature); + if (language == EShLangVertex) { + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ~EEsProfile, 330, 2, exts, feature); + profileRequires(loc, EEsProfile, 300, nullptr, feature); + } else { + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + break; + } + case EvqVaryingOut: + { + const char* feature = "location qualifier on output"; + if (isEsProfile() && version < 310) + requireStage(loc, EShLangFragment, feature); + else + requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature); + if (language == EShLangFragment) { + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + profileRequires(loc, ~EEsProfile, 330, 2, exts, feature); + profileRequires(loc, EEsProfile, 300, nullptr, feature); + } else { + profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + } + break; + } +#endif + case EvqUniform: + case EvqBuffer: + { + const char* feature = "location qualifier on uniform or buffer"; + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile | ENoProfile, feature); + profileRequires(loc, ~EEsProfile, 330, E_GL_ARB_explicit_attrib_location, feature); + profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_explicit_uniform_location, feature); + profileRequires(loc, EEsProfile, 310, nullptr, feature); + break; + } + default: + break; + } + if (qualifier.hasIndex()) { + if (qualifier.storage != EvqVaryingOut) + error(loc, "can only be used on an output", "index", ""); + if (! qualifier.hasLocation()) + error(loc, "can only be used with an explicit location", "index", ""); + } + } + + if (qualifier.hasBinding()) { + if (! qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) + error(loc, "requires uniform or buffer storage qualifier", "binding", ""); + } + if (qualifier.hasStream()) { + if (!qualifier.isPipeOutput()) + error(loc, "can only be used on an output", "stream", ""); + } + if (qualifier.hasXfb()) { + if (!qualifier.isPipeOutput()) + error(loc, "can only be used on an output", "xfb layout qualifier", ""); + } + if (qualifier.hasUniformLayout()) { + if (! qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) { + if (qualifier.hasMatrix() || qualifier.hasPacking()) + error(loc, "matrix or packing qualifiers can only be used on a uniform or buffer", "layout", ""); + if (qualifier.hasOffset() || qualifier.hasAlign()) + error(loc, "offset/align can only be used on a uniform or buffer", "layout", ""); + } + } + if (qualifier.isPushConstant()) { + if (qualifier.storage != EvqUniform) + error(loc, "can only be used with a uniform", "push_constant", ""); + if (qualifier.hasSet()) + error(loc, "cannot be used with push_constant", "set", ""); + } + if (qualifier.hasBufferReference()) { + if (qualifier.storage != EvqBuffer) + error(loc, "can only be used with buffer", "buffer_reference", ""); + } + if (qualifier.isShaderRecord()) { + if (qualifier.storage != EvqBuffer) + error(loc, "can only be used with a buffer", "shaderRecordNV", ""); + if (qualifier.hasBinding()) + error(loc, "cannot be used with shaderRecordNV", "binding", ""); + if (qualifier.hasSet()) + error(loc, "cannot be used with shaderRecordNV", "set", ""); + + } + if (qualifier.storage == EvqHitAttr && qualifier.hasLayout()) { + error(loc, "cannot apply layout qualifiers to hitAttributeNV variable", "hitAttributeNV", ""); + } +} + +// For places that can't have shader-level layout qualifiers +void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQualifiers& shaderQualifiers) +{ +#ifndef GLSLANG_WEB + const char* message = "can only apply to a standalone qualifier"; + + if (shaderQualifiers.geometry != ElgNone) + error(loc, message, TQualifier::getGeometryString(shaderQualifiers.geometry), ""); + if (shaderQualifiers.spacing != EvsNone) + error(loc, message, TQualifier::getVertexSpacingString(shaderQualifiers.spacing), ""); + if (shaderQualifiers.order != EvoNone) + error(loc, message, TQualifier::getVertexOrderString(shaderQualifiers.order), ""); + if (shaderQualifiers.pointMode) + error(loc, message, "point_mode", ""); + if (shaderQualifiers.invocations != TQualifier::layoutNotSet) + error(loc, message, "invocations", ""); + for (int i = 0; i < 3; ++i) { + if (shaderQualifiers.localSize[i] > 1) + error(loc, message, "local_size", ""); + if (shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) + error(loc, message, "local_size id", ""); + } + if (shaderQualifiers.vertices != TQualifier::layoutNotSet) { + if (language == EShLangGeometry || language == EShLangMeshNV) + error(loc, message, "max_vertices", ""); + else if (language == EShLangTessControl) + error(loc, message, "vertices", ""); + else + assert(0); + } + if (shaderQualifiers.earlyFragmentTests) + error(loc, message, "early_fragment_tests", ""); + if (shaderQualifiers.postDepthCoverage) + error(loc, message, "post_depth_coverage", ""); + if (shaderQualifiers.primitives != TQualifier::layoutNotSet) { + if (language == EShLangMeshNV) + error(loc, message, "max_primitives", ""); + else + assert(0); + } + if (shaderQualifiers.hasBlendEquation()) + error(loc, message, "blend equation", ""); + if (shaderQualifiers.numViews != TQualifier::layoutNotSet) + error(loc, message, "num_views", ""); + if (shaderQualifiers.interlockOrdering != EioNone) + error(loc, message, TQualifier::getInterlockOrderingString(shaderQualifiers.interlockOrdering), ""); + if (shaderQualifiers.layoutPrimitiveCulling) + error(loc, "can only be applied as standalone", "primitive_culling", ""); +#endif +} + +// Correct and/or advance an object's offset layout qualifier. +void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol) +{ + const TQualifier& qualifier = symbol.getType().getQualifier(); +#ifndef GLSLANG_WEB + if (symbol.getType().isAtomic()) { + if (qualifier.hasBinding() && (int)qualifier.layoutBinding < resources.maxAtomicCounterBindings) { + + // Set the offset + int offset; + if (qualifier.hasOffset()) + offset = qualifier.layoutOffset; + else + offset = atomicUintOffsets[qualifier.layoutBinding]; + + if (offset % 4 != 0) + error(loc, "atomic counters offset should align based on 4:", "offset", "%d", offset); + + symbol.getWritableType().getQualifier().layoutOffset = offset; + + // Check for overlap + int numOffsets = 4; + if (symbol.getType().isArray()) { + if (symbol.getType().isSizedArray() && !symbol.getType().getArraySizes()->isInnerUnsized()) + numOffsets *= symbol.getType().getCumulativeArraySize(); + else { + // "It is a compile-time error to declare an unsized array of atomic_uint." + error(loc, "array must be explicitly sized", "atomic_uint", ""); + } + } + int repeated = intermediate.addUsedOffsets(qualifier.layoutBinding, offset, numOffsets); + if (repeated >= 0) + error(loc, "atomic counters sharing the same offset:", "offset", "%d", repeated); + + // Bump the default offset + atomicUintOffsets[qualifier.layoutBinding] = offset + numOffsets; + } + } +#endif +} + +// +// Look up a function name in the symbol table, and make sure it is a function. +// +// Return the function symbol if found, otherwise nullptr. +// +const TFunction* TParseContext::findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + if (symbolTable.isFunctionNameVariable(call.getName())) { + error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); + return nullptr; + } + +#ifdef GLSLANG_WEB + return findFunctionExact(loc, call, builtIn); +#endif + + const TFunction* function = nullptr; + + // debugPrintfEXT has var args and is in the symbol table as "debugPrintfEXT()", + // mangled to "debugPrintfEXT(" + if (call.getName() == "debugPrintfEXT") { + TSymbol* symbol = symbolTable.find("debugPrintfEXT(", &builtIn); + if (symbol) + return symbol->getAsFunction(); + } + + bool explicitTypesEnabled = extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int32) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int64) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32) || + extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64); + + if (isEsProfile()) + function = (extensionTurnedOn(E_GL_EXT_shader_implicit_conversions) && version >= 310) ? + findFunction120(loc, call, builtIn) : findFunctionExact(loc, call, builtIn); + else if (version < 120) + function = findFunctionExact(loc, call, builtIn); + else if (version < 400) + function = extensionTurnedOn(E_GL_ARB_gpu_shader_fp64) ? findFunction400(loc, call, builtIn) : findFunction120(loc, call, builtIn); + else if (explicitTypesEnabled) + function = findFunctionExplicitTypes(loc, call, builtIn); + else + function = findFunction400(loc, call, builtIn); + + return function; +} + +// Function finding algorithm for ES and desktop 110. +const TFunction* TParseContext::findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol == nullptr) { + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + + return nullptr; + } + + return symbol->getAsFunction(); +} + +// Function finding algorithm for desktop versions 120 through 330. +const TFunction* TParseContext::findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // exact match not found, look through a list of overloaded functions of the same name + + // "If no exact match is found, then [implicit conversions] will be applied to find a match. Mismatched types + // on input parameters (in or inout or default) must have a conversion from the calling argument type to the + // formal parameter type. Mismatched types on output parameters (out or inout) must have a conversion + // from the formal parameter type to the calling argument type. When argument conversions are used to find + // a match, it is a semantic error if there are multiple ways to apply these conversions to make the call match + // more than one function." + + const TFunction* candidate = nullptr; + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + for (auto it = candidateList.begin(); it != candidateList.end(); ++it) { + const TFunction& function = *(*it); + + // to even be a potential match, number of arguments has to match + if (call.getParamCount() != function.getParamCount()) + continue; + + bool possibleMatch = true; + for (int i = 0; i < function.getParamCount(); ++i) { + // same types is easy + if (*function[i].type == *call[i].type) + continue; + + // We have a mismatch in type, see if it is implicitly convertible + + if (function[i].type->isArray() || call[i].type->isArray() || + ! function[i].type->sameElementShape(*call[i].type)) + possibleMatch = false; + else { + // do direction-specific checks for conversion of basic type + if (function[i].type->getQualifier().isParamInput()) { + if (! intermediate.canImplicitlyPromote(call[i].type->getBasicType(), function[i].type->getBasicType())) + possibleMatch = false; + } + if (function[i].type->getQualifier().isParamOutput()) { + if (! intermediate.canImplicitlyPromote(function[i].type->getBasicType(), call[i].type->getBasicType())) + possibleMatch = false; + } + } + if (! possibleMatch) + break; + } + if (possibleMatch) { + if (candidate) { + // our second match, meaning ambiguity + error(loc, "ambiguous function signature match: multiple signatures match under implicit type conversion", call.getName().c_str(), ""); + } else + candidate = &function; + } + } + + if (candidate == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + + return candidate; +} + +// Function finding algorithm for desktop version 400 and above. +// +// "When function calls are resolved, an exact type match for all the arguments +// is sought. If an exact match is found, all other functions are ignored, and +// the exact match is used. If no exact match is found, then the implicit +// conversions in section 4.1.10 Implicit Conversions will be applied to find +// a match. Mismatched types on input parameters (in or inout or default) must +// have a conversion from the calling argument type to the formal parameter type. +// Mismatched types on output parameters (out or inout) must have a conversion +// from the formal parameter type to the calling argument type. +// +// "If implicit conversions can be used to find more than one matching function, +// a single best-matching function is sought. To determine a best match, the +// conversions between calling argument and formal parameter types are compared +// for each function argument and pair of matching functions. After these +// comparisons are performed, each pair of matching functions are compared. +// A function declaration A is considered a better match than function +// declaration B if +// +// * for at least one function argument, the conversion for that argument in A +// is better than the corresponding conversion in B; and +// * there is no function argument for which the conversion in B is better than +// the corresponding conversion in A. +// +// "If a single function declaration is considered a better match than every +// other matching function declaration, it will be used. Otherwise, a +// compile-time semantic error for an ambiguous overloaded function call occurs. +// +// "To determine whether the conversion for a single argument in one match is +// better than that for another match, the following rules are applied, in order: +// +// 1. An exact match is better than a match involving any implicit conversion. +// 2. A match involving an implicit conversion from float to double is better +// than a match involving any other implicit conversion. +// 3. A match involving an implicit conversion from either int or uint to float +// is better than a match involving an implicit conversion from either int +// or uint to double. +// +// "If none of the rules above apply to a particular pair of conversions, neither +// conversion is considered better than the other." +// +const TFunction* TParseContext::findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // no exact match, use the generic selector, parameterized by the GLSL rules + + // create list of candidates to send + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + // can 'from' convert to 'to'? + const auto convertible = [this,builtIn](const TType& from, const TType& to, TOperator, int) -> bool { + if (from == to) + return true; + if (from.coopMatParameterOK(to)) + return true; + // Allow a sized array to be passed through an unsized array parameter, for coopMatLoad/Store functions + if (builtIn && from.isArray() && to.isUnsizedArray()) { + TType fromElementType(from, 0); + TType toElementType(to, 0); + if (fromElementType == toElementType) + return true; + } + if (from.isArray() || to.isArray() || ! from.sameElementShape(to)) + return false; + if (from.isCoopMat() && to.isCoopMat()) + return from.sameCoopMatBaseType(to); + return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType()); + }; + + // Is 'to2' a better conversion than 'to1'? + // Ties should not be considered as better. + // Assumes 'convertible' already said true. + const auto better = [](const TType& from, const TType& to1, const TType& to2) -> bool { + // 1. exact match + if (from == to2) + return from != to1; + if (from == to1) + return false; + + // 2. float -> double is better + if (from.getBasicType() == EbtFloat) { + if (to2.getBasicType() == EbtDouble && to1.getBasicType() != EbtDouble) + return true; + } + + // 3. -> float is better than -> double + return to2.getBasicType() == EbtFloat && to1.getBasicType() == EbtDouble; + }; + + // for ambiguity reporting + bool tie = false; + + // send to the generic selector + const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie); + + if (bestMatch == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + else if (tie) + error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); + + return bestMatch; +} + +// "To determine whether the conversion for a single argument in one match +// is better than that for another match, the conversion is assigned of the +// three ranks ordered from best to worst: +// 1. Exact match: no conversion. +// 2. Promotion: integral or floating-point promotion. +// 3. Conversion: integral conversion, floating-point conversion, +// floating-integral conversion. +// A conversion C1 is better than a conversion C2 if the rank of C1 is +// better than the rank of C2." +const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn) +{ + // first, look for an exact match + TSymbol* symbol = symbolTable.find(call.getMangledName(), &builtIn); + if (symbol) + return symbol->getAsFunction(); + + // no exact match, use the generic selector, parameterized by the GLSL rules + + // create list of candidates to send + TVector candidateList; + symbolTable.findFunctionNameList(call.getMangledName(), candidateList, builtIn); + + // can 'from' convert to 'to'? + const auto convertible = [this,builtIn](const TType& from, const TType& to, TOperator, int) -> bool { + if (from == to) + return true; + if (from.coopMatParameterOK(to)) + return true; + // Allow a sized array to be passed through an unsized array parameter, for coopMatLoad/Store functions + if (builtIn && from.isArray() && to.isUnsizedArray()) { + TType fromElementType(from, 0); + TType toElementType(to, 0); + if (fromElementType == toElementType) + return true; + } + if (from.isArray() || to.isArray() || ! from.sameElementShape(to)) + return false; + if (from.isCoopMat() && to.isCoopMat()) + return from.sameCoopMatBaseType(to); + return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType()); + }; + + // Is 'to2' a better conversion than 'to1'? + // Ties should not be considered as better. + // Assumes 'convertible' already said true. + const auto better = [this](const TType& from, const TType& to1, const TType& to2) -> bool { + // 1. exact match + if (from == to2) + return from != to1; + if (from == to1) + return false; + + // 2. Promotion (integral, floating-point) is better + TBasicType from_type = from.getBasicType(); + TBasicType to1_type = to1.getBasicType(); + TBasicType to2_type = to2.getBasicType(); + bool isPromotion1 = (intermediate.isIntegralPromotion(from_type, to1_type) || + intermediate.isFPPromotion(from_type, to1_type)); + bool isPromotion2 = (intermediate.isIntegralPromotion(from_type, to2_type) || + intermediate.isFPPromotion(from_type, to2_type)); + if (isPromotion2) + return !isPromotion1; + if(isPromotion1) + return false; + + // 3. Conversion (integral, floating-point , floating-integral) + bool isConversion1 = (intermediate.isIntegralConversion(from_type, to1_type) || + intermediate.isFPConversion(from_type, to1_type) || + intermediate.isFPIntegralConversion(from_type, to1_type)); + bool isConversion2 = (intermediate.isIntegralConversion(from_type, to2_type) || + intermediate.isFPConversion(from_type, to2_type) || + intermediate.isFPIntegralConversion(from_type, to2_type)); + + return isConversion2 && !isConversion1; + }; + + // for ambiguity reporting + bool tie = false; + + // send to the generic selector + const TFunction* bestMatch = selectFunction(candidateList, call, convertible, better, tie); + + if (bestMatch == nullptr) + error(loc, "no matching overloaded function found", call.getName().c_str(), ""); + else if (tie) + error(loc, "ambiguous best function under implicit type conversion", call.getName().c_str(), ""); + + return bestMatch; +} + +// When a declaration includes a type, but not a variable name, it can be used +// to establish defaults. +void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType& publicType) +{ +#ifndef GLSLANG_WEB + if (publicType.basicType == EbtAtomicUint && publicType.qualifier.hasBinding()) { + if (publicType.qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { + error(loc, "atomic_uint binding is too large", "binding", ""); + return; + } + if (publicType.qualifier.hasOffset()) + atomicUintOffsets[publicType.qualifier.layoutBinding] = publicType.qualifier.layoutOffset; + return; + } + + if (publicType.arraySizes) { + error(loc, "expect an array name", "", ""); + } + + if (publicType.qualifier.hasLayout() && !publicType.qualifier.hasBufferReference()) + warn(loc, "useless application of layout qualifier", "layout", ""); +#endif +} + +// +// Do everything necessary to handle a variable (non-block) declaration. +// Either redeclaring a variable, or making a new one, updating the symbol +// table, and all error checking. +// +// Returns a subtree node that computes an initializer, if needed. +// Returns nullptr if there is no code to execute for initialization. +// +// 'publicType' is the type part of the declaration (to the left) +// 'arraySizes' is the arrayness tagged on the identifier (to the right) +// +TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& identifier, const TPublicType& publicType, + TArraySizes* arraySizes, TIntermTyped* initializer) +{ + // Make a fresh type that combines the characteristics from the individual + // identifier syntax and the declaration-type syntax. + TType type(publicType); + type.transferArraySizes(arraySizes); + type.copyArrayInnerSizes(publicType.arraySizes); + arrayOfArrayVersionCheck(loc, type.getArraySizes()); + + if (initializer) { + if (type.getBasicType() == EbtRayQuery) { + error(loc, "ray queries can only be initialized by using the rayQueryInitializeEXT intrinsic:", "=", identifier.c_str()); + } + } + + if (type.isCoopMat()) { + intermediate.setUseVulkanMemoryModel(); + intermediate.setUseStorageBuffer(); + + if (!publicType.typeParameters || publicType.typeParameters->getNumDims() != 4) { + error(loc, "expected four type parameters", identifier.c_str(), ""); + } + if (publicType.typeParameters) { + if (isTypeFloat(publicType.basicType) && + publicType.typeParameters->getDimSize(0) != 16 && + publicType.typeParameters->getDimSize(0) != 32 && + publicType.typeParameters->getDimSize(0) != 64) { + error(loc, "expected 16, 32, or 64 bits for first type parameter", identifier.c_str(), ""); + } + if (isTypeInt(publicType.basicType) && + publicType.typeParameters->getDimSize(0) != 8 && + publicType.typeParameters->getDimSize(0) != 32) { + error(loc, "expected 8 or 32 bits for first type parameter", identifier.c_str(), ""); + } + } + + } else { + if (publicType.typeParameters && publicType.typeParameters->getNumDims() != 0) { + error(loc, "unexpected type parameters", identifier.c_str(), ""); + } + } + + if (voidErrorCheck(loc, identifier, type.getBasicType())) + return nullptr; + + if (initializer) + rValueErrorCheck(loc, "initializer", initializer); + else + nonInitConstCheck(loc, identifier, type); + + samplerCheck(loc, type, identifier, initializer); + transparentOpaqueCheck(loc, type, identifier); +#ifndef GLSLANG_WEB + atomicUintCheck(loc, type, identifier); + accStructCheck(loc, type, identifier); + checkAndResizeMeshViewDim(loc, type, /*isBlockMember*/ false); +#endif + if (type.getQualifier().storage == EvqConst && type.containsReference()) { + error(loc, "variables with reference type can't have qualifier 'const'", "qualifier", ""); + } + + if (type.getQualifier().storage != EvqUniform && type.getQualifier().storage != EvqBuffer) { + if (type.contains16BitFloat()) + requireFloat16Arithmetic(loc, "qualifier", "float16 types can only be in uniform block or buffer storage"); + if (type.contains16BitInt()) + requireInt16Arithmetic(loc, "qualifier", "(u)int16 types can only be in uniform block or buffer storage"); + if (type.contains8BitInt()) + requireInt8Arithmetic(loc, "qualifier", "(u)int8 types can only be in uniform block or buffer storage"); + } + + if (type.getQualifier().storage == EvqShared && type.containsCoopMat()) + error(loc, "qualifier", "Cooperative matrix types must not be used in shared memory", ""); + + if (profile == EEsProfile) { + if (type.getQualifier().isPipeInput() && type.getBasicType() == EbtStruct) { + if (type.getQualifier().isArrayedIo(language)) { + TType perVertexType(type, 0); + if (perVertexType.containsArray() && perVertexType.containsBuiltIn() == false) { + error(loc, "A per vertex structure containing an array is not allowed as input in ES", type.getTypeName().c_str(), ""); + } + } + else if (type.containsArray() && type.containsBuiltIn() == false) { + error(loc, "A structure containing an array is not allowed as input in ES", type.getTypeName().c_str(), ""); + } + if (type.containsStructure()) + error(loc, "A structure containing an struct is not allowed as input in ES", type.getTypeName().c_str(), ""); + } + } + + if (identifier != "gl_FragCoord" && (publicType.shaderQualifiers.originUpperLeft || publicType.shaderQualifiers.pixelCenterInteger)) + error(loc, "can only apply origin_upper_left and pixel_center_origin to gl_FragCoord", "layout qualifier", ""); + if (identifier != "gl_FragDepth" && publicType.shaderQualifiers.getDepth() != EldNone) + error(loc, "can only apply depth layout to gl_FragDepth", "layout qualifier", ""); + + // Check for redeclaration of built-ins and/or attempting to declare a reserved name + TSymbol* symbol = redeclareBuiltinVariable(loc, identifier, type.getQualifier(), publicType.shaderQualifiers); + if (symbol == nullptr) + reservedErrorCheck(loc, identifier); + + inheritGlobalDefaults(type.getQualifier()); + + // Declare the variable + if (type.isArray()) { + // Check that implicit sizing is only where allowed. + arraySizesCheck(loc, type.getQualifier(), type.getArraySizes(), initializer, false); + + if (! arrayQualifierError(loc, type.getQualifier()) && ! arrayError(loc, type)) + declareArray(loc, identifier, type, symbol); + + if (initializer) { + profileRequires(loc, ENoProfile, 120, E_GL_3DL_array_objects, "initializer"); + profileRequires(loc, EEsProfile, 300, nullptr, "initializer"); + } + } else { + // non-array case + if (symbol == nullptr) + symbol = declareNonArray(loc, identifier, type); + else if (type != symbol->getType()) + error(loc, "cannot change the type of", "redeclaration", symbol->getName().c_str()); + } + + if (symbol == nullptr) + return nullptr; + + // Deal with initializer + TIntermNode* initNode = nullptr; + if (symbol != nullptr && initializer) { + TVariable* variable = symbol->getAsVariable(); + if (! variable) { + error(loc, "initializer requires a variable, not a member", identifier.c_str(), ""); + return nullptr; + } + initNode = executeInitializer(loc, initializer, variable); + } + + // look for errors in layout qualifier use + layoutObjectCheck(loc, *symbol); + + // fix up + fixOffset(loc, *symbol); + + return initNode; +} + +// Pick up global defaults from the provide global defaults into dst. +void TParseContext::inheritGlobalDefaults(TQualifier& dst) const +{ +#ifndef GLSLANG_WEB + if (dst.storage == EvqVaryingOut) { + if (! dst.hasStream() && language == EShLangGeometry) + dst.layoutStream = globalOutputDefaults.layoutStream; + if (! dst.hasXfbBuffer()) + dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + } +#endif +} + +// +// Make an internal-only variable whose name is for debug purposes only +// and won't be searched for. Callers will only use the return value to use +// the variable, not the name to look it up. It is okay if the name +// is the same as other names; there won't be any conflict. +// +TVariable* TParseContext::makeInternalVariable(const char* name, const TType& type) const +{ + TString* nameString = NewPoolTString(name); + TVariable* variable = new TVariable(nameString, type); + symbolTable.makeInternalVariable(*variable); + + return variable; +} + +// +// Declare a non-array variable, the main point being there is no redeclaration +// for resizing allowed. +// +// Return the successfully declared variable. +// +TVariable* TParseContext::declareNonArray(const TSourceLoc& loc, const TString& identifier, const TType& type) +{ + // make a new variable + TVariable* variable = new TVariable(&identifier, type); + +#ifndef GLSLANG_WEB + ioArrayCheck(loc, type, identifier); +#endif + + // add variable to symbol table + if (symbolTable.insert(*variable)) { + if (symbolTable.atGlobalLevel()) + trackLinkage(*variable); + return variable; + } + + error(loc, "redefinition", variable->getName().c_str(), ""); + return nullptr; +} + +// +// Handle all types of initializers from the grammar. +// +// Returning nullptr just means there is no code to execute to handle the +// initializer, which will, for example, be the case for constant initializers. +// +TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyped* initializer, TVariable* variable) +{ + // + // Identifier must be of type constant, a global, or a temporary, and + // starting at version 120, desktop allows uniforms to have initializers. + // + TStorageQualifier qualifier = variable->getType().getQualifier().storage; + if (! (qualifier == EvqTemporary || qualifier == EvqGlobal || qualifier == EvqConst || + (qualifier == EvqUniform && !isEsProfile() && version >= 120))) { + error(loc, " cannot initialize this type of qualifier ", variable->getType().getStorageQualifierString(), ""); + return nullptr; + } + arrayObjectCheck(loc, variable->getType(), "array initializer"); + + // + // If the initializer was from braces { ... }, we convert the whole subtree to a + // constructor-style subtree, allowing the rest of the code to operate + // identically for both kinds of initializers. + // + // Type can't be deduced from the initializer list, so a skeletal type to + // follow has to be passed in. Constness and specialization-constness + // should be deduced bottom up, not dictated by the skeletal type. + // + TType skeletalType; + skeletalType.shallowCopy(variable->getType()); + skeletalType.getQualifier().makeTemporary(); +#ifndef GLSLANG_WEB + initializer = convertInitializerList(loc, skeletalType, initializer); +#endif + if (! initializer) { + // error recovery; don't leave const without constant values + if (qualifier == EvqConst) + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // Fix outer arrayness if variable is unsized, getting size from the initializer + if (initializer->getType().isSizedArray() && variable->getType().isUnsizedArray()) + variable->getWritableType().changeOuterArraySize(initializer->getType().getOuterArraySize()); + + // Inner arrayness can also get set by an initializer + if (initializer->getType().isArrayOfArrays() && variable->getType().isArrayOfArrays() && + initializer->getType().getArraySizes()->getNumDims() == + variable->getType().getArraySizes()->getNumDims()) { + // adopt unsized sizes from the initializer's sizes + for (int d = 1; d < variable->getType().getArraySizes()->getNumDims(); ++d) { + if (variable->getType().getArraySizes()->getDimSize(d) == UnsizedArraySize) { + variable->getWritableType().getArraySizes()->setDimSize(d, + initializer->getType().getArraySizes()->getDimSize(d)); + } + } + } + + // Uniforms require a compile-time constant initializer + if (qualifier == EvqUniform && ! initializer->getType().getQualifier().isFrontEndConstant()) { + error(loc, "uniform initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + // Global consts require a constant initializer (specialization constant is okay) + if (qualifier == EvqConst && symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { + error(loc, "global const initializers must be constant", "=", "'%s'", variable->getType().getCompleteString().c_str()); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // Const variables require a constant initializer, depending on version + if (qualifier == EvqConst) { + if (! initializer->getType().getQualifier().isConstant()) { + const char* initFeature = "non-constant initializer"; + requireProfile(loc, ~EEsProfile, initFeature); + profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + variable->getWritableType().getQualifier().storage = EvqConstReadOnly; + qualifier = EvqConstReadOnly; + } + } else { + // Non-const global variables in ES need a const initializer. + // + // "In declarations of global variables with no storage qualifier or with a const + // qualifier any initializer must be a constant expression." + if (symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { + const char* initFeature = "non-constant global initializer (needs GL_EXT_shader_non_constant_global_initializers)"; + if (isEsProfile()) { + if (relaxedErrors() && ! extensionTurnedOn(E_GL_EXT_shader_non_constant_global_initializers)) + warn(loc, "not allowed in this version", initFeature, ""); + else + profileRequires(loc, EEsProfile, 0, E_GL_EXT_shader_non_constant_global_initializers, initFeature); + } + } + } + + if (qualifier == EvqConst || qualifier == EvqUniform) { + // Compile-time tagging of the variable with its constant value... + + initializer = intermediate.addConversion(EOpAssign, variable->getType(), initializer); + if (! initializer || ! initializer->getType().getQualifier().isConstant() || variable->getType() != initializer->getType()) { + error(loc, "non-matching or non-convertible constant type for const initializer", + variable->getType().getStorageQualifierString(), ""); + variable->getWritableType().getQualifier().makeTemporary(); + return nullptr; + } + + // We either have a folded constant in getAsConstantUnion, or we have to use + // the initializer's subtree in the AST to represent the computation of a + // specialization constant. + assert(initializer->getAsConstantUnion() || initializer->getType().getQualifier().isSpecConstant()); + if (initializer->getAsConstantUnion()) + variable->setConstArray(initializer->getAsConstantUnion()->getConstArray()); + else { + // It's a specialization constant. + variable->getWritableType().getQualifier().makeSpecConstant(); + + // Keep the subtree that computes the specialization constant with the variable. + // Later, a symbol node will adopt the subtree from the variable. + variable->setConstSubtree(initializer); + } + } else { + // normal assigning of a value to a variable... + specializationCheck(loc, initializer->getType(), "initializer"); + TIntermSymbol* intermSymbol = intermediate.addSymbol(*variable, loc); + TIntermTyped* initNode = intermediate.addAssign(EOpAssign, intermSymbol, initializer, loc); + if (! initNode) + assignError(loc, "=", intermSymbol->getCompleteString(), initializer->getCompleteString()); + + return initNode; + } + + return nullptr; +} + +// +// Reprocess any initializer-list (the "{ ... }" syntax) parts of the +// initializer. +// +// Need to hierarchically assign correct types and implicit +// conversions. Will do this mimicking the same process used for +// creating a constructor-style initializer, ensuring we get the +// same form. However, it has to in parallel walk the 'type' +// passed in, as type cannot be deduced from an initializer list. +// +TIntermTyped* TParseContext::convertInitializerList(const TSourceLoc& loc, const TType& type, TIntermTyped* initializer) +{ + // Will operate recursively. Once a subtree is found that is constructor style, + // everything below it is already good: Only the "top part" of the initializer + // can be an initializer list, where "top part" can extend for several (or all) levels. + + // see if we have bottomed out in the tree within the initializer-list part + TIntermAggregate* initList = initializer->getAsAggregate(); + if (! initList || initList->getOp() != EOpNull) + return initializer; + + // Of the initializer-list set of nodes, need to process bottom up, + // so recurse deep, then process on the way up. + + // Go down the tree here... + if (type.isArray()) { + // The type's array might be unsized, which could be okay, so base sizes on the size of the aggregate. + // Later on, initializer execution code will deal with array size logic. + TType arrayType; + arrayType.shallowCopy(type); // sharing struct stuff is fine + arrayType.copyArraySizes(*type.getArraySizes()); // but get a fresh copy of the array information, to edit below + + // edit array sizes to fill in unsized dimensions + arrayType.changeOuterArraySize((int)initList->getSequence().size()); + TIntermTyped* firstInit = initList->getSequence()[0]->getAsTyped(); + if (arrayType.isArrayOfArrays() && firstInit->getType().isArray() && + arrayType.getArraySizes()->getNumDims() == firstInit->getType().getArraySizes()->getNumDims() + 1) { + for (int d = 1; d < arrayType.getArraySizes()->getNumDims(); ++d) { + if (arrayType.getArraySizes()->getDimSize(d) == UnsizedArraySize) + arrayType.getArraySizes()->setDimSize(d, firstInit->getType().getArraySizes()->getDimSize(d - 1)); + } + } + + TType elementType(arrayType, 0); // dereferenced type + for (size_t i = 0; i < initList->getSequence().size(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, elementType, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + + return addConstructor(loc, initList, arrayType); + } else if (type.isStruct()) { + if (type.getStruct()->size() != initList->getSequence().size()) { + error(loc, "wrong number of structure members", "initializer list", ""); + return nullptr; + } + for (size_t i = 0; i < type.getStruct()->size(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, *(*type.getStruct())[i].type, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } else if (type.isMatrix()) { + if (type.getMatrixCols() != (int)initList->getSequence().size()) { + error(loc, "wrong number of matrix columns:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + TType vectorType(type, 0); // dereferenced type + for (int i = 0; i < type.getMatrixCols(); ++i) { + initList->getSequence()[i] = convertInitializerList(loc, vectorType, initList->getSequence()[i]->getAsTyped()); + if (initList->getSequence()[i] == nullptr) + return nullptr; + } + } else if (type.isVector()) { + if (type.getVectorSize() != (int)initList->getSequence().size()) { + error(loc, "wrong vector size (or rows in a matrix column):", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + TBasicType destType = type.getBasicType(); + for (int i = 0; i < type.getVectorSize(); ++i) { + TBasicType initType = initList->getSequence()[i]->getAsTyped()->getBasicType(); + if (destType != initType && !intermediate.canImplicitlyPromote(initType, destType)) { + error(loc, "type mismatch in initializer list", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + + } + } else { + error(loc, "unexpected initializer-list type:", "initializer list", type.getCompleteString().c_str()); + return nullptr; + } + + // Now that the subtree is processed, process this node as if the + // initializer list is a set of arguments to a constructor. + TIntermNode* emulatedConstructorArguments; + if (initList->getSequence().size() == 1) + emulatedConstructorArguments = initList->getSequence()[0]; + else + emulatedConstructorArguments = initList; + return addConstructor(loc, emulatedConstructorArguments, type); +} + +// +// Test for the correctness of the parameters passed to various constructor functions +// and also convert them to the right data type, if allowed and required. +// +// 'node' is what to construct from. +// 'type' is what type to construct. +// +// Returns nullptr for an error or the constructed node (aggregate or typed) for no error. +// +TIntermTyped* TParseContext::addConstructor(const TSourceLoc& loc, TIntermNode* node, const TType& type) +{ + if (node == nullptr || node->getAsTyped() == nullptr) + return nullptr; + rValueErrorCheck(loc, "constructor", node->getAsTyped()); + + TIntermAggregate* aggrNode = node->getAsAggregate(); + TOperator op = intermediate.mapTypeToConstructorOp(type); + + // Combined texture-sampler constructors are completely semantic checked + // in constructorTextureSamplerError() + if (op == EOpConstructTextureSampler) { + if (aggrNode->getSequence()[1]->getAsTyped()->getType().getSampler().shadow) { + // Transfer depth into the texture (SPIR-V image) type, as a hint + // for tools to know this texture/image is a depth image. + aggrNode->getSequence()[0]->getAsTyped()->getWritableType().getSampler().shadow = true; + } + return intermediate.setAggregateOperator(aggrNode, op, type, loc); + } + + TTypeList::const_iterator memberTypes; + if (op == EOpConstructStruct) + memberTypes = type.getStruct()->begin(); + + TType elementType; + if (type.isArray()) { + TType dereferenced(type, 0); + elementType.shallowCopy(dereferenced); + } else + elementType.shallowCopy(type); + + bool singleArg; + if (aggrNode) { + if (aggrNode->getOp() != EOpNull) + singleArg = true; + else + singleArg = false; + } else + singleArg = true; + + TIntermTyped *newNode; + if (singleArg) { + // If structure constructor or array constructor is being called + // for only one parameter inside the structure, we need to call constructAggregate function once. + if (type.isArray()) + newNode = constructAggregate(node, elementType, 1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(node, *(*memberTypes).type, 1, node->getLoc()); + else + newNode = constructBuiltIn(type, op, node->getAsTyped(), node->getLoc(), false); + + if (newNode && (type.isArray() || op == EOpConstructStruct)) + newNode = intermediate.setAggregateOperator(newNode, EOpConstructStruct, type, loc); + + return newNode; + } + + // + // Handle list of arguments. + // + TIntermSequence &sequenceVector = aggrNode->getSequence(); // Stores the information about the parameter to the constructor + // if the structure constructor contains more than one parameter, then construct + // each parameter + + int paramCount = 0; // keeps track of the constructor parameter number being checked + + // for each parameter to the constructor call, check to see if the right type is passed or convert them + // to the right type if possible (and allowed). + // for structure constructors, just check if the right type is passed, no conversion is allowed. + for (TIntermSequence::iterator p = sequenceVector.begin(); + p != sequenceVector.end(); p++, paramCount++) { + if (type.isArray()) + newNode = constructAggregate(*p, elementType, paramCount+1, node->getLoc()); + else if (op == EOpConstructStruct) + newNode = constructAggregate(*p, *(memberTypes[paramCount]).type, paramCount+1, node->getLoc()); + else + newNode = constructBuiltIn(type, op, (*p)->getAsTyped(), node->getLoc(), true); + + if (newNode) + *p = newNode; + else + return nullptr; + } + + return intermediate.setAggregateOperator(aggrNode, op, type, loc); +} + +// Function for constructor implementation. Calls addUnaryMath with appropriate EOp value +// for the parameter to the constructor (passed to this function). Essentially, it converts +// the parameter types correctly. If a constructor expects an int (like ivec2) and is passed a +// float, then float is converted to int. +// +// Returns nullptr for an error or the constructed node. +// +TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, TIntermTyped* node, const TSourceLoc& loc, + bool subset) +{ + // If we are changing a matrix in both domain of basic type and to a non matrix, + // do the shape change first (by default, below, basic type is changed before shape). + // This avoids requesting a matrix of a new type that is going to be discarded anyway. + // TODO: This could be generalized to more type combinations, but that would require + // more extensive testing and full algorithm rework. For now, the need to do two changes makes + // the recursive call work, and avoids the most egregious case of creating integer matrices. + if (node->getType().isMatrix() && (type.isScalar() || type.isVector()) && + type.isFloatingDomain() != node->getType().isFloatingDomain()) { + TType transitionType(node->getBasicType(), glslang::EvqTemporary, type.getVectorSize(), 0, 0, node->isVector()); + TOperator transitionOp = intermediate.mapTypeToConstructorOp(transitionType); + node = constructBuiltIn(transitionType, transitionOp, node, loc, false); + } + + TIntermTyped* newNode; + TOperator basicOp; + + // + // First, convert types as needed. + // + switch (op) { + case EOpConstructVec2: + case EOpConstructVec3: + case EOpConstructVec4: + case EOpConstructMat2x2: + case EOpConstructMat2x3: + case EOpConstructMat2x4: + case EOpConstructMat3x2: + case EOpConstructMat3x3: + case EOpConstructMat3x4: + case EOpConstructMat4x2: + case EOpConstructMat4x3: + case EOpConstructMat4x4: + case EOpConstructFloat: + basicOp = EOpConstructFloat; + break; + + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructInt: + basicOp = EOpConstructInt; + break; + + case EOpConstructUVec2: + if (node->getType().getBasicType() == EbtReference) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference_uvec2, "reference conversion to uvec2"); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUvec2, true, node, + type); + return newNode; + } + case EOpConstructUVec3: + case EOpConstructUVec4: + case EOpConstructUint: + basicOp = EOpConstructUint; + break; + + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructBool: + basicOp = EOpConstructBool; + break; + +#ifndef GLSLANG_WEB + + case EOpConstructDVec2: + case EOpConstructDVec3: + case EOpConstructDVec4: + case EOpConstructDMat2x2: + case EOpConstructDMat2x3: + case EOpConstructDMat2x4: + case EOpConstructDMat3x2: + case EOpConstructDMat3x3: + case EOpConstructDMat3x4: + case EOpConstructDMat4x2: + case EOpConstructDMat4x3: + case EOpConstructDMat4x4: + case EOpConstructDouble: + basicOp = EOpConstructDouble; + break; + + case EOpConstructF16Vec2: + case EOpConstructF16Vec3: + case EOpConstructF16Vec4: + case EOpConstructF16Mat2x2: + case EOpConstructF16Mat2x3: + case EOpConstructF16Mat2x4: + case EOpConstructF16Mat3x2: + case EOpConstructF16Mat3x3: + case EOpConstructF16Mat3x4: + case EOpConstructF16Mat4x2: + case EOpConstructF16Mat4x3: + case EOpConstructF16Mat4x4: + case EOpConstructFloat16: + basicOp = EOpConstructFloat16; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticFloat16Enabled()) { + TType tempType(EbtFloat, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructFloat16) + aggregateOp = EOpConstructFloat; + else + aggregateOp = (TOperator)(EOpConstructVec2 + op - EOpConstructF16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtFloat16, newNode); + return newNode; + } + break; + + case EOpConstructI8Vec2: + case EOpConstructI8Vec3: + case EOpConstructI8Vec4: + case EOpConstructInt8: + basicOp = EOpConstructInt8; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt8Enabled()) { + TType tempType(EbtInt, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructInt8) + aggregateOp = EOpConstructInt; + else + aggregateOp = (TOperator)(EOpConstructIVec2 + op - EOpConstructI8Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtInt8, newNode); + return newNode; + } + break; + + case EOpConstructU8Vec2: + case EOpConstructU8Vec3: + case EOpConstructU8Vec4: + case EOpConstructUint8: + basicOp = EOpConstructUint8; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt8Enabled()) { + TType tempType(EbtUint, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructUint8) + aggregateOp = EOpConstructUint; + else + aggregateOp = (TOperator)(EOpConstructUVec2 + op - EOpConstructU8Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtUint8, newNode); + return newNode; + } + break; + + case EOpConstructI16Vec2: + case EOpConstructI16Vec3: + case EOpConstructI16Vec4: + case EOpConstructInt16: + basicOp = EOpConstructInt16; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt16Enabled()) { + TType tempType(EbtInt, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructInt16) + aggregateOp = EOpConstructInt; + else + aggregateOp = (TOperator)(EOpConstructIVec2 + op - EOpConstructI16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtInt16, newNode); + return newNode; + } + break; + + case EOpConstructU16Vec2: + case EOpConstructU16Vec3: + case EOpConstructU16Vec4: + case EOpConstructUint16: + basicOp = EOpConstructUint16; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt16Enabled()) { + TType tempType(EbtUint, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructUint16) + aggregateOp = EOpConstructUint; + else + aggregateOp = (TOperator)(EOpConstructUVec2 + op - EOpConstructU16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtUint16, newNode); + return newNode; + } + break; + + case EOpConstructI64Vec2: + case EOpConstructI64Vec3: + case EOpConstructI64Vec4: + case EOpConstructInt64: + basicOp = EOpConstructInt64; + break; + + case EOpConstructUint64: + if (type.isScalar() && node->getType().isReference()) { + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUint64, true, node, type); + return newNode; + } + // fall through + case EOpConstructU64Vec2: + case EOpConstructU64Vec3: + case EOpConstructU64Vec4: + basicOp = EOpConstructUint64; + break; + + case EOpConstructNonuniform: + // Make a nonuniform copy of node + newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpCopyObject, true, node, type); + return newNode; + + case EOpConstructReference: + // construct reference from reference + if (node->getType().isReference()) { + newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConstructReference, true, node, type); + return newNode; + // construct reference from uint64 + } else if (node->getType().isScalar() && node->getType().getBasicType() == EbtUint64) { + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUint64ToPtr, true, node, + type); + return newNode; + // construct reference from uvec2 + } else if (node->getType().isVector() && node->getType().getBasicType() == EbtUint && + node->getVectorSize() == 2) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference_uvec2, "uvec2 conversion to reference"); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUvec2ToPtr, true, node, + type); + return newNode; + } else { + return nullptr; + } + + case EOpConstructCooperativeMatrix: + if (!node->getType().isCoopMat()) { + if (type.getBasicType() != node->getType().getBasicType()) { + node = intermediate.addConversion(type.getBasicType(), node); + if (node == nullptr) + return nullptr; + } + node = intermediate.setAggregateOperator(node, EOpConstructCooperativeMatrix, type, node->getLoc()); + } else { + TOperator op = EOpNull; + switch (type.getBasicType()) { + default: + assert(0); + break; + case EbtInt: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToInt; break; + case EbtFloat16: op = EOpConvFloat16ToInt; break; + case EbtUint8: op = EOpConvUint8ToInt; break; + case EbtInt8: op = EOpConvInt8ToInt; break; + case EbtUint: op = EOpConvUintToInt; break; + default: assert(0); + } + break; + case EbtUint: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToUint; break; + case EbtFloat16: op = EOpConvFloat16ToUint; break; + case EbtUint8: op = EOpConvUint8ToUint; break; + case EbtInt8: op = EOpConvInt8ToUint; break; + case EbtInt: op = EOpConvIntToUint; break; + case EbtUint: op = EOpConvUintToInt8; break; + default: assert(0); + } + break; + case EbtInt8: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToInt8; break; + case EbtFloat16: op = EOpConvFloat16ToInt8; break; + case EbtUint8: op = EOpConvUint8ToInt8; break; + case EbtInt: op = EOpConvIntToInt8; break; + case EbtUint: op = EOpConvUintToInt8; break; + default: assert(0); + } + break; + case EbtUint8: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToUint8; break; + case EbtFloat16: op = EOpConvFloat16ToUint8; break; + case EbtInt8: op = EOpConvInt8ToUint8; break; + case EbtInt: op = EOpConvIntToUint8; break; + case EbtUint: op = EOpConvUintToUint8; break; + default: assert(0); + } + break; + case EbtFloat: + switch (node->getType().getBasicType()) { + case EbtFloat16: op = EOpConvFloat16ToFloat; break; + case EbtInt8: op = EOpConvInt8ToFloat; break; + case EbtUint8: op = EOpConvUint8ToFloat; break; + case EbtInt: op = EOpConvIntToFloat; break; + case EbtUint: op = EOpConvUintToFloat; break; + default: assert(0); + } + break; + case EbtFloat16: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToFloat16; break; + case EbtInt8: op = EOpConvInt8ToFloat16; break; + case EbtUint8: op = EOpConvUint8ToFloat16; break; + case EbtInt: op = EOpConvIntToFloat16; break; + case EbtUint: op = EOpConvUintToFloat16; break; + default: assert(0); + } + break; + } + + node = intermediate.addUnaryNode(op, node, node->getLoc(), type); + // If it's a (non-specialization) constant, it must be folded. + if (node->getAsUnaryNode()->getOperand()->getAsConstantUnion()) + return node->getAsUnaryNode()->getOperand()->getAsConstantUnion()->fold(op, node->getType()); + } + + return node; + +#endif // GLSLANG_WEB + + default: + error(loc, "unsupported construction", "", ""); + + return nullptr; + } + newNode = intermediate.addUnaryMath(basicOp, node, node->getLoc()); + if (newNode == nullptr) { + error(loc, "can't convert", "constructor", ""); + return nullptr; + } + + // + // Now, if there still isn't an operation to do the construction, and we need one, add one. + // + + // Otherwise, skip out early. + if (subset || (newNode != node && newNode->getType() == type)) + return newNode; + + // setAggregateOperator will insert a new node for the constructor, as needed. + return intermediate.setAggregateOperator(newNode, op, type, loc); +} + +// This function tests for the type of the parameters to the structure or array constructor. Raises +// an error message if the expected type does not match the parameter passed to the constructor. +// +// Returns nullptr for an error or the input node itself if the expected and the given parameter types match. +// +TIntermTyped* TParseContext::constructAggregate(TIntermNode* node, const TType& type, int paramCount, const TSourceLoc& loc) +{ + TIntermTyped* converted = intermediate.addConversion(EOpConstructStruct, type, node->getAsTyped()); + if (! converted || converted->getType() != type) { + error(loc, "", "constructor", "cannot convert parameter %d from '%s' to '%s'", paramCount, + node->getAsTyped()->getType().getCompleteString().c_str(), type.getCompleteString().c_str()); + + return nullptr; + } + + return converted; +} + +// If a memory qualifier is present in 'to', also make it present in 'from'. +void TParseContext::inheritMemoryQualifiers(const TQualifier& from, TQualifier& to) +{ +#ifndef GLSLANG_WEB + if (from.isReadOnly()) + to.readonly = from.readonly; + if (from.isWriteOnly()) + to.writeonly = from.writeonly; + if (from.coherent) + to.coherent = from.coherent; + if (from.volatil) + to.volatil = from.volatil; + if (from.restrict) + to.restrict = from.restrict; +#endif +} + +// +// Do everything needed to add an interface block. +// +void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName, + TArraySizes* arraySizes) +{ + blockStageIoCheck(loc, currentBlockQualifier); + blockQualifierCheck(loc, currentBlockQualifier, instanceName != nullptr); + if (arraySizes != nullptr) { + arraySizesCheck(loc, currentBlockQualifier, arraySizes, nullptr, false); + arrayOfArrayVersionCheck(loc, arraySizes); + if (arraySizes->getNumDims() > 1) + requireProfile(loc, ~EEsProfile, "array-of-array of block"); + } + + // Inherit and check member storage qualifiers WRT to the block-level qualifier. + for (unsigned int member = 0; member < typeList.size(); ++member) { + TType& memberType = *typeList[member].type; + TQualifier& memberQualifier = memberType.getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal && memberQualifier.storage != currentBlockQualifier.storage) + error(memberLoc, "member storage qualifier cannot contradict block storage qualifier", memberType.getFieldName().c_str(), ""); + memberQualifier.storage = currentBlockQualifier.storage; + globalQualifierFixCheck(memberLoc, memberQualifier); +#ifndef GLSLANG_WEB + inheritMemoryQualifiers(currentBlockQualifier, memberQualifier); + if (currentBlockQualifier.perPrimitiveNV) + memberQualifier.perPrimitiveNV = currentBlockQualifier.perPrimitiveNV; + if (currentBlockQualifier.perViewNV) + memberQualifier.perViewNV = currentBlockQualifier.perViewNV; + if (currentBlockQualifier.perTaskNV) + memberQualifier.perTaskNV = currentBlockQualifier.perTaskNV; +#endif + if ((currentBlockQualifier.storage == EvqUniform || currentBlockQualifier.storage == EvqBuffer) && (memberQualifier.isInterpolation() || memberQualifier.isAuxiliary())) + error(memberLoc, "member of uniform or buffer block cannot have an auxiliary or interpolation qualifier", memberType.getFieldName().c_str(), ""); + if (memberType.isArray()) + arraySizesCheck(memberLoc, currentBlockQualifier, memberType.getArraySizes(), nullptr, member == typeList.size() - 1); + if (memberQualifier.hasOffset()) { + if (spvVersion.spv == 0) { + profileRequires(memberLoc, ~EEsProfile, 440, E_GL_ARB_enhanced_layouts, "\"offset\" on block member"); + profileRequires(memberLoc, EEsProfile, 300, E_GL_ARB_enhanced_layouts, "\"offset\" on block member"); + } + } + + if (memberType.containsOpaque()) + error(memberLoc, "member of block cannot be or contain a sampler, image, or atomic_uint type", typeList[member].type->getFieldName().c_str(), ""); + + if (memberType.containsCoopMat()) + error(memberLoc, "member of block cannot be or contain a cooperative matrix type", typeList[member].type->getFieldName().c_str(), ""); + } + + // This might be a redeclaration of a built-in block. If so, redeclareBuiltinBlock() will + // do all the rest. + if (! symbolTable.atBuiltInLevel() && builtInName(*blockName)) { + redeclareBuiltinBlock(loc, typeList, *blockName, instanceName, arraySizes); + return; + } + + // Not a redeclaration of a built-in; check that all names are user names. + reservedErrorCheck(loc, *blockName); + if (instanceName) + reservedErrorCheck(loc, *instanceName); + for (unsigned int member = 0; member < typeList.size(); ++member) + reservedErrorCheck(typeList[member].loc, typeList[member].type->getFieldName()); + + // Make default block qualification, and adjust the member qualifications + + TQualifier defaultQualification; + switch (currentBlockQualifier.storage) { + case EvqUniform: defaultQualification = globalUniformDefaults; break; + case EvqBuffer: defaultQualification = globalBufferDefaults; break; + case EvqVaryingIn: defaultQualification = globalInputDefaults; break; + case EvqVaryingOut: defaultQualification = globalOutputDefaults; break; + default: defaultQualification.clear(); break; + } + + // Special case for "push_constant uniform", which has a default of std430, + // contrary to normal uniform defaults, and can't have a default tracked for it. + if ((currentBlockQualifier.isPushConstant() && !currentBlockQualifier.hasPacking()) || + (currentBlockQualifier.isShaderRecord() && !currentBlockQualifier.hasPacking())) + currentBlockQualifier.layoutPacking = ElpStd430; + + // Special case for "taskNV in/out", which has a default of std430, + if (currentBlockQualifier.isTaskMemory() && !currentBlockQualifier.hasPacking()) + currentBlockQualifier.layoutPacking = ElpStd430; + + // fix and check for member layout qualifiers + + mergeObjectLayoutQualifiers(defaultQualification, currentBlockQualifier, true); + + // "The align qualifier can only be used on blocks or block members, and only for blocks declared with std140 or std430 layouts." + if (currentBlockQualifier.hasAlign()) { + if (defaultQualification.layoutPacking != ElpStd140 && + defaultQualification.layoutPacking != ElpStd430 && + defaultQualification.layoutPacking != ElpScalar) { + error(loc, "can only be used with std140, std430, or scalar layout packing", "align", ""); + defaultQualification.layoutAlign = -1; + } + } + + bool memberWithLocation = false; + bool memberWithoutLocation = false; + bool memberWithPerViewQualifier = false; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; +#ifndef GLSLANG_WEB + if (memberQualifier.hasStream()) { + if (defaultQualification.layoutStream != memberQualifier.layoutStream) + error(memberLoc, "member cannot contradict block", "stream", ""); + } + + // "This includes a block's inheritance of the + // current global default buffer, a block member's inheritance of the block's + // buffer, and the requirement that any *xfb_buffer* declared on a block + // member must match the buffer inherited from the block." + if (memberQualifier.hasXfbBuffer()) { + if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer) + error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); + } +#endif + + if (memberQualifier.hasPacking()) + error(memberLoc, "member of block cannot have a packing layout qualifier", typeList[member].type->getFieldName().c_str(), ""); + if (memberQualifier.hasLocation()) { + const char* feature = "location on block member"; + switch (currentBlockQualifier.storage) { +#ifndef GLSLANG_WEB + case EvqVaryingIn: + case EvqVaryingOut: + requireProfile(memberLoc, ECoreProfile | ECompatibilityProfile | EEsProfile, feature); + profileRequires(memberLoc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, feature); + profileRequires(memberLoc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature); + memberWithLocation = true; + break; +#endif + default: + error(memberLoc, "can only use in an in/out block", feature, ""); + break; + } + } else + memberWithoutLocation = true; + + // "The offset qualifier can only be used on block members of blocks declared with std140 or std430 layouts." + // "The align qualifier can only be used on blocks or block members, and only for blocks declared with std140 or std430 layouts." + if (memberQualifier.hasAlign() || memberQualifier.hasOffset()) { + if (defaultQualification.layoutPacking != ElpStd140 && + defaultQualification.layoutPacking != ElpStd430 && + defaultQualification.layoutPacking != ElpScalar) + error(memberLoc, "can only be used with std140, std430, or scalar layout packing", "offset/align", ""); + } + + if (memberQualifier.isPerView()) { + memberWithPerViewQualifier = true; + } + + TQualifier newMemberQualification = defaultQualification; + mergeQualifiers(memberLoc, newMemberQualification, memberQualifier, false); + memberQualifier = newMemberQualification; + } + + layoutMemberLocationArrayCheck(loc, memberWithLocation, arraySizes); + +#ifndef GLSLANG_WEB + // Ensure that the block has an XfbBuffer assigned. This is needed + // because if the block has a XfbOffset assigned, then it is + // assumed that it has implicitly assigned the current global + // XfbBuffer, and because it's members need to be assigned a + // XfbOffset if they lack it. + if (currentBlockQualifier.storage == EvqVaryingOut && globalOutputDefaults.hasXfbBuffer()) { + if (!currentBlockQualifier.hasXfbBuffer() && currentBlockQualifier.hasXfbOffset()) + currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; + } +#endif + + // Process the members + fixBlockLocations(loc, currentBlockQualifier, typeList, memberWithLocation, memberWithoutLocation); + fixXfbOffsets(currentBlockQualifier, typeList); + fixBlockUniformOffsets(currentBlockQualifier, typeList); + fixBlockUniformLayoutMatrix(currentBlockQualifier, &typeList, nullptr); + fixBlockUniformLayoutPacking(currentBlockQualifier, &typeList, nullptr); + for (unsigned int member = 0; member < typeList.size(); ++member) + layoutTypeCheck(typeList[member].loc, *typeList[member].type); + +#ifndef GLSLANG_WEB + if (memberWithPerViewQualifier) { + for (unsigned int member = 0; member < typeList.size(); ++member) { + checkAndResizeMeshViewDim(typeList[member].loc, *typeList[member].type, /*isBlockMember*/ true); + } + } +#endif + + // reverse merge, so that currentBlockQualifier now has all layout information + // (can't use defaultQualification directly, it's missing other non-layout-default-class qualifiers) + mergeObjectLayoutQualifiers(currentBlockQualifier, defaultQualification, true); + + // + // Build and add the interface block as a new type named 'blockName' + // + + TType blockType(&typeList, *blockName, currentBlockQualifier); + if (arraySizes != nullptr) + blockType.transferArraySizes(arraySizes); + +#ifndef GLSLANG_WEB + if (arraySizes == nullptr) + ioArrayCheck(loc, blockType, instanceName ? *instanceName : *blockName); + if (currentBlockQualifier.hasBufferReference()) { + + if (currentBlockQualifier.storage != EvqBuffer) + error(loc, "can only be used with buffer", "buffer_reference", ""); + + // Create the block reference type. If it was forward-declared, detect that + // as a referent struct type with no members. Replace the referent type with + // blockType. + TType blockNameType(EbtReference, blockType, *blockName); + TVariable* blockNameVar = new TVariable(blockName, blockNameType, true); + if (! symbolTable.insert(*blockNameVar)) { + TSymbol* existingName = symbolTable.find(*blockName); + if (existingName->getType().isReference() && + existingName->getType().getReferentType()->getStruct() && + existingName->getType().getReferentType()->getStruct()->size() == 0 && + existingName->getType().getQualifier().storage == blockType.getQualifier().storage) { + existingName->getType().getReferentType()->deepCopy(blockType); + } else { + error(loc, "block name cannot be redefined", blockName->c_str(), ""); + } + } + if (!instanceName) { + return; + } + } else +#endif + { + // + // Don't make a user-defined type out of block name; that will cause an error + // if the same block name gets reused in a different interface. + // + // "Block names have no other use within a shader + // beyond interface matching; it is a compile-time error to use a block name at global scope for anything + // other than as a block name (e.g., use of a block name for a global variable name or function name is + // currently reserved)." + // + // Use the symbol table to prevent normal reuse of the block's name, as a variable entry, + // whose type is EbtBlock, but without all the structure; that will come from the type + // the instances point to. + // + TType blockNameType(EbtBlock, blockType.getQualifier().storage); + TVariable* blockNameVar = new TVariable(blockName, blockNameType); + if (! symbolTable.insert(*blockNameVar)) { + TSymbol* existingName = symbolTable.find(*blockName); + if (existingName->getType().getBasicType() == EbtBlock) { + if (existingName->getType().getQualifier().storage == blockType.getQualifier().storage) { + error(loc, "Cannot reuse block name within the same interface:", blockName->c_str(), blockType.getStorageQualifierString()); + return; + } + } else { + error(loc, "block name cannot redefine a non-block name", blockName->c_str(), ""); + return; + } + } + } + + // Add the variable, as anonymous or named instanceName. + // Make an anonymous variable if no name was provided. + if (! instanceName) + instanceName = NewPoolTString(""); + + TVariable& variable = *new TVariable(instanceName, blockType); + if (! symbolTable.insert(variable)) { + if (*instanceName == "") + error(loc, "nameless block contains a member that already has a name at global scope", blockName->c_str(), ""); + else + error(loc, "block instance name redefinition", variable.getName().c_str(), ""); + + return; + } + + // Check for general layout qualifier errors + layoutObjectCheck(loc, variable); + +#ifndef GLSLANG_WEB + // fix up + if (isIoResizeArray(blockType)) { + ioArraySymbolResizeList.push_back(&variable); + checkIoArraysConsistency(loc, true); + } else + fixIoArraySize(loc, variable.getWritableType()); +#endif + + // Save it in the AST for linker use. + trackLinkage(variable); +} + +// Do all block-declaration checking regarding the combination of in/out/uniform/buffer +// with a particular stage. +void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + const char *extsrt[2] = { E_GL_NV_ray_tracing, E_GL_EXT_ray_tracing }; + switch (qualifier.storage) { + case EvqUniform: + profileRequires(loc, EEsProfile, 300, nullptr, "uniform block"); + profileRequires(loc, ENoProfile, 140, E_GL_ARB_uniform_buffer_object, "uniform block"); + if (currentBlockQualifier.layoutPacking == ElpStd430 && ! currentBlockQualifier.isPushConstant()) + requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "std430 requires the buffer storage qualifier"); + break; + case EvqBuffer: + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "buffer block"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_shader_storage_buffer_object, "buffer block"); + profileRequires(loc, EEsProfile, 310, nullptr, "buffer block"); + break; + case EvqVaryingIn: + profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "input block"); + // It is a compile-time error to have an input block in a vertex shader or an output block in a fragment shader + // "Compute shaders do not permit user-defined input variables..." + requireStage(loc, (EShLanguageMask)(EShLangTessControlMask|EShLangTessEvaluationMask|EShLangGeometryMask| + EShLangFragmentMask|EShLangMeshNVMask), "input block"); + if (language == EShLangFragment) { + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "fragment input block"); + } else if (language == EShLangMeshNV && ! qualifier.isTaskMemory()) { + error(loc, "input blocks cannot be used in a mesh shader", "out", ""); + } + break; + case EvqVaryingOut: + profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "output block"); + requireStage(loc, (EShLanguageMask)(EShLangVertexMask|EShLangTessControlMask|EShLangTessEvaluationMask| + EShLangGeometryMask|EShLangMeshNVMask|EShLangTaskNVMask), "output block"); + // ES 310 can have a block before shader_io is turned on, so skip this test for built-ins + if (language == EShLangVertex && ! parsingBuiltins) { + profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "vertex output block"); + } else if (language == EShLangMeshNV && qualifier.isTaskMemory()) { + error(loc, "can only use on input blocks in mesh shader", "taskNV", ""); + } else if (language == EShLangTaskNV && ! qualifier.isTaskMemory()) { + error(loc, "output blocks cannot be used in a task shader", "out", ""); + } + break; +#ifndef GLSLANG_WEB + case EvqPayload: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "rayPayloadNV block"); + requireStage(loc, (EShLanguageMask)(EShLangRayGenMask | EShLangAnyHitMask | EShLangClosestHitMask | EShLangMissMask), + "rayPayloadNV block"); + break; + case EvqPayloadIn: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "rayPayloadInNV block"); + requireStage(loc, (EShLanguageMask)(EShLangAnyHitMask | EShLangClosestHitMask | EShLangMissMask), + "rayPayloadInNV block"); + break; + case EvqHitAttr: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "hitAttributeNV block"); + requireStage(loc, (EShLanguageMask)(EShLangIntersectMask | EShLangAnyHitMask | EShLangClosestHitMask), "hitAttributeNV block"); + break; + case EvqCallableData: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "callableDataNV block"); + requireStage(loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), + "callableDataNV block"); + break; + case EvqCallableDataIn: + profileRequires(loc, ~EEsProfile, 460, 2, extsrt, "callableDataInNV block"); + requireStage(loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV block"); + break; +#endif + default: + error(loc, "only uniform, buffer, in, or out blocks are supported", blockName->c_str(), ""); + break; + } +} + +// Do all block-declaration checking regarding its qualifiers. +void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier& qualifier, bool /*instanceName*/) +{ + // The 4.5 specification says: + // + // interface-block : + // layout-qualifieropt interface-qualifier block-name { member-list } instance-nameopt ; + // + // interface-qualifier : + // in + // out + // patch in + // patch out + // uniform + // buffer + // + // Note however memory qualifiers aren't included, yet the specification also says + // + // "...memory qualifiers may also be used in the declaration of shader storage blocks..." + + if (qualifier.isInterpolation()) + error(loc, "cannot use interpolation qualifiers on an interface block", "flat/smooth/noperspective", ""); + if (qualifier.centroid) + error(loc, "cannot use centroid qualifier on an interface block", "centroid", ""); + if (qualifier.isSample()) + error(loc, "cannot use sample qualifier on an interface block", "sample", ""); + if (qualifier.invariant) + error(loc, "cannot use invariant qualifier on an interface block", "invariant", ""); + if (qualifier.isPushConstant()) + intermediate.addPushConstantCount(); + if (qualifier.isShaderRecord()) + intermediate.addShaderRecordCount(); + if (qualifier.isTaskMemory()) + intermediate.addTaskNVCount(); +} + +// +// "For a block, this process applies to the entire block, or until the first member +// is reached that has a location layout qualifier. When a block member is declared with a location +// qualifier, its location comes from that qualifier: The member's location qualifier overrides the block-level +// declaration. Subsequent members are again assigned consecutive locations, based on the newest location, +// until the next member declared with a location qualifier. The values used for locations do not have to be +// declared in increasing order." +void TParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifier, TTypeList& typeList, bool memberWithLocation, bool memberWithoutLocation) +{ + // "If a block has no block-level location layout qualifier, it is required that either all or none of its members + // have a location layout qualifier, or a compile-time error results." + if (! qualifier.hasLocation() && memberWithLocation && memberWithoutLocation) + error(loc, "either the block needs a location, or all members need a location, or no members have a location", "location", ""); + else { + if (memberWithLocation) { + // remove any block-level location and make it per *every* member + int nextLocation = 0; // by the rule above, initial value is not relevant + if (qualifier.hasAnyLocation()) { + nextLocation = qualifier.layoutLocation; + qualifier.layoutLocation = TQualifier::layoutLocationEnd; + if (qualifier.hasComponent()) { + // "It is a compile-time error to apply the *component* qualifier to a ... block" + error(loc, "cannot apply to a block", "component", ""); + } + if (qualifier.hasIndex()) { + error(loc, "cannot apply to a block", "index", ""); + } + } + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + if (! memberQualifier.hasLocation()) { + if (nextLocation >= (int)TQualifier::layoutLocationEnd) + error(memberLoc, "location is too large", "location", ""); + memberQualifier.layoutLocation = nextLocation; + memberQualifier.layoutComponent = TQualifier::layoutComponentEnd; + } + nextLocation = memberQualifier.layoutLocation + intermediate.computeTypeLocationSize( + *typeList[member].type, language); + } + } + } +} + +void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) +{ +#ifndef GLSLANG_WEB + // "If a block is qualified with xfb_offset, all its + // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any + // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer + // offsets." + + if (! qualifier.hasXfbBuffer() || ! qualifier.hasXfbOffset()) + return; + + int nextOffset = qualifier.layoutXfbOffset; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + bool contains64BitType = false; + bool contains32BitType = false; + bool contains16BitType = false; + int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType); + // see if we need to auto-assign an offset to this member + if (! memberQualifier.hasXfbOffset()) { + // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8" + if (contains64BitType) + RoundToPow2(nextOffset, 8); + else if (contains32BitType) + RoundToPow2(nextOffset, 4); + else if (contains16BitType) + RoundToPow2(nextOffset, 2); + memberQualifier.layoutXfbOffset = nextOffset; + } else + nextOffset = memberQualifier.layoutXfbOffset; + nextOffset += memberSize; + } + + // The above gave all block members an offset, so we can take it off the block now, + // which will avoid double counting the offset usage. + qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd; +#endif +} + +// Calculate and save the offset of each block member, using the recursively +// defined block offset rules and the user-provided offset and align. +// +// Also, compute and save the total size of the block. For the block's size, arrayness +// is not taken into account, as each element is backed by a separate buffer. +// +void TParseContext::fixBlockUniformOffsets(TQualifier& qualifier, TTypeList& typeList) +{ + if (!qualifier.isUniformOrBuffer() && !qualifier.isTaskMemory()) + return; + if (qualifier.layoutPacking != ElpStd140 && qualifier.layoutPacking != ElpStd430 && qualifier.layoutPacking != ElpScalar) + return; + + int offset = 0; + int memberSize; + for (unsigned int member = 0; member < typeList.size(); ++member) { + TQualifier& memberQualifier = typeList[member].type->getQualifier(); + const TSourceLoc& memberLoc = typeList[member].loc; + + // "When align is applied to an array, it effects only the start of the array, not the array's internal stride." + + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = typeList[member].type->getQualifier().layoutMatrix; + int dummyStride; + int memberAlignment = intermediate.getMemberAlignment(*typeList[member].type, memberSize, dummyStride, qualifier.layoutPacking, + subMatrixLayout != ElmNone ? subMatrixLayout == ElmRowMajor : qualifier.layoutMatrix == ElmRowMajor); + if (memberQualifier.hasOffset()) { + // "The specified offset must be a multiple + // of the base alignment of the type of the block member it qualifies, or a compile-time error results." + if (! IsMultipleOfPow2(memberQualifier.layoutOffset, memberAlignment)) + error(memberLoc, "must be a multiple of the member's alignment", "offset", ""); + + // GLSL: "It is a compile-time error to specify an offset that is smaller than the offset of the previous + // member in the block or that lies within the previous member of the block" + if (spvVersion.spv == 0) { + if (memberQualifier.layoutOffset < offset) + error(memberLoc, "cannot lie in previous members", "offset", ""); + + // "The offset qualifier forces the qualified member to start at or after the specified + // integral-constant expression, which will be its byte offset from the beginning of the buffer. + // "The actual offset of a member is computed as + // follows: If offset was declared, start with that offset, otherwise start with the next available offset." + offset = std::max(offset, memberQualifier.layoutOffset); + } else { + // TODO: Vulkan: "It is a compile-time error to have any offset, explicit or assigned, + // that lies within another member of the block." + + offset = memberQualifier.layoutOffset; + } + } + + // "The actual alignment of a member will be the greater of the specified align alignment and the standard + // (e.g., std140) base alignment for the member's type." + if (memberQualifier.hasAlign()) + memberAlignment = std::max(memberAlignment, memberQualifier.layoutAlign); + + // "If the resulting offset is not a multiple of the actual alignment, + // increase it to the first offset that is a multiple of + // the actual alignment." + RoundToPow2(offset, memberAlignment); + typeList[member].type->getQualifier().layoutOffset = offset; + offset += memberSize; + } +} + +// +// Spread LayoutMatrix to uniform block member, if a uniform block member is a struct, +// we need spread LayoutMatrix to this struct member too. and keep this rule for recursive. +// +void TParseContext::fixBlockUniformLayoutMatrix(TQualifier& qualifier, TTypeList* originTypeList, + TTypeList* tmpTypeList) +{ + assert(tmpTypeList == nullptr || originTypeList->size() == tmpTypeList->size()); + for (unsigned int member = 0; member < originTypeList->size(); ++member) { + if (qualifier.layoutPacking != ElpNone) { + if (tmpTypeList == nullptr) { + if (((*originTypeList)[member].type->isMatrix() || + (*originTypeList)[member].type->getBasicType() == EbtStruct) && + (*originTypeList)[member].type->getQualifier().layoutMatrix == ElmNone) { + (*originTypeList)[member].type->getQualifier().layoutMatrix = qualifier.layoutMatrix; + } + } else { + if (((*tmpTypeList)[member].type->isMatrix() || + (*tmpTypeList)[member].type->getBasicType() == EbtStruct) && + (*tmpTypeList)[member].type->getQualifier().layoutMatrix == ElmNone) { + (*tmpTypeList)[member].type->getQualifier().layoutMatrix = qualifier.layoutMatrix; + } + } + } + + if ((*originTypeList)[member].type->getBasicType() == EbtStruct) { + TQualifier* memberQualifier = nullptr; + // block member can be declare a matrix style, so it should be update to the member's style + if ((*originTypeList)[member].type->getQualifier().layoutMatrix == ElmNone) { + memberQualifier = &qualifier; + } else { + memberQualifier = &((*originTypeList)[member].type->getQualifier()); + } + + const TType* tmpType = tmpTypeList == nullptr ? + (*originTypeList)[member].type->clone() : (*tmpTypeList)[member].type; + + fixBlockUniformLayoutMatrix(*memberQualifier, (*originTypeList)[member].type->getWritableStruct(), + tmpType->getWritableStruct()); + + const TTypeList* structure = recordStructCopy(matrixFixRecord, (*originTypeList)[member].type, tmpType); + + if (tmpTypeList == nullptr) { + (*originTypeList)[member].type->setStruct(const_cast(structure)); + } + if (tmpTypeList != nullptr) { + (*tmpTypeList)[member].type->setStruct(const_cast(structure)); + } + } + } +} + +// +// Spread LayoutPacking to block member, if a block member is a struct, we need spread LayoutPacking to +// this struct member too. and keep this rule for recursive. +// +void TParseContext::fixBlockUniformLayoutPacking(TQualifier& qualifier, TTypeList* originTypeList, + TTypeList* tmpTypeList) +{ + assert(tmpTypeList == nullptr || originTypeList->size() == tmpTypeList->size()); + for (unsigned int member = 0; member < originTypeList->size(); ++member) { + if (qualifier.layoutPacking != ElpNone) { + if (tmpTypeList == nullptr) { + if ((*originTypeList)[member].type->getQualifier().layoutPacking == ElpNone) { + (*originTypeList)[member].type->getQualifier().layoutPacking = qualifier.layoutPacking; + } + } else { + if ((*tmpTypeList)[member].type->getQualifier().layoutPacking == ElpNone) { + (*tmpTypeList)[member].type->getQualifier().layoutPacking = qualifier.layoutPacking; + } + } + } + + if ((*originTypeList)[member].type->getBasicType() == EbtStruct) { + // Deep copy the type in pool. + // Because, struct use in different block may have different layout qualifier. + // We have to new a object to distinguish between them. + const TType* tmpType = tmpTypeList == nullptr ? + (*originTypeList)[member].type->clone() : (*tmpTypeList)[member].type; + + fixBlockUniformLayoutPacking(qualifier, (*originTypeList)[member].type->getWritableStruct(), + tmpType->getWritableStruct()); + + const TTypeList* structure = recordStructCopy(packingFixRecord, (*originTypeList)[member].type, tmpType); + + if (tmpTypeList == nullptr) { + (*originTypeList)[member].type->setStruct(const_cast(structure)); + } + if (tmpTypeList != nullptr) { + (*tmpTypeList)[member].type->setStruct(const_cast(structure)); + } + } + } +} + +// For an identifier that is already declared, add more qualification to it. +void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, const TString& identifier) +{ + TSymbol* symbol = symbolTable.find(identifier); + + // A forward declaration of a block reference looks to the grammar like adding + // a qualifier to an existing symbol. Detect this and create the block reference + // type with an empty type list, which will be filled in later in + // TParseContext::declareBlock. + if (!symbol && qualifier.hasBufferReference()) { + TTypeList typeList; + TType blockType(&typeList, identifier, qualifier);; + TType blockNameType(EbtReference, blockType, identifier); + TVariable* blockNameVar = new TVariable(&identifier, blockNameType, true); + if (! symbolTable.insert(*blockNameVar)) { + error(loc, "block name cannot redefine a non-block name", blockName->c_str(), ""); + } + return; + } + + if (! symbol) { + error(loc, "identifier not previously declared", identifier.c_str(), ""); + return; + } + if (symbol->getAsFunction()) { + error(loc, "cannot re-qualify a function name", identifier.c_str(), ""); + return; + } + + if (qualifier.isAuxiliary() || + qualifier.isMemory() || + qualifier.isInterpolation() || + qualifier.hasLayout() || + qualifier.storage != EvqTemporary || + qualifier.precision != EpqNone) { + error(loc, "cannot add storage, auxiliary, memory, interpolation, layout, or precision qualifier to an existing variable", identifier.c_str(), ""); + return; + } + + // For read-only built-ins, add a new symbol for holding the modified qualifier. + // This will bring up an entire block, if a block type has to be modified (e.g., gl_Position inside a block) + if (symbol->isReadOnly()) + symbol = symbolTable.copyUp(symbol); + + if (qualifier.invariant) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "invariant", ""); + symbol->getWritableType().getQualifier().invariant = true; + invariantCheck(loc, symbol->getType().getQualifier()); + } else if (qualifier.isNoContraction()) { + if (intermediate.inIoAccessed(identifier)) + error(loc, "cannot change qualification after use", "precise", ""); + symbol->getWritableType().getQualifier().setNoContraction(); + } else if (qualifier.specConstant) { + symbol->getWritableType().getQualifier().makeSpecConstant(); + if (qualifier.hasSpecConstantId()) + symbol->getWritableType().getQualifier().layoutSpecConstantId = qualifier.layoutSpecConstantId; + } else + warn(loc, "unknown requalification", "", ""); +} + +void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qualifier, TIdentifierList& identifiers) +{ + for (unsigned int i = 0; i < identifiers.size(); ++i) + addQualifierToExisting(loc, qualifier, *identifiers[i]); +} + +// Make sure 'invariant' isn't being applied to a non-allowed object. +void TParseContext::invariantCheck(const TSourceLoc& loc, const TQualifier& qualifier) +{ + if (! qualifier.invariant) + return; + + bool pipeOut = qualifier.isPipeOutput(); + bool pipeIn = qualifier.isPipeInput(); + if ((version >= 300 && isEsProfile()) || (!isEsProfile() && version >= 420)) { + if (! pipeOut) + error(loc, "can only apply to an output", "invariant", ""); + } else { + if ((language == EShLangVertex && pipeIn) || (! pipeOut && ! pipeIn)) + error(loc, "can only apply to an output, or to an input in a non-vertex stage\n", "invariant", ""); + } +} + +// +// Updating default qualifier for the case of a declaration with just a qualifier, +// no type, block, or identifier. +// +void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) +{ +#ifndef GLSLANG_WEB + if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) { + assert(language == EShLangTessControl || language == EShLangGeometry || language == EShLangMeshNV); + const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices"; + + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", id, ""); + if (! intermediate.setVertices(publicType.shaderQualifiers.vertices)) + error(loc, "cannot change previously set layout value", id, ""); + + if (language == EShLangTessControl) + checkIoArraysConsistency(loc); + } + if (publicType.shaderQualifiers.primitives != TQualifier::layoutNotSet) { + assert(language == EShLangMeshNV); + const char* id = "max_primitives"; + + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", id, ""); + if (! intermediate.setPrimitives(publicType.shaderQualifiers.primitives)) + error(loc, "cannot change previously set layout value", id, ""); + } + if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) { + if (publicType.qualifier.storage != EvqVaryingIn) + error(loc, "can only apply to 'in'", "invocations", ""); + if (! intermediate.setInvocations(publicType.shaderQualifiers.invocations)) + error(loc, "cannot change previously set layout value", "invocations", ""); + } + if (publicType.shaderQualifiers.geometry != ElgNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + switch (publicType.shaderQualifiers.geometry) { + case ElgPoints: + case ElgLines: + case ElgLinesAdjacency: + case ElgTriangles: + case ElgTrianglesAdjacency: + case ElgQuads: + case ElgIsolines: + if (language == EShLangMeshNV) { + error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + } + if (intermediate.setInputPrimitive(publicType.shaderQualifiers.geometry)) { + if (language == EShLangGeometry) + checkIoArraysConsistency(loc); + } else + error(loc, "cannot change previously set input primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + default: + error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + } + } else if (publicType.qualifier.storage == EvqVaryingOut) { + switch (publicType.shaderQualifiers.geometry) { + case ElgLines: + case ElgTriangles: + if (language != EShLangMeshNV) { + error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + } + // Fall through + case ElgPoints: + case ElgLineStrip: + case ElgTriangleStrip: + if (! intermediate.setOutputPrimitive(publicType.shaderQualifiers.geometry)) + error(loc, "cannot change previously set output primitive", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + break; + default: + error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); + } + } else + error(loc, "cannot apply to:", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), GetStorageQualifierString(publicType.qualifier.storage)); + } + if (publicType.shaderQualifiers.spacing != EvsNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setVertexSpacing(publicType.shaderQualifiers.spacing)) + error(loc, "cannot change previously set vertex spacing", TQualifier::getVertexSpacingString(publicType.shaderQualifiers.spacing), ""); + } else + error(loc, "can only apply to 'in'", TQualifier::getVertexSpacingString(publicType.shaderQualifiers.spacing), ""); + } + if (publicType.shaderQualifiers.order != EvoNone) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setVertexOrder(publicType.shaderQualifiers.order)) + error(loc, "cannot change previously set vertex order", TQualifier::getVertexOrderString(publicType.shaderQualifiers.order), ""); + } else + error(loc, "can only apply to 'in'", TQualifier::getVertexOrderString(publicType.shaderQualifiers.order), ""); + } + if (publicType.shaderQualifiers.pointMode) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setPointMode(); + else + error(loc, "can only apply to 'in'", "point_mode", ""); + } +#endif + for (int i = 0; i < 3; ++i) { + if (publicType.shaderQualifiers.localSizeNotDefault[i]) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setLocalSize(i, publicType.shaderQualifiers.localSize[i])) + error(loc, "cannot change previously set size", "local_size", ""); + else { + int max = 0; + if (language == EShLangCompute) { + switch (i) { + case 0: max = resources.maxComputeWorkGroupSizeX; break; + case 1: max = resources.maxComputeWorkGroupSizeY; break; + case 2: max = resources.maxComputeWorkGroupSizeZ; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", ""); + } +#ifndef GLSLANG_WEB + else if (language == EShLangMeshNV) { + switch (i) { + case 0: max = resources.maxMeshWorkGroupSizeX_NV; break; + case 1: max = resources.maxMeshWorkGroupSizeY_NV; break; + case 2: max = resources.maxMeshWorkGroupSizeZ_NV; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxMeshWorkGroupSizeNV", "local_size", ""); + } else if (language == EShLangTaskNV) { + switch (i) { + case 0: max = resources.maxTaskWorkGroupSizeX_NV; break; + case 1: max = resources.maxTaskWorkGroupSizeY_NV; break; + case 2: max = resources.maxTaskWorkGroupSizeZ_NV; break; + default: break; + } + if (intermediate.getLocalSize(i) > (unsigned int)max) + error(loc, "too large; see gl_MaxTaskWorkGroupSizeNV", "local_size", ""); + } +#endif + else { + assert(0); + } + + // Fix the existing constant gl_WorkGroupSize with this new information. + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + if (workGroupSize != nullptr) + workGroupSize->getWritableConstArray()[i].setUConst(intermediate.getLocalSize(i)); + } + } else + error(loc, "can only apply to 'in'", "local_size", ""); + } + if (publicType.shaderQualifiers.localSizeSpecId[i] != TQualifier::layoutNotSet) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (! intermediate.setLocalSizeSpecId(i, publicType.shaderQualifiers.localSizeSpecId[i])) + error(loc, "cannot change previously set size", "local_size", ""); + } else + error(loc, "can only apply to 'in'", "local_size id", ""); + // Set the workgroup built-in variable as a specialization constant + TVariable* workGroupSize = getEditableVariable("gl_WorkGroupSize"); + if (workGroupSize != nullptr) + workGroupSize->getWritableType().getQualifier().specConstant = true; + } + } + +#ifndef GLSLANG_WEB + if (publicType.shaderQualifiers.earlyFragmentTests) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setEarlyFragmentTests(); + else + error(loc, "can only apply to 'in'", "early_fragment_tests", ""); + } + if (publicType.shaderQualifiers.postDepthCoverage) { + if (publicType.qualifier.storage == EvqVaryingIn) + intermediate.setPostDepthCoverage(); + else + error(loc, "can only apply to 'in'", "post_coverage_coverage", ""); + } + if (publicType.shaderQualifiers.hasBlendEquation()) { + if (publicType.qualifier.storage != EvqVaryingOut) + error(loc, "can only apply to 'out'", "blend equation", ""); + } + if (publicType.shaderQualifiers.interlockOrdering) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (!intermediate.setInterlockOrdering(publicType.shaderQualifiers.interlockOrdering)) + error(loc, "cannot change previously set fragment shader interlock ordering", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), ""); + } + else + error(loc, "can only apply to 'in'", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), ""); + } + + if (publicType.shaderQualifiers.layoutDerivativeGroupQuads && + publicType.shaderQualifiers.layoutDerivativeGroupLinear) { + error(loc, "cannot be both specified", "derivative_group_quadsNV and derivative_group_linearNV", ""); + } + + if (publicType.shaderQualifiers.layoutDerivativeGroupQuads) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if ((intermediate.getLocalSize(0) & 1) || + (intermediate.getLocalSize(1) & 1)) + error(loc, "requires local_size_x and local_size_y to be multiple of two", "derivative_group_quadsNV", ""); + else + intermediate.setLayoutDerivativeMode(LayoutDerivativeGroupQuads); + } + else + error(loc, "can only apply to 'in'", "derivative_group_quadsNV", ""); + } + if (publicType.shaderQualifiers.layoutDerivativeGroupLinear) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if((intermediate.getLocalSize(0) * + intermediate.getLocalSize(1) * + intermediate.getLocalSize(2)) % 4 != 0) + error(loc, "requires total group size to be multiple of four", "derivative_group_linearNV", ""); + else + intermediate.setLayoutDerivativeMode(LayoutDerivativeGroupLinear); + } + else + error(loc, "can only apply to 'in'", "derivative_group_linearNV", ""); + } + // Check mesh out array sizes, once all the necessary out qualifiers are defined. + if ((language == EShLangMeshNV) && + (intermediate.getVertices() != TQualifier::layoutNotSet) && + (intermediate.getPrimitives() != TQualifier::layoutNotSet) && + (intermediate.getOutputPrimitive() != ElgNone)) + { + checkIoArraysConsistency(loc); + } + + if (publicType.shaderQualifiers.layoutPrimitiveCulling) { + if (publicType.qualifier.storage != EvqTemporary) + error(loc, "layout qualifier can not have storage qualifiers", "primitive_culling","", ""); + else { + intermediate.setLayoutPrimitiveCulling(); + } + // Exit early as further checks are not valid + return; + } +#endif + const TQualifier& qualifier = publicType.qualifier; + + if (qualifier.isAuxiliary() || + qualifier.isMemory() || + qualifier.isInterpolation() || + qualifier.precision != EpqNone) + error(loc, "cannot use auxiliary, memory, interpolation, or precision qualifier in a default qualifier declaration (declaration with no type)", "qualifier", ""); + + // "The offset qualifier can only be used on block members of blocks..." + // "The align qualifier can only be used on blocks or block members..." + if (qualifier.hasOffset() || + qualifier.hasAlign()) + error(loc, "cannot use offset or align qualifiers in a default qualifier declaration (declaration with no type)", "layout qualifier", ""); + + layoutQualifierCheck(loc, qualifier); + + switch (qualifier.storage) { + case EvqUniform: + if (qualifier.hasMatrix()) + globalUniformDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalUniformDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqBuffer: + if (qualifier.hasMatrix()) + globalBufferDefaults.layoutMatrix = qualifier.layoutMatrix; + if (qualifier.hasPacking()) + globalBufferDefaults.layoutPacking = qualifier.layoutPacking; + break; + case EvqVaryingIn: + break; + case EvqVaryingOut: +#ifndef GLSLANG_WEB + if (qualifier.hasStream()) + globalOutputDefaults.layoutStream = qualifier.layoutStream; + if (qualifier.hasXfbBuffer()) + globalOutputDefaults.layoutXfbBuffer = qualifier.layoutXfbBuffer; + if (globalOutputDefaults.hasXfbBuffer() && qualifier.hasXfbStride()) { + if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride)) + error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); + } +#endif + break; + default: + error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); + return; + } + + if (qualifier.hasBinding()) + error(loc, "cannot declare a default, include a type or full declaration", "binding", ""); + if (qualifier.hasAnyLocation()) + error(loc, "cannot declare a default, use a full declaration", "location/component/index", ""); + if (qualifier.hasXfbOffset()) + error(loc, "cannot declare a default, use a full declaration", "xfb_offset", ""); + if (qualifier.isPushConstant()) + error(loc, "cannot declare a default, can only be used on a block", "push_constant", ""); + if (qualifier.hasBufferReference()) + error(loc, "cannot declare a default, can only be used on a block", "buffer_reference", ""); + if (qualifier.hasSpecConstantId()) + error(loc, "cannot declare a default, can only be used on a scalar", "constant_id", ""); + if (qualifier.isShaderRecord()) + error(loc, "cannot declare a default, can only be used on a block", "shaderRecordNV", ""); +} + +// +// Take the sequence of statements that has been built up since the last case/default, +// put it on the list of top-level nodes for the current (inner-most) switch statement, +// and follow that by the case/default we are on now. (See switch topology comment on +// TIntermSwitch.) +// +void TParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) +{ + TIntermSequence* switchSequence = switchSequenceStack.back(); + + if (statements) { + if (switchSequence->size() == 0) + error(statements->getLoc(), "cannot have statements before first case/default label", "switch", ""); + statements->setOperator(EOpSequence); + switchSequence->push_back(statements); + } + if (branchNode) { + // check all previous cases for the same label (or both are 'default') + for (unsigned int s = 0; s < switchSequence->size(); ++s) { + TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode(); + if (prevBranch) { + TIntermTyped* prevExpression = prevBranch->getExpression(); + TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression(); + if (prevExpression == nullptr && newExpression == nullptr) + error(branchNode->getLoc(), "duplicate label", "default", ""); + else if (prevExpression != nullptr && + newExpression != nullptr && + prevExpression->getAsConstantUnion() && + newExpression->getAsConstantUnion() && + prevExpression->getAsConstantUnion()->getConstArray()[0].getIConst() == + newExpression->getAsConstantUnion()->getConstArray()[0].getIConst()) + error(branchNode->getLoc(), "duplicated value", "case", ""); + } + } + switchSequence->push_back(branchNode); + } +} + +// +// Turn the top-level node sequence built up of wrapupSwitchSubsequence9) +// into a switch node. +// +TIntermNode* TParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expression, TIntermAggregate* lastStatements) +{ + profileRequires(loc, EEsProfile, 300, nullptr, "switch statements"); + profileRequires(loc, ENoProfile, 130, nullptr, "switch statements"); + + wrapupSwitchSubsequence(lastStatements, nullptr); + + if (expression == nullptr || + (expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint) || + expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) + error(loc, "condition must be a scalar integer expression", "switch", ""); + + // If there is nothing to do, drop the switch but still execute the expression + TIntermSequence* switchSequence = switchSequenceStack.back(); + if (switchSequence->size() == 0) + return expression; + + if (lastStatements == nullptr) { + // This was originally an ERRROR, because early versions of the specification said + // "it is an error to have no statement between a label and the end of the switch statement." + // The specifications were updated to remove this (being ill-defined what a "statement" was), + // so, this became a warning. However, 3.0 tests still check for the error. + if (isEsProfile() && version <= 300 && ! relaxedErrors()) + error(loc, "last case/default label not followed by statements", "switch", ""); + else + warn(loc, "last case/default label not followed by statements", "switch", ""); + + // emulate a break for error recovery + lastStatements = intermediate.makeAggregate(intermediate.addBranch(EOpBreak, loc)); + lastStatements->setOperator(EOpSequence); + switchSequence->push_back(lastStatements); + } + + TIntermAggregate* body = new TIntermAggregate(EOpSequence); + body->getSequence() = *switchSequenceStack.back(); + body->setLoc(loc); + + TIntermSwitch* switchNode = new TIntermSwitch(expression, body); + switchNode->setLoc(loc); + + return switchNode; +} + +// +// When a struct used in block, and has it's own layout packing, layout matrix, +// record the origin structure of a struct to map, and Record the structure copy to the copy table, +// +const TTypeList* TParseContext::recordStructCopy(TStructRecord& record, const TType* originType, const TType* tmpType) +{ + size_t memberCount = tmpType->getStruct()->size(); + size_t originHash = 0, tmpHash = 0; + std::hash hasher; + for (size_t i = 0; i < memberCount; i++) { + size_t originMemberHash = hasher(originType->getStruct()->at(i).type->getQualifier().layoutPacking + + originType->getStruct()->at(i).type->getQualifier().layoutMatrix); + size_t tmpMemberHash = hasher(tmpType->getStruct()->at(i).type->getQualifier().layoutPacking + + tmpType->getStruct()->at(i).type->getQualifier().layoutMatrix); + originHash = hasher((originHash ^ originMemberHash) << 1); + tmpHash = hasher((tmpHash ^ tmpMemberHash) << 1); + } + const TTypeList* originStruct = originType->getStruct(); + const TTypeList* tmpStruct = tmpType->getStruct(); + if (originHash != tmpHash) { + auto fixRecords = record.find(originStruct); + if (fixRecords != record.end()) { + auto fixRecord = fixRecords->second.find(tmpHash); + if (fixRecord != fixRecords->second.end()) { + return fixRecord->second; + } else { + record[originStruct][tmpHash] = tmpStruct; + return tmpStruct; + } + } else { + record[originStruct] = std::map(); + record[originStruct][tmpHash] = tmpStruct; + return tmpStruct; + } + } + return originStruct; +} + +} // end namespace glslang + diff --git a/third_party/glslang/glslang/MachineIndependent/ParseHelper.h b/third_party/glslang/glslang/MachineIndependent/ParseHelper.h new file mode 100644 index 0000000..fe2b6fb --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/ParseHelper.h @@ -0,0 +1,535 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This header defines a two-level parse-helper hierarchy, derived from +// TParseVersions: +// - TParseContextBase: sharable across multiple parsers +// - TParseContext: GLSL specific helper +// + +#ifndef _PARSER_HELPER_INCLUDED_ +#define _PARSER_HELPER_INCLUDED_ + +#include +#include + +#include "parseVersions.h" +#include "../Include/ShHandle.h" +#include "SymbolTable.h" +#include "localintermediate.h" +#include "Scan.h" +#include "attribute.h" + +namespace glslang { + +struct TPragma { + TPragma(bool o, bool d) : optimize(o), debug(d) { } + bool optimize; + bool debug; + TPragmaTable pragmaTable; +}; + +class TScanContext; +class TPpContext; + +typedef std::set TIdSetType; +typedef std::map> TStructRecord; + +// +// Sharable code (as well as what's in TParseVersions) across +// parse helpers. +// +class TParseContextBase : public TParseVersions { +public: + TParseContextBase(TSymbolTable& symbolTable, TIntermediate& interm, bool parsingBuiltins, int version, + EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + TInfoSink& infoSink, bool forwardCompatible, EShMessages messages, + const TString* entryPoint = nullptr) + : TParseVersions(interm, version, profile, spvVersion, language, infoSink, forwardCompatible, messages), + scopeMangler("::"), + symbolTable(symbolTable), + statementNestingLevel(0), loopNestingLevel(0), structNestingLevel(0), blockNestingLevel(0), controlFlowNestingLevel(0), + currentFunctionType(nullptr), + postEntryPointReturn(false), + contextPragma(true, false), + beginInvocationInterlockCount(0), endInvocationInterlockCount(0), + parsingBuiltins(parsingBuiltins), scanContext(nullptr), ppContext(nullptr), + limits(resources.limits), + globalUniformBlock(nullptr), + globalUniformBinding(TQualifier::layoutBindingEnd), + globalUniformSet(TQualifier::layoutSetEnd) + { + if (entryPoint != nullptr) + sourceEntryPointName = *entryPoint; + } + virtual ~TParseContextBase() { } + +#if !defined(GLSLANG_WEB) || defined(GLSLANG_WEB_DEVEL) + virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); + virtual void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...); +#endif + + virtual void setLimits(const TBuiltInResource&) = 0; + + void checkIndex(const TSourceLoc&, const TType&, int& index); + + EShLanguage getLanguage() const { return language; } + void setScanContext(TScanContext* c) { scanContext = c; } + TScanContext* getScanContext() const { return scanContext; } + void setPpContext(TPpContext* c) { ppContext = c; } + TPpContext* getPpContext() const { return ppContext; } + + virtual void setLineCallback(const std::function& func) { lineCallback = func; } + virtual void setExtensionCallback(const std::function& func) { extensionCallback = func; } + virtual void setVersionCallback(const std::function& func) { versionCallback = func; } + virtual void setPragmaCallback(const std::function&)>& func) { pragmaCallback = func; } + virtual void setErrorCallback(const std::function& func) { errorCallback = func; } + + virtual void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) = 0; + virtual bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) = 0; + virtual bool lineDirectiveShouldSetNextLine() const = 0; + virtual void handlePragma(const TSourceLoc&, const TVector&) = 0; + + virtual bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) = 0; + + virtual void notifyVersion(int line, int version, const char* type_string) + { + if (versionCallback) + versionCallback(line, version, type_string); + } + virtual void notifyErrorDirective(int line, const char* error_message) + { + if (errorCallback) + errorCallback(line, error_message); + } + virtual void notifyLineDirective(int curLineNo, int newLineNo, bool hasSource, int sourceNum, const char* sourceName) + { + if (lineCallback) + lineCallback(curLineNo, newLineNo, hasSource, sourceNum, sourceName); + } + virtual void notifyExtensionDirective(int line, const char* extension, const char* behavior) + { + if (extensionCallback) + extensionCallback(line, extension, behavior); + } + +#ifdef ENABLE_HLSL + // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL) + virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr); +#endif + + // Potentially rename shader entry point function + void renameShaderFunction(TString*& name) const + { + // Replace the entry point name given in the shader with the real entry point name, + // if there is a substitution. + if (name != nullptr && *name == sourceEntryPointName && intermediate.getEntryPointName().size() > 0) + name = NewPoolTString(intermediate.getEntryPointName().c_str()); + } + + virtual bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*); + virtual void rValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*); + + const char* const scopeMangler; + + // Basic parsing state, easily accessible to the grammar + + TSymbolTable& symbolTable; // symbol table that goes with the current language, version, and profile + int statementNestingLevel; // 0 if outside all flow control or compound statements + int loopNestingLevel; // 0 if outside all loops + int structNestingLevel; // 0 if outside structures + int blockNestingLevel; // 0 if outside blocks + int controlFlowNestingLevel; // 0 if outside all flow control + const TType* currentFunctionType; // the return type of the function that's currently being parsed + bool functionReturnsValue; // true if a non-void function has a return + // if inside a function, true if the function is the entry point and this is after a return statement + bool postEntryPointReturn; + // case, node, case, case, node, ...; ensure only one node between cases; stack of them for nesting + TList switchSequenceStack; + // the statementNestingLevel the current switch statement is at, which must match the level of its case statements + TList switchLevel; + struct TPragma contextPragma; + int beginInvocationInterlockCount; + int endInvocationInterlockCount; + +protected: + TParseContextBase(TParseContextBase&); + TParseContextBase& operator=(TParseContextBase&); + + const bool parsingBuiltins; // true if parsing built-in symbols/functions + TVector linkageSymbols; // will be transferred to 'linkage', after all editing is done, order preserving + TScanContext* scanContext; + TPpContext* ppContext; + TBuiltInResource resources; + TLimits& limits; + TString sourceEntryPointName; + + // These, if set, will be called when a line, pragma ... is preprocessed. + // They will be called with any parameters to the original directive. + std::function lineCallback; + std::function&)> pragmaCallback; + std::function versionCallback; + std::function extensionCallback; + std::function errorCallback; + + // see implementation for detail + const TFunction* selectFunction(const TVector, const TFunction&, + std::function, + std::function, + /* output */ bool& tie); + + virtual void parseSwizzleSelector(const TSourceLoc&, const TString&, int size, + TSwizzleSelectors&); + + // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL) + TVariable* globalUniformBlock; // the actual block, inserted into the symbol table + unsigned int globalUniformBinding; // the block's binding number + unsigned int globalUniformSet; // the block's set number + int firstNewMember; // the index of the first member not yet inserted into the symbol table + // override this to set the language-specific name + virtual const char* getGlobalUniformBlockName() const { return ""; } + virtual void setUniformBlockDefaults(TType&) const { } + virtual void finalizeGlobalUniformBlockLayout(TVariable&) { } + virtual void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, TPrefixType prefix, + va_list args); + virtual void trackLinkage(TSymbol& symbol); + virtual void makeEditable(TSymbol*&); + virtual TVariable* getEditableVariable(const char* name); + virtual void finish(); +}; + +// +// Manage the state for when to respect precision qualifiers and when to warn about +// the defaults being different than might be expected. +// +class TPrecisionManager { +public: + TPrecisionManager() : obey(false), warn(false), explicitIntDefault(false), explicitFloatDefault(false){ } + virtual ~TPrecisionManager() {} + + void respectPrecisionQualifiers() { obey = true; } + bool respectingPrecisionQualifiers() const { return obey; } + bool shouldWarnAboutDefaults() const { return warn; } + void defaultWarningGiven() { warn = false; } + void warnAboutDefaults() { warn = true; } + void explicitIntDefaultSeen() + { + explicitIntDefault = true; + if (explicitFloatDefault) + warn = false; + } + void explicitFloatDefaultSeen() + { + explicitFloatDefault = true; + if (explicitIntDefault) + warn = false; + } + +protected: + bool obey; // respect precision qualifiers + bool warn; // need to give a warning about the defaults + bool explicitIntDefault; // user set the default for int/uint + bool explicitFloatDefault; // user set the default for float +}; + +// +// GLSL-specific parse helper. Should have GLSL in the name, but that's +// too big of a change for comparing branches at the moment, and perhaps +// impacts downstream consumers as well. +// +class TParseContext : public TParseContextBase { +public: + TParseContext(TSymbolTable&, TIntermediate&, bool parsingBuiltins, int version, EProfile, const SpvVersion& spvVersion, EShLanguage, TInfoSink&, + bool forwardCompatible = false, EShMessages messages = EShMsgDefault, + const TString* entryPoint = nullptr); + virtual ~TParseContext(); + + bool obeyPrecisionQualifiers() const { return precisionManager.respectingPrecisionQualifiers(); } + void setPrecisionDefaults(); + + void setLimits(const TBuiltInResource&) override; + bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override; + void parserError(const char* s); // for bison's yyerror + + void reservedErrorCheck(const TSourceLoc&, const TString&); + void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) override; + bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) override; + bool lineDirectiveShouldSetNextLine() const override; + bool builtInName(const TString&); + + void handlePragma(const TSourceLoc&, const TVector&) override; + TIntermTyped* handleVariable(const TSourceLoc&, TSymbol* symbol, const TString* string); + TIntermTyped* handleBracketDereference(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + void handleIndexLimits(const TSourceLoc&, TIntermTyped* base, TIntermTyped* index); + +#ifndef GLSLANG_WEB + void makeEditable(TSymbol*&) override; + void ioArrayCheck(const TSourceLoc&, const TType&, const TString& identifier); +#endif + bool isIoResizeArray(const TType&) const; + void fixIoArraySize(const TSourceLoc&, TType&); + void handleIoResizeArrayAccess(const TSourceLoc&, TIntermTyped* base); + void checkIoArraysConsistency(const TSourceLoc&, bool tailOnly = false); + int getIoArrayImplicitSize(const TQualifier&, TString* featureString = nullptr) const; + void checkIoArrayConsistency(const TSourceLoc&, int requiredSize, const char* feature, TType&, const TString&); + + TIntermTyped* handleBinaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right); + TIntermTyped* handleUnaryMath(const TSourceLoc&, const char* str, TOperator op, TIntermTyped* childNode); + TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field); + TIntermTyped* handleDotSwizzle(const TSourceLoc&, TIntermTyped* base, const TString& field); + void blockMemberExtensionCheck(const TSourceLoc&, const TIntermTyped* base, int member, const TString& memberName); + TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype); + TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&); + TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*); + TIntermTyped* handleBuiltInFunctionCall(TSourceLoc, TIntermNode* arguments, const TFunction& function); + void computeBuiltinPrecisions(TIntermTyped&, const TFunction&); + TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*); + void checkLocation(const TSourceLoc&, TOperator); + TIntermTyped* handleLengthMethod(const TSourceLoc&, TFunction*, TIntermNode*); + void addInputArgumentConversions(const TFunction&, TIntermNode*&) const; + TIntermTyped* addOutputArgumentConversions(const TFunction&, TIntermAggregate&) const; + TIntermTyped* addAssign(const TSourceLoc&, TOperator op, TIntermTyped* left, TIntermTyped* right); + void builtInOpCheck(const TSourceLoc&, const TFunction&, TIntermOperator&); + void nonOpBuiltInCheck(const TSourceLoc&, const TFunction&, TIntermAggregate&); + void userFunctionCallCheck(const TSourceLoc&, TIntermAggregate&); + void samplerConstructorLocationCheck(const TSourceLoc&, const char* token, TIntermNode*); + TFunction* handleConstructorCall(const TSourceLoc&, const TPublicType&); + void handlePrecisionQualifier(const TSourceLoc&, TQualifier&, TPrecisionQualifier); + void checkPrecisionQualifier(const TSourceLoc&, TPrecisionQualifier); + void memorySemanticsCheck(const TSourceLoc&, const TFunction&, const TIntermOperator& callNode); + + void assignError(const TSourceLoc&, const char* op, TString left, TString right); + void unaryOpError(const TSourceLoc&, const char* op, TString operand); + void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right); + void variableCheck(TIntermTyped*& nodePtr); + bool lValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; + void rValueErrorCheck(const TSourceLoc&, const char* op, TIntermTyped*) override; + void constantValueCheck(TIntermTyped* node, const char* token); + void integerCheck(const TIntermTyped* node, const char* token); + void globalCheck(const TSourceLoc&, const char* token); + bool constructorError(const TSourceLoc&, TIntermNode*, TFunction&, TOperator, TType&); + bool constructorTextureSamplerError(const TSourceLoc&, const TFunction&); + void arraySizeCheck(const TSourceLoc&, TIntermTyped* expr, TArraySize&, const char *sizeType); + bool arrayQualifierError(const TSourceLoc&, const TQualifier&); + bool arrayError(const TSourceLoc&, const TType&); + void arraySizeRequiredCheck(const TSourceLoc&, const TArraySizes&); + void structArrayCheck(const TSourceLoc&, const TType& structure); + void arraySizesCheck(const TSourceLoc&, const TQualifier&, TArraySizes*, const TIntermTyped* initializer, bool lastMember); + void arrayOfArrayVersionCheck(const TSourceLoc&, const TArraySizes*); + bool voidErrorCheck(const TSourceLoc&, const TString&, TBasicType); + void boolCheck(const TSourceLoc&, const TIntermTyped*); + void boolCheck(const TSourceLoc&, const TPublicType&); + void samplerCheck(const TSourceLoc&, const TType&, const TString& identifier, TIntermTyped* initializer); + void atomicUintCheck(const TSourceLoc&, const TType&, const TString& identifier); + void accStructCheck(const TSourceLoc & loc, const TType & type, const TString & identifier); + void transparentOpaqueCheck(const TSourceLoc&, const TType&, const TString& identifier); + void memberQualifierCheck(glslang::TPublicType&); + void globalQualifierFixCheck(const TSourceLoc&, TQualifier&, bool isMemberCheck = false); + void globalQualifierTypeCheck(const TSourceLoc&, const TQualifier&, const TPublicType&); + bool structQualifierErrorCheck(const TSourceLoc&, const TPublicType& pType); + void mergeQualifiers(const TSourceLoc&, TQualifier& dst, const TQualifier& src, bool force); + void setDefaultPrecision(const TSourceLoc&, TPublicType&, TPrecisionQualifier); + int computeSamplerTypeIndex(TSampler&); + TPrecisionQualifier getDefaultPrecision(TPublicType&); + void precisionQualifierCheck(const TSourceLoc&, TBasicType, TQualifier&); + void parameterTypeCheck(const TSourceLoc&, TStorageQualifier qualifier, const TType& type); + bool containsFieldWithBasicType(const TType& type ,TBasicType basicType); + TSymbol* redeclareBuiltinVariable(const TSourceLoc&, const TString&, const TQualifier&, const TShaderQualifiers&); + void redeclareBuiltinBlock(const TSourceLoc&, TTypeList& typeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes); + void paramCheckFixStorage(const TSourceLoc&, const TStorageQualifier&, TType& type); + void paramCheckFix(const TSourceLoc&, const TQualifier&, TType& type); + void nestedBlockCheck(const TSourceLoc&); + void nestedStructCheck(const TSourceLoc&); + void arrayObjectCheck(const TSourceLoc&, const TType&, const char* op); + void opaqueCheck(const TSourceLoc&, const TType&, const char* op); + void referenceCheck(const TSourceLoc&, const TType&, const char* op); + void storage16BitAssignmentCheck(const TSourceLoc&, const TType&, const char* op); + void specializationCheck(const TSourceLoc&, const TType&, const char* op); + void structTypeCheck(const TSourceLoc&, TPublicType&); + void inductiveLoopCheck(const TSourceLoc&, TIntermNode* init, TIntermLoop* loop); + void arrayLimitCheck(const TSourceLoc&, const TString&, int size); + void limitCheck(const TSourceLoc&, int value, const char* limit, const char* feature); + + void inductiveLoopBodyCheck(TIntermNode*, int loopIndexId, TSymbolTable&); + void constantIndexExpressionCheck(TIntermNode*); + + void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&); + void setLayoutQualifier(const TSourceLoc&, TPublicType&, TString&, const TIntermTyped*); + void mergeObjectLayoutQualifiers(TQualifier& dest, const TQualifier& src, bool inheritOnly); + void layoutObjectCheck(const TSourceLoc&, const TSymbol&); + void layoutMemberLocationArrayCheck(const TSourceLoc&, bool memberWithLocation, TArraySizes* arraySizes); + void layoutTypeCheck(const TSourceLoc&, const TType&); + void layoutQualifierCheck(const TSourceLoc&, const TQualifier&); + void checkNoShaderLayouts(const TSourceLoc&, const TShaderQualifiers&); + void fixOffset(const TSourceLoc&, TSymbol&); + + const TFunction* findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunctionExact(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunction120(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunction400(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + const TFunction* findFunctionExplicitTypes(const TSourceLoc& loc, const TFunction& call, bool& builtIn); + void declareTypeDefaults(const TSourceLoc&, const TPublicType&); + TIntermNode* declareVariable(const TSourceLoc&, TString& identifier, const TPublicType&, TArraySizes* typeArray = 0, TIntermTyped* initializer = 0); + TIntermTyped* addConstructor(const TSourceLoc&, TIntermNode*, const TType&); + TIntermTyped* constructAggregate(TIntermNode*, const TType&, int, const TSourceLoc&); + TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); + void inheritMemoryQualifiers(const TQualifier& from, TQualifier& to); + void declareBlock(const TSourceLoc&, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0); + void blockStageIoCheck(const TSourceLoc&, const TQualifier&); + void blockQualifierCheck(const TSourceLoc&, const TQualifier&, bool instanceName); + void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation); + void fixXfbOffsets(TQualifier&, TTypeList&); + void fixBlockUniformOffsets(TQualifier&, TTypeList&); + void fixBlockUniformLayoutMatrix(TQualifier&, TTypeList*, TTypeList*); + void fixBlockUniformLayoutPacking(TQualifier&, TTypeList*, TTypeList*); + void addQualifierToExisting(const TSourceLoc&, TQualifier, const TString& identifier); + void addQualifierToExisting(const TSourceLoc&, TQualifier, TIdentifierList&); + void invariantCheck(const TSourceLoc&, const TQualifier&); + void updateStandaloneQualifierDefaults(const TSourceLoc&, const TPublicType&); + void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode); + TIntermNode* addSwitch(const TSourceLoc&, TIntermTyped* expression, TIntermAggregate* body); + const TTypeList* recordStructCopy(TStructRecord&, const TType*, const TType*); + +#ifndef GLSLANG_WEB + TAttributeType attributeFromName(const TString& name) const; + TAttributes* makeAttributes(const TString& identifier) const; + TAttributes* makeAttributes(const TString& identifier, TIntermNode* node) const; + TAttributes* mergeAttributes(TAttributes*, TAttributes*) const; + + // Determine selection control from attributes + void handleSelectionAttributes(const TAttributes& attributes, TIntermNode*); + void handleSwitchAttributes(const TAttributes& attributes, TIntermNode*); + // Determine loop control from attributes + void handleLoopAttributes(const TAttributes& attributes, TIntermNode*); +#endif + + void checkAndResizeMeshViewDim(const TSourceLoc&, TType&, bool isBlockMember); + +protected: + void nonInitConstCheck(const TSourceLoc&, TString& identifier, TType& type); + void inheritGlobalDefaults(TQualifier& dst) const; + TVariable* makeInternalVariable(const char* name, const TType&) const; + TVariable* declareNonArray(const TSourceLoc&, const TString& identifier, const TType&); + void declareArray(const TSourceLoc&, const TString& identifier, const TType&, TSymbol*&); + void checkRuntimeSizable(const TSourceLoc&, const TIntermTyped&); + bool isRuntimeLength(const TIntermTyped&) const; + TIntermNode* executeInitializer(const TSourceLoc&, TIntermTyped* initializer, TVariable* variable); + TIntermTyped* convertInitializerList(const TSourceLoc&, const TType&, TIntermTyped* initializer); +#ifndef GLSLANG_WEB + void finish() override; +#endif + +public: + // + // Generally, bison productions, the scanner, and the PP need read/write access to these; just give them direct access + // + + // Current state of parsing + bool inMain; // if inside a function, true if the function is main + const TString* blockName; + TQualifier currentBlockQualifier; + TPrecisionQualifier defaultPrecision[EbtNumTypes]; + TBuiltInResource resources; + TLimits& limits; + +protected: + TParseContext(TParseContext&); + TParseContext& operator=(TParseContext&); + + static const int maxSamplerIndex = EsdNumDims * (EbtNumTypes * (2 * 2 * 2 * 2 * 2)); // see computeSamplerTypeIndex() + TPrecisionQualifier defaultSamplerPrecision[maxSamplerIndex]; + TPrecisionManager precisionManager; + TQualifier globalBufferDefaults; + TQualifier globalUniformDefaults; + TQualifier globalInputDefaults; + TQualifier globalOutputDefaults; + TString currentCaller; // name of last function body entered (not valid when at global scope) +#ifndef GLSLANG_WEB + int* atomicUintOffsets; // to become an array of the right size to hold an offset per binding point + bool anyIndexLimits; + TIdSetType inductiveLoopIds; + TVector needsIndexLimitationChecking; + TStructRecord matrixFixRecord; + TStructRecord packingFixRecord; + + // + // Geometry shader input arrays: + // - array sizing is based on input primitive and/or explicit size + // + // Tessellation control output arrays: + // - array sizing is based on output layout(vertices=...) and/or explicit size + // + // Both: + // - array sizing is retroactive + // - built-in block redeclarations interact with this + // + // Design: + // - use a per-context "resize-list", a list of symbols whose array sizes + // can be fixed + // + // - the resize-list starts empty at beginning of user-shader compilation, it does + // not have built-ins in it + // + // - on built-in array use: copyUp() symbol and add it to the resize-list + // + // - on user array declaration: add it to the resize-list + // + // - on block redeclaration: copyUp() symbol and add it to the resize-list + // * note, that appropriately gives an error if redeclaring a block that + // was already used and hence already copied-up + // + // - on seeing a layout declaration that sizes the array, fix everything in the + // resize-list, giving errors for mismatch + // + // - on seeing an array size declaration, give errors on mismatch between it and previous + // array-sizing declarations + // + TVector ioArraySymbolResizeList; +#endif +}; + +} // end namespace glslang + +#endif // _PARSER_HELPER_INCLUDED_ diff --git a/third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp b/third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp new file mode 100644 index 0000000..84c40f4 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/PoolAlloc.cpp @@ -0,0 +1,315 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/Common.h" +#include "../Include/PoolAlloc.h" + +#include "../Include/InitializeGlobals.h" +#include "../OSDependent/osinclude.h" + +namespace glslang { + +// Process-wide TLS index +OS_TLSIndex PoolIndex; + +// Return the thread-specific current pool. +TPoolAllocator& GetThreadPoolAllocator() +{ + return *static_cast(OS_GetTLSValue(PoolIndex)); +} + +// Set the thread-specific current pool. +void SetThreadPoolAllocator(TPoolAllocator* poolAllocator) +{ + OS_SetTLSValue(PoolIndex, poolAllocator); +} + +// Process-wide set up of the TLS pool storage. +bool InitializePoolIndex() +{ + // Allocate a TLS index. + if ((PoolIndex = OS_AllocTLSIndex()) == OS_INVALID_TLS_INDEX) + return false; + + return true; +} + +// +// Implement the functionality of the TPoolAllocator class, which +// is documented in PoolAlloc.h. +// +TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) : + pageSize(growthIncrement), + alignment(allocationAlignment), + freeList(nullptr), + inUseList(nullptr), + numCalls(0) +{ + // + // Don't allow page sizes we know are smaller than all common + // OS page sizes. + // + if (pageSize < 4*1024) + pageSize = 4*1024; + + // + // A large currentPageOffset indicates a new page needs to + // be obtained to allocate memory. + // + currentPageOffset = pageSize; + + // + // Adjust alignment to be at least pointer aligned and + // power of 2. + // + size_t minAlign = sizeof(void*); + alignment &= ~(minAlign - 1); + if (alignment < minAlign) + alignment = minAlign; + size_t a = 1; + while (a < alignment) + a <<= 1; + alignment = a; + alignmentMask = a - 1; + + // + // Align header skip + // + headerSkip = minAlign; + if (headerSkip < sizeof(tHeader)) { + headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask; + } + + push(); +} + +TPoolAllocator::~TPoolAllocator() +{ + while (inUseList) { + tHeader* next = inUseList->nextPage; + inUseList->~tHeader(); + delete [] reinterpret_cast(inUseList); + inUseList = next; + } + + // + // Always delete the free list memory - it can't be being + // (correctly) referenced, whether the pool allocator was + // global or not. We should not check the guard blocks + // here, because we did it already when the block was + // placed into the free list. + // + while (freeList) { + tHeader* next = freeList->nextPage; + delete [] reinterpret_cast(freeList); + freeList = next; + } +} + +const unsigned char TAllocation::guardBlockBeginVal = 0xfb; +const unsigned char TAllocation::guardBlockEndVal = 0xfe; +const unsigned char TAllocation::userDataFill = 0xcd; + +# ifdef GUARD_BLOCKS + const size_t TAllocation::guardBlockSize = 16; +# else + const size_t TAllocation::guardBlockSize = 0; +# endif + +// +// Check a single guard block for damage +// +#ifdef GUARD_BLOCKS +void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const +#else +void TAllocation::checkGuardBlock(unsigned char*, unsigned char, const char*) const +#endif +{ +#ifdef GUARD_BLOCKS + for (size_t x = 0; x < guardBlockSize; x++) { + if (blockMem[x] != val) { + const int maxSize = 80; + char assertMsg[maxSize]; + + // We don't print the assert message. It's here just to be helpful. + snprintf(assertMsg, maxSize, "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n", + locText, size, data()); + assert(0 && "PoolAlloc: Damage in guard block"); + } + } +#else + assert(guardBlockSize == 0); +#endif +} + +void TPoolAllocator::push() +{ + tAllocState state = { currentPageOffset, inUseList }; + + stack.push_back(state); + + // + // Indicate there is no current page to allocate from. + // + currentPageOffset = pageSize; +} + +// +// Do a mass-deallocation of all the individual allocations +// that have occurred since the last push(), or since the +// last pop(), or since the object's creation. +// +// The deallocated pages are saved for future allocations. +// +void TPoolAllocator::pop() +{ + if (stack.size() < 1) + return; + + tHeader* page = stack.back().page; + currentPageOffset = stack.back().offset; + + while (inUseList != page) { + tHeader* nextInUse = inUseList->nextPage; + size_t pageCount = inUseList->pageCount; + + // This technically ends the lifetime of the header as C++ object, + // but we will still control the memory and reuse it. + inUseList->~tHeader(); // currently, just a debug allocation checker + + if (pageCount > 1) { + delete [] reinterpret_cast(inUseList); + } else { + inUseList->nextPage = freeList; + freeList = inUseList; + } + inUseList = nextInUse; + } + + stack.pop_back(); +} + +// +// Do a mass-deallocation of all the individual allocations +// that have occurred. +// +void TPoolAllocator::popAll() +{ + while (stack.size() > 0) + pop(); +} + +void* TPoolAllocator::allocate(size_t numBytes) +{ + // If we are using guard blocks, all allocations are bracketed by + // them: [guardblock][allocation][guardblock]. numBytes is how + // much memory the caller asked for. allocationSize is the total + // size including guard blocks. In release build, + // guardBlockSize=0 and this all gets optimized away. + size_t allocationSize = TAllocation::allocationSize(numBytes); + + // + // Just keep some interesting statistics. + // + ++numCalls; + totalBytes += numBytes; + + // + // Do the allocation, most likely case first, for efficiency. + // This step could be moved to be inline sometime. + // + if (currentPageOffset + allocationSize <= pageSize) { + // + // Safe to allocate from currentPageOffset. + // + unsigned char* memory = reinterpret_cast(inUseList) + currentPageOffset; + currentPageOffset += allocationSize; + currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask; + + return initializeAllocation(inUseList, memory, numBytes); + } + + if (allocationSize + headerSkip > pageSize) { + // + // Do a multi-page allocation. Don't mix these with the others. + // The OS is efficient and allocating and free-ing multiple pages. + // + size_t numBytesToAlloc = allocationSize + headerSkip; + tHeader* memory = reinterpret_cast(::new char[numBytesToAlloc]); + if (memory == 0) + return 0; + + // Use placement-new to initialize header + new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize); + inUseList = memory; + + currentPageOffset = pageSize; // make next allocation come from a new page + + // No guard blocks for multi-page allocations (yet) + return reinterpret_cast(reinterpret_cast(memory) + headerSkip); + } + + // + // Need a simple page to allocate from. + // + tHeader* memory; + if (freeList) { + memory = freeList; + freeList = freeList->nextPage; + } else { + memory = reinterpret_cast(::new char[pageSize]); + if (memory == 0) + return 0; + } + + // Use placement-new to initialize header + new(memory) tHeader(inUseList, 1); + inUseList = memory; + + unsigned char* ret = reinterpret_cast(inUseList) + headerSkip; + currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask; + + return initializeAllocation(inUseList, ret, numBytes); +} + +// +// Check all allocations in a list for damage by calling check on each. +// +void TAllocation::checkAllocList() const +{ + for (const TAllocation* alloc = this; alloc != 0; alloc = alloc->prevAlloc) + alloc->check(); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp b/third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp new file mode 100644 index 0000000..1d33bfd --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/RemoveTree.cpp @@ -0,0 +1,118 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../Include/intermediate.h" +#include "RemoveTree.h" + +namespace glslang { + +// +// Code to recursively delete the intermediate tree. +// +struct TRemoveTraverser : TIntermTraverser { + TRemoveTraverser() : TIntermTraverser(false, false, true, false) {} + + virtual void visitSymbol(TIntermSymbol* node) + { + delete node; + } + + virtual bool visitBinary(TVisit /* visit*/ , TIntermBinary* node) + { + delete node; + + return true; + } + + virtual bool visitUnary(TVisit /* visit */, TIntermUnary* node) + { + delete node; + + return true; + } + + virtual bool visitAggregate(TVisit /* visit*/ , TIntermAggregate* node) + { + delete node; + + return true; + } + + virtual bool visitSelection(TVisit /* visit*/ , TIntermSelection* node) + { + delete node; + + return true; + } + + virtual bool visitSwitch(TVisit /* visit*/ , TIntermSwitch* node) + { + delete node; + + return true; + } + + virtual void visitConstantUnion(TIntermConstantUnion* node) + { + delete node; + } + + virtual bool visitLoop(TVisit /* visit*/ , TIntermLoop* node) + { + delete node; + + return true; + } + + virtual bool visitBranch(TVisit /* visit*/ , TIntermBranch* node) + { + delete node; + + return true; + } +}; + +// +// Entry point. +// +void RemoveAllTreeNodes(TIntermNode* root) +{ + TRemoveTraverser it; + + root->traverse(&it); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/RemoveTree.h b/third_party/glslang/glslang/MachineIndependent/RemoveTree.h new file mode 100644 index 0000000..1ed0156 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/RemoveTree.h @@ -0,0 +1,41 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#pragma once + +namespace glslang { + +void RemoveAllTreeNodes(TIntermNode*); + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/Scan.cpp b/third_party/glslang/glslang/MachineIndependent/Scan.cpp new file mode 100644 index 0000000..bcf4047 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/Scan.cpp @@ -0,0 +1,1917 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2020 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// GLSL scanning, leveraging the scanning done by the preprocessor. +// + +#include +#include +#include + +#include "../Include/Types.h" +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "attribute.h" +#include "glslang_tab.cpp.h" +#include "ScanContext.h" +#include "Scan.h" + +// preprocessor includes +#include "preprocessor/PpContext.h" +#include "preprocessor/PpTokens.h" + +// Required to avoid missing prototype warnings for some compilers +int yylex(YYSTYPE*, glslang::TParseContext&); + +namespace glslang { + +// read past any white space +void TInputScanner::consumeWhiteSpace(bool& foundNonSpaceTab) +{ + int c = peek(); // don't accidentally consume anything other than whitespace + while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + if (c == '\r' || c == '\n') + foundNonSpaceTab = true; + get(); + c = peek(); + } +} + +// return true if a comment was actually consumed +bool TInputScanner::consumeComment() +{ + if (peek() != '/') + return false; + + get(); // consume the '/' + int c = peek(); + if (c == '/') { + + // a '//' style comment + get(); // consume the second '/' + c = get(); + do { + while (c != EndOfInput && c != '\\' && c != '\r' && c != '\n') + c = get(); + + if (c == EndOfInput || c == '\r' || c == '\n') { + while (c == '\r' || c == '\n') + c = get(); + + // we reached the end of the comment + break; + } else { + // it's a '\', so we need to keep going, after skipping what's escaped + + // read the skipped character + c = get(); + + // if it's a two-character newline, skip both characters + if (c == '\r' && peek() == '\n') + get(); + c = get(); + } + } while (true); + + // put back the last non-comment character + if (c != EndOfInput) + unget(); + + return true; + } else if (c == '*') { + + // a '/*' style comment + get(); // consume the '*' + c = get(); + do { + while (c != EndOfInput && c != '*') + c = get(); + if (c == '*') { + c = get(); + if (c == '/') + break; // end of comment + // not end of comment + } else // end of input + break; + } while (true); + + return true; + } else { + // it's not a comment, put the '/' back + unget(); + + return false; + } +} + +// skip whitespace, then skip a comment, rinse, repeat +void TInputScanner::consumeWhitespaceComment(bool& foundNonSpaceTab) +{ + do { + consumeWhiteSpace(foundNonSpaceTab); + + // if not starting a comment now, then done + int c = peek(); + if (c != '/' || c == EndOfInput) + return; + + // skip potential comment + foundNonSpaceTab = true; + if (! consumeComment()) + return; + + } while (true); +} + +// Returns true if there was non-white space (e.g., a comment, newline) before the #version +// or no #version was found; otherwise, returns false. There is no error case, it always +// succeeds, but will leave version == 0 if no #version was found. +// +// Sets notFirstToken based on whether tokens (beyond white space and comments) +// appeared before the #version. +// +// N.B. does not attempt to leave input in any particular known state. The assumption +// is that scanning will start anew, following the rules for the chosen version/profile, +// and with a corresponding parsing context. +// +bool TInputScanner::scanVersion(int& version, EProfile& profile, bool& notFirstToken) +{ + // This function doesn't have to get all the semantics correct, + // just find the #version if there is a correct one present. + // The preprocessor will have the responsibility of getting all the semantics right. + + bool versionNotFirst = false; // means not first WRT comments and white space, nothing more + notFirstToken = false; // means not first WRT to real tokens + version = 0; // means not found + profile = ENoProfile; + + bool foundNonSpaceTab = false; + bool lookingInMiddle = false; + int c; + do { + if (lookingInMiddle) { + notFirstToken = true; + // make forward progress by finishing off the current line plus extra new lines + if (peek() != '\n' && peek() != '\r') { + do { + c = get(); + } while (c != EndOfInput && c != '\n' && c != '\r'); + } + while (peek() == '\n' || peek() == '\r') + get(); + if (peek() == EndOfInput) + return true; + } + lookingInMiddle = true; + + // Nominal start, skipping the desktop allowed comments and white space, but tracking if + // something else was found for ES: + consumeWhitespaceComment(foundNonSpaceTab); + if (foundNonSpaceTab) + versionNotFirst = true; + + // "#" + if (get() != '#') { + versionNotFirst = true; + continue; + } + + // whitespace + do { + c = get(); + } while (c == ' ' || c == '\t'); + + // "version" + if ( c != 'v' || + get() != 'e' || + get() != 'r' || + get() != 's' || + get() != 'i' || + get() != 'o' || + get() != 'n') { + versionNotFirst = true; + continue; + } + + // whitespace + do { + c = get(); + } while (c == ' ' || c == '\t'); + + // version number + while (c >= '0' && c <= '9') { + version = 10 * version + (c - '0'); + c = get(); + } + if (version == 0) { + versionNotFirst = true; + continue; + } + + // whitespace + while (c == ' ' || c == '\t') + c = get(); + + // profile + const int maxProfileLength = 13; // not including any 0 + char profileString[maxProfileLength]; + int profileLength; + for (profileLength = 0; profileLength < maxProfileLength; ++profileLength) { + if (c == EndOfInput || c == ' ' || c == '\t' || c == '\n' || c == '\r') + break; + profileString[profileLength] = (char)c; + c = get(); + } + if (c != EndOfInput && c != ' ' && c != '\t' && c != '\n' && c != '\r') { + versionNotFirst = true; + continue; + } + + if (profileLength == 2 && strncmp(profileString, "es", profileLength) == 0) + profile = EEsProfile; + else if (profileLength == 4 && strncmp(profileString, "core", profileLength) == 0) + profile = ECoreProfile; + else if (profileLength == 13 && strncmp(profileString, "compatibility", profileLength) == 0) + profile = ECompatibilityProfile; + + return versionNotFirst; + } while (true); +} + +// Fill this in when doing glslang-level scanning, to hand back to the parser. +class TParserToken { +public: + explicit TParserToken(YYSTYPE& b) : sType(b) { } + + YYSTYPE& sType; +protected: + TParserToken(TParserToken&); + TParserToken& operator=(TParserToken&); +}; + +} // end namespace glslang + +// This is the function the glslang parser (i.e., bison) calls to get its next token +int yylex(YYSTYPE* glslangTokenDesc, glslang::TParseContext& parseContext) +{ + glslang::TParserToken token(*glslangTokenDesc); + + return parseContext.getScanContext()->tokenize(parseContext.getPpContext(), token); +} + +namespace { + +struct str_eq +{ + bool operator()(const char* lhs, const char* rhs) const + { + return strcmp(lhs, rhs) == 0; + } +}; + +struct str_hash +{ + size_t operator()(const char* str) const + { + // djb2 + unsigned long hash = 5381; + int c; + + while ((c = *str++) != 0) + hash = ((hash << 5) + hash) + c; + + return hash; + } +}; + +// A single global usable by all threads, by all versions, by all languages. +// After a single process-level initialization, this is read only and thread safe +std::unordered_map* KeywordMap = nullptr; +#ifndef GLSLANG_WEB +std::unordered_set* ReservedSet = nullptr; +#endif + +}; + +namespace glslang { + +void TScanContext::fillInKeywordMap() +{ + if (KeywordMap != nullptr) { + // this is really an error, as this should called only once per process + // but, the only risk is if two threads called simultaneously + return; + } + KeywordMap = new std::unordered_map; + + (*KeywordMap)["const"] = CONST; + (*KeywordMap)["uniform"] = UNIFORM; + (*KeywordMap)["buffer"] = BUFFER; + (*KeywordMap)["in"] = IN; + (*KeywordMap)["out"] = OUT; + (*KeywordMap)["smooth"] = SMOOTH; + (*KeywordMap)["flat"] = FLAT; + (*KeywordMap)["centroid"] = CENTROID; + (*KeywordMap)["invariant"] = INVARIANT; + (*KeywordMap)["packed"] = PACKED; + (*KeywordMap)["resource"] = RESOURCE; + (*KeywordMap)["inout"] = INOUT; + (*KeywordMap)["struct"] = STRUCT; + (*KeywordMap)["break"] = BREAK; + (*KeywordMap)["continue"] = CONTINUE; + (*KeywordMap)["do"] = DO; + (*KeywordMap)["for"] = FOR; + (*KeywordMap)["while"] = WHILE; + (*KeywordMap)["switch"] = SWITCH; + (*KeywordMap)["case"] = CASE; + (*KeywordMap)["default"] = DEFAULT; + (*KeywordMap)["if"] = IF; + (*KeywordMap)["else"] = ELSE; + (*KeywordMap)["discard"] = DISCARD; + (*KeywordMap)["terminateInvocation"] = TERMINATE_INVOCATION; + (*KeywordMap)["return"] = RETURN; + (*KeywordMap)["void"] = VOID; + (*KeywordMap)["bool"] = BOOL; + (*KeywordMap)["float"] = FLOAT; + (*KeywordMap)["int"] = INT; + (*KeywordMap)["bvec2"] = BVEC2; + (*KeywordMap)["bvec3"] = BVEC3; + (*KeywordMap)["bvec4"] = BVEC4; + (*KeywordMap)["vec2"] = VEC2; + (*KeywordMap)["vec3"] = VEC3; + (*KeywordMap)["vec4"] = VEC4; + (*KeywordMap)["ivec2"] = IVEC2; + (*KeywordMap)["ivec3"] = IVEC3; + (*KeywordMap)["ivec4"] = IVEC4; + (*KeywordMap)["mat2"] = MAT2; + (*KeywordMap)["mat3"] = MAT3; + (*KeywordMap)["mat4"] = MAT4; + (*KeywordMap)["true"] = BOOLCONSTANT; + (*KeywordMap)["false"] = BOOLCONSTANT; + (*KeywordMap)["layout"] = LAYOUT; + (*KeywordMap)["shared"] = SHARED; + (*KeywordMap)["highp"] = HIGH_PRECISION; + (*KeywordMap)["mediump"] = MEDIUM_PRECISION; + (*KeywordMap)["lowp"] = LOW_PRECISION; + (*KeywordMap)["superp"] = SUPERP; + (*KeywordMap)["precision"] = PRECISION; + (*KeywordMap)["mat2x2"] = MAT2X2; + (*KeywordMap)["mat2x3"] = MAT2X3; + (*KeywordMap)["mat2x4"] = MAT2X4; + (*KeywordMap)["mat3x2"] = MAT3X2; + (*KeywordMap)["mat3x3"] = MAT3X3; + (*KeywordMap)["mat3x4"] = MAT3X4; + (*KeywordMap)["mat4x2"] = MAT4X2; + (*KeywordMap)["mat4x3"] = MAT4X3; + (*KeywordMap)["mat4x4"] = MAT4X4; + (*KeywordMap)["uint"] = UINT; + (*KeywordMap)["uvec2"] = UVEC2; + (*KeywordMap)["uvec3"] = UVEC3; + (*KeywordMap)["uvec4"] = UVEC4; + +#ifndef GLSLANG_WEB + (*KeywordMap)["nonuniformEXT"] = NONUNIFORM; + (*KeywordMap)["demote"] = DEMOTE; + (*KeywordMap)["attribute"] = ATTRIBUTE; + (*KeywordMap)["varying"] = VARYING; + (*KeywordMap)["noperspective"] = NOPERSPECTIVE; + (*KeywordMap)["coherent"] = COHERENT; + (*KeywordMap)["devicecoherent"] = DEVICECOHERENT; + (*KeywordMap)["queuefamilycoherent"] = QUEUEFAMILYCOHERENT; + (*KeywordMap)["workgroupcoherent"] = WORKGROUPCOHERENT; + (*KeywordMap)["subgroupcoherent"] = SUBGROUPCOHERENT; + (*KeywordMap)["shadercallcoherent"] = SHADERCALLCOHERENT; + (*KeywordMap)["nonprivate"] = NONPRIVATE; + (*KeywordMap)["restrict"] = RESTRICT; + (*KeywordMap)["readonly"] = READONLY; + (*KeywordMap)["writeonly"] = WRITEONLY; + (*KeywordMap)["atomic_uint"] = ATOMIC_UINT; + (*KeywordMap)["volatile"] = VOLATILE; + (*KeywordMap)["patch"] = PATCH; + (*KeywordMap)["sample"] = SAMPLE; + (*KeywordMap)["subroutine"] = SUBROUTINE; + (*KeywordMap)["dmat2"] = DMAT2; + (*KeywordMap)["dmat3"] = DMAT3; + (*KeywordMap)["dmat4"] = DMAT4; + (*KeywordMap)["dmat2x2"] = DMAT2X2; + (*KeywordMap)["dmat2x3"] = DMAT2X3; + (*KeywordMap)["dmat2x4"] = DMAT2X4; + (*KeywordMap)["dmat3x2"] = DMAT3X2; + (*KeywordMap)["dmat3x3"] = DMAT3X3; + (*KeywordMap)["dmat3x4"] = DMAT3X4; + (*KeywordMap)["dmat4x2"] = DMAT4X2; + (*KeywordMap)["dmat4x3"] = DMAT4X3; + (*KeywordMap)["dmat4x4"] = DMAT4X4; + (*KeywordMap)["image1D"] = IMAGE1D; + (*KeywordMap)["iimage1D"] = IIMAGE1D; + (*KeywordMap)["uimage1D"] = UIMAGE1D; + (*KeywordMap)["image2D"] = IMAGE2D; + (*KeywordMap)["iimage2D"] = IIMAGE2D; + (*KeywordMap)["uimage2D"] = UIMAGE2D; + (*KeywordMap)["image3D"] = IMAGE3D; + (*KeywordMap)["iimage3D"] = IIMAGE3D; + (*KeywordMap)["uimage3D"] = UIMAGE3D; + (*KeywordMap)["image2DRect"] = IMAGE2DRECT; + (*KeywordMap)["iimage2DRect"] = IIMAGE2DRECT; + (*KeywordMap)["uimage2DRect"] = UIMAGE2DRECT; + (*KeywordMap)["imageCube"] = IMAGECUBE; + (*KeywordMap)["iimageCube"] = IIMAGECUBE; + (*KeywordMap)["uimageCube"] = UIMAGECUBE; + (*KeywordMap)["imageBuffer"] = IMAGEBUFFER; + (*KeywordMap)["iimageBuffer"] = IIMAGEBUFFER; + (*KeywordMap)["uimageBuffer"] = UIMAGEBUFFER; + (*KeywordMap)["image1DArray"] = IMAGE1DARRAY; + (*KeywordMap)["iimage1DArray"] = IIMAGE1DARRAY; + (*KeywordMap)["uimage1DArray"] = UIMAGE1DARRAY; + (*KeywordMap)["image2DArray"] = IMAGE2DARRAY; + (*KeywordMap)["iimage2DArray"] = IIMAGE2DARRAY; + (*KeywordMap)["uimage2DArray"] = UIMAGE2DARRAY; + (*KeywordMap)["imageCubeArray"] = IMAGECUBEARRAY; + (*KeywordMap)["iimageCubeArray"] = IIMAGECUBEARRAY; + (*KeywordMap)["uimageCubeArray"] = UIMAGECUBEARRAY; + (*KeywordMap)["image2DMS"] = IMAGE2DMS; + (*KeywordMap)["iimage2DMS"] = IIMAGE2DMS; + (*KeywordMap)["uimage2DMS"] = UIMAGE2DMS; + (*KeywordMap)["image2DMSArray"] = IMAGE2DMSARRAY; + (*KeywordMap)["iimage2DMSArray"] = IIMAGE2DMSARRAY; + (*KeywordMap)["uimage2DMSArray"] = UIMAGE2DMSARRAY; + (*KeywordMap)["i64image1D"] = I64IMAGE1D; + (*KeywordMap)["u64image1D"] = U64IMAGE1D; + (*KeywordMap)["i64image2D"] = I64IMAGE2D; + (*KeywordMap)["u64image2D"] = U64IMAGE2D; + (*KeywordMap)["i64image3D"] = I64IMAGE3D; + (*KeywordMap)["u64image3D"] = U64IMAGE3D; + (*KeywordMap)["i64image2DRect"] = I64IMAGE2DRECT; + (*KeywordMap)["u64image2DRect"] = U64IMAGE2DRECT; + (*KeywordMap)["i64imageCube"] = I64IMAGECUBE; + (*KeywordMap)["u64imageCube"] = U64IMAGECUBE; + (*KeywordMap)["i64imageBuffer"] = I64IMAGEBUFFER; + (*KeywordMap)["u64imageBuffer"] = U64IMAGEBUFFER; + (*KeywordMap)["i64image1DArray"] = I64IMAGE1DARRAY; + (*KeywordMap)["u64image1DArray"] = U64IMAGE1DARRAY; + (*KeywordMap)["i64image2DArray"] = I64IMAGE2DARRAY; + (*KeywordMap)["u64image2DArray"] = U64IMAGE2DARRAY; + (*KeywordMap)["i64imageCubeArray"] = I64IMAGECUBEARRAY; + (*KeywordMap)["u64imageCubeArray"] = U64IMAGECUBEARRAY; + (*KeywordMap)["i64image2DMS"] = I64IMAGE2DMS; + (*KeywordMap)["u64image2DMS"] = U64IMAGE2DMS; + (*KeywordMap)["i64image2DMSArray"] = I64IMAGE2DMSARRAY; + (*KeywordMap)["u64image2DMSArray"] = U64IMAGE2DMSARRAY; + (*KeywordMap)["double"] = DOUBLE; + (*KeywordMap)["dvec2"] = DVEC2; + (*KeywordMap)["dvec3"] = DVEC3; + (*KeywordMap)["dvec4"] = DVEC4; + (*KeywordMap)["int64_t"] = INT64_T; + (*KeywordMap)["uint64_t"] = UINT64_T; + (*KeywordMap)["i64vec2"] = I64VEC2; + (*KeywordMap)["i64vec3"] = I64VEC3; + (*KeywordMap)["i64vec4"] = I64VEC4; + (*KeywordMap)["u64vec2"] = U64VEC2; + (*KeywordMap)["u64vec3"] = U64VEC3; + (*KeywordMap)["u64vec4"] = U64VEC4; + + // GL_EXT_shader_explicit_arithmetic_types + (*KeywordMap)["int8_t"] = INT8_T; + (*KeywordMap)["i8vec2"] = I8VEC2; + (*KeywordMap)["i8vec3"] = I8VEC3; + (*KeywordMap)["i8vec4"] = I8VEC4; + (*KeywordMap)["uint8_t"] = UINT8_T; + (*KeywordMap)["u8vec2"] = U8VEC2; + (*KeywordMap)["u8vec3"] = U8VEC3; + (*KeywordMap)["u8vec4"] = U8VEC4; + + (*KeywordMap)["int16_t"] = INT16_T; + (*KeywordMap)["i16vec2"] = I16VEC2; + (*KeywordMap)["i16vec3"] = I16VEC3; + (*KeywordMap)["i16vec4"] = I16VEC4; + (*KeywordMap)["uint16_t"] = UINT16_T; + (*KeywordMap)["u16vec2"] = U16VEC2; + (*KeywordMap)["u16vec3"] = U16VEC3; + (*KeywordMap)["u16vec4"] = U16VEC4; + + (*KeywordMap)["int32_t"] = INT32_T; + (*KeywordMap)["i32vec2"] = I32VEC2; + (*KeywordMap)["i32vec3"] = I32VEC3; + (*KeywordMap)["i32vec4"] = I32VEC4; + (*KeywordMap)["uint32_t"] = UINT32_T; + (*KeywordMap)["u32vec2"] = U32VEC2; + (*KeywordMap)["u32vec3"] = U32VEC3; + (*KeywordMap)["u32vec4"] = U32VEC4; + + (*KeywordMap)["float16_t"] = FLOAT16_T; + (*KeywordMap)["f16vec2"] = F16VEC2; + (*KeywordMap)["f16vec3"] = F16VEC3; + (*KeywordMap)["f16vec4"] = F16VEC4; + (*KeywordMap)["f16mat2"] = F16MAT2; + (*KeywordMap)["f16mat3"] = F16MAT3; + (*KeywordMap)["f16mat4"] = F16MAT4; + (*KeywordMap)["f16mat2x2"] = F16MAT2X2; + (*KeywordMap)["f16mat2x3"] = F16MAT2X3; + (*KeywordMap)["f16mat2x4"] = F16MAT2X4; + (*KeywordMap)["f16mat3x2"] = F16MAT3X2; + (*KeywordMap)["f16mat3x3"] = F16MAT3X3; + (*KeywordMap)["f16mat3x4"] = F16MAT3X4; + (*KeywordMap)["f16mat4x2"] = F16MAT4X2; + (*KeywordMap)["f16mat4x3"] = F16MAT4X3; + (*KeywordMap)["f16mat4x4"] = F16MAT4X4; + + (*KeywordMap)["float32_t"] = FLOAT32_T; + (*KeywordMap)["f32vec2"] = F32VEC2; + (*KeywordMap)["f32vec3"] = F32VEC3; + (*KeywordMap)["f32vec4"] = F32VEC4; + (*KeywordMap)["f32mat2"] = F32MAT2; + (*KeywordMap)["f32mat3"] = F32MAT3; + (*KeywordMap)["f32mat4"] = F32MAT4; + (*KeywordMap)["f32mat2x2"] = F32MAT2X2; + (*KeywordMap)["f32mat2x3"] = F32MAT2X3; + (*KeywordMap)["f32mat2x4"] = F32MAT2X4; + (*KeywordMap)["f32mat3x2"] = F32MAT3X2; + (*KeywordMap)["f32mat3x3"] = F32MAT3X3; + (*KeywordMap)["f32mat3x4"] = F32MAT3X4; + (*KeywordMap)["f32mat4x2"] = F32MAT4X2; + (*KeywordMap)["f32mat4x3"] = F32MAT4X3; + (*KeywordMap)["f32mat4x4"] = F32MAT4X4; + (*KeywordMap)["float64_t"] = FLOAT64_T; + (*KeywordMap)["f64vec2"] = F64VEC2; + (*KeywordMap)["f64vec3"] = F64VEC3; + (*KeywordMap)["f64vec4"] = F64VEC4; + (*KeywordMap)["f64mat2"] = F64MAT2; + (*KeywordMap)["f64mat3"] = F64MAT3; + (*KeywordMap)["f64mat4"] = F64MAT4; + (*KeywordMap)["f64mat2x2"] = F64MAT2X2; + (*KeywordMap)["f64mat2x3"] = F64MAT2X3; + (*KeywordMap)["f64mat2x4"] = F64MAT2X4; + (*KeywordMap)["f64mat3x2"] = F64MAT3X2; + (*KeywordMap)["f64mat3x3"] = F64MAT3X3; + (*KeywordMap)["f64mat3x4"] = F64MAT3X4; + (*KeywordMap)["f64mat4x2"] = F64MAT4X2; + (*KeywordMap)["f64mat4x3"] = F64MAT4X3; + (*KeywordMap)["f64mat4x4"] = F64MAT4X4; +#endif + + (*KeywordMap)["sampler2D"] = SAMPLER2D; + (*KeywordMap)["samplerCube"] = SAMPLERCUBE; + (*KeywordMap)["samplerCubeShadow"] = SAMPLERCUBESHADOW; + (*KeywordMap)["sampler2DArray"] = SAMPLER2DARRAY; + (*KeywordMap)["sampler2DArrayShadow"] = SAMPLER2DARRAYSHADOW; + (*KeywordMap)["isampler2D"] = ISAMPLER2D; + (*KeywordMap)["isampler3D"] = ISAMPLER3D; + (*KeywordMap)["isamplerCube"] = ISAMPLERCUBE; + (*KeywordMap)["isampler2DArray"] = ISAMPLER2DARRAY; + (*KeywordMap)["usampler2D"] = USAMPLER2D; + (*KeywordMap)["usampler3D"] = USAMPLER3D; + (*KeywordMap)["usamplerCube"] = USAMPLERCUBE; + (*KeywordMap)["usampler2DArray"] = USAMPLER2DARRAY; + (*KeywordMap)["sampler3D"] = SAMPLER3D; + (*KeywordMap)["sampler2DShadow"] = SAMPLER2DSHADOW; + + (*KeywordMap)["texture2D"] = TEXTURE2D; + (*KeywordMap)["textureCube"] = TEXTURECUBE; + (*KeywordMap)["texture2DArray"] = TEXTURE2DARRAY; + (*KeywordMap)["itexture2D"] = ITEXTURE2D; + (*KeywordMap)["itexture3D"] = ITEXTURE3D; + (*KeywordMap)["itextureCube"] = ITEXTURECUBE; + (*KeywordMap)["itexture2DArray"] = ITEXTURE2DARRAY; + (*KeywordMap)["utexture2D"] = UTEXTURE2D; + (*KeywordMap)["utexture3D"] = UTEXTURE3D; + (*KeywordMap)["utextureCube"] = UTEXTURECUBE; + (*KeywordMap)["utexture2DArray"] = UTEXTURE2DARRAY; + (*KeywordMap)["texture3D"] = TEXTURE3D; + + (*KeywordMap)["sampler"] = SAMPLER; + (*KeywordMap)["samplerShadow"] = SAMPLERSHADOW; + +#ifndef GLSLANG_WEB + (*KeywordMap)["textureCubeArray"] = TEXTURECUBEARRAY; + (*KeywordMap)["itextureCubeArray"] = ITEXTURECUBEARRAY; + (*KeywordMap)["utextureCubeArray"] = UTEXTURECUBEARRAY; + (*KeywordMap)["samplerCubeArray"] = SAMPLERCUBEARRAY; + (*KeywordMap)["samplerCubeArrayShadow"] = SAMPLERCUBEARRAYSHADOW; + (*KeywordMap)["isamplerCubeArray"] = ISAMPLERCUBEARRAY; + (*KeywordMap)["usamplerCubeArray"] = USAMPLERCUBEARRAY; + (*KeywordMap)["sampler1DArrayShadow"] = SAMPLER1DARRAYSHADOW; + (*KeywordMap)["isampler1DArray"] = ISAMPLER1DARRAY; + (*KeywordMap)["usampler1D"] = USAMPLER1D; + (*KeywordMap)["isampler1D"] = ISAMPLER1D; + (*KeywordMap)["usampler1DArray"] = USAMPLER1DARRAY; + (*KeywordMap)["samplerBuffer"] = SAMPLERBUFFER; + (*KeywordMap)["isampler2DRect"] = ISAMPLER2DRECT; + (*KeywordMap)["usampler2DRect"] = USAMPLER2DRECT; + (*KeywordMap)["isamplerBuffer"] = ISAMPLERBUFFER; + (*KeywordMap)["usamplerBuffer"] = USAMPLERBUFFER; + (*KeywordMap)["sampler2DMS"] = SAMPLER2DMS; + (*KeywordMap)["isampler2DMS"] = ISAMPLER2DMS; + (*KeywordMap)["usampler2DMS"] = USAMPLER2DMS; + (*KeywordMap)["sampler2DMSArray"] = SAMPLER2DMSARRAY; + (*KeywordMap)["isampler2DMSArray"] = ISAMPLER2DMSARRAY; + (*KeywordMap)["usampler2DMSArray"] = USAMPLER2DMSARRAY; + (*KeywordMap)["sampler1D"] = SAMPLER1D; + (*KeywordMap)["sampler1DShadow"] = SAMPLER1DSHADOW; + (*KeywordMap)["sampler2DRect"] = SAMPLER2DRECT; + (*KeywordMap)["sampler2DRectShadow"] = SAMPLER2DRECTSHADOW; + (*KeywordMap)["sampler1DArray"] = SAMPLER1DARRAY; + + (*KeywordMap)["samplerExternalOES"] = SAMPLEREXTERNALOES; // GL_OES_EGL_image_external + + (*KeywordMap)["__samplerExternal2DY2YEXT"] = SAMPLEREXTERNAL2DY2YEXT; // GL_EXT_YUV_target + + (*KeywordMap)["itexture1DArray"] = ITEXTURE1DARRAY; + (*KeywordMap)["utexture1D"] = UTEXTURE1D; + (*KeywordMap)["itexture1D"] = ITEXTURE1D; + (*KeywordMap)["utexture1DArray"] = UTEXTURE1DARRAY; + (*KeywordMap)["textureBuffer"] = TEXTUREBUFFER; + (*KeywordMap)["itexture2DRect"] = ITEXTURE2DRECT; + (*KeywordMap)["utexture2DRect"] = UTEXTURE2DRECT; + (*KeywordMap)["itextureBuffer"] = ITEXTUREBUFFER; + (*KeywordMap)["utextureBuffer"] = UTEXTUREBUFFER; + (*KeywordMap)["texture2DMS"] = TEXTURE2DMS; + (*KeywordMap)["itexture2DMS"] = ITEXTURE2DMS; + (*KeywordMap)["utexture2DMS"] = UTEXTURE2DMS; + (*KeywordMap)["texture2DMSArray"] = TEXTURE2DMSARRAY; + (*KeywordMap)["itexture2DMSArray"] = ITEXTURE2DMSARRAY; + (*KeywordMap)["utexture2DMSArray"] = UTEXTURE2DMSARRAY; + (*KeywordMap)["texture1D"] = TEXTURE1D; + (*KeywordMap)["texture2DRect"] = TEXTURE2DRECT; + (*KeywordMap)["texture1DArray"] = TEXTURE1DARRAY; + + (*KeywordMap)["subpassInput"] = SUBPASSINPUT; + (*KeywordMap)["subpassInputMS"] = SUBPASSINPUTMS; + (*KeywordMap)["isubpassInput"] = ISUBPASSINPUT; + (*KeywordMap)["isubpassInputMS"] = ISUBPASSINPUTMS; + (*KeywordMap)["usubpassInput"] = USUBPASSINPUT; + (*KeywordMap)["usubpassInputMS"] = USUBPASSINPUTMS; + + (*KeywordMap)["f16sampler1D"] = F16SAMPLER1D; + (*KeywordMap)["f16sampler2D"] = F16SAMPLER2D; + (*KeywordMap)["f16sampler3D"] = F16SAMPLER3D; + (*KeywordMap)["f16sampler2DRect"] = F16SAMPLER2DRECT; + (*KeywordMap)["f16samplerCube"] = F16SAMPLERCUBE; + (*KeywordMap)["f16sampler1DArray"] = F16SAMPLER1DARRAY; + (*KeywordMap)["f16sampler2DArray"] = F16SAMPLER2DARRAY; + (*KeywordMap)["f16samplerCubeArray"] = F16SAMPLERCUBEARRAY; + (*KeywordMap)["f16samplerBuffer"] = F16SAMPLERBUFFER; + (*KeywordMap)["f16sampler2DMS"] = F16SAMPLER2DMS; + (*KeywordMap)["f16sampler2DMSArray"] = F16SAMPLER2DMSARRAY; + (*KeywordMap)["f16sampler1DShadow"] = F16SAMPLER1DSHADOW; + (*KeywordMap)["f16sampler2DShadow"] = F16SAMPLER2DSHADOW; + (*KeywordMap)["f16sampler2DRectShadow"] = F16SAMPLER2DRECTSHADOW; + (*KeywordMap)["f16samplerCubeShadow"] = F16SAMPLERCUBESHADOW; + (*KeywordMap)["f16sampler1DArrayShadow"] = F16SAMPLER1DARRAYSHADOW; + (*KeywordMap)["f16sampler2DArrayShadow"] = F16SAMPLER2DARRAYSHADOW; + (*KeywordMap)["f16samplerCubeArrayShadow"] = F16SAMPLERCUBEARRAYSHADOW; + + (*KeywordMap)["f16image1D"] = F16IMAGE1D; + (*KeywordMap)["f16image2D"] = F16IMAGE2D; + (*KeywordMap)["f16image3D"] = F16IMAGE3D; + (*KeywordMap)["f16image2DRect"] = F16IMAGE2DRECT; + (*KeywordMap)["f16imageCube"] = F16IMAGECUBE; + (*KeywordMap)["f16image1DArray"] = F16IMAGE1DARRAY; + (*KeywordMap)["f16image2DArray"] = F16IMAGE2DARRAY; + (*KeywordMap)["f16imageCubeArray"] = F16IMAGECUBEARRAY; + (*KeywordMap)["f16imageBuffer"] = F16IMAGEBUFFER; + (*KeywordMap)["f16image2DMS"] = F16IMAGE2DMS; + (*KeywordMap)["f16image2DMSArray"] = F16IMAGE2DMSARRAY; + + (*KeywordMap)["f16texture1D"] = F16TEXTURE1D; + (*KeywordMap)["f16texture2D"] = F16TEXTURE2D; + (*KeywordMap)["f16texture3D"] = F16TEXTURE3D; + (*KeywordMap)["f16texture2DRect"] = F16TEXTURE2DRECT; + (*KeywordMap)["f16textureCube"] = F16TEXTURECUBE; + (*KeywordMap)["f16texture1DArray"] = F16TEXTURE1DARRAY; + (*KeywordMap)["f16texture2DArray"] = F16TEXTURE2DARRAY; + (*KeywordMap)["f16textureCubeArray"] = F16TEXTURECUBEARRAY; + (*KeywordMap)["f16textureBuffer"] = F16TEXTUREBUFFER; + (*KeywordMap)["f16texture2DMS"] = F16TEXTURE2DMS; + (*KeywordMap)["f16texture2DMSArray"] = F16TEXTURE2DMSARRAY; + + (*KeywordMap)["f16subpassInput"] = F16SUBPASSINPUT; + (*KeywordMap)["f16subpassInputMS"] = F16SUBPASSINPUTMS; + (*KeywordMap)["__explicitInterpAMD"] = EXPLICITINTERPAMD; + (*KeywordMap)["pervertexNV"] = PERVERTEXNV; + (*KeywordMap)["precise"] = PRECISE; + + (*KeywordMap)["rayPayloadNV"] = PAYLOADNV; + (*KeywordMap)["rayPayloadEXT"] = PAYLOADEXT; + (*KeywordMap)["rayPayloadInNV"] = PAYLOADINNV; + (*KeywordMap)["rayPayloadInEXT"] = PAYLOADINEXT; + (*KeywordMap)["hitAttributeNV"] = HITATTRNV; + (*KeywordMap)["hitAttributeEXT"] = HITATTREXT; + (*KeywordMap)["callableDataNV"] = CALLDATANV; + (*KeywordMap)["callableDataEXT"] = CALLDATAEXT; + (*KeywordMap)["callableDataInNV"] = CALLDATAINNV; + (*KeywordMap)["callableDataInEXT"] = CALLDATAINEXT; + (*KeywordMap)["accelerationStructureNV"] = ACCSTRUCTNV; + (*KeywordMap)["accelerationStructureEXT"] = ACCSTRUCTEXT; + (*KeywordMap)["rayQueryEXT"] = RAYQUERYEXT; + (*KeywordMap)["perprimitiveNV"] = PERPRIMITIVENV; + (*KeywordMap)["perviewNV"] = PERVIEWNV; + (*KeywordMap)["taskNV"] = PERTASKNV; + + (*KeywordMap)["fcoopmatNV"] = FCOOPMATNV; + (*KeywordMap)["icoopmatNV"] = ICOOPMATNV; + (*KeywordMap)["ucoopmatNV"] = UCOOPMATNV; + + ReservedSet = new std::unordered_set; + + ReservedSet->insert("common"); + ReservedSet->insert("partition"); + ReservedSet->insert("active"); + ReservedSet->insert("asm"); + ReservedSet->insert("class"); + ReservedSet->insert("union"); + ReservedSet->insert("enum"); + ReservedSet->insert("typedef"); + ReservedSet->insert("template"); + ReservedSet->insert("this"); + ReservedSet->insert("goto"); + ReservedSet->insert("inline"); + ReservedSet->insert("noinline"); + ReservedSet->insert("public"); + ReservedSet->insert("static"); + ReservedSet->insert("extern"); + ReservedSet->insert("external"); + ReservedSet->insert("interface"); + ReservedSet->insert("long"); + ReservedSet->insert("short"); + ReservedSet->insert("half"); + ReservedSet->insert("fixed"); + ReservedSet->insert("unsigned"); + ReservedSet->insert("input"); + ReservedSet->insert("output"); + ReservedSet->insert("hvec2"); + ReservedSet->insert("hvec3"); + ReservedSet->insert("hvec4"); + ReservedSet->insert("fvec2"); + ReservedSet->insert("fvec3"); + ReservedSet->insert("fvec4"); + ReservedSet->insert("sampler3DRect"); + ReservedSet->insert("filter"); + ReservedSet->insert("sizeof"); + ReservedSet->insert("cast"); + ReservedSet->insert("namespace"); + ReservedSet->insert("using"); +#endif +} + +void TScanContext::deleteKeywordMap() +{ + delete KeywordMap; + KeywordMap = nullptr; +#ifndef GLSLANG_WEB + delete ReservedSet; + ReservedSet = nullptr; +#endif +} + +// Called by yylex to get the next token. +// Returning 0 implies end of input. +int TScanContext::tokenize(TPpContext* pp, TParserToken& token) +{ + do { + parserToken = &token; + TPpToken ppToken; + int token = pp->tokenize(ppToken); + if (token == EndOfInput) + return 0; + + tokenText = ppToken.name; + loc = ppToken.loc; + parserToken->sType.lex.loc = loc; + switch (token) { + case ';': afterType = false; afterBuffer = false; return SEMICOLON; + case ',': afterType = false; return COMMA; + case ':': return COLON; + case '=': afterType = false; return EQUAL; + case '(': afterType = false; return LEFT_PAREN; + case ')': afterType = false; return RIGHT_PAREN; + case '.': field = true; return DOT; + case '!': return BANG; + case '-': return DASH; + case '~': return TILDE; + case '+': return PLUS; + case '*': return STAR; + case '/': return SLASH; + case '%': return PERCENT; + case '<': return LEFT_ANGLE; + case '>': return RIGHT_ANGLE; + case '|': return VERTICAL_BAR; + case '^': return CARET; + case '&': return AMPERSAND; + case '?': return QUESTION; + case '[': return LEFT_BRACKET; + case ']': return RIGHT_BRACKET; + case '{': afterStruct = false; afterBuffer = false; return LEFT_BRACE; + case '}': return RIGHT_BRACE; + case '\\': + parseContext.error(loc, "illegal use of escape character", "\\", ""); + break; + + case PPAtomAddAssign: return ADD_ASSIGN; + case PPAtomSubAssign: return SUB_ASSIGN; + case PPAtomMulAssign: return MUL_ASSIGN; + case PPAtomDivAssign: return DIV_ASSIGN; + case PPAtomModAssign: return MOD_ASSIGN; + + case PpAtomRight: return RIGHT_OP; + case PpAtomLeft: return LEFT_OP; + + case PpAtomRightAssign: return RIGHT_ASSIGN; + case PpAtomLeftAssign: return LEFT_ASSIGN; + case PpAtomAndAssign: return AND_ASSIGN; + case PpAtomOrAssign: return OR_ASSIGN; + case PpAtomXorAssign: return XOR_ASSIGN; + + case PpAtomAnd: return AND_OP; + case PpAtomOr: return OR_OP; + case PpAtomXor: return XOR_OP; + + case PpAtomEQ: return EQ_OP; + case PpAtomGE: return GE_OP; + case PpAtomNE: return NE_OP; + case PpAtomLE: return LE_OP; + + case PpAtomDecrement: return DEC_OP; + case PpAtomIncrement: return INC_OP; + + case PpAtomColonColon: + parseContext.error(loc, "not supported", "::", ""); + break; + + case PpAtomConstString: parserToken->sType.lex.string = NewPoolTString(tokenText); return STRING_LITERAL; + case PpAtomConstInt: parserToken->sType.lex.i = ppToken.ival; return INTCONSTANT; + case PpAtomConstUint: parserToken->sType.lex.i = ppToken.ival; return UINTCONSTANT; + case PpAtomConstFloat: parserToken->sType.lex.d = ppToken.dval; return FLOATCONSTANT; +#ifndef GLSLANG_WEB + case PpAtomConstInt16: parserToken->sType.lex.i = ppToken.ival; return INT16CONSTANT; + case PpAtomConstUint16: parserToken->sType.lex.i = ppToken.ival; return UINT16CONSTANT; + case PpAtomConstInt64: parserToken->sType.lex.i64 = ppToken.i64val; return INT64CONSTANT; + case PpAtomConstUint64: parserToken->sType.lex.i64 = ppToken.i64val; return UINT64CONSTANT; + case PpAtomConstDouble: parserToken->sType.lex.d = ppToken.dval; return DOUBLECONSTANT; + case PpAtomConstFloat16: parserToken->sType.lex.d = ppToken.dval; return FLOAT16CONSTANT; +#endif + case PpAtomIdentifier: + { + int token = tokenizeIdentifier(); + field = false; + return token; + } + + case EndOfInput: return 0; + + default: + char buf[2]; + buf[0] = (char)token; + buf[1] = 0; + parseContext.error(loc, "unexpected token", buf, ""); + break; + } + } while (true); +} + +int TScanContext::tokenizeIdentifier() +{ +#ifndef GLSLANG_WEB + if (ReservedSet->find(tokenText) != ReservedSet->end()) + return reservedWord(); +#endif + + auto it = KeywordMap->find(tokenText); + if (it == KeywordMap->end()) { + // Should have an identifier of some sort + return identifierOrType(); + } + keyword = it->second; + + switch (keyword) { + case CONST: + case UNIFORM: + case IN: + case OUT: + case INOUT: + case BREAK: + case CONTINUE: + case DO: + case FOR: + case WHILE: + case IF: + case ELSE: + case DISCARD: + case RETURN: + case CASE: + return keyword; + + case TERMINATE_INVOCATION: + if (!parseContext.extensionTurnedOn(E_GL_EXT_terminate_invocation)) + return identifierOrType(); + return keyword; + + case BUFFER: + afterBuffer = true; + if ((parseContext.isEsProfile() && parseContext.version < 310) || + (!parseContext.isEsProfile() && (parseContext.version < 430 && + !parseContext.extensionTurnedOn(E_GL_ARB_shader_storage_buffer_object)))) + return identifierOrType(); + return keyword; + + case STRUCT: + afterStruct = true; + return keyword; + + case SWITCH: + case DEFAULT: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 130)) + reservedWord(); + return keyword; + + case VOID: + case BOOL: + case FLOAT: + case INT: + case BVEC2: + case BVEC3: + case BVEC4: + case VEC2: + case VEC3: + case VEC4: + case IVEC2: + case IVEC3: + case IVEC4: + case MAT2: + case MAT3: + case MAT4: + case SAMPLER2D: + case SAMPLERCUBE: + afterType = true; + return keyword; + + case BOOLCONSTANT: + if (strcmp("true", tokenText) == 0) + parserToken->sType.lex.b = true; + else + parserToken->sType.lex.b = false; + return keyword; + + case SMOOTH: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 130)) + return identifierOrType(); + return keyword; + case FLAT: + if (parseContext.isEsProfile() && parseContext.version < 300) + reservedWord(); + else if (!parseContext.isEsProfile() && parseContext.version < 130) + return identifierOrType(); + return keyword; + case CENTROID: + if (parseContext.version < 120) + return identifierOrType(); + return keyword; + case INVARIANT: + if (!parseContext.isEsProfile() && parseContext.version < 120) + return identifierOrType(); + return keyword; + case PACKED: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 140)) + return reservedWord(); + return identifierOrType(); + + case RESOURCE: + { + bool reserved = (parseContext.isEsProfile() && parseContext.version >= 300) || + (!parseContext.isEsProfile() && parseContext.version >= 420); + return identifierOrReserved(reserved); + } + case SUPERP: + { + bool reserved = parseContext.isEsProfile() || parseContext.version >= 130; + return identifierOrReserved(reserved); + } + +#ifndef GLSLANG_WEB + case NOPERSPECTIVE: + if (parseContext.extensionTurnedOn(E_GL_NV_shader_noperspective_interpolation)) + return keyword; + return es30ReservedFromGLSL(130); + + case NONUNIFORM: + if (parseContext.extensionTurnedOn(E_GL_EXT_nonuniform_qualifier)) + return keyword; + else + return identifierOrType(); + case ATTRIBUTE: + case VARYING: + if (parseContext.isEsProfile() && parseContext.version >= 300) + reservedWord(); + return keyword; + case PAYLOADNV: + case PAYLOADINNV: + case HITATTRNV: + case CALLDATANV: + case CALLDATAINNV: + case ACCSTRUCTNV: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_ray_tracing)) + return keyword; + return identifierOrType(); + case PAYLOADEXT: + case PAYLOADINEXT: + case HITATTREXT: + case CALLDATAEXT: + case CALLDATAINEXT: + case ACCSTRUCTEXT: + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_ray_tracing) || + parseContext.extensionTurnedOn(E_GL_EXT_ray_query)) + return keyword; + return identifierOrType(); + case RAYQUERYEXT: + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && parseContext.version >= 460 + && parseContext.extensionTurnedOn(E_GL_EXT_ray_query))) + return keyword; + return identifierOrType(); + case ATOMIC_UINT: + if ((parseContext.isEsProfile() && parseContext.version >= 310) || + parseContext.extensionTurnedOn(E_GL_ARB_shader_atomic_counters)) + return keyword; + return es30ReservedFromGLSL(420); + + case COHERENT: + case DEVICECOHERENT: + case QUEUEFAMILYCOHERENT: + case WORKGROUPCOHERENT: + case SUBGROUPCOHERENT: + case SHADERCALLCOHERENT: + case NONPRIVATE: + case RESTRICT: + case READONLY: + case WRITEONLY: + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + return es30ReservedFromGLSL(parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store) ? 130 : 420); + case VOLATILE: + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + if (! parseContext.symbolTable.atBuiltInLevel() && (parseContext.isEsProfile() || + (parseContext.version < 420 && ! parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store)))) + reservedWord(); + return keyword; + case PATCH: + if (parseContext.symbolTable.atBuiltInLevel() || + (parseContext.isEsProfile() && + (parseContext.version >= 320 || + parseContext.extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader))) || + (!parseContext.isEsProfile() && parseContext.extensionTurnedOn(E_GL_ARB_tessellation_shader))) + return keyword; + + return es30ReservedFromGLSL(400); + + case SAMPLE: + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(1, &E_GL_OES_shader_multisample_interpolation)) + return keyword; + return es30ReservedFromGLSL(400); + + case SUBROUTINE: + return es30ReservedFromGLSL(400); +#endif + case SHARED: + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 140)) + return identifierOrType(); + return keyword; + case LAYOUT: + { + const int numLayoutExts = 2; + const char* layoutExts[numLayoutExts] = { E_GL_ARB_shading_language_420pack, + E_GL_ARB_explicit_attrib_location }; + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 140 && + ! parseContext.extensionsTurnedOn(numLayoutExts, layoutExts))) + return identifierOrType(); + return keyword; + } + + case HIGH_PRECISION: + case MEDIUM_PRECISION: + case LOW_PRECISION: + case PRECISION: + return precisionKeyword(); + + case MAT2X2: + case MAT2X3: + case MAT2X4: + case MAT3X2: + case MAT3X3: + case MAT3X4: + case MAT4X2: + case MAT4X3: + case MAT4X4: + return matNxM(); + +#ifndef GLSLANG_WEB + case DMAT2: + case DMAT3: + case DMAT4: + case DMAT2X2: + case DMAT2X3: + case DMAT2X4: + case DMAT3X2: + case DMAT3X3: + case DMAT3X4: + case DMAT4X2: + case DMAT4X3: + case DMAT4X4: + return dMat(); + + case IMAGE1D: + case IIMAGE1D: + case UIMAGE1D: + case IMAGE1DARRAY: + case IIMAGE1DARRAY: + case UIMAGE1DARRAY: + case IMAGE2DRECT: + case IIMAGE2DRECT: + case UIMAGE2DRECT: + afterType = true; + return firstGenerationImage(false); + + case I64IMAGE1D: + case U64IMAGE1D: + case I64IMAGE1DARRAY: + case U64IMAGE1DARRAY: + case I64IMAGE2DRECT: + case U64IMAGE2DRECT: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) { + return firstGenerationImage(false); + } + return identifierOrType(); + + case IMAGEBUFFER: + case IIMAGEBUFFER: + case UIMAGEBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return firstGenerationImage(false); + + case I64IMAGEBUFFER: + case U64IMAGEBUFFER: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) { + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return firstGenerationImage(false); + } + return identifierOrType(); + + case IMAGE2D: + case IIMAGE2D: + case UIMAGE2D: + case IMAGE3D: + case IIMAGE3D: + case UIMAGE3D: + case IMAGECUBE: + case IIMAGECUBE: + case UIMAGECUBE: + case IMAGE2DARRAY: + case IIMAGE2DARRAY: + case UIMAGE2DARRAY: + afterType = true; + return firstGenerationImage(true); + + case I64IMAGE2D: + case U64IMAGE2D: + case I64IMAGE3D: + case U64IMAGE3D: + case I64IMAGECUBE: + case U64IMAGECUBE: + case I64IMAGE2DARRAY: + case U64IMAGE2DARRAY: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) + return firstGenerationImage(true); + return identifierOrType(); + + case IMAGECUBEARRAY: + case IIMAGECUBEARRAY: + case UIMAGECUBEARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + return keyword; + return secondGenerationImage(); + + case I64IMAGECUBEARRAY: + case U64IMAGECUBEARRAY: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) { + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + return keyword; + return secondGenerationImage(); + } + return identifierOrType(); + + case IMAGE2DMS: + case IIMAGE2DMS: + case UIMAGE2DMS: + case IMAGE2DMSARRAY: + case IIMAGE2DMSARRAY: + case UIMAGE2DMSARRAY: + afterType = true; + return secondGenerationImage(); + + case I64IMAGE2DMS: + case U64IMAGE2DMS: + case I64IMAGE2DMSARRAY: + case U64IMAGE2DMSARRAY: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_image_int64)) { + return secondGenerationImage(); + } + return identifierOrType(); + + case DOUBLE: + case DVEC2: + case DVEC3: + case DVEC4: + afterType = true; + if (parseContext.isEsProfile() || parseContext.version < 150 || + (!parseContext.symbolTable.atBuiltInLevel() && + (parseContext.version < 400 && !parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_fp64) && + (parseContext.version < 410 && !parseContext.extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit))))) + reservedWord(); + return keyword; + + case INT64_T: + case UINT64_T: + case I64VEC2: + case I64VEC3: + case I64VEC4: + case U64VEC2: + case U64VEC3: + case U64VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_int64) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int64)) + return keyword; + return identifierOrType(); + + case INT8_T: + case UINT8_T: + case I8VEC2: + case I8VEC3: + case I8VEC4: + case U8VEC2: + case U8VEC3: + case U8VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_8bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8)) + return keyword; + return identifierOrType(); + + case INT16_T: + case UINT16_T: + case I16VEC2: + case I16VEC3: + case I16VEC4: + case U16VEC2: + case U16VEC3: + case U16VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_int16) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_16bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16)) + return keyword; + return identifierOrType(); + case INT32_T: + case UINT32_T: + case I32VEC2: + case I32VEC3: + case I32VEC4: + case U32VEC2: + case U32VEC3: + case U32VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int32)) + return keyword; + return identifierOrType(); + case FLOAT32_T: + case F32VEC2: + case F32VEC3: + case F32VEC4: + case F32MAT2: + case F32MAT3: + case F32MAT4: + case F32MAT2X2: + case F32MAT2X3: + case F32MAT2X4: + case F32MAT3X2: + case F32MAT3X3: + case F32MAT3X4: + case F32MAT4X2: + case F32MAT4X3: + case F32MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32)) + return keyword; + return identifierOrType(); + + case FLOAT64_T: + case F64VEC2: + case F64VEC3: + case F64VEC4: + case F64MAT2: + case F64MAT3: + case F64MAT4: + case F64MAT2X2: + case F64MAT2X3: + case F64MAT2X4: + case F64MAT3X2: + case F64MAT3X3: + case F64MAT3X4: + case F64MAT4X2: + case F64MAT4X3: + case F64MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64)) + return keyword; + return identifierOrType(); + + case FLOAT16_T: + case F16VEC2: + case F16VEC3: + case F16VEC4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_16bit_storage) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16)) + return keyword; + + return identifierOrType(); + + case F16MAT2: + case F16MAT3: + case F16MAT4: + case F16MAT2X2: + case F16MAT2X3: + case F16MAT2X4: + case F16MAT3X2: + case F16MAT3X3: + case F16MAT3X4: + case F16MAT4X2: + case F16MAT4X3: + case F16MAT4X4: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || + parseContext.extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float16)) + return keyword; + + return identifierOrType(); + + case SAMPLERCUBEARRAY: + case SAMPLERCUBEARRAYSHADOW: + case ISAMPLERCUBEARRAY: + case USAMPLERCUBEARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_cube_map_array, AEP_texture_cube_map_array)) + return keyword; + if (parseContext.isEsProfile() || (parseContext.version < 400 && ! parseContext.extensionTurnedOn(E_GL_ARB_texture_cube_map_array))) + reservedWord(); + return keyword; + + case TEXTURECUBEARRAY: + case ITEXTURECUBEARRAY: + case UTEXTURECUBEARRAY: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); +#endif + + case UINT: + case UVEC2: + case UVEC3: + case UVEC4: + case SAMPLERCUBESHADOW: + case SAMPLER2DARRAY: + case SAMPLER2DARRAYSHADOW: + case ISAMPLER2D: + case ISAMPLER3D: + case ISAMPLERCUBE: + case ISAMPLER2DARRAY: + case USAMPLER2D: + case USAMPLER3D: + case USAMPLERCUBE: + case USAMPLER2DARRAY: + afterType = true; + return nonreservedKeyword(300, 130); + + case SAMPLER3D: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version < 300) { + if (!parseContext.extensionTurnedOn(E_GL_OES_texture_3D)) + reservedWord(); + } + return keyword; + + case SAMPLER2DSHADOW: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version < 300) { + if (!parseContext.extensionTurnedOn(E_GL_EXT_shadow_samplers)) + reservedWord(); + } + return keyword; + + case TEXTURE2D: + case TEXTURECUBE: + case TEXTURE2DARRAY: + case ITEXTURE2D: + case ITEXTURE3D: + case ITEXTURECUBE: + case ITEXTURE2DARRAY: + case UTEXTURE2D: + case UTEXTURE3D: + case UTEXTURECUBE: + case UTEXTURE2DARRAY: + case TEXTURE3D: + case SAMPLER: + case SAMPLERSHADOW: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + +#ifndef GLSLANG_WEB + case ISAMPLER1D: + case ISAMPLER1DARRAY: + case SAMPLER1DARRAYSHADOW: + case USAMPLER1D: + case USAMPLER1DARRAY: + afterType = true; + return es30ReservedFromGLSL(130); + case ISAMPLER2DRECT: + case USAMPLER2DRECT: + afterType = true; + return es30ReservedFromGLSL(140); + + case SAMPLERBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return es30ReservedFromGLSL(130); + + case ISAMPLERBUFFER: + case USAMPLERBUFFER: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(Num_AEP_texture_buffer, AEP_texture_buffer)) + return keyword; + return es30ReservedFromGLSL(140); + + case SAMPLER2DMS: + case ISAMPLER2DMS: + case USAMPLER2DMS: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version >= 310) + return keyword; + if (!parseContext.isEsProfile() && (parseContext.version > 140 || + (parseContext.version == 140 && parseContext.extensionsTurnedOn(1, &E_GL_ARB_texture_multisample)))) + return keyword; + return es30ReservedFromGLSL(150); + + case SAMPLER2DMSARRAY: + case ISAMPLER2DMSARRAY: + case USAMPLER2DMSARRAY: + afterType = true; + if ((parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionsTurnedOn(1, &E_GL_OES_texture_storage_multisample_2d_array)) + return keyword; + if (!parseContext.isEsProfile() && (parseContext.version > 140 || + (parseContext.version == 140 && parseContext.extensionsTurnedOn(1, &E_GL_ARB_texture_multisample)))) + return keyword; + return es30ReservedFromGLSL(150); + + case SAMPLER1D: + case SAMPLER1DSHADOW: + afterType = true; + if (parseContext.isEsProfile()) + reservedWord(); + return keyword; + + case SAMPLER2DRECT: + case SAMPLER2DRECTSHADOW: + afterType = true; + if (parseContext.isEsProfile()) + reservedWord(); + else if (parseContext.version < 140 && ! parseContext.symbolTable.atBuiltInLevel() && ! parseContext.extensionTurnedOn(E_GL_ARB_texture_rectangle)) { + if (parseContext.relaxedErrors()) + parseContext.requireExtensions(loc, 1, &E_GL_ARB_texture_rectangle, "texture-rectangle sampler keyword"); + else + reservedWord(); + } + return keyword; + + case SAMPLER1DARRAY: + afterType = true; + if (parseContext.isEsProfile() && parseContext.version == 300) + reservedWord(); + else if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < 130)) + return identifierOrType(); + return keyword; + + case SAMPLEREXTERNALOES: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external) || + parseContext.extensionTurnedOn(E_GL_OES_EGL_image_external_essl3)) + return keyword; + return identifierOrType(); + + case SAMPLEREXTERNAL2DY2YEXT: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_EXT_YUV_target)) + return keyword; + return identifierOrType(); + + case ITEXTURE1DARRAY: + case UTEXTURE1D: + case ITEXTURE1D: + case UTEXTURE1DARRAY: + case TEXTUREBUFFER: + case ITEXTURE2DRECT: + case UTEXTURE2DRECT: + case ITEXTUREBUFFER: + case UTEXTUREBUFFER: + case TEXTURE2DMS: + case ITEXTURE2DMS: + case UTEXTURE2DMS: + case TEXTURE2DMSARRAY: + case ITEXTURE2DMSARRAY: + case UTEXTURE2DMSARRAY: + case TEXTURE1D: + case TEXTURE2DRECT: + case TEXTURE1DARRAY: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + + case SUBPASSINPUT: + case SUBPASSINPUTMS: + case ISUBPASSINPUT: + case ISUBPASSINPUTMS: + case USUBPASSINPUT: + case USUBPASSINPUTMS: + if (parseContext.spvVersion.vulkan > 0) + return keyword; + else + return identifierOrType(); + + case F16SAMPLER1D: + case F16SAMPLER2D: + case F16SAMPLER3D: + case F16SAMPLER2DRECT: + case F16SAMPLERCUBE: + case F16SAMPLER1DARRAY: + case F16SAMPLER2DARRAY: + case F16SAMPLERCUBEARRAY: + case F16SAMPLERBUFFER: + case F16SAMPLER2DMS: + case F16SAMPLER2DMSARRAY: + case F16SAMPLER1DSHADOW: + case F16SAMPLER2DSHADOW: + case F16SAMPLER1DARRAYSHADOW: + case F16SAMPLER2DARRAYSHADOW: + case F16SAMPLER2DRECTSHADOW: + case F16SAMPLERCUBESHADOW: + case F16SAMPLERCUBEARRAYSHADOW: + + case F16IMAGE1D: + case F16IMAGE2D: + case F16IMAGE3D: + case F16IMAGE2DRECT: + case F16IMAGECUBE: + case F16IMAGE1DARRAY: + case F16IMAGE2DARRAY: + case F16IMAGECUBEARRAY: + case F16IMAGEBUFFER: + case F16IMAGE2DMS: + case F16IMAGE2DMSARRAY: + + case F16TEXTURE1D: + case F16TEXTURE2D: + case F16TEXTURE3D: + case F16TEXTURE2DRECT: + case F16TEXTURECUBE: + case F16TEXTURE1DARRAY: + case F16TEXTURE2DARRAY: + case F16TEXTURECUBEARRAY: + case F16TEXTUREBUFFER: + case F16TEXTURE2DMS: + case F16TEXTURE2DMSARRAY: + + case F16SUBPASSINPUT: + case F16SUBPASSINPUTMS: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_AMD_gpu_shader_half_float_fetch)) + return keyword; + return identifierOrType(); + + case EXPLICITINTERPAMD: + if (parseContext.extensionTurnedOn(E_GL_AMD_shader_explicit_vertex_parameter)) + return keyword; + return identifierOrType(); + + case PERVERTEXNV: + if ((!parseContext.isEsProfile() && parseContext.version >= 450) || + parseContext.extensionTurnedOn(E_GL_NV_fragment_shader_barycentric)) + return keyword; + return identifierOrType(); + + case PRECISE: + if ((parseContext.isEsProfile() && + (parseContext.version >= 320 || parseContext.extensionsTurnedOn(Num_AEP_gpu_shader5, AEP_gpu_shader5))) || + (!parseContext.isEsProfile() && parseContext.version >= 400)) + return keyword; + if (parseContext.isEsProfile() && parseContext.version == 310) { + reservedWord(); + return keyword; + } + return identifierOrType(); + + case PERPRIMITIVENV: + case PERVIEWNV: + case PERTASKNV: + if ((!parseContext.isEsProfile() && parseContext.version >= 450) || + (parseContext.isEsProfile() && parseContext.version >= 320) || + parseContext.extensionTurnedOn(E_GL_NV_mesh_shader)) + return keyword; + return identifierOrType(); + + case FCOOPMATNV: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_cooperative_matrix)) + return keyword; + return identifierOrType(); + + case UCOOPMATNV: + case ICOOPMATNV: + afterType = true; + if (parseContext.symbolTable.atBuiltInLevel() || + parseContext.extensionTurnedOn(E_GL_NV_integer_cooperative_matrix)) + return keyword; + return identifierOrType(); + + case DEMOTE: + if (parseContext.extensionTurnedOn(E_GL_EXT_demote_to_helper_invocation)) + return keyword; + else + return identifierOrType(); +#endif + + default: + parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc); + return 0; + } +} + +int TScanContext::identifierOrType() +{ + parserToken->sType.lex.string = NewPoolTString(tokenText); + if (field) + return IDENTIFIER; + + parserToken->sType.lex.symbol = parseContext.symbolTable.find(*parserToken->sType.lex.string); + if ((afterType == false && afterStruct == false) && parserToken->sType.lex.symbol != nullptr) { + if (const TVariable* variable = parserToken->sType.lex.symbol->getAsVariable()) { + if (variable->isUserType() && + // treat redeclaration of forward-declared buffer/uniform reference as an identifier + !(variable->getType().isReference() && afterBuffer)) { + afterType = true; + + return TYPE_NAME; + } + } + } + + return IDENTIFIER; +} + +// Give an error for use of a reserved symbol. +// However, allow built-in declarations to use reserved words, to allow +// extension support before the extension is enabled. +int TScanContext::reservedWord() +{ + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.error(loc, "Reserved word.", tokenText, "", ""); + + return 0; +} + +int TScanContext::identifierOrReserved(bool reserved) +{ + if (reserved) { + reservedWord(); + + return 0; + } + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future reserved keyword", tokenText, ""); + + return identifierOrType(); +} + +// For keywords that suddenly showed up on non-ES (not previously reserved) +// but then got reserved by ES 3.0. +int TScanContext::es30ReservedFromGLSL(int version) +{ + if (parseContext.symbolTable.atBuiltInLevel()) + return keyword; + + if ((parseContext.isEsProfile() && parseContext.version < 300) || + (!parseContext.isEsProfile() && parseContext.version < version)) { + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "future reserved word in ES 300 and keyword in GLSL", tokenText, ""); + + return identifierOrType(); + } else if (parseContext.isEsProfile() && parseContext.version >= 300) + reservedWord(); + + return keyword; +} + +// For a keyword that was never reserved, until it suddenly +// showed up, both in an es version and a non-ES version. +int TScanContext::nonreservedKeyword(int esVersion, int nonEsVersion) +{ + if ((parseContext.isEsProfile() && parseContext.version < esVersion) || + (!parseContext.isEsProfile() && parseContext.version < nonEsVersion)) { + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future keyword", tokenText, ""); + + return identifierOrType(); + } + + return keyword; +} + +int TScanContext::precisionKeyword() +{ + if (parseContext.isEsProfile() || parseContext.version >= 130) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using ES precision qualifier keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::matNxM() +{ + afterType = true; + + if (parseContext.version > 110) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future non-square matrix type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::dMat() +{ + afterType = true; + + if (parseContext.isEsProfile() && parseContext.version >= 300) { + reservedWord(); + + return keyword; + } + + if (!parseContext.isEsProfile() && (parseContext.version >= 400 || + parseContext.symbolTable.atBuiltInLevel() || + (parseContext.version >= 150 && parseContext.extensionTurnedOn(E_GL_ARB_gpu_shader_fp64)) || + (parseContext.version >= 150 && parseContext.extensionTurnedOn(E_GL_ARB_vertex_attrib_64bit) + && parseContext.language == EShLangVertex))) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::firstGenerationImage(bool inEs310) +{ + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && (parseContext.version >= 420 || + parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store))) || + (inEs310 && parseContext.isEsProfile() && parseContext.version >= 310)) + return keyword; + + if ((parseContext.isEsProfile() && parseContext.version >= 300) || + (!parseContext.isEsProfile() && parseContext.version >= 130)) { + reservedWord(); + + return keyword; + } + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +int TScanContext::secondGenerationImage() +{ + if (parseContext.isEsProfile() && parseContext.version >= 310) { + reservedWord(); + return keyword; + } + + if (parseContext.symbolTable.atBuiltInLevel() || + (!parseContext.isEsProfile() && + (parseContext.version >= 420 || parseContext.extensionTurnedOn(E_GL_ARB_shader_image_load_store)))) + return keyword; + + if (parseContext.isForwardCompatible()) + parseContext.warn(loc, "using future type keyword", tokenText, ""); + + return identifierOrType(); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/Scan.h b/third_party/glslang/glslang/MachineIndependent/Scan.h new file mode 100644 index 0000000..24b75cf --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/Scan.h @@ -0,0 +1,276 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef _GLSLANG_SCAN_INCLUDED_ +#define _GLSLANG_SCAN_INCLUDED_ + +#include "Versions.h" + +namespace glslang { + +// Use a global end-of-input character, so no translation is needed across +// layers of encapsulation. Characters are all 8 bit, and positive, so there is +// no aliasing of character 255 onto -1, for example. +const int EndOfInput = -1; + +// +// A character scanner that seamlessly, on read-only strings, reads across an +// array of strings without assuming null termination. +// +class TInputScanner { +public: + TInputScanner(int n, const char* const s[], size_t L[], const char* const* names = nullptr, + int b = 0, int f = 0, bool single = false) : + numSources(n), + // up to this point, common usage is "char*", but now we need positive 8-bit characters + sources(reinterpret_cast(s)), + lengths(L), currentSource(0), currentChar(0), stringBias(b), finale(f), singleLogical(single), + endOfFileReached(false) + { + loc = new TSourceLoc[numSources]; + for (int i = 0; i < numSources; ++i) { + loc[i].init(i - stringBias); + } + if (names != nullptr) { + for (int i = 0; i < numSources; ++i) + loc[i].name = names[i] != nullptr ? NewPoolTString(names[i]) : nullptr; + } + loc[currentSource].line = 1; + logicalSourceLoc.init(1); + logicalSourceLoc.name = loc[0].name; + } + + virtual ~TInputScanner() + { + delete [] loc; + } + + // retrieve the next character and advance one character + int get() + { + int ret = peek(); + if (ret == EndOfInput) + return ret; + ++loc[currentSource].column; + ++logicalSourceLoc.column; + if (ret == '\n') { + ++loc[currentSource].line; + ++logicalSourceLoc.line; + logicalSourceLoc.column = 0; + loc[currentSource].column = 0; + } + advance(); + + return ret; + } + + // retrieve the next character, no advance + int peek() + { + if (currentSource >= numSources) { + endOfFileReached = true; + return EndOfInput; + } + // Make sure we do not read off the end of a string. + // N.B. Sources can have a length of 0. + int sourceToRead = currentSource; + size_t charToRead = currentChar; + while(charToRead >= lengths[sourceToRead]) { + charToRead = 0; + sourceToRead += 1; + if (sourceToRead >= numSources) { + return EndOfInput; + } + } + + // Here, we care about making negative valued characters positive + return sources[sourceToRead][charToRead]; + } + + // go back one character + void unget() + { + // Do not roll back once we've reached the end of the file. + if (endOfFileReached) + return; + + if (currentChar > 0) { + --currentChar; + --loc[currentSource].column; + --logicalSourceLoc.column; + if (loc[currentSource].column < 0) { + // We've moved back past a new line. Find the + // previous newline (or start of the file) to compute + // the column count on the now current line. + size_t chIndex = currentChar; + while (chIndex > 0) { + if (sources[currentSource][chIndex] == '\n') { + break; + } + --chIndex; + } + logicalSourceLoc.column = (int)(currentChar - chIndex); + loc[currentSource].column = (int)(currentChar - chIndex); + } + } else { + do { + --currentSource; + } while (currentSource > 0 && lengths[currentSource] == 0); + if (lengths[currentSource] == 0) { + // set to 0 if we've backed up to the start of an empty string + currentChar = 0; + } else + currentChar = lengths[currentSource] - 1; + } + if (peek() == '\n') { + --loc[currentSource].line; + --logicalSourceLoc.line; + } + } + + // for #line override + void setLine(int newLine) + { + logicalSourceLoc.line = newLine; + loc[getLastValidSourceIndex()].line = newLine; + } + + // for #line override in filename based parsing + void setFile(const char* filename) + { + TString* fn_tstr = NewPoolTString(filename); + logicalSourceLoc.name = fn_tstr; + loc[getLastValidSourceIndex()].name = fn_tstr; + } + + void setFile(const char* filename, int i) + { + TString* fn_tstr = NewPoolTString(filename); + if (i == getLastValidSourceIndex()) { + logicalSourceLoc.name = fn_tstr; + } + loc[i].name = fn_tstr; + } + + void setString(int newString) + { + logicalSourceLoc.string = newString; + loc[getLastValidSourceIndex()].string = newString; + logicalSourceLoc.name = nullptr; + loc[getLastValidSourceIndex()].name = nullptr; + } + + // for #include content indentation + void setColumn(int col) + { + logicalSourceLoc.column = col; + loc[getLastValidSourceIndex()].column = col; + } + + void setEndOfInput() + { + endOfFileReached = true; + currentSource = numSources; + } + + bool atEndOfInput() const { return endOfFileReached; } + + const TSourceLoc& getSourceLoc() const + { + if (singleLogical) { + return logicalSourceLoc; + } else { + return loc[std::max(0, std::min(currentSource, numSources - finale - 1))]; + } + } + // Returns the index (starting from 0) of the most recent valid source string we are reading from. + int getLastValidSourceIndex() const { return std::min(currentSource, numSources - 1); } + + void consumeWhiteSpace(bool& foundNonSpaceTab); + bool consumeComment(); + void consumeWhitespaceComment(bool& foundNonSpaceTab); + bool scanVersion(int& version, EProfile& profile, bool& notFirstToken); + +protected: + + // advance one character + void advance() + { + ++currentChar; + if (currentChar >= lengths[currentSource]) { + ++currentSource; + if (currentSource < numSources) { + loc[currentSource].string = loc[currentSource - 1].string + 1; + loc[currentSource].line = 1; + loc[currentSource].column = 0; + } + while (currentSource < numSources && lengths[currentSource] == 0) { + ++currentSource; + if (currentSource < numSources) { + loc[currentSource].string = loc[currentSource - 1].string + 1; + loc[currentSource].line = 1; + loc[currentSource].column = 0; + } + } + currentChar = 0; + } + } + + int numSources; // number of strings in source + const unsigned char* const *sources; // array of strings; must be converted to positive values on use, to avoid aliasing with -1 as EndOfInput + const size_t *lengths; // length of each string + int currentSource; + size_t currentChar; + + // This is for reporting what string/line an error occurred on, and can be overridden by #line. + // It remembers the last state of each source string as it is left for the next one, so unget() + // can restore that state. + TSourceLoc* loc; // an array + + int stringBias; // the first string that is the user's string number 0 + int finale; // number of internal strings after user's last string + + TSourceLoc logicalSourceLoc; + bool singleLogical; // treats the strings as a single logical string. + // locations will be reported from the first string. + + // Set to true once peek() returns EndOfFile, so that we won't roll back + // once we've reached EndOfFile. + bool endOfFileReached; +}; + +} // end namespace glslang + +#endif // _GLSLANG_SCAN_INCLUDED_ diff --git a/third_party/glslang/glslang/MachineIndependent/ScanContext.h b/third_party/glslang/glslang/MachineIndependent/ScanContext.h new file mode 100644 index 0000000..74b2b3c --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/ScanContext.h @@ -0,0 +1,93 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This holds context specific to the GLSL scanner, which +// sits between the preprocessor scanner and parser. +// + +#pragma once + +#include "ParseHelper.h" + +namespace glslang { + +class TPpContext; +class TPpToken; +class TParserToken; + +class TScanContext { +public: + explicit TScanContext(TParseContextBase& pc) : + parseContext(pc), + afterType(false), afterStruct(false), + field(false), afterBuffer(false) { } + virtual ~TScanContext() { } + + static void fillInKeywordMap(); + static void deleteKeywordMap(); + + int tokenize(TPpContext*, TParserToken&); + +protected: + TScanContext(TScanContext&); + TScanContext& operator=(TScanContext&); + + int tokenizeIdentifier(); + int identifierOrType(); + int reservedWord(); + int identifierOrReserved(bool reserved); + int es30ReservedFromGLSL(int version); + int nonreservedKeyword(int esVersion, int nonEsVersion); + int precisionKeyword(); + int matNxM(); + int dMat(); + int firstGenerationImage(bool inEs310); + int secondGenerationImage(); + + TParseContextBase& parseContext; + bool afterType; // true if we've recognized a type, so can only be looking for an identifier + bool afterStruct; // true if we've recognized the STRUCT keyword, so can only be looking for an identifier + bool field; // true if we're on a field, right after a '.' + bool afterBuffer; // true if we've recognized the BUFFER keyword + TSourceLoc loc; + TParserToken* parserToken; + TPpToken* ppToken; + + const char* tokenText; + int keyword; +}; + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp b/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp new file mode 100644 index 0000000..c6030bd --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/ShaderLang.cpp @@ -0,0 +1,2146 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2015-2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Implement the top-level of interface to the compiler/linker, +// as defined in ShaderLang.h +// This is the platform independent interface between an OGL driver +// and the shading language compiler/linker. +// +#include +#include +#include +#include +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "Scan.h" +#include "ScanContext.h" + +#ifdef ENABLE_HLSL +#include "../HLSL/hlslParseHelper.h" +#include "../HLSL/hlslParseables.h" +#include "../HLSL/hlslScanContext.h" +#endif + +#include "../Include/ShHandle.h" +#include "../../OGLCompilersDLL/InitializeDll.h" + +#include "preprocessor/PpContext.h" + +#define SH_EXPORTING +#include "../Public/ShaderLang.h" +#include "reflection.h" +#include "iomapper.h" +#include "Initialize.h" + +// TODO: this really shouldn't be here, it is only because of the trial addition +// of printing pre-processed tokens, which requires knowing the string literal +// token to print ", but none of that seems appropriate for this file. +#include "preprocessor/PpTokens.h" + +// Build-time generated includes +#include "glslang/build_info.h" + +namespace { // anonymous namespace for file-local functions and symbols + +// Total number of successful initializers of glslang: a refcount +// Shared global; access should be protected by a global mutex/critical section. +int NumberOfClients = 0; + +using namespace glslang; + +// Create a language specific version of parseables. +TBuiltInParseables* CreateBuiltInParseables(TInfoSink& infoSink, EShSource source) +{ + switch (source) { + case EShSourceGlsl: return new TBuiltIns(); // GLSL builtIns +#ifdef ENABLE_HLSL + case EShSourceHlsl: return new TBuiltInParseablesHlsl(); // HLSL intrinsics +#endif + + default: + infoSink.info.message(EPrefixInternalError, "Unable to determine source language"); + return nullptr; + } +} + +// Create a language specific version of a parse context. +TParseContextBase* CreateParseContext(TSymbolTable& symbolTable, TIntermediate& intermediate, + int version, EProfile profile, EShSource source, + EShLanguage language, TInfoSink& infoSink, + SpvVersion spvVersion, bool forwardCompatible, EShMessages messages, + bool parsingBuiltIns, std::string sourceEntryPointName = "") +{ + switch (source) { + case EShSourceGlsl: { + if (sourceEntryPointName.size() == 0) + intermediate.setEntryPointName("main"); + TString entryPoint = sourceEntryPointName.c_str(); + return new TParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion, + language, infoSink, forwardCompatible, messages, &entryPoint); + } +#ifdef ENABLE_HLSL + case EShSourceHlsl: + return new HlslParseContext(symbolTable, intermediate, parsingBuiltIns, version, profile, spvVersion, + language, infoSink, sourceEntryPointName.c_str(), forwardCompatible, messages); +#endif + default: + infoSink.info.message(EPrefixInternalError, "Unable to determine source language"); + return nullptr; + } +} + +// Local mapping functions for making arrays of symbol tables.... + +const int VersionCount = 17; // index range in MapVersionToIndex + +int MapVersionToIndex(int version) +{ + int index = 0; + + switch (version) { + case 100: index = 0; break; + case 110: index = 1; break; + case 120: index = 2; break; + case 130: index = 3; break; + case 140: index = 4; break; + case 150: index = 5; break; + case 300: index = 6; break; + case 330: index = 7; break; + case 400: index = 8; break; + case 410: index = 9; break; + case 420: index = 10; break; + case 430: index = 11; break; + case 440: index = 12; break; + case 310: index = 13; break; + case 450: index = 14; break; + case 500: index = 0; break; // HLSL + case 320: index = 15; break; + case 460: index = 16; break; + default: assert(0); break; + } + + assert(index < VersionCount); + + return index; +} + +const int SpvVersionCount = 3; // index range in MapSpvVersionToIndex + +int MapSpvVersionToIndex(const SpvVersion& spvVersion) +{ + int index = 0; + + if (spvVersion.openGl > 0) + index = 1; + else if (spvVersion.vulkan > 0) + index = 2; + + assert(index < SpvVersionCount); + + return index; +} + +const int ProfileCount = 4; // index range in MapProfileToIndex + +int MapProfileToIndex(EProfile profile) +{ + int index = 0; + + switch (profile) { + case ENoProfile: index = 0; break; + case ECoreProfile: index = 1; break; + case ECompatibilityProfile: index = 2; break; + case EEsProfile: index = 3; break; + default: break; + } + + assert(index < ProfileCount); + + return index; +} + +const int SourceCount = 2; + +int MapSourceToIndex(EShSource source) +{ + int index = 0; + + switch (source) { + case EShSourceGlsl: index = 0; break; + case EShSourceHlsl: index = 1; break; + default: break; + } + + assert(index < SourceCount); + + return index; +} + +// only one of these needed for non-ES; ES needs 2 for different precision defaults of built-ins +enum EPrecisionClass { + EPcGeneral, + EPcFragment, + EPcCount +}; + +// A process-global symbol table per version per profile for built-ins common +// to multiple stages (languages), and a process-global symbol table per version +// per profile per stage for built-ins unique to each stage. They will be sparsely +// populated, so they will only be generated as needed. +// +// Each has a different set of built-ins, and we want to preserve that from +// compile to compile. +// +TSymbolTable* CommonSymbolTable[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EPcCount] = {}; +TSymbolTable* SharedSymbolTables[VersionCount][SpvVersionCount][ProfileCount][SourceCount][EShLangCount] = {}; + +TPoolAllocator* PerProcessGPA = nullptr; + +// +// Parse and add to the given symbol table the content of the given shader string. +// +bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, const SpvVersion& spvVersion, EShLanguage language, + EShSource source, TInfoSink& infoSink, TSymbolTable& symbolTable) +{ + TIntermediate intermediate(language, version, profile); + + intermediate.setSource(source); + + std::unique_ptr parseContext(CreateParseContext(symbolTable, intermediate, version, profile, source, + language, infoSink, spvVersion, true, EShMsgDefault, + true)); + + TShader::ForbidIncluder includer; + TPpContext ppContext(*parseContext, "", includer); + TScanContext scanContext(*parseContext); + parseContext->setScanContext(&scanContext); + parseContext->setPpContext(&ppContext); + + // + // Push the symbol table to give it an initial scope. This + // push should not have a corresponding pop, so that built-ins + // are preserved, and the test for an empty table fails. + // + + symbolTable.push(); + + const char* builtInShaders[2]; + size_t builtInLengths[2]; + builtInShaders[0] = builtIns.c_str(); + builtInLengths[0] = builtIns.size(); + + if (builtInLengths[0] == 0) + return true; + + TInputScanner input(1, builtInShaders, builtInLengths); + if (! parseContext->parseShaderStrings(ppContext, input) != 0) { + infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins"); + printf("Unable to parse built-ins\n%s\n", infoSink.info.c_str()); + printf("%s\n", builtInShaders[0]); + + return false; + } + + return true; +} + +int CommonIndex(EProfile profile, EShLanguage language) +{ + return (profile == EEsProfile && language == EShLangFragment) ? EPcFragment : EPcGeneral; +} + +// +// To initialize per-stage shared tables, with the common table already complete. +// +void InitializeStageSymbolTable(TBuiltInParseables& builtInParseables, int version, EProfile profile, const SpvVersion& spvVersion, + EShLanguage language, EShSource source, TInfoSink& infoSink, TSymbolTable** commonTable, + TSymbolTable** symbolTables) +{ +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#elif defined(GLSLANG_ANGLE) + profile = ECoreProfile; + version = 450; +#endif + + (*symbolTables[language]).adoptLevels(*commonTable[CommonIndex(profile, language)]); + InitializeSymbolTable(builtInParseables.getStageString(language), version, profile, spvVersion, language, source, + infoSink, *symbolTables[language]); + builtInParseables.identifyBuiltIns(version, profile, spvVersion, language, *symbolTables[language]); + if (profile == EEsProfile && version >= 300) + (*symbolTables[language]).setNoBuiltInRedeclarations(); + if (version == 110) + (*symbolTables[language]).setSeparateNameSpaces(); +} + +// +// Initialize the full set of shareable symbol tables; +// The common (cross-stage) and those shareable per-stage. +// +bool InitializeSymbolTables(TInfoSink& infoSink, TSymbolTable** commonTable, TSymbolTable** symbolTables, int version, EProfile profile, const SpvVersion& spvVersion, EShSource source) +{ +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#elif defined(GLSLANG_ANGLE) + profile = ECoreProfile; + version = 450; +#endif + + std::unique_ptr builtInParseables(CreateBuiltInParseables(infoSink, source)); + + if (builtInParseables == nullptr) + return false; + + builtInParseables->initialize(version, profile, spvVersion); + + // do the common tables + InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangVertex, source, + infoSink, *commonTable[EPcGeneral]); + if (profile == EEsProfile) + InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, EShLangFragment, source, + infoSink, *commonTable[EPcFragment]); + + // do the per-stage tables + + // always have vertex and fragment + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangVertex, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangFragment, source, + infoSink, commonTable, symbolTables); + +#ifndef GLSLANG_WEB + // check for tessellation + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) { + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessControl, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTessEvaluation, source, + infoSink, commonTable, symbolTables); + } + + // check for geometry + if ((profile != EEsProfile && version >= 150) || + (profile == EEsProfile && version >= 310)) + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangGeometry, source, + infoSink, commonTable, symbolTables); + + // check for compute + if ((profile != EEsProfile && version >= 420) || + (profile == EEsProfile && version >= 310)) + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCompute, source, + infoSink, commonTable, symbolTables); + +#ifndef GLSLANG_ANGLE + // check for ray tracing stages + if (profile != EEsProfile && version >= 450) { + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangRayGen, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangIntersect, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangAnyHit, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangClosestHit, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMiss, source, + infoSink, commonTable, symbolTables); + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangCallable, source, + infoSink, commonTable, symbolTables); + } + + // check for mesh + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangMeshNV, source, + infoSink, commonTable, symbolTables); + + // check for task + if ((profile != EEsProfile && version >= 450) || + (profile == EEsProfile && version >= 320)) + InitializeStageSymbolTable(*builtInParseables, version, profile, spvVersion, EShLangTaskNV, source, + infoSink, commonTable, symbolTables); +#endif // !GLSLANG_ANGLE +#endif // !GLSLANG_WEB + + return true; +} + +bool AddContextSpecificSymbols(const TBuiltInResource* resources, TInfoSink& infoSink, TSymbolTable& symbolTable, int version, + EProfile profile, const SpvVersion& spvVersion, EShLanguage language, EShSource source) +{ + std::unique_ptr builtInParseables(CreateBuiltInParseables(infoSink, source)); + + if (builtInParseables == nullptr) + return false; + + builtInParseables->initialize(*resources, version, profile, spvVersion, language); + InitializeSymbolTable(builtInParseables->getCommonString(), version, profile, spvVersion, language, source, infoSink, symbolTable); + builtInParseables->identifyBuiltIns(version, profile, spvVersion, language, symbolTable, *resources); + + return true; +} + +// +// To do this on the fly, we want to leave the current state of our thread's +// pool allocator intact, so: +// - Switch to a new pool for parsing the built-ins +// - Do the parsing, which builds the symbol table, using the new pool +// - Switch to the process-global pool to save a copy of the resulting symbol table +// - Free up the new pool used to parse the built-ins +// - Switch back to the original thread's pool +// +// This only gets done the first time any thread needs a particular symbol table +// (lazy evaluation). +// +void SetupBuiltinSymbolTable(int version, EProfile profile, const SpvVersion& spvVersion, EShSource source) +{ + TInfoSink infoSink; + + // Make sure only one thread tries to do this at a time + glslang::GetGlobalLock(); + + // See if it's already been done for this version/profile combination + int versionIndex = MapVersionToIndex(version); + int spvVersionIndex = MapSpvVersionToIndex(spvVersion); + int profileIndex = MapProfileToIndex(profile); + int sourceIndex = MapSourceToIndex(source); + if (CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][EPcGeneral]) { + glslang::ReleaseGlobalLock(); + + return; + } + + // Switch to a new pool + TPoolAllocator& previousAllocator = GetThreadPoolAllocator(); + TPoolAllocator* builtInPoolAllocator = new TPoolAllocator; + SetThreadPoolAllocator(builtInPoolAllocator); + + // Dynamically allocate the local symbol tables so we can control when they are deallocated WRT when the pool is popped. + TSymbolTable* commonTable[EPcCount]; + TSymbolTable* stageTables[EShLangCount]; + for (int precClass = 0; precClass < EPcCount; ++precClass) + commonTable[precClass] = new TSymbolTable; + for (int stage = 0; stage < EShLangCount; ++stage) + stageTables[stage] = new TSymbolTable; + + // Generate the local symbol tables using the new pool + InitializeSymbolTables(infoSink, commonTable, stageTables, version, profile, spvVersion, source); + + // Switch to the process-global pool + SetThreadPoolAllocator(PerProcessGPA); + + // Copy the local symbol tables from the new pool to the global tables using the process-global pool + for (int precClass = 0; precClass < EPcCount; ++precClass) { + if (! commonTable[precClass]->isEmpty()) { + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass] = new TSymbolTable; + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->copyTable(*commonTable[precClass]); + CommonSymbolTable[versionIndex][spvVersionIndex][profileIndex][sourceIndex][precClass]->readOnly(); + } + } + for (int stage = 0; stage < EShLangCount; ++stage) { + if (! stageTables[stage]->isEmpty()) { + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage] = new TSymbolTable; + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->adoptLevels(*CommonSymbolTable + [versionIndex][spvVersionIndex][profileIndex][sourceIndex][CommonIndex(profile, (EShLanguage)stage)]); + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->copyTable(*stageTables[stage]); + SharedSymbolTables[versionIndex][spvVersionIndex][profileIndex][sourceIndex][stage]->readOnly(); + } + } + + // Clean up the local tables before deleting the pool they used. + for (int precClass = 0; precClass < EPcCount; ++precClass) + delete commonTable[precClass]; + for (int stage = 0; stage < EShLangCount; ++stage) + delete stageTables[stage]; + + delete builtInPoolAllocator; + SetThreadPoolAllocator(&previousAllocator); + + glslang::ReleaseGlobalLock(); +} + +// Function to Print all builtins +void DumpBuiltinSymbolTable(TInfoSink& infoSink, const TSymbolTable& symbolTable) +{ +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + infoSink.debug << "BuiltinSymbolTable {\n"; + + symbolTable.dump(infoSink, true); + + infoSink.debug << "}\n"; +#endif +} + +// Return true if the shader was correctly specified for version/profile/stage. +bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion, + EShSource source, int& version, EProfile& profile, const SpvVersion& spvVersion) +{ + const int FirstProfileVersion = 150; + bool correct = true; + + if (source == EShSourceHlsl) { + version = 500; // shader model; currently a characteristic of glslang, not the input + profile = ECoreProfile; // allow doubles in prototype parsing + return correct; + } + + // Get a version... + if (version == 0) { + version = defaultVersion; + // infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader"); + } + + // Get a good profile... + if (profile == ENoProfile) { + if (version == 300 || version == 310 || version == 320) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 require specifying the 'es' profile"); + profile = EEsProfile; + } else if (version == 100) + profile = EEsProfile; + else if (version >= FirstProfileVersion) + profile = ECoreProfile; + else + profile = ENoProfile; + } else { + // a profile was provided... + if (version < 150) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token"); + if (version == 100) + profile = EEsProfile; + else + profile = ENoProfile; + } else if (version == 300 || version == 310 || version == 320) { + if (profile != EEsProfile) { + correct = false; + infoSink.info.message(EPrefixError, "#version: versions 300, 310, and 320 support only the es profile"); + } + profile = EEsProfile; + } else { + if (profile == EEsProfile) { + correct = false; + infoSink.info.message(EPrefixError, "#version: only version 300, 310, and 320 support the es profile"); + if (version >= FirstProfileVersion) + profile = ECoreProfile; + else + profile = ENoProfile; + } + // else: typical desktop case... e.g., "#version 410 core" + } + } + + // Fix version... + switch (version) { + // ES versions + case 100: break; + case 300: break; + case 310: break; + case 320: break; + + // desktop versions + case 110: break; + case 120: break; + case 130: break; + case 140: break; + case 150: break; + case 330: break; + case 400: break; + case 410: break; + case 420: break; + case 430: break; + case 440: break; + case 450: break; + case 460: break; + + // unknown version + default: + correct = false; + infoSink.info.message(EPrefixError, "version not supported"); + if (profile == EEsProfile) + version = 310; + else { + version = 450; + profile = ECoreProfile; + } + break; + } + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + // Correct for stage type... + switch (stage) { + case EShLangGeometry: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 150)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: geometry shaders require es profile with version 310 or non-es profile with version 150 or above"); + version = (profile == EEsProfile) ? 310 : 150; + if (profile == EEsProfile || profile == ENoProfile) + profile = ECoreProfile; + } + break; + case EShLangTessControl: + case EShLangTessEvaluation: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 150)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: tessellation shaders require es profile with version 310 or non-es profile with version 150 or above"); + version = (profile == EEsProfile) ? 310 : 400; // 150 supports the extension, correction is to 400 which does not + if (profile == EEsProfile || profile == ENoProfile) + profile = ECoreProfile; + } + break; + case EShLangCompute: + if ((profile == EEsProfile && version < 310) || + (profile != EEsProfile && version < 420)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: compute shaders require es profile with version 310 or above, or non-es profile with version 420 or above"); + version = profile == EEsProfile ? 310 : 420; + } + break; + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (profile == EEsProfile || version < 460) { + correct = false; + infoSink.info.message(EPrefixError, "#version: ray tracing shaders require non-es profile with version 460 or above"); + version = 460; + } + break; + case EShLangMeshNV: + case EShLangTaskNV: + if ((profile == EEsProfile && version < 320) || + (profile != EEsProfile && version < 450)) { + correct = false; + infoSink.info.message(EPrefixError, "#version: mesh/task shaders require es profile with version 320 or above, or non-es profile with version 450 or above"); + version = profile == EEsProfile ? 320 : 450; + } + default: + break; + } + + if (profile == EEsProfile && version >= 300 && versionNotFirst) { + correct = false; + infoSink.info.message(EPrefixError, "#version: statement must appear first in es-profile shader; before comments or newlines"); + } + + // Check for SPIR-V compatibility + if (spvVersion.spv != 0) { + switch (profile) { + case EEsProfile: + if (version < 310) { + correct = false; + infoSink.info.message(EPrefixError, "#version: ES shaders for SPIR-V require version 310 or higher"); + version = 310; + } + break; + case ECompatibilityProfile: + infoSink.info.message(EPrefixError, "#version: compilation for SPIR-V does not support the compatibility profile"); + break; + default: + if (spvVersion.vulkan > 0 && version < 140) { + correct = false; + infoSink.info.message(EPrefixError, "#version: Desktop shaders for Vulkan SPIR-V require version 140 or higher"); + version = 140; + } + if (spvVersion.openGl >= 100 && version < 330) { + correct = false; + infoSink.info.message(EPrefixError, "#version: Desktop shaders for OpenGL SPIR-V require version 330 or higher"); + version = 330; + } + break; + } + } +#endif + + return correct; +} + +// There are multiple paths in for setting environment stuff. +// TEnvironment takes precedence, for what it sets, so sort all this out. +// Ideally, the internal code could be made to use TEnvironment, but for +// now, translate it to the historically used parameters. +void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages, EShSource& source, + EShLanguage& stage, SpvVersion& spvVersion) +{ + // Set up environmental defaults, first ignoring 'environment'. + if (messages & EShMsgSpvRules) + spvVersion.spv = EShTargetSpv_1_0; + if (messages & EShMsgVulkanRules) { + spvVersion.vulkan = EShTargetVulkan_1_0; + spvVersion.vulkanGlsl = 100; + } else if (spvVersion.spv != 0) + spvVersion.openGl = 100; + + // Now, override, based on any content set in 'environment'. + // 'environment' must be cleared to ESh*None settings when items + // are not being set. + if (environment != nullptr) { + // input language + if (environment->input.languageFamily != EShSourceNone) { + stage = environment->input.stage; + switch (environment->input.dialect) { + case EShClientNone: + break; + case EShClientVulkan: + spvVersion.vulkanGlsl = environment->input.dialectVersion; + break; + case EShClientOpenGL: + spvVersion.openGl = environment->input.dialectVersion; + break; + case EShClientCount: + assert(0); + break; + } + switch (environment->input.languageFamily) { + case EShSourceNone: + break; + case EShSourceGlsl: + source = EShSourceGlsl; + messages = static_cast(messages & ~EShMsgReadHlsl); + break; + case EShSourceHlsl: + source = EShSourceHlsl; + messages = static_cast(messages | EShMsgReadHlsl); + break; + case EShSourceCount: + assert(0); + break; + } + } + + // client + switch (environment->client.client) { + case EShClientVulkan: + spvVersion.vulkan = environment->client.version; + break; + default: + break; + } + + // generated code + switch (environment->target.language) { + case EshTargetSpv: + spvVersion.spv = environment->target.version; + break; + default: + break; + } + } +} + +// Most processes are recorded when set in the intermediate representation, +// These are the few that are not. +void RecordProcesses(TIntermediate& intermediate, EShMessages messages, const std::string& sourceEntryPointName) +{ + if ((messages & EShMsgRelaxedErrors) != 0) + intermediate.addProcess("relaxed-errors"); + if ((messages & EShMsgSuppressWarnings) != 0) + intermediate.addProcess("suppress-warnings"); + if ((messages & EShMsgKeepUncalled) != 0) + intermediate.addProcess("keep-uncalled"); + if (sourceEntryPointName.size() > 0) { + intermediate.addProcess("source-entrypoint"); + intermediate.addProcessArgument(sourceEntryPointName); + } +} + +// This is the common setup and cleanup code for PreprocessDeferred and +// CompileDeferred. +// It takes any callable with a signature of +// bool (TParseContextBase& parseContext, TPpContext& ppContext, +// TInputScanner& input, bool versionWillBeError, +// TSymbolTable& , TIntermediate& , +// EShOptimizationLevel , EShMessages ); +// Which returns false if a failure was detected and true otherwise. +// +template +bool ProcessDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* customPreamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop; this is the GLSL version, not SPIR-V or Vulkan + EProfile defaultProfile, + // set version/profile to defaultVersion/defaultProfile regardless of the #version + // directive in the source code + bool forceDefaultVersionAndProfile, + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TIntermediate& intermediate, // returned tree, etc. + ProcessingContext& processingContext, + bool requireNonempty, + TShader::Includer& includer, + const std::string sourceEntryPointName = "", + const TEnvironment* environment = nullptr) // optional way of fully setting all versions, overriding the above +{ + // This must be undone (.pop()) by the caller, after it finishes consuming the created tree. + GetThreadPoolAllocator().push(); + + if (numStrings == 0) + return true; + + // Move to length-based strings, rather than null-terminated strings. + // Also, add strings to include the preamble and to ensure the shader is not null, + // which lets the grammar accept what was a null (post preprocessing) shader. + // + // Shader will look like + // string 0: system preamble + // string 1: custom preamble + // string 2...numStrings+1: user's shader + // string numStrings+2: "int;" + const int numPre = 2; + const int numPost = requireNonempty? 1 : 0; + const int numTotal = numPre + numStrings + numPost; + std::unique_ptr lengths(new size_t[numTotal]); + std::unique_ptr strings(new const char*[numTotal]); + std::unique_ptr names(new const char*[numTotal]); + for (int s = 0; s < numStrings; ++s) { + strings[s + numPre] = shaderStrings[s]; + if (inputLengths == nullptr || inputLengths[s] < 0) + lengths[s + numPre] = strlen(shaderStrings[s]); + else + lengths[s + numPre] = inputLengths[s]; + } + if (stringNames != nullptr) { + for (int s = 0; s < numStrings; ++s) + names[s + numPre] = stringNames[s]; + } else { + for (int s = 0; s < numStrings; ++s) + names[s + numPre] = nullptr; + } + + // Get all the stages, languages, clients, and other environment + // stuff sorted out. + EShSource sourceGuess = (messages & EShMsgReadHlsl) != 0 ? EShSourceHlsl : EShSourceGlsl; + SpvVersion spvVersion; + EShLanguage stage = compiler->getLanguage(); + TranslateEnvironment(environment, messages, sourceGuess, stage, spvVersion); +#ifdef ENABLE_HLSL + EShSource source = sourceGuess; + if (environment != nullptr && environment->target.hlslFunctionality1) + intermediate.setHlslFunctionality1(); +#else + const EShSource source = EShSourceGlsl; +#endif + // First, without using the preprocessor or parser, find the #version, so we know what + // symbol tables, processing rules, etc. to set up. This does not need the extra strings + // outlined above, just the user shader, after the system and user preambles. + glslang::TInputScanner userInput(numStrings, &strings[numPre], &lengths[numPre]); + int version = 0; + EProfile profile = ENoProfile; + bool versionNotFirstToken = false; + bool versionNotFirst = (source == EShSourceHlsl) + ? true + : userInput.scanVersion(version, profile, versionNotFirstToken); + bool versionNotFound = version == 0; + if (forceDefaultVersionAndProfile && source == EShSourceGlsl) { +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + if (! (messages & EShMsgSuppressWarnings) && ! versionNotFound && + (version != defaultVersion || profile != defaultProfile)) { + compiler->infoSink.info << "Warning, (version, profile) forced to be (" + << defaultVersion << ", " << ProfileName(defaultProfile) + << "), while in source code it is (" + << version << ", " << ProfileName(profile) << ")\n"; + } +#endif + if (versionNotFound) { + versionNotFirstToken = false; + versionNotFirst = false; + versionNotFound = false; + } + version = defaultVersion; + profile = defaultProfile; + } + + bool goodVersion = DeduceVersionProfile(compiler->infoSink, stage, + versionNotFirst, defaultVersion, source, version, profile, spvVersion); +#ifdef GLSLANG_WEB + profile = EEsProfile; + version = 310; +#elif defined(GLSLANG_ANGLE) + profile = ECoreProfile; + version = 450; +#endif + + bool versionWillBeError = (versionNotFound || (profile == EEsProfile && version >= 300 && versionNotFirst)); +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + bool warnVersionNotFirst = false; + if (! versionWillBeError && versionNotFirstToken) { + if (messages & EShMsgRelaxedErrors) + warnVersionNotFirst = true; + else + versionWillBeError = true; + } +#endif + + intermediate.setSource(source); + intermediate.setVersion(version); + intermediate.setProfile(profile); + intermediate.setSpv(spvVersion); + RecordProcesses(intermediate, messages, sourceEntryPointName); + if (spvVersion.vulkan > 0) + intermediate.setOriginUpperLeft(); +#ifdef ENABLE_HLSL + if ((messages & EShMsgHlslOffsets) || source == EShSourceHlsl) + intermediate.setHlslOffsets(); +#endif + if (messages & EShMsgDebugInfo) { + intermediate.setSourceFile(names[numPre]); + for (int s = 0; s < numStrings; ++s) { + // The string may not be null-terminated, so make sure we provide + // the length along with the string. + intermediate.addSourceText(strings[numPre + s], lengths[numPre + s]); + } + } + SetupBuiltinSymbolTable(version, profile, spvVersion, source); + + TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)] + [MapSpvVersionToIndex(spvVersion)] + [MapProfileToIndex(profile)] + [MapSourceToIndex(source)] + [stage]; + + // Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool. + std::unique_ptr symbolTable(new TSymbolTable); + if (cachedTable) + symbolTable->adoptLevels(*cachedTable); + + // Add built-in symbols that are potentially context dependent; + // they get popped again further down. + if (! AddContextSpecificSymbols(resources, compiler->infoSink, *symbolTable, version, profile, spvVersion, + stage, source)) { + return false; + } + + if (messages & EShMsgBuiltinSymbolTable) + DumpBuiltinSymbolTable(compiler->infoSink, *symbolTable); + + // + // Now we can process the full shader under proper symbols and rules. + // + + std::unique_ptr parseContext(CreateParseContext(*symbolTable, intermediate, version, profile, source, + stage, compiler->infoSink, + spvVersion, forwardCompatible, messages, false, sourceEntryPointName)); + TPpContext ppContext(*parseContext, names[numPre] ? names[numPre] : "", includer); + + // only GLSL (bison triggered, really) needs an externally set scan context + glslang::TScanContext scanContext(*parseContext); + if (source == EShSourceGlsl) + parseContext->setScanContext(&scanContext); + + parseContext->setPpContext(&ppContext); + parseContext->setLimits(*resources); + if (! goodVersion) + parseContext->addError(); +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + if (warnVersionNotFirst) { + TSourceLoc loc; + loc.init(); + parseContext->warn(loc, "Illegal to have non-comment, non-whitespace tokens before #version", "#version", ""); + } +#endif + + parseContext->initializeExtensionBehavior(); + + // Fill in the strings as outlined above. + std::string preamble; + parseContext->getPreamble(preamble); + strings[0] = preamble.c_str(); + lengths[0] = strlen(strings[0]); + names[0] = nullptr; + strings[1] = customPreamble; + lengths[1] = strlen(strings[1]); + names[1] = nullptr; + assert(2 == numPre); + if (requireNonempty) { + const int postIndex = numStrings + numPre; + strings[postIndex] = "\n int;"; + lengths[postIndex] = strlen(strings[numStrings + numPre]); + names[postIndex] = nullptr; + } + TInputScanner fullInput(numStrings + numPre + numPost, strings.get(), lengths.get(), names.get(), numPre, numPost); + + // Push a new symbol allocation scope that will get used for the shader's globals. + symbolTable->push(); + + bool success = processingContext(*parseContext, ppContext, fullInput, + versionWillBeError, *symbolTable, + intermediate, optLevel, messages); + return success; +} + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +// Responsible for keeping track of the most recent source string and line in +// the preprocessor and outputting newlines appropriately if the source string +// or line changes. +class SourceLineSynchronizer { +public: + SourceLineSynchronizer(const std::function& lastSourceIndex, + std::string* output) + : getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {} +// SourceLineSynchronizer(const SourceLineSynchronizer&) = delete; +// SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete; + + // Sets the internally tracked source string index to that of the most + // recently read token. If we switched to a new source string, returns + // true and inserts a newline. Otherwise, returns false and outputs nothing. + bool syncToMostRecentString() { + if (getLastSourceIndex() != lastSource) { + // After switching to a new source string, we need to reset lastLine + // because line number resets every time a new source string is + // used. We also need to output a newline to separate the output + // from the previous source string (if there is one). + if (lastSource != -1 || lastLine != 0) + *output += '\n'; + lastSource = getLastSourceIndex(); + lastLine = -1; + return true; + } + return false; + } + + // Calls syncToMostRecentString() and then sets the internally tracked line + // number to tokenLine. If we switched to a new line, returns true and inserts + // newlines appropriately. Otherwise, returns false and outputs nothing. + bool syncToLine(int tokenLine) { + syncToMostRecentString(); + const bool newLineStarted = lastLine < tokenLine; + for (; lastLine < tokenLine; ++lastLine) { + if (lastLine > 0) *output += '\n'; + } + return newLineStarted; + } + + // Sets the internally tracked line number to newLineNum. + void setLineNum(int newLineNum) { lastLine = newLineNum; } + +private: + SourceLineSynchronizer& operator=(const SourceLineSynchronizer&); + + // A function for getting the index of the last valid source string we've + // read tokens from. + const std::function getLastSourceIndex; + // output string for newlines. + std::string* output; + // lastSource is the source string index (starting from 0) of the last token + // processed. It is tracked in order for newlines to be inserted when a new + // source string starts. -1 means we haven't started processing any source + // string. + int lastSource; + // lastLine is the line number (starting from 1) of the last token processed. + // It is tracked in order for newlines to be inserted when a token appears + // on a new line. 0 means we haven't started processing any line in the + // current source string. + int lastLine; +}; + +// DoPreprocessing is a valid ProcessingContext template argument, +// which only performs the preprocessing step of compilation. +// It places the result in the "string" argument to its constructor. +// +// This is not an officially supported or fully working path. +struct DoPreprocessing { + explicit DoPreprocessing(std::string* string): outputString(string) {} + bool operator()(TParseContextBase& parseContext, TPpContext& ppContext, + TInputScanner& input, bool versionWillBeError, + TSymbolTable&, TIntermediate&, + EShOptimizationLevel, EShMessages) + { + // This is a list of tokens that do not require a space before or after. + static const std::string unNeededSpaceTokens = ";()[]"; + static const std::string noSpaceBeforeTokens = ","; + glslang::TPpToken ppToken; + + parseContext.setScanner(&input); + ppContext.setInput(input, versionWillBeError); + + std::string outputBuffer; + SourceLineSynchronizer lineSync( + std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputBuffer); + + parseContext.setExtensionCallback([&lineSync, &outputBuffer]( + int line, const char* extension, const char* behavior) { + lineSync.syncToLine(line); + outputBuffer += "#extension "; + outputBuffer += extension; + outputBuffer += " : "; + outputBuffer += behavior; + }); + + parseContext.setLineCallback([&lineSync, &outputBuffer, &parseContext]( + int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) { + // SourceNum is the number of the source-string that is being parsed. + lineSync.syncToLine(curLineNum); + outputBuffer += "#line "; + outputBuffer += std::to_string(newLineNum); + if (hasSource) { + outputBuffer += ' '; + if (sourceName != nullptr) { + outputBuffer += '\"'; + outputBuffer += sourceName; + outputBuffer += '\"'; + } else { + outputBuffer += std::to_string(sourceNum); + } + } + if (parseContext.lineDirectiveShouldSetNextLine()) { + // newLineNum is the new line number for the line following the #line + // directive. So the new line number for the current line is + newLineNum -= 1; + } + outputBuffer += '\n'; + // And we are at the next line of the #line directive now. + lineSync.setLineNum(newLineNum + 1); + }); + + parseContext.setVersionCallback( + [&lineSync, &outputBuffer](int line, int version, const char* str) { + lineSync.syncToLine(line); + outputBuffer += "#version "; + outputBuffer += std::to_string(version); + if (str) { + outputBuffer += ' '; + outputBuffer += str; + } + }); + + parseContext.setPragmaCallback([&lineSync, &outputBuffer]( + int line, const glslang::TVector& ops) { + lineSync.syncToLine(line); + outputBuffer += "#pragma "; + for(size_t i = 0; i < ops.size(); ++i) { + outputBuffer += ops[i].c_str(); + } + }); + + parseContext.setErrorCallback([&lineSync, &outputBuffer]( + int line, const char* errorMessage) { + lineSync.syncToLine(line); + outputBuffer += "#error "; + outputBuffer += errorMessage; + }); + + int lastToken = EndOfInput; // lastToken records the last token processed. + do { + int token = ppContext.tokenize(ppToken); + if (token == EndOfInput) + break; + + bool isNewString = lineSync.syncToMostRecentString(); + bool isNewLine = lineSync.syncToLine(ppToken.loc.line); + + if (isNewLine) { + // Don't emit whitespace onto empty lines. + // Copy any whitespace characters at the start of a line + // from the input to the output. + outputBuffer += std::string(ppToken.loc.column - 1, ' '); + } + + // Output a space in between tokens, but not at the start of a line, + // and also not around special tokens. This helps with readability + // and consistency. + if (!isNewString && !isNewLine && lastToken != EndOfInput && + (unNeededSpaceTokens.find((char)token) == std::string::npos) && + (unNeededSpaceTokens.find((char)lastToken) == std::string::npos) && + (noSpaceBeforeTokens.find((char)token) == std::string::npos)) { + outputBuffer += ' '; + } + lastToken = token; + if (token == PpAtomConstString) + outputBuffer += "\""; + outputBuffer += ppToken.name; + if (token == PpAtomConstString) + outputBuffer += "\""; + } while (true); + outputBuffer += '\n'; + *outputString = std::move(outputBuffer); + + bool success = true; + if (parseContext.getNumErrors() > 0) { + success = false; + parseContext.infoSink.info.prefix(EPrefixError); + parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n"; + } + return success; + } + std::string* outputString; +}; + +#endif + +// DoFullParse is a valid ProcessingConext template argument for fully +// parsing the shader. It populates the "intermediate" with the AST. +struct DoFullParse{ + bool operator()(TParseContextBase& parseContext, TPpContext& ppContext, + TInputScanner& fullInput, bool versionWillBeError, + TSymbolTable&, TIntermediate& intermediate, + EShOptimizationLevel optLevel, EShMessages messages) + { + bool success = true; + // Parse the full shader. + if (! parseContext.parseShaderStrings(ppContext, fullInput, versionWillBeError)) + success = false; + + if (success && intermediate.getTreeRoot()) { + if (optLevel == EShOptNoGeneration) + parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested."); + else + success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.getLanguage()); + } else if (! success) { + parseContext.infoSink.info.prefix(EPrefixError); + parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n"; + } + +#ifndef GLSLANG_ANGLE + if (messages & EShMsgAST) + intermediate.output(parseContext.infoSink, true); +#endif + + return success; + } +}; + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) +// Take a single compilation unit, and run the preprocessor on it. +// Return: True if there were no issues found in preprocessing, +// False if during preprocessing any unknown version, pragmas or +// extensions were found. +// +// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string +// is not an officially supported or fully working path. +bool PreprocessDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* preamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop + EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TShader::Includer& includer, + TIntermediate& intermediate, // returned tree, etc. + std::string* outputString) +{ + DoPreprocessing parser(outputString); + return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, + preamble, optLevel, resources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, + forwardCompatible, messages, intermediate, parser, + false, includer); +} +#endif + +// +// do a partial compile on the given strings for a single compilation unit +// for a potential deferred link into a single stage (and deferred full compile of that +// stage through machine-dependent compilation). +// +// all preprocessing, parsing, semantic checks, etc. for a single compilation unit +// are done here. +// +// return: the tree and other information is filled into the intermediate argument, +// and true is returned by the function for success. +// +bool CompileDeferred( + TCompiler* compiler, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const char* const stringNames[], + const char* preamble, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int defaultVersion, // use 100 for ES environment, 110 for desktop + EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages, // warnings/errors/AST; things to print out + TIntermediate& intermediate,// returned tree, etc. + TShader::Includer& includer, + const std::string sourceEntryPointName = "", + TEnvironment* environment = nullptr) +{ + DoFullParse parser; + return ProcessDeferred(compiler, shaderStrings, numStrings, inputLengths, stringNames, + preamble, optLevel, resources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, + forwardCompatible, messages, intermediate, parser, + true, includer, sourceEntryPointName, environment); +} + +} // end anonymous namespace for local functions + +// +// ShInitialize() should be called exactly once per process, not per thread. +// +int ShInitialize() +{ + glslang::InitGlobalLock(); + + if (! InitProcess()) + return 0; + + glslang::GetGlobalLock(); + ++NumberOfClients; + glslang::ReleaseGlobalLock(); + + if (PerProcessGPA == nullptr) + PerProcessGPA = new TPoolAllocator(); + + glslang::TScanContext::fillInKeywordMap(); +#ifdef ENABLE_HLSL + glslang::HlslScanContext::fillInKeywordMap(); +#endif + + return 1; +} + +// +// Driver calls these to create and destroy compiler/linker +// objects. +// + +ShHandle ShConstructCompiler(const EShLanguage language, int debugOptions) +{ + if (!InitThread()) + return 0; + + TShHandleBase* base = static_cast(ConstructCompiler(language, debugOptions)); + + return reinterpret_cast(base); +} + +ShHandle ShConstructLinker(const EShExecutable executable, int debugOptions) +{ + if (!InitThread()) + return 0; + + TShHandleBase* base = static_cast(ConstructLinker(executable, debugOptions)); + + return reinterpret_cast(base); +} + +ShHandle ShConstructUniformMap() +{ + if (!InitThread()) + return 0; + + TShHandleBase* base = static_cast(ConstructUniformMap()); + + return reinterpret_cast(base); +} + +void ShDestruct(ShHandle handle) +{ + if (handle == 0) + return; + + TShHandleBase* base = static_cast(handle); + + if (base->getAsCompiler()) + DeleteCompiler(base->getAsCompiler()); + else if (base->getAsLinker()) + DeleteLinker(base->getAsLinker()); + else if (base->getAsUniformMap()) + DeleteUniformMap(base->getAsUniformMap()); +} + +// +// Cleanup symbol tables +// +int ShFinalize() +{ + glslang::GetGlobalLock(); + --NumberOfClients; + assert(NumberOfClients >= 0); + bool finalize = NumberOfClients == 0; + glslang::ReleaseGlobalLock(); + if (! finalize) + return 1; + + for (int version = 0; version < VersionCount; ++version) { + for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) { + for (int p = 0; p < ProfileCount; ++p) { + for (int source = 0; source < SourceCount; ++source) { + for (int stage = 0; stage < EShLangCount; ++stage) { + delete SharedSymbolTables[version][spvVersion][p][source][stage]; + SharedSymbolTables[version][spvVersion][p][source][stage] = 0; + } + } + } + } + } + + for (int version = 0; version < VersionCount; ++version) { + for (int spvVersion = 0; spvVersion < SpvVersionCount; ++spvVersion) { + for (int p = 0; p < ProfileCount; ++p) { + for (int source = 0; source < SourceCount; ++source) { + for (int pc = 0; pc < EPcCount; ++pc) { + delete CommonSymbolTable[version][spvVersion][p][source][pc]; + CommonSymbolTable[version][spvVersion][p][source][pc] = 0; + } + } + } + } + } + + if (PerProcessGPA != nullptr) { + delete PerProcessGPA; + PerProcessGPA = nullptr; + } + + glslang::TScanContext::deleteKeywordMap(); +#ifdef ENABLE_HLSL + glslang::HlslScanContext::deleteKeywordMap(); +#endif + + return 1; +} + +// +// Do a full compile on the given strings for a single compilation unit +// forming a complete stage. The result of the machine dependent compilation +// is left in the provided compile object. +// +// Return: The return value is really boolean, indicating +// success (1) or failure (0). +// +int ShCompile( + const ShHandle handle, + const char* const shaderStrings[], + const int numStrings, + const int* inputLengths, + const EShOptimizationLevel optLevel, + const TBuiltInResource* resources, + int /*debugOptions*/, + int defaultVersion, // use 100 for ES environment, 110 for desktop + bool forwardCompatible, // give errors for use of deprecated features + EShMessages messages // warnings/errors/AST; things to print out + ) +{ + // Map the generic handle to the C++ object + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TCompiler* compiler = base->getAsCompiler(); + if (compiler == 0) + return 0; + + SetThreadPoolAllocator(compiler->getPool()); + + compiler->infoSink.info.erase(); + compiler->infoSink.debug.erase(); + + TIntermediate intermediate(compiler->getLanguage()); + TShader::ForbidIncluder includer; + bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, nullptr, + "", optLevel, resources, defaultVersion, ENoProfile, false, + forwardCompatible, messages, intermediate, includer); + + // + // Call the machine dependent compiler + // + if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration) + success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile()); + + intermediate.removeTree(); + + // Throw away all the temporary memory used by the compilation process. + // The push was done in the CompileDeferred() call above. + GetThreadPoolAllocator().pop(); + + return success ? 1 : 0; +} + +// +// Link the given compile objects. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShLinkExt( + const ShHandle linkHandle, + const ShHandle compHandles[], + const int numHandles) +{ + if (linkHandle == 0 || numHandles == 0) + return 0; + + THandleList cObjects; + + for (int i = 0; i < numHandles; ++i) { + if (compHandles[i] == 0) + return 0; + TShHandleBase* base = reinterpret_cast(compHandles[i]); + if (base->getAsLinker()) { + cObjects.push_back(base->getAsLinker()); + } + if (base->getAsCompiler()) + cObjects.push_back(base->getAsCompiler()); + + if (cObjects[i] == 0) + return 0; + } + + TShHandleBase* base = reinterpret_cast(linkHandle); + TLinker* linker = static_cast(base->getAsLinker()); + + SetThreadPoolAllocator(linker->getPool()); + + if (linker == 0) + return 0; + + linker->infoSink.info.erase(); + + for (int i = 0; i < numHandles; ++i) { + if (cObjects[i]->getAsCompiler()) { + if (! cObjects[i]->getAsCompiler()->linkable()) { + linker->infoSink.info.message(EPrefixError, "Not all shaders have valid object code."); + return 0; + } + } + } + + bool ret = linker->link(cObjects); + + return ret ? 1 : 0; +} + +// +// ShSetEncrpytionMethod is a place-holder for specifying +// how source code is encrypted. +// +void ShSetEncryptionMethod(ShHandle handle) +{ + if (handle == 0) + return; +} + +// +// Return any compiler/linker/uniformmap log of messages for the application. +// +const char* ShGetInfoLog(const ShHandle handle) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = static_cast(handle); + TInfoSink* infoSink; + + if (base->getAsCompiler()) + infoSink = &(base->getAsCompiler()->getInfoSink()); + else if (base->getAsLinker()) + infoSink = &(base->getAsLinker()->getInfoSink()); + else + return 0; + + infoSink->info << infoSink->debug.c_str(); + return infoSink->info.c_str(); +} + +// +// Return the resulting binary code from the link process. Structure +// is machine dependent. +// +const void* ShGetExecutable(const ShHandle handle) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + + TLinker* linker = static_cast(base->getAsLinker()); + if (linker == 0) + return 0; + + return linker->getObjectCode(); +} + +// +// Let the linker know where the application said it's attributes are bound. +// The linker does not use these values, they are remapped by the ICD or +// hardware. It just needs them to know what's aliased. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShSetVirtualAttributeBindings(const ShHandle handle, const ShBindingTable* table) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + + if (linker == 0) + return 0; + + linker->setAppAttributeBindings(table); + + return 1; +} + +// +// Let the linker know where the predefined attributes have to live. +// +int ShSetFixedAttributeBindings(const ShHandle handle, const ShBindingTable* table) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + + if (linker == 0) + return 0; + + linker->setFixedAttributeBindings(table); + return 1; +} + +// +// Some attribute locations are off-limits to the linker... +// +int ShExcludeAttributes(const ShHandle handle, int *attributes, int count) +{ + if (handle == 0) + return 0; + + TShHandleBase* base = reinterpret_cast(handle); + TLinker* linker = static_cast(base->getAsLinker()); + if (linker == 0) + return 0; + + linker->setExcludedAttributes(attributes, count); + + return 1; +} + +// +// Return the index for OpenGL to use for knowing where a uniform lives. +// +// Return: The return value of is really boolean, indicating +// success or failure. +// +int ShGetUniformLocation(const ShHandle handle, const char* name) +{ + if (handle == 0) + return -1; + + TShHandleBase* base = reinterpret_cast(handle); + TUniformMap* uniformMap= base->getAsUniformMap(); + if (uniformMap == 0) + return -1; + + return uniformMap->getLocation(name); +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Deferred-Lowering C++ Interface +// ----------------------------------- +// +// Below is a new alternate C++ interface that might potentially replace the above +// opaque handle-based interface. +// +// See more detailed comment in ShaderLang.h +// + +namespace glslang { + +Version GetVersion() +{ + Version version; + version.major = GLSLANG_VERSION_MAJOR; + version.minor = GLSLANG_VERSION_MINOR; + version.patch = GLSLANG_VERSION_PATCH; + version.flavor = GLSLANG_VERSION_FLAVOR; + return version; +} + +#define QUOTE(s) #s +#define STR(n) QUOTE(n) + +const char* GetEsslVersionString() +{ + return "OpenGL ES GLSL 3.20 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR( + GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR; +} + +const char* GetGlslVersionString() +{ + return "4.60 glslang Khronos. " STR(GLSLANG_VERSION_MAJOR) "." STR(GLSLANG_VERSION_MINOR) "." STR( + GLSLANG_VERSION_PATCH) GLSLANG_VERSION_FLAVOR; +} + +int GetKhronosToolId() +{ + return 8; +} + +bool InitializeProcess() +{ + return ShInitialize() != 0; +} + +void FinalizeProcess() +{ + ShFinalize(); +} + +class TDeferredCompiler : public TCompiler { +public: + TDeferredCompiler(EShLanguage s, TInfoSink& i) : TCompiler(s, i) { } + virtual bool compile(TIntermNode*, int = 0, EProfile = ENoProfile) { return true; } +}; + +TShader::TShader(EShLanguage s) + : stage(s), lengths(nullptr), stringNames(nullptr), preamble("") +{ + pool = new TPoolAllocator; + infoSink = new TInfoSink; + compiler = new TDeferredCompiler(stage, *infoSink); + intermediate = new TIntermediate(s); + + // clear environment (avoid constructors in them for use in a C interface) + environment.input.languageFamily = EShSourceNone; + environment.input.dialect = EShClientNone; + environment.client.client = EShClientNone; + environment.target.language = EShTargetNone; + environment.target.hlslFunctionality1 = false; +} + +TShader::~TShader() +{ + delete infoSink; + delete compiler; + delete intermediate; + delete pool; +} + +void TShader::setStrings(const char* const* s, int n) +{ + strings = s; + numStrings = n; + lengths = nullptr; +} + +void TShader::setStringsWithLengths(const char* const* s, const int* l, int n) +{ + strings = s; + numStrings = n; + lengths = l; +} + +void TShader::setStringsWithLengthsAndNames( + const char* const* s, const int* l, const char* const* names, int n) +{ + strings = s; + numStrings = n; + lengths = l; + stringNames = names; +} + +void TShader::setEntryPoint(const char* entryPoint) +{ + intermediate->setEntryPointName(entryPoint); +} + +void TShader::setSourceEntryPoint(const char* name) +{ + sourceEntryPointName = name; +} + +// Log initial settings and transforms. +// See comment for class TProcesses. +void TShader::addProcesses(const std::vector& p) +{ + intermediate->addProcesses(p); +} + +void TShader::setInvertY(bool invert) { intermediate->setInvertY(invert); } +void TShader::setNanMinMaxClamp(bool useNonNan) { intermediate->setNanMinMaxClamp(useNonNan); } + +#ifndef GLSLANG_WEB + +// Set binding base for given resource type +void TShader::setShiftBinding(TResourceType res, unsigned int base) { + intermediate->setShiftBinding(res, base); +} + +// Set binding base for given resource type for a given binding set. +void TShader::setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set) { + intermediate->setShiftBindingForSet(res, base, set); +} + +// Set binding base for sampler types +void TShader::setShiftSamplerBinding(unsigned int base) { setShiftBinding(EResSampler, base); } +// Set binding base for texture types (SRV) +void TShader::setShiftTextureBinding(unsigned int base) { setShiftBinding(EResTexture, base); } +// Set binding base for image types +void TShader::setShiftImageBinding(unsigned int base) { setShiftBinding(EResImage, base); } +// Set binding base for uniform buffer objects (CBV) +void TShader::setShiftUboBinding(unsigned int base) { setShiftBinding(EResUbo, base); } +// Synonym for setShiftUboBinding, to match HLSL language. +void TShader::setShiftCbufferBinding(unsigned int base) { setShiftBinding(EResUbo, base); } +// Set binding base for UAV (unordered access view) +void TShader::setShiftUavBinding(unsigned int base) { setShiftBinding(EResUav, base); } +// Set binding base for SSBOs +void TShader::setShiftSsboBinding(unsigned int base) { setShiftBinding(EResSsbo, base); } +// Enables binding automapping using TIoMapper +void TShader::setAutoMapBindings(bool map) { intermediate->setAutoMapBindings(map); } +// Enables position.Y output negation in vertex shader + +// Fragile: currently within one stage: simple auto-assignment of location +void TShader::setAutoMapLocations(bool map) { intermediate->setAutoMapLocations(map); } +void TShader::addUniformLocationOverride(const char* name, int loc) +{ + intermediate->addUniformLocationOverride(name, loc); +} +void TShader::setUniformLocationBase(int base) +{ + intermediate->setUniformLocationBase(base); +} +void TShader::setNoStorageFormat(bool useUnknownFormat) { intermediate->setNoStorageFormat(useUnknownFormat); } +void TShader::setResourceSetBinding(const std::vector& base) { intermediate->setResourceSetBinding(base); } +void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); } +#endif + +#ifdef ENABLE_HLSL +// See comment above TDefaultHlslIoMapper in iomapper.cpp: +void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); } +void TShader::setFlattenUniformArrays(bool flatten) { intermediate->setFlattenUniformArrays(flatten); } +#endif + +// +// Turn the shader strings into a parse tree in the TIntermediate. +// +// Returns true for success. +// +bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages messages, Includer& includer) +{ + if (! InitThread()) + return false; + SetThreadPoolAllocator(pool); + + if (! preamble) + preamble = ""; + + return CompileDeferred(compiler, strings, numStrings, lengths, stringNames, + preamble, EShOptNone, builtInResources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, + forwardCompatible, messages, *intermediate, includer, sourceEntryPointName, + &environment); +} + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) +// Fill in a string with the result of preprocessing ShaderStrings +// Returns true if all extensions, pragmas and version strings were valid. +// +// NOTE: Doing just preprocessing to obtain a correct preprocessed shader string +// is not an officially supported or fully working path. +bool TShader::preprocess(const TBuiltInResource* builtInResources, + int defaultVersion, EProfile defaultProfile, + bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages message, + std::string* output_string, + Includer& includer) +{ + if (! InitThread()) + return false; + SetThreadPoolAllocator(pool); + + if (! preamble) + preamble = ""; + + return PreprocessDeferred(compiler, strings, numStrings, lengths, stringNames, preamble, + EShOptNone, builtInResources, defaultVersion, + defaultProfile, forceDefaultVersionAndProfile, + forwardCompatible, message, includer, *intermediate, output_string); +} +#endif + +const char* TShader::getInfoLog() +{ + return infoSink->info.c_str(); +} + +const char* TShader::getInfoDebugLog() +{ + return infoSink->debug.c_str(); +} + +TProgram::TProgram() : +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + reflection(0), +#endif + linked(false) +{ + pool = new TPoolAllocator; + infoSink = new TInfoSink; + for (int s = 0; s < EShLangCount; ++s) { + intermediate[s] = 0; + newedIntermediate[s] = false; + } +} + +TProgram::~TProgram() +{ + delete infoSink; +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + delete reflection; +#endif + + for (int s = 0; s < EShLangCount; ++s) + if (newedIntermediate[s]) + delete intermediate[s]; + + delete pool; +} + +// +// Merge the compilation units within each stage into a single TIntermediate. +// All starting compilation units need to be the result of calling TShader::parse(). +// +// Return true for success. +// +bool TProgram::link(EShMessages messages) +{ + if (linked) + return false; + linked = true; + + bool error = false; + + SetThreadPoolAllocator(pool); + + for (int s = 0; s < EShLangCount; ++s) { + if (! linkStage((EShLanguage)s, messages)) + error = true; + } + + // TODO: Link: cross-stage error checking + + return ! error; +} + +// +// Merge the compilation units within the given stage into a single TIntermediate. +// +// Return true for success. +// +bool TProgram::linkStage(EShLanguage stage, EShMessages messages) +{ + if (stages[stage].size() == 0) + return true; + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + int numEsShaders = 0, numNonEsShaders = 0; + for (auto it = stages[stage].begin(); it != stages[stage].end(); ++it) { + if ((*it)->intermediate->getProfile() == EEsProfile) { + numEsShaders++; + } else { + numNonEsShaders++; + } + } + + if (numEsShaders > 0 && numNonEsShaders > 0) { + infoSink->info.message(EPrefixError, "Cannot mix ES profile with non-ES profile shaders"); + return false; + } else if (numEsShaders > 1) { + infoSink->info.message(EPrefixError, "Cannot attach multiple ES shaders of the same type to a single program"); + return false; + } + + // + // Be efficient for the common single compilation unit per stage case, + // reusing it's TIntermediate instead of merging into a new one. + // + TIntermediate *firstIntermediate = stages[stage].front()->intermediate; + if (stages[stage].size() == 1) + intermediate[stage] = firstIntermediate; + else { + intermediate[stage] = new TIntermediate(stage, + firstIntermediate->getVersion(), + firstIntermediate->getProfile()); + intermediate[stage]->setLimits(firstIntermediate->getLimits()); + + // The new TIntermediate must use the same origin as the original TIntermediates. + // Otherwise linking will fail due to different coordinate systems. + if (firstIntermediate->getOriginUpperLeft()) { + intermediate[stage]->setOriginUpperLeft(); + } + intermediate[stage]->setSpv(firstIntermediate->getSpv()); + + newedIntermediate[stage] = true; + } + + if (messages & EShMsgAST) + infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n"; + + if (stages[stage].size() > 1) { + std::list::const_iterator it; + for (it = stages[stage].begin(); it != stages[stage].end(); ++it) + intermediate[stage]->merge(*infoSink, *(*it)->intermediate); + } +#else + intermediate[stage] = stages[stage].front()->intermediate; +#endif + intermediate[stage]->finalCheck(*infoSink, (messages & EShMsgKeepUncalled) != 0); + +#ifndef GLSLANG_ANGLE + if (messages & EShMsgAST) + intermediate[stage]->output(*infoSink, true); +#endif + + return intermediate[stage]->getNumErrors() == 0; +} + +const char* TProgram::getInfoLog() +{ + return infoSink->info.c_str(); +} + +const char* TProgram::getInfoDebugLog() +{ + return infoSink->debug.c_str(); +} + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +// +// Reflection implementation. +// + +bool TProgram::buildReflection(int opts) +{ + if (! linked || reflection != nullptr) + return false; + + int firstStage = EShLangVertex, lastStage = EShLangFragment; + + if (opts & EShReflectionIntermediateIO) { + // if we're reflecting intermediate I/O, determine the first and last stage linked and use those as the + // boundaries for which stages generate pipeline inputs/outputs + firstStage = EShLangCount; + lastStage = 0; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + firstStage = std::min(firstStage, s); + lastStage = std::max(lastStage, s); + } + } + } + + reflection = new TReflection((EShReflectionOptions)opts, (EShLanguage)firstStage, (EShLanguage)lastStage); + + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + if (! reflection->addStage((EShLanguage)s, *intermediate[s])) + return false; + } + } + + return true; +} + +unsigned TProgram::getLocalSize(int dim) const { return reflection->getLocalSize(dim); } +int TProgram::getReflectionIndex(const char* name) const { return reflection->getIndex(name); } +int TProgram::getReflectionPipeIOIndex(const char* name, const bool inOrOut) const + { return reflection->getPipeIOIndex(name, inOrOut); } + +int TProgram::getNumUniformVariables() const { return reflection->getNumUniforms(); } +const TObjectReflection& TProgram::getUniform(int index) const { return reflection->getUniform(index); } +int TProgram::getNumUniformBlocks() const { return reflection->getNumUniformBlocks(); } +const TObjectReflection& TProgram::getUniformBlock(int index) const { return reflection->getUniformBlock(index); } +int TProgram::getNumPipeInputs() const { return reflection->getNumPipeInputs(); } +const TObjectReflection& TProgram::getPipeInput(int index) const { return reflection->getPipeInput(index); } +int TProgram::getNumPipeOutputs() const { return reflection->getNumPipeOutputs(); } +const TObjectReflection& TProgram::getPipeOutput(int index) const { return reflection->getPipeOutput(index); } +int TProgram::getNumBufferVariables() const { return reflection->getNumBufferVariables(); } +const TObjectReflection& TProgram::getBufferVariable(int index) const { return reflection->getBufferVariable(index); } +int TProgram::getNumBufferBlocks() const { return reflection->getNumStorageBuffers(); } +const TObjectReflection& TProgram::getBufferBlock(int index) const { return reflection->getStorageBufferBlock(index); } +int TProgram::getNumAtomicCounters() const { return reflection->getNumAtomicCounters(); } +const TObjectReflection& TProgram::getAtomicCounter(int index) const { return reflection->getAtomicCounter(index); } +void TProgram::dumpReflection() { if (reflection != nullptr) reflection->dump(); } + +// +// I/O mapping implementation. +// +bool TProgram::mapIO(TIoMapResolver* pResolver, TIoMapper* pIoMapper) +{ + if (! linked) + return false; + TIoMapper* ioMapper = nullptr; + TIoMapper defaultIOMapper; + if (pIoMapper == nullptr) + ioMapper = &defaultIOMapper; + else + ioMapper = pIoMapper; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) { + if (! ioMapper->addStage((EShLanguage)s, *intermediate[s], *infoSink, pResolver)) + return false; + } + } + + return ioMapper->doMap(pResolver, *infoSink); +} + +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp b/third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp new file mode 100644 index 0000000..f6291c3 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/SymbolTable.cpp @@ -0,0 +1,450 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Symbol table for parsing. Most functionality and main ideas +// are documented in the header file. +// + +#include "SymbolTable.h" + +namespace glslang { + +// +// TType helper function needs a place to live. +// + +// +// Recursively generate mangled names. +// +void TType::buildMangledName(TString& mangledName) const +{ + if (isMatrix()) + mangledName += 'm'; + else if (isVector()) + mangledName += 'v'; + + switch (basicType) { + case EbtFloat: mangledName += 'f'; break; + case EbtInt: mangledName += 'i'; break; + case EbtUint: mangledName += 'u'; break; + case EbtBool: mangledName += 'b'; break; +#ifndef GLSLANG_WEB + case EbtDouble: mangledName += 'd'; break; + case EbtFloat16: mangledName += "f16"; break; + case EbtInt8: mangledName += "i8"; break; + case EbtUint8: mangledName += "u8"; break; + case EbtInt16: mangledName += "i16"; break; + case EbtUint16: mangledName += "u16"; break; + case EbtInt64: mangledName += "i64"; break; + case EbtUint64: mangledName += "u64"; break; + case EbtAtomicUint: mangledName += "au"; break; + case EbtAccStruct: mangledName += "as"; break; + case EbtRayQuery: mangledName += "rq"; break; +#endif + case EbtSampler: + switch (sampler.type) { +#ifndef GLSLANG_WEB + case EbtFloat16: mangledName += "f16"; break; +#endif + case EbtInt: mangledName += "i"; break; + case EbtUint: mangledName += "u"; break; + case EbtInt64: mangledName += "i64"; break; + case EbtUint64: mangledName += "u64"; break; + default: break; // some compilers want this + } + if (sampler.isImageClass()) + mangledName += "I"; // a normal image or subpass + else if (sampler.isPureSampler()) + mangledName += "p"; // a "pure" sampler + else if (!sampler.isCombined()) + mangledName += "t"; // a "pure" texture + else + mangledName += "s"; // traditional combined sampler + if (sampler.isArrayed()) + mangledName += "A"; + if (sampler.isShadow()) + mangledName += "S"; + if (sampler.isExternal()) + mangledName += "E"; + if (sampler.isYuv()) + mangledName += "Y"; + switch (sampler.dim) { + case Esd2D: mangledName += "2"; break; + case Esd3D: mangledName += "3"; break; + case EsdCube: mangledName += "C"; break; +#ifndef GLSLANG_WEB + case Esd1D: mangledName += "1"; break; + case EsdRect: mangledName += "R2"; break; + case EsdBuffer: mangledName += "B"; break; + case EsdSubpass: mangledName += "P"; break; +#endif + default: break; // some compilers want this + } + +#ifdef ENABLE_HLSL + if (sampler.hasReturnStruct()) { + // Name mangle for sampler return struct uses struct table index. + mangledName += "-tx-struct"; + + char text[16]; // plenty enough space for the small integers. + snprintf(text, sizeof(text), "%u-", sampler.getStructReturnIndex()); + mangledName += text; + } else { + switch (sampler.getVectorSize()) { + case 1: mangledName += "1"; break; + case 2: mangledName += "2"; break; + case 3: mangledName += "3"; break; + case 4: break; // default to prior name mangle behavior + } + } +#endif + + if (sampler.isMultiSample()) + mangledName += "M"; + break; + case EbtStruct: + case EbtBlock: + if (basicType == EbtStruct) + mangledName += "struct-"; + else + mangledName += "block-"; + if (typeName) + mangledName += *typeName; + for (unsigned int i = 0; i < structure->size(); ++i) { + if ((*structure)[i].type->getBasicType() == EbtVoid) + continue; + mangledName += '-'; + (*structure)[i].type->buildMangledName(mangledName); + } + default: + break; + } + + if (getVectorSize() > 0) + mangledName += static_cast('0' + getVectorSize()); + else { + mangledName += static_cast('0' + getMatrixCols()); + mangledName += static_cast('0' + getMatrixRows()); + } + + if (arraySizes) { + const int maxSize = 11; + char buf[maxSize]; + for (int i = 0; i < arraySizes->getNumDims(); ++i) { + if (arraySizes->getDimNode(i)) { + if (arraySizes->getDimNode(i)->getAsSymbolNode()) + snprintf(buf, maxSize, "s%d", arraySizes->getDimNode(i)->getAsSymbolNode()->getId()); + else + snprintf(buf, maxSize, "s%p", arraySizes->getDimNode(i)); + } else + snprintf(buf, maxSize, "%d", arraySizes->getDimSize(i)); + mangledName += '['; + mangledName += buf; + mangledName += ']'; + } + } +} + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +// +// Dump functions. +// + +void TSymbol::dumpExtensions(TInfoSink& infoSink) const +{ + int numExtensions = getNumExtensions(); + if (numExtensions) { + infoSink.debug << " <"; + + for (int i = 0; i < numExtensions; i++) + infoSink.debug << getExtensions()[i] << ","; + + infoSink.debug << ">"; + } +} + +void TVariable::dump(TInfoSink& infoSink, bool complete) const +{ + if (complete) { + infoSink.debug << getName().c_str() << ": " << type.getCompleteString(); + dumpExtensions(infoSink); + } else { + infoSink.debug << getName().c_str() << ": " << type.getStorageQualifierString() << " " + << type.getBasicTypeString(); + + if (type.isArray()) + infoSink.debug << "[0]"; + } + + infoSink.debug << "\n"; +} + +void TFunction::dump(TInfoSink& infoSink, bool complete) const +{ + if (complete) { + infoSink.debug << getName().c_str() << ": " << returnType.getCompleteString() << " " << getName().c_str() + << "("; + + int numParams = getParamCount(); + for (int i = 0; i < numParams; i++) { + const TParameter ¶m = parameters[i]; + infoSink.debug << param.type->getCompleteString() << " " + << (param.type->isStruct() ? "of " + param.type->getTypeName() + " " : "") + << (param.name ? *param.name : "") << (i < numParams - 1 ? "," : ""); + } + + infoSink.debug << ")"; + dumpExtensions(infoSink); + } else { + infoSink.debug << getName().c_str() << ": " << returnType.getBasicTypeString() << " " + << getMangledName().c_str() << "n"; + } + + infoSink.debug << "\n"; +} + +void TAnonMember::dump(TInfoSink& TInfoSink, bool) const +{ + TInfoSink.debug << "anonymous member " << getMemberNumber() << " of " << getAnonContainer().getName().c_str() + << "\n"; +} + +void TSymbolTableLevel::dump(TInfoSink& infoSink, bool complete) const +{ + tLevel::const_iterator it; + for (it = level.begin(); it != level.end(); ++it) + (*it).second->dump(infoSink, complete); +} + +void TSymbolTable::dump(TInfoSink& infoSink, bool complete) const +{ + for (int level = currentLevel(); level >= 0; --level) { + infoSink.debug << "LEVEL " << level << "\n"; + table[level]->dump(infoSink, complete); + } +} + +#endif + +// +// Functions have buried pointers to delete. +// +TFunction::~TFunction() +{ + for (TParamList::iterator i = parameters.begin(); i != parameters.end(); ++i) + delete (*i).type; +} + +// +// Symbol table levels are a map of pointers to symbols that have to be deleted. +// +TSymbolTableLevel::~TSymbolTableLevel() +{ + for (tLevel::iterator it = level.begin(); it != level.end(); ++it) + delete (*it).second; + + delete [] defaultPrecision; +} + +// +// Change all function entries in the table with the non-mangled name +// to be related to the provided built-in operation. +// +void TSymbolTableLevel::relateToOperator(const char* name, TOperator op) +{ + tLevel::const_iterator candidate = level.lower_bound(name); + while (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) { + TFunction* function = (*candidate).second->getAsFunction(); + function->relateToOperator(op); + } else + break; + ++candidate; + } +} + +// Make all function overloads of the given name require an extension(s). +// Should only be used for a version/profile that actually needs the extension(s). +void TSymbolTableLevel::setFunctionExtensions(const char* name, int num, const char* const extensions[]) +{ + tLevel::const_iterator candidate = level.lower_bound(name); + while (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) { + TSymbol* symbol = candidate->second; + symbol->setExtensions(num, extensions); + } else + break; + ++candidate; + } +} + +// +// Make all symbols in this table level read only. +// +void TSymbolTableLevel::readOnly() +{ + for (tLevel::iterator it = level.begin(); it != level.end(); ++it) + (*it).second->makeReadOnly(); +} + +// +// Copy a symbol, but the copy is writable; call readOnly() afterward if that's not desired. +// +TSymbol::TSymbol(const TSymbol& copyOf) +{ + name = NewPoolTString(copyOf.name->c_str()); + uniqueId = copyOf.uniqueId; + writable = true; +} + +TVariable::TVariable(const TVariable& copyOf) : TSymbol(copyOf) +{ + type.deepCopy(copyOf.type); + userType = copyOf.userType; + + // we don't support specialization-constant subtrees in cloned tables, only extensions + constSubtree = nullptr; + extensions = nullptr; + memberExtensions = nullptr; + if (copyOf.getNumExtensions() > 0) + setExtensions(copyOf.getNumExtensions(), copyOf.getExtensions()); + if (copyOf.hasMemberExtensions()) { + for (int m = 0; m < (int)copyOf.type.getStruct()->size(); ++m) { + if (copyOf.getNumMemberExtensions(m) > 0) + setMemberExtensions(m, copyOf.getNumMemberExtensions(m), copyOf.getMemberExtensions(m)); + } + } + + if (! copyOf.constArray.empty()) { + assert(! copyOf.type.isStruct()); + TConstUnionArray newArray(copyOf.constArray, 0, copyOf.constArray.size()); + constArray = newArray; + } +} + +TVariable* TVariable::clone() const +{ + TVariable *variable = new TVariable(*this); + + return variable; +} + +TFunction::TFunction(const TFunction& copyOf) : TSymbol(copyOf) +{ + for (unsigned int i = 0; i < copyOf.parameters.size(); ++i) { + TParameter param; + parameters.push_back(param); + parameters.back().copyParam(copyOf.parameters[i]); + } + + extensions = nullptr; + if (copyOf.getNumExtensions() > 0) + setExtensions(copyOf.getNumExtensions(), copyOf.getExtensions()); + returnType.deepCopy(copyOf.returnType); + mangledName = copyOf.mangledName; + op = copyOf.op; + defined = copyOf.defined; + prototyped = copyOf.prototyped; + implicitThis = copyOf.implicitThis; + illegalImplicitThis = copyOf.illegalImplicitThis; + defaultParamCount = copyOf.defaultParamCount; +} + +TFunction* TFunction::clone() const +{ + TFunction *function = new TFunction(*this); + + return function; +} + +TAnonMember* TAnonMember::clone() const +{ + // Anonymous members of a given block should be cloned at a higher level, + // where they can all be assured to still end up pointing to a single + // copy of the original container. + assert(0); + + return 0; +} + +TSymbolTableLevel* TSymbolTableLevel::clone() const +{ + TSymbolTableLevel *symTableLevel = new TSymbolTableLevel(); + symTableLevel->anonId = anonId; + symTableLevel->thisLevel = thisLevel; + std::vector containerCopied(anonId, false); + tLevel::const_iterator iter; + for (iter = level.begin(); iter != level.end(); ++iter) { + const TAnonMember* anon = iter->second->getAsAnonMember(); + if (anon) { + // Insert all the anonymous members of this same container at once, + // avoid inserting the remaining members in the future, once this has been done, + // allowing them to all be part of the same new container. + if (! containerCopied[anon->getAnonId()]) { + TVariable* container = anon->getAnonContainer().clone(); + container->changeName(NewPoolTString("")); + // insert the container and all its members + symTableLevel->insert(*container, false); + containerCopied[anon->getAnonId()] = true; + } + } else + symTableLevel->insert(*iter->second->clone(), false); + } + + return symTableLevel; +} + +void TSymbolTable::copyTable(const TSymbolTable& copyOf) +{ + assert(adoptedLevels == copyOf.adoptedLevels); + + uniqueId = copyOf.uniqueId; + noBuiltInRedeclarations = copyOf.noBuiltInRedeclarations; + separateNameSpaces = copyOf.separateNameSpaces; + for (unsigned int i = copyOf.adoptedLevels; i < copyOf.table.size(); ++i) + table.push_back(copyOf.table[i]->clone()); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/SymbolTable.h b/third_party/glslang/glslang/MachineIndependent/SymbolTable.h new file mode 100644 index 0000000..db16c19 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/SymbolTable.h @@ -0,0 +1,899 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _SYMBOL_TABLE_INCLUDED_ +#define _SYMBOL_TABLE_INCLUDED_ + +// +// Symbol table for parsing. Has these design characteristics: +// +// * Same symbol table can be used to compile many shaders, to preserve +// effort of creating and loading with the large numbers of built-in +// symbols. +// +// --> This requires a copy mechanism, so initial pools used to create +// the shared information can be popped. Done through "clone" +// methods. +// +// * Name mangling will be used to give each function a unique name +// so that symbol table lookups are never ambiguous. This allows +// a simpler symbol table structure. +// +// * Pushing and popping of scope, so symbol table will really be a stack +// of symbol tables. Searched from the top, with new inserts going into +// the top. +// +// * Constants: Compile time constant symbols will keep their values +// in the symbol table. The parser can substitute constants at parse +// time, including doing constant folding and constant propagation. +// +// * No temporaries: Temporaries made from operations (+, --, .xy, etc.) +// are tracked in the intermediate representation, not the symbol table. +// + +#include "../Include/Common.h" +#include "../Include/intermediate.h" +#include "../Include/InfoSink.h" + +namespace glslang { + +// +// Symbol base class. (Can build functions or variables out of these...) +// + +class TVariable; +class TFunction; +class TAnonMember; + +typedef TVector TExtensionList; + +class TSymbol { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + explicit TSymbol(const TString *n) : name(n), extensions(0), writable(true) { } + virtual TSymbol* clone() const = 0; + virtual ~TSymbol() { } // rely on all symbol owned memory coming from the pool + + virtual const TString& getName() const { return *name; } + virtual void changeName(const TString* newName) { name = newName; } + virtual void addPrefix(const char* prefix) + { + TString newName(prefix); + newName.append(*name); + changeName(NewPoolTString(newName.c_str())); + } + virtual const TString& getMangledName() const { return getName(); } + virtual TFunction* getAsFunction() { return 0; } + virtual const TFunction* getAsFunction() const { return 0; } + virtual TVariable* getAsVariable() { return 0; } + virtual const TVariable* getAsVariable() const { return 0; } + virtual const TAnonMember* getAsAnonMember() const { return 0; } + virtual const TType& getType() const = 0; + virtual TType& getWritableType() = 0; + virtual void setUniqueId(int id) { uniqueId = id; } + virtual int getUniqueId() const { return uniqueId; } + virtual void setExtensions(int numExts, const char* const exts[]) + { + assert(extensions == 0); + assert(numExts > 0); + extensions = NewPoolObject(extensions); + for (int e = 0; e < numExts; ++e) + extensions->push_back(exts[e]); + } + virtual int getNumExtensions() const { return extensions == nullptr ? 0 : (int)extensions->size(); } + virtual const char** getExtensions() const { return extensions->data(); } + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + virtual void dump(TInfoSink& infoSink, bool complete = false) const = 0; + void dumpExtensions(TInfoSink& infoSink) const; +#endif + + virtual bool isReadOnly() const { return ! writable; } + virtual void makeReadOnly() { writable = false; } + +protected: + explicit TSymbol(const TSymbol&); + TSymbol& operator=(const TSymbol&); + + const TString *name; + unsigned int uniqueId; // For cross-scope comparing during code generation + + // For tracking what extensions must be present + // (don't use if correct version/profile is present). + TExtensionList* extensions; // an array of pointers to existing constant char strings + + // + // N.B.: Non-const functions that will be generally used should assert on this, + // to avoid overwriting shared symbol-table information. + // + bool writable; +}; + +// +// Variable class, meaning a symbol that's not a function. +// +// There could be a separate class hierarchy for Constant variables; +// Only one of int, bool, or float, (or none) is correct for +// any particular use, but it's easy to do this way, and doesn't +// seem worth having separate classes, and "getConst" can't simply return +// different values for different types polymorphically, so this is +// just simple and pragmatic. +// +class TVariable : public TSymbol { +public: + TVariable(const TString *name, const TType& t, bool uT = false ) + : TSymbol(name), + userType(uT), + constSubtree(nullptr), + memberExtensions(nullptr), + anonId(-1) + { type.shallowCopy(t); } + virtual TVariable* clone() const; + virtual ~TVariable() { } + + virtual TVariable* getAsVariable() { return this; } + virtual const TVariable* getAsVariable() const { return this; } + virtual const TType& getType() const { return type; } + virtual TType& getWritableType() { assert(writable); return type; } + virtual bool isUserType() const { return userType; } + virtual const TConstUnionArray& getConstArray() const { return constArray; } + virtual TConstUnionArray& getWritableConstArray() { assert(writable); return constArray; } + virtual void setConstArray(const TConstUnionArray& array) { constArray = array; } + virtual void setConstSubtree(TIntermTyped* subtree) { constSubtree = subtree; } + virtual TIntermTyped* getConstSubtree() const { return constSubtree; } + virtual void setAnonId(int i) { anonId = i; } + virtual int getAnonId() const { return anonId; } + + virtual void setMemberExtensions(int member, int numExts, const char* const exts[]) + { + assert(type.isStruct()); + assert(numExts > 0); + if (memberExtensions == nullptr) { + memberExtensions = NewPoolObject(memberExtensions); + memberExtensions->resize(type.getStruct()->size()); + } + for (int e = 0; e < numExts; ++e) + (*memberExtensions)[member].push_back(exts[e]); + } + virtual bool hasMemberExtensions() const { return memberExtensions != nullptr; } + virtual int getNumMemberExtensions(int member) const + { + return memberExtensions == nullptr ? 0 : (int)(*memberExtensions)[member].size(); + } + virtual const char** getMemberExtensions(int member) const { return (*memberExtensions)[member].data(); } + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + virtual void dump(TInfoSink& infoSink, bool complete = false) const; +#endif + +protected: + explicit TVariable(const TVariable&); + TVariable& operator=(const TVariable&); + + TType type; + bool userType; + + // we are assuming that Pool Allocator will free the memory allocated to unionArray + // when this object is destroyed + + TConstUnionArray constArray; // for compile-time constant value + TIntermTyped* constSubtree; // for specialization constant computation + TVector* memberExtensions; // per-member extension list, allocated only when needed + int anonId; // the ID used for anonymous blocks: TODO: see if uniqueId could serve a dual purpose +}; + +// +// The function sub-class of symbols and the parser will need to +// share this definition of a function parameter. +// +struct TParameter { + TString *name; + TType* type; + TIntermTyped* defaultValue; + void copyParam(const TParameter& param) + { + if (param.name) + name = NewPoolTString(param.name->c_str()); + else + name = 0; + type = param.type->clone(); + defaultValue = param.defaultValue; + } + TBuiltInVariable getDeclaredBuiltIn() const { return type->getQualifier().declaredBuiltIn; } +}; + +// +// The function sub-class of a symbol. +// +class TFunction : public TSymbol { +public: + explicit TFunction(TOperator o) : + TSymbol(0), + op(o), + defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0) { } + TFunction(const TString *name, const TType& retType, TOperator tOp = EOpNull) : + TSymbol(name), + mangledName(*name + '('), + op(tOp), + defined(false), prototyped(false), implicitThis(false), illegalImplicitThis(false), defaultParamCount(0) + { + returnType.shallowCopy(retType); + declaredBuiltIn = retType.getQualifier().builtIn; + } + virtual TFunction* clone() const override; + virtual ~TFunction(); + + virtual TFunction* getAsFunction() override { return this; } + virtual const TFunction* getAsFunction() const override { return this; } + + // Install 'p' as the (non-'this') last parameter. + // Non-'this' parameters are reflected in both the list of parameters and the + // mangled name. + virtual void addParameter(TParameter& p) + { + assert(writable); + parameters.push_back(p); + p.type->appendMangledName(mangledName); + + if (p.defaultValue != nullptr) + defaultParamCount++; + } + + // Install 'this' as the first parameter. + // 'this' is reflected in the list of parameters, but not the mangled name. + virtual void addThisParameter(TType& type, const char* name) + { + TParameter p = { NewPoolTString(name), new TType, nullptr }; + p.type->shallowCopy(type); + parameters.insert(parameters.begin(), p); + } + + virtual void addPrefix(const char* prefix) override + { + TSymbol::addPrefix(prefix); + mangledName.insert(0, prefix); + } + + virtual void removePrefix(const TString& prefix) + { + assert(mangledName.compare(0, prefix.size(), prefix) == 0); + mangledName.erase(0, prefix.size()); + } + + virtual const TString& getMangledName() const override { return mangledName; } + virtual const TType& getType() const override { return returnType; } + virtual TBuiltInVariable getDeclaredBuiltInType() const { return declaredBuiltIn; } + virtual TType& getWritableType() override { return returnType; } + virtual void relateToOperator(TOperator o) { assert(writable); op = o; } + virtual TOperator getBuiltInOp() const { return op; } + virtual void setDefined() { assert(writable); defined = true; } + virtual bool isDefined() const { return defined; } + virtual void setPrototyped() { assert(writable); prototyped = true; } + virtual bool isPrototyped() const { return prototyped; } + virtual void setImplicitThis() { assert(writable); implicitThis = true; } + virtual bool hasImplicitThis() const { return implicitThis; } + virtual void setIllegalImplicitThis() { assert(writable); illegalImplicitThis = true; } + virtual bool hasIllegalImplicitThis() const { return illegalImplicitThis; } + + // Return total number of parameters + virtual int getParamCount() const { return static_cast(parameters.size()); } + // Return number of parameters with default values. + virtual int getDefaultParamCount() const { return defaultParamCount; } + // Return number of fixed parameters (without default values) + virtual int getFixedParamCount() const { return getParamCount() - getDefaultParamCount(); } + + virtual TParameter& operator[](int i) { assert(writable); return parameters[i]; } + virtual const TParameter& operator[](int i) const { return parameters[i]; } + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + virtual void dump(TInfoSink& infoSink, bool complete = false) const override; +#endif + +protected: + explicit TFunction(const TFunction&); + TFunction& operator=(const TFunction&); + + typedef TVector TParamList; + TParamList parameters; + TType returnType; + TBuiltInVariable declaredBuiltIn; + + TString mangledName; + TOperator op; + bool defined; + bool prototyped; + bool implicitThis; // True if this function is allowed to see all members of 'this' + bool illegalImplicitThis; // True if this function is not supposed to have access to dynamic members of 'this', + // even if it finds member variables in the symbol table. + // This is important for a static member function that has member variables in scope, + // but is not allowed to use them, or see hidden symbols instead. + int defaultParamCount; +}; + +// +// Members of anonymous blocks are a kind of TSymbol. They are not hidden in +// the symbol table behind a container; rather they are visible and point to +// their anonymous container. (The anonymous container is found through the +// member, not the other way around.) +// +class TAnonMember : public TSymbol { +public: + TAnonMember(const TString* n, unsigned int m, TVariable& a, int an) : TSymbol(n), anonContainer(a), memberNumber(m), anonId(an) { } + virtual TAnonMember* clone() const override; + virtual ~TAnonMember() { } + + virtual const TAnonMember* getAsAnonMember() const override { return this; } + virtual const TVariable& getAnonContainer() const { return anonContainer; } + virtual unsigned int getMemberNumber() const { return memberNumber; } + + virtual const TType& getType() const override + { + const TTypeList& types = *anonContainer.getType().getStruct(); + return *types[memberNumber].type; + } + + virtual TType& getWritableType() override + { + assert(writable); + const TTypeList& types = *anonContainer.getType().getStruct(); + return *types[memberNumber].type; + } + + virtual void setExtensions(int numExts, const char* const exts[]) override + { + anonContainer.setMemberExtensions(memberNumber, numExts, exts); + } + virtual int getNumExtensions() const override { return anonContainer.getNumMemberExtensions(memberNumber); } + virtual const char** getExtensions() const override { return anonContainer.getMemberExtensions(memberNumber); } + + virtual int getAnonId() const { return anonId; } +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + virtual void dump(TInfoSink& infoSink, bool complete = false) const override; +#endif + +protected: + explicit TAnonMember(const TAnonMember&); + TAnonMember& operator=(const TAnonMember&); + + TVariable& anonContainer; + unsigned int memberNumber; + int anonId; +}; + +class TSymbolTableLevel { +public: + POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator()) + TSymbolTableLevel() : defaultPrecision(0), anonId(0), thisLevel(false) { } + ~TSymbolTableLevel(); + + bool insert(TSymbol& symbol, bool separateNameSpaces) + { + // + // returning true means symbol was added to the table with no semantic errors + // + const TString& name = symbol.getName(); + if (name == "") { + symbol.getAsVariable()->setAnonId(anonId++); + // An empty name means an anonymous container, exposing its members to the external scope. + // Give it a name and insert its members in the symbol table, pointing to the container. + char buf[20]; + snprintf(buf, 20, "%s%d", AnonymousPrefix, symbol.getAsVariable()->getAnonId()); + symbol.changeName(NewPoolTString(buf)); + + return insertAnonymousMembers(symbol, 0); + } else { + // Check for redefinition errors: + // - STL itself will tell us if there is a direct name collision, with name mangling, at this level + // - additionally, check for function-redefining-variable name collisions + const TString& insertName = symbol.getMangledName(); + if (symbol.getAsFunction()) { + // make sure there isn't a variable of this name + if (! separateNameSpaces && level.find(name) != level.end()) + return false; + + // insert, and whatever happens is okay + level.insert(tLevelPair(insertName, &symbol)); + + return true; + } else + return level.insert(tLevelPair(insertName, &symbol)).second; + } + } + + // Add more members to an already inserted aggregate object + bool amend(TSymbol& symbol, int firstNewMember) + { + // See insert() for comments on basic explanation of insert. + // This operates similarly, but more simply. + // Only supporting amend of anonymous blocks so far. + if (IsAnonymous(symbol.getName())) + return insertAnonymousMembers(symbol, firstNewMember); + else + return false; + } + + bool insertAnonymousMembers(TSymbol& symbol, int firstMember) + { + const TTypeList& types = *symbol.getAsVariable()->getType().getStruct(); + for (unsigned int m = firstMember; m < types.size(); ++m) { + TAnonMember* member = new TAnonMember(&types[m].type->getFieldName(), m, *symbol.getAsVariable(), symbol.getAsVariable()->getAnonId()); + if (! level.insert(tLevelPair(member->getMangledName(), member)).second) + return false; + } + + return true; + } + + TSymbol* find(const TString& name) const + { + tLevel::const_iterator it = level.find(name); + if (it == level.end()) + return 0; + else + return (*it).second; + } + + void findFunctionNameList(const TString& name, TVector& list) + { + size_t parenAt = name.find_first_of('('); + TString base(name, 0, parenAt + 1); + + tLevel::const_iterator begin = level.lower_bound(base); + base[parenAt] = ')'; // assume ')' is lexically after '(' + tLevel::const_iterator end = level.upper_bound(base); + for (tLevel::const_iterator it = begin; it != end; ++it) + list.push_back(it->second->getAsFunction()); + } + + // See if there is already a function in the table having the given non-function-style name. + bool hasFunctionName(const TString& name) const + { + tLevel::const_iterator candidate = level.lower_bound(name); + if (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt != candidateName.npos && candidateName.compare(0, parenAt, name) == 0) + + return true; + } + + return false; + } + + // See if there is a variable at this level having the given non-function-style name. + // Return true if name is found, and set variable to true if the name was a variable. + bool findFunctionVariableName(const TString& name, bool& variable) const + { + tLevel::const_iterator candidate = level.lower_bound(name); + if (candidate != level.end()) { + const TString& candidateName = (*candidate).first; + TString::size_type parenAt = candidateName.find_first_of('('); + if (parenAt == candidateName.npos) { + // not a mangled name + if (candidateName == name) { + // found a variable name match + variable = true; + return true; + } + } else { + // a mangled name + if (candidateName.compare(0, parenAt, name) == 0) { + // found a function name match + variable = false; + return true; + } + } + } + + return false; + } + + // Use this to do a lazy 'push' of precision defaults the first time + // a precision statement is seen in a new scope. Leave it at 0 for + // when no push was needed. Thus, it is not the current defaults, + // it is what to restore the defaults to when popping a level. + void setPreviousDefaultPrecisions(const TPrecisionQualifier *p) + { + // can call multiple times at one scope, will only latch on first call, + // as we're tracking the previous scope's values, not the current values + if (defaultPrecision != 0) + return; + + defaultPrecision = new TPrecisionQualifier[EbtNumTypes]; + for (int t = 0; t < EbtNumTypes; ++t) + defaultPrecision[t] = p[t]; + } + + void getPreviousDefaultPrecisions(TPrecisionQualifier *p) + { + // can be called for table level pops that didn't set the + // defaults + if (defaultPrecision == 0 || p == 0) + return; + + for (int t = 0; t < EbtNumTypes; ++t) + p[t] = defaultPrecision[t]; + } + + void relateToOperator(const char* name, TOperator op); + void setFunctionExtensions(const char* name, int num, const char* const extensions[]); +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + void dump(TInfoSink& infoSink, bool complete = false) const; +#endif + TSymbolTableLevel* clone() const; + void readOnly(); + + void setThisLevel() { thisLevel = true; } + bool isThisLevel() const { return thisLevel; } + +protected: + explicit TSymbolTableLevel(TSymbolTableLevel&); + TSymbolTableLevel& operator=(TSymbolTableLevel&); + + typedef std::map, pool_allocator > > tLevel; + typedef const tLevel::value_type tLevelPair; + typedef std::pair tInsertResult; + + tLevel level; // named mappings + TPrecisionQualifier *defaultPrecision; + int anonId; + bool thisLevel; // True if this level of the symbol table is a structure scope containing member function + // that are supposed to see anonymous access to member variables. +}; + +class TSymbolTable { +public: + TSymbolTable() : uniqueId(0), noBuiltInRedeclarations(false), separateNameSpaces(false), adoptedLevels(0) + { + // + // This symbol table cannot be used until push() is called. + // + } + ~TSymbolTable() + { + // this can be called explicitly; safest to code it so it can be called multiple times + + // don't deallocate levels passed in from elsewhere + while (table.size() > adoptedLevels) + pop(0); + } + + void adoptLevels(TSymbolTable& symTable) + { + for (unsigned int level = 0; level < symTable.table.size(); ++level) { + table.push_back(symTable.table[level]); + ++adoptedLevels; + } + uniqueId = symTable.uniqueId; + noBuiltInRedeclarations = symTable.noBuiltInRedeclarations; + separateNameSpaces = symTable.separateNameSpaces; + } + + // + // While level adopting is generic, the methods below enact a the following + // convention for levels: + // 0: common built-ins shared across all stages, all compiles, only one copy for all symbol tables + // 1: per-stage built-ins, shared across all compiles, but a different copy per stage + // 2: built-ins specific to a compile, like resources that are context-dependent, or redeclared built-ins + // 3: user-shader globals + // +protected: + static const int globalLevel = 3; + static bool isSharedLevel(int level) { return level <= 1; } // exclude all per-compile levels + static bool isBuiltInLevel(int level) { return level <= 2; } // exclude user globals + static bool isGlobalLevel(int level) { return level <= globalLevel; } // include user globals +public: + bool isEmpty() { return table.size() == 0; } + bool atBuiltInLevel() { return isBuiltInLevel(currentLevel()); } + bool atGlobalLevel() { return isGlobalLevel(currentLevel()); } + static bool isBuiltInSymbol(int uniqueId) { + int level = uniqueId >> LevelFlagBitOffset; + return isBuiltInLevel(level); + } + void setNoBuiltInRedeclarations() { noBuiltInRedeclarations = true; } + void setSeparateNameSpaces() { separateNameSpaces = true; } + + void push() + { + table.push_back(new TSymbolTableLevel); + updateUniqueIdLevelFlag(); + } + + // Make a new symbol-table level to represent the scope introduced by a structure + // containing member functions, such that the member functions can find anonymous + // references to member variables. + // + // 'thisSymbol' should have a name of "" to trigger anonymous structure-member + // symbol finds. + void pushThis(TSymbol& thisSymbol) + { + assert(thisSymbol.getName().size() == 0); + table.push_back(new TSymbolTableLevel); + updateUniqueIdLevelFlag(); + table.back()->setThisLevel(); + insert(thisSymbol); + } + + void pop(TPrecisionQualifier *p) + { + table[currentLevel()]->getPreviousDefaultPrecisions(p); + delete table.back(); + table.pop_back(); + updateUniqueIdLevelFlag(); + } + + // + // Insert a visible symbol into the symbol table so it can + // be found later by name. + // + // Returns false if the was a name collision. + // + bool insert(TSymbol& symbol) + { + symbol.setUniqueId(++uniqueId); + + // make sure there isn't a function of this variable name + if (! separateNameSpaces && ! symbol.getAsFunction() && table[currentLevel()]->hasFunctionName(symbol.getName())) + return false; + + // check for not overloading or redefining a built-in function + if (noBuiltInRedeclarations) { + if (atGlobalLevel() && currentLevel() > 0) { + if (table[0]->hasFunctionName(symbol.getName())) + return false; + if (currentLevel() > 1 && table[1]->hasFunctionName(symbol.getName())) + return false; + } + } + + return table[currentLevel()]->insert(symbol, separateNameSpaces); + } + + // Add more members to an already inserted aggregate object + bool amend(TSymbol& symbol, int firstNewMember) + { + // See insert() for comments on basic explanation of insert. + // This operates similarly, but more simply. + return table[currentLevel()]->amend(symbol, firstNewMember); + } + + // + // To allocate an internal temporary, which will need to be uniquely + // identified by the consumer of the AST, but never need to + // found by doing a symbol table search by name, hence allowed an + // arbitrary name in the symbol with no worry of collision. + // + void makeInternalVariable(TSymbol& symbol) + { + symbol.setUniqueId(++uniqueId); + } + + // + // Copy a variable or anonymous member's structure from a shared level so that + // it can be added (soon after return) to the symbol table where it can be + // modified without impacting other users of the shared table. + // + TSymbol* copyUpDeferredInsert(TSymbol* shared) + { + if (shared->getAsVariable()) { + TSymbol* copy = shared->clone(); + copy->setUniqueId(shared->getUniqueId()); + return copy; + } else { + const TAnonMember* anon = shared->getAsAnonMember(); + assert(anon); + TVariable* container = anon->getAnonContainer().clone(); + container->changeName(NewPoolTString("")); + container->setUniqueId(anon->getAnonContainer().getUniqueId()); + return container; + } + } + + TSymbol* copyUp(TSymbol* shared) + { + TSymbol* copy = copyUpDeferredInsert(shared); + table[globalLevel]->insert(*copy, separateNameSpaces); + if (shared->getAsVariable()) + return copy; + else { + // return the copy of the anonymous member + return table[globalLevel]->find(shared->getName()); + } + } + + // Normal find of a symbol, that can optionally say whether the symbol was found + // at a built-in level or the current top-scope level. + TSymbol* find(const TString& name, bool* builtIn = 0, bool* currentScope = 0, int* thisDepthP = 0) + { + int level = currentLevel(); + TSymbol* symbol; + int thisDepth = 0; + do { + if (table[level]->isThisLevel()) + ++thisDepth; + symbol = table[level]->find(name); + --level; + } while (symbol == nullptr && level >= 0); + level++; + if (builtIn) + *builtIn = isBuiltInLevel(level); + if (currentScope) + *currentScope = isGlobalLevel(currentLevel()) || level == currentLevel(); // consider shared levels as "current scope" WRT user globals + if (thisDepthP != nullptr) { + if (! table[level]->isThisLevel()) + thisDepth = 0; + *thisDepthP = thisDepth; + } + + return symbol; + } + + // Find of a symbol that returns how many layers deep of nested + // structures-with-member-functions ('this' scopes) deep the symbol was + // found in. + TSymbol* find(const TString& name, int& thisDepth) + { + int level = currentLevel(); + TSymbol* symbol; + thisDepth = 0; + do { + if (table[level]->isThisLevel()) + ++thisDepth; + symbol = table[level]->find(name); + --level; + } while (symbol == 0 && level >= 0); + + if (! table[level + 1]->isThisLevel()) + thisDepth = 0; + + return symbol; + } + + bool isFunctionNameVariable(const TString& name) const + { + if (separateNameSpaces) + return false; + + int level = currentLevel(); + do { + bool variable; + bool found = table[level]->findFunctionVariableName(name, variable); + if (found) + return variable; + --level; + } while (level >= 0); + + return false; + } + + void findFunctionNameList(const TString& name, TVector& list, bool& builtIn) + { + // For user levels, return the set found in the first scope with a match + builtIn = false; + int level = currentLevel(); + do { + table[level]->findFunctionNameList(name, list); + --level; + } while (list.empty() && level >= globalLevel); + + if (! list.empty()) + return; + + // Gather across all built-in levels; they don't hide each other + builtIn = true; + do { + table[level]->findFunctionNameList(name, list); + --level; + } while (level >= 0); + } + + void relateToOperator(const char* name, TOperator op) + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->relateToOperator(name, op); + } + + void setFunctionExtensions(const char* name, int num, const char* const extensions[]) + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->setFunctionExtensions(name, num, extensions); + } + + void setVariableExtensions(const char* name, int numExts, const char* const extensions[]) + { + TSymbol* symbol = find(TString(name)); + if (symbol == nullptr) + return; + + symbol->setExtensions(numExts, extensions); + } + + void setVariableExtensions(const char* blockName, const char* name, int numExts, const char* const extensions[]) + { + TSymbol* symbol = find(TString(blockName)); + if (symbol == nullptr) + return; + TVariable* variable = symbol->getAsVariable(); + assert(variable != nullptr); + + const TTypeList& structure = *variable->getAsVariable()->getType().getStruct(); + for (int member = 0; member < (int)structure.size(); ++member) { + if (structure[member].type->getFieldName().compare(name) == 0) { + variable->setMemberExtensions(member, numExts, extensions); + return; + } + } + } + + int getMaxSymbolId() { return uniqueId; } +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + void dump(TInfoSink& infoSink, bool complete = false) const; +#endif + void copyTable(const TSymbolTable& copyOf); + + void setPreviousDefaultPrecisions(TPrecisionQualifier *p) { table[currentLevel()]->setPreviousDefaultPrecisions(p); } + + void readOnly() + { + for (unsigned int level = 0; level < table.size(); ++level) + table[level]->readOnly(); + } + + // Add current level in the high-bits of unique id + void updateUniqueIdLevelFlag() { + // clamp level to avoid overflow + uint32_t level = currentLevel() > 7 ? 7 : currentLevel(); + uniqueId &= ((1 << LevelFlagBitOffset) - 1); + uniqueId |= (level << LevelFlagBitOffset); + } + +protected: + TSymbolTable(TSymbolTable&); + TSymbolTable& operator=(TSymbolTableLevel&); + + int currentLevel() const { return static_cast(table.size()) - 1; } + static const uint32_t LevelFlagBitOffset = 28; + std::vector table; + int uniqueId; // for unique identification in code generation + bool noBuiltInRedeclarations; + bool separateNameSpaces; + unsigned int adoptedLevels; +}; + +} // end namespace glslang + +#endif // _SYMBOL_TABLE_INCLUDED_ diff --git a/third_party/glslang/glslang/MachineIndependent/Versions.cpp b/third_party/glslang/glslang/MachineIndependent/Versions.cpp new file mode 100644 index 0000000..69b8863 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/Versions.cpp @@ -0,0 +1,1296 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2020 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Help manage multiple profiles, versions, extensions etc. +// +// These don't return error codes, as the presumption is parsing will +// always continue as if the tested feature were enabled, and thus there +// is no error recovery needed. +// + +// +// HOW TO add a feature enabled by an extension. +// +// To add a new hypothetical "Feature F" to the front end, where an extension +// "XXX_extension_X" can be used to enable the feature, do the following. +// +// OVERVIEW: Specific features are what are error-checked for, not +// extensions: A specific Feature F might be enabled by an extension, or a +// particular version in a particular profile, or a stage, or combinations, etc. +// +// The basic mechanism is to use the following to "declare" all the things that +// enable/disable Feature F, in a code path that implements Feature F: +// +// requireProfile() +// profileRequires() +// requireStage() +// checkDeprecated() +// requireNotRemoved() +// requireExtensions() +// extensionRequires() +// +// Typically, only the first two calls are needed. They go into a code path that +// implements Feature F, and will log the proper error/warning messages. Parsing +// will then always continue as if the tested feature was enabled. +// +// There is typically no if-testing or conditional parsing, just insertion of the calls above. +// However, if symbols specific to the extension are added (step 5), they will +// only be added under tests that the minimum version and profile are present. +// +// 1) Add a symbol name for the extension string at the bottom of Versions.h: +// +// const char* const XXX_extension_X = "XXX_extension_X"; +// +// 2) Add extension initialization to TParseVersions::initializeExtensionBehavior(), +// the first function below and optionally a entry to extensionData for additional +// error checks: +// +// extensionBehavior[XXX_extension_X] = EBhDisable; +// (Optional) exts[] = {XXX_extension_X, EShTargetSpv_1_4} +// +// 3) Add any preprocessor directives etc. in the next function, TParseVersions::getPreamble(): +// +// "#define XXX_extension_X 1\n" +// +// The new-line is important, as that ends preprocess tokens. +// +// 4) Insert a profile check in the feature's path (unless all profiles support the feature, +// for some version level). That is, call requireProfile() to constrain the profiles, e.g.: +// +// // ... in a path specific to Feature F... +// requireProfile(loc, +// ECoreProfile | ECompatibilityProfile, +// "Feature F"); +// +// 5) For each profile that supports the feature, insert version/extension checks: +// +// The mostly likely scenario is that Feature F can only be used with a +// particular profile if XXX_extension_X is present or the version is +// high enough that the core specification already incorporated it. +// +// // following the requireProfile() call... +// profileRequires(loc, +// ECoreProfile | ECompatibilityProfile, +// 420, // 0 if no version incorporated the feature into the core spec. +// XXX_extension_X, // can be a list of extensions that all add the feature +// "Feature F Description"); +// +// This allows the feature if either A) one of the extensions is enabled or +// B) the version is high enough. If no version yet incorporates the feature +// into core, pass in 0. +// +// This can be called multiple times, if different profiles support the +// feature starting at different version numbers or with different +// extensions. +// +// This must be called for each profile allowed by the initial call to requireProfile(). +// +// Profiles are all masks, which can be "or"-ed together. +// +// ENoProfile +// ECoreProfile +// ECompatibilityProfile +// EEsProfile +// +// The ENoProfile profile is only for desktop, before profiles showed up in version 150; +// All other #version with no profile default to either es or core, and so have profiles. +// +// You can select all but a particular profile using ~. The following basically means "desktop": +// +// ~EEsProfile +// +// 6) If built-in symbols are added by the extension, add them in Initialize.cpp: Their use +// will be automatically error checked against the extensions enabled at that moment. +// see the comment at the top of Initialize.cpp for where to put them. Establish them at +// the earliest release that supports the extension. Then, tag them with the +// set of extensions that both enable them and are necessary, given the version of the symbol +// table. (There is a different symbol table for each version.) +// +// 7) If the extension has additional requirements like minimum SPIR-V version required, add them +// to extensionRequires() + +#include "parseVersions.h" +#include "localintermediate.h" + +namespace glslang { + +#ifndef GLSLANG_WEB + +// +// Initialize all extensions, almost always to 'disable', as once their features +// are incorporated into a core version, their features are supported through allowing that +// core version, not through a pseudo-enablement of the extension. +// +void TParseVersions::initializeExtensionBehavior() +{ + typedef struct { + const char *const extensionName; + EShTargetLanguageVersion minSpvVersion; + } extensionData; + + const extensionData exts[] = { {E_GL_EXT_ray_tracing, EShTargetSpv_1_4} }; + + for (size_t ii = 0; ii < sizeof(exts) / sizeof(exts[0]); ii++) { + // Add only extensions which require > spv1.0 to save space in map + if (exts[ii].minSpvVersion > EShTargetSpv_1_0) { + extensionMinSpv[E_GL_EXT_ray_tracing] = exts[ii].minSpvVersion; + } + } + + extensionBehavior[E_GL_OES_texture_3D] = EBhDisable; + extensionBehavior[E_GL_OES_standard_derivatives] = EBhDisable; + extensionBehavior[E_GL_EXT_frag_depth] = EBhDisable; + extensionBehavior[E_GL_OES_EGL_image_external] = EBhDisable; + extensionBehavior[E_GL_OES_EGL_image_external_essl3] = EBhDisable; + extensionBehavior[E_GL_EXT_YUV_target] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_texture_lod] = EBhDisable; + extensionBehavior[E_GL_EXT_shadow_samplers] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_rectangle] = EBhDisable; + extensionBehavior[E_GL_3DL_array_objects] = EBhDisable; + extensionBehavior[E_GL_ARB_shading_language_420pack] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_gather] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader5] = EBhDisablePartial; + extensionBehavior[E_GL_ARB_separate_shader_objects] = EBhDisable; + extensionBehavior[E_GL_ARB_compute_shader] = EBhDisable; + extensionBehavior[E_GL_ARB_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_ARB_enhanced_layouts] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_cube_map_array] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_multisample] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_texture_lod] = EBhDisable; + extensionBehavior[E_GL_ARB_explicit_attrib_location] = EBhDisable; + extensionBehavior[E_GL_ARB_explicit_uniform_location] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_image_load_store] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_atomic_counters] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_draw_parameters] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_group_vote] = EBhDisable; + extensionBehavior[E_GL_ARB_derivative_control] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_texture_image_samples] = EBhDisable; + extensionBehavior[E_GL_ARB_viewport_array] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader_int64] = EBhDisable; + extensionBehavior[E_GL_ARB_gpu_shader_fp64] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_ballot] = EBhDisable; + extensionBehavior[E_GL_ARB_sparse_texture2] = EBhDisable; + extensionBehavior[E_GL_ARB_sparse_texture_clamp] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_stencil_export] = EBhDisable; +// extensionBehavior[E_GL_ARB_cull_distance] = EBhDisable; // present for 4.5, but need extension control over block members + extensionBehavior[E_GL_ARB_post_depth_coverage] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_viewport_layer_array] = EBhDisable; + extensionBehavior[E_GL_ARB_fragment_shader_interlock] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_clock] = EBhDisable; + extensionBehavior[E_GL_ARB_uniform_buffer_object] = EBhDisable; + extensionBehavior[E_GL_ARB_sample_shading] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_bit_encoding] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_image_size] = EBhDisable; + extensionBehavior[E_GL_ARB_shader_storage_buffer_object] = EBhDisable; + extensionBehavior[E_GL_ARB_shading_language_packing] = EBhDisable; + extensionBehavior[E_GL_ARB_texture_query_lod] = EBhDisable; + extensionBehavior[E_GL_ARB_vertex_attrib_64bit] = EBhDisable; + + extensionBehavior[E_GL_KHR_shader_subgroup_basic] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_vote] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_arithmetic] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_ballot] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_shuffle] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_shuffle_relative] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_clustered] = EBhDisable; + extensionBehavior[E_GL_KHR_shader_subgroup_quad] = EBhDisable; + extensionBehavior[E_GL_KHR_memory_scope_semantics] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_atomic_int64] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_non_constant_global_initializers] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_image_load_formatted] = EBhDisable; + extensionBehavior[E_GL_EXT_post_depth_coverage] = EBhDisable; + extensionBehavior[E_GL_EXT_control_flow_attributes] = EBhDisable; + extensionBehavior[E_GL_EXT_nonuniform_qualifier] = EBhDisable; + extensionBehavior[E_GL_EXT_samplerless_texture_functions] = EBhDisable; + extensionBehavior[E_GL_EXT_scalar_block_layout] = EBhDisable; + extensionBehavior[E_GL_EXT_fragment_invocation_density] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference2] = EBhDisable; + extensionBehavior[E_GL_EXT_buffer_reference_uvec2] = EBhDisable; + extensionBehavior[E_GL_EXT_demote_to_helper_invocation] = EBhDisable; + extensionBehavior[E_GL_EXT_debug_printf] = EBhDisable; + + extensionBehavior[E_GL_EXT_shader_16bit_storage] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_8bit_storage] = EBhDisable; + + // #line and #include + extensionBehavior[E_GL_GOOGLE_cpp_style_line_directive] = EBhDisable; + extensionBehavior[E_GL_GOOGLE_include_directive] = EBhDisable; + + extensionBehavior[E_GL_AMD_shader_ballot] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_trinary_minmax] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_explicit_vertex_parameter] = EBhDisable; + extensionBehavior[E_GL_AMD_gcn_shader] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_half_float] = EBhDisable; + extensionBehavior[E_GL_AMD_texture_gather_bias_lod] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_int16] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_image_load_store_lod] = EBhDisable; + extensionBehavior[E_GL_AMD_shader_fragment_mask] = EBhDisable; + extensionBehavior[E_GL_AMD_gpu_shader_half_float_fetch] = EBhDisable; + + extensionBehavior[E_GL_INTEL_shader_integer_functions2] = EBhDisable; + + extensionBehavior[E_GL_NV_sample_mask_override_coverage] = EBhDisable; + extensionBehavior[E_SPV_NV_geometry_shader_passthrough] = EBhDisable; + extensionBehavior[E_GL_NV_viewport_array2] = EBhDisable; + extensionBehavior[E_GL_NV_stereo_view_rendering] = EBhDisable; + extensionBehavior[E_GL_NVX_multiview_per_view_attributes] = EBhDisable; + extensionBehavior[E_GL_NV_shader_atomic_int64] = EBhDisable; + extensionBehavior[E_GL_NV_conservative_raster_underestimation] = EBhDisable; + extensionBehavior[E_GL_NV_shader_noperspective_interpolation] = EBhDisable; + extensionBehavior[E_GL_NV_shader_subgroup_partitioned] = EBhDisable; + extensionBehavior[E_GL_NV_shading_rate_image] = EBhDisable; + extensionBehavior[E_GL_NV_ray_tracing] = EBhDisable; + extensionBehavior[E_GL_NV_fragment_shader_barycentric] = EBhDisable; + extensionBehavior[E_GL_NV_compute_shader_derivatives] = EBhDisable; + extensionBehavior[E_GL_NV_shader_texture_footprint] = EBhDisable; + extensionBehavior[E_GL_NV_mesh_shader] = EBhDisable; + + extensionBehavior[E_GL_NV_cooperative_matrix] = EBhDisable; + extensionBehavior[E_GL_NV_shader_sm_builtins] = EBhDisable; + extensionBehavior[E_GL_NV_integer_cooperative_matrix] = EBhDisable; + + // AEP + extensionBehavior[E_GL_ANDROID_extension_pack_es31a] = EBhDisable; + extensionBehavior[E_GL_KHR_blend_equation_advanced] = EBhDisable; + extensionBehavior[E_GL_OES_sample_variables] = EBhDisable; + extensionBehavior[E_GL_OES_shader_image_atomic] = EBhDisable; + extensionBehavior[E_GL_OES_shader_multisample_interpolation] = EBhDisable; + extensionBehavior[E_GL_OES_texture_storage_multisample_2d_array] = EBhDisable; + extensionBehavior[E_GL_EXT_geometry_shader] = EBhDisable; + extensionBehavior[E_GL_EXT_geometry_point_size] = EBhDisable; + extensionBehavior[E_GL_EXT_gpu_shader5] = EBhDisable; + extensionBehavior[E_GL_EXT_primitive_bounding_box] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_io_blocks] = EBhDisable; + extensionBehavior[E_GL_EXT_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_EXT_tessellation_point_size] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_buffer] = EBhDisable; + extensionBehavior[E_GL_EXT_texture_cube_map_array] = EBhDisable; + + // OES matching AEP + extensionBehavior[E_GL_OES_geometry_shader] = EBhDisable; + extensionBehavior[E_GL_OES_geometry_point_size] = EBhDisable; + extensionBehavior[E_GL_OES_gpu_shader5] = EBhDisable; + extensionBehavior[E_GL_OES_primitive_bounding_box] = EBhDisable; + extensionBehavior[E_GL_OES_shader_io_blocks] = EBhDisable; + extensionBehavior[E_GL_OES_tessellation_shader] = EBhDisable; + extensionBehavior[E_GL_OES_tessellation_point_size] = EBhDisable; + extensionBehavior[E_GL_OES_texture_buffer] = EBhDisable; + extensionBehavior[E_GL_OES_texture_cube_map_array] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_integer_mix] = EBhDisable; + + // EXT extensions + extensionBehavior[E_GL_EXT_device_group] = EBhDisable; + extensionBehavior[E_GL_EXT_multiview] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_realtime_clock] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_tracing] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_query] = EBhDisable; + extensionBehavior[E_GL_EXT_ray_flags_primitive_culling] = EBhDisable; + extensionBehavior[E_GL_EXT_blend_func_extended] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_implicit_conversions] = EBhDisable; + extensionBehavior[E_GL_EXT_fragment_shading_rate] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_image_int64] = EBhDisable; + extensionBehavior[E_GL_EXT_terminate_invocation] = EBhDisable; + + // OVR extensions + extensionBehavior[E_GL_OVR_multiview] = EBhDisable; + extensionBehavior[E_GL_OVR_multiview2] = EBhDisable; + + // explicit types + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int8] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int32] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_int64] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float32] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_explicit_arithmetic_types_float64] = EBhDisable; + + // subgroup extended types + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int8] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_int64] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_subgroup_extended_types_float16] = EBhDisable; + extensionBehavior[E_GL_EXT_shader_atomic_float] = EBhDisable; +} + +#endif // GLSLANG_WEB + +// Get code that is not part of a shared symbol table, is specific to this shader, +// or needed by the preprocessor (which does not use a shared symbol table). +void TParseVersions::getPreamble(std::string& preamble) +{ + if (isEsProfile()) { + preamble = + "#define GL_ES 1\n" + "#define GL_FRAGMENT_PRECISION_HIGH 1\n" +#ifdef GLSLANG_WEB + ; +#else + "#define GL_OES_texture_3D 1\n" + "#define GL_OES_standard_derivatives 1\n" + "#define GL_EXT_frag_depth 1\n" + "#define GL_OES_EGL_image_external 1\n" + "#define GL_OES_EGL_image_external_essl3 1\n" + "#define GL_EXT_YUV_target 1\n" + "#define GL_EXT_shader_texture_lod 1\n" + "#define GL_EXT_shadow_samplers 1\n" + "#define GL_EXT_fragment_shading_rate 1\n" + + // AEP + "#define GL_ANDROID_extension_pack_es31a 1\n" + "#define GL_OES_sample_variables 1\n" + "#define GL_OES_shader_image_atomic 1\n" + "#define GL_OES_shader_multisample_interpolation 1\n" + "#define GL_OES_texture_storage_multisample_2d_array 1\n" + "#define GL_EXT_geometry_shader 1\n" + "#define GL_EXT_geometry_point_size 1\n" + "#define GL_EXT_gpu_shader5 1\n" + "#define GL_EXT_primitive_bounding_box 1\n" + "#define GL_EXT_shader_io_blocks 1\n" + "#define GL_EXT_tessellation_shader 1\n" + "#define GL_EXT_tessellation_point_size 1\n" + "#define GL_EXT_texture_buffer 1\n" + "#define GL_EXT_texture_cube_map_array 1\n" + "#define GL_EXT_shader_implicit_conversions 1\n" + "#define GL_EXT_shader_integer_mix 1\n" + "#define GL_EXT_blend_func_extended 1\n" + + // OES matching AEP + "#define GL_OES_geometry_shader 1\n" + "#define GL_OES_geometry_point_size 1\n" + "#define GL_OES_gpu_shader5 1\n" + "#define GL_OES_primitive_bounding_box 1\n" + "#define GL_OES_shader_io_blocks 1\n" + "#define GL_OES_tessellation_shader 1\n" + "#define GL_OES_tessellation_point_size 1\n" + "#define GL_OES_texture_buffer 1\n" + "#define GL_OES_texture_cube_map_array 1\n" + "#define GL_EXT_shader_non_constant_global_initializers 1\n" + ; + + if (isEsProfile() && version >= 300) { + preamble += "#define GL_NV_shader_noperspective_interpolation 1\n"; + } + + } else { // !isEsProfile() + preamble = + "#define GL_FRAGMENT_PRECISION_HIGH 1\n" + "#define GL_ARB_texture_rectangle 1\n" + "#define GL_ARB_shading_language_420pack 1\n" + "#define GL_ARB_texture_gather 1\n" + "#define GL_ARB_gpu_shader5 1\n" + "#define GL_ARB_separate_shader_objects 1\n" + "#define GL_ARB_compute_shader 1\n" + "#define GL_ARB_tessellation_shader 1\n" + "#define GL_ARB_enhanced_layouts 1\n" + "#define GL_ARB_texture_cube_map_array 1\n" + "#define GL_ARB_texture_multisample 1\n" + "#define GL_ARB_shader_texture_lod 1\n" + "#define GL_ARB_explicit_attrib_location 1\n" + "#define GL_ARB_explicit_uniform_location 1\n" + "#define GL_ARB_shader_image_load_store 1\n" + "#define GL_ARB_shader_atomic_counters 1\n" + "#define GL_ARB_shader_draw_parameters 1\n" + "#define GL_ARB_shader_group_vote 1\n" + "#define GL_ARB_derivative_control 1\n" + "#define GL_ARB_shader_texture_image_samples 1\n" + "#define GL_ARB_viewport_array 1\n" + "#define GL_ARB_gpu_shader_int64 1\n" + "#define GL_ARB_gpu_shader_fp64 1\n" + "#define GL_ARB_shader_ballot 1\n" + "#define GL_ARB_sparse_texture2 1\n" + "#define GL_ARB_sparse_texture_clamp 1\n" + "#define GL_ARB_shader_stencil_export 1\n" + "#define GL_ARB_sample_shading 1\n" + "#define GL_ARB_shader_image_size 1\n" + "#define GL_ARB_shading_language_packing 1\n" +// "#define GL_ARB_cull_distance 1\n" // present for 4.5, but need extension control over block members + "#define GL_ARB_post_depth_coverage 1\n" + "#define GL_ARB_fragment_shader_interlock 1\n" + "#define GL_ARB_uniform_buffer_object 1\n" + "#define GL_ARB_shader_bit_encoding 1\n" + "#define GL_ARB_shader_storage_buffer_object 1\n" + "#define GL_ARB_texture_query_lod 1\n" + "#define GL_ARB_vertex_attrib_64bit 1\n" + "#define GL_EXT_shader_non_constant_global_initializers 1\n" + "#define GL_EXT_shader_image_load_formatted 1\n" + "#define GL_EXT_post_depth_coverage 1\n" + "#define GL_EXT_control_flow_attributes 1\n" + "#define GL_EXT_nonuniform_qualifier 1\n" + "#define GL_EXT_shader_16bit_storage 1\n" + "#define GL_EXT_shader_8bit_storage 1\n" + "#define GL_EXT_samplerless_texture_functions 1\n" + "#define GL_EXT_scalar_block_layout 1\n" + "#define GL_EXT_fragment_invocation_density 1\n" + "#define GL_EXT_buffer_reference 1\n" + "#define GL_EXT_buffer_reference2 1\n" + "#define GL_EXT_buffer_reference_uvec2 1\n" + "#define GL_EXT_demote_to_helper_invocation 1\n" + "#define GL_EXT_debug_printf 1\n" + "#define GL_EXT_fragment_shading_rate 1\n" + + // GL_KHR_shader_subgroup + "#define GL_KHR_shader_subgroup_basic 1\n" + "#define GL_KHR_shader_subgroup_vote 1\n" + "#define GL_KHR_shader_subgroup_arithmetic 1\n" + "#define GL_KHR_shader_subgroup_ballot 1\n" + "#define GL_KHR_shader_subgroup_shuffle 1\n" + "#define GL_KHR_shader_subgroup_shuffle_relative 1\n" + "#define GL_KHR_shader_subgroup_clustered 1\n" + "#define GL_KHR_shader_subgroup_quad 1\n" + + "#define GL_EXT_shader_image_int64 1\n" + "#define GL_EXT_shader_atomic_int64 1\n" + "#define GL_EXT_shader_realtime_clock 1\n" + "#define GL_EXT_ray_tracing 1\n" + "#define GL_EXT_ray_query 1\n" + "#define GL_EXT_ray_flags_primitive_culling 1\n" + + "#define GL_AMD_shader_ballot 1\n" + "#define GL_AMD_shader_trinary_minmax 1\n" + "#define GL_AMD_shader_explicit_vertex_parameter 1\n" + "#define GL_AMD_gcn_shader 1\n" + "#define GL_AMD_gpu_shader_half_float 1\n" + "#define GL_AMD_texture_gather_bias_lod 1\n" + "#define GL_AMD_gpu_shader_int16 1\n" + "#define GL_AMD_shader_image_load_store_lod 1\n" + "#define GL_AMD_shader_fragment_mask 1\n" + "#define GL_AMD_gpu_shader_half_float_fetch 1\n" + + "#define GL_INTEL_shader_integer_functions2 1\n" + + "#define GL_NV_sample_mask_override_coverage 1\n" + "#define GL_NV_geometry_shader_passthrough 1\n" + "#define GL_NV_viewport_array2 1\n" + "#define GL_NV_shader_atomic_int64 1\n" + "#define GL_NV_conservative_raster_underestimation 1\n" + "#define GL_NV_shader_subgroup_partitioned 1\n" + "#define GL_NV_shading_rate_image 1\n" + "#define GL_NV_ray_tracing 1\n" + "#define GL_NV_fragment_shader_barycentric 1\n" + "#define GL_NV_compute_shader_derivatives 1\n" + "#define GL_NV_shader_texture_footprint 1\n" + "#define GL_NV_mesh_shader 1\n" + "#define GL_NV_cooperative_matrix 1\n" + "#define GL_NV_integer_cooperative_matrix 1\n" + + "#define GL_EXT_shader_explicit_arithmetic_types 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int8 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int16 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int32 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_int64 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float16 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float32 1\n" + "#define GL_EXT_shader_explicit_arithmetic_types_float64 1\n" + + "#define GL_EXT_shader_subgroup_extended_types_int8 1\n" + "#define GL_EXT_shader_subgroup_extended_types_int16 1\n" + "#define GL_EXT_shader_subgroup_extended_types_int64 1\n" + "#define GL_EXT_shader_subgroup_extended_types_float16 1\n" + + "#define GL_EXT_shader_atomic_float 1\n" + ; + + if (version >= 150) { + // define GL_core_profile and GL_compatibility_profile + preamble += "#define GL_core_profile 1\n"; + + if (profile == ECompatibilityProfile) + preamble += "#define GL_compatibility_profile 1\n"; + } +#endif // GLSLANG_WEB + } + +#ifndef GLSLANG_WEB + if ((!isEsProfile() && version >= 140) || + (isEsProfile() && version >= 310)) { + preamble += + "#define GL_EXT_device_group 1\n" + "#define GL_EXT_multiview 1\n" + "#define GL_NV_shader_sm_builtins 1\n" + ; + } + + if (version >= 300 /* both ES and non-ES */) { + preamble += + "#define GL_OVR_multiview 1\n" + "#define GL_OVR_multiview2 1\n" + ; + } + + // #line and #include + preamble += + "#define GL_GOOGLE_cpp_style_line_directive 1\n" + "#define GL_GOOGLE_include_directive 1\n" + "#define GL_KHR_blend_equation_advanced 1\n" + ; + + // other general extensions + preamble += + "#define GL_EXT_terminate_invocation 1\n" + ; +#endif + + // #define VULKAN XXXX + const int numberBufSize = 12; + char numberBuf[numberBufSize]; + if (spvVersion.vulkanGlsl > 0) { + preamble += "#define VULKAN "; + snprintf(numberBuf, numberBufSize, "%d", spvVersion.vulkanGlsl); + preamble += numberBuf; + preamble += "\n"; + } + +#ifndef GLSLANG_WEB + // #define GL_SPIRV XXXX + if (spvVersion.openGl > 0) { + preamble += "#define GL_SPIRV "; + snprintf(numberBuf, numberBufSize, "%d", spvVersion.openGl); + preamble += numberBuf; + preamble += "\n"; + } +#endif +} + +// +// Map from stage enum to externally readable text name. +// +const char* StageName(EShLanguage stage) +{ + switch(stage) { + case EShLangVertex: return "vertex"; + case EShLangFragment: return "fragment"; + case EShLangCompute: return "compute"; +#ifndef GLSLANG_WEB + case EShLangTessControl: return "tessellation control"; + case EShLangTessEvaluation: return "tessellation evaluation"; + case EShLangGeometry: return "geometry"; + case EShLangRayGen: return "ray-generation"; + case EShLangIntersect: return "intersection"; + case EShLangAnyHit: return "any-hit"; + case EShLangClosestHit: return "closest-hit"; + case EShLangMiss: return "miss"; + case EShLangCallable: return "callable"; + case EShLangMeshNV: return "mesh"; + case EShLangTaskNV: return "task"; +#endif + default: return "unknown stage"; + } +} + +// +// When to use requireStage() +// +// If only some stages support a feature. +// +// Operation: If the current stage is not present, give an error message. +// +void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguageMask languageMask, const char* featureDesc) +{ + if (((1 << language) & languageMask) == 0) + error(loc, "not supported in this stage:", featureDesc, StageName(language)); +} + +// If only one stage supports a feature, this can be called. But, all supporting stages +// must be specified with one call. +void TParseVersions::requireStage(const TSourceLoc& loc, EShLanguage stage, const char* featureDesc) +{ + requireStage(loc, static_cast(1 << stage), featureDesc); +} + +#ifndef GLSLANG_WEB +// +// When to use requireProfile(): +// +// Use if only some profiles support a feature. However, if within a profile the feature +// is version or extension specific, follow this call with calls to profileRequires(). +// +// Operation: If the current profile is not one of the profileMask, +// give an error message. +// +void TParseVersions::requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc) +{ + if (! (profile & profileMask)) + error(loc, "not supported with this profile:", featureDesc, ProfileName(profile)); +} + +// +// When to use profileRequires(): +// +// If a set of profiles have the same requirements for what version or extensions +// are needed to support a feature. +// +// It must be called for each profile that needs protection. Use requireProfile() first +// to reduce that set of profiles. +// +// Operation: Will issue warnings/errors based on the current profile, version, and extension +// behaviors. It only checks extensions when the current profile is one of the profileMask. +// +// A minVersion of 0 means no version of the profileMask support this in core, +// the extension must be present. +// + +// entry point that takes multiple extensions +void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, + const char* const extensions[], const char* featureDesc) +{ + if (profile & profileMask) { + bool okay = minVersion > 0 && version >= minVersion; +#ifndef GLSLANG_WEB + for (int i = 0; i < numExtensions; ++i) { + switch (getExtensionBehavior(extensions[i])) { + case EBhWarn: + infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc); + // fall through + case EBhRequire: + case EBhEnable: + okay = true; + break; + default: break; // some compilers want this + } + } +#endif + if (! okay) + error(loc, "not supported for this version or the enabled extensions", featureDesc, ""); + } +} + +// entry point for the above that takes a single extension +void TParseVersions::profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, + const char* featureDesc) +{ + profileRequires(loc, profileMask, minVersion, extension ? 1 : 0, &extension, featureDesc); +} + +void TParseVersions::unimplemented(const TSourceLoc& loc, const char* featureDesc) +{ + error(loc, "feature not yet implemented", featureDesc, ""); +} + +// +// Within a set of profiles, see if a feature is deprecated and give an error or warning based on whether +// a future compatibility context is being use. +// +void TParseVersions::checkDeprecated(const TSourceLoc& loc, int profileMask, int depVersion, const char* featureDesc) +{ + if (profile & profileMask) { + if (version >= depVersion) { + if (forwardCompatible) + error(loc, "deprecated, may be removed in future release", featureDesc, ""); + else if (! suppressWarnings()) + infoSink.info.message(EPrefixWarning, (TString(featureDesc) + " deprecated in version " + + String(depVersion) + "; may be removed in future release").c_str(), loc); + } + } +} + +// +// Within a set of profiles, see if a feature has now been removed and if so, give an error. +// The version argument is the first version no longer having the feature. +// +void TParseVersions::requireNotRemoved(const TSourceLoc& loc, int profileMask, int removedVersion, const char* featureDesc) +{ + if (profile & profileMask) { + if (version >= removedVersion) { + const int maxSize = 60; + char buf[maxSize]; + snprintf(buf, maxSize, "%s profile; removed in version %d", ProfileName(profile), removedVersion); + error(loc, "no longer supported in", featureDesc, buf); + } + } +} + +// Returns true if at least one of the extensions in the extensions parameter is requested. Otherwise, returns false. +// Warns appropriately if the requested behavior of an extension is "warn". +bool TParseVersions::checkExtensionsRequested(const TSourceLoc& loc, int numExtensions, const char* const extensions[], const char* featureDesc) +{ + // First, see if any of the extensions are enabled + for (int i = 0; i < numExtensions; ++i) { + TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhEnable || behavior == EBhRequire) + return true; + } + + // See if any extensions want to give a warning on use; give warnings for all such extensions + bool warned = false; + for (int i = 0; i < numExtensions; ++i) { + TExtensionBehavior behavior = getExtensionBehavior(extensions[i]); + if (behavior == EBhDisable && relaxedErrors()) { + infoSink.info.message(EPrefixWarning, "The following extension must be enabled to use this feature:", loc); + behavior = EBhWarn; + } + if (behavior == EBhWarn) { + infoSink.info.message(EPrefixWarning, ("extension " + TString(extensions[i]) + " is being used for " + featureDesc).c_str(), loc); + warned = true; + } + } + if (warned) + return true; + return false; +} + +// +// Use when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseVersions::requireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], + const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) + return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + error(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + error(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + +// +// Use by preprocessor when there are no profile/version to check, it's just an error if one of the +// extensions is not present. +// +void TParseVersions::ppRequireExtensions(const TSourceLoc& loc, int numExtensions, const char* const extensions[], + const char* featureDesc) +{ + if (checkExtensionsRequested(loc, numExtensions, extensions, featureDesc)) + return; + + // If we get this far, give errors explaining what extensions are needed + if (numExtensions == 1) + ppError(loc, "required extension not requested:", featureDesc, extensions[0]); + else { + ppError(loc, "required extension not requested:", featureDesc, "Possible extensions include:"); + for (int i = 0; i < numExtensions; ++i) + infoSink.info.message(EPrefixNone, extensions[i]); + } +} + +TExtensionBehavior TParseVersions::getExtensionBehavior(const char* extension) +{ + auto iter = extensionBehavior.find(TString(extension)); + if (iter == extensionBehavior.end()) + return EBhMissing; + else + return iter->second; +} + +// Returns true if the given extension is set to enable, require, or warn. +bool TParseVersions::extensionTurnedOn(const char* const extension) +{ + switch (getExtensionBehavior(extension)) { + case EBhEnable: + case EBhRequire: + case EBhWarn: + return true; + default: + break; + } + return false; +} +// See if any of the extensions are set to enable, require, or warn. +bool TParseVersions::extensionsTurnedOn(int numExtensions, const char* const extensions[]) +{ + for (int i = 0; i < numExtensions; ++i) { + if (extensionTurnedOn(extensions[i])) + return true; + } + return false; +} + +// +// Change the current state of an extension's behavior. +// +void TParseVersions::updateExtensionBehavior(int line, const char* extension, const char* behaviorString) +{ + // Translate from text string of extension's behavior to an enum. + TExtensionBehavior behavior = EBhDisable; + if (! strcmp("require", behaviorString)) + behavior = EBhRequire; + else if (! strcmp("enable", behaviorString)) + behavior = EBhEnable; + else if (! strcmp("disable", behaviorString)) + behavior = EBhDisable; + else if (! strcmp("warn", behaviorString)) + behavior = EBhWarn; + else { + error(getCurrentLoc(), "behavior not supported:", "#extension", behaviorString); + return; + } + bool on = behavior != EBhDisable; + + // check if extension is used with correct shader stage + checkExtensionStage(getCurrentLoc(), extension); + + // check if extension has additional requirements + extensionRequires(getCurrentLoc(), extension ,behaviorString); + + // update the requested extension + updateExtensionBehavior(extension, behavior); + + // see if need to propagate to implicitly modified things + if (strcmp(extension, "GL_ANDROID_extension_pack_es31a") == 0) { + // to everything in AEP + updateExtensionBehavior(line, "GL_KHR_blend_equation_advanced", behaviorString); + updateExtensionBehavior(line, "GL_OES_sample_variables", behaviorString); + updateExtensionBehavior(line, "GL_OES_shader_image_atomic", behaviorString); + updateExtensionBehavior(line, "GL_OES_shader_multisample_interpolation", behaviorString); + updateExtensionBehavior(line, "GL_OES_texture_storage_multisample_2d_array", behaviorString); + updateExtensionBehavior(line, "GL_EXT_geometry_shader", behaviorString); + updateExtensionBehavior(line, "GL_EXT_gpu_shader5", behaviorString); + updateExtensionBehavior(line, "GL_EXT_primitive_bounding_box", behaviorString); + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + updateExtensionBehavior(line, "GL_EXT_tessellation_shader", behaviorString); + updateExtensionBehavior(line, "GL_EXT_texture_buffer", behaviorString); + updateExtensionBehavior(line, "GL_EXT_texture_cube_map_array", behaviorString); + } + // geometry to io_blocks + else if (strcmp(extension, "GL_EXT_geometry_shader") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_OES_geometry_shader") == 0) + updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); + // tessellation to io_blocks + else if (strcmp(extension, "GL_EXT_tessellation_shader") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_OES_tessellation_shader") == 0) + updateExtensionBehavior(line, "GL_OES_shader_io_blocks", behaviorString); + else if (strcmp(extension, "GL_GOOGLE_include_directive") == 0) + updateExtensionBehavior(line, "GL_GOOGLE_cpp_style_line_directive", behaviorString); + // subgroup_* to subgroup_basic + else if (strcmp(extension, "GL_KHR_shader_subgroup_vote") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_arithmetic") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_ballot") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_shuffle") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_shuffle_relative") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_clustered") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_KHR_shader_subgroup_quad") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_NV_shader_subgroup_partitioned") == 0) + updateExtensionBehavior(line, "GL_KHR_shader_subgroup_basic", behaviorString); + else if (strcmp(extension, "GL_EXT_buffer_reference2") == 0 || + strcmp(extension, "GL_EXT_buffer_reference_uvec2") == 0) + updateExtensionBehavior(line, "GL_EXT_buffer_reference", behaviorString); + else if (strcmp(extension, "GL_NV_integer_cooperative_matrix") == 0) + updateExtensionBehavior(line, "GL_NV_cooperative_matrix", behaviorString); + // subgroup extended types to explicit types + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int8") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int8", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int16") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int16", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_int64") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_int64", behaviorString); + else if (strcmp(extension, "GL_EXT_shader_subgroup_extended_types_float16") == 0) + updateExtensionBehavior(line, "GL_EXT_shader_explicit_arithmetic_types_float16", behaviorString); + + // see if we need to update the numeric features + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_int8") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_int8, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_int16") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_int16, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_int32") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_int32, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_int64") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_int64, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_float16") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_float16, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_float32") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_float32, on); + else if (strcmp(extension, "GL_EXT_shader_explicit_arithmetic_types_float64") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_explicit_arithmetic_types_float64, on); + else if (strcmp(extension, "GL_EXT_shader_implicit_conversions") == 0) + intermediate.updateNumericFeature(TNumericFeatures::shader_implicit_conversions, on); + else if (strcmp(extension, "GL_ARB_gpu_shader_fp64") == 0) + intermediate.updateNumericFeature(TNumericFeatures::gpu_shader_fp64, on); + else if (strcmp(extension, "GL_AMD_gpu_shader_int16") == 0) + intermediate.updateNumericFeature(TNumericFeatures::gpu_shader_int16, on); + else if (strcmp(extension, "GL_AMD_gpu_shader_half_float") == 0) + intermediate.updateNumericFeature(TNumericFeatures::gpu_shader_half_float, on); +} + +void TParseVersions::updateExtensionBehavior(const char* extension, TExtensionBehavior behavior) +{ + // Update the current behavior + if (strcmp(extension, "all") == 0) { + // special case for the 'all' extension; apply it to every extension present + if (behavior == EBhRequire || behavior == EBhEnable) { + error(getCurrentLoc(), "extension 'all' cannot have 'require' or 'enable' behavior", "#extension", ""); + return; + } else { + for (auto iter = extensionBehavior.begin(); iter != extensionBehavior.end(); ++iter) + iter->second = behavior; + } + } else { + // Do the update for this single extension + auto iter = extensionBehavior.find(TString(extension)); + if (iter == extensionBehavior.end()) { + switch (behavior) { + case EBhRequire: + error(getCurrentLoc(), "extension not supported:", "#extension", extension); + break; + case EBhEnable: + case EBhWarn: + case EBhDisable: + warn(getCurrentLoc(), "extension not supported:", "#extension", extension); + break; + default: + assert(0 && "unexpected behavior"); + } + + return; + } else { + if (iter->second == EBhDisablePartial) + warn(getCurrentLoc(), "extension is only partially supported:", "#extension", extension); + if (behavior != EBhDisable) + intermediate.addRequestedExtension(extension); + iter->second = behavior; + } + } +} + +// Check if extension is used with correct shader stage. +void TParseVersions::checkExtensionStage(const TSourceLoc& loc, const char * const extension) +{ + // GL_NV_mesh_shader extension is only allowed in task/mesh shaders + if (strcmp(extension, "GL_NV_mesh_shader") == 0) { + requireStage(loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask | EShLangFragmentMask), + "#extension GL_NV_mesh_shader"); + profileRequires(loc, ECoreProfile, 450, 0, "#extension GL_NV_mesh_shader"); + profileRequires(loc, EEsProfile, 320, 0, "#extension GL_NV_mesh_shader"); + } +} + +// Check if extension has additional requirements +void TParseVersions::extensionRequires(const TSourceLoc &loc, const char * const extension, const char *behaviorString) +{ + bool isEnabled = false; + if (!strcmp("require", behaviorString)) + isEnabled = true; + else if (!strcmp("enable", behaviorString)) + isEnabled = true; + + if (isEnabled) { + unsigned int minSpvVersion = 0; + auto iter = extensionMinSpv.find(TString(extension)); + if (iter != extensionMinSpv.end()) + minSpvVersion = iter->second; + requireSpv(loc, extension, minSpvVersion); + } +} + +// Call for any operation needing full GLSL integer data-type support. +void TParseVersions::fullIntegerCheck(const TSourceLoc& loc, const char* op) +{ + profileRequires(loc, ENoProfile, 130, nullptr, op); + profileRequires(loc, EEsProfile, 300, nullptr, op); +} + +// Call for any operation needing GLSL double data-type support. +void TParseVersions::doubleCheck(const TSourceLoc& loc, const char* op) +{ + + //requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + if (language == EShLangVertex) { + const char* const f64_Extensions[] = {E_GL_ARB_gpu_shader_fp64, E_GL_ARB_vertex_attrib_64bit}; + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, 2, f64_Extensions, op); + } else + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader_fp64, op); +} + +// Call for any operation needing GLSL float16 data-type support. +void TParseVersions::float16Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +bool TParseVersions::float16Arithmetic() +{ + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +bool TParseVersions::int16Arithmetic() +{ + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +bool TParseVersions::int8Arithmetic() +{ + const char* const extensions[] = { + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + return extensionsTurnedOn(sizeof(extensions)/sizeof(extensions[0]), extensions); +} + +void TParseVersions::requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) +{ + TString combined; + combined = op; + combined += ": "; + combined += featureDesc; + + const char* const extensions[] = { + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, combined.c_str()); +} + +void TParseVersions::float16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_half_float, + E_GL_EXT_shader_16bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +// Call for any operation needing GLSL float32 data-type support. +void TParseVersions::explicitFloat32Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float32}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL float64 data-type support. +void TParseVersions::explicitFloat64Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_float64}; + requireExtensions(loc, 2, extensions, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +// Call for any operation needing GLSL explicit int8 data-type support. +void TParseVersions::explicitInt8Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL float16 opaque-type support +void TParseVersions::float16OpaqueCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + requireExtensions(loc, 1, &E_GL_AMD_gpu_shader_half_float_fetch, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +// Call for any operation needing GLSL explicit int16 data-type support. +void TParseVersions::explicitInt16Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::int16ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_16bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::int8ScalarVectorCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[] = { + E_GL_EXT_shader_8bit_storage, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int8}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +// Call for any operation needing GLSL explicit int32 data-type support. +void TParseVersions::explicitInt32Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[2] = {E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int32}; + requireExtensions(loc, 2, extensions, op); + } +} + +// Call for any operation needing GLSL 64-bit integer data-type support. +void TParseVersions::int64Check(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (! builtIn) { + const char* const extensions[3] = {E_GL_ARB_gpu_shader_int64, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int64}; + requireExtensions(loc, 3, extensions, op); + requireProfile(loc, ECoreProfile | ECompatibilityProfile, op); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 400, nullptr, op); + } +} + +void TParseVersions::fcoopmatCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_NV_cooperative_matrix}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} + +void TParseVersions::intcoopmatCheck(const TSourceLoc& loc, const char* op, bool builtIn) +{ + if (!builtIn) { + const char* const extensions[] = {E_GL_NV_integer_cooperative_matrix}; + requireExtensions(loc, sizeof(extensions)/sizeof(extensions[0]), extensions, op); + } +} +#endif // GLSLANG_WEB +// Call for any operation removed because SPIR-V is in use. +void TParseVersions::spvRemoved(const TSourceLoc& loc, const char* op) +{ + if (spvVersion.spv != 0) + error(loc, "not allowed when generating SPIR-V", op, ""); +} + +// Call for any operation removed because Vulkan SPIR-V is being generated. +void TParseVersions::vulkanRemoved(const TSourceLoc& loc, const char* op) +{ + if (spvVersion.vulkan > 0) + error(loc, "not allowed when using GLSL for Vulkan", op, ""); +} + +// Call for any operation that requires Vulkan. +void TParseVersions::requireVulkan(const TSourceLoc& loc, const char* op) +{ +#ifndef GLSLANG_WEB + if (spvVersion.vulkan == 0) + error(loc, "only allowed when using GLSL for Vulkan", op, ""); +#endif +} + +// Call for any operation that requires SPIR-V. +void TParseVersions::requireSpv(const TSourceLoc& loc, const char* op) +{ +#ifndef GLSLANG_WEB + if (spvVersion.spv == 0) + error(loc, "only allowed when generating SPIR-V", op, ""); +#endif +} +void TParseVersions::requireSpv(const TSourceLoc& loc, const char *op, unsigned int version) +{ +#ifndef GLSLANG_WEB + if (spvVersion.spv < version) + error(loc, "not supported for current targeted SPIR-V version", op, ""); +#endif +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/Versions.h b/third_party/glslang/glslang/MachineIndependent/Versions.h new file mode 100644 index 0000000..eb17c52 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/Versions.h @@ -0,0 +1,337 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _VERSIONS_INCLUDED_ +#define _VERSIONS_INCLUDED_ + +#define LAST_ELEMENT_MARKER(x) x + +// +// Help manage multiple profiles, versions, extensions etc. +// + +// +// Profiles are set up for masking operations, so queries can be done on multiple +// profiles at the same time. +// +// Don't maintain an ordinal set of enums (0,1,2,3...) to avoid all possible +// defects from mixing the two different forms. +// +typedef enum : unsigned { + EBadProfile = 0, + ENoProfile = (1 << 0), // only for desktop, before profiles showed up + ECoreProfile = (1 << 1), + ECompatibilityProfile = (1 << 2), + EEsProfile = (1 << 3), + LAST_ELEMENT_MARKER(EProfileCount), +} EProfile; + +namespace glslang { + +// +// Map from profile enum to externally readable text name. +// +inline const char* ProfileName(EProfile profile) +{ + switch (profile) { + case ENoProfile: return "none"; + case ECoreProfile: return "core"; + case ECompatibilityProfile: return "compatibility"; + case EEsProfile: return "es"; + default: return "unknown profile"; + } +} + +// +// What source rules, validation rules, target language, etc. are needed or +// desired for SPIR-V? +// +// 0 means a target or rule set is not enabled (ignore rules from that entity). +// Non-0 means to apply semantic rules arising from that version of its rule set. +// The union of all requested rule sets will be applied. +// +struct SpvVersion { + SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0) {} + unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header + int vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX" + int vulkan; // the version of Vulkan, for which SPIR-V execution environment rules to use + int openGl; // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX" +}; + +// +// The behaviors from the GLSL "#extension extension_name : behavior" +// +typedef enum { + EBhMissing = 0, + EBhRequire, + EBhEnable, + EBhWarn, + EBhDisable, + EBhDisablePartial // use as initial state of an extension that is only partially implemented +} TExtensionBehavior; + +// +// Symbolic names for extensions. Strings may be directly used when calling the +// functions, but better to have the compiler do spelling checks. +// +const char* const E_GL_OES_texture_3D = "GL_OES_texture_3D"; +const char* const E_GL_OES_standard_derivatives = "GL_OES_standard_derivatives"; +const char* const E_GL_EXT_frag_depth = "GL_EXT_frag_depth"; +const char* const E_GL_OES_EGL_image_external = "GL_OES_EGL_image_external"; +const char* const E_GL_OES_EGL_image_external_essl3 = "GL_OES_EGL_image_external_essl3"; +const char* const E_GL_EXT_YUV_target = "GL_EXT_YUV_target"; +const char* const E_GL_EXT_shader_texture_lod = "GL_EXT_shader_texture_lod"; +const char* const E_GL_EXT_shadow_samplers = "GL_EXT_shadow_samplers"; + +const char* const E_GL_ARB_texture_rectangle = "GL_ARB_texture_rectangle"; +const char* const E_GL_3DL_array_objects = "GL_3DL_array_objects"; +const char* const E_GL_ARB_shading_language_420pack = "GL_ARB_shading_language_420pack"; +const char* const E_GL_ARB_texture_gather = "GL_ARB_texture_gather"; +const char* const E_GL_ARB_gpu_shader5 = "GL_ARB_gpu_shader5"; +const char* const E_GL_ARB_separate_shader_objects = "GL_ARB_separate_shader_objects"; +const char* const E_GL_ARB_compute_shader = "GL_ARB_compute_shader"; +const char* const E_GL_ARB_tessellation_shader = "GL_ARB_tessellation_shader"; +const char* const E_GL_ARB_enhanced_layouts = "GL_ARB_enhanced_layouts"; +const char* const E_GL_ARB_texture_cube_map_array = "GL_ARB_texture_cube_map_array"; +const char* const E_GL_ARB_texture_multisample = "GL_ARB_texture_multisample"; +const char* const E_GL_ARB_shader_texture_lod = "GL_ARB_shader_texture_lod"; +const char* const E_GL_ARB_explicit_attrib_location = "GL_ARB_explicit_attrib_location"; +const char* const E_GL_ARB_explicit_uniform_location = "GL_ARB_explicit_uniform_location"; +const char* const E_GL_ARB_shader_image_load_store = "GL_ARB_shader_image_load_store"; +const char* const E_GL_ARB_shader_atomic_counters = "GL_ARB_shader_atomic_counters"; +const char* const E_GL_ARB_shader_draw_parameters = "GL_ARB_shader_draw_parameters"; +const char* const E_GL_ARB_shader_group_vote = "GL_ARB_shader_group_vote"; +const char* const E_GL_ARB_derivative_control = "GL_ARB_derivative_control"; +const char* const E_GL_ARB_shader_texture_image_samples = "GL_ARB_shader_texture_image_samples"; +const char* const E_GL_ARB_viewport_array = "GL_ARB_viewport_array"; +const char* const E_GL_ARB_gpu_shader_int64 = "GL_ARB_gpu_shader_int64"; +const char* const E_GL_ARB_gpu_shader_fp64 = "GL_ARB_gpu_shader_fp64"; +const char* const E_GL_ARB_shader_ballot = "GL_ARB_shader_ballot"; +const char* const E_GL_ARB_sparse_texture2 = "GL_ARB_sparse_texture2"; +const char* const E_GL_ARB_sparse_texture_clamp = "GL_ARB_sparse_texture_clamp"; +const char* const E_GL_ARB_shader_stencil_export = "GL_ARB_shader_stencil_export"; +// const char* const E_GL_ARB_cull_distance = "GL_ARB_cull_distance"; // present for 4.5, but need extension control over block members +const char* const E_GL_ARB_post_depth_coverage = "GL_ARB_post_depth_coverage"; +const char* const E_GL_ARB_shader_viewport_layer_array = "GL_ARB_shader_viewport_layer_array"; +const char* const E_GL_ARB_fragment_shader_interlock = "GL_ARB_fragment_shader_interlock"; +const char* const E_GL_ARB_shader_clock = "GL_ARB_shader_clock"; +const char* const E_GL_ARB_uniform_buffer_object = "GL_ARB_uniform_buffer_object"; +const char* const E_GL_ARB_sample_shading = "GL_ARB_sample_shading"; +const char* const E_GL_ARB_shader_bit_encoding = "GL_ARB_shader_bit_encoding"; +const char* const E_GL_ARB_shader_image_size = "GL_ARB_shader_image_size"; +const char* const E_GL_ARB_shader_storage_buffer_object = "GL_ARB_shader_storage_buffer_object"; +const char* const E_GL_ARB_shading_language_packing = "GL_ARB_shading_language_packing"; +const char* const E_GL_ARB_texture_query_lod = "GL_ARB_texture_query_lod"; +const char* const E_GL_ARB_vertex_attrib_64bit = "GL_ARB_vertex_attrib_64bit"; + +const char* const E_GL_KHR_shader_subgroup_basic = "GL_KHR_shader_subgroup_basic"; +const char* const E_GL_KHR_shader_subgroup_vote = "GL_KHR_shader_subgroup_vote"; +const char* const E_GL_KHR_shader_subgroup_arithmetic = "GL_KHR_shader_subgroup_arithmetic"; +const char* const E_GL_KHR_shader_subgroup_ballot = "GL_KHR_shader_subgroup_ballot"; +const char* const E_GL_KHR_shader_subgroup_shuffle = "GL_KHR_shader_subgroup_shuffle"; +const char* const E_GL_KHR_shader_subgroup_shuffle_relative = "GL_KHR_shader_subgroup_shuffle_relative"; +const char* const E_GL_KHR_shader_subgroup_clustered = "GL_KHR_shader_subgroup_clustered"; +const char* const E_GL_KHR_shader_subgroup_quad = "GL_KHR_shader_subgroup_quad"; +const char* const E_GL_KHR_memory_scope_semantics = "GL_KHR_memory_scope_semantics"; + +const char* const E_GL_EXT_shader_atomic_int64 = "GL_EXT_shader_atomic_int64"; + +const char* const E_GL_EXT_shader_non_constant_global_initializers = "GL_EXT_shader_non_constant_global_initializers"; +const char* const E_GL_EXT_shader_image_load_formatted = "GL_EXT_shader_image_load_formatted"; + +const char* const E_GL_EXT_shader_16bit_storage = "GL_EXT_shader_16bit_storage"; +const char* const E_GL_EXT_shader_8bit_storage = "GL_EXT_shader_8bit_storage"; + + +// EXT extensions +const char* const E_GL_EXT_device_group = "GL_EXT_device_group"; +const char* const E_GL_EXT_multiview = "GL_EXT_multiview"; +const char* const E_GL_EXT_post_depth_coverage = "GL_EXT_post_depth_coverage"; +const char* const E_GL_EXT_control_flow_attributes = "GL_EXT_control_flow_attributes"; +const char* const E_GL_EXT_nonuniform_qualifier = "GL_EXT_nonuniform_qualifier"; +const char* const E_GL_EXT_samplerless_texture_functions = "GL_EXT_samplerless_texture_functions"; +const char* const E_GL_EXT_scalar_block_layout = "GL_EXT_scalar_block_layout"; +const char* const E_GL_EXT_fragment_invocation_density = "GL_EXT_fragment_invocation_density"; +const char* const E_GL_EXT_buffer_reference = "GL_EXT_buffer_reference"; +const char* const E_GL_EXT_buffer_reference2 = "GL_EXT_buffer_reference2"; +const char* const E_GL_EXT_buffer_reference_uvec2 = "GL_EXT_buffer_reference_uvec2"; +const char* const E_GL_EXT_demote_to_helper_invocation = "GL_EXT_demote_to_helper_invocation"; +const char* const E_GL_EXT_shader_realtime_clock = "GL_EXT_shader_realtime_clock"; +const char* const E_GL_EXT_debug_printf = "GL_EXT_debug_printf"; +const char* const E_GL_EXT_ray_tracing = "GL_EXT_ray_tracing"; +const char* const E_GL_EXT_ray_query = "GL_EXT_ray_query"; +const char* const E_GL_EXT_ray_flags_primitive_culling = "GL_EXT_ray_flags_primitive_culling"; +const char* const E_GL_EXT_blend_func_extended = "GL_EXT_blend_func_extended"; +const char* const E_GL_EXT_shader_implicit_conversions = "GL_EXT_shader_implicit_conversions"; +const char* const E_GL_EXT_fragment_shading_rate = "GL_EXT_fragment_shading_rate"; +const char* const E_GL_EXT_shader_image_int64 = "GL_EXT_shader_image_int64"; + +// Arrays of extensions for the above viewportEXTs duplications + +const char* const post_depth_coverageEXTs[] = { E_GL_ARB_post_depth_coverage, E_GL_EXT_post_depth_coverage }; +const int Num_post_depth_coverageEXTs = sizeof(post_depth_coverageEXTs) / sizeof(post_depth_coverageEXTs[0]); + +// OVR extensions +const char* const E_GL_OVR_multiview = "GL_OVR_multiview"; +const char* const E_GL_OVR_multiview2 = "GL_OVR_multiview2"; + +const char* const OVR_multiview_EXTs[] = { E_GL_OVR_multiview, E_GL_OVR_multiview2 }; +const int Num_OVR_multiview_EXTs = sizeof(OVR_multiview_EXTs) / sizeof(OVR_multiview_EXTs[0]); + +// #line and #include +const char* const E_GL_GOOGLE_cpp_style_line_directive = "GL_GOOGLE_cpp_style_line_directive"; +const char* const E_GL_GOOGLE_include_directive = "GL_GOOGLE_include_directive"; + +const char* const E_GL_AMD_shader_ballot = "GL_AMD_shader_ballot"; +const char* const E_GL_AMD_shader_trinary_minmax = "GL_AMD_shader_trinary_minmax"; +const char* const E_GL_AMD_shader_explicit_vertex_parameter = "GL_AMD_shader_explicit_vertex_parameter"; +const char* const E_GL_AMD_gcn_shader = "GL_AMD_gcn_shader"; +const char* const E_GL_AMD_gpu_shader_half_float = "GL_AMD_gpu_shader_half_float"; +const char* const E_GL_AMD_texture_gather_bias_lod = "GL_AMD_texture_gather_bias_lod"; +const char* const E_GL_AMD_gpu_shader_int16 = "GL_AMD_gpu_shader_int16"; +const char* const E_GL_AMD_shader_image_load_store_lod = "GL_AMD_shader_image_load_store_lod"; +const char* const E_GL_AMD_shader_fragment_mask = "GL_AMD_shader_fragment_mask"; +const char* const E_GL_AMD_gpu_shader_half_float_fetch = "GL_AMD_gpu_shader_half_float_fetch"; + +const char* const E_GL_INTEL_shader_integer_functions2 = "GL_INTEL_shader_integer_functions2"; + +const char* const E_GL_NV_sample_mask_override_coverage = "GL_NV_sample_mask_override_coverage"; +const char* const E_SPV_NV_geometry_shader_passthrough = "GL_NV_geometry_shader_passthrough"; +const char* const E_GL_NV_viewport_array2 = "GL_NV_viewport_array2"; +const char* const E_GL_NV_stereo_view_rendering = "GL_NV_stereo_view_rendering"; +const char* const E_GL_NVX_multiview_per_view_attributes = "GL_NVX_multiview_per_view_attributes"; +const char* const E_GL_NV_shader_atomic_int64 = "GL_NV_shader_atomic_int64"; +const char* const E_GL_NV_conservative_raster_underestimation = "GL_NV_conservative_raster_underestimation"; +const char* const E_GL_NV_shader_noperspective_interpolation = "GL_NV_shader_noperspective_interpolation"; +const char* const E_GL_NV_shader_subgroup_partitioned = "GL_NV_shader_subgroup_partitioned"; +const char* const E_GL_NV_shading_rate_image = "GL_NV_shading_rate_image"; +const char* const E_GL_NV_ray_tracing = "GL_NV_ray_tracing"; +const char* const E_GL_NV_fragment_shader_barycentric = "GL_NV_fragment_shader_barycentric"; +const char* const E_GL_NV_compute_shader_derivatives = "GL_NV_compute_shader_derivatives"; +const char* const E_GL_NV_shader_texture_footprint = "GL_NV_shader_texture_footprint"; +const char* const E_GL_NV_mesh_shader = "GL_NV_mesh_shader"; + +// Arrays of extensions for the above viewportEXTs duplications + +const char* const viewportEXTs[] = { E_GL_ARB_shader_viewport_layer_array, E_GL_NV_viewport_array2 }; +const int Num_viewportEXTs = sizeof(viewportEXTs) / sizeof(viewportEXTs[0]); + +const char* const E_GL_NV_cooperative_matrix = "GL_NV_cooperative_matrix"; +const char* const E_GL_NV_shader_sm_builtins = "GL_NV_shader_sm_builtins"; +const char* const E_GL_NV_integer_cooperative_matrix = "GL_NV_integer_cooperative_matrix"; + +// AEP +const char* const E_GL_ANDROID_extension_pack_es31a = "GL_ANDROID_extension_pack_es31a"; +const char* const E_GL_KHR_blend_equation_advanced = "GL_KHR_blend_equation_advanced"; +const char* const E_GL_OES_sample_variables = "GL_OES_sample_variables"; +const char* const E_GL_OES_shader_image_atomic = "GL_OES_shader_image_atomic"; +const char* const E_GL_OES_shader_multisample_interpolation = "GL_OES_shader_multisample_interpolation"; +const char* const E_GL_OES_texture_storage_multisample_2d_array = "GL_OES_texture_storage_multisample_2d_array"; +const char* const E_GL_EXT_geometry_shader = "GL_EXT_geometry_shader"; +const char* const E_GL_EXT_geometry_point_size = "GL_EXT_geometry_point_size"; +const char* const E_GL_EXT_gpu_shader5 = "GL_EXT_gpu_shader5"; +const char* const E_GL_EXT_primitive_bounding_box = "GL_EXT_primitive_bounding_box"; +const char* const E_GL_EXT_shader_io_blocks = "GL_EXT_shader_io_blocks"; +const char* const E_GL_EXT_tessellation_shader = "GL_EXT_tessellation_shader"; +const char* const E_GL_EXT_tessellation_point_size = "GL_EXT_tessellation_point_size"; +const char* const E_GL_EXT_texture_buffer = "GL_EXT_texture_buffer"; +const char* const E_GL_EXT_texture_cube_map_array = "GL_EXT_texture_cube_map_array"; +const char* const E_GL_EXT_shader_integer_mix = "GL_EXT_shader_integer_mix"; + +// OES matching AEP +const char* const E_GL_OES_geometry_shader = "GL_OES_geometry_shader"; +const char* const E_GL_OES_geometry_point_size = "GL_OES_geometry_point_size"; +const char* const E_GL_OES_gpu_shader5 = "GL_OES_gpu_shader5"; +const char* const E_GL_OES_primitive_bounding_box = "GL_OES_primitive_bounding_box"; +const char* const E_GL_OES_shader_io_blocks = "GL_OES_shader_io_blocks"; +const char* const E_GL_OES_tessellation_shader = "GL_OES_tessellation_shader"; +const char* const E_GL_OES_tessellation_point_size = "GL_OES_tessellation_point_size"; +const char* const E_GL_OES_texture_buffer = "GL_OES_texture_buffer"; +const char* const E_GL_OES_texture_cube_map_array = "GL_OES_texture_cube_map_array"; + +// EXT +const char* const E_GL_EXT_shader_explicit_arithmetic_types = "GL_EXT_shader_explicit_arithmetic_types"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int8 = "GL_EXT_shader_explicit_arithmetic_types_int8"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int16 = "GL_EXT_shader_explicit_arithmetic_types_int16"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int32 = "GL_EXT_shader_explicit_arithmetic_types_int32"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_int64 = "GL_EXT_shader_explicit_arithmetic_types_int64"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float16 = "GL_EXT_shader_explicit_arithmetic_types_float16"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float32 = "GL_EXT_shader_explicit_arithmetic_types_float32"; +const char* const E_GL_EXT_shader_explicit_arithmetic_types_float64 = "GL_EXT_shader_explicit_arithmetic_types_float64"; + +const char* const E_GL_EXT_shader_subgroup_extended_types_int8 = "GL_EXT_shader_subgroup_extended_types_int8"; +const char* const E_GL_EXT_shader_subgroup_extended_types_int16 = "GL_EXT_shader_subgroup_extended_types_int16"; +const char* const E_GL_EXT_shader_subgroup_extended_types_int64 = "GL_EXT_shader_subgroup_extended_types_int64"; +const char* const E_GL_EXT_shader_subgroup_extended_types_float16 = "GL_EXT_shader_subgroup_extended_types_float16"; +const char* const E_GL_EXT_terminate_invocation = "GL_EXT_terminate_invocation"; + +const char* const E_GL_EXT_shader_atomic_float = "GL_EXT_shader_atomic_float"; + +// Arrays of extensions for the above AEP duplications + +const char* const AEP_geometry_shader[] = { E_GL_EXT_geometry_shader, E_GL_OES_geometry_shader }; +const int Num_AEP_geometry_shader = sizeof(AEP_geometry_shader)/sizeof(AEP_geometry_shader[0]); + +const char* const AEP_geometry_point_size[] = { E_GL_EXT_geometry_point_size, E_GL_OES_geometry_point_size }; +const int Num_AEP_geometry_point_size = sizeof(AEP_geometry_point_size)/sizeof(AEP_geometry_point_size[0]); + +const char* const AEP_gpu_shader5[] = { E_GL_EXT_gpu_shader5, E_GL_OES_gpu_shader5 }; +const int Num_AEP_gpu_shader5 = sizeof(AEP_gpu_shader5)/sizeof(AEP_gpu_shader5[0]); + +const char* const AEP_primitive_bounding_box[] = { E_GL_EXT_primitive_bounding_box, E_GL_OES_primitive_bounding_box }; +const int Num_AEP_primitive_bounding_box = sizeof(AEP_primitive_bounding_box)/sizeof(AEP_primitive_bounding_box[0]); + +const char* const AEP_shader_io_blocks[] = { E_GL_EXT_shader_io_blocks, E_GL_OES_shader_io_blocks }; +const int Num_AEP_shader_io_blocks = sizeof(AEP_shader_io_blocks)/sizeof(AEP_shader_io_blocks[0]); + +const char* const AEP_tessellation_shader[] = { E_GL_EXT_tessellation_shader, E_GL_OES_tessellation_shader }; +const int Num_AEP_tessellation_shader = sizeof(AEP_tessellation_shader)/sizeof(AEP_tessellation_shader[0]); + +const char* const AEP_tessellation_point_size[] = { E_GL_EXT_tessellation_point_size, E_GL_OES_tessellation_point_size }; +const int Num_AEP_tessellation_point_size = sizeof(AEP_tessellation_point_size)/sizeof(AEP_tessellation_point_size[0]); + +const char* const AEP_texture_buffer[] = { E_GL_EXT_texture_buffer, E_GL_OES_texture_buffer }; +const int Num_AEP_texture_buffer = sizeof(AEP_texture_buffer)/sizeof(AEP_texture_buffer[0]); + +const char* const AEP_texture_cube_map_array[] = { E_GL_EXT_texture_cube_map_array, E_GL_OES_texture_cube_map_array }; +const int Num_AEP_texture_cube_map_array = sizeof(AEP_texture_cube_map_array)/sizeof(AEP_texture_cube_map_array[0]); + +} // end namespace glslang + +#endif // _VERSIONS_INCLUDED_ diff --git a/third_party/glslang/glslang/MachineIndependent/attribute.cpp b/third_party/glslang/glslang/MachineIndependent/attribute.cpp new file mode 100644 index 0000000..9585518 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/attribute.cpp @@ -0,0 +1,346 @@ +// +// Copyright (C) 2017 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef GLSLANG_WEB + +#include "attribute.h" +#include "../Include/intermediate.h" +#include "ParseHelper.h" + +namespace glslang { + +// extract integers out of attribute arguments stored in attribute aggregate +bool TAttributeArgs::getInt(int& value, int argNum) const +{ + const TConstUnion* intConst = getConstUnion(EbtInt, argNum); + + if (intConst == nullptr) + return false; + + value = intConst->getIConst(); + return true; +} + + +// extract strings out of attribute arguments stored in attribute aggregate. +// convert to lower case if converToLower is true (for case-insensitive compare convenience) +bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const +{ + const TConstUnion* stringConst = getConstUnion(EbtString, argNum); + + if (stringConst == nullptr) + return false; + + value = *stringConst->getSConst(); + + // Convenience. + if (convertToLower) + std::transform(value.begin(), value.end(), value.begin(), ::tolower); + + return true; +} + +// How many arguments were supplied? +int TAttributeArgs::size() const +{ + return args == nullptr ? 0 : (int)args->getSequence().size(); +} + +// Helper to get attribute const union. Returns nullptr on failure. +const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const +{ + if (args == nullptr) + return nullptr; + + if (argNum >= (int)args->getSequence().size()) + return nullptr; + + if (args->getSequence()[argNum]->getAsConstantUnion() == nullptr) + return nullptr; + + const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0]; + if (constVal == nullptr || constVal->getType() != basicType) + return nullptr; + + return constVal; +} + +// Implementation of TParseContext parts of attributes +TAttributeType TParseContext::attributeFromName(const TString& name) const +{ + if (name == "branch" || name == "dont_flatten") + return EatBranch; + else if (name == "flatten") + return EatFlatten; + else if (name == "unroll") + return EatUnroll; + else if (name == "loop" || name == "dont_unroll") + return EatLoop; + else if (name == "dependency_infinite") + return EatDependencyInfinite; + else if (name == "dependency_length") + return EatDependencyLength; + else if (name == "min_iterations") + return EatMinIterations; + else if (name == "max_iterations") + return EatMaxIterations; + else if (name == "iteration_multiple") + return EatIterationMultiple; + else if (name == "peel_count") + return EatPeelCount; + else if (name == "partial_count") + return EatPartialCount; + else + return EatNone; +} + +// Make an initial leaf for the grammar from a no-argument attribute +TAttributes* TParseContext::makeAttributes(const TString& identifier) const +{ + TAttributes *attributes = nullptr; + attributes = NewPoolObject(attributes); + TAttributeArgs args = { attributeFromName(identifier), nullptr }; + attributes->push_back(args); + return attributes; +} + +// Make an initial leaf for the grammar from a one-argument attribute +TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const +{ + TAttributes *attributes = nullptr; + attributes = NewPoolObject(attributes); + + // for now, node is always a simple single expression, but other code expects + // a list, so make it so + TIntermAggregate* agg = intermediate.makeAggregate(node); + TAttributeArgs args = { attributeFromName(identifier), agg }; + attributes->push_back(args); + return attributes; +} + +// Merge two sets of attributes into a single set. +// The second argument is destructively consumed. +TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const +{ + attr1->splice(attr1->end(), *attr2); + return attr1; +} + +// +// Selection attributes +// +void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermSelection* selection = node->getAsSelectionNode(); + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + if (it->size() > 0) { + warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", ""); + continue; + } + + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(node->getLoc(), "attribute does not apply to a selection", "", ""); + break; + } + } +} + +// +// Switch attributes +// +void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermSwitch* selection = node->getAsSwitchNode(); + if (selection == nullptr) + return; + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + if (it->size() > 0) { + warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", ""); + continue; + } + + switch (it->name) { + case EatFlatten: + selection->setFlatten(); + break; + case EatBranch: + selection->setDontFlatten(); + break; + default: + warn(node->getLoc(), "attribute does not apply to a switch", "", ""); + break; + } + } +} + +// +// Loop attributes +// +void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node) +{ + TIntermLoop* loop = node->getAsLoopNode(); + if (loop == nullptr) { + // the actual loop might be part of a sequence + TIntermAggregate* agg = node->getAsAggregate(); + if (agg == nullptr) + return; + for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) { + loop = (*it)->getAsLoopNode(); + if (loop != nullptr) + break; + } + if (loop == nullptr) + return; + } + + for (auto it = attributes.begin(); it != attributes.end(); ++it) { + + const auto noArgument = [&](const char* feature) { + if (it->size() > 0) { + warn(node->getLoc(), "expected no arguments", feature, ""); + return false; + } + return true; + }; + + const auto positiveSignedArgument = [&](const char* feature, int& value) { + if (it->size() == 1 && it->getInt(value)) { + if (value <= 0) { + error(node->getLoc(), "must be positive", feature, ""); + return false; + } + } else { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + return true; + }; + + const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) { + int value; + if (!(it->size() == 1 && it->getInt(value))) { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + uiValue = (unsigned int)value; + return true; + }; + + const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) { + int value; + if (it->size() == 1 && it->getInt(value)) { + if (value == 0) { + error(node->getLoc(), "must be greater than or equal to 1", feature, ""); + return false; + } + } else { + warn(node->getLoc(), "expected a single integer argument", feature, ""); + return false; + } + uiValue = (unsigned int)value; + return true; + }; + + const auto spirv14 = [&](const char* feature) { + if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4) + warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, ""); + }; + + int value = 0; + unsigned uiValue = 0; + switch (it->name) { + case EatUnroll: + if (noArgument("unroll")) + loop->setUnroll(); + break; + case EatLoop: + if (noArgument("dont_unroll")) + loop->setDontUnroll(); + break; + case EatDependencyInfinite: + if (noArgument("dependency_infinite")) + loop->setLoopDependency(TIntermLoop::dependencyInfinite); + break; + case EatDependencyLength: + if (positiveSignedArgument("dependency_length", value)) + loop->setLoopDependency(value); + break; + case EatMinIterations: + spirv14("min_iterations"); + if (unsignedArgument("min_iterations", uiValue)) + loop->setMinIterations(uiValue); + break; + case EatMaxIterations: + spirv14("max_iterations"); + if (unsignedArgument("max_iterations", uiValue)) + loop->setMaxIterations(uiValue); + break; + case EatIterationMultiple: + spirv14("iteration_multiple"); + if (positiveUnsignedArgument("iteration_multiple", uiValue)) + loop->setIterationMultiple(uiValue); + break; + case EatPeelCount: + spirv14("peel_count"); + if (unsignedArgument("peel_count", uiValue)) + loop->setPeelCount(uiValue); + break; + case EatPartialCount: + spirv14("partial_count"); + if (unsignedArgument("partial_count", uiValue)) + loop->setPartialCount(uiValue); + break; + default: + warn(node->getLoc(), "attribute does not apply to a loop", "", ""); + break; + } + } +} + +} // end namespace glslang + +#endif // GLSLANG_WEB diff --git a/third_party/glslang/glslang/MachineIndependent/attribute.h b/third_party/glslang/glslang/MachineIndependent/attribute.h new file mode 100644 index 0000000..38a943d --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/attribute.h @@ -0,0 +1,149 @@ +// +// Copyright (C) 2017 LunarG, Inc. +// Copyright (C) 2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _ATTRIBUTE_INCLUDED_ +#define _ATTRIBUTE_INCLUDED_ + +#include "../Include/Common.h" +#include "../Include/ConstantUnion.h" + +namespace glslang { + + enum TAttributeType { + EatNone, + EatAllow_uav_condition, + EatBranch, + EatCall, + EatDomain, + EatEarlyDepthStencil, + EatFastOpt, + EatFlatten, + EatForceCase, + EatInstance, + EatMaxTessFactor, + EatNumThreads, + EatMaxVertexCount, + EatOutputControlPoints, + EatOutputTopology, + EatPartitioning, + EatPatchConstantFunc, + EatPatchSize, + EatUnroll, + EatLoop, + EatBinding, + EatGlobalBinding, + EatLocation, + EatInputAttachment, + EatBuiltIn, + EatPushConstant, + EatConstantId, + EatDependencyInfinite, + EatDependencyLength, + EatMinIterations, + EatMaxIterations, + EatIterationMultiple, + EatPeelCount, + EatPartialCount, + EatFormatRgba32f, + EatFormatRgba16f, + EatFormatR32f, + EatFormatRgba8, + EatFormatRgba8Snorm, + EatFormatRg32f, + EatFormatRg16f, + EatFormatR11fG11fB10f, + EatFormatR16f, + EatFormatRgba16, + EatFormatRgb10A2, + EatFormatRg16, + EatFormatRg8, + EatFormatR16, + EatFormatR8, + EatFormatRgba16Snorm, + EatFormatRg16Snorm, + EatFormatRg8Snorm, + EatFormatR16Snorm, + EatFormatR8Snorm, + EatFormatRgba32i, + EatFormatRgba16i, + EatFormatRgba8i, + EatFormatR32i, + EatFormatRg32i, + EatFormatRg16i, + EatFormatRg8i, + EatFormatR16i, + EatFormatR8i, + EatFormatRgba32ui, + EatFormatRgba16ui, + EatFormatRgba8ui, + EatFormatR32ui, + EatFormatRgb10a2ui, + EatFormatRg32ui, + EatFormatRg16ui, + EatFormatRg8ui, + EatFormatR16ui, + EatFormatR8ui, + EatFormatUnknown, + EatNonWritable, + EatNonReadable + }; + + class TIntermAggregate; + + struct TAttributeArgs { + TAttributeType name; + const TIntermAggregate* args; + + // Obtain attribute as integer + // Return false if it cannot be obtained + bool getInt(int& value, int argNum = 0) const; + + // Obtain attribute as string, with optional to-lower transform + // Return false if it cannot be obtained + bool getString(TString& value, int argNum = 0, bool convertToLower = true) const; + + // How many arguments were provided to the attribute? + int size() const; + + protected: + const TConstUnion* getConstUnion(TBasicType basicType, int argNum) const; + }; + + typedef TList TAttributes; + +} // end namespace glslang + +#endif // _ATTRIBUTE_INCLUDED_ diff --git a/third_party/glslang/glslang/MachineIndependent/gl_types.h b/third_party/glslang/glslang/MachineIndependent/gl_types.h new file mode 100644 index 0000000..b9372d4 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/gl_types.h @@ -0,0 +1,218 @@ +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +#pragma once + +#define GL_FLOAT 0x1406 +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 + +#define GL_DOUBLE 0x140A +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE + +#define GL_INT 0x1404 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 + +#define GL_UNSIGNED_INT 0x1405 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 + +#define GL_INT64_ARB 0x140E +#define GL_INT64_VEC2_ARB 0x8FE9 +#define GL_INT64_VEC3_ARB 0x8FEA +#define GL_INT64_VEC4_ARB 0x8FEB + +#define GL_UNSIGNED_INT64_ARB 0x140F +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FE5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FE6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FE7 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 + +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 + +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 + +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A + +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E + +// Those constants are borrowed from extension NV_gpu_shader5 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB + +#define GL_FLOAT16_MAT2_AMD 0x91C5 +#define GL_FLOAT16_MAT3_AMD 0x91C6 +#define GL_FLOAT16_MAT4_AMD 0x91C7 +#define GL_FLOAT16_MAT2x3_AMD 0x91C8 +#define GL_FLOAT16_MAT2x4_AMD 0x91C9 +#define GL_FLOAT16_MAT3x2_AMD 0x91CA +#define GL_FLOAT16_MAT3x4_AMD 0x91CB +#define GL_FLOAT16_MAT4x2_AMD 0x91CC +#define GL_FLOAT16_MAT4x3_AMD 0x91CD + +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D + +#define GL_FLOAT16_SAMPLER_1D_AMD 0x91CE +#define GL_FLOAT16_SAMPLER_2D_AMD 0x91CF +#define GL_FLOAT16_SAMPLER_3D_AMD 0x91D0 +#define GL_FLOAT16_SAMPLER_CUBE_AMD 0x91D1 +#define GL_FLOAT16_SAMPLER_2D_RECT_AMD 0x91D2 +#define GL_FLOAT16_SAMPLER_1D_ARRAY_AMD 0x91D3 +#define GL_FLOAT16_SAMPLER_2D_ARRAY_AMD 0x91D4 +#define GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_AMD 0x91D5 +#define GL_FLOAT16_SAMPLER_BUFFER_AMD 0x91D6 +#define GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_AMD 0x91D7 +#define GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_ARRAY_AMD 0x91D8 + +#define GL_FLOAT16_SAMPLER_1D_SHADOW_AMD 0x91D9 +#define GL_FLOAT16_SAMPLER_2D_SHADOW_AMD 0x91DA +#define GL_FLOAT16_SAMPLER_2D_RECT_SHADOW_AMD 0x91DB +#define GL_FLOAT16_SAMPLER_1D_ARRAY_SHADOW_AMD 0x91DC +#define GL_FLOAT16_SAMPLER_2D_ARRAY_SHADOW_AMD 0x91DD +#define GL_FLOAT16_SAMPLER_CUBE_SHADOW_AMD 0x91DE +#define GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_SHADOW_AMD 0x91DF + +#define GL_FLOAT16_IMAGE_1D_AMD 0x91E0 +#define GL_FLOAT16_IMAGE_2D_AMD 0x91E1 +#define GL_FLOAT16_IMAGE_3D_AMD 0x91E2 +#define GL_FLOAT16_IMAGE_2D_RECT_AMD 0x91E3 +#define GL_FLOAT16_IMAGE_CUBE_AMD 0x91E4 +#define GL_FLOAT16_IMAGE_1D_ARRAY_AMD 0x91E5 +#define GL_FLOAT16_IMAGE_2D_ARRAY_AMD 0x91E6 +#define GL_FLOAT16_IMAGE_CUBE_MAP_ARRAY_AMD 0x91E7 +#define GL_FLOAT16_IMAGE_BUFFER_AMD 0x91E8 +#define GL_FLOAT16_IMAGE_2D_MULTISAMPLE_AMD 0x91E9 +#define GL_FLOAT16_IMAGE_2D_MULTISAMPLE_ARRAY_AMD 0x91EA + +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E + +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A + +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C + +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB diff --git a/third_party/glslang/glslang/MachineIndependent/glslang.m4 b/third_party/glslang/glslang/MachineIndependent/glslang.m4 new file mode 100644 index 0000000..5b0adb0 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/glslang.m4 @@ -0,0 +1,4033 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2019 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do not edit the .y file, only edit the .m4 file. +// The .y bison file is not a source file, it is a derivative of the .m4 file. +// The m4 file needs to be processed by m4 to generate the .y bison file. +// +// Code sandwiched between a pair: +// +// GLSLANG_WEB_EXCLUDE_ON +// ... +// ... +// ... +// GLSLANG_WEB_EXCLUDE_OFF +// +// Will be excluded from the grammar when m4 is executed as: +// +// m4 -P -DGLSLANG_WEB +// +// It will be included when m4 is executed as: +// +// m4 -P +// + +m4_define(`GLSLANG_WEB_EXCLUDE_ON', `m4_ifdef(`GLSLANG_WEB', `m4_divert(`-1')')') +m4_define(`GLSLANG_WEB_EXCLUDE_OFF', `m4_ifdef(`GLSLANG_WEB', `m4_divert')') + +/** + * This is bison grammar and productions for parsing all versions of the + * GLSL shading languages. + */ +%{ + +/* Based on: +ANSI C Yacc grammar + +In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a +matching Lex specification) for the April 30, 1985 draft version of the +ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that +original, as mentioned in the answer to question 17.25 of the comp.lang.c +FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z. + +I intend to keep this version as close to the current C Standard grammar as +possible; please let me know if you discover discrepancies. + +Jutta Degener, 1995 +*/ + +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "../Public/ShaderLang.h" +#include "attribute.h" + +using namespace glslang; + +%} + +%define parse.error verbose + +%union { + struct { + glslang::TSourceLoc loc; + union { + glslang::TString *string; + int i; + unsigned int u; + long long i64; + unsigned long long u64; + bool b; + double d; + }; + glslang::TSymbol* symbol; + } lex; + struct { + glslang::TSourceLoc loc; + glslang::TOperator op; + union { + TIntermNode* intermNode; + glslang::TIntermNodePair nodePair; + glslang::TIntermTyped* intermTypedNode; + glslang::TAttributes* attributes; + }; + union { + glslang::TPublicType type; + glslang::TFunction* function; + glslang::TParameter param; + glslang::TTypeLoc typeLine; + glslang::TTypeList* typeList; + glslang::TArraySizes* arraySizes; + glslang::TIdentifierList* identifierList; + }; + glslang::TArraySizes* typeParameters; + } interm; +} + +%{ + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4065) + #pragma warning(disable : 4127) + #pragma warning(disable : 4244) +#endif + +#define parseContext (*pParseContext) +#define yyerror(context, msg) context->parserError(msg) + +extern int yylex(YYSTYPE*, TParseContext&); + +%} + +%parse-param {glslang::TParseContext* pParseContext} +%lex-param {parseContext} +%pure-parser // enable thread safety +%expect 1 // One shift reduce conflict because of if | else + +%token CONST BOOL INT UINT FLOAT +%token BVEC2 BVEC3 BVEC4 +%token IVEC2 IVEC3 IVEC4 +%token UVEC2 UVEC3 UVEC4 +%token VEC2 VEC3 VEC4 +%token MAT2 MAT3 MAT4 +%token MAT2X2 MAT2X3 MAT2X4 +%token MAT3X2 MAT3X3 MAT3X4 +%token MAT4X2 MAT4X3 MAT4X4 + +// combined image/sampler +%token SAMPLER2D SAMPLER3D SAMPLERCUBE SAMPLER2DSHADOW +%token SAMPLERCUBESHADOW SAMPLER2DARRAY +%token SAMPLER2DARRAYSHADOW ISAMPLER2D ISAMPLER3D ISAMPLERCUBE +%token ISAMPLER2DARRAY USAMPLER2D USAMPLER3D +%token USAMPLERCUBE USAMPLER2DARRAY + +// separate image/sampler +%token SAMPLER SAMPLERSHADOW +%token TEXTURE2D TEXTURE3D TEXTURECUBE TEXTURE2DARRAY +%token ITEXTURE2D ITEXTURE3D ITEXTURECUBE ITEXTURE2DARRAY +%token UTEXTURE2D UTEXTURE3D UTEXTURECUBE UTEXTURE2DARRAY + +GLSLANG_WEB_EXCLUDE_ON + +%token ATTRIBUTE VARYING +%token FLOAT16_T FLOAT32_T DOUBLE FLOAT64_T +%token INT64_T UINT64_T INT32_T UINT32_T INT16_T UINT16_T INT8_T UINT8_T +%token I64VEC2 I64VEC3 I64VEC4 +%token U64VEC2 U64VEC3 U64VEC4 +%token I32VEC2 I32VEC3 I32VEC4 +%token U32VEC2 U32VEC3 U32VEC4 +%token I16VEC2 I16VEC3 I16VEC4 +%token U16VEC2 U16VEC3 U16VEC4 +%token I8VEC2 I8VEC3 I8VEC4 +%token U8VEC2 U8VEC3 U8VEC4 +%token DVEC2 DVEC3 DVEC4 DMAT2 DMAT3 DMAT4 +%token F16VEC2 F16VEC3 F16VEC4 F16MAT2 F16MAT3 F16MAT4 +%token F32VEC2 F32VEC3 F32VEC4 F32MAT2 F32MAT3 F32MAT4 +%token F64VEC2 F64VEC3 F64VEC4 F64MAT2 F64MAT3 F64MAT4 +%token DMAT2X2 DMAT2X3 DMAT2X4 +%token DMAT3X2 DMAT3X3 DMAT3X4 +%token DMAT4X2 DMAT4X3 DMAT4X4 +%token F16MAT2X2 F16MAT2X3 F16MAT2X4 +%token F16MAT3X2 F16MAT3X3 F16MAT3X4 +%token F16MAT4X2 F16MAT4X3 F16MAT4X4 +%token F32MAT2X2 F32MAT2X3 F32MAT2X4 +%token F32MAT3X2 F32MAT3X3 F32MAT3X4 +%token F32MAT4X2 F32MAT4X3 F32MAT4X4 +%token F64MAT2X2 F64MAT2X3 F64MAT2X4 +%token F64MAT3X2 F64MAT3X3 F64MAT3X4 +%token F64MAT4X2 F64MAT4X3 F64MAT4X4 +%token ATOMIC_UINT +%token ACCSTRUCTNV +%token ACCSTRUCTEXT +%token RAYQUERYEXT +%token FCOOPMATNV ICOOPMATNV UCOOPMATNV + +// combined image/sampler +%token SAMPLERCUBEARRAY SAMPLERCUBEARRAYSHADOW +%token ISAMPLERCUBEARRAY USAMPLERCUBEARRAY +%token SAMPLER1D SAMPLER1DARRAY SAMPLER1DARRAYSHADOW ISAMPLER1D SAMPLER1DSHADOW +%token SAMPLER2DRECT SAMPLER2DRECTSHADOW ISAMPLER2DRECT USAMPLER2DRECT +%token SAMPLERBUFFER ISAMPLERBUFFER USAMPLERBUFFER +%token SAMPLER2DMS ISAMPLER2DMS USAMPLER2DMS +%token SAMPLER2DMSARRAY ISAMPLER2DMSARRAY USAMPLER2DMSARRAY +%token SAMPLEREXTERNALOES +%token SAMPLEREXTERNAL2DY2YEXT +%token ISAMPLER1DARRAY USAMPLER1D USAMPLER1DARRAY +%token F16SAMPLER1D F16SAMPLER2D F16SAMPLER3D F16SAMPLER2DRECT F16SAMPLERCUBE +%token F16SAMPLER1DARRAY F16SAMPLER2DARRAY F16SAMPLERCUBEARRAY +%token F16SAMPLERBUFFER F16SAMPLER2DMS F16SAMPLER2DMSARRAY +%token F16SAMPLER1DSHADOW F16SAMPLER2DSHADOW F16SAMPLER1DARRAYSHADOW F16SAMPLER2DARRAYSHADOW +%token F16SAMPLER2DRECTSHADOW F16SAMPLERCUBESHADOW F16SAMPLERCUBEARRAYSHADOW + +// images +%token IMAGE1D IIMAGE1D UIMAGE1D IMAGE2D IIMAGE2D +%token UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D +%token IMAGE2DRECT IIMAGE2DRECT UIMAGE2DRECT +%token IMAGECUBE IIMAGECUBE UIMAGECUBE +%token IMAGEBUFFER IIMAGEBUFFER UIMAGEBUFFER +%token IMAGE1DARRAY IIMAGE1DARRAY UIMAGE1DARRAY +%token IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY +%token IMAGECUBEARRAY IIMAGECUBEARRAY UIMAGECUBEARRAY +%token IMAGE2DMS IIMAGE2DMS UIMAGE2DMS +%token IMAGE2DMSARRAY IIMAGE2DMSARRAY UIMAGE2DMSARRAY + +%token F16IMAGE1D F16IMAGE2D F16IMAGE3D F16IMAGE2DRECT +%token F16IMAGECUBE F16IMAGE1DARRAY F16IMAGE2DARRAY F16IMAGECUBEARRAY +%token F16IMAGEBUFFER F16IMAGE2DMS F16IMAGE2DMSARRAY + +%token I64IMAGE1D U64IMAGE1D +%token I64IMAGE2D U64IMAGE2D +%token I64IMAGE3D U64IMAGE3D +%token I64IMAGE2DRECT U64IMAGE2DRECT +%token I64IMAGECUBE U64IMAGECUBE +%token I64IMAGEBUFFER U64IMAGEBUFFER +%token I64IMAGE1DARRAY U64IMAGE1DARRAY +%token I64IMAGE2DARRAY U64IMAGE2DARRAY +%token I64IMAGECUBEARRAY U64IMAGECUBEARRAY +%token I64IMAGE2DMS U64IMAGE2DMS +%token I64IMAGE2DMSARRAY U64IMAGE2DMSARRAY + +// texture without sampler +%token TEXTURECUBEARRAY ITEXTURECUBEARRAY UTEXTURECUBEARRAY +%token TEXTURE1D ITEXTURE1D UTEXTURE1D +%token TEXTURE1DARRAY ITEXTURE1DARRAY UTEXTURE1DARRAY +%token TEXTURE2DRECT ITEXTURE2DRECT UTEXTURE2DRECT +%token TEXTUREBUFFER ITEXTUREBUFFER UTEXTUREBUFFER +%token TEXTURE2DMS ITEXTURE2DMS UTEXTURE2DMS +%token TEXTURE2DMSARRAY ITEXTURE2DMSARRAY UTEXTURE2DMSARRAY + +%token F16TEXTURE1D F16TEXTURE2D F16TEXTURE3D F16TEXTURE2DRECT F16TEXTURECUBE +%token F16TEXTURE1DARRAY F16TEXTURE2DARRAY F16TEXTURECUBEARRAY +%token F16TEXTUREBUFFER F16TEXTURE2DMS F16TEXTURE2DMSARRAY + +// input attachments +%token SUBPASSINPUT SUBPASSINPUTMS ISUBPASSINPUT ISUBPASSINPUTMS USUBPASSINPUT USUBPASSINPUTMS +%token F16SUBPASSINPUT F16SUBPASSINPUTMS + +GLSLANG_WEB_EXCLUDE_OFF + +%token LEFT_OP RIGHT_OP +%token INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP +%token AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN +%token MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN +%token SUB_ASSIGN +%token STRING_LITERAL + +%token LEFT_PAREN RIGHT_PAREN LEFT_BRACKET RIGHT_BRACKET LEFT_BRACE RIGHT_BRACE DOT +%token COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT +%token LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION + +%token INVARIANT +%token HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION +%token PACKED RESOURCE SUPERP + +%token FLOATCONSTANT INTCONSTANT UINTCONSTANT BOOLCONSTANT +%token IDENTIFIER TYPE_NAME +%token CENTROID IN OUT INOUT +%token STRUCT VOID WHILE +%token BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT +%token TERMINATE_INVOCATION +%token UNIFORM SHARED BUFFER +%token FLAT SMOOTH LAYOUT + +GLSLANG_WEB_EXCLUDE_ON +%token DOUBLECONSTANT INT16CONSTANT UINT16CONSTANT FLOAT16CONSTANT INT32CONSTANT UINT32CONSTANT +%token INT64CONSTANT UINT64CONSTANT +%token SUBROUTINE DEMOTE +%token PAYLOADNV PAYLOADINNV HITATTRNV CALLDATANV CALLDATAINNV +%token PAYLOADEXT PAYLOADINEXT HITATTREXT CALLDATAEXT CALLDATAINEXT +%token PATCH SAMPLE NONUNIFORM +%token COHERENT VOLATILE RESTRICT READONLY WRITEONLY DEVICECOHERENT QUEUEFAMILYCOHERENT WORKGROUPCOHERENT +%token SUBGROUPCOHERENT NONPRIVATE SHADERCALLCOHERENT +%token NOPERSPECTIVE EXPLICITINTERPAMD PERVERTEXNV PERPRIMITIVENV PERVIEWNV PERTASKNV +%token PRECISE +GLSLANG_WEB_EXCLUDE_OFF + +%type assignment_operator unary_operator +%type variable_identifier primary_expression postfix_expression +%type expression integer_expression assignment_expression +%type unary_expression multiplicative_expression additive_expression +%type relational_expression equality_expression +%type conditional_expression constant_expression +%type logical_or_expression logical_xor_expression logical_and_expression +%type shift_expression and_expression exclusive_or_expression inclusive_or_expression +%type function_call initializer condition conditionopt + +%type translation_unit function_definition +%type statement simple_statement +%type statement_list switch_statement_list compound_statement +%type declaration_statement selection_statement selection_statement_nonattributed expression_statement +%type switch_statement switch_statement_nonattributed case_label +%type declaration external_declaration +%type for_init_statement compound_statement_no_new_scope +%type selection_rest_statement for_rest_statement +%type iteration_statement iteration_statement_nonattributed jump_statement statement_no_new_scope statement_scoped +%type single_declaration init_declarator_list + +%type parameter_declaration parameter_declarator parameter_type_specifier + +%type array_specifier +%type invariant_qualifier interpolation_qualifier storage_qualifier precision_qualifier +%type layout_qualifier layout_qualifier_id_list layout_qualifier_id + +%type type_parameter_specifier +%type type_parameter_specifier_opt +%type type_parameter_specifier_list + +%type type_qualifier fully_specified_type type_specifier +%type single_type_qualifier +%type type_specifier_nonarray +%type struct_specifier +%type struct_declarator +%type struct_declarator_list struct_declaration struct_declaration_list +%type block_structure +%type function_header function_declarator +%type function_header_with_parameters +%type function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype +%type function_call_or_method function_identifier function_call_header + +%type identifier_list + +GLSLANG_WEB_EXCLUDE_ON +%type precise_qualifier non_uniform_qualifier +%type type_name_list +%type attribute attribute_list single_attribute +%type demote_statement +%type initializer_list +GLSLANG_WEB_EXCLUDE_OFF + +%start translation_unit +%% + +variable_identifier + : IDENTIFIER { + $$ = parseContext.handleVariable($1.loc, $1.symbol, $1.string); + } + ; + +primary_expression + : variable_identifier { + $$ = $1; + } + | LEFT_PAREN expression RIGHT_PAREN { + $$ = $2; + if ($$->getAsConstantUnion()) + $$->getAsConstantUnion()->setExpression(); + } + | FLOATCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat, $1.loc, true); + } + | INTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINTCONSTANT { + parseContext.fullIntegerCheck($1.loc, "unsigned literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | BOOLCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.b, $1.loc, true); + } +GLSLANG_WEB_EXCLUDE_ON + | STRING_LITERAL { + $$ = parseContext.intermediate.addConstantUnion($1.string, $1.loc, true); + } + | INT32CONSTANT { + parseContext.explicitInt32Check($1.loc, "32-bit signed literal"); + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINT32CONSTANT { + parseContext.explicitInt32Check($1.loc, "32-bit signed literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | INT64CONSTANT { + parseContext.int64Check($1.loc, "64-bit integer literal"); + $$ = parseContext.intermediate.addConstantUnion($1.i64, $1.loc, true); + } + | UINT64CONSTANT { + parseContext.int64Check($1.loc, "64-bit unsigned integer literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u64, $1.loc, true); + } + | INT16CONSTANT { + parseContext.explicitInt16Check($1.loc, "16-bit integer literal"); + $$ = parseContext.intermediate.addConstantUnion((short)$1.i, $1.loc, true); + } + | UINT16CONSTANT { + parseContext.explicitInt16Check($1.loc, "16-bit unsigned integer literal"); + $$ = parseContext.intermediate.addConstantUnion((unsigned short)$1.u, $1.loc, true); + } + | DOUBLECONSTANT { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double literal"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double literal"); + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtDouble, $1.loc, true); + } + | FLOAT16CONSTANT { + parseContext.float16Check($1.loc, "half float literal"); + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat16, $1.loc, true); + } +GLSLANG_WEB_EXCLUDE_OFF + ; + +postfix_expression + : primary_expression { + $$ = $1; + } + | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET { + $$ = parseContext.handleBracketDereference($2.loc, $1, $3); + } + | function_call { + $$ = $1; + } + | postfix_expression DOT IDENTIFIER { + $$ = parseContext.handleDotDereference($3.loc, $1, *$3.string); + } + | postfix_expression INC_OP { + parseContext.variableCheck($1); + parseContext.lValueErrorCheck($2.loc, "++", $1); + $$ = parseContext.handleUnaryMath($2.loc, "++", EOpPostIncrement, $1); + } + | postfix_expression DEC_OP { + parseContext.variableCheck($1); + parseContext.lValueErrorCheck($2.loc, "--", $1); + $$ = parseContext.handleUnaryMath($2.loc, "--", EOpPostDecrement, $1); + } + ; + +integer_expression + : expression { + parseContext.integerCheck($1, "[]"); + $$ = $1; + } + ; + +function_call + : function_call_or_method { + $$ = parseContext.handleFunctionCall($1.loc, $1.function, $1.intermNode); + delete $1.function; + } + ; + +function_call_or_method + : function_call_generic { + $$ = $1; + } + ; + +function_call_generic + : function_call_header_with_parameters RIGHT_PAREN { + $$ = $1; + $$.loc = $2.loc; + } + | function_call_header_no_parameters RIGHT_PAREN { + $$ = $1; + $$.loc = $2.loc; + } + ; + +function_call_header_no_parameters + : function_call_header VOID { + $$ = $1; + } + | function_call_header { + $$ = $1; + } + ; + +function_call_header_with_parameters + : function_call_header assignment_expression { + TParameter param = { 0, new TType }; + param.type->shallowCopy($2->getType()); + $1.function->addParameter(param); + $$.function = $1.function; + $$.intermNode = $2; + } + | function_call_header_with_parameters COMMA assignment_expression { + TParameter param = { 0, new TType }; + param.type->shallowCopy($3->getType()); + $1.function->addParameter(param); + $$.function = $1.function; + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, $3, $2.loc); + } + ; + +function_call_header + : function_identifier LEFT_PAREN { + $$ = $1; + } + ; + +// Grammar Note: Constructors look like functions, but are recognized as types. + +function_identifier + : type_specifier { + // Constructor + $$.intermNode = 0; + $$.function = parseContext.handleConstructorCall($1.loc, $1); + } + | postfix_expression { + // + // Should be a method or subroutine call, but we haven't recognized the arguments yet. + // + $$.function = 0; + $$.intermNode = 0; + + TIntermMethod* method = $1->getAsMethodNode(); + if (method) { + $$.function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength); + $$.intermNode = method->getObject(); + } else { + TIntermSymbol* symbol = $1->getAsSymbolNode(); + if (symbol) { + parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName()); + TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid)); + $$.function = function; + } else + parseContext.error($1->getLoc(), "function call, method, or subroutine call expected", "", ""); + } + + if ($$.function == 0) { + // error recover + TString* empty = NewPoolTString(""); + $$.function = new TFunction(empty, TType(EbtVoid), EOpNull); + } + } +GLSLANG_WEB_EXCLUDE_ON + | non_uniform_qualifier { + // Constructor + $$.intermNode = 0; + $$.function = parseContext.handleConstructorCall($1.loc, $1); + } +GLSLANG_WEB_EXCLUDE_OFF + ; + +unary_expression + : postfix_expression { + parseContext.variableCheck($1); + $$ = $1; + if (TIntermMethod* method = $1->getAsMethodNode()) + parseContext.error($1->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), ""); + } + | INC_OP unary_expression { + parseContext.lValueErrorCheck($1.loc, "++", $2); + $$ = parseContext.handleUnaryMath($1.loc, "++", EOpPreIncrement, $2); + } + | DEC_OP unary_expression { + parseContext.lValueErrorCheck($1.loc, "--", $2); + $$ = parseContext.handleUnaryMath($1.loc, "--", EOpPreDecrement, $2); + } + | unary_operator unary_expression { + if ($1.op != EOpNull) { + char errorOp[2] = {0, 0}; + switch($1.op) { + case EOpNegative: errorOp[0] = '-'; break; + case EOpLogicalNot: errorOp[0] = '!'; break; + case EOpBitwiseNot: errorOp[0] = '~'; break; + default: break; // some compilers want this + } + $$ = parseContext.handleUnaryMath($1.loc, errorOp, $1.op, $2); + } else { + $$ = $2; + if ($$->getAsConstantUnion()) + $$->getAsConstantUnion()->setExpression(); + } + } + ; +// Grammar Note: No traditional style type casts. + +unary_operator + : PLUS { $$.loc = $1.loc; $$.op = EOpNull; } + | DASH { $$.loc = $1.loc; $$.op = EOpNegative; } + | BANG { $$.loc = $1.loc; $$.op = EOpLogicalNot; } + | TILDE { $$.loc = $1.loc; $$.op = EOpBitwiseNot; + parseContext.fullIntegerCheck($1.loc, "bitwise not"); } + ; +// Grammar Note: No '*' or '&' unary ops. Pointers are not supported. + +multiplicative_expression + : unary_expression { $$ = $1; } + | multiplicative_expression STAR unary_expression { + $$ = parseContext.handleBinaryMath($2.loc, "*", EOpMul, $1, $3); + if ($$ == 0) + $$ = $1; + } + | multiplicative_expression SLASH unary_expression { + $$ = parseContext.handleBinaryMath($2.loc, "/", EOpDiv, $1, $3); + if ($$ == 0) + $$ = $1; + } + | multiplicative_expression PERCENT unary_expression { + parseContext.fullIntegerCheck($2.loc, "%"); + $$ = parseContext.handleBinaryMath($2.loc, "%", EOpMod, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +additive_expression + : multiplicative_expression { $$ = $1; } + | additive_expression PLUS multiplicative_expression { + $$ = parseContext.handleBinaryMath($2.loc, "+", EOpAdd, $1, $3); + if ($$ == 0) + $$ = $1; + } + | additive_expression DASH multiplicative_expression { + $$ = parseContext.handleBinaryMath($2.loc, "-", EOpSub, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +shift_expression + : additive_expression { $$ = $1; } + | shift_expression LEFT_OP additive_expression { + parseContext.fullIntegerCheck($2.loc, "bit shift left"); + $$ = parseContext.handleBinaryMath($2.loc, "<<", EOpLeftShift, $1, $3); + if ($$ == 0) + $$ = $1; + } + | shift_expression RIGHT_OP additive_expression { + parseContext.fullIntegerCheck($2.loc, "bit shift right"); + $$ = parseContext.handleBinaryMath($2.loc, ">>", EOpRightShift, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +relational_expression + : shift_expression { $$ = $1; } + | relational_expression LEFT_ANGLE shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, "<", EOpLessThan, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression RIGHT_ANGLE shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, ">", EOpGreaterThan, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression LE_OP shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, "<=", EOpLessThanEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression GE_OP shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, ">=", EOpGreaterThanEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +equality_expression + : relational_expression { $$ = $1; } + | equality_expression EQ_OP relational_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison"); + parseContext.opaqueCheck($2.loc, $1->getType(), "=="); + parseContext.specializationCheck($2.loc, $1->getType(), "=="); + parseContext.referenceCheck($2.loc, $1->getType(), "=="); + $$ = parseContext.handleBinaryMath($2.loc, "==", EOpEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | equality_expression NE_OP relational_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison"); + parseContext.opaqueCheck($2.loc, $1->getType(), "!="); + parseContext.specializationCheck($2.loc, $1->getType(), "!="); + parseContext.referenceCheck($2.loc, $1->getType(), "!="); + $$ = parseContext.handleBinaryMath($2.loc, "!=", EOpNotEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +and_expression + : equality_expression { $$ = $1; } + | and_expression AMPERSAND equality_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise and"); + $$ = parseContext.handleBinaryMath($2.loc, "&", EOpAnd, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +exclusive_or_expression + : and_expression { $$ = $1; } + | exclusive_or_expression CARET and_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise exclusive or"); + $$ = parseContext.handleBinaryMath($2.loc, "^", EOpExclusiveOr, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +inclusive_or_expression + : exclusive_or_expression { $$ = $1; } + | inclusive_or_expression VERTICAL_BAR exclusive_or_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise inclusive or"); + $$ = parseContext.handleBinaryMath($2.loc, "|", EOpInclusiveOr, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +logical_and_expression + : inclusive_or_expression { $$ = $1; } + | logical_and_expression AND_OP inclusive_or_expression { + $$ = parseContext.handleBinaryMath($2.loc, "&&", EOpLogicalAnd, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +logical_xor_expression + : logical_and_expression { $$ = $1; } + | logical_xor_expression XOR_OP logical_and_expression { + $$ = parseContext.handleBinaryMath($2.loc, "^^", EOpLogicalXor, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +logical_or_expression + : logical_xor_expression { $$ = $1; } + | logical_or_expression OR_OP logical_xor_expression { + $$ = parseContext.handleBinaryMath($2.loc, "||", EOpLogicalOr, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +conditional_expression + : logical_or_expression { $$ = $1; } + | logical_or_expression QUESTION { + ++parseContext.controlFlowNestingLevel; + } + expression COLON assignment_expression { + --parseContext.controlFlowNestingLevel; + parseContext.boolCheck($2.loc, $1); + parseContext.rValueErrorCheck($2.loc, "?", $1); + parseContext.rValueErrorCheck($5.loc, ":", $4); + parseContext.rValueErrorCheck($5.loc, ":", $6); + $$ = parseContext.intermediate.addSelection($1, $4, $6, $2.loc); + if ($$ == 0) { + parseContext.binaryOpError($2.loc, ":", $4->getCompleteString(), $6->getCompleteString()); + $$ = $6; + } + } + ; + +assignment_expression + : conditional_expression { $$ = $1; } + | unary_expression assignment_operator assignment_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array assignment"); + parseContext.opaqueCheck($2.loc, $1->getType(), "="); + parseContext.storage16BitAssignmentCheck($2.loc, $1->getType(), "="); + parseContext.specializationCheck($2.loc, $1->getType(), "="); + parseContext.lValueErrorCheck($2.loc, "assign", $1); + parseContext.rValueErrorCheck($2.loc, "assign", $3); + $$ = parseContext.addAssign($2.loc, $2.op, $1, $3); + if ($$ == 0) { + parseContext.assignError($2.loc, "assign", $1->getCompleteString(), $3->getCompleteString()); + $$ = $1; + } + } + ; + +assignment_operator + : EQUAL { + $$.loc = $1.loc; + $$.op = EOpAssign; + } + | MUL_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpMulAssign; + } + | DIV_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpDivAssign; + } + | MOD_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "%="); + $$.loc = $1.loc; + $$.op = EOpModAssign; + } + | ADD_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpAddAssign; + } + | SUB_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpSubAssign; + } + | LEFT_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bit-shift left assign"); + $$.loc = $1.loc; $$.op = EOpLeftShiftAssign; + } + | RIGHT_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bit-shift right assign"); + $$.loc = $1.loc; $$.op = EOpRightShiftAssign; + } + | AND_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-and assign"); + $$.loc = $1.loc; $$.op = EOpAndAssign; + } + | XOR_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-xor assign"); + $$.loc = $1.loc; $$.op = EOpExclusiveOrAssign; + } + | OR_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-or assign"); + $$.loc = $1.loc; $$.op = EOpInclusiveOrAssign; + } + ; + +expression + : assignment_expression { + $$ = $1; + } + | expression COMMA assignment_expression { + parseContext.samplerConstructorLocationCheck($2.loc, ",", $3); + $$ = parseContext.intermediate.addComma($1, $3, $2.loc); + if ($$ == 0) { + parseContext.binaryOpError($2.loc, ",", $1->getCompleteString(), $3->getCompleteString()); + $$ = $3; + } + } + ; + +constant_expression + : conditional_expression { + parseContext.constantValueCheck($1, ""); + $$ = $1; + } + ; + +declaration + : function_prototype SEMICOLON { + parseContext.handleFunctionDeclarator($1.loc, *$1.function, true /* prototype */); + $$ = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } + | init_declarator_list SEMICOLON { + if ($1.intermNode && $1.intermNode->getAsAggregate()) + $1.intermNode->getAsAggregate()->setOperator(EOpSequence); + $$ = $1.intermNode; + } + | PRECISION precision_qualifier type_specifier SEMICOLON { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "precision statement"); + // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope + parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]); + parseContext.setDefaultPrecision($1.loc, $3, $2.qualifier.precision); + $$ = 0; + } + | block_structure SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList); + $$ = 0; + } + | block_structure IDENTIFIER SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList, $2.string); + $$ = 0; + } + | block_structure IDENTIFIER array_specifier SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList, $2.string, $3.arraySizes); + $$ = 0; + } + | type_qualifier SEMICOLON { + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.updateStandaloneQualifierDefaults($1.loc, $1); + $$ = 0; + } + | type_qualifier IDENTIFIER SEMICOLON { + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$2.string); + $$ = 0; + } + | type_qualifier IDENTIFIER identifier_list SEMICOLON { + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + $3->push_back($2.string); + parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$3); + $$ = 0; + } + ; + +block_structure + : type_qualifier IDENTIFIER LEFT_BRACE { parseContext.nestedBlockCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + --parseContext.blockNestingLevel; + parseContext.blockName = $2.string; + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.currentBlockQualifier = $1.qualifier; + $$.loc = $1.loc; + $$.typeList = $5; + } + +identifier_list + : COMMA IDENTIFIER { + $$ = new TIdentifierList; + $$->push_back($2.string); + } + | identifier_list COMMA IDENTIFIER { + $$ = $1; + $$->push_back($3.string); + } + ; + +function_prototype + : function_declarator RIGHT_PAREN { + $$.function = $1; + $$.loc = $2.loc; + } + ; + +function_declarator + : function_header { + $$ = $1; + } + | function_header_with_parameters { + $$ = $1; + } + ; + + +function_header_with_parameters + : function_header parameter_declaration { + // Add the parameter + $$ = $1; + if ($2.param.type->getBasicType() != EbtVoid) + $1->addParameter($2.param); + else + delete $2.param.type; + } + | function_header_with_parameters COMMA parameter_declaration { + // + // Only first parameter of one-parameter functions can be void + // The check for named parameters not being void is done in parameter_declarator + // + if ($3.param.type->getBasicType() == EbtVoid) { + // + // This parameter > first is void + // + parseContext.error($2.loc, "cannot be an argument type except for '(void)'", "void", ""); + delete $3.param.type; + } else { + // Add the parameter + $$ = $1; + $1->addParameter($3.param); + } + } + ; + +function_header + : fully_specified_type IDENTIFIER LEFT_PAREN { + if ($1.qualifier.storage != EvqGlobal && $1.qualifier.storage != EvqTemporary) { + parseContext.error($2.loc, "no qualifiers allowed for function return", + GetStorageQualifierString($1.qualifier.storage), ""); + } + if ($1.arraySizes) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + + // Add the function as a prototype after parsing it (we do not support recursion) + TFunction *function; + TType type($1); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction($2.string); + + // Make the function + function = new TFunction($2.string, type); + $$ = function; + } + ; + +parameter_declarator + // Type + name + : type_specifier IDENTIFIER { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + if ($1.basicType == EbtVoid) { + parseContext.error($2.loc, "illegal use of type 'void'", $2.string->c_str(), ""); + } + parseContext.reservedErrorCheck($2.loc, *$2.string); + + TParameter param = {$2.string, new TType($1)}; + $$.loc = $2.loc; + $$.param = param; + } + | type_specifier IDENTIFIER array_specifier { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + TType* type = new TType($1); + type->transferArraySizes($3.arraySizes); + type->copyArrayInnerSizes($1.arraySizes); + + parseContext.arrayOfArrayVersionCheck($2.loc, type->getArraySizes()); + parseContext.arraySizeRequiredCheck($3.loc, *$3.arraySizes); + parseContext.reservedErrorCheck($2.loc, *$2.string); + + TParameter param = { $2.string, type }; + + $$.loc = $2.loc; + $$.param = param; + } + ; + +parameter_declaration + // + // With name + // + : type_qualifier parameter_declarator { + $$ = $2; + if ($1.qualifier.precision != EpqNone) + $$.param.type->getQualifier().precision = $1.qualifier.precision; + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type); + parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type); + + } + | parameter_declarator { + $$ = $1; + + parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type); + parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type); + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + } + // + // Without name + // + | type_qualifier parameter_type_specifier { + $$ = $2; + if ($1.qualifier.precision != EpqNone) + $$.param.type->getQualifier().precision = $1.qualifier.precision; + parseContext.precisionQualifierCheck($1.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type); + parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type); + } + | parameter_type_specifier { + $$ = $1; + + parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type); + parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type); + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + } + ; + +parameter_type_specifier + : type_specifier { + TParameter param = { 0, new TType($1) }; + $$.param = param; + if ($1.arraySizes) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + ; + +init_declarator_list + : single_declaration { + $$ = $1; + } + | init_declarator_list COMMA IDENTIFIER { + $$ = $1; + parseContext.declareVariable($3.loc, *$3.string, $1.type); + } + | init_declarator_list COMMA IDENTIFIER array_specifier { + $$ = $1; + parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes); + } + | init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer { + $$.type = $1.type; + TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes, $6); + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $5.loc); + } + | init_declarator_list COMMA IDENTIFIER EQUAL initializer { + $$.type = $1.type; + TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, 0, $5); + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $4.loc); + } + ; + +single_declaration + : fully_specified_type { + $$.type = $1; + $$.intermNode = 0; +GLSLANG_WEB_EXCLUDE_ON + parseContext.declareTypeDefaults($$.loc, $$.type); +GLSLANG_WEB_EXCLUDE_OFF + } + | fully_specified_type IDENTIFIER { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareVariable($2.loc, *$2.string, $1); + } + | fully_specified_type IDENTIFIER array_specifier { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes); + } + | fully_specified_type IDENTIFIER array_specifier EQUAL initializer { + $$.type = $1; + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes, $5); + $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $4.loc); + } + | fully_specified_type IDENTIFIER EQUAL initializer { + $$.type = $1; + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4); + $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $3.loc); + } + +// Grammar Note: No 'enum', or 'typedef'. + +fully_specified_type + : type_specifier { + $$ = $1; + + parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $$); + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + } + parseContext.precisionQualifierCheck($$.loc, $$.basicType, $$.qualifier); + } + | type_qualifier type_specifier { + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $2); + + if ($2.arraySizes) { + parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type"); + } + + if ($2.arraySizes && parseContext.arrayQualifierError($2.loc, $1.qualifier)) + $2.arraySizes = nullptr; + + parseContext.checkNoShaderLayouts($2.loc, $1.shaderQualifiers); + $2.shaderQualifiers.merge($1.shaderQualifiers); + parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true); + parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier); + + $$ = $2; + + if (! $$.qualifier.isInterpolation() && + ((parseContext.language == EShLangVertex && $$.qualifier.storage == EvqVaryingOut) || + (parseContext.language == EShLangFragment && $$.qualifier.storage == EvqVaryingIn))) + $$.qualifier.smooth = true; + } + ; + +invariant_qualifier + : INVARIANT { + parseContext.globalCheck($1.loc, "invariant"); + parseContext.profileRequires($$.loc, ENoProfile, 120, 0, "invariant"); + $$.init($1.loc); + $$.qualifier.invariant = true; + } + ; + +interpolation_qualifier + : SMOOTH { + parseContext.globalCheck($1.loc, "smooth"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "smooth"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "smooth"); + $$.init($1.loc); + $$.qualifier.smooth = true; + } + | FLAT { + parseContext.globalCheck($1.loc, "flat"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "flat"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "flat"); + $$.init($1.loc); + $$.qualifier.flat = true; + } +GLSLANG_WEB_EXCLUDE_ON + | NOPERSPECTIVE { + parseContext.globalCheck($1.loc, "noperspective"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "noperspective"); + $$.init($1.loc); + $$.qualifier.nopersp = true; + } + | EXPLICITINTERPAMD { + parseContext.globalCheck($1.loc, "__explicitInterpAMD"); + parseContext.profileRequires($1.loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + $$.init($1.loc); + $$.qualifier.explicitInterp = true; + } + | PERVERTEXNV { + parseContext.globalCheck($1.loc, "pervertexNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + $$.init($1.loc); + $$.qualifier.pervertexNV = true; + } + | PERPRIMITIVENV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perprimitiveNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshNVMask), "perprimitiveNV"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions($1.loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV"); + $$.init($1.loc); + $$.qualifier.perPrimitiveNV = true; + } + | PERVIEWNV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perviewNV"); + parseContext.requireStage($1.loc, EShLangMeshNV, "perviewNV"); + $$.init($1.loc); + $$.qualifier.perViewNV = true; + } + | PERTASKNV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "taskNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask), "taskNV"); + $$.init($1.loc); + $$.qualifier.perTaskNV = true; + } +GLSLANG_WEB_EXCLUDE_OFF + ; + +layout_qualifier + : LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN { + $$ = $3; + } + ; + +layout_qualifier_id_list + : layout_qualifier_id { + $$ = $1; + } + | layout_qualifier_id_list COMMA layout_qualifier_id { + $$ = $1; + $$.shaderQualifiers.merge($3.shaderQualifiers); + parseContext.mergeObjectLayoutQualifiers($$.qualifier, $3.qualifier, false); + } + +layout_qualifier_id + : IDENTIFIER { + $$.init($1.loc); + parseContext.setLayoutQualifier($1.loc, $$, *$1.string); + } + | IDENTIFIER EQUAL constant_expression { + $$.init($1.loc); + parseContext.setLayoutQualifier($1.loc, $$, *$1.string, $3); + } + | SHARED { // because "shared" is both an identifier and a keyword + $$.init($1.loc); + TString strShared("shared"); + parseContext.setLayoutQualifier($1.loc, $$, strShared); + } + ; + +GLSLANG_WEB_EXCLUDE_ON +precise_qualifier + : PRECISE { + parseContext.profileRequires($$.loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise"); + parseContext.profileRequires($1.loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise"); + $$.init($1.loc); + $$.qualifier.noContraction = true; + } + ; +GLSLANG_WEB_EXCLUDE_OFF + +type_qualifier + : single_type_qualifier { + $$ = $1; + } + | type_qualifier single_type_qualifier { + $$ = $1; + if ($$.basicType == EbtVoid) + $$.basicType = $2.basicType; + + $$.shaderQualifiers.merge($2.shaderQualifiers); + parseContext.mergeQualifiers($$.loc, $$.qualifier, $2.qualifier, false); + } + ; + +single_type_qualifier + : storage_qualifier { + $$ = $1; + } + | layout_qualifier { + $$ = $1; + } + | precision_qualifier { + parseContext.checkPrecisionQualifier($1.loc, $1.qualifier.precision); + $$ = $1; + } + | interpolation_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | invariant_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } +GLSLANG_WEB_EXCLUDE_ON + | precise_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | non_uniform_qualifier { + $$ = $1; + } +GLSLANG_WEB_EXCLUDE_OFF + ; + +storage_qualifier + : CONST { + $$.init($1.loc); + $$.qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant + } + | INOUT { + parseContext.globalCheck($1.loc, "inout"); + $$.init($1.loc); + $$.qualifier.storage = EvqInOut; + } + | IN { + parseContext.globalCheck($1.loc, "in"); + $$.init($1.loc); + // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later + $$.qualifier.storage = EvqIn; + } + | OUT { + parseContext.globalCheck($1.loc, "out"); + $$.init($1.loc); + // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later + $$.qualifier.storage = EvqOut; + } + | CENTROID { + parseContext.profileRequires($1.loc, ENoProfile, 120, 0, "centroid"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "centroid"); + parseContext.globalCheck($1.loc, "centroid"); + $$.init($1.loc); + $$.qualifier.centroid = true; + } + | UNIFORM { + parseContext.globalCheck($1.loc, "uniform"); + $$.init($1.loc); + $$.qualifier.storage = EvqUniform; + } + | SHARED { + parseContext.globalCheck($1.loc, "shared"); + parseContext.profileRequires($1.loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared"); + parseContext.profileRequires($1.loc, EEsProfile, 310, 0, "shared"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshNVMask | EShLangTaskNVMask), "shared"); + $$.init($1.loc); + $$.qualifier.storage = EvqShared; + } + | BUFFER { + parseContext.globalCheck($1.loc, "buffer"); + $$.init($1.loc); + $$.qualifier.storage = EvqBuffer; + } +GLSLANG_WEB_EXCLUDE_ON + | ATTRIBUTE { + parseContext.requireStage($1.loc, EShLangVertex, "attribute"); + parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "attribute"); + parseContext.checkDeprecated($1.loc, ENoProfile, 130, "attribute"); + parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "attribute"); + parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "attribute"); + + parseContext.globalCheck($1.loc, "attribute"); + + $$.init($1.loc); + $$.qualifier.storage = EvqVaryingIn; + } + | VARYING { + parseContext.checkDeprecated($1.loc, ENoProfile, 130, "varying"); + parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "varying"); + parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "varying"); + parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "varying"); + + parseContext.globalCheck($1.loc, "varying"); + + $$.init($1.loc); + if (parseContext.language == EShLangVertex) + $$.qualifier.storage = EvqVaryingOut; + else + $$.qualifier.storage = EvqVaryingIn; + } + | PATCH { + parseContext.globalCheck($1.loc, "patch"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch"); + $$.init($1.loc); + $$.qualifier.patch = true; + } + | SAMPLE { + parseContext.globalCheck($1.loc, "sample"); + $$.init($1.loc); + $$.qualifier.sample = true; + } + | HITATTRNV { + parseContext.globalCheck($1.loc, "hitAttributeNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitAttr; + } + | HITATTREXT { + parseContext.globalCheck($1.loc, "hitAttributeEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "hitAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitAttr; + } + | PAYLOADNV { + parseContext.globalCheck($1.loc, "rayPayloadNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayload; + } + | PAYLOADEXT { + parseContext.globalCheck($1.loc, "rayPayloadEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayload; + } + | PAYLOADINNV { + parseContext.globalCheck($1.loc, "rayPayloadInNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayloadIn; + } + | PAYLOADINEXT { + parseContext.globalCheck($1.loc, "rayPayloadInEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadInEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayloadIn; + } + | CALLDATANV { + parseContext.globalCheck($1.loc, "callableDataNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableData; + } + | CALLDATAEXT { + parseContext.globalCheck($1.loc, "callableDataEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableData; + } + | CALLDATAINNV { + parseContext.globalCheck($1.loc, "callableDataInNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableDataIn; + } + | CALLDATAINEXT { + parseContext.globalCheck($1.loc, "callableDataInEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataInEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableDataIn; + } + | COHERENT { + $$.init($1.loc); + $$.qualifier.coherent = true; + } + | DEVICECOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent"); + $$.qualifier.devicecoherent = true; + } + | QUEUEFAMILYCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent"); + $$.qualifier.queuefamilycoherent = true; + } + | WORKGROUPCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent"); + $$.qualifier.workgroupcoherent = true; + } + | SUBGROUPCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent"); + $$.qualifier.subgroupcoherent = true; + } + | NONPRIVATE { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate"); + $$.qualifier.nonprivate = true; + } + | SHADERCALLCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_ray_tracing, "shadercallcoherent"); + $$.qualifier.shadercallcoherent = true; + } + | VOLATILE { + $$.init($1.loc); + $$.qualifier.volatil = true; + } + | RESTRICT { + $$.init($1.loc); + $$.qualifier.restrict = true; + } + | READONLY { + $$.init($1.loc); + $$.qualifier.readonly = true; + } + | WRITEONLY { + $$.init($1.loc); + $$.qualifier.writeonly = true; + } + | SUBROUTINE { + parseContext.spvRemoved($1.loc, "subroutine"); + parseContext.globalCheck($1.loc, "subroutine"); + parseContext.unimplemented($1.loc, "subroutine"); + $$.init($1.loc); + } + | SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN { + parseContext.spvRemoved($1.loc, "subroutine"); + parseContext.globalCheck($1.loc, "subroutine"); + parseContext.unimplemented($1.loc, "subroutine"); + $$.init($1.loc); + } +GLSLANG_WEB_EXCLUDE_OFF + ; + +GLSLANG_WEB_EXCLUDE_ON +non_uniform_qualifier + : NONUNIFORM { + $$.init($1.loc); + $$.qualifier.nonUniform = true; + } + ; + +type_name_list + : IDENTIFIER { + // TODO + } + | type_name_list COMMA IDENTIFIER { + // TODO: 4.0 semantics: subroutines + // 1) make sure each identifier is a type declared earlier with SUBROUTINE + // 2) save all of the identifiers for future comparison with the declared function + } + ; +GLSLANG_WEB_EXCLUDE_OFF + +type_specifier + : type_specifier_nonarray type_parameter_specifier_opt { + $$ = $1; + $$.qualifier.precision = parseContext.getDefaultPrecision($$); + $$.typeParameters = $2; + } + | type_specifier_nonarray type_parameter_specifier_opt array_specifier { + parseContext.arrayOfArrayVersionCheck($3.loc, $3.arraySizes); + $$ = $1; + $$.qualifier.precision = parseContext.getDefaultPrecision($$); + $$.typeParameters = $2; + $$.arraySizes = $3.arraySizes; + } + ; + +array_specifier + : LEFT_BRACKET RIGHT_BRACKET { + $$.loc = $1.loc; + $$.arraySizes = new TArraySizes; + $$.arraySizes->addInnerSize(); + } + | LEFT_BRACKET conditional_expression RIGHT_BRACKET { + $$.loc = $1.loc; + $$.arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck($2->getLoc(), $2, size, "array size"); + $$.arraySizes->addInnerSize(size); + } + | array_specifier LEFT_BRACKET RIGHT_BRACKET { + $$ = $1; + $$.arraySizes->addInnerSize(); + } + | array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET { + $$ = $1; + + TArraySize size; + parseContext.arraySizeCheck($3->getLoc(), $3, size, "array size"); + $$.arraySizes->addInnerSize(size); + } + ; + +type_parameter_specifier_opt + : type_parameter_specifier { + $$ = $1; + } + | /* May be null */ { + $$ = 0; + } + ; + +type_parameter_specifier + : LEFT_ANGLE type_parameter_specifier_list RIGHT_ANGLE { + $$ = $2; + } + ; + +type_parameter_specifier_list + : unary_expression { + $$ = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck($1->getLoc(), $1, size, "type parameter"); + $$->addInnerSize(size); + } + | type_parameter_specifier_list COMMA unary_expression { + $$ = $1; + + TArraySize size; + parseContext.arraySizeCheck($3->getLoc(), $3, size, "type parameter"); + $$->addInnerSize(size); + } + ; + +type_specifier_nonarray + : VOID { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtVoid; + } + | FLOAT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + } + | INT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + } + | UINT { + parseContext.fullIntegerCheck($1.loc, "unsigned integer"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + } + | BOOL { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + } + | VEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(2); + } + | VEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(3); + } + | VEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(4); + } + | BVEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(2); + } + | BVEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(3); + } + | BVEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(4); + } + | IVEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(2); + } + | IVEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(3); + } + | IVEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(4); + } + | UVEC2 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(2); + } + | UVEC3 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(3); + } + | UVEC4 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(4); + } + | MAT2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | MAT3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | MAT4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | MAT2X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | MAT2X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 3); + } + | MAT2X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 4); + } + | MAT3X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 2); + } + | MAT3X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | MAT3X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 4); + } + | MAT4X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 2); + } + | MAT4X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 3); + } + | MAT4X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } +GLSLANG_WEB_EXCLUDE_ON + | DOUBLE { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + } + | FLOAT16_T { + parseContext.float16ScalarVectorCheck($1.loc, "float16_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + } + | FLOAT32_T { + parseContext.explicitFloat32Check($1.loc, "float32_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + } + | FLOAT64_T { + parseContext.explicitFloat64Check($1.loc, "float64_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + } + | INT8_T { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + } + | UINT8_T { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + } + | INT16_T { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + } + | UINT16_T { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + } + | INT32_T { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + } + | UINT32_T { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + } + | INT64_T { + parseContext.int64Check($1.loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + } + | UINT64_T { + parseContext.int64Check($1.loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + } + | DVEC2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(2); + } + | DVEC3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(3); + } + | DVEC4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(4); + } + | F16VEC2 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(2); + } + | F16VEC3 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(3); + } + | F16VEC4 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(4); + } + | F32VEC2 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(2); + } + | F32VEC3 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(3); + } + | F32VEC4 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(4); + } + | F64VEC2 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(2); + } + | F64VEC3 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(3); + } + | F64VEC4 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(4); + } + | I8VEC2 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(2); + } + | I8VEC3 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(3); + } + | I8VEC4 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(4); + } + | I16VEC2 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(2); + } + | I16VEC3 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(3); + } + | I16VEC4 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(4); + } + | I32VEC2 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(2); + } + | I32VEC3 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(3); + } + | I32VEC4 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(4); + } + | I64VEC2 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(2); + } + | I64VEC3 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(3); + } + | I64VEC4 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(4); + } + | U8VEC2 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(2); + } + | U8VEC3 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(3); + } + | U8VEC4 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(4); + } + | U16VEC2 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(2); + } + | U16VEC3 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(3); + } + | U16VEC4 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(4); + } + | U32VEC2 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(2); + } + | U32VEC3 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(3); + } + | U32VEC4 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(4); + } + | U64VEC2 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(2); + } + | U64VEC3 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(3); + } + | U64VEC4 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(4); + } + | DMAT2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | DMAT3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | DMAT4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | DMAT2X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | DMAT2X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 3); + } + | DMAT2X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 4); + } + | DMAT3X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 2); + } + | DMAT3X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | DMAT3X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 4); + } + | DMAT4X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 2); + } + | DMAT4X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 3); + } + | DMAT4X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | F16MAT2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 2); + } + | F16MAT3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 3); + } + | F16MAT4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 4); + } + | F16MAT2X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 2); + } + | F16MAT2X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 3); + } + | F16MAT2X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 4); + } + | F16MAT3X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 2); + } + | F16MAT3X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 3); + } + | F16MAT3X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 4); + } + | F16MAT4X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 2); + } + | F16MAT4X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 3); + } + | F16MAT4X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 4); + } + | F32MAT2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | F32MAT3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | F32MAT4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | F32MAT2X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | F32MAT2X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 3); + } + | F32MAT2X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 4); + } + | F32MAT3X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 2); + } + | F32MAT3X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | F32MAT3X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 4); + } + | F32MAT4X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 2); + } + | F32MAT4X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 3); + } + | F32MAT4X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | F64MAT2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | F64MAT3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | F64MAT4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | F64MAT2X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | F64MAT2X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 3); + } + | F64MAT2X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 4); + } + | F64MAT3X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 2); + } + | F64MAT3X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | F64MAT3X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 4); + } + | F64MAT4X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 2); + } + | F64MAT4X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 3); + } + | F64MAT4X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | ACCSTRUCTNV { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAccStruct; + } + | ACCSTRUCTEXT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAccStruct; + } + | RAYQUERYEXT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtRayQuery; + } + | ATOMIC_UINT { + parseContext.vulkanRemoved($1.loc, "atomic counter types"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAtomicUint; + } + | SAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D); + } +GLSLANG_WEB_EXCLUDE_OFF + | SAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + } + | SAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd3D); + } + | SAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube); + } + | SAMPLER2DSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, false, true); + } + | SAMPLERCUBESHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, false, true); + } + | SAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true); + } + | SAMPLER2DARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true, true); + } +GLSLANG_WEB_EXCLUDE_ON + | SAMPLER1DSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, false, true); + } + | SAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, true); + } + | SAMPLER1DARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, true, true); + } + | SAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, true); + } + | SAMPLERCUBEARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, true, true); + } + | F16SAMPLER1D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D); + } + | F16SAMPLER2D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D); + } + | F16SAMPLER3D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd3D); + } + | F16SAMPLERCUBE { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube); + } + | F16SAMPLER1DSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, false, true); + } + | F16SAMPLER2DSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, false, true); + } + | F16SAMPLERCUBESHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, false, true); + } + | F16SAMPLER1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, true); + } + | F16SAMPLER2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true); + } + | F16SAMPLER1DARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, true, true); + } + | F16SAMPLER2DARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true, true); + } + | F16SAMPLERCUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, true); + } + | F16SAMPLERCUBEARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, true, true); + } + | ISAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd1D); + } +GLSLANG_WEB_EXCLUDE_OFF + | ISAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D); + } + | ISAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd3D); + } + | ISAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdCube); + } + | ISAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, true); + } + | USAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D); + } + | USAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd3D); + } + | USAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdCube); + } +GLSLANG_WEB_EXCLUDE_ON + | ISAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd1D, true); + } + | ISAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdCube, true); + } + | USAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd1D); + } + | USAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd1D, true); + } + | USAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdCube, true); + } + | TEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdCube, true); + } + | ITEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdCube, true); + } + | UTEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdCube, true); + } +GLSLANG_WEB_EXCLUDE_OFF + | USAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, true); + } + | TEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D); + } + | TEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd3D); + } + | TEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, true); + } + | TEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdCube); + } + | ITEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D); + } + | ITEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd3D); + } + | ITEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdCube); + } + | ITEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, true); + } + | UTEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D); + } + | UTEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd3D); + } + | UTEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdCube); + } + | UTEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, true); + } + | SAMPLER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setPureSampler(false); + } + | SAMPLERSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setPureSampler(true); + } +GLSLANG_WEB_EXCLUDE_ON + | SAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdRect); + } + | SAMPLER2DRECTSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdRect, false, true); + } + | F16SAMPLER2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdRect); + } + | F16SAMPLER2DRECTSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdRect, false, true); + } + | ISAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdRect); + } + | USAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdRect); + } + | SAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdBuffer); + } + | F16SAMPLERBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdBuffer); + } + | ISAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdBuffer); + } + | USAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdBuffer); + } + | SAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, false, false, true); + } + | F16SAMPLER2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, false, false, true); + } + | ISAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, false, false, true); + } + | USAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, false, false, true); + } + | SAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true, false, true); + } + | F16SAMPLER2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true, false, true); + } + | ISAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, true, false, true); + } + | USAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, true, false, true); + } + | TEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd1D); + } + | F16TEXTURE1D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd1D); + } + | F16TEXTURE2D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D); + } + | F16TEXTURE3D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd3D); + } + | F16TEXTURECUBE { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdCube); + } + | TEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd1D, true); + } + | F16TEXTURE1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd1D, true); + } + | F16TEXTURE2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, true); + } + | F16TEXTURECUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdCube, true); + } + | ITEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd1D); + } + | ITEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd1D, true); + } + | UTEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd1D); + } + | UTEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd1D, true); + } + | TEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdRect); + } + | F16TEXTURE2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdRect); + } + | ITEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdRect); + } + | UTEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdRect); + } + | TEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdBuffer); + } + | F16TEXTUREBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdBuffer); + } + | ITEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdBuffer); + } + | UTEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdBuffer); + } + | TEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, false, false, true); + } + | F16TEXTURE2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, false, false, true); + } + | ITEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, false, false, true); + } + | UTEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, false, false, true); + } + | TEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, true, false, true); + } + | F16TEXTURE2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, true, false, true); + } + | ITEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, true, false, true); + } + | UTEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, true, false, true); + } + | IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd1D); + } + | F16IMAGE1D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd1D); + } + | IIMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd1D); + } + | UIMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd1D); + } + | IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D); + } + | F16IMAGE2D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D); + } + | IIMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D); + } + | UIMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D); + } + | IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd3D); + } + | F16IMAGE3D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd3D); + } + | IIMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd3D); + } + | UIMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd3D); + } + | IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdRect); + } + | F16IMAGE2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdRect); + } + | IIMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdRect); + } + | UIMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdRect); + } + | IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdCube); + } + | F16IMAGECUBE { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdCube); + } + | IIMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdCube); + } + | UIMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdCube); + } + | IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdBuffer); + } + | F16IMAGEBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdBuffer); + } + | IIMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdBuffer); + } + | UIMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdBuffer); + } + | IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd1D, true); + } + | F16IMAGE1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd1D, true); + } + | IIMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd1D, true); + } + | UIMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd1D, true); + } + | IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, true); + } + | F16IMAGE2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, true); + } + | IIMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, true); + } + | UIMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, true); + } + | IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdCube, true); + } + | F16IMAGECUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdCube, true); + } + | IIMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdCube, true); + } + | UIMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdCube, true); + } + | IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, false, false, true); + } + | F16IMAGE2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, false, false, true); + } + | IIMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, false, false, true); + } + | UIMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, false, false, true); + } + | IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, true, false, true); + } + | F16IMAGE2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, true, false, true); + } + | IIMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, true, false, true); + } + | UIMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, true, false, true); + } + | I64IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd1D); + } + | U64IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd1D); + } + | I64IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D); + } + | U64IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D); + } + | I64IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd3D); + } + | U64IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd3D); + } + | I64IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdRect); + } + | U64IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdRect); + } + | I64IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdCube); + } + | U64IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdCube); + } + | I64IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdBuffer); + } + | U64IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdBuffer); + } + | I64IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd1D, true); + } + | U64IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd1D, true); + } + | I64IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, true); + } + | U64IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, true); + } + | I64IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdCube, true); + } + | U64IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdCube, true); + } + | I64IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, false, false, true); + } + | U64IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, false, false, true); + } + | I64IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, true, false, true); + } + | U64IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, true, false, true); + } + | SAMPLEREXTERNALOES { // GL_OES_EGL_image_external + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.external = true; + } + | SAMPLEREXTERNAL2DY2YEXT { // GL_EXT_YUV_target + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.yuv = true; + } + | SUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat); + } + | SUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat, true); + } + | F16SUBPASSINPUT { + parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat16); + } + | F16SUBPASSINPUTMS { + parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat16, true); + } + | ISUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtInt); + } + | ISUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtInt, true); + } + | USUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtUint); + } + | USUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtUint, true); + } + | FCOOPMATNV { + parseContext.fcoopmatCheck($1.loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.coopmat = true; + } + | ICOOPMATNV { + parseContext.intcoopmatCheck($1.loc, "icoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.coopmat = true; + } + | UCOOPMATNV { + parseContext.intcoopmatCheck($1.loc, "ucoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.coopmat = true; + } +GLSLANG_WEB_EXCLUDE_OFF + | struct_specifier { + $$ = $1; + $$.qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; + parseContext.structTypeCheck($$.loc, $$); + } + | TYPE_NAME { + // + // This is for user defined type names. The lexical phase looked up the + // type. + // + if (const TVariable* variable = ($1.symbol)->getAsVariable()) { + const TType& structure = variable->getType(); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtStruct; + $$.userDef = &structure; + } else + parseContext.error($1.loc, "expected type name", $1.string->c_str(), ""); + } + ; + +precision_qualifier + : HIGH_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "highp precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqHigh); + } + | MEDIUM_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "mediump precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqMedium); + } + | LOW_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "lowp precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqLow); + } + ; + +struct_specifier + : STRUCT IDENTIFIER LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + TType* structure = new TType($5, *$2.string); + parseContext.structArrayCheck($2.loc, *structure); + TVariable* userTypeDef = new TVariable($2.string, *structure, true); + if (! parseContext.symbolTable.insert(*userTypeDef)) + parseContext.error($2.loc, "redefinition", $2.string->c_str(), "struct"); + $$.init($1.loc); + $$.basicType = EbtStruct; + $$.userDef = structure; + --parseContext.structNestingLevel; + } + | STRUCT LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + TType* structure = new TType($4, TString("")); + $$.init($1.loc); + $$.basicType = EbtStruct; + $$.userDef = structure; + --parseContext.structNestingLevel; + } + ; + +struct_declaration_list + : struct_declaration { + $$ = $1; + } + | struct_declaration_list struct_declaration { + $$ = $1; + for (unsigned int i = 0; i < $2->size(); ++i) { + for (unsigned int j = 0; j < $$->size(); ++j) { + if ((*$$)[j].type->getFieldName() == (*$2)[i].type->getFieldName()) + parseContext.error((*$2)[i].loc, "duplicate member name:", "", (*$2)[i].type->getFieldName().c_str()); + } + $$->push_back((*$2)[i]); + } + } + ; + +struct_declaration + : type_specifier struct_declarator_list SEMICOLON { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + + $$ = $2; + + parseContext.voidErrorCheck($1.loc, (*$2)[0].type->getFieldName(), $1.basicType); + parseContext.precisionQualifierCheck($1.loc, $1.basicType, $1.qualifier); + + for (unsigned int i = 0; i < $$->size(); ++i) { + TType type($1); + type.setFieldName((*$$)[i].type->getFieldName()); + type.transferArraySizes((*$$)[i].type->getArraySizes()); + type.copyArrayInnerSizes($1.arraySizes); + parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes()); + (*$$)[i].type->shallowCopy(type); + } + } + | type_qualifier type_specifier struct_declarator_list SEMICOLON { + if ($2.arraySizes) { + parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck($2.loc, *$2.arraySizes); + } + + $$ = $3; + + parseContext.memberQualifierCheck($1); + parseContext.voidErrorCheck($2.loc, (*$3)[0].type->getFieldName(), $2.basicType); + parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true); + parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier); + + for (unsigned int i = 0; i < $$->size(); ++i) { + TType type($2); + type.setFieldName((*$$)[i].type->getFieldName()); + type.transferArraySizes((*$$)[i].type->getArraySizes()); + type.copyArrayInnerSizes($2.arraySizes); + parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes()); + (*$$)[i].type->shallowCopy(type); + } + } + ; + +struct_declarator_list + : struct_declarator { + $$ = new TTypeList; + $$->push_back($1); + } + | struct_declarator_list COMMA struct_declarator { + $$->push_back($3); + } + ; + +struct_declarator + : IDENTIFIER { + $$.type = new TType(EbtVoid); + $$.loc = $1.loc; + $$.type->setFieldName(*$1.string); + } + | IDENTIFIER array_specifier { + parseContext.arrayOfArrayVersionCheck($1.loc, $2.arraySizes); + + $$.type = new TType(EbtVoid); + $$.loc = $1.loc; + $$.type->setFieldName(*$1.string); + $$.type->transferArraySizes($2.arraySizes); + } + ; + +initializer + : assignment_expression { + $$ = $1; + } +GLSLANG_WEB_EXCLUDE_ON + | LEFT_BRACE initializer_list RIGHT_BRACE { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile($1.loc, ~EEsProfile, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + $$ = $2; + } + | LEFT_BRACE initializer_list COMMA RIGHT_BRACE { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile($1.loc, ~EEsProfile, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + $$ = $2; + } +GLSLANG_WEB_EXCLUDE_OFF + ; + +GLSLANG_WEB_EXCLUDE_ON +initializer_list + : initializer { + $$ = parseContext.intermediate.growAggregate(0, $1, $1->getLoc()); + } + | initializer_list COMMA initializer { + $$ = parseContext.intermediate.growAggregate($1, $3); + } + ; +GLSLANG_WEB_EXCLUDE_OFF + +declaration_statement + : declaration { $$ = $1; } + ; + +statement + : compound_statement { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +// Grammar Note: labeled statements for switch statements only; 'goto' is not supported. + +simple_statement + : declaration_statement { $$ = $1; } + | expression_statement { $$ = $1; } + | selection_statement { $$ = $1; } + | switch_statement { $$ = $1; } + | case_label { $$ = $1; } + | iteration_statement { $$ = $1; } + | jump_statement { $$ = $1; } +GLSLANG_WEB_EXCLUDE_ON + | demote_statement { $$ = $1; } +GLSLANG_WEB_EXCLUDE_OFF + ; + +GLSLANG_WEB_EXCLUDE_ON +demote_statement + : DEMOTE SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "demote"); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_demote_to_helper_invocation, "demote"); + $$ = parseContext.intermediate.addBranch(EOpDemote, $1.loc); + } + ; +GLSLANG_WEB_EXCLUDE_OFF + +compound_statement + : LEFT_BRACE RIGHT_BRACE { $$ = 0; } + | LEFT_BRACE { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + statement_list { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + RIGHT_BRACE { + if ($3 && $3->getAsAggregate()) + $3->getAsAggregate()->setOperator(EOpSequence); + $$ = $3; + } + ; + +statement_no_new_scope + : compound_statement_no_new_scope { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +statement_scoped + : { + ++parseContext.controlFlowNestingLevel; + } + compound_statement { + --parseContext.controlFlowNestingLevel; + $$ = $2; + } + | { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + simple_statement { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + $$ = $2; + } + +compound_statement_no_new_scope + // Statement that doesn't create a new scope, for selection_statement, iteration_statement + : LEFT_BRACE RIGHT_BRACE { + $$ = 0; + } + | LEFT_BRACE statement_list RIGHT_BRACE { + if ($2 && $2->getAsAggregate()) + $2->getAsAggregate()->setOperator(EOpSequence); + $$ = $2; + } + ; + +statement_list + : statement { + $$ = parseContext.intermediate.makeAggregate($1); + if ($1 && $1->getAsBranchNode() && ($1->getAsBranchNode()->getFlowOp() == EOpCase || + $1->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence(0, $1); + $$ = 0; // start a fresh subsequence for what's after this case + } + } + | statement_list statement { + if ($2 && $2->getAsBranchNode() && ($2->getAsBranchNode()->getFlowOp() == EOpCase || + $2->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence($1 ? $1->getAsAggregate() : 0, $2); + $$ = 0; // start a fresh subsequence for what's after this case + } else + $$ = parseContext.intermediate.growAggregate($1, $2); + } + ; + +expression_statement + : SEMICOLON { $$ = 0; } + | expression SEMICOLON { $$ = static_cast($1); } + ; + +selection_statement + : selection_statement_nonattributed { + $$ = $1; + } +GLSLANG_WEB_EXCLUDE_ON + | attribute selection_statement_nonattributed { + parseContext.handleSelectionAttributes(*$1, $2); + $$ = $2; + } +GLSLANG_WEB_EXCLUDE_OFF + +selection_statement_nonattributed + : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement { + parseContext.boolCheck($1.loc, $3); + $$ = parseContext.intermediate.addSelection($3, $5, $1.loc); + } + ; + +selection_rest_statement + : statement_scoped ELSE statement_scoped { + $$.node1 = $1; + $$.node2 = $3; + } + | statement_scoped { + $$.node1 = $1; + $$.node2 = 0; + } + ; + +condition + // In 1996 c++ draft, conditions can include single declarations + : expression { + $$ = $1; + parseContext.boolCheck($1->getLoc(), $1); + } + | fully_specified_type IDENTIFIER EQUAL initializer { + parseContext.boolCheck($2.loc, $1); + + TType type($1); + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4); + if (initNode) + $$ = initNode->getAsTyped(); + else + $$ = 0; + } + ; + +switch_statement + : switch_statement_nonattributed { + $$ = $1; + } +GLSLANG_WEB_EXCLUDE_ON + | attribute switch_statement_nonattributed { + parseContext.handleSwitchAttributes(*$1, $2); + $$ = $2; + } +GLSLANG_WEB_EXCLUDE_OFF + +switch_statement_nonattributed + : SWITCH LEFT_PAREN expression RIGHT_PAREN { + // start new switch sequence on the switch stack + ++parseContext.controlFlowNestingLevel; + ++parseContext.statementNestingLevel; + parseContext.switchSequenceStack.push_back(new TIntermSequence); + parseContext.switchLevel.push_back(parseContext.statementNestingLevel); + parseContext.symbolTable.push(); + } + LEFT_BRACE switch_statement_list RIGHT_BRACE { + $$ = parseContext.addSwitch($1.loc, $3, $7 ? $7->getAsAggregate() : 0); + delete parseContext.switchSequenceStack.back(); + parseContext.switchSequenceStack.pop_back(); + parseContext.switchLevel.pop_back(); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + ; + +switch_statement_list + : /* nothing */ { + $$ = 0; + } + | statement_list { + $$ = $1; + } + ; + +case_label + : CASE expression COLON { + $$ = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error($1.loc, "cannot appear outside switch statement", "case", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error($1.loc, "cannot be nested inside control flow", "case", ""); + else { + parseContext.constantValueCheck($2, "case"); + parseContext.integerCheck($2, "case"); + $$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.loc); + } + } + | DEFAULT COLON { + $$ = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error($1.loc, "cannot appear outside switch statement", "default", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error($1.loc, "cannot be nested inside control flow", "default", ""); + else + $$ = parseContext.intermediate.addBranch(EOpDefault, $1.loc); + } + ; + +iteration_statement + : iteration_statement_nonattributed { + $$ = $1; + } +GLSLANG_WEB_EXCLUDE_ON + | attribute iteration_statement_nonattributed { + parseContext.handleLoopAttributes(*$1, $2); + $$ = $2; + } +GLSLANG_WEB_EXCLUDE_OFF + +iteration_statement_nonattributed + : WHILE LEFT_PAREN { + if (! parseContext.limits.whileLoops) + parseContext.error($1.loc, "while loops not available", "limitation", ""); + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + condition RIGHT_PAREN statement_no_new_scope { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.addLoop($6, $4, 0, true, $1.loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + | DO { + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { + if (! parseContext.limits.whileLoops) + parseContext.error($1.loc, "do-while loops not available", "limitation", ""); + + parseContext.boolCheck($8.loc, $6); + + $$ = parseContext.intermediate.addLoop($3, $6, 0, false, $4.loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + | FOR LEFT_PAREN { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.makeAggregate($4, $2.loc); + TIntermLoop* forLoop = parseContext.intermediate.addLoop($7, reinterpret_cast($5.node1), reinterpret_cast($5.node2), true, $1.loc); + if (! parseContext.limits.nonInductiveForLoops) + parseContext.inductiveLoopCheck($1.loc, $4, forLoop); + $$ = parseContext.intermediate.growAggregate($$, forLoop, $1.loc); + $$->getAsAggregate()->setOperator(EOpSequence); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + ; + +for_init_statement + : expression_statement { + $$ = $1; + } + | declaration_statement { + $$ = $1; + } + ; + +conditionopt + : condition { + $$ = $1; + } + | /* May be null */ { + $$ = 0; + } + ; + +for_rest_statement + : conditionopt SEMICOLON { + $$.node1 = $1; + $$.node2 = 0; + } + | conditionopt SEMICOLON expression { + $$.node1 = $1; + $$.node2 = $3; + } + ; + +jump_statement + : CONTINUE SEMICOLON { + if (parseContext.loopNestingLevel <= 0) + parseContext.error($1.loc, "continue statement only allowed in loops", "", ""); + $$ = parseContext.intermediate.addBranch(EOpContinue, $1.loc); + } + | BREAK SEMICOLON { + if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) + parseContext.error($1.loc, "break statement only allowed in switch and loops", "", ""); + $$ = parseContext.intermediate.addBranch(EOpBreak, $1.loc); + } + | RETURN SEMICOLON { + $$ = parseContext.intermediate.addBranch(EOpReturn, $1.loc); + if (parseContext.currentFunctionType->getBasicType() != EbtVoid) + parseContext.error($1.loc, "non-void function must return a value", "return", ""); + if (parseContext.inMain) + parseContext.postEntryPointReturn = true; + } + | RETURN expression SEMICOLON { + $$ = parseContext.handleReturnValue($1.loc, $2); + } + | DISCARD SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "discard"); + $$ = parseContext.intermediate.addBranch(EOpKill, $1.loc); + } + | TERMINATE_INVOCATION SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "terminateInvocation"); + $$ = parseContext.intermediate.addBranch(EOpTerminateInvocation, $1.loc); + } + ; + +// Grammar Note: No 'goto'. Gotos are not supported. + +translation_unit + : external_declaration { + $$ = $1; + parseContext.intermediate.setTreeRoot($$); + } + | translation_unit external_declaration { + if ($2 != nullptr) { + $$ = parseContext.intermediate.growAggregate($1, $2); + parseContext.intermediate.setTreeRoot($$); + } + } + ; + +external_declaration + : function_definition { + $$ = $1; + } + | declaration { + $$ = $1; + } +GLSLANG_WEB_EXCLUDE_ON + | SEMICOLON { + parseContext.requireProfile($1.loc, ~EEsProfile, "extraneous semicolon"); + parseContext.profileRequires($1.loc, ~EEsProfile, 460, nullptr, "extraneous semicolon"); + $$ = nullptr; + } +GLSLANG_WEB_EXCLUDE_OFF + ; + +function_definition + : function_prototype { + $1.function = parseContext.handleFunctionDeclarator($1.loc, *$1.function, false /* not prototype */); + $1.intermNode = parseContext.handleFunctionDefinition($1.loc, *$1.function); + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + } + compound_statement_no_new_scope { + // May be best done as post process phase on intermediate code + if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue) + parseContext.error($1.loc, "function does not return a value:", "", $1.function->getName().c_str()); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.growAggregate($1.intermNode, $3); + parseContext.intermediate.setAggregateOperator($$, EOpFunction, $1.function->getType(), $1.loc); + $$->getAsAggregate()->setName($1.function->getMangledName().c_str()); + + // store the pragma information for debug and optimize and other vendor specific + // information. This information can be queried from the parse tree + $$->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize); + $$->getAsAggregate()->setDebug(parseContext.contextPragma.debug); + $$->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable); + + // Set currentFunctionType to empty pointer when goes outside of the function + parseContext.currentFunctionType = nullptr; + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + } + ; + +GLSLANG_WEB_EXCLUDE_ON +attribute + : LEFT_BRACKET LEFT_BRACKET attribute_list RIGHT_BRACKET RIGHT_BRACKET { + $$ = $3; + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_control_flow_attributes, "attribute"); + } + +attribute_list + : single_attribute { + $$ = $1; + } + | attribute_list COMMA single_attribute { + $$ = parseContext.mergeAttributes($1, $3); + } + +single_attribute + : IDENTIFIER { + $$ = parseContext.makeAttributes(*$1.string); + } + | IDENTIFIER LEFT_PAREN constant_expression RIGHT_PAREN { + $$ = parseContext.makeAttributes(*$1.string, $3); + } +GLSLANG_WEB_EXCLUDE_OFF + +%% diff --git a/third_party/glslang/glslang/MachineIndependent/glslang.y b/third_party/glslang/glslang/MachineIndependent/glslang.y new file mode 100644 index 0000000..4093b7d --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/glslang.y @@ -0,0 +1,4033 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2019 Google, Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do not edit the .y file, only edit the .m4 file. +// The .y bison file is not a source file, it is a derivative of the .m4 file. +// The m4 file needs to be processed by m4 to generate the .y bison file. +// +// Code sandwiched between a pair: +// +// GLSLANG_WEB_EXCLUDE_ON +// ... +// ... +// ... +// GLSLANG_WEB_EXCLUDE_OFF +// +// Will be excluded from the grammar when m4 is executed as: +// +// m4 -P -DGLSLANG_WEB +// +// It will be included when m4 is executed as: +// +// m4 -P +// + + + + +/** + * This is bison grammar and productions for parsing all versions of the + * GLSL shading languages. + */ +%{ + +/* Based on: +ANSI C Yacc grammar + +In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a +matching Lex specification) for the April 30, 1985 draft version of the +ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that +original, as mentioned in the answer to question 17.25 of the comp.lang.c +FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z. + +I intend to keep this version as close to the current C Standard grammar as +possible; please let me know if you discover discrepancies. + +Jutta Degener, 1995 +*/ + +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "../Public/ShaderLang.h" +#include "attribute.h" + +using namespace glslang; + +%} + +%define parse.error verbose + +%union { + struct { + glslang::TSourceLoc loc; + union { + glslang::TString *string; + int i; + unsigned int u; + long long i64; + unsigned long long u64; + bool b; + double d; + }; + glslang::TSymbol* symbol; + } lex; + struct { + glslang::TSourceLoc loc; + glslang::TOperator op; + union { + TIntermNode* intermNode; + glslang::TIntermNodePair nodePair; + glslang::TIntermTyped* intermTypedNode; + glslang::TAttributes* attributes; + }; + union { + glslang::TPublicType type; + glslang::TFunction* function; + glslang::TParameter param; + glslang::TTypeLoc typeLine; + glslang::TTypeList* typeList; + glslang::TArraySizes* arraySizes; + glslang::TIdentifierList* identifierList; + }; + glslang::TArraySizes* typeParameters; + } interm; +} + +%{ + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4065) + #pragma warning(disable : 4127) + #pragma warning(disable : 4244) +#endif + +#define parseContext (*pParseContext) +#define yyerror(context, msg) context->parserError(msg) + +extern int yylex(YYSTYPE*, TParseContext&); + +%} + +%parse-param {glslang::TParseContext* pParseContext} +%lex-param {parseContext} +%pure-parser // enable thread safety +%expect 1 // One shift reduce conflict because of if | else + +%token CONST BOOL INT UINT FLOAT +%token BVEC2 BVEC3 BVEC4 +%token IVEC2 IVEC3 IVEC4 +%token UVEC2 UVEC3 UVEC4 +%token VEC2 VEC3 VEC4 +%token MAT2 MAT3 MAT4 +%token MAT2X2 MAT2X3 MAT2X4 +%token MAT3X2 MAT3X3 MAT3X4 +%token MAT4X2 MAT4X3 MAT4X4 + +// combined image/sampler +%token SAMPLER2D SAMPLER3D SAMPLERCUBE SAMPLER2DSHADOW +%token SAMPLERCUBESHADOW SAMPLER2DARRAY +%token SAMPLER2DARRAYSHADOW ISAMPLER2D ISAMPLER3D ISAMPLERCUBE +%token ISAMPLER2DARRAY USAMPLER2D USAMPLER3D +%token USAMPLERCUBE USAMPLER2DARRAY + +// separate image/sampler +%token SAMPLER SAMPLERSHADOW +%token TEXTURE2D TEXTURE3D TEXTURECUBE TEXTURE2DARRAY +%token ITEXTURE2D ITEXTURE3D ITEXTURECUBE ITEXTURE2DARRAY +%token UTEXTURE2D UTEXTURE3D UTEXTURECUBE UTEXTURE2DARRAY + + + +%token ATTRIBUTE VARYING +%token FLOAT16_T FLOAT32_T DOUBLE FLOAT64_T +%token INT64_T UINT64_T INT32_T UINT32_T INT16_T UINT16_T INT8_T UINT8_T +%token I64VEC2 I64VEC3 I64VEC4 +%token U64VEC2 U64VEC3 U64VEC4 +%token I32VEC2 I32VEC3 I32VEC4 +%token U32VEC2 U32VEC3 U32VEC4 +%token I16VEC2 I16VEC3 I16VEC4 +%token U16VEC2 U16VEC3 U16VEC4 +%token I8VEC2 I8VEC3 I8VEC4 +%token U8VEC2 U8VEC3 U8VEC4 +%token DVEC2 DVEC3 DVEC4 DMAT2 DMAT3 DMAT4 +%token F16VEC2 F16VEC3 F16VEC4 F16MAT2 F16MAT3 F16MAT4 +%token F32VEC2 F32VEC3 F32VEC4 F32MAT2 F32MAT3 F32MAT4 +%token F64VEC2 F64VEC3 F64VEC4 F64MAT2 F64MAT3 F64MAT4 +%token DMAT2X2 DMAT2X3 DMAT2X4 +%token DMAT3X2 DMAT3X3 DMAT3X4 +%token DMAT4X2 DMAT4X3 DMAT4X4 +%token F16MAT2X2 F16MAT2X3 F16MAT2X4 +%token F16MAT3X2 F16MAT3X3 F16MAT3X4 +%token F16MAT4X2 F16MAT4X3 F16MAT4X4 +%token F32MAT2X2 F32MAT2X3 F32MAT2X4 +%token F32MAT3X2 F32MAT3X3 F32MAT3X4 +%token F32MAT4X2 F32MAT4X3 F32MAT4X4 +%token F64MAT2X2 F64MAT2X3 F64MAT2X4 +%token F64MAT3X2 F64MAT3X3 F64MAT3X4 +%token F64MAT4X2 F64MAT4X3 F64MAT4X4 +%token ATOMIC_UINT +%token ACCSTRUCTNV +%token ACCSTRUCTEXT +%token RAYQUERYEXT +%token FCOOPMATNV ICOOPMATNV UCOOPMATNV + +// combined image/sampler +%token SAMPLERCUBEARRAY SAMPLERCUBEARRAYSHADOW +%token ISAMPLERCUBEARRAY USAMPLERCUBEARRAY +%token SAMPLER1D SAMPLER1DARRAY SAMPLER1DARRAYSHADOW ISAMPLER1D SAMPLER1DSHADOW +%token SAMPLER2DRECT SAMPLER2DRECTSHADOW ISAMPLER2DRECT USAMPLER2DRECT +%token SAMPLERBUFFER ISAMPLERBUFFER USAMPLERBUFFER +%token SAMPLER2DMS ISAMPLER2DMS USAMPLER2DMS +%token SAMPLER2DMSARRAY ISAMPLER2DMSARRAY USAMPLER2DMSARRAY +%token SAMPLEREXTERNALOES +%token SAMPLEREXTERNAL2DY2YEXT +%token ISAMPLER1DARRAY USAMPLER1D USAMPLER1DARRAY +%token F16SAMPLER1D F16SAMPLER2D F16SAMPLER3D F16SAMPLER2DRECT F16SAMPLERCUBE +%token F16SAMPLER1DARRAY F16SAMPLER2DARRAY F16SAMPLERCUBEARRAY +%token F16SAMPLERBUFFER F16SAMPLER2DMS F16SAMPLER2DMSARRAY +%token F16SAMPLER1DSHADOW F16SAMPLER2DSHADOW F16SAMPLER1DARRAYSHADOW F16SAMPLER2DARRAYSHADOW +%token F16SAMPLER2DRECTSHADOW F16SAMPLERCUBESHADOW F16SAMPLERCUBEARRAYSHADOW + +// images +%token IMAGE1D IIMAGE1D UIMAGE1D IMAGE2D IIMAGE2D +%token UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D +%token IMAGE2DRECT IIMAGE2DRECT UIMAGE2DRECT +%token IMAGECUBE IIMAGECUBE UIMAGECUBE +%token IMAGEBUFFER IIMAGEBUFFER UIMAGEBUFFER +%token IMAGE1DARRAY IIMAGE1DARRAY UIMAGE1DARRAY +%token IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY +%token IMAGECUBEARRAY IIMAGECUBEARRAY UIMAGECUBEARRAY +%token IMAGE2DMS IIMAGE2DMS UIMAGE2DMS +%token IMAGE2DMSARRAY IIMAGE2DMSARRAY UIMAGE2DMSARRAY + +%token F16IMAGE1D F16IMAGE2D F16IMAGE3D F16IMAGE2DRECT +%token F16IMAGECUBE F16IMAGE1DARRAY F16IMAGE2DARRAY F16IMAGECUBEARRAY +%token F16IMAGEBUFFER F16IMAGE2DMS F16IMAGE2DMSARRAY + +%token I64IMAGE1D U64IMAGE1D +%token I64IMAGE2D U64IMAGE2D +%token I64IMAGE3D U64IMAGE3D +%token I64IMAGE2DRECT U64IMAGE2DRECT +%token I64IMAGECUBE U64IMAGECUBE +%token I64IMAGEBUFFER U64IMAGEBUFFER +%token I64IMAGE1DARRAY U64IMAGE1DARRAY +%token I64IMAGE2DARRAY U64IMAGE2DARRAY +%token I64IMAGECUBEARRAY U64IMAGECUBEARRAY +%token I64IMAGE2DMS U64IMAGE2DMS +%token I64IMAGE2DMSARRAY U64IMAGE2DMSARRAY + +// texture without sampler +%token TEXTURECUBEARRAY ITEXTURECUBEARRAY UTEXTURECUBEARRAY +%token TEXTURE1D ITEXTURE1D UTEXTURE1D +%token TEXTURE1DARRAY ITEXTURE1DARRAY UTEXTURE1DARRAY +%token TEXTURE2DRECT ITEXTURE2DRECT UTEXTURE2DRECT +%token TEXTUREBUFFER ITEXTUREBUFFER UTEXTUREBUFFER +%token TEXTURE2DMS ITEXTURE2DMS UTEXTURE2DMS +%token TEXTURE2DMSARRAY ITEXTURE2DMSARRAY UTEXTURE2DMSARRAY + +%token F16TEXTURE1D F16TEXTURE2D F16TEXTURE3D F16TEXTURE2DRECT F16TEXTURECUBE +%token F16TEXTURE1DARRAY F16TEXTURE2DARRAY F16TEXTURECUBEARRAY +%token F16TEXTUREBUFFER F16TEXTURE2DMS F16TEXTURE2DMSARRAY + +// input attachments +%token SUBPASSINPUT SUBPASSINPUTMS ISUBPASSINPUT ISUBPASSINPUTMS USUBPASSINPUT USUBPASSINPUTMS +%token F16SUBPASSINPUT F16SUBPASSINPUTMS + + + +%token LEFT_OP RIGHT_OP +%token INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP +%token AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN +%token MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN +%token SUB_ASSIGN +%token STRING_LITERAL + +%token LEFT_PAREN RIGHT_PAREN LEFT_BRACKET RIGHT_BRACKET LEFT_BRACE RIGHT_BRACE DOT +%token COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT +%token LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION + +%token INVARIANT +%token HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION +%token PACKED RESOURCE SUPERP + +%token FLOATCONSTANT INTCONSTANT UINTCONSTANT BOOLCONSTANT +%token IDENTIFIER TYPE_NAME +%token CENTROID IN OUT INOUT +%token STRUCT VOID WHILE +%token BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT +%token TERMINATE_INVOCATION +%token UNIFORM SHARED BUFFER +%token FLAT SMOOTH LAYOUT + + +%token DOUBLECONSTANT INT16CONSTANT UINT16CONSTANT FLOAT16CONSTANT INT32CONSTANT UINT32CONSTANT +%token INT64CONSTANT UINT64CONSTANT +%token SUBROUTINE DEMOTE +%token PAYLOADNV PAYLOADINNV HITATTRNV CALLDATANV CALLDATAINNV +%token PAYLOADEXT PAYLOADINEXT HITATTREXT CALLDATAEXT CALLDATAINEXT +%token PATCH SAMPLE NONUNIFORM +%token COHERENT VOLATILE RESTRICT READONLY WRITEONLY DEVICECOHERENT QUEUEFAMILYCOHERENT WORKGROUPCOHERENT +%token SUBGROUPCOHERENT NONPRIVATE SHADERCALLCOHERENT +%token NOPERSPECTIVE EXPLICITINTERPAMD PERVERTEXNV PERPRIMITIVENV PERVIEWNV PERTASKNV +%token PRECISE + + +%type assignment_operator unary_operator +%type variable_identifier primary_expression postfix_expression +%type expression integer_expression assignment_expression +%type unary_expression multiplicative_expression additive_expression +%type relational_expression equality_expression +%type conditional_expression constant_expression +%type logical_or_expression logical_xor_expression logical_and_expression +%type shift_expression and_expression exclusive_or_expression inclusive_or_expression +%type function_call initializer condition conditionopt + +%type translation_unit function_definition +%type statement simple_statement +%type statement_list switch_statement_list compound_statement +%type declaration_statement selection_statement selection_statement_nonattributed expression_statement +%type switch_statement switch_statement_nonattributed case_label +%type declaration external_declaration +%type for_init_statement compound_statement_no_new_scope +%type selection_rest_statement for_rest_statement +%type iteration_statement iteration_statement_nonattributed jump_statement statement_no_new_scope statement_scoped +%type single_declaration init_declarator_list + +%type parameter_declaration parameter_declarator parameter_type_specifier + +%type array_specifier +%type invariant_qualifier interpolation_qualifier storage_qualifier precision_qualifier +%type layout_qualifier layout_qualifier_id_list layout_qualifier_id + +%type type_parameter_specifier +%type type_parameter_specifier_opt +%type type_parameter_specifier_list + +%type type_qualifier fully_specified_type type_specifier +%type single_type_qualifier +%type type_specifier_nonarray +%type struct_specifier +%type struct_declarator +%type struct_declarator_list struct_declaration struct_declaration_list +%type block_structure +%type function_header function_declarator +%type function_header_with_parameters +%type function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype +%type function_call_or_method function_identifier function_call_header + +%type identifier_list + + +%type precise_qualifier non_uniform_qualifier +%type type_name_list +%type attribute attribute_list single_attribute +%type demote_statement +%type initializer_list + + +%start translation_unit +%% + +variable_identifier + : IDENTIFIER { + $$ = parseContext.handleVariable($1.loc, $1.symbol, $1.string); + } + ; + +primary_expression + : variable_identifier { + $$ = $1; + } + | LEFT_PAREN expression RIGHT_PAREN { + $$ = $2; + if ($$->getAsConstantUnion()) + $$->getAsConstantUnion()->setExpression(); + } + | FLOATCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat, $1.loc, true); + } + | INTCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINTCONSTANT { + parseContext.fullIntegerCheck($1.loc, "unsigned literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | BOOLCONSTANT { + $$ = parseContext.intermediate.addConstantUnion($1.b, $1.loc, true); + } + + | STRING_LITERAL { + $$ = parseContext.intermediate.addConstantUnion($1.string, $1.loc, true); + } + | INT32CONSTANT { + parseContext.explicitInt32Check($1.loc, "32-bit signed literal"); + $$ = parseContext.intermediate.addConstantUnion($1.i, $1.loc, true); + } + | UINT32CONSTANT { + parseContext.explicitInt32Check($1.loc, "32-bit signed literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u, $1.loc, true); + } + | INT64CONSTANT { + parseContext.int64Check($1.loc, "64-bit integer literal"); + $$ = parseContext.intermediate.addConstantUnion($1.i64, $1.loc, true); + } + | UINT64CONSTANT { + parseContext.int64Check($1.loc, "64-bit unsigned integer literal"); + $$ = parseContext.intermediate.addConstantUnion($1.u64, $1.loc, true); + } + | INT16CONSTANT { + parseContext.explicitInt16Check($1.loc, "16-bit integer literal"); + $$ = parseContext.intermediate.addConstantUnion((short)$1.i, $1.loc, true); + } + | UINT16CONSTANT { + parseContext.explicitInt16Check($1.loc, "16-bit unsigned integer literal"); + $$ = parseContext.intermediate.addConstantUnion((unsigned short)$1.u, $1.loc, true); + } + | DOUBLECONSTANT { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double literal"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double literal"); + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtDouble, $1.loc, true); + } + | FLOAT16CONSTANT { + parseContext.float16Check($1.loc, "half float literal"); + $$ = parseContext.intermediate.addConstantUnion($1.d, EbtFloat16, $1.loc, true); + } + + ; + +postfix_expression + : primary_expression { + $$ = $1; + } + | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET { + $$ = parseContext.handleBracketDereference($2.loc, $1, $3); + } + | function_call { + $$ = $1; + } + | postfix_expression DOT IDENTIFIER { + $$ = parseContext.handleDotDereference($3.loc, $1, *$3.string); + } + | postfix_expression INC_OP { + parseContext.variableCheck($1); + parseContext.lValueErrorCheck($2.loc, "++", $1); + $$ = parseContext.handleUnaryMath($2.loc, "++", EOpPostIncrement, $1); + } + | postfix_expression DEC_OP { + parseContext.variableCheck($1); + parseContext.lValueErrorCheck($2.loc, "--", $1); + $$ = parseContext.handleUnaryMath($2.loc, "--", EOpPostDecrement, $1); + } + ; + +integer_expression + : expression { + parseContext.integerCheck($1, "[]"); + $$ = $1; + } + ; + +function_call + : function_call_or_method { + $$ = parseContext.handleFunctionCall($1.loc, $1.function, $1.intermNode); + delete $1.function; + } + ; + +function_call_or_method + : function_call_generic { + $$ = $1; + } + ; + +function_call_generic + : function_call_header_with_parameters RIGHT_PAREN { + $$ = $1; + $$.loc = $2.loc; + } + | function_call_header_no_parameters RIGHT_PAREN { + $$ = $1; + $$.loc = $2.loc; + } + ; + +function_call_header_no_parameters + : function_call_header VOID { + $$ = $1; + } + | function_call_header { + $$ = $1; + } + ; + +function_call_header_with_parameters + : function_call_header assignment_expression { + TParameter param = { 0, new TType }; + param.type->shallowCopy($2->getType()); + $1.function->addParameter(param); + $$.function = $1.function; + $$.intermNode = $2; + } + | function_call_header_with_parameters COMMA assignment_expression { + TParameter param = { 0, new TType }; + param.type->shallowCopy($3->getType()); + $1.function->addParameter(param); + $$.function = $1.function; + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, $3, $2.loc); + } + ; + +function_call_header + : function_identifier LEFT_PAREN { + $$ = $1; + } + ; + +// Grammar Note: Constructors look like functions, but are recognized as types. + +function_identifier + : type_specifier { + // Constructor + $$.intermNode = 0; + $$.function = parseContext.handleConstructorCall($1.loc, $1); + } + | postfix_expression { + // + // Should be a method or subroutine call, but we haven't recognized the arguments yet. + // + $$.function = 0; + $$.intermNode = 0; + + TIntermMethod* method = $1->getAsMethodNode(); + if (method) { + $$.function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength); + $$.intermNode = method->getObject(); + } else { + TIntermSymbol* symbol = $1->getAsSymbolNode(); + if (symbol) { + parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName()); + TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid)); + $$.function = function; + } else + parseContext.error($1->getLoc(), "function call, method, or subroutine call expected", "", ""); + } + + if ($$.function == 0) { + // error recover + TString* empty = NewPoolTString(""); + $$.function = new TFunction(empty, TType(EbtVoid), EOpNull); + } + } + + | non_uniform_qualifier { + // Constructor + $$.intermNode = 0; + $$.function = parseContext.handleConstructorCall($1.loc, $1); + } + + ; + +unary_expression + : postfix_expression { + parseContext.variableCheck($1); + $$ = $1; + if (TIntermMethod* method = $1->getAsMethodNode()) + parseContext.error($1->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), ""); + } + | INC_OP unary_expression { + parseContext.lValueErrorCheck($1.loc, "++", $2); + $$ = parseContext.handleUnaryMath($1.loc, "++", EOpPreIncrement, $2); + } + | DEC_OP unary_expression { + parseContext.lValueErrorCheck($1.loc, "--", $2); + $$ = parseContext.handleUnaryMath($1.loc, "--", EOpPreDecrement, $2); + } + | unary_operator unary_expression { + if ($1.op != EOpNull) { + char errorOp[2] = {0, 0}; + switch($1.op) { + case EOpNegative: errorOp[0] = '-'; break; + case EOpLogicalNot: errorOp[0] = '!'; break; + case EOpBitwiseNot: errorOp[0] = '~'; break; + default: break; // some compilers want this + } + $$ = parseContext.handleUnaryMath($1.loc, errorOp, $1.op, $2); + } else { + $$ = $2; + if ($$->getAsConstantUnion()) + $$->getAsConstantUnion()->setExpression(); + } + } + ; +// Grammar Note: No traditional style type casts. + +unary_operator + : PLUS { $$.loc = $1.loc; $$.op = EOpNull; } + | DASH { $$.loc = $1.loc; $$.op = EOpNegative; } + | BANG { $$.loc = $1.loc; $$.op = EOpLogicalNot; } + | TILDE { $$.loc = $1.loc; $$.op = EOpBitwiseNot; + parseContext.fullIntegerCheck($1.loc, "bitwise not"); } + ; +// Grammar Note: No '*' or '&' unary ops. Pointers are not supported. + +multiplicative_expression + : unary_expression { $$ = $1; } + | multiplicative_expression STAR unary_expression { + $$ = parseContext.handleBinaryMath($2.loc, "*", EOpMul, $1, $3); + if ($$ == 0) + $$ = $1; + } + | multiplicative_expression SLASH unary_expression { + $$ = parseContext.handleBinaryMath($2.loc, "/", EOpDiv, $1, $3); + if ($$ == 0) + $$ = $1; + } + | multiplicative_expression PERCENT unary_expression { + parseContext.fullIntegerCheck($2.loc, "%"); + $$ = parseContext.handleBinaryMath($2.loc, "%", EOpMod, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +additive_expression + : multiplicative_expression { $$ = $1; } + | additive_expression PLUS multiplicative_expression { + $$ = parseContext.handleBinaryMath($2.loc, "+", EOpAdd, $1, $3); + if ($$ == 0) + $$ = $1; + } + | additive_expression DASH multiplicative_expression { + $$ = parseContext.handleBinaryMath($2.loc, "-", EOpSub, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +shift_expression + : additive_expression { $$ = $1; } + | shift_expression LEFT_OP additive_expression { + parseContext.fullIntegerCheck($2.loc, "bit shift left"); + $$ = parseContext.handleBinaryMath($2.loc, "<<", EOpLeftShift, $1, $3); + if ($$ == 0) + $$ = $1; + } + | shift_expression RIGHT_OP additive_expression { + parseContext.fullIntegerCheck($2.loc, "bit shift right"); + $$ = parseContext.handleBinaryMath($2.loc, ">>", EOpRightShift, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +relational_expression + : shift_expression { $$ = $1; } + | relational_expression LEFT_ANGLE shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, "<", EOpLessThan, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression RIGHT_ANGLE shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, ">", EOpGreaterThan, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression LE_OP shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, "<=", EOpLessThanEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | relational_expression GE_OP shift_expression { + $$ = parseContext.handleBinaryMath($2.loc, ">=", EOpGreaterThanEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +equality_expression + : relational_expression { $$ = $1; } + | equality_expression EQ_OP relational_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison"); + parseContext.opaqueCheck($2.loc, $1->getType(), "=="); + parseContext.specializationCheck($2.loc, $1->getType(), "=="); + parseContext.referenceCheck($2.loc, $1->getType(), "=="); + $$ = parseContext.handleBinaryMath($2.loc, "==", EOpEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + | equality_expression NE_OP relational_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array comparison"); + parseContext.opaqueCheck($2.loc, $1->getType(), "!="); + parseContext.specializationCheck($2.loc, $1->getType(), "!="); + parseContext.referenceCheck($2.loc, $1->getType(), "!="); + $$ = parseContext.handleBinaryMath($2.loc, "!=", EOpNotEqual, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +and_expression + : equality_expression { $$ = $1; } + | and_expression AMPERSAND equality_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise and"); + $$ = parseContext.handleBinaryMath($2.loc, "&", EOpAnd, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +exclusive_or_expression + : and_expression { $$ = $1; } + | exclusive_or_expression CARET and_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise exclusive or"); + $$ = parseContext.handleBinaryMath($2.loc, "^", EOpExclusiveOr, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +inclusive_or_expression + : exclusive_or_expression { $$ = $1; } + | inclusive_or_expression VERTICAL_BAR exclusive_or_expression { + parseContext.fullIntegerCheck($2.loc, "bitwise inclusive or"); + $$ = parseContext.handleBinaryMath($2.loc, "|", EOpInclusiveOr, $1, $3); + if ($$ == 0) + $$ = $1; + } + ; + +logical_and_expression + : inclusive_or_expression { $$ = $1; } + | logical_and_expression AND_OP inclusive_or_expression { + $$ = parseContext.handleBinaryMath($2.loc, "&&", EOpLogicalAnd, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +logical_xor_expression + : logical_and_expression { $$ = $1; } + | logical_xor_expression XOR_OP logical_and_expression { + $$ = parseContext.handleBinaryMath($2.loc, "^^", EOpLogicalXor, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +logical_or_expression + : logical_xor_expression { $$ = $1; } + | logical_or_expression OR_OP logical_xor_expression { + $$ = parseContext.handleBinaryMath($2.loc, "||", EOpLogicalOr, $1, $3); + if ($$ == 0) + $$ = parseContext.intermediate.addConstantUnion(false, $2.loc); + } + ; + +conditional_expression + : logical_or_expression { $$ = $1; } + | logical_or_expression QUESTION { + ++parseContext.controlFlowNestingLevel; + } + expression COLON assignment_expression { + --parseContext.controlFlowNestingLevel; + parseContext.boolCheck($2.loc, $1); + parseContext.rValueErrorCheck($2.loc, "?", $1); + parseContext.rValueErrorCheck($5.loc, ":", $4); + parseContext.rValueErrorCheck($5.loc, ":", $6); + $$ = parseContext.intermediate.addSelection($1, $4, $6, $2.loc); + if ($$ == 0) { + parseContext.binaryOpError($2.loc, ":", $4->getCompleteString(), $6->getCompleteString()); + $$ = $6; + } + } + ; + +assignment_expression + : conditional_expression { $$ = $1; } + | unary_expression assignment_operator assignment_expression { + parseContext.arrayObjectCheck($2.loc, $1->getType(), "array assignment"); + parseContext.opaqueCheck($2.loc, $1->getType(), "="); + parseContext.storage16BitAssignmentCheck($2.loc, $1->getType(), "="); + parseContext.specializationCheck($2.loc, $1->getType(), "="); + parseContext.lValueErrorCheck($2.loc, "assign", $1); + parseContext.rValueErrorCheck($2.loc, "assign", $3); + $$ = parseContext.addAssign($2.loc, $2.op, $1, $3); + if ($$ == 0) { + parseContext.assignError($2.loc, "assign", $1->getCompleteString(), $3->getCompleteString()); + $$ = $1; + } + } + ; + +assignment_operator + : EQUAL { + $$.loc = $1.loc; + $$.op = EOpAssign; + } + | MUL_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpMulAssign; + } + | DIV_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpDivAssign; + } + | MOD_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "%="); + $$.loc = $1.loc; + $$.op = EOpModAssign; + } + | ADD_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpAddAssign; + } + | SUB_ASSIGN { + $$.loc = $1.loc; + $$.op = EOpSubAssign; + } + | LEFT_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bit-shift left assign"); + $$.loc = $1.loc; $$.op = EOpLeftShiftAssign; + } + | RIGHT_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bit-shift right assign"); + $$.loc = $1.loc; $$.op = EOpRightShiftAssign; + } + | AND_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-and assign"); + $$.loc = $1.loc; $$.op = EOpAndAssign; + } + | XOR_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-xor assign"); + $$.loc = $1.loc; $$.op = EOpExclusiveOrAssign; + } + | OR_ASSIGN { + parseContext.fullIntegerCheck($1.loc, "bitwise-or assign"); + $$.loc = $1.loc; $$.op = EOpInclusiveOrAssign; + } + ; + +expression + : assignment_expression { + $$ = $1; + } + | expression COMMA assignment_expression { + parseContext.samplerConstructorLocationCheck($2.loc, ",", $3); + $$ = parseContext.intermediate.addComma($1, $3, $2.loc); + if ($$ == 0) { + parseContext.binaryOpError($2.loc, ",", $1->getCompleteString(), $3->getCompleteString()); + $$ = $3; + } + } + ; + +constant_expression + : conditional_expression { + parseContext.constantValueCheck($1, ""); + $$ = $1; + } + ; + +declaration + : function_prototype SEMICOLON { + parseContext.handleFunctionDeclarator($1.loc, *$1.function, true /* prototype */); + $$ = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } + | init_declarator_list SEMICOLON { + if ($1.intermNode && $1.intermNode->getAsAggregate()) + $1.intermNode->getAsAggregate()->setOperator(EOpSequence); + $$ = $1.intermNode; + } + | PRECISION precision_qualifier type_specifier SEMICOLON { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "precision statement"); + // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope + parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]); + parseContext.setDefaultPrecision($1.loc, $3, $2.qualifier.precision); + $$ = 0; + } + | block_structure SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList); + $$ = 0; + } + | block_structure IDENTIFIER SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList, $2.string); + $$ = 0; + } + | block_structure IDENTIFIER array_specifier SEMICOLON { + parseContext.declareBlock($1.loc, *$1.typeList, $2.string, $3.arraySizes); + $$ = 0; + } + | type_qualifier SEMICOLON { + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.updateStandaloneQualifierDefaults($1.loc, $1); + $$ = 0; + } + | type_qualifier IDENTIFIER SEMICOLON { + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$2.string); + $$ = 0; + } + | type_qualifier IDENTIFIER identifier_list SEMICOLON { + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + $3->push_back($2.string); + parseContext.addQualifierToExisting($1.loc, $1.qualifier, *$3); + $$ = 0; + } + ; + +block_structure + : type_qualifier IDENTIFIER LEFT_BRACE { parseContext.nestedBlockCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + --parseContext.blockNestingLevel; + parseContext.blockName = $2.string; + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.currentBlockQualifier = $1.qualifier; + $$.loc = $1.loc; + $$.typeList = $5; + } + +identifier_list + : COMMA IDENTIFIER { + $$ = new TIdentifierList; + $$->push_back($2.string); + } + | identifier_list COMMA IDENTIFIER { + $$ = $1; + $$->push_back($3.string); + } + ; + +function_prototype + : function_declarator RIGHT_PAREN { + $$.function = $1; + $$.loc = $2.loc; + } + ; + +function_declarator + : function_header { + $$ = $1; + } + | function_header_with_parameters { + $$ = $1; + } + ; + + +function_header_with_parameters + : function_header parameter_declaration { + // Add the parameter + $$ = $1; + if ($2.param.type->getBasicType() != EbtVoid) + $1->addParameter($2.param); + else + delete $2.param.type; + } + | function_header_with_parameters COMMA parameter_declaration { + // + // Only first parameter of one-parameter functions can be void + // The check for named parameters not being void is done in parameter_declarator + // + if ($3.param.type->getBasicType() == EbtVoid) { + // + // This parameter > first is void + // + parseContext.error($2.loc, "cannot be an argument type except for '(void)'", "void", ""); + delete $3.param.type; + } else { + // Add the parameter + $$ = $1; + $1->addParameter($3.param); + } + } + ; + +function_header + : fully_specified_type IDENTIFIER LEFT_PAREN { + if ($1.qualifier.storage != EvqGlobal && $1.qualifier.storage != EvqTemporary) { + parseContext.error($2.loc, "no qualifiers allowed for function return", + GetStorageQualifierString($1.qualifier.storage), ""); + } + if ($1.arraySizes) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + + // Add the function as a prototype after parsing it (we do not support recursion) + TFunction *function; + TType type($1); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction($2.string); + + // Make the function + function = new TFunction($2.string, type); + $$ = function; + } + ; + +parameter_declarator + // Type + name + : type_specifier IDENTIFIER { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + if ($1.basicType == EbtVoid) { + parseContext.error($2.loc, "illegal use of type 'void'", $2.string->c_str(), ""); + } + parseContext.reservedErrorCheck($2.loc, *$2.string); + + TParameter param = {$2.string, new TType($1)}; + $$.loc = $2.loc; + $$.param = param; + } + | type_specifier IDENTIFIER array_specifier { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + TType* type = new TType($1); + type->transferArraySizes($3.arraySizes); + type->copyArrayInnerSizes($1.arraySizes); + + parseContext.arrayOfArrayVersionCheck($2.loc, type->getArraySizes()); + parseContext.arraySizeRequiredCheck($3.loc, *$3.arraySizes); + parseContext.reservedErrorCheck($2.loc, *$2.string); + + TParameter param = { $2.string, type }; + + $$.loc = $2.loc; + $$.param = param; + } + ; + +parameter_declaration + // + // With name + // + : type_qualifier parameter_declarator { + $$ = $2; + if ($1.qualifier.precision != EpqNone) + $$.param.type->getQualifier().precision = $1.qualifier.precision; + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type); + parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type); + + } + | parameter_declarator { + $$ = $1; + + parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type); + parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type); + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + } + // + // Without name + // + | type_qualifier parameter_type_specifier { + $$ = $2; + if ($1.qualifier.precision != EpqNone) + $$.param.type->getQualifier().precision = $1.qualifier.precision; + parseContext.precisionQualifierCheck($1.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + + parseContext.checkNoShaderLayouts($1.loc, $1.shaderQualifiers); + parseContext.parameterTypeCheck($2.loc, $1.qualifier.storage, *$$.param.type); + parseContext.paramCheckFix($1.loc, $1.qualifier, *$$.param.type); + } + | parameter_type_specifier { + $$ = $1; + + parseContext.parameterTypeCheck($1.loc, EvqIn, *$1.param.type); + parseContext.paramCheckFixStorage($1.loc, EvqTemporary, *$$.param.type); + parseContext.precisionQualifierCheck($$.loc, $$.param.type->getBasicType(), $$.param.type->getQualifier()); + } + ; + +parameter_type_specifier + : type_specifier { + TParameter param = { 0, new TType($1) }; + $$.param = param; + if ($1.arraySizes) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + ; + +init_declarator_list + : single_declaration { + $$ = $1; + } + | init_declarator_list COMMA IDENTIFIER { + $$ = $1; + parseContext.declareVariable($3.loc, *$3.string, $1.type); + } + | init_declarator_list COMMA IDENTIFIER array_specifier { + $$ = $1; + parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes); + } + | init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer { + $$.type = $1.type; + TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, $4.arraySizes, $6); + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $5.loc); + } + | init_declarator_list COMMA IDENTIFIER EQUAL initializer { + $$.type = $1.type; + TIntermNode* initNode = parseContext.declareVariable($3.loc, *$3.string, $1.type, 0, $5); + $$.intermNode = parseContext.intermediate.growAggregate($1.intermNode, initNode, $4.loc); + } + ; + +single_declaration + : fully_specified_type { + $$.type = $1; + $$.intermNode = 0; + + parseContext.declareTypeDefaults($$.loc, $$.type); + + } + | fully_specified_type IDENTIFIER { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareVariable($2.loc, *$2.string, $1); + } + | fully_specified_type IDENTIFIER array_specifier { + $$.type = $1; + $$.intermNode = 0; + parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes); + } + | fully_specified_type IDENTIFIER array_specifier EQUAL initializer { + $$.type = $1; + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes, $5); + $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $4.loc); + } + | fully_specified_type IDENTIFIER EQUAL initializer { + $$.type = $1; + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4); + $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $3.loc); + } + +// Grammar Note: No 'enum', or 'typedef'. + +fully_specified_type + : type_specifier { + $$ = $1; + + parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $$); + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + } + parseContext.precisionQualifierCheck($$.loc, $$.basicType, $$.qualifier); + } + | type_qualifier type_specifier { + parseContext.globalQualifierFixCheck($1.loc, $1.qualifier); + parseContext.globalQualifierTypeCheck($1.loc, $1.qualifier, $2); + + if ($2.arraySizes) { + parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type"); + } + + if ($2.arraySizes && parseContext.arrayQualifierError($2.loc, $1.qualifier)) + $2.arraySizes = nullptr; + + parseContext.checkNoShaderLayouts($2.loc, $1.shaderQualifiers); + $2.shaderQualifiers.merge($1.shaderQualifiers); + parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true); + parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier); + + $$ = $2; + + if (! $$.qualifier.isInterpolation() && + ((parseContext.language == EShLangVertex && $$.qualifier.storage == EvqVaryingOut) || + (parseContext.language == EShLangFragment && $$.qualifier.storage == EvqVaryingIn))) + $$.qualifier.smooth = true; + } + ; + +invariant_qualifier + : INVARIANT { + parseContext.globalCheck($1.loc, "invariant"); + parseContext.profileRequires($$.loc, ENoProfile, 120, 0, "invariant"); + $$.init($1.loc); + $$.qualifier.invariant = true; + } + ; + +interpolation_qualifier + : SMOOTH { + parseContext.globalCheck($1.loc, "smooth"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "smooth"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "smooth"); + $$.init($1.loc); + $$.qualifier.smooth = true; + } + | FLAT { + parseContext.globalCheck($1.loc, "flat"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "flat"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "flat"); + $$.init($1.loc); + $$.qualifier.flat = true; + } + + | NOPERSPECTIVE { + parseContext.globalCheck($1.loc, "noperspective"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective"); + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "noperspective"); + $$.init($1.loc); + $$.qualifier.nopersp = true; + } + | EXPLICITINTERPAMD { + parseContext.globalCheck($1.loc, "__explicitInterpAMD"); + parseContext.profileRequires($1.loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + $$.init($1.loc); + $$.qualifier.explicitInterp = true; + } + | PERVERTEXNV { + parseContext.globalCheck($1.loc, "pervertexNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires($1.loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + $$.init($1.loc); + $$.qualifier.pervertexNV = true; + } + | PERPRIMITIVENV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perprimitiveNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshNVMask), "perprimitiveNV"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions($1.loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV"); + $$.init($1.loc); + $$.qualifier.perPrimitiveNV = true; + } + | PERVIEWNV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "perviewNV"); + parseContext.requireStage($1.loc, EShLangMeshNV, "perviewNV"); + $$.init($1.loc); + $$.qualifier.perViewNV = true; + } + | PERTASKNV { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck($1.loc, "taskNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask), "taskNV"); + $$.init($1.loc); + $$.qualifier.perTaskNV = true; + } + + ; + +layout_qualifier + : LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN { + $$ = $3; + } + ; + +layout_qualifier_id_list + : layout_qualifier_id { + $$ = $1; + } + | layout_qualifier_id_list COMMA layout_qualifier_id { + $$ = $1; + $$.shaderQualifiers.merge($3.shaderQualifiers); + parseContext.mergeObjectLayoutQualifiers($$.qualifier, $3.qualifier, false); + } + +layout_qualifier_id + : IDENTIFIER { + $$.init($1.loc); + parseContext.setLayoutQualifier($1.loc, $$, *$1.string); + } + | IDENTIFIER EQUAL constant_expression { + $$.init($1.loc); + parseContext.setLayoutQualifier($1.loc, $$, *$1.string, $3); + } + | SHARED { // because "shared" is both an identifier and a keyword + $$.init($1.loc); + TString strShared("shared"); + parseContext.setLayoutQualifier($1.loc, $$, strShared); + } + ; + + +precise_qualifier + : PRECISE { + parseContext.profileRequires($$.loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise"); + parseContext.profileRequires($1.loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise"); + $$.init($1.loc); + $$.qualifier.noContraction = true; + } + ; + + +type_qualifier + : single_type_qualifier { + $$ = $1; + } + | type_qualifier single_type_qualifier { + $$ = $1; + if ($$.basicType == EbtVoid) + $$.basicType = $2.basicType; + + $$.shaderQualifiers.merge($2.shaderQualifiers); + parseContext.mergeQualifiers($$.loc, $$.qualifier, $2.qualifier, false); + } + ; + +single_type_qualifier + : storage_qualifier { + $$ = $1; + } + | layout_qualifier { + $$ = $1; + } + | precision_qualifier { + parseContext.checkPrecisionQualifier($1.loc, $1.qualifier.precision); + $$ = $1; + } + | interpolation_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | invariant_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + + | precise_qualifier { + // allow inheritance of storage qualifier from block declaration + $$ = $1; + } + | non_uniform_qualifier { + $$ = $1; + } + + ; + +storage_qualifier + : CONST { + $$.init($1.loc); + $$.qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant + } + | INOUT { + parseContext.globalCheck($1.loc, "inout"); + $$.init($1.loc); + $$.qualifier.storage = EvqInOut; + } + | IN { + parseContext.globalCheck($1.loc, "in"); + $$.init($1.loc); + // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later + $$.qualifier.storage = EvqIn; + } + | OUT { + parseContext.globalCheck($1.loc, "out"); + $$.init($1.loc); + // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later + $$.qualifier.storage = EvqOut; + } + | CENTROID { + parseContext.profileRequires($1.loc, ENoProfile, 120, 0, "centroid"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "centroid"); + parseContext.globalCheck($1.loc, "centroid"); + $$.init($1.loc); + $$.qualifier.centroid = true; + } + | UNIFORM { + parseContext.globalCheck($1.loc, "uniform"); + $$.init($1.loc); + $$.qualifier.storage = EvqUniform; + } + | SHARED { + parseContext.globalCheck($1.loc, "shared"); + parseContext.profileRequires($1.loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared"); + parseContext.profileRequires($1.loc, EEsProfile, 310, 0, "shared"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshNVMask | EShLangTaskNVMask), "shared"); + $$.init($1.loc); + $$.qualifier.storage = EvqShared; + } + | BUFFER { + parseContext.globalCheck($1.loc, "buffer"); + $$.init($1.loc); + $$.qualifier.storage = EvqBuffer; + } + + | ATTRIBUTE { + parseContext.requireStage($1.loc, EShLangVertex, "attribute"); + parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "attribute"); + parseContext.checkDeprecated($1.loc, ENoProfile, 130, "attribute"); + parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "attribute"); + parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "attribute"); + + parseContext.globalCheck($1.loc, "attribute"); + + $$.init($1.loc); + $$.qualifier.storage = EvqVaryingIn; + } + | VARYING { + parseContext.checkDeprecated($1.loc, ENoProfile, 130, "varying"); + parseContext.checkDeprecated($1.loc, ECoreProfile, 130, "varying"); + parseContext.requireNotRemoved($1.loc, ECoreProfile, 420, "varying"); + parseContext.requireNotRemoved($1.loc, EEsProfile, 300, "varying"); + + parseContext.globalCheck($1.loc, "varying"); + + $$.init($1.loc); + if (parseContext.language == EShLangVertex) + $$.qualifier.storage = EvqVaryingOut; + else + $$.qualifier.storage = EvqVaryingIn; + } + | PATCH { + parseContext.globalCheck($1.loc, "patch"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch"); + $$.init($1.loc); + $$.qualifier.patch = true; + } + | SAMPLE { + parseContext.globalCheck($1.loc, "sample"); + $$.init($1.loc); + $$.qualifier.sample = true; + } + | HITATTRNV { + parseContext.globalCheck($1.loc, "hitAttributeNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitAttr; + } + | HITATTREXT { + parseContext.globalCheck($1.loc, "hitAttributeEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "hitAttributeNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqHitAttr; + } + | PAYLOADNV { + parseContext.globalCheck($1.loc, "rayPayloadNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayload; + } + | PAYLOADEXT { + parseContext.globalCheck($1.loc, "rayPayloadEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayload; + } + | PAYLOADINNV { + parseContext.globalCheck($1.loc, "rayPayloadInNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayloadIn; + } + | PAYLOADINEXT { + parseContext.globalCheck($1.loc, "rayPayloadInEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadInEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqPayloadIn; + } + | CALLDATANV { + parseContext.globalCheck($1.loc, "callableDataNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableData; + } + | CALLDATAEXT { + parseContext.globalCheck($1.loc, "callableDataEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableData; + } + | CALLDATAINNV { + parseContext.globalCheck($1.loc, "callableDataInNV"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableDataIn; + } + | CALLDATAINEXT { + parseContext.globalCheck($1.loc, "callableDataInEXT"); + parseContext.requireStage($1.loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInEXT"); + parseContext.profileRequires($1.loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataInEXT"); + $$.init($1.loc); + $$.qualifier.storage = EvqCallableDataIn; + } + | COHERENT { + $$.init($1.loc); + $$.qualifier.coherent = true; + } + | DEVICECOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent"); + $$.qualifier.devicecoherent = true; + } + | QUEUEFAMILYCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent"); + $$.qualifier.queuefamilycoherent = true; + } + | WORKGROUPCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent"); + $$.qualifier.workgroupcoherent = true; + } + | SUBGROUPCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent"); + $$.qualifier.subgroupcoherent = true; + } + | NONPRIVATE { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate"); + $$.qualifier.nonprivate = true; + } + | SHADERCALLCOHERENT { + $$.init($1.loc); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_ray_tracing, "shadercallcoherent"); + $$.qualifier.shadercallcoherent = true; + } + | VOLATILE { + $$.init($1.loc); + $$.qualifier.volatil = true; + } + | RESTRICT { + $$.init($1.loc); + $$.qualifier.restrict = true; + } + | READONLY { + $$.init($1.loc); + $$.qualifier.readonly = true; + } + | WRITEONLY { + $$.init($1.loc); + $$.qualifier.writeonly = true; + } + | SUBROUTINE { + parseContext.spvRemoved($1.loc, "subroutine"); + parseContext.globalCheck($1.loc, "subroutine"); + parseContext.unimplemented($1.loc, "subroutine"); + $$.init($1.loc); + } + | SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN { + parseContext.spvRemoved($1.loc, "subroutine"); + parseContext.globalCheck($1.loc, "subroutine"); + parseContext.unimplemented($1.loc, "subroutine"); + $$.init($1.loc); + } + + ; + + +non_uniform_qualifier + : NONUNIFORM { + $$.init($1.loc); + $$.qualifier.nonUniform = true; + } + ; + +type_name_list + : IDENTIFIER { + // TODO + } + | type_name_list COMMA IDENTIFIER { + // TODO: 4.0 semantics: subroutines + // 1) make sure each identifier is a type declared earlier with SUBROUTINE + // 2) save all of the identifiers for future comparison with the declared function + } + ; + + +type_specifier + : type_specifier_nonarray type_parameter_specifier_opt { + $$ = $1; + $$.qualifier.precision = parseContext.getDefaultPrecision($$); + $$.typeParameters = $2; + } + | type_specifier_nonarray type_parameter_specifier_opt array_specifier { + parseContext.arrayOfArrayVersionCheck($3.loc, $3.arraySizes); + $$ = $1; + $$.qualifier.precision = parseContext.getDefaultPrecision($$); + $$.typeParameters = $2; + $$.arraySizes = $3.arraySizes; + } + ; + +array_specifier + : LEFT_BRACKET RIGHT_BRACKET { + $$.loc = $1.loc; + $$.arraySizes = new TArraySizes; + $$.arraySizes->addInnerSize(); + } + | LEFT_BRACKET conditional_expression RIGHT_BRACKET { + $$.loc = $1.loc; + $$.arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck($2->getLoc(), $2, size, "array size"); + $$.arraySizes->addInnerSize(size); + } + | array_specifier LEFT_BRACKET RIGHT_BRACKET { + $$ = $1; + $$.arraySizes->addInnerSize(); + } + | array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET { + $$ = $1; + + TArraySize size; + parseContext.arraySizeCheck($3->getLoc(), $3, size, "array size"); + $$.arraySizes->addInnerSize(size); + } + ; + +type_parameter_specifier_opt + : type_parameter_specifier { + $$ = $1; + } + | /* May be null */ { + $$ = 0; + } + ; + +type_parameter_specifier + : LEFT_ANGLE type_parameter_specifier_list RIGHT_ANGLE { + $$ = $2; + } + ; + +type_parameter_specifier_list + : unary_expression { + $$ = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck($1->getLoc(), $1, size, "type parameter"); + $$->addInnerSize(size); + } + | type_parameter_specifier_list COMMA unary_expression { + $$ = $1; + + TArraySize size; + parseContext.arraySizeCheck($3->getLoc(), $3, size, "type parameter"); + $$->addInnerSize(size); + } + ; + +type_specifier_nonarray + : VOID { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtVoid; + } + | FLOAT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + } + | INT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + } + | UINT { + parseContext.fullIntegerCheck($1.loc, "unsigned integer"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + } + | BOOL { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + } + | VEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(2); + } + | VEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(3); + } + | VEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(4); + } + | BVEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(2); + } + | BVEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(3); + } + | BVEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtBool; + $$.setVector(4); + } + | IVEC2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(2); + } + | IVEC3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(3); + } + | IVEC4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(4); + } + | UVEC2 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(2); + } + | UVEC3 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(3); + } + | UVEC4 { + parseContext.fullIntegerCheck($1.loc, "unsigned integer vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(4); + } + | MAT2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | MAT3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | MAT4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | MAT2X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | MAT2X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 3); + } + | MAT2X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 4); + } + | MAT3X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 2); + } + | MAT3X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | MAT3X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 4); + } + | MAT4X2 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 2); + } + | MAT4X3 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 3); + } + | MAT4X4 { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + + | DOUBLE { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + } + | FLOAT16_T { + parseContext.float16ScalarVectorCheck($1.loc, "float16_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + } + | FLOAT32_T { + parseContext.explicitFloat32Check($1.loc, "float32_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + } + | FLOAT64_T { + parseContext.explicitFloat64Check($1.loc, "float64_t", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + } + | INT8_T { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + } + | UINT8_T { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + } + | INT16_T { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + } + | UINT16_T { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + } + | INT32_T { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + } + | UINT32_T { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + } + | INT64_T { + parseContext.int64Check($1.loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + } + | UINT64_T { + parseContext.int64Check($1.loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + } + | DVEC2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(2); + } + | DVEC3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(3); + } + | DVEC4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double vector"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(4); + } + | F16VEC2 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(2); + } + | F16VEC3 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(3); + } + | F16VEC4 { + parseContext.float16ScalarVectorCheck($1.loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setVector(4); + } + | F32VEC2 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(2); + } + | F32VEC3 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(3); + } + | F32VEC4 { + parseContext.explicitFloat32Check($1.loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setVector(4); + } + | F64VEC2 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(2); + } + | F64VEC3 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(3); + } + | F64VEC4 { + parseContext.explicitFloat64Check($1.loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setVector(4); + } + | I8VEC2 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(2); + } + | I8VEC3 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(3); + } + | I8VEC4 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt8; + $$.setVector(4); + } + | I16VEC2 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(2); + } + | I16VEC3 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(3); + } + | I16VEC4 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt16; + $$.setVector(4); + } + | I32VEC2 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(2); + } + | I32VEC3 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(3); + } + | I32VEC4 { + parseContext.explicitInt32Check($1.loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.setVector(4); + } + | I64VEC2 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(2); + } + | I64VEC3 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(3); + } + | I64VEC4 { + parseContext.int64Check($1.loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt64; + $$.setVector(4); + } + | U8VEC2 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(2); + } + | U8VEC3 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(3); + } + | U8VEC4 { + parseContext.int8ScalarVectorCheck($1.loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint8; + $$.setVector(4); + } + | U16VEC2 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(2); + } + | U16VEC3 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(3); + } + | U16VEC4 { + parseContext.int16ScalarVectorCheck($1.loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint16; + $$.setVector(4); + } + | U32VEC2 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(2); + } + | U32VEC3 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(3); + } + | U32VEC4 { + parseContext.explicitInt32Check($1.loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.setVector(4); + } + | U64VEC2 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(2); + } + | U64VEC3 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(3); + } + | U64VEC4 { + parseContext.int64Check($1.loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint64; + $$.setVector(4); + } + | DMAT2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | DMAT3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | DMAT4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | DMAT2X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | DMAT2X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 3); + } + | DMAT2X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 4); + } + | DMAT3X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 2); + } + | DMAT3X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | DMAT3X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 4); + } + | DMAT4X2 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 2); + } + | DMAT4X3 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 3); + } + | DMAT4X4 { + parseContext.requireProfile($1.loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck($1.loc, "double matrix"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | F16MAT2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 2); + } + | F16MAT3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 3); + } + | F16MAT4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 4); + } + | F16MAT2X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 2); + } + | F16MAT2X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 3); + } + | F16MAT2X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(2, 4); + } + | F16MAT3X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 2); + } + | F16MAT3X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 3); + } + | F16MAT3X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(3, 4); + } + | F16MAT4X2 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 2); + } + | F16MAT4X3 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 3); + } + | F16MAT4X4 { + parseContext.float16Check($1.loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat16; + $$.setMatrix(4, 4); + } + | F32MAT2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | F32MAT3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | F32MAT4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | F32MAT2X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 2); + } + | F32MAT2X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 3); + } + | F32MAT2X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(2, 4); + } + | F32MAT3X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 2); + } + | F32MAT3X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 3); + } + | F32MAT3X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(3, 4); + } + | F32MAT4X2 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 2); + } + | F32MAT4X3 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 3); + } + | F32MAT4X4 { + parseContext.explicitFloat32Check($1.loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.setMatrix(4, 4); + } + | F64MAT2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | F64MAT3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | F64MAT4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | F64MAT2X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 2); + } + | F64MAT2X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 3); + } + | F64MAT2X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(2, 4); + } + | F64MAT3X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 2); + } + | F64MAT3X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 3); + } + | F64MAT3X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(3, 4); + } + | F64MAT4X2 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 2); + } + | F64MAT4X3 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 3); + } + | F64MAT4X4 { + parseContext.explicitFloat64Check($1.loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtDouble; + $$.setMatrix(4, 4); + } + | ACCSTRUCTNV { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAccStruct; + } + | ACCSTRUCTEXT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAccStruct; + } + | RAYQUERYEXT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtRayQuery; + } + | ATOMIC_UINT { + parseContext.vulkanRemoved($1.loc, "atomic counter types"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtAtomicUint; + } + | SAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D); + } + + | SAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + } + | SAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd3D); + } + | SAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube); + } + | SAMPLER2DSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, false, true); + } + | SAMPLERCUBESHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, false, true); + } + | SAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true); + } + | SAMPLER2DARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true, true); + } + + | SAMPLER1DSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, false, true); + } + | SAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, true); + } + | SAMPLER1DARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd1D, true, true); + } + | SAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, true); + } + | SAMPLERCUBEARRAYSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdCube, true, true); + } + | F16SAMPLER1D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D); + } + | F16SAMPLER2D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D); + } + | F16SAMPLER3D { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd3D); + } + | F16SAMPLERCUBE { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube); + } + | F16SAMPLER1DSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, false, true); + } + | F16SAMPLER2DSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, false, true); + } + | F16SAMPLERCUBESHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, false, true); + } + | F16SAMPLER1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, true); + } + | F16SAMPLER2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true); + } + | F16SAMPLER1DARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd1D, true, true); + } + | F16SAMPLER2DARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true, true); + } + | F16SAMPLERCUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, true); + } + | F16SAMPLERCUBEARRAYSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdCube, true, true); + } + | ISAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd1D); + } + + | ISAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D); + } + | ISAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd3D); + } + | ISAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdCube); + } + | ISAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, true); + } + | USAMPLER2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D); + } + | USAMPLER3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd3D); + } + | USAMPLERCUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdCube); + } + + | ISAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd1D, true); + } + | ISAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdCube, true); + } + | USAMPLER1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd1D); + } + | USAMPLER1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd1D, true); + } + | USAMPLERCUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdCube, true); + } + | TEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdCube, true); + } + | ITEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdCube, true); + } + | UTEXTURECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdCube, true); + } + + | USAMPLER2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, true); + } + | TEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D); + } + | TEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd3D); + } + | TEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, true); + } + | TEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdCube); + } + | ITEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D); + } + | ITEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd3D); + } + | ITEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdCube); + } + | ITEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, true); + } + | UTEXTURE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D); + } + | UTEXTURE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd3D); + } + | UTEXTURECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdCube); + } + | UTEXTURE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, true); + } + | SAMPLER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setPureSampler(false); + } + | SAMPLERSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setPureSampler(true); + } + + | SAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdRect); + } + | SAMPLER2DRECTSHADOW { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdRect, false, true); + } + | F16SAMPLER2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdRect); + } + | F16SAMPLER2DRECTSHADOW { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdRect, false, true); + } + | ISAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdRect); + } + | USAMPLER2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdRect); + } + | SAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, EsdBuffer); + } + | F16SAMPLERBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, EsdBuffer); + } + | ISAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, EsdBuffer); + } + | USAMPLERBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, EsdBuffer); + } + | SAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, false, false, true); + } + | F16SAMPLER2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, false, false, true); + } + | ISAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, false, false, true); + } + | USAMPLER2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, false, false, true); + } + | SAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D, true, false, true); + } + | F16SAMPLER2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat16, Esd2D, true, false, true); + } + | ISAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtInt, Esd2D, true, false, true); + } + | USAMPLER2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtUint, Esd2D, true, false, true); + } + | TEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd1D); + } + | F16TEXTURE1D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd1D); + } + | F16TEXTURE2D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D); + } + | F16TEXTURE3D { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd3D); + } + | F16TEXTURECUBE { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdCube); + } + | TEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd1D, true); + } + | F16TEXTURE1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd1D, true); + } + | F16TEXTURE2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, true); + } + | F16TEXTURECUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdCube, true); + } + | ITEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd1D); + } + | ITEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd1D, true); + } + | UTEXTURE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd1D); + } + | UTEXTURE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd1D, true); + } + | TEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdRect); + } + | F16TEXTURE2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdRect); + } + | ITEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdRect); + } + | UTEXTURE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdRect); + } + | TEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, EsdBuffer); + } + | F16TEXTUREBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, EsdBuffer); + } + | ITEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, EsdBuffer); + } + | UTEXTUREBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, EsdBuffer); + } + | TEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, false, false, true); + } + | F16TEXTURE2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, false, false, true); + } + | ITEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, false, false, true); + } + | UTEXTURE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, false, false, true); + } + | TEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat, Esd2D, true, false, true); + } + | F16TEXTURE2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtFloat16, Esd2D, true, false, true); + } + | ITEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtInt, Esd2D, true, false, true); + } + | UTEXTURE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setTexture(EbtUint, Esd2D, true, false, true); + } + | IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd1D); + } + | F16IMAGE1D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd1D); + } + | IIMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd1D); + } + | UIMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd1D); + } + | IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D); + } + | F16IMAGE2D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D); + } + | IIMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D); + } + | UIMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D); + } + | IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd3D); + } + | F16IMAGE3D { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd3D); + } + | IIMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd3D); + } + | UIMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd3D); + } + | IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdRect); + } + | F16IMAGE2DRECT { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdRect); + } + | IIMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdRect); + } + | UIMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdRect); + } + | IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdCube); + } + | F16IMAGECUBE { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdCube); + } + | IIMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdCube); + } + | UIMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdCube); + } + | IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdBuffer); + } + | F16IMAGEBUFFER { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdBuffer); + } + | IIMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdBuffer); + } + | UIMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdBuffer); + } + | IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd1D, true); + } + | F16IMAGE1DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd1D, true); + } + | IIMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd1D, true); + } + | UIMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd1D, true); + } + | IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, true); + } + | F16IMAGE2DARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, true); + } + | IIMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, true); + } + | UIMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, true); + } + | IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, EsdCube, true); + } + | F16IMAGECUBEARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, EsdCube, true); + } + | IIMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, EsdCube, true); + } + | UIMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, EsdCube, true); + } + | IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, false, false, true); + } + | F16IMAGE2DMS { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, false, false, true); + } + | IIMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, false, false, true); + } + | UIMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, false, false, true); + } + | IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat, Esd2D, true, false, true); + } + | F16IMAGE2DMSARRAY { + parseContext.float16OpaqueCheck($1.loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtFloat16, Esd2D, true, false, true); + } + | IIMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt, Esd2D, true, false, true); + } + | UIMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint, Esd2D, true, false, true); + } + | I64IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd1D); + } + | U64IMAGE1D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd1D); + } + | I64IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D); + } + | U64IMAGE2D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D); + } + | I64IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd3D); + } + | U64IMAGE3D { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd3D); + } + | I64IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdRect); + } + | U64IMAGE2DRECT { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdRect); + } + | I64IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdCube); + } + | U64IMAGECUBE { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdCube); + } + | I64IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdBuffer); + } + | U64IMAGEBUFFER { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdBuffer); + } + | I64IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd1D, true); + } + | U64IMAGE1DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd1D, true); + } + | I64IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, true); + } + | U64IMAGE2DARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, true); + } + | I64IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, EsdCube, true); + } + | U64IMAGECUBEARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, EsdCube, true); + } + | I64IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, false, false, true); + } + | U64IMAGE2DMS { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, false, false, true); + } + | I64IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtInt64, Esd2D, true, false, true); + } + | U64IMAGE2DMSARRAY { + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setImage(EbtUint64, Esd2D, true, false, true); + } + | SAMPLEREXTERNALOES { // GL_OES_EGL_image_external + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.external = true; + } + | SAMPLEREXTERNAL2DY2YEXT { // GL_EXT_YUV_target + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.set(EbtFloat, Esd2D); + $$.sampler.yuv = true; + } + | SUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat); + } + | SUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat, true); + } + | F16SUBPASSINPUT { + parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat16); + } + | F16SUBPASSINPUTMS { + parseContext.float16OpaqueCheck($1.loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtFloat16, true); + } + | ISUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtInt); + } + | ISUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtInt, true); + } + | USUBPASSINPUT { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtUint); + } + | USUBPASSINPUTMS { + parseContext.requireStage($1.loc, EShLangFragment, "subpass input"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtSampler; + $$.sampler.setSubpass(EbtUint, true); + } + | FCOOPMATNV { + parseContext.fcoopmatCheck($1.loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtFloat; + $$.coopmat = true; + } + | ICOOPMATNV { + parseContext.intcoopmatCheck($1.loc, "icoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtInt; + $$.coopmat = true; + } + | UCOOPMATNV { + parseContext.intcoopmatCheck($1.loc, "ucoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtUint; + $$.coopmat = true; + } + + | struct_specifier { + $$ = $1; + $$.qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; + parseContext.structTypeCheck($$.loc, $$); + } + | TYPE_NAME { + // + // This is for user defined type names. The lexical phase looked up the + // type. + // + if (const TVariable* variable = ($1.symbol)->getAsVariable()) { + const TType& structure = variable->getType(); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + $$.basicType = EbtStruct; + $$.userDef = &structure; + } else + parseContext.error($1.loc, "expected type name", $1.string->c_str(), ""); + } + ; + +precision_qualifier + : HIGH_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "highp precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqHigh); + } + | MEDIUM_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "mediump precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqMedium); + } + | LOW_PRECISION { + parseContext.profileRequires($1.loc, ENoProfile, 130, 0, "lowp precision qualifier"); + $$.init($1.loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier($1.loc, $$.qualifier, EpqLow); + } + ; + +struct_specifier + : STRUCT IDENTIFIER LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + TType* structure = new TType($5, *$2.string); + parseContext.structArrayCheck($2.loc, *structure); + TVariable* userTypeDef = new TVariable($2.string, *structure, true); + if (! parseContext.symbolTable.insert(*userTypeDef)) + parseContext.error($2.loc, "redefinition", $2.string->c_str(), "struct"); + $$.init($1.loc); + $$.basicType = EbtStruct; + $$.userDef = structure; + --parseContext.structNestingLevel; + } + | STRUCT LEFT_BRACE { parseContext.nestedStructCheck($1.loc); } struct_declaration_list RIGHT_BRACE { + TType* structure = new TType($4, TString("")); + $$.init($1.loc); + $$.basicType = EbtStruct; + $$.userDef = structure; + --parseContext.structNestingLevel; + } + ; + +struct_declaration_list + : struct_declaration { + $$ = $1; + } + | struct_declaration_list struct_declaration { + $$ = $1; + for (unsigned int i = 0; i < $2->size(); ++i) { + for (unsigned int j = 0; j < $$->size(); ++j) { + if ((*$$)[j].type->getFieldName() == (*$2)[i].type->getFieldName()) + parseContext.error((*$2)[i].loc, "duplicate member name:", "", (*$2)[i].type->getFieldName().c_str()); + } + $$->push_back((*$2)[i]); + } + } + ; + +struct_declaration + : type_specifier struct_declarator_list SEMICOLON { + if ($1.arraySizes) { + parseContext.profileRequires($1.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($1.loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck($1.loc, *$1.arraySizes); + } + + $$ = $2; + + parseContext.voidErrorCheck($1.loc, (*$2)[0].type->getFieldName(), $1.basicType); + parseContext.precisionQualifierCheck($1.loc, $1.basicType, $1.qualifier); + + for (unsigned int i = 0; i < $$->size(); ++i) { + TType type($1); + type.setFieldName((*$$)[i].type->getFieldName()); + type.transferArraySizes((*$$)[i].type->getArraySizes()); + type.copyArrayInnerSizes($1.arraySizes); + parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes()); + (*$$)[i].type->shallowCopy(type); + } + } + | type_qualifier type_specifier struct_declarator_list SEMICOLON { + if ($2.arraySizes) { + parseContext.profileRequires($2.loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires($2.loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck($2.loc, *$2.arraySizes); + } + + $$ = $3; + + parseContext.memberQualifierCheck($1); + parseContext.voidErrorCheck($2.loc, (*$3)[0].type->getFieldName(), $2.basicType); + parseContext.mergeQualifiers($2.loc, $2.qualifier, $1.qualifier, true); + parseContext.precisionQualifierCheck($2.loc, $2.basicType, $2.qualifier); + + for (unsigned int i = 0; i < $$->size(); ++i) { + TType type($2); + type.setFieldName((*$$)[i].type->getFieldName()); + type.transferArraySizes((*$$)[i].type->getArraySizes()); + type.copyArrayInnerSizes($2.arraySizes); + parseContext.arrayOfArrayVersionCheck((*$$)[i].loc, type.getArraySizes()); + (*$$)[i].type->shallowCopy(type); + } + } + ; + +struct_declarator_list + : struct_declarator { + $$ = new TTypeList; + $$->push_back($1); + } + | struct_declarator_list COMMA struct_declarator { + $$->push_back($3); + } + ; + +struct_declarator + : IDENTIFIER { + $$.type = new TType(EbtVoid); + $$.loc = $1.loc; + $$.type->setFieldName(*$1.string); + } + | IDENTIFIER array_specifier { + parseContext.arrayOfArrayVersionCheck($1.loc, $2.arraySizes); + + $$.type = new TType(EbtVoid); + $$.loc = $1.loc; + $$.type->setFieldName(*$1.string); + $$.type->transferArraySizes($2.arraySizes); + } + ; + +initializer + : assignment_expression { + $$ = $1; + } + + | LEFT_BRACE initializer_list RIGHT_BRACE { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile($1.loc, ~EEsProfile, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + $$ = $2; + } + | LEFT_BRACE initializer_list COMMA RIGHT_BRACE { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile($1.loc, ~EEsProfile, initFeature); + parseContext.profileRequires($1.loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + $$ = $2; + } + + ; + + +initializer_list + : initializer { + $$ = parseContext.intermediate.growAggregate(0, $1, $1->getLoc()); + } + | initializer_list COMMA initializer { + $$ = parseContext.intermediate.growAggregate($1, $3); + } + ; + + +declaration_statement + : declaration { $$ = $1; } + ; + +statement + : compound_statement { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +// Grammar Note: labeled statements for switch statements only; 'goto' is not supported. + +simple_statement + : declaration_statement { $$ = $1; } + | expression_statement { $$ = $1; } + | selection_statement { $$ = $1; } + | switch_statement { $$ = $1; } + | case_label { $$ = $1; } + | iteration_statement { $$ = $1; } + | jump_statement { $$ = $1; } + + | demote_statement { $$ = $1; } + + ; + + +demote_statement + : DEMOTE SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "demote"); + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_demote_to_helper_invocation, "demote"); + $$ = parseContext.intermediate.addBranch(EOpDemote, $1.loc); + } + ; + + +compound_statement + : LEFT_BRACE RIGHT_BRACE { $$ = 0; } + | LEFT_BRACE { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + statement_list { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + RIGHT_BRACE { + if ($3 && $3->getAsAggregate()) + $3->getAsAggregate()->setOperator(EOpSequence); + $$ = $3; + } + ; + +statement_no_new_scope + : compound_statement_no_new_scope { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +statement_scoped + : { + ++parseContext.controlFlowNestingLevel; + } + compound_statement { + --parseContext.controlFlowNestingLevel; + $$ = $2; + } + | { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + simple_statement { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + $$ = $2; + } + +compound_statement_no_new_scope + // Statement that doesn't create a new scope, for selection_statement, iteration_statement + : LEFT_BRACE RIGHT_BRACE { + $$ = 0; + } + | LEFT_BRACE statement_list RIGHT_BRACE { + if ($2 && $2->getAsAggregate()) + $2->getAsAggregate()->setOperator(EOpSequence); + $$ = $2; + } + ; + +statement_list + : statement { + $$ = parseContext.intermediate.makeAggregate($1); + if ($1 && $1->getAsBranchNode() && ($1->getAsBranchNode()->getFlowOp() == EOpCase || + $1->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence(0, $1); + $$ = 0; // start a fresh subsequence for what's after this case + } + } + | statement_list statement { + if ($2 && $2->getAsBranchNode() && ($2->getAsBranchNode()->getFlowOp() == EOpCase || + $2->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence($1 ? $1->getAsAggregate() : 0, $2); + $$ = 0; // start a fresh subsequence for what's after this case + } else + $$ = parseContext.intermediate.growAggregate($1, $2); + } + ; + +expression_statement + : SEMICOLON { $$ = 0; } + | expression SEMICOLON { $$ = static_cast($1); } + ; + +selection_statement + : selection_statement_nonattributed { + $$ = $1; + } + + | attribute selection_statement_nonattributed { + parseContext.handleSelectionAttributes(*$1, $2); + $$ = $2; + } + + +selection_statement_nonattributed + : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement { + parseContext.boolCheck($1.loc, $3); + $$ = parseContext.intermediate.addSelection($3, $5, $1.loc); + } + ; + +selection_rest_statement + : statement_scoped ELSE statement_scoped { + $$.node1 = $1; + $$.node2 = $3; + } + | statement_scoped { + $$.node1 = $1; + $$.node2 = 0; + } + ; + +condition + // In 1996 c++ draft, conditions can include single declarations + : expression { + $$ = $1; + parseContext.boolCheck($1->getLoc(), $1); + } + | fully_specified_type IDENTIFIER EQUAL initializer { + parseContext.boolCheck($2.loc, $1); + + TType type($1); + TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4); + if (initNode) + $$ = initNode->getAsTyped(); + else + $$ = 0; + } + ; + +switch_statement + : switch_statement_nonattributed { + $$ = $1; + } + + | attribute switch_statement_nonattributed { + parseContext.handleSwitchAttributes(*$1, $2); + $$ = $2; + } + + +switch_statement_nonattributed + : SWITCH LEFT_PAREN expression RIGHT_PAREN { + // start new switch sequence on the switch stack + ++parseContext.controlFlowNestingLevel; + ++parseContext.statementNestingLevel; + parseContext.switchSequenceStack.push_back(new TIntermSequence); + parseContext.switchLevel.push_back(parseContext.statementNestingLevel); + parseContext.symbolTable.push(); + } + LEFT_BRACE switch_statement_list RIGHT_BRACE { + $$ = parseContext.addSwitch($1.loc, $3, $7 ? $7->getAsAggregate() : 0); + delete parseContext.switchSequenceStack.back(); + parseContext.switchSequenceStack.pop_back(); + parseContext.switchLevel.pop_back(); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + ; + +switch_statement_list + : /* nothing */ { + $$ = 0; + } + | statement_list { + $$ = $1; + } + ; + +case_label + : CASE expression COLON { + $$ = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error($1.loc, "cannot appear outside switch statement", "case", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error($1.loc, "cannot be nested inside control flow", "case", ""); + else { + parseContext.constantValueCheck($2, "case"); + parseContext.integerCheck($2, "case"); + $$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.loc); + } + } + | DEFAULT COLON { + $$ = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error($1.loc, "cannot appear outside switch statement", "default", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error($1.loc, "cannot be nested inside control flow", "default", ""); + else + $$ = parseContext.intermediate.addBranch(EOpDefault, $1.loc); + } + ; + +iteration_statement + : iteration_statement_nonattributed { + $$ = $1; + } + + | attribute iteration_statement_nonattributed { + parseContext.handleLoopAttributes(*$1, $2); + $$ = $2; + } + + +iteration_statement_nonattributed + : WHILE LEFT_PAREN { + if (! parseContext.limits.whileLoops) + parseContext.error($1.loc, "while loops not available", "limitation", ""); + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + condition RIGHT_PAREN statement_no_new_scope { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.addLoop($6, $4, 0, true, $1.loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + | DO { + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { + if (! parseContext.limits.whileLoops) + parseContext.error($1.loc, "do-while loops not available", "limitation", ""); + + parseContext.boolCheck($8.loc, $6); + + $$ = parseContext.intermediate.addLoop($3, $6, 0, false, $4.loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + | FOR LEFT_PAREN { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } + for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.makeAggregate($4, $2.loc); + TIntermLoop* forLoop = parseContext.intermediate.addLoop($7, reinterpret_cast($5.node1), reinterpret_cast($5.node2), true, $1.loc); + if (! parseContext.limits.nonInductiveForLoops) + parseContext.inductiveLoopCheck($1.loc, $4, forLoop); + $$ = parseContext.intermediate.growAggregate($$, forLoop, $1.loc); + $$->getAsAggregate()->setOperator(EOpSequence); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } + ; + +for_init_statement + : expression_statement { + $$ = $1; + } + | declaration_statement { + $$ = $1; + } + ; + +conditionopt + : condition { + $$ = $1; + } + | /* May be null */ { + $$ = 0; + } + ; + +for_rest_statement + : conditionopt SEMICOLON { + $$.node1 = $1; + $$.node2 = 0; + } + | conditionopt SEMICOLON expression { + $$.node1 = $1; + $$.node2 = $3; + } + ; + +jump_statement + : CONTINUE SEMICOLON { + if (parseContext.loopNestingLevel <= 0) + parseContext.error($1.loc, "continue statement only allowed in loops", "", ""); + $$ = parseContext.intermediate.addBranch(EOpContinue, $1.loc); + } + | BREAK SEMICOLON { + if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) + parseContext.error($1.loc, "break statement only allowed in switch and loops", "", ""); + $$ = parseContext.intermediate.addBranch(EOpBreak, $1.loc); + } + | RETURN SEMICOLON { + $$ = parseContext.intermediate.addBranch(EOpReturn, $1.loc); + if (parseContext.currentFunctionType->getBasicType() != EbtVoid) + parseContext.error($1.loc, "non-void function must return a value", "return", ""); + if (parseContext.inMain) + parseContext.postEntryPointReturn = true; + } + | RETURN expression SEMICOLON { + $$ = parseContext.handleReturnValue($1.loc, $2); + } + | DISCARD SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "discard"); + $$ = parseContext.intermediate.addBranch(EOpKill, $1.loc); + } + | TERMINATE_INVOCATION SEMICOLON { + parseContext.requireStage($1.loc, EShLangFragment, "terminateInvocation"); + $$ = parseContext.intermediate.addBranch(EOpTerminateInvocation, $1.loc); + } + ; + +// Grammar Note: No 'goto'. Gotos are not supported. + +translation_unit + : external_declaration { + $$ = $1; + parseContext.intermediate.setTreeRoot($$); + } + | translation_unit external_declaration { + if ($2 != nullptr) { + $$ = parseContext.intermediate.growAggregate($1, $2); + parseContext.intermediate.setTreeRoot($$); + } + } + ; + +external_declaration + : function_definition { + $$ = $1; + } + | declaration { + $$ = $1; + } + + | SEMICOLON { + parseContext.requireProfile($1.loc, ~EEsProfile, "extraneous semicolon"); + parseContext.profileRequires($1.loc, ~EEsProfile, 460, nullptr, "extraneous semicolon"); + $$ = nullptr; + } + + ; + +function_definition + : function_prototype { + $1.function = parseContext.handleFunctionDeclarator($1.loc, *$1.function, false /* not prototype */); + $1.intermNode = parseContext.handleFunctionDefinition($1.loc, *$1.function); + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + } + compound_statement_no_new_scope { + // May be best done as post process phase on intermediate code + if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue) + parseContext.error($1.loc, "function does not return a value:", "", $1.function->getName().c_str()); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + $$ = parseContext.intermediate.growAggregate($1.intermNode, $3); + parseContext.intermediate.setAggregateOperator($$, EOpFunction, $1.function->getType(), $1.loc); + $$->getAsAggregate()->setName($1.function->getMangledName().c_str()); + + // store the pragma information for debug and optimize and other vendor specific + // information. This information can be queried from the parse tree + $$->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize); + $$->getAsAggregate()->setDebug(parseContext.contextPragma.debug); + $$->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable); + + // Set currentFunctionType to empty pointer when goes outside of the function + parseContext.currentFunctionType = nullptr; + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + } + ; + + +attribute + : LEFT_BRACKET LEFT_BRACKET attribute_list RIGHT_BRACKET RIGHT_BRACKET { + $$ = $3; + parseContext.requireExtensions($1.loc, 1, &E_GL_EXT_control_flow_attributes, "attribute"); + } + +attribute_list + : single_attribute { + $$ = $1; + } + | attribute_list COMMA single_attribute { + $$ = parseContext.mergeAttributes($1, $3); + } + +single_attribute + : IDENTIFIER { + $$ = parseContext.makeAttributes(*$1.string); + } + | IDENTIFIER LEFT_PAREN constant_expression RIGHT_PAREN { + $$ = parseContext.makeAttributes(*$1.string, $3); + } + + +%% diff --git a/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp b/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp new file mode 100644 index 0000000..ad7deb3 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp @@ -0,0 +1,11188 @@ +/* A Bison parser, made by GNU Bison 3.7.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "3.7.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 69 "glslang/MachineIndependent/glslang.y" + + +/* Based on: +ANSI C Yacc grammar + +In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a +matching Lex specification) for the April 30, 1985 draft version of the +ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that +original, as mentioned in the answer to question 17.25 of the comp.lang.c +FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z. + +I intend to keep this version as close to the current C Standard grammar as +possible; please let me know if you discover discrepancies. + +Jutta Degener, 1995 +*/ + +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "../Public/ShaderLang.h" +#include "attribute.h" + +using namespace glslang; + + +#line 97 "glslang/MachineIndependent/glslang_tab.cpp" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +#include "glslang_tab.cpp.h" +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_CONST = 3, /* CONST */ + YYSYMBOL_BOOL = 4, /* BOOL */ + YYSYMBOL_INT = 5, /* INT */ + YYSYMBOL_UINT = 6, /* UINT */ + YYSYMBOL_FLOAT = 7, /* FLOAT */ + YYSYMBOL_BVEC2 = 8, /* BVEC2 */ + YYSYMBOL_BVEC3 = 9, /* BVEC3 */ + YYSYMBOL_BVEC4 = 10, /* BVEC4 */ + YYSYMBOL_IVEC2 = 11, /* IVEC2 */ + YYSYMBOL_IVEC3 = 12, /* IVEC3 */ + YYSYMBOL_IVEC4 = 13, /* IVEC4 */ + YYSYMBOL_UVEC2 = 14, /* UVEC2 */ + YYSYMBOL_UVEC3 = 15, /* UVEC3 */ + YYSYMBOL_UVEC4 = 16, /* UVEC4 */ + YYSYMBOL_VEC2 = 17, /* VEC2 */ + YYSYMBOL_VEC3 = 18, /* VEC3 */ + YYSYMBOL_VEC4 = 19, /* VEC4 */ + YYSYMBOL_MAT2 = 20, /* MAT2 */ + YYSYMBOL_MAT3 = 21, /* MAT3 */ + YYSYMBOL_MAT4 = 22, /* MAT4 */ + YYSYMBOL_MAT2X2 = 23, /* MAT2X2 */ + YYSYMBOL_MAT2X3 = 24, /* MAT2X3 */ + YYSYMBOL_MAT2X4 = 25, /* MAT2X4 */ + YYSYMBOL_MAT3X2 = 26, /* MAT3X2 */ + YYSYMBOL_MAT3X3 = 27, /* MAT3X3 */ + YYSYMBOL_MAT3X4 = 28, /* MAT3X4 */ + YYSYMBOL_MAT4X2 = 29, /* MAT4X2 */ + YYSYMBOL_MAT4X3 = 30, /* MAT4X3 */ + YYSYMBOL_MAT4X4 = 31, /* MAT4X4 */ + YYSYMBOL_SAMPLER2D = 32, /* SAMPLER2D */ + YYSYMBOL_SAMPLER3D = 33, /* SAMPLER3D */ + YYSYMBOL_SAMPLERCUBE = 34, /* SAMPLERCUBE */ + YYSYMBOL_SAMPLER2DSHADOW = 35, /* SAMPLER2DSHADOW */ + YYSYMBOL_SAMPLERCUBESHADOW = 36, /* SAMPLERCUBESHADOW */ + YYSYMBOL_SAMPLER2DARRAY = 37, /* SAMPLER2DARRAY */ + YYSYMBOL_SAMPLER2DARRAYSHADOW = 38, /* SAMPLER2DARRAYSHADOW */ + YYSYMBOL_ISAMPLER2D = 39, /* ISAMPLER2D */ + YYSYMBOL_ISAMPLER3D = 40, /* ISAMPLER3D */ + YYSYMBOL_ISAMPLERCUBE = 41, /* ISAMPLERCUBE */ + YYSYMBOL_ISAMPLER2DARRAY = 42, /* ISAMPLER2DARRAY */ + YYSYMBOL_USAMPLER2D = 43, /* USAMPLER2D */ + YYSYMBOL_USAMPLER3D = 44, /* USAMPLER3D */ + YYSYMBOL_USAMPLERCUBE = 45, /* USAMPLERCUBE */ + YYSYMBOL_USAMPLER2DARRAY = 46, /* USAMPLER2DARRAY */ + YYSYMBOL_SAMPLER = 47, /* SAMPLER */ + YYSYMBOL_SAMPLERSHADOW = 48, /* SAMPLERSHADOW */ + YYSYMBOL_TEXTURE2D = 49, /* TEXTURE2D */ + YYSYMBOL_TEXTURE3D = 50, /* TEXTURE3D */ + YYSYMBOL_TEXTURECUBE = 51, /* TEXTURECUBE */ + YYSYMBOL_TEXTURE2DARRAY = 52, /* TEXTURE2DARRAY */ + YYSYMBOL_ITEXTURE2D = 53, /* ITEXTURE2D */ + YYSYMBOL_ITEXTURE3D = 54, /* ITEXTURE3D */ + YYSYMBOL_ITEXTURECUBE = 55, /* ITEXTURECUBE */ + YYSYMBOL_ITEXTURE2DARRAY = 56, /* ITEXTURE2DARRAY */ + YYSYMBOL_UTEXTURE2D = 57, /* UTEXTURE2D */ + YYSYMBOL_UTEXTURE3D = 58, /* UTEXTURE3D */ + YYSYMBOL_UTEXTURECUBE = 59, /* UTEXTURECUBE */ + YYSYMBOL_UTEXTURE2DARRAY = 60, /* UTEXTURE2DARRAY */ + YYSYMBOL_ATTRIBUTE = 61, /* ATTRIBUTE */ + YYSYMBOL_VARYING = 62, /* VARYING */ + YYSYMBOL_FLOAT16_T = 63, /* FLOAT16_T */ + YYSYMBOL_FLOAT32_T = 64, /* FLOAT32_T */ + YYSYMBOL_DOUBLE = 65, /* DOUBLE */ + YYSYMBOL_FLOAT64_T = 66, /* FLOAT64_T */ + YYSYMBOL_INT64_T = 67, /* INT64_T */ + YYSYMBOL_UINT64_T = 68, /* UINT64_T */ + YYSYMBOL_INT32_T = 69, /* INT32_T */ + YYSYMBOL_UINT32_T = 70, /* UINT32_T */ + YYSYMBOL_INT16_T = 71, /* INT16_T */ + YYSYMBOL_UINT16_T = 72, /* UINT16_T */ + YYSYMBOL_INT8_T = 73, /* INT8_T */ + YYSYMBOL_UINT8_T = 74, /* UINT8_T */ + YYSYMBOL_I64VEC2 = 75, /* I64VEC2 */ + YYSYMBOL_I64VEC3 = 76, /* I64VEC3 */ + YYSYMBOL_I64VEC4 = 77, /* I64VEC4 */ + YYSYMBOL_U64VEC2 = 78, /* U64VEC2 */ + YYSYMBOL_U64VEC3 = 79, /* U64VEC3 */ + YYSYMBOL_U64VEC4 = 80, /* U64VEC4 */ + YYSYMBOL_I32VEC2 = 81, /* I32VEC2 */ + YYSYMBOL_I32VEC3 = 82, /* I32VEC3 */ + YYSYMBOL_I32VEC4 = 83, /* I32VEC4 */ + YYSYMBOL_U32VEC2 = 84, /* U32VEC2 */ + YYSYMBOL_U32VEC3 = 85, /* U32VEC3 */ + YYSYMBOL_U32VEC4 = 86, /* U32VEC4 */ + YYSYMBOL_I16VEC2 = 87, /* I16VEC2 */ + YYSYMBOL_I16VEC3 = 88, /* I16VEC3 */ + YYSYMBOL_I16VEC4 = 89, /* I16VEC4 */ + YYSYMBOL_U16VEC2 = 90, /* U16VEC2 */ + YYSYMBOL_U16VEC3 = 91, /* U16VEC3 */ + YYSYMBOL_U16VEC4 = 92, /* U16VEC4 */ + YYSYMBOL_I8VEC2 = 93, /* I8VEC2 */ + YYSYMBOL_I8VEC3 = 94, /* I8VEC3 */ + YYSYMBOL_I8VEC4 = 95, /* I8VEC4 */ + YYSYMBOL_U8VEC2 = 96, /* U8VEC2 */ + YYSYMBOL_U8VEC3 = 97, /* U8VEC3 */ + YYSYMBOL_U8VEC4 = 98, /* U8VEC4 */ + YYSYMBOL_DVEC2 = 99, /* DVEC2 */ + YYSYMBOL_DVEC3 = 100, /* DVEC3 */ + YYSYMBOL_DVEC4 = 101, /* DVEC4 */ + YYSYMBOL_DMAT2 = 102, /* DMAT2 */ + YYSYMBOL_DMAT3 = 103, /* DMAT3 */ + YYSYMBOL_DMAT4 = 104, /* DMAT4 */ + YYSYMBOL_F16VEC2 = 105, /* F16VEC2 */ + YYSYMBOL_F16VEC3 = 106, /* F16VEC3 */ + YYSYMBOL_F16VEC4 = 107, /* F16VEC4 */ + YYSYMBOL_F16MAT2 = 108, /* F16MAT2 */ + YYSYMBOL_F16MAT3 = 109, /* F16MAT3 */ + YYSYMBOL_F16MAT4 = 110, /* F16MAT4 */ + YYSYMBOL_F32VEC2 = 111, /* F32VEC2 */ + YYSYMBOL_F32VEC3 = 112, /* F32VEC3 */ + YYSYMBOL_F32VEC4 = 113, /* F32VEC4 */ + YYSYMBOL_F32MAT2 = 114, /* F32MAT2 */ + YYSYMBOL_F32MAT3 = 115, /* F32MAT3 */ + YYSYMBOL_F32MAT4 = 116, /* F32MAT4 */ + YYSYMBOL_F64VEC2 = 117, /* F64VEC2 */ + YYSYMBOL_F64VEC3 = 118, /* F64VEC3 */ + YYSYMBOL_F64VEC4 = 119, /* F64VEC4 */ + YYSYMBOL_F64MAT2 = 120, /* F64MAT2 */ + YYSYMBOL_F64MAT3 = 121, /* F64MAT3 */ + YYSYMBOL_F64MAT4 = 122, /* F64MAT4 */ + YYSYMBOL_DMAT2X2 = 123, /* DMAT2X2 */ + YYSYMBOL_DMAT2X3 = 124, /* DMAT2X3 */ + YYSYMBOL_DMAT2X4 = 125, /* DMAT2X4 */ + YYSYMBOL_DMAT3X2 = 126, /* DMAT3X2 */ + YYSYMBOL_DMAT3X3 = 127, /* DMAT3X3 */ + YYSYMBOL_DMAT3X4 = 128, /* DMAT3X4 */ + YYSYMBOL_DMAT4X2 = 129, /* DMAT4X2 */ + YYSYMBOL_DMAT4X3 = 130, /* DMAT4X3 */ + YYSYMBOL_DMAT4X4 = 131, /* DMAT4X4 */ + YYSYMBOL_F16MAT2X2 = 132, /* F16MAT2X2 */ + YYSYMBOL_F16MAT2X3 = 133, /* F16MAT2X3 */ + YYSYMBOL_F16MAT2X4 = 134, /* F16MAT2X4 */ + YYSYMBOL_F16MAT3X2 = 135, /* F16MAT3X2 */ + YYSYMBOL_F16MAT3X3 = 136, /* F16MAT3X3 */ + YYSYMBOL_F16MAT3X4 = 137, /* F16MAT3X4 */ + YYSYMBOL_F16MAT4X2 = 138, /* F16MAT4X2 */ + YYSYMBOL_F16MAT4X3 = 139, /* F16MAT4X3 */ + YYSYMBOL_F16MAT4X4 = 140, /* F16MAT4X4 */ + YYSYMBOL_F32MAT2X2 = 141, /* F32MAT2X2 */ + YYSYMBOL_F32MAT2X3 = 142, /* F32MAT2X3 */ + YYSYMBOL_F32MAT2X4 = 143, /* F32MAT2X4 */ + YYSYMBOL_F32MAT3X2 = 144, /* F32MAT3X2 */ + YYSYMBOL_F32MAT3X3 = 145, /* F32MAT3X3 */ + YYSYMBOL_F32MAT3X4 = 146, /* F32MAT3X4 */ + YYSYMBOL_F32MAT4X2 = 147, /* F32MAT4X2 */ + YYSYMBOL_F32MAT4X3 = 148, /* F32MAT4X3 */ + YYSYMBOL_F32MAT4X4 = 149, /* F32MAT4X4 */ + YYSYMBOL_F64MAT2X2 = 150, /* F64MAT2X2 */ + YYSYMBOL_F64MAT2X3 = 151, /* F64MAT2X3 */ + YYSYMBOL_F64MAT2X4 = 152, /* F64MAT2X4 */ + YYSYMBOL_F64MAT3X2 = 153, /* F64MAT3X2 */ + YYSYMBOL_F64MAT3X3 = 154, /* F64MAT3X3 */ + YYSYMBOL_F64MAT3X4 = 155, /* F64MAT3X4 */ + YYSYMBOL_F64MAT4X2 = 156, /* F64MAT4X2 */ + YYSYMBOL_F64MAT4X3 = 157, /* F64MAT4X3 */ + YYSYMBOL_F64MAT4X4 = 158, /* F64MAT4X4 */ + YYSYMBOL_ATOMIC_UINT = 159, /* ATOMIC_UINT */ + YYSYMBOL_ACCSTRUCTNV = 160, /* ACCSTRUCTNV */ + YYSYMBOL_ACCSTRUCTEXT = 161, /* ACCSTRUCTEXT */ + YYSYMBOL_RAYQUERYEXT = 162, /* RAYQUERYEXT */ + YYSYMBOL_FCOOPMATNV = 163, /* FCOOPMATNV */ + YYSYMBOL_ICOOPMATNV = 164, /* ICOOPMATNV */ + YYSYMBOL_UCOOPMATNV = 165, /* UCOOPMATNV */ + YYSYMBOL_SAMPLERCUBEARRAY = 166, /* SAMPLERCUBEARRAY */ + YYSYMBOL_SAMPLERCUBEARRAYSHADOW = 167, /* SAMPLERCUBEARRAYSHADOW */ + YYSYMBOL_ISAMPLERCUBEARRAY = 168, /* ISAMPLERCUBEARRAY */ + YYSYMBOL_USAMPLERCUBEARRAY = 169, /* USAMPLERCUBEARRAY */ + YYSYMBOL_SAMPLER1D = 170, /* SAMPLER1D */ + YYSYMBOL_SAMPLER1DARRAY = 171, /* SAMPLER1DARRAY */ + YYSYMBOL_SAMPLER1DARRAYSHADOW = 172, /* SAMPLER1DARRAYSHADOW */ + YYSYMBOL_ISAMPLER1D = 173, /* ISAMPLER1D */ + YYSYMBOL_SAMPLER1DSHADOW = 174, /* SAMPLER1DSHADOW */ + YYSYMBOL_SAMPLER2DRECT = 175, /* SAMPLER2DRECT */ + YYSYMBOL_SAMPLER2DRECTSHADOW = 176, /* SAMPLER2DRECTSHADOW */ + YYSYMBOL_ISAMPLER2DRECT = 177, /* ISAMPLER2DRECT */ + YYSYMBOL_USAMPLER2DRECT = 178, /* USAMPLER2DRECT */ + YYSYMBOL_SAMPLERBUFFER = 179, /* SAMPLERBUFFER */ + YYSYMBOL_ISAMPLERBUFFER = 180, /* ISAMPLERBUFFER */ + YYSYMBOL_USAMPLERBUFFER = 181, /* USAMPLERBUFFER */ + YYSYMBOL_SAMPLER2DMS = 182, /* SAMPLER2DMS */ + YYSYMBOL_ISAMPLER2DMS = 183, /* ISAMPLER2DMS */ + YYSYMBOL_USAMPLER2DMS = 184, /* USAMPLER2DMS */ + YYSYMBOL_SAMPLER2DMSARRAY = 185, /* SAMPLER2DMSARRAY */ + YYSYMBOL_ISAMPLER2DMSARRAY = 186, /* ISAMPLER2DMSARRAY */ + YYSYMBOL_USAMPLER2DMSARRAY = 187, /* USAMPLER2DMSARRAY */ + YYSYMBOL_SAMPLEREXTERNALOES = 188, /* SAMPLEREXTERNALOES */ + YYSYMBOL_SAMPLEREXTERNAL2DY2YEXT = 189, /* SAMPLEREXTERNAL2DY2YEXT */ + YYSYMBOL_ISAMPLER1DARRAY = 190, /* ISAMPLER1DARRAY */ + YYSYMBOL_USAMPLER1D = 191, /* USAMPLER1D */ + YYSYMBOL_USAMPLER1DARRAY = 192, /* USAMPLER1DARRAY */ + YYSYMBOL_F16SAMPLER1D = 193, /* F16SAMPLER1D */ + YYSYMBOL_F16SAMPLER2D = 194, /* F16SAMPLER2D */ + YYSYMBOL_F16SAMPLER3D = 195, /* F16SAMPLER3D */ + YYSYMBOL_F16SAMPLER2DRECT = 196, /* F16SAMPLER2DRECT */ + YYSYMBOL_F16SAMPLERCUBE = 197, /* F16SAMPLERCUBE */ + YYSYMBOL_F16SAMPLER1DARRAY = 198, /* F16SAMPLER1DARRAY */ + YYSYMBOL_F16SAMPLER2DARRAY = 199, /* F16SAMPLER2DARRAY */ + YYSYMBOL_F16SAMPLERCUBEARRAY = 200, /* F16SAMPLERCUBEARRAY */ + YYSYMBOL_F16SAMPLERBUFFER = 201, /* F16SAMPLERBUFFER */ + YYSYMBOL_F16SAMPLER2DMS = 202, /* F16SAMPLER2DMS */ + YYSYMBOL_F16SAMPLER2DMSARRAY = 203, /* F16SAMPLER2DMSARRAY */ + YYSYMBOL_F16SAMPLER1DSHADOW = 204, /* F16SAMPLER1DSHADOW */ + YYSYMBOL_F16SAMPLER2DSHADOW = 205, /* F16SAMPLER2DSHADOW */ + YYSYMBOL_F16SAMPLER1DARRAYSHADOW = 206, /* F16SAMPLER1DARRAYSHADOW */ + YYSYMBOL_F16SAMPLER2DARRAYSHADOW = 207, /* F16SAMPLER2DARRAYSHADOW */ + YYSYMBOL_F16SAMPLER2DRECTSHADOW = 208, /* F16SAMPLER2DRECTSHADOW */ + YYSYMBOL_F16SAMPLERCUBESHADOW = 209, /* F16SAMPLERCUBESHADOW */ + YYSYMBOL_F16SAMPLERCUBEARRAYSHADOW = 210, /* F16SAMPLERCUBEARRAYSHADOW */ + YYSYMBOL_IMAGE1D = 211, /* IMAGE1D */ + YYSYMBOL_IIMAGE1D = 212, /* IIMAGE1D */ + YYSYMBOL_UIMAGE1D = 213, /* UIMAGE1D */ + YYSYMBOL_IMAGE2D = 214, /* IMAGE2D */ + YYSYMBOL_IIMAGE2D = 215, /* IIMAGE2D */ + YYSYMBOL_UIMAGE2D = 216, /* UIMAGE2D */ + YYSYMBOL_IMAGE3D = 217, /* IMAGE3D */ + YYSYMBOL_IIMAGE3D = 218, /* IIMAGE3D */ + YYSYMBOL_UIMAGE3D = 219, /* UIMAGE3D */ + YYSYMBOL_IMAGE2DRECT = 220, /* IMAGE2DRECT */ + YYSYMBOL_IIMAGE2DRECT = 221, /* IIMAGE2DRECT */ + YYSYMBOL_UIMAGE2DRECT = 222, /* UIMAGE2DRECT */ + YYSYMBOL_IMAGECUBE = 223, /* IMAGECUBE */ + YYSYMBOL_IIMAGECUBE = 224, /* IIMAGECUBE */ + YYSYMBOL_UIMAGECUBE = 225, /* UIMAGECUBE */ + YYSYMBOL_IMAGEBUFFER = 226, /* IMAGEBUFFER */ + YYSYMBOL_IIMAGEBUFFER = 227, /* IIMAGEBUFFER */ + YYSYMBOL_UIMAGEBUFFER = 228, /* UIMAGEBUFFER */ + YYSYMBOL_IMAGE1DARRAY = 229, /* IMAGE1DARRAY */ + YYSYMBOL_IIMAGE1DARRAY = 230, /* IIMAGE1DARRAY */ + YYSYMBOL_UIMAGE1DARRAY = 231, /* UIMAGE1DARRAY */ + YYSYMBOL_IMAGE2DARRAY = 232, /* IMAGE2DARRAY */ + YYSYMBOL_IIMAGE2DARRAY = 233, /* IIMAGE2DARRAY */ + YYSYMBOL_UIMAGE2DARRAY = 234, /* UIMAGE2DARRAY */ + YYSYMBOL_IMAGECUBEARRAY = 235, /* IMAGECUBEARRAY */ + YYSYMBOL_IIMAGECUBEARRAY = 236, /* IIMAGECUBEARRAY */ + YYSYMBOL_UIMAGECUBEARRAY = 237, /* UIMAGECUBEARRAY */ + YYSYMBOL_IMAGE2DMS = 238, /* IMAGE2DMS */ + YYSYMBOL_IIMAGE2DMS = 239, /* IIMAGE2DMS */ + YYSYMBOL_UIMAGE2DMS = 240, /* UIMAGE2DMS */ + YYSYMBOL_IMAGE2DMSARRAY = 241, /* IMAGE2DMSARRAY */ + YYSYMBOL_IIMAGE2DMSARRAY = 242, /* IIMAGE2DMSARRAY */ + YYSYMBOL_UIMAGE2DMSARRAY = 243, /* UIMAGE2DMSARRAY */ + YYSYMBOL_F16IMAGE1D = 244, /* F16IMAGE1D */ + YYSYMBOL_F16IMAGE2D = 245, /* F16IMAGE2D */ + YYSYMBOL_F16IMAGE3D = 246, /* F16IMAGE3D */ + YYSYMBOL_F16IMAGE2DRECT = 247, /* F16IMAGE2DRECT */ + YYSYMBOL_F16IMAGECUBE = 248, /* F16IMAGECUBE */ + YYSYMBOL_F16IMAGE1DARRAY = 249, /* F16IMAGE1DARRAY */ + YYSYMBOL_F16IMAGE2DARRAY = 250, /* F16IMAGE2DARRAY */ + YYSYMBOL_F16IMAGECUBEARRAY = 251, /* F16IMAGECUBEARRAY */ + YYSYMBOL_F16IMAGEBUFFER = 252, /* F16IMAGEBUFFER */ + YYSYMBOL_F16IMAGE2DMS = 253, /* F16IMAGE2DMS */ + YYSYMBOL_F16IMAGE2DMSARRAY = 254, /* F16IMAGE2DMSARRAY */ + YYSYMBOL_I64IMAGE1D = 255, /* I64IMAGE1D */ + YYSYMBOL_U64IMAGE1D = 256, /* U64IMAGE1D */ + YYSYMBOL_I64IMAGE2D = 257, /* I64IMAGE2D */ + YYSYMBOL_U64IMAGE2D = 258, /* U64IMAGE2D */ + YYSYMBOL_I64IMAGE3D = 259, /* I64IMAGE3D */ + YYSYMBOL_U64IMAGE3D = 260, /* U64IMAGE3D */ + YYSYMBOL_I64IMAGE2DRECT = 261, /* I64IMAGE2DRECT */ + YYSYMBOL_U64IMAGE2DRECT = 262, /* U64IMAGE2DRECT */ + YYSYMBOL_I64IMAGECUBE = 263, /* I64IMAGECUBE */ + YYSYMBOL_U64IMAGECUBE = 264, /* U64IMAGECUBE */ + YYSYMBOL_I64IMAGEBUFFER = 265, /* I64IMAGEBUFFER */ + YYSYMBOL_U64IMAGEBUFFER = 266, /* U64IMAGEBUFFER */ + YYSYMBOL_I64IMAGE1DARRAY = 267, /* I64IMAGE1DARRAY */ + YYSYMBOL_U64IMAGE1DARRAY = 268, /* U64IMAGE1DARRAY */ + YYSYMBOL_I64IMAGE2DARRAY = 269, /* I64IMAGE2DARRAY */ + YYSYMBOL_U64IMAGE2DARRAY = 270, /* U64IMAGE2DARRAY */ + YYSYMBOL_I64IMAGECUBEARRAY = 271, /* I64IMAGECUBEARRAY */ + YYSYMBOL_U64IMAGECUBEARRAY = 272, /* U64IMAGECUBEARRAY */ + YYSYMBOL_I64IMAGE2DMS = 273, /* I64IMAGE2DMS */ + YYSYMBOL_U64IMAGE2DMS = 274, /* U64IMAGE2DMS */ + YYSYMBOL_I64IMAGE2DMSARRAY = 275, /* I64IMAGE2DMSARRAY */ + YYSYMBOL_U64IMAGE2DMSARRAY = 276, /* U64IMAGE2DMSARRAY */ + YYSYMBOL_TEXTURECUBEARRAY = 277, /* TEXTURECUBEARRAY */ + YYSYMBOL_ITEXTURECUBEARRAY = 278, /* ITEXTURECUBEARRAY */ + YYSYMBOL_UTEXTURECUBEARRAY = 279, /* UTEXTURECUBEARRAY */ + YYSYMBOL_TEXTURE1D = 280, /* TEXTURE1D */ + YYSYMBOL_ITEXTURE1D = 281, /* ITEXTURE1D */ + YYSYMBOL_UTEXTURE1D = 282, /* UTEXTURE1D */ + YYSYMBOL_TEXTURE1DARRAY = 283, /* TEXTURE1DARRAY */ + YYSYMBOL_ITEXTURE1DARRAY = 284, /* ITEXTURE1DARRAY */ + YYSYMBOL_UTEXTURE1DARRAY = 285, /* UTEXTURE1DARRAY */ + YYSYMBOL_TEXTURE2DRECT = 286, /* TEXTURE2DRECT */ + YYSYMBOL_ITEXTURE2DRECT = 287, /* ITEXTURE2DRECT */ + YYSYMBOL_UTEXTURE2DRECT = 288, /* UTEXTURE2DRECT */ + YYSYMBOL_TEXTUREBUFFER = 289, /* TEXTUREBUFFER */ + YYSYMBOL_ITEXTUREBUFFER = 290, /* ITEXTUREBUFFER */ + YYSYMBOL_UTEXTUREBUFFER = 291, /* UTEXTUREBUFFER */ + YYSYMBOL_TEXTURE2DMS = 292, /* TEXTURE2DMS */ + YYSYMBOL_ITEXTURE2DMS = 293, /* ITEXTURE2DMS */ + YYSYMBOL_UTEXTURE2DMS = 294, /* UTEXTURE2DMS */ + YYSYMBOL_TEXTURE2DMSARRAY = 295, /* TEXTURE2DMSARRAY */ + YYSYMBOL_ITEXTURE2DMSARRAY = 296, /* ITEXTURE2DMSARRAY */ + YYSYMBOL_UTEXTURE2DMSARRAY = 297, /* UTEXTURE2DMSARRAY */ + YYSYMBOL_F16TEXTURE1D = 298, /* F16TEXTURE1D */ + YYSYMBOL_F16TEXTURE2D = 299, /* F16TEXTURE2D */ + YYSYMBOL_F16TEXTURE3D = 300, /* F16TEXTURE3D */ + YYSYMBOL_F16TEXTURE2DRECT = 301, /* F16TEXTURE2DRECT */ + YYSYMBOL_F16TEXTURECUBE = 302, /* F16TEXTURECUBE */ + YYSYMBOL_F16TEXTURE1DARRAY = 303, /* F16TEXTURE1DARRAY */ + YYSYMBOL_F16TEXTURE2DARRAY = 304, /* F16TEXTURE2DARRAY */ + YYSYMBOL_F16TEXTURECUBEARRAY = 305, /* F16TEXTURECUBEARRAY */ + YYSYMBOL_F16TEXTUREBUFFER = 306, /* F16TEXTUREBUFFER */ + YYSYMBOL_F16TEXTURE2DMS = 307, /* F16TEXTURE2DMS */ + YYSYMBOL_F16TEXTURE2DMSARRAY = 308, /* F16TEXTURE2DMSARRAY */ + YYSYMBOL_SUBPASSINPUT = 309, /* SUBPASSINPUT */ + YYSYMBOL_SUBPASSINPUTMS = 310, /* SUBPASSINPUTMS */ + YYSYMBOL_ISUBPASSINPUT = 311, /* ISUBPASSINPUT */ + YYSYMBOL_ISUBPASSINPUTMS = 312, /* ISUBPASSINPUTMS */ + YYSYMBOL_USUBPASSINPUT = 313, /* USUBPASSINPUT */ + YYSYMBOL_USUBPASSINPUTMS = 314, /* USUBPASSINPUTMS */ + YYSYMBOL_F16SUBPASSINPUT = 315, /* F16SUBPASSINPUT */ + YYSYMBOL_F16SUBPASSINPUTMS = 316, /* F16SUBPASSINPUTMS */ + YYSYMBOL_LEFT_OP = 317, /* LEFT_OP */ + YYSYMBOL_RIGHT_OP = 318, /* RIGHT_OP */ + YYSYMBOL_INC_OP = 319, /* INC_OP */ + YYSYMBOL_DEC_OP = 320, /* DEC_OP */ + YYSYMBOL_LE_OP = 321, /* LE_OP */ + YYSYMBOL_GE_OP = 322, /* GE_OP */ + YYSYMBOL_EQ_OP = 323, /* EQ_OP */ + YYSYMBOL_NE_OP = 324, /* NE_OP */ + YYSYMBOL_AND_OP = 325, /* AND_OP */ + YYSYMBOL_OR_OP = 326, /* OR_OP */ + YYSYMBOL_XOR_OP = 327, /* XOR_OP */ + YYSYMBOL_MUL_ASSIGN = 328, /* MUL_ASSIGN */ + YYSYMBOL_DIV_ASSIGN = 329, /* DIV_ASSIGN */ + YYSYMBOL_ADD_ASSIGN = 330, /* ADD_ASSIGN */ + YYSYMBOL_MOD_ASSIGN = 331, /* MOD_ASSIGN */ + YYSYMBOL_LEFT_ASSIGN = 332, /* LEFT_ASSIGN */ + YYSYMBOL_RIGHT_ASSIGN = 333, /* RIGHT_ASSIGN */ + YYSYMBOL_AND_ASSIGN = 334, /* AND_ASSIGN */ + YYSYMBOL_XOR_ASSIGN = 335, /* XOR_ASSIGN */ + YYSYMBOL_OR_ASSIGN = 336, /* OR_ASSIGN */ + YYSYMBOL_SUB_ASSIGN = 337, /* SUB_ASSIGN */ + YYSYMBOL_STRING_LITERAL = 338, /* STRING_LITERAL */ + YYSYMBOL_LEFT_PAREN = 339, /* LEFT_PAREN */ + YYSYMBOL_RIGHT_PAREN = 340, /* RIGHT_PAREN */ + YYSYMBOL_LEFT_BRACKET = 341, /* LEFT_BRACKET */ + YYSYMBOL_RIGHT_BRACKET = 342, /* RIGHT_BRACKET */ + YYSYMBOL_LEFT_BRACE = 343, /* LEFT_BRACE */ + YYSYMBOL_RIGHT_BRACE = 344, /* RIGHT_BRACE */ + YYSYMBOL_DOT = 345, /* DOT */ + YYSYMBOL_COMMA = 346, /* COMMA */ + YYSYMBOL_COLON = 347, /* COLON */ + YYSYMBOL_EQUAL = 348, /* EQUAL */ + YYSYMBOL_SEMICOLON = 349, /* SEMICOLON */ + YYSYMBOL_BANG = 350, /* BANG */ + YYSYMBOL_DASH = 351, /* DASH */ + YYSYMBOL_TILDE = 352, /* TILDE */ + YYSYMBOL_PLUS = 353, /* PLUS */ + YYSYMBOL_STAR = 354, /* STAR */ + YYSYMBOL_SLASH = 355, /* SLASH */ + YYSYMBOL_PERCENT = 356, /* PERCENT */ + YYSYMBOL_LEFT_ANGLE = 357, /* LEFT_ANGLE */ + YYSYMBOL_RIGHT_ANGLE = 358, /* RIGHT_ANGLE */ + YYSYMBOL_VERTICAL_BAR = 359, /* VERTICAL_BAR */ + YYSYMBOL_CARET = 360, /* CARET */ + YYSYMBOL_AMPERSAND = 361, /* AMPERSAND */ + YYSYMBOL_QUESTION = 362, /* QUESTION */ + YYSYMBOL_INVARIANT = 363, /* INVARIANT */ + YYSYMBOL_HIGH_PRECISION = 364, /* HIGH_PRECISION */ + YYSYMBOL_MEDIUM_PRECISION = 365, /* MEDIUM_PRECISION */ + YYSYMBOL_LOW_PRECISION = 366, /* LOW_PRECISION */ + YYSYMBOL_PRECISION = 367, /* PRECISION */ + YYSYMBOL_PACKED = 368, /* PACKED */ + YYSYMBOL_RESOURCE = 369, /* RESOURCE */ + YYSYMBOL_SUPERP = 370, /* SUPERP */ + YYSYMBOL_FLOATCONSTANT = 371, /* FLOATCONSTANT */ + YYSYMBOL_INTCONSTANT = 372, /* INTCONSTANT */ + YYSYMBOL_UINTCONSTANT = 373, /* UINTCONSTANT */ + YYSYMBOL_BOOLCONSTANT = 374, /* BOOLCONSTANT */ + YYSYMBOL_IDENTIFIER = 375, /* IDENTIFIER */ + YYSYMBOL_TYPE_NAME = 376, /* TYPE_NAME */ + YYSYMBOL_CENTROID = 377, /* CENTROID */ + YYSYMBOL_IN = 378, /* IN */ + YYSYMBOL_OUT = 379, /* OUT */ + YYSYMBOL_INOUT = 380, /* INOUT */ + YYSYMBOL_STRUCT = 381, /* STRUCT */ + YYSYMBOL_VOID = 382, /* VOID */ + YYSYMBOL_WHILE = 383, /* WHILE */ + YYSYMBOL_BREAK = 384, /* BREAK */ + YYSYMBOL_CONTINUE = 385, /* CONTINUE */ + YYSYMBOL_DO = 386, /* DO */ + YYSYMBOL_ELSE = 387, /* ELSE */ + YYSYMBOL_FOR = 388, /* FOR */ + YYSYMBOL_IF = 389, /* IF */ + YYSYMBOL_DISCARD = 390, /* DISCARD */ + YYSYMBOL_RETURN = 391, /* RETURN */ + YYSYMBOL_SWITCH = 392, /* SWITCH */ + YYSYMBOL_CASE = 393, /* CASE */ + YYSYMBOL_DEFAULT = 394, /* DEFAULT */ + YYSYMBOL_TERMINATE_INVOCATION = 395, /* TERMINATE_INVOCATION */ + YYSYMBOL_UNIFORM = 396, /* UNIFORM */ + YYSYMBOL_SHARED = 397, /* SHARED */ + YYSYMBOL_BUFFER = 398, /* BUFFER */ + YYSYMBOL_FLAT = 399, /* FLAT */ + YYSYMBOL_SMOOTH = 400, /* SMOOTH */ + YYSYMBOL_LAYOUT = 401, /* LAYOUT */ + YYSYMBOL_DOUBLECONSTANT = 402, /* DOUBLECONSTANT */ + YYSYMBOL_INT16CONSTANT = 403, /* INT16CONSTANT */ + YYSYMBOL_UINT16CONSTANT = 404, /* UINT16CONSTANT */ + YYSYMBOL_FLOAT16CONSTANT = 405, /* FLOAT16CONSTANT */ + YYSYMBOL_INT32CONSTANT = 406, /* INT32CONSTANT */ + YYSYMBOL_UINT32CONSTANT = 407, /* UINT32CONSTANT */ + YYSYMBOL_INT64CONSTANT = 408, /* INT64CONSTANT */ + YYSYMBOL_UINT64CONSTANT = 409, /* UINT64CONSTANT */ + YYSYMBOL_SUBROUTINE = 410, /* SUBROUTINE */ + YYSYMBOL_DEMOTE = 411, /* DEMOTE */ + YYSYMBOL_PAYLOADNV = 412, /* PAYLOADNV */ + YYSYMBOL_PAYLOADINNV = 413, /* PAYLOADINNV */ + YYSYMBOL_HITATTRNV = 414, /* HITATTRNV */ + YYSYMBOL_CALLDATANV = 415, /* CALLDATANV */ + YYSYMBOL_CALLDATAINNV = 416, /* CALLDATAINNV */ + YYSYMBOL_PAYLOADEXT = 417, /* PAYLOADEXT */ + YYSYMBOL_PAYLOADINEXT = 418, /* PAYLOADINEXT */ + YYSYMBOL_HITATTREXT = 419, /* HITATTREXT */ + YYSYMBOL_CALLDATAEXT = 420, /* CALLDATAEXT */ + YYSYMBOL_CALLDATAINEXT = 421, /* CALLDATAINEXT */ + YYSYMBOL_PATCH = 422, /* PATCH */ + YYSYMBOL_SAMPLE = 423, /* SAMPLE */ + YYSYMBOL_NONUNIFORM = 424, /* NONUNIFORM */ + YYSYMBOL_COHERENT = 425, /* COHERENT */ + YYSYMBOL_VOLATILE = 426, /* VOLATILE */ + YYSYMBOL_RESTRICT = 427, /* RESTRICT */ + YYSYMBOL_READONLY = 428, /* READONLY */ + YYSYMBOL_WRITEONLY = 429, /* WRITEONLY */ + YYSYMBOL_DEVICECOHERENT = 430, /* DEVICECOHERENT */ + YYSYMBOL_QUEUEFAMILYCOHERENT = 431, /* QUEUEFAMILYCOHERENT */ + YYSYMBOL_WORKGROUPCOHERENT = 432, /* WORKGROUPCOHERENT */ + YYSYMBOL_SUBGROUPCOHERENT = 433, /* SUBGROUPCOHERENT */ + YYSYMBOL_NONPRIVATE = 434, /* NONPRIVATE */ + YYSYMBOL_SHADERCALLCOHERENT = 435, /* SHADERCALLCOHERENT */ + YYSYMBOL_NOPERSPECTIVE = 436, /* NOPERSPECTIVE */ + YYSYMBOL_EXPLICITINTERPAMD = 437, /* EXPLICITINTERPAMD */ + YYSYMBOL_PERVERTEXNV = 438, /* PERVERTEXNV */ + YYSYMBOL_PERPRIMITIVENV = 439, /* PERPRIMITIVENV */ + YYSYMBOL_PERVIEWNV = 440, /* PERVIEWNV */ + YYSYMBOL_PERTASKNV = 441, /* PERTASKNV */ + YYSYMBOL_PRECISE = 442, /* PRECISE */ + YYSYMBOL_YYACCEPT = 443, /* $accept */ + YYSYMBOL_variable_identifier = 444, /* variable_identifier */ + YYSYMBOL_primary_expression = 445, /* primary_expression */ + YYSYMBOL_postfix_expression = 446, /* postfix_expression */ + YYSYMBOL_integer_expression = 447, /* integer_expression */ + YYSYMBOL_function_call = 448, /* function_call */ + YYSYMBOL_function_call_or_method = 449, /* function_call_or_method */ + YYSYMBOL_function_call_generic = 450, /* function_call_generic */ + YYSYMBOL_function_call_header_no_parameters = 451, /* function_call_header_no_parameters */ + YYSYMBOL_function_call_header_with_parameters = 452, /* function_call_header_with_parameters */ + YYSYMBOL_function_call_header = 453, /* function_call_header */ + YYSYMBOL_function_identifier = 454, /* function_identifier */ + YYSYMBOL_unary_expression = 455, /* unary_expression */ + YYSYMBOL_unary_operator = 456, /* unary_operator */ + YYSYMBOL_multiplicative_expression = 457, /* multiplicative_expression */ + YYSYMBOL_additive_expression = 458, /* additive_expression */ + YYSYMBOL_shift_expression = 459, /* shift_expression */ + YYSYMBOL_relational_expression = 460, /* relational_expression */ + YYSYMBOL_equality_expression = 461, /* equality_expression */ + YYSYMBOL_and_expression = 462, /* and_expression */ + YYSYMBOL_exclusive_or_expression = 463, /* exclusive_or_expression */ + YYSYMBOL_inclusive_or_expression = 464, /* inclusive_or_expression */ + YYSYMBOL_logical_and_expression = 465, /* logical_and_expression */ + YYSYMBOL_logical_xor_expression = 466, /* logical_xor_expression */ + YYSYMBOL_logical_or_expression = 467, /* logical_or_expression */ + YYSYMBOL_conditional_expression = 468, /* conditional_expression */ + YYSYMBOL_469_1 = 469, /* $@1 */ + YYSYMBOL_assignment_expression = 470, /* assignment_expression */ + YYSYMBOL_assignment_operator = 471, /* assignment_operator */ + YYSYMBOL_expression = 472, /* expression */ + YYSYMBOL_constant_expression = 473, /* constant_expression */ + YYSYMBOL_declaration = 474, /* declaration */ + YYSYMBOL_block_structure = 475, /* block_structure */ + YYSYMBOL_476_2 = 476, /* $@2 */ + YYSYMBOL_identifier_list = 477, /* identifier_list */ + YYSYMBOL_function_prototype = 478, /* function_prototype */ + YYSYMBOL_function_declarator = 479, /* function_declarator */ + YYSYMBOL_function_header_with_parameters = 480, /* function_header_with_parameters */ + YYSYMBOL_function_header = 481, /* function_header */ + YYSYMBOL_parameter_declarator = 482, /* parameter_declarator */ + YYSYMBOL_parameter_declaration = 483, /* parameter_declaration */ + YYSYMBOL_parameter_type_specifier = 484, /* parameter_type_specifier */ + YYSYMBOL_init_declarator_list = 485, /* init_declarator_list */ + YYSYMBOL_single_declaration = 486, /* single_declaration */ + YYSYMBOL_fully_specified_type = 487, /* fully_specified_type */ + YYSYMBOL_invariant_qualifier = 488, /* invariant_qualifier */ + YYSYMBOL_interpolation_qualifier = 489, /* interpolation_qualifier */ + YYSYMBOL_layout_qualifier = 490, /* layout_qualifier */ + YYSYMBOL_layout_qualifier_id_list = 491, /* layout_qualifier_id_list */ + YYSYMBOL_layout_qualifier_id = 492, /* layout_qualifier_id */ + YYSYMBOL_precise_qualifier = 493, /* precise_qualifier */ + YYSYMBOL_type_qualifier = 494, /* type_qualifier */ + YYSYMBOL_single_type_qualifier = 495, /* single_type_qualifier */ + YYSYMBOL_storage_qualifier = 496, /* storage_qualifier */ + YYSYMBOL_non_uniform_qualifier = 497, /* non_uniform_qualifier */ + YYSYMBOL_type_name_list = 498, /* type_name_list */ + YYSYMBOL_type_specifier = 499, /* type_specifier */ + YYSYMBOL_array_specifier = 500, /* array_specifier */ + YYSYMBOL_type_parameter_specifier_opt = 501, /* type_parameter_specifier_opt */ + YYSYMBOL_type_parameter_specifier = 502, /* type_parameter_specifier */ + YYSYMBOL_type_parameter_specifier_list = 503, /* type_parameter_specifier_list */ + YYSYMBOL_type_specifier_nonarray = 504, /* type_specifier_nonarray */ + YYSYMBOL_precision_qualifier = 505, /* precision_qualifier */ + YYSYMBOL_struct_specifier = 506, /* struct_specifier */ + YYSYMBOL_507_3 = 507, /* $@3 */ + YYSYMBOL_508_4 = 508, /* $@4 */ + YYSYMBOL_struct_declaration_list = 509, /* struct_declaration_list */ + YYSYMBOL_struct_declaration = 510, /* struct_declaration */ + YYSYMBOL_struct_declarator_list = 511, /* struct_declarator_list */ + YYSYMBOL_struct_declarator = 512, /* struct_declarator */ + YYSYMBOL_initializer = 513, /* initializer */ + YYSYMBOL_initializer_list = 514, /* initializer_list */ + YYSYMBOL_declaration_statement = 515, /* declaration_statement */ + YYSYMBOL_statement = 516, /* statement */ + YYSYMBOL_simple_statement = 517, /* simple_statement */ + YYSYMBOL_demote_statement = 518, /* demote_statement */ + YYSYMBOL_compound_statement = 519, /* compound_statement */ + YYSYMBOL_520_5 = 520, /* $@5 */ + YYSYMBOL_521_6 = 521, /* $@6 */ + YYSYMBOL_statement_no_new_scope = 522, /* statement_no_new_scope */ + YYSYMBOL_statement_scoped = 523, /* statement_scoped */ + YYSYMBOL_524_7 = 524, /* $@7 */ + YYSYMBOL_525_8 = 525, /* $@8 */ + YYSYMBOL_compound_statement_no_new_scope = 526, /* compound_statement_no_new_scope */ + YYSYMBOL_statement_list = 527, /* statement_list */ + YYSYMBOL_expression_statement = 528, /* expression_statement */ + YYSYMBOL_selection_statement = 529, /* selection_statement */ + YYSYMBOL_selection_statement_nonattributed = 530, /* selection_statement_nonattributed */ + YYSYMBOL_selection_rest_statement = 531, /* selection_rest_statement */ + YYSYMBOL_condition = 532, /* condition */ + YYSYMBOL_switch_statement = 533, /* switch_statement */ + YYSYMBOL_switch_statement_nonattributed = 534, /* switch_statement_nonattributed */ + YYSYMBOL_535_9 = 535, /* $@9 */ + YYSYMBOL_switch_statement_list = 536, /* switch_statement_list */ + YYSYMBOL_case_label = 537, /* case_label */ + YYSYMBOL_iteration_statement = 538, /* iteration_statement */ + YYSYMBOL_iteration_statement_nonattributed = 539, /* iteration_statement_nonattributed */ + YYSYMBOL_540_10 = 540, /* $@10 */ + YYSYMBOL_541_11 = 541, /* $@11 */ + YYSYMBOL_542_12 = 542, /* $@12 */ + YYSYMBOL_for_init_statement = 543, /* for_init_statement */ + YYSYMBOL_conditionopt = 544, /* conditionopt */ + YYSYMBOL_for_rest_statement = 545, /* for_rest_statement */ + YYSYMBOL_jump_statement = 546, /* jump_statement */ + YYSYMBOL_translation_unit = 547, /* translation_unit */ + YYSYMBOL_external_declaration = 548, /* external_declaration */ + YYSYMBOL_function_definition = 549, /* function_definition */ + YYSYMBOL_550_13 = 550, /* $@13 */ + YYSYMBOL_attribute = 551, /* attribute */ + YYSYMBOL_attribute_list = 552, /* attribute_list */ + YYSYMBOL_single_attribute = 553 /* single_attribute */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + +/* Second part of user prologue. */ +#line 133 "glslang/MachineIndependent/glslang.y" + + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4065) + #pragma warning(disable : 4127) + #pragma warning(disable : 4244) +#endif + +#define parseContext (*pParseContext) +#define yyerror(context, msg) context->parserError(msg) + +extern int yylex(YYSTYPE*, TParseContext&); + + +#line 700 "glslang/MachineIndependent/glslang_tab.cpp" + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int16 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(E) ((void) (E)) +#else +# define YYUSE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 416 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 10058 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 443 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 111 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 614 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 760 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 697 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int16 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 442 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 370, 370, 376, 379, 384, 387, 390, 394, 398, + 401, 405, 409, 413, 417, 421, 425, 431, 439, 442, + 445, 448, 451, 456, 464, 471, 478, 484, 488, 495, + 498, 504, 511, 521, 529, 534, 562, 571, 577, 581, + 585, 605, 606, 607, 608, 614, 615, 620, 625, 634, + 635, 640, 648, 649, 655, 664, 665, 670, 675, 680, + 688, 689, 698, 710, 711, 720, 721, 730, 731, 740, + 741, 749, 750, 758, 759, 767, 768, 768, 786, 787, + 803, 807, 811, 815, 820, 824, 828, 832, 836, 840, + 844, 851, 854, 865, 872, 877, 882, 889, 893, 897, + 901, 906, 911, 920, 920, 931, 935, 942, 949, 952, + 959, 967, 987, 1010, 1025, 1050, 1061, 1071, 1081, 1091, + 1100, 1103, 1107, 1111, 1116, 1124, 1131, 1136, 1141, 1146, + 1155, 1165, 1192, 1201, 1208, 1216, 1223, 1230, 1238, 1248, + 1255, 1266, 1272, 1275, 1282, 1286, 1290, 1299, 1309, 1312, + 1323, 1326, 1329, 1333, 1337, 1342, 1346, 1353, 1357, 1362, + 1368, 1374, 1381, 1386, 1394, 1400, 1412, 1426, 1432, 1437, + 1445, 1453, 1461, 1469, 1477, 1485, 1493, 1501, 1508, 1515, + 1519, 1524, 1529, 1534, 1539, 1544, 1549, 1553, 1557, 1561, + 1565, 1571, 1582, 1589, 1592, 1601, 1606, 1616, 1621, 1629, + 1633, 1643, 1646, 1652, 1658, 1665, 1675, 1679, 1683, 1687, + 1692, 1696, 1701, 1706, 1711, 1716, 1721, 1726, 1731, 1736, + 1741, 1747, 1753, 1759, 1764, 1769, 1774, 1779, 1784, 1789, + 1794, 1799, 1804, 1809, 1814, 1820, 1827, 1832, 1837, 1842, + 1847, 1852, 1857, 1862, 1867, 1872, 1877, 1882, 1890, 1898, + 1906, 1912, 1918, 1924, 1930, 1936, 1942, 1948, 1954, 1960, + 1966, 1972, 1978, 1984, 1990, 1996, 2002, 2008, 2014, 2020, + 2026, 2032, 2038, 2044, 2050, 2056, 2062, 2068, 2074, 2080, + 2086, 2092, 2098, 2104, 2112, 2120, 2128, 2136, 2144, 2152, + 2160, 2168, 2176, 2184, 2192, 2200, 2206, 2212, 2218, 2224, + 2230, 2236, 2242, 2248, 2254, 2260, 2266, 2272, 2278, 2284, + 2290, 2296, 2302, 2308, 2314, 2320, 2326, 2332, 2338, 2344, + 2350, 2356, 2362, 2368, 2374, 2380, 2386, 2392, 2398, 2404, + 2410, 2416, 2420, 2424, 2428, 2433, 2439, 2444, 2449, 2454, + 2459, 2464, 2469, 2475, 2480, 2485, 2490, 2495, 2500, 2506, + 2512, 2518, 2524, 2530, 2536, 2542, 2548, 2554, 2560, 2566, + 2572, 2578, 2584, 2589, 2594, 2599, 2604, 2609, 2614, 2620, + 2625, 2630, 2635, 2640, 2645, 2650, 2655, 2661, 2666, 2671, + 2676, 2681, 2686, 2691, 2696, 2701, 2706, 2711, 2716, 2721, + 2726, 2731, 2737, 2742, 2747, 2753, 2759, 2764, 2769, 2774, + 2780, 2785, 2790, 2795, 2801, 2806, 2811, 2816, 2822, 2827, + 2832, 2837, 2843, 2849, 2855, 2861, 2866, 2872, 2878, 2884, + 2889, 2894, 2899, 2904, 2909, 2915, 2920, 2925, 2930, 2936, + 2941, 2946, 2951, 2957, 2962, 2967, 2972, 2978, 2983, 2988, + 2993, 2999, 3004, 3009, 3014, 3020, 3025, 3030, 3035, 3041, + 3046, 3051, 3056, 3062, 3067, 3072, 3077, 3083, 3088, 3093, + 3098, 3104, 3109, 3114, 3119, 3125, 3130, 3135, 3140, 3146, + 3151, 3156, 3161, 3167, 3172, 3177, 3182, 3188, 3193, 3198, + 3203, 3209, 3214, 3219, 3224, 3229, 3234, 3239, 3244, 3249, + 3254, 3259, 3264, 3269, 3274, 3279, 3284, 3289, 3294, 3299, + 3304, 3309, 3314, 3319, 3324, 3329, 3335, 3341, 3347, 3353, + 3360, 3367, 3373, 3379, 3385, 3391, 3397, 3403, 3410, 3415, + 3431, 3436, 3441, 3449, 3449, 3460, 3460, 3470, 3473, 3486, + 3508, 3535, 3539, 3545, 3550, 3561, 3565, 3571, 3582, 3585, + 3592, 3596, 3597, 3603, 3604, 3605, 3606, 3607, 3608, 3609, + 3611, 3617, 3626, 3627, 3631, 3627, 3643, 3644, 3648, 3648, + 3655, 3655, 3669, 3672, 3680, 3688, 3699, 3700, 3704, 3708, + 3715, 3722, 3726, 3734, 3738, 3751, 3755, 3762, 3762, 3782, + 3785, 3791, 3803, 3815, 3819, 3826, 3826, 3841, 3841, 3857, + 3857, 3878, 3881, 3887, 3890, 3896, 3900, 3907, 3912, 3917, + 3924, 3927, 3931, 3940, 3944, 3953, 3956, 3960, 3969, 3969, + 4011, 4017, 4020, 4025, 4028 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "CONST", "BOOL", "INT", + "UINT", "FLOAT", "BVEC2", "BVEC3", "BVEC4", "IVEC2", "IVEC3", "IVEC4", + "UVEC2", "UVEC3", "UVEC4", "VEC2", "VEC3", "VEC4", "MAT2", "MAT3", + "MAT4", "MAT2X2", "MAT2X3", "MAT2X4", "MAT3X2", "MAT3X3", "MAT3X4", + "MAT4X2", "MAT4X3", "MAT4X4", "SAMPLER2D", "SAMPLER3D", "SAMPLERCUBE", + "SAMPLER2DSHADOW", "SAMPLERCUBESHADOW", "SAMPLER2DARRAY", + "SAMPLER2DARRAYSHADOW", "ISAMPLER2D", "ISAMPLER3D", "ISAMPLERCUBE", + "ISAMPLER2DARRAY", "USAMPLER2D", "USAMPLER3D", "USAMPLERCUBE", + "USAMPLER2DARRAY", "SAMPLER", "SAMPLERSHADOW", "TEXTURE2D", "TEXTURE3D", + "TEXTURECUBE", "TEXTURE2DARRAY", "ITEXTURE2D", "ITEXTURE3D", + "ITEXTURECUBE", "ITEXTURE2DARRAY", "UTEXTURE2D", "UTEXTURE3D", + "UTEXTURECUBE", "UTEXTURE2DARRAY", "ATTRIBUTE", "VARYING", "FLOAT16_T", + "FLOAT32_T", "DOUBLE", "FLOAT64_T", "INT64_T", "UINT64_T", "INT32_T", + "UINT32_T", "INT16_T", "UINT16_T", "INT8_T", "UINT8_T", "I64VEC2", + "I64VEC3", "I64VEC4", "U64VEC2", "U64VEC3", "U64VEC4", "I32VEC2", + "I32VEC3", "I32VEC4", "U32VEC2", "U32VEC3", "U32VEC4", "I16VEC2", + "I16VEC3", "I16VEC4", "U16VEC2", "U16VEC3", "U16VEC4", "I8VEC2", + "I8VEC3", "I8VEC4", "U8VEC2", "U8VEC3", "U8VEC4", "DVEC2", "DVEC3", + "DVEC4", "DMAT2", "DMAT3", "DMAT4", "F16VEC2", "F16VEC3", "F16VEC4", + "F16MAT2", "F16MAT3", "F16MAT4", "F32VEC2", "F32VEC3", "F32VEC4", + "F32MAT2", "F32MAT3", "F32MAT4", "F64VEC2", "F64VEC3", "F64VEC4", + "F64MAT2", "F64MAT3", "F64MAT4", "DMAT2X2", "DMAT2X3", "DMAT2X4", + "DMAT3X2", "DMAT3X3", "DMAT3X4", "DMAT4X2", "DMAT4X3", "DMAT4X4", + "F16MAT2X2", "F16MAT2X3", "F16MAT2X4", "F16MAT3X2", "F16MAT3X3", + "F16MAT3X4", "F16MAT4X2", "F16MAT4X3", "F16MAT4X4", "F32MAT2X2", + "F32MAT2X3", "F32MAT2X4", "F32MAT3X2", "F32MAT3X3", "F32MAT3X4", + "F32MAT4X2", "F32MAT4X3", "F32MAT4X4", "F64MAT2X2", "F64MAT2X3", + "F64MAT2X4", "F64MAT3X2", "F64MAT3X3", "F64MAT3X4", "F64MAT4X2", + "F64MAT4X3", "F64MAT4X4", "ATOMIC_UINT", "ACCSTRUCTNV", "ACCSTRUCTEXT", + "RAYQUERYEXT", "FCOOPMATNV", "ICOOPMATNV", "UCOOPMATNV", + "SAMPLERCUBEARRAY", "SAMPLERCUBEARRAYSHADOW", "ISAMPLERCUBEARRAY", + "USAMPLERCUBEARRAY", "SAMPLER1D", "SAMPLER1DARRAY", + "SAMPLER1DARRAYSHADOW", "ISAMPLER1D", "SAMPLER1DSHADOW", "SAMPLER2DRECT", + "SAMPLER2DRECTSHADOW", "ISAMPLER2DRECT", "USAMPLER2DRECT", + "SAMPLERBUFFER", "ISAMPLERBUFFER", "USAMPLERBUFFER", "SAMPLER2DMS", + "ISAMPLER2DMS", "USAMPLER2DMS", "SAMPLER2DMSARRAY", "ISAMPLER2DMSARRAY", + "USAMPLER2DMSARRAY", "SAMPLEREXTERNALOES", "SAMPLEREXTERNAL2DY2YEXT", + "ISAMPLER1DARRAY", "USAMPLER1D", "USAMPLER1DARRAY", "F16SAMPLER1D", + "F16SAMPLER2D", "F16SAMPLER3D", "F16SAMPLER2DRECT", "F16SAMPLERCUBE", + "F16SAMPLER1DARRAY", "F16SAMPLER2DARRAY", "F16SAMPLERCUBEARRAY", + "F16SAMPLERBUFFER", "F16SAMPLER2DMS", "F16SAMPLER2DMSARRAY", + "F16SAMPLER1DSHADOW", "F16SAMPLER2DSHADOW", "F16SAMPLER1DARRAYSHADOW", + "F16SAMPLER2DARRAYSHADOW", "F16SAMPLER2DRECTSHADOW", + "F16SAMPLERCUBESHADOW", "F16SAMPLERCUBEARRAYSHADOW", "IMAGE1D", + "IIMAGE1D", "UIMAGE1D", "IMAGE2D", "IIMAGE2D", "UIMAGE2D", "IMAGE3D", + "IIMAGE3D", "UIMAGE3D", "IMAGE2DRECT", "IIMAGE2DRECT", "UIMAGE2DRECT", + "IMAGECUBE", "IIMAGECUBE", "UIMAGECUBE", "IMAGEBUFFER", "IIMAGEBUFFER", + "UIMAGEBUFFER", "IMAGE1DARRAY", "IIMAGE1DARRAY", "UIMAGE1DARRAY", + "IMAGE2DARRAY", "IIMAGE2DARRAY", "UIMAGE2DARRAY", "IMAGECUBEARRAY", + "IIMAGECUBEARRAY", "UIMAGECUBEARRAY", "IMAGE2DMS", "IIMAGE2DMS", + "UIMAGE2DMS", "IMAGE2DMSARRAY", "IIMAGE2DMSARRAY", "UIMAGE2DMSARRAY", + "F16IMAGE1D", "F16IMAGE2D", "F16IMAGE3D", "F16IMAGE2DRECT", + "F16IMAGECUBE", "F16IMAGE1DARRAY", "F16IMAGE2DARRAY", + "F16IMAGECUBEARRAY", "F16IMAGEBUFFER", "F16IMAGE2DMS", + "F16IMAGE2DMSARRAY", "I64IMAGE1D", "U64IMAGE1D", "I64IMAGE2D", + "U64IMAGE2D", "I64IMAGE3D", "U64IMAGE3D", "I64IMAGE2DRECT", + "U64IMAGE2DRECT", "I64IMAGECUBE", "U64IMAGECUBE", "I64IMAGEBUFFER", + "U64IMAGEBUFFER", "I64IMAGE1DARRAY", "U64IMAGE1DARRAY", + "I64IMAGE2DARRAY", "U64IMAGE2DARRAY", "I64IMAGECUBEARRAY", + "U64IMAGECUBEARRAY", "I64IMAGE2DMS", "U64IMAGE2DMS", "I64IMAGE2DMSARRAY", + "U64IMAGE2DMSARRAY", "TEXTURECUBEARRAY", "ITEXTURECUBEARRAY", + "UTEXTURECUBEARRAY", "TEXTURE1D", "ITEXTURE1D", "UTEXTURE1D", + "TEXTURE1DARRAY", "ITEXTURE1DARRAY", "UTEXTURE1DARRAY", "TEXTURE2DRECT", + "ITEXTURE2DRECT", "UTEXTURE2DRECT", "TEXTUREBUFFER", "ITEXTUREBUFFER", + "UTEXTUREBUFFER", "TEXTURE2DMS", "ITEXTURE2DMS", "UTEXTURE2DMS", + "TEXTURE2DMSARRAY", "ITEXTURE2DMSARRAY", "UTEXTURE2DMSARRAY", + "F16TEXTURE1D", "F16TEXTURE2D", "F16TEXTURE3D", "F16TEXTURE2DRECT", + "F16TEXTURECUBE", "F16TEXTURE1DARRAY", "F16TEXTURE2DARRAY", + "F16TEXTURECUBEARRAY", "F16TEXTUREBUFFER", "F16TEXTURE2DMS", + "F16TEXTURE2DMSARRAY", "SUBPASSINPUT", "SUBPASSINPUTMS", "ISUBPASSINPUT", + "ISUBPASSINPUTMS", "USUBPASSINPUT", "USUBPASSINPUTMS", "F16SUBPASSINPUT", + "F16SUBPASSINPUTMS", "LEFT_OP", "RIGHT_OP", "INC_OP", "DEC_OP", "LE_OP", + "GE_OP", "EQ_OP", "NE_OP", "AND_OP", "OR_OP", "XOR_OP", "MUL_ASSIGN", + "DIV_ASSIGN", "ADD_ASSIGN", "MOD_ASSIGN", "LEFT_ASSIGN", "RIGHT_ASSIGN", + "AND_ASSIGN", "XOR_ASSIGN", "OR_ASSIGN", "SUB_ASSIGN", "STRING_LITERAL", + "LEFT_PAREN", "RIGHT_PAREN", "LEFT_BRACKET", "RIGHT_BRACKET", + "LEFT_BRACE", "RIGHT_BRACE", "DOT", "COMMA", "COLON", "EQUAL", + "SEMICOLON", "BANG", "DASH", "TILDE", "PLUS", "STAR", "SLASH", "PERCENT", + "LEFT_ANGLE", "RIGHT_ANGLE", "VERTICAL_BAR", "CARET", "AMPERSAND", + "QUESTION", "INVARIANT", "HIGH_PRECISION", "MEDIUM_PRECISION", + "LOW_PRECISION", "PRECISION", "PACKED", "RESOURCE", "SUPERP", + "FLOATCONSTANT", "INTCONSTANT", "UINTCONSTANT", "BOOLCONSTANT", + "IDENTIFIER", "TYPE_NAME", "CENTROID", "IN", "OUT", "INOUT", "STRUCT", + "VOID", "WHILE", "BREAK", "CONTINUE", "DO", "ELSE", "FOR", "IF", + "DISCARD", "RETURN", "SWITCH", "CASE", "DEFAULT", "TERMINATE_INVOCATION", + "UNIFORM", "SHARED", "BUFFER", "FLAT", "SMOOTH", "LAYOUT", + "DOUBLECONSTANT", "INT16CONSTANT", "UINT16CONSTANT", "FLOAT16CONSTANT", + "INT32CONSTANT", "UINT32CONSTANT", "INT64CONSTANT", "UINT64CONSTANT", + "SUBROUTINE", "DEMOTE", "PAYLOADNV", "PAYLOADINNV", "HITATTRNV", + "CALLDATANV", "CALLDATAINNV", "PAYLOADEXT", "PAYLOADINEXT", "HITATTREXT", + "CALLDATAEXT", "CALLDATAINEXT", "PATCH", "SAMPLE", "NONUNIFORM", + "COHERENT", "VOLATILE", "RESTRICT", "READONLY", "WRITEONLY", + "DEVICECOHERENT", "QUEUEFAMILYCOHERENT", "WORKGROUPCOHERENT", + "SUBGROUPCOHERENT", "NONPRIVATE", "SHADERCALLCOHERENT", "NOPERSPECTIVE", + "EXPLICITINTERPAMD", "PERVERTEXNV", "PERPRIMITIVENV", "PERVIEWNV", + "PERTASKNV", "PRECISE", "$accept", "variable_identifier", + "primary_expression", "postfix_expression", "integer_expression", + "function_call", "function_call_or_method", "function_call_generic", + "function_call_header_no_parameters", + "function_call_header_with_parameters", "function_call_header", + "function_identifier", "unary_expression", "unary_operator", + "multiplicative_expression", "additive_expression", "shift_expression", + "relational_expression", "equality_expression", "and_expression", + "exclusive_or_expression", "inclusive_or_expression", + "logical_and_expression", "logical_xor_expression", + "logical_or_expression", "conditional_expression", "$@1", + "assignment_expression", "assignment_operator", "expression", + "constant_expression", "declaration", "block_structure", "$@2", + "identifier_list", "function_prototype", "function_declarator", + "function_header_with_parameters", "function_header", + "parameter_declarator", "parameter_declaration", + "parameter_type_specifier", "init_declarator_list", "single_declaration", + "fully_specified_type", "invariant_qualifier", "interpolation_qualifier", + "layout_qualifier", "layout_qualifier_id_list", "layout_qualifier_id", + "precise_qualifier", "type_qualifier", "single_type_qualifier", + "storage_qualifier", "non_uniform_qualifier", "type_name_list", + "type_specifier", "array_specifier", "type_parameter_specifier_opt", + "type_parameter_specifier", "type_parameter_specifier_list", + "type_specifier_nonarray", "precision_qualifier", "struct_specifier", + "$@3", "$@4", "struct_declaration_list", "struct_declaration", + "struct_declarator_list", "struct_declarator", "initializer", + "initializer_list", "declaration_statement", "statement", + "simple_statement", "demote_statement", "compound_statement", "$@5", + "$@6", "statement_no_new_scope", "statement_scoped", "$@7", "$@8", + "compound_statement_no_new_scope", "statement_list", + "expression_statement", "selection_statement", + "selection_statement_nonattributed", "selection_rest_statement", + "condition", "switch_statement", "switch_statement_nonattributed", "$@9", + "switch_statement_list", "case_label", "iteration_statement", + "iteration_statement_nonattributed", "$@10", "$@11", "$@12", + "for_init_statement", "conditionopt", "for_rest_statement", + "jump_statement", "translation_unit", "external_declaration", + "function_definition", "$@13", "attribute", "attribute_list", + "single_attribute", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, + 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, + 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, + 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, + 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, + 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, + 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, + 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, + 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, + 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, + 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, + 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, + 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, + 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, + 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, + 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, + 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, + 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, + 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, + 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, + 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, + 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, + 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, + 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, + 695, 696, 697 +}; +#endif + +#define YYPACT_NINF (-728) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-559) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + 4283, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + 109, -728, -728, -728, -728, -728, 1, -728, -728, -728, + -728, -728, -728, -327, -323, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, 11, -271, 12, + 19, 6483, 8, -728, 57, -728, -728, -728, -728, 4723, + -728, -728, -728, -728, 37, -728, -728, 763, -728, -728, + 16, -728, 107, -29, 92, -728, -336, -728, 136, -728, + 6483, -728, -728, -728, 6483, 110, 117, -728, 54, -728, + 68, -728, -728, 9027, 140, -728, -728, -728, 134, 6483, + -728, 146, -728, 13, -728, -728, 59, 7343, -728, -335, + 1203, -728, -728, -728, -728, 140, -331, -728, 7764, -330, + -728, 126, -728, 85, 9027, 9027, -728, 9027, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, 36, -728, -728, + -728, 170, 66, 9448, 173, -728, 9027, -728, -728, -343, + 172, -728, 6483, 141, 5163, -728, 6483, 9027, -728, -29, + -728, 145, -728, -728, 142, 93, 108, 26, 114, 154, + 157, 159, 196, 195, 23, 181, 8185, -728, 183, 182, + -728, -728, 186, 178, 180, -728, 191, 192, 184, 8606, + 193, 9027, 187, 188, 194, 129, -728, -728, 99, -728, + -271, 197, 202, -728, -728, -728, -728, -728, 1643, -728, + -728, -728, -728, -728, -728, -728, -728, -728, -22, 172, + 7764, 21, 7764, -728, -728, 7764, 6483, -728, 160, -728, + -728, -728, 76, -728, -728, 9027, 167, -728, -728, 9027, + 204, -728, -728, -728, 9027, -728, 141, 140, 106, -728, + -728, -728, 5603, -728, -728, -728, -728, 9027, 9027, 9027, + 9027, 9027, 9027, 9027, 9027, 9027, 9027, 9027, 9027, 9027, + 9027, 9027, 9027, 9027, 9027, 9027, -728, -728, -728, 207, + 171, -728, 2083, -728, -728, -728, 2083, -728, 9027, -728, + -728, 122, 9027, 143, -728, -728, -728, -728, -728, -728, + -728, -728, -728, -728, -728, -728, -728, -728, 9027, 9027, + -728, -728, -728, -728, -728, -728, -728, 7764, -728, 132, + -728, 6043, -728, -728, 208, 205, -728, -728, -728, 123, + 172, 141, -728, -728, -728, -728, -728, 142, 142, 93, + 93, 108, 108, 108, 108, 26, 26, 114, 154, 157, + 159, 196, 195, 9027, -728, 213, 61, -728, 2083, 3843, + 174, 3403, 78, -728, 81, -728, -728, -728, -728, -728, + 6922, -728, -728, -728, -728, 153, 9027, 211, 171, 210, + 205, 185, 6483, 218, 220, -728, -728, 3843, 219, -728, + -728, -728, 9027, 221, -728, -728, -728, 215, 2523, 9027, + -728, 217, 224, 189, 225, 2963, -728, 226, -728, -728, + 7764, -728, -728, -728, 83, 9027, 2523, 219, -728, -728, + 2083, -728, 222, 205, -728, -728, 2083, 223, -728, -728 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int16 yydefact[] = +{ + 0, 157, 210, 208, 209, 207, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 211, 212, 213, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 336, 337, 338, 339, 340, 341, 342, 362, 363, 364, + 365, 366, 367, 368, 377, 390, 391, 378, 379, 381, + 380, 382, 383, 384, 385, 386, 387, 388, 389, 165, + 166, 236, 237, 235, 238, 245, 246, 243, 244, 241, + 242, 239, 240, 268, 269, 270, 280, 281, 282, 265, + 266, 267, 277, 278, 279, 262, 263, 264, 274, 275, + 276, 259, 260, 261, 271, 272, 273, 247, 248, 249, + 283, 284, 285, 250, 251, 252, 295, 296, 297, 253, + 254, 255, 307, 308, 309, 256, 257, 258, 319, 320, + 321, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 310, + 311, 312, 313, 314, 315, 316, 317, 318, 322, 323, + 324, 325, 326, 327, 328, 329, 330, 334, 331, 332, + 333, 515, 516, 517, 346, 347, 370, 373, 335, 344, + 345, 361, 343, 392, 393, 396, 397, 398, 400, 401, + 402, 404, 405, 406, 408, 409, 505, 506, 369, 371, + 372, 348, 349, 350, 394, 351, 355, 356, 359, 399, + 403, 407, 352, 353, 357, 358, 395, 354, 360, 439, + 441, 442, 443, 445, 446, 447, 449, 450, 451, 453, + 454, 455, 457, 458, 459, 461, 462, 463, 465, 466, + 467, 469, 470, 471, 473, 474, 475, 477, 478, 479, + 481, 482, 440, 444, 448, 452, 456, 464, 468, 472, + 460, 476, 480, 483, 484, 485, 486, 487, 488, 489, + 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, + 500, 501, 502, 503, 504, 374, 375, 376, 410, 419, + 421, 415, 420, 422, 423, 425, 426, 427, 429, 430, + 431, 433, 434, 435, 437, 438, 411, 412, 413, 424, + 414, 416, 417, 418, 428, 432, 436, 507, 508, 511, + 512, 513, 514, 509, 510, 607, 132, 520, 521, 522, + 0, 519, 161, 159, 160, 158, 0, 206, 162, 163, + 164, 134, 133, 0, 190, 171, 173, 169, 175, 177, + 172, 174, 170, 176, 178, 167, 168, 192, 179, 186, + 187, 188, 189, 180, 181, 182, 183, 184, 185, 135, + 136, 137, 138, 139, 140, 147, 606, 0, 608, 0, + 109, 108, 0, 120, 125, 154, 153, 151, 155, 0, + 148, 150, 156, 130, 202, 152, 518, 0, 603, 605, + 0, 525, 0, 0, 0, 97, 0, 94, 0, 107, + 0, 116, 110, 118, 0, 119, 0, 95, 126, 100, + 0, 149, 131, 0, 195, 201, 1, 604, 0, 0, + 523, 144, 146, 0, 142, 193, 0, 0, 98, 0, + 0, 609, 111, 115, 117, 113, 121, 112, 0, 127, + 103, 0, 101, 0, 0, 0, 9, 0, 43, 42, + 44, 41, 5, 6, 7, 8, 2, 16, 14, 15, + 17, 10, 11, 12, 13, 3, 18, 37, 20, 25, + 26, 0, 0, 30, 0, 204, 0, 36, 34, 0, + 196, 96, 0, 0, 0, 527, 0, 0, 141, 0, + 191, 0, 197, 45, 49, 52, 55, 60, 63, 65, + 67, 69, 71, 73, 75, 0, 0, 99, 0, 553, + 562, 566, 0, 0, 0, 587, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 45, 78, 91, 0, 540, + 0, 156, 130, 543, 564, 542, 550, 541, 0, 544, + 545, 568, 546, 575, 547, 548, 583, 549, 0, 114, + 0, 122, 0, 535, 129, 0, 0, 105, 0, 102, + 38, 39, 0, 22, 23, 0, 0, 28, 27, 0, + 206, 31, 33, 40, 0, 203, 0, 533, 0, 531, + 526, 528, 0, 93, 145, 143, 194, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 76, 198, 199, 0, + 0, 552, 0, 585, 598, 597, 0, 589, 0, 601, + 599, 0, 0, 0, 582, 602, 551, 81, 82, 84, + 83, 86, 87, 88, 89, 90, 85, 80, 0, 0, + 567, 563, 565, 569, 576, 584, 124, 0, 538, 0, + 128, 0, 106, 4, 0, 24, 21, 32, 205, 0, + 534, 0, 529, 524, 46, 47, 48, 51, 50, 53, + 54, 58, 59, 56, 57, 61, 62, 64, 66, 68, + 70, 72, 74, 0, 200, 613, 0, 611, 554, 0, + 0, 0, 0, 600, 0, 581, 79, 92, 123, 536, + 0, 104, 19, 530, 532, 0, 0, 0, 0, 0, + 573, 0, 0, 0, 0, 592, 591, 594, 560, 577, + 537, 539, 0, 0, 610, 612, 555, 0, 0, 0, + 593, 0, 0, 572, 0, 0, 570, 0, 77, 614, + 0, 557, 586, 556, 0, 595, 0, 560, 559, 561, + 579, 574, 0, 596, 590, 571, 580, 0, 588, 578 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -728, -728, -728, -728, -728, -728, -728, -728, -728, -728, + -728, -728, 9352, -728, -87, -84, -154, -93, -30, -28, + -27, -26, -25, -31, -728, -86, -728, -99, -728, -111, + -126, 2, -728, -728, -728, 4, -728, -728, -728, 177, + 190, 179, -728, -728, -339, -728, -728, -728, -728, 95, + -728, -37, -46, -728, 9, -728, 0, -63, -728, -728, + -728, -728, 265, -728, -728, -728, -479, -149, 10, -74, + -212, -728, -103, -201, -727, -728, -145, -728, -728, -153, + -155, -728, -728, 198, -270, -97, -728, 47, -728, -120, + -728, 50, -728, -728, -728, -728, 51, -728, -728, -728, + -728, -728, -728, -728, -728, 216, -728, -728, -728, -728, + -108 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 465, 466, 467, 654, 468, 469, 470, 471, 472, + 473, 474, 525, 476, 494, 495, 496, 497, 498, 499, + 500, 501, 502, 503, 504, 526, 683, 527, 638, 528, + 584, 529, 367, 556, 443, 530, 369, 370, 371, 401, + 402, 403, 372, 373, 374, 375, 376, 377, 423, 424, + 378, 379, 380, 381, 477, 426, 478, 429, 414, 415, + 479, 384, 385, 386, 486, 419, 484, 485, 578, 579, + 554, 649, 533, 534, 535, 536, 537, 612, 709, 742, + 733, 734, 735, 743, 538, 539, 540, 541, 736, 713, + 542, 543, 737, 757, 544, 545, 546, 689, 616, 691, + 717, 731, 732, 547, 387, 388, 389, 398, 548, 686, + 687 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 383, 741, 366, 574, 368, 427, 506, 582, 749, 382, + 427, 506, 393, 428, 507, 575, 394, 550, 555, 741, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 651, 397, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 411, 404, 581, 562, 642, 646, 553, + 648, 505, 688, 650, 391, 439, 421, 594, 595, 605, + 711, 480, 399, 488, 406, 563, 564, 407, 411, 489, + 395, 512, 506, 404, 515, 400, 516, 517, 422, 647, + 520, 405, 549, 551, 571, -35, 392, 565, 711, 412, + 382, 566, 482, 596, 597, 606, 396, 383, 382, 366, + 418, 368, 321, 437, 413, 427, 382, 326, 327, 490, + 405, 583, 438, 707, 405, 491, 568, 708, 621, 382, + 623, 440, 569, 382, 441, 690, 653, 442, 718, 483, + 609, 719, 639, 752, 639, 592, 593, 639, 382, 639, + 532, 558, 408, 581, 559, 698, 411, 598, 599, 531, + 671, 672, 673, 674, 590, 639, 591, 482, 640, 482, + 420, 553, 661, 553, 655, 662, 553, 627, 628, 629, + 630, 631, 632, 633, 634, 635, 636, 425, 639, 661, + 657, 693, 703, 317, 318, 319, 699, 637, 700, 430, + 756, 427, 576, 481, 483, 435, 483, 642, 721, 639, + 695, 382, 436, 382, 487, 382, 587, 588, 589, 639, + 722, 557, 581, 667, 668, 675, 676, 692, 669, 670, + 567, 694, 572, 506, 660, 600, 577, 601, 602, 482, + 586, 603, 604, 607, 610, 613, 611, 614, 751, 615, + 617, 618, 622, 619, 624, 652, -36, 625, 532, 696, + 697, -34, 656, 626, -29, 482, 685, 531, 553, 684, + 702, 639, 706, 724, 726, 642, 483, 714, 728, 729, + 727, 739, -558, 740, 746, 382, 745, 759, 509, 750, + 677, 758, 705, 678, 682, 679, 747, 680, 710, 681, + 723, 433, 483, 434, 585, 390, 659, 704, 715, 748, + 432, 382, 755, 754, 716, 643, 431, 730, 644, 645, + 725, 553, 0, 417, 0, 0, 710, 0, 0, 0, + 0, 0, 532, 0, 482, 0, 532, 0, 744, 0, + 583, 531, 0, 738, 0, 531, 0, 0, 0, 0, + 0, 0, 0, 0, 753, 0, 0, 0, 0, 0, + 0, 553, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 483, 712, 0, 0, 0, 0, 0, 0, 0, + 382, 0, 0, 0, 0, 0, 411, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 712, 0, 0, 0, 0, 0, 0, 0, 532, 532, + 0, 532, 0, 0, 0, 0, 0, 531, 531, 0, + 531, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 412, 0, 0, 0, 0, 532, 0, 0, + 0, 382, 0, 0, 0, 0, 531, 0, 532, 0, + 0, 0, 0, 0, 0, 532, 0, 531, 0, 0, + 0, 0, 0, 0, 531, 0, 532, 0, 0, 0, + 532, 0, 0, 0, 0, 531, 532, 0, 0, 531, + 0, 0, 0, 416, 0, 531, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 315, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 320, 0, 0, 0, 0, 0, 0, 0, 0, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 0, 0, 0, 0, 0, + 0, 0, 0, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 444, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 446, 447, 0, 508, 0, 509, 510, 0, 0, + 0, 0, 511, 448, 449, 450, 451, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 320, 0, 0, 0, 452, 453, 454, 455, 456, 321, + 322, 323, 324, 325, 326, 327, 512, 513, 514, 515, + 0, 516, 517, 518, 519, 520, 521, 522, 523, 328, + 329, 330, 331, 332, 333, 457, 458, 459, 460, 461, + 462, 463, 464, 334, 524, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 444, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 446, 447, 0, 508, 0, 509, 641, 0, 0, + 0, 0, 511, 448, 449, 450, 451, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 320, 0, 0, 0, 452, 453, 454, 455, 456, 321, + 322, 323, 324, 325, 326, 327, 512, 513, 514, 515, + 0, 516, 517, 518, 519, 520, 521, 522, 523, 328, + 329, 330, 331, 332, 333, 457, 458, 459, 460, 461, + 462, 463, 464, 334, 524, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 444, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 446, 447, 0, 508, 0, 509, 0, 0, 0, + 0, 0, 511, 448, 449, 450, 451, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 320, 0, 0, 0, 452, 453, 454, 455, 456, 321, + 322, 323, 324, 325, 326, 327, 512, 513, 514, 515, + 0, 516, 517, 518, 519, 520, 521, 522, 523, 328, + 329, 330, 331, 332, 333, 457, 458, 459, 460, 461, + 462, 463, 464, 334, 524, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 444, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 446, 447, 0, 508, 0, 430, 0, 0, 0, + 0, 0, 511, 448, 449, 450, 451, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 320, 0, 0, 0, 452, 453, 454, 455, 456, 321, + 322, 323, 324, 325, 326, 327, 512, 513, 514, 515, + 0, 516, 517, 518, 519, 520, 521, 522, 523, 328, + 329, 330, 331, 332, 333, 457, 458, 459, 460, 461, + 462, 463, 464, 334, 524, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 444, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 446, 447, 0, 508, 0, 0, 0, 0, 0, + 0, 0, 511, 448, 449, 450, 451, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 320, 0, 0, 0, 452, 453, 454, 455, 456, 321, + 322, 323, 324, 325, 326, 327, 512, 513, 514, 515, + 0, 516, 517, 518, 519, 520, 521, 522, 523, 328, + 329, 330, 331, 332, 333, 457, 458, 459, 460, 461, + 462, 463, 464, 334, 524, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 444, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 446, 447, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 511, 448, 449, 450, 451, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 320, 0, 0, 0, 452, 453, 454, 455, 456, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 457, 458, 459, 460, 461, + 462, 463, 464, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 444, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 446, 447, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 448, 449, 450, 451, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 0, 0, 0, 0, 452, 453, 454, 455, 456, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 457, 458, 459, 460, 461, + 462, 463, 464, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 315, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 320, 0, 0, 0, 0, 0, 0, 0, 0, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 0, 0, 0, 0, 0, + 0, 0, 0, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 409, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 0, 0, 0, 0, 0, 0, 0, 0, 410, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 0, 0, 0, 0, 0, + 0, 0, 0, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 580, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 0, 0, 0, 0, 0, + 0, 0, 0, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 663, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 0, 0, 0, 0, 0, + 0, 0, 0, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 701, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 0, 0, 0, 0, 0, + 0, 0, 0, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 316, 317, 318, 319, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 321, + 322, 323, 324, 325, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 328, + 329, 330, 331, 332, 333, 0, 0, 0, 0, 0, + 0, 0, 0, 334, 0, 335, 336, 337, 338, 339, + 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, + 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 362, 363, 364, 365, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 0, 0, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 0, + 0, 444, 445, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 446, 447, 0, 0, 0, 552, 720, 0, 0, 0, + 0, 0, 448, 449, 450, 451, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 452, 453, 454, 455, 456, 321, 0, + 0, 0, 0, 326, 327, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 457, 458, 459, 460, 461, 462, + 463, 464, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 347, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 0, 0, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 0, 0, 444, 445, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 446, 447, 0, 0, 492, 0, 0, 0, 0, + 0, 0, 0, 448, 449, 450, 451, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 452, 453, 454, 455, 456, 321, + 0, 0, 0, 0, 326, 327, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 457, 458, 459, 460, 461, + 462, 463, 464, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 347, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 0, 0, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 0, 0, 444, 445, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 446, 447, 0, 0, 0, 552, 0, 0, + 0, 0, 0, 0, 448, 449, 450, 451, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 452, 453, 454, 455, 456, + 321, 0, 0, 0, 0, 326, 327, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 457, 458, 459, 460, + 461, 462, 463, 464, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 347, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 0, 0, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 0, 0, 444, 445, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 446, 447, 0, 0, 608, 0, 0, + 0, 0, 0, 0, 0, 448, 449, 450, 451, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 452, 453, 454, 455, + 456, 321, 0, 0, 0, 0, 326, 327, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 457, 458, 459, + 460, 461, 462, 463, 464, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 347, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 0, 0, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 0, 0, 444, 445, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 446, 447, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 620, 448, 449, 450, 451, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 452, 453, 454, + 455, 456, 321, 0, 0, 0, 0, 326, 327, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 457, 458, + 459, 460, 461, 462, 463, 464, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 347, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, 0, 0, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, + 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, + 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, + 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, + 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, + 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 314, 0, 0, 444, 445, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 446, 447, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 448, 449, 450, + 451, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 452, 453, + 454, 455, 456, 321, 0, 0, 0, 0, 326, 327, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 457, + 458, 459, 460, 461, 462, 463, 464, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 347, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 0, + 0, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, + 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, + 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, + 310, 311, 312, 313, 314, 475, 0, 444, 445, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 493, + 0, 0, 0, 0, 0, 0, 446, 447, 0, 0, + 0, 0, 0, 0, 0, 0, 560, 561, 448, 449, + 450, 451, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 452, + 453, 454, 455, 456, 321, 0, 0, 0, 573, 326, + 570, 0, 0, 0, 0, 0, 0, 0, 0, 493, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 457, 458, 459, 460, 461, 462, 463, 464, 493, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 347, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 658, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 664, + 665, 666, 493, 493, 493, 493, 493, 493, 493, 493, + 493, 493, 493, 493, 493, 493, 493, 493, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 493 +}; + +static const yytype_int16 yycheck[] = +{ + 0, 728, 0, 346, 0, 341, 341, 486, 735, 0, + 341, 341, 339, 349, 349, 358, 339, 348, 348, 746, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 556, 349, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, 379, 371, 484, 447, 538, 550, 438, + 552, 427, 612, 555, 343, 408, 375, 321, 322, 326, + 689, 414, 340, 340, 346, 319, 320, 349, 404, 346, + 349, 383, 341, 400, 386, 346, 388, 389, 397, 348, + 392, 371, 435, 436, 473, 339, 375, 341, 717, 379, + 371, 345, 419, 357, 358, 362, 375, 387, 379, 387, + 390, 387, 376, 339, 357, 341, 387, 381, 382, 340, + 400, 487, 348, 342, 404, 346, 340, 346, 519, 400, + 521, 343, 346, 404, 346, 616, 340, 349, 340, 419, + 506, 340, 346, 340, 346, 317, 318, 346, 419, 346, + 430, 346, 375, 582, 349, 647, 482, 323, 324, 430, + 594, 595, 596, 597, 351, 346, 353, 484, 349, 486, + 343, 550, 346, 552, 565, 349, 555, 328, 329, 330, + 331, 332, 333, 334, 335, 336, 337, 375, 346, 346, + 569, 349, 349, 364, 365, 366, 344, 348, 346, 343, + 750, 341, 482, 349, 484, 375, 486, 688, 700, 346, + 347, 482, 375, 484, 348, 486, 354, 355, 356, 346, + 347, 375, 651, 590, 591, 598, 599, 618, 592, 593, + 340, 622, 339, 341, 577, 361, 375, 360, 359, 556, + 375, 325, 327, 342, 341, 339, 344, 349, 740, 349, + 339, 339, 339, 349, 347, 375, 339, 349, 538, 638, + 639, 339, 375, 349, 340, 582, 375, 538, 647, 342, + 342, 346, 339, 342, 344, 756, 556, 383, 340, 339, + 375, 340, 343, 348, 340, 556, 349, 344, 343, 343, + 600, 349, 683, 601, 605, 602, 387, 603, 689, 604, + 706, 404, 582, 404, 489, 320, 576, 661, 691, 734, + 400, 582, 747, 746, 691, 548, 398, 717, 548, 548, + 708, 700, -1, 387, -1, -1, 717, -1, -1, -1, + -1, -1, 612, -1, 651, -1, 616, -1, 729, -1, + 706, 612, -1, 722, -1, 616, -1, -1, -1, -1, + -1, -1, -1, -1, 745, -1, -1, -1, -1, -1, + -1, 740, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 651, 689, -1, -1, -1, -1, -1, -1, -1, + 651, -1, -1, -1, -1, -1, 712, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 717, -1, -1, -1, -1, -1, -1, -1, 688, 689, + -1, 691, -1, -1, -1, -1, -1, 688, 689, -1, + 691, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 712, -1, -1, -1, -1, 717, -1, -1, + -1, 712, -1, -1, -1, -1, 717, -1, 728, -1, + -1, -1, -1, -1, -1, 735, -1, 728, -1, -1, + -1, -1, -1, -1, 735, -1, 746, -1, -1, -1, + 750, -1, -1, -1, -1, 746, 756, -1, -1, 750, + -1, -1, -1, 0, -1, 756, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 349, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + 367, -1, -1, -1, -1, -1, -1, -1, -1, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, -1, -1, -1, -1, -1, + -1, -1, -1, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, 319, 320, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 338, 339, -1, 341, -1, 343, 344, -1, -1, + -1, -1, 349, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + 367, -1, -1, -1, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, + -1, 388, 389, 390, 391, 392, 393, 394, 395, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, 319, 320, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 338, 339, -1, 341, -1, 343, 344, -1, -1, + -1, -1, 349, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + 367, -1, -1, -1, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, + -1, 388, 389, 390, 391, 392, 393, 394, 395, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, 319, 320, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 338, 339, -1, 341, -1, 343, -1, -1, -1, + -1, -1, 349, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + 367, -1, -1, -1, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, + -1, 388, 389, 390, 391, 392, 393, 394, 395, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, 319, 320, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 338, 339, -1, 341, -1, 343, -1, -1, -1, + -1, -1, 349, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + 367, -1, -1, -1, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, + -1, 388, 389, 390, 391, 392, 393, 394, 395, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, 319, 320, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 338, 339, -1, 341, -1, -1, -1, -1, -1, + -1, -1, 349, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + 367, -1, -1, -1, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, + -1, 388, 389, 390, 391, 392, 393, 394, 395, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, 319, 320, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 338, 339, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 349, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + 367, -1, -1, -1, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, 319, 320, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 338, 339, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + -1, -1, -1, -1, 371, 372, 373, 374, 375, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, + 407, 408, 409, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 349, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + 367, -1, -1, -1, -1, -1, -1, -1, -1, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, -1, -1, -1, -1, -1, + -1, -1, -1, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 349, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + -1, -1, -1, -1, -1, -1, -1, -1, 375, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, -1, -1, -1, -1, -1, + -1, -1, -1, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 344, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, -1, -1, -1, -1, -1, + -1, -1, -1, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 344, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, -1, -1, -1, -1, -1, + -1, -1, -1, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 344, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, -1, -1, -1, -1, -1, + -1, -1, -1, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 363, 364, 365, 366, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 376, + 377, 378, 379, 380, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 396, + 397, 398, 399, 400, 401, -1, -1, -1, -1, -1, + -1, -1, -1, 410, -1, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, -1, -1, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, + 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, + 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, + 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, + 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, 316, -1, + -1, 319, 320, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 338, 339, -1, -1, -1, 343, 344, -1, -1, -1, + -1, -1, 350, 351, 352, 353, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 371, 372, 373, 374, 375, 376, -1, + -1, -1, -1, 381, 382, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 402, 403, 404, 405, 406, 407, + 408, 409, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 424, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, -1, -1, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, + 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, + 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, + 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, + 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, + -1, -1, 319, 320, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 338, 339, -1, -1, 342, -1, -1, -1, -1, + -1, -1, -1, 350, 351, 352, 353, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 371, 372, 373, 374, 375, 376, + -1, -1, -1, -1, 381, 382, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 402, 403, 404, 405, 406, + 407, 408, 409, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 424, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, -1, -1, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, + 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, + 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, + 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, + 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, + 316, -1, -1, 319, 320, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 338, 339, -1, -1, -1, 343, -1, -1, + -1, -1, -1, -1, 350, 351, 352, 353, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 371, 372, 373, 374, 375, + 376, -1, -1, -1, -1, 381, 382, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 402, 403, 404, 405, + 406, 407, 408, 409, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 424, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, -1, -1, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, -1, -1, 319, 320, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 338, 339, -1, -1, 342, -1, -1, + -1, -1, -1, -1, -1, 350, 351, 352, 353, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 371, 372, 373, 374, + 375, 376, -1, -1, -1, -1, 381, 382, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 402, 403, 404, + 405, 406, 407, 408, 409, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 424, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, -1, -1, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, + 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, + 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, + 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, + 314, 315, 316, -1, -1, 319, 320, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 338, 339, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 349, 350, 351, 352, 353, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 371, 372, 373, + 374, 375, 376, -1, -1, -1, -1, 381, 382, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 402, 403, + 404, 405, 406, 407, 408, 409, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 424, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, -1, -1, + 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, + 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, + 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, + 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, + 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, -1, -1, 319, 320, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 338, 339, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 350, 351, 352, + 353, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 371, 372, + 373, 374, 375, 376, -1, -1, -1, -1, 381, 382, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 402, + 403, 404, 405, 406, 407, 408, 409, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 424, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, -1, + -1, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 413, -1, 319, 320, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 427, + -1, -1, -1, -1, -1, -1, 338, 339, -1, -1, + -1, -1, -1, -1, -1, -1, 444, 445, 350, 351, + 352, 353, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 371, + 372, 373, 374, 375, 376, -1, -1, -1, 476, 381, + 382, -1, -1, -1, -1, -1, -1, -1, -1, 487, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 402, 403, 404, 405, 406, 407, 408, 409, 506, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 424, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 574, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 587, + 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, + 598, 599, 600, 601, 602, 603, 604, 605, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 706 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int16 yystos[] = +{ + 0, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, + 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, + 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, + 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, + 312, 313, 314, 315, 316, 349, 363, 364, 365, 366, + 367, 376, 377, 378, 379, 380, 381, 382, 396, 397, + 398, 399, 400, 401, 410, 412, 413, 414, 415, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, + 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 440, 441, 442, 474, 475, 478, 479, + 480, 481, 485, 486, 487, 488, 489, 490, 493, 494, + 495, 496, 497, 499, 504, 505, 506, 547, 548, 549, + 505, 343, 375, 339, 339, 349, 375, 349, 550, 340, + 346, 482, 483, 484, 494, 499, 346, 349, 375, 349, + 375, 495, 499, 357, 501, 502, 0, 548, 499, 508, + 343, 375, 397, 491, 492, 375, 498, 341, 349, 500, + 343, 526, 483, 482, 484, 375, 375, 339, 348, 500, + 343, 346, 349, 477, 319, 320, 338, 339, 350, 351, + 352, 353, 371, 372, 373, 374, 375, 402, 403, 404, + 405, 406, 407, 408, 409, 444, 445, 446, 448, 449, + 450, 451, 452, 453, 454, 455, 456, 497, 499, 503, + 500, 349, 494, 499, 509, 510, 507, 348, 340, 346, + 340, 346, 342, 455, 457, 458, 459, 460, 461, 462, + 463, 464, 465, 466, 467, 468, 341, 349, 341, 343, + 344, 349, 383, 384, 385, 386, 388, 389, 390, 391, + 392, 393, 394, 395, 411, 455, 468, 470, 472, 474, + 478, 497, 499, 515, 516, 517, 518, 519, 527, 528, + 529, 530, 533, 534, 537, 538, 539, 546, 551, 500, + 348, 500, 343, 470, 513, 348, 476, 375, 346, 349, + 455, 455, 472, 319, 320, 341, 345, 340, 340, 346, + 382, 470, 339, 455, 346, 358, 499, 375, 511, 512, + 344, 510, 509, 468, 473, 492, 375, 354, 355, 356, + 351, 353, 317, 318, 321, 322, 357, 358, 323, 324, + 361, 360, 359, 325, 327, 326, 362, 342, 342, 468, + 341, 344, 520, 339, 349, 349, 541, 339, 339, 349, + 349, 472, 339, 472, 347, 349, 349, 328, 329, 330, + 331, 332, 333, 334, 335, 336, 337, 348, 471, 346, + 349, 344, 516, 530, 534, 539, 513, 348, 513, 514, + 513, 509, 375, 340, 447, 472, 375, 470, 455, 511, + 500, 346, 349, 344, 455, 455, 455, 457, 457, 458, + 458, 459, 459, 459, 459, 460, 460, 461, 462, 463, + 464, 465, 466, 469, 342, 375, 552, 553, 527, 540, + 516, 542, 472, 349, 472, 347, 470, 470, 513, 344, + 346, 344, 342, 349, 512, 472, 339, 342, 346, 521, + 472, 487, 494, 532, 383, 515, 528, 543, 340, 340, + 344, 513, 347, 473, 342, 553, 344, 375, 340, 339, + 532, 544, 545, 523, 524, 525, 531, 535, 470, 340, + 348, 517, 522, 526, 472, 349, 340, 387, 519, 517, + 343, 513, 340, 472, 522, 523, 527, 536, 349, 344 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int16 yyr1[] = +{ + 0, 443, 444, 445, 445, 445, 445, 445, 445, 445, + 445, 445, 445, 445, 445, 445, 445, 445, 446, 446, + 446, 446, 446, 446, 447, 448, 449, 450, 450, 451, + 451, 452, 452, 453, 454, 454, 454, 455, 455, 455, + 455, 456, 456, 456, 456, 457, 457, 457, 457, 458, + 458, 458, 459, 459, 459, 460, 460, 460, 460, 460, + 461, 461, 461, 462, 462, 463, 463, 464, 464, 465, + 465, 466, 466, 467, 467, 468, 469, 468, 470, 470, + 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, + 471, 472, 472, 473, 474, 474, 474, 474, 474, 474, + 474, 474, 474, 476, 475, 477, 477, 478, 479, 479, + 480, 480, 481, 482, 482, 483, 483, 483, 483, 484, + 485, 485, 485, 485, 485, 486, 486, 486, 486, 486, + 487, 487, 488, 489, 489, 489, 489, 489, 489, 489, + 489, 490, 491, 491, 492, 492, 492, 493, 494, 494, + 495, 495, 495, 495, 495, 495, 495, 496, 496, 496, + 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, + 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, + 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, + 496, 496, 497, 498, 498, 499, 499, 500, 500, 500, + 500, 501, 501, 502, 503, 503, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, + 505, 505, 505, 507, 506, 508, 506, 509, 509, 510, + 510, 511, 511, 512, 512, 513, 513, 513, 514, 514, + 515, 516, 516, 517, 517, 517, 517, 517, 517, 517, + 517, 518, 519, 520, 521, 519, 522, 522, 524, 523, + 525, 523, 526, 526, 527, 527, 528, 528, 529, 529, + 530, 531, 531, 532, 532, 533, 533, 535, 534, 536, + 536, 537, 537, 538, 538, 540, 539, 541, 539, 542, + 539, 543, 543, 544, 544, 545, 545, 546, 546, 546, + 546, 546, 546, 547, 547, 548, 548, 548, 550, 549, + 551, 552, 552, 553, 553 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 1, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 1, 3, 2, 2, 1, 1, 1, 2, 2, 2, + 1, 2, 3, 2, 1, 1, 1, 1, 2, 2, + 2, 1, 1, 1, 1, 1, 3, 3, 3, 1, + 3, 3, 1, 3, 3, 1, 3, 3, 3, 3, + 1, 3, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 0, 6, 1, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 1, 2, 2, 4, 2, 3, 4, + 2, 3, 4, 0, 6, 2, 3, 2, 1, 1, + 2, 3, 3, 2, 3, 2, 1, 2, 1, 1, + 1, 3, 4, 6, 5, 1, 2, 3, 5, 4, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 1, 3, 1, 3, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 1, 1, 3, 2, 3, 2, 3, 3, + 4, 1, 0, 3, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 0, 6, 0, 5, 1, 2, 3, + 4, 1, 3, 1, 2, 1, 3, 4, 1, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 0, 0, 5, 1, 1, 0, 2, + 0, 2, 2, 3, 1, 2, 1, 2, 1, 2, + 5, 3, 1, 1, 4, 1, 2, 0, 8, 0, + 1, 3, 2, 1, 2, 0, 6, 0, 8, 0, + 7, 1, 1, 1, 0, 2, 3, 2, 2, 2, + 3, 2, 2, 1, 2, 1, 1, 1, 0, 3, + 5, 1, 3, 1, 4 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (pParseContext, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +/* This macro is provided for backward compatibility. */ +# ifndef YY_LOCATION_PRINT +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, pParseContext); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, glslang::TParseContext* pParseContext) +{ + FILE *yyoutput = yyo; + YYUSE (yyoutput); + YYUSE (pParseContext); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, glslang::TParseContext* pParseContext) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep, pParseContext); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule, glslang::TParseContext* pParseContext) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], pParseContext); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule, pParseContext); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +#endif + +#ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +yystpcpy (char *yydest, const char *yysrc) +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +#endif + +#ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_T yyn = 0; + char const *yyp = yystr; + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + { + char *yyp = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, glslang::TParseContext* pParseContext) +{ + YYUSE (yyvaluep); + YYUSE (pParseContext); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YYUSE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (glslang::TParseContext* pParseContext) +{ +/* Lookahead token kind. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + + /* Number of syntax errors so far. */ + int yynerrs = 0; + + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, parseContext); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* variable_identifier: IDENTIFIER */ +#line 370 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleVariable((yyvsp[0].lex).loc, (yyvsp[0].lex).symbol, (yyvsp[0].lex).string); + } +#line 4576 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 3: /* primary_expression: variable_identifier */ +#line 376 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 4584 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 4: /* primary_expression: LEFT_PAREN expression RIGHT_PAREN */ +#line 379 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode); + if ((yyval.interm.intermTypedNode)->getAsConstantUnion()) + (yyval.interm.intermTypedNode)->getAsConstantUnion()->setExpression(); + } +#line 4594 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 5: /* primary_expression: FLOATCONSTANT */ +#line 384 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat, (yyvsp[0].lex).loc, true); + } +#line 4602 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 6: /* primary_expression: INTCONSTANT */ +#line 387 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 4610 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 7: /* primary_expression: UINTCONSTANT */ +#line 390 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 4619 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 8: /* primary_expression: BOOLCONSTANT */ +#line 394 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).b, (yyvsp[0].lex).loc, true); + } +#line 4627 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 9: /* primary_expression: STRING_LITERAL */ +#line 398 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).string, (yyvsp[0].lex).loc, true); + } +#line 4635 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 10: /* primary_expression: INT32CONSTANT */ +#line 401 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 4644 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 11: /* primary_expression: UINT32CONSTANT */ +#line 405 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 4653 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 12: /* primary_expression: INT64CONSTANT */ +#line 409 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).i64, (yyvsp[0].lex).loc, true); + } +#line 4662 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 13: /* primary_expression: UINT64CONSTANT */ +#line 413 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).u64, (yyvsp[0].lex).loc, true); + } +#line 4671 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 14: /* primary_expression: INT16CONSTANT */ +#line 417 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt16Check((yyvsp[0].lex).loc, "16-bit integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((short)(yyvsp[0].lex).i, (yyvsp[0].lex).loc, true); + } +#line 4680 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 15: /* primary_expression: UINT16CONSTANT */ +#line 421 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt16Check((yyvsp[0].lex).loc, "16-bit unsigned integer literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((unsigned short)(yyvsp[0].lex).u, (yyvsp[0].lex).loc, true); + } +#line 4689 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 16: /* primary_expression: DOUBLECONSTANT */ +#line 425 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double literal"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtDouble, (yyvsp[0].lex).loc, true); + } +#line 4700 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 17: /* primary_expression: FLOAT16CONSTANT */ +#line 431 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float literal"); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion((yyvsp[0].lex).d, EbtFloat16, (yyvsp[0].lex).loc, true); + } +#line 4709 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 18: /* postfix_expression: primary_expression */ +#line 439 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 4717 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 19: /* postfix_expression: postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET */ +#line 442 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBracketDereference((yyvsp[-2].lex).loc, (yyvsp[-3].interm.intermTypedNode), (yyvsp[-1].interm.intermTypedNode)); + } +#line 4725 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 20: /* postfix_expression: function_call */ +#line 445 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 4733 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 21: /* postfix_expression: postfix_expression DOT IDENTIFIER */ +#line 448 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleDotDereference((yyvsp[0].lex).loc, (yyvsp[-2].interm.intermTypedNode), *(yyvsp[0].lex).string); + } +#line 4741 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 22: /* postfix_expression: postfix_expression INC_OP */ +#line 451 "glslang/MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[-1].interm.intermTypedNode)); + parseContext.lValueErrorCheck((yyvsp[0].lex).loc, "++", (yyvsp[-1].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[0].lex).loc, "++", EOpPostIncrement, (yyvsp[-1].interm.intermTypedNode)); + } +#line 4751 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 23: /* postfix_expression: postfix_expression DEC_OP */ +#line 456 "glslang/MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[-1].interm.intermTypedNode)); + parseContext.lValueErrorCheck((yyvsp[0].lex).loc, "--", (yyvsp[-1].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[0].lex).loc, "--", EOpPostDecrement, (yyvsp[-1].interm.intermTypedNode)); + } +#line 4761 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 24: /* integer_expression: expression */ +#line 464 "glslang/MachineIndependent/glslang.y" + { + parseContext.integerCheck((yyvsp[0].interm.intermTypedNode), "[]"); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 4770 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 25: /* function_call: function_call_or_method */ +#line 471 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleFunctionCall((yyvsp[0].interm).loc, (yyvsp[0].interm).function, (yyvsp[0].interm).intermNode); + delete (yyvsp[0].interm).function; + } +#line 4779 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 26: /* function_call_or_method: function_call_generic */ +#line 478 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 4787 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 27: /* function_call_generic: function_call_header_with_parameters RIGHT_PAREN */ +#line 484 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 4796 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 28: /* function_call_generic: function_call_header_no_parameters RIGHT_PAREN */ +#line 488 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 4805 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 29: /* function_call_header_no_parameters: function_call_header VOID */ +#line 495 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + } +#line 4813 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 30: /* function_call_header_no_parameters: function_call_header */ +#line 498 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 4821 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 31: /* function_call_header_with_parameters: function_call_header assignment_expression */ +#line 504 "glslang/MachineIndependent/glslang.y" + { + TParameter param = { 0, new TType }; + param.type->shallowCopy((yyvsp[0].interm.intermTypedNode)->getType()); + (yyvsp[-1].interm).function->addParameter(param); + (yyval.interm).function = (yyvsp[-1].interm).function; + (yyval.interm).intermNode = (yyvsp[0].interm.intermTypedNode); + } +#line 4833 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 32: /* function_call_header_with_parameters: function_call_header_with_parameters COMMA assignment_expression */ +#line 511 "glslang/MachineIndependent/glslang.y" + { + TParameter param = { 0, new TType }; + param.type->shallowCopy((yyvsp[0].interm.intermTypedNode)->getType()); + (yyvsp[-2].interm).function->addParameter(param); + (yyval.interm).function = (yyvsp[-2].interm).function; + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc); + } +#line 4845 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 33: /* function_call_header: function_identifier LEFT_PAREN */ +#line 521 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-1].interm); + } +#line 4853 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 34: /* function_identifier: type_specifier */ +#line 529 "glslang/MachineIndependent/glslang.y" + { + // Constructor + (yyval.interm).intermNode = 0; + (yyval.interm).function = parseContext.handleConstructorCall((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type)); + } +#line 4863 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 35: /* function_identifier: postfix_expression */ +#line 534 "glslang/MachineIndependent/glslang.y" + { + // + // Should be a method or subroutine call, but we haven't recognized the arguments yet. + // + (yyval.interm).function = 0; + (yyval.interm).intermNode = 0; + + TIntermMethod* method = (yyvsp[0].interm.intermTypedNode)->getAsMethodNode(); + if (method) { + (yyval.interm).function = new TFunction(&method->getMethodName(), TType(EbtInt), EOpArrayLength); + (yyval.interm).intermNode = method->getObject(); + } else { + TIntermSymbol* symbol = (yyvsp[0].interm.intermTypedNode)->getAsSymbolNode(); + if (symbol) { + parseContext.reservedErrorCheck(symbol->getLoc(), symbol->getName()); + TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid)); + (yyval.interm).function = function; + } else + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "function call, method, or subroutine call expected", "", ""); + } + + if ((yyval.interm).function == 0) { + // error recover + TString* empty = NewPoolTString(""); + (yyval.interm).function = new TFunction(empty, TType(EbtVoid), EOpNull); + } + } +#line 4895 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 36: /* function_identifier: non_uniform_qualifier */ +#line 562 "glslang/MachineIndependent/glslang.y" + { + // Constructor + (yyval.interm).intermNode = 0; + (yyval.interm).function = parseContext.handleConstructorCall((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type)); + } +#line 4905 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 37: /* unary_expression: postfix_expression */ +#line 571 "glslang/MachineIndependent/glslang.y" + { + parseContext.variableCheck((yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + if (TIntermMethod* method = (yyvsp[0].interm.intermTypedNode)->getAsMethodNode()) + parseContext.error((yyvsp[0].interm.intermTypedNode)->getLoc(), "incomplete method syntax", method->getMethodName().c_str(), ""); + } +#line 4916 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 38: /* unary_expression: INC_OP unary_expression */ +#line 577 "glslang/MachineIndependent/glslang.y" + { + parseContext.lValueErrorCheck((yyvsp[-1].lex).loc, "++", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].lex).loc, "++", EOpPreIncrement, (yyvsp[0].interm.intermTypedNode)); + } +#line 4925 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 39: /* unary_expression: DEC_OP unary_expression */ +#line 581 "glslang/MachineIndependent/glslang.y" + { + parseContext.lValueErrorCheck((yyvsp[-1].lex).loc, "--", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].lex).loc, "--", EOpPreDecrement, (yyvsp[0].interm.intermTypedNode)); + } +#line 4934 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 40: /* unary_expression: unary_operator unary_expression */ +#line 585 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm).op != EOpNull) { + char errorOp[2] = {0, 0}; + switch((yyvsp[-1].interm).op) { + case EOpNegative: errorOp[0] = '-'; break; + case EOpLogicalNot: errorOp[0] = '!'; break; + case EOpBitwiseNot: errorOp[0] = '~'; break; + default: break; // some compilers want this + } + (yyval.interm.intermTypedNode) = parseContext.handleUnaryMath((yyvsp[-1].interm).loc, errorOp, (yyvsp[-1].interm).op, (yyvsp[0].interm.intermTypedNode)); + } else { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + if ((yyval.interm.intermTypedNode)->getAsConstantUnion()) + (yyval.interm.intermTypedNode)->getAsConstantUnion()->setExpression(); + } + } +#line 4955 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 41: /* unary_operator: PLUS */ +#line 605 "glslang/MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpNull; } +#line 4961 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 42: /* unary_operator: DASH */ +#line 606 "glslang/MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpNegative; } +#line 4967 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 43: /* unary_operator: BANG */ +#line 607 "glslang/MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpLogicalNot; } +#line 4973 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 44: /* unary_operator: TILDE */ +#line 608 "glslang/MachineIndependent/glslang.y" + { (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpBitwiseNot; + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise not"); } +#line 4980 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 45: /* multiplicative_expression: unary_expression */ +#line 614 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 4986 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 46: /* multiplicative_expression: multiplicative_expression STAR unary_expression */ +#line 615 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "*", EOpMul, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 4996 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 47: /* multiplicative_expression: multiplicative_expression SLASH unary_expression */ +#line 620 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "/", EOpDiv, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5006 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 48: /* multiplicative_expression: multiplicative_expression PERCENT unary_expression */ +#line 625 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "%"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "%", EOpMod, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5017 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 49: /* additive_expression: multiplicative_expression */ +#line 634 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5023 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 50: /* additive_expression: additive_expression PLUS multiplicative_expression */ +#line 635 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "+", EOpAdd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5033 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 51: /* additive_expression: additive_expression DASH multiplicative_expression */ +#line 640 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "-", EOpSub, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5043 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 52: /* shift_expression: additive_expression */ +#line 648 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5049 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 53: /* shift_expression: shift_expression LEFT_OP additive_expression */ +#line 649 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bit shift left"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<<", EOpLeftShift, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5060 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 54: /* shift_expression: shift_expression RIGHT_OP additive_expression */ +#line 655 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bit shift right"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">>", EOpRightShift, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5071 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 55: /* relational_expression: shift_expression */ +#line 664 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5077 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 56: /* relational_expression: relational_expression LEFT_ANGLE shift_expression */ +#line 665 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<", EOpLessThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5087 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 57: /* relational_expression: relational_expression RIGHT_ANGLE shift_expression */ +#line 670 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">", EOpGreaterThan, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5097 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 58: /* relational_expression: relational_expression LE_OP shift_expression */ +#line 675 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "<=", EOpLessThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5107 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 59: /* relational_expression: relational_expression GE_OP shift_expression */ +#line 680 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, ">=", EOpGreaterThanEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5117 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 60: /* equality_expression: relational_expression */ +#line 688 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5123 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 61: /* equality_expression: equality_expression EQ_OP relational_expression */ +#line 689 "glslang/MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array comparison"); + parseContext.opaqueCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + parseContext.specializationCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + parseContext.referenceCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "=="); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "==", EOpEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5137 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 62: /* equality_expression: equality_expression NE_OP relational_expression */ +#line 698 "glslang/MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array comparison"); + parseContext.opaqueCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + parseContext.specializationCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + parseContext.referenceCheck((yyvsp[-1].lex).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "!="); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "!=", EOpNotEqual, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5151 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 63: /* and_expression: equality_expression */ +#line 710 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5157 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 64: /* and_expression: and_expression AMPERSAND equality_expression */ +#line 711 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise and"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "&", EOpAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5168 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 65: /* exclusive_or_expression: and_expression */ +#line 720 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5174 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 66: /* exclusive_or_expression: exclusive_or_expression CARET and_expression */ +#line 721 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise exclusive or"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "^", EOpExclusiveOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5185 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 67: /* inclusive_or_expression: exclusive_or_expression */ +#line 730 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5191 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 68: /* inclusive_or_expression: inclusive_or_expression VERTICAL_BAR exclusive_or_expression */ +#line 731 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[-1].lex).loc, "bitwise inclusive or"); + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "|", EOpInclusiveOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 5202 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 69: /* logical_and_expression: inclusive_or_expression */ +#line 740 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5208 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 70: /* logical_and_expression: logical_and_expression AND_OP inclusive_or_expression */ +#line 741 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "&&", EOpLogicalAnd, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5218 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 71: /* logical_xor_expression: logical_and_expression */ +#line 749 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5224 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 72: /* logical_xor_expression: logical_xor_expression XOR_OP logical_and_expression */ +#line 750 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "^^", EOpLogicalXor, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5234 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 73: /* logical_or_expression: logical_xor_expression */ +#line 758 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5240 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 74: /* logical_or_expression: logical_or_expression OR_OP logical_xor_expression */ +#line 759 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.handleBinaryMath((yyvsp[-1].lex).loc, "||", EOpLogicalOr, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) + (yyval.interm.intermTypedNode) = parseContext.intermediate.addConstantUnion(false, (yyvsp[-1].lex).loc); + } +#line 5250 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 75: /* conditional_expression: logical_or_expression */ +#line 767 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5256 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 76: /* $@1: %empty */ +#line 768 "glslang/MachineIndependent/glslang.y" + { + ++parseContext.controlFlowNestingLevel; + } +#line 5264 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 77: /* conditional_expression: logical_or_expression QUESTION $@1 expression COLON assignment_expression */ +#line 771 "glslang/MachineIndependent/glslang.y" + { + --parseContext.controlFlowNestingLevel; + parseContext.boolCheck((yyvsp[-4].lex).loc, (yyvsp[-5].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-4].lex).loc, "?", (yyvsp[-5].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].lex).loc, ":", (yyvsp[-2].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].lex).loc, ":", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addSelection((yyvsp[-5].interm.intermTypedNode), (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-4].lex).loc); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.binaryOpError((yyvsp[-4].lex).loc, ":", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString()); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } + } +#line 5281 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 78: /* assignment_expression: conditional_expression */ +#line 786 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); } +#line 5287 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 79: /* assignment_expression: unary_expression assignment_operator assignment_expression */ +#line 787 "glslang/MachineIndependent/glslang.y" + { + parseContext.arrayObjectCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "array assignment"); + parseContext.opaqueCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.storage16BitAssignmentCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.specializationCheck((yyvsp[-1].interm).loc, (yyvsp[-2].interm.intermTypedNode)->getType(), "="); + parseContext.lValueErrorCheck((yyvsp[-1].interm).loc, "assign", (yyvsp[-2].interm.intermTypedNode)); + parseContext.rValueErrorCheck((yyvsp[-1].interm).loc, "assign", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.addAssign((yyvsp[-1].interm).loc, (yyvsp[-1].interm).op, (yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.assignError((yyvsp[-1].interm).loc, "assign", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString()); + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } + } +#line 5305 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 80: /* assignment_operator: EQUAL */ +#line 803 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpAssign; + } +#line 5314 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 81: /* assignment_operator: MUL_ASSIGN */ +#line 807 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpMulAssign; + } +#line 5323 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 82: /* assignment_operator: DIV_ASSIGN */ +#line 811 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpDivAssign; + } +#line 5332 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 83: /* assignment_operator: MOD_ASSIGN */ +#line 815 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "%="); + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpModAssign; + } +#line 5342 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 84: /* assignment_operator: ADD_ASSIGN */ +#line 820 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpAddAssign; + } +#line 5351 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 85: /* assignment_operator: SUB_ASSIGN */ +#line 824 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).op = EOpSubAssign; + } +#line 5360 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 86: /* assignment_operator: LEFT_ASSIGN */ +#line 828 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bit-shift left assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpLeftShiftAssign; + } +#line 5369 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 87: /* assignment_operator: RIGHT_ASSIGN */ +#line 832 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bit-shift right assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpRightShiftAssign; + } +#line 5378 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 88: /* assignment_operator: AND_ASSIGN */ +#line 836 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-and assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpAndAssign; + } +#line 5387 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 89: /* assignment_operator: XOR_ASSIGN */ +#line 840 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-xor assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpExclusiveOrAssign; + } +#line 5396 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 90: /* assignment_operator: OR_ASSIGN */ +#line 844 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "bitwise-or assign"); + (yyval.interm).loc = (yyvsp[0].lex).loc; (yyval.interm).op = EOpInclusiveOrAssign; + } +#line 5405 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 91: /* expression: assignment_expression */ +#line 851 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 5413 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 92: /* expression: expression COMMA assignment_expression */ +#line 854 "glslang/MachineIndependent/glslang.y" + { + parseContext.samplerConstructorLocationCheck((yyvsp[-1].lex).loc, ",", (yyvsp[0].interm.intermTypedNode)); + (yyval.interm.intermTypedNode) = parseContext.intermediate.addComma((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode), (yyvsp[-1].lex).loc); + if ((yyval.interm.intermTypedNode) == 0) { + parseContext.binaryOpError((yyvsp[-1].lex).loc, ",", (yyvsp[-2].interm.intermTypedNode)->getCompleteString(), (yyvsp[0].interm.intermTypedNode)->getCompleteString()); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } + } +#line 5426 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 93: /* constant_expression: conditional_expression */ +#line 865 "glslang/MachineIndependent/glslang.y" + { + parseContext.constantValueCheck((yyvsp[0].interm.intermTypedNode), ""); + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 5435 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 94: /* declaration: function_prototype SEMICOLON */ +#line 872 "glslang/MachineIndependent/glslang.y" + { + parseContext.handleFunctionDeclarator((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).function, true /* prototype */); + (yyval.interm.intermNode) = 0; + // TODO: 4.0 functionality: subroutines: make the identifier a user type for this signature + } +#line 5445 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 95: /* declaration: init_declarator_list SEMICOLON */ +#line 877 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm).intermNode && (yyvsp[-1].interm).intermNode->getAsAggregate()) + (yyvsp[-1].interm).intermNode->getAsAggregate()->setOperator(EOpSequence); + (yyval.interm.intermNode) = (yyvsp[-1].interm).intermNode; + } +#line 5455 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 96: /* declaration: PRECISION precision_qualifier type_specifier SEMICOLON */ +#line 882 "glslang/MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[-3].lex).loc, ENoProfile, 130, 0, "precision statement"); + // lazy setting of the previous scope's defaults, has effect only the first time it is called in a particular scope + parseContext.symbolTable.setPreviousDefaultPrecisions(&parseContext.defaultPrecision[0]); + parseContext.setDefaultPrecision((yyvsp[-3].lex).loc, (yyvsp[-1].interm.type), (yyvsp[-2].interm.type).qualifier.precision); + (yyval.interm.intermNode) = 0; + } +#line 5467 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 97: /* declaration: block_structure SEMICOLON */ +#line 889 "glslang/MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-1].interm).loc, *(yyvsp[-1].interm).typeList); + (yyval.interm.intermNode) = 0; + } +#line 5476 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 98: /* declaration: block_structure IDENTIFIER SEMICOLON */ +#line 893 "glslang/MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-2].interm).loc, *(yyvsp[-2].interm).typeList, (yyvsp[-1].lex).string); + (yyval.interm.intermNode) = 0; + } +#line 5485 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 99: /* declaration: block_structure IDENTIFIER array_specifier SEMICOLON */ +#line 897 "glslang/MachineIndependent/glslang.y" + { + parseContext.declareBlock((yyvsp[-3].interm).loc, *(yyvsp[-3].interm).typeList, (yyvsp[-2].lex).string, (yyvsp[-1].interm).arraySizes); + (yyval.interm.intermNode) = 0; + } +#line 5494 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 100: /* declaration: type_qualifier SEMICOLON */ +#line 901 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalQualifierFixCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier); + parseContext.updateStandaloneQualifierDefaults((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type)); + (yyval.interm.intermNode) = 0; + } +#line 5504 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 101: /* declaration: type_qualifier IDENTIFIER SEMICOLON */ +#line 906 "glslang/MachineIndependent/glslang.y" + { + parseContext.checkNoShaderLayouts((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).shaderQualifiers); + parseContext.addQualifierToExisting((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).qualifier, *(yyvsp[-1].lex).string); + (yyval.interm.intermNode) = 0; + } +#line 5514 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 102: /* declaration: type_qualifier IDENTIFIER identifier_list SEMICOLON */ +#line 911 "glslang/MachineIndependent/glslang.y" + { + parseContext.checkNoShaderLayouts((yyvsp[-3].interm.type).loc, (yyvsp[-3].interm.type).shaderQualifiers); + (yyvsp[-1].interm.identifierList)->push_back((yyvsp[-2].lex).string); + parseContext.addQualifierToExisting((yyvsp[-3].interm.type).loc, (yyvsp[-3].interm.type).qualifier, *(yyvsp[-1].interm.identifierList)); + (yyval.interm.intermNode) = 0; + } +#line 5525 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 103: /* $@2: %empty */ +#line 920 "glslang/MachineIndependent/glslang.y" + { parseContext.nestedBlockCheck((yyvsp[-2].interm.type).loc); } +#line 5531 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 104: /* block_structure: type_qualifier IDENTIFIER LEFT_BRACE $@2 struct_declaration_list RIGHT_BRACE */ +#line 920 "glslang/MachineIndependent/glslang.y" + { + --parseContext.blockNestingLevel; + parseContext.blockName = (yyvsp[-4].lex).string; + parseContext.globalQualifierFixCheck((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).qualifier); + parseContext.checkNoShaderLayouts((yyvsp[-5].interm.type).loc, (yyvsp[-5].interm.type).shaderQualifiers); + parseContext.currentBlockQualifier = (yyvsp[-5].interm.type).qualifier; + (yyval.interm).loc = (yyvsp[-5].interm.type).loc; + (yyval.interm).typeList = (yyvsp[-1].interm.typeList); + } +#line 5545 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 105: /* identifier_list: COMMA IDENTIFIER */ +#line 931 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.identifierList) = new TIdentifierList; + (yyval.interm.identifierList)->push_back((yyvsp[0].lex).string); + } +#line 5554 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 106: /* identifier_list: identifier_list COMMA IDENTIFIER */ +#line 935 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.identifierList) = (yyvsp[-2].interm.identifierList); + (yyval.interm.identifierList)->push_back((yyvsp[0].lex).string); + } +#line 5563 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 107: /* function_prototype: function_declarator RIGHT_PAREN */ +#line 942 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).function = (yyvsp[-1].interm.function); + (yyval.interm).loc = (yyvsp[0].lex).loc; + } +#line 5572 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 108: /* function_declarator: function_header */ +#line 949 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.function) = (yyvsp[0].interm.function); + } +#line 5580 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 109: /* function_declarator: function_header_with_parameters */ +#line 952 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.function) = (yyvsp[0].interm.function); + } +#line 5588 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 110: /* function_header_with_parameters: function_header parameter_declaration */ +#line 959 "glslang/MachineIndependent/glslang.y" + { + // Add the parameter + (yyval.interm.function) = (yyvsp[-1].interm.function); + if ((yyvsp[0].interm).param.type->getBasicType() != EbtVoid) + (yyvsp[-1].interm.function)->addParameter((yyvsp[0].interm).param); + else + delete (yyvsp[0].interm).param.type; + } +#line 5601 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 111: /* function_header_with_parameters: function_header_with_parameters COMMA parameter_declaration */ +#line 967 "glslang/MachineIndependent/glslang.y" + { + // + // Only first parameter of one-parameter functions can be void + // The check for named parameters not being void is done in parameter_declarator + // + if ((yyvsp[0].interm).param.type->getBasicType() == EbtVoid) { + // + // This parameter > first is void + // + parseContext.error((yyvsp[-1].lex).loc, "cannot be an argument type except for '(void)'", "void", ""); + delete (yyvsp[0].interm).param.type; + } else { + // Add the parameter + (yyval.interm.function) = (yyvsp[-2].interm.function); + (yyvsp[-2].interm.function)->addParameter((yyvsp[0].interm).param); + } + } +#line 5623 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 112: /* function_header: fully_specified_type IDENTIFIER LEFT_PAREN */ +#line 987 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).qualifier.storage != EvqGlobal && (yyvsp[-2].interm.type).qualifier.storage != EvqTemporary) { + parseContext.error((yyvsp[-1].lex).loc, "no qualifiers allowed for function return", + GetStorageQualifierString((yyvsp[-2].interm.type).qualifier.storage), ""); + } + if ((yyvsp[-2].interm.type).arraySizes) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + + // Add the function as a prototype after parsing it (we do not support recursion) + TFunction *function; + TType type((yyvsp[-2].interm.type)); + + // Potentially rename shader entry point function. No-op most of the time. + parseContext.renameShaderFunction((yyvsp[-1].lex).string); + + // Make the function + function = new TFunction((yyvsp[-1].lex).string, type); + (yyval.interm.function) = function; + } +#line 5647 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 113: /* parameter_declarator: type_specifier IDENTIFIER */ +#line 1010 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-1].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-1].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck((yyvsp[-1].interm.type).loc, *(yyvsp[-1].interm.type).arraySizes); + } + if ((yyvsp[-1].interm.type).basicType == EbtVoid) { + parseContext.error((yyvsp[0].lex).loc, "illegal use of type 'void'", (yyvsp[0].lex).string->c_str(), ""); + } + parseContext.reservedErrorCheck((yyvsp[0].lex).loc, *(yyvsp[0].lex).string); + + TParameter param = {(yyvsp[0].lex).string, new TType((yyvsp[-1].interm.type))}; + (yyval.interm).loc = (yyvsp[0].lex).loc; + (yyval.interm).param = param; + } +#line 5667 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 114: /* parameter_declarator: type_specifier IDENTIFIER array_specifier */ +#line 1025 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + TType* type = new TType((yyvsp[-2].interm.type)); + type->transferArraySizes((yyvsp[0].interm).arraySizes); + type->copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + + parseContext.arrayOfArrayVersionCheck((yyvsp[-1].lex).loc, type->getArraySizes()); + parseContext.arraySizeRequiredCheck((yyvsp[0].interm).loc, *(yyvsp[0].interm).arraySizes); + parseContext.reservedErrorCheck((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string); + + TParameter param = { (yyvsp[-1].lex).string, type }; + + (yyval.interm).loc = (yyvsp[-1].lex).loc; + (yyval.interm).param = param; + } +#line 5691 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 115: /* parameter_declaration: type_qualifier parameter_declarator */ +#line 1050 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + if ((yyvsp[-1].interm.type).qualifier.precision != EpqNone) + (yyval.interm).param.type->getQualifier().precision = (yyvsp[-1].interm.type).qualifier.precision; + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier()); + + parseContext.checkNoShaderLayouts((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, (yyvsp[-1].interm.type).qualifier.storage, *(yyval.interm).param.type); + parseContext.paramCheckFix((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, *(yyval.interm).param.type); + + } +#line 5707 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 116: /* parameter_declaration: parameter_declarator */ +#line 1061 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, EvqIn, *(yyvsp[0].interm).param.type); + parseContext.paramCheckFixStorage((yyvsp[0].interm).loc, EvqTemporary, *(yyval.interm).param.type); + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier()); + } +#line 5719 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 117: /* parameter_declaration: type_qualifier parameter_type_specifier */ +#line 1071 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + if ((yyvsp[-1].interm.type).qualifier.precision != EpqNone) + (yyval.interm).param.type->getQualifier().precision = (yyvsp[-1].interm.type).qualifier.precision; + parseContext.precisionQualifierCheck((yyvsp[-1].interm.type).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier()); + + parseContext.checkNoShaderLayouts((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, (yyvsp[-1].interm.type).qualifier.storage, *(yyval.interm).param.type); + parseContext.paramCheckFix((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, *(yyval.interm).param.type); + } +#line 5734 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 118: /* parameter_declaration: parameter_type_specifier */ +#line 1081 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + + parseContext.parameterTypeCheck((yyvsp[0].interm).loc, EvqIn, *(yyvsp[0].interm).param.type); + parseContext.paramCheckFixStorage((yyvsp[0].interm).loc, EvqTemporary, *(yyval.interm).param.type); + parseContext.precisionQualifierCheck((yyval.interm).loc, (yyval.interm).param.type->getBasicType(), (yyval.interm).param.type->getQualifier()); + } +#line 5746 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 119: /* parameter_type_specifier: type_specifier */ +#line 1091 "glslang/MachineIndependent/glslang.y" + { + TParameter param = { 0, new TType((yyvsp[0].interm.type)) }; + (yyval.interm).param = param; + if ((yyvsp[0].interm.type).arraySizes) + parseContext.arraySizeRequiredCheck((yyvsp[0].interm.type).loc, *(yyvsp[0].interm.type).arraySizes); + } +#line 5757 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 120: /* init_declarator_list: single_declaration */ +#line 1100 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[0].interm); + } +#line 5765 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 121: /* init_declarator_list: init_declarator_list COMMA IDENTIFIER */ +#line 1103 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-2].interm); + parseContext.declareVariable((yyvsp[0].lex).loc, *(yyvsp[0].lex).string, (yyvsp[-2].interm).type); + } +#line 5774 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 122: /* init_declarator_list: init_declarator_list COMMA IDENTIFIER array_specifier */ +#line 1107 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-3].interm); + parseContext.declareVariable((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string, (yyvsp[-3].interm).type, (yyvsp[0].interm).arraySizes); + } +#line 5783 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 123: /* init_declarator_list: init_declarator_list COMMA IDENTIFIER array_specifier EQUAL initializer */ +#line 1111 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-5].interm).type; + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-3].lex).loc, *(yyvsp[-3].lex).string, (yyvsp[-5].interm).type, (yyvsp[-2].interm).arraySizes, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-5].interm).intermNode, initNode, (yyvsp[-1].lex).loc); + } +#line 5793 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 124: /* init_declarator_list: init_declarator_list COMMA IDENTIFIER EQUAL initializer */ +#line 1116 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-4].interm).type; + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-4].interm).type, 0, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate((yyvsp[-4].interm).intermNode, initNode, (yyvsp[-1].lex).loc); + } +#line 5803 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 125: /* single_declaration: fully_specified_type */ +#line 1124 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[0].interm.type); + (yyval.interm).intermNode = 0; + + parseContext.declareTypeDefaults((yyval.interm).loc, (yyval.interm).type); + + } +#line 5815 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 126: /* single_declaration: fully_specified_type IDENTIFIER */ +#line 1131 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-1].interm.type); + (yyval.interm).intermNode = 0; + parseContext.declareVariable((yyvsp[0].lex).loc, *(yyvsp[0].lex).string, (yyvsp[-1].interm.type)); + } +#line 5825 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 127: /* single_declaration: fully_specified_type IDENTIFIER array_specifier */ +#line 1136 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-2].interm.type); + (yyval.interm).intermNode = 0; + parseContext.declareVariable((yyvsp[-1].lex).loc, *(yyvsp[-1].lex).string, (yyvsp[-2].interm.type), (yyvsp[0].interm).arraySizes); + } +#line 5835 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 128: /* single_declaration: fully_specified_type IDENTIFIER array_specifier EQUAL initializer */ +#line 1141 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-4].interm.type); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-3].lex).loc, *(yyvsp[-3].lex).string, (yyvsp[-4].interm.type), (yyvsp[-2].interm).arraySizes, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate(0, initNode, (yyvsp[-1].lex).loc); + } +#line 5845 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 129: /* single_declaration: fully_specified_type IDENTIFIER EQUAL initializer */ +#line 1146 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).type = (yyvsp[-3].interm.type); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), 0, (yyvsp[0].interm.intermTypedNode)); + (yyval.interm).intermNode = parseContext.intermediate.growAggregate(0, initNode, (yyvsp[-1].lex).loc); + } +#line 5855 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 130: /* fully_specified_type: type_specifier */ +#line 1155 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + + parseContext.globalQualifierTypeCheck((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier, (yyval.interm.type)); + if ((yyvsp[0].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[0].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[0].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + } + parseContext.precisionQualifierCheck((yyval.interm.type).loc, (yyval.interm.type).basicType, (yyval.interm.type).qualifier); + } +#line 5870 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 131: /* fully_specified_type: type_qualifier type_specifier */ +#line 1165 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalQualifierFixCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier); + parseContext.globalQualifierTypeCheck((yyvsp[-1].interm.type).loc, (yyvsp[-1].interm.type).qualifier, (yyvsp[0].interm.type)); + + if ((yyvsp[0].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[0].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[0].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + } + + if ((yyvsp[0].interm.type).arraySizes && parseContext.arrayQualifierError((yyvsp[0].interm.type).loc, (yyvsp[-1].interm.type).qualifier)) + (yyvsp[0].interm.type).arraySizes = nullptr; + + parseContext.checkNoShaderLayouts((yyvsp[0].interm.type).loc, (yyvsp[-1].interm.type).shaderQualifiers); + (yyvsp[0].interm.type).shaderQualifiers.merge((yyvsp[-1].interm.type).shaderQualifiers); + parseContext.mergeQualifiers((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier, (yyvsp[-1].interm.type).qualifier, true); + parseContext.precisionQualifierCheck((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).basicType, (yyvsp[0].interm.type).qualifier); + + (yyval.interm.type) = (yyvsp[0].interm.type); + + if (! (yyval.interm.type).qualifier.isInterpolation() && + ((parseContext.language == EShLangVertex && (yyval.interm.type).qualifier.storage == EvqVaryingOut) || + (parseContext.language == EShLangFragment && (yyval.interm.type).qualifier.storage == EvqVaryingIn))) + (yyval.interm.type).qualifier.smooth = true; + } +#line 5899 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 132: /* invariant_qualifier: INVARIANT */ +#line 1192 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "invariant"); + parseContext.profileRequires((yyval.interm.type).loc, ENoProfile, 120, 0, "invariant"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.invariant = true; + } +#line 5910 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 133: /* interpolation_qualifier: SMOOTH */ +#line 1201 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "smooth"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "smooth"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "smooth"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.smooth = true; + } +#line 5922 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 134: /* interpolation_qualifier: FLAT */ +#line 1208 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "flat"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "flat"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "flat"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.flat = true; + } +#line 5934 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 135: /* interpolation_qualifier: NOPERSPECTIVE */ +#line 1216 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "noperspective"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_NV_shader_noperspective_interpolation, "noperspective"); + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "noperspective"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.nopersp = true; + } +#line 5946 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 136: /* interpolation_qualifier: EXPLICITINTERPAMD */ +#line 1223 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "__explicitInterpAMD"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 450, E_GL_AMD_shader_explicit_vertex_parameter, "explicit interpolation"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.explicitInterp = true; + } +#line 5958 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 137: /* interpolation_qualifier: PERVERTEXNV */ +#line 1230 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "pervertexNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECompatibilityProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 0, E_GL_NV_fragment_shader_barycentric, "fragment shader barycentric"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.pervertexNV = true; + } +#line 5971 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 138: /* interpolation_qualifier: PERPRIMITIVENV */ +#line 1238 "glslang/MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "perprimitiveNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangFragmentMask | EShLangMeshNVMask), "perprimitiveNV"); + // Fragment shader stage doesn't check for extension. So we explicitly add below extension check. + if (parseContext.language == EShLangFragment) + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_NV_mesh_shader, "perprimitiveNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perPrimitiveNV = true; + } +#line 5986 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 139: /* interpolation_qualifier: PERVIEWNV */ +#line 1248 "glslang/MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "perviewNV"); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangMeshNV, "perviewNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perViewNV = true; + } +#line 5998 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 140: /* interpolation_qualifier: PERTASKNV */ +#line 1255 "glslang/MachineIndependent/glslang.y" + { + // No need for profile version or extension check. Shader stage already checks both. + parseContext.globalCheck((yyvsp[0].lex).loc, "taskNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTaskNVMask | EShLangMeshNVMask), "taskNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.perTaskNV = true; + } +#line 6010 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 141: /* layout_qualifier: LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN */ +#line 1266 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + } +#line 6018 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 142: /* layout_qualifier_id_list: layout_qualifier_id */ +#line 1272 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6026 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 143: /* layout_qualifier_id_list: layout_qualifier_id_list COMMA layout_qualifier_id */ +#line 1275 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-2].interm.type); + (yyval.interm.type).shaderQualifiers.merge((yyvsp[0].interm.type).shaderQualifiers); + parseContext.mergeObjectLayoutQualifiers((yyval.interm.type).qualifier, (yyvsp[0].interm.type).qualifier, false); + } +#line 6036 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 144: /* layout_qualifier_id: IDENTIFIER */ +#line 1282 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.setLayoutQualifier((yyvsp[0].lex).loc, (yyval.interm.type), *(yyvsp[0].lex).string); + } +#line 6045 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 145: /* layout_qualifier_id: IDENTIFIER EQUAL constant_expression */ +#line 1286 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[-2].lex).loc); + parseContext.setLayoutQualifier((yyvsp[-2].lex).loc, (yyval.interm.type), *(yyvsp[-2].lex).string, (yyvsp[0].interm.intermTypedNode)); + } +#line 6054 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 146: /* layout_qualifier_id: SHARED */ +#line 1290 "glslang/MachineIndependent/glslang.y" + { // because "shared" is both an identifier and a keyword + (yyval.interm.type).init((yyvsp[0].lex).loc); + TString strShared("shared"); + parseContext.setLayoutQualifier((yyvsp[0].lex).loc, (yyval.interm.type), strShared); + } +#line 6064 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 147: /* precise_qualifier: PRECISE */ +#line 1299 "glslang/MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyval.interm.type).loc, ECoreProfile | ECompatibilityProfile, 400, E_GL_ARB_gpu_shader5, "precise"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "precise"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.noContraction = true; + } +#line 6075 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 148: /* type_qualifier: single_type_qualifier */ +#line 1309 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6083 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 149: /* type_qualifier: type_qualifier single_type_qualifier */ +#line 1312 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + if ((yyval.interm.type).basicType == EbtVoid) + (yyval.interm.type).basicType = (yyvsp[0].interm.type).basicType; + + (yyval.interm.type).shaderQualifiers.merge((yyvsp[0].interm.type).shaderQualifiers); + parseContext.mergeQualifiers((yyval.interm.type).loc, (yyval.interm.type).qualifier, (yyvsp[0].interm.type).qualifier, false); + } +#line 6096 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 150: /* single_type_qualifier: storage_qualifier */ +#line 1323 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6104 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 151: /* single_type_qualifier: layout_qualifier */ +#line 1326 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6112 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 152: /* single_type_qualifier: precision_qualifier */ +#line 1329 "glslang/MachineIndependent/glslang.y" + { + parseContext.checkPrecisionQualifier((yyvsp[0].interm.type).loc, (yyvsp[0].interm.type).qualifier.precision); + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6121 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 153: /* single_type_qualifier: interpolation_qualifier */ +#line 1333 "glslang/MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6130 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 154: /* single_type_qualifier: invariant_qualifier */ +#line 1337 "glslang/MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6139 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 155: /* single_type_qualifier: precise_qualifier */ +#line 1342 "glslang/MachineIndependent/glslang.y" + { + // allow inheritance of storage qualifier from block declaration + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6148 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 156: /* single_type_qualifier: non_uniform_qualifier */ +#line 1346 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + } +#line 6156 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 157: /* storage_qualifier: CONST */ +#line 1353 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqConst; // will later turn into EvqConstReadOnly, if the initializer is not constant + } +#line 6165 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 158: /* storage_qualifier: INOUT */ +#line 1357 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "inout"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqInOut; + } +#line 6175 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 159: /* storage_qualifier: IN */ +#line 1362 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "in"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + // whether this is a parameter "in" or a pipeline "in" will get sorted out a bit later + (yyval.interm.type).qualifier.storage = EvqIn; + } +#line 6186 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 160: /* storage_qualifier: OUT */ +#line 1368 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "out"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + // whether this is a parameter "out" or a pipeline "out" will get sorted out a bit later + (yyval.interm.type).qualifier.storage = EvqOut; + } +#line 6197 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 161: /* storage_qualifier: CENTROID */ +#line 1374 "glslang/MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 120, 0, "centroid"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 300, 0, "centroid"); + parseContext.globalCheck((yyvsp[0].lex).loc, "centroid"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.centroid = true; + } +#line 6209 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 162: /* storage_qualifier: UNIFORM */ +#line 1381 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "uniform"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqUniform; + } +#line 6219 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 163: /* storage_qualifier: SHARED */ +#line 1386 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "shared"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, 430, E_GL_ARB_compute_shader, "shared"); + parseContext.profileRequires((yyvsp[0].lex).loc, EEsProfile, 310, 0, "shared"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangComputeMask | EShLangMeshNVMask | EShLangTaskNVMask), "shared"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqShared; + } +#line 6232 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 164: /* storage_qualifier: BUFFER */ +#line 1394 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "buffer"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqBuffer; + } +#line 6242 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 165: /* storage_qualifier: ATTRIBUTE */ +#line 1400 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangVertex, "attribute"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ECoreProfile, 130, "attribute"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ENoProfile, 130, "attribute"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, ECoreProfile, 420, "attribute"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, EEsProfile, 300, "attribute"); + + parseContext.globalCheck((yyvsp[0].lex).loc, "attribute"); + + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqVaryingIn; + } +#line 6259 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 166: /* storage_qualifier: VARYING */ +#line 1412 "glslang/MachineIndependent/glslang.y" + { + parseContext.checkDeprecated((yyvsp[0].lex).loc, ENoProfile, 130, "varying"); + parseContext.checkDeprecated((yyvsp[0].lex).loc, ECoreProfile, 130, "varying"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, ECoreProfile, 420, "varying"); + parseContext.requireNotRemoved((yyvsp[0].lex).loc, EEsProfile, 300, "varying"); + + parseContext.globalCheck((yyvsp[0].lex).loc, "varying"); + + (yyval.interm.type).init((yyvsp[0].lex).loc); + if (parseContext.language == EShLangVertex) + (yyval.interm.type).qualifier.storage = EvqVaryingOut; + else + (yyval.interm.type).qualifier.storage = EvqVaryingIn; + } +#line 6278 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 167: /* storage_qualifier: PATCH */ +#line 1426 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "patch"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangTessControlMask | EShLangTessEvaluationMask), "patch"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.patch = true; + } +#line 6289 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 168: /* storage_qualifier: SAMPLE */ +#line 1432 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "sample"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.sample = true; + } +#line 6299 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 169: /* storage_qualifier: HITATTRNV */ +#line 1437 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "hitAttributeNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "hitAttributeNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqHitAttr; + } +#line 6312 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 170: /* storage_qualifier: HITATTREXT */ +#line 1445 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "hitAttributeEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangIntersectMask | EShLangClosestHitMask + | EShLangAnyHitMask), "hitAttributeEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "hitAttributeNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqHitAttr; + } +#line 6325 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 171: /* storage_qualifier: PAYLOADNV */ +#line 1453 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayload; + } +#line 6338 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 172: /* storage_qualifier: PAYLOADEXT */ +#line 1461 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayload; + } +#line 6351 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 173: /* storage_qualifier: PAYLOADINNV */ +#line 1469 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadInNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "rayPayloadInNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayloadIn; + } +#line 6364 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 174: /* storage_qualifier: PAYLOADINEXT */ +#line 1477 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "rayPayloadInEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangClosestHitMask | + EShLangAnyHitMask | EShLangMissMask), "rayPayloadInEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "rayPayloadInEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqPayloadIn; + } +#line 6377 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 175: /* storage_qualifier: CALLDATANV */ +#line 1485 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableData; + } +#line 6390 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 176: /* storage_qualifier: CALLDATAEXT */ +#line 1493 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangRayGenMask | + EShLangClosestHitMask | EShLangMissMask | EShLangCallableMask), "callableDataEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableData; + } +#line 6403 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 177: /* storage_qualifier: CALLDATAINNV */ +#line 1501 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataInNV"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInNV"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_NV_ray_tracing, "callableDataInNV"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableDataIn; + } +#line 6415 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 178: /* storage_qualifier: CALLDATAINEXT */ +#line 1508 "glslang/MachineIndependent/glslang.y" + { + parseContext.globalCheck((yyvsp[0].lex).loc, "callableDataInEXT"); + parseContext.requireStage((yyvsp[0].lex).loc, (EShLanguageMask)(EShLangCallableMask), "callableDataInEXT"); + parseContext.profileRequires((yyvsp[0].lex).loc, ECoreProfile, 460, E_GL_EXT_ray_tracing, "callableDataInEXT"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.storage = EvqCallableDataIn; + } +#line 6427 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 179: /* storage_qualifier: COHERENT */ +#line 1515 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.coherent = true; + } +#line 6436 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 180: /* storage_qualifier: DEVICECOHERENT */ +#line 1519 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "devicecoherent"); + (yyval.interm.type).qualifier.devicecoherent = true; + } +#line 6446 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 181: /* storage_qualifier: QUEUEFAMILYCOHERENT */ +#line 1524 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "queuefamilycoherent"); + (yyval.interm.type).qualifier.queuefamilycoherent = true; + } +#line 6456 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 182: /* storage_qualifier: WORKGROUPCOHERENT */ +#line 1529 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "workgroupcoherent"); + (yyval.interm.type).qualifier.workgroupcoherent = true; + } +#line 6466 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 183: /* storage_qualifier: SUBGROUPCOHERENT */ +#line 1534 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "subgroupcoherent"); + (yyval.interm.type).qualifier.subgroupcoherent = true; + } +#line 6476 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 184: /* storage_qualifier: NONPRIVATE */ +#line 1539 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_KHR_memory_scope_semantics, "nonprivate"); + (yyval.interm.type).qualifier.nonprivate = true; + } +#line 6486 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 185: /* storage_qualifier: SHADERCALLCOHERENT */ +#line 1544 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + parseContext.requireExtensions((yyvsp[0].lex).loc, 1, &E_GL_EXT_ray_tracing, "shadercallcoherent"); + (yyval.interm.type).qualifier.shadercallcoherent = true; + } +#line 6496 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 186: /* storage_qualifier: VOLATILE */ +#line 1549 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.volatil = true; + } +#line 6505 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 187: /* storage_qualifier: RESTRICT */ +#line 1553 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.restrict = true; + } +#line 6514 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 188: /* storage_qualifier: READONLY */ +#line 1557 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.readonly = true; + } +#line 6523 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 189: /* storage_qualifier: WRITEONLY */ +#line 1561 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.writeonly = true; + } +#line 6532 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 190: /* storage_qualifier: SUBROUTINE */ +#line 1565 "glslang/MachineIndependent/glslang.y" + { + parseContext.spvRemoved((yyvsp[0].lex).loc, "subroutine"); + parseContext.globalCheck((yyvsp[0].lex).loc, "subroutine"); + parseContext.unimplemented((yyvsp[0].lex).loc, "subroutine"); + (yyval.interm.type).init((yyvsp[0].lex).loc); + } +#line 6543 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 191: /* storage_qualifier: SUBROUTINE LEFT_PAREN type_name_list RIGHT_PAREN */ +#line 1571 "glslang/MachineIndependent/glslang.y" + { + parseContext.spvRemoved((yyvsp[-3].lex).loc, "subroutine"); + parseContext.globalCheck((yyvsp[-3].lex).loc, "subroutine"); + parseContext.unimplemented((yyvsp[-3].lex).loc, "subroutine"); + (yyval.interm.type).init((yyvsp[-3].lex).loc); + } +#line 6554 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 192: /* non_uniform_qualifier: NONUNIFORM */ +#line 1582 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc); + (yyval.interm.type).qualifier.nonUniform = true; + } +#line 6563 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 193: /* type_name_list: IDENTIFIER */ +#line 1589 "glslang/MachineIndependent/glslang.y" + { + // TODO + } +#line 6571 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 194: /* type_name_list: type_name_list COMMA IDENTIFIER */ +#line 1592 "glslang/MachineIndependent/glslang.y" + { + // TODO: 4.0 semantics: subroutines + // 1) make sure each identifier is a type declared earlier with SUBROUTINE + // 2) save all of the identifiers for future comparison with the declared function + } +#line 6581 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 195: /* type_specifier: type_specifier_nonarray type_parameter_specifier_opt */ +#line 1601 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[-1].interm.type); + (yyval.interm.type).qualifier.precision = parseContext.getDefaultPrecision((yyval.interm.type)); + (yyval.interm.type).typeParameters = (yyvsp[0].interm.typeParameters); + } +#line 6591 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 196: /* type_specifier: type_specifier_nonarray type_parameter_specifier_opt array_specifier */ +#line 1606 "glslang/MachineIndependent/glslang.y" + { + parseContext.arrayOfArrayVersionCheck((yyvsp[0].interm).loc, (yyvsp[0].interm).arraySizes); + (yyval.interm.type) = (yyvsp[-2].interm.type); + (yyval.interm.type).qualifier.precision = parseContext.getDefaultPrecision((yyval.interm.type)); + (yyval.interm.type).typeParameters = (yyvsp[-1].interm.typeParameters); + (yyval.interm.type).arraySizes = (yyvsp[0].interm).arraySizes; + } +#line 6603 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 197: /* array_specifier: LEFT_BRACKET RIGHT_BRACKET */ +#line 1616 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[-1].lex).loc; + (yyval.interm).arraySizes = new TArraySizes; + (yyval.interm).arraySizes->addInnerSize(); + } +#line 6613 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 198: /* array_specifier: LEFT_BRACKET conditional_expression RIGHT_BRACKET */ +#line 1621 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm).loc = (yyvsp[-2].lex).loc; + (yyval.interm).arraySizes = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[-1].interm.intermTypedNode)->getLoc(), (yyvsp[-1].interm.intermTypedNode), size, "array size"); + (yyval.interm).arraySizes->addInnerSize(size); + } +#line 6626 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 199: /* array_specifier: array_specifier LEFT_BRACKET RIGHT_BRACKET */ +#line 1629 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-2].interm); + (yyval.interm).arraySizes->addInnerSize(); + } +#line 6635 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 200: /* array_specifier: array_specifier LEFT_BRACKET conditional_expression RIGHT_BRACKET */ +#line 1633 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm) = (yyvsp[-3].interm); + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[-1].interm.intermTypedNode)->getLoc(), (yyvsp[-1].interm.intermTypedNode), size, "array size"); + (yyval.interm).arraySizes->addInnerSize(size); + } +#line 6647 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 201: /* type_parameter_specifier_opt: type_parameter_specifier */ +#line 1643 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[0].interm.typeParameters); + } +#line 6655 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 202: /* type_parameter_specifier_opt: %empty */ +#line 1646 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = 0; + } +#line 6663 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 203: /* type_parameter_specifier: LEFT_ANGLE type_parameter_specifier_list RIGHT_ANGLE */ +#line 1652 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[-1].interm.typeParameters); + } +#line 6671 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 204: /* type_parameter_specifier_list: unary_expression */ +#line 1658 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = new TArraySizes; + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode), size, "type parameter"); + (yyval.interm.typeParameters)->addInnerSize(size); + } +#line 6683 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 205: /* type_parameter_specifier_list: type_parameter_specifier_list COMMA unary_expression */ +#line 1665 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeParameters) = (yyvsp[-2].interm.typeParameters); + + TArraySize size; + parseContext.arraySizeCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode), size, "type parameter"); + (yyval.interm.typeParameters)->addInnerSize(size); + } +#line 6695 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 206: /* type_specifier_nonarray: VOID */ +#line 1675 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtVoid; + } +#line 6704 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 207: /* type_specifier_nonarray: FLOAT */ +#line 1679 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + } +#line 6713 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 208: /* type_specifier_nonarray: INT */ +#line 1683 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + } +#line 6722 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 209: /* type_specifier_nonarray: UINT */ +#line 1687 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + } +#line 6732 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 210: /* type_specifier_nonarray: BOOL */ +#line 1692 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + } +#line 6741 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 211: /* type_specifier_nonarray: VEC2 */ +#line 1696 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(2); + } +#line 6751 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 212: /* type_specifier_nonarray: VEC3 */ +#line 1701 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(3); + } +#line 6761 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 213: /* type_specifier_nonarray: VEC4 */ +#line 1706 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(4); + } +#line 6771 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 214: /* type_specifier_nonarray: BVEC2 */ +#line 1711 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(2); + } +#line 6781 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 215: /* type_specifier_nonarray: BVEC3 */ +#line 1716 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(3); + } +#line 6791 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 216: /* type_specifier_nonarray: BVEC4 */ +#line 1721 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtBool; + (yyval.interm.type).setVector(4); + } +#line 6801 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 217: /* type_specifier_nonarray: IVEC2 */ +#line 1726 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(2); + } +#line 6811 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 218: /* type_specifier_nonarray: IVEC3 */ +#line 1731 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(3); + } +#line 6821 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 219: /* type_specifier_nonarray: IVEC4 */ +#line 1736 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(4); + } +#line 6831 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 220: /* type_specifier_nonarray: UVEC2 */ +#line 1741 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(2); + } +#line 6842 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 221: /* type_specifier_nonarray: UVEC3 */ +#line 1747 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(3); + } +#line 6853 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 222: /* type_specifier_nonarray: UVEC4 */ +#line 1753 "glslang/MachineIndependent/glslang.y" + { + parseContext.fullIntegerCheck((yyvsp[0].lex).loc, "unsigned integer vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(4); + } +#line 6864 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 223: /* type_specifier_nonarray: MAT2 */ +#line 1759 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 6874 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 224: /* type_specifier_nonarray: MAT3 */ +#line 1764 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 6884 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 225: /* type_specifier_nonarray: MAT4 */ +#line 1769 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 6894 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 226: /* type_specifier_nonarray: MAT2X2 */ +#line 1774 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 6904 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 227: /* type_specifier_nonarray: MAT2X3 */ +#line 1779 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 3); + } +#line 6914 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 228: /* type_specifier_nonarray: MAT2X4 */ +#line 1784 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 4); + } +#line 6924 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 229: /* type_specifier_nonarray: MAT3X2 */ +#line 1789 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 2); + } +#line 6934 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 230: /* type_specifier_nonarray: MAT3X3 */ +#line 1794 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 6944 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 231: /* type_specifier_nonarray: MAT3X4 */ +#line 1799 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 4); + } +#line 6954 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 232: /* type_specifier_nonarray: MAT4X2 */ +#line 1804 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 2); + } +#line 6964 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 233: /* type_specifier_nonarray: MAT4X3 */ +#line 1809 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 3); + } +#line 6974 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 234: /* type_specifier_nonarray: MAT4X4 */ +#line 1814 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 6984 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 235: /* type_specifier_nonarray: DOUBLE */ +#line 1820 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + } +#line 6996 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 236: /* type_specifier_nonarray: FLOAT16_T */ +#line 1827 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "float16_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + } +#line 7006 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 237: /* type_specifier_nonarray: FLOAT32_T */ +#line 1832 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + } +#line 7016 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 238: /* type_specifier_nonarray: FLOAT64_T */ +#line 1837 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + } +#line 7026 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 239: /* type_specifier_nonarray: INT8_T */ +#line 1842 "glslang/MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + } +#line 7036 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 240: /* type_specifier_nonarray: UINT8_T */ +#line 1847 "glslang/MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + } +#line 7046 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 241: /* type_specifier_nonarray: INT16_T */ +#line 1852 "glslang/MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + } +#line 7056 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 242: /* type_specifier_nonarray: UINT16_T */ +#line 1857 "glslang/MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + } +#line 7066 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 243: /* type_specifier_nonarray: INT32_T */ +#line 1862 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + } +#line 7076 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 244: /* type_specifier_nonarray: UINT32_T */ +#line 1867 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + } +#line 7086 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 245: /* type_specifier_nonarray: INT64_T */ +#line 1872 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + } +#line 7096 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 246: /* type_specifier_nonarray: UINT64_T */ +#line 1877 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + } +#line 7106 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 247: /* type_specifier_nonarray: DVEC2 */ +#line 1882 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(2); + } +#line 7119 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 248: /* type_specifier_nonarray: DVEC3 */ +#line 1890 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(3); + } +#line 7132 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 249: /* type_specifier_nonarray: DVEC4 */ +#line 1898 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double vector"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double vector"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(4); + } +#line 7145 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 250: /* type_specifier_nonarray: F16VEC2 */ +#line 1906 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(2); + } +#line 7156 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 251: /* type_specifier_nonarray: F16VEC3 */ +#line 1912 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(3); + } +#line 7167 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 252: /* type_specifier_nonarray: F16VEC4 */ +#line 1918 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16ScalarVectorCheck((yyvsp[0].lex).loc, "half float vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setVector(4); + } +#line 7178 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 253: /* type_specifier_nonarray: F32VEC2 */ +#line 1924 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(2); + } +#line 7189 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 254: /* type_specifier_nonarray: F32VEC3 */ +#line 1930 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(3); + } +#line 7200 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 255: /* type_specifier_nonarray: F32VEC4 */ +#line 1936 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setVector(4); + } +#line 7211 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 256: /* type_specifier_nonarray: F64VEC2 */ +#line 1942 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(2); + } +#line 7222 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 257: /* type_specifier_nonarray: F64VEC3 */ +#line 1948 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(3); + } +#line 7233 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 258: /* type_specifier_nonarray: F64VEC4 */ +#line 1954 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setVector(4); + } +#line 7244 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 259: /* type_specifier_nonarray: I8VEC2 */ +#line 1960 "glslang/MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(2); + } +#line 7255 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 260: /* type_specifier_nonarray: I8VEC3 */ +#line 1966 "glslang/MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(3); + } +#line 7266 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 261: /* type_specifier_nonarray: I8VEC4 */ +#line 1972 "glslang/MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt8; + (yyval.interm.type).setVector(4); + } +#line 7277 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 262: /* type_specifier_nonarray: I16VEC2 */ +#line 1978 "glslang/MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(2); + } +#line 7288 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 263: /* type_specifier_nonarray: I16VEC3 */ +#line 1984 "glslang/MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(3); + } +#line 7299 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 264: /* type_specifier_nonarray: I16VEC4 */ +#line 1990 "glslang/MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt16; + (yyval.interm.type).setVector(4); + } +#line 7310 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 265: /* type_specifier_nonarray: I32VEC2 */ +#line 1996 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(2); + } +#line 7321 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 266: /* type_specifier_nonarray: I32VEC3 */ +#line 2002 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(3); + } +#line 7332 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 267: /* type_specifier_nonarray: I32VEC4 */ +#line 2008 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit signed integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).setVector(4); + } +#line 7343 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 268: /* type_specifier_nonarray: I64VEC2 */ +#line 2014 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(2); + } +#line 7354 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 269: /* type_specifier_nonarray: I64VEC3 */ +#line 2020 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(3); + } +#line 7365 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 270: /* type_specifier_nonarray: I64VEC4 */ +#line 2026 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt64; + (yyval.interm.type).setVector(4); + } +#line 7376 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 271: /* type_specifier_nonarray: U8VEC2 */ +#line 2032 "glslang/MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(2); + } +#line 7387 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 272: /* type_specifier_nonarray: U8VEC3 */ +#line 2038 "glslang/MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(3); + } +#line 7398 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 273: /* type_specifier_nonarray: U8VEC4 */ +#line 2044 "glslang/MachineIndependent/glslang.y" + { + parseContext.int8ScalarVectorCheck((yyvsp[0].lex).loc, "8-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint8; + (yyval.interm.type).setVector(4); + } +#line 7409 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 274: /* type_specifier_nonarray: U16VEC2 */ +#line 2050 "glslang/MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(2); + } +#line 7420 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 275: /* type_specifier_nonarray: U16VEC3 */ +#line 2056 "glslang/MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(3); + } +#line 7431 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 276: /* type_specifier_nonarray: U16VEC4 */ +#line 2062 "glslang/MachineIndependent/glslang.y" + { + parseContext.int16ScalarVectorCheck((yyvsp[0].lex).loc, "16-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint16; + (yyval.interm.type).setVector(4); + } +#line 7442 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 277: /* type_specifier_nonarray: U32VEC2 */ +#line 2068 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(2); + } +#line 7453 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 278: /* type_specifier_nonarray: U32VEC3 */ +#line 2074 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(3); + } +#line 7464 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 279: /* type_specifier_nonarray: U32VEC4 */ +#line 2080 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitInt32Check((yyvsp[0].lex).loc, "32-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).setVector(4); + } +#line 7475 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 280: /* type_specifier_nonarray: U64VEC2 */ +#line 2086 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(2); + } +#line 7486 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 281: /* type_specifier_nonarray: U64VEC3 */ +#line 2092 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(3); + } +#line 7497 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 282: /* type_specifier_nonarray: U64VEC4 */ +#line 2098 "glslang/MachineIndependent/glslang.y" + { + parseContext.int64Check((yyvsp[0].lex).loc, "64-bit unsigned integer vector", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint64; + (yyval.interm.type).setVector(4); + } +#line 7508 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 283: /* type_specifier_nonarray: DMAT2 */ +#line 2104 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7521 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 284: /* type_specifier_nonarray: DMAT3 */ +#line 2112 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7534 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 285: /* type_specifier_nonarray: DMAT4 */ +#line 2120 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7547 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 286: /* type_specifier_nonarray: DMAT2X2 */ +#line 2128 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7560 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 287: /* type_specifier_nonarray: DMAT2X3 */ +#line 2136 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7573 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 288: /* type_specifier_nonarray: DMAT2X4 */ +#line 2144 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7586 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 289: /* type_specifier_nonarray: DMAT3X2 */ +#line 2152 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 2); + } +#line 7599 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 290: /* type_specifier_nonarray: DMAT3X3 */ +#line 2160 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7612 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 291: /* type_specifier_nonarray: DMAT3X4 */ +#line 2168 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 4); + } +#line 7625 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 292: /* type_specifier_nonarray: DMAT4X2 */ +#line 2176 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 2); + } +#line 7638 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 293: /* type_specifier_nonarray: DMAT4X3 */ +#line 2184 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 3); + } +#line 7651 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 294: /* type_specifier_nonarray: DMAT4X4 */ +#line 2192 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ECoreProfile | ECompatibilityProfile, "double matrix"); + if (! parseContext.symbolTable.atBuiltInLevel()) + parseContext.doubleCheck((yyvsp[0].lex).loc, "double matrix"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7664 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 295: /* type_specifier_nonarray: F16MAT2 */ +#line 2200 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7675 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 296: /* type_specifier_nonarray: F16MAT3 */ +#line 2206 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7686 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 297: /* type_specifier_nonarray: F16MAT4 */ +#line 2212 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7697 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 298: /* type_specifier_nonarray: F16MAT2X2 */ +#line 2218 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7708 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 299: /* type_specifier_nonarray: F16MAT2X3 */ +#line 2224 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7719 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 300: /* type_specifier_nonarray: F16MAT2X4 */ +#line 2230 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7730 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 301: /* type_specifier_nonarray: F16MAT3X2 */ +#line 2236 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 2); + } +#line 7741 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 302: /* type_specifier_nonarray: F16MAT3X3 */ +#line 2242 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7752 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 303: /* type_specifier_nonarray: F16MAT3X4 */ +#line 2248 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(3, 4); + } +#line 7763 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 304: /* type_specifier_nonarray: F16MAT4X2 */ +#line 2254 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 2); + } +#line 7774 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 305: /* type_specifier_nonarray: F16MAT4X3 */ +#line 2260 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 3); + } +#line 7785 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 306: /* type_specifier_nonarray: F16MAT4X4 */ +#line 2266 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16Check((yyvsp[0].lex).loc, "half float matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat16; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7796 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 307: /* type_specifier_nonarray: F32MAT2 */ +#line 2272 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7807 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 308: /* type_specifier_nonarray: F32MAT3 */ +#line 2278 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7818 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 309: /* type_specifier_nonarray: F32MAT4 */ +#line 2284 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7829 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 310: /* type_specifier_nonarray: F32MAT2X2 */ +#line 2290 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7840 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 311: /* type_specifier_nonarray: F32MAT2X3 */ +#line 2296 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7851 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 312: /* type_specifier_nonarray: F32MAT2X4 */ +#line 2302 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7862 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 313: /* type_specifier_nonarray: F32MAT3X2 */ +#line 2308 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 2); + } +#line 7873 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 314: /* type_specifier_nonarray: F32MAT3X3 */ +#line 2314 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7884 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 315: /* type_specifier_nonarray: F32MAT3X4 */ +#line 2320 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(3, 4); + } +#line 7895 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 316: /* type_specifier_nonarray: F32MAT4X2 */ +#line 2326 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 2); + } +#line 7906 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 317: /* type_specifier_nonarray: F32MAT4X3 */ +#line 2332 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 3); + } +#line 7917 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 318: /* type_specifier_nonarray: F32MAT4X4 */ +#line 2338 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat32Check((yyvsp[0].lex).loc, "float32_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7928 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 319: /* type_specifier_nonarray: F64MAT2 */ +#line 2344 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7939 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 320: /* type_specifier_nonarray: F64MAT3 */ +#line 2350 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 7950 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 321: /* type_specifier_nonarray: F64MAT4 */ +#line 2356 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 7961 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 322: /* type_specifier_nonarray: F64MAT2X2 */ +#line 2362 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 2); + } +#line 7972 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 323: /* type_specifier_nonarray: F64MAT2X3 */ +#line 2368 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 3); + } +#line 7983 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 324: /* type_specifier_nonarray: F64MAT2X4 */ +#line 2374 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(2, 4); + } +#line 7994 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 325: /* type_specifier_nonarray: F64MAT3X2 */ +#line 2380 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 2); + } +#line 8005 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 326: /* type_specifier_nonarray: F64MAT3X3 */ +#line 2386 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 3); + } +#line 8016 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 327: /* type_specifier_nonarray: F64MAT3X4 */ +#line 2392 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(3, 4); + } +#line 8027 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 328: /* type_specifier_nonarray: F64MAT4X2 */ +#line 2398 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 2); + } +#line 8038 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 329: /* type_specifier_nonarray: F64MAT4X3 */ +#line 2404 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 3); + } +#line 8049 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 330: /* type_specifier_nonarray: F64MAT4X4 */ +#line 2410 "glslang/MachineIndependent/glslang.y" + { + parseContext.explicitFloat64Check((yyvsp[0].lex).loc, "float64_t matrix", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtDouble; + (yyval.interm.type).setMatrix(4, 4); + } +#line 8060 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 331: /* type_specifier_nonarray: ACCSTRUCTNV */ +#line 2416 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAccStruct; + } +#line 8069 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 332: /* type_specifier_nonarray: ACCSTRUCTEXT */ +#line 2420 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAccStruct; + } +#line 8078 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 333: /* type_specifier_nonarray: RAYQUERYEXT */ +#line 2424 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtRayQuery; + } +#line 8087 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 334: /* type_specifier_nonarray: ATOMIC_UINT */ +#line 2428 "glslang/MachineIndependent/glslang.y" + { + parseContext.vulkanRemoved((yyvsp[0].lex).loc, "atomic counter types"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtAtomicUint; + } +#line 8097 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 335: /* type_specifier_nonarray: SAMPLER1D */ +#line 2433 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D); + } +#line 8107 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 336: /* type_specifier_nonarray: SAMPLER2D */ +#line 2439 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + } +#line 8117 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 337: /* type_specifier_nonarray: SAMPLER3D */ +#line 2444 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd3D); + } +#line 8127 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 338: /* type_specifier_nonarray: SAMPLERCUBE */ +#line 2449 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube); + } +#line 8137 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 339: /* type_specifier_nonarray: SAMPLER2DSHADOW */ +#line 2454 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, false, true); + } +#line 8147 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 340: /* type_specifier_nonarray: SAMPLERCUBESHADOW */ +#line 2459 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, false, true); + } +#line 8157 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 341: /* type_specifier_nonarray: SAMPLER2DARRAY */ +#line 2464 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true); + } +#line 8167 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 342: /* type_specifier_nonarray: SAMPLER2DARRAYSHADOW */ +#line 2469 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true, true); + } +#line 8177 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 343: /* type_specifier_nonarray: SAMPLER1DSHADOW */ +#line 2475 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, false, true); + } +#line 8187 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 344: /* type_specifier_nonarray: SAMPLER1DARRAY */ +#line 2480 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, true); + } +#line 8197 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 345: /* type_specifier_nonarray: SAMPLER1DARRAYSHADOW */ +#line 2485 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd1D, true, true); + } +#line 8207 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 346: /* type_specifier_nonarray: SAMPLERCUBEARRAY */ +#line 2490 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, true); + } +#line 8217 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 347: /* type_specifier_nonarray: SAMPLERCUBEARRAYSHADOW */ +#line 2495 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdCube, true, true); + } +#line 8227 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 348: /* type_specifier_nonarray: F16SAMPLER1D */ +#line 2500 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D); + } +#line 8238 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 349: /* type_specifier_nonarray: F16SAMPLER2D */ +#line 2506 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D); + } +#line 8249 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 350: /* type_specifier_nonarray: F16SAMPLER3D */ +#line 2512 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd3D); + } +#line 8260 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 351: /* type_specifier_nonarray: F16SAMPLERCUBE */ +#line 2518 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube); + } +#line 8271 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 352: /* type_specifier_nonarray: F16SAMPLER1DSHADOW */ +#line 2524 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, false, true); + } +#line 8282 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 353: /* type_specifier_nonarray: F16SAMPLER2DSHADOW */ +#line 2530 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, false, true); + } +#line 8293 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 354: /* type_specifier_nonarray: F16SAMPLERCUBESHADOW */ +#line 2536 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, false, true); + } +#line 8304 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 355: /* type_specifier_nonarray: F16SAMPLER1DARRAY */ +#line 2542 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, true); + } +#line 8315 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 356: /* type_specifier_nonarray: F16SAMPLER2DARRAY */ +#line 2548 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true); + } +#line 8326 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 357: /* type_specifier_nonarray: F16SAMPLER1DARRAYSHADOW */ +#line 2554 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd1D, true, true); + } +#line 8337 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 358: /* type_specifier_nonarray: F16SAMPLER2DARRAYSHADOW */ +#line 2560 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true, true); + } +#line 8348 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 359: /* type_specifier_nonarray: F16SAMPLERCUBEARRAY */ +#line 2566 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, true); + } +#line 8359 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 360: /* type_specifier_nonarray: F16SAMPLERCUBEARRAYSHADOW */ +#line 2572 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdCube, true, true); + } +#line 8370 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 361: /* type_specifier_nonarray: ISAMPLER1D */ +#line 2578 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd1D); + } +#line 8380 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 362: /* type_specifier_nonarray: ISAMPLER2D */ +#line 2584 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D); + } +#line 8390 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 363: /* type_specifier_nonarray: ISAMPLER3D */ +#line 2589 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd3D); + } +#line 8400 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 364: /* type_specifier_nonarray: ISAMPLERCUBE */ +#line 2594 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdCube); + } +#line 8410 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 365: /* type_specifier_nonarray: ISAMPLER2DARRAY */ +#line 2599 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, true); + } +#line 8420 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 366: /* type_specifier_nonarray: USAMPLER2D */ +#line 2604 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D); + } +#line 8430 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 367: /* type_specifier_nonarray: USAMPLER3D */ +#line 2609 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd3D); + } +#line 8440 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 368: /* type_specifier_nonarray: USAMPLERCUBE */ +#line 2614 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdCube); + } +#line 8450 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 369: /* type_specifier_nonarray: ISAMPLER1DARRAY */ +#line 2620 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd1D, true); + } +#line 8460 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 370: /* type_specifier_nonarray: ISAMPLERCUBEARRAY */ +#line 2625 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdCube, true); + } +#line 8470 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 371: /* type_specifier_nonarray: USAMPLER1D */ +#line 2630 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd1D); + } +#line 8480 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 372: /* type_specifier_nonarray: USAMPLER1DARRAY */ +#line 2635 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd1D, true); + } +#line 8490 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 373: /* type_specifier_nonarray: USAMPLERCUBEARRAY */ +#line 2640 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdCube, true); + } +#line 8500 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 374: /* type_specifier_nonarray: TEXTURECUBEARRAY */ +#line 2645 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdCube, true); + } +#line 8510 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 375: /* type_specifier_nonarray: ITEXTURECUBEARRAY */ +#line 2650 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdCube, true); + } +#line 8520 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 376: /* type_specifier_nonarray: UTEXTURECUBEARRAY */ +#line 2655 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdCube, true); + } +#line 8530 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 377: /* type_specifier_nonarray: USAMPLER2DARRAY */ +#line 2661 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, true); + } +#line 8540 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 378: /* type_specifier_nonarray: TEXTURE2D */ +#line 2666 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D); + } +#line 8550 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 379: /* type_specifier_nonarray: TEXTURE3D */ +#line 2671 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd3D); + } +#line 8560 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 380: /* type_specifier_nonarray: TEXTURE2DARRAY */ +#line 2676 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, true); + } +#line 8570 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 381: /* type_specifier_nonarray: TEXTURECUBE */ +#line 2681 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdCube); + } +#line 8580 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 382: /* type_specifier_nonarray: ITEXTURE2D */ +#line 2686 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D); + } +#line 8590 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 383: /* type_specifier_nonarray: ITEXTURE3D */ +#line 2691 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd3D); + } +#line 8600 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 384: /* type_specifier_nonarray: ITEXTURECUBE */ +#line 2696 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdCube); + } +#line 8610 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 385: /* type_specifier_nonarray: ITEXTURE2DARRAY */ +#line 2701 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, true); + } +#line 8620 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 386: /* type_specifier_nonarray: UTEXTURE2D */ +#line 2706 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D); + } +#line 8630 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 387: /* type_specifier_nonarray: UTEXTURE3D */ +#line 2711 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd3D); + } +#line 8640 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 388: /* type_specifier_nonarray: UTEXTURECUBE */ +#line 2716 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdCube); + } +#line 8650 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 389: /* type_specifier_nonarray: UTEXTURE2DARRAY */ +#line 2721 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, true); + } +#line 8660 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 390: /* type_specifier_nonarray: SAMPLER */ +#line 2726 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setPureSampler(false); + } +#line 8670 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 391: /* type_specifier_nonarray: SAMPLERSHADOW */ +#line 2731 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setPureSampler(true); + } +#line 8680 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 392: /* type_specifier_nonarray: SAMPLER2DRECT */ +#line 2737 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdRect); + } +#line 8690 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 393: /* type_specifier_nonarray: SAMPLER2DRECTSHADOW */ +#line 2742 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdRect, false, true); + } +#line 8700 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 394: /* type_specifier_nonarray: F16SAMPLER2DRECT */ +#line 2747 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdRect); + } +#line 8711 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 395: /* type_specifier_nonarray: F16SAMPLER2DRECTSHADOW */ +#line 2753 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdRect, false, true); + } +#line 8722 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 396: /* type_specifier_nonarray: ISAMPLER2DRECT */ +#line 2759 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdRect); + } +#line 8732 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 397: /* type_specifier_nonarray: USAMPLER2DRECT */ +#line 2764 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdRect); + } +#line 8742 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 398: /* type_specifier_nonarray: SAMPLERBUFFER */ +#line 2769 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, EsdBuffer); + } +#line 8752 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 399: /* type_specifier_nonarray: F16SAMPLERBUFFER */ +#line 2774 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, EsdBuffer); + } +#line 8763 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 400: /* type_specifier_nonarray: ISAMPLERBUFFER */ +#line 2780 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, EsdBuffer); + } +#line 8773 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 401: /* type_specifier_nonarray: USAMPLERBUFFER */ +#line 2785 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, EsdBuffer); + } +#line 8783 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 402: /* type_specifier_nonarray: SAMPLER2DMS */ +#line 2790 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, false, false, true); + } +#line 8793 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 403: /* type_specifier_nonarray: F16SAMPLER2DMS */ +#line 2795 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, false, false, true); + } +#line 8804 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 404: /* type_specifier_nonarray: ISAMPLER2DMS */ +#line 2801 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, false, false, true); + } +#line 8814 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 405: /* type_specifier_nonarray: USAMPLER2DMS */ +#line 2806 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, false, false, true); + } +#line 8824 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 406: /* type_specifier_nonarray: SAMPLER2DMSARRAY */ +#line 2811 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D, true, false, true); + } +#line 8834 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 407: /* type_specifier_nonarray: F16SAMPLER2DMSARRAY */ +#line 2816 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float sampler", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat16, Esd2D, true, false, true); + } +#line 8845 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 408: /* type_specifier_nonarray: ISAMPLER2DMSARRAY */ +#line 2822 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtInt, Esd2D, true, false, true); + } +#line 8855 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 409: /* type_specifier_nonarray: USAMPLER2DMSARRAY */ +#line 2827 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtUint, Esd2D, true, false, true); + } +#line 8865 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 410: /* type_specifier_nonarray: TEXTURE1D */ +#line 2832 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd1D); + } +#line 8875 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 411: /* type_specifier_nonarray: F16TEXTURE1D */ +#line 2837 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd1D); + } +#line 8886 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 412: /* type_specifier_nonarray: F16TEXTURE2D */ +#line 2843 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D); + } +#line 8897 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 413: /* type_specifier_nonarray: F16TEXTURE3D */ +#line 2849 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd3D); + } +#line 8908 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 414: /* type_specifier_nonarray: F16TEXTURECUBE */ +#line 2855 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdCube); + } +#line 8919 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 415: /* type_specifier_nonarray: TEXTURE1DARRAY */ +#line 2861 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd1D, true); + } +#line 8929 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 416: /* type_specifier_nonarray: F16TEXTURE1DARRAY */ +#line 2866 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd1D, true); + } +#line 8940 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 417: /* type_specifier_nonarray: F16TEXTURE2DARRAY */ +#line 2872 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, true); + } +#line 8951 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 418: /* type_specifier_nonarray: F16TEXTURECUBEARRAY */ +#line 2878 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdCube, true); + } +#line 8962 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 419: /* type_specifier_nonarray: ITEXTURE1D */ +#line 2884 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd1D); + } +#line 8972 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 420: /* type_specifier_nonarray: ITEXTURE1DARRAY */ +#line 2889 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd1D, true); + } +#line 8982 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 421: /* type_specifier_nonarray: UTEXTURE1D */ +#line 2894 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd1D); + } +#line 8992 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 422: /* type_specifier_nonarray: UTEXTURE1DARRAY */ +#line 2899 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd1D, true); + } +#line 9002 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 423: /* type_specifier_nonarray: TEXTURE2DRECT */ +#line 2904 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdRect); + } +#line 9012 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 424: /* type_specifier_nonarray: F16TEXTURE2DRECT */ +#line 2909 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdRect); + } +#line 9023 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 425: /* type_specifier_nonarray: ITEXTURE2DRECT */ +#line 2915 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdRect); + } +#line 9033 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 426: /* type_specifier_nonarray: UTEXTURE2DRECT */ +#line 2920 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdRect); + } +#line 9043 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 427: /* type_specifier_nonarray: TEXTUREBUFFER */ +#line 2925 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, EsdBuffer); + } +#line 9053 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 428: /* type_specifier_nonarray: F16TEXTUREBUFFER */ +#line 2930 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, EsdBuffer); + } +#line 9064 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 429: /* type_specifier_nonarray: ITEXTUREBUFFER */ +#line 2936 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, EsdBuffer); + } +#line 9074 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 430: /* type_specifier_nonarray: UTEXTUREBUFFER */ +#line 2941 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, EsdBuffer); + } +#line 9084 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 431: /* type_specifier_nonarray: TEXTURE2DMS */ +#line 2946 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, false, false, true); + } +#line 9094 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 432: /* type_specifier_nonarray: F16TEXTURE2DMS */ +#line 2951 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, false, false, true); + } +#line 9105 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 433: /* type_specifier_nonarray: ITEXTURE2DMS */ +#line 2957 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, false, false, true); + } +#line 9115 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 434: /* type_specifier_nonarray: UTEXTURE2DMS */ +#line 2962 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, false, false, true); + } +#line 9125 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 435: /* type_specifier_nonarray: TEXTURE2DMSARRAY */ +#line 2967 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat, Esd2D, true, false, true); + } +#line 9135 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 436: /* type_specifier_nonarray: F16TEXTURE2DMSARRAY */ +#line 2972 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float texture", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtFloat16, Esd2D, true, false, true); + } +#line 9146 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 437: /* type_specifier_nonarray: ITEXTURE2DMSARRAY */ +#line 2978 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtInt, Esd2D, true, false, true); + } +#line 9156 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 438: /* type_specifier_nonarray: UTEXTURE2DMSARRAY */ +#line 2983 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setTexture(EbtUint, Esd2D, true, false, true); + } +#line 9166 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 439: /* type_specifier_nonarray: IMAGE1D */ +#line 2988 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd1D); + } +#line 9176 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 440: /* type_specifier_nonarray: F16IMAGE1D */ +#line 2993 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd1D); + } +#line 9187 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 441: /* type_specifier_nonarray: IIMAGE1D */ +#line 2999 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd1D); + } +#line 9197 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 442: /* type_specifier_nonarray: UIMAGE1D */ +#line 3004 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd1D); + } +#line 9207 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 443: /* type_specifier_nonarray: IMAGE2D */ +#line 3009 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D); + } +#line 9217 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 444: /* type_specifier_nonarray: F16IMAGE2D */ +#line 3014 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D); + } +#line 9228 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 445: /* type_specifier_nonarray: IIMAGE2D */ +#line 3020 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D); + } +#line 9238 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 446: /* type_specifier_nonarray: UIMAGE2D */ +#line 3025 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D); + } +#line 9248 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 447: /* type_specifier_nonarray: IMAGE3D */ +#line 3030 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd3D); + } +#line 9258 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 448: /* type_specifier_nonarray: F16IMAGE3D */ +#line 3035 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd3D); + } +#line 9269 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 449: /* type_specifier_nonarray: IIMAGE3D */ +#line 3041 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd3D); + } +#line 9279 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 450: /* type_specifier_nonarray: UIMAGE3D */ +#line 3046 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd3D); + } +#line 9289 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 451: /* type_specifier_nonarray: IMAGE2DRECT */ +#line 3051 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdRect); + } +#line 9299 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 452: /* type_specifier_nonarray: F16IMAGE2DRECT */ +#line 3056 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdRect); + } +#line 9310 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 453: /* type_specifier_nonarray: IIMAGE2DRECT */ +#line 3062 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdRect); + } +#line 9320 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 454: /* type_specifier_nonarray: UIMAGE2DRECT */ +#line 3067 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdRect); + } +#line 9330 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 455: /* type_specifier_nonarray: IMAGECUBE */ +#line 3072 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdCube); + } +#line 9340 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 456: /* type_specifier_nonarray: F16IMAGECUBE */ +#line 3077 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdCube); + } +#line 9351 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 457: /* type_specifier_nonarray: IIMAGECUBE */ +#line 3083 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdCube); + } +#line 9361 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 458: /* type_specifier_nonarray: UIMAGECUBE */ +#line 3088 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdCube); + } +#line 9371 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 459: /* type_specifier_nonarray: IMAGEBUFFER */ +#line 3093 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdBuffer); + } +#line 9381 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 460: /* type_specifier_nonarray: F16IMAGEBUFFER */ +#line 3098 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdBuffer); + } +#line 9392 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 461: /* type_specifier_nonarray: IIMAGEBUFFER */ +#line 3104 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdBuffer); + } +#line 9402 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 462: /* type_specifier_nonarray: UIMAGEBUFFER */ +#line 3109 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdBuffer); + } +#line 9412 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 463: /* type_specifier_nonarray: IMAGE1DARRAY */ +#line 3114 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd1D, true); + } +#line 9422 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 464: /* type_specifier_nonarray: F16IMAGE1DARRAY */ +#line 3119 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd1D, true); + } +#line 9433 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 465: /* type_specifier_nonarray: IIMAGE1DARRAY */ +#line 3125 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd1D, true); + } +#line 9443 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 466: /* type_specifier_nonarray: UIMAGE1DARRAY */ +#line 3130 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd1D, true); + } +#line 9453 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 467: /* type_specifier_nonarray: IMAGE2DARRAY */ +#line 3135 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, true); + } +#line 9463 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 468: /* type_specifier_nonarray: F16IMAGE2DARRAY */ +#line 3140 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, true); + } +#line 9474 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 469: /* type_specifier_nonarray: IIMAGE2DARRAY */ +#line 3146 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, true); + } +#line 9484 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 470: /* type_specifier_nonarray: UIMAGE2DARRAY */ +#line 3151 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, true); + } +#line 9494 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 471: /* type_specifier_nonarray: IMAGECUBEARRAY */ +#line 3156 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, EsdCube, true); + } +#line 9504 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 472: /* type_specifier_nonarray: F16IMAGECUBEARRAY */ +#line 3161 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, EsdCube, true); + } +#line 9515 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 473: /* type_specifier_nonarray: IIMAGECUBEARRAY */ +#line 3167 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, EsdCube, true); + } +#line 9525 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 474: /* type_specifier_nonarray: UIMAGECUBEARRAY */ +#line 3172 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, EsdCube, true); + } +#line 9535 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 475: /* type_specifier_nonarray: IMAGE2DMS */ +#line 3177 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, false, false, true); + } +#line 9545 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 476: /* type_specifier_nonarray: F16IMAGE2DMS */ +#line 3182 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, false, false, true); + } +#line 9556 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 477: /* type_specifier_nonarray: IIMAGE2DMS */ +#line 3188 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, false, false, true); + } +#line 9566 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 478: /* type_specifier_nonarray: UIMAGE2DMS */ +#line 3193 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, false, false, true); + } +#line 9576 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 479: /* type_specifier_nonarray: IMAGE2DMSARRAY */ +#line 3198 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat, Esd2D, true, false, true); + } +#line 9586 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 480: /* type_specifier_nonarray: F16IMAGE2DMSARRAY */ +#line 3203 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float image", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtFloat16, Esd2D, true, false, true); + } +#line 9597 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 481: /* type_specifier_nonarray: IIMAGE2DMSARRAY */ +#line 3209 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt, Esd2D, true, false, true); + } +#line 9607 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 482: /* type_specifier_nonarray: UIMAGE2DMSARRAY */ +#line 3214 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint, Esd2D, true, false, true); + } +#line 9617 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 483: /* type_specifier_nonarray: I64IMAGE1D */ +#line 3219 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd1D); + } +#line 9627 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 484: /* type_specifier_nonarray: U64IMAGE1D */ +#line 3224 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd1D); + } +#line 9637 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 485: /* type_specifier_nonarray: I64IMAGE2D */ +#line 3229 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd2D); + } +#line 9647 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 486: /* type_specifier_nonarray: U64IMAGE2D */ +#line 3234 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd2D); + } +#line 9657 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 487: /* type_specifier_nonarray: I64IMAGE3D */ +#line 3239 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd3D); + } +#line 9667 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 488: /* type_specifier_nonarray: U64IMAGE3D */ +#line 3244 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd3D); + } +#line 9677 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 489: /* type_specifier_nonarray: I64IMAGE2DRECT */ +#line 3249 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, EsdRect); + } +#line 9687 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 490: /* type_specifier_nonarray: U64IMAGE2DRECT */ +#line 3254 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, EsdRect); + } +#line 9697 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 491: /* type_specifier_nonarray: I64IMAGECUBE */ +#line 3259 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, EsdCube); + } +#line 9707 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 492: /* type_specifier_nonarray: U64IMAGECUBE */ +#line 3264 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, EsdCube); + } +#line 9717 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 493: /* type_specifier_nonarray: I64IMAGEBUFFER */ +#line 3269 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, EsdBuffer); + } +#line 9727 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 494: /* type_specifier_nonarray: U64IMAGEBUFFER */ +#line 3274 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, EsdBuffer); + } +#line 9737 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 495: /* type_specifier_nonarray: I64IMAGE1DARRAY */ +#line 3279 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd1D, true); + } +#line 9747 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 496: /* type_specifier_nonarray: U64IMAGE1DARRAY */ +#line 3284 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd1D, true); + } +#line 9757 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 497: /* type_specifier_nonarray: I64IMAGE2DARRAY */ +#line 3289 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd2D, true); + } +#line 9767 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 498: /* type_specifier_nonarray: U64IMAGE2DARRAY */ +#line 3294 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd2D, true); + } +#line 9777 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 499: /* type_specifier_nonarray: I64IMAGECUBEARRAY */ +#line 3299 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, EsdCube, true); + } +#line 9787 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 500: /* type_specifier_nonarray: U64IMAGECUBEARRAY */ +#line 3304 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, EsdCube, true); + } +#line 9797 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 501: /* type_specifier_nonarray: I64IMAGE2DMS */ +#line 3309 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd2D, false, false, true); + } +#line 9807 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 502: /* type_specifier_nonarray: U64IMAGE2DMS */ +#line 3314 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd2D, false, false, true); + } +#line 9817 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 503: /* type_specifier_nonarray: I64IMAGE2DMSARRAY */ +#line 3319 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtInt64, Esd2D, true, false, true); + } +#line 9827 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 504: /* type_specifier_nonarray: U64IMAGE2DMSARRAY */ +#line 3324 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setImage(EbtUint64, Esd2D, true, false, true); + } +#line 9837 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 505: /* type_specifier_nonarray: SAMPLEREXTERNALOES */ +#line 3329 "glslang/MachineIndependent/glslang.y" + { // GL_OES_EGL_image_external + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + (yyval.interm.type).sampler.external = true; + } +#line 9848 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 506: /* type_specifier_nonarray: SAMPLEREXTERNAL2DY2YEXT */ +#line 3335 "glslang/MachineIndependent/glslang.y" + { // GL_EXT_YUV_target + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.set(EbtFloat, Esd2D); + (yyval.interm.type).sampler.yuv = true; + } +#line 9859 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 507: /* type_specifier_nonarray: SUBPASSINPUT */ +#line 3341 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat); + } +#line 9870 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 508: /* type_specifier_nonarray: SUBPASSINPUTMS */ +#line 3347 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat, true); + } +#line 9881 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 509: /* type_specifier_nonarray: F16SUBPASSINPUT */ +#line 3353 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat16); + } +#line 9893 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 510: /* type_specifier_nonarray: F16SUBPASSINPUTMS */ +#line 3360 "glslang/MachineIndependent/glslang.y" + { + parseContext.float16OpaqueCheck((yyvsp[0].lex).loc, "half float subpass input", parseContext.symbolTable.atBuiltInLevel()); + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtFloat16, true); + } +#line 9905 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 511: /* type_specifier_nonarray: ISUBPASSINPUT */ +#line 3367 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtInt); + } +#line 9916 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 512: /* type_specifier_nonarray: ISUBPASSINPUTMS */ +#line 3373 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtInt, true); + } +#line 9927 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 513: /* type_specifier_nonarray: USUBPASSINPUT */ +#line 3379 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtUint); + } +#line 9938 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 514: /* type_specifier_nonarray: USUBPASSINPUTMS */ +#line 3385 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[0].lex).loc, EShLangFragment, "subpass input"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtSampler; + (yyval.interm.type).sampler.setSubpass(EbtUint, true); + } +#line 9949 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 515: /* type_specifier_nonarray: FCOOPMATNV */ +#line 3391 "glslang/MachineIndependent/glslang.y" + { + parseContext.fcoopmatCheck((yyvsp[0].lex).loc, "fcoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtFloat; + (yyval.interm.type).coopmat = true; + } +#line 9960 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 516: /* type_specifier_nonarray: ICOOPMATNV */ +#line 3397 "glslang/MachineIndependent/glslang.y" + { + parseContext.intcoopmatCheck((yyvsp[0].lex).loc, "icoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtInt; + (yyval.interm.type).coopmat = true; + } +#line 9971 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 517: /* type_specifier_nonarray: UCOOPMATNV */ +#line 3403 "glslang/MachineIndependent/glslang.y" + { + parseContext.intcoopmatCheck((yyvsp[0].lex).loc, "ucoopmatNV", parseContext.symbolTable.atBuiltInLevel()); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtUint; + (yyval.interm.type).coopmat = true; + } +#line 9982 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 518: /* type_specifier_nonarray: struct_specifier */ +#line 3410 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.type) = (yyvsp[0].interm.type); + (yyval.interm.type).qualifier.storage = parseContext.symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; + parseContext.structTypeCheck((yyval.interm.type).loc, (yyval.interm.type)); + } +#line 9992 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 519: /* type_specifier_nonarray: TYPE_NAME */ +#line 3415 "glslang/MachineIndependent/glslang.y" + { + // + // This is for user defined type names. The lexical phase looked up the + // type. + // + if (const TVariable* variable = ((yyvsp[0].lex).symbol)->getAsVariable()) { + const TType& structure = variable->getType(); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = &structure; + } else + parseContext.error((yyvsp[0].lex).loc, "expected type name", (yyvsp[0].lex).string->c_str(), ""); + } +#line 10010 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 520: /* precision_qualifier: HIGH_PRECISION */ +#line 3431 "glslang/MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "highp precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqHigh); + } +#line 10020 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 521: /* precision_qualifier: MEDIUM_PRECISION */ +#line 3436 "glslang/MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "mediump precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqMedium); + } +#line 10030 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 522: /* precision_qualifier: LOW_PRECISION */ +#line 3441 "glslang/MachineIndependent/glslang.y" + { + parseContext.profileRequires((yyvsp[0].lex).loc, ENoProfile, 130, 0, "lowp precision qualifier"); + (yyval.interm.type).init((yyvsp[0].lex).loc, parseContext.symbolTable.atGlobalLevel()); + parseContext.handlePrecisionQualifier((yyvsp[0].lex).loc, (yyval.interm.type).qualifier, EpqLow); + } +#line 10040 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 523: /* $@3: %empty */ +#line 3449 "glslang/MachineIndependent/glslang.y" + { parseContext.nestedStructCheck((yyvsp[-2].lex).loc); } +#line 10046 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 524: /* struct_specifier: STRUCT IDENTIFIER LEFT_BRACE $@3 struct_declaration_list RIGHT_BRACE */ +#line 3449 "glslang/MachineIndependent/glslang.y" + { + TType* structure = new TType((yyvsp[-1].interm.typeList), *(yyvsp[-4].lex).string); + parseContext.structArrayCheck((yyvsp[-4].lex).loc, *structure); + TVariable* userTypeDef = new TVariable((yyvsp[-4].lex).string, *structure, true); + if (! parseContext.symbolTable.insert(*userTypeDef)) + parseContext.error((yyvsp[-4].lex).loc, "redefinition", (yyvsp[-4].lex).string->c_str(), "struct"); + (yyval.interm.type).init((yyvsp[-5].lex).loc); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = structure; + --parseContext.structNestingLevel; + } +#line 10062 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 525: /* $@4: %empty */ +#line 3460 "glslang/MachineIndependent/glslang.y" + { parseContext.nestedStructCheck((yyvsp[-1].lex).loc); } +#line 10068 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 526: /* struct_specifier: STRUCT LEFT_BRACE $@4 struct_declaration_list RIGHT_BRACE */ +#line 3460 "glslang/MachineIndependent/glslang.y" + { + TType* structure = new TType((yyvsp[-1].interm.typeList), TString("")); + (yyval.interm.type).init((yyvsp[-4].lex).loc); + (yyval.interm.type).basicType = EbtStruct; + (yyval.interm.type).userDef = structure; + --parseContext.structNestingLevel; + } +#line 10080 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 527: /* struct_declaration_list: struct_declaration */ +#line 3470 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = (yyvsp[0].interm.typeList); + } +#line 10088 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 528: /* struct_declaration_list: struct_declaration_list struct_declaration */ +#line 3473 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + for (unsigned int i = 0; i < (yyvsp[0].interm.typeList)->size(); ++i) { + for (unsigned int j = 0; j < (yyval.interm.typeList)->size(); ++j) { + if ((*(yyval.interm.typeList))[j].type->getFieldName() == (*(yyvsp[0].interm.typeList))[i].type->getFieldName()) + parseContext.error((*(yyvsp[0].interm.typeList))[i].loc, "duplicate member name:", "", (*(yyvsp[0].interm.typeList))[i].type->getFieldName().c_str()); + } + (yyval.interm.typeList)->push_back((*(yyvsp[0].interm.typeList))[i]); + } + } +#line 10103 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 529: /* struct_declaration: type_specifier struct_declarator_list SEMICOLON */ +#line 3486 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + + parseContext.voidErrorCheck((yyvsp[-2].interm.type).loc, (*(yyvsp[-1].interm.typeList))[0].type->getFieldName(), (yyvsp[-2].interm.type).basicType); + parseContext.precisionQualifierCheck((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).basicType, (yyvsp[-2].interm.type).qualifier); + + for (unsigned int i = 0; i < (yyval.interm.typeList)->size(); ++i) { + TType type((yyvsp[-2].interm.type)); + type.setFieldName((*(yyval.interm.typeList))[i].type->getFieldName()); + type.transferArraySizes((*(yyval.interm.typeList))[i].type->getArraySizes()); + type.copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + parseContext.arrayOfArrayVersionCheck((*(yyval.interm.typeList))[i].loc, type.getArraySizes()); + (*(yyval.interm.typeList))[i].type->shallowCopy(type); + } + } +#line 10130 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 530: /* struct_declaration: type_qualifier type_specifier struct_declarator_list SEMICOLON */ +#line 3508 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.type).arraySizes) { + parseContext.profileRequires((yyvsp[-2].interm.type).loc, ENoProfile, 120, E_GL_3DL_array_objects, "arrayed type"); + parseContext.profileRequires((yyvsp[-2].interm.type).loc, EEsProfile, 300, 0, "arrayed type"); + if (parseContext.isEsProfile()) + parseContext.arraySizeRequiredCheck((yyvsp[-2].interm.type).loc, *(yyvsp[-2].interm.type).arraySizes); + } + + (yyval.interm.typeList) = (yyvsp[-1].interm.typeList); + + parseContext.memberQualifierCheck((yyvsp[-3].interm.type)); + parseContext.voidErrorCheck((yyvsp[-2].interm.type).loc, (*(yyvsp[-1].interm.typeList))[0].type->getFieldName(), (yyvsp[-2].interm.type).basicType); + parseContext.mergeQualifiers((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).qualifier, (yyvsp[-3].interm.type).qualifier, true); + parseContext.precisionQualifierCheck((yyvsp[-2].interm.type).loc, (yyvsp[-2].interm.type).basicType, (yyvsp[-2].interm.type).qualifier); + + for (unsigned int i = 0; i < (yyval.interm.typeList)->size(); ++i) { + TType type((yyvsp[-2].interm.type)); + type.setFieldName((*(yyval.interm.typeList))[i].type->getFieldName()); + type.transferArraySizes((*(yyval.interm.typeList))[i].type->getArraySizes()); + type.copyArrayInnerSizes((yyvsp[-2].interm.type).arraySizes); + parseContext.arrayOfArrayVersionCheck((*(yyval.interm.typeList))[i].loc, type.getArraySizes()); + (*(yyval.interm.typeList))[i].type->shallowCopy(type); + } + } +#line 10159 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 531: /* struct_declarator_list: struct_declarator */ +#line 3535 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeList) = new TTypeList; + (yyval.interm.typeList)->push_back((yyvsp[0].interm.typeLine)); + } +#line 10168 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 532: /* struct_declarator_list: struct_declarator_list COMMA struct_declarator */ +#line 3539 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeList)->push_back((yyvsp[0].interm.typeLine)); + } +#line 10176 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 533: /* struct_declarator: IDENTIFIER */ +#line 3545 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.typeLine).type = new TType(EbtVoid); + (yyval.interm.typeLine).loc = (yyvsp[0].lex).loc; + (yyval.interm.typeLine).type->setFieldName(*(yyvsp[0].lex).string); + } +#line 10186 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 534: /* struct_declarator: IDENTIFIER array_specifier */ +#line 3550 "glslang/MachineIndependent/glslang.y" + { + parseContext.arrayOfArrayVersionCheck((yyvsp[-1].lex).loc, (yyvsp[0].interm).arraySizes); + + (yyval.interm.typeLine).type = new TType(EbtVoid); + (yyval.interm.typeLine).loc = (yyvsp[-1].lex).loc; + (yyval.interm.typeLine).type->setFieldName(*(yyvsp[-1].lex).string); + (yyval.interm.typeLine).type->transferArraySizes((yyvsp[0].interm).arraySizes); + } +#line 10199 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 535: /* initializer: assignment_expression */ +#line 3561 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 10207 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 536: /* initializer: LEFT_BRACE initializer_list RIGHT_BRACE */ +#line 3565 "glslang/MachineIndependent/glslang.y" + { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile((yyvsp[-2].lex).loc, ~EEsProfile, initFeature); + parseContext.profileRequires((yyvsp[-2].lex).loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + (yyval.interm.intermTypedNode) = (yyvsp[-1].interm.intermTypedNode); + } +#line 10218 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 537: /* initializer: LEFT_BRACE initializer_list COMMA RIGHT_BRACE */ +#line 3571 "glslang/MachineIndependent/glslang.y" + { + const char* initFeature = "{ } style initializers"; + parseContext.requireProfile((yyvsp[-3].lex).loc, ~EEsProfile, initFeature); + parseContext.profileRequires((yyvsp[-3].lex).loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, initFeature); + (yyval.interm.intermTypedNode) = (yyvsp[-2].interm.intermTypedNode); + } +#line 10229 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 538: /* initializer_list: initializer */ +#line 3582 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.growAggregate(0, (yyvsp[0].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)->getLoc()); + } +#line 10237 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 539: /* initializer_list: initializer_list COMMA initializer */ +#line 3585 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.intermTypedNode)); + } +#line 10245 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 540: /* declaration_statement: declaration */ +#line 3592 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10251 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 541: /* statement: compound_statement */ +#line 3596 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10257 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 542: /* statement: simple_statement */ +#line 3597 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10263 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 543: /* simple_statement: declaration_statement */ +#line 3603 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10269 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 544: /* simple_statement: expression_statement */ +#line 3604 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10275 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 545: /* simple_statement: selection_statement */ +#line 3605 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10281 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 546: /* simple_statement: switch_statement */ +#line 3606 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10287 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 547: /* simple_statement: case_label */ +#line 3607 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10293 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 548: /* simple_statement: iteration_statement */ +#line 3608 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10299 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 549: /* simple_statement: jump_statement */ +#line 3609 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10305 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 550: /* simple_statement: demote_statement */ +#line 3611 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10311 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 551: /* demote_statement: DEMOTE SEMICOLON */ +#line 3617 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "demote"); + parseContext.requireExtensions((yyvsp[-1].lex).loc, 1, &E_GL_EXT_demote_to_helper_invocation, "demote"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpDemote, (yyvsp[-1].lex).loc); + } +#line 10321 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 552: /* compound_statement: LEFT_BRACE RIGHT_BRACE */ +#line 3626 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = 0; } +#line 10327 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 553: /* $@5: %empty */ +#line 3627 "glslang/MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } +#line 10336 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 554: /* $@6: %empty */ +#line 3631 "glslang/MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } +#line 10345 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 555: /* compound_statement: LEFT_BRACE $@5 statement_list $@6 RIGHT_BRACE */ +#line 3635 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-2].interm.intermNode) && (yyvsp[-2].interm.intermNode)->getAsAggregate()) + (yyvsp[-2].interm.intermNode)->getAsAggregate()->setOperator(EOpSequence); + (yyval.interm.intermNode) = (yyvsp[-2].interm.intermNode); + } +#line 10355 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 556: /* statement_no_new_scope: compound_statement_no_new_scope */ +#line 3643 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10361 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 557: /* statement_no_new_scope: simple_statement */ +#line 3644 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); } +#line 10367 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 558: /* $@7: %empty */ +#line 3648 "glslang/MachineIndependent/glslang.y" + { + ++parseContext.controlFlowNestingLevel; + } +#line 10375 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 559: /* statement_scoped: $@7 compound_statement */ +#line 3651 "glslang/MachineIndependent/glslang.y" + { + --parseContext.controlFlowNestingLevel; + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10384 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 560: /* $@8: %empty */ +#line 3655 "glslang/MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 10394 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 561: /* statement_scoped: $@8 simple_statement */ +#line 3660 "glslang/MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10405 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 562: /* compound_statement_no_new_scope: LEFT_BRACE RIGHT_BRACE */ +#line 3669 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + } +#line 10413 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 563: /* compound_statement_no_new_scope: LEFT_BRACE statement_list RIGHT_BRACE */ +#line 3672 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[-1].interm.intermNode) && (yyvsp[-1].interm.intermNode)->getAsAggregate()) + (yyvsp[-1].interm.intermNode)->getAsAggregate()->setOperator(EOpSequence); + (yyval.interm.intermNode) = (yyvsp[-1].interm.intermNode); + } +#line 10423 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 564: /* statement_list: statement */ +#line 3680 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[0].interm.intermNode)); + if ((yyvsp[0].interm.intermNode) && (yyvsp[0].interm.intermNode)->getAsBranchNode() && ((yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpCase || + (yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence(0, (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = 0; // start a fresh subsequence for what's after this case + } + } +#line 10436 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 565: /* statement_list: statement_list statement */ +#line 3688 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermNode) && (yyvsp[0].interm.intermNode)->getAsBranchNode() && ((yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpCase || + (yyvsp[0].interm.intermNode)->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence((yyvsp[-1].interm.intermNode) ? (yyvsp[-1].interm.intermNode)->getAsAggregate() : 0, (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = 0; // start a fresh subsequence for what's after this case + } else + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-1].interm.intermNode), (yyvsp[0].interm.intermNode)); + } +#line 10449 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 566: /* expression_statement: SEMICOLON */ +#line 3699 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = 0; } +#line 10455 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 567: /* expression_statement: expression SEMICOLON */ +#line 3700 "glslang/MachineIndependent/glslang.y" + { (yyval.interm.intermNode) = static_cast((yyvsp[-1].interm.intermTypedNode)); } +#line 10461 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 568: /* selection_statement: selection_statement_nonattributed */ +#line 3704 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10469 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 569: /* selection_statement: attribute selection_statement_nonattributed */ +#line 3708 "glslang/MachineIndependent/glslang.y" + { + parseContext.handleSelectionAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10478 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 570: /* selection_statement_nonattributed: IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement */ +#line 3715 "glslang/MachineIndependent/glslang.y" + { + parseContext.boolCheck((yyvsp[-4].lex).loc, (yyvsp[-2].interm.intermTypedNode)); + (yyval.interm.intermNode) = parseContext.intermediate.addSelection((yyvsp[-2].interm.intermTypedNode), (yyvsp[0].interm.nodePair), (yyvsp[-4].lex).loc); + } +#line 10487 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 571: /* selection_rest_statement: statement_scoped ELSE statement_scoped */ +#line 3722 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermNode); + (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermNode); + } +#line 10496 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 572: /* selection_rest_statement: statement_scoped */ +#line 3726 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[0].interm.intermNode); + (yyval.interm.nodePair).node2 = 0; + } +#line 10505 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 573: /* condition: expression */ +#line 3734 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + parseContext.boolCheck((yyvsp[0].interm.intermTypedNode)->getLoc(), (yyvsp[0].interm.intermTypedNode)); + } +#line 10514 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 574: /* condition: fully_specified_type IDENTIFIER EQUAL initializer */ +#line 3738 "glslang/MachineIndependent/glslang.y" + { + parseContext.boolCheck((yyvsp[-2].lex).loc, (yyvsp[-3].interm.type)); + + TType type((yyvsp[-3].interm.type)); + TIntermNode* initNode = parseContext.declareVariable((yyvsp[-2].lex).loc, *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), 0, (yyvsp[0].interm.intermTypedNode)); + if (initNode) + (yyval.interm.intermTypedNode) = initNode->getAsTyped(); + else + (yyval.interm.intermTypedNode) = 0; + } +#line 10529 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 575: /* switch_statement: switch_statement_nonattributed */ +#line 3751 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10537 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 576: /* switch_statement: attribute switch_statement_nonattributed */ +#line 3755 "glslang/MachineIndependent/glslang.y" + { + parseContext.handleSwitchAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10546 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 577: /* $@9: %empty */ +#line 3762 "glslang/MachineIndependent/glslang.y" + { + // start new switch sequence on the switch stack + ++parseContext.controlFlowNestingLevel; + ++parseContext.statementNestingLevel; + parseContext.switchSequenceStack.push_back(new TIntermSequence); + parseContext.switchLevel.push_back(parseContext.statementNestingLevel); + parseContext.symbolTable.push(); + } +#line 10559 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 578: /* switch_statement_nonattributed: SWITCH LEFT_PAREN expression RIGHT_PAREN $@9 LEFT_BRACE switch_statement_list RIGHT_BRACE */ +#line 3770 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.addSwitch((yyvsp[-7].lex).loc, (yyvsp[-5].interm.intermTypedNode), (yyvsp[-1].interm.intermNode) ? (yyvsp[-1].interm.intermNode)->getAsAggregate() : 0); + delete parseContext.switchSequenceStack.back(); + parseContext.switchSequenceStack.pop_back(); + parseContext.switchLevel.pop_back(); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 10573 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 579: /* switch_statement_list: %empty */ +#line 3782 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + } +#line 10581 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 580: /* switch_statement_list: statement_list */ +#line 3785 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10589 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 581: /* case_label: CASE expression COLON */ +#line 3791 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error((yyvsp[-2].lex).loc, "cannot appear outside switch statement", "case", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error((yyvsp[-2].lex).loc, "cannot be nested inside control flow", "case", ""); + else { + parseContext.constantValueCheck((yyvsp[-1].interm.intermTypedNode), "case"); + parseContext.integerCheck((yyvsp[-1].interm.intermTypedNode), "case"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpCase, (yyvsp[-1].interm.intermTypedNode), (yyvsp[-2].lex).loc); + } + } +#line 10606 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 582: /* case_label: DEFAULT COLON */ +#line 3803 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = 0; + if (parseContext.switchLevel.size() == 0) + parseContext.error((yyvsp[-1].lex).loc, "cannot appear outside switch statement", "default", ""); + else if (parseContext.switchLevel.back() != parseContext.statementNestingLevel) + parseContext.error((yyvsp[-1].lex).loc, "cannot be nested inside control flow", "default", ""); + else + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpDefault, (yyvsp[-1].lex).loc); + } +#line 10620 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 583: /* iteration_statement: iteration_statement_nonattributed */ +#line 3815 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10628 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 584: /* iteration_statement: attribute iteration_statement_nonattributed */ +#line 3819 "glslang/MachineIndependent/glslang.y" + { + parseContext.handleLoopAttributes(*(yyvsp[-1].interm.attributes), (yyvsp[0].interm.intermNode)); + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10637 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 585: /* $@10: %empty */ +#line 3826 "glslang/MachineIndependent/glslang.y" + { + if (! parseContext.limits.whileLoops) + parseContext.error((yyvsp[-1].lex).loc, "while loops not available", "limitation", ""); + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 10650 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 586: /* iteration_statement_nonattributed: WHILE LEFT_PAREN $@10 condition RIGHT_PAREN statement_no_new_scope */ +#line 3834 "glslang/MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.addLoop((yyvsp[0].interm.intermNode), (yyvsp[-2].interm.intermTypedNode), 0, true, (yyvsp[-5].lex).loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 10662 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 587: /* $@11: %empty */ +#line 3841 "glslang/MachineIndependent/glslang.y" + { + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 10672 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 588: /* iteration_statement_nonattributed: DO $@11 statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON */ +#line 3846 "glslang/MachineIndependent/glslang.y" + { + if (! parseContext.limits.whileLoops) + parseContext.error((yyvsp[-7].lex).loc, "do-while loops not available", "limitation", ""); + + parseContext.boolCheck((yyvsp[0].lex).loc, (yyvsp[-2].interm.intermTypedNode)); + + (yyval.interm.intermNode) = parseContext.intermediate.addLoop((yyvsp[-5].interm.intermNode), (yyvsp[-2].interm.intermTypedNode), 0, false, (yyvsp[-4].lex).loc); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 10688 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 589: /* $@12: %empty */ +#line 3857 "glslang/MachineIndependent/glslang.y" + { + parseContext.symbolTable.push(); + ++parseContext.loopNestingLevel; + ++parseContext.statementNestingLevel; + ++parseContext.controlFlowNestingLevel; + } +#line 10699 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 590: /* iteration_statement_nonattributed: FOR LEFT_PAREN $@12 for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope */ +#line 3863 "glslang/MachineIndependent/glslang.y" + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.makeAggregate((yyvsp[-3].interm.intermNode), (yyvsp[-5].lex).loc); + TIntermLoop* forLoop = parseContext.intermediate.addLoop((yyvsp[0].interm.intermNode), reinterpret_cast((yyvsp[-2].interm.nodePair).node1), reinterpret_cast((yyvsp[-2].interm.nodePair).node2), true, (yyvsp[-6].lex).loc); + if (! parseContext.limits.nonInductiveForLoops) + parseContext.inductiveLoopCheck((yyvsp[-6].lex).loc, (yyvsp[-3].interm.intermNode), forLoop); + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyval.interm.intermNode), forLoop, (yyvsp[-6].lex).loc); + (yyval.interm.intermNode)->getAsAggregate()->setOperator(EOpSequence); + --parseContext.loopNestingLevel; + --parseContext.statementNestingLevel; + --parseContext.controlFlowNestingLevel; + } +#line 10716 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 591: /* for_init_statement: expression_statement */ +#line 3878 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10724 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 592: /* for_init_statement: declaration_statement */ +#line 3881 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10732 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 593: /* conditionopt: condition */ +#line 3887 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode); + } +#line 10740 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 594: /* conditionopt: %empty */ +#line 3890 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermTypedNode) = 0; + } +#line 10748 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 595: /* for_rest_statement: conditionopt SEMICOLON */ +#line 3896 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-1].interm.intermTypedNode); + (yyval.interm.nodePair).node2 = 0; + } +#line 10757 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 596: /* for_rest_statement: conditionopt SEMICOLON expression */ +#line 3900 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.nodePair).node1 = (yyvsp[-2].interm.intermTypedNode); + (yyval.interm.nodePair).node2 = (yyvsp[0].interm.intermTypedNode); + } +#line 10766 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 597: /* jump_statement: CONTINUE SEMICOLON */ +#line 3907 "glslang/MachineIndependent/glslang.y" + { + if (parseContext.loopNestingLevel <= 0) + parseContext.error((yyvsp[-1].lex).loc, "continue statement only allowed in loops", "", ""); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpContinue, (yyvsp[-1].lex).loc); + } +#line 10776 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 598: /* jump_statement: BREAK SEMICOLON */ +#line 3912 "glslang/MachineIndependent/glslang.y" + { + if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) + parseContext.error((yyvsp[-1].lex).loc, "break statement only allowed in switch and loops", "", ""); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpBreak, (yyvsp[-1].lex).loc); + } +#line 10786 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 599: /* jump_statement: RETURN SEMICOLON */ +#line 3917 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpReturn, (yyvsp[-1].lex).loc); + if (parseContext.currentFunctionType->getBasicType() != EbtVoid) + parseContext.error((yyvsp[-1].lex).loc, "non-void function must return a value", "return", ""); + if (parseContext.inMain) + parseContext.postEntryPointReturn = true; + } +#line 10798 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 600: /* jump_statement: RETURN expression SEMICOLON */ +#line 3924 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = parseContext.handleReturnValue((yyvsp[-2].lex).loc, (yyvsp[-1].interm.intermTypedNode)); + } +#line 10806 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 601: /* jump_statement: DISCARD SEMICOLON */ +#line 3927 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "discard"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpKill, (yyvsp[-1].lex).loc); + } +#line 10815 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 602: /* jump_statement: TERMINATE_INVOCATION SEMICOLON */ +#line 3931 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireStage((yyvsp[-1].lex).loc, EShLangFragment, "terminateInvocation"); + (yyval.interm.intermNode) = parseContext.intermediate.addBranch(EOpTerminateInvocation, (yyvsp[-1].lex).loc); + } +#line 10824 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 603: /* translation_unit: external_declaration */ +#line 3940 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + parseContext.intermediate.setTreeRoot((yyval.interm.intermNode)); + } +#line 10833 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 604: /* translation_unit: translation_unit external_declaration */ +#line 3944 "glslang/MachineIndependent/glslang.y" + { + if ((yyvsp[0].interm.intermNode) != nullptr) { + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-1].interm.intermNode), (yyvsp[0].interm.intermNode)); + parseContext.intermediate.setTreeRoot((yyval.interm.intermNode)); + } + } +#line 10844 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 605: /* external_declaration: function_definition */ +#line 3953 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10852 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 606: /* external_declaration: declaration */ +#line 3956 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.intermNode) = (yyvsp[0].interm.intermNode); + } +#line 10860 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 607: /* external_declaration: SEMICOLON */ +#line 3960 "glslang/MachineIndependent/glslang.y" + { + parseContext.requireProfile((yyvsp[0].lex).loc, ~EEsProfile, "extraneous semicolon"); + parseContext.profileRequires((yyvsp[0].lex).loc, ~EEsProfile, 460, nullptr, "extraneous semicolon"); + (yyval.interm.intermNode) = nullptr; + } +#line 10870 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 608: /* $@13: %empty */ +#line 3969 "glslang/MachineIndependent/glslang.y" + { + (yyvsp[0].interm).function = parseContext.handleFunctionDeclarator((yyvsp[0].interm).loc, *(yyvsp[0].interm).function, false /* not prototype */); + (yyvsp[0].interm).intermNode = parseContext.handleFunctionDefinition((yyvsp[0].interm).loc, *(yyvsp[0].interm).function); + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.push(); + ++parseContext.statementNestingLevel; + } + } +#line 10887 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 609: /* function_definition: function_prototype $@13 compound_statement_no_new_scope */ +#line 3981 "glslang/MachineIndependent/glslang.y" + { + // May be best done as post process phase on intermediate code + if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue) + parseContext.error((yyvsp[-2].interm).loc, "function does not return a value:", "", (yyvsp[-2].interm).function->getName().c_str()); + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + (yyval.interm.intermNode) = parseContext.intermediate.growAggregate((yyvsp[-2].interm).intermNode, (yyvsp[0].interm.intermNode)); + parseContext.intermediate.setAggregateOperator((yyval.interm.intermNode), EOpFunction, (yyvsp[-2].interm).function->getType(), (yyvsp[-2].interm).loc); + (yyval.interm.intermNode)->getAsAggregate()->setName((yyvsp[-2].interm).function->getMangledName().c_str()); + + // store the pragma information for debug and optimize and other vendor specific + // information. This information can be queried from the parse tree + (yyval.interm.intermNode)->getAsAggregate()->setOptimize(parseContext.contextPragma.optimize); + (yyval.interm.intermNode)->getAsAggregate()->setDebug(parseContext.contextPragma.debug); + (yyval.interm.intermNode)->getAsAggregate()->setPragmaTable(parseContext.contextPragma.pragmaTable); + + // Set currentFunctionType to empty pointer when goes outside of the function + parseContext.currentFunctionType = nullptr; + + // For ES 100 only, according to ES shading language 100 spec: A function + // body has a scope nested inside the function's definition. + if (parseContext.profile == EEsProfile && parseContext.version == 100) + { + parseContext.symbolTable.pop(&parseContext.defaultPrecision[0]); + --parseContext.statementNestingLevel; + } + } +#line 10918 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 610: /* attribute: LEFT_BRACKET LEFT_BRACKET attribute_list RIGHT_BRACKET RIGHT_BRACKET */ +#line 4011 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = (yyvsp[-2].interm.attributes); + parseContext.requireExtensions((yyvsp[-4].lex).loc, 1, &E_GL_EXT_control_flow_attributes, "attribute"); + } +#line 10927 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 611: /* attribute_list: single_attribute */ +#line 4017 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = (yyvsp[0].interm.attributes); + } +#line 10935 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 612: /* attribute_list: attribute_list COMMA single_attribute */ +#line 4020 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.mergeAttributes((yyvsp[-2].interm.attributes), (yyvsp[0].interm.attributes)); + } +#line 10943 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 613: /* single_attribute: IDENTIFIER */ +#line 4025 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.makeAttributes(*(yyvsp[0].lex).string); + } +#line 10951 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + case 614: /* single_attribute: IDENTIFIER LEFT_PAREN constant_expression RIGHT_PAREN */ +#line 4028 "glslang/MachineIndependent/glslang.y" + { + (yyval.interm.attributes) = parseContext.makeAttributes(*(yyvsp[-3].lex).string, (yyvsp[-1].interm.intermTypedNode)); + } +#line 10959 "glslang/MachineIndependent/glslang_tab.cpp" + break; + + +#line 10963 "glslang/MachineIndependent/glslang_tab.cpp" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (pParseContext, yymsgp); + if (yysyntax_error_status == YYENOMEM) + goto yyexhaustedlab; + } + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, pParseContext); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, pParseContext); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + + +#if 1 +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (pParseContext, YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, pParseContext); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, pParseContext); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + +#line 4033 "glslang/MachineIndependent/glslang.y" + diff --git a/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp.h b/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp.h new file mode 100644 index 0000000..66983a0 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/glslang_tab.cpp.h @@ -0,0 +1,553 @@ +/* A Bison parser, made by GNU Bison 3.7.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2020 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_GLSLANG_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED +# define YY_YY_GLSLANG_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + CONST = 258, /* CONST */ + BOOL = 259, /* BOOL */ + INT = 260, /* INT */ + UINT = 261, /* UINT */ + FLOAT = 262, /* FLOAT */ + BVEC2 = 263, /* BVEC2 */ + BVEC3 = 264, /* BVEC3 */ + BVEC4 = 265, /* BVEC4 */ + IVEC2 = 266, /* IVEC2 */ + IVEC3 = 267, /* IVEC3 */ + IVEC4 = 268, /* IVEC4 */ + UVEC2 = 269, /* UVEC2 */ + UVEC3 = 270, /* UVEC3 */ + UVEC4 = 271, /* UVEC4 */ + VEC2 = 272, /* VEC2 */ + VEC3 = 273, /* VEC3 */ + VEC4 = 274, /* VEC4 */ + MAT2 = 275, /* MAT2 */ + MAT3 = 276, /* MAT3 */ + MAT4 = 277, /* MAT4 */ + MAT2X2 = 278, /* MAT2X2 */ + MAT2X3 = 279, /* MAT2X3 */ + MAT2X4 = 280, /* MAT2X4 */ + MAT3X2 = 281, /* MAT3X2 */ + MAT3X3 = 282, /* MAT3X3 */ + MAT3X4 = 283, /* MAT3X4 */ + MAT4X2 = 284, /* MAT4X2 */ + MAT4X3 = 285, /* MAT4X3 */ + MAT4X4 = 286, /* MAT4X4 */ + SAMPLER2D = 287, /* SAMPLER2D */ + SAMPLER3D = 288, /* SAMPLER3D */ + SAMPLERCUBE = 289, /* SAMPLERCUBE */ + SAMPLER2DSHADOW = 290, /* SAMPLER2DSHADOW */ + SAMPLERCUBESHADOW = 291, /* SAMPLERCUBESHADOW */ + SAMPLER2DARRAY = 292, /* SAMPLER2DARRAY */ + SAMPLER2DARRAYSHADOW = 293, /* SAMPLER2DARRAYSHADOW */ + ISAMPLER2D = 294, /* ISAMPLER2D */ + ISAMPLER3D = 295, /* ISAMPLER3D */ + ISAMPLERCUBE = 296, /* ISAMPLERCUBE */ + ISAMPLER2DARRAY = 297, /* ISAMPLER2DARRAY */ + USAMPLER2D = 298, /* USAMPLER2D */ + USAMPLER3D = 299, /* USAMPLER3D */ + USAMPLERCUBE = 300, /* USAMPLERCUBE */ + USAMPLER2DARRAY = 301, /* USAMPLER2DARRAY */ + SAMPLER = 302, /* SAMPLER */ + SAMPLERSHADOW = 303, /* SAMPLERSHADOW */ + TEXTURE2D = 304, /* TEXTURE2D */ + TEXTURE3D = 305, /* TEXTURE3D */ + TEXTURECUBE = 306, /* TEXTURECUBE */ + TEXTURE2DARRAY = 307, /* TEXTURE2DARRAY */ + ITEXTURE2D = 308, /* ITEXTURE2D */ + ITEXTURE3D = 309, /* ITEXTURE3D */ + ITEXTURECUBE = 310, /* ITEXTURECUBE */ + ITEXTURE2DARRAY = 311, /* ITEXTURE2DARRAY */ + UTEXTURE2D = 312, /* UTEXTURE2D */ + UTEXTURE3D = 313, /* UTEXTURE3D */ + UTEXTURECUBE = 314, /* UTEXTURECUBE */ + UTEXTURE2DARRAY = 315, /* UTEXTURE2DARRAY */ + ATTRIBUTE = 316, /* ATTRIBUTE */ + VARYING = 317, /* VARYING */ + FLOAT16_T = 318, /* FLOAT16_T */ + FLOAT32_T = 319, /* FLOAT32_T */ + DOUBLE = 320, /* DOUBLE */ + FLOAT64_T = 321, /* FLOAT64_T */ + INT64_T = 322, /* INT64_T */ + UINT64_T = 323, /* UINT64_T */ + INT32_T = 324, /* INT32_T */ + UINT32_T = 325, /* UINT32_T */ + INT16_T = 326, /* INT16_T */ + UINT16_T = 327, /* UINT16_T */ + INT8_T = 328, /* INT8_T */ + UINT8_T = 329, /* UINT8_T */ + I64VEC2 = 330, /* I64VEC2 */ + I64VEC3 = 331, /* I64VEC3 */ + I64VEC4 = 332, /* I64VEC4 */ + U64VEC2 = 333, /* U64VEC2 */ + U64VEC3 = 334, /* U64VEC3 */ + U64VEC4 = 335, /* U64VEC4 */ + I32VEC2 = 336, /* I32VEC2 */ + I32VEC3 = 337, /* I32VEC3 */ + I32VEC4 = 338, /* I32VEC4 */ + U32VEC2 = 339, /* U32VEC2 */ + U32VEC3 = 340, /* U32VEC3 */ + U32VEC4 = 341, /* U32VEC4 */ + I16VEC2 = 342, /* I16VEC2 */ + I16VEC3 = 343, /* I16VEC3 */ + I16VEC4 = 344, /* I16VEC4 */ + U16VEC2 = 345, /* U16VEC2 */ + U16VEC3 = 346, /* U16VEC3 */ + U16VEC4 = 347, /* U16VEC4 */ + I8VEC2 = 348, /* I8VEC2 */ + I8VEC3 = 349, /* I8VEC3 */ + I8VEC4 = 350, /* I8VEC4 */ + U8VEC2 = 351, /* U8VEC2 */ + U8VEC3 = 352, /* U8VEC3 */ + U8VEC4 = 353, /* U8VEC4 */ + DVEC2 = 354, /* DVEC2 */ + DVEC3 = 355, /* DVEC3 */ + DVEC4 = 356, /* DVEC4 */ + DMAT2 = 357, /* DMAT2 */ + DMAT3 = 358, /* DMAT3 */ + DMAT4 = 359, /* DMAT4 */ + F16VEC2 = 360, /* F16VEC2 */ + F16VEC3 = 361, /* F16VEC3 */ + F16VEC4 = 362, /* F16VEC4 */ + F16MAT2 = 363, /* F16MAT2 */ + F16MAT3 = 364, /* F16MAT3 */ + F16MAT4 = 365, /* F16MAT4 */ + F32VEC2 = 366, /* F32VEC2 */ + F32VEC3 = 367, /* F32VEC3 */ + F32VEC4 = 368, /* F32VEC4 */ + F32MAT2 = 369, /* F32MAT2 */ + F32MAT3 = 370, /* F32MAT3 */ + F32MAT4 = 371, /* F32MAT4 */ + F64VEC2 = 372, /* F64VEC2 */ + F64VEC3 = 373, /* F64VEC3 */ + F64VEC4 = 374, /* F64VEC4 */ + F64MAT2 = 375, /* F64MAT2 */ + F64MAT3 = 376, /* F64MAT3 */ + F64MAT4 = 377, /* F64MAT4 */ + DMAT2X2 = 378, /* DMAT2X2 */ + DMAT2X3 = 379, /* DMAT2X3 */ + DMAT2X4 = 380, /* DMAT2X4 */ + DMAT3X2 = 381, /* DMAT3X2 */ + DMAT3X3 = 382, /* DMAT3X3 */ + DMAT3X4 = 383, /* DMAT3X4 */ + DMAT4X2 = 384, /* DMAT4X2 */ + DMAT4X3 = 385, /* DMAT4X3 */ + DMAT4X4 = 386, /* DMAT4X4 */ + F16MAT2X2 = 387, /* F16MAT2X2 */ + F16MAT2X3 = 388, /* F16MAT2X3 */ + F16MAT2X4 = 389, /* F16MAT2X4 */ + F16MAT3X2 = 390, /* F16MAT3X2 */ + F16MAT3X3 = 391, /* F16MAT3X3 */ + F16MAT3X4 = 392, /* F16MAT3X4 */ + F16MAT4X2 = 393, /* F16MAT4X2 */ + F16MAT4X3 = 394, /* F16MAT4X3 */ + F16MAT4X4 = 395, /* F16MAT4X4 */ + F32MAT2X2 = 396, /* F32MAT2X2 */ + F32MAT2X3 = 397, /* F32MAT2X3 */ + F32MAT2X4 = 398, /* F32MAT2X4 */ + F32MAT3X2 = 399, /* F32MAT3X2 */ + F32MAT3X3 = 400, /* F32MAT3X3 */ + F32MAT3X4 = 401, /* F32MAT3X4 */ + F32MAT4X2 = 402, /* F32MAT4X2 */ + F32MAT4X3 = 403, /* F32MAT4X3 */ + F32MAT4X4 = 404, /* F32MAT4X4 */ + F64MAT2X2 = 405, /* F64MAT2X2 */ + F64MAT2X3 = 406, /* F64MAT2X3 */ + F64MAT2X4 = 407, /* F64MAT2X4 */ + F64MAT3X2 = 408, /* F64MAT3X2 */ + F64MAT3X3 = 409, /* F64MAT3X3 */ + F64MAT3X4 = 410, /* F64MAT3X4 */ + F64MAT4X2 = 411, /* F64MAT4X2 */ + F64MAT4X3 = 412, /* F64MAT4X3 */ + F64MAT4X4 = 413, /* F64MAT4X4 */ + ATOMIC_UINT = 414, /* ATOMIC_UINT */ + ACCSTRUCTNV = 415, /* ACCSTRUCTNV */ + ACCSTRUCTEXT = 416, /* ACCSTRUCTEXT */ + RAYQUERYEXT = 417, /* RAYQUERYEXT */ + FCOOPMATNV = 418, /* FCOOPMATNV */ + ICOOPMATNV = 419, /* ICOOPMATNV */ + UCOOPMATNV = 420, /* UCOOPMATNV */ + SAMPLERCUBEARRAY = 421, /* SAMPLERCUBEARRAY */ + SAMPLERCUBEARRAYSHADOW = 422, /* SAMPLERCUBEARRAYSHADOW */ + ISAMPLERCUBEARRAY = 423, /* ISAMPLERCUBEARRAY */ + USAMPLERCUBEARRAY = 424, /* USAMPLERCUBEARRAY */ + SAMPLER1D = 425, /* SAMPLER1D */ + SAMPLER1DARRAY = 426, /* SAMPLER1DARRAY */ + SAMPLER1DARRAYSHADOW = 427, /* SAMPLER1DARRAYSHADOW */ + ISAMPLER1D = 428, /* ISAMPLER1D */ + SAMPLER1DSHADOW = 429, /* SAMPLER1DSHADOW */ + SAMPLER2DRECT = 430, /* SAMPLER2DRECT */ + SAMPLER2DRECTSHADOW = 431, /* SAMPLER2DRECTSHADOW */ + ISAMPLER2DRECT = 432, /* ISAMPLER2DRECT */ + USAMPLER2DRECT = 433, /* USAMPLER2DRECT */ + SAMPLERBUFFER = 434, /* SAMPLERBUFFER */ + ISAMPLERBUFFER = 435, /* ISAMPLERBUFFER */ + USAMPLERBUFFER = 436, /* USAMPLERBUFFER */ + SAMPLER2DMS = 437, /* SAMPLER2DMS */ + ISAMPLER2DMS = 438, /* ISAMPLER2DMS */ + USAMPLER2DMS = 439, /* USAMPLER2DMS */ + SAMPLER2DMSARRAY = 440, /* SAMPLER2DMSARRAY */ + ISAMPLER2DMSARRAY = 441, /* ISAMPLER2DMSARRAY */ + USAMPLER2DMSARRAY = 442, /* USAMPLER2DMSARRAY */ + SAMPLEREXTERNALOES = 443, /* SAMPLEREXTERNALOES */ + SAMPLEREXTERNAL2DY2YEXT = 444, /* SAMPLEREXTERNAL2DY2YEXT */ + ISAMPLER1DARRAY = 445, /* ISAMPLER1DARRAY */ + USAMPLER1D = 446, /* USAMPLER1D */ + USAMPLER1DARRAY = 447, /* USAMPLER1DARRAY */ + F16SAMPLER1D = 448, /* F16SAMPLER1D */ + F16SAMPLER2D = 449, /* F16SAMPLER2D */ + F16SAMPLER3D = 450, /* F16SAMPLER3D */ + F16SAMPLER2DRECT = 451, /* F16SAMPLER2DRECT */ + F16SAMPLERCUBE = 452, /* F16SAMPLERCUBE */ + F16SAMPLER1DARRAY = 453, /* F16SAMPLER1DARRAY */ + F16SAMPLER2DARRAY = 454, /* F16SAMPLER2DARRAY */ + F16SAMPLERCUBEARRAY = 455, /* F16SAMPLERCUBEARRAY */ + F16SAMPLERBUFFER = 456, /* F16SAMPLERBUFFER */ + F16SAMPLER2DMS = 457, /* F16SAMPLER2DMS */ + F16SAMPLER2DMSARRAY = 458, /* F16SAMPLER2DMSARRAY */ + F16SAMPLER1DSHADOW = 459, /* F16SAMPLER1DSHADOW */ + F16SAMPLER2DSHADOW = 460, /* F16SAMPLER2DSHADOW */ + F16SAMPLER1DARRAYSHADOW = 461, /* F16SAMPLER1DARRAYSHADOW */ + F16SAMPLER2DARRAYSHADOW = 462, /* F16SAMPLER2DARRAYSHADOW */ + F16SAMPLER2DRECTSHADOW = 463, /* F16SAMPLER2DRECTSHADOW */ + F16SAMPLERCUBESHADOW = 464, /* F16SAMPLERCUBESHADOW */ + F16SAMPLERCUBEARRAYSHADOW = 465, /* F16SAMPLERCUBEARRAYSHADOW */ + IMAGE1D = 466, /* IMAGE1D */ + IIMAGE1D = 467, /* IIMAGE1D */ + UIMAGE1D = 468, /* UIMAGE1D */ + IMAGE2D = 469, /* IMAGE2D */ + IIMAGE2D = 470, /* IIMAGE2D */ + UIMAGE2D = 471, /* UIMAGE2D */ + IMAGE3D = 472, /* IMAGE3D */ + IIMAGE3D = 473, /* IIMAGE3D */ + UIMAGE3D = 474, /* UIMAGE3D */ + IMAGE2DRECT = 475, /* IMAGE2DRECT */ + IIMAGE2DRECT = 476, /* IIMAGE2DRECT */ + UIMAGE2DRECT = 477, /* UIMAGE2DRECT */ + IMAGECUBE = 478, /* IMAGECUBE */ + IIMAGECUBE = 479, /* IIMAGECUBE */ + UIMAGECUBE = 480, /* UIMAGECUBE */ + IMAGEBUFFER = 481, /* IMAGEBUFFER */ + IIMAGEBUFFER = 482, /* IIMAGEBUFFER */ + UIMAGEBUFFER = 483, /* UIMAGEBUFFER */ + IMAGE1DARRAY = 484, /* IMAGE1DARRAY */ + IIMAGE1DARRAY = 485, /* IIMAGE1DARRAY */ + UIMAGE1DARRAY = 486, /* UIMAGE1DARRAY */ + IMAGE2DARRAY = 487, /* IMAGE2DARRAY */ + IIMAGE2DARRAY = 488, /* IIMAGE2DARRAY */ + UIMAGE2DARRAY = 489, /* UIMAGE2DARRAY */ + IMAGECUBEARRAY = 490, /* IMAGECUBEARRAY */ + IIMAGECUBEARRAY = 491, /* IIMAGECUBEARRAY */ + UIMAGECUBEARRAY = 492, /* UIMAGECUBEARRAY */ + IMAGE2DMS = 493, /* IMAGE2DMS */ + IIMAGE2DMS = 494, /* IIMAGE2DMS */ + UIMAGE2DMS = 495, /* UIMAGE2DMS */ + IMAGE2DMSARRAY = 496, /* IMAGE2DMSARRAY */ + IIMAGE2DMSARRAY = 497, /* IIMAGE2DMSARRAY */ + UIMAGE2DMSARRAY = 498, /* UIMAGE2DMSARRAY */ + F16IMAGE1D = 499, /* F16IMAGE1D */ + F16IMAGE2D = 500, /* F16IMAGE2D */ + F16IMAGE3D = 501, /* F16IMAGE3D */ + F16IMAGE2DRECT = 502, /* F16IMAGE2DRECT */ + F16IMAGECUBE = 503, /* F16IMAGECUBE */ + F16IMAGE1DARRAY = 504, /* F16IMAGE1DARRAY */ + F16IMAGE2DARRAY = 505, /* F16IMAGE2DARRAY */ + F16IMAGECUBEARRAY = 506, /* F16IMAGECUBEARRAY */ + F16IMAGEBUFFER = 507, /* F16IMAGEBUFFER */ + F16IMAGE2DMS = 508, /* F16IMAGE2DMS */ + F16IMAGE2DMSARRAY = 509, /* F16IMAGE2DMSARRAY */ + I64IMAGE1D = 510, /* I64IMAGE1D */ + U64IMAGE1D = 511, /* U64IMAGE1D */ + I64IMAGE2D = 512, /* I64IMAGE2D */ + U64IMAGE2D = 513, /* U64IMAGE2D */ + I64IMAGE3D = 514, /* I64IMAGE3D */ + U64IMAGE3D = 515, /* U64IMAGE3D */ + I64IMAGE2DRECT = 516, /* I64IMAGE2DRECT */ + U64IMAGE2DRECT = 517, /* U64IMAGE2DRECT */ + I64IMAGECUBE = 518, /* I64IMAGECUBE */ + U64IMAGECUBE = 519, /* U64IMAGECUBE */ + I64IMAGEBUFFER = 520, /* I64IMAGEBUFFER */ + U64IMAGEBUFFER = 521, /* U64IMAGEBUFFER */ + I64IMAGE1DARRAY = 522, /* I64IMAGE1DARRAY */ + U64IMAGE1DARRAY = 523, /* U64IMAGE1DARRAY */ + I64IMAGE2DARRAY = 524, /* I64IMAGE2DARRAY */ + U64IMAGE2DARRAY = 525, /* U64IMAGE2DARRAY */ + I64IMAGECUBEARRAY = 526, /* I64IMAGECUBEARRAY */ + U64IMAGECUBEARRAY = 527, /* U64IMAGECUBEARRAY */ + I64IMAGE2DMS = 528, /* I64IMAGE2DMS */ + U64IMAGE2DMS = 529, /* U64IMAGE2DMS */ + I64IMAGE2DMSARRAY = 530, /* I64IMAGE2DMSARRAY */ + U64IMAGE2DMSARRAY = 531, /* U64IMAGE2DMSARRAY */ + TEXTURECUBEARRAY = 532, /* TEXTURECUBEARRAY */ + ITEXTURECUBEARRAY = 533, /* ITEXTURECUBEARRAY */ + UTEXTURECUBEARRAY = 534, /* UTEXTURECUBEARRAY */ + TEXTURE1D = 535, /* TEXTURE1D */ + ITEXTURE1D = 536, /* ITEXTURE1D */ + UTEXTURE1D = 537, /* UTEXTURE1D */ + TEXTURE1DARRAY = 538, /* TEXTURE1DARRAY */ + ITEXTURE1DARRAY = 539, /* ITEXTURE1DARRAY */ + UTEXTURE1DARRAY = 540, /* UTEXTURE1DARRAY */ + TEXTURE2DRECT = 541, /* TEXTURE2DRECT */ + ITEXTURE2DRECT = 542, /* ITEXTURE2DRECT */ + UTEXTURE2DRECT = 543, /* UTEXTURE2DRECT */ + TEXTUREBUFFER = 544, /* TEXTUREBUFFER */ + ITEXTUREBUFFER = 545, /* ITEXTUREBUFFER */ + UTEXTUREBUFFER = 546, /* UTEXTUREBUFFER */ + TEXTURE2DMS = 547, /* TEXTURE2DMS */ + ITEXTURE2DMS = 548, /* ITEXTURE2DMS */ + UTEXTURE2DMS = 549, /* UTEXTURE2DMS */ + TEXTURE2DMSARRAY = 550, /* TEXTURE2DMSARRAY */ + ITEXTURE2DMSARRAY = 551, /* ITEXTURE2DMSARRAY */ + UTEXTURE2DMSARRAY = 552, /* UTEXTURE2DMSARRAY */ + F16TEXTURE1D = 553, /* F16TEXTURE1D */ + F16TEXTURE2D = 554, /* F16TEXTURE2D */ + F16TEXTURE3D = 555, /* F16TEXTURE3D */ + F16TEXTURE2DRECT = 556, /* F16TEXTURE2DRECT */ + F16TEXTURECUBE = 557, /* F16TEXTURECUBE */ + F16TEXTURE1DARRAY = 558, /* F16TEXTURE1DARRAY */ + F16TEXTURE2DARRAY = 559, /* F16TEXTURE2DARRAY */ + F16TEXTURECUBEARRAY = 560, /* F16TEXTURECUBEARRAY */ + F16TEXTUREBUFFER = 561, /* F16TEXTUREBUFFER */ + F16TEXTURE2DMS = 562, /* F16TEXTURE2DMS */ + F16TEXTURE2DMSARRAY = 563, /* F16TEXTURE2DMSARRAY */ + SUBPASSINPUT = 564, /* SUBPASSINPUT */ + SUBPASSINPUTMS = 565, /* SUBPASSINPUTMS */ + ISUBPASSINPUT = 566, /* ISUBPASSINPUT */ + ISUBPASSINPUTMS = 567, /* ISUBPASSINPUTMS */ + USUBPASSINPUT = 568, /* USUBPASSINPUT */ + USUBPASSINPUTMS = 569, /* USUBPASSINPUTMS */ + F16SUBPASSINPUT = 570, /* F16SUBPASSINPUT */ + F16SUBPASSINPUTMS = 571, /* F16SUBPASSINPUTMS */ + LEFT_OP = 572, /* LEFT_OP */ + RIGHT_OP = 573, /* RIGHT_OP */ + INC_OP = 574, /* INC_OP */ + DEC_OP = 575, /* DEC_OP */ + LE_OP = 576, /* LE_OP */ + GE_OP = 577, /* GE_OP */ + EQ_OP = 578, /* EQ_OP */ + NE_OP = 579, /* NE_OP */ + AND_OP = 580, /* AND_OP */ + OR_OP = 581, /* OR_OP */ + XOR_OP = 582, /* XOR_OP */ + MUL_ASSIGN = 583, /* MUL_ASSIGN */ + DIV_ASSIGN = 584, /* DIV_ASSIGN */ + ADD_ASSIGN = 585, /* ADD_ASSIGN */ + MOD_ASSIGN = 586, /* MOD_ASSIGN */ + LEFT_ASSIGN = 587, /* LEFT_ASSIGN */ + RIGHT_ASSIGN = 588, /* RIGHT_ASSIGN */ + AND_ASSIGN = 589, /* AND_ASSIGN */ + XOR_ASSIGN = 590, /* XOR_ASSIGN */ + OR_ASSIGN = 591, /* OR_ASSIGN */ + SUB_ASSIGN = 592, /* SUB_ASSIGN */ + STRING_LITERAL = 593, /* STRING_LITERAL */ + LEFT_PAREN = 594, /* LEFT_PAREN */ + RIGHT_PAREN = 595, /* RIGHT_PAREN */ + LEFT_BRACKET = 596, /* LEFT_BRACKET */ + RIGHT_BRACKET = 597, /* RIGHT_BRACKET */ + LEFT_BRACE = 598, /* LEFT_BRACE */ + RIGHT_BRACE = 599, /* RIGHT_BRACE */ + DOT = 600, /* DOT */ + COMMA = 601, /* COMMA */ + COLON = 602, /* COLON */ + EQUAL = 603, /* EQUAL */ + SEMICOLON = 604, /* SEMICOLON */ + BANG = 605, /* BANG */ + DASH = 606, /* DASH */ + TILDE = 607, /* TILDE */ + PLUS = 608, /* PLUS */ + STAR = 609, /* STAR */ + SLASH = 610, /* SLASH */ + PERCENT = 611, /* PERCENT */ + LEFT_ANGLE = 612, /* LEFT_ANGLE */ + RIGHT_ANGLE = 613, /* RIGHT_ANGLE */ + VERTICAL_BAR = 614, /* VERTICAL_BAR */ + CARET = 615, /* CARET */ + AMPERSAND = 616, /* AMPERSAND */ + QUESTION = 617, /* QUESTION */ + INVARIANT = 618, /* INVARIANT */ + HIGH_PRECISION = 619, /* HIGH_PRECISION */ + MEDIUM_PRECISION = 620, /* MEDIUM_PRECISION */ + LOW_PRECISION = 621, /* LOW_PRECISION */ + PRECISION = 622, /* PRECISION */ + PACKED = 623, /* PACKED */ + RESOURCE = 624, /* RESOURCE */ + SUPERP = 625, /* SUPERP */ + FLOATCONSTANT = 626, /* FLOATCONSTANT */ + INTCONSTANT = 627, /* INTCONSTANT */ + UINTCONSTANT = 628, /* UINTCONSTANT */ + BOOLCONSTANT = 629, /* BOOLCONSTANT */ + IDENTIFIER = 630, /* IDENTIFIER */ + TYPE_NAME = 631, /* TYPE_NAME */ + CENTROID = 632, /* CENTROID */ + IN = 633, /* IN */ + OUT = 634, /* OUT */ + INOUT = 635, /* INOUT */ + STRUCT = 636, /* STRUCT */ + VOID = 637, /* VOID */ + WHILE = 638, /* WHILE */ + BREAK = 639, /* BREAK */ + CONTINUE = 640, /* CONTINUE */ + DO = 641, /* DO */ + ELSE = 642, /* ELSE */ + FOR = 643, /* FOR */ + IF = 644, /* IF */ + DISCARD = 645, /* DISCARD */ + RETURN = 646, /* RETURN */ + SWITCH = 647, /* SWITCH */ + CASE = 648, /* CASE */ + DEFAULT = 649, /* DEFAULT */ + TERMINATE_INVOCATION = 650, /* TERMINATE_INVOCATION */ + UNIFORM = 651, /* UNIFORM */ + SHARED = 652, /* SHARED */ + BUFFER = 653, /* BUFFER */ + FLAT = 654, /* FLAT */ + SMOOTH = 655, /* SMOOTH */ + LAYOUT = 656, /* LAYOUT */ + DOUBLECONSTANT = 657, /* DOUBLECONSTANT */ + INT16CONSTANT = 658, /* INT16CONSTANT */ + UINT16CONSTANT = 659, /* UINT16CONSTANT */ + FLOAT16CONSTANT = 660, /* FLOAT16CONSTANT */ + INT32CONSTANT = 661, /* INT32CONSTANT */ + UINT32CONSTANT = 662, /* UINT32CONSTANT */ + INT64CONSTANT = 663, /* INT64CONSTANT */ + UINT64CONSTANT = 664, /* UINT64CONSTANT */ + SUBROUTINE = 665, /* SUBROUTINE */ + DEMOTE = 666, /* DEMOTE */ + PAYLOADNV = 667, /* PAYLOADNV */ + PAYLOADINNV = 668, /* PAYLOADINNV */ + HITATTRNV = 669, /* HITATTRNV */ + CALLDATANV = 670, /* CALLDATANV */ + CALLDATAINNV = 671, /* CALLDATAINNV */ + PAYLOADEXT = 672, /* PAYLOADEXT */ + PAYLOADINEXT = 673, /* PAYLOADINEXT */ + HITATTREXT = 674, /* HITATTREXT */ + CALLDATAEXT = 675, /* CALLDATAEXT */ + CALLDATAINEXT = 676, /* CALLDATAINEXT */ + PATCH = 677, /* PATCH */ + SAMPLE = 678, /* SAMPLE */ + NONUNIFORM = 679, /* NONUNIFORM */ + COHERENT = 680, /* COHERENT */ + VOLATILE = 681, /* VOLATILE */ + RESTRICT = 682, /* RESTRICT */ + READONLY = 683, /* READONLY */ + WRITEONLY = 684, /* WRITEONLY */ + DEVICECOHERENT = 685, /* DEVICECOHERENT */ + QUEUEFAMILYCOHERENT = 686, /* QUEUEFAMILYCOHERENT */ + WORKGROUPCOHERENT = 687, /* WORKGROUPCOHERENT */ + SUBGROUPCOHERENT = 688, /* SUBGROUPCOHERENT */ + NONPRIVATE = 689, /* NONPRIVATE */ + SHADERCALLCOHERENT = 690, /* SHADERCALLCOHERENT */ + NOPERSPECTIVE = 691, /* NOPERSPECTIVE */ + EXPLICITINTERPAMD = 692, /* EXPLICITINTERPAMD */ + PERVERTEXNV = 693, /* PERVERTEXNV */ + PERPRIMITIVENV = 694, /* PERPRIMITIVENV */ + PERVIEWNV = 695, /* PERVIEWNV */ + PERTASKNV = 696, /* PERTASKNV */ + PRECISE = 697 /* PRECISE */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 97 "glslang/MachineIndependent/glslang.y" + + struct { + glslang::TSourceLoc loc; + union { + glslang::TString *string; + int i; + unsigned int u; + long long i64; + unsigned long long u64; + bool b; + double d; + }; + glslang::TSymbol* symbol; + } lex; + struct { + glslang::TSourceLoc loc; + glslang::TOperator op; + union { + TIntermNode* intermNode; + glslang::TIntermNodePair nodePair; + glslang::TIntermTyped* intermTypedNode; + glslang::TAttributes* attributes; + }; + union { + glslang::TPublicType type; + glslang::TFunction* function; + glslang::TParameter param; + glslang::TTypeLoc typeLine; + glslang::TTypeList* typeList; + glslang::TArraySizes* arraySizes; + glslang::TIdentifierList* identifierList; + }; + glslang::TArraySizes* typeParameters; + } interm; + +#line 542 "glslang/MachineIndependent/glslang_tab.cpp.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + + +int yyparse (glslang::TParseContext* pParseContext); + +#endif /* !YY_YY_GLSLANG_MACHINEINDEPENDENT_GLSLANG_TAB_CPP_H_INCLUDED */ diff --git a/third_party/glslang/glslang/MachineIndependent/intermOut.cpp b/third_party/glslang/glslang/MachineIndependent/intermOut.cpp new file mode 100644 index 0000000..ac862e6 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/intermOut.cpp @@ -0,0 +1,1569 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2012-2016 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +#include "localintermediate.h" +#include "../Include/InfoSink.h" + +#ifdef _MSC_VER +#include +#else +#include +#endif +#include + +namespace { + +bool IsInfinity(double x) { +#ifdef _MSC_VER + switch (_fpclass(x)) { + case _FPCLASS_NINF: + case _FPCLASS_PINF: + return true; + default: + return false; + } +#else + return std::isinf(x); +#endif +} + +bool IsNan(double x) { +#ifdef _MSC_VER + switch (_fpclass(x)) { + case _FPCLASS_SNAN: + case _FPCLASS_QNAN: + return true; + default: + return false; + } +#else + return std::isnan(x); +#endif +} + +} + +namespace glslang { + +// +// Two purposes: +// 1. Show an example of how to iterate tree. Functions can +// also directly call Traverse() on children themselves to +// have finer grained control over the process than shown here. +// See the last function for how to get started. +// 2. Print out a text based description of the tree. +// + +// +// Use this class to carry along data from node to node in +// the traversal +// +class TOutputTraverser : public TIntermTraverser { +public: + TOutputTraverser(TInfoSink& i) : infoSink(i), extraOutput(NoExtraOutput) { } + + enum EExtraOutput { + NoExtraOutput, + BinaryDoubleOutput + }; + void setDoubleOutput(EExtraOutput extra) { extraOutput = extra; } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual bool visitUnary(TVisit, TIntermUnary* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + virtual bool visitSelection(TVisit, TIntermSelection* node); + virtual void visitConstantUnion(TIntermConstantUnion* node); + virtual void visitSymbol(TIntermSymbol* node); + virtual bool visitLoop(TVisit, TIntermLoop* node); + virtual bool visitBranch(TVisit, TIntermBranch* node); + virtual bool visitSwitch(TVisit, TIntermSwitch* node); + + TInfoSink& infoSink; +protected: + TOutputTraverser(TOutputTraverser&); + TOutputTraverser& operator=(TOutputTraverser&); + + EExtraOutput extraOutput; +}; + +// +// Helper functions for printing, not part of traversing. +// + +static void OutputTreeText(TInfoSink& infoSink, const TIntermNode* node, const int depth) +{ + int i; + + infoSink.debug << node->getLoc().string << ":"; + if (node->getLoc().line) + infoSink.debug << node->getLoc().line; + else + infoSink.debug << "? "; + + for (i = 0; i < depth; ++i) + infoSink.debug << " "; +} + +// +// The rest of the file are the traversal functions. The last one +// is the one that starts the traversal. +// +// Return true from interior nodes to have the external traversal +// continue on to children. If you process children yourself, +// return false. +// + +bool TOutputTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + switch (node->getOp()) { + case EOpAssign: out.debug << "move second child to first child"; break; + case EOpAddAssign: out.debug << "add second child into first child"; break; + case EOpSubAssign: out.debug << "subtract second child into first child"; break; + case EOpMulAssign: out.debug << "multiply second child into first child"; break; + case EOpVectorTimesMatrixAssign: out.debug << "matrix mult second child into first child"; break; + case EOpVectorTimesScalarAssign: out.debug << "vector scale second child into first child"; break; + case EOpMatrixTimesScalarAssign: out.debug << "matrix scale second child into first child"; break; + case EOpMatrixTimesMatrixAssign: out.debug << "matrix mult second child into first child"; break; + case EOpDivAssign: out.debug << "divide second child into first child"; break; + case EOpModAssign: out.debug << "mod second child into first child"; break; + case EOpAndAssign: out.debug << "and second child into first child"; break; + case EOpInclusiveOrAssign: out.debug << "or second child into first child"; break; + case EOpExclusiveOrAssign: out.debug << "exclusive or second child into first child"; break; + case EOpLeftShiftAssign: out.debug << "left shift second child into first child"; break; + case EOpRightShiftAssign: out.debug << "right shift second child into first child"; break; + + case EOpIndexDirect: out.debug << "direct index"; break; + case EOpIndexIndirect: out.debug << "indirect index"; break; + case EOpIndexDirectStruct: + { + bool reference = node->getLeft()->getType().isReference(); + const TTypeList *members = reference ? node->getLeft()->getType().getReferentType()->getStruct() : node->getLeft()->getType().getStruct(); + out.debug << (*members)[node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst()].type->getFieldName(); + out.debug << ": direct index for structure"; break; + } + case EOpVectorSwizzle: out.debug << "vector swizzle"; break; + case EOpMatrixSwizzle: out.debug << "matrix swizzle"; break; + + case EOpAdd: out.debug << "add"; break; + case EOpSub: out.debug << "subtract"; break; + case EOpMul: out.debug << "component-wise multiply"; break; + case EOpDiv: out.debug << "divide"; break; + case EOpMod: out.debug << "mod"; break; + case EOpRightShift: out.debug << "right-shift"; break; + case EOpLeftShift: out.debug << "left-shift"; break; + case EOpAnd: out.debug << "bitwise and"; break; + case EOpInclusiveOr: out.debug << "inclusive-or"; break; + case EOpExclusiveOr: out.debug << "exclusive-or"; break; + case EOpEqual: out.debug << "Compare Equal"; break; + case EOpNotEqual: out.debug << "Compare Not Equal"; break; + case EOpLessThan: out.debug << "Compare Less Than"; break; + case EOpGreaterThan: out.debug << "Compare Greater Than"; break; + case EOpLessThanEqual: out.debug << "Compare Less Than or Equal"; break; + case EOpGreaterThanEqual: out.debug << "Compare Greater Than or Equal"; break; + case EOpVectorEqual: out.debug << "Equal"; break; + case EOpVectorNotEqual: out.debug << "NotEqual"; break; + + case EOpVectorTimesScalar: out.debug << "vector-scale"; break; + case EOpVectorTimesMatrix: out.debug << "vector-times-matrix"; break; + case EOpMatrixTimesVector: out.debug << "matrix-times-vector"; break; + case EOpMatrixTimesScalar: out.debug << "matrix-scale"; break; + case EOpMatrixTimesMatrix: out.debug << "matrix-multiply"; break; + + case EOpLogicalOr: out.debug << "logical-or"; break; + case EOpLogicalXor: out.debug << "logical-xor"; break; + case EOpLogicalAnd: out.debug << "logical-and"; break; + + case EOpAbsDifference: out.debug << "absoluteDifference"; break; + case EOpAddSaturate: out.debug << "addSaturate"; break; + case EOpSubSaturate: out.debug << "subtractSaturate"; break; + case EOpAverage: out.debug << "average"; break; + case EOpAverageRounded: out.debug << "averageRounded"; break; + case EOpMul32x16: out.debug << "multiply32x16"; break; + + default: out.debug << ""; + } + + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + switch (node->getOp()) { + case EOpNegative: out.debug << "Negate value"; break; + case EOpVectorLogicalNot: + case EOpLogicalNot: out.debug << "Negate conditional"; break; + case EOpBitwiseNot: out.debug << "Bitwise not"; break; + + case EOpPostIncrement: out.debug << "Post-Increment"; break; + case EOpPostDecrement: out.debug << "Post-Decrement"; break; + case EOpPreIncrement: out.debug << "Pre-Increment"; break; + case EOpPreDecrement: out.debug << "Pre-Decrement"; break; + case EOpCopyObject: out.debug << "copy object"; break; + + // * -> bool + case EOpConvInt8ToBool: out.debug << "Convert int8_t to bool"; break; + case EOpConvUint8ToBool: out.debug << "Convert uint8_t to bool"; break; + case EOpConvInt16ToBool: out.debug << "Convert int16_t to bool"; break; + case EOpConvUint16ToBool: out.debug << "Convert uint16_t to bool";break; + case EOpConvIntToBool: out.debug << "Convert int to bool"; break; + case EOpConvUintToBool: out.debug << "Convert uint to bool"; break; + case EOpConvInt64ToBool: out.debug << "Convert int64 to bool"; break; + case EOpConvUint64ToBool: out.debug << "Convert uint64 to bool"; break; + case EOpConvFloat16ToBool: out.debug << "Convert float16_t to bool"; break; + case EOpConvFloatToBool: out.debug << "Convert float to bool"; break; + case EOpConvDoubleToBool: out.debug << "Convert double to bool"; break; + + // bool -> * + case EOpConvBoolToInt8: out.debug << "Convert bool to int8_t"; break; + case EOpConvBoolToUint8: out.debug << "Convert bool to uint8_t"; break; + case EOpConvBoolToInt16: out.debug << "Convert bool to in16t_t"; break; + case EOpConvBoolToUint16: out.debug << "Convert bool to uint16_t";break; + case EOpConvBoolToInt: out.debug << "Convert bool to int" ; break; + case EOpConvBoolToUint: out.debug << "Convert bool to uint"; break; + case EOpConvBoolToInt64: out.debug << "Convert bool to int64"; break; + case EOpConvBoolToUint64: out.debug << "Convert bool to uint64";break; + case EOpConvBoolToFloat16: out.debug << "Convert bool to float16_t"; break; + case EOpConvBoolToFloat: out.debug << "Convert bool to float"; break; + case EOpConvBoolToDouble: out.debug << "Convert bool to double"; break; + + // int8_t -> (u)int* + case EOpConvInt8ToInt16: out.debug << "Convert int8_t to int16_t";break; + case EOpConvInt8ToInt: out.debug << "Convert int8_t to int"; break; + case EOpConvInt8ToInt64: out.debug << "Convert int8_t to int64"; break; + case EOpConvInt8ToUint8: out.debug << "Convert int8_t to uint8_t";break; + case EOpConvInt8ToUint16: out.debug << "Convert int8_t to uint16_t";break; + case EOpConvInt8ToUint: out.debug << "Convert int8_t to uint"; break; + case EOpConvInt8ToUint64: out.debug << "Convert int8_t to uint64"; break; + + // uint8_t -> (u)int* + case EOpConvUint8ToInt8: out.debug << "Convert uint8_t to int8_t";break; + case EOpConvUint8ToInt16: out.debug << "Convert uint8_t to int16_t";break; + case EOpConvUint8ToInt: out.debug << "Convert uint8_t to int"; break; + case EOpConvUint8ToInt64: out.debug << "Convert uint8_t to int64"; break; + case EOpConvUint8ToUint16: out.debug << "Convert uint8_t to uint16_t";break; + case EOpConvUint8ToUint: out.debug << "Convert uint8_t to uint"; break; + case EOpConvUint8ToUint64: out.debug << "Convert uint8_t to uint64"; break; + + // int8_t -> float* + case EOpConvInt8ToFloat16: out.debug << "Convert int8_t to float16_t";break; + case EOpConvInt8ToFloat: out.debug << "Convert int8_t to float"; break; + case EOpConvInt8ToDouble: out.debug << "Convert int8_t to double"; break; + + // uint8_t -> float* + case EOpConvUint8ToFloat16: out.debug << "Convert uint8_t to float16_t";break; + case EOpConvUint8ToFloat: out.debug << "Convert uint8_t to float"; break; + case EOpConvUint8ToDouble: out.debug << "Convert uint8_t to double"; break; + + // int16_t -> (u)int* + case EOpConvInt16ToInt8: out.debug << "Convert int16_t to int8_t";break; + case EOpConvInt16ToInt: out.debug << "Convert int16_t to int"; break; + case EOpConvInt16ToInt64: out.debug << "Convert int16_t to int64"; break; + case EOpConvInt16ToUint8: out.debug << "Convert int16_t to uint8_t";break; + case EOpConvInt16ToUint16: out.debug << "Convert int16_t to uint16_t";break; + case EOpConvInt16ToUint: out.debug << "Convert int16_t to uint"; break; + case EOpConvInt16ToUint64: out.debug << "Convert int16_t to uint64"; break; + + // int16_t -> float* + case EOpConvInt16ToFloat16: out.debug << "Convert int16_t to float16_t";break; + case EOpConvInt16ToFloat: out.debug << "Convert int16_t to float"; break; + case EOpConvInt16ToDouble: out.debug << "Convert int16_t to double"; break; + + // uint16_t -> (u)int* + case EOpConvUint16ToInt8: out.debug << "Convert uint16_t to int8_t";break; + case EOpConvUint16ToInt16: out.debug << "Convert uint16_t to int16_t";break; + case EOpConvUint16ToInt: out.debug << "Convert uint16_t to int"; break; + case EOpConvUint16ToInt64: out.debug << "Convert uint16_t to int64"; break; + case EOpConvUint16ToUint8: out.debug << "Convert uint16_t to uint8_t";break; + case EOpConvUint16ToUint: out.debug << "Convert uint16_t to uint"; break; + case EOpConvUint16ToUint64: out.debug << "Convert uint16_t to uint64"; break; + + // uint16_t -> float* + case EOpConvUint16ToFloat16: out.debug << "Convert uint16_t to float16_t";break; + case EOpConvUint16ToFloat: out.debug << "Convert uint16_t to float"; break; + case EOpConvUint16ToDouble: out.debug << "Convert uint16_t to double"; break; + + // int32_t -> (u)int* + case EOpConvIntToInt8: out.debug << "Convert int to int8_t";break; + case EOpConvIntToInt16: out.debug << "Convert int to int16_t";break; + case EOpConvIntToInt64: out.debug << "Convert int to int64"; break; + case EOpConvIntToUint8: out.debug << "Convert int to uint8_t";break; + case EOpConvIntToUint16: out.debug << "Convert int to uint16_t";break; + case EOpConvIntToUint: out.debug << "Convert int to uint"; break; + case EOpConvIntToUint64: out.debug << "Convert int to uint64"; break; + + // int32_t -> float* + case EOpConvIntToFloat16: out.debug << "Convert int to float16_t";break; + case EOpConvIntToFloat: out.debug << "Convert int to float"; break; + case EOpConvIntToDouble: out.debug << "Convert int to double"; break; + + // uint32_t -> (u)int* + case EOpConvUintToInt8: out.debug << "Convert uint to int8_t";break; + case EOpConvUintToInt16: out.debug << "Convert uint to int16_t";break; + case EOpConvUintToInt: out.debug << "Convert uint to int";break; + case EOpConvUintToInt64: out.debug << "Convert uint to int64"; break; + case EOpConvUintToUint8: out.debug << "Convert uint to uint8_t";break; + case EOpConvUintToUint16: out.debug << "Convert uint to uint16_t";break; + case EOpConvUintToUint64: out.debug << "Convert uint to uint64"; break; + + // uint32_t -> float* + case EOpConvUintToFloat16: out.debug << "Convert uint to float16_t";break; + case EOpConvUintToFloat: out.debug << "Convert uint to float"; break; + case EOpConvUintToDouble: out.debug << "Convert uint to double"; break; + + // int64 -> (u)int* + case EOpConvInt64ToInt8: out.debug << "Convert int64 to int8_t"; break; + case EOpConvInt64ToInt16: out.debug << "Convert int64 to int16_t"; break; + case EOpConvInt64ToInt: out.debug << "Convert int64 to int"; break; + case EOpConvInt64ToUint8: out.debug << "Convert int64 to uint8_t";break; + case EOpConvInt64ToUint16: out.debug << "Convert int64 to uint16_t";break; + case EOpConvInt64ToUint: out.debug << "Convert int64 to uint"; break; + case EOpConvInt64ToUint64: out.debug << "Convert int64 to uint64"; break; + + // int64 -> float* + case EOpConvInt64ToFloat16: out.debug << "Convert int64 to float16_t";break; + case EOpConvInt64ToFloat: out.debug << "Convert int64 to float"; break; + case EOpConvInt64ToDouble: out.debug << "Convert int64 to double"; break; + + // uint64 -> (u)int* + case EOpConvUint64ToInt8: out.debug << "Convert uint64 to int8_t";break; + case EOpConvUint64ToInt16: out.debug << "Convert uint64 to int16_t";break; + case EOpConvUint64ToInt: out.debug << "Convert uint64 to int"; break; + case EOpConvUint64ToInt64: out.debug << "Convert uint64 to int64"; break; + case EOpConvUint64ToUint8: out.debug << "Convert uint64 to uint8_t";break; + case EOpConvUint64ToUint16: out.debug << "Convert uint64 to uint16"; break; + case EOpConvUint64ToUint: out.debug << "Convert uint64 to uint"; break; + + // uint64 -> float* + case EOpConvUint64ToFloat16: out.debug << "Convert uint64 to float16_t";break; + case EOpConvUint64ToFloat: out.debug << "Convert uint64 to float"; break; + case EOpConvUint64ToDouble: out.debug << "Convert uint64 to double"; break; + + // float16_t -> int* + case EOpConvFloat16ToInt8: out.debug << "Convert float16_t to int8_t"; break; + case EOpConvFloat16ToInt16: out.debug << "Convert float16_t to int16_t"; break; + case EOpConvFloat16ToInt: out.debug << "Convert float16_t to int"; break; + case EOpConvFloat16ToInt64: out.debug << "Convert float16_t to int64"; break; + + // float16_t -> uint* + case EOpConvFloat16ToUint8: out.debug << "Convert float16_t to uint8_t"; break; + case EOpConvFloat16ToUint16: out.debug << "Convert float16_t to uint16_t"; break; + case EOpConvFloat16ToUint: out.debug << "Convert float16_t to uint"; break; + case EOpConvFloat16ToUint64: out.debug << "Convert float16_t to uint64"; break; + + // float16_t -> float* + case EOpConvFloat16ToFloat: out.debug << "Convert float16_t to float"; break; + case EOpConvFloat16ToDouble: out.debug << "Convert float16_t to double"; break; + + // float32 -> float* + case EOpConvFloatToFloat16: out.debug << "Convert float to float16_t"; break; + case EOpConvFloatToDouble: out.debug << "Convert float to double"; break; + + // float32_t -> int* + case EOpConvFloatToInt8: out.debug << "Convert float to int8_t"; break; + case EOpConvFloatToInt16: out.debug << "Convert float to int16_t"; break; + case EOpConvFloatToInt: out.debug << "Convert float to int"; break; + case EOpConvFloatToInt64: out.debug << "Convert float to int64"; break; + + // float32_t -> uint* + case EOpConvFloatToUint8: out.debug << "Convert float to uint8_t"; break; + case EOpConvFloatToUint16: out.debug << "Convert float to uint16_t"; break; + case EOpConvFloatToUint: out.debug << "Convert float to uint"; break; + case EOpConvFloatToUint64: out.debug << "Convert float to uint64"; break; + + // double -> float* + case EOpConvDoubleToFloat16: out.debug << "Convert double to float16_t"; break; + case EOpConvDoubleToFloat: out.debug << "Convert double to float"; break; + + // double -> int* + case EOpConvDoubleToInt8: out.debug << "Convert double to int8_t"; break; + case EOpConvDoubleToInt16: out.debug << "Convert double to int16_t"; break; + case EOpConvDoubleToInt: out.debug << "Convert double to int"; break; + case EOpConvDoubleToInt64: out.debug << "Convert double to int64"; break; + + // float32_t -> uint* + case EOpConvDoubleToUint8: out.debug << "Convert double to uint8_t"; break; + case EOpConvDoubleToUint16: out.debug << "Convert double to uint16_t"; break; + case EOpConvDoubleToUint: out.debug << "Convert double to uint"; break; + case EOpConvDoubleToUint64: out.debug << "Convert double to uint64"; break; + + case EOpConvUint64ToPtr: out.debug << "Convert uint64_t to pointer"; break; + case EOpConvPtrToUint64: out.debug << "Convert pointer to uint64_t"; break; + + case EOpRadians: out.debug << "radians"; break; + case EOpDegrees: out.debug << "degrees"; break; + case EOpSin: out.debug << "sine"; break; + case EOpCos: out.debug << "cosine"; break; + case EOpTan: out.debug << "tangent"; break; + case EOpAsin: out.debug << "arc sine"; break; + case EOpAcos: out.debug << "arc cosine"; break; + case EOpAtan: out.debug << "arc tangent"; break; + case EOpSinh: out.debug << "hyp. sine"; break; + case EOpCosh: out.debug << "hyp. cosine"; break; + case EOpTanh: out.debug << "hyp. tangent"; break; + case EOpAsinh: out.debug << "arc hyp. sine"; break; + case EOpAcosh: out.debug << "arc hyp. cosine"; break; + case EOpAtanh: out.debug << "arc hyp. tangent"; break; + + case EOpExp: out.debug << "exp"; break; + case EOpLog: out.debug << "log"; break; + case EOpExp2: out.debug << "exp2"; break; + case EOpLog2: out.debug << "log2"; break; + case EOpSqrt: out.debug << "sqrt"; break; + case EOpInverseSqrt: out.debug << "inverse sqrt"; break; + + case EOpAbs: out.debug << "Absolute value"; break; + case EOpSign: out.debug << "Sign"; break; + case EOpFloor: out.debug << "Floor"; break; + case EOpTrunc: out.debug << "trunc"; break; + case EOpRound: out.debug << "round"; break; + case EOpRoundEven: out.debug << "roundEven"; break; + case EOpCeil: out.debug << "Ceiling"; break; + case EOpFract: out.debug << "Fraction"; break; + + case EOpIsNan: out.debug << "isnan"; break; + case EOpIsInf: out.debug << "isinf"; break; + + case EOpFloatBitsToInt: out.debug << "floatBitsToInt"; break; + case EOpFloatBitsToUint:out.debug << "floatBitsToUint"; break; + case EOpIntBitsToFloat: out.debug << "intBitsToFloat"; break; + case EOpUintBitsToFloat:out.debug << "uintBitsToFloat"; break; + case EOpDoubleBitsToInt64: out.debug << "doubleBitsToInt64"; break; + case EOpDoubleBitsToUint64: out.debug << "doubleBitsToUint64"; break; + case EOpInt64BitsToDouble: out.debug << "int64BitsToDouble"; break; + case EOpUint64BitsToDouble: out.debug << "uint64BitsToDouble"; break; + case EOpFloat16BitsToInt16: out.debug << "float16BitsToInt16"; break; + case EOpFloat16BitsToUint16: out.debug << "float16BitsToUint16"; break; + case EOpInt16BitsToFloat16: out.debug << "int16BitsToFloat16"; break; + case EOpUint16BitsToFloat16: out.debug << "uint16BitsToFloat16"; break; + + case EOpPackSnorm2x16: out.debug << "packSnorm2x16"; break; + case EOpUnpackSnorm2x16:out.debug << "unpackSnorm2x16"; break; + case EOpPackUnorm2x16: out.debug << "packUnorm2x16"; break; + case EOpUnpackUnorm2x16:out.debug << "unpackUnorm2x16"; break; + case EOpPackHalf2x16: out.debug << "packHalf2x16"; break; + case EOpUnpackHalf2x16: out.debug << "unpackHalf2x16"; break; + case EOpPack16: out.debug << "pack16"; break; + case EOpPack32: out.debug << "pack32"; break; + case EOpPack64: out.debug << "pack64"; break; + case EOpUnpack32: out.debug << "unpack32"; break; + case EOpUnpack16: out.debug << "unpack16"; break; + case EOpUnpack8: out.debug << "unpack8"; break; + + case EOpPackSnorm4x8: out.debug << "PackSnorm4x8"; break; + case EOpUnpackSnorm4x8: out.debug << "UnpackSnorm4x8"; break; + case EOpPackUnorm4x8: out.debug << "PackUnorm4x8"; break; + case EOpUnpackUnorm4x8: out.debug << "UnpackUnorm4x8"; break; + case EOpPackDouble2x32: out.debug << "PackDouble2x32"; break; + case EOpUnpackDouble2x32: out.debug << "UnpackDouble2x32"; break; + + case EOpPackInt2x32: out.debug << "packInt2x32"; break; + case EOpUnpackInt2x32: out.debug << "unpackInt2x32"; break; + case EOpPackUint2x32: out.debug << "packUint2x32"; break; + case EOpUnpackUint2x32: out.debug << "unpackUint2x32"; break; + + case EOpPackInt2x16: out.debug << "packInt2x16"; break; + case EOpUnpackInt2x16: out.debug << "unpackInt2x16"; break; + case EOpPackUint2x16: out.debug << "packUint2x16"; break; + case EOpUnpackUint2x16: out.debug << "unpackUint2x16"; break; + + case EOpPackInt4x16: out.debug << "packInt4x16"; break; + case EOpUnpackInt4x16: out.debug << "unpackInt4x16"; break; + case EOpPackUint4x16: out.debug << "packUint4x16"; break; + case EOpUnpackUint4x16: out.debug << "unpackUint4x16"; break; + case EOpPackFloat2x16: out.debug << "packFloat2x16"; break; + case EOpUnpackFloat2x16: out.debug << "unpackFloat2x16"; break; + + case EOpLength: out.debug << "length"; break; + case EOpNormalize: out.debug << "normalize"; break; + case EOpDPdx: out.debug << "dPdx"; break; + case EOpDPdy: out.debug << "dPdy"; break; + case EOpFwidth: out.debug << "fwidth"; break; + case EOpDPdxFine: out.debug << "dPdxFine"; break; + case EOpDPdyFine: out.debug << "dPdyFine"; break; + case EOpFwidthFine: out.debug << "fwidthFine"; break; + case EOpDPdxCoarse: out.debug << "dPdxCoarse"; break; + case EOpDPdyCoarse: out.debug << "dPdyCoarse"; break; + case EOpFwidthCoarse: out.debug << "fwidthCoarse"; break; + + case EOpInterpolateAtCentroid: out.debug << "interpolateAtCentroid"; break; + + case EOpDeterminant: out.debug << "determinant"; break; + case EOpMatrixInverse: out.debug << "inverse"; break; + case EOpTranspose: out.debug << "transpose"; break; + + case EOpAny: out.debug << "any"; break; + case EOpAll: out.debug << "all"; break; + + case EOpArrayLength: out.debug << "array length"; break; + + case EOpEmitStreamVertex: out.debug << "EmitStreamVertex"; break; + case EOpEndStreamPrimitive: out.debug << "EndStreamPrimitive"; break; + + case EOpAtomicCounterIncrement: out.debug << "AtomicCounterIncrement";break; + case EOpAtomicCounterDecrement: out.debug << "AtomicCounterDecrement";break; + case EOpAtomicCounter: out.debug << "AtomicCounter"; break; + + case EOpTextureQuerySize: out.debug << "textureSize"; break; + case EOpTextureQueryLod: out.debug << "textureQueryLod"; break; + case EOpTextureQueryLevels: out.debug << "textureQueryLevels"; break; + case EOpTextureQuerySamples: out.debug << "textureSamples"; break; + case EOpImageQuerySize: out.debug << "imageQuerySize"; break; + case EOpImageQuerySamples: out.debug << "imageQuerySamples"; break; + case EOpImageLoad: out.debug << "imageLoad"; break; + + case EOpBitFieldReverse: out.debug << "bitFieldReverse"; break; + case EOpBitCount: out.debug << "bitCount"; break; + case EOpFindLSB: out.debug << "findLSB"; break; + case EOpFindMSB: out.debug << "findMSB"; break; + + case EOpCountLeadingZeros: out.debug << "countLeadingZeros"; break; + case EOpCountTrailingZeros: out.debug << "countTrailingZeros"; break; + + case EOpNoise: out.debug << "noise"; break; + + case EOpBallot: out.debug << "ballot"; break; + case EOpReadFirstInvocation: out.debug << "readFirstInvocation"; break; + + case EOpAnyInvocation: out.debug << "anyInvocation"; break; + case EOpAllInvocations: out.debug << "allInvocations"; break; + case EOpAllInvocationsEqual: out.debug << "allInvocationsEqual"; break; + + case EOpSubgroupElect: out.debug << "subgroupElect"; break; + case EOpSubgroupAll: out.debug << "subgroupAll"; break; + case EOpSubgroupAny: out.debug << "subgroupAny"; break; + case EOpSubgroupAllEqual: out.debug << "subgroupAllEqual"; break; + case EOpSubgroupBroadcast: out.debug << "subgroupBroadcast"; break; + case EOpSubgroupBroadcastFirst: out.debug << "subgroupBroadcastFirst"; break; + case EOpSubgroupBallot: out.debug << "subgroupBallot"; break; + case EOpSubgroupInverseBallot: out.debug << "subgroupInverseBallot"; break; + case EOpSubgroupBallotBitExtract: out.debug << "subgroupBallotBitExtract"; break; + case EOpSubgroupBallotBitCount: out.debug << "subgroupBallotBitCount"; break; + case EOpSubgroupBallotInclusiveBitCount: out.debug << "subgroupBallotInclusiveBitCount"; break; + case EOpSubgroupBallotExclusiveBitCount: out.debug << "subgroupBallotExclusiveBitCount"; break; + case EOpSubgroupBallotFindLSB: out.debug << "subgroupBallotFindLSB"; break; + case EOpSubgroupBallotFindMSB: out.debug << "subgroupBallotFindMSB"; break; + case EOpSubgroupShuffle: out.debug << "subgroupShuffle"; break; + case EOpSubgroupShuffleXor: out.debug << "subgroupShuffleXor"; break; + case EOpSubgroupShuffleUp: out.debug << "subgroupShuffleUp"; break; + case EOpSubgroupShuffleDown: out.debug << "subgroupShuffleDown"; break; + case EOpSubgroupAdd: out.debug << "subgroupAdd"; break; + case EOpSubgroupMul: out.debug << "subgroupMul"; break; + case EOpSubgroupMin: out.debug << "subgroupMin"; break; + case EOpSubgroupMax: out.debug << "subgroupMax"; break; + case EOpSubgroupAnd: out.debug << "subgroupAnd"; break; + case EOpSubgroupOr: out.debug << "subgroupOr"; break; + case EOpSubgroupXor: out.debug << "subgroupXor"; break; + case EOpSubgroupInclusiveAdd: out.debug << "subgroupInclusiveAdd"; break; + case EOpSubgroupInclusiveMul: out.debug << "subgroupInclusiveMul"; break; + case EOpSubgroupInclusiveMin: out.debug << "subgroupInclusiveMin"; break; + case EOpSubgroupInclusiveMax: out.debug << "subgroupInclusiveMax"; break; + case EOpSubgroupInclusiveAnd: out.debug << "subgroupInclusiveAnd"; break; + case EOpSubgroupInclusiveOr: out.debug << "subgroupInclusiveOr"; break; + case EOpSubgroupInclusiveXor: out.debug << "subgroupInclusiveXor"; break; + case EOpSubgroupExclusiveAdd: out.debug << "subgroupExclusiveAdd"; break; + case EOpSubgroupExclusiveMul: out.debug << "subgroupExclusiveMul"; break; + case EOpSubgroupExclusiveMin: out.debug << "subgroupExclusiveMin"; break; + case EOpSubgroupExclusiveMax: out.debug << "subgroupExclusiveMax"; break; + case EOpSubgroupExclusiveAnd: out.debug << "subgroupExclusiveAnd"; break; + case EOpSubgroupExclusiveOr: out.debug << "subgroupExclusiveOr"; break; + case EOpSubgroupExclusiveXor: out.debug << "subgroupExclusiveXor"; break; + case EOpSubgroupClusteredAdd: out.debug << "subgroupClusteredAdd"; break; + case EOpSubgroupClusteredMul: out.debug << "subgroupClusteredMul"; break; + case EOpSubgroupClusteredMin: out.debug << "subgroupClusteredMin"; break; + case EOpSubgroupClusteredMax: out.debug << "subgroupClusteredMax"; break; + case EOpSubgroupClusteredAnd: out.debug << "subgroupClusteredAnd"; break; + case EOpSubgroupClusteredOr: out.debug << "subgroupClusteredOr"; break; + case EOpSubgroupClusteredXor: out.debug << "subgroupClusteredXor"; break; + case EOpSubgroupQuadBroadcast: out.debug << "subgroupQuadBroadcast"; break; + case EOpSubgroupQuadSwapHorizontal: out.debug << "subgroupQuadSwapHorizontal"; break; + case EOpSubgroupQuadSwapVertical: out.debug << "subgroupQuadSwapVertical"; break; + case EOpSubgroupQuadSwapDiagonal: out.debug << "subgroupQuadSwapDiagonal"; break; + + case EOpSubgroupPartition: out.debug << "subgroupPartitionNV"; break; + case EOpSubgroupPartitionedAdd: out.debug << "subgroupPartitionedAddNV"; break; + case EOpSubgroupPartitionedMul: out.debug << "subgroupPartitionedMulNV"; break; + case EOpSubgroupPartitionedMin: out.debug << "subgroupPartitionedMinNV"; break; + case EOpSubgroupPartitionedMax: out.debug << "subgroupPartitionedMaxNV"; break; + case EOpSubgroupPartitionedAnd: out.debug << "subgroupPartitionedAndNV"; break; + case EOpSubgroupPartitionedOr: out.debug << "subgroupPartitionedOrNV"; break; + case EOpSubgroupPartitionedXor: out.debug << "subgroupPartitionedXorNV"; break; + case EOpSubgroupPartitionedInclusiveAdd: out.debug << "subgroupPartitionedInclusiveAddNV"; break; + case EOpSubgroupPartitionedInclusiveMul: out.debug << "subgroupPartitionedInclusiveMulNV"; break; + case EOpSubgroupPartitionedInclusiveMin: out.debug << "subgroupPartitionedInclusiveMinNV"; break; + case EOpSubgroupPartitionedInclusiveMax: out.debug << "subgroupPartitionedInclusiveMaxNV"; break; + case EOpSubgroupPartitionedInclusiveAnd: out.debug << "subgroupPartitionedInclusiveAndNV"; break; + case EOpSubgroupPartitionedInclusiveOr: out.debug << "subgroupPartitionedInclusiveOrNV"; break; + case EOpSubgroupPartitionedInclusiveXor: out.debug << "subgroupPartitionedInclusiveXorNV"; break; + case EOpSubgroupPartitionedExclusiveAdd: out.debug << "subgroupPartitionedExclusiveAddNV"; break; + case EOpSubgroupPartitionedExclusiveMul: out.debug << "subgroupPartitionedExclusiveMulNV"; break; + case EOpSubgroupPartitionedExclusiveMin: out.debug << "subgroupPartitionedExclusiveMinNV"; break; + case EOpSubgroupPartitionedExclusiveMax: out.debug << "subgroupPartitionedExclusiveMaxNV"; break; + case EOpSubgroupPartitionedExclusiveAnd: out.debug << "subgroupPartitionedExclusiveAndNV"; break; + case EOpSubgroupPartitionedExclusiveOr: out.debug << "subgroupPartitionedExclusiveOrNV"; break; + case EOpSubgroupPartitionedExclusiveXor: out.debug << "subgroupPartitionedExclusiveXorNV"; break; + + case EOpClip: out.debug << "clip"; break; + case EOpIsFinite: out.debug << "isfinite"; break; + case EOpLog10: out.debug << "log10"; break; + case EOpRcp: out.debug << "rcp"; break; + case EOpSaturate: out.debug << "saturate"; break; + + case EOpSparseTexelsResident: out.debug << "sparseTexelsResident"; break; + + case EOpMinInvocations: out.debug << "minInvocations"; break; + case EOpMaxInvocations: out.debug << "maxInvocations"; break; + case EOpAddInvocations: out.debug << "addInvocations"; break; + case EOpMinInvocationsNonUniform: out.debug << "minInvocationsNonUniform"; break; + case EOpMaxInvocationsNonUniform: out.debug << "maxInvocationsNonUniform"; break; + case EOpAddInvocationsNonUniform: out.debug << "addInvocationsNonUniform"; break; + + case EOpMinInvocationsInclusiveScan: out.debug << "minInvocationsInclusiveScan"; break; + case EOpMaxInvocationsInclusiveScan: out.debug << "maxInvocationsInclusiveScan"; break; + case EOpAddInvocationsInclusiveScan: out.debug << "addInvocationsInclusiveScan"; break; + case EOpMinInvocationsInclusiveScanNonUniform: out.debug << "minInvocationsInclusiveScanNonUniform"; break; + case EOpMaxInvocationsInclusiveScanNonUniform: out.debug << "maxInvocationsInclusiveScanNonUniform"; break; + case EOpAddInvocationsInclusiveScanNonUniform: out.debug << "addInvocationsInclusiveScanNonUniform"; break; + + case EOpMinInvocationsExclusiveScan: out.debug << "minInvocationsExclusiveScan"; break; + case EOpMaxInvocationsExclusiveScan: out.debug << "maxInvocationsExclusiveScan"; break; + case EOpAddInvocationsExclusiveScan: out.debug << "addInvocationsExclusiveScan"; break; + case EOpMinInvocationsExclusiveScanNonUniform: out.debug << "minInvocationsExclusiveScanNonUniform"; break; + case EOpMaxInvocationsExclusiveScanNonUniform: out.debug << "maxInvocationsExclusiveScanNonUniform"; break; + case EOpAddInvocationsExclusiveScanNonUniform: out.debug << "addInvocationsExclusiveScanNonUniform"; break; + + case EOpMbcnt: out.debug << "mbcnt"; break; + + case EOpFragmentMaskFetch: out.debug << "fragmentMaskFetchAMD"; break; + case EOpFragmentFetch: out.debug << "fragmentFetchAMD"; break; + + case EOpCubeFaceIndex: out.debug << "cubeFaceIndex"; break; + case EOpCubeFaceCoord: out.debug << "cubeFaceCoord"; break; + + case EOpSubpassLoad: out.debug << "subpassLoad"; break; + case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break; + + case EOpConstructReference: out.debug << "Construct reference type"; break; + + default: out.debug.message(EPrefixError, "Bad unary op"); + } + + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + TInfoSink& out = infoSink; + + if (node->getOp() == EOpNull) { + out.debug.message(EPrefixError, "node is still EOpNull!"); + return true; + } + + OutputTreeText(out, node, depth); + + switch (node->getOp()) { + case EOpSequence: out.debug << "Sequence\n"; return true; + case EOpLinkerObjects: out.debug << "Linker Objects\n"; return true; + case EOpComma: out.debug << "Comma"; break; + case EOpFunction: out.debug << "Function Definition: " << node->getName(); break; + case EOpFunctionCall: out.debug << "Function Call: " << node->getName(); break; + case EOpParameters: out.debug << "Function Parameters: "; break; + + case EOpConstructFloat: out.debug << "Construct float"; break; + case EOpConstructDouble:out.debug << "Construct double"; break; + + case EOpConstructVec2: out.debug << "Construct vec2"; break; + case EOpConstructVec3: out.debug << "Construct vec3"; break; + case EOpConstructVec4: out.debug << "Construct vec4"; break; + case EOpConstructDVec2: out.debug << "Construct dvec2"; break; + case EOpConstructDVec3: out.debug << "Construct dvec3"; break; + case EOpConstructDVec4: out.debug << "Construct dvec4"; break; + case EOpConstructBool: out.debug << "Construct bool"; break; + case EOpConstructBVec2: out.debug << "Construct bvec2"; break; + case EOpConstructBVec3: out.debug << "Construct bvec3"; break; + case EOpConstructBVec4: out.debug << "Construct bvec4"; break; + case EOpConstructInt8: out.debug << "Construct int8_t"; break; + case EOpConstructI8Vec2: out.debug << "Construct i8vec2"; break; + case EOpConstructI8Vec3: out.debug << "Construct i8vec3"; break; + case EOpConstructI8Vec4: out.debug << "Construct i8vec4"; break; + case EOpConstructInt: out.debug << "Construct int"; break; + case EOpConstructIVec2: out.debug << "Construct ivec2"; break; + case EOpConstructIVec3: out.debug << "Construct ivec3"; break; + case EOpConstructIVec4: out.debug << "Construct ivec4"; break; + case EOpConstructUint8: out.debug << "Construct uint8_t"; break; + case EOpConstructU8Vec2: out.debug << "Construct u8vec2"; break; + case EOpConstructU8Vec3: out.debug << "Construct u8vec3"; break; + case EOpConstructU8Vec4: out.debug << "Construct u8vec4"; break; + case EOpConstructUint: out.debug << "Construct uint"; break; + case EOpConstructUVec2: out.debug << "Construct uvec2"; break; + case EOpConstructUVec3: out.debug << "Construct uvec3"; break; + case EOpConstructUVec4: out.debug << "Construct uvec4"; break; + case EOpConstructInt64: out.debug << "Construct int64"; break; + case EOpConstructI64Vec2: out.debug << "Construct i64vec2"; break; + case EOpConstructI64Vec3: out.debug << "Construct i64vec3"; break; + case EOpConstructI64Vec4: out.debug << "Construct i64vec4"; break; + case EOpConstructUint64: out.debug << "Construct uint64"; break; + case EOpConstructU64Vec2: out.debug << "Construct u64vec2"; break; + case EOpConstructU64Vec3: out.debug << "Construct u64vec3"; break; + case EOpConstructU64Vec4: out.debug << "Construct u64vec4"; break; + case EOpConstructInt16: out.debug << "Construct int16_t"; break; + case EOpConstructI16Vec2: out.debug << "Construct i16vec2"; break; + case EOpConstructI16Vec3: out.debug << "Construct i16vec3"; break; + case EOpConstructI16Vec4: out.debug << "Construct i16vec4"; break; + case EOpConstructUint16: out.debug << "Construct uint16_t"; break; + case EOpConstructU16Vec2: out.debug << "Construct u16vec2"; break; + case EOpConstructU16Vec3: out.debug << "Construct u16vec3"; break; + case EOpConstructU16Vec4: out.debug << "Construct u16vec4"; break; + case EOpConstructMat2x2: out.debug << "Construct mat2"; break; + case EOpConstructMat2x3: out.debug << "Construct mat2x3"; break; + case EOpConstructMat2x4: out.debug << "Construct mat2x4"; break; + case EOpConstructMat3x2: out.debug << "Construct mat3x2"; break; + case EOpConstructMat3x3: out.debug << "Construct mat3"; break; + case EOpConstructMat3x4: out.debug << "Construct mat3x4"; break; + case EOpConstructMat4x2: out.debug << "Construct mat4x2"; break; + case EOpConstructMat4x3: out.debug << "Construct mat4x3"; break; + case EOpConstructMat4x4: out.debug << "Construct mat4"; break; + case EOpConstructDMat2x2: out.debug << "Construct dmat2"; break; + case EOpConstructDMat2x3: out.debug << "Construct dmat2x3"; break; + case EOpConstructDMat2x4: out.debug << "Construct dmat2x4"; break; + case EOpConstructDMat3x2: out.debug << "Construct dmat3x2"; break; + case EOpConstructDMat3x3: out.debug << "Construct dmat3"; break; + case EOpConstructDMat3x4: out.debug << "Construct dmat3x4"; break; + case EOpConstructDMat4x2: out.debug << "Construct dmat4x2"; break; + case EOpConstructDMat4x3: out.debug << "Construct dmat4x3"; break; + case EOpConstructDMat4x4: out.debug << "Construct dmat4"; break; + case EOpConstructIMat2x2: out.debug << "Construct imat2"; break; + case EOpConstructIMat2x3: out.debug << "Construct imat2x3"; break; + case EOpConstructIMat2x4: out.debug << "Construct imat2x4"; break; + case EOpConstructIMat3x2: out.debug << "Construct imat3x2"; break; + case EOpConstructIMat3x3: out.debug << "Construct imat3"; break; + case EOpConstructIMat3x4: out.debug << "Construct imat3x4"; break; + case EOpConstructIMat4x2: out.debug << "Construct imat4x2"; break; + case EOpConstructIMat4x3: out.debug << "Construct imat4x3"; break; + case EOpConstructIMat4x4: out.debug << "Construct imat4"; break; + case EOpConstructUMat2x2: out.debug << "Construct umat2"; break; + case EOpConstructUMat2x3: out.debug << "Construct umat2x3"; break; + case EOpConstructUMat2x4: out.debug << "Construct umat2x4"; break; + case EOpConstructUMat3x2: out.debug << "Construct umat3x2"; break; + case EOpConstructUMat3x3: out.debug << "Construct umat3"; break; + case EOpConstructUMat3x4: out.debug << "Construct umat3x4"; break; + case EOpConstructUMat4x2: out.debug << "Construct umat4x2"; break; + case EOpConstructUMat4x3: out.debug << "Construct umat4x3"; break; + case EOpConstructUMat4x4: out.debug << "Construct umat4"; break; + case EOpConstructBMat2x2: out.debug << "Construct bmat2"; break; + case EOpConstructBMat2x3: out.debug << "Construct bmat2x3"; break; + case EOpConstructBMat2x4: out.debug << "Construct bmat2x4"; break; + case EOpConstructBMat3x2: out.debug << "Construct bmat3x2"; break; + case EOpConstructBMat3x3: out.debug << "Construct bmat3"; break; + case EOpConstructBMat3x4: out.debug << "Construct bmat3x4"; break; + case EOpConstructBMat4x2: out.debug << "Construct bmat4x2"; break; + case EOpConstructBMat4x3: out.debug << "Construct bmat4x3"; break; + case EOpConstructBMat4x4: out.debug << "Construct bmat4"; break; + case EOpConstructFloat16: out.debug << "Construct float16_t"; break; + case EOpConstructF16Vec2: out.debug << "Construct f16vec2"; break; + case EOpConstructF16Vec3: out.debug << "Construct f16vec3"; break; + case EOpConstructF16Vec4: out.debug << "Construct f16vec4"; break; + case EOpConstructF16Mat2x2: out.debug << "Construct f16mat2"; break; + case EOpConstructF16Mat2x3: out.debug << "Construct f16mat2x3"; break; + case EOpConstructF16Mat2x4: out.debug << "Construct f16mat2x4"; break; + case EOpConstructF16Mat3x2: out.debug << "Construct f16mat3x2"; break; + case EOpConstructF16Mat3x3: out.debug << "Construct f16mat3"; break; + case EOpConstructF16Mat3x4: out.debug << "Construct f16mat3x4"; break; + case EOpConstructF16Mat4x2: out.debug << "Construct f16mat4x2"; break; + case EOpConstructF16Mat4x3: out.debug << "Construct f16mat4x3"; break; + case EOpConstructF16Mat4x4: out.debug << "Construct f16mat4"; break; + case EOpConstructStruct: out.debug << "Construct structure"; break; + case EOpConstructTextureSampler: out.debug << "Construct combined texture-sampler"; break; + case EOpConstructReference: out.debug << "Construct reference"; break; + case EOpConstructCooperativeMatrix: out.debug << "Construct cooperative matrix"; break; + + case EOpLessThan: out.debug << "Compare Less Than"; break; + case EOpGreaterThan: out.debug << "Compare Greater Than"; break; + case EOpLessThanEqual: out.debug << "Compare Less Than or Equal"; break; + case EOpGreaterThanEqual: out.debug << "Compare Greater Than or Equal"; break; + case EOpVectorEqual: out.debug << "Equal"; break; + case EOpVectorNotEqual: out.debug << "NotEqual"; break; + + case EOpMod: out.debug << "mod"; break; + case EOpModf: out.debug << "modf"; break; + case EOpPow: out.debug << "pow"; break; + + case EOpAtan: out.debug << "arc tangent"; break; + + case EOpMin: out.debug << "min"; break; + case EOpMax: out.debug << "max"; break; + case EOpClamp: out.debug << "clamp"; break; + case EOpMix: out.debug << "mix"; break; + case EOpStep: out.debug << "step"; break; + case EOpSmoothStep: out.debug << "smoothstep"; break; + + case EOpDistance: out.debug << "distance"; break; + case EOpDot: out.debug << "dot-product"; break; + case EOpCross: out.debug << "cross-product"; break; + case EOpFaceForward: out.debug << "face-forward"; break; + case EOpReflect: out.debug << "reflect"; break; + case EOpRefract: out.debug << "refract"; break; + case EOpMul: out.debug << "component-wise multiply"; break; + case EOpOuterProduct: out.debug << "outer product"; break; + + case EOpEmitVertex: out.debug << "EmitVertex"; break; + case EOpEndPrimitive: out.debug << "EndPrimitive"; break; + + case EOpBarrier: out.debug << "Barrier"; break; + case EOpMemoryBarrier: out.debug << "MemoryBarrier"; break; + case EOpMemoryBarrierAtomicCounter: out.debug << "MemoryBarrierAtomicCounter"; break; + case EOpMemoryBarrierBuffer: out.debug << "MemoryBarrierBuffer"; break; + case EOpMemoryBarrierImage: out.debug << "MemoryBarrierImage"; break; + case EOpMemoryBarrierShared: out.debug << "MemoryBarrierShared"; break; + case EOpGroupMemoryBarrier: out.debug << "GroupMemoryBarrier"; break; + + case EOpReadInvocation: out.debug << "readInvocation"; break; + + case EOpSwizzleInvocations: out.debug << "swizzleInvocations"; break; + case EOpSwizzleInvocationsMasked: out.debug << "swizzleInvocationsMasked"; break; + case EOpWriteInvocation: out.debug << "writeInvocation"; break; + + case EOpMin3: out.debug << "min3"; break; + case EOpMax3: out.debug << "max3"; break; + case EOpMid3: out.debug << "mid3"; break; + case EOpTime: out.debug << "time"; break; + + case EOpAtomicAdd: out.debug << "AtomicAdd"; break; + case EOpAtomicMin: out.debug << "AtomicMin"; break; + case EOpAtomicMax: out.debug << "AtomicMax"; break; + case EOpAtomicAnd: out.debug << "AtomicAnd"; break; + case EOpAtomicOr: out.debug << "AtomicOr"; break; + case EOpAtomicXor: out.debug << "AtomicXor"; break; + case EOpAtomicExchange: out.debug << "AtomicExchange"; break; + case EOpAtomicCompSwap: out.debug << "AtomicCompSwap"; break; + case EOpAtomicLoad: out.debug << "AtomicLoad"; break; + case EOpAtomicStore: out.debug << "AtomicStore"; break; + + case EOpAtomicCounterAdd: out.debug << "AtomicCounterAdd"; break; + case EOpAtomicCounterSubtract: out.debug << "AtomicCounterSubtract"; break; + case EOpAtomicCounterMin: out.debug << "AtomicCounterMin"; break; + case EOpAtomicCounterMax: out.debug << "AtomicCounterMax"; break; + case EOpAtomicCounterAnd: out.debug << "AtomicCounterAnd"; break; + case EOpAtomicCounterOr: out.debug << "AtomicCounterOr"; break; + case EOpAtomicCounterXor: out.debug << "AtomicCounterXor"; break; + case EOpAtomicCounterExchange: out.debug << "AtomicCounterExchange"; break; + case EOpAtomicCounterCompSwap: out.debug << "AtomicCounterCompSwap"; break; + + case EOpImageQuerySize: out.debug << "imageQuerySize"; break; + case EOpImageQuerySamples: out.debug << "imageQuerySamples"; break; + case EOpImageLoad: out.debug << "imageLoad"; break; + case EOpImageStore: out.debug << "imageStore"; break; + case EOpImageAtomicAdd: out.debug << "imageAtomicAdd"; break; + case EOpImageAtomicMin: out.debug << "imageAtomicMin"; break; + case EOpImageAtomicMax: out.debug << "imageAtomicMax"; break; + case EOpImageAtomicAnd: out.debug << "imageAtomicAnd"; break; + case EOpImageAtomicOr: out.debug << "imageAtomicOr"; break; + case EOpImageAtomicXor: out.debug << "imageAtomicXor"; break; + case EOpImageAtomicExchange: out.debug << "imageAtomicExchange"; break; + case EOpImageAtomicCompSwap: out.debug << "imageAtomicCompSwap"; break; + case EOpImageAtomicLoad: out.debug << "imageAtomicLoad"; break; + case EOpImageAtomicStore: out.debug << "imageAtomicStore"; break; + case EOpImageLoadLod: out.debug << "imageLoadLod"; break; + case EOpImageStoreLod: out.debug << "imageStoreLod"; break; + + case EOpTextureQuerySize: out.debug << "textureSize"; break; + case EOpTextureQueryLod: out.debug << "textureQueryLod"; break; + case EOpTextureQueryLevels: out.debug << "textureQueryLevels"; break; + case EOpTextureQuerySamples: out.debug << "textureSamples"; break; + case EOpTexture: out.debug << "texture"; break; + case EOpTextureProj: out.debug << "textureProj"; break; + case EOpTextureLod: out.debug << "textureLod"; break; + case EOpTextureOffset: out.debug << "textureOffset"; break; + case EOpTextureFetch: out.debug << "textureFetch"; break; + case EOpTextureFetchOffset: out.debug << "textureFetchOffset"; break; + case EOpTextureProjOffset: out.debug << "textureProjOffset"; break; + case EOpTextureLodOffset: out.debug << "textureLodOffset"; break; + case EOpTextureProjLod: out.debug << "textureProjLod"; break; + case EOpTextureProjLodOffset: out.debug << "textureProjLodOffset"; break; + case EOpTextureGrad: out.debug << "textureGrad"; break; + case EOpTextureGradOffset: out.debug << "textureGradOffset"; break; + case EOpTextureProjGrad: out.debug << "textureProjGrad"; break; + case EOpTextureProjGradOffset: out.debug << "textureProjGradOffset"; break; + case EOpTextureGather: out.debug << "textureGather"; break; + case EOpTextureGatherOffset: out.debug << "textureGatherOffset"; break; + case EOpTextureGatherOffsets: out.debug << "textureGatherOffsets"; break; + case EOpTextureClamp: out.debug << "textureClamp"; break; + case EOpTextureOffsetClamp: out.debug << "textureOffsetClamp"; break; + case EOpTextureGradClamp: out.debug << "textureGradClamp"; break; + case EOpTextureGradOffsetClamp: out.debug << "textureGradOffsetClamp"; break; + case EOpTextureGatherLod: out.debug << "textureGatherLod"; break; + case EOpTextureGatherLodOffset: out.debug << "textureGatherLodOffset"; break; + case EOpTextureGatherLodOffsets: out.debug << "textureGatherLodOffsets"; break; + + case EOpSparseTexture: out.debug << "sparseTexture"; break; + case EOpSparseTextureOffset: out.debug << "sparseTextureOffset"; break; + case EOpSparseTextureLod: out.debug << "sparseTextureLod"; break; + case EOpSparseTextureLodOffset: out.debug << "sparseTextureLodOffset"; break; + case EOpSparseTextureFetch: out.debug << "sparseTexelFetch"; break; + case EOpSparseTextureFetchOffset: out.debug << "sparseTexelFetchOffset"; break; + case EOpSparseTextureGrad: out.debug << "sparseTextureGrad"; break; + case EOpSparseTextureGradOffset: out.debug << "sparseTextureGradOffset"; break; + case EOpSparseTextureGather: out.debug << "sparseTextureGather"; break; + case EOpSparseTextureGatherOffset: out.debug << "sparseTextureGatherOffset"; break; + case EOpSparseTextureGatherOffsets: out.debug << "sparseTextureGatherOffsets"; break; + case EOpSparseImageLoad: out.debug << "sparseImageLoad"; break; + case EOpSparseTextureClamp: out.debug << "sparseTextureClamp"; break; + case EOpSparseTextureOffsetClamp: out.debug << "sparseTextureOffsetClamp"; break; + case EOpSparseTextureGradClamp: out.debug << "sparseTextureGradClamp"; break; + case EOpSparseTextureGradOffsetClamp: out.debug << "sparseTextureGradOffsetClam"; break; + case EOpSparseTextureGatherLod: out.debug << "sparseTextureGatherLod"; break; + case EOpSparseTextureGatherLodOffset: out.debug << "sparseTextureGatherLodOffset"; break; + case EOpSparseTextureGatherLodOffsets: out.debug << "sparseTextureGatherLodOffsets"; break; + case EOpSparseImageLoadLod: out.debug << "sparseImageLoadLod"; break; + case EOpImageSampleFootprintNV: out.debug << "imageSampleFootprintNV"; break; + case EOpImageSampleFootprintClampNV: out.debug << "imageSampleFootprintClampNV"; break; + case EOpImageSampleFootprintLodNV: out.debug << "imageSampleFootprintLodNV"; break; + case EOpImageSampleFootprintGradNV: out.debug << "imageSampleFootprintGradNV"; break; + case EOpImageSampleFootprintGradClampNV: out.debug << "mageSampleFootprintGradClampNV"; break; + case EOpAddCarry: out.debug << "addCarry"; break; + case EOpSubBorrow: out.debug << "subBorrow"; break; + case EOpUMulExtended: out.debug << "uMulExtended"; break; + case EOpIMulExtended: out.debug << "iMulExtended"; break; + case EOpBitfieldExtract: out.debug << "bitfieldExtract"; break; + case EOpBitfieldInsert: out.debug << "bitfieldInsert"; break; + + case EOpFma: out.debug << "fma"; break; + case EOpFrexp: out.debug << "frexp"; break; + case EOpLdexp: out.debug << "ldexp"; break; + + case EOpInterpolateAtSample: out.debug << "interpolateAtSample"; break; + case EOpInterpolateAtOffset: out.debug << "interpolateAtOffset"; break; + case EOpInterpolateAtVertex: out.debug << "interpolateAtVertex"; break; + + case EOpSinCos: out.debug << "sincos"; break; + case EOpGenMul: out.debug << "mul"; break; + + case EOpAllMemoryBarrierWithGroupSync: out.debug << "AllMemoryBarrierWithGroupSync"; break; + case EOpDeviceMemoryBarrier: out.debug << "DeviceMemoryBarrier"; break; + case EOpDeviceMemoryBarrierWithGroupSync: out.debug << "DeviceMemoryBarrierWithGroupSync"; break; + case EOpWorkgroupMemoryBarrier: out.debug << "WorkgroupMemoryBarrier"; break; + case EOpWorkgroupMemoryBarrierWithGroupSync: out.debug << "WorkgroupMemoryBarrierWithGroupSync"; break; + + case EOpSubgroupBarrier: out.debug << "subgroupBarrier"; break; + case EOpSubgroupMemoryBarrier: out.debug << "subgroupMemoryBarrier"; break; + case EOpSubgroupMemoryBarrierBuffer: out.debug << "subgroupMemoryBarrierBuffer"; break; + case EOpSubgroupMemoryBarrierImage: out.debug << "subgroupMemoryBarrierImage"; break; + case EOpSubgroupMemoryBarrierShared: out.debug << "subgroupMemoryBarrierShared"; break; + case EOpSubgroupElect: out.debug << "subgroupElect"; break; + case EOpSubgroupAll: out.debug << "subgroupAll"; break; + case EOpSubgroupAny: out.debug << "subgroupAny"; break; + case EOpSubgroupAllEqual: out.debug << "subgroupAllEqual"; break; + case EOpSubgroupBroadcast: out.debug << "subgroupBroadcast"; break; + case EOpSubgroupBroadcastFirst: out.debug << "subgroupBroadcastFirst"; break; + case EOpSubgroupBallot: out.debug << "subgroupBallot"; break; + case EOpSubgroupInverseBallot: out.debug << "subgroupInverseBallot"; break; + case EOpSubgroupBallotBitExtract: out.debug << "subgroupBallotBitExtract"; break; + case EOpSubgroupBallotBitCount: out.debug << "subgroupBallotBitCount"; break; + case EOpSubgroupBallotInclusiveBitCount: out.debug << "subgroupBallotInclusiveBitCount"; break; + case EOpSubgroupBallotExclusiveBitCount: out.debug << "subgroupBallotExclusiveBitCount"; break; + case EOpSubgroupBallotFindLSB: out.debug << "subgroupBallotFindLSB"; break; + case EOpSubgroupBallotFindMSB: out.debug << "subgroupBallotFindMSB"; break; + case EOpSubgroupShuffle: out.debug << "subgroupShuffle"; break; + case EOpSubgroupShuffleXor: out.debug << "subgroupShuffleXor"; break; + case EOpSubgroupShuffleUp: out.debug << "subgroupShuffleUp"; break; + case EOpSubgroupShuffleDown: out.debug << "subgroupShuffleDown"; break; + case EOpSubgroupAdd: out.debug << "subgroupAdd"; break; + case EOpSubgroupMul: out.debug << "subgroupMul"; break; + case EOpSubgroupMin: out.debug << "subgroupMin"; break; + case EOpSubgroupMax: out.debug << "subgroupMax"; break; + case EOpSubgroupAnd: out.debug << "subgroupAnd"; break; + case EOpSubgroupOr: out.debug << "subgroupOr"; break; + case EOpSubgroupXor: out.debug << "subgroupXor"; break; + case EOpSubgroupInclusiveAdd: out.debug << "subgroupInclusiveAdd"; break; + case EOpSubgroupInclusiveMul: out.debug << "subgroupInclusiveMul"; break; + case EOpSubgroupInclusiveMin: out.debug << "subgroupInclusiveMin"; break; + case EOpSubgroupInclusiveMax: out.debug << "subgroupInclusiveMax"; break; + case EOpSubgroupInclusiveAnd: out.debug << "subgroupInclusiveAnd"; break; + case EOpSubgroupInclusiveOr: out.debug << "subgroupInclusiveOr"; break; + case EOpSubgroupInclusiveXor: out.debug << "subgroupInclusiveXor"; break; + case EOpSubgroupExclusiveAdd: out.debug << "subgroupExclusiveAdd"; break; + case EOpSubgroupExclusiveMul: out.debug << "subgroupExclusiveMul"; break; + case EOpSubgroupExclusiveMin: out.debug << "subgroupExclusiveMin"; break; + case EOpSubgroupExclusiveMax: out.debug << "subgroupExclusiveMax"; break; + case EOpSubgroupExclusiveAnd: out.debug << "subgroupExclusiveAnd"; break; + case EOpSubgroupExclusiveOr: out.debug << "subgroupExclusiveOr"; break; + case EOpSubgroupExclusiveXor: out.debug << "subgroupExclusiveXor"; break; + case EOpSubgroupClusteredAdd: out.debug << "subgroupClusteredAdd"; break; + case EOpSubgroupClusteredMul: out.debug << "subgroupClusteredMul"; break; + case EOpSubgroupClusteredMin: out.debug << "subgroupClusteredMin"; break; + case EOpSubgroupClusteredMax: out.debug << "subgroupClusteredMax"; break; + case EOpSubgroupClusteredAnd: out.debug << "subgroupClusteredAnd"; break; + case EOpSubgroupClusteredOr: out.debug << "subgroupClusteredOr"; break; + case EOpSubgroupClusteredXor: out.debug << "subgroupClusteredXor"; break; + case EOpSubgroupQuadBroadcast: out.debug << "subgroupQuadBroadcast"; break; + case EOpSubgroupQuadSwapHorizontal: out.debug << "subgroupQuadSwapHorizontal"; break; + case EOpSubgroupQuadSwapVertical: out.debug << "subgroupQuadSwapVertical"; break; + case EOpSubgroupQuadSwapDiagonal: out.debug << "subgroupQuadSwapDiagonal"; break; + + case EOpSubgroupPartition: out.debug << "subgroupPartitionNV"; break; + case EOpSubgroupPartitionedAdd: out.debug << "subgroupPartitionedAddNV"; break; + case EOpSubgroupPartitionedMul: out.debug << "subgroupPartitionedMulNV"; break; + case EOpSubgroupPartitionedMin: out.debug << "subgroupPartitionedMinNV"; break; + case EOpSubgroupPartitionedMax: out.debug << "subgroupPartitionedMaxNV"; break; + case EOpSubgroupPartitionedAnd: out.debug << "subgroupPartitionedAndNV"; break; + case EOpSubgroupPartitionedOr: out.debug << "subgroupPartitionedOrNV"; break; + case EOpSubgroupPartitionedXor: out.debug << "subgroupPartitionedXorNV"; break; + case EOpSubgroupPartitionedInclusiveAdd: out.debug << "subgroupPartitionedInclusiveAddNV"; break; + case EOpSubgroupPartitionedInclusiveMul: out.debug << "subgroupPartitionedInclusiveMulNV"; break; + case EOpSubgroupPartitionedInclusiveMin: out.debug << "subgroupPartitionedInclusiveMinNV"; break; + case EOpSubgroupPartitionedInclusiveMax: out.debug << "subgroupPartitionedInclusiveMaxNV"; break; + case EOpSubgroupPartitionedInclusiveAnd: out.debug << "subgroupPartitionedInclusiveAndNV"; break; + case EOpSubgroupPartitionedInclusiveOr: out.debug << "subgroupPartitionedInclusiveOrNV"; break; + case EOpSubgroupPartitionedInclusiveXor: out.debug << "subgroupPartitionedInclusiveXorNV"; break; + case EOpSubgroupPartitionedExclusiveAdd: out.debug << "subgroupPartitionedExclusiveAddNV"; break; + case EOpSubgroupPartitionedExclusiveMul: out.debug << "subgroupPartitionedExclusiveMulNV"; break; + case EOpSubgroupPartitionedExclusiveMin: out.debug << "subgroupPartitionedExclusiveMinNV"; break; + case EOpSubgroupPartitionedExclusiveMax: out.debug << "subgroupPartitionedExclusiveMaxNV"; break; + case EOpSubgroupPartitionedExclusiveAnd: out.debug << "subgroupPartitionedExclusiveAndNV"; break; + case EOpSubgroupPartitionedExclusiveOr: out.debug << "subgroupPartitionedExclusiveOrNV"; break; + case EOpSubgroupPartitionedExclusiveXor: out.debug << "subgroupPartitionedExclusiveXorNV"; break; + + case EOpSubpassLoad: out.debug << "subpassLoad"; break; + case EOpSubpassLoadMS: out.debug << "subpassLoadMS"; break; + + case EOpTrace: out.debug << "traceNV"; break; + case EOpReportIntersection: out.debug << "reportIntersectionNV"; break; + case EOpIgnoreIntersection: out.debug << "ignoreIntersectionNV"; break; + case EOpTerminateRay: out.debug << "terminateRayNV"; break; + case EOpExecuteCallable: out.debug << "executeCallableNV"; break; + case EOpWritePackedPrimitiveIndices4x8NV: out.debug << "writePackedPrimitiveIndices4x8NV"; break; + + case EOpRayQueryInitialize: out.debug << "rayQueryInitializeEXT"; break; + case EOpRayQueryTerminate: out.debug << "rayQueryTerminateEXT"; break; + case EOpRayQueryGenerateIntersection: out.debug << "rayQueryGenerateIntersectionEXT"; break; + case EOpRayQueryConfirmIntersection: out.debug << "rayQueryConfirmIntersectionEXT"; break; + case EOpRayQueryProceed: out.debug << "rayQueryProceedEXT"; break; + case EOpRayQueryGetIntersectionType: out.debug << "rayQueryGetIntersectionTypeEXT"; break; + case EOpRayQueryGetRayTMin: out.debug << "rayQueryGetRayTMinEXT"; break; + case EOpRayQueryGetRayFlags: out.debug << "rayQueryGetRayFlagsEXT"; break; + case EOpRayQueryGetIntersectionT: out.debug << "rayQueryGetIntersectionTEXT"; break; + case EOpRayQueryGetIntersectionInstanceCustomIndex: out.debug << "rayQueryGetIntersectionInstanceCustomIndexEXT"; break; + case EOpRayQueryGetIntersectionInstanceId: out.debug << "rayQueryGetIntersectionInstanceIdEXT"; break; + case EOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffset: out.debug << "rayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetEXT"; break; + case EOpRayQueryGetIntersectionGeometryIndex: out.debug << "rayQueryGetIntersectionGeometryIndexEXT"; break; + case EOpRayQueryGetIntersectionPrimitiveIndex: out.debug << "rayQueryGetIntersectionPrimitiveIndexEXT"; break; + case EOpRayQueryGetIntersectionBarycentrics: out.debug << "rayQueryGetIntersectionBarycentricsEXT"; break; + case EOpRayQueryGetIntersectionFrontFace: out.debug << "rayQueryGetIntersectionFrontFaceEXT"; break; + case EOpRayQueryGetIntersectionCandidateAABBOpaque: out.debug << "rayQueryGetIntersectionCandidateAABBOpaqueEXT"; break; + case EOpRayQueryGetIntersectionObjectRayDirection: out.debug << "rayQueryGetIntersectionObjectRayDirectionEXT"; break; + case EOpRayQueryGetIntersectionObjectRayOrigin: out.debug << "rayQueryGetIntersectionObjectRayOriginEXT"; break; + case EOpRayQueryGetWorldRayDirection: out.debug << "rayQueryGetWorldRayDirectionEXT"; break; + case EOpRayQueryGetWorldRayOrigin: out.debug << "rayQueryGetWorldRayOriginEXT"; break; + case EOpRayQueryGetIntersectionObjectToWorld: out.debug << "rayQueryGetIntersectionObjectToWorldEXT"; break; + case EOpRayQueryGetIntersectionWorldToObject: out.debug << "rayQueryGetIntersectionWorldToObjectEXT"; break; + + case EOpCooperativeMatrixLoad: out.debug << "Load cooperative matrix"; break; + case EOpCooperativeMatrixStore: out.debug << "Store cooperative matrix"; break; + case EOpCooperativeMatrixMulAdd: out.debug << "MulAdd cooperative matrices"; break; + + case EOpIsHelperInvocation: out.debug << "IsHelperInvocation"; break; + case EOpDebugPrintf: out.debug << "Debug printf"; break; + + default: out.debug.message(EPrefixError, "Bad aggregation op"); + } + + if (node->getOp() != EOpSequence && node->getOp() != EOpParameters) + out.debug << " (" << node->getCompleteString() << ")"; + + out.debug << "\n"; + + return true; +} + +bool TOutputTraverser::visitSelection(TVisit /* visit */, TIntermSelection* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + out.debug << "Test condition and select"; + out.debug << " (" << node->getCompleteString() << ")"; + + if (node->getShortCircuit() == false) + out.debug << ": no shortcircuit"; + if (node->getFlatten()) + out.debug << ": Flatten"; + if (node->getDontFlatten()) + out.debug << ": DontFlatten"; + out.debug << "\n"; + + ++depth; + + OutputTreeText(out, node, depth); + out.debug << "Condition\n"; + node->getCondition()->traverse(this); + + OutputTreeText(out, node, depth); + if (node->getTrueBlock()) { + out.debug << "true case\n"; + node->getTrueBlock()->traverse(this); + } else + out.debug << "true case is null\n"; + + if (node->getFalseBlock()) { + OutputTreeText(out, node, depth); + out.debug << "false case\n"; + node->getFalseBlock()->traverse(this); + } + + --depth; + + return false; +} + +// Print infinities and NaNs, and numbers in a portable way. +// Goals: +// - portable (across IEEE 754 platforms) +// - shows all possible IEEE values +// - shows simple numbers in a simple way, e.g., no leading/trailing 0s +// - shows all digits, no premature rounding +static void OutputDouble(TInfoSink& out, double value, TOutputTraverser::EExtraOutput extra) +{ + if (IsInfinity(value)) { + if (value < 0) + out.debug << "-1.#INF"; + else + out.debug << "+1.#INF"; + } else if (IsNan(value)) + out.debug << "1.#IND"; + else { + const int maxSize = 340; + char buf[maxSize]; + const char* format = "%f"; + if (fabs(value) > 0.0 && (fabs(value) < 1e-5 || fabs(value) > 1e12)) + format = "%-.13e"; + int len = snprintf(buf, maxSize, format, value); + assert(len < maxSize); + + // remove a leading zero in the 100s slot in exponent; it is not portable + // pattern: XX...XXXe+0XX or XX...XXXe-0XX + if (len > 5) { + if (buf[len-5] == 'e' && (buf[len-4] == '+' || buf[len-4] == '-') && buf[len-3] == '0') { + buf[len-3] = buf[len-2]; + buf[len-2] = buf[len-1]; + buf[len-1] = '\0'; + } + } + + out.debug << buf; + + switch (extra) { + case TOutputTraverser::BinaryDoubleOutput: + { + uint64_t b; + static_assert(sizeof(b) == sizeof(value), "sizeof(uint64_t) != sizeof(double)"); + memcpy(&b, &value, sizeof(b)); + + out.debug << " : "; + for (size_t i = 0; i < 8 * sizeof(value); ++i, ++b) { + out.debug << ((b & 0x8000000000000000) != 0 ? "1" : "0"); + b <<= 1; + } + break; + } + default: + break; + } + } +} + +static void OutputConstantUnion(TInfoSink& out, const TIntermTyped* node, const TConstUnionArray& constUnion, + TOutputTraverser::EExtraOutput extra, int depth) +{ + int size = node->getType().computeNumComponents(); + + for (int i = 0; i < size; i++) { + OutputTreeText(out, node, depth); + switch (constUnion[i].getType()) { + case EbtBool: + if (constUnion[i].getBConst()) + out.debug << "true"; + else + out.debug << "false"; + + out.debug << " (" << "const bool" << ")"; + + out.debug << "\n"; + break; + case EbtFloat: + case EbtDouble: + case EbtFloat16: + OutputDouble(out, constUnion[i].getDConst(), extra); + out.debug << "\n"; + break; + case EbtInt8: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getI8Const(), "const int8_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint8: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getU8Const(), "const uint8_t"); + + out.debug << buf << "\n"; + } + break; + case EbtInt16: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getI16Const(), "const int16_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint16: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getU16Const(), "const uint16_t"); + + out.debug << buf << "\n"; + } + break; + case EbtInt: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%d (%s)", constUnion[i].getIConst(), "const int"); + + out.debug << buf << "\n"; + } + break; + case EbtUint: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%u (%s)", constUnion[i].getUConst(), "const uint"); + + out.debug << buf << "\n"; + } + break; + case EbtInt64: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%lld (%s)", constUnion[i].getI64Const(), "const int64_t"); + + out.debug << buf << "\n"; + } + break; + case EbtUint64: + { + const int maxSize = 300; + char buf[maxSize]; + snprintf(buf, maxSize, "%llu (%s)", constUnion[i].getU64Const(), "const uint64_t"); + + out.debug << buf << "\n"; + } + break; + case EbtString: + out.debug << "\"" << constUnion[i].getSConst()->c_str() << "\"\n"; + break; + default: + out.info.message(EPrefixInternalError, "Unknown constant", node->getLoc()); + break; + } + } +} + +void TOutputTraverser::visitConstantUnion(TIntermConstantUnion* node) +{ + OutputTreeText(infoSink, node, depth); + infoSink.debug << "Constant:\n"; + + OutputConstantUnion(infoSink, node, node->getConstArray(), extraOutput, depth + 1); +} + +void TOutputTraverser::visitSymbol(TIntermSymbol* node) +{ + OutputTreeText(infoSink, node, depth); + + infoSink.debug << "'" << node->getName() << "' (" << node->getCompleteString() << ")\n"; + + if (! node->getConstArray().empty()) + OutputConstantUnion(infoSink, node, node->getConstArray(), extraOutput, depth + 1); + else if (node->getConstSubtree()) { + incrementDepth(node); + node->getConstSubtree()->traverse(this); + decrementDepth(); + } +} + +bool TOutputTraverser::visitLoop(TVisit /* visit */, TIntermLoop* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + out.debug << "Loop with condition "; + if (! node->testFirst()) + out.debug << "not "; + out.debug << "tested first"; + + if (node->getUnroll()) + out.debug << ": Unroll"; + if (node->getDontUnroll()) + out.debug << ": DontUnroll"; + if (node->getLoopDependency()) { + out.debug << ": Dependency "; + out.debug << node->getLoopDependency(); + } + out.debug << "\n"; + + ++depth; + + OutputTreeText(infoSink, node, depth); + if (node->getTest()) { + out.debug << "Loop Condition\n"; + node->getTest()->traverse(this); + } else + out.debug << "No loop condition\n"; + + OutputTreeText(infoSink, node, depth); + if (node->getBody()) { + out.debug << "Loop Body\n"; + node->getBody()->traverse(this); + } else + out.debug << "No loop body\n"; + + if (node->getTerminal()) { + OutputTreeText(infoSink, node, depth); + out.debug << "Loop Terminal Expression\n"; + node->getTerminal()->traverse(this); + } + + --depth; + + return false; +} + +bool TOutputTraverser::visitBranch(TVisit /* visit*/, TIntermBranch* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + + switch (node->getFlowOp()) { + case EOpKill: out.debug << "Branch: Kill"; break; + case EOpTerminateInvocation: out.debug << "Branch: TerminateInvocation"; break; + case EOpBreak: out.debug << "Branch: Break"; break; + case EOpContinue: out.debug << "Branch: Continue"; break; + case EOpReturn: out.debug << "Branch: Return"; break; + case EOpCase: out.debug << "case: "; break; + case EOpDemote: out.debug << "Demote"; break; + case EOpDefault: out.debug << "default: "; break; + default: out.debug << "Branch: Unknown Branch"; break; + } + + if (node->getExpression()) { + out.debug << " with expression\n"; + ++depth; + node->getExpression()->traverse(this); + --depth; + } else + out.debug << "\n"; + + return false; +} + +bool TOutputTraverser::visitSwitch(TVisit /* visit */, TIntermSwitch* node) +{ + TInfoSink& out = infoSink; + + OutputTreeText(out, node, depth); + out.debug << "switch"; + + if (node->getFlatten()) + out.debug << ": Flatten"; + if (node->getDontFlatten()) + out.debug << ": DontFlatten"; + out.debug << "\n"; + + OutputTreeText(out, node, depth); + out.debug << "condition\n"; + ++depth; + node->getCondition()->traverse(this); + + --depth; + OutputTreeText(out, node, depth); + out.debug << "body\n"; + ++depth; + node->getBody()->traverse(this); + + --depth; + + return false; +} + +// +// This function is the one to call externally to start the traversal. +// Individual functions can be initialized to 0 to skip processing of that +// type of node. It's children will still be processed. +// +void TIntermediate::output(TInfoSink& infoSink, bool tree) +{ + infoSink.debug << "Shader version: " << version << "\n"; + if (requestedExtensions.size() > 0) { + for (auto extIt = requestedExtensions.begin(); extIt != requestedExtensions.end(); ++extIt) + infoSink.debug << "Requested " << *extIt << "\n"; + } + + if (xfbMode) + infoSink.debug << "in xfb mode\n"; + + switch (language) { + case EShLangVertex: + break; + + case EShLangTessControl: + infoSink.debug << "vertices = " << vertices << "\n"; + + if (inputPrimitive != ElgNone) + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + if (vertexSpacing != EvsNone) + infoSink.debug << "vertex spacing = " << TQualifier::getVertexSpacingString(vertexSpacing) << "\n"; + if (vertexOrder != EvoNone) + infoSink.debug << "triangle order = " << TQualifier::getVertexOrderString(vertexOrder) << "\n"; + break; + + case EShLangTessEvaluation: + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + infoSink.debug << "vertex spacing = " << TQualifier::getVertexSpacingString(vertexSpacing) << "\n"; + infoSink.debug << "triangle order = " << TQualifier::getVertexOrderString(vertexOrder) << "\n"; + if (pointMode) + infoSink.debug << "using point mode\n"; + break; + + case EShLangGeometry: + infoSink.debug << "invocations = " << invocations << "\n"; + infoSink.debug << "max_vertices = " << vertices << "\n"; + infoSink.debug << "input primitive = " << TQualifier::getGeometryString(inputPrimitive) << "\n"; + infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n"; + break; + + case EShLangFragment: + if (pixelCenterInteger) + infoSink.debug << "gl_FragCoord pixel center is integer\n"; + if (originUpperLeft) + infoSink.debug << "gl_FragCoord origin is upper left\n"; + if (earlyFragmentTests) + infoSink.debug << "using early_fragment_tests\n"; + if (postDepthCoverage) + infoSink.debug << "using post_depth_coverage\n"; + if (depthLayout != EldNone) + infoSink.debug << "using " << TQualifier::getLayoutDepthString(depthLayout) << "\n"; + if (blendEquations != 0) { + infoSink.debug << "using"; + // blendEquations is a mask, decode it + for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { + if (blendEquations & (1 << be)) + infoSink.debug << " " << TQualifier::getBlendEquationString(be); + } + infoSink.debug << "\n"; + } + if (interlockOrdering != EioNone) + infoSink.debug << "interlock ordering = " << TQualifier::getInterlockOrderingString(interlockOrdering) << "\n"; + break; + + case EShLangMeshNV: + infoSink.debug << "max_vertices = " << vertices << "\n"; + infoSink.debug << "max_primitives = " << primitives << "\n"; + infoSink.debug << "output primitive = " << TQualifier::getGeometryString(outputPrimitive) << "\n"; + // Fall through + case EShLangTaskNV: + // Fall through + case EShLangCompute: + infoSink.debug << "local_size = (" << localSize[0] << ", " << localSize[1] << ", " << localSize[2] << ")\n"; + { + if (localSizeSpecId[0] != TQualifier::layoutNotSet || + localSizeSpecId[1] != TQualifier::layoutNotSet || + localSizeSpecId[2] != TQualifier::layoutNotSet) { + infoSink.debug << "local_size ids = (" << + localSizeSpecId[0] << ", " << + localSizeSpecId[1] << ", " << + localSizeSpecId[2] << ")\n"; + } + } + break; + + default: + break; + } + + if (treeRoot == 0 || ! tree) + return; + + TOutputTraverser it(infoSink); + if (getBinaryDoubleOutput()) + it.setDoubleOutput(TOutputTraverser::BinaryDoubleOutput); + treeRoot->traverse(&it); +} + +} // end namespace glslang + +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE diff --git a/third_party/glslang/glslang/MachineIndependent/iomapper.cpp b/third_party/glslang/glslang/MachineIndependent/iomapper.cpp new file mode 100644 index 0000000..c42e74f --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/iomapper.cpp @@ -0,0 +1,1601 @@ +// +// Copyright (C) 2016-2017 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +#include "../Include/Common.h" +#include "../Include/InfoSink.h" +#include "../Include/Types.h" + +#include "gl_types.h" +#include "iomapper.h" +#include "SymbolTable.h" + +// +// Map IO bindings. +// +// High-level algorithm for one stage: +// +// 1. Traverse all code (live+dead) to find the explicitly provided bindings. +// +// 2. Traverse (just) the live code to determine which non-provided bindings +// require auto-numbering. We do not auto-number dead ones. +// +// 3. Traverse all the code to apply the bindings: +// a. explicitly given bindings are offset according to their type +// b. implicit live bindings are auto-numbered into the holes, using +// any open binding slot. +// c. implicit dead bindings are left un-bound. +// + +namespace glslang { + +class TVarGatherTraverser : public TLiveTraverser { +public: + TVarGatherTraverser(const TIntermediate& i, bool traverseDeadCode, TVarLiveMap& inList, TVarLiveMap& outList, TVarLiveMap& uniformList) + : TLiveTraverser(i, traverseDeadCode, true, true, false) + , inputList(inList) + , outputList(outList) + , uniformList(uniformList) + { + } + + virtual void visitSymbol(TIntermSymbol* base) + { + TVarLiveMap* target = nullptr; + if (base->getQualifier().storage == EvqVaryingIn) + target = &inputList; + else if (base->getQualifier().storage == EvqVaryingOut) + target = &outputList; + else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) + target = &uniformList; + // If a global is being visited, then we should also traverse it incase it's evaluation + // ends up visiting inputs we want to tag as live + else if (base->getQualifier().storage == EvqGlobal) + addGlobalReference(base->getAccessName()); + + if (target) { + TVarEntryInfo ent = {base->getId(), base, ! traverseAll}; + ent.stage = intermediate.getStage(); + TVarLiveMap::iterator at = target->find( + ent.symbol->getAccessName()); // std::lower_bound(target->begin(), target->end(), ent, TVarEntryInfo::TOrderById()); + if (at != target->end() && at->second.id == ent.id) + at->second.live = at->second.live || ! traverseAll; // update live state + else + (*target)[ent.symbol->getAccessName()] = ent; + } + } + +private: + TVarLiveMap& inputList; + TVarLiveMap& outputList; + TVarLiveMap& uniformList; +}; + +class TVarSetTraverser : public TLiveTraverser +{ +public: + TVarSetTraverser(const TIntermediate& i, const TVarLiveMap& inList, const TVarLiveMap& outList, const TVarLiveMap& uniformList) + : TLiveTraverser(i, true, true, true, false) + , inputList(inList) + , outputList(outList) + , uniformList(uniformList) + { + } + + virtual void visitSymbol(TIntermSymbol* base) { + const TVarLiveMap* source; + if (base->getQualifier().storage == EvqVaryingIn) + source = &inputList; + else if (base->getQualifier().storage == EvqVaryingOut) + source = &outputList; + else if (base->getQualifier().isUniformOrBuffer()) + source = &uniformList; + else + return; + + TVarEntryInfo ent = { base->getId() }; + // Fix a defect, when block has no instance name, we need to find its block name + TVarLiveMap::const_iterator at = source->find(base->getAccessName()); + if (at == source->end()) + return; + + if (at->second.id != ent.id) + return; + + if (at->second.newBinding != -1) + base->getWritableType().getQualifier().layoutBinding = at->second.newBinding; + if (at->second.newSet != -1) + base->getWritableType().getQualifier().layoutSet = at->second.newSet; + if (at->second.newLocation != -1) + base->getWritableType().getQualifier().layoutLocation = at->second.newLocation; + if (at->second.newComponent != -1) + base->getWritableType().getQualifier().layoutComponent = at->second.newComponent; + if (at->second.newIndex != -1) + base->getWritableType().getQualifier().layoutIndex = at->second.newIndex; + } + + private: + const TVarLiveMap& inputList; + const TVarLiveMap& outputList; + const TVarLiveMap& uniformList; +}; + +struct TNotifyUniformAdaptor +{ + EShLanguage stage; + TIoMapResolver& resolver; + inline TNotifyUniformAdaptor(EShLanguage s, TIoMapResolver& r) + : stage(s) + , resolver(r) + { + } + + inline void operator()(std::pair& entKey) + { + resolver.notifyBinding(stage, entKey.second); + } + +private: + TNotifyUniformAdaptor& operator=(TNotifyUniformAdaptor&) = delete; +}; + +struct TNotifyInOutAdaptor +{ + EShLanguage stage; + TIoMapResolver& resolver; + inline TNotifyInOutAdaptor(EShLanguage s, TIoMapResolver& r) + : stage(s) + , resolver(r) + { + } + + inline void operator()(std::pair& entKey) + { + resolver.notifyInOut(entKey.second.stage, entKey.second); + } + +private: + TNotifyInOutAdaptor& operator=(TNotifyInOutAdaptor&) = delete; +}; + +struct TResolverUniformAdaptor { + TResolverUniformAdaptor(EShLanguage s, TIoMapResolver& r, TVarLiveMap* uniform[EShLangCount], TInfoSink& i, bool& e) + : stage(s) + , resolver(r) + , infoSink(i) + , error(e) + { + memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*))); + } + + inline void operator()(std::pair& entKey) { + TVarEntryInfo& ent = entKey.second; + ent.newLocation = -1; + ent.newComponent = -1; + ent.newBinding = -1; + ent.newSet = -1; + ent.newIndex = -1; + const bool isValid = resolver.validateBinding(stage, ent); + if (isValid) { + resolver.resolveBinding(ent.stage, ent); + resolver.resolveSet(ent.stage, ent); + resolver.resolveUniformLocation(ent.stage, ent); + + if (ent.newBinding != -1) { + if (ent.newBinding >= int(TQualifier::layoutBindingEnd)) { + TString err = "mapped binding out of range: " + entKey.first; + + infoSink.info.message(EPrefixInternalError, err.c_str()); + error = true; + } + + if (ent.symbol->getQualifier().hasBinding()) { + for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) { + if (idx == ent.stage || uniformVarMap[idx] == nullptr) + continue; + auto entKey2 = uniformVarMap[idx]->find(entKey.first); + if (entKey2 != uniformVarMap[idx]->end()) { + entKey2->second.newBinding = ent.newBinding; + } + } + } + } + if (ent.newSet != -1) { + if (ent.newSet >= int(TQualifier::layoutSetEnd)) { + TString err = "mapped set out of range: " + entKey.first; + + infoSink.info.message(EPrefixInternalError, err.c_str()); + error = true; + } + if (ent.symbol->getQualifier().hasSet()) { + for (uint32_t idx = EShLangVertex; idx < EShLangCount; ++idx) { + if ((idx == stage) || (uniformVarMap[idx] == nullptr)) + continue; + auto entKey2 = uniformVarMap[idx]->find(entKey.first); + if (entKey2 != uniformVarMap[idx]->end()) { + entKey2->second.newSet = ent.newSet; + } + } + } + } + } else { + TString errorMsg = "Invalid binding: " + entKey.first; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + error = true; + } + } + + inline void setStage(EShLanguage s) { stage = s; } + + EShLanguage stage; + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& error; + TVarLiveMap* uniformVarMap[EShLangCount]; +private: + TResolverUniformAdaptor& operator=(TResolverUniformAdaptor&) = delete; +}; + +struct TResolverInOutAdaptor { + TResolverInOutAdaptor(EShLanguage s, TIoMapResolver& r, TInfoSink& i, bool& e) + : stage(s) + , resolver(r) + , infoSink(i) + , error(e) + { + } + + inline void operator()(std::pair& entKey) + { + TVarEntryInfo& ent = entKey.second; + ent.newLocation = -1; + ent.newComponent = -1; + ent.newBinding = -1; + ent.newSet = -1; + ent.newIndex = -1; + const bool isValid = resolver.validateInOut(ent.stage, ent); + if (isValid) { + resolver.resolveInOutLocation(stage, ent); + resolver.resolveInOutComponent(stage, ent); + resolver.resolveInOutIndex(stage, ent); + } else { + TString errorMsg; + if (ent.symbol->getType().getQualifier().semanticName != nullptr) { + errorMsg = "Invalid shader In/Out variable semantic: "; + errorMsg += ent.symbol->getType().getQualifier().semanticName; + } else { + errorMsg = "Invalid shader In/Out variable: "; + errorMsg += ent.symbol->getName(); + } + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + error = true; + } + } + + inline void setStage(EShLanguage s) { stage = s; } + + EShLanguage stage; + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& error; + +private: + TResolverInOutAdaptor& operator=(TResolverInOutAdaptor&) = delete; +}; + +// The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings + +struct TSymbolValidater +{ + TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount], + TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version) + : preStage(EShLangCount) + , currentStage(EShLangCount) + , nextStage(EShLangCount) + , resolver(r) + , infoSink(i) + , hadError(hadError) + , profile(profile) + , version(version) + { + memcpy(inVarMaps, in, EShLangCount * (sizeof(TVarLiveMap*))); + memcpy(outVarMaps, out, EShLangCount * (sizeof(TVarLiveMap*))); + memcpy(uniformVarMap, uniform, EShLangCount * (sizeof(TVarLiveMap*))); + + std::map anonymousMemberMap; + std::vector usedUniformLocation; + std::vector usedUniformName; + usedUniformLocation.clear(); + usedUniformName.clear(); + for (int i = 0; i < EShLangCount; i++) { + if (uniformVarMap[i]) { + for (auto uniformVar : *uniformVarMap[i]) + { + TIntermSymbol* pSymbol = uniformVar.second.symbol; + TQualifier qualifier = uniformVar.second.symbol->getQualifier(); + TString symbolName = pSymbol->getAccessName(); + + // All the uniform needs multi-stage location check (block/default) + int uniformLocation = qualifier.layoutLocation; + + if (uniformLocation != TQualifier::layoutLocationEnd) { + // Total size of current uniform, could be block, struct or other types. + int size = TIntermediate::computeTypeUniformLocationSize(pSymbol->getType()); + + TRange locationRange(uniformLocation, uniformLocation + size - 1); + + // Combine location and component ranges + int overlapLocation = -1; + bool diffLocation = false; + + // Check for collisions, except for vertex inputs on desktop targeting OpenGL + overlapLocation = checkLocationOverlap(locationRange, usedUniformLocation, symbolName, usedUniformName, diffLocation); + + // Overlap locations of uniforms, regardless of components (multi stages) + if (overlapLocation == -1) { + usedUniformLocation.push_back(locationRange); + usedUniformName.push_back(symbolName); + } + else if (overlapLocation >= 0) { + if (diffLocation == true) { + TString err = ("Uniform location should be equal for same uniforms: " +std::to_string(overlapLocation)).c_str(); + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + break; + } + else { + TString err = ("Uniform location overlaps across stages: " + std::to_string(overlapLocation)).c_str(); + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + break; + } + } + } + + if ((uniformVar.second.symbol->getBasicType() == EbtBlock) && + IsAnonymous(uniformVar.second.symbol->getName())) + { + auto blockType = uniformVar.second.symbol->getType().getStruct(); + for (size_t memberIdx = 0; memberIdx < blockType->size(); ++memberIdx) { + auto memberName = (*blockType)[memberIdx].type->getFieldName(); + if (anonymousMemberMap.find(memberName) != anonymousMemberMap.end()) + { + if (anonymousMemberMap[memberName] != uniformVar.second.symbol->getType().getTypeName()) + { + TString err = "Invalid block member name: " + memberName; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + break; + } + } + else + { + anonymousMemberMap[memberName] = uniformVar.second.symbol->getType().getTypeName(); + } + } + } + if (hadError) + break; + } + } + } + } + + // In case we need to new an intermediate, which costs too much + int checkLocationOverlap(const TRange& locationRange, std::vector& usedUniformLocation, const TString symbolName, std::vector& usedUniformName, bool& diffLocation) + { + for (size_t r = 0; r < usedUniformLocation.size(); ++r) { + if (usedUniformName[r] == symbolName) { + diffLocation = true; + return (usedUniformLocation[r].start == locationRange.start && + usedUniformLocation[r].last == locationRange.last) + ? -2 : std::max(locationRange.start, usedUniformLocation[r].start); + } + if (locationRange.overlap(usedUniformLocation[r])) { + // there is a collision; pick one + return std::max(locationRange.start, usedUniformLocation[r].start); + } + } + + return -1; // no collision + } + + inline void operator()(std::pair& entKey) { + TVarEntryInfo& ent1 = entKey.second; + TIntermSymbol* base = ent1.symbol; + const TType& type = ent1.symbol->getType(); + const TString& name = entKey.first; + EShLanguage stage = ent1.stage; + TString mangleName1, mangleName2; + if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + nextStage = EShLangCount; + for (int i = currentStage + 1; i < EShLangCount; i++) { + if (inVarMaps[i] != nullptr) { + nextStage = static_cast(i); + break; + } + } + } + + if (type.getQualifier().isArrayedIo(stage)) { + TType subType(type, 0); + subType.appendMangledName(mangleName1); + } else { + type.appendMangledName(mangleName1); + } + + if (base->getQualifier().storage == EvqVaryingIn) { + // validate stage in; + if (preStage == EShLangCount) + return; + if (TSymbolTable::isBuiltInSymbol(base->getId())) + return; + if (outVarMaps[preStage] != nullptr) { + auto ent2 = outVarMaps[preStage]->find(name); + uint32_t location = base->getType().getQualifier().layoutLocation; + if (ent2 == outVarMaps[preStage]->end() && + location != glslang::TQualifier::layoutLocationEnd) { + for (auto var = outVarMaps[preStage]->begin(); var != ent2; var++) { + if (var->second.symbol->getType().getQualifier().layoutLocation == location) { + ent2 = var; + break; + } + } + } + if (ent2 != outVarMaps[preStage]->end()) { + auto& type1 = base->getType(); + auto& type2 = ent2->second.symbol->getType(); + hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false); + if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) { + TType subType(ent2->second.symbol->getType(), 0); + subType.appendMangledName(mangleName2); + } + else { + ent2->second.symbol->getType().appendMangledName(mangleName2); + } + + if (mangleName1 == mangleName2) { + // For ES 3.0 only, other versions have no such restrictions + // According to ES 3.0 spec: The type and presence of the interpolation qualifiers and + // storage qualifiers of variables with the same name declared in all linked shaders must + // match, otherwise the link command will fail. + if (profile == EEsProfile && version == 300) { + // Don't need to check smooth qualifier, as it uses the default interpolation mode + if (ent1.stage == EShLangFragment && type1.isBuiltIn() == false) { + if (type1.getQualifier().flat != type2.getQualifier().flat || + type1.getQualifier().nopersp != type2.getQualifier().nopersp) { + TString err = "Interpolation qualifier mismatch : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + } + } + return; + } + else { + TString err = "Invalid In/Out variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + } + else if (!base->getType().isBuiltIn()) { + // According to spec: A link error is generated if any statically referenced input variable + // or block does not have a matching output + if (profile == EEsProfile && ent1.live) { + hadError = true; + TString errorStr = name + ": not been declare as a output variable in pre shader stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + } + return; + } + } else if (base->getQualifier().storage == EvqVaryingOut) { + // validate stage out; + if (nextStage == EShLangCount) + return; + if (TSymbolTable::isBuiltInSymbol(base->getId())) + return; + if (inVarMaps[nextStage] != nullptr) { + auto ent2 = inVarMaps[nextStage]->find(name); + if (ent2 != inVarMaps[nextStage]->end()) { + if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) { + TType subType(ent2->second.symbol->getType(), 0); + subType.appendMangledName(mangleName2); + } + else { + ent2->second.symbol->getType().appendMangledName(mangleName2); + } + if (mangleName1 == mangleName2) + return; + else { + TString err = "Invalid In/Out variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + } + return; + } + } else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().isPushConstant()) { + // validate uniform type; + for (int i = 0; i < EShLangCount; i++) { + if (i != currentStage && outVarMaps[i] != nullptr) { + auto ent2 = uniformVarMap[i]->find(name); + if (ent2 != uniformVarMap[i]->end()) { + ent2->second.symbol->getType().appendMangledName(mangleName2); + if (mangleName1 != mangleName2) { + TString err = "Invalid Uniform variable type : " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + mangleName2.clear(); + + // validate instance name of blocks + if (hadError == false && + base->getType().getBasicType() == EbtBlock && + IsAnonymous(base->getName()) != IsAnonymous(ent2->second.symbol->getName())) { + TString err = "Matched uniform block names must also either all be lacking " + "an instance name or all having an instance name: " + entKey.first; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + } + + // validate uniform block member qualifier and member names + auto& type1 = base->getType(); + auto& type2 = ent2->second.symbol->getType(); + if (hadError == false && base->getType().getBasicType() == EbtBlock) { + hadError = hadError || typeCheck(&type1, &type2, name.c_str(), true); + } + else { + hadError = hadError || typeCheck(&type1, &type2, name.c_str(), false); + } + } + else if (base->getBasicType() == EbtBlock) + { + if (IsAnonymous(base->getName())) + { + // The name of anonymous block member can't same with default uniform variable. + auto blockType1 = base->getType().getStruct(); + for (size_t memberIdx = 0; memberIdx < blockType1->size(); ++memberIdx) { + auto memberName = (*blockType1)[memberIdx].type->getFieldName(); + if (uniformVarMap[i]->find(memberName) != uniformVarMap[i]->end()) + { + TString err = "Invalid Uniform variable name : " + memberName; + infoSink.info.message(EPrefixInternalError, err.c_str()); + hadError = true; + break; + } + } + } + } + } + } + } + } + + TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount]; + // Use for mark pre stage, to get more interface symbol information. + EShLanguage preStage, currentStage, nextStage; + // Use for mark current shader stage for resolver + TIoMapResolver& resolver; + TInfoSink& infoSink; + bool& hadError; + EProfile profile; + int version; + +private: + TSymbolValidater& operator=(TSymbolValidater&) = delete; + + bool qualifierCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock) + { + bool hasError = false; + const TQualifier& qualifier1 = type1->getQualifier(); + const TQualifier& qualifier2 = type2->getQualifier(); + + if (((isBlock == false) && + (type1->getQualifier().storage == EvqUniform && type2->getQualifier().storage == EvqUniform)) || + (type1->getQualifier().storage == EvqGlobal && type2->getQualifier().storage == EvqGlobal)) { + if (qualifier1.precision != qualifier2.precision) { + hasError = true; + std::string errorStr = name + ": have precision conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + if (qualifier1.hasFormat() && qualifier2.hasFormat()) { + if (qualifier1.layoutFormat != qualifier2.layoutFormat) { + hasError = true; + std::string errorStr = name + ": have layout format conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + + } + } + + if (isBlock == true) { + if (qualifier1.layoutPacking != qualifier2.layoutPacking) { + hasError = true; + std::string errorStr = name + ": have layoutPacking conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + if (qualifier1.layoutMatrix != qualifier2.layoutMatrix) { + hasError = true; + std::string errorStr = name + ": have layoutMatrix conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + if (qualifier1.layoutOffset != qualifier2.layoutOffset) { + hasError = true; + std::string errorStr = name + ": have layoutOffset conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + if (qualifier1.layoutAlign != qualifier2.layoutAlign) { + hasError = true; + std::string errorStr = name + ": have layoutAlign conflict cross stage."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + } + } + + return hasError; + } + + bool typeCheck(const TType* const type1, const TType* const type2, const std::string& name, bool isBlock) + { + bool hasError = false; + if (!(type1->isStruct() && type2->isStruct())) { + hasError = hasError || qualifierCheck(type1, type2, name, isBlock); + } + else { + if (type1->getBasicType() == EbtBlock && type2->getBasicType() == EbtBlock) + isBlock = true; + const TTypeList* typeList1 = type1->getStruct(); + const TTypeList* typeList2 = type2->getStruct(); + + std::string newName = name; + size_t memberCount = typeList1->size(); + size_t index2 = 0; + for (size_t index = 0; index < memberCount; index++, index2++) { + // Skip inactive member + if (typeList1->at(index).type->getBasicType() == EbtVoid) + continue; + while (index2 < typeList2->size() && typeList2->at(index2).type->getBasicType() == EbtVoid) { + ++index2; + } + + // TypeList1 has more members in list + if (index2 == typeList2->size()) { + std::string errorStr = name + ": struct mismatch."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + hasError = true; + break; + } + + if (typeList1->at(index).type->getFieldName() != typeList2->at(index2).type->getFieldName()) { + std::string errorStr = name + ": member name mismatch."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + hasError = true; + } + else { + newName = typeList1->at(index).type->getFieldName().c_str(); + } + hasError = hasError || typeCheck(typeList1->at(index).type, typeList2->at(index2).type, newName, isBlock); + } + + while (index2 < typeList2->size()) + { + // TypeList2 has more members + if (typeList2->at(index2).type->getBasicType() != EbtVoid) { + std::string errorStr = name + ": struct mismatch."; + infoSink.info.message(EPrefixError, errorStr.c_str()); + hasError = true; + break; + } + ++index2; + } + } + return hasError; + } +}; + +struct TSlotCollector { + TSlotCollector(TIoMapResolver& r, TInfoSink& i) : resolver(r), infoSink(i) { } + + inline void operator()(std::pair& entKey) { + resolver.reserverStorageSlot(entKey.second, infoSink); + resolver.reserverResourceSlot(entKey.second, infoSink); + } + TIoMapResolver& resolver; + TInfoSink& infoSink; + +private: + TSlotCollector& operator=(TSlotCollector&) = delete; +}; + +TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate) + : intermediate(intermediate) + , nextUniformLocation(intermediate.getUniformLocationBase()) + , nextInputLocation(0) + , nextOutputLocation(0) +{ + memset(stageMask, false, sizeof(bool) * (EShLangCount + 1)); +} + +int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const { + return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set)); +} + +const std::vector& TDefaultIoResolverBase::getResourceSetBinding() const { + return intermediate.getResourceSetBinding(); +} + +bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); } + +bool TDefaultIoResolverBase::doAutoLocationMapping() const { return intermediate.getAutoMapLocations(); } + +TDefaultIoResolverBase::TSlotSet::iterator TDefaultIoResolverBase::findSlot(int set, int slot) { + return std::lower_bound(slots[set].begin(), slots[set].end(), slot); +} + +bool TDefaultIoResolverBase::checkEmpty(int set, int slot) { + TSlotSet::iterator at = findSlot(set, slot); + return ! (at != slots[set].end() && *at == slot); +} + +int TDefaultIoResolverBase::reserveSlot(int set, int slot, int size) { + TSlotSet::iterator at = findSlot(set, slot); + // tolerate aliasing, by not double-recording aliases + // (policy about appropriateness of the alias is higher up) + for (int i = 0; i < size; i++) { + if (at == slots[set].end() || *at != slot + i) + at = slots[set].insert(at, slot + i); + ++at; + } + return slot; +} + +int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) { + TSlotSet::iterator at = findSlot(set, base); + if (at == slots[set].end()) + return reserveSlot(set, base, size); + // look for a big enough gap + for (; at != slots[set].end(); ++at) { + if (*at - base >= size) + break; + base = *at + 1; + } + return reserveSlot(set, base, size); +} + +int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + if (type.getQualifier().hasSet()) { + return ent.newSet = type.getQualifier().layoutSet; + } + // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN) + if (getResourceSetBinding().size() == 1) { + return ent.newSet = atoi(getResourceSetBinding()[0].c_str()); + } + return ent.newSet = 0; +} + +int TDefaultIoResolverBase::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const char* name = ent.symbol->getAccessName().c_str(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // no locations added if already present, a built-in variable, a block, or an opaque + if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || + type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + int location = intermediate.getUniformLocationOverride(name); + if (location != -1) { + return ent.newLocation = location; + } + location = nextUniformLocation; + nextUniformLocation += TIntermediate::computeTypeUniformLocationSize(type); + return ent.newLocation = location; +} + +int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + + // no locations added if already present, or a built-in variable + if (type.getQualifier().hasLocation() || type.isBuiltIn()) { + return ent.newLocation = -1; + } + + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + // point to the right input or output location counter + int& nextLocation = type.getQualifier().isPipeInput() ? nextInputLocation : nextOutputLocation; + // Placeholder. This does not do proper cross-stage lining up, nor + // work with mixed location/no-location declarations. + int location = nextLocation; + int typeLocationSize; + // Don’t take into account the outer-most array if the stage’s + // interface is automatically an array. + typeLocationSize = computeTypeLocationSize(type, stage); + nextLocation += typeLocationSize; + return ent.newLocation = location; +} + +int TDefaultIoResolverBase::resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) { + return ent.newComponent = -1; +} + +int TDefaultIoResolverBase::resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) { return ent.newIndex = -1; } + +uint32_t TDefaultIoResolverBase::computeTypeLocationSize(const TType& type, EShLanguage stage) { + int typeLocationSize; + // Don’t take into account the outer-most array if the stage’s + // interface is automatically an array. + if (type.getQualifier().isArrayedIo(stage)) { + TType elementType(type, 0); + typeLocationSize = TIntermediate::computeTypeLocationSize(elementType, stage); + } else { + typeLocationSize = TIntermediate::computeTypeLocationSize(type, stage); + } + return typeLocationSize; +} + +//TDefaultGlslIoResolver +TResourceType TDefaultGlslIoResolver::getResourceType(const glslang::TType& type) { + if (isImageType(type)) { + return EResImage; + } + if (isTextureType(type)) { + return EResTexture; + } + if (isSsboType(type)) { + return EResSsbo; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; +} + +TDefaultGlslIoResolver::TDefaultGlslIoResolver(const TIntermediate& intermediate) + : TDefaultIoResolverBase(intermediate) + , preStage(EShLangCount) + , currentStage(EShLangCount) +{ } + +int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // expand the location to each element if the symbol is a struct or array + if (type.getQualifier().hasLocation()) { + return ent.newLocation = type.getQualifier().layoutLocation; + } + // no locations added if already present, or a built-in variable + if (type.isBuiltIn()) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + int typeLocationSize = computeTypeLocationSize(type, stage); + int location = type.getQualifier().layoutLocation; + bool hasLocation = false; + EShLanguage keyStage(EShLangCount); + TStorageQualifier storage; + storage = EvqInOut; + if (type.getQualifier().isPipeInput()) { + // If this symbol is a input, search pre stage's out + keyStage = preStage; + } + if (type.getQualifier().isPipeOutput()) { + // If this symbol is a output, search next stage's in + keyStage = currentStage; + } + // The in/out in current stage is not declared with location, but it is possible declared + // with explicit location in other stages, find the storageSlotMap firstly to check whether + // the in/out has location + int resourceKey = buildStorageKey(keyStage, storage); + if (! storageSlotMap[resourceKey].empty()) { + TVarSlotMap::iterator iter = storageSlotMap[resourceKey].find(name); + if (iter != storageSlotMap[resourceKey].end()) { + // If interface resource be found, set it has location and this symbol's new location + // equal the symbol's explicit location declaration in pre or next stage. + // + // vs: out vec4 a; + // fs: layout(..., location = 3,...) in vec4 a; + hasLocation = true; + location = iter->second; + // if we want deal like that: + // vs: layout(location=4) out vec4 a; + // out vec4 b; + // + // fs: in vec4 a; + // layout(location = 4) in vec4 b; + // we need retraverse the map. + } + if (! hasLocation) { + // If interface resource note found, It's mean the location in two stage are both implicit declarat. + // So we should find a new slot for this interface. + // + // vs: out vec4 a; + // fs: in vec4 a; + location = getFreeSlot(resourceKey, 0, typeLocationSize); + storageSlotMap[resourceKey][name] = location; + } + } else { + // the first interface declarated in a program. + TVarSlotMap varSlotMap; + location = getFreeSlot(resourceKey, 0, typeLocationSize); + varSlotMap[name] = location; + storageSlotMap[resourceKey] = varSlotMap; + } + //Update location + return ent.newLocation = location; +} + +int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + // kick out of not doing this + if (! doAutoLocationMapping()) { + return ent.newLocation = -1; + } + // expand the location to each element if the symbol is a struct or array + if (type.getQualifier().hasLocation() && (type.isStruct() || type.isArray())) { + return ent.newLocation = type.getQualifier().layoutLocation; + } else { + // no locations added if already present, a built-in variable, a block, or an opaque + if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getBasicType() == EbtBlock || + type.isAtomic() || (type.containsOpaque() && intermediate.getSpv().openGl == 0)) { + return ent.newLocation = -1; + } + // no locations on blocks of built-in variables + if (type.isStruct()) { + if (type.getStruct()->size() < 1) { + return ent.newLocation = -1; + } + if ((*type.getStruct())[0].type->isBuiltIn()) { + return ent.newLocation = -1; + } + } + } + int location = intermediate.getUniformLocationOverride(name.c_str()); + if (location != -1) { + return ent.newLocation = location; + } + + int size = TIntermediate::computeTypeUniformLocationSize(type); + + // The uniform in current stage is not declared with location, but it is possible declared + // with explicit location in other stages, find the storageSlotMap firstly to check whether + // the uniform has location + bool hasLocation = false; + int resourceKey = buildStorageKey(EShLangCount, EvqUniform); + TVarSlotMap& slotMap = storageSlotMap[resourceKey]; + // Check dose shader program has uniform resource + if (! slotMap.empty()) { + // If uniform resource not empty, try find a same name uniform + TVarSlotMap::iterator iter = slotMap.find(name); + if (iter != slotMap.end()) { + // If uniform resource be found, set it has location and this symbol's new location + // equal the uniform's explicit location declaration in other stage. + // + // vs: uniform vec4 a; + // fs: layout(..., location = 3,...) uniform vec4 a; + hasLocation = true; + location = iter->second; + } + if (! hasLocation) { + // No explicit location declaration in other stage. + // So we should find a new slot for this uniform. + // + // vs: uniform vec4 a; + // fs: uniform vec4 a; + location = getFreeSlot(resourceKey, 0, computeTypeLocationSize(type, currentStage)); + storageSlotMap[resourceKey][name] = location; + } + } else { + // the first uniform declaration in a program. + TVarSlotMap varSlotMap; + location = getFreeSlot(resourceKey, 0, size); + varSlotMap[name] = location; + storageSlotMap[resourceKey] = varSlotMap; + } + return ent.newLocation = location; +} + +int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + // On OpenGL arrays of opaque types take a separate binding for each element + int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; + TResourceType resource = getResourceType(type); + // don't need to handle uniform symbol, it will be handled in resolveUniformLocation + if (resource == EResUbo && type.getBasicType() != EbtBlock) { + return ent.newBinding = -1; + } + // There is no 'set' qualifier in OpenGL shading language, each resource has its own + // binding name space, so remap the 'set' to resource type which make each resource + // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS + int set = resource; + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings); + return ent.newBinding; + } else if (ent.live && doAutoBindingMapping()) { + // The resource in current stage is not declared with binding, but it is possible declared + // with explicit binding in other stages, find the resourceSlotMap firstly to check whether + // the resource has binding, don't need to allocate if it already has a binding + bool hasBinding = false; + if (! resourceSlotMap[resource].empty()) { + TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name); + if (iter != resourceSlotMap[resource].end()) { + hasBinding = true; + ent.newBinding = iter->second; + } + } + if (! hasBinding) { + TVarSlotMap varSlotMap; + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings); + varSlotMap[name] = binding; + resourceSlotMap[resource] = varSlotMap; + ent.newBinding = binding; + } + return ent.newBinding; + } + } + return ent.newBinding = -1; +} + +void TDefaultGlslIoResolver::beginResolve(EShLanguage stage) { + // reset stage state + if (stage == EShLangCount) + preStage = currentStage = stage; + // update stage state + else if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } +} + +void TDefaultGlslIoResolver::endResolve(EShLanguage /*stage*/) { + // TODO nothing +} + +void TDefaultGlslIoResolver::beginCollect(EShLanguage stage) { + // reset stage state + if (stage == EShLangCount) + preStage = currentStage = stage; + // update stage state + else if (currentStage != stage) { + preStage = currentStage; + currentStage = stage; + } +} + +void TDefaultGlslIoResolver::endCollect(EShLanguage /*stage*/) { + // TODO nothing +} + +void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + TStorageQualifier storage = type.getQualifier().storage; + EShLanguage stage(EShLangCount); + switch (storage) { + case EvqUniform: + if (type.getBasicType() != EbtBlock && type.getQualifier().hasLocation()) { + // + // Reserve the slots for the uniforms who has explicit location + int storageKey = buildStorageKey(EShLangCount, EvqUniform); + int location = type.getQualifier().layoutLocation; + TVarSlotMap& varSlotMap = storageSlotMap[storageKey]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + if (iter == varSlotMap.end()) { + int numLocations = TIntermediate::computeTypeUniformLocationSize(type); + reserveSlot(storageKey, location, numLocations); + varSlotMap[name] = location; + } else { + // Allocate location by name for OpenGL driver, so the uniform in different + // stages should be declared with the same location + if (iter->second != location) { + TString errorMsg = "Invalid location: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } + break; + case EvqVaryingIn: + case EvqVaryingOut: + // + // Reserve the slots for the inout who has explicit location + if (type.getQualifier().hasLocation()) { + stage = storage == EvqVaryingIn ? preStage : stage; + stage = storage == EvqVaryingOut ? currentStage : stage; + int storageKey = buildStorageKey(stage, EvqInOut); + int location = type.getQualifier().layoutLocation; + TVarSlotMap& varSlotMap = storageSlotMap[storageKey]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + if (iter == varSlotMap.end()) { + int numLocations = TIntermediate::computeTypeUniformLocationSize(type); + reserveSlot(storageKey, location, numLocations); + varSlotMap[name] = location; + } else { + // Allocate location by name for OpenGL driver, so the uniform in different + // stages should be declared with the same location + if (iter->second != location) { + TString errorMsg = "Invalid location: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } + break; + default: + break; + } +} + +void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { + const TType& type = ent.symbol->getType(); + const TString& name = ent.symbol->getAccessName(); + int resource = getResourceType(type); + if (type.getQualifier().hasBinding()) { + TVarSlotMap& varSlotMap = resourceSlotMap[resource]; + TVarSlotMap::iterator iter = varSlotMap.find(name); + int binding = type.getQualifier().layoutBinding; + if (iter == varSlotMap.end()) { + // Reserve the slots for the ubo, ssbo and opaques who has explicit binding + int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1; + varSlotMap[name] = binding; + reserveSlot(resource, binding, numBindings); + } else { + // Allocate binding by name for OpenGL driver, so the resource in different + // stages should be declared with the same binding + if (iter->second != binding) { + TString errorMsg = "Invalid binding: " + name; + infoSink.info.message(EPrefixInternalError, errorMsg.c_str()); + hasError = true; + } + } + } +} + +//TDefaultGlslIoResolver end + +/* + * Basic implementation of glslang::TIoMapResolver that replaces the + * previous offset behavior. + * It does the same, uses the offsets for the corresponding uniform + * types. Also respects the EOptionAutoMapBindings flag and binds + * them if needed. + */ +/* + * Default resolver + */ +struct TDefaultIoResolver : public TDefaultIoResolverBase { + TDefaultIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { } + + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + + TResourceType getResourceType(const glslang::TType& type) override { + if (isImageType(type)) { + return EResImage; + } + if (isTextureType(type)) { + return EResTexture; + } + if (isSsboType(type)) { + return EResSsbo; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; + } + + int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { + const TType& type = ent.symbol->getType(); + const int set = getLayoutSet(type); + // On OpenGL arrays of opaque types take a seperate binding for each element + int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; + TResourceType resource = getResourceType(type); + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + return ent.newBinding = reserveSlot( + set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings); + } else if (ent.live && doAutoBindingMapping()) { + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings); + } + } + return ent.newBinding = -1; + } +}; + +#ifdef ENABLE_HLSL +/******************************************************************************** +The following IO resolver maps types in HLSL register space, as follows: + +t - for shader resource views (SRV) + TEXTURE1D + TEXTURE1DARRAY + TEXTURE2D + TEXTURE2DARRAY + TEXTURE3D + TEXTURECUBE + TEXTURECUBEARRAY + TEXTURE2DMS + TEXTURE2DMSARRAY + STRUCTUREDBUFFER + BYTEADDRESSBUFFER + BUFFER + TBUFFER + +s - for samplers + SAMPLER + SAMPLER1D + SAMPLER2D + SAMPLER3D + SAMPLERCUBE + SAMPLERSTATE + SAMPLERCOMPARISONSTATE + +u - for unordered access views (UAV) + RWBYTEADDRESSBUFFER + RWSTRUCTUREDBUFFER + APPENDSTRUCTUREDBUFFER + CONSUMESTRUCTUREDBUFFER + RWBUFFER + RWTEXTURE1D + RWTEXTURE1DARRAY + RWTEXTURE2D + RWTEXTURE2DARRAY + RWTEXTURE3D + +b - for constant buffer views (CBV) + CBUFFER + CONSTANTBUFFER + ********************************************************************************/ +struct TDefaultHlslIoResolver : public TDefaultIoResolverBase { + TDefaultHlslIoResolver(const TIntermediate& intermediate) : TDefaultIoResolverBase(intermediate) { } + + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + + TResourceType getResourceType(const glslang::TType& type) override { + if (isUavType(type)) { + return EResUav; + } + if (isSrvType(type)) { + return EResTexture; + } + if (isSamplerType(type)) { + return EResSampler; + } + if (isUboType(type)) { + return EResUbo; + } + return EResCount; + } + + int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { + const TType& type = ent.symbol->getType(); + const int set = getLayoutSet(type); + TResourceType resource = getResourceType(type); + if (resource < EResCount) { + if (type.getQualifier().hasBinding()) { + return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding); + } else if (ent.live && doAutoBindingMapping()) { + // find free slot, the caller did make sure it passes all vars with binding + // first and now all are passed that do not have a binding and needs one + return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set)); + } + } + return ent.newBinding = -1; + } +}; +#endif + +// Map I/O variables to provided offsets, and make bindings for +// unbound but live variables. +// +// Returns false if the input is too malformed to do this. +bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) { + bool somethingToDo = ! intermediate.getResourceSetBinding().empty() || intermediate.getAutoMapBindings() || + intermediate.getAutoMapLocations(); + // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce + // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true. + for (int res = 0; (res < EResCount && !somethingToDo); ++res) { + somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || + intermediate.hasShiftBindingForSet(TResourceType(res)); + } + if (! somethingToDo && resolver == nullptr) + return true; + if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) + return false; + TIntermNode* root = intermediate.getTreeRoot(); + if (root == nullptr) + return false; + // if no resolver is provided, use the default resolver with the given shifts and auto map settings + TDefaultIoResolver defaultResolver(intermediate); +#ifdef ENABLE_HLSL + TDefaultHlslIoResolver defaultHlslResolver(intermediate); + if (resolver == nullptr) { + // TODO: use a passed in IO mapper for this + if (intermediate.usingHlslIoMapping()) + resolver = &defaultHlslResolver; + else + resolver = &defaultResolver; + } + resolver->addStage(stage); +#else + resolver = &defaultResolver; +#endif + + TVarLiveMap inVarMap, outVarMap, uniformVarMap; + TVarLiveVector inVector, outVector, uniformVector; + TVarGatherTraverser iter_binding_all(intermediate, true, inVarMap, outVarMap, uniformVarMap); + TVarGatherTraverser iter_binding_live(intermediate, false, inVarMap, outVarMap, uniformVarMap); + root->traverse(&iter_binding_all); + iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); + while (! iter_binding_live.destinations.empty()) { + TIntermNode* destination = iter_binding_live.destinations.back(); + iter_binding_live.destinations.pop_back(); + destination->traverse(&iter_binding_live); + } + + // sort entries by priority. see TVarEntryInfo::TOrderByPriority for info. + for (auto& var : inVarMap) { inVector.push_back(var); } + std::sort(inVector.begin(), inVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + for (auto& var : outVarMap) { outVector.push_back(var); } + std::sort(outVector.begin(), outVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + for (auto& var : uniformVarMap) { uniformVector.push_back(var); } + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + bool hadError = false; + TVarLiveMap* dummyUniformVarMap[EShLangCount] = {}; + TNotifyInOutAdaptor inOutNotify(stage, *resolver); + TNotifyUniformAdaptor uniformNotify(stage, *resolver); + TResolverUniformAdaptor uniformResolve(stage, *resolver, dummyUniformVarMap, infoSink, hadError); + TResolverInOutAdaptor inOutResolve(stage, *resolver, infoSink, hadError); + resolver->beginNotifications(stage); + std::for_each(inVector.begin(), inVector.end(), inOutNotify); + std::for_each(outVector.begin(), outVector.end(), inOutNotify); + std::for_each(uniformVector.begin(), uniformVector.end(), uniformNotify); + resolver->endNotifications(stage); + resolver->beginResolve(stage); + for (auto& var : inVector) { inOutResolve(var); } + std::for_each(inVector.begin(), inVector.end(), [&inVarMap](TVarLivePair p) { + auto at = inVarMap.find(p.second.symbol->getAccessName()); + if (at != inVarMap.end() && p.second.id == at->second.id) + at->second = p.second; + }); + for (auto& var : outVector) { inOutResolve(var); } + std::for_each(outVector.begin(), outVector.end(), [&outVarMap](TVarLivePair p) { + auto at = outVarMap.find(p.second.symbol->getAccessName()); + if (at != outVarMap.end() && p.second.id == at->second.id) + at->second = p.second; + }); + std::for_each(uniformVector.begin(), uniformVector.end(), uniformResolve); + std::for_each(uniformVector.begin(), uniformVector.end(), [&uniformVarMap](TVarLivePair p) { + auto at = uniformVarMap.find(p.second.symbol->getAccessName()); + if (at != uniformVarMap.end() && p.second.id == at->second.id) + at->second = p.second; + }); + resolver->endResolve(stage); + if (!hadError) { + TVarSetTraverser iter_iomap(intermediate, inVarMap, outVarMap, uniformVarMap); + root->traverse(&iter_iomap); + } + return !hadError; +} + +// Map I/O variables to provided offsets, and make bindings for +// unbound but live variables. +// +// Returns false if the input is too malformed to do this. +bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSink& infoSink, TIoMapResolver* resolver) { + bool somethingToDo = !intermediate.getResourceSetBinding().empty() || + intermediate.getAutoMapBindings() || + intermediate.getAutoMapLocations(); + + // Profile and version are use for symbol validate. + profile = intermediate.getProfile(); + version = intermediate.getVersion(); + + // Restrict the stricter condition to further check 'somethingToDo' only if 'somethingToDo' has not been set, reduce + // unnecessary or insignificant for-loop operation after 'somethingToDo' have been true. + for (int res = 0; (res < EResCount && !somethingToDo); ++res) { + somethingToDo = somethingToDo || (intermediate.getShiftBinding(TResourceType(res)) != 0) || + intermediate.hasShiftBindingForSet(TResourceType(res)); + } + if (! somethingToDo && resolver == nullptr) { + return true; + } + if (intermediate.getNumEntryPoints() != 1 || intermediate.isRecursive()) { + return false; + } + TIntermNode* root = intermediate.getTreeRoot(); + if (root == nullptr) { + return false; + } + // if no resolver is provided, use the default resolver with the given shifts and auto map settings + TDefaultGlslIoResolver defaultResolver(intermediate); + if (resolver == nullptr) { + resolver = &defaultResolver; + } + resolver->addStage(stage); + inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap(); + TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage], + *uniformVarMap[stage]); + TVarGatherTraverser iter_binding_live(intermediate, false, *inVarMaps[stage], *outVarMaps[stage], + *uniformVarMap[stage]); + root->traverse(&iter_binding_all); + iter_binding_live.pushFunction(intermediate.getEntryPointMangledName().c_str()); + while (! iter_binding_live.destinations.empty()) { + TIntermNode* destination = iter_binding_live.destinations.back(); + iter_binding_live.destinations.pop_back(); + destination->traverse(&iter_binding_live); + } + + TNotifyInOutAdaptor inOutNotify(stage, *resolver); + TNotifyUniformAdaptor uniformNotify(stage, *resolver); + // Resolve current stage input symbol location with previous stage output here, + // uniform symbol, ubo, ssbo and opaque symbols are per-program resource, + // will resolve uniform symbol location and ubo/ssbo/opaque binding in doMap() + resolver->beginNotifications(stage); + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), inOutNotify); + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), inOutNotify); + std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), uniformNotify); + resolver->endNotifications(stage); + TSlotCollector slotCollector(*resolver, infoSink); + resolver->beginCollect(stage); + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), slotCollector); + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), slotCollector); + std::for_each(uniformVarMap[stage]->begin(), uniformVarMap[stage]->end(), slotCollector); + resolver->endCollect(stage); + intermediates[stage] = &intermediate; + return !hadError; +} + +bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) { + resolver->endResolve(EShLangCount); + if (!hadError) { + //Resolve uniform location, ubo/ssbo/opaque bindings across stages + TResolverUniformAdaptor uniformResolve(EShLangCount, *resolver, uniformVarMap, infoSink, hadError); + TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError); + TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, + outVarMaps, uniformVarMap, hadError, profile, version); + TVarLiveVector uniformVector; + resolver->beginResolve(EShLangCount); + for (int stage = EShLangVertex; stage < EShLangCount; stage++) { + if (inVarMaps[stage] != nullptr) { + inOutResolve.setStage(EShLanguage(stage)); + for (auto& var : *(inVarMaps[stage])) { symbolValidater(var); } + for (auto& var : *(inVarMaps[stage])) { inOutResolve(var); } + for (auto& var : *(outVarMaps[stage])) { symbolValidater(var); } + for (auto& var : *(outVarMaps[stage])) { inOutResolve(var); } + } + if (uniformVarMap[stage] != nullptr) { + uniformResolve.setStage(EShLanguage(stage)); + for (auto& var : *(uniformVarMap[stage])) { uniformVector.push_back(var); } + } + } + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + for (auto& var : uniformVector) { symbolValidater(var); } + for (auto& var : uniformVector) { uniformResolve(var); } + std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + resolver->endResolve(EShLangCount); + for (size_t stage = 0; stage < EShLangCount; stage++) { + if (intermediates[stage] != nullptr) { + // traverse each stage, set new location to each input/output and unifom symbol, set new binding to + // ubo, ssbo and opaque symbols + TVarLiveMap** pUniformVarMap = uniformResolve.uniformVarMap; + std::for_each(uniformVector.begin(), uniformVector.end(), [pUniformVarMap, stage](TVarLivePair p) { + auto at = pUniformVarMap[stage]->find(p.second.symbol->getAccessName()); + if (at != pUniformVarMap[stage]->end() && at->second.id == p.second.id){ + int resolvedBinding = at->second.newBinding; + at->second = p.second; + if (resolvedBinding > 0) + at->second.newBinding = resolvedBinding; + } + }); + TVarSetTraverser iter_iomap(*intermediates[stage], *inVarMaps[stage], *outVarMaps[stage], + *uniformResolve.uniformVarMap[stage]); + intermediates[stage]->getTreeRoot()->traverse(&iter_iomap); + } + } + return !hadError; + } else { + return false; + } +} + +} // end namespace glslang + +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE diff --git a/third_party/glslang/glslang/MachineIndependent/iomapper.h b/third_party/glslang/glslang/MachineIndependent/iomapper.h new file mode 100644 index 0000000..7934c4a --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/iomapper.h @@ -0,0 +1,305 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +#ifndef _IOMAPPER_INCLUDED +#define _IOMAPPER_INCLUDED + +#include +#include "LiveTraverser.h" +#include +#include +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +class TInfoSink; + +namespace glslang { + +class TIntermediate; +struct TVarEntryInfo { + int id; + TIntermSymbol* symbol; + bool live; + int newBinding; + int newSet; + int newLocation; + int newComponent; + int newIndex; + EShLanguage stage; + struct TOrderById { + inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { return l.id < r.id; } + }; + + struct TOrderByPriority { + // ordering: + // 1) has both binding and set + // 2) has binding but no set + // 3) has no binding but set + // 4) has no binding and no set + inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { + const TQualifier& lq = l.symbol->getQualifier(); + const TQualifier& rq = r.symbol->getQualifier(); + + // simple rules: + // has binding gives 2 points + // has set gives 1 point + // who has the most points is more important. + int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0); + int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0); + + if (lPoints == rPoints) + return l.id < r.id; + return lPoints > rPoints; + } + }; +}; + +// Base class for shared TIoMapResolver services, used by several derivations. +struct TDefaultIoResolverBase : public glslang::TIoMapResolver { +public: + TDefaultIoResolverBase(const TIntermediate& intermediate); + typedef std::vector TSlotSet; + typedef std::unordered_map TSlotSetMap; + + // grow the reflection stage by stage + void notifyBinding(EShLanguage, TVarEntryInfo& /*ent*/) override {} + void notifyInOut(EShLanguage, TVarEntryInfo& /*ent*/) override {} + void beginNotifications(EShLanguage) override {} + void endNotifications(EShLanguage) override {} + void beginResolve(EShLanguage) override {} + void endResolve(EShLanguage) override {} + void beginCollect(EShLanguage) override {} + void endCollect(EShLanguage) override {} + void reserverResourceSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} + void reserverStorageSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} + int getBaseBinding(TResourceType res, unsigned int set) const; + const std::vector& getResourceSetBinding() const; + virtual TResourceType getResourceType(const glslang::TType& type) = 0; + bool doAutoBindingMapping() const; + bool doAutoLocationMapping() const; + TSlotSet::iterator findSlot(int set, int slot); + bool checkEmpty(int set, int slot); + bool validateInOut(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + int reserveSlot(int set, int slot, int size = 1); + int getFreeSlot(int set, int base, int size = 1); + int resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; + int resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + void addStage(EShLanguage stage) override { + if (stage < EShLangCount) + stageMask[stage] = true; + } + uint32_t computeTypeLocationSize(const TType& type, EShLanguage stage); + + TSlotSetMap slots; + bool hasError = false; + +protected: + TDefaultIoResolverBase(TDefaultIoResolverBase&); + TDefaultIoResolverBase& operator=(TDefaultIoResolverBase&); + const TIntermediate& intermediate; + int nextUniformLocation; + int nextInputLocation; + int nextOutputLocation; + bool stageMask[EShLangCount + 1]; + // Return descriptor set specific base if there is one, and the generic base otherwise. + int selectBaseBinding(int base, int descriptorSetBase) const { + return descriptorSetBase != -1 ? descriptorSetBase : base; + } + + static int getLayoutSet(const glslang::TType& type) { + if (type.getQualifier().hasSet()) + return type.getQualifier().layoutSet; + else + return 0; + } + + static bool isSamplerType(const glslang::TType& type) { + return type.getBasicType() == glslang::EbtSampler && type.getSampler().isPureSampler(); + } + + static bool isTextureType(const glslang::TType& type) { + return (type.getBasicType() == glslang::EbtSampler && + (type.getSampler().isTexture() || type.getSampler().isSubpass())); + } + + static bool isUboType(const glslang::TType& type) { + return type.getQualifier().storage == EvqUniform; + } + + static bool isImageType(const glslang::TType& type) { + return type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage(); + } + + static bool isSsboType(const glslang::TType& type) { + return type.getQualifier().storage == EvqBuffer; + } + + // Return true if this is a SRV (shader resource view) type: + static bool isSrvType(const glslang::TType& type) { + return isTextureType(type) || type.getQualifier().storage == EvqBuffer; + } + + // Return true if this is a UAV (unordered access view) type: + static bool isUavType(const glslang::TType& type) { + if (type.getQualifier().isReadOnly()) + return false; + return (type.getBasicType() == glslang::EbtSampler && type.getSampler().isImage()) || + (type.getQualifier().storage == EvqBuffer); + } +}; + +// Default I/O resolver for OpenGL +struct TDefaultGlslIoResolver : public TDefaultIoResolverBase { +public: + typedef std::map TVarSlotMap; // + typedef std::map TSlotMap; // + TDefaultGlslIoResolver(const TIntermediate& intermediate); + bool validateBinding(EShLanguage /*stage*/, TVarEntryInfo& /*ent*/) override { return true; } + TResourceType getResourceType(const glslang::TType& type) override; + int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; + int resolveUniformLocation(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override; + void beginResolve(EShLanguage /*stage*/) override; + void endResolve(EShLanguage stage) override; + void beginCollect(EShLanguage) override; + void endCollect(EShLanguage) override; + void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override; + void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) override; + // in/out symbol and uniform symbol are stored in the same resourceSlotMap, the storage key is used to identify each type of symbol. + // We use stage and storage qualifier to construct a storage key. it can help us identify the same storage resource used in different stage. + // if a resource is a program resource and we don't need know it usage stage, we can use same stage to build storage key. + // Note: both stage and type must less then 0xffff. + int buildStorageKey(EShLanguage stage, TStorageQualifier type) { + assert(static_cast(stage) <= 0x0000ffff && static_cast(type) <= 0x0000ffff); + return (stage << 16) | type; + } + +protected: + // Use for mark pre stage, to get more interface symbol information. + EShLanguage preStage; + // Use for mark current shader stage for resolver + EShLanguage currentStage; + // Slot map for storage resource(location of uniform and interface symbol) It's a program share slot + TSlotMap resourceSlotMap; + // Slot map for other resource(image, ubo, ssbo), It's a program share slot. + TSlotMap storageSlotMap; +}; + +typedef std::map TVarLiveMap; + +// override function "operator=", if a vector being sort, +// when use vc++, the sort function will call : +// pair& operator=(const pair<_Other1, _Other2>& _Right) +// { +// first = _Right.first; +// second = _Right.second; +// return (*this); +// } +// that will make a const type handing on left. +// override this function can avoid a compiler error. +// In the future, if the vc++ compiler can handle such a situation, +// this part of the code will be removed. +struct TVarLivePair : std::pair { + TVarLivePair(const std::pair& _Right) : pair(_Right.first, _Right.second) {} + TVarLivePair& operator=(const TVarLivePair& _Right) { + const_cast(first) = _Right.first; + second = _Right.second; + return (*this); + } + TVarLivePair(const TVarLivePair& src) : pair(src) { } +}; +typedef std::vector TVarLiveVector; + +// I/O mapper +class TIoMapper { +public: + TIoMapper() {} + virtual ~TIoMapper() {} + // grow the reflection stage by stage + bool virtual addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*); + bool virtual doMap(TIoMapResolver*, TInfoSink&) { return true; } +}; + +// I/O mapper for OpenGL +class TGlslIoMapper : public TIoMapper { +public: + TGlslIoMapper() { + memset(inVarMaps, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1)); + memset(outVarMaps, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1)); + memset(uniformVarMap, 0, sizeof(TVarLiveMap*) * (EShLangCount + 1)); + memset(intermediates, 0, sizeof(TIntermediate*) * (EShLangCount + 1)); + profile = ENoProfile; + version = 0; + } + virtual ~TGlslIoMapper() { + for (size_t stage = 0; stage < EShLangCount; stage++) { + if (inVarMaps[stage] != nullptr) { + delete inVarMaps[stage]; + inVarMaps[stage] = nullptr; + } + if (outVarMaps[stage] != nullptr) { + delete outVarMaps[stage]; + outVarMaps[stage] = nullptr; + } + if (uniformVarMap[stage] != nullptr) { + delete uniformVarMap[stage]; + uniformVarMap[stage] = nullptr; + } + if (intermediates[stage] != nullptr) + intermediates[stage] = nullptr; + } + } + // grow the reflection stage by stage + bool addStage(EShLanguage, TIntermediate&, TInfoSink&, TIoMapResolver*) override; + bool doMap(TIoMapResolver*, TInfoSink&) override; + TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], + *uniformVarMap[EShLangCount]; + TIntermediate* intermediates[EShLangCount]; + bool hadError = false; + EProfile profile; + int version; +}; + +} // end namespace glslang + +#endif // _IOMAPPER_INCLUDED + +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE diff --git a/third_party/glslang/glslang/MachineIndependent/limits.cpp b/third_party/glslang/glslang/MachineIndependent/limits.cpp new file mode 100644 index 0000000..51d9300 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/limits.cpp @@ -0,0 +1,200 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do sub tree walks for +// 1) inductive loop bodies to see if the inductive variable is modified +// 2) array-index expressions to see if they are "constant-index-expression" +// +// These are per Appendix A of ES 2.0: +// +// "Within the body of the loop, the loop index is not statically assigned to nor is it used as the +// argument to a function out or inout parameter." +// +// "The following are constant-index-expressions: +// - Constant expressions +// - Loop indices as defined in section 4 +// - Expressions composed of both of the above" +// +// N.B.: assuming the last rule excludes function calls +// + +#include "ParseHelper.h" + +namespace glslang { + +// +// The inductive loop-body traverser. +// +// Just look at things that might modify the loop index. +// + +class TInductiveTraverser : public TIntermTraverser { +public: + TInductiveTraverser(int id, TSymbolTable& st) + : loopId(id), symbolTable(st), bad(false) { } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual bool visitUnary(TVisit, TIntermUnary* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + + int loopId; // unique ID of the symbol that's the loop inductive variable + TSymbolTable& symbolTable; + bool bad; + TSourceLoc badLoc; + +protected: + TInductiveTraverser(TInductiveTraverser&); + TInductiveTraverser& operator=(TInductiveTraverser&); +}; + +// check binary operations for those modifying the loop index +bool TInductiveTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + if (node->modifiesState() && node->getLeft()->getAsSymbolNode() && + node->getLeft()->getAsSymbolNode()->getId() == loopId) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// check unary operations for those modifying the loop index +bool TInductiveTraverser::visitUnary(TVisit /* visit */, TIntermUnary* node) +{ + if (node->modifiesState() && node->getOperand()->getAsSymbolNode() && + node->getOperand()->getAsSymbolNode()->getId() == loopId) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// check function calls for arguments modifying the loop index +bool TInductiveTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (node->getOp() == EOpFunctionCall) { + // see if an out or inout argument is the loop index + const TIntermSequence& args = node->getSequence(); + for (int i = 0; i < (int)args.size(); ++i) { + if (args[i]->getAsSymbolNode() && args[i]->getAsSymbolNode()->getId() == loopId) { + TSymbol* function = symbolTable.find(node->getName()); + const TType* type = (*function->getAsFunction())[i].type; + if (type->getQualifier().storage == EvqOut || + type->getQualifier().storage == EvqInOut) { + bad = true; + badLoc = node->getLoc(); + } + } + } + } + + return true; +} + +// +// External function to call for loop check. +// +void TParseContext::inductiveLoopBodyCheck(TIntermNode* body, int loopId, TSymbolTable& symbolTable) +{ + TInductiveTraverser it(loopId, symbolTable); + + if (body == nullptr) + return; + + body->traverse(&it); + + if (it.bad) + error(it.badLoc, "inductive loop index modified", "limitations", ""); +} + +// +// The "constant-index-expression" tranverser. +// +// Just look at things that can form an index. +// + +class TIndexTraverser : public TIntermTraverser { +public: + TIndexTraverser(const TIdSetType& ids) : inductiveLoopIds(ids), bad(false) { } + virtual void visitSymbol(TIntermSymbol* symbol); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + const TIdSetType& inductiveLoopIds; + bool bad; + TSourceLoc badLoc; + +protected: + TIndexTraverser(TIndexTraverser&); + TIndexTraverser& operator=(TIndexTraverser&); +}; + +// make sure symbols are inductive-loop indexes +void TIndexTraverser::visitSymbol(TIntermSymbol* symbol) +{ + if (inductiveLoopIds.find(symbol->getId()) == inductiveLoopIds.end()) { + bad = true; + badLoc = symbol->getLoc(); + } +} + +// check for function calls, assuming they are bad; spec. doesn't really say +bool TIndexTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (node->getOp() == EOpFunctionCall) { + bad = true; + badLoc = node->getLoc(); + } + + return true; +} + +// +// External function to call for loop check. +// +void TParseContext::constantIndexExpressionCheck(TIntermNode* index) +{ +#ifndef GLSLANG_WEB + TIndexTraverser it(inductiveLoopIds); + + index->traverse(&it); + + if (it.bad) + error(it.badLoc, "Non-constant-index-expression", "limitations", ""); +#endif +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/linkValidate.cpp b/third_party/glslang/glslang/MachineIndependent/linkValidate.cpp new file mode 100755 index 0000000..1796fed --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/linkValidate.cpp @@ -0,0 +1,1781 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Do link-time merging and validation of intermediate representations. +// +// Basic model is that during compilation, each compilation unit (shader) is +// compiled into one TIntermediate instance. Then, at link time, multiple +// units for the same stage can be merged together, which can generate errors. +// Then, after all merging, a single instance of TIntermediate represents +// the whole stage. A final error check can be done on the resulting stage, +// even if no merging was done (i.e., the stage was only one compilation unit). +// + +#include "localintermediate.h" +#include "../Include/InfoSink.h" + +namespace glslang { + +// +// Link-time error emitter. +// +void TIntermediate::error(TInfoSink& infoSink, const char* message) +{ +#ifndef GLSLANG_WEB + infoSink.info.prefix(EPrefixError); + infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; +#endif + + ++numErrors; +} + +// Link-time warning. +void TIntermediate::warn(TInfoSink& infoSink, const char* message) +{ +#ifndef GLSLANG_WEB + infoSink.info.prefix(EPrefixWarning); + infoSink.info << "Linking " << StageName(language) << " stage: " << message << "\n"; +#endif +} + +// TODO: 4.4 offset/align: "Two blocks linked together in the same program with the same block +// name must have the exact same set of members qualified with offset and their integral-constant +// expression values must be the same, or a link-time error results." + +// +// Merge the information from 'unit' into 'this' +// +void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit) +{ +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + mergeCallGraphs(infoSink, unit); + mergeModes(infoSink, unit); + mergeTrees(infoSink, unit); +#endif +} + +void TIntermediate::mergeCallGraphs(TInfoSink& infoSink, TIntermediate& unit) +{ + if (unit.getNumEntryPoints() > 0) { + if (getNumEntryPoints() > 0) + error(infoSink, "can't handle multiple entry points per stage"); + else { + entryPointName = unit.getEntryPointName(); + entryPointMangledName = unit.getEntryPointMangledName(); + } + } + numEntryPoints += unit.getNumEntryPoints(); + + callGraph.insert(callGraph.end(), unit.callGraph.begin(), unit.callGraph.end()); +} + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +#define MERGE_MAX(member) member = std::max(member, unit.member) +#define MERGE_TRUE(member) if (unit.member) member = unit.member; + +void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit) +{ + if (language != unit.language) + error(infoSink, "stages must match when linking into a single stage"); + + if (getSource() == EShSourceNone) + setSource(unit.getSource()); + if (getSource() != unit.getSource()) + error(infoSink, "can't link compilation units from different source languages"); + + if (treeRoot == nullptr) { + profile = unit.profile; + version = unit.version; + requestedExtensions = unit.requestedExtensions; + } else { + if ((isEsProfile()) != (unit.isEsProfile())) + error(infoSink, "Cannot cross link ES and desktop profiles"); + else if (unit.profile == ECompatibilityProfile) + profile = ECompatibilityProfile; + version = std::max(version, unit.version); + requestedExtensions.insert(unit.requestedExtensions.begin(), unit.requestedExtensions.end()); + } + + MERGE_MAX(spvVersion.spv); + MERGE_MAX(spvVersion.vulkanGlsl); + MERGE_MAX(spvVersion.vulkan); + MERGE_MAX(spvVersion.openGl); + + numErrors += unit.getNumErrors(); + // Only one push_constant is allowed, mergeLinkerObjects() will ensure the push_constant + // is the same for all units. + if (numPushConstants > 1 || unit.numPushConstants > 1) + error(infoSink, "Only one push_constant block is allowed per stage"); + numPushConstants = std::min(numPushConstants + unit.numPushConstants, 1); + + if (unit.invocations != TQualifier::layoutNotSet) { + if (invocations == TQualifier::layoutNotSet) + invocations = unit.invocations; + else if (invocations != unit.invocations) + error(infoSink, "number of invocations must match between compilation units"); + } + + if (vertices == TQualifier::layoutNotSet) + vertices = unit.vertices; + else if (unit.vertices != TQualifier::layoutNotSet && vertices != unit.vertices) { + if (language == EShLangGeometry || language == EShLangMeshNV) + error(infoSink, "Contradictory layout max_vertices values"); + else if (language == EShLangTessControl) + error(infoSink, "Contradictory layout vertices values"); + else + assert(0); + } + if (primitives == TQualifier::layoutNotSet) + primitives = unit.primitives; + else if (primitives != unit.primitives) { + if (language == EShLangMeshNV) + error(infoSink, "Contradictory layout max_primitives values"); + else + assert(0); + } + + if (inputPrimitive == ElgNone) + inputPrimitive = unit.inputPrimitive; + else if (unit.inputPrimitive != ElgNone && inputPrimitive != unit.inputPrimitive) + error(infoSink, "Contradictory input layout primitives"); + + if (outputPrimitive == ElgNone) + outputPrimitive = unit.outputPrimitive; + else if (unit.outputPrimitive != ElgNone && outputPrimitive != unit.outputPrimitive) + error(infoSink, "Contradictory output layout primitives"); + + if (originUpperLeft != unit.originUpperLeft || pixelCenterInteger != unit.pixelCenterInteger) + error(infoSink, "gl_FragCoord redeclarations must match across shaders"); + + if (vertexSpacing == EvsNone) + vertexSpacing = unit.vertexSpacing; + else if (vertexSpacing != unit.vertexSpacing) + error(infoSink, "Contradictory input vertex spacing"); + + if (vertexOrder == EvoNone) + vertexOrder = unit.vertexOrder; + else if (vertexOrder != unit.vertexOrder) + error(infoSink, "Contradictory triangle ordering"); + + MERGE_TRUE(pointMode); + + for (int i = 0; i < 3; ++i) { + if (!localSizeNotDefault[i] && unit.localSizeNotDefault[i]) { + localSize[i] = unit.localSize[i]; + localSizeNotDefault[i] = true; + } + else if (localSize[i] != unit.localSize[i]) + error(infoSink, "Contradictory local size"); + + if (localSizeSpecId[i] == TQualifier::layoutNotSet) + localSizeSpecId[i] = unit.localSizeSpecId[i]; + else if (localSizeSpecId[i] != unit.localSizeSpecId[i]) + error(infoSink, "Contradictory local size specialization ids"); + } + + MERGE_TRUE(earlyFragmentTests); + MERGE_TRUE(postDepthCoverage); + + if (depthLayout == EldNone) + depthLayout = unit.depthLayout; + else if (depthLayout != unit.depthLayout) + error(infoSink, "Contradictory depth layouts"); + + MERGE_TRUE(depthReplacing); + MERGE_TRUE(hlslFunctionality1); + + blendEquations |= unit.blendEquations; + + MERGE_TRUE(xfbMode); + + for (size_t b = 0; b < xfbBuffers.size(); ++b) { + if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd) + xfbBuffers[b].stride = unit.xfbBuffers[b].stride; + else if (xfbBuffers[b].stride != unit.xfbBuffers[b].stride) + error(infoSink, "Contradictory xfb_stride"); + xfbBuffers[b].implicitStride = std::max(xfbBuffers[b].implicitStride, unit.xfbBuffers[b].implicitStride); + if (unit.xfbBuffers[b].contains64BitType) + xfbBuffers[b].contains64BitType = true; + if (unit.xfbBuffers[b].contains32BitType) + xfbBuffers[b].contains32BitType = true; + if (unit.xfbBuffers[b].contains16BitType) + xfbBuffers[b].contains16BitType = true; + // TODO: 4.4 link: enhanced layouts: compare ranges + } + + MERGE_TRUE(multiStream); + MERGE_TRUE(layoutOverrideCoverage); + MERGE_TRUE(geoPassthroughEXT); + + for (unsigned int i = 0; i < unit.shiftBinding.size(); ++i) { + if (unit.shiftBinding[i] > 0) + setShiftBinding((TResourceType)i, unit.shiftBinding[i]); + } + + for (unsigned int i = 0; i < unit.shiftBindingForSet.size(); ++i) { + for (auto it = unit.shiftBindingForSet[i].begin(); it != unit.shiftBindingForSet[i].end(); ++it) + setShiftBindingForSet((TResourceType)i, it->second, it->first); + } + + resourceSetBinding.insert(resourceSetBinding.end(), unit.resourceSetBinding.begin(), unit.resourceSetBinding.end()); + + MERGE_TRUE(autoMapBindings); + MERGE_TRUE(autoMapLocations); + MERGE_TRUE(invertY); + MERGE_TRUE(flattenUniformArrays); + MERGE_TRUE(useUnknownFormat); + MERGE_TRUE(hlslOffsets); + MERGE_TRUE(useStorageBuffer); + MERGE_TRUE(hlslIoMapping); + + // TODO: sourceFile + // TODO: sourceText + // TODO: processes + + MERGE_TRUE(needToLegalize); + MERGE_TRUE(binaryDoubleOutput); + MERGE_TRUE(usePhysicalStorageBuffer); +} + +// +// Merge the 'unit' AST into 'this' AST. +// That includes rationalizing the unique IDs, which were set up independently, +// and might have overlaps that are not the same symbol, or might have different +// IDs for what should be the same shared symbol. +// +void TIntermediate::mergeTrees(TInfoSink& infoSink, TIntermediate& unit) +{ + if (unit.treeRoot == nullptr) + return; + + if (treeRoot == nullptr) { + treeRoot = unit.treeRoot; + return; + } + + // Getting this far means we have two existing trees to merge... + numShaderRecordBlocks += unit.numShaderRecordBlocks; + numTaskNVBlocks += unit.numTaskNVBlocks; + + // Get the top-level globals of each unit + TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence(); + TIntermSequence& unitGlobals = unit.treeRoot->getAsAggregate()->getSequence(); + + // Get the linker-object lists + TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + const TIntermSequence& unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // Map by global name to unique ID to rationalize the same object having + // differing IDs in different trees. + TIdMaps idMaps; + int maxId; + seedIdMap(idMaps, maxId); + remapIds(idMaps, maxId + 1, unit); + + mergeBodies(infoSink, globals, unitGlobals); + mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects); + ioAccessed.insert(unit.ioAccessed.begin(), unit.ioAccessed.end()); +} + +#endif + +static const TString& getNameForIdMap(TIntermSymbol* symbol) +{ + TShaderInterface si = symbol->getType().getShaderInterface(); + if (si == EsiNone) + return symbol->getName(); + else + return symbol->getType().getTypeName(); +} + + + +// Traverser that seeds an ID map with all built-ins, and tracks the +// maximum ID used. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TBuiltInIdTraverser : public TIntermTraverser { +public: + TBuiltInIdTraverser(TIdMaps& idMaps) : idMaps(idMaps), maxId(0) { } + // If it's a built in, add it to the map. + // Track the max ID. + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + if (qualifier.builtIn != EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + idMaps[si][getNameForIdMap(symbol)] = symbol->getId(); + } + maxId = std::max(maxId, symbol->getId()); + } + int getMaxId() const { return maxId; } +protected: + TBuiltInIdTraverser(TBuiltInIdTraverser&); + TBuiltInIdTraverser& operator=(TBuiltInIdTraverser&); + TIdMaps& idMaps; + int maxId; +}; + +// Traverser that seeds an ID map with non-builtins. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TUserIdTraverser : public TIntermTraverser { +public: + TUserIdTraverser(TIdMaps& idMaps) : idMaps(idMaps) { } + // If its a non-built-in global, add it to the map. + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + if (qualifier.builtIn == EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + idMaps[si][getNameForIdMap(symbol)] = symbol->getId(); + } + } + +protected: + TUserIdTraverser(TUserIdTraverser&); + TUserIdTraverser& operator=(TUserIdTraverser&); + TIdMaps& idMaps; // over biggest id +}; + +// Initialize the the ID map with what we know of 'this' AST. +void TIntermediate::seedIdMap(TIdMaps& idMaps, int& maxId) +{ + // all built-ins everywhere need to align on IDs and contribute to the max ID + TBuiltInIdTraverser builtInIdTraverser(idMaps); + treeRoot->traverse(&builtInIdTraverser); + maxId = builtInIdTraverser.getMaxId(); + + // user variables in the linker object list need to align on ids + TUserIdTraverser userIdTraverser(idMaps); + findLinkerObjects()->traverse(&userIdTraverser); +} + +// Traverser to map an AST ID to what was known from the seeding AST. +// (It would be nice to put this in a function, but that causes warnings +// on having no bodies for the copy-constructor/operator=.) +class TRemapIdTraverser : public TIntermTraverser { +public: + TRemapIdTraverser(const TIdMaps& idMaps, int idShift) : idMaps(idMaps), idShift(idShift) { } + // Do the mapping: + // - if the same symbol, adopt the 'this' ID + // - otherwise, ensure a unique ID by shifting to a new space + virtual void visitSymbol(TIntermSymbol* symbol) + { + const TQualifier& qualifier = symbol->getType().getQualifier(); + bool remapped = false; + if (qualifier.isLinkable() || qualifier.builtIn != EbvNone) { + TShaderInterface si = symbol->getType().getShaderInterface(); + auto it = idMaps[si].find(getNameForIdMap(symbol)); + if (it != idMaps[si].end()) { + symbol->changeId(it->second); + remapped = true; + } + } + if (!remapped) + symbol->changeId(symbol->getId() + idShift); + } +protected: + TRemapIdTraverser(TRemapIdTraverser&); + TRemapIdTraverser& operator=(TRemapIdTraverser&); + const TIdMaps& idMaps; + int idShift; +}; + +void TIntermediate::remapIds(const TIdMaps& idMaps, int idShift, TIntermediate& unit) +{ + // Remap all IDs to either share or be unique, as dictated by the idMap and idShift. + TRemapIdTraverser idTraverser(idMaps, idShift); + unit.getTreeRoot()->traverse(&idTraverser); +} + +// +// Merge the function bodies and global-level initializers from unitGlobals into globals. +// Will error check duplication of function bodies for the same signature. +// +void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, const TIntermSequence& unitGlobals) +{ + // TODO: link-time performance: Processing in alphabetical order will be faster + + // Error check the global objects, not including the linker objects + for (unsigned int child = 0; child < globals.size() - 1; ++child) { + for (unsigned int unitChild = 0; unitChild < unitGlobals.size() - 1; ++unitChild) { + TIntermAggregate* body = globals[child]->getAsAggregate(); + TIntermAggregate* unitBody = unitGlobals[unitChild]->getAsAggregate(); + if (body && unitBody && body->getOp() == EOpFunction && unitBody->getOp() == EOpFunction && body->getName() == unitBody->getName()) { + error(infoSink, "Multiple function bodies in multiple compilation units for the same signature in the same stage:"); + infoSink.info << " " << globals[child]->getAsAggregate()->getName() << "\n"; + } + } + } + + // Merge the global objects, just in front of the linker objects + globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1); +} + +// +// Merge the linker objects from unitLinkerObjects into linkerObjects. +// Duplication is expected and filtered out, but contradictions are an error. +// +void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects) +{ + // Error check and merge the linker objects (duplicates should not be created) + std::size_t initialNumLinkerObjects = linkerObjects.size(); + for (unsigned int unitLinkObj = 0; unitLinkObj < unitLinkerObjects.size(); ++unitLinkObj) { + bool merge = true; + for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) { + TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode(); + TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); + assert(symbol && unitSymbol); + + bool isSameSymbol = false; + // If they are both blocks in the same shader interface, + // match by the block-name, not the identifier name. + if (symbol->getType().getBasicType() == EbtBlock && unitSymbol->getType().getBasicType() == EbtBlock) { + if (symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) { + isSameSymbol = symbol->getType().getTypeName() == unitSymbol->getType().getTypeName(); + } + } + else if (symbol->getName() == unitSymbol->getName()) + isSameSymbol = true; + + if (isSameSymbol) { + // filter out copy + merge = false; + + // but if one has an initializer and the other does not, update + // the initializer + if (symbol->getConstArray().empty() && ! unitSymbol->getConstArray().empty()) + symbol->setConstArray(unitSymbol->getConstArray()); + + // Similarly for binding + if (! symbol->getQualifier().hasBinding() && unitSymbol->getQualifier().hasBinding()) + symbol->getQualifier().layoutBinding = unitSymbol->getQualifier().layoutBinding; + + // Update implicit array sizes + mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType()); + + // Check for consistent types/qualification/initializers etc. + mergeErrorCheck(infoSink, *symbol, *unitSymbol, false); + } + // If different symbols, verify they arn't push_constant since there can only be one per stage + else if (symbol->getQualifier().isPushConstant() && unitSymbol->getQualifier().isPushConstant()) + error(infoSink, "Only one push_constant block is allowed per stage"); + } + if (merge) + linkerObjects.push_back(unitLinkerObjects[unitLinkObj]); + } +} + +// TODO 4.5 link functionality: cull distance array size checking + +// Recursively merge the implicit array sizes through the objects' respective type trees. +void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType) +{ + if (type.isUnsizedArray()) { + if (unitType.isUnsizedArray()) { + type.updateImplicitArraySize(unitType.getImplicitArraySize()); + if (unitType.isArrayVariablyIndexed()) + type.setArrayVariablyIndexed(); + } else if (unitType.isSizedArray()) + type.changeOuterArraySize(unitType.getOuterArraySize()); + } + + // Type mismatches are caught and reported after this, just be careful for now. + if (! type.isStruct() || ! unitType.isStruct() || type.getStruct()->size() != unitType.getStruct()->size()) + return; + + for (int i = 0; i < (int)type.getStruct()->size(); ++i) + mergeImplicitArraySizes(*(*type.getStruct())[i].type, *(*unitType.getStruct())[i].type); +} + +// +// Compare two global objects from two compilation units and see if they match +// well enough. Rules can be different for intra- vs. cross-stage matching. +// +// This function only does one of intra- or cross-stage matching per call. +// +void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, bool crossStage) +{ +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + bool writeTypeComparison = false; + + // Types have to match + if (symbol.getType() != unitSymbol.getType()) { + // but, we make an exception if one is an implicit array and the other is sized + if (! (symbol.getType().isArray() && unitSymbol.getType().isArray() && + symbol.getType().sameElementType(unitSymbol.getType()) && + (symbol.getType().isUnsizedArray() || unitSymbol.getType().isUnsizedArray()))) { + error(infoSink, "Types must match:"); + writeTypeComparison = true; + } + } + + // Qualifiers have to (almost) match + + // Storage... + if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage) { + error(infoSink, "Storage qualifiers must match:"); + writeTypeComparison = true; + } + + // Uniform and buffer blocks must either both have an instance name, or + // must both be anonymous. The names don't need to match though. + if (symbol.getQualifier().isUniformOrBuffer() && + (IsAnonymous(symbol.getName()) != IsAnonymous(unitSymbol.getName()))) { + error(infoSink, "Matched Uniform or Storage blocks must all be anonymous," + " or all be named:"); + writeTypeComparison = true; + } + + if (symbol.getQualifier().storage == unitSymbol.getQualifier().storage && + (IsAnonymous(symbol.getName()) != IsAnonymous(unitSymbol.getName()) || + (!IsAnonymous(symbol.getName()) && symbol.getName() != unitSymbol.getName()))) { + warn(infoSink, "Matched shader interfaces are using different instance names."); + writeTypeComparison = true; + } + + // Precision... + if (symbol.getQualifier().precision != unitSymbol.getQualifier().precision) { + error(infoSink, "Precision qualifiers must match:"); + writeTypeComparison = true; + } + + // Invariance... + if (! crossStage && symbol.getQualifier().invariant != unitSymbol.getQualifier().invariant) { + error(infoSink, "Presence of invariant qualifier must match:"); + writeTypeComparison = true; + } + + // Precise... + if (! crossStage && symbol.getQualifier().isNoContraction() != unitSymbol.getQualifier().isNoContraction()) { + error(infoSink, "Presence of precise qualifier must match:"); + writeTypeComparison = true; + } + + // Auxiliary and interpolation... + if (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid || + symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth || + symbol.getQualifier().flat != unitSymbol.getQualifier().flat || + symbol.getQualifier().isSample()!= unitSymbol.getQualifier().isSample() || + symbol.getQualifier().isPatch() != unitSymbol.getQualifier().isPatch() || + symbol.getQualifier().isNonPerspective() != unitSymbol.getQualifier().isNonPerspective()) { + error(infoSink, "Interpolation and auxiliary storage qualifiers must match:"); + writeTypeComparison = true; + } + + // Memory... + if (symbol.getQualifier().coherent != unitSymbol.getQualifier().coherent || + symbol.getQualifier().devicecoherent != unitSymbol.getQualifier().devicecoherent || + symbol.getQualifier().queuefamilycoherent != unitSymbol.getQualifier().queuefamilycoherent || + symbol.getQualifier().workgroupcoherent != unitSymbol.getQualifier().workgroupcoherent || + symbol.getQualifier().subgroupcoherent != unitSymbol.getQualifier().subgroupcoherent || + symbol.getQualifier().shadercallcoherent!= unitSymbol.getQualifier().shadercallcoherent || + symbol.getQualifier().nonprivate != unitSymbol.getQualifier().nonprivate || + symbol.getQualifier().volatil != unitSymbol.getQualifier().volatil || + symbol.getQualifier().restrict != unitSymbol.getQualifier().restrict || + symbol.getQualifier().readonly != unitSymbol.getQualifier().readonly || + symbol.getQualifier().writeonly != unitSymbol.getQualifier().writeonly) { + error(infoSink, "Memory qualifiers must match:"); + writeTypeComparison = true; + } + + // Layouts... + // TODO: 4.4 enhanced layouts: Generalize to include offset/align: current spec + // requires separate user-supplied offset from actual computed offset, but + // current implementation only has one offset. + if (symbol.getQualifier().layoutMatrix != unitSymbol.getQualifier().layoutMatrix || + symbol.getQualifier().layoutPacking != unitSymbol.getQualifier().layoutPacking || + symbol.getQualifier().layoutLocation != unitSymbol.getQualifier().layoutLocation || + symbol.getQualifier().layoutComponent != unitSymbol.getQualifier().layoutComponent || + symbol.getQualifier().layoutIndex != unitSymbol.getQualifier().layoutIndex || + symbol.getQualifier().layoutBinding != unitSymbol.getQualifier().layoutBinding || + (symbol.getQualifier().hasBinding() && (symbol.getQualifier().layoutOffset != unitSymbol.getQualifier().layoutOffset))) { + error(infoSink, "Layout qualification must match:"); + writeTypeComparison = true; + } + + // Initializers have to match, if both are present, and if we don't already know the types don't match + if (! writeTypeComparison) { + if (! symbol.getConstArray().empty() && ! unitSymbol.getConstArray().empty()) { + if (symbol.getConstArray() != unitSymbol.getConstArray()) { + error(infoSink, "Initializers must match:"); + infoSink.info << " " << symbol.getName() << "\n"; + } + } + } + + if (writeTypeComparison) { + infoSink.info << " " << symbol.getName() << ": \"" << symbol.getType().getCompleteString() << "\" versus "; + if (symbol.getName() != unitSymbol.getName()) + infoSink.info << unitSymbol.getName() << ": "; + + infoSink.info << "\"" << unitSymbol.getType().getCompleteString() << "\"\n"; + } +#endif +} + +// +// Do final link-time error checking of a complete (merged) intermediate representation. +// (Much error checking was done during merging). +// +// Also, lock in defaults of things not set, including array sizes. +// +void TIntermediate::finalCheck(TInfoSink& infoSink, bool keepUncalled) +{ + if (getTreeRoot() == nullptr) + return; + + if (numEntryPoints < 1) { + if (getSource() == EShSourceGlsl) + error(infoSink, "Missing entry point: Each stage requires one entry point"); + else + warn(infoSink, "Entry point not found"); + } + + // recursion and missing body checking + checkCallGraphCycles(infoSink); + checkCallGraphBodies(infoSink, keepUncalled); + + // overlap/alias/missing I/O, etc. + inOutLocationCheck(infoSink); + +#ifndef GLSLANG_WEB + if (getNumPushConstants() > 1) + error(infoSink, "Only one push_constant block is allowed per stage"); + + // invocations + if (invocations == TQualifier::layoutNotSet) + invocations = 1; + + if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipVertex")) + error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipVertex (gl_ClipDistance is preferred)"); + if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_ClipVertex")) + error(infoSink, "Can only use one of gl_CullDistance or gl_ClipVertex (gl_ClipDistance is preferred)"); + + if (userOutputUsed() && (inIoAccessed("gl_FragColor") || inIoAccessed("gl_FragData"))) + error(infoSink, "Cannot use gl_FragColor or gl_FragData when using user-defined outputs"); + if (inIoAccessed("gl_FragColor") && inIoAccessed("gl_FragData")) + error(infoSink, "Cannot use both gl_FragColor and gl_FragData"); + + for (size_t b = 0; b < xfbBuffers.size(); ++b) { + if (xfbBuffers[b].contains64BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 8); + else if (xfbBuffers[b].contains32BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 4); + else if (xfbBuffers[b].contains16BitType) + RoundToPow2(xfbBuffers[b].implicitStride, 2); + + // "It is a compile-time or link-time error to have + // any xfb_offset that overflows xfb_stride, whether stated on declarations before or after the xfb_stride, or + // in different compilation units. While xfb_stride can be declared multiple times for the same buffer, it is a + // compile-time or link-time error to have different values specified for the stride for the same buffer." + if (xfbBuffers[b].stride != TQualifier::layoutXfbStrideEnd && xfbBuffers[b].implicitStride > xfbBuffers[b].stride) { + error(infoSink, "xfb_stride is too small to hold all buffer entries:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << ", minimum stride needed: " << xfbBuffers[b].implicitStride << "\n"; + } + if (xfbBuffers[b].stride == TQualifier::layoutXfbStrideEnd) + xfbBuffers[b].stride = xfbBuffers[b].implicitStride; + + // "If the buffer is capturing any + // outputs with double-precision or 64-bit integer components, the stride must be a multiple of 8, otherwise it must be a + // multiple of 4, or a compile-time or link-time error results." + if (xfbBuffers[b].contains64BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 8)) { + error(infoSink, "xfb_stride must be multiple of 8 for buffer holding a double or 64-bit integer:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } else if (xfbBuffers[b].contains32BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 4)) { + error(infoSink, "xfb_stride must be multiple of 4:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } + // "If the buffer is capturing any + // outputs with half-precision or 16-bit integer components, the stride must be a multiple of 2" + else if (xfbBuffers[b].contains16BitType && ! IsMultipleOfPow2(xfbBuffers[b].stride, 2)) { + error(infoSink, "xfb_stride must be multiple of 2 for buffer holding a half float or 16-bit integer:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", xfb_stride " << xfbBuffers[b].stride << "\n"; + } + + // "The resulting stride (implicit or explicit), when divided by 4, must be less than or equal to the + // implementation-dependent constant gl_MaxTransformFeedbackInterleavedComponents." + if (xfbBuffers[b].stride > (unsigned int)(4 * resources->maxTransformFeedbackInterleavedComponents)) { + error(infoSink, "xfb_stride is too large:"); + infoSink.info.prefix(EPrefixError); + infoSink.info << " xfb_buffer " << (unsigned int)b << ", components (1/4 stride) needed are " << xfbBuffers[b].stride/4 << ", gl_MaxTransformFeedbackInterleavedComponents is " << resources->maxTransformFeedbackInterleavedComponents << "\n"; + } + } + + switch (language) { + case EShLangVertex: + break; + case EShLangTessControl: + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify an output layout(vertices=...)"); + break; + case EShLangTessEvaluation: + if (getSource() == EShSourceGlsl) { + if (inputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an input layout primitive"); + if (vertexSpacing == EvsNone) + vertexSpacing = EvsEqual; + if (vertexOrder == EvoNone) + vertexOrder = EvoCcw; + } + break; + case EShLangGeometry: + if (inputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an input layout primitive"); + if (outputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an output layout primitive"); + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); + break; + case EShLangFragment: + // for GL_ARB_post_depth_coverage, EarlyFragmentTest is set automatically in + // ParseHelper.cpp. So if we reach here, this must be GL_EXT_post_depth_coverage + // requiring explicit early_fragment_tests + if (getPostDepthCoverage() && !getEarlyFragmentTests()) + error(infoSink, "post_depth_coverage requires early_fragment_tests"); + break; + case EShLangCompute: + break; + case EShLangRayGen: + case EShLangIntersect: + case EShLangAnyHit: + case EShLangClosestHit: + case EShLangMiss: + case EShLangCallable: + if (numShaderRecordBlocks > 1) + error(infoSink, "Only one shaderRecordNV buffer block is allowed per stage"); + break; + case EShLangMeshNV: + // NV_mesh_shader doesn't allow use of both single-view and per-view builtins. + if (inIoAccessed("gl_Position") && inIoAccessed("gl_PositionPerViewNV")) + error(infoSink, "Can only use one of gl_Position or gl_PositionPerViewNV"); + if (inIoAccessed("gl_ClipDistance") && inIoAccessed("gl_ClipDistancePerViewNV")) + error(infoSink, "Can only use one of gl_ClipDistance or gl_ClipDistancePerViewNV"); + if (inIoAccessed("gl_CullDistance") && inIoAccessed("gl_CullDistancePerViewNV")) + error(infoSink, "Can only use one of gl_CullDistance or gl_CullDistancePerViewNV"); + if (inIoAccessed("gl_Layer") && inIoAccessed("gl_LayerPerViewNV")) + error(infoSink, "Can only use one of gl_Layer or gl_LayerPerViewNV"); + if (inIoAccessed("gl_ViewportMask") && inIoAccessed("gl_ViewportMaskPerViewNV")) + error(infoSink, "Can only use one of gl_ViewportMask or gl_ViewportMaskPerViewNV"); + if (outputPrimitive == ElgNone) + error(infoSink, "At least one shader must specify an output layout primitive"); + if (vertices == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_vertices = value)"); + if (primitives == TQualifier::layoutNotSet) + error(infoSink, "At least one shader must specify a layout(max_primitives = value)"); + // fall through + case EShLangTaskNV: + if (numTaskNVBlocks > 1) + error(infoSink, "Only one taskNV interface block is allowed per shader"); + break; + default: + error(infoSink, "Unknown Stage."); + break; + } + + // Process the tree for any node-specific work. + class TFinalLinkTraverser : public TIntermTraverser { + public: + TFinalLinkTraverser() { } + virtual ~TFinalLinkTraverser() { } + + virtual void visitSymbol(TIntermSymbol* symbol) + { + // Implicitly size arrays. + // If an unsized array is left as unsized, it effectively + // becomes run-time sized. + symbol->getWritableType().adoptImplicitArraySizes(false); + } + } finalLinkTraverser; + + treeRoot->traverse(&finalLinkTraverser); +#endif +} + +// +// See if the call graph contains any static recursion, which is disallowed +// by the specification. +// +void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink) +{ + // Clear fields we'll use for this. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + call->visited = false; + call->currentPath = false; + call->errorGiven = false; + } + + // + // Loop, looking for a new connected subgraph. One subgraph is handled per loop iteration. + // + + TCall* newRoot; + do { + // See if we have unvisited parts of the graph. + newRoot = 0; + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (! call->visited) { + newRoot = &(*call); + break; + } + } + + // If not, we are done. + if (! newRoot) + break; + + // Otherwise, we found a new subgraph, process it: + // See what all can be reached by this new root, and if any of + // that is recursive. This is done by depth-first traversals, seeing + // if a new call is found that was already in the currentPath (a back edge), + // thereby detecting recursion. + std::list stack; + newRoot->currentPath = true; // currentPath will be true iff it is on the stack + stack.push_back(newRoot); + while (! stack.empty()) { + // get a caller + TCall* call = stack.back(); + + // Add to the stack just one callee. + // This algorithm always terminates, because only !visited and !currentPath causes a push + // and all pushes change currentPath to true, and all pops change visited to true. + TGraph::iterator child = callGraph.begin(); + for (; child != callGraph.end(); ++child) { + + // If we already visited this node, its whole subgraph has already been processed, so skip it. + if (child->visited) + continue; + + if (call->callee == child->caller) { + if (child->currentPath) { + // Then, we found a back edge + if (! child->errorGiven) { + error(infoSink, "Recursion detected:"); + infoSink.info << " " << call->callee << " calling " << child->callee << "\n"; + child->errorGiven = true; + recursive = true; + } + } else { + child->currentPath = true; + stack.push_back(&(*child)); + break; + } + } + } + if (child == callGraph.end()) { + // no more callees, we bottomed out, never look at this node again + stack.back()->currentPath = false; + stack.back()->visited = true; + stack.pop_back(); + } + } // end while, meaning nothing left to process in this subtree + + } while (newRoot); // redundant loop check; should always exit via the 'break' above +} + +// +// See which functions are reachable from the entry point and which have bodies. +// Reachable ones with missing bodies are errors. +// Unreachable bodies are dead code. +// +void TIntermediate::checkCallGraphBodies(TInfoSink& infoSink, bool keepUncalled) +{ + // Clear fields we'll use for this. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + call->visited = false; + call->calleeBodyPosition = -1; + } + + // The top level of the AST includes function definitions (bodies). + // Compare these to function calls in the call graph. + // We'll end up knowing which have bodies, and if so, + // how to map the call-graph node to the location in the AST. + TIntermSequence &functionSequence = getTreeRoot()->getAsAggregate()->getSequence(); + std::vector reachable(functionSequence.size(), true); // so that non-functions are reachable + for (int f = 0; f < (int)functionSequence.size(); ++f) { + glslang::TIntermAggregate* node = functionSequence[f]->getAsAggregate(); + if (node && (node->getOp() == glslang::EOpFunction)) { + if (node->getName().compare(getEntryPointMangledName().c_str()) != 0) + reachable[f] = false; // so that function bodies are unreachable, until proven otherwise + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->callee == node->getName()) + call->calleeBodyPosition = f; + } + } + } + + // Start call-graph traversal by visiting the entry point nodes. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->caller.compare(getEntryPointMangledName().c_str()) == 0) + call->visited = true; + } + + // Propagate 'visited' through the call-graph to every part of the graph it + // can reach (seeded with the entry-point setting above). + bool changed; + do { + changed = false; + for (auto call1 = callGraph.begin(); call1 != callGraph.end(); ++call1) { + if (call1->visited) { + for (TGraph::iterator call2 = callGraph.begin(); call2 != callGraph.end(); ++call2) { + if (! call2->visited) { + if (call1->callee == call2->caller) { + changed = true; + call2->visited = true; + } + } + } + } + } + } while (changed); + + // Any call-graph node set to visited but without a callee body is an error. + for (TGraph::iterator call = callGraph.begin(); call != callGraph.end(); ++call) { + if (call->visited) { + if (call->calleeBodyPosition == -1) { + error(infoSink, "No function definition (body) found: "); + infoSink.info << " " << call->callee << "\n"; + } else + reachable[call->calleeBodyPosition] = true; + } + } + + // Bodies in the AST not reached by the call graph are dead; + // clear them out, since they can't be reached and also can't + // be translated further due to possibility of being ill defined. + if (! keepUncalled) { + for (int f = 0; f < (int)functionSequence.size(); ++f) { + if (! reachable[f]) + functionSequence[f] = nullptr; + } + functionSequence.erase(std::remove(functionSequence.begin(), functionSequence.end(), nullptr), functionSequence.end()); + } +} + +// +// Satisfy rules for location qualifiers on inputs and outputs +// +void TIntermediate::inOutLocationCheck(TInfoSink& infoSink) +{ + // ES 3.0 requires all outputs to have location qualifiers if there is more than one output + bool fragOutWithNoLocation = false; + int numFragOut = 0; + + // TODO: linker functionality: location collision checking + + TIntermSequence& linkObjects = findLinkerObjects()->getSequence(); + for (size_t i = 0; i < linkObjects.size(); ++i) { + const TType& type = linkObjects[i]->getAsTyped()->getType(); + const TQualifier& qualifier = type.getQualifier(); + if (language == EShLangFragment) { + if (qualifier.storage == EvqVaryingOut && qualifier.builtIn == EbvNone) { + ++numFragOut; + if (!qualifier.hasAnyLocation()) + fragOutWithNoLocation = true; + } + } + } + + if (isEsProfile()) { + if (numFragOut > 1 && fragOutWithNoLocation) + error(infoSink, "when more than one fragment shader output, all must have location qualifiers"); + } +} + +TIntermAggregate* TIntermediate::findLinkerObjects() const +{ + // Get the top-level globals + TIntermSequence& globals = treeRoot->getAsAggregate()->getSequence(); + + // Get the last member of the sequences, expected to be the linker-object lists + assert(globals.back()->getAsAggregate()->getOp() == EOpLinkerObjects); + + return globals.back()->getAsAggregate(); +} + +// See if a variable was both a user-declared output and used. +// Note: the spec discusses writing to one, but this looks at read or write, which +// is more useful, and perhaps the spec should be changed to reflect that. +bool TIntermediate::userOutputUsed() const +{ + const TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + + bool found = false; + for (size_t i = 0; i < linkerObjects.size(); ++i) { + const TIntermSymbol& symbolNode = *linkerObjects[i]->getAsSymbolNode(); + if (symbolNode.getQualifier().storage == EvqVaryingOut && + symbolNode.getName().compare(0, 3, "gl_") != 0 && + inIoAccessed(symbolNode.getName())) { + found = true; + break; + } + } + + return found; +} + +// Accumulate locations used for inputs, outputs, and uniforms, and check for collisions +// as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +// typeCollision is set to true if there is no direct collision, but the types in the same location +// are different. +// +int TIntermediate::addUsedLocation(const TQualifier& qualifier, const TType& type, bool& typeCollision) +{ + typeCollision = false; + + int set; + if (qualifier.isPipeInput()) + set = 0; + else if (qualifier.isPipeOutput()) + set = 1; + else if (qualifier.storage == EvqUniform) + set = 2; + else if (qualifier.storage == EvqBuffer) + set = 3; + else + return -1; + + int size; + if (qualifier.isUniformOrBuffer() || qualifier.isTaskMemory()) { + if (type.isSizedArray()) + size = type.getCumulativeArraySize(); + else + size = 1; + } else { + // Strip off the outer array dimension for those having an extra one. + if (type.isArray() && qualifier.isArrayedIo(language)) { + TType elementType(type, 0); + size = computeTypeLocationSize(elementType, language); + } else + size = computeTypeLocationSize(type, language); + } + + // Locations, and components within locations. + // + // Almost always, dealing with components means a single location is involved. + // The exception is a dvec3. From the spec: + // + // "A dvec3 will consume all four components of the first location and components 0 and 1 of + // the second location. This leaves components 2 and 3 available for other component-qualified + // declarations." + // + // That means, without ever mentioning a component, a component range + // for a different location gets specified, if it's not a vertex shader input. (!) + // (A vertex shader input will show using only one location, even for a dvec3/4.) + // + // So, for the case of dvec3, we need two independent ioRanges. + + int collision = -1; // no collision +#ifndef GLSLANG_WEB + if (size == 2 && type.getBasicType() == EbtDouble && type.getVectorSize() == 3 && + (qualifier.isPipeInput() || qualifier.isPipeOutput())) { + // Dealing with dvec3 in/out split across two locations. + // Need two io-ranges. + // The case where the dvec3 doesn't start at component 0 was previously caught as overflow. + + // First range: + TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation); + TRange componentRange(0, 3); + TIoRange range(locationRange, componentRange, type.getBasicType(), 0); + + // check for collisions + collision = checkLocationRange(set, range, type, typeCollision); + if (collision < 0) { + usedIo[set].push_back(range); + + // Second range: + TRange locationRange2(qualifier.layoutLocation + 1, qualifier.layoutLocation + 1); + TRange componentRange2(0, 1); + TIoRange range2(locationRange2, componentRange2, type.getBasicType(), 0); + + // check for collisions + collision = checkLocationRange(set, range2, type, typeCollision); + if (collision < 0) + usedIo[set].push_back(range2); + } + } else +#endif + { + // Not a dvec3 in/out split across two locations, generic path. + // Need a single IO-range block. + + TRange locationRange(qualifier.layoutLocation, qualifier.layoutLocation + size - 1); + TRange componentRange(0, 3); + if (qualifier.hasComponent() || type.getVectorSize() > 0) { + int consumedComponents = type.getVectorSize() * (type.getBasicType() == EbtDouble ? 2 : 1); + if (qualifier.hasComponent()) + componentRange.start = qualifier.layoutComponent; + componentRange.last = componentRange.start + consumedComponents - 1; + } + + // combine location and component ranges + TIoRange range(locationRange, componentRange, type.getBasicType(), qualifier.hasIndex() ? qualifier.getIndex() : 0); + + // check for collisions, except for vertex inputs on desktop targeting OpenGL + if (! (!isEsProfile() && language == EShLangVertex && qualifier.isPipeInput()) || spvVersion.vulkan > 0) + collision = checkLocationRange(set, range, type, typeCollision); + + if (collision < 0) + usedIo[set].push_back(range); + } + + return collision; +} + +// Compare a new (the passed in) 'range' against the existing set, and see +// if there are any collisions. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::checkLocationRange(int set, const TIoRange& range, const TType& type, bool& typeCollision) +{ + for (size_t r = 0; r < usedIo[set].size(); ++r) { + if (range.overlap(usedIo[set][r])) { + // there is a collision; pick one + return std::max(range.location.start, usedIo[set][r].location.start); + } else if (range.location.overlap(usedIo[set][r].location) && type.getBasicType() != usedIo[set][r].basicType) { + // aliased-type mismatch + typeCollision = true; + return std::max(range.location.start, usedIo[set][r].location.start); + } + } + + return -1; // no collision +} + +// Accumulate bindings and offsets, and check for collisions +// as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::addUsedOffsets(int binding, int offset, int numOffsets) +{ + TRange bindingRange(binding, binding); + TRange offsetRange(offset, offset + numOffsets - 1); + TOffsetRange range(bindingRange, offsetRange); + + // check for collisions, except for vertex inputs on desktop + for (size_t r = 0; r < usedAtomics.size(); ++r) { + if (range.overlap(usedAtomics[r])) { + // there is a collision; pick one + return std::max(offset, usedAtomics[r].offset.start); + } + } + + usedAtomics.push_back(range); + + return -1; // no collision +} + +// Accumulate used constant_id values. +// +// Return false is one was already used. +bool TIntermediate::addUsedConstantId(int id) +{ + if (usedConstantId.find(id) != usedConstantId.end()) + return false; + + usedConstantId.insert(id); + + return true; +} + +// Recursively figure out how many locations are used up by an input or output type. +// Return the size of type, as measured by "locations". +int TIntermediate::computeTypeLocationSize(const TType& type, EShLanguage stage) +{ + // "If the declared input is an array of size n and each element takes m locations, it will be assigned m * n + // consecutive locations..." + if (type.isArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + // TODO: are there valid cases of having an unsized array with a location? If so, running this code too early. + TType elementType(type, 0); + if (type.isSizedArray() && !type.getQualifier().isPerView()) + return type.getOuterArraySize() * computeTypeLocationSize(elementType, stage); + else { +#ifndef GLSLANG_WEB + // unset perViewNV attributes for arrayed per-view outputs: "perviewNV vec4 v[MAX_VIEWS][3];" + elementType.getQualifier().perViewNV = false; +#endif + return computeTypeLocationSize(elementType, stage); + } + } + + // "The locations consumed by block and structure members are determined by applying the rules above + // recursively..." + if (type.isStruct()) { + int size = 0; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + size += computeTypeLocationSize(memberType, stage); + } + return size; + } + + // ES: "If a shader input is any scalar or vector type, it will consume a single location." + + // Desktop: "If a vertex shader input is any scalar or vector type, it will consume a single location. If a non-vertex + // shader input is a scalar or vector type other than dvec3 or dvec4, it will consume a single location, while + // types dvec3 or dvec4 will consume two consecutive locations. Inputs of type double and dvec2 will + // consume only a single location, in all stages." + if (type.isScalar()) + return 1; + if (type.isVector()) { + if (stage == EShLangVertex && type.getQualifier().isPipeInput()) + return 1; + if (type.getBasicType() == EbtDouble && type.getVectorSize() > 2) + return 2; + else + return 1; + } + + // "If the declared input is an n x m single- or double-precision matrix, ... + // The number of locations assigned for each matrix will be the same as + // for an n-element array of m-component vectors..." + if (type.isMatrix()) { + TType columnType(type, 0); + return type.getMatrixCols() * computeTypeLocationSize(columnType, stage); + } + + assert(0); + return 1; +} + +// Same as computeTypeLocationSize but for uniforms +int TIntermediate::computeTypeUniformLocationSize(const TType& type) +{ + // "Individual elements of a uniform array are assigned + // consecutive locations with the first element taking location + // location." + if (type.isArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + TType elementType(type, 0); + if (type.isSizedArray()) { + return type.getOuterArraySize() * computeTypeUniformLocationSize(elementType); + } else { + // TODO: are there valid cases of having an implicitly-sized array with a location? If so, running this code too early. + return computeTypeUniformLocationSize(elementType); + } + } + + // "Each subsequent inner-most member or element gets incremental + // locations for the entire structure or array." + if (type.isStruct()) { + int size = 0; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + size += computeTypeUniformLocationSize(memberType); + } + return size; + } + + return 1; +} + +#ifndef GLSLANG_WEB + +// Accumulate xfb buffer ranges and check for collisions as the accumulation is done. +// +// Returns < 0 if no collision, >= 0 if collision and the value returned is a colliding value. +// +int TIntermediate::addXfbBufferOffset(const TType& type) +{ + const TQualifier& qualifier = type.getQualifier(); + + assert(qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()); + TXfbBuffer& buffer = xfbBuffers[qualifier.layoutXfbBuffer]; + + // compute the range + unsigned int size = computeTypeXfbSize(type, buffer.contains64BitType, buffer.contains32BitType, buffer.contains16BitType); + buffer.implicitStride = std::max(buffer.implicitStride, qualifier.layoutXfbOffset + size); + TRange range(qualifier.layoutXfbOffset, qualifier.layoutXfbOffset + size - 1); + + // check for collisions + for (size_t r = 0; r < buffer.ranges.size(); ++r) { + if (range.overlap(buffer.ranges[r])) { + // there is a collision; pick an example to return + return std::max(range.start, buffer.ranges[r].start); + } + } + + buffer.ranges.push_back(range); + + return -1; // no collision +} + +// Recursively figure out how many bytes of xfb buffer are used by the given type. +// Return the size of type, in bytes. +// Sets contains64BitType to true if the type contains a 64-bit data type. +// Sets contains32BitType to true if the type contains a 32-bit data type. +// Sets contains16BitType to true if the type contains a 16-bit data type. +// N.B. Caller must set contains64BitType, contains32BitType, and contains16BitType to false before calling. +unsigned int TIntermediate::computeTypeXfbSize(const TType& type, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const +{ + // "...if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8, + // and the space taken in the buffer will be a multiple of 8. + // ...within the qualified entity, subsequent components are each + // assigned, in order, to the next available offset aligned to a multiple of + // that component's size. Aggregate types are flattened down to the component + // level to get this sequence of components." + + if (type.isSizedArray()) { + // TODO: perf: this can be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + // Unsized array use to xfb should be a compile error. + TType elementType(type, 0); + return type.getOuterArraySize() * computeTypeXfbSize(elementType, contains64BitType, contains16BitType, contains16BitType); + } + + if (type.isStruct()) { + unsigned int size = 0; + bool structContains64BitType = false; + bool structContains32BitType = false; + bool structContains16BitType = false; + for (int member = 0; member < (int)type.getStruct()->size(); ++member) { + TType memberType(type, member); + // "... if applied to + // an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8, + // and the space taken in the buffer will be a multiple of 8." + bool memberContains64BitType = false; + bool memberContains32BitType = false; + bool memberContains16BitType = false; + int memberSize = computeTypeXfbSize(memberType, memberContains64BitType, memberContains32BitType, memberContains16BitType); + if (memberContains64BitType) { + structContains64BitType = true; + RoundToPow2(size, 8); + } else if (memberContains32BitType) { + structContains32BitType = true; + RoundToPow2(size, 4); + } else if (memberContains16BitType) { + structContains16BitType = true; + RoundToPow2(size, 2); + } + size += memberSize; + } + + if (structContains64BitType) { + contains64BitType = true; + RoundToPow2(size, 8); + } else if (structContains32BitType) { + contains32BitType = true; + RoundToPow2(size, 4); + } else if (structContains16BitType) { + contains16BitType = true; + RoundToPow2(size, 2); + } + return size; + } + + int numComponents; + if (type.isScalar()) + numComponents = 1; + else if (type.isVector()) + numComponents = type.getVectorSize(); + else if (type.isMatrix()) + numComponents = type.getMatrixCols() * type.getMatrixRows(); + else { + assert(0); + numComponents = 1; + } + + if (type.getBasicType() == EbtDouble || type.getBasicType() == EbtInt64 || type.getBasicType() == EbtUint64) { + contains64BitType = true; + return 8 * numComponents; + } else if (type.getBasicType() == EbtFloat16 || type.getBasicType() == EbtInt16 || type.getBasicType() == EbtUint16) { + contains16BitType = true; + return 2 * numComponents; + } else if (type.getBasicType() == EbtInt8 || type.getBasicType() == EbtUint8) + return numComponents; + else { + contains32BitType = true; + return 4 * numComponents; + } +} + +#endif + +const int baseAlignmentVec4Std140 = 16; + +// Return the size and alignment of a component of the given type. +// The size is returned in the 'size' parameter +// Return value is the alignment.. +int TIntermediate::getBaseAlignmentScalar(const TType& type, int& size) +{ +#ifdef GLSLANG_WEB + size = 4; return 4; +#endif + + switch (type.getBasicType()) { + case EbtInt64: + case EbtUint64: + case EbtDouble: size = 8; return 8; + case EbtFloat16: size = 2; return 2; + case EbtInt8: + case EbtUint8: size = 1; return 1; + case EbtInt16: + case EbtUint16: size = 2; return 2; + case EbtReference: size = 8; return 8; + default: size = 4; return 4; + } +} + +// Implement base-alignment and size rules from section 7.6.2.2 Standard Uniform Block Layout +// Operates recursively. +// +// If std140 is true, it does the rounding up to vec4 size required by std140, +// otherwise it does not, yielding std430 rules. +// +// The size is returned in the 'size' parameter +// +// The stride is only non-0 for arrays or matrices, and is the stride of the +// top-level object nested within the type. E.g., for an array of matrices, +// it is the distances needed between matrices, despite the rules saying the +// stride comes from the flattening down to vectors. +// +// Return value is the alignment of the type. +int TIntermediate::getBaseAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor) +{ + int alignment; + + bool std140 = layoutPacking == glslang::ElpStd140; + // When using the std140 storage layout, structures will be laid out in buffer + // storage with its members stored in monotonically increasing order based on their + // location in the declaration. A structure and each structure member have a base + // offset and a base alignment, from which an aligned offset is computed by rounding + // the base offset up to a multiple of the base alignment. The base offset of the first + // member of a structure is taken from the aligned offset of the structure itself. The + // base offset of all other structure members is derived by taking the offset of the + // last basic machine unit consumed by the previous member and adding one. Each + // structure member is stored in memory at its aligned offset. The members of a top- + // level uniform block are laid out in buffer storage by treating the uniform block as + // a structure with a base offset of zero. + // + // 1. If the member is a scalar consuming N basic machine units, the base alignment is N. + // + // 2. If the member is a two- or four-component vector with components consuming N basic + // machine units, the base alignment is 2N or 4N, respectively. + // + // 3. If the member is a three-component vector with components consuming N + // basic machine units, the base alignment is 4N. + // + // 4. If the member is an array of scalars or vectors, the base alignment and array + // stride are set to match the base alignment of a single array element, according + // to rules (1), (2), and (3), and rounded up to the base alignment of a vec4. The + // array may have padding at the end; the base offset of the member following + // the array is rounded up to the next multiple of the base alignment. + // + // 5. If the member is a column-major matrix with C columns and R rows, the + // matrix is stored identically to an array of C column vectors with R + // components each, according to rule (4). + // + // 6. If the member is an array of S column-major matrices with C columns and + // R rows, the matrix is stored identically to a row of S X C column vectors + // with R components each, according to rule (4). + // + // 7. If the member is a row-major matrix with C columns and R rows, the matrix + // is stored identically to an array of R row vectors with C components each, + // according to rule (4). + // + // 8. If the member is an array of S row-major matrices with C columns and R + // rows, the matrix is stored identically to a row of S X R row vectors with C + // components each, according to rule (4). + // + // 9. If the member is a structure, the base alignment of the structure is N , where + // N is the largest base alignment value of any of its members, and rounded + // up to the base alignment of a vec4. The individual members of this substructure + // are then assigned offsets by applying this set of rules recursively, + // where the base offset of the first member of the sub-structure is equal to the + // aligned offset of the structure. The structure may have padding at the end; + // the base offset of the member following the sub-structure is rounded up to + // the next multiple of the base alignment of the structure. + // + // 10. If the member is an array of S structures, the S elements of the array are laid + // out in order, according to rule (9). + // + // Assuming, for rule 10: The stride is the same as the size of an element. + + stride = 0; + int dummyStride; + + // rules 4, 6, 8, and 10 + if (type.isArray()) { + // TODO: perf: this might be flattened by using getCumulativeArraySize(), and a deref that discards all arrayness + TType derefType(type, 0); + alignment = getBaseAlignment(derefType, size, dummyStride, layoutPacking, rowMajor); + if (std140) + alignment = std::max(baseAlignmentVec4Std140, alignment); + RoundToPow2(size, alignment); + stride = size; // uses full matrix size for stride of an array of matrices (not quite what rule 6/8, but what's expected) + // uses the assumption for rule 10 in the comment above + // use one element to represent the last member of SSBO which is unsized array + int arraySize = (type.isUnsizedArray() && (type.getOuterArraySize() == 0)) ? 1 : type.getOuterArraySize(); + size = stride * arraySize; + return alignment; + } + + // rule 9 + if (type.getBasicType() == EbtStruct) { + const TTypeList& memberList = *type.getStruct(); + + size = 0; + int maxAlignment = std140 ? baseAlignmentVec4Std140 : 0; + for (size_t m = 0; m < memberList.size(); ++m) { + int memberSize; + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix; + int memberAlignment = getBaseAlignment(*memberList[m].type, memberSize, dummyStride, layoutPacking, + (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor); + maxAlignment = std::max(maxAlignment, memberAlignment); + RoundToPow2(size, memberAlignment); + size += memberSize; + } + + // The structure may have padding at the end; the base offset of + // the member following the sub-structure is rounded up to the next + // multiple of the base alignment of the structure. + RoundToPow2(size, maxAlignment); + + return maxAlignment; + } + + // rule 1 + if (type.isScalar()) + return getBaseAlignmentScalar(type, size); + + // rules 2 and 3 + if (type.isVector()) { + int scalarAlign = getBaseAlignmentScalar(type, size); + switch (type.getVectorSize()) { + case 1: // HLSL has this, GLSL does not + return scalarAlign; + case 2: + size *= 2; + return 2 * scalarAlign; + default: + size *= type.getVectorSize(); + return 4 * scalarAlign; + } + } + + // rules 5 and 7 + if (type.isMatrix()) { + // rule 5: deref to row, not to column, meaning the size of vector is num columns instead of num rows + TType derefType(type, 0, rowMajor); + + alignment = getBaseAlignment(derefType, size, dummyStride, layoutPacking, rowMajor); + if (std140) + alignment = std::max(baseAlignmentVec4Std140, alignment); + RoundToPow2(size, alignment); + stride = size; // use intra-matrix stride for stride of a just a matrix + if (rowMajor) + size = stride * type.getMatrixRows(); + else + size = stride * type.getMatrixCols(); + + return alignment; + } + + assert(0); // all cases should be covered above + size = baseAlignmentVec4Std140; + return baseAlignmentVec4Std140; +} + +// To aid the basic HLSL rule about crossing vec4 boundaries. +bool TIntermediate::improperStraddle(const TType& type, int size, int offset) +{ + if (! type.isVector() || type.isArray()) + return false; + + return size <= 16 ? offset / 16 != (offset + size - 1) / 16 + : offset % 16 != 0; +} + +int TIntermediate::getScalarAlignment(const TType& type, int& size, int& stride, bool rowMajor) +{ + int alignment; + + stride = 0; + int dummyStride; + + if (type.isArray()) { + TType derefType(type, 0); + alignment = getScalarAlignment(derefType, size, dummyStride, rowMajor); + + stride = size; + RoundToPow2(stride, alignment); + + size = stride * (type.getOuterArraySize() - 1) + size; + return alignment; + } + + if (type.getBasicType() == EbtStruct) { + const TTypeList& memberList = *type.getStruct(); + + size = 0; + int maxAlignment = 0; + for (size_t m = 0; m < memberList.size(); ++m) { + int memberSize; + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberList[m].type->getQualifier().layoutMatrix; + int memberAlignment = getScalarAlignment(*memberList[m].type, memberSize, dummyStride, + (subMatrixLayout != ElmNone) ? (subMatrixLayout == ElmRowMajor) : rowMajor); + maxAlignment = std::max(maxAlignment, memberAlignment); + RoundToPow2(size, memberAlignment); + size += memberSize; + } + + return maxAlignment; + } + + if (type.isScalar()) + return getBaseAlignmentScalar(type, size); + + if (type.isVector()) { + int scalarAlign = getBaseAlignmentScalar(type, size); + + size *= type.getVectorSize(); + return scalarAlign; + } + + if (type.isMatrix()) { + TType derefType(type, 0, rowMajor); + + alignment = getScalarAlignment(derefType, size, dummyStride, rowMajor); + + stride = size; // use intra-matrix stride for stride of a just a matrix + if (rowMajor) + size = stride * type.getMatrixRows(); + else + size = stride * type.getMatrixCols(); + + return alignment; + } + + assert(0); // all cases should be covered above + size = 1; + return 1; +} + +int TIntermediate::getMemberAlignment(const TType& type, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor) +{ + if (layoutPacking == glslang::ElpScalar) { + return getScalarAlignment(type, size, stride, rowMajor); + } else { + return getBaseAlignment(type, size, stride, layoutPacking, rowMajor); + } +} + +// shared calculation by getOffset and getOffsets +void TIntermediate::updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize) +{ + int dummyStride; + + // modify just the children's view of matrix layout, if there is one for this member + TLayoutMatrix subMatrixLayout = memberType.getQualifier().layoutMatrix; + int memberAlignment = getMemberAlignment(memberType, memberSize, dummyStride, + parentType.getQualifier().layoutPacking, + subMatrixLayout != ElmNone + ? subMatrixLayout == ElmRowMajor + : parentType.getQualifier().layoutMatrix == ElmRowMajor); + RoundToPow2(offset, memberAlignment); +} + +// Lookup or calculate the offset of a block member, using the recursively +// defined block offset rules. +int TIntermediate::getOffset(const TType& type, int index) +{ + const TTypeList& memberList = *type.getStruct(); + + // Don't calculate offset if one is present, it could be user supplied + // and different than what would be calculated. That is, this is faster, + // but not just an optimization. + if (memberList[index].type->getQualifier().hasOffset()) + return memberList[index].type->getQualifier().layoutOffset; + + int memberSize = 0; + int offset = 0; + for (int m = 0; m <= index; ++m) { + updateOffset(type, *memberList[m].type, offset, memberSize); + + if (m < index) + offset += memberSize; + } + + return offset; +} + +// Calculate the block data size. +// Block arrayness is not taken into account, each element is backed by a separate buffer. +int TIntermediate::getBlockSize(const TType& blockType) +{ + const TTypeList& memberList = *blockType.getStruct(); + int lastIndex = (int)memberList.size() - 1; + int lastOffset = getOffset(blockType, lastIndex); + + int lastMemberSize; + int dummyStride; + getMemberAlignment(*memberList[lastIndex].type, lastMemberSize, dummyStride, + blockType.getQualifier().layoutPacking, + blockType.getQualifier().layoutMatrix == ElmRowMajor); + + return lastOffset + lastMemberSize; +} + +int TIntermediate::computeBufferReferenceTypeSize(const TType& type) +{ + assert(type.isReference()); + int size = getBlockSize(*type.getReferentType()); + + int align = type.getBufferReferenceAlignment(); + + if (align) { + size = (size + align - 1) & ~(align-1); + } + + return size; +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/localintermediate.h b/third_party/glslang/glslang/MachineIndependent/localintermediate.h new file mode 100644 index 0000000..f874701 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/localintermediate.h @@ -0,0 +1,1071 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2016 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef _LOCAL_INTERMEDIATE_INCLUDED_ +#define _LOCAL_INTERMEDIATE_INCLUDED_ + +#include "../Include/intermediate.h" +#include "../Public/ShaderLang.h" +#include "Versions.h" + +#include +#include +#include +#include +#include + +class TInfoSink; + +namespace glslang { + +struct TMatrixSelector { + int coord1; // stay agnostic about column/row; this is parse order + int coord2; +}; + +typedef int TVectorSelector; + +const int MaxSwizzleSelectors = 4; + +template +class TSwizzleSelectors { +public: + TSwizzleSelectors() : size_(0) { } + + void push_back(selectorType comp) + { + if (size_ < MaxSwizzleSelectors) + components[size_++] = comp; + } + void resize(int s) + { + assert(s <= size_); + size_ = s; + } + int size() const { return size_; } + selectorType operator[](int i) const + { + assert(i < MaxSwizzleSelectors); + return components[i]; + } + +private: + int size_; + selectorType components[MaxSwizzleSelectors]; +}; + +// +// Some helper structures for TIntermediate. Their contents are encapsulated +// by TIntermediate. +// + +// Used for call-graph algorithms for detecting recursion, missing bodies, and dead bodies. +// A "call" is a pair: . +// There can be duplicates. General assumption is the list is small. +struct TCall { + TCall(const TString& pCaller, const TString& pCallee) : caller(pCaller), callee(pCallee) { } + TString caller; + TString callee; + bool visited; + bool currentPath; + bool errorGiven; + int calleeBodyPosition; +}; + +// A generic 1-D range. +struct TRange { + TRange(int start, int last) : start(start), last(last) { } + bool overlap(const TRange& rhs) const + { + return last >= rhs.start && start <= rhs.last; + } + int start; + int last; +}; + +// An IO range is a 3-D rectangle; the set of (location, component, index) triples all lying +// within the same location range, component range, and index value. Locations don't alias unless +// all other dimensions of their range overlap. +struct TIoRange { + TIoRange(TRange location, TRange component, TBasicType basicType, int index) + : location(location), component(component), basicType(basicType), index(index) { } + bool overlap(const TIoRange& rhs) const + { + return location.overlap(rhs.location) && component.overlap(rhs.component) && index == rhs.index; + } + TRange location; + TRange component; + TBasicType basicType; + int index; +}; + +// An offset range is a 2-D rectangle; the set of (binding, offset) pairs all lying +// within the same binding and offset range. +struct TOffsetRange { + TOffsetRange(TRange binding, TRange offset) + : binding(binding), offset(offset) { } + bool overlap(const TOffsetRange& rhs) const + { + return binding.overlap(rhs.binding) && offset.overlap(rhs.offset); + } + TRange binding; + TRange offset; +}; + +#ifndef GLSLANG_WEB +// Things that need to be tracked per xfb buffer. +struct TXfbBuffer { + TXfbBuffer() : stride(TQualifier::layoutXfbStrideEnd), implicitStride(0), contains64BitType(false), + contains32BitType(false), contains16BitType(false) { } + std::vector ranges; // byte offsets that have already been assigned + unsigned int stride; + unsigned int implicitStride; + bool contains64BitType; + bool contains32BitType; + bool contains16BitType; +}; +#endif + +// Track a set of strings describing how the module was processed. +// This includes command line options, transforms, etc., ideally inclusive enough +// to reproduce the steps used to transform the input source to the output. +// E.g., see SPIR-V OpModuleProcessed. +// Each "process" or "transform" uses is expressed in the form: +// process arg0 arg1 arg2 ... +// process arg0 arg1 arg2 ... +// where everything is textual, and there can be zero or more arguments +class TProcesses { +public: + TProcesses() {} + ~TProcesses() {} + + void addProcess(const char* process) + { + processes.push_back(process); + } + void addProcess(const std::string& process) + { + processes.push_back(process); + } + void addArgument(int arg) + { + processes.back().append(" "); + std::string argString = std::to_string(arg); + processes.back().append(argString); + } + void addArgument(const char* arg) + { + processes.back().append(" "); + processes.back().append(arg); + } + void addArgument(const std::string& arg) + { + processes.back().append(" "); + processes.back().append(arg); + } + void addIfNonZero(const char* process, int value) + { + if (value != 0) { + addProcess(process); + addArgument(value); + } + } + + const std::vector& getProcesses() const { return processes; } + +private: + std::vector processes; +}; + +class TSymbolTable; +class TSymbol; +class TVariable; + +// +// Texture and Sampler transformation mode. +// +enum ComputeDerivativeMode { + LayoutDerivativeNone, // default layout as SPV_NV_compute_shader_derivatives not enabled + LayoutDerivativeGroupQuads, // derivative_group_quadsNV + LayoutDerivativeGroupLinear, // derivative_group_linearNV +}; + +class TIdMaps { +public: + TMap& operator[](int i) { return maps[i]; } + const TMap& operator[](int i) const { return maps[i]; } +private: + TMap maps[EsiCount]; +}; + +class TNumericFeatures { +public: + TNumericFeatures() : features(0) { } + TNumericFeatures(const TNumericFeatures&) = delete; + TNumericFeatures& operator=(const TNumericFeatures&) = delete; + typedef enum : unsigned int { + shader_explicit_arithmetic_types = 1 << 0, + shader_explicit_arithmetic_types_int8 = 1 << 1, + shader_explicit_arithmetic_types_int16 = 1 << 2, + shader_explicit_arithmetic_types_int32 = 1 << 3, + shader_explicit_arithmetic_types_int64 = 1 << 4, + shader_explicit_arithmetic_types_float16 = 1 << 5, + shader_explicit_arithmetic_types_float32 = 1 << 6, + shader_explicit_arithmetic_types_float64 = 1 << 7, + shader_implicit_conversions = 1 << 8, + gpu_shader_fp64 = 1 << 9, + gpu_shader_int16 = 1 << 10, + gpu_shader_half_float = 1 << 11, + } feature; + void insert(feature f) { features |= f; } + void erase(feature f) { features &= ~f; } + bool contains(feature f) const { return (features & f) != 0; } +private: + unsigned int features; +}; + +// MustBeAssigned wraps a T, asserting that it has been assigned with +// operator =() before attempting to read with operator T() or operator ->(). +// Used to catch cases where fields are read before they have been assigned. +template +class MustBeAssigned +{ +public: + MustBeAssigned() = default; + MustBeAssigned(const T& v) : value(v) {} + operator const T&() const { assert(isSet); return value; } + const T* operator ->() const { assert(isSet); return &value; } + MustBeAssigned& operator = (const T& v) { value = v; isSet = true; return *this; } +private: + T value; + bool isSet = false; +}; + +// +// Set of helper functions to help parse and build the tree. +// +class TIntermediate { +public: + explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : + language(l), +#ifndef GLSLANG_ANGLE + profile(p), version(v), +#endif + treeRoot(0), + resources(TBuiltInResource{}), + numEntryPoints(0), numErrors(0), numPushConstants(0), recursive(false), + invertY(false), + useStorageBuffer(false), + nanMinMaxClamp(false), + depthReplacing(false) +#ifndef GLSLANG_WEB + , + implicitThisName("@this"), implicitCounterName("@count"), + source(EShSourceNone), + useVulkanMemoryModel(false), + invocations(TQualifier::layoutNotSet), vertices(TQualifier::layoutNotSet), + inputPrimitive(ElgNone), outputPrimitive(ElgNone), + pixelCenterInteger(false), originUpperLeft(false), + vertexSpacing(EvsNone), vertexOrder(EvoNone), interlockOrdering(EioNone), pointMode(false), earlyFragmentTests(false), + postDepthCoverage(false), depthLayout(EldNone), + hlslFunctionality1(false), + blendEquations(0), xfbMode(false), multiStream(false), + layoutOverrideCoverage(false), + geoPassthroughEXT(false), + numShaderRecordBlocks(0), + computeDerivativeMode(LayoutDerivativeNone), + primitives(TQualifier::layoutNotSet), + numTaskNVBlocks(0), + layoutPrimitiveCulling(false), + autoMapBindings(false), + autoMapLocations(false), + flattenUniformArrays(false), + useUnknownFormat(false), + hlslOffsets(false), + hlslIoMapping(false), + useVariablePointers(false), + textureSamplerTransformMode(EShTexSampTransKeep), + needToLegalize(false), + binaryDoubleOutput(false), + usePhysicalStorageBuffer(false), + uniformLocationBase(0) +#endif + { + localSize[0] = 1; + localSize[1] = 1; + localSize[2] = 1; + localSizeNotDefault[0] = false; + localSizeNotDefault[1] = false; + localSizeNotDefault[2] = false; + localSizeSpecId[0] = TQualifier::layoutNotSet; + localSizeSpecId[1] = TQualifier::layoutNotSet; + localSizeSpecId[2] = TQualifier::layoutNotSet; +#ifndef GLSLANG_WEB + xfbBuffers.resize(TQualifier::layoutXfbBufferEnd); + shiftBinding.fill(0); +#endif + } + + void setVersion(int v) + { +#ifndef GLSLANG_ANGLE + version = v; +#endif + } + void setProfile(EProfile p) + { +#ifndef GLSLANG_ANGLE + profile = p; +#endif + } + + int getVersion() const { return version; } + EProfile getProfile() const { return profile; } + void setSpv(const SpvVersion& s) + { + spvVersion = s; + + // client processes + if (spvVersion.vulkan > 0) + processes.addProcess("client vulkan100"); + if (spvVersion.openGl > 0) + processes.addProcess("client opengl100"); + + // target SPV + switch (spvVersion.spv) { + case 0: + break; + case EShTargetSpv_1_0: + break; + case EShTargetSpv_1_1: + processes.addProcess("target-env spirv1.1"); + break; + case EShTargetSpv_1_2: + processes.addProcess("target-env spirv1.2"); + break; + case EShTargetSpv_1_3: + processes.addProcess("target-env spirv1.3"); + break; + case EShTargetSpv_1_4: + processes.addProcess("target-env spirv1.4"); + break; + case EShTargetSpv_1_5: + processes.addProcess("target-env spirv1.5"); + break; + default: + processes.addProcess("target-env spirvUnknown"); + break; + } + + // target-environment processes + switch (spvVersion.vulkan) { + case 0: + break; + case EShTargetVulkan_1_0: + processes.addProcess("target-env vulkan1.0"); + break; + case EShTargetVulkan_1_1: + processes.addProcess("target-env vulkan1.1"); + break; + case EShTargetVulkan_1_2: + processes.addProcess("target-env vulkan1.2"); + break; + default: + processes.addProcess("target-env vulkanUnknown"); + break; + } + if (spvVersion.openGl > 0) + processes.addProcess("target-env opengl"); + } + const SpvVersion& getSpv() const { return spvVersion; } + EShLanguage getStage() const { return language; } + void addRequestedExtension(const char* extension) { requestedExtensions.insert(extension); } + const std::set& getRequestedExtensions() const { return requestedExtensions; } + + void setTreeRoot(TIntermNode* r) { treeRoot = r; } + TIntermNode* getTreeRoot() const { return treeRoot; } + void incrementEntryPointCount() { ++numEntryPoints; } + int getNumEntryPoints() const { return numEntryPoints; } + int getNumErrors() const { return numErrors; } + void addPushConstantCount() { ++numPushConstants; } + void setLimits(const TBuiltInResource& r) { resources = r; } + const TBuiltInResource& getLimits() const { return resources; } + + bool postProcess(TIntermNode*, EShLanguage); + void removeTree(); + + void setEntryPointName(const char* ep) + { + entryPointName = ep; + processes.addProcess("entry-point"); + processes.addArgument(entryPointName); + } + void setEntryPointMangledName(const char* ep) { entryPointMangledName = ep; } + const std::string& getEntryPointName() const { return entryPointName; } + const std::string& getEntryPointMangledName() const { return entryPointMangledName; } + + void setInvertY(bool invert) + { + invertY = invert; + if (invertY) + processes.addProcess("invert-y"); + } + bool getInvertY() const { return invertY; } + +#ifdef ENABLE_HLSL + void setSource(EShSource s) { source = s; } + EShSource getSource() const { return source; } +#else + void setSource(EShSource s) { assert(s == EShSourceGlsl); (void)s; } + EShSource getSource() const { return EShSourceGlsl; } +#endif + + bool isRecursive() const { return recursive; } + + TIntermSymbol* addSymbol(const TVariable&); + TIntermSymbol* addSymbol(const TVariable&, const TSourceLoc&); + TIntermSymbol* addSymbol(const TType&, const TSourceLoc&); + TIntermSymbol* addSymbol(const TIntermSymbol&); + TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*); + std::tuple addPairConversion(TOperator op, TIntermTyped* node0, TIntermTyped* node1); + TIntermTyped* addUniShapeConversion(TOperator, const TType&, TIntermTyped*); + TIntermTyped* addConversion(TBasicType convertTo, TIntermTyped* node) const; + void addBiShapeConversion(TOperator, TIntermTyped*& lhsNode, TIntermTyped*& rhsNode); + TIntermTyped* addShapeConversion(const TType&, TIntermTyped*); + TIntermTyped* addBinaryMath(TOperator, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); + TIntermTyped* addAssign(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); + TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, const TSourceLoc&); + TIntermTyped* addUnaryMath(TOperator, TIntermTyped* child, const TSourceLoc&); + TIntermTyped* addBuiltInFunctionCall(const TSourceLoc& line, TOperator, bool unary, TIntermNode*, const TType& returnType); + bool canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op = EOpNull) const; + bool isIntegralPromotion(TBasicType from, TBasicType to) const; + bool isFPPromotion(TBasicType from, TBasicType to) const; + bool isIntegralConversion(TBasicType from, TBasicType to) const; + bool isFPConversion(TBasicType from, TBasicType to) const; + bool isFPIntegralConversion(TBasicType from, TBasicType to) const; + TOperator mapTypeToConstructorOp(const TType&) const; + TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right); + TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&); + TIntermAggregate* makeAggregate(TIntermNode* node); + TIntermAggregate* makeAggregate(TIntermNode* node, const TSourceLoc&); + TIntermAggregate* makeAggregate(const TSourceLoc&); + TIntermTyped* setAggregateOperator(TIntermNode*, TOperator, const TType& type, const TSourceLoc&); + bool areAllChildConst(TIntermAggregate* aggrNode); + TIntermSelection* addSelection(TIntermTyped* cond, TIntermNodePair code, const TSourceLoc&); + TIntermTyped* addSelection(TIntermTyped* cond, TIntermTyped* trueBlock, TIntermTyped* falseBlock, const TSourceLoc&); + TIntermTyped* addComma(TIntermTyped* left, TIntermTyped* right, const TSourceLoc&); + TIntermTyped* addMethod(TIntermTyped*, const TType&, const TString*, const TSourceLoc&); + TIntermConstantUnion* addConstantUnion(const TConstUnionArray&, const TType&, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(signed char, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned char, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(signed short, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned short, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(int, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned int, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(long long, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(unsigned long long, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(bool, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(double, TBasicType, const TSourceLoc&, bool literal = false) const; + TIntermConstantUnion* addConstantUnion(const TString*, const TSourceLoc&, bool literal = false) const; + TIntermTyped* promoteConstantUnion(TBasicType, TIntermConstantUnion*) const; + bool parseConstTree(TIntermNode*, TConstUnionArray, TOperator, const TType&, bool singleConstantParam = false); + TIntermLoop* addLoop(TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, const TSourceLoc&); + TIntermAggregate* addForLoop(TIntermNode*, TIntermNode*, TIntermTyped*, TIntermTyped*, bool testFirst, + const TSourceLoc&, TIntermLoop*&); + TIntermBranch* addBranch(TOperator, const TSourceLoc&); + TIntermBranch* addBranch(TOperator, TIntermTyped*, const TSourceLoc&); + template TIntermTyped* addSwizzle(TSwizzleSelectors&, const TSourceLoc&); + + // Low level functions to add nodes (no conversions or other higher level transformations) + // If a type is provided, the node's type will be set to it. + TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&) const; + TIntermBinary* addBinaryNode(TOperator op, TIntermTyped* left, TIntermTyped* right, const TSourceLoc&, + const TType&) const; + TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc&) const; + TIntermUnary* addUnaryNode(TOperator op, TIntermTyped* child, const TSourceLoc&, const TType&) const; + + // Constant folding (in Constant.cpp) + TIntermTyped* fold(TIntermAggregate* aggrNode); + TIntermTyped* foldConstructor(TIntermAggregate* aggrNode); + TIntermTyped* foldDereference(TIntermTyped* node, int index, const TSourceLoc&); + TIntermTyped* foldSwizzle(TIntermTyped* node, TSwizzleSelectors& fields, const TSourceLoc&); + + // Tree ops + static const TIntermTyped* findLValueBase(const TIntermTyped*, bool swizzleOkay); + + // Linkage related + void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&); + void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&); + + void setUseStorageBuffer() { useStorageBuffer = true; } + bool usingStorageBuffer() const { return useStorageBuffer; } + void setDepthReplacing() { depthReplacing = true; } + bool isDepthReplacing() const { return depthReplacing; } + bool setLocalSize(int dim, int size) + { + if (localSizeNotDefault[dim]) + return size == localSize[dim]; + localSizeNotDefault[dim] = true; + localSize[dim] = size; + return true; + } + unsigned int getLocalSize(int dim) const { return localSize[dim]; } + bool setLocalSizeSpecId(int dim, int id) + { + if (localSizeSpecId[dim] != TQualifier::layoutNotSet) + return id == localSizeSpecId[dim]; + localSizeSpecId[dim] = id; + return true; + } + int getLocalSizeSpecId(int dim) const { return localSizeSpecId[dim]; } +#ifdef GLSLANG_WEB + void output(TInfoSink&, bool tree) { } + + bool isEsProfile() const { return false; } + bool getXfbMode() const { return false; } + bool isMultiStream() const { return false; } + TLayoutGeometry getOutputPrimitive() const { return ElgNone; } + bool getPostDepthCoverage() const { return false; } + bool getEarlyFragmentTests() const { return false; } + TLayoutDepth getDepth() const { return EldNone; } + bool getPixelCenterInteger() const { return false; } + void setOriginUpperLeft() { } + bool getOriginUpperLeft() const { return true; } + TInterlockOrdering getInterlockOrdering() const { return EioNone; } + + bool getAutoMapBindings() const { return false; } + bool getAutoMapLocations() const { return false; } + int getNumPushConstants() const { return 0; } + void addShaderRecordCount() { } + void addTaskNVCount() { } + void setUseVulkanMemoryModel() { } + bool usingVulkanMemoryModel() const { return false; } + bool usingPhysicalStorageBuffer() const { return false; } + bool usingVariablePointers() const { return false; } + unsigned getXfbStride(int buffer) const { return 0; } + bool hasLayoutDerivativeModeNone() const { return false; } + ComputeDerivativeMode getLayoutDerivativeModeNone() const { return LayoutDerivativeNone; } +#else + void output(TInfoSink&, bool tree); + + bool isEsProfile() const { return profile == EEsProfile; } + + void setShiftBinding(TResourceType res, unsigned int shift) + { + shiftBinding[res] = shift; + + const char* name = getResourceName(res); + if (name != nullptr) + processes.addIfNonZero(name, shift); + } + + unsigned int getShiftBinding(TResourceType res) const { return shiftBinding[res]; } + + void setShiftBindingForSet(TResourceType res, unsigned int shift, unsigned int set) + { + if (shift == 0) // ignore if there's no shift: it's a no-op. + return; + + shiftBindingForSet[res][set] = shift; + + const char* name = getResourceName(res); + if (name != nullptr) { + processes.addProcess(name); + processes.addArgument(shift); + processes.addArgument(set); + } + } + + int getShiftBindingForSet(TResourceType res, unsigned int set) const + { + const auto shift = shiftBindingForSet[res].find(set); + return shift == shiftBindingForSet[res].end() ? -1 : shift->second; + } + bool hasShiftBindingForSet(TResourceType res) const { return !shiftBindingForSet[res].empty(); } + + void setResourceSetBinding(const std::vector& shift) + { + resourceSetBinding = shift; + if (shift.size() > 0) { + processes.addProcess("resource-set-binding"); + for (int s = 0; s < (int)shift.size(); ++s) + processes.addArgument(shift[s]); + } + } + const std::vector& getResourceSetBinding() const { return resourceSetBinding; } + void setAutoMapBindings(bool map) + { + autoMapBindings = map; + if (autoMapBindings) + processes.addProcess("auto-map-bindings"); + } + bool getAutoMapBindings() const { return autoMapBindings; } + void setAutoMapLocations(bool map) + { + autoMapLocations = map; + if (autoMapLocations) + processes.addProcess("auto-map-locations"); + } + bool getAutoMapLocations() const { return autoMapLocations; } + +#ifdef ENABLE_HLSL + void setFlattenUniformArrays(bool flatten) + { + flattenUniformArrays = flatten; + if (flattenUniformArrays) + processes.addProcess("flatten-uniform-arrays"); + } + bool getFlattenUniformArrays() const { return flattenUniformArrays; } +#endif + void setNoStorageFormat(bool b) + { + useUnknownFormat = b; + if (useUnknownFormat) + processes.addProcess("no-storage-format"); + } + bool getNoStorageFormat() const { return useUnknownFormat; } + void setUseVulkanMemoryModel() + { + useVulkanMemoryModel = true; + processes.addProcess("use-vulkan-memory-model"); + } + bool usingVulkanMemoryModel() const { return useVulkanMemoryModel; } + void setUsePhysicalStorageBuffer() + { + usePhysicalStorageBuffer = true; + } + bool usingPhysicalStorageBuffer() const { return usePhysicalStorageBuffer; } + void setUseVariablePointers() + { + useVariablePointers = true; + processes.addProcess("use-variable-pointers"); + } + bool usingVariablePointers() const { return useVariablePointers; } + +#ifdef ENABLE_HLSL + template T addCounterBufferName(const T& name) const { return name + implicitCounterName; } + bool hasCounterBufferName(const TString& name) const { + size_t len = strlen(implicitCounterName); + return name.size() > len && + name.compare(name.size() - len, len, implicitCounterName) == 0; + } +#endif + + void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { textureSamplerTransformMode = mode; } + int getNumPushConstants() const { return numPushConstants; } + void addShaderRecordCount() { ++numShaderRecordBlocks; } + void addTaskNVCount() { ++numTaskNVBlocks; } + + bool setInvocations(int i) + { + if (invocations != TQualifier::layoutNotSet) + return invocations == i; + invocations = i; + return true; + } + int getInvocations() const { return invocations; } + bool setVertices(int m) + { + if (vertices != TQualifier::layoutNotSet) + return vertices == m; + vertices = m; + return true; + } + int getVertices() const { return vertices; } + bool setInputPrimitive(TLayoutGeometry p) + { + if (inputPrimitive != ElgNone) + return inputPrimitive == p; + inputPrimitive = p; + return true; + } + TLayoutGeometry getInputPrimitive() const { return inputPrimitive; } + bool setVertexSpacing(TVertexSpacing s) + { + if (vertexSpacing != EvsNone) + return vertexSpacing == s; + vertexSpacing = s; + return true; + } + TVertexSpacing getVertexSpacing() const { return vertexSpacing; } + bool setVertexOrder(TVertexOrder o) + { + if (vertexOrder != EvoNone) + return vertexOrder == o; + vertexOrder = o; + return true; + } + TVertexOrder getVertexOrder() const { return vertexOrder; } + void setPointMode() { pointMode = true; } + bool getPointMode() const { return pointMode; } + + bool setInterlockOrdering(TInterlockOrdering o) + { + if (interlockOrdering != EioNone) + return interlockOrdering == o; + interlockOrdering = o; + return true; + } + TInterlockOrdering getInterlockOrdering() const { return interlockOrdering; } + + void setXfbMode() { xfbMode = true; } + bool getXfbMode() const { return xfbMode; } + void setMultiStream() { multiStream = true; } + bool isMultiStream() const { return multiStream; } + bool setOutputPrimitive(TLayoutGeometry p) + { + if (outputPrimitive != ElgNone) + return outputPrimitive == p; + outputPrimitive = p; + return true; + } + TLayoutGeometry getOutputPrimitive() const { return outputPrimitive; } + void setPostDepthCoverage() { postDepthCoverage = true; } + bool getPostDepthCoverage() const { return postDepthCoverage; } + void setEarlyFragmentTests() { earlyFragmentTests = true; } + bool getEarlyFragmentTests() const { return earlyFragmentTests; } + bool setDepth(TLayoutDepth d) + { + if (depthLayout != EldNone) + return depthLayout == d; + depthLayout = d; + return true; + } + TLayoutDepth getDepth() const { return depthLayout; } + void setOriginUpperLeft() { originUpperLeft = true; } + bool getOriginUpperLeft() const { return originUpperLeft; } + void setPixelCenterInteger() { pixelCenterInteger = true; } + bool getPixelCenterInteger() const { return pixelCenterInteger; } + void addBlendEquation(TBlendEquationShift b) { blendEquations |= (1 << b); } + unsigned int getBlendEquations() const { return blendEquations; } + bool setXfbBufferStride(int buffer, unsigned stride) + { + if (xfbBuffers[buffer].stride != TQualifier::layoutXfbStrideEnd) + return xfbBuffers[buffer].stride == stride; + xfbBuffers[buffer].stride = stride; + return true; + } + unsigned getXfbStride(int buffer) const { return xfbBuffers[buffer].stride; } + int addXfbBufferOffset(const TType&); + unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType, bool& contains32BitType, bool& contains16BitType) const; + unsigned int computeTypeXfbSize(const TType&, bool& contains64BitType) const; + void setLayoutOverrideCoverage() { layoutOverrideCoverage = true; } + bool getLayoutOverrideCoverage() const { return layoutOverrideCoverage; } + void setGeoPassthroughEXT() { geoPassthroughEXT = true; } + bool getGeoPassthroughEXT() const { return geoPassthroughEXT; } + void setLayoutDerivativeMode(ComputeDerivativeMode mode) { computeDerivativeMode = mode; } + bool hasLayoutDerivativeModeNone() const { return computeDerivativeMode != LayoutDerivativeNone; } + ComputeDerivativeMode getLayoutDerivativeModeNone() const { return computeDerivativeMode; } + void setLayoutPrimitiveCulling() { layoutPrimitiveCulling = true; } + bool getLayoutPrimitiveCulling() const { return layoutPrimitiveCulling; } + bool setPrimitives(int m) + { + if (primitives != TQualifier::layoutNotSet) + return primitives == m; + primitives = m; + return true; + } + int getPrimitives() const { return primitives; } + const char* addSemanticName(const TString& name) + { + return semanticNameSet.insert(name).first->c_str(); + } + void addUniformLocationOverride(const char* nameStr, int location) + { + std::string name = nameStr; + uniformLocationOverrides[name] = location; + } + + int getUniformLocationOverride(const char* nameStr) const + { + std::string name = nameStr; + auto pos = uniformLocationOverrides.find(name); + if (pos == uniformLocationOverrides.end()) + return -1; + else + return pos->second; + } + + void setUniformLocationBase(int base) { uniformLocationBase = base; } + int getUniformLocationBase() const { return uniformLocationBase; } + + void setNeedsLegalization() { needToLegalize = true; } + bool needsLegalization() const { return needToLegalize; } + + void setBinaryDoubleOutput() { binaryDoubleOutput = true; } + bool getBinaryDoubleOutput() { return binaryDoubleOutput; } +#endif // GLSLANG_WEB + +#ifdef ENABLE_HLSL + void setHlslFunctionality1() { hlslFunctionality1 = true; } + bool getHlslFunctionality1() const { return hlslFunctionality1; } + void setHlslOffsets() + { + hlslOffsets = true; + if (hlslOffsets) + processes.addProcess("hlsl-offsets"); + } + bool usingHlslOffsets() const { return hlslOffsets; } + void setHlslIoMapping(bool b) + { + hlslIoMapping = b; + if (hlslIoMapping) + processes.addProcess("hlsl-iomap"); + } + bool usingHlslIoMapping() { return hlslIoMapping; } +#else + bool getHlslFunctionality1() const { return false; } + bool usingHlslOffsets() const { return false; } + bool usingHlslIoMapping() { return false; } +#endif + + void addToCallGraph(TInfoSink&, const TString& caller, const TString& callee); + void merge(TInfoSink&, TIntermediate&); + void finalCheck(TInfoSink&, bool keepUncalled); + + bool buildConvertOp(TBasicType dst, TBasicType src, TOperator& convertOp) const; + TIntermTyped* createConversion(TBasicType convertTo, TIntermTyped* node) const; + + void addIoAccessed(const TString& name) { ioAccessed.insert(name); } + bool inIoAccessed(const TString& name) const { return ioAccessed.find(name) != ioAccessed.end(); } + + int addUsedLocation(const TQualifier&, const TType&, bool& typeCollision); + int checkLocationRange(int set, const TIoRange& range, const TType&, bool& typeCollision); + int addUsedOffsets(int binding, int offset, int numOffsets); + bool addUsedConstantId(int id); + static int computeTypeLocationSize(const TType&, EShLanguage); + static int computeTypeUniformLocationSize(const TType&); + + static int getBaseAlignmentScalar(const TType&, int& size); + static int getBaseAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor); + static int getScalarAlignment(const TType&, int& size, int& stride, bool rowMajor); + static int getMemberAlignment(const TType&, int& size, int& stride, TLayoutPacking layoutPacking, bool rowMajor); + static bool improperStraddle(const TType& type, int size, int offset); + static void updateOffset(const TType& parentType, const TType& memberType, int& offset, int& memberSize); + static int getOffset(const TType& type, int index); + static int getBlockSize(const TType& blockType); + static int computeBufferReferenceTypeSize(const TType&); + bool promote(TIntermOperator*); + void setNanMinMaxClamp(bool setting) { nanMinMaxClamp = setting; } + bool getNanMinMaxClamp() const { return nanMinMaxClamp; } + + void setSourceFile(const char* file) { if (file != nullptr) sourceFile = file; } + const std::string& getSourceFile() const { return sourceFile; } + void addSourceText(const char* text, size_t len) { sourceText.append(text, len); } + const std::string& getSourceText() const { return sourceText; } + const std::map& getIncludeText() const { return includeText; } + void addIncludeText(const char* name, const char* text, size_t len) { includeText[name].assign(text,len); } + void addProcesses(const std::vector& p) + { + for (int i = 0; i < (int)p.size(); ++i) + processes.addProcess(p[i]); + } + void addProcess(const std::string& process) { processes.addProcess(process); } + void addProcessArgument(const std::string& arg) { processes.addArgument(arg); } + const std::vector& getProcesses() const { return processes.getProcesses(); } + + // Certain explicit conversions are allowed conditionally +#ifdef GLSLANG_WEB + bool getArithemeticInt8Enabled() const { return false; } + bool getArithemeticInt16Enabled() const { return false; } + bool getArithemeticFloat16Enabled() const { return false; } + void updateNumericFeature(TNumericFeatures::feature f, bool on) { } +#else + bool getArithemeticInt8Enabled() const { + return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int8); + } + bool getArithemeticInt16Enabled() const { + return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::gpu_shader_int16) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_int16); + } + + bool getArithemeticFloat16Enabled() const { + return numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types) || + numericFeatures.contains(TNumericFeatures::gpu_shader_half_float) || + numericFeatures.contains(TNumericFeatures::shader_explicit_arithmetic_types_float16); + } + void updateNumericFeature(TNumericFeatures::feature f, bool on) + { on ? numericFeatures.insert(f) : numericFeatures.erase(f); } +#endif + +protected: + TIntermSymbol* addSymbol(int Id, const TString&, const TType&, const TConstUnionArray&, TIntermTyped* subtree, const TSourceLoc&); + void error(TInfoSink& infoSink, const char*); + void warn(TInfoSink& infoSink, const char*); + void mergeCallGraphs(TInfoSink&, TIntermediate&); + void mergeModes(TInfoSink&, TIntermediate&); + void mergeTrees(TInfoSink&, TIntermediate&); + void seedIdMap(TIdMaps& idMaps, int& maxId); + void remapIds(const TIdMaps& idMaps, int idShift, TIntermediate&); + void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals); + void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects); + void mergeImplicitArraySizes(TType&, const TType&); + void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage); + void checkCallGraphCycles(TInfoSink&); + void checkCallGraphBodies(TInfoSink&, bool keepUncalled); + void inOutLocationCheck(TInfoSink&); + TIntermAggregate* findLinkerObjects() const; + bool userOutputUsed() const; + bool isSpecializationOperation(const TIntermOperator&) const; + bool isNonuniformPropagating(TOperator) const; + bool promoteUnary(TIntermUnary&); + bool promoteBinary(TIntermBinary&); + void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&); + bool promoteAggregate(TIntermAggregate&); + void pushSelector(TIntermSequence&, const TVectorSelector&, const TSourceLoc&); + void pushSelector(TIntermSequence&, const TMatrixSelector&, const TSourceLoc&); + bool specConstantPropagates(const TIntermTyped&, const TIntermTyped&); + void performTextureUpgradeAndSamplerRemovalTransformation(TIntermNode* root); + bool isConversionAllowed(TOperator op, TIntermTyped* node) const; + std::tuple getConversionDestinationType(TBasicType type0, TBasicType type1, TOperator op) const; + + static const char* getResourceName(TResourceType); + + const EShLanguage language; // stage, known at construction time + std::string entryPointName; + std::string entryPointMangledName; + typedef std::list TGraph; + TGraph callGraph; + +#ifdef GLSLANG_ANGLE + const EProfile profile = ECoreProfile; + const int version = 450; +#else + EProfile profile; // source profile + int version; // source version +#endif + SpvVersion spvVersion; + TIntermNode* treeRoot; + std::set requestedExtensions; // cumulation of all enabled or required extensions; not connected to what subset of the shader used them + MustBeAssigned resources; + int numEntryPoints; + int numErrors; + int numPushConstants; + bool recursive; + bool invertY; + bool useStorageBuffer; + bool nanMinMaxClamp; // true if desiring min/max/clamp to favor non-NaN over NaN + bool depthReplacing; + int localSize[3]; + bool localSizeNotDefault[3]; + int localSizeSpecId[3]; +#ifndef GLSLANG_WEB +public: + const char* const implicitThisName; + const char* const implicitCounterName; +protected: + EShSource source; // source language, known a bit later + bool useVulkanMemoryModel; + int invocations; + int vertices; + TLayoutGeometry inputPrimitive; + TLayoutGeometry outputPrimitive; + bool pixelCenterInteger; + bool originUpperLeft; + TVertexSpacing vertexSpacing; + TVertexOrder vertexOrder; + TInterlockOrdering interlockOrdering; + bool pointMode; + bool earlyFragmentTests; + bool postDepthCoverage; + TLayoutDepth depthLayout; + bool hlslFunctionality1; + int blendEquations; // an 'or'ing of masks of shifts of TBlendEquationShift + bool xfbMode; + std::vector xfbBuffers; // all the data we need to track per xfb buffer + bool multiStream; + bool layoutOverrideCoverage; + bool geoPassthroughEXT; + int numShaderRecordBlocks; + ComputeDerivativeMode computeDerivativeMode; + int primitives; + int numTaskNVBlocks; + bool layoutPrimitiveCulling; + + // Base shift values + std::array shiftBinding; + + // Per-descriptor-set shift values + std::array, EResCount> shiftBindingForSet; + + std::vector resourceSetBinding; + bool autoMapBindings; + bool autoMapLocations; + bool flattenUniformArrays; + bool useUnknownFormat; + bool hlslOffsets; + bool hlslIoMapping; + bool useVariablePointers; + + std::set semanticNameSet; + + EShTextureSamplerTransformMode textureSamplerTransformMode; + + bool needToLegalize; + bool binaryDoubleOutput; + bool usePhysicalStorageBuffer; + + std::unordered_map uniformLocationOverrides; + int uniformLocationBase; + TNumericFeatures numericFeatures; +#endif + + std::unordered_set usedConstantId; // specialization constant ids used + std::vector usedAtomics; // sets of bindings used by atomic counters + std::vector usedIo[4]; // sets of used locations, one for each of in, out, uniform, and buffers + // set of names of statically read/written I/O that might need extra checking + std::set ioAccessed; + // source code of shader, useful as part of debug information + std::string sourceFile; + std::string sourceText; + + // Included text. First string is a name, second is the included text + std::map includeText; + + // for OpModuleProcessed, or equivalent + TProcesses processes; + +private: + void operator=(TIntermediate&); // prevent assignments +}; + +} // end namespace glslang + +#endif // _LOCAL_INTERMEDIATE_INCLUDED_ diff --git a/third_party/glslang/glslang/MachineIndependent/parseConst.cpp b/third_party/glslang/glslang/MachineIndependent/parseConst.cpp new file mode 100644 index 0000000..7c04743 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/parseConst.cpp @@ -0,0 +1,214 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// Traverse a tree of constants to create a single folded constant. +// It should only be used when the whole tree is known to be constant. +// + +#include "ParseHelper.h" + +namespace glslang { + +class TConstTraverser : public TIntermTraverser { +public: + TConstTraverser(const TConstUnionArray& cUnion, bool singleConstParam, TOperator constructType, const TType& t) + : unionArray(cUnion), type(t), + constructorType(constructType), singleConstantParam(singleConstParam), error(false), isMatrix(false), + matrixCols(0), matrixRows(0) { index = 0; tOp = EOpNull; } + + virtual void visitConstantUnion(TIntermConstantUnion* node); + virtual bool visitAggregate(TVisit, TIntermAggregate* node); + + int index; + TConstUnionArray unionArray; + TOperator tOp; + const TType& type; + TOperator constructorType; + bool singleConstantParam; + bool error; + int size; // size of the constructor ( 4 for vec4) + bool isMatrix; + int matrixCols; + int matrixRows; + +protected: + TConstTraverser(TConstTraverser&); + TConstTraverser& operator=(TConstTraverser&); +}; + +bool TConstTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node) +{ + if (! node->isConstructor() && node->getOp() != EOpComma) { + error = true; + + return false; + } + + bool flag = node->getSequence().size() == 1 && node->getSequence()[0]->getAsTyped()->getAsConstantUnion(); + if (flag) { + singleConstantParam = true; + constructorType = node->getOp(); + size = node->getType().computeNumComponents(); + + if (node->getType().isMatrix()) { + isMatrix = true; + matrixCols = node->getType().getMatrixCols(); + matrixRows = node->getType().getMatrixRows(); + } + } + + for (TIntermSequence::iterator p = node->getSequence().begin(); + p != node->getSequence().end(); p++) { + + if (node->getOp() == EOpComma) + index = 0; + + (*p)->traverse(this); + } + if (flag) + { + singleConstantParam = false; + constructorType = EOpNull; + size = 0; + isMatrix = false; + matrixCols = 0; + matrixRows = 0; + } + + return false; +} + +void TConstTraverser::visitConstantUnion(TIntermConstantUnion* node) +{ + TConstUnionArray leftUnionArray(unionArray); + int instanceSize = type.computeNumComponents(); + + if (index >= instanceSize) + return; + + if (! singleConstantParam) { + int rightUnionSize = node->getType().computeNumComponents(); + + const TConstUnionArray& rightUnionArray = node->getConstArray(); + for (int i = 0; i < rightUnionSize; i++) { + if (index >= instanceSize) + return; + leftUnionArray[index] = rightUnionArray[i]; + + index++; + } + } else { + int endIndex = index + size; + const TConstUnionArray& rightUnionArray = node->getConstArray(); + if (! isMatrix) { + int count = 0; + int nodeComps = node->getType().computeNumComponents(); + for (int i = index; i < endIndex; i++) { + if (i >= instanceSize) + return; + + leftUnionArray[i] = rightUnionArray[count]; + + (index)++; + + if (nodeComps > 1) + count++; + } + } else { + // constructing a matrix, but from what? + if (node->isMatrix()) { + // Matrix from a matrix; this has the outer matrix, node is the argument matrix. + // Traverse the outer, potentially bigger matrix, fill in missing pieces with the + // identity matrix. + for (int c = 0; c < matrixCols; ++c) { + for (int r = 0; r < matrixRows; ++r) { + int targetOffset = index + c * matrixRows + r; + if (r < node->getType().getMatrixRows() && c < node->getType().getMatrixCols()) { + int srcOffset = c * node->getType().getMatrixRows() + r; + leftUnionArray[targetOffset] = rightUnionArray[srcOffset]; + } else if (r == c) + leftUnionArray[targetOffset].setDConst(1.0); + else + leftUnionArray[targetOffset].setDConst(0.0); + } + } + } else { + // matrix from vector or scalar + int count = 0; + const int startIndex = index; + int nodeComps = node->getType().computeNumComponents(); + for (int i = startIndex; i < endIndex; i++) { + if (i >= instanceSize) + return; + if (nodeComps == 1) { + // If there is a single scalar parameter to a matrix + // constructor, it is used to initialize all the + // components on the matrix's diagonal, with the + // remaining components initialized to 0.0. + if (i == startIndex || (i - startIndex) % (matrixRows + 1) == 0 ) + leftUnionArray[i] = rightUnionArray[count]; + else + leftUnionArray[i].setDConst(0.0); + } else { + // construct the matrix in column-major order, from + // the components provided, in order + leftUnionArray[i] = rightUnionArray[count]; + } + + index++; + + if (nodeComps > 1) + count++; + } + } + } + } +} + +bool TIntermediate::parseConstTree(TIntermNode* root, TConstUnionArray unionArray, TOperator constructorType, const TType& t, bool singleConstantParam) +{ + if (root == 0) + return false; + + TConstTraverser it(unionArray, singleConstantParam, constructorType, t); + + root->traverse(&it); + if (it.error) + return true; + else + return false; +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/parseVersions.h b/third_party/glslang/glslang/MachineIndependent/parseVersions.h new file mode 100644 index 0000000..7248354 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/parseVersions.h @@ -0,0 +1,245 @@ +// +// Copyright (C) 2015-2018 Google, Inc. +// Copyright (C) 2017 ARM Limited. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// This is implemented in Versions.cpp + +#ifndef _PARSE_VERSIONS_INCLUDED_ +#define _PARSE_VERSIONS_INCLUDED_ + +#include "../Public/ShaderLang.h" +#include "../Include/InfoSink.h" +#include "Scan.h" + +#include + +namespace glslang { + +// +// Base class for parse helpers. +// This just has version-related information and checking. +// This class should be sufficient for preprocessing. +// +class TParseVersions { +public: + TParseVersions(TIntermediate& interm, int version, EProfile profile, + const SpvVersion& spvVersion, EShLanguage language, TInfoSink& infoSink, + bool forwardCompatible, EShMessages messages) + : +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + forwardCompatible(forwardCompatible), + profile(profile), +#endif + infoSink(infoSink), version(version), + language(language), + spvVersion(spvVersion), + intermediate(interm), messages(messages), numErrors(0), currentScanner(0) { } + virtual ~TParseVersions() { } + void requireStage(const TSourceLoc&, EShLanguageMask, const char* featureDesc); + void requireStage(const TSourceLoc&, EShLanguage, const char* featureDesc); +#ifdef GLSLANG_WEB + const EProfile profile = EEsProfile; + bool isEsProfile() const { return true; } + void requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc) + { + if (! (EEsProfile & profileMask)) + error(loc, "not supported with this profile:", featureDesc, ProfileName(profile)); + } + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, + const char* const extensions[], const char* featureDesc) + { + if ((EEsProfile & profileMask) && (minVersion == 0 || version < minVersion)) + error(loc, "not supported for this version or the enabled extensions", featureDesc, ""); + } + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, + const char* featureDesc) + { + profileRequires(loc, profileMask, minVersion, extension ? 1 : 0, &extension, featureDesc); + } + void initializeExtensionBehavior() { } + void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc) { } + void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc) { } + void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc) { } + void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc) { } + TExtensionBehavior getExtensionBehavior(const char*) { return EBhMissing; } + bool extensionTurnedOn(const char* const extension) { return false; } + bool extensionsTurnedOn(int numExtensions, const char* const extensions[]) { return false; } + void updateExtensionBehavior(int line, const char* const extension, const char* behavior) { } + void updateExtensionBehavior(const char* const extension, TExtensionBehavior) { } + void checkExtensionStage(const TSourceLoc&, const char* const extension) { } + void extensionRequires(const TSourceLoc&, const char* const extension, const char* behavior) { } + void fullIntegerCheck(const TSourceLoc&, const char* op) { } + void doubleCheck(const TSourceLoc&, const char* op) { } + bool float16Arithmetic() { return false; } + void requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { } + bool int16Arithmetic() { return false; } + void requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { } + bool int8Arithmetic() { return false; } + void requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc) { } + void int64Check(const TSourceLoc&, const char* op, bool builtIn = false) { } + void explicitFloat32Check(const TSourceLoc&, const char* op, bool builtIn = false) { } + void explicitFloat64Check(const TSourceLoc&, const char* op, bool builtIn = false) { } + bool relaxedErrors() const { return false; } + bool suppressWarnings() const { return true; } + bool isForwardCompatible() const { return false; } +#else +#ifdef GLSLANG_ANGLE + const bool forwardCompatible = true; + const EProfile profile = ECoreProfile; +#else + bool forwardCompatible; // true if errors are to be given for use of deprecated features + EProfile profile; // the declared profile in the shader (core by default) +#endif + bool isEsProfile() const { return profile == EEsProfile; } + void requireProfile(const TSourceLoc& loc, int profileMask, const char* featureDesc); + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, int numExtensions, + const char* const extensions[], const char* featureDesc); + void profileRequires(const TSourceLoc& loc, int profileMask, int minVersion, const char* extension, + const char* featureDesc); + virtual void initializeExtensionBehavior(); + virtual void checkDeprecated(const TSourceLoc&, int queryProfiles, int depVersion, const char* featureDesc); + virtual void requireNotRemoved(const TSourceLoc&, int queryProfiles, int removedVersion, const char* featureDesc); + virtual void requireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + virtual void ppRequireExtensions(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + virtual TExtensionBehavior getExtensionBehavior(const char*); + virtual bool extensionTurnedOn(const char* const extension); + virtual bool extensionsTurnedOn(int numExtensions, const char* const extensions[]); + virtual void updateExtensionBehavior(int line, const char* const extension, const char* behavior); + virtual void updateExtensionBehavior(const char* const extension, TExtensionBehavior); + virtual bool checkExtensionsRequested(const TSourceLoc&, int numExtensions, const char* const extensions[], + const char* featureDesc); + virtual void checkExtensionStage(const TSourceLoc&, const char* const extension); + virtual void extensionRequires(const TSourceLoc&, const char* const extension, const char* behavior); + virtual void fullIntegerCheck(const TSourceLoc&, const char* op); + + virtual void unimplemented(const TSourceLoc&, const char* featureDesc); + virtual void doubleCheck(const TSourceLoc&, const char* op); + virtual void float16Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void float16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool float16Arithmetic(); + virtual void requireFloat16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void int16ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool int16Arithmetic(); + virtual void requireInt16Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void int8ScalarVectorCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual bool int8Arithmetic(); + virtual void requireInt8Arithmetic(const TSourceLoc& loc, const char* op, const char* featureDesc); + virtual void float16OpaqueCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void int64Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt8Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt16Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitInt32Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitFloat32Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void explicitFloat64Check(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void fcoopmatCheck(const TSourceLoc&, const char* op, bool builtIn = false); + virtual void intcoopmatCheck(const TSourceLoc&, const char *op, bool builtIn = false); + bool relaxedErrors() const { return (messages & EShMsgRelaxedErrors) != 0; } + bool suppressWarnings() const { return (messages & EShMsgSuppressWarnings) != 0; } + bool isForwardCompatible() const { return forwardCompatible; } +#endif // GLSLANG_WEB + virtual void spvRemoved(const TSourceLoc&, const char* op); + virtual void vulkanRemoved(const TSourceLoc&, const char* op); + virtual void requireVulkan(const TSourceLoc&, const char* op); + virtual void requireSpv(const TSourceLoc&, const char* op); + virtual void requireSpv(const TSourceLoc&, const char *op, unsigned int version); + + +#if defined(GLSLANG_WEB) && !defined(GLSLANG_WEB_DEVEL) + void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) { addError(); } + void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) { } + void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) { addError(); } + void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) { } +#else + virtual void C_DECL error(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL warn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL ppError(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; + virtual void C_DECL ppWarn(const TSourceLoc&, const char* szReason, const char* szToken, + const char* szExtraInfoFormat, ...) = 0; +#endif + + void addError() { ++numErrors; } + int getNumErrors() const { return numErrors; } + + void setScanner(TInputScanner* scanner) { currentScanner = scanner; } + TInputScanner* getScanner() const { return currentScanner; } + const TSourceLoc& getCurrentLoc() const { return currentScanner->getSourceLoc(); } + void setCurrentLine(int line) { currentScanner->setLine(line); } + void setCurrentColumn(int col) { currentScanner->setColumn(col); } + void setCurrentSourceName(const char* name) { currentScanner->setFile(name); } + void setCurrentString(int string) { currentScanner->setString(string); } + + void getPreamble(std::string&); +#ifdef ENABLE_HLSL + bool isReadingHLSL() const { return (messages & EShMsgReadHlsl) == EShMsgReadHlsl; } + bool hlslEnable16BitTypes() const { return (messages & EShMsgHlslEnable16BitTypes) != 0; } + bool hlslDX9Compatible() const { return (messages & EShMsgHlslDX9Compatible) != 0; } +#else + bool isReadingHLSL() const { return false; } +#endif + + TInfoSink& infoSink; + + // compilation mode + int version; // version, updated by #version in the shader + EShLanguage language; // really the stage + SpvVersion spvVersion; + TIntermediate& intermediate; // helper for making and hooking up pieces of the parse tree + +protected: + TMap extensionBehavior; // for each extension string, what its current behavior is + TMap extensionMinSpv; // for each extension string, store minimum spirv required + EShMessages messages; // errors/warnings/rule-sets + int numErrors; // number of compile-time errors encountered + TInputScanner* currentScanner; + +private: + explicit TParseVersions(const TParseVersions&); + TParseVersions& operator=(const TParseVersions&); +}; + +} // end namespace glslang + +#endif // _PARSE_VERSIONS_INCLUDED_ diff --git a/third_party/glslang/glslang/MachineIndependent/pch.h b/third_party/glslang/glslang/MachineIndependent/pch.h new file mode 100644 index 0000000..6ea3761 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/pch.h @@ -0,0 +1,49 @@ +#ifndef _PCH_H +#define _PCH_H +// +// Copyright (C) 2018 The Khronos Group Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include "SymbolTable.h" +#include "ParseHelper.h" +#include "Scan.h" +#include "ScanContext.h" + +#endif /* _PCH_H */ diff --git a/third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp b/third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp new file mode 100644 index 0000000..aa1e0d7 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/preprocessor/Pp.cpp @@ -0,0 +1,1346 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace glslang { + +// Handle #define +int TPpContext::CPPdefine(TPpToken* ppToken) +{ + MacroSymbol mac; + + // get the macro name + int token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#define", ""); + return token; + } + if (ppToken->loc.string >= 0) { + // We are in user code; check for reserved name use: + parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#define"); + } + + // save the macro name + const int defAtom = atomStrings.getAddAtom(ppToken->name); + TSourceLoc defineLoc = ppToken->loc; // because ppToken might go to the next line before we report errors + + // gather parameters to the macro, between (...) + token = scanToken(ppToken); + if (token == '(' && !ppToken->space) { + mac.functionLike = 1; + do { + token = scanToken(ppToken); + if (mac.args.size() == 0 && token == ')') + break; + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "bad argument", "#define", ""); + + return token; + } + const int argAtom = atomStrings.getAddAtom(ppToken->name); + + // check for duplication of parameter name + bool duplicate = false; + for (size_t a = 0; a < mac.args.size(); ++a) { + if (mac.args[a] == argAtom) { + parseContext.ppError(ppToken->loc, "duplicate macro parameter", "#define", ""); + duplicate = true; + break; + } + } + if (! duplicate) + mac.args.push_back(argAtom); + token = scanToken(ppToken); + } while (token == ','); + if (token != ')') { + parseContext.ppError(ppToken->loc, "missing parenthesis", "#define", ""); + + return token; + } + + token = scanToken(ppToken); + } else if (token != '\n' && token != EndOfInput && !ppToken->space) { + parseContext.ppWarn(ppToken->loc, "missing space after macro name", "#define", ""); + + return token; + } + + // record the definition of the macro + while (token != '\n' && token != EndOfInput) { + mac.body.putToken(token, ppToken); + token = scanToken(ppToken); + if (token != '\n' && ppToken->space) + mac.body.putToken(' ', ppToken); + } + + // check for duplicate definition + MacroSymbol* existing = lookupMacroDef(defAtom); + if (existing != nullptr) { + if (! existing->undef) { + // Already defined -- need to make sure they are identical: + // "Two replacement lists are identical if and only if the + // preprocessing tokens in both have the same number, + // ordering, spelling, and white-space separation, where all + // white-space separations are considered identical." + if (existing->functionLike != mac.functionLike) { + parseContext.ppError(defineLoc, "Macro redefined; function-like versus object-like:", "#define", + atomStrings.getString(defAtom)); + } else if (existing->args.size() != mac.args.size()) { + parseContext.ppError(defineLoc, "Macro redefined; different number of arguments:", "#define", + atomStrings.getString(defAtom)); + } else { + if (existing->args != mac.args) { + parseContext.ppError(defineLoc, "Macro redefined; different argument names:", "#define", + atomStrings.getString(defAtom)); + } + // set up to compare the two + existing->body.reset(); + mac.body.reset(); + int newToken; + bool firstToken = true; + do { + int oldToken; + TPpToken oldPpToken; + TPpToken newPpToken; + oldToken = existing->body.getToken(parseContext, &oldPpToken); + newToken = mac.body.getToken(parseContext, &newPpToken); + // for the first token, preceding spaces don't matter + if (firstToken) { + newPpToken.space = oldPpToken.space; + firstToken = false; + } + if (oldToken != newToken || oldPpToken != newPpToken) { + parseContext.ppError(defineLoc, "Macro redefined; different substitutions:", "#define", + atomStrings.getString(defAtom)); + break; + } + } while (newToken != EndOfInput); + } + } + *existing = mac; + } else + addMacroDef(defAtom, mac); + + return '\n'; +} + +// Handle #undef +int TPpContext::CPPundef(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#undef", ""); + + return token; + } + + parseContext.reservedPpErrorCheck(ppToken->loc, ppToken->name, "#undef"); + + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + if (macro != nullptr) + macro->undef = 1; + token = scanToken(ppToken); + if (token != '\n') + parseContext.ppError(ppToken->loc, "can only be followed by a single macro name", "#undef", ""); + + return token; +} + +// Handle #else +/* Skip forward to appropriate spot. This is used both +** to skip to a #endif after seeing an #else, AND to skip to a #else, +** #elif, or #endif after a #if/#ifdef/#ifndef/#elif test was false. +*/ +int TPpContext::CPPelse(int matchelse, TPpToken* ppToken) +{ + int depth = 0; + int token = scanToken(ppToken); + + while (token != EndOfInput) { + if (token != '#') { + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + + if (token == EndOfInput) + return token; + + token = scanToken(ppToken); + continue; + } + + if ((token = scanToken(ppToken)) != PpAtomIdentifier) + continue; + + int nextAtom = atomStrings.getAtom(ppToken->name); + if (nextAtom == PpAtomIf || nextAtom == PpAtomIfdef || nextAtom == PpAtomIfndef) { + depth++; + if (ifdepth >= maxIfNesting || elsetracker >= maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if/#ifdef/#ifndef", ""); + return EndOfInput; + } else { + ifdepth++; + elsetracker++; + } + } else if (nextAtom == PpAtomEndif) { + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + elseSeen[elsetracker] = false; + --elsetracker; + if (depth == 0) { + // found the #endif we are looking for + if (ifdepth > 0) + --ifdepth; + break; + } + --depth; + --ifdepth; + } else if (matchelse && depth == 0) { + if (nextAtom == PpAtomElse) { + elseSeen[elsetracker] = true; + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + // found the #else we are looking for + break; + } else if (nextAtom == PpAtomElif) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + /* we decrement ifdepth here, because CPPif will increment + * it and we really want to leave it alone */ + if (ifdepth > 0) { + --ifdepth; + elseSeen[elsetracker] = false; + --elsetracker; + } + + return CPPif(ppToken); + } + } else if (nextAtom == PpAtomElse) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#else after #else", "#else", ""); + else + elseSeen[elsetracker] = true; + token = extraTokenCheck(nextAtom, ppToken, scanToken(ppToken)); + } else if (nextAtom == PpAtomElif) { + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + } + } + + return token; +} + +// Call when there should be no more tokens left on a line. +int TPpContext::extraTokenCheck(int contextAtom, TPpToken* ppToken, int token) +{ + if (token != '\n' && token != EndOfInput) { + static const char* message = "unexpected tokens following directive"; + + const char* label; + if (contextAtom == PpAtomElse) + label = "#else"; + else if (contextAtom == PpAtomElif) + label = "#elif"; + else if (contextAtom == PpAtomEndif) + label = "#endif"; + else if (contextAtom == PpAtomIf) + label = "#if"; + else if (contextAtom == PpAtomLine) + label = "#line"; + else + label = ""; + + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, message, label, ""); + else + parseContext.ppError(ppToken->loc, message, label, ""); + + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + } + + return token; +} + +enum eval_prec { + MIN_PRECEDENCE, + COND, LOGOR, LOGAND, OR, XOR, AND, EQUAL, RELATION, SHIFT, ADD, MUL, UNARY, + MAX_PRECEDENCE +}; + +namespace { + + int op_logor(int a, int b) { return a || b; } + int op_logand(int a, int b) { return a && b; } + int op_or(int a, int b) { return a | b; } + int op_xor(int a, int b) { return a ^ b; } + int op_and(int a, int b) { return a & b; } + int op_eq(int a, int b) { return a == b; } + int op_ne(int a, int b) { return a != b; } + int op_ge(int a, int b) { return a >= b; } + int op_le(int a, int b) { return a <= b; } + int op_gt(int a, int b) { return a > b; } + int op_lt(int a, int b) { return a < b; } + int op_shl(int a, int b) { return a << b; } + int op_shr(int a, int b) { return a >> b; } + int op_add(int a, int b) { return a + b; } + int op_sub(int a, int b) { return a - b; } + int op_mul(int a, int b) { return a * b; } + int op_div(int a, int b) { return a == INT_MIN && b == -1 ? 0 : a / b; } + int op_mod(int a, int b) { return a == INT_MIN && b == -1 ? 0 : a % b; } + int op_pos(int a) { return a; } + int op_neg(int a) { return -a; } + int op_cmpl(int a) { return ~a; } + int op_not(int a) { return !a; } + +}; + +struct TBinop { + int token, precedence, (*op)(int, int); +} binop[] = { + { PpAtomOr, LOGOR, op_logor }, + { PpAtomAnd, LOGAND, op_logand }, + { '|', OR, op_or }, + { '^', XOR, op_xor }, + { '&', AND, op_and }, + { PpAtomEQ, EQUAL, op_eq }, + { PpAtomNE, EQUAL, op_ne }, + { '>', RELATION, op_gt }, + { PpAtomGE, RELATION, op_ge }, + { '<', RELATION, op_lt }, + { PpAtomLE, RELATION, op_le }, + { PpAtomLeft, SHIFT, op_shl }, + { PpAtomRight, SHIFT, op_shr }, + { '+', ADD, op_add }, + { '-', ADD, op_sub }, + { '*', MUL, op_mul }, + { '/', MUL, op_div }, + { '%', MUL, op_mod }, +}; + +struct TUnop { + int token, (*op)(int); +} unop[] = { + { '+', op_pos }, + { '-', op_neg }, + { '~', op_cmpl }, + { '!', op_not }, +}; + +#define NUM_ELEMENTS(A) (sizeof(A) / sizeof(A[0])) + +int TPpContext::eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken* ppToken) +{ + TSourceLoc loc = ppToken->loc; // because we sometimes read the newline before reporting the error + if (token == PpAtomIdentifier) { + if (strcmp("defined", ppToken->name) == 0) { + if (! parseContext.isReadingHLSL() && isMacroInput()) { + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, "nonportable when expanded from macros for preprocessor expression", + "defined", ""); + else + parseContext.ppError(ppToken->loc, "cannot use in preprocessor expression when expanded from macros", + "defined", ""); + } + bool needclose = 0; + token = scanToken(ppToken); + if (token == '(') { + needclose = true; + token = scanToken(ppToken); + } + if (token != PpAtomIdentifier) { + parseContext.ppError(loc, "incorrect directive, expected identifier", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + res = macro != nullptr ? !macro->undef : 0; + token = scanToken(ppToken); + if (needclose) { + if (token != ')') { + parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + token = scanToken(ppToken); + } + } else { + token = tokenPaste(token, *ppToken); + token = evalToToken(token, shortCircuit, res, err, ppToken); + return eval(token, precedence, shortCircuit, res, err, ppToken); + } + } else if (token == PpAtomConstInt) { + res = ppToken->ival; + token = scanToken(ppToken); + } else if (token == '(') { + token = scanToken(ppToken); + token = eval(token, MIN_PRECEDENCE, shortCircuit, res, err, ppToken); + if (! err) { + if (token != ')') { + parseContext.ppError(loc, "expected ')'", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + token = scanToken(ppToken); + } + } else { + int op = NUM_ELEMENTS(unop) - 1; + for (; op >= 0; op--) { + if (unop[op].token == token) + break; + } + if (op >= 0) { + token = scanToken(ppToken); + token = eval(token, UNARY, shortCircuit, res, err, ppToken); + res = unop[op].op(res); + } else { + parseContext.ppError(loc, "bad expression", "preprocessor evaluation", ""); + err = true; + res = 0; + + return token; + } + } + + token = evalToToken(token, shortCircuit, res, err, ppToken); + + // Perform evaluation of binary operation, if there is one, otherwise we are done. + while (! err) { + if (token == ')' || token == '\n') + break; + int op; + for (op = NUM_ELEMENTS(binop) - 1; op >= 0; op--) { + if (binop[op].token == token) + break; + } + if (op < 0 || binop[op].precedence <= precedence) + break; + int leftSide = res; + + // Setup short-circuiting, needed for ES, unless already in a short circuit. + // (Once in a short-circuit, can't turn off again, until that whole subexpression is done. + if (! shortCircuit) { + if ((token == PpAtomOr && leftSide == 1) || + (token == PpAtomAnd && leftSide == 0)) + shortCircuit = true; + } + + token = scanToken(ppToken); + token = eval(token, binop[op].precedence, shortCircuit, res, err, ppToken); + + if (binop[op].op == op_div || binop[op].op == op_mod) { + if (res == 0) { + parseContext.ppError(loc, "division by 0", "preprocessor evaluation", ""); + res = 1; + } + } + res = binop[op].op(leftSide, res); + } + + return token; +} + +// Expand macros, skipping empty expansions, to get to the first real token in those expansions. +int TPpContext::evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken* ppToken) +{ + while (token == PpAtomIdentifier && strcmp("defined", ppToken->name) != 0) { + switch (MacroExpand(ppToken, true, false)) { + case MacroExpandNotStarted: + case MacroExpandError: + parseContext.ppError(ppToken->loc, "can't evaluate expression", "preprocessor evaluation", ""); + err = true; + res = 0; + break; + case MacroExpandStarted: + break; + case MacroExpandUndef: + if (! shortCircuit && parseContext.isEsProfile()) { + const char* message = "undefined macro in expression not allowed in es profile"; + if (parseContext.relaxedErrors()) + parseContext.ppWarn(ppToken->loc, message, "preprocessor evaluation", ppToken->name); + else + parseContext.ppError(ppToken->loc, message, "preprocessor evaluation", ppToken->name); + } + break; + } + token = scanToken(ppToken); + if (err) + break; + } + + return token; +} + +// Handle #if +int TPpContext::CPPif(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (ifdepth >= maxIfNesting || elsetracker >= maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#if", ""); + return EndOfInput; + } else { + elsetracker++; + ifdepth++; + } + int res = 0; + bool err = false; + token = eval(token, MIN_PRECEDENCE, false, res, err, ppToken); + token = extraTokenCheck(PpAtomIf, ppToken, token); + if (!res && !err) + token = CPPelse(1, ppToken); + + return token; +} + +// Handle #ifdef +int TPpContext::CPPifdef(int defined, TPpToken* ppToken) +{ + int token = scanToken(ppToken); + if (ifdepth > maxIfNesting || elsetracker > maxIfNesting) { + parseContext.ppError(ppToken->loc, "maximum nesting depth exceeded", "#ifdef", ""); + return EndOfInput; + } else { + elsetracker++; + ifdepth++; + } + + if (token != PpAtomIdentifier) { + if (defined) + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifdef", ""); + else + parseContext.ppError(ppToken->loc, "must be followed by macro name", "#ifndef", ""); + } else { + MacroSymbol* macro = lookupMacroDef(atomStrings.getAtom(ppToken->name)); + token = scanToken(ppToken); + if (token != '\n') { + parseContext.ppError(ppToken->loc, "unexpected tokens following #ifdef directive - expected a newline", "#ifdef", ""); + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + } + if (((macro != nullptr && !macro->undef) ? 1 : 0) != defined) + token = CPPelse(1, ppToken); + } + + return token; +} + +// Handle #include ... +// TODO: Handle macro expansions for the header name +int TPpContext::CPPinclude(TPpToken* ppToken) +{ + const TSourceLoc directiveLoc = ppToken->loc; + bool startWithLocalSearch = true; // to additionally include the extra "" paths + int token; + + // Find the first non-whitespace char after #include + int ch = getChar(); + while (ch == ' ' || ch == '\t') { + ch = getChar(); + } + if (ch == '<') { + // style + startWithLocalSearch = false; + token = scanHeaderName(ppToken, '>'); + } else if (ch == '"') { + // "header-name" style + token = scanHeaderName(ppToken, '"'); + } else { + // unexpected, get the full token to generate the error + ungetChar(); + token = scanToken(ppToken); + } + + if (token != PpAtomConstString) { + parseContext.ppError(directiveLoc, "must be followed by a header name", "#include", ""); + return token; + } + + // Make a copy of the name because it will be overwritten by the next token scan. + const std::string filename = ppToken->name; + + // See if the directive was well formed + token = scanToken(ppToken); + if (token != '\n') { + if (token == EndOfInput) + parseContext.ppError(ppToken->loc, "expected newline after header name:", "#include", "%s", filename.c_str()); + else + parseContext.ppError(ppToken->loc, "extra content after header name:", "#include", "%s", filename.c_str()); + return token; + } + + // Process well-formed directive + + // Find the inclusion, first look in "Local" ("") paths, if requested, + // otherwise, only search the "System" (<>) paths. + TShader::Includer::IncludeResult* res = nullptr; + if (startWithLocalSearch) + res = includer.includeLocal(filename.c_str(), currentSourceFile.c_str(), includeStack.size() + 1); + if (res == nullptr || res->headerName.empty()) { + includer.releaseInclude(res); + res = includer.includeSystem(filename.c_str(), currentSourceFile.c_str(), includeStack.size() + 1); + } + + // Process the results + if (res != nullptr && !res->headerName.empty()) { + if (res->headerData != nullptr && res->headerLength > 0) { + // path for processing one or more tokens from an included header, hand off 'res' + const bool forNextLine = parseContext.lineDirectiveShouldSetNextLine(); + std::ostringstream prologue; + std::ostringstream epilogue; + prologue << "#line " << forNextLine << " " << "\"" << res->headerName << "\"\n"; + epilogue << (res->headerData[res->headerLength - 1] == '\n'? "" : "\n") << + "#line " << directiveLoc.line + forNextLine << " " << directiveLoc.getStringNameOrNum() << "\n"; + pushInput(new TokenizableIncludeFile(directiveLoc, prologue.str(), res, epilogue.str(), this)); + parseContext.intermediate.addIncludeText(res->headerName.c_str(), res->headerData, res->headerLength); + // There's no "current" location anymore. + parseContext.setCurrentColumn(0); + } else { + // things are okay, but there is nothing to process + includer.releaseInclude(res); + } + } else { + // error path, clean up + std::string message = + res != nullptr ? std::string(res->headerData, res->headerLength) + : std::string("Could not process include directive"); + parseContext.ppError(directiveLoc, message.c_str(), "#include", "for header name: %s", filename.c_str()); + includer.releaseInclude(res); + } + + return token; +} + +// Handle #line +int TPpContext::CPPline(TPpToken* ppToken) +{ + // "#line must have, after macro substitution, one of the following forms: + // "#line line + // "#line line source-string-number" + + int token = scanToken(ppToken); + const TSourceLoc directiveLoc = ppToken->loc; + if (token == '\n') { + parseContext.ppError(ppToken->loc, "must by followed by an integral literal", "#line", ""); + return token; + } + + int lineRes = 0; // Line number after macro expansion. + int lineToken = 0; + bool hasFile = false; + int fileRes = 0; // Source file number after macro expansion. + const char* sourceName = nullptr; // Optional source file name. + bool lineErr = false; + bool fileErr = false; + disableEscapeSequences = true; + token = eval(token, MIN_PRECEDENCE, false, lineRes, lineErr, ppToken); + disableEscapeSequences = false; + if (! lineErr) { + lineToken = lineRes; + if (token == '\n') + ++lineRes; + + if (parseContext.lineDirectiveShouldSetNextLine()) + --lineRes; + parseContext.setCurrentLine(lineRes); + + if (token != '\n') { +#ifndef GLSLANG_WEB + if (token == PpAtomConstString) { + parseContext.ppRequireExtensions(directiveLoc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based #line"); + // We need to save a copy of the string instead of pointing + // to the name field of the token since the name field + // will likely be overwritten by the next token scan. + sourceName = atomStrings.getString(atomStrings.getAddAtom(ppToken->name)); + parseContext.setCurrentSourceName(sourceName); + hasFile = true; + token = scanToken(ppToken); + } else +#endif + { + token = eval(token, MIN_PRECEDENCE, false, fileRes, fileErr, ppToken); + if (! fileErr) { + parseContext.setCurrentString(fileRes); + hasFile = true; + } + } + } + } + if (!fileErr && !lineErr) { + parseContext.notifyLineDirective(directiveLoc.line, lineToken, hasFile, fileRes, sourceName); + } + token = extraTokenCheck(PpAtomLine, ppToken, token); + + return token; +} + +// Handle #error +int TPpContext::CPPerror(TPpToken* ppToken) +{ + disableEscapeSequences = true; + int token = scanToken(ppToken); + disableEscapeSequences = false; + std::string message; + TSourceLoc loc = ppToken->loc; + + while (token != '\n' && token != EndOfInput) { + if (token == PpAtomConstInt16 || token == PpAtomConstUint16 || + token == PpAtomConstInt || token == PpAtomConstUint || + token == PpAtomConstInt64 || token == PpAtomConstUint64 || + token == PpAtomConstFloat16 || + token == PpAtomConstFloat || token == PpAtomConstDouble) { + message.append(ppToken->name); + } else if (token == PpAtomIdentifier || token == PpAtomConstString) { + message.append(ppToken->name); + } else { + message.append(atomStrings.getString(token)); + } + message.append(" "); + token = scanToken(ppToken); + } + parseContext.notifyErrorDirective(loc.line, message.c_str()); + // store this msg into the shader's information log..set the Compile Error flag!!!! + parseContext.ppError(loc, message.c_str(), "#error", ""); + + return '\n'; +} + +// Handle #pragma +int TPpContext::CPPpragma(TPpToken* ppToken) +{ + char SrcStrName[2]; + TVector tokens; + + TSourceLoc loc = ppToken->loc; // because we go to the next line before processing + int token = scanToken(ppToken); + while (token != '\n' && token != EndOfInput) { + switch (token) { + case PpAtomIdentifier: + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstFloat: + case PpAtomConstDouble: + case PpAtomConstFloat16: + tokens.push_back(ppToken->name); + break; + default: + SrcStrName[0] = (char)token; + SrcStrName[1] = '\0'; + tokens.push_back(SrcStrName); + } + token = scanToken(ppToken); + } + + if (token == EndOfInput) + parseContext.ppError(loc, "directive must end with a newline", "#pragma", ""); + else + parseContext.handlePragma(loc, tokens); + + return token; +} + +// #version: This is just for error checking: the version and profile are decided before preprocessing starts +int TPpContext::CPPversion(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + + if (errorOnVersion || versionSeen) { + if (parseContext.isReadingHLSL()) + parseContext.ppError(ppToken->loc, "invalid preprocessor command", "#version", ""); + else + parseContext.ppError(ppToken->loc, "must occur first in shader", "#version", ""); + } + versionSeen = true; + + if (token == '\n') { + parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", ""); + + return token; + } + + if (token != PpAtomConstInt) + parseContext.ppError(ppToken->loc, "must be followed by version number", "#version", ""); + + ppToken->ival = atoi(ppToken->name); + int versionNumber = ppToken->ival; + int line = ppToken->loc.line; + token = scanToken(ppToken); + + if (token == '\n') { + parseContext.notifyVersion(line, versionNumber, nullptr); + return token; + } else { + int profileAtom = atomStrings.getAtom(ppToken->name); + if (profileAtom != PpAtomCore && + profileAtom != PpAtomCompatibility && + profileAtom != PpAtomEs) + parseContext.ppError(ppToken->loc, "bad profile name; use es, core, or compatibility", "#version", ""); + parseContext.notifyVersion(line, versionNumber, ppToken->name); + token = scanToken(ppToken); + + if (token == '\n') + return token; + else + parseContext.ppError(ppToken->loc, "bad tokens following profile -- expected newline", "#version", ""); + } + + return token; +} + +// Handle #extension +int TPpContext::CPPextension(TPpToken* ppToken) +{ + int line = ppToken->loc.line; + int token = scanToken(ppToken); + char extensionName[MaxTokenLength + 1]; + + if (token=='\n') { + parseContext.ppError(ppToken->loc, "extension name not specified", "#extension", ""); + return token; + } + + if (token != PpAtomIdentifier) + parseContext.ppError(ppToken->loc, "extension name expected", "#extension", ""); + + snprintf(extensionName, sizeof(extensionName), "%s", ppToken->name); + + token = scanToken(ppToken); + if (token != ':') { + parseContext.ppError(ppToken->loc, "':' missing after extension name", "#extension", ""); + return token; + } + + token = scanToken(ppToken); + if (token != PpAtomIdentifier) { + parseContext.ppError(ppToken->loc, "behavior for extension not specified", "#extension", ""); + return token; + } + + parseContext.updateExtensionBehavior(line, extensionName, ppToken->name); + parseContext.notifyExtensionDirective(line, extensionName, ppToken->name); + + token = scanToken(ppToken); + if (token == '\n') + return token; + else + parseContext.ppError(ppToken->loc, "extra tokens -- expected newline", "#extension",""); + + return token; +} + +int TPpContext::readCPPline(TPpToken* ppToken) +{ + int token = scanToken(ppToken); + + if (token == PpAtomIdentifier) { + switch (atomStrings.getAtom(ppToken->name)) { + case PpAtomDefine: + token = CPPdefine(ppToken); + break; + case PpAtomElse: + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#else after #else", "#else", ""); + elseSeen[elsetracker] = true; + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#else", ""); + token = extraTokenCheck(PpAtomElse, ppToken, scanToken(ppToken)); + token = CPPelse(0, ppToken); + break; + case PpAtomElif: + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#elif", ""); + if (elseSeen[elsetracker]) + parseContext.ppError(ppToken->loc, "#elif after #else", "#elif", ""); + // this token is really a dont care, but we still need to eat the tokens + token = scanToken(ppToken); + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + token = CPPelse(0, ppToken); + break; + case PpAtomEndif: + if (ifdepth == 0) + parseContext.ppError(ppToken->loc, "mismatched statements", "#endif", ""); + else { + elseSeen[elsetracker] = false; + --elsetracker; + --ifdepth; + } + token = extraTokenCheck(PpAtomEndif, ppToken, scanToken(ppToken)); + break; + case PpAtomIf: + token = CPPif(ppToken); + break; + case PpAtomIfdef: + token = CPPifdef(1, ppToken); + break; + case PpAtomIfndef: + token = CPPifdef(0, ppToken); + break; + case PpAtomLine: + token = CPPline(ppToken); + break; +#ifndef GLSLANG_WEB + case PpAtomInclude: + if(!parseContext.isReadingHLSL()) { + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_include_directive, "#include"); + } + token = CPPinclude(ppToken); + break; + case PpAtomPragma: + token = CPPpragma(ppToken); + break; +#endif + case PpAtomUndef: + token = CPPundef(ppToken); + break; + case PpAtomError: + token = CPPerror(ppToken); + break; + case PpAtomVersion: + token = CPPversion(ppToken); + break; + case PpAtomExtension: + token = CPPextension(ppToken); + break; + default: + parseContext.ppError(ppToken->loc, "invalid directive:", "#", ppToken->name); + break; + } + } else if (token != '\n' && token != EndOfInput) + parseContext.ppError(ppToken->loc, "invalid directive", "#", ""); + + while (token != '\n' && token != EndOfInput) + token = scanToken(ppToken); + + return token; +} + +// Context-dependent parsing of a #include . +// Assumes no macro expansions etc. are being done; the name is just on the current input. +// Always creates a name and returns PpAtomicConstString, unless we run out of input. +int TPpContext::scanHeaderName(TPpToken* ppToken, char delimit) +{ + bool tooLong = false; + + if (inputStack.empty()) + return EndOfInput; + + int len = 0; + ppToken->name[0] = '\0'; + do { + int ch = inputStack.back()->getch(); + + // done yet? + if (ch == delimit) { + ppToken->name[len] = '\0'; + if (tooLong) + parseContext.ppError(ppToken->loc, "header name too long", "", ""); + return PpAtomConstString; + } else if (ch == EndOfInput) + return EndOfInput; + + // found a character to expand the name with + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else + tooLong = true; + } while (true); +} + +// Macro-expand a macro argument 'arg' to create 'expandedArg'. +// Does not replace 'arg'. +// Returns nullptr if no expanded argument is created. +TPpContext::TokenStream* TPpContext::PrescanMacroArg(TokenStream& arg, TPpToken* ppToken, bool newLineOkay) +{ + // expand the argument + TokenStream* expandedArg = new TokenStream; + pushInput(new tMarkerInput(this)); + pushTokenStreamInput(arg); + int token; + while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) { + token = tokenPaste(token, *ppToken); + if (token == PpAtomIdentifier) { + switch (MacroExpand(ppToken, false, newLineOkay)) { + case MacroExpandNotStarted: + break; + case MacroExpandError: + // toss the rest of the pushed-input argument by scanning until tMarkerInput + while ((token = scanToken(ppToken)) != tMarkerInput::marker && token != EndOfInput) + ; + break; + case MacroExpandStarted: + case MacroExpandUndef: + continue; + } + } + if (token == tMarkerInput::marker || token == EndOfInput) + break; + expandedArg->putToken(token, ppToken); + } + + if (token != tMarkerInput::marker) { + // Error, or MacroExpand ate the marker, so had bad input, recover + delete expandedArg; + expandedArg = nullptr; + } + + return expandedArg; +} + +// +// Return the next token for a macro expansion, handling macro arguments, +// whose semantics are dependent on being adjacent to ##. +// +int TPpContext::tMacroInput::scan(TPpToken* ppToken) +{ + int token; + do { + token = mac->body.getToken(pp->parseContext, ppToken); + } while (token == ' '); // handle white space in macro + + // Hash operators basically turn off a round of macro substitution + // (the round done on the argument before the round done on the RHS of the + // macro definition): + // + // "A parameter in the replacement list, unless preceded by a # or ## + // preprocessing token or followed by a ## preprocessing token (see below), + // is replaced by the corresponding argument after all macros contained + // therein have been expanded." + // + // "If, in the replacement list, a parameter is immediately preceded or + // followed by a ## preprocessing token, the parameter is replaced by the + // corresponding argument's preprocessing token sequence." + + bool pasting = false; + if (postpaste) { + // don't expand next token + pasting = true; + postpaste = false; + } + + if (prepaste) { + // already know we should be on a ##, verify + assert(token == PpAtomPaste); + prepaste = false; + postpaste = true; + } + + // see if are preceding a ## + if (mac->body.peekUntokenizedPasting()) { + prepaste = true; + pasting = true; + } + + // HLSL does expand macros before concatenation + if (pasting && pp->parseContext.isReadingHLSL()) + pasting = false; + + // TODO: preprocessor: properly handle whitespace (or lack of it) between tokens when expanding + if (token == PpAtomIdentifier) { + int i; + for (i = (int)mac->args.size() - 1; i >= 0; i--) + if (strcmp(pp->atomStrings.getString(mac->args[i]), ppToken->name) == 0) + break; + if (i >= 0) { + TokenStream* arg = expandedArgs[i]; + if (arg == nullptr || pasting) + arg = args[i]; + pp->pushTokenStreamInput(*arg, prepaste); + + return pp->scanToken(ppToken); + } + } + + if (token == EndOfInput) + mac->busy = 0; + + return token; +} + +// return a textual zero, for scanning a macro that was never defined +int TPpContext::tZeroInput::scan(TPpToken* ppToken) +{ + if (done) + return EndOfInput; + + ppToken->name[0] = '0'; + ppToken->name[1] = 0; + ppToken->ival = 0; + ppToken->space = false; + done = true; + + return PpAtomConstInt; +} + +// +// Check a token to see if it is a macro that should be expanded: +// - If it is, and defined, push a tInput that will produce the appropriate +// expansion and return MacroExpandStarted. +// - If it is, but undefined, and expandUndef is requested, push a tInput +// that will expand to 0 and return MacroExpandUndef. +// - Otherwise, there is no expansion, and there are two cases: +// * It might be okay there is no expansion, and no specific error was +// detected. Returns MacroExpandNotStarted. +// * The expansion was started, but could not be completed, due to an error +// that cannot be recovered from. Returns MacroExpandError. +// +MacroExpandResult TPpContext::MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay) +{ + ppToken->space = false; + int macroAtom = atomStrings.getAtom(ppToken->name); + switch (macroAtom) { + case PpAtomLineMacro: + // Arguments which are macro have been replaced in the first stage. + if (ppToken->ival == 0) + ppToken->ival = parseContext.getCurrentLoc().line; + snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + + case PpAtomFileMacro: { + if (parseContext.getCurrentLoc().name) + parseContext.ppRequireExtensions(ppToken->loc, 1, &E_GL_GOOGLE_cpp_style_line_directive, "filename-based __FILE__"); + ppToken->ival = parseContext.getCurrentLoc().string; + snprintf(ppToken->name, sizeof(ppToken->name), "%s", ppToken->loc.getStringNameOrNum().c_str()); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + } + + case PpAtomVersionMacro: + ppToken->ival = parseContext.version; + snprintf(ppToken->name, sizeof(ppToken->name), "%d", ppToken->ival); + UngetToken(PpAtomConstInt, ppToken); + return MacroExpandStarted; + + default: + break; + } + + MacroSymbol* macro = macroAtom == 0 ? nullptr : lookupMacroDef(macroAtom); + + // no recursive expansions + if (macro != nullptr && macro->busy) + return MacroExpandNotStarted; + + // not expanding undefined macros + if ((macro == nullptr || macro->undef) && ! expandUndef) + return MacroExpandNotStarted; + + // 0 is the value of an undefined macro + if ((macro == nullptr || macro->undef) && expandUndef) { + pushInput(new tZeroInput(this)); + return MacroExpandUndef; + } + + tMacroInput *in = new tMacroInput(this); + + TSourceLoc loc = ppToken->loc; // in case we go to the next line before discovering the error + in->mac = macro; + if (macro->functionLike) { + // We don't know yet if this will be a successful call of a + // function-like macro; need to look for a '(', but without trashing + // the passed in ppToken, until we know we are no longer speculative. + TPpToken parenToken; + int token = scanToken(&parenToken); + if (newLineOkay) { + while (token == '\n') + token = scanToken(&parenToken); + } + if (token != '(') { + // Function-like macro called with object-like syntax: okay, don't expand. + // (We ate exactly one token that might not be white space; put it back. + UngetToken(token, &parenToken); + delete in; + return MacroExpandNotStarted; + } + in->args.resize(in->mac->args.size()); + for (size_t i = 0; i < in->mac->args.size(); i++) + in->args[i] = new TokenStream; + in->expandedArgs.resize(in->mac->args.size()); + for (size_t i = 0; i < in->mac->args.size(); i++) + in->expandedArgs[i] = nullptr; + size_t arg = 0; + bool tokenRecorded = false; + do { + TVector nestStack; + while (true) { + token = scanToken(ppToken); + if (token == EndOfInput || token == tMarkerInput::marker) { + parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + if (token == '\n') { + if (! newLineOkay) { + parseContext.ppError(loc, "End of line in macro substitution:", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + continue; + } + if (token == '#') { + parseContext.ppError(ppToken->loc, "unexpected '#'", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + if (in->mac->args.size() == 0 && token != ')') + break; + if (nestStack.size() == 0 && (token == ',' || token == ')')) + break; + if (token == '(') + nestStack.push_back(')'); + else if (token == '{' && parseContext.isReadingHLSL()) + nestStack.push_back('}'); + else if (nestStack.size() > 0 && token == nestStack.back()) + nestStack.pop_back(); + + //Macro replacement list is expanded in the last stage. + if (atomStrings.getAtom(ppToken->name) == PpAtomLineMacro) + ppToken->ival = parseContext.getCurrentLoc().line; + + in->args[arg]->putToken(token, ppToken); + tokenRecorded = true; + } + // end of single argument scan + + if (token == ')') { + // closing paren of call + if (in->mac->args.size() == 1 && !tokenRecorded) + break; + arg++; + break; + } + arg++; + } while (arg < in->mac->args.size()); + // end of all arguments scan + + if (arg < in->mac->args.size()) + parseContext.ppError(loc, "Too few args in Macro", "macro expansion", atomStrings.getString(macroAtom)); + else if (token != ')') { + // Error recover code; find end of call, if possible + int depth = 0; + while (token != EndOfInput && (depth > 0 || token != ')')) { + if (token == ')' || token == '}') + depth--; + token = scanToken(ppToken); + if (token == '(' || token == '{') + depth++; + } + + if (token == EndOfInput) { + parseContext.ppError(loc, "End of input in macro", "macro expansion", atomStrings.getString(macroAtom)); + delete in; + return MacroExpandError; + } + parseContext.ppError(loc, "Too many args in macro", "macro expansion", atomStrings.getString(macroAtom)); + } + + // We need both expanded and non-expanded forms of the argument, for whether or + // not token pasting will be applied later when the argument is consumed next to ##. + for (size_t i = 0; i < in->mac->args.size(); i++) + in->expandedArgs[i] = PrescanMacroArg(*in->args[i], ppToken, newLineOkay); + } + + pushInput(in); + macro->busy = 1; + macro->body.reset(); + + return MacroExpandStarted; +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp new file mode 100644 index 0000000..06c2333 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpAtom.cpp @@ -0,0 +1,181 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace { + +using namespace glslang; + +const struct { + int val; + const char* str; +} tokens[] = { + + { PPAtomAddAssign, "+=" }, + { PPAtomSubAssign, "-=" }, + { PPAtomMulAssign, "*=" }, + { PPAtomDivAssign, "/=" }, + { PPAtomModAssign, "%=" }, + + { PpAtomRight, ">>" }, + { PpAtomLeft, "<<" }, + { PpAtomAnd, "&&" }, + { PpAtomOr, "||" }, + { PpAtomXor, "^^" }, + + { PpAtomRightAssign, ">>=" }, + { PpAtomLeftAssign, "<<=" }, + { PpAtomAndAssign, "&=" }, + { PpAtomOrAssign, "|=" }, + { PpAtomXorAssign, "^=" }, + + { PpAtomEQ, "==" }, + { PpAtomNE, "!=" }, + { PpAtomGE, ">=" }, + { PpAtomLE, "<=" }, + + { PpAtomDecrement, "--" }, + { PpAtomIncrement, "++" }, + + { PpAtomColonColon, "::" }, + + { PpAtomDefine, "define" }, + { PpAtomUndef, "undef" }, + { PpAtomIf, "if" }, + { PpAtomElif, "elif" }, + { PpAtomElse, "else" }, + { PpAtomEndif, "endif" }, + { PpAtomIfdef, "ifdef" }, + { PpAtomIfndef, "ifndef" }, + { PpAtomLine, "line" }, + { PpAtomPragma, "pragma" }, + { PpAtomError, "error" }, + + { PpAtomVersion, "version" }, + { PpAtomCore, "core" }, + { PpAtomCompatibility, "compatibility" }, + { PpAtomEs, "es" }, + { PpAtomExtension, "extension" }, + + { PpAtomLineMacro, "__LINE__" }, + { PpAtomFileMacro, "__FILE__" }, + { PpAtomVersionMacro, "__VERSION__" }, + + { PpAtomInclude, "include" }, +}; + +} // end anonymous namespace + +namespace glslang { + +// +// Initialize the atom table. +// +TStringAtomMap::TStringAtomMap() +{ + badToken.assign(""); + + // Add single character tokens to the atom table: + const char* s = "~!%^&*()-+=|,.<>/?;:[]{}#\\"; + char t[2]; + + t[1] = '\0'; + while (*s) { + t[0] = *s; + addAtomFixed(t, s[0]); + s++; + } + + // Add multiple character scanner tokens : + for (size_t ii = 0; ii < sizeof(tokens)/sizeof(tokens[0]); ii++) + addAtomFixed(tokens[ii].str, tokens[ii].val); + + nextAtom = PpAtomLast; +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp new file mode 100644 index 0000000..1363ce2 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.cpp @@ -0,0 +1,120 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#include +#include + +#include "PpContext.h" + +namespace glslang { + +TPpContext::TPpContext(TParseContextBase& pc, const std::string& rootFileName, TShader::Includer& inclr) : + preamble(0), strings(0), previous_token('\n'), parseContext(pc), includer(inclr), inComment(false), + rootFileName(rootFileName), + currentSourceFile(rootFileName), + disableEscapeSequences(false) +{ + ifdepth = 0; + for (elsetracker = 0; elsetracker < maxIfNesting; elsetracker++) + elseSeen[elsetracker] = false; + elsetracker = 0; + + strtodStream.imbue(std::locale::classic()); +} + +TPpContext::~TPpContext() +{ + delete [] preamble; + + // free up the inputStack + while (! inputStack.empty()) + popInput(); +} + +void TPpContext::setInput(TInputScanner& input, bool versionWillBeError) +{ + assert(inputStack.size() == 0); + + pushInput(new tStringInput(this, input)); + + errorOnVersion = versionWillBeError; + versionSeen = false; +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.h b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.h new file mode 100644 index 0000000..714b5ea --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpContext.h @@ -0,0 +1,703 @@ +// +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef PPCONTEXT_H +#define PPCONTEXT_H + +#include +#include +#include + +#include "../ParseHelper.h" +#include "PpTokens.h" + +/* windows only pragma */ +#ifdef _MSC_VER + #pragma warning(disable : 4127) +#endif + +namespace glslang { + +class TPpToken { +public: + TPpToken() { clear(); } + void clear() + { + space = false; + i64val = 0; + loc.init(); + name[0] = 0; + } + + // Used for comparing macro definitions, so checks what is relevant for that. + bool operator==(const TPpToken& right) const + { + return space == right.space && + ival == right.ival && dval == right.dval && i64val == right.i64val && + strncmp(name, right.name, MaxTokenLength) == 0; + } + bool operator!=(const TPpToken& right) const { return ! operator==(right); } + + TSourceLoc loc; + // True if a space (for white space or a removed comment) should also be + // recognized, in front of the token returned: + bool space; + // Numeric value of the token: + union { + int ival; + double dval; + long long i64val; + }; + // Text string of the token: + char name[MaxTokenLength + 1]; +}; + +class TStringAtomMap { +// +// Implementation is in PpAtom.cpp +// +// Maintain a bi-directional mapping between relevant preprocessor strings and +// "atoms" which a unique integers (small, contiguous, not hash-like) per string. +// +public: + TStringAtomMap(); + + // Map string -> atom. + // Return 0 if no existing string. + int getAtom(const char* s) const + { + auto it = atomMap.find(s); + return it == atomMap.end() ? 0 : it->second; + } + + // Map a new or existing string -> atom, inventing a new atom if necessary. + int getAddAtom(const char* s) + { + int atom = getAtom(s); + if (atom == 0) { + atom = nextAtom++; + addAtomFixed(s, atom); + } + return atom; + } + + // Map atom -> string. + const char* getString(int atom) const { return stringMap[atom]->c_str(); } + +protected: + TStringAtomMap(TStringAtomMap&); + TStringAtomMap& operator=(TStringAtomMap&); + + TUnorderedMap atomMap; + TVector stringMap; // these point into the TString in atomMap + int nextAtom; + + // Bad source characters can lead to bad atoms, so gracefully handle those by + // pre-filling the table with them (to avoid if tests later). + TString badToken; + + // Add bi-directional mappings: + // - string -> atom + // - atom -> string + void addAtomFixed(const char* s, int atom) + { + auto it = atomMap.insert(std::pair(s, atom)).first; + if (stringMap.size() < (size_t)atom + 1) + stringMap.resize(atom + 100, &badToken); + stringMap[atom] = &it->first; + } +}; + +class TInputScanner; + +enum MacroExpandResult { + MacroExpandNotStarted, // macro not expanded, which might not be an error + MacroExpandError, // a clear error occurred while expanding, no expansion + MacroExpandStarted, // macro expansion process has started + MacroExpandUndef // macro is undefined and will be expanded +}; + +// This class is the result of turning a huge pile of C code communicating through globals +// into a class. This was done to allowing instancing to attain thread safety. +// Don't expect too much in terms of OO design. +class TPpContext { +public: + TPpContext(TParseContextBase&, const std::string& rootFileName, TShader::Includer&); + virtual ~TPpContext(); + + void setPreamble(const char* preamble, size_t length); + + int tokenize(TPpToken& ppToken); + int tokenPaste(int token, TPpToken&); + + class tInput { + public: + tInput(TPpContext* p) : done(false), pp(p) { } + virtual ~tInput() { } + + virtual int scan(TPpToken*) = 0; + virtual int getch() = 0; + virtual void ungetch() = 0; + virtual bool peekPasting() { return false; } // true when about to see ## + virtual bool peekContinuedPasting(int) { return false; } // true when non-spaced tokens can paste + virtual bool endOfReplacementList() { return false; } // true when at the end of a macro replacement list (RHS of #define) + virtual bool isMacroInput() { return false; } + + // Will be called when we start reading tokens from this instance + virtual void notifyActivated() {} + // Will be called when we do not read tokens from this instance anymore + virtual void notifyDeleted() {} + protected: + bool done; + TPpContext* pp; + }; + + void setInput(TInputScanner& input, bool versionWillBeError); + + void pushInput(tInput* in) + { + inputStack.push_back(in); + in->notifyActivated(); + } + void popInput() + { + inputStack.back()->notifyDeleted(); + delete inputStack.back(); + inputStack.pop_back(); + } + + // + // From PpTokens.cpp + // + + // Capture the needed parts of a token stream for macro recording/playback. + class TokenStream { + public: + // Manage a stream of these 'Token', which capture the relevant parts + // of a TPpToken, plus its atom. + class Token { + public: + Token(int atom, const TPpToken& ppToken) : + atom(atom), + space(ppToken.space), + i64val(ppToken.i64val), + name(ppToken.name) { } + int get(TPpToken& ppToken) + { + ppToken.clear(); + ppToken.space = space; + ppToken.i64val = i64val; + snprintf(ppToken.name, sizeof(ppToken.name), "%s", name.c_str()); + return atom; + } + bool isAtom(int a) const { return atom == a; } + int getAtom() const { return atom; } + bool nonSpaced() const { return !space; } + protected: + Token() {} + int atom; + bool space; // did a space precede the token? + long long i64val; + TString name; + }; + + TokenStream() : currentPos(0) { } + + void putToken(int token, TPpToken* ppToken); + bool peekToken(int atom) { return !atEnd() && stream[currentPos].isAtom(atom); } + bool peekContinuedPasting(int atom) + { + // This is basically necessary because, for example, the PP + // tokenizer only accepts valid numeric-literals plus suffixes, so + // separates numeric-literals plus bad suffix into two tokens, which + // should get both pasted together as one token when token pasting. + // + // The following code is a bit more generalized than the above example. + if (!atEnd() && atom == PpAtomIdentifier && stream[currentPos].nonSpaced()) { + switch(stream[currentPos].getAtom()) { + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstFloat: + case PpAtomConstDouble: + case PpAtomConstFloat16: + case PpAtomConstString: + case PpAtomIdentifier: + return true; + default: + break; + } + } + + return false; + } + int getToken(TParseContextBase&, TPpToken*); + bool atEnd() { return currentPos >= stream.size(); } + bool peekTokenizedPasting(bool lastTokenPastes); + bool peekUntokenizedPasting(); + void reset() { currentPos = 0; } + + protected: + TVector stream; + size_t currentPos; + }; + + // + // From Pp.cpp + // + + struct MacroSymbol { + MacroSymbol() : functionLike(0), busy(0), undef(0) { } + TVector args; + TokenStream body; + unsigned functionLike : 1; // 0 means object-like, 1 means function-like + unsigned busy : 1; + unsigned undef : 1; + }; + + typedef TMap TSymbolMap; + TSymbolMap macroDefs; // map atoms to macro definitions + MacroSymbol* lookupMacroDef(int atom) + { + auto existingMacroIt = macroDefs.find(atom); + return (existingMacroIt == macroDefs.end()) ? nullptr : &(existingMacroIt->second); + } + void addMacroDef(int atom, MacroSymbol& macroDef) { macroDefs[atom] = macroDef; } + +protected: + TPpContext(TPpContext&); + TPpContext& operator=(TPpContext&); + + TStringAtomMap atomStrings; + char* preamble; // string to parse, all before line 1 of string 0, it is 0 if no preamble + int preambleLength; + char** strings; // official strings of shader, starting a string 0 line 1 + size_t* lengths; + int numStrings; // how many official strings there are + int currentString; // which string we're currently parsing (-1 for preamble) + + // Scanner data: + int previous_token; + TParseContextBase& parseContext; + + // Get the next token from *stack* of input sources, popping input sources + // that are out of tokens, down until an input source is found that has a token. + // Return EndOfInput when there are no more tokens to be found by doing this. + int scanToken(TPpToken* ppToken) + { + int token = EndOfInput; + + while (! inputStack.empty()) { + token = inputStack.back()->scan(ppToken); + if (token != EndOfInput || inputStack.empty()) + break; + popInput(); + } + + return token; + } + int getChar() { return inputStack.back()->getch(); } + void ungetChar() { inputStack.back()->ungetch(); } + bool peekPasting() { return !inputStack.empty() && inputStack.back()->peekPasting(); } + bool peekContinuedPasting(int a) + { + return !inputStack.empty() && inputStack.back()->peekContinuedPasting(a); + } + bool endOfReplacementList() { return inputStack.empty() || inputStack.back()->endOfReplacementList(); } + bool isMacroInput() { return inputStack.size() > 0 && inputStack.back()->isMacroInput(); } + + static const int maxIfNesting = 65; + + int ifdepth; // current #if-#else-#endif nesting in the cpp.c file (pre-processor) + bool elseSeen[maxIfNesting]; // Keep a track of whether an else has been seen at a particular depth + int elsetracker; // #if-#else and #endif constructs...Counter. + + class tMacroInput : public tInput { + public: + tMacroInput(TPpContext* pp) : tInput(pp), prepaste(false), postpaste(false) { } + virtual ~tMacroInput() + { + for (size_t i = 0; i < args.size(); ++i) + delete args[i]; + for (size_t i = 0; i < expandedArgs.size(); ++i) + delete expandedArgs[i]; + } + + virtual int scan(TPpToken*) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + bool peekPasting() override { return prepaste; } + bool peekContinuedPasting(int a) override { return mac->body.peekContinuedPasting(a); } + bool endOfReplacementList() override { return mac->body.atEnd(); } + bool isMacroInput() override { return true; } + + MacroSymbol *mac; + TVector args; + TVector expandedArgs; + + protected: + bool prepaste; // true if we are just before ## + bool postpaste; // true if we are right after ## + }; + + class tMarkerInput : public tInput { + public: + tMarkerInput(TPpContext* pp) : tInput(pp) { } + virtual int scan(TPpToken*) override + { + if (done) + return EndOfInput; + done = true; + + return marker; + } + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + static const int marker = -3; + }; + + class tZeroInput : public tInput { + public: + tZeroInput(TPpContext* pp) : tInput(pp) { } + virtual int scan(TPpToken*) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + }; + + std::vector inputStack; + bool errorOnVersion; + bool versionSeen; + + // + // from Pp.cpp + // + + // Used to obtain #include content. + TShader::Includer& includer; + + int CPPdefine(TPpToken * ppToken); + int CPPundef(TPpToken * ppToken); + int CPPelse(int matchelse, TPpToken * ppToken); + int extraTokenCheck(int atom, TPpToken* ppToken, int token); + int eval(int token, int precedence, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); + int evalToToken(int token, bool shortCircuit, int& res, bool& err, TPpToken * ppToken); + int CPPif (TPpToken * ppToken); + int CPPifdef(int defined, TPpToken * ppToken); + int CPPinclude(TPpToken * ppToken); + int CPPline(TPpToken * ppToken); + int CPPerror(TPpToken * ppToken); + int CPPpragma(TPpToken * ppToken); + int CPPversion(TPpToken * ppToken); + int CPPextension(TPpToken * ppToken); + int readCPPline(TPpToken * ppToken); + int scanHeaderName(TPpToken* ppToken, char delimit); + TokenStream* PrescanMacroArg(TokenStream&, TPpToken*, bool newLineOkay); + MacroExpandResult MacroExpand(TPpToken* ppToken, bool expandUndef, bool newLineOkay); + + // + // From PpTokens.cpp + // + void pushTokenStreamInput(TokenStream&, bool pasting = false); + void UngetToken(int token, TPpToken*); + + class tTokenInput : public tInput { + public: + tTokenInput(TPpContext* pp, TokenStream* t, bool prepasting) : + tInput(pp), + tokens(t), + lastTokenPastes(prepasting) { } + virtual int scan(TPpToken *ppToken) override { return tokens->getToken(pp->parseContext, ppToken); } + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + virtual bool peekPasting() override { return tokens->peekTokenizedPasting(lastTokenPastes); } + bool peekContinuedPasting(int a) override { return tokens->peekContinuedPasting(a); } + protected: + TokenStream* tokens; + bool lastTokenPastes; // true if the last token in the input is to be pasted, rather than consumed as a token + }; + + class tUngotTokenInput : public tInput { + public: + tUngotTokenInput(TPpContext* pp, int t, TPpToken* p) : tInput(pp), token(t), lval(*p) { } + virtual int scan(TPpToken *) override; + virtual int getch() override { assert(0); return EndOfInput; } + virtual void ungetch() override { assert(0); } + protected: + int token; + TPpToken lval; + }; + + // + // From PpScanner.cpp + // + class tStringInput : public tInput { + public: + tStringInput(TPpContext* pp, TInputScanner& i) : tInput(pp), input(&i) { } + virtual int scan(TPpToken*) override; + + // Scanner used to get source stream characters. + // - Escaped newlines are handled here, invisibly to the caller. + // - All forms of newline are handled, and turned into just a '\n'. + int getch() override + { + int ch = input->get(); + + if (ch == '\\') { + // Move past escaped newlines, as many as sequentially exist + do { + if (input->peek() == '\r' || input->peek() == '\n') { + bool allowed = pp->parseContext.lineContinuationCheck(input->getSourceLoc(), pp->inComment); + if (! allowed && pp->inComment) + return '\\'; + + // escape one newline now + ch = input->get(); + int nextch = input->get(); + if (ch == '\r' && nextch == '\n') + ch = input->get(); + else + ch = nextch; + } else + return '\\'; + } while (ch == '\\'); + } + + // handle any non-escaped newline + if (ch == '\r' || ch == '\n') { + if (ch == '\r' && input->peek() == '\n') + input->get(); + return '\n'; + } + + return ch; + } + + // Scanner used to backup the source stream characters. Newlines are + // handled here, invisibly to the caller, meaning have to undo exactly + // what getch() above does (e.g., don't leave things in the middle of a + // sequence of escaped newlines). + void ungetch() override + { + input->unget(); + + do { + int ch = input->peek(); + if (ch == '\r' || ch == '\n') { + if (ch == '\n') { + // correct for two-character newline + input->unget(); + if (input->peek() != '\r') + input->get(); + } + // now in front of a complete newline, move past an escape character + input->unget(); + if (input->peek() == '\\') + input->unget(); + else { + input->get(); + break; + } + } else + break; + } while (true); + } + + protected: + TInputScanner* input; + }; + + // Holds a reference to included file data, as well as a + // prologue and an epilogue string. This can be scanned using the tInput + // interface and acts as a single source string. + class TokenizableIncludeFile : public tInput { + public: + // Copies prologue and epilogue. The includedFile must remain valid + // until this TokenizableIncludeFile is no longer used. + TokenizableIncludeFile(const TSourceLoc& startLoc, + const std::string& prologue, + TShader::Includer::IncludeResult* includedFile, + const std::string& epilogue, + TPpContext* pp) + : tInput(pp), + prologue_(prologue), + epilogue_(epilogue), + includedFile_(includedFile), + scanner(3, strings, lengths, nullptr, 0, 0, true), + prevScanner(nullptr), + stringInput(pp, scanner) + { + strings[0] = prologue_.data(); + strings[1] = includedFile_->headerData; + strings[2] = epilogue_.data(); + + lengths[0] = prologue_.size(); + lengths[1] = includedFile_->headerLength; + lengths[2] = epilogue_.size(); + + scanner.setLine(startLoc.line); + scanner.setString(startLoc.string); + + scanner.setFile(startLoc.getFilenameStr(), 0); + scanner.setFile(startLoc.getFilenameStr(), 1); + scanner.setFile(startLoc.getFilenameStr(), 2); + } + + // tInput methods: + int scan(TPpToken* t) override { return stringInput.scan(t); } + int getch() override { return stringInput.getch(); } + void ungetch() override { stringInput.ungetch(); } + + void notifyActivated() override + { + prevScanner = pp->parseContext.getScanner(); + pp->parseContext.setScanner(&scanner); + pp->push_include(includedFile_); + } + + void notifyDeleted() override + { + pp->parseContext.setScanner(prevScanner); + pp->pop_include(); + } + + private: + TokenizableIncludeFile& operator=(const TokenizableIncludeFile&); + + // Stores the prologue for this string. + const std::string prologue_; + + // Stores the epilogue for this string. + const std::string epilogue_; + + // Points to the IncludeResult that this TokenizableIncludeFile represents. + TShader::Includer::IncludeResult* includedFile_; + + // Will point to prologue_, includedFile_->headerData and epilogue_ + // This is passed to scanner constructor. + // These do not own the storage and it must remain valid until this + // object has been destroyed. + const char* strings[3]; + // Length of str_, passed to scanner constructor. + size_t lengths[3]; + // Scans over str_. + TInputScanner scanner; + // The previous effective scanner before the scanner in this instance + // has been activated. + TInputScanner* prevScanner; + // Delegate object implementing the tInput interface. + tStringInput stringInput; + }; + + int ScanFromString(char* s); + void missingEndifCheck(); + int lFloatConst(int len, int ch, TPpToken* ppToken); + int characterLiteral(TPpToken* ppToken); + + void push_include(TShader::Includer::IncludeResult* result) + { + currentSourceFile = result->headerName; + includeStack.push(result); + } + + void pop_include() + { + TShader::Includer::IncludeResult* include = includeStack.top(); + includeStack.pop(); + includer.releaseInclude(include); + if (includeStack.empty()) { + currentSourceFile = rootFileName; + } else { + currentSourceFile = includeStack.top()->headerName; + } + } + + bool inComment; + std::string rootFileName; + std::stack includeStack; + std::string currentSourceFile; + + std::istringstream strtodStream; + bool disableEscapeSequences; +}; + +} // end namespace glslang + +#endif // PPCONTEXT_H diff --git a/third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp new file mode 100644 index 0000000..e0f44f8 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpScanner.cpp @@ -0,0 +1,1315 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" +#include "../Scan.h" + +namespace glslang { + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Floating point constants: ///////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +// +// Scan a single- or double-precision floating point constant. +// Assumes that the scanner has seen at least one digit, +// followed by either a decimal '.' or the letter 'e', or a +// precision ending (e.g., F or LF). +// +// This is technically not correct, as the preprocessor should just +// accept the numeric literal along with whatever suffix it has, but +// currently, it stops on seeing a bad suffix, treating that as the +// next token. This effects things like token pasting, where it is +// relevant how many tokens something was broken into. +// +// See peekContinuedPasting(). +int TPpContext::lFloatConst(int len, int ch, TPpToken* ppToken) +{ + const auto saveName = [&](int ch) { + if (len <= MaxTokenLength) + ppToken->name[len++] = static_cast(ch); + }; + + // find the range of non-zero digits before the decimal point + int startNonZero = 0; + while (startNonZero < len && ppToken->name[startNonZero] == '0') + ++startNonZero; + int endNonZero = len; + while (endNonZero > startNonZero && ppToken->name[endNonZero-1] == '0') + --endNonZero; + int numWholeNumberDigits = endNonZero - startNonZero; + + // accumulate the range's value + bool fastPath = numWholeNumberDigits <= 15; // when the number gets too complex, set to false + unsigned long long wholeNumber = 0; + if (fastPath) { + for (int i = startNonZero; i < endNonZero; ++i) + wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0'); + } + int decimalShift = len - endNonZero; + + // Decimal point: + bool hasDecimalOrExponent = false; + if (ch == '.') { + hasDecimalOrExponent = true; + saveName(ch); + ch = getChar(); + int firstDecimal = len; + +#ifdef ENABLE_HLSL + // 1.#INF or -1.#INF + if (ch == '#' && (ifdepth > 0 || parseContext.intermediate.getSource() == EShSourceHlsl)) { + if ((len < 2) || + (len == 2 && ppToken->name[0] != '1') || + (len == 3 && ppToken->name[1] != '1' && !(ppToken->name[0] == '-' || ppToken->name[0] == '+')) || + (len > 3)) + parseContext.ppError(ppToken->loc, "unexpected use of", "#", ""); + else { + // we have 1.# or -1.# or +1.#, check for 'INF' + if ((ch = getChar()) != 'I' || + (ch = getChar()) != 'N' || + (ch = getChar()) != 'F') + parseContext.ppError(ppToken->loc, "expected 'INF'", "#", ""); + else { + // we have [+-].#INF, and we are targeting IEEE 754, so wrap it up: + saveName('I'); + saveName('N'); + saveName('F'); + ppToken->name[len] = '\0'; + if (ppToken->name[0] == '-') + ppToken->i64val = 0xfff0000000000000; // -Infinity + else + ppToken->i64val = 0x7ff0000000000000; // +Infinity + return PpAtomConstFloat; + } + } + } +#endif + + // Consume leading-zero digits after the decimal point + while (ch == '0') { + saveName(ch); + ch = getChar(); + } + int startNonZeroDecimal = len; + int endNonZeroDecimal = len; + + // Consume remaining digits, up to the exponent + while (ch >= '0' && ch <= '9') { + saveName(ch); + if (ch != '0') + endNonZeroDecimal = len; + ch = getChar(); + } + + // Compute accumulation up to the last non-zero digit + if (endNonZeroDecimal > startNonZeroDecimal) { + numWholeNumberDigits += endNonZeroDecimal - endNonZero - 1; // don't include the "." + if (numWholeNumberDigits > 15) + fastPath = false; + if (fastPath) { + for (int i = endNonZero; i < endNonZeroDecimal; ++i) { + if (ppToken->name[i] != '.') + wholeNumber = wholeNumber * 10 + (ppToken->name[i] - '0'); + } + } + decimalShift = firstDecimal - endNonZeroDecimal; + } + } + + // Exponent: + bool negativeExponent = false; + double exponentValue = 0.0; + int exponent = 0; + { + if (ch == 'e' || ch == 'E') { + hasDecimalOrExponent = true; + saveName(ch); + ch = getChar(); + if (ch == '+' || ch == '-') { + negativeExponent = ch == '-'; + saveName(ch); + ch = getChar(); + } + if (ch >= '0' && ch <= '9') { + while (ch >= '0' && ch <= '9') { + exponent = exponent * 10 + (ch - '0'); + saveName(ch); + ch = getChar(); + } + } else { + parseContext.ppError(ppToken->loc, "bad character in float exponent", "", ""); + } + } + + // Compensate for location of decimal + if (negativeExponent) + exponent -= decimalShift; + else { + exponent += decimalShift; + if (exponent < 0) { + negativeExponent = true; + exponent = -exponent; + } + } + if (exponent > 22) + fastPath = false; + + if (fastPath) { + // Compute the floating-point value of the exponent + exponentValue = 1.0; + if (exponent > 0) { + double expFactor = 10; + while (exponent > 0) { + if (exponent & 0x1) + exponentValue *= expFactor; + expFactor *= expFactor; + exponent >>= 1; + } + } + } + } + + // Suffix: + bool isDouble = false; + bool isFloat16 = false; +#ifndef GLSLANG_WEB + if (ch == 'l' || ch == 'L') { + if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl) + parseContext.doubleCheck(ppToken->loc, "double floating-point suffix"); + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + if (parseContext.intermediate.getSource() == EShSourceGlsl) { + int ch2 = getChar(); + if (ch2 != 'f' && ch2 != 'F') { + ungetChar(); + ungetChar(); + } else { + saveName(ch); + saveName(ch2); + isDouble = true; + } + } else if (parseContext.intermediate.getSource() == EShSourceHlsl) { + saveName(ch); + isDouble = true; + } + } else if (ch == 'h' || ch == 'H') { + if (ifdepth == 0 && parseContext.intermediate.getSource() == EShSourceGlsl) + parseContext.float16Check(ppToken->loc, "half floating-point suffix"); + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + if (parseContext.intermediate.getSource() == EShSourceGlsl) { + int ch2 = getChar(); + if (ch2 != 'f' && ch2 != 'F') { + ungetChar(); + ungetChar(); + } else { + saveName(ch); + saveName(ch2); + isFloat16 = true; + } + } else if (parseContext.intermediate.getSource() == EShSourceHlsl) { + saveName(ch); + isFloat16 = true; + } + } else +#endif + if (ch == 'f' || ch == 'F') { +#ifndef GLSLANG_WEB + if (ifdepth == 0) + parseContext.profileRequires(ppToken->loc, EEsProfile, 300, nullptr, "floating-point suffix"); + if (ifdepth == 0 && !parseContext.relaxedErrors()) + parseContext.profileRequires(ppToken->loc, ~EEsProfile, 120, nullptr, "floating-point suffix"); +#endif + if (ifdepth == 0 && !hasDecimalOrExponent) + parseContext.ppError(ppToken->loc, "float literal needs a decimal point or exponent", "", ""); + saveName(ch); + } else + ungetChar(); + + // Patch up the name and length for overflow + + if (len > MaxTokenLength) { + len = MaxTokenLength; + parseContext.ppError(ppToken->loc, "float literal too long", "", ""); + } + ppToken->name[len] = '\0'; + + // Compute the numerical value + if (fastPath) { + // compute the floating-point value of the exponent + if (exponentValue == 0.0) + ppToken->dval = (double)wholeNumber; + else if (negativeExponent) + ppToken->dval = (double)wholeNumber / exponentValue; + else + ppToken->dval = (double)wholeNumber * exponentValue; + } else { + // slow path + ppToken->dval = 0.0; + + // remove suffix + TString numstr(ppToken->name); + if (numstr.back() == 'f' || numstr.back() == 'F') + numstr.pop_back(); + if (numstr.back() == 'h' || numstr.back() == 'H') + numstr.pop_back(); + if (numstr.back() == 'l' || numstr.back() == 'L') + numstr.pop_back(); + + // use platform library + strtodStream.clear(); + strtodStream.str(numstr.c_str()); + strtodStream >> ppToken->dval; + if (strtodStream.fail()) { + // Assume failure combined with a large exponent was overflow, in + // an attempt to set INF. + if (!negativeExponent && exponent + numWholeNumberDigits > 300) + ppToken->i64val = 0x7ff0000000000000; // +Infinity + // Assume failure combined with a small exponent was overflow. + if (negativeExponent && exponent + numWholeNumberDigits > 300) + ppToken->dval = 0.0; + // Unknown reason for failure. Theory is that either + // - the 0.0 is still there, or + // - something reasonable was written that is better than 0.0 + } + } + + // Return the right token type + if (isDouble) + return PpAtomConstDouble; + else if (isFloat16) + return PpAtomConstFloat16; + else + return PpAtomConstFloat; +} + +// Recognize a character literal. +// +// The first ' has already been accepted, read the rest, through the closing '. +// +// Always returns PpAtomConstInt. +// +int TPpContext::characterLiteral(TPpToken* ppToken) +{ + ppToken->name[0] = 0; + ppToken->ival = 0; + + if (parseContext.intermediate.getSource() != EShSourceHlsl) { + // illegal, except in macro definition, for which case we report the character + return '\''; + } + + int ch = getChar(); + switch (ch) { + case '\'': + // As empty sequence: '' + parseContext.ppError(ppToken->loc, "unexpected", "\'", ""); + return PpAtomConstInt; + case '\\': + // As escape sequence: '\XXX' + switch (ch = getChar()) { + case 'a': + ppToken->ival = 7; + break; + case 'b': + ppToken->ival = 8; + break; + case 't': + ppToken->ival = 9; + break; + case 'n': + ppToken->ival = 10; + break; + case 'v': + ppToken->ival = 11; + break; + case 'f': + ppToken->ival = 12; + break; + case 'r': + ppToken->ival = 13; + break; + case 'x': + case '0': + parseContext.ppError(ppToken->loc, "octal and hex sequences not supported", "\\", ""); + break; + default: + // This catches '\'', '\"', '\?', etc. + // Also, things like '\C' mean the same thing as 'C' + // (after the above cases are filtered out). + ppToken->ival = ch; + break; + } + break; + default: + ppToken->ival = ch; + break; + } + ppToken->name[0] = (char)ppToken->ival; + ppToken->name[1] = '\0'; + ch = getChar(); + if (ch != '\'') { + parseContext.ppError(ppToken->loc, "expected", "\'", ""); + // Look ahead for a closing ' + do { + ch = getChar(); + } while (ch != '\'' && ch != EndOfInput && ch != '\n'); + } + + return PpAtomConstInt; +} + +// +// Scanner used to tokenize source stream. +// +// N.B. Invalid numeric suffixes are not consumed.// +// This is technically not correct, as the preprocessor should just +// accept the numeric literal along with whatever suffix it has, but +// currently, it stops on seeing a bad suffix, treating that as the +// next token. This effects things like token pasting, where it is +// relevant how many tokens something was broken into. +// See peekContinuedPasting(). +// +int TPpContext::tStringInput::scan(TPpToken* ppToken) +{ + int AlreadyComplained = 0; + int len = 0; + int ch = 0; + int ii = 0; + unsigned long long ival = 0; + const auto floatingPointChar = [&](int ch) { return ch == '.' || ch == 'e' || ch == 'E' || + ch == 'f' || ch == 'F' || + ch == 'h' || ch == 'H'; }; + + static const char* const Int64_Extensions[] = { + E_GL_ARB_gpu_shader_int64, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int64 }; + static const int Num_Int64_Extensions = sizeof(Int64_Extensions) / sizeof(Int64_Extensions[0]); + + static const char* const Int16_Extensions[] = { + E_GL_AMD_gpu_shader_int16, + E_GL_EXT_shader_explicit_arithmetic_types, + E_GL_EXT_shader_explicit_arithmetic_types_int16 }; + static const int Num_Int16_Extensions = sizeof(Int16_Extensions) / sizeof(Int16_Extensions[0]); + + ppToken->ival = 0; + ppToken->i64val = 0; + ppToken->space = false; + ch = getch(); + for (;;) { + while (ch == ' ' || ch == '\t') { + ppToken->space = true; + ch = getch(); + } + + ppToken->loc = pp->parseContext.getCurrentLoc(); + len = 0; + switch (ch) { + default: + // Single character token, including EndOfInput, '#' and '\' (escaped newlines are handled at a lower level, so this is just a '\' token) + if (ch > PpAtomMaxSingle) + ch = PpAtomBadToken; + return ch; + + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': case '_': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + do { + if (len < MaxTokenLength) { + ppToken->name[len++] = (char)ch; + ch = getch(); + } else { + if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "name too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } + } while ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + ch == '_'); + + // line continuation with no token before or after makes len == 0, and need to start over skipping white space, etc. + if (len == 0) + continue; + + ppToken->name[len] = '\0'; + ungetch(); + return PpAtomIdentifier; + case '0': + ppToken->name[len++] = (char)ch; + ch = getch(); + if (ch == 'x' || ch == 'X') { + // must be hexadecimal + + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + ppToken->name[len++] = (char)ch; + ch = getch(); + if ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')) { + + ival = 0; + do { + if (len < MaxTokenLength && ival <= 0x0fffffffffffffffull) { + ppToken->name[len++] = (char)ch; + if (ch >= '0' && ch <= '9') { + ii = ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + ii = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + ii = ch - 'a' + 10; + } else + pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", ""); + ival = (ival << 4) | ii; + } else { + if (! AlreadyComplained) { + if(len < MaxTokenLength) + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", ""); + else + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too long", "", ""); + AlreadyComplained = 1; + } + ival = 0xffffffffffffffffull; + } + ch = getch(); + } while ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || + (ch >= 'a' && ch <= 'f')); + } else { + pp->parseContext.ppError(ppToken->loc, "bad digit in hexadecimal literal", "", ""); + } + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + +#ifndef GLSLANG_WEB + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } else + ungetch(); + ppToken->name[len] = '\0'; + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit hexadecimal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit hexadecimal literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0) { + if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit hexadecimal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit hexadecimal literal"); + } + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + if (ival > 0xffffffffu && !AlreadyComplained) + pp->parseContext.ppError(ppToken->loc, "hexadecimal literal too big", "", ""); + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } else { + // could be octal integer or floating point, speculative pursue octal until it must be floating point + + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + bool octalOverflow = false; + bool nonOctal = false; + ival = 0; + + // see how much octal-like stuff we can read + while (ch >= '0' && ch <= '7') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + if (ival <= 0x1fffffffffffffffull) { + ii = ch - '0'; + ival = (ival << 3) | ii; + } else + octalOverflow = true; + ch = getch(); + } + + // could be part of a float... + if (ch == '8' || ch == '9') { + nonOctal = true; + do { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } while (ch >= '0' && ch <= '9'); + } + if (floatingPointChar(ch)) + return pp->lFloatConst(len, ch, ppToken); + + // wasn't a float, so must be octal... + if (nonOctal) + pp->parseContext.ppError(ppToken->loc, "octal literal digit too large", "", ""); + + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + +#ifndef GLSLANG_WEB + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } else + ungetch(); + ppToken->name[len] = '\0'; + + if (!isInt64 && ival > 0xffffffffu) + octalOverflow = true; + + if (octalOverflow) + pp->parseContext.ppError(ppToken->loc, "octal literal too big", "", ""); + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit octal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit octal literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0) { + if (pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit octal literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit octal literal"); + } + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } + break; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + // can't be hexadecimal or octal, is either decimal or floating point + + do { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + else if (! AlreadyComplained) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too long", "", ""); + AlreadyComplained = 1; + } + ch = getch(); + } while (ch >= '0' && ch <= '9'); + if (floatingPointChar(ch)) + return pp->lFloatConst(len, ch, ppToken); + else { + // Finish handling signed and unsigned integers + int numericLen = len; + bool isUnsigned = false; + bool isInt64 = false; + bool isInt16 = false; + if (ch == 'u' || ch == 'U') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isUnsigned = true; + +#ifndef GLSLANG_WEB + int nextCh = getch(); + if (nextCh == 'l' || nextCh == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt64 = true; + } else + ungetch(); + + nextCh = getch(); + if ((nextCh == 's' || nextCh == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)nextCh; + isInt16 = true; + } else + ungetch(); + } else if (ch == 'l' || ch == 'L') { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt64 = true; + } else if ((ch == 's' || ch == 'S') && + pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (len < MaxTokenLength) + ppToken->name[len++] = (char)ch; + isInt16 = true; +#endif + } else + ungetch(); + + ppToken->name[len] = '\0'; + ival = 0; + const unsigned oneTenthMaxInt = 0xFFFFFFFFu / 10; + const unsigned remainderMaxInt = 0xFFFFFFFFu - 10 * oneTenthMaxInt; + const unsigned long long oneTenthMaxInt64 = 0xFFFFFFFFFFFFFFFFull / 10; + const unsigned long long remainderMaxInt64 = 0xFFFFFFFFFFFFFFFFull - 10 * oneTenthMaxInt64; + const unsigned short oneTenthMaxInt16 = 0xFFFFu / 10; + const unsigned short remainderMaxInt16 = 0xFFFFu - 10 * oneTenthMaxInt16; + for (int i = 0; i < numericLen; i++) { + ch = ppToken->name[i] - '0'; + bool overflow = false; + if (isInt64) + overflow = (ival > oneTenthMaxInt64 || (ival == oneTenthMaxInt64 && (unsigned long long)ch > remainderMaxInt64)); + else if (isInt16) + overflow = (ival > oneTenthMaxInt16 || (ival == oneTenthMaxInt16 && (unsigned short)ch > remainderMaxInt16)); + else + overflow = (ival > oneTenthMaxInt || (ival == oneTenthMaxInt && (unsigned)ch > remainderMaxInt)); + if (overflow) { + pp->parseContext.ppError(ppToken->loc, "numeric literal too big", "", ""); + ival = 0xFFFFFFFFFFFFFFFFull; + break; + } else + ival = ival * 10 + ch; + } + + if (isInt64 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + if (pp->ifdepth == 0) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "64-bit literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int64_Extensions, Int64_Extensions, "64-bit literal"); + } + ppToken->i64val = ival; + return isUnsigned ? PpAtomConstUint64 : PpAtomConstInt64; + } else if (isInt16) { + if (pp->ifdepth == 0 && pp->parseContext.intermediate.getSource() == EShSourceGlsl) { + pp->parseContext.requireProfile(ppToken->loc, ~EEsProfile, + "16-bit literal"); + pp->parseContext.profileRequires(ppToken->loc, ~EEsProfile, 0, + Num_Int16_Extensions, Int16_Extensions, "16-bit literal"); + } + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint16 : PpAtomConstInt16; + } else { + ppToken->ival = (int)ival; + return isUnsigned ? PpAtomConstUint : PpAtomConstInt; + } + } + break; + case '-': + ch = getch(); + if (ch == '-') { + return PpAtomDecrement; + } else if (ch == '=') { + return PPAtomSubAssign; + } else { + ungetch(); + return '-'; + } + case '+': + ch = getch(); + if (ch == '+') { + return PpAtomIncrement; + } else if (ch == '=') { + return PPAtomAddAssign; + } else { + ungetch(); + return '+'; + } + case '*': + ch = getch(); + if (ch == '=') { + return PPAtomMulAssign; + } else { + ungetch(); + return '*'; + } + case '%': + ch = getch(); + if (ch == '=') { + return PPAtomModAssign; + } else { + ungetch(); + return '%'; + } + case '^': + ch = getch(); + if (ch == '^') { + return PpAtomXor; + } else { + if (ch == '=') + return PpAtomXorAssign; + else{ + ungetch(); + return '^'; + } + } + + case '=': + ch = getch(); + if (ch == '=') { + return PpAtomEQ; + } else { + ungetch(); + return '='; + } + case '!': + ch = getch(); + if (ch == '=') { + return PpAtomNE; + } else { + ungetch(); + return '!'; + } + case '|': + ch = getch(); + if (ch == '|') { + return PpAtomOr; + } else if (ch == '=') { + return PpAtomOrAssign; + } else { + ungetch(); + return '|'; + } + case '&': + ch = getch(); + if (ch == '&') { + return PpAtomAnd; + } else if (ch == '=') { + return PpAtomAndAssign; + } else { + ungetch(); + return '&'; + } + case '<': + ch = getch(); + if (ch == '<') { + ch = getch(); + if (ch == '=') + return PpAtomLeftAssign; + else { + ungetch(); + return PpAtomLeft; + } + } else if (ch == '=') { + return PpAtomLE; + } else { + ungetch(); + return '<'; + } + case '>': + ch = getch(); + if (ch == '>') { + ch = getch(); + if (ch == '=') + return PpAtomRightAssign; + else { + ungetch(); + return PpAtomRight; + } + } else if (ch == '=') { + return PpAtomGE; + } else { + ungetch(); + return '>'; + } + case '.': + ch = getch(); + if (ch >= '0' && ch <= '9') { + ungetch(); + return pp->lFloatConst(0, '.', ppToken); + } else { + ungetch(); + return '.'; + } + case '/': + ch = getch(); + if (ch == '/') { + pp->inComment = true; + do { + ch = getch(); + } while (ch != '\n' && ch != EndOfInput); + ppToken->space = true; + pp->inComment = false; + + return ch; + } else if (ch == '*') { + ch = getch(); + do { + while (ch != '*') { + if (ch == EndOfInput) { + pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", ""); + return ch; + } + ch = getch(); + } + ch = getch(); + if (ch == EndOfInput) { + pp->parseContext.ppError(ppToken->loc, "End of input in comment", "comment", ""); + return ch; + } + } while (ch != '/'); + ppToken->space = true; + // loop again to get the next token... + break; + } else if (ch == '=') { + return PPAtomDivAssign; + } else { + ungetch(); + return '/'; + } + break; + case '\'': + return pp->characterLiteral(ppToken); + case '"': + // #include uses scanHeaderName() to ignore these escape sequences. + ch = getch(); + while (ch != '"' && ch != '\n' && ch != EndOfInput) { + if (len < MaxTokenLength) { + if (ch == '\\' && !pp->disableEscapeSequences) { + int nextCh = getch(); + switch (nextCh) { + case '\'': ch = 0x27; break; + case '"': ch = 0x22; break; + case '?': ch = 0x3f; break; + case '\\': ch = 0x5c; break; + case 'a': ch = 0x07; break; + case 'b': ch = 0x08; break; + case 'f': ch = 0x0c; break; + case 'n': ch = 0x0a; break; + case 'r': ch = 0x0d; break; + case 't': ch = 0x09; break; + case 'v': ch = 0x0b; break; + case 'x': + // Hex value, arbitrary number of characters. Terminated by the first + // non-hex digit + { + int numDigits = 0; + ch = 0; + while (true) { + nextCh = getch(); + if (nextCh >= '0' && nextCh <= '9') + nextCh -= '0'; + else if (nextCh >= 'A' && nextCh <= 'F') + nextCh -= 'A' - 10; + else if (nextCh >= 'a' && nextCh <= 'f') + nextCh -= 'a' - 10; + else { + ungetch(); + break; + } + numDigits++; + ch = ch * 0x10 + nextCh; + } + if (numDigits == 0) { + pp->parseContext.ppError(ppToken->loc, "Expected hex value in escape sequence", "string", ""); + } + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + // Octal value, up to three octal digits + { + int numDigits = 1; + ch = nextCh - '0'; + while (numDigits < 3) { + nextCh = getch(); + if (nextCh >= '0' && nextCh <= '7') + nextCh -= '0'; + else { + ungetch(); + break; + } + numDigits++; + ch = ch * 8 + nextCh; + } + break; + } + default: + pp->parseContext.ppError(ppToken->loc, "Invalid escape sequence", "string", ""); + break; + } + } + ppToken->name[len] = (char)ch; + len++; + ch = getch(); + } else + break; + }; + ppToken->name[len] = '\0'; + if (ch != '"') { + ungetch(); + pp->parseContext.ppError(ppToken->loc, "End of line in string", "string", ""); + } + return PpAtomConstString; + case ':': + ch = getch(); + if (ch == ':') + return PpAtomColonColon; + ungetch(); + return ':'; + } + + ch = getch(); + } +} + +// +// The main functional entry point into the preprocessor, which will +// scan the source strings to figure out and return the next processing token. +// +// Return the token, or EndOfInput when no more tokens. +// +int TPpContext::tokenize(TPpToken& ppToken) +{ + for(;;) { + int token = scanToken(&ppToken); + + // Handle token-pasting logic + token = tokenPaste(token, ppToken); + + if (token == EndOfInput) { + missingEndifCheck(); + return EndOfInput; + } + if (token == '#') { + if (previous_token == '\n') { + token = readCPPline(&ppToken); + if (token == EndOfInput) { + missingEndifCheck(); + return EndOfInput; + } + continue; + } else { + parseContext.ppError(ppToken.loc, "preprocessor directive cannot be preceded by another token", "#", ""); + return EndOfInput; + } + } + previous_token = token; + + if (token == '\n') + continue; + + // expand macros + if (token == PpAtomIdentifier) { + switch (MacroExpand(&ppToken, false, true)) { + case MacroExpandNotStarted: + break; + case MacroExpandError: + return EndOfInput; + case MacroExpandStarted: + case MacroExpandUndef: + continue; + } + } + + switch (token) { + case PpAtomIdentifier: + case PpAtomConstInt: + case PpAtomConstUint: + case PpAtomConstFloat: + case PpAtomConstInt64: + case PpAtomConstUint64: + case PpAtomConstInt16: + case PpAtomConstUint16: + case PpAtomConstDouble: + case PpAtomConstFloat16: + if (ppToken.name[0] == '\0') + continue; + break; + case PpAtomConstString: + // HLSL allows string literals. + // GLSL allows string literals with GL_EXT_debug_printf. + if (ifdepth == 0 && parseContext.intermediate.getSource() != EShSourceHlsl) { + parseContext.requireExtensions(ppToken.loc, 1, &E_GL_EXT_debug_printf, "string literal"); + if (!parseContext.extensionTurnedOn(E_GL_EXT_debug_printf)) + continue; + } + break; + case '\'': + parseContext.ppError(ppToken.loc, "character literals not supported", "\'", ""); + continue; + default: + snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(token)); + break; + } + + return token; + } +} + +// +// Do all token-pasting related combining of two pasted tokens when getting a +// stream of tokens from a replacement list. Degenerates to no processing if a +// replacement list is not the source of the token stream. +// +int TPpContext::tokenPaste(int token, TPpToken& ppToken) +{ + // starting with ## is illegal, skip to next token + if (token == PpAtomPaste) { + parseContext.ppError(ppToken.loc, "unexpected location", "##", ""); + return scanToken(&ppToken); + } + + int resultToken = token; // "foo" pasted with "35" is an identifier, not a number + + // ## can be chained, process all in the chain at once + while (peekPasting()) { + TPpToken pastedPpToken; + + // next token has to be ## + token = scanToken(&pastedPpToken); + assert(token == PpAtomPaste); + + // This covers end of macro expansion + if (endOfReplacementList()) { + parseContext.ppError(ppToken.loc, "unexpected location; end of replacement list", "##", ""); + break; + } + + // Get the token(s) after the ##. + // Because of "space" semantics, and prior tokenization, what + // appeared a single token, e.g. "3A", might have been tokenized + // into two tokens "3" and "A", but the "A" will have 'space' set to + // false. Accumulate all of these to recreate the original lexical + // appearing token. + do { + token = scanToken(&pastedPpToken); + + // This covers end of argument expansion + if (token == tMarkerInput::marker) { + parseContext.ppError(ppToken.loc, "unexpected location; end of argument", "##", ""); + return resultToken; + } + + // get the token text + switch (resultToken) { + case PpAtomIdentifier: + // already have the correct text in token.names + break; + case '=': + case '!': + case '-': + case '~': + case '+': + case '*': + case '/': + case '%': + case '<': + case '>': + case '|': + case '^': + case '&': + case PpAtomRight: + case PpAtomLeft: + case PpAtomAnd: + case PpAtomOr: + case PpAtomXor: + snprintf(ppToken.name, sizeof(ppToken.name), "%s", atomStrings.getString(resultToken)); + snprintf(pastedPpToken.name, sizeof(pastedPpToken.name), "%s", atomStrings.getString(token)); + break; + default: + parseContext.ppError(ppToken.loc, "not supported for these tokens", "##", ""); + return resultToken; + } + + // combine the tokens + if (strlen(ppToken.name) + strlen(pastedPpToken.name) > MaxTokenLength) { + parseContext.ppError(ppToken.loc, "combined tokens are too long", "##", ""); + return resultToken; + } + snprintf(&ppToken.name[0] + strlen(ppToken.name), sizeof(ppToken.name) - strlen(ppToken.name), + "%s", pastedPpToken.name); + + // correct the kind of token we are making, if needed (identifiers stay identifiers) + if (resultToken != PpAtomIdentifier) { + int newToken = atomStrings.getAtom(ppToken.name); + if (newToken > 0) + resultToken = newToken; + else + parseContext.ppError(ppToken.loc, "combined token is invalid", "##", ""); + } + } while (peekContinuedPasting(resultToken)); + } + + return resultToken; +} + +// Checks if we've seen balanced #if...#endif +void TPpContext::missingEndifCheck() +{ + if (ifdepth > 0) + parseContext.ppError(parseContext.getCurrentLoc(), "missing #endif", "", ""); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp new file mode 100755 index 0000000..7ed5870 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.cpp @@ -0,0 +1,221 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +// +// For recording and playing back the stream of tokens in a macro definition. +// + +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) +#define snprintf sprintf_s +#endif + +#include +#include +#include +#include + +#include "PpContext.h" +#include "PpTokens.h" + +namespace glslang { + +// Add a token (including backing string) to the end of a macro +// token stream, for later playback. +void TPpContext::TokenStream::putToken(int atom, TPpToken* ppToken) +{ + TokenStream::Token streamToken(atom, *ppToken); + stream.push_back(streamToken); +} + +// Read the next token from a macro token stream. +int TPpContext::TokenStream::getToken(TParseContextBase& parseContext, TPpToken *ppToken) +{ + if (atEnd()) + return EndOfInput; + + int atom = stream[currentPos++].get(*ppToken); + ppToken->loc = parseContext.getCurrentLoc(); + +#ifndef GLSLANG_WEB + // Check for ##, unless the current # is the last character + if (atom == '#') { + if (peekToken('#')) { + parseContext.requireProfile(ppToken->loc, ~EEsProfile, "token pasting (##)"); + parseContext.profileRequires(ppToken->loc, ~EEsProfile, 130, 0, "token pasting (##)"); + currentPos++; + atom = PpAtomPaste; + } + } +#endif + + return atom; +} + +// We are pasting if +// 1. we are preceding a pasting operator within this stream +// or +// 2. the entire macro is preceding a pasting operator (lastTokenPastes) +// and we are also on the last token +bool TPpContext::TokenStream::peekTokenizedPasting(bool lastTokenPastes) +{ + // 1. preceding ##? + + size_t savePos = currentPos; + // skip white space + while (peekToken(' ')) + ++currentPos; + if (peekToken(PpAtomPaste)) { + currentPos = savePos; + return true; + } + + // 2. last token and we've been told after this there will be a ## + + if (! lastTokenPastes) + return false; + // Getting here means the last token will be pasted, after this + + // Are we at the last non-whitespace token? + savePos = currentPos; + bool moreTokens = false; + do { + if (atEnd()) + break; + if (!peekToken(' ')) { + moreTokens = true; + break; + } + ++currentPos; + } while (true); + currentPos = savePos; + + return !moreTokens; +} + +// See if the next non-white-space tokens are two consecutive # +bool TPpContext::TokenStream::peekUntokenizedPasting() +{ + // don't return early, have to restore this + size_t savePos = currentPos; + + // skip white-space + while (peekToken(' ')) + ++currentPos; + + // check for ## + bool pasting = false; + if (peekToken('#')) { + ++currentPos; + if (peekToken('#')) + pasting = true; + } + + currentPos = savePos; + + return pasting; +} + +void TPpContext::pushTokenStreamInput(TokenStream& ts, bool prepasting) +{ + pushInput(new tTokenInput(this, &ts, prepasting)); + ts.reset(); +} + +int TPpContext::tUngotTokenInput::scan(TPpToken* ppToken) +{ + if (done) + return EndOfInput; + + int ret = token; + *ppToken = lval; + done = true; + + return ret; +} + +void TPpContext::UngetToken(int token, TPpToken* ppToken) +{ + pushInput(new tUngotTokenInput(this, token, ppToken)); +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h new file mode 100644 index 0000000..7b0f815 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/preprocessor/PpTokens.h @@ -0,0 +1,179 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +/****************************************************************************\ +Copyright (c) 2002, NVIDIA Corporation. + +NVIDIA Corporation("NVIDIA") supplies this software to you in +consideration of your agreement to the following terms, and your use, +installation, modification or redistribution of this NVIDIA software +constitutes acceptance of these terms. If you do not agree with these +terms, please do not use, install, modify or redistribute this NVIDIA +software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, NVIDIA grants you a personal, non-exclusive +license, under NVIDIA's copyrights in this original NVIDIA software (the +"NVIDIA Software"), to use, reproduce, modify and redistribute the +NVIDIA Software, with or without modifications, in source and/or binary +forms; provided that if you redistribute the NVIDIA Software, you must +retain the copyright notice of NVIDIA, this notice and the following +text and disclaimers in all such redistributions of the NVIDIA Software. +Neither the name, trademarks, service marks nor logos of NVIDIA +Corporation may be used to endorse or promote products derived from the +NVIDIA Software without specific prior written permission from NVIDIA. +Except as expressly stated in this notice, no other rights or licenses +express or implied, are granted by NVIDIA herein, including but not +limited to any patent rights that may be infringed by your derivative +works or by other works in which the NVIDIA Software may be +incorporated. No hardware is licensed hereunder. + +THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, +INCLUDING WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +ITS USE AND OPERATION EITHER ALONE OR IN COMBINATION WITH OTHER +PRODUCTS. + +IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, +INCIDENTAL, EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, LOST PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY +OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE +NVIDIA SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, +TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF +NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\****************************************************************************/ + +#ifndef PARSER_H +#define PARSER_H + +namespace glslang { + +// Multi-character tokens +enum EFixedAtoms { + // single character tokens get their own char value as their token; start here for multi-character tokens + PpAtomMaxSingle = 127, + + // replace bad character tokens with this, to avoid accidental aliasing with the below + PpAtomBadToken, + + // Operators + + PPAtomAddAssign, + PPAtomSubAssign, + PPAtomMulAssign, + PPAtomDivAssign, + PPAtomModAssign, + + PpAtomRight, + PpAtomLeft, + + PpAtomRightAssign, + PpAtomLeftAssign, + PpAtomAndAssign, + PpAtomOrAssign, + PpAtomXorAssign, + + PpAtomAnd, + PpAtomOr, + PpAtomXor, + + PpAtomEQ, + PpAtomNE, + PpAtomGE, + PpAtomLE, + + PpAtomDecrement, + PpAtomIncrement, + + PpAtomColonColon, + + PpAtomPaste, + + // Constants + + PpAtomConstInt, + PpAtomConstUint, + PpAtomConstInt64, + PpAtomConstUint64, + PpAtomConstInt16, + PpAtomConstUint16, + PpAtomConstFloat, + PpAtomConstDouble, + PpAtomConstFloat16, + PpAtomConstString, + + // Identifiers + PpAtomIdentifier, + + // preprocessor "keywords" + + PpAtomDefine, + PpAtomUndef, + + PpAtomIf, + PpAtomIfdef, + PpAtomIfndef, + PpAtomElse, + PpAtomElif, + PpAtomEndif, + + PpAtomLine, + PpAtomPragma, + PpAtomError, + + // #version ... + PpAtomVersion, + PpAtomCore, + PpAtomCompatibility, + PpAtomEs, + + // #extension + PpAtomExtension, + + // __LINE__, __FILE__, __VERSION__ + + PpAtomLineMacro, + PpAtomFileMacro, + PpAtomVersionMacro, + + // #include + PpAtomInclude, + + PpAtomLast, +}; + +} // end namespace glslang + +#endif /* not PARSER_H */ diff --git a/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp b/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp new file mode 100644 index 0000000..9def592 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.cpp @@ -0,0 +1,870 @@ +// +// Copyright (C) 2015-2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// propagate the 'noContraction' qualifier. +// + +#ifndef GLSLANG_WEB + +#include "propagateNoContraction.h" + +#include +#include +#include +#include +#include + +#include "localintermediate.h" +namespace { + +// Use a string to hold the access chain information, as in most cases the +// access chain is short and may contain only one element, which is the symbol +// ID. +// Example: struct {float a; float b;} s; +// Object s.a will be represented with: /0 +// Object s.b will be represented with: /1 +// Object s will be represented with: +// For members of vector, matrix and arrays, they will be represented with the +// same symbol ID of their container symbol objects. This is because their +// preciseness is always the same as their container symbol objects. +typedef std::string ObjectAccessChain; + +// The delimiter used in the ObjectAccessChain string to separate symbol ID and +// different level of struct indices. +const char ObjectAccesschainDelimiter = '/'; + +// Mapping from Symbol IDs of symbol nodes, to their defining operation +// nodes. +typedef std::unordered_multimap NodeMapping; +// Mapping from object nodes to their access chain info string. +typedef std::unordered_map AccessChainMapping; + +// Set of object IDs. +typedef std::unordered_set ObjectAccesschainSet; +// Set of return branch nodes. +typedef std::unordered_set ReturnBranchNodeSet; + +// A helper function to tell whether a node is 'noContraction'. Returns true if +// the node has 'noContraction' qualifier, otherwise false. +bool isPreciseObjectNode(glslang::TIntermTyped* node) +{ + return node->getType().getQualifier().isNoContraction(); +} + +// Returns true if the opcode is a dereferencing one. +bool isDereferenceOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpIndexDirect: + case glslang::EOpIndexDirectStruct: + case glslang::EOpIndexIndirect: + case glslang::EOpVectorSwizzle: + case glslang::EOpMatrixSwizzle: + return true; + default: + return false; + } +} + +// Returns true if the opcode leads to an assignment operation. +bool isAssignOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpAssign: + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + case glslang::EOpAndAssign: + case glslang::EOpLeftShiftAssign: + case glslang::EOpRightShiftAssign: + case glslang::EOpInclusiveOrAssign: + case glslang::EOpExclusiveOrAssign: + + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + return true; + default: + return false; + } +} + +// A helper function to get the unsigned int from a given constant union node. +// Note the node should only hold a uint scalar. +unsigned getStructIndexFromConstantUnion(glslang::TIntermTyped* node) +{ + assert(node->getAsConstantUnion() && node->getAsConstantUnion()->isScalar()); + unsigned struct_dereference_index = node->getAsConstantUnion()->getConstArray()[0].getUConst(); + return struct_dereference_index; +} + +// A helper function to generate symbol_label. +ObjectAccessChain generateSymbolLabel(glslang::TIntermSymbol* node) +{ + ObjectAccessChain symbol_id = + std::to_string(node->getId()) + "(" + node->getName().c_str() + ")"; + return symbol_id; +} + +// Returns true if the operation is an arithmetic operation and valid for +// the 'NoContraction' decoration. +bool isArithmeticOperation(glslang::TOperator op) +{ + switch (op) { + case glslang::EOpAddAssign: + case glslang::EOpSubAssign: + case glslang::EOpMulAssign: + case glslang::EOpVectorTimesMatrixAssign: + case glslang::EOpVectorTimesScalarAssign: + case glslang::EOpMatrixTimesScalarAssign: + case glslang::EOpMatrixTimesMatrixAssign: + case glslang::EOpDivAssign: + case glslang::EOpModAssign: + + case glslang::EOpNegative: + + case glslang::EOpAdd: + case glslang::EOpSub: + case glslang::EOpMul: + case glslang::EOpDiv: + case glslang::EOpMod: + + case glslang::EOpVectorTimesScalar: + case glslang::EOpVectorTimesMatrix: + case glslang::EOpMatrixTimesVector: + case glslang::EOpMatrixTimesScalar: + case glslang::EOpMatrixTimesMatrix: + + case glslang::EOpDot: + + case glslang::EOpPostIncrement: + case glslang::EOpPostDecrement: + case glslang::EOpPreIncrement: + case glslang::EOpPreDecrement: + return true; + default: + return false; + } +} + +// A helper class to help manage the populating_initial_no_contraction_ flag. +template class StateSettingGuard { +public: + StateSettingGuard(T* state_ptr, T new_state_value) + : state_ptr_(state_ptr), previous_state_(*state_ptr) + { + *state_ptr = new_state_value; + } + StateSettingGuard(T* state_ptr) : state_ptr_(state_ptr), previous_state_(*state_ptr) {} + void setState(T new_state_value) { *state_ptr_ = new_state_value; } + ~StateSettingGuard() { *state_ptr_ = previous_state_; } + +private: + T* state_ptr_; + T previous_state_; +}; + +// A helper function to get the front element from a given ObjectAccessChain +ObjectAccessChain getFrontElement(const ObjectAccessChain& chain) +{ + size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter); + return pos_delimiter == std::string::npos ? chain : chain.substr(0, pos_delimiter); +} + +// A helper function to get the access chain starting from the second element. +ObjectAccessChain subAccessChainFromSecondElement(const ObjectAccessChain& chain) +{ + size_t pos_delimiter = chain.find(ObjectAccesschainDelimiter); + return pos_delimiter == std::string::npos ? "" : chain.substr(pos_delimiter + 1); +} + +// A helper function to get the access chain after removing a given prefix. +ObjectAccessChain getSubAccessChainAfterPrefix(const ObjectAccessChain& chain, + const ObjectAccessChain& prefix) +{ + size_t pos = chain.find(prefix); + if (pos != 0) + return chain; + return chain.substr(prefix.length() + sizeof(ObjectAccesschainDelimiter)); +} + +// +// A traverser which traverses the whole AST and populates: +// 1) A mapping from symbol nodes' IDs to their defining operation nodes. +// 2) A set of access chains of the initial precise object nodes. +// +class TSymbolDefinitionCollectingTraverser : public glslang::TIntermTraverser { +public: + TSymbolDefinitionCollectingTraverser(NodeMapping* symbol_definition_mapping, + AccessChainMapping* accesschain_mapping, + ObjectAccesschainSet* precise_objects, + ReturnBranchNodeSet* precise_return_nodes); + + bool visitUnary(glslang::TVisit, glslang::TIntermUnary*) override; + bool visitBinary(glslang::TVisit, glslang::TIntermBinary*) override; + void visitSymbol(glslang::TIntermSymbol*) override; + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate*) override; + bool visitBranch(glslang::TVisit, glslang::TIntermBranch*) override; + +protected: + TSymbolDefinitionCollectingTraverser& operator=(const TSymbolDefinitionCollectingTraverser&); + + // The mapping from symbol node IDs to their defining nodes. This should be + // populated along traversing the AST. + NodeMapping& symbol_definition_mapping_; + // The set of symbol node IDs for precise symbol nodes, the ones marked as + // 'noContraction'. + ObjectAccesschainSet& precise_objects_; + // The set of precise return nodes. + ReturnBranchNodeSet& precise_return_nodes_; + // A temporary cache of the symbol node whose defining node is to be found + // currently along traversing the AST. + ObjectAccessChain current_object_; + // A map from object node to its access chain. This traverser stores + // the built access chains into this map for each object node it has + // visited. + AccessChainMapping& accesschain_mapping_; + // The pointer to the Function Definition node, so we can get the + // preciseness of the return expression from it when we traverse the + // return branch node. + glslang::TIntermAggregate* current_function_definition_node_; +}; + +TSymbolDefinitionCollectingTraverser::TSymbolDefinitionCollectingTraverser( + NodeMapping* symbol_definition_mapping, AccessChainMapping* accesschain_mapping, + ObjectAccesschainSet* precise_objects, + std::unordered_set* precise_return_nodes) + : TIntermTraverser(true, false, false), symbol_definition_mapping_(*symbol_definition_mapping), + precise_objects_(*precise_objects), precise_return_nodes_(*precise_return_nodes), + current_object_(), accesschain_mapping_(*accesschain_mapping), + current_function_definition_node_(nullptr) {} + +// Visits a symbol node, set the current_object_ to the +// current node symbol ID, and record a mapping from this node to the current +// current_object_, which is the just obtained symbol +// ID. +void TSymbolDefinitionCollectingTraverser::visitSymbol(glslang::TIntermSymbol* node) +{ + current_object_ = generateSymbolLabel(node); + accesschain_mapping_[node] = current_object_; +} + +// Visits an aggregate node, traverses all of its children. +bool TSymbolDefinitionCollectingTraverser::visitAggregate(glslang::TVisit, + glslang::TIntermAggregate* node) +{ + // This aggregate node might be a function definition node, in which case we need to + // cache this node, so we can get the preciseness information of the return value + // of this function later. + StateSettingGuard current_function_definition_node_setting_guard( + ¤t_function_definition_node_); + if (node->getOp() == glslang::EOpFunction) { + // This is function definition node, we need to cache this node so that we can + // get the preciseness of the return value later. + current_function_definition_node_setting_guard.setState(node); + } + // Traverse the items in the sequence. + glslang::TIntermSequence& seq = node->getSequence(); + for (int i = 0; i < (int)seq.size(); ++i) { + current_object_.clear(); + seq[i]->traverse(this); + } + return false; +} + +bool TSymbolDefinitionCollectingTraverser::visitBranch(glslang::TVisit, + glslang::TIntermBranch* node) +{ + if (node->getFlowOp() == glslang::EOpReturn && node->getExpression() && + current_function_definition_node_ && + current_function_definition_node_->getType().getQualifier().noContraction) { + // This node is a return node with an expression, and its function has a + // precise return value. We need to find the involved objects in its + // expression and add them to the set of initial precise objects. + precise_return_nodes_.insert(node); + node->getExpression()->traverse(this); + } + return false; +} + +// Visits a unary node. This might be an implicit assignment like i++, i--. etc. +bool TSymbolDefinitionCollectingTraverser::visitUnary(glslang::TVisit /* visit */, + glslang::TIntermUnary* node) +{ + current_object_.clear(); + node->getOperand()->traverse(this); + if (isAssignOperation(node->getOp())) { + // We should always be able to get an access chain of the operand node. + assert(!current_object_.empty()); + + // If the operand node object is 'precise', we collect its access chain + // for the initial set of 'precise' objects. + if (isPreciseObjectNode(node->getOperand())) { + // The operand node is an 'precise' object node, add its + // access chain to the set of 'precise' objects. This is to collect + // the initial set of 'precise' objects. + precise_objects_.insert(current_object_); + } + // Gets the symbol ID from the object's access chain. + ObjectAccessChain id_symbol = getFrontElement(current_object_); + // Add a mapping from the symbol ID to this assignment operation node. + symbol_definition_mapping_.insert(std::make_pair(id_symbol, node)); + } + // A unary node is not a dereference node, so we clear the access chain which + // is under construction. + current_object_.clear(); + return false; +} + +// Visits a binary node and updates the mapping from symbol IDs to the definition +// nodes. Also collects the access chains for the initial precise objects. +bool TSymbolDefinitionCollectingTraverser::visitBinary(glslang::TVisit /* visit */, + glslang::TIntermBinary* node) +{ + // Traverses the left node to build the access chain info for the object. + current_object_.clear(); + node->getLeft()->traverse(this); + + if (isAssignOperation(node->getOp())) { + // We should always be able to get an access chain for the left node. + assert(!current_object_.empty()); + + // If the left node object is 'precise', it is an initial precise object + // specified in the shader source. Adds it to the initial work list to + // process later. + if (isPreciseObjectNode(node->getLeft())) { + // The left node is an 'precise' object node, add its access chain to + // the set of 'precise' objects. This is to collect the initial set + // of 'precise' objects. + precise_objects_.insert(current_object_); + } + // Gets the symbol ID from the object access chain, which should be the + // first element recorded in the access chain. + ObjectAccessChain id_symbol = getFrontElement(current_object_); + // Adds a mapping from the symbol ID to this assignment operation node. + symbol_definition_mapping_.insert(std::make_pair(id_symbol, node)); + + // Traverses the right node, there may be other 'assignment' + // operations in the right. + current_object_.clear(); + node->getRight()->traverse(this); + + } else if (isDereferenceOperation(node->getOp())) { + // The left node (parent node) is a struct type object. We need to + // record the access chain information of the current node into its + // object id. + if (node->getOp() == glslang::EOpIndexDirectStruct) { + unsigned struct_dereference_index = getStructIndexFromConstantUnion(node->getRight()); + current_object_.push_back(ObjectAccesschainDelimiter); + current_object_.append(std::to_string(struct_dereference_index)); + } + accesschain_mapping_[node] = current_object_; + + // For a dereference node, there is no need to traverse the right child + // node as the right node should always be an integer type object. + + } else { + // For other binary nodes, still traverse the right node. + current_object_.clear(); + node->getRight()->traverse(this); + } + return false; +} + +// Traverses the AST and returns a tuple of four members: +// 1) a mapping from symbol IDs to the definition nodes (aka. assignment nodes) of these symbols. +// 2) a mapping from object nodes in the AST to the access chains of these objects. +// 3) a set of access chains of precise objects. +// 4) a set of return nodes with precise expressions. +std::tuple +getSymbolToDefinitionMappingAndPreciseSymbolIDs(const glslang::TIntermediate& intermediate) +{ + auto result_tuple = std::make_tuple(NodeMapping(), AccessChainMapping(), ObjectAccesschainSet(), + ReturnBranchNodeSet()); + + TIntermNode* root = intermediate.getTreeRoot(); + if (root == 0) + return result_tuple; + + NodeMapping& symbol_definition_mapping = std::get<0>(result_tuple); + AccessChainMapping& accesschain_mapping = std::get<1>(result_tuple); + ObjectAccesschainSet& precise_objects = std::get<2>(result_tuple); + ReturnBranchNodeSet& precise_return_nodes = std::get<3>(result_tuple); + + // Traverses the AST and populate the results. + TSymbolDefinitionCollectingTraverser collector(&symbol_definition_mapping, &accesschain_mapping, + &precise_objects, &precise_return_nodes); + root->traverse(&collector); + + return result_tuple; +} + +// +// A traverser that determine whether the left node (or operand node for unary +// node) of an assignment node is 'precise', containing 'precise' or not, +// according to the access chain a given precise object which share the same +// symbol as the left node. +// +// Post-orderly traverses the left node subtree of an binary assignment node and: +// +// 1) Propagates the 'precise' from the left object nodes to this object node. +// +// 2) Builds object access chain along the traversal, and also compares with +// the access chain of the given 'precise' object along with the traversal to +// tell if the node to be defined is 'precise' or not. +// +class TNoContractionAssigneeCheckingTraverser : public glslang::TIntermTraverser { + + enum DecisionStatus { + // The object node to be assigned to may contain 'precise' objects and also not 'precise' objects. + Mixed = 0, + // The object node to be assigned to is either a 'precise' object or a struct objects whose members are all 'precise'. + Precise = 1, + // The object node to be assigned to is not a 'precise' object. + NotPreicse = 2, + }; + +public: + TNoContractionAssigneeCheckingTraverser(const AccessChainMapping& accesschain_mapping) + : TIntermTraverser(true, false, false), accesschain_mapping_(accesschain_mapping), + precise_object_(nullptr) {} + + // Checks the preciseness of a given assignment node with a precise object + // represented as access chain. The precise object shares the same symbol + // with the assignee of the given assignment node. Return a tuple of two: + // + // 1) The preciseness of the assignee node of this assignment node. True + // if the assignee contains 'precise' objects or is 'precise', false if + // the assignee is not 'precise' according to the access chain of the given + // precise object. + // + // 2) The incremental access chain from the assignee node to its nested + // 'precise' object, according to the access chain of the given precise + // object. This incremental access chain can be empty, which means the + // assignee is 'precise'. Otherwise it shows the path to the nested + // precise object. + std::tuple + getPrecisenessAndRemainedAccessChain(glslang::TIntermOperator* node, + const ObjectAccessChain& precise_object) + { + assert(isAssignOperation(node->getOp())); + precise_object_ = &precise_object; + ObjectAccessChain assignee_object; + if (glslang::TIntermBinary* BN = node->getAsBinaryNode()) { + // This is a binary assignment node, we need to check the + // preciseness of the left node. + assert(accesschain_mapping_.count(BN->getLeft())); + // The left node (assignee node) is an object node, traverse the + // node to let the 'precise' of nesting objects being transfered to + // nested objects. + BN->getLeft()->traverse(this); + // After traversing the left node, if the left node is 'precise', + // we can conclude this assignment should propagate 'precise'. + if (isPreciseObjectNode(BN->getLeft())) { + return make_tuple(true, ObjectAccessChain()); + } + // If the preciseness of the left node (assignee node) can not + // be determined by now, we need to compare the access chain string + // of the assignee object with the given precise object. + assignee_object = accesschain_mapping_.at(BN->getLeft()); + + } else if (glslang::TIntermUnary* UN = node->getAsUnaryNode()) { + // This is a unary assignment node, we need to check the + // preciseness of the operand node. For unary assignment node, the + // operand node should always be an object node. + assert(accesschain_mapping_.count(UN->getOperand())); + // Traverse the operand node to let the 'precise' being propagated + // from lower nodes to upper nodes. + UN->getOperand()->traverse(this); + // After traversing the operand node, if the operand node is + // 'precise', this assignment should propagate 'precise'. + if (isPreciseObjectNode(UN->getOperand())) { + return make_tuple(true, ObjectAccessChain()); + } + // If the preciseness of the operand node (assignee node) can not + // be determined by now, we need to compare the access chain string + // of the assignee object with the given precise object. + assignee_object = accesschain_mapping_.at(UN->getOperand()); + } else { + // Not a binary or unary node, should not happen. + assert(false); + } + + // Compare the access chain string of the assignee node with the given + // precise object to determine if this assignment should propagate + // 'precise'. + if (assignee_object.find(precise_object) == 0) { + // The access chain string of the given precise object is a prefix + // of assignee's access chain string. The assignee should be + // 'precise'. + return make_tuple(true, ObjectAccessChain()); + } else if (precise_object.find(assignee_object) == 0) { + // The assignee's access chain string is a prefix of the given + // precise object, the assignee object contains 'precise' object, + // and we need to pass the remained access chain to the object nodes + // in the right. + return make_tuple(true, getSubAccessChainAfterPrefix(precise_object, assignee_object)); + } else { + // The access chain strings do not match, the assignee object can + // not be labeled as 'precise' according to the given precise + // object. + return make_tuple(false, ObjectAccessChain()); + } + } + +protected: + TNoContractionAssigneeCheckingTraverser& operator=(const TNoContractionAssigneeCheckingTraverser&); + + bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override; + void visitSymbol(glslang::TIntermSymbol* node) override; + + // A map from object nodes to their access chain string (used as object ID). + const AccessChainMapping& accesschain_mapping_; + // A given precise object, represented in it access chain string. This + // precise object is used to be compared with the assignee node to tell if + // the assignee node is 'precise', contains 'precise' object or not + // 'precise'. + const ObjectAccessChain* precise_object_; +}; + +// Visits a binary node. If the node is an object node, it must be a dereference +// node. In such cases, if the left node is 'precise', this node should also be +// 'precise'. +bool TNoContractionAssigneeCheckingTraverser::visitBinary(glslang::TVisit, + glslang::TIntermBinary* node) +{ + // Traverses the left so that we transfer the 'precise' from nesting object + // to its nested object. + node->getLeft()->traverse(this); + // If this binary node is an object node, we should have it in the + // accesschain_mapping_. + if (accesschain_mapping_.count(node)) { + // A binary object node must be a dereference node. + assert(isDereferenceOperation(node->getOp())); + // If the left node is 'precise', this node should also be precise, + // otherwise, compare with the given precise_object_. If the + // access chain of this node matches with the given precise_object_, + // this node should be marked as 'precise'. + if (isPreciseObjectNode(node->getLeft())) { + node->getWritableType().getQualifier().noContraction = true; + } else if (accesschain_mapping_.at(node) == *precise_object_) { + node->getWritableType().getQualifier().noContraction = true; + } + } + return false; +} + +// Visits a symbol node, if the symbol node ID (its access chain string) matches +// with the given precise object, this node should be 'precise'. +void TNoContractionAssigneeCheckingTraverser::visitSymbol(glslang::TIntermSymbol* node) +{ + // A symbol node should always be an object node, and should have been added + // to the map from object nodes to their access chain strings. + assert(accesschain_mapping_.count(node)); + if (accesschain_mapping_.at(node) == *precise_object_) { + node->getWritableType().getQualifier().noContraction = true; + } +} + +// +// A traverser that only traverses the right side of binary assignment nodes +// and the operand node of unary assignment nodes. +// +// 1) Marks arithmetic operations as 'NoContraction'. +// +// 2) Find the object which should be marked as 'precise' in the right and +// update the 'precise' object work list. +// +class TNoContractionPropagator : public glslang::TIntermTraverser { +public: + TNoContractionPropagator(ObjectAccesschainSet* precise_objects, + const AccessChainMapping& accesschain_mapping) + : TIntermTraverser(true, false, false), + precise_objects_(*precise_objects), added_precise_object_ids_(), + remained_accesschain_(), accesschain_mapping_(accesschain_mapping) {} + + // Propagates 'precise' in the right nodes of a given assignment node with + // access chain record from the assignee node to a 'precise' object it + // contains. + void + propagateNoContractionInOneExpression(glslang::TIntermTyped* defining_node, + const ObjectAccessChain& assignee_remained_accesschain) + { + remained_accesschain_ = assignee_remained_accesschain; + if (glslang::TIntermBinary* BN = defining_node->getAsBinaryNode()) { + assert(isAssignOperation(BN->getOp())); + BN->getRight()->traverse(this); + if (isArithmeticOperation(BN->getOp())) { + BN->getWritableType().getQualifier().noContraction = true; + } + } else if (glslang::TIntermUnary* UN = defining_node->getAsUnaryNode()) { + assert(isAssignOperation(UN->getOp())); + UN->getOperand()->traverse(this); + if (isArithmeticOperation(UN->getOp())) { + UN->getWritableType().getQualifier().noContraction = true; + } + } + } + + // Propagates 'precise' in a given precise return node. + void propagateNoContractionInReturnNode(glslang::TIntermBranch* return_node) + { + remained_accesschain_ = ""; + assert(return_node->getFlowOp() == glslang::EOpReturn && return_node->getExpression()); + return_node->getExpression()->traverse(this); + } + +protected: + TNoContractionPropagator& operator=(const TNoContractionPropagator&); + + // Visits an aggregate node. The node can be a initializer list, in which + // case we need to find the 'precise' or 'precise' containing object node + // with the access chain record. In other cases, just need to traverse all + // the children nodes. + bool visitAggregate(glslang::TVisit, glslang::TIntermAggregate* node) override + { + if (!remained_accesschain_.empty() && node->getOp() == glslang::EOpConstructStruct) { + // This is a struct initializer node, and the remained + // access chain is not empty, we need to refer to the + // assignee_remained_access_chain_ to find the nested + // 'precise' object. And we don't need to visit other nodes in this + // aggregate node. + + // Gets the struct dereference index that leads to 'precise' object. + ObjectAccessChain precise_accesschain_index_str = + getFrontElement(remained_accesschain_); + unsigned precise_accesschain_index = (unsigned)strtoul(precise_accesschain_index_str.c_str(), nullptr, 10); + // Gets the node pointed by the access chain index extracted before. + glslang::TIntermTyped* potential_precise_node = + node->getSequence()[precise_accesschain_index]->getAsTyped(); + assert(potential_precise_node); + // Pop the front access chain index from the path, and visit the nested node. + { + ObjectAccessChain next_level_accesschain = + subAccessChainFromSecondElement(remained_accesschain_); + StateSettingGuard setup_remained_accesschain_for_next_level( + &remained_accesschain_, next_level_accesschain); + potential_precise_node->traverse(this); + } + return false; + } + return true; + } + + // Visits a binary node. A binary node can be an object node, e.g. a dereference node. + // As only the top object nodes in the right side of an assignment needs to be visited + // and added to 'precise' work list, this traverser won't visit the children nodes of + // an object node. If the binary node does not represent an object node, it should + // go on to traverse its children nodes and if it is an arithmetic operation node, this + // operation should be marked as 'noContraction'. + bool visitBinary(glslang::TVisit, glslang::TIntermBinary* node) override + { + if (isDereferenceOperation(node->getOp())) { + // This binary node is an object node. Need to update the precise + // object set with the access chain of this node + remained + // access chain . + ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node); + if (remained_accesschain_.empty()) { + node->getWritableType().getQualifier().noContraction = true; + } else { + new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_; + } + // Cache the access chain as added precise object, so we won't add the + // same object to the work list again. + if (!added_precise_object_ids_.count(new_precise_accesschain)) { + precise_objects_.insert(new_precise_accesschain); + added_precise_object_ids_.insert(new_precise_accesschain); + } + // Only the upper-most object nodes should be visited, so do not + // visit children of this object node. + return false; + } + // If this is an arithmetic operation, marks this node as 'noContraction'. + if (isArithmeticOperation(node->getOp()) && node->getBasicType() != glslang::EbtInt) { + node->getWritableType().getQualifier().noContraction = true; + } + // As this node is not an object node, need to traverse the children nodes. + return true; + } + + // Visits a unary node. A unary node can not be an object node. If the operation + // is an arithmetic operation, need to mark this node as 'noContraction'. + bool visitUnary(glslang::TVisit /* visit */, glslang::TIntermUnary* node) override + { + // If this is an arithmetic operation, marks this with 'noContraction' + if (isArithmeticOperation(node->getOp())) { + node->getWritableType().getQualifier().noContraction = true; + } + return true; + } + + // Visits a symbol node. A symbol node is always an object node. So we + // should always be able to find its in our collected mapping from object + // nodes to access chains. As an object node, a symbol node can be either + // 'precise' or containing 'precise' objects according to unused + // access chain information we have when we visit this node. + void visitSymbol(glslang::TIntermSymbol* node) override + { + // Symbol nodes are object nodes and should always have an + // access chain collected before matches with it. + assert(accesschain_mapping_.count(node)); + ObjectAccessChain new_precise_accesschain = accesschain_mapping_.at(node); + // If the unused access chain is empty, this symbol node should be + // marked as 'precise'. Otherwise, the unused access chain should be + // appended to the symbol ID to build a new access chain which points to + // the nested 'precise' object in this symbol object. + if (remained_accesschain_.empty()) { + node->getWritableType().getQualifier().noContraction = true; + } else { + new_precise_accesschain += ObjectAccesschainDelimiter + remained_accesschain_; + } + // Add the new 'precise' access chain to the work list and make sure we + // don't visit it again. + if (!added_precise_object_ids_.count(new_precise_accesschain)) { + precise_objects_.insert(new_precise_accesschain); + added_precise_object_ids_.insert(new_precise_accesschain); + } + } + + // A set of precise objects, represented as access chains. + ObjectAccesschainSet& precise_objects_; + // Visited symbol nodes, should not revisit these nodes. + ObjectAccesschainSet added_precise_object_ids_; + // The left node of an assignment operation might be an parent of 'precise' objects. + // This means the left node might not be an 'precise' object node, but it may contains + // 'precise' qualifier which should be propagated to the corresponding child node in + // the right. So we need the path from the left node to its nested 'precise' node to + // tell us how to find the corresponding 'precise' node in the right. + ObjectAccessChain remained_accesschain_; + // A map from node pointers to their access chains. + const AccessChainMapping& accesschain_mapping_; +}; +} + +namespace glslang { + +void PropagateNoContraction(const glslang::TIntermediate& intermediate) +{ + // First, traverses the AST, records symbols with their defining operations + // and collects the initial set of precise symbols (symbol nodes that marked + // as 'noContraction') and precise return nodes. + auto mappings_and_precise_objects = + getSymbolToDefinitionMappingAndPreciseSymbolIDs(intermediate); + + // The mapping of symbol node IDs to their defining nodes. This enables us + // to get the defining node directly from a given symbol ID without + // traversing the tree again. + NodeMapping& symbol_definition_mapping = std::get<0>(mappings_and_precise_objects); + + // The mapping of object nodes to their access chains recorded. + AccessChainMapping& accesschain_mapping = std::get<1>(mappings_and_precise_objects); + + // The initial set of 'precise' objects which are represented as the + // access chain toward them. + ObjectAccesschainSet& precise_object_accesschains = std::get<2>(mappings_and_precise_objects); + + // The set of 'precise' return nodes. + ReturnBranchNodeSet& precise_return_nodes = std::get<3>(mappings_and_precise_objects); + + // Second, uses the initial set of precise objects as a work list, pops an + // access chain, extract the symbol ID from it. Then: + // 1) Check the assignee object, see if it is 'precise' object node or + // contains 'precise' object. Obtain the incremental access chain from the + // assignee node to its nested 'precise' node (if any). + // 2) If the assignee object node is 'precise' or it contains 'precise' + // objects, traverses the right side of the assignment operation + // expression to mark arithmetic operations as 'noContration' and update + // 'precise' access chain work list with new found object nodes. + // Repeat above steps until the work list is empty. + TNoContractionAssigneeCheckingTraverser checker(accesschain_mapping); + TNoContractionPropagator propagator(&precise_object_accesschains, accesschain_mapping); + + // We have two initial precise work lists to handle: + // 1) precise return nodes + // 2) precise object access chains + // We should process the precise return nodes first and the involved + // objects in the return expression should be added to the precise object + // access chain set. + while (!precise_return_nodes.empty()) { + glslang::TIntermBranch* precise_return_node = *precise_return_nodes.begin(); + propagator.propagateNoContractionInReturnNode(precise_return_node); + precise_return_nodes.erase(precise_return_node); + } + + while (!precise_object_accesschains.empty()) { + // Get the access chain of a precise object from the work list. + ObjectAccessChain precise_object_accesschain = *precise_object_accesschains.begin(); + // Get the symbol id from the access chain. + ObjectAccessChain symbol_id = getFrontElement(precise_object_accesschain); + // Get all the defining nodes of that symbol ID. + std::pair range = + symbol_definition_mapping.equal_range(symbol_id); + // Visits all the assignment nodes of that symbol ID and + // 1) Check if the assignee node is 'precise' or contains 'precise' + // objects. + // 2) Propagate the 'precise' to the top layer object nodes + // in the right side of the assignment operation, update the 'precise' + // work list with new access chains representing the new 'precise' + // objects, and mark arithmetic operations as 'noContraction'. + for (NodeMapping::iterator defining_node_iter = range.first; + defining_node_iter != range.second; defining_node_iter++) { + TIntermOperator* defining_node = defining_node_iter->second; + // Check the assignee node. + auto checker_result = checker.getPrecisenessAndRemainedAccessChain( + defining_node, precise_object_accesschain); + bool& contain_precise = std::get<0>(checker_result); + ObjectAccessChain& remained_accesschain = std::get<1>(checker_result); + // If the assignee node is 'precise' or contains 'precise', propagate the + // 'precise' to the right. Otherwise just skip this assignment node. + if (contain_precise) { + propagator.propagateNoContractionInOneExpression(defining_node, + remained_accesschain); + } + } + // Remove the last processed 'precise' object from the work list. + precise_object_accesschains.erase(precise_object_accesschain); + } +} +}; + +#endif // GLSLANG_WEB diff --git a/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.h b/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.h new file mode 100644 index 0000000..8521ad7 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/propagateNoContraction.h @@ -0,0 +1,55 @@ +// +// Copyright (C) 2015-2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// +// Visit the nodes in the glslang intermediate tree representation to +// propagate 'noContraction' qualifier. +// + +#pragma once + +#include "../Include/intermediate.h" + +namespace glslang { + +// Propagates the 'precise' qualifier for objects (objects marked with +// 'noContraction' qualifier) from the shader source specified 'precise' +// variables to all the involved objects, and add 'noContraction' qualifier for +// the involved arithmetic operations. +// Note that the same qualifier: 'noContraction' is used in both object nodes +// and arithmetic operation nodes, but has different meaning. For object nodes, +// 'noContraction' means the object is 'precise'; and for arithmetic operation +// nodes, it means the operation should not be contracted. +void PropagateNoContraction(const glslang::TIntermediate& intermediate); +}; diff --git a/third_party/glslang/glslang/MachineIndependent/reflection.cpp b/third_party/glslang/glslang/MachineIndependent/reflection.cpp new file mode 100644 index 0000000..7295002 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/reflection.cpp @@ -0,0 +1,1272 @@ +// +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +#include "../Include/Common.h" +#include "reflection.h" +#include "LiveTraverser.h" +#include "localintermediate.h" + +#include "gl_types.h" + +// +// Grow the reflection database through a friend traverser class of TReflection and a +// collection of functions to do a liveness traversal that note what uniforms are used +// in semantically non-dead code. +// +// Can be used multiple times, once per stage, to grow a program reflection. +// +// High-level algorithm for one stage: +// +// 1. Put the entry point on the list of live functions. +// +// 2. Traverse any live function, while skipping if-tests with a compile-time constant +// condition of false, and while adding any encountered function calls to the live +// function list. +// +// Repeat until the live function list is empty. +// +// 3. Add any encountered uniform variables and blocks to the reflection database. +// +// Can be attempted with a failed link, but will return false if recursion had been detected, or +// there wasn't exactly one entry point. +// + +namespace glslang { + +// +// The traverser: mostly pass through, except +// - processing binary nodes to see if they are dereferences of an aggregates to track +// - processing symbol nodes to see if they are non-aggregate objects to track +// +// This ignores semantically dead code by using TLiveTraverser. +// +// This is in the glslang namespace directly so it can be a friend of TReflection. +// + +class TReflectionTraverser : public TIntermTraverser { +public: + TReflectionTraverser(const TIntermediate& i, TReflection& r) : + TIntermTraverser(), intermediate(i), reflection(r), updateStageMasks(true) { } + + virtual bool visitBinary(TVisit, TIntermBinary* node); + virtual void visitSymbol(TIntermSymbol* base); + + // Add a simple reference to a uniform variable to the uniform database, no dereference involved. + // However, no dereference doesn't mean simple... it could be a complex aggregate. + void addUniform(const TIntermSymbol& base) + { + if (processedDerefs.find(&base) == processedDerefs.end()) { + processedDerefs.insert(&base); + + int blockIndex = -1; + int offset = -1; + TList derefs; + TString baseName = base.getName(); + + if (base.getType().getBasicType() == EbtBlock) { + offset = 0; + bool anonymous = IsAnonymous(baseName); + const TString& blockName = base.getType().getTypeName(); + + if (!anonymous) + baseName = blockName; + else + baseName = ""; + + blockIndex = addBlockName(blockName, base.getType(), intermediate.getBlockSize(base.getType())); + } + + // Use a degenerate (empty) set of dereferences to immediately put as at the end of + // the dereference change expected by blowUpActiveAggregate. + blowUpActiveAggregate(base.getType(), baseName, derefs, derefs.end(), offset, blockIndex, 0, -1, 0, + base.getQualifier().storage, updateStageMasks); + } + } + + void addPipeIOVariable(const TIntermSymbol& base) + { + if (processedDerefs.find(&base) == processedDerefs.end()) { + processedDerefs.insert(&base); + + const TString &name = base.getName(); + const TType &type = base.getType(); + const bool input = base.getQualifier().isPipeInput(); + + TReflection::TMapIndexToReflection &ioItems = + input ? reflection.indexToPipeInput : reflection.indexToPipeOutput; + + + TReflection::TNameToIndex &ioMapper = + input ? reflection.pipeInNameToIndex : reflection.pipeOutNameToIndex; + + if (reflection.options & EShReflectionUnwrapIOBlocks) { + bool anonymous = IsAnonymous(name); + + TString baseName; + if (type.getBasicType() == EbtBlock) { + baseName = anonymous ? TString() : type.getTypeName(); + } else { + baseName = anonymous ? TString() : name; + } + + // by convention if this is an arrayed block we ignore the array in the reflection + if (type.isArray() && type.getBasicType() == EbtBlock) { + blowUpIOAggregate(input, baseName, TType(type, 0)); + } else { + blowUpIOAggregate(input, baseName, type); + } + } else { + TReflection::TNameToIndex::const_iterator it = ioMapper.find(name.c_str()); + if (it == ioMapper.end()) { + // seperate pipe i/o params from uniforms and blocks + // in is only for input in first stage as out is only for last stage. check traverse in call stack. + ioMapper[name.c_str()] = static_cast(ioItems.size()); + ioItems.push_back( + TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0)); + EShLanguageMask& stages = ioItems.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + EShLanguageMask& stages = ioItems[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + } + } + + // Lookup or calculate the offset of all block members at once, using the recursively + // defined block offset rules. + void getOffsets(const TType& type, TVector& offsets) + { + const TTypeList& memberList = *type.getStruct(); + int memberSize = 0; + int offset = 0; + + for (size_t m = 0; m < offsets.size(); ++m) { + // if the user supplied an offset, snap to it now + if (memberList[m].type->getQualifier().hasOffset()) + offset = memberList[m].type->getQualifier().layoutOffset; + + // calculate the offset of the next member and align the current offset to this member + intermediate.updateOffset(type, *memberList[m].type, offset, memberSize); + + // save the offset of this member + offsets[m] = offset; + + // update for the next member + offset += memberSize; + } + } + + // Calculate the stride of an array type + int getArrayStride(const TType& baseType, const TType& type) + { + int dummySize; + int stride; + + // consider blocks to have 0 stride, so that all offsets are relative to the start of their block + if (type.getBasicType() == EbtBlock) + return 0; + + TLayoutMatrix subMatrixLayout = type.getQualifier().layoutMatrix; + intermediate.getMemberAlignment(type, dummySize, stride, + baseType.getQualifier().layoutPacking, + subMatrixLayout != ElmNone + ? subMatrixLayout == ElmRowMajor + : baseType.getQualifier().layoutMatrix == ElmRowMajor); + + return stride; + } + + // count the total number of leaf members from iterating out of a block type + int countAggregateMembers(const TType& parentType) + { + if (! parentType.isStruct()) + return 1; + + const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix); + + bool blockParent = (parentType.getBasicType() == EbtBlock && parentType.getQualifier().storage == EvqBuffer); + + const TTypeList &memberList = *parentType.getStruct(); + + int ret = 0; + + for (size_t i = 0; i < memberList.size(); i++) + { + const TType &memberType = *memberList[i].type; + int numMembers = countAggregateMembers(memberType); + // for sized arrays of structs, apply logic to expand out the same as we would below in + // blowUpActiveAggregate + if (memberType.isArray() && ! memberType.getArraySizes()->hasUnsized() && memberType.isStruct()) { + if (! strictArraySuffix || ! blockParent) + numMembers *= memberType.getArraySizes()->getCumulativeSize(); + } + ret += numMembers; + } + + return ret; + } + + // Traverse the provided deref chain, including the base, and + // - build a full reflection-granularity name, array size, etc. entry out of it, if it goes down to that granularity + // - recursively expand any variable array index in the middle of that traversal + // - recursively expand what's left at the end if the deref chain did not reach down to reflection granularity + // + // arraySize tracks, just for the final dereference in the chain, if there was a specific known size. + // A value of 0 for arraySize will mean to use the full array's size. + void blowUpActiveAggregate(const TType& baseType, const TString& baseName, const TList& derefs, + TList::const_iterator deref, int offset, int blockIndex, int arraySize, + int topLevelArraySize, int topLevelArrayStride, TStorageQualifier baseStorage, bool active) + { + // when strictArraySuffix is enabled, we closely follow the rules from ARB_program_interface_query. + // Broadly: + // * arrays-of-structs always have a [x] suffix. + // * with array-of-struct variables in the root of a buffer block, only ever return [0]. + // * otherwise, array suffixes are added whenever we iterate, even if that means expanding out an array. + const bool strictArraySuffix = (reflection.options & EShReflectionStrictArraySuffix); + + // is this variable inside a buffer block. This flag is set back to false after we iterate inside the first array element. + bool blockParent = (baseType.getBasicType() == EbtBlock && baseType.getQualifier().storage == EvqBuffer); + + // process the part of the dereference chain that was explicit in the shader + TString name = baseName; + const TType* terminalType = &baseType; + for (; deref != derefs.end(); ++deref) { + TIntermBinary* visitNode = *deref; + terminalType = &visitNode->getType(); + int index; + switch (visitNode->getOp()) { + case EOpIndexIndirect: { + int stride = getArrayStride(baseType, visitNode->getLeft()->getType()); + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + // Visit all the indices of this array, and for each one add on the remaining dereferencing + for (int i = 0; i < std::max(visitNode->getLeft()->getType().getOuterArraySize(), 1); ++i) { + TString newBaseName = name; + if (terminalType->getBasicType() == EbtBlock) {} + else if (strictArraySuffix && blockParent) + newBaseName.append(TString("[0]")); + else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) + newBaseName.append(TString("[") + String(i) + "]"); + TList::const_iterator nextDeref = deref; + ++nextDeref; + blowUpActiveAggregate(*terminalType, newBaseName, derefs, nextDeref, offset, blockIndex, arraySize, + topLevelArraySize, topLevelArrayStride, baseStorage, active); + + if (offset >= 0) + offset += stride; + } + + // it was all completed in the recursive calls above + return; + } + case EOpIndexDirect: { + int stride = getArrayStride(baseType, visitNode->getLeft()->getType()); + + index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (terminalType->getBasicType() == EbtBlock) {} + else if (strictArraySuffix && blockParent) + name.append(TString("[0]")); + else if (strictArraySuffix || baseType.getBasicType() != EbtBlock) { + name.append(TString("[") + String(index) + "]"); + + if (offset >= 0) + offset += stride * index; + } + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + // expand top-level arrays in blocks with [0] suffix + if (topLevelArrayStride != 0 && visitNode->getLeft()->getType().isArray()) { + blockParent = false; + } + break; + } + case EOpIndexDirectStruct: + index = visitNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); + if (offset >= 0) + offset += intermediate.getOffset(visitNode->getLeft()->getType(), index); + if (name.size() > 0) + name.append("."); + name.append((*visitNode->getLeft()->getType().getStruct())[index].type->getFieldName()); + + // expand non top-level arrays with [x] suffix + if (visitNode->getLeft()->getType().getBasicType() != EbtBlock && terminalType->isArray()) + { + blockParent = false; + } + break; + default: + break; + } + } + + // if the terminalType is still too coarse a granularity, this is still an aggregate to expand, expand it... + if (! isReflectionGranularity(*terminalType)) { + // the base offset of this node, that children are relative to + int baseOffset = offset; + + if (terminalType->isArray()) { + // Visit all the indices of this array, and for each one, + // fully explode the remaining aggregate to dereference + + int stride = 0; + if (offset >= 0) + stride = getArrayStride(baseType, *terminalType); + + int arrayIterateSize = std::max(terminalType->getOuterArraySize(), 1); + + // for top-level arrays in blocks, only expand [0] to avoid explosion of items + if ((strictArraySuffix && blockParent) || + ((topLevelArraySize == arrayIterateSize) && (topLevelArrayStride == 0))) { + arrayIterateSize = 1; + } + + if (topLevelArrayStride == 0) + topLevelArrayStride = stride; + + for (int i = 0; i < arrayIterateSize; ++i) { + TString newBaseName = name; + if (terminalType->getBasicType() != EbtBlock) + newBaseName.append(TString("[") + String(i) + "]"); + TType derefType(*terminalType, 0); + if (offset >= 0) + offset = baseOffset + stride * i; + + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0, + topLevelArraySize, topLevelArrayStride, baseStorage, active); + } + } else { + // Visit all members of this aggregate, and for each one, + // fully explode the remaining aggregate to dereference + const TTypeList& typeList = *terminalType->getStruct(); + + TVector memberOffsets; + + if (baseOffset >= 0) { + memberOffsets.resize(typeList.size()); + getOffsets(*terminalType, memberOffsets); + } + + for (int i = 0; i < (int)typeList.size(); ++i) { + TString newBaseName = name; + if (newBaseName.size() > 0) + newBaseName.append("."); + newBaseName.append(typeList[i].type->getFieldName()); + TType derefType(*terminalType, i); + if (offset >= 0) + offset = baseOffset + memberOffsets[i]; + + int arrayStride = topLevelArrayStride; + if (terminalType->getBasicType() == EbtBlock && terminalType->getQualifier().storage == EvqBuffer && + derefType.isArray()) { + arrayStride = getArrayStride(baseType, derefType); + } + + if (topLevelArraySize == -1 && arrayStride == 0 && blockParent) + topLevelArraySize = 1; + + if (strictArraySuffix && blockParent) { + // if this member is an array, store the top-level array stride but start the explosion from + // the inner struct type. + if (derefType.isArray() && derefType.isStruct()) { + newBaseName.append("[0]"); + auto dimSize = derefType.isUnsizedArray() ? 0 : derefType.getArraySizes()->getDimSize(0); + blowUpActiveAggregate(TType(derefType, 0), newBaseName, derefs, derefs.end(), memberOffsets[i], + blockIndex, 0, dimSize, arrayStride, terminalType->getQualifier().storage, false); + } + else if (derefType.isArray()) { + auto dimSize = derefType.isUnsizedArray() ? 0 : derefType.getArraySizes()->getDimSize(0); + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), memberOffsets[i], blockIndex, + 0, dimSize, 0, terminalType->getQualifier().storage, false); + } + else { + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), memberOffsets[i], blockIndex, + 0, 1, 0, terminalType->getQualifier().storage, false); + } + } else { + blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0, + topLevelArraySize, arrayStride, baseStorage, active); + } + } + } + + // it was all completed in the recursive calls above + return; + } + + if ((reflection.options & EShReflectionBasicArraySuffix) && terminalType->isArray()) { + name.append(TString("[0]")); + } + + // Finally, add a full string to the reflection database, and update the array size if necessary. + // If the dereferenced entity to record is an array, compute the size and update the maximum size. + + // there might not be a final array dereference, it could have been copied as an array object + if (arraySize == 0) + arraySize = mapToGlArraySize(*terminalType); + + TReflection::TMapIndexToReflection& variables = reflection.GetVariableMapForStorage(baseStorage); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); + if (it == reflection.nameToIndex.end()) { + int uniformIndex = (int)variables.size(); + reflection.nameToIndex[name.c_str()] = uniformIndex; + variables.push_back(TObjectReflection(name.c_str(), *terminalType, offset, mapToGlType(*terminalType), + arraySize, blockIndex)); + if (terminalType->isArray()) { + variables.back().arrayStride = getArrayStride(baseType, *terminalType); + if (topLevelArrayStride == 0) + topLevelArrayStride = variables.back().arrayStride; + } + + if ((reflection.options & EShReflectionSeparateBuffers) && terminalType->isAtomic()) + reflection.atomicCounterUniformIndices.push_back(uniformIndex); + + variables.back().topLevelArraySize = topLevelArraySize; + variables.back().topLevelArrayStride = topLevelArrayStride; + + if ((reflection.options & EShReflectionAllBlockVariables) && active) { + EShLanguageMask& stages = variables.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } else { + if (arraySize > 1) { + int& reflectedArraySize = variables[it->second].size; + reflectedArraySize = std::max(arraySize, reflectedArraySize); + } + + if ((reflection.options & EShReflectionAllBlockVariables) && active) { + EShLanguageMask& stages = variables[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + } + + // similar to blowUpActiveAggregate, but with simpler rules and no dereferences to follow. + void blowUpIOAggregate(bool input, const TString &baseName, const TType &type) + { + TString name = baseName; + + // if the type is still too coarse a granularity, this is still an aggregate to expand, expand it... + if (! isReflectionGranularity(type)) { + if (type.isArray()) { + // Visit all the indices of this array, and for each one, + // fully explode the remaining aggregate to dereference + for (int i = 0; i < std::max(type.getOuterArraySize(), 1); ++i) { + TString newBaseName = name; + newBaseName.append(TString("[") + String(i) + "]"); + TType derefType(type, 0); + + blowUpIOAggregate(input, newBaseName, derefType); + } + } else { + // Visit all members of this aggregate, and for each one, + // fully explode the remaining aggregate to dereference + const TTypeList& typeList = *type.getStruct(); + + for (int i = 0; i < (int)typeList.size(); ++i) { + TString newBaseName = name; + if (newBaseName.size() > 0) + newBaseName.append("."); + newBaseName.append(typeList[i].type->getFieldName()); + TType derefType(type, i); + + blowUpIOAggregate(input, newBaseName, derefType); + } + } + + // it was all completed in the recursive calls above + return; + } + + if ((reflection.options & EShReflectionBasicArraySuffix) && type.isArray()) { + name.append(TString("[0]")); + } + + TReflection::TMapIndexToReflection &ioItems = + input ? reflection.indexToPipeInput : reflection.indexToPipeOutput; + + std::string namespacedName = input ? "in " : "out "; + namespacedName += name.c_str(); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(namespacedName); + if (it == reflection.nameToIndex.end()) { + reflection.nameToIndex[namespacedName] = (int)ioItems.size(); + ioItems.push_back( + TObjectReflection(name.c_str(), type, 0, mapToGlType(type), mapToGlArraySize(type), 0)); + + EShLanguageMask& stages = ioItems.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } else { + EShLanguageMask& stages = ioItems[it->second].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + + // Add a uniform dereference where blocks/struct/arrays are involved in the access. + // Handles the situation where the left node is at the correct or too coarse a + // granularity for reflection. (That is, further dereferences up the tree will be + // skipped.) Earlier dereferences, down the tree, will be handled + // at the same time, and logged to prevent reprocessing as the tree is traversed. + // + // Note: Other things like the following must be caught elsewhere: + // - a simple non-array, non-struct variable (no dereference even conceivable) + // - an aggregrate consumed en masse, without a dereference + // + // So, this code is for cases like + // - a struct/block dereferencing a member (whether the member is array or not) + // - an array of struct + // - structs/arrays containing the above + // + void addDereferencedUniform(TIntermBinary* topNode) + { + // See if too fine-grained to process (wait to get further down the tree) + const TType& leftType = topNode->getLeft()->getType(); + if ((leftType.isVector() || leftType.isMatrix()) && ! leftType.isArray()) + return; + + // We have an array or structure or block dereference, see if it's a uniform + // based dereference (if not, skip it). + TIntermSymbol* base = findBase(topNode); + if (! base || ! base->getQualifier().isUniformOrBuffer()) + return; + + // See if we've already processed this (e.g., in the middle of something + // we did earlier), and if so skip it + if (processedDerefs.find(topNode) != processedDerefs.end()) + return; + + // Process this uniform dereference + + int offset = -1; + int blockIndex = -1; + bool anonymous = false; + + // See if we need to record the block itself + bool block = base->getBasicType() == EbtBlock; + if (block) { + offset = 0; + anonymous = IsAnonymous(base->getName()); + + const TString& blockName = base->getType().getTypeName(); + TString baseName; + + if (! anonymous) + baseName = blockName; + + blockIndex = addBlockName(blockName, base->getType(), intermediate.getBlockSize(base->getType())); + + if (reflection.options & EShReflectionAllBlockVariables) { + // Use a degenerate (empty) set of dereferences to immediately put as at the end of + // the dereference change expected by blowUpActiveAggregate. + TList derefs; + + // otherwise - if we're not using strict array suffix rules, or this isn't a block so we are + // expanding root arrays anyway, just start the iteration from the base block type. + blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.end(), 0, blockIndex, 0, -1, 0, + base->getQualifier().storage, false); + } + } + + // Process the dereference chain, backward, accumulating the pieces for later forward traversal. + // If the topNode is a reflection-granularity-array dereference, don't include that last dereference. + TList derefs; + for (TIntermBinary* visitNode = topNode; visitNode; visitNode = visitNode->getLeft()->getAsBinaryNode()) { + if (isReflectionGranularity(visitNode->getLeft()->getType())) + continue; + + derefs.push_front(visitNode); + processedDerefs.insert(visitNode); + } + processedDerefs.insert(base); + + // See if we have a specific array size to stick to while enumerating the explosion of the aggregate + int arraySize = 0; + if (isReflectionGranularity(topNode->getLeft()->getType()) && topNode->getLeft()->isArray()) { + if (topNode->getOp() == EOpIndexDirect) + arraySize = topNode->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst() + 1; + } + + // Put the dereference chain together, forward + TString baseName; + if (! anonymous) { + if (block) + baseName = base->getType().getTypeName(); + else + baseName = base->getName(); + } + blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, -1, 0, + base->getQualifier().storage, true); + } + + int addBlockName(const TString& name, const TType& type, int size) + { + int blockIndex = 0; + if (type.isArray()) { + TType derefType(type, 0); + for (int e = 0; e < type.getOuterArraySize(); ++e) { + int memberBlockIndex = addBlockName(name + "[" + String(e) + "]", derefType, size); + if (e == 0) + blockIndex = memberBlockIndex; + } + } else { + TReflection::TMapIndexToReflection& blocks = reflection.GetBlockMapForStorage(type.getQualifier().storage); + + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); + if (reflection.nameToIndex.find(name.c_str()) == reflection.nameToIndex.end()) { + blockIndex = (int)blocks.size(); + reflection.nameToIndex[name.c_str()] = blockIndex; + blocks.push_back(TObjectReflection(name.c_str(), type, -1, -1, size, blockIndex)); + + blocks.back().numMembers = countAggregateMembers(type); + + if (updateStageMasks) { + EShLanguageMask& stages = blocks.back().stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + else { + blockIndex = it->second; + if (updateStageMasks) { + EShLanguageMask& stages = blocks[blockIndex].stages; + stages = static_cast(stages | 1 << intermediate.getStage()); + } + } + } + + return blockIndex; + } + + // Are we at a level in a dereference chain at which individual active uniform queries are made? + bool isReflectionGranularity(const TType& type) + { + return type.getBasicType() != EbtBlock && type.getBasicType() != EbtStruct && !type.isArrayOfArrays(); + } + + // For a binary operation indexing into an aggregate, chase down the base of the aggregate. + // Return 0 if the topology does not fit this situation. + TIntermSymbol* findBase(const TIntermBinary* node) + { + TIntermSymbol *base = node->getLeft()->getAsSymbolNode(); + if (base) + return base; + TIntermBinary* left = node->getLeft()->getAsBinaryNode(); + if (! left) + return nullptr; + + return findBase(left); + } + + // + // Translate a glslang sampler type into the GL API #define number. + // + int mapSamplerToGlType(TSampler sampler) + { + if (! sampler.image) { + // a sampler... + switch (sampler.type) { + case EbtFloat: + switch ((int)sampler.dim) { + case Esd1D: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_SAMPLER_1D_ARRAY : GL_SAMPLER_1D; + case true: return sampler.arrayed ? GL_SAMPLER_1D_ARRAY_SHADOW : GL_SAMPLER_1D_SHADOW; + } + case Esd2D: + switch ((int)sampler.ms) { + case false: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_SAMPLER_2D_ARRAY : GL_SAMPLER_2D; + case true: return sampler.arrayed ? GL_SAMPLER_2D_ARRAY_SHADOW : GL_SAMPLER_2D_SHADOW; + } + case true: return sampler.arrayed ? GL_SAMPLER_2D_MULTISAMPLE_ARRAY : GL_SAMPLER_2D_MULTISAMPLE; + } + case Esd3D: + return GL_SAMPLER_3D; + case EsdCube: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_SAMPLER_CUBE_MAP_ARRAY : GL_SAMPLER_CUBE; + case true: return sampler.arrayed ? GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW : GL_SAMPLER_CUBE_SHADOW; + } + case EsdRect: + return sampler.shadow ? GL_SAMPLER_2D_RECT_SHADOW : GL_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_SAMPLER_BUFFER; + } + case EbtFloat16: + switch ((int)sampler.dim) { + case Esd1D: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_1D_ARRAY_AMD : GL_FLOAT16_SAMPLER_1D_AMD; + case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_1D_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_1D_SHADOW_AMD; + } + case Esd2D: + switch ((int)sampler.ms) { + case false: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_ARRAY_AMD : GL_FLOAT16_SAMPLER_2D_AMD; + case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_2D_SHADOW_AMD; + } + case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_ARRAY_AMD : GL_FLOAT16_SAMPLER_2D_MULTISAMPLE_AMD; + } + case Esd3D: + return GL_FLOAT16_SAMPLER_3D_AMD; + case EsdCube: + switch ((int)sampler.shadow) { + case false: return sampler.arrayed ? GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_AMD : GL_FLOAT16_SAMPLER_CUBE_AMD; + case true: return sampler.arrayed ? GL_FLOAT16_SAMPLER_CUBE_MAP_ARRAY_SHADOW_AMD : GL_FLOAT16_SAMPLER_CUBE_SHADOW_AMD; + } + case EsdRect: + return sampler.shadow ? GL_FLOAT16_SAMPLER_2D_RECT_SHADOW_AMD : GL_FLOAT16_SAMPLER_2D_RECT_AMD; + case EsdBuffer: + return GL_FLOAT16_SAMPLER_BUFFER_AMD; + } + case EbtInt: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_INT_SAMPLER_1D_ARRAY : GL_INT_SAMPLER_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_INT_SAMPLER_2D_ARRAY : GL_INT_SAMPLER_2D; + case true: return sampler.arrayed ? GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY + : GL_INT_SAMPLER_2D_MULTISAMPLE; + } + case Esd3D: + return GL_INT_SAMPLER_3D; + case EsdCube: + return sampler.arrayed ? GL_INT_SAMPLER_CUBE_MAP_ARRAY : GL_INT_SAMPLER_CUBE; + case EsdRect: + return GL_INT_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_INT_SAMPLER_BUFFER; + } + case EbtUint: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_1D_ARRAY : GL_UNSIGNED_INT_SAMPLER_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_2D_ARRAY : GL_UNSIGNED_INT_SAMPLER_2D; + case true: return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY + : GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE; + } + case Esd3D: + return GL_UNSIGNED_INT_SAMPLER_3D; + case EsdCube: + return sampler.arrayed ? GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY : GL_UNSIGNED_INT_SAMPLER_CUBE; + case EsdRect: + return GL_UNSIGNED_INT_SAMPLER_2D_RECT; + case EsdBuffer: + return GL_UNSIGNED_INT_SAMPLER_BUFFER; + } + default: + return 0; + } + } else { + // an image... + switch (sampler.type) { + case EbtFloat: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_IMAGE_1D_ARRAY : GL_IMAGE_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_IMAGE_2D_ARRAY : GL_IMAGE_2D; + case true: return sampler.arrayed ? GL_IMAGE_2D_MULTISAMPLE_ARRAY : GL_IMAGE_2D_MULTISAMPLE; + } + case Esd3D: + return GL_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_IMAGE_CUBE_MAP_ARRAY : GL_IMAGE_CUBE; + case EsdRect: + return GL_IMAGE_2D_RECT; + case EsdBuffer: + return GL_IMAGE_BUFFER; + } + case EbtFloat16: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_FLOAT16_IMAGE_1D_ARRAY_AMD : GL_FLOAT16_IMAGE_1D_AMD; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_FLOAT16_IMAGE_2D_ARRAY_AMD : GL_FLOAT16_IMAGE_2D_AMD; + case true: return sampler.arrayed ? GL_FLOAT16_IMAGE_2D_MULTISAMPLE_ARRAY_AMD : GL_FLOAT16_IMAGE_2D_MULTISAMPLE_AMD; + } + case Esd3D: + return GL_FLOAT16_IMAGE_3D_AMD; + case EsdCube: + return sampler.arrayed ? GL_FLOAT16_IMAGE_CUBE_MAP_ARRAY_AMD : GL_FLOAT16_IMAGE_CUBE_AMD; + case EsdRect: + return GL_FLOAT16_IMAGE_2D_RECT_AMD; + case EsdBuffer: + return GL_FLOAT16_IMAGE_BUFFER_AMD; + } + case EbtInt: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_INT_IMAGE_1D_ARRAY : GL_INT_IMAGE_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_INT_IMAGE_2D_ARRAY : GL_INT_IMAGE_2D; + case true: return sampler.arrayed ? GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY : GL_INT_IMAGE_2D_MULTISAMPLE; + } + case Esd3D: + return GL_INT_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_INT_IMAGE_CUBE_MAP_ARRAY : GL_INT_IMAGE_CUBE; + case EsdRect: + return GL_INT_IMAGE_2D_RECT; + case EsdBuffer: + return GL_INT_IMAGE_BUFFER; + } + case EbtUint: + switch ((int)sampler.dim) { + case Esd1D: + return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_1D_ARRAY : GL_UNSIGNED_INT_IMAGE_1D; + case Esd2D: + switch ((int)sampler.ms) { + case false: return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_2D_ARRAY : GL_UNSIGNED_INT_IMAGE_2D; + case true: return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY + : GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE; + } + case Esd3D: + return GL_UNSIGNED_INT_IMAGE_3D; + case EsdCube: + return sampler.arrayed ? GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY : GL_UNSIGNED_INT_IMAGE_CUBE; + case EsdRect: + return GL_UNSIGNED_INT_IMAGE_2D_RECT; + case EsdBuffer: + return GL_UNSIGNED_INT_IMAGE_BUFFER; + } + default: + return 0; + } + } + } + + // + // Translate a glslang type into the GL API #define number. + // Ignores arrayness. + // + int mapToGlType(const TType& type) + { + switch (type.getBasicType()) { + case EbtSampler: + return mapSamplerToGlType(type.getSampler()); + case EbtStruct: + case EbtBlock: + case EbtVoid: + return 0; + default: + break; + } + + if (type.isVector()) { + int offset = type.getVectorSize() - 2; + switch (type.getBasicType()) { + case EbtFloat: return GL_FLOAT_VEC2 + offset; + case EbtDouble: return GL_DOUBLE_VEC2 + offset; + case EbtFloat16: return GL_FLOAT16_VEC2_NV + offset; + case EbtInt: return GL_INT_VEC2 + offset; + case EbtUint: return GL_UNSIGNED_INT_VEC2 + offset; + case EbtInt64: return GL_INT64_ARB + offset; + case EbtUint64: return GL_UNSIGNED_INT64_ARB + offset; + case EbtBool: return GL_BOOL_VEC2 + offset; + case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER + offset; + default: return 0; + } + } + if (type.isMatrix()) { + switch (type.getBasicType()) { + case EbtFloat: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT2; + case 3: return GL_FLOAT_MAT2x3; + case 4: return GL_FLOAT_MAT2x4; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT3x2; + case 3: return GL_FLOAT_MAT3; + case 4: return GL_FLOAT_MAT3x4; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT_MAT4x2; + case 3: return GL_FLOAT_MAT4x3; + case 4: return GL_FLOAT_MAT4; + default: return 0; + } + } + case EbtDouble: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT2; + case 3: return GL_DOUBLE_MAT2x3; + case 4: return GL_DOUBLE_MAT2x4; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT3x2; + case 3: return GL_DOUBLE_MAT3; + case 4: return GL_DOUBLE_MAT3x4; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_DOUBLE_MAT4x2; + case 3: return GL_DOUBLE_MAT4x3; + case 4: return GL_DOUBLE_MAT4; + default: return 0; + } + } + case EbtFloat16: + switch (type.getMatrixCols()) { + case 2: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT2_AMD; + case 3: return GL_FLOAT16_MAT2x3_AMD; + case 4: return GL_FLOAT16_MAT2x4_AMD; + default: return 0; + } + case 3: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT3x2_AMD; + case 3: return GL_FLOAT16_MAT3_AMD; + case 4: return GL_FLOAT16_MAT3x4_AMD; + default: return 0; + } + case 4: + switch (type.getMatrixRows()) { + case 2: return GL_FLOAT16_MAT4x2_AMD; + case 3: return GL_FLOAT16_MAT4x3_AMD; + case 4: return GL_FLOAT16_MAT4_AMD; + default: return 0; + } + } + default: + return 0; + } + } + if (type.getVectorSize() == 1) { + switch (type.getBasicType()) { + case EbtFloat: return GL_FLOAT; + case EbtDouble: return GL_DOUBLE; + case EbtFloat16: return GL_FLOAT16_NV; + case EbtInt: return GL_INT; + case EbtUint: return GL_UNSIGNED_INT; + case EbtInt64: return GL_INT64_ARB; + case EbtUint64: return GL_UNSIGNED_INT64_ARB; + case EbtBool: return GL_BOOL; + case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER; + default: return 0; + } + } + + return 0; + } + + int mapToGlArraySize(const TType& type) + { + return type.isArray() ? type.getOuterArraySize() : 1; + } + + const TIntermediate& intermediate; + TReflection& reflection; + std::set processedDerefs; + bool updateStageMasks; + +protected: + TReflectionTraverser(TReflectionTraverser&); + TReflectionTraverser& operator=(TReflectionTraverser&); +}; + +// +// Implement the traversal functions of interest. +// + +// To catch dereferenced aggregates that must be reflected. +// This catches them at the highest level possible in the tree. +bool TReflectionTraverser::visitBinary(TVisit /* visit */, TIntermBinary* node) +{ + switch (node->getOp()) { + case EOpIndexDirect: + case EOpIndexIndirect: + case EOpIndexDirectStruct: + addDereferencedUniform(node); + break; + default: + break; + } + + // still need to visit everything below, which could contain sub-expressions + // containing different uniforms + return true; +} + +// To reflect non-dereferenced objects. +void TReflectionTraverser::visitSymbol(TIntermSymbol* base) +{ + if (base->getQualifier().storage == EvqUniform) { + if (base->getBasicType() == EbtBlock) { + if (reflection.options & EShReflectionSharedStd140UBO) { + addUniform(*base); + } + } else { + addUniform(*base); + } + } + + // #TODO add std140/layout active rules for ssbo, same with ubo. + // Storage buffer blocks will be collected and expanding in this part. + if((reflection.options & EShReflectionSharedStd140SSBO) && + (base->getQualifier().storage == EvqBuffer && base->getBasicType() == EbtBlock && + (base->getQualifier().layoutPacking == ElpStd140 || base->getQualifier().layoutPacking == ElpShared))) + addUniform(*base); + + if ((intermediate.getStage() == reflection.firstStage && base->getQualifier().isPipeInput()) || + (intermediate.getStage() == reflection.lastStage && base->getQualifier().isPipeOutput())) + addPipeIOVariable(*base); +} + +// +// Implement TObjectReflection methods. +// + +TObjectReflection::TObjectReflection(const std::string &pName, const TType &pType, int pOffset, int pGLDefineType, + int pSize, int pIndex) + : name(pName), offset(pOffset), glDefineType(pGLDefineType), size(pSize), index(pIndex), counterIndex(-1), + numMembers(-1), arrayStride(0), topLevelArrayStride(0), stages(EShLanguageMask(0)), type(pType.clone()) +{ +} + +int TObjectReflection::getBinding() const +{ + if (type == nullptr || !type->getQualifier().hasBinding()) + return -1; + return type->getQualifier().layoutBinding; +} + +void TObjectReflection::dump() const +{ + printf("%s: offset %d, type %x, size %d, index %d, binding %d, stages %d", name.c_str(), offset, glDefineType, size, + index, getBinding(), stages); + + if (counterIndex != -1) + printf(", counter %d", counterIndex); + + if (numMembers != -1) + printf(", numMembers %d", numMembers); + + if (arrayStride != 0) + printf(", arrayStride %d", arrayStride); + + if (topLevelArrayStride != 0) + printf(", topLevelArrayStride %d", topLevelArrayStride); + + printf("\n"); +} + +// +// Implement TReflection methods. +// + +// Track any required attribute reflection, such as compute shader numthreads. +// +void TReflection::buildAttributeReflection(EShLanguage stage, const TIntermediate& intermediate) +{ + if (stage == EShLangCompute) { + // Remember thread dimensions + for (int dim=0; dim<3; ++dim) + localSize[dim] = intermediate.getLocalSize(dim); + } +} + +// build counter block index associations for buffers +void TReflection::buildCounterIndices(const TIntermediate& intermediate) +{ +#ifdef ENABLE_HLSL + // search for ones that have counters + for (int i = 0; i < int(indexToUniformBlock.size()); ++i) { + const TString counterName(intermediate.addCounterBufferName(indexToUniformBlock[i].name).c_str()); + const int index = getIndex(counterName); + + if (index >= 0) + indexToUniformBlock[i].counterIndex = index; + } +#endif +} + +// build Shader Stages mask for all uniforms +void TReflection::buildUniformStageMask(const TIntermediate& intermediate) +{ + if (options & EShReflectionAllBlockVariables) + return; + + for (int i = 0; i < int(indexToUniform.size()); ++i) { + indexToUniform[i].stages = static_cast(indexToUniform[i].stages | 1 << intermediate.getStage()); + } + + for (int i = 0; i < int(indexToBufferVariable.size()); ++i) { + indexToBufferVariable[i].stages = + static_cast(indexToBufferVariable[i].stages | 1 << intermediate.getStage()); + } +} + +// Merge live symbols from 'intermediate' into the existing reflection database. +// +// Returns false if the input is too malformed to do this. +bool TReflection::addStage(EShLanguage stage, const TIntermediate& intermediate) +{ + if (intermediate.getTreeRoot() == nullptr || + intermediate.getNumEntryPoints() != 1 || + intermediate.isRecursive()) + return false; + + buildAttributeReflection(stage, intermediate); + + TReflectionTraverser it(intermediate, *this); + + for (auto& sequnence : intermediate.getTreeRoot()->getAsAggregate()->getSequence()) { + if (sequnence->getAsAggregate() != nullptr) { + if (sequnence->getAsAggregate()->getOp() == glslang::EOpLinkerObjects) { + it.updateStageMasks = false; + TIntermAggregate* linkerObjects = sequnence->getAsAggregate(); + for (auto& sequnence : linkerObjects->getSequence()) { + auto pNode = sequnence->getAsSymbolNode(); + if (pNode != nullptr) { + if ((pNode->getQualifier().storage == EvqUniform && + (options & EShReflectionSharedStd140UBO)) || + (pNode->getQualifier().storage == EvqBuffer && + (options & EShReflectionSharedStd140SSBO))) { + // collect std140 and shared uniform block form AST + if ((pNode->getBasicType() == EbtBlock) && + ((pNode->getQualifier().layoutPacking == ElpStd140) || + (pNode->getQualifier().layoutPacking == ElpShared))) { + pNode->traverse(&it); + } + } + else if ((options & EShReflectionAllIOVariables) && + (pNode->getQualifier().isPipeInput() || pNode->getQualifier().isPipeOutput())) + { + pNode->traverse(&it); + } + } + } + } else { + // This traverser will travers all function in AST. + // If we want reflect uncalled function, we need set linke message EShMsgKeepUncalled. + // When EShMsgKeepUncalled been set to true, all function will be keep in AST, even it is a uncalled function. + // This will keep some uniform variables in reflection, if those uniform variables is used in these uncalled function. + // + // If we just want reflect only live node, we can use a default link message or set EShMsgKeepUncalled false. + // When linke message not been set EShMsgKeepUncalled, linker won't keep uncalled function in AST. + // So, travers all function node can equivalent to travers live function. + it.updateStageMasks = true; + sequnence->getAsAggregate()->traverse(&it); + } + } + } + it.updateStageMasks = true; + + buildCounterIndices(intermediate); + buildUniformStageMask(intermediate); + + return true; +} + +void TReflection::dump() +{ + printf("Uniform reflection:\n"); + for (size_t i = 0; i < indexToUniform.size(); ++i) + indexToUniform[i].dump(); + printf("\n"); + + printf("Uniform block reflection:\n"); + for (size_t i = 0; i < indexToUniformBlock.size(); ++i) + indexToUniformBlock[i].dump(); + printf("\n"); + + printf("Buffer variable reflection:\n"); + for (size_t i = 0; i < indexToBufferVariable.size(); ++i) + indexToBufferVariable[i].dump(); + printf("\n"); + + printf("Buffer block reflection:\n"); + for (size_t i = 0; i < indexToBufferBlock.size(); ++i) + indexToBufferBlock[i].dump(); + printf("\n"); + + printf("Pipeline input reflection:\n"); + for (size_t i = 0; i < indexToPipeInput.size(); ++i) + indexToPipeInput[i].dump(); + printf("\n"); + + printf("Pipeline output reflection:\n"); + for (size_t i = 0; i < indexToPipeOutput.size(); ++i) + indexToPipeOutput[i].dump(); + printf("\n"); + + if (getLocalSize(0) > 1) { + static const char* axis[] = { "X", "Y", "Z" }; + + for (int dim=0; dim<3; ++dim) + if (getLocalSize(dim) > 1) + printf("Local size %s: %u\n", axis[dim], getLocalSize(dim)); + + printf("\n"); + } + + // printf("Live names\n"); + // for (TNameToIndex::const_iterator it = nameToIndex.begin(); it != nameToIndex.end(); ++it) + // printf("%s: %d\n", it->first.c_str(), it->second); + // printf("\n"); +} + +} // end namespace glslang + +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE diff --git a/third_party/glslang/glslang/MachineIndependent/reflection.h b/third_party/glslang/glslang/MachineIndependent/reflection.h new file mode 100644 index 0000000..5af4467 --- /dev/null +++ b/third_party/glslang/glslang/MachineIndependent/reflection.h @@ -0,0 +1,223 @@ +// +// Copyright (C) 2013-2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +#ifndef _REFLECTION_INCLUDED +#define _REFLECTION_INCLUDED + +#include "../Public/ShaderLang.h" +#include "../Include/Types.h" + +#include +#include + +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +namespace glslang { + +class TIntermediate; +class TIntermAggregate; +class TReflectionTraverser; + +// The full reflection database +class TReflection { +public: + TReflection(EShReflectionOptions opts, EShLanguage first, EShLanguage last) + : options(opts), firstStage(first), lastStage(last), badReflection(TObjectReflection::badReflection()) + { + for (int dim=0; dim<3; ++dim) + localSize[dim] = 0; + } + + virtual ~TReflection() {} + + // grow the reflection stage by stage + bool addStage(EShLanguage, const TIntermediate&); + + // for mapping a uniform index to a uniform object's description + int getNumUniforms() { return (int)indexToUniform.size(); } + const TObjectReflection& getUniform(int i) const + { + if (i >= 0 && i < (int)indexToUniform.size()) + return indexToUniform[i]; + else + return badReflection; + } + + // for mapping a block index to the block's description + int getNumUniformBlocks() const { return (int)indexToUniformBlock.size(); } + const TObjectReflection& getUniformBlock(int i) const + { + if (i >= 0 && i < (int)indexToUniformBlock.size()) + return indexToUniformBlock[i]; + else + return badReflection; + } + + // for mapping an pipeline input index to the input's description + int getNumPipeInputs() { return (int)indexToPipeInput.size(); } + const TObjectReflection& getPipeInput(int i) const + { + if (i >= 0 && i < (int)indexToPipeInput.size()) + return indexToPipeInput[i]; + else + return badReflection; + } + + // for mapping an pipeline output index to the output's description + int getNumPipeOutputs() { return (int)indexToPipeOutput.size(); } + const TObjectReflection& getPipeOutput(int i) const + { + if (i >= 0 && i < (int)indexToPipeOutput.size()) + return indexToPipeOutput[i]; + else + return badReflection; + } + + // for mapping from an atomic counter to the uniform index + int getNumAtomicCounters() const { return (int)atomicCounterUniformIndices.size(); } + const TObjectReflection& getAtomicCounter(int i) const + { + if (i >= 0 && i < (int)atomicCounterUniformIndices.size()) + return getUniform(atomicCounterUniformIndices[i]); + else + return badReflection; + } + + // for mapping a buffer variable index to a buffer variable object's description + int getNumBufferVariables() { return (int)indexToBufferVariable.size(); } + const TObjectReflection& getBufferVariable(int i) const + { + if (i >= 0 && i < (int)indexToBufferVariable.size()) + return indexToBufferVariable[i]; + else + return badReflection; + } + + // for mapping a storage block index to the storage block's description + int getNumStorageBuffers() const { return (int)indexToBufferBlock.size(); } + const TObjectReflection& getStorageBufferBlock(int i) const + { + if (i >= 0 && i < (int)indexToBufferBlock.size()) + return indexToBufferBlock[i]; + else + return badReflection; + } + + // for mapping any name to its index (block names, uniform names and input/output names) + int getIndex(const char* name) const + { + TNameToIndex::const_iterator it = nameToIndex.find(name); + if (it == nameToIndex.end()) + return -1; + else + return it->second; + } + + // see getIndex(const char*) + int getIndex(const TString& name) const { return getIndex(name.c_str()); } + + + // for mapping any name to its index (only pipe input/output names) + int getPipeIOIndex(const char* name, const bool inOrOut) const + { + TNameToIndex::const_iterator it = inOrOut ? pipeInNameToIndex.find(name) : pipeOutNameToIndex.find(name); + if (it == (inOrOut ? pipeInNameToIndex.end() : pipeOutNameToIndex.end())) + return -1; + else + return it->second; + } + + // see gePipeIOIndex(const char*, const bool) + int getPipeIOIndex(const TString& name, const bool inOrOut) const { return getPipeIOIndex(name.c_str(), inOrOut); } + + // Thread local size + unsigned getLocalSize(int dim) const { return dim <= 2 ? localSize[dim] : 0; } + + void dump(); + +protected: + friend class glslang::TReflectionTraverser; + + void buildCounterIndices(const TIntermediate&); + void buildUniformStageMask(const TIntermediate& intermediate); + void buildAttributeReflection(EShLanguage, const TIntermediate&); + + // Need a TString hash: typedef std::unordered_map TNameToIndex; + typedef std::map TNameToIndex; + typedef std::vector TMapIndexToReflection; + typedef std::vector TIndices; + + TMapIndexToReflection& GetBlockMapForStorage(TStorageQualifier storage) + { + if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer) + return indexToBufferBlock; + return indexToUniformBlock; + } + TMapIndexToReflection& GetVariableMapForStorage(TStorageQualifier storage) + { + if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer) + return indexToBufferVariable; + return indexToUniform; + } + + EShReflectionOptions options; + + EShLanguage firstStage; + EShLanguage lastStage; + + TObjectReflection badReflection; // return for queries of -1 or generally out of range; has expected descriptions with in it for this + TNameToIndex nameToIndex; // maps names to indexes; can hold all types of data: uniform/buffer and which function names have been processed + TNameToIndex pipeInNameToIndex; // maps pipe in names to indexes, this is a fix to seperate pipe I/O from uniforms and buffers. + TNameToIndex pipeOutNameToIndex; // maps pipe out names to indexes, this is a fix to seperate pipe I/O from uniforms and buffers. + TMapIndexToReflection indexToUniform; + TMapIndexToReflection indexToUniformBlock; + TMapIndexToReflection indexToBufferVariable; + TMapIndexToReflection indexToBufferBlock; + TMapIndexToReflection indexToPipeInput; + TMapIndexToReflection indexToPipeOutput; + TIndices atomicCounterUniformIndices; + + unsigned int localSize[3]; +}; + +} // end namespace glslang + +#endif // _REFLECTION_INCLUDED + +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE diff --git a/third_party/glslang/glslang/OSDependent/Unix/CMakeLists.txt b/third_party/glslang/glslang/OSDependent/Unix/CMakeLists.txt new file mode 100644 index 0000000..354a3e9 --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Unix/CMakeLists.txt @@ -0,0 +1,59 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +add_library(OSDependent STATIC ossource.cpp ../osinclude.h) +set_property(TARGET OSDependent PROPERTY FOLDER glslang) +set_property(TARGET OSDependent PROPERTY POSITION_INDEPENDENT_CODE ON) + +# Link pthread +set(CMAKE_THREAD_PREFER_PTHREAD ON) +if(${CMAKE_VERSION} VERSION_LESS "3.1.0" OR CMAKE_CROSSCOMPILING) + # Needed as long as we support CMake 2.8 for Ubuntu 14.04, + # which does not support the recommended Threads::Threads target. + # https://cmake.org/cmake/help/v2.8.12/cmake.html#module:FindThreads + # Also needed when cross-compiling to work around + # https://gitlab.kitware.com/cmake/cmake/issues/16920 + find_package(Threads) + target_link_libraries(OSDependent ${CMAKE_THREAD_LIBS_INIT}) +else() + # This is the recommended way, so we use it for 3.1+. + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads) + target_link_libraries(OSDependent Threads::Threads) +endif() + +if(ENABLE_GLSLANG_INSTALL) + install(TARGETS OSDependent EXPORT OSDependentTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(EXPORT OSDependentTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/glslang/OSDependent/Unix/ossource.cpp b/third_party/glslang/glslang/OSDependent/Unix/ossource.cpp new file mode 100644 index 0000000..3f029f0 --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Unix/ossource.cpp @@ -0,0 +1,207 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// +// This file contains the Linux-specific functions +// +#include "../osinclude.h" +#include "../../../OGLCompilersDLL/InitializeDll.h" + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__Fuchsia__) +#include +#endif + +namespace glslang { + +// +// Thread cleanup +// + +// +// Wrapper for Linux call to DetachThread. This is required as pthread_cleanup_push() expects +// the cleanup routine to return void. +// +static void DetachThreadLinux(void *) +{ + DetachThread(); +} + +// +// Registers cleanup handler, sets cancel type and state, and executes the thread specific +// cleanup handler. This function will be called in the Standalone.cpp for regression +// testing. When OpenGL applications are run with the driver code, Linux OS does the +// thread cleanup. +// +void OS_CleanupThreadData(void) +{ +#if defined(__ANDROID__) || defined(__Fuchsia__) + DetachThreadLinux(NULL); +#else + int old_cancel_state, old_cancel_type; + void *cleanupArg = NULL; + + // + // Set thread cancel state and push cleanup handler. + // + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old_cancel_state); + pthread_cleanup_push(DetachThreadLinux, (void *) cleanupArg); + + // + // Put the thread in deferred cancellation mode. + // + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old_cancel_type); + + // + // Pop cleanup handler and execute it prior to unregistering the cleanup handler. + // + pthread_cleanup_pop(1); + + // + // Restore the thread's previous cancellation mode. + // + pthread_setcanceltype(old_cancel_state, NULL); +#endif +} + +// +// Thread Local Storage Operations +// +inline OS_TLSIndex PthreadKeyToTLSIndex(pthread_key_t key) +{ + return (OS_TLSIndex)((uintptr_t)key + 1); +} + +inline pthread_key_t TLSIndexToPthreadKey(OS_TLSIndex nIndex) +{ + return (pthread_key_t)((uintptr_t)nIndex - 1); +} + +OS_TLSIndex OS_AllocTLSIndex() +{ + pthread_key_t pPoolIndex; + + // + // Create global pool key. + // + if ((pthread_key_create(&pPoolIndex, NULL)) != 0) { + assert(0 && "OS_AllocTLSIndex(): Unable to allocate Thread Local Storage"); + return OS_INVALID_TLS_INDEX; + } + else + return PthreadKeyToTLSIndex(pPoolIndex); +} + +bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue) +{ + if (nIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); + return false; + } + + if (pthread_setspecific(TLSIndexToPthreadKey(nIndex), lpvValue) == 0) + return true; + else + return false; +} + +void* OS_GetTLSValue(OS_TLSIndex nIndex) +{ + // + // This function should return 0 if nIndex is invalid. + // + assert(nIndex != OS_INVALID_TLS_INDEX); + return pthread_getspecific(TLSIndexToPthreadKey(nIndex)); +} + +bool OS_FreeTLSIndex(OS_TLSIndex nIndex) +{ + if (nIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); + return false; + } + + // + // Delete the global pool key. + // + if (pthread_key_delete(TLSIndexToPthreadKey(nIndex)) == 0) + return true; + else + return false; +} + +namespace { + pthread_mutex_t gMutex; +} + +void InitGlobalLock() +{ + pthread_mutexattr_t mutexattr; + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&gMutex, &mutexattr); +} + +void GetGlobalLock() +{ + pthread_mutex_lock(&gMutex); +} + +void ReleaseGlobalLock() +{ + pthread_mutex_unlock(&gMutex); +} + +// #define DUMP_COUNTERS + +void OS_DumpMemoryCounters() +{ +#ifdef DUMP_COUNTERS + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage) == 0) + printf("Working set size: %ld\n", usage.ru_maxrss * 1024); +#else + printf("Recompile with DUMP_COUNTERS defined to see counters.\n"); +#endif +} + +} // end namespace glslang diff --git a/third_party/glslang/glslang/OSDependent/Web/CMakeLists.txt b/third_party/glslang/glslang/OSDependent/Web/CMakeLists.txt new file mode 100644 index 0000000..0f60dbc --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Web/CMakeLists.txt @@ -0,0 +1,71 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +if(ENABLE_GLSLANG_JS) + add_executable(glslang.js "glslang.js.cpp") + glslang_set_link_args(glslang.js) + target_link_libraries(glslang.js glslang SPIRV) + + # Link library names that start with "-" are treated as link flags. + # "-Os" should be OK in MSVC; don't use /Os because CMake won't + # treat it as a link flag. + target_link_libraries(glslang.js "-Os") + + if(EMSCRIPTEN) + set_target_properties(glslang.js PROPERTIES + OUTPUT_NAME "glslang" + SUFFIX ".js") + em_link_pre_js(glslang.js "${CMAKE_CURRENT_SOURCE_DIR}/glslang.pre.js") + + target_link_libraries(glslang.js "--llvm-lto 1") + target_link_libraries(glslang.js "--closure 1") + target_link_libraries(glslang.js "-s MODULARIZE=1") + target_link_libraries(glslang.js "-s ALLOW_MEMORY_GROWTH=1") + target_link_libraries(glslang.js "-s FILESYSTEM=0") + + if(ENABLE_EMSCRIPTEN_SINGLE_FILE) + target_link_libraries(glslang.js "-s SINGLE_FILE=1") + endif(ENABLE_EMSCRIPTEN_SINGLE_FILE) + + if(ENABLE_EMSCRIPTEN_ENVIRONMENT_NODE) + target_link_libraries(glslang.js "-s ENVIRONMENT=node -s BINARYEN_ASYNC_COMPILATION=0") + else() + target_link_libraries(glslang.js "-s ENVIRONMENT=web,worker") + endif() + + if(NOT ENABLE_EMSCRIPTEN_ENVIRONMENT_NODE) + add_custom_command(TARGET glslang.js POST_BUILD + COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/glslang.after.js >> ${CMAKE_CURRENT_BINARY_DIR}/glslang.js) + endif() + endif(EMSCRIPTEN) +endif(ENABLE_GLSLANG_JS) diff --git a/third_party/glslang/glslang/OSDependent/Web/glslang.after.js b/third_party/glslang/glslang/OSDependent/Web/glslang.after.js new file mode 100644 index 0000000..c2cfc35 --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Web/glslang.after.js @@ -0,0 +1,26 @@ +export default (() => { + const initialize = () => { + return new Promise(resolve => { + Module({ + locateFile() { + const i = import.meta.url.lastIndexOf('/') + return import.meta.url.substring(0, i) + '/glslang.wasm'; + }, + onRuntimeInitialized() { + resolve({ + compileGLSLZeroCopy: this.compileGLSLZeroCopy, + compileGLSL: this.compileGLSL, + }); + }, + }); + }); + }; + + let instance; + return () => { + if (!instance) { + instance = initialize(); + } + return instance; + }; +})(); diff --git a/third_party/glslang/glslang/OSDependent/Web/glslang.js.cpp b/third_party/glslang/glslang/OSDependent/Web/glslang.js.cpp new file mode 100644 index 0000000..f2306a6 --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Web/glslang.js.cpp @@ -0,0 +1,287 @@ +// +// Copyright (C) 2019 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include +#include +#include + +#ifdef __EMSCRIPTEN__ +#include +#endif + +#include "../../../SPIRV/GlslangToSpv.h" +#include "../../../glslang/Public/ShaderLang.h" + +#ifndef __EMSCRIPTEN__ +#define EMSCRIPTEN_KEEPALIVE +#endif + +const TBuiltInResource DefaultTBuiltInResource = { + /* .MaxLights = */ 32, + /* .MaxClipPlanes = */ 6, + /* .MaxTextureUnits = */ 32, + /* .MaxTextureCoords = */ 32, + /* .MaxVertexAttribs = */ 64, + /* .MaxVertexUniformComponents = */ 4096, + /* .MaxVaryingFloats = */ 64, + /* .MaxVertexTextureImageUnits = */ 32, + /* .MaxCombinedTextureImageUnits = */ 80, + /* .MaxTextureImageUnits = */ 32, + /* .MaxFragmentUniformComponents = */ 4096, + /* .MaxDrawBuffers = */ 32, + /* .MaxVertexUniformVectors = */ 128, + /* .MaxVaryingVectors = */ 8, + /* .MaxFragmentUniformVectors = */ 16, + /* .MaxVertexOutputVectors = */ 16, + /* .MaxFragmentInputVectors = */ 15, + /* .MinProgramTexelOffset = */ -8, + /* .MaxProgramTexelOffset = */ 7, + /* .MaxClipDistances = */ 8, + /* .MaxComputeWorkGroupCountX = */ 65535, + /* .MaxComputeWorkGroupCountY = */ 65535, + /* .MaxComputeWorkGroupCountZ = */ 65535, + /* .MaxComputeWorkGroupSizeX = */ 1024, + /* .MaxComputeWorkGroupSizeY = */ 1024, + /* .MaxComputeWorkGroupSizeZ = */ 64, + /* .MaxComputeUniformComponents = */ 1024, + /* .MaxComputeTextureImageUnits = */ 16, + /* .MaxComputeImageUniforms = */ 8, + /* .MaxComputeAtomicCounters = */ 8, + /* .MaxComputeAtomicCounterBuffers = */ 1, + /* .MaxVaryingComponents = */ 60, + /* .MaxVertexOutputComponents = */ 64, + /* .MaxGeometryInputComponents = */ 64, + /* .MaxGeometryOutputComponents = */ 128, + /* .MaxFragmentInputComponents = */ 128, + /* .MaxImageUnits = */ 8, + /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8, + /* .MaxCombinedShaderOutputResources = */ 8, + /* .MaxImageSamples = */ 0, + /* .MaxVertexImageUniforms = */ 0, + /* .MaxTessControlImageUniforms = */ 0, + /* .MaxTessEvaluationImageUniforms = */ 0, + /* .MaxGeometryImageUniforms = */ 0, + /* .MaxFragmentImageUniforms = */ 8, + /* .MaxCombinedImageUniforms = */ 8, + /* .MaxGeometryTextureImageUnits = */ 16, + /* .MaxGeometryOutputVertices = */ 256, + /* .MaxGeometryTotalOutputComponents = */ 1024, + /* .MaxGeometryUniformComponents = */ 1024, + /* .MaxGeometryVaryingComponents = */ 64, + /* .MaxTessControlInputComponents = */ 128, + /* .MaxTessControlOutputComponents = */ 128, + /* .MaxTessControlTextureImageUnits = */ 16, + /* .MaxTessControlUniformComponents = */ 1024, + /* .MaxTessControlTotalOutputComponents = */ 4096, + /* .MaxTessEvaluationInputComponents = */ 128, + /* .MaxTessEvaluationOutputComponents = */ 128, + /* .MaxTessEvaluationTextureImageUnits = */ 16, + /* .MaxTessEvaluationUniformComponents = */ 1024, + /* .MaxTessPatchComponents = */ 120, + /* .MaxPatchVertices = */ 32, + /* .MaxTessGenLevel = */ 64, + /* .MaxViewports = */ 16, + /* .MaxVertexAtomicCounters = */ 0, + /* .MaxTessControlAtomicCounters = */ 0, + /* .MaxTessEvaluationAtomicCounters = */ 0, + /* .MaxGeometryAtomicCounters = */ 0, + /* .MaxFragmentAtomicCounters = */ 8, + /* .MaxCombinedAtomicCounters = */ 8, + /* .MaxAtomicCounterBindings = */ 1, + /* .MaxVertexAtomicCounterBuffers = */ 0, + /* .MaxTessControlAtomicCounterBuffers = */ 0, + /* .MaxTessEvaluationAtomicCounterBuffers = */ 0, + /* .MaxGeometryAtomicCounterBuffers = */ 0, + /* .MaxFragmentAtomicCounterBuffers = */ 1, + /* .MaxCombinedAtomicCounterBuffers = */ 1, + /* .MaxAtomicCounterBufferSize = */ 16384, + /* .MaxTransformFeedbackBuffers = */ 4, + /* .MaxTransformFeedbackInterleavedComponents = */ 64, + /* .MaxCullDistances = */ 8, + /* .MaxCombinedClipAndCullDistances = */ 8, + /* .MaxSamples = */ 4, + /* .maxMeshOutputVerticesNV = */ 256, + /* .maxMeshOutputPrimitivesNV = */ 512, + /* .maxMeshWorkGroupSizeX_NV = */ 32, + /* .maxMeshWorkGroupSizeY_NV = */ 1, + /* .maxMeshWorkGroupSizeZ_NV = */ 1, + /* .maxTaskWorkGroupSizeX_NV = */ 32, + /* .maxTaskWorkGroupSizeY_NV = */ 1, + /* .maxTaskWorkGroupSizeZ_NV = */ 1, + /* .maxMeshViewCountNV = */ 4, + /* .maxDualSourceDrawBuffersEXT = */ 1, + + /* .limits = */ { + /* .nonInductiveForLoops = */ 1, + /* .whileLoops = */ 1, + /* .doWhileLoops = */ 1, + /* .generalUniformIndexing = */ 1, + /* .generalAttributeMatrixVectorIndexing = */ 1, + /* .generalVaryingIndexing = */ 1, + /* .generalSamplerIndexing = */ 1, + /* .generalVariableIndexing = */ 1, + /* .generalConstantMatrixVectorIndexing = */ 1, + }}; + +static bool initialized = false; + +extern "C" { + +/* + * Takes in a GLSL shader as a string and converts it to SPIR-V in binary form. + * + * |glsl| Null-terminated string containing the shader to be converted. + * |stage_int| Magic number indicating the type of shader being processed. +* Legal values are as follows: + * Vertex = 0 + * Fragment = 4 + * Compute = 5 + * |gen_debug| Flag to indicate if debug information should be generated. + * |spirv| Output parameter for a pointer to the resulting SPIR-V data. + * |spirv_len| Output parameter for the length of the output binary buffer. + * + * Returns a void* pointer which, if not null, must be destroyed by + * destroy_output_buffer.o. (This is not the same pointer returned in |spirv|.) + * If null, the compilation failed. + */ +EMSCRIPTEN_KEEPALIVE +void* convert_glsl_to_spirv(const char* glsl, + int stage_int, + bool gen_debug, + glslang::EShTargetLanguageVersion spirv_version, + uint32_t** spirv, + size_t* spirv_len) +{ + if (glsl == nullptr) { + fprintf(stderr, "Input pointer null\n"); + return nullptr; + } + if (spirv == nullptr || spirv_len == nullptr) { + fprintf(stderr, "Output pointer null\n"); + return nullptr; + } + *spirv = nullptr; + *spirv_len = 0; + + if (stage_int != 0 && stage_int != 4 && stage_int != 5) { + fprintf(stderr, "Invalid shader stage\n"); + return nullptr; + } + EShLanguage stage = static_cast(stage_int); + switch (spirv_version) { + case glslang::EShTargetSpv_1_0: + case glslang::EShTargetSpv_1_1: + case glslang::EShTargetSpv_1_2: + case glslang::EShTargetSpv_1_3: + case glslang::EShTargetSpv_1_4: + case glslang::EShTargetSpv_1_5: + break; + default: + fprintf(stderr, "Invalid SPIR-V version number\n"); + return nullptr; + } + + if (!initialized) { + glslang::InitializeProcess(); + initialized = true; + } + + glslang::TShader shader(stage); + shader.setStrings(&glsl, 1); + shader.setEnvInput(glslang::EShSourceGlsl, stage, glslang::EShClientVulkan, 100); + shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_0); + shader.setEnvTarget(glslang::EShTargetSpv, spirv_version); + if (!shader.parse(&DefaultTBuiltInResource, 100, true, EShMsgDefault)) { + fprintf(stderr, "Parse failed\n"); + fprintf(stderr, "%s\n", shader.getInfoLog()); + return nullptr; + } + + glslang::TProgram program; + program.addShader(&shader); + if (!program.link(EShMsgDefault)) { + fprintf(stderr, "Link failed\n"); + fprintf(stderr, "%s\n", program.getInfoLog()); + return nullptr; + } + + glslang::SpvOptions spvOptions; + spvOptions.generateDebugInfo = gen_debug; + spvOptions.optimizeSize = false; + spvOptions.disassemble = false; + spvOptions.validate = false; + + std::vector* output = new std::vector; + glslang::GlslangToSpv(*program.getIntermediate(stage), *output, nullptr, &spvOptions); + + *spirv_len = output->size(); + *spirv = output->data(); + return output; +} + +/* + * Destroys a buffer created by convert_glsl_to_spirv + */ +EMSCRIPTEN_KEEPALIVE +void destroy_output_buffer(void* p) +{ + delete static_cast*>(p); +} + +} // extern "C" + +/* + * For non-Emscripten builds we supply a generic main, so that the glslang.js + * build target can generate an executable with a trivial use case instead of + * generating a WASM binary. This is done so that there is a target that can be + * built and output analyzed using desktop tools, since WASM binaries are + * specific to the Emscripten toolchain. + */ +#ifndef __EMSCRIPTEN__ +int main() { + const char* input = R"(#version 310 es + +void main() { })"; + + uint32_t* output; + size_t output_len; + + void* id = convert_glsl_to_spirv(input, 4, false, glslang::EShTargetSpv_1_0, &output, &output_len); + assert(output != nullptr); + assert(output_len != 0); + destroy_output_buffer(id); + return 0; +} +#endif // ifndef __EMSCRIPTEN__ diff --git a/third_party/glslang/glslang/OSDependent/Web/glslang.pre.js b/third_party/glslang/glslang/OSDependent/Web/glslang.pre.js new file mode 100644 index 0000000..46a5695 --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Web/glslang.pre.js @@ -0,0 +1,56 @@ +Module['compileGLSLZeroCopy'] = function(glsl, shader_stage, gen_debug, spirv_version) { + gen_debug = !!gen_debug; + + var shader_stage_int; // EShLanguage + switch (shader_stage) { + case 'vertex': shader_stage_int = 0; break; + case 'fragment': shader_stage_int = 4; break; + case 'compute': shader_stage_int = 5; break; + default: + throw new Error("shader_stage must be 'vertex', 'fragment', or 'compute'."); + } + + spirv_version = spirv_version || '1.0'; + var spirv_version_int; // EShTargetLanguageVersion + switch (spirv_version) { + case '1.0': spirv_version_int = (1 << 16) | (0 << 8); break; + case '1.1': spirv_version_int = (1 << 16) | (1 << 8); break; + case '1.2': spirv_version_int = (1 << 16) | (2 << 8); break; + case '1.3': spirv_version_int = (1 << 16) | (3 << 8); break; + case '1.4': spirv_version_int = (1 << 16) | (4 << 8); break; + case '1.5': spirv_version_int = (1 << 16) | (5 << 8); break; + default: + throw new Error("spirv_version must be '1.0' ~ '1.5'."); + } + + var p_output = Module['_malloc'](4); + var p_output_len = Module['_malloc'](4); + var id = ccall('convert_glsl_to_spirv', + 'number', + ['string', 'number', 'boolean', 'number', 'number', 'number'], + [glsl, shader_stage_int, gen_debug, spirv_version_int, p_output, p_output_len]); + var output = getValue(p_output, 'i32'); + var output_len = getValue(p_output_len, 'i32'); + Module['_free'](p_output); + Module['_free'](p_output_len); + + if (id === 0) { + throw new Error('GLSL compilation failed'); + } + + var ret = {}; + var outputIndexU32 = output / 4; + ret['data'] = Module['HEAPU32'].subarray(outputIndexU32, outputIndexU32 + output_len); + ret['free'] = function() { + Module['_destroy_output_buffer'](id); + }; + + return ret; +}; + +Module['compileGLSL'] = function(glsl, shader_stage, gen_debug, spirv_version) { + var compiled = Module['compileGLSLZeroCopy'](glsl, shader_stage, gen_debug, spirv_version); + var ret = compiled['data'].slice() + compiled['free'](); + return ret; +}; diff --git a/third_party/glslang/glslang/OSDependent/Windows/CMakeLists.txt b/third_party/glslang/glslang/OSDependent/Windows/CMakeLists.txt new file mode 100644 index 0000000..9cf1b7f --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Windows/CMakeLists.txt @@ -0,0 +1,54 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set(SOURCES ossource.cpp ../osinclude.h) + +add_library(OSDependent STATIC ${SOURCES}) +set_property(TARGET OSDependent PROPERTY FOLDER glslang) +set_property(TARGET OSDependent PROPERTY POSITION_INDEPENDENT_CODE ON) + +# MinGW GCC complains about function pointer casts to void*. +# Turn that off with -fpermissive. +if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + target_compile_options(OSDependent PRIVATE -fpermissive) +endif() + +if(WIN32) + source_group("Source" FILES ${SOURCES}) +endif(WIN32) + +if(ENABLE_GLSLANG_INSTALL) + install(TARGETS OSDependent EXPORT OSDependentTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(EXPORT OSDependentTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/glslang/OSDependent/Windows/main.cpp b/third_party/glslang/glslang/OSDependent/Windows/main.cpp new file mode 100644 index 0000000..0bcde7b --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Windows/main.cpp @@ -0,0 +1,74 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "InitializeDll.h" + +#define STRICT +#define VC_EXTRALEAN 1 +#include +#include + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + + if (! glslang::InitProcess()) + return FALSE; + break; + case DLL_THREAD_ATTACH: + + if (! glslang::InitThread()) + return FALSE; + break; + + case DLL_THREAD_DETACH: + + if (! glslang::DetachThread()) + return FALSE; + break; + + case DLL_PROCESS_DETACH: + + glslang::DetachProcess(); + break; + + default: + assert(0 && "DllMain(): Reason for calling DLL Main is unknown"); + return FALSE; + } + + return TRUE; +} diff --git a/third_party/glslang/glslang/OSDependent/Windows/ossource.cpp b/third_party/glslang/glslang/OSDependent/Windows/ossource.cpp new file mode 100644 index 0000000..870840c --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/Windows/ossource.cpp @@ -0,0 +1,147 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "../osinclude.h" + +#define STRICT +#define VC_EXTRALEAN 1 +#include +#include +#include +#include +#include +#include + +// +// This file contains the Window-OS-specific functions +// + +#if !(defined(_WIN32) || defined(_WIN64)) +#error Trying to build a windows specific file in a non windows build. +#endif + +namespace glslang { + +inline OS_TLSIndex ToGenericTLSIndex (DWORD handle) +{ + return (OS_TLSIndex)((uintptr_t)handle + 1); +} + +inline DWORD ToNativeTLSIndex (OS_TLSIndex nIndex) +{ + return (DWORD)((uintptr_t)nIndex - 1); +} + +// +// Thread Local Storage Operations +// +OS_TLSIndex OS_AllocTLSIndex() +{ + DWORD dwIndex = TlsAlloc(); + if (dwIndex == TLS_OUT_OF_INDEXES) { + assert(0 && "OS_AllocTLSIndex(): Unable to allocate Thread Local Storage"); + return OS_INVALID_TLS_INDEX; + } + + return ToGenericTLSIndex(dwIndex); +} + +bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue) +{ + if (nIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); + return false; + } + + if (TlsSetValue(ToNativeTLSIndex(nIndex), lpvValue)) + return true; + else + return false; +} + +void* OS_GetTLSValue(OS_TLSIndex nIndex) +{ + assert(nIndex != OS_INVALID_TLS_INDEX); + return TlsGetValue(ToNativeTLSIndex(nIndex)); +} + +bool OS_FreeTLSIndex(OS_TLSIndex nIndex) +{ + if (nIndex == OS_INVALID_TLS_INDEX) { + assert(0 && "OS_SetTLSValue(): Invalid TLS Index"); + return false; + } + + if (TlsFree(ToNativeTLSIndex(nIndex))) + return true; + else + return false; +} + +HANDLE GlobalLock; + +void InitGlobalLock() +{ + GlobalLock = CreateMutex(0, false, 0); +} + +void GetGlobalLock() +{ + WaitForSingleObject(GlobalLock, INFINITE); +} + +void ReleaseGlobalLock() +{ + ReleaseMutex(GlobalLock); +} + +unsigned int __stdcall EnterGenericThread (void* entry) +{ + return ((TThreadEntrypoint)entry)(0); +} + +//#define DUMP_COUNTERS + +void OS_DumpMemoryCounters() +{ +#ifdef DUMP_COUNTERS + PROCESS_MEMORY_COUNTERS counters; + GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)); + printf("Working set size: %d\n", counters.WorkingSetSize); +#else + printf("Recompile with DUMP_COUNTERS defined to see counters.\n"); +#endif +} + +} // namespace glslang diff --git a/third_party/glslang/glslang/OSDependent/osinclude.h b/third_party/glslang/glslang/OSDependent/osinclude.h new file mode 100644 index 0000000..218abe4 --- /dev/null +++ b/third_party/glslang/glslang/OSDependent/osinclude.h @@ -0,0 +1,63 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef __OSINCLUDE_H +#define __OSINCLUDE_H + +namespace glslang { + +// +// Thread Local Storage Operations +// +typedef void* OS_TLSIndex; +#define OS_INVALID_TLS_INDEX ((void*)0) + +OS_TLSIndex OS_AllocTLSIndex(); +bool OS_SetTLSValue(OS_TLSIndex nIndex, void *lpvValue); +bool OS_FreeTLSIndex(OS_TLSIndex nIndex); +void* OS_GetTLSValue(OS_TLSIndex nIndex); + +void InitGlobalLock(); +void GetGlobalLock(); +void ReleaseGlobalLock(); + +typedef unsigned int (*TThreadEntrypoint)(void*); + +void OS_CleanupThreadData(void); + +void OS_DumpMemoryCounters(); + +} // end namespace glslang + +#endif // __OSINCLUDE_H diff --git a/third_party/glslang/glslang/Public/ShaderLang.h b/third_party/glslang/glslang/Public/ShaderLang.h new file mode 100644 index 0000000..273f156 --- /dev/null +++ b/third_party/glslang/glslang/Public/ShaderLang.h @@ -0,0 +1,948 @@ +// +// Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +// Copyright (C) 2013-2016 LunarG, Inc. +// Copyright (C) 2015-2018 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +#ifndef _COMPILER_INTERFACE_INCLUDED_ +#define _COMPILER_INTERFACE_INCLUDED_ + +#include "../Include/ResourceLimits.h" +#include "../MachineIndependent/Versions.h" + +#include +#include + +#ifdef _WIN32 + #define C_DECL __cdecl +#else + #define C_DECL +#endif + +#ifdef GLSLANG_IS_SHARED_LIBRARY + #ifdef _WIN32 + #ifdef GLSLANG_EXPORTING + #define GLSLANG_EXPORT __declspec(dllexport) + #else + #define GLSLANG_EXPORT __declspec(dllimport) + #endif + #elif __GNUC__ >= 4 + #define GLSLANG_EXPORT __attribute__((visibility("default"))) + #endif +#endif // GLSLANG_IS_SHARED_LIBRARY + +#ifndef GLSLANG_EXPORT +#define GLSLANG_EXPORT +#endif + +// +// This is the platform independent interface between an OGL driver +// and the shading language compiler/linker. +// + +#ifdef __cplusplus + extern "C" { +#endif + +// +// Call before doing any other compiler/linker operations. +// +// (Call once per process, not once per thread.) +// +GLSLANG_EXPORT int ShInitialize(); + +// +// Call this at process shutdown to clean up memory. +// +GLSLANG_EXPORT int ShFinalize(); + +// +// Types of languages the compiler can consume. +// +typedef enum { + EShLangVertex, + EShLangTessControl, + EShLangTessEvaluation, + EShLangGeometry, + EShLangFragment, + EShLangCompute, + EShLangRayGen, + EShLangRayGenNV = EShLangRayGen, + EShLangIntersect, + EShLangIntersectNV = EShLangIntersect, + EShLangAnyHit, + EShLangAnyHitNV = EShLangAnyHit, + EShLangClosestHit, + EShLangClosestHitNV = EShLangClosestHit, + EShLangMiss, + EShLangMissNV = EShLangMiss, + EShLangCallable, + EShLangCallableNV = EShLangCallable, + EShLangTaskNV, + EShLangMeshNV, + LAST_ELEMENT_MARKER(EShLangCount), +} EShLanguage; // would be better as stage, but this is ancient now + +typedef enum : unsigned { + EShLangVertexMask = (1 << EShLangVertex), + EShLangTessControlMask = (1 << EShLangTessControl), + EShLangTessEvaluationMask = (1 << EShLangTessEvaluation), + EShLangGeometryMask = (1 << EShLangGeometry), + EShLangFragmentMask = (1 << EShLangFragment), + EShLangComputeMask = (1 << EShLangCompute), + EShLangRayGenMask = (1 << EShLangRayGen), + EShLangRayGenNVMask = EShLangRayGenMask, + EShLangIntersectMask = (1 << EShLangIntersect), + EShLangIntersectNVMask = EShLangIntersectMask, + EShLangAnyHitMask = (1 << EShLangAnyHit), + EShLangAnyHitNVMask = EShLangAnyHitMask, + EShLangClosestHitMask = (1 << EShLangClosestHit), + EShLangClosestHitNVMask = EShLangClosestHitMask, + EShLangMissMask = (1 << EShLangMiss), + EShLangMissNVMask = EShLangMissMask, + EShLangCallableMask = (1 << EShLangCallable), + EShLangCallableNVMask = EShLangCallableMask, + EShLangTaskNVMask = (1 << EShLangTaskNV), + EShLangMeshNVMask = (1 << EShLangMeshNV), + LAST_ELEMENT_MARKER(EShLanguageMaskCount), +} EShLanguageMask; + +namespace glslang { + +class TType; + +typedef enum { + EShSourceNone, + EShSourceGlsl, // GLSL, includes ESSL (OpenGL ES GLSL) + EShSourceHlsl, // HLSL + LAST_ELEMENT_MARKER(EShSourceCount), +} EShSource; // if EShLanguage were EShStage, this could be EShLanguage instead + +typedef enum { + EShClientNone, // use when there is no client, e.g. for validation + EShClientVulkan, + EShClientOpenGL, + LAST_ELEMENT_MARKER(EShClientCount), +} EShClient; + +typedef enum { + EShTargetNone, + EShTargetSpv, // SPIR-V (preferred spelling) + EshTargetSpv = EShTargetSpv, // legacy spelling + LAST_ELEMENT_MARKER(EShTargetCount), +} EShTargetLanguage; + +typedef enum { + EShTargetVulkan_1_0 = (1 << 22), // Vulkan 1.0 + EShTargetVulkan_1_1 = (1 << 22) | (1 << 12), // Vulkan 1.1 + EShTargetVulkan_1_2 = (1 << 22) | (2 << 12), // Vulkan 1.2 + EShTargetOpenGL_450 = 450, // OpenGL + LAST_ELEMENT_MARKER(EShTargetClientVersionCount), +} EShTargetClientVersion; + +typedef EShTargetClientVersion EshTargetClientVersion; + +typedef enum { + EShTargetSpv_1_0 = (1 << 16), // SPIR-V 1.0 + EShTargetSpv_1_1 = (1 << 16) | (1 << 8), // SPIR-V 1.1 + EShTargetSpv_1_2 = (1 << 16) | (2 << 8), // SPIR-V 1.2 + EShTargetSpv_1_3 = (1 << 16) | (3 << 8), // SPIR-V 1.3 + EShTargetSpv_1_4 = (1 << 16) | (4 << 8), // SPIR-V 1.4 + EShTargetSpv_1_5 = (1 << 16) | (5 << 8), // SPIR-V 1.5 + LAST_ELEMENT_MARKER(EShTargetLanguageVersionCount), +} EShTargetLanguageVersion; + +struct TInputLanguage { + EShSource languageFamily; // redundant information with other input, this one overrides when not EShSourceNone + EShLanguage stage; // redundant information with other input, this one overrides when not EShSourceNone + EShClient dialect; + int dialectVersion; // version of client's language definition, not the client (when not EShClientNone) +}; + +struct TClient { + EShClient client; + EShTargetClientVersion version; // version of client itself (not the client's input dialect) +}; + +struct TTarget { + EShTargetLanguage language; + EShTargetLanguageVersion version; // version to target, if SPIR-V, defined by "word 1" of the SPIR-V header + bool hlslFunctionality1; // can target hlsl_functionality1 extension(s) +}; + +// All source/client/target versions and settings. +// Can override previous methods of setting, when items are set here. +// Expected to grow, as more are added, rather than growing parameter lists. +struct TEnvironment { + TInputLanguage input; // definition of the input language + TClient client; // what client is the overall compilation being done for? + TTarget target; // what to generate +}; + +GLSLANG_EXPORT const char* StageName(EShLanguage); + +} // end namespace glslang + +// +// Types of output the linker will create. +// +typedef enum { + EShExVertexFragment, + EShExFragment +} EShExecutable; + +// +// Optimization level for the compiler. +// +typedef enum { + EShOptNoGeneration, + EShOptNone, + EShOptSimple, // Optimizations that can be done quickly + EShOptFull, // Optimizations that will take more time + LAST_ELEMENT_MARKER(EshOptLevelCount), +} EShOptimizationLevel; + +// +// Texture and Sampler transformation mode. +// +typedef enum { + EShTexSampTransKeep, // keep textures and samplers as is (default) + EShTexSampTransUpgradeTextureRemoveSampler, // change texture w/o embeded sampler into sampled texture and throw away all samplers + LAST_ELEMENT_MARKER(EShTexSampTransCount), +} EShTextureSamplerTransformMode; + +// +// Message choices for what errors and warnings are given. +// +enum EShMessages : unsigned { + EShMsgDefault = 0, // default is to give all required errors and extra warnings + EShMsgRelaxedErrors = (1 << 0), // be liberal in accepting input + EShMsgSuppressWarnings = (1 << 1), // suppress all warnings, except those required by the specification + EShMsgAST = (1 << 2), // print the AST intermediate representation + EShMsgSpvRules = (1 << 3), // issue messages for SPIR-V generation + EShMsgVulkanRules = (1 << 4), // issue messages for Vulkan-requirements of GLSL for SPIR-V + EShMsgOnlyPreprocessor = (1 << 5), // only print out errors produced by the preprocessor + EShMsgReadHlsl = (1 << 6), // use HLSL parsing rules and semantics + EShMsgCascadingErrors = (1 << 7), // get cascading errors; risks error-recovery issues, instead of an early exit + EShMsgKeepUncalled = (1 << 8), // for testing, don't eliminate uncalled functions + EShMsgHlslOffsets = (1 << 9), // allow block offsets to follow HLSL rules instead of GLSL rules + EShMsgDebugInfo = (1 << 10), // save debug information + EShMsgHlslEnable16BitTypes = (1 << 11), // enable use of 16-bit types in SPIR-V for HLSL + EShMsgHlslLegalization = (1 << 12), // enable HLSL Legalization messages + EShMsgHlslDX9Compatible = (1 << 13), // enable HLSL DX9 compatible mode (for samplers and semantics) + EShMsgBuiltinSymbolTable = (1 << 14), // print the builtin symbol table + LAST_ELEMENT_MARKER(EShMsgCount), +}; + +// +// Options for building reflection +// +typedef enum { + EShReflectionDefault = 0, // default is original behaviour before options were added + EShReflectionStrictArraySuffix = (1 << 0), // reflection will follow stricter rules for array-of-structs suffixes + EShReflectionBasicArraySuffix = (1 << 1), // arrays of basic types will be appended with [0] as in GL reflection + EShReflectionIntermediateIO = (1 << 2), // reflect inputs and outputs to program, even with no vertex shader + EShReflectionSeparateBuffers = (1 << 3), // buffer variables and buffer blocks are reflected separately + EShReflectionAllBlockVariables = (1 << 4), // reflect all variables in blocks, even if they are inactive + EShReflectionUnwrapIOBlocks = (1 << 5), // unwrap input/output blocks the same as with uniform blocks + EShReflectionAllIOVariables = (1 << 6), // reflect all input/output variables, even if they are inactive + EShReflectionSharedStd140SSBO = (1 << 7), // Apply std140/shared rules for ubo to ssbo + EShReflectionSharedStd140UBO = (1 << 8), // Apply std140/shared rules for ubo to ssbo + LAST_ELEMENT_MARKER(EShReflectionCount), +} EShReflectionOptions; + +// +// Build a table for bindings. This can be used for locating +// attributes, uniforms, globals, etc., as needed. +// +typedef struct { + const char* name; + int binding; +} ShBinding; + +typedef struct { + int numBindings; + ShBinding* bindings; // array of bindings +} ShBindingTable; + +// +// ShHandle held by but opaque to the driver. It is allocated, +// managed, and de-allocated by the compiler/linker. It's contents +// are defined by and used by the compiler and linker. For example, +// symbol table information and object code passed from the compiler +// to the linker can be stored where ShHandle points. +// +// If handle creation fails, 0 will be returned. +// +typedef void* ShHandle; + +// +// Driver calls these to create and destroy compiler/linker +// objects. +// +GLSLANG_EXPORT ShHandle ShConstructCompiler(const EShLanguage, int debugOptions); // one per shader +GLSLANG_EXPORT ShHandle ShConstructLinker(const EShExecutable, int debugOptions); // one per shader pair +GLSLANG_EXPORT ShHandle ShConstructUniformMap(); // one per uniform namespace (currently entire program object) +GLSLANG_EXPORT void ShDestruct(ShHandle); + +// +// The return value of ShCompile is boolean, non-zero indicating +// success. +// +// The info-log should be written by ShCompile into +// ShHandle, so it can answer future queries. +// +GLSLANG_EXPORT int ShCompile( + const ShHandle, + const char* const shaderStrings[], + const int numStrings, + const int* lengths, + const EShOptimizationLevel, + const TBuiltInResource *resources, + int debugOptions, + int defaultVersion = 110, // use 100 for ES environment, overridden by #version in shader + bool forwardCompatible = false, // give errors for use of deprecated features + EShMessages messages = EShMsgDefault // warnings and errors + ); + +GLSLANG_EXPORT int ShLinkExt( + const ShHandle, // linker object + const ShHandle h[], // compiler objects to link together + const int numHandles); + +// +// ShSetEncrpytionMethod is a place-holder for specifying +// how source code is encrypted. +// +GLSLANG_EXPORT void ShSetEncryptionMethod(ShHandle); + +// +// All the following return 0 if the information is not +// available in the object passed down, or the object is bad. +// +GLSLANG_EXPORT const char* ShGetInfoLog(const ShHandle); +GLSLANG_EXPORT const void* ShGetExecutable(const ShHandle); +GLSLANG_EXPORT int ShSetVirtualAttributeBindings(const ShHandle, const ShBindingTable*); // to detect user aliasing +GLSLANG_EXPORT int ShSetFixedAttributeBindings(const ShHandle, const ShBindingTable*); // to force any physical mappings +// +// Tell the linker to never assign a vertex attribute to this list of physical attributes +// +GLSLANG_EXPORT int ShExcludeAttributes(const ShHandle, int *attributes, int count); + +// +// Returns the location ID of the named uniform. +// Returns -1 if error. +// +GLSLANG_EXPORT int ShGetUniformLocation(const ShHandle uniformMap, const char* name); + +#ifdef __cplusplus + } // end extern "C" +#endif + +//////////////////////////////////////////////////////////////////////////////////////////// +// +// Deferred-Lowering C++ Interface +// ----------------------------------- +// +// Below is a new alternate C++ interface, which deprecates the above +// opaque handle-based interface. +// +// The below is further designed to handle multiple compilation units per stage, where +// the intermediate results, including the parse tree, are preserved until link time, +// rather than the above interface which is designed to have each compilation unit +// lowered at compile time. In the above model, linking occurs on the lowered results, +// whereas in this model intra-stage linking can occur at the parse tree +// (treeRoot in TIntermediate) level, and then a full stage can be lowered. +// + +#include +#include +#include + +class TCompiler; +class TInfoSink; + +namespace glslang { + +struct Version { + int major; + int minor; + int patch; + const char* flavor; +}; + +GLSLANG_EXPORT Version GetVersion(); +GLSLANG_EXPORT const char* GetEsslVersionString(); +GLSLANG_EXPORT const char* GetGlslVersionString(); +GLSLANG_EXPORT int GetKhronosToolId(); + +class TIntermediate; +class TProgram; +class TPoolAllocator; + +// Call this exactly once per process before using anything else +GLSLANG_EXPORT bool InitializeProcess(); + +// Call once per process to tear down everything +GLSLANG_EXPORT void FinalizeProcess(); + +// Resource type for IO resolver +enum TResourceType { + EResSampler, + EResTexture, + EResImage, + EResUbo, + EResSsbo, + EResUav, + EResCount +}; + + +// Make one TShader per shader that you will link into a program. Then +// - provide the shader through setStrings() or setStringsWithLengths() +// - optionally call setEnv*(), see below for more detail +// - optionally use setPreamble() to set a special shader string that will be +// processed before all others but won't affect the validity of #version +// - optionally call addProcesses() for each setting/transform, +// see comment for class TProcesses +// - call parse(): source language and target environment must be selected +// either by correct setting of EShMessages sent to parse(), or by +// explicitly calling setEnv*() +// - query the info logs +// +// N.B.: Does not yet support having the same TShader instance being linked into +// multiple programs. +// +// N.B.: Destruct a linked program *before* destructing the shaders linked into it. +// +class TShader { +public: + GLSLANG_EXPORT explicit TShader(EShLanguage); + GLSLANG_EXPORT virtual ~TShader(); + GLSLANG_EXPORT void setStrings(const char* const* s, int n); + GLSLANG_EXPORT void setStringsWithLengths( + const char* const* s, const int* l, int n); + GLSLANG_EXPORT void setStringsWithLengthsAndNames( + const char* const* s, const int* l, const char* const* names, int n); + void setPreamble(const char* s) { preamble = s; } + GLSLANG_EXPORT void setEntryPoint(const char* entryPoint); + GLSLANG_EXPORT void setSourceEntryPoint(const char* sourceEntryPointName); + GLSLANG_EXPORT void addProcesses(const std::vector&); + + // IO resolver binding data: see comments in ShaderLang.cpp + GLSLANG_EXPORT void setShiftBinding(TResourceType res, unsigned int base); + GLSLANG_EXPORT void setShiftSamplerBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftTextureBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftImageBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftUboBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftUavBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftCbufferBinding(unsigned int base); // synonym for setShiftUboBinding + GLSLANG_EXPORT void setShiftSsboBinding(unsigned int base); // DEPRECATED: use setShiftBinding + GLSLANG_EXPORT void setShiftBindingForSet(TResourceType res, unsigned int base, unsigned int set); + GLSLANG_EXPORT void setResourceSetBinding(const std::vector& base); + GLSLANG_EXPORT void setAutoMapBindings(bool map); + GLSLANG_EXPORT void setAutoMapLocations(bool map); + GLSLANG_EXPORT void addUniformLocationOverride(const char* name, int loc); + GLSLANG_EXPORT void setUniformLocationBase(int base); + GLSLANG_EXPORT void setInvertY(bool invert); +#ifdef ENABLE_HLSL + GLSLANG_EXPORT void setHlslIoMapping(bool hlslIoMap); + GLSLANG_EXPORT void setFlattenUniformArrays(bool flatten); +#endif + GLSLANG_EXPORT void setNoStorageFormat(bool useUnknownFormat); + GLSLANG_EXPORT void setNanMinMaxClamp(bool nanMinMaxClamp); + GLSLANG_EXPORT void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode); + + // For setting up the environment (cleared to nothingness in the constructor). + // These must be called so that parsing is done for the right source language and + // target environment, either indirectly through TranslateEnvironment() based on + // EShMessages et. al., or directly by the user. + // + // setEnvInput: The input source language and stage. If generating code for a + // specific client, the input client semantics to use and the + // version of the that client's input semantics to use, otherwise + // use EShClientNone and version of 0, e.g. for validation mode. + // Note 'version' does not describe the target environment, + // just the version of the source dialect to compile under. + // + // See the definitions of TEnvironment, EShSource, EShLanguage, + // and EShClient for choices and more detail. + // + // setEnvClient: The client that will be hosting the execution, and it's version. + // Note 'version' is not the version of the languages involved, but + // the version of the client environment. + // Use EShClientNone and version of 0 if there is no client, e.g. + // for validation mode. + // + // See EShTargetClientVersion for choices. + // + // setEnvTarget: The language to translate to when generating code, and that + // language's version. + // Use EShTargetNone and version of 0 if there is no client, e.g. + // for validation mode. + // + void setEnvInput(EShSource lang, EShLanguage envStage, EShClient client, int version) + { + environment.input.languageFamily = lang; + environment.input.stage = envStage; + environment.input.dialect = client; + environment.input.dialectVersion = version; + } + void setEnvClient(EShClient client, EShTargetClientVersion version) + { + environment.client.client = client; + environment.client.version = version; + } + void setEnvTarget(EShTargetLanguage lang, EShTargetLanguageVersion version) + { + environment.target.language = lang; + environment.target.version = version; + } + + void getStrings(const char* const* &s, int& n) { s = strings; n = numStrings; } + +#ifdef ENABLE_HLSL + void setEnvTargetHlslFunctionality1() { environment.target.hlslFunctionality1 = true; } + bool getEnvTargetHlslFunctionality1() const { return environment.target.hlslFunctionality1; } +#else + bool getEnvTargetHlslFunctionality1() const { return false; } +#endif + + // Interface to #include handlers. + // + // To support #include, a client of Glslang does the following: + // 1. Call setStringsWithNames to set the source strings and associated + // names. For example, the names could be the names of the files + // containing the shader sources. + // 2. Call parse with an Includer. + // + // When the Glslang parser encounters an #include directive, it calls + // the Includer's include method with the requested include name + // together with the current string name. The returned IncludeResult + // contains the fully resolved name of the included source, together + // with the source text that should replace the #include directive + // in the source stream. After parsing that source, Glslang will + // release the IncludeResult object. + class Includer { + public: + // An IncludeResult contains the resolved name and content of a source + // inclusion. + struct IncludeResult { + IncludeResult(const std::string& headerName, const char* const headerData, const size_t headerLength, void* userData) : + headerName(headerName), headerData(headerData), headerLength(headerLength), userData(userData) { } + // For a successful inclusion, the fully resolved name of the requested + // include. For example, in a file system-based includer, full resolution + // should convert a relative path name into an absolute path name. + // For a failed inclusion, this is an empty string. + const std::string headerName; + // The content and byte length of the requested inclusion. The + // Includer producing this IncludeResult retains ownership of the + // storage. + // For a failed inclusion, the header + // field points to a string containing error details. + const char* const headerData; + const size_t headerLength; + // Include resolver's context. + void* userData; + protected: + IncludeResult& operator=(const IncludeResult&); + IncludeResult(); + }; + + // For both include methods below: + // + // Resolves an inclusion request by name, current source name, + // and include depth. + // On success, returns an IncludeResult containing the resolved name + // and content of the include. + // On failure, returns a nullptr, or an IncludeResult + // with an empty string for the headerName and error details in the + // header field. + // The Includer retains ownership of the contents + // of the returned IncludeResult value, and those contents must + // remain valid until the releaseInclude method is called on that + // IncludeResult object. + // + // Note "local" vs. "system" is not an "either/or": "local" is an + // extra thing to do over "system". Both might get called, as per + // the C++ specification. + + // For the "system" or <>-style includes; search the "system" paths. + virtual IncludeResult* includeSystem(const char* /*headerName*/, + const char* /*includerName*/, + size_t /*inclusionDepth*/) { return nullptr; } + + // For the "local"-only aspect of a "" include. Should not search in the + // "system" paths, because on returning a failure, the parser will + // call includeSystem() to look in the "system" locations. + virtual IncludeResult* includeLocal(const char* /*headerName*/, + const char* /*includerName*/, + size_t /*inclusionDepth*/) { return nullptr; } + + // Signals that the parser will no longer use the contents of the + // specified IncludeResult. + virtual void releaseInclude(IncludeResult*) = 0; + virtual ~Includer() {} + }; + + // Fail all Includer searches + class ForbidIncluder : public Includer { + public: + virtual void releaseInclude(IncludeResult*) override { } + }; + + GLSLANG_EXPORT bool parse( + const TBuiltInResource*, int defaultVersion, EProfile defaultProfile, + bool forceDefaultVersionAndProfile, bool forwardCompatible, + EShMessages, Includer&); + + bool parse(const TBuiltInResource* res, int defaultVersion, EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages messages) + { + TShader::ForbidIncluder includer; + return parse(res, defaultVersion, defaultProfile, forceDefaultVersionAndProfile, forwardCompatible, messages, includer); + } + + // Equivalent to parse() without a default profile and without forcing defaults. + bool parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages) + { + return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages); + } + + bool parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages, + Includer& includer) + { + return parse(builtInResources, defaultVersion, ENoProfile, false, forwardCompatible, messages, includer); + } + + // NOTE: Doing just preprocessing to obtain a correct preprocessed shader string + // is not an officially supported or fully working path. + GLSLANG_EXPORT bool preprocess( + const TBuiltInResource* builtInResources, int defaultVersion, + EProfile defaultProfile, bool forceDefaultVersionAndProfile, + bool forwardCompatible, EShMessages message, std::string* outputString, + Includer& includer); + + GLSLANG_EXPORT const char* getInfoLog(); + GLSLANG_EXPORT const char* getInfoDebugLog(); + EShLanguage getStage() const { return stage; } + TIntermediate* getIntermediate() const { return intermediate; } + +protected: + TPoolAllocator* pool; + EShLanguage stage; + TCompiler* compiler; + TIntermediate* intermediate; + TInfoSink* infoSink; + // strings and lengths follow the standard for glShaderSource: + // strings is an array of numStrings pointers to string data. + // lengths can be null, but if not it is an array of numStrings + // integers containing the length of the associated strings. + // if lengths is null or lengths[n] < 0 the associated strings[n] is + // assumed to be null-terminated. + // stringNames is the optional names for all the strings. If stringNames + // is null, then none of the strings has name. If a certain element in + // stringNames is null, then the corresponding string does not have name. + const char* const* strings; // explicit code to compile, see previous comment + const int* lengths; + const char* const* stringNames; + int numStrings; // size of the above arrays + const char* preamble; // string of implicit code to compile before the explicitly provided code + + // a function in the source string can be renamed FROM this TO the name given in setEntryPoint. + std::string sourceEntryPointName; + + TEnvironment environment; + + friend class TProgram; + +private: + TShader& operator=(TShader&); +}; + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + +// +// A reflection database and its interface, consistent with the OpenGL API reflection queries. +// + +// Data needed for just a single object at the granularity exchanged by the reflection API +class TObjectReflection { +public: + GLSLANG_EXPORT TObjectReflection(const std::string& pName, const TType& pType, int pOffset, int pGLDefineType, int pSize, int pIndex); + + GLSLANG_EXPORT const TType* getType() const { return type; } + GLSLANG_EXPORT int getBinding() const; + GLSLANG_EXPORT void dump() const; + static TObjectReflection badReflection() { return TObjectReflection(); } + + std::string name; + int offset; + int glDefineType; + int size; // data size in bytes for a block, array size for a (non-block) object that's an array + int index; + int counterIndex; + int numMembers; + int arrayStride; // stride of an array variable + int topLevelArraySize; // size of the top-level variable in a storage buffer member + int topLevelArrayStride; // stride of the top-level variable in a storage buffer member + EShLanguageMask stages; + +protected: + TObjectReflection() + : offset(-1), glDefineType(-1), size(-1), index(-1), counterIndex(-1), numMembers(-1), arrayStride(0), + topLevelArrayStride(0), stages(EShLanguageMask(0)), type(nullptr) + { + } + + const TType* type; +}; + +class TReflection; +class TIoMapper; +struct TVarEntryInfo; + +// Allows to customize the binding layout after linking. +// All used uniform variables will invoke at least validateBinding. +// If validateBinding returned true then the other resolveBinding, +// resolveSet, and resolveLocation are invoked to resolve the binding +// and descriptor set index respectively. +// +// Invocations happen in a particular order: +// 1) all shader inputs +// 2) all shader outputs +// 3) all uniforms with binding and set already defined +// 4) all uniforms with binding but no set defined +// 5) all uniforms with set but no binding defined +// 6) all uniforms with no binding and no set defined +// +// mapIO will use this resolver in two phases. The first +// phase is a notification phase, calling the corresponging +// notifiy callbacks, this phase ends with a call to endNotifications. +// Phase two starts directly after the call to endNotifications +// and calls all other callbacks to validate and to get the +// bindings, sets, locations, component and color indices. +// +// NOTE: that still limit checks are applied to bindings and sets +// and may result in an error. +class TIoMapResolver +{ +public: + virtual ~TIoMapResolver() {} + + // Should return true if the resulting/current binding would be okay. + // Basic idea is to do aliasing binding checks with this. + virtual bool validateBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current binding should be overridden. + // Return -1 if the current binding (including no binding) should be kept. + virtual int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current set should be overridden. + // Return -1 if the current set (including no set) should be kept. + virtual int resolveSet(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current location should be overridden. + // Return -1 if the current location (including no location) should be kept. + virtual int resolveUniformLocation(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return true if the resulting/current setup would be okay. + // Basic idea is to do aliasing checks and reject invalid semantic names. + virtual bool validateInOut(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current location should be overridden. + // Return -1 if the current location (including no location) should be kept. + virtual int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current component index should be overridden. + // Return -1 if the current component index (including no index) should be kept. + virtual int resolveInOutComponent(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Should return a value >= 0 if the current color index should be overridden. + // Return -1 if the current color index (including no index) should be kept. + virtual int resolveInOutIndex(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Notification of a uniform variable + virtual void notifyBinding(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Notification of a in or out variable + virtual void notifyInOut(EShLanguage stage, TVarEntryInfo& ent) = 0; + // Called by mapIO when it starts its notify pass for the given stage + virtual void beginNotifications(EShLanguage stage) = 0; + // Called by mapIO when it has finished the notify pass + virtual void endNotifications(EShLanguage stage) = 0; + // Called by mipIO when it starts its resolve pass for the given stage + virtual void beginResolve(EShLanguage stage) = 0; + // Called by mapIO when it has finished the resolve pass + virtual void endResolve(EShLanguage stage) = 0; + // Called by mapIO when it starts its symbol collect for teh given stage + virtual void beginCollect(EShLanguage stage) = 0; + // Called by mapIO when it has finished the symbol collect + virtual void endCollect(EShLanguage stage) = 0; + // Called by TSlotCollector to resolve storage locations or bindings + virtual void reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0; + // Called by TSlotCollector to resolve resource locations or bindings + virtual void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0; + // Called by mapIO.addStage to set shader stage mask to mark a stage be added to this pipeline + virtual void addStage(EShLanguage stage) = 0; +}; + +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE + +// Make one TProgram per set of shaders that will get linked together. Add all +// the shaders that are to be linked together. After calling shader.parse() +// for all shaders, call link(). +// +// N.B.: Destruct a linked program *before* destructing the shaders linked into it. +// +class TProgram { +public: + GLSLANG_EXPORT TProgram(); + GLSLANG_EXPORT virtual ~TProgram(); + void addShader(TShader* shader) { stages[shader->stage].push_back(shader); } + std::list& getShaders(EShLanguage stage) { return stages[stage]; } + // Link Validation interface + GLSLANG_EXPORT bool link(EShMessages); + GLSLANG_EXPORT const char* getInfoLog(); + GLSLANG_EXPORT const char* getInfoDebugLog(); + + TIntermediate* getIntermediate(EShLanguage stage) const { return intermediate[stage]; } + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + + // Reflection Interface + + // call first, to do liveness analysis, index mapping, etc.; returns false on failure + GLSLANG_EXPORT bool buildReflection(int opts = EShReflectionDefault); + GLSLANG_EXPORT unsigned getLocalSize(int dim) const; // return dim'th local size + GLSLANG_EXPORT int getReflectionIndex(const char *name) const; + GLSLANG_EXPORT int getReflectionPipeIOIndex(const char* name, const bool inOrOut) const; + GLSLANG_EXPORT int getNumUniformVariables() const; + GLSLANG_EXPORT const TObjectReflection& getUniform(int index) const; + GLSLANG_EXPORT int getNumUniformBlocks() const; + GLSLANG_EXPORT const TObjectReflection& getUniformBlock(int index) const; + GLSLANG_EXPORT int getNumPipeInputs() const; + GLSLANG_EXPORT const TObjectReflection& getPipeInput(int index) const; + GLSLANG_EXPORT int getNumPipeOutputs() const; + GLSLANG_EXPORT const TObjectReflection& getPipeOutput(int index) const; + GLSLANG_EXPORT int getNumBufferVariables() const; + GLSLANG_EXPORT const TObjectReflection& getBufferVariable(int index) const; + GLSLANG_EXPORT int getNumBufferBlocks() const; + GLSLANG_EXPORT const TObjectReflection& getBufferBlock(int index) const; + GLSLANG_EXPORT int getNumAtomicCounters() const; + GLSLANG_EXPORT const TObjectReflection& getAtomicCounter(int index) const; + + // Legacy Reflection Interface - expressed in terms of above interface + + // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS) + int getNumLiveUniformVariables() const { return getNumUniformVariables(); } + + // can be used for glGetProgramiv(GL_ACTIVE_UNIFORM_BLOCKS) + int getNumLiveUniformBlocks() const { return getNumUniformBlocks(); } + + // can be used for glGetProgramiv(GL_ACTIVE_ATTRIBUTES) + int getNumLiveAttributes() const { return getNumPipeInputs(); } + + // can be used for glGetUniformIndices() + int getUniformIndex(const char *name) const { return getReflectionIndex(name); } + + int getPipeIOIndex(const char *name, const bool inOrOut) const + { return getReflectionPipeIOIndex(name, inOrOut); } + + // can be used for "name" part of glGetActiveUniform() + const char *getUniformName(int index) const { return getUniform(index).name.c_str(); } + + // returns the binding number + int getUniformBinding(int index) const { return getUniform(index).getBinding(); } + + // returns Shaders Stages where a Uniform is present + EShLanguageMask getUniformStages(int index) const { return getUniform(index).stages; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX) + int getUniformBlockIndex(int index) const { return getUniform(index).index; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE) + int getUniformType(int index) const { return getUniform(index).glDefineType; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET) + int getUniformBufferOffset(int index) const { return getUniform(index).offset; } + + // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE) + int getUniformArraySize(int index) const { return getUniform(index).size; } + + // returns a TType* + const TType *getUniformTType(int index) const { return getUniform(index).getType(); } + + // can be used for glGetActiveUniformBlockName() + const char *getUniformBlockName(int index) const { return getUniformBlock(index).name.c_str(); } + + // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE) + int getUniformBlockSize(int index) const { return getUniformBlock(index).size; } + + // returns the block binding number + int getUniformBlockBinding(int index) const { return getUniformBlock(index).getBinding(); } + + // returns block index of associated counter. + int getUniformBlockCounterIndex(int index) const { return getUniformBlock(index).counterIndex; } + + // returns a TType* + const TType *getUniformBlockTType(int index) const { return getUniformBlock(index).getType(); } + + // can be used for glGetActiveAttrib() + const char *getAttributeName(int index) const { return getPipeInput(index).name.c_str(); } + + // can be used for glGetActiveAttrib() + int getAttributeType(int index) const { return getPipeInput(index).glDefineType; } + + // returns a TType* + const TType *getAttributeTType(int index) const { return getPipeInput(index).getType(); } + + GLSLANG_EXPORT void dumpReflection(); + // I/O mapping: apply base offsets and map live unbound variables + // If resolver is not provided it uses the previous approach + // and respects auto assignment and offsets. + GLSLANG_EXPORT bool mapIO(TIoMapResolver* pResolver = nullptr, TIoMapper* pIoMapper = nullptr); +#endif // !GLSLANG_WEB && !GLSLANG_ANGLE + +protected: + GLSLANG_EXPORT bool linkStage(EShLanguage, EShMessages); + + TPoolAllocator* pool; + std::list stages[EShLangCount]; + TIntermediate* intermediate[EShLangCount]; + bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage + TInfoSink* infoSink; +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + TReflection* reflection; +#endif + bool linked; + +private: + TProgram(TProgram&); + TProgram& operator=(TProgram&); +}; + +} // end namespace glslang + +#endif // _COMPILER_INTERFACE_INCLUDED_ diff --git a/third_party/glslang/glslang/tnt/CMakeLists.txt b/third_party/glslang/glslang/tnt/CMakeLists.txt new file mode 100644 index 0000000..8805e10 --- /dev/null +++ b/third_party/glslang/glslang/tnt/CMakeLists.txt @@ -0,0 +1,117 @@ +if(WIN32) + add_subdirectory(../OSDependent/Windows "${CMAKE_CURRENT_BINARY_DIR}/OSDependent/Windows") +elseif(UNIX) + add_subdirectory(../OSDependent/Unix "${CMAKE_CURRENT_BINARY_DIR}/OSDependent/Unix") +else(WIN32) + message("unknown platform") +endif(WIN32) + +set(SOURCES + ../MachineIndependent/glslang.y + ../MachineIndependent/glslang_tab.cpp + ../MachineIndependent/attribute.cpp + ../MachineIndependent/Constant.cpp + ../MachineIndependent/iomapper.cpp + ../MachineIndependent/InfoSink.cpp + ../MachineIndependent/Initialize.cpp + ../MachineIndependent/IntermTraverse.cpp + ../MachineIndependent/Intermediate.cpp + ../MachineIndependent/ParseContextBase.cpp + ../MachineIndependent/ParseHelper.cpp + ../MachineIndependent/PoolAlloc.cpp + ../MachineIndependent/RemoveTree.cpp + ../MachineIndependent/Scan.cpp + ../MachineIndependent/ShaderLang.cpp + ../MachineIndependent/SymbolTable.cpp + ../MachineIndependent/Versions.cpp + ../MachineIndependent/intermOut.cpp + ../MachineIndependent/limits.cpp + ../MachineIndependent/linkValidate.cpp + ../MachineIndependent/parseConst.cpp + ../MachineIndependent/reflection.cpp + ../MachineIndependent/preprocessor/Pp.cpp + ../MachineIndependent/preprocessor/PpAtom.cpp + ../MachineIndependent/preprocessor/PpContext.cpp + ../MachineIndependent/preprocessor/PpScanner.cpp + ../MachineIndependent/preprocessor/PpTokens.cpp + ../MachineIndependent/propagateNoContraction.cpp + ../GenericCodeGen/CodeGen.cpp + ../GenericCodeGen/Link.cpp) + +set(HEADERS + ../Public/ShaderLang.h + ../Include/arrays.h + ../Include/BaseTypes.h + ../Include/Common.h + ../Include/ConstantUnion.h + ../Include/InfoSink.h + ../Include/InitializeGlobals.h + ../Include/intermediate.h + ../Include/PoolAlloc.h + ../Include/ResourceLimits.h + ../Include/ShHandle.h + ../Include/Types.h + ../MachineIndependent/attribute.h + ../MachineIndependent/glslang_tab.cpp.h + ../MachineIndependent/gl_types.h + ../MachineIndependent/Initialize.h + ../MachineIndependent/iomapper.h + ../MachineIndependent/LiveTraverser.h + ../MachineIndependent/localintermediate.h + ../MachineIndependent/ParseHelper.h + ../MachineIndependent/reflection.h + ../MachineIndependent/RemoveTree.h + ../MachineIndependent/Scan.h + ../MachineIndependent/ScanContext.h + ../MachineIndependent/SymbolTable.h + ../MachineIndependent/Versions.h + ../MachineIndependent/parseVersions.h + ../MachineIndependent/propagateNoContraction.h + ../MachineIndependent/preprocessor/PpContext.h + ../MachineIndependent/preprocessor/PpTokens.h) + +# This might be useful for making grammar changes: +# +# find_package(BISON) +# add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/MachineIndependent/glslang_tab.cpp ${CMAKE_CURRENT_SOURCE_DIR}/MachineIndependent/glslang_tab.cpp.h +# COMMAND ${BISON_EXECUTABLE} --defines=${CMAKE_CURRENT_SOURCE_DIR}/MachineIndependent/glslang_tab.cpp.h -t ${CMAKE_CURRENT_SOURCE_DIR}/MachineIndependent/glslang.y -o ${CMAKE_CURRENT_SOURCE_DIR}/MachineIndependent/glslang_tab.cpp +# MAIN_DEPENDENCY MachineIndependent/glslang.y +# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +# set(BISON_GLSLParser_OUTPUT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/MachineIndependent/glslang_tab.cpp) + +add_library(glslang STATIC ${BISON_GLSLParser_OUTPUT_SOURCE} ${SOURCES} ${HEADERS}) +set_property(TARGET glslang PROPERTY FOLDER glslang) +set_property(TARGET glslang PROPERTY POSITION_INDEPENDENT_CODE ON) + +glslang_add_build_info_dependency(glslang) + +set(PUBLIC_HEADER_FOLDERS + ../Include + ../Public + ../MachineIndependent) + +target_include_directories(glslang PUBLIC ${PUBLIC_HEADER_FOLDERS}) +target_link_libraries(glslang OGLCompiler OSDependent) +#if(ENABLE_HLSL) +# target_link_libraries(glslang HLSL) +#endif() + +if(WIN32) + source_group("../Public" REGULAR_EXPRESSION "Public/*") + source_group("../MachineIndependent" REGULAR_EXPRESSION "MachineIndependent/[^/]*") + source_group("../Include" REGULAR_EXPRESSION "Include/[^/]*") + source_group("../GenericCodeGen" REGULAR_EXPRESSION "GenericCodeGen/*") + source_group("MachineIndependent\\Preprocessor" REGULAR_EXPRESSION "MachineIndependent/preprocessor/*") +endif(WIN32) + +if(ENABLE_GLSLANG_INSTALL) + install(TARGETS glslang + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif(ENABLE_GLSLANG_INSTALL) + +if(ENABLE_GLSLANG_INSTALL) + foreach(file ${HEADERS}) + get_filename_component(dir ${file} DIRECTORY) + install(FILES ${file} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/glslang/${dir}) + endforeach() +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/glslang/updateGrammar b/third_party/glslang/glslang/updateGrammar new file mode 100755 index 0000000..9209493 --- /dev/null +++ b/third_party/glslang/glslang/updateGrammar @@ -0,0 +1,49 @@ +#!/bin/bash + +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +if [ "$1" = 'web' ] +then + m4 -P -DGLSLANG_WEB MachineIndependent/glslang.m4 > MachineIndependent/glslang.y +elif [ "$#" -eq 0 ] +then + m4 -P MachineIndependent/glslang.m4 > MachineIndependent/glslang.y +else + echo usage: + echo $0 web + echo $0 + exit +fi + +bison --defines=MachineIndependent/glslang_tab.cpp.h -t MachineIndependent/glslang.y -o MachineIndependent/glslang_tab.cpp diff --git a/third_party/glslang/gtests/AST.FromFile.cpp b/third_party/glslang/gtests/AST.FromFile.cpp new file mode 100644 index 0000000..dc7fea3 --- /dev/null +++ b/third_party/glslang/gtests/AST.FromFile.cpp @@ -0,0 +1,298 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +using CompileToAstTest = GlslangTest<::testing::TestWithParam>; + +using CompileToAstTestNV = GlslangTest<::testing::TestWithParam>; + +TEST_P(CompileToAstTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::OpenGL, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::AST); +} + +// Compiling GLSL to SPIR-V under OpenGL semantics (NV extensions enabled). +TEST_P(CompileToAstTestNV, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::OpenGL, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::AST); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileToAstTest, + ::testing::ValuesIn(std::vector({ + "sample.frag", + "sample.vert", + "decls.frag", + "specExamples.frag", + "specExamples.vert", + "versionsClean.frag", + "versionsClean.vert", + "versionsErrors.frag", + "versionsErrors.vert", + "100.frag", + "100samplerExternal.frag", + "120.vert", + "120.frag", + "130.vert", + "130.frag", + "140.vert", + "140.frag", + "150.vert", + "150.geom", + "150.frag", + "precision.frag", + "precision.vert", + "nonSquare.vert", + "matrixError.vert", + "cppSimple.vert", + "cppIndent.vert", + "cppIntMinOverNegativeOne.frag", + "cppMerge.frag", + "cppNest.vert", + "cppBad.vert", + "cppBad2.vert", + "cppBad3.vert", + "cppBad4.vert", + "cppBad5.vert", + "cppComplexExpr.vert", + "cppDeepNest.frag", + "cppPassMacroName.frag", + "cppRelaxSkipTokensErrors.vert", + "badChars.frag", + "pointCoord.frag", + "array.frag", + "array100.frag", + "comment.frag", + "300.vert", + "300.frag", + "300BuiltIns.frag", + "300layout.vert", + "300layout.frag", + "300operations.frag", + "300block.frag", + "300samplerExternal.frag", + "300samplerExternalYUV.frag", + "310.comp", + "310.vert", + "310.geom", + "310.frag", + "310.tesc", + "310.tese", + "310implicitSizeArrayError.vert", + "310.inheritMemory.frag", + "310AofA.vert", + "310runtimeArray.vert", + "320.comp", + "320.vert", + "320.geom", + "320.frag", + "320.tesc", + "320.tese", + "330.frag", + "330comp.frag", + "constErrors.frag", + "constFold.frag", + "constFoldIntMin.frag", + "errors.frag", + "forwardRef.frag", + "uint.frag", + "switch.frag", + "tokenLength.vert", + "100Limits.vert", + "100scope.vert", + "110scope.vert", + "300scope.vert", + "400.frag", + "400.vert", + "410.vert", + "420.comp", + "420.frag", + "420.vert", + "420.geom", + "420_size_gl_in.geom", + "430scope.vert", + "lineContinuation100.vert", + "lineContinuation.vert", + "numeral.frag", + "400.geom", + "400.tesc", + "400.tese", + "410.tesc", + "420.tesc", + "420.tese", + "410.geom", + "430.vert", + "430.comp", + "430AofA.frag", + "435.vert", + "440.vert", + "440.frag", + "450.vert", + "450.geom", + "450.tesc", + "450.tese", + "450.frag", + "450.comp", + "460.frag", + "460.vert", + "dce.frag", + "atomic_uint.frag", + "implicitInnerAtomicUint.frag", + "aggOps.frag", + "always-discard.frag", + "always-discard2.frag", + "conditionalDiscard.frag", + "conversion.frag", + "dataOut.frag", + "dataOutIndirect.frag", + "deepRvalue.frag", + "depthOut.frag", + "discard-dce.frag", + "doWhileLoop.frag", + "earlyReturnDiscard.frag", + "flowControl.frag", + "forLoop.frag", + "functionCall.frag", + "functionSemantics.frag", + "length.frag", + "localAggregates.frag", + "loops.frag", + "loopsArtificial.frag", + "matrix.frag", + "matrix2.frag", + "mixedArrayDecls.frag", + "nonuniform.frag", + "newTexture.frag", + "Operations.frag", + "overlongLiteral.frag", + "prepost.frag", + "runtimeArray.vert", + "simpleFunctionCall.frag", + "stringToDouble.vert", + "structAssignment.frag", + "structDeref.frag", + "structure.frag", + "swizzle.frag", + "invalidSwizzle.vert", + "syntaxError.frag", + "test.frag", + "texture.frag", + "tokenPaste.vert", + "types.frag", + "uniformArray.frag", + "variableArrayIndex.frag", + "varyingArray.frag", + "varyingArrayIndirect.frag", + "voidFunction.frag", + "whileLoop.frag", + "nonVulkan.frag", + "negativeArraySize.comp", + "precise.tesc", + "precise_struct_block.vert", + "maxClipDistances.vert", + "findFunction.frag", + "constantUnaryConversion.comp", + "xfbUnsizedArray.error.vert", + "glsl.140.layoutOffset.error.vert", + "glsl.430.layoutOffset.error.vert", + "glsl.450.subgroup.frag", + "glsl.450.subgroup.geom", + "glsl.450.subgroup.tesc", + "glsl.450.subgroup.tese", + "glsl.450.subgroup.vert", + "glsl.450.subgroupArithmetic.comp", + "glsl.450.subgroupBasic.comp", + "glsl.450.subgroupBallot.comp", + "glsl.450.subgroupBallotNeg.comp", + "glsl.450.subgroupClustered.comp", + "glsl.450.subgroupClusteredNeg.comp", + "glsl.450.subgroupPartitioned.comp", + "glsl.450.subgroupShuffle.comp", + "glsl.450.subgroupShuffleRelative.comp", + "glsl.450.subgroupQuad.comp", + "glsl.450.subgroupVote.comp", + "glsl.460.subgroup.mesh", + "glsl.460.subgroup.task", + "glsl.460.subgroup.rahit", + "glsl.460.subgroup.rcall", + "glsl.460.subgroup.rchit", + "glsl.460.subgroup.rgen", + "glsl.460.subgroup.rint", + "glsl.460.subgroup.rmiss", + "glsl.es300.layoutOffset.error.vert", + "glsl.es320.subgroup.frag", + "glsl.es320.subgroup.geom", + "glsl.es320.subgroup.tesc", + "glsl.es320.subgroup.tese", + "glsl.es320.subgroup.vert", + "glsl.es320.subgroupArithmetic.comp", + "glsl.es320.subgroupBasic.comp", + "glsl.es320.subgroupBallot.comp", + "glsl.es320.subgroupBallotNeg.comp", + "glsl.es320.subgroupClustered.comp", + "glsl.es320.subgroupClusteredNeg.comp", + "glsl.es320.subgroupPartitioned.comp", + "glsl.es320.subgroupShuffle.comp", + "glsl.es320.subgroupShuffleRelative.comp", + "glsl.es320.subgroupQuad.comp", + "glsl.es320.subgroupVote.comp", + "terminate.frag", + "terminate.vert", + })), + FileNameAsCustomTestSuffix +); + +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileToAstTestNV, + ::testing::ValuesIn(std::vector({ + "nvShaderNoperspectiveInterpolation.frag", + })), + FileNameAsCustomTestSuffix +); + +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/BuiltInResource.FromFile.cpp b/third_party/glslang/gtests/BuiltInResource.FromFile.cpp new file mode 100644 index 0000000..da81fe9 --- /dev/null +++ b/third_party/glslang/gtests/BuiltInResource.FromFile.cpp @@ -0,0 +1,57 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include + +#include "StandAlone/ResourceLimits.h" +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +using DefaultResourceTest = GlslangTest<::testing::Test>; + +TEST_F(DefaultResourceTest, FromFile) +{ + const std::string path = GlobalTestSettings.testRoot + "/baseResults/test.conf"; + std::string expectedConfig; + tryLoadFile(path, "expected resource limit", &expectedConfig); + const std::string realConfig = glslang::GetDefaultTBuiltInResourceString(); + ASSERT_EQ(expectedConfig, realConfig); +} + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/CMakeLists.txt b/third_party/glslang/gtests/CMakeLists.txt new file mode 100644 index 0000000..0617ff8 --- /dev/null +++ b/third_party/glslang/gtests/CMakeLists.txt @@ -0,0 +1,108 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +if(BUILD_TESTING) + if(TARGET gmock) + message(STATUS "Google Mock found - building tests") + + set(TEST_SOURCES + # Framework related source files + ${CMAKE_CURRENT_SOURCE_DIR}/Initializer.h + ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Settings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Settings.h + ${CMAKE_CURRENT_SOURCE_DIR}/TestFixture.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TestFixture.h + + # Test related source files + ${CMAKE_CURRENT_SOURCE_DIR}/AST.FromFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/BuiltInResource.FromFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Config.FromFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/HexFloat.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Hlsl.FromFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Link.FromFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Link.FromFile.Vk.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Pp.FromFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Spv.FromFile.cpp) + + if(ENABLE_SPVREMAPPER) + set(TEST_SOURCES ${TEST_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/Remap.FromFile.cpp) + endif() + + + add_executable(glslangtests ${TEST_SOURCES}) + glslang_pch(glslangtests ${CMAKE_CURRENT_SOURCE_DIR}/pch.h) + set_property(TARGET glslangtests PROPERTY FOLDER tests) + glslang_set_link_args(glslangtests) + if(ENABLE_GLSLANG_INSTALL) + install(TARGETS glslangtests EXPORT glslangtestsTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(EXPORT glslangtestsTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) + endif(ENABLE_GLSLANG_INSTALL) + + set(GLSLANG_TEST_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../Test") + # Supply a default test root directory, so that manual testing + # doesn't have to specify the --test-root option in the normal + # case that you want to use the tests from the same source tree. + target_compile_definitions(glslangtests + PRIVATE GLSLANG_TEST_DIRECTORY="${GLSLANG_TEST_DIRECTORY}") + target_include_directories(glslangtests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR} + ${gmock_SOURCE_DIR}/include + ${gtest_SOURCE_DIR}/include) + + if(ENABLE_OPT) + target_include_directories(glslangtests + PRIVATE ${spirv-tools_SOURCE_DIR}/include + ) + endif(ENABLE_OPT) + + set(LIBRARIES + glslang OSDependent OGLCompiler glslang + SPIRV glslang-default-resource-limits) + + if(ENABLE_SPVREMAPPER) + set(LIBRARIES ${LIBRARIES} SPVRemapper) + endif() + + if(ENABLE_HLSL) + set(LIBRARIES ${LIBRARIES} HLSL) + endif(ENABLE_HLSL) + target_link_libraries(glslangtests PRIVATE ${LIBRARIES} gmock) + + add_test(NAME glslang-gtests + COMMAND glslangtests --test-root "${GLSLANG_TEST_DIRECTORY}") + endif() +endif() diff --git a/third_party/glslang/gtests/Config.FromFile.cpp b/third_party/glslang/gtests/Config.FromFile.cpp new file mode 100644 index 0000000..dd18c13 --- /dev/null +++ b/third_party/glslang/gtests/Config.FromFile.cpp @@ -0,0 +1,108 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "StandAlone/ResourceLimits.h" +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +struct TestCaseSpec { + std::string input; + std::string config; + std::string output; + EShMessages controls; +}; + +using ConfigTest = GlslangTest<::testing::TestWithParam>; + +TEST_P(ConfigTest, FromFile) +{ + TestCaseSpec testCase = GetParam(); + GlslangResult result; + result.validationResult = true; + + // Get the contents for input shader and limit configurations. + std::string shaderContents, configContents; + tryLoadFile(GlobalTestSettings.testRoot + "/" + testCase.input, "input", &shaderContents); + tryLoadFile(GlobalTestSettings.testRoot + "/" + testCase.config, "limits config", &configContents); + + // Decode limit configurations. + TBuiltInResource resources = {}; + { + const size_t len = configContents.size(); + char* configChars = new char[len + 1]; + memcpy(configChars, configContents.data(), len); + configChars[len] = 0; + glslang::DecodeResourceLimits(&resources, configChars); + delete[] configChars; + } + + // Compile the shader. + glslang::TShader shader(GetShaderStage(GetSuffix(testCase.input))); + compile(&shader, shaderContents, "", testCase.controls, &resources); + result.shaderResults.push_back( + {testCase.input, shader.getInfoLog(), shader.getInfoDebugLog()}); + + // Link the shader. + glslang::TProgram program; + program.addShader(&shader); + program.link(testCase.controls); + result.linkingOutput = program.getInfoLog(); + result.linkingError = program.getInfoDebugLog(); + + std::ostringstream stream; + outputResultToStream(&stream, result, testCase.controls); + + // Check with expected results. + const std::string expectedOutputFname = + GlobalTestSettings.testRoot + "/baseResults/" + testCase.output; + std::string expectedOutput; + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, ConfigTest, + ::testing::ValuesIn(std::vector({ + {"specExamples.vert", "baseResults/test.conf", "specExamplesConf.vert.out", (EShMessages)(EShMsgAST | EShMsgCascadingErrors)}, + {"100Limits.vert", "100.conf", "100LimitsConf.vert.out", EShMsgCascadingErrors}, + })) +); +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/HexFloat.cpp b/third_party/glslang/gtests/HexFloat.cpp new file mode 100644 index 0000000..0a11d96 --- /dev/null +++ b/third_party/glslang/gtests/HexFloat.cpp @@ -0,0 +1,1231 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include +#include "SPIRV/hex_float.h" + +namespace { +using ::testing::Eq; +using spvutils::BitwiseCast; +using spvutils::Float16; +using spvutils::FloatProxy; +using spvutils::HexFloat; +using spvutils::ParseNormalFloat; + +// In this file "encode" means converting a number into a string, +// and "decode" means converting a string into a number. + +using HexFloatTest = + ::testing::TestWithParam, std::string>>; +using DecodeHexFloatTest = + ::testing::TestWithParam>>; +using HexDoubleTest = + ::testing::TestWithParam, std::string>>; +using DecodeHexDoubleTest = + ::testing::TestWithParam>>; + +// Hex-encodes a float value. +template +std::string EncodeViaHexFloat(const T& value) { + std::stringstream ss; + ss << spvutils::HexFloat(value); + return ss.str(); +} + +// The following two tests can't be DRY because they take different parameter +// types. + +TEST_P(HexFloatTest, EncodeCorrectly) { + EXPECT_THAT(EncodeViaHexFloat(GetParam().first), Eq(GetParam().second)); +} + +TEST_P(HexDoubleTest, EncodeCorrectly) { + EXPECT_THAT(EncodeViaHexFloat(GetParam().first), Eq(GetParam().second)); +} + +// Decodes a hex-float string. +template +FloatProxy Decode(const std::string& str) { + spvutils::HexFloat> decoded(0.f); + EXPECT_TRUE((std::stringstream(str) >> decoded).eof()); + return decoded.value(); +} + +TEST_P(HexFloatTest, DecodeCorrectly) { + EXPECT_THAT(Decode(GetParam().second), Eq(GetParam().first)); +} + +TEST_P(HexDoubleTest, DecodeCorrectly) { + EXPECT_THAT(Decode(GetParam().second), Eq(GetParam().first)); +} + +INSTANTIATE_TEST_SUITE_P( + Float32Tests, HexFloatTest, + ::testing::ValuesIn(std::vector, std::string>>({ + {0.f, "0x0p+0"}, + {1.f, "0x1p+0"}, + {2.f, "0x1p+1"}, + {3.f, "0x1.8p+1"}, + {0.5f, "0x1p-1"}, + {0.25f, "0x1p-2"}, + {0.75f, "0x1.8p-1"}, + {-0.f, "-0x0p+0"}, + {-1.f, "-0x1p+0"}, + {-0.5f, "-0x1p-1"}, + {-0.25f, "-0x1p-2"}, + {-0.75f, "-0x1.8p-1"}, + + // Larger numbers + {512.f, "0x1p+9"}, + {-512.f, "-0x1p+9"}, + {1024.f, "0x1p+10"}, + {-1024.f, "-0x1p+10"}, + {1024.f + 8.f, "0x1.02p+10"}, + {-1024.f - 8.f, "-0x1.02p+10"}, + + // Small numbers + {1.0f / 512.f, "0x1p-9"}, + {1.0f / -512.f, "-0x1p-9"}, + {1.0f / 1024.f, "0x1p-10"}, + {1.0f / -1024.f, "-0x1p-10"}, + {1.0f / 1024.f + 1.0f / 8.f, "0x1.02p-3"}, + {1.0f / -1024.f - 1.0f / 8.f, "-0x1.02p-3"}, + + // lowest non-denorm + {float(ldexp(1.0f, -126)), "0x1p-126"}, + {float(ldexp(-1.0f, -126)), "-0x1p-126"}, + + // Denormalized values + {float(ldexp(1.0f, -127)), "0x1p-127"}, + {float(ldexp(1.0f, -127) / 2.0f), "0x1p-128"}, + {float(ldexp(1.0f, -127) / 4.0f), "0x1p-129"}, + {float(ldexp(1.0f, -127) / 8.0f), "0x1p-130"}, + {float(ldexp(-1.0f, -127)), "-0x1p-127"}, + {float(ldexp(-1.0f, -127) / 2.0f), "-0x1p-128"}, + {float(ldexp(-1.0f, -127) / 4.0f), "-0x1p-129"}, + {float(ldexp(-1.0f, -127) / 8.0f), "-0x1p-130"}, + + {float(ldexp(1.0, -127) + (ldexp(1.0, -127) / 2.0f)), "0x1.8p-127"}, + {float(ldexp(1.0, -127) / 2.0 + (ldexp(1.0, -127) / 4.0f)), + "0x1.8p-128"}, + + }))); + +INSTANTIATE_TEST_SUITE_P( + Float32NanTests, HexFloatTest, + ::testing::ValuesIn(std::vector, std::string>>({ + // Various NAN and INF cases + {uint32_t(0xFF800000), "-0x1p+128"}, // -inf + {uint32_t(0x7F800000), "0x1p+128"}, // inf + {uint32_t(0xFFC00000), "-0x1.8p+128"}, // -nan + {uint32_t(0xFF800100), "-0x1.0002p+128"}, // -nan + {uint32_t(0xFF800c00), "-0x1.0018p+128"}, // -nan + {uint32_t(0xFF80F000), "-0x1.01ep+128"}, // -nan + {uint32_t(0xFFFFFFFF), "-0x1.fffffep+128"}, // -nan + {uint32_t(0x7FC00000), "0x1.8p+128"}, // +nan + {uint32_t(0x7F800100), "0x1.0002p+128"}, // +nan + {uint32_t(0x7f800c00), "0x1.0018p+128"}, // +nan + {uint32_t(0x7F80F000), "0x1.01ep+128"}, // +nan + {uint32_t(0x7FFFFFFF), "0x1.fffffep+128"}, // +nan + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64Tests, HexDoubleTest, + ::testing::ValuesIn( + std::vector, std::string>>({ + {0., "0x0p+0"}, + {1., "0x1p+0"}, + {2., "0x1p+1"}, + {3., "0x1.8p+1"}, + {0.5, "0x1p-1"}, + {0.25, "0x1p-2"}, + {0.75, "0x1.8p-1"}, + {-0., "-0x0p+0"}, + {-1., "-0x1p+0"}, + {-0.5, "-0x1p-1"}, + {-0.25, "-0x1p-2"}, + {-0.75, "-0x1.8p-1"}, + + // Larger numbers + {512., "0x1p+9"}, + {-512., "-0x1p+9"}, + {1024., "0x1p+10"}, + {-1024., "-0x1p+10"}, + {1024. + 8., "0x1.02p+10"}, + {-1024. - 8., "-0x1.02p+10"}, + + // Large outside the range of normal floats + {ldexp(1.0, 128), "0x1p+128"}, + {ldexp(1.0, 129), "0x1p+129"}, + {ldexp(-1.0, 128), "-0x1p+128"}, + {ldexp(-1.0, 129), "-0x1p+129"}, + {ldexp(1.0, 128) + ldexp(1.0, 90), "0x1.0000000004p+128"}, + {ldexp(1.0, 129) + ldexp(1.0, 120), "0x1.008p+129"}, + {ldexp(-1.0, 128) + ldexp(1.0, 90), "-0x1.fffffffff8p+127"}, + {ldexp(-1.0, 129) + ldexp(1.0, 120), "-0x1.ffp+128"}, + + // Small numbers + {1.0 / 512., "0x1p-9"}, + {1.0 / -512., "-0x1p-9"}, + {1.0 / 1024., "0x1p-10"}, + {1.0 / -1024., "-0x1p-10"}, + {1.0 / 1024. + 1.0 / 8., "0x1.02p-3"}, + {1.0 / -1024. - 1.0 / 8., "-0x1.02p-3"}, + + // Small outside the range of normal floats + {ldexp(1.0, -128), "0x1p-128"}, + {ldexp(1.0, -129), "0x1p-129"}, + {ldexp(-1.0, -128), "-0x1p-128"}, + {ldexp(-1.0, -129), "-0x1p-129"}, + {ldexp(1.0, -128) + ldexp(1.0, -90), "0x1.0000000004p-90"}, + {ldexp(1.0, -129) + ldexp(1.0, -120), "0x1.008p-120"}, + {ldexp(-1.0, -128) + ldexp(1.0, -90), "0x1.fffffffff8p-91"}, + {ldexp(-1.0, -129) + ldexp(1.0, -120), "0x1.ffp-121"}, + + // lowest non-denorm + {ldexp(1.0, -1022), "0x1p-1022"}, + {ldexp(-1.0, -1022), "-0x1p-1022"}, + + // Denormalized values + {ldexp(1.0, -1023), "0x1p-1023"}, + {ldexp(1.0, -1023) / 2.0, "0x1p-1024"}, + {ldexp(1.0, -1023) / 4.0, "0x1p-1025"}, + {ldexp(1.0, -1023) / 8.0, "0x1p-1026"}, + {ldexp(-1.0, -1024), "-0x1p-1024"}, + {ldexp(-1.0, -1024) / 2.0, "-0x1p-1025"}, + {ldexp(-1.0, -1024) / 4.0, "-0x1p-1026"}, + {ldexp(-1.0, -1024) / 8.0, "-0x1p-1027"}, + + {ldexp(1.0, -1023) + (ldexp(1.0, -1023) / 2.0), "0x1.8p-1023"}, + {ldexp(1.0, -1023) / 2.0 + (ldexp(1.0, -1023) / 4.0), + "0x1.8p-1024"}, + + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64NanTests, HexDoubleTest, + ::testing::ValuesIn(std::vector< + std::pair, std::string>>({ + // Various NAN and INF cases + {uint64_t(0xFFF0000000000000LL), "-0x1p+1024"}, //-inf + {uint64_t(0x7FF0000000000000LL), "0x1p+1024"}, //+inf + {uint64_t(0xFFF8000000000000LL), "-0x1.8p+1024"}, // -nan + {uint64_t(0xFFF0F00000000000LL), "-0x1.0fp+1024"}, // -nan + {uint64_t(0xFFF0000000000001LL), "-0x1.0000000000001p+1024"}, // -nan + {uint64_t(0xFFF0000300000000LL), "-0x1.00003p+1024"}, // -nan + {uint64_t(0xFFFFFFFFFFFFFFFFLL), "-0x1.fffffffffffffp+1024"}, // -nan + {uint64_t(0x7FF8000000000000LL), "0x1.8p+1024"}, // +nan + {uint64_t(0x7FF0F00000000000LL), "0x1.0fp+1024"}, // +nan + {uint64_t(0x7FF0000000000001LL), "0x1.0000000000001p+1024"}, // -nan + {uint64_t(0x7FF0000300000000LL), "0x1.00003p+1024"}, // -nan + {uint64_t(0x7FFFFFFFFFFFFFFFLL), "0x1.fffffffffffffp+1024"}, // -nan + }))); + +TEST(HexFloatStreamTest, OperatorLeftShiftPreservesFloatAndFill) { + std::stringstream s; + s << std::setw(4) << std::oct << std::setfill('x') << 8 << " " + << FloatProxy(uint32_t(0xFF800100)) << " " << std::setw(4) << 9; + EXPECT_THAT(s.str(), Eq(std::string("xx10 -0x1.0002p+128 xx11"))); +} + +TEST(HexDoubleStreamTest, OperatorLeftShiftPreservesFloatAndFill) { + std::stringstream s; + s << std::setw(4) << std::oct << std::setfill('x') << 8 << " " + << FloatProxy(uint64_t(0x7FF0F00000000000LL)) << " " << std::setw(4) + << 9; + EXPECT_THAT(s.str(), Eq(std::string("xx10 0x1.0fp+1024 xx11"))); +} + +TEST_P(DecodeHexFloatTest, DecodeCorrectly) { + EXPECT_THAT(Decode(GetParam().first), Eq(GetParam().second)); +} + +TEST_P(DecodeHexDoubleTest, DecodeCorrectly) { + EXPECT_THAT(Decode(GetParam().first), Eq(GetParam().second)); +} + +INSTANTIATE_TEST_SUITE_P( + Float32DecodeTests, DecodeHexFloatTest, + ::testing::ValuesIn(std::vector>>({ + {"0x0p+000", 0.f}, + {"0x0p0", 0.f}, + {"0x0p-0", 0.f}, + + // flush to zero cases + {"0x1p-500", 0.f}, // Exponent underflows. + {"-0x1p-500", -0.f}, + {"0x0.00000000001p-126", 0.f}, // Fraction causes underflow. + {"-0x0.0000000001p-127", -0.f}, + {"-0x0.01p-142", -0.f}, // Fraction causes additional underflow. + {"0x0.01p-142", 0.f}, + + // Some floats that do not encode the same way as they decode. + {"0x2p+0", 2.f}, + {"0xFFp+0", 255.f}, + {"0x0.8p+0", 0.5f}, + {"0x0.4p+0", 0.25f}, + }))); + +INSTANTIATE_TEST_SUITE_P( + Float32DecodeInfTests, DecodeHexFloatTest, + ::testing::ValuesIn(std::vector>>({ + // inf cases + {"-0x1p+128", uint32_t(0xFF800000)}, // -inf + {"0x32p+127", uint32_t(0x7F800000)}, // inf + {"0x32p+500", uint32_t(0x7F800000)}, // inf + {"-0x32p+127", uint32_t(0xFF800000)}, // -inf + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64DecodeTests, DecodeHexDoubleTest, + ::testing::ValuesIn( + std::vector>>({ + {"0x0p+000", 0.}, + {"0x0p0", 0.}, + {"0x0p-0", 0.}, + + // flush to zero cases + {"0x1p-5000", 0.}, // Exponent underflows. + {"-0x1p-5000", -0.}, + {"0x0.0000000000000001p-1023", 0.}, // Fraction causes underflow. + {"-0x0.000000000000001p-1024", -0.}, + {"-0x0.01p-1090", -0.f}, // Fraction causes additional underflow. + {"0x0.01p-1090", 0.}, + + // Some floats that do not encode the same way as they decode. + {"0x2p+0", 2.}, + {"0xFFp+0", 255.}, + {"0x0.8p+0", 0.5}, + {"0x0.4p+0", 0.25}, + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64DecodeInfTests, DecodeHexDoubleTest, + ::testing::ValuesIn( + std::vector>>({ + // inf cases + {"-0x1p+1024", uint64_t(0xFFF0000000000000)}, // -inf + {"0x32p+1023", uint64_t(0x7FF0000000000000)}, // inf + {"0x32p+5000", uint64_t(0x7FF0000000000000)}, // inf + {"-0x32p+1023", uint64_t(0xFFF0000000000000)}, // -inf + }))); + +TEST(FloatProxy, ValidConversion) { + EXPECT_THAT(FloatProxy(1.f).getAsFloat(), Eq(1.0f)); + EXPECT_THAT(FloatProxy(32.f).getAsFloat(), Eq(32.0f)); + EXPECT_THAT(FloatProxy(-1.f).getAsFloat(), Eq(-1.0f)); + EXPECT_THAT(FloatProxy(0.f).getAsFloat(), Eq(0.0f)); + EXPECT_THAT(FloatProxy(-0.f).getAsFloat(), Eq(-0.0f)); + EXPECT_THAT(FloatProxy(1.2e32f).getAsFloat(), Eq(1.2e32f)); + + EXPECT_TRUE(std::isinf(FloatProxy(uint32_t(0xFF800000)).getAsFloat())); + EXPECT_TRUE(std::isinf(FloatProxy(uint32_t(0x7F800000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFFC00000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFF800100)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFF800c00)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFF80F000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFFFFFFFF)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7FC00000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7F800100)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7f800c00)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7F80F000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7FFFFFFF)).getAsFloat())); + + EXPECT_THAT(FloatProxy(uint32_t(0xFF800000)).data(), Eq(0xFF800000u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F800000)).data(), Eq(0x7F800000u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFFC00000)).data(), Eq(0xFFC00000u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF800100)).data(), Eq(0xFF800100u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF800c00)).data(), Eq(0xFF800c00u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF80F000)).data(), Eq(0xFF80F000u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFFFFFFFF)).data(), Eq(0xFFFFFFFFu)); + EXPECT_THAT(FloatProxy(uint32_t(0x7FC00000)).data(), Eq(0x7FC00000u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F800100)).data(), Eq(0x7F800100u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7f800c00)).data(), Eq(0x7f800c00u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F80F000)).data(), Eq(0x7F80F000u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7FFFFFFF)).data(), Eq(0x7FFFFFFFu)); +} + +TEST(FloatProxy, Nan) { + EXPECT_TRUE(FloatProxy(uint32_t(0xFFC00000)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0xFF800100)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0xFF800c00)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0xFF80F000)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0xFFFFFFFF)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7FC00000)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7F800100)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7f800c00)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7F80F000)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7FFFFFFF)).isNan()); +} + +TEST(FloatProxy, Negation) { + EXPECT_THAT((-FloatProxy(1.f)).getAsFloat(), Eq(-1.0f)); + EXPECT_THAT((-FloatProxy(0.f)).getAsFloat(), Eq(-0.0f)); + + EXPECT_THAT((-FloatProxy(-1.f)).getAsFloat(), Eq(1.0f)); + EXPECT_THAT((-FloatProxy(-0.f)).getAsFloat(), Eq(0.0f)); + + EXPECT_THAT((-FloatProxy(32.f)).getAsFloat(), Eq(-32.0f)); + EXPECT_THAT((-FloatProxy(-32.f)).getAsFloat(), Eq(32.0f)); + + EXPECT_THAT((-FloatProxy(1.2e32f)).getAsFloat(), Eq(-1.2e32f)); + EXPECT_THAT((-FloatProxy(-1.2e32f)).getAsFloat(), Eq(1.2e32f)); + + EXPECT_THAT( + (-FloatProxy(std::numeric_limits::infinity())).getAsFloat(), + Eq(-std::numeric_limits::infinity())); + EXPECT_THAT((-FloatProxy(-std::numeric_limits::infinity())) + .getAsFloat(), + Eq(std::numeric_limits::infinity())); +} + +// Test conversion of FloatProxy values to strings. +// +// In previous cases, we always wrapped the FloatProxy value in a HexFloat +// before conversion to a string. In the following cases, the FloatProxy +// decides for itself whether to print as a regular number or as a hex float. + +using FloatProxyFloatTest = + ::testing::TestWithParam, std::string>>; +using FloatProxyDoubleTest = + ::testing::TestWithParam, std::string>>; + +// Converts a float value to a string via a FloatProxy. +template +std::string EncodeViaFloatProxy(const T& value) { + std::stringstream ss; + ss << value; + return ss.str(); +} + +// Converts a floating point string so that the exponent prefix +// is 'e', and the exponent value does not have leading zeros. +// The Microsoft runtime library likes to write things like "2.5E+010". +// Convert that to "2.5e+10". +// We don't care what happens to strings that are not floating point +// strings. +std::string NormalizeExponentInFloatString(std::string in) { + std::string result; + // Reserve one spot for the terminating null, even when the sscanf fails. + std::vector prefix(in.size() + 1); + char e; + char plus_or_minus; + int exponent; // in base 10 + if ((4 == std::sscanf(in.c_str(), "%[-+.0123456789]%c%c%d", prefix.data(), &e, + &plus_or_minus, &exponent)) && + (e == 'e' || e == 'E') && + (plus_or_minus == '-' || plus_or_minus == '+')) { + // It looks like a floating point value with exponent. + std::stringstream out; + out << prefix.data() << 'e' << plus_or_minus << exponent; + result = out.str(); + } else { + result = in; + } + return result; +} + +TEST(NormalizeFloat, Sample) { + EXPECT_THAT(NormalizeExponentInFloatString(""), Eq("")); + EXPECT_THAT(NormalizeExponentInFloatString("1e-12"), Eq("1e-12")); + EXPECT_THAT(NormalizeExponentInFloatString("1E+14"), Eq("1e+14")); + EXPECT_THAT(NormalizeExponentInFloatString("1e-0012"), Eq("1e-12")); + EXPECT_THAT(NormalizeExponentInFloatString("1.263E+014"), Eq("1.263e+14")); +} + +// The following two tests can't be DRY because they take different parameter +// types. +TEST_P(FloatProxyFloatTest, EncodeCorrectly) { + EXPECT_THAT( + NormalizeExponentInFloatString(EncodeViaFloatProxy(GetParam().first)), + Eq(GetParam().second)); +} + +TEST_P(FloatProxyDoubleTest, EncodeCorrectly) { + EXPECT_THAT( + NormalizeExponentInFloatString(EncodeViaFloatProxy(GetParam().first)), + Eq(GetParam().second)); +} + +INSTANTIATE_TEST_SUITE_P( + Float32Tests, FloatProxyFloatTest, + ::testing::ValuesIn(std::vector, std::string>>({ + // Zero + {0.f, "0"}, + // Normal numbers + {1.f, "1"}, + {-0.25f, "-0.25"}, + {1000.0f, "1000"}, + + // Still normal numbers, but with large magnitude exponents. + {float(ldexp(1.f, 126)), "8.50706e+37"}, + {float(ldexp(-1.f, -126)), "-1.17549e-38"}, + + // denormalized values are printed as hex floats. + {float(ldexp(1.0f, -127)), "0x1p-127"}, + {float(ldexp(1.5f, -128)), "0x1.8p-128"}, + {float(ldexp(1.25, -129)), "0x1.4p-129"}, + {float(ldexp(1.125, -130)), "0x1.2p-130"}, + {float(ldexp(-1.0f, -127)), "-0x1p-127"}, + {float(ldexp(-1.0f, -128)), "-0x1p-128"}, + {float(ldexp(-1.0f, -129)), "-0x1p-129"}, + {float(ldexp(-1.5f, -130)), "-0x1.8p-130"}, + + // NaNs + {FloatProxy(uint32_t(0xFFC00000)), "-0x1.8p+128"}, + {FloatProxy(uint32_t(0xFF800100)), "-0x1.0002p+128"}, + + {std::numeric_limits::infinity(), "0x1p+128"}, + {-std::numeric_limits::infinity(), "-0x1p+128"}, + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64Tests, FloatProxyDoubleTest, + ::testing::ValuesIn( + std::vector, std::string>>({ + {0., "0"}, + {1., "1"}, + {-0.25, "-0.25"}, + {1000.0, "1000"}, + + // Large outside the range of normal floats + {ldexp(1.0, 128), "3.40282366920938e+38"}, + {ldexp(1.5, 129), "1.02084710076282e+39"}, + {ldexp(-1.0, 128), "-3.40282366920938e+38"}, + {ldexp(-1.5, 129), "-1.02084710076282e+39"}, + + // Small outside the range of normal floats + {ldexp(1.5, -129), "2.20405190779179e-39"}, + {ldexp(-1.5, -129), "-2.20405190779179e-39"}, + + // lowest non-denorm + {ldexp(1.0, -1022), "2.2250738585072e-308"}, + {ldexp(-1.0, -1022), "-2.2250738585072e-308"}, + + // Denormalized values + {ldexp(1.125, -1023), "0x1.2p-1023"}, + {ldexp(-1.375, -1024), "-0x1.6p-1024"}, + + // NaNs + {uint64_t(0x7FF8000000000000LL), "0x1.8p+1024"}, + {uint64_t(0xFFF0F00000000000LL), "-0x1.0fp+1024"}, + + // Infinity + {std::numeric_limits::infinity(), "0x1p+1024"}, + {-std::numeric_limits::infinity(), "-0x1p+1024"}, + + }))); + +// double is used so that unbiased_exponent can be used with the output +// of ldexp directly. +int32_t unbiased_exponent(double f) { + return spvutils::HexFloat>( + static_cast(f)).getUnbiasedNormalizedExponent(); +} + +int16_t unbiased_half_exponent(uint16_t f) { + return spvutils::HexFloat>(f) + .getUnbiasedNormalizedExponent(); +} + +TEST(HexFloatOperationTest, UnbiasedExponent) { + // Float cases + EXPECT_EQ(0, unbiased_exponent(ldexp(1.0f, 0))); + EXPECT_EQ(-32, unbiased_exponent(ldexp(1.0f, -32))); + EXPECT_EQ(42, unbiased_exponent(ldexp(1.0f, 42))); + EXPECT_EQ(125, unbiased_exponent(ldexp(1.0f, 125))); + // Saturates to 128 + EXPECT_EQ(128, unbiased_exponent(ldexp(1.0f, 256))); + + EXPECT_EQ(-100, unbiased_exponent(ldexp(1.0f, -100))); + EXPECT_EQ(-127, unbiased_exponent(ldexp(1.0f, -127))); // First denorm + EXPECT_EQ(-128, unbiased_exponent(ldexp(1.0f, -128))); + EXPECT_EQ(-129, unbiased_exponent(ldexp(1.0f, -129))); + EXPECT_EQ(-140, unbiased_exponent(ldexp(1.0f, -140))); + // Smallest representable number + EXPECT_EQ(-126 - 23, unbiased_exponent(ldexp(1.0f, -126 - 23))); + // Should get rounded to 0 first. + EXPECT_EQ(0, unbiased_exponent(ldexp(1.0f, -127 - 23))); + + // Float16 cases + // The exponent is represented in the bits 0x7C00 + // The offset is -15 + EXPECT_EQ(0, unbiased_half_exponent(0x3C00)); + EXPECT_EQ(3, unbiased_half_exponent(0x4800)); + EXPECT_EQ(-1, unbiased_half_exponent(0x3800)); + EXPECT_EQ(-14, unbiased_half_exponent(0x0400)); + EXPECT_EQ(16, unbiased_half_exponent(0x7C00)); + EXPECT_EQ(10, unbiased_half_exponent(0x6400)); + + // Smallest representable number + EXPECT_EQ(-24, unbiased_half_exponent(0x0001)); +} + +// Creates a float that is the sum of 1/(2 ^ fractions[i]) for i in factions +float float_fractions(const std::vector& fractions) { + float f = 0; + for(int32_t i: fractions) { + f += std::ldexp(1.0f, -i); + } + return f; +} + +// Returns the normalized significand of a HexFloat> +// that was created by calling float_fractions with the input fractions, +// raised to the power of exp. +uint32_t normalized_significand(const std::vector& fractions, uint32_t exp) { + return spvutils::HexFloat>( + static_cast(ldexp(float_fractions(fractions), exp))) + .getNormalizedSignificand(); +} + +// Sets the bits from MSB to LSB of the significand part of a float. +// For example 0 would set the bit 23 (counting from LSB to MSB), +// and 1 would set the 22nd bit. +uint32_t bits_set(const std::vector& bits) { + const uint32_t top_bit = 1u << 22u; + uint32_t val= 0; + for(uint32_t i: bits) { + val |= top_bit >> i; + } + return val; +} + +// The same as bits_set but for a Float16 value instead of 32-bit floating +// point. +uint16_t half_bits_set(const std::vector& bits) { + const uint32_t top_bit = 1u << 9u; + uint32_t val= 0; + for(uint32_t i: bits) { + val |= top_bit >> i; + } + return static_cast(val); +} + +TEST(HexFloatOperationTest, NormalizedSignificand) { + // For normalized numbers (the following) it should be a simple matter + // of getting rid of the top implicit bit + EXPECT_EQ(bits_set({}), normalized_significand({0}, 0)); + EXPECT_EQ(bits_set({0}), normalized_significand({0, 1}, 0)); + EXPECT_EQ(bits_set({0, 1}), normalized_significand({0, 1, 2}, 0)); + EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 0)); + EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 32)); + EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 126)); + + // For denormalized numbers we expect the normalized significand to + // shift as if it were normalized. This means, in practice that the + // top_most set bit will be cut off. Looks very similar to above (on purpose) + EXPECT_EQ(bits_set({}), normalized_significand({0}, -127)); + EXPECT_EQ(bits_set({3}), normalized_significand({0, 4}, -128)); + EXPECT_EQ(bits_set({3}), normalized_significand({0, 4}, -127)); + EXPECT_EQ(bits_set({}), normalized_significand({22}, -127)); + EXPECT_EQ(bits_set({0}), normalized_significand({21, 22}, -127)); +} + +// Returns the 32-bit floating point value created by +// calling setFromSignUnbiasedExponentAndNormalizedSignificand +// on a HexFloat> +float set_from_sign(bool negative, int32_t unbiased_exponent, + uint32_t significand, bool round_denorm_up) { + spvutils::HexFloat> f(0.f); + f.setFromSignUnbiasedExponentAndNormalizedSignificand( + negative, unbiased_exponent, significand, round_denorm_up); + return f.value().getAsFloat(); +} + +TEST(HexFloatOperationTests, + SetFromSignUnbiasedExponentAndNormalizedSignificand) { + + EXPECT_EQ(1.f, set_from_sign(false, 0, 0, false)); + + // Tests insertion of various denormalized numbers with and without round up. + EXPECT_EQ(static_cast(ldexp(1.f, -149)), set_from_sign(false, -149, 0, false)); + EXPECT_EQ(static_cast(ldexp(1.f, -149)), set_from_sign(false, -149, 0, true)); + EXPECT_EQ(0.f, set_from_sign(false, -150, 1, false)); + EXPECT_EQ(static_cast(ldexp(1.f, -149)), set_from_sign(false, -150, 1, true)); + + EXPECT_EQ(ldexp(1.0f, -127), set_from_sign(false, -127, 0, false)); + EXPECT_EQ(ldexp(1.0f, -128), set_from_sign(false, -128, 0, false)); + EXPECT_EQ(float_fractions({0, 1, 2, 5}), + set_from_sign(false, 0, bits_set({0, 1, 4}), false)); + EXPECT_EQ(ldexp(float_fractions({0, 1, 2, 5}), -32), + set_from_sign(false, -32, bits_set({0, 1, 4}), false)); + EXPECT_EQ(ldexp(float_fractions({0, 1, 2, 5}), -128), + set_from_sign(false, -128, bits_set({0, 1, 4}), false)); + + // The negative cases from above. + EXPECT_EQ(-1.f, set_from_sign(true, 0, 0, false)); + EXPECT_EQ(-ldexp(1.0, -127), set_from_sign(true, -127, 0, false)); + EXPECT_EQ(-ldexp(1.0, -128), set_from_sign(true, -128, 0, false)); + EXPECT_EQ(-float_fractions({0, 1, 2, 5}), + set_from_sign(true, 0, bits_set({0, 1, 4}), false)); + EXPECT_EQ(-ldexp(float_fractions({0, 1, 2, 5}), -32), + set_from_sign(true, -32, bits_set({0, 1, 4}), false)); + EXPECT_EQ(-ldexp(float_fractions({0, 1, 2, 5}), -128), + set_from_sign(true, -128, bits_set({0, 1, 4}), false)); +} + +TEST(HexFloatOperationTests, NonRounding) { + // Rounding from 32-bit hex-float to 32-bit hex-float should be trivial, + // except in the denorm case which is a bit more complex. + using HF = spvutils::HexFloat>; + bool carry_bit = false; + + spvutils::round_direction rounding[] = { + spvutils::kRoundToZero, + spvutils::kRoundToNearestEven, + spvutils::kRoundToPositiveInfinity, + spvutils::kRoundToNegativeInfinity}; + + // Everything fits, so this should be straight-forward + for (spvutils::round_direction round : rounding) { + EXPECT_EQ(bits_set({}), HF(0.f).getRoundedNormalizedSignificand( + round, &carry_bit)); + EXPECT_FALSE(carry_bit); + + EXPECT_EQ(bits_set({0}), + HF(float_fractions({0, 1})) + .getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + + EXPECT_EQ(bits_set({1, 3}), + HF(float_fractions({0, 2, 4})) + .getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + + EXPECT_EQ( + bits_set({0, 1, 4}), + HF(static_cast(-ldexp(float_fractions({0, 1, 2, 5}), -128))) + .getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + + EXPECT_EQ( + bits_set({0, 1, 4, 22}), + HF(static_cast(float_fractions({0, 1, 2, 5, 23}))) + .getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + } +} + +struct RoundSignificandCase { + float source_float; + std::pair expected_results; + spvutils::round_direction round; +}; + +using HexFloatRoundTest = + ::testing::TestWithParam; + +TEST_P(HexFloatRoundTest, RoundDownToFP16) { + using HF = spvutils::HexFloat>; + using HF16 = spvutils::HexFloat>; + + HF input_value(GetParam().source_float); + bool carry_bit = false; + EXPECT_EQ(GetParam().expected_results.first, + input_value.getRoundedNormalizedSignificand( + GetParam().round, &carry_bit)); + EXPECT_EQ(carry_bit, GetParam().expected_results.second); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(F32ToF16, HexFloatRoundTest, + ::testing::ValuesIn(std::vector( + { + {float_fractions({0}), std::make_pair(half_bits_set({}), false), spvutils::kRoundToZero}, + {float_fractions({0}), std::make_pair(half_bits_set({}), false), spvutils::kRoundToNearestEven}, + {float_fractions({0}), std::make_pair(half_bits_set({}), false), spvutils::kRoundToPositiveInfinity}, + {float_fractions({0}), std::make_pair(half_bits_set({}), false), spvutils::kRoundToNegativeInfinity}, + {float_fractions({0, 1}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToZero}, + + {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToZero}, + {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToPositiveInfinity}, + {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToNegativeInfinity}, + {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToNearestEven}, + + {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToZero}, + {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 8}), false), spvutils::kRoundToPositiveInfinity}, + {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToNegativeInfinity}, + {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 8}), false), spvutils::kRoundToNearestEven}, + + {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToZero}, + {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToPositiveInfinity}, + {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToNegativeInfinity}, + {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToNearestEven}, + + {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToZero}, + {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToPositiveInfinity}, + {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToNegativeInfinity}, + {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToNearestEven}, + + {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToZero}, + {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToPositiveInfinity}, + {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToNegativeInfinity}, + {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToNearestEven}, + + // Carries + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), false), spvutils::kRoundToZero}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({}), true), spvutils::kRoundToPositiveInfinity}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), false), spvutils::kRoundToNegativeInfinity}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({}), true), spvutils::kRoundToNearestEven}, + + // Cases where original number was denorm. Note: this should have no effect + // the number is pre-normalized. + {static_cast(ldexp(float_fractions({0, 1, 11, 13}), -128)), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToZero}, + {static_cast(ldexp(float_fractions({0, 1, 11, 13}), -129)), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToPositiveInfinity}, + {static_cast(ldexp(float_fractions({0, 1, 11, 13}), -131)), std::make_pair(half_bits_set({0}), false), spvutils::kRoundToNegativeInfinity}, + {static_cast(ldexp(float_fractions({0, 1, 11, 13}), -130)), std::make_pair(half_bits_set({0, 9}), false), spvutils::kRoundToNearestEven}, + }))); +// clang-format on + +struct UpCastSignificandCase { + uint16_t source_half; + uint32_t expected_result; +}; + +using HexFloatRoundUpSignificandTest = + ::testing::TestWithParam; +TEST_P(HexFloatRoundUpSignificandTest, Widening) { + using HF = spvutils::HexFloat>; + using HF16 = spvutils::HexFloat>; + bool carry_bit = false; + + spvutils::round_direction rounding[] = { + spvutils::kRoundToZero, + spvutils::kRoundToNearestEven, + spvutils::kRoundToPositiveInfinity, + spvutils::kRoundToNegativeInfinity}; + + // Everything fits, so everything should just be bit-shifts. + for (spvutils::round_direction round : rounding) { + carry_bit = false; + HF16 input_value(GetParam().source_half); + EXPECT_EQ( + GetParam().expected_result, + input_value.getRoundedNormalizedSignificand(round, &carry_bit)) + << std::hex << "0x" + << input_value.getRoundedNormalizedSignificand(round, &carry_bit) + << " 0x" << GetParam().expected_result; + EXPECT_FALSE(carry_bit); + } +} + +INSTANTIATE_TEST_SUITE_P(F16toF32, HexFloatRoundUpSignificandTest, + // 0xFC00 of the source 16-bit hex value cover the sign and the exponent. + // They are ignored for this test. + ::testing::ValuesIn(std::vector( + { + {0x3F00, 0x600000}, + {0x0F00, 0x600000}, + {0x0F01, 0x602000}, + {0x0FFF, 0x7FE000}, + }))); + +struct DownCastTest { + float source_float; + uint16_t expected_half; + std::vector directions; +}; + +std::string get_round_text(spvutils::round_direction direction) { +#define CASE(round_direction) \ + case round_direction: \ + return #round_direction + + switch (direction) { + CASE(spvutils::kRoundToZero); + CASE(spvutils::kRoundToPositiveInfinity); + CASE(spvutils::kRoundToNegativeInfinity); + CASE(spvutils::kRoundToNearestEven); + } +#undef CASE + return ""; +} + +using HexFloatFP32To16Tests = ::testing::TestWithParam; + +TEST_P(HexFloatFP32To16Tests, NarrowingCasts) { + using HF = spvutils::HexFloat>; + using HF16 = spvutils::HexFloat>; + HF f(GetParam().source_float); + for (auto round : GetParam().directions) { + HF16 half(0); + f.castTo(half, round); + EXPECT_EQ(GetParam().expected_half, half.value().getAsFloat().get_value()) + << get_round_text(round) << " " << std::hex + << spvutils::BitwiseCast(GetParam().source_float) + << " cast to: " << half.value().getAsFloat().get_value(); + } +} + +const uint16_t positive_infinity = 0x7C00; +const uint16_t negative_infinity = 0xFC00; + +INSTANTIATE_TEST_SUITE_P(F32ToF16, HexFloatFP32To16Tests, + ::testing::ValuesIn(std::vector( + { + // Exactly representable as half. + {0.f, 0x0, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {-0.f, 0x8000, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {1.0f, 0x3C00, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {-1.0f, 0xBC00, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + + {float_fractions({0, 1, 10}) , 0x3E01, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {-float_fractions({0, 1, 10}) , 0xBE01, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(ldexp(float_fractions({0, 1, 10}), 3)), 0x4A01, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(-ldexp(float_fractions({0, 1, 10}), 3)), 0xCA01, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + + + // Underflow + {static_cast(ldexp(1.0f, -25)), 0x0, {spvutils::kRoundToZero, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(ldexp(1.0f, -25)), 0x1, {spvutils::kRoundToPositiveInfinity}}, + {static_cast(-ldexp(1.0f, -25)), 0x8000, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(-ldexp(1.0f, -25)), 0x8001, {spvutils::kRoundToNegativeInfinity}}, + {static_cast(ldexp(1.0f, -24)), 0x1, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + + // Overflow + {static_cast(ldexp(1.0f, 16)), positive_infinity, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(ldexp(1.0f, 18)), positive_infinity, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(ldexp(1.3f, 16)), positive_infinity, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(-ldexp(1.0f, 16)), negative_infinity, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(-ldexp(1.0f, 18)), negative_infinity, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {static_cast(-ldexp(1.3f, 16)), negative_infinity, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + + // Transfer of Infinities + {std::numeric_limits::infinity(), positive_infinity, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + {-std::numeric_limits::infinity(), negative_infinity, {spvutils::kRoundToZero, spvutils::kRoundToPositiveInfinity, spvutils::kRoundToNegativeInfinity, spvutils::kRoundToNearestEven}}, + + // Nans are below because we cannot test for equality. + }))); + +struct UpCastCase{ + uint16_t source_half; + float expected_float; +}; + +using HexFloatFP16To32Tests = ::testing::TestWithParam; +TEST_P(HexFloatFP16To32Tests, WideningCasts) { + using HF = spvutils::HexFloat>; + using HF16 = spvutils::HexFloat>; + HF16 f(GetParam().source_half); + + spvutils::round_direction rounding[] = { + spvutils::kRoundToZero, + spvutils::kRoundToNearestEven, + spvutils::kRoundToPositiveInfinity, + spvutils::kRoundToNegativeInfinity}; + + // Everything fits, so everything should just be bit-shifts. + for (spvutils::round_direction round : rounding) { + HF flt(0.f); + f.castTo(flt, round); + EXPECT_EQ(GetParam().expected_float, flt.value().getAsFloat()) + << get_round_text(round) << " " << std::hex + << spvutils::BitwiseCast(GetParam().source_half) + << " cast to: " << flt.value().getAsFloat(); + } +} + +INSTANTIATE_TEST_SUITE_P(F16ToF32, HexFloatFP16To32Tests, + ::testing::ValuesIn(std::vector( + { + {0x0000, 0.f}, + {0x8000, -0.f}, + {0x3C00, 1.0f}, + {0xBC00, -1.0f}, + {0x3F00, float_fractions({0, 1, 2})}, + {0xBF00, -float_fractions({0, 1, 2})}, + {0x3F01, float_fractions({0, 1, 2, 10})}, + {0xBF01, -float_fractions({0, 1, 2, 10})}, + + // denorm + {0x0001, static_cast(ldexp(1.0, -24))}, + {0x0002, static_cast(ldexp(1.0, -23))}, + {0x8001, static_cast(-ldexp(1.0, -24))}, + {0x8011, static_cast(-ldexp(1.0, -20) + -ldexp(1.0, -24))}, + + // inf + {0x7C00, std::numeric_limits::infinity()}, + {0xFC00, -std::numeric_limits::infinity()}, + }))); + +TEST(HexFloatOperationTests, NanTests) { + using HF = spvutils::HexFloat>; + using HF16 = spvutils::HexFloat>; + spvutils::round_direction rounding[] = { + spvutils::kRoundToZero, + spvutils::kRoundToNearestEven, + spvutils::kRoundToPositiveInfinity, + spvutils::kRoundToNegativeInfinity}; + + // Everything fits, so everything should just be bit-shifts. + for (spvutils::round_direction round : rounding) { + HF16 f16(0); + HF f(0.f); + HF(std::numeric_limits::quiet_NaN()).castTo(f16, round); + EXPECT_TRUE(f16.value().isNan()); + HF(std::numeric_limits::signaling_NaN()).castTo(f16, round); + EXPECT_TRUE(f16.value().isNan()); + + HF16(0x7C01).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + HF16(0x7C11).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + HF16(0xFC01).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + HF16(0x7C10).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + HF16(0xFF00).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + } +} + +// A test case for parsing good and bad HexFloat> literals. +template +struct FloatParseCase { + std::string literal; + bool negate_value; + bool expect_success; + HexFloat> expected_value; +}; + +using ParseNormalFloatTest = ::testing::TestWithParam>; + +TEST_P(ParseNormalFloatTest, Samples) { + std::stringstream input(GetParam().literal); + HexFloat> parsed_value(0.0f); + ParseNormalFloat(input, GetParam().negate_value, parsed_value); + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().literal + << " negate: " << GetParam().negate_value; + if (GetParam().expect_success) { + EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value())) + << " literal: " << GetParam().literal + << " negate: " << GetParam().negate_value; + } +} + +// Returns a FloatParseCase with expected failure. +template +FloatParseCase BadFloatParseCase(std::string literal, bool negate_value, + T expected_value) { + HexFloat> proxy_expected_value(expected_value); + return FloatParseCase{literal, negate_value, false, proxy_expected_value}; +} + +// Returns a FloatParseCase that should successfully parse to a given value. +template +FloatParseCase GoodFloatParseCase(std::string literal, bool negate_value, + T expected_value) { + HexFloat> proxy_expected_value(expected_value); + return FloatParseCase{literal, negate_value, true, proxy_expected_value}; +} + +INSTANTIATE_TEST_SUITE_P( + FloatParse, ParseNormalFloatTest, + ::testing::ValuesIn(std::vector>{ + // Failing cases due to trivially incorrect syntax. + BadFloatParseCase("abc", false, 0.0f), + BadFloatParseCase("abc", true, 0.0f), + + // Valid cases. + GoodFloatParseCase("0", false, 0.0f), + GoodFloatParseCase("0.0", false, 0.0f), + GoodFloatParseCase("-0.0", false, -0.0f), + GoodFloatParseCase("2.0", false, 2.0f), + GoodFloatParseCase("-2.0", false, -2.0f), + GoodFloatParseCase("+2.0", false, 2.0f), + // Cases with negate_value being true. + GoodFloatParseCase("0.0", true, -0.0f), + GoodFloatParseCase("2.0", true, -2.0f), + + // When negate_value is true, we should not accept a + // leading minus or plus. + BadFloatParseCase("-0.0", true, 0.0f), + BadFloatParseCase("-2.0", true, 0.0f), + BadFloatParseCase("+0.0", true, 0.0f), + BadFloatParseCase("+2.0", true, 0.0f), + + // Overflow is an error for 32-bit float parsing. + BadFloatParseCase("1e40", false, FLT_MAX), + BadFloatParseCase("1e40", true, -FLT_MAX), + BadFloatParseCase("-1e40", false, -FLT_MAX), + // We can't have -1e40 and negate_value == true since + // that represents an original case of "--1e40" which + // is invalid. + })); + +using ParseNormalFloat16Test = + ::testing::TestWithParam>; + +TEST_P(ParseNormalFloat16Test, Samples) { + std::stringstream input(GetParam().literal); + HexFloat> parsed_value(0); + ParseNormalFloat(input, GetParam().negate_value, parsed_value); + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().literal + << " negate: " << GetParam().negate_value; + if (GetParam().expect_success) { + EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value())) + << " literal: " << GetParam().literal + << " negate: " << GetParam().negate_value; + } +} + +INSTANTIATE_TEST_SUITE_P( + Float16Parse, ParseNormalFloat16Test, + ::testing::ValuesIn(std::vector>{ + // Failing cases due to trivially incorrect syntax. + BadFloatParseCase("abc", false, uint16_t{0}), + BadFloatParseCase("abc", true, uint16_t{0}), + + // Valid cases. + GoodFloatParseCase("0", false, uint16_t{0}), + GoodFloatParseCase("0.0", false, uint16_t{0}), + GoodFloatParseCase("-0.0", false, uint16_t{0x8000}), + GoodFloatParseCase("2.0", false, uint16_t{0x4000}), + GoodFloatParseCase("-2.0", false, uint16_t{0xc000}), + GoodFloatParseCase("+2.0", false, uint16_t{0x4000}), + // Cases with negate_value being true. + GoodFloatParseCase("0.0", true, uint16_t{0x8000}), + GoodFloatParseCase("2.0", true, uint16_t{0xc000}), + + // When negate_value is true, we should not accept a leading minus or + // plus. + BadFloatParseCase("-0.0", true, uint16_t{0}), + BadFloatParseCase("-2.0", true, uint16_t{0}), + BadFloatParseCase("+0.0", true, uint16_t{0}), + BadFloatParseCase("+2.0", true, uint16_t{0}), + })); + +// A test case for detecting infinities. +template +struct OverflowParseCase { + std::string input; + bool expect_success; + T expected_value; +}; + +using FloatProxyParseOverflowFloatTest = + ::testing::TestWithParam>; + +TEST_P(FloatProxyParseOverflowFloatTest, Sample) { + std::istringstream input(GetParam().input); + HexFloat> value(0.0f); + input >> value; + EXPECT_NE(GetParam().expect_success, input.fail()); + if (GetParam().expect_success) { + EXPECT_THAT(value.value().getAsFloat(), GetParam().expected_value); + } +} + +INSTANTIATE_TEST_SUITE_P( + FloatOverflow, FloatProxyParseOverflowFloatTest, + ::testing::ValuesIn(std::vector>({ + {"0", true, 0.0f}, + {"0.0", true, 0.0f}, + {"1.0", true, 1.0f}, + {"1e38", true, 1e38f}, + {"-1e38", true, -1e38f}, + {"1e40", false, FLT_MAX}, + {"-1e40", false, -FLT_MAX}, + {"1e400", false, FLT_MAX}, + {"-1e400", false, -FLT_MAX}, + }))); + +using FloatProxyParseOverflowDoubleTest = + ::testing::TestWithParam>; + +TEST_P(FloatProxyParseOverflowDoubleTest, Sample) { + std::istringstream input(GetParam().input); + HexFloat> value(0.0); + input >> value; + EXPECT_NE(GetParam().expect_success, input.fail()); + if (GetParam().expect_success) { + EXPECT_THAT(value.value().getAsFloat(), Eq(GetParam().expected_value)); + } +} + +INSTANTIATE_TEST_SUITE_P( + DoubleOverflow, FloatProxyParseOverflowDoubleTest, + ::testing::ValuesIn(std::vector>({ + {"0", true, 0.0}, + {"0.0", true, 0.0}, + {"1.0", true, 1.0}, + {"1e38", true, 1e38}, + {"-1e38", true, -1e38}, + {"1e40", true, 1e40}, + {"-1e40", true, -1e40}, + {"1e400", false, DBL_MAX}, + {"-1e400", false, -DBL_MAX}, + }))); + +using FloatProxyParseOverflowFloat16Test = + ::testing::TestWithParam>; + +TEST_P(FloatProxyParseOverflowFloat16Test, Sample) { + std::istringstream input(GetParam().input); + HexFloat> value(0); + input >> value; + EXPECT_NE(GetParam().expect_success, input.fail()) << " literal: " + << GetParam().input; + if (GetParam().expect_success) { + EXPECT_THAT(value.value().data(), Eq(GetParam().expected_value)) + << " literal: " << GetParam().input; + } +} + +INSTANTIATE_TEST_SUITE_P( + Float16Overflow, FloatProxyParseOverflowFloat16Test, + ::testing::ValuesIn(std::vector>({ + {"0", true, uint16_t{0}}, + {"0.0", true, uint16_t{0}}, + {"1.0", true, uint16_t{0x3c00}}, + // Overflow for 16-bit float is an error, and returns max or + // lowest value. + {"1e38", false, uint16_t{0x7bff}}, + {"1e40", false, uint16_t{0x7bff}}, + {"1e400", false, uint16_t{0x7bff}}, + {"-1e38", false, uint16_t{0xfbff}}, + {"-1e40", false, uint16_t{0xfbff}}, + {"-1e400", false, uint16_t{0xfbff}}, + }))); + +TEST(FloatProxy, Max) { + EXPECT_THAT(FloatProxy::max().getAsFloat().get_value(), + Eq(uint16_t{0x7bff})); + EXPECT_THAT(FloatProxy::max().getAsFloat(), + Eq(std::numeric_limits::max())); + EXPECT_THAT(FloatProxy::max().getAsFloat(), + Eq(std::numeric_limits::max())); +} + +TEST(FloatProxy, Lowest) { + EXPECT_THAT(FloatProxy::lowest().getAsFloat().get_value(), + Eq(uint16_t{0xfbff})); + EXPECT_THAT(FloatProxy::lowest().getAsFloat(), + Eq(std::numeric_limits::lowest())); + EXPECT_THAT(FloatProxy::lowest().getAsFloat(), + Eq(std::numeric_limits::lowest())); +} + +// TODO(awoloszyn): Add fp16 tests and HexFloatTraits. +} // anonymous namespace diff --git a/third_party/glslang/gtests/Hlsl.FromFile.cpp b/third_party/glslang/gtests/Hlsl.FromFile.cpp new file mode 100755 index 0000000..4d1cb50 --- /dev/null +++ b/third_party/glslang/gtests/Hlsl.FromFile.cpp @@ -0,0 +1,511 @@ +// +// Copyright (C) 2016 Google, Inc. +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +struct FileNameEntryPointPair { + const char* fileName; + const char* entryPoint; +}; + +// We are using FileNameEntryPointPair objects as parameters for instantiating +// the template, so the global FileNameAsCustomTestSuffix() won't work since +// it assumes std::string as parameters. Thus, an overriding one here. +std::string FileNameAsCustomTestSuffix( + const ::testing::TestParamInfo& info) { + std::string name = info.param.fileName; + // A valid test case suffix cannot have '.' and '-' inside. + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '-', '_'); + return name; +} + +using HlslCompileTest = GlslangTest<::testing::TestWithParam>; +using HlslVulkan1_1CompileTest = GlslangTest<::testing::TestWithParam>; +using HlslCompileAndFlattenTest = GlslangTest<::testing::TestWithParam>; +using HlslLegalizeTest = GlslangTest<::testing::TestWithParam>; +using HlslDebugTest = GlslangTest<::testing::TestWithParam>; +using HlslDX9CompatibleTest = GlslangTest<::testing::TestWithParam>; +using HlslLegalDebugTest = GlslangTest<::testing::TestWithParam>; + +// Compiling HLSL to pre-legalized SPIR-V under Vulkan semantics. Expected +// to successfully generate both AST and SPIR-V. +TEST_P(HlslCompileTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + Source::HLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::BothASTAndSpv, true, GetParam().entryPoint); +} + +TEST_P(HlslVulkan1_1CompileTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + Source::HLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_1, glslang::EShTargetSpv_1_3, + Target::BothASTAndSpv, true, GetParam().entryPoint); +} + +TEST_P(HlslCompileAndFlattenTest, FromFile) +{ + loadFileCompileFlattenUniformsAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + Source::HLSL, Semantics::Vulkan, + Target::BothASTAndSpv, GetParam().entryPoint); +} + +// Compiling HLSL to legal SPIR-V under Vulkan semantics. Expected to +// successfully generate SPIR-V. +TEST_P(HlslLegalizeTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + Source::HLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv, true, GetParam().entryPoint, + "/baseLegalResults/", true); +} + +// Compiling HLSL to pre-legalized SPIR-V. Expected to successfully generate +// SPIR-V with debug instructions, particularly line info. +TEST_P(HlslDebugTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + Source::HLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv, true, GetParam().entryPoint, + "/baseResults/", false, true); +} + +TEST_P(HlslDX9CompatibleTest, FromFile) +{ + loadFileCompileAndCheckWithOptions(GlobalTestSettings.testRoot, GetParam().fileName, Source::HLSL, + Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::BothASTAndSpv, true, + GetParam().entryPoint, "/baseResults/", + EShMessages::EShMsgHlslDX9Compatible); +} + +// Compiling HLSL to legalized SPIR-V with debug instructions. Expected to +// successfully generate SPIR-V with debug instructions preserved through +// legalization, particularly line info. +TEST_P(HlslLegalDebugTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + Source::HLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv, true, GetParam().entryPoint, + "/baseResults/", true, true); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + ToSpirv, HlslCompileTest, + ::testing::ValuesIn(std::vector{ + {"hlsl.amend.frag", "f1"}, + {"hlsl.aliasOpaque.frag", "main"}, + {"hlsl.array.frag", "PixelShaderFunction"}, + {"hlsl.array.implicit-size.frag", "PixelShaderFunction"}, + {"hlsl.array.multidim.frag", "main"}, + {"hlsl.assoc.frag", "PixelShaderFunction"}, + {"hlsl.attribute.frag", "PixelShaderFunction"}, + {"hlsl.attribute.expression.comp", "main"}, + {"hlsl.attributeC11.frag", "main"}, + {"hlsl.attributeGlobalBuffer.frag", "main"}, + {"hlsl.basic.comp", "main"}, + {"hlsl.basic.geom", "main"}, + {"hlsl.boolConv.vert", "main"}, + {"hlsl.buffer.frag", "PixelShaderFunction"}, + {"hlsl.calculatelod.dx10.frag", "main"}, + {"hlsl.calculatelodunclamped.dx10.frag", "main"}, + {"hlsl.cast.frag", "PixelShaderFunction"}, + {"hlsl.cbuffer-identifier.vert", "main"}, + {"hlsl.charLit.vert", "main"}, + {"hlsl.clip.frag", "main"}, + {"hlsl.clipdistance-1.frag", "main"}, + {"hlsl.clipdistance-1.geom", "main"}, + {"hlsl.clipdistance-1.vert", "main"}, + {"hlsl.clipdistance-2.frag", "main"}, + {"hlsl.clipdistance-2.geom", "main"}, + {"hlsl.clipdistance-2.vert", "main"}, + {"hlsl.clipdistance-3.frag", "main"}, + {"hlsl.clipdistance-3.geom", "main"}, + {"hlsl.clipdistance-3.vert", "main"}, + {"hlsl.clipdistance-4.frag", "main"}, + {"hlsl.clipdistance-4.geom", "main"}, + {"hlsl.clipdistance-4.vert", "main"}, + {"hlsl.clipdistance-5.frag", "main"}, + {"hlsl.clipdistance-5.vert", "main"}, + {"hlsl.clipdistance-6.frag", "main"}, + {"hlsl.clipdistance-6.vert", "main"}, + {"hlsl.clipdistance-7.frag", "main"}, + {"hlsl.clipdistance-7.vert", "main"}, + {"hlsl.clipdistance-8.frag", "main"}, + {"hlsl.clipdistance-8.vert", "main"}, + {"hlsl.clipdistance-9.frag", "main"}, + {"hlsl.clipdistance-9.vert", "main"}, + {"hlsl.color.hull.tesc", "main"}, + {"hlsl.comparison.vec.frag", "main"}, + {"hlsl.conditional.frag", "PixelShaderFunction"}, + {"hlsl.constantbuffer.frag", "main"}, + {"hlsl.constructArray.vert", "main"}, + {"hlsl.constructexpr.frag", "main"}, + {"hlsl.constructimat.frag", "main"}, + {"hlsl.coverage.frag", "main"}, + {"hlsl.depthGreater.frag", "PixelShaderFunction"}, + {"hlsl.depthLess.frag", "PixelShaderFunction"}, + {"hlsl.discard.frag", "PixelShaderFunction"}, + {"hlsl.doLoop.frag", "PixelShaderFunction"}, + {"hlsl.earlydepthstencil.frag", "main"}, + {"hlsl.emptystructreturn.frag", "main"}, + {"hlsl.emptystructreturn.vert", "main"}, + {"hlsl.emptystruct.init.vert", "main"}, + {"hlsl.entry-in.frag", "PixelShaderFunction"}, + {"hlsl.entry-out.frag", "PixelShaderFunction"}, + {"hlsl.fraggeom.frag", "main"}, + {"hlsl.float1.frag", "PixelShaderFunction"}, + {"hlsl.float4.frag", "PixelShaderFunction"}, + {"hlsl.flatten.return.frag", "main"}, + {"hlsl.flattenOpaque.frag", "main"}, + {"hlsl.flattenOpaqueInit.vert", "main"}, + {"hlsl.flattenOpaqueInitMix.vert", "main"}, + {"hlsl.flattenSubset.frag", "main"}, + {"hlsl.flattenSubset2.frag", "main"}, + {"hlsl.forLoop.frag", "PixelShaderFunction"}, + {"hlsl.gather.array.dx10.frag", "main"}, + {"hlsl.gather.basic.dx10.frag", "main"}, + {"hlsl.gather.basic.dx10.vert", "main"}, + {"hlsl.gather.offset.dx10.frag", "main"}, + {"hlsl.gather.offsetarray.dx10.frag", "main"}, + {"hlsl.gathercmpRGBA.offset.dx10.frag", "main"}, + {"hlsl.gatherRGBA.array.dx10.frag", "main"}, + {"hlsl.gatherRGBA.basic.dx10.frag", "main"}, + {"hlsl.gatherRGBA.offset.dx10.frag", "main"}, + {"hlsl.gatherRGBA.offsetarray.dx10.frag", "main"}, + {"hlsl.getdimensions.dx10.frag", "main"}, + {"hlsl.getdimensions.rw.dx10.frag", "main"}, + {"hlsl.getdimensions.dx10.vert", "main"}, + {"hlsl.getsampleposition.dx10.frag", "main"}, + {"hlsl.global-const-init.frag", "main"}, + {"hlsl.gs-hs-mix.tesc", "HSMain"}, + {"hlsl.domain.1.tese", "main"}, + {"hlsl.domain.2.tese", "main"}, + {"hlsl.domain.3.tese", "main"}, + {"hlsl.function.frag", "main"}, + {"hlsl.hull.1.tesc", "main"}, + {"hlsl.hull.2.tesc", "main"}, + {"hlsl.hull.3.tesc", "main"}, + {"hlsl.hull.4.tesc", "main"}, + {"hlsl.hull.5.tesc", "main"}, + {"hlsl.hull.6.tesc", "main"}, + {"hlsl.hull.void.tesc", "main"}, + {"hlsl.hull.ctrlpt-1.tesc", "main"}, + {"hlsl.hull.ctrlpt-2.tesc", "main"}, + {"hlsl.format.rwtexture.frag", "main"}, + {"hlsl.groupid.comp", "main"}, + {"hlsl.identifier.sample.frag", "main"}, + {"hlsl.if.frag", "PixelShaderFunction"}, + {"hlsl.imagefetch-subvec4.comp", "main"}, + {"hlsl.implicitBool.frag", "main"}, + {"hlsl.inf.vert", "main"}, + {"hlsl.inoutquals.frag", "main"}, + {"hlsl.inoutquals.negative.frag", "main"}, + {"hlsl.init.frag", "ShaderFunction"}, + {"hlsl.init2.frag", "main"}, + {"hlsl.isfinite.frag", "main"}, + {"hlsl.intrinsics.barriers.comp", "ComputeShaderFunction"}, + {"hlsl.intrinsics.comp", "ComputeShaderFunction"}, + {"hlsl.intrinsics.evalfns.frag", "main"}, + {"hlsl.intrinsics.d3dcolortoubyte4.frag", "main"}, + {"hlsl.intrinsics.double.frag", "PixelShaderFunction"}, + {"hlsl.intrinsics.f1632.frag", "main"}, + {"hlsl.intrinsics.f3216.frag", "main"}, + {"hlsl.intrinsics.frag", "main"}, + {"hlsl.intrinsic.frexp.frag", "main"}, + {"hlsl.intrinsics.lit.frag", "PixelShaderFunction"}, + {"hlsl.intrinsics.negative.comp", "ComputeShaderFunction"}, + {"hlsl.intrinsics.negative.frag", "PixelShaderFunction"}, + {"hlsl.intrinsics.negative.vert", "VertexShaderFunction"}, + {"hlsl.intrinsics.promote.frag", "main"}, + {"hlsl.intrinsics.promote.down.frag", "main"}, + {"hlsl.intrinsics.promote.outputs.frag", "main"}, + {"hlsl.layout.frag", "main"}, + {"hlsl.layoutOverride.vert", "main"}, + {"hlsl.load.2dms.dx10.frag", "main"}, + {"hlsl.load.array.dx10.frag", "main"}, + {"hlsl.load.basic.dx10.frag", "main"}, + {"hlsl.load.basic.dx10.vert", "main"}, + {"hlsl.load.buffer.dx10.frag", "main"}, + {"hlsl.load.buffer.float.dx10.frag", "main"}, + {"hlsl.load.rwbuffer.dx10.frag", "main"}, + {"hlsl.load.rwtexture.dx10.frag", "main"}, + {"hlsl.load.rwtexture.array.dx10.frag", "main"}, + {"hlsl.load.offset.dx10.frag", "main"}, + {"hlsl.load.offsetarray.dx10.frag", "main"}, + {"hlsl.localStructuredBuffer.comp", "main"}, + {"hlsl.logical.binary.frag", "main"}, + {"hlsl.logical.binary.vec.frag", "main"}, + {"hlsl.logicalConvert.frag", "main"}, + {"hlsl.logical.unary.frag", "main"}, + {"hlsl.loopattr.frag", "main"}, + {"hlsl.matpack-pragma.frag", "main"}, + {"hlsl.matpack-pragma-global.frag", "main"}, + {"hlsl.mip.operator.frag", "main"}, + {"hlsl.mip.negative.frag", "main"}, + {"hlsl.mip.negative2.frag", "main"}, + {"hlsl.namespace.frag", "main"}, + {"hlsl.nonint-index.frag", "main"}, + {"hlsl.matNx1.frag", "main"}, + {"hlsl.matpack-1.frag", "main"}, + {"hlsl.matrixSwizzle.vert", "ShaderFunction"}, + {"hlsl.memberFunCall.frag", "main"}, + {"hlsl.mintypes.frag", "main"}, + {"hlsl.mul-truncate.frag", "main"}, + {"hlsl.multiEntry.vert", "RealEntrypoint"}, + {"hlsl.multiReturn.frag", "main"}, + {"hlsl.matrixindex.frag", "main"}, + {"hlsl.nonstaticMemberFunction.frag", "main"}, + {"hlsl.numericsuffixes.frag", "main"}, + {"hlsl.numthreads.comp", "main_aux2"}, + {"hlsl.overload.frag", "PixelShaderFunction"}, + {"hlsl.opaque-type-bug.frag", "main"}, + {"hlsl.params.default.frag", "main"}, + {"hlsl.params.default.negative.frag", "main"}, + {"hlsl.partialInit.frag", "PixelShaderFunction"}, + {"hlsl.partialFlattenLocal.vert", "main"}, + {"hlsl.PointSize.geom", "main"}, + {"hlsl.PointSize.vert", "main"}, + {"hlsl.pp.vert", "main"}, + {"hlsl.pp.line.frag", "main"}, + {"hlsl.precise.frag", "main"}, + {"hlsl.printf.comp", "main"}, + {"hlsl.promote.atomic.frag", "main"}, + {"hlsl.promote.binary.frag", "main"}, + {"hlsl.promote.vec1.frag", "main"}, + {"hlsl.promotions.frag", "main"}, + {"hlsl.rw.atomics.frag", "main"}, + {"hlsl.rw.bracket.frag", "main"}, + {"hlsl.rw.register.frag", "main"}, + {"hlsl.rw.scalar.bracket.frag", "main"}, + {"hlsl.rw.swizzle.frag", "main"}, + {"hlsl.rw.vec2.bracket.frag", "main"}, + {"hlsl.sample.array.dx10.frag", "main"}, + {"hlsl.sample.basic.dx10.frag", "main"}, + {"hlsl.sample.offset.dx10.frag", "main"}, + {"hlsl.sample.offsetarray.dx10.frag", "main"}, + {"hlsl.samplebias.array.dx10.frag", "main"}, + {"hlsl.samplebias.basic.dx10.frag", "main"}, + {"hlsl.samplebias.offset.dx10.frag", "main"}, + {"hlsl.samplebias.offsetarray.dx10.frag", "main"}, + {"hlsl.samplecmp.array.dx10.frag", "main"}, + {"hlsl.samplecmp.basic.dx10.frag", "main"}, + {"hlsl.samplecmp.dualmode.frag", "main"}, + {"hlsl.samplecmp.offset.dx10.frag", "main"}, + {"hlsl.samplecmp.offsetarray.dx10.frag", "main"}, + {"hlsl.samplecmp.negative.frag", "main"}, + {"hlsl.samplecmp.negative2.frag", "main"}, + {"hlsl.samplecmplevelzero.array.dx10.frag", "main"}, + {"hlsl.samplecmplevelzero.basic.dx10.frag", "main"}, + {"hlsl.samplecmplevelzero.offset.dx10.frag", "main"}, + {"hlsl.samplecmplevelzero.offsetarray.dx10.frag", "main"}, + {"hlsl.samplegrad.array.dx10.frag", "main"}, + {"hlsl.samplegrad.basic.dx10.frag", "main"}, + {"hlsl.samplegrad.basic.dx10.vert", "main"}, + {"hlsl.samplegrad.offset.dx10.frag", "main"}, + {"hlsl.samplegrad.offsetarray.dx10.frag", "main"}, + {"hlsl.samplelevel.array.dx10.frag", "main"}, + {"hlsl.samplelevel.basic.dx10.frag", "main"}, + {"hlsl.samplelevel.basic.dx10.vert", "main"}, + {"hlsl.samplelevel.offset.dx10.frag", "main"}, + {"hlsl.samplelevel.offsetarray.dx10.frag", "main"}, + {"hlsl.sample.sub-vec4.dx10.frag", "main"}, + {"hlsl.scalar-length.frag", "main"}, + {"hlsl.scalarCast.vert", "main"}, + {"hlsl.semicolons.frag", "main"}, + {"hlsl.shapeConv.frag", "main"}, + {"hlsl.shapeConvRet.frag", "main"}, + {"hlsl.singleArgIntPromo.vert", "main"}, + {"hlsl.self_cast.frag", "main"}, + {"hlsl.snorm.uav.comp", "main"}, + {"hlsl.specConstant.frag", "main"}, + {"hlsl.staticMemberFunction.frag", "main"}, + {"hlsl.staticFuncInit.frag", "main"}, + {"hlsl.store.rwbyteaddressbuffer.type.comp", "main"}, + {"hlsl.stringtoken.frag", "main"}, + {"hlsl.string.frag", "main"}, + {"hlsl.struct.split-1.vert", "main"}, + {"hlsl.struct.split.array.geom", "main"}, + {"hlsl.struct.split.assign.frag", "main"}, + {"hlsl.struct.split.call.vert", "main"}, + {"hlsl.struct.split.nested.geom", "main"}, + {"hlsl.struct.split.trivial.geom", "main"}, + {"hlsl.struct.split.trivial.vert", "main"}, + {"hlsl.structarray.flatten.frag", "main"}, + {"hlsl.structarray.flatten.geom", "main"}, + {"hlsl.structbuffer.frag", "main"}, + {"hlsl.structbuffer.append.frag", "main"}, + {"hlsl.structbuffer.append.fn.frag", "main"}, + {"hlsl.structbuffer.atomics.frag", "main"}, + {"hlsl.structbuffer.byte.frag", "main"}, + {"hlsl.structbuffer.coherent.frag", "main"}, + {"hlsl.structbuffer.floatidx.comp", "main"}, + {"hlsl.structbuffer.incdec.frag", "main"}, + {"hlsl.structbuffer.fn.frag", "main"}, + {"hlsl.structbuffer.fn2.comp", "main"}, + {"hlsl.structbuffer.rw.frag", "main"}, + {"hlsl.structbuffer.rwbyte.frag", "main"}, + {"hlsl.structin.vert", "main"}, + {"hlsl.structIoFourWay.frag", "main"}, + {"hlsl.structStructName.frag", "main"}, + {"hlsl.subpass.frag", "main"}, + {"hlsl.synthesizeInput.frag", "main"}, + {"hlsl.texturebuffer.frag", "main"}, + {"hlsl.texture.struct.frag", "main"}, + {"hlsl.texture.subvec4.frag", "main"}, + {"hlsl.this.frag", "main"}, + {"hlsl.intrinsics.vert", "VertexShaderFunction"}, + {"hlsl.intrinsic.frexp.vert", "VertexShaderFunction"}, + {"hlsl.matType.frag", "PixelShaderFunction"}, + {"hlsl.matType.bool.frag", "main"}, + {"hlsl.matType.int.frag", "main"}, + {"hlsl.max.frag", "PixelShaderFunction"}, + {"hlsl.preprocessor.frag", "main"}, + {"hlsl.precedence.frag", "PixelShaderFunction"}, + {"hlsl.precedence2.frag", "PixelShaderFunction"}, + {"hlsl.scalar2matrix.frag", "main"}, + {"hlsl.semantic.geom", "main"}, + {"hlsl.semantic.vert", "main"}, + {"hlsl.semantic-1.vert", "main"}, + {"hlsl.scope.frag", "PixelShaderFunction"}, + {"hlsl.sin.frag", "PixelShaderFunction"}, + {"hlsl.struct.frag", "PixelShaderFunction"}, + {"hlsl.switch.frag", "PixelShaderFunction"}, + {"hlsl.swizzle.frag", "PixelShaderFunction"}, + {"hlsl.target.frag", "main"}, + {"hlsl.targetStruct1.frag", "main"}, + {"hlsl.targetStruct2.frag", "main"}, + {"hlsl.templatetypes.frag", "PixelShaderFunction"}, + {"hlsl.tristream-append.geom", "main"}, + {"hlsl.tx.bracket.frag", "main"}, + {"hlsl.tx.overload.frag", "main"}, + {"hlsl.type.half.frag", "main"}, + {"hlsl.type.identifier.frag", "main"}, + {"hlsl.typeGraphCopy.vert", "main"}, + {"hlsl.typedef.frag", "PixelShaderFunction"}, + {"hlsl.whileLoop.frag", "PixelShaderFunction"}, + {"hlsl.void.frag", "PixelShaderFunction"}, + {"hlsl.type.type.conversion.all.frag", "main"} + }), + FileNameAsCustomTestSuffix +); +// clang-format on + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + ToSpirv, HlslVulkan1_1CompileTest, + ::testing::ValuesIn(std::vector{ + {"hlsl.wavebroadcast.comp", "CSMain"}, + {"hlsl.waveprefix.comp", "CSMain"}, + {"hlsl.wavequad.comp", "CSMain"}, + {"hlsl.wavequery.comp", "CSMain"}, + {"hlsl.wavequery.frag", "PixelShaderFunction"}, + {"hlsl.wavereduction.comp", "CSMain"}, + {"hlsl.wavevote.comp", "CSMain"}, + { "hlsl.type.type.conversion.valid.frag", "main" }, + {"hlsl.int.dot.frag", "main"} + }), + FileNameAsCustomTestSuffix +); +// clang-format on + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + ToSpirv, HlslCompileAndFlattenTest, + ::testing::ValuesIn(std::vector{ + {"hlsl.array.flatten.frag", "main"}, + {"hlsl.partialFlattenMixed.vert", "main"}, + }), + FileNameAsCustomTestSuffix +); +// clang-format on + +#if ENABLE_OPT +// clang-format off +INSTANTIATE_TEST_SUITE_P( + ToSpirv, HlslLegalizeTest, + ::testing::ValuesIn(std::vector{ + {"hlsl.aliasOpaque.frag", "main"}, + {"hlsl.flattenOpaque.frag", "main"}, + {"hlsl.flattenOpaqueInit.vert", "main"}, + {"hlsl.flattenOpaqueInitMix.vert", "main"}, + {"hlsl.flattenSubset.frag", "main"}, + {"hlsl.flattenSubset2.frag", "main"}, + {"hlsl.partialFlattenLocal.vert", "main"}, + {"hlsl.partialFlattenMixed.vert", "main"} + }), + FileNameAsCustomTestSuffix +); +// clang-format on +#endif + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + ToSpirv, HlslDebugTest, + ::testing::ValuesIn(std::vector{ + {"hlsl.pp.line2.frag", "MainPs"} + }), + FileNameAsCustomTestSuffix +); + +INSTANTIATE_TEST_SUITE_P( + ToSpirv, HlslDX9CompatibleTest, + ::testing::ValuesIn(std::vector{ + {"hlsl.sample.dx9.frag", "main"}, + {"hlsl.sample.dx9.vert", "main"}, + }), + FileNameAsCustomTestSuffix +); + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + ToSpirv, HlslLegalDebugTest, + ::testing::ValuesIn(std::vector{ + {"hlsl.pp.line4.frag", "MainPs"} + }), + FileNameAsCustomTestSuffix +); + +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/Initializer.h b/third_party/glslang/gtests/Initializer.h new file mode 100644 index 0000000..b46b3f5 --- /dev/null +++ b/third_party/glslang/gtests/Initializer.h @@ -0,0 +1,55 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_GTESTS_INITIALIZER_H +#define GLSLANG_GTESTS_INITIALIZER_H + +#include "glslang/Public/ShaderLang.h" + +namespace glslangtest { + +// Initializes glslang on creation, and destroys it on completion. +// This object is expected to be a singleton, so that internal glslang state +// can be correctly handled. +// +class GlslangInitializer { +public: + GlslangInitializer() { glslang::InitializeProcess(); } + + ~GlslangInitializer() { glslang::FinalizeProcess(); } +}; + +} // namespace glslangtest + +#endif // GLSLANG_GTESTS_INITIALIZER_H diff --git a/third_party/glslang/gtests/Link.FromFile.Vk.cpp b/third_party/glslang/gtests/Link.FromFile.Vk.cpp new file mode 100755 index 0000000..2909a9c --- /dev/null +++ b/third_party/glslang/gtests/Link.FromFile.Vk.cpp @@ -0,0 +1,132 @@ +// +// Copyright (C) 2016-2017 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include + +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +using LinkTestVulkan = GlslangTest< + ::testing::TestWithParam>>; + +TEST_P(LinkTestVulkan, FromFile) +{ + const auto& fileNames = GetParam(); + const size_t fileCount = fileNames.size(); + const EShMessages controls = DeriveOptions(Source::GLSL, Semantics::Vulkan, Target::AST); + GlslangResult result; + result.validationResult = false; + + // Compile each input shader file. + bool success = true; + std::vector> shaders; + for (size_t i = 0; i < fileCount; ++i) { + std::string contents; + tryLoadFile(GlobalTestSettings.testRoot + "/" + fileNames[i], + "input", &contents); + shaders.emplace_back( + new glslang::TShader(GetShaderStage(GetSuffix(fileNames[i])))); + auto* shader = shaders.back().get(); + shader->setAutoMapLocations(true); + success &= compile(shader, contents, "", controls); + result.shaderResults.push_back( + {fileNames[i], shader->getInfoLog(), shader->getInfoDebugLog()}); + } + + // Link all of them. + glslang::TProgram program; + for (const auto& shader : shaders) program.addShader(shader.get()); + success &= program.link(controls); + result.linkingOutput = program.getInfoLog(); + result.linkingError = program.getInfoDebugLog(); + +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + if (success) + program.mapIO(); +#endif + + if (success && (controls & EShMsgSpvRules)) { + spv::SpvBuildLogger logger; + std::vector spirv_binary; + options().disableOptimizer = true; + glslang::GlslangToSpv(*program.getIntermediate(shaders.front()->getStage()), + spirv_binary, &logger, &options()); + + std::ostringstream disassembly_stream; + spv::Parameterize(); + spv::Disassemble(disassembly_stream, spirv_binary); + result.spirvWarningsErrors = logger.getAllMessages(); + result.spirv = disassembly_stream.str(); + result.validationResult = !options().validate || logger.getAllMessages().empty(); + } + + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + // Check with expected results. + const std::string expectedOutputFname = + GlobalTestSettings.testRoot + "/baseResults/" + fileNames.front() + ".out"; + std::string expectedOutput; + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname, + result.spirvWarningsErrors); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, LinkTestVulkan, + ::testing::ValuesIn(std::vector>({ + {"link1.vk.frag", "link2.vk.frag"}, + {"spv.unit1.frag", "spv.unit2.frag", "spv.unit3.frag"}, + {"link.vk.matchingPC.0.0.frag", "link.vk.matchingPC.0.1.frag", + "link.vk.matchingPC.0.2.frag"}, + {"link.vk.differentPC.0.0.frag", "link.vk.differentPC.0.1.frag", + "link.vk.differentPC.0.2.frag"}, + {"link.vk.differentPC.1.0.frag", "link.vk.differentPC.1.1.frag", + "link.vk.differentPC.1.2.frag"}, + {"link.vk.pcNamingValid.0.0.vert", "link.vk.pcNamingValid.0.1.vert"}, + {"link.vk.pcNamingInvalid.0.0.vert", "link.vk.pcNamingInvalid.0.1.vert"}, + {"link.vk.multiBlocksValid.0.0.vert", "link.vk.multiBlocksValid.0.1.vert"}, + {"link.vk.multiBlocksValid.1.0.geom", "link.vk.multiBlocksValid.1.1.geom"}, + })) +); +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/Link.FromFile.cpp b/third_party/glslang/gtests/Link.FromFile.cpp new file mode 100755 index 0000000..29590c0 --- /dev/null +++ b/third_party/glslang/gtests/Link.FromFile.cpp @@ -0,0 +1,114 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include + +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +using LinkTest = GlslangTest< + ::testing::TestWithParam>>; + +TEST_P(LinkTest, FromFile) +{ + const auto& fileNames = GetParam(); + const size_t fileCount = fileNames.size(); + const EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST); + GlslangResult result; + result.validationResult = true; + + // Compile each input shader file. + std::vector> shaders; + for (size_t i = 0; i < fileCount; ++i) { + std::string contents; + tryLoadFile(GlobalTestSettings.testRoot + "/" + fileNames[i], + "input", &contents); + shaders.emplace_back( + new glslang::TShader(GetShaderStage(GetSuffix(fileNames[i])))); + auto* shader = shaders.back().get(); + compile(shader, contents, "", controls); + result.shaderResults.push_back( + {fileNames[i], shader->getInfoLog(), shader->getInfoDebugLog()}); + } + + // Link all of them. + glslang::TProgram program; + for (const auto& shader : shaders) program.addShader(shader.get()); + program.link(controls); + result.linkingOutput = program.getInfoLog(); + result.linkingError = program.getInfoDebugLog(); + + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + // Check with expected results. + const std::string expectedOutputFname = + GlobalTestSettings.testRoot + "/baseResults/" + fileNames.front() + ".out"; + std::string expectedOutput; + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, LinkTest, + ::testing::ValuesIn(std::vector>({ + {"mains1.frag", "mains2.frag", "noMain1.geom", "noMain2.geom"}, + {"noMain.vert", "mains.frag"}, + {"link1.frag", "link2.frag", "link3.frag"}, + {"recurse1.vert", "recurse1.frag", "recurse2.frag"}, + {"300link.frag"}, + {"300link2.frag"}, + {"300link3.frag"}, + {"empty.frag", "empty2.frag", "empty3.frag"}, + {"150.tesc", "150.tese", "400.tesc", "400.tese", "410.tesc", "420.tesc", "420.tese"}, + {"max_vertices_0.geom"}, + {"contradict_0.geom", "contradict_1.geom"}, + {"es-link1.frag", "es-link2.frag"}, + {"missingBodies.vert"}, + {"link.multiAnonBlocksInvalid.0.0.vert", "link.multiAnonBlocksInvalid.0.1.vert"}, + {"link.multiAnonBlocksValid.0.0.vert", "link.multiAnonBlocksValid.0.1.vert"}, + {"link.multiBlocksInvalid.0.0.vert", "link.multiBlocksInvalid.0.1.vert"}, + {"link.multiBlocksValid.1.0.vert", "link.multiBlocksValid.1.1.vert"}, + })) +); +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/Pp.FromFile.cpp b/third_party/glslang/gtests/Pp.FromFile.cpp new file mode 100644 index 0000000..92b4d24 --- /dev/null +++ b/third_party/glslang/gtests/Pp.FromFile.cpp @@ -0,0 +1,76 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +using PreprocessingTest = GlslangTest<::testing::TestWithParam>; + +TEST_P(PreprocessingTest, FromFile) +{ + loadFilePreprocessAndCheck(GlobalTestSettings.testRoot, GetParam()); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, PreprocessingTest, + ::testing::ValuesIn(std::vector({ + "preprocessor.bad_arg.vert", + "preprocessor.cpp_style_line_directive.vert", + "preprocessor.cpp_style___FILE__.vert", + "preprocessor.edge_cases.vert", + "preprocessor.errors.vert", + "preprocessor.extensions.vert", + "preprocessor.function_macro.vert", + "preprocessor.include.enabled.vert", + "preprocessor.include.disabled.vert", + "preprocessor.line.vert", + "preprocessor.line.frag", + "preprocessor.pragma.vert", + "preprocessor.simple.vert", + "preprocessor.success_if_parse_would_fail.vert", + "preprocessor.defined.vert", + "preprocessor.many.endif.vert", + "preprocessor.eof_missing.vert", + })), + FileNameAsCustomTestSuffix +); +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/README.md b/third_party/glslang/gtests/README.md new file mode 100644 index 0000000..c8261cc --- /dev/null +++ b/third_party/glslang/gtests/README.md @@ -0,0 +1,26 @@ +Glslang Tests based on the Google Test Framework +================================================ + +This directory contains [Google Test][gtest] based test fixture and test +cases for glslang. + +Apart from typical unit tests, necessary utility methods are added into +the [`GlslangTests`](TestFixture.h) fixture to provide the ability to do +file-based integration tests. Various `*.FromFile.cpp` files lists names +of files containing input shader code in the `Test/` directory. Utility +methods will load the input shader source, compile them, and compare with +the corresponding expected output in the `Test/baseResults/` directory. + +How to run the tests +-------------------- + +Please make sure you have a copy of [Google Test][gtest] checked out under +the `External` directory before building. After building, just run the +`ctest` command or the `gtests/glslangtests` binary in your build directory. + +The `gtests/glslangtests` binary also provides an `--update-mode` command +line option, which, if supplied, will overwrite the golden files under +the `Test/baseResults/` directory with real output from that invocation. +This serves as an easy way to update golden files. + +[gtest]: https://github.com/google/googletest diff --git a/third_party/glslang/gtests/Remap.FromFile.cpp b/third_party/glslang/gtests/Remap.FromFile.cpp new file mode 100644 index 0000000..f014253 --- /dev/null +++ b/third_party/glslang/gtests/Remap.FromFile.cpp @@ -0,0 +1,118 @@ +// +// Copyright (C) 2016 LunarG, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +struct RemapTestArgs { + const char* fileName; + const char* entryPoint; + Source sourceLanguage; + unsigned int remapOpts; +}; + +// We are using FileNameEntryPointPair objects as parameters for instantiating +// the template, so the global FileNameAsCustomTestSuffix() won't work since +// it assumes std::string as parameters. Thus, an overriding one here. +std::string FileNameAsCustomTestSuffix( + const ::testing::TestParamInfo& info) { + std::string name = info.param.fileName; + // A valid test case suffix cannot have '.' and '-' inside. + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '-', '_'); + return name; +} + +using RemapTest = GlslangTest<::testing::TestWithParam>; + +// Remapping SPIR-V modules. +TEST_P(RemapTest, FromFile) +{ + if (GetSuffix(GetParam().fileName) == "spv") { + loadFileRemapAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + GetParam().sourceLanguage, + Semantics::Vulkan, + Target::Spv, + GetParam().remapOpts); + } else { + loadFileCompileRemapAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + GetParam().sourceLanguage, + Semantics::Vulkan, + Target::Spv, + GetParam().entryPoint, + GetParam().remapOpts); + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + ToSpirv, RemapTest, + ::testing::ValuesIn(std::vector{ + // GLSL remapper tests + // testname entry language remapper_options + { "remap.basic.none.frag", "main", Source::GLSL, spv::spirvbin_t::NONE }, + { "remap.basic.everything.frag", "main", Source::GLSL, spv::spirvbin_t::DO_EVERYTHING }, + { "remap.basic.dcefunc.frag", "main", Source::GLSL, spv::spirvbin_t::DCE_FUNCS }, + { "remap.basic.strip.frag", "main", Source::GLSL, spv::spirvbin_t::STRIP }, + { "remap.specconst.comp", "main", Source::GLSL, spv::spirvbin_t::DO_EVERYTHING }, + { "remap.switch.none.frag", "main", Source::GLSL, spv::spirvbin_t::NONE }, + { "remap.switch.everything.frag", "main", Source::GLSL, spv::spirvbin_t::DO_EVERYTHING }, + { "remap.literal64.none.spv", "main", Source::GLSL, spv::spirvbin_t::NONE }, + { "remap.literal64.everything.spv", "main", Source::GLSL, spv::spirvbin_t::DO_EVERYTHING }, + { "remap.if.none.frag", "main", Source::GLSL, spv::spirvbin_t::NONE }, + { "remap.if.everything.frag", "main", Source::GLSL, spv::spirvbin_t::DO_EVERYTHING }, + { "remap.similar_1a.none.frag", "main", Source::GLSL, spv::spirvbin_t::NONE }, + { "remap.similar_1b.none.frag", "main", Source::GLSL, spv::spirvbin_t::NONE }, + { "remap.similar_1a.everything.frag", "main", Source::GLSL, spv::spirvbin_t::DO_EVERYTHING }, + { "remap.similar_1b.everything.frag", "main", Source::GLSL, spv::spirvbin_t::DO_EVERYTHING }, + { "remap.uniformarray.none.frag", "main", Source::GLSL, spv::spirvbin_t::NONE }, + { "remap.uniformarray.everything.frag", "main", Source::GLSL, spv::spirvbin_t::DO_EVERYTHING }, + + // HLSL remapper tests + { "remap.hlsl.sample.basic.strip.frag", "main", Source::HLSL, spv::spirvbin_t::STRIP }, + { "remap.hlsl.sample.basic.everything.frag", "main", Source::HLSL, spv::spirvbin_t::DO_EVERYTHING }, + { "remap.hlsl.sample.basic.none.frag", "main", Source::HLSL, spv::spirvbin_t::NONE }, + { "remap.hlsl.templatetypes.none.frag", "main", Source::HLSL, spv::spirvbin_t::NONE }, + { "remap.hlsl.templatetypes.everything.frag", "main", Source::HLSL, spv::spirvbin_t::DO_EVERYTHING }, + }), + FileNameAsCustomTestSuffix +); +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/Settings.cpp b/third_party/glslang/gtests/Settings.cpp new file mode 100644 index 0000000..0ac5844 --- /dev/null +++ b/third_party/glslang/gtests/Settings.cpp @@ -0,0 +1,51 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "Settings.h" + +namespace glslangtest { + +// We need CMake to provide us the absolute path to the directory containing +// test files, so we are certain to find those files no matter where the test +// harness binary is generated. This provides out-of-source build capability. +// This will be used as the default test root, but can be overridden with +// the --test-root argument. +#ifndef GLSLANG_TEST_DIRECTORY +#error \ + "GLSLANG_TEST_DIRECTORY needs to be defined for gtest to locate test files." +#endif + +GTestSettings GlobalTestSettings = {nullptr, false, GLSLANG_TEST_DIRECTORY}; + +} // namespace glslangtest diff --git a/third_party/glslang/gtests/Settings.h b/third_party/glslang/gtests/Settings.h new file mode 100644 index 0000000..c38474c --- /dev/null +++ b/third_party/glslang/gtests/Settings.h @@ -0,0 +1,58 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_GTESTS_SETTINGS_H +#define GLSLANG_GTESTS_SETTINGS_H + +#include + +namespace glslangtest { + +class GlslangInitializer; + +struct GTestSettings { + // A handle to GlslangInitializer instance. + GlslangInitializer* initializer; + // An indicator of whether GTest should write real output to the file for + // the expected output. + bool updateMode; + // The root directory for test files. + std::string testRoot; +}; + +extern GTestSettings GlobalTestSettings; + +} // namespace glslangtest + +#endif // GLSLANG_GTESTS_SETTINGS_H diff --git a/third_party/glslang/gtests/Spv.FromFile.cpp b/third_party/glslang/gtests/Spv.FromFile.cpp new file mode 100644 index 0000000..9cb3dba --- /dev/null +++ b/third_party/glslang/gtests/Spv.FromFile.cpp @@ -0,0 +1,737 @@ + // +// Copyright (C) 2016 Google, Inc. +// Copyright (C) 2019 ARM Limited. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include + +#include + +#include "TestFixture.h" + +namespace glslangtest { +namespace { + +struct IoMapData { + const char* fileName; + const char* entryPoint; + int baseSamplerBinding; + int baseTextureBinding; + int baseImageBinding; + int baseUboBinding; + int baseSsboBinding; + bool autoMapBindings; + bool flattenUniforms; +}; + +std::string FileNameAsCustomTestSuffixIoMap( + const ::testing::TestParamInfo& info) { + std::string name = info.param.fileName; + // A valid test case suffix cannot have '.' and '-' inside. + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '-', '_'); + return name; +} + +using CompileVulkanToSpirvTest = GlslangTest<::testing::TestWithParam>; +using CompileVulkanToSpirvDeadCodeElimTest = GlslangTest<::testing::TestWithParam>; +using CompileVulkanToDebugSpirvTest = GlslangTest<::testing::TestWithParam>; +using CompileVulkan1_1ToSpirvTest = GlslangTest<::testing::TestWithParam>; +using CompileToSpirv14Test = GlslangTest<::testing::TestWithParam>; +using CompileOpenGLToSpirvTest = GlslangTest<::testing::TestWithParam>; +using VulkanSemantics = GlslangTest<::testing::TestWithParam>; +using OpenGLSemantics = GlslangTest<::testing::TestWithParam>; +using VulkanAstSemantics = GlslangTest<::testing::TestWithParam>; +using HlslIoMap = GlslangTest<::testing::TestWithParam>; +using GlslIoMap = GlslangTest<::testing::TestWithParam>; +using CompileVulkanToSpirvTestAMD = GlslangTest<::testing::TestWithParam>; +using CompileVulkanToSpirvTestNV = GlslangTest<::testing::TestWithParam>; +using CompileUpgradeTextureToSampledTextureAndDropSamplersTest = GlslangTest<::testing::TestWithParam>; + +// Compiling GLSL to SPIR-V under Vulkan semantics. Expected to successfully +// generate SPIR-V. +TEST_P(CompileVulkanToSpirvTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv); +} + +TEST_P(CompileVulkanToSpirvDeadCodeElimTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv); +} + +// Compiling GLSL to SPIR-V with debug info under Vulkan semantics. Expected +// to successfully generate SPIR-V. +TEST_P(CompileVulkanToDebugSpirvTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, + glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv, true, "", + "/baseResults/", false, true); +} + + +TEST_P(CompileVulkan1_1ToSpirvTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_1, glslang::EShTargetSpv_1_3, + Target::Spv); +} + +TEST_P(CompileToSpirv14Test, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_1, glslang::EShTargetSpv_1_4, + Target::Spv); +} + +// Compiling GLSL to SPIR-V under OpenGL semantics. Expected to successfully +// generate SPIR-V. +TEST_P(CompileOpenGLToSpirvTest, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::OpenGL, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv); +} + +// GLSL-level Vulkan semantics test. Expected to error out before generating +// SPIR-V. +TEST_P(VulkanSemantics, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv, false); +} + +// GLSL-level Vulkan semantics test. Expected to error out before generating +// SPIR-V. +TEST_P(OpenGLSemantics, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::OpenGL, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv, false); +} + +// GLSL-level Vulkan semantics test that need to see the AST for validation. +TEST_P(VulkanAstSemantics, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::AST); +} + +// HLSL-level Vulkan semantics tests. +TEST_P(HlslIoMap, FromFile) +{ + loadFileCompileIoMapAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + Source::HLSL, Semantics::Vulkan, + Target::Spv, GetParam().entryPoint, + GetParam().baseSamplerBinding, + GetParam().baseTextureBinding, + GetParam().baseImageBinding, + GetParam().baseUboBinding, + GetParam().baseSsboBinding, + GetParam().autoMapBindings, + GetParam().flattenUniforms); +} + +// GLSL-level Vulkan semantics tests. +TEST_P(GlslIoMap, FromFile) +{ + loadFileCompileIoMapAndCheck(GlobalTestSettings.testRoot, GetParam().fileName, + Source::GLSL, Semantics::Vulkan, + Target::Spv, GetParam().entryPoint, + GetParam().baseSamplerBinding, + GetParam().baseTextureBinding, + GetParam().baseImageBinding, + GetParam().baseUboBinding, + GetParam().baseSsboBinding, + GetParam().autoMapBindings, + GetParam().flattenUniforms); +} + +// Compiling GLSL to SPIR-V under Vulkan semantics (AMD extensions enabled). +// Expected to successfully generate SPIR-V. +TEST_P(CompileVulkanToSpirvTestAMD, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv); +} + +// Compiling GLSL to SPIR-V under Vulkan semantics (NV extensions enabled). +// Expected to successfully generate SPIR-V. +TEST_P(CompileVulkanToSpirvTestNV, FromFile) +{ + loadFileCompileAndCheck(GlobalTestSettings.testRoot, GetParam(), + Source::GLSL, Semantics::Vulkan, glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, + Target::Spv); +} + +TEST_P(CompileUpgradeTextureToSampledTextureAndDropSamplersTest, FromFile) +{ + loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(GlobalTestSettings.testRoot, + GetParam(), + Source::GLSL, + Semantics::Vulkan, + Target::Spv); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileVulkanToSpirvTest, + ::testing::ValuesIn(std::vector({ + // Test looping constructs. + // No tests yet for making sure break and continue from a nested loop + // goes to the innermost target. + "spv.barrier.vert", + "spv.do-simple.vert", + "spv.do-while-continue-break.vert", + "spv.for-complex-condition.vert", + "spv.for-continue-break.vert", + "spv.for-simple.vert", + "spv.for-notest.vert", + "spv.for-nobody.vert", + "spv.while-continue-break.vert", + "spv.while-simple.vert", + // vulkan-specific tests + "rayQuery.rgen", + "rayQuery-no-cse.rgen", + "rayQuery-initialize.rgen", + "rayQuery-allOps.rgen", + "rayQuery-allOps.Error.rgen", + "rayQuery-committed.Error.rgen", + "rayQuery-allOps.comp", + "rayQuery-allOps.frag", + "rayQuery-initialization.Error.comp", + "spv.set.vert", + "spv.double.comp", + "spv.100ops.frag", + "spv.130.frag", + "spv.140.frag", + "spv.150.geom", + "spv.150.vert", + "spv.16bitstorage.frag", + "spv.16bitstorage_Error.frag", + "spv.16bitstorage-int.frag", + "spv.16bitstorage_Error-int.frag", + "spv.16bitstorage-uint.frag", + "spv.16bitstorage_Error-uint.frag", + "spv.300BuiltIns.vert", + "spv.300layout.frag", + "spv.300layout.vert", + "spv.300layoutp.vert", + "spv.310.comp", + "spv.310.bitcast.frag", + "spv.330.geom", + "spv.400.frag", + "spv.400.tesc", + "spv.400.tese", + "spv.420.geom", + "spv.430.frag", + "spv.430.vert", + "spv.450.tesc", + "spv.450.geom", + "spv.450.noRedecl.tesc", + "spv.8bitstorage-int.frag", + "spv.8bitstorage_Error-int.frag", + "spv.8bitstorage-uint.frag", + "spv.8bitstorage_Error-uint.frag", + "spv.8bitstorage-ubo.vert", + "spv.8bitstorage-ssbo.vert", + "spv.8bit-16bit-construction.frag", + "spv.accessChain.frag", + "spv.aggOps.frag", + "spv.always-discard.frag", + "spv.always-discard2.frag", + "spv.arbPostDepthCoverage.frag", + "spv.arbPostDepthCoverage_Error.frag", + "spv.atomicCounter.comp", + "spv.bitCast.frag", + "spv.bool.vert", + "spv.boolInBlock.frag", + "spv.branch-return.vert", + "spv.bufferhandle1.frag", + "spv.bufferhandle10.frag", + "spv.bufferhandle11.frag", + "spv.bufferhandle12.frag", + "spv.bufferhandle13.frag", + "spv.bufferhandle14.frag", + "spv.bufferhandle15.frag", + "spv.bufferhandle16.frag", + "spv.bufferhandle17_Errors.frag", + "spv.bufferhandle18.frag", + "spv.bufferhandle19_Errors.frag", + "spv.bufferhandle2.frag", + "spv.bufferhandle3.frag", + "spv.bufferhandle4.frag", + "spv.bufferhandle5.frag", + "spv.bufferhandle6.frag", + "spv.bufferhandle7.frag", + "spv.bufferhandle8.frag", + "spv.bufferhandle9.frag", + "spv.bufferhandleUvec2.frag", + "spv.bufferhandle_Error.frag", + "spv.builtInXFB.vert", + "spv.conditionalDemote.frag", + "spv.conditionalDiscard.frag", + "spv.constructComposite.comp", + "spv.constStruct.vert", + "spv.constConstruct.vert", + "spv.controlFlowAttributes.frag", + "spv.conversion.frag", + "spv.coopmat.comp", + "spv.coopmat_Error.comp", + "spv.dataOut.frag", + "spv.dataOutIndirect.frag", + "spv.dataOutIndirect.vert", + "spv.debugPrintf.frag", + "spv.debugPrintf_Error.frag", + "spv.demoteDisabled.frag", + "spv.deepRvalue.frag", + "spv.depthOut.frag", + "spv.discard-dce.frag", + "spv.doWhileLoop.frag", + "spv.earlyReturnDiscard.frag", + "spv.extPostDepthCoverage.frag", + "spv.extPostDepthCoverage_Error.frag", + "spv.float16convertonlyarith.comp", + "spv.float16convertonlystorage.comp", + "spv.flowControl.frag", + "spv.forLoop.frag", + "spv.forwardFun.frag", + "spv.fragmentDensity.frag", + "spv.fragmentDensity.vert", + "spv.fragmentDensity-es.frag", + "spv.fragmentDensity-neg.frag", + "spv.fsi.frag", + "spv.fsi_Error.frag", + "spv.fullyCovered.frag", + "spv.functionCall.frag", + "spv.functionNestedOpaque.vert", + "spv.functionSemantics.frag", + "spv.functionParameterTypes.frag", + "spv.GeometryShaderPassthrough.geom", + "spv.interpOps.frag", + "spv.int64.frag", + "spv.intcoopmat.comp", + "spv.intOps.vert", + "spv.layoutNested.vert", + "spv.length.frag", + "spv.localAggregates.frag", + "spv.loops.frag", + "spv.loopsArtificial.frag", + "spv.matFun.vert", + "spv.matrix.frag", + "spv.matrix2.frag", + "spv.memoryQualifier.frag", + "spv.merge-unreachable.frag", + "spv.multiStruct.comp", + "spv.multiStructFuncall.frag", + "spv.newTexture.frag", + "spv.noDeadDecorations.vert", + "spv.nonSquare.vert", + "spv.nonuniform.frag", + "spv.nonuniform2.frag", + "spv.nonuniform3.frag", + "spv.nonuniform4.frag", + "spv.nonuniform5.frag", + "spv.noWorkgroup.comp", + "spv.offsets.frag", + "spv.Operations.frag", + "spv.paramMemory.frag", + "spv.paramMemory.420.frag", + "spv.precision.frag", + "spv.precisionArgs.frag", + "spv.precisionNonESSamp.frag", + "spv.precisionTexture.frag", + "spv.prepost.frag", + "spv.privateVariableTypes.frag", + "spv.qualifiers.vert", + "spv.sample.frag", + "spv.sampleId.frag", + "spv.samplePosition.frag", + "spv.sampleMaskOverrideCoverage.frag", + "spv.scalarlayout.frag", + "spv.scalarlayoutfloat16.frag", + "spv.shaderBallot.comp", + "spv.shaderDrawParams.vert", + "spv.shaderGroupVote.comp", + "spv.shaderStencilExport.frag", + "spv.shiftOps.frag", + "spv.simpleFunctionCall.frag", + "spv.simpleMat.vert", + "spv.sparseTexture.frag", + "spv.sparseTextureClamp.frag", + "spv.structAssignment.frag", + "spv.structDeref.frag", + "spv.structure.frag", + "spv.switch.frag", + "spv.swizzle.frag", + "spv.swizzleInversion.frag", + "spv.test.frag", + "spv.test.vert", + "spv.texture.frag", + "spv.texture.vert", + "spv.textureBuffer.vert", + "spv.image.frag", + "spv.imageAtomic64.frag", + "spv.types.frag", + "spv.uint.frag", + "spv.uniformArray.frag", + "spv.variableArrayIndex.frag", + "spv.varyingArray.frag", + "spv.varyingArrayIndirect.frag", + "spv.vecMatConstruct.frag", + "spv.voidFunction.frag", + "spv.whileLoop.frag", + "spv.AofA.frag", + "spv.queryL.frag", + "spv.separate.frag", + "spv.shortCircuit.frag", + "spv.pushConstant.vert", + "spv.pushConstantAnon.vert", + "spv.subpass.frag", + "spv.specConstant.vert", + "spv.specConstant.comp", + "spv.specConstantComposite.vert", + "spv.specConstantOperations.vert", + "spv.specConstant.float16.comp", + "spv.specConstant.int16.comp", + "spv.specConstant.int8.comp", + "spv.storageBuffer.vert", + "spv.terminate.frag", + "spv.precise.tese", + "spv.precise.tesc", + "spv.volatileAtomic.comp", + "spv.vulkan100.subgroupArithmetic.comp", + "spv.vulkan100.subgroupPartitioned.comp", + "spv.xfb.vert", + "spv.xfb2.vert", + "spv.xfb3.vert", + "spv.samplerlessTextureFunctions.frag", + "spv.smBuiltins.vert", + "spv.smBuiltins.frag", + "spv.builtin.PrimitiveShadingRateEXT.vert", + "spv.builtin.ShadingRateEXT.frag", + })), + FileNameAsCustomTestSuffix +); + +// Cases with deliberately unreachable code. +// By default the compiler will aggressively eliminate +// unreachable merges and continues. +INSTANTIATE_TEST_SUITE_P( + GlslWithDeadCode, CompileVulkanToSpirvDeadCodeElimTest, + ::testing::ValuesIn(std::vector({ + "spv.dead-after-continue.vert", + "spv.dead-after-discard.frag", + "spv.dead-after-return.vert", + "spv.dead-after-loop-break.vert", + "spv.dead-after-switch-break.vert", + "spv.dead-complex-continue-after-return.vert", + "spv.dead-complex-merge-after-return.vert", + })), + FileNameAsCustomTestSuffix +); + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileVulkanToDebugSpirvTest, + ::testing::ValuesIn(std::vector({ + "spv.pp.line.frag", + })), + FileNameAsCustomTestSuffix +); + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileVulkan1_1ToSpirvTest, + ::testing::ValuesIn(std::vector({ + "spv.1.3.8bitstorage-ubo.vert", + "spv.1.3.8bitstorage-ssbo.vert", + "spv.1.3.coopmat.comp", + "spv.deviceGroup.frag", + "spv.drawParams.vert", + "spv.int8.frag", + "spv.vulkan110.int16.frag", + "spv.int32.frag", + "spv.explicittypes.frag", + "spv.float32.frag", + "spv.float64.frag", + "spv.memoryScopeSemantics.comp", + "spv.memoryScopeSemantics_Error.comp", + "spv.multiView.frag", + "spv.RayGenShader11.rgen", + "spv.subgroup.frag", + "spv.subgroup.geom", + "spv.subgroup.tesc", + "spv.subgroup.tese", + "spv.subgroup.vert", + "spv.subgroupArithmetic.comp", + "spv.subgroupBasic.comp", + "spv.subgroupBallot.comp", + "spv.subgroupBallotNeg.comp", + "spv.subgroupClustered.comp", + "spv.subgroupClusteredNeg.comp", + "spv.subgroupPartitioned.comp", + "spv.subgroupShuffle.comp", + "spv.subgroupShuffleRelative.comp", + "spv.subgroupQuad.comp", + "spv.subgroupVote.comp", + "spv.subgroupExtendedTypesArithmetic.comp", + "spv.subgroupExtendedTypesArithmeticNeg.comp", + "spv.subgroupExtendedTypesBallot.comp", + "spv.subgroupExtendedTypesBallotNeg.comp", + "spv.subgroupExtendedTypesClustered.comp", + "spv.subgroupExtendedTypesClusteredNeg.comp", + "spv.subgroupExtendedTypesPartitioned.comp", + "spv.subgroupExtendedTypesPartitionedNeg.comp", + "spv.subgroupExtendedTypesShuffle.comp", + "spv.subgroupExtendedTypesShuffleNeg.comp", + "spv.subgroupExtendedTypesShuffleRelative.comp", + "spv.subgroupExtendedTypesShuffleRelativeNeg.comp", + "spv.subgroupExtendedTypesQuad.comp", + "spv.subgroupExtendedTypesQuadNeg.comp", + "spv.subgroupExtendedTypesVote.comp", + "spv.subgroupExtendedTypesVoteNeg.comp", + "spv.vulkan110.storageBuffer.vert", + })), + FileNameAsCustomTestSuffix +); + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileToSpirv14Test, + ::testing::ValuesIn(std::vector({ + "spv.1.4.LoopControl.frag", + "spv.1.4.NonWritable.frag", + "spv.1.4.OpEntryPoint.frag", + "spv.1.4.OpEntryPoint.opaqueParams.vert", + "spv.1.4.OpSelect.frag", + "spv.1.4.OpCopyLogical.comp", + "spv.1.4.OpCopyLogicalBool.comp", + "spv.1.4.OpCopyLogical.funcall.frag", + "spv.1.4.image.frag", + "spv.1.4.sparseTexture.frag", + "spv.1.4.texture.frag", + "spv.1.4.constructComposite.comp", + "spv.ext.AnyHitShader.rahit", + "spv.ext.AnyHitShader_Errors.rahit", + "spv.ext.ClosestHitShader.rchit", + "spv.ext.ClosestHitShader_Errors.rchit", + "spv.ext.IntersectShader.rint", + "spv.ext.IntersectShader_Errors.rint", + "spv.ext.MissShader.rmiss", + "spv.ext.MissShader_Errors.rmiss", + "spv.ext.RayPrimCull_Errors.rgen", + "spv.ext.RayCallable.rcall", + "spv.ext.RayCallable_Errors.rcall", + "spv.ext.RayConstants.rgen", + "spv.ext.RayGenShader.rgen", + "spv.ext.RayGenShader11.rgen", + "spv.ext.RayGenShaderArray.rgen", + "spv.ext.World3x4.rahit", + })), + FileNameAsCustomTestSuffix +); + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Hlsl, HlslIoMap, + ::testing::ValuesIn(std::vector{ + { "spv.register.autoassign.frag", "main_ep", 5, 10, 0, 20, 30, true, false }, + { "spv.register.noautoassign.frag", "main_ep", 5, 10, 0, 15, 30, false, false }, + { "spv.register.autoassign-2.frag", "main", 5, 10, 0, 15, 30, true, true }, + { "spv.register.subpass.frag", "main", 0, 20, 0, 0, 0, true, true }, + { "spv.buffer.autoassign.frag", "main", 5, 10, 0, 15, 30, true, true }, + { "spv.ssbo.autoassign.frag", "main", 5, 10, 0, 15, 30, true, true }, + { "spv.ssboAlias.frag", "main", 0, 0, 0, 0, 83, true, false }, + { "spv.rw.autoassign.frag", "main", 5, 10, 20, 15, 30, true, true }, + { "spv.register.autoassign.rangetest.frag", "main", + glslang::TQualifier::layoutBindingEnd-2, + glslang::TQualifier::layoutBindingEnd+5, + 20, 30, true, false }, + }), + FileNameAsCustomTestSuffixIoMap +); + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Hlsl, GlslIoMap, + ::testing::ValuesIn(std::vector{ + { "spv.glsl.register.autoassign.frag", "main", 5, 10, 0, 20, 30, true, false }, + { "spv.glsl.register.noautoassign.frag", "main", 5, 10, 0, 15, 30, false, false }, + }), + FileNameAsCustomTestSuffixIoMap +); + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileOpenGLToSpirvTest, + ::testing::ValuesIn(std::vector({ + "spv.460.frag", + "spv.460.vert", + "spv.460.comp", + "spv.atomic.comp", + "spv.atomicFloat.comp", + "spv.atomicFloat_Error.comp", + "spv.glFragColor.frag", + "spv.rankShift.comp", + "spv.specConst.vert", + "spv.specTexture.frag", + "spv.OVR_multiview.vert", + "spv.uniformInitializer.frag", + "spv.uniformInitializerSpecConstant.frag", + "spv.uniformInitializerStruct.frag", + "spv.xfbOffsetOnBlockMembersAssignment.vert", + "spv.xfbOffsetOnStructMembersAssignment.vert", + "spv.xfbOverlapOffsetCheckWithBlockAndMember.vert", + "spv.xfbStrideJustOnce.vert", + })), + FileNameAsCustomTestSuffix +); + +INSTANTIATE_TEST_SUITE_P( + Glsl, VulkanSemantics, + ::testing::ValuesIn(std::vector({ + "vulkan.frag", + "vulkan.vert", + "vulkan.comp", + "samplerlessTextureFunctions.frag", + "spv.specConstArrayCheck.vert", + })), + FileNameAsCustomTestSuffix +); + +INSTANTIATE_TEST_SUITE_P( + Glsl, OpenGLSemantics, + ::testing::ValuesIn(std::vector({ + "glspv.esversion.vert", + "glspv.version.frag", + "glspv.version.vert", + "glspv.frag", + "glspv.vert", + })), + FileNameAsCustomTestSuffix +); + +INSTANTIATE_TEST_SUITE_P( + Glsl, VulkanAstSemantics, + ::testing::ValuesIn(std::vector({ + "vulkan.ast.vert", + })), + FileNameAsCustomTestSuffix +); + +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileVulkanToSpirvTestAMD, + ::testing::ValuesIn(std::vector({ + "spv.16bitxfb.vert", + "spv.float16.frag", + "spv.float16Fetch.frag", + "spv.imageLoadStoreLod.frag", + "spv.int16.frag", + "spv.int16.amd.frag", + "spv.shaderBallotAMD.comp", + "spv.shaderFragMaskAMD.frag", + "spv.textureGatherBiasLod.frag", + })), + FileNameAsCustomTestSuffix +); + +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileVulkanToSpirvTestNV, + ::testing::ValuesIn(std::vector({ + "spv.sampleMaskOverrideCoverage.frag", + "spv.GeometryShaderPassthrough.geom", + "spv.viewportArray2.vert", + "spv.viewportArray2.tesc", + "spv.stereoViewRendering.vert", + "spv.stereoViewRendering.tesc", + "spv.multiviewPerViewAttributes.vert", + "spv.multiviewPerViewAttributes.tesc", + "spv.atomicInt64.comp", + "spv.shadingRate.frag", + "spv.RayGenShader.rgen", + "spv.RayGenShaderArray.rgen", + "spv.RayGenShader_Errors.rgen", + "spv.RayConstants.rgen", + "spv.IntersectShader.rint", + "spv.IntersectShader_Errors.rint", + "spv.AnyHitShader.rahit", + "spv.AnyHitShader_Errors.rahit", + "spv.ClosestHitShader.rchit", + "spv.ClosestHitShader_Errors.rchit", + "spv.MissShader.rmiss", + "spv.MissShader_Errors.rmiss", + "spv.RayCallable.rcall", + "spv.RayCallable_Errors.rcall", + "spv.fragmentShaderBarycentric.frag", + "spv.fragmentShaderBarycentric2.frag", + "spv.computeShaderDerivatives.comp", + "spv.computeShaderDerivatives2.comp", + "spv.shaderImageFootprint.frag", + "spv.meshShaderBuiltins.mesh", + "spv.meshShaderUserDefined.mesh", + "spv.meshShaderPerViewBuiltins.mesh", + "spv.meshShaderPerViewUserDefined.mesh", + "spv.meshShaderPerView_Errors.mesh", + "spv.meshShaderSharedMem.mesh", + "spv.meshShaderTaskMem.mesh", + "spv.320.meshShaderUserDefined.mesh", + "spv.meshShaderRedeclBuiltins.mesh", + "spv.meshShaderRedeclPerViewBuiltins.mesh", + "spv.meshTaskShader.task", + "spv.perprimitiveNV.frag", +})), +FileNameAsCustomTestSuffix +); + +INSTANTIATE_TEST_SUITE_P( + Glsl, CompileUpgradeTextureToSampledTextureAndDropSamplersTest, + ::testing::ValuesIn(std::vector({ + "spv.texture.sampler.transform.frag", + })), + FileNameAsCustomTestSuffix +); +// clang-format on + +} // anonymous namespace +} // namespace glslangtest diff --git a/third_party/glslang/gtests/TestFixture.cpp b/third_party/glslang/gtests/TestFixture.cpp new file mode 100644 index 0000000..ced6fcc --- /dev/null +++ b/third_party/glslang/gtests/TestFixture.cpp @@ -0,0 +1,180 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "TestFixture.h" + +namespace glslangtest { + +std::string FileNameAsCustomTestSuffix( + const ::testing::TestParamInfo& info) +{ + std::string name = info.param; + // A valid test case suffix cannot have '.' and '-' inside. + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), '-', '_'); + return name; +} + +EShLanguage GetShaderStage(const std::string& stage) +{ + if (stage == "vert") { + return EShLangVertex; + } else if (stage == "tesc") { + return EShLangTessControl; + } else if (stage == "tese") { + return EShLangTessEvaluation; + } else if (stage == "geom") { + return EShLangGeometry; + } else if (stage == "frag") { + return EShLangFragment; + } else if (stage == "comp") { + return EShLangCompute; + } else if (stage == "rgen") { + return EShLangRayGen; + } else if (stage == "rint") { + return EShLangIntersect; + } else if (stage == "rahit") { + return EShLangAnyHit; + } else if (stage == "rchit") { + return EShLangClosestHit; + } else if (stage == "rmiss") { + return EShLangMiss; + } else if (stage == "rcall") { + return EShLangCallable; + } else if (stage == "task") { + return EShLangTaskNV; + } else if (stage == "mesh") { + return EShLangMeshNV; + } else { + assert(0 && "Unknown shader stage"); + return EShLangCount; + } +} + +EShMessages DeriveOptions(Source source, Semantics semantics, Target target) +{ + EShMessages result = EShMsgCascadingErrors; + + switch (source) { + case Source::GLSL: + break; + case Source::HLSL: + result = static_cast(result | EShMsgReadHlsl); + break; + } + + switch (target) { + case Target::AST: + result = static_cast(result | EShMsgAST); + break; + case Target::Spv: + result = static_cast(result | EShMsgSpvRules); + result = static_cast(result | EShMsgKeepUncalled); + break; + case Target::BothASTAndSpv: + result = static_cast(result | EShMsgSpvRules | EShMsgAST); + result = static_cast(result | EShMsgKeepUncalled); + break; + }; + + switch (semantics) { + case Semantics::OpenGL: + break; + case Semantics::Vulkan: + result = static_cast(result | EShMsgVulkanRules | EShMsgSpvRules); + break; + } + + result = static_cast(result | EShMsgHlslLegalization); + + return result; +} + +std::pair ReadFile(const std::string& path) +{ + std::ifstream fstream(path, std::ios::in); + if (fstream) { + std::string contents; + fstream.seekg(0, std::ios::end); + contents.reserve((std::string::size_type)fstream.tellg()); + fstream.seekg(0, std::ios::beg); + contents.assign((std::istreambuf_iterator(fstream)), + std::istreambuf_iterator()); + return std::make_pair(true, contents); + } + return std::make_pair(false, ""); +} + +std::pair > ReadSpvBinaryFile(const std::string& path) +{ + std::ifstream fstream(path, std::fstream::in | std::fstream::binary); + + if (!fstream) + return std::make_pair(false, std::vector()); + + std::vector contents; + + // Reserve space (for efficiency, not for correctness) + fstream.seekg(0, fstream.end); + contents.reserve(size_t(fstream.tellg()) / sizeof(std::uint32_t)); + fstream.seekg(0, fstream.beg); + + // There is no istream iterator traversing by uint32_t, so we must loop. + while (!fstream.eof()) { + std::uint32_t inWord; + fstream.read((char *)&inWord, sizeof(inWord)); + + if (!fstream.eof()) + contents.push_back(inWord); + } + + return std::make_pair(true, contents); // hopefully, c++11 move semantics optimizes the copy away. +} + +bool WriteFile(const std::string& path, const std::string& contents) +{ + std::ofstream fstream(path, std::ios::out); + if (!fstream) return false; + fstream << contents; + fstream.flush(); + return true; +} + +std::string GetSuffix(const std::string& name) +{ + const size_t pos = name.rfind('.'); + return (pos == std::string::npos) ? "" : name.substr(name.rfind('.') + 1); +} + +} // namespace glslangtest diff --git a/third_party/glslang/gtests/TestFixture.h b/third_party/glslang/gtests/TestFixture.h new file mode 100755 index 0000000..2b057dc --- /dev/null +++ b/third_party/glslang/gtests/TestFixture.h @@ -0,0 +1,715 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef GLSLANG_GTESTS_TEST_FIXTURE_H +#define GLSLANG_GTESTS_TEST_FIXTURE_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "SPIRV/GlslangToSpv.h" +#include "SPIRV/disassemble.h" +#include "SPIRV/doc.h" +#include "SPIRV/SPVRemapper.h" +#include "StandAlone/ResourceLimits.h" +#include "glslang/Public/ShaderLang.h" + +#include "Initializer.h" +#include "Settings.h" + +namespace glslangtest { + +// This function is used to provide custom test name suffixes based on the +// shader source file names. Otherwise, the test name suffixes will just be +// numbers, which are not quite obvious. +std::string FileNameAsCustomTestSuffix( + const ::testing::TestParamInfo& info); + +enum class Source { + GLSL, + HLSL, +}; + +// Enum for shader compilation semantics. +enum class Semantics { + OpenGL, + Vulkan +}; + +// Enum for compilation target. +enum class Target { + AST, + Spv, + BothASTAndSpv, +}; + +EShLanguage GetShaderStage(const std::string& stage); + +EShMessages DeriveOptions(Source, Semantics, Target); + +// Reads the content of the file at the given |path|. On success, returns true +// and the contents; otherwise, returns false and an empty string. +std::pair ReadFile(const std::string& path); +std::pair > ReadSpvBinaryFile(const std::string& path); + +// Writes the given |contents| into the file at the given |path|. Returns true +// on successful output. +bool WriteFile(const std::string& path, const std::string& contents); + +// Returns the suffix of the given |name|. +std::string GetSuffix(const std::string& name); + +// Base class for glslang integration tests. It contains many handy utility-like +// methods such as reading shader source files, compiling into AST/SPIR-V, and +// comparing with expected outputs. +// +// To write value-Parameterized tests: +// using ValueParamTest = GlslangTest<::testing::TestWithParam>; +// To use as normal fixture: +// using FixtureTest = GlslangTest<::testing::Test>; +template +class GlslangTest : public GT { +public: + GlslangTest() + : defaultVersion(100), + defaultProfile(ENoProfile), + forceVersionProfile(false), + isForwardCompatible(false) { + // Perform validation by default. + spirvOptions.validate = true; + } + + // Tries to load the contents from the file at the given |path|. On success, + // writes the contents into |contents|. On failure, errors out. + void tryLoadFile(const std::string& path, const std::string& tag, + std::string* contents) + { + bool fileReadOk; + std::tie(fileReadOk, *contents) = ReadFile(path); + ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path; + } + + // Tries to load the contents from the file at the given |path|. On success, + // writes the contents into |contents|. On failure, errors out. + void tryLoadSpvFile(const std::string& path, const std::string& tag, + std::vector& contents) + { + bool fileReadOk; + std::tie(fileReadOk, contents) = ReadSpvBinaryFile(path); + ASSERT_TRUE(fileReadOk) << "Cannot open " << tag << " file: " << path; + } + + // Checks the equality of |expected| and |real|. If they are not equal, + // write |real| to the given file named as |fname| if update mode is on. + void checkEqAndUpdateIfRequested(const std::string& expected, + const std::string& real, + const std::string& fname, + const std::string& errorsAndWarnings = "") + { + // In order to output the message we want under proper circumstances, + // we need the following operator<< stuff. + EXPECT_EQ(expected, real) + << (GlobalTestSettings.updateMode + ? ("Mismatch found and update mode turned on - " + "flushing expected result output.\n") + : "") + << "The following warnings/errors occurred:\n" + << errorsAndWarnings; + + // Update the expected output file if requested. + // It looks weird to duplicate the comparison between expected_output + // and stream.str(). However, if creating a variable for the comparison + // result, we cannot have pretty print of the string diff in the above. + if (GlobalTestSettings.updateMode && expected != real) { + EXPECT_TRUE(WriteFile(fname, real)) << "Flushing failed"; + } + } + + struct ShaderResult { + std::string shaderName; + std::string output; + std::string error; + }; + + // A struct for holding all the information returned by glslang compilation + // and linking. + struct GlslangResult { + std::vector shaderResults; + std::string linkingOutput; + std::string linkingError; + bool validationResult; + std::string spirvWarningsErrors; + std::string spirv; // Optional SPIR-V disassembly text. + }; + + // Compiles and the given source |code| of the given shader |stage| into + // the target under the semantics conveyed via |controls|. Returns true + // and modifies |shader| on success. + bool compile(glslang::TShader* shader, const std::string& code, + const std::string& entryPointName, EShMessages controls, + const TBuiltInResource* resources=nullptr, + const std::string* shaderName=nullptr) + { + const char* shaderStrings = code.data(); + const int shaderLengths = static_cast(code.size()); + const char* shaderNames = nullptr; + + if ((controls & EShMsgDebugInfo) && shaderName != nullptr) { + shaderNames = shaderName->data(); + shader->setStringsWithLengthsAndNames( + &shaderStrings, &shaderLengths, &shaderNames, 1); + } else + shader->setStringsWithLengths(&shaderStrings, &shaderLengths, 1); + if (!entryPointName.empty()) shader->setEntryPoint(entryPointName.c_str()); + return shader->parse( + (resources ? resources : &glslang::DefaultTBuiltInResource), + defaultVersion, isForwardCompatible, controls); + } + + // Compiles and links the given source |code| of the given shader + // |stage| into the target under the semantics specified via |controls|. + // Returns a GlslangResult instance containing all the information generated + // during the process. If the target includes SPIR-V, also disassembles + // the result and returns disassembly text. + GlslangResult compileAndLink( + const std::string& shaderName, const std::string& code, + const std::string& entryPointName, EShMessages controls, + glslang::EShTargetClientVersion clientTargetVersion, + glslang::EShTargetLanguageVersion targetLanguageVersion, + bool flattenUniformArrays = false, + EShTextureSamplerTransformMode texSampTransMode = EShTexSampTransKeep, + bool enableOptimizer = false, + bool enableDebug = false, + bool automap = true) + { + const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); + + glslang::TShader shader(stage); + if (automap) { + shader.setAutoMapLocations(true); + shader.setAutoMapBindings(true); + } + shader.setTextureSamplerTransformMode(texSampTransMode); +#ifdef ENABLE_HLSL + shader.setFlattenUniformArrays(flattenUniformArrays); +#endif + + if (controls & EShMsgSpvRules) { + if (controls & EShMsgVulkanRules) { + shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl + : glslang::EShSourceGlsl, + stage, glslang::EShClientVulkan, 100); + shader.setEnvClient(glslang::EShClientVulkan, clientTargetVersion); + shader.setEnvTarget(glslang::EShTargetSpv, targetLanguageVersion); + } else { + shader.setEnvInput((controls & EShMsgReadHlsl) ? glslang::EShSourceHlsl + : glslang::EShSourceGlsl, + stage, glslang::EShClientOpenGL, 100); + shader.setEnvClient(glslang::EShClientOpenGL, clientTargetVersion); + shader.setEnvTarget(glslang::EshTargetSpv, glslang::EShTargetSpv_1_0); + } + } + + bool success = compile( + &shader, code, entryPointName, controls, nullptr, &shaderName); + + glslang::TProgram program; + program.addShader(&shader); + success &= program.link(controls); +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + if (success) + program.mapIO(); +#endif + + if (success && (controls & EShMsgSpvRules)) { + spv::SpvBuildLogger logger; + std::vector spirv_binary; + options().disableOptimizer = !enableOptimizer; + options().generateDebugInfo = enableDebug; + glslang::GlslangToSpv(*program.getIntermediate(stage), + spirv_binary, &logger, &options()); + + std::ostringstream disassembly_stream; + spv::Parameterize(); + spv::Disassemble(disassembly_stream, spirv_binary); + bool validation_result = !options().validate || logger.getAllMessages().empty(); + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), + validation_result, logger.getAllMessages(), disassembly_stream.str()}; + } else { + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; + } + } + + // Compiles and links the given source |code| of the given shader + // |stage| into the target under the semantics specified via |controls|. + // Returns a GlslangResult instance containing all the information generated + // during the process. If the target includes SPIR-V, also disassembles + // the result and returns disassembly text. + GlslangResult compileLinkIoMap( + const std::string shaderName, const std::string& code, + const std::string& entryPointName, EShMessages controls, + int baseSamplerBinding, + int baseTextureBinding, + int baseImageBinding, + int baseUboBinding, + int baseSsboBinding, + bool autoMapBindings, + bool flattenUniformArrays) + { + const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); + + glslang::TShader shader(stage); + shader.setShiftSamplerBinding(baseSamplerBinding); + shader.setShiftTextureBinding(baseTextureBinding); + shader.setShiftImageBinding(baseImageBinding); + shader.setShiftUboBinding(baseUboBinding); + shader.setShiftSsboBinding(baseSsboBinding); + shader.setAutoMapBindings(autoMapBindings); + shader.setAutoMapLocations(true); +#ifdef ENABLE_HLSL + shader.setFlattenUniformArrays(flattenUniformArrays); +#endif + + bool success = compile(&shader, code, entryPointName, controls); + + glslang::TProgram program; + program.addShader(&shader); + + success &= program.link(controls); +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + if (success) + program.mapIO(); +#endif + + spv::SpvBuildLogger logger; + + if (success && (controls & EShMsgSpvRules)) { + std::vector spirv_binary; + glslang::GlslangToSpv(*program.getIntermediate(stage), + spirv_binary, &logger, &options()); + + std::ostringstream disassembly_stream; + spv::Parameterize(); + spv::Disassemble(disassembly_stream, spirv_binary); + bool validation_result = !options().validate || logger.getAllMessages().empty(); + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), + validation_result, logger.getAllMessages(), disassembly_stream.str()}; + } else { + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; + } + } + + // This is like compileAndLink but with remapping of the SPV binary + // through spirvbin_t::remap(). While technically this could be merged + // with compileAndLink() above (with the remap step optionally being a no-op) + // it is given separately here for ease of future extraction. + GlslangResult compileLinkRemap( + const std::string shaderName, const std::string& code, + const std::string& entryPointName, EShMessages controls, + const unsigned int remapOptions = spv::spirvbin_t::NONE) + { + const EShLanguage stage = GetShaderStage(GetSuffix(shaderName)); + + glslang::TShader shader(stage); + shader.setAutoMapBindings(true); + shader.setAutoMapLocations(true); + + bool success = compile(&shader, code, entryPointName, controls); + + glslang::TProgram program; + program.addShader(&shader); + success &= program.link(controls); +#if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + if (success) + program.mapIO(); +#endif + + if (success && (controls & EShMsgSpvRules)) { + spv::SpvBuildLogger logger; + std::vector spirv_binary; + glslang::GlslangToSpv(*program.getIntermediate(stage), + spirv_binary, &logger, &options()); + + spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions); + + std::ostringstream disassembly_stream; + spv::Parameterize(); + spv::Disassemble(disassembly_stream, spirv_binary); + bool validation_result = !options().validate || logger.getAllMessages().empty(); + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), + validation_result, logger.getAllMessages(), disassembly_stream.str()}; + } else { + return {{{shaderName, shader.getInfoLog(), shader.getInfoDebugLog()},}, + program.getInfoLog(), program.getInfoDebugLog(), true, "", ""}; + } + } + + // remap the binary in 'code' with the options in remapOptions + GlslangResult remap( + const std::string shaderName, const std::vector& code, + EShMessages controls, + const unsigned int remapOptions = spv::spirvbin_t::NONE) + { + if ((controls & EShMsgSpvRules)) { + std::vector spirv_binary(code); // scratch copy + + spv::spirvbin_t(0 /*verbosity*/).remap(spirv_binary, remapOptions); + + std::ostringstream disassembly_stream; + spv::Parameterize(); + spv::Disassemble(disassembly_stream, spirv_binary); + + return {{{shaderName, "", ""},}, + "", "", + true, "", disassembly_stream.str()}; + } else { + return {{{shaderName, "", ""},}, "", "", true, "", ""}; + } + } + + void outputResultToStream(std::ostringstream* stream, + const GlslangResult& result, + EShMessages controls) + { + const auto outputIfNotEmpty = [&stream](const std::string& str) { + if (!str.empty()) *stream << str << "\n"; + }; + + for (const auto& shaderResult : result.shaderResults) { + *stream << shaderResult.shaderName << "\n"; + outputIfNotEmpty(shaderResult.output); + outputIfNotEmpty(shaderResult.error); + } + outputIfNotEmpty(result.linkingOutput); + outputIfNotEmpty(result.linkingError); + if (!result.validationResult) { + *stream << "Validation failed\n"; + } + + if (controls & EShMsgSpvRules) { + *stream + << (result.spirv.empty() + ? "SPIR-V is not generated for failed compile or link\n" + : result.spirv); + } + } + + void loadFileCompileAndCheck(const std::string& testDir, + const std::string& testName, + Source source, + Semantics semantics, + glslang::EShTargetClientVersion clientTargetVersion, + glslang::EShTargetLanguageVersion targetLanguageVersion, + Target target, + bool automap = true, + const std::string& entryPointName="", + const std::string& baseDir="/baseResults/", + const bool enableOptimizer = false, + const bool enableDebug = false) + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = + testDir + baseDir + testName + ".out"; + std::string input, expectedOutput; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + EShMessages controls = DeriveOptions(source, semantics, target); + if (enableOptimizer) + controls = static_cast(controls & ~EShMsgHlslLegalization); + if (enableDebug) + controls = static_cast(controls | EShMsgDebugInfo); + GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion, + targetLanguageVersion, false, EShTexSampTransKeep, enableOptimizer, enableDebug, automap); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), + expectedOutputFname, result.spirvWarningsErrors); + } + + void loadFileCompileAndCheckWithOptions(const std::string &testDir, + const std::string &testName, + Source source, + Semantics semantics, + glslang::EShTargetClientVersion clientTargetVersion, + glslang::EShTargetLanguageVersion targetLanguageVersion, + Target target, bool automap = true, const std::string &entryPointName = "", + const std::string &baseDir = "/baseResults/", + const EShMessages additionalOptions = EShMessages::EShMsgDefault) + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = testDir + baseDir + testName + ".out"; + std::string input, expectedOutput; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + EShMessages controls = DeriveOptions(source, semantics, target); + controls = static_cast(controls | additionalOptions); + GlslangResult result = compileAndLink(testName, input, entryPointName, controls, clientTargetVersion, + targetLanguageVersion, false, EShTexSampTransKeep, false, automap); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), expectedOutputFname); + } + + void loadFileCompileFlattenUniformsAndCheck(const std::string& testDir, + const std::string& testName, + Source source, + Semantics semantics, + Target target, + const std::string& entryPointName="") + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = + testDir + "/baseResults/" + testName + ".out"; + std::string input, expectedOutput; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + const EShMessages controls = DeriveOptions(source, semantics, target); + GlslangResult result = compileAndLink(testName, input, entryPointName, controls, + glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, true); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), + expectedOutputFname, result.spirvWarningsErrors); + } + + void loadFileCompileIoMapAndCheck(const std::string& testDir, + const std::string& testName, + Source source, + Semantics semantics, + Target target, + const std::string& entryPointName, + int baseSamplerBinding, + int baseTextureBinding, + int baseImageBinding, + int baseUboBinding, + int baseSsboBinding, + bool autoMapBindings, + bool flattenUniformArrays) + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = + testDir + "/baseResults/" + testName + ".out"; + std::string input, expectedOutput; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + const EShMessages controls = DeriveOptions(source, semantics, target); + GlslangResult result = compileLinkIoMap(testName, input, entryPointName, controls, + baseSamplerBinding, baseTextureBinding, baseImageBinding, + baseUboBinding, baseSsboBinding, + autoMapBindings, + flattenUniformArrays); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), + expectedOutputFname, result.spirvWarningsErrors); + } + + void loadFileCompileRemapAndCheck(const std::string& testDir, + const std::string& testName, + Source source, + Semantics semantics, + Target target, + const std::string& entryPointName="", + const unsigned int remapOptions = spv::spirvbin_t::NONE) + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = + testDir + "/baseResults/" + testName + ".out"; + std::string input, expectedOutput; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + const EShMessages controls = DeriveOptions(source, semantics, target); + GlslangResult result = compileLinkRemap(testName, input, entryPointName, controls, remapOptions); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), + expectedOutputFname, result.spirvWarningsErrors); + } + + void loadFileRemapAndCheck(const std::string& testDir, + const std::string& testName, + Source source, + Semantics semantics, + Target target, + const unsigned int remapOptions = spv::spirvbin_t::NONE) + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = + testDir + "/baseResults/" + testName + ".out"; + std::vector input; + std::string expectedOutput; + + tryLoadSpvFile(inputFname, "input", input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + const EShMessages controls = DeriveOptions(source, semantics, target); + GlslangResult result = remap(testName, input, controls, remapOptions); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), + expectedOutputFname, result.spirvWarningsErrors); + } + + // Preprocesses the given |source| code. On success, returns true, the + // preprocessed shader, and warning messages. Otherwise, returns false, an + // empty string, and error messages. + std::tuple preprocess( + const std::string& source) + { + const char* shaderStrings = source.data(); + const int shaderLengths = static_cast(source.size()); + + glslang::TShader shader(EShLangVertex); + shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1); + std::string ppShader; + glslang::TShader::ForbidIncluder includer; + const bool success = shader.preprocess( + &glslang::DefaultTBuiltInResource, defaultVersion, defaultProfile, + forceVersionProfile, isForwardCompatible, (EShMessages)(EShMsgOnlyPreprocessor | EShMsgCascadingErrors), + &ppShader, includer); + + std::string log = shader.getInfoLog(); + log += shader.getInfoDebugLog(); + if (success) { + return std::make_tuple(true, ppShader, log); + } else { + return std::make_tuple(false, "", log); + } + } + + void loadFilePreprocessAndCheck(const std::string& testDir, + const std::string& testName) + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = + testDir + "/baseResults/" + testName + ".out"; + const std::string expectedErrorFname = + testDir + "/baseResults/" + testName + ".err"; + std::string input, expectedOutput, expectedError; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + tryLoadFile(expectedErrorFname, "expected error", &expectedError); + + bool ppOk; + std::string output, error; + std::tie(ppOk, output, error) = preprocess(input); + if (!output.empty()) output += '\n'; + if (!error.empty()) error += '\n'; + + checkEqAndUpdateIfRequested(expectedOutput, output, + expectedOutputFname); + checkEqAndUpdateIfRequested(expectedError, error, + expectedErrorFname); + } + + void loadCompileUpgradeTextureToSampledTextureAndDropSamplersAndCheck(const std::string& testDir, + const std::string& testName, + Source source, + Semantics semantics, + Target target, + const std::string& entryPointName = "") + { + const std::string inputFname = testDir + "/" + testName; + const std::string expectedOutputFname = testDir + "/baseResults/" + testName + ".out"; + std::string input, expectedOutput; + + tryLoadFile(inputFname, "input", &input); + tryLoadFile(expectedOutputFname, "expected output", &expectedOutput); + + const EShMessages controls = DeriveOptions(source, semantics, target); + GlslangResult result = compileAndLink(testName, input, entryPointName, controls, + glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0, false, + EShTexSampTransUpgradeTextureRemoveSampler); + + // Generate the hybrid output in the way of glslangValidator. + std::ostringstream stream; + outputResultToStream(&stream, result, controls); + + checkEqAndUpdateIfRequested(expectedOutput, stream.str(), + expectedOutputFname, result.spirvWarningsErrors); + } + + glslang::SpvOptions& options() { return spirvOptions; } + +private: + const int defaultVersion; + const EProfile defaultProfile; + const bool forceVersionProfile; + const bool isForwardCompatible; + glslang::SpvOptions spirvOptions; +}; + +} // namespace glslangtest + +#endif // GLSLANG_GTESTS_TEST_FIXTURE_H diff --git a/third_party/glslang/gtests/main.cpp b/third_party/glslang/gtests/main.cpp new file mode 100644 index 0000000..9cd06d1 --- /dev/null +++ b/third_party/glslang/gtests/main.cpp @@ -0,0 +1,79 @@ +// +// Copyright (C) 2016 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include + +#include "Initializer.h" +#include "Settings.h" + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + + std::unique_ptr initializer( + new glslangtest::GlslangInitializer); + + glslangtest::GlobalTestSettings.initializer = initializer.get(); + + for (int i = 1; i < argc; ++i) { + if (std::string("--update-mode") == argv[i]) { + glslangtest::GlobalTestSettings.updateMode = true; + } + if (std::string("--test-root") == argv[i]) { + // Allow the user set the test root directory. This is useful + // for testing with files from another source tree. + if (i + 1 < argc) { + glslangtest::GlobalTestSettings.testRoot = argv[i + 1]; + i++; + } else { + printf("error: --test-root requires an argument\n"); + return 1; + } + } + if (std::string("--help") == argv[i]) { + printf("\nExtra options:\n\n"); + printf(" --update-mode\n Update the golden results for the tests.\n"); + printf(" --test-root \n Specify the test root directory (useful for testing with\n files from another source tree).\n"); + } + } + + const int result = RUN_ALL_TESTS(); + + glslangtest::GlobalTestSettings.initializer = nullptr; + + return result; +} diff --git a/third_party/glslang/gtests/pch.h b/third_party/glslang/gtests/pch.h new file mode 100644 index 0000000..94e95b1 --- /dev/null +++ b/third_party/glslang/gtests/pch.h @@ -0,0 +1,39 @@ +#ifndef _PCH_H +#define _PCH_H +// +// Copyright (C) 2018 The Khronos Group Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include "TestFixture.h" + +#endif /* _PCH_H */ diff --git a/third_party/glslang/hlsl/CMakeLists.txt b/third_party/glslang/hlsl/CMakeLists.txt new file mode 100644 index 0000000..62faa19 --- /dev/null +++ b/third_party/glslang/hlsl/CMakeLists.txt @@ -0,0 +1,59 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# The HLSL source is directly embedded into the glslang target when ENABLE_HLSL +# is set. +# This source now lives at: glslang/HLSL/ +# The HLSL target is now just a stub that exists for backwards compatibility for +# projects that referenced this target. + +add_library(HLSL ${LIB_TYPE} "stub.cpp") +set_property(TARGET HLSL PROPERTY FOLDER hlsl) +set_property(TARGET HLSL PROPERTY POSITION_INDEPENDENT_CODE ON) + +if(WIN32 AND BUILD_SHARED_LIBS) + set_target_properties(HLSL PROPERTIES PREFIX "") +endif() + +if(ENABLE_GLSLANG_INSTALL) + if(BUILD_SHARED_LIBS) + install(TARGETS HLSL EXPORT HLSLTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + else() + install(TARGETS HLSL EXPORT HLSLTargets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + install(EXPORT HLSLTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) +endif(ENABLE_GLSLANG_INSTALL) diff --git a/third_party/glslang/hlsl/stub.cpp b/third_party/glslang/hlsl/stub.cpp new file mode 100644 index 0000000..f1d39c1 --- /dev/null +++ b/third_party/glslang/hlsl/stub.cpp @@ -0,0 +1,41 @@ +// +// Copyright (C) 2020 Google, Inc. +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +// The HLSL source is directly embedded into the glslang target when ENABLE_HLSL +// is set. +// This source now lives at: glslang/HLSL/ +// The HLSL target is now just a stub that exists for backwards compatibility +// for projects that referenced this target. As a target requires at least one +// source file to build, this file acts as that stub. diff --git a/third_party/glslang/known_good.json b/third_party/glslang/known_good.json new file mode 100644 index 0000000..5c498dc --- /dev/null +++ b/third_party/glslang/known_good.json @@ -0,0 +1,18 @@ +{ + "commits" : [ + { + "name" : "spirv-tools", + "site" : "github", + "subrepo" : "KhronosGroup/SPIRV-Tools", + "subdir" : "External/spirv-tools", + "commit" : "56d0f50357a192602216bfc4873e714905323e35" + }, + { + "name" : "spirv-tools/external/spirv-headers", + "site" : "github", + "subrepo" : "KhronosGroup/SPIRV-Headers", + "subdir" : "External/spirv-tools/external/spirv-headers", + "commit" : "05836bdba63e7debce9fa9feaed42f20cd43af9d" + } + ] +} diff --git a/third_party/glslang/known_good_khr.json b/third_party/glslang/known_good_khr.json new file mode 100644 index 0000000..a64198a --- /dev/null +++ b/third_party/glslang/known_good_khr.json @@ -0,0 +1,18 @@ +{ + "commits" : [ + { + "name" : "spirv-tools", + "site" : "gitlab", + "subrepo" : "spirv/spirv-tools", + "subdir" : "External/spirv-tools", + "commit" : "d4e2c2eaa6fd2e9f9cd218ea9add9b0c8ae759ba" + }, + { + "name" : "spirv-tools/external/spirv-headers", + "site" : "gitlab", + "subrepo" : "spirv/SPIRV-Headers", + "subdir" : "External/spirv-tools/external/spirv-headers", + "commit" : "gitlab-prelim-rc4" + } + ] +} diff --git a/third_party/glslang/kokoro/android-ndk-build/build-docker.sh b/third_party/glslang/kokoro/android-ndk-build/build-docker.sh new file mode 100755 index 0000000..2b23b27 --- /dev/null +++ b/third_party/glslang/kokoro/android-ndk-build/build-docker.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. + +. /bin/using.sh # Declare the bash `using` function for configuring toolchains. + +set -x # Display commands being run. + +using ndk-r21d + +export NDK_PROJECT_PATH="${ROOT_DIR}/ndk_test" +export APP_BUILD_SCRIPT="${ROOT_DIR}/ndk_test/Android.mk" + +echo "Building..." +ndk-build -j diff --git a/third_party/glslang/kokoro/android-ndk-build/build.sh b/third_party/glslang/kokoro/android-ndk-build/build.sh new file mode 100755 index 0000000..5924f33 --- /dev/null +++ b/third_party/glslang/kokoro/android-ndk-build/build.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )" + +docker run --rm -i \ + --volume "${ROOT_DIR}:${ROOT_DIR}" \ + --workdir "${ROOT_DIR}" \ + --env ROOT_DIR="${ROOT_DIR}" \ + --env SCRIPT_DIR="${SCRIPT_DIR}" \ + --entrypoint "${SCRIPT_DIR}/build-docker.sh" \ + "gcr.io/shaderc-build/radial-build:latest" diff --git a/third_party/glslang/kokoro/android-ndk-build/continuous.cfg b/third_party/glslang/kokoro/android-ndk-build/continuous.cfg new file mode 100644 index 0000000..ee0c4fb --- /dev/null +++ b/third_party/glslang/kokoro/android-ndk-build/continuous.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/android-ndk-build/build.sh" diff --git a/third_party/glslang/kokoro/android-ndk-build/presubmit.cfg b/third_party/glslang/kokoro/android-ndk-build/presubmit.cfg new file mode 100644 index 0000000..16f4fb7 --- /dev/null +++ b/third_party/glslang/kokoro/android-ndk-build/presubmit.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/android-ndk-build/build.sh" diff --git a/third_party/glslang/kokoro/license-check/build-docker.sh b/third_party/glslang/kokoro/license-check/build-docker.sh new file mode 100755 index 0000000..41b0592 --- /dev/null +++ b/third_party/glslang/kokoro/license-check/build-docker.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. +set -x # Display commands being run. + +license-checker --dir="$ROOT_DIR" diff --git a/third_party/glslang/kokoro/license-check/build.sh b/third_party/glslang/kokoro/license-check/build.sh new file mode 100755 index 0000000..6e9d980 --- /dev/null +++ b/third_party/glslang/kokoro/license-check/build.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )" + +docker run --rm -i \ + --volume "${ROOT_DIR}:${ROOT_DIR}:ro" \ + --workdir "${ROOT_DIR}" \ + --env ROOT_DIR="${ROOT_DIR}" \ + --env SCRIPT_DIR="${SCRIPT_DIR}" \ + --entrypoint "${SCRIPT_DIR}/build-docker.sh" \ + "gcr.io/shaderc-build/radial-build:latest" diff --git a/third_party/glslang/kokoro/license-check/continuous.cfg b/third_party/glslang/kokoro/license-check/continuous.cfg new file mode 100644 index 0000000..1f36031 --- /dev/null +++ b/third_party/glslang/kokoro/license-check/continuous.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/license-check/build.sh" diff --git a/third_party/glslang/kokoro/license-check/presubmit.cfg b/third_party/glslang/kokoro/license-check/presubmit.cfg new file mode 100644 index 0000000..4cf9238 --- /dev/null +++ b/third_party/glslang/kokoro/license-check/presubmit.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/license-check/build.sh" diff --git a/third_party/glslang/kokoro/linux-clang-cmake/build-docker.sh b/third_party/glslang/kokoro/linux-clang-cmake/build-docker.sh new file mode 100755 index 0000000..c5fdcd2 --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-cmake/build-docker.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. + +. /bin/using.sh # Declare the bash `using` function for configuring toolchains. + +set -x # Display commands being run. + +using cmake-3.17.2 +using clang-10.0.0 +using ninja-1.10.0 + +echo "Building..." +mkdir /build && cd /build + +cmake "$ROOT_DIR" -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DBUILD_SHARED_LIBS=$BUILD_SHARED_LIBS +ninja install diff --git a/third_party/glslang/kokoro/linux-clang-cmake/build.sh b/third_party/glslang/kokoro/linux-clang-cmake/build.sh new file mode 100755 index 0000000..53d8291 --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-cmake/build.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )" + +docker run --rm -i \ + --volume "${ROOT_DIR}:${ROOT_DIR}:ro" \ + --workdir "${ROOT_DIR}" \ + --env ROOT_DIR="${ROOT_DIR}" \ + --env SCRIPT_DIR="${SCRIPT_DIR}" \ + --env BUILD_SHARED_LIBS="${BUILD_SHARED_LIBS:-0}" \ + --entrypoint "${SCRIPT_DIR}/build-docker.sh" \ + "gcr.io/shaderc-build/radial-build:latest" diff --git a/third_party/glslang/kokoro/linux-clang-cmake/shared/continuous.cfg b/third_party/glslang/kokoro/linux-clang-cmake/shared/continuous.cfg new file mode 100644 index 0000000..e3de7ac --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-cmake/shared/continuous.cfg @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/linux-clang-cmake/build.sh" + +env_vars { + key: "BUILD_SHARED_LIBS" + value: "1" +} diff --git a/third_party/glslang/kokoro/linux-clang-cmake/shared/presubmit.cfg b/third_party/glslang/kokoro/linux-clang-cmake/shared/presubmit.cfg new file mode 100644 index 0000000..2d2e320 --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-cmake/shared/presubmit.cfg @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/linux-clang-cmake/build.sh" + +env_vars { + key: "BUILD_SHARED_LIBS" + value: "1" +} diff --git a/third_party/glslang/kokoro/linux-clang-cmake/static/continuous.cfg b/third_party/glslang/kokoro/linux-clang-cmake/static/continuous.cfg new file mode 100644 index 0000000..f8f86c3 --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-cmake/static/continuous.cfg @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/linux-clang-cmake/build.sh" + +env_vars { + key: "BUILD_SHARED_LIBS" + value: "0" +} diff --git a/third_party/glslang/kokoro/linux-clang-cmake/static/presubmit.cfg b/third_party/glslang/kokoro/linux-clang-cmake/static/presubmit.cfg new file mode 100644 index 0000000..9f30ddc --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-cmake/static/presubmit.cfg @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/linux-clang-cmake/build.sh" + +env_vars { + key: "BUILD_SHARED_LIBS" + value: "0" +} diff --git a/third_party/glslang/kokoro/linux-clang-gn/build-docker.sh b/third_party/glslang/kokoro/linux-clang-gn/build-docker.sh new file mode 100755 index 0000000..1035ab8 --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-gn/build-docker.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. +set -x # Display commands being run. + +echo "Fetching external projects..." +./update_glslang_sources.py + +echo "Fetching depot_tools..." +mkdir -p /tmp/depot_tools +curl https://storage.googleapis.com/chrome-infra/depot_tools.zip -o /tmp/depot_tools.zip +unzip /tmp/depot_tools.zip -d /tmp/depot_tools +rm /tmp/depot_tools.zip +export PATH="/tmp/depot_tools:$PATH" + +echo "Syncing client..." +gclient sync --gclientfile=standalone.gclient +gn gen out/Default + +echo "Building..." +cd out/Default +ninja diff --git a/third_party/glslang/kokoro/linux-clang-gn/build.sh b/third_party/glslang/kokoro/linux-clang-gn/build.sh new file mode 100755 index 0000000..563432a --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-gn/build.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )" + +docker run --rm -i \ + --volume "${ROOT_DIR}:${ROOT_DIR}" \ + --workdir "${ROOT_DIR}" \ + --env ROOT_DIR="${ROOT_DIR}" \ + --env SCRIPT_DIR="${SCRIPT_DIR}" \ + --entrypoint "${SCRIPT_DIR}/build-docker.sh" \ + "gcr.io/shaderc-build/radial-build:latest" + +sudo chown -R "$(id -u):$(id -g)" "${ROOT_DIR}" diff --git a/third_party/glslang/kokoro/linux-clang-gn/continuous.cfg b/third_party/glslang/kokoro/linux-clang-gn/continuous.cfg new file mode 100644 index 0000000..1b19146 --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-gn/continuous.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/linux-clang-gn/build.sh" diff --git a/third_party/glslang/kokoro/linux-clang-gn/presubmit.cfg b/third_party/glslang/kokoro/linux-clang-gn/presubmit.cfg new file mode 100644 index 0000000..a72b9a8 --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-gn/presubmit.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/linux-clang-gn/build.sh" diff --git a/third_party/glslang/kokoro/linux-clang-release-bazel/build.sh b/third_party/glslang/kokoro/linux-clang-release-bazel/build.sh new file mode 100644 index 0000000..190e3d7 --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-release-bazel/build.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Copyright (C) 2019 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +CC=clang +CXX=clang++ +SRC=$PWD/github/glslang +cd $SRC + +# Bazel limitation: No 'External' directory is allowed!! +mv External third_party + +gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-linux-x86_64 . +chmod +x bazel-0.29.1-linux-x86_64 + +echo $(date): Build everything... +./bazel-0.29.1-linux-x86_64 build :all +echo $(date): Build completed. + +echo $(date): Starting bazel test... +./bazel-0.29.1-linux-x86_64 test :all --test_output=all +echo $(date): Bazel test completed. diff --git a/third_party/glslang/kokoro/linux-clang-release-bazel/continuous.cfg b/third_party/glslang/kokoro/linux-clang-release-bazel/continuous.cfg new file mode 100644 index 0000000..767556d --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-release-bazel/continuous.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/linux-clang-release-bazel/build.sh" diff --git a/third_party/glslang/kokoro/linux-clang-release-bazel/presubmit.cfg b/third_party/glslang/kokoro/linux-clang-release-bazel/presubmit.cfg new file mode 100644 index 0000000..669491f --- /dev/null +++ b/third_party/glslang/kokoro/linux-clang-release-bazel/presubmit.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/linux-clang-release-bazel/build.sh" diff --git a/third_party/glslang/kokoro/linux-gcc-cmake/build-docker.sh b/third_party/glslang/kokoro/linux-gcc-cmake/build-docker.sh new file mode 100755 index 0000000..0edc05e --- /dev/null +++ b/third_party/glslang/kokoro/linux-gcc-cmake/build-docker.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. + +. /bin/using.sh # Declare the bash `using` function for configuring toolchains. + +set -x # Display commands being run. + +using cmake-3.17.2 +using gcc-9 +using ninja-1.10.0 + +echo "Building..." +mkdir /build && cd /build + +cmake "$ROOT_DIR" -GNinja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$(pwd)/install" -DBUILD_SHARED_LIBS=$BUILD_SHARED_LIBS +ninja install diff --git a/third_party/glslang/kokoro/linux-gcc-cmake/build.sh b/third_party/glslang/kokoro/linux-gcc-cmake/build.sh new file mode 100755 index 0000000..53d8291 --- /dev/null +++ b/third_party/glslang/kokoro/linux-gcc-cmake/build.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +set -e # Fail on any error. + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )" +ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )" + +docker run --rm -i \ + --volume "${ROOT_DIR}:${ROOT_DIR}:ro" \ + --workdir "${ROOT_DIR}" \ + --env ROOT_DIR="${ROOT_DIR}" \ + --env SCRIPT_DIR="${SCRIPT_DIR}" \ + --env BUILD_SHARED_LIBS="${BUILD_SHARED_LIBS:-0}" \ + --entrypoint "${SCRIPT_DIR}/build-docker.sh" \ + "gcr.io/shaderc-build/radial-build:latest" diff --git a/third_party/glslang/kokoro/linux-gcc-cmake/shared/continuous.cfg b/third_party/glslang/kokoro/linux-gcc-cmake/shared/continuous.cfg new file mode 100644 index 0000000..0bbc221 --- /dev/null +++ b/third_party/glslang/kokoro/linux-gcc-cmake/shared/continuous.cfg @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/linux-gcc-cmake/build.sh" + +env_vars { + key: "BUILD_SHARED_LIBS" + value: "1" +} diff --git a/third_party/glslang/kokoro/linux-gcc-cmake/shared/presubmit.cfg b/third_party/glslang/kokoro/linux-gcc-cmake/shared/presubmit.cfg new file mode 100644 index 0000000..9f5f03e --- /dev/null +++ b/third_party/glslang/kokoro/linux-gcc-cmake/shared/presubmit.cfg @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/linux-gcc-cmake/build.sh" + +env_vars { + key: "BUILD_SHARED_LIBS" + value: "1" +} diff --git a/third_party/glslang/kokoro/linux-gcc-cmake/static/continuous.cfg b/third_party/glslang/kokoro/linux-gcc-cmake/static/continuous.cfg new file mode 100644 index 0000000..07a9e7e --- /dev/null +++ b/third_party/glslang/kokoro/linux-gcc-cmake/static/continuous.cfg @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/linux-gcc-cmake/build.sh" + +env_vars { + key: "BUILD_SHARED_LIBS" + value: "0" +} diff --git a/third_party/glslang/kokoro/linux-gcc-cmake/static/presubmit.cfg b/third_party/glslang/kokoro/linux-gcc-cmake/static/presubmit.cfg new file mode 100644 index 0000000..cbdf061 --- /dev/null +++ b/third_party/glslang/kokoro/linux-gcc-cmake/static/presubmit.cfg @@ -0,0 +1,40 @@ +# Copyright (C) 2020 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/linux-gcc-cmake/build.sh" + +env_vars { + key: "BUILD_SHARED_LIBS" + value: "0" +} diff --git a/third_party/glslang/kokoro/macos-clang-release-bazel/build.sh b/third_party/glslang/kokoro/macos-clang-release-bazel/build.sh new file mode 100644 index 0000000..8f1b251 --- /dev/null +++ b/third_party/glslang/kokoro/macos-clang-release-bazel/build.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Copyright (C) 2019 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# macOS Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +CC=clang +CXX=clang++ +SRC=$PWD/github/glslang +cd $SRC + +mv External third_party + +# Get bazel 0.29.1. +gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-darwin-x86_64 . +chmod +x bazel-0.29.1-darwin-x86_64 + +echo $(date): Build everything... +./bazel-0.29.1-darwin-x86_64 build :all +echo $(date): Build completed. + +echo $(date): Starting bazel test... +./bazel-0.29.1-darwin-x86_64 test :all --test_output=all +echo $(date): Bazel test completed. diff --git a/third_party/glslang/kokoro/macos-clang-release-bazel/continuous.cfg b/third_party/glslang/kokoro/macos-clang-release-bazel/continuous.cfg new file mode 100644 index 0000000..f198079 --- /dev/null +++ b/third_party/glslang/kokoro/macos-clang-release-bazel/continuous.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/macos-clang-release-bazel/build.sh" diff --git a/third_party/glslang/kokoro/macos-clang-release-bazel/presubmit.cfg b/third_party/glslang/kokoro/macos-clang-release-bazel/presubmit.cfg new file mode 100644 index 0000000..daa30be --- /dev/null +++ b/third_party/glslang/kokoro/macos-clang-release-bazel/presubmit.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/macos-clang-release-bazel/build.sh" diff --git a/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/build.bat b/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/build.bat new file mode 100644 index 0000000..fb2009b --- /dev/null +++ b/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/build.bat @@ -0,0 +1,75 @@ +:: Copyright (C) 2019 Google, Inc. +:: +:: All rights reserved. +:: +:: Redistribution and use in source and binary forms, with or without +:: modification, are permitted provided that the following conditions +:: are met: +:: +:: Redistributions of source code must retain the above copyright +:: notice, this list of conditions and the following disclaimer. +:: +:: Redistributions in binary form must reproduce the above +:: copyright notice, this list of conditions and the following +:: disclaimer in the documentation and/or other materials provided +:: with the distribution. +:: +:: Neither the name of Google Inc. nor the names of its +:: contributors may be used to endorse or promote products derived +:: from this software without specific prior written permission. +:: +:: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +:: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +:: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +:: FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +:: COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +:: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +:: BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +:: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +:: CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +:: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +:: ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +:: POSSIBILITY OF SUCH DAMAGE. +:: Copyright (c) 2019 Google LLC. +:: +:: Windows Build Script. + +@echo on + +set SRC=%cd%\github\glslang + +:: Force usage of python 3.6 +set PATH=C:\python36;%PATH% +cd %SRC% + +mv External third_party + +:: REM Install Bazel. +wget -q https://github.com/bazelbuild/bazel/releases/download/0.29.1/bazel-0.29.1-windows-x86_64.zip +unzip -q bazel-0.29.1-windows-x86_64.zip + +:: Set up MSVC +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 +set BAZEL_VS=C:\Program Files (x86)\Microsoft Visual Studio 14.0 +set BAZEL_VC=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC +set BAZEL_SH=c:\tools\msys64\usr\bin\bash.exe +set BAZEL_PYTHON=c:\tools\python2\python.exe + +:: ######################################### +:: Start building. +:: ######################################### +echo "Build everything... %DATE% %TIME%" +bazel.exe build :all +if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% +echo "Build Completed %DATE% %TIME%" + +:: ############## +:: Run the tests +:: ############## +echo "Running Tests... %DATE% %TIME%" +bazel.exe test :all --test_output=all +if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% +echo "Tests Completed %DATE% %TIME%" + +exit /b 0 + diff --git a/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/continuous.cfg b/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/continuous.cfg new file mode 100644 index 0000000..554d29d --- /dev/null +++ b/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/continuous.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Continuous build configuration. +build_file: "glslang/kokoro/windows-msvc-2015-release-bazel/build.bat" diff --git a/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg b/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg new file mode 100644 index 0000000..4980bb6 --- /dev/null +++ b/third_party/glslang/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg @@ -0,0 +1,35 @@ +# Copyright (C) 2019 Google, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Presubmit build configuration. +build_file: "glslang/kokoro/windows-msvc-2015-release-bazel/build.bat" diff --git a/third_party/glslang/license-checker.cfg b/third_party/glslang/license-checker.cfg new file mode 100644 index 0000000..886b335 --- /dev/null +++ b/third_party/glslang/license-checker.cfg @@ -0,0 +1,51 @@ +[ + { + "licenses": [ + "Apache-2.0-Header", + "BSD-2-Clause", + "BSD-3-Clause", + "MIT-0", + "MIT" + ], + "paths": [ + { + "exclude": [ + "**.md", + + "_config.yml", + ".*", + "CMakeSettings.json", + "known_good_khr.json", + "known_good.json", + "LICENSE.txt", + "make-revision", + "README-spirv-remap.txt", + "WORKSPACE", + + "glslang/OSDependent/Web/glslang.*.js", + "glslang/MachineIndependent/glslang_tab.cpp", + "glslang/MachineIndependent/glslang_tab.cpp.h", + + "build/**", + "out/**", + "Test/**", + "External/spirv-tools/**" + ] + } + ] + }, + { + "licenses": [ + "GPL-Header" + ], + "paths": [ + { "exclude": [ "**" ] }, + { + "include": [ + "glslang/MachineIndependent/glslang_tab.cpp", + "glslang/MachineIndependent/glslang_tab.cpp.h" + ] + } + ] + } +] diff --git a/third_party/glslang/ndk_test/Android.mk b/third_party/glslang/ndk_test/Android.mk new file mode 100644 index 0000000..b1b2207 --- /dev/null +++ b/third_party/glslang/ndk_test/Android.mk @@ -0,0 +1,45 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CPP_EXTENSION := .cc .cpp .cxx +LOCAL_SRC_FILES:=test.cpp +LOCAL_MODULE:=glslang_ndk_test +LOCAL_LDLIBS:=-landroid +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror +LOCAL_STATIC_LIBRARIES:=glslang SPIRV HLSL +include $(BUILD_SHARED_LIBRARY) + +include $(LOCAL_PATH)/../Android.mk diff --git a/third_party/glslang/ndk_test/jni/Application.mk b/third_party/glslang/ndk_test/jni/Application.mk new file mode 100644 index 0000000..07b7615 --- /dev/null +++ b/third_party/glslang/ndk_test/jni/Application.mk @@ -0,0 +1,38 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +APP_ABI := all +APP_BUILD_SCRIPT := Android.mk +APP_STL := c++_static +APP_PLATFORM := android-9 +NDK_TOOLCHAIN_VERSION := 4.9 diff --git a/third_party/glslang/ndk_test/test.cpp b/third_party/glslang/ndk_test/test.cpp new file mode 100644 index 0000000..dec53d0 --- /dev/null +++ b/third_party/glslang/ndk_test/test.cpp @@ -0,0 +1,19 @@ +// Copyright 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "SPIRV/GlslangToSpv.h" + +void android_main(struct android_app* state) { + int version = glslang::GetSpirvGeneratorVersion(); +} diff --git a/third_party/glslang/standalone.gclient b/third_party/glslang/standalone.gclient new file mode 100644 index 0000000..de067db --- /dev/null +++ b/third_party/glslang/standalone.gclient @@ -0,0 +1,42 @@ +# Copyright (C) 2020 The Khronos Group Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# Neither the name of The Khronos Group Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +solutions = [ + { "name" : ".", + "url" : "https://github.com/KhronosGroup/glslang", + "deps_file" : "DEPS", + "managed" : False, + "custom_deps" : { + }, + }, +] diff --git a/third_party/glslang/tnt/CMakeLists.txt b/third_party/glslang/tnt/CMakeLists.txt new file mode 100644 index 0000000..dc709d5 --- /dev/null +++ b/third_party/glslang/tnt/CMakeLists.txt @@ -0,0 +1,113 @@ +cmake_minimum_required(VERSION 3.19) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# Adhere to GNU filesystem layout conventions +include(GNUInstallDirs) + +option(ENABLE_AMD_EXTENSIONS "Enables support of AMD-specific extensions" ON) +option(ENABLE_NV_EXTENSIONS "Enables support of Nvidia-specific extensions" ON) +option(ENABLE_OPT "Enables spirv-opt capability if present" OFF) + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND WIN32) + set(CMAKE_INSTALL_PREFIX "install" CACHE STRING "..." FORCE) +endif() + +project(../glslang) +# make testing optional +include(CTest) + +#if (LINUX) +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") +#endif() + +if(ENABLE_AMD_EXTENSIONS) + add_definitions(-DAMD_EXTENSIONS) +endif(ENABLE_AMD_EXTENSIONS) + +if(ENABLE_NV_EXTENSIONS) + add_definitions(-DNV_EXTENSIONS) +endif(ENABLE_NV_EXTENSIONS) + +#if(ENABLE_HLSL) +# add_definitions(-DENABLE_HLSL) +#endif(ENABLE_HLSL) + +if(WIN32) + set(CMAKE_DEBUG_POSTFIX "d") + add_definitions(-DGLSLANG_OSINCLUDE_WIN32) +elseif(UNIX) + add_definitions(-DGLSLANG_OSINCLUDE_UNIX) +else(WIN32) + message("unknown platform") +endif(WIN32) + +function(glslang_set_link_args TARGET) + # For MinGW compiles, statically link against the GCC and C++ runtimes. + # This avoids the need to ship those runtimes as DLLs. + if (WIN32 AND ${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + set_target_properties(${TARGET} PROPERTIES + LINK_FLAGS "-static -static-libgcc -static-libstdc++") + endif() +endfunction(glslang_set_link_args) + +# We keep the optimizer disabled, we call it ourselves +# if(ENABLE_OPT) +# message(STATUS "SPIR-V optimizer enabled") +# add_definitions(-DENABLE_OPT) +# elseif(ENABLE_HLSL) +# message(STATUS "spirv-tools not linked - illegal SPIRV may be generated for HLSL") +# endif() + +find_package(PythonInterp 3 REQUIRED) + +# Root directory for build-time generated include files +set(GLSLANG_GENERATED_INCLUDEDIR "${CMAKE_BINARY_DIR}/include") + +################################################################################ +# Build version information generation +################################################################################ +set(GLSLANG_CHANGES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../CHANGES.md") +set(GLSLANG_BUILD_INFO_PY "${CMAKE_CURRENT_SOURCE_DIR}/../build_info.py") +set(GLSLANG_BUILD_INFO_H_TMPL "${CMAKE_CURRENT_SOURCE_DIR}/../build_info.h.tmpl") +set(GLSLANG_BUILD_INFO_H "${GLSLANG_GENERATED_INCLUDEDIR}/glslang/build_info.h") + +# Command to build the build_info.h file +add_custom_command( + OUTPUT ${GLSLANG_BUILD_INFO_H} + COMMAND ${PYTHON_EXECUTABLE} "${GLSLANG_BUILD_INFO_PY}" + ${CMAKE_CURRENT_SOURCE_DIR}/.. + "-i" ${GLSLANG_BUILD_INFO_H_TMPL} + "-o" ${GLSLANG_BUILD_INFO_H} + DEPENDS ${GLSLANG_BUILD_INFO_PY} + ${GLSLANG_CHANGES_FILE} + ${GLSLANG_BUILD_INFO_H_TMPL} + COMMENT "Generating ${GLSLANG_BUILD_INFO_H}") + +# Target to build the build_info.h file +add_custom_target(glslang-build-info DEPENDS ${GLSLANG_BUILD_INFO_H}) + +# Populate the CMake GLSLANG_VERSION* variables with the build version +# information. +execute_process( + COMMAND ${PYTHON_EXECUTABLE} "${GLSLANG_BUILD_INFO_PY}" + ${CMAKE_CURRENT_SOURCE_DIR}/.. "..<-flavor>;;;;" + OUTPUT_VARIABLE "GLSLANG_VERSIONS" + OUTPUT_STRIP_TRAILING_WHITESPACE) +list(GET "GLSLANG_VERSIONS" 0 "GLSLANG_VERSION") +list(GET "GLSLANG_VERSIONS" 1 "GLSLANG_VERSION_MAJOR") +list(GET "GLSLANG_VERSIONS" 2 "GLSLANG_VERSION_MINOR") +list(GET "GLSLANG_VERSIONS" 3 "GLSLANG_VERSION_PATCH") +list(GET "GLSLANG_VERSIONS" 4 "GLSLANG_VERSION_FLAVOR") +configure_file(${GLSLANG_CHANGES_FILE} "${CMAKE_CURRENT_BINARY_DIR}/CHANGES.md") # Required to re-run cmake on version change + +# glslang_add_build_info_dependency() adds the glslang-build-info dependency and +# generated include directories to target. +function(glslang_add_build_info_dependency target) + target_include_directories(${target} PUBLIC $) + add_dependencies(${target} glslang-build-info) +endfunction() + + +add_subdirectory(../glslang/tnt "${CMAKE_CURRENT_BINARY_DIR}/glslang") +add_subdirectory(../OGLCompilersDLL/tnt "${CMAKE_CURRENT_BINARY_DIR}/OGLCompilersDLL") +add_subdirectory(../SPIRV/tnt "${CMAKE_CURRENT_BINARY_DIR}/SPIRV") diff --git a/third_party/glslang/tnt/README.md b/third_party/glslang/tnt/README.md new file mode 100644 index 0000000..7514cab --- /dev/null +++ b/third_party/glslang/tnt/README.md @@ -0,0 +1,20 @@ +To update glslang use the following steps: + +- Download a ZIP of the [glslang repository](https://github.com/KhronosGroup/glslang) into `third_party/glslang`. +- Run the following commands (on Mac) to unzip and copy the new source files: + +``` +cd third_party +curl -L https://github.com/KhronosGroup/glslang/archive/master.zip > main.zip +unzip main.zip +rsync -r glslang-master/ glslang/ --delete +rm -r main.zip glslang-master +rm -rf glslang/Test/ +git checkout glslang/*/tnt/* +git restore glslang/LICENSE +git add glslang +``` + +- If necessary, update the `DefaultTBuiltInResource` definition inside `libs/filamat/src/sca/builtinResource.h` to glslang's located at +`third_party/glslang/StandAlone/ResourceLimits.cpp` +- Compile and test `matc` diff --git a/third_party/glslang/update_glslang_sources.py b/third_party/glslang/update_glslang_sources.py new file mode 100755 index 0000000..65be2f6 --- /dev/null +++ b/third_party/glslang/update_glslang_sources.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python + +# Copyright 2017 The Glslang Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Get source files for Glslang and its dependencies from public repositories. +""" + +from __future__ import print_function + +import argparse +import json +import distutils.dir_util +import os.path +import subprocess +import sys + +KNOWN_GOOD_FILE = 'known_good.json' + +SITE_TO_KNOWN_GOOD_FILE = { 'github' : 'known_good.json', + 'gitlab' : 'known_good_khr.json' } + +# Maps a site name to its hostname. +SITE_TO_HOST = { 'github' : 'https://github.com/', + 'gitlab' : 'git@gitlab.khronos.org:' } + +VERBOSE = True + + +def command_output(cmd, directory, fail_ok=False): + """Runs a command in a directory and returns its standard output stream. + + Captures the standard error stream. + + Raises a RuntimeError if the command fails to launch or otherwise fails. + """ + if VERBOSE: + print('In {d}: {cmd}'.format(d=directory, cmd=cmd)) + p = subprocess.Popen(cmd, + cwd=directory, + stdout=subprocess.PIPE) + (stdout, _) = p.communicate() + if p.returncode != 0 and not fail_ok: + raise RuntimeError('Failed to run {} in {}'.format(cmd, directory)) + if VERBOSE: + print(stdout) + return stdout + + +def command_retval(cmd, directory): + """Runs a command in a directory and returns its return value. + + Captures the standard error stream. + """ + p = subprocess.Popen(cmd, + cwd=directory, + stdout=subprocess.PIPE) + p.communicate() + return p.returncode + + +class GoodCommit(object): + """Represents a good commit for a repository.""" + + def __init__(self, json): + """Initializes this good commit object. + + Args: + 'json': A fully populated JSON object describing the commit. + """ + self._json = json + self.name = json['name'] + self.site = json['site'] + self.subrepo = json['subrepo'] + self.subdir = json['subdir'] if ('subdir' in json) else '.' + self.commit = json['commit'] + + def GetUrl(self): + """Returns the URL for the repository.""" + host = SITE_TO_HOST[self.site] + return '{host}{subrepo}'.format( + host=host, + subrepo=self.subrepo) + + def AddRemote(self): + """Add the remote 'known-good' if it does not exist.""" + remotes = command_output(['git', 'remote'], self.subdir).splitlines() + if b'known-good' not in remotes: + command_output(['git', 'remote', 'add', 'known-good', self.GetUrl()], self.subdir) + + def HasCommit(self): + """Check if the repository contains the known-good commit.""" + return 0 == subprocess.call(['git', 'rev-parse', '--verify', '--quiet', + self.commit + "^{commit}"], + cwd=self.subdir) + + def Clone(self): + distutils.dir_util.mkpath(self.subdir) + command_output(['git', 'clone', self.GetUrl(), '.'], self.subdir) + + def Fetch(self): + command_output(['git', 'fetch', 'known-good'], self.subdir) + + def Checkout(self): + if not os.path.exists(os.path.join(self.subdir,'.git')): + self.Clone() + self.AddRemote() + if not self.HasCommit(): + self.Fetch() + command_output(['git', 'checkout', self.commit], self.subdir) + + +def GetGoodCommits(site): + """Returns the latest list of GoodCommit objects.""" + known_good_file = SITE_TO_KNOWN_GOOD_FILE[site] + with open(known_good_file) as known_good: + return [GoodCommit(c) for c in json.loads(known_good.read())['commits']] + + +def main(): + parser = argparse.ArgumentParser(description='Get Glslang source dependencies at a known-good commit') + parser.add_argument('--dir', dest='dir', default='.', + help="Set target directory for Glslang source root. Default is \'.\'.") + parser.add_argument('--site', dest='site', default='github', + help="Set git server site. Default is github.") + + args = parser.parse_args() + + commits = GetGoodCommits(args.site) + + distutils.dir_util.mkpath(args.dir) + print('Change directory to {d}'.format(d=args.dir)) + os.chdir(args.dir) + + # Create the subdirectories in sorted order so that parent git repositories + # are created first. + for c in sorted(commits, key=lambda x: x.subdir): + print('Get {n}\n'.format(n=c.name)) + c.Checkout() + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-cross/.clang-format b/third_party/spirv-cross/.clang-format new file mode 100755 index 0000000..443f90b --- /dev/null +++ b/third_party/spirv-cross/.clang-format @@ -0,0 +1,167 @@ +# The style used for all options not specifically set in the configuration. +BasedOnStyle: LLVM + +# The extra indent or outdent of access modifiers, e.g. public:. +AccessModifierOffset: -4 + +# If true, aligns escaped newlines as far left as possible. Otherwise puts them into the right-most column. +AlignEscapedNewlinesLeft: true + +# If true, aligns trailing comments. +AlignTrailingComments: false + +# Allow putting all parameters of a function declaration onto the next line even if BinPackParameters is false. +AllowAllParametersOfDeclarationOnNextLine: false + +# Allows contracting simple braced statements to a single line. +AllowShortBlocksOnASingleLine: false + +# If true, short case labels will be contracted to a single line. +AllowShortCaseLabelsOnASingleLine: false + +# Dependent on the value, int f() { return 0; } can be put on a single line. Possible values: None, Inline, All. +AllowShortFunctionsOnASingleLine: None + +# If true, if (a) return; can be put on a single line. +AllowShortIfStatementsOnASingleLine: false + +# If true, while (true) continue; can be put on a single line. +AllowShortLoopsOnASingleLine: false + +# If true, always break after function definition return types. +AlwaysBreakAfterDefinitionReturnType: false + +# If true, always break before multiline string literals. +AlwaysBreakBeforeMultilineStrings: false + +# If true, always break after the template<...> of a template declaration. +AlwaysBreakTemplateDeclarations: true + +# If false, a function call's arguments will either be all on the same line or will have one line each. +BinPackArguments: true + +# If false, a function declaration's or function definition's parameters will either all be on the same line +# or will have one line each. +BinPackParameters: true + +# The way to wrap binary operators. Possible values: None, NonAssignment, All. +BreakBeforeBinaryOperators: None + +# The brace breaking style to use. Possible values: Attach, Linux, Stroustrup, Allman, GNU. +BreakBeforeBraces: Allman + +# If true, ternary operators will be placed after line breaks. +BreakBeforeTernaryOperators: false + +# Always break constructor initializers before commas and align the commas with the colon. +BreakConstructorInitializersBeforeComma: true + +# The column limit. A column limit of 0 means that there is no column limit. +ColumnLimit: 120 + +# A regular expression that describes comments with special meaning, which should not be split into lines or otherwise changed. +CommentPragmas: '^ *' + +# If the constructor initializers don't fit on a line, put each initializer on its own line. +ConstructorInitializerAllOnOneLineOrOnePerLine: false + +# The number of characters to use for indentation of constructor initializer lists. +ConstructorInitializerIndentWidth: 4 + +# Indent width for line continuations. +ContinuationIndentWidth: 4 + +# If true, format braced lists as best suited for C++11 braced lists. +Cpp11BracedListStyle: false + +# Disables formatting at all. +DisableFormat: false + +# A vector of macros that should be interpreted as foreach loops instead of as function calls. +#ForEachMacros: '' + +# Indent case labels one level from the switch statement. +# When false, use the same indentation level as for the switch statement. +# Switch statement body is always indented one level more than case labels. +IndentCaseLabels: false + +# The number of columns to use for indentation. +IndentWidth: 4 + +# Indent if a function definition or declaration is wrapped after the type. +IndentWrappedFunctionNames: false + +# If true, empty lines at the start of blocks are kept. +KeepEmptyLinesAtTheStartOfBlocks: true + +# Language, this format style is targeted at. Possible values: None, Cpp, Java, JavaScript, Proto. +Language: Cpp + +# The maximum number of consecutive empty lines to keep. +MaxEmptyLinesToKeep: 1 + +# The indentation used for namespaces. Possible values: None, Inner, All. +NamespaceIndentation: None + +# The penalty for breaking a function call after "call(". +PenaltyBreakBeforeFirstCallParameter: 19 + +# The penalty for each line break introduced inside a comment. +PenaltyBreakComment: 300 + +# The penalty for breaking before the first <<. +PenaltyBreakFirstLessLess: 120 + +# The penalty for each line break introduced inside a string literal. +PenaltyBreakString: 1000 + +# The penalty for each character outside of the column limit. +PenaltyExcessCharacter: 1000000 + +# Penalty for putting the return type of a function onto its own line. +PenaltyReturnTypeOnItsOwnLine: 1000000000 + +# Pointer and reference alignment style. Possible values: Left, Right, Middle. +PointerAlignment: Right + +# If true, a space may be inserted after C style casts. +SpaceAfterCStyleCast: false + +# If false, spaces will be removed before assignment operators. +SpaceBeforeAssignmentOperators: true + +# Defines in which cases to put a space before opening parentheses. Possible values: Never, ControlStatements, Always. +SpaceBeforeParens: ControlStatements + +# If true, spaces may be inserted into '()'. +SpaceInEmptyParentheses: false + +# The number of spaces before trailing line comments (// - comments). +SpacesBeforeTrailingComments: 1 + +# If true, spaces will be inserted after '<' and before '>' in template argument lists. +SpacesInAngles: false + +# If true, spaces may be inserted into C style casts. +SpacesInCStyleCastParentheses: false + +# If true, spaces are inserted inside container literals (e.g. ObjC and Javascript array and dict literals). +SpacesInContainerLiterals: false + +# If true, spaces will be inserted after '(' and before ')'. +SpacesInParentheses: false + +# If true, spaces will be inserted after '[' and befor']'. +SpacesInSquareBrackets: false + +# Format compatible with this standard, e.g. use A > instead of A> for LS_Cpp03. Possible values: Cpp03, Cpp11, Auto. +Standard: Cpp11 + +# The number of columns used for tab stops. +TabWidth: 4 + +# The way to use tab characters in the resulting file. Possible values: Never, ForIndentation, Always. +UseTab: ForIndentation + +# Do not reflow comments +ReflowComments: false diff --git a/third_party/spirv-cross/.gitignore b/third_party/spirv-cross/.gitignore new file mode 100644 index 0000000..abd7189 --- /dev/null +++ b/third_party/spirv-cross/.gitignore @@ -0,0 +1,20 @@ +*.o +*.d +*.txt +/test +/spirv-cross +/obj +/msvc/x64 +/msvc/Debug +/msvc/Release +*.suo +*.sdf +*.opensdf +*.shader +*.a +*.bc +/external +.vs/ +*.vcxproj.user + +!CMakeLists.txt diff --git a/third_party/spirv-cross/.travis.yml b/third_party/spirv-cross/.travis.yml new file mode 100644 index 0000000..7791336 --- /dev/null +++ b/third_party/spirv-cross/.travis.yml @@ -0,0 +1,72 @@ +language: + - cpp + - python + +python: 3.9 + +matrix: + include: + - os: linux + dist: trusty + compiler: gcc + env: + - GENERATOR="Unix Makefiles" + - ARTIFACT=gcc-trusty-64bit + - os: linux + dist: trusty + compiler: clang + env: + - GENERATOR="Unix Makefiles" + - ARTIFACT=clang-trusty-64bit + - os: osx + compiler: clang + osx_image: xcode10 + env: + - GENERATOR="Unix Makefiles" + - ARTIFACT=clang-macos-64bit + - os: windows + before_install: + - choco install python3 + - export PATH="/c/Python39:/c/Python39/Scripts:$PATH" + env: + - GENERATOR="Visual Studio 15 2017" + - ARTIFACT=vs2017-32bit + - os: windows + before_install: + - choco install python3 + - export PATH="/c/Python39:/c/Python39/Scripts:$PATH" + env: + - GENERATOR="Visual Studio 15 2017 Win64" + - ARTIFACT=vs2017-64bit + +before_script: + - "./checkout_glslang_spirv_tools.sh" + +script: + - if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then PYTHON3=$(which python); fi + - if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then PYTHON3=$(which python3); fi + - "./build_glslang_spirv_tools.sh Release" + - mkdir build + - cd build + - cmake .. -DSPIRV_CROSS_WERROR=ON -DSPIRV_CROSS_MISC_WARNINGS=ON -DSPIRV_CROSS_SHARED=ON -DCMAKE_INSTALL_PREFIX=output -DCMAKE_BUILD_TYPE=Release -G "${GENERATOR}" -DPYTHON_EXECUTABLE:FILEPATH="${PYTHON3}" -DSPIRV_CROSS_ENABLE_TESTS=ON + - cmake --build . --config Release + - cmake --build . --config Release --target install + - ctest --verbose -C Release + - cd .. + +before_deploy: + - REV=${ARTIFACT}-$(git rev-parse --short=10 HEAD) + - cd build/output + - tar cf spirv-cross-${REV}.tar * + - gzip spirv-cross-${REV}.tar + - cd ../.. + - export FILE_TO_UPLOAD=build/output/spirv-cross-${REV}.tar.gz + +deploy: + provider: releases + api_key: + secure: c7YEOyzhE19TFo76UnbLWk/kikRQxsHsOxzkOqN6Q2aL8joNRw5kmcG84rGd+Rf6isX62cykCzA6qHkyJCv9QTIzcyXnLju17rLvgib7cXDcseaq8x4mFvet2yUxCglthDpFY2M2LB0Aqws71lPeYIrKXa6hCFEh8jO3AWxnaor7O3RYfNZylM9d33HgH6KLT3sDx/cukwBstmKeg7EG9OUnrSvairkPW0W2+jlq3SXPlq/WeVhf8hQs3Yg0BluExGbmLOwe9EaeUpeGuJMyHRxXypnToQv1/KwoScKpap5tYxdNWiwRGZ4lYcmKrjAYVvilTioh654oX5LQpn34mE/oe8Ko9AaATkSaoiisRFp6meWtnB39oFBoL5Yn15DqLQpRXPr1AJsnBXSGAac3aDBO1j4MIqTHmYlYlfRw3n2ZsBaFaTZnv++438SNQ54nkivyoDTIWjoOmYa9+K4mQc3415RDdQmjZTJM+lu+GAlMmNBTVbfNvrbU55Usu9Lo6BZJKKdUMvdBB78kJ5FHvcBlL+eMgmk1pABQY0IZROCt7NztHcv1UmAxoWNxveSFs5glydPNNjNS8bogc4dzBGYG0KMmILbBHihVbY2toA1M9CMdDHdp+LucfDMmzECmYSEmlx0h8win+Jjb74/qpOhaXuUZ0NnzVgCOyeUYuMQ= + file: "${FILE_TO_UPLOAD}" + skip_cleanup: true + on: + tags: true diff --git a/third_party/spirv-cross/CMakeLists.txt b/third_party/spirv-cross/CMakeLists.txt new file mode 100644 index 0000000..66f19a6 --- /dev/null +++ b/third_party/spirv-cross/CMakeLists.txt @@ -0,0 +1,608 @@ +# Copyright 2016-2020 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 2.8) +set(CMAKE_CXX_STANDARD 11) + +# Avoid a warning if parent project sets VERSION in project(). +if (${CMAKE_VERSION} VERSION_GREATER "3.0.1") + cmake_policy(SET CMP0048 NEW) +endif() + +project(SPIRV-Cross LANGUAGES CXX C) +enable_testing() + +include(GNUInstallDirs) + +option(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS "Instead of throwing exceptions assert" OFF) +option(SPIRV_CROSS_SHARED "Build the C API as a single shared library." OFF) +option(SPIRV_CROSS_STATIC "Build the C and C++ API as static libraries." ON) +option(SPIRV_CROSS_CLI "Build the CLI binary. Requires SPIRV_CROSS_STATIC." ON) +option(SPIRV_CROSS_ENABLE_TESTS "Enable SPIRV-Cross tests." ON) + +option(SPIRV_CROSS_ENABLE_GLSL "Enable GLSL support." ON) +option(SPIRV_CROSS_ENABLE_HLSL "Enable HLSL target support." ON) +option(SPIRV_CROSS_ENABLE_MSL "Enable MSL target support." ON) +option(SPIRV_CROSS_ENABLE_CPP "Enable C++ target support." ON) +option(SPIRV_CROSS_ENABLE_REFLECT "Enable JSON reflection target support." ON) +option(SPIRV_CROSS_ENABLE_C_API "Enable C API wrapper support in static library." ON) +option(SPIRV_CROSS_ENABLE_UTIL "Enable util module support." ON) + +option(SPIRV_CROSS_SANITIZE_ADDRESS "Sanitize address" OFF) +option(SPIRV_CROSS_SANITIZE_MEMORY "Sanitize memory" OFF) +option(SPIRV_CROSS_SANITIZE_THREADS "Sanitize threads" OFF) +option(SPIRV_CROSS_SANITIZE_UNDEFINED "Sanitize undefined" OFF) + +option(SPIRV_CROSS_NAMESPACE_OVERRIDE "" "Override the namespace used in the C++ API.") +option(SPIRV_CROSS_FORCE_STL_TYPES "Force use of STL types instead of STL replacements in certain places. Might reduce performance." OFF) + +option(SPIRV_CROSS_SKIP_INSTALL "Skips installation targets." OFF) + +option(SPIRV_CROSS_WERROR "Fail build on warnings." OFF) +option(SPIRV_CROSS_MISC_WARNINGS "Misc warnings useful for Travis runs." OFF) + +option(SPIRV_CROSS_FORCE_PIC "Force position-independent code for all targets." OFF) + +if(${CMAKE_GENERATOR} MATCHES "Makefile") + if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) + message(FATAL_ERROR "Build out of tree to avoid overwriting Makefile") + endif() +endif() + +set(spirv-compiler-options "") +set(spirv-compiler-defines "") +set(spirv-cross-link-flags "") + +message(STATUS "SPIRV-Cross: Finding Git version for SPIRV-Cross.") +set(spirv-cross-build-version "unknown") +find_package(Git) +if (GIT_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE spirv-cross-build-version + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + message(STATUS "SPIRV-Cross: Git hash: ${spirv-cross-build-version}") +else() + message(STATUS "SPIRV-Cross: Git not found, using unknown build version.") +endif() + +string(TIMESTAMP spirv-cross-timestamp) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/gitversion.in.h ${CMAKE_CURRENT_BINARY_DIR}/gitversion.h @ONLY) + +if (SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) + set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) + if (NOT MSVC) + set(spirv-compiler-options ${spirv-compiler-options} -fno-exceptions) + endif() +endif() + +if (SPIRV_CROSS_FORCE_STL_TYPES) + set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_FORCE_STL_TYPES) +endif() + +if (WIN32) + set(CMAKE_DEBUG_POSTFIX "d") +endif() + +if (CMAKE_COMPILER_IS_GNUCXX OR ((${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") AND NOT MSVC)) + set(spirv-compiler-options ${spirv-compiler-options} -Wall -Wextra -Wshadow -Wno-deprecated-declarations) + if (SPIRV_CROSS_MISC_WARNINGS) + if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") + set(spirv-compiler-options ${spirv-compiler-options} -Wshorten-64-to-32) + endif() + endif() + if (SPIRV_CROSS_WERROR) + set(spirv-compiler-options ${spirv-compiler-options} -Werror) + endif() + + if (SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) + set(spirv-compiler-options ${spirv-compiler-options} -fno-exceptions) + endif() + + if (SPIRV_CROSS_SANITIZE_ADDRESS) + set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=address) + set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=address") + endif() + + if (SPIRV_CROSS_SANITIZE_UNDEFINED) + set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=undefined) + set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=undefined") + endif() + + if (SPIRV_CROSS_SANITIZE_MEMORY) + set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=memory) + set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=memory") + endif() + + if (SPIRV_CROSS_SANITIZE_THREADS) + set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=thread) + set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=thread") + endif() +elseif (MSVC) + set(spirv-compiler-options ${spirv-compiler-options} /wd4267 /wd4996) +endif() + +macro(extract_headers out_abs file_list) + set(${out_abs}) # absolute paths + foreach(_a ${file_list}) + # get_filename_component only returns the longest extension, so use a regex + string(REGEX REPLACE ".*\\.(h|hpp)" "\\1" ext ${_a}) + + # For shared library, we are only interested in the C header. + if (SPIRV_CROSS_STATIC) + if(("${ext}" STREQUAL "h") OR ("${ext}" STREQUAL "hpp")) + list(APPEND ${out_abs} "${_a}") + endif() + else() + if("${ext}" STREQUAL "h") + list(APPEND ${out_abs} "${_a}") + endif() + endif() + endforeach() +endmacro() + +macro(spirv_cross_add_library name config_name library_type) + add_library(${name} ${library_type} ${ARGN}) + extract_headers(hdrs "${ARGN}") + target_include_directories(${name} PUBLIC + $ + $) + set_target_properties(${name} PROPERTIES + PUBLIC_HEADERS "${hdrs}") + if (SPIRV_CROSS_FORCE_PIC) + set_target_properties(${name} PROPERTIES POSITION_INDEPENDENT_CODE ON) + endif() + target_compile_options(${name} PRIVATE ${spirv-compiler-options}) + target_compile_definitions(${name} PRIVATE ${spirv-compiler-defines}) + if (SPIRV_CROSS_NAMESPACE_OVERRIDE) + if (${library_type} MATCHES "STATIC") + target_compile_definitions(${name} PUBLIC SPIRV_CROSS_NAMESPACE_OVERRIDE=${SPIRV_CROSS_NAMESPACE_OVERRIDE}) + else() + target_compile_definitions(${name} PRIVATE SPIRV_CROSS_NAMESPACE_OVERRIDE=${SPIRV_CROSS_NAMESPACE_OVERRIDE}) + endif() + endif() + + if (NOT SPIRV_CROSS_SKIP_INSTALL) + install(TARGETS ${name} + EXPORT ${config_name}Config + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/spirv_cross) + install(FILES ${hdrs} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/spirv_cross) + install(EXPORT ${config_name}Config DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${config_name}/cmake) + export(TARGETS ${name} FILE ${config_name}Config.cmake) + endif() +endmacro() + +set(spirv-cross-core-sources + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.std.450.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_common.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_containers.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_error_handling.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_parser.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_parser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_parsed_ir.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_parsed_ir.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cfg.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cfg.cpp) + +set(spirv-cross-c-sources + spirv.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_c.h) + +set(spirv-cross-glsl-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_glsl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_glsl.hpp) + +set(spirv-cross-cpp-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.hpp) + +set(spirv-cross-msl-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.hpp) + +set(spirv-cross-hlsl-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.hpp) + +set(spirv-cross-reflect-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.hpp) + +set(spirv-cross-util-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_util.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_util.hpp) + +if (SPIRV_CROSS_STATIC) + spirv_cross_add_library(spirv-cross-core spirv_cross_core STATIC + ${spirv-cross-core-sources}) + + if (SPIRV_CROSS_ENABLE_GLSL) + spirv_cross_add_library(spirv-cross-glsl spirv_cross_glsl STATIC + ${spirv-cross-glsl-sources}) + target_link_libraries(spirv-cross-glsl PRIVATE spirv-cross-core) + endif() + + if (SPIRV_CROSS_ENABLE_CPP) + spirv_cross_add_library(spirv-cross-cpp spirv_cross_cpp STATIC + ${spirv-cross-cpp-sources}) + + if (SPIRV_CROSS_ENABLE_GLSL) + target_link_libraries(spirv-cross-cpp PRIVATE spirv-cross-glsl) + else() + message(FATAL_ERROR "Must enable GLSL support to enable C++ support.") + endif() + endif() + + if (SPIRV_CROSS_ENABLE_REFLECT) + if (SPIRV_CROSS_ENABLE_GLSL) + spirv_cross_add_library(spirv-cross-reflect spirv_cross_reflect STATIC + ${spirv-cross-reflect-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable JSON reflection support.") + endif() + endif() + + if (SPIRV_CROSS_ENABLE_MSL) + spirv_cross_add_library(spirv-cross-msl spirv_cross_msl STATIC + ${spirv-cross-msl-sources}) + if (SPIRV_CROSS_ENABLE_GLSL) + target_link_libraries(spirv-cross-msl PRIVATE spirv-cross-glsl) + else() + message(FATAL_ERROR "Must enable GLSL support to enable MSL support.") + endif() + endif() + + if (SPIRV_CROSS_ENABLE_HLSL) + spirv_cross_add_library(spirv-cross-hlsl spirv_cross_hlsl STATIC + ${spirv-cross-hlsl-sources}) + if (SPIRV_CROSS_ENABLE_GLSL) + target_link_libraries(spirv-cross-hlsl PRIVATE spirv-cross-glsl) + else() + message(FATAL_ERROR "Must enable GLSL support to enable HLSL support.") + endif() + endif() + + if (SPIRV_CROSS_ENABLE_UTIL) + spirv_cross_add_library(spirv-cross-util spirv_cross_util STATIC + ${spirv-cross-util-sources}) + target_link_libraries(spirv-cross-util PRIVATE spirv-cross-core) + endif() + + if (SPIRV_CROSS_ENABLE_C_API) + spirv_cross_add_library(spirv-cross-c spirv_cross_c STATIC + ${spirv-cross-c-sources}) + target_include_directories(spirv-cross-c PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_compile_definitions(spirv-cross-c PRIVATE HAVE_SPIRV_CROSS_GIT_VERSION) + + if (SPIRV_CROSS_ENABLE_GLSL) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-glsl) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_GLSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_HLSL) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-hlsl) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_HLSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_MSL) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-msl) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_MSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_CPP) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-cpp) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_CPP=1) + endif() + + if (SPIRV_CROSS_ENABLE_REFLECT) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-reflect) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_REFLECT=1) + endif() + endif() +endif() + +set(spirv-cross-abi-major 0) +set(spirv-cross-abi-minor 42) +set(spirv-cross-abi-patch 0) + +if (SPIRV_CROSS_SHARED) + set(SPIRV_CROSS_VERSION ${spirv-cross-abi-major}.${spirv-cross-abi-minor}.${spirv-cross-abi-patch}) + + if (NOT SPIRV_CROSS_SKIP_INSTALL) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/pkg-config/spirv-cross-c-shared.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/spirv-cross-c-shared.pc @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/spirv-cross-c-shared.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + endif() + + spirv_cross_add_library(spirv-cross-c-shared spirv_cross_c_shared SHARED + ${spirv-cross-core-sources} + ${spirv-cross-c-sources}) + + target_include_directories(spirv-cross-c-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_compile_definitions(spirv-cross-c-shared PRIVATE HAVE_SPIRV_CROSS_GIT_VERSION) + + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-glsl-sources}) + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_GLSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_HLSL) + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-hlsl-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable HLSL support.") + endif() + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_HLSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_MSL) + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-msl-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable MSL support.") + endif() + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_MSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_CPP) + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-cpp-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable C++ support.") + endif() + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_CPP=1) + endif() + + if (SPIRV_CROSS_ENABLE_REFLECT) + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-reflect-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable JSON reflection support.") + endif() + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_REFLECT=1) + endif() + + if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")) + # Only export the C API. + target_compile_options(spirv-cross-c-shared PRIVATE -fvisibility=hidden) + if (NOT APPLE) + set_target_properties(spirv-cross-c-shared PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + endif() + endif() + + target_compile_definitions(spirv-cross-c-shared PRIVATE SPVC_EXPORT_SYMBOLS) + + set_target_properties(spirv-cross-c-shared PROPERTIES + VERSION ${SPIRV_CROSS_VERSION} + SOVERSION ${spirv-cross-abi-major}) +endif() + +if (SPIRV_CROSS_CLI) + if (NOT SPIRV_CROSS_ENABLE_GLSL) + message(FATAL_ERROR "Must enable GLSL if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_HLSL) + message(FATAL_ERROR "Must enable HLSL if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_MSL) + message(FATAL_ERROR "Must enable MSL if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_CPP) + message(FATAL_ERROR "Must enable C++ if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_REFLECT) + message(FATAL_ERROR "Must enable reflection if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_UTIL) + message(FATAL_ERROR "Must enable utils if building CLI.") + endif() + + if (NOT SPIRV_CROSS_STATIC) + message(FATAL_ERROR "Must build static libraries if building CLI.") + endif() + add_executable(spirv-cross main.cpp) + target_compile_options(spirv-cross PRIVATE ${spirv-compiler-options}) + target_include_directories(spirv-cross PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_compile_definitions(spirv-cross PRIVATE ${spirv-compiler-defines} HAVE_SPIRV_CROSS_GIT_VERSION) + set_target_properties(spirv-cross PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + if (NOT SPIRV_CROSS_SKIP_INSTALL) + install(TARGETS spirv-cross RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + target_link_libraries(spirv-cross PRIVATE + spirv-cross-glsl + spirv-cross-hlsl + spirv-cross-cpp + spirv-cross-reflect + spirv-cross-msl + spirv-cross-util + spirv-cross-core) + + if (SPIRV_CROSS_ENABLE_TESTS) + # Set up tests, using only the simplest modes of the test_shaders + # script. You have to invoke the script manually to: + # - Update the reference files + # - Get cycle counts from malisc + # - Keep failing outputs + find_package(PythonInterp) + find_program(spirv-cross-glslang NAMES glslangValidator + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/glslang-build/output/bin + NO_DEFAULT_PATH) + find_program(spirv-cross-spirv-as NAMES spirv-as + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin + NO_DEFAULT_PATH) + find_program(spirv-cross-spirv-val NAMES spirv-val + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin + NO_DEFAULT_PATH) + find_program(spirv-cross-spirv-opt NAMES spirv-opt + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin + NO_DEFAULT_PATH) + + if ((${spirv-cross-glslang} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-as} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-val} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-opt} MATCHES "NOTFOUND")) + set(SPIRV_CROSS_ENABLE_TESTS OFF) + message("SPIRV-Cross: Testing will be disabled for SPIRV-Cross. Could not find glslang or SPIRV-Tools build under external/. To enable testing, run ./checkout_glslang_spirv_tools.sh and ./build_glslang_spirv_tools.sh first.") + else() + set(SPIRV_CROSS_ENABLE_TESTS ON) + message("SPIRV-Cross: Found glslang and SPIRV-Tools. Enabling test suite.") + message("SPIRV-Cross: Found glslangValidator in: ${spirv-cross-glslang}.") + message("SPIRV-Cross: Found spirv-as in: ${spirv-cross-spirv-as}.") + message("SPIRV-Cross: Found spirv-val in: ${spirv-cross-spirv-val}.") + message("SPIRV-Cross: Found spirv-opt in: ${spirv-cross-spirv-opt}.") + endif() + + set(spirv-cross-externals + --glslang "${spirv-cross-glslang}" + --spirv-as "${spirv-cross-spirv-as}" + --spirv-opt "${spirv-cross-spirv-opt}" + --spirv-val "${spirv-cross-spirv-val}") + + if (${PYTHONINTERP_FOUND} AND SPIRV_CROSS_ENABLE_TESTS) + if (${PYTHON_VERSION_MAJOR} GREATER 2) + add_executable(spirv-cross-c-api-test tests-other/c_api_test.c) + target_link_libraries(spirv-cross-c-api-test spirv-cross-c) + set_target_properties(spirv-cross-c-api-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-small-vector-test tests-other/small_vector.cpp) + target_link_libraries(spirv-cross-small-vector-test spirv-cross-core) + set_target_properties(spirv-cross-small-vector-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-msl-constexpr-test tests-other/msl_constexpr_test.cpp) + target_link_libraries(spirv-cross-msl-constexpr-test spirv-cross-c) + set_target_properties(spirv-cross-msl-constexpr-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-msl-resource-binding-test tests-other/msl_resource_bindings.cpp) + target_link_libraries(spirv-cross-msl-resource-binding-test spirv-cross-c) + set_target_properties(spirv-cross-msl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-hlsl-resource-binding-test tests-other/hlsl_resource_bindings.cpp) + target_link_libraries(spirv-cross-hlsl-resource-binding-test spirv-cross-c) + set_target_properties(spirv-cross-hlsl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-msl-ycbcr-conversion-test tests-other/msl_ycbcr_conversion_test.cpp) + target_link_libraries(spirv-cross-msl-ycbcr-conversion-test spirv-cross-c) + set_target_properties(spirv-cross-msl-ycbcr-conversion-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-typed-id-test tests-other/typed_id_test.cpp) + target_link_libraries(spirv-cross-typed-id-test spirv-cross-core) + set_target_properties(spirv-cross-typed-id-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")) + target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra) + endif() + add_test(NAME spirv-cross-c-api-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/c_api_test.spv + ${spirv-cross-abi-major} + ${spirv-cross-abi-minor} + ${spirv-cross-abi-patch}) + add_test(NAME spirv-cross-small-vector-test + COMMAND $) + add_test(NAME spirv-cross-msl-constexpr-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_constexpr_test.spv) + add_test(NAME spirv-cross-msl-resource-binding-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_resource_binding.spv) + add_test(NAME spirv-cross-hlsl-resource-binding-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/hlsl_resource_binding.spv) + add_test(NAME spirv-cross-msl-ycbcr-conversion-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test.spv) + add_test(NAME spirv-cross-msl-ycbcr-conversion-test-2 + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test_2.spv) + add_test(NAME spirv-cross-typed-id-test + COMMAND $) + add_test(NAME spirv-cross-test + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-no-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-no-opt + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-metal + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-metal-no-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl-no-opt + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-hlsl + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-hlsl-no-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl-no-opt + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --opt --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-metal-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --opt --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-hlsl-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --opt --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-reflection + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --reflect --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-reflection + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-ue4 + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --msl --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-ue4 + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-ue4-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --msl --opt --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-ue4 + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-ue4-no-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --msl --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-ue4-no-opt + WORKING_DIRECTORY $) + endif() + elseif(NOT ${PYTHONINTERP_FOUND}) + message(WARNING "SPIRV-Cross: Testing disabled. Could not find python3. If you have python3 installed try running " + "cmake with -DPYTHON_EXECUTABLE:FILEPATH=/path/to/python3 to help it find the executable") + endif() + endif() +endif() diff --git a/third_party/spirv-cross/CODE_OF_CONDUCT.md b/third_party/spirv-cross/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a11610b --- /dev/null +++ b/third_party/spirv-cross/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +A reminder that this issue tracker is managed by the Khronos Group. Interactions here should follow the Khronos Code of Conduct (https://www.khronos.org/developers/code-of-conduct), which prohibits aggressive or derogatory language. Please keep the discussion friendly and civil. diff --git a/third_party/spirv-cross/GLSL.std.450.h b/third_party/spirv-cross/GLSL.std.450.h new file mode 100644 index 0000000..54cc00e --- /dev/null +++ b/third_party/spirv-cross/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/third_party/spirv-cross/LICENSE b/third_party/spirv-cross/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/spirv-cross/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/spirv-cross/Makefile b/third_party/spirv-cross/Makefile new file mode 100644 index 0000000..1f4e00b --- /dev/null +++ b/third_party/spirv-cross/Makefile @@ -0,0 +1,41 @@ +TARGET := spirv-cross + +SOURCES := $(wildcard spirv_*.cpp) +CLI_SOURCES := main.cpp + +OBJECTS := $(SOURCES:.cpp=.o) +CLI_OBJECTS := $(CLI_SOURCES:.cpp=.o) + +STATIC_LIB := lib$(TARGET).a + +DEPS := $(OBJECTS:.o=.d) $(CLI_OBJECTS:.o=.d) + +CXXFLAGS += -std=c++11 -Wall -Wextra -Wshadow -Wno-deprecated-declarations + +ifeq ($(DEBUG), 1) + CXXFLAGS += -O0 -g +else + CXXFLAGS += -O2 -DNDEBUG +endif + +ifeq ($(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS), 1) + CXXFLAGS += -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS -fno-exceptions +endif + +all: $(TARGET) + +-include $(DEPS) + +$(TARGET): $(CLI_OBJECTS) $(STATIC_LIB) + $(CXX) -o $@ $(CLI_OBJECTS) $(STATIC_LIB) $(LDFLAGS) + +$(STATIC_LIB): $(OBJECTS) + $(AR) rcs $@ $(OBJECTS) + +%.o: %.cpp + $(CXX) -c -o $@ $< $(CXXFLAGS) -MMD + +clean: + rm -f $(TARGET) $(OBJECTS) $(CLI_OBJECTS) $(STATIC_LIB) $(DEPS) + +.PHONY: clean diff --git a/third_party/spirv-cross/README.md b/third_party/spirv-cross/README.md new file mode 100644 index 0000000..058db89 --- /dev/null +++ b/third_party/spirv-cross/README.md @@ -0,0 +1,515 @@ +# SPIRV-Cross + +SPIRV-Cross is a tool designed for parsing and converting SPIR-V to other shader languages. + +[![Build Status](https://travis-ci.org/KhronosGroup/SPIRV-Cross.svg?branch=master)](https://travis-ci.org/KhronosGroup/SPIRV-Cross) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/KhronosGroup/SPIRV-Cross?svg=true&branch=master)](https://ci.appveyor.com/project/HansKristian-Work/SPIRV-Cross) + +## Features + + - Convert SPIR-V to readable, usable and efficient GLSL + - Convert SPIR-V to readable, usable and efficient Metal Shading Language (MSL) + - Convert SPIR-V to readable, usable and efficient HLSL + - Convert SPIR-V to debuggable C++ [DEPRECATED] + - Convert SPIR-V to a JSON reflection format [EXPERIMENTAL] + - Reflection API to simplify the creation of Vulkan pipeline layouts + - Reflection API to modify and tweak OpDecorations + - Supports "all" of vertex, fragment, tessellation, geometry and compute shaders. + +SPIRV-Cross tries hard to emit readable and clean output from the SPIR-V. +The goal is to emit GLSL or MSL that looks like it was written by a human and not awkward IR/assembly-like code. + +NOTE: Individual features are expected to be mostly complete, but it is possible that certain obscure GLSL features are not yet supported. +However, most missing features are expected to be "trivial" improvements at this stage. + +## Building + +SPIRV-Cross has been tested on Linux, iOS/OSX, Windows and Android. CMake is the main build system. + +### Linux and macOS + +Building with CMake is recommended, as it is the only build system which is tested in continuous integration. +It is also the only build system which has install commands and other useful build system features. + +However, you can just run `make` on the command line as a fallback if you only care about the CLI tool. + +A non-ancient GCC (4.8+) or Clang (3.x+) compiler is required as SPIRV-Cross uses C++11 extensively. + +### Windows + +Building with CMake is recommended, which is the only way to target MSVC. +MinGW-w64 based compilation works with `make` as a fallback. + +### Android + +SPIRV-Cross is only useful as a library here. Use the CMake build to link SPIRV-Cross to your project. + +### C++ exceptions + +The make and CMake build flavors offer the option to treat exceptions as assertions. To disable exceptions for make just append `SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=1` to the command line. For CMake append `-DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=ON`. By default exceptions are enabled. + +### Static, shared and CLI + +You can use `-DSPIRV_CROSS_STATIC=ON/OFF` `-DSPIRV_CROSS_SHARED=ON/OFF` `-DSPIRV_CROSS_CLI=ON/OFF` to control which modules are built (and installed). + +## Usage + +### Using the C++ API + +The C++ API is the main API for SPIRV-Cross. For more in-depth documentation than what's provided in this README, +please have a look at the [Wiki](https://github.com/KhronosGroup/SPIRV-Cross/wiki). +**NOTE**: This API is not guaranteed to be ABI-stable, and it is highly recommended to link against this API statically. +The API is generally quite stable, but it can change over time, see the C API for more stability. + +To perform reflection and convert to other shader languages you can use the SPIRV-Cross API. +For example: + +```c++ +#include "spirv_glsl.hpp" +#include +#include + +extern std::vector load_spirv_file(); + +int main() +{ + // Read SPIR-V from disk or similar. + std::vector spirv_binary = load_spirv_file(); + + spirv_cross::CompilerGLSL glsl(std::move(spirv_binary)); + + // The SPIR-V is now parsed, and we can perform reflection on it. + spirv_cross::ShaderResources resources = glsl.get_shader_resources(); + + // Get all sampled images in the shader. + for (auto &resource : resources.sampled_images) + { + unsigned set = glsl.get_decoration(resource.id, spv::DecorationDescriptorSet); + unsigned binding = glsl.get_decoration(resource.id, spv::DecorationBinding); + printf("Image %s at set = %u, binding = %u\n", resource.name.c_str(), set, binding); + + // Modify the decoration to prepare it for GLSL. + glsl.unset_decoration(resource.id, spv::DecorationDescriptorSet); + + // Some arbitrary remapping if we want. + glsl.set_decoration(resource.id, spv::DecorationBinding, set * 16 + binding); + } + + // Set some options. + spirv_cross::CompilerGLSL::Options options; + options.version = 310; + options.es = true; + glsl.set_options(options); + + // Compile to GLSL, ready to give to GL driver. + std::string source = glsl.compile(); +} +``` + +### Using the C API wrapper + +To facilitate C compatibility and compatibility with foreign programming languages, a C89-compatible API wrapper is provided. Unlike the C++ API, +the goal of this wrapper is to be fully stable, both API and ABI-wise. +This is the only interface which is supported when building SPIRV-Cross as a shared library. + +An important point of the wrapper is that all memory allocations are contained in the `spvc_context`. +This simplifies the use of the API greatly. However, you should destroy the context as soon as reasonable, +or use `spvc_context_release_allocations()` if you intend to reuse the `spvc_context` object again soon. + +Most functions return a `spvc_result`, where `SPVC_SUCCESS` is the only success code. +For brevity, the code below does not do any error checking. + +```c +#include + +const SpvId *spirv = get_spirv_data(); +size_t word_count = get_spirv_word_count(); + +spvc_context context = NULL; +spvc_parsed_ir ir = NULL; +spvc_compiler compiler_glsl = NULL; +spvc_compiler_options options = NULL; +spvc_resources resources = NULL; +const spvc_reflected_resource *list = NULL; +const char *result = NULL; +size_t count; +size_t i; + +// Create context. +spvc_context_create(&context); + +// Set debug callback. +spvc_context_set_error_callback(context, error_callback, userdata); + +// Parse the SPIR-V. +spvc_context_parse_spirv(context, spirv, word_count, &ir); + +// Hand it off to a compiler instance and give it ownership of the IR. +spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler_glsl); + +// Do some basic reflection. +spvc_compiler_create_shader_resources(compiler_glsl, &resources); +spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count); + +for (i = 0; i < count; i++) +{ + printf("ID: %u, BaseTypeID: %u, TypeID: %u, Name: %s\n", list[i].id, list[i].base_type_id, list[i].type_id, + list[i].name); + printf(" Set: %u, Binding: %u\n", + spvc_compiler_get_decoration(compiler_glsl, list[i].id, SpvDecorationDescriptorSet), + spvc_compiler_get_decoration(compiler_glsl, list[i].id, SpvDecorationBinding)); +} + +// Modify options. +spvc_compiler_create_compiler_options(context, &options); +spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 330); +spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_FALSE); +spvc_compiler_install_compiler_options(compiler_glsl, options); + +spvc_compiler_compile(compiler, &result); +printf("Cross-compiled source: %s\n", result); + +// Frees all memory we allocated so far. +spvc_context_destroy(context); +``` + +### Linking + +#### CMake add_subdirectory() + +This is the recommended way if you are using CMake and want to link against SPIRV-Cross statically. + +#### Integrating SPIRV-Cross in a custom build system + +To add SPIRV-Cross to your own codebase, just copy the source and header files from root directory +and build the relevant .cpp files you need. Make sure to build with C++11 support, e.g. `-std=c++11` in GCC and Clang. +Alternatively, the Makefile generates a libspirv-cross.a static library during build that can be linked in. + +#### Linking against SPIRV-Cross as a system library + +It is possible to link against SPIRV-Cross when it is installed as a system library, +which would be mostly relevant for Unix-like platforms. + +##### pkg-config + +For Unix-based systems, a pkg-config is installed for the C API, e.g.: + +``` +$ pkg-config spirv-cross-c-shared --libs --cflags +-I/usr/local/include/spirv_cross -L/usr/local/lib -lspirv-cross-c-shared +``` + +##### CMake + +If the project is installed, it can be found with `find_package()`, e.g.: + +``` +cmake_minimum_required(VERSION 3.5) +set(CMAKE_C_STANDARD 99) +project(Test LANGUAGES C) + +find_package(spirv_cross_c_shared) +if (spirv_cross_c_shared_FOUND) + message(STATUS "Found SPIRV-Cross C API! :)") +else() + message(STATUS "Could not find SPIRV-Cross C API! :(") +endif() + +add_executable(test test.c) +target_link_libraries(test spirv-cross-c-shared) +``` + +test.c: +```c +#include + +int main(void) +{ + spvc_context context; + spvc_context_create(&context); + spvc_context_destroy(context); +} +``` + +### CLI + +The CLI is suitable for basic cross-compilation tasks, but it cannot support the full flexibility that the API can. +Some examples below. + +#### Creating a SPIR-V file from GLSL with glslang + +``` +glslangValidator -H -V -o test.spv test.frag +``` + +#### Converting a SPIR-V file to GLSL ES + +``` +glslangValidator -H -V -o test.spv shaders/comp/basic.comp +./spirv-cross --version 310 --es test.spv +``` + +#### Converting to desktop GLSL + +``` +glslangValidator -H -V -o test.spv shaders/comp/basic.comp +./spirv-cross --version 330 --no-es test.spv --output test.comp +``` + +#### Disable prettifying optimizations + +``` +glslangValidator -H -V -o test.spv shaders/comp/basic.comp +./spirv-cross --version 310 --es test.spv --output test.comp --force-temporary +``` + +### Using shaders generated from C++ backend + +Please see `samples/cpp` where some GLSL shaders are compiled to SPIR-V, decompiled to C++ and run with test data. +Reading through the samples should explain how to use the C++ interface. +A simple Makefile is included to build all shaders in the directory. + +### Implementation notes + +When using SPIR-V and SPIRV-Cross as an intermediate step for cross-compiling between high level languages there are some considerations to take into account, +as not all features used by one high-level language are necessarily supported natively by the target shader language. +SPIRV-Cross aims to provide the tools needed to handle these scenarios in a clean and robust way, but some manual action is required to maintain compatibility. + +#### HLSL source to GLSL + +##### HLSL entry points + +When using SPIR-V shaders compiled from HLSL, there are some extra things you need to take care of. +First make sure that the entry point is used correctly. +If you forget to set the entry point correctly in glslangValidator (-e MyFancyEntryPoint), +you will likely encounter this error message: + +``` +Cannot end a function before ending the current block. +Likely cause: If this SPIR-V was created from glslang HLSL, make sure the entry point is valid. +``` + +##### Vertex/Fragment interface linking + +HLSL relies on semantics in order to effectively link together shader stages. In the SPIR-V generated by glslang, the transformation from HLSL to GLSL ends up looking like + +```c++ +struct VSOutput { + // SV_Position is rerouted to gl_Position + float4 position : SV_Position; + float4 coord : TEXCOORD0; +}; + +VSOutput main(...) {} +``` + +```c++ +struct VSOutput { + float4 coord; +} +layout(location = 0) out VSOutput _magicNameGeneratedByGlslang; +``` + +While this works, be aware of the type of the struct which is used in the vertex stage and the fragment stage. +There may be issues if the structure type name differs in vertex stage and fragment stage. + +You can make use of the reflection interface to force the name of the struct type. + +``` +// Something like this for both vertex outputs and fragment inputs. +compiler.set_name(varying_resource.base_type_id, "VertexFragmentLinkage"); +``` + +Some platform may require identical variable name for both vertex outputs and fragment inputs. (for example MacOSX) +to rename variable base on location, please add +``` +--rename-interface-variable +``` + +#### HLSL source to legacy GLSL/ESSL + +HLSL tends to emit varying struct types to pass data between vertex and fragment. +This is not supported in legacy GL/GLES targets, so to support this, varying structs are flattened. +This is done automatically, but the API user might need to be aware that this is happening in order to support all cases. + +Modern GLES code like this: +```c++ +struct Output { + vec4 a; + vec2 b; +}; +out Output vout; +``` + +Is transformed into: +```c++ +struct Output { + vec4 a; + vec2 b; +}; +varying vec4 Output_a; +varying vec2 Output_b; +``` + +Note that now, both the struct name and the member names will participate in the linking interface between vertex and fragment, so +API users might want to ensure that both the struct names and member names match so that vertex outputs and fragment inputs can link properly. + + +#### Separate image samplers (HLSL/Vulkan) for backends which do not support it (GLSL) + +Another thing you need to remember is when using samplers and textures in HLSL these are separable, and not directly compatible with GLSL. If you need to use this with desktop GL/GLES, you need to call `Compiler::build_combined_image_samplers` first before calling `Compiler::compile`, or you will get an exception. + +```c++ +// From main.cpp +// Builds a mapping for all combinations of images and samplers. +compiler->build_combined_image_samplers(); + +// Give the remapped combined samplers new names. +// Here you can also set up decorations if you want (binding = #N). +for (auto &remap : compiler->get_combined_image_samplers()) +{ + compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id), + compiler->get_name(remap.sampler_id))); +} +``` + +If your target is Vulkan GLSL, `--vulkan-semantics` will emit separate image samplers as you'd expect. +The command line client calls `Compiler::build_combined_image_samplers` automatically, but if you're calling the library, you'll need to do this yourself. + +#### Descriptor sets (Vulkan GLSL) for backends which do not support them (HLSL/GLSL/Metal) + +Descriptor sets are unique to Vulkan, so make sure that descriptor set + binding is remapped to a flat binding scheme (set always 0), so that other APIs can make sense of the bindings. +This can be done with `Compiler::set_decoration(id, spv::DecorationDescriptorSet)`. + +#### Linking by name for targets which do not support explicit locations (legacy GLSL/ESSL) + +Modern GLSL and HLSL sources (and SPIR-V) relies on explicit layout(location) qualifiers to guide the linking process between shader stages, +but older GLSL relies on symbol names to perform the linking. When emitting shaders with older versions, these layout statements will be removed, +so it is important that the API user ensures that the names of I/O variables are sanitized so that linking will work properly. +The reflection API can rename variables, struct types and struct members to deal with these scenarios using `Compiler::set_name` and friends. + +#### Clip-space conventions + +SPIRV-Cross can perform some common clip space conversions on gl_Position/SV_Position by enabling `CompilerGLSL::Options.vertex.fixup_clipspace`. +While this can be convenient, it is recommended to modify the projection matrices instead as that can achieve the same result. + +For GLSL targets, enabling this will convert a shader which assumes `[0, w]` depth range (Vulkan / D3D / Metal) into `[-w, w]` range. +For MSL and HLSL targets, enabling this will convert a shader in `[-w, w]` depth range (OpenGL) to `[0, w]` depth range. + +By default, the CLI will not enable `fixup_clipspace`, but in the API you might want to set an explicit value using `CompilerGLSL::set_options()`. + +Y-flipping of gl_Position and similar is also supported. +The use of this is discouraged, because relying on vertex shader Y-flipping tends to get quite messy. +To enable this, set `CompilerGLSL::Options.vertex.flip_vert_y` or `--flip-vert-y` in CLI. + +#### Reserved identifiers + +When cross-compiling, certain identifiers are considered to be reserved by the implementation. +Code generated by SPIRV-Cross cannot emit these identifiers as they are reserved and used for various internal purposes, +and such variables will typically show up as `_RESERVED_IDENTIFIER_FIXUP_` +or some similar name to make it more obvious that an identifier has been renamed. + +Reflection output will follow the exact name specified in the SPIR-V module. It might not be a valid identifier in the C sense, +as it may contain non-alphanumeric/non-underscore characters. + +Reserved identifiers currently assumed by the implementation are (in pseudo-regex): + +- _$digit+, e.g. `_100`, `_2` +- _$digit+_.+, e.g. `_100_tmp`, `_2_foobar`. `_2Bar` is **not** reserved. +- gl_- prefix +- spv- prefix +- SPIRV_Cross prefix +- Double underscores (reserved by all target languages). + +Members of structs also have a reserved identifier: +- _m$digit+$END, e.g. `_m20` and `_m40` are reserved, but not `_m40Foobar`. + +## Contributing + +Contributions to SPIRV-Cross are welcome. See Testing and Licensing sections for details. + +### Testing + +SPIRV-Cross maintains a test suite of shaders with reference output of how the output looks after going through a roundtrip through +glslangValidator/spirv-as then back through SPIRV-Cross again. +The reference files are stored inside the repository in order to be able to track regressions. + +All pull requests should ensure that test output does not change unexpectedly. This can be tested with: + +``` +./checkout_glslang_spirv_tools.sh # Checks out glslang and SPIRV-Tools at a fixed revision which matches the reference output. + # NOTE: Some users have reported problems cloning from git:// paths. To use https:// instead pass in + # $ PROTOCOL=https ./checkout_glslang_spirv_tools.sh + # instead. +./build_glslang_spirv_tools.sh # Builds glslang and SPIRV-Tools. +./test_shaders.sh # Runs over all changes and makes sure that there are no deltas compared to reference files. +``` + +`./test_shaders.sh` currently requires a Makefile setup with GCC/Clang to be set up. +However, on Windows, this can be rather inconvenient if a MinGW environment is not set up. +To use a spirv-cross binary you built with CMake (or otherwise), you can pass in an environment variable as such: + +``` +SPIRV_CROSS_PATH=path/to/custom/spirv-cross ./test_shaders.sh +``` + +However, when improving SPIRV-Cross there are of course legitimate cases where reference output should change. +In these cases, run: + +``` +./update_test_shaders.sh # SPIRV_CROSS_PATH also works here. +``` + +to update the reference files and include these changes as part of the pull request. +Always make sure you are running the correct version of glslangValidator as well as SPIRV-Tools when updating reference files. +See `checkout_glslang_spirv_tools.sh` which revisions are currently expected. The revisions change regularly. + +In short, the master branch should always be able to run `./test_shaders.py shaders` and friends without failure. +SPIRV-Cross uses Travis CI to test all pull requests, so it is not strictly needed to perform testing yourself if you have problems running it locally. +A pull request which does not pass testing on Travis will not be accepted however. + +When adding support for new features to SPIRV-Cross, a new shader and reference file should be added which covers usage of the new shader features in question. +Travis CI runs the test suite with the CMake, by running `ctest`. This is a more straight-forward alternative to `./test_shaders.sh`. + +### Licensing + +Contributors of new files should add a copyright header at the top of every new source code file with their copyright +along with the Apache 2.0 licensing stub. + +### Formatting + +SPIRV-Cross uses `clang-format` to automatically format code. +Please use `clang-format` with the style sheet found in `.clang-format` to automatically format code before submitting a pull request. + +To make things easy, the `format_all.sh` script can be used to format all +source files in the library. In this directory, run the following from the +command line: + + ./format_all.sh + +## Regression testing + +In shaders/ a collection of shaders are maintained for purposes of regression testing. +The current reference output is contained in reference/. +`./test_shaders.py shaders` can be run to perform regression testing. + +See `./test_shaders.py --help` for more. + +### Metal backend + +To test the roundtrip path GLSL -> SPIR-V -> MSL, `--msl` can be added, e.g. `./test_shaders.py --msl shaders-msl`. + +### HLSL backend + +To test the roundtrip path GLSL -> SPIR-V -> HLSL, `--hlsl` can be added, e.g. `./test_shaders.py --hlsl shaders-hlsl`. + +### Updating regression tests + +When legitimate changes are found, use `--update` flag to update regression files. +Otherwise, `./test_shaders.py` will fail with error code. + +### Mali Offline Compiler cycle counts + +To obtain a CSV of static shader cycle counts before and after going through spirv-cross, add +`--malisc` flag to `./test_shaders`. This requires the Mali Offline Compiler to be installed in PATH. + diff --git a/third_party/spirv-cross/appveyor.yml b/third_party/spirv-cross/appveyor.yml new file mode 100644 index 0000000..2f427f1 --- /dev/null +++ b/third_party/spirv-cross/appveyor.yml @@ -0,0 +1,31 @@ + +environment: + matrix: + - GENERATOR: "Visual Studio 12 2013 Win64" + CONFIG: Debug + + - GENERATOR: "Visual Studio 12 2013 Win64" + CONFIG: Release + + - GENERATOR: "Visual Studio 14 2015 Win64" + CONFIG: Debug + + - GENERATOR: "Visual Studio 14 2015 Win64" + CONFIG: Release + + - GENERATOR: "Visual Studio 12 2013" + CONFIG: Debug + + - GENERATOR: "Visual Studio 12 2013" + CONFIG: Release + + - GENERATOR: "Visual Studio 14 2015" + CONFIG: Debug + + - GENERATOR: "Visual Studio 14 2015" + CONFIG: Release + +build_script: + - git submodule update --init + - cmake "-G%GENERATOR%" -H. -B_builds + - cmake --build _builds --config "%CONFIG%" diff --git a/third_party/spirv-cross/build_glslang_spirv_tools.sh b/third_party/spirv-cross/build_glslang_spirv_tools.sh new file mode 100644 index 0000000..fb4f7de --- /dev/null +++ b/third_party/spirv-cross/build_glslang_spirv_tools.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +PROFILE=Release + +if [ ! -z $1 ]; then + PROFILE=$1 +fi + +if [ ! -z $2 ]; then + NPROC="--parallel $2" +fi + +echo "Building glslang." +mkdir -p external/glslang-build +cd external/glslang-build +cmake ../glslang -DCMAKE_BUILD_TYPE=$PROFILE -DCMAKE_INSTALL_PREFIX=output +cmake --build . --config $PROFILE --target install ${NPROC} +cd ../.. + +echo "Building SPIRV-Tools." +mkdir -p external/spirv-tools-build +cd external/spirv-tools-build +cmake ../spirv-tools -DCMAKE_BUILD_TYPE=$PROFILE -DSPIRV_WERROR=OFF -DCMAKE_INSTALL_PREFIX=output +cmake --build . --config $PROFILE --target install ${NPROC} +cd ../.. + diff --git a/third_party/spirv-cross/checkout_glslang_spirv_tools.sh b/third_party/spirv-cross/checkout_glslang_spirv_tools.sh new file mode 100755 index 0000000..51643d1 --- /dev/null +++ b/third_party/spirv-cross/checkout_glslang_spirv_tools.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +GLSLANG_REV=4dcc12d1a441b29d5901bc708bb1343d29d6459f +SPIRV_TOOLS_REV=0a1fb588cd365f7737cb121fdd64553923e0cef6 +SPIRV_HEADERS_REV=060627f0b0d2fa8581b5acb939f46e3b9e500593 + +if [ -z $PROTOCOL ]; then + PROTOCOL=git +fi + +echo "Using protocol \"$PROTOCOL\" for checking out repositories. If this is problematic, try PROTOCOL=https $0." + +if [ -d external/glslang ]; then + echo "Updating glslang to revision $GLSLANG_REV." + cd external/glslang + git fetch origin + git checkout $GLSLANG_REV +else + echo "Cloning glslang revision $GLSLANG_REV." + mkdir -p external + cd external + git clone $PROTOCOL://github.com/KhronosGroup/glslang.git + cd glslang + git checkout $GLSLANG_REV +fi +cd ../.. + +if [ -d external/spirv-tools ]; then + echo "Updating SPIRV-Tools to revision $SPIRV_TOOLS_REV." + cd external/spirv-tools + git fetch origin + git checkout $SPIRV_TOOLS_REV +else + echo "Cloning SPIRV-Tools revision $SPIRV_TOOLS_REV." + mkdir -p external + cd external + git clone $PROTOCOL://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools + cd spirv-tools + git checkout $SPIRV_TOOLS_REV +fi + +if [ -d external/spirv-headers ]; then + cd external/spirv-headers + git pull origin master + git checkout $SPIRV_HEADERS_REV + cd ../.. +else + git clone $PROTOCOL://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers + cd external/spirv-headers + git checkout $SPIRV_HEADERS_REV + cd ../.. +fi + +cd ../.. + diff --git a/third_party/spirv-cross/cmake/gitversion.in.h b/third_party/spirv-cross/cmake/gitversion.in.h new file mode 100644 index 0000000..7135e28 --- /dev/null +++ b/third_party/spirv-cross/cmake/gitversion.in.h @@ -0,0 +1,6 @@ +#ifndef SPIRV_CROSS_GIT_VERSION_H_ +#define SPIRV_CROSS_GIT_VERSION_H_ + +#define SPIRV_CROSS_GIT_REVISION "Git commit: @spirv-cross-build-version@ Timestamp: @spirv-cross-timestamp@" + +#endif diff --git a/third_party/spirv-cross/format_all.sh b/third_party/spirv-cross/format_all.sh new file mode 100755 index 0000000..fcfffc5 --- /dev/null +++ b/third_party/spirv-cross/format_all.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +#for file in spirv_*.{cpp,hpp} include/spirv_cross/*.{hpp,h} samples/cpp/*.cpp main.cpp +for file in spirv_*.{cpp,hpp} main.cpp +do + echo "Formatting file: $file ..." + clang-format -style=file -i $file +done diff --git a/third_party/spirv-cross/gn/BUILD.gn b/third_party/spirv-cross/gn/BUILD.gn new file mode 100644 index 0000000..8458c1a --- /dev/null +++ b/third_party/spirv-cross/gn/BUILD.gn @@ -0,0 +1,63 @@ +# Copyright (C) 2019 Google, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +config("spirv_cross_public") { + include_dirs = [ ".." ] + + defines = [ "SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS" ] +} + +source_set("spirv_cross_sources") { + public_configs = [ ":spirv_cross_public" ] + + sources = [ + "../GLSL.std.450.h", + "../spirv.hpp", + "../spirv_cfg.cpp", + "../spirv_cfg.hpp", + "../spirv_common.hpp", + "../spirv_cross.cpp", + "../spirv_cross.hpp", + "../spirv_cross_containers.hpp", + "../spirv_cross_error_handling.hpp", + "../spirv_cross_parsed_ir.cpp", + "../spirv_cross_parsed_ir.hpp", + "../spirv_cross_util.cpp", + "../spirv_cross_util.hpp", + "../spirv_glsl.cpp", + "../spirv_glsl.hpp", + "../spirv_msl.cpp", + "../spirv_msl.hpp", + "../spirv_parser.cpp", + "../spirv_parser.hpp", + "../spirv_reflect.cpp", + "../spirv_reflect.hpp", + ] + + cflags = [ "-fno-exceptions" ] + + if (is_clang) { + cflags_cc = [ + "-Wno-extra-semi", + "-Wno-ignored-qualifiers", + "-Wno-implicit-fallthrough", + "-Wno-inconsistent-missing-override", + "-Wno-missing-field-initializers", + "-Wno-newline-eof", + "-Wno-sign-compare", + "-Wno-unused-variable", + ] + } +} diff --git a/third_party/spirv-cross/include/spirv_cross/barrier.hpp b/third_party/spirv-cross/include/spirv_cross/barrier.hpp new file mode 100644 index 0000000..bfcd228 --- /dev/null +++ b/third_party/spirv-cross/include/spirv_cross/barrier.hpp @@ -0,0 +1,79 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_BARRIER_HPP +#define SPIRV_CROSS_BARRIER_HPP + +#include +#include + +namespace spirv_cross +{ +class Barrier +{ +public: + Barrier() + { + count.store(0); + iteration.store(0); + } + + void set_release_divisor(unsigned divisor) + { + this->divisor = divisor; + } + + static inline void memoryBarrier() + { + std::atomic_thread_fence(std::memory_order_seq_cst); + } + + void reset_counter() + { + count.store(0); + iteration.store(0); + } + + void wait() + { + unsigned target_iteration = iteration.load(std::memory_order_relaxed) + 1; + // Overflows cleanly. + unsigned target_count = divisor * target_iteration; + + // Barriers don't enforce memory ordering. + // Be as relaxed about the barrier as we possibly can! + unsigned c = count.fetch_add(1u, std::memory_order_relaxed); + + if (c + 1 == target_count) + { + iteration.store(target_iteration, std::memory_order_relaxed); + } + else + { + // If we have more threads than the CPU, don't hog the CPU for very long periods of time. + while (iteration.load(std::memory_order_relaxed) != target_iteration) + std::this_thread::yield(); + } + } + +private: + unsigned divisor = 1; + std::atomic count; + std::atomic iteration; +}; +} + +#endif diff --git a/third_party/spirv-cross/include/spirv_cross/external_interface.h b/third_party/spirv-cross/include/spirv_cross/external_interface.h new file mode 100644 index 0000000..1d26f1e --- /dev/null +++ b/third_party/spirv-cross/include/spirv_cross/external_interface.h @@ -0,0 +1,126 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_EXTERNAL_INTERFACE_H +#define SPIRV_CROSS_EXTERNAL_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct spirv_cross_shader spirv_cross_shader_t; + +struct spirv_cross_interface +{ + spirv_cross_shader_t *(*construct)(void); + void (*destruct)(spirv_cross_shader_t *thiz); + void (*invoke)(spirv_cross_shader_t *thiz); +}; + +void spirv_cross_set_stage_input(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size); + +void spirv_cross_set_stage_output(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size); + +void spirv_cross_set_push_constant(spirv_cross_shader_t *thiz, void *data, size_t size); + +void spirv_cross_set_uniform_constant(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size); + +void spirv_cross_set_resource(spirv_cross_shader_t *thiz, unsigned set, unsigned binding, void **data, size_t size); + +const struct spirv_cross_interface *spirv_cross_get_interface(void); + +typedef enum spirv_cross_builtin { + SPIRV_CROSS_BUILTIN_POSITION = 0, + SPIRV_CROSS_BUILTIN_FRAG_COORD = 1, + SPIRV_CROSS_BUILTIN_WORK_GROUP_ID = 2, + SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS = 3, + SPIRV_CROSS_NUM_BUILTINS +} spirv_cross_builtin; + +void spirv_cross_set_builtin(spirv_cross_shader_t *thiz, spirv_cross_builtin builtin, void *data, size_t size); + +#define SPIRV_CROSS_NUM_DESCRIPTOR_SETS 4 +#define SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS 16 +#define SPIRV_CROSS_NUM_STAGE_INPUTS 16 +#define SPIRV_CROSS_NUM_STAGE_OUTPUTS 16 +#define SPIRV_CROSS_NUM_UNIFORM_CONSTANTS 32 + +enum spirv_cross_format +{ + SPIRV_CROSS_FORMAT_R8_UNORM = 0, + SPIRV_CROSS_FORMAT_R8G8_UNORM = 1, + SPIRV_CROSS_FORMAT_R8G8B8_UNORM = 2, + SPIRV_CROSS_FORMAT_R8G8B8A8_UNORM = 3, + + SPIRV_CROSS_NUM_FORMATS +}; + +enum spirv_cross_wrap +{ + SPIRV_CROSS_WRAP_CLAMP_TO_EDGE = 0, + SPIRV_CROSS_WRAP_REPEAT = 1, + + SPIRV_CROSS_NUM_WRAP +}; + +enum spirv_cross_filter +{ + SPIRV_CROSS_FILTER_NEAREST = 0, + SPIRV_CROSS_FILTER_LINEAR = 1, + + SPIRV_CROSS_NUM_FILTER +}; + +enum spirv_cross_mipfilter +{ + SPIRV_CROSS_MIPFILTER_BASE = 0, + SPIRV_CROSS_MIPFILTER_NEAREST = 1, + SPIRV_CROSS_MIPFILTER_LINEAR = 2, + + SPIRV_CROSS_NUM_MIPFILTER +}; + +struct spirv_cross_miplevel +{ + const void *data; + unsigned width, height; + size_t stride; +}; + +struct spirv_cross_sampler_info +{ + const struct spirv_cross_miplevel *mipmaps; + unsigned num_mipmaps; + + enum spirv_cross_format format; + enum spirv_cross_wrap wrap_s; + enum spirv_cross_wrap wrap_t; + enum spirv_cross_filter min_filter; + enum spirv_cross_filter mag_filter; + enum spirv_cross_mipfilter mip_filter; +}; + +typedef struct spirv_cross_sampler_2d spirv_cross_sampler_2d_t; +spirv_cross_sampler_2d_t *spirv_cross_create_sampler_2d(const struct spirv_cross_sampler_info *info); +void spirv_cross_destroy_sampler_2d(spirv_cross_sampler_2d_t *samp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/spirv-cross/include/spirv_cross/image.hpp b/third_party/spirv-cross/include/spirv_cross/image.hpp new file mode 100644 index 0000000..73de894 --- /dev/null +++ b/third_party/spirv-cross/include/spirv_cross/image.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_IMAGE_HPP +#define SPIRV_CROSS_IMAGE_HPP + +#ifndef GLM_SWIZZLE +#define GLM_SWIZZLE +#endif + +#ifndef GLM_FORCE_RADIANS +#define GLM_FORCE_RADIANS +#endif + +#include + +namespace spirv_cross +{ +template +struct image2DBase +{ + virtual ~image2DBase() = default; + inline virtual T load(glm::ivec2 coord) const + { + return T(0, 0, 0, 1); + } + inline virtual void store(glm::ivec2 coord, const T &v) + { + } +}; + +typedef image2DBase image2D; +typedef image2DBase iimage2D; +typedef image2DBase uimage2D; + +template +inline T imageLoad(const image2DBase &image, glm::ivec2 coord) +{ + return image.load(coord); +} + +template +void imageStore(image2DBase &image, glm::ivec2 coord, const T &value) +{ + image.store(coord, value); +} +} + +#endif diff --git a/third_party/spirv-cross/include/spirv_cross/internal_interface.hpp b/third_party/spirv-cross/include/spirv_cross/internal_interface.hpp new file mode 100644 index 0000000..e56223d --- /dev/null +++ b/third_party/spirv-cross/include/spirv_cross/internal_interface.hpp @@ -0,0 +1,603 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_INTERNAL_INTERFACE_HPP +#define SPIRV_CROSS_INTERNAL_INTERFACE_HPP + +// This file must only be included by the shader generated by spirv-cross! + +#ifndef GLM_FORCE_SWIZZLE +#define GLM_FORCE_SWIZZLE +#endif + +#ifndef GLM_FORCE_RADIANS +#define GLM_FORCE_RADIANS +#endif + +#include + +#include "barrier.hpp" +#include "external_interface.h" +#include "image.hpp" +#include "sampler.hpp" +#include "thread_group.hpp" +#include +#include + +namespace internal +{ +// Adaptor helpers to adapt GLSL access chain syntax to C++. +// Don't bother with arrays of arrays on uniforms ... +// Would likely need horribly complex variadic template munging. + +template +struct Interface +{ + enum + { + ArraySize = 1, + Size = sizeof(T) + }; + + Interface() + : ptr(0) + { + } + T &get() + { + assert(ptr); + return *ptr; + } + + T *ptr; +}; + +// For array types, return a pointer instead. +template +struct Interface +{ + enum + { + ArraySize = U, + Size = U * sizeof(T) + }; + + Interface() + : ptr(0) + { + } + T *get() + { + assert(ptr); + return ptr; + } + + T *ptr; +}; + +// For case when array size is 1, avoid double dereference. +template +struct PointerInterface +{ + enum + { + ArraySize = 1, + Size = sizeof(T *) + }; + enum + { + PreDereference = true + }; + + PointerInterface() + : ptr(0) + { + } + + T &get() + { + assert(ptr); + return *ptr; + } + + T *ptr; +}; + +// Automatically converts a pointer down to reference to match GLSL syntax. +template +struct DereferenceAdaptor +{ + DereferenceAdaptor(T **ptr) + : ptr(ptr) + { + } + T &operator[](unsigned index) const + { + return *(ptr[index]); + } + T **ptr; +}; + +// We can't have a linear array of T* since T* can be an abstract type in case of samplers. +// We also need a list of pointers since we can have run-time length SSBOs. +template +struct PointerInterface +{ + enum + { + ArraySize = U, + Size = sizeof(T *) * U + }; + enum + { + PreDereference = false + }; + PointerInterface() + : ptr(0) + { + } + + DereferenceAdaptor get() + { + assert(ptr); + return DereferenceAdaptor(ptr); + } + + T **ptr; +}; + +// Resources can be more abstract and be unsized, +// so we need to have an array of pointers for those cases. +template +struct Resource : PointerInterface +{ +}; + +// POD with no unknown sizes, so we can express these as flat arrays. +template +struct UniformConstant : Interface +{ +}; +template +struct StageInput : Interface +{ +}; +template +struct StageOutput : Interface +{ +}; +template +struct PushConstant : Interface +{ +}; +} + +struct spirv_cross_shader +{ + struct PPSize + { + PPSize() + : ptr(0) + , size(0) + { + } + void **ptr; + size_t size; + }; + + struct PPSizeResource + { + PPSizeResource() + : ptr(0) + , size(0) + , pre_dereference(false) + { + } + void **ptr; + size_t size; + bool pre_dereference; + }; + + PPSizeResource resources[SPIRV_CROSS_NUM_DESCRIPTOR_SETS][SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS]; + PPSize stage_inputs[SPIRV_CROSS_NUM_STAGE_INPUTS]; + PPSize stage_outputs[SPIRV_CROSS_NUM_STAGE_OUTPUTS]; + PPSize uniform_constants[SPIRV_CROSS_NUM_UNIFORM_CONSTANTS]; + PPSize push_constant; + PPSize builtins[SPIRV_CROSS_NUM_BUILTINS]; + + template + void register_builtin(spirv_cross_builtin builtin, const U &value) + { + assert(!builtins[builtin].ptr); + + builtins[builtin].ptr = (void **)&value.ptr; + builtins[builtin].size = sizeof(*value.ptr) * U::ArraySize; + } + + void set_builtin(spirv_cross_builtin builtin, void *data, size_t size) + { + assert(builtins[builtin].ptr); + assert(size >= builtins[builtin].size); + + *builtins[builtin].ptr = data; + } + + template + void register_resource(const internal::Resource &value, unsigned set, unsigned binding) + { + assert(set < SPIRV_CROSS_NUM_DESCRIPTOR_SETS); + assert(binding < SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS); + assert(!resources[set][binding].ptr); + + resources[set][binding].ptr = (void **)&value.ptr; + resources[set][binding].size = internal::Resource::Size; + resources[set][binding].pre_dereference = internal::Resource::PreDereference; + } + + template + void register_stage_input(const internal::StageInput &value, unsigned location) + { + assert(location < SPIRV_CROSS_NUM_STAGE_INPUTS); + assert(!stage_inputs[location].ptr); + + stage_inputs[location].ptr = (void **)&value.ptr; + stage_inputs[location].size = internal::StageInput::Size; + } + + template + void register_stage_output(const internal::StageOutput &value, unsigned location) + { + assert(location < SPIRV_CROSS_NUM_STAGE_OUTPUTS); + assert(!stage_outputs[location].ptr); + + stage_outputs[location].ptr = (void **)&value.ptr; + stage_outputs[location].size = internal::StageOutput::Size; + } + + template + void register_uniform_constant(const internal::UniformConstant &value, unsigned location) + { + assert(location < SPIRV_CROSS_NUM_UNIFORM_CONSTANTS); + assert(!uniform_constants[location].ptr); + + uniform_constants[location].ptr = (void **)&value.ptr; + uniform_constants[location].size = internal::UniformConstant::Size; + } + + template + void register_push_constant(const internal::PushConstant &value) + { + assert(!push_constant.ptr); + + push_constant.ptr = (void **)&value.ptr; + push_constant.size = internal::PushConstant::Size; + } + + void set_stage_input(unsigned location, void *data, size_t size) + { + assert(location < SPIRV_CROSS_NUM_STAGE_INPUTS); + assert(stage_inputs[location].ptr); + assert(size >= stage_inputs[location].size); + + *stage_inputs[location].ptr = data; + } + + void set_stage_output(unsigned location, void *data, size_t size) + { + assert(location < SPIRV_CROSS_NUM_STAGE_OUTPUTS); + assert(stage_outputs[location].ptr); + assert(size >= stage_outputs[location].size); + + *stage_outputs[location].ptr = data; + } + + void set_uniform_constant(unsigned location, void *data, size_t size) + { + assert(location < SPIRV_CROSS_NUM_UNIFORM_CONSTANTS); + assert(uniform_constants[location].ptr); + assert(size >= uniform_constants[location].size); + + *uniform_constants[location].ptr = data; + } + + void set_push_constant(void *data, size_t size) + { + assert(push_constant.ptr); + assert(size >= push_constant.size); + + *push_constant.ptr = data; + } + + void set_resource(unsigned set, unsigned binding, void **data, size_t size) + { + assert(set < SPIRV_CROSS_NUM_DESCRIPTOR_SETS); + assert(binding < SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS); + assert(resources[set][binding].ptr); + assert(size >= resources[set][binding].size); + + // We're using the regular PointerInterface, dereference ahead of time. + if (resources[set][binding].pre_dereference) + *resources[set][binding].ptr = *data; + else + *resources[set][binding].ptr = data; + } +}; + +namespace spirv_cross +{ +template +struct BaseShader : spirv_cross_shader +{ + void invoke() + { + static_cast(this)->main(); + } +}; + +struct FragmentResources +{ + internal::StageOutput gl_FragCoord; + void init(spirv_cross_shader &s) + { + s.register_builtin(SPIRV_CROSS_BUILTIN_FRAG_COORD, gl_FragCoord); + } +#define gl_FragCoord __res->gl_FragCoord.get() +}; + +template +struct FragmentShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + FragmentShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct VertexResources +{ + internal::StageOutput gl_Position; + void init(spirv_cross_shader &s) + { + s.register_builtin(SPIRV_CROSS_BUILTIN_POSITION, gl_Position); + } +#define gl_Position __res->gl_Position.get() +}; + +template +struct VertexShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + VertexShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct TessEvaluationResources +{ + inline void init(spirv_cross_shader &) + { + } +}; + +template +struct TessEvaluationShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + TessEvaluationShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct TessControlResources +{ + inline void init(spirv_cross_shader &) + { + } +}; + +template +struct TessControlShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + TessControlShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct GeometryResources +{ + inline void init(spirv_cross_shader &) + { + } +}; + +template +struct GeometryShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + GeometryShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct ComputeResources +{ + internal::StageInput gl_WorkGroupID__; + internal::StageInput gl_NumWorkGroups__; + void init(spirv_cross_shader &s) + { + s.register_builtin(SPIRV_CROSS_BUILTIN_WORK_GROUP_ID, gl_WorkGroupID__); + s.register_builtin(SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS, gl_NumWorkGroups__); + } +#define gl_WorkGroupID __res->gl_WorkGroupID__.get() +#define gl_NumWorkGroups __res->gl_NumWorkGroups__.get() + + Barrier barrier__; +#define barrier() __res->barrier__.wait() +}; + +struct ComputePrivateResources +{ + uint32_t gl_LocalInvocationIndex__; +#define gl_LocalInvocationIndex __priv_res.gl_LocalInvocationIndex__ + glm::uvec3 gl_LocalInvocationID__; +#define gl_LocalInvocationID __priv_res.gl_LocalInvocationID__ + glm::uvec3 gl_GlobalInvocationID__; +#define gl_GlobalInvocationID __priv_res.gl_GlobalInvocationID__ +}; + +template +struct ComputeShader : BaseShader> +{ + inline void main() + { + resources.barrier__.reset_counter(); + + for (unsigned z = 0; z < WorkGroupZ; z++) + for (unsigned y = 0; y < WorkGroupY; y++) + for (unsigned x = 0; x < WorkGroupX; x++) + impl[z][y][x].__priv_res.gl_GlobalInvocationID__ = + glm::uvec3(WorkGroupX, WorkGroupY, WorkGroupZ) * resources.gl_WorkGroupID__.get() + + glm::uvec3(x, y, z); + + group.run(); + group.wait(); + } + + ComputeShader() + : group(&impl[0][0][0]) + { + resources.init(*this); + resources.barrier__.set_release_divisor(WorkGroupX * WorkGroupY * WorkGroupZ); + + unsigned i = 0; + for (unsigned z = 0; z < WorkGroupZ; z++) + { + for (unsigned y = 0; y < WorkGroupY; y++) + { + for (unsigned x = 0; x < WorkGroupX; x++) + { + impl[z][y][x].__priv_res.gl_LocalInvocationID__ = glm::uvec3(x, y, z); + impl[z][y][x].__priv_res.gl_LocalInvocationIndex__ = i++; + impl[z][y][x].__res = &resources; + } + } + } + } + + T impl[WorkGroupZ][WorkGroupY][WorkGroupX]; + ThreadGroup group; + Res resources; +}; + +inline void memoryBarrierShared() +{ + Barrier::memoryBarrier(); +} +inline void memoryBarrier() +{ + Barrier::memoryBarrier(); +} +// TODO: Rest of the barriers. + +// Atomics +template +inline T atomicAdd(T &v, T a) +{ + static_assert(sizeof(std::atomic) == sizeof(T), "Cannot cast properly to std::atomic."); + + // We need explicit memory barriers in GLSL to enfore any ordering. + // FIXME: Can we really cast this? There is no other way I think ... + return std::atomic_fetch_add_explicit(reinterpret_cast *>(&v), a, std::memory_order_relaxed); +} +} + +void spirv_cross_set_stage_input(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size) +{ + shader->set_stage_input(location, data, size); +} + +void spirv_cross_set_stage_output(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size) +{ + shader->set_stage_output(location, data, size); +} + +void spirv_cross_set_uniform_constant(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size) +{ + shader->set_uniform_constant(location, data, size); +} + +void spirv_cross_set_resource(spirv_cross_shader_t *shader, unsigned set, unsigned binding, void **data, size_t size) +{ + shader->set_resource(set, binding, data, size); +} + +void spirv_cross_set_push_constant(spirv_cross_shader_t *shader, void *data, size_t size) +{ + shader->set_push_constant(data, size); +} + +void spirv_cross_set_builtin(spirv_cross_shader_t *shader, spirv_cross_builtin builtin, void *data, size_t size) +{ + shader->set_builtin(builtin, data, size); +} + +#endif diff --git a/third_party/spirv-cross/include/spirv_cross/sampler.hpp b/third_party/spirv-cross/include/spirv_cross/sampler.hpp new file mode 100644 index 0000000..542e11b --- /dev/null +++ b/third_party/spirv-cross/include/spirv_cross/sampler.hpp @@ -0,0 +1,105 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_SAMPLER_HPP +#define SPIRV_CROSS_SAMPLER_HPP + +#include + +namespace spirv_cross +{ +struct spirv_cross_sampler_2d +{ + inline virtual ~spirv_cross_sampler_2d() + { + } +}; + +template +struct sampler2DBase : spirv_cross_sampler_2d +{ + sampler2DBase(const spirv_cross_sampler_info *info) + { + mips.insert(mips.end(), info->mipmaps, info->mipmaps + info->num_mipmaps); + format = info->format; + wrap_s = info->wrap_s; + wrap_t = info->wrap_t; + min_filter = info->min_filter; + mag_filter = info->mag_filter; + mip_filter = info->mip_filter; + } + + inline virtual T sample(glm::vec2 uv, float bias) + { + return sampleLod(uv, bias); + } + + inline virtual T sampleLod(glm::vec2 uv, float lod) + { + if (mag_filter == SPIRV_CROSS_FILTER_NEAREST) + { + uv.x = wrap(uv.x, wrap_s, mips[0].width); + uv.y = wrap(uv.y, wrap_t, mips[0].height); + glm::vec2 uv_full = uv * glm::vec2(mips[0].width, mips[0].height); + + int x = int(uv_full.x); + int y = int(uv_full.y); + return sample(x, y, 0); + } + else + { + return T(0, 0, 0, 1); + } + } + + inline float wrap(float v, spirv_cross_wrap wrap, unsigned size) + { + switch (wrap) + { + case SPIRV_CROSS_WRAP_REPEAT: + return v - glm::floor(v); + case SPIRV_CROSS_WRAP_CLAMP_TO_EDGE: + { + float half = 0.5f / size; + return glm::clamp(v, half, 1.0f - half); + } + + default: + return 0.0f; + } + } + + std::vector mips; + spirv_cross_format format; + spirv_cross_wrap wrap_s; + spirv_cross_wrap wrap_t; + spirv_cross_filter min_filter; + spirv_cross_filter mag_filter; + spirv_cross_mipfilter mip_filter; +}; + +typedef sampler2DBase sampler2D; +typedef sampler2DBase isampler2D; +typedef sampler2DBase usampler2D; + +template +inline T texture(const sampler2DBase &samp, const glm::vec2 &uv, float bias = 0.0f) +{ + return samp.sample(uv, bias); +} +} + +#endif diff --git a/third_party/spirv-cross/include/spirv_cross/thread_group.hpp b/third_party/spirv-cross/include/spirv_cross/thread_group.hpp new file mode 100644 index 0000000..377f098 --- /dev/null +++ b/third_party/spirv-cross/include/spirv_cross/thread_group.hpp @@ -0,0 +1,113 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_THREAD_GROUP_HPP +#define SPIRV_CROSS_THREAD_GROUP_HPP + +#include +#include +#include + +namespace spirv_cross +{ +template +class ThreadGroup +{ +public: + ThreadGroup(T *impl) + { + for (unsigned i = 0; i < Size; i++) + workers[i].start(&impl[i]); + } + + void run() + { + for (auto &worker : workers) + worker.run(); + } + + void wait() + { + for (auto &worker : workers) + worker.wait(); + } + +private: + struct Thread + { + enum State + { + Idle, + Running, + Dying + }; + State state = Idle; + + void start(T *impl) + { + worker = std::thread([impl, this] { + for (;;) + { + { + std::unique_lock l{ lock }; + cond.wait(l, [this] { return state != Idle; }); + if (state == Dying) + break; + } + + impl->main(); + + std::lock_guard l{ lock }; + state = Idle; + cond.notify_one(); + } + }); + } + + void wait() + { + std::unique_lock l{ lock }; + cond.wait(l, [this] { return state == Idle; }); + } + + void run() + { + std::lock_guard l{ lock }; + state = Running; + cond.notify_one(); + } + + ~Thread() + { + if (worker.joinable()) + { + { + std::lock_guard l{ lock }; + state = Dying; + cond.notify_one(); + } + worker.join(); + } + } + std::thread worker; + std::condition_variable cond; + std::mutex lock; + }; + Thread workers[Size]; +}; +} + +#endif diff --git a/third_party/spirv-cross/main.cpp b/third_party/spirv-cross/main.cpp new file mode 100644 index 0000000..da24776 --- /dev/null +++ b/third_party/spirv-cross/main.cpp @@ -0,0 +1,1591 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cpp.hpp" +#include "spirv_cross_util.hpp" +#include "spirv_glsl.hpp" +#include "spirv_hlsl.hpp" +#include "spirv_msl.hpp" +#include "spirv_parser.hpp" +#include "spirv_reflect.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SPIRV_CROSS_GIT_VERSION +#include "gitversion.h" +#endif + +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +static inline void THROW(const char *str) +{ + fprintf(stderr, "SPIRV-Cross will abort: %s\n", str); + fflush(stderr); + abort(); +} +#else +#define THROW(x) throw runtime_error(x) +#endif + +struct CLIParser; +struct CLICallbacks +{ + void add(const char *cli, const function &func) + { + callbacks[cli] = func; + } + unordered_map> callbacks; + function error_handler; + function default_handler; +}; + +struct CLIParser +{ + CLIParser(CLICallbacks cbs_, int argc_, char *argv_[]) + : cbs(move(cbs_)) + , argc(argc_) + , argv(argv_) + { + } + + bool parse() + { +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS + try +#endif + { + while (argc && !ended_state) + { + const char *next = *argv++; + argc--; + + if (*next != '-' && cbs.default_handler) + { + cbs.default_handler(next); + } + else + { + auto itr = cbs.callbacks.find(next); + if (itr == ::end(cbs.callbacks)) + { + THROW("Invalid argument"); + } + + itr->second(*this); + } + } + + return true; + } +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS + catch (...) + { + if (cbs.error_handler) + { + cbs.error_handler(); + } + return false; + } +#endif + } + + void end() + { + ended_state = true; + } + + uint32_t next_uint() + { + if (!argc) + { + THROW("Tried to parse uint, but nothing left in arguments"); + } + + uint64_t val = stoul(*argv); + if (val > numeric_limits::max()) + { + THROW("next_uint() out of range"); + } + + argc--; + argv++; + + return uint32_t(val); + } + + uint32_t next_hex_uint() + { + if (!argc) + { + THROW("Tried to parse uint, but nothing left in arguments"); + } + + uint64_t val = stoul(*argv, nullptr, 16); + if (val > numeric_limits::max()) + { + THROW("next_uint() out of range"); + } + + argc--; + argv++; + + return uint32_t(val); + } + + double next_double() + { + if (!argc) + { + THROW("Tried to parse double, but nothing left in arguments"); + } + + double val = stod(*argv); + + argc--; + argv++; + + return val; + } + + // Return a string only if it's not prefixed with `--`, otherwise return the default value + const char *next_value_string(const char *default_value) + { + if (!argc) + { + return default_value; + } + + if (0 == strncmp("--", *argv, 2)) + { + return default_value; + } + + return next_string(); + } + + const char *next_string() + { + if (!argc) + { + THROW("Tried to parse string, but nothing left in arguments"); + } + + const char *ret = *argv; + argc--; + argv++; + return ret; + } + + CLICallbacks cbs; + int argc; + char **argv; + bool ended_state = false; +}; + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +static vector read_spirv_file(const char *path) +{ + FILE *file = fopen(path, "rb"); + if (!file) + { + fprintf(stderr, "Failed to open SPIR-V file: %s\n", path); + return {}; + } + + fseek(file, 0, SEEK_END); + long len = ftell(file) / sizeof(uint32_t); + rewind(file); + + vector spirv(len); + if (fread(spirv.data(), sizeof(uint32_t), len, file) != size_t(len)) + spirv.clear(); + + fclose(file); + return spirv; +} + +static bool write_string_to_file(const char *path, const char *string) +{ + FILE *file = fopen(path, "w"); + if (!file) + { + fprintf(stderr, "Failed to write file: %s\n", path); + return false; + } + + fprintf(file, "%s", string); + fclose(file); + return true; +} + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +static void print_resources(const Compiler &compiler, const char *tag, const SmallVector &resources) +{ + fprintf(stderr, "%s\n", tag); + fprintf(stderr, "=============\n\n"); + bool print_ssbo = !strcmp(tag, "ssbos"); + + for (auto &res : resources) + { + auto &type = compiler.get_type(res.type_id); + + if (print_ssbo && compiler.buffer_is_hlsl_counter_buffer(res.id)) + continue; + + // If we don't have a name, use the fallback for the type instead of the variable + // for SSBOs and UBOs since those are the only meaningful names to use externally. + // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks. + bool is_push_constant = compiler.get_storage_class(res.id) == StorageClassPushConstant; + bool is_block = compiler.get_decoration_bitset(type.self).get(DecorationBlock) || + compiler.get_decoration_bitset(type.self).get(DecorationBufferBlock); + bool is_sized_block = is_block && (compiler.get_storage_class(res.id) == StorageClassUniform || + compiler.get_storage_class(res.id) == StorageClassUniformConstant); + ID fallback_id = !is_push_constant && is_block ? ID(res.base_type_id) : ID(res.id); + + uint32_t block_size = 0; + uint32_t runtime_array_stride = 0; + if (is_sized_block) + { + auto &base_type = compiler.get_type(res.base_type_id); + block_size = uint32_t(compiler.get_declared_struct_size(base_type)); + runtime_array_stride = uint32_t(compiler.get_declared_struct_size_runtime_array(base_type, 1) - + compiler.get_declared_struct_size_runtime_array(base_type, 0)); + } + + Bitset mask; + if (print_ssbo) + mask = compiler.get_buffer_block_flags(res.id); + else + mask = compiler.get_decoration_bitset(res.id); + + string array; + for (auto arr : type.array) + array = join("[", arr ? convert_to_string(arr) : "", "]") + array; + + fprintf(stderr, " ID %03u : %s%s", uint32_t(res.id), + !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str(), array.c_str()); + + if (mask.get(DecorationLocation)) + fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation)); + if (mask.get(DecorationDescriptorSet)) + fprintf(stderr, " (Set : %u)", compiler.get_decoration(res.id, DecorationDescriptorSet)); + if (mask.get(DecorationBinding)) + fprintf(stderr, " (Binding : %u)", compiler.get_decoration(res.id, DecorationBinding)); + if (static_cast(compiler).variable_is_depth_or_compare(res.id)) + fprintf(stderr, " (comparison)"); + if (mask.get(DecorationInputAttachmentIndex)) + fprintf(stderr, " (Attachment : %u)", compiler.get_decoration(res.id, DecorationInputAttachmentIndex)); + if (mask.get(DecorationNonReadable)) + fprintf(stderr, " writeonly"); + if (mask.get(DecorationNonWritable)) + fprintf(stderr, " readonly"); + if (is_sized_block) + { + fprintf(stderr, " (BlockSize : %u bytes)", block_size); + if (runtime_array_stride) + fprintf(stderr, " (Unsized array stride: %u bytes)", runtime_array_stride); + } + + uint32_t counter_id = 0; + if (print_ssbo && compiler.buffer_get_hlsl_counter_buffer(res.id, counter_id)) + fprintf(stderr, " (HLSL counter buffer ID: %u)", counter_id); + fprintf(stderr, "\n"); + } + fprintf(stderr, "=============\n\n"); +} + +static const char *execution_model_to_str(spv::ExecutionModel model) +{ + switch (model) + { + case spv::ExecutionModelVertex: + return "vertex"; + case spv::ExecutionModelTessellationControl: + return "tessellation control"; + case ExecutionModelTessellationEvaluation: + return "tessellation evaluation"; + case ExecutionModelGeometry: + return "geometry"; + case ExecutionModelFragment: + return "fragment"; + case ExecutionModelGLCompute: + return "compute"; + case ExecutionModelRayGenerationNV: + return "raygenNV"; + case ExecutionModelIntersectionNV: + return "intersectionNV"; + case ExecutionModelCallableNV: + return "callableNV"; + case ExecutionModelAnyHitNV: + return "anyhitNV"; + case ExecutionModelClosestHitNV: + return "closesthitNV"; + case ExecutionModelMissNV: + return "missNV"; + default: + return "???"; + } +} + +static void print_resources(const Compiler &compiler, const ShaderResources &res) +{ + auto &modes = compiler.get_execution_mode_bitset(); + + fprintf(stderr, "Entry points:\n"); + auto entry_points = compiler.get_entry_points_and_stages(); + for (auto &e : entry_points) + fprintf(stderr, " %s (%s)\n", e.name.c_str(), execution_model_to_str(e.execution_model)); + fprintf(stderr, "\n"); + + fprintf(stderr, "Execution modes:\n"); + modes.for_each_bit([&](uint32_t i) { + auto mode = static_cast(i); + uint32_t arg0 = compiler.get_execution_mode_argument(mode, 0); + uint32_t arg1 = compiler.get_execution_mode_argument(mode, 1); + uint32_t arg2 = compiler.get_execution_mode_argument(mode, 2); + + switch (static_cast(i)) + { + case ExecutionModeInvocations: + fprintf(stderr, " Invocations: %u\n", arg0); + break; + + case ExecutionModeLocalSize: + fprintf(stderr, " LocalSize: (%u, %u, %u)\n", arg0, arg1, arg2); + break; + + case ExecutionModeOutputVertices: + fprintf(stderr, " OutputVertices: %u\n", arg0); + break; + +#define CHECK_MODE(m) \ + case ExecutionMode##m: \ + fprintf(stderr, " %s\n", #m); \ + break + CHECK_MODE(SpacingEqual); + CHECK_MODE(SpacingFractionalEven); + CHECK_MODE(SpacingFractionalOdd); + CHECK_MODE(VertexOrderCw); + CHECK_MODE(VertexOrderCcw); + CHECK_MODE(PixelCenterInteger); + CHECK_MODE(OriginUpperLeft); + CHECK_MODE(OriginLowerLeft); + CHECK_MODE(EarlyFragmentTests); + CHECK_MODE(PointMode); + CHECK_MODE(Xfb); + CHECK_MODE(DepthReplacing); + CHECK_MODE(DepthGreater); + CHECK_MODE(DepthLess); + CHECK_MODE(DepthUnchanged); + CHECK_MODE(LocalSizeHint); + CHECK_MODE(InputPoints); + CHECK_MODE(InputLines); + CHECK_MODE(InputLinesAdjacency); + CHECK_MODE(Triangles); + CHECK_MODE(InputTrianglesAdjacency); + CHECK_MODE(Quads); + CHECK_MODE(Isolines); + CHECK_MODE(OutputPoints); + CHECK_MODE(OutputLineStrip); + CHECK_MODE(OutputTriangleStrip); + CHECK_MODE(VecTypeHint); + CHECK_MODE(ContractionOff); + + default: + break; + } + }); + fprintf(stderr, "\n"); + + print_resources(compiler, "subpass inputs", res.subpass_inputs); + print_resources(compiler, "inputs", res.stage_inputs); + print_resources(compiler, "outputs", res.stage_outputs); + print_resources(compiler, "textures", res.sampled_images); + print_resources(compiler, "separate images", res.separate_images); + print_resources(compiler, "separate samplers", res.separate_samplers); + print_resources(compiler, "images", res.storage_images); + print_resources(compiler, "ssbos", res.storage_buffers); + print_resources(compiler, "ubos", res.uniform_buffers); + print_resources(compiler, "push", res.push_constant_buffers); + print_resources(compiler, "counters", res.atomic_counters); + print_resources(compiler, "acceleration structures", res.acceleration_structures); +} + +static void print_push_constant_resources(const Compiler &compiler, const SmallVector &res) +{ + for (auto &block : res) + { + auto ranges = compiler.get_active_buffer_ranges(block.id); + fprintf(stderr, "Active members in buffer: %s\n", + !block.name.empty() ? block.name.c_str() : compiler.get_fallback_name(block.id).c_str()); + + fprintf(stderr, "==================\n\n"); + for (auto &range : ranges) + { + const auto &name = compiler.get_member_name(block.base_type_id, range.index); + + fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index, + !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(), + unsigned(range.offset), unsigned(range.range)); + } + fprintf(stderr, "==================\n\n"); + } +} + +static void print_spec_constants(const Compiler &compiler) +{ + auto spec_constants = compiler.get_specialization_constants(); + fprintf(stderr, "Specialization constants\n"); + fprintf(stderr, "==================\n\n"); + for (auto &c : spec_constants) + fprintf(stderr, "ID: %u, Spec ID: %u\n", uint32_t(c.id), c.constant_id); + fprintf(stderr, "==================\n\n"); +} + +static void print_capabilities_and_extensions(const Compiler &compiler) +{ + fprintf(stderr, "Capabilities\n"); + fprintf(stderr, "============\n"); + for (auto &capability : compiler.get_declared_capabilities()) + fprintf(stderr, "Capability: %u\n", static_cast(capability)); + fprintf(stderr, "============\n\n"); + + fprintf(stderr, "Extensions\n"); + fprintf(stderr, "============\n"); + for (auto &ext : compiler.get_declared_extensions()) + fprintf(stderr, "Extension: %s\n", ext.c_str()); + fprintf(stderr, "============\n\n"); +} + +struct PLSArg +{ + PlsFormat format; + string name; +}; + +struct Remap +{ + string src_name; + string dst_name; + unsigned components; +}; + +struct VariableTypeRemap +{ + string variable_name; + string new_variable_type; +}; + +struct InterfaceVariableRename +{ + StorageClass storageClass; + uint32_t location; + string variable_name; +}; + +struct CLIArguments +{ + const char *input = nullptr; + const char *output = nullptr; + const char *cpp_interface_name = nullptr; + uint32_t version = 0; + uint32_t shader_model = 0; + uint32_t msl_version = 0; + bool es = false; + bool set_version = false; + bool set_shader_model = false; + bool set_msl_version = false; + bool set_es = false; + bool dump_resources = false; + bool force_temporary = false; + bool flatten_ubo = false; + bool fixup = false; + bool yflip = false; + bool sso = false; + bool support_nonzero_baseinstance = true; + bool msl_capture_output_to_buffer = false; + bool msl_swizzle_texture_samples = false; + bool msl_ios = false; + bool msl_pad_fragment_output = false; + bool msl_domain_lower_left = false; + bool msl_argument_buffers = false; + bool msl_texture_buffer_native = false; + bool msl_framebuffer_fetch = false; + bool msl_invariant_float_math = false; + bool msl_emulate_cube_array = false; + bool msl_multiview = false; + bool msl_multiview_layered_rendering = true; + bool msl_view_index_from_device_index = false; + bool msl_dispatch_base = false; + bool msl_decoration_binding = false; + bool msl_force_active_argument_buffer_resources = false; + bool msl_force_native_arrays = false; + bool msl_enable_frag_depth_builtin = true; + bool msl_enable_frag_stencil_ref_builtin = true; + uint32_t msl_enable_frag_output_mask = 0xffffffff; + bool msl_enable_clip_distance_user_varying = true; + bool msl_multi_patch_workgroup = false; + bool msl_vertex_for_tessellation = false; + uint32_t msl_additional_fixed_sample_mask = 0xffffffff; + bool msl_arrayed_subpass_input = false; + uint32_t msl_r32ui_linear_texture_alignment = 4; + uint32_t msl_r32ui_alignment_constant_id = 65535; + bool msl_texture_1d_as_2d = false; + bool glsl_emit_push_constant_as_ubo = false; + bool glsl_emit_ubo_as_plain_uniforms = false; + bool glsl_force_flattened_io_blocks = false; + SmallVector> glsl_ext_framebuffer_fetch; + bool vulkan_glsl_disable_ext_samplerless_texture_functions = false; + bool emit_line_directives = false; + bool enable_storage_image_qualifier_deduction = true; + bool force_zero_initialized_variables = false; + SmallVector msl_discrete_descriptor_sets; + SmallVector msl_device_argument_buffers; + SmallVector> msl_dynamic_buffers; + SmallVector> msl_inline_uniform_blocks; + SmallVector msl_shader_inputs; + SmallVector pls_in; + SmallVector pls_out; + SmallVector remaps; + SmallVector extensions; + SmallVector variable_type_remaps; + SmallVector interface_variable_renames; + SmallVector hlsl_attr_remap; + string entry; + string entry_stage; + + struct Rename + { + string old_name; + string new_name; + ExecutionModel execution_model; + }; + SmallVector entry_point_rename; + + uint32_t iterations = 1; + bool cpp = false; + string reflect; + bool msl = false; + bool hlsl = false; + bool hlsl_compat = false; + bool hlsl_support_nonzero_base = false; + bool hlsl_force_storage_buffer_as_uav = false; + bool hlsl_nonwritable_uav_texture_as_srv = false; + bool hlsl_enable_16bit_types = false; + bool hlsl_flatten_matrix_vertex_input_semantics = false; + HLSLBindingFlags hlsl_binding_flags = 0; + bool vulkan_semantics = false; + bool flatten_multidimensional_arrays = false; + bool use_420pack_extension = true; + bool remove_unused = false; + bool combined_samplers_inherit_bindings = false; +}; + +static void print_version() +{ +#ifdef HAVE_SPIRV_CROSS_GIT_VERSION + fprintf(stderr, "%s\n", SPIRV_CROSS_GIT_REVISION); +#else + fprintf(stderr, "Git revision unknown. Build with CMake to create timestamp and revision info.\n"); +#endif +} + +static void print_help_backend() +{ + // clang-format off + fprintf(stderr, "\nSelect backend:\n" + "\tBy default, OpenGL-style GLSL is the target, with #version and GLSL/ESSL information inherited from the SPIR-V module if present.\n" + "\t[--vulkan-semantics] or [-V]:\n\t\tEmit Vulkan GLSL instead of plain GLSL. Makes use of Vulkan-only features to match SPIR-V.\n" + "\t[--msl]:\n\t\tEmit Metal Shading Language (MSL).\n" + "\t[--hlsl]:\n\t\tEmit HLSL.\n" + "\t[--reflect]:\n\t\tEmit JSON reflection.\n" + "\t[--cpp]:\n\t\tDEPRECATED. Emits C++ code.\n" + ); + // clang-format on +} + +static void print_help_glsl() +{ + // clang-format off + fprintf(stderr, "\nGLSL options:\n" + "\t[--es]:\n\t\tForce ESSL.\n" + "\t[--no-es]:\n\t\tForce desktop GLSL.\n" + "\t[--version ]:\n\t\tE.g. --version 450 will emit '#version 450' in shader.\n" + "\t\tCode generation will depend on the version used.\n" + "\t[--flatten-ubo]:\n\t\tEmit UBOs as plain uniform arrays which are suitable for use with glUniform4*v().\n" + "\t\tThis can be an optimization on GL implementations where this is faster or works around buggy driver implementations.\n" + "\t\tE.g.: uniform MyUBO { vec4 a; float b, c, d, e; }; will be emitted as uniform vec4 MyUBO[2];\n" + "\t\tCaveat: You cannot mix and match floating-point and integer in the same UBO with this option.\n" + "\t\tLegacy GLSL/ESSL (where this flattening makes sense) does not support bit-casting, which would have been the obvious workaround.\n" + "\t[--extension ext]:\n\t\tAdd #extension string of your choosing to GLSL output.\n" + "\t\tUseful if you use variable name remapping to something that requires an extension unknown to SPIRV-Cross.\n" + "\t[--remove-unused-variables]:\n\t\tDo not emit interface variables which are not statically accessed by the shader.\n" + "\t[--separate-shader-objects]:\n\t\tRedeclare gl_PerVertex blocks to be suitable for desktop GL separate shader objects.\n" + "\t[--glsl-emit-push-constant-as-ubo]:\n\t\tInstead of a plain uniform of struct for push constants, emit a UBO block instead.\n" + "\t[--glsl-emit-ubo-as-plain-uniforms]:\n\t\tInstead of emitting UBOs, emit them as plain uniform structs.\n" + "\t[--glsl-remap-ext-framebuffer-fetch input-attachment color-location]:\n\t\tRemaps an input attachment to use GL_EXT_shader_framebuffer_fetch.\n" + "\t\tgl_LastFragData[location] is read from. The attachment to read from must be declared as an output in the shader.\n" + "\t[--vulkan-glsl-disable-ext-samplerless-texture-functions]:\n\t\tDo not allow use of GL_EXT_samperless_texture_functions, even in Vulkan GLSL.\n" + "\t\tUse of texelFetch and similar might have to create dummy samplers to work around it.\n" + "\t[--combined-samplers-inherit-bindings]:\n\t\tInherit binding information from the textures when building combined image samplers from separate textures and samplers.\n" + "\t[--no-support-nonzero-baseinstance]:\n\t\tWhen using gl_InstanceIndex with desktop GL,\n" + "\t\tassume that base instance is always 0, and do not attempt to fix up gl_InstanceID to match Vulkan semantics.\n" + "\t[--pls-in format input-name]:\n\t\tRemaps a subpass input with name into a GL_EXT_pixel_local_storage input.\n" + "\t\tEntry in PLS block is ordered where first --pls-in marks the first entry. Can be called multiple times.\n" + "\t\tFormats allowed: r11f_g11f_b10f, r32f, rg16f, rg16, rgb10_a2, rgba8, rgba8i, rgba8ui, rg16i, rgb10_a2ui, rg16ui, r32ui.\n" + "\t\tRequires ESSL.\n" + "\t[--pls-out format output-name]:\n\t\tRemaps a color output with name into a GL_EXT_pixel_local_storage output.\n" + "\t\tEntry in PLS block is ordered where first --pls-output marks the first entry. Can be called multiple times.\n" + "\t\tFormats allowed: r11f_g11f_b10f, r32f, rg16f, rg16, rgb10_a2, rgba8, rgba8i, rgba8ui, rg16i, rgb10_a2ui, rg16ui, r32ui.\n" + "\t\tRequires ESSL.\n" + "\t[--remap source_name target_name components]:\n\t\tRemaps a variable to a different name with N components.\n" + "\t\tMain use case is to remap a subpass input to gl_LastFragDepthARM.\n" + "\t\tE.g.:\n" + "\t\tuniform subpassInput uDepth;\n" + "\t\t--remap uDepth gl_LastFragDepthARM 1 --extension GL_ARM_shader_framebuffer_fetch_depth_stencil\n" + "\t[--no-420pack-extension]:\n\t\tDo not make use of GL_ARB_shading_language_420pack in older GL targets to support layout(binding).\n" + "\t[--remap-variable-type ]:\n\t\tRemaps a variable type based on name.\n" + "\t\tPrimary use case is supporting external samplers in ESSL for video rendering on Android where you could remap a texture to a YUV one.\n" + "\t[--glsl-force-flattened-io-blocks]:\n\t\tAlways flatten I/O blocks and structs.\n" + ); + // clang-format on +} + +static void print_help_hlsl() +{ + // clang-format off + fprintf(stderr, "\nHLSL options:\n" + "\t[--shader-model]:\n\t\tEnables a specific shader model, e.g. --shader-model 50 for SM 5.0.\n" + "\t[--hlsl-enable-compat]:\n\t\tAllow point size and point coord to be used, even if they won't work as expected.\n" + "\t\tPointSize is ignored, and PointCoord returns (0.5, 0.5).\n" + "\t[--hlsl-support-nonzero-basevertex-baseinstance]:\n\t\tSupport base vertex and base instance by emitting a special cbuffer declared as:\n" + "\t\tcbuffer SPIRV_Cross_VertexInfo { int SPIRV_Cross_BaseVertex; int SPIRV_Cross_BaseInstance; };\n" + "\t[--hlsl-auto-binding (push, cbv, srv, uav, sampler, all)]\n" + "\t\tDo not emit any : register(#) bindings for specific resource types, and rely on HLSL compiler to assign something.\n" + "\t[--hlsl-force-storage-buffer-as-uav]:\n\t\tAlways emit SSBOs as UAVs, even when marked as read-only.\n" + "\t\tNormally, SSBOs marked with NonWritable will be emitted as SRVs.\n" + "\t[--hlsl-nonwritable-uav-texture-as-srv]:\n\t\tEmit NonWritable storage images as SRV textures instead of UAV.\n" + "\t\tUsing this option messes with the type system. SPIRV-Cross cannot guarantee that this will work.\n" + "\t\tOne major problem area with this feature is function arguments, where we won't know if we're seeing a UAV or SRV.\n" + "\t\tShader must ensure that read/write state is consistent at all call sites.\n" + "\t[--set-hlsl-vertex-input-semantic ]:\n\t\tEmits a specific vertex input semantic for a given location.\n" + "\t\tOtherwise, TEXCOORD# is used as semantics, where # is location.\n" + "\t[--hlsl-enable-16bit-types]:\n\t\tEnables native use of half/int16_t/uint16_t and ByteAddressBuffer interaction with these types. Requires SM 6.2.\n" + "\t[--hlsl-flatten-matrix-vertex-input-semantics]:\n\t\tEmits matrix vertex inputs with input semantics as if they were independent vectors, e.g. TEXCOORD{2,3,4} rather than matrix form TEXCOORD2_{0,1,2}.\n" + ); + // clang-format on +} + +static void print_help_msl() +{ + // clang-format off + fprintf(stderr, "\nMSL options:\n" + "\t[--msl-version ]:\n\t\tUses a specific MSL version, e.g. --msl-version 20100 for MSL 2.1.\n" + "\t[--msl-capture-output]:\n\t\tWrites geometry varyings to a buffer instead of as stage-outputs.\n" + "\t[--msl-swizzle-texture-samples]:\n\t\tWorks around lack of support for VkImageView component swizzles.\n" + "\t\tThis has a massive impact on performance and bloat. Do not use this unless you are absolutely forced to.\n" + "\t\tTo use this feature, the API side must pass down swizzle buffers.\n" + "\t\tShould only be used by translation layers as a last resort.\n" + "\t\tRecent Metal versions do not require this workaround.\n" + "\t[--msl-ios]:\n\t\tTarget iOS Metal instead of macOS Metal.\n" + "\t[--msl-pad-fragment-output]:\n\t\tAlways emit color outputs as 4-component variables.\n" + "\t\tIn Metal, the fragment shader must emit at least as many components as the render target format.\n" + "\t[--msl-domain-lower-left]:\n\t\tUse a lower-left tessellation domain.\n" + "\t[--msl-argument-buffers]:\n\t\tEmit Indirect Argument buffers instead of plain bindings.\n" + "\t\tRequires MSL 2.0 to be enabled.\n" + "\t[--msl-texture-buffer-native]:\n\t\tEnable native support for texel buffers. Otherwise, it is emulated as a normal texture.\n" + "\t[--msl-framebuffer-fetch]:\n\t\tImplement subpass inputs with frame buffer fetch.\n" + "\t\tEmits [[color(N)]] inputs in fragment stage.\n" + "\t\tRequires an Apple GPU.\n" + "\t[--msl-emulate-cube-array]:\n\t\tEmulate cube arrays with 2D array and manual math.\n" + "\t[--msl-discrete-descriptor-set ]:\n\t\tWhen using argument buffers, forces a specific descriptor set to be implemented without argument buffers.\n" + "\t\tUseful for implementing push descriptors in emulation layers.\n" + "\t\tCan be used multiple times for each descriptor set in question.\n" + "\t[--msl-device-argument-buffer ]:\n\t\tUse device address space to hold indirect argument buffers instead of constant.\n" + "\t\tComes up when trying to support argument buffers which are larger than 64 KiB.\n" + "\t[--msl-multiview]:\n\t\tEnable SPV_KHR_multiview emulation.\n" + "\t[--msl-multiview-no-layered-rendering]:\n\t\tDon't set [[render_target_array_index]] in multiview shaders.\n" + "\t\tUseful for devices which don't support layered rendering. Only effective when --msl-multiview is enabled.\n" + "\t[--msl-view-index-from-device-index]:\n\t\tTreat the view index as the device index instead.\n" + "\t\tFor multi-GPU rendering.\n" + "\t[--msl-dispatch-base]:\n\t\tAdd support for vkCmdDispatchBase() or similar APIs.\n" + "\t\tOffsets the workgroup ID based on a buffer.\n" + "\t[--msl-dynamic-buffer ]:\n\t\tMarks a buffer as having dynamic offset.\n" + "\t\tThe offset is applied in the shader with pointer arithmetic.\n" + "\t\tUseful for argument buffers where it is non-trivial to apply dynamic offset otherwise.\n" + "\t[--msl-inline-uniform-block ]:\n\t\tIn argument buffers, mark an UBO as being an inline uniform block which is embedded into the argument buffer itself.\n" + "\t[--msl-decoration-binding]:\n\t\tUse SPIR-V bindings directly as MSL bindings.\n" + "\t\tThis does not work in the general case as there is no descriptor set support, and combined image samplers are split up.\n" + "\t\tHowever, if the shader author knows of binding limitations, this option will avoid the need for reflection on Metal side.\n" + "\t[--msl-force-active-argument-buffer-resources]:\n\t\tAlways emit resources which are part of argument buffers.\n" + "\t\tThis makes sure that similar shaders with same resource declarations can share the argument buffer as declaring an argument buffer implies an ABI.\n" + "\t[--msl-force-native-arrays]:\n\t\tRather than implementing array types as a templated value type ala std::array, use plain, native arrays.\n" + "\t\tThis will lead to worse code-gen, but can work around driver bugs on certain driver revisions of certain Intel-based Macbooks where template arrays break.\n" + "\t[--msl-disable-frag-depth-builtin]:\n\t\tDisables FragDepth output. Useful if pipeline does not enable depth, as pipeline creation might otherwise fail.\n" + "\t[--msl-disable-frag-stencil-ref-builtin]:\n\t\tDisable FragStencilRef output. Useful if pipeline does not enable stencil output, as pipeline creation might otherwise fail.\n" + "\t[--msl-enable-frag-output-mask ]:\n\t\tOnly selectively enable fragment outputs. Useful if pipeline does not enable fragment output for certain locations, as pipeline creation might otherwise fail.\n" + "\t[--msl-no-clip-distance-user-varying]:\n\t\tDo not emit user varyings to emulate gl_ClipDistance in fragment shaders.\n" + "\t[--msl-shader-input ]:\n\t\tSpecify the format of the shader input at .\n" + "\t\t can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, " + "or other-typed variable. is the vector length of the variable, which must be greater than or equal to that declared in the shader.\n" + "\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n" + "\t[--msl-multi-patch-workgroup]:\n\t\tUse the new style of tessellation control processing, where multiple patches are processed per workgroup.\n" + "\t\tThis should increase throughput by ensuring all the GPU's SIMD lanes are occupied, but it is not compatible with the old style.\n" + "\t\tIn addition, this style also passes input variables in buffers directly instead of using vertex attribute processing.\n" + "\t\tIn a future version of SPIRV-Cross, this will become the default.\n" + "\t[--msl-vertex-for-tessellation]:\n\t\tWhen handling a vertex shader, marks it as one that will be used with a new-style tessellation control shader.\n" + "\t\tThe vertex shader is output to MSL as a compute kernel which outputs vertices to the buffer in the order they are received, rather than in index order as with --msl-capture-output normally.\n" + "\t[--msl-additional-fixed-sample-mask ]:\n" + "\t\tSet an additional fixed sample mask. If the shader outputs a sample mask, then the final sample mask will be a bitwise AND of the two.\n" + "\t[--msl-arrayed-subpass-input]:\n\t\tAssume that images of dimension SubpassData have multiple layers. Layered input attachments are accessed relative to BuiltInLayer.\n" + "\t\tThis option has no effect if multiview is also enabled.\n" + "\t[--msl-r32ui-linear-texture-align ]:\n\t\tThe required alignment of linear textures of format MTLPixelFormatR32Uint.\n" + "\t\tThis is used to align the row stride for atomic accesses to such images.\n" + "\t[--msl-r32ui-linear-texture-align-constant-id ]:\n\t\tThe function constant ID to use for the linear texture alignment.\n" + "\t\tOn MSL 1.2 or later, you can override the alignment by setting this function constant.\n" + "\t[--msl-texture-1d-as-2d]:\n\t\tEmit Image variables of dimension Dim1D as texture2d.\n" + "\t\tIn Metal, 1D textures do not support all features that 2D textures do. Use this option if your code relies on these features.\n"); + // clang-format on +} + +static void print_help_common() +{ + // clang-format off + fprintf(stderr, "\nCommon options:\n" + "\t[--entry name]:\n\t\tUse a specific entry point. By default, the first entry point in the module is used.\n" + "\t[--stage ]:\n\t\tForces use of a certain shader stage.\n" + "\t\tCan disambiguate the entry point if more than one entry point exists with same name, but different stage.\n" + "\t[--emit-line-directives]:\n\t\tIf SPIR-V has OpLine directives, aim to emit those accurately in output code as well.\n" + "\t[--rename-entry-point ]:\n\t\tRenames an entry point from what is declared in SPIR-V to code output.\n" + "\t\tMostly relevant for HLSL or MSL.\n" + "\t[--rename-interface-variable ]:\n\t\tRename an interface variable based on location decoration.\n" + "\t[--force-zero-initialized-variables]:\n\t\tForces temporary variables to be initialized to zero.\n" + "\t\tCan be useful in environments where compilers do not allow potentially uninitialized variables.\n" + "\t\tThis usually comes up with Phi temporaries.\n" + "\t[--fixup-clipspace]:\n\t\tFixup Z clip-space at the end of a vertex shader. The behavior is backend-dependent.\n" + "\t\tGLSL: Rewrites [0, w] Z range (D3D/Metal/Vulkan) to GL-style [-w, w].\n" + "\t\tHLSL/MSL: Rewrites [-w, w] Z range (GL) to D3D/Metal/Vulkan-style [0, w].\n" + "\t[--flip-vert-y]:\n\t\tInverts gl_Position.y (or equivalent) at the end of a vertex shader. This is equivalent to using negative viewport height.\n" + ); + // clang-format on +} + +static void print_help_obscure() +{ + // clang-format off + fprintf(stderr, "\nObscure options:\n" + "\tThese options are not meant to be used on a regular basis. They have some occasional uses in the test suite.\n" + + "\t[--force-temporary]:\n\t\tAggressively emit temporary expressions instead of forwarding expressions. Very rarely used and under-tested.\n" + "\t[--revision]:\n\t\tPrints build timestamp and Git commit information (updated when cmake is configured).\n" + "\t[--iterations iter]:\n\t\tRecompiles the same shader over and over, benchmarking related.\n" + "\t[--disable-storage-image-qualifier-deduction]:\n\t\tIf storage images are received without any nonwritable or nonreadable information,\n""" + "\t\tdo not attempt to analyze usage, and always emit read/write state.\n" + "\t[--flatten-multidimensional-arrays]:\n\t\tDo not support multi-dimensional arrays and flatten them to one dimension.\n" + "\t[--cpp-interface-name ]:\n\t\tEmit a specific class name in C++ codegen.\n" + ); + // clang-format on +} + +static void print_help() +{ + print_version(); + + // clang-format off + fprintf(stderr, "Usage: spirv-cross <...>\n" + "\nBasic:\n" + "\t[SPIR-V file]\n" + "\t[--output ]: If not provided, prints output to stdout.\n" + "\t[--dump-resources]:\n\t\tPrints a basic reflection of the SPIR-V module along with other output.\n" + "\t[--help]:\n\t\tPrints this help message.\n" + ); + // clang-format on + + print_help_backend(); + print_help_common(); + print_help_glsl(); + print_help_msl(); + print_help_hlsl(); + print_help_obscure(); +} + +static bool remap_generic(Compiler &compiler, const SmallVector &resources, const Remap &remap) +{ + auto itr = + find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; }); + + if (itr != end(resources)) + { + compiler.set_remapped_variable_state(itr->id, true); + compiler.set_name(itr->id, remap.dst_name); + compiler.set_subpass_input_remapped_components(itr->id, remap.components); + return true; + } + else + return false; +} + +static vector remap_pls(const SmallVector &pls_variables, const SmallVector &resources, + const SmallVector *secondary_resources) +{ + vector ret; + + for (auto &pls : pls_variables) + { + bool found = false; + for (auto &res : resources) + { + if (res.name == pls.name) + { + ret.push_back({ res.id, pls.format }); + found = true; + break; + } + } + + if (!found && secondary_resources) + { + for (auto &res : *secondary_resources) + { + if (res.name == pls.name) + { + ret.push_back({ res.id, pls.format }); + found = true; + break; + } + } + } + + if (!found) + fprintf(stderr, "Did not find stage input/output/target with name \"%s\".\n", pls.name.c_str()); + } + + return ret; +} + +static PlsFormat pls_format(const char *str) +{ + if (!strcmp(str, "r11f_g11f_b10f")) + return PlsR11FG11FB10F; + else if (!strcmp(str, "r32f")) + return PlsR32F; + else if (!strcmp(str, "rg16f")) + return PlsRG16F; + else if (!strcmp(str, "rg16")) + return PlsRG16; + else if (!strcmp(str, "rgb10_a2")) + return PlsRGB10A2; + else if (!strcmp(str, "rgba8")) + return PlsRGBA8; + else if (!strcmp(str, "rgba8i")) + return PlsRGBA8I; + else if (!strcmp(str, "rgba8ui")) + return PlsRGBA8UI; + else if (!strcmp(str, "rg16i")) + return PlsRG16I; + else if (!strcmp(str, "rgb10_a2ui")) + return PlsRGB10A2UI; + else if (!strcmp(str, "rg16ui")) + return PlsRG16UI; + else if (!strcmp(str, "r32ui")) + return PlsR32UI; + else + return PlsNone; +} + +static ExecutionModel stage_to_execution_model(const std::string &stage) +{ + if (stage == "vert") + return ExecutionModelVertex; + else if (stage == "frag") + return ExecutionModelFragment; + else if (stage == "comp") + return ExecutionModelGLCompute; + else if (stage == "tesc") + return ExecutionModelTessellationControl; + else if (stage == "tese") + return ExecutionModelTessellationEvaluation; + else if (stage == "geom") + return ExecutionModelGeometry; + else + SPIRV_CROSS_THROW("Invalid stage."); +} + +static HLSLBindingFlags hlsl_resource_type_to_flag(const std::string &arg) +{ + if (arg == "push") + return HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT; + else if (arg == "cbv") + return HLSL_BINDING_AUTO_CBV_BIT; + else if (arg == "srv") + return HLSL_BINDING_AUTO_SRV_BIT; + else if (arg == "uav") + return HLSL_BINDING_AUTO_UAV_BIT; + else if (arg == "sampler") + return HLSL_BINDING_AUTO_SAMPLER_BIT; + else if (arg == "all") + return HLSL_BINDING_AUTO_ALL; + else + { + fprintf(stderr, "Invalid resource type for --hlsl-auto-binding: %s\n", arg.c_str()); + return 0; + } +} + +static string compile_iteration(const CLIArguments &args, std::vector spirv_file) +{ + Parser spirv_parser(move(spirv_file)); + spirv_parser.parse(); + + unique_ptr compiler; + bool combined_image_samplers = false; + bool build_dummy_sampler = false; + + if (args.cpp) + { + compiler.reset(new CompilerCPP(move(spirv_parser.get_parsed_ir()))); + if (args.cpp_interface_name) + static_cast(compiler.get())->set_interface_name(args.cpp_interface_name); + } + else if (args.msl) + { + compiler.reset(new CompilerMSL(move(spirv_parser.get_parsed_ir()))); + + auto *msl_comp = static_cast(compiler.get()); + auto msl_opts = msl_comp->get_msl_options(); + if (args.set_msl_version) + msl_opts.msl_version = args.msl_version; + msl_opts.capture_output_to_buffer = args.msl_capture_output_to_buffer; + msl_opts.swizzle_texture_samples = args.msl_swizzle_texture_samples; + msl_opts.invariant_float_math = args.msl_invariant_float_math; + if (args.msl_ios) + { + msl_opts.platform = CompilerMSL::Options::iOS; + msl_opts.emulate_cube_array = args.msl_emulate_cube_array; + } + msl_opts.use_framebuffer_fetch_subpasses = args.msl_framebuffer_fetch; + msl_opts.pad_fragment_output_components = args.msl_pad_fragment_output; + msl_opts.tess_domain_origin_lower_left = args.msl_domain_lower_left; + msl_opts.argument_buffers = args.msl_argument_buffers; + msl_opts.texture_buffer_native = args.msl_texture_buffer_native; + msl_opts.multiview = args.msl_multiview; + msl_opts.multiview_layered_rendering = args.msl_multiview_layered_rendering; + msl_opts.view_index_from_device_index = args.msl_view_index_from_device_index; + msl_opts.dispatch_base = args.msl_dispatch_base; + msl_opts.enable_decoration_binding = args.msl_decoration_binding; + msl_opts.force_active_argument_buffer_resources = args.msl_force_active_argument_buffer_resources; + msl_opts.force_native_arrays = args.msl_force_native_arrays; + msl_opts.enable_frag_depth_builtin = args.msl_enable_frag_depth_builtin; + msl_opts.enable_frag_stencil_ref_builtin = args.msl_enable_frag_stencil_ref_builtin; + msl_opts.enable_frag_output_mask = args.msl_enable_frag_output_mask; + msl_opts.enable_clip_distance_user_varying = args.msl_enable_clip_distance_user_varying; + msl_opts.multi_patch_workgroup = args.msl_multi_patch_workgroup; + msl_opts.vertex_for_tessellation = args.msl_vertex_for_tessellation; + msl_opts.additional_fixed_sample_mask = args.msl_additional_fixed_sample_mask; + msl_opts.arrayed_subpass_input = args.msl_arrayed_subpass_input; + msl_opts.r32ui_linear_texture_alignment = args.msl_r32ui_linear_texture_alignment; + msl_opts.r32ui_alignment_constant_id = args.msl_r32ui_alignment_constant_id; + msl_opts.texture_1D_as_2D = args.msl_texture_1d_as_2d; + msl_comp->set_msl_options(msl_opts); + for (auto &v : args.msl_discrete_descriptor_sets) + msl_comp->add_discrete_descriptor_set(v); + for (auto &v : args.msl_device_argument_buffers) + msl_comp->set_argument_buffer_device_address_space(v, true); + uint32_t i = 0; + for (auto &v : args.msl_dynamic_buffers) + msl_comp->add_dynamic_buffer(v.first, v.second, i++); + for (auto &v : args.msl_inline_uniform_blocks) + msl_comp->add_inline_uniform_block(v.first, v.second); + for (auto &v : args.msl_shader_inputs) + msl_comp->add_msl_shader_input(v); + } + else if (args.hlsl) + compiler.reset(new CompilerHLSL(move(spirv_parser.get_parsed_ir()))); + else + { + combined_image_samplers = !args.vulkan_semantics; + if (!args.vulkan_semantics || args.vulkan_glsl_disable_ext_samplerless_texture_functions) + build_dummy_sampler = true; + compiler.reset(new CompilerGLSL(move(spirv_parser.get_parsed_ir()))); + } + + if (!args.variable_type_remaps.empty()) + { + auto remap_cb = [&](const SPIRType &, const string &name, string &out) -> void { + for (const VariableTypeRemap &remap : args.variable_type_remaps) + if (name == remap.variable_name) + out = remap.new_variable_type; + }; + + compiler->set_variable_type_remap_callback(move(remap_cb)); + } + + for (auto &rename : args.entry_point_rename) + compiler->rename_entry_point(rename.old_name, rename.new_name, rename.execution_model); + + auto entry_points = compiler->get_entry_points_and_stages(); + auto entry_point = args.entry; + ExecutionModel model = ExecutionModelMax; + + if (!args.entry_stage.empty()) + { + model = stage_to_execution_model(args.entry_stage); + if (entry_point.empty()) + { + // Just use the first entry point with this stage. + for (auto &e : entry_points) + { + if (e.execution_model == model) + { + entry_point = e.name; + break; + } + } + + if (entry_point.empty()) + { + fprintf(stderr, "Could not find an entry point with stage: %s\n", args.entry_stage.c_str()); + exit(EXIT_FAILURE); + } + } + else + { + // Make sure both stage and name exists. + bool exists = false; + for (auto &e : entry_points) + { + if (e.execution_model == model && e.name == entry_point) + { + exists = true; + break; + } + } + + if (!exists) + { + fprintf(stderr, "Could not find an entry point %s with stage: %s\n", entry_point.c_str(), + args.entry_stage.c_str()); + exit(EXIT_FAILURE); + } + } + } + else if (!entry_point.empty()) + { + // Make sure there is just one entry point with this name, or the stage + // is ambiguous. + uint32_t stage_count = 0; + for (auto &e : entry_points) + { + if (e.name == entry_point) + { + stage_count++; + model = e.execution_model; + } + } + + if (stage_count == 0) + { + fprintf(stderr, "There is no entry point with name: %s\n", entry_point.c_str()); + exit(EXIT_FAILURE); + } + else if (stage_count > 1) + { + fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", entry_point.c_str()); + exit(EXIT_FAILURE); + } + } + + if (!entry_point.empty()) + compiler->set_entry_point(entry_point, model); + + if (!args.set_version && !compiler->get_common_options().version) + { + fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n"); + print_help(); + exit(EXIT_FAILURE); + } + + CompilerGLSL::Options opts = compiler->get_common_options(); + if (args.set_version) + opts.version = args.version; + if (args.set_es) + opts.es = args.es; + opts.force_temporary = args.force_temporary; + opts.separate_shader_objects = args.sso; + opts.flatten_multidimensional_arrays = args.flatten_multidimensional_arrays; + opts.enable_420pack_extension = args.use_420pack_extension; + opts.vulkan_semantics = args.vulkan_semantics; + opts.vertex.fixup_clipspace = args.fixup; + opts.vertex.flip_vert_y = args.yflip; + opts.vertex.support_nonzero_base_instance = args.support_nonzero_baseinstance; + opts.emit_push_constant_as_uniform_buffer = args.glsl_emit_push_constant_as_ubo; + opts.emit_uniform_buffer_as_plain_uniforms = args.glsl_emit_ubo_as_plain_uniforms; + opts.force_flattened_io_blocks = args.glsl_force_flattened_io_blocks; + opts.emit_line_directives = args.emit_line_directives; + opts.enable_storage_image_qualifier_deduction = args.enable_storage_image_qualifier_deduction; + opts.force_zero_initialized_variables = args.force_zero_initialized_variables; + compiler->set_common_options(opts); + + for (auto &fetch : args.glsl_ext_framebuffer_fetch) + compiler->remap_ext_framebuffer_fetch(fetch.first, fetch.second); + + // Set HLSL specific options. + if (args.hlsl) + { + auto *hlsl = static_cast(compiler.get()); + auto hlsl_opts = hlsl->get_hlsl_options(); + if (args.set_shader_model) + { + if (args.shader_model < 30) + { + fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n"); + exit(EXIT_FAILURE); + } + + hlsl_opts.shader_model = args.shader_model; + } + + if (args.hlsl_compat) + { + // Enable all compat options. + hlsl_opts.point_size_compat = true; + hlsl_opts.point_coord_compat = true; + } + + if (hlsl_opts.shader_model <= 30) + { + combined_image_samplers = true; + build_dummy_sampler = true; + } + + hlsl_opts.support_nonzero_base_vertex_base_instance = args.hlsl_support_nonzero_base; + hlsl_opts.force_storage_buffer_as_uav = args.hlsl_force_storage_buffer_as_uav; + hlsl_opts.nonwritable_uav_texture_as_srv = args.hlsl_nonwritable_uav_texture_as_srv; + hlsl_opts.enable_16bit_types = args.hlsl_enable_16bit_types; + hlsl_opts.flatten_matrix_vertex_input_semantics = args.hlsl_flatten_matrix_vertex_input_semantics; + hlsl->set_hlsl_options(hlsl_opts); + hlsl->set_resource_binding_flags(args.hlsl_binding_flags); + } + + if (build_dummy_sampler) + { + uint32_t sampler = compiler->build_dummy_sampler_for_combined_images(); + if (sampler != 0) + { + // Set some defaults to make validation happy. + compiler->set_decoration(sampler, DecorationDescriptorSet, 0); + compiler->set_decoration(sampler, DecorationBinding, 0); + } + } + + ShaderResources res; + if (args.remove_unused) + { + auto active = compiler->get_active_interface_variables(); + res = compiler->get_shader_resources(active); + compiler->set_enabled_interface_variables(move(active)); + } + else + res = compiler->get_shader_resources(); + + if (args.flatten_ubo) + { + for (auto &ubo : res.uniform_buffers) + compiler->flatten_buffer_block(ubo.id); + for (auto &ubo : res.push_constant_buffers) + compiler->flatten_buffer_block(ubo.id); + } + + auto pls_inputs = remap_pls(args.pls_in, res.stage_inputs, &res.subpass_inputs); + auto pls_outputs = remap_pls(args.pls_out, res.stage_outputs, nullptr); + compiler->remap_pixel_local_storage(move(pls_inputs), move(pls_outputs)); + + for (auto &ext : args.extensions) + compiler->require_extension(ext); + + for (auto &remap : args.remaps) + { + if (remap_generic(*compiler, res.stage_inputs, remap)) + continue; + if (remap_generic(*compiler, res.stage_outputs, remap)) + continue; + if (remap_generic(*compiler, res.subpass_inputs, remap)) + continue; + } + + for (auto &rename : args.interface_variable_renames) + { + if (rename.storageClass == StorageClassInput) + spirv_cross_util::rename_interface_variable(*compiler, res.stage_inputs, rename.location, + rename.variable_name); + else if (rename.storageClass == StorageClassOutput) + spirv_cross_util::rename_interface_variable(*compiler, res.stage_outputs, rename.location, + rename.variable_name); + else + { + fprintf(stderr, "error at --rename-interface-variable ...\n"); + exit(EXIT_FAILURE); + } + } + + if (combined_image_samplers) + { + compiler->build_combined_image_samplers(); + if (args.combined_samplers_inherit_bindings) + spirv_cross_util::inherit_combined_sampler_bindings(*compiler); + + // Give the remapped combined samplers new names. + for (auto &remap : compiler->get_combined_image_samplers()) + { + compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id), + compiler->get_name(remap.sampler_id))); + } + } + + if (args.hlsl) + { + auto *hlsl_compiler = static_cast(compiler.get()); + uint32_t new_builtin = hlsl_compiler->remap_num_workgroups_builtin(); + if (new_builtin) + { + hlsl_compiler->set_decoration(new_builtin, DecorationDescriptorSet, 0); + hlsl_compiler->set_decoration(new_builtin, DecorationBinding, 0); + } + } + + if (args.hlsl) + { + for (auto &remap : args.hlsl_attr_remap) + static_cast(compiler.get())->add_vertex_attribute_remap(remap); + } + + auto ret = compiler->compile(); + + if (args.dump_resources) + { + print_resources(*compiler, res); + print_push_constant_resources(*compiler, res.push_constant_buffers); + print_spec_constants(*compiler); + print_capabilities_and_extensions(*compiler); + } + + return ret; +} + +static int main_inner(int argc, char *argv[]) +{ + CLIArguments args; + CLICallbacks cbs; + + cbs.add("--help", [](CLIParser &parser) { + print_help(); + parser.end(); + }); + cbs.add("--revision", [](CLIParser &parser) { + print_version(); + parser.end(); + }); + cbs.add("--output", [&args](CLIParser &parser) { args.output = parser.next_string(); }); + cbs.add("--es", [&args](CLIParser &) { + args.es = true; + args.set_es = true; + }); + cbs.add("--no-es", [&args](CLIParser &) { + args.es = false; + args.set_es = true; + }); + cbs.add("--version", [&args](CLIParser &parser) { + args.version = parser.next_uint(); + args.set_version = true; + }); + cbs.add("--dump-resources", [&args](CLIParser &) { args.dump_resources = true; }); + cbs.add("--force-temporary", [&args](CLIParser &) { args.force_temporary = true; }); + cbs.add("--flatten-ubo", [&args](CLIParser &) { args.flatten_ubo = true; }); + cbs.add("--fixup-clipspace", [&args](CLIParser &) { args.fixup = true; }); + cbs.add("--flip-vert-y", [&args](CLIParser &) { args.yflip = true; }); + cbs.add("--iterations", [&args](CLIParser &parser) { args.iterations = parser.next_uint(); }); + cbs.add("--cpp", [&args](CLIParser &) { args.cpp = true; }); + cbs.add("--reflect", [&args](CLIParser &parser) { args.reflect = parser.next_value_string("json"); }); + cbs.add("--cpp-interface-name", [&args](CLIParser &parser) { args.cpp_interface_name = parser.next_string(); }); + cbs.add("--metal", [&args](CLIParser &) { args.msl = true; }); // Legacy compatibility + cbs.add("--glsl-emit-push-constant-as-ubo", [&args](CLIParser &) { args.glsl_emit_push_constant_as_ubo = true; }); + cbs.add("--glsl-emit-ubo-as-plain-uniforms", [&args](CLIParser &) { args.glsl_emit_ubo_as_plain_uniforms = true; }); + cbs.add("--glsl-force-flattened-io-blocks", [&args](CLIParser &) { args.glsl_force_flattened_io_blocks = true; }); + cbs.add("--glsl-remap-ext-framebuffer-fetch", [&args](CLIParser &parser) { + uint32_t input_index = parser.next_uint(); + uint32_t color_attachment = parser.next_uint(); + args.glsl_ext_framebuffer_fetch.push_back({ input_index, color_attachment }); + }); + cbs.add("--vulkan-glsl-disable-ext-samplerless-texture-functions", + [&args](CLIParser &) { args.vulkan_glsl_disable_ext_samplerless_texture_functions = true; }); + cbs.add("--disable-storage-image-qualifier-deduction", + [&args](CLIParser &) { args.enable_storage_image_qualifier_deduction = false; }); + cbs.add("--force-zero-initialized-variables", + [&args](CLIParser &) { args.force_zero_initialized_variables = true; }); + cbs.add("--msl", [&args](CLIParser &) { args.msl = true; }); + cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; }); + cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; }); + cbs.add("--hlsl-support-nonzero-basevertex-baseinstance", + [&args](CLIParser &) { args.hlsl_support_nonzero_base = true; }); + cbs.add("--hlsl-auto-binding", [&args](CLIParser &parser) { + args.hlsl_binding_flags |= hlsl_resource_type_to_flag(parser.next_string()); + }); + cbs.add("--hlsl-force-storage-buffer-as-uav", + [&args](CLIParser &) { args.hlsl_force_storage_buffer_as_uav = true; }); + cbs.add("--hlsl-nonwritable-uav-texture-as-srv", + [&args](CLIParser &) { args.hlsl_nonwritable_uav_texture_as_srv = true; }); + cbs.add("--hlsl-enable-16bit-types", [&args](CLIParser &) { args.hlsl_enable_16bit_types = true; }); + cbs.add("--hlsl-flatten-matrix-vertex-input-semantics", + [&args](CLIParser &) { args.hlsl_flatten_matrix_vertex_input_semantics = true; }); + cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; }); + cbs.add("-V", [&args](CLIParser &) { args.vulkan_semantics = true; }); + cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; }); + cbs.add("--no-420pack-extension", [&args](CLIParser &) { args.use_420pack_extension = false; }); + cbs.add("--msl-capture-output", [&args](CLIParser &) { args.msl_capture_output_to_buffer = true; }); + cbs.add("--msl-swizzle-texture-samples", [&args](CLIParser &) { args.msl_swizzle_texture_samples = true; }); + cbs.add("--msl-ios", [&args](CLIParser &) { args.msl_ios = true; }); + cbs.add("--msl-pad-fragment-output", [&args](CLIParser &) { args.msl_pad_fragment_output = true; }); + cbs.add("--msl-domain-lower-left", [&args](CLIParser &) { args.msl_domain_lower_left = true; }); + cbs.add("--msl-argument-buffers", [&args](CLIParser &) { args.msl_argument_buffers = true; }); + cbs.add("--msl-discrete-descriptor-set", + [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); }); + cbs.add("--msl-device-argument-buffer", + [&args](CLIParser &parser) { args.msl_device_argument_buffers.push_back(parser.next_uint()); }); + cbs.add("--msl-texture-buffer-native", [&args](CLIParser &) { args.msl_texture_buffer_native = true; }); + cbs.add("--msl-framebuffer-fetch", [&args](CLIParser &) { args.msl_framebuffer_fetch = true; }); + cbs.add("--msl-invariant-float-math", [&args](CLIParser &) { args.msl_invariant_float_math = true; }); + cbs.add("--msl-emulate-cube-array", [&args](CLIParser &) { args.msl_emulate_cube_array = true; }); + cbs.add("--msl-multiview", [&args](CLIParser &) { args.msl_multiview = true; }); + cbs.add("--msl-multiview-no-layered-rendering", + [&args](CLIParser &) { args.msl_multiview_layered_rendering = false; }); + cbs.add("--msl-view-index-from-device-index", + [&args](CLIParser &) { args.msl_view_index_from_device_index = true; }); + cbs.add("--msl-dispatch-base", [&args](CLIParser &) { args.msl_dispatch_base = true; }); + cbs.add("--msl-dynamic-buffer", [&args](CLIParser &parser) { + args.msl_argument_buffers = true; + // Make sure next_uint() is called in-order. + uint32_t desc_set = parser.next_uint(); + uint32_t binding = parser.next_uint(); + args.msl_dynamic_buffers.push_back(make_pair(desc_set, binding)); + }); + cbs.add("--msl-decoration-binding", [&args](CLIParser &) { args.msl_decoration_binding = true; }); + cbs.add("--msl-force-active-argument-buffer-resources", + [&args](CLIParser &) { args.msl_force_active_argument_buffer_resources = true; }); + cbs.add("--msl-inline-uniform-block", [&args](CLIParser &parser) { + args.msl_argument_buffers = true; + // Make sure next_uint() is called in-order. + uint32_t desc_set = parser.next_uint(); + uint32_t binding = parser.next_uint(); + args.msl_inline_uniform_blocks.push_back(make_pair(desc_set, binding)); + }); + cbs.add("--msl-force-native-arrays", [&args](CLIParser &) { args.msl_force_native_arrays = true; }); + cbs.add("--msl-disable-frag-depth-builtin", [&args](CLIParser &) { args.msl_enable_frag_depth_builtin = false; }); + cbs.add("--msl-disable-frag-stencil-ref-builtin", + [&args](CLIParser &) { args.msl_enable_frag_stencil_ref_builtin = false; }); + cbs.add("--msl-enable-frag-output-mask", + [&args](CLIParser &parser) { args.msl_enable_frag_output_mask = parser.next_hex_uint(); }); + cbs.add("--msl-no-clip-distance-user-varying", + [&args](CLIParser &) { args.msl_enable_clip_distance_user_varying = false; }); + cbs.add("--msl-shader-input", [&args](CLIParser &parser) { + MSLShaderInput input; + // Make sure next_uint() is called in-order. + input.location = parser.next_uint(); + const char *format = parser.next_value_string("other"); + if (strcmp(format, "any32") == 0) + input.format = MSL_SHADER_INPUT_FORMAT_ANY32; + else if (strcmp(format, "any16") == 0) + input.format = MSL_SHADER_INPUT_FORMAT_ANY16; + else if (strcmp(format, "u16") == 0) + input.format = MSL_SHADER_INPUT_FORMAT_UINT16; + else if (strcmp(format, "u8") == 0) + input.format = MSL_SHADER_INPUT_FORMAT_UINT8; + else + input.format = MSL_SHADER_INPUT_FORMAT_OTHER; + input.vecsize = parser.next_uint(); + args.msl_shader_inputs.push_back(input); + }); + cbs.add("--msl-multi-patch-workgroup", [&args](CLIParser &) { args.msl_multi_patch_workgroup = true; }); + cbs.add("--msl-vertex-for-tessellation", [&args](CLIParser &) { args.msl_vertex_for_tessellation = true; }); + cbs.add("--msl-additional-fixed-sample-mask", + [&args](CLIParser &parser) { args.msl_additional_fixed_sample_mask = parser.next_hex_uint(); }); + cbs.add("--msl-arrayed-subpass-input", [&args](CLIParser &) { args.msl_arrayed_subpass_input = true; }); + cbs.add("--msl-r32ui-linear-texture-align", + [&args](CLIParser &parser) { args.msl_r32ui_linear_texture_alignment = parser.next_uint(); }); + cbs.add("--msl-r32ui-linear-texture-align-constant-id", + [&args](CLIParser &parser) { args.msl_r32ui_alignment_constant_id = parser.next_uint(); }); + cbs.add("--msl-texture-1d-as-2d", [&args](CLIParser &) { args.msl_texture_1d_as_2d = true; }); + cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); }); + cbs.add("--rename-entry-point", [&args](CLIParser &parser) { + auto old_name = parser.next_string(); + auto new_name = parser.next_string(); + auto model = stage_to_execution_model(parser.next_string()); + args.entry_point_rename.push_back({ old_name, new_name, move(model) }); + }); + cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); }); + cbs.add("--stage", [&args](CLIParser &parser) { args.entry_stage = parser.next_string(); }); + cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; }); + cbs.add("--set-hlsl-vertex-input-semantic", [&args](CLIParser &parser) { + HLSLVertexAttributeRemap remap; + remap.location = parser.next_uint(); + remap.semantic = parser.next_string(); + args.hlsl_attr_remap.push_back(move(remap)); + }); + + cbs.add("--remap", [&args](CLIParser &parser) { + string src = parser.next_string(); + string dst = parser.next_string(); + uint32_t components = parser.next_uint(); + args.remaps.push_back({ move(src), move(dst), components }); + }); + + cbs.add("--remap-variable-type", [&args](CLIParser &parser) { + string var_name = parser.next_string(); + string new_type = parser.next_string(); + args.variable_type_remaps.push_back({ move(var_name), move(new_type) }); + }); + + cbs.add("--rename-interface-variable", [&args](CLIParser &parser) { + StorageClass cls = StorageClassMax; + string clsStr = parser.next_string(); + if (clsStr == "in") + cls = StorageClassInput; + else if (clsStr == "out") + cls = StorageClassOutput; + + uint32_t loc = parser.next_uint(); + string var_name = parser.next_string(); + args.interface_variable_renames.push_back({ cls, loc, move(var_name) }); + }); + + cbs.add("--pls-in", [&args](CLIParser &parser) { + auto fmt = pls_format(parser.next_string()); + auto name = parser.next_string(); + args.pls_in.push_back({ move(fmt), move(name) }); + }); + cbs.add("--pls-out", [&args](CLIParser &parser) { + auto fmt = pls_format(parser.next_string()); + auto name = parser.next_string(); + args.pls_out.push_back({ move(fmt), move(name) }); + }); + cbs.add("--shader-model", [&args](CLIParser &parser) { + args.shader_model = parser.next_uint(); + args.set_shader_model = true; + }); + cbs.add("--msl-version", [&args](CLIParser &parser) { + args.msl_version = parser.next_uint(); + args.set_msl_version = true; + }); + + cbs.add("--remove-unused-variables", [&args](CLIParser &) { args.remove_unused = true; }); + cbs.add("--combined-samplers-inherit-bindings", + [&args](CLIParser &) { args.combined_samplers_inherit_bindings = true; }); + + cbs.add("--no-support-nonzero-baseinstance", [&](CLIParser &) { args.support_nonzero_baseinstance = false; }); + cbs.add("--emit-line-directives", [&args](CLIParser &) { args.emit_line_directives = true; }); + + cbs.default_handler = [&args](const char *value) { args.input = value; }; + cbs.error_handler = [] { print_help(); }; + + CLIParser parser{ move(cbs), argc - 1, argv + 1 }; + if (!parser.parse()) + return EXIT_FAILURE; + else if (parser.ended_state) + return EXIT_SUCCESS; + + if (!args.input) + { + fprintf(stderr, "Didn't specify input file.\n"); + print_help(); + return EXIT_FAILURE; + } + + auto spirv_file = read_spirv_file(args.input); + if (spirv_file.empty()) + return EXIT_FAILURE; + + // Special case reflection because it has little to do with the path followed by code-outputting compilers + if (!args.reflect.empty()) + { + Parser spirv_parser(move(spirv_file)); + spirv_parser.parse(); + + CompilerReflection compiler(move(spirv_parser.get_parsed_ir())); + compiler.set_format(args.reflect); + auto json = compiler.compile(); + if (args.output) + write_string_to_file(args.output, json.c_str()); + else + printf("%s", json.c_str()); + return EXIT_SUCCESS; + } + + string compiled_output; + + if (args.iterations == 1) + compiled_output = compile_iteration(args, move(spirv_file)); + else + { + for (unsigned i = 0; i < args.iterations; i++) + compiled_output = compile_iteration(args, spirv_file); + } + + if (args.output) + write_string_to_file(args.output, compiled_output.c_str()); + else + printf("%s", compiled_output.c_str()); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) +{ +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS + return main_inner(argc, argv); +#else + // Make sure we catch the exception or it just disappears into the aether on Windows. + try + { + return main_inner(argc, argv); + } + catch (const std::exception &e) + { + fprintf(stderr, "SPIRV-Cross threw an exception: %s\n", e.what()); + return EXIT_FAILURE; + } +#endif +} diff --git a/third_party/spirv-cross/pkg-config/spirv-cross-c-shared.pc.in b/third_party/spirv-cross/pkg-config/spirv-cross-c-shared.pc.in new file mode 100644 index 0000000..09678c5 --- /dev/null +++ b/third_party/spirv-cross/pkg-config/spirv-cross-c-shared.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +sharedlibdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@/spirv_cross + +Name: spirv-cross-c-shared +Description: C API for SPIRV-Cross +Version: @SPIRV_CROSS_VERSION@ + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lspirv-cross-c-shared +Cflags: -I${includedir} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp new file mode 100644 index 0000000..b8265fc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp @@ -0,0 +1,19 @@ +RWByteAddressBuffer _4 : register(u0); + +void comp_main() +{ + uint _21 = _4.Load(_4.Load(0) * 4 + 4); + for (uint _23 = 0u; _23 < 64u; ) + { + _4.Store(_23 * 4 + 4, 0u); + _23++; + continue; + } + _4.Store(_4.Load(0) * 4 + 4, _21); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..c534cea --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,22 @@ +RWByteAddressBuffer u0_counter : register(u1); +RWBuffer u0 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + uint _29; + u0_counter.InterlockedAdd(0, -1, _29); + u0[asint(asfloat(_29))] = uint(int(gl_GlobalInvocationID.x)).x; +} + +[numthreads(4, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..5e7d282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,22 @@ +RWByteAddressBuffer u0_counter : register(u1); +RWBuffer u0 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + uint _29; + u0_counter.InterlockedAdd(0, 1, _29); + u0[asint(asfloat(_29))] = uint(int(gl_GlobalInvocationID.x)).x; +} + +[numthreads(4, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..35143a4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,28 @@ +RWByteAddressBuffer _5 : register(u0); +RWByteAddressBuffer _6 : register(u1); + +void comp_main() +{ + bool4 _31 = bool4(int(_5.Load4(16).x) < int4(_5.Load4(0)).x, int(_5.Load4(16).y) < int4(_5.Load4(0)).y, int(_5.Load4(16).z) < int4(_5.Load4(0)).z, int(_5.Load4(16).w) < int4(_5.Load4(0)).w); + bool4 _32 = bool4(int(_5.Load4(16).x) <= int4(_5.Load4(0)).x, int(_5.Load4(16).y) <= int4(_5.Load4(0)).y, int(_5.Load4(16).z) <= int4(_5.Load4(0)).z, int(_5.Load4(16).w) <= int4(_5.Load4(0)).w); + bool4 _33 = bool4(_5.Load4(16).x < uint(int4(_5.Load4(0)).x), _5.Load4(16).y < uint(int4(_5.Load4(0)).y), _5.Load4(16).z < uint(int4(_5.Load4(0)).z), _5.Load4(16).w < uint(int4(_5.Load4(0)).w)); + bool4 _34 = bool4(_5.Load4(16).x <= uint(int4(_5.Load4(0)).x), _5.Load4(16).y <= uint(int4(_5.Load4(0)).y), _5.Load4(16).z <= uint(int4(_5.Load4(0)).z), _5.Load4(16).w <= uint(int4(_5.Load4(0)).w)); + bool4 _35 = bool4(int(_5.Load4(16).x) > int4(_5.Load4(0)).x, int(_5.Load4(16).y) > int4(_5.Load4(0)).y, int(_5.Load4(16).z) > int4(_5.Load4(0)).z, int(_5.Load4(16).w) > int4(_5.Load4(0)).w); + bool4 _36 = bool4(int(_5.Load4(16).x) >= int4(_5.Load4(0)).x, int(_5.Load4(16).y) >= int4(_5.Load4(0)).y, int(_5.Load4(16).z) >= int4(_5.Load4(0)).z, int(_5.Load4(16).w) >= int4(_5.Load4(0)).w); + bool4 _37 = bool4(_5.Load4(16).x > uint(int4(_5.Load4(0)).x), _5.Load4(16).y > uint(int4(_5.Load4(0)).y), _5.Load4(16).z > uint(int4(_5.Load4(0)).z), _5.Load4(16).w > uint(int4(_5.Load4(0)).w)); + bool4 _38 = bool4(_5.Load4(16).x >= uint(int4(_5.Load4(0)).x), _5.Load4(16).y >= uint(int4(_5.Load4(0)).y), _5.Load4(16).z >= uint(int4(_5.Load4(0)).z), _5.Load4(16).w >= uint(int4(_5.Load4(0)).w)); + _6.Store4(0, uint4(_31.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _31.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _31.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _31.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_32.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _32.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _32.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _32.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_33.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _33.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _33.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _33.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_34.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _34.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _34.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _34.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_35.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _35.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _35.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _35.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_36.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _36.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _36.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _36.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_37.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _37.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _37.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _37.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_38.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _38.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _38.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _38.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..e184e03 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,39 @@ +struct A +{ + int a; + int b; +}; + +RWByteAddressBuffer C1 : register(u1); +cbuffer C2 : register(b2) +{ + A C2_1_Data[1024] : packoffset(c0); +}; + +RWByteAddressBuffer C3 : register(u0); +cbuffer B : register(b3) +{ + A C4_Data[1024] : packoffset(c0); +}; + + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + C1.Store(gl_GlobalInvocationID.x * 8 + 0, uint(C2_1_Data[gl_GlobalInvocationID.x].a)); + C1.Store(gl_GlobalInvocationID.x * 8 + 4, uint(C2_1_Data[gl_GlobalInvocationID.x].b)); + C3.Store(gl_GlobalInvocationID.x * 8 + 0, uint(C4_Data[gl_GlobalInvocationID.x].a)); + C3.Store(gl_GlobalInvocationID.x * 8 + 4, uint(C4_Data[gl_GlobalInvocationID.x].b)); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000..70df6ba --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,41 @@ +RWByteAddressBuffer bar : register(u0); +RWByteAddressBuffer foo : register(u1); + +void comp_main() +{ + bar.Store4(0, asuint(asfloat(foo.Load4(0)))); + bar.Store4(16, asuint(asfloat(foo.Load4(16)))); + bar.Store4(32, asuint(asfloat(foo.Load4(32)))); + bar.Store4(48, asuint(asfloat(foo.Load4(48)))); + bar.Store4(64, asuint(asfloat(foo.Load4(64)))); + bar.Store4(80, asuint(asfloat(foo.Load4(80)))); + bar.Store4(96, asuint(asfloat(foo.Load4(96)))); + bar.Store4(112, asuint(asfloat(foo.Load4(112)))); + bar.Store4(128, asuint(asfloat(foo.Load4(128)))); + bar.Store4(144, asuint(asfloat(foo.Load4(144)))); + bar.Store4(160, asuint(asfloat(foo.Load4(160)))); + bar.Store4(176, asuint(asfloat(foo.Load4(176)))); + bar.Store4(192, asuint(asfloat(foo.Load4(192)))); + bar.Store4(208, asuint(asfloat(foo.Load4(208)))); + bar.Store4(224, asuint(asfloat(foo.Load4(224)))); + bar.Store4(240, asuint(asfloat(foo.Load4(240)))); + [loop] + for (int _137 = 0; _137 < 16; ) + { + bar.Store4((15 - _137) * 16 + 0, asuint(asfloat(foo.Load4(_137 * 16 + 0)))); + _137++; + continue; + } + [branch] + if (asfloat(bar.Load(160)) > 10.0f) + { + foo.Store4(320, asuint(5.0f.xxxx)); + } + foo.Store4(320, asuint(20.0f.xxxx)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..d8bce8d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,9 @@ +void comp_main() +{ +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp new file mode 100644 index 0000000..7acd67d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp @@ -0,0 +1,87 @@ +RWByteAddressBuffer _4 : register(u0); + +void comp_main() +{ + _4.Store(0, asuint(isnan(asfloat(_4.Load(96))) ? asfloat(_4.Load(48)) : (isnan(asfloat(_4.Load(48))) ? asfloat(_4.Load(96)) : min(asfloat(_4.Load(48)), asfloat(_4.Load(96)))))); + bool2 _146 = isnan(asfloat(_4.Load2(56))); + bool2 _147 = isnan(asfloat(_4.Load2(104))); + float2 _148 = min(asfloat(_4.Load2(56)), asfloat(_4.Load2(104))); + float2 _149 = float2(_146.x ? asfloat(_4.Load2(104)).x : _148.x, _146.y ? asfloat(_4.Load2(104)).y : _148.y); + _4.Store2(8, asuint(float2(_147.x ? asfloat(_4.Load2(56)).x : _149.x, _147.y ? asfloat(_4.Load2(56)).y : _149.y))); + bool3 _151 = isnan(asfloat(_4.Load3(64))); + bool3 _152 = isnan(asfloat(_4.Load3(112))); + float3 _153 = min(asfloat(_4.Load3(64)), asfloat(_4.Load3(112))); + float3 _154 = float3(_151.x ? asfloat(_4.Load3(112)).x : _153.x, _151.y ? asfloat(_4.Load3(112)).y : _153.y, _151.z ? asfloat(_4.Load3(112)).z : _153.z); + _4.Store3(16, asuint(float3(_152.x ? asfloat(_4.Load3(64)).x : _154.x, _152.y ? asfloat(_4.Load3(64)).y : _154.y, _152.z ? asfloat(_4.Load3(64)).z : _154.z))); + bool4 _156 = isnan(asfloat(_4.Load4(80))); + bool4 _157 = isnan(asfloat(_4.Load4(128))); + float4 _158 = min(asfloat(_4.Load4(80)), asfloat(_4.Load4(128))); + float4 _159 = float4(_156.x ? asfloat(_4.Load4(128)).x : _158.x, _156.y ? asfloat(_4.Load4(128)).y : _158.y, _156.z ? asfloat(_4.Load4(128)).z : _158.z, _156.w ? asfloat(_4.Load4(128)).w : _158.w); + _4.Store4(32, asuint(float4(_157.x ? asfloat(_4.Load4(80)).x : _159.x, _157.y ? asfloat(_4.Load4(80)).y : _159.y, _157.z ? asfloat(_4.Load4(80)).z : _159.z, _157.w ? asfloat(_4.Load4(80)).w : _159.w))); + _4.Store(0, asuint(isnan(asfloat(_4.Load(96))) ? asfloat(_4.Load(48)) : (isnan(asfloat(_4.Load(48))) ? asfloat(_4.Load(96)) : max(asfloat(_4.Load(48)), asfloat(_4.Load(96)))))); + bool2 _166 = isnan(asfloat(_4.Load2(56))); + bool2 _167 = isnan(asfloat(_4.Load2(104))); + float2 _168 = max(asfloat(_4.Load2(56)), asfloat(_4.Load2(104))); + float2 _169 = float2(_166.x ? asfloat(_4.Load2(104)).x : _168.x, _166.y ? asfloat(_4.Load2(104)).y : _168.y); + _4.Store2(8, asuint(float2(_167.x ? asfloat(_4.Load2(56)).x : _169.x, _167.y ? asfloat(_4.Load2(56)).y : _169.y))); + bool3 _171 = isnan(asfloat(_4.Load3(64))); + bool3 _172 = isnan(asfloat(_4.Load3(112))); + float3 _173 = max(asfloat(_4.Load3(64)), asfloat(_4.Load3(112))); + float3 _174 = float3(_171.x ? asfloat(_4.Load3(112)).x : _173.x, _171.y ? asfloat(_4.Load3(112)).y : _173.y, _171.z ? asfloat(_4.Load3(112)).z : _173.z); + _4.Store3(16, asuint(float3(_172.x ? asfloat(_4.Load3(64)).x : _174.x, _172.y ? asfloat(_4.Load3(64)).y : _174.y, _172.z ? asfloat(_4.Load3(64)).z : _174.z))); + bool4 _176 = isnan(asfloat(_4.Load4(80))); + bool4 _177 = isnan(asfloat(_4.Load4(128))); + float4 _178 = max(asfloat(_4.Load4(80)), asfloat(_4.Load4(128))); + float4 _179 = float4(_176.x ? asfloat(_4.Load4(128)).x : _178.x, _176.y ? asfloat(_4.Load4(128)).y : _178.y, _176.z ? asfloat(_4.Load4(128)).z : _178.z, _176.w ? asfloat(_4.Load4(128)).w : _178.w); + _4.Store4(32, asuint(float4(_177.x ? asfloat(_4.Load4(80)).x : _179.x, _177.y ? asfloat(_4.Load4(80)).y : _179.y, _177.z ? asfloat(_4.Load4(80)).z : _179.z, _177.w ? asfloat(_4.Load4(80)).w : _179.w))); + float _180 = isnan(asfloat(_4.Load(48))) ? asfloat(_4.Load(0)) : (isnan(asfloat(_4.Load(0))) ? asfloat(_4.Load(48)) : max(asfloat(_4.Load(0)), asfloat(_4.Load(48)))); + _4.Store(0, asuint(isnan(asfloat(_4.Load(96))) ? _180 : (isnan(_180) ? asfloat(_4.Load(96)) : min(_180, asfloat(_4.Load(96)))))); + bool2 _193 = isnan(asfloat(_4.Load2(8))); + bool2 _194 = isnan(asfloat(_4.Load2(56))); + float2 _195 = max(asfloat(_4.Load2(8)), asfloat(_4.Load2(56))); + float2 _196 = float2(_193.x ? asfloat(_4.Load2(56)).x : _195.x, _193.y ? asfloat(_4.Load2(56)).y : _195.y); + float2 _191 = float2(_194.x ? asfloat(_4.Load2(8)).x : _196.x, _194.y ? asfloat(_4.Load2(8)).y : _196.y); + bool2 _198 = isnan(_191); + bool2 _199 = isnan(asfloat(_4.Load2(104))); + float2 _200 = min(_191, asfloat(_4.Load2(104))); + float2 _201 = float2(_198.x ? asfloat(_4.Load2(104)).x : _200.x, _198.y ? asfloat(_4.Load2(104)).y : _200.y); + _4.Store2(8, asuint(float2(_199.x ? _191.x : _201.x, _199.y ? _191.y : _201.y))); + bool3 _204 = isnan(asfloat(_4.Load3(16))); + bool3 _205 = isnan(asfloat(_4.Load3(64))); + float3 _206 = max(asfloat(_4.Load3(16)), asfloat(_4.Load3(64))); + float3 _207 = float3(_204.x ? asfloat(_4.Load3(64)).x : _206.x, _204.y ? asfloat(_4.Load3(64)).y : _206.y, _204.z ? asfloat(_4.Load3(64)).z : _206.z); + float3 _202 = float3(_205.x ? asfloat(_4.Load3(16)).x : _207.x, _205.y ? asfloat(_4.Load3(16)).y : _207.y, _205.z ? asfloat(_4.Load3(16)).z : _207.z); + bool3 _209 = isnan(_202); + bool3 _210 = isnan(asfloat(_4.Load3(112))); + float3 _211 = min(_202, asfloat(_4.Load3(112))); + float3 _212 = float3(_209.x ? asfloat(_4.Load3(112)).x : _211.x, _209.y ? asfloat(_4.Load3(112)).y : _211.y, _209.z ? asfloat(_4.Load3(112)).z : _211.z); + _4.Store3(16, asuint(float3(_210.x ? _202.x : _212.x, _210.y ? _202.y : _212.y, _210.z ? _202.z : _212.z))); + bool4 _215 = isnan(asfloat(_4.Load4(32))); + bool4 _216 = isnan(asfloat(_4.Load4(80))); + float4 _217 = max(asfloat(_4.Load4(32)), asfloat(_4.Load4(80))); + float4 _218 = float4(_215.x ? asfloat(_4.Load4(80)).x : _217.x, _215.y ? asfloat(_4.Load4(80)).y : _217.y, _215.z ? asfloat(_4.Load4(80)).z : _217.z, _215.w ? asfloat(_4.Load4(80)).w : _217.w); + float4 _213 = float4(_216.x ? asfloat(_4.Load4(32)).x : _218.x, _216.y ? asfloat(_4.Load4(32)).y : _218.y, _216.z ? asfloat(_4.Load4(32)).z : _218.z, _216.w ? asfloat(_4.Load4(32)).w : _218.w); + bool4 _220 = isnan(_213); + bool4 _221 = isnan(asfloat(_4.Load4(128))); + float4 _222 = min(_213, asfloat(_4.Load4(128))); + float4 _223 = float4(_220.x ? asfloat(_4.Load4(128)).x : _222.x, _220.y ? asfloat(_4.Load4(128)).y : _222.y, _220.z ? asfloat(_4.Load4(128)).z : _222.z, _220.w ? asfloat(_4.Load4(128)).w : _222.w); + _4.Store4(32, asuint(float4(_221.x ? _213.x : _223.x, _221.y ? _213.y : _223.y, _221.z ? _213.z : _223.z, _221.w ? _213.w : _223.w))); + for (int _139 = 0; _139 < 2; ) + { + bool2 _225 = isnan(asfloat(_4.Load2(56))); + bool2 _226 = isnan(asfloat(_4.Load2(104))); + float2 _227 = min(asfloat(_4.Load2(56)), asfloat(_4.Load2(104))); + float2 _228 = float2(_225.x ? asfloat(_4.Load2(104)).x : _227.x, _225.y ? asfloat(_4.Load2(104)).y : _227.y); + _4.Store2(8, asuint(float2(_226.x ? asfloat(_4.Load2(56)).x : _228.x, _226.y ? asfloat(_4.Load2(56)).y : _228.y))); + float _229 = isnan(asfloat(_4.Load(56))) ? asfloat(_4.Load(0)) : (isnan(asfloat(_4.Load(0))) ? asfloat(_4.Load(56)) : max(asfloat(_4.Load(0)), asfloat(_4.Load(56)))); + _4.Store(0, asuint(isnan(asfloat(_4.Load(60))) ? _229 : (isnan(_229) ? asfloat(_4.Load(60)) : min(_229, asfloat(_4.Load(60)))))); + _139++; + continue; + } +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag new file mode 100644 index 0000000..b410010 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag @@ -0,0 +1,26 @@ +cbuffer _4_5 : register(b0) +{ + column_major float2x4 _5_m0 : packoffset(c0); + float4 _5_m1 : packoffset(c4); +}; + + +static float2 _3; + +struct SPIRV_Cross_Output +{ + float2 _3 : SV_Target0; +}; + +void frag_main() +{ + _3 = mul(_5_m0, _5_m1); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output._3 = _3; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag new file mode 100644 index 0000000..3951fd5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag @@ -0,0 +1,30 @@ +Texture2D uTex : register(t1); +SamplerState uSampler : register(s0); + +static float4 FragColor; +static float2 vUV; + +struct SPIRV_Cross_Input +{ + float2 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uTex.Sample(uSampler, vUV); + FragColor += uTex.Sample(uSampler, vUV, int2(1, 1)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..3b50282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/empty-struct.asm.frag @@ -0,0 +1,8 @@ +void frag_main() +{ +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/frem.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/frem.asm.frag new file mode 100644 index 0000000..67998c5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/frem.asm.frag @@ -0,0 +1,29 @@ +static float4 FragColor; +static float4 vA; +static float4 vB; + +struct SPIRV_Cross_Input +{ + float4 vA : TEXCOORD0; + float4 vB : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = fmod(vA, vB); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vA = stage_input.vA; + vB = stage_input.vB; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..93f8414 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,19 @@ +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = 10.0f.xxxx; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..ed53720 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,31 @@ +Texture2D uTexture : register(t0); +SamplerState _uTexture_sampler : register(s0); + +static int2 Size; + +struct SPIRV_Cross_Output +{ + int2 Size : SV_Target0; +}; + +uint2 SPIRV_Cross_textureSize(Texture2D Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(Level, ret.x, ret.y, Param); + return ret; +} + +void frag_main() +{ + uint _19_dummy_parameter; + uint _20_dummy_parameter; + Size = int2(SPIRV_Cross_textureSize(uTexture, uint(0), _19_dummy_parameter)) + int2(SPIRV_Cross_textureSize(uTexture, uint(1), _20_dummy_parameter)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.Size = Size; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..67f14fc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,57 @@ +Texture2D uImage : register(t0); +SamplerState _uImage_sampler : register(s0); + +static float4 v0; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 v0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float phi; + float4 _36; + int _51; + _51 = 0; + phi = 1.0f; + _36 = float4(1.0f, 2.0f, 1.0f, 2.0f); + for (;;) + { + FragColor = _36; + if (_51 < 4) + { + if (v0[_51] > 0.0f) + { + float2 _48 = phi.xx; + _51++; + phi += 2.0f; + _36 = uImage.SampleLevel(_uImage_sampler, _48, 0.0f); + continue; + } + else + { + break; + } + } + else + { + break; + } + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + v0 = stage_input.v0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..d20cf99 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,19 @@ +static float3 FragColor; + +struct SPIRV_Cross_Output +{ + float3 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float3(asfloat(0x7f800000u), asfloat(0xff800000u), asfloat(0x7fc00000u)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..bb5ff55 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,86 @@ +static float FragColor; +static float vColor; + +struct SPIRV_Cross_Input +{ + float vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +#line 8 "test.frag" +void frag_main() +{ +#line 8 "test.frag" + FragColor = 1.0f; +#line 9 "test.frag" + FragColor = 2.0f; +#line 10 "test.frag" + if (vColor < 0.0f) + { +#line 12 "test.frag" + FragColor = 3.0f; + } + else + { +#line 16 "test.frag" + FragColor = 4.0f; + } + for (int _127 = 0; float(_127) < (40.0f + vColor); ) + { +#line 21 "test.frag" + FragColor += 0.20000000298023223876953125f; +#line 22 "test.frag" + FragColor += 0.300000011920928955078125f; + _127 += (int(vColor) + 5); + continue; + } + switch (int(vColor)) + { + case 0: + { +#line 28 "test.frag" + FragColor += 0.20000000298023223876953125f; +#line 29 "test.frag" + break; + } + case 1: + { +#line 32 "test.frag" + FragColor += 0.4000000059604644775390625f; +#line 33 "test.frag" + break; + } + default: + { +#line 36 "test.frag" + FragColor += 0.800000011920928955078125f; +#line 37 "test.frag" + break; + } + } + for (;;) + { + FragColor += (10.0f + vColor); +#line 43 "test.frag" + if (FragColor < 100.0f) + { + } + else + { + break; + } + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..5deae3a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,57 @@ +static const float _46[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f }; +static const float4 _76[4] = { 0.0f.xxxx, 1.0f.xxxx, 8.0f.xxxx, 5.0f.xxxx }; +static const float4 _90[4] = { 20.0f.xxxx, 30.0f.xxxx, 50.0f.xxxx, 60.0f.xxxx }; + +static float FragColor; +static int index; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 foobar[4] = _76; + float4 baz[4] = _76; + FragColor = _46[index]; + if (index < 10) + { + FragColor += _46[index ^ 1]; + } + else + { + FragColor += _46[index & 1]; + } + bool _99 = index > 30; + if (_99) + { + FragColor += _76[index & 3].y; + } + else + { + FragColor += _76[index & 1].x; + } + if (_99) + { + foobar[1].z = 20.0f; + } + int _37 = index & 3; + FragColor += foobar[_37].z; + baz = _90; + FragColor += baz[_37].z; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag new file mode 100644 index 0000000..c7534f3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag @@ -0,0 +1,33 @@ +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +uint64_t SPIRV_Cross_packUint2x32(uint2 value) +{ + return (uint64_t(value.y) << 32) | uint64_t(value.x); +} + +uint2 SPIRV_Cross_unpackUint2x32(uint64_t value) +{ + uint2 Unpacked; + Unpacked.x = uint(value & 0xffffffff); + Unpacked.y = uint(value >> 32); + return Unpacked; +} + +void frag_main() +{ + uint2 unpacked = SPIRV_Cross_unpackUint2x32(SPIRV_Cross_packUint2x32(uint2(18u, 52u))); + FragColor = float4(float(unpacked.x), float(unpacked.y), 1.0f, 1.0f); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..56ac1f2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,25 @@ +cbuffer Registers +{ + float registers_foo : packoffset(c0); +}; + + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = 10.0f + registers_foo; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..41dfc08 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,30 @@ +Texture2D g_Texture : register(t0); +SamplerState g_Sampler : register(s0); +SamplerComparisonState g_CompareSampler : register(s1); + +static float2 in_var_TEXCOORD0; +static float out_var_SV_Target; + +struct SPIRV_Cross_Input +{ + float2 in_var_TEXCOORD0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float out_var_SV_Target : SV_Target0; +}; + +void frag_main() +{ + out_var_SV_Target = g_Texture.Sample(g_Sampler, in_var_TEXCOORD0).x + g_Texture.SampleCmpLevelZero(g_CompareSampler, in_var_TEXCOORD0, 0.5f); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + in_var_TEXCOORD0 = stage_input.in_var_TEXCOORD0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.out_var_SV_Target = out_var_SV_Target; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..269cecb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,63 @@ +struct myType +{ + float data; +}; + +static const myType _18 = { 0.0f }; +static const myType _20 = { 1.0f }; +static const myType _21[5] = { { 0.0f }, { 1.0f }, { 0.0f }, { 1.0f }, { 0.0f } }; + +static float4 gl_FragCoord; +static float4 o_color; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 o_color : SV_Target0; +}; + +float mod(float x, float y) +{ + return x - y * floor(x / y); +} + +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x / y); +} + +float3 mod(float3 x, float3 y) +{ + return x - y * floor(x / y); +} + +float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} + +void frag_main() +{ + if (_21[int(mod(gl_FragCoord.x, 4.0f))].data > 0.0f) + { + o_color = float4(0.0f, 1.0f, 0.0f, 1.0f); + } + else + { + o_color = float4(1.0f, 0.0f, 0.0f, 1.0f); + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.o_color = o_color; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/srem.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/srem.asm.frag new file mode 100644 index 0000000..db5e717 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/srem.asm.frag @@ -0,0 +1,29 @@ +static float4 FragColor; +static int4 vA; +static int4 vB; + +struct SPIRV_Cross_Input +{ + nointerpolation int4 vA : TEXCOORD0; + nointerpolation int4 vB : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float4(vA - vB * (vA / vB)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vA = stage_input.vA; + vB = stage_input.vB; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..1905b5e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,23 @@ +static const float4 _20[2] = { float4(1.0f, 2.0f, 3.0f, 4.0f), 10.0f.xxxx }; + +static float4 FragColors[2] = _20; +static float4 FragColor = 5.0f.xxxx; + +struct SPIRV_Cross_Output +{ + float4 FragColors[2] : SV_Target0; + float4 FragColor : SV_Target2; +}; + +void frag_main() +{ +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColors = FragColors; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..74c1294 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,30 @@ +Texture2D uTexture : register(t0); +SamplerState _uTexture_sampler : register(s0); + +static float4 gl_FragCoord; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uTexture.Load(int3(int2(gl_FragCoord.xy), 0)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag new file mode 100644 index 0000000..322c86b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag @@ -0,0 +1,29 @@ +Texture2D uTexture : register(t0); +SamplerState _uTexture_sampler : register(s0); + +static min16float4 FragColor; +static min16float2 UV; + +struct SPIRV_Cross_Input +{ + min16float2 UV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + min16float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = min16float4(uTexture.Sample(_uTexture_sampler, UV)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + UV = stage_input.UV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000..5b894de --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,31 @@ +Texture2D uShadow : register(t0); +SamplerComparisonState _uShadow_sampler : register(s0); +Texture2D uTexture : register(t1); +SamplerComparisonState uSampler : register(s2); + +static float3 vUV; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float3 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uShadow.SampleCmp(_uShadow_sampler, vUV.xy, vUV.z) + uTexture.SampleCmp(uSampler, vUV.xy, vUV.z); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..4a024c8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/frag/unreachable.asm.frag @@ -0,0 +1,40 @@ +static int counter; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + nointerpolation int counter : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 _33; + for (;;) + { + if (counter == 10) + { + _33 = 10.0f.xxxx; + break; + } + else + { + _33 = 30.0f.xxxx; + break; + } + } + FragColor = _33; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + counter = stage_input.counter; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..54cfec9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,45 @@ +struct InstanceData +{ + column_major float4x4 MATRIX_MVP; + float4 Color; +}; + +cbuffer gInstanceData : register(b0) +{ + InstanceData gInstanceData_1_data[32] : packoffset(c0); +}; + + +static float4 gl_Position; +static int gl_InstanceIndex; +static float3 PosL; +static float4 _entryPointOutput_Color; + +struct SPIRV_Cross_Input +{ + float3 PosL : TEXCOORD0; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 _entryPointOutput_Color : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = mul(float4(PosL, 1.0f), gInstanceData_1_data[uint(gl_InstanceIndex)].MATRIX_MVP); + _entryPointOutput_Color = gInstanceData_1_data[uint(gl_InstanceIndex)].Color; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + PosL = stage_input.PosL; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output._entryPointOutput_Color = _entryPointOutput_Color; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert new file mode 100644 index 0000000..2cebfff --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert @@ -0,0 +1,46 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_201 +#define SPIRV_CROSS_CONSTANT_ID_201 -10 +#endif +static const int _7 = SPIRV_CROSS_CONSTANT_ID_201; +static const int _20 = (_7 + 2); +#ifndef SPIRV_CROSS_CONSTANT_ID_202 +#define SPIRV_CROSS_CONSTANT_ID_202 100u +#endif +static const uint _8 = SPIRV_CROSS_CONSTANT_ID_202; +static const uint _25 = (_8 % 5u); +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 int4(20, 30, _20, _20) +#endif +static const int4 _30 = SPIRV_CROSS_CONSTANT_ID_0; +static const int2 _32 = int2(_30.y, _30.x); +static const int _33 = _30.y; + +static float4 gl_Position; +static int _4; + +struct SPIRV_Cross_Output +{ + nointerpolation int _4 : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + float4 _63 = 0.0f.xxxx; + _63.y = float(_20); + float4 _66 = _63; + _66.z = float(_25); + float4 _52 = _66 + float4(_30); + float2 _56 = _52.xy + float2(_32); + gl_Position = float4(_56.x, _56.y, _52.z, _52.w); + _4 = _33; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output._4 = _4; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..0d1e8cc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = float(uint(gl_VertexIndex) + uint(gl_InstanceIndex)).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..48b2df2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = float(gl_VertexIndex + gl_InstanceIndex).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chain-load-composite.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chain-load-composite.comp new file mode 100644 index 0000000..778f62e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chain-load-composite.comp @@ -0,0 +1,108 @@ +struct Baz +{ + float c; +}; + +struct Bar +{ + float d[2][4]; + Baz baz[2]; +}; + +struct Foo +{ + column_major float2x2 a; + float2 b; + Bar c[5]; +}; + +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _31 : register(u0); + +void comp_main() +{ + Foo _36; + _36.a = asfloat(uint2x2(_31.Load(0), _31.Load(8), _31.Load(4), _31.Load(12))); + _36.b = asfloat(_31.Load2(16)); + [unroll] + for (int _4ident = 0; _4ident < 5; _4ident++) + { + [unroll] + for (int _5ident = 0; _5ident < 2; _5ident++) + { + [unroll] + for (int _6ident = 0; _6ident < 4; _6ident++) + { + _36.c[_4ident].d[_5ident][_6ident] = asfloat(_31.Load(_6ident * 4 + _5ident * 16 + _4ident * 40 + 24)); + } + } + [unroll] + for (int _7ident = 0; _7ident < 2; _7ident++) + { + _36.c[_4ident].baz[_7ident].c = asfloat(_31.Load(_7ident * 4 + _4ident * 40 + 56)); + } + } + float2x2 _234 = float2x2(_36.a[0] + 1.0f.xx, _36.a[1] + 1.0f.xx); + _31.Store(224, asuint(_234[0].x)); + _31.Store(228, asuint(_234[1].x)); + _31.Store(232, asuint(_234[0].y)); + _31.Store(236, asuint(_234[1].y)); + _31.Store2(240, asuint(_36.b + 2.0f.xx)); + _31.Store(248, asuint(_36.c[0].d[0][0])); + _31.Store(252, asuint(_36.c[0].d[0][1])); + _31.Store(256, asuint(_36.c[0].d[0][2])); + _31.Store(260, asuint(_36.c[0].d[0][3])); + _31.Store(264, asuint(_36.c[0].d[1][0])); + _31.Store(268, asuint(_36.c[0].d[1][1])); + _31.Store(272, asuint(_36.c[0].d[1][2])); + _31.Store(276, asuint(_36.c[0].d[1][3])); + _31.Store(280, asuint(_36.c[0].baz[0].c)); + _31.Store(284, asuint(_36.c[0].baz[1].c)); + _31.Store(288, asuint(_36.c[1].d[0][0])); + _31.Store(292, asuint(_36.c[1].d[0][1])); + _31.Store(296, asuint(_36.c[1].d[0][2])); + _31.Store(300, asuint(_36.c[1].d[0][3])); + _31.Store(304, asuint(_36.c[1].d[1][0])); + _31.Store(308, asuint(_36.c[1].d[1][1])); + _31.Store(312, asuint(_36.c[1].d[1][2])); + _31.Store(316, asuint(_36.c[1].d[1][3])); + _31.Store(320, asuint(_36.c[1].baz[0].c)); + _31.Store(324, asuint(_36.c[1].baz[1].c)); + _31.Store(328, asuint(_36.c[2].d[0][0])); + _31.Store(332, asuint(_36.c[2].d[0][1])); + _31.Store(336, asuint(_36.c[2].d[0][2])); + _31.Store(340, asuint(_36.c[2].d[0][3])); + _31.Store(344, asuint(_36.c[2].d[1][0])); + _31.Store(348, asuint(_36.c[2].d[1][1])); + _31.Store(352, asuint(_36.c[2].d[1][2])); + _31.Store(356, asuint(_36.c[2].d[1][3])); + _31.Store(360, asuint(_36.c[2].baz[0].c)); + _31.Store(364, asuint(_36.c[2].baz[1].c)); + _31.Store(368, asuint(_36.c[3].d[0][0])); + _31.Store(372, asuint(_36.c[3].d[0][1])); + _31.Store(376, asuint(_36.c[3].d[0][2])); + _31.Store(380, asuint(_36.c[3].d[0][3])); + _31.Store(384, asuint(_36.c[3].d[1][0])); + _31.Store(388, asuint(_36.c[3].d[1][1] + 5.0f)); + _31.Store(392, asuint(_36.c[3].d[1][2])); + _31.Store(396, asuint(_36.c[3].d[1][3])); + _31.Store(400, asuint(_36.c[3].baz[0].c)); + _31.Store(404, asuint(_36.c[3].baz[1].c)); + _31.Store(408, asuint(_36.c[4].d[0][0])); + _31.Store(412, asuint(_36.c[4].d[0][1])); + _31.Store(416, asuint(_36.c[4].d[0][2])); + _31.Store(420, asuint(_36.c[4].d[0][3])); + _31.Store(424, asuint(_36.c[4].d[1][0])); + _31.Store(428, asuint(_36.c[4].d[1][1])); + _31.Store(432, asuint(_36.c[4].d[1][2])); + _31.Store(436, asuint(_36.c[4].d[1][3])); + _31.Store(440, asuint(_36.c[4].baz[0].c)); + _31.Store(444, asuint(_36.c[4].baz[1].c)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chains.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chains.comp new file mode 100644 index 0000000..c748200 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chains.comp @@ -0,0 +1,23 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer wo : register(u1); +ByteAddressBuffer ro : register(t0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + wo.Store4(gl_GlobalInvocationID.x * 64 + 272, asuint(asfloat(ro.Load4(gl_GlobalInvocationID.x * 64 + 160)))); + wo.Store4(gl_GlobalInvocationID.x * 16 + 480, asuint(asfloat(ro.Load4(gl_GlobalInvocationID.x * 16 + 480)))); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chains.force-uav.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chains.force-uav.comp new file mode 100644 index 0000000..97d046d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/access-chains.force-uav.comp @@ -0,0 +1,23 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer wo : register(u1); +RWByteAddressBuffer ro : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + wo.Store4(gl_GlobalInvocationID.x * 64 + 272, asuint(asfloat(ro.Load4(gl_GlobalInvocationID.x * 64 + 160)))); + wo.Store4(gl_GlobalInvocationID.x * 16 + 480, asuint(asfloat(ro.Load4(gl_GlobalInvocationID.x * 16 + 480)))); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/address-buffers.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/address-buffers.comp new file mode 100644 index 0000000..7f1c797 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/address-buffers.comp @@ -0,0 +1,17 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer WriteOnly : register(u2); +ByteAddressBuffer ReadOnly : register(t0); +RWByteAddressBuffer ReadWrite : register(u1); + +void comp_main() +{ + WriteOnly.Store4(0, asuint(asfloat(ReadOnly.Load4(0)))); + ReadWrite.Store4(0, asuint(asfloat(ReadWrite.Load4(0)) + 10.0f.xxxx)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/atomic.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/atomic.comp new file mode 100644 index 0000000..e6ff891 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/atomic.comp @@ -0,0 +1,91 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer ssbo : register(u2); +RWTexture2D uImage : register(u0); +RWTexture2D iImage : register(u1); + +groupshared int int_atomic; +groupshared uint uint_atomic; +groupshared int int_atomic_array[1]; +groupshared uint uint_atomic_array[1]; + +void comp_main() +{ + uint _19; + InterlockedAdd(uImage[int2(1, 5)], 1u, _19); + uint _27; + InterlockedAdd(uImage[int2(1, 5)], 1u, _27); + iImage[int2(1, 6)] = int(_27).x; + uint _32; + InterlockedOr(uImage[int2(1, 5)], 1u, _32); + uint _34; + InterlockedXor(uImage[int2(1, 5)], 1u, _34); + uint _36; + InterlockedAnd(uImage[int2(1, 5)], 1u, _36); + uint _38; + InterlockedMin(uImage[int2(1, 5)], 1u, _38); + uint _40; + InterlockedMax(uImage[int2(1, 5)], 1u, _40); + uint _44; + InterlockedCompareExchange(uImage[int2(1, 5)], 10u, 2u, _44); + int _47; + InterlockedAdd(iImage[int2(1, 6)], 1, _47); + int _49; + InterlockedOr(iImage[int2(1, 6)], 1, _49); + int _51; + InterlockedXor(iImage[int2(1, 6)], 1, _51); + int _53; + InterlockedAnd(iImage[int2(1, 6)], 1, _53); + int _55; + InterlockedMin(iImage[int2(1, 6)], 1, _55); + int _57; + InterlockedMax(iImage[int2(1, 6)], 1, _57); + int _61; + InterlockedCompareExchange(iImage[int2(1, 5)], 10, 2, _61); + uint _68; + ssbo.InterlockedAdd(0, 1u, _68); + uint _70; + ssbo.InterlockedOr(0, 1u, _70); + uint _72; + ssbo.InterlockedXor(0, 1u, _72); + uint _74; + ssbo.InterlockedAnd(0, 1u, _74); + uint _76; + ssbo.InterlockedMin(0, 1u, _76); + uint _78; + ssbo.InterlockedMax(0, 1u, _78); + uint _80; + ssbo.InterlockedExchange(0, 1u, _80); + uint _82; + ssbo.InterlockedCompareExchange(0, 10u, 2u, _82); + int _85; + ssbo.InterlockedAdd(4, 1, _85); + int _87; + ssbo.InterlockedOr(4, 1, _87); + int _89; + ssbo.InterlockedXor(4, 1, _89); + int _91; + ssbo.InterlockedAnd(4, 1, _91); + int _93; + ssbo.InterlockedMin(4, 1, _93); + int _95; + ssbo.InterlockedMax(4, 1, _95); + int _97; + ssbo.InterlockedExchange(4, 1, _97); + int _99; + ssbo.InterlockedCompareExchange(4, 10, 2, _99); + int _102; + InterlockedAdd(int_atomic, 10, _102); + uint _105; + InterlockedAdd(uint_atomic, 10u, _105); + int _110; + InterlockedAdd(int_atomic_array[0], 10, _110); + uint _115; + InterlockedAdd(uint_atomic_array[0], 10u, _115); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/barriers.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/barriers.comp new file mode 100644 index 0000000..7ac2a65 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/barriers.comp @@ -0,0 +1,26 @@ +static const uint3 gl_WorkGroupSize = uint3(4u, 1u, 1u); + +void comp_main() +{ + GroupMemoryBarrier(); + AllMemoryBarrier(); + DeviceMemoryBarrier(); + DeviceMemoryBarrier(); + AllMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); + AllMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); + DeviceMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); + DeviceMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); + AllMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); + GroupMemoryBarrierWithGroupSync(); +} + +[numthreads(4, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/builtins.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/builtins.comp new file mode 100644 index 0000000..7f88aa7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/builtins.comp @@ -0,0 +1,11 @@ +static const uint3 gl_WorkGroupSize = uint3(8u, 4u, 2u); + +void comp_main() +{ +} + +[numthreads(8, 4, 2)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/composite-array-initialization.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/composite-array-initialization.comp new file mode 100644 index 0000000..e39f4d0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/composite-array-initialization.comp @@ -0,0 +1,50 @@ +struct Data +{ + float a; + float b; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 4.0f +#endif +static const float X = SPIRV_CROSS_CONSTANT_ID_0; + +static const uint3 gl_WorkGroupSize = uint3(2u, 1u, 1u); + +static const Data _21 = { 1.0f, 2.0f }; +static const Data _24 = { 3.0f, 4.0f }; +static const Data _25[2] = { { 1.0f, 2.0f }, { 3.0f, 4.0f } }; +static const Data _30 = { 3.0f, 5.0f }; + +RWByteAddressBuffer _61 : register(u0); + +static uint3 gl_WorkGroupID; +static uint3 gl_LocalInvocationID; +static uint gl_LocalInvocationIndex; +struct SPIRV_Cross_Input +{ + uint3 gl_WorkGroupID : SV_GroupID; + uint3 gl_LocalInvocationID : SV_GroupThreadID; + uint gl_LocalInvocationIndex : SV_GroupIndex; +}; + +void comp_main() +{ + Data _28 = { X, 2.0f }; + Data _31[2] = { _28, _30 }; + Data data2[2] = _31; + if (gl_LocalInvocationIndex == 0u) + { + _61.Store(gl_WorkGroupID.x * 8 + 0, asuint(_25[gl_LocalInvocationID.x].a + data2[gl_LocalInvocationID.x].a)); + _61.Store(gl_WorkGroupID.x * 8 + 4, asuint(_25[gl_LocalInvocationID.x].b + data2[gl_LocalInvocationID.x].b)); + } +} + +[numthreads(2, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_WorkGroupID = stage_input.gl_WorkGroupID; + gl_LocalInvocationID = stage_input.gl_LocalInvocationID; + gl_LocalInvocationIndex = stage_input.gl_LocalInvocationIndex; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/globallycoherent.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000..b5f1e37 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,18 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +globallycoherent RWByteAddressBuffer _29 : register(u3); +ByteAddressBuffer _33 : register(t2); +RWTexture2D uImageIn : register(u0); +globallycoherent RWTexture2D uImageOut : register(u1); + +void comp_main() +{ + uImageOut[int2(9, 7)] = uImageIn[int2(9, 7)].x; + _29.Store(0, asuint(asfloat(_33.Load(0)))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/image.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/image.comp new file mode 100644 index 0000000..e2f6b0a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/image.comp @@ -0,0 +1,66 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWTexture2D uImageInF : register(u0); +RWTexture2D uImageOutF : register(u1); +RWTexture2D uImageInI : register(u2); +RWTexture2D uImageOutI : register(u3); +RWTexture2D uImageInU : register(u4); +RWTexture2D uImageOutU : register(u5); +RWBuffer uImageInBuffer : register(u6); +RWBuffer uImageOutBuffer : register(u7); +RWTexture2D uImageInF2 : register(u8); +RWTexture2D uImageOutF2 : register(u9); +RWTexture2D uImageInI2 : register(u10); +RWTexture2D uImageOutI2 : register(u11); +RWTexture2D uImageInU2 : register(u12); +RWTexture2D uImageOutU2 : register(u13); +RWBuffer uImageInBuffer2 : register(u14); +RWBuffer uImageOutBuffer2 : register(u15); +RWTexture2D uImageInF4 : register(u16); +RWTexture2D uImageOutF4 : register(u17); +RWTexture2D uImageInI4 : register(u18); +RWTexture2D uImageOutI4 : register(u19); +RWTexture2D uImageInU4 : register(u20); +RWTexture2D uImageOutU4 : register(u21); +RWBuffer uImageInBuffer4 : register(u22); +RWBuffer uImageOutBuffer4 : register(u23); +RWTexture2D uImageNoFmtF : register(u24); +RWTexture2D uImageNoFmtU : register(u25); +RWTexture2D uImageNoFmtI : register(u26); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + int2 _23 = int2(gl_GlobalInvocationID.xy); + uImageOutF[_23] = uImageInF[_23].x; + uImageOutI[_23] = uImageInI[_23].x; + uImageOutU[_23] = uImageInU[_23].x; + int _74 = int(gl_GlobalInvocationID.x); + uImageOutBuffer[_74] = uImageInBuffer[_74].x; + uImageOutF2[_23] = uImageInF2[_23].xy; + uImageOutI2[_23] = uImageInI2[_23].xy; + uImageOutU2[_23] = uImageInU2[_23].xy; + float4 _135 = uImageInBuffer2[_74].xyyy; + uImageOutBuffer2[_74] = _135.xy; + uImageOutF4[_23] = uImageInF4[_23]; + int4 _165 = uImageInI4[_23]; + uImageOutI4[_23] = _165; + uint4 _180 = uImageInU4[_23]; + uImageOutU4[_23] = _180; + uImageOutBuffer4[_74] = uImageInBuffer4[_74]; + uImageNoFmtF[_23] = _135; + uImageNoFmtU[_23] = _180; + uImageNoFmtI[_23] = _165; +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/image.nonwritable-uav-texture.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/image.nonwritable-uav-texture.comp new file mode 100644 index 0000000..6c4a213 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/image.nonwritable-uav-texture.comp @@ -0,0 +1,66 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +Texture2D uImageInF : register(t0); +RWTexture2D uImageOutF : register(u1); +Texture2D uImageInI : register(t2); +RWTexture2D uImageOutI : register(u3); +Texture2D uImageInU : register(t4); +RWTexture2D uImageOutU : register(u5); +Buffer uImageInBuffer : register(t6); +RWBuffer uImageOutBuffer : register(u7); +Texture2D uImageInF2 : register(t8); +RWTexture2D uImageOutF2 : register(u9); +Texture2D uImageInI2 : register(t10); +RWTexture2D uImageOutI2 : register(u11); +Texture2D uImageInU2 : register(t12); +RWTexture2D uImageOutU2 : register(u13); +Buffer uImageInBuffer2 : register(t14); +RWBuffer uImageOutBuffer2 : register(u15); +Texture2D uImageInF4 : register(t16); +RWTexture2D uImageOutF4 : register(u17); +Texture2D uImageInI4 : register(t18); +RWTexture2D uImageOutI4 : register(u19); +Texture2D uImageInU4 : register(t20); +RWTexture2D uImageOutU4 : register(u21); +Buffer uImageInBuffer4 : register(t22); +RWBuffer uImageOutBuffer4 : register(u23); +RWTexture2D uImageNoFmtF : register(u24); +RWTexture2D uImageNoFmtU : register(u25); +RWTexture2D uImageNoFmtI : register(u26); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + int2 _23 = int2(gl_GlobalInvocationID.xy); + uImageOutF[_23] = uImageInF[_23].x; + uImageOutI[_23] = uImageInI[_23].x; + uImageOutU[_23] = uImageInU[_23].x; + int _74 = int(gl_GlobalInvocationID.x); + uImageOutBuffer[_74] = uImageInBuffer[_74].x; + uImageOutF2[_23] = uImageInF2[_23].xy; + uImageOutI2[_23] = uImageInI2[_23].xy; + uImageOutU2[_23] = uImageInU2[_23].xy; + float4 _135 = uImageInBuffer2[_74]; + uImageOutBuffer2[_74] = _135.xy; + uImageOutF4[_23] = uImageInF4[_23]; + int4 _165 = uImageInI4[_23]; + uImageOutI4[_23] = _165; + uint4 _180 = uImageInU4[_23]; + uImageOutU4[_23] = _180; + uImageOutBuffer4[_74] = uImageInBuffer4[_74]; + uImageNoFmtF[_23] = _135; + uImageNoFmtU[_23] = _180; + uImageNoFmtI[_23] = _165; +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/inverse.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/inverse.comp new file mode 100644 index 0000000..f9ec89a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/inverse.comp @@ -0,0 +1,124 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _15 : register(u0); +ByteAddressBuffer _20 : register(t1); + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float2x2 SPIRV_Cross_Inverse(float2x2 m) +{ + float2x2 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = m[1][1]; + adj[0][1] = -m[0][1]; + + adj[1][0] = -m[1][0]; + adj[1][1] = m[0][0]; + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the determinant of a 2x2 matrix. +float SPIRV_Cross_Det2x2(float a1, float a2, float b1, float b2) +{ + return a1 * b2 - b1 * a2; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float3x3 SPIRV_Cross_Inverse(float3x3 m) +{ + float3x3 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = SPIRV_Cross_Det2x2(m[1][1], m[1][2], m[2][1], m[2][2]); + adj[0][1] = -SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[2][1], m[2][2]); + adj[0][2] = SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[1][1], m[1][2]); + + adj[1][0] = -SPIRV_Cross_Det2x2(m[1][0], m[1][2], m[2][0], m[2][2]); + adj[1][1] = SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[2][0], m[2][2]); + adj[1][2] = -SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[1][0], m[1][2]); + + adj[2][0] = SPIRV_Cross_Det2x2(m[1][0], m[1][1], m[2][0], m[2][1]); + adj[2][1] = -SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[2][0], m[2][1]); + adj[2][2] = SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[1][0], m[1][1]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the determinant of a 3x3 matrix. +float SPIRV_Cross_Det3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) +{ + return a1 * SPIRV_Cross_Det2x2(b2, b3, c2, c3) - b1 * SPIRV_Cross_Det2x2(a2, a3, c2, c3) + c1 * SPIRV_Cross_Det2x2(a2, a3, b2, b3); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float4x4 SPIRV_Cross_Inverse(float4x4 m) +{ + float4x4 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = SPIRV_Cross_Det3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][1] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][2] = SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3]); + adj[0][3] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]); + + adj[1][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3]); + adj[1][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]); + + adj[2][0] = SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][1] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][2] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3]); + adj[2][3] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]); + + adj[3][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2]); + adj[3][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] * m[3][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +void comp_main() +{ + float2x2 _23 = asfloat(uint2x2(_20.Load2(0), _20.Load2(8))); + float2x2 _24 = SPIRV_Cross_Inverse(_23); + _15.Store2(0, asuint(_24[0])); + _15.Store2(8, asuint(_24[1])); + float3x3 _29 = asfloat(uint3x3(_20.Load3(16), _20.Load3(32), _20.Load3(48))); + float3x3 _30 = SPIRV_Cross_Inverse(_29); + _15.Store3(16, asuint(_30[0])); + _15.Store3(32, asuint(_30[1])); + _15.Store3(48, asuint(_30[2])); + float4x4 _35 = asfloat(uint4x4(_20.Load4(64), _20.Load4(80), _20.Load4(96), _20.Load4(112))); + float4x4 _36 = SPIRV_Cross_Inverse(_35); + _15.Store4(64, asuint(_36[0])); + _15.Store4(80, asuint(_36[1])); + _15.Store4(96, asuint(_36[2])); + _15.Store4(112, asuint(_36[3])); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/num-workgroups-alone.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/num-workgroups-alone.comp new file mode 100644 index 0000000..dc87dc8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/num-workgroups-alone.comp @@ -0,0 +1,19 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _10 : register(u0); +cbuffer SPIRV_Cross_NumWorkgroups : register(b0) +{ + uint3 SPIRV_Cross_NumWorkgroups_1_count : packoffset(c0); +}; + + +void comp_main() +{ + _10.Store3(0, SPIRV_Cross_NumWorkgroups_1_count); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/num-workgroups-with-builtins.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/num-workgroups-with-builtins.comp new file mode 100644 index 0000000..2e2ad55 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/num-workgroups-with-builtins.comp @@ -0,0 +1,26 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _10 : register(u0); +cbuffer SPIRV_Cross_NumWorkgroups : register(b0) +{ + uint3 SPIRV_Cross_NumWorkgroups_1_count : packoffset(c0); +}; + + +static uint3 gl_WorkGroupID; +struct SPIRV_Cross_Input +{ + uint3 gl_WorkGroupID : SV_GroupID; +}; + +void comp_main() +{ + _10.Store3(0, SPIRV_Cross_NumWorkgroups_1_count + gl_WorkGroupID); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_WorkGroupID = stage_input.gl_WorkGroupID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/outer-product.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/outer-product.comp new file mode 100644 index 0000000..e58c02f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/outer-product.comp @@ -0,0 +1,50 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _21 : register(u0); +ByteAddressBuffer _26 : register(t1); + +void comp_main() +{ + float2x2 _32 = float2x2(asfloat(_26.Load2(0)) * asfloat(_26.Load2(0)).x, asfloat(_26.Load2(0)) * asfloat(_26.Load2(0)).y); + _21.Store2(0, asuint(_32[0])); + _21.Store2(8, asuint(_32[1])); + float2x3 _41 = float2x3(asfloat(_26.Load3(16)) * asfloat(_26.Load2(0)).x, asfloat(_26.Load3(16)) * asfloat(_26.Load2(0)).y); + _21.Store3(16, asuint(_41[0])); + _21.Store3(32, asuint(_41[1])); + float2x4 _50 = float2x4(asfloat(_26.Load4(32)) * asfloat(_26.Load2(0)).x, asfloat(_26.Load4(32)) * asfloat(_26.Load2(0)).y); + _21.Store4(48, asuint(_50[0])); + _21.Store4(64, asuint(_50[1])); + float3x2 _58 = float3x2(asfloat(_26.Load2(0)) * asfloat(_26.Load3(16)).x, asfloat(_26.Load2(0)) * asfloat(_26.Load3(16)).y, asfloat(_26.Load2(0)) * asfloat(_26.Load3(16)).z); + _21.Store2(80, asuint(_58[0])); + _21.Store2(88, asuint(_58[1])); + _21.Store2(96, asuint(_58[2])); + float3x3 _66 = float3x3(asfloat(_26.Load3(16)) * asfloat(_26.Load3(16)).x, asfloat(_26.Load3(16)) * asfloat(_26.Load3(16)).y, asfloat(_26.Load3(16)) * asfloat(_26.Load3(16)).z); + _21.Store3(112, asuint(_66[0])); + _21.Store3(128, asuint(_66[1])); + _21.Store3(144, asuint(_66[2])); + float3x4 _74 = float3x4(asfloat(_26.Load4(32)) * asfloat(_26.Load3(16)).x, asfloat(_26.Load4(32)) * asfloat(_26.Load3(16)).y, asfloat(_26.Load4(32)) * asfloat(_26.Load3(16)).z); + _21.Store4(160, asuint(_74[0])); + _21.Store4(176, asuint(_74[1])); + _21.Store4(192, asuint(_74[2])); + float4x2 _82 = float4x2(asfloat(_26.Load2(0)) * asfloat(_26.Load4(32)).x, asfloat(_26.Load2(0)) * asfloat(_26.Load4(32)).y, asfloat(_26.Load2(0)) * asfloat(_26.Load4(32)).z, asfloat(_26.Load2(0)) * asfloat(_26.Load4(32)).w); + _21.Store2(208, asuint(_82[0])); + _21.Store2(216, asuint(_82[1])); + _21.Store2(224, asuint(_82[2])); + _21.Store2(232, asuint(_82[3])); + float4x3 _90 = float4x3(asfloat(_26.Load3(16)) * asfloat(_26.Load4(32)).x, asfloat(_26.Load3(16)) * asfloat(_26.Load4(32)).y, asfloat(_26.Load3(16)) * asfloat(_26.Load4(32)).z, asfloat(_26.Load3(16)) * asfloat(_26.Load4(32)).w); + _21.Store3(240, asuint(_90[0])); + _21.Store3(256, asuint(_90[1])); + _21.Store3(272, asuint(_90[2])); + _21.Store3(288, asuint(_90[3])); + float4x4 _98 = float4x4(asfloat(_26.Load4(32)) * asfloat(_26.Load4(32)).x, asfloat(_26.Load4(32)) * asfloat(_26.Load4(32)).y, asfloat(_26.Load4(32)) * asfloat(_26.Load4(32)).z, asfloat(_26.Load4(32)) * asfloat(_26.Load4(32)).w); + _21.Store4(304, asuint(_98[0])); + _21.Store4(320, asuint(_98[1])); + _21.Store4(336, asuint(_98[2])); + _21.Store4(352, asuint(_98[3])); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/rmw-matrix.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/rmw-matrix.comp new file mode 100644 index 0000000..30ac03f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/rmw-matrix.comp @@ -0,0 +1,22 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _11 : register(u0); + +void comp_main() +{ + _11.Store(0, asuint(asfloat(_11.Load(0)) * asfloat(_11.Load(96)))); + _11.Store4(16, asuint(asfloat(_11.Load4(16)) * asfloat(_11.Load4(112)))); + float4x4 _35 = asfloat(uint4x4(_11.Load4(128), _11.Load4(144), _11.Load4(160), _11.Load4(176))); + float4x4 _37 = asfloat(uint4x4(_11.Load4(32), _11.Load4(48), _11.Load4(64), _11.Load4(80))); + float4x4 _38 = mul(_35, _37); + _11.Store4(32, asuint(_38[0])); + _11.Store4(48, asuint(_38[1])); + _11.Store4(64, asuint(_38[2])); + _11.Store4(80, asuint(_38[3])); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/rwbuffer-matrix.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/rwbuffer-matrix.comp new file mode 100644 index 0000000..09cbd2f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/rwbuffer-matrix.comp @@ -0,0 +1,93 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _28 : register(u0); +cbuffer UBO : register(b1) +{ + int _68_index0 : packoffset(c0); + int _68_index1 : packoffset(c0.y); +}; + + +void comp_main() +{ + float4x4 _258 = asfloat(uint4x4(_28.Load(64), _28.Load(80), _28.Load(96), _28.Load(112), _28.Load(68), _28.Load(84), _28.Load(100), _28.Load(116), _28.Load(72), _28.Load(88), _28.Load(104), _28.Load(120), _28.Load(76), _28.Load(92), _28.Load(108), _28.Load(124))); + _28.Store4(0, asuint(_258[0])); + _28.Store4(16, asuint(_258[1])); + _28.Store4(32, asuint(_258[2])); + _28.Store4(48, asuint(_258[3])); + float2x2 _261 = asfloat(uint2x2(_28.Load(144), _28.Load(152), _28.Load(148), _28.Load(156))); + _28.Store2(128, asuint(_261[0])); + _28.Store2(136, asuint(_261[1])); + float2x3 _264 = asfloat(uint2x3(_28.Load(192), _28.Load(200), _28.Load(208), _28.Load(196), _28.Load(204), _28.Load(212))); + _28.Store3(160, asuint(_264[0])); + _28.Store3(176, asuint(_264[1])); + float3x2 _267 = asfloat(uint3x2(_28.Load(240), _28.Load(256), _28.Load(244), _28.Load(260), _28.Load(248), _28.Load(264))); + _28.Store2(216, asuint(_267[0])); + _28.Store2(224, asuint(_267[1])); + _28.Store2(232, asuint(_267[2])); + float4x4 _271 = asfloat(uint4x4(_28.Load4(0), _28.Load4(16), _28.Load4(32), _28.Load4(48))); + _28.Store(64, asuint(_271[0].x)); + _28.Store(68, asuint(_271[1].x)); + _28.Store(72, asuint(_271[2].x)); + _28.Store(76, asuint(_271[3].x)); + _28.Store(80, asuint(_271[0].y)); + _28.Store(84, asuint(_271[1].y)); + _28.Store(88, asuint(_271[2].y)); + _28.Store(92, asuint(_271[3].y)); + _28.Store(96, asuint(_271[0].z)); + _28.Store(100, asuint(_271[1].z)); + _28.Store(104, asuint(_271[2].z)); + _28.Store(108, asuint(_271[3].z)); + _28.Store(112, asuint(_271[0].w)); + _28.Store(116, asuint(_271[1].w)); + _28.Store(120, asuint(_271[2].w)); + _28.Store(124, asuint(_271[3].w)); + float2x2 _274 = asfloat(uint2x2(_28.Load2(128), _28.Load2(136))); + _28.Store(144, asuint(_274[0].x)); + _28.Store(148, asuint(_274[1].x)); + _28.Store(152, asuint(_274[0].y)); + _28.Store(156, asuint(_274[1].y)); + float2x3 _277 = asfloat(uint2x3(_28.Load3(160), _28.Load3(176))); + _28.Store(192, asuint(_277[0].x)); + _28.Store(196, asuint(_277[1].x)); + _28.Store(200, asuint(_277[0].y)); + _28.Store(204, asuint(_277[1].y)); + _28.Store(208, asuint(_277[0].z)); + _28.Store(212, asuint(_277[1].z)); + float3x2 _280 = asfloat(uint3x2(_28.Load2(216), _28.Load2(224), _28.Load2(232))); + _28.Store(240, asuint(_280[0].x)); + _28.Store(244, asuint(_280[1].x)); + _28.Store(248, asuint(_280[2].x)); + _28.Store(256, asuint(_280[0].y)); + _28.Store(260, asuint(_280[1].y)); + _28.Store(264, asuint(_280[2].y)); + _28.Store(_68_index0 * 4 + _68_index1 * 16 + 64, asuint(1.0f)); + _28.Store(_68_index0 * 4 + _68_index1 * 8 + 144, asuint(2.0f)); + _28.Store(_68_index0 * 4 + _68_index1 * 8 + 192, asuint(3.0f)); + _28.Store(_68_index0 * 4 + _68_index1 * 16 + 240, asuint(4.0f)); + _28.Store(_68_index0 * 4 + 64, asuint(1.0f.x)); + _28.Store(_68_index0 * 4 + 80, asuint(1.0f.xxxx.y)); + _28.Store(_68_index0 * 4 + 96, asuint(1.0f.xxxx.z)); + _28.Store(_68_index0 * 4 + 112, asuint(1.0f.xxxx.w)); + _28.Store(_68_index0 * 4 + 144, asuint(2.0f.x)); + _28.Store(_68_index0 * 4 + 152, asuint(2.0f.xx.y)); + _28.Store(_68_index0 * 4 + 192, asuint(3.0f.x)); + _28.Store(_68_index0 * 4 + 200, asuint(3.0f.xxx.y)); + _28.Store(_68_index0 * 4 + 208, asuint(3.0f.xxx.z)); + _28.Store(_68_index0 * 4 + 240, asuint(4.0f.x)); + _28.Store(_68_index0 * 4 + 256, asuint(4.0f.xx.y)); + _28.Store(_68_index0 * 16 + _68_index1 * 4 + 0, asuint(1.0f)); + _28.Store(_68_index0 * 8 + _68_index1 * 4 + 128, asuint(2.0f)); + _28.Store(_68_index0 * 16 + _68_index1 * 4 + 160, asuint(3.0f)); + _28.Store(_68_index0 * 8 + _68_index1 * 4 + 216, asuint(4.0f)); + _28.Store4(_68_index0 * 16 + 0, asuint(1.0f.xxxx)); + _28.Store2(_68_index0 * 8 + 128, asuint(2.0f.xx)); + _28.Store3(_68_index0 * 16 + 160, asuint(3.0f.xxx)); + _28.Store2(_68_index0 * 8 + 216, asuint(4.0f.xx)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..db2bbe9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,16 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _9 : register(u0); + +void comp_main() +{ + _9.Store(8, asuint(distance(asfloat(_9.Load(0)), asfloat(_9.Load(4))))); + _9.Store(12, asuint(length(asfloat(_9.Load(0))))); + _9.Store(16, asuint(sign(asfloat(_9.Load(0))))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/shared.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/shared.comp new file mode 100644 index 0000000..9831302 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/shared.comp @@ -0,0 +1,29 @@ +static const uint3 gl_WorkGroupSize = uint3(4u, 1u, 1u); + +ByteAddressBuffer _22 : register(t0); +RWByteAddressBuffer _44 : register(u1); + +static uint3 gl_GlobalInvocationID; +static uint gl_LocalInvocationIndex; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; + uint gl_LocalInvocationIndex : SV_GroupIndex; +}; + +groupshared float sShared[4]; + +void comp_main() +{ + sShared[gl_LocalInvocationIndex] = asfloat(_22.Load(gl_GlobalInvocationID.x * 4 + 0)); + GroupMemoryBarrierWithGroupSync(); + _44.Store(gl_GlobalInvocationID.x * 4 + 0, asuint(sShared[3u - gl_LocalInvocationIndex])); +} + +[numthreads(4, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + gl_LocalInvocationIndex = stage_input.gl_LocalInvocationIndex; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/spec-constant-op-member-array.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/spec-constant-op-member-array.comp new file mode 100644 index 0000000..4e7c5e6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/spec-constant-op-member-array.comp @@ -0,0 +1,51 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 100 +#endif +static const int a = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 200 +#endif +static const int b = SPIRV_CROSS_CONSTANT_ID_1; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 300 +#endif +static const int c = SPIRV_CROSS_CONSTANT_ID_2; +static const int d = (c + 50); +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 400 +#endif +static const int e = SPIRV_CROSS_CONSTANT_ID_3; +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _22 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + _22.Store(gl_GlobalInvocationID.x * 4 + 2800, uint(int(_22.Load(gl_GlobalInvocationID.x * 4 + 2800)) + (int(_22.Load(gl_GlobalInvocationID.x * 4 + 2400)) + e))); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/spec-constant-work-group-size.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/spec-constant-work-group-size.comp new file mode 100644 index 0000000..55ebf32 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/spec-constant-work-group-size.comp @@ -0,0 +1,43 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 2 +#endif +static const int b = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 1 +#endif +static const int a = SPIRV_CROSS_CONSTANT_ID_0; +static const uint _26 = (uint(a) + 0u); +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 1u +#endif +static const uint _27 = SPIRV_CROSS_CONSTANT_ID_10; +static const uint3 gl_WorkGroupSize = uint3(_27, 20u, 1u); +static const uint _32 = gl_WorkGroupSize.x; +static const uint _33 = (_26 + _32); +static const uint _34 = gl_WorkGroupSize.y; +static const uint _35 = (_33 + _34); +static const int _42 = (1 - a); + +RWByteAddressBuffer _23 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + int spec_const_array_size[b]; + spec_const_array_size[0] = 10; + spec_const_array_size[1] = 40; + spec_const_array_size[a] = a; + _23.Store((_35 + gl_GlobalInvocationID.x) * 4 + 0, uint(b + spec_const_array_size[_42])); +} + +[numthreads(SPIRV_CROSS_CONSTANT_ID_10, 20, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/ssbo-array-length.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/ssbo-array-length.comp new file mode 100644 index 0000000..82657ca --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/ssbo-array-length.comp @@ -0,0 +1,17 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _11 : register(u1); + +void comp_main() +{ + uint _14; + _11.GetDimensions(_14); + _14 = (_14 - 16) / 16; + _11.Store(0, uint(int(_14))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/ssbo-array.comp b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/ssbo-array.comp new file mode 100644 index 0000000..ee202a2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/comp/ssbo-array.comp @@ -0,0 +1,11 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +void comp_main() +{ +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..3adf7d9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,34 @@ +static const float _17[5] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + +static float4 FragColor; +static float4 v0; + +struct SPIRV_Cross_Input +{ + float4 v0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + for (int _46 = 0; _46 < 4; ) + { + int _33 = _46 + 1; + FragColor += _17[_33].xxxx; + _46 = _33; + continue; + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + v0 = stage_input.v0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic-color-3comp.sm30.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic-color-3comp.sm30.frag new file mode 100644 index 0000000..d3697d6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic-color-3comp.sm30.frag @@ -0,0 +1,26 @@ +static float3 FragColor; +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : COLOR0; +}; + +void frag_main() +{ + FragColor = vColor.xyz; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = float4(FragColor, 0.0); + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic-color-3comp.sm50.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic-color-3comp.sm50.frag new file mode 100644 index 0000000..52f6fed --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic-color-3comp.sm50.frag @@ -0,0 +1,26 @@ +static float3 FragColor; +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float3 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = vColor.xyz; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic.frag new file mode 100644 index 0000000..6d06704 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/basic.frag @@ -0,0 +1,32 @@ +Texture2D uTex : register(t0); +SamplerState _uTex_sampler : register(s0); + +static float4 FragColor; +static float4 vColor; +static float2 vTex; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; + float2 vTex : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = vColor * uTex.Sample(_uTex_sampler, vTex); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + vTex = stage_input.vTex; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/bit-conversions.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/bit-conversions.frag new file mode 100644 index 0000000..b60b2eb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/bit-conversions.frag @@ -0,0 +1,26 @@ +static float2 value; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float2 value : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float4(1.0f, 0.0f, asfloat(asint(value.x)), 1.0f); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + value = stage_input.value; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/boolean-mix.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/boolean-mix.frag new file mode 100644 index 0000000..f3e8489 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/boolean-mix.frag @@ -0,0 +1,27 @@ +static float2 FragColor; +static float2 x0; + +struct SPIRV_Cross_Input +{ + float2 x0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float2 FragColor : SV_Target0; +}; + +void frag_main() +{ + bool2 _27 = (x0.x > x0.y).xx; + FragColor = float2(_27.x ? float2(1.0f, 0.0f).x : float2(0.0f, 1.0f).x, _27.y ? float2(1.0f, 0.0f).y : float2(0.0f, 1.0f).y); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + x0 = stage_input.x0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/builtins.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/builtins.frag new file mode 100644 index 0000000..8432c42 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/builtins.frag @@ -0,0 +1,34 @@ +static float4 gl_FragCoord; +static float gl_FragDepth; +static float4 FragColor; +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; + float gl_FragDepth : SV_Depth; +}; + +void frag_main() +{ + FragColor = gl_FragCoord + vColor; + gl_FragDepth = 0.5f; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_FragDepth = gl_FragDepth; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/bvec-operations.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/bvec-operations.frag new file mode 100644 index 0000000..2770e97 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/bvec-operations.frag @@ -0,0 +1,29 @@ +static float2 value; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float2 value : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +static bool _47; + +void frag_main() +{ + bool2 _25 = bool2(value.x == 0.0f, _47); + FragColor = float4(1.0f, 0.0f, float(bool2(!_25.x, !_25.y).x), float(bool2(value.x <= float2(1.5f, 0.5f).x, value.y <= float2(1.5f, 0.5f).y).x)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + value = stage_input.value; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/clip-cull-distance.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/clip-cull-distance.frag new file mode 100644 index 0000000..52f1ac3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/clip-cull-distance.frag @@ -0,0 +1,30 @@ +static float gl_ClipDistance[2]; +static float gl_CullDistance[1]; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float2 gl_ClipDistance0 : SV_ClipDistance0; + float gl_CullDistance0 : SV_CullDistance0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = (gl_ClipDistance[0] + gl_CullDistance[0]) + gl_ClipDistance[1]; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_ClipDistance[0] = stage_input.gl_ClipDistance0.x; + gl_ClipDistance[1] = stage_input.gl_ClipDistance0.y; + gl_CullDistance[0] = stage_input.gl_CullDistance0.x; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/combined-texture-sampler-parameter.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/combined-texture-sampler-parameter.frag new file mode 100644 index 0000000..18968cb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/combined-texture-sampler-parameter.frag @@ -0,0 +1,24 @@ +Texture2D uSampler : register(t0); +SamplerState _uSampler_sampler : register(s0); +Texture2D uSamplerShadow : register(t1); +SamplerComparisonState _uSamplerShadow_sampler : register(s1); + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = (uSampler.Sample(_uSampler_sampler, 1.0f.xx) + uSampler.Load(int3(int2(10, 10), 0))).x + uSamplerShadow.SampleCmp(_uSamplerShadow_sampler, 1.0f.xxx.xy, 1.0f); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/combined-texture-sampler-shadow.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/combined-texture-sampler-shadow.frag new file mode 100644 index 0000000..6e8f833 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/combined-texture-sampler-shadow.frag @@ -0,0 +1,23 @@ +Texture2D uDepth : register(t2); +SamplerComparisonState uSampler : register(s0); +SamplerState uSampler1 : register(s1); + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uDepth.SampleCmp(uSampler, 1.0f.xxx.xy, 1.0f) + uDepth.Sample(uSampler1, 1.0f.xx).x; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..1de8824 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,38 @@ +RWByteAddressBuffer _34 : register(u0); +Texture2D Buf : register(t1); +SamplerState _Buf_sampler : register(s1); + +static float4 gl_FragCoord; +static int vIn; +static int vIn2; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + nointerpolation int vIn : TEXCOORD0; + nointerpolation int vIn2 : TEXCOORD1; + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int _40 = Buf.Load(int3(int2(gl_FragCoord.xy), 0)).x % 16; + FragColor = (asfloat(_34.Load4(_40 * 16 + 0)) + asfloat(_34.Load4(_40 * 16 + 0))) + asfloat(_34.Load4(((vIn * vIn) + (vIn2 * vIn2)) * 16 + 0)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + vIn = stage_input.vIn; + vIn2 = stage_input.vIn2; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/constant-composites.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/constant-composites.frag new file mode 100644 index 0000000..306ca5c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/constant-composites.frag @@ -0,0 +1,38 @@ +struct Foo +{ + float a; + float b; +}; + +static const float _16[4] = { 1.0f, 4.0f, 3.0f, 2.0f }; +static const Foo _24 = { 10.0f, 20.0f }; +static const Foo _27 = { 30.0f, 40.0f }; +static const Foo _28[2] = { { 10.0f, 20.0f }, { 30.0f, 40.0f } }; + +static float4 FragColor; +static int _line; + +struct SPIRV_Cross_Input +{ + nointerpolation int _line : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _16[_line].xxxx; + FragColor += (_28[_line].a * _28[1 - _line].a).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + _line = stage_input._line; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..1b314e1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,55 @@ +Texture2D uSampler : register(t0); +SamplerState _uSampler_sampler : register(s0); + +static float4 FragColor; +static float4 vInput; + +struct SPIRV_Cross_Input +{ + float4 vInput : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = vInput; + float4 _23 = uSampler.Sample(_uSampler_sampler, vInput.xy); + float4 _26 = ddx(vInput); + float4 _29 = ddy(vInput); + float4 _32 = fwidth(vInput); + float4 _35 = ddx_coarse(vInput); + float4 _38 = ddy_coarse(vInput); + float4 _41 = fwidth(vInput); + float4 _44 = ddx_fine(vInput); + float4 _47 = ddy_fine(vInput); + float4 _50 = fwidth(vInput); + float _56_tmp = uSampler.CalculateLevelOfDetail(_uSampler_sampler, vInput.zw); + float2 _56 = _56_tmp.xx; + if (vInput.y > 10.0f) + { + FragColor += _23; + FragColor += _26; + FragColor += _29; + FragColor += _32; + FragColor += _35; + FragColor += _38; + FragColor += _41; + FragColor += _44; + FragColor += _47; + FragColor += _50; + FragColor += _56.xyxy; + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vInput = stage_input.vInput; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/demote-to-helper.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/demote-to-helper.frag new file mode 100644 index 0000000..743a422 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/demote-to-helper.frag @@ -0,0 +1,9 @@ +void frag_main() +{ + discard; +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/depth-greater-than.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/depth-greater-than.frag new file mode 100644 index 0000000..b9f50db --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/depth-greater-than.frag @@ -0,0 +1,19 @@ +static float gl_FragDepth; +struct SPIRV_Cross_Output +{ + float gl_FragDepth : SV_DepthGreaterEqual; +}; + +void frag_main() +{ + gl_FragDepth = 0.5f; +} + +[earlydepthstencil] +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_FragDepth = gl_FragDepth; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/depth-less-than.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/depth-less-than.frag new file mode 100644 index 0000000..a702fd9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/depth-less-than.frag @@ -0,0 +1,19 @@ +static float gl_FragDepth; +struct SPIRV_Cross_Output +{ + float gl_FragDepth : SV_DepthLessEqual; +}; + +void frag_main() +{ + gl_FragDepth = 0.5f; +} + +[earlydepthstencil] +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_FragDepth = gl_FragDepth; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/dual-source-blending.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/dual-source-blending.frag new file mode 100644 index 0000000..961e800 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/dual-source-blending.frag @@ -0,0 +1,23 @@ +static float4 FragColor0; +static float4 FragColor1; + +struct SPIRV_Cross_Output +{ + float4 FragColor0 : SV_Target0; + float4 FragColor1 : SV_Target1; +}; + +void frag_main() +{ + FragColor0 = 1.0f.xxxx; + FragColor1 = 2.0f.xxxx; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor0 = FragColor0; + stage_output.FragColor1 = FragColor1; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/early-fragment-test.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/early-fragment-test.frag new file mode 100644 index 0000000..ae2569d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/early-fragment-test.frag @@ -0,0 +1,9 @@ +void frag_main() +{ +} + +[earlydepthstencil] +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..f37c1fc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,25 @@ +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = 0.0f.xxxx; + for (int _43 = 0; _43 < 3; ) + { + FragColor[_43] += float(_43); + _43++; + continue; + } +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/fp16-packing.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/fp16-packing.frag new file mode 100644 index 0000000..d878282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/fp16-packing.frag @@ -0,0 +1,44 @@ +static float2 FP32Out; +static uint FP16; +static uint FP16Out; +static float2 FP32; + +struct SPIRV_Cross_Input +{ + nointerpolation uint FP16 : TEXCOORD0; + nointerpolation float2 FP32 : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float2 FP32Out : SV_Target0; + uint FP16Out : SV_Target1; +}; + +uint SPIRV_Cross_packHalf2x16(float2 value) +{ + uint2 Packed = f32tof16(value); + return Packed.x | (Packed.y << 16); +} + +float2 SPIRV_Cross_unpackHalf2x16(uint value) +{ + return f16tof32(uint2(value & 0xffff, value >> 16)); +} + +void frag_main() +{ + FP32Out = SPIRV_Cross_unpackHalf2x16(FP16); + FP16Out = SPIRV_Cross_packHalf2x16(FP32); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + FP16 = stage_input.FP16; + FP32 = stage_input.FP32; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FP32Out = FP32Out; + stage_output.FP16Out = FP16Out; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/front-facing.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/front-facing.frag new file mode 100644 index 0000000..4ed09a2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/front-facing.frag @@ -0,0 +1,39 @@ +static bool gl_FrontFacing; +static float4 FragColor; +static float4 vA; +static float4 vB; + +struct SPIRV_Cross_Input +{ + float4 vA : TEXCOORD0; + float4 vB : TEXCOORD1; + bool gl_FrontFacing : SV_IsFrontFace; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + if (gl_FrontFacing) + { + FragColor = vA; + } + else + { + FragColor = vB; + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FrontFacing = stage_input.gl_FrontFacing; + vA = stage_input.vA; + vB = stage_input.vB; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-selective.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-selective.frag new file mode 100644 index 0000000..3b50282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-selective.frag @@ -0,0 +1,8 @@ +void frag_main() +{ +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-uav.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-uav.frag new file mode 100644 index 0000000..3b50282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-uav.frag @@ -0,0 +1,8 @@ +void frag_main() +{ +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag new file mode 100644 index 0000000..3b50282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag @@ -0,0 +1,8 @@ +void frag_main() +{ +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query.frag new file mode 100644 index 0000000..3b50282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/image-query.frag @@ -0,0 +1,8 @@ +void frag_main() +{ +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/input-attachment-ms.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/input-attachment-ms.frag new file mode 100644 index 0000000..54cb1dd --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/input-attachment-ms.frag @@ -0,0 +1,33 @@ +Texture2DMS uSubpass0 : register(t0); +Texture2DMS uSubpass1 : register(t1); + +static float4 gl_FragCoord; +static int gl_SampleID; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; + uint gl_SampleID : SV_SampleIndex; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = (uSubpass0.Load(int2(gl_FragCoord.xy), 1) + uSubpass1.Load(int2(gl_FragCoord.xy), 2)) + uSubpass0.Load(int2(gl_FragCoord.xy), gl_SampleID); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + gl_SampleID = stage_input.gl_SampleID; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/input-attachment.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/input-attachment.frag new file mode 100644 index 0000000..34aaafc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/input-attachment.frag @@ -0,0 +1,30 @@ +Texture2D uSubpass0 : register(t0); +Texture2D uSubpass1 : register(t1); + +static float4 gl_FragCoord; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uSubpass0.Load(int3(int2(gl_FragCoord.xy), 0)) + uSubpass1.Load(int3(int2(gl_FragCoord.xy), 0)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/io-block.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/io-block.frag new file mode 100644 index 0000000..52c1f51 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/io-block.frag @@ -0,0 +1,28 @@ +static float4 FragColor; + +struct VertexOut +{ + float4 a : TEXCOORD1; + float4 b : TEXCOORD2; +}; + +static VertexOut _12; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _12.a + _12.b; +} + +SPIRV_Cross_Output main(in VertexOut stage_input_12) +{ + _12 = stage_input_12; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag new file mode 100644 index 0000000..2af0e51 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag @@ -0,0 +1,32 @@ +uniform sampler2D uSampler; + +static float4 FragColor; +static float2 vUV; + +struct SPIRV_Cross_Input +{ + float2 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : COLOR0; +}; + +void frag_main() +{ + float3 _23 = float3(vUV, 5.0f); + FragColor = tex2Dproj(uSampler, float4(_23.xy, 0.0, _23.z)); + FragColor += tex2Dbias(uSampler, float4(vUV, 0.0, 3.0f)); + FragColor += tex2Dlod(uSampler, float4(vUV, 0.0, 2.0f)); + FragColor += tex2Dgrad(uSampler, vUV, 4.0f.xx, 5.0f.xx); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = float4(FragColor); + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/lut-promotion.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000..4d89d36 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/lut-promotion.frag @@ -0,0 +1,55 @@ +static const float _16[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f }; +static const float4 _60[4] = { 0.0f.xxxx, 1.0f.xxxx, 8.0f.xxxx, 5.0f.xxxx }; +static const float4 _104[4] = { 20.0f.xxxx, 30.0f.xxxx, 50.0f.xxxx, 60.0f.xxxx }; + +static float FragColor; +static int index; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _16[index]; + if (index < 10) + { + FragColor += _16[index ^ 1]; + } + else + { + FragColor += _16[index & 1]; + } + bool _63 = index > 30; + if (_63) + { + FragColor += _60[index & 3].y; + } + else + { + FragColor += _60[index & 1].x; + } + float4 foobar[4] = _60; + if (_63) + { + foobar[1].z = 20.0f; + } + int _91 = index & 3; + FragColor += foobar[_91].z; + FragColor += _104[_91].z; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/matrix-input.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/matrix-input.frag new file mode 100644 index 0000000..92d87d3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/matrix-input.frag @@ -0,0 +1,26 @@ +static float4 FragColor; +static float4x4 m; + +struct SPIRV_Cross_Input +{ + float4x4 m : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = ((m[0] + m[1]) + m[2]) + m[3]; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + m = stage_input.m; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/mod.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/mod.frag new file mode 100644 index 0000000..41ac930 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/mod.frag @@ -0,0 +1,67 @@ +static float4 a4; +static float4 b4; +static float3 a3; +static float3 b3; +static float2 a2; +static float2 b2; +static float a1; +static float b1; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 a4 : TEXCOORD0; + float3 a3 : TEXCOORD1; + float2 a2 : TEXCOORD2; + float a1 : TEXCOORD3; + float4 b4 : TEXCOORD4; + float3 b3 : TEXCOORD5; + float2 b2 : TEXCOORD6; + float b1 : TEXCOORD7; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +float mod(float x, float y) +{ + return x - y * floor(x / y); +} + +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x / y); +} + +float3 mod(float3 x, float3 y) +{ + return x - y * floor(x / y); +} + +float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} + +void frag_main() +{ + FragColor = ((mod(a4, b4) + mod(a3, b3).xyzx) + mod(a2, b2).xyxy) + mod(a1, b1).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + a4 = stage_input.a4; + b4 = stage_input.b4; + a3 = stage_input.a3; + b3 = stage_input.b3; + a2 = stage_input.a2; + b2 = stage_input.b2; + a1 = stage_input.a1; + b1 = stage_input.b1; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/mrt.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/mrt.frag new file mode 100644 index 0000000..e69e911 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/mrt.frag @@ -0,0 +1,31 @@ +static float4 RT0; +static float4 RT1; +static float4 RT2; +static float4 RT3; + +struct SPIRV_Cross_Output +{ + float4 RT0 : SV_Target0; + float4 RT1 : SV_Target1; + float4 RT2 : SV_Target2; + float4 RT3 : SV_Target3; +}; + +void frag_main() +{ + RT0 = 1.0f.xxxx; + RT1 = 2.0f.xxxx; + RT2 = 3.0f.xxxx; + RT3 = 4.0f.xxxx; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.RT0 = RT0; + stage_output.RT1 = RT1; + stage_output.RT2 = RT2; + stage_output.RT3 = RT3; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/no-return.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/no-return.frag new file mode 100644 index 0000000..3b50282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/no-return.frag @@ -0,0 +1,8 @@ +void frag_main() +{ +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/no-return2.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/no-return2.frag new file mode 100644 index 0000000..e9d7bbc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/no-return2.frag @@ -0,0 +1,16 @@ +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +void frag_main() +{ +} + +void main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag new file mode 100644 index 0000000..65b4865 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag @@ -0,0 +1,49 @@ +struct UBO_1_1 +{ + float4 v[64]; +}; + +ConstantBuffer ubos[] : register(b0, space3); +ByteAddressBuffer ssbos[] : register(t0, space4); +Texture2D uSamplers[] : register(t0, space0); +SamplerState uSamps[] : register(s0, space2); +Texture2D uCombinedSamplers[] : register(t0, space1); +SamplerState _uCombinedSamplers_sampler[] : register(s0, space1); + +static int vIndex; +static float4 FragColor; +static float2 vUV; + +struct SPIRV_Cross_Input +{ + nointerpolation int vIndex : TEXCOORD0; + float2 vUV : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int _23 = vIndex + 10; + int _34 = vIndex + 40; + FragColor = uSamplers[NonUniformResourceIndex(_23)].Sample(uSamps[NonUniformResourceIndex(_34)], vUV); + FragColor = uCombinedSamplers[NonUniformResourceIndex(_23)].Sample(_uCombinedSamplers_sampler[NonUniformResourceIndex(_23)], vUV); + int _66 = vIndex + 20; + FragColor += ubos[NonUniformResourceIndex(_66)].v[_34]; + int _84 = vIndex + 50; + int _88 = vIndex + 60; + FragColor += asfloat(ssbos[NonUniformResourceIndex(_84)].Load4(_88 * 16 + 0)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vIndex = stage_input.vIndex; + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/partial-write-preserve.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/partial-write-preserve.frag new file mode 100644 index 0000000..3b50282 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/partial-write-preserve.frag @@ -0,0 +1,8 @@ +void frag_main() +{ +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag new file mode 100644 index 0000000..8923f96 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag @@ -0,0 +1,24 @@ +RWByteAddressBuffer _9 : register(u6, space0); +globallycoherent RasterizerOrderedByteAddressBuffer _42 : register(u3, space0); +RasterizerOrderedByteAddressBuffer _52 : register(u4, space0); +RWTexture2D img4 : register(u5, space0); +RasterizerOrderedTexture2D img : register(u0, space0); +RasterizerOrderedTexture2D img3 : register(u2, space0); +RasterizerOrderedTexture2D img2 : register(u1, space0); + +void frag_main() +{ + _9.Store(0, uint(0)); + img4[int2(1, 1)] = float4(1.0f, 0.0f, 0.0f, 1.0f); + img[int2(0, 0)] = img3[int2(0, 0)]; + uint _39; + InterlockedAdd(img2[int2(0, 0)], 1u, _39); + _42.Store(0, uint(int(_42.Load(0)) + 42)); + uint _55; + _42.InterlockedAnd(4, _52.Load(0), _55); +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/point-coord-compat.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/point-coord-compat.frag new file mode 100644 index 0000000..6291539 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/point-coord-compat.frag @@ -0,0 +1,19 @@ +static float2 FragColor; + +struct SPIRV_Cross_Output +{ + float2 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float2(0.5f, 0.5f); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/query-lod.desktop.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/query-lod.desktop.frag new file mode 100644 index 0000000..a9d4bd8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/query-lod.desktop.frag @@ -0,0 +1,31 @@ +Texture2D uSampler : register(t0); +SamplerState _uSampler_sampler : register(s0); + +static float4 FragColor; +static float2 vTexCoord; + +struct SPIRV_Cross_Input +{ + float2 vTexCoord : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float _19_tmp = uSampler.CalculateLevelOfDetail(_uSampler_sampler, vTexCoord); + float2 _19 = _19_tmp.xx; + FragColor = _19.xyxy; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vTexCoord = stage_input.vTexCoord; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag new file mode 100644 index 0000000..bbe3e4a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag @@ -0,0 +1,21 @@ +globallycoherent RWByteAddressBuffer _12 : register(u0); + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = asfloat(_12.Load4(0)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/readonly-coherent-ssbo.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/readonly-coherent-ssbo.frag new file mode 100644 index 0000000..02252f9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/readonly-coherent-ssbo.frag @@ -0,0 +1,21 @@ +ByteAddressBuffer _12 : register(t0); + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = asfloat(_12.Load4(0)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/resources.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/resources.frag new file mode 100644 index 0000000..aac0d53 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/resources.frag @@ -0,0 +1,41 @@ +cbuffer CBuffer : register(b3) +{ + float4 cbuf_a : packoffset(c0); +}; + +cbuffer PushMe +{ + float4 registers_d : packoffset(c0); +}; + +Texture2D uSampledImage : register(t4); +SamplerState _uSampledImage_sampler : register(s4); +Texture2D uTexture : register(t5); +SamplerState uSampler : register(s6); + +static float2 vTex; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float2 vTex : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = (uSampledImage.Sample(_uSampledImage_sampler, vTex) + uTexture.Sample(uSampler, vTex)) + (cbuf_a + registers_d); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vTex = stage_input.vTex; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/row-major-layout-in-struct.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/row-major-layout-in-struct.frag new file mode 100644 index 0000000..7df0fd9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/row-major-layout-in-struct.frag @@ -0,0 +1,38 @@ +struct Foo +{ + row_major float4x4 v; + row_major float4x4 w; +}; + +cbuffer UBO : register(b0) +{ + Foo _17_foo : packoffset(c0); +}; + + +static float4 FragColor; +static float4 vUV; + +struct SPIRV_Cross_Input +{ + float4 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = mul(mul(vUV, _17_foo.w), _17_foo.v); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-cmp-level-zero.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-cmp-level-zero.frag new file mode 100644 index 0000000..c6539b1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-cmp-level-zero.frag @@ -0,0 +1,40 @@ +Texture2D uSampler2D : register(t0); +SamplerComparisonState _uSampler2D_sampler : register(s0); +Texture2DArray uSampler2DArray : register(t1); +SamplerComparisonState _uSampler2DArray_sampler : register(s1); +TextureCube uSamplerCube : register(t2); +SamplerComparisonState _uSamplerCube_sampler : register(s2); +TextureCubeArray uSamplerCubeArray : register(t3); +SamplerComparisonState _uSamplerCubeArray_sampler : register(s3); + +static float3 vUVRef; +static float4 vDirRef; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float3 vUVRef : TEXCOORD0; + float4 vDirRef : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 _80 = vDirRef; + _80.z = vDirRef.w; + FragColor = (((((((uSampler2D.SampleCmp(_uSampler2D_sampler, vUVRef.xy, vUVRef.z, int2(-1, -1)) + uSampler2DArray.SampleCmp(_uSampler2DArray_sampler, vDirRef.xyz, vDirRef.w, int2(-1, -1))) + uSamplerCube.SampleCmp(_uSamplerCube_sampler, vDirRef.xyz, vDirRef.w)) + uSamplerCubeArray.SampleCmp(_uSamplerCubeArray_sampler, vDirRef, 0.5f)) + uSampler2D.SampleCmpLevelZero(_uSampler2D_sampler, vUVRef.xy, vUVRef.z, int2(-1, -1))) + uSampler2DArray.SampleCmpLevelZero(_uSampler2DArray_sampler, vDirRef.xyz, vDirRef.w, int2(-1, -1))) + uSamplerCube.SampleCmpLevelZero(_uSamplerCube_sampler, vDirRef.xyz, vDirRef.w)) + uSampler2D.SampleCmp(_uSampler2D_sampler, _80.xy / _80.z, vDirRef.z / _80.z, int2(1, 1))) + uSampler2D.SampleCmpLevelZero(_uSampler2D_sampler, _80.xy / _80.z, vDirRef.z / _80.z, int2(1, 1)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUVRef = stage_input.vUVRef; + vDirRef = stage_input.vDirRef; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-in-and-out.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-in-and-out.frag new file mode 100644 index 0000000..185a098 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-in-and-out.frag @@ -0,0 +1,30 @@ +static int gl_SampleMaskIn; +static int gl_SampleMask; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + uint gl_SampleMaskIn : SV_Coverage; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; + uint gl_SampleMask : SV_Coverage; +}; + +void frag_main() +{ + FragColor = 1.0f.xxxx; + gl_SampleMask = gl_SampleMaskIn; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_SampleMaskIn = stage_input.gl_SampleMaskIn; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_SampleMask = gl_SampleMask; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-in.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-in.frag new file mode 100644 index 0000000..8f6cfaf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-in.frag @@ -0,0 +1,32 @@ +static int gl_SampleID; +static int gl_SampleMaskIn; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + uint gl_SampleID : SV_SampleIndex; + uint gl_SampleMaskIn : SV_Coverage; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + if ((gl_SampleMaskIn & (1 << gl_SampleID)) != 0) + { + FragColor = 1.0f.xxxx; + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_SampleID = stage_input.gl_SampleID; + gl_SampleMaskIn = stage_input.gl_SampleMaskIn; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-out.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-out.frag new file mode 100644 index 0000000..a966c03 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sample-mask-out.frag @@ -0,0 +1,23 @@ +static int gl_SampleMask; +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; + uint gl_SampleMask : SV_Coverage; +}; + +void frag_main() +{ + FragColor = 1.0f.xxxx; + gl_SampleMask = 0; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_SampleMask = gl_SampleMask; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sampler-array.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sampler-array.frag new file mode 100644 index 0000000..8ecdc6c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sampler-array.frag @@ -0,0 +1,31 @@ +Texture2D uCombined[4] : register(t0); +SamplerState _uCombined_sampler[4] : register(s0); +Texture2D uTex[4] : register(t4); +SamplerState uSampler[4] : register(s8); +RWTexture2D uImage[8] : register(u12); + +static float4 gl_FragCoord; +static float2 vTex; +static int vIndex; + +struct SPIRV_Cross_Input +{ + float2 vTex : TEXCOORD0; + nointerpolation int vIndex : TEXCOORD1; + float4 gl_FragCoord : SV_Position; +}; + +void frag_main() +{ + int _72 = vIndex + 1; + uImage[vIndex][int2(gl_FragCoord.xy)] = ((uCombined[vIndex].Sample(_uCombined_sampler[vIndex], vTex) + uTex[vIndex].Sample(uSampler[vIndex], vTex)) + uCombined[_72].Sample(_uCombined_sampler[_72], vTex)) + uTex[_72].Sample(uSampler[_72], vTex); +} + +void main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + vTex = stage_input.vTex; + vIndex = stage_input.vIndex; + frag_main(); +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sampler-image-arrays.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sampler-image-arrays.frag new file mode 100644 index 0000000..b6d0e94 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/sampler-image-arrays.frag @@ -0,0 +1,39 @@ +Texture2D uSampler[4] : register(t0); +SamplerState _uSampler_sampler[4] : register(s0); +Texture2D uTextures[4] : register(t8); +SamplerState uSamplers[4] : register(s4); + +static int vIndex; +static float2 vTex; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + nointerpolation float2 vTex : TEXCOORD0; + nointerpolation int vIndex : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = 0.0f.xxxx; + FragColor += uTextures[2].Sample(uSamplers[1], vTex); + FragColor += uSampler[vIndex].Sample(_uSampler_sampler[vIndex], vTex); + FragColor += uSampler[vIndex].Sample(_uSampler_sampler[vIndex], vTex + 0.100000001490116119384765625f.xx); + FragColor += uSampler[vIndex].Sample(_uSampler_sampler[vIndex], vTex + 0.20000000298023223876953125f.xx); + FragColor += uSampler[3].Sample(_uSampler_sampler[3], vTex + 0.300000011920928955078125f.xx); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vIndex = stage_input.vIndex; + vTex = stage_input.vTex; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..0fb694c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/scalar-refract-reflect.frag @@ -0,0 +1,49 @@ +static float FragColor; +static float3 vRefract; + +struct SPIRV_Cross_Input +{ + float3 vRefract : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +float SPIRV_Cross_Reflect(float i, float n) +{ + return i - 2.0 * dot(n, i) * n; +} + +float SPIRV_Cross_Refract(float i, float n, float eta) +{ + float NoI = n * i; + float NoI2 = NoI * NoI; + float k = 1.0 - eta * eta * (1.0 - NoI2); + if (k < 0.0) + { + return 0.0; + } + else + { + return eta * i - (eta * NoI + sqrt(k)) * n; + } +} + +void frag_main() +{ + FragColor = SPIRV_Cross_Refract(vRefract.x, vRefract.y, vRefract.z); + FragColor += SPIRV_Cross_Reflect(vRefract.x, vRefract.y); + FragColor += refract(vRefract.xy, vRefract.yz, vRefract.z).y; + FragColor += reflect(vRefract.xy, vRefract.zy).y; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vRefract = stage_input.vRefract; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag new file mode 100644 index 0000000..faed406 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag @@ -0,0 +1,22 @@ +uniform sampler2D uSamp; +uniform sampler2D SPIRV_Cross_CombineduTuS; + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : COLOR0; +}; + +void frag_main() +{ + FragColor = tex2D(uSamp, 0.5f.xx) + tex2D(SPIRV_Cross_CombineduTuS, 0.5f.xx); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = float4(FragColor); + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/spec-constant-block-size.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/spec-constant-block-size.frag new file mode 100644 index 0000000..415886d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/spec-constant-block-size.frag @@ -0,0 +1,37 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 2 +#endif +static const int Value = SPIRV_CROSS_CONSTANT_ID_10; + +cbuffer SpecConstArray : register(b0) +{ + float4 _15_samples[Value] : packoffset(c0); +}; + + +static float4 FragColor; +static int Index; + +struct SPIRV_Cross_Input +{ + nointerpolation int Index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _15_samples[Index]; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + Index = stage_input.Index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000..942da54 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,26 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 10u +#endif +static const uint s = SPIRV_CROSS_CONSTANT_ID_0; +static const bool _13 = (s > 20u); +static const uint f = _13 ? 30u : 50u; + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float(f); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/switch-unsigned-case.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..d7ec92f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/switch-unsigned-case.frag @@ -0,0 +1,38 @@ +cbuffer Buff : register(b0) +{ + uint _15_TestVal : packoffset(c0); +}; + + +static float4 fsout_Color; + +struct SPIRV_Cross_Output +{ + float4 fsout_Color : SV_Target0; +}; + +void frag_main() +{ + fsout_Color = 1.0f.xxxx; + switch (_15_TestVal) + { + case 0u: + { + fsout_Color = 0.100000001490116119384765625f.xxxx; + break; + } + case 1u: + { + fsout_Color = 0.20000000298023223876953125f.xxxx; + break; + } + } +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fsout_Color = fsout_Color; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/swizzle-scalar.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/swizzle-scalar.frag new file mode 100644 index 0000000..ab310b8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/swizzle-scalar.frag @@ -0,0 +1,41 @@ +static float4 Float; +static float vFloat; +static int4 Int; +static int vInt; +static float4 Float2; +static int4 Int2; + +struct SPIRV_Cross_Input +{ + nointerpolation float vFloat : TEXCOORD0; + nointerpolation int vInt : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 Float : SV_Target0; + int4 Int : SV_Target1; + float4 Float2 : SV_Target2; + int4 Int2 : SV_Target3; +}; + +void frag_main() +{ + Float = vFloat.xxxx * 2.0f; + Int = vInt.xxxx * int4(2, 2, 2, 2); + Float2 = 10.0f.xxxx; + Int2 = int4(10, 10, 10, 10); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vFloat = stage_input.vFloat; + vInt = stage_input.vInt; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.Float = Float; + stage_output.Int = Int; + stage_output.Float2 = Float2; + stage_output.Int2 = Int2; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling-ms.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling-ms.frag new file mode 100644 index 0000000..d4dd78d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling-ms.frag @@ -0,0 +1,34 @@ +Texture2DMS uTex : register(t0); +SamplerState _uTex_sampler : register(s0); + +static float4 gl_FragCoord; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int2 _22 = int2(gl_FragCoord.xy); + FragColor = uTex.Load(_22, 0); + FragColor += uTex.Load(_22, 1); + FragColor += uTex.Load(_22, 2); + FragColor += uTex.Load(_22, 3); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling.frag new file mode 100644 index 0000000..fd5681b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling.frag @@ -0,0 +1,71 @@ +Texture1D tex1d : register(t0); +SamplerState _tex1d_sampler : register(s0); +Texture2D tex2d : register(t1); +SamplerState _tex2d_sampler : register(s1); +Texture3D tex3d : register(t2); +SamplerState _tex3d_sampler : register(s2); +TextureCube texCube : register(t3); +SamplerState _texCube_sampler : register(s3); +Texture1D tex1dShadow : register(t4); +SamplerComparisonState _tex1dShadow_sampler : register(s4); +Texture2D tex2dShadow : register(t5); +SamplerComparisonState _tex2dShadow_sampler : register(s5); +TextureCube texCubeShadow : register(t6); +SamplerComparisonState _texCubeShadow_sampler : register(s6); +Texture1DArray tex1dArray : register(t7); +SamplerState _tex1dArray_sampler : register(s7); +Texture2DArray tex2dArray : register(t8); +SamplerState _tex2dArray_sampler : register(s8); +TextureCubeArray texCubeArray : register(t9); +SamplerState _texCubeArray_sampler : register(s9); +Texture2D separateTex2d : register(t12); +SamplerState samplerNonDepth : register(s11); +Texture2D separateTex2dDepth : register(t13); +SamplerComparisonState samplerDepth : register(s10); + +static float texCoord1d; +static float2 texCoord2d; +static float3 texCoord3d; +static float4 texCoord4d; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float texCoord1d : TEXCOORD0; + float2 texCoord2d : TEXCOORD1; + float3 texCoord3d : TEXCOORD2; + float4 texCoord4d : TEXCOORD3; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float2 _41 = float2(texCoord1d, 2.0f); + float3 _88 = float3(texCoord2d, 2.0f); + float4 _135 = float4(texCoord3d, 2.0f); + float4 _162 = (((((((((((((((((((tex1d.Sample(_tex1d_sampler, texCoord1d) + tex1d.Sample(_tex1d_sampler, texCoord1d, 1)) + tex1d.SampleLevel(_tex1d_sampler, texCoord1d, 2.0f)) + tex1d.SampleGrad(_tex1d_sampler, texCoord1d, 1.0f, 2.0f)) + tex1d.Sample(_tex1d_sampler, _41.x / _41.y)) + tex1d.SampleBias(_tex1d_sampler, texCoord1d, 1.0f)) + tex2d.Sample(_tex2d_sampler, texCoord2d)) + tex2d.Sample(_tex2d_sampler, texCoord2d, int2(1, 2))) + tex2d.SampleLevel(_tex2d_sampler, texCoord2d, 2.0f)) + tex2d.SampleGrad(_tex2d_sampler, texCoord2d, float2(1.0f, 2.0f), float2(3.0f, 4.0f))) + tex2d.Sample(_tex2d_sampler, _88.xy / _88.z)) + tex2d.SampleBias(_tex2d_sampler, texCoord2d, 1.0f)) + tex3d.Sample(_tex3d_sampler, texCoord3d)) + tex3d.Sample(_tex3d_sampler, texCoord3d, int3(1, 2, 3))) + tex3d.SampleLevel(_tex3d_sampler, texCoord3d, 2.0f)) + tex3d.SampleGrad(_tex3d_sampler, texCoord3d, float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))) + tex3d.Sample(_tex3d_sampler, _135.xyz / _135.w)) + tex3d.SampleBias(_tex3d_sampler, texCoord3d, 1.0f)) + texCube.Sample(_texCube_sampler, texCoord3d)) + texCube.SampleLevel(_texCube_sampler, texCoord3d, 2.0f)) + texCube.SampleBias(_texCube_sampler, texCoord3d, 1.0f); + float4 _333 = _162; + _333.w = ((_162.w + tex1dShadow.SampleCmp(_tex1dShadow_sampler, float3(texCoord1d, 0.0f, 0.0f).x, 0.0f)) + tex2dShadow.SampleCmp(_tex2dShadow_sampler, float3(texCoord2d, 0.0f).xy, 0.0f)) + texCubeShadow.SampleCmp(_texCubeShadow_sampler, float4(texCoord3d, 0.0f).xyz, 0.0f); + float4 _243 = tex2d.GatherRed(_tex2d_sampler, texCoord2d); + float4 _269 = tex2d.GatherRed(_tex2d_sampler, texCoord2d, int2(1, 1)); + float4 _308 = ((((((((((((((_333 + tex1dArray.Sample(_tex1dArray_sampler, texCoord2d)) + tex2dArray.Sample(_tex2dArray_sampler, texCoord3d)) + texCubeArray.Sample(_texCubeArray_sampler, texCoord4d)) + _243) + _243) + tex2d.GatherGreen(_tex2d_sampler, texCoord2d)) + tex2d.GatherBlue(_tex2d_sampler, texCoord2d)) + tex2d.GatherAlpha(_tex2d_sampler, texCoord2d)) + _269) + _269) + tex2d.GatherGreen(_tex2d_sampler, texCoord2d, int2(1, 1))) + tex2d.GatherBlue(_tex2d_sampler, texCoord2d, int2(1, 1))) + tex2d.GatherAlpha(_tex2d_sampler, texCoord2d, int2(1, 1))) + tex2d.Load(int3(int2(1, 2), 0))) + separateTex2d.Sample(samplerNonDepth, texCoord2d); + float4 _336 = _308; + _336.w = _308.w + separateTex2dDepth.SampleCmp(samplerDepth, texCoord3d.xy, texCoord3d.z); + FragColor = _336; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + texCoord1d = stage_input.texCoord1d; + texCoord2d = stage_input.texCoord2d; + texCoord3d = stage_input.texCoord3d; + texCoord4d = stage_input.texCoord4d; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling.sm30.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling.sm30.frag new file mode 100644 index 0000000..4a2d9b6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/tex-sampling.sm30.frag @@ -0,0 +1,54 @@ +uniform sampler1D tex1d; +uniform sampler2D tex2d; +uniform sampler3D tex3d; +uniform samplerCUBE texCube; +uniform sampler1D tex1dShadow; +uniform sampler2D tex2dShadow; + +static float texCoord1d; +static float2 texCoord2d; +static float3 texCoord3d; +static float4 FragColor; +static float4 texCoord4d; + +struct SPIRV_Cross_Input +{ + float texCoord1d : TEXCOORD0; + float2 texCoord2d : TEXCOORD1; + float3 texCoord3d : TEXCOORD2; + float4 texCoord4d : TEXCOORD3; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : COLOR0; +}; + +void frag_main() +{ + float2 _34 = float2(texCoord1d, 2.0f); + float3 _73 = float3(texCoord2d, 2.0f); + float4 _112 = float4(texCoord3d, 2.0f); + float4 _139 = ((((((((((((((((tex1D(tex1d, texCoord1d) + tex1Dlod(tex1d, float4(texCoord1d, 0.0, 0.0, 2.0f))) + tex1Dgrad(tex1d, texCoord1d, 1.0f, 2.0f)) + tex1Dproj(tex1d, float4(_34.x, 0.0, 0.0, _34.y))) + tex1Dbias(tex1d, float4(texCoord1d, 0.0, 0.0, 1.0f))) + tex2D(tex2d, texCoord2d)) + tex2Dlod(tex2d, float4(texCoord2d, 0.0, 2.0f))) + tex2Dgrad(tex2d, texCoord2d, float2(1.0f, 2.0f), float2(3.0f, 4.0f))) + tex2Dproj(tex2d, float4(_73.xy, 0.0, _73.z))) + tex2Dbias(tex2d, float4(texCoord2d, 0.0, 1.0f))) + tex3D(tex3d, texCoord3d)) + tex3Dlod(tex3d, float4(texCoord3d, 2.0f))) + tex3Dgrad(tex3d, texCoord3d, float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f))) + tex3Dproj(tex3d, float4(_112.xyz, _112.w))) + tex3Dbias(tex3d, float4(texCoord3d, 1.0f))) + texCUBE(texCube, texCoord3d)) + texCUBElod(texCube, float4(texCoord3d, 2.0f))) + texCUBEbias(texCube, float4(texCoord3d, 1.0f)); + float3 _147 = float3(texCoord1d, 0.0f, 0.0f); + float4 _171 = float4(texCoord1d, 0.0f, 0.0f, 2.0f); + _171.y = 2.0f; + float3 _194 = float3(texCoord2d, 0.0f); + float4 _219 = float4(texCoord2d, 0.0f, 2.0f); + _219.z = 2.0f; + float4 _264 = _139; + _264.w = (((((((_139.w + tex1Dproj(tex1dShadow, float4(_147.x, 0.0, 0.0f, 1.0)).x) + tex1Dlod(tex1dShadow, float4(_147.x, 0.0, 0.0f, 2.0f)).x) + tex1Dproj(tex1dShadow, float4(_171.x, 0.0, 0.0f, _171.y)).x) + tex1Dbias(tex1dShadow, float4(_147.x, 0.0, 0.0f, 1.0f)).x) + tex2Dproj(tex2dShadow, float4(_194.xy, 0.0f, 1.0)).x) + tex2Dlod(tex2dShadow, float4(_194.xy, 0.0f, 2.0f)).x) + tex2Dproj(tex2dShadow, float4(_219.xy, 0.0f, _219.z)).x) + tex2Dbias(tex2dShadow, float4(_194.xy, 0.0f, 1.0f)).x; + FragColor = _264; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + texCoord1d = stage_input.texCoord1d; + texCoord2d = stage_input.texCoord2d; + texCoord3d = stage_input.texCoord3d; + texCoord4d = stage_input.texCoord4d; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = float4(FragColor); + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texel-fetch-offset.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..9bd2769 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texel-fetch-offset.frag @@ -0,0 +1,32 @@ +Texture2D uTexture : register(t0); +SamplerState _uTexture_sampler : register(s0); + +static float4 gl_FragCoord; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int2 _22 = int2(gl_FragCoord.xy); + FragColor = uTexture.Load(int3(_22, 0), int2(1, 1)); + FragColor += uTexture.Load(int3(_22, 0), int2(-1, 1)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texture-proj-shadow.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texture-proj-shadow.frag new file mode 100644 index 0000000..07e0600 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texture-proj-shadow.frag @@ -0,0 +1,51 @@ +Texture1D uShadow1D : register(t0); +SamplerComparisonState _uShadow1D_sampler : register(s0); +Texture2D uShadow2D : register(t1); +SamplerComparisonState _uShadow2D_sampler : register(s1); +Texture1D uSampler1D : register(t2); +SamplerState _uSampler1D_sampler : register(s2); +Texture2D uSampler2D : register(t3); +SamplerState _uSampler2D_sampler : register(s3); +Texture3D uSampler3D : register(t4); +SamplerState _uSampler3D_sampler : register(s4); + +static float FragColor; +static float4 vClip4; +static float2 vClip2; +static float3 vClip3; + +struct SPIRV_Cross_Input +{ + float3 vClip3 : TEXCOORD0; + float4 vClip4 : TEXCOORD1; + float2 vClip2 : TEXCOORD2; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 _20 = vClip4; + _20.y = vClip4.w; + FragColor = uShadow1D.SampleCmp(_uShadow1D_sampler, _20.x / _20.y, vClip4.z / _20.y); + float4 _30 = vClip4; + _30.z = vClip4.w; + FragColor = uShadow2D.SampleCmp(_uShadow2D_sampler, _30.xy / _30.z, vClip4.z / _30.z); + FragColor = uSampler1D.Sample(_uSampler1D_sampler, vClip2.x / vClip2.y).x; + FragColor = uSampler2D.Sample(_uSampler2D_sampler, vClip3.xy / vClip3.z).x; + FragColor = uSampler3D.Sample(_uSampler3D_sampler, vClip4.xyz / vClip4.w).x; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vClip4 = stage_input.vClip4; + vClip2 = stage_input.vClip2; + vClip3 = stage_input.vClip3; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texture-size-combined-image-sampler.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texture-size-combined-image-sampler.frag new file mode 100644 index 0000000..d5c3737 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/texture-size-combined-image-sampler.frag @@ -0,0 +1,30 @@ +Texture2D uTex : register(t0); +SamplerState uSampler : register(s1); + +static int2 FooOut; + +struct SPIRV_Cross_Output +{ + int2 FooOut : SV_Target0; +}; + +uint2 SPIRV_Cross_textureSize(Texture2D Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(Level, ret.x, ret.y, Param); + return ret; +} + +void frag_main() +{ + uint _23_dummy_parameter; + FooOut = int2(SPIRV_Cross_textureSize(uTex, uint(0), _23_dummy_parameter)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FooOut = FooOut; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/unary-enclose.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/unary-enclose.frag new file mode 100644 index 0000000..348b91c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/unary-enclose.frag @@ -0,0 +1,29 @@ +static float4 FragColor; +static float4 vIn; +static int4 vIn1; + +struct SPIRV_Cross_Input +{ + float4 vIn : TEXCOORD0; + nointerpolation int4 vIn1 : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = vIn; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vIn = stage_input.vIn; + vIn1 = stage_input.vIn1; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/unorm-snorm-packing.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/unorm-snorm-packing.frag new file mode 100644 index 0000000..57b5950 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/unorm-snorm-packing.frag @@ -0,0 +1,109 @@ +static float4 FP32Out; +static uint UNORM8; +static uint SNORM8; +static uint UNORM16; +static uint SNORM16; +static uint UNORM8Out; +static float4 FP32; +static uint SNORM8Out; +static uint UNORM16Out; +static uint SNORM16Out; + +struct SPIRV_Cross_Input +{ + nointerpolation uint SNORM8 : TEXCOORD0; + nointerpolation uint UNORM8 : TEXCOORD1; + nointerpolation uint SNORM16 : TEXCOORD2; + nointerpolation uint UNORM16 : TEXCOORD3; + nointerpolation float4 FP32 : TEXCOORD4; +}; + +struct SPIRV_Cross_Output +{ + float4 FP32Out : SV_Target0; + uint UNORM8Out : SV_Target1; + uint SNORM8Out : SV_Target2; + uint UNORM16Out : SV_Target3; + uint SNORM16Out : SV_Target4; +}; + +uint SPIRV_Cross_packUnorm4x8(float4 value) +{ + uint4 Packed = uint4(round(saturate(value) * 255.0)); + return Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24); +} + +float4 SPIRV_Cross_unpackUnorm4x8(uint value) +{ + uint4 Packed = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24); + return float4(Packed) / 255.0; +} + +uint SPIRV_Cross_packSnorm4x8(float4 value) +{ + int4 Packed = int4(round(clamp(value, -1.0, 1.0) * 127.0)) & 0xff; + return uint(Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24)); +} + +float4 SPIRV_Cross_unpackSnorm4x8(uint value) +{ + int SignedValue = int(value); + int4 Packed = int4(SignedValue << 24, SignedValue << 16, SignedValue << 8, SignedValue) >> 24; + return clamp(float4(Packed) / 127.0, -1.0, 1.0); +} + +uint SPIRV_Cross_packUnorm2x16(float2 value) +{ + uint2 Packed = uint2(round(saturate(value) * 65535.0)); + return Packed.x | (Packed.y << 16); +} + +float2 SPIRV_Cross_unpackUnorm2x16(uint value) +{ + uint2 Packed = uint2(value & 0xffff, value >> 16); + return float2(Packed) / 65535.0; +} + +uint SPIRV_Cross_packSnorm2x16(float2 value) +{ + int2 Packed = int2(round(clamp(value, -1.0, 1.0) * 32767.0)) & 0xffff; + return uint(Packed.x | (Packed.y << 16)); +} + +float2 SPIRV_Cross_unpackSnorm2x16(uint value) +{ + int SignedValue = int(value); + int2 Packed = int2(SignedValue << 16, SignedValue) >> 16; + return clamp(float2(Packed) / 32767.0, -1.0, 1.0); +} + +void frag_main() +{ + FP32Out = SPIRV_Cross_unpackUnorm4x8(UNORM8); + FP32Out = SPIRV_Cross_unpackSnorm4x8(SNORM8); + float2 _21 = SPIRV_Cross_unpackUnorm2x16(UNORM16); + FP32Out = float4(_21.x, _21.y, FP32Out.z, FP32Out.w); + float2 _26 = SPIRV_Cross_unpackSnorm2x16(SNORM16); + FP32Out = float4(_26.x, _26.y, FP32Out.z, FP32Out.w); + UNORM8Out = SPIRV_Cross_packUnorm4x8(FP32); + SNORM8Out = SPIRV_Cross_packSnorm4x8(FP32); + UNORM16Out = SPIRV_Cross_packUnorm2x16(FP32.xy); + SNORM16Out = SPIRV_Cross_packSnorm2x16(FP32.zw); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + UNORM8 = stage_input.UNORM8; + SNORM8 = stage_input.SNORM8; + UNORM16 = stage_input.UNORM16; + SNORM16 = stage_input.SNORM16; + FP32 = stage_input.FP32; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FP32Out = FP32Out; + stage_output.UNORM8Out = UNORM8Out; + stage_output.SNORM8Out = SNORM8Out; + stage_output.UNORM16Out = UNORM16Out; + stage_output.SNORM16Out = SNORM16Out; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/various-glsl-ops.frag b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/various-glsl-ops.frag new file mode 100644 index 0000000..0bc2fc1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/frag/various-glsl-ops.frag @@ -0,0 +1,26 @@ +static float2 interpolant; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float2 interpolant : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float4(0.0f, 0.0f, 0.0f, EvaluateAttributeSnapped(interpolant, 0.100000001490116119384765625f.xx).x) + float4(0.0f, 0.0f, 0.0f, ddx_coarse(interpolant.x)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + interpolant = stage_input.interpolant; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/basic.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/basic.vert new file mode 100644 index 0000000..e0bcebf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/basic.vert @@ -0,0 +1,39 @@ +cbuffer UBO : register(b0) +{ + row_major float4x4 _16_uMVP : packoffset(c0); +}; + + +static float4 gl_Position; +static float4 aVertex; +static float3 vNormal; +static float3 aNormal; + +struct SPIRV_Cross_Input +{ + float4 aVertex : TEXCOORD0; + float3 aNormal : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float3 vNormal : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = mul(aVertex, _16_uMVP); + vNormal = aNormal; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + aVertex = stage_input.aVertex; + aNormal = stage_input.aNormal; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vNormal = vNormal; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/clip-cull-distance.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/clip-cull-distance.vert new file mode 100644 index 0000000..7e0d104 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/clip-cull-distance.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static float gl_ClipDistance[2]; +static float gl_CullDistance[1]; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; + float2 gl_ClipDistance0 : SV_ClipDistance0; + float gl_CullDistance0 : SV_CullDistance0; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; + gl_ClipDistance[0] = 0.0f; + gl_ClipDistance[1] = 0.0f; + gl_CullDistance[0] = 4.0f; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.gl_ClipDistance0.x = gl_ClipDistance[0]; + stage_output.gl_ClipDistance0.y = gl_ClipDistance[1]; + stage_output.gl_CullDistance0.x = gl_CullDistance[0]; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/instancing.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/instancing.vert new file mode 100644 index 0000000..48b2df2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/instancing.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = float(gl_VertexIndex + gl_InstanceIndex).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/locations.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/locations.vert new file mode 100644 index 0000000..b007582 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/locations.vert @@ -0,0 +1,73 @@ +struct Foo +{ + float3 a; + float3 b; + float3 c; +}; + +static const Foo _71 = { 1.0f.xxx, 1.0f.xxx, 1.0f.xxx }; + +static float4 gl_Position; +static float4 Input2; +static float4 Input4; +static float4 Input0; +static float vLocation0; +static float vLocation1; +static float vLocation2[2]; +static Foo vLocation4; +static float vLocation9; + +struct VertexOut +{ + float3 color : TEXCOORD7; + float3 foo : TEXCOORD8; +}; + +static VertexOut vout; + +struct SPIRV_Cross_Input +{ + float4 Input0 : TEXCOORD0; + float4 Input2 : TEXCOORD2; + float4 Input4 : TEXCOORD4; +}; + +struct SPIRV_Cross_Output +{ + float vLocation0 : TEXCOORD0; + float vLocation1 : TEXCOORD1; + float vLocation2[2] : TEXCOORD2; + Foo vLocation4 : TEXCOORD4; + float vLocation9 : TEXCOORD9; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = ((1.0f.xxxx + Input2) + Input4) + Input0; + vLocation0 = 0.0f; + vLocation1 = 1.0f; + vLocation2[0] = 2.0f; + vLocation2[1] = 2.0f; + vLocation4 = _71; + vLocation9 = 9.0f; + vout.color = 2.0f.xxx; + vout.foo = 4.0f.xxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input, out VertexOut stage_outputvout) +{ + Input2 = stage_input.Input2; + Input4 = stage_input.Input4; + Input0 = stage_input.Input0; + vert_main(); + stage_outputvout = vout; + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vLocation0 = vLocation0; + stage_output.vLocation1 = vLocation1; + stage_output.vLocation2 = vLocation2; + stage_output.vLocation4 = vLocation4; + stage_output.vLocation9 = vLocation9; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/matrix-attribute.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/matrix-attribute.vert new file mode 100644 index 0000000..a3d0eef --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/matrix-attribute.vert @@ -0,0 +1,35 @@ +static float4 gl_Position; +static float4x4 m; +static float3 pos; + +struct SPIRV_Cross_Input +{ + float3 pos : TEXCOORD0; + float4 m_0 : TEXCOORD1_0; + float4 m_1 : TEXCOORD1_1; + float4 m_2 : TEXCOORD1_2; + float4 m_3 : TEXCOORD1_3; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = mul(float4(pos, 1.0f), m); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + m[0] = stage_input.m_0; + m[1] = stage_input.m_1; + m[2] = stage_input.m_2; + m[3] = stage_input.m_3; + pos = stage_input.pos; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/matrix-output.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/matrix-output.vert new file mode 100644 index 0000000..dc776cb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/matrix-output.vert @@ -0,0 +1,23 @@ +static float4 gl_Position; +static float4x4 m; + +struct SPIRV_Cross_Output +{ + float4x4 m : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; + m = float4x4(float4(1.0f, 0.0f, 0.0f, 0.0f), float4(0.0f, 1.0f, 0.0f, 0.0f), float4(0.0f, 0.0f, 1.0f, 0.0f), float4(0.0f, 0.0f, 0.0f, 1.0f)); +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.m = m; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/no-input.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/no-input.vert new file mode 100644 index 0000000..c98544d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/no-input.vert @@ -0,0 +1,18 @@ +static float4 gl_Position; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/point-size-compat.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/point-size-compat.vert new file mode 100644 index 0000000..95f45d0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/point-size-compat.vert @@ -0,0 +1,20 @@ +static float4 gl_Position; +static float gl_PointSize; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; + gl_PointSize = 1.0f; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/qualifiers.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/qualifiers.vert new file mode 100644 index 0000000..13ee2a8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/qualifiers.vert @@ -0,0 +1,50 @@ +static float4 gl_Position; +static float vFlat; +static float vCentroid; +static float vSample; +static float vNoperspective; + +struct Block +{ + nointerpolation float vFlat : TEXCOORD4; + centroid float vCentroid : TEXCOORD5; + sample float vSample : TEXCOORD6; + noperspective float vNoperspective : TEXCOORD7; +}; + +static Block vout; + +struct SPIRV_Cross_Output +{ + nointerpolation float vFlat : TEXCOORD0; + centroid float vCentroid : TEXCOORD1; + sample float vSample : TEXCOORD2; + noperspective float vNoperspective : TEXCOORD3; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; + vFlat = 0.0f; + vCentroid = 1.0f; + vSample = 2.0f; + vNoperspective = 3.0f; + vout.vFlat = 0.0f; + vout.vCentroid = 1.0f; + vout.vSample = 2.0f; + vout.vNoperspective = 3.0f; +} + +SPIRV_Cross_Output main(out Block stage_outputvout) +{ + vert_main(); + stage_outputvout = vout; + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vFlat = vFlat; + stage_output.vCentroid = vCentroid; + stage_output.vSample = vSample; + stage_output.vNoperspective = vNoperspective; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/read-from-row-major-array.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..03fa4f3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/read-from-row-major-array.vert @@ -0,0 +1,36 @@ +cbuffer Block : register(b0) +{ + column_major float2x3 _104_var[3][4] : packoffset(c0); +}; + + +static float4 gl_Position; +static float4 a_position; +static float v_vtxResult; + +struct SPIRV_Cross_Input +{ + float4 a_position : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float v_vtxResult : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = a_position; + v_vtxResult = ((float(abs(_104_var[0][0][0].x - 2.0f) < 0.0500000007450580596923828125f) * float(abs(_104_var[0][0][0].y - 6.0f) < 0.0500000007450580596923828125f)) * float(abs(_104_var[0][0][0].z - (-6.0f)) < 0.0500000007450580596923828125f)) * ((float(abs(_104_var[0][0][1].x) < 0.0500000007450580596923828125f) * float(abs(_104_var[0][0][1].y - 5.0f) < 0.0500000007450580596923828125f)) * float(abs(_104_var[0][0][1].z - 5.0f) < 0.0500000007450580596923828125f)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + a_position = stage_input.a_position; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.v_vtxResult = v_vtxResult; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/return-array.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/return-array.vert new file mode 100644 index 0000000..bd15755 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/return-array.vert @@ -0,0 +1,29 @@ +static float4 gl_Position; +static float4 vInput0; +static float4 vInput1; + +struct SPIRV_Cross_Input +{ + float4 vInput0 : TEXCOORD0; + float4 vInput1 : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 10.0f.xxxx + vInput1; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vInput0 = stage_input.vInput0; + vInput1 = stage_input.vInput1; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/sampler-buffers.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/sampler-buffers.vert new file mode 100644 index 0000000..3652185 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/sampler-buffers.vert @@ -0,0 +1,22 @@ +Buffer uFloatSampler : register(t1); +Buffer uIntSampler : register(t2); +Buffer uUintSampler : register(t3); + +static float4 gl_Position; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = (uFloatSampler.Load(20) + asfloat(uIntSampler.Load(40))) + asfloat(uUintSampler.Load(60)); +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/struct-composite-decl.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/struct-composite-decl.vert new file mode 100644 index 0000000..76bd349 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/struct-composite-decl.vert @@ -0,0 +1,44 @@ +struct VOut +{ + float4 a; + float4 b; + float4 c; + float4 d; +}; + +static VOut vout; +static float4 a; +static float4 b; +static float4 c; +static float4 d; + +struct SPIRV_Cross_Input +{ + float4 a : TEXCOORD0; + float4 b : TEXCOORD1; + float4 c : TEXCOORD2; + float4 d : TEXCOORD3; +}; + +struct SPIRV_Cross_Output +{ + VOut vout : TEXCOORD0; +}; + +void vert_main() +{ + VOut _26 = { a, b, c, d }; + vout = _26; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + a = stage_input.a; + b = stage_input.b; + c = stage_input.c; + d = stage_input.d; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.vout = vout; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/texture_buffer.vert b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/texture_buffer.vert new file mode 100644 index 0000000..1c92f6f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-hlsl/vert/texture_buffer.vert @@ -0,0 +1,21 @@ +Buffer uSamp : register(t4); +RWBuffer uSampo : register(u5); + +static float4 gl_Position; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = uSamp.Load(10) + uSampo[100]; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/amd/shader_trinary_minmax.msl21.comp b/third_party/spirv-cross/reference/opt/shaders-msl/amd/shader_trinary_minmax.msl21.comp new file mode 100644 index 0000000..9c33c22 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/amd/shader_trinary_minmax.msl21.comp @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(64u, 1u, 1u); + +kernel void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..3dc6fff --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,27 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct u0_counters +{ + uint c; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +kernel void main0(device u0_counters& u0_counter [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _29 = atomic_fetch_sub_explicit((device atomic_uint*)&u0_counter.c, 1, memory_order_relaxed); + u0.write(uint4(uint(int(gl_GlobalInvocationID.x))), spvTexelBufferCoord(as_type(as_type(_29)))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..6f42bb9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,27 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct u0_counters +{ + uint c; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +kernel void main0(device u0_counters& u0_counter [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _29 = atomic_fetch_add_explicit((device atomic_uint*)&u0_counter.c, 1, memory_order_relaxed); + u0.write(uint4(uint(int(gl_GlobalInvocationID.x))), spvTexelBufferCoord(as_type(as_type(_29)))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_iadd.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_iadd.asm.comp new file mode 100644 index 0000000..ad61d75 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_iadd.asm.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& restrict _5 [[buffer(0)]], device _4& restrict _6 [[buffer(1)]]) +{ + _6._m0 = _5._m1 + uint4(_5._m0); + _6._m0 = uint4(_5._m0) + _5._m1; + _6._m0 = _5._m1 + _5._m1; + _6._m0 = uint4(_5._m0 + _5._m0); + _6._m1 = int4(_5._m1 + _5._m1); + _6._m1 = _5._m0 + _5._m0; + _6._m1 = int4(_5._m1) + _5._m0; + _6._m1 = _5._m0 + int4(_5._m1); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..31c71da --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& restrict _5 [[buffer(0)]], device _4& restrict _6 [[buffer(1)]]) +{ + _6._m0 = select(uint4(0u), uint4(1u), int4(_5._m1) < _5._m0); + _6._m0 = select(uint4(0u), uint4(1u), int4(_5._m1) <= _5._m0); + _6._m0 = select(uint4(0u), uint4(1u), _5._m1 < uint4(_5._m0)); + _6._m0 = select(uint4(0u), uint4(1u), _5._m1 <= uint4(_5._m0)); + _6._m0 = select(uint4(0u), uint4(1u), int4(_5._m1) > _5._m0); + _6._m0 = select(uint4(0u), uint4(1u), int4(_5._m1) >= _5._m0); + _6._m0 = select(uint4(0u), uint4(1u), _5._m1 > uint4(_5._m0)); + _6._m0 = select(uint4(0u), uint4(1u), _5._m1 >= uint4(_5._m0)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_sar.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_sar.asm.comp new file mode 100644 index 0000000..4176830 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_sar.asm.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& _5 [[buffer(0)]], device _4& _6 [[buffer(1)]]) +{ + int4 _22 = _5._m0; + uint4 _23 = _5._m1; + _6._m0 = uint4(int4(_23) >> _22); + _6._m0 = uint4(_22 >> int4(_23)); + _6._m0 = uint4(int4(_23) >> int4(_23)); + _6._m0 = uint4(_22 >> _22); + _6._m1 = int4(_23) >> int4(_23); + _6._m1 = _22 >> _22; + _6._m1 = int4(_23) >> _22; + _6._m1 = _22 >> int4(_23); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_sdiv.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_sdiv.asm.comp new file mode 100644 index 0000000..6b80dff --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_sdiv.asm.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& _5 [[buffer(0)]], device _4& _6 [[buffer(1)]]) +{ + int4 _22 = _5._m0; + uint4 _23 = _5._m1; + _6._m0 = uint4(int4(_23) / _22); + _6._m0 = uint4(_22 / int4(_23)); + _6._m0 = uint4(int4(_23) / int4(_23)); + _6._m0 = uint4(_22 / _22); + _6._m1 = int4(_23) / int4(_23); + _6._m1 = _22 / _22; + _6._m1 = int4(_23) / _22; + _6._m1 = _22 / int4(_23); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_slr.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_slr.asm.comp new file mode 100644 index 0000000..1dfca39 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/bitcast_slr.asm.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& _5 [[buffer(0)]], device _4& _6 [[buffer(1)]]) +{ + int4 _22 = _5._m0; + uint4 _23 = _5._m1; + _6._m0 = _23 >> uint4(_22); + _6._m0 = uint4(_22) >> _23; + _6._m0 = _23 >> _23; + _6._m0 = uint4(_22) >> uint4(_22); + _6._m1 = int4(_23 >> _23); + _6._m1 = int4(uint4(_22) >> uint4(_22)); + _6._m1 = int4(_23 >> uint4(_22)); + _6._m1 = int4(uint4(_22) >> _23); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..6dcc14e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,46 @@ +#include +#include + +using namespace metal; + +struct A +{ + int a; + int b; +}; + +struct A_1 +{ + A Data[1]; +}; + +struct A_2 +{ + int a; + int b; + char _m0_final_padding[8]; +}; + +struct A_3 +{ + A_2 Data[1024]; +}; + +struct B +{ + A Data[1]; +}; + +struct B_1 +{ + A_2 Data[1024]; +}; + +kernel void main0(device A_1& C1 [[buffer(0)]], constant A_3& C2 [[buffer(1)]], device B& C3 [[buffer(2)]], constant B_1& C4 [[buffer(3)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + C1.Data[gl_GlobalInvocationID.x].a = C2.Data[gl_GlobalInvocationID.x].a; + C1.Data[gl_GlobalInvocationID.x].b = C2.Data[gl_GlobalInvocationID.x].b; + C3.Data[gl_GlobalInvocationID.x].a = C4.Data[gl_GlobalInvocationID.x].a; + C3.Data[gl_GlobalInvocationID.x].b = C4.Data[gl_GlobalInvocationID.x].b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp new file mode 100644 index 0000000..bb51fd7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp @@ -0,0 +1,29 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct cb5_struct +{ + float4 _m0[5]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +kernel void main0(constant cb5_struct& cb0_5 [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + uint _44 = as_type(as_type(int(gl_LocalInvocationID.x) << 4)) >> 2u; + uint4 _51 = as_type(cb0_5._m0[uint(as_type(as_type(int(gl_LocalInvocationID.x)))) + 1u]); + u0.write(_51.xxxx, spvTexelBufferCoord(_44)); + u0.write(_51.yyyy, spvTexelBufferCoord((_44 + 1u))); + u0.write(_51.zzzz, spvTexelBufferCoord((_44 + 2u))); + u0.write(_51.wwww, spvTexelBufferCoord((_44 + 3u))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/buffer-write.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/buffer-write.asm.comp new file mode 100644 index 0000000..8c9e23a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/buffer-write.asm.comp @@ -0,0 +1,24 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct cb +{ + float value; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +kernel void main0(constant cb& _6 [[buffer(0)]], texture2d _buffer [[texture(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + _buffer.write(float4(_6.value), spvTexelBufferCoord(((32u * gl_WorkGroupID.x) + gl_LocalInvocationIndex))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..59fc03a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,9 @@ +#include +#include + +using namespace metal; + +kernel void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp new file mode 100644 index 0000000..fb97d0d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp @@ -0,0 +1,10 @@ +#include +#include + +using namespace metal; + +kernel void main0(texture2d TargetTexture [[texture(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +{ + TargetTexture.write((TargetTexture.read(uint2(gl_WorkGroupID.xy)).xy + float2(1.0)).xyyy, uint2((gl_WorkGroupID.xy + uint2(1u)))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/multiple-entry.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/multiple-entry.asm.comp new file mode 100644 index 0000000..25ccf62 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/multiple-entry.asm.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct _6 +{ + int4 _m0; + uint4 _m1; +}; + +struct _7 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _6& restrict _8 [[buffer(0)]], device _7& restrict _9 [[buffer(1)]]) +{ + _9._m0 = _8._m1 + uint4(_8._m0); + _9._m0 = uint4(_8._m0) + _8._m1; + _9._m0 = _8._m1 + _8._m1; + _9._m0 = uint4(_8._m0 + _8._m0); + _9._m1 = int4(_8._m1 + _8._m1); + _9._m1 = _8._m0 + _8._m0; + _9._m1 = int4(_8._m1) + _8._m0; + _9._m1 = _8._m0 + int4(_8._m1); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/quantize.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/quantize.asm.comp new file mode 100644 index 0000000..1839ec7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/quantize.asm.comp @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct SSBO0 +{ + float scalar; + float2 vec2_val; + float3 vec3_val; + float4 vec4_val; +}; + +kernel void main0(device SSBO0& _4 [[buffer(0)]]) +{ + _4.scalar = float(half(_4.scalar)); + _4.vec2_val = float2(half2(_4.vec2_val)); + _4.vec3_val = float3(half3(_4.vec3_val)); + _4.vec4_val = float4(half4(_4.vec4_val)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/relaxed-block-layout.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/relaxed-block-layout.asm.comp new file mode 100644 index 0000000..6728a4e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/relaxed-block-layout.asm.comp @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct foo +{ + uint bar; + packed_float3 baz; + uchar quux; + packed_uchar4 blah; + packed_half2 wibble; +}; + +kernel void main0(device foo& _8 [[buffer(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_NumWorkGroups [[threadgroups_per_grid]]) +{ + _8.bar = gl_LocalInvocationID.x; + _8.baz = float3(gl_GlobalInvocationID); + _8.blah = uchar4(uint4(uint4(uchar4(_8.blah)).xyz + gl_WorkGroupID, 0u)); + _8.wibble = half2(float2(half2(_8.wibble)) * float2(gl_NumWorkGroups.xy)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp new file mode 100644 index 0000000..1e2880f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float a; +}; + +constant uint _5_tmp [[function_constant(10)]]; +constant uint _5 = is_function_constant_defined(_5_tmp) ? _5_tmp : 9u; +constant uint _6_tmp [[function_constant(12)]]; +constant uint _6 = is_function_constant_defined(_6_tmp) ? _6_tmp : 4u; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(_5, 20u, _6); + +kernel void main0(device SSBO& _4 [[buffer(0)]]) +{ + _4.a += 1.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp new file mode 100644 index 0000000..5b1f0a0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp @@ -0,0 +1,16 @@ +#include +#include + +using namespace metal; + +struct bufA +{ + uint _data[1]; +}; + +kernel void main0(device bufA& bufA_1 [[buffer(0)]], device bufA& bufB [[buffer(1)]]) +{ + bufA_1._data[0] = 0u; + bufB._data[0] = 0u; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/variable-pointers-2.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/variable-pointers-2.asm.comp new file mode 100644 index 0000000..a276b40 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/variable-pointers-2.asm.comp @@ -0,0 +1,51 @@ +#include +#include + +using namespace metal; + +struct foo +{ + int a[128]; + uint b; + float2 c; +}; + +struct bar +{ + int d; +}; + +kernel void main0(device foo& buf [[buffer(0)]], constant bar& cb [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + bool _71 = cb.d != 0; + device foo* _72 = _71 ? &buf : nullptr; + device foo* _67 = _72; + device foo* _45 = _72; + thread uint3* _79 = _71 ? &gl_GlobalInvocationID : &gl_LocalInvocationID; + thread uint3* _74 = _79; + device int* _52; + device int* _55; + _52 = &_72->a[0u]; + _55 = &buf.a[0u]; + int _57; + int _58; + for (;;) + { + _57 = *_52; + _58 = *_55; + if (_57 != _58) + { + int _66 = (_57 + _58) + int((*_79).x); + *_52 = _66; + *_55 = _66; + _52 = &_52[1u]; + _55 = &_55[1u]; + continue; + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp new file mode 100644 index 0000000..e186173 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct foo +{ + int a; +}; + +struct bar +{ + int b; +}; + +kernel void main0(device foo& x [[buffer(0)]], device bar& y [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + device int* _47 = (gl_GlobalInvocationID.x != 0u) ? &x.a : &y.b; + device int* _40 = _47; + device int* _33 = _47; + int _37 = x.a; + *_47 = 0; + y.b = _37 + _37; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp new file mode 100644 index 0000000..afbcadd --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct cb1_struct +{ + float4 _RESERVED_IDENTIFIER_FIXUP_m0[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(16u, 16u, 1u); + +kernel void main0(constant cb1_struct& cb0_1 [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + int2 _46 = int2(u0.get_width(), u0.get_height()) >> int2(uint2(4u)); + int _98; + _98 = 0; + for (; _98 < _46.y; _98++) + { + for (int _99 = 0; _99 < _46.x; ) + { + u0.write(cb0_1._RESERVED_IDENTIFIER_FIXUP_m0[0].xxxx, uint2(((_46 * int3(gl_LocalInvocationID).xy) + int2(_98, _99)))); + _99++; + continue; + } + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp new file mode 100644 index 0000000..e572525 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct cb1_struct +{ + float4 _RESERVED_IDENTIFIER_FIXUP_m0[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(16u, 16u, 1u); + +kernel void main0(constant cb1_struct& cb0_1 [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + int2 _40 = int2(u0.get_width(), u0.get_height()) >> int2(uint2(4u)); + int _80; + _80 = 0; + for (; _80 < _40.y; _80++) + { + for (int _81 = 0; _81 < _40.x; ) + { + u0.write(cb0_1._RESERVED_IDENTIFIER_FIXUP_m0[0].xxxx, uint2(((_40 * int3(gl_LocalInvocationID).xy) + int2(_80, _81)))); + _81++; + continue; + } + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag new file mode 100644 index 0000000..703dd0a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTex [[texture(0)]], sampler uSampler [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample(uSampler, in.vUV); + out.FragColor += uTex.sample(uSampler, in.vUV, int2(1)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/default-member-names.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/default-member-names.asm.frag new file mode 100644 index 0000000..82b41d1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/default-member-names.asm.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +constant float _49 = {}; + +struct main0_out +{ + float4 m_3 [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.m_3 = float4(_49); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag new file mode 100644 index 0000000..1870f67 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag @@ -0,0 +1,48 @@ +#include +#include + +using namespace metal; + +struct _4 +{ + float4 _m0; +}; + +struct _6 +{ + int _m0; +}; + +struct _7 +{ + float4 _m0; +}; + +struct main0_out +{ + float4 m_3 [[color(0)]]; +}; + +fragment main0_out main0(const device _4* _5_0 [[buffer(0)]], const device _4* _5_1 [[buffer(1)]], const device _4* _5_2 [[buffer(2)]], const device _4* _5_3 [[buffer(3)]], constant _6& _20 [[buffer(4)]], constant _7* _8_0 [[buffer(5)]], constant _7* _8_1 [[buffer(6)]], constant _7* _8_2 [[buffer(7)]], constant _7* _8_3 [[buffer(8)]]) +{ + const device _4* _5[] = + { + _5_0, + _5_1, + _5_2, + _5_3, + }; + + constant _7* _8[] = + { + _8_0, + _8_1, + _8_2, + _8_3, + }; + + main0_out out = {}; + out.m_3 = _5[_20._m0]->_m0 + (_8[_20._m0]->_m0 * float4(0.20000000298023223876953125)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag new file mode 100644 index 0000000..b64ccab --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 o1 [[color(1)]]; + float4 o3 [[color(3)]]; + float4 o6 [[color(6)]]; + float4 o7 [[color(7)]]; +}; + +fragment main0_out main0() +{ + float4 o0; + float4 o2; + float4 o4; + float4 o5; + float gl_FragDepth; + int gl_FragStencilRefARB; + main0_out out = {}; + o0 = float4(0.0, 0.0, 0.0, 1.0); + out.o1 = float4(1.0, 0.0, 0.0, 1.0); + o2 = float4(0.0, 1.0, 0.0, 1.0); + out.o3 = float4(0.0, 0.0, 1.0, 1.0); + o4 = float4(1.0, 0.0, 1.0, 0.5); + o5 = float4(0.25); + out.o6 = float4(0.75); + out.o7 = float4(1.0); + gl_FragDepth = 0.89999997615814208984375; + gl_FragStencilRefARB = uint(127); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..92ac1d9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/empty-struct.asm.frag @@ -0,0 +1,9 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag new file mode 100644 index 0000000..09f8ed8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + packed_float3 a; + float b; +}; + +struct buf +{ + Foo results[16]; + float4 bar; +}; + +struct main0_out +{ + float4 _entryPointOutput [[color(0)]]; +}; + +fragment main0_out main0(constant buf& _11 [[buffer(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + int _68 = int(gl_FragCoord.x) % 16; + out._entryPointOutput = float4(dot(float3(_11.results[_68].a), _11.bar.xyz), _11.results[_68].b, 0.0, 0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/frem.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/frem.asm.frag new file mode 100644 index 0000000..ebc73d5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/frem.asm.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vA [[user(locn0)]]; + float4 vB [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = fmod(in.vA, in.vB); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..64edee8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(10.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..0d691b3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + int2 Size [[color(0)]]; +}; + +fragment main0_out main0(texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.Size = int2(uTexture.get_width(), uTexture.get_height()) + int2(uTexture.get_width(1), uTexture.get_height(1)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..dd977a9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,50 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 v0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uImage [[texture(0)]], sampler uImageSmplr [[sampler(0)]]) +{ + main0_out out = {}; + float phi; + float4 _36; + int _51; + _51 = 0; + phi = 1.0; + _36 = float4(1.0, 2.0, 1.0, 2.0); + for (;;) + { + out.FragColor = _36; + if (_51 < 4) + { + if (in.v0[_51] > 0.0) + { + float2 _48 = float2(phi); + _51++; + phi += 2.0; + _36 = uImage.sample(uImageSmplr, _48, level(0.0)); + continue; + } + else + { + break; + } + } + else + { + break; + } + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..8537dac --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float3 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float3(as_type(0x7f800000u), as_type(0xff800000u), as_type(0x7fc00000u)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag new file mode 100644 index 0000000..41472ad --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag @@ -0,0 +1,47 @@ +#include +#include + +using namespace metal; + +struct Input +{ + float2 v0; + float2 v1; + float3 v2; + float4 v3; + float v4; + float v5; + float v6; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 Input_v0 [[user(locn0)]]; + float2 Input_v1 [[user(locn1), center_no_perspective]]; + float3 Input_v2 [[user(locn2), centroid_perspective]]; + float4 Input_v3 [[user(locn3), centroid_no_perspective]]; + float Input_v4 [[user(locn4), sample_perspective]]; + float Input_v5 [[user(locn5), sample_no_perspective]]; + float Input_v6 [[user(locn6), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Input inp = {}; + inp.v0 = in.Input_v0; + inp.v1 = in.Input_v1; + inp.v2 = in.Input_v2; + inp.v3 = in.Input_v3; + inp.v4 = in.Input_v4; + inp.v5 = in.Input_v5; + inp.v6 = in.Input_v6; + out.FragColor = float4(inp.v0.x + inp.v1.y, inp.v2.xy, ((inp.v3.w * inp.v4) + inp.v5) - inp.v6); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..f70254a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,82 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float vColor [[user(locn0)]]; +}; + +#line 8 "test.frag" +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; +#line 8 "test.frag" + out.FragColor = 1.0; +#line 9 "test.frag" + out.FragColor = 2.0; +#line 10 "test.frag" + if (in.vColor < 0.0) + { +#line 12 "test.frag" + out.FragColor = 3.0; + } + else + { +#line 16 "test.frag" + out.FragColor = 4.0; + } + for (int _127 = 0; float(_127) < (40.0 + in.vColor); ) + { +#line 21 "test.frag" + out.FragColor += 0.20000000298023223876953125; +#line 22 "test.frag" + out.FragColor += 0.300000011920928955078125; + _127 += (int(in.vColor) + 5); + continue; + } + switch (int(in.vColor)) + { + case 0: + { +#line 28 "test.frag" + out.FragColor += 0.20000000298023223876953125; +#line 29 "test.frag" + break; + } + case 1: + { +#line 32 "test.frag" + out.FragColor += 0.4000000059604644775390625; +#line 33 "test.frag" + break; + } + default: + { +#line 36 "test.frag" + out.FragColor += 0.800000011920928955078125; +#line 37 "test.frag" + break; + } + } + for (;;) + { + out.FragColor += (10.0 + in.vColor); +#line 43 "test.frag" + if (out.FragColor < 100.0) + { + } + else + { + break; + } + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/locations-components.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/locations-components.asm.frag new file mode 100644 index 0000000..07ab82a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/locations-components.asm.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 o0 [[color(0)]]; +}; + +struct main0_in +{ + float2 m_2 [[user(locn1)]]; + float m_3 [[user(locn1_2)]]; + float m_4 [[user(locn2), flat]]; + uint m_5 [[user(locn2_1)]]; + uint m_6 [[user(locn2_2)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 v1; + v1 = float4(in.m_2.x, in.m_2.y, v1.z, v1.w); + v1.z = in.m_3; + float4 v2; + v2.x = in.m_4; + v2.y = as_type(in.m_5); + v2.z = as_type(in.m_6); + out.o0.y = float(as_type(as_type(as_type(v2.y) + as_type(v2.z)))); + out.o0.x = v1.y + v2.x; + out.o0 = float4(out.o0.x, out.o0.y, v1.z, v1.x); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..7269766 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,94 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _46 = spvUnsafeArray({ 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0 }); +constant spvUnsafeArray _76 = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); +constant spvUnsafeArray _90 = spvUnsafeArray({ float4(20.0), float4(30.0), float4(50.0), float4(60.0) }); + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + spvUnsafeArray foobar = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); + spvUnsafeArray baz = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); + main0_out out = {}; + out.FragColor = _46[in.index]; + if (in.index < 10) + { + out.FragColor += _46[in.index ^ 1]; + } + else + { + out.FragColor += _46[in.index & 1]; + } + bool _99 = in.index > 30; + if (_99) + { + out.FragColor += _76[in.index & 3].y; + } + else + { + out.FragColor += _76[in.index & 1].x; + } + if (_99) + { + foobar[1].z = 20.0; + } + int _37 = in.index & 3; + out.FragColor += foobar[_37].z; + baz = _90; + out.FragColor += baz[_37].z; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/min-lod.msl22.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/min-lod.msl22.asm.frag new file mode 100644 index 0000000..5193b2c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/min-lod.msl22.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uSampler.sample(uSamplerSmplr, in.vUV, min_lod_clamp(4.0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/op-constant-null.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/op-constant-null.asm.frag new file mode 100644 index 0000000..e1badb5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/op-constant-null.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = 0.0; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/op-image-sampled-image.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/op-image-sampled-image.asm.frag new file mode 100644 index 0000000..45f0ca5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/op-image-sampled-image.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct push_cb +{ + float4 cb0[1]; +}; + +struct main0_out +{ + float4 o0 [[color(0)]]; +}; + +fragment main0_out main0(constant push_cb& _19 [[buffer(0)]], texture2d t0 [[texture(0)]], sampler dummy_sampler [[sampler(0)]]) +{ + main0_out out = {}; + out.o0 = t0.read(uint2(as_type(_19.cb0[0u].zw)) + uint2(int2(-1, -2)), as_type(0.0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..3858f6d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct Registers +{ + float foo; +}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant Registers& registers [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = 10.0 + registers.foo; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/phi-loop-variable.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/phi-loop-variable.asm.frag new file mode 100644 index 0000000..92ac1d9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/phi-loop-variable.asm.frag @@ -0,0 +1,9 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag new file mode 100644 index 0000000..a401789 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag @@ -0,0 +1,181 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct _13 +{ + float4 x; + float4 y; + float4 z; + spvUnsafeArray u; + spvUnsafeArray v; + spvUnsafeArray w; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + interpolant foo [[user(locn0)]]; + interpolant bar [[user(locn1)]]; + interpolant baz [[user(locn2)]]; + int sid [[user(locn3)]]; + interpolant a_0 [[user(locn4)]]; + interpolant a_1 [[user(locn5)]]; + interpolant b_0 [[user(locn6)]]; + interpolant b_1 [[user(locn7)]]; + interpolant c_0 [[user(locn8)]]; + interpolant c_1 [[user(locn9)]]; + interpolant m_13_x [[user(locn10)]]; + interpolant m_13_y [[user(locn11)]]; + interpolant m_13_z [[user(locn12)]]; + interpolant m_13_u_0 [[user(locn13)]]; + interpolant m_13_u_1 [[user(locn14)]]; + interpolant m_13_v_0 [[user(locn15)]]; + interpolant m_13_v_1 [[user(locn16)]]; + interpolant m_13_w_0 [[user(locn17)]]; + interpolant m_13_w_1 [[user(locn18)]]; + interpolant m_13_w_2 [[user(locn19)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + spvUnsafeArray a = {}; + _13 s = {}; + spvUnsafeArray b = {}; + spvUnsafeArray c = {}; + a[0] = in.a_0.interpolate_at_center(); + a[1] = in.a_1.interpolate_at_center(); + s.x = in.m_13_x.interpolate_at_center(); + s.y = in.m_13_y.interpolate_at_centroid(); + s.z = in.m_13_z.interpolate_at_sample(gl_SampleID); + s.u[0] = in.m_13_u_0.interpolate_at_centroid(); + s.u[1] = in.m_13_u_1.interpolate_at_centroid(); + s.v[0] = in.m_13_v_0.interpolate_at_sample(gl_SampleID); + s.v[1] = in.m_13_v_1.interpolate_at_sample(gl_SampleID); + s.w[0] = in.m_13_w_0.interpolate_at_center(); + s.w[1] = in.m_13_w_1.interpolate_at_center(); + s.w[2] = in.m_13_w_2.interpolate_at_center(); + b[0] = in.b_0.interpolate_at_centroid(); + b[1] = in.b_1.interpolate_at_centroid(); + c[0] = in.c_0.interpolate_at_sample(gl_SampleID); + c[1] = in.c_1.interpolate_at_sample(gl_SampleID); + out.FragColor = in.foo.interpolate_at_center(); + out.FragColor += in.foo.interpolate_at_centroid(); + out.FragColor += in.foo.interpolate_at_sample(in.sid); + out.FragColor += in.foo.interpolate_at_offset(float2(0.100000001490116119384765625) + 0.4375); + float3 _65 = out.FragColor.xyz + in.bar.interpolate_at_centroid(); + out.FragColor = float4(_65.x, _65.y, _65.z, out.FragColor.w); + float3 _71 = out.FragColor.xyz + in.bar.interpolate_at_centroid(); + out.FragColor = float4(_71.x, _71.y, _71.z, out.FragColor.w); + float3 _78 = out.FragColor.xyz + in.bar.interpolate_at_sample(in.sid); + out.FragColor = float4(_78.x, _78.y, _78.z, out.FragColor.w); + float3 _84 = out.FragColor.xyz + in.bar.interpolate_at_offset(float2(-0.100000001490116119384765625) + 0.4375); + out.FragColor = float4(_84.x, _84.y, _84.z, out.FragColor.w); + float2 _91 = out.FragColor.xy + b[0]; + out.FragColor = float4(_91.x, _91.y, out.FragColor.z, out.FragColor.w); + float2 _98 = out.FragColor.xy + in.b_1.interpolate_at_centroid(); + out.FragColor = float4(_98.x, _98.y, out.FragColor.z, out.FragColor.w); + float2 _105 = out.FragColor.xy + in.b_0.interpolate_at_sample(2); + out.FragColor = float4(_105.x, _105.y, out.FragColor.z, out.FragColor.w); + float2 _112 = out.FragColor.xy + in.b_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + out.FragColor = float4(_112.x, _112.y, out.FragColor.z, out.FragColor.w); + float2 _119 = out.FragColor.xy + c[0]; + out.FragColor = float4(_119.x, _119.y, out.FragColor.z, out.FragColor.w); + float2 _127 = out.FragColor.xy + in.c_1.interpolate_at_centroid().xy; + out.FragColor = float4(_127.x, _127.y, out.FragColor.z, out.FragColor.w); + float2 _135 = out.FragColor.xy + in.c_0.interpolate_at_sample(2).yx; + out.FragColor = float4(_135.x, _135.y, out.FragColor.z, out.FragColor.w); + float2 _143 = out.FragColor.xy + in.c_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375).xx; + out.FragColor = float4(_143.x, _143.y, out.FragColor.z, out.FragColor.w); + out.FragColor += s.x; + out.FragColor += in.m_13_x.interpolate_at_centroid(); + out.FragColor += in.m_13_x.interpolate_at_sample(in.sid); + out.FragColor += in.m_13_x.interpolate_at_offset(float2(0.100000001490116119384765625) + 0.4375); + out.FragColor += s.y; + out.FragColor += in.m_13_y.interpolate_at_centroid(); + out.FragColor += in.m_13_y.interpolate_at_sample(in.sid); + out.FragColor += in.m_13_y.interpolate_at_offset(float2(-0.100000001490116119384765625) + 0.4375); + float2 _184 = out.FragColor.xy + s.v[0]; + out.FragColor = float4(_184.x, _184.y, out.FragColor.z, out.FragColor.w); + float2 _191 = out.FragColor.xy + in.m_13_v_1.interpolate_at_centroid(); + out.FragColor = float4(_191.x, _191.y, out.FragColor.z, out.FragColor.w); + float2 _198 = out.FragColor.xy + in.m_13_v_0.interpolate_at_sample(2); + out.FragColor = float4(_198.x, _198.y, out.FragColor.z, out.FragColor.w); + float2 _205 = out.FragColor.xy + in.m_13_v_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + out.FragColor = float4(_205.x, _205.y, out.FragColor.z, out.FragColor.w); + out.FragColor.x += s.w[0]; + out.FragColor.x += in.m_13_w_1.interpolate_at_centroid(); + out.FragColor.x += in.m_13_w_0.interpolate_at_sample(2); + out.FragColor.x += in.m_13_w_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + float2 _328 = out.FragColor.xy + in.baz.interpolate_at_sample(gl_SampleID); + out.FragColor = float4(_328.x, _328.y, out.FragColor.z, out.FragColor.w); + out.FragColor.x += in.baz.interpolate_at_centroid().x; + out.FragColor.y += in.baz.interpolate_at_sample(3).y; + out.FragColor.z += in.baz.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375).y; + float2 _353 = out.FragColor.xy + in.a_1.interpolate_at_centroid(); + out.FragColor = float4(_353.x, _353.y, out.FragColor.z, out.FragColor.w); + float2 _360 = out.FragColor.xy + in.a_0.interpolate_at_sample(2); + out.FragColor = float4(_360.x, _360.y, out.FragColor.z, out.FragColor.w); + float2 _367 = out.FragColor.xy + in.a_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + out.FragColor = float4(_367.x, _367.y, out.FragColor.z, out.FragColor.w); + out.FragColor += s.z; + float2 _379 = out.FragColor.xy + in.m_13_z.interpolate_at_centroid().yy; + out.FragColor = float4(_379.x, _379.y, out.FragColor.z, out.FragColor.w); + float2 _387 = out.FragColor.yz + in.m_13_z.interpolate_at_sample(3).xy; + out.FragColor = float4(out.FragColor.x, _387.x, _387.y, out.FragColor.w); + float2 _395 = out.FragColor.zw + in.m_13_z.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375).wx; + out.FragColor = float4(out.FragColor.x, out.FragColor.y, _395.x, _395.y); + out.FragColor += s.u[0]; + out.FragColor += in.m_13_u_1.interpolate_at_centroid(); + out.FragColor += in.m_13_u_0.interpolate_at_sample(2); + out.FragColor += in.m_13_u_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..aed8fd3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float out_var_SV_Target [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d g_Texture [[texture(0)]], sampler g_Sampler [[sampler(0)]], sampler g_CompareSampler [[sampler(1)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = float4(g_Texture.sample(g_Sampler, in.in_var_TEXCOORD0)).x + g_Texture.sample_compare(g_CompareSampler, in.in_var_TEXCOORD0, 0.5, level(0.0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..4c0ea90 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,79 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct myType +{ + float data; +}; + +struct main0_out +{ + float4 o_color [[color(0)]]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +fragment main0_out main0(float4 gl_FragCoord [[position]]) +{ + spvUnsafeArray _21 = spvUnsafeArray({ myType{ 0.0 }, myType{ 1.0 }, myType{ 0.0 }, myType{ 1.0 }, myType{ 0.0 } }); + + main0_out out = {}; + if (_21[int(mod(gl_FragCoord.x, 4.0))].data > 0.0) + { + out.o_color = float4(0.0, 1.0, 0.0, 1.0); + } + else + { + out.o_color = float4(1.0, 0.0, 0.0, 1.0); + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/srem.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/srem.asm.frag new file mode 100644 index 0000000..f0cdd57 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/srem.asm.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int4 vA [[user(locn0)]]; + int4 vB [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = float4(in.vA - in.vB * (in.vA / in.vB)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..d59013d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,65 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _20 = spvUnsafeArray({ float4(1.0, 2.0, 3.0, 4.0), float4(10.0) }); + +struct main0_out +{ + float4 FragColors_0 [[color(0)]]; + float4 FragColors_1 [[color(1)]]; + float4 FragColor [[color(2)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + spvUnsafeArray FragColors = spvUnsafeArray({ float4(1.0, 2.0, 3.0, 4.0), float4(10.0) }); + out.FragColor = float4(5.0); + out.FragColors_0 = FragColors[0]; + out.FragColors_1 = FragColors[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..dd308c3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = uTexture.read(uint2(int2(gl_FragCoord.xy)), 0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-atomics.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-atomics.asm.frag new file mode 100644 index 0000000..ab5be64 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-atomics.asm.frag @@ -0,0 +1,121 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_StructuredBuffer_v4float +{ + spvUnsafeArray _m0; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_v4float& CulledObjectBoxBounds [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d RWShadowTileNumCulledObjects [[texture(2)]], device atomic_uint* RWShadowTileNumCulledObjects_atomic [[buffer(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float((_Globals.ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2(_Globals.ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _107 = _103 + 1u; + if (all(CulledObjectBoxBounds._m0[_107].xy > _96.xy) && all(CulledObjectBoxBounds._m0[_103].xyz < _102)) + { + float _122 = _96.x; + float _123 = _96.y; + spvUnsafeArray _73; + _73[0] = float3(_122, _123, -1000.0); + float _126 = _100.x; + _73[1] = float3(_126, _123, -1000.0); + float _129 = _100.y; + _73[2] = float3(_122, _129, -1000.0); + _73[3] = float3(_126, _129, -1000.0); + _73[4] = float3(_122, _123, 1.0); + _73[5] = float3(_126, _123, 1.0); + _73[6] = float3(_122, _129, 1.0); + _73[7] = float3(_126, _129, 1.0); + float3 _155; + float3 _158; + _155 = float3(-500000.0); + _158 = float3(500000.0); + for (int _160 = 0; _160 < 8; ) + { + float3 _166 = _73[_160] - (float3(0.5) * (CulledObjectBoxBounds._m0[_103].xyz + CulledObjectBoxBounds._m0[_107].xyz)); + float3 _170 = float3(dot(_166, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + _155 = fast::max(_155, _170); + _158 = fast::min(_158, _170); + _160++; + continue; + } + if (all(_158 < float3(1.0)) && all(_155 > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&RWShadowTileNumCulledObjects_atomic[(_78 * _Globals.ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-atomics.asm.graphics-robust-access.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-atomics.asm.graphics-robust-access.frag new file mode 100644 index 0000000..ca5e3ea --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-atomics.asm.graphics-robust-access.frag @@ -0,0 +1,122 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_StructuredBuffer_v4float +{ + spvUnsafeArray _m0; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvBufferSizeConstants [[buffer(25)]], const device type_StructuredBuffer_v4float& CulledObjectBoxBounds [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d RWShadowTileNumCulledObjects [[texture(2)]], device atomic_uint* RWShadowTileNumCulledObjects_atomic [[buffer(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + constant uint& CulledObjectBoxBoundsBufferSize = spvBufferSizeConstants[0]; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float((_Globals.ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2(_Globals.ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _186 = clamp(_103 + 1u, 0u, ((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u); + if (all(CulledObjectBoxBounds._m0[_186].xy > _96.xy) && all(CulledObjectBoxBounds._m0[clamp(_103, 0u, ((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u)].xyz < _102)) + { + float _122 = _96.x; + float _123 = _96.y; + spvUnsafeArray _73; + _73[0] = float3(_122, _123, -1000.0); + float _126 = _100.x; + _73[1] = float3(_126, _123, -1000.0); + float _129 = _100.y; + _73[2] = float3(_122, _129, -1000.0); + _73[3] = float3(_126, _129, -1000.0); + _73[4] = float3(_122, _123, 1.0); + _73[5] = float3(_126, _123, 1.0); + _73[6] = float3(_122, _129, 1.0); + _73[7] = float3(_126, _129, 1.0); + float3 _155; + float3 _158; + _155 = float3(-500000.0); + _158 = float3(500000.0); + for (int _160 = 0; _160 < 8; ) + { + float3 _166 = _73[int(clamp(uint(_160), uint(0), uint(7)))] - (float3(0.5) * (CulledObjectBoxBounds._m0[clamp(_103, 0u, ((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u)].xyz + CulledObjectBoxBounds._m0[_186].xyz)); + float3 _170 = float3(dot(_166, CulledObjectBoxBounds._m0[clamp(_103 + 2u, 0u, ((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u)].xyz), dot(_166, CulledObjectBoxBounds._m0[clamp(_103 + 3u, 0u, ((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u)].xyz), dot(_166, CulledObjectBoxBounds._m0[clamp(_103 + 4u, 0u, ((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u)].xyz)); + _155 = fast::max(_155, _170); + _158 = fast::min(_158, _170); + _160++; + continue; + } + if (all(_158 < float3(1.0)) && all(_155 > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&RWShadowTileNumCulledObjects_atomic[(_78 * _Globals.ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag new file mode 100644 index 0000000..9734582 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + half4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + half2 UV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = half4(uTexture.sample(uTextureSmplr, float2(in.UV))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/undef-variable-store.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/undef-variable-store.asm.frag new file mode 100644 index 0000000..a5380c5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/undef-variable-store.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 _entryPointOutput [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out._entryPointOutput = float4(1.0, 1.0, 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000..dc87406 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uShadow [[texture(0)]], depth2d uTexture [[texture(1)]], sampler uShadowSmplr [[sampler(0)]], sampler uSampler [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = uShadow.sample_compare(uShadowSmplr, in.vUV.xy, in.vUV.z) + uTexture.sample_compare(uSampler, in.vUV.xy, in.vUV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unord-relational-op.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unord-relational-op.asm.frag new file mode 100644 index 0000000..aee290f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unord-relational-op.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant float a_tmp [[function_constant(1)]]; +constant float a = is_function_constant_defined(a_tmp) ? a_tmp : 1.0; +constant float b_tmp [[function_constant(2)]]; +constant float b = is_function_constant_defined(b_tmp) ? b_tmp : 2.0; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(a + b); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..918a0b9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/unreachable.asm.frag @@ -0,0 +1,36 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int counter [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 _33; + for (;;) + { + if (in.counter == 10) + { + _33 = float4(10.0); + break; + } + else + { + _33 = float4(30.0); + break; + } + } + out.FragColor = _33; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag new file mode 100644 index 0000000..ed2076d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag @@ -0,0 +1,282 @@ +#include +#include + +using namespace metal; + +struct _28 +{ + float4 _m0; +}; + +struct _6 +{ + float4 _m0; + float _m1; + float4 _m2; +}; + +struct _10 +{ + float3 _m0; + packed_float3 _m1; + float _m2; + packed_float3 _m3; + float _m4; + packed_float3 _m5; + float _m6; + packed_float3 _m7; + float _m8; + packed_float3 _m9; + float _m10; + packed_float3 _m11; + float _m12; + float2 _m13; + float2 _m14; + packed_float3 _m15; + float _m16; + float _m17; + float _m18; + float _m19; + float _m20; + float4 _m21; + float4 _m22; + float4x4 _m23; + float4 _m24; +}; + +struct _18 +{ + float4x4 _m0; + float4x4 _m1; + float4x4 _m2; + float4x4 _m3; + float4 _m4; + float4 _m5; + float _m6; + float _m7; + float _m8; + float _m9; + packed_float3 _m10; + float _m11; + packed_float3 _m12; + float _m13; + packed_float3 _m14; + float _m15; + packed_float3 _m16; + float _m17; + float _m18; + float _m19; + float2 _m20; + float2 _m21; + float2 _m22; + float4 _m23; + float2 _m24; + float2 _m25; + float2 _m26; + char _m27_pad[8]; + packed_float3 _m27; + float _m28; + float _m29; + float _m30; + float _m31; + float _m32; + float2 _m33; + float _m34; + float _m35; + float3 _m36; + float4x4 _m37[2]; + float4 _m38[2]; +}; + +constant _28 _74 = {}; + +struct main0_out +{ + float4 m_5 [[color(0)]]; +}; + +fragment main0_out main0(constant _6& _7 [[buffer(0)]], constant _10& _11 [[buffer(1)]], constant _18& _19 [[buffer(2)]], texture2d _8 [[texture(0)]], texture2d _12 [[texture(1)]], texture2d _14 [[texture(2)]], sampler _9 [[sampler(0)]], sampler _13 [[sampler(1)]], sampler _15 [[sampler(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float2 _82 = gl_FragCoord.xy * _19._m23.xy; + float4 _88 = _7._m2 * _7._m0.xyxy; + float2 _95 = _88.xy; + float2 _96 = _88.zw; + float2 _97 = fast::clamp(_82 + (float2(0.0, -2.0) * _7._m0.xy), _95, _96); + float3 _109 = float3(_11._m5) * fast::clamp(_8.sample(_9, _97, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _113 = _12.sample(_13, _97, level(0.0)); + float _114 = _113.y; + float3 _129; + if (_114 > 0.0) + { + _129 = _109 + (_14.sample(_15, _97, level(0.0)).xyz * fast::clamp(_114 * _113.z, 0.0, 1.0)); + } + else + { + _129 = _109; + } + float2 _144 = fast::clamp(_82 + (float2(-1.0) * _7._m0.xy), _95, _96); + float3 _156 = float3(_11._m5) * fast::clamp(_8.sample(_9, _144, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _160 = _12.sample(_13, _144, level(0.0)); + float _161 = _160.y; + float3 _176; + if (_161 > 0.0) + { + _176 = _156 + (_14.sample(_15, _144, level(0.0)).xyz * fast::clamp(_161 * _160.z, 0.0, 1.0)); + } + else + { + _176 = _156; + } + float2 _191 = fast::clamp(_82 + (float2(0.0, -1.0) * _7._m0.xy), _95, _96); + float3 _203 = float3(_11._m5) * fast::clamp(_8.sample(_9, _191, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _207 = _12.sample(_13, _191, level(0.0)); + float _208 = _207.y; + float3 _223; + if (_208 > 0.0) + { + _223 = _203 + (_14.sample(_15, _191, level(0.0)).xyz * fast::clamp(_208 * _207.z, 0.0, 1.0)); + } + else + { + _223 = _203; + } + float2 _238 = fast::clamp(_82 + (float2(1.0, -1.0) * _7._m0.xy), _95, _96); + float3 _250 = float3(_11._m5) * fast::clamp(_8.sample(_9, _238, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _254 = _12.sample(_13, _238, level(0.0)); + float _255 = _254.y; + float3 _270; + if (_255 > 0.0) + { + _270 = _250 + (_14.sample(_15, _238, level(0.0)).xyz * fast::clamp(_255 * _254.z, 0.0, 1.0)); + } + else + { + _270 = _250; + } + float2 _285 = fast::clamp(_82 + (float2(-2.0, 0.0) * _7._m0.xy), _95, _96); + float3 _297 = float3(_11._m5) * fast::clamp(_8.sample(_9, _285, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _301 = _12.sample(_13, _285, level(0.0)); + float _302 = _301.y; + float3 _317; + if (_302 > 0.0) + { + _317 = _297 + (_14.sample(_15, _285, level(0.0)).xyz * fast::clamp(_302 * _301.z, 0.0, 1.0)); + } + else + { + _317 = _297; + } + float2 _332 = fast::clamp(_82 + (float2(-1.0, 0.0) * _7._m0.xy), _95, _96); + float3 _344 = float3(_11._m5) * fast::clamp(_8.sample(_9, _332, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _348 = _12.sample(_13, _332, level(0.0)); + float _349 = _348.y; + float3 _364; + if (_349 > 0.0) + { + _364 = _344 + (_14.sample(_15, _332, level(0.0)).xyz * fast::clamp(_349 * _348.z, 0.0, 1.0)); + } + else + { + _364 = _344; + } + float2 _379 = fast::clamp(_82, _95, _96); + float3 _391 = float3(_11._m5) * fast::clamp(_8.sample(_9, _379, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _395 = _12.sample(_13, _379, level(0.0)); + float _396 = _395.y; + float3 _411; + if (_396 > 0.0) + { + _411 = _391 + (_14.sample(_15, _379, level(0.0)).xyz * fast::clamp(_396 * _395.z, 0.0, 1.0)); + } + else + { + _411 = _391; + } + float2 _426 = fast::clamp(_82 + (float2(1.0, 0.0) * _7._m0.xy), _95, _96); + float3 _438 = float3(_11._m5) * fast::clamp(_8.sample(_9, _426, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _442 = _12.sample(_13, _426, level(0.0)); + float _443 = _442.y; + float3 _458; + if (_443 > 0.0) + { + _458 = _438 + (_14.sample(_15, _426, level(0.0)).xyz * fast::clamp(_443 * _442.z, 0.0, 1.0)); + } + else + { + _458 = _438; + } + float2 _473 = fast::clamp(_82 + (float2(2.0, 0.0) * _7._m0.xy), _95, _96); + float3 _485 = float3(_11._m5) * fast::clamp(_8.sample(_9, _473, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _489 = _12.sample(_13, _473, level(0.0)); + float _490 = _489.y; + float3 _505; + if (_490 > 0.0) + { + _505 = _485 + (_14.sample(_15, _473, level(0.0)).xyz * fast::clamp(_490 * _489.z, 0.0, 1.0)); + } + else + { + _505 = _485; + } + float2 _520 = fast::clamp(_82 + (float2(-1.0, 1.0) * _7._m0.xy), _95, _96); + float3 _532 = float3(_11._m5) * fast::clamp(_8.sample(_9, _520, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _536 = _12.sample(_13, _520, level(0.0)); + float _537 = _536.y; + float3 _552; + if (_537 > 0.0) + { + _552 = _532 + (_14.sample(_15, _520, level(0.0)).xyz * fast::clamp(_537 * _536.z, 0.0, 1.0)); + } + else + { + _552 = _532; + } + float2 _567 = fast::clamp(_82 + (float2(0.0, 1.0) * _7._m0.xy), _95, _96); + float3 _579 = float3(_11._m5) * fast::clamp(_8.sample(_9, _567, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _583 = _12.sample(_13, _567, level(0.0)); + float _584 = _583.y; + float3 _599; + if (_584 > 0.0) + { + _599 = _579 + (_14.sample(_15, _567, level(0.0)).xyz * fast::clamp(_584 * _583.z, 0.0, 1.0)); + } + else + { + _599 = _579; + } + float2 _614 = fast::clamp(_82 + _7._m0.xy, _95, _96); + float3 _626 = float3(_11._m5) * fast::clamp(_8.sample(_9, _614, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _630 = _12.sample(_13, _614, level(0.0)); + float _631 = _630.y; + float3 _646; + if (_631 > 0.0) + { + _646 = _626 + (_14.sample(_15, _614, level(0.0)).xyz * fast::clamp(_631 * _630.z, 0.0, 1.0)); + } + else + { + _646 = _626; + } + float2 _661 = fast::clamp(_82 + (float2(0.0, 2.0) * _7._m0.xy), _95, _96); + float3 _673 = float3(_11._m5) * fast::clamp(_8.sample(_9, _661, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _677 = _12.sample(_13, _661, level(0.0)); + float _678 = _677.y; + float3 _693; + if (_678 > 0.0) + { + _693 = _673 + (_14.sample(_15, _661, level(0.0)).xyz * fast::clamp(_678 * _677.z, 0.0, 1.0)); + } + else + { + _693 = _673; + } + float3 _702 = (((((((((((((_129 * 0.5).xyz + (_176 * 0.5)).xyz + (_223 * 0.75)).xyz + (_270 * 0.5)).xyz + (_317 * 0.5)).xyz + (_364 * 0.75)).xyz + (_411 * 1.0)).xyz + (_458 * 0.75)).xyz + (_505 * 0.5)).xyz + (_552 * 0.5)).xyz + (_599 * 0.75)).xyz + (_646 * 0.5)).xyz + (_693 * 0.5)).xyz * float3(0.125); + _28 _704 = _74; + _704._m0 = float4(_702.x, _702.y, _702.z, float4(0.0).w); + _28 _705 = _704; + _705._m0.w = 1.0; + out.m_5 = _705._m0; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc new file mode 100644 index 0000000..6c9903e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct TessLevels +{ + float inner0; + float inner1; + float outer0; + float outer1; + float outer2; + float outer3; +}; + +kernel void main0(const device TessLevels& sb_levels [[buffer(0)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]]) +{ + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(sb_levels.inner0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(sb_levels.outer0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(sb_levels.outer1); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(sb_levels.outer2); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc new file mode 100644 index 0000000..a5f316d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct TessLevels +{ + float inner0; + float inner1; + float outer0; + float outer1; + float outer2; + float outer3; +}; + +kernel void main0(const device TessLevels& sb_levels [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]]) +{ + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1]); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(sb_levels.inner0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(sb_levels.outer0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(sb_levels.outer1); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(sb_levels.outer2); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese b/third_party/spirv-cross/reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese new file mode 100644 index 0000000..83ef729 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float2 gl_TessLevelInner [[attribute(0)]]; + float4 gl_TessLevelOuter [[attribute(1)]]; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + out.gl_Position = float4(((gl_TessCoord.x * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.x) + (((1.0 - gl_TessCoord.x) * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.z), ((gl_TessCoord.y * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.y) + (((1.0 - gl_TessCoord.y) * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.w), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert new file mode 100644 index 0000000..1528c83 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +struct main0_in +{ + float4 pos [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.pos; + out.gl_ClipDistance[0] = in.pos.x; + out.gl_ClipDistance[1] = in.pos.y; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert new file mode 100644 index 0000000..1d68859 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; +}; + +struct main0_in +{ + float4 pos [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.pos; + out.gl_ClipDistance[0] = in.pos.x; + out.gl_ClipDistance[1] = in.pos.y; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/copy-memory-interface.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/copy-memory-interface.asm.vert new file mode 100644 index 0000000..63ab796 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/copy-memory-interface.asm.vert @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 o1 [[user(locn1)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 v0 [[attribute(0)]]; + float4 v1 [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.v0; + out.o1 = in.v1; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..f1b7a87 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct InstanceData +{ + float4x4 MATRIX_MVP; + float4 Color; +}; + +struct gInstanceData +{ + InstanceData _data[1]; +}; + +struct main0_out +{ + float4 _entryPointOutput_Color [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 PosL [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], const device gInstanceData& gInstanceData_1 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + out.gl_Position = float4(in.PosL, 1.0) * gInstanceData_1._data[gl_InstanceIndex].MATRIX_MVP; + out._entryPointOutput_Color = gInstanceData_1._data[gl_InstanceIndex].Color; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/fake-builtin-input.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/fake-builtin-input.asm.vert new file mode 100644 index 0000000..f9fcbc8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/fake-builtin-input.asm.vert @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float2 in_var_POSITION [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = float4(in.in_var_POSITION, 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/invariant.msl21.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/invariant.msl21.asm.vert new file mode 100644 index 0000000..88ca455 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/invariant.msl21.asm.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position, invariant]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + out.gl_Position = float4(1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert new file mode 100644 index 0000000..1926ff9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Struct +{ + uint flags[1]; +}; + +struct defaultUniformsVS +{ + Struct flags; + float4 uquad[4]; + float4x4 umatrix; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant defaultUniformsVS& _9 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.gl_Position = _9.umatrix * float4(_9.uquad[int(gl_VertexIndex)].x, _9.uquad[int(gl_VertexIndex)].y, in.a_position.z, in.a_position.w); + if (_9.flags.flags[0] != 0u) + { + out.gl_Position.z = 0.0; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert new file mode 100644 index 0000000..ee20638 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Struct +{ + uint2 flags[1]; +}; + +struct defaultUniformsVS +{ + Struct flags; + float4 uquad[4]; + float4x4 umatrix; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant defaultUniformsVS& _9 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.gl_Position = _9.umatrix * float4(_9.uquad[int(gl_VertexIndex)].x, _9.uquad[int(gl_VertexIndex)].y, in.a_position.z, in.a_position.w); + if (_9.flags.flags[0].x != 0u) + { + out.gl_Position.z = 0.0; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packing-test.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packing-test.asm.vert new file mode 100644 index 0000000..9e024c2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/packing-test.asm.vert @@ -0,0 +1,9 @@ +#include +#include + +using namespace metal; + +vertex void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert new file mode 100644 index 0000000..ed5c5f9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +constant int _7_tmp [[function_constant(201)]]; +constant int _7 = is_function_constant_defined(_7_tmp) ? _7_tmp : -10; +constant int _20 = (_7 + 2); +constant uint _8_tmp [[function_constant(202)]]; +constant uint _8 = is_function_constant_defined(_8_tmp) ? _8_tmp : 100u; +constant uint _25 = (_8 % 5u); +constant int4 _30 = int4(20, 30, _20, _20); +constant int2 _32 = int2(_30.y, _30.x); +constant int _33 = _30.y; + +struct main0_out +{ + int m_4 [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + float4 _63 = float4(0.0); + _63.y = float(_20); + float4 _66 = _63; + _66.z = float(_25); + float4 _52 = _66 + float4(_30); + float2 _56 = _52.xy + float2(_32); + out.gl_Position = float4(_56.x, _56.y, _52.z, _52.w); + out.m_4 = _33; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..d453aad --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + out.gl_Position = float4(float(gl_VertexIndex + gl_InstanceIndex)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/access-private-workgroup-in-function.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/access-private-workgroup-in-function.comp new file mode 100644 index 0000000..e57b2ea --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/access-private-workgroup-in-function.comp @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp new file mode 100644 index 0000000..18cfd68 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct SSBO3 +{ + float4 v; +}; + +struct SSBO0 +{ + float4 v; +}; + +struct SSBO1 +{ + float4 v; +}; + +struct SSBO2 +{ + float4 v; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +struct spvDescriptorSetBuffer0 +{ + const device SSBO0* ssbo0 [[id(0)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + const device SSBO1* ssbo1 [[id(0)]]; +}; + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], device SSBO3& ssbo3 [[buffer(2)]], const device SSBO2& ssbo2 [[buffer(3)]]) +{ + ssbo3.v = ((*spvDescriptorSet0.ssbo0).v + (*spvDescriptorSet1.ssbo1).v) + ssbo2.v; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp new file mode 100644 index 0000000..25a0233 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +kernel void main0(texture2d uImage [[texture(0)]], texture2d uImageRead [[texture(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + int2 _17 = int2(gl_GlobalInvocationID.xy); + uImage.write(uImageRead.read(uint2(_17)), uint2(_17)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp new file mode 100644 index 0000000..2f8067e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct spvDescriptorSetBuffer0 +{ + texture2d uImage [[id(0)]]; + texture2d uImageRead [[id(1)]]; +}; + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + int2 _17 = int2(gl_GlobalInvocationID.xy); + spvDescriptorSet0.uImage.write(spvDescriptorSet0.uImageRead.read(uint2(_17)), uint2(_17)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/array-length.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/array-length.comp new file mode 100644 index 0000000..5a284b9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/array-length.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + uint size; + float4 v[1]; +}; + +struct SSBO1 +{ + float bz[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device SSBO& _14 [[buffer(0)]], device SSBO1* ssbos_0 [[buffer(1)]], device SSBO1* ssbos_1 [[buffer(2)]]) +{ + device SSBO1* ssbos[] = + { + ssbos_0, + ssbos_1, + }; + + constant uint& _14BufferSize = spvBufferSizeConstants[0]; + constant uint* ssbosBufferSize = &spvBufferSizeConstants[1]; + _14.size = uint(int((_14BufferSize - 16) / 16) + int((ssbosBufferSize[1] - 0) / 4)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/array-length.msl2.argument.discrete.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/array-length.msl2.argument.discrete.comp new file mode 100644 index 0000000..d804e18 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/array-length.msl2.argument.discrete.comp @@ -0,0 +1,56 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + uint size; + float4 v[1]; +}; + +struct SSBO1 +{ + float bz[1]; +}; + +struct SSBO2 +{ + uint size2; + float4 w[1]; +}; + +struct SSBO3 +{ + float bz[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +struct spvDescriptorSetBuffer0 +{ + device SSBO* m_16 [[id(0)]]; + constant uint* spvBufferSizeConstants [[id(1)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + device SSBO1* ssbos [[id(0)]][2]; + constant uint* spvBufferSizeConstants [[id(2)]]; +}; + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], constant uint* spvBufferSizeConstants [[buffer(25)]], device SSBO2& _38 [[buffer(2)]], device SSBO3* ssbos2_0 [[buffer(3)]], device SSBO3* ssbos2_1 [[buffer(4)]]) +{ + device SSBO3* ssbos2[] = + { + ssbos2_0, + ssbos2_1, + }; + + constant uint& spvDescriptorSet0_m_16BufferSize = spvDescriptorSet0.spvBufferSizeConstants[0]; + constant uint* spvDescriptorSet1_ssbosBufferSize = &spvDescriptorSet1.spvBufferSizeConstants[0]; + constant uint& _38BufferSize = spvBufferSizeConstants[2]; + constant uint* ssbos2BufferSize = &spvBufferSizeConstants[3]; + (*spvDescriptorSet0.m_16).size = ((uint(int((spvDescriptorSet0_m_16BufferSize - 16) / 16)) + uint(int((spvDescriptorSet1_ssbosBufferSize[1] - 0) / 4))) + uint(int((_38BufferSize - 16) / 16))) + uint(int((ssbos2BufferSize[0] - 0) / 4)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/atomic.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/atomic.comp new file mode 100644 index 0000000..fca72bf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/atomic.comp @@ -0,0 +1,72 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + uint u32; + int i32; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& ssbo [[buffer(0)]]) +{ + threadgroup uint shared_u32; + threadgroup int shared_i32; + uint _16 = atomic_fetch_add_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _18 = atomic_fetch_or_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _20 = atomic_fetch_xor_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _22 = atomic_fetch_and_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _24 = atomic_fetch_min_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _26 = atomic_fetch_max_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _28 = atomic_exchange_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _32; + do + { + _32 = 10u; + } while (!atomic_compare_exchange_weak_explicit((device atomic_uint*)&ssbo.u32, &_32, 2u, memory_order_relaxed, memory_order_relaxed) && _32 == 10u); + int _36 = atomic_fetch_add_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _38 = atomic_fetch_or_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _40 = atomic_fetch_xor_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _42 = atomic_fetch_and_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _44 = atomic_fetch_min_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _46 = atomic_fetch_max_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _48 = atomic_exchange_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _52; + do + { + _52 = 10; + } while (!atomic_compare_exchange_weak_explicit((device atomic_int*)&ssbo.i32, &_52, 2, memory_order_relaxed, memory_order_relaxed) && _52 == 10); + shared_u32 = 10u; + shared_i32 = 10; + uint _57 = atomic_fetch_add_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _58 = atomic_fetch_or_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _59 = atomic_fetch_xor_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _60 = atomic_fetch_and_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _61 = atomic_fetch_min_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _62 = atomic_fetch_max_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _63 = atomic_exchange_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _64; + do + { + _64 = 10u; + } while (!atomic_compare_exchange_weak_explicit((threadgroup atomic_uint*)&shared_u32, &_64, 2u, memory_order_relaxed, memory_order_relaxed) && _64 == 10u); + int _65 = atomic_fetch_add_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _66 = atomic_fetch_or_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _67 = atomic_fetch_xor_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _68 = atomic_fetch_and_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _69 = atomic_fetch_min_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _70 = atomic_fetch_max_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _71 = atomic_exchange_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _72; + do + { + _72 = 10; + } while (!atomic_compare_exchange_weak_explicit((threadgroup atomic_int*)&shared_i32, &_72, 2, memory_order_relaxed, memory_order_relaxed) && _72 == 10); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/barriers.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/barriers.comp new file mode 100644 index 0000000..164cefd --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/barriers.comp @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(4u, 1u, 1u); + +kernel void main0() +{ + threadgroup_barrier(mem_flags::mem_threadgroup); + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); + threadgroup_barrier(mem_flags::mem_texture); + threadgroup_barrier(mem_flags::mem_device); + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); + threadgroup_barrier(mem_flags::mem_threadgroup); + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); + threadgroup_barrier(mem_flags::mem_texture); + threadgroup_barrier(mem_flags::mem_device); + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); + threadgroup_barrier(mem_flags::mem_threadgroup); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.comp new file mode 100644 index 0000000..dbb839f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.comp @@ -0,0 +1,35 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +struct SSBO3 +{ + uint counter; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _23 [[buffer(0)]], device SSBO2& _45 [[buffer(1)]], device SSBO3& _48 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float4 _29 = _23.in_data[gl_GlobalInvocationID.x]; + if (dot(_29, float4(1.0, 5.0, 6.0, 2.0)) > 8.19999980926513671875) + { + uint _52 = atomic_fetch_add_explicit((device atomic_uint*)&_48.counter, 1u, memory_order_relaxed); + _45.out_data[_52] = _29; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.dispatchbase.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.dispatchbase.comp new file mode 100644 index 0000000..ebbc144 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.dispatchbase.comp @@ -0,0 +1,38 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +struct SSBO3 +{ + uint counter; +}; + +constant uint _59_tmp [[function_constant(10)]]; +constant uint _59 = is_function_constant_defined(_59_tmp) ? _59_tmp : 1u; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(_59, 1u, 1u); + +kernel void main0(const device SSBO& _27 [[buffer(0)]], device SSBO2& _49 [[buffer(1)]], device SSBO3& _52 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvDispatchBase [[grid_origin]]) +{ + gl_GlobalInvocationID += spvDispatchBase * gl_WorkGroupSize; + float4 _33 = _27.in_data[gl_GlobalInvocationID.x]; + if (dot(_33, float4(1.0, 5.0, 6.0, 2.0)) > 8.19999980926513671875) + { + uint _56 = atomic_fetch_add_explicit((device atomic_uint*)&_52.counter, 1u, memory_order_relaxed); + _49.out_data[_56] = _33; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.dispatchbase.msl11.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.dispatchbase.msl11.comp new file mode 100644 index 0000000..2d991f5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.dispatchbase.msl11.comp @@ -0,0 +1,36 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +struct SSBO3 +{ + uint counter; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(constant uint3& spvDispatchBase [[buffer(29)]], const device SSBO& _27 [[buffer(0)]], device SSBO2& _49 [[buffer(1)]], device SSBO3& _52 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + gl_GlobalInvocationID += spvDispatchBase * gl_WorkGroupSize; + float4 _33 = _27.in_data[gl_GlobalInvocationID.x]; + if (dot(_33, float4(1.0, 5.0, 6.0, 2.0)) > 8.19999980926513671875) + { + uint _56 = atomic_fetch_add_explicit((device atomic_uint*)&_52.counter, 1u, memory_order_relaxed); + _49.out_data[_56] = _33; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.inline-block.msl2.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.inline-block.msl2.comp new file mode 100644 index 0000000..902dfc9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/basic.inline-block.msl2.comp @@ -0,0 +1,53 @@ +#include +#include + +using namespace metal; + +typedef packed_float4 packed_float4x4[4]; + +struct Baz +{ + int f; + int g; +}; + +struct X +{ + int x; + int y; + float z; +}; + +struct Foo +{ + int a; + int b; + packed_float4x4 c; + X x[2]; +}; + +struct Bar +{ + int d; + int e; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(3u, 3u, 2u); + +struct spvDescriptorSetBuffer0 +{ + constant Bar* m_38 [[id(0)]]; + Foo m_32 [[id(1)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + device Baz* baz [[id(0)]][3]; +}; + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + spvDescriptorSet1.baz[gl_GlobalInvocationID.x]->f = spvDescriptorSet0.m_32.a + (*spvDescriptorSet0.m_38).d; + spvDescriptorSet1.baz[gl_GlobalInvocationID.x]->g = spvDescriptorSet0.m_32.b * (*spvDescriptorSet0.m_38).e; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/builtins.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/builtins.comp new file mode 100644 index 0000000..7576715 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/builtins.comp @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(8u, 4u, 2u); + +kernel void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/cfg-preserve-parameter.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/cfg-preserve-parameter.comp new file mode 100644 index 0000000..59fc03a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/cfg-preserve-parameter.comp @@ -0,0 +1,9 @@ +#include +#include + +using namespace metal; + +kernel void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/coherent-block.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/coherent-block.comp new file mode 100644 index 0000000..58bbacb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/coherent-block.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 value; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(volatile device SSBO& _10 [[buffer(0)]]) +{ + _10.value = float4(20.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/coherent-image.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/coherent-image.comp new file mode 100644 index 0000000..5090484 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/coherent-image.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + int4 value; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(volatile device SSBO& _10 [[buffer(0)]], texture2d uImage [[texture(0)]]) +{ + _10.value = uImage.read(uint2(int2(10))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/complex-composite-constant-array.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/complex-composite-constant-array.comp new file mode 100644 index 0000000..bd58c95 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/complex-composite-constant-array.comp @@ -0,0 +1,59 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct SSBO +{ + float4x4 a; + uint index; +}; + +kernel void main0(device SSBO& _14 [[buffer(0)]]) +{ + spvUnsafeArray _32 = spvUnsafeArray({ float4x4(float4(1.0, 0.0, 0.0, 0.0), float4(0.0, 1.0, 0.0, 0.0), float4(0.0, 0.0, 1.0, 0.0), float4(0.0, 0.0, 0.0, 1.0)), float4x4(float4(2.0, 0.0, 0.0, 0.0), float4(0.0, 2.0, 0.0, 0.0), float4(0.0, 0.0, 2.0, 0.0), float4(0.0, 0.0, 0.0, 2.0)) }); + + _14.a = _32[_14.index]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/complex-type-alias.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/complex-type-alias.comp new file mode 100644 index 0000000..43e1297 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/complex-type-alias.comp @@ -0,0 +1,56 @@ +#include +#include + +using namespace metal; + +struct Foo0 +{ + float a; +}; + +struct Foo1 +{ + Foo0 a; +}; + +struct Foo2 +{ + Foo1 a; + float weight; +}; + +struct Foo0_1 +{ + float a; +}; + +struct Foo1_1 +{ + Foo0_1 a; +}; + +struct Foo2_1 +{ + Foo1_1 a; + float weight; +}; + +struct SSBO +{ + Foo2_1 outputs[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(8u, 8u, 1u); + +kernel void main0(device SSBO& _53 [[buffer(0)]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +{ + threadgroup Foo2 coeffs[64]; + coeffs[gl_LocalInvocationIndex] = Foo2{ Foo1{ Foo0{ 0.0 } }, 0.0 }; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_LocalInvocationIndex == 0u) + { + _53.outputs[gl_WorkGroupID.x].a.a.a = coeffs[0].a.a.a; + _53.outputs[gl_WorkGroupID.x].weight = coeffs[0].weight; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-array-initialization.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-array-initialization.comp new file mode 100644 index 0000000..6181ae6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-array-initialization.comp @@ -0,0 +1,79 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Data +{ + float a; + float b; +}; + +constant float X_tmp [[function_constant(0)]]; +constant float X = is_function_constant_defined(X_tmp) ? X_tmp : 4.0; + +struct Data_1 +{ + float a; + float b; +}; + +struct SSBO +{ + Data_1 outdata[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(2u, 1u, 1u); + +kernel void main0(device SSBO& _53 [[buffer(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + spvUnsafeArray _25 = spvUnsafeArray({ Data{ 1.0, 2.0 }, Data{ 3.0, 4.0 } }); + + spvUnsafeArray _31 = spvUnsafeArray({ Data{ X, 2.0 }, Data{ 3.0, 5.0 } }); + spvUnsafeArray data2; + data2 = _31; + _53.outdata[gl_WorkGroupID.x].a = _25[gl_LocalInvocationID.x].a + data2[gl_LocalInvocationID.x].a; + _53.outdata[gl_WorkGroupID.x].b = _25[gl_LocalInvocationID.x].b + data2[gl_LocalInvocationID.x].b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-array-initialization.force-native-array.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-array-initialization.force-native-array.comp new file mode 100644 index 0000000..aa612ec --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-array-initialization.force-native-array.comp @@ -0,0 +1,148 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Data +{ + float a; + float b; +}; + +constant float X_tmp [[function_constant(0)]]; +constant float X = is_function_constant_defined(X_tmp) ? X_tmp : 4.0; + +struct Data_1 +{ + float a; + float b; +}; + +struct SSBO +{ + Data_1 outdata[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(2u, 1u, 1u); + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +kernel void main0(device SSBO& _53 [[buffer(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + Data _25[2] = { Data{ 1.0, 2.0 }, Data{ 3.0, 4.0 } }; + + Data _31[2] = { Data{ X, 2.0 }, Data{ 3.0, 5.0 } }; + Data data2[2]; + spvArrayCopyFromStackToStack1(data2, _31); + _53.outdata[gl_WorkGroupID.x].a = _25[gl_LocalInvocationID.x].a + data2[gl_LocalInvocationID.x].a; + _53.outdata[gl_WorkGroupID.x].b = _25[gl_LocalInvocationID.x].b + data2[gl_LocalInvocationID.x].b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-construct.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-construct.comp new file mode 100644 index 0000000..09e6fc7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/composite-construct.comp @@ -0,0 +1,67 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct SSBO0 +{ + float4 as[1]; +}; + +struct SSBO1 +{ + float4 bs[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO0& _16 [[buffer(0)]], device SSBO1& _32 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + spvUnsafeArray _37 = spvUnsafeArray({ _16.as[gl_GlobalInvocationID.x], _32.bs[gl_GlobalInvocationID.x] }); + spvUnsafeArray values; + values = _37; + _16.as[0] = values[gl_LocalInvocationIndex]; + _32.bs[1] = float4(40.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/copy-array-of-arrays.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/copy-array-of-arrays.comp new file mode 100644 index 0000000..cb396cf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/copy-array-of-arrays.comp @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct BUF +{ + int a; + float b; + float c; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device BUF& o [[buffer(0)]]) +{ + o.a = 4; + o.b = o.c; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp new file mode 100644 index 0000000..cb396cf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct BUF +{ + int a; + float b; + float c; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device BUF& o [[buffer(0)]]) +{ + o.a = 4; + o.b = o.c; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/culling.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/culling.comp new file mode 100644 index 0000000..5573547 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/culling.comp @@ -0,0 +1,35 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float in_data[1]; +}; + +struct SSBO2 +{ + float out_data[1]; +}; + +struct SSBO3 +{ + uint count; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(4u, 1u, 1u); + +kernel void main0(const device SSBO& _22 [[buffer(0)]], device SSBO2& _38 [[buffer(1)]], device SSBO3& _41 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float _28 = _22.in_data[gl_GlobalInvocationID.x]; + if (_28 > 12.0) + { + uint _45 = atomic_fetch_add_explicit((device atomic_uint*)&_41.count, 1u, memory_order_relaxed); + _38.out_data[_45] = _28; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/defer-parens.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/defer-parens.comp new file mode 100644 index 0000000..8c130e3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/defer-parens.comp @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 data; + int index; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _13 [[buffer(0)]]) +{ + float4 _17 = _13.data; + float2 _28 = _17.yz + float2(10.0); + _13.data = float4(_17.x, _28, _17.w); + _13.data = (_17 + _17) + _17; + _13.data = _28.xxyy; + _13.data = float4(_28.y); + _13.data = float4((_17.zw + float2(10.0))[_13.index]); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/dowhile.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/dowhile.comp new file mode 100644 index 0000000..b503c94 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/dowhile.comp @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4x4 mvp; + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _28 [[buffer(0)]], device SSBO2& _52 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float4 _59; + int _60; + _60 = 0; + _59 = _28.in_data[gl_GlobalInvocationID.x]; + float4 _42; + for (;;) + { + _42 = _28.mvp * _59; + int _44 = _60 + 1; + if (_44 < 16) + { + _60 = _44; + _59 = _42; + } + else + { + break; + } + } + _52.out_data[gl_GlobalInvocationID.x] = _42; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/force-recompile-hooks.swizzle.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/force-recompile-hooks.swizzle.comp new file mode 100644 index 0000000..fbf4c4f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/force-recompile-hooks.swizzle.comp @@ -0,0 +1,73 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +kernel void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture2d foo [[texture(0)]], texture2d bar [[texture(1)]], sampler fooSmplr [[sampler(0)]]) +{ + constant uint& fooSwzl = spvSwizzleConstants[0]; + bar.write(spvTextureSwizzle(foo.sample(fooSmplr, float2(1.0), level(0.0)), fooSwzl), uint2(int2(0))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/functions.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/functions.comp new file mode 100644 index 0000000..59fc03a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/functions.comp @@ -0,0 +1,9 @@ +#include +#include + +using namespace metal; + +kernel void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp new file mode 100644 index 0000000..9900b59 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b[1]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_GlobalInvocationID.x] = mod(myStorage.b[gl_GlobalInvocationID.x] + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/global-invocation-id.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/global-invocation-id.comp new file mode 100644 index 0000000..9900b59 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/global-invocation-id.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b[1]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_GlobalInvocationID.x] = mod(myStorage.b[gl_GlobalInvocationID.x] + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp new file mode 100644 index 0000000..7dea8b7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp @@ -0,0 +1,37 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 outdata; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +struct spvDescriptorSetBuffer0 +{ + texture2d uImage [[id(0)]]; + device atomic_uint* uImage_atomic [[id(1)]]; + device SSBO* m_31 [[id(2)]]; + texture2d uTexture [[id(3)]]; + sampler uTextureSmplr [[id(4)]]; +}; + +// The required alignment of a linear texture of R32Uint format. +constant uint spvLinearTextureAlignmentOverride [[function_constant(65535)]]; +constant uint spvLinearTextureAlignment = is_function_constant_defined(spvLinearTextureAlignmentOverride) ? spvLinearTextureAlignmentOverride : 4; +// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics +#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + spvLinearTextureAlignment / 4 - 1) & ~( spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x) + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _26 = atomic_fetch_add_explicit((device atomic_uint*)&spvDescriptorSet0.uImage_atomic[spvImage2DAtomicCoord(int2(gl_GlobalInvocationID.xy), spvDescriptorSet0.uImage)], 10u, memory_order_relaxed); + (*spvDescriptorSet0.m_31).outdata = spvDescriptorSet0.uTexture.sample(spvDescriptorSet0.uTextureSmplr, float2(gl_GlobalInvocationID.xy), level(0.0)) + float4(float(_26)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-atomic-automatic-bindings.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-atomic-automatic-bindings.comp new file mode 100644 index 0000000..a284647 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-atomic-automatic-bindings.comp @@ -0,0 +1,28 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 outdata; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +// The required alignment of a linear texture of R32Uint format. +constant uint spvLinearTextureAlignmentOverride [[function_constant(65535)]]; +constant uint spvLinearTextureAlignment = is_function_constant_defined(spvLinearTextureAlignmentOverride) ? spvLinearTextureAlignmentOverride : 4; +// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics +#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + spvLinearTextureAlignment / 4 - 1) & ~( spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x) + +kernel void main0(device SSBO& _31 [[buffer(1)]], texture2d uImage [[texture(0)]], device atomic_uint* uImage_atomic [[buffer(0)]], texture2d uTexture [[texture(1)]], sampler uTextureSmplr [[sampler(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _26 = atomic_fetch_add_explicit((device atomic_uint*)&uImage_atomic[spvImage2DAtomicCoord(int2(gl_GlobalInvocationID.xy), uImage)], 10u, memory_order_relaxed); + _31.outdata = uTexture.sample(uTextureSmplr, float2(gl_GlobalInvocationID.xy), level(0.0)) + float4(float(_26)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-cube-array-load-store.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-cube-array-load-store.comp new file mode 100644 index 0000000..41c4dfc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/image-cube-array-load-store.comp @@ -0,0 +1,12 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(texturecube_array uImageIn [[texture(0)]], texturecube_array uImageOut [[texture(1)]]) +{ + uImageOut.write(uImageIn.read(uint2(int3(9, 7, 11).xy), uint(int3(9, 7, 11).z) % 6u, uint(int3(9, 7, 11).z) / 6u), uint2(int3(9, 7, 11).xy), uint(int3(9, 7, 11).z) % 6u, uint(int3(9, 7, 11).z) / 6u); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/image.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/image.comp new file mode 100644 index 0000000..c875e78 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/image.comp @@ -0,0 +1,13 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(texture2d uImageIn [[texture(0)]], texture2d uImageOut [[texture(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + int2 _23 = int2(gl_GlobalInvocationID.xy); + uImageOut.write(uImageIn.read(uint2((_23 + int2(uImageIn.get_width(), uImageIn.get_height())))), uint2(_23)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/insert.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/insert.comp new file mode 100644 index 0000000..36b846e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/insert.comp @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +constant float4 _53 = {}; + +kernel void main0(device SSBO& _27 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float4 _46 = _53; + _46.x = 10.0; + float4 _48 = _46; + _48.y = 30.0; + float4 _50 = _48; + _50.z = 70.0; + float4 _52 = _50; + _52.w = 90.0; + _27.out_data[gl_GlobalInvocationID.x] = _52; + ((device float*)&_27.out_data[gl_GlobalInvocationID.x])[1u] = 20.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/inverse.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/inverse.comp new file mode 100644 index 0000000..33aed46 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/inverse.comp @@ -0,0 +1,130 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct MatrixOut +{ + float2x2 m2out; + float3x3 m3out; + float4x4 m4out; +}; + +struct MatrixIn +{ + float2x2 m2in; + float3x3 m3in; + float4x4 m4in; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +// Returns the determinant of a 2x2 matrix. +static inline __attribute__((always_inline)) +float spvDet2x2(float a1, float a2, float b1, float b2) +{ + return a1 * b2 - b1 * a2; +} + +// Returns the determinant of a 3x3 matrix. +static inline __attribute__((always_inline)) +float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) +{ + return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * spvDet2x2(a2, a3, b2, b3); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float4x4 spvInverse4x4(float4x4 m) +{ + float4x4 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3]); + adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]); + + adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3]); + adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]); + + adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3]); + adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]); + + adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2]); + adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] * m[3][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float3x3 spvInverse3x3(float3x3 m) +{ + float3x3 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = spvDet2x2(m[1][1], m[1][2], m[2][1], m[2][2]); + adj[0][1] = -spvDet2x2(m[0][1], m[0][2], m[2][1], m[2][2]); + adj[0][2] = spvDet2x2(m[0][1], m[0][2], m[1][1], m[1][2]); + + adj[1][0] = -spvDet2x2(m[1][0], m[1][2], m[2][0], m[2][2]); + adj[1][1] = spvDet2x2(m[0][0], m[0][2], m[2][0], m[2][2]); + adj[1][2] = -spvDet2x2(m[0][0], m[0][2], m[1][0], m[1][2]); + + adj[2][0] = spvDet2x2(m[1][0], m[1][1], m[2][0], m[2][1]); + adj[2][1] = -spvDet2x2(m[0][0], m[0][1], m[2][0], m[2][1]); + adj[2][2] = spvDet2x2(m[0][0], m[0][1], m[1][0], m[1][1]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float2x2 spvInverse2x2(float2x2 m) +{ + float2x2 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = m[1][1]; + adj[0][1] = -m[0][1]; + + adj[1][0] = -m[1][0]; + adj[1][1] = m[0][0]; + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +kernel void main0(device MatrixOut& _15 [[buffer(0)]], const device MatrixIn& _20 [[buffer(1)]]) +{ + _15.m2out = spvInverse2x2(_20.m2in); + _15.m3out = spvInverse3x3(_20.m3in); + _15.m4out = spvInverse4x4(_20.m4in); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/local-invocation-id.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/local-invocation-id.comp new file mode 100644 index 0000000..362aaa3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/local-invocation-id.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b[1]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_LocalInvocationID.x] = mod(myStorage.b[gl_LocalInvocationID.x] + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/local-invocation-index.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/local-invocation-index.comp new file mode 100644 index 0000000..f8c1550 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/local-invocation-index.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b[1]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_LocalInvocationIndex] = mod(myStorage.b[gl_LocalInvocationIndex] + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/mat3.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/mat3.comp new file mode 100644 index 0000000..31351ba --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/mat3.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct SSBO2 +{ + float3x3 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _22 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _22.out_data[gl_GlobalInvocationID.x] = float3x3(float3(10.0), float3(20.0), float3(40.0)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/mod.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/mod.comp new file mode 100644 index 0000000..e8c01f9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/mod.comp @@ -0,0 +1,33 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(const device SSBO& _23 [[buffer(0)]], device SSBO2& _33 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _33.out_data[gl_GlobalInvocationID.x] = mod(_23.in_data[gl_GlobalInvocationID.x], _33.out_data[gl_GlobalInvocationID.x]); + _33.out_data[gl_GlobalInvocationID.x] = as_type(as_type(_23.in_data[gl_GlobalInvocationID.x]) % as_type(_33.out_data[gl_GlobalInvocationID.x])); + _33.out_data[gl_GlobalInvocationID.x] = as_type(as_type(_23.in_data[gl_GlobalInvocationID.x]) % as_type(_33.out_data[gl_GlobalInvocationID.x])); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/modf.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/modf.comp new file mode 100644 index 0000000..df19cae --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/modf.comp @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _23 [[buffer(0)]], device SSBO2& _35 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float4 i; + float4 _31 = modf(_23.in_data[gl_GlobalInvocationID.x], i); + _35.out_data[gl_GlobalInvocationID.x] = _31; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/outer-product.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/outer-product.comp new file mode 100644 index 0000000..e589642 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/outer-product.comp @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float2x2 m22; + float2x3 m23; + float2x4 m24; + float3x2 m32; + float3x3 m33; + float3x4 m34; + float4x2 m42; + float4x3 m43; + float4x4 m44; +}; + +struct ReadSSBO +{ + float2 v2; + float3 v3; + float4 v4; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _21 [[buffer(0)]], const device ReadSSBO& _26 [[buffer(1)]]) +{ + _21.m22 = float2x2(_26.v2 * _26.v2.x, _26.v2 * _26.v2.y); + _21.m23 = float2x3(_26.v3 * _26.v2.x, _26.v3 * _26.v2.y); + _21.m24 = float2x4(_26.v4 * _26.v2.x, _26.v4 * _26.v2.y); + _21.m32 = float3x2(_26.v2 * _26.v3.x, _26.v2 * _26.v3.y, _26.v2 * _26.v3.z); + _21.m33 = float3x3(_26.v3 * _26.v3.x, _26.v3 * _26.v3.y, _26.v3 * _26.v3.z); + _21.m34 = float3x4(_26.v4 * _26.v3.x, _26.v4 * _26.v3.y, _26.v4 * _26.v3.z); + _21.m42 = float4x2(_26.v2 * _26.v4.x, _26.v2 * _26.v4.y, _26.v2 * _26.v4.z, _26.v2 * _26.v4.w); + _21.m43 = float4x3(_26.v3 * _26.v4.x, _26.v3 * _26.v4.y, _26.v3 * _26.v4.z, _26.v3 * _26.v4.w); + _21.m44 = float4x4(_26.v4 * _26.v4.x, _26.v4 * _26.v4.y, _26.v4 * _26.v4.z, _26.v4 * _26.v4.w); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/packing-test-1.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/packing-test-1.comp new file mode 100644 index 0000000..0649f57 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/packing-test-1.comp @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct T1 +{ + packed_float3 a; + float b; +}; + +struct Buffer0 +{ + T1 buf0[1]; +}; + +struct Buffer1 +{ + float buf1[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(32u, 1u, 1u); + +kernel void main0(device Buffer0& _15 [[buffer(0)]], device Buffer1& _34 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _34.buf1[gl_GlobalInvocationID.x] = _15.buf0[0].b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/packing-test-2.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/packing-test-2.comp new file mode 100644 index 0000000..abce48a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/packing-test-2.comp @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct T1 +{ + packed_float3 a; + float b; +}; + +struct Buffer0 +{ + T1 buf0[1]; +}; + +struct Buffer1 +{ + float buf1[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(32u, 1u, 1u); + +kernel void main0(device Buffer0& _14 [[buffer(0)]], device Buffer1& _24 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _24.buf1[gl_GlobalInvocationID.x] = _14.buf0[0].b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/read-write-only.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/read-write-only.comp new file mode 100644 index 0000000..0cf8d8e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/read-write-only.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct SSBO2 +{ + float4 data4; + float4 data5; +}; + +struct SSBO0 +{ + float4 data0; + float4 data1; +}; + +struct SSBO1 +{ + float4 data2; + float4 data3; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _10 [[buffer(0)]], const device SSBO0& _15 [[buffer(1)]], device SSBO1& _21 [[buffer(2)]]) +{ + _10.data4 = _15.data0 + _21.data2; + _10.data5 = _15.data1 + _21.data3; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/rmw-matrix.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/rmw-matrix.comp new file mode 100644 index 0000000..b53a3a7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/rmw-matrix.comp @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float a; + float4 b; + float4x4 c; + float a1; + float4 b1; + float4x4 c1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _11 [[buffer(0)]]) +{ + _11.a *= _11.a1; + _11.b *= _11.b1; + _11.c = _11.c * _11.c1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/rmw-opt.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/rmw-opt.comp new file mode 100644 index 0000000..f93967d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/rmw-opt.comp @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + int a; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _9 [[buffer(0)]]) +{ + _9.a += 10; + _9.a -= 10; + _9.a *= 10; + _9.a /= 10; + _9.a = _9.a << 2; + _9.a = _9.a >> 3; + _9.a &= 40; + _9.a ^= 10; + _9.a %= 40; + _9.a |= 1; + _9.a = 0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..9bf8781 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float a; + float b; + float c; + float d; + float e; + float f; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _9 [[buffer(0)]]) +{ + _9.c = abs(_9.a - _9.b); + _9.d = abs(_9.a); + _9.e = sign(_9.a); + _9.f = abs((_9.a - 1.0) - (_9.b - 2.0)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/shared-array-of-arrays.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/shared-array-of-arrays.comp new file mode 100644 index 0000000..ddb8c96 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/shared-array-of-arrays.comp @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(4u, 4u, 1u); + +kernel void main0(device SSBO& _67 [[buffer(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + threadgroup float foo[4][4]; + foo[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = float(gl_LocalInvocationIndex); + threadgroup_barrier(mem_flags::mem_threadgroup); + _67.out_data[gl_GlobalInvocationID.x] = ((foo[gl_LocalInvocationID.x][0] + foo[gl_LocalInvocationID.x][1]) + foo[gl_LocalInvocationID.x][2]) + foo[gl_LocalInvocationID.x][3]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/shared.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/shared.comp new file mode 100644 index 0000000..029ab53 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/shared.comp @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float in_data[1]; +}; + +struct SSBO2 +{ + float out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(4u, 1u, 1u); + +kernel void main0(const device SSBO& _22 [[buffer(0)]], device SSBO2& _44 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + threadgroup float sShared[4]; + sShared[gl_LocalInvocationIndex] = _22.in_data[gl_GlobalInvocationID.x]; + threadgroup_barrier(mem_flags::mem_threadgroup); + _44.out_data[gl_GlobalInvocationID.x] = sShared[3u - gl_LocalInvocationIndex]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/spec-constant-op-member-array.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/spec-constant-op-member-array.comp new file mode 100644 index 0000000..8f54f05 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/spec-constant-op-member-array.comp @@ -0,0 +1,49 @@ +#include +#include + +using namespace metal; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 100 +#endif +constant int a = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 200 +#endif +constant int b = SPIRV_CROSS_CONSTANT_ID_1; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 300 +#endif +constant int c = SPIRV_CROSS_CONSTANT_ID_2; +constant int d = (c + 50); + +struct SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +}; + +constant int e_tmp [[function_constant(3)]]; +constant int e = is_function_constant_defined(e_tmp) ? e_tmp : 400; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _22 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _22.w[gl_GlobalInvocationID.x] += (_22.v[gl_GlobalInvocationID.x] + e); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/spec-constant-work-group-size.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/spec-constant-work-group-size.comp new file mode 100644 index 0000000..de30ede --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/spec-constant-work-group-size.comp @@ -0,0 +1,75 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 2 +#endif +constant int b = SPIRV_CROSS_CONSTANT_ID_1; +constant int a_tmp [[function_constant(0)]]; +constant int a = is_function_constant_defined(a_tmp) ? a_tmp : 1; + +struct SSBO +{ + int v[1]; +}; + +constant uint _21 = (uint(a) + 0u); +constant uint _22_tmp [[function_constant(10)]]; +constant uint _22 = is_function_constant_defined(_22_tmp) ? _22_tmp : 1u; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(_22, 20u, 1u); +constant uint _27 = gl_WorkGroupSize.x; +constant uint _28 = (_21 + _27); +constant uint _29 = gl_WorkGroupSize.y; +constant uint _30 = (_28 + _29); +constant int _32 = (1 - a); + +kernel void main0(device SSBO& _17 [[buffer(0)]]) +{ + spvUnsafeArray spec_const_array_size; + spec_const_array_size[a] = a; + _17.v[_30] = b + spec_const_array_size[_32]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/storage-buffer-std140-vector-array.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/storage-buffer-std140-vector-array.comp new file mode 100644 index 0000000..b584f30 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/storage-buffer-std140-vector-array.comp @@ -0,0 +1,91 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Sub +{ + float4 f[2]; + float4 f2[2]; + float3 f3[2]; + float4 f4[2]; +}; + +struct SSBO +{ + Sub sub[2]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _27 [[buffer(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + spvUnsafeArray _155; + _155[0] = _27.sub[gl_WorkGroupID.x].f[0].x; + _155[1] = _27.sub[gl_WorkGroupID.x].f[1].x; + spvUnsafeArray _156; + _156[0] = _27.sub[gl_WorkGroupID.x].f2[0].xy; + _156[1] = _27.sub[gl_WorkGroupID.x].f2[1].xy; + spvUnsafeArray _157; + _157[0] = _27.sub[gl_WorkGroupID.x].f3[0]; + _157[1] = _27.sub[gl_WorkGroupID.x].f3[1]; + spvUnsafeArray _158; + _158[0] = _27.sub[gl_WorkGroupID.x].f4[0]; + _158[1] = _27.sub[gl_WorkGroupID.x].f4[1]; + _155[gl_GlobalInvocationID.x] += 1.0; + _156[gl_GlobalInvocationID.x] += float2(2.0); + _157[gl_GlobalInvocationID.x] += float3(3.0); + _158[gl_GlobalInvocationID.x] += float4(4.0); + (device float&)_27.sub[gl_WorkGroupID.x].f[0] = _155[0]; + (device float&)_27.sub[gl_WorkGroupID.x].f[1] = _155[1]; + (device float2&)_27.sub[gl_WorkGroupID.x].f2[0] = _156[0]; + (device float2&)_27.sub[gl_WorkGroupID.x].f2[1] = _156[1]; + _27.sub[gl_WorkGroupID.x].f3[0] = _157[0]; + _27.sub[gl_WorkGroupID.x].f3[1] = _157[1]; + _27.sub[gl_WorkGroupID.x].f4[0] = _158[0]; + _27.sub[gl_WorkGroupID.x].f4[1] = _158[1]; + (device float&)_27.sub[0].f[0] = _27.sub[0].f[0].x + 5.0; + (device float2&)_27.sub[0].f2[1] = _27.sub[0].f2[1].xy + float2(5.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-layout.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-layout.comp new file mode 100644 index 0000000..0445f5a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-layout.comp @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + float4x4 m; +}; + +struct SSBO2 +{ + Foo out_data[1]; +}; + +struct SSBO +{ + Foo in_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _23 [[buffer(0)]], const device SSBO& _30 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _23.out_data[gl_GlobalInvocationID.x].m = _30.in_data[gl_GlobalInvocationID.x].m * _30.in_data[gl_GlobalInvocationID.x].m; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-nested.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-nested.comp new file mode 100644 index 0000000..ad706c5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-nested.comp @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct s1 +{ + int a; +}; + +struct s2 +{ + s1 b; +}; + +struct dstbuffer +{ + s2 test[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device dstbuffer& _19 [[buffer(0)]]) +{ + _19.test[0].b.a = 0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-packing.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-packing.comp new file mode 100644 index 0000000..dc16543 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/struct-packing.comp @@ -0,0 +1,152 @@ +#include +#include + +using namespace metal; + +struct S0 +{ + float2 a[1]; + float b; + char _m0_final_padding[4]; +}; + +struct S1 +{ + packed_float3 a; + float b; +}; + +struct S2 +{ + float3 a[1]; + float b; + char _m0_final_padding[12]; +}; + +struct S3 +{ + float2 a; + float b; +}; + +struct S4 +{ + float2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + S4 m3s[8]; + char _m0_final_padding[8]; +}; + +struct SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + float2x2 m0; + float2x2 m1; + float2x3 m2[4]; + float3x2 m3; + float2x2 m4; + float2x2 m5[9]; + float3x2 m6[4][2]; + float2x3 m7; + float array[1]; +}; + +struct S0_1 +{ + float2 a[1]; + char _m1_pad[8]; + float b; + char _m0_final_padding[12]; +}; + +struct S1_1 +{ + packed_float3 a; + float b; +}; + +struct S2_1 +{ + float3 a[1]; + float b; + char _m0_final_padding[12]; +}; + +struct S3_1 +{ + float2 a; + float b; +}; + +struct S4_1 +{ + float2 c; + char _m0_final_padding[8]; +}; + +struct Content_1 +{ + S0_1 m0s[1]; + S1_1 m1s[1]; + S2_1 m2s[1]; + S0_1 m0; + S1_1 m1; + S2_1 m2; + S3_1 m3; + float m4; + char _m8_pad[8]; + S4_1 m3s[8]; +}; + +struct SSBO0 +{ + Content_1 content; + Content_1 content1[2]; + Content_1 content2; + float4 array[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO1& ssbo_430 [[buffer(0)]], device SSBO0& ssbo_140 [[buffer(1)]]) +{ + Content_1 _60 = ssbo_140.content; + ssbo_430.content.m0s[0].a[0] = _60.m0s[0].a[0]; + ssbo_430.content.m0s[0].b = _60.m0s[0].b; + ssbo_430.content.m1s[0].a = float3(_60.m1s[0].a); + ssbo_430.content.m1s[0].b = _60.m1s[0].b; + ssbo_430.content.m2s[0].a[0] = _60.m2s[0].a[0]; + ssbo_430.content.m2s[0].b = _60.m2s[0].b; + ssbo_430.content.m0.a[0] = _60.m0.a[0]; + ssbo_430.content.m0.b = _60.m0.b; + ssbo_430.content.m1.a = float3(_60.m1.a); + ssbo_430.content.m1.b = _60.m1.b; + ssbo_430.content.m2.a[0] = _60.m2.a[0]; + ssbo_430.content.m2.b = _60.m2.b; + ssbo_430.content.m3.a = _60.m3.a; + ssbo_430.content.m3.b = _60.m3.b; + ssbo_430.content.m4 = _60.m4; + ssbo_430.content.m3s[0].c = _60.m3s[0].c; + ssbo_430.content.m3s[1].c = _60.m3s[1].c; + ssbo_430.content.m3s[2].c = _60.m3s[2].c; + ssbo_430.content.m3s[3].c = _60.m3s[3].c; + ssbo_430.content.m3s[4].c = _60.m3s[4].c; + ssbo_430.content.m3s[5].c = _60.m3s[5].c; + ssbo_430.content.m3s[6].c = _60.m3s[6].c; + ssbo_430.content.m3s[7].c = _60.m3s[7].c; + ssbo_430.content.m1.a = ssbo_430.content.m3.a * ssbo_430.m6[1][1]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/torture-loop.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/torture-loop.comp new file mode 100644 index 0000000..ff7e02e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/torture-loop.comp @@ -0,0 +1,44 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4x4 mvp; + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _24 [[buffer(0)]], device SSBO2& _89 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float4 _101; + _101 = _24.in_data[gl_GlobalInvocationID.x]; + for (int _95 = 0; (_95 + 1) < 10; ) + { + _101 *= 2.0; + _95 += 2; + continue; + } + float4 _100; + _100 = _101; + float4 _105; + for (uint _96 = 0u; _96 < 16u; _100 = _105, _96++) + { + _105 = _100; + for (uint _102 = 0u; _102 < 30u; ) + { + _105 = _24.mvp * _105; + _102++; + continue; + } + } + _89.out_data[gl_GlobalInvocationID.x] = _100; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/type-alias.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/type-alias.comp new file mode 100644 index 0000000..2f6a0b7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/type-alias.comp @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +struct S0 +{ + float4 a; +}; + +struct SSBO0 +{ + S0 s0s[1]; +}; + +struct S1 +{ + float4 a; +}; + +struct SSBO1 +{ + S1 s1s[1]; +}; + +struct SSBO2 +{ + float4 outputs[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO0& _36 [[buffer(0)]], device SSBO1& _55 [[buffer(1)]], device SSBO2& _66 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _66.outputs[gl_GlobalInvocationID.x] = _36.s0s[gl_GlobalInvocationID.x].a + _55.s1s[gl_GlobalInvocationID.x].a; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/udiv.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/udiv.comp new file mode 100644 index 0000000..7f7315b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/udiv.comp @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct SSBO2 +{ + uint outputs[1]; +}; + +struct SSBO +{ + uint inputs[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _10 [[buffer(0)]], device SSBO& _23 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _10.outputs[gl_GlobalInvocationID.x] = _23.inputs[gl_GlobalInvocationID.x] / 29u; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/comp/writable-ssbo.comp b/third_party/spirv-cross/reference/opt/shaders-msl/comp/writable-ssbo.comp new file mode 100644 index 0000000..dcec81a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/comp/writable-ssbo.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b = mod(myStorage.b + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp new file mode 100644 index 0000000..cea1298 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp @@ -0,0 +1,179 @@ +#include +#include + +using namespace metal; + +struct SSBOUint +{ + uint a; + uint b; + uint c; + uint d; + uint2 a2; + uint2 b2; + uint2 c2; + uint2 d2; + uint3 a3; + uint3 b3; + uint3 c3; + uint3 d3; + uint4 a4; + uint4 b4; + uint4 c4; + uint4 d4; +}; + +struct ResType +{ + uint _m0; + uint _m1; +}; + +struct ResType_1 +{ + uint2 _m0; + uint2 _m1; +}; + +struct ResType_2 +{ + uint3 _m0; + uint3 _m1; +}; + +struct ResType_3 +{ + uint4 _m0; + uint4 _m1; +}; + +struct SSBOInt +{ + int a; + int b; + int c; + int d; + int2 a2; + int2 b2; + int2 c2; + int2 d2; + int3 a3; + int3 b3; + int3 c3; + int3 d3; + int4 a4; + int4 b4; + int4 c4; + int4 d4; +}; + +struct ResType_4 +{ + int _m0; + int _m1; +}; + +struct ResType_5 +{ + int2 _m0; + int2 _m1; +}; + +struct ResType_6 +{ + int3 _m0; + int3 _m1; +}; + +struct ResType_7 +{ + int4 _m0; + int4 _m1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBOUint& u [[buffer(0)]], device SSBOInt& i [[buffer(1)]]) +{ + ResType _25; + _25._m0 = u.a + u.b; + _25._m1 = select(uint(1), uint(0), _25._m0 >= max(u.a, u.b)); + u.d = _25._m1; + u.c = _25._m0; + ResType_1 _40; + _40._m0 = u.a2 + u.b2; + _40._m1 = select(uint2(1), uint2(0), _40._m0 >= max(u.a2, u.b2)); + u.d2 = _40._m1; + u.c2 = _40._m0; + ResType_2 _55; + _55._m0 = u.a3 + u.b3; + _55._m1 = select(uint3(1), uint3(0), _55._m0 >= max(u.a3, u.b3)); + u.d3 = _55._m1; + u.c3 = _55._m0; + ResType_3 _70; + _70._m0 = u.a4 + u.b4; + _70._m1 = select(uint4(1), uint4(0), _70._m0 >= max(u.a4, u.b4)); + u.d4 = _70._m1; + u.c4 = _70._m0; + ResType _79; + _79._m0 = u.a - u.b; + _79._m1 = select(uint(1), uint(0), u.a >= u.b); + u.d = _79._m1; + u.c = _79._m0; + ResType_1 _88; + _88._m0 = u.a2 - u.b2; + _88._m1 = select(uint2(1), uint2(0), u.a2 >= u.b2); + u.d2 = _88._m1; + u.c2 = _88._m0; + ResType_2 _97; + _97._m0 = u.a3 - u.b3; + _97._m1 = select(uint3(1), uint3(0), u.a3 >= u.b3); + u.d3 = _97._m1; + u.c3 = _97._m0; + ResType_3 _106; + _106._m0 = u.a4 - u.b4; + _106._m1 = select(uint4(1), uint4(0), u.a4 >= u.b4); + u.d4 = _106._m1; + u.c4 = _106._m0; + ResType _116; + _116._m0 = u.a * u.b; + _116._m1 = mulhi(u.a, u.b); + u.d = _116._m0; + u.c = _116._m1; + ResType_1 _125; + _125._m0 = u.a2 * u.b2; + _125._m1 = mulhi(u.a2, u.b2); + u.d2 = _125._m0; + u.c2 = _125._m1; + ResType_2 _134; + _134._m0 = u.a3 * u.b3; + _134._m1 = mulhi(u.a3, u.b3); + u.d3 = _134._m0; + u.c3 = _134._m1; + ResType_3 _143; + _143._m0 = u.a4 * u.b4; + _143._m1 = mulhi(u.a4, u.b4); + u.d4 = _143._m0; + u.c4 = _143._m1; + ResType_4 _160; + _160._m0 = i.a * i.b; + _160._m1 = mulhi(i.a, i.b); + i.d = _160._m0; + i.c = _160._m1; + ResType_5 _171; + _171._m0 = i.a2 * i.b2; + _171._m1 = mulhi(i.a2, i.b2); + i.d2 = _171._m0; + i.c2 = _171._m1; + ResType_6 _182; + _182._m0 = i.a3 * i.b3; + _182._m1 = mulhi(i.a3, i.b3); + i.d3 = _182._m0; + i.c3 = _182._m1; + ResType_7 _193; + _193._m0 = i.a4 * i.b4; + _193._m1 = mulhi(i.a4, i.b4); + i.d4 = _193._m0; + i.c4 = _193._m1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/image-ms.desktop.frag b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/image-ms.desktop.frag new file mode 100644 index 0000000..4083e4e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/image-ms.desktop.frag @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +fragment void main0(texture2d_ms uImageMS [[texture(0)]], texture2d_array uImageArray [[texture(1)]], texture2d uImage [[texture(2)]]) +{ + uImage.write(uImageMS.read(uint2(int2(1, 2)), 2), uint2(int2(2, 3))); + uImageArray.write(uImageArray.read(uint2(int3(1, 2, 4).xy), uint(int3(1, 2, 4).z)), uint2(int3(2, 3, 7).xy), uint(int3(2, 3, 7).z)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/query-levels.desktop.frag b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/query-levels.desktop.frag new file mode 100644 index 0000000..922796b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/query-levels.desktop.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = float4(float(int(uSampler.get_num_mip_levels()))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag new file mode 100644 index 0000000..330bc64 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_ms uSampler [[texture(0)]], texture2d_ms uImage [[texture(1)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = float4(float(int(uSampler.get_num_samples()) + int(uImage.get_num_samples()))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc new file mode 100644 index 0000000..01fceeb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc @@ -0,0 +1,85 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float3 vVertex; +}; + +struct main0_patchOut +{ + spvUnsafeArray vPatch; +}; + +struct main0_in +{ + float3 vInput [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + gl_out[gl_InvocationID].vVertex = gl_in[gl_InvocationID].vInput + gl_in[gl_InvocationID ^ 1].vInput; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0) + { + patchOut.vPatch[0] = float3(10.0); + patchOut.vPatch[1] = float3(20.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(2.0); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc new file mode 100644 index 0000000..863a32a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc @@ -0,0 +1,39 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +struct main0_patchOut +{ + float3 vFoo; +}; + +struct main0_in +{ + uint3 m_86; + ushort2 m_90; + float4 gl_Position; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 1]; + device main0_patchOut& patchOut = spvPatchOut[gl_GlobalInvocationID.x / 1]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 1; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1]); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.900000095367431640625); + patchOut.vFoo = float3(1.0); + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc new file mode 100644 index 0000000..7276384 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc @@ -0,0 +1,39 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +struct main0_patchOut +{ + float3 vFoo; +}; + +struct main0_in +{ + float4 gl_Position [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 1]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 1) + return; + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.900000095367431640625); + patchOut.vFoo = float3(1.0); + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc new file mode 100644 index 0000000..e47d56a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Boo +{ + float3 a; + uint3 b; +}; + +struct main0_out +{ + Boo vVertex; +}; + +struct main0_in +{ + float3 Boo_a; + uint3 Boo_b; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + Boo _26 = Boo{ gl_in[gl_InvocationID].Boo_a, gl_in[gl_InvocationID].Boo_b }; + gl_out[gl_InvocationID].vVertex = _26; + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(2.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc new file mode 100644 index 0000000..e7e184a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +struct Boo +{ + float3 a; + float3 b; +}; + +struct main0_out +{ + Boo vVertex; +}; + +struct main0_in +{ + float3 Boo_a [[attribute(0)]]; + float3 Boo_b [[attribute(1)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + Boo _25 = Boo{ gl_in[gl_InvocationID].Boo_a, gl_in[gl_InvocationID].Boo_b }; + gl_out[gl_InvocationID].vVertex = _25; + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(2.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese new file mode 100644 index 0000000..dff8cb7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 gl_Position [[attribute(0)]]; +}; + +struct main0_patchIn +{ + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + out.gl_Position = ((patchIn.gl_in[0].gl_Position * gl_TessCoord.x) + (patchIn.gl_in[1].gl_Position * gl_TessCoord.y)) + (patchIn.gl_in[2].gl_Position * gl_TessCoord.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/basic.desktop.sso.vert b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/basic.desktop.sso.vert new file mode 100644 index 0000000..ffb4357 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/basic.desktop.sso.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert new file mode 100644 index 0000000..a414c98 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + out.gl_Position = float4(10.0); + out.gl_ClipDistance[0] = 1.0; + out.gl_ClipDistance[1] = 4.0; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert new file mode 100644 index 0000000..2d98929 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + out.gl_Position = float4(10.0); + out.gl_ClipDistance[0] = 1.0; + out.gl_ClipDistance[1] = 4.0; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert new file mode 100644 index 0000000..b3c8b6b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvStageInputSize [[grid_size]], uint3 spvDispatchBase [[grid_origin]], device main0_out* spvOut [[buffer(28)]]) +{ + device main0_out& out = spvOut[gl_GlobalInvocationID.y * spvStageInputSize.x + gl_GlobalInvocationID.x]; + if (any(gl_GlobalInvocationID >= spvStageInputSize)) + return; + uint gl_BaseVertex = spvDispatchBase.x; + uint gl_BaseInstance = spvDispatchBase.y; + out.gl_Position = float4(float(int(gl_BaseVertex)), float(int(gl_BaseInstance)), 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert new file mode 100644 index 0000000..a32c194 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(uint gl_BaseVertex [[base_vertex]], uint gl_BaseInstance [[base_instance]]) +{ + main0_out out = {}; + out.gl_Position = float4(float(int(gl_BaseVertex)), float(int(gl_BaseInstance)), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/flatten/basic.flatten.vert b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/basic.flatten.vert new file mode 100644 index 0000000..ffb4357 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/basic.flatten.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/flatten/multiindex.flatten.vert b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/multiindex.flatten.vert new file mode 100644 index 0000000..f4549ab --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/multiindex.flatten.vert @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 Data[3][5]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + int2 aIndex [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _20 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _20.Data[in.aIndex.x][in.aIndex.y]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/flatten/push-constant.flatten.vert b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/push-constant.flatten.vert new file mode 100644 index 0000000..8f2e8c1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/push-constant.flatten.vert @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct PushMe +{ + float4x4 MVP; + float2x2 Rot; + float Arr[4]; +}; + +struct main0_out +{ + float2 vRot [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float2 Rot [[attribute(0)]]; + float4 Pos [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant PushMe& registers [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = registers.MVP * in.Pos; + out.vRot = (registers.Rot * in.Rot) + float2(registers.Arr[2]); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/flatten/rowmajor.flatten.vert b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/rowmajor.flatten.vert new file mode 100644 index 0000000..b405281 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/rowmajor.flatten.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVPR; + float4x4 uMVPC; + float4x4 uMVP; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = (_18.uMVPR * in.aVertex) + (in.aVertex * _18.uMVPC); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/flatten/struct.flatten.vert b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/struct.flatten.vert new file mode 100644 index 0000000..d97a34a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/struct.flatten.vert @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +struct Light +{ + packed_float3 Position; + float Radius; + float4 Color; +}; + +struct UBO +{ + float4x4 uMVP; + Light light; +}; + +struct main0_out +{ + float4 vColor [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _18.uMVP * in.aVertex; + out.vColor = float4(0.0); + float3 _39 = in.aVertex.xyz - float3(_18.light.Position); + out.vColor += ((_18.light.Color * fast::clamp(1.0 - (length(_39) / _18.light.Radius), 0.0, 1.0)) * dot(in.aNormal, normalize(_39))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/flatten/swizzle.flatten.vert b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/swizzle.flatten.vert new file mode 100644 index 0000000..5294041 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/swizzle.flatten.vert @@ -0,0 +1,47 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 A; + float2 B0; + float2 B1; + float C0; + float3 C1; + packed_float3 D0; + float D1; + float E0; + float E1; + float E2; + float E3; + float F0; + float2 F1; + float F2; +}; + +struct main0_out +{ + float4 oA [[user(locn0)]]; + float4 oB [[user(locn1)]]; + float4 oC [[user(locn2)]]; + float4 oD [[user(locn3)]]; + float4 oE [[user(locn4)]]; + float4 oF [[user(locn5)]]; + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(constant UBO& _22 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = float4(0.0); + out.oA = _22.A; + out.oB = float4(_22.B0, _22.B1); + out.oC = float4(_22.C0, _22.C1) + float4(_22.C1.xy, _22.C1.z, _22.C0); + out.oD = float4(_22.D0[0], _22.D0[1], _22.D0[2], _22.D1) + float4(_22.D0[0], _22.D0[1], _22.D0[2u], _22.D1); + out.oE = float4(_22.E0, _22.E1, _22.E2, _22.E3); + out.oF = float4(_22.F0, _22.F1, _22.F2); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/flatten/types.flatten.frag b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/types.flatten.frag new file mode 100644 index 0000000..cee53d9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/flatten/types.flatten.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct UBO1 +{ + int4 c; + int4 d; +}; + +struct UBO2 +{ + uint4 e; + uint4 f; +}; + +struct UBO0 +{ + float4 a; + float4 b; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant UBO1& _14 [[buffer(0)]], constant UBO2& _29 [[buffer(1)]], constant UBO0& _41 [[buffer(2)]]) +{ + main0_out out = {}; + out.FragColor = ((((float4(_14.c) + float4(_14.d)) + float4(_29.e)) + float4(_29.f)) + _41.a) + _41.b; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/argument-buffers.msl2.argument.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/argument-buffers.msl2.argument.frag new file mode 100644 index 0000000..bdf9080 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/argument-buffers.msl2.argument.frag @@ -0,0 +1,73 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 ssbo; +}; + +struct SSBOs +{ + float4 ssbo; +}; + +struct Push +{ + float4 push; +}; + +struct UBO +{ + float4 ubo; +}; + +struct UBOs +{ + float4 ubo; +}; + +struct spvDescriptorSetBuffer0 +{ + texture2d uTexture [[id(0)]]; + sampler uTextureSmplr [[id(1)]]; + array, 2> uTextures [[id(2)]]; + array uTexturesSmplr [[id(4)]]; + constant UBO* m_90 [[id(6)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + array, 4> uTexture2 [[id(0)]]; + array uSampler [[id(4)]]; + device SSBO* m_60 [[id(6)]]; + const device SSBOs* ssbos [[id(7)]][2]; +}; + +struct spvDescriptorSetBuffer2 +{ + constant UBOs* ubos [[id(0)]][4]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], constant spvDescriptorSetBuffer2& spvDescriptorSet2 [[buffer(2)]], constant Push& registers [[buffer(3)]]) +{ + main0_out out = {}; + out.FragColor = ((((((spvDescriptorSet0.uTexture.sample(spvDescriptorSet0.uTextureSmplr, in.vUV) + spvDescriptorSet1.uTexture2[2].sample(spvDescriptorSet1.uSampler[1], in.vUV)) + spvDescriptorSet0.uTextures[1].sample(spvDescriptorSet0.uTexturesSmplr[1], in.vUV)) + (*spvDescriptorSet1.m_60).ssbo) + spvDescriptorSet1.ssbos[0]->ssbo) + registers.push) + (*spvDescriptorSet0.m_90).ubo) + spvDescriptorSet2.ubos[0]->ubo; + out.FragColor += (*spvDescriptorSet0.m_90).ubo; + out.FragColor += (*spvDescriptorSet1.m_60).ssbo; + out.FragColor += spvDescriptorSet2.ubos[1]->ubo; + out.FragColor += registers.push; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..79f9025 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,66 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _17 = spvUnsafeArray({ 1.0, 2.0, 3.0, 4.0, 5.0 }); + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + for (int _46 = 0; _46 < 4; ) + { + int _33 = _46 + 1; + out.FragColor += float4(_17[_33]); + _46 = _33; + continue; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-array-lut.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-array-lut.frag new file mode 100644 index 0000000..ba55382 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-array-lut.frag @@ -0,0 +1,68 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _17 = spvUnsafeArray({ 1.0, 2.0, 3.0 }); +constant spvUnsafeArray _21 = spvUnsafeArray({ 4.0, 5.0, 6.0 }); +constant spvUnsafeArray, 2> _22 = spvUnsafeArray, 2>({ spvUnsafeArray({ 1.0, 2.0, 3.0 }), spvUnsafeArray({ 4.0, 5.0, 6.0 }) }); + +struct main0_out +{ + float vOutput [[color(0)]]; +}; + +struct main0_in +{ + int vIndex1 [[user(locn0)]]; + int vIndex2 [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.vOutput = _22[in.vIndex1][in.vIndex2]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag new file mode 100644 index 0000000..0df1243 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag @@ -0,0 +1,105 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + uint index; +}; + +struct UBO2 +{ + uint index2; +}; + +struct spvDescriptorSetBuffer0 +{ + array, 4> uSampler [[id(0)]]; + array uSamplerSmplr [[id(4)]]; + constant UBO* uUBO [[id(8)]]; + constant UBO2* m_50 [[id(9)]]; + constant uint* spvSwizzleConstants [[id(10)]]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant uint* spvSwizzleConstants [[buffer(30)]]) +{ + main0_out out = {}; + constant uint* spvDescriptorSet0_uSamplerSwzl = &spvDescriptorSet0.spvSwizzleConstants[0]; + out.FragColor = spvTextureSwizzle(spvDescriptorSet0.uSampler[(*spvDescriptorSet0.uUBO).index].sample(spvDescriptorSet0.uSamplerSmplr[(*spvDescriptorSet0.uUBO).index], in.vUV), spvDescriptorSet0_uSamplerSwzl[(*spvDescriptorSet0.uUBO).index]); + out.FragColor += spvTextureSwizzle(spvDescriptorSet0.uSampler[(*spvDescriptorSet0.m_50).index2].sample(spvDescriptorSet0.uSamplerSmplr[(*spvDescriptorSet0.m_50).index2], in.vUV), spvDescriptorSet0_uSamplerSwzl[(*spvDescriptorSet0.m_50).index2]); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag new file mode 100644 index 0000000..c447536 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag @@ -0,0 +1,96 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + uint index; +}; + +struct UBO2 +{ + uint index2; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvSwizzleConstants [[buffer(30)]], constant UBO& uUBO [[buffer(0)]], constant UBO2& _50 [[buffer(1)]], array, 4> uSampler [[texture(0)]], array uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + constant uint* uSamplerSwzl = &spvSwizzleConstants[0]; + out.FragColor = spvTextureSwizzle(uSampler[uUBO.index].sample(uSamplerSmplr[uUBO.index], in.vUV), uSamplerSwzl[uUBO.index]); + out.FragColor += spvTextureSwizzle(uSampler[_50.index2].sample(uSamplerSmplr[_50.index2], in.vUV), uSamplerSwzl[_50.index2]); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag new file mode 100644 index 0000000..d00acd7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag @@ -0,0 +1,97 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct spvDescriptorSetBuffer0 +{ + array, 4> uSampler0 [[id(0)]]; + array uSampler0Smplr [[id(4)]]; + constant uint* spvSwizzleConstants [[id(8)]]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant uint* spvSwizzleConstants [[buffer(30)]], texture2d uSampler1 [[texture(0)]], sampler uSampler1Smplr [[sampler(0)]]) +{ + main0_out out = {}; + constant uint* spvDescriptorSet0_uSampler0Swzl = &spvDescriptorSet0.spvSwizzleConstants[0]; + constant uint& uSampler1Swzl = spvSwizzleConstants[0]; + out.FragColor = spvTextureSwizzle(spvDescriptorSet0.uSampler0[2].sample(spvDescriptorSet0.uSampler0Smplr[2], in.vUV), spvDescriptorSet0_uSampler0Swzl[2]); + float4 _73 = spvTextureSwizzle(uSampler1.sample(uSampler1Smplr, in.vUV), uSampler1Swzl); + out.FragColor += _73; + out.FragColor += spvTextureSwizzle(spvDescriptorSet0.uSampler0[1].sample(spvDescriptorSet0.uSampler0Smplr[1], in.vUV), spvDescriptorSet0_uSampler0Swzl[1]); + out.FragColor += _73; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag new file mode 100644 index 0000000..2f9aef1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag @@ -0,0 +1,86 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvSwizzleConstants [[buffer(30)]], array, 4> uSampler [[texture(0)]], array uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + constant uint* uSamplerSwzl = &spvSwizzleConstants[0]; + out.FragColor = spvTextureSwizzle(uSampler[2].sample(uSamplerSmplr[2], in.vUV), uSamplerSwzl[2]); + out.FragColor += spvTextureSwizzle(uSampler[1].sample(uSamplerSmplr[1], in.vUV), uSamplerSwzl[1]); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag new file mode 100644 index 0000000..53b8a74 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct Vertices +{ + float2 uvs[1]; +}; + +struct main0_out +{ + float2 value [[color(0)]]; +}; + +struct main0_in +{ + float3 gl_BaryCoordNoPerspNV [[barycentric_coord, center_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]]) +{ + main0_out out = {}; + int _23 = 3 * int(gl_PrimitiveID); + out.value = ((_19.uvs[_23] * in.gl_BaryCoordNoPerspNV.x) + (_19.uvs[_23 + 1] * in.gl_BaryCoordNoPerspNV.y)) + (_19.uvs[_23 + 2] * in.gl_BaryCoordNoPerspNV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/barycentric-nv.msl22.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/barycentric-nv.msl22.frag new file mode 100644 index 0000000..ae2c704 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/barycentric-nv.msl22.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct Vertices +{ + float2 uvs[1]; +}; + +struct main0_out +{ + float2 value [[color(0)]]; +}; + +struct main0_in +{ + float3 gl_BaryCoordNV [[barycentric_coord, center_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]]) +{ + main0_out out = {}; + int _23 = 3 * int(gl_PrimitiveID); + out.value = ((_19.uvs[_23] * in.gl_BaryCoordNV.x) + (_19.uvs[_23 + 1] * in.gl_BaryCoordNV.y)) + (_19.uvs[_23 + 2] * in.gl_BaryCoordNV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/basic.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/basic.frag new file mode 100644 index 0000000..f33db61 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/basic.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, in.vTex); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/binary-func-unpack-pack-arguments.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/binary-func-unpack-pack-arguments.frag new file mode 100644 index 0000000..134cfe1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/binary-func-unpack-pack-arguments.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + packed_float3 color; + float v; +}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vIn [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = dot(in.vIn, float3(_15.color)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/binary-unpack-pack-arguments.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/binary-unpack-pack-arguments.frag new file mode 100644 index 0000000..8bd538b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/binary-unpack-pack-arguments.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + packed_float3 color; + float v; +}; + +struct main0_out +{ + float3 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vIn [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = cross(in.vIn, float3(_15.color) - in.vIn); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/bitcasting.1d-as-2d.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/bitcasting.1d-as-2d.frag new file mode 100644 index 0000000..d341397 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/bitcasting.1d-as-2d.frag @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor0 [[color(0)]]; + float4 FragColor1 [[color(1)]]; +}; + +struct main0_in +{ + float4 VertGeom [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d TextureBase [[texture(0)]], texture2d TextureDetail [[texture(1)]], sampler TextureBaseSmplr [[sampler(0)]], sampler TextureDetailSmplr [[sampler(1)]]) +{ + main0_out out = {}; + float4 _22 = TextureBase.sample(TextureBaseSmplr, float2(in.VertGeom.x, 0.5)); + float4 _30 = TextureDetail.sample(TextureDetailSmplr, float2(in.VertGeom.x, 0.5), int2(3, 0)); + out.FragColor0 = as_type(as_type(_22)) * as_type(as_type(_30)); + out.FragColor1 = as_type(as_type(_22)) * as_type(as_type(_30)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/bitcasting.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/bitcasting.frag new file mode 100644 index 0000000..ae6d45e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/bitcasting.frag @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor0 [[color(0)]]; + float4 FragColor1 [[color(1)]]; +}; + +struct main0_in +{ + float4 VertGeom [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d TextureBase [[texture(0)]], texture2d TextureDetail [[texture(1)]], sampler TextureBaseSmplr [[sampler(0)]], sampler TextureDetailSmplr [[sampler(1)]]) +{ + main0_out out = {}; + float4 _20 = TextureBase.sample(TextureBaseSmplr, in.VertGeom.xy); + float4 _31 = TextureDetail.sample(TextureDetailSmplr, in.VertGeom.xy, int2(3, 2)); + out.FragColor0 = as_type(as_type(_20)) * as_type(as_type(_31)); + out.FragColor1 = as_type(as_type(_20)) * as_type(as_type(_31)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/buffer-read-write.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/buffer-read-write.frag new file mode 100644 index 0000000..166d431 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/buffer-read-write.frag @@ -0,0 +1,27 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +fragment main0_out main0(texture2d buf [[texture(0)]], texture2d bufOut [[texture(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = buf.read(spvTexelBufferCoord(0)); + bufOut.write(out.FragColor, spvTexelBufferCoord(int(gl_FragCoord.x))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag new file mode 100644 index 0000000..71496a4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture_buffer buf [[texture(0)]], texture_buffer bufOut [[texture(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = buf.read(uint(0)); + bufOut.write(out.FragColor, uint(int(gl_FragCoord.x))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/builtins.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/builtins.frag new file mode 100644 index 0000000..f908525 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/builtins.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + float gl_FragDepth [[depth(any)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = gl_FragCoord + in.vColor; + out.gl_FragDepth = 0.5; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/clip-distance-varying.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/clip-distance-varying.frag new file mode 100644 index 0000000..9a72d5b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/clip-distance-varying.frag @@ -0,0 +1,67 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray gl_ClipDistance = {}; + gl_ClipDistance[0] = in.gl_ClipDistance_0; + gl_ClipDistance[1] = in.gl_ClipDistance_1; + out.FragColor = float4((1.0 - gl_ClipDistance[0]) - gl_ClipDistance[1]); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..0889469 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 results[1024]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int vIn [[user(locn0)]]; + int vIn2 [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], device UBO& _34 [[buffer(0)]], texture2d Buf [[texture(0)]], sampler BufSmplr [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + int _40 = Buf.read(uint2(int2(gl_FragCoord.xy)), 0).x % 16; + out.FragColor = (_34.results[_40] + _34.results[_40]) + _34.results[(in.vIn * in.vIn) + (in.vIn2 * in.vIn2)]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/composite-extract-forced-temporary.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/composite-extract-forced-temporary.frag new file mode 100644 index 0000000..6948e4d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/composite-extract-forced-temporary.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vTexCoord [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d Texture [[texture(0)]], sampler TextureSmplr [[sampler(0)]]) +{ + main0_out out = {}; + float4 _19 = Texture.sample(TextureSmplr, in.vTexCoord); + float _22 = _19.x; + out.FragColor = float4(_22 * _22); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/constant-array.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/constant-array.frag new file mode 100644 index 0000000..ca7efc5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/constant-array.frag @@ -0,0 +1,76 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Foobar +{ + float a; + float b; +}; + +constant spvUnsafeArray _37 = spvUnsafeArray({ float4(1.0), float4(2.0), float4(3.0) }); +constant spvUnsafeArray _49 = spvUnsafeArray({ float4(1.0), float4(2.0) }); +constant spvUnsafeArray _54 = spvUnsafeArray({ float4(8.0), float4(10.0) }); +constant spvUnsafeArray, 2> _55 = spvUnsafeArray, 2>({ spvUnsafeArray({ float4(1.0), float4(2.0) }), spvUnsafeArray({ float4(8.0), float4(10.0) }) }); + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + spvUnsafeArray _75 = spvUnsafeArray({ Foobar{ 10.0, 40.0 }, Foobar{ 90.0, 70.0 } }); + + main0_out out = {}; + out.FragColor = ((_37[in.index] + _55[in.index][in.index + 1]) + float4(30.0)) + float4(_75[in.index].a + _75[in.index].b); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/constant-composites.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/constant-composites.frag new file mode 100644 index 0000000..e0fa980 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/constant-composites.frag @@ -0,0 +1,74 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Foo +{ + float a; + float b; +}; + +constant spvUnsafeArray _16 = spvUnsafeArray({ 1.0, 4.0, 3.0, 2.0 }); + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int line [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + spvUnsafeArray _28 = spvUnsafeArray({ Foo{ 10.0, 20.0 }, Foo{ 30.0, 40.0 } }); + + main0_out out = {}; + out.FragColor = float4(_16[in.line]); + out.FragColor += float4(_28[in.line].a * _28[1 - in.line].a); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..b75f86d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,45 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vInput [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = in.vInput; + float4 _23 = uSampler.sample(uSamplerSmplr, in.vInput.xy); + float4 _26 = dfdx(in.vInput); + float4 _29 = dfdy(in.vInput); + float4 _32 = fwidth(in.vInput); + float4 _35 = dfdx(in.vInput); + float4 _38 = dfdy(in.vInput); + float4 _41 = fwidth(in.vInput); + float4 _44 = dfdx(in.vInput); + float4 _47 = dfdy(in.vInput); + float4 _50 = fwidth(in.vInput); + if (in.vInput.y > 10.0) + { + out.FragColor += _23; + out.FragColor += _26; + out.FragColor += _29; + out.FragColor += _32; + out.FragColor += _35; + out.FragColor += _38; + out.FragColor += _41; + out.FragColor += _44; + out.FragColor += _47; + out.FragColor += _50; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/depth-greater-than.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/depth-greater-than.frag new file mode 100644 index 0000000..5861509 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/depth-greater-than.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float gl_FragDepth [[depth(greater)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.gl_FragDepth = 0.5; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/depth-less-than.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/depth-less-than.frag new file mode 100644 index 0000000..f1177fa --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/depth-less-than.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float gl_FragDepth [[depth(less)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.gl_FragDepth = 0.5; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/disable-frag-output.frag-output.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/disable-frag-output.frag-output.frag new file mode 100644 index 0000000..63bc45b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/disable-frag-output.frag-output.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 buf1 [[color(1)]]; + float4 buf3 [[color(3)]]; + float4 buf6 [[color(6)]]; + float4 buf7 [[color(7)]]; +}; + +fragment main0_out main0() +{ + float4 buf0; + float4 buf2; + float4 buf4; + float4 buf5; + float gl_FragDepth; + int gl_FragStencilRefARB; + main0_out out = {}; + buf0 = float4(0.0, 0.0, 0.0, 1.0); + out.buf1 = float4(1.0, 0.0, 0.0, 1.0); + buf2 = float4(0.0, 1.0, 0.0, 1.0); + out.buf3 = float4(0.0, 0.0, 1.0, 1.0); + buf4 = float4(1.0, 0.0, 1.0, 0.5); + buf5 = float4(0.25); + out.buf6 = float4(0.75); + out.buf7 = float4(1.0); + gl_FragDepth = 0.89999997615814208984375; + gl_FragStencilRefARB = uint(127); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/dual-source-blending.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/dual-source-blending.frag new file mode 100644 index 0000000..37938bf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/dual-source-blending.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor0 [[color(0), index(0)]]; + float4 FragColor1 [[color(0), index(1)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor0 = float4(1.0); + out.FragColor1 = float4(2.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/early-fragment-tests.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/early-fragment-tests.frag new file mode 100644 index 0000000..850fdc9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/early-fragment-tests.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +[[ early_fragment_tests ]] fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/false-loop-init.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/false-loop-init.frag new file mode 100644 index 0000000..9233caa --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/false-loop-init.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 result [[color(0)]]; +}; + +struct main0_in +{ + float4 accum [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.result = float4(0.0); + for (int _48 = 0; _48 < 4; ) + { + out.result += in.accum; + _48 += int((in.accum.y > 10.0) ? 40u : 30u); + continue; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/flush_params.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/flush_params.frag new file mode 100644 index 0000000..64edee8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/flush_params.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(10.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..b4cd208 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(0.0); + for (int _43 = 0; _43 < 3; ) + { + out.FragColor[_43] += float(_43); + _43++; + continue; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/for-loop-init.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/for-loop-init.frag new file mode 100644 index 0000000..1a42f6e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/for-loop-init.frag @@ -0,0 +1,71 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + int FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + do + { + out.FragColor = 16; + for (int _143 = 0; _143 < 25; ) + { + out.FragColor += 10; + _143++; + continue; + } + for (int _144 = 1; _144 < 30; ) + { + out.FragColor += 11; + _144++; + continue; + } + int _145; + _145 = 0; + for (; _145 < 20; ) + { + out.FragColor += 12; + _145++; + continue; + } + int _62 = _145 + 3; + out.FragColor += _62; + if (_62 == 40) + { + for (int _149 = 0; _149 < 40; ) + { + out.FragColor += 13; + _149++; + continue; + } + break; + } + out.FragColor += _62; + int2 _146; + _146 = int2(0); + for (; _146.x < 10; ) + { + out.FragColor += _146.y; + int2 _142 = _146; + _142.x = _146.x + 4; + _146 = _142; + continue; + } + for (int _148 = _62; _148 < 40; ) + { + out.FragColor += _148; + _148++; + continue; + } + out.FragColor += _62; + break; + } while(false); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/fp16-packing.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/fp16-packing.frag new file mode 100644 index 0000000..e21feb4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/fp16-packing.frag @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float2 FP32Out [[color(0)]]; + uint FP16Out [[color(1)]]; +}; + +struct main0_in +{ + uint FP16 [[user(locn0)]]; + float2 FP32 [[user(locn1), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FP32Out = float2(as_type(in.FP16)); + out.FP16Out = as_type(half2(in.FP32)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/fragment-component-padding.pad-fragment.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/fragment-component-padding.pad-fragment.frag new file mode 100644 index 0000000..19840fa --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/fragment-component-padding.pad-fragment.frag @@ -0,0 +1,76 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColors_0 [[color(0)]]; + float4 FragColors_1 [[color(1)]]; + float4 FragColor2 [[color(2)]]; + float4 FragColor3 [[color(3)]]; +}; + +struct main0_in +{ + float3 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray FragColors = {}; + float2 FragColor2 = {}; + float3 FragColor3 = {}; + FragColors[0] = in.vColor.x; + FragColors[1] = in.vColor.y; + FragColor2 = in.vColor.xz; + FragColor3 = in.vColor.zzz; + out.FragColors_0 = float4(FragColors[0]); + out.FragColors_1 = float4(FragColors[1]); + out.FragColor2.xy = FragColor2; + out.FragColor3.xyz = FragColor3; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/front-facing.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/front-facing.frag new file mode 100644 index 0000000..2f83642 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/front-facing.frag @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vA [[user(locn0)]]; + float4 vB [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], bool gl_FrontFacing [[front_facing]]) +{ + main0_out out = {}; + if (gl_FrontFacing) + { + out.FragColor = in.vA; + } + else + { + out.FragColor = in.vB; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/gather-dref.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/gather-dref.frag new file mode 100644 index 0000000..c5c5ccf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/gather-dref.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uT [[texture(0)]], sampler uTSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uT.gather_compare(uTSmplr, in.vUV.xy, in.vUV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/gather-offset.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/gather-offset.frag new file mode 100644 index 0000000..02b8019 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/gather-offset.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uT [[texture(0)]], sampler uTSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uT.gather(uTSmplr, float2(0.5), int2(0), component::w); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/helper-invocation.msl21.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/helper-invocation.msl21.frag new file mode 100644 index 0000000..a833fa0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/helper-invocation.msl21.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + bool gl_HelperInvocation = simd_is_helper_thread(); + float4 _52; + if (!gl_HelperInvocation) + { + _52 = uSampler.sample(uSamplerSmplr, in.vUV, level(0.0)); + } + else + { + _52 = float4(1.0); + } + out.FragColor = _52; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag new file mode 100644 index 0000000..0c6e6f4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag @@ -0,0 +1,44 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 v; +}; + +struct spvDescriptorSetBuffer0 +{ + array, 10000> uSamplers [[id(0)]]; + array uSamplersSmplr [[id(10000)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + constant UBO* vs [[id(0)]][10000]; +}; + +struct spvDescriptorSetBuffer2 +{ + texture2d uSampler [[id(0)]]; + sampler uSamplerSmplr [[id(1)]]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], const device spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], constant spvDescriptorSetBuffer2& spvDescriptorSet2 [[buffer(2)]]) +{ + main0_out out = {}; + out.FragColor = (spvDescriptorSet0.uSamplers[9999].sample(spvDescriptorSet0.uSamplersSmplr[9999], in.vUV) + spvDescriptorSet1.vs[5000]->v) + spvDescriptorSet2.uSampler.sample(spvDescriptorSet2.uSamplerSmplr, in.vUV); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/illegal-name-test-0.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/illegal-name-test-0.frag new file mode 100644 index 0000000..81cd3b5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/illegal-name-test-0.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(40.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/image-query-lod.msl22.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/image-query-lod.msl22.frag new file mode 100644 index 0000000..de7a60c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/image-query-lod.msl22.frag @@ -0,0 +1,70 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float2 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler2D [[texture(0)]], texture3d uSampler3D [[texture(1)]], texturecube uSamplerCube [[texture(2)]], texture2d uTexture2D [[texture(3)]], texture3d uTexture3D [[texture(4)]], texturecube uTextureCube [[texture(5)]], sampler uSampler2DSmplr [[sampler(0)]], sampler uSampler3DSmplr [[sampler(1)]], sampler uSamplerCubeSmplr [[sampler(2)]], sampler uSampler [[sampler(3)]]) +{ + main0_out out = {}; + out.FragColor = float2(0.0); + float2 _79; + _79.x = uSampler2D.calculate_clamped_lod(uSampler2DSmplr, in.vUV.xy); + _79.y = uSampler2D.calculate_unclamped_lod(uSampler2DSmplr, in.vUV.xy); + out.FragColor += _79; + float2 _84; + _84.x = uSampler3D.calculate_clamped_lod(uSampler3DSmplr, in.vUV); + _84.y = uSampler3D.calculate_unclamped_lod(uSampler3DSmplr, in.vUV); + out.FragColor += _84; + float2 _89; + _89.x = uSamplerCube.calculate_clamped_lod(uSamplerCubeSmplr, in.vUV); + _89.y = uSamplerCube.calculate_unclamped_lod(uSamplerCubeSmplr, in.vUV); + out.FragColor += _89; + float2 _97; + _97.x = uTexture2D.calculate_clamped_lod(uSampler, in.vUV.xy); + _97.y = uTexture2D.calculate_unclamped_lod(uSampler, in.vUV.xy); + out.FragColor += _97; + float2 _104; + _104.x = uTexture3D.calculate_clamped_lod(uSampler, in.vUV); + _104.y = uTexture3D.calculate_unclamped_lod(uSampler, in.vUV); + out.FragColor += _104; + float2 _111; + _111.x = uTextureCube.calculate_clamped_lod(uSampler, in.vUV); + _111.y = uTextureCube.calculate_unclamped_lod(uSampler, in.vUV); + out.FragColor += _111; + float2 _119; + _119.x = uSampler2D.calculate_clamped_lod(uSampler2DSmplr, in.vUV.xy); + _119.y = uSampler2D.calculate_unclamped_lod(uSampler2DSmplr, in.vUV.xy); + out.FragColor += _119; + float2 _124; + _124.x = uSampler3D.calculate_clamped_lod(uSampler3DSmplr, in.vUV); + _124.y = uSampler3D.calculate_unclamped_lod(uSampler3DSmplr, in.vUV); + out.FragColor += _124; + float2 _129; + _129.x = uSamplerCube.calculate_clamped_lod(uSamplerCubeSmplr, in.vUV); + _129.y = uSamplerCube.calculate_unclamped_lod(uSamplerCubeSmplr, in.vUV); + out.FragColor += _129; + float2 _137; + _137.x = uTexture2D.calculate_clamped_lod(uSampler, in.vUV.xy); + _137.y = uTexture2D.calculate_unclamped_lod(uSampler, in.vUV.xy); + out.FragColor += _137; + float2 _144; + _144.x = uTexture3D.calculate_clamped_lod(uSampler, in.vUV); + _144.y = uTexture3D.calculate_unclamped_lod(uSampler, in.vUV); + out.FragColor += _144; + float2 _151; + _151.x = uTextureCube.calculate_clamped_lod(uSampler, in.vUV); + _151.y = uTextureCube.calculate_unclamped_lod(uSampler, in.vUV); + out.FragColor += _151; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/in_block.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/in_block.frag new file mode 100644 index 0000000..8178c9a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/in_block.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct VertexOut +{ + float4 color; + float4 color2; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 VertexOut_color [[user(locn2)]]; + float4 VertexOut_color2 [[user(locn3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + VertexOut inputs = {}; + inputs.color = in.VertexOut_color; + inputs.color2 = in.VertexOut_color2; + out.FragColor = inputs.color + inputs.color2; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/in_mat.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/in_mat.frag new file mode 100644 index 0000000..83ed9b5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/in_mat.frag @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 outFragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 inPos [[user(locn0)]]; + float3 inNormal [[user(locn1)]]; + float4 inInvModelView_0 [[user(locn2)]]; + float4 inInvModelView_1 [[user(locn3)]]; + float4 inInvModelView_2 [[user(locn4)]]; + float4 inInvModelView_3 [[user(locn5)]]; + float inLodBias [[user(locn6)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texturecube samplerColor [[texture(0)]], sampler samplerColorSmplr [[sampler(0)]]) +{ + main0_out out = {}; + float4x4 inInvModelView = {}; + inInvModelView[0] = in.inInvModelView_0; + inInvModelView[1] = in.inInvModelView_1; + inInvModelView[2] = in.inInvModelView_2; + inInvModelView[3] = in.inInvModelView_3; + float4 _31 = inInvModelView * float4(reflect(normalize(in.inPos), normalize(in.inNormal)), 0.0); + float _33 = _31.x; + float3 _59 = float3(_33, _31.yz); + _59.x = _33 * (-1.0); + out.outFragColor = samplerColor.sample(samplerColorSmplr, _59, bias(in.inLodBias)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag new file mode 100644 index 0000000..5f137b0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_ms_array uSubpass0 [[texture(0)]], texture2d_ms_array uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]], uint gl_Layer [[render_target_array_index]]) +{ + main0_out out = {}; + out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), gl_Layer, 1) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_Layer, 2)) + uSubpass0.read(uint2(gl_FragCoord.xy), gl_Layer, gl_SampleID); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.frag new file mode 100644 index 0000000..906cabb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_ms uSubpass0 [[texture(0)]], texture2d_ms uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), 1) + uSubpass1.read(uint2(gl_FragCoord.xy), 2)) + uSubpass0.read(uint2(gl_FragCoord.xy), gl_SampleID); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag new file mode 100644 index 0000000..2e4ca8a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant uint* spvViewMask [[buffer(24)]], texture2d_ms_array uSubpass0 [[texture(0)]], texture2d_ms_array uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]], uint gl_ViewIndex [[render_target_array_index]]) +{ + main0_out out = {}; + gl_ViewIndex += spvViewMask[0]; + out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), gl_ViewIndex, 1) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_ViewIndex, 2)) + uSubpass0.read(uint2(gl_FragCoord.xy), gl_ViewIndex, gl_SampleID); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.arrayed-subpass.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.arrayed-subpass.frag new file mode 100644 index 0000000..5d5ee43 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.arrayed-subpass.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_array uSubpass0 [[texture(0)]], texture2d_array uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]], uint gl_Layer [[render_target_array_index]]) +{ + main0_out out = {}; + out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy), gl_Layer) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_Layer); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.frag new file mode 100644 index 0000000..790dce3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uSubpass0 [[texture(0)]], texture2d uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy)) + uSubpass1.read(uint2(gl_FragCoord.xy)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.multiview.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.multiview.frag new file mode 100644 index 0000000..7cf06d2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/input-attachment.multiview.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant uint* spvViewMask [[buffer(24)]], texture2d_array uSubpass0 [[texture(0)]], texture2d_array uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]], uint gl_ViewIndex [[render_target_array_index]]) +{ + main0_out out = {}; + gl_ViewIndex += spvViewMask[0]; + out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy), gl_ViewIndex) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_ViewIndex); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/interpolation-qualifiers-block.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/interpolation-qualifiers-block.frag new file mode 100644 index 0000000..2b42019 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/interpolation-qualifiers-block.frag @@ -0,0 +1,47 @@ +#include +#include + +using namespace metal; + +struct Input +{ + float2 v0; + float2 v1; + float3 v2; + float4 v3; + float v4; + float v5; + float v6; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 Input_v0 [[user(locn0), centroid_no_perspective]]; + float2 Input_v1 [[user(locn1), centroid_no_perspective]]; + float3 Input_v2 [[user(locn2), centroid_no_perspective]]; + float4 Input_v3 [[user(locn3), centroid_no_perspective]]; + float Input_v4 [[user(locn4), centroid_no_perspective]]; + float Input_v5 [[user(locn5), centroid_no_perspective]]; + float Input_v6 [[user(locn6), centroid_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Input inp = {}; + inp.v0 = in.Input_v0; + inp.v1 = in.Input_v1; + inp.v2 = in.Input_v2; + inp.v3 = in.Input_v3; + inp.v4 = in.Input_v4; + inp.v5 = in.Input_v5; + inp.v6 = in.Input_v6; + out.FragColor = float4(inp.v0.x + inp.v1.y, inp.v2.xy, ((inp.v3.w * inp.v4) + inp.v5) - inp.v6); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/interpolation-qualifiers.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/interpolation-qualifiers.frag new file mode 100644 index 0000000..aff6e1b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/interpolation-qualifiers.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 v0 [[user(locn0)]]; + float2 v1 [[user(locn1), center_no_perspective]]; + float3 v2 [[user(locn2), centroid_perspective]]; + float4 v3 [[user(locn3), centroid_no_perspective]]; + float v4 [[user(locn4), sample_perspective]]; + float v5 [[user(locn5), sample_no_perspective]]; + float v6 [[user(locn6), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = float4(in.v0.x + in.v1.y, in.v2.xy, ((in.v3.w * in.v4) + in.v5) - in.v6); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/lut-promotion.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000..e24bcd6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/lut-promotion.frag @@ -0,0 +1,92 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _16 = spvUnsafeArray({ 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0 }); +constant spvUnsafeArray _60 = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); +constant spvUnsafeArray _104 = spvUnsafeArray({ float4(20.0), float4(30.0), float4(50.0), float4(60.0) }); + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = _16[in.index]; + if (in.index < 10) + { + out.FragColor += _16[in.index ^ 1]; + } + else + { + out.FragColor += _16[in.index & 1]; + } + bool _63 = in.index > 30; + if (_63) + { + out.FragColor += _60[in.index & 3].y; + } + else + { + out.FragColor += _60[in.index & 1].x; + } + spvUnsafeArray foobar = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); + if (_63) + { + foobar[1].z = 20.0; + } + int _91 = in.index & 3; + out.FragColor += foobar[_91].z; + out.FragColor += _104[_91].z; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/mix.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/mix.frag new file mode 100644 index 0000000..5e38508 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/mix.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vIn0 [[user(locn0)]]; + float4 vIn1 [[user(locn1)]]; + float vIn2 [[user(locn2)]]; + float vIn3 [[user(locn3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = float4(in.vIn0.x, in.vIn1.y, in.vIn0.z, in.vIn0.w); + out.FragColor = float4(in.vIn3); + out.FragColor = in.vIn0.xyzw; + out.FragColor = float4(in.vIn2); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/mrt-array.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/mrt-array.frag new file mode 100644 index 0000000..79fc039 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/mrt-array.frag @@ -0,0 +1,82 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor_0 [[color(0)]]; + float4 FragColor_1 [[color(1)]]; + float4 FragColor_2 [[color(2)]]; + float4 FragColor_3 [[color(3)]]; +}; + +struct main0_in +{ + float4 vA [[user(locn0)]]; + float4 vB [[user(locn1)]]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray FragColor = {}; + FragColor[0] = mod(in.vA, in.vB); + FragColor[1] = in.vA + in.vB; + FragColor[2] = in.vA - in.vB; + FragColor[3] = in.vA * in.vB; + out.FragColor_0 = FragColor[0]; + out.FragColor_1 = FragColor[1]; + out.FragColor_2 = FragColor[2]; + out.FragColor_3 = FragColor[3]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/nonuniform-qualifier.msl2.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/nonuniform-qualifier.msl2.frag new file mode 100644 index 0000000..c120959 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/nonuniform-qualifier.msl2.frag @@ -0,0 +1,53 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 v[64]; +}; + +struct SSBO +{ + float4 v[1]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int vIndex [[user(locn0)]]; + float2 vUV [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant UBO* ubos_0 [[buffer(0)]], constant UBO* ubos_1 [[buffer(1)]], const device SSBO* ssbos_0 [[buffer(2)]], const device SSBO* ssbos_1 [[buffer(3)]], array, 8> uSamplers [[texture(0)]], array, 8> uCombinedSamplers [[texture(8)]], array uSamps [[sampler(0)]], array uCombinedSamplersSmplr [[sampler(7)]]) +{ + constant UBO* ubos[] = + { + ubos_0, + ubos_1, + }; + + const device SSBO* ssbos[] = + { + ssbos_0, + ssbos_1, + }; + + main0_out out = {}; + int _25 = in.vIndex + 10; + int _37 = in.vIndex + 40; + out.FragColor = uSamplers[_25].sample(uSamps[_37], in.vUV); + out.FragColor = uCombinedSamplers[_25].sample(uCombinedSamplersSmplr[_25], in.vUV); + int _69 = in.vIndex + 20; + out.FragColor += ubos[(_69)]->v[_37]; + int _87 = in.vIndex + 50; + int _91 = in.vIndex + 60; + out.FragColor += ssbos[(_87)]->v[_91]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/packed-expression-vector-shuffle.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/packed-expression-vector-shuffle.frag new file mode 100644 index 0000000..dd319af --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/packed-expression-vector-shuffle.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + packed_float3 color; + float v; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant UBO& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = float4(_15.color[0], _15.color[1], _15.color[2], float4(1.0).w); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/packing-test-3.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/packing-test-3.frag new file mode 100644 index 0000000..1f696e9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/packing-test-3.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct TestStruct +{ + packed_float3 position; + float radius; +}; + +struct CB0 +{ + TestStruct CB0[16]; +}; + +struct main0_out +{ + float4 _entryPointOutput [[color(0)]]; +}; + +fragment main0_out main0(constant CB0& _RESERVED_IDENTIFIER_FIXUP_24 [[buffer(0)]]) +{ + main0_out out = {}; + out._entryPointOutput = float4(_RESERVED_IDENTIFIER_FIXUP_24.CB0[1].position[0], _RESERVED_IDENTIFIER_FIXUP_24.CB0[1].position[1], _RESERVED_IDENTIFIER_FIXUP_24.CB0[1].position[2], _RESERVED_IDENTIFIER_FIXUP_24.CB0[1].radius); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag new file mode 100644 index 0000000..adea453 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag @@ -0,0 +1,53 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct Buffer3 +{ + int baz; +}; + +struct Buffer +{ + int foo; + uint bar; +}; + +struct Buffer2 +{ + uint quux; +}; + +struct spvDescriptorSetBuffer0 +{ + device Buffer3* m_9 [[id(0)]]; + texture2d img4 [[id(1)]]; + texture2d img [[id(2), raster_order_group(0)]]; + texture2d img3 [[id(3), raster_order_group(0)]]; + texture2d img2 [[id(4), raster_order_group(0)]]; + device atomic_uint* img2_atomic [[id(5), raster_order_group(0)]]; + volatile device Buffer* m_42 [[id(6), raster_order_group(0)]]; + device Buffer2* m_52 [[id(7), raster_order_group(0)]]; +}; + +// The required alignment of a linear texture of R32Uint format. +constant uint spvLinearTextureAlignmentOverride [[function_constant(65535)]]; +constant uint spvLinearTextureAlignment = is_function_constant_defined(spvLinearTextureAlignmentOverride) ? spvLinearTextureAlignmentOverride : 4; +// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics +#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + spvLinearTextureAlignment / 4 - 1) & ~( spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x) + +fragment void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) +{ + (*spvDescriptorSet0.m_9).baz = 0; + spvDescriptorSet0.img4.write(float4(1.0, 0.0, 0.0, 1.0), uint2(int2(1))); + spvDescriptorSet0.img.write(spvDescriptorSet0.img3.read(uint2(int2(0))), uint2(int2(0))); + uint _39 = atomic_fetch_add_explicit((device atomic_uint*)&spvDescriptorSet0.img2_atomic[spvImage2DAtomicCoord(int2(0), spvDescriptorSet0.img2)], 1u, memory_order_relaxed); + (*spvDescriptorSet0.m_42).foo += 42; + uint _55 = atomic_fetch_and_explicit((volatile device atomic_uint*)&(*spvDescriptorSet0.m_42).bar, (*spvDescriptorSet0.m_52).quux, memory_order_relaxed); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/pixel-interlock-ordered.msl2.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/pixel-interlock-ordered.msl2.frag new file mode 100644 index 0000000..e409ea0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/pixel-interlock-ordered.msl2.frag @@ -0,0 +1,41 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct Buffer3 +{ + int baz; +}; + +struct Buffer +{ + int foo; + uint bar; +}; + +struct Buffer2 +{ + uint quux; +}; + +// The required alignment of a linear texture of R32Uint format. +constant uint spvLinearTextureAlignmentOverride [[function_constant(65535)]]; +constant uint spvLinearTextureAlignment = is_function_constant_defined(spvLinearTextureAlignmentOverride) ? spvLinearTextureAlignmentOverride : 4; +// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics +#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + spvLinearTextureAlignment / 4 - 1) & ~( spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x) + +fragment void main0(device Buffer3& _9 [[buffer(0)]], volatile device Buffer& _42 [[buffer(2), raster_order_group(0)]], device Buffer2& _52 [[buffer(3), raster_order_group(0)]], texture2d img4 [[texture(0)]], texture2d img [[texture(1), raster_order_group(0)]], texture2d img3 [[texture(2), raster_order_group(0)]], texture2d img2 [[texture(3), raster_order_group(0)]], device atomic_uint* img2_atomic [[buffer(1), raster_order_group(0)]]) +{ + _9.baz = 0; + img4.write(float4(1.0, 0.0, 0.0, 1.0), uint2(int2(1))); + img.write(img3.read(uint2(int2(0))), uint2(int2(0))); + uint _39 = atomic_fetch_add_explicit((device atomic_uint*)&img2_atomic[spvImage2DAtomicCoord(int2(0), img2)], 1u, memory_order_relaxed); + _42.foo += 42; + uint _55 = atomic_fetch_and_explicit((volatile device atomic_uint*)&_42.bar, _52.quux, memory_order_relaxed); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/pls.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/pls.frag new file mode 100644 index 0000000..ee774a0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/pls.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 PLSOut0 [[color(0)]]; + float4 PLSOut1 [[color(1)]]; + float4 PLSOut2 [[color(2)]]; + float4 PLSOut3 [[color(3)]]; +}; + +struct main0_in +{ + float4 PLSIn0 [[user(locn0)]]; + float4 PLSIn1 [[user(locn1)]]; + float4 PLSIn2 [[user(locn2)]]; + float4 PLSIn3 [[user(locn3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.PLSOut0 = in.PLSIn0 * 2.0; + out.PLSOut1 = in.PLSIn1 * 6.0; + out.PLSOut2 = in.PLSIn2 * 7.0; + out.PLSOut3 = in.PLSIn3 * 4.0; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/post-depth-coverage.ios.msl2.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/post-depth-coverage.ios.msl2.frag new file mode 100644 index 0000000..3b2885e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/post-depth-coverage.ios.msl2.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +[[ early_fragment_tests ]] fragment main0_out main0(uint gl_SampleMaskIn [[sample_mask, post_depth_coverage]]) +{ + main0_out out = {}; + out.FragColor = float4(float(gl_SampleMaskIn)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/post-depth-coverage.msl23.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/post-depth-coverage.msl23.frag new file mode 100644 index 0000000..3b2885e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/post-depth-coverage.msl23.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +[[ early_fragment_tests ]] fragment main0_out main0(uint gl_SampleMaskIn [[sample_mask, post_depth_coverage]]) +{ + main0_out out = {}; + out.FragColor = float4(float(gl_SampleMaskIn)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/private-variable-prototype-declaration.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/private-variable-prototype-declaration.frag new file mode 100644 index 0000000..1e9dc12 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/private-variable-prototype-declaration.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float3 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float3(1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/readonly-ssbo.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/readonly-ssbo.frag new file mode 100644 index 0000000..777fd65 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/readonly-ssbo.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 v; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(const device SSBO& _13 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = _13.v + _13.v; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag new file mode 100644 index 0000000..4f7e9b5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uTexture [[texture(0)]], sampler uSampler [[sampler(0)]], sampler uSamplerShadow [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = float4(uTexture.sample(uSampler, in.vUV.xy)).x; + out.FragColor += uTexture.sample_compare(uSamplerShadow, in.vUV.xy, in.vUV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-depth-separate-image-sampler.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-depth-separate-image-sampler.frag new file mode 100644 index 0000000..6626946 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-depth-separate-image-sampler.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0(depth2d uDepth [[texture(0)]], texture2d uColor [[texture(1)]], sampler uSamplerShadow [[sampler(0)]], sampler uSampler [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = uDepth.sample_compare(uSamplerShadow, float3(0.5).xy, 0.5) + uColor.sample(uSampler, float2(0.5)).x; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag new file mode 100644 index 0000000..21ca717 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +fragment main0_out main0(uint gl_SampleMaskIn [[sample_mask]]) +{ + main0_out out = {}; + out.FragColor = float4(1.0); + out.gl_SampleMask = gl_SampleMaskIn; + out.gl_SampleMask &= 34; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag new file mode 100644 index 0000000..040c641 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(1.0); + out.gl_SampleMask = 34; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask.fixed-sample-mask.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask.fixed-sample-mask.frag new file mode 100644 index 0000000..2044477 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask.fixed-sample-mask.frag @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(1.0); + out.gl_SampleMask = 0; + out.gl_SampleMask &= 34; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask.frag new file mode 100644 index 0000000..6a28239 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-mask.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(1.0); + out.gl_SampleMask = 0; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-position-func.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-position-func.frag new file mode 100644 index 0000000..87875bb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-position-func.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + float2 gl_SamplePosition = get_sample_position(gl_SampleID); + out.FragColor = float4(gl_SamplePosition, float(in.index), 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-position.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-position.frag new file mode 100644 index 0000000..8d26acb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sample-position.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + float2 gl_SamplePosition = get_sample_position(gl_SampleID); + out.FragColor = float4(gl_SamplePosition, float(gl_SampleID), 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag new file mode 100644 index 0000000..70278b1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float vTex [[user(locn0), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor += ((uSampler.sample(uSamplerSmplr, float2(in.vTex, 0.5), bias(2.0)) + uSampler.sample(uSamplerSmplr, float2(in.vTex, 0.5), level(3.0))) + uSampler.sample(uSamplerSmplr, float2(in.vTex, 0.5), gradient2d(5.0, 8.0))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-1d-lod.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-1d-lod.frag new file mode 100644 index 0000000..96914f8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-1d-lod.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float vTex [[user(locn0), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture1d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor += ((uSampler.sample(uSamplerSmplr, in.vTex) + uSampler.sample(uSamplerSmplr, in.vTex)) + uSampler.sample(uSamplerSmplr, in.vTex)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag new file mode 100644 index 0000000..6aaffe5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, float2(in.vUV.x, 0.5), uint(round(in.vUV.y)), in.vUV.z, bias(1.0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.frag new file mode 100644 index 0000000..630511b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, level(0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag new file mode 100644 index 0000000..a29ebf0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, gradient2d(float2(0.0), float2(0.0))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag new file mode 100644 index 0000000..0784569 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, level(0)) + uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, gradient2d(float2(1.0), float2(1.0))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-image-arrays.msl2.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-image-arrays.msl2.frag new file mode 100644 index 0000000..f3e10fc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-image-arrays.msl2.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vTex [[user(locn0), flat]]; + int vIndex [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], array, 4> uSampler [[texture(0)]], array, 4> uTextures [[texture(4)]], array uSamplerSmplr [[sampler(0)]], array uSamplers [[sampler(4)]]) +{ + main0_out out = {}; + out.FragColor = float4(0.0); + out.FragColor += uTextures[2].sample(uSamplers[1], in.vTex); + out.FragColor += uSampler[in.vIndex].sample(uSamplerSmplr[in.vIndex], in.vTex); + out.FragColor += uSampler[in.vIndex].sample(uSamplerSmplr[in.vIndex], (in.vTex + float2(0.100000001490116119384765625))); + out.FragColor += uSampler[in.vIndex].sample(uSamplerSmplr[in.vIndex], (in.vTex + float2(0.20000000298023223876953125))); + out.FragColor += uSampler[3].sample(uSamplerSmplr[3], (in.vTex + float2(0.300000011920928955078125))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-ms.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-ms.frag new file mode 100644 index 0000000..8245ed8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler-ms.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_ms uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + int2 _17 = int2(gl_FragCoord.xy); + out.FragColor = ((uSampler.read(uint2(_17), 0) + uSampler.read(uint2(_17), 1)) + uSampler.read(uint2(_17), 2)) + uSampler.read(uint2(_17), 3); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler.frag new file mode 100644 index 0000000..f33db61 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/sampler.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, in.vTex); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..592d445 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/scalar-refract-reflect.frag @@ -0,0 +1,49 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vRefract [[user(locn0)]]; +}; + +template +inline T spvReflect(T i, T n) +{ + return i - T(2) * i * n * n; +} + +template +inline T spvRefract(T i, T n, T eta) +{ + T NoI = n * i; + T NoI2 = NoI * NoI; + T k = T(1) - eta * eta * (T(1) - NoI2); + if (k < T(0)) + { + return T(0); + } + else + { + return eta * i - (eta * NoI + sqrt(k)) * n; + } +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = spvRefract(in.vRefract.x, in.vRefract.y, in.vRefract.z); + out.FragColor += spvReflect(in.vRefract.x, in.vRefract.y); + out.FragColor += refract(in.vRefract.xy, in.vRefract.yz, in.vRefract.z).y; + out.FragColor += reflect(in.vRefract.xy, in.vRefract.zy).y; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/separate-image-sampler-argument.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/separate-image-sampler-argument.frag new file mode 100644 index 0000000..e576b49 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/separate-image-sampler-argument.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uDepth [[texture(0)]], sampler uSampler [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uDepth.sample(uSampler, float2(0.5)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/shader-arithmetic-8bit.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/shader-arithmetic-8bit.frag new file mode 100644 index 0000000..c4d6fde --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/shader-arithmetic-8bit.frag @@ -0,0 +1,53 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + char i8[16]; + uchar u8[16]; +}; + +struct Push +{ + char i8; + uchar u8; +}; + +struct UBO +{ + char i8; + uchar u8; +}; + +struct main0_out +{ + int4 FragColorInt [[color(0)]]; + uint4 FragColorUint [[color(1)]]; +}; + +struct main0_in +{ + int4 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], device SSBO& ssbo [[buffer(0)]], constant Push& registers [[buffer(1)]], constant UBO& ubo [[buffer(2)]]) +{ + main0_out out = {}; + char4 _199 = as_type(20); + ssbo.i8[0] = _199.x; + ssbo.i8[1] = _199.y; + ssbo.i8[2] = _199.z; + ssbo.i8[3] = _199.w; + uchar4 _224 = as_type(20u); + ssbo.u8[0] = _224.x; + ssbo.u8[1] = _224.y; + ssbo.u8[2] = _224.z; + ssbo.u8[3] = _224.w; + char4 _249 = char4(in.vColor); + out.FragColorInt = int4((((((_249 + char4(registers.i8)) + char4(-40)) + char4(-50)) + char4(char(10), char(20), char(30), char(40))) + char4(ssbo.i8[4])) + char4(ubo.i8)); + out.FragColorUint = uint4((((((uchar4(_249) + uchar4(registers.u8)) + uchar4(216)) + uchar4(206)) + uchar4(uchar(10), uchar(20), uchar(30), uchar(40))) + uchar4(ssbo.u8[4])) + uchar4(ubo.u8)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/spec-constant-block-size.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/spec-constant-block-size.frag new file mode 100644 index 0000000..36456b8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/spec-constant-block-size.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 2 +#endif +constant int Value = SPIRV_CROSS_CONSTANT_ID_10; + +struct SpecConstArray +{ + float4 samples[Value]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int Index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant SpecConstArray& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = _15.samples[in.Index]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/spec-constant-ternary.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000..0590065 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant uint s_tmp [[function_constant(0)]]; +constant uint s = is_function_constant_defined(s_tmp) ? s_tmp : 10u; +constant bool _13 = (s > 20u); +constant uint f = _13 ? 30u : 50u; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float(f); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/stencil-export.msl21.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/stencil-export.msl21.frag new file mode 100644 index 0000000..5fc3cf9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/stencil-export.msl21.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 MRT0 [[color(0)]]; + float4 MRT1 [[color(1)]]; + uint gl_FragStencilRefARB [[stencil]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.MRT0 = float4(1.0); + out.MRT1 = float4(1.0, 0.0, 1.0, 1.0); + out.gl_FragStencilRefARB = uint(100); + out.gl_FragStencilRefARB = uint(int(out.gl_FragStencilRefARB) + 10); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/subgroup-builtins.msl22.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/subgroup-builtins.msl22.frag new file mode 100644 index 0000000..03a536f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/subgroup-builtins.msl22.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + uint2 FragColor [[color(0)]]; +}; + +fragment main0_out main0(uint gl_SubgroupSize [[threads_per_simdgroup]], uint gl_SubgroupInvocationID [[thread_index_in_simdgroup]]) +{ + main0_out out = {}; + out.FragColor.x = gl_SubgroupSize; + out.FragColor.y = gl_SubgroupInvocationID; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/switch-unsigned-case.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..4cd2b68 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/switch-unsigned-case.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct Buff +{ + uint TestVal; +}; + +struct main0_out +{ + float4 fsout_Color [[color(0)]]; +}; + +fragment main0_out main0(constant Buff& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.fsout_Color = float4(1.0); + switch (_15.TestVal) + { + case 0u: + { + out.fsout_Color = float4(0.100000001490116119384765625); + break; + } + case 1u: + { + out.fsout_Color = float4(0.20000000298023223876953125); + break; + } + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/swizzle.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/swizzle.frag new file mode 100644 index 0000000..cda2309 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/swizzle.frag @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vNormal [[user(locn1)]]; + float2 vUV [[user(locn2)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d samp [[texture(0)]], sampler sampSmplr [[sampler(0)]]) +{ + main0_out out = {}; + float4 _19 = samp.sample(sampSmplr, in.vUV); + float _23 = _19.x; + out.FragColor = float4(_23, _19.yz, 1.0); + out.FragColor = float4(_23, _19.z, 1.0, 4.0); + out.FragColor = float4(_23, _23, samp.sample(sampSmplr, (in.vUV + float2(0.100000001490116119384765625))).yy); + out.FragColor = float4(in.vNormal, 1.0); + out.FragColor = float4(in.vNormal + float3(1.7999999523162841796875), 1.0); + out.FragColor = float4(in.vUV, in.vUV + float2(1.7999999523162841796875)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag new file mode 100644 index 0000000..98b9bb7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uTexture [[texture(0)]], texture2d uTexture2 [[texture(1)]], sampler uTextureSmplr [[sampler(0)]], sampler uTexture2Smplr [[sampler(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = uTexture.read(uint2(int2(gl_FragCoord.xy)) + uint2(int2(1)), 0); + out.FragColor += uTexture2.read(uint2(uint(int(gl_FragCoord.x)), 0) + uint2(uint(-1), 0), 0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/texel-fetch-offset.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..d4db1ae --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texel-fetch-offset.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + int2 _22 = int2(gl_FragCoord.xy); + out.FragColor = uTexture.read(uint2(_22) + uint2(int2(1)), 0); + out.FragColor += uTexture.read(uint2(_22) + uint2(int2(-1, 1)), 0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-cube-array.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-cube-array.frag new file mode 100644 index 0000000..0af8a04 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-cube-array.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texturecube cubeSampler [[texture(0)]], texturecube_array cubeArraySampler [[texture(1)]], texture2d_array texArraySampler [[texture(2)]], sampler cubeSamplerSmplr [[sampler(0)]], sampler cubeArraySamplerSmplr [[sampler(1)]], sampler texArraySamplerSmplr [[sampler(2)]]) +{ + main0_out out = {}; + out.FragColor = (cubeSampler.sample(cubeSamplerSmplr, in.vUV.xyz) + cubeArraySampler.sample(cubeArraySamplerSmplr, in.vUV.xyz, uint(round(in.vUV.w)))) + texArraySampler.sample(texArraySamplerSmplr, in.vUV.xyz.xy, uint(round(in.vUV.xyz.z))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag new file mode 100644 index 0000000..217a7a9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag @@ -0,0 +1,58 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float3 spvCubemapTo2DArrayFace(float3 P) +{ + float3 Coords = abs(P.xyz); + float CubeFace = 0; + float ProjectionAxis = 0; + float u = 0; + float v = 0; + if (Coords.x >= Coords.y && Coords.x >= Coords.z) + { + CubeFace = P.x >= 0 ? 0 : 1; + ProjectionAxis = Coords.x; + u = P.x >= 0 ? -P.z : P.z; + v = -P.y; + } + else if (Coords.y >= Coords.x && Coords.y >= Coords.z) + { + CubeFace = P.y >= 0 ? 2 : 3; + ProjectionAxis = Coords.y; + u = P.x; + v = P.y >= 0 ? P.z : -P.z; + } + else + { + CubeFace = P.z >= 0 ? 4 : 5; + ProjectionAxis = Coords.z; + u = P.z >= 0 ? P.x : -P.x; + v = -P.y; + } + u = 0.5 * (u/ProjectionAxis + 1); + v = 0.5 * (v/ProjectionAxis + 1); + return float3(u, v, CubeFace); +} + +fragment main0_out main0(main0_in in [[stage_in]], texturecube cubeSampler [[texture(0)]], texture2d_array cubeArraySampler [[texture(1)]], texture2d_array texArraySampler [[texture(2)]], sampler cubeSamplerSmplr [[sampler(0)]], sampler cubeArraySamplerSmplr [[sampler(1)]], sampler texArraySamplerSmplr [[sampler(2)]]) +{ + main0_out out = {}; + out.FragColor = (cubeSampler.sample(cubeSamplerSmplr, in.vUV.xyz) + cubeArraySampler.sample(cubeArraySamplerSmplr, spvCubemapTo2DArrayFace(in.vUV.xyz).xy, uint(spvCubemapTo2DArrayFace(in.vUV.xyz).z) + (uint(round(in.vUV.w)) * 6u))) + texArraySampler.sample(texArraySamplerSmplr, in.vUV.xyz.xy, uint(round(in.vUV.xyz.z))); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-multisample-array.msl21.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-multisample-array.msl21.frag new file mode 100644 index 0000000..ed1e81f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-multisample-array.msl21.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int3 vCoord [[user(locn0)]]; + int vSample [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d_ms_array uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTexture.read(uint2(in.vCoord.xy), uint(in.vCoord.z), in.vSample); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-proj-shadow.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-proj-shadow.frag new file mode 100644 index 0000000..52d4a02 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/texture-proj-shadow.frag @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vClip3 [[user(locn0)]]; + float4 vClip4 [[user(locn1)]]; + float2 vClip2 [[user(locn2)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uShadow2D [[texture(0)]], texture1d uSampler1D [[texture(1)]], texture2d uSampler2D [[texture(2)]], texture3d uSampler3D [[texture(3)]], sampler uShadow2DSmplr [[sampler(0)]], sampler uSampler1DSmplr [[sampler(1)]], sampler uSampler2DSmplr [[sampler(2)]], sampler uSampler3DSmplr [[sampler(3)]]) +{ + main0_out out = {}; + float4 _20 = in.vClip4; + _20.z = in.vClip4.w; + out.FragColor = uShadow2D.sample_compare(uShadow2DSmplr, _20.xy / _20.z, in.vClip4.z / _20.z); + out.FragColor = uSampler1D.sample(uSampler1DSmplr, in.vClip2.x / in.vClip2.y).x; + out.FragColor = uSampler2D.sample(uSampler2DSmplr, in.vClip3.xy / in.vClip3.z).x; + out.FragColor = uSampler3D.sample(uSampler3DSmplr, in.vClip4.xyz / in.vClip4.w).x; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/ubo_layout.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/ubo_layout.frag new file mode 100644 index 0000000..4ca603d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/ubo_layout.frag @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +struct Str +{ + float4x4 foo; +}; + +struct UBO1 +{ + Str foo; +}; + +struct Str_1 +{ + float4x4 foo; +}; + +struct UBO2 +{ + Str_1 foo; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant UBO1& ubo1 [[buffer(0)]], constant UBO2& ubo0 [[buffer(1)]]) +{ + main0_out out = {}; + out.FragColor = float4(ubo1.foo.foo[0][0], ubo1.foo.foo[1][0], ubo1.foo.foo[2][0], ubo1.foo.foo[3][0]) + ubo0.foo.foo[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/unary-enclose.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/unary-enclose.frag new file mode 100644 index 0000000..c8648f1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/unary-enclose.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vIn [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = in.vIn; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag new file mode 100644 index 0000000..6116dea --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag @@ -0,0 +1,75 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + ushort2 a [[user(locn0)]]; + uint3 b [[user(locn1)]]; + ushort c_0 [[user(locn2)]]; + ushort c_1 [[user(locn3)]]; + uint4 e_0 [[user(locn4)]]; + uint4 e_1 [[user(locn5)]]; + float4 d [[user(locn6)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray c = {}; + spvUnsafeArray e = {}; + c[0] = in.c_0; + c[1] = in.c_1; + e[0] = in.e_0; + e[1] = in.e_1; + out.FragColor = float4(float(int(in.a.x)), float(in.b.x), float2(float(uint(c[1])), float(e[0].w)) + in.d.xy); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/frag/write-depth-in-function.frag b/third_party/spirv-cross/reference/opt/shaders-msl/frag/write-depth-in-function.frag new file mode 100644 index 0000000..4ab74f1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/frag/write-depth-in-function.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; + float gl_FragDepth [[depth(any)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = 1.0; + out.gl_FragDepth = 0.20000000298023223876953125; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/intel/shader-integer-functions2.asm.comp b/third_party/spirv-cross/reference/opt/shaders-msl/intel/shader-integer-functions2.asm.comp new file mode 100644 index 0000000..1e5d889 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/intel/shader-integer-functions2.asm.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct foo +{ + uint a; + uint b; + int c; + int d; +}; + +kernel void main0(device foo& _4 [[buffer(0)]]) +{ + _4.a = clz(_4.a); + _4.a = ctz(_4.a); + _4.a = absdiff(_4.c, _4.d); + _4.a = absdiff(_4.a, _4.b); + _4.c = addsat(_4.c, _4.d); + _4.a = addsat(_4.a, _4.b); + _4.c = hadd(_4.c, _4.d); + _4.a = hadd(_4.a, _4.b); + _4.c = rhadd(_4.c, _4.d); + _4.a = rhadd(_4.a, _4.b); + _4.c = subsat(_4.c, _4.d); + _4.a = subsat(_4.a, _4.b); + _4.c = int(short(_4.c)) * int(short(_4.d)); + _4.a = uint(ushort(_4.a)) * uint(ushort(_4.b)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/legacy/vert/transpose.legacy.vert b/third_party/spirv-cross/reference/opt/shaders-msl/legacy/vert/transpose.legacy.vert new file mode 100644 index 0000000..abd884c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/legacy/vert/transpose.legacy.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct Buffer +{ + float4x4 MVPRowMajor; + float4x4 MVPColMajor; + float4x4 M; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant Buffer& _13 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = (((_13.M * (in.Position * _13.MVPRowMajor)) + (_13.M * (_13.MVPColMajor * in.Position))) + (_13.M * (_13.MVPRowMajor * in.Position))) + (_13.M * (in.Position * _13.MVPColMajor)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/basic.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/basic.multi-patch.tesc new file mode 100644 index 0000000..b1403a3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/basic.multi-patch.tesc @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_patchOut +{ + float3 vFoo; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]]) +{ + device main0_patchOut& patchOut = spvPatchOut[gl_GlobalInvocationID.x / 1]; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1]); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.900000095367431640625); + patchOut.vFoo = float3(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/basic.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/basic.tesc new file mode 100644 index 0000000..a9ff5b3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/basic.tesc @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_patchOut +{ + float3 vFoo; +}; + +kernel void main0(uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]]) +{ + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.900000095367431640625); + patchOut.vFoo = float3(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc new file mode 100644 index 0000000..4f4cf0b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc @@ -0,0 +1,68 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4x4 vOutputs; +}; + +struct main0_in +{ + float4x4 vInputs; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + spvUnsafeArray _16 = spvUnsafeArray({ gl_in[0].vInputs, gl_in[1].vInputs, gl_in[2].vInputs, gl_in[3].vInputs, gl_in[4].vInputs, gl_in[5].vInputs, gl_in[6].vInputs, gl_in[7].vInputs, gl_in[8].vInputs, gl_in[9].vInputs, gl_in[10].vInputs, gl_in[11].vInputs, gl_in[12].vInputs, gl_in[13].vInputs, gl_in[14].vInputs, gl_in[15].vInputs, gl_in[16].vInputs, gl_in[17].vInputs, gl_in[18].vInputs, gl_in[19].vInputs, gl_in[20].vInputs, gl_in[21].vInputs, gl_in[22].vInputs, gl_in[23].vInputs, gl_in[24].vInputs, gl_in[25].vInputs, gl_in[26].vInputs, gl_in[27].vInputs, gl_in[28].vInputs, gl_in[29].vInputs, gl_in[30].vInputs, gl_in[31].vInputs }); + spvUnsafeArray tmp; + tmp = _16; + gl_out[gl_InvocationID].vOutputs = tmp[gl_InvocationID]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-matrix.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-matrix.tesc new file mode 100644 index 0000000..46d4b4a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-matrix.tesc @@ -0,0 +1,73 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4x4 vOutputs; +}; + +struct main0_in +{ + float4 vInputs_0 [[attribute(0)]]; + float4 vInputs_1 [[attribute(1)]]; + float4 vInputs_2 [[attribute(2)]]; + float4 vInputs_3 [[attribute(3)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + spvUnsafeArray _16 = spvUnsafeArray({ float4x4(gl_in[0].vInputs_0, gl_in[0].vInputs_1, gl_in[0].vInputs_2, gl_in[0].vInputs_3), float4x4(gl_in[1].vInputs_0, gl_in[1].vInputs_1, gl_in[1].vInputs_2, gl_in[1].vInputs_3), float4x4(gl_in[2].vInputs_0, gl_in[2].vInputs_1, gl_in[2].vInputs_2, gl_in[2].vInputs_3), float4x4(gl_in[3].vInputs_0, gl_in[3].vInputs_1, gl_in[3].vInputs_2, gl_in[3].vInputs_3), float4x4(gl_in[4].vInputs_0, gl_in[4].vInputs_1, gl_in[4].vInputs_2, gl_in[4].vInputs_3), float4x4(gl_in[5].vInputs_0, gl_in[5].vInputs_1, gl_in[5].vInputs_2, gl_in[5].vInputs_3), float4x4(gl_in[6].vInputs_0, gl_in[6].vInputs_1, gl_in[6].vInputs_2, gl_in[6].vInputs_3), float4x4(gl_in[7].vInputs_0, gl_in[7].vInputs_1, gl_in[7].vInputs_2, gl_in[7].vInputs_3), float4x4(gl_in[8].vInputs_0, gl_in[8].vInputs_1, gl_in[8].vInputs_2, gl_in[8].vInputs_3), float4x4(gl_in[9].vInputs_0, gl_in[9].vInputs_1, gl_in[9].vInputs_2, gl_in[9].vInputs_3), float4x4(gl_in[10].vInputs_0, gl_in[10].vInputs_1, gl_in[10].vInputs_2, gl_in[10].vInputs_3), float4x4(gl_in[11].vInputs_0, gl_in[11].vInputs_1, gl_in[11].vInputs_2, gl_in[11].vInputs_3), float4x4(gl_in[12].vInputs_0, gl_in[12].vInputs_1, gl_in[12].vInputs_2, gl_in[12].vInputs_3), float4x4(gl_in[13].vInputs_0, gl_in[13].vInputs_1, gl_in[13].vInputs_2, gl_in[13].vInputs_3), float4x4(gl_in[14].vInputs_0, gl_in[14].vInputs_1, gl_in[14].vInputs_2, gl_in[14].vInputs_3), float4x4(gl_in[15].vInputs_0, gl_in[15].vInputs_1, gl_in[15].vInputs_2, gl_in[15].vInputs_3), float4x4(gl_in[16].vInputs_0, gl_in[16].vInputs_1, gl_in[16].vInputs_2, gl_in[16].vInputs_3), float4x4(gl_in[17].vInputs_0, gl_in[17].vInputs_1, gl_in[17].vInputs_2, gl_in[17].vInputs_3), float4x4(gl_in[18].vInputs_0, gl_in[18].vInputs_1, gl_in[18].vInputs_2, gl_in[18].vInputs_3), float4x4(gl_in[19].vInputs_0, gl_in[19].vInputs_1, gl_in[19].vInputs_2, gl_in[19].vInputs_3), float4x4(gl_in[20].vInputs_0, gl_in[20].vInputs_1, gl_in[20].vInputs_2, gl_in[20].vInputs_3), float4x4(gl_in[21].vInputs_0, gl_in[21].vInputs_1, gl_in[21].vInputs_2, gl_in[21].vInputs_3), float4x4(gl_in[22].vInputs_0, gl_in[22].vInputs_1, gl_in[22].vInputs_2, gl_in[22].vInputs_3), float4x4(gl_in[23].vInputs_0, gl_in[23].vInputs_1, gl_in[23].vInputs_2, gl_in[23].vInputs_3), float4x4(gl_in[24].vInputs_0, gl_in[24].vInputs_1, gl_in[24].vInputs_2, gl_in[24].vInputs_3), float4x4(gl_in[25].vInputs_0, gl_in[25].vInputs_1, gl_in[25].vInputs_2, gl_in[25].vInputs_3), float4x4(gl_in[26].vInputs_0, gl_in[26].vInputs_1, gl_in[26].vInputs_2, gl_in[26].vInputs_3), float4x4(gl_in[27].vInputs_0, gl_in[27].vInputs_1, gl_in[27].vInputs_2, gl_in[27].vInputs_3), float4x4(gl_in[28].vInputs_0, gl_in[28].vInputs_1, gl_in[28].vInputs_2, gl_in[28].vInputs_3), float4x4(gl_in[29].vInputs_0, gl_in[29].vInputs_1, gl_in[29].vInputs_2, gl_in[29].vInputs_3), float4x4(gl_in[30].vInputs_0, gl_in[30].vInputs_1, gl_in[30].vInputs_2, gl_in[30].vInputs_3), float4x4(gl_in[31].vInputs_0, gl_in[31].vInputs_1, gl_in[31].vInputs_2, gl_in[31].vInputs_3) }); + spvUnsafeArray tmp; + tmp = _16; + gl_out[gl_InvocationID].vOutputs = tmp[gl_InvocationID]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc new file mode 100644 index 0000000..8bd5515 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc @@ -0,0 +1,78 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct VertexData +{ + float4x4 a; + spvUnsafeArray b; + float4 c; +}; + +struct main0_out +{ + float4 vOutputs; +}; + +struct main0_in +{ + float4x4 VertexData_a; + spvUnsafeArray VertexData_b; + float4 VertexData_c; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + spvUnsafeArray _19 = spvUnsafeArray({ VertexData{ gl_in[0].VertexData_a, spvUnsafeArray({ gl_in[0].VertexData_b[0], gl_in[0].VertexData_b[1] }), gl_in[0].VertexData_c }, VertexData{ gl_in[1].VertexData_a, spvUnsafeArray({ gl_in[1].VertexData_b[0], gl_in[1].VertexData_b[1] }), gl_in[1].VertexData_c }, VertexData{ gl_in[2].VertexData_a, spvUnsafeArray({ gl_in[2].VertexData_b[0], gl_in[2].VertexData_b[1] }), gl_in[2].VertexData_c }, VertexData{ gl_in[3].VertexData_a, spvUnsafeArray({ gl_in[3].VertexData_b[0], gl_in[3].VertexData_b[1] }), gl_in[3].VertexData_c }, VertexData{ gl_in[4].VertexData_a, spvUnsafeArray({ gl_in[4].VertexData_b[0], gl_in[4].VertexData_b[1] }), gl_in[4].VertexData_c }, VertexData{ gl_in[5].VertexData_a, spvUnsafeArray({ gl_in[5].VertexData_b[0], gl_in[5].VertexData_b[1] }), gl_in[5].VertexData_c }, VertexData{ gl_in[6].VertexData_a, spvUnsafeArray({ gl_in[6].VertexData_b[0], gl_in[6].VertexData_b[1] }), gl_in[6].VertexData_c }, VertexData{ gl_in[7].VertexData_a, spvUnsafeArray({ gl_in[7].VertexData_b[0], gl_in[7].VertexData_b[1] }), gl_in[7].VertexData_c }, VertexData{ gl_in[8].VertexData_a, spvUnsafeArray({ gl_in[8].VertexData_b[0], gl_in[8].VertexData_b[1] }), gl_in[8].VertexData_c }, VertexData{ gl_in[9].VertexData_a, spvUnsafeArray({ gl_in[9].VertexData_b[0], gl_in[9].VertexData_b[1] }), gl_in[9].VertexData_c }, VertexData{ gl_in[10].VertexData_a, spvUnsafeArray({ gl_in[10].VertexData_b[0], gl_in[10].VertexData_b[1] }), gl_in[10].VertexData_c }, VertexData{ gl_in[11].VertexData_a, spvUnsafeArray({ gl_in[11].VertexData_b[0], gl_in[11].VertexData_b[1] }), gl_in[11].VertexData_c }, VertexData{ gl_in[12].VertexData_a, spvUnsafeArray({ gl_in[12].VertexData_b[0], gl_in[12].VertexData_b[1] }), gl_in[12].VertexData_c }, VertexData{ gl_in[13].VertexData_a, spvUnsafeArray({ gl_in[13].VertexData_b[0], gl_in[13].VertexData_b[1] }), gl_in[13].VertexData_c }, VertexData{ gl_in[14].VertexData_a, spvUnsafeArray({ gl_in[14].VertexData_b[0], gl_in[14].VertexData_b[1] }), gl_in[14].VertexData_c }, VertexData{ gl_in[15].VertexData_a, spvUnsafeArray({ gl_in[15].VertexData_b[0], gl_in[15].VertexData_b[1] }), gl_in[15].VertexData_c }, VertexData{ gl_in[16].VertexData_a, spvUnsafeArray({ gl_in[16].VertexData_b[0], gl_in[16].VertexData_b[1] }), gl_in[16].VertexData_c }, VertexData{ gl_in[17].VertexData_a, spvUnsafeArray({ gl_in[17].VertexData_b[0], gl_in[17].VertexData_b[1] }), gl_in[17].VertexData_c }, VertexData{ gl_in[18].VertexData_a, spvUnsafeArray({ gl_in[18].VertexData_b[0], gl_in[18].VertexData_b[1] }), gl_in[18].VertexData_c }, VertexData{ gl_in[19].VertexData_a, spvUnsafeArray({ gl_in[19].VertexData_b[0], gl_in[19].VertexData_b[1] }), gl_in[19].VertexData_c }, VertexData{ gl_in[20].VertexData_a, spvUnsafeArray({ gl_in[20].VertexData_b[0], gl_in[20].VertexData_b[1] }), gl_in[20].VertexData_c }, VertexData{ gl_in[21].VertexData_a, spvUnsafeArray({ gl_in[21].VertexData_b[0], gl_in[21].VertexData_b[1] }), gl_in[21].VertexData_c }, VertexData{ gl_in[22].VertexData_a, spvUnsafeArray({ gl_in[22].VertexData_b[0], gl_in[22].VertexData_b[1] }), gl_in[22].VertexData_c }, VertexData{ gl_in[23].VertexData_a, spvUnsafeArray({ gl_in[23].VertexData_b[0], gl_in[23].VertexData_b[1] }), gl_in[23].VertexData_c }, VertexData{ gl_in[24].VertexData_a, spvUnsafeArray({ gl_in[24].VertexData_b[0], gl_in[24].VertexData_b[1] }), gl_in[24].VertexData_c }, VertexData{ gl_in[25].VertexData_a, spvUnsafeArray({ gl_in[25].VertexData_b[0], gl_in[25].VertexData_b[1] }), gl_in[25].VertexData_c }, VertexData{ gl_in[26].VertexData_a, spvUnsafeArray({ gl_in[26].VertexData_b[0], gl_in[26].VertexData_b[1] }), gl_in[26].VertexData_c }, VertexData{ gl_in[27].VertexData_a, spvUnsafeArray({ gl_in[27].VertexData_b[0], gl_in[27].VertexData_b[1] }), gl_in[27].VertexData_c }, VertexData{ gl_in[28].VertexData_a, spvUnsafeArray({ gl_in[28].VertexData_b[0], gl_in[28].VertexData_b[1] }), gl_in[28].VertexData_c }, VertexData{ gl_in[29].VertexData_a, spvUnsafeArray({ gl_in[29].VertexData_b[0], gl_in[29].VertexData_b[1] }), gl_in[29].VertexData_c }, VertexData{ gl_in[30].VertexData_a, spvUnsafeArray({ gl_in[30].VertexData_b[0], gl_in[30].VertexData_b[1] }), gl_in[30].VertexData_c }, VertexData{ gl_in[31].VertexData_a, spvUnsafeArray({ gl_in[31].VertexData_b[0], gl_in[31].VertexData_b[1] }), gl_in[31].VertexData_c } }); + spvUnsafeArray tmp; + tmp = _19; + int _27 = gl_InvocationID ^ 1; + gl_out[gl_InvocationID].vOutputs = ((tmp[gl_InvocationID].a[1] + tmp[gl_InvocationID].b[1]) + tmp[gl_InvocationID].c) + gl_in[_27].VertexData_c; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-struct.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-struct.tesc new file mode 100644 index 0000000..fc2cec1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array-of-struct.tesc @@ -0,0 +1,84 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct VertexData +{ + float4x4 a; + spvUnsafeArray b; + float4 c; +}; + +struct main0_out +{ + float4 vOutputs; +}; + +struct main0_in +{ + float4 VertexData_a_0 [[attribute(0)]]; + float4 VertexData_a_1 [[attribute(1)]]; + float4 VertexData_a_2 [[attribute(2)]]; + float4 VertexData_a_3 [[attribute(3)]]; + float4 VertexData_b_0 [[attribute(4)]]; + float4 VertexData_b_1 [[attribute(5)]]; + float4 VertexData_c [[attribute(6)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + spvUnsafeArray _19 = spvUnsafeArray({ VertexData{ float4x4(gl_in[0].VertexData_a_0, gl_in[0].VertexData_a_1, gl_in[0].VertexData_a_2, gl_in[0].VertexData_a_3), spvUnsafeArray({ gl_in[0].VertexData_b_0, gl_in[0].VertexData_b_1 }), gl_in[0].VertexData_c }, VertexData{ float4x4(gl_in[1].VertexData_a_0, gl_in[1].VertexData_a_1, gl_in[1].VertexData_a_2, gl_in[1].VertexData_a_3), spvUnsafeArray({ gl_in[1].VertexData_b_0, gl_in[1].VertexData_b_1 }), gl_in[1].VertexData_c }, VertexData{ float4x4(gl_in[2].VertexData_a_0, gl_in[2].VertexData_a_1, gl_in[2].VertexData_a_2, gl_in[2].VertexData_a_3), spvUnsafeArray({ gl_in[2].VertexData_b_0, gl_in[2].VertexData_b_1 }), gl_in[2].VertexData_c }, VertexData{ float4x4(gl_in[3].VertexData_a_0, gl_in[3].VertexData_a_1, gl_in[3].VertexData_a_2, gl_in[3].VertexData_a_3), spvUnsafeArray({ gl_in[3].VertexData_b_0, gl_in[3].VertexData_b_1 }), gl_in[3].VertexData_c }, VertexData{ float4x4(gl_in[4].VertexData_a_0, gl_in[4].VertexData_a_1, gl_in[4].VertexData_a_2, gl_in[4].VertexData_a_3), spvUnsafeArray({ gl_in[4].VertexData_b_0, gl_in[4].VertexData_b_1 }), gl_in[4].VertexData_c }, VertexData{ float4x4(gl_in[5].VertexData_a_0, gl_in[5].VertexData_a_1, gl_in[5].VertexData_a_2, gl_in[5].VertexData_a_3), spvUnsafeArray({ gl_in[5].VertexData_b_0, gl_in[5].VertexData_b_1 }), gl_in[5].VertexData_c }, VertexData{ float4x4(gl_in[6].VertexData_a_0, gl_in[6].VertexData_a_1, gl_in[6].VertexData_a_2, gl_in[6].VertexData_a_3), spvUnsafeArray({ gl_in[6].VertexData_b_0, gl_in[6].VertexData_b_1 }), gl_in[6].VertexData_c }, VertexData{ float4x4(gl_in[7].VertexData_a_0, gl_in[7].VertexData_a_1, gl_in[7].VertexData_a_2, gl_in[7].VertexData_a_3), spvUnsafeArray({ gl_in[7].VertexData_b_0, gl_in[7].VertexData_b_1 }), gl_in[7].VertexData_c }, VertexData{ float4x4(gl_in[8].VertexData_a_0, gl_in[8].VertexData_a_1, gl_in[8].VertexData_a_2, gl_in[8].VertexData_a_3), spvUnsafeArray({ gl_in[8].VertexData_b_0, gl_in[8].VertexData_b_1 }), gl_in[8].VertexData_c }, VertexData{ float4x4(gl_in[9].VertexData_a_0, gl_in[9].VertexData_a_1, gl_in[9].VertexData_a_2, gl_in[9].VertexData_a_3), spvUnsafeArray({ gl_in[9].VertexData_b_0, gl_in[9].VertexData_b_1 }), gl_in[9].VertexData_c }, VertexData{ float4x4(gl_in[10].VertexData_a_0, gl_in[10].VertexData_a_1, gl_in[10].VertexData_a_2, gl_in[10].VertexData_a_3), spvUnsafeArray({ gl_in[10].VertexData_b_0, gl_in[10].VertexData_b_1 }), gl_in[10].VertexData_c }, VertexData{ float4x4(gl_in[11].VertexData_a_0, gl_in[11].VertexData_a_1, gl_in[11].VertexData_a_2, gl_in[11].VertexData_a_3), spvUnsafeArray({ gl_in[11].VertexData_b_0, gl_in[11].VertexData_b_1 }), gl_in[11].VertexData_c }, VertexData{ float4x4(gl_in[12].VertexData_a_0, gl_in[12].VertexData_a_1, gl_in[12].VertexData_a_2, gl_in[12].VertexData_a_3), spvUnsafeArray({ gl_in[12].VertexData_b_0, gl_in[12].VertexData_b_1 }), gl_in[12].VertexData_c }, VertexData{ float4x4(gl_in[13].VertexData_a_0, gl_in[13].VertexData_a_1, gl_in[13].VertexData_a_2, gl_in[13].VertexData_a_3), spvUnsafeArray({ gl_in[13].VertexData_b_0, gl_in[13].VertexData_b_1 }), gl_in[13].VertexData_c }, VertexData{ float4x4(gl_in[14].VertexData_a_0, gl_in[14].VertexData_a_1, gl_in[14].VertexData_a_2, gl_in[14].VertexData_a_3), spvUnsafeArray({ gl_in[14].VertexData_b_0, gl_in[14].VertexData_b_1 }), gl_in[14].VertexData_c }, VertexData{ float4x4(gl_in[15].VertexData_a_0, gl_in[15].VertexData_a_1, gl_in[15].VertexData_a_2, gl_in[15].VertexData_a_3), spvUnsafeArray({ gl_in[15].VertexData_b_0, gl_in[15].VertexData_b_1 }), gl_in[15].VertexData_c }, VertexData{ float4x4(gl_in[16].VertexData_a_0, gl_in[16].VertexData_a_1, gl_in[16].VertexData_a_2, gl_in[16].VertexData_a_3), spvUnsafeArray({ gl_in[16].VertexData_b_0, gl_in[16].VertexData_b_1 }), gl_in[16].VertexData_c }, VertexData{ float4x4(gl_in[17].VertexData_a_0, gl_in[17].VertexData_a_1, gl_in[17].VertexData_a_2, gl_in[17].VertexData_a_3), spvUnsafeArray({ gl_in[17].VertexData_b_0, gl_in[17].VertexData_b_1 }), gl_in[17].VertexData_c }, VertexData{ float4x4(gl_in[18].VertexData_a_0, gl_in[18].VertexData_a_1, gl_in[18].VertexData_a_2, gl_in[18].VertexData_a_3), spvUnsafeArray({ gl_in[18].VertexData_b_0, gl_in[18].VertexData_b_1 }), gl_in[18].VertexData_c }, VertexData{ float4x4(gl_in[19].VertexData_a_0, gl_in[19].VertexData_a_1, gl_in[19].VertexData_a_2, gl_in[19].VertexData_a_3), spvUnsafeArray({ gl_in[19].VertexData_b_0, gl_in[19].VertexData_b_1 }), gl_in[19].VertexData_c }, VertexData{ float4x4(gl_in[20].VertexData_a_0, gl_in[20].VertexData_a_1, gl_in[20].VertexData_a_2, gl_in[20].VertexData_a_3), spvUnsafeArray({ gl_in[20].VertexData_b_0, gl_in[20].VertexData_b_1 }), gl_in[20].VertexData_c }, VertexData{ float4x4(gl_in[21].VertexData_a_0, gl_in[21].VertexData_a_1, gl_in[21].VertexData_a_2, gl_in[21].VertexData_a_3), spvUnsafeArray({ gl_in[21].VertexData_b_0, gl_in[21].VertexData_b_1 }), gl_in[21].VertexData_c }, VertexData{ float4x4(gl_in[22].VertexData_a_0, gl_in[22].VertexData_a_1, gl_in[22].VertexData_a_2, gl_in[22].VertexData_a_3), spvUnsafeArray({ gl_in[22].VertexData_b_0, gl_in[22].VertexData_b_1 }), gl_in[22].VertexData_c }, VertexData{ float4x4(gl_in[23].VertexData_a_0, gl_in[23].VertexData_a_1, gl_in[23].VertexData_a_2, gl_in[23].VertexData_a_3), spvUnsafeArray({ gl_in[23].VertexData_b_0, gl_in[23].VertexData_b_1 }), gl_in[23].VertexData_c }, VertexData{ float4x4(gl_in[24].VertexData_a_0, gl_in[24].VertexData_a_1, gl_in[24].VertexData_a_2, gl_in[24].VertexData_a_3), spvUnsafeArray({ gl_in[24].VertexData_b_0, gl_in[24].VertexData_b_1 }), gl_in[24].VertexData_c }, VertexData{ float4x4(gl_in[25].VertexData_a_0, gl_in[25].VertexData_a_1, gl_in[25].VertexData_a_2, gl_in[25].VertexData_a_3), spvUnsafeArray({ gl_in[25].VertexData_b_0, gl_in[25].VertexData_b_1 }), gl_in[25].VertexData_c }, VertexData{ float4x4(gl_in[26].VertexData_a_0, gl_in[26].VertexData_a_1, gl_in[26].VertexData_a_2, gl_in[26].VertexData_a_3), spvUnsafeArray({ gl_in[26].VertexData_b_0, gl_in[26].VertexData_b_1 }), gl_in[26].VertexData_c }, VertexData{ float4x4(gl_in[27].VertexData_a_0, gl_in[27].VertexData_a_1, gl_in[27].VertexData_a_2, gl_in[27].VertexData_a_3), spvUnsafeArray({ gl_in[27].VertexData_b_0, gl_in[27].VertexData_b_1 }), gl_in[27].VertexData_c }, VertexData{ float4x4(gl_in[28].VertexData_a_0, gl_in[28].VertexData_a_1, gl_in[28].VertexData_a_2, gl_in[28].VertexData_a_3), spvUnsafeArray({ gl_in[28].VertexData_b_0, gl_in[28].VertexData_b_1 }), gl_in[28].VertexData_c }, VertexData{ float4x4(gl_in[29].VertexData_a_0, gl_in[29].VertexData_a_1, gl_in[29].VertexData_a_2, gl_in[29].VertexData_a_3), spvUnsafeArray({ gl_in[29].VertexData_b_0, gl_in[29].VertexData_b_1 }), gl_in[29].VertexData_c }, VertexData{ float4x4(gl_in[30].VertexData_a_0, gl_in[30].VertexData_a_1, gl_in[30].VertexData_a_2, gl_in[30].VertexData_a_3), spvUnsafeArray({ gl_in[30].VertexData_b_0, gl_in[30].VertexData_b_1 }), gl_in[30].VertexData_c }, VertexData{ float4x4(gl_in[31].VertexData_a_0, gl_in[31].VertexData_a_1, gl_in[31].VertexData_a_2, gl_in[31].VertexData_a_3), spvUnsafeArray({ gl_in[31].VertexData_b_0, gl_in[31].VertexData_b_1 }), gl_in[31].VertexData_c } }); + spvUnsafeArray tmp; + tmp = _19; + int _27 = gl_InvocationID ^ 1; + gl_out[gl_InvocationID].vOutputs = ((tmp[gl_InvocationID].a[1] + tmp[gl_InvocationID].b[1]) + tmp[gl_InvocationID].c) + gl_in[_27].VertexData_c; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array.multi-patch.tesc new file mode 100644 index 0000000..416bc68 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array.multi-patch.tesc @@ -0,0 +1,69 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 vOutputs; +}; + +struct main0_in +{ + float4 vInputs; + ushort2 m_43; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + spvUnsafeArray _15 = spvUnsafeArray({ gl_in[0].vInputs, gl_in[1].vInputs, gl_in[2].vInputs, gl_in[3].vInputs, gl_in[4].vInputs, gl_in[5].vInputs, gl_in[6].vInputs, gl_in[7].vInputs, gl_in[8].vInputs, gl_in[9].vInputs, gl_in[10].vInputs, gl_in[11].vInputs, gl_in[12].vInputs, gl_in[13].vInputs, gl_in[14].vInputs, gl_in[15].vInputs, gl_in[16].vInputs, gl_in[17].vInputs, gl_in[18].vInputs, gl_in[19].vInputs, gl_in[20].vInputs, gl_in[21].vInputs, gl_in[22].vInputs, gl_in[23].vInputs, gl_in[24].vInputs, gl_in[25].vInputs, gl_in[26].vInputs, gl_in[27].vInputs, gl_in[28].vInputs, gl_in[29].vInputs, gl_in[30].vInputs, gl_in[31].vInputs }); + spvUnsafeArray tmp; + tmp = _15; + gl_out[gl_InvocationID].vOutputs = tmp[gl_InvocationID]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array.tesc new file mode 100644 index 0000000..d04571a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/load-control-point-array.tesc @@ -0,0 +1,70 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 vOutputs; +}; + +struct main0_in +{ + float4 vInputs [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + spvUnsafeArray _15 = spvUnsafeArray({ gl_in[0].vInputs, gl_in[1].vInputs, gl_in[2].vInputs, gl_in[3].vInputs, gl_in[4].vInputs, gl_in[5].vInputs, gl_in[6].vInputs, gl_in[7].vInputs, gl_in[8].vInputs, gl_in[9].vInputs, gl_in[10].vInputs, gl_in[11].vInputs, gl_in[12].vInputs, gl_in[13].vInputs, gl_in[14].vInputs, gl_in[15].vInputs, gl_in[16].vInputs, gl_in[17].vInputs, gl_in[18].vInputs, gl_in[19].vInputs, gl_in[20].vInputs, gl_in[21].vInputs, gl_in[22].vInputs, gl_in[23].vInputs, gl_in[24].vInputs, gl_in[25].vInputs, gl_in[26].vInputs, gl_in[27].vInputs, gl_in[28].vInputs, gl_in[29].vInputs, gl_in[30].vInputs, gl_in[31].vInputs }); + spvUnsafeArray tmp; + tmp = _15; + gl_out[gl_InvocationID].vOutputs = tmp[gl_InvocationID]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/matrix-output.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/matrix-output.multi-patch.tesc new file mode 100644 index 0000000..b3b06d1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/matrix-output.multi-patch.tesc @@ -0,0 +1,41 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float in_te_attr; + float4x3 in_te_data0; + float4x3 in_te_data1; +}; + +struct main0_in +{ + float3 in_tc_attr; + ushort2 m_104; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 3]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 3; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1]); + float _15 = float(gl_InvocationID); + float3 _18 = float3(_15, 0.0, 0.0); + float3 _19 = float3(0.0, _15, 0.0); + float3 _20 = float3(0.0, 0.0, _15); + gl_out[gl_InvocationID].in_te_data0 = float4x3(_18, _19, _20, float3(0.0)); + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + int _42 = (gl_InvocationID + 1) % 3; + gl_out[gl_InvocationID].in_te_data1 = float4x3(_18 + gl_out[_42].in_te_data0[0], _19 + gl_out[_42].in_te_data0[1], _20 + gl_out[_42].in_te_data0[2], gl_out[_42].in_te_data0[3]); + gl_out[gl_InvocationID].in_te_attr = gl_in[gl_InvocationID].in_tc_attr.x; + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/reload-tess-level.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/reload-tess-level.multi-patch.tesc new file mode 100644 index 0000000..a55755e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/reload-tess-level.multi-patch.tesc @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +struct main0_in +{ + uint3 m_82; + ushort2 m_86; + float4 gl_Position; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + if (gl_InvocationID == 0) + { + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(4.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(5.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(mix(float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0]), float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3]), 0.5)); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(mix(float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2]), float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1]), 0.5)); + } + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/reload-tess-level.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/reload-tess-level.tesc new file mode 100644 index 0000000..eafc506 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/reload-tess-level.tesc @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +struct main0_in +{ + float4 gl_Position [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + if (gl_InvocationID == 0) + { + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(4.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(5.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(mix(float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0]), float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3]), 0.5)); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(mix(float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2]), float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1]), 0.5)); + } + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/struct-output.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/struct-output.multi-patch.tesc new file mode 100644 index 0000000..eb59973 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/struct-output.multi-patch.tesc @@ -0,0 +1,48 @@ +#include +#include + +using namespace metal; + +struct te_data +{ + float a; + float b; + uint c; +}; + +struct main0_out +{ + float in_te_attr; + te_data in_te_data0; + te_data in_te_data1; +}; + +struct main0_in +{ + float3 in_tc_attr; + ushort2 m_119; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 3]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 3; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1]); + float _15 = float(gl_InvocationID); + int _18 = gl_InvocationID + 1; + float _19 = float(_18); + uint _21 = uint(gl_InvocationID); + gl_out[gl_InvocationID].in_te_data0 = te_data{ _15, _19, _21 }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + int _38 = _18 % 3; + gl_out[gl_InvocationID].in_te_data1 = te_data{ _15 + gl_out[_38].in_te_data0.a, _19 + gl_out[_38].in_te_data0.b, _21 + gl_out[_38].in_te_data0.c }; + gl_out[gl_InvocationID].in_te_attr = gl_in[gl_InvocationID].in_tc_attr.x; + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/water_tess.multi-patch.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/water_tess.multi-patch.tesc new file mode 100644 index 0000000..6264a8e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/water_tess.multi-patch.tesc @@ -0,0 +1,91 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 uScale; + float3 uCamPos; + float2 uPatchSize; + float2 uMaxTessLevel; + float uDistanceMod; + float4 uFrustum[6]; +}; + +struct main0_patchOut +{ + float2 vOutPatchPosBase; + float4 vPatchLods; +}; + +struct main0_in +{ + float3 vPatchPosBase; + ushort2 m_996; +}; + +kernel void main0(constant UBO& _41 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_patchOut& patchOut = spvPatchOut[gl_GlobalInvocationID.x / 1]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1]); + float2 _431 = (gl_in[0].vPatchPosBase.xy - float2(10.0)) * _41.uScale.xy; + float2 _441 = ((gl_in[0].vPatchPosBase.xy + _41.uPatchSize) + float2(10.0)) * _41.uScale.xy; + float3 _446 = float3(_431.x, -10.0, _431.y); + float3 _451 = float3(_441.x, 10.0, _441.y); + float4 _467 = float4((_446 + _451) * 0.5, 1.0); + float3 _514 = float3(length(_451 - _446) * (-0.5)); + bool _516 = any(float3(dot(_41.uFrustum[0], _467), dot(_41.uFrustum[1], _467), dot(_41.uFrustum[2], _467)) <= _514); + bool _526; + if (!_516) + { + _526 = any(float3(dot(_41.uFrustum[3], _467), dot(_41.uFrustum[4], _467), dot(_41.uFrustum[5], _467)) <= _514); + } + else + { + _526 = _516; + } + if (!(!_526)) + { + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(-1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(-1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(-1.0); + } + else + { + patchOut.vOutPatchPosBase = gl_in[0].vPatchPosBase.xy; + float2 _681 = (gl_in[0].vPatchPosBase.xy + (float2(-0.5) * _41.uPatchSize)) * _41.uScale.xy; + float2 _710 = (gl_in[0].vPatchPosBase.xy + (float2(0.5, -0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _729 = fast::clamp(log2((length(_41.uCamPos - float3(_710.x, 0.0, _710.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _739 = (gl_in[0].vPatchPosBase.xy + (float2(1.5, -0.5) * _41.uPatchSize)) * _41.uScale.xy; + float2 _768 = (gl_in[0].vPatchPosBase.xy + (float2(-0.5, 0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _787 = fast::clamp(log2((length(_41.uCamPos - float3(_768.x, 0.0, _768.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _797 = (gl_in[0].vPatchPosBase.xy + (float2(0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _816 = fast::clamp(log2((length(_41.uCamPos - float3(_797.x, 0.0, _797.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _826 = (gl_in[0].vPatchPosBase.xy + (float2(1.5, 0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _845 = fast::clamp(log2((length(_41.uCamPos - float3(_826.x, 0.0, _826.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _855 = (gl_in[0].vPatchPosBase.xy + (float2(-0.5, 1.5) * _41.uPatchSize)) * _41.uScale.xy; + float2 _884 = (gl_in[0].vPatchPosBase.xy + (float2(0.5, 1.5) * _41.uPatchSize)) * _41.uScale.xy; + float _903 = fast::clamp(log2((length(_41.uCamPos - float3(_884.x, 0.0, _884.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _913 = (gl_in[0].vPatchPosBase.xy + (float2(1.5) * _41.uPatchSize)) * _41.uScale.xy; + float _614 = dot(float4(_787, _816, fast::clamp(log2((length(_41.uCamPos - float3(_855.x, 0.0, _855.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _903), float4(0.25)); + float _620 = dot(float4(fast::clamp(log2((length(_41.uCamPos - float3(_681.x, 0.0, _681.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _729, _787, _816), float4(0.25)); + float _626 = dot(float4(_729, fast::clamp(log2((length(_41.uCamPos - float3(_739.x, 0.0, _739.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _816, _845), float4(0.25)); + float _632 = dot(float4(_816, _845, _903, fast::clamp(log2((length(_41.uCamPos - float3(_913.x, 0.0, _913.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x)), float4(0.25)); + float4 _633 = float4(_614, _620, _626, _632); + patchOut.vPatchLods = _633; + float4 _940 = exp2(-fast::min(_633, _633.yzwx)) * _41.uMaxTessLevel.y; + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(_940.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(_940.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(_940.z); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(_940.w); + float _948 = _41.uMaxTessLevel.y * exp2(-fast::min(fast::min(fast::min(_614, _620), fast::min(_626, _632)), _816)); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(_948); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(_948); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tesc/water_tess.tesc b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/water_tess.tesc new file mode 100644 index 0000000..1848419 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tesc/water_tess.tesc @@ -0,0 +1,93 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 uScale; + float3 uCamPos; + float2 uPatchSize; + float2 uMaxTessLevel; + float uDistanceMod; + float4 uFrustum[6]; +}; + +struct main0_patchOut +{ + float2 vOutPatchPosBase; + float4 vPatchLods; +}; + +struct main0_in +{ + float2 vPatchPosBase [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant UBO& _41 [[buffer(0)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 1) + return; + float2 _431 = (gl_in[0].vPatchPosBase - float2(10.0)) * _41.uScale.xy; + float2 _441 = ((gl_in[0].vPatchPosBase + _41.uPatchSize) + float2(10.0)) * _41.uScale.xy; + float3 _446 = float3(_431.x, -10.0, _431.y); + float3 _451 = float3(_441.x, 10.0, _441.y); + float4 _467 = float4((_446 + _451) * 0.5, 1.0); + float3 _514 = float3(length(_451 - _446) * (-0.5)); + bool _516 = any(float3(dot(_41.uFrustum[0], _467), dot(_41.uFrustum[1], _467), dot(_41.uFrustum[2], _467)) <= _514); + bool _526; + if (!_516) + { + _526 = any(float3(dot(_41.uFrustum[3], _467), dot(_41.uFrustum[4], _467), dot(_41.uFrustum[5], _467)) <= _514); + } + else + { + _526 = _516; + } + if (!(!_526)) + { + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(-1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(-1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(-1.0); + } + else + { + patchOut.vOutPatchPosBase = gl_in[0].vPatchPosBase; + float2 _681 = (gl_in[0].vPatchPosBase + (float2(-0.5) * _41.uPatchSize)) * _41.uScale.xy; + float2 _710 = (gl_in[0].vPatchPosBase + (float2(0.5, -0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _729 = fast::clamp(log2((length(_41.uCamPos - float3(_710.x, 0.0, _710.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _739 = (gl_in[0].vPatchPosBase + (float2(1.5, -0.5) * _41.uPatchSize)) * _41.uScale.xy; + float2 _768 = (gl_in[0].vPatchPosBase + (float2(-0.5, 0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _787 = fast::clamp(log2((length(_41.uCamPos - float3(_768.x, 0.0, _768.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _797 = (gl_in[0].vPatchPosBase + (float2(0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _816 = fast::clamp(log2((length(_41.uCamPos - float3(_797.x, 0.0, _797.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _826 = (gl_in[0].vPatchPosBase + (float2(1.5, 0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _845 = fast::clamp(log2((length(_41.uCamPos - float3(_826.x, 0.0, _826.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _855 = (gl_in[0].vPatchPosBase + (float2(-0.5, 1.5) * _41.uPatchSize)) * _41.uScale.xy; + float2 _884 = (gl_in[0].vPatchPosBase + (float2(0.5, 1.5) * _41.uPatchSize)) * _41.uScale.xy; + float _903 = fast::clamp(log2((length(_41.uCamPos - float3(_884.x, 0.0, _884.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + float2 _913 = (gl_in[0].vPatchPosBase + (float2(1.5) * _41.uPatchSize)) * _41.uScale.xy; + float _614 = dot(float4(_787, _816, fast::clamp(log2((length(_41.uCamPos - float3(_855.x, 0.0, _855.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _903), float4(0.25)); + float _620 = dot(float4(fast::clamp(log2((length(_41.uCamPos - float3(_681.x, 0.0, _681.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _729, _787, _816), float4(0.25)); + float _626 = dot(float4(_729, fast::clamp(log2((length(_41.uCamPos - float3(_739.x, 0.0, _739.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _816, _845), float4(0.25)); + float _632 = dot(float4(_816, _845, _903, fast::clamp(log2((length(_41.uCamPos - float3(_913.x, 0.0, _913.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x)), float4(0.25)); + float4 _633 = float4(_614, _620, _626, _632); + patchOut.vPatchLods = _633; + float4 _940 = exp2(-fast::min(_633, _633.yzwx)) * _41.uMaxTessLevel.y; + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(_940.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(_940.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(_940.z); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(_940.w); + float _948 = _41.uMaxTessLevel.y * exp2(-fast::min(fast::min(fast::min(_614, _620), fast::min(_626, _632)), _816)); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(_948); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(_948); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/input-array.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/input-array.tese new file mode 100644 index 0000000..97a83b4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/input-array.tese @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Floats [[attribute(0)]]; + float4 Floats2 [[attribute(2)]]; +}; + +struct main0_patchIn +{ + patch_control_point gl_in; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + out.gl_Position = (patchIn.gl_in[0].Floats * gl_TessCoord.x) + (patchIn.gl_in[1].Floats2 * gl_TessCoord.y); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/input-types.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/input-types.tese new file mode 100644 index 0000000..0ce04ca --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/input-types.tese @@ -0,0 +1,76 @@ +#include +#include + +using namespace metal; + +struct Block +{ + float4 a; + float4 b; +}; + +struct PatchBlock +{ + float4 a; + float4 b; +}; + +struct Foo +{ + float4 a; + float4 b; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vColor [[attribute(0)]]; + float4 Block_a [[attribute(4)]]; + float4 Block_b [[attribute(5)]]; + float4 Foo_a [[attribute(14)]]; + float4 Foo_b [[attribute(15)]]; +}; + +struct main0_patchIn +{ + float4 vColors [[attribute(1)]]; + float4 PatchBlock_a [[attribute(6)]]; + float4 PatchBlock_b [[attribute(7)]]; + float4 Foo_a [[attribute(8)]]; + float4 Foo_b [[attribute(9)]]; + patch_control_point gl_in; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]]) +{ + main0_out out = {}; + PatchBlock patch_block = {}; + Foo vFoo = {}; + patch_block.a = patchIn.PatchBlock_a; + patch_block.b = patchIn.PatchBlock_b; + vFoo.a = patchIn.Foo_a; + vFoo.b = patchIn.Foo_b; + out.gl_Position = patchIn.gl_in[0].Block_a; + out.gl_Position += patchIn.gl_in[0].Block_b; + out.gl_Position += patchIn.gl_in[1].Block_a; + out.gl_Position += patchIn.gl_in[1].Block_b; + out.gl_Position += patch_block.a; + out.gl_Position += patch_block.b; + out.gl_Position += patchIn.gl_in[0].vColor; + out.gl_Position += patchIn.gl_in[1].vColor; + out.gl_Position += patchIn.vColors; + out.gl_Position += vFoo.a; + out.gl_Position += vFoo.b; + Foo _204 = Foo{ patchIn.gl_in[0].Foo_a, patchIn.gl_in[0].Foo_b }; + out.gl_Position += _204.a; + out.gl_Position += _204.b; + Foo _218 = Foo{ patchIn.gl_in[1].Foo_a, patchIn.gl_in[1].Foo_b }; + out.gl_Position += _218.a; + out.gl_Position += _218.b; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/load-control-point-array-of-matrix.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/load-control-point-array-of-matrix.tese new file mode 100644 index 0000000..e4bbeb7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/load-control-point-array-of-matrix.tese @@ -0,0 +1,85 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vInputs_0 [[attribute(0)]]; + float4 vInputs_1 [[attribute(1)]]; + float4 vInputs_2 [[attribute(2)]]; + float4 vInputs_3 [[attribute(3)]]; +}; + +struct main0_patchIn +{ + float4 vBoo_0 [[attribute(4)]]; + float4 vBoo_1 [[attribute(5)]]; + float4 vBoo_2 [[attribute(6)]]; + float4 vBoo_3 [[attribute(7)]]; + int vIndex [[attribute(8)]]; + patch_control_point gl_in; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray vBoo = {}; + vBoo[0] = patchIn.vBoo_0; + vBoo[1] = patchIn.vBoo_1; + vBoo[2] = patchIn.vBoo_2; + vBoo[3] = patchIn.vBoo_3; + float4x4 _57 = float4x4(patchIn.gl_in[0u].vInputs_0, patchIn.gl_in[0u].vInputs_1, patchIn.gl_in[0u].vInputs_2, patchIn.gl_in[0u].vInputs_3); + float4x4 _59 = float4x4(patchIn.gl_in[1u].vInputs_0, patchIn.gl_in[1u].vInputs_1, patchIn.gl_in[1u].vInputs_2, patchIn.gl_in[1u].vInputs_3); + float4x4 _47 = _57; + float4x4 _48 = _59; + out.gl_Position = (_47[patchIn.vIndex] + _48[patchIn.vIndex]) + vBoo[patchIn.vIndex]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/load-control-point-array.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/load-control-point-array.tese new file mode 100644 index 0000000..54d7419 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/load-control-point-array.tese @@ -0,0 +1,78 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vInputs [[attribute(0)]]; +}; + +struct main0_patchIn +{ + float4 vBoo_0 [[attribute(1)]]; + float4 vBoo_1 [[attribute(2)]]; + float4 vBoo_2 [[attribute(3)]]; + float4 vBoo_3 [[attribute(4)]]; + int vIndex [[attribute(5)]]; + patch_control_point gl_in; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray vBoo = {}; + vBoo[0] = patchIn.vBoo_0; + vBoo[1] = patchIn.vBoo_1; + vBoo[2] = patchIn.vBoo_2; + vBoo[3] = patchIn.vBoo_3; + out.gl_Position = (patchIn.gl_in[0u].vInputs + patchIn.gl_in[1u].vInputs) + vBoo[patchIn.vIndex]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/quad.domain.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/quad.domain.tese new file mode 100644 index 0000000..78b58ab --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/quad.domain.tese @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float2 gl_TessLevelInner [[attribute(0)]]; + float4 gl_TessLevelOuter [[attribute(1)]]; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + gl_TessCoord.y = 1.0 - gl_TessCoord.y; + out.gl_Position = float4(((gl_TessCoord.x * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.x) + (((1.0 - gl_TessCoord.x) * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.z), ((gl_TessCoord.y * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.w) + (((1.0 - gl_TessCoord.y) * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.y), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/quad.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/quad.tese new file mode 100644 index 0000000..83ef729 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/quad.tese @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float2 gl_TessLevelInner [[attribute(0)]]; + float4 gl_TessLevelOuter [[attribute(1)]]; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + out.gl_Position = float4(((gl_TessCoord.x * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.x) + (((1.0 - gl_TessCoord.x) * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.z), ((gl_TessCoord.y * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.y) + (((1.0 - gl_TessCoord.y) * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.w), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/set-from-function.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/set-from-function.tese new file mode 100644 index 0000000..6dcdbe1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/set-from-function.tese @@ -0,0 +1,55 @@ +#include +#include + +using namespace metal; + +struct Block +{ + float4 a; + float4 b; +}; + +struct Foo +{ + float4 a; + float4 b; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vColor [[attribute(0)]]; + float4 Block_a [[attribute(2)]]; + float4 Block_b [[attribute(3)]]; +}; + +struct main0_patchIn +{ + float4 vColors [[attribute(1)]]; + float4 Foo_a [[attribute(4)]]; + float4 Foo_b [[attribute(5)]]; + patch_control_point gl_in; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]]) +{ + main0_out out = {}; + Foo vFoo = {}; + vFoo.a = patchIn.Foo_a; + vFoo.b = patchIn.Foo_b; + out.gl_Position = patchIn.gl_in[0].Block_a; + out.gl_Position += patchIn.gl_in[0].Block_b; + out.gl_Position += patchIn.gl_in[1].Block_a; + out.gl_Position += patchIn.gl_in[1].Block_b; + out.gl_Position += patchIn.gl_in[0].vColor; + out.gl_Position += patchIn.gl_in[1].vColor; + out.gl_Position += patchIn.vColors; + out.gl_Position += vFoo.a; + out.gl_Position += vFoo.b; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/triangle-tess-level.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/triangle-tess-level.tese new file mode 100644 index 0000000..6930e14 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/triangle-tess-level.tese @@ -0,0 +1,69 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float4 gl_TessLevel [[attribute(0)]]; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray gl_TessLevelInner = {}; + spvUnsafeArray gl_TessLevelOuter = {}; + gl_TessLevelInner[0] = patchIn.gl_TessLevel.w; + gl_TessLevelOuter[0] = patchIn.gl_TessLevel.x; + gl_TessLevelOuter[1] = patchIn.gl_TessLevel.y; + gl_TessLevelOuter[2] = patchIn.gl_TessLevel.z; + out.gl_Position = float4((gl_TessCoord.x * gl_TessLevelInner[0]) * gl_TessLevelOuter[0], (gl_TessCoord.y * gl_TessLevelInner[0]) * gl_TessLevelOuter[1], (gl_TessCoord.z * gl_TessLevelInner[0]) * gl_TessLevelOuter[2], 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/triangle.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/triangle.tese new file mode 100644 index 0000000..12715be --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/triangle.tese @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0() +{ + main0_out out = {}; + out.gl_Position = float4(1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/tese/water_tess.tese b/third_party/spirv-cross/reference/opt/shaders-msl/tese/water_tess.tese new file mode 100644 index 0000000..df539eb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/tese/water_tess.tese @@ -0,0 +1,45 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; + float4 uScale; + float2 uInvScale; + float3 uCamPos; + float2 uPatchSize; + float2 uInvHeightmapSize; +}; + +struct main0_out +{ + float3 vWorld [[user(locn0)]]; + float4 vGradNormalTex [[user(locn1)]]; + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float2 vOutPatchPosBase [[attribute(0)]]; + float4 vPatchLods [[attribute(1)]]; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant UBO& _31 [[buffer(0)]], texture2d uHeightmapDisplacement [[texture(0)]], sampler uHeightmapDisplacementSmplr [[sampler(0)]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + float2 _202 = patchIn.vOutPatchPosBase + (float3(gl_TessCoord, 0).xy * _31.uPatchSize); + float2 _216 = mix(patchIn.vPatchLods.yx, patchIn.vPatchLods.zw, float2(float3(gl_TessCoord, 0).x)); + float _223 = mix(_216.x, _216.y, float3(gl_TessCoord, 0).y); + float _225 = floor(_223); + float2 _125 = _202 * _31.uInvHeightmapSize; + float2 _141 = _31.uInvHeightmapSize * exp2(_225); + out.vGradNormalTex = float4(_125 + (_31.uInvHeightmapSize * 0.5), _125 * _31.uScale.zw); + float3 _256 = mix(uHeightmapDisplacement.sample(uHeightmapDisplacementSmplr, (_125 + (_141 * 0.5)), level(_225)).xyz, uHeightmapDisplacement.sample(uHeightmapDisplacementSmplr, (_125 + (_141 * 1.0)), level(_225 + 1.0)).xyz, float3(_223 - _225)); + float2 _171 = (_202 * _31.uScale.xy) + _256.yz; + out.vWorld = float3(_171.x, _256.x, _171.y); + out.gl_Position = _31.uMVP * float4(out.vWorld, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.capture.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.capture.vert new file mode 100644 index 0000000..a4177d3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.capture.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex void main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]], uint gl_BaseVertex [[base_vertex]], uint gl_InstanceIndex [[instance_id]], uint gl_BaseInstance [[base_instance]], device main0_out* spvOut [[buffer(28)]], device uint* spvIndirectParams [[buffer(29)]]) +{ + device main0_out& out = spvOut[(gl_InstanceIndex - gl_BaseInstance) * spvIndirectParams[0] + gl_VertexIndex - gl_BaseVertex]; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.for-tess.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.for-tess.vert new file mode 100644 index 0000000..c99a95a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.for-tess.vert @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal; + float4 gl_Position; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvStageInputSize [[grid_size]], device main0_out* spvOut [[buffer(28)]]) +{ + device main0_out& out = spvOut[gl_GlobalInvocationID.y * spvStageInputSize.x + gl_GlobalInvocationID.x]; + if (any(gl_GlobalInvocationID >= spvStageInputSize)) + return; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.vert new file mode 100644 index 0000000..ffb4357 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/basic.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/clip-distance-block.no-user-varying.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/clip-distance-block.no-user-varying.vert new file mode 100644 index 0000000..c78105e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/clip-distance-block.no-user-varying.vert @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.Position; + out.gl_ClipDistance[0] = in.Position.x; + out.gl_ClipDistance[1] = in.Position.y; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/clip-distance-block.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/clip-distance-block.vert new file mode 100644 index 0000000..af58f35 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/clip-distance-block.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.Position; + out.gl_ClipDistance[0] = in.Position.x; + out.gl_ClipDistance[1] = in.Position.y; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/copy.flatten.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/copy.flatten.vert new file mode 100644 index 0000000..d73ee32 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/copy.flatten.vert @@ -0,0 +1,45 @@ +#include +#include + +using namespace metal; + +struct Light +{ + packed_float3 Position; + float Radius; + float4 Color; +}; + +struct UBO +{ + float4x4 uMVP; + Light lights[4]; +}; + +struct main0_out +{ + float4 vColor [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _21 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _21.uMVP * in.aVertex; + out.vColor = float4(0.0); + for (int _96 = 0; _96 < 4; ) + { + float3 _68 = in.aVertex.xyz - float3(_21.lights[_96].Position); + out.vColor += ((_21.lights[_96].Color * fast::clamp(1.0 - (length(_68) / _21.lights[_96].Radius), 0.0, 1.0)) * dot(in.aNormal, normalize(_68))); + _96++; + continue; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/dynamic.flatten.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/dynamic.flatten.vert new file mode 100644 index 0000000..92911a4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/dynamic.flatten.vert @@ -0,0 +1,45 @@ +#include +#include + +using namespace metal; + +struct Light +{ + packed_float3 Position; + float Radius; + float4 Color; +}; + +struct UBO +{ + float4x4 uMVP; + Light lights[4]; +}; + +struct main0_out +{ + float4 vColor [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _21 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _21.uMVP * in.aVertex; + out.vColor = float4(0.0); + for (int _82 = 0; _82 < 4; ) + { + float3 _54 = in.aVertex.xyz - float3(_21.lights[_82].Position); + out.vColor += ((_21.lights[_82].Color * fast::clamp(1.0 - (length(_54) / _21.lights[_82].Radius), 0.0, 1.0)) * dot(in.aNormal, normalize(_54))); + _82++; + continue; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/float-math.invariant-float-math.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/float-math.invariant-float-math.vert new file mode 100644 index 0000000..05e09e2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/float-math.invariant-float-math.vert @@ -0,0 +1,137 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Matrices +{ + float4x4 vpMatrix; + float4x4 wMatrix; + float4x3 wMatrix4x3; + float3x4 wMatrix3x4; +}; + +struct main0_out +{ + float3 OutNormal [[user(locn0)]]; + float4 OutWorldPos_0 [[user(locn1)]]; + float4 OutWorldPos_1 [[user(locn2)]]; + float4 OutWorldPos_2 [[user(locn3)]]; + float4 OutWorldPos_3 [[user(locn4)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 InPos [[attribute(0)]]; + float3 InNormal [[attribute(1)]]; +}; + +template +T spvFMul(T l, T r) +{ + return fma(l, r, T(0)); +} + +template +vec spvFMulVectorMatrix(vec v, matrix m) +{ + vec res = vec(0); + for (uint i = Rows; i > 0; --i) + { + vec tmp(0); + for (uint j = 0; j < Cols; ++j) + { + tmp[j] = m[j][i - 1]; + } + res = fma(tmp, vec(v[i - 1]), res); + } + return res; +} + +template +vec spvFMulMatrixVector(matrix m, vec v) +{ + vec res = vec(0); + for (uint i = Cols; i > 0; --i) + { + res = fma(m[i - 1], vec(v[i - 1]), res); + } + return res; +} + +template +matrix spvFMulMatrixMatrix(matrix l, matrix r) +{ + matrix res; + for (uint i = 0; i < RCols; i++) + { + vec tmp(0); + for (uint j = 0; j < LCols; j++) + { + tmp = fma(vec(r[i][j]), l[j], tmp); + } + res[i] = tmp; + } + return res; +} + +vertex main0_out main0(main0_in in [[stage_in]], constant Matrices& _22 [[buffer(0)]]) +{ + main0_out out = {}; + spvUnsafeArray OutWorldPos = {}; + float4 _37 = float4(in.InPos, 1.0); + out.gl_Position = spvFMulMatrixVector(spvFMulMatrixMatrix(_22.vpMatrix, _22.wMatrix), _37); + OutWorldPos[0] = spvFMulMatrixVector(_22.wMatrix, _37); + OutWorldPos[1] = spvFMulVectorMatrix(_37, _22.wMatrix); + OutWorldPos[2] = spvFMulMatrixVector(_22.wMatrix3x4, in.InPos); + OutWorldPos[3] = spvFMulVectorMatrix(in.InPos, _22.wMatrix4x3); + out.OutNormal = spvFMulMatrixVector(_22.wMatrix, float4(in.InNormal, 0.0)).xyz; + out.OutWorldPos_0 = OutWorldPos[0]; + out.OutWorldPos_1 = OutWorldPos[1]; + out.OutWorldPos_2 = OutWorldPos[2]; + out.OutWorldPos_3 = OutWorldPos[3]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/float-math.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/float-math.vert new file mode 100644 index 0000000..da468c2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/float-math.vert @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Matrices +{ + float4x4 vpMatrix; + float4x4 wMatrix; + float4x3 wMatrix4x3; + float3x4 wMatrix3x4; +}; + +struct main0_out +{ + float3 OutNormal [[user(locn0)]]; + float4 OutWorldPos_0 [[user(locn1)]]; + float4 OutWorldPos_1 [[user(locn2)]]; + float4 OutWorldPos_2 [[user(locn3)]]; + float4 OutWorldPos_3 [[user(locn4)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 InPos [[attribute(0)]]; + float3 InNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant Matrices& _22 [[buffer(0)]]) +{ + main0_out out = {}; + spvUnsafeArray OutWorldPos = {}; + float4 _37 = float4(in.InPos, 1.0); + out.gl_Position = (_22.vpMatrix * _22.wMatrix) * _37; + OutWorldPos[0] = _22.wMatrix * _37; + OutWorldPos[1] = _37 * _22.wMatrix; + OutWorldPos[2] = _22.wMatrix3x4 * in.InPos; + OutWorldPos[3] = in.InPos * _22.wMatrix4x3; + out.OutNormal = (_22.wMatrix * float4(in.InNormal, 0.0)).xyz; + out.OutWorldPos_0 = OutWorldPos[0]; + out.OutWorldPos_1 = OutWorldPos[1]; + out.OutWorldPos_2 = OutWorldPos[2]; + out.OutWorldPos_3 = OutWorldPos[3]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/functions.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/functions.vert new file mode 100644 index 0000000..73eaa8b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/functions.vert @@ -0,0 +1,122 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; + float3 rotDeg; + float3 rotRad; + int2 bits; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float3 vRotDeg [[user(locn1)]]; + float3 vRotRad [[user(locn2)]]; + int2 vLSB [[user(locn3)]]; + int2 vMSB [[user(locn4)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +// Implementation of the GLSL radians() function +template +inline T radians(T d) +{ + return d * T(0.01745329251); +} + +// Implementation of the GLSL degrees() function +template +inline T degrees(T r) +{ + return r * T(57.2957795131); +} + +// Implementation of the GLSL findLSB() function +template +inline T spvFindLSB(T x) +{ + return select(ctz(x), T(-1), x == T(0)); +} + +// Implementation of the signed GLSL findMSB() function +template +inline T spvFindSMSB(T x) +{ + T v = select(x, T(-1) - x, x < T(0)); + return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); +} + +// Returns the determinant of a 2x2 matrix. +static inline __attribute__((always_inline)) +float spvDet2x2(float a1, float a2, float b1, float b2) +{ + return a1 * b2 - b1 * a2; +} + +// Returns the determinant of a 3x3 matrix. +static inline __attribute__((always_inline)) +float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) +{ + return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * spvDet2x2(a2, a3, b2, b3); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float4x4 spvInverse4x4(float4x4 m) +{ + float4x4 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3]); + adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]); + + adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3]); + adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]); + + adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3]); + adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]); + + adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2]); + adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] * m[3][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = spvInverse4x4(_18.uMVP) * in.aVertex; + out.vNormal = in.aNormal; + out.vRotDeg = degrees(_18.rotRad); + out.vRotRad = radians(_18.rotDeg); + out.vLSB = spvFindLSB(_18.bits); + out.vMSB = spvFindSMSB(_18.bits); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/in_out_array_mat.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/in_out_array_mat.vert new file mode 100644 index 0000000..7a74f49 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/in_out_array_mat.vert @@ -0,0 +1,108 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float lodBias; +}; + +struct main0_out +{ + float3 outPos [[user(locn0)]]; + float3 outNormal [[user(locn1)]]; + float4 outTransModel_0 [[user(locn2)]]; + float4 outTransModel_1 [[user(locn3)]]; + float4 outTransModel_2 [[user(locn4)]]; + float4 outTransModel_3 [[user(locn5)]]; + float outLodBias [[user(locn6)]]; + float4 color [[user(locn7)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 inPos [[attribute(0)]]; + float4 colors_0 [[attribute(1)]]; + float4 colors_1 [[attribute(2)]]; + float4 colors_2 [[attribute(3)]]; + float3 inNormal [[attribute(4)]]; + float4 inViewMat_0 [[attribute(5)]]; + float4 inViewMat_1 [[attribute(6)]]; + float4 inViewMat_2 [[attribute(7)]]; + float4 inViewMat_3 [[attribute(8)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& ubo [[buffer(0)]]) +{ + main0_out out = {}; + float4x4 outTransModel = {}; + spvUnsafeArray colors = {}; + float4x4 inViewMat = {}; + colors[0] = in.colors_0; + colors[1] = in.colors_1; + colors[2] = in.colors_2; + inViewMat[0] = in.inViewMat_0; + inViewMat[1] = in.inViewMat_1; + inViewMat[2] = in.inViewMat_2; + inViewMat[3] = in.inViewMat_3; + float4 _64 = float4(in.inPos, 1.0); + out.gl_Position = (ubo.projection * ubo.model) * _64; + out.outPos = float3((ubo.model * _64).xyz); + out.outNormal = float3x3(float3(ubo.model[0].x, ubo.model[0].y, ubo.model[0].z), float3(ubo.model[1].x, ubo.model[1].y, ubo.model[1].z), float3(ubo.model[2].x, ubo.model[2].y, ubo.model[2].z)) * in.inNormal; + out.outLodBias = ubo.lodBias; + outTransModel = transpose(ubo.model) * inViewMat; + outTransModel[2] = float4(in.inNormal, 1.0); + outTransModel[1].y = ubo.lodBias; + out.color = colors[2]; + out.outTransModel_0 = outTransModel[0]; + out.outTransModel_1 = outTransModel[1]; + out.outTransModel_2 = outTransModel[2]; + out.outTransModel_3 = outTransModel[3]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/interface-block-block-composites.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vert/interface-block-block-composites.frag new file mode 100644 index 0000000..4b27f4a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/interface-block-block-composites.frag @@ -0,0 +1,99 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Vert +{ + float3x3 wMatrix; + float4 wTmp; + spvUnsafeArray arr; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vMatrix_0 [[user(locn0)]]; + float3 vMatrix_1 [[user(locn1)]]; + float3 vMatrix_2 [[user(locn2)]]; + float3 Vert_wMatrix_0 [[user(locn4)]]; + float3 Vert_wMatrix_1 [[user(locn5)]]; + float3 Vert_wMatrix_2 [[user(locn6)]]; + float4 Vert_wTmp [[user(locn7)]]; + float Vert_arr_0 [[user(locn8)]]; + float Vert_arr_1 [[user(locn9)]]; + float Vert_arr_2 [[user(locn10)]]; + float Vert_arr_3 [[user(locn11)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Vert _17 = {}; + float3x3 vMatrix = {}; + _17.wMatrix[0] = in.Vert_wMatrix_0; + _17.wMatrix[1] = in.Vert_wMatrix_1; + _17.wMatrix[2] = in.Vert_wMatrix_2; + _17.wTmp = in.Vert_wTmp; + _17.arr[0] = in.Vert_arr_0; + _17.arr[1] = in.Vert_arr_1; + _17.arr[2] = in.Vert_arr_2; + _17.arr[3] = in.Vert_arr_3; + vMatrix[0] = in.vMatrix_0; + vMatrix[1] = in.vMatrix_1; + vMatrix[2] = in.vMatrix_2; + out.FragColor = (_17.wMatrix[0].xxyy + _17.wTmp) + vMatrix[1].yyzz; + for (int _56 = 0; _56 < 4; ) + { + out.FragColor += float4(_17.arr[_56]); + _56++; + continue; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/interface-block-block-composites.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/interface-block-block-composites.vert new file mode 100644 index 0000000..69f271a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/interface-block-block-composites.vert @@ -0,0 +1,105 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Vert +{ + spvUnsafeArray arr; + float3x3 wMatrix; + float4 wTmp; +}; + +struct main0_out +{ + float3 vMatrix_0 [[user(locn0)]]; + float3 vMatrix_1 [[user(locn1)]]; + float3 vMatrix_2 [[user(locn2)]]; + float Vert_arr_0 [[user(locn4)]]; + float Vert_arr_1 [[user(locn5)]]; + float Vert_arr_2 [[user(locn6)]]; + float3 Vert_wMatrix_0 [[user(locn7)]]; + float3 Vert_wMatrix_1 [[user(locn8)]]; + float3 Vert_wMatrix_2 [[user(locn9)]]; + float4 Vert_wTmp [[user(locn10)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 Matrix_0 [[attribute(0)]]; + float3 Matrix_1 [[attribute(1)]]; + float3 Matrix_2 [[attribute(2)]]; + float4 Pos [[attribute(4)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float3x3 vMatrix = {}; + Vert _20 = {}; + float3x3 Matrix = {}; + Matrix[0] = in.Matrix_0; + Matrix[1] = in.Matrix_1; + Matrix[2] = in.Matrix_2; + vMatrix = Matrix; + _20.wMatrix = Matrix; + _20.arr[0] = 1.0; + _20.arr[1] = 2.0; + _20.arr[2] = 3.0; + _20.wTmp = in.Pos; + out.gl_Position = in.Pos; + out.vMatrix_0 = vMatrix[0]; + out.vMatrix_1 = vMatrix[1]; + out.vMatrix_2 = vMatrix[2]; + out.Vert_arr_0 = _20.arr[0]; + out.Vert_arr_1 = _20.arr[1]; + out.Vert_arr_2 = _20.arr[2]; + out.Vert_wMatrix_0 = _20.wMatrix[0]; + out.Vert_wMatrix_1 = _20.wMatrix[1]; + out.Vert_wMatrix_2 = _20.wMatrix[2]; + out.Vert_wTmp = _20.wTmp; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/interpolation-qualifiers-block.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/interpolation-qualifiers-block.vert new file mode 100644 index 0000000..4206623 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/interpolation-qualifiers-block.vert @@ -0,0 +1,55 @@ +#include +#include + +using namespace metal; + +struct Output +{ + float2 v0; + float2 v1; + float3 v2; + float4 v3; + float v4; + float v5; + float v6; +}; + +struct main0_out +{ + float2 Output_v0 [[user(locn0)]]; + float2 Output_v1 [[user(locn1)]]; + float3 Output_v2 [[user(locn2)]]; + float4 Output_v3 [[user(locn3)]]; + float Output_v4 [[user(locn4)]]; + float Output_v5 [[user(locn5)]]; + float Output_v6 [[user(locn6)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Output outp = {}; + outp.v0 = in.Position.xy; + outp.v1 = in.Position.zw; + outp.v2 = float3(in.Position.x, in.Position.z * in.Position.y, in.Position.x); + outp.v3 = in.Position.xxyy; + outp.v4 = in.Position.w; + outp.v5 = in.Position.y; + outp.v6 = in.Position.x * in.Position.w; + out.gl_Position = in.Position; + out.Output_v0 = outp.v0; + out.Output_v1 = outp.v1; + out.Output_v2 = outp.v2; + out.Output_v3 = outp.v3; + out.Output_v4 = outp.v4; + out.Output_v5 = outp.v5; + out.Output_v6 = outp.v6; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/interpolation-qualifiers.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/interpolation-qualifiers.vert new file mode 100644 index 0000000..ba2c4fb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/interpolation-qualifiers.vert @@ -0,0 +1,36 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float2 v0 [[user(locn0)]]; + float2 v1 [[user(locn1)]]; + float3 v2 [[user(locn2)]]; + float4 v3 [[user(locn3)]]; + float v4 [[user(locn4)]]; + float v5 [[user(locn5)]]; + float v6 [[user(locn6)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.v0 = in.Position.xy; + out.v1 = in.Position.zw; + out.v2 = float3(in.Position.x, in.Position.z * in.Position.y, in.Position.x); + out.v3 = in.Position.xxyy; + out.v4 = in.Position.w; + out.v5 = in.Position.y; + out.v6 = in.Position.x * in.Position.w; + out.gl_Position = in.Position; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/invariant.msl21.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/invariant.msl21.vert new file mode 100644 index 0000000..73b0ec7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/invariant.msl21.vert @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position, invariant]]; +}; + +struct main0_in +{ + float4 vInput0 [[attribute(0)]]; + float4 vInput1 [[attribute(1)]]; + float4 vInput2 [[attribute(2)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 _20 = in.vInput1 * in.vInput2; + float4 _21 = in.vInput0 + _20; + out.gl_Position = _21; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/leaf-function.capture.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/leaf-function.capture.vert new file mode 100644 index 0000000..85276e1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/leaf-function.capture.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex void main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]], uint gl_BaseVertex [[base_vertex]], uint gl_InstanceIndex [[instance_id]], uint gl_BaseInstance [[base_instance]], device main0_out* spvOut [[buffer(28)]], device uint* spvIndirectParams [[buffer(29)]]) +{ + device main0_out& out = spvOut[(gl_InstanceIndex - gl_BaseInstance) * spvIndirectParams[0] + gl_VertexIndex - gl_BaseVertex]; + out.gl_Position = _18.uMVP * in.aVertex; + out.vNormal = in.aNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/leaf-function.for-tess.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/leaf-function.for-tess.vert new file mode 100644 index 0000000..e3d2d1f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/leaf-function.for-tess.vert @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal; + float4 gl_Position; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvStageInputSize [[grid_size]], device main0_out* spvOut [[buffer(28)]]) +{ + device main0_out& out = spvOut[gl_GlobalInvocationID.y * spvStageInputSize.x + gl_GlobalInvocationID.x]; + if (any(gl_GlobalInvocationID >= spvStageInputSize)) + return; + out.gl_Position = _18.uMVP * in.aVertex; + out.vNormal = in.aNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/no-disable-vertex-out.frag-output.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no-disable-vertex-out.frag-output.vert new file mode 100644 index 0000000..14cc949 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no-disable-vertex-out.frag-output.vert @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct buf +{ + float4x4 MVP; + float4 position[36]; + float4 attr[36]; +}; + +struct main0_out +{ + float4 texcoord [[user(locn0)]]; + float3 frag_pos [[user(locn1)]]; + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(constant buf& ubuf [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.texcoord = ubuf.attr[int(gl_VertexIndex)]; + out.gl_Position = ubuf.MVP * ubuf.position[int(gl_VertexIndex)]; + out.frag_pos = out.gl_Position.xyz; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.for-tess.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.for-tess.vert new file mode 100644 index 0000000..984e832 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.for-tess.vert @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_10_12 +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_m0[1024]; +}; + +struct main0_in +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_19 [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], device _RESERVED_IDENTIFIER_FIXUP_10_12& _RESERVED_IDENTIFIER_FIXUP_12 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvStageInputSize [[grid_size]], uint3 spvDispatchBase [[grid_origin]]) +{ + if (any(gl_GlobalInvocationID >= spvStageInputSize)) + return; + uint gl_VertexIndex = gl_GlobalInvocationID.x + spvDispatchBase.x; + _RESERVED_IDENTIFIER_FIXUP_12._RESERVED_IDENTIFIER_FIXUP_m0[int(gl_VertexIndex)] = in._RESERVED_IDENTIFIER_FIXUP_19; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.vert new file mode 100644 index 0000000..e804da6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.vert @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_10_12 +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_m0[1024]; +}; + +struct main0_in +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_19 [[attribute(0)]]; +}; + +vertex void main0(main0_in in [[stage_in]], device _RESERVED_IDENTIFIER_FIXUP_10_12& _RESERVED_IDENTIFIER_FIXUP_12 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + _RESERVED_IDENTIFIER_FIXUP_12._RESERVED_IDENTIFIER_FIXUP_m0[int(gl_VertexIndex)] = in._RESERVED_IDENTIFIER_FIXUP_19; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_buff.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_buff.vert new file mode 100644 index 0000000..296293a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_buff.vert @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_33_35 +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_m0[1024]; +}; + +struct _RESERVED_IDENTIFIER_FIXUP_38_40 +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_m0[1024]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 _RESERVED_IDENTIFIER_FIXUP_14 [[attribute(0)]]; +}; + +vertex void main0(main0_in in [[stage_in]], device _RESERVED_IDENTIFIER_FIXUP_33_35& _RESERVED_IDENTIFIER_FIXUP_35 [[buffer(0)]], constant _RESERVED_IDENTIFIER_FIXUP_38_40& _RESERVED_IDENTIFIER_FIXUP_40 [[buffer(1)]]) +{ + main0_out out = {}; + out.gl_Position = in._RESERVED_IDENTIFIER_FIXUP_14; + for (int _52 = 0; _52 < 1024; ) + { + _RESERVED_IDENTIFIER_FIXUP_35._RESERVED_IDENTIFIER_FIXUP_m0[_52] = _RESERVED_IDENTIFIER_FIXUP_40._RESERVED_IDENTIFIER_FIXUP_m0[_52]; + _52++; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_buff_atomic.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_buff_atomic.vert new file mode 100644 index 0000000..92fbf55 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_buff_atomic.vert @@ -0,0 +1,30 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_19_21 +{ + uint _RESERVED_IDENTIFIER_FIXUP_m0; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 _RESERVED_IDENTIFIER_FIXUP_14 [[attribute(0)]]; +}; + +vertex void main0(main0_in in [[stage_in]], volatile device _RESERVED_IDENTIFIER_FIXUP_19_21& _RESERVED_IDENTIFIER_FIXUP_21 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = in._RESERVED_IDENTIFIER_FIXUP_14; + uint _29 = atomic_fetch_add_explicit((volatile device atomic_uint*)&_RESERVED_IDENTIFIER_FIXUP_21._RESERVED_IDENTIFIER_FIXUP_m0, 1u, memory_order_relaxed); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_tex.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_tex.vert new file mode 100644 index 0000000..9d87efe --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/no_stage_out.write_tex.vert @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 _RESERVED_IDENTIFIER_FIXUP_14 [[attribute(0)]]; +}; + +vertex void main0(main0_in in [[stage_in]], texture1d _RESERVED_IDENTIFIER_FIXUP_32 [[texture(0)]], texture1d _RESERVED_IDENTIFIER_FIXUP_35 [[texture(1)]]) +{ + main0_out out = {}; + out.gl_Position = in._RESERVED_IDENTIFIER_FIXUP_14; + for (int _45 = 0; _45 < 128; ) + { + _RESERVED_IDENTIFIER_FIXUP_32.write(_RESERVED_IDENTIFIER_FIXUP_35.read(uint(_45)), uint(_45)); + _45++; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/out_block.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/out_block.vert new file mode 100644 index 0000000..45b8970 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/out_block.vert @@ -0,0 +1,41 @@ +#include +#include + +using namespace metal; + +struct Transform +{ + float4x4 transform; +}; + +struct VertexOut +{ + float4 color; + float4 color2; +}; + +struct main0_out +{ + float4 VertexOut_color [[user(locn2)]]; + float4 VertexOut_color2 [[user(locn3)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 position [[attribute(0)]]; + float4 color [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant Transform& block [[buffer(0)]]) +{ + main0_out out = {}; + VertexOut outputs = {}; + out.gl_Position = block.transform * float4(in.position, 1.0); + outputs.color = in.color; + outputs.color2 = in.color + float4(1.0); + out.VertexOut_color = outputs.color; + out.VertexOut_color2 = outputs.color2; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed-bool-to-uint.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed-bool-to-uint.vert new file mode 100644 index 0000000..6cc5520 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed-bool-to-uint.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Struct +{ + uint flags[1]; +}; + +struct defaultUniformsVS +{ + Struct flags; + float4 uquad[4]; + float4x4 umatrix; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant defaultUniformsVS& _24 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.gl_Position = _24.umatrix * float4(_24.uquad[int(gl_VertexIndex)].x, _24.uquad[int(gl_VertexIndex)].y, in.a_position.z, in.a_position.w); + if (_24.flags.flags[0] != 0u) + { + out.gl_Position.z = 0.0; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed-bool2-to-packed_uint2.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed-bool2-to-packed_uint2.vert new file mode 100644 index 0000000..4c46aae --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed-bool2-to-packed_uint2.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Struct +{ + uint2 flags[1]; +}; + +struct defaultUniformsVS +{ + Struct flags; + float4 uquad[4]; + float4x4 umatrix; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant defaultUniformsVS& _25 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.gl_Position = _25.umatrix * float4(_25.uquad[int(gl_VertexIndex)].x, _25.uquad[int(gl_VertexIndex)].y, in.a_position.z, in.a_position.w); + if (_25.flags.flags[0].x != 0u) + { + out.gl_Position.z = 0.0; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed_matrix.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed_matrix.vert new file mode 100644 index 0000000..74b2c5f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/packed_matrix.vert @@ -0,0 +1,48 @@ +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_1365_18812 +{ + float3x4 _RESERVED_IDENTIFIER_FIXUP_m0; + float3x4 _RESERVED_IDENTIFIER_FIXUP_m1; +}; + +struct _RESERVED_IDENTIFIER_FIXUP_1126_22044 +{ + float4x4 _RESERVED_IDENTIFIER_FIXUP_m0; + float4x4 _RESERVED_IDENTIFIER_FIXUP_m1; + float _RESERVED_IDENTIFIER_FIXUP_m9; + char _m3_pad[12]; + packed_float3 _RESERVED_IDENTIFIER_FIXUP_m10; + float _RESERVED_IDENTIFIER_FIXUP_m11; + packed_float3 _RESERVED_IDENTIFIER_FIXUP_m12; + float _RESERVED_IDENTIFIER_FIXUP_m17; + float _RESERVED_IDENTIFIER_FIXUP_m18; + float _RESERVED_IDENTIFIER_FIXUP_m19; + float2 _RESERVED_IDENTIFIER_FIXUP_m20; +}; + +struct main0_out +{ + float3 _RESERVED_IDENTIFIER_FIXUP_3976 [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 _RESERVED_IDENTIFIER_FIXUP_5275 [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant _RESERVED_IDENTIFIER_FIXUP_1365_18812& _RESERVED_IDENTIFIER_FIXUP_18812 [[buffer(0)]], constant _RESERVED_IDENTIFIER_FIXUP_1126_22044& _RESERVED_IDENTIFIER_FIXUP_22044 [[buffer(1)]]) +{ + main0_out out = {}; + float4 _70 = _RESERVED_IDENTIFIER_FIXUP_22044._RESERVED_IDENTIFIER_FIXUP_m0 * float4(float3(_RESERVED_IDENTIFIER_FIXUP_22044._RESERVED_IDENTIFIER_FIXUP_m10) + (in._RESERVED_IDENTIFIER_FIXUP_5275.xyz * (_RESERVED_IDENTIFIER_FIXUP_22044._RESERVED_IDENTIFIER_FIXUP_m17 + _RESERVED_IDENTIFIER_FIXUP_22044._RESERVED_IDENTIFIER_FIXUP_m18)), 1.0); + out._RESERVED_IDENTIFIER_FIXUP_3976 = normalize(float4(in._RESERVED_IDENTIFIER_FIXUP_5275.xyz, 0.0) * _RESERVED_IDENTIFIER_FIXUP_18812._RESERVED_IDENTIFIER_FIXUP_m1); + float4 _94 = _70; + _94.y = -_70.y; + out.gl_Position = _94; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/pointsize.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/pointsize.vert new file mode 100644 index 0000000..8e5782b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/pointsize.vert @@ -0,0 +1,33 @@ +#include +#include + +using namespace metal; + +struct params +{ + float4x4 mvp; + float psize; +}; + +struct main0_out +{ + float4 color [[user(locn0)]]; + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; +}; + +struct main0_in +{ + float4 position [[attribute(0)]]; + float4 color0 [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant params& _19 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _19.mvp * in.position; + out.gl_PointSize = _19.psize; + out.color = in.color0; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/read-from-row-major-array.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..ec3e7b7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/read-from-row-major-array.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct Block +{ + float3x4 var[3][4]; +}; + +struct main0_out +{ + float v_vtxResult [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant Block& _104 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = in.a_position; + out.v_vtxResult = ((float(abs(_104.var[0][0][0][0] - 2.0) < 0.0500000007450580596923828125) * float(abs(_104.var[0][0][1][0] - 6.0) < 0.0500000007450580596923828125)) * float(abs(_104.var[0][0][2][0] - (-6.0)) < 0.0500000007450580596923828125)) * ((float(abs(_104.var[0][0][0][1]) < 0.0500000007450580596923828125) * float(abs(_104.var[0][0][1][1] - 5.0) < 0.0500000007450580596923828125)) * float(abs(_104.var[0][0][2][1] - 5.0) < 0.0500000007450580596923828125)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/resource-arrays-leaf.ios.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/resource-arrays-leaf.ios.vert new file mode 100644 index 0000000..91be3a0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/resource-arrays-leaf.ios.vert @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct storage_block +{ + uint4 baz; + int2 quux; +}; + +struct constant_block +{ + float4 foo; + int bar; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 3 +#endif +constant int arraySize = SPIRV_CROSS_CONSTANT_ID_0; + +vertex void main0(device storage_block* storage_0 [[buffer(0)]], device storage_block* storage_1 [[buffer(1)]], constant constant_block* constants_0 [[buffer(2)]], constant constant_block* constants_1 [[buffer(3)]], constant constant_block* constants_2 [[buffer(4)]], constant constant_block* constants_3 [[buffer(5)]], array, 3> images [[texture(0)]]) +{ + device storage_block* storage[] = + { + storage_0, + storage_1, + }; + + constant constant_block* constants[] = + { + constants_0, + constants_1, + constants_2, + constants_3, + }; + + storage[0]->baz = uint4(constants[3]->foo); + storage[1]->quux = images[2].read(uint2(int2(constants[1]->bar))).xy; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/resource-arrays.ios.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/resource-arrays.ios.vert new file mode 100644 index 0000000..91be3a0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/resource-arrays.ios.vert @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct storage_block +{ + uint4 baz; + int2 quux; +}; + +struct constant_block +{ + float4 foo; + int bar; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 3 +#endif +constant int arraySize = SPIRV_CROSS_CONSTANT_ID_0; + +vertex void main0(device storage_block* storage_0 [[buffer(0)]], device storage_block* storage_1 [[buffer(1)]], constant constant_block* constants_0 [[buffer(2)]], constant constant_block* constants_1 [[buffer(3)]], constant constant_block* constants_2 [[buffer(4)]], constant constant_block* constants_3 [[buffer(5)]], array, 3> images [[texture(0)]]) +{ + device storage_block* storage[] = + { + storage_0, + storage_1, + }; + + constant constant_block* constants[] = + { + constants_0, + constants_1, + constants_2, + constants_3, + }; + + storage[0]->baz = uint4(constants[3]->foo); + storage[1]->quux = images[2].read(uint2(int2(constants[1]->bar))).xy; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/return-array.force-native-array.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/return-array.force-native-array.vert new file mode 100644 index 0000000..ce13349 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/return-array.force-native-array.vert @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vInput1 [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = float4(10.0) + in.vInput1; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/return-array.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/return-array.vert new file mode 100644 index 0000000..ce13349 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/return-array.vert @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vInput1 [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = float4(10.0) + in.vInput1; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/set_builtin_in_func.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000..51a858a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + out.gl_PointSize = 1.0; + out.gl_Position = float4(out.gl_PointSize); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/sign-int-types.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/sign-int-types.vert new file mode 100644 index 0000000..a510645 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/sign-int-types.vert @@ -0,0 +1,60 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; + float4 uFloatVec4; + float3 uFloatVec3; + float2 uFloatVec2; + float uFloat; + int4 uIntVec4; + int3 uIntVec3; + int2 uIntVec2; + int uInt; +}; + +struct main0_out +{ + float4 vFloatVec4 [[user(locn0)]]; + float3 vFloatVec3 [[user(locn1)]]; + float2 vFloatVec2 [[user(locn2)]]; + float vFloat [[user(locn3)]]; + int4 vIntVec4 [[user(locn4)]]; + int3 vIntVec3 [[user(locn5)]]; + int2 vIntVec2 [[user(locn6)]]; + int vInt [[user(locn7)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; +}; + +// Implementation of the GLSL sign() function for integer types +template::value>::type> +inline T sign(T x) +{ + return select(select(select(x, T(0), x == T(0)), T(1), x > T(0)), T(-1), x < T(0)); +} + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _21 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _21.uMVP * in.aVertex; + out.vFloatVec4 = sign(_21.uFloatVec4); + out.vFloatVec3 = sign(_21.uFloatVec3); + out.vFloatVec2 = sign(_21.uFloatVec2); + out.vFloat = sign(_21.uFloat); + out.vIntVec4 = sign(_21.uIntVec4); + out.vIntVec3 = sign(_21.uIntVec3); + out.vIntVec2 = sign(_21.uIntVec2); + out.vInt = sign(_21.uInt); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/signedness-mismatch.shader-inputs.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/signedness-mismatch.shader-inputs.vert new file mode 100644 index 0000000..d6e056b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/signedness-mismatch.shader-inputs.vert @@ -0,0 +1,74 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + ushort2 a [[attribute(0)]]; + uint3 b [[attribute(1)]]; + ushort c_0 [[attribute(2)]]; + ushort c_1 [[attribute(3)]]; + uint4 d_0 [[attribute(4)]]; + uint4 d_1 [[attribute(5)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray c = {}; + spvUnsafeArray d = {}; + c[0] = in.c_0; + c[1] = in.c_1; + d[0] = in.d_0; + d[1] = in.d_1; + out.gl_Position = float4(float(int(in.a.x)), float(in.b.x), float(uint(c[1])), float(d[0].w)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert new file mode 100644 index 0000000..33c077c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(texture_buffer uSamp [[texture(0)]], texture_buffer uSampo [[texture(1)]]) +{ + main0_out out = {}; + out.gl_Position = uSamp.read(uint(10)) + uSampo.read(uint(100)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/texture_buffer.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/texture_buffer.vert new file mode 100644 index 0000000..3b3d92b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/texture_buffer.vert @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +vertex main0_out main0(texture2d uSamp [[texture(0)]], texture2d uSampo [[texture(1)]]) +{ + main0_out out = {}; + out.gl_Position = uSamp.read(spvTexelBufferCoord(10)) + uSampo.read(spvTexelBufferCoord(100)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/ubo.alignment.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/ubo.alignment.vert new file mode 100644 index 0000000..c48111e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/ubo.alignment.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 mvp; + float2 targSize; + char _m2_pad[8]; + packed_float3 color; + float opacity; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float3 vColor [[user(locn1)]]; + float2 vSize [[user(locn2)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _18.mvp * in.aVertex; + out.vNormal = in.aNormal; + out.vColor = float3(_18.color) * _18.opacity; + out.vSize = _18.targSize * _18.opacity; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vert/ubo.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vert/ubo.vert new file mode 100644 index 0000000..86ba1e9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vert/ubo.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 mvp; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _16.mvp * in.aVertex; + out.vNormal = in.aNormal; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag new file mode 100644 index 0000000..f0935f6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag @@ -0,0 +1,73 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex_0 [[user(locn1)]]; + float2 vTex_1 [[user(locn2)]]; + float2 vTex_2 [[user(locn3)]]; + float2 vTex_3 [[user(locn4)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]]) +{ + main0_out out = {}; + spvUnsafeArray vTex = {}; + vTex[0] = in.vTex_0; + vTex[1] = in.vTex_1; + vTex[2] = in.vTex_2; + vTex[3] = in.vTex_3; + const uint gl_ViewIndex = spvViewMask[0]; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, vTex[int(gl_ViewIndex)]); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag new file mode 100644 index 0000000..67895e3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag @@ -0,0 +1,73 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex_0 [[user(locn1)]]; + float2 vTex_1 [[user(locn2)]]; + float2 vTex_2 [[user(locn3)]]; + float2 vTex_3 [[user(locn4)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]], uint gl_ViewIndex [[render_target_array_index]]) +{ + main0_out out = {}; + spvUnsafeArray vTex = {}; + vTex[0] = in.vTex_0; + vTex[1] = in.vTex_1; + vTex[2] = in.vTex_2; + vTex[3] = in.vTex_3; + gl_ViewIndex += spvViewMask[0]; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, vTex[int(gl_ViewIndex)]); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag new file mode 100644 index 0000000..0992a76 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + bool _15 = simd_is_helper_thread(); + discard_fragment(); + if (!_15) + { + out.FragColor = float4(1.0, 0.0, 0.0, 1.0); + } + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag new file mode 100644 index 0000000..fd306b3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ + discard_fragment(); + bool _9 = simd_is_helper_thread(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag new file mode 100644 index 0000000..fd306b3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ + discard_fragment(); + bool _9 = simd_is_helper_thread(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/push-constant.vk.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/push-constant.vk.frag new file mode 100644 index 0000000..7b8c502 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/push-constant.vk.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct PushConstants +{ + float4 value0; + float4 value1; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant PushConstants& push [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = (in.vColor + push.value0) + push.value1; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag new file mode 100644 index 0000000..6baf93f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 1.0 +#endif +constant float a = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 2.0 +#endif +constant float b = SPIRV_CROSS_CONSTANT_ID_2; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(a + b); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/spec-constant.vk.frag b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/spec-constant.vk.frag new file mode 100644 index 0000000..aee290f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/frag/spec-constant.vk.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant float a_tmp [[function_constant(1)]]; +constant float a = is_function_constant_defined(a_tmp) ? a_tmp : 1.0; +constant float b_tmp [[function_constant(2)]]; +constant float b = is_function_constant_defined(b_tmp) ? b_tmp : 2.0; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(a + b); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert new file mode 100644 index 0000000..e36576b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + const int gl_DeviceIndex = 0; + const uint gl_ViewIndex = 0; + out.gl_Position = float4(float(gl_DeviceIndex), float(int(gl_ViewIndex)), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert new file mode 100644 index 0000000..cc4bcc4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + const int gl_DeviceIndex = 0; + out.gl_Position = float4(float(gl_DeviceIndex)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert new file mode 100644 index 0000000..8959afe --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], constant MVPs& _19 [[buffer(0)]]) +{ + main0_out out = {}; + const uint gl_ViewIndex = spvViewMask[0]; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert new file mode 100644 index 0000000..20eff0a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], constant MVPs& _19 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]], uint gl_BaseInstance [[base_instance]]) +{ + main0_out out = {}; + uint gl_ViewIndex = spvViewMask[0] + (gl_InstanceIndex - gl_BaseInstance) % spvViewMask[1]; + gl_InstanceIndex = (gl_InstanceIndex - gl_BaseInstance) / spvViewMask[1] + gl_BaseInstance; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + out.gl_Layer = gl_ViewIndex - spvViewMask[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert new file mode 100644 index 0000000..5152b62 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant MVPs& _19 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]], uint gl_BaseInstance [[base_instance]]) +{ + main0_out out = {}; + const uint gl_ViewIndex = 0; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/small-storage.vk.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/small-storage.vk.vert new file mode 100644 index 0000000..c9ef91b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/small-storage.vk.vert @@ -0,0 +1,48 @@ +#include +#include + +using namespace metal; + +struct block +{ + short2 a; + ushort2 b; + char2 c; + uchar2 d; + half2 e; +}; + +struct storage +{ + short3 f; + ushort3 g; + char3 h; + uchar3 i; + half3 j; +}; + +struct main0_out +{ + short4 p [[user(locn0)]]; + ushort4 q [[user(locn1)]]; + half4 r [[user(locn2)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + short foo [[attribute(0)]]; + ushort bar [[attribute(1)]]; + half baz [[attribute(2)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant block& _26 [[buffer(0)]], const device storage& _53 [[buffer(1)]]) +{ + main0_out out = {}; + out.p = short4((int4(int(in.foo)) + int4(int2(_26.a), int2(_26.c))) - int4(int3(_53.f) / int3(_53.h), 1)); + out.q = ushort4((uint4(uint(in.bar)) + uint4(uint2(_26.b), uint2(_26.d))) - uint4(uint3(_53.g) / uint3(_53.i), 1u)); + out.r = half4((float4(float(in.baz)) + float4(float2(_26.e), 0.0, 1.0)) - float4(float3(_53.j), 1.0)); + out.gl_Position = float4(0.0, 0.0, 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert new file mode 100644 index 0000000..86a0cea --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + out.gl_Position = float4(1.0, 2.0, 3.0, 4.0) * float(int(gl_VertexIndex) + int(gl_InstanceIndex)); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/depth-compare.asm.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/depth-compare.asm.frag new file mode 100644 index 0000000..7f593ef --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/depth-compare.asm.frag @@ -0,0 +1,314 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Globals +{ + float3 SoftTransitionScale; + float4x4 ShadowViewProjectionMatrices[6]; + float InvShadowmapResolution; + float ShadowFadeFraction; + float ShadowSharpen; + float4 LightPositionAndInvRadius; + float2 ProjectionDepthBiasParameters; + float4 PointLightDepthBiasAndProjParameters; +}; + +constant float4 _453 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d SceneTexturesStruct_SceneDepthTexture [[texture(0)]], texture2d SceneTexturesStruct_GBufferATexture [[texture(1)]], texture2d SceneTexturesStruct_GBufferBTexture [[texture(2)]], texture2d SceneTexturesStruct_GBufferDTexture [[texture(3)]], depthcube ShadowDepthCubeTexture [[texture(4)]], texture2d SSProfilesTexture [[texture(5)]], sampler SceneTexturesStruct_SceneDepthTextureSampler [[sampler(0)]], sampler SceneTexturesStruct_GBufferATextureSampler [[sampler(1)]], sampler SceneTexturesStruct_GBufferBTextureSampler [[sampler(2)]], sampler SceneTexturesStruct_GBufferDTextureSampler [[sampler(3)]], sampler ShadowDepthTextureSampler [[sampler(4)]], sampler ShadowDepthCubeTextureSampler [[sampler(5)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float2 _114 = gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw; + float4 _118 = SceneTexturesStruct_SceneDepthTexture.sample(SceneTexturesStruct_SceneDepthTextureSampler, _114, level(0.0)); + float _119 = _118.x; + float _133 = ((_119 * View.View_InvDeviceZToWorldZTransform.x) + View.View_InvDeviceZToWorldZTransform.y) + (1.0 / ((_119 * View.View_InvDeviceZToWorldZTransform.z) - View.View_InvDeviceZToWorldZTransform.w)); + float4 _147 = View.View_ScreenToWorld * float4(((_114 - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_133), _133, 1.0); + float3 _148 = _147.xyz; + float3 _152 = _Globals.LightPositionAndInvRadius.xyz - _148; + float _158 = length(_152); + bool _160 = (_158 * _Globals.LightPositionAndInvRadius.w) < 1.0; + float _207; + if (_160) + { + float3 _165 = abs(_152); + float _166 = _165.x; + float _167 = _165.y; + float _168 = _165.z; + float _170 = fast::max(_166, fast::max(_167, _168)); + int _189; + if (_170 == _166) + { + _189 = (_166 == _152.x) ? 0 : 1; + } + else + { + int _185; + if (_170 == _167) + { + _185 = (_167 == _152.y) ? 2 : 3; + } + else + { + _185 = (_168 == _152.z) ? 4 : 5; + } + _189 = _185; + } + float4 _196 = _Globals.ShadowViewProjectionMatrices[_189] * float4(_147.xyz, 1.0); + float _198 = _196.w; + _207 = ShadowDepthCubeTexture.sample_compare(ShadowDepthCubeTextureSampler, (_152 / float3(_158)), (_196.z / _198) + ((-_Globals.PointLightDepthBiasAndProjParameters.x) / _198), level(0.0)); + } + else + { + _207 = 1.0; + } + float _213 = fast::clamp(((_207 - 0.5) * _Globals.ShadowSharpen) + 0.5, 0.0, 1.0); + float _218 = sqrt(mix(1.0, _213 * _213, _Globals.ShadowFadeFraction)); + float4 _219 = _453; + _219.z = _218; + float3 _236 = normalize((SceneTexturesStruct_GBufferATexture.sample(SceneTexturesStruct_GBufferATextureSampler, _114, level(0.0)).xyz * float3(2.0)) - float3(1.0)); + uint _240 = uint(round(SceneTexturesStruct_GBufferBTexture.sample(SceneTexturesStruct_GBufferBTextureSampler, _114, level(0.0)).w * 255.0)); + bool _248 = (_240 & 15u) == 5u; + float _448; + if (_248) + { + float4 _260 = SSProfilesTexture.read(uint2(int3(1, int(uint((select(float4(0.0), SceneTexturesStruct_GBufferDTexture.sample(SceneTexturesStruct_GBufferDTextureSampler, _114, level(0.0)), bool4(!(((_240 & 4294967280u) & 16u) != 0u))).x * 255.0) + 0.5)), 0).xy), 0); + float _263 = _260.y * 0.5; + float _274 = pow(fast::clamp(dot(-(_152 * float3(rsqrt(dot(_152, _152)))), _236), 0.0, 1.0), 1.0); + float _445; + if (_160) + { + float3 _278 = _152 / float3(_158); + float3 _280 = normalize(cross(_278, float3(0.0, 0.0, 1.0))); + float3 _284 = float3(_Globals.InvShadowmapResolution); + float3 _285 = _280 * _284; + float3 _286 = cross(_280, _278) * _284; + float3 _287 = abs(_278); + float _288 = _287.x; + float _289 = _287.y; + float _290 = _287.z; + float _292 = fast::max(_288, fast::max(_289, _290)); + int _311; + if (_292 == _288) + { + _311 = (_288 == _278.x) ? 0 : 1; + } + else + { + int _307; + if (_292 == _289) + { + _307 = (_289 == _278.y) ? 2 : 3; + } + else + { + _307 = (_290 == _278.z) ? 4 : 5; + } + _311 = _307; + } + float4 _318 = _Globals.ShadowViewProjectionMatrices[_311] * float4(_148 - (_236 * float3(_263)), 1.0); + float _323 = _260.x * (10.0 / _Globals.LightPositionAndInvRadius.w); + float _329 = (1.0 / (((_318.z / _318.w) * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w; + float _342 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, (_278 + (_286 * float3(2.5))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + float _364 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, ((_278 + (_285 * float3(2.3776409626007080078125))) + (_286 * float3(0.77254199981689453125))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + float _387 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, ((_278 + (_285 * float3(1.46946299076080322265625))) + (_286 * float3(-2.0225429534912109375))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + float _410 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, ((_278 + (_285 * float3(-1.46946299076080322265625))) + (_286 * float3(-2.02254199981689453125))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + float _433 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, ((_278 + (_285 * float3(-2.3776409626007080078125))) + (_286 * float3(0.772543013095855712890625))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + _445 = (((((fast::clamp(abs((_342 > 0.0) ? (_342 + _263) : fast::max(0.0, (_342 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25) + (fast::clamp(abs((_364 > 0.0) ? (_364 + _263) : fast::max(0.0, (_364 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25)) + (fast::clamp(abs((_387 > 0.0) ? (_387 + _263) : fast::max(0.0, (_387 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25)) + (fast::clamp(abs((_410 > 0.0) ? (_410 + _263) : fast::max(0.0, (_410 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25)) + (fast::clamp(abs((_433 > 0.0) ? (_433 + _263) : fast::max(0.0, (_433 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25)) * 0.20000000298023223876953125; + } + else + { + _445 = 1.0; + } + _448 = 1.0 - (_445 * 0.20000000298023223876953125); + } + else + { + _448 = 1.0; + } + float4 _451 = float4(float3(1.0).x, float3(1.0).y, _219.z, float3(1.0).z); + _451.w = _248 ? sqrt(_448) : _218; + out.out_var_SV_Target0 = _451; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/global-constant-arrays.asm.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/global-constant-arrays.asm.frag new file mode 100644 index 0000000..c10e391 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/global-constant-arrays.asm.frag @@ -0,0 +1,1141 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_Globals +{ + float4 MappingPolynomial; + float3 InverseGamma; + float4 ColorMatrixR_ColorCurveCd1; + float4 ColorMatrixG_ColorCurveCd3Cm3; + float4 ColorMatrixB_ColorCurveCm2; + float4 ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3; + float4 ColorCurve_Ch1_Ch2; + float4 ColorShadow_Luma; + float4 ColorShadow_Tint1; + float4 ColorShadow_Tint2; + float FilmSlope; + float FilmToe; + float FilmShoulder; + float FilmBlackClip; + float FilmWhiteClip; + packed_float3 ColorScale; + float4 OverlayColor; + float WhiteTemp; + float WhiteTint; + float4 ColorSaturation; + float4 ColorContrast; + float4 ColorGamma; + float4 ColorGain; + float4 ColorOffset; + float4 ColorSaturationShadows; + float4 ColorContrastShadows; + float4 ColorGammaShadows; + float4 ColorGainShadows; + float4 ColorOffsetShadows; + float4 ColorSaturationMidtones; + float4 ColorContrastMidtones; + float4 ColorGammaMidtones; + float4 ColorGainMidtones; + float4 ColorOffsetMidtones; + float4 ColorSaturationHighlights; + float4 ColorContrastHighlights; + float4 ColorGammaHighlights; + float4 ColorGainHighlights; + float4 ColorOffsetHighlights; + float ColorCorrectionShadowsMax; + float ColorCorrectionHighlightsMin; + uint OutputDevice; + uint OutputGamut; + float BlueCorrection; + float ExpandGamut; +}; + +constant spvUnsafeArray _475 = spvUnsafeArray({ -4.0, -4.0, -3.1573765277862548828125, -0.485249996185302734375, 1.84773242473602294921875, 1.84773242473602294921875 }); +constant spvUnsafeArray _476 = spvUnsafeArray({ -0.718548238277435302734375, 2.0810306072235107421875, 3.66812419891357421875, 4.0, 4.0, 4.0 }); +constant spvUnsafeArray _479 = spvUnsafeArray({ -4.97062206268310546875, -3.0293781757354736328125, -2.1261999607086181640625, -1.5104999542236328125, -1.0578000545501708984375, -0.4668000042438507080078125, 0.11937999725341796875, 0.7088134288787841796875, 1.2911865711212158203125, 1.2911865711212158203125 }); +constant spvUnsafeArray _480 = spvUnsafeArray({ 0.80891323089599609375, 1.19108676910400390625, 1.5683000087738037109375, 1.94830000400543212890625, 2.308300018310546875, 2.63840007781982421875, 2.85949993133544921875, 2.9872608184814453125, 3.0127391815185546875, 3.0127391815185546875 }); +constant spvUnsafeArray _482 = spvUnsafeArray({ -2.3010299205780029296875, -2.3010299205780029296875, -1.9312000274658203125, -1.5204999446868896484375, -1.0578000545501708984375, -0.4668000042438507080078125, 0.11937999725341796875, 0.7088134288787841796875, 1.2911865711212158203125, 1.2911865711212158203125 }); +constant spvUnsafeArray _483 = spvUnsafeArray({ 0.801995217800140380859375, 1.19800484180450439453125, 1.5943000316619873046875, 1.99730002880096435546875, 2.3782999515533447265625, 2.7683999538421630859375, 3.0515000820159912109375, 3.2746293544769287109375, 3.32743072509765625, 3.32743072509765625 }); + +constant float3 _391 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0), center_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_Globals& _Globals [[buffer(0)]], uint gl_Layer [[render_target_array_index]]) +{ + main0_out out = {}; + float3x3 _546 = float3x3(float3(0.41245639324188232421875, 0.3575761020183563232421875, 0.180437505245208740234375), float3(0.21267290413379669189453125, 0.715152204036712646484375, 0.072175003588199615478515625), float3(0.01933390088379383087158203125, 0.119191996753215789794921875, 0.950304090976715087890625)) * float3x3(float3(1.01303005218505859375, 0.0061053098179399967193603515625, -0.014971000142395496368408203125), float3(0.0076982299797236919403076171875, 0.99816501140594482421875, -0.005032029934227466583251953125), float3(-0.0028413101099431514739990234375, 0.0046851597726345062255859375, 0.92450702190399169921875)); + float3x3 _547 = _546 * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)); + float3x3 _548 = float3x3(float3(0.662454187870025634765625, 0.1340042054653167724609375, 0.1561876833438873291015625), float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625), float3(-0.0055746496655046939849853515625, 0.0040607335977256298065185546875, 1.01033914089202880859375)) * float3x3(float3(0.98722398281097412109375, -0.0061132698319852352142333984375, 0.01595330052077770233154296875), float3(-0.007598360069096088409423828125, 1.00186002254486083984375, 0.0053300200961530208587646484375), float3(0.003072570078074932098388671875, -0.0050959498621523380279541015625, 1.0816800594329833984375)); + float3x3 _549 = _548 * float3x3(float3(3.2409698963165283203125, -1.53738319873809814453125, -0.4986107647418975830078125), float3(-0.96924364566802978515625, 1.875967502593994140625, 0.0415550582110881805419921875), float3(0.055630080401897430419921875, -0.2039769589900970458984375, 1.05697154998779296875)); + float3x3 _550 = float3x3(float3(0.952552378177642822265625, 0.0, 9.25), float3(0.3439664542675018310546875, 0.728166103363037109375, -0.07213254272937774658203125), float3(0.0, 0.0, 1.00882518291473388671875)) * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)); + float3x3 _551 = float3x3(float3(0.662454187870025634765625, 0.1340042054653167724609375, 0.1561876833438873291015625), float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625), float3(-0.0055746496655046939849853515625, 0.0040607335977256298065185546875, 1.01033914089202880859375)) * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625)); + float3x3 _576; + for (;;) + { + if (_Globals.OutputGamut == 1u) + { + _576 = _548 * float3x3(float3(2.493396282196044921875, -0.931345880031585693359375, -0.4026944935321807861328125), float3(-0.829486787319183349609375, 1.76265966892242431640625, 0.02362460084259510040283203125), float3(0.0358506999909877777099609375, -0.076182700693607330322265625, 0.957014024257659912109375)); + break; + } + else + { + if (_Globals.OutputGamut == 2u) + { + _576 = _548 * float3x3(float3(1.71660840511322021484375, -0.3556621074676513671875, -0.253360092639923095703125), float3(-0.666682898998260498046875, 1.61647760868072509765625, 0.01576850004494190216064453125), float3(0.017642199993133544921875, -0.04277630150318145751953125, 0.94222867488861083984375)); + break; + } + else + { + if (_Globals.OutputGamut == 3u) + { + _576 = float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625)); + break; + } + else + { + if (_Globals.OutputGamut == 4u) + { + _576 = float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 1.0, 0.0), float3(0.0, 0.0, 1.0)); + break; + } + else + { + _576 = _549; + break; + } + } + } + } + } + float3 _577 = float4((in.in_var_TEXCOORD0 - float2(0.015625)) * float2(1.03225803375244140625), float(gl_Layer) * 0.0322580635547637939453125, 0.0).xyz; + float3 _599; + if (_Globals.OutputDevice >= 3u) + { + float3 _591 = pow(_577, float3(0.0126833133399486541748046875)); + _599 = pow(fast::max(float3(0.0), _591 - float3(0.8359375)) / (float3(18.8515625) - (float3(18.6875) * _591)), float3(6.277394771575927734375)) * float3(10000.0); + } + else + { + _599 = (exp2((_577 - float3(0.434017598628997802734375)) * float3(14.0)) * float3(0.180000007152557373046875)) - float3(0.00266771926544606685638427734375); + } + float _602 = _Globals.WhiteTemp * 1.00055634975433349609375; + float _616 = (_602 <= 7000.0) ? (0.24406300485134124755859375 + ((99.1100006103515625 + ((2967800.0 - (4604438528.0 / _Globals.WhiteTemp)) / _602)) / _602)) : (0.23703999817371368408203125 + ((247.4799957275390625 + ((1901800.0 - (2005284352.0 / _Globals.WhiteTemp)) / _602)) / _602)); + float _633 = ((0.860117733478546142578125 + (0.00015411825734190642833709716796875 * _Globals.WhiteTemp)) + ((1.2864121856637211749330163002014e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)) / ((1.0 + (0.0008424202096648514270782470703125 * _Globals.WhiteTemp)) + ((7.0814513719597016461193561553955e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)); + float _644 = ((0.317398726940155029296875 + (4.25 * _Globals.WhiteTemp)) + ((4.2048167614439080352894961833954e-08 * _Globals.WhiteTemp) * _Globals.WhiteTemp)) / ((1.0 - (2.8974181986995972692966461181641e-05 * _Globals.WhiteTemp)) + ((1.6145605741257895715534687042236e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)); + float _649 = ((2.0 * _633) - (8.0 * _644)) + 4.0; + float2 _653 = float2((3.0 * _633) / _649, (2.0 * _644) / _649); + float2 _660 = normalize(float2(_633, _644)); + float _665 = _633 + (((-_660.y) * _Globals.WhiteTint) * 0.0500000007450580596923828125); + float _669 = _644 + ((_660.x * _Globals.WhiteTint) * 0.0500000007450580596923828125); + float _674 = ((2.0 * _665) - (8.0 * _669)) + 4.0; + float2 _680 = select(float2(_616, (_616 * (((-3.0) * _616) + 2.86999988555908203125)) - 0.2750000059604644775390625), _653, bool2(_Globals.WhiteTemp < 4000.0)) + (float2((3.0 * _665) / _674, (2.0 * _669) / _674) - _653); + float _681 = _680.x; + float _682 = _680.y; + float _683 = fast::max(_682, 1.0000000133514319600180897396058e-10); + float3 _685 = _391; + _685.x = _681 / _683; + float3 _686 = _685; + _686.y = 1.0; + float3 _690 = _686; + _690.z = ((1.0 - _681) - _682) / _683; + float3 _693 = _391; + _693.x = 0.950455963611602783203125; + float3 _694 = _693; + _694.y = 1.0; + float3 _696 = _694; + _696.z = 1.0890576839447021484375; + float3 _697 = _690 * float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)); + float3 _698 = _696 * float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)); + float3 _717 = (_599 * ((float3x3(float3(0.41245639324188232421875, 0.3575761020183563232421875, 0.180437505245208740234375), float3(0.21267290413379669189453125, 0.715152204036712646484375, 0.072175003588199615478515625), float3(0.01933390088379383087158203125, 0.119191996753215789794921875, 0.950304090976715087890625)) * ((float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)) * float3x3(float3(_698.x / _697.x, 0.0, 0.0), float3(0.0, _698.y / _697.y, 0.0), float3(0.0, 0.0, _698.z / _697.z))) * float3x3(float3(0.986992895603179931640625, -0.14705429971218109130859375, 0.15996269881725311279296875), float3(0.4323053061962127685546875, 0.518360316753387451171875, 0.049291200935840606689453125), float3(-0.00852870009839534759521484375, 0.0400427989661693572998046875, 0.968486726284027099609375)))) * float3x3(float3(3.2409698963165283203125, -1.53738319873809814453125, -0.4986107647418975830078125), float3(-0.96924364566802978515625, 1.875967502593994140625, 0.0415550582110881805419921875), float3(0.055630080401897430419921875, -0.2039769589900970458984375, 1.05697154998779296875)))) * _547; + float3 _745; + if (_Globals.ColorShadow_Tint2.w != 0.0) + { + float _724 = dot(_717, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625)); + float3 _727 = (_717 / float3(_724)) - float3(1.0); + _745 = mix(_717, _717 * (_549 * (float3x3(float3(0.544169127941131591796875, 0.23959259688854217529296875, 0.16669429838657379150390625), float3(0.23946559429168701171875, 0.702153027057647705078125, 0.058381401002407073974609375), float3(-0.0023439000360667705535888671875, 0.0361833982169628143310546875, 1.05521833896636962890625)) * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)))), float3((1.0 - exp2((-4.0) * dot(_727, _727))) * (1.0 - exp2((((-4.0) * _Globals.ExpandGamut) * _724) * _724)))); + } + else + { + _745 = _717; + } + float _746 = dot(_745, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625)); + float4 _751 = _Globals.ColorSaturationShadows * _Globals.ColorSaturation; + float4 _756 = _Globals.ColorContrastShadows * _Globals.ColorContrast; + float4 _761 = _Globals.ColorGammaShadows * _Globals.ColorGamma; + float4 _766 = _Globals.ColorGainShadows * _Globals.ColorGain; + float4 _771 = _Globals.ColorOffsetShadows + _Globals.ColorOffset; + float3 _772 = float3(_746); + float _804 = smoothstep(0.0, _Globals.ColorCorrectionShadowsMax, _746); + float4 _808 = _Globals.ColorSaturationHighlights * _Globals.ColorSaturation; + float4 _811 = _Globals.ColorContrastHighlights * _Globals.ColorContrast; + float4 _814 = _Globals.ColorGammaHighlights * _Globals.ColorGamma; + float4 _817 = _Globals.ColorGainHighlights * _Globals.ColorGain; + float4 _820 = _Globals.ColorOffsetHighlights + _Globals.ColorOffset; + float _852 = smoothstep(_Globals.ColorCorrectionHighlightsMin, 1.0, _746); + float4 _855 = _Globals.ColorSaturationMidtones * _Globals.ColorSaturation; + float4 _858 = _Globals.ColorContrastMidtones * _Globals.ColorContrast; + float4 _861 = _Globals.ColorGammaMidtones * _Globals.ColorGamma; + float4 _864 = _Globals.ColorGainMidtones * _Globals.ColorGain; + float4 _867 = _Globals.ColorOffsetMidtones + _Globals.ColorOffset; + float3 _905 = ((((pow(pow(fast::max(float3(0.0), mix(_772, _745, _751.xyz * float3(_751.w))) * float3(5.5555553436279296875), _756.xyz * float3(_756.w)) * float3(0.180000007152557373046875), float3(1.0) / (_761.xyz * float3(_761.w))) * (_766.xyz * float3(_766.w))) + (_771.xyz + float3(_771.w))) * float3(1.0 - _804)) + (((pow(pow(fast::max(float3(0.0), mix(_772, _745, _855.xyz * float3(_855.w))) * float3(5.5555553436279296875), _858.xyz * float3(_858.w)) * float3(0.180000007152557373046875), float3(1.0) / (_861.xyz * float3(_861.w))) * (_864.xyz * float3(_864.w))) + (_867.xyz + float3(_867.w))) * float3(_804 - _852))) + (((pow(pow(fast::max(float3(0.0), mix(_772, _745, _808.xyz * float3(_808.w))) * float3(5.5555553436279296875), _811.xyz * float3(_811.w)) * float3(0.180000007152557373046875), float3(1.0) / (_814.xyz * float3(_814.w))) * (_817.xyz * float3(_817.w))) + (_820.xyz + float3(_820.w))) * float3(_852)); + float3 _906 = _905 * _549; + float3 _914 = float3(_Globals.BlueCorrection); + float3 _916 = mix(_905, _905 * ((_551 * float3x3(float3(0.940437257289886474609375, -0.01830687932670116424560546875, 0.07786960899829864501953125), float3(0.008378696627914905548095703125, 0.82866001129150390625, 0.162961304187774658203125), float3(0.0005471261101774871349334716796875, -0.00088337459601461887359619140625, 1.00033628940582275390625))) * _550), _914) * _551; + float _917 = _916.x; + float _918 = _916.y; + float _920 = _916.z; + float _923 = fast::max(fast::max(_917, _918), _920); + float _928 = (fast::max(_923, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_917, _918), _920), 1.0000000133514319600180897396058e-10)) / fast::max(_923, 0.00999999977648258209228515625); + float _941 = ((_920 + _918) + _917) + (1.75 * sqrt(((_920 * (_920 - _918)) + (_918 * (_918 - _917))) + (_917 * (_917 - _920)))); + float _942 = _941 * 0.3333333432674407958984375; + float _943 = _928 - 0.4000000059604644775390625; + float _948 = fast::max(1.0 - abs(_943 * 2.5), 0.0); + float _956 = (1.0 + (float(int(sign(_943 * 5.0))) * (1.0 - (_948 * _948)))) * 0.02500000037252902984619140625; + float _969; + if (_942 <= 0.053333334624767303466796875) + { + _969 = _956; + } + else + { + float _968; + if (_942 >= 0.1599999964237213134765625) + { + _968 = 0.0; + } + else + { + _968 = _956 * ((0.23999999463558197021484375 / _941) - 0.5); + } + _969 = _968; + } + float3 _972 = _916 * float3(1.0 + _969); + float _973 = _972.x; + float _974 = _972.y; + float _976 = _972.z; + float _990; + if ((_973 == _974) && (_974 == _976)) + { + _990 = 0.0; + } + else + { + _990 = 57.2957763671875 * atan2(1.73205077648162841796875 * (_974 - _976), ((2.0 * _973) - _974) - _976); + } + float _995; + if (_990 < 0.0) + { + _995 = _990 + 360.0; + } + else + { + _995 = _990; + } + float _996 = fast::clamp(_995, 0.0, 360.0); + float _1001; + if (_996 > 180.0) + { + _1001 = _996 - 360.0; + } + else + { + _1001 = _996; + } + float _1005 = smoothstep(0.0, 1.0, 1.0 - abs(_1001 * 0.01481481455266475677490234375)); + float3 _1012 = _972; + _1012.x = _973 + ((((_1005 * _1005) * _928) * (0.02999999932944774627685546875 - _973)) * 0.180000007152557373046875); + float3 _1014 = fast::max(float3(0.0), _1012 * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375))); + float _1023 = (1.0 + _Globals.FilmBlackClip) - _Globals.FilmToe; + float _1026 = 1.0 + _Globals.FilmWhiteClip; + float _1029 = _1026 - _Globals.FilmShoulder; + float _1056; + if (_Globals.FilmToe > 0.800000011920928955078125) + { + _1056 = ((0.819999992847442626953125 - _Globals.FilmToe) / _Globals.FilmSlope) + (-0.744727432727813720703125); + } + else + { + float _1035 = (0.180000007152557373046875 + _Globals.FilmBlackClip) / _1023; + _1056 = (-0.744727432727813720703125) - ((0.5 * log(_1035 / (2.0 - _1035))) * (_1023 / _Globals.FilmSlope)); + } + float _1061 = ((1.0 - _Globals.FilmToe) / _Globals.FilmSlope) - _1056; + float _1063 = (_Globals.FilmShoulder / _Globals.FilmSlope) - _1061; + float3 _1067 = log(mix(float3(dot(_1014, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1014, float3(0.959999978542327880859375))) * float3(0.4342944622039794921875); + float3 _1071 = float3(_Globals.FilmSlope) * (_1067 + float3(_1061)); + float3 _1079 = float3(_1056); + float3 _1080 = _1067 - _1079; + float3 _1092 = float3(_1063); + float3 _1106 = fast::clamp(_1080 / float3(_1063 - _1056), float3(0.0), float3(1.0)); + float3 _1110 = select(_1106, float3(1.0) - _1106, bool3(_1063 < _1056)); + float3 _1115 = mix(select(_1071, float3(-_Globals.FilmBlackClip) + (float3(2.0 * _1023) / (float3(1.0) + exp(float3(((-2.0) * _Globals.FilmSlope) / _1023) * _1080))), _1067 < _1079), select(_1071, float3(_1026) - (float3(2.0 * _1029) / (float3(1.0) + exp(float3((2.0 * _Globals.FilmSlope) / _1029) * (_1067 - _1092)))), _1067 > _1092), ((float3(3.0) - (float3(2.0) * _1110)) * _1110) * _1110); + float3 _1119 = fast::max(float3(0.0), mix(float3(dot(_1115, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1115, float3(0.930000007152557373046875))); + float3 _1189; + if (_Globals.ColorShadow_Tint2.w == 0.0) + { + float3 _1131 = _391; + _1131.x = dot(_906, _Globals.ColorMatrixR_ColorCurveCd1.xyz); + float3 _1136 = _1131; + _1136.y = dot(_906, _Globals.ColorMatrixG_ColorCurveCd3Cm3.xyz); + float3 _1141 = _1136; + _1141.z = dot(_906, _Globals.ColorMatrixB_ColorCurveCm2.xyz); + float3 _1157 = fast::max(float3(0.0), _1141 * (_Globals.ColorShadow_Tint1.xyz + (_Globals.ColorShadow_Tint2.xyz * float3(1.0 / (dot(_906, _Globals.ColorShadow_Luma.xyz) + 1.0))))); + float3 _1162 = fast::max(float3(0.0), _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.xxx - _1157); + float3 _1164 = fast::max(_1157, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.zzz); + _1189 = ((((_1164 * _Globals.ColorCurve_Ch1_Ch2.xxx) + _Globals.ColorCurve_Ch1_Ch2.yyy) * (float3(1.0) / (_1164 + _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.www))) + ((fast::clamp(_1157, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.xxx, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.zzz) * _Globals.ColorMatrixB_ColorCurveCm2.www) + (((_1162 * _Globals.ColorMatrixR_ColorCurveCd1.www) * (float3(1.0) / (_1162 + _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.yyy))) + _Globals.ColorMatrixG_ColorCurveCd3Cm3.www))) - float3(0.00200000009499490261077880859375); + } + else + { + _1189 = fast::max(float3(0.0), mix(_1119, _1119 * ((_551 * float3x3(float3(1.06317996978759765625, 0.02339559979736804962158203125, -0.08657260239124298095703125), float3(-0.010633699595928192138671875, 1.2063200473785400390625, -0.1956900060176849365234375), float3(-0.0005908869788981974124908447265625, 0.00105247995816171169281005859375, 0.999538004398345947265625))) * _550), _914) * _549); + } + float3 _1218 = pow(fast::max(float3(0.0), mix((((float3(_Globals.MappingPolynomial.x) * (_1189 * _1189)) + (float3(_Globals.MappingPolynomial.y) * _1189)) + float3(_Globals.MappingPolynomial.z)) * float3(_Globals.ColorScale), _Globals.OverlayColor.xyz, float3(_Globals.OverlayColor.w))), float3(_Globals.InverseGamma.y)); + float3 _3001; + if (_Globals.OutputDevice == 0u) + { + float _2961 = _1218.x; + float _2973; + for (;;) + { + if (_2961 < 0.00313066993840038776397705078125) + { + _2973 = _2961 * 12.9200000762939453125; + break; + } + _2973 = (pow(_2961, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _2974 = _1218.y; + float _2986; + for (;;) + { + if (_2974 < 0.00313066993840038776397705078125) + { + _2986 = _2974 * 12.9200000762939453125; + break; + } + _2986 = (pow(_2974, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _2987 = _1218.z; + float _2999; + for (;;) + { + if (_2987 < 0.00313066993840038776397705078125) + { + _2999 = _2987 * 12.9200000762939453125; + break; + } + _2999 = (pow(_2987, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + _3001 = float3(_2973, _2986, _2999); + } + else + { + float3 _2960; + if (_Globals.OutputDevice == 1u) + { + float3 _2953 = fast::max(float3(6.1035199905745685100555419921875e-05), (_1218 * _547) * _576); + _2960 = fast::min(_2953 * float3(4.5), (pow(fast::max(_2953, float3(0.017999999225139617919921875)), float3(0.449999988079071044921875)) * float3(1.09899997711181640625)) - float3(0.098999999463558197021484375)); + } + else + { + float3 _2950; + if ((_Globals.OutputDevice == 3u) || (_Globals.OutputDevice == 5u)) + { + float3 _2100 = (_906 * float3(1.5)) * (_546 * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625))); + float _2101 = _2100.x; + float _2102 = _2100.y; + float _2104 = _2100.z; + float _2107 = fast::max(fast::max(_2101, _2102), _2104); + float _2112 = (fast::max(_2107, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_2101, _2102), _2104), 1.0000000133514319600180897396058e-10)) / fast::max(_2107, 0.00999999977648258209228515625); + float _2125 = ((_2104 + _2102) + _2101) + (1.75 * sqrt(((_2104 * (_2104 - _2102)) + (_2102 * (_2102 - _2101))) + (_2101 * (_2101 - _2104)))); + float _2126 = _2125 * 0.3333333432674407958984375; + float _2127 = _2112 - 0.4000000059604644775390625; + float _2132 = fast::max(1.0 - abs(_2127 * 2.5), 0.0); + float _2140 = (1.0 + (float(int(sign(_2127 * 5.0))) * (1.0 - (_2132 * _2132)))) * 0.02500000037252902984619140625; + float _2153; + if (_2126 <= 0.053333334624767303466796875) + { + _2153 = _2140; + } + else + { + float _2152; + if (_2126 >= 0.1599999964237213134765625) + { + _2152 = 0.0; + } + else + { + _2152 = _2140 * ((0.23999999463558197021484375 / _2125) - 0.5); + } + _2153 = _2152; + } + float3 _2156 = _2100 * float3(1.0 + _2153); + float _2157 = _2156.x; + float _2158 = _2156.y; + float _2160 = _2156.z; + float _2174; + if ((_2157 == _2158) && (_2158 == _2160)) + { + _2174 = 0.0; + } + else + { + _2174 = 57.2957763671875 * atan2(1.73205077648162841796875 * (_2158 - _2160), ((2.0 * _2157) - _2158) - _2160); + } + float _2179; + if (_2174 < 0.0) + { + _2179 = _2174 + 360.0; + } + else + { + _2179 = _2174; + } + float _2180 = fast::clamp(_2179, 0.0, 360.0); + float _2185; + if (_2180 > 180.0) + { + _2185 = _2180 - 360.0; + } + else + { + _2185 = _2180; + } + float _2235; + if ((_2185 > (-67.5)) && (_2185 < 67.5)) + { + float _2192 = (_2185 - (-67.5)) * 0.0296296291053295135498046875; + int _2193 = int(_2192); + float _2195 = _2192 - float(_2193); + float _2196 = _2195 * _2195; + float _2197 = _2196 * _2195; + float _2234; + if (_2193 == 3) + { + _2234 = (((_2197 * (-0.16666667163372039794921875)) + (_2196 * 0.5)) + (_2195 * (-0.5))) + 0.16666667163372039794921875; + } + else + { + float _2227; + if (_2193 == 2) + { + _2227 = ((_2197 * 0.5) + (_2196 * (-1.0))) + 0.666666686534881591796875; + } + else + { + float _2222; + if (_2193 == 1) + { + _2222 = (((_2197 * (-0.5)) + (_2196 * 0.5)) + (_2195 * 0.5)) + 0.16666667163372039794921875; + } + else + { + float _2215; + if (_2193 == 0) + { + _2215 = _2197 * 0.16666667163372039794921875; + } + else + { + _2215 = 0.0; + } + _2222 = _2215; + } + _2227 = _2222; + } + _2234 = _2227; + } + _2235 = _2234; + } + else + { + _2235 = 0.0; + } + float3 _2242 = _2156; + _2242.x = _2157 + ((((_2235 * 1.5) * _2112) * (0.02999999932944774627685546875 - _2157)) * 0.180000007152557373046875); + float3 _2245 = fast::clamp(fast::clamp(_2242, float3(0.0), float3(65535.0)) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)), float3(0.0), float3(65535.0)); + float3 _2248 = mix(float3(dot(_2245, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _2245, float3(0.959999978542327880859375)); + float _2249 = _2248.x; + float _2258 = log((_2249 <= 0.0) ? 6.103515625e-05 : _2249) * 0.4342944622039794921875; + float _2327; + if (_2258 <= (-5.2601776123046875)) + { + _2327 = -4.0; + } + else + { + float _2324; + if ((_2258 > (-5.2601776123046875)) && (_2258 < (-0.744727432727813720703125))) + { + float _2307 = (_2258 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _2308 = int(_2307); + float _2310 = _2307 - float(_2308); + _2324 = dot(float3(_2310 * _2310, _2310, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_2308], _475[_2308 + 1], _475[_2308 + 2])); + } + else + { + float _2303; + if ((_2258 >= (-0.744727432727813720703125)) && (_2258 < 4.673812389373779296875)) + { + float _2286 = (_2258 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _2287 = int(_2286); + float _2289 = _2286 - float(_2287); + _2303 = dot(float3(_2289 * _2289, _2289, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2287], _476[_2287 + 1], _476[_2287 + 2])); + } + else + { + _2303 = 4.0; + } + _2324 = _2303; + } + _2327 = _2324; + } + float3 _2329 = _391; + _2329.x = pow(10.0, _2327); + float _2330 = _2248.y; + float _2334 = log((_2330 <= 0.0) ? 6.103515625e-05 : _2330) * 0.4342944622039794921875; + float _2401; + if (_2334 <= (-5.2601776123046875)) + { + _2401 = -4.0; + } + else + { + float _2398; + if ((_2334 > (-5.2601776123046875)) && (_2334 < (-0.744727432727813720703125))) + { + float _2381 = (_2334 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _2382 = int(_2381); + float _2384 = _2381 - float(_2382); + _2398 = dot(float3(_2384 * _2384, _2384, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_2382], _475[_2382 + 1], _475[_2382 + 2])); + } + else + { + float _2377; + if ((_2334 >= (-0.744727432727813720703125)) && (_2334 < 4.673812389373779296875)) + { + float _2360 = (_2334 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _2361 = int(_2360); + float _2363 = _2360 - float(_2361); + _2377 = dot(float3(_2363 * _2363, _2363, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2361], _476[_2361 + 1], _476[_2361 + 2])); + } + else + { + _2377 = 4.0; + } + _2398 = _2377; + } + _2401 = _2398; + } + float3 _2403 = _2329; + _2403.y = pow(10.0, _2401); + float _2404 = _2248.z; + float _2408 = log((_2404 <= 0.0) ? 6.103515625e-05 : _2404) * 0.4342944622039794921875; + float _2475; + if (_2408 <= (-5.2601776123046875)) + { + _2475 = -4.0; + } + else + { + float _2472; + if ((_2408 > (-5.2601776123046875)) && (_2408 < (-0.744727432727813720703125))) + { + float _2455 = (_2408 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _2456 = int(_2455); + float _2458 = _2455 - float(_2456); + _2472 = dot(float3(_2458 * _2458, _2458, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_2456], _475[_2456 + 1], _475[_2456 + 2])); + } + else + { + float _2451; + if ((_2408 >= (-0.744727432727813720703125)) && (_2408 < 4.673812389373779296875)) + { + float _2434 = (_2408 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _2435 = int(_2434); + float _2437 = _2434 - float(_2435); + _2451 = dot(float3(_2437 * _2437, _2437, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2435], _476[_2435 + 1], _476[_2435 + 2])); + } + else + { + _2451 = 4.0; + } + _2472 = _2451; + } + _2475 = _2472; + } + float3 _2477 = _2403; + _2477.z = pow(10.0, _2475); + float3 _2479 = (_2477 * float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625))) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)); + float _2612 = pow(10.0, (float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(-0.718548238277435302734375, 2.0810306072235107421875, 3.66812419891357421875)).z); + float _2684 = pow(10.0, dot(float3(0.4444443881511688232421875, 0.66666662693023681640625, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(2.0810306072235107421875, 3.66812419891357421875, 4.0))); + float _2685 = _2479.x; + float _2688 = log((_2685 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2685); + float _2689 = _2688 * 0.4342944622039794921875; + float _2690 = log(pow(10.0, dot(float3(0.3600003719329833984375, 0.600000321865081787109375, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(-4.0, -4.0, -3.1573765277862548828125)))); + float _2691 = _2690 * 0.4342944622039794921875; + float _2768; + if (_2689 <= _2691) + { + _2768 = (_2688 * 1.3028833866119384765625) + ((-4.0) - (_2690 * 1.3028833866119384765625)); + } + else + { + float _2698 = log(_2612) * 0.4342944622039794921875; + float _2760; + if ((_2689 > _2691) && (_2689 < _2698)) + { + float _2743 = (7.0 * (_2689 - _2691)) / (_2698 - _2691); + int _2744 = int(_2743); + float _2746 = _2743 - float(_2744); + _2760 = dot(float3(_2746 * _2746, _2746, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_479[_2744], _479[_2744 + 1], _479[_2744 + 2])); + } + else + { + float _2705 = log(_2684); + float _2706 = _2705 * 0.4342944622039794921875; + float _2739; + if ((_2689 >= _2698) && (_2689 < _2706)) + { + float _2722 = (7.0 * (_2689 - _2698)) / (_2706 - _2698); + int _2723 = int(_2722); + float _2725 = _2722 - float(_2723); + _2739 = dot(float3(_2725 * _2725, _2725, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_480[_2723], _480[_2723 + 1], _480[_2723 + 2])); + } + else + { + _2739 = (_2688 * 0.026057668030261993408203125) + (3.0 - (_2705 * 0.026057668030261993408203125)); + } + _2760 = _2739; + } + _2768 = _2760; + } + float3 _2770 = _391; + _2770.x = pow(10.0, _2768); + float _2771 = _2479.y; + float _2774 = log((_2771 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2771); + float _2775 = _2774 * 0.4342944622039794921875; + float _2852; + if (_2775 <= _2691) + { + _2852 = (_2774 * 1.3028833866119384765625) + ((-4.0) - (_2690 * 1.3028833866119384765625)); + } + else + { + float _2782 = log(_2612) * 0.4342944622039794921875; + float _2844; + if ((_2775 > _2691) && (_2775 < _2782)) + { + float _2827 = (7.0 * (_2775 - _2691)) / (_2782 - _2691); + int _2828 = int(_2827); + float _2830 = _2827 - float(_2828); + _2844 = dot(float3(_2830 * _2830, _2830, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_479[_2828], _479[_2828 + 1], _479[_2828 + 2])); + } + else + { + float _2789 = log(_2684); + float _2790 = _2789 * 0.4342944622039794921875; + float _2823; + if ((_2775 >= _2782) && (_2775 < _2790)) + { + float _2806 = (7.0 * (_2775 - _2782)) / (_2790 - _2782); + int _2807 = int(_2806); + float _2809 = _2806 - float(_2807); + _2823 = dot(float3(_2809 * _2809, _2809, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_480[_2807], _480[_2807 + 1], _480[_2807 + 2])); + } + else + { + _2823 = (_2774 * 0.026057668030261993408203125) + (3.0 - (_2789 * 0.026057668030261993408203125)); + } + _2844 = _2823; + } + _2852 = _2844; + } + float3 _2854 = _2770; + _2854.y = pow(10.0, _2852); + float _2855 = _2479.z; + float _2858 = log((_2855 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2855); + float _2859 = _2858 * 0.4342944622039794921875; + float _2936; + if (_2859 <= _2691) + { + _2936 = (_2858 * 1.3028833866119384765625) + ((-4.0) - (_2690 * 1.3028833866119384765625)); + } + else + { + float _2866 = log(_2612) * 0.4342944622039794921875; + float _2928; + if ((_2859 > _2691) && (_2859 < _2866)) + { + float _2911 = (7.0 * (_2859 - _2691)) / (_2866 - _2691); + int _2912 = int(_2911); + float _2914 = _2911 - float(_2912); + _2928 = dot(float3(_2914 * _2914, _2914, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_479[_2912], _479[_2912 + 1], _479[_2912 + 2])); + } + else + { + float _2873 = log(_2684); + float _2874 = _2873 * 0.4342944622039794921875; + float _2907; + if ((_2859 >= _2866) && (_2859 < _2874)) + { + float _2890 = (7.0 * (_2859 - _2866)) / (_2874 - _2866); + int _2891 = int(_2890); + float _2893 = _2890 - float(_2891); + _2907 = dot(float3(_2893 * _2893, _2893, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_480[_2891], _480[_2891 + 1], _480[_2891 + 2])); + } + else + { + _2907 = (_2858 * 0.026057668030261993408203125) + (3.0 - (_2873 * 0.026057668030261993408203125)); + } + _2928 = _2907; + } + _2936 = _2928; + } + float3 _2938 = _2854; + _2938.z = pow(10.0, _2936); + float3 _2942 = pow(((_2938 - float3(3.5073844628641381859779357910156e-05)) * _576) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _2950 = pow((float3(0.8359375) + (float3(18.8515625) * _2942)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _2942))), float3(78.84375)); + } + else + { + float3 _2097; + if ((_Globals.OutputDevice == 4u) || (_Globals.OutputDevice == 6u)) + { + float3 _1263 = (_906 * float3(1.5)) * (_546 * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625))); + float _1264 = _1263.x; + float _1265 = _1263.y; + float _1267 = _1263.z; + float _1270 = fast::max(fast::max(_1264, _1265), _1267); + float _1275 = (fast::max(_1270, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_1264, _1265), _1267), 1.0000000133514319600180897396058e-10)) / fast::max(_1270, 0.00999999977648258209228515625); + float _1288 = ((_1267 + _1265) + _1264) + (1.75 * sqrt(((_1267 * (_1267 - _1265)) + (_1265 * (_1265 - _1264))) + (_1264 * (_1264 - _1267)))); + float _1289 = _1288 * 0.3333333432674407958984375; + float _1290 = _1275 - 0.4000000059604644775390625; + float _1295 = fast::max(1.0 - abs(_1290 * 2.5), 0.0); + float _1303 = (1.0 + (float(int(sign(_1290 * 5.0))) * (1.0 - (_1295 * _1295)))) * 0.02500000037252902984619140625; + float _1316; + if (_1289 <= 0.053333334624767303466796875) + { + _1316 = _1303; + } + else + { + float _1315; + if (_1289 >= 0.1599999964237213134765625) + { + _1315 = 0.0; + } + else + { + _1315 = _1303 * ((0.23999999463558197021484375 / _1288) - 0.5); + } + _1316 = _1315; + } + float3 _1319 = _1263 * float3(1.0 + _1316); + float _1320 = _1319.x; + float _1321 = _1319.y; + float _1323 = _1319.z; + float _1337; + if ((_1320 == _1321) && (_1321 == _1323)) + { + _1337 = 0.0; + } + else + { + _1337 = 57.2957763671875 * atan2(1.73205077648162841796875 * (_1321 - _1323), ((2.0 * _1320) - _1321) - _1323); + } + float _1342; + if (_1337 < 0.0) + { + _1342 = _1337 + 360.0; + } + else + { + _1342 = _1337; + } + float _1343 = fast::clamp(_1342, 0.0, 360.0); + float _1348; + if (_1343 > 180.0) + { + _1348 = _1343 - 360.0; + } + else + { + _1348 = _1343; + } + float _1398; + if ((_1348 > (-67.5)) && (_1348 < 67.5)) + { + float _1355 = (_1348 - (-67.5)) * 0.0296296291053295135498046875; + int _1356 = int(_1355); + float _1358 = _1355 - float(_1356); + float _1359 = _1358 * _1358; + float _1360 = _1359 * _1358; + float _1397; + if (_1356 == 3) + { + _1397 = (((_1360 * (-0.16666667163372039794921875)) + (_1359 * 0.5)) + (_1358 * (-0.5))) + 0.16666667163372039794921875; + } + else + { + float _1390; + if (_1356 == 2) + { + _1390 = ((_1360 * 0.5) + (_1359 * (-1.0))) + 0.666666686534881591796875; + } + else + { + float _1385; + if (_1356 == 1) + { + _1385 = (((_1360 * (-0.5)) + (_1359 * 0.5)) + (_1358 * 0.5)) + 0.16666667163372039794921875; + } + else + { + float _1378; + if (_1356 == 0) + { + _1378 = _1360 * 0.16666667163372039794921875; + } + else + { + _1378 = 0.0; + } + _1385 = _1378; + } + _1390 = _1385; + } + _1397 = _1390; + } + _1398 = _1397; + } + else + { + _1398 = 0.0; + } + float3 _1405 = _1319; + _1405.x = _1320 + ((((_1398 * 1.5) * _1275) * (0.02999999932944774627685546875 - _1320)) * 0.180000007152557373046875); + float3 _1408 = fast::clamp(fast::clamp(_1405, float3(0.0), float3(65535.0)) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)), float3(0.0), float3(65535.0)); + float3 _1411 = mix(float3(dot(_1408, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1408, float3(0.959999978542327880859375)); + float _1412 = _1411.x; + float _1421 = log((_1412 <= 0.0) ? 6.103515625e-05 : _1412) * 0.4342944622039794921875; + float _1490; + if (_1421 <= (-5.2601776123046875)) + { + _1490 = -4.0; + } + else + { + float _1487; + if ((_1421 > (-5.2601776123046875)) && (_1421 < (-0.744727432727813720703125))) + { + float _1470 = (_1421 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _1471 = int(_1470); + float _1473 = _1470 - float(_1471); + _1487 = dot(float3(_1473 * _1473, _1473, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_1471], _475[_1471 + 1], _475[_1471 + 2])); + } + else + { + float _1466; + if ((_1421 >= (-0.744727432727813720703125)) && (_1421 < 4.673812389373779296875)) + { + float _1449 = (_1421 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _1450 = int(_1449); + float _1452 = _1449 - float(_1450); + _1466 = dot(float3(_1452 * _1452, _1452, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1450], _476[_1450 + 1], _476[_1450 + 2])); + } + else + { + _1466 = 4.0; + } + _1487 = _1466; + } + _1490 = _1487; + } + float3 _1492 = _391; + _1492.x = pow(10.0, _1490); + float _1493 = _1411.y; + float _1497 = log((_1493 <= 0.0) ? 6.103515625e-05 : _1493) * 0.4342944622039794921875; + float _1564; + if (_1497 <= (-5.2601776123046875)) + { + _1564 = -4.0; + } + else + { + float _1561; + if ((_1497 > (-5.2601776123046875)) && (_1497 < (-0.744727432727813720703125))) + { + float _1544 = (_1497 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _1545 = int(_1544); + float _1547 = _1544 - float(_1545); + _1561 = dot(float3(_1547 * _1547, _1547, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_1545], _475[_1545 + 1], _475[_1545 + 2])); + } + else + { + float _1540; + if ((_1497 >= (-0.744727432727813720703125)) && (_1497 < 4.673812389373779296875)) + { + float _1523 = (_1497 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _1524 = int(_1523); + float _1526 = _1523 - float(_1524); + _1540 = dot(float3(_1526 * _1526, _1526, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1524], _476[_1524 + 1], _476[_1524 + 2])); + } + else + { + _1540 = 4.0; + } + _1561 = _1540; + } + _1564 = _1561; + } + float3 _1566 = _1492; + _1566.y = pow(10.0, _1564); + float _1567 = _1411.z; + float _1571 = log((_1567 <= 0.0) ? 6.103515625e-05 : _1567) * 0.4342944622039794921875; + float _1638; + if (_1571 <= (-5.2601776123046875)) + { + _1638 = -4.0; + } + else + { + float _1635; + if ((_1571 > (-5.2601776123046875)) && (_1571 < (-0.744727432727813720703125))) + { + float _1618 = (_1571 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _1619 = int(_1618); + float _1621 = _1618 - float(_1619); + _1635 = dot(float3(_1621 * _1621, _1621, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_1619], _475[_1619 + 1], _475[_1619 + 2])); + } + else + { + float _1614; + if ((_1571 >= (-0.744727432727813720703125)) && (_1571 < 4.673812389373779296875)) + { + float _1597 = (_1571 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _1598 = int(_1597); + float _1600 = _1597 - float(_1598); + _1614 = dot(float3(_1600 * _1600, _1600, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1598], _476[_1598 + 1], _476[_1598 + 2])); + } + else + { + _1614 = 4.0; + } + _1635 = _1614; + } + _1638 = _1635; + } + float3 _1640 = _1566; + _1640.z = pow(10.0, _1638); + float3 _1642 = (_1640 * float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625))) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)); + float _1775 = pow(10.0, (float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(-0.718548238277435302734375, 2.0810306072235107421875, 3.66812419891357421875)).z); + float _1847 = pow(10.0, dot(float3(0.69444429874420166015625, 0.8333332538604736328125, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(2.0810306072235107421875, 3.66812419891357421875, 4.0))); + float _1848 = _1642.x; + float _1851 = log((_1848 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _1848); + float _1852 = _1851 * 0.4342944622039794921875; + float _1854 = log(pow(10.0, dot(float3(0.3600003719329833984375, 0.600000321865081787109375, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(-4.0, -4.0, -3.1573765277862548828125)))) * 0.4342944622039794921875; + float _1926; + if (_1852 <= _1854) + { + _1926 = -2.3010299205780029296875; + } + else + { + float _1861 = log(_1775) * 0.4342944622039794921875; + float _1923; + if ((_1852 > _1854) && (_1852 < _1861)) + { + float _1906 = (7.0 * (_1852 - _1854)) / (_1861 - _1854); + int _1907 = int(_1906); + float _1909 = _1906 - float(_1907); + _1923 = dot(float3(_1909 * _1909, _1909, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_482[_1907], _482[_1907 + 1], _482[_1907 + 2])); + } + else + { + float _1868 = log(_1847); + float _1869 = _1868 * 0.4342944622039794921875; + float _1902; + if ((_1852 >= _1861) && (_1852 < _1869)) + { + float _1885 = (7.0 * (_1852 - _1861)) / (_1869 - _1861); + int _1886 = int(_1885); + float _1888 = _1885 - float(_1886); + _1902 = dot(float3(_1888 * _1888, _1888, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_483[_1886], _483[_1886 + 1], _483[_1886 + 2])); + } + else + { + _1902 = (_1851 * 0.05211533606052398681640625) + (3.3010299205780029296875 - (_1868 * 0.05211533606052398681640625)); + } + _1923 = _1902; + } + _1926 = _1923; + } + float3 _1928 = _391; + _1928.x = pow(10.0, _1926); + float _1929 = _1642.y; + float _1932 = log((_1929 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _1929); + float _1933 = _1932 * 0.4342944622039794921875; + float _2005; + if (_1933 <= _1854) + { + _2005 = -2.3010299205780029296875; + } + else + { + float _1940 = log(_1775) * 0.4342944622039794921875; + float _2002; + if ((_1933 > _1854) && (_1933 < _1940)) + { + float _1985 = (7.0 * (_1933 - _1854)) / (_1940 - _1854); + int _1986 = int(_1985); + float _1988 = _1985 - float(_1986); + _2002 = dot(float3(_1988 * _1988, _1988, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_482[_1986], _482[_1986 + 1], _482[_1986 + 2])); + } + else + { + float _1947 = log(_1847); + float _1948 = _1947 * 0.4342944622039794921875; + float _1981; + if ((_1933 >= _1940) && (_1933 < _1948)) + { + float _1964 = (7.0 * (_1933 - _1940)) / (_1948 - _1940); + int _1965 = int(_1964); + float _1967 = _1964 - float(_1965); + _1981 = dot(float3(_1967 * _1967, _1967, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_483[_1965], _483[_1965 + 1], _483[_1965 + 2])); + } + else + { + _1981 = (_1932 * 0.05211533606052398681640625) + (3.3010299205780029296875 - (_1947 * 0.05211533606052398681640625)); + } + _2002 = _1981; + } + _2005 = _2002; + } + float3 _2007 = _1928; + _2007.y = pow(10.0, _2005); + float _2008 = _1642.z; + float _2011 = log((_2008 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2008); + float _2012 = _2011 * 0.4342944622039794921875; + float _2084; + if (_2012 <= _1854) + { + _2084 = -2.3010299205780029296875; + } + else + { + float _2019 = log(_1775) * 0.4342944622039794921875; + float _2081; + if ((_2012 > _1854) && (_2012 < _2019)) + { + float _2064 = (7.0 * (_2012 - _1854)) / (_2019 - _1854); + int _2065 = int(_2064); + float _2067 = _2064 - float(_2065); + _2081 = dot(float3(_2067 * _2067, _2067, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_482[_2065], _482[_2065 + 1], _482[_2065 + 2])); + } + else + { + float _2026 = log(_1847); + float _2027 = _2026 * 0.4342944622039794921875; + float _2060; + if ((_2012 >= _2019) && (_2012 < _2027)) + { + float _2043 = (7.0 * (_2012 - _2019)) / (_2027 - _2019); + int _2044 = int(_2043); + float _2046 = _2043 - float(_2044); + _2060 = dot(float3(_2046 * _2046, _2046, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_483[_2044], _483[_2044 + 1], _483[_2044 + 2])); + } + else + { + _2060 = (_2011 * 0.05211533606052398681640625) + (3.3010299205780029296875 - (_2026 * 0.05211533606052398681640625)); + } + _2081 = _2060; + } + _2084 = _2081; + } + float3 _2086 = _2007; + _2086.z = pow(10.0, _2084); + float3 _2089 = pow((_2086 * _576) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _2097 = pow((float3(0.8359375) + (float3(18.8515625) * _2089)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _2089))), float3(78.84375)); + } + else + { + float3 _1260; + if (_Globals.OutputDevice == 7u) + { + float3 _1252 = pow(((_906 * _547) * _576) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _1260 = pow((float3(0.8359375) + (float3(18.8515625) * _1252)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _1252))), float3(78.84375)); + } + else + { + _1260 = pow((_1218 * _547) * _576, float3(_Globals.InverseGamma.z)); + } + _2097 = _1260; + } + _2950 = _2097; + } + _2960 = _2950; + } + _3001 = _2960; + } + float3 _3002 = _3001 * float3(0.95238101482391357421875); + float4 _3004 = float4(_3002.x, _3002.y, _3002.z, float4(0.0).w); + _3004.w = 0.0; + out.out_var_SV_Target0 = _3004; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag new file mode 100644 index 0000000..2926c32 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag @@ -0,0 +1,1190 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_Globals +{ + float4 MappingPolynomial; + float3 InverseGamma; + float4 ColorMatrixR_ColorCurveCd1; + float4 ColorMatrixG_ColorCurveCd3Cm3; + float4 ColorMatrixB_ColorCurveCm2; + float4 ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3; + float4 ColorCurve_Ch1_Ch2; + float4 ColorShadow_Luma; + float4 ColorShadow_Tint1; + float4 ColorShadow_Tint2; + float FilmSlope; + float FilmToe; + float FilmShoulder; + float FilmBlackClip; + float FilmWhiteClip; + float4 LUTWeights[5]; + float3 ColorScale; + float4 OverlayColor; + float WhiteTemp; + float WhiteTint; + float4 ColorSaturation; + float4 ColorContrast; + float4 ColorGamma; + float4 ColorGain; + float4 ColorOffset; + float4 ColorSaturationShadows; + float4 ColorContrastShadows; + float4 ColorGammaShadows; + float4 ColorGainShadows; + float4 ColorOffsetShadows; + float4 ColorSaturationMidtones; + float4 ColorContrastMidtones; + float4 ColorGammaMidtones; + float4 ColorGainMidtones; + float4 ColorOffsetMidtones; + float4 ColorSaturationHighlights; + float4 ColorContrastHighlights; + float4 ColorGammaHighlights; + float4 ColorGainHighlights; + float4 ColorOffsetHighlights; + float ColorCorrectionShadowsMax; + float ColorCorrectionHighlightsMin; + uint OutputDevice; + uint OutputGamut; + float BlueCorrection; + float ExpandGamut; +}; + +constant spvUnsafeArray _499 = spvUnsafeArray({ -4.0, -4.0, -3.1573765277862548828125, -0.485249996185302734375, 1.84773242473602294921875, 1.84773242473602294921875 }); +constant spvUnsafeArray _500 = spvUnsafeArray({ -0.718548238277435302734375, 2.0810306072235107421875, 3.66812419891357421875, 4.0, 4.0, 4.0 }); +constant spvUnsafeArray _503 = spvUnsafeArray({ -4.97062206268310546875, -3.0293781757354736328125, -2.1261999607086181640625, -1.5104999542236328125, -1.0578000545501708984375, -0.4668000042438507080078125, 0.11937999725341796875, 0.7088134288787841796875, 1.2911865711212158203125, 1.2911865711212158203125 }); +constant spvUnsafeArray _504 = spvUnsafeArray({ 0.80891323089599609375, 1.19108676910400390625, 1.5683000087738037109375, 1.94830000400543212890625, 2.308300018310546875, 2.63840007781982421875, 2.85949993133544921875, 2.9872608184814453125, 3.0127391815185546875, 3.0127391815185546875 }); +constant spvUnsafeArray _506 = spvUnsafeArray({ -2.3010299205780029296875, -2.3010299205780029296875, -1.9312000274658203125, -1.5204999446868896484375, -1.0578000545501708984375, -0.4668000042438507080078125, 0.11937999725341796875, 0.7088134288787841796875, 1.2911865711212158203125, 1.2911865711212158203125 }); +constant spvUnsafeArray _507 = spvUnsafeArray({ 0.801995217800140380859375, 1.19800484180450439453125, 1.5943000316619873046875, 1.99730002880096435546875, 2.3782999515533447265625, 2.7683999538421630859375, 3.0515000820159912109375, 3.2746293544769287109375, 3.32743072509765625, 3.32743072509765625 }); + +constant float3 _523 = {}; +constant float3 _3265 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0), center_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_Globals& _Globals [[buffer(0)]], texture2d Texture1 [[texture(0)]], sampler Texture1Sampler [[sampler(0)]], uint gl_Layer [[render_target_array_index]]) +{ + main0_out out = {}; + float3x3 _572 = float3x3(float3(0.41245639324188232421875, 0.3575761020183563232421875, 0.180437505245208740234375), float3(0.21267290413379669189453125, 0.715152204036712646484375, 0.072175003588199615478515625), float3(0.01933390088379383087158203125, 0.119191996753215789794921875, 0.950304090976715087890625)) * float3x3(float3(1.01303005218505859375, 0.0061053098179399967193603515625, -0.014971000142395496368408203125), float3(0.0076982299797236919403076171875, 0.99816501140594482421875, -0.005032029934227466583251953125), float3(-0.0028413101099431514739990234375, 0.0046851597726345062255859375, 0.92450702190399169921875)); + float3x3 _573 = _572 * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)); + float3x3 _574 = float3x3(float3(0.662454187870025634765625, 0.1340042054653167724609375, 0.1561876833438873291015625), float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625), float3(-0.0055746496655046939849853515625, 0.0040607335977256298065185546875, 1.01033914089202880859375)) * float3x3(float3(0.98722398281097412109375, -0.0061132698319852352142333984375, 0.01595330052077770233154296875), float3(-0.007598360069096088409423828125, 1.00186002254486083984375, 0.0053300200961530208587646484375), float3(0.003072570078074932098388671875, -0.0050959498621523380279541015625, 1.0816800594329833984375)); + float3x3 _575 = _574 * float3x3(float3(3.2409698963165283203125, -1.53738319873809814453125, -0.4986107647418975830078125), float3(-0.96924364566802978515625, 1.875967502593994140625, 0.0415550582110881805419921875), float3(0.055630080401897430419921875, -0.2039769589900970458984375, 1.05697154998779296875)); + float3x3 _576 = float3x3(float3(0.952552378177642822265625, 0.0, 9.25), float3(0.3439664542675018310546875, 0.728166103363037109375, -0.07213254272937774658203125), float3(0.0, 0.0, 1.00882518291473388671875)) * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)); + float3x3 _577 = float3x3(float3(0.662454187870025634765625, 0.1340042054653167724609375, 0.1561876833438873291015625), float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625), float3(-0.0055746496655046939849853515625, 0.0040607335977256298065185546875, 1.01033914089202880859375)) * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625)); + float3x3 _602; + for (;;) + { + if (_Globals.OutputGamut == 1u) + { + _602 = _574 * float3x3(float3(2.493396282196044921875, -0.931345880031585693359375, -0.4026944935321807861328125), float3(-0.829486787319183349609375, 1.76265966892242431640625, 0.02362460084259510040283203125), float3(0.0358506999909877777099609375, -0.076182700693607330322265625, 0.957014024257659912109375)); + break; + } + else + { + if (_Globals.OutputGamut == 2u) + { + _602 = _574 * float3x3(float3(1.71660840511322021484375, -0.3556621074676513671875, -0.253360092639923095703125), float3(-0.666682898998260498046875, 1.61647760868072509765625, 0.01576850004494190216064453125), float3(0.017642199993133544921875, -0.04277630150318145751953125, 0.94222867488861083984375)); + break; + } + else + { + if (_Globals.OutputGamut == 3u) + { + _602 = float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625)); + break; + } + else + { + if (_Globals.OutputGamut == 4u) + { + _602 = float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 1.0, 0.0), float3(0.0, 0.0, 1.0)); + break; + } + else + { + _602 = _575; + break; + } + } + } + } + } + float3 _603 = float4((in.in_var_TEXCOORD0 - float2(0.015625)) * float2(1.03225803375244140625), float(gl_Layer) * 0.0322580635547637939453125, 0.0).xyz; + float3 _625; + if (_Globals.OutputDevice >= 3u) + { + float3 _617 = pow(_603, float3(0.0126833133399486541748046875)); + _625 = pow(fast::max(float3(0.0), _617 - float3(0.8359375)) / (float3(18.8515625) - (float3(18.6875) * _617)), float3(6.277394771575927734375)) * float3(10000.0); + } + else + { + _625 = (exp2((_603 - float3(0.434017598628997802734375)) * float3(14.0)) * float3(0.180000007152557373046875)) - float3(0.00266771926544606685638427734375); + } + float _628 = _Globals.WhiteTemp * 1.00055634975433349609375; + float _642 = (_628 <= 7000.0) ? (0.24406300485134124755859375 + ((99.1100006103515625 + ((2967800.0 - (4604438528.0 / _Globals.WhiteTemp)) / _628)) / _628)) : (0.23703999817371368408203125 + ((247.4799957275390625 + ((1901800.0 - (2005284352.0 / _Globals.WhiteTemp)) / _628)) / _628)); + float _659 = ((0.860117733478546142578125 + (0.00015411825734190642833709716796875 * _Globals.WhiteTemp)) + ((1.2864121856637211749330163002014e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)) / ((1.0 + (0.0008424202096648514270782470703125 * _Globals.WhiteTemp)) + ((7.0814513719597016461193561553955e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)); + float _670 = ((0.317398726940155029296875 + (4.25 * _Globals.WhiteTemp)) + ((4.2048167614439080352894961833954e-08 * _Globals.WhiteTemp) * _Globals.WhiteTemp)) / ((1.0 - (2.8974181986995972692966461181641e-05 * _Globals.WhiteTemp)) + ((1.6145605741257895715534687042236e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)); + float _675 = ((2.0 * _659) - (8.0 * _670)) + 4.0; + float2 _679 = float2((3.0 * _659) / _675, (2.0 * _670) / _675); + float2 _686 = normalize(float2(_659, _670)); + float _691 = _659 + (((-_686.y) * _Globals.WhiteTint) * 0.0500000007450580596923828125); + float _695 = _670 + ((_686.x * _Globals.WhiteTint) * 0.0500000007450580596923828125); + float _700 = ((2.0 * _691) - (8.0 * _695)) + 4.0; + float2 _706 = select(float2(_642, (_642 * (((-3.0) * _642) + 2.86999988555908203125)) - 0.2750000059604644775390625), _679, bool2(_Globals.WhiteTemp < 4000.0)) + (float2((3.0 * _691) / _700, (2.0 * _695) / _700) - _679); + float _707 = _706.x; + float _708 = _706.y; + float _709 = fast::max(_708, 1.0000000133514319600180897396058e-10); + float3 _711 = _523; + _711.x = _707 / _709; + float3 _712 = _711; + _712.y = 1.0; + float3 _716 = _712; + _716.z = ((1.0 - _707) - _708) / _709; + float3 _719 = _523; + _719.x = 0.950455963611602783203125; + float3 _720 = _719; + _720.y = 1.0; + float3 _722 = _720; + _722.z = 1.0890576839447021484375; + float3 _723 = _716 * float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)); + float3 _724 = _722 * float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)); + float3 _743 = (_625 * ((float3x3(float3(0.41245639324188232421875, 0.3575761020183563232421875, 0.180437505245208740234375), float3(0.21267290413379669189453125, 0.715152204036712646484375, 0.072175003588199615478515625), float3(0.01933390088379383087158203125, 0.119191996753215789794921875, 0.950304090976715087890625)) * ((float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)) * float3x3(float3(_724.x / _723.x, 0.0, 0.0), float3(0.0, _724.y / _723.y, 0.0), float3(0.0, 0.0, _724.z / _723.z))) * float3x3(float3(0.986992895603179931640625, -0.14705429971218109130859375, 0.15996269881725311279296875), float3(0.4323053061962127685546875, 0.518360316753387451171875, 0.049291200935840606689453125), float3(-0.00852870009839534759521484375, 0.0400427989661693572998046875, 0.968486726284027099609375)))) * float3x3(float3(3.2409698963165283203125, -1.53738319873809814453125, -0.4986107647418975830078125), float3(-0.96924364566802978515625, 1.875967502593994140625, 0.0415550582110881805419921875), float3(0.055630080401897430419921875, -0.2039769589900970458984375, 1.05697154998779296875)))) * _573; + float3 _771; + if (_Globals.ColorShadow_Tint2.w != 0.0) + { + float _750 = dot(_743, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625)); + float3 _753 = (_743 / float3(_750)) - float3(1.0); + _771 = mix(_743, _743 * (_575 * (float3x3(float3(0.544169127941131591796875, 0.23959259688854217529296875, 0.16669429838657379150390625), float3(0.23946559429168701171875, 0.702153027057647705078125, 0.058381401002407073974609375), float3(-0.0023439000360667705535888671875, 0.0361833982169628143310546875, 1.05521833896636962890625)) * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)))), float3((1.0 - exp2((-4.0) * dot(_753, _753))) * (1.0 - exp2((((-4.0) * _Globals.ExpandGamut) * _750) * _750)))); + } + else + { + _771 = _743; + } + float _772 = dot(_771, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625)); + float4 _777 = _Globals.ColorSaturationShadows * _Globals.ColorSaturation; + float4 _782 = _Globals.ColorContrastShadows * _Globals.ColorContrast; + float4 _787 = _Globals.ColorGammaShadows * _Globals.ColorGamma; + float4 _792 = _Globals.ColorGainShadows * _Globals.ColorGain; + float4 _797 = _Globals.ColorOffsetShadows + _Globals.ColorOffset; + float3 _798 = float3(_772); + float _830 = smoothstep(0.0, _Globals.ColorCorrectionShadowsMax, _772); + float4 _834 = _Globals.ColorSaturationHighlights * _Globals.ColorSaturation; + float4 _837 = _Globals.ColorContrastHighlights * _Globals.ColorContrast; + float4 _840 = _Globals.ColorGammaHighlights * _Globals.ColorGamma; + float4 _843 = _Globals.ColorGainHighlights * _Globals.ColorGain; + float4 _846 = _Globals.ColorOffsetHighlights + _Globals.ColorOffset; + float _878 = smoothstep(_Globals.ColorCorrectionHighlightsMin, 1.0, _772); + float4 _881 = _Globals.ColorSaturationMidtones * _Globals.ColorSaturation; + float4 _884 = _Globals.ColorContrastMidtones * _Globals.ColorContrast; + float4 _887 = _Globals.ColorGammaMidtones * _Globals.ColorGamma; + float4 _890 = _Globals.ColorGainMidtones * _Globals.ColorGain; + float4 _893 = _Globals.ColorOffsetMidtones + _Globals.ColorOffset; + float3 _931 = ((((pow(pow(fast::max(float3(0.0), mix(_798, _771, _777.xyz * float3(_777.w))) * float3(5.5555553436279296875), _782.xyz * float3(_782.w)) * float3(0.180000007152557373046875), float3(1.0) / (_787.xyz * float3(_787.w))) * (_792.xyz * float3(_792.w))) + (_797.xyz + float3(_797.w))) * float3(1.0 - _830)) + (((pow(pow(fast::max(float3(0.0), mix(_798, _771, _881.xyz * float3(_881.w))) * float3(5.5555553436279296875), _884.xyz * float3(_884.w)) * float3(0.180000007152557373046875), float3(1.0) / (_887.xyz * float3(_887.w))) * (_890.xyz * float3(_890.w))) + (_893.xyz + float3(_893.w))) * float3(_830 - _878))) + (((pow(pow(fast::max(float3(0.0), mix(_798, _771, _834.xyz * float3(_834.w))) * float3(5.5555553436279296875), _837.xyz * float3(_837.w)) * float3(0.180000007152557373046875), float3(1.0) / (_840.xyz * float3(_840.w))) * (_843.xyz * float3(_843.w))) + (_846.xyz + float3(_846.w))) * float3(_878)); + float3 _932 = _931 * _575; + float3 _940 = float3(_Globals.BlueCorrection); + float3 _942 = mix(_931, _931 * ((_577 * float3x3(float3(0.940437257289886474609375, -0.01830687932670116424560546875, 0.07786960899829864501953125), float3(0.008378696627914905548095703125, 0.82866001129150390625, 0.162961304187774658203125), float3(0.0005471261101774871349334716796875, -0.00088337459601461887359619140625, 1.00033628940582275390625))) * _576), _940) * _577; + float _943 = _942.x; + float _944 = _942.y; + float _946 = _942.z; + float _949 = fast::max(fast::max(_943, _944), _946); + float _954 = (fast::max(_949, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_943, _944), _946), 1.0000000133514319600180897396058e-10)) / fast::max(_949, 0.00999999977648258209228515625); + float _967 = ((_946 + _944) + _943) + (1.75 * sqrt(((_946 * (_946 - _944)) + (_944 * (_944 - _943))) + (_943 * (_943 - _946)))); + float _968 = _967 * 0.3333333432674407958984375; + float _969 = _954 - 0.4000000059604644775390625; + float _974 = fast::max(1.0 - abs(_969 * 2.5), 0.0); + float _982 = (1.0 + (float(int(sign(_969 * 5.0))) * (1.0 - (_974 * _974)))) * 0.02500000037252902984619140625; + float _995; + if (_968 <= 0.053333334624767303466796875) + { + _995 = _982; + } + else + { + float _994; + if (_968 >= 0.1599999964237213134765625) + { + _994 = 0.0; + } + else + { + _994 = _982 * ((0.23999999463558197021484375 / _967) - 0.5); + } + _995 = _994; + } + float3 _998 = _942 * float3(1.0 + _995); + float _999 = _998.x; + float _1000 = _998.y; + float _1002 = _998.z; + float _1016; + if ((_999 == _1000) && (_1000 == _1002)) + { + _1016 = 0.0; + } + else + { + _1016 = 57.2957763671875 * atan2(1.73205077648162841796875 * (_1000 - _1002), ((2.0 * _999) - _1000) - _1002); + } + float _1021; + if (_1016 < 0.0) + { + _1021 = _1016 + 360.0; + } + else + { + _1021 = _1016; + } + float _1022 = fast::clamp(_1021, 0.0, 360.0); + float _1027; + if (_1022 > 180.0) + { + _1027 = _1022 - 360.0; + } + else + { + _1027 = _1022; + } + float _1031 = smoothstep(0.0, 1.0, 1.0 - abs(_1027 * 0.01481481455266475677490234375)); + float3 _1038 = _998; + _1038.x = _999 + ((((_1031 * _1031) * _954) * (0.02999999932944774627685546875 - _999)) * 0.180000007152557373046875); + float3 _1040 = fast::max(float3(0.0), _1038 * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375))); + float _1049 = (1.0 + _Globals.FilmBlackClip) - _Globals.FilmToe; + float _1052 = 1.0 + _Globals.FilmWhiteClip; + float _1055 = _1052 - _Globals.FilmShoulder; + float _1082; + if (_Globals.FilmToe > 0.800000011920928955078125) + { + _1082 = ((0.819999992847442626953125 - _Globals.FilmToe) / _Globals.FilmSlope) + (-0.744727432727813720703125); + } + else + { + float _1061 = (0.180000007152557373046875 + _Globals.FilmBlackClip) / _1049; + _1082 = (-0.744727432727813720703125) - ((0.5 * log(_1061 / (2.0 - _1061))) * (_1049 / _Globals.FilmSlope)); + } + float _1087 = ((1.0 - _Globals.FilmToe) / _Globals.FilmSlope) - _1082; + float _1089 = (_Globals.FilmShoulder / _Globals.FilmSlope) - _1087; + float3 _1093 = log(mix(float3(dot(_1040, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1040, float3(0.959999978542327880859375))) * float3(0.4342944622039794921875); + float3 _1097 = float3(_Globals.FilmSlope) * (_1093 + float3(_1087)); + float3 _1105 = float3(_1082); + float3 _1106 = _1093 - _1105; + float3 _1118 = float3(_1089); + float3 _1132 = fast::clamp(_1106 / float3(_1089 - _1082), float3(0.0), float3(1.0)); + float3 _1136 = select(_1132, float3(1.0) - _1132, bool3(_1089 < _1082)); + float3 _1141 = mix(select(_1097, float3(-_Globals.FilmBlackClip) + (float3(2.0 * _1049) / (float3(1.0) + exp(float3(((-2.0) * _Globals.FilmSlope) / _1049) * _1106))), _1093 < _1105), select(_1097, float3(_1052) - (float3(2.0 * _1055) / (float3(1.0) + exp(float3((2.0 * _Globals.FilmSlope) / _1055) * (_1093 - _1118)))), _1093 > _1118), ((float3(3.0) - (float3(2.0) * _1136)) * _1136) * _1136); + float3 _1145 = fast::max(float3(0.0), mix(float3(dot(_1141, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1141, float3(0.930000007152557373046875))); + float3 _1215; + if (_Globals.ColorShadow_Tint2.w == 0.0) + { + float3 _1157 = _3265; + _1157.x = dot(_932, _Globals.ColorMatrixR_ColorCurveCd1.xyz); + float3 _1162 = _1157; + _1162.y = dot(_932, _Globals.ColorMatrixG_ColorCurveCd3Cm3.xyz); + float3 _1167 = _1162; + _1167.z = dot(_932, _Globals.ColorMatrixB_ColorCurveCm2.xyz); + float3 _1183 = fast::max(float3(0.0), _1167 * (_Globals.ColorShadow_Tint1.xyz + (_Globals.ColorShadow_Tint2.xyz * float3(1.0 / (dot(_932, _Globals.ColorShadow_Luma.xyz) + 1.0))))); + float3 _1188 = fast::max(float3(0.0), _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.xxx - _1183); + float3 _1190 = fast::max(_1183, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.zzz); + _1215 = ((((_1190 * _Globals.ColorCurve_Ch1_Ch2.xxx) + _Globals.ColorCurve_Ch1_Ch2.yyy) * (float3(1.0) / (_1190 + _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.www))) + ((fast::clamp(_1183, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.xxx, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.zzz) * _Globals.ColorMatrixB_ColorCurveCm2.www) + (((_1188 * _Globals.ColorMatrixR_ColorCurveCd1.www) * (float3(1.0) / (_1188 + _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.yyy))) + _Globals.ColorMatrixG_ColorCurveCd3Cm3.www))) - float3(0.00200000009499490261077880859375); + } + else + { + _1215 = fast::max(float3(0.0), mix(_1145, _1145 * ((_577 * float3x3(float3(1.06317996978759765625, 0.02339559979736804962158203125, -0.08657260239124298095703125), float3(-0.010633699595928192138671875, 1.2063200473785400390625, -0.1956900060176849365234375), float3(-0.0005908869788981974124908447265625, 0.00105247995816171169281005859375, 0.999538004398345947265625))) * _576), _940) * _575); + } + float3 _1216 = fast::clamp(_1215, float3(0.0), float3(1.0)); + float _1217 = _1216.x; + float _1229; + for (;;) + { + if (_1217 < 0.00313066993840038776397705078125) + { + _1229 = _1217 * 12.9200000762939453125; + break; + } + _1229 = (pow(_1217, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _1230 = _1216.y; + float _1242; + for (;;) + { + if (_1230 < 0.00313066993840038776397705078125) + { + _1242 = _1230 * 12.9200000762939453125; + break; + } + _1242 = (pow(_1230, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _1243 = _1216.z; + float _1255; + for (;;) + { + if (_1243 < 0.00313066993840038776397705078125) + { + _1255 = _1243 * 12.9200000762939453125; + break; + } + _1255 = (pow(_1243, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float3 _1256 = float3(_1229, _1242, _1255); + float3 _1258 = (_1256 * float3(0.9375)) + float3(0.03125); + float _1270 = (_1258.z * 16.0) - 0.5; + float _1271 = floor(_1270); + float _1275 = (_1258.x + _1271) * 0.0625; + float _1276 = _1258.y; + float4 _1279 = Texture1.sample(Texture1Sampler, float2(_1275, _1276)); + float4 _1283 = Texture1.sample(Texture1Sampler, float2(_1275 + 0.0625, _1276)); + float3 _1289 = fast::max(float3(6.1035199905745685100555419921875e-05), (float3(_Globals.LUTWeights[0].x) * _1256) + (float3(_Globals.LUTWeights[1].x) * mix(_1279, _1283, float4(_1270 - _1271)).xyz)); + float3 _1295 = select(_1289 * float3(0.077399380505084991455078125), pow((_1289 * float3(0.94786727428436279296875)) + float3(0.0521326996386051177978515625), float3(2.400000095367431640625)), _1289 > float3(0.040449999272823333740234375)); + float3 _1324 = pow(fast::max(float3(0.0), mix((((float3(_Globals.MappingPolynomial.x) * (_1295 * _1295)) + (float3(_Globals.MappingPolynomial.y) * _1295)) + float3(_Globals.MappingPolynomial.z)) * _Globals.ColorScale, _Globals.OverlayColor.xyz, float3(_Globals.OverlayColor.w))), float3(_Globals.InverseGamma.y)); + float3 _3103; + if (_Globals.OutputDevice == 0u) + { + float _3063 = _1324.x; + float _3075; + for (;;) + { + if (_3063 < 0.00313066993840038776397705078125) + { + _3075 = _3063 * 12.9200000762939453125; + break; + } + _3075 = (pow(_3063, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _3076 = _1324.y; + float _3088; + for (;;) + { + if (_3076 < 0.00313066993840038776397705078125) + { + _3088 = _3076 * 12.9200000762939453125; + break; + } + _3088 = (pow(_3076, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _3089 = _1324.z; + float _3101; + for (;;) + { + if (_3089 < 0.00313066993840038776397705078125) + { + _3101 = _3089 * 12.9200000762939453125; + break; + } + _3101 = (pow(_3089, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + _3103 = float3(_3075, _3088, _3101); + } + else + { + float3 _3062; + if (_Globals.OutputDevice == 1u) + { + float3 _3055 = fast::max(float3(6.1035199905745685100555419921875e-05), (_1324 * _573) * _602); + _3062 = fast::min(_3055 * float3(4.5), (pow(fast::max(_3055, float3(0.017999999225139617919921875)), float3(0.449999988079071044921875)) * float3(1.09899997711181640625)) - float3(0.098999999463558197021484375)); + } + else + { + float3 _3052; + if ((_Globals.OutputDevice == 3u) || (_Globals.OutputDevice == 5u)) + { + float3 _2204 = (_932 * float3(1.5)) * (_572 * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625))); + float _2205 = _2204.x; + float _2206 = _2204.y; + float _2208 = _2204.z; + float _2211 = fast::max(fast::max(_2205, _2206), _2208); + float _2216 = (fast::max(_2211, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_2205, _2206), _2208), 1.0000000133514319600180897396058e-10)) / fast::max(_2211, 0.00999999977648258209228515625); + float _2229 = ((_2208 + _2206) + _2205) + (1.75 * sqrt(((_2208 * (_2208 - _2206)) + (_2206 * (_2206 - _2205))) + (_2205 * (_2205 - _2208)))); + float _2230 = _2229 * 0.3333333432674407958984375; + float _2231 = _2216 - 0.4000000059604644775390625; + float _2236 = fast::max(1.0 - abs(_2231 * 2.5), 0.0); + float _2244 = (1.0 + (float(int(sign(_2231 * 5.0))) * (1.0 - (_2236 * _2236)))) * 0.02500000037252902984619140625; + float _2257; + if (_2230 <= 0.053333334624767303466796875) + { + _2257 = _2244; + } + else + { + float _2256; + if (_2230 >= 0.1599999964237213134765625) + { + _2256 = 0.0; + } + else + { + _2256 = _2244 * ((0.23999999463558197021484375 / _2229) - 0.5); + } + _2257 = _2256; + } + float3 _2260 = _2204 * float3(1.0 + _2257); + float _2261 = _2260.x; + float _2262 = _2260.y; + float _2264 = _2260.z; + float _2278; + if ((_2261 == _2262) && (_2262 == _2264)) + { + _2278 = 0.0; + } + else + { + _2278 = 57.2957763671875 * atan2(1.73205077648162841796875 * (_2262 - _2264), ((2.0 * _2261) - _2262) - _2264); + } + float _2283; + if (_2278 < 0.0) + { + _2283 = _2278 + 360.0; + } + else + { + _2283 = _2278; + } + float _2284 = fast::clamp(_2283, 0.0, 360.0); + float _2289; + if (_2284 > 180.0) + { + _2289 = _2284 - 360.0; + } + else + { + _2289 = _2284; + } + float _2339; + if ((_2289 > (-67.5)) && (_2289 < 67.5)) + { + float _2296 = (_2289 - (-67.5)) * 0.0296296291053295135498046875; + int _2297 = int(_2296); + float _2299 = _2296 - float(_2297); + float _2300 = _2299 * _2299; + float _2301 = _2300 * _2299; + float _2338; + if (_2297 == 3) + { + _2338 = (((_2301 * (-0.16666667163372039794921875)) + (_2300 * 0.5)) + (_2299 * (-0.5))) + 0.16666667163372039794921875; + } + else + { + float _2331; + if (_2297 == 2) + { + _2331 = ((_2301 * 0.5) + (_2300 * (-1.0))) + 0.666666686534881591796875; + } + else + { + float _2326; + if (_2297 == 1) + { + _2326 = (((_2301 * (-0.5)) + (_2300 * 0.5)) + (_2299 * 0.5)) + 0.16666667163372039794921875; + } + else + { + float _2319; + if (_2297 == 0) + { + _2319 = _2301 * 0.16666667163372039794921875; + } + else + { + _2319 = 0.0; + } + _2326 = _2319; + } + _2331 = _2326; + } + _2338 = _2331; + } + _2339 = _2338; + } + else + { + _2339 = 0.0; + } + float3 _2346 = _2260; + _2346.x = _2261 + ((((_2339 * 1.5) * _2216) * (0.02999999932944774627685546875 - _2261)) * 0.180000007152557373046875); + float3 _2349 = fast::clamp(fast::clamp(_2346, float3(0.0), float3(65535.0)) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)), float3(0.0), float3(65535.0)); + float3 _2352 = mix(float3(dot(_2349, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _2349, float3(0.959999978542327880859375)); + float _2353 = _2352.x; + float _2362 = log((_2353 <= 0.0) ? 6.103515625e-05 : _2353) * 0.4342944622039794921875; + float _2431; + if (_2362 <= (-5.2601776123046875)) + { + _2431 = -4.0; + } + else + { + float _2428; + if ((_2362 > (-5.2601776123046875)) && (_2362 < (-0.744727432727813720703125))) + { + float _2411 = (_2362 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _2412 = int(_2411); + float _2414 = _2411 - float(_2412); + _2428 = dot(float3(_2414 * _2414, _2414, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_2412], _499[_2412 + 1], _499[_2412 + 2])); + } + else + { + float _2407; + if ((_2362 >= (-0.744727432727813720703125)) && (_2362 < 4.673812389373779296875)) + { + float _2390 = (_2362 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _2391 = int(_2390); + float _2393 = _2390 - float(_2391); + _2407 = dot(float3(_2393 * _2393, _2393, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2391], _500[_2391 + 1], _500[_2391 + 2])); + } + else + { + _2407 = 4.0; + } + _2428 = _2407; + } + _2431 = _2428; + } + float3 _2433 = _523; + _2433.x = pow(10.0, _2431); + float _2434 = _2352.y; + float _2438 = log((_2434 <= 0.0) ? 6.103515625e-05 : _2434) * 0.4342944622039794921875; + float _2505; + if (_2438 <= (-5.2601776123046875)) + { + _2505 = -4.0; + } + else + { + float _2502; + if ((_2438 > (-5.2601776123046875)) && (_2438 < (-0.744727432727813720703125))) + { + float _2485 = (_2438 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _2486 = int(_2485); + float _2488 = _2485 - float(_2486); + _2502 = dot(float3(_2488 * _2488, _2488, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_2486], _499[_2486 + 1], _499[_2486 + 2])); + } + else + { + float _2481; + if ((_2438 >= (-0.744727432727813720703125)) && (_2438 < 4.673812389373779296875)) + { + float _2464 = (_2438 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _2465 = int(_2464); + float _2467 = _2464 - float(_2465); + _2481 = dot(float3(_2467 * _2467, _2467, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2465], _500[_2465 + 1], _500[_2465 + 2])); + } + else + { + _2481 = 4.0; + } + _2502 = _2481; + } + _2505 = _2502; + } + float3 _2507 = _2433; + _2507.y = pow(10.0, _2505); + float _2508 = _2352.z; + float _2512 = log((_2508 <= 0.0) ? 6.103515625e-05 : _2508) * 0.4342944622039794921875; + float _2579; + if (_2512 <= (-5.2601776123046875)) + { + _2579 = -4.0; + } + else + { + float _2576; + if ((_2512 > (-5.2601776123046875)) && (_2512 < (-0.744727432727813720703125))) + { + float _2559 = (_2512 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _2560 = int(_2559); + float _2562 = _2559 - float(_2560); + _2576 = dot(float3(_2562 * _2562, _2562, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_2560], _499[_2560 + 1], _499[_2560 + 2])); + } + else + { + float _2555; + if ((_2512 >= (-0.744727432727813720703125)) && (_2512 < 4.673812389373779296875)) + { + float _2538 = (_2512 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _2539 = int(_2538); + float _2541 = _2538 - float(_2539); + _2555 = dot(float3(_2541 * _2541, _2541, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2539], _500[_2539 + 1], _500[_2539 + 2])); + } + else + { + _2555 = 4.0; + } + _2576 = _2555; + } + _2579 = _2576; + } + float3 _2581 = _2507; + _2581.z = pow(10.0, _2579); + float3 _2583 = (_2581 * float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625))) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)); + float _2714 = pow(10.0, (float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(-0.718548238277435302734375, 2.0810306072235107421875, 3.66812419891357421875)).z); + float _2786 = pow(10.0, dot(float3(0.4444443881511688232421875, 0.66666662693023681640625, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(2.0810306072235107421875, 3.66812419891357421875, 4.0))); + float _2787 = _2583.x; + float _2790 = log((_2787 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2787); + float _2791 = _2790 * 0.4342944622039794921875; + float _2792 = log(pow(10.0, dot(float3(0.3600003719329833984375, 0.600000321865081787109375, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(-4.0, -4.0, -3.1573765277862548828125)))); + float _2793 = _2792 * 0.4342944622039794921875; + float _2870; + if (_2791 <= _2793) + { + _2870 = (_2790 * 1.3028833866119384765625) + ((-4.0) - (_2792 * 1.3028833866119384765625)); + } + else + { + float _2800 = log(_2714) * 0.4342944622039794921875; + float _2862; + if ((_2791 > _2793) && (_2791 < _2800)) + { + float _2845 = (7.0 * (_2791 - _2793)) / (_2800 - _2793); + int _2846 = int(_2845); + float _2848 = _2845 - float(_2846); + _2862 = dot(float3(_2848 * _2848, _2848, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_503[_2846], _503[_2846 + 1], _503[_2846 + 2])); + } + else + { + float _2807 = log(_2786); + float _2808 = _2807 * 0.4342944622039794921875; + float _2841; + if ((_2791 >= _2800) && (_2791 < _2808)) + { + float _2824 = (7.0 * (_2791 - _2800)) / (_2808 - _2800); + int _2825 = int(_2824); + float _2827 = _2824 - float(_2825); + _2841 = dot(float3(_2827 * _2827, _2827, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_504[_2825], _504[_2825 + 1], _504[_2825 + 2])); + } + else + { + _2841 = (_2790 * 0.026057668030261993408203125) + (3.0 - (_2807 * 0.026057668030261993408203125)); + } + _2862 = _2841; + } + _2870 = _2862; + } + float3 _2872 = _523; + _2872.x = pow(10.0, _2870); + float _2873 = _2583.y; + float _2876 = log((_2873 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2873); + float _2877 = _2876 * 0.4342944622039794921875; + float _2954; + if (_2877 <= _2793) + { + _2954 = (_2876 * 1.3028833866119384765625) + ((-4.0) - (_2792 * 1.3028833866119384765625)); + } + else + { + float _2884 = log(_2714) * 0.4342944622039794921875; + float _2946; + if ((_2877 > _2793) && (_2877 < _2884)) + { + float _2929 = (7.0 * (_2877 - _2793)) / (_2884 - _2793); + int _2930 = int(_2929); + float _2932 = _2929 - float(_2930); + _2946 = dot(float3(_2932 * _2932, _2932, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_503[_2930], _503[_2930 + 1], _503[_2930 + 2])); + } + else + { + float _2891 = log(_2786); + float _2892 = _2891 * 0.4342944622039794921875; + float _2925; + if ((_2877 >= _2884) && (_2877 < _2892)) + { + float _2908 = (7.0 * (_2877 - _2884)) / (_2892 - _2884); + int _2909 = int(_2908); + float _2911 = _2908 - float(_2909); + _2925 = dot(float3(_2911 * _2911, _2911, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_504[_2909], _504[_2909 + 1], _504[_2909 + 2])); + } + else + { + _2925 = (_2876 * 0.026057668030261993408203125) + (3.0 - (_2891 * 0.026057668030261993408203125)); + } + _2946 = _2925; + } + _2954 = _2946; + } + float3 _2956 = _2872; + _2956.y = pow(10.0, _2954); + float _2957 = _2583.z; + float _2960 = log((_2957 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2957); + float _2961 = _2960 * 0.4342944622039794921875; + float _3038; + if (_2961 <= _2793) + { + _3038 = (_2960 * 1.3028833866119384765625) + ((-4.0) - (_2792 * 1.3028833866119384765625)); + } + else + { + float _2968 = log(_2714) * 0.4342944622039794921875; + float _3030; + if ((_2961 > _2793) && (_2961 < _2968)) + { + float _3013 = (7.0 * (_2961 - _2793)) / (_2968 - _2793); + int _3014 = int(_3013); + float _3016 = _3013 - float(_3014); + _3030 = dot(float3(_3016 * _3016, _3016, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_503[_3014], _503[_3014 + 1], _503[_3014 + 2])); + } + else + { + float _2975 = log(_2786); + float _2976 = _2975 * 0.4342944622039794921875; + float _3009; + if ((_2961 >= _2968) && (_2961 < _2976)) + { + float _2992 = (7.0 * (_2961 - _2968)) / (_2976 - _2968); + int _2993 = int(_2992); + float _2995 = _2992 - float(_2993); + _3009 = dot(float3(_2995 * _2995, _2995, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_504[_2993], _504[_2993 + 1], _504[_2993 + 2])); + } + else + { + _3009 = (_2960 * 0.026057668030261993408203125) + (3.0 - (_2975 * 0.026057668030261993408203125)); + } + _3030 = _3009; + } + _3038 = _3030; + } + float3 _3040 = _2956; + _3040.z = pow(10.0, _3038); + float3 _3044 = pow(((_3040 - float3(3.5073844628641381859779357910156e-05)) * _602) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _3052 = pow((float3(0.8359375) + (float3(18.8515625) * _3044)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _3044))), float3(78.84375)); + } + else + { + float3 _2201; + if ((_Globals.OutputDevice == 4u) || (_Globals.OutputDevice == 6u)) + { + float3 _1369 = (_932 * float3(1.5)) * (_572 * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625))); + float _1370 = _1369.x; + float _1371 = _1369.y; + float _1373 = _1369.z; + float _1376 = fast::max(fast::max(_1370, _1371), _1373); + float _1381 = (fast::max(_1376, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_1370, _1371), _1373), 1.0000000133514319600180897396058e-10)) / fast::max(_1376, 0.00999999977648258209228515625); + float _1394 = ((_1373 + _1371) + _1370) + (1.75 * sqrt(((_1373 * (_1373 - _1371)) + (_1371 * (_1371 - _1370))) + (_1370 * (_1370 - _1373)))); + float _1395 = _1394 * 0.3333333432674407958984375; + float _1396 = _1381 - 0.4000000059604644775390625; + float _1401 = fast::max(1.0 - abs(_1396 * 2.5), 0.0); + float _1409 = (1.0 + (float(int(sign(_1396 * 5.0))) * (1.0 - (_1401 * _1401)))) * 0.02500000037252902984619140625; + float _1422; + if (_1395 <= 0.053333334624767303466796875) + { + _1422 = _1409; + } + else + { + float _1421; + if (_1395 >= 0.1599999964237213134765625) + { + _1421 = 0.0; + } + else + { + _1421 = _1409 * ((0.23999999463558197021484375 / _1394) - 0.5); + } + _1422 = _1421; + } + float3 _1425 = _1369 * float3(1.0 + _1422); + float _1426 = _1425.x; + float _1427 = _1425.y; + float _1429 = _1425.z; + float _1443; + if ((_1426 == _1427) && (_1427 == _1429)) + { + _1443 = 0.0; + } + else + { + _1443 = 57.2957763671875 * atan2(1.73205077648162841796875 * (_1427 - _1429), ((2.0 * _1426) - _1427) - _1429); + } + float _1448; + if (_1443 < 0.0) + { + _1448 = _1443 + 360.0; + } + else + { + _1448 = _1443; + } + float _1449 = fast::clamp(_1448, 0.0, 360.0); + float _1454; + if (_1449 > 180.0) + { + _1454 = _1449 - 360.0; + } + else + { + _1454 = _1449; + } + float _1504; + if ((_1454 > (-67.5)) && (_1454 < 67.5)) + { + float _1461 = (_1454 - (-67.5)) * 0.0296296291053295135498046875; + int _1462 = int(_1461); + float _1464 = _1461 - float(_1462); + float _1465 = _1464 * _1464; + float _1466 = _1465 * _1464; + float _1503; + if (_1462 == 3) + { + _1503 = (((_1466 * (-0.16666667163372039794921875)) + (_1465 * 0.5)) + (_1464 * (-0.5))) + 0.16666667163372039794921875; + } + else + { + float _1496; + if (_1462 == 2) + { + _1496 = ((_1466 * 0.5) + (_1465 * (-1.0))) + 0.666666686534881591796875; + } + else + { + float _1491; + if (_1462 == 1) + { + _1491 = (((_1466 * (-0.5)) + (_1465 * 0.5)) + (_1464 * 0.5)) + 0.16666667163372039794921875; + } + else + { + float _1484; + if (_1462 == 0) + { + _1484 = _1466 * 0.16666667163372039794921875; + } + else + { + _1484 = 0.0; + } + _1491 = _1484; + } + _1496 = _1491; + } + _1503 = _1496; + } + _1504 = _1503; + } + else + { + _1504 = 0.0; + } + float3 _1511 = _1425; + _1511.x = _1426 + ((((_1504 * 1.5) * _1381) * (0.02999999932944774627685546875 - _1426)) * 0.180000007152557373046875); + float3 _1514 = fast::clamp(fast::clamp(_1511, float3(0.0), float3(65535.0)) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)), float3(0.0), float3(65535.0)); + float3 _1517 = mix(float3(dot(_1514, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1514, float3(0.959999978542327880859375)); + float _1518 = _1517.x; + float _1527 = log((_1518 <= 0.0) ? 6.103515625e-05 : _1518) * 0.4342944622039794921875; + float _1596; + if (_1527 <= (-5.2601776123046875)) + { + _1596 = -4.0; + } + else + { + float _1593; + if ((_1527 > (-5.2601776123046875)) && (_1527 < (-0.744727432727813720703125))) + { + float _1576 = (_1527 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _1577 = int(_1576); + float _1579 = _1576 - float(_1577); + _1593 = dot(float3(_1579 * _1579, _1579, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_1577], _499[_1577 + 1], _499[_1577 + 2])); + } + else + { + float _1572; + if ((_1527 >= (-0.744727432727813720703125)) && (_1527 < 4.673812389373779296875)) + { + float _1555 = (_1527 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _1556 = int(_1555); + float _1558 = _1555 - float(_1556); + _1572 = dot(float3(_1558 * _1558, _1558, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1556], _500[_1556 + 1], _500[_1556 + 2])); + } + else + { + _1572 = 4.0; + } + _1593 = _1572; + } + _1596 = _1593; + } + float3 _1598 = _523; + _1598.x = pow(10.0, _1596); + float _1599 = _1517.y; + float _1603 = log((_1599 <= 0.0) ? 6.103515625e-05 : _1599) * 0.4342944622039794921875; + float _1670; + if (_1603 <= (-5.2601776123046875)) + { + _1670 = -4.0; + } + else + { + float _1667; + if ((_1603 > (-5.2601776123046875)) && (_1603 < (-0.744727432727813720703125))) + { + float _1650 = (_1603 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _1651 = int(_1650); + float _1653 = _1650 - float(_1651); + _1667 = dot(float3(_1653 * _1653, _1653, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_1651], _499[_1651 + 1], _499[_1651 + 2])); + } + else + { + float _1646; + if ((_1603 >= (-0.744727432727813720703125)) && (_1603 < 4.673812389373779296875)) + { + float _1629 = (_1603 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _1630 = int(_1629); + float _1632 = _1629 - float(_1630); + _1646 = dot(float3(_1632 * _1632, _1632, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1630], _500[_1630 + 1], _500[_1630 + 2])); + } + else + { + _1646 = 4.0; + } + _1667 = _1646; + } + _1670 = _1667; + } + float3 _1672 = _1598; + _1672.y = pow(10.0, _1670); + float _1673 = _1517.z; + float _1677 = log((_1673 <= 0.0) ? 6.103515625e-05 : _1673) * 0.4342944622039794921875; + float _1744; + if (_1677 <= (-5.2601776123046875)) + { + _1744 = -4.0; + } + else + { + float _1741; + if ((_1677 > (-5.2601776123046875)) && (_1677 < (-0.744727432727813720703125))) + { + float _1724 = (_1677 - (-5.2601776123046875)) * 0.6643855571746826171875; + int _1725 = int(_1724); + float _1727 = _1724 - float(_1725); + _1741 = dot(float3(_1727 * _1727, _1727, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_1725], _499[_1725 + 1], _499[_1725 + 2])); + } + else + { + float _1720; + if ((_1677 >= (-0.744727432727813720703125)) && (_1677 < 4.673812389373779296875)) + { + float _1703 = (_1677 - (-0.744727432727813720703125)) * 0.55365467071533203125; + int _1704 = int(_1703); + float _1706 = _1703 - float(_1704); + _1720 = dot(float3(_1706 * _1706, _1706, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1704], _500[_1704 + 1], _500[_1704 + 2])); + } + else + { + _1720 = 4.0; + } + _1741 = _1720; + } + _1744 = _1741; + } + float3 _1746 = _1672; + _1746.z = pow(10.0, _1744); + float3 _1748 = (_1746 * float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625))) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)); + float _1879 = pow(10.0, (float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(-0.718548238277435302734375, 2.0810306072235107421875, 3.66812419891357421875)).z); + float _1951 = pow(10.0, dot(float3(0.69444429874420166015625, 0.8333332538604736328125, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(2.0810306072235107421875, 3.66812419891357421875, 4.0))); + float _1952 = _1748.x; + float _1955 = log((_1952 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _1952); + float _1956 = _1955 * 0.4342944622039794921875; + float _1958 = log(pow(10.0, dot(float3(0.3600003719329833984375, 0.600000321865081787109375, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(-4.0, -4.0, -3.1573765277862548828125)))) * 0.4342944622039794921875; + float _2030; + if (_1956 <= _1958) + { + _2030 = -2.3010299205780029296875; + } + else + { + float _1965 = log(_1879) * 0.4342944622039794921875; + float _2027; + if ((_1956 > _1958) && (_1956 < _1965)) + { + float _2010 = (7.0 * (_1956 - _1958)) / (_1965 - _1958); + int _2011 = int(_2010); + float _2013 = _2010 - float(_2011); + _2027 = dot(float3(_2013 * _2013, _2013, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_506[_2011], _506[_2011 + 1], _506[_2011 + 2])); + } + else + { + float _1972 = log(_1951); + float _1973 = _1972 * 0.4342944622039794921875; + float _2006; + if ((_1956 >= _1965) && (_1956 < _1973)) + { + float _1989 = (7.0 * (_1956 - _1965)) / (_1973 - _1965); + int _1990 = int(_1989); + float _1992 = _1989 - float(_1990); + _2006 = dot(float3(_1992 * _1992, _1992, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_507[_1990], _507[_1990 + 1], _507[_1990 + 2])); + } + else + { + _2006 = (_1955 * 0.05211533606052398681640625) + (3.3010299205780029296875 - (_1972 * 0.05211533606052398681640625)); + } + _2027 = _2006; + } + _2030 = _2027; + } + float3 _2032 = _523; + _2032.x = pow(10.0, _2030); + float _2033 = _1748.y; + float _2036 = log((_2033 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2033); + float _2037 = _2036 * 0.4342944622039794921875; + float _2109; + if (_2037 <= _1958) + { + _2109 = -2.3010299205780029296875; + } + else + { + float _2044 = log(_1879) * 0.4342944622039794921875; + float _2106; + if ((_2037 > _1958) && (_2037 < _2044)) + { + float _2089 = (7.0 * (_2037 - _1958)) / (_2044 - _1958); + int _2090 = int(_2089); + float _2092 = _2089 - float(_2090); + _2106 = dot(float3(_2092 * _2092, _2092, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_506[_2090], _506[_2090 + 1], _506[_2090 + 2])); + } + else + { + float _2051 = log(_1951); + float _2052 = _2051 * 0.4342944622039794921875; + float _2085; + if ((_2037 >= _2044) && (_2037 < _2052)) + { + float _2068 = (7.0 * (_2037 - _2044)) / (_2052 - _2044); + int _2069 = int(_2068); + float _2071 = _2068 - float(_2069); + _2085 = dot(float3(_2071 * _2071, _2071, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_507[_2069], _507[_2069 + 1], _507[_2069 + 2])); + } + else + { + _2085 = (_2036 * 0.05211533606052398681640625) + (3.3010299205780029296875 - (_2051 * 0.05211533606052398681640625)); + } + _2106 = _2085; + } + _2109 = _2106; + } + float3 _2111 = _2032; + _2111.y = pow(10.0, _2109); + float _2112 = _1748.z; + float _2115 = log((_2112 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2112); + float _2116 = _2115 * 0.4342944622039794921875; + float _2188; + if (_2116 <= _1958) + { + _2188 = -2.3010299205780029296875; + } + else + { + float _2123 = log(_1879) * 0.4342944622039794921875; + float _2185; + if ((_2116 > _1958) && (_2116 < _2123)) + { + float _2168 = (7.0 * (_2116 - _1958)) / (_2123 - _1958); + int _2169 = int(_2168); + float _2171 = _2168 - float(_2169); + _2185 = dot(float3(_2171 * _2171, _2171, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_506[_2169], _506[_2169 + 1], _506[_2169 + 2])); + } + else + { + float _2130 = log(_1951); + float _2131 = _2130 * 0.4342944622039794921875; + float _2164; + if ((_2116 >= _2123) && (_2116 < _2131)) + { + float _2147 = (7.0 * (_2116 - _2123)) / (_2131 - _2123); + int _2148 = int(_2147); + float _2150 = _2147 - float(_2148); + _2164 = dot(float3(_2150 * _2150, _2150, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_507[_2148], _507[_2148 + 1], _507[_2148 + 2])); + } + else + { + _2164 = (_2115 * 0.05211533606052398681640625) + (3.3010299205780029296875 - (_2130 * 0.05211533606052398681640625)); + } + _2185 = _2164; + } + _2188 = _2185; + } + float3 _2190 = _2111; + _2190.z = pow(10.0, _2188); + float3 _2193 = pow((_2190 * _602) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _2201 = pow((float3(0.8359375) + (float3(18.8515625) * _2193)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _2193))), float3(78.84375)); + } + else + { + float3 _1366; + if (_Globals.OutputDevice == 7u) + { + float3 _1358 = pow(((_932 * _573) * _602) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _1366 = pow((float3(0.8359375) + (float3(18.8515625) * _1358)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _1358))), float3(78.84375)); + } + else + { + _1366 = pow((_1324 * _573) * _602, float3(_Globals.InverseGamma.z)); + } + _2201 = _1366; + } + _3052 = _2201; + } + _3062 = _3052; + } + _3103 = _3062; + } + float3 _3104 = _3103 * float3(0.95238101482391357421875); + float4 _3106 = float4(_3104.x, _3104.y, _3104.z, float4(0.0).w); + _3106.w = 0.0; + out.out_var_SV_Target0 = _3106; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag new file mode 100644 index 0000000..479471d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag @@ -0,0 +1,503 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct type_TranslucentBasePass +{ + uint TranslucentBasePass_Shared_Forward_NumLocalLights; + uint TranslucentBasePass_Shared_Forward_NumReflectionCaptures; + uint TranslucentBasePass_Shared_Forward_HasDirectionalLight; + uint TranslucentBasePass_Shared_Forward_NumGridCells; + packed_int3 TranslucentBasePass_Shared_Forward_CulledGridSize; + uint TranslucentBasePass_Shared_Forward_MaxCulledLightsPerCell; + uint TranslucentBasePass_Shared_Forward_LightGridPixelSizeShift; + uint PrePadding_TranslucentBasePass_Shared_Forward_36; + uint PrePadding_TranslucentBasePass_Shared_Forward_40; + uint PrePadding_TranslucentBasePass_Shared_Forward_44; + packed_float3 TranslucentBasePass_Shared_Forward_LightGridZParams; + float PrePadding_TranslucentBasePass_Shared_Forward_60; + packed_float3 TranslucentBasePass_Shared_Forward_DirectionalLightDirection; + float PrePadding_TranslucentBasePass_Shared_Forward_76; + packed_float3 TranslucentBasePass_Shared_Forward_DirectionalLightColor; + float TranslucentBasePass_Shared_Forward_DirectionalLightVolumetricScatteringIntensity; + uint TranslucentBasePass_Shared_Forward_DirectionalLightShadowMapChannelMask; + uint PrePadding_TranslucentBasePass_Shared_Forward_100; + float2 TranslucentBasePass_Shared_Forward_DirectionalLightDistanceFadeMAD; + uint TranslucentBasePass_Shared_Forward_NumDirectionalLightCascades; + uint PrePadding_TranslucentBasePass_Shared_Forward_116; + uint PrePadding_TranslucentBasePass_Shared_Forward_120; + uint PrePadding_TranslucentBasePass_Shared_Forward_124; + float4 TranslucentBasePass_Shared_Forward_CascadeEndDepths; + float4x4 TranslucentBasePass_Shared_Forward_DirectionalLightWorldToShadowMatrix[4]; + float4 TranslucentBasePass_Shared_Forward_DirectionalLightShadowmapMinMax[4]; + float4 TranslucentBasePass_Shared_Forward_DirectionalLightShadowmapAtlasBufferSize; + float TranslucentBasePass_Shared_Forward_DirectionalLightDepthBias; + uint TranslucentBasePass_Shared_Forward_DirectionalLightUseStaticShadowing; + uint PrePadding_TranslucentBasePass_Shared_Forward_488; + uint PrePadding_TranslucentBasePass_Shared_Forward_492; + float4 TranslucentBasePass_Shared_Forward_DirectionalLightStaticShadowBufferSize; + float4x4 TranslucentBasePass_Shared_Forward_DirectionalLightWorldToStaticShadow; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_576; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_580; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_584; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_588; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_592; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_596; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_600; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_604; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_608; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_612; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_616; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_620; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_624; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_628; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_632; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_636; + uint TranslucentBasePass_Shared_ForwardISR_NumLocalLights; + uint TranslucentBasePass_Shared_ForwardISR_NumReflectionCaptures; + uint TranslucentBasePass_Shared_ForwardISR_HasDirectionalLight; + uint TranslucentBasePass_Shared_ForwardISR_NumGridCells; + packed_int3 TranslucentBasePass_Shared_ForwardISR_CulledGridSize; + uint TranslucentBasePass_Shared_ForwardISR_MaxCulledLightsPerCell; + uint TranslucentBasePass_Shared_ForwardISR_LightGridPixelSizeShift; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_676; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_680; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_684; + packed_float3 TranslucentBasePass_Shared_ForwardISR_LightGridZParams; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_700; + packed_float3 TranslucentBasePass_Shared_ForwardISR_DirectionalLightDirection; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_716; + packed_float3 TranslucentBasePass_Shared_ForwardISR_DirectionalLightColor; + float TranslucentBasePass_Shared_ForwardISR_DirectionalLightVolumetricScatteringIntensity; + uint TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowMapChannelMask; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_740; + float2 TranslucentBasePass_Shared_ForwardISR_DirectionalLightDistanceFadeMAD; + uint TranslucentBasePass_Shared_ForwardISR_NumDirectionalLightCascades; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_756; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_760; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_764; + float4 TranslucentBasePass_Shared_ForwardISR_CascadeEndDepths; + float4x4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightWorldToShadowMatrix[4]; + float4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowmapMinMax[4]; + float4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowmapAtlasBufferSize; + float TranslucentBasePass_Shared_ForwardISR_DirectionalLightDepthBias; + uint TranslucentBasePass_Shared_ForwardISR_DirectionalLightUseStaticShadowing; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_1128; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_1132; + float4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightStaticShadowBufferSize; + float4x4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightWorldToStaticShadow; + float PrePadding_TranslucentBasePass_Shared_Reflection_1216; + float PrePadding_TranslucentBasePass_Shared_Reflection_1220; + float PrePadding_TranslucentBasePass_Shared_Reflection_1224; + float PrePadding_TranslucentBasePass_Shared_Reflection_1228; + float PrePadding_TranslucentBasePass_Shared_Reflection_1232; + float PrePadding_TranslucentBasePass_Shared_Reflection_1236; + float PrePadding_TranslucentBasePass_Shared_Reflection_1240; + float PrePadding_TranslucentBasePass_Shared_Reflection_1244; + float PrePadding_TranslucentBasePass_Shared_Reflection_1248; + float PrePadding_TranslucentBasePass_Shared_Reflection_1252; + float PrePadding_TranslucentBasePass_Shared_Reflection_1256; + float PrePadding_TranslucentBasePass_Shared_Reflection_1260; + float PrePadding_TranslucentBasePass_Shared_Reflection_1264; + float PrePadding_TranslucentBasePass_Shared_Reflection_1268; + float PrePadding_TranslucentBasePass_Shared_Reflection_1272; + float PrePadding_TranslucentBasePass_Shared_Reflection_1276; + float4 TranslucentBasePass_Shared_Reflection_SkyLightParameters; + float TranslucentBasePass_Shared_Reflection_SkyLightCubemapBrightness; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1300; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1304; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1308; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1312; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1316; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1320; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1324; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1328; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1332; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1336; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1340; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1344; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1348; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1352; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1356; + float4 TranslucentBasePass_Shared_PlanarReflection_ReflectionPlane; + float4 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionOrigin; + float4 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionXAxis; + float4 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionYAxis; + float3x4 TranslucentBasePass_Shared_PlanarReflection_InverseTransposeMirrorMatrix; + packed_float3 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionParameters; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1484; + float2 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionParameters2; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1496; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1500; + float4x4 TranslucentBasePass_Shared_PlanarReflection_ProjectionWithExtraFOV[2]; + float4 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionScreenScaleBias[2]; + float2 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionScreenBound; + uint TranslucentBasePass_Shared_PlanarReflection_bIsStereo; + float PrePadding_TranslucentBasePass_Shared_Fog_1676; + float PrePadding_TranslucentBasePass_Shared_Fog_1680; + float PrePadding_TranslucentBasePass_Shared_Fog_1684; + float PrePadding_TranslucentBasePass_Shared_Fog_1688; + float PrePadding_TranslucentBasePass_Shared_Fog_1692; + float4 TranslucentBasePass_Shared_Fog_ExponentialFogParameters; + float4 TranslucentBasePass_Shared_Fog_ExponentialFogParameters2; + float4 TranslucentBasePass_Shared_Fog_ExponentialFogColorParameter; + float4 TranslucentBasePass_Shared_Fog_ExponentialFogParameters3; + float4 TranslucentBasePass_Shared_Fog_InscatteringLightDirection; + float4 TranslucentBasePass_Shared_Fog_DirectionalInscatteringColor; + float2 TranslucentBasePass_Shared_Fog_SinCosInscatteringColorCubemapRotation; + float PrePadding_TranslucentBasePass_Shared_Fog_1800; + float PrePadding_TranslucentBasePass_Shared_Fog_1804; + packed_float3 TranslucentBasePass_Shared_Fog_FogInscatteringTextureParameters; + float TranslucentBasePass_Shared_Fog_ApplyVolumetricFog; + float PrePadding_TranslucentBasePass_1824; + float PrePadding_TranslucentBasePass_1828; + float PrePadding_TranslucentBasePass_1832; + float PrePadding_TranslucentBasePass_1836; + float PrePadding_TranslucentBasePass_1840; + float PrePadding_TranslucentBasePass_1844; + float PrePadding_TranslucentBasePass_1848; + float PrePadding_TranslucentBasePass_1852; + float PrePadding_TranslucentBasePass_1856; + float PrePadding_TranslucentBasePass_1860; + float PrePadding_TranslucentBasePass_1864; + float PrePadding_TranslucentBasePass_1868; + float PrePadding_TranslucentBasePass_1872; + float PrePadding_TranslucentBasePass_1876; + float PrePadding_TranslucentBasePass_1880; + float PrePadding_TranslucentBasePass_1884; + float PrePadding_TranslucentBasePass_1888; + float PrePadding_TranslucentBasePass_1892; + float PrePadding_TranslucentBasePass_1896; + float PrePadding_TranslucentBasePass_1900; + float PrePadding_TranslucentBasePass_1904; + float PrePadding_TranslucentBasePass_1908; + float PrePadding_TranslucentBasePass_1912; + float PrePadding_TranslucentBasePass_1916; + float PrePadding_TranslucentBasePass_1920; + float PrePadding_TranslucentBasePass_1924; + float PrePadding_TranslucentBasePass_1928; + float PrePadding_TranslucentBasePass_1932; + float PrePadding_TranslucentBasePass_1936; + float PrePadding_TranslucentBasePass_1940; + float PrePadding_TranslucentBasePass_1944; + float PrePadding_TranslucentBasePass_1948; + float PrePadding_TranslucentBasePass_1952; + float PrePadding_TranslucentBasePass_1956; + float PrePadding_TranslucentBasePass_1960; + float PrePadding_TranslucentBasePass_1964; + float PrePadding_TranslucentBasePass_1968; + float PrePadding_TranslucentBasePass_1972; + float PrePadding_TranslucentBasePass_1976; + float PrePadding_TranslucentBasePass_1980; + float PrePadding_TranslucentBasePass_1984; + float PrePadding_TranslucentBasePass_1988; + float PrePadding_TranslucentBasePass_1992; + float PrePadding_TranslucentBasePass_1996; + float PrePadding_TranslucentBasePass_2000; + float PrePadding_TranslucentBasePass_2004; + float PrePadding_TranslucentBasePass_2008; + float PrePadding_TranslucentBasePass_2012; + float PrePadding_TranslucentBasePass_2016; + float PrePadding_TranslucentBasePass_2020; + float PrePadding_TranslucentBasePass_2024; + float PrePadding_TranslucentBasePass_2028; + float PrePadding_TranslucentBasePass_2032; + float PrePadding_TranslucentBasePass_2036; + float PrePadding_TranslucentBasePass_2040; + float PrePadding_TranslucentBasePass_2044; + float PrePadding_TranslucentBasePass_2048; + float PrePadding_TranslucentBasePass_2052; + float PrePadding_TranslucentBasePass_2056; + float PrePadding_TranslucentBasePass_2060; + float PrePadding_TranslucentBasePass_2064; + float PrePadding_TranslucentBasePass_2068; + float PrePadding_TranslucentBasePass_2072; + float PrePadding_TranslucentBasePass_2076; + float PrePadding_TranslucentBasePass_2080; + float PrePadding_TranslucentBasePass_2084; + float PrePadding_TranslucentBasePass_2088; + float PrePadding_TranslucentBasePass_2092; + float PrePadding_TranslucentBasePass_2096; + float PrePadding_TranslucentBasePass_2100; + float PrePadding_TranslucentBasePass_2104; + float PrePadding_TranslucentBasePass_2108; + float PrePadding_TranslucentBasePass_2112; + float PrePadding_TranslucentBasePass_2116; + float PrePadding_TranslucentBasePass_2120; + float PrePadding_TranslucentBasePass_2124; + float PrePadding_TranslucentBasePass_2128; + float PrePadding_TranslucentBasePass_2132; + float PrePadding_TranslucentBasePass_2136; + float PrePadding_TranslucentBasePass_2140; + float4 TranslucentBasePass_HZBUvFactorAndInvFactor; + float4 TranslucentBasePass_PrevScreenPositionScaleBias; + float TranslucentBasePass_PrevSceneColorPreExposureInv; +}; + +struct type_Material +{ + float4 Material_VectorExpressions[2]; + float4 Material_ScalarExpressions[1]; +}; + +constant float _108 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[user(locn0)]]; + float4 in_var_TEXCOORD11_centroid [[user(locn1)]]; + uint in_var_PRIMITIVE_ID [[user(locn2)]]; + float4 in_var_TEXCOORD7 [[user(locn3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], const device type_StructuredBuffer_v4float& View_PrimitiveSceneData [[buffer(1)]], constant type_TranslucentBasePass& TranslucentBasePass [[buffer(2)]], constant type_Material& Material [[buffer(3)]], texture3d TranslucentBasePass_Shared_Fog_IntegratedLightScattering [[texture(0)]], sampler View_SharedBilinearClampedSampler [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleMaskIn [[sample_mask]]) +{ + main0_out out = {}; + float4 _137 = View.View_SVPositionToTranslatedWorld * float4(gl_FragCoord.xyz, 1.0); + float3 _142 = (_137.xyz / float3(_137.w)) - float3(View.View_PreViewTranslation); + bool _165 = TranslucentBasePass.TranslucentBasePass_Shared_Fog_ApplyVolumetricFog > 0.0; + float4 _215; + if (_165) + { + float4 _172 = View.View_WorldToClip * float4(_142, 1.0); + float _173 = _172.w; + float4 _202; + if (_165) + { + _202 = TranslucentBasePass_Shared_Fog_IntegratedLightScattering.sample(View_SharedBilinearClampedSampler, float3(((_172.xy / float2(_173)).xy * float2(0.5, -0.5)) + float2(0.5), (log2((_173 * View.View_VolumetricFogGridZParams[0]) + View.View_VolumetricFogGridZParams[1]) * View.View_VolumetricFogGridZParams[2]) * View.View_VolumetricFogInvGridSize[2]), level(0.0)); + } + else + { + _202 = float4(0.0, 0.0, 0.0, 1.0); + } + _215 = float4(_202.xyz + (in.in_var_TEXCOORD7.xyz * float3(_202.w)), _202.w * in.in_var_TEXCOORD7.w); + } + else + { + _215 = in.in_var_TEXCOORD7; + } + float3 _216 = fast::max(Material.Material_VectorExpressions[1].xyz * float3(((1.0 + dot(float3(-0.2857142984867095947265625, -0.4285714328289031982421875, 0.857142865657806396484375), normalize(float3x3(in.in_var_TEXCOORD10_centroid.xyz, cross(in.in_var_TEXCOORD11_centroid.xyz, in.in_var_TEXCOORD10_centroid.xyz) * float3(in.in_var_TEXCOORD11_centroid.w), in.in_var_TEXCOORD11_centroid.xyz) * normalize((float3(0.0, 0.0, 1.0) * float3(View.View_NormalOverrideParameter.w)) + View.View_NormalOverrideParameter.xyz)))) * 0.5) + 0.20000000298023223876953125), float3(0.0)); + float3 _246; + if (View.View_OutOfBoundsMask > 0.0) + { + uint _222 = in.in_var_PRIMITIVE_ID * 26u; + float3 _245; + if (any(abs(_142 - View_PrimitiveSceneData._m0[_222 + 5u].xyz) > (View_PrimitiveSceneData._m0[_222 + 19u].xyz + float3(1.0)))) + { + _245 = mix(float3(1.0, 1.0, 0.0), float3(0.0, 1.0, 1.0), select(float3(0.0), float3(1.0), float3(fract(dot(_142, float3(0.57700002193450927734375)) * 0.00200000009499490261077880859375)) > float3(0.5))); + } + else + { + _245 = _216; + } + _246 = _245; + } + else + { + _246 = _216; + } + float4 _256 = float4((_246 * float3(_215.w)) + _215.xyz, _108); + _256.w = 1.0; + float4 _268; + uint _269; + if (View.View_NumSceneColorMSAASamples > 1) + { + _268 = _256 * float4(float(View.View_NumSceneColorMSAASamples) * 0.25); + _269 = gl_SampleMaskIn & 15u; + } + else + { + _268 = _256; + _269 = gl_SampleMaskIn; + } + out.out_var_SV_Target0 = _268; + out.gl_SampleMask = _269; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag new file mode 100644 index 0000000..817b1cf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag @@ -0,0 +1,213 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Globals +{ + float3 SoftTransitionScale; + float4 ShadowBufferSize; + float ShadowFadeFraction; + float ShadowSharpen; + float4 LightPositionAndInvRadius; + float4x4 ScreenToShadowMatrix; + float2 ProjectionDepthBiasParameters; + float4 ModulatedShadowColor; + float4 ShadowTileOffsetAndSize; +}; + +constant float4 _58 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float4 _67 = _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData; + float _68 = _67.w; + float4 _82 = _Globals.ScreenToShadowMatrix * float4((((gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw) - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_68), _68, 1.0); + float _118 = fast::clamp(((fast::clamp((ShadowDepthTexture.sample(ShadowDepthTextureSampler, (((_82.xyz / float3(_82.w)).xy * _Globals.ShadowTileOffsetAndSize.zw).xy + _Globals.ShadowTileOffsetAndSize.xy).xy, level(0.0)).xxx * float3(_Globals.SoftTransitionScale.z)) - float3((fast::min(_82.z, 0.999989986419677734375) * _Globals.SoftTransitionScale.z) - 1.0), float3(0.0), float3(1.0)).x - 0.5) * _Globals.ShadowSharpen) + 0.5, 0.0, 1.0); + float3 _127 = mix(_Globals.ModulatedShadowColor.xyz, float3(1.0), float3(mix(1.0, _118 * _118, _Globals.ShadowFadeFraction))); + float4 _129 = float4(_127.x, _127.y, _127.z, _58.w); + _129.w = 0.0; + out.out_var_SV_Target0 = _129; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag new file mode 100644 index 0000000..817b1cf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag @@ -0,0 +1,213 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Globals +{ + float3 SoftTransitionScale; + float4 ShadowBufferSize; + float ShadowFadeFraction; + float ShadowSharpen; + float4 LightPositionAndInvRadius; + float4x4 ScreenToShadowMatrix; + float2 ProjectionDepthBiasParameters; + float4 ModulatedShadowColor; + float4 ShadowTileOffsetAndSize; +}; + +constant float4 _58 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float4 _67 = _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData; + float _68 = _67.w; + float4 _82 = _Globals.ScreenToShadowMatrix * float4((((gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw) - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_68), _68, 1.0); + float _118 = fast::clamp(((fast::clamp((ShadowDepthTexture.sample(ShadowDepthTextureSampler, (((_82.xyz / float3(_82.w)).xy * _Globals.ShadowTileOffsetAndSize.zw).xy + _Globals.ShadowTileOffsetAndSize.xy).xy, level(0.0)).xxx * float3(_Globals.SoftTransitionScale.z)) - float3((fast::min(_82.z, 0.999989986419677734375) * _Globals.SoftTransitionScale.z) - 1.0), float3(0.0), float3(1.0)).x - 0.5) * _Globals.ShadowSharpen) + 0.5, 0.0, 1.0); + float3 _127 = mix(_Globals.ModulatedShadowColor.xyz, float3(1.0), float3(mix(1.0, _118 * _118, _Globals.ShadowFadeFraction))); + float4 _129 = float4(_127.x, _127.y, _127.z, _58.w); + _129.w = 0.0; + out.out_var_SV_Target0 = _129; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag new file mode 100644 index 0000000..c5c37a2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag @@ -0,0 +1,84 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +struct spvDescriptorSetBuffer0 +{ + const device type_StructuredBuffer_v4float* CulledObjectBoxBounds [[id(0)]]; + constant type_Globals* _Globals [[id(1)]]; + texture2d RWShadowTileNumCulledObjects [[id(2)]]; + device atomic_uint* RWShadowTileNumCulledObjects_atomic [[id(3)]]; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float(((*spvDescriptorSet0._Globals).ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2((*spvDescriptorSet0._Globals).ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _107 = _103 + 1u; + if (all((*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_107].xy > _96.xy) && all((*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103].xyz < _102)) + { + float3 _121 = float3(0.5) * ((*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103].xyz + (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_107].xyz); + float _122 = _96.x; + float _123 = _96.y; + float _126 = _100.x; + float _129 = _100.y; + float3 _166 = float3(_122, _123, -1000.0) - _121; + float3 _170 = float3(dot(_166, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_166, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_166, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + float3 _189 = float3(_126, _123, -1000.0) - _121; + float3 _193 = float3(dot(_189, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_189, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_189, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + float3 _205 = float3(_122, _129, -1000.0) - _121; + float3 _209 = float3(dot(_205, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_205, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_205, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + float3 _221 = float3(_126, _129, -1000.0) - _121; + float3 _225 = float3(dot(_221, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_221, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_221, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + float3 _237 = float3(_122, _123, 1.0) - _121; + float3 _241 = float3(dot(_237, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_237, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_237, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + float3 _253 = float3(_126, _123, 1.0) - _121; + float3 _257 = float3(dot(_253, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_253, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_253, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + float3 _269 = float3(_122, _129, 1.0) - _121; + float3 _273 = float3(dot(_269, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_269, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_269, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + float3 _285 = float3(_126, _129, 1.0) - _121; + float3 _289 = float3(dot(_285, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_285, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_285, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + if (all(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(float3(500000.0), _170), _193), _209), _225), _241), _257), _273), _289) < float3(1.0)) && all(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(float3(-500000.0), _170), _193), _209), _225), _241), _257), _273), _289) > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&spvDescriptorSet0.RWShadowTileNumCulledObjects_atomic[(_78 * (*spvDescriptorSet0._Globals).ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.frag new file mode 100644 index 0000000..d5ce54c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.frag @@ -0,0 +1,76 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_v4float& CulledObjectBoxBounds [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d RWShadowTileNumCulledObjects [[texture(0)]], device atomic_uint* RWShadowTileNumCulledObjects_atomic [[buffer(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float((_Globals.ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2(_Globals.ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _107 = _103 + 1u; + if (all(CulledObjectBoxBounds._m0[_107].xy > _96.xy) && all(CulledObjectBoxBounds._m0[_103].xyz < _102)) + { + float3 _121 = float3(0.5) * (CulledObjectBoxBounds._m0[_103].xyz + CulledObjectBoxBounds._m0[_107].xyz); + float _122 = _96.x; + float _123 = _96.y; + float _126 = _100.x; + float _129 = _100.y; + float3 _166 = float3(_122, _123, -1000.0) - _121; + float3 _170 = float3(dot(_166, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + float3 _189 = float3(_126, _123, -1000.0) - _121; + float3 _193 = float3(dot(_189, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_189, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_189, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + float3 _205 = float3(_122, _129, -1000.0) - _121; + float3 _209 = float3(dot(_205, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_205, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_205, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + float3 _221 = float3(_126, _129, -1000.0) - _121; + float3 _225 = float3(dot(_221, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_221, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_221, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + float3 _237 = float3(_122, _123, 1.0) - _121; + float3 _241 = float3(dot(_237, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_237, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_237, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + float3 _253 = float3(_126, _123, 1.0) - _121; + float3 _257 = float3(dot(_253, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_253, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_253, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + float3 _269 = float3(_122, _129, 1.0) - _121; + float3 _273 = float3(dot(_269, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_269, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_269, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + float3 _285 = float3(_126, _129, 1.0) - _121; + float3 _289 = float3(dot(_285, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_285, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_285, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + if (all(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(float3(500000.0), _170), _193), _209), _225), _241), _257), _273), _289) < float3(1.0)) && all(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(float3(-500000.0), _170), _193), _209), _225), _241), _257), _273), _289) > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&RWShadowTileNumCulledObjects_atomic[(_78 * _Globals.ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag new file mode 100644 index 0000000..a7bb0ff --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag @@ -0,0 +1,77 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvBufferSizeConstants [[buffer(25)]], const device type_StructuredBuffer_v4float& CulledObjectBoxBounds [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d RWShadowTileNumCulledObjects [[texture(0)]], device atomic_uint* RWShadowTileNumCulledObjects_atomic [[buffer(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + constant uint& CulledObjectBoxBoundsBufferSize = spvBufferSizeConstants[0]; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float((_Globals.ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2(_Globals.ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _310 = uint(clamp(int(_103 + 1u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u)))); + if (all(CulledObjectBoxBounds._m0[_310].xy > _96.xy) && all(CulledObjectBoxBounds._m0[uint(clamp(int(_103), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz < _102)) + { + float3 _121 = float3(0.5) * (CulledObjectBoxBounds._m0[uint(clamp(int(_103), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz + CulledObjectBoxBounds._m0[_310].xyz); + float _122 = _96.x; + float _123 = _96.y; + float _126 = _100.x; + float _129 = _100.y; + float3 _166 = float3(_122, _123, -1000.0) - _121; + float3 _170 = float3(dot(_166, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 2u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_166, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 3u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_166, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 4u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz)); + float3 _189 = float3(_126, _123, -1000.0) - _121; + float3 _193 = float3(dot(_189, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 2u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_189, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 3u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_189, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 4u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz)); + float3 _205 = float3(_122, _129, -1000.0) - _121; + float3 _209 = float3(dot(_205, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 2u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_205, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 3u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_205, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 4u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz)); + float3 _221 = float3(_126, _129, -1000.0) - _121; + float3 _225 = float3(dot(_221, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 2u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_221, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 3u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_221, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 4u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz)); + float3 _237 = float3(_122, _123, 1.0) - _121; + float3 _241 = float3(dot(_237, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 2u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_237, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 3u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_237, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 4u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz)); + float3 _253 = float3(_126, _123, 1.0) - _121; + float3 _257 = float3(dot(_253, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 2u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_253, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 3u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_253, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 4u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz)); + float3 _269 = float3(_122, _129, 1.0) - _121; + float3 _273 = float3(dot(_269, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 2u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_269, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 3u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_269, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 4u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz)); + float3 _285 = float3(_126, _129, 1.0) - _121; + float3 _289 = float3(dot(_285, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 2u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_285, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 3u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz), dot(_285, CulledObjectBoxBounds._m0[uint(clamp(int(_103 + 4u), int(0u), int(min((((CulledObjectBoxBoundsBufferSize - 0) / 16) - 1u), 2147483647u))))].xyz)); + if (all(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(fast::min(float3(500000.0), _170), _193), _209), _225), _241), _257), _273), _289) < float3(1.0)) && all(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(fast::max(float3(-500000.0), _170), _193), _209), _225), _241), _257), _273), _289) > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&RWShadowTileNumCulledObjects_atomic[(_78 * _Globals.ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc new file mode 100644 index 0000000..ec5d996 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc @@ -0,0 +1,399 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct FVertexFactoryInterpolantsVSToPS +{ + float4 TangentToWorld0; + float4 TangentToWorld2; + float4 Color; + spvUnsafeArray TexCoords; + float4 LightMapCoordinate; + uint PrimitiveId; + uint LightmapDataIndex; +}; + +struct FVertexFactoryInterpolantsVSToDS +{ + FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; +}; + +struct FSharedBasePassInterpolants +{ +}; +struct FBasePassInterpolantsVSToDS +{ + FSharedBasePassInterpolants _m0; +}; + +struct FBasePassVSToDS +{ + FVertexFactoryInterpolantsVSToDS FactoryInterpolants; + FBasePassInterpolantsVSToDS BasePassInterpolants; + float4 Position; +}; + +struct FPNTessellationHSToDS +{ + FBasePassVSToDS PassSpecificData; + spvUnsafeArray WorldPosition; + float3 DisplacementScale; + float TessellationMultiplier; + float WorldDisplacementMultiplier; +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +constant float4 _602 = {}; + +struct main0_out +{ + float4 out_var_COLOR0; + uint out_var_LIGHTMAP_ID; + float3 out_var_PN_DisplacementScales; + spvUnsafeArray out_var_PN_POSITION; + float out_var_PN_TessellationMultiplier; + float out_var_PN_WorldDisplacementMultiplier; + uint out_var_PRIMITIVE_ID; + spvUnsafeArray out_var_TEXCOORD0; + float4 out_var_TEXCOORD10_centroid; + float4 out_var_TEXCOORD11_centroid; + float4 out_var_TEXCOORD4; + float4 out_var_VS_To_DS_Position; +}; + +struct main0_patchOut +{ + float4 out_var_PN_POSITION9; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[attribute(0)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(1)]]; + float4 in_var_COLOR0 [[attribute(2)]]; + float4 in_var_TEXCOORD0_0 [[attribute(3)]]; + float4 in_var_TEXCOORD4 [[attribute(4)]]; + uint in_var_PRIMITIVE_ID [[attribute(5)]]; + uint in_var_LIGHTMAP_ID [[attribute(6)]]; + float4 in_var_VS_To_DS_Position [[attribute(7)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], const device type_StructuredBuffer_v4float& View_PrimitiveSceneData [[buffer(1)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + threadgroup FPNTessellationHSToDS temp_var_hullMainRetVal[3]; + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray _144 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD10_centroid, gl_in[1].in_var_TEXCOORD10_centroid, gl_in[2].in_var_TEXCOORD10_centroid, gl_in[3].in_var_TEXCOORD10_centroid, gl_in[4].in_var_TEXCOORD10_centroid, gl_in[5].in_var_TEXCOORD10_centroid, gl_in[6].in_var_TEXCOORD10_centroid, gl_in[7].in_var_TEXCOORD10_centroid, gl_in[8].in_var_TEXCOORD10_centroid, gl_in[9].in_var_TEXCOORD10_centroid, gl_in[10].in_var_TEXCOORD10_centroid, gl_in[11].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _145 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD11_centroid, gl_in[1].in_var_TEXCOORD11_centroid, gl_in[2].in_var_TEXCOORD11_centroid, gl_in[3].in_var_TEXCOORD11_centroid, gl_in[4].in_var_TEXCOORD11_centroid, gl_in[5].in_var_TEXCOORD11_centroid, gl_in[6].in_var_TEXCOORD11_centroid, gl_in[7].in_var_TEXCOORD11_centroid, gl_in[8].in_var_TEXCOORD11_centroid, gl_in[9].in_var_TEXCOORD11_centroid, gl_in[10].in_var_TEXCOORD11_centroid, gl_in[11].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _146 = spvUnsafeArray({ gl_in[0].in_var_COLOR0, gl_in[1].in_var_COLOR0, gl_in[2].in_var_COLOR0, gl_in[3].in_var_COLOR0, gl_in[4].in_var_COLOR0, gl_in[5].in_var_COLOR0, gl_in[6].in_var_COLOR0, gl_in[7].in_var_COLOR0, gl_in[8].in_var_COLOR0, gl_in[9].in_var_COLOR0, gl_in[10].in_var_COLOR0, gl_in[11].in_var_COLOR0 }); + spvUnsafeArray, 12> _147 = spvUnsafeArray, 12>({ spvUnsafeArray({ gl_in[0].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[1].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[2].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[3].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[4].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[5].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[6].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[7].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[8].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[9].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[10].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[11].in_var_TEXCOORD0_0 }) }); + spvUnsafeArray _148 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD4, gl_in[1].in_var_TEXCOORD4, gl_in[2].in_var_TEXCOORD4, gl_in[3].in_var_TEXCOORD4, gl_in[4].in_var_TEXCOORD4, gl_in[5].in_var_TEXCOORD4, gl_in[6].in_var_TEXCOORD4, gl_in[7].in_var_TEXCOORD4, gl_in[8].in_var_TEXCOORD4, gl_in[9].in_var_TEXCOORD4, gl_in[10].in_var_TEXCOORD4, gl_in[11].in_var_TEXCOORD4 }); + spvUnsafeArray _149 = spvUnsafeArray({ gl_in[0].in_var_PRIMITIVE_ID, gl_in[1].in_var_PRIMITIVE_ID, gl_in[2].in_var_PRIMITIVE_ID, gl_in[3].in_var_PRIMITIVE_ID, gl_in[4].in_var_PRIMITIVE_ID, gl_in[5].in_var_PRIMITIVE_ID, gl_in[6].in_var_PRIMITIVE_ID, gl_in[7].in_var_PRIMITIVE_ID, gl_in[8].in_var_PRIMITIVE_ID, gl_in[9].in_var_PRIMITIVE_ID, gl_in[10].in_var_PRIMITIVE_ID, gl_in[11].in_var_PRIMITIVE_ID }); + spvUnsafeArray _150 = spvUnsafeArray({ gl_in[0].in_var_LIGHTMAP_ID, gl_in[1].in_var_LIGHTMAP_ID, gl_in[2].in_var_LIGHTMAP_ID, gl_in[3].in_var_LIGHTMAP_ID, gl_in[4].in_var_LIGHTMAP_ID, gl_in[5].in_var_LIGHTMAP_ID, gl_in[6].in_var_LIGHTMAP_ID, gl_in[7].in_var_LIGHTMAP_ID, gl_in[8].in_var_LIGHTMAP_ID, gl_in[9].in_var_LIGHTMAP_ID, gl_in[10].in_var_LIGHTMAP_ID, gl_in[11].in_var_LIGHTMAP_ID }); + spvUnsafeArray _259 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_Position, gl_in[1].in_var_VS_To_DS_Position, gl_in[2].in_var_VS_To_DS_Position, gl_in[3].in_var_VS_To_DS_Position, gl_in[4].in_var_VS_To_DS_Position, gl_in[5].in_var_VS_To_DS_Position, gl_in[6].in_var_VS_To_DS_Position, gl_in[7].in_var_VS_To_DS_Position, gl_in[8].in_var_VS_To_DS_Position, gl_in[9].in_var_VS_To_DS_Position, gl_in[10].in_var_VS_To_DS_Position, gl_in[11].in_var_VS_To_DS_Position }); + spvUnsafeArray _284 = spvUnsafeArray({ FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[0], _145[0], _146[0], _147[0], _148[0], _149[0], _150[0] } }, FBasePassInterpolantsVSToDS{ { } }, _259[0] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[1], _145[1], _146[1], _147[1], _148[1], _149[1], _150[1] } }, FBasePassInterpolantsVSToDS{ { } }, _259[1] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[2], _145[2], _146[2], _147[2], _148[2], _149[2], _150[2] } }, FBasePassInterpolantsVSToDS{ { } }, _259[2] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[3], _145[3], _146[3], _147[3], _148[3], _149[3], _150[3] } }, FBasePassInterpolantsVSToDS{ { } }, _259[3] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[4], _145[4], _146[4], _147[4], _148[4], _149[4], _150[4] } }, FBasePassInterpolantsVSToDS{ { } }, _259[4] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[5], _145[5], _146[5], _147[5], _148[5], _149[5], _150[5] } }, FBasePassInterpolantsVSToDS{ { } }, _259[5] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[6], _145[6], _146[6], _147[6], _148[6], _149[6], _150[6] } }, FBasePassInterpolantsVSToDS{ { } }, _259[6] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[7], _145[7], _146[7], _147[7], _148[7], _149[7], _150[7] } }, FBasePassInterpolantsVSToDS{ { } }, _259[7] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[8], _145[8], _146[8], _147[8], _148[8], _149[8], _150[8] } }, FBasePassInterpolantsVSToDS{ { } }, _259[8] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[9], _145[9], _146[9], _147[9], _148[9], _149[9], _150[9] } }, FBasePassInterpolantsVSToDS{ { } }, _259[9] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[10], _145[10], _146[10], _147[10], _148[10], _149[10], _150[10] } }, FBasePassInterpolantsVSToDS{ { } }, _259[10] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[11], _145[11], _146[11], _147[11], _148[11], _149[11], _150[11] } }, FBasePassInterpolantsVSToDS{ { } }, _259[11] } }); + spvUnsafeArray param_var_I; + param_var_I = _284; + float4 _301 = float4(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float3 _310 = View_PrimitiveSceneData._m0[(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.PrimitiveId * 26u) + 22u].xyz * float3x3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz, cross(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz) * float3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.w), param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz); + uint _313 = (gl_InvocationID < 2u) ? (gl_InvocationID + 1u) : 0u; + uint _314 = 2u * gl_InvocationID; + uint _315 = 3u + _314; + uint _316 = _314 + 4u; + float4 _328 = float4(param_var_I[_313].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _336 = float4(param_var_I[_315].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _344 = float4(param_var_I[_316].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + spvUnsafeArray _392 = spvUnsafeArray({ param_var_I[gl_InvocationID].Position, ((((float4(2.0) * param_var_I[gl_InvocationID].Position) + param_var_I[_313].Position) - (float4(dot(param_var_I[_313].Position - param_var_I[gl_InvocationID].Position, _301)) * _301)) + (((float4(2.0) * param_var_I[_315].Position) + param_var_I[_316].Position) - (float4(dot(param_var_I[_316].Position - param_var_I[_315].Position, _336)) * _336))) * float4(0.16666667163372039794921875), ((((float4(2.0) * param_var_I[_313].Position) + param_var_I[gl_InvocationID].Position) - (float4(dot(param_var_I[gl_InvocationID].Position - param_var_I[_313].Position, _328)) * _328)) + (((float4(2.0) * param_var_I[_316].Position) + param_var_I[_315].Position) - (float4(dot(param_var_I[_315].Position - param_var_I[_316].Position, _344)) * _344))) * float4(0.16666667163372039794921875) }); + gl_out[gl_InvocationID].out_var_TEXCOORD10_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + gl_out[gl_InvocationID].out_var_TEXCOORD11_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + gl_out[gl_InvocationID].out_var_COLOR0 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.Color; + gl_out[gl_InvocationID].out_var_TEXCOORD0 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TexCoords; + gl_out[gl_InvocationID].out_var_TEXCOORD4 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.LightMapCoordinate; + gl_out[gl_InvocationID].out_var_PRIMITIVE_ID = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.PrimitiveId; + gl_out[gl_InvocationID].out_var_LIGHTMAP_ID = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.LightmapDataIndex; + gl_out[gl_InvocationID].out_var_VS_To_DS_Position = param_var_I[gl_InvocationID].Position; + gl_out[gl_InvocationID].out_var_PN_POSITION = _392; + gl_out[gl_InvocationID].out_var_PN_DisplacementScales = _310; + gl_out[gl_InvocationID].out_var_PN_TessellationMultiplier = 1.0; + gl_out[gl_InvocationID].out_var_PN_WorldDisplacementMultiplier = 1.0; + temp_var_hullMainRetVal[gl_InvocationID] = FPNTessellationHSToDS{ param_var_I[gl_InvocationID], _392, _310, 1.0, 1.0 }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0u) + { + float4 _450 = (((((temp_var_hullMainRetVal[0u].WorldPosition[1] + temp_var_hullMainRetVal[0u].WorldPosition[2]) + temp_var_hullMainRetVal[1u].WorldPosition[1]) + temp_var_hullMainRetVal[1u].WorldPosition[2]) + temp_var_hullMainRetVal[2u].WorldPosition[1]) + temp_var_hullMainRetVal[2u].WorldPosition[2]) * float4(0.16666667163372039794921875); + float4 _463 = _602; + _463.x = 0.5 * (temp_var_hullMainRetVal[1u].TessellationMultiplier + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _469 = _463; + _469.y = 0.5 * (temp_var_hullMainRetVal[2u].TessellationMultiplier + temp_var_hullMainRetVal[0u].TessellationMultiplier); + float4 _474 = _469; + _474.z = 0.5 * (temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier); + float4 _481 = _474; + _481.w = 0.333000004291534423828125 * ((temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier) + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _589; + for (;;) + { + float4 _489 = View.View_ViewToClip * float4(0.0); + float4 _494 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[0u].WorldPosition[0].xyz, 1.0); + float3 _495 = _494.xyz; + float3 _496 = _489.xyz; + float _498 = _494.w; + float _499 = _489.w; + float4 _516 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[1u].WorldPosition[0].xyz, 1.0); + float3 _517 = _516.xyz; + float _519 = _516.w; + float4 _537 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[2u].WorldPosition[0].xyz, 1.0); + float3 _538 = _537.xyz; + float _540 = _537.w; + if (any((((select(int3(0), int3(1), (_495 - _496) < float3(_498 + _499)) + (int3(2) * select(int3(0), int3(1), (_495 + _496) > float3((-_498) - _499)))) | (select(int3(0), int3(1), (_517 - _496) < float3(_519 + _499)) + (int3(2) * select(int3(0), int3(1), (_517 + _496) > float3((-_519) - _499))))) | (select(int3(0), int3(1), (_538 - _496) < float3(_540 + _499)) + (int3(2) * select(int3(0), int3(1), (_538 + _496) > float3((-_540) - _499))))) != int3(3))) + { + _589 = float4(0.0); + break; + } + float3 _558 = temp_var_hullMainRetVal[0u].WorldPosition[0].xyz - temp_var_hullMainRetVal[1u].WorldPosition[0].xyz; + float3 _559 = temp_var_hullMainRetVal[1u].WorldPosition[0].xyz - temp_var_hullMainRetVal[2u].WorldPosition[0].xyz; + float3 _560 = temp_var_hullMainRetVal[2u].WorldPosition[0].xyz - temp_var_hullMainRetVal[0u].WorldPosition[0].xyz; + float3 _563 = (float3(0.5) * (temp_var_hullMainRetVal[0u].WorldPosition[0].xyz + temp_var_hullMainRetVal[1u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _566 = (float3(0.5) * (temp_var_hullMainRetVal[1u].WorldPosition[0].xyz + temp_var_hullMainRetVal[2u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _569 = (float3(0.5) * (temp_var_hullMainRetVal[2u].WorldPosition[0].xyz + temp_var_hullMainRetVal[0u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float _573 = sqrt(dot(_559, _559) / dot(_566, _566)); + float _577 = sqrt(dot(_560, _560) / dot(_569, _569)); + float _581 = sqrt(dot(_558, _558) / dot(_563, _563)); + float4 _586 = float4(_573, _577, _581, 1.0); + _586.w = 0.333000004291534423828125 * ((_573 + _577) + _581); + _589 = float4(View.View_AdaptiveTessellationFactor) * _586; + break; + } + float4 _591 = fast::clamp(_481 * _589, float4(1.0), float4(15.0)); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0u] = half(_591.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1u] = half(_591.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2u] = half(_591.z); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_591.w); + patchOut.out_var_PN_POSITION9 = _450 + ((_450 - (((temp_var_hullMainRetVal[2u].WorldPosition[0] + temp_var_hullMainRetVal[1u].WorldPosition[0]) + temp_var_hullMainRetVal[0u].WorldPosition[0]) * float4(0.3333333432674407958984375))) * float4(0.5)); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc new file mode 100644 index 0000000..848aa9a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc @@ -0,0 +1,467 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct FVertexFactoryInterpolantsVSToPS +{ + float4 TangentToWorld0; + float4 TangentToWorld2; +}; + +struct FVertexFactoryInterpolantsVSToDS +{ + FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; +}; + +struct FHitProxyVSToDS +{ + FVertexFactoryInterpolantsVSToDS FactoryInterpolants; + float4 Position; + uint VertexID; +}; + +struct FHullShaderConstantDominantVertexData +{ + float2 UV; + float4 Normal; + float3 Tangent; +}; + +struct FHullShaderConstantDominantEdgeData +{ + float2 UV0; + float2 UV1; + float4 Normal0; + float4 Normal1; + float3 Tangent0; + float3 Tangent1; +}; + +struct FPNTessellationHSToDS +{ + FHitProxyVSToDS PassSpecificData; + spvUnsafeArray WorldPosition; + float3 DisplacementScale; + float TessellationMultiplier; + float WorldDisplacementMultiplier; + FHullShaderConstantDominantVertexData DominantVertex; + FHullShaderConstantDominantEdgeData DominantEdge; +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_DrawsVelocity; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + uint Primitive_LightingChannelMask; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightmapDataIndex; + packed_float3 Primitive_PreSkinnedLocalBounds; + int Primitive_SingleCaptureIndex; + uint Primitive_OutputVelocity; + uint PrePadding_Primitive_420; + uint PrePadding_Primitive_424; + uint PrePadding_Primitive_428; + float4 Primitive_CustomPrimitiveData[4]; +}; + +constant float4 _613 = {}; + +struct main0_out +{ + float3 out_var_PN_DisplacementScales; + float2 out_var_PN_DominantEdge; + float2 out_var_PN_DominantEdge1; + float4 out_var_PN_DominantEdge2; + float4 out_var_PN_DominantEdge3; + float3 out_var_PN_DominantEdge4; + float3 out_var_PN_DominantEdge5; + float2 out_var_PN_DominantVertex; + float4 out_var_PN_DominantVertex1; + float3 out_var_PN_DominantVertex2; + spvUnsafeArray out_var_PN_POSITION; + float out_var_PN_TessellationMultiplier; + float out_var_PN_WorldDisplacementMultiplier; + float4 out_var_TEXCOORD10_centroid; + float4 out_var_TEXCOORD11_centroid; + float4 out_var_VS_To_DS_Position; + uint out_var_VS_To_DS_VertexID; +}; + +struct main0_patchOut +{ + float4 out_var_PN_POSITION9; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[attribute(0)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(1)]]; + float4 in_var_VS_To_DS_Position [[attribute(2)]]; + uint in_var_VS_To_DS_VertexID [[attribute(3)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_Primitive& Primitive [[buffer(1)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + threadgroup FPNTessellationHSToDS temp_var_hullMainRetVal[3]; + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray _142 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD10_centroid, gl_in[1].in_var_TEXCOORD10_centroid, gl_in[2].in_var_TEXCOORD10_centroid, gl_in[3].in_var_TEXCOORD10_centroid, gl_in[4].in_var_TEXCOORD10_centroid, gl_in[5].in_var_TEXCOORD10_centroid, gl_in[6].in_var_TEXCOORD10_centroid, gl_in[7].in_var_TEXCOORD10_centroid, gl_in[8].in_var_TEXCOORD10_centroid, gl_in[9].in_var_TEXCOORD10_centroid, gl_in[10].in_var_TEXCOORD10_centroid, gl_in[11].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _143 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD11_centroid, gl_in[1].in_var_TEXCOORD11_centroid, gl_in[2].in_var_TEXCOORD11_centroid, gl_in[3].in_var_TEXCOORD11_centroid, gl_in[4].in_var_TEXCOORD11_centroid, gl_in[5].in_var_TEXCOORD11_centroid, gl_in[6].in_var_TEXCOORD11_centroid, gl_in[7].in_var_TEXCOORD11_centroid, gl_in[8].in_var_TEXCOORD11_centroid, gl_in[9].in_var_TEXCOORD11_centroid, gl_in[10].in_var_TEXCOORD11_centroid, gl_in[11].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _192 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_Position, gl_in[1].in_var_VS_To_DS_Position, gl_in[2].in_var_VS_To_DS_Position, gl_in[3].in_var_VS_To_DS_Position, gl_in[4].in_var_VS_To_DS_Position, gl_in[5].in_var_VS_To_DS_Position, gl_in[6].in_var_VS_To_DS_Position, gl_in[7].in_var_VS_To_DS_Position, gl_in[8].in_var_VS_To_DS_Position, gl_in[9].in_var_VS_To_DS_Position, gl_in[10].in_var_VS_To_DS_Position, gl_in[11].in_var_VS_To_DS_Position }); + spvUnsafeArray _193 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_VertexID, gl_in[1].in_var_VS_To_DS_VertexID, gl_in[2].in_var_VS_To_DS_VertexID, gl_in[3].in_var_VS_To_DS_VertexID, gl_in[4].in_var_VS_To_DS_VertexID, gl_in[5].in_var_VS_To_DS_VertexID, gl_in[6].in_var_VS_To_DS_VertexID, gl_in[7].in_var_VS_To_DS_VertexID, gl_in[8].in_var_VS_To_DS_VertexID, gl_in[9].in_var_VS_To_DS_VertexID, gl_in[10].in_var_VS_To_DS_VertexID, gl_in[11].in_var_VS_To_DS_VertexID }); + spvUnsafeArray _230 = spvUnsafeArray({ FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[0], _143[0] } }, _192[0], _193[0] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[1], _143[1] } }, _192[1], _193[1] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[2], _143[2] } }, _192[2], _193[2] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[3], _143[3] } }, _192[3], _193[3] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[4], _143[4] } }, _192[4], _193[4] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[5], _143[5] } }, _192[5], _193[5] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[6], _143[6] } }, _192[6], _193[6] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[7], _143[7] } }, _192[7], _193[7] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[8], _143[8] } }, _192[8], _193[8] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[9], _143[9] } }, _192[9], _193[9] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[10], _143[10] } }, _192[10], _193[10] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[11], _143[11] } }, _192[11], _193[11] } }); + spvUnsafeArray param_var_I; + param_var_I = _230; + float4 _247 = float4(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float3 _251 = Primitive.Primitive_NonUniformScale.xyz * float3x3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz, cross(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz) * float3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.w), param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz); + uint _254 = (gl_InvocationID < 2u) ? (gl_InvocationID + 1u) : 0u; + uint _255 = 2u * gl_InvocationID; + uint _256 = 3u + _255; + uint _257 = _255 + 4u; + uint _264 = (_254 < 2u) ? (_254 + 1u) : 0u; + uint _265 = 2u * _254; + uint _266 = 3u + _265; + uint _267 = _265 + 4u; + float4 _279 = float4(param_var_I[9u + gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _315; + float4 _316; + float4 _317; + float4 _318; + if ((param_var_I[_266].VertexID < param_var_I[_254].VertexID) || ((param_var_I[_266].VertexID == param_var_I[_254].VertexID) && (param_var_I[_267].VertexID < param_var_I[_264].VertexID))) + { + _315 = param_var_I[_267].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + _316 = param_var_I[_267].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + _317 = param_var_I[_266].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + _318 = param_var_I[_266].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + } + else + { + _315 = param_var_I[_264].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + _316 = param_var_I[_264].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + _317 = param_var_I[_254].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + _318 = param_var_I[_254].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + } + float4 _324 = float4(_318.xyz, 0.0); + float4 _328 = float4(_316.xyz, 0.0); + float4 _336 = float4(param_var_I[_254].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _344 = float4(param_var_I[_256].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _352 = float4(param_var_I[_257].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + spvUnsafeArray _402 = spvUnsafeArray({ param_var_I[gl_InvocationID].Position, ((((float4(2.0) * param_var_I[gl_InvocationID].Position) + param_var_I[_254].Position) - (float4(dot(param_var_I[_254].Position - param_var_I[gl_InvocationID].Position, _247)) * _247)) + (((float4(2.0) * param_var_I[_256].Position) + param_var_I[_257].Position) - (float4(dot(param_var_I[_257].Position - param_var_I[_256].Position, _344)) * _344))) * float4(0.16666667163372039794921875), ((((float4(2.0) * param_var_I[_254].Position) + param_var_I[gl_InvocationID].Position) - (float4(dot(param_var_I[gl_InvocationID].Position - param_var_I[_254].Position, _336)) * _336)) + (((float4(2.0) * param_var_I[_257].Position) + param_var_I[_256].Position) - (float4(dot(param_var_I[_256].Position - param_var_I[_257].Position, _352)) * _352))) * float4(0.16666667163372039794921875) }); + gl_out[gl_InvocationID].out_var_TEXCOORD10_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + gl_out[gl_InvocationID].out_var_TEXCOORD11_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + gl_out[gl_InvocationID].out_var_VS_To_DS_Position = param_var_I[gl_InvocationID].Position; + gl_out[gl_InvocationID].out_var_VS_To_DS_VertexID = param_var_I[gl_InvocationID].VertexID; + gl_out[gl_InvocationID].out_var_PN_POSITION = _402; + gl_out[gl_InvocationID].out_var_PN_DisplacementScales = _251; + gl_out[gl_InvocationID].out_var_PN_TessellationMultiplier = 1.0; + gl_out[gl_InvocationID].out_var_PN_WorldDisplacementMultiplier = 1.0; + gl_out[gl_InvocationID].out_var_PN_DominantVertex = float2(0.0); + gl_out[gl_InvocationID].out_var_PN_DominantVertex1 = _279; + gl_out[gl_InvocationID].out_var_PN_DominantVertex2 = param_var_I[9u + gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz; + gl_out[gl_InvocationID].out_var_PN_DominantEdge = float2(0.0); + gl_out[gl_InvocationID].out_var_PN_DominantEdge1 = float2(0.0); + gl_out[gl_InvocationID].out_var_PN_DominantEdge2 = _324; + gl_out[gl_InvocationID].out_var_PN_DominantEdge3 = _328; + gl_out[gl_InvocationID].out_var_PN_DominantEdge4 = _317.xyz; + gl_out[gl_InvocationID].out_var_PN_DominantEdge5 = _315.xyz; + temp_var_hullMainRetVal[gl_InvocationID] = FPNTessellationHSToDS{ param_var_I[gl_InvocationID], _402, _251, 1.0, 1.0, FHullShaderConstantDominantVertexData{ float2(0.0), _279, param_var_I[9u + gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz }, FHullShaderConstantDominantEdgeData{ float2(0.0), float2(0.0), _324, _328, _317.xyz, _315.xyz } }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0u) + { + float4 _461 = (((((temp_var_hullMainRetVal[0u].WorldPosition[1] + temp_var_hullMainRetVal[0u].WorldPosition[2]) + temp_var_hullMainRetVal[1u].WorldPosition[1]) + temp_var_hullMainRetVal[1u].WorldPosition[2]) + temp_var_hullMainRetVal[2u].WorldPosition[1]) + temp_var_hullMainRetVal[2u].WorldPosition[2]) * float4(0.16666667163372039794921875); + float4 _474 = _613; + _474.x = 0.5 * (temp_var_hullMainRetVal[1u].TessellationMultiplier + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _480 = _474; + _480.y = 0.5 * (temp_var_hullMainRetVal[2u].TessellationMultiplier + temp_var_hullMainRetVal[0u].TessellationMultiplier); + float4 _485 = _480; + _485.z = 0.5 * (temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier); + float4 _492 = _485; + _492.w = 0.333000004291534423828125 * ((temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier) + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _600; + for (;;) + { + float4 _500 = View.View_ViewToClip * float4(0.0); + float4 _505 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[0u].WorldPosition[0].xyz, 1.0); + float3 _506 = _505.xyz; + float3 _507 = _500.xyz; + float _509 = _505.w; + float _510 = _500.w; + float4 _527 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[1u].WorldPosition[0].xyz, 1.0); + float3 _528 = _527.xyz; + float _530 = _527.w; + float4 _548 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[2u].WorldPosition[0].xyz, 1.0); + float3 _549 = _548.xyz; + float _551 = _548.w; + if (any((((select(int3(0), int3(1), (_506 - _507) < float3(_509 + _510)) + (int3(2) * select(int3(0), int3(1), (_506 + _507) > float3((-_509) - _510)))) | (select(int3(0), int3(1), (_528 - _507) < float3(_530 + _510)) + (int3(2) * select(int3(0), int3(1), (_528 + _507) > float3((-_530) - _510))))) | (select(int3(0), int3(1), (_549 - _507) < float3(_551 + _510)) + (int3(2) * select(int3(0), int3(1), (_549 + _507) > float3((-_551) - _510))))) != int3(3))) + { + _600 = float4(0.0); + break; + } + float3 _569 = temp_var_hullMainRetVal[0u].WorldPosition[0].xyz - temp_var_hullMainRetVal[1u].WorldPosition[0].xyz; + float3 _570 = temp_var_hullMainRetVal[1u].WorldPosition[0].xyz - temp_var_hullMainRetVal[2u].WorldPosition[0].xyz; + float3 _571 = temp_var_hullMainRetVal[2u].WorldPosition[0].xyz - temp_var_hullMainRetVal[0u].WorldPosition[0].xyz; + float3 _574 = (float3(0.5) * (temp_var_hullMainRetVal[0u].WorldPosition[0].xyz + temp_var_hullMainRetVal[1u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _577 = (float3(0.5) * (temp_var_hullMainRetVal[1u].WorldPosition[0].xyz + temp_var_hullMainRetVal[2u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _580 = (float3(0.5) * (temp_var_hullMainRetVal[2u].WorldPosition[0].xyz + temp_var_hullMainRetVal[0u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float _584 = sqrt(dot(_570, _570) / dot(_577, _577)); + float _588 = sqrt(dot(_571, _571) / dot(_580, _580)); + float _592 = sqrt(dot(_569, _569) / dot(_574, _574)); + float4 _597 = float4(_584, _588, _592, 1.0); + _597.w = 0.333000004291534423828125 * ((_584 + _588) + _592); + _600 = float4(View.View_AdaptiveTessellationFactor) * _597; + break; + } + float4 _602 = fast::clamp(_492 * _600, float4(1.0), float4(15.0)); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0u] = half(_602.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1u] = half(_602.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2u] = half(_602.z); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_602.w); + patchOut.out_var_PN_POSITION9 = _461 + ((_461 - (((temp_var_hullMainRetVal[2u].WorldPosition[0] + temp_var_hullMainRetVal[1u].WorldPosition[0]) + temp_var_hullMainRetVal[0u].WorldPosition[0]) * float4(0.3333333432674407958984375))) * float4(0.5)); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc new file mode 100644 index 0000000..674992c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc @@ -0,0 +1,411 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct FVertexFactoryInterpolantsVSToPS +{ + float4 TangentToWorld0; + float4 TangentToWorld2; + float4 Color; + spvUnsafeArray TexCoords; +}; + +struct FVertexFactoryInterpolantsVSToDS +{ + FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; +}; + +struct FHitProxyVSToDS +{ + FVertexFactoryInterpolantsVSToDS FactoryInterpolants; + float4 Position; +}; + +struct FPNTessellationHSToDS +{ + FHitProxyVSToDS PassSpecificData; + spvUnsafeArray WorldPosition; + float3 DisplacementScale; + float TessellationMultiplier; + float WorldDisplacementMultiplier; +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_DrawsVelocity; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + uint Primitive_LightingChannelMask; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightmapDataIndex; + packed_float3 Primitive_PreSkinnedLocalBounds; + int Primitive_SingleCaptureIndex; + uint Primitive_OutputVelocity; + uint PrePadding_Primitive_420; + uint PrePadding_Primitive_424; + uint PrePadding_Primitive_428; + float4 Primitive_CustomPrimitiveData[4]; +}; + +constant float4 _537 = {}; + +struct main0_out +{ + float4 out_var_COLOR0; + float3 out_var_PN_DisplacementScales; + spvUnsafeArray out_var_PN_POSITION; + float out_var_PN_TessellationMultiplier; + float out_var_PN_WorldDisplacementMultiplier; + spvUnsafeArray out_var_TEXCOORD0; + float4 out_var_TEXCOORD10_centroid; + float4 out_var_TEXCOORD11_centroid; + float4 out_var_VS_To_DS_Position; +}; + +struct main0_patchOut +{ + float4 out_var_PN_POSITION9; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[attribute(0)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(1)]]; + float4 in_var_COLOR0 [[attribute(2)]]; + float2 in_var_TEXCOORD0_0 [[attribute(3)]]; + float2 in_var_TEXCOORD0_1 [[attribute(4)]]; + float4 in_var_VS_To_DS_Position [[attribute(5)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_Primitive& Primitive [[buffer(1)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + threadgroup FPNTessellationHSToDS temp_var_hullMainRetVal[3]; + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray _129 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD10_centroid, gl_in[1].in_var_TEXCOORD10_centroid, gl_in[2].in_var_TEXCOORD10_centroid, gl_in[3].in_var_TEXCOORD10_centroid, gl_in[4].in_var_TEXCOORD10_centroid, gl_in[5].in_var_TEXCOORD10_centroid, gl_in[6].in_var_TEXCOORD10_centroid, gl_in[7].in_var_TEXCOORD10_centroid, gl_in[8].in_var_TEXCOORD10_centroid, gl_in[9].in_var_TEXCOORD10_centroid, gl_in[10].in_var_TEXCOORD10_centroid, gl_in[11].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _130 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD11_centroid, gl_in[1].in_var_TEXCOORD11_centroid, gl_in[2].in_var_TEXCOORD11_centroid, gl_in[3].in_var_TEXCOORD11_centroid, gl_in[4].in_var_TEXCOORD11_centroid, gl_in[5].in_var_TEXCOORD11_centroid, gl_in[6].in_var_TEXCOORD11_centroid, gl_in[7].in_var_TEXCOORD11_centroid, gl_in[8].in_var_TEXCOORD11_centroid, gl_in[9].in_var_TEXCOORD11_centroid, gl_in[10].in_var_TEXCOORD11_centroid, gl_in[11].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _131 = spvUnsafeArray({ gl_in[0].in_var_COLOR0, gl_in[1].in_var_COLOR0, gl_in[2].in_var_COLOR0, gl_in[3].in_var_COLOR0, gl_in[4].in_var_COLOR0, gl_in[5].in_var_COLOR0, gl_in[6].in_var_COLOR0, gl_in[7].in_var_COLOR0, gl_in[8].in_var_COLOR0, gl_in[9].in_var_COLOR0, gl_in[10].in_var_COLOR0, gl_in[11].in_var_COLOR0 }); + spvUnsafeArray, 12> _132 = spvUnsafeArray, 12>({ spvUnsafeArray({ gl_in[0].in_var_TEXCOORD0_0, gl_in[0].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[1].in_var_TEXCOORD0_0, gl_in[1].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[2].in_var_TEXCOORD0_0, gl_in[2].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[3].in_var_TEXCOORD0_0, gl_in[3].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[4].in_var_TEXCOORD0_0, gl_in[4].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[5].in_var_TEXCOORD0_0, gl_in[5].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[6].in_var_TEXCOORD0_0, gl_in[6].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[7].in_var_TEXCOORD0_0, gl_in[7].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[8].in_var_TEXCOORD0_0, gl_in[8].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[9].in_var_TEXCOORD0_0, gl_in[9].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[10].in_var_TEXCOORD0_0, gl_in[10].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[11].in_var_TEXCOORD0_0, gl_in[11].in_var_TEXCOORD0_1 }) }); + spvUnsafeArray _205 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_Position, gl_in[1].in_var_VS_To_DS_Position, gl_in[2].in_var_VS_To_DS_Position, gl_in[3].in_var_VS_To_DS_Position, gl_in[4].in_var_VS_To_DS_Position, gl_in[5].in_var_VS_To_DS_Position, gl_in[6].in_var_VS_To_DS_Position, gl_in[7].in_var_VS_To_DS_Position, gl_in[8].in_var_VS_To_DS_Position, gl_in[9].in_var_VS_To_DS_Position, gl_in[10].in_var_VS_To_DS_Position, gl_in[11].in_var_VS_To_DS_Position }); + spvUnsafeArray _230 = spvUnsafeArray({ FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[0], _130[0], _131[0], _132[0] } }, _205[0] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[1], _130[1], _131[1], _132[1] } }, _205[1] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[2], _130[2], _131[2], _132[2] } }, _205[2] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[3], _130[3], _131[3], _132[3] } }, _205[3] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[4], _130[4], _131[4], _132[4] } }, _205[4] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[5], _130[5], _131[5], _132[5] } }, _205[5] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[6], _130[6], _131[6], _132[6] } }, _205[6] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[7], _130[7], _131[7], _132[7] } }, _205[7] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[8], _130[8], _131[8], _132[8] } }, _205[8] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[9], _130[9], _131[9], _132[9] } }, _205[9] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[10], _130[10], _131[10], _132[10] } }, _205[10] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[11], _130[11], _131[11], _132[11] } }, _205[11] } }); + spvUnsafeArray param_var_I; + param_var_I = _230; + float4 _247 = float4(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float3 _251 = Primitive.Primitive_NonUniformScale.xyz * float3x3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz, cross(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz) * float3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.w), param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz); + uint _254 = (gl_InvocationID < 2u) ? (gl_InvocationID + 1u) : 0u; + uint _255 = 2u * gl_InvocationID; + uint _256 = 3u + _255; + uint _257 = _255 + 4u; + float4 _269 = float4(param_var_I[_254].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _277 = float4(param_var_I[_256].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _285 = float4(param_var_I[_257].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + spvUnsafeArray _333 = spvUnsafeArray({ param_var_I[gl_InvocationID].Position, ((((float4(2.0) * param_var_I[gl_InvocationID].Position) + param_var_I[_254].Position) - (float4(dot(param_var_I[_254].Position - param_var_I[gl_InvocationID].Position, _247)) * _247)) + (((float4(2.0) * param_var_I[_256].Position) + param_var_I[_257].Position) - (float4(dot(param_var_I[_257].Position - param_var_I[_256].Position, _277)) * _277))) * float4(0.16666667163372039794921875), ((((float4(2.0) * param_var_I[_254].Position) + param_var_I[gl_InvocationID].Position) - (float4(dot(param_var_I[gl_InvocationID].Position - param_var_I[_254].Position, _269)) * _269)) + (((float4(2.0) * param_var_I[_257].Position) + param_var_I[_256].Position) - (float4(dot(param_var_I[_256].Position - param_var_I[_257].Position, _285)) * _285))) * float4(0.16666667163372039794921875) }); + gl_out[gl_InvocationID].out_var_TEXCOORD10_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + gl_out[gl_InvocationID].out_var_TEXCOORD11_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + gl_out[gl_InvocationID].out_var_COLOR0 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.Color; + gl_out[gl_InvocationID].out_var_TEXCOORD0 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TexCoords; + gl_out[gl_InvocationID].out_var_VS_To_DS_Position = param_var_I[gl_InvocationID].Position; + gl_out[gl_InvocationID].out_var_PN_POSITION = _333; + gl_out[gl_InvocationID].out_var_PN_DisplacementScales = _251; + gl_out[gl_InvocationID].out_var_PN_TessellationMultiplier = 1.0; + gl_out[gl_InvocationID].out_var_PN_WorldDisplacementMultiplier = 1.0; + temp_var_hullMainRetVal[gl_InvocationID] = FPNTessellationHSToDS{ param_var_I[gl_InvocationID], _333, _251, 1.0, 1.0 }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0u) + { + float4 _385 = (((((temp_var_hullMainRetVal[0u].WorldPosition[1] + temp_var_hullMainRetVal[0u].WorldPosition[2]) + temp_var_hullMainRetVal[1u].WorldPosition[1]) + temp_var_hullMainRetVal[1u].WorldPosition[2]) + temp_var_hullMainRetVal[2u].WorldPosition[1]) + temp_var_hullMainRetVal[2u].WorldPosition[2]) * float4(0.16666667163372039794921875); + float4 _398 = _537; + _398.x = 0.5 * (temp_var_hullMainRetVal[1u].TessellationMultiplier + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _404 = _398; + _404.y = 0.5 * (temp_var_hullMainRetVal[2u].TessellationMultiplier + temp_var_hullMainRetVal[0u].TessellationMultiplier); + float4 _409 = _404; + _409.z = 0.5 * (temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier); + float4 _416 = _409; + _416.w = 0.333000004291534423828125 * ((temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier) + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _524; + for (;;) + { + float4 _424 = View.View_ViewToClip * float4(0.0); + float4 _429 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[0u].WorldPosition[0].xyz, 1.0); + float3 _430 = _429.xyz; + float3 _431 = _424.xyz; + float _433 = _429.w; + float _434 = _424.w; + float4 _451 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[1u].WorldPosition[0].xyz, 1.0); + float3 _452 = _451.xyz; + float _454 = _451.w; + float4 _472 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[2u].WorldPosition[0].xyz, 1.0); + float3 _473 = _472.xyz; + float _475 = _472.w; + if (any((((select(int3(0), int3(1), (_430 - _431) < float3(_433 + _434)) + (int3(2) * select(int3(0), int3(1), (_430 + _431) > float3((-_433) - _434)))) | (select(int3(0), int3(1), (_452 - _431) < float3(_454 + _434)) + (int3(2) * select(int3(0), int3(1), (_452 + _431) > float3((-_454) - _434))))) | (select(int3(0), int3(1), (_473 - _431) < float3(_475 + _434)) + (int3(2) * select(int3(0), int3(1), (_473 + _431) > float3((-_475) - _434))))) != int3(3))) + { + _524 = float4(0.0); + break; + } + float3 _493 = temp_var_hullMainRetVal[0u].WorldPosition[0].xyz - temp_var_hullMainRetVal[1u].WorldPosition[0].xyz; + float3 _494 = temp_var_hullMainRetVal[1u].WorldPosition[0].xyz - temp_var_hullMainRetVal[2u].WorldPosition[0].xyz; + float3 _495 = temp_var_hullMainRetVal[2u].WorldPosition[0].xyz - temp_var_hullMainRetVal[0u].WorldPosition[0].xyz; + float3 _498 = (float3(0.5) * (temp_var_hullMainRetVal[0u].WorldPosition[0].xyz + temp_var_hullMainRetVal[1u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _501 = (float3(0.5) * (temp_var_hullMainRetVal[1u].WorldPosition[0].xyz + temp_var_hullMainRetVal[2u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _504 = (float3(0.5) * (temp_var_hullMainRetVal[2u].WorldPosition[0].xyz + temp_var_hullMainRetVal[0u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float _508 = sqrt(dot(_494, _494) / dot(_501, _501)); + float _512 = sqrt(dot(_495, _495) / dot(_504, _504)); + float _516 = sqrt(dot(_493, _493) / dot(_498, _498)); + float4 _521 = float4(_508, _512, _516, 1.0); + _521.w = 0.333000004291534423828125 * ((_508 + _512) + _516); + _524 = float4(View.View_AdaptiveTessellationFactor) * _521; + break; + } + float4 _526 = fast::clamp(_416 * _524, float4(1.0), float4(15.0)); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0u] = half(_526.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1u] = half(_526.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2u] = half(_526.z); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_526.w); + patchOut.out_var_PN_POSITION9 = _385 + ((_385 - (((temp_var_hullMainRetVal[2u].WorldPosition[0] + temp_var_hullMainRetVal[1u].WorldPosition[0]) + temp_var_hullMainRetVal[0u].WorldPosition[0]) * float4(0.3333333432674407958984375))) * float4(0.5)); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc new file mode 100644 index 0000000..13ea8bb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc @@ -0,0 +1,178 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct FVertexFactoryInterpolantsVSToPS +{ + float4 TangentToWorld0; + float4 TangentToWorld2; +}; + +struct FVertexFactoryInterpolantsVSToDS +{ + FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; +}; + +struct FSharedBasePassInterpolants +{ +}; +struct FBasePassInterpolantsVSToDS +{ + FSharedBasePassInterpolants _m0; +}; + +struct FBasePassVSToDS +{ + FVertexFactoryInterpolantsVSToDS FactoryInterpolants; + FBasePassInterpolantsVSToDS BasePassInterpolants; + float4 Position; +}; + +struct FFlatTessellationHSToDS +{ + FBasePassVSToDS PassSpecificData; + float3 DisplacementScale; + float TessellationMultiplier; + float WorldDisplacementMultiplier; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_DrawsVelocity; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + uint Primitive_LightingChannelMask; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightmapDataIndex; + packed_float3 Primitive_PreSkinnedLocalBounds; + int Primitive_SingleCaptureIndex; + uint Primitive_OutputVelocity; + uint PrePadding_Primitive_420; + uint PrePadding_Primitive_424; + uint PrePadding_Primitive_428; + float4 Primitive_CustomPrimitiveData[4]; +}; + +struct type_Material +{ + float4 Material_VectorExpressions[3]; + float4 Material_ScalarExpressions[1]; +}; + +constant float4 _182 = {}; + +struct main0_out +{ + float3 out_var_Flat_DisplacementScales; + float out_var_Flat_TessellationMultiplier; + float out_var_Flat_WorldDisplacementMultiplier; + float4 out_var_TEXCOORD10_centroid; + float4 out_var_TEXCOORD11_centroid; + float4 out_var_VS_To_DS_Position; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[attribute(0)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(1)]]; + float4 in_var_VS_To_DS_Position [[attribute(2)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant type_Primitive& Primitive [[buffer(0)]], constant type_Material& Material [[buffer(1)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + threadgroup FFlatTessellationHSToDS temp_var_hullMainRetVal[3]; + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray _90 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD10_centroid, gl_in[1].in_var_TEXCOORD10_centroid, gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _91 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD11_centroid, gl_in[1].in_var_TEXCOORD11_centroid, gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _104 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_Position, gl_in[1].in_var_VS_To_DS_Position, gl_in[2].in_var_VS_To_DS_Position }); + spvUnsafeArray _111 = spvUnsafeArray({ FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _90[0], _91[0] } }, FBasePassInterpolantsVSToDS{ { } }, _104[0] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _90[1], _91[1] } }, FBasePassInterpolantsVSToDS{ { } }, _104[1] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _90[2], _91[2] } }, FBasePassInterpolantsVSToDS{ { } }, _104[2] } }); + spvUnsafeArray param_var_I; + param_var_I = _111; + float3 _128 = Primitive.Primitive_NonUniformScale.xyz * float3x3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz, cross(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz) * float3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.w), param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz); + gl_out[gl_InvocationID].out_var_TEXCOORD10_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + gl_out[gl_InvocationID].out_var_TEXCOORD11_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + gl_out[gl_InvocationID].out_var_VS_To_DS_Position = param_var_I[gl_InvocationID].Position; + gl_out[gl_InvocationID].out_var_Flat_DisplacementScales = _128; + gl_out[gl_InvocationID].out_var_Flat_TessellationMultiplier = Material.Material_ScalarExpressions[0].x; + gl_out[gl_InvocationID].out_var_Flat_WorldDisplacementMultiplier = 1.0; + temp_var_hullMainRetVal[gl_InvocationID] = FFlatTessellationHSToDS{ param_var_I[gl_InvocationID], _128, Material.Material_ScalarExpressions[0].x, 1.0 }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0u) + { + float4 _154 = _182; + _154.x = 0.5 * (temp_var_hullMainRetVal[1u].TessellationMultiplier + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _160 = _154; + _160.y = 0.5 * (temp_var_hullMainRetVal[2u].TessellationMultiplier + temp_var_hullMainRetVal[0u].TessellationMultiplier); + float4 _165 = _160; + _165.z = 0.5 * (temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier); + float4 _172 = _165; + _172.w = 0.333000004291534423828125 * ((temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier) + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _173 = fast::clamp(_172, float4(1.0), float4(15.0)); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0u] = half(_173.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1u] = half(_173.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2u] = half(_173.z); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_173.w); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese new file mode 100644 index 0000000..19e402e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese @@ -0,0 +1,419 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_ShadowDepthPass +{ + float PrePadding_ShadowDepthPass_LPV_0; + float PrePadding_ShadowDepthPass_LPV_4; + float PrePadding_ShadowDepthPass_LPV_8; + float PrePadding_ShadowDepthPass_LPV_12; + float PrePadding_ShadowDepthPass_LPV_16; + float PrePadding_ShadowDepthPass_LPV_20; + float PrePadding_ShadowDepthPass_LPV_24; + float PrePadding_ShadowDepthPass_LPV_28; + float PrePadding_ShadowDepthPass_LPV_32; + float PrePadding_ShadowDepthPass_LPV_36; + float PrePadding_ShadowDepthPass_LPV_40; + float PrePadding_ShadowDepthPass_LPV_44; + float PrePadding_ShadowDepthPass_LPV_48; + float PrePadding_ShadowDepthPass_LPV_52; + float PrePadding_ShadowDepthPass_LPV_56; + float PrePadding_ShadowDepthPass_LPV_60; + float PrePadding_ShadowDepthPass_LPV_64; + float PrePadding_ShadowDepthPass_LPV_68; + float PrePadding_ShadowDepthPass_LPV_72; + float PrePadding_ShadowDepthPass_LPV_76; + float PrePadding_ShadowDepthPass_LPV_80; + float PrePadding_ShadowDepthPass_LPV_84; + float PrePadding_ShadowDepthPass_LPV_88; + float PrePadding_ShadowDepthPass_LPV_92; + float PrePadding_ShadowDepthPass_LPV_96; + float PrePadding_ShadowDepthPass_LPV_100; + float PrePadding_ShadowDepthPass_LPV_104; + float PrePadding_ShadowDepthPass_LPV_108; + float PrePadding_ShadowDepthPass_LPV_112; + float PrePadding_ShadowDepthPass_LPV_116; + float PrePadding_ShadowDepthPass_LPV_120; + float PrePadding_ShadowDepthPass_LPV_124; + float PrePadding_ShadowDepthPass_LPV_128; + float PrePadding_ShadowDepthPass_LPV_132; + float PrePadding_ShadowDepthPass_LPV_136; + float PrePadding_ShadowDepthPass_LPV_140; + float PrePadding_ShadowDepthPass_LPV_144; + float PrePadding_ShadowDepthPass_LPV_148; + float PrePadding_ShadowDepthPass_LPV_152; + float PrePadding_ShadowDepthPass_LPV_156; + float PrePadding_ShadowDepthPass_LPV_160; + float PrePadding_ShadowDepthPass_LPV_164; + float PrePadding_ShadowDepthPass_LPV_168; + float PrePadding_ShadowDepthPass_LPV_172; + float PrePadding_ShadowDepthPass_LPV_176; + float PrePadding_ShadowDepthPass_LPV_180; + float PrePadding_ShadowDepthPass_LPV_184; + float PrePadding_ShadowDepthPass_LPV_188; + float PrePadding_ShadowDepthPass_LPV_192; + float PrePadding_ShadowDepthPass_LPV_196; + float PrePadding_ShadowDepthPass_LPV_200; + float PrePadding_ShadowDepthPass_LPV_204; + float PrePadding_ShadowDepthPass_LPV_208; + float PrePadding_ShadowDepthPass_LPV_212; + float PrePadding_ShadowDepthPass_LPV_216; + float PrePadding_ShadowDepthPass_LPV_220; + float PrePadding_ShadowDepthPass_LPV_224; + float PrePadding_ShadowDepthPass_LPV_228; + float PrePadding_ShadowDepthPass_LPV_232; + float PrePadding_ShadowDepthPass_LPV_236; + float PrePadding_ShadowDepthPass_LPV_240; + float PrePadding_ShadowDepthPass_LPV_244; + float PrePadding_ShadowDepthPass_LPV_248; + float PrePadding_ShadowDepthPass_LPV_252; + float PrePadding_ShadowDepthPass_LPV_256; + float PrePadding_ShadowDepthPass_LPV_260; + float PrePadding_ShadowDepthPass_LPV_264; + float PrePadding_ShadowDepthPass_LPV_268; + float4x4 ShadowDepthPass_LPV_mRsmToWorld; + float4 ShadowDepthPass_LPV_mLightColour; + float4 ShadowDepthPass_LPV_GeometryVolumeCaptureLightDirection; + float4 ShadowDepthPass_LPV_mEyePos; + packed_int3 ShadowDepthPass_LPV_mOldGridOffset; + int PrePadding_ShadowDepthPass_LPV_396; + packed_int3 ShadowDepthPass_LPV_mLpvGridOffset; + float ShadowDepthPass_LPV_ClearMultiplier; + float ShadowDepthPass_LPV_LpvScale; + float ShadowDepthPass_LPV_OneOverLpvScale; + float ShadowDepthPass_LPV_DirectionalOcclusionIntensity; + float ShadowDepthPass_LPV_DirectionalOcclusionRadius; + float ShadowDepthPass_LPV_RsmAreaIntensityMultiplier; + float ShadowDepthPass_LPV_RsmPixelToTexcoordMultiplier; + float ShadowDepthPass_LPV_SecondaryOcclusionStrength; + float ShadowDepthPass_LPV_SecondaryBounceStrength; + float ShadowDepthPass_LPV_VplInjectionBias; + float ShadowDepthPass_LPV_GeometryVolumeInjectionBias; + float ShadowDepthPass_LPV_EmissiveInjectionMultiplier; + int ShadowDepthPass_LPV_PropagationIndex; + float4x4 ShadowDepthPass_ProjectionMatrix; + float4x4 ShadowDepthPass_ViewMatrix; + float4 ShadowDepthPass_ShadowParams; + float ShadowDepthPass_bClampToNearPlane; + float PrePadding_ShadowDepthPass_612; + float PrePadding_ShadowDepthPass_616; + float PrePadding_ShadowDepthPass_620; + float4x4 ShadowDepthPass_ShadowViewProjectionMatrices[6]; + float4x4 ShadowDepthPass_ShadowViewMatrices[6]; +}; + +constant float4 _113 = {}; + +struct main0_out +{ + float4 out_var_TEXCOORD10_centroid [[user(locn0)]]; + float4 out_var_TEXCOORD11_centroid [[user(locn1)]]; + float4 out_var_COLOR0 [[user(locn2)]]; + float4 out_var_TEXCOORD0_0 [[user(locn3)]]; + uint out_var_PRIMITIVE_ID [[user(locn4)]]; + float out_var_TEXCOORD6 [[user(locn5)]]; + float out_var_TEXCOORD8 [[user(locn6)]]; + float3 out_var_TEXCOORD7 [[user(locn7)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_COLOR0 [[attribute(0)]]; + float4 in_var_PN_POSITION_0 [[attribute(2)]]; + float4 in_var_PN_POSITION_1 [[attribute(3)]]; + float4 in_var_PN_POSITION_2 [[attribute(4)]]; + float in_var_PN_WorldDisplacementMultiplier [[attribute(7)]]; + uint in_var_PRIMITIVE_ID [[attribute(8)]]; + float4 in_var_TEXCOORD0_0 [[attribute(9)]]; + float4 in_var_TEXCOORD10_centroid [[attribute(10)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(11)]]; +}; + +struct main0_patchIn +{ + float4 in_var_PN_POSITION9 [[attribute(5)]]; + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant type_View& View [[buffer(0)]], constant type_ShadowDepthPass& ShadowDepthPass [[buffer(1)]], texture2d Material_Texture2D_3 [[texture(0)]], sampler Material_Texture2D_3Sampler [[sampler(0)]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray out_var_TEXCOORD0 = {}; + spvUnsafeArray _117 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD10_centroid, patchIn.gl_in[1].in_var_TEXCOORD10_centroid, patchIn.gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _118 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD11_centroid, patchIn.gl_in[1].in_var_TEXCOORD11_centroid, patchIn.gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _119 = spvUnsafeArray({ patchIn.gl_in[0].in_var_COLOR0, patchIn.gl_in[1].in_var_COLOR0, patchIn.gl_in[2].in_var_COLOR0 }); + spvUnsafeArray, 3> _120 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD0_0 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_TEXCOORD0_0 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_TEXCOORD0_0 }) }); + spvUnsafeArray, 3> _135 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_POSITION_0, patchIn.gl_in[0].in_var_PN_POSITION_1, patchIn.gl_in[0].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_PN_POSITION_0, patchIn.gl_in[1].in_var_PN_POSITION_1, patchIn.gl_in[1].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_PN_POSITION_0, patchIn.gl_in[2].in_var_PN_POSITION_1, patchIn.gl_in[2].in_var_PN_POSITION_2 }) }); + spvUnsafeArray _136 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_WorldDisplacementMultiplier, patchIn.gl_in[1].in_var_PN_WorldDisplacementMultiplier, patchIn.gl_in[2].in_var_PN_WorldDisplacementMultiplier }); + float _157 = gl_TessCoord.x * gl_TessCoord.x; + float _158 = gl_TessCoord.y * gl_TessCoord.y; + float _159 = gl_TessCoord.z * gl_TessCoord.z; + float4 _165 = float4(gl_TessCoord.x); + float4 _169 = float4(gl_TessCoord.y); + float4 _174 = float4(gl_TessCoord.z); + float4 _177 = float4(_157 * 3.0); + float4 _181 = float4(_158 * 3.0); + float4 _188 = float4(_159 * 3.0); + float4 _202 = ((((((((((_135[0][0] * float4(_157)) * _165) + ((_135[1][0] * float4(_158)) * _169)) + ((_135[2][0] * float4(_159)) * _174)) + ((_135[0][1] * _177) * _169)) + ((_135[0][2] * _181) * _165)) + ((_135[1][1] * _181) * _174)) + ((_135[1][2] * _188) * _169)) + ((_135[2][1] * _188) * _165)) + ((_135[2][2] * _177) * _174)) + ((((patchIn.in_var_PN_POSITION9 * float4(6.0)) * _174) * _165) * _169); + float3 _226 = ((_117[0].xyz * float3(gl_TessCoord.x)) + (_117[1].xyz * float3(gl_TessCoord.y))).xyz + (_117[2].xyz * float3(gl_TessCoord.z)); + float4 _229 = ((_118[0] * _165) + (_118[1] * _169)) + (_118[2] * _174); + float4 _231 = ((_119[0] * _165) + (_119[1] * _169)) + (_119[2] * _174); + float4 _233 = ((_120[0][0] * _165) + (_120[1][0] * _169)) + (_120[2][0] * _174); + spvUnsafeArray _234 = spvUnsafeArray({ _233 }); + float3 _236 = _229.xyz; + float3 _264 = _202.xyz + (((float3((Material_Texture2D_3.sample(Material_Texture2D_3Sampler, (float2(View.View_GameTime * 0.20000000298023223876953125, View.View_GameTime * (-0.699999988079071044921875)) + (_233.zw * float2(1.0, 2.0))), level(-1.0)).x * 10.0) * (1.0 - _231.x)) * _236) * float3(0.5)) * float3(((_136[0] * gl_TessCoord.x) + (_136[1] * gl_TessCoord.y)) + (_136[2] * gl_TessCoord.z))); + float4 _270 = ShadowDepthPass.ShadowDepthPass_ProjectionMatrix * float4(_264.x, _264.y, _264.z, _202.w); + float4 _281; + if ((ShadowDepthPass.ShadowDepthPass_bClampToNearPlane > 0.0) && (_270.z < 0.0)) + { + float4 _279 = _270; + _279.z = 9.9999999747524270787835121154785e-07; + float4 _280 = _279; + _280.w = 1.0; + _281 = _280; + } + else + { + _281 = _270; + } + float _290 = abs(dot(float3(ShadowDepthPass.ShadowDepthPass_ViewMatrix[0].z, ShadowDepthPass.ShadowDepthPass_ViewMatrix[1].z, ShadowDepthPass.ShadowDepthPass_ViewMatrix[2].z), _236)); + out.out_var_TEXCOORD10_centroid = float4(_226.x, _226.y, _226.z, _113.w); + out.out_var_TEXCOORD11_centroid = _229; + out.out_var_COLOR0 = _231; + out_var_TEXCOORD0 = _234; + out.out_var_PRIMITIVE_ID = patchIn.gl_in[0u].in_var_PRIMITIVE_ID; + out.out_var_TEXCOORD6 = _281.z; + out.out_var_TEXCOORD8 = (ShadowDepthPass.ShadowDepthPass_ShadowParams.y * fast::clamp((abs(_290) > 0.0) ? (sqrt(fast::clamp(1.0 - (_290 * _290), 0.0, 1.0)) / _290) : ShadowDepthPass.ShadowDepthPass_ShadowParams.z, 0.0, ShadowDepthPass.ShadowDepthPass_ShadowParams.z)) + ShadowDepthPass.ShadowDepthPass_ShadowParams.x; + out.out_var_TEXCOORD7 = _264.xyz; + out.gl_Position = _281; + out.out_var_TEXCOORD0_0 = out_var_TEXCOORD0[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese new file mode 100644 index 0000000..738b073 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese @@ -0,0 +1,417 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; + float PrePadding_View_3048; + float PrePadding_View_3052; + float4x4 View_WorldToVirtualTexture; + float4 View_VirtualTextureParams; + float4 View_XRPassthroughCameraUVs[2]; +}; + +struct type_Material +{ + float4 Material_VectorExpressions[5]; + float4 Material_ScalarExpressions[2]; +}; + +constant float4 _118 = {}; + +struct main0_out +{ + float4 out_var_TEXCOORD6 [[user(locn0)]]; + float4 out_var_TEXCOORD7 [[user(locn1)]]; + float4 out_var_TEXCOORD10_centroid [[user(locn2)]]; + float4 out_var_TEXCOORD11_centroid [[user(locn3)]]; + float gl_ClipDistance [[clip_distance]] [1]; + float gl_ClipDistance_0 [[user(clip0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_PN_DominantEdge2 [[attribute(3)]]; + float4 in_var_PN_DominantEdge3 [[attribute(4)]]; + float3 in_var_PN_DominantEdge4 [[attribute(5)]]; + float3 in_var_PN_DominantEdge5 [[attribute(6)]]; + float4 in_var_PN_DominantVertex1 [[attribute(8)]]; + float3 in_var_PN_DominantVertex2 [[attribute(9)]]; + float4 in_var_PN_POSITION_0 [[attribute(10)]]; + float4 in_var_PN_POSITION_1 [[attribute(11)]]; + float4 in_var_PN_POSITION_2 [[attribute(12)]]; + float in_var_PN_WorldDisplacementMultiplier [[attribute(15)]]; + float4 in_var_TEXCOORD10_centroid [[attribute(16)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(17)]]; + float4 in_var_TEXCOORD6 [[attribute(18)]]; + float4 in_var_TEXCOORD8 [[attribute(19)]]; +}; + +struct main0_patchIn +{ + float4 in_var_PN_POSITION9 [[attribute(13)]]; + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant type_View& View [[buffer(0)]], constant type_Material& Material [[buffer(1)]], texture3d View_GlobalDistanceFieldTexture0 [[texture(0)]], texture3d View_GlobalDistanceFieldTexture1 [[texture(1)]], texture3d View_GlobalDistanceFieldTexture2 [[texture(2)]], texture3d View_GlobalDistanceFieldTexture3 [[texture(3)]], sampler View_GlobalDistanceFieldSampler0 [[sampler(0)]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray _120 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD6, patchIn.gl_in[1].in_var_TEXCOORD6, patchIn.gl_in[2].in_var_TEXCOORD6 }); + spvUnsafeArray _121 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD8, patchIn.gl_in[1].in_var_TEXCOORD8, patchIn.gl_in[2].in_var_TEXCOORD8 }); + spvUnsafeArray _128 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD10_centroid, patchIn.gl_in[1].in_var_TEXCOORD10_centroid, patchIn.gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _129 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD11_centroid, patchIn.gl_in[1].in_var_TEXCOORD11_centroid, patchIn.gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray, 3> _136 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_POSITION_0, patchIn.gl_in[0].in_var_PN_POSITION_1, patchIn.gl_in[0].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_PN_POSITION_0, patchIn.gl_in[1].in_var_PN_POSITION_1, patchIn.gl_in[1].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_PN_POSITION_0, patchIn.gl_in[2].in_var_PN_POSITION_1, patchIn.gl_in[2].in_var_PN_POSITION_2 }) }); + spvUnsafeArray _137 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_WorldDisplacementMultiplier, patchIn.gl_in[1].in_var_PN_WorldDisplacementMultiplier, patchIn.gl_in[2].in_var_PN_WorldDisplacementMultiplier }); + spvUnsafeArray _138 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantVertex1, patchIn.gl_in[1].in_var_PN_DominantVertex1, patchIn.gl_in[2].in_var_PN_DominantVertex1 }); + spvUnsafeArray _139 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantVertex2, patchIn.gl_in[1].in_var_PN_DominantVertex2, patchIn.gl_in[2].in_var_PN_DominantVertex2 }); + spvUnsafeArray _146 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantEdge2, patchIn.gl_in[1].in_var_PN_DominantEdge2, patchIn.gl_in[2].in_var_PN_DominantEdge2 }); + spvUnsafeArray _147 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantEdge3, patchIn.gl_in[1].in_var_PN_DominantEdge3, patchIn.gl_in[2].in_var_PN_DominantEdge3 }); + spvUnsafeArray _148 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantEdge4, patchIn.gl_in[1].in_var_PN_DominantEdge4, patchIn.gl_in[2].in_var_PN_DominantEdge4 }); + spvUnsafeArray _149 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantEdge5, patchIn.gl_in[1].in_var_PN_DominantEdge5, patchIn.gl_in[2].in_var_PN_DominantEdge5 }); + float _190 = gl_TessCoord.x * gl_TessCoord.x; + float _191 = gl_TessCoord.y * gl_TessCoord.y; + float _192 = gl_TessCoord.z * gl_TessCoord.z; + float4 _198 = float4(gl_TessCoord.x); + float4 _202 = float4(gl_TessCoord.y); + float4 _207 = float4(gl_TessCoord.z); + float4 _210 = float4(_190 * 3.0); + float4 _214 = float4(_191 * 3.0); + float4 _221 = float4(_192 * 3.0); + float4 _235 = ((((((((((_136[0][0] * float4(_190)) * _198) + ((_136[1][0] * float4(_191)) * _202)) + ((_136[2][0] * float4(_192)) * _207)) + ((_136[0][1] * _210) * _202)) + ((_136[0][2] * _214) * _198)) + ((_136[1][1] * _214) * _207)) + ((_136[1][2] * _221) * _202)) + ((_136[2][1] * _221) * _198)) + ((_136[2][2] * _210) * _207)) + ((((patchIn.in_var_PN_POSITION9 * float4(6.0)) * _207) * _198) * _202); + float3 _237 = float3(gl_TessCoord.x); + float3 _240 = float3(gl_TessCoord.y); + float3 _254 = float3(gl_TessCoord.z); + float3 _256 = ((_128[0].xyz * _237) + (_128[1].xyz * _240)).xyz + (_128[2].xyz * _254); + float4 _259 = ((_129[0] * _198) + (_129[1] * _202)) + (_129[2] * _207); + float3 _264 = _235.xyz; + float3 _265 = _256.xyz; + float3 _266 = _259.xyz; + float3 _272 = _264 + float3(View.View_WorldCameraOrigin); + float _279 = float(int(gl_TessCoord.x == 0.0)); + float _282 = float(int(gl_TessCoord.y == 0.0)); + float _285 = float(int(gl_TessCoord.z == 0.0)); + float _286 = _279 + _282; + float _287 = _286 + _285; + float4 _387; + float3 _388; + if (float(int(_287 == 2.0)) == 1.0) + { + float _363 = float(int((_282 + _285) == 2.0)); + float _367 = float(int((_285 + _279) == 2.0)); + float _370 = float(int(_286 == 2.0)); + _387 = ((float4(_363) * _138[0]) + (float4(_367) * _138[1])) + (float4(_370) * _138[2]); + _388 = ((float3(_363) * _139[0]) + (float3(_367) * _139[1])) + (float3(_370) * _139[2]); + } + else + { + float4 _358; + float3 _359; + if (float(int(_287 == 1.0)) != 0.0) + { + float4 _304 = float4(_279); + float4 _306 = float4(_282); + float4 _309 = float4(_285); + float4 _311 = ((_304 * _146[0]) + (_306 * _146[1])) + (_309 * _146[2]); + float4 _316 = ((_304 * _147[0]) + (_306 * _147[1])) + (_309 * _147[2]); + float3 _331 = float3(_279); + float3 _333 = float3(_282); + float3 _336 = float3(_285); + float3 _338 = ((_331 * _148[0]) + (_333 * _148[1])) + (_336 * _148[2]); + float3 _343 = ((_331 * _149[0]) + (_333 * _149[1])) + (_336 * _149[2]); + _358 = ((_304 * ((_202 * _311) + (_207 * _316))) + (_306 * ((_207 * _311) + (_198 * _316)))) + (_309 * ((_198 * _311) + (_202 * _316))); + _359 = ((_331 * ((_240 * _338) + (_254 * _343))) + (_333 * ((_254 * _338) + (_237 * _343)))) + (_336 * ((_237 * _338) + (_240 * _343))); + } + else + { + _358 = float4(_259.xyz, 0.0); + _359 = _265; + } + _387 = _358; + _388 = _359; + } + float3x3 _398; + if (float(int(_287 == 0.0)) == 0.0) + { + _398 = float3x3(_388, cross(_387.xyz, _388) * float3(_387.w), _387.xyz); + } + else + { + _398 = float3x3(_265, cross(_266, _265) * float3(_259.w), _266); + } + float3 _411 = fast::min(fast::max((_272 - View.View_GlobalVolumeCenterAndExtent[0].xyz) + View.View_GlobalVolumeCenterAndExtent[0].www, float3(0.0)), fast::max((View.View_GlobalVolumeCenterAndExtent[0].xyz + View.View_GlobalVolumeCenterAndExtent[0].www) - _272, float3(0.0))); + float _547; + if (fast::min(_411.x, fast::min(_411.y, _411.z)) > (View.View_GlobalVolumeCenterAndExtent[0].w * View.View_GlobalVolumeTexelSize)) + { + _547 = View_GlobalDistanceFieldTexture0.sample(View_GlobalDistanceFieldSampler0, ((_272 * View.View_GlobalVolumeWorldToUVAddAndMul[0u].www) + View.View_GlobalVolumeWorldToUVAddAndMul[0u].xyz), level(0.0)).x; + } + else + { + float3 _436 = fast::min(fast::max((_272 - View.View_GlobalVolumeCenterAndExtent[1].xyz) + View.View_GlobalVolumeCenterAndExtent[1].www, float3(0.0)), fast::max((View.View_GlobalVolumeCenterAndExtent[1].xyz + View.View_GlobalVolumeCenterAndExtent[1].www) - _272, float3(0.0))); + float _535; + if (fast::min(_436.x, fast::min(_436.y, _436.z)) > (View.View_GlobalVolumeCenterAndExtent[1].w * View.View_GlobalVolumeTexelSize)) + { + _535 = View_GlobalDistanceFieldTexture1.sample(View_GlobalDistanceFieldSampler0, ((_272 * View.View_GlobalVolumeWorldToUVAddAndMul[1u].www) + View.View_GlobalVolumeWorldToUVAddAndMul[1u].xyz), level(0.0)).x; + } + else + { + float3 _459 = fast::min(fast::max((_272 - View.View_GlobalVolumeCenterAndExtent[2].xyz) + View.View_GlobalVolumeCenterAndExtent[2].www, float3(0.0)), fast::max((View.View_GlobalVolumeCenterAndExtent[2].xyz + View.View_GlobalVolumeCenterAndExtent[2].www) - _272, float3(0.0))); + float3 _475 = fast::min(fast::max((_272 - View.View_GlobalVolumeCenterAndExtent[3].xyz) + View.View_GlobalVolumeCenterAndExtent[3].www, float3(0.0)), fast::max((View.View_GlobalVolumeCenterAndExtent[3].xyz + View.View_GlobalVolumeCenterAndExtent[3].www) - _272, float3(0.0))); + float _480 = fast::min(_475.x, fast::min(_475.y, _475.z)); + float _523; + if (fast::min(_459.x, fast::min(_459.y, _459.z)) > (View.View_GlobalVolumeCenterAndExtent[2].w * View.View_GlobalVolumeTexelSize)) + { + _523 = View_GlobalDistanceFieldTexture2.sample(View_GlobalDistanceFieldSampler0, ((_272 * View.View_GlobalVolumeWorldToUVAddAndMul[2u].www) + View.View_GlobalVolumeWorldToUVAddAndMul[2u].xyz), level(0.0)).x; + } + else + { + float _511; + if (_480 > (View.View_GlobalVolumeCenterAndExtent[3].w * View.View_GlobalVolumeTexelSize)) + { + _511 = mix(View.View_MaxGlobalDistance, View_GlobalDistanceFieldTexture3.sample(View_GlobalDistanceFieldSampler0, ((_272 * View.View_GlobalVolumeWorldToUVAddAndMul[3u].www) + View.View_GlobalVolumeWorldToUVAddAndMul[3u].xyz), level(0.0)).x, fast::clamp((_480 * 10.0) * View.View_GlobalVolumeWorldToUVAddAndMul[3].w, 0.0, 1.0)); + } + else + { + _511 = View.View_MaxGlobalDistance; + } + _523 = _511; + } + _535 = _523; + } + _547 = _535; + } + float3 _565 = _264 + ((_398[2] * float3(fast::min(_547 + Material.Material_ScalarExpressions[0].z, 0.0) * Material.Material_ScalarExpressions[0].w)) * float3(((_137[0] * gl_TessCoord.x) + (_137[1] * gl_TessCoord.y)) + (_137[2] * gl_TessCoord.z))); + float4 _574 = View.View_TranslatedWorldToClip * float4(_565.x, _565.y, _565.z, _235.w); + float4 _579 = _574; + _579.z = _574.z + (0.001000000047497451305389404296875 * _574.w); + out.gl_Position = _579; + out.out_var_TEXCOORD6 = ((_120[0] * _198) + (_120[1] * _202)) + (_120[2] * _207); + out.out_var_TEXCOORD7 = ((_121[0] * _198) + (_121[1] * _202)) + (_121[2] * _207); + out.out_var_TEXCOORD10_centroid = float4(_256.x, _256.y, _256.z, _118.w); + out.out_var_TEXCOORD11_centroid = _259; + out.gl_ClipDistance[0u] = dot(View.View_GlobalClippingPlane, float4(_565.xyz - float3(View.View_PreViewTranslation), 1.0)); + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese new file mode 100644 index 0000000..4a872e5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese @@ -0,0 +1,216 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_ShadowDepthPass +{ + float PrePadding_ShadowDepthPass_LPV_0; + float PrePadding_ShadowDepthPass_LPV_4; + float PrePadding_ShadowDepthPass_LPV_8; + float PrePadding_ShadowDepthPass_LPV_12; + float PrePadding_ShadowDepthPass_LPV_16; + float PrePadding_ShadowDepthPass_LPV_20; + float PrePadding_ShadowDepthPass_LPV_24; + float PrePadding_ShadowDepthPass_LPV_28; + float PrePadding_ShadowDepthPass_LPV_32; + float PrePadding_ShadowDepthPass_LPV_36; + float PrePadding_ShadowDepthPass_LPV_40; + float PrePadding_ShadowDepthPass_LPV_44; + float PrePadding_ShadowDepthPass_LPV_48; + float PrePadding_ShadowDepthPass_LPV_52; + float PrePadding_ShadowDepthPass_LPV_56; + float PrePadding_ShadowDepthPass_LPV_60; + float PrePadding_ShadowDepthPass_LPV_64; + float PrePadding_ShadowDepthPass_LPV_68; + float PrePadding_ShadowDepthPass_LPV_72; + float PrePadding_ShadowDepthPass_LPV_76; + float PrePadding_ShadowDepthPass_LPV_80; + float PrePadding_ShadowDepthPass_LPV_84; + float PrePadding_ShadowDepthPass_LPV_88; + float PrePadding_ShadowDepthPass_LPV_92; + float PrePadding_ShadowDepthPass_LPV_96; + float PrePadding_ShadowDepthPass_LPV_100; + float PrePadding_ShadowDepthPass_LPV_104; + float PrePadding_ShadowDepthPass_LPV_108; + float PrePadding_ShadowDepthPass_LPV_112; + float PrePadding_ShadowDepthPass_LPV_116; + float PrePadding_ShadowDepthPass_LPV_120; + float PrePadding_ShadowDepthPass_LPV_124; + float PrePadding_ShadowDepthPass_LPV_128; + float PrePadding_ShadowDepthPass_LPV_132; + float PrePadding_ShadowDepthPass_LPV_136; + float PrePadding_ShadowDepthPass_LPV_140; + float PrePadding_ShadowDepthPass_LPV_144; + float PrePadding_ShadowDepthPass_LPV_148; + float PrePadding_ShadowDepthPass_LPV_152; + float PrePadding_ShadowDepthPass_LPV_156; + float PrePadding_ShadowDepthPass_LPV_160; + float PrePadding_ShadowDepthPass_LPV_164; + float PrePadding_ShadowDepthPass_LPV_168; + float PrePadding_ShadowDepthPass_LPV_172; + float PrePadding_ShadowDepthPass_LPV_176; + float PrePadding_ShadowDepthPass_LPV_180; + float PrePadding_ShadowDepthPass_LPV_184; + float PrePadding_ShadowDepthPass_LPV_188; + float PrePadding_ShadowDepthPass_LPV_192; + float PrePadding_ShadowDepthPass_LPV_196; + float PrePadding_ShadowDepthPass_LPV_200; + float PrePadding_ShadowDepthPass_LPV_204; + float PrePadding_ShadowDepthPass_LPV_208; + float PrePadding_ShadowDepthPass_LPV_212; + float PrePadding_ShadowDepthPass_LPV_216; + float PrePadding_ShadowDepthPass_LPV_220; + float PrePadding_ShadowDepthPass_LPV_224; + float PrePadding_ShadowDepthPass_LPV_228; + float PrePadding_ShadowDepthPass_LPV_232; + float PrePadding_ShadowDepthPass_LPV_236; + float PrePadding_ShadowDepthPass_LPV_240; + float PrePadding_ShadowDepthPass_LPV_244; + float PrePadding_ShadowDepthPass_LPV_248; + float PrePadding_ShadowDepthPass_LPV_252; + float PrePadding_ShadowDepthPass_LPV_256; + float PrePadding_ShadowDepthPass_LPV_260; + float PrePadding_ShadowDepthPass_LPV_264; + float PrePadding_ShadowDepthPass_LPV_268; + float4x4 ShadowDepthPass_LPV_mRsmToWorld; + float4 ShadowDepthPass_LPV_mLightColour; + float4 ShadowDepthPass_LPV_GeometryVolumeCaptureLightDirection; + float4 ShadowDepthPass_LPV_mEyePos; + packed_int3 ShadowDepthPass_LPV_mOldGridOffset; + int PrePadding_ShadowDepthPass_LPV_396; + packed_int3 ShadowDepthPass_LPV_mLpvGridOffset; + float ShadowDepthPass_LPV_ClearMultiplier; + float ShadowDepthPass_LPV_LpvScale; + float ShadowDepthPass_LPV_OneOverLpvScale; + float ShadowDepthPass_LPV_DirectionalOcclusionIntensity; + float ShadowDepthPass_LPV_DirectionalOcclusionRadius; + float ShadowDepthPass_LPV_RsmAreaIntensityMultiplier; + float ShadowDepthPass_LPV_RsmPixelToTexcoordMultiplier; + float ShadowDepthPass_LPV_SecondaryOcclusionStrength; + float ShadowDepthPass_LPV_SecondaryBounceStrength; + float ShadowDepthPass_LPV_VplInjectionBias; + float ShadowDepthPass_LPV_GeometryVolumeInjectionBias; + float ShadowDepthPass_LPV_EmissiveInjectionMultiplier; + int ShadowDepthPass_LPV_PropagationIndex; + float4x4 ShadowDepthPass_ProjectionMatrix; + float4x4 ShadowDepthPass_ViewMatrix; + float4 ShadowDepthPass_ShadowParams; + float ShadowDepthPass_bClampToNearPlane; + float PrePadding_ShadowDepthPass_612; + float PrePadding_ShadowDepthPass_616; + float PrePadding_ShadowDepthPass_620; + float4x4 ShadowDepthPass_ShadowViewProjectionMatrices[6]; + float4x4 ShadowDepthPass_ShadowViewMatrices[6]; +}; + +constant float4 _90 = {}; + +struct main0_out +{ + float4 out_var_TEXCOORD10_centroid [[user(locn0)]]; + float4 out_var_TEXCOORD11_centroid [[user(locn1)]]; + float out_var_TEXCOORD6 [[user(locn2)]]; + float3 out_var_TEXCOORD7 [[user(locn3)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_PN_POSITION_0 [[attribute(10)]]; + float4 in_var_PN_POSITION_1 [[attribute(11)]]; + float4 in_var_PN_POSITION_2 [[attribute(12)]]; + float4 in_var_TEXCOORD10_centroid [[attribute(16)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(17)]]; +}; + +struct main0_patchIn +{ + float4 in_var_PN_POSITION9 [[attribute(13)]]; + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant type_ShadowDepthPass& ShadowDepthPass [[buffer(0)]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray _93 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD10_centroid, patchIn.gl_in[1].in_var_TEXCOORD10_centroid, patchIn.gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _94 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD11_centroid, patchIn.gl_in[1].in_var_TEXCOORD11_centroid, patchIn.gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray, 3> _101 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_POSITION_0, patchIn.gl_in[0].in_var_PN_POSITION_1, patchIn.gl_in[0].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_PN_POSITION_0, patchIn.gl_in[1].in_var_PN_POSITION_1, patchIn.gl_in[1].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_PN_POSITION_0, patchIn.gl_in[2].in_var_PN_POSITION_1, patchIn.gl_in[2].in_var_PN_POSITION_2 }) }); + float _119 = gl_TessCoord.x * gl_TessCoord.x; + float _120 = gl_TessCoord.y * gl_TessCoord.y; + float _121 = gl_TessCoord.z * gl_TessCoord.z; + float4 _127 = float4(gl_TessCoord.x); + float4 _131 = float4(gl_TessCoord.y); + float4 _136 = float4(gl_TessCoord.z); + float4 _139 = float4(_119 * 3.0); + float4 _143 = float4(_120 * 3.0); + float4 _150 = float4(_121 * 3.0); + float4 _164 = ((((((((((_101[0][0] * float4(_119)) * _127) + ((_101[1][0] * float4(_120)) * _131)) + ((_101[2][0] * float4(_121)) * _136)) + ((_101[0][1] * _139) * _131)) + ((_101[0][2] * _143) * _127)) + ((_101[1][1] * _143) * _136)) + ((_101[1][2] * _150) * _131)) + ((_101[2][1] * _150) * _127)) + ((_101[2][2] * _139) * _136)) + ((((patchIn.in_var_PN_POSITION9 * float4(6.0)) * _136) * _127) * _131); + float3 _179 = ((_93[0].xyz * float3(gl_TessCoord.x)) + (_93[1].xyz * float3(gl_TessCoord.y))).xyz + (_93[2].xyz * float3(gl_TessCoord.z)); + float4 _182 = ((_94[0] * _127) + (_94[1] * _131)) + (_94[2] * _136); + float4 _189 = ShadowDepthPass.ShadowDepthPass_ProjectionMatrix * float4(_164.x, _164.y, _164.z, _164.w); + float4 _200; + if ((ShadowDepthPass.ShadowDepthPass_bClampToNearPlane > 0.0) && (_189.z < 0.0)) + { + float4 _198 = _189; + _198.z = 9.9999999747524270787835121154785e-07; + float4 _199 = _198; + _199.w = 1.0; + _200 = _199; + } + else + { + _200 = _189; + } + float _209 = abs(dot(float3(ShadowDepthPass.ShadowDepthPass_ViewMatrix[0].z, ShadowDepthPass.ShadowDepthPass_ViewMatrix[1].z, ShadowDepthPass.ShadowDepthPass_ViewMatrix[2].z), _182.xyz)); + float4 _234 = _200; + _234.z = ((_200.z * ShadowDepthPass.ShadowDepthPass_ShadowParams.w) + ((ShadowDepthPass.ShadowDepthPass_ShadowParams.y * fast::clamp((abs(_209) > 0.0) ? (sqrt(fast::clamp(1.0 - (_209 * _209), 0.0, 1.0)) / _209) : ShadowDepthPass.ShadowDepthPass_ShadowParams.z, 0.0, ShadowDepthPass.ShadowDepthPass_ShadowParams.z)) + ShadowDepthPass.ShadowDepthPass_ShadowParams.x)) * _200.w; + out.out_var_TEXCOORD10_centroid = float4(_179.x, _179.y, _179.z, _90.w); + out.out_var_TEXCOORD11_centroid = _182; + out.out_var_TEXCOORD6 = 0.0; + out.out_var_TEXCOORD7 = _164.xyz; + out.gl_Position = _234; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese new file mode 100644 index 0000000..346d7e3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese @@ -0,0 +1,318 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; + float PrePadding_View_3048; + float PrePadding_View_3052; + float4x4 View_WorldToVirtualTexture; + float4 View_VirtualTextureParams; + float4 View_XRPassthroughCameraUVs[2]; +}; + +constant float4 _68 = {}; + +struct main0_out +{ + float4 out_var_TEXCOORD10_centroid [[user(locn0)]]; + float4 out_var_TEXCOORD11_centroid [[user(locn1)]]; + float4 out_var_TEXCOORD0_0 [[user(locn2)]]; + float4 out_var_COLOR1 [[user(locn3)]]; + float4 out_var_COLOR2 [[user(locn4)]]; + float4 out_var_TEXCOORD6 [[user(locn5)]]; + float3 out_var_TEXCOORD7 [[user(locn6)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_COLOR1 [[attribute(0)]]; + float4 in_var_COLOR2 [[attribute(1)]]; + float4 in_var_TEXCOORD0_0 [[attribute(5)]]; + float4 in_var_TEXCOORD10_centroid [[attribute(6)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(7)]]; + float3 in_var_TEXCOORD7 [[attribute(8)]]; + float4 in_var_VS_To_DS_Position [[attribute(9)]]; +}; + +struct main0_patchIn +{ + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant type_View& View [[buffer(0)]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray out_var_TEXCOORD0 = {}; + spvUnsafeArray _77 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD10_centroid, patchIn.gl_in[1].in_var_TEXCOORD10_centroid, patchIn.gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _78 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD11_centroid, patchIn.gl_in[1].in_var_TEXCOORD11_centroid, patchIn.gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray, 3> _79 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD0_0 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_TEXCOORD0_0 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_TEXCOORD0_0 }) }); + spvUnsafeArray _80 = spvUnsafeArray({ patchIn.gl_in[0].in_var_COLOR1, patchIn.gl_in[1].in_var_COLOR1, patchIn.gl_in[2].in_var_COLOR1 }); + spvUnsafeArray _81 = spvUnsafeArray({ patchIn.gl_in[0].in_var_COLOR2, patchIn.gl_in[1].in_var_COLOR2, patchIn.gl_in[2].in_var_COLOR2 }); + spvUnsafeArray _97 = spvUnsafeArray({ patchIn.gl_in[0].in_var_VS_To_DS_Position, patchIn.gl_in[1].in_var_VS_To_DS_Position, patchIn.gl_in[2].in_var_VS_To_DS_Position }); + spvUnsafeArray _98 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD7, patchIn.gl_in[1].in_var_TEXCOORD7, patchIn.gl_in[2].in_var_TEXCOORD7 }); + float4 _111 = float4(gl_TessCoord.x); + float4 _113 = float4(gl_TessCoord.y); + float4 _116 = float4(gl_TessCoord.z); + float4 _118 = ((_97[0] * _111) + (_97[1] * _113)) + (_97[2] * _116); + spvUnsafeArray _72; + _72 = _79[0]; + spvUnsafeArray _71; + _71 = _79[1]; + float3 _120 = float3(gl_TessCoord.x); + float3 _123 = float3(gl_TessCoord.y); + spvUnsafeArray _73; + for (int _133 = 0; _133 < 1; ) + { + _73[_133] = (_72[_133] * _111) + (_71[_133] * _113); + _133++; + continue; + } + spvUnsafeArray _75; + _75 = _73; + spvUnsafeArray _74; + _74 = _79[2]; + float3 _155 = float3(gl_TessCoord.z); + float3 _157 = ((_77[0].xyz * _120) + (_77[1].xyz * _123)).xyz + (_77[2].xyz * _155); + spvUnsafeArray _76; + for (int _164 = 0; _164 < 1; ) + { + _76[_164] = _75[_164] + (_74[_164] * _116); + _164++; + continue; + } + float4 _181 = float4(_118.x, _118.y, _118.z, _118.w); + out.out_var_TEXCOORD10_centroid = float4(_157.x, _157.y, _157.z, _68.w); + out.out_var_TEXCOORD11_centroid = ((_78[0] * _111) + (_78[1] * _113)) + (_78[2] * _116); + out_var_TEXCOORD0 = _76; + out.out_var_COLOR1 = ((_80[0] * _111) + (_80[1] * _113)) + (_80[2] * _116); + out.out_var_COLOR2 = ((_81[0] * _111) + (_81[1] * _113)) + (_81[2] * _116); + out.out_var_TEXCOORD6 = _181; + out.out_var_TEXCOORD7 = ((_98[0] * _120) + (_98[1] * _123)) + (_98[2] * _155); + out.gl_Position = View.View_TranslatedWorldToClip * _181; + out.out_var_TEXCOORD0_0 = out_var_TEXCOORD0[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/vert/array-missing-copies.asm.vert b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/vert/array-missing-copies.asm.vert new file mode 100644 index 0000000..520b58e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/vert/array-missing-copies.asm.vert @@ -0,0 +1,457 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_MobileBasePass +{ + float4 MobileBasePass_Fog_ExponentialFogParameters; + float4 MobileBasePass_Fog_ExponentialFogParameters2; + float4 MobileBasePass_Fog_ExponentialFogColorParameter; + float4 MobileBasePass_Fog_ExponentialFogParameters3; + float4 MobileBasePass_Fog_InscatteringLightDirection; + float4 MobileBasePass_Fog_DirectionalInscatteringColor; + float2 MobileBasePass_Fog_SinCosInscatteringColorCubemapRotation; + float PrePadding_MobileBasePass_Fog_104; + float PrePadding_MobileBasePass_Fog_108; + packed_float3 MobileBasePass_Fog_FogInscatteringTextureParameters; + float MobileBasePass_Fog_ApplyVolumetricFog; + float PrePadding_MobileBasePass_PlanarReflection_128; + float PrePadding_MobileBasePass_PlanarReflection_132; + float PrePadding_MobileBasePass_PlanarReflection_136; + float PrePadding_MobileBasePass_PlanarReflection_140; + float PrePadding_MobileBasePass_PlanarReflection_144; + float PrePadding_MobileBasePass_PlanarReflection_148; + float PrePadding_MobileBasePass_PlanarReflection_152; + float PrePadding_MobileBasePass_PlanarReflection_156; + float4 MobileBasePass_PlanarReflection_ReflectionPlane; + float4 MobileBasePass_PlanarReflection_PlanarReflectionOrigin; + float4 MobileBasePass_PlanarReflection_PlanarReflectionXAxis; + float4 MobileBasePass_PlanarReflection_PlanarReflectionYAxis; + float3x4 MobileBasePass_PlanarReflection_InverseTransposeMirrorMatrix; + packed_float3 MobileBasePass_PlanarReflection_PlanarReflectionParameters; + float PrePadding_MobileBasePass_PlanarReflection_284; + float2 MobileBasePass_PlanarReflection_PlanarReflectionParameters2; + float PrePadding_MobileBasePass_PlanarReflection_296; + float PrePadding_MobileBasePass_PlanarReflection_300; + float4x4 MobileBasePass_PlanarReflection_ProjectionWithExtraFOV[2]; + float4 MobileBasePass_PlanarReflection_PlanarReflectionScreenScaleBias[2]; + float2 MobileBasePass_PlanarReflection_PlanarReflectionScreenBound; + uint MobileBasePass_PlanarReflection_bIsStereo; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_UseEditorDepthTest; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + float PrePadding_Primitive_380; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightingChannelMask; + uint Primitive_LightmapDataIndex; + int Primitive_SingleCaptureIndex; +}; + +struct type_LandscapeParameters +{ + float4 LandscapeParameters_HeightmapUVScaleBias; + float4 LandscapeParameters_WeightmapUVScaleBias; + float4 LandscapeParameters_LandscapeLightmapScaleBias; + float4 LandscapeParameters_SubsectionSizeVertsLayerUVPan; + float4 LandscapeParameters_SubsectionOffsetParams; + float4 LandscapeParameters_LightmapSubsectionOffsetParams; + float4x4 LandscapeParameters_LocalToWorldNoScaling; +}; + +struct type_Globals +{ + float4 LodBias; + float4 LodValues; + float4 SectionLods; + float4 NeighborSectionLod[4]; +}; + +struct main0_out +{ + float2 out_var_TEXCOORD0 [[user(locn0)]]; + float2 out_var_TEXCOORD1 [[user(locn1)]]; + float4 out_var_TEXCOORD2 [[user(locn2)]]; + float4 out_var_TEXCOORD3 [[user(locn3)]]; + float4 out_var_TEXCOORD8 [[user(locn4)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_ATTRIBUTE0 [[attribute(0)]]; + float4 in_var_ATTRIBUTE1_0 [[attribute(1)]]; + float4 in_var_ATTRIBUTE1_1 [[attribute(2)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_MobileBasePass& MobileBasePass [[buffer(1)]], constant type_Primitive& Primitive [[buffer(2)]], constant type_LandscapeParameters& LandscapeParameters [[buffer(3)]], constant type_Globals& _Globals [[buffer(4)]]) +{ + main0_out out = {}; + spvUnsafeArray in_var_ATTRIBUTE1 = {}; + in_var_ATTRIBUTE1[0] = in.in_var_ATTRIBUTE1_0; + in_var_ATTRIBUTE1[1] = in.in_var_ATTRIBUTE1_1; + float4 _115 = in.in_var_ATTRIBUTE0 * float4(255.0); + float2 _116 = _115.zw; + float2 _119 = fract(_116 * float2(0.5)) * float2(2.0); + float2 _121 = (_116 - _119) * float2(0.0039215688593685626983642578125); + float2 _122 = _115.xy; + float2 _126 = _122 * float2(_Globals.LodValues.w); + float _127 = _126.y; + float _128 = _126.x; + float4 _132 = float4(_127, _128, 1.0 - _128, 1.0 - _127) * float4(2.0); + float4 _186; + if (_119.y > 0.5) + { + float4 _161; + if (_119.x > 0.5) + { + _161 = (_132 * float4(_Globals.SectionLods.w)) + ((float4(1.0) - _132) * _Globals.NeighborSectionLod[3]); + } + else + { + _161 = (_132 * float4(_Globals.SectionLods.z)) + ((float4(1.0) - _132) * _Globals.NeighborSectionLod[2]); + } + _186 = _161; + } + else + { + float4 _185; + if (_119.x > 0.5) + { + _185 = (_132 * float4(_Globals.SectionLods.y)) + ((float4(1.0) - _132) * _Globals.NeighborSectionLod[1]); + } + else + { + _185 = (_132 * float4(_Globals.SectionLods.x)) + ((float4(1.0) - _132) * _Globals.NeighborSectionLod[0]); + } + _186 = _185; + } + float _206; + if ((_128 + _127) > 1.0) + { + float _198; + if (_128 < _127) + { + _198 = _186.w; + } + else + { + _198 = _186.z; + } + _206 = _198; + } + else + { + float _205; + if (_128 < _127) + { + _205 = _186.y; + } + else + { + _205 = _186.x; + } + _206 = _205; + } + float _207 = floor(_206); + float _220 = _121.x; + float3 _235 = select(select(select(select(select(float3(0.03125, _121.yy), float3(0.0625, _220, _121.y), bool3(_207 < 5.0)), float3(0.125, in_var_ATTRIBUTE1[1].w, _220), bool3(_207 < 4.0)), float3(0.25, in_var_ATTRIBUTE1[1].zw), bool3(_207 < 3.0)), float3(0.5, in_var_ATTRIBUTE1[1].yz), bool3(_207 < 2.0)), float3(1.0, in_var_ATTRIBUTE1[1].xy), bool3(_207 < 1.0)); + float _236 = _235.x; + float _245 = (((in_var_ATTRIBUTE1[0].x * 65280.0) + (in_var_ATTRIBUTE1[0].y * 255.0)) - 32768.0) * 0.0078125; + float _252 = (((in_var_ATTRIBUTE1[0].z * 65280.0) + (in_var_ATTRIBUTE1[0].w * 255.0)) - 32768.0) * 0.0078125; + float2 _257 = floor(_122 * float2(_236)); + float2 _271 = float2((LandscapeParameters.LandscapeParameters_SubsectionSizeVertsLayerUVPan.x * _236) - 1.0, fast::max((LandscapeParameters.LandscapeParameters_SubsectionSizeVertsLayerUVPan.x * 0.5) * _236, 2.0) - 1.0) * float2(LandscapeParameters.LandscapeParameters_SubsectionSizeVertsLayerUVPan.y); + float3 _287 = mix(float3(_257 / float2(_271.x), mix(_245, _252, _235.y)), float3(floor(_257 * float2(0.5)) / float2(_271.y), mix(_245, _252, _235.z)), float3(_206 - _207)); + float2 _288 = _119.xy; + float2 _292 = _288 * LandscapeParameters.LandscapeParameters_SubsectionOffsetParams.ww; + float3 _296 = _287 + float3(_292, 0.0); + float4 _322 = float4((((Primitive.Primitive_LocalToWorld[0u].xyz * _296.xxx) + (Primitive.Primitive_LocalToWorld[1u].xyz * _296.yyy)) + (Primitive.Primitive_LocalToWorld[2u].xyz * _296.zzz)) + (Primitive.Primitive_LocalToWorld[3u].xyz + float3(View.View_PreViewTranslation)), 1.0); + float2 _323 = _287.xy; + float4 _338 = float4(_322.x, _322.y, _322.z, _322.w); + float4 _339 = View.View_TranslatedWorldToClip * _338; + float3 _341 = _322.xyz - float3(View.View_TranslatedWorldCameraOrigin); + float _345 = dot(_341, _341); + float _346 = rsqrt(_345); + float _347 = _345 * _346; + float _354 = _341.z; + float _357 = fast::max(0.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters.w); + float _393; + float _394; + float _395; + float _396; + if (_357 > 0.0) + { + float _361 = _357 * _346; + float _362 = _361 * _354; + float _365 = View.View_WorldCameraOrigin[2] + _362; + _393 = (1.0 - _361) * _347; + _394 = MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.z * exp2(-fast::max(-127.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.y * (_365 - MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.w))); + _395 = MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters3.x * exp2(-fast::max(-127.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters.y * (_365 - MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters3.y))); + _396 = _354 - _362; + } + else + { + _393 = _347; + _394 = MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.x; + _395 = MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters.x; + _396 = _354; + } + float _400 = fast::max(-127.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters.y * _396); + float _417 = fast::max(-127.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.y * _396); + float _428 = (_395 * ((abs(_400) > 0.00999999977648258209228515625) ? ((1.0 - exp2(-_400)) / _400) : (0.693147182464599609375 - (0.2402265071868896484375 * _400)))) + (_394 * ((abs(_417) > 0.00999999977648258209228515625) ? ((1.0 - exp2(-_417)) / _417) : (0.693147182464599609375 - (0.2402265071868896484375 * _417)))); + float3 _459; + if (MobileBasePass.MobileBasePass_Fog_InscatteringLightDirection.w >= 0.0) + { + _459 = (MobileBasePass.MobileBasePass_Fog_DirectionalInscatteringColor.xyz * float3(pow(fast::clamp(dot(_341 * float3(_346), MobileBasePass.MobileBasePass_Fog_InscatteringLightDirection.xyz), 0.0, 1.0), MobileBasePass.MobileBasePass_Fog_DirectionalInscatteringColor.w))) * float3(1.0 - fast::clamp(exp2(-(_428 * fast::max(_393 - MobileBasePass.MobileBasePass_Fog_InscatteringLightDirection.w, 0.0))), 0.0, 1.0)); + } + else + { + _459 = float3(0.0); + } + bool _468 = (MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters3.w > 0.0) && (_347 > MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters3.w); + float _471 = _468 ? 1.0 : fast::max(fast::clamp(exp2(-(_428 * _393)), 0.0, 1.0), MobileBasePass.MobileBasePass_Fog_ExponentialFogColorParameter.w); + float4 _479 = float4((MobileBasePass.MobileBasePass_Fog_ExponentialFogColorParameter.xyz * float3(1.0 - _471)) + select(_459, float3(0.0), bool3(_468)), _471); + float4 _482 = _338; + _482.w = _339.w; + out.out_var_TEXCOORD0 = ((_323 + LandscapeParameters.LandscapeParameters_SubsectionSizeVertsLayerUVPan.zw) + _292).xy; + out.out_var_TEXCOORD1 = ((_323 * LandscapeParameters.LandscapeParameters_WeightmapUVScaleBias.xy) + LandscapeParameters.LandscapeParameters_WeightmapUVScaleBias.zw) + (_288 * LandscapeParameters.LandscapeParameters_SubsectionOffsetParams.zz); + out.out_var_TEXCOORD2 = float4(float4(0.0).x, float4(0.0).y, _479.x, _479.y); + out.out_var_TEXCOORD3 = float4(float4(0.0).x, float4(0.0).y, _479.z, _479.w); + out.out_var_TEXCOORD8 = _482; + out.gl_Position = _339; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders-ue4/asm/vert/texture-buffer.asm.vert b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/vert/texture-buffer.asm.vert new file mode 100644 index 0000000..6384e1b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders-ue4/asm/vert/texture-buffer.asm.vert @@ -0,0 +1,387 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_UseEditorDepthTest; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + float PrePadding_Primitive_380; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightingChannelMask; + uint Primitive_LightmapDataIndex; + int Primitive_SingleCaptureIndex; +}; + +struct type_MobileShadowDepthPass +{ + float PrePadding_MobileShadowDepthPass_0; + float PrePadding_MobileShadowDepthPass_4; + float PrePadding_MobileShadowDepthPass_8; + float PrePadding_MobileShadowDepthPass_12; + float PrePadding_MobileShadowDepthPass_16; + float PrePadding_MobileShadowDepthPass_20; + float PrePadding_MobileShadowDepthPass_24; + float PrePadding_MobileShadowDepthPass_28; + float PrePadding_MobileShadowDepthPass_32; + float PrePadding_MobileShadowDepthPass_36; + float PrePadding_MobileShadowDepthPass_40; + float PrePadding_MobileShadowDepthPass_44; + float PrePadding_MobileShadowDepthPass_48; + float PrePadding_MobileShadowDepthPass_52; + float PrePadding_MobileShadowDepthPass_56; + float PrePadding_MobileShadowDepthPass_60; + float PrePadding_MobileShadowDepthPass_64; + float PrePadding_MobileShadowDepthPass_68; + float PrePadding_MobileShadowDepthPass_72; + float PrePadding_MobileShadowDepthPass_76; + float4x4 MobileShadowDepthPass_ProjectionMatrix; + float2 MobileShadowDepthPass_ShadowParams; + float MobileShadowDepthPass_bClampToNearPlane; + float PrePadding_MobileShadowDepthPass_156; + float4x4 MobileShadowDepthPass_ShadowViewProjectionMatrices[6]; +}; + +struct type_EmitterDynamicUniforms +{ + float2 EmitterDynamicUniforms_LocalToWorldScale; + float EmitterDynamicUniforms_EmitterInstRandom; + float PrePadding_EmitterDynamicUniforms_12; + float4 EmitterDynamicUniforms_AxisLockRight; + float4 EmitterDynamicUniforms_AxisLockUp; + float4 EmitterDynamicUniforms_DynamicColor; + float4 EmitterDynamicUniforms_MacroUVParameters; +}; + +struct type_EmitterUniforms +{ + float4 EmitterUniforms_ColorCurve; + float4 EmitterUniforms_ColorScale; + float4 EmitterUniforms_ColorBias; + float4 EmitterUniforms_MiscCurve; + float4 EmitterUniforms_MiscScale; + float4 EmitterUniforms_MiscBias; + float4 EmitterUniforms_SizeBySpeed; + float4 EmitterUniforms_SubImageSize; + float4 EmitterUniforms_TangentSelector; + packed_float3 EmitterUniforms_CameraFacingBlend; + float EmitterUniforms_RemoveHMDRoll; + float EmitterUniforms_RotationRateScale; + float EmitterUniforms_RotationBias; + float EmitterUniforms_CameraMotionBlurAmount; + float PrePadding_EmitterUniforms_172; + float2 EmitterUniforms_PivotOffset; +}; + +struct type_Globals +{ + uint ParticleIndicesOffset; +}; + +struct main0_out +{ + float out_var_TEXCOORD6 [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float2 in_var_ATTRIBUTE0 [[attribute(0)]]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +vertex main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_Primitive& Primitive [[buffer(1)]], constant type_MobileShadowDepthPass& MobileShadowDepthPass [[buffer(2)]], constant type_EmitterDynamicUniforms& EmitterDynamicUniforms [[buffer(3)]], constant type_EmitterUniforms& EmitterUniforms [[buffer(4)]], constant type_Globals& _Globals [[buffer(5)]], texture2d ParticleIndices [[texture(0)]], texture2d PositionTexture [[texture(1)]], texture2d VelocityTexture [[texture(2)]], texture2d AttributesTexture [[texture(3)]], texture2d CurveTexture [[texture(4)]], sampler PositionTextureSampler [[sampler(0)]], sampler VelocityTextureSampler [[sampler(1)]], sampler AttributesTextureSampler [[sampler(2)]], sampler CurveTextureSampler [[sampler(3)]], uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + float2 _133 = ParticleIndices.read(spvTexelBufferCoord((_Globals.ParticleIndicesOffset + ((gl_InstanceIndex * 16u) + (gl_VertexIndex / 4u))))).xy; + float4 _137 = PositionTexture.sample(PositionTextureSampler, _133, level(0.0)); + float4 _145 = AttributesTexture.sample(AttributesTextureSampler, _133, level(0.0)); + float _146 = _137.w; + float3 _158 = float3x3(Primitive.Primitive_LocalToWorld[0].xyz, Primitive.Primitive_LocalToWorld[1].xyz, Primitive.Primitive_LocalToWorld[2].xyz) * VelocityTexture.sample(VelocityTextureSampler, _133, level(0.0)).xyz; + float3 _160 = normalize(_158 + float3(0.0, 0.0, 9.9999997473787516355514526367188e-05)); + float2 _204 = ((((_145.xy + float2((_145.x < 0.5) ? 0.0 : (-0.5), (_145.y < 0.5) ? 0.0 : (-0.5))) * float2(2.0)) * (((CurveTexture.sample(CurveTextureSampler, (EmitterUniforms.EmitterUniforms_MiscCurve.xy + (EmitterUniforms.EmitterUniforms_MiscCurve.zw * float2(_146))), level(0.0)) * EmitterUniforms.EmitterUniforms_MiscScale) + EmitterUniforms.EmitterUniforms_MiscBias).xy * EmitterDynamicUniforms.EmitterDynamicUniforms_LocalToWorldScale)) * fast::min(fast::max(EmitterUniforms.EmitterUniforms_SizeBySpeed.xy * float2(length(_158)), float2(1.0)), EmitterUniforms.EmitterUniforms_SizeBySpeed.zw)) * float2(step(_146, 1.0)); + float3 _239 = float4((((Primitive.Primitive_LocalToWorld[0u].xyz * _137.xxx) + (Primitive.Primitive_LocalToWorld[1u].xyz * _137.yyy)) + (Primitive.Primitive_LocalToWorld[2u].xyz * _137.zzz)) + (Primitive.Primitive_LocalToWorld[3u].xyz + float3(View.View_PreViewTranslation)), 1.0).xyz; + float3 _242 = float3(EmitterUniforms.EmitterUniforms_RemoveHMDRoll); + float3 _251 = mix(mix(float3(View.View_ViewRight), float3(View.View_HMDViewNoRollRight), _242), EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockRight.xyz, float3(EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockRight.w)); + float3 _259 = mix(-mix(float3(View.View_ViewUp), float3(View.View_HMDViewNoRollUp), _242), EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockUp.xyz, float3(EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockUp.w)); + float3 _260 = float3(View.View_TranslatedWorldCameraOrigin) - _239; + float _261 = dot(_260, _260); + float3 _265 = _260 / float3(sqrt(fast::max(_261, 0.00999999977648258209228515625))); + float3 _335; + float3 _336; + if (EmitterUniforms.EmitterUniforms_CameraFacingBlend[0] > 0.0) + { + float3 _279 = cross(_265, float3(0.0, 0.0, 1.0)); + float3 _284 = _279 / float3(sqrt(fast::max(dot(_279, _279), 0.00999999977648258209228515625))); + float3 _286 = float3(fast::clamp((_261 * EmitterUniforms.EmitterUniforms_CameraFacingBlend[1]) - EmitterUniforms.EmitterUniforms_CameraFacingBlend[2], 0.0, 1.0)); + _335 = normalize(mix(_251, _284, _286)); + _336 = normalize(mix(_259, cross(_265, _284), _286)); + } + else + { + float3 _333; + float3 _334; + if (EmitterUniforms.EmitterUniforms_TangentSelector.y > 0.0) + { + float3 _297 = cross(_265, _160); + _333 = _297 / float3(sqrt(fast::max(dot(_297, _297), 0.00999999977648258209228515625))); + _334 = -_160; + } + else + { + float3 _331; + float3 _332; + if (EmitterUniforms.EmitterUniforms_TangentSelector.z > 0.0) + { + float3 _310 = cross(EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockRight.xyz, _265); + _331 = EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockRight.xyz; + _332 = -(_310 / float3(sqrt(fast::max(dot(_310, _310), 0.00999999977648258209228515625)))); + } + else + { + float3 _329; + float3 _330; + if (EmitterUniforms.EmitterUniforms_TangentSelector.w > 0.0) + { + float3 _322 = cross(_265, float3(0.0, 0.0, 1.0)); + float3 _327 = _322 / float3(sqrt(fast::max(dot(_322, _322), 0.00999999977648258209228515625))); + _329 = _327; + _330 = cross(_265, _327); + } + else + { + _329 = _251; + _330 = _259; + } + _331 = _329; + _332 = _330; + } + _333 = _331; + _334 = _332; + } + _335 = _333; + _336 = _334; + } + float _339 = ((_145.z + ((_145.w * EmitterUniforms.EmitterUniforms_RotationRateScale) * _146)) * 6.283185482025146484375) + EmitterUniforms.EmitterUniforms_RotationBias; + float3 _342 = float3(sin(_339)); + float3 _344 = float3(cos(_339)); + float4 _371 = float4(_239 + ((float3(_204.x * (in.in_var_ATTRIBUTE0.x + EmitterUniforms.EmitterUniforms_PivotOffset.x)) * ((_342 * _336) + (_344 * _335))) + (float3(_204.y * (in.in_var_ATTRIBUTE0.y + EmitterUniforms.EmitterUniforms_PivotOffset.y)) * ((_344 * _336) - (_342 * _335)))), 1.0); + float4 _375 = MobileShadowDepthPass.MobileShadowDepthPass_ProjectionMatrix * float4(_371.x, _371.y, _371.z, _371.w); + float4 _386; + if ((MobileShadowDepthPass.MobileShadowDepthPass_bClampToNearPlane > 0.0) && (_375.z < 0.0)) + { + float4 _384 = _375; + _384.z = 9.9999999747524270787835121154785e-07; + float4 _385 = _384; + _385.w = 1.0; + _386 = _385; + } + else + { + _386 = _375; + } + float4 _396 = _386; + _396.z = ((_386.z * MobileShadowDepthPass.MobileShadowDepthPass_ShadowParams.y) + MobileShadowDepthPass.MobileShadowDepthPass_ShadowParams.x) * _386.w; + out.out_var_TEXCOORD6 = 0.0; + out.gl_Position = _396; + return out; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/amd/gcn_shader.comp b/third_party/spirv-cross/reference/opt/shaders/amd/gcn_shader.comp new file mode 100644 index 0000000..e4bb67e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/amd/gcn_shader.comp @@ -0,0 +1,9 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_AMD_gcn_shader : require +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/amd/shader_ballot.comp b/third_party/spirv-cross/reference/opt/shaders/amd/shader_ballot.comp new file mode 100644 index 0000000..1f27c30 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/amd/shader_ballot.comp @@ -0,0 +1,27 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_ARB_shader_ballot : require +#extension GL_AMD_shader_ballot : require +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer inputData +{ + float inputDataArray[]; +} _12; + +layout(binding = 1, std430) buffer outputData +{ + float outputDataArray[]; +} _74; + +void main() +{ + bool _31 = _12.inputDataArray[gl_LocalInvocationID.x] > 0.0; + uvec4 _37 = uvec4(unpackUint2x32(ballotARB(_31)), 0u, 0u); + uint _44 = mbcntAMD(packUint2x32(uvec2(_37.xy))); + if (_31) + { + _74.outputDataArray[_44] = _12.inputDataArray[gl_LocalInvocationID.x]; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/amd/shader_group_vote.comp b/third_party/spirv-cross/reference/opt/shaders/amd/shader_group_vote.comp new file mode 100644 index 0000000..2669981 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/amd/shader_group_vote.comp @@ -0,0 +1,14 @@ +#version 450 +#extension GL_ARB_shader_group_vote : require +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer inputData +{ + float inputDataArray[]; +} _12; + +void main() +{ + bool _31 = _12.inputDataArray[gl_LocalInvocationID.x] > 0.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/amd/shader_trinary_minmax.comp b/third_party/spirv-cross/reference/opt/shaders/amd/shader_trinary_minmax.comp new file mode 100644 index 0000000..2644551 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/amd/shader_trinary_minmax.comp @@ -0,0 +1,8 @@ +#version 450 +#extension GL_AMD_shader_trinary_minmax : require +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..673ca71 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,16 @@ +#version 450 +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer u0_counters +{ + uint c; +} u0_counter; + +layout(binding = 0, r32ui) uniform writeonly uimageBuffer u0; + +void main() +{ + uint _29 = atomicAdd(u0_counter.c, uint(-1)); + imageStore(u0, floatBitsToInt(uintBitsToFloat(_29)), uvec4(uint(int(gl_GlobalInvocationID.x)))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..e45ae59 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,16 @@ +#version 450 +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer u0_counters +{ + uint c; +} u0_counter; + +layout(binding = 0, r32ui) uniform writeonly uimageBuffer u0; + +void main() +{ + uint _29 = atomicAdd(u0_counter.c, 1u); + imageStore(u0, floatBitsToInt(uintBitsToFloat(_29)), uvec4(uint(int(gl_GlobalInvocationID.x)))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_iadd.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_iadd.asm.comp new file mode 100644 index 0000000..bed2dff --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_iadd.asm.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) restrict buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) restrict buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + _6._m0 = _5._m1 + uvec4(_5._m0); + _6._m0 = uvec4(_5._m0) + _5._m1; + _6._m0 = _5._m1 + _5._m1; + _6._m0 = uvec4(_5._m0 + _5._m0); + _6._m1 = ivec4(_5._m1 + _5._m1); + _6._m1 = _5._m0 + _5._m0; + _6._m1 = ivec4(_5._m1) + _5._m0; + _6._m1 = _5._m0 + ivec4(_5._m1); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..bed3b90 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) restrict buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) restrict buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + _6._m0 = mix(uvec4(0u), uvec4(1u), lessThan(ivec4(_5._m1), _5._m0)); + _6._m0 = mix(uvec4(0u), uvec4(1u), lessThanEqual(ivec4(_5._m1), _5._m0)); + _6._m0 = mix(uvec4(0u), uvec4(1u), lessThan(_5._m1, uvec4(_5._m0))); + _6._m0 = mix(uvec4(0u), uvec4(1u), lessThanEqual(_5._m1, uvec4(_5._m0))); + _6._m0 = mix(uvec4(0u), uvec4(1u), greaterThan(ivec4(_5._m1), _5._m0)); + _6._m0 = mix(uvec4(0u), uvec4(1u), greaterThanEqual(ivec4(_5._m1), _5._m0)); + _6._m0 = mix(uvec4(0u), uvec4(1u), greaterThan(_5._m1, uvec4(_5._m0))); + _6._m0 = mix(uvec4(0u), uvec4(1u), greaterThanEqual(_5._m1, uvec4(_5._m0))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_iequal.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_iequal.asm.comp new file mode 100644 index 0000000..bdb3eeb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_iequal.asm.comp @@ -0,0 +1,33 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + ivec4 _30 = _5._m0; + uvec4 _31 = _5._m1; + bvec4 _34 = equal(ivec4(_31), _30); + bvec4 _35 = equal(_30, ivec4(_31)); + bvec4 _36 = equal(_31, _31); + bvec4 _37 = equal(_30, _30); + _6._m0 = mix(uvec4(0u), uvec4(1u), _34); + _6._m0 = mix(uvec4(0u), uvec4(1u), _35); + _6._m0 = mix(uvec4(0u), uvec4(1u), _36); + _6._m0 = mix(uvec4(0u), uvec4(1u), _37); + _6._m1 = mix(ivec4(0), ivec4(1), _34); + _6._m1 = mix(ivec4(0), ivec4(1), _35); + _6._m1 = mix(ivec4(0), ivec4(1), _36); + _6._m1 = mix(ivec4(0), ivec4(1), _37); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_sar.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_sar.asm.comp new file mode 100644 index 0000000..283b444 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_sar.asm.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + ivec4 _22 = _5._m0; + uvec4 _23 = _5._m1; + _6._m0 = uvec4(ivec4(_23) >> _22); + _6._m0 = uvec4(_22 >> ivec4(_23)); + _6._m0 = uvec4(ivec4(_23) >> ivec4(_23)); + _6._m0 = uvec4(_22 >> _22); + _6._m1 = ivec4(_23) >> ivec4(_23); + _6._m1 = _22 >> _22; + _6._m1 = ivec4(_23) >> _22; + _6._m1 = _22 >> ivec4(_23); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_sdiv.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_sdiv.asm.comp new file mode 100644 index 0000000..e28c481 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_sdiv.asm.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + ivec4 _22 = _5._m0; + uvec4 _23 = _5._m1; + _6._m0 = uvec4(ivec4(_23) / _22); + _6._m0 = uvec4(_22 / ivec4(_23)); + _6._m0 = uvec4(ivec4(_23) / ivec4(_23)); + _6._m0 = uvec4(_22 / _22); + _6._m1 = ivec4(_23) / ivec4(_23); + _6._m1 = _22 / _22; + _6._m1 = ivec4(_23) / _22; + _6._m1 = _22 / ivec4(_23); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_slr.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_slr.asm.comp new file mode 100644 index 0000000..78efaf3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/bitcast_slr.asm.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + ivec4 _22 = _5._m0; + uvec4 _23 = _5._m1; + _6._m0 = _23 >> uvec4(_22); + _6._m0 = uvec4(_22) >> _23; + _6._m0 = _23 >> _23; + _6._m0 = uvec4(_22) >> uvec4(_22); + _6._m1 = ivec4(_23 >> _23); + _6._m1 = ivec4(uvec4(_22) >> uvec4(_22)); + _6._m1 = ivec4(_23 >> uvec4(_22)); + _6._m1 = ivec4(uvec4(_22) >> _23); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..86ba0a3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,37 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct A +{ + int a; + int b; +}; + +layout(binding = 1, std430) buffer C1 +{ + A Data[]; +} C1_1; + +layout(binding = 2, std140) uniform C2 +{ + A Data[1024]; +} C2_1; + +layout(binding = 0, std430) buffer B +{ + A Data[]; +} C3; + +layout(binding = 3, std140) uniform B +{ + A Data[1024]; +} C4; + +void main() +{ + C1_1.Data[gl_GlobalInvocationID.x].a = C2_1.Data[gl_GlobalInvocationID.x].a; + C1_1.Data[gl_GlobalInvocationID.x].b = C2_1.Data[gl_GlobalInvocationID.x].b; + C3.Data[gl_GlobalInvocationID.x].a = C4.Data[gl_GlobalInvocationID.x].a; + C3.Data[gl_GlobalInvocationID.x].b = C4.Data[gl_GlobalInvocationID.x].b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/builtin-compute-bitcast.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/builtin-compute-bitcast.asm.comp new file mode 100644 index 0000000..abb8a79 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/builtin-compute-bitcast.asm.comp @@ -0,0 +1,13 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer BUF +{ + int values[]; +} _6; + +void main() +{ + _6.values[int(gl_WorkGroupID.y)] = int(gl_GlobalInvocationID.z); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/decoration-group.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/decoration-group.asm.comp new file mode 100644 index 0000000..28ad4d4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/decoration-group.asm.comp @@ -0,0 +1,38 @@ +#version 430 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 5, std430) buffer _6_15 +{ + float _m0[]; +} _15; + +layout(binding = 0, std430) buffer _7_16 +{ + float _m0[]; +} _16; + +layout(binding = 1, std430) buffer _8_17 +{ + float _m0[]; +} _17; + +layout(binding = 2, std430) restrict readonly buffer _9_18 +{ + float _m0[]; +} _18; + +layout(binding = 3, std430) restrict readonly buffer _10_19 +{ + float _m0[]; +} _19; + +layout(binding = 4, std430) restrict readonly buffer _11_20 +{ + float _m0[]; +} _20; + +void main() +{ + _15._m0[gl_GlobalInvocationID.x] = (((_16._m0[gl_GlobalInvocationID.x] + _17._m0[gl_GlobalInvocationID.x]) + _18._m0[gl_GlobalInvocationID.x]) + _19._m0[gl_GlobalInvocationID.x]) + _20._m0[gl_GlobalInvocationID.x]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..37b2863 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,7 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/hlsl-functionality.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/hlsl-functionality.asm.comp new file mode 100644 index 0000000..d30cdda --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/hlsl-functionality.asm.comp @@ -0,0 +1,19 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer Buf +{ + vec4 _data[]; +} Buf_1; + +layout(binding = 1, std430) buffer Buf_count +{ + int _count; +} Buf_count_1; + +void main() +{ + int _33 = atomicAdd(Buf_count_1._count, 1); + Buf_1._data[_33] = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/logical.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/logical.asm.comp new file mode 100644 index 0000000..124652b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/logical.asm.comp @@ -0,0 +1,7 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/multiple-entry.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/multiple-entry.asm.comp new file mode 100644 index 0000000..6418464 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/multiple-entry.asm.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) restrict buffer _6_8 +{ + ivec4 _m0; + uvec4 _m1; +} _8; + +layout(binding = 1, std430) restrict buffer _7_9 +{ + uvec4 _m0; + ivec4 _m1; +} _9; + +void main() +{ + _9._m0 = _8._m1 + uvec4(_8._m0); + _9._m0 = uvec4(_8._m0) + _8._m1; + _9._m0 = _8._m1 + _8._m1; + _9._m0 = uvec4(_8._m0 + _8._m0); + _9._m1 = ivec4(_8._m1 + _8._m1); + _9._m1 = _8._m0 + _8._m0; + _9._m1 = ivec4(_8._m1) + _8._m0; + _9._m1 = _8._m0 + ivec4(_8._m1); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/nmin-max-clamp.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/nmin-max-clamp.asm.comp new file mode 100644 index 0000000..5ef1bc9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/nmin-max-clamp.asm.comp @@ -0,0 +1,47 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float a1; + vec2 a2; + vec3 a3; + vec4 a4; + float b1; + vec2 b2; + vec3 b3; + vec4 b4; + float c1; + vec2 c2; + vec3 c3; + vec4 c4; +} _4; + +void main() +{ + _4.a1 = isnan(_4.c1) ? _4.b1 : (isnan(_4.b1) ? _4.c1 : min(_4.b1, _4.c1)); + _4.a2 = mix(mix(min(_4.b2, _4.c2), _4.c2, isnan(_4.b2)), _4.b2, isnan(_4.c2)); + _4.a3 = mix(mix(min(_4.b3, _4.c3), _4.c3, isnan(_4.b3)), _4.b3, isnan(_4.c3)); + _4.a4 = mix(mix(min(_4.b4, _4.c4), _4.c4, isnan(_4.b4)), _4.b4, isnan(_4.c4)); + _4.a1 = isnan(_4.c1) ? _4.b1 : (isnan(_4.b1) ? _4.c1 : max(_4.b1, _4.c1)); + _4.a2 = mix(mix(max(_4.b2, _4.c2), _4.c2, isnan(_4.b2)), _4.b2, isnan(_4.c2)); + _4.a3 = mix(mix(max(_4.b3, _4.c3), _4.c3, isnan(_4.b3)), _4.b3, isnan(_4.c3)); + _4.a4 = mix(mix(max(_4.b4, _4.c4), _4.c4, isnan(_4.b4)), _4.b4, isnan(_4.c4)); + float _180 = isnan(_4.b1) ? _4.a1 : (isnan(_4.a1) ? _4.b1 : max(_4.a1, _4.b1)); + _4.a1 = isnan(_4.c1) ? _180 : (isnan(_180) ? _4.c1 : min(_180, _4.c1)); + vec2 _191 = mix(mix(max(_4.a2, _4.b2), _4.b2, isnan(_4.a2)), _4.a2, isnan(_4.b2)); + _4.a2 = mix(mix(min(_191, _4.c2), _4.c2, isnan(_191)), _191, isnan(_4.c2)); + vec3 _202 = mix(mix(max(_4.a3, _4.b3), _4.b3, isnan(_4.a3)), _4.a3, isnan(_4.b3)); + _4.a3 = mix(mix(min(_202, _4.c3), _4.c3, isnan(_202)), _202, isnan(_4.c3)); + vec4 _213 = mix(mix(max(_4.a4, _4.b4), _4.b4, isnan(_4.a4)), _4.a4, isnan(_4.b4)); + _4.a4 = mix(mix(min(_213, _4.c4), _4.c4, isnan(_213)), _213, isnan(_4.c4)); + for (int _139 = 0; _139 < 2; ) + { + _4.a2 = mix(mix(min(_4.b2, _4.c2), _4.c2, isnan(_4.b2)), _4.b2, isnan(_4.c2)); + float _229 = isnan(_4.b2.x) ? _4.a1 : (isnan(_4.a1) ? _4.b2.x : max(_4.a1, _4.b2.x)); + _4.a1 = isnan(_4.b2.y) ? _229 : (isnan(_229) ? _4.b2.y : min(_229, _4.b2.y)); + _139++; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/op-phi-swap.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/op-phi-swap.asm.comp new file mode 100644 index 0000000..a1a57e1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/op-phi-swap.asm.comp @@ -0,0 +1,39 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_4 +{ + float _m0[]; +} _4; + +layout(binding = 1, std430) buffer _3_5 +{ + float _m0[]; +} _5; + +void main() +{ + bool _34; + float _35; + float _35_copy; + float _36; + _34 = true; + _35 = _4._m0[gl_GlobalInvocationID.x]; + _36 = 8.5; + for (;;) + { + if (_34) + { + _34 = false; + _35_copy = _35; + _35 = _36; + _36 = _35_copy; + } + else + { + break; + } + } + _5._m0[gl_GlobalInvocationID.x] = _35 - _36; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/quantize.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/quantize.asm.comp new file mode 100644 index 0000000..c089213 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/quantize.asm.comp @@ -0,0 +1,19 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + float scalar; + vec2 vec2_val; + vec3 vec3_val; + vec4 vec4_val; +} _4; + +void main() +{ + _4.scalar = unpackHalf2x16(packHalf2x16(vec2(_4.scalar))).x; + _4.vec2_val = unpackHalf2x16(packHalf2x16(_4.vec2_val)); + _4.vec3_val = vec3(unpackHalf2x16(packHalf2x16(_4.vec3_val.xy)), unpackHalf2x16(packHalf2x16(_4.vec3_val.zz)).x); + _4.vec4_val = vec4(unpackHalf2x16(packHalf2x16(_4.vec4_val.xy)), unpackHalf2x16(packHalf2x16(_4.vec4_val.zw))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/recompile-block-naming.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/recompile-block-naming.asm.comp new file mode 100644 index 0000000..7d43b6f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/recompile-block-naming.asm.comp @@ -0,0 +1,35 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer MyFirstBuffer +{ + uint _data[]; +} MyFirstBuffer_1; + +layout(binding = 0, std430) buffer MySecondBuffer +{ + uint _data[]; +} MySecondBuffer_1; + +layout(binding = 0, std430) buffer MyThirdBuffer +{ + uint _data[]; +} MyThirdBuffer_1; + +void main() +{ + uint _106 = MyFirstBuffer_1._data[0]; + uint _110 = MyFirstBuffer_1._data[1]; + uint _114 = MyFirstBuffer_1._data[2]; + uint _118 = MyFirstBuffer_1._data[3]; + uint _123 = MySecondBuffer_1._data[1]; + uint _127 = MySecondBuffer_1._data[2]; + uint _131 = MySecondBuffer_1._data[3]; + uint _135 = MySecondBuffer_1._data[4]; + uvec4 _141 = uvec4(_106, _110, _114, _118) + uvec4(_123, _127, _131, _135); + MyThirdBuffer_1._data[0] = _141.x; + MyThirdBuffer_1._data[1] = _141.y; + MyThirdBuffer_1._data[2] = _141.z; + MyThirdBuffer_1._data[3] = _141.w; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/specialization-constant-workgroup.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/specialization-constant-workgroup.asm.comp new file mode 100644 index 0000000..8016eba --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/specialization-constant-workgroup.asm.comp @@ -0,0 +1,21 @@ +#version 310 es + +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 9u +#endif +#ifndef SPIRV_CROSS_CONSTANT_ID_12 +#define SPIRV_CROSS_CONSTANT_ID_12 4u +#endif + +layout(local_size_x = SPIRV_CROSS_CONSTANT_ID_10, local_size_y = 20, local_size_z = SPIRV_CROSS_CONSTANT_ID_12) in; + +layout(binding = 0, std430) buffer SSBO +{ + float a; +} _4; + +void main() +{ + _4.a += 1.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/comp/switch-break-ladder.asm.comp b/third_party/spirv-cross/reference/opt/shaders/asm/comp/switch-break-ladder.asm.comp new file mode 100644 index 0000000..2aebe79 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/comp/switch-break-ladder.asm.comp @@ -0,0 +1,71 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer BUF +{ + int a; + int b; + int d; +} o; + +void main() +{ + int _44; + _44 = o.a; + int _41; + for (;;) + { + int _48; + bool _22_ladder_break = false; + switch (_44) + { + case 5: + { + for (;;) + { + bool _30_ladder_break = false; + switch (o.d) + { + case 10: + case 20: + { + _30_ladder_break = true; + break; + } + default: + { + continue; + } + } + if (_30_ladder_break) + { + break; + } + } + _48 = _44 + _44; + break; + } + case 1: + case 2: + case 3: + { + _22_ladder_break = true; + break; + } + default: + { + _48 = _44; + break; + } + } + if (_22_ladder_break) + { + break; + } + _41 = _48 + 1; + _44 = _41; + continue; + } + o.b = _44; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag new file mode 100644 index 0000000..b5e59f8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombineduTexuSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; + +void main() +{ + FragColor = texture(SPIRV_Cross_CombineduTexuSampler, vUV); + FragColor += textureOffset(SPIRV_Cross_CombineduTexuSampler, vUV, ivec2(1)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag.vk b/third_party/spirv-cross/reference/opt/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag.vk new file mode 100644 index 0000000..bce9808 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(set = 0, binding = 1) uniform texture2D uTex; +layout(set = 0, binding = 0) uniform sampler uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; + +void main() +{ + FragColor = texture(sampler2D(uTex, uSampler), vUV); + FragColor += textureOffset(sampler2D(uTex, uSampler), vUV, ivec2(1)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/complex-name-workarounds.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/complex-name-workarounds.asm.frag new file mode 100644 index 0000000..d68f84b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/complex-name-workarounds.asm.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in vec4 _; +layout(location = 1) in vec4 a; +layout(location = 0) out vec4 b; + +void main() +{ + vec4 _28 = (_ + a) + _; + b = _28; + b = _; + b = _28; + b = _; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag new file mode 100644 index 0000000..45e83db --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag @@ -0,0 +1,12 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) in vec2 foo; +layout(location = 0) out float FooOut; + +void main() +{ + FooOut = foo.x + foo.y; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/default-member-names.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/default-member-names.asm.frag new file mode 100644 index 0000000..13f81b1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/default-member-names.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) out vec4 _3; + +float _49; + +void main() +{ + _3 = vec4(_49); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/do-while-statement-fallback.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/do-while-statement-fallback.asm.frag new file mode 100644 index 0000000..35bec98 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/do-while-statement-fallback.asm.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = 5.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..05ce10a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/empty-struct.asm.frag @@ -0,0 +1,6 @@ +#version 450 + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/for-loop-phi-only-continue.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/for-loop-phi-only-continue.asm.frag new file mode 100644 index 0000000..7a78d00 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/for-loop-phi-only-continue.asm.frag @@ -0,0 +1,17 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + float _19; + _19 = 0.0; + for (int _22 = 0; _22 < 16; ) + { + _19 += 1.0; + _22++; + continue; + } + FragColor = vec4(_19); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/frem.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/frem.asm.frag new file mode 100644 index 0000000..1095ab0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/frem.asm.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; + +void main() +{ + FragColor = vA - vB * trunc(vA / vB); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..16b4994 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(10.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag new file mode 100644 index 0000000..924b6f6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +uniform samplerCubeShadow SPIRV_Cross_CombinedpointLightShadowMapshadowSamplerPCF; + +layout(location = 0) out float _entryPointOutput; + +void main() +{ + _entryPointOutput = textureGrad(SPIRV_Cross_CombinedpointLightShadowMapshadowSamplerPCF, vec4(vec4(0.100000001490116119384765625, 0.100000001490116119384765625, 0.100000001490116119384765625, 0.5).xyz, 0.5), vec3(0.0), vec3(0.0)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag new file mode 100644 index 0000000..c4e3704 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag @@ -0,0 +1,14 @@ +#version 450 + +uniform sampler2DArrayShadow SPIRV_Cross_CombinedShadowMapShadowSamplerPCF; + +layout(location = 0) in vec2 texCoords; +layout(location = 1) in float cascadeIndex; +layout(location = 2) in float fragDepth; +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + _entryPointOutput = vec4(textureGrad(SPIRV_Cross_CombinedShadowMapShadowSamplerPCF, vec4(vec4(texCoords, cascadeIndex, fragDepth).xyz, fragDepth), vec2(0.0), vec2(0.0))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..ab2749b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uTexture; + +layout(location = 0) out ivec2 Size; + +void main() +{ + Size = textureSize(uTexture, 0) + textureSize(uTexture, 1); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag new file mode 100644 index 0000000..350142d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_CombinedSampledImageSampler; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + ivec2 _154 = ivec3(int(gl_FragCoord.x * 1280.0), int(gl_FragCoord.y * 720.0), 0).xy; + _entryPointOutput = ((texelFetch(SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler, _154, 0) + texelFetch(SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler, _154, 0)) + texture(SPIRV_Cross_CombinedSampledImageSampler, gl_FragCoord.xy)) + texture(SPIRV_Cross_CombinedSampledImageSampler, gl_FragCoord.xy); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag.vk new file mode 100644 index 0000000..b898b01 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag.vk @@ -0,0 +1,14 @@ +#version 450 +#extension GL_EXT_samplerless_texture_functions : require + +layout(set = 0, binding = 0) uniform sampler Sampler; +layout(set = 0, binding = 0) uniform texture2D SampledImage; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + ivec2 _154 = ivec3(int(gl_FragCoord.x * 1280.0), int(gl_FragCoord.y * 720.0), 0).xy; + _entryPointOutput = ((texelFetch(SampledImage, _154, 0) + texelFetch(SampledImage, _154, 0)) + texture(sampler2D(SampledImage, Sampler), gl_FragCoord.xy)) + texture(sampler2D(SampledImage, Sampler), gl_FragCoord.xy); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag new file mode 100644 index 0000000..350142d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_CombinedSampledImageSampler; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + ivec2 _154 = ivec3(int(gl_FragCoord.x * 1280.0), int(gl_FragCoord.y * 720.0), 0).xy; + _entryPointOutput = ((texelFetch(SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler, _154, 0) + texelFetch(SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler, _154, 0)) + texture(SPIRV_Cross_CombinedSampledImageSampler, gl_FragCoord.xy)) + texture(SPIRV_Cross_CombinedSampledImageSampler, gl_FragCoord.xy); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag.vk new file mode 100644 index 0000000..21797b4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler Sampler; +layout(set = 0, binding = 0) uniform texture2D SampledImage; +layout(set = 0, binding = 0) uniform sampler SPIRV_Cross_DummySampler; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + ivec2 _154 = ivec3(int(gl_FragCoord.x * 1280.0), int(gl_FragCoord.y * 720.0), 0).xy; + _entryPointOutput = ((texelFetch(sampler2D(SampledImage, SPIRV_Cross_DummySampler), _154, 0) + texelFetch(sampler2D(SampledImage, SPIRV_Cross_DummySampler), _154, 0)) + texture(sampler2D(SampledImage, Sampler), gl_FragCoord.xy)) + texture(sampler2D(SampledImage, Sampler), gl_FragCoord.xy); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag new file mode 100644 index 0000000..05ce10a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag @@ -0,0 +1,6 @@ +#version 450 + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag.vk b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag.vk new file mode 100644 index 0000000..05ce10a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag.vk @@ -0,0 +1,6 @@ +#version 450 + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.vk.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.vk.asm.frag new file mode 100644 index 0000000..05ce10a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.vk.asm.frag @@ -0,0 +1,6 @@ +#version 450 + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.vk.asm.frag.vk b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.vk.asm.frag.vk new file mode 100644 index 0000000..05ce10a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/image-query-no-sampler.vk.asm.frag.vk @@ -0,0 +1,6 @@ +#version 450 + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..8a7f64d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,40 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uImage; + +layout(location = 0) in vec4 v0; +layout(location = 0) out vec4 FragColor; + +void main() +{ + float phi; + vec4 _36; + int _51; + _51 = 0; + phi = 1.0; + _36 = vec4(1.0, 2.0, 1.0, 2.0); + for (;;) + { + FragColor = _36; + if (_51 < 4) + { + if (v0[_51] > 0.0) + { + vec2 _48 = vec2(phi); + _51++; + phi += 2.0; + _36 = textureLod(uImage, _48, 0.0); + continue; + } + else + { + break; + } + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/inf-nan-constant-double.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/inf-nan-constant-double.asm.frag new file mode 100644 index 0000000..d8e29aa --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/inf-nan-constant-double.asm.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require + +layout(location = 0) out vec3 FragColor; +layout(location = 0) flat in double vTmp; + +void main() +{ + FragColor = vec3(dvec3(uint64BitsToDouble(0x7ff0000000000000ul), uint64BitsToDouble(0xfff0000000000000ul), uint64BitsToDouble(0x7ff8000000000000ul)) + dvec3(vTmp)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..dd4284c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out highp vec3 FragColor; + +void main() +{ + FragColor = vec3(uintBitsToFloat(0x7f800000u), uintBitsToFloat(0xff800000u), uintBitsToFloat(0x7fc00000u)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/invalidation.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/invalidation.asm.frag new file mode 100644 index 0000000..c0dc7b6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/invalidation.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in float v0; +layout(location = 1) in float v1; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = (v0 + v1) * v1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..5540808 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,71 @@ +#version 450 +#extension GL_GOOGLE_cpp_style_line_directive : require + +layout(location = 0) out float FragColor; +layout(location = 0) in float vColor; + +#line 8 "test.frag" +void main() +{ +#line 8 "test.frag" + FragColor = 1.0; +#line 9 "test.frag" + FragColor = 2.0; +#line 10 "test.frag" + if (vColor < 0.0) + { +#line 12 "test.frag" + FragColor = 3.0; + } + else + { +#line 16 "test.frag" + FragColor = 4.0; + } + for (int _127 = 0; float(_127) < (40.0 + vColor); ) + { +#line 21 "test.frag" + FragColor += 0.20000000298023223876953125; +#line 22 "test.frag" + FragColor += 0.300000011920928955078125; + _127 += (int(vColor) + 5); + continue; + } + switch (int(vColor)) + { + case 0: + { +#line 28 "test.frag" + FragColor += 0.20000000298023223876953125; +#line 29 "test.frag" + break; + } + case 1: + { +#line 32 "test.frag" + FragColor += 0.4000000059604644775390625; +#line 33 "test.frag" + break; + } + default: + { +#line 36 "test.frag" + FragColor += 0.800000011920928955078125; +#line 37 "test.frag" + break; + } + } + for (;;) + { + FragColor += (10.0 + vColor); +#line 43 "test.frag" + if (FragColor < 100.0) + { + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/locations-components.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/locations-components.asm.frag new file mode 100644 index 0000000..b1e3784 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/locations-components.asm.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(location = 1) in vec2 _2; +layout(location = 1, component = 2) in float _3; +layout(location = 2) flat in float _4; +layout(location = 2, component = 1) flat in uint _5; +layout(location = 2, component = 2) flat in uint _6; +layout(location = 0) out vec4 o0; +vec4 v1; +vec4 v2; + +void main() +{ + v1 = vec4(_2.x, _2.y, v1.z, v1.w); + v1.z = _3; + v2.x = _4; + v2.y = uintBitsToFloat(_5); + v2.z = uintBitsToFloat(_6); + o0.y = float(floatBitsToUint(intBitsToFloat(floatBitsToInt(v2.y) + floatBitsToInt(v2.z)))); + o0.x = v1.y + v2.x; + o0 = vec4(o0.x, o0.y, v1.z, v1.x); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag new file mode 100644 index 0000000..4df83d4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag @@ -0,0 +1,70 @@ +#version 450 + +layout(binding = 0, std140) uniform Foo +{ + layout(row_major) mat4 lightVP[64]; + uint shadowCascadesNum; + int test; +} _11; + +layout(location = 0) in vec3 fragWorld; +layout(location = 0) out int _entryPointOutput; + +int _231; + +mat4 SPIRV_Cross_workaround_load_row_major(mat4 wrap) { return wrap; } + +void main() +{ + int _228; + do + { + bool _225; + int _229; + uint _222 = 0u; + for (;;) + { + if (_222 < _11.shadowCascadesNum) + { + mat4 _223; + do + { + if (_11.test == 0) + { + _223 = mat4(vec4(0.5, 0.0, 0.0, 0.0), vec4(0.0, 0.5, 0.0, 0.0), vec4(0.0, 0.0, 0.5, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + break; + } + _223 = mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + break; + } while(false); + vec4 _170 = (_223 * SPIRV_Cross_workaround_load_row_major(_11.lightVP[_222])) * vec4(fragWorld, 1.0); + float _172 = _170.z; + float _179 = _170.x; + float _181 = _170.y; + if ((((_172 >= 0.0) && (_172 <= 1.0)) && (max(_179, _181) <= 1.0)) && (min(_179, _181) >= 0.0)) + { + _229 = int(_222); + _225 = true; + break; + } + _222++; + continue; + } + else + { + _229 = _231; + _225 = false; + break; + } + } + if (_225) + { + _228 = _229; + break; + } + _228 = -1; + break; + } while(false); + _entryPointOutput = _228; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/loop-header-to-continue.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/loop-header-to-continue.asm.frag new file mode 100644 index 0000000..6497ad7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/loop-header-to-continue.asm.frag @@ -0,0 +1,45 @@ +#version 450 + +struct Params +{ + vec4 TextureSize; + vec4 Params1; + vec4 Params2; + vec4 Params3; + vec4 Params4; + vec4 Bloom; +}; + +layout(binding = 1, std140) uniform CB1 +{ + Params CB1; +} _8; + +uniform sampler2D SPIRV_Cross_CombinedmapTexturemapSampler; + +layout(location = 0) in vec2 IN_uv; +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + vec2 _45 = vec2(0.0, _8.CB1.TextureSize.w); + vec4 _49 = texture(SPIRV_Cross_CombinedmapTexturemapSampler, IN_uv); + float _50 = _49.y; + float _53 = clamp(_50 * 0.06399999558925628662109375, 7.999999797903001308441162109375e-05, 0.008000000379979610443115234375); + float _55; + float _58; + _55 = 0.0; + _58 = 0.0; + for (int _60 = -3; _60 <= 3; ) + { + float _64 = float(_60); + vec4 _72 = texture(SPIRV_Cross_CombinedmapTexturemapSampler, IN_uv + (_45 * _64)); + float _78 = exp(((-_64) * _64) * 0.2222220003604888916015625) * float(abs(_72.y - _50) < _53); + _55 += (_72.x * _78); + _58 += _78; + _60++; + continue; + } + _entryPointOutput = vec4(_55 / _58, _50, 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..d88c0e3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,42 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _46[16] = float[](1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0); +const vec4 _76[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + +layout(location = 0) out float FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + vec4 foobar[4] = _76; + vec4 baz[4] = _76; + FragColor = _46[index]; + if (index < 10) + { + FragColor += _46[index ^ 1]; + } + else + { + FragColor += _46[index & 1]; + } + bool _99 = index > 30; + if (_99) + { + FragColor += _76[index & 3].y; + } + else + { + FragColor += _76[index & 1].x; + } + if (_99) + { + foobar[1].z = 20.0; + } + mediump int _37 = index & 3; + FragColor += foobar[_37].z; + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[_37].z; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/multi-for-loop-init.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/multi-for-loop-init.asm.frag new file mode 100644 index 0000000..679d9d5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/multi-for-loop-init.asm.frag @@ -0,0 +1,22 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int counter; + +void main() +{ + FragColor = vec4(0.0); + mediump int _53 = 0; + mediump uint _54 = 1u; + for (; (_53 < 10) && (int(_54) < int(20u)); ) + { + FragColor += vec4(float(_53)); + FragColor += vec4(float(_54)); + _54 += uint(counter); + _53 += counter; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/op-constant-null.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/op-constant-null.asm.frag new file mode 100644 index 0000000..873a64c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/op-constant-null.asm.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = 0.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/op-phi-swap-continue-block.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/op-phi-swap-continue-block.asm.frag new file mode 100644 index 0000000..ee45619 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/op-phi-swap-continue-block.asm.frag @@ -0,0 +1,29 @@ +#version 450 + +layout(binding = 0, std140) uniform UBO +{ + int uCount; + int uJ; + int uK; +} _5; + +layout(location = 0) out float FragColor; + +void main() +{ + int _23; + int _23_copy; + int _24; + _23 = _5.uK; + _24 = _5.uJ; + for (int _26 = 0; _26 < _5.uCount; ) + { + _23_copy = _23; + _23 = _24; + _24 = _23_copy; + _26++; + continue; + } + FragColor = float(_24 - _23) * float(_5.uJ * _5.uK); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/pack-and-unpack-uint2.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/pack-and-unpack-uint2.asm.frag new file mode 100644 index 0000000..6d81fa6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/pack-and-unpack-uint2.asm.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require + +layout(location = 0) out vec4 FragColor; + +void main() +{ + uvec2 unpacked = unpackUint2x32(packUint2x32(uvec2(18u, 52u))); + FragColor = vec4(float(unpacked.x), float(unpacked.y), 1.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..fbbfe18 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,16 @@ +#version 450 + +struct Registers +{ + float foo; +}; + +uniform Registers registers; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = 10.0 + registers.foo; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/phi-loop-variable.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/phi-loop-variable.asm.frag new file mode 100644 index 0000000..05ce10a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/phi-loop-variable.asm.frag @@ -0,0 +1,6 @@ +#version 450 + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..d6489ed --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_Combinedg_Textureg_Sampler; +uniform sampler2DShadow SPIRV_Cross_Combinedg_Textureg_CompareSampler; + +layout(location = 0) in vec2 in_var_TEXCOORD0; +layout(location = 0) out float out_var_SV_Target; + +void main() +{ + out_var_SV_Target = texture(SPIRV_Cross_Combinedg_Textureg_Sampler, in_var_TEXCOORD0).x + textureLod(SPIRV_Cross_Combinedg_Textureg_CompareSampler, vec3(in_var_TEXCOORD0, 0.5), 0.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag new file mode 100644 index 0000000..bdda0d6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag @@ -0,0 +1,18 @@ +#version 450 + +struct Registers +{ + int index; +}; + +uniform Registers registers; + +uniform sampler2D SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler[4]; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = (texelFetch(SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler[registers.index], ivec2(10), 0) + texelFetch(SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler[registers.index], ivec2(4), 0)) + texelFetch(SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler[registers.index], ivec2(4), 0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag new file mode 100644 index 0000000..89058f1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(binding = 0, rgba32f) uniform writeonly imageBuffer RWTex; +layout(binding = 1) uniform samplerBuffer Tex; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + imageStore(RWTex, 20, vec4(1.0, 2.0, 3.0, 4.0)); + _entryPointOutput = texelFetch(Tex, 10); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..45fc4b1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,23 @@ +#version 460 + +struct myType +{ + float data; +}; + +const myType _21[5] = myType[](myType(0.0), myType(1.0), myType(0.0), myType(1.0), myType(0.0)); + +layout(location = 0) out vec4 o_color; + +void main() +{ + if (_21[int(mod(gl_FragCoord.x, 4.0))].data > 0.0) + { + o_color = vec4(0.0, 1.0, 0.0, 1.0); + } + else + { + o_color = vec4(1.0, 0.0, 0.0, 1.0); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/srem.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/srem.asm.frag new file mode 100644 index 0000000..05a3d75 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/srem.asm.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in ivec4 vA; +layout(location = 1) flat in ivec4 vB; + +void main() +{ + FragColor = vec4(vA - vB * (vA / vB)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..2293587 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) out vec4 FragColors[2]; +layout(location = 2) out vec4 FragColor; + +void main() +{ + FragColors = vec4[](vec4(1.0, 2.0, 3.0, 4.0), vec4(10.0)); + FragColor = vec4(5.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag new file mode 100644 index 0000000..b2473f4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct Foo +{ + float var1; + float var2; +}; + +layout(binding = 0) uniform mediump sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; + +Foo _22; + +void main() +{ + FragColor = texture(uSampler, vec2(_22.var1, _22.var2)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000..748f96c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,32 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) flat in mediump int vIndex; +layout(location = 0) out float FragColor; + +void main() +{ + highp float _19; + switch (vIndex) + { + case 0: + case 2: + { + _19 = 1.0; + break; + } + default: + { + _19 = 3.0; + break; + } + case 8: + { + _19 = 8.0; + break; + } + } + FragColor = _19; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/temporary-name-alias.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/temporary-name-alias.asm.frag new file mode 100644 index 0000000..05ce10a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/temporary-name-alias.asm.frag @@ -0,0 +1,6 @@ +#version 450 + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/temporary-phi-hoisting.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/temporary-phi-hoisting.asm.frag new file mode 100644 index 0000000..ade51d4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/temporary-phi-hoisting.asm.frag @@ -0,0 +1,27 @@ +#version 450 + +struct MyStruct +{ + vec4 color; +}; + +layout(binding = 0, std140) uniform MyStruct_CB +{ + MyStruct g_MyStruct[4]; +} _6; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + vec3 _28; + _28 = vec3(0.0); + for (int _31 = 0; _31 < 4; ) + { + _28 += _6.g_MyStruct[_31].color.xyz; + _31++; + continue; + } + _entryPointOutput = vec4(_28, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..6193de0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uTexture; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texelFetch(uTexture, ivec2(gl_FragCoord.xy), 0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag new file mode 100644 index 0000000..e0feb49 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag @@ -0,0 +1,19 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif + +layout(binding = 0) uniform sampler2D uTexture; + +layout(location = 0) out f16vec4 FragColor; +layout(location = 0) in f16vec2 UV; + +void main() +{ + FragColor = f16vec4(texture(uTexture, UV)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag.vk new file mode 100644 index 0000000..e13e425 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag.vk @@ -0,0 +1,20 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_EXT_shader_explicit_arithmetic_types_float16) +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require +#else +#error No extension available for FP16. +#endif +#extension GL_EXT_shader_16bit_storage : require + +layout(set = 0, binding = 0) uniform sampler2D uTexture; + +layout(location = 0) out f16vec4 FragColor; +layout(location = 0) in f16vec2 UV; + +void main() +{ + FragColor = f16vec4(texture(uTexture, UV)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/undef-variable-store.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/undef-variable-store.asm.frag new file mode 100644 index 0000000..84eb23a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/undef-variable-store.asm.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + _entryPointOutput = vec4(1.0, 1.0, 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000..6953ec6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(binding = 0) uniform sampler2DShadow uShadow; +uniform sampler2DShadow SPIRV_Cross_CombineduTextureuSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(uShadow, vec3(vUV.xy, vUV.z)) + texture(SPIRV_Cross_CombineduTextureuSampler, vec3(vUV.xy, vUV.z)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk new file mode 100644 index 0000000..2f99703 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2DShadow uShadow; +layout(set = 0, binding = 1) uniform texture2D uTexture; +layout(set = 0, binding = 2) uniform samplerShadow uSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(uShadow, vec3(vUV.xy, vUV.z)) + texture(sampler2DShadow(uTexture, uSampler), vec3(vUV.xy, vUV.z)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..c8986c1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/unreachable.asm.frag @@ -0,0 +1,24 @@ +#version 450 + +layout(location = 0) flat in int counter; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 _33; + for (;;) + { + if (counter == 10) + { + _33 = vec4(10.0); + break; + } + else + { + _33 = vec4(30.0); + break; + } + } + FragColor = _33; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/frag/vector-shuffle-oom.asm.frag b/third_party/spirv-cross/reference/opt/shaders/asm/frag/vector-shuffle-oom.asm.frag new file mode 100644 index 0000000..427a3a5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/frag/vector-shuffle-oom.asm.frag @@ -0,0 +1,277 @@ +#version 450 + +struct _28 +{ + vec4 _m0; +}; + +layout(binding = 0, std140) uniform _6_7 +{ + vec4 _m0; + float _m1; + vec4 _m2; +} _7; + +layout(binding = 2, std140) uniform _10_11 +{ + vec3 _m0; + vec3 _m1; + float _m2; + vec3 _m3; + float _m4; + vec3 _m5; + float _m6; + vec3 _m7; + float _m8; + vec3 _m9; + float _m10; + vec3 _m11; + float _m12; + vec2 _m13; + vec2 _m14; + vec3 _m15; + float _m16; + float _m17; + float _m18; + float _m19; + float _m20; + vec4 _m21; + vec4 _m22; + layout(row_major) mat4 _m23; + vec4 _m24; +} _11; + +layout(binding = 1, std140) uniform _18_19 +{ + layout(row_major) mat4 _m0; + layout(row_major) mat4 _m1; + layout(row_major) mat4 _m2; + layout(row_major) mat4 _m3; + vec4 _m4; + vec4 _m5; + float _m6; + float _m7; + float _m8; + float _m9; + vec3 _m10; + float _m11; + vec3 _m12; + float _m13; + vec3 _m14; + float _m15; + vec3 _m16; + float _m17; + float _m18; + float _m19; + vec2 _m20; + vec2 _m21; + vec2 _m22; + vec4 _m23; + vec2 _m24; + vec2 _m25; + vec2 _m26; + vec3 _m27; + float _m28; + float _m29; + float _m30; + float _m31; + float _m32; + vec2 _m33; + float _m34; + float _m35; + vec3 _m36; + layout(row_major) mat4 _m37[2]; + vec4 _m38[2]; +} _19; + +uniform sampler2D SPIRV_Cross_Combined; +uniform sampler2D SPIRV_Cross_Combined_1; +uniform sampler2D SPIRV_Cross_Combined_2; + +layout(location = 0) out vec4 _5; + +_28 _74; + +void main() +{ + vec2 _82 = gl_FragCoord.xy * _19._m23.xy; + vec4 _88 = _7._m2 * _7._m0.xyxy; + vec2 _95 = _88.xy; + vec2 _96 = _88.zw; + vec2 _97 = clamp(_82 + (vec2(0.0, -2.0) * _7._m0.xy), _95, _96); + vec3 _109 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _97, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _113 = textureLod(SPIRV_Cross_Combined_1, _97, 0.0); + float _114 = _113.y; + vec3 _129; + if (_114 > 0.0) + { + _129 = _109 + (textureLod(SPIRV_Cross_Combined_2, _97, 0.0).xyz * clamp(_114 * _113.z, 0.0, 1.0)); + } + else + { + _129 = _109; + } + vec2 _144 = clamp(_82 + (vec2(-1.0) * _7._m0.xy), _95, _96); + vec3 _156 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _144, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _160 = textureLod(SPIRV_Cross_Combined_1, _144, 0.0); + float _161 = _160.y; + vec3 _176; + if (_161 > 0.0) + { + _176 = _156 + (textureLod(SPIRV_Cross_Combined_2, _144, 0.0).xyz * clamp(_161 * _160.z, 0.0, 1.0)); + } + else + { + _176 = _156; + } + vec2 _191 = clamp(_82 + (vec2(0.0, -1.0) * _7._m0.xy), _95, _96); + vec3 _203 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _191, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _207 = textureLod(SPIRV_Cross_Combined_1, _191, 0.0); + float _208 = _207.y; + vec3 _223; + if (_208 > 0.0) + { + _223 = _203 + (textureLod(SPIRV_Cross_Combined_2, _191, 0.0).xyz * clamp(_208 * _207.z, 0.0, 1.0)); + } + else + { + _223 = _203; + } + vec2 _238 = clamp(_82 + (vec2(1.0, -1.0) * _7._m0.xy), _95, _96); + vec3 _250 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _238, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _254 = textureLod(SPIRV_Cross_Combined_1, _238, 0.0); + float _255 = _254.y; + vec3 _270; + if (_255 > 0.0) + { + _270 = _250 + (textureLod(SPIRV_Cross_Combined_2, _238, 0.0).xyz * clamp(_255 * _254.z, 0.0, 1.0)); + } + else + { + _270 = _250; + } + vec2 _285 = clamp(_82 + (vec2(-2.0, 0.0) * _7._m0.xy), _95, _96); + vec3 _297 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _285, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _301 = textureLod(SPIRV_Cross_Combined_1, _285, 0.0); + float _302 = _301.y; + vec3 _317; + if (_302 > 0.0) + { + _317 = _297 + (textureLod(SPIRV_Cross_Combined_2, _285, 0.0).xyz * clamp(_302 * _301.z, 0.0, 1.0)); + } + else + { + _317 = _297; + } + vec2 _332 = clamp(_82 + (vec2(-1.0, 0.0) * _7._m0.xy), _95, _96); + vec3 _344 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _332, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _348 = textureLod(SPIRV_Cross_Combined_1, _332, 0.0); + float _349 = _348.y; + vec3 _364; + if (_349 > 0.0) + { + _364 = _344 + (textureLod(SPIRV_Cross_Combined_2, _332, 0.0).xyz * clamp(_349 * _348.z, 0.0, 1.0)); + } + else + { + _364 = _344; + } + vec2 _379 = clamp(_82, _95, _96); + vec3 _391 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _379, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _395 = textureLod(SPIRV_Cross_Combined_1, _379, 0.0); + float _396 = _395.y; + vec3 _411; + if (_396 > 0.0) + { + _411 = _391 + (textureLod(SPIRV_Cross_Combined_2, _379, 0.0).xyz * clamp(_396 * _395.z, 0.0, 1.0)); + } + else + { + _411 = _391; + } + vec2 _426 = clamp(_82 + (vec2(1.0, 0.0) * _7._m0.xy), _95, _96); + vec3 _438 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _426, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _442 = textureLod(SPIRV_Cross_Combined_1, _426, 0.0); + float _443 = _442.y; + vec3 _458; + if (_443 > 0.0) + { + _458 = _438 + (textureLod(SPIRV_Cross_Combined_2, _426, 0.0).xyz * clamp(_443 * _442.z, 0.0, 1.0)); + } + else + { + _458 = _438; + } + vec2 _473 = clamp(_82 + (vec2(2.0, 0.0) * _7._m0.xy), _95, _96); + vec3 _485 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _473, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _489 = textureLod(SPIRV_Cross_Combined_1, _473, 0.0); + float _490 = _489.y; + vec3 _505; + if (_490 > 0.0) + { + _505 = _485 + (textureLod(SPIRV_Cross_Combined_2, _473, 0.0).xyz * clamp(_490 * _489.z, 0.0, 1.0)); + } + else + { + _505 = _485; + } + vec2 _520 = clamp(_82 + (vec2(-1.0, 1.0) * _7._m0.xy), _95, _96); + vec3 _532 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _520, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _536 = textureLod(SPIRV_Cross_Combined_1, _520, 0.0); + float _537 = _536.y; + vec3 _552; + if (_537 > 0.0) + { + _552 = _532 + (textureLod(SPIRV_Cross_Combined_2, _520, 0.0).xyz * clamp(_537 * _536.z, 0.0, 1.0)); + } + else + { + _552 = _532; + } + vec2 _567 = clamp(_82 + (vec2(0.0, 1.0) * _7._m0.xy), _95, _96); + vec3 _579 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _567, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _583 = textureLod(SPIRV_Cross_Combined_1, _567, 0.0); + float _584 = _583.y; + vec3 _599; + if (_584 > 0.0) + { + _599 = _579 + (textureLod(SPIRV_Cross_Combined_2, _567, 0.0).xyz * clamp(_584 * _583.z, 0.0, 1.0)); + } + else + { + _599 = _579; + } + vec2 _614 = clamp(_82 + _7._m0.xy, _95, _96); + vec3 _626 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _614, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _630 = textureLod(SPIRV_Cross_Combined_1, _614, 0.0); + float _631 = _630.y; + vec3 _646; + if (_631 > 0.0) + { + _646 = _626 + (textureLod(SPIRV_Cross_Combined_2, _614, 0.0).xyz * clamp(_631 * _630.z, 0.0, 1.0)); + } + else + { + _646 = _626; + } + vec2 _661 = clamp(_82 + (vec2(0.0, 2.0) * _7._m0.xy), _95, _96); + vec3 _673 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _661, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _677 = textureLod(SPIRV_Cross_Combined_1, _661, 0.0); + float _678 = _677.y; + vec3 _693; + if (_678 > 0.0) + { + _693 = _673 + (textureLod(SPIRV_Cross_Combined_2, _661, 0.0).xyz * clamp(_678 * _677.z, 0.0, 1.0)); + } + else + { + _693 = _673; + } + vec3 _702 = (((((((((((((_129 * 0.5).xyz + (_176 * 0.5)).xyz + (_223 * 0.75)).xyz + (_270 * 0.5)).xyz + (_317 * 0.5)).xyz + (_364 * 0.75)).xyz + (_411 * 1.0)).xyz + (_458 * 0.75)).xyz + (_505 * 0.5)).xyz + (_552 * 0.5)).xyz + (_599 * 0.75)).xyz + (_646 * 0.5)).xyz + (_693 * 0.5)).xyz * vec3(0.125); + _28 _704 = _74; + _704._m0 = vec4(_702.x, _702.y, _702.z, vec4(0.0).w); + _28 _705 = _704; + _705._m0.w = 1.0; + _5 = _705._m0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/geom/block-name-namespace.asm.geom b/third_party/spirv-cross/reference/opt/shaders/asm/geom/block-name-namespace.asm.geom new file mode 100644 index 0000000..bb3e7d6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/geom/block-name-namespace.asm.geom @@ -0,0 +1,32 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 4, triangle_strip) out; + +layout(binding = 0, std140) uniform VertexInput +{ + vec4 a; +} VertexInput_1; + +layout(binding = 0, std430) buffer VertexInput +{ + vec4 b; +} VertexInput_2; + +layout(location = 0) out VertexInput +{ + vec4 vColor; +} VertexInput_3; + +layout(location = 0) in VertexInput +{ + vec4 vColor; +} vin[3]; + + +void main() +{ + gl_Position = (vec4(1.0) + VertexInput_1.a) + VertexInput_2.b; + VertexInput_3.vColor = vin[0].vColor; + EmitVertex(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/geom/inout-split-access-chain-handle.asm.geom b/third_party/spirv-cross/reference/opt/shaders/asm/geom/inout-split-access-chain-handle.asm.geom new file mode 100644 index 0000000..ca1381c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/geom/inout-split-access-chain-handle.asm.geom @@ -0,0 +1,9 @@ +#version 440 +layout(triangles) in; +layout(max_vertices = 5, triangle_strip) out; + +void main() +{ + gl_Position = gl_in[0].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/geom/split-access-chain-input.asm.geom b/third_party/spirv-cross/reference/opt/shaders/asm/geom/split-access-chain-input.asm.geom new file mode 100644 index 0000000..511d87f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/geom/split-access-chain-input.asm.geom @@ -0,0 +1,9 @@ +#version 440 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +void main() +{ + gl_Position = gl_in[0].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/geom/unroll-glposition-load.asm.geom b/third_party/spirv-cross/reference/opt/shaders/asm/geom/unroll-glposition-load.asm.geom new file mode 100644 index 0000000..0b1f5ca --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/geom/unroll-glposition-load.asm.geom @@ -0,0 +1,22 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +void main() +{ + vec4 _35_unrolled[3]; + for (int i = 0; i < int(3); i++) + { + _35_unrolled[i] = gl_in[i].gl_Position; + } + vec4 param[3] = _35_unrolled; + for (int _74 = 0; _74 < 3; ) + { + gl_Position = param[_74]; + EmitVertex(); + _74++; + continue; + } + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/tese/unroll-input-array-load.asm.tese b/third_party/spirv-cross/reference/opt/shaders/asm/tese/unroll-input-array-load.asm.tese new file mode 100644 index 0000000..731ed3f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/tese/unroll-input-array-load.asm.tese @@ -0,0 +1,17 @@ +#version 450 +layout(quads) in; + +struct ControlPoint +{ + vec4 baz; +}; + +layout(location = 0) patch in vec4 input_foo; +layout(location = 1) patch in vec4 input_bar; +layout(location = 2) in ControlPoint CPData[]; + +void main() +{ + gl_Position = (((input_foo + input_bar) + vec2(gl_TessCoord.xy).xyxy) + CPData[0u].baz) + CPData[3u].baz; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/empty-io.asm.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/empty-io.asm.vert new file mode 100644 index 0000000..3819a71 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/empty-io.asm.vert @@ -0,0 +1,14 @@ +#version 450 + +struct VSOutput +{ + int empty_struct_member; +}; + +layout(location = 0) in vec4 position; + +void main() +{ + gl_Position = position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..3ac9732 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,30 @@ +#version 450 +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +struct InstanceData +{ + mat4 MATRIX_MVP; + vec4 Color; +}; + +layout(binding = 0, std430) readonly buffer gInstanceData +{ + layout(row_major) InstanceData _data[]; +} gInstanceData_1; + +layout(location = 0) in vec3 PosL; +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +layout(location = 0) out vec4 _entryPointOutput_Color; + +void main() +{ + gl_Position = gInstanceData_1._data[uint((gl_InstanceID + SPIRV_Cross_BaseInstance))].MATRIX_MVP * vec4(PosL, 1.0); + _entryPointOutput_Color = gInstanceData_1._data[uint((gl_InstanceID + SPIRV_Cross_BaseInstance))].Color; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/global-builtin.sso.asm.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/global-builtin.sso.asm.vert new file mode 100644 index 0000000..20cb3b1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/global-builtin.sso.asm.vert @@ -0,0 +1,20 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +struct VSOut +{ + float a; +}; + +layout(location = 0) out VSOut _entryPointOutput; + +void main() +{ + _entryPointOutput.a = 40.0; + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant-block.asm.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant-block.asm.vert new file mode 100644 index 0000000..9b2f05a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant-block.asm.vert @@ -0,0 +1,9 @@ +#version 450 + +invariant gl_Position; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant-block.sso.asm.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant-block.sso.asm.vert new file mode 100644 index 0000000..eb88694 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant-block.sso.asm.vert @@ -0,0 +1,17 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[1]; + float gl_CullDistance[1]; +}; + +invariant gl_Position; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant.asm.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant.asm.vert new file mode 100644 index 0000000..9b2f05a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant.asm.vert @@ -0,0 +1,9 @@ +#version 450 + +invariant gl_Position; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant.sso.asm.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant.sso.asm.vert new file mode 100644 index 0000000..4f7e2f5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/invariant.sso.asm.vert @@ -0,0 +1,14 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +invariant gl_Position; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert new file mode 100644 index 0000000..2608c1d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert @@ -0,0 +1,30 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_201 +#define SPIRV_CROSS_CONSTANT_ID_201 -10 +#endif +const int _7 = SPIRV_CROSS_CONSTANT_ID_201; +const int _20 = (_7 + 2); +#ifndef SPIRV_CROSS_CONSTANT_ID_202 +#define SPIRV_CROSS_CONSTANT_ID_202 100u +#endif +const uint _8 = SPIRV_CROSS_CONSTANT_ID_202; +const uint _25 = (_8 % 5u); +const ivec4 _30 = ivec4(20, 30, _20, _20); +const ivec2 _32 = ivec2(_30.y, _30.x); +const int _33 = _30.y; + +layout(location = 0) flat out int _4; + +void main() +{ + vec4 _63 = vec4(0.0); + _63.y = float(_20); + vec4 _66 = _63; + _66.z = float(_25); + vec4 _52 = _66 + vec4(_30); + vec2 _56 = _52.xy + vec2(_32); + gl_Position = vec4(_56.x, _56.y, _52.z, _52.w); + _4 = _33; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert.vk b/third_party/spirv-cross/reference/opt/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert.vk new file mode 100644 index 0000000..10da8f4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert.vk @@ -0,0 +1,24 @@ +#version 450 + +layout(constant_id = 201) const int _7 = -10; +const int _20 = (_7 + 2); +layout(constant_id = 202) const uint _8 = 100u; +const uint _25 = (_8 % 5u); +const ivec4 _30 = ivec4(20, 30, _20, _20); +const ivec2 _32 = ivec2(_30.y, _30.x); +const int _33 = _30.y; + +layout(location = 0) flat out int _4; + +void main() +{ + vec4 _63 = vec4(0.0); + _63.y = float(_20); + vec4 _66 = _63; + _66.z = float(_25); + vec4 _52 = _66 + vec4(_30); + vec2 _56 = _52.xy + vec2(_32); + gl_Position = vec4(_56.x, _56.y, _52.z, _52.w); + _4 = _33; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..134e08d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,16 @@ +#version 450 +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif + +void main() +{ + gl_Position = vec4(float(uint(gl_VertexID) + uint((gl_InstanceID + SPIRV_Cross_BaseInstance)))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/atomic.comp b/third_party/spirv-cross/reference/opt/shaders/comp/atomic.comp new file mode 100644 index 0000000..89b1351 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/atomic.comp @@ -0,0 +1,49 @@ +#version 310 es +#extension GL_OES_shader_image_atomic : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 2, std430) buffer SSBO +{ + uint u32; + int i32; +} ssbo; + +layout(binding = 0, r32ui) uniform highp uimage2D uImage; +layout(binding = 1, r32i) uniform highp iimage2D iImage; + +void main() +{ + uint _19 = imageAtomicAdd(uImage, ivec2(1, 5), 1u); + uint _27 = imageAtomicAdd(uImage, ivec2(1, 5), 1u); + imageStore(iImage, ivec2(1, 6), ivec4(int(_27))); + uint _32 = imageAtomicOr(uImage, ivec2(1, 5), 1u); + uint _34 = imageAtomicXor(uImage, ivec2(1, 5), 1u); + uint _36 = imageAtomicAnd(uImage, ivec2(1, 5), 1u); + uint _38 = imageAtomicMin(uImage, ivec2(1, 5), 1u); + uint _40 = imageAtomicMax(uImage, ivec2(1, 5), 1u); + uint _44 = imageAtomicCompSwap(uImage, ivec2(1, 5), 10u, 2u); + int _47 = imageAtomicAdd(iImage, ivec2(1, 6), 1); + int _49 = imageAtomicOr(iImage, ivec2(1, 6), 1); + int _51 = imageAtomicXor(iImage, ivec2(1, 6), 1); + int _53 = imageAtomicAnd(iImage, ivec2(1, 6), 1); + int _55 = imageAtomicMin(iImage, ivec2(1, 6), 1); + int _57 = imageAtomicMax(iImage, ivec2(1, 6), 1); + int _61 = imageAtomicCompSwap(iImage, ivec2(1, 5), 10, 2); + uint _68 = atomicAdd(ssbo.u32, 1u); + uint _70 = atomicOr(ssbo.u32, 1u); + uint _72 = atomicXor(ssbo.u32, 1u); + uint _74 = atomicAnd(ssbo.u32, 1u); + uint _76 = atomicMin(ssbo.u32, 1u); + uint _78 = atomicMax(ssbo.u32, 1u); + uint _80 = atomicExchange(ssbo.u32, 1u); + uint _82 = atomicCompSwap(ssbo.u32, 10u, 2u); + int _85 = atomicAdd(ssbo.i32, 1); + int _87 = atomicOr(ssbo.i32, 1); + int _89 = atomicXor(ssbo.i32, 1); + int _91 = atomicAnd(ssbo.i32, 1); + int _93 = atomicMin(ssbo.i32, 1); + int _95 = atomicMax(ssbo.i32, 1); + int _97 = atomicExchange(ssbo.i32, 1); + int _99 = atomicCompSwap(ssbo.i32, 10, 2); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/bake_gradient.comp b/third_party/spirv-cross/reference/opt/shaders/comp/bake_gradient.comp new file mode 100644 index 0000000..69634d5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/bake_gradient.comp @@ -0,0 +1,26 @@ +#version 310 es +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(binding = 4, std140) uniform UBO +{ + vec4 uInvSize; + vec4 uScale; +} _46; + +layout(binding = 0) uniform mediump sampler2D uHeight; +layout(binding = 1) uniform mediump sampler2D uDisplacement; +layout(binding = 2, rgba16f) uniform writeonly mediump image2D iHeightDisplacement; +layout(binding = 3, rgba16f) uniform writeonly mediump image2D iGradJacobian; + +void main() +{ + vec4 _59 = (vec2(gl_GlobalInvocationID.xy) * _46.uInvSize.xy).xyxy + (_46.uInvSize * 0.5); + vec2 _67 = _59.xy; + vec2 _128 = _59.zw; + vec2 _157 = ((textureLodOffset(uDisplacement, _128, 0.0, ivec2(1, 0)).xy - textureLodOffset(uDisplacement, _128, 0.0, ivec2(-1, 0)).xy) * 0.60000002384185791015625) * _46.uScale.z; + vec2 _161 = ((textureLodOffset(uDisplacement, _128, 0.0, ivec2(0, 1)).xy - textureLodOffset(uDisplacement, _128, 0.0, ivec2(0, -1)).xy) * 0.60000002384185791015625) * _46.uScale.z; + ivec2 _172 = ivec2(gl_GlobalInvocationID.xy); + imageStore(iHeightDisplacement, _172, vec4(textureLod(uHeight, _67, 0.0).x, 0.0, 0.0, 0.0)); + imageStore(iGradJacobian, _172, vec4((_46.uScale.xy * 0.5) * vec2(textureLodOffset(uHeight, _67, 0.0, ivec2(1, 0)).x - textureLodOffset(uHeight, _67, 0.0, ivec2(-1, 0)).x, textureLodOffset(uHeight, _67, 0.0, ivec2(0, 1)).x - textureLodOffset(uHeight, _67, 0.0, ivec2(0, -1)).x), ((1.0 + _157.x) * (1.0 + _161.y)) - (_157.y * _161.x), 0.0)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/barriers.comp b/third_party/spirv-cross/reference/opt/shaders/comp/barriers.comp new file mode 100644 index 0000000..7dfde37 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/barriers.comp @@ -0,0 +1,22 @@ +#version 310 es +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + memoryBarrierShared(); + memoryBarrier(); + memoryBarrierImage(); + memoryBarrierBuffer(); + groupMemoryBarrier(); + barrier(); + memoryBarrier(); + barrier(); + memoryBarrierImage(); + barrier(); + memoryBarrierBuffer(); + barrier(); + groupMemoryBarrier(); + barrier(); + barrier(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/basic.comp b/third_party/spirv-cross/reference/opt/shaders/comp/basic.comp new file mode 100644 index 0000000..f025d53 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/basic.comp @@ -0,0 +1,28 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + vec4 in_data[]; +} _23; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _45; + +layout(binding = 2, std430) buffer SSBO3 +{ + uint counter; +} _48; + +void main() +{ + vec4 _29 = _23.in_data[gl_GlobalInvocationID.x]; + if (dot(_29, vec4(1.0, 5.0, 6.0, 2.0)) > 8.19999980926513671875) + { + uint _52 = atomicAdd(_48.counter, 1u); + _45.out_data[_52] = _29; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/casts.comp b/third_party/spirv-cross/reference/opt/shaders/comp/casts.comp new file mode 100644 index 0000000..11ef362 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/casts.comp @@ -0,0 +1,18 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) buffer SSBO1 +{ + ivec4 outputs[]; +} _21; + +layout(binding = 0, std430) buffer SSBO0 +{ + ivec4 inputs[]; +} _27; + +void main() +{ + _21.outputs[gl_GlobalInvocationID.x] = mix(ivec4(0), ivec4(1), notEqual((_27.inputs[gl_GlobalInvocationID.x] & ivec4(3)), ivec4(uvec4(0u)))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/cfg-preserve-parameter.comp b/third_party/spirv-cross/reference/opt/shaders/comp/cfg-preserve-parameter.comp new file mode 100644 index 0000000..124652b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/cfg-preserve-parameter.comp @@ -0,0 +1,7 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/cfg.comp b/third_party/spirv-cross/reference/opt/shaders/comp/cfg.comp new file mode 100644 index 0000000..97cdbc7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/cfg.comp @@ -0,0 +1,44 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float data; +} _11; + +float _188; + +void main() +{ + if (!(_11.data == 0.0)) + { + _11.data = 10.0; + } + else + { + _11.data = 15.0; + } + switch (int(_11.data)) + { + case 0: + { + _11.data = 20.0; + break; + } + case 1: + { + _11.data = 30.0; + break; + } + } + float _185; + _185 = _188; + for (int _184 = 0; _184 < 20; ) + { + _185 += 10.0; + _184++; + continue; + } + _11.data = _185; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/coherent-block.comp b/third_party/spirv-cross/reference/opt/shaders/comp/coherent-block.comp new file mode 100644 index 0000000..bfab6bb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/coherent-block.comp @@ -0,0 +1,13 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) coherent restrict writeonly buffer SSBO +{ + vec4 value; +} _10; + +void main() +{ + _10.value = vec4(20.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/coherent-image.comp b/third_party/spirv-cross/reference/opt/shaders/comp/coherent-image.comp new file mode 100644 index 0000000..b3992f2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/coherent-image.comp @@ -0,0 +1,15 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) coherent restrict writeonly buffer SSBO +{ + ivec4 value; +} _10; + +layout(binding = 3, r32i) uniform coherent restrict readonly mediump iimage2D uImage; + +void main() +{ + _10.value = imageLoad(uImage, ivec2(10)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/composite-array-initialization.comp b/third_party/spirv-cross/reference/opt/shaders/comp/composite-array-initialization.comp new file mode 100644 index 0000000..2e6dbeb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/composite-array-initialization.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 2, local_size_y = 1, local_size_z = 1) in; + +struct Data +{ + float a; + float b; +}; + +const Data _25[2] = Data[](Data(1.0, 2.0), Data(3.0, 4.0)); +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 4.0 +#endif +const float X = SPIRV_CROSS_CONSTANT_ID_0; + +layout(binding = 0, std430) buffer SSBO +{ + Data outdata[]; +} _53; + +void main() +{ + Data data2[2] = Data[](Data(X, 2.0), Data(3.0, 5.0)); + _53.outdata[gl_WorkGroupID.x].a = _25[gl_LocalInvocationID.x].a + data2[gl_LocalInvocationID.x].a; + _53.outdata[gl_WorkGroupID.x].b = _25[gl_LocalInvocationID.x].b + data2[gl_LocalInvocationID.x].b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/composite-construct.comp b/third_party/spirv-cross/reference/opt/shaders/comp/composite-construct.comp new file mode 100644 index 0000000..0e85797 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/composite-construct.comp @@ -0,0 +1,18 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + vec4 as[]; +} _41; + +layout(binding = 1, std430) buffer SSBO1 +{ + vec4 bs[]; +} _55; + +void main() +{ + _41.as[gl_GlobalInvocationID.x] = ((_41.as[gl_GlobalInvocationID.x] + _55.bs[gl_GlobalInvocationID.x]) + _55.bs[gl_GlobalInvocationID.x]) + vec4(10.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/culling.comp b/third_party/spirv-cross/reference/opt/shaders/comp/culling.comp new file mode 100644 index 0000000..f4dea4b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/culling.comp @@ -0,0 +1,28 @@ +#version 310 es +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + float in_data[]; +} _22; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + float out_data[]; +} _38; + +layout(binding = 2, std430) buffer SSBO3 +{ + uint count; +} _41; + +void main() +{ + float _28 = _22.in_data[gl_GlobalInvocationID.x]; + if (_28 > 12.0) + { + uint _45 = atomicAdd(_41.count, 1u); + _38.out_data[_45] = _28; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/defer-parens.comp b/third_party/spirv-cross/reference/opt/shaders/comp/defer-parens.comp new file mode 100644 index 0000000..c48fb9e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/defer-parens.comp @@ -0,0 +1,20 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + vec4 data; + int index; +} _13; + +void main() +{ + vec4 _17 = _13.data; + vec2 _28 = _17.yz + vec2(10.0); + _13.data = vec4(_17.x, _28, _17.w); + _13.data = (_17 + _17) + _17; + _13.data = _28.xxyy; + _13.data = vec4(_28.y); + _13.data = vec4((_17.zw + vec2(10.0))[_13.index]); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/dowhile.comp b/third_party/spirv-cross/reference/opt/shaders/comp/dowhile.comp new file mode 100644 index 0000000..d9a9f77 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/dowhile.comp @@ -0,0 +1,38 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +} _28; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _52; + +void main() +{ + vec4 _59; + int _60; + _60 = 0; + _59 = _28.in_data[gl_GlobalInvocationID.x]; + vec4 _42; + for (;;) + { + _42 = _28.mvp * _59; + int _44 = _60 + 1; + if (_44 < 16) + { + _60 = _44; + _59 = _42; + } + else + { + break; + } + } + _52.out_data[gl_GlobalInvocationID.x] = _42; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/generate_height.comp b/third_party/spirv-cross/reference/opt/shaders/comp/generate_height.comp new file mode 100644 index 0000000..bfd7830 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/generate_height.comp @@ -0,0 +1,56 @@ +#version 310 es +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer Distribution +{ + vec2 distribution[]; +} _137; + +layout(binding = 2, std140) uniform UBO +{ + vec4 uModTime; +} _166; + +layout(binding = 1, std430) writeonly buffer HeightmapFFT +{ + uint heights[]; +} _225; + +void main() +{ + uvec2 _265 = uvec2(64u, 1u) * gl_NumWorkGroups.xy; + uvec2 _270 = _265 - gl_GlobalInvocationID.xy; + bvec2 _272 = equal(gl_GlobalInvocationID.xy, uvec2(0u)); + uint _460; + if (_272.x) + { + _460 = 0u; + } + else + { + _460 = _270.x; + } + uint _461; + if (_272.y) + { + _461 = 0u; + } + else + { + _461 = _270.y; + } + uint _277 = _265.x; + uint _281 = (gl_GlobalInvocationID.y * _277) + gl_GlobalInvocationID.x; + uint _291 = (_461 * _277) + _460; + vec2 _298 = vec2(gl_GlobalInvocationID.xy); + vec2 _300 = vec2(_265); + float _310 = sqrt(9.81000041961669921875 * length(_166.uModTime.xy * mix(_298, _298 - _300, greaterThan(_298, _300 * 0.5)))) * _166.uModTime.z; + vec2 _317 = vec2(cos(_310), sin(_310)); + vec2 _391 = _317.xx; + vec2 _396 = _317.yy; + vec2 _399 = _396 * _137.distribution[_281].yx; + vec2 _426 = _396 * _137.distribution[_291].yx; + vec2 _434 = (_137.distribution[_291] * _391) + vec2(-_426.x, _426.y); + _225.heights[_281] = packHalf2x16(((_137.distribution[_281] * _391) + vec2(-_399.x, _399.y)) + vec2(_434.x, -_434.y)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/image.comp b/third_party/spirv-cross/reference/opt/shaders/comp/image.comp new file mode 100644 index 0000000..cdb5714 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/image.comp @@ -0,0 +1,12 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, rgba8) uniform readonly mediump image2D uImageIn; +layout(binding = 1, rgba8) uniform writeonly mediump image2D uImageOut; + +void main() +{ + ivec2 _23 = ivec2(gl_GlobalInvocationID.xy); + imageStore(uImageOut, _23, imageLoad(uImageIn, _23 + imageSize(uImageIn))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/insert.comp b/third_party/spirv-cross/reference/opt/shaders/comp/insert.comp new file mode 100644 index 0000000..ad394aa --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/insert.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) writeonly buffer SSBO +{ + vec4 out_data[]; +} _27; + +vec4 _53; + +void main() +{ + vec4 _46 = _53; + _46.x = 10.0; + vec4 _48 = _46; + _48.y = 30.0; + vec4 _50 = _48; + _50.z = 70.0; + vec4 _52 = _50; + _52.w = 90.0; + _27.out_data[gl_GlobalInvocationID.x] = _52; + _27.out_data[gl_GlobalInvocationID.x].y = 20.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/mat3.comp b/third_party/spirv-cross/reference/opt/shaders/comp/mat3.comp new file mode 100644 index 0000000..b1c585b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/mat3.comp @@ -0,0 +1,13 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + mat3 out_data[]; +} _22; + +void main() +{ + _22.out_data[gl_GlobalInvocationID.x] = mat3(vec3(10.0), vec3(20.0), vec3(40.0)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/mod.comp b/third_party/spirv-cross/reference/opt/shaders/comp/mod.comp new file mode 100644 index 0000000..d8ee0ff --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/mod.comp @@ -0,0 +1,20 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + vec4 in_data[]; +} _23; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _33; + +void main() +{ + _33.out_data[gl_GlobalInvocationID.x] = mod(_23.in_data[gl_GlobalInvocationID.x], _33.out_data[gl_GlobalInvocationID.x]); + _33.out_data[gl_GlobalInvocationID.x] = uintBitsToFloat(floatBitsToUint(_23.in_data[gl_GlobalInvocationID.x]) % floatBitsToUint(_33.out_data[gl_GlobalInvocationID.x])); + _33.out_data[gl_GlobalInvocationID.x] = intBitsToFloat(floatBitsToInt(_23.in_data[gl_GlobalInvocationID.x]) % floatBitsToInt(_33.out_data[gl_GlobalInvocationID.x])); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/modf.comp b/third_party/spirv-cross/reference/opt/shaders/comp/modf.comp new file mode 100644 index 0000000..3c8ab6e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/modf.comp @@ -0,0 +1,20 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + vec4 in_data[]; +} _23; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _35; + +void main() +{ + vec4 i; + vec4 _31 = modf(_23.in_data[gl_GlobalInvocationID.x], i); + _35.out_data[gl_GlobalInvocationID.x] = _31; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/outer-product.comp b/third_party/spirv-cross/reference/opt/shaders/comp/outer-product.comp new file mode 100644 index 0000000..d31dad3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/outer-product.comp @@ -0,0 +1,36 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) writeonly buffer SSBO +{ + mat2 m22; + mat2x3 m23; + mat2x4 m24; + mat3x2 m32; + mat3 m33; + mat3x4 m34; + mat4x2 m42; + mat4x3 m43; + mat4 m44; +} _21; + +layout(binding = 1, std430) readonly buffer ReadSSBO +{ + vec2 v2; + vec3 v3; + vec4 v4; +} _26; + +void main() +{ + _21.m22 = outerProduct(_26.v2, _26.v2); + _21.m23 = outerProduct(_26.v3, _26.v2); + _21.m24 = outerProduct(_26.v4, _26.v2); + _21.m32 = outerProduct(_26.v2, _26.v3); + _21.m33 = outerProduct(_26.v3, _26.v3); + _21.m34 = outerProduct(_26.v4, _26.v3); + _21.m42 = outerProduct(_26.v2, _26.v4); + _21.m43 = outerProduct(_26.v3, _26.v4); + _21.m44 = outerProduct(_26.v4, _26.v4); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/read-write-only.comp b/third_party/spirv-cross/reference/opt/shaders/comp/read-write-only.comp new file mode 100644 index 0000000..06227ee --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/read-write-only.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 2, std430) restrict writeonly buffer SSBO2 +{ + vec4 data4; + vec4 data5; +} _10; + +layout(binding = 0, std430) readonly buffer SSBO0 +{ + vec4 data0; + vec4 data1; +} _15; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + vec4 data2; + vec4 data3; +} _21; + +void main() +{ + _10.data4 = _15.data0 + _21.data2; + _10.data5 = _15.data1 + _21.data3; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/rmw-matrix.comp b/third_party/spirv-cross/reference/opt/shaders/comp/rmw-matrix.comp new file mode 100644 index 0000000..5c4ac94 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/rmw-matrix.comp @@ -0,0 +1,20 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float a; + vec4 b; + mat4 c; + float a1; + vec4 b1; + mat4 c1; +} _11; + +void main() +{ + _11.a *= _11.a1; + _11.b *= _11.b1; + _11.c = _11.c * _11.c1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/rmw-opt.comp b/third_party/spirv-cross/reference/opt/shaders/comp/rmw-opt.comp new file mode 100644 index 0000000..342e663 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/rmw-opt.comp @@ -0,0 +1,23 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + int a; +} _9; + +void main() +{ + _9.a += 10; + _9.a -= 10; + _9.a *= 10; + _9.a /= 10; + _9.a = _9.a << 2; + _9.a = _9.a >> 3; + _9.a &= 40; + _9.a ^= 10; + _9.a %= 40; + _9.a |= 1; + _9.a = 0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/reference/opt/shaders/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..c28face --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,19 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float a; + float b; + float c; + float d; + float e; +} _9; + +void main() +{ + _9.c = distance(_9.a, _9.b); + _9.d = length(_9.a); + _9.e = normalize(_9.a); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/shared.comp b/third_party/spirv-cross/reference/opt/shaders/comp/shared.comp new file mode 100644 index 0000000..62cf4a4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/shared.comp @@ -0,0 +1,22 @@ +#version 310 es +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + float in_data[]; +} _22; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + float out_data[]; +} _44; + +shared float sShared[4]; + +void main() +{ + sShared[gl_LocalInvocationIndex] = _22.in_data[gl_GlobalInvocationID.x]; + barrier(); + _44.out_data[gl_GlobalInvocationID.x] = sShared[3u - gl_LocalInvocationIndex]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/ssbo-array-length.comp b/third_party/spirv-cross/reference/opt/shaders/comp/ssbo-array-length.comp new file mode 100644 index 0000000..ddc666e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/ssbo-array-length.comp @@ -0,0 +1,14 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std140) buffer SSBO +{ + uint size; + float v[]; +} _11; + +void main() +{ + _11.size = uint(int(uint(_11.v.length()))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/ssbo-array.comp b/third_party/spirv-cross/reference/opt/shaders/comp/ssbo-array.comp new file mode 100644 index 0000000..6caf8f4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/ssbo-array.comp @@ -0,0 +1,13 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + vec4 data[]; +} ssbos[2]; + +void main() +{ + ssbos[1].data[gl_GlobalInvocationID.x] = ssbos[0].data[gl_GlobalInvocationID.x]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/struct-layout.comp b/third_party/spirv-cross/reference/opt/shaders/comp/struct-layout.comp new file mode 100644 index 0000000..0f73fa7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/struct-layout.comp @@ -0,0 +1,23 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct Foo +{ + mat4 m; +}; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + Foo out_data[]; +} _23; + +layout(binding = 0, std430) readonly buffer SSBO +{ + Foo in_data[]; +} _30; + +void main() +{ + _23.out_data[gl_GlobalInvocationID.x].m = _30.in_data[gl_GlobalInvocationID.x].m * _30.in_data[gl_GlobalInvocationID.x].m; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/struct-packing.comp b/third_party/spirv-cross/reference/opt/shaders/comp/struct-packing.comp new file mode 100644 index 0000000..f4b5834 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/struct-packing.comp @@ -0,0 +1,104 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + S4 m3s[8]; +}; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + mat2 m0; + mat2 m1; + mat2x3 m2[4]; + mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + mat2 m0; + mat2 m1; + mat2x3 m2[4]; + mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content.m0s[0].a[0] = ssbo_140.content.m0s[0].a[0]; + ssbo_430.content.m0s[0].b = ssbo_140.content.m0s[0].b; + ssbo_430.content.m1s[0].a = ssbo_140.content.m1s[0].a; + ssbo_430.content.m1s[0].b = ssbo_140.content.m1s[0].b; + ssbo_430.content.m2s[0].a[0] = ssbo_140.content.m2s[0].a[0]; + ssbo_430.content.m2s[0].b = ssbo_140.content.m2s[0].b; + ssbo_430.content.m0.a[0] = ssbo_140.content.m0.a[0]; + ssbo_430.content.m0.b = ssbo_140.content.m0.b; + ssbo_430.content.m1.a = ssbo_140.content.m1.a; + ssbo_430.content.m1.b = ssbo_140.content.m1.b; + ssbo_430.content.m2.a[0] = ssbo_140.content.m2.a[0]; + ssbo_430.content.m2.b = ssbo_140.content.m2.b; + ssbo_430.content.m3.a = ssbo_140.content.m3.a; + ssbo_430.content.m3.b = ssbo_140.content.m3.b; + ssbo_430.content.m4 = ssbo_140.content.m4; + ssbo_430.content.m3s[0].c = ssbo_140.content.m3s[0].c; + ssbo_430.content.m3s[1].c = ssbo_140.content.m3s[1].c; + ssbo_430.content.m3s[2].c = ssbo_140.content.m3s[2].c; + ssbo_430.content.m3s[3].c = ssbo_140.content.m3s[3].c; + ssbo_430.content.m3s[4].c = ssbo_140.content.m3s[4].c; + ssbo_430.content.m3s[5].c = ssbo_140.content.m3s[5].c; + ssbo_430.content.m3s[6].c = ssbo_140.content.m3s[6].c; + ssbo_430.content.m3s[7].c = ssbo_140.content.m3s[7].c; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/torture-loop.comp b/third_party/spirv-cross/reference/opt/shaders/comp/torture-loop.comp new file mode 100644 index 0000000..9ca2b95 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/torture-loop.comp @@ -0,0 +1,40 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +} _24; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _89; + +void main() +{ + vec4 _101; + _101 = _24.in_data[gl_GlobalInvocationID.x]; + for (int _95 = 0; (_95 + 1) < 10; ) + { + _101 *= 2.0; + _95 += 2; + continue; + } + vec4 _100; + _100 = _101; + vec4 _105; + for (uint _96 = 0u; _96 < 16u; _100 = _105, _96++) + { + _105 = _100; + for (uint _102 = 0u; _102 < 30u; ) + { + _105 = _24.mvp * _105; + _102++; + continue; + } + } + _89.out_data[gl_GlobalInvocationID.x] = _100; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/type-alias.comp b/third_party/spirv-cross/reference/opt/shaders/comp/type-alias.comp new file mode 100644 index 0000000..c0f57f4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/type-alias.comp @@ -0,0 +1,33 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct S0 +{ + vec4 a; +}; + +struct S1 +{ + vec4 a; +}; + +layout(binding = 0, std430) buffer SSBO0 +{ + S0 s0s[]; +} _36; + +layout(binding = 1, std430) buffer SSBO1 +{ + S1 s1s[]; +} _55; + +layout(binding = 2, std430) buffer SSBO2 +{ + vec4 outputs[]; +} _66; + +void main() +{ + _66.outputs[gl_GlobalInvocationID.x] = _36.s0s[gl_GlobalInvocationID.x].a + _55.s1s[gl_GlobalInvocationID.x].a; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/comp/udiv.comp b/third_party/spirv-cross/reference/opt/shaders/comp/udiv.comp new file mode 100644 index 0000000..0c1f926 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/comp/udiv.comp @@ -0,0 +1,18 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO2 +{ + uint outputs[]; +} _10; + +layout(binding = 0, std430) buffer SSBO +{ + uint inputs[]; +} _23; + +void main() +{ + _10.outputs[gl_GlobalInvocationID.x] = _23.inputs[gl_GlobalInvocationID.x] / 29u; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/enhanced-layouts.comp b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/enhanced-layouts.comp new file mode 100644 index 0000000..ba37ca2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/enhanced-layouts.comp @@ -0,0 +1,40 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct Foo +{ + int a; + int b; + int c; +}; + +layout(binding = 1, std140) buffer SSBO1 +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ssbo1; + +layout(binding = 2, std430) buffer SSBO2 +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ssbo2; + +layout(binding = 0, std140) uniform UBO +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ubo; + +void main() +{ + ssbo1.a = ssbo2.a; + ssbo1.b = ubo.b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/extended-arithmetic.desktop.comp b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/extended-arithmetic.desktop.comp new file mode 100644 index 0000000..9c55c74 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/extended-arithmetic.desktop.comp @@ -0,0 +1,159 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct ResType +{ + uint _m0; + uint _m1; +}; + +struct ResType_1 +{ + uvec2 _m0; + uvec2 _m1; +}; + +struct ResType_2 +{ + uvec3 _m0; + uvec3 _m1; +}; + +struct ResType_3 +{ + uvec4 _m0; + uvec4 _m1; +}; + +struct ResType_4 +{ + int _m0; + int _m1; +}; + +struct ResType_5 +{ + ivec2 _m0; + ivec2 _m1; +}; + +struct ResType_6 +{ + ivec3 _m0; + ivec3 _m1; +}; + +struct ResType_7 +{ + ivec4 _m0; + ivec4 _m1; +}; + +layout(binding = 0, std430) buffer SSBOUint +{ + uint a; + uint b; + uint c; + uint d; + uvec2 a2; + uvec2 b2; + uvec2 c2; + uvec2 d2; + uvec3 a3; + uvec3 b3; + uvec3 c3; + uvec3 d3; + uvec4 a4; + uvec4 b4; + uvec4 c4; + uvec4 d4; +} u; + +layout(binding = 1, std430) buffer SSBOInt +{ + int a; + int b; + int c; + int d; + ivec2 a2; + ivec2 b2; + ivec2 c2; + ivec2 d2; + ivec3 a3; + ivec3 b3; + ivec3 c3; + ivec3 d3; + ivec4 a4; + ivec4 b4; + ivec4 c4; + ivec4 d4; +} i; + +void main() +{ + ResType _25; + _25._m0 = uaddCarry(u.a, u.b, _25._m1); + u.d = _25._m1; + u.c = _25._m0; + ResType_1 _40; + _40._m0 = uaddCarry(u.a2, u.b2, _40._m1); + u.d2 = _40._m1; + u.c2 = _40._m0; + ResType_2 _55; + _55._m0 = uaddCarry(u.a3, u.b3, _55._m1); + u.d3 = _55._m1; + u.c3 = _55._m0; + ResType_3 _70; + _70._m0 = uaddCarry(u.a4, u.b4, _70._m1); + u.d4 = _70._m1; + u.c4 = _70._m0; + ResType _79; + _79._m0 = usubBorrow(u.a, u.b, _79._m1); + u.d = _79._m1; + u.c = _79._m0; + ResType_1 _88; + _88._m0 = usubBorrow(u.a2, u.b2, _88._m1); + u.d2 = _88._m1; + u.c2 = _88._m0; + ResType_2 _97; + _97._m0 = usubBorrow(u.a3, u.b3, _97._m1); + u.d3 = _97._m1; + u.c3 = _97._m0; + ResType_3 _106; + _106._m0 = usubBorrow(u.a4, u.b4, _106._m1); + u.d4 = _106._m1; + u.c4 = _106._m0; + ResType _116; + umulExtended(u.a, u.b, _116._m1, _116._m0); + u.d = _116._m0; + u.c = _116._m1; + ResType_1 _125; + umulExtended(u.a2, u.b2, _125._m1, _125._m0); + u.d2 = _125._m0; + u.c2 = _125._m1; + ResType_2 _134; + umulExtended(u.a3, u.b3, _134._m1, _134._m0); + u.d3 = _134._m0; + u.c3 = _134._m1; + ResType_3 _143; + umulExtended(u.a4, u.b4, _143._m1, _143._m0); + u.d4 = _143._m0; + u.c4 = _143._m1; + ResType_4 _160; + imulExtended(i.a, i.b, _160._m1, _160._m0); + i.d = _160._m0; + i.c = _160._m1; + ResType_5 _171; + imulExtended(i.a2, i.b2, _171._m1, _171._m0); + i.d2 = _171._m0; + i.c2 = _171._m1; + ResType_6 _182; + imulExtended(i.a3, i.b3, _182._m1, _182._m0); + i.d3 = _182._m0; + i.c3 = _182._m1; + ResType_7 _193; + imulExtended(i.a4, i.b4, _193._m1, _193._m0); + i.d4 = _193._m0; + i.c4 = _193._m1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/fp64.desktop.comp b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/fp64.desktop.comp new file mode 100644 index 0000000..3839a09 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/fp64.desktop.comp @@ -0,0 +1,63 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct M0 +{ + double v; + dvec2 b[2]; + dmat2x3 c; + dmat3x2 d; +}; + +layout(binding = 0, std430) buffer SSBO0 +{ + dvec4 a; + M0 m0; + dmat4 b; +} ssbo_0; + +layout(binding = 1, std430) buffer SSBO1 +{ + dmat4 a; + dvec4 b; + M0 m0; +} ssbo_1; + +layout(binding = 2, std430) buffer SSBO2 +{ + double a[4]; + dvec2 b[4]; +} ssbo_2; + +layout(binding = 3, std140) buffer SSBO3 +{ + double a[4]; + dvec2 b[4]; +} ssbo_3; + +void main() +{ + ssbo_0.a += dvec4(10.0lf, 20.0lf, 30.0lf, 40.0lf); + ssbo_0.a += dvec4(20.0lf); + dvec4 _40 = ssbo_0.a; + ssbo_0.a = abs(_40); + ssbo_0.a = sign(_40); + ssbo_0.a = floor(_40); + ssbo_0.a = trunc(_40); + ssbo_0.a = round(_40); + ssbo_0.a = roundEven(_40); + ssbo_0.a = ceil(_40); + ssbo_0.a = fract(_40); + ssbo_0.a = mod(_40, dvec4(20.0lf)); + ssbo_0.a = mod(_40, _40); + ssbo_0.a = min(_40, _40); + ssbo_0.a = max(_40, _40); + ssbo_0.a = clamp(_40, _40, _40); + ssbo_0.a = mix(_40, _40, _40); + ssbo_0.a = step(_40, _40); + ssbo_0.a = smoothstep(_40, _40, _40); + ssbo_1.b.x += 1.0lf; + ssbo_2.b[0].x += 1.0lf; + ssbo_3.b[0].x += 1.0lf; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp new file mode 100644 index 0000000..37b2863 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp @@ -0,0 +1,7 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/int64.desktop.comp b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/int64.desktop.comp new file mode 100644 index 0000000..702456b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/comp/int64.desktop.comp @@ -0,0 +1,52 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct M0 +{ + int64_t v; + i64vec2 b[2]; + uint64_t c; + uint64_t d[5]; +}; + +layout(binding = 0, std430) buffer SSBO0 +{ + i64vec4 a; + M0 m0; +} ssbo_0; + +layout(binding = 1, std430) buffer SSBO1 +{ + u64vec4 b; + M0 m0; +} ssbo_1; + +layout(binding = 2, std430) buffer SSBO2 +{ + int64_t a[4]; + i64vec2 b[4]; +} ssbo_2; + +layout(binding = 3, std140) buffer SSBO3 +{ + int64_t a[4]; + i64vec2 b[4]; +} ssbo_3; + +void main() +{ + ssbo_0.a += i64vec4(10l, 20l, 30l, 40l); + ssbo_1.b += u64vec4(999999999999999999ul, 8888888888888888ul, 77777777777777777ul, 6666666666666666ul); + ssbo_0.a += i64vec4(20l); + ssbo_0.a = abs(ssbo_0.a + i64vec4(ssbo_1.b)); + ssbo_0.a += i64vec4(1l); + ssbo_1.b += u64vec4(i64vec4(1l)); + ssbo_0.a -= i64vec4(1l); + ssbo_1.b -= u64vec4(i64vec4(1l)); + ssbo_1.b = doubleBitsToUint64(int64BitsToDouble(ssbo_0.a)); + ssbo_0.a = doubleBitsToInt64(uint64BitsToDouble(ssbo_1.b)); + ssbo_2.a[0] += 1l; + ssbo_3.a[0] += 2l; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/clip-cull-distance.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/clip-cull-distance.desktop.frag new file mode 100644 index 0000000..3cc3205 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/clip-cull-distance.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +in float gl_ClipDistance[4]; +in float gl_CullDistance[3]; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = gl_ClipDistance[0] + gl_CullDistance[0]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..29c5901 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,37 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vInput; + +void main() +{ + FragColor = vInput; + vec4 _23 = texture(uSampler, vInput.xy); + vec4 _26 = dFdx(vInput); + vec4 _29 = dFdy(vInput); + vec4 _32 = fwidth(vInput); + vec4 _35 = dFdxCoarse(vInput); + vec4 _38 = dFdyCoarse(vInput); + vec4 _41 = fwidthCoarse(vInput); + vec4 _44 = dFdxFine(vInput); + vec4 _47 = dFdyFine(vInput); + vec4 _50 = fwidthFine(vInput); + vec2 _56 = textureQueryLod(uSampler, vInput.zw); + if (vInput.y > 10.0) + { + FragColor += _23; + FragColor += _26; + FragColor += _29; + FragColor += _32; + FragColor += _35; + FragColor += _38; + FragColor += _41; + FragColor += _44; + FragColor += _47; + FragColor += _50; + FragColor += _56.xyxy; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/depth-greater-than.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/depth-greater-than.desktop.frag new file mode 100644 index 0000000..8b7c296 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/depth-greater-than.desktop.frag @@ -0,0 +1,9 @@ +#version 450 +layout(depth_greater) out float gl_FragDepth; +layout(early_fragment_tests) in; + +void main() +{ + gl_FragDepth = 0.5; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/depth-less-than.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/depth-less-than.desktop.frag new file mode 100644 index 0000000..44752eb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/depth-less-than.desktop.frag @@ -0,0 +1,9 @@ +#version 450 +layout(depth_less) out float gl_FragDepth; +layout(early_fragment_tests) in; + +void main() +{ + gl_FragDepth = 0.5; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/dual-source-blending.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/dual-source-blending.desktop.frag new file mode 100644 index 0000000..3d946b0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/dual-source-blending.desktop.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0, index = 0) out vec4 FragColor0; +layout(location = 0, index = 1) out vec4 FragColor1; + +void main() +{ + FragColor0 = vec4(1.0); + FragColor1 = vec4(2.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag new file mode 100644 index 0000000..2d0809f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag @@ -0,0 +1,19 @@ +#version 450 + +layout(binding = 0, std430) buffer Foobar +{ + vec4 _data[]; +} Foobar_1; + +layout(binding = 1, std430) buffer Foobaz +{ + vec4 _data[]; +} Foobaz_1; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + _entryPointOutput = Foobar_1._data[0] + Foobaz_1._data[0]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-ms.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-ms.desktop.frag new file mode 100644 index 0000000..1276981 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-ms.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0, rgba8) uniform image2DMS uImage; +layout(binding = 1, rgba8) uniform image2DMSArray uImageArray; + +void main() +{ + vec4 _29 = imageLoad(uImageArray, ivec3(1, 2, 4), 3); + imageStore(uImage, ivec2(2, 3), 1, imageLoad(uImage, ivec2(1, 2), 2)); + imageStore(uImageArray, ivec3(2, 3, 7), 1, _29); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-query.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-query.desktop.frag new file mode 100644 index 0000000..05ce10a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-query.desktop.frag @@ -0,0 +1,6 @@ +#version 450 + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-size.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-size.frag new file mode 100644 index 0000000..5bb0603 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-size.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0, r32f) uniform readonly writeonly image2D uImage1; +layout(binding = 1, r32f) uniform readonly writeonly image2D uImage2; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(vec2(imageSize(uImage1)), vec2(imageSize(uImage2))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag new file mode 100644 index 0000000..1d90620 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0, r32f) uniform image2D uImage1; +layout(binding = 1, r32f) uniform image2D uImage2; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(vec2(imageSize(uImage1)), vec2(imageSize(uImage2))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/in-block-qualifiers.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/in-block-qualifiers.frag new file mode 100644 index 0000000..d462280 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/in-block-qualifiers.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in VertexData +{ + flat float f; + centroid vec4 g; + flat int h; + float i; +} vin; + +layout(location = 4) flat in float f; +layout(location = 5) centroid in vec4 g; +layout(location = 6) flat in int h; +layout(location = 7) sample in float i; + +void main() +{ + FragColor = ((((((vec4(vin.f) + vin.g) + vec4(float(vin.h))) + vec4(vin.i)) + vec4(f)) + g) + vec4(float(h))) + vec4(i); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/layout-component.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/layout-component.desktop.frag new file mode 100644 index 0000000..13f17fe --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/layout-component.desktop.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) out vec2 FragColor; +layout(location = 0, component = 0) in vec2 v0; +layout(location = 0, component = 2) in float v1; +in Vertex +{ + layout(location = 1, component = 2) float v3; +} _20; + + +void main() +{ + FragColor = (v0 + vec2(v1)) + vec2(_20.v3); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/query-levels.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/query-levels.desktop.frag new file mode 100644 index 0000000..4a80cbf --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/query-levels.desktop.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(textureQueryLevels(uSampler))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/query-lod.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/query-lod.desktop.frag new file mode 100644 index 0000000..f43543b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/query-lod.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTexCoord; + +void main() +{ + FragColor = textureQueryLod(uSampler, vTexCoord).xyxy; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/sampler-ms-query.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/sampler-ms-query.desktop.frag new file mode 100644 index 0000000..4c30ed1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/sampler-ms-query.desktop.frag @@ -0,0 +1,14 @@ +#version 450 + +layout(binding = 0) uniform sampler2DMS uSampler; +layout(binding = 1) uniform sampler2DMSArray uSamplerArray; +layout(binding = 2, rgba8) uniform readonly writeonly image2DMS uImage; +layout(binding = 3, rgba8) uniform readonly writeonly image2DMSArray uImageArray; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(((textureSamples(uSampler) + textureSamples(uSamplerArray)) + imageSamples(uImage)) + imageSamples(uImageArray))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/stencil-export.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/stencil-export.desktop.frag new file mode 100644 index 0000000..65082a8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/stencil-export.desktop.frag @@ -0,0 +1,13 @@ +#version 450 +#extension GL_ARB_shader_stencil_export : require + +layout(location = 0) out vec4 MRT0; +layout(location = 1) out vec4 MRT1; + +void main() +{ + MRT0 = vec4(1.0); + MRT1 = vec4(1.0, 0.0, 1.0, 1.0); + gl_FragStencilRefARB = 100; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag new file mode 100644 index 0000000..d5e45bd --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag @@ -0,0 +1,26 @@ +#version 450 + +layout(binding = 0) uniform sampler1DShadow uShadow1D; +layout(binding = 1) uniform sampler2DShadow uShadow2D; +layout(binding = 2) uniform sampler1D uSampler1D; +layout(binding = 3) uniform sampler2D uSampler2D; +layout(binding = 4) uniform sampler3D uSampler3D; + +layout(location = 0) out float FragColor; +layout(location = 1) in vec4 vClip4; +layout(location = 2) in vec2 vClip2; +layout(location = 0) in vec3 vClip3; + +void main() +{ + vec4 _20 = vClip4; + _20.y = vClip4.w; + FragColor = textureProj(uShadow1D, vec4(_20.x, 0.0, vClip4.z, _20.y)); + vec4 _30 = vClip4; + _30.z = vClip4.w; + FragColor = textureProj(uShadow2D, vec4(_30.xy, vClip4.z, _30.z)); + FragColor = textureProj(uSampler1D, vClip2).x; + FragColor = textureProj(uSampler2D, vClip3).x; + FragColor = textureProj(uSampler3D, vClip4).x; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/geom/basic.desktop.sso.geom b/third_party/spirv-cross/reference/opt/shaders/desktop-only/geom/basic.desktop.sso.geom new file mode 100644 index 0000000..8e51cfa --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/geom/basic.desktop.sso.geom @@ -0,0 +1,36 @@ +#version 450 +layout(invocations = 4, triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[3]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + float _37 = float(gl_InvocationID); + vNormal = vin[0].normal + vec3(_37); + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal + vec3(4.0 * _37); + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal + vec3(2.0 * _37); + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/geom/viewport-index.desktop.geom b/third_party/spirv-cross/reference/opt/shaders/desktop-only/geom/viewport-index.desktop.geom new file mode 100644 index 0000000..773aeb8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/geom/viewport-index.desktop.geom @@ -0,0 +1,9 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 4, triangle_strip) out; + +void main() +{ + gl_ViewportIndex = 1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/tesc/basic.desktop.sso.tesc b/third_party/spirv-cross/reference/opt/shaders/desktop-only/tesc/basic.desktop.sso.tesc new file mode 100644 index 0000000..c51699d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/tesc/basic.desktop.sso.tesc @@ -0,0 +1,27 @@ +#version 450 +layout(vertices = 1) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +out gl_PerVertex +{ + vec4 gl_Position; +} gl_out[1]; + +layout(location = 0) patch out vec3 vFoo; + +void main() +{ + gl_TessLevelInner[0] = 8.8999996185302734375; + gl_TessLevelInner[1] = 6.900000095367431640625; + gl_TessLevelOuter[0] = 8.8999996185302734375; + gl_TessLevelOuter[1] = 6.900000095367431640625; + gl_TessLevelOuter[2] = 3.900000095367431640625; + gl_TessLevelOuter[3] = 4.900000095367431640625; + vFoo = vec3(1.0); + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/tese/triangle.desktop.sso.tese b/third_party/spirv-cross/reference/opt/shaders/desktop-only/tese/triangle.desktop.sso.tese new file mode 100644 index 0000000..c9bacd4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/tese/triangle.desktop.sso.tese @@ -0,0 +1,18 @@ +#version 450 +layout(triangles, cw, fractional_even_spacing) in; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = ((gl_in[0].gl_Position * gl_TessCoord.x) + (gl_in[1].gl_Position * gl_TessCoord.y)) + (gl_in[2].gl_Position * gl_TessCoord.z); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/basic.desktop.sso.vert b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/basic.desktop.sso.vert new file mode 100644 index 0000000..2f88039 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/basic.desktop.sso.vert @@ -0,0 +1,22 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(binding = 0, std140) uniform UBO +{ + mat4 uMVP; +} _16; + +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec3 vNormal; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = _16.uMVP * aVertex; + vNormal = aNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert new file mode 100644 index 0000000..a7c5d76 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert @@ -0,0 +1,20 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[4]; + float gl_CullDistance[3]; +}; + +void main() +{ + gl_Position = vec4(1.0); + gl_ClipDistance[0] = 0.0; + gl_ClipDistance[1] = 0.0; + gl_ClipDistance[2] = 0.0; + gl_ClipDistance[3] = 0.0; + gl_CullDistance[1] = 4.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/clip-cull-distance.desktop.vert b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/clip-cull-distance.desktop.vert new file mode 100644 index 0000000..2f3d49f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/clip-cull-distance.desktop.vert @@ -0,0 +1,15 @@ +#version 450 + +out float gl_ClipDistance[4]; +out float gl_CullDistance[3]; + +void main() +{ + gl_Position = vec4(1.0); + gl_ClipDistance[0] = 0.0; + gl_ClipDistance[1] = 0.0; + gl_ClipDistance[2] = 0.0; + gl_ClipDistance[3] = 0.0; + gl_CullDistance[1] = 4.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/out-block-qualifiers.vert b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/out-block-qualifiers.vert new file mode 100644 index 0000000..7c73168 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/out-block-qualifiers.vert @@ -0,0 +1,27 @@ +#version 450 + +layout(location = 0) out VertexData +{ + flat float f; + centroid vec4 g; + flat int h; + float i; +} vout; + +layout(location = 4) flat out float f; +layout(location = 5) centroid out vec4 g; +layout(location = 6) flat out int h; +layout(location = 7) out float i; + +void main() +{ + vout.f = 10.0; + vout.g = vec4(20.0); + vout.h = 20; + vout.i = 30.0; + f = 10.0; + g = vec4(20.0); + h = 20; + i = 30.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert new file mode 100644 index 0000000..2b3c5ce --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert @@ -0,0 +1,24 @@ +#version 450 +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseVertex gl_BaseVertexARB +#else +uniform int SPIRV_Cross_BaseVertex; +#endif +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +#ifndef GL_ARB_shader_draw_parameters +#error GL_ARB_shader_draw_parameters is not supported. +#endif + +void main() +{ + gl_Position = vec4(float(SPIRV_Cross_BaseVertex), float(SPIRV_Cross_BaseInstance), float(gl_DrawIDARB), 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert.vk b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert.vk new file mode 100644 index 0000000..6121dd8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert.vk @@ -0,0 +1,8 @@ +#version 450 +#extension GL_ARB_shader_draw_parameters : require + +void main() +{ + gl_Position = vec4(float(gl_BaseVertexARB), float(gl_BaseInstanceARB), float(gl_DrawIDARB), 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert new file mode 100644 index 0000000..bc16d04 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert @@ -0,0 +1,24 @@ +#version 460 +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseVertex gl_BaseVertexARB +#else +uniform int SPIRV_Cross_BaseVertex; +#endif +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +#ifndef GL_ARB_shader_draw_parameters +#error GL_ARB_shader_draw_parameters is not supported. +#endif + +void main() +{ + gl_Position = vec4(float(SPIRV_Cross_BaseVertex), float(SPIRV_Cross_BaseInstance), float(gl_DrawIDARB), 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert.vk b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert.vk new file mode 100644 index 0000000..b6948fb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert.vk @@ -0,0 +1,7 @@ +#version 460 + +void main() +{ + gl_Position = vec4(float(gl_BaseVertex), float(gl_BaseInstance), float(gl_DrawID), 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/array.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/array.flatten.vert new file mode 100644 index 0000000..de4eb3b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/array.flatten.vert @@ -0,0 +1,10 @@ +#version 310 es + +uniform vec4 UBO[56]; +layout(location = 0) in vec4 aVertex; + +void main() +{ + gl_Position = ((mat4(UBO[40], UBO[41], UBO[42], UBO[43]) * aVertex) + UBO[55]) + ((UBO[50] + UBO[45]) + vec4(UBO[54].x)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/basic.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/basic.flatten.vert new file mode 100644 index 0000000..f7eb758 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/basic.flatten.vert @@ -0,0 +1,13 @@ +#version 310 es + +uniform vec4 UBO[4]; +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec3 vNormal; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex; + vNormal = aNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/copy.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/copy.flatten.vert new file mode 100644 index 0000000..33caec4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/copy.flatten.vert @@ -0,0 +1,27 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + vec4 Color; +}; + +uniform vec4 UBO[12]; +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec4 vColor; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex; + vColor = vec4(0.0); + for (int _96 = 0; _96 < 4; ) + { + vec3 _68 = aVertex.xyz - Light(UBO[_96 * 2 + 4].xyz, UBO[_96 * 2 + 4].w, UBO[_96 * 2 + 5]).Position; + vColor += ((UBO[_96 * 2 + 5] * clamp(1.0 - (length(_68) / Light(UBO[_96 * 2 + 4].xyz, UBO[_96 * 2 + 4].w, UBO[_96 * 2 + 5]).Radius), 0.0, 1.0)) * dot(aNormal, normalize(_68))); + _96++; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/dynamic.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/dynamic.flatten.vert new file mode 100644 index 0000000..7129af2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/dynamic.flatten.vert @@ -0,0 +1,27 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + vec4 Color; +}; + +uniform vec4 UBO[12]; +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec4 vColor; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex; + vColor = vec4(0.0); + for (int _82 = 0; _82 < 4; ) + { + vec3 _54 = aVertex.xyz - UBO[_82 * 2 + 4].xyz; + vColor += ((UBO[_82 * 2 + 5] * clamp(1.0 - (length(_54) / UBO[_82 * 2 + 4].w), 0.0, 1.0)) * dot(aNormal, normalize(_54))); + _82++; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/matrix-conversion.flatten.frag b/third_party/spirv-cross/reference/opt/shaders/flatten/matrix-conversion.flatten.frag new file mode 100644 index 0000000..ee79bf5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/matrix-conversion.flatten.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform vec4 UBO[4]; +layout(location = 0) out vec3 FragColor; +layout(location = 0) flat in vec3 vNormal; + +void main() +{ + mat4 _19 = mat4(UBO[0], UBO[1], UBO[2], UBO[3]); + FragColor = mat3(_19[0].xyz, _19[1].xyz, _19[2].xyz) * vNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/matrixindex.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/matrixindex.flatten.vert new file mode 100644 index 0000000..f6d0fa4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/matrixindex.flatten.vert @@ -0,0 +1,19 @@ +#version 310 es + +uniform vec4 UBO[14]; +layout(location = 0) out vec4 oA; +layout(location = 1) out vec4 oB; +layout(location = 2) out vec4 oC; +layout(location = 3) out vec4 oD; +layout(location = 4) out vec4 oE; + +void main() +{ + gl_Position = vec4(0.0); + oA = UBO[1]; + oB = vec4(UBO[4].y, UBO[5].y, UBO[6].y, UBO[7].y); + oC = UBO[9]; + oD = vec4(UBO[10].x, UBO[11].x, UBO[12].x, UBO[13].x); + oE = vec4(UBO[1].z, UBO[6].y, UBO[9].z, UBO[12].y); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/multiindex.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/multiindex.flatten.vert new file mode 100644 index 0000000..3850bf6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/multiindex.flatten.vert @@ -0,0 +1,10 @@ +#version 310 es + +uniform vec4 UBO[15]; +layout(location = 0) in ivec2 aIndex; + +void main() +{ + gl_Position = UBO[aIndex.x * 5 + aIndex.y * 1 + 0]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/push-constant.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/push-constant.flatten.vert new file mode 100644 index 0000000..216c1f9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/push-constant.flatten.vert @@ -0,0 +1,13 @@ +#version 310 es + +uniform vec4 PushMe[6]; +layout(location = 1) in vec4 Pos; +layout(location = 0) out vec2 vRot; +layout(location = 0) in vec2 Rot; + +void main() +{ + gl_Position = mat4(PushMe[0], PushMe[1], PushMe[2], PushMe[3]) * Pos; + vRot = (mat2(PushMe[4].xy, PushMe[4].zw) * Rot) + vec2(PushMe[5].z); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/rowmajor.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/rowmajor.flatten.vert new file mode 100644 index 0000000..b74aa00 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/rowmajor.flatten.vert @@ -0,0 +1,10 @@ +#version 310 es + +uniform vec4 UBO[12]; +layout(location = 0) in vec4 aVertex; + +void main() +{ + gl_Position = (mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex) + (aVertex * mat4(UBO[4], UBO[5], UBO[6], UBO[7])); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/struct.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/struct.flatten.vert new file mode 100644 index 0000000..35db010 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/struct.flatten.vert @@ -0,0 +1,22 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + vec4 Color; +}; + +uniform vec4 UBO[6]; +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec4 vColor; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex; + vColor = vec4(0.0); + vec3 _39 = aVertex.xyz - UBO[4].xyz; + vColor += ((UBO[5] * clamp(1.0 - (length(_39) / UBO[4].w), 0.0, 1.0)) * dot(aNormal, normalize(_39))); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/struct.rowmajor.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/struct.rowmajor.flatten.vert new file mode 100644 index 0000000..709d992 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/struct.rowmajor.flatten.vert @@ -0,0 +1,21 @@ +#version 310 es + +struct Foo +{ + mat3x4 MVP0; + mat3x4 MVP1; +}; + +uniform vec4 UBO[8]; +layout(location = 0) in vec4 v0; +layout(location = 1) in vec4 v1; +layout(location = 0) out vec3 V0; +layout(location = 1) out vec3 V1; + +void main() +{ + Foo _20 = Foo(transpose(mat4x3(UBO[0].xyz, UBO[1].xyz, UBO[2].xyz, UBO[3].xyz)), transpose(mat4x3(UBO[4].xyz, UBO[5].xyz, UBO[6].xyz, UBO[7].xyz))); + V0 = v0 * _20.MVP0; + V1 = v1 * _20.MVP1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/swizzle.flatten.vert b/third_party/spirv-cross/reference/opt/shaders/flatten/swizzle.flatten.vert new file mode 100644 index 0000000..92afb47 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/swizzle.flatten.vert @@ -0,0 +1,21 @@ +#version 310 es + +uniform vec4 UBO[8]; +layout(location = 0) out vec4 oA; +layout(location = 1) out vec4 oB; +layout(location = 2) out vec4 oC; +layout(location = 3) out vec4 oD; +layout(location = 4) out vec4 oE; +layout(location = 5) out vec4 oF; + +void main() +{ + gl_Position = vec4(0.0); + oA = UBO[0]; + oB = vec4(UBO[1].xy, UBO[1].zw); + oC = vec4(UBO[2].x, UBO[3].xyz); + oD = vec4(UBO[4].xyz, UBO[4].w); + oE = vec4(UBO[5].x, UBO[5].y, UBO[5].z, UBO[5].w); + oF = vec4(UBO[6].x, UBO[6].zw, UBO[7].x); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/flatten/types.flatten.frag b/third_party/spirv-cross/reference/opt/shaders/flatten/types.flatten.frag new file mode 100644 index 0000000..a74327d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/flatten/types.flatten.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump ivec4 UBO1[2]; +uniform mediump uvec4 UBO2[2]; +uniform vec4 UBO0[2]; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = ((((vec4(UBO1[0]) + vec4(UBO1[1])) + vec4(UBO2[0])) + vec4(UBO2[1])) + UBO0[0]) + UBO0[1]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/reference/opt/shaders/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..f50d0d4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _17[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); + +layout(location = 0) out vec4 FragColor; + +void main() +{ + for (mediump int _46 = 0; _46 < 4; ) + { + mediump int _33 = _46 + 1; + FragColor += vec4(_17[_33]); + _46 = _33; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/avoid-expression-lowering-to-loop.frag b/third_party/spirv-cross/reference/opt/shaders/frag/avoid-expression-lowering-to-loop.frag new file mode 100644 index 0000000..9019ac0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/avoid-expression-lowering-to-loop.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 1, std140) uniform Count +{ + float count; +} _44; + +layout(binding = 0) uniform mediump sampler2D tex; + +layout(location = 0) in highp vec4 vertex; +layout(location = 0) out vec4 fragColor; + +void main() +{ + highp float _24 = 1.0 / float(textureSize(tex, 0).x); + highp float _34 = dFdx(vertex.x); + float _62; + _62 = 0.0; + for (float _61 = 0.0; _61 < _44.count; ) + { + _62 += (_24 * _34); + _61 += 1.0; + continue; + } + fragColor = vec4(_62); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/barycentric-nv.frag b/third_party/spirv-cross/reference/opt/shaders/frag/barycentric-nv.frag new file mode 100644 index 0000000..cc3b4de --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/barycentric-nv.frag @@ -0,0 +1,19 @@ +#version 450 +#extension GL_NV_fragment_shader_barycentric : require + +layout(binding = 0, std430) readonly buffer Vertices +{ + vec2 uvs[]; +} _19; + +layout(location = 0) out vec2 value; + +void main() +{ + int _23 = 3 * gl_PrimitiveID; + int _32 = _23 + 1; + int _39 = _23 + 2; + value = ((_19.uvs[_23] * gl_BaryCoordNV.x) + (_19.uvs[_32] * gl_BaryCoordNV.y)) + (_19.uvs[_39] * gl_BaryCoordNV.z); + value += (((_19.uvs[_23] * gl_BaryCoordNoPerspNV.x) + (_19.uvs[_32] * gl_BaryCoordNoPerspNV.y)) + (_19.uvs[_39] * gl_BaryCoordNoPerspNV.z)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/basic.frag b/third_party/spirv-cross/reference/opt/shaders/frag/basic.frag new file mode 100644 index 0000000..2a4e440 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/basic.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uTex; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; + +void main() +{ + FragColor = vColor * texture(uTex, vTex); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/reference/opt/shaders/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..56b4c89 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0, std430) buffer UBO +{ + vec4 results[1024]; +} _34; + +layout(binding = 1) uniform highp isampler2D Buf; + +layout(location = 0) flat in mediump int vIn; +layout(location = 1) flat in mediump int vIn2; +layout(location = 0) out vec4 FragColor; + +void main() +{ + mediump int _40 = texelFetch(Buf, ivec2(gl_FragCoord.xy), 0).x % 16; + FragColor = (_34.results[_40] + _34.results[_40]) + _34.results[(vIn * vIn) + (vIn2 * vIn2)]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/composite-extract-forced-temporary.frag b/third_party/spirv-cross/reference/opt/shaders/frag/composite-extract-forced-temporary.frag new file mode 100644 index 0000000..eb59732 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/composite-extract-forced-temporary.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D Texture; + +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 _19 = texture(Texture, vTexCoord); + float _22 = _19.x; + FragColor = vec4(_22 * _22); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/constant-array.frag b/third_party/spirv-cross/reference/opt/shaders/frag/constant-array.frag new file mode 100644 index 0000000..914888a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/constant-array.frag @@ -0,0 +1,22 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct Foobar +{ + float a; + float b; +}; + +const vec4 _37[3] = vec4[](vec4(1.0), vec4(2.0), vec4(3.0)); +const vec4 _55[2][2] = vec4[][](vec4[](vec4(1.0), vec4(2.0)), vec4[](vec4(8.0), vec4(10.0))); +const Foobar _75[2] = Foobar[](Foobar(10.0, 40.0), Foobar(90.0, 70.0)); + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + FragColor = ((_37[index] + _55[index][index + 1]) + vec4(30.0)) + vec4(_75[index].a + _75[index].b); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/constant-composites.frag b/third_party/spirv-cross/reference/opt/shaders/frag/constant-composites.frag new file mode 100644 index 0000000..7813b98 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/constant-composites.frag @@ -0,0 +1,23 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _16[4] = float[](1.0, 4.0, 3.0, 2.0); + +struct Foo +{ + float a; + float b; +}; + +const Foo _28[2] = Foo[](Foo(10.0, 20.0), Foo(30.0, 40.0)); + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int line; + +void main() +{ + FragColor = vec4(_16[line]); + FragColor += vec4(_28[line].a * _28[1 - line].a); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/false-loop-init.frag b/third_party/spirv-cross/reference/opt/shaders/frag/false-loop-init.frag new file mode 100644 index 0000000..7ded0ea --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/false-loop-init.frag @@ -0,0 +1,18 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 result; +layout(location = 0) in vec4 accum; + +void main() +{ + result = vec4(0.0); + for (mediump int _48 = 0; _48 < 4; ) + { + result += accum; + _48 += int((accum.y > 10.0) ? 40u : 30u); + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/flush_params.frag b/third_party/spirv-cross/reference/opt/shaders/frag/flush_params.frag new file mode 100644 index 0000000..16b4994 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/flush_params.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(10.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/reference/opt/shaders/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..f49b906 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(0.0); + for (int _43 = 0; _43 < 3; ) + { + FragColor[_43] += float(_43); + _43++; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/for-loop-init.frag b/third_party/spirv-cross/reference/opt/shaders/frag/for-loop-init.frag new file mode 100644 index 0000000..6c2dfb5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/for-loop-init.frag @@ -0,0 +1,65 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out mediump int FragColor; + +void main() +{ + do + { + FragColor = 16; + for (mediump int _143 = 0; _143 < 25; ) + { + FragColor += 10; + _143++; + continue; + } + for (mediump int _144 = 1; _144 < 30; ) + { + FragColor += 11; + _144++; + continue; + } + mediump int _145; + _145 = 0; + for (; _145 < 20; ) + { + FragColor += 12; + _145++; + continue; + } + mediump int _62 = _145 + 3; + FragColor += _62; + if (_62 == 40) + { + for (mediump int _149 = 0; _149 < 40; ) + { + FragColor += 13; + _149++; + continue; + } + break; + } + FragColor += _62; + mediump ivec2 _146; + _146 = ivec2(0); + for (; _146.x < 10; ) + { + FragColor += _146.y; + mediump ivec2 _142 = _146; + _142.x = _146.x + 4; + _146 = _142; + continue; + } + for (mediump int _148 = _62; _148 < 40; ) + { + FragColor += _148; + _148++; + continue; + } + FragColor += _62; + break; + } while(false); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/frexp-modf.frag b/third_party/spirv-cross/reference/opt/shaders/frag/frexp-modf.frag new file mode 100644 index 0000000..25f3360 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/frexp-modf.frag @@ -0,0 +1,33 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct ResType +{ + highp float _m0; + int _m1; +}; + +struct ResType_1 +{ + highp vec2 _m0; + ivec2 _m1; +}; + +layout(location = 0) in float v0; +layout(location = 1) in vec2 v1; +layout(location = 0) out float FragColor; + +void main() +{ + ResType _22; + _22._m0 = frexp(v0 + 1.0, _22._m1); + ResType_1 _35; + _35._m0 = frexp(v1, _35._m1); + float r0; + float _41 = modf(v0, r0); + vec2 r1; + vec2 _45 = modf(v1, r1); + FragColor = ((((_22._m0 + _35._m0.x) + _35._m0.y) + _41) + _45.x) + _45.y; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/front-facing.frag b/third_party/spirv-cross/reference/opt/shaders/frag/front-facing.frag new file mode 100644 index 0000000..cc9aecc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/front-facing.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; + +void main() +{ + if (gl_FrontFacing) + { + FragColor = vA; + } + else + { + FragColor = vB; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/gather-dref.frag b/third_party/spirv-cross/reference/opt/shaders/frag/gather-dref.frag new file mode 100644 index 0000000..5416f79 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/gather-dref.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2DShadow uT; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec3 vUV; + +void main() +{ + FragColor = textureGather(uT, vUV.xy, vUV.z); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/ground.frag b/third_party/spirv-cross/reference/opt/shaders/frag/ground.frag new file mode 100644 index 0000000..f59a402 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/ground.frag @@ -0,0 +1,35 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 4, std140) uniform GlobalPSData +{ + vec4 g_CamPos; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_ResolutionParams; + vec4 g_TimeParams; + vec4 g_FogColor_Distance; +} _101; + +layout(binding = 2) uniform mediump sampler2D TexNormalmap; + +layout(location = 3) out vec4 LightingOut; +layout(location = 2) out vec4 NormalOut; +layout(location = 1) out vec4 SpecularOut; +layout(location = 0) out vec4 AlbedoOut; +layout(location = 0) in vec2 TexCoord; +layout(location = 1) in vec3 EyeVec; + +void main() +{ + vec3 _68 = normalize((texture(TexNormalmap, TexCoord).xyz * 2.0) - vec3(1.0)); + float _113 = smoothstep(0.0, 0.1500000059604644775390625, (_101.g_CamPos.y + EyeVec.y) * 0.004999999888241291046142578125); + float _125 = smoothstep(0.699999988079071044921875, 0.75, _68.y); + vec3 _130 = mix(vec3(0.100000001490116119384765625), mix(vec3(0.100000001490116119384765625, 0.300000011920928955078125, 0.100000001490116119384765625), vec3(0.800000011920928955078125), vec3(_113)), vec3(_125)); + LightingOut = vec4(0.0); + NormalOut = vec4((_68 * 0.5) + vec3(0.5), 0.0); + SpecularOut = vec4(1.0 - (_125 * _113), 0.0, 0.0, 0.0); + AlbedoOut = vec4(_130 * _130, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/helper-invocation.frag b/third_party/spirv-cross/reference/opt/shaders/frag/helper-invocation.frag new file mode 100644 index 0000000..0c44f72 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/helper-invocation.frag @@ -0,0 +1,23 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uSampler; + +layout(location = 0) in vec2 vUV; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 _52; + if (!gl_HelperInvocation) + { + _52 = textureLod(uSampler, vUV, 0.0); + } + else + { + _52 = vec4(1.0); + } + FragColor = _52; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag b/third_party/spirv-cross/reference/opt/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag new file mode 100644 index 0000000..050218b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag @@ -0,0 +1,28 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int vA; +layout(location = 1) flat in mediump int vB; + +void main() +{ + FragColor = vec4(0.0); + mediump int _49; + mediump int _58; + for (mediump int _57 = 0, _60 = 0; _57 < vA; _60 = _58, _57 += _49) + { + if ((vA + _57) == 20) + { + _58 = 50; + } + else + { + _58 = ((vB + _57) == 40) ? 60 : _60; + } + _49 = _58 + 10; + FragColor += vec4(1.0); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/image-load-store-uint-coord.asm.frag b/third_party/spirv-cross/reference/opt/shaders/frag/image-load-store-uint-coord.asm.frag new file mode 100644 index 0000000..f25b4b7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/image-load-store-uint-coord.asm.frag @@ -0,0 +1,17 @@ +#version 450 + +layout(binding = 1, rgba32f) uniform image2D RWIm; +layout(binding = 0, rgba32f) uniform writeonly imageBuffer RWBuf; +layout(binding = 1) uniform sampler2D ROIm; +layout(binding = 0) uniform samplerBuffer ROBuf; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + imageStore(RWIm, ivec2(uvec2(10u)), vec4(10.0, 0.5, 8.0, 2.0)); + vec4 _70 = imageLoad(RWIm, ivec2(uvec2(30u))); + imageStore(RWBuf, int(80u), _70); + _entryPointOutput = (_70 + texelFetch(ROIm, ivec2(uvec2(50u, 60u)), 0)) + texelFetch(ROBuf, int(80u)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/inside-loop-dominated-variable-preservation.frag b/third_party/spirv-cross/reference/opt/shaders/frag/inside-loop-dominated-variable-preservation.frag new file mode 100644 index 0000000..7724b14 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/inside-loop-dominated-variable-preservation.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/loop-dominator-and-switch-default.frag b/third_party/spirv-cross/reference/opt/shaders/frag/loop-dominator-and-switch-default.frag new file mode 100644 index 0000000..2c19348 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/loop-dominator-and-switch-default.frag @@ -0,0 +1,56 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 fragColor; + +vec4 _80; + +void main() +{ + mediump int _18 = int(_80.x); + vec4 _82; + _82 = _80; + vec4 _89; + for (mediump int _81 = 0; _81 < _18; _82 = _89, _81++) + { + vec4 _83; + switch (_18) + { + case 0: + { + vec4 _74 = _82; + _74.y = 0.0; + _83 = _74; + break; + } + case 1: + { + vec4 _76 = _82; + _76.y = 1.0; + _83 = _76; + break; + } + default: + { + vec4 _88; + _88 = _82; + for (mediump int _84 = 0; _84 < _18; ) + { + vec4 _72 = _88; + _72.y = _88.y + 0.5; + _88 = _72; + _84++; + continue; + } + _89 = _88; + continue; + } + } + vec4 _79 = _83; + _79.y = _83.y + 0.5; + _89 = _79; + } + fragColor = _82; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/lut-promotion.frag b/third_party/spirv-cross/reference/opt/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000..2f57d84 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/lut-promotion.frag @@ -0,0 +1,41 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _16[16] = float[](1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0); +const vec4 _60[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); +const vec4 _104[4] = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + +layout(location = 0) out float FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + FragColor = _16[index]; + if (index < 10) + { + FragColor += _16[index ^ 1]; + } + else + { + FragColor += _16[index & 1]; + } + bool _63 = index > 30; + if (_63) + { + FragColor += _60[index & 3].y; + } + else + { + FragColor += _60[index & 1].x; + } + vec4 foobar[4] = _60; + if (_63) + { + foobar[1].z = 20.0; + } + mediump int _91 = index & 3; + FragColor += foobar[_91].z; + FragColor += _104[_91].z; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/mix.frag b/third_party/spirv-cross/reference/opt/shaders/frag/mix.frag new file mode 100644 index 0000000..f791d45 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/mix.frag @@ -0,0 +1,18 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vIn0; +layout(location = 1) in vec4 vIn1; +layout(location = 2) in float vIn2; +layout(location = 3) in float vIn3; + +void main() +{ + FragColor = vec4(vIn0.x, vIn1.y, vIn0.z, vIn0.w); + FragColor = vec4(vIn3); + FragColor = vIn0.xyzw; + FragColor = vec4(vIn2); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/partial-write-preserve.frag b/third_party/spirv-cross/reference/opt/shaders/frag/partial-write-preserve.frag new file mode 100644 index 0000000..d6c1fe8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/partial-write-preserve.frag @@ -0,0 +1,8 @@ +#version 310 es +precision mediump float; +precision highp int; + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/pixel-interlock-ordered.frag b/third_party/spirv-cross/reference/opt/shaders/frag/pixel-interlock-ordered.frag new file mode 100644 index 0000000..46cca96 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/pixel-interlock-ordered.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(binding = 2, std430) coherent buffer Buffer +{ + int foo; + uint bar; +} _30; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0), vec4(1.0, 0.0, 0.0, 1.0)); + uint _27 = imageAtomicAdd(img2, ivec2(0), 1u); + _30.foo += 42; + uint _41 = atomicAnd(_30.bar, 255u); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/pixel-interlock-unordered.frag b/third_party/spirv-cross/reference/opt/shaders/frag/pixel-interlock-unordered.frag new file mode 100644 index 0000000..d60cd14 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/pixel-interlock-unordered.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_unordered) in; + +layout(binding = 2, std430) coherent buffer Buffer +{ + int foo; + uint bar; +} _30; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0), vec4(1.0, 0.0, 0.0, 1.0)); + uint _27 = imageAtomicAdd(img2, ivec2(0), 1u); + _30.foo += 42; + uint _41 = atomicAnd(_30.bar, 255u); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/pls.frag b/third_party/spirv-cross/reference/opt/shaders/frag/pls.frag new file mode 100644 index 0000000..1cafdbd --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/pls.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 PLSOut0; +layout(location = 0) in vec4 PLSIn0; +layout(location = 1) out vec4 PLSOut1; +layout(location = 1) in vec4 PLSIn1; +layout(location = 2) out vec4 PLSOut2; +layout(location = 2) in vec4 PLSIn2; +layout(location = 3) out vec4 PLSOut3; +layout(location = 3) in vec4 PLSIn3; + +void main() +{ + PLSOut0 = PLSIn0 * 2.0; + PLSOut1 = PLSIn1 * 6.0; + PLSOut2 = PLSIn2 * 7.0; + PLSOut3 = PLSIn3 * 4.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/post-depth-coverage-es.frag b/third_party/spirv-cross/reference/opt/shaders/frag/post-depth-coverage-es.frag new file mode 100644 index 0000000..d086560 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/post-depth-coverage-es.frag @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_post_depth_coverage : require +#extension GL_OES_sample_variables : require +precision mediump float; +precision highp int; +layout(early_fragment_tests, post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(gl_SampleMaskIn[0])); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/post-depth-coverage.frag b/third_party/spirv-cross/reference/opt/shaders/frag/post-depth-coverage.frag new file mode 100644 index 0000000..caca9c0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/post-depth-coverage.frag @@ -0,0 +1,15 @@ +#version 450 +#if defined(GL_ARB_post_depth_coverge) +#extension GL_ARB_post_depth_coverage : require +#else +#extension GL_EXT_post_depth_coverage : require +#endif +layout(early_fragment_tests, post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(gl_SampleMaskIn[0])); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/round-even.frag b/third_party/spirv-cross/reference/opt/shaders/frag/round-even.frag new file mode 100644 index 0000000..ab6f37a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/round-even.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in float vB; + +void main() +{ + FragColor = roundEven(vA); + FragColor *= roundEven(vB); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/round.frag b/third_party/spirv-cross/reference/opt/shaders/frag/round.frag new file mode 100644 index 0000000..0f1fc0d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/round.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in float vB; + +void main() +{ + FragColor = round(vA); + FragColor *= round(vB); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/sample-interlock-ordered.frag b/third_party/spirv-cross/reference/opt/shaders/frag/sample-interlock-ordered.frag new file mode 100644 index 0000000..67ca556 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/sample-interlock-ordered.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(sample_interlock_ordered) in; + +layout(binding = 2, std430) coherent buffer Buffer +{ + int foo; + uint bar; +} _30; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0), vec4(1.0, 0.0, 0.0, 1.0)); + uint _27 = imageAtomicAdd(img2, ivec2(0), 1u); + _30.foo += 42; + uint _47 = atomicAnd(_30.bar, uint(gl_SampleMaskIn[0])); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/sample-interlock-unordered.frag b/third_party/spirv-cross/reference/opt/shaders/frag/sample-interlock-unordered.frag new file mode 100644 index 0000000..ea74397 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/sample-interlock-unordered.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(sample_interlock_unordered) in; + +layout(binding = 2, std430) coherent buffer Buffer +{ + int foo; + uint bar; +} _30; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0), vec4(1.0, 0.0, 0.0, 1.0)); + uint _27 = imageAtomicAdd(img2, ivec2(0), 1u); + _30.foo += 42; + uint _41 = atomicAnd(_30.bar, 255u); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/sample-parameter.frag b/third_party/spirv-cross/reference/opt/shaders/frag/sample-parameter.frag new file mode 100644 index 0000000..3c130e6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/sample-parameter.frag @@ -0,0 +1,13 @@ +#version 310 es +#extension GL_OES_sample_variables : require +precision mediump float; +precision highp int; + +layout(location = 0) out vec2 FragColor; + +void main() +{ + FragColor = (gl_SamplePosition + vec2(float(gl_SampleMaskIn[0]))) + vec2(float(gl_SampleID)); + gl_SampleMask[0] = 1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/sampler-ms.frag b/third_party/spirv-cross/reference/opt/shaders/frag/sampler-ms.frag new file mode 100644 index 0000000..d78b805 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/sampler-ms.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2DMS uSampler; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + ivec2 _17 = ivec2(gl_FragCoord.xy); + FragColor = ((texelFetch(uSampler, _17, 0) + texelFetch(uSampler, _17, 1)) + texelFetch(uSampler, _17, 2)) + texelFetch(uSampler, _17, 3); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/sampler-proj.frag b/third_party/spirv-cross/reference/opt/shaders/frag/sampler-proj.frag new file mode 100644 index 0000000..865dec6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/sampler-proj.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uTex; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vTex; + +void main() +{ + highp vec4 _19 = vTex; + _19.z = vTex.w; + FragColor = textureProj(uTex, _19.xyz); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/sampler.frag b/third_party/spirv-cross/reference/opt/shaders/frag/sampler.frag new file mode 100644 index 0000000..2a4e440 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/sampler.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uTex; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; + +void main() +{ + FragColor = vColor * texture(uTex, vTex); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/reference/opt/shaders/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..94d671b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/scalar-refract-reflect.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vRefract; + +void main() +{ + FragColor = refract(vRefract.x, vRefract.y, vRefract.z); + FragColor += reflect(vRefract.x, vRefract.y); + FragColor += refract(vRefract.xy, vRefract.yz, vRefract.z).y; + FragColor += reflect(vRefract.xy, vRefract.zy).y; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/selection-block-dominator.frag b/third_party/spirv-cross/reference/opt/shaders/frag/selection-block-dominator.frag new file mode 100644 index 0000000..50a5a37 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/selection-block-dominator.frag @@ -0,0 +1,19 @@ +#version 450 + +layout(location = 0) flat in int vIndex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + do + { + if (vIndex != 1) + { + FragColor = vec4(1.0); + break; + } + FragColor = vec4(10.0); + break; + } while(false); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/struct-type-unrelated-alias.frag b/third_party/spirv-cross/reference/opt/shaders/frag/struct-type-unrelated-alias.frag new file mode 100644 index 0000000..d6fa667 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/struct-type-unrelated-alias.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = 30.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/switch-unsigned-case.frag b/third_party/spirv-cross/reference/opt/shaders/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..4177f9e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/switch-unsigned-case.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0, std140) uniform Buff +{ + mediump uint TestVal; +} _15; + +layout(location = 0) out vec4 fsout_Color; + +void main() +{ + fsout_Color = vec4(1.0); + switch (_15.TestVal) + { + case 0u: + { + fsout_Color = vec4(0.100000001490116119384765625); + break; + } + case 1u: + { + fsout_Color = vec4(0.20000000298023223876953125); + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/swizzle.frag b/third_party/spirv-cross/reference/opt/shaders/frag/swizzle.frag new file mode 100644 index 0000000..51f5b19 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/swizzle.frag @@ -0,0 +1,22 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D samp; + +layout(location = 0) out vec4 FragColor; +layout(location = 2) in vec2 vUV; +layout(location = 1) in vec3 vNormal; + +void main() +{ + vec4 _19 = texture(samp, vUV); + float _23 = _19.x; + FragColor = vec4(_23, _19.yz, 1.0); + FragColor = vec4(_23, _19.z, 1.0, 4.0); + FragColor = vec4(_23, _23, texture(samp, vUV + vec2(0.100000001490116119384765625)).yy); + FragColor = vec4(vNormal, 1.0); + FragColor = vec4(vNormal + vec3(1.7999999523162841796875), 1.0); + FragColor = vec4(vUV, vUV + vec2(1.7999999523162841796875)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/texel-fetch-offset.frag b/third_party/spirv-cross/reference/opt/shaders/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..416f764 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/texel-fetch-offset.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uTexture; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + mediump ivec2 _22 = ivec2(gl_FragCoord.xy); + FragColor = texelFetchOffset(uTexture, _22, 0, ivec2(1)); + FragColor += texelFetchOffset(uTexture, _22, 0, ivec2(-1, 1)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/ubo-load-row-major-workaround.frag b/third_party/spirv-cross/reference/opt/shaders/frag/ubo-load-row-major-workaround.frag new file mode 100644 index 0000000..6ed3f78 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/ubo-load-row-major-workaround.frag @@ -0,0 +1,46 @@ +#version 450 + +struct RowMajor +{ + mat4 B; +}; + +struct NestedRowMajor +{ + RowMajor rm; +}; + +layout(binding = 2, std140) uniform UBO3 +{ + layout(row_major) NestedRowMajor rm2; +} _17; + +layout(binding = 1, std140) uniform UBO2 +{ + layout(row_major) RowMajor rm; +} _35; + +layout(binding = 0, std140) uniform UBO +{ + layout(row_major) mat4 A; + mat4 C; +} _42; + +layout(binding = 3, std140) uniform UBONoWorkaround +{ + mat4 D; +} _56; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 Clip; + +NestedRowMajor SPIRV_Cross_workaround_load_row_major(NestedRowMajor wrap) { return wrap; } +mat4 SPIRV_Cross_workaround_load_row_major(mat4 wrap) { return wrap; } + +void main() +{ + FragColor = (((SPIRV_Cross_workaround_load_row_major(_17.rm2).rm.B * SPIRV_Cross_workaround_load_row_major(_35.rm.B)) * SPIRV_Cross_workaround_load_row_major(_42.A)) * SPIRV_Cross_workaround_load_row_major(_42.C)) * Clip; + FragColor += (_56.D * Clip); + FragColor += (_42.A[1] * Clip); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/ubo_layout.frag b/third_party/spirv-cross/reference/opt/shaders/frag/ubo_layout.frag new file mode 100644 index 0000000..bc0b01c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/ubo_layout.frag @@ -0,0 +1,26 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct Str +{ + mat4 foo; +}; + +layout(binding = 0, std140) uniform UBO1 +{ + layout(row_major) Str foo; +} ubo1; + +layout(binding = 1, std140) uniform UBO2 +{ + Str foo; +} ubo0; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = ubo1.foo.foo[0] + ubo0.foo.foo[0]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/frag/unary-enclose.frag b/third_party/spirv-cross/reference/opt/shaders/frag/unary-enclose.frag new file mode 100644 index 0000000..e7b0bf5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/frag/unary-enclose.frag @@ -0,0 +1,12 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vIn; + +void main() +{ + FragColor = vIn; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/basic.geom b/third_party/spirv-cross/reference/opt/shaders/geom/basic.geom new file mode 100644 index 0000000..f91136f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/basic.geom @@ -0,0 +1,27 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(invocations = 4, triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[3]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + float _37 = float(gl_InvocationID); + vNormal = vin[0].normal + vec3(_37); + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal + vec3(4.0 * _37); + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal + vec3(2.0 * _37); + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/geometry-passthrough.geom b/third_party/spirv-cross/reference/opt/shaders/geom/geometry-passthrough.geom new file mode 100644 index 0000000..d0d8806 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/geometry-passthrough.geom @@ -0,0 +1,27 @@ +#version 450 +#extension GL_NV_geometry_shader_passthrough : require +layout(triangles) in; + +layout(passthrough) in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +layout(passthrough, location = 0) in VertexBlock +{ + int a; + int b; +} v1[3]; + +layout(location = 2) in VertexBlock2 +{ + int a; + layout(passthrough) int b; +} v2[3]; + + +void main() +{ + gl_Layer = (gl_InvocationID + v1[0].a) + v2[1].b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/lines-adjacency.geom b/third_party/spirv-cross/reference/opt/shaders/geom/lines-adjacency.geom new file mode 100644 index 0000000..46a21e9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/lines-adjacency.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(lines_adjacency) in; +layout(max_vertices = 3, line_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[4]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/lines.geom b/third_party/spirv-cross/reference/opt/shaders/geom/lines.geom new file mode 100644 index 0000000..c5aaa53 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/lines.geom @@ -0,0 +1,23 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(lines) in; +layout(max_vertices = 2, line_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[2]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/multi-stream.geom b/third_party/spirv-cross/reference/opt/shaders/geom/multi-stream.geom new file mode 100644 index 0000000..548164d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/multi-stream.geom @@ -0,0 +1,14 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 2, points) out; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + EmitStreamVertex(0); + EndStreamPrimitive(0); + gl_Position = gl_in[0].gl_Position + vec4(2.0); + EmitStreamVertex(1); + EndStreamPrimitive(1); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/points.geom b/third_party/spirv-cross/reference/opt/shaders/geom/points.geom new file mode 100644 index 0000000..4d59137 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/points.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(points) in; +layout(max_vertices = 3, points) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[1]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/single-invocation.geom b/third_party/spirv-cross/reference/opt/shaders/geom/single-invocation.geom new file mode 100644 index 0000000..fdccacc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/single-invocation.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[3]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/transform-feedback-streams.geom b/third_party/spirv-cross/reference/opt/shaders/geom/transform-feedback-streams.geom new file mode 100644 index 0000000..4d238b4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/transform-feedback-streams.geom @@ -0,0 +1,26 @@ +#version 450 +layout(points) in; +layout(max_vertices = 2, points) out; + +layout(xfb_buffer = 1, xfb_stride = 20, stream = 1) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(location = 0, xfb_buffer = 2, xfb_stride = 32, xfb_offset = 16, stream = 1) out vec4 vFoo; +layout(xfb_buffer = 3, xfb_stride = 16, stream = 2) out VertOut +{ + layout(location = 1, xfb_offset = 0) vec4 vBar; +} _23; + + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + EmitStreamVertex(1); + _23.vBar = vec4(5.0); + EmitStreamVertex(2); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/triangles-adjacency.geom b/third_party/spirv-cross/reference/opt/shaders/geom/triangles-adjacency.geom new file mode 100644 index 0000000..e9e6857 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/triangles-adjacency.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(triangles_adjacency) in; +layout(max_vertices = 3, triangle_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[6]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/geom/triangles.geom b/third_party/spirv-cross/reference/opt/shaders/geom/triangles.geom new file mode 100644 index 0000000..fdccacc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/geom/triangles.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[3]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/explicit-lod.legacy.frag b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/explicit-lod.legacy.frag new file mode 100644 index 0000000..6e8dbf1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/explicit-lod.legacy.frag @@ -0,0 +1,12 @@ +#version 100 +#extension GL_EXT_shader_texture_lod : require +precision mediump float; +precision highp int; + +uniform mediump sampler2D tex; + +void main() +{ + gl_FragData[0] = texture2DLodEXT(tex, vec2(0.4000000059604644775390625, 0.60000002384185791015625), 0.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/explicit-lod.legacy.vert b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/explicit-lod.legacy.vert new file mode 100644 index 0000000..b73faa4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/explicit-lod.legacy.vert @@ -0,0 +1,11 @@ +#version 100 + +uniform mediump sampler2D tex; + +varying mediump vec4 FragColor; + +void main() +{ + FragColor = texture2DLod(tex, vec2(0.4000000059604644775390625, 0.60000002384185791015625), 3.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/fma.legacy.frag b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/fma.legacy.frag new file mode 100644 index 0000000..bcb2d4c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/fma.legacy.frag @@ -0,0 +1,13 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp vec4 vA; +varying highp vec4 vB; +varying highp vec4 vC; + +void main() +{ + gl_FragData[0] = vA * vB + vC; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/io-blocks.legacy.frag b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/io-blocks.legacy.frag new file mode 100644 index 0000000..d5a60d5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/io-blocks.legacy.frag @@ -0,0 +1,12 @@ +#version 100 +precision mediump float; +precision highp int; + +varying vec4 vin_color; +varying highp vec3 vin_normal; + +void main() +{ + gl_FragData[0] = vin_color + vin_normal.xyzz; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag new file mode 100644 index 0000000..10ce5a5 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag @@ -0,0 +1,36 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Foo +{ + highp vec4 a; + highp vec4 b; +}; + +struct Bar +{ + highp vec4 a; + highp vec4 b; +}; + +struct Baz +{ + Foo foo; + Bar bar; +}; + +varying highp vec4 baz_foo_a; +varying highp vec4 baz_foo_b; +varying highp vec4 baz_bar_a; +varying highp vec4 baz_bar_b; +varying highp vec4 _33_a_a; +varying highp vec4 _33_a_b; +varying highp vec4 _33_b_a; +varying highp vec4 _33_b_b; + +void main() +{ + gl_FragData[0] = (((_33_a_a + _33_b_b) + baz_foo_b) + baz_foo_a) + baz_bar_b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/round.legacy.frag b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/round.legacy.frag new file mode 100644 index 0000000..9033bc3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/round.legacy.frag @@ -0,0 +1,13 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp vec4 vA; +varying highp float vB; + +void main() +{ + gl_FragData[0] = floor(vA + vec4(0.5)); + gl_FragData[0] *= floor(vB + float(0.5)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/struct-varying.legacy.frag b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/struct-varying.legacy.frag new file mode 100644 index 0000000..e131f2e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/struct-varying.legacy.frag @@ -0,0 +1,18 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Inputs +{ + highp vec4 a; + highp vec2 b; +}; + +varying highp vec4 vin_a; +varying highp vec2 vin_b; + +void main() +{ + gl_FragData[0] = ((((Inputs(vin_a, vin_b).a + Inputs(vin_a, vin_b).b.xxyy) + Inputs(vin_a, vin_b).a) + Inputs(vin_a, vin_b).b.yyxx) + vin_a) + vin_b.xxyy; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/switch.legacy.frag b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/switch.legacy.frag new file mode 100644 index 0000000..1920958 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/fragment/switch.legacy.frag @@ -0,0 +1,77 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp float vIndexF; + +void main() +{ + int _13 = int(vIndexF); + highp vec4 _65; + highp vec4 _66; + highp vec4 _68; + for (int SPIRV_Cross_Dummy25 = 0; SPIRV_Cross_Dummy25 < 1; SPIRV_Cross_Dummy25++) + { + if (_13 == 2) + { + _68 = vec4(0.0, 2.0, 3.0, 4.0); + break; + } + else if ((_13 == 4) || (_13 == 5)) + { + _68 = vec4(1.0, 2.0, 3.0, 4.0); + break; + } + else if ((_13 == 8) || (_13 == 9)) + { + _68 = vec4(40.0, 20.0, 30.0, 40.0); + break; + } + else if (_13 == 10) + { + _65 = vec4(10.0); + highp vec4 _45 = _65 + vec4(1.0); + _66 = _45; + highp vec4 _48 = _66 + vec4(2.0); + _68 = _48; + break; + } + else if (_13 == 11) + { + _65 = vec4(0.0); + highp vec4 _45 = _65 + vec4(1.0); + _66 = _45; + highp vec4 _48 = _66 + vec4(2.0); + _68 = _48; + break; + } + else if (_13 == 12) + { + _66 = vec4(0.0); + highp vec4 _48 = _66 + vec4(2.0); + _68 = _48; + break; + } + else + { + _68 = vec4(10.0, 20.0, 30.0, 40.0); + break; + } + } + highp vec4 _70; + for (int SPIRV_Cross_Dummy146 = 0; SPIRV_Cross_Dummy146 < 1; SPIRV_Cross_Dummy146++) + { + if ((_13 == 10) || (_13 == 20)) + { + _70 = vec4(40.0); + break; + } + else + { + _70 = vec4(20.0); + break; + } + } + gl_FragData[0] = _68 + _70; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/vert/implicit-lod.legacy.vert b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/implicit-lod.legacy.vert new file mode 100644 index 0000000..2d20504 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/implicit-lod.legacy.vert @@ -0,0 +1,9 @@ +#version 100 + +uniform mediump sampler2D tex; + +void main() +{ + gl_Position = texture2DLod(tex, vec2(0.4000000059604644775390625, 0.60000002384185791015625), 0.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/vert/io-block.legacy.vert b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/io-block.legacy.vert new file mode 100644 index 0000000..3c518dc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/io-block.legacy.vert @@ -0,0 +1,13 @@ +#version 100 + +attribute vec4 Position; +varying vec4 vout_color; +varying vec3 vout_normal; + +void main() +{ + gl_Position = Position; + vout_color = vec4(1.0); + vout_normal = vec3(0.5); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert new file mode 100644 index 0000000..837a11a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert @@ -0,0 +1,17 @@ +#version 100 + +struct Foo +{ + float a[4]; +}; + +varying float foo_a[4]; + +void main() +{ + gl_Position = vec4(1.0); + for (int _46 = 0; _46 < 4; foo_a[_46] = float(_46 + 2), _46++) + { + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert new file mode 100644 index 0000000..cf807c4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert @@ -0,0 +1,49 @@ +#version 100 + +struct Foo +{ + vec4 a; + vec4 b; +}; + +struct Bar +{ + vec4 a; + vec4 b; +}; + +struct Baz +{ + Foo foo; + Bar bar; +}; + +varying vec4 _12_a_a; +varying vec4 _12_a_b; +varying vec4 _12_b_a; +varying vec4 _12_b_b; +varying vec4 baz_foo_a; +varying vec4 baz_foo_b; +varying vec4 baz_bar_a; +varying vec4 baz_bar_b; + +void main() +{ + _12_a_a = vec4(10.0); + _12_a_b = vec4(20.0); + _12_b_a = vec4(30.0); + _12_b_b = vec4(40.0); + _12_a_a = Foo(vec4(50.0), vec4(60.0)).a; + _12_a_b = Foo(vec4(50.0), vec4(60.0)).b; + _12_b_a = Bar(vec4(50.0), vec4(60.0)).a; + _12_b_b = Bar(vec4(50.0), vec4(60.0)).b; + baz_foo_a = Foo(vec4(100.0), vec4(200.0)).a; + baz_foo_b = Foo(vec4(100.0), vec4(200.0)).b; + baz_bar_a = Bar(vec4(300.0), vec4(400.0)).a; + baz_bar_b = Bar(vec4(300.0), vec4(400.0)).b; + baz_foo_a = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))).foo.a; + baz_foo_b = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))).foo.b; + baz_bar_a = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))).bar.a; + baz_bar_b = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))).bar.b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-varying.legacy.vert b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-varying.legacy.vert new file mode 100644 index 0000000..66136d2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/struct-varying.legacy.vert @@ -0,0 +1,24 @@ +#version 100 + +struct Output +{ + vec4 a; + vec2 b; +}; + +varying vec4 vout_a; +varying vec2 vout_b; + +void main() +{ + vout_a = Output(vec4(0.5), vec2(0.25)).a; + vout_b = Output(vec4(0.5), vec2(0.25)).b; + vout_a = Output(vec4(0.5), vec2(0.25)).a; + vout_b = Output(vec4(0.5), vec2(0.25)).b; + Output _22 = Output(vout_a, vout_b); + vout_a = _22.a; + vout_b = _22.b; + vout_a.x = 1.0; + vout_b.y = 1.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/vert/switch-nested.legacy.vert b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/switch-nested.legacy.vert new file mode 100644 index 0000000..fb8d46e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/switch-nested.legacy.vert @@ -0,0 +1,43 @@ +#version 100 + +struct UBO +{ + int func_arg; + int inner_func_arg; +}; + +uniform UBO _34; + +void main() +{ + vec4 _102; + for (int SPIRV_Cross_Dummy30 = 0; SPIRV_Cross_Dummy30 < 1; SPIRV_Cross_Dummy30++) + { + if (_34.func_arg != 0) + { + vec4 _101; + for (int SPIRV_Cross_Dummy45 = 0; SPIRV_Cross_Dummy45 < 1; SPIRV_Cross_Dummy45++) + { + if (_34.inner_func_arg != 0) + { + _101 = vec4(1.0); + break; + } + else + { + _101 = vec4(0.0); + break; + } + } + _102 = _101; + break; + } + else + { + _102 = vec4(0.0); + break; + } + } + gl_Position = _102; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/legacy/vert/transpose.legacy.vert b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/transpose.legacy.vert new file mode 100644 index 0000000..0f9b97a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/legacy/vert/transpose.legacy.vert @@ -0,0 +1,29 @@ +#version 100 + +struct Buffer +{ + mat4 MVPRowMajor; + mat4 MVPColMajor; + mat4 M; +}; + +uniform Buffer _13; + +attribute vec4 Position; + +mat4 SPIRV_Cross_workaround_load_row_major(mat4 wrap) { return wrap; } + +mat4 SPIRV_Cross_Transpose(mat4 m) +{ + return mat4(m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], m[1][2], m[2][2], m[3][2], m[0][3], m[1][3], m[2][3], m[3][3]); +} + +void main() +{ + mat4 _55 = _13.MVPRowMajor; + mat4 _61 = SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor); + mat4 _80 = SPIRV_Cross_Transpose(_13.MVPRowMajor) * 2.0; + mat4 _87 = SPIRV_Cross_Transpose(_61) * 2.0; + gl_Position = (((((((((((SPIRV_Cross_workaround_load_row_major(_13.M) * (Position * _13.MVPRowMajor)) + (SPIRV_Cross_workaround_load_row_major(_13.M) * (SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor) * Position))) + (SPIRV_Cross_workaround_load_row_major(_13.M) * (_13.MVPRowMajor * Position))) + (SPIRV_Cross_workaround_load_row_major(_13.M) * (Position * SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor)))) + (_55 * Position)) + (Position * _61)) + (Position * _55)) + (_61 * Position)) + (_80 * Position)) + (_87 * Position)) + (Position * _80)) + (Position * _87); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tesc/basic.tesc b/third_party/spirv-cross/reference/opt/shaders/tesc/basic.tesc new file mode 100644 index 0000000..6019151 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tesc/basic.tesc @@ -0,0 +1,17 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(vertices = 1) out; + +layout(location = 0) patch out vec3 vFoo; + +void main() +{ + gl_TessLevelInner[0] = 8.8999996185302734375; + gl_TessLevelInner[1] = 6.900000095367431640625; + gl_TessLevelOuter[0] = 8.8999996185302734375; + gl_TessLevelOuter[1] = 6.900000095367431640625; + gl_TessLevelOuter[2] = 3.900000095367431640625; + gl_TessLevelOuter[3] = 4.900000095367431640625; + vFoo = vec3(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tesc/water_tess.tesc b/third_party/spirv-cross/reference/opt/shaders/tesc/water_tess.tesc new file mode 100644 index 0000000..79da68b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tesc/water_tess.tesc @@ -0,0 +1,79 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(vertices = 1) out; + +layout(binding = 0, std140) uniform UBO +{ + vec4 uScale; + vec3 uCamPos; + vec2 uPatchSize; + vec2 uMaxTessLevel; + float uDistanceMod; + vec4 uFrustum[6]; +} _41; + +layout(location = 1) patch out vec2 vOutPatchPosBase; +layout(location = 2) patch out vec4 vPatchLods; +layout(location = 0) in vec2 vPatchPosBase[]; + +void main() +{ + vec2 _431 = (vPatchPosBase[0] - vec2(10.0)) * _41.uScale.xy; + vec2 _441 = ((vPatchPosBase[0] + _41.uPatchSize) + vec2(10.0)) * _41.uScale.xy; + vec3 _446 = vec3(_431.x, -10.0, _431.y); + vec3 _451 = vec3(_441.x, 10.0, _441.y); + vec4 _467 = vec4((_446 + _451) * 0.5, 1.0); + vec3 _514 = vec3(length(_451 - _446) * (-0.5)); + bool _516 = any(lessThanEqual(vec3(dot(_41.uFrustum[0], _467), dot(_41.uFrustum[1], _467), dot(_41.uFrustum[2], _467)), _514)); + bool _526; + if (!_516) + { + _526 = any(lessThanEqual(vec3(dot(_41.uFrustum[3], _467), dot(_41.uFrustum[4], _467), dot(_41.uFrustum[5], _467)), _514)); + } + else + { + _526 = _516; + } + if (!(!_526)) + { + gl_TessLevelOuter[0] = -1.0; + gl_TessLevelOuter[1] = -1.0; + gl_TessLevelOuter[2] = -1.0; + gl_TessLevelOuter[3] = -1.0; + gl_TessLevelInner[0] = -1.0; + gl_TessLevelInner[1] = -1.0; + } + else + { + vOutPatchPosBase = vPatchPosBase[0]; + vec2 _681 = (vPatchPosBase[0] + (vec2(-0.5) * _41.uPatchSize)) * _41.uScale.xy; + vec2 _710 = (vPatchPosBase[0] + (vec2(0.5, -0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _729 = clamp(log2((length(_41.uCamPos - vec3(_710.x, 0.0, _710.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + vec2 _739 = (vPatchPosBase[0] + (vec2(1.5, -0.5) * _41.uPatchSize)) * _41.uScale.xy; + vec2 _768 = (vPatchPosBase[0] + (vec2(-0.5, 0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _787 = clamp(log2((length(_41.uCamPos - vec3(_768.x, 0.0, _768.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + vec2 _797 = (vPatchPosBase[0] + (vec2(0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _816 = clamp(log2((length(_41.uCamPos - vec3(_797.x, 0.0, _797.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + vec2 _826 = (vPatchPosBase[0] + (vec2(1.5, 0.5) * _41.uPatchSize)) * _41.uScale.xy; + float _845 = clamp(log2((length(_41.uCamPos - vec3(_826.x, 0.0, _826.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + vec2 _855 = (vPatchPosBase[0] + (vec2(-0.5, 1.5) * _41.uPatchSize)) * _41.uScale.xy; + vec2 _884 = (vPatchPosBase[0] + (vec2(0.5, 1.5) * _41.uPatchSize)) * _41.uScale.xy; + float _903 = clamp(log2((length(_41.uCamPos - vec3(_884.x, 0.0, _884.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x); + vec2 _913 = (vPatchPosBase[0] + (vec2(1.5) * _41.uPatchSize)) * _41.uScale.xy; + float _614 = dot(vec4(_787, _816, clamp(log2((length(_41.uCamPos - vec3(_855.x, 0.0, _855.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _903), vec4(0.25)); + float _620 = dot(vec4(clamp(log2((length(_41.uCamPos - vec3(_681.x, 0.0, _681.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _729, _787, _816), vec4(0.25)); + float _626 = dot(vec4(_729, clamp(log2((length(_41.uCamPos - vec3(_739.x, 0.0, _739.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x), _816, _845), vec4(0.25)); + float _632 = dot(vec4(_816, _845, _903, clamp(log2((length(_41.uCamPos - vec3(_913.x, 0.0, _913.y)) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod), 0.0, _41.uMaxTessLevel.x)), vec4(0.25)); + vec4 _633 = vec4(_614, _620, _626, _632); + vPatchLods = _633; + vec4 _940 = exp2(-min(_633, _633.yzwx)) * _41.uMaxTessLevel.y; + gl_TessLevelOuter[0] = _940.x; + gl_TessLevelOuter[1] = _940.y; + gl_TessLevelOuter[2] = _940.z; + gl_TessLevelOuter[3] = _940.w; + float _948 = _41.uMaxTessLevel.y * exp2(-min(min(min(_614, _620), min(_626, _632)), _816)); + gl_TessLevelInner[0] = _948; + gl_TessLevelInner[1] = _948; + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/ccw.tese b/third_party/spirv-cross/reference/opt/shaders/tese/ccw.tese new file mode 100644 index 0000000..a2a4508 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/ccw.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, ccw, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/cw.tese b/third_party/spirv-cross/reference/opt/shaders/tese/cw.tese new file mode 100644 index 0000000..9578149 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/cw.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/equal.tese b/third_party/spirv-cross/reference/opt/shaders/tese/equal.tese new file mode 100644 index 0000000..6d30518 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/equal.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, equal_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/fractional_even.tese b/third_party/spirv-cross/reference/opt/shaders/tese/fractional_even.tese new file mode 100644 index 0000000..9578149 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/fractional_even.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/fractional_odd.tese b/third_party/spirv-cross/reference/opt/shaders/tese/fractional_odd.tese new file mode 100644 index 0000000..608c19a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/fractional_odd.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, fractional_odd_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/input-array.tese b/third_party/spirv-cross/reference/opt/shaders/tese/input-array.tese new file mode 100644 index 0000000..8a1aaf9 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/input-array.tese @@ -0,0 +1,11 @@ +#version 450 +layout(quads, ccw, fractional_odd_spacing) in; + +layout(location = 0) in vec4 Floats[]; +layout(location = 2) in vec4 Floats2[]; + +void main() +{ + gl_Position = (Floats[0] * gl_TessCoord.x) + (Floats2[1] * gl_TessCoord.y); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/line.tese b/third_party/spirv-cross/reference/opt/shaders/tese/line.tese new file mode 100644 index 0000000..8b6ad8d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/line.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(isolines, point_mode, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/load-array-of-array.tese b/third_party/spirv-cross/reference/opt/shaders/tese/load-array-of-array.tese new file mode 100644 index 0000000..e4b426d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/load-array-of-array.tese @@ -0,0 +1,10 @@ +#version 450 +layout(quads, ccw, equal_spacing) in; + +layout(location = 0) in vec4 vTexCoord[][1]; + +void main() +{ + gl_Position = (vTexCoord[0u][0] + vTexCoord[2u][0]) + vTexCoord[3u][0]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/patch-input-array.tese b/third_party/spirv-cross/reference/opt/shaders/tese/patch-input-array.tese new file mode 100644 index 0000000..413d8b3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/patch-input-array.tese @@ -0,0 +1,10 @@ +#version 450 +layout(quads, ccw, equal_spacing) in; + +layout(location = 0) patch in float P[4]; + +void main() +{ + gl_Position = vec4(P[0], P[1], P[2], P[3]); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/triangle.tese b/third_party/spirv-cross/reference/opt/shaders/tese/triangle.tese new file mode 100644 index 0000000..9578149 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/triangle.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/tese/water_tess.tese b/third_party/spirv-cross/reference/opt/shaders/tese/water_tess.tese new file mode 100644 index 0000000..6eb0d1a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/tese/water_tess.tese @@ -0,0 +1,36 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(quads, cw, fractional_even_spacing) in; + +layout(binding = 1, std140) uniform UBO +{ + mat4 uMVP; + vec4 uScale; + vec2 uInvScale; + vec3 uCamPos; + vec2 uPatchSize; + vec2 uInvHeightmapSize; +} _31; + +layout(binding = 0) uniform mediump sampler2D uHeightmapDisplacement; + +layout(location = 0) patch in vec2 vOutPatchPosBase; +layout(location = 1) patch in vec4 vPatchLods; +layout(location = 1) out vec4 vGradNormalTex; +layout(location = 0) out vec3 vWorld; + +void main() +{ + vec2 _202 = vOutPatchPosBase + (gl_TessCoord.xy * _31.uPatchSize); + vec2 _216 = mix(vPatchLods.yx, vPatchLods.zw, vec2(gl_TessCoord.x)); + float _223 = mix(_216.x, _216.y, gl_TessCoord.y); + mediump float _225 = floor(_223); + vec2 _125 = _202 * _31.uInvHeightmapSize; + vec2 _141 = _31.uInvHeightmapSize * exp2(_225); + vGradNormalTex = vec4(_125 + (_31.uInvHeightmapSize * 0.5), _125 * _31.uScale.zw); + mediump vec3 _256 = mix(textureLod(uHeightmapDisplacement, _125 + (_141 * 0.5), _225).xyz, textureLod(uHeightmapDisplacement, _125 + (_141 * 1.0), _225 + 1.0).xyz, vec3(_223 - _225)); + vec2 _171 = (_202 * _31.uScale.xy) + _256.yz; + vWorld = vec3(_171.x, _256.x, _171.y); + gl_Position = _31.uMVP * vec4(vWorld, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/basic.vert b/third_party/spirv-cross/reference/opt/shaders/vert/basic.vert new file mode 100644 index 0000000..8f251cb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/basic.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(binding = 0, std140) uniform UBO +{ + mat4 uMVP; +} _16; + +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec3 vNormal; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = _16.uMVP * aVertex; + vNormal = aNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/ground.vert b/third_party/spirv-cross/reference/opt/shaders/vert/ground.vert new file mode 100644 index 0000000..d938357 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/ground.vert @@ -0,0 +1,94 @@ +#version 310 es +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +struct PatchData +{ + vec4 Position; + vec4 LODs; +}; + +layout(binding = 0, std140) uniform PerPatch +{ + PatchData Patches[256]; +} _53; + +layout(binding = 2, std140) uniform GlobalGround +{ + vec4 GroundScale; + vec4 GroundPosition; + vec4 InvGroundSize_PatchScale; +} _156; + +layout(binding = 0, std140) uniform GlobalVSData +{ + vec4 g_ViewProj_Row0; + vec4 g_ViewProj_Row1; + vec4 g_ViewProj_Row2; + vec4 g_ViewProj_Row3; + vec4 g_CamPos; + vec4 g_CamRight; + vec4 g_CamUp; + vec4 g_CamFront; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_TimeParams; + vec4 g_ResolutionParams; + vec4 g_CamAxisRight; + vec4 g_FogColor_Distance; + vec4 g_ShadowVP_Row0; + vec4 g_ShadowVP_Row1; + vec4 g_ShadowVP_Row2; + vec4 g_ShadowVP_Row3; +} _236; + +layout(binding = 1) uniform mediump sampler2D TexLOD; +layout(binding = 0) uniform mediump sampler2D TexHeightmap; + +layout(location = 1) in vec4 LODWeights; +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +layout(location = 0) in vec2 Position; +layout(location = 1) out vec3 EyeVec; +layout(location = 0) out vec2 TexCoord; + +void main() +{ + float _301 = all(equal(LODWeights, vec4(0.0))) ? _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].Position.w : dot(LODWeights, _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].LODs); + float _303 = floor(_301); + uint _308 = uint(_303); + uvec2 _310 = uvec2(Position); + uvec2 _317 = (uvec2(1u) << uvec2(_308, _308 + 1u)) - uvec2(1u); + uint _384; + if (_310.x < 32u) + { + _384 = _317.x; + } + else + { + _384 = 0u; + } + uint _385; + if (_310.y < 32u) + { + _385 = _317.y; + } + else + { + _385 = 0u; + } + vec4 _345 = vec4((_310 + uvec2(_384, _385)).xyxy & (~_317).xxyy); + vec2 _173 = ((_53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].Position.xz * _156.InvGroundSize_PatchScale.zw) + mix(_345.xy, _345.zw, vec2(_301 - _303))) * _156.InvGroundSize_PatchScale.xy; + mediump float _362 = textureLod(TexLOD, _173, 0.0).x * 7.96875; + float _364 = floor(_362); + vec2 _185 = _156.InvGroundSize_PatchScale.xy * exp2(_364); + vec3 _230 = (vec3(_173.x, mix(textureLod(TexHeightmap, _173 + (_185 * 0.5), _364).x, textureLod(TexHeightmap, _173 + (_185 * 1.0), _364 + 1.0).x, _362 - _364), _173.y) * _156.GroundScale.xyz) + _156.GroundPosition.xyz; + EyeVec = _230 - _236.g_CamPos.xyz; + TexCoord = _173 + (_156.InvGroundSize_PatchScale.xy * 0.5); + gl_Position = (((_236.g_ViewProj_Row0 * _230.x) + (_236.g_ViewProj_Row1 * _230.y)) + (_236.g_ViewProj_Row2 * _230.z)) + _236.g_ViewProj_Row3; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/invariant.vert b/third_party/spirv-cross/reference/opt/shaders/vert/invariant.vert new file mode 100644 index 0000000..648ea29 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/invariant.vert @@ -0,0 +1,19 @@ +#version 310 es + +invariant gl_Position; + +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; +layout(location = 2) in vec4 vInput2; +layout(location = 0) invariant out vec4 vColor; + +void main() +{ + vec4 _20 = vInput1 * vInput2; + vec4 _21 = vInput0 + _20; + gl_Position = _21; + vec4 _27 = vInput0 - vInput1; + vec4 _29 = _27 * vInput2; + vColor = _29; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/ocean.vert b/third_party/spirv-cross/reference/opt/shaders/vert/ocean.vert new file mode 100644 index 0000000..9656938 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/ocean.vert @@ -0,0 +1,126 @@ +#version 310 es +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +struct PatchData +{ + vec4 Position; + vec4 LODs; +}; + +layout(binding = 0, std140) uniform Offsets +{ + PatchData Patches[256]; +} _53; + +layout(binding = 4, std140) uniform GlobalOcean +{ + vec4 OceanScale; + vec4 OceanPosition; + vec4 InvOceanSize_PatchScale; + vec4 NormalTexCoordScale; +} _180; + +layout(binding = 0, std140) uniform GlobalVSData +{ + vec4 g_ViewProj_Row0; + vec4 g_ViewProj_Row1; + vec4 g_ViewProj_Row2; + vec4 g_ViewProj_Row3; + vec4 g_CamPos; + vec4 g_CamRight; + vec4 g_CamUp; + vec4 g_CamFront; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_TimeParams; + vec4 g_ResolutionParams; + vec4 g_CamAxisRight; + vec4 g_FogColor_Distance; + vec4 g_ShadowVP_Row0; + vec4 g_ShadowVP_Row1; + vec4 g_ShadowVP_Row2; + vec4 g_ShadowVP_Row3; +} _273; + +layout(binding = 1) uniform mediump sampler2D TexLOD; +layout(binding = 0) uniform mediump sampler2D TexDisplacement; + +layout(location = 1) in vec4 LODWeights; +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +layout(location = 0) in vec4 Position; +layout(location = 0) out vec3 EyeVec; +layout(location = 1) out vec4 TexCoord; + +uvec4 _476; + +void main() +{ + float _351 = all(equal(LODWeights, vec4(0.0))) ? _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].Position.w : dot(LODWeights, _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].LODs); + float _353 = floor(_351); + uint _358 = uint(_353); + uvec4 _360 = uvec4(Position); + uvec2 _367 = (uvec2(1u) << uvec2(_358, _358 + 1u)) - uvec2(1u); + bool _370 = _360.x < 32u; + uint _467; + if (_370) + { + _467 = _367.x; + } + else + { + _467 = 0u; + } + uvec4 _445 = _476; + _445.x = _467; + bool _380 = _360.y < 32u; + uint _470; + if (_380) + { + _470 = _367.x; + } + else + { + _470 = 0u; + } + uvec4 _449 = _445; + _449.y = _470; + uint _472; + if (_370) + { + _472 = _367.y; + } + else + { + _472 = 0u; + } + uvec4 _453 = _449; + _453.z = _472; + uint _474; + if (_380) + { + _474 = _367.y; + } + else + { + _474 = 0u; + } + uvec4 _457 = _453; + _457.w = _474; + vec4 _416 = vec4((_360.xyxy + _457) & (~_367).xxyy); + vec2 _197 = ((_53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].Position.xz * _180.InvOceanSize_PatchScale.zw) + mix(_416.xy, _416.zw, vec2(_351 - _353))) * _180.InvOceanSize_PatchScale.xy; + vec2 _204 = _197 * _180.NormalTexCoordScale.zw; + mediump float _433 = textureLod(TexLOD, _197, 0.0).x * 7.96875; + float _435 = floor(_433); + vec2 _220 = (_180.InvOceanSize_PatchScale.xy * exp2(_435)) * _180.NormalTexCoordScale.zw; + vec3 _267 = ((vec3(_197.x, 0.0, _197.y) + mix(textureLod(TexDisplacement, _204 + (_220 * 0.5), _435).yxz, textureLod(TexDisplacement, _204 + (_220 * 1.0), _435 + 1.0).yxz, vec3(_433 - _435))) * _180.OceanScale.xyz) + _180.OceanPosition.xyz; + EyeVec = _267 - _273.g_CamPos.xyz; + TexCoord = vec4(_204, _204 * _180.NormalTexCoordScale.xy) + ((_180.InvOceanSize_PatchScale.xyxy * 0.5) * _180.NormalTexCoordScale.zwzw); + gl_Position = (((_273.g_ViewProj_Row0 * _267.x) + (_273.g_ViewProj_Row1 * _267.y)) + (_273.g_ViewProj_Row2 * _267.z)) + _273.g_ViewProj_Row3; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/read-from-row-major-array.vert b/third_party/spirv-cross/reference/opt/shaders/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..6c31cae --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/read-from-row-major-array.vert @@ -0,0 +1,18 @@ +#version 310 es + +layout(binding = 0, std140) uniform Block +{ + layout(row_major) mat2x3 var[3][4]; +} _104; + +layout(location = 0) in vec4 a_position; +layout(location = 0) out mediump float v_vtxResult; + +mat2x3 SPIRV_Cross_workaround_load_row_major(mat2x3 wrap) { return wrap; } + +void main() +{ + gl_Position = a_position; + v_vtxResult = ((float(abs(SPIRV_Cross_workaround_load_row_major(_104.var[0][0])[0].x - 2.0) < 0.0500000007450580596923828125) * float(abs(SPIRV_Cross_workaround_load_row_major(_104.var[0][0])[0].y - 6.0) < 0.0500000007450580596923828125)) * float(abs(SPIRV_Cross_workaround_load_row_major(_104.var[0][0])[0].z - (-6.0)) < 0.0500000007450580596923828125)) * ((float(abs(SPIRV_Cross_workaround_load_row_major(_104.var[0][0])[1].x) < 0.0500000007450580596923828125) * float(abs(SPIRV_Cross_workaround_load_row_major(_104.var[0][0])[1].y - 5.0) < 0.0500000007450580596923828125)) * float(abs(SPIRV_Cross_workaround_load_row_major(_104.var[0][0])[1].z - 5.0) < 0.0500000007450580596923828125)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/return-array.vert b/third_party/spirv-cross/reference/opt/shaders/vert/return-array.vert new file mode 100644 index 0000000..b78ca5f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/return-array.vert @@ -0,0 +1,9 @@ +#version 310 es + +layout(location = 1) in vec4 vInput1; + +void main() +{ + gl_Position = vec4(10.0) + vInput1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/texture_buffer.vert b/third_party/spirv-cross/reference/opt/shaders/vert/texture_buffer.vert new file mode 100644 index 0000000..e9442ce --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/texture_buffer.vert @@ -0,0 +1,11 @@ +#version 310 es +#extension GL_OES_texture_buffer : require + +layout(binding = 4) uniform highp samplerBuffer uSamp; +layout(binding = 5, rgba32f) uniform readonly highp imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/transform-feedback-decorations.vert b/third_party/spirv-cross/reference/opt/shaders/vert/transform-feedback-decorations.vert new file mode 100644 index 0000000..23e7cf3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/transform-feedback-decorations.vert @@ -0,0 +1,22 @@ +#version 450 + +layout(xfb_buffer = 1, xfb_stride = 20) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(location = 0, xfb_buffer = 2, xfb_stride = 32, xfb_offset = 16) out vec4 vFoo; +layout(xfb_buffer = 3, xfb_stride = 16) out VertOut +{ + layout(location = 1, xfb_offset = 0) vec4 vBar; +} _22; + + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + _22.vBar = vec4(5.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vert/ubo.vert b/third_party/spirv-cross/reference/opt/shaders/vert/ubo.vert new file mode 100644 index 0000000..4e7236b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vert/ubo.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(binding = 0, std140) uniform UBO +{ + mat4 mvp; +} _16; + +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec3 vNormal; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = _16.mvp * aVertex; + vNormal = aNormal; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp.vk new file mode 100644 index 0000000..82ebb96 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp.vk @@ -0,0 +1,25 @@ +#version 450 +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(buffer_reference) buffer Block; +layout(buffer_reference, std430) buffer Block +{ + float v; +}; + +layout(set = 0, binding = 0, std140) uniform UBO +{ + Block blocks[4]; +} ubo; + +void main() +{ + Block blocks[4]; + blocks[0] = ubo.blocks[0]; + blocks[1] = ubo.blocks[1]; + blocks[2] = ubo.blocks[2]; + blocks[3] = ubo.blocks[3]; + blocks[gl_WorkGroupID.x].v = 20.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp.vk new file mode 100644 index 0000000..5752f81 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp.vk @@ -0,0 +1,26 @@ +#version 450 +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(buffer_reference) buffer PtrUint; +layout(buffer_reference) buffer PtrInt; +layout(buffer_reference, std430) buffer PtrUint +{ + uint value; +}; + +layout(buffer_reference, std430) buffer PtrInt +{ + int value; +}; + +layout(set = 0, binding = 0, std430) buffer Buf +{ + PtrUint ptr; +} _11; + +void main() +{ + PtrInt(_11.ptr).value = 10; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp.vk new file mode 100644 index 0000000..b30330c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp.vk @@ -0,0 +1,35 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(buffer_reference) buffer Node; +layout(buffer_reference, std430) buffer Node +{ + layout(offset = 0) int value; + layout(offset = 16) Node next; + layout(offset = 32) Node prev; +}; + +layout(set = 0, binding = 0, std430) restrict buffer LinkedList +{ + Node head1; + Node head2; +} _50; + +void main() +{ + Node _114; + if (gl_WorkGroupID.x < 4u) + { + _114 = _50.head1; + } + else + { + _114 = _50.head2; + } + _114.next.value = _50.head1.value + _50.head2.value; + _50.head1.value = 20; + _50.head1.value = _50.head2.value * 10; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp new file mode 100644 index 0000000..7c4c7ed --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp @@ -0,0 +1,47 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 100 +#endif +const int a = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 200 +#endif +const int b = SPIRV_CROSS_CONSTANT_ID_1; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 300 +#endif +const int c = SPIRV_CROSS_CONSTANT_ID_2; +const int d = (c + 50); +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 400 +#endif +const int e = SPIRV_CROSS_CONSTANT_ID_3; + +layout(binding = 0, std430) buffer SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +} _22; + +void main() +{ + _22.w[gl_GlobalInvocationID.x] += (_22.v[gl_GlobalInvocationID.x] + e); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp.vk new file mode 100644 index 0000000..b7571b7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp.vk @@ -0,0 +1,35 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(constant_id = 0) const int a = 100; +layout(constant_id = 1) const int b = 200; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +layout(constant_id = 2) const int c = 300; +const int d = (c + 50); +layout(constant_id = 3) const int e = 400; + +layout(set = 1, binding = 0, std430) buffer SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +} _22; + +void main() +{ + _22.w[gl_GlobalInvocationID.x] += (_22.v[gl_GlobalInvocationID.x] + e); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp new file mode 100644 index 0000000..888f4b1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp @@ -0,0 +1,34 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 2 +#endif +const int b = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 1 +#endif +const int a = SPIRV_CROSS_CONSTANT_ID_0; +const uint _21 = (uint(a) + 0u); +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 1u +#endif +const uint _27 = gl_WorkGroupSize.x; +const uint _28 = (_21 + _27); +const uint _29 = gl_WorkGroupSize.y; +const uint _30 = (_28 + _29); +const int _32 = (1 - a); + +layout(local_size_x = SPIRV_CROSS_CONSTANT_ID_10, local_size_y = 20, local_size_z = 1) in; + +layout(binding = 0, std430) writeonly buffer SSBO +{ + int v[]; +} _17; + +void main() +{ + int spec_const_array_size[b]; + spec_const_array_size[a] = a; + _17.v[_30] = b + spec_const_array_size[_32]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp.vk new file mode 100644 index 0000000..bdf72df --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp.vk @@ -0,0 +1,24 @@ +#version 450 +layout(local_size_x_id = 10, local_size_y = 20, local_size_z = 1) in; + +layout(constant_id = 1) const int b = 2; +layout(constant_id = 0) const int a = 1; +const uint _21 = (uint(a) + 0u); +const uint _27 = gl_WorkGroupSize.x; +const uint _28 = (_21 + _27); +const uint _29 = gl_WorkGroupSize.y; +const uint _30 = (_28 + _29); +const int _32 = (1 - a); + +layout(set = 1, binding = 0, std430) writeonly buffer SSBO +{ + int v[]; +} _17; + +void main() +{ + int spec_const_array_size[b]; + spec_const_array_size[a] = a; + _17.v[_30] = b + spec_const_array_size[_32]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag new file mode 100644 index 0000000..f0729fd --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump sampler2DShadow SPIRV_Cross_CombineduDepthuSampler; +uniform mediump sampler2D SPIRV_Cross_CombineduDepthuSampler1; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(SPIRV_Cross_CombineduDepthuSampler, vec3(vec3(1.0).xy, 1.0)) + texture(SPIRV_Cross_CombineduDepthuSampler1, vec2(1.0)).x; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag.vk new file mode 100644 index 0000000..b6ad1e3 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag.vk @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(set = 0, binding = 2) uniform mediump texture2D uDepth; +layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(sampler2DShadow(uDepth, uSampler), vec3(vec3(1.0).xy, 1.0)) + texture(sampler2D(uDepth, uSampler1), vec2(1.0)).x; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler.vk.frag new file mode 100644 index 0000000..29c247d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler.vk.frag @@ -0,0 +1,17 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump sampler2D SPIRV_Cross_CombineduTexture0uSampler0; +uniform mediump sampler2D SPIRV_Cross_CombineduTexture1uSampler1; +uniform mediump sampler2D SPIRV_Cross_CombineduTexture1uSampler0; +uniform mediump sampler2D SPIRV_Cross_CombineduTexture0uSampler1; + +layout(location = 0) in vec2 vTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = ((((texture(SPIRV_Cross_CombineduTexture0uSampler0, vTex) + texture(SPIRV_Cross_CombineduTexture1uSampler1, vTex)) + (texture(SPIRV_Cross_CombineduTexture0uSampler0, vTex) + texture(SPIRV_Cross_CombineduTexture1uSampler0, vTex))) + (texture(SPIRV_Cross_CombineduTexture0uSampler1, vTex) + texture(SPIRV_Cross_CombineduTexture1uSampler1, vTex))) + (texture(SPIRV_Cross_CombineduTexture0uSampler0, vTex) + texture(SPIRV_Cross_CombineduTexture0uSampler1, vTex))) + (texture(SPIRV_Cross_CombineduTexture1uSampler0, vTex) + texture(SPIRV_Cross_CombineduTexture1uSampler1, vTex)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler.vk.frag.vk new file mode 100644 index 0000000..7a543c4 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/combined-texture-sampler.vk.frag.vk @@ -0,0 +1,17 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(set = 0, binding = 2) uniform mediump texture2D uTexture0; +layout(set = 0, binding = 3) uniform mediump texture2D uTexture1; +layout(set = 0, binding = 0) uniform mediump sampler uSampler0; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; + +layout(location = 0) in vec2 vTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = ((((texture(sampler2D(uTexture0, uSampler0), vTex) + texture(sampler2D(uTexture1, uSampler1), vTex)) + (texture(sampler2D(uTexture0, uSampler0), vTex) + texture(sampler2D(uTexture1, uSampler0), vTex))) + (texture(sampler2D(uTexture0, uSampler1), vTex) + texture(sampler2D(uTexture1, uSampler1), vTex))) + (texture(sampler2D(uTexture0, uSampler0), vTex) + texture(sampler2D(uTexture0, uSampler1), vTex))) + (texture(sampler2D(uTexture1, uSampler0), vTex) + texture(sampler2D(uTexture1, uSampler1), vTex)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag.vk new file mode 100644 index 0000000..1531649 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag.vk @@ -0,0 +1,15 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +layout(location = 0) out vec4 FragColor; + +void main() +{ + bool _15 = helperInvocationEXT(); + demote; + if (!_15) + { + FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag.vk new file mode 100644 index 0000000..688a580 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag.vk @@ -0,0 +1,9 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +void main() +{ + demote; + bool _9 = helperInvocationEXT(); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/desktop-mediump.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/desktop-mediump.vk.frag new file mode 100644 index 0000000..8f7508e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/desktop-mediump.vk.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 F; +layout(location = 1) flat in ivec4 I; +layout(location = 2) flat in uvec4 U; + +void main() +{ + FragColor = (F + vec4(I)) + vec4(U); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/desktop-mediump.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/desktop-mediump.vk.frag.vk new file mode 100644 index 0000000..4c0506b --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/desktop-mediump.vk.frag.vk @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out mediump vec4 FragColor; +layout(location = 0) in mediump vec4 F; +layout(location = 1) flat in mediump ivec4 I; +layout(location = 2) flat in mediump uvec4 U; + +void main() +{ + FragColor = (F + vec4(I)) + vec4(U); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment-ms.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment-ms.vk.frag new file mode 100644 index 0000000..ea460c1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment-ms.vk.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0) uniform sampler2DMS uSubpass0; +layout(binding = 1) uniform sampler2DMS uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = (texelFetch(uSubpass0, ivec2(gl_FragCoord.xy), 1) + texelFetch(uSubpass1, ivec2(gl_FragCoord.xy), 2)) + texelFetch(uSubpass0, ivec2(gl_FragCoord.xy), gl_SampleID); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk new file mode 100644 index 0000000..462df22 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk @@ -0,0 +1,12 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = (subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2)) + subpassLoad(uSubpass0, gl_SampleID); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment.vk.frag new file mode 100644 index 0000000..8d216b2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment.vk.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uSubpass0; +layout(binding = 1) uniform mediump sampler2D uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texelFetch(uSubpass0, ivec2(gl_FragCoord.xy), 0) + texelFetch(uSubpass1, ivec2(gl_FragCoord.xy), 0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment.vk.frag.vk new file mode 100644 index 0000000..c8b5d9a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/input-attachment.vk.frag.vk @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0) + subpassLoad(uSubpass1); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag.vk new file mode 100644 index 0000000..7662cd2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag.vk @@ -0,0 +1,34 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require + +layout(set = 0, binding = 2, std140) uniform UBO +{ + vec4 v[64]; +} ubos[]; + +layout(set = 0, binding = 3, std430) readonly buffer SSBO +{ + vec4 v[]; +} ssbos[]; + +layout(set = 0, binding = 0) uniform texture2D uSamplers[]; +layout(set = 0, binding = 1) uniform sampler uSamps[]; +layout(set = 0, binding = 4) uniform sampler2D uCombinedSamplers[]; + +layout(location = 0) flat in int vIndex; +layout(location = 0) out vec4 FragColor; +layout(location = 1) in vec2 vUV; + +void main() +{ + int _23 = vIndex + 10; + int _34 = vIndex + 40; + FragColor = texture(sampler2D(uSamplers[nonuniformEXT(_23)], uSamps[nonuniformEXT(_34)]), vUV); + FragColor = texture(uCombinedSamplers[nonuniformEXT(_23)], vUV); + int _66 = vIndex + 20; + FragColor += ubos[nonuniformEXT(_66)].v[_34]; + int _84 = vIndex + 50; + int _88 = vIndex + 60; + FragColor += ssbos[nonuniformEXT(_84)].v[_88]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag new file mode 100644 index 0000000..21618f8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag @@ -0,0 +1,14 @@ +#version 450 + +layout(std140) uniform UBO +{ + float ubo[4]; +} _14; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = _14.ubo[1]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag.vk new file mode 100644 index 0000000..8ca4f23 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(push_constant, std140) uniform UBO +{ + float ubo[4]; +} _14; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = _14.ubo[1]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant.vk.frag new file mode 100644 index 0000000..c04a7ca --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant.vk.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct PushConstants +{ + vec4 value0; + vec4 value1; +}; + +uniform PushConstants push; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = (vColor + push.value0) + push.value1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant.vk.frag.vk new file mode 100644 index 0000000..6cec90f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/push-constant.vk.frag.vk @@ -0,0 +1,18 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = (vColor + push.value0) + push.value1; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag new file mode 100644 index 0000000..c17c8e6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSamp; +uniform sampler2D SPIRV_Cross_CombineduTuS; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texture(uSamp, vec2(0.5)) + texture(SPIRV_Cross_CombineduTuS, vec2(0.5)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag.vk new file mode 100644 index 0000000..5a5ec20 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag.vk @@ -0,0 +1,13 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2D uSamp; +layout(set = 0, binding = 1) uniform texture2D uT; +layout(set = 0, binding = 2) uniform sampler uS; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texture(uSamp, vec2(0.5)) + texture(sampler2D(uT, uS), vec2(0.5)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag new file mode 100644 index 0000000..df2994e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump sampler2D SPIRV_Cross_CombineduTextureuSampler[4]; +uniform mediump sampler2DArray SPIRV_Cross_CombineduTextureArrayuSampler[4]; +uniform mediump samplerCube SPIRV_Cross_CombineduTextureCubeuSampler[4]; +uniform mediump sampler3D SPIRV_Cross_CombineduTexture3DuSampler[4]; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec2 _95 = (vTex + (vec2(1.0) / vec2(textureSize(SPIRV_Cross_CombineduTextureuSampler[1], 0)))) + (vec2(1.0) / vec2(textureSize(SPIRV_Cross_CombineduTextureuSampler[2], 1))); + FragColor = ((((texture(SPIRV_Cross_CombineduTextureuSampler[2], _95) + texture(SPIRV_Cross_CombineduTextureuSampler[1], _95)) + texture(SPIRV_Cross_CombineduTextureuSampler[1], _95)) + texture(SPIRV_Cross_CombineduTextureArrayuSampler[3], vTex3)) + texture(SPIRV_Cross_CombineduTextureCubeuSampler[1], vTex3)) + texture(SPIRV_Cross_CombineduTexture3DuSampler[2], vTex3); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag.vk new file mode 100644 index 0000000..d275a0f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag.vk @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(set = 0, binding = 1) uniform mediump texture2D uTexture[4]; +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray[4]; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube[4]; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D[4]; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec2 _95 = (vTex + (vec2(1.0) / vec2(textureSize(sampler2D(uTexture[1], uSampler), 0)))) + (vec2(1.0) / vec2(textureSize(sampler2D(uTexture[2], uSampler), 1))); + FragColor = ((((texture(sampler2D(uTexture[2], uSampler), _95) + texture(sampler2D(uTexture[1], uSampler), _95)) + texture(sampler2D(uTexture[1], uSampler), _95)) + texture(sampler2DArray(uTextureArray[3], uSampler), vTex3)) + texture(samplerCube(uTextureCube[1], uSampler), vTex3)) + texture(sampler3D(uTexture3D[2], uSampler), vTex3); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture.vk.frag new file mode 100644 index 0000000..aad1e43 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture.vk.frag @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump sampler2D SPIRV_Cross_CombineduTextureuSampler; +uniform mediump sampler2DArray SPIRV_Cross_CombineduTextureArrayuSampler; +uniform mediump samplerCube SPIRV_Cross_CombineduTextureCubeuSampler; +uniform mediump sampler3D SPIRV_Cross_CombineduTexture3DuSampler; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec2 _73 = (vTex + (vec2(1.0) / vec2(textureSize(SPIRV_Cross_CombineduTextureuSampler, 0)))) + (vec2(1.0) / vec2(textureSize(SPIRV_Cross_CombineduTextureuSampler, 1))); + FragColor = (((texture(SPIRV_Cross_CombineduTextureuSampler, _73) + texture(SPIRV_Cross_CombineduTextureuSampler, _73)) + texture(SPIRV_Cross_CombineduTextureArrayuSampler, vTex3)) + texture(SPIRV_Cross_CombineduTextureCubeuSampler, vTex3)) + texture(SPIRV_Cross_CombineduTexture3DuSampler, vTex3); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture.vk.frag.vk new file mode 100644 index 0000000..b79374a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/separate-sampler-texture.vk.frag.vk @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(set = 0, binding = 1) uniform mediump texture2D uTexture; +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec2 _73 = (vTex + (vec2(1.0) / vec2(textureSize(sampler2D(uTexture, uSampler), 0)))) + (vec2(1.0) / vec2(textureSize(sampler2D(uTexture, uSampler), 1))); + FragColor = (((texture(sampler2D(uTexture, uSampler), _73) + texture(sampler2D(uTexture, uSampler), _73)) + texture(sampler2DArray(uTextureArray, uSampler), vTex3)) + texture(samplerCube(uTextureCube, uSampler), vTex3)) + texture(sampler3D(uTexture3D, uSampler), vTex3); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag.vk new file mode 100644 index 0000000..dbc8073 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag.vk @@ -0,0 +1,43 @@ +#version 450 +#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require +#extension GL_EXT_shader_8bit_storage : require + +layout(set = 0, binding = 1, std430) buffer SSBO +{ + int8_t i8[16]; + uint8_t u8[16]; +} ssbo; + +layout(set = 0, binding = 0, std140) uniform UBO +{ + int8_t i8; + uint8_t u8; +} ubo; + +layout(push_constant, std430) uniform Push +{ + int8_t i8; + uint8_t u8; +} registers; + +layout(location = 0) flat in ivec4 vColor; +layout(location = 0) out ivec4 FragColorInt; +layout(location = 1) out uvec4 FragColorUint; + +void main() +{ + i8vec4 _199 = unpack8(20); + ssbo.i8[0] = _199.x; + ssbo.i8[1] = _199.y; + ssbo.i8[2] = _199.z; + ssbo.i8[3] = _199.w; + u8vec4 _224 = unpack8(20u); + ssbo.u8[0] = _224.x; + ssbo.u8[1] = _224.y; + ssbo.u8[2] = _224.z; + ssbo.u8[3] = _224.w; + i8vec4 _249 = i8vec4(vColor); + FragColorInt = ivec4((((((_249 + i8vec4(registers.i8)) + i8vec4(-40)) + i8vec4(-50)) + i8vec4(int8_t(10), int8_t(20), int8_t(30), int8_t(40))) + i8vec4(ssbo.i8[4])) + i8vec4(ubo.i8)); + FragColorUint = uvec4((((((u8vec4(_249) + u8vec4(registers.u8)) + u8vec4(216)) + u8vec4(206)) + u8vec4(uint8_t(10), uint8_t(20), uint8_t(30), uint8_t(40))) + u8vec4(ssbo.u8[4])) + u8vec4(ubo.u8)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-block-size.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-block-size.vk.frag new file mode 100644 index 0000000..19ea6ae --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-block-size.vk.frag @@ -0,0 +1,22 @@ +#version 310 es +precision mediump float; +precision highp int; + +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 2 +#endif +const int Value = SPIRV_CROSS_CONSTANT_ID_10; + +layout(binding = 0, std140) uniform SpecConstArray +{ + vec4 samples[Value]; +} _15; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int Index; + +void main() +{ + FragColor = _15.samples[Index]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-block-size.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-block-size.vk.frag.vk new file mode 100644 index 0000000..133761a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-block-size.vk.frag.vk @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(constant_id = 10) const int Value = 2; + +layout(set = 0, binding = 0, std140) uniform SpecConstArray +{ + vec4 samples[Value]; +} _15; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int Index; + +void main() +{ + FragColor = _15.samples[Index]; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000..081206f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,16 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 10u +#endif +const uint s = SPIRV_CROSS_CONSTANT_ID_0; +const bool _13 = (s > 20u); +const uint f = _13 ? 30u : 50u; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float(f); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk new file mode 100644 index 0000000..34bfea0 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk @@ -0,0 +1,13 @@ +#version 450 + +layout(constant_id = 0) const uint s = 10u; +const bool _13 = (s > 20u); +const uint f = _13 ? 30u : 50u; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float(f); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit.vk new file mode 100644 index 0000000..614a04d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit.vk @@ -0,0 +1,24 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Foo +{ + float a; + float b; +}; + +struct Foo2 +{ + float a; + float b; +}; + +layout(location = 0) rayPayloadInNV Foo payload; +hitAttributeNV Foo2 hit; + +void main() +{ + payload.a = hit.a; + payload.b = hit.b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit.vk new file mode 100644 index 0000000..614a04d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit.vk @@ -0,0 +1,24 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Foo +{ + float a; + float b; +}; + +struct Foo2 +{ + float a; + float b; +}; + +layout(location = 0) rayPayloadInNV Foo payload; +hitAttributeNV Foo2 hit; + +void main() +{ + payload.a = hit.a; + payload.b = hit.b; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit.vk new file mode 100644 index 0000000..908d963 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit.vk @@ -0,0 +1,11 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec2 payload; +hitAttributeNV vec2 hit; + +void main() +{ + payload = hit; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit.vk new file mode 100644 index 0000000..133bdfc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit.vk @@ -0,0 +1,17 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Foo +{ + float a; + float b; +}; + +layout(location = 0) rayPayloadInNV Foo payload; +hitAttributeNV Foo hit; + +void main() +{ + payload = hit; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit.vk new file mode 100644 index 0000000..64f79a8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_HitKindNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit.vk new file mode 100644 index 0000000..9004a00 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_HitTNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit.vk new file mode 100644 index 0000000..d17ab8c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_IncomingRayFlagsNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit.vk new file mode 100644 index 0000000..531a1fc --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = uint(gl_InstanceCustomIndexNV); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit.vk new file mode 100644 index 0000000..ff551db --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = uint(gl_InstanceID); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit.vk new file mode 100644 index 0000000..01afa0e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectRayDirectionNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit.vk new file mode 100644 index 0000000..a49e17a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectRayOriginNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit.vk new file mode 100644 index 0000000..fc2c5ed --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectToWorldNV * vec4(payload, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/payloads.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/payloads.nocompat.vk.rchit.vk new file mode 100644 index 0000000..6d865f7 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/payloads.nocompat.vk.rchit.vk @@ -0,0 +1,15 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Payload +{ + vec4 a; +}; + +layout(location = 0) rayPayloadInNV Payload payload; + +void main() +{ + payload.a = vec4(10.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit.vk new file mode 100644 index 0000000..d3b0ef1 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = uint(gl_PrimitiveID); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit.vk new file mode 100644 index 0000000..769c96a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_RayTmaxNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit.vk new file mode 100644 index 0000000..2709899 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_RayTminNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit.vk new file mode 100644 index 0000000..103fd66 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = 1.0 + float(gl_InstanceID); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit.vk new file mode 100644 index 0000000..4acf03e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldRayDirectionNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit.vk new file mode 100644 index 0000000..70241f2 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldRayOriginNV; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit.vk new file mode 100644 index 0000000..0b93e38 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldToObjectNV * vec4(payload, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen.vk new file mode 100644 index 0000000..926546c --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen.vk @@ -0,0 +1,15 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform accelerationStructureNV as; +layout(set = 0, binding = 1, rgba32f) uniform writeonly image2D image; +layout(location = 0) rayPayloadNV vec4 payload; +layout(location = 0) callableDataNV float blend; + +void main() +{ + traceNV(as, 1u, 255u, 0u, 0u, 0u, vec3(0.0), 0.0, vec3(0.0, 0.0, -1.0), 100.0, 0); + executeCallableNV(0u, 0); + imageStore(image, ivec2(gl_LaunchIDNV.xy), payload + vec4(blend)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen.vk new file mode 100644 index 0000000..f907e6f --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform writeonly image2D uImage; + +void main() +{ + imageStore(uImage, ivec2(gl_LaunchIDNV.xy), vec4(1.0)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen.vk new file mode 100644 index 0000000..08992c6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform writeonly image2D uImage; + +void main() +{ + imageStore(uImage, ivec2(gl_LaunchSizeNV.xy) - ivec2(1), vec4(1.0)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/payloads.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/payloads.nocompat.vk.rgen.vk new file mode 100644 index 0000000..8212fa6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/payloads.nocompat.vk.rgen.vk @@ -0,0 +1,31 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Payload +{ + float a; + float b; +}; + +struct Block +{ + float a; + float b; + Payload c; + Payload d; +}; + +layout(set = 0, binding = 1) uniform accelerationStructureNV as; +layout(location = 1) rayPayloadNV Payload payload2; +layout(location = 0) rayPayloadNV float payload1; +layout(location = 2) rayPayloadNV Block _71; +layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image; + +void main() +{ + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(1.0, 0.0, 0.0), 0.0, vec3(0.0, 1.0, 0.0), 1000.0, 0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(1.0, 0.0, 0.0), 0.0, vec3(0.0, 1.0, 0.0), 1000.0, 1); + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(1.0, 0.0, 0.0), 0.0, vec3(0.0, 1.0, 0.0), 1000.0, 2); + imageStore(image, ivec2(gl_LaunchIDNV.xy), (vec4(payload1) + (vec4(payload2.a) + vec4(payload2.b))) + vec4(((((_71.a + _71.b) + _71.c.a) + _71.c.b) + _71.d.a) + _71.d.b)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen.vk new file mode 100644 index 0000000..31603fe --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen.vk @@ -0,0 +1,12 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 1) uniform accelerationStructureNV as; + +void main() +{ + vec2 _55 = vec2(gl_LaunchIDNV.xy); + vec2 _59 = vec2(gl_LaunchSizeNV.xy); + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(_55.x / _59.x, _55.y / _59.y, 1.0), 0.0, vec3(0.0, 0.0, -1.0), 1000.0, 0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen.vk new file mode 100644 index 0000000..b39ea89 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen.vk @@ -0,0 +1,15 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 1) uniform accelerationStructureNV as; +layout(location = 0) rayPayloadNV float payload; +layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image; + +void main() +{ + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(float(gl_LaunchIDNV.x) / float(gl_LaunchSizeNV.x), float(gl_LaunchIDNV.y) / float(gl_LaunchSizeNV.y), 1.0), 0.0, vec3(0.0, 0.0, -1.0), 1000.0, 0); + vec4 _68 = vec4(0.0, 0.0, 0.0, 1.0); + _68.y = payload; + imageStore(image, ivec2(gl_LaunchIDNV.xy), _68); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen.vk new file mode 100644 index 0000000..daa9c3e --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen.vk @@ -0,0 +1,16 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(shaderRecordNV, std430) buffer sbt +{ + vec3 direction; + float tmax; +} _20; + +layout(set = 0, binding = 0) uniform accelerationStructureNV as; + +void main() +{ + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(0.0), 0.0, _20.direction, _20.tmax, 0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss.vk new file mode 100644 index 0000000..ec3072a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = 0.0; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/device-group.nocompat.vk.vert.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/device-group.nocompat.vk.vert.vk new file mode 100644 index 0000000..9cadcdb --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/device-group.nocompat.vk.vert.vk @@ -0,0 +1,8 @@ +#version 450 +#extension GL_EXT_device_group : require + +void main() +{ + gl_Position = vec4(float(gl_DeviceIndex)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/multiview.nocompat.vk.vert.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/multiview.nocompat.vk.vert.vk new file mode 100644 index 0000000..9005547 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/multiview.nocompat.vk.vert.vk @@ -0,0 +1,15 @@ +#version 310 es +#extension GL_EXT_multiview : require + +layout(set = 0, binding = 0, std140) uniform MVPs +{ + mat4 MVP[2]; +} _19; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = _19.MVP[gl_ViewIndex] * Position; +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/small-storage.vk.vert b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/small-storage.vk.vert new file mode 100644 index 0000000..b3aafc8 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/small-storage.vk.vert @@ -0,0 +1,59 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_int16) +#extension GL_AMD_gpu_shader_int16 : require +#else +#error No extension available for Int16. +#endif +#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif + +layout(binding = 0, std140) uniform block +{ + i16vec2 a; + u16vec2 b; + i8vec2 c; + u8vec2 d; + f16vec2 e; +} _26; + +layout(binding = 1, std430) readonly buffer storage +{ + i16vec3 f; + u16vec3 g; + i8vec3 h; + u8vec3 i; + f16vec3 j; +} _53; + +struct pushconst +{ + i16vec4 k; + u16vec4 l; + i8vec4 m; + u8vec4 n; + f16vec4 o; +}; + +uniform pushconst _76; + +layout(location = 0) out i16vec4 p; +layout(location = 0, component = 0) in int16_t foo; +layout(location = 1) out u16vec4 q; +layout(location = 0, component = 1) in uint16_t bar; +layout(location = 2) out f16vec4 r; +layout(location = 1) in float16_t baz; + +void main() +{ + p = i16vec4((((ivec4(int(foo)) + ivec4(ivec2(_26.a), ivec2(_26.c))) - ivec4(ivec3(_53.f) / ivec3(_53.h), 1)) + ivec4(_76.k)) + ivec4(_76.m)); + q = u16vec4((((uvec4(uint(bar)) + uvec4(uvec2(_26.b), uvec2(_26.d))) - uvec4(uvec3(_53.g) / uvec3(_53.i), 1u)) + uvec4(_76.l)) + uvec4(_76.n)); + r = f16vec4(((vec4(float(baz)) + vec4(vec2(_26.e), 0.0, 1.0)) - vec4(vec3(_53.j), 1.0)) + vec4(_76.o)); + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/small-storage.vk.vert.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/small-storage.vk.vert.vk new file mode 100644 index 0000000..caec60a --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/small-storage.vk.vert.vk @@ -0,0 +1,55 @@ +#version 450 +#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require +#extension GL_EXT_shader_16bit_storage : require +#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require +#extension GL_EXT_shader_8bit_storage : require +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_EXT_shader_explicit_arithmetic_types_float16) +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require +#else +#error No extension available for FP16. +#endif + +layout(set = 0, binding = 0, std140) uniform block +{ + i16vec2 a; + u16vec2 b; + i8vec2 c; + u8vec2 d; + f16vec2 e; +} _26; + +layout(set = 0, binding = 1, std430) readonly buffer storage +{ + i16vec3 f; + u16vec3 g; + i8vec3 h; + u8vec3 i; + f16vec3 j; +} _53; + +layout(push_constant, std430) uniform pushconst +{ + i16vec4 k; + u16vec4 l; + i8vec4 m; + u8vec4 n; + f16vec4 o; +} _76; + +layout(location = 0) out i16vec4 p; +layout(location = 0, component = 0) in int16_t foo; +layout(location = 1) out u16vec4 q; +layout(location = 0, component = 1) in uint16_t bar; +layout(location = 2) out f16vec4 r; +layout(location = 1) in float16_t baz; + +void main() +{ + p = i16vec4((((ivec4(int(foo)) + ivec4(ivec2(_26.a), ivec2(_26.c))) - ivec4(ivec3(_53.f) / ivec3(_53.h), 1)) + ivec4(_76.k)) + ivec4(_76.m)); + q = u16vec4((((uvec4(uint(bar)) + uvec4(uvec2(_26.b), uvec2(_26.d))) - uvec4(uvec3(_53.g) / uvec3(_53.i), 1u)) + uvec4(_76.l)) + uvec4(_76.n)); + r = f16vec4(((vec4(float(baz)) + vec4(vec2(_26.e), 0.0, 1.0)) - vec4(vec3(_53.j), 1.0)) + vec4(_76.o)); + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/vulkan-vertex.vk.vert b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/vulkan-vertex.vk.vert new file mode 100644 index 0000000..d939aa6 --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/vulkan-vertex.vk.vert @@ -0,0 +1,16 @@ +#version 310 es +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif + +void main() +{ + gl_Position = vec4(1.0, 2.0, 3.0, 4.0) * float(gl_VertexID + (gl_InstanceID + SPIRV_Cross_BaseInstance)); +} + diff --git a/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/vulkan-vertex.vk.vert.vk b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/vulkan-vertex.vk.vert.vk new file mode 100644 index 0000000..8c4930d --- /dev/null +++ b/third_party/spirv-cross/reference/opt/shaders/vulkan/vert/vulkan-vertex.vk.vert.vk @@ -0,0 +1,7 @@ +#version 310 es + +void main() +{ + gl_Position = vec4(1.0, 2.0, 3.0, 4.0) * float(gl_VertexIndex + gl_InstanceIndex); +} + diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/access-chain-load-store-composite.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/access-chain-load-store-composite.asm.comp new file mode 100644 index 0000000..986cc62 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/access-chain-load-store-composite.asm.comp @@ -0,0 +1,75 @@ +struct Baz +{ + float c; +}; + +struct Bar +{ + float d[2][4]; + Baz baz[2]; +}; + +struct Foo +{ + column_major float2x2 a; + float2 b; + Bar c[5]; +}; + +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _10 : register(u0); + +void comp_main() +{ + Foo _64; + _64.a = asfloat(uint2x2(_10.Load(0), _10.Load(8), _10.Load(4), _10.Load(12))); + _64.b = asfloat(_10.Load2(16)); + [unroll] + for (int _0ident = 0; _0ident < 5; _0ident++) + { + [unroll] + for (int _1ident = 0; _1ident < 2; _1ident++) + { + [unroll] + for (int _2ident = 0; _2ident < 4; _2ident++) + { + _64.c[_0ident].d[_1ident][_2ident] = asfloat(_10.Load(_2ident * 4 + _1ident * 16 + _0ident * 40 + 24)); + } + } + [unroll] + for (int _3ident = 0; _3ident < 2; _3ident++) + { + _64.c[_0ident].baz[_3ident].c = asfloat(_10.Load(_3ident * 4 + _0ident * 40 + 56)); + } + } + _10.Store(224, asuint(_64.a[0].x)); + _10.Store(228, asuint(_64.a[1].x)); + _10.Store(232, asuint(_64.a[0].y)); + _10.Store(236, asuint(_64.a[1].y)); + _10.Store2(240, asuint(_64.b)); + [unroll] + for (int _4ident = 0; _4ident < 5; _4ident++) + { + [unroll] + for (int _5ident = 0; _5ident < 2; _5ident++) + { + [unroll] + for (int _6ident = 0; _6ident < 4; _6ident++) + { + _10.Store(_6ident * 4 + _5ident * 16 + _4ident * 40 + 248, asuint(_64.c[_4ident].d[_5ident][_6ident])); + } + } + [unroll] + for (int _7ident = 0; _7ident < 2; _7ident++) + { + _10.Store(_7ident * 4 + _4ident * 40 + 280, asuint(_64.c[_4ident].baz[_7ident].c)); + } + } +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp new file mode 100644 index 0000000..b751571 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp @@ -0,0 +1,22 @@ +struct T +{ + float c; +}; + +static const T _18 = { 40.0f }; + +RWByteAddressBuffer _7 : register(u0); +RWByteAddressBuffer _10 : register(u1); + +void comp_main() +{ + T v = _18; + _7.Store(40, asuint(v.c)); + _10.Store(480, asuint(v.c)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/atomic-load-store.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/atomic-load-store.asm.comp new file mode 100644 index 0000000..4f6a3e3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/atomic-load-store.asm.comp @@ -0,0 +1,18 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _5 : register(u0); + +void comp_main() +{ + uint _20; + _5.InterlockedAdd(4, 0, _20); + uint c = _20; + uint _23; + _5.InterlockedExchange(0, c, _23); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/atomic-result-temporary.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/atomic-result-temporary.asm.comp new file mode 100644 index 0000000..3a03faf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/atomic-result-temporary.asm.comp @@ -0,0 +1,24 @@ +RWByteAddressBuffer _5 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + uint _24; + _5.InterlockedAdd(0, 1u, _24); + if (_24 < 1024u) + { + _5.Store(_24 * 4 + 4, gl_GlobalInvocationID.x); + } +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/bitfield-signed-operations.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/bitfield-signed-operations.asm.comp new file mode 100644 index 0000000..ae5fe5a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/bitfield-signed-operations.asm.comp @@ -0,0 +1,105 @@ +RWByteAddressBuffer _3 : register(u0); + +uint SPIRV_Cross_bitfieldInsert(uint Base, uint Insert, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31)); + return (Base & ~Mask) | ((Insert << Offset) & Mask); +} + +uint2 SPIRV_Cross_bitfieldInsert(uint2 Base, uint2 Insert, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31)); + return (Base & ~Mask) | ((Insert << Offset) & Mask); +} + +uint3 SPIRV_Cross_bitfieldInsert(uint3 Base, uint3 Insert, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31)); + return (Base & ~Mask) | ((Insert << Offset) & Mask); +} + +uint4 SPIRV_Cross_bitfieldInsert(uint4 Base, uint4 Insert, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31)); + return (Base & ~Mask) | ((Insert << Offset) & Mask); +} + +uint SPIRV_Cross_bitfieldUExtract(uint Base, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1); + return (Base >> Offset) & Mask; +} + +uint2 SPIRV_Cross_bitfieldUExtract(uint2 Base, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1); + return (Base >> Offset) & Mask; +} + +uint3 SPIRV_Cross_bitfieldUExtract(uint3 Base, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1); + return (Base >> Offset) & Mask; +} + +uint4 SPIRV_Cross_bitfieldUExtract(uint4 Base, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1); + return (Base >> Offset) & Mask; +} + +int SPIRV_Cross_bitfieldSExtract(int Base, int Offset, int Count) +{ + int Mask = Count == 32 ? -1 : ((1 << Count) - 1); + int Masked = (Base >> Offset) & Mask; + int ExtendShift = (32 - Count) & 31; + return (Masked << ExtendShift) >> ExtendShift; +} + +int2 SPIRV_Cross_bitfieldSExtract(int2 Base, int Offset, int Count) +{ + int Mask = Count == 32 ? -1 : ((1 << Count) - 1); + int2 Masked = (Base >> Offset) & Mask; + int ExtendShift = (32 - Count) & 31; + return (Masked << ExtendShift) >> ExtendShift; +} + +int3 SPIRV_Cross_bitfieldSExtract(int3 Base, int Offset, int Count) +{ + int Mask = Count == 32 ? -1 : ((1 << Count) - 1); + int3 Masked = (Base >> Offset) & Mask; + int ExtendShift = (32 - Count) & 31; + return (Masked << ExtendShift) >> ExtendShift; +} + +int4 SPIRV_Cross_bitfieldSExtract(int4 Base, int Offset, int Count) +{ + int Mask = Count == 32 ? -1 : ((1 << Count) - 1); + int4 Masked = (Base >> Offset) & Mask; + int ExtendShift = (32 - Count) & 31; + return (Masked << ExtendShift) >> ExtendShift; +} + +void comp_main() +{ + int4 _19 = int4(_3.Load4(0)); + uint4 _20 = _3.Load4(16); + _3.Store4(0, uint4(countbits(_19))); + _3.Store4(16, uint4(countbits(_19))); + _3.Store4(0, uint4(int4(countbits(_20)))); + _3.Store4(16, countbits(_20)); + _3.Store4(0, uint4(reversebits(_19))); + _3.Store4(16, reversebits(_20)); + _3.Store4(0, uint4(SPIRV_Cross_bitfieldSExtract(_19, 1, 11u))); + _3.Store4(16, SPIRV_Cross_bitfieldSExtract(_20, 11u, 1)); + _3.Store4(0, uint4(SPIRV_Cross_bitfieldUExtract(_19, 1, 11u))); + _3.Store4(16, SPIRV_Cross_bitfieldUExtract(_20, 11u, 1)); + _3.Store4(0, uint4(int4(SPIRV_Cross_bitfieldInsert(_19, _19.wzyx, 1, 11u)))); + _3.Store4(16, SPIRV_Cross_bitfieldInsert(_20, _20.wzyx, 11u, 1)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/bitscan.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/bitscan.asm.comp new file mode 100644 index 0000000..f8a5fb6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/bitscan.asm.comp @@ -0,0 +1,25 @@ +RWByteAddressBuffer _4 : register(u0); + +void comp_main() +{ + uint4 _19 = _4.Load4(0); + int4 _20 = int4(_4.Load4(16)); + _4.Store4(0, firstbitlow(_19)); + _4.Store4(16, uint4(int4(firstbitlow(_19)))); + _4.Store4(0, uint4(firstbitlow(_20))); + _4.Store4(16, uint4(firstbitlow(_20))); + _4.Store4(0, firstbithigh(_19)); + _4.Store4(16, uint4(int4(firstbithigh(_19)))); + _4.Store4(0, firstbithigh(uint4(_20))); + _4.Store4(16, uint4(int4(firstbithigh(uint4(_20))))); + _4.Store4(0, uint4(firstbithigh(int4(_19)))); + _4.Store4(16, uint4(firstbithigh(int4(_19)))); + _4.Store4(0, uint4(firstbithigh(_20))); + _4.Store4(16, uint4(firstbithigh(_20))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/buffer-atomic-nonuniform.asm.sm51.nonuniformresource.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/buffer-atomic-nonuniform.asm.sm51.nonuniformresource.comp new file mode 100644 index 0000000..0b6d93e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/buffer-atomic-nonuniform.asm.sm51.nonuniformresource.comp @@ -0,0 +1,21 @@ +RWByteAddressBuffer ssbos[] : register(u0, space0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + uint _24 = gl_GlobalInvocationID.z; + uint _25; + ssbos[NonUniformResourceIndex(_24)].InterlockedAdd(0, 1u, _25); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/constant-composite-undef.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/constant-composite-undef.asm.comp new file mode 100644 index 0000000..e05b2f1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/constant-composite-undef.asm.comp @@ -0,0 +1,14 @@ +RWByteAddressBuffer block : register(u0); + +static float _15; + +void comp_main() +{ + block.Store4(0, asuint(float4(0.100000001490116119384765625f, 0.20000000298023223876953125f, 0.300000011920928955078125f, 0.0f))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/glsl-signed-operations.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/glsl-signed-operations.asm.comp new file mode 100644 index 0000000..db16cf6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/glsl-signed-operations.asm.comp @@ -0,0 +1,45 @@ +RWByteAddressBuffer _4 : register(u0); + +void comp_main() +{ + int4 _19 = int4(_4.Load4(0)); + uint4 _20 = _4.Load4(16); + _4.Store4(0, uint4(abs(_19))); + _4.Store4(16, uint4(abs(_19))); + _4.Store4(0, uint4(abs(int4(_20)))); + _4.Store4(16, uint4(abs(int4(_20)))); + _4.Store4(0, uint4(sign(_19))); + _4.Store4(16, uint4(sign(_19))); + _4.Store4(0, uint4(sign(int4(_20)))); + _4.Store4(16, uint4(sign(int4(_20)))); + _4.Store4(0, uint4(firstbithigh(int4(_20)))); + _4.Store4(16, uint4(firstbithigh(int4(_20)))); + _4.Store4(0, uint4(int4(firstbithigh(uint4(_19))))); + _4.Store4(16, firstbithigh(uint4(_19))); + _4.Store4(0, uint4(min(_19, _19))); + _4.Store4(16, uint4(min(_19, int4(_20)))); + _4.Store4(0, uint4(min(int4(_20), int4(_20)))); + _4.Store4(16, uint4(min(int4(_20), _19))); + _4.Store4(0, uint4(int4(min(uint4(_19), _20)))); + _4.Store4(16, min(uint4(_19), _20)); + _4.Store4(0, uint4(int4(min(_20, uint4(_19))))); + _4.Store4(16, min(_20, uint4(_19))); + _4.Store4(0, uint4(max(_19, _19))); + _4.Store4(16, uint4(max(_19, _19))); + _4.Store4(0, uint4(max(int4(_20), _19))); + _4.Store4(16, uint4(max(int4(_20), _19))); + _4.Store4(0, uint4(int4(max(uint4(_19), _20)))); + _4.Store4(16, max(uint4(_19), uint4(_19))); + _4.Store4(0, uint4(int4(max(_20, uint4(_19))))); + _4.Store4(16, max(_20, uint4(_19))); + _4.Store4(0, uint4(clamp(int4(_20), int4(_20), int4(_20)))); + _4.Store4(16, uint4(clamp(int4(_20), int4(_20), int4(_20)))); + _4.Store4(0, uint4(int4(clamp(uint4(_19), uint4(_19), uint4(_19))))); + _4.Store4(16, clamp(uint4(_19), uint4(_19), uint4(_19))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.fxconly.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.fxconly.asm.comp new file mode 100644 index 0000000..b123263 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.fxconly.asm.comp @@ -0,0 +1,31 @@ +struct _8 +{ + float _m0; + float _m1; +}; + +struct _15 +{ + float _m0; + int _m1; +}; + +RWByteAddressBuffer _4 : register(u0); + +void comp_main() +{ + _8 _23; + _23._m0 = modf(20.0f, _23._m1); + _15 _24; + _24._m0 = frexp(40.0f, _24._m1); + _4.Store(0, asuint(_23._m0)); + _4.Store(0, asuint(_23._m1)); + _4.Store(0, asuint(_24._m0)); + _4.Store(4, uint(_24._m1)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/image-atomic-nonuniform.asm.sm51.nonuniformresource.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/image-atomic-nonuniform.asm.sm51.nonuniformresource.comp new file mode 100644 index 0000000..07f87ca --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/image-atomic-nonuniform.asm.sm51.nonuniformresource.comp @@ -0,0 +1,21 @@ +RWTexture2D uImage[] : register(u0, space0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + uint _26 = gl_GlobalInvocationID.z; + uint _31; + InterlockedAdd(uImage[NonUniformResourceIndex(_26)][int2(gl_GlobalInvocationID.xy)], 1u, _31); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/specialization-constant-workgroup.nofxc.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/specialization-constant-workgroup.nofxc.asm.comp new file mode 100644 index 0000000..8c239b3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/specialization-constant-workgroup.nofxc.asm.comp @@ -0,0 +1,22 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 9u +#endif +static const uint _5 = SPIRV_CROSS_CONSTANT_ID_10; +#ifndef SPIRV_CROSS_CONSTANT_ID_12 +#define SPIRV_CROSS_CONSTANT_ID_12 4u +#endif +static const uint _6 = SPIRV_CROSS_CONSTANT_ID_12; +static const uint3 gl_WorkGroupSize = uint3(_5, 20u, _6); + +RWByteAddressBuffer _4 : register(u0); + +void comp_main() +{ + _4.Store(0, asuint(asfloat(_4.Load(0)) + 1.0f)); +} + +[numthreads(SPIRV_CROSS_CONSTANT_ID_10, 20, SPIRV_CROSS_CONSTANT_ID_12)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/storage-buffer-basic.invalid.nofxc.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/storage-buffer-basic.invalid.nofxc.asm.comp new file mode 100644 index 0000000..c567fba --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/comp/storage-buffer-basic.invalid.nofxc.asm.comp @@ -0,0 +1,32 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 1u +#endif +static const uint _3 = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 3u +#endif +static const uint _4 = SPIRV_CROSS_CONSTANT_ID_2; +static const uint3 gl_WorkGroupSize = uint3(_3, 2u, _4); + +RWByteAddressBuffer _8 : register(u0); +RWByteAddressBuffer _9 : register(u1); + +static uint3 gl_WorkGroupID; +struct SPIRV_Cross_Input +{ + uint3 gl_WorkGroupID : SV_GroupID; +}; + +static uint3 _22 = gl_WorkGroupSize; + +void comp_main() +{ + _8.Store(gl_WorkGroupID.x * 4 + 0, asuint(asfloat(_9.Load(gl_WorkGroupID.x * 4 + 0)) + asfloat(_8.Load(gl_WorkGroupID.x * 4 + 0)))); +} + +[numthreads(SPIRV_CROSS_CONSTANT_ID_0, 2, SPIRV_CROSS_CONSTANT_ID_2)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_WorkGroupID = stage_input.gl_WorkGroupID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/empty-struct-in-struct.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/empty-struct-in-struct.asm.frag new file mode 100644 index 0000000..e7ffd8d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/empty-struct-in-struct.asm.frag @@ -0,0 +1,37 @@ +struct EmptyStructTest +{ + int empty_struct_member; +}; + +struct EmptyStruct2Test +{ + EmptyStructTest _m0; +}; + +static const EmptyStructTest _30 = { 0 }; +static const EmptyStruct2Test _20 = { { 0 } }; + +float GetValue(EmptyStruct2Test self) +{ + return 0.0f; +} + +float GetValue_1(EmptyStruct2Test self) +{ + return 0.0f; +} + +void frag_main() +{ + EmptyStructTest _25 = { 0 }; + EmptyStruct2Test _26 = { _25 }; + EmptyStruct2Test emptyStruct; + float value = GetValue(emptyStruct); + value = GetValue_1(_26); + value = GetValue_1(_20); +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag new file mode 100644 index 0000000..c5a7613 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag @@ -0,0 +1,28 @@ +Texture2D Tex : register(t0); + +static uint3 in_var_TEXCOORD0; +static float4 out_var_SV_Target0; + +struct SPIRV_Cross_Input +{ + nointerpolation uint3 in_var_TEXCOORD0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 out_var_SV_Target0 : SV_Target0; +}; + +void frag_main() +{ + out_var_SV_Target0 = Tex.Load(int3(in_var_TEXCOORD0.xy, in_var_TEXCOORD0.z)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + in_var_TEXCOORD0 = stage_input.in_var_TEXCOORD0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.out_var_SV_Target0 = out_var_SV_Target0; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/nonuniform-bracket-handling-2.nonuniformresource.sm51.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/nonuniform-bracket-handling-2.nonuniformresource.sm51.asm.frag new file mode 100644 index 0000000..b7e67dd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/nonuniform-bracket-handling-2.nonuniformresource.sm51.asm.frag @@ -0,0 +1,37 @@ +ByteAddressBuffer _8 : register(t0, space0); +Texture2D uSamplers[] : register(t0, space0); +SamplerState _uSamplers_sampler[] : register(s0, space0); +Texture2D uSampler : register(t0, space1); +SamplerState _uSampler_sampler : register(s0, space1); + +static float4 gl_FragCoord; +static float4 FragColor; +static float2 vUV; + +struct SPIRV_Cross_Input +{ + float2 vUV : TEXCOORD0; + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uSamplers[NonUniformResourceIndex(_8.Load(40))].SampleLevel(_uSamplers_sampler[NonUniformResourceIndex(_8.Load(40))], vUV, 0.0f); + FragColor += uSampler.SampleLevel(_uSampler_sampler, vUV, float(_8.Load(int(gl_FragCoord.y) * 4 + 0))); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/nonuniform-qualifier-propagation.nonuniformresource.sm51.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/nonuniform-qualifier-propagation.nonuniformresource.sm51.asm.frag new file mode 100644 index 0000000..44cc8ab --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/nonuniform-qualifier-propagation.nonuniformresource.sm51.asm.frag @@ -0,0 +1,52 @@ +struct UBO_1_1 +{ + float4 v[64]; +}; + +ConstantBuffer ubos[] : register(b0, space2); +ByteAddressBuffer ssbos[] : register(t0, space3); +Texture2D uSamplers[] : register(t0, space0); +SamplerState uSamps[] : register(s0, space1); +Texture2D uCombinedSamplers[] : register(t4, space0); +SamplerState _uCombinedSamplers_sampler[] : register(s4, space0); + +static int vIndex; +static float4 FragColor; +static float2 vUV; + +struct SPIRV_Cross_Input +{ + nointerpolation int vIndex : TEXCOORD0; + float2 vUV : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int i = vIndex; + int _59 = i + 10; + int _64 = i + 40; + FragColor = uSamplers[NonUniformResourceIndex(_59)].Sample(uSamps[NonUniformResourceIndex(_64)], vUV); + int _71 = i + 10; + FragColor = uCombinedSamplers[NonUniformResourceIndex(_71)].Sample(_uCombinedSamplers_sampler[NonUniformResourceIndex(_71)], vUV); + int _77 = i + 20; + int _80 = i + 40; + FragColor += ubos[NonUniformResourceIndex(_77)].v[_80]; + int _87 = i + 50; + int _90 = i + 60; + FragColor += asfloat(ssbos[NonUniformResourceIndex(_87)].Load4(_90 * 16 + 0)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vIndex = stage_input.vIndex; + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/phi.zero-initialize.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/phi.zero-initialize.asm.frag new file mode 100644 index 0000000..c9c152b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/phi.zero-initialize.asm.frag @@ -0,0 +1,45 @@ +struct Foo +{ + int a; +}; + +static float4 vColor; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +static int uninit_int = 0; +static int4 uninit_vector = int4(0, 0, 0, 0); +static float4x4 uninit_matrix = float4x4(0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx); +static Foo uninit_foo = { 0 }; + +void frag_main() +{ + int _39 = 0; + if (vColor.x > 10.0f) + { + _39 = 10; + } + else + { + _39 = 20; + } + FragColor = vColor; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-callstack.sm51.fxconly.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-callstack.sm51.fxconly.asm.frag new file mode 100644 index 0000000..8a47b91 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-callstack.sm51.fxconly.asm.frag @@ -0,0 +1,33 @@ +RasterizerOrderedByteAddressBuffer _7 : register(u1, space0); +RWByteAddressBuffer _9 : register(u0, space0); + +static float4 gl_FragCoord; +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +void callee2() +{ + int _31 = int(gl_FragCoord.x); + _7.Store(_31 * 4 + 0, _7.Load(_31 * 4 + 0) + 1u); +} + +void callee() +{ + int _39 = int(gl_FragCoord.x); + _9.Store(_39 * 4 + 0, _9.Load(_39 * 4 + 0) + 1u); + callee2(); +} + +void frag_main() +{ + callee(); +} + +void main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-control-flow.sm51.fxconly.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-control-flow.sm51.fxconly.asm.frag new file mode 100644 index 0000000..01bbe7d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-control-flow.sm51.fxconly.asm.frag @@ -0,0 +1,43 @@ +RasterizerOrderedByteAddressBuffer _7 : register(u1, space0); +RWByteAddressBuffer _13 : register(u2, space0); +RasterizerOrderedByteAddressBuffer _9 : register(u0, space0); + +static float4 gl_FragCoord; +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +void callee2() +{ + int _44 = int(gl_FragCoord.x); + _7.Store(_44 * 4 + 0, _7.Load(_44 * 4 + 0) + 1u); +} + +void callee() +{ + int _52 = int(gl_FragCoord.x); + _9.Store(_52 * 4 + 0, _9.Load(_52 * 4 + 0) + 1u); + callee2(); + if (true) + { + } +} + +void _35() +{ + _13.Store(int(gl_FragCoord.x) * 4 + 0, 4u); +} + +void frag_main() +{ + callee(); + _35(); +} + +void main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-split-functions.sm51.fxconly.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-split-functions.sm51.fxconly.asm.frag new file mode 100644 index 0000000..c1fb6eb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/pixel-interlock-split-functions.sm51.fxconly.asm.frag @@ -0,0 +1,43 @@ +RasterizerOrderedByteAddressBuffer _7 : register(u1, space0); +RasterizerOrderedByteAddressBuffer _9 : register(u0, space0); + +static float4 gl_FragCoord; +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +void callee2() +{ + int _37 = int(gl_FragCoord.x); + _7.Store(_37 * 4 + 0, _7.Load(_37 * 4 + 0) + 1u); +} + +void callee() +{ + int _45 = int(gl_FragCoord.x); + _9.Store(_45 * 4 + 0, _9.Load(_45 * 4 + 0) + 1u); + callee2(); +} + +void _29() +{ +} + +void _31() +{ +} + +void frag_main() +{ + callee(); + _29(); + _31(); +} + +void main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/struct-packing-last-element-array-matrix-rule.invalid.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/struct-packing-last-element-array-matrix-rule.invalid.asm.frag new file mode 100644 index 0000000..b88ac0d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/struct-packing-last-element-array-matrix-rule.invalid.asm.frag @@ -0,0 +1,42 @@ +struct Foo +{ + row_major float3x3 m[2]; + float v; +}; + +struct Bar +{ + row_major float3x3 m; + float v; +}; + +cbuffer FooUBO : register(b0) +{ + Foo _6_foo : packoffset(c0); +}; + +cbuffer BarUBO : register(b1) +{ + Bar _9_bar : packoffset(c0); +}; + + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = (_6_foo.v + _9_bar.v).xxxx; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/subgroup-arithmetic-cast.invalid.nofxc.sm60.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/subgroup-arithmetic-cast.invalid.nofxc.sm60.asm.frag new file mode 100644 index 0000000..b98e681 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/subgroup-arithmetic-cast.invalid.nofxc.sm60.asm.frag @@ -0,0 +1,30 @@ +static int index; +static uint FragColor; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + uint FragColor : SV_Target0; +}; + +void frag_main() +{ + uint _17 = uint(index); + FragColor = uint(WaveActiveMin(index)); + FragColor = uint(WaveActiveMax(int(_17))); + FragColor = WaveActiveMin(uint(index)); + FragColor = WaveActiveMax(_17); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag new file mode 100644 index 0000000..19af59d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag @@ -0,0 +1,82 @@ +static int vIndex; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + nointerpolation int vIndex : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int i = 0; + int j; + int _30; + int _31; + switch (vIndex) + { + case 0: + { + _30 = 3; + j = _30; + _31 = 0; + j = _31; + break; + } + default: + { + _30 = 2; + j = _30; + _31 = 0; + j = _31; + break; + } + case 1: + case 11: + { + _31 = 1; + j = _31; + break; + } + case 2: + { + break; + } + case 3: + { + if (vIndex > 3) + { + i = 0; + break; + } + else + { + break; + } + } + case 4: + { + i = 0; + break; + } + case 5: + { + i = 0; + break; + } + } + FragColor = float(i).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vIndex = stage_input.vIndex; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/unordered-compare.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/unordered-compare.asm.frag new file mode 100644 index 0000000..1143ade --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/unordered-compare.asm.frag @@ -0,0 +1,51 @@ +static float4 A; +static float4 B; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 A : TEXCOORD0; + float4 B : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +float4 test_vector() +{ + bool4 le = bool4(!(A.x >= B.x), !(A.y >= B.y), !(A.z >= B.z), !(A.w >= B.w)); + bool4 leq = bool4(!(A.x > B.x), !(A.y > B.y), !(A.z > B.z), !(A.w > B.w)); + bool4 ge = bool4(!(A.x <= B.x), !(A.y <= B.y), !(A.z <= B.z), !(A.w <= B.w)); + bool4 geq = bool4(!(A.x < B.x), !(A.y < B.y), !(A.z < B.z), !(A.w < B.w)); + bool4 eq = bool4(A.x == B.x, A.y == B.y, A.z == B.z, A.w == B.w); + bool4 neq = bool4(A.x != B.x, A.y != B.y, A.z != B.z, A.w != B.w); + return ((((float4(le.x ? 1.0f.xxxx.x : 0.0f.xxxx.x, le.y ? 1.0f.xxxx.y : 0.0f.xxxx.y, le.z ? 1.0f.xxxx.z : 0.0f.xxxx.z, le.w ? 1.0f.xxxx.w : 0.0f.xxxx.w) + float4(leq.x ? 1.0f.xxxx.x : 0.0f.xxxx.x, leq.y ? 1.0f.xxxx.y : 0.0f.xxxx.y, leq.z ? 1.0f.xxxx.z : 0.0f.xxxx.z, leq.w ? 1.0f.xxxx.w : 0.0f.xxxx.w)) + float4(ge.x ? 1.0f.xxxx.x : 0.0f.xxxx.x, ge.y ? 1.0f.xxxx.y : 0.0f.xxxx.y, ge.z ? 1.0f.xxxx.z : 0.0f.xxxx.z, ge.w ? 1.0f.xxxx.w : 0.0f.xxxx.w)) + float4(geq.x ? 1.0f.xxxx.x : 0.0f.xxxx.x, geq.y ? 1.0f.xxxx.y : 0.0f.xxxx.y, geq.z ? 1.0f.xxxx.z : 0.0f.xxxx.z, geq.w ? 1.0f.xxxx.w : 0.0f.xxxx.w)) + float4(eq.x ? 1.0f.xxxx.x : 0.0f.xxxx.x, eq.y ? 1.0f.xxxx.y : 0.0f.xxxx.y, eq.z ? 1.0f.xxxx.z : 0.0f.xxxx.z, eq.w ? 1.0f.xxxx.w : 0.0f.xxxx.w)) + float4(neq.x ? 1.0f.xxxx.x : 0.0f.xxxx.x, neq.y ? 1.0f.xxxx.y : 0.0f.xxxx.y, neq.z ? 1.0f.xxxx.z : 0.0f.xxxx.z, neq.w ? 1.0f.xxxx.w : 0.0f.xxxx.w); +} + +float test_scalar() +{ + bool le = !(A.x >= B.x); + bool leq = !(A.x > B.x); + bool ge = !(A.x <= B.x); + bool geq = !(A.x < B.x); + bool eq = A.x == B.x; + bool neq = A.x != B.x; + return ((((float(le) + float(leq)) + float(ge)) + float(geq)) + float(eq)) + float(neq); +} + +void frag_main() +{ + FragColor = test_vector() + test_scalar().xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + A = stage_input.A; + B = stage_input.B; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag new file mode 100644 index 0000000..fa41b13 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag @@ -0,0 +1,28 @@ +static float4 FragColor; +static float4 vFloat; + +struct SPIRV_Cross_Input +{ + float4 vFloat : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +static float4 undef; + +void frag_main() +{ + FragColor = float4(undef.x, vFloat.y, 0.0f, vFloat.w) + float4(vFloat.z, vFloat.y, 0.0f, vFloat.w); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vFloat = stage_input.vFloat; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/temporary.zero-initialize.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/temporary.zero-initialize.asm.frag new file mode 100644 index 0000000..dbdd784 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/temporary.zero-initialize.asm.frag @@ -0,0 +1,44 @@ +static float4 FragColor; +static int vA; +static int vB; + +struct SPIRV_Cross_Input +{ + nointerpolation int vA : TEXCOORD0; + nointerpolation int vB : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = 0.0f.xxxx; + int _10 = 0; + int _15 = 0; + for (int _16 = 0, _17 = 0; _16 < vA; _17 = _15, _16 += _10) + { + if ((vA + _16) == 20) + { + _15 = 50; + } + else + { + _15 = ((vB + _16) == 40) ? 60 : _17; + } + _10 = _15 + 10; + FragColor += 1.0f.xxxx; + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vA = stage_input.vA; + vB = stage_input.vB; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert new file mode 100644 index 0000000..45973c2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert @@ -0,0 +1,54 @@ +struct Struct_vec4 +{ + float4 m0; +}; + +cbuffer UBO : register(b0) +{ + Struct_vec4 ubo_binding_0_m0 : packoffset(c0); + Struct_vec4 ubo_binding_0_m1 : packoffset(c1); +}; + + +static float4 gl_Position; +static Struct_vec4 output_location_2; +static Struct_vec4 output_location_3; + +struct VertexOut +{ + Struct_vec4 m0 : TEXCOORD0; + Struct_vec4 m1 : TEXCOORD1; +}; + +static VertexOut output_location_0; + +struct SPIRV_Cross_Output +{ + Struct_vec4 output_location_2 : TEXCOORD2; + Struct_vec4 output_location_3 : TEXCOORD3; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + Struct_vec4 c; + c.m0 = ubo_binding_0_m0.m0; + Struct_vec4 b; + b.m0 = ubo_binding_0_m1.m0; + gl_Position = c.m0 + b.m0; + output_location_0.m0 = c; + output_location_0.m1 = b; + output_location_2 = c; + output_location_3 = b; +} + +SPIRV_Cross_Output main(out VertexOut stage_outputoutput_location_0) +{ + vert_main(); + stage_outputoutput_location_0 = output_location_0; + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.output_location_2 = output_location_2; + stage_output.output_location_3 = output_location_3; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/vert/empty-struct-composite.asm.vert b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/vert/empty-struct-composite.asm.vert new file mode 100644 index 0000000..ba1f576 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/asm/vert/empty-struct-composite.asm.vert @@ -0,0 +1,15 @@ +struct Test +{ + int empty_struct_member; +}; + +void vert_main() +{ + Test _14 = { 0 }; + Test t = _14; +} + +void main() +{ + vert_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/bitfield.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/bitfield.comp new file mode 100644 index 0000000..0dc089c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/bitfield.comp @@ -0,0 +1,113 @@ +uint SPIRV_Cross_bitfieldInsert(uint Base, uint Insert, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31)); + return (Base & ~Mask) | ((Insert << Offset) & Mask); +} + +uint2 SPIRV_Cross_bitfieldInsert(uint2 Base, uint2 Insert, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31)); + return (Base & ~Mask) | ((Insert << Offset) & Mask); +} + +uint3 SPIRV_Cross_bitfieldInsert(uint3 Base, uint3 Insert, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31)); + return (Base & ~Mask) | ((Insert << Offset) & Mask); +} + +uint4 SPIRV_Cross_bitfieldInsert(uint4 Base, uint4 Insert, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31)); + return (Base & ~Mask) | ((Insert << Offset) & Mask); +} + +uint SPIRV_Cross_bitfieldUExtract(uint Base, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1); + return (Base >> Offset) & Mask; +} + +uint2 SPIRV_Cross_bitfieldUExtract(uint2 Base, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1); + return (Base >> Offset) & Mask; +} + +uint3 SPIRV_Cross_bitfieldUExtract(uint3 Base, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1); + return (Base >> Offset) & Mask; +} + +uint4 SPIRV_Cross_bitfieldUExtract(uint4 Base, uint Offset, uint Count) +{ + uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1); + return (Base >> Offset) & Mask; +} + +int SPIRV_Cross_bitfieldSExtract(int Base, int Offset, int Count) +{ + int Mask = Count == 32 ? -1 : ((1 << Count) - 1); + int Masked = (Base >> Offset) & Mask; + int ExtendShift = (32 - Count) & 31; + return (Masked << ExtendShift) >> ExtendShift; +} + +int2 SPIRV_Cross_bitfieldSExtract(int2 Base, int Offset, int Count) +{ + int Mask = Count == 32 ? -1 : ((1 << Count) - 1); + int2 Masked = (Base >> Offset) & Mask; + int ExtendShift = (32 - Count) & 31; + return (Masked << ExtendShift) >> ExtendShift; +} + +int3 SPIRV_Cross_bitfieldSExtract(int3 Base, int Offset, int Count) +{ + int Mask = Count == 32 ? -1 : ((1 << Count) - 1); + int3 Masked = (Base >> Offset) & Mask; + int ExtendShift = (32 - Count) & 31; + return (Masked << ExtendShift) >> ExtendShift; +} + +int4 SPIRV_Cross_bitfieldSExtract(int4 Base, int Offset, int Count) +{ + int Mask = Count == 32 ? -1 : ((1 << Count) - 1); + int4 Masked = (Base >> Offset) & Mask; + int ExtendShift = (32 - Count) & 31; + return (Masked << ExtendShift) >> ExtendShift; +} + +void comp_main() +{ + int signed_value = 0; + uint unsigned_value = 0u; + int3 signed_values = int3(0, 0, 0); + uint3 unsigned_values = uint3(0u, 0u, 0u); + int s = SPIRV_Cross_bitfieldSExtract(signed_value, 5, 20); + uint u = SPIRV_Cross_bitfieldUExtract(unsigned_value, 6, 21); + s = int(SPIRV_Cross_bitfieldInsert(s, 40, 5, 4)); + u = SPIRV_Cross_bitfieldInsert(u, 60u, 5, 4); + u = reversebits(u); + s = reversebits(s); + int v0 = int(countbits(u)); + int v1 = countbits(s); + int v2 = int(firstbithigh(u)); + int v3 = firstbitlow(s); + int3 s_1 = SPIRV_Cross_bitfieldSExtract(signed_values, 5, 20); + uint3 u_1 = SPIRV_Cross_bitfieldUExtract(unsigned_values, 6, 21); + s_1 = int3(SPIRV_Cross_bitfieldInsert(s_1, int3(40, 40, 40), 5, 4)); + u_1 = SPIRV_Cross_bitfieldInsert(u_1, uint3(60u, 60u, 60u), 5, 4); + u_1 = reversebits(u_1); + s_1 = reversebits(s_1); + int3 v0_1 = int3(countbits(u_1)); + int3 v1_1 = countbits(s_1); + int3 v2_1 = int3(firstbithigh(u_1)); + int3 v3_1 = firstbitlow(s_1); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/glsl.std450.fxconly.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/glsl.std450.fxconly.comp new file mode 100644 index 0000000..7558afa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/glsl.std450.fxconly.comp @@ -0,0 +1,297 @@ +struct ResType +{ + float _m0; + int _m1; +}; + +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _19 : register(u0); + +uint SPIRV_Cross_packHalf2x16(float2 value) +{ + uint2 Packed = f32tof16(value); + return Packed.x | (Packed.y << 16); +} + +float2 SPIRV_Cross_unpackHalf2x16(uint value) +{ + return f16tof32(uint2(value & 0xffff, value >> 16)); +} + +uint SPIRV_Cross_packUnorm4x8(float4 value) +{ + uint4 Packed = uint4(round(saturate(value) * 255.0)); + return Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24); +} + +float4 SPIRV_Cross_unpackUnorm4x8(uint value) +{ + uint4 Packed = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24); + return float4(Packed) / 255.0; +} + +uint SPIRV_Cross_packSnorm4x8(float4 value) +{ + int4 Packed = int4(round(clamp(value, -1.0, 1.0) * 127.0)) & 0xff; + return uint(Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24)); +} + +float4 SPIRV_Cross_unpackSnorm4x8(uint value) +{ + int SignedValue = int(value); + int4 Packed = int4(SignedValue << 24, SignedValue << 16, SignedValue << 8, SignedValue) >> 24; + return clamp(float4(Packed) / 127.0, -1.0, 1.0); +} + +uint SPIRV_Cross_packUnorm2x16(float2 value) +{ + uint2 Packed = uint2(round(saturate(value) * 65535.0)); + return Packed.x | (Packed.y << 16); +} + +float2 SPIRV_Cross_unpackUnorm2x16(uint value) +{ + uint2 Packed = uint2(value & 0xffff, value >> 16); + return float2(Packed) / 65535.0; +} + +uint SPIRV_Cross_packSnorm2x16(float2 value) +{ + int2 Packed = int2(round(clamp(value, -1.0, 1.0) * 32767.0)) & 0xffff; + return uint(Packed.x | (Packed.y << 16)); +} + +float2 SPIRV_Cross_unpackSnorm2x16(uint value) +{ + int SignedValue = int(value); + int2 Packed = int2(SignedValue << 16, SignedValue) >> 16; + return clamp(float2(Packed) / 32767.0, -1.0, 1.0); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float2x2 SPIRV_Cross_Inverse(float2x2 m) +{ + float2x2 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = m[1][1]; + adj[0][1] = -m[0][1]; + + adj[1][0] = -m[1][0]; + adj[1][1] = m[0][0]; + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the determinant of a 2x2 matrix. +float SPIRV_Cross_Det2x2(float a1, float a2, float b1, float b2) +{ + return a1 * b2 - b1 * a2; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float3x3 SPIRV_Cross_Inverse(float3x3 m) +{ + float3x3 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = SPIRV_Cross_Det2x2(m[1][1], m[1][2], m[2][1], m[2][2]); + adj[0][1] = -SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[2][1], m[2][2]); + adj[0][2] = SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[1][1], m[1][2]); + + adj[1][0] = -SPIRV_Cross_Det2x2(m[1][0], m[1][2], m[2][0], m[2][2]); + adj[1][1] = SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[2][0], m[2][2]); + adj[1][2] = -SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[1][0], m[1][2]); + + adj[2][0] = SPIRV_Cross_Det2x2(m[1][0], m[1][1], m[2][0], m[2][1]); + adj[2][1] = -SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[2][0], m[2][1]); + adj[2][2] = SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[1][0], m[1][1]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the determinant of a 3x3 matrix. +float SPIRV_Cross_Det3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) +{ + return a1 * SPIRV_Cross_Det2x2(b2, b3, c2, c3) - b1 * SPIRV_Cross_Det2x2(a2, a3, c2, c3) + c1 * SPIRV_Cross_Det2x2(a2, a3, b2, b3); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float4x4 SPIRV_Cross_Inverse(float4x4 m) +{ + float4x4 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = SPIRV_Cross_Det3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][1] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][2] = SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3]); + adj[0][3] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]); + + adj[1][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3]); + adj[1][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]); + + adj[2][0] = SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][1] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][2] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3]); + adj[2][3] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]); + + adj[3][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2]); + adj[3][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] * m[3][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +float SPIRV_Cross_Reflect(float i, float n) +{ + return i - 2.0 * dot(n, i) * n; +} + +float SPIRV_Cross_Refract(float i, float n, float eta) +{ + float NoI = n * i; + float NoI2 = NoI * NoI; + float k = 1.0 - eta * eta * (1.0 - NoI2); + if (k < 0.0) + { + return 0.0; + } + else + { + return eta * i - (eta * NoI + sqrt(k)) * n; + } +} + +float SPIRV_Cross_FaceForward(float n, float i, float nref) +{ + return i * nref < 0.0 ? n : -n; +} + +void comp_main() +{ + _19.Store(0, asuint(round(asfloat(_19.Load(16))))); + _19.Store(0, asuint(trunc(asfloat(_19.Load(16))))); + _19.Store(0, asuint(abs(asfloat(_19.Load(16))))); + _19.Store(4, uint(abs(int(_19.Load(32))))); + _19.Store(0, asuint(sign(asfloat(_19.Load(16))))); + _19.Store(4, uint(sign(int(_19.Load(32))))); + _19.Store(0, asuint(floor(asfloat(_19.Load(16))))); + _19.Store(0, asuint(ceil(asfloat(_19.Load(16))))); + _19.Store(0, asuint(frac(asfloat(_19.Load(16))))); + _19.Store(0, asuint(radians(asfloat(_19.Load(16))))); + _19.Store(0, asuint(degrees(asfloat(_19.Load(16))))); + _19.Store(0, asuint(sin(asfloat(_19.Load(16))))); + _19.Store(0, asuint(cos(asfloat(_19.Load(16))))); + _19.Store(0, asuint(tan(asfloat(_19.Load(16))))); + _19.Store(0, asuint(asin(asfloat(_19.Load(16))))); + _19.Store(0, asuint(acos(asfloat(_19.Load(16))))); + _19.Store(0, asuint(atan(asfloat(_19.Load(16))))); + _19.Store(0, asuint(sinh(asfloat(_19.Load(16))))); + _19.Store(0, asuint(cosh(asfloat(_19.Load(16))))); + _19.Store(0, asuint(tanh(asfloat(_19.Load(16))))); + _19.Store(0, asuint(atan2(asfloat(_19.Load(16)), asfloat(_19.Load(20))))); + _19.Store(0, asuint(pow(asfloat(_19.Load(16)), asfloat(_19.Load(20))))); + _19.Store(0, asuint(exp(asfloat(_19.Load(16))))); + _19.Store(0, asuint(log(asfloat(_19.Load(16))))); + _19.Store(0, asuint(exp2(asfloat(_19.Load(16))))); + _19.Store(0, asuint(log2(asfloat(_19.Load(16))))); + _19.Store(0, asuint(sqrt(asfloat(_19.Load(16))))); + _19.Store(0, asuint(rsqrt(asfloat(_19.Load(16))))); + _19.Store(0, asuint(length(asfloat(_19.Load(16))))); + _19.Store(0, asuint(distance(asfloat(_19.Load(16)), asfloat(_19.Load(20))))); + _19.Store(0, asuint(sign(asfloat(_19.Load(16))))); + _19.Store(0, asuint(SPIRV_Cross_FaceForward(asfloat(_19.Load(16)), asfloat(_19.Load(20)), asfloat(_19.Load(24))))); + _19.Store(0, asuint(SPIRV_Cross_Reflect(asfloat(_19.Load(16)), asfloat(_19.Load(20))))); + _19.Store(0, asuint(SPIRV_Cross_Refract(asfloat(_19.Load(16)), asfloat(_19.Load(20)), asfloat(_19.Load(24))))); + _19.Store(0, asuint(length(asfloat(_19.Load4(16)).xy))); + _19.Store(0, asuint(distance(asfloat(_19.Load4(16)).xy, asfloat(_19.Load4(16)).zw))); + float2 v2 = normalize(asfloat(_19.Load4(16)).xy); + v2 = faceforward(asfloat(_19.Load4(16)).xy, asfloat(_19.Load4(16)).yz, asfloat(_19.Load4(16)).zw); + v2 = reflect(asfloat(_19.Load4(16)).xy, asfloat(_19.Load4(16)).zw); + v2 = refract(asfloat(_19.Load4(16)).xy, asfloat(_19.Load4(16)).yz, asfloat(_19.Load(28))); + float3 v3 = cross(asfloat(_19.Load4(16)).xyz, asfloat(_19.Load4(16)).yzw); + float2x2 _240 = asfloat(uint2x2(_19.Load2(64), _19.Load2(72))); + _19.Store(0, asuint(determinant(_240))); + float3x3 _246 = asfloat(uint3x3(_19.Load3(80), _19.Load3(96), _19.Load3(112))); + _19.Store(0, asuint(determinant(_246))); + float4x4 _252 = asfloat(uint4x4(_19.Load4(128), _19.Load4(144), _19.Load4(160), _19.Load4(176))); + _19.Store(0, asuint(determinant(_252))); + float2x2 _256 = asfloat(uint2x2(_19.Load2(64), _19.Load2(72))); + float2x2 _257 = SPIRV_Cross_Inverse(_256); + _19.Store2(64, asuint(_257[0])); + _19.Store2(72, asuint(_257[1])); + float3x3 _260 = asfloat(uint3x3(_19.Load3(80), _19.Load3(96), _19.Load3(112))); + float3x3 _261 = SPIRV_Cross_Inverse(_260); + _19.Store3(80, asuint(_261[0])); + _19.Store3(96, asuint(_261[1])); + _19.Store3(112, asuint(_261[2])); + float4x4 _264 = asfloat(uint4x4(_19.Load4(128), _19.Load4(144), _19.Load4(160), _19.Load4(176))); + float4x4 _265 = SPIRV_Cross_Inverse(_264); + _19.Store4(128, asuint(_265[0])); + _19.Store4(144, asuint(_265[1])); + _19.Store4(160, asuint(_265[2])); + _19.Store4(176, asuint(_265[3])); + float tmp; + float _271 = modf(asfloat(_19.Load(16)), tmp); + _19.Store(0, asuint(_271)); + _19.Store(0, asuint(min(asfloat(_19.Load(16)), asfloat(_19.Load(20))))); + _19.Store(8, min(_19.Load(48), _19.Load(52))); + _19.Store(4, uint(min(int(_19.Load(32)), int(_19.Load(36))))); + _19.Store(0, asuint(max(asfloat(_19.Load(16)), asfloat(_19.Load(20))))); + _19.Store(8, max(_19.Load(48), _19.Load(52))); + _19.Store(4, uint(max(int(_19.Load(32)), int(_19.Load(36))))); + _19.Store(0, asuint(clamp(asfloat(_19.Load(16)), asfloat(_19.Load(20)), asfloat(_19.Load(24))))); + _19.Store(8, clamp(_19.Load(48), _19.Load(52), _19.Load(56))); + _19.Store(4, uint(clamp(int(_19.Load(32)), int(_19.Load(36)), int(_19.Load(40))))); + _19.Store(0, asuint(lerp(asfloat(_19.Load(16)), asfloat(_19.Load(20)), asfloat(_19.Load(24))))); + _19.Store(0, asuint(step(asfloat(_19.Load(16)), asfloat(_19.Load(20))))); + _19.Store(0, asuint(smoothstep(asfloat(_19.Load(16)), asfloat(_19.Load(20)), asfloat(_19.Load(24))))); + _19.Store(0, asuint(mad(asfloat(_19.Load(16)), asfloat(_19.Load(20)), asfloat(_19.Load(24))))); + ResType _371; + _371._m0 = frexp(asfloat(_19.Load(16)), _371._m1); + int itmp = _371._m1; + _19.Store(0, asuint(_371._m0)); + _19.Store(0, asuint(ldexp(asfloat(_19.Load(16)), itmp))); + _19.Store(8, SPIRV_Cross_packSnorm4x8(asfloat(_19.Load4(16)))); + _19.Store(8, SPIRV_Cross_packUnorm4x8(asfloat(_19.Load4(16)))); + _19.Store(8, SPIRV_Cross_packSnorm2x16(asfloat(_19.Load4(16)).xy)); + _19.Store(8, SPIRV_Cross_packUnorm2x16(asfloat(_19.Load4(16)).xy)); + _19.Store(8, SPIRV_Cross_packHalf2x16(asfloat(_19.Load4(16)).xy)); + v2 = SPIRV_Cross_unpackSnorm2x16(_19.Load(48)); + v2 = SPIRV_Cross_unpackUnorm2x16(_19.Load(48)); + v2 = SPIRV_Cross_unpackHalf2x16(_19.Load(48)); + float4 v4 = SPIRV_Cross_unpackSnorm4x8(_19.Load(48)); + v4 = SPIRV_Cross_unpackUnorm4x8(_19.Load(48)); + _19.Store4(32, uint4(firstbitlow(int4(_19.Load4(32))))); + _19.Store4(32, uint4(int4(firstbitlow(_19.Load4(48))))); + _19.Store4(32, uint4(firstbithigh(int4(_19.Load4(32))))); + _19.Store4(32, uint4(int4(firstbithigh(_19.Load4(48))))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/illegal-struct-name.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/illegal-struct-name.asm.comp new file mode 100644 index 0000000..dc972bd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/illegal-struct-name.asm.comp @@ -0,0 +1,22 @@ +struct Foo +{ + float _abs; +}; + +RWByteAddressBuffer _7 : register(u0); + +void comp_main() +{ + Foo _24; + _24._abs = asfloat(_7.Load(0)); + Foo f; + f._abs = _24._abs; + int _abs = 10; + _7.Store(4, asuint(f._abs)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/subgroups.invalid.nofxc.sm60.comp b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/subgroups.invalid.nofxc.sm60.comp new file mode 100644 index 0000000..0957a70 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/comp/subgroups.invalid.nofxc.sm60.comp @@ -0,0 +1,95 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _9 : register(u0, space0); + +static uint4 gl_SubgroupEqMask; +static uint4 gl_SubgroupGeMask; +static uint4 gl_SubgroupGtMask; +static uint4 gl_SubgroupLeMask; +static uint4 gl_SubgroupLtMask; +void comp_main() +{ + _9.Store(0, asuint(float(WaveGetLaneCount()))); + _9.Store(0, asuint(float(WaveGetLaneIndex()))); + bool elected = WaveIsFirstLane(); + _9.Store(0, asuint(float4(gl_SubgroupEqMask).x)); + _9.Store(0, asuint(float4(gl_SubgroupGeMask).x)); + _9.Store(0, asuint(float4(gl_SubgroupGtMask).x)); + _9.Store(0, asuint(float4(gl_SubgroupLeMask).x)); + _9.Store(0, asuint(float4(gl_SubgroupLtMask).x)); + float4 broadcasted = WaveReadLaneAt(10.0f.xxxx, 8u); + float3 first = WaveReadLaneFirst(20.0f.xxx); + uint4 ballot_value = WaveActiveBallot(true); + uint bit_count = countbits(ballot_value.x) + countbits(ballot_value.y) + countbits(ballot_value.z) + countbits(ballot_value.w); + bool has_all = WaveActiveAllTrue(true); + bool has_any = WaveActiveAnyTrue(true); + bool has_equal = WaveActiveAllEqualBool(true); + float4 added = WaveActiveSum(20.0f.xxxx); + int4 iadded = WaveActiveSum(int4(20, 20, 20, 20)); + float4 multiplied = WaveActiveProduct(20.0f.xxxx); + int4 imultiplied = WaveActiveProduct(int4(20, 20, 20, 20)); + float4 lo = WaveActiveMin(20.0f.xxxx); + float4 hi = WaveActiveMax(20.0f.xxxx); + int4 slo = WaveActiveMin(int4(20, 20, 20, 20)); + int4 shi = WaveActiveMax(int4(20, 20, 20, 20)); + uint4 ulo = WaveActiveMin(uint4(20u, 20u, 20u, 20u)); + uint4 uhi = WaveActiveMax(uint4(20u, 20u, 20u, 20u)); + uint4 anded = WaveActiveBitAnd(ballot_value); + uint4 ored = WaveActiveBitOr(ballot_value); + uint4 xored = WaveActiveBitXor(ballot_value); + added = WavePrefixSum(added) + added; + iadded = WavePrefixSum(iadded) + iadded; + multiplied = WavePrefixProduct(multiplied) * multiplied; + imultiplied = WavePrefixProduct(imultiplied) * imultiplied; + added = WavePrefixSum(multiplied); + multiplied = WavePrefixProduct(multiplied); + iadded = WavePrefixSum(imultiplied); + imultiplied = WavePrefixProduct(imultiplied); + float4 swap_horiz = QuadReadAcrossX(20.0f.xxxx); + float4 swap_vertical = QuadReadAcrossY(20.0f.xxxx); + float4 swap_diagonal = QuadReadAcrossDiagonal(20.0f.xxxx); + float4 quad_broadcast = QuadReadLaneAt(20.0f.xxxx, 3u); +} + +[numthreads(1, 1, 1)] +void main() +{ + gl_SubgroupEqMask = 1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96)); + if (WaveGetLaneIndex() >= 32) gl_SubgroupEqMask.x = 0; + if (WaveGetLaneIndex() >= 64 || WaveGetLaneIndex() < 32) gl_SubgroupEqMask.y = 0; + if (WaveGetLaneIndex() >= 96 || WaveGetLaneIndex() < 64) gl_SubgroupEqMask.z = 0; + if (WaveGetLaneIndex() < 96) gl_SubgroupEqMask.w = 0; + gl_SubgroupGeMask = ~((1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u); + if (WaveGetLaneIndex() >= 32) gl_SubgroupGeMask.x = 0u; + if (WaveGetLaneIndex() >= 64) gl_SubgroupGeMask.y = 0u; + if (WaveGetLaneIndex() >= 96) gl_SubgroupGeMask.z = 0u; + if (WaveGetLaneIndex() < 32) gl_SubgroupGeMask.y = ~0u; + if (WaveGetLaneIndex() < 64) gl_SubgroupGeMask.z = ~0u; + if (WaveGetLaneIndex() < 96) gl_SubgroupGeMask.w = ~0u; + uint gt_lane_index = WaveGetLaneIndex() + 1; + gl_SubgroupGtMask = ~((1u << (gt_lane_index - uint4(0, 32, 64, 96))) - 1u); + if (gt_lane_index >= 32) gl_SubgroupGtMask.x = 0u; + if (gt_lane_index >= 64) gl_SubgroupGtMask.y = 0u; + if (gt_lane_index >= 96) gl_SubgroupGtMask.z = 0u; + if (gt_lane_index >= 128) gl_SubgroupGtMask.w = 0u; + if (gt_lane_index < 32) gl_SubgroupGtMask.y = ~0u; + if (gt_lane_index < 64) gl_SubgroupGtMask.z = ~0u; + if (gt_lane_index < 96) gl_SubgroupGtMask.w = ~0u; + uint le_lane_index = WaveGetLaneIndex() + 1; + gl_SubgroupLeMask = (1u << (le_lane_index - uint4(0, 32, 64, 96))) - 1u; + if (le_lane_index >= 32) gl_SubgroupLeMask.x = ~0u; + if (le_lane_index >= 64) gl_SubgroupLeMask.y = ~0u; + if (le_lane_index >= 96) gl_SubgroupLeMask.z = ~0u; + if (le_lane_index >= 128) gl_SubgroupLeMask.w = ~0u; + if (le_lane_index < 32) gl_SubgroupLeMask.y = 0u; + if (le_lane_index < 64) gl_SubgroupLeMask.z = 0u; + if (le_lane_index < 96) gl_SubgroupLeMask.w = 0u; + gl_SubgroupLtMask = (1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u; + if (WaveGetLaneIndex() >= 32) gl_SubgroupLtMask.x = ~0u; + if (WaveGetLaneIndex() >= 64) gl_SubgroupLtMask.y = ~0u; + if (WaveGetLaneIndex() >= 96) gl_SubgroupLtMask.z = ~0u; + if (WaveGetLaneIndex() < 32) gl_SubgroupLtMask.y = 0u; + if (WaveGetLaneIndex() < 64) gl_SubgroupLtMask.z = 0u; + if (WaveGetLaneIndex() < 96) gl_SubgroupLtMask.w = 0u; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/cbuffer-packing-straddle.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/cbuffer-packing-straddle.frag new file mode 100644 index 0000000..2b8ec81 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/cbuffer-packing-straddle.frag @@ -0,0 +1,56 @@ +cbuffer UBO : register(b0) +{ + float4 _18_a[2] : packoffset(c0); + float4 _18_b : packoffset(c2); + float4 _18_c : packoffset(c3); + row_major float4x4 _18_d : packoffset(c4); + float _18_e : packoffset(c8); + float2 _18_f : packoffset(c8.z); + float _18_g : packoffset(c9); + float2 _18_h : packoffset(c9.z); + float _18_i : packoffset(c10); + float2 _18_j : packoffset(c10.z); + float _18_k : packoffset(c11); + float2 _18_l : packoffset(c11.z); + float _18_m : packoffset(c12); + float _18_n : packoffset(c12.y); + float _18_o : packoffset(c12.z); + float4 _18_p : packoffset(c13); + float4 _18_q : packoffset(c14); + float3 _18_r : packoffset(c15); + float4 _18_s : packoffset(c16); + float4 _18_t : packoffset(c17); + float4 _18_u : packoffset(c18); + float _18_v : packoffset(c19); + float _18_w : packoffset(c19.y); + float _18_x : packoffset(c19.z); + float _18_y : packoffset(c19.w); + float _18_z : packoffset(c20); + float _18_aa : packoffset(c20.y); + float _18_ab : packoffset(c20.z); + float _18_ac : packoffset(c20.w); + float _18_ad : packoffset(c21); + float _18_ae : packoffset(c21.y); + float4 _18_ef : packoffset(c22); +}; + + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _18_a[1]; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/constant-buffer-array.invalid.sm51.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/constant-buffer-array.invalid.sm51.frag new file mode 100644 index 0000000..d330706 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/constant-buffer-array.invalid.sm51.frag @@ -0,0 +1,44 @@ +struct CBO_1 +{ + float4 a; + float4 b; + float4 c; + float4 d; +}; + +ConstantBuffer cbo[2][4] : register(b4, space0); +cbuffer PushMe +{ + float4 push_a : packoffset(c0); + float4 push_b : packoffset(c1); + float4 push_c : packoffset(c2); + float4 push_d : packoffset(c3); +}; + + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = cbo[1][2].a; + FragColor += cbo[1][2].b; + FragColor += cbo[1][2].c; + FragColor += cbo[1][2].d; + FragColor += push_a; + FragColor += push_b; + FragColor += push_c; + FragColor += push_d; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/fp16.invalid.desktop.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/fp16.invalid.desktop.frag new file mode 100644 index 0000000..e10d672 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/fp16.invalid.desktop.frag @@ -0,0 +1,179 @@ +static min16float4 v4; +static min16float3 v3; +static min16float v1; +static min16float2 v2; +static float o1; +static float2 o2; +static float3 o3; +static float4 o4; + +struct SPIRV_Cross_Input +{ + min16float v1 : TEXCOORD0; + min16float2 v2 : TEXCOORD1; + min16float3 v3 : TEXCOORD2; + min16float4 v4 : TEXCOORD3; +}; + +struct SPIRV_Cross_Output +{ + float o1 : SV_Target0; + float2 o2 : SV_Target1; + float3 o3 : SV_Target2; + float4 o4 : SV_Target3; +}; + +float mod(float x, float y) +{ + return x - y * floor(x / y); +} + +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x / y); +} + +float3 mod(float3 x, float3 y) +{ + return x - y * floor(x / y); +} + +float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} + +uint SPIRV_Cross_packFloat2x16(min16float2 value) +{ + uint2 Packed = f32tof16(value); + return Packed.x | (Packed.y << 16); +} + +min16float2 SPIRV_Cross_unpackFloat2x16(uint value) +{ + return min16float2(f16tof32(uint2(value & 0xffff, value >> 16))); +} + +void test_constants() +{ + min16float a = min16float(1.0); + min16float b = min16float(1.5); + min16float c = min16float(-1.5); + min16float d = min16float(0.0 / 0.0); + min16float e = min16float(1.0 / 0.0); + min16float f = min16float(-1.0 / 0.0); + min16float g = min16float(1014.0); + min16float h = min16float(9.5367431640625e-07); +} + +min16float test_result() +{ + return min16float(1.0); +} + +void test_conversions() +{ + min16float one = test_result(); + int a = int(one); + uint b = uint(one); + bool c = one != min16float(0.0); + float d = float(one); + double e = double(one); + min16float a2 = min16float(a); + min16float b2 = min16float(b); + min16float c2 = min16float(c); + min16float d2 = min16float(d); + min16float e2 = min16float(e); +} + +void test_builtins() +{ + min16float4 res = radians(v4); + res = degrees(v4); + res = sin(v4); + res = cos(v4); + res = tan(v4); + res = asin(v4); + res = atan2(v4, v3.xyzz); + res = atan(v4); + res = sinh(v4); + res = cosh(v4); + res = tanh(v4); + res = pow(v4, v4); + res = exp(v4); + res = log(v4); + res = exp2(v4); + res = log2(v4); + res = sqrt(v4); + res = rsqrt(v4); + res = abs(v4); + res = sign(v4); + res = floor(v4); + res = trunc(v4); + res = round(v4); + res = ceil(v4); + res = frac(v4); + res = mod(v4, v4); + min16float4 tmp; + min16float4 _144 = modf(v4, tmp); + res = _144; + res = min(v4, v4); + res = max(v4, v4); + res = clamp(v4, v4, v4); + res = lerp(v4, v4, v4); + bool4 _164 = bool4(v4.x < v4.x, v4.y < v4.y, v4.z < v4.z, v4.w < v4.w); + res = min16float4(_164.x ? v4.x : v4.x, _164.y ? v4.y : v4.y, _164.z ? v4.z : v4.z, _164.w ? v4.w : v4.w); + res = step(v4, v4); + res = smoothstep(v4, v4, v4); + bool4 btmp = isnan(v4); + btmp = isinf(v4); + res = mad(v4, v4, v4); + uint pack0 = SPIRV_Cross_packFloat2x16(v4.xy); + uint pack1 = SPIRV_Cross_packFloat2x16(v4.zw); + res = min16float4(SPIRV_Cross_unpackFloat2x16(pack0), SPIRV_Cross_unpackFloat2x16(pack1)); + min16float t0 = length(v4); + t0 = distance(v4, v4); + t0 = dot(v4, v4); + min16float3 res3 = cross(v3, v3); + res = normalize(v4); + res = faceforward(v4, v4, v4); + res = reflect(v4, v4); + res = refract(v4, v4, v1); + btmp = bool4(v4.x < v4.x, v4.y < v4.y, v4.z < v4.z, v4.w < v4.w); + btmp = bool4(v4.x <= v4.x, v4.y <= v4.y, v4.z <= v4.z, v4.w <= v4.w); + btmp = bool4(v4.x > v4.x, v4.y > v4.y, v4.z > v4.z, v4.w > v4.w); + btmp = bool4(v4.x >= v4.x, v4.y >= v4.y, v4.z >= v4.z, v4.w >= v4.w); + btmp = bool4(v4.x == v4.x, v4.y == v4.y, v4.z == v4.z, v4.w == v4.w); + btmp = bool4(v4.x != v4.x, v4.y != v4.y, v4.z != v4.z, v4.w != v4.w); + res = ddx(v4); + res = ddy(v4); + res = ddx_fine(v4); + res = ddy_fine(v4); + res = ddx_coarse(v4); + res = ddy_coarse(v4); + res = fwidth(v4); + res = fwidth(v4); + res = fwidth(v4); +} + +void frag_main() +{ + test_constants(); + test_conversions(); + test_builtins(); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + v4 = stage_input.v4; + v3 = stage_input.v3; + v1 = stage_input.v1; + v2 = stage_input.v2; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.o1 = o1; + stage_output.o2 = o2; + stage_output.o3 = o3; + stage_output.o4 = o4; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/frag-coord.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/frag-coord.frag new file mode 100644 index 0000000..17cb4c4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/frag-coord.frag @@ -0,0 +1,27 @@ +static float4 gl_FragCoord; +static float3 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float3 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = gl_FragCoord.xyz / gl_FragCoord.w.xxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/native-16bit-types.fxconly.nofxc.sm62.native-16bit.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/native-16bit-types.fxconly.nofxc.sm62.native-16bit.frag new file mode 100644 index 0000000..020831d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/native-16bit-types.fxconly.nofxc.sm62.native-16bit.frag @@ -0,0 +1,79 @@ +RWByteAddressBuffer _62 : register(u0, space0); + +static float4 gl_FragCoord; +static half4 Output; +static half4 Input; +static int16_t4 OutputI; +static int16_t4 InputI; +static uint16_t4 OutputU; +static uint16_t4 InputU; + +struct SPIRV_Cross_Input +{ + half4 Input : TEXCOORD0; + nointerpolation int16_t4 InputI : TEXCOORD1; + nointerpolation uint16_t4 InputU : TEXCOORD2; + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + half4 Output : SV_Target0; + int16_t4 OutputI : SV_Target1; + uint16_t4 OutputU : SV_Target2; +}; + +void frag_main() +{ + int index = int(gl_FragCoord.x); + Output = Input + half(20.0).xxxx; + OutputI = InputI + int16_t4(int16_t(-40), int16_t(-40), int16_t(-40), int16_t(-40)); + OutputU = InputU + uint16_t4(20u, 20u, 20u, 20u); + Output += _62.Load(index * 2 + 0).xxxx; + OutputI += _62.Load(index * 2 + 8).xxxx; + OutputU += _62.Load(index * 2 + 16).xxxx; + Output += _62.Load(index * 8 + 24); + OutputI += _62.Load(index * 8 + 56); + OutputU += _62.Load(index * 8 + 88); + Output += _62.Load(index * 16 + 128).xyzz; + Output += half3(_62.Load(index * 12 + 186), _62.Load(index * 12 + 190), _62.Load(index * 12 + 194)).xyzz; + half2x3 _128 = half2x3(_62.Load(index * 16 + 120), _62.Load(index * 16 + 128)); + half2x3 m0 = _128; + half2x3 _132 = half2x3(_62.Load(index * 12 + 184), _62.Load(index * 12 + 188), _62.Load(index * 12 + 192), _62.Load(index * 12 + 186), _62.Load(index * 12 + 190), _62.Load(index * 12 + 194)); + half2x3 m1 = _132; + _62.Store(index * 2 + 0, Output.x); + _62.Store(index * 2 + 8, OutputI.y); + _62.Store(index * 2 + 16, OutputU.z); + _62.Store(index * 8 + 24, Output); + _62.Store(index * 8 + 56, OutputI); + _62.Store(index * 8 + 88, OutputU); + _62.Store(index * 16 + 128, Output.xyz); + _62.Store(index * 12 + 186, Output.x); + _62.Store(index * 12 + 190, Output.xyz.y); + _62.Store(index * 12 + 194, Output.xyz.z); + half2x3 _182 = half2x3(half3(Output.xyz), half3(Output.wzy)); + _62.Store(index * 16 + 120, _182[0]); + _62.Store(index * 16 + 128, _182[1]); + half2x3 _197 = half2x3(half3(Output.xyz), half3(Output.wzy)); + _62.Store(index * 12 + 184, _197[0].x); + _62.Store(index * 12 + 186, _197[1].x); + _62.Store(index * 12 + 188, _197[0].y); + _62.Store(index * 12 + 190, _197[1].y); + _62.Store(index * 12 + 192, _197[0].z); + _62.Store(index * 12 + 194, _197[1].z); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + Input = stage_input.Input; + InputI = stage_input.InputI; + InputU = stage_input.InputU; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.Output = Output; + stage_output.OutputI = OutputI; + stage_output.OutputU = OutputU; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/pixel-interlock-simple-callstack.sm51.fxconly.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/pixel-interlock-simple-callstack.sm51.fxconly.frag new file mode 100644 index 0000000..aace6f5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/pixel-interlock-simple-callstack.sm51.fxconly.frag @@ -0,0 +1,33 @@ +RasterizerOrderedByteAddressBuffer _14 : register(u1, space0); +RasterizerOrderedByteAddressBuffer _35 : register(u0, space0); + +static float4 gl_FragCoord; +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +void callee2() +{ + int _25 = int(gl_FragCoord.x); + _14.Store(_25 * 4 + 0, _14.Load(_25 * 4 + 0) + 1u); +} + +void callee() +{ + int _38 = int(gl_FragCoord.x); + _35.Store(_38 * 4 + 0, _35.Load(_38 * 4 + 0) + 1u); + callee2(); +} + +void frag_main() +{ + callee(); +} + +void main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/spec-constant.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/spec-constant.frag new file mode 100644 index 0000000..89a5f5c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/spec-constant.frag @@ -0,0 +1,142 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 1.0f +#endif +static const float a = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 2.0f +#endif +static const float b = SPIRV_CROSS_CONSTANT_ID_2; +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 3 +#endif +static const int c = SPIRV_CROSS_CONSTANT_ID_3; +static const uint _18 = (uint(c) + 0u); +static const int _21 = (-c); +static const int _23 = (~c); +#ifndef SPIRV_CROSS_CONSTANT_ID_4 +#define SPIRV_CROSS_CONSTANT_ID_4 4 +#endif +static const int d = SPIRV_CROSS_CONSTANT_ID_4; +static const int _26 = (c + d); +static const int _28 = (c - d); +static const int _30 = (c * d); +static const int _32 = (c / d); +#ifndef SPIRV_CROSS_CONSTANT_ID_5 +#define SPIRV_CROSS_CONSTANT_ID_5 5u +#endif +static const uint e = SPIRV_CROSS_CONSTANT_ID_5; +#ifndef SPIRV_CROSS_CONSTANT_ID_6 +#define SPIRV_CROSS_CONSTANT_ID_6 6u +#endif +static const uint f = SPIRV_CROSS_CONSTANT_ID_6; +static const uint _36 = (e / f); +static const int _38 = (c % d); +static const uint _40 = (e % f); +static const int _42 = (c >> d); +static const uint _44 = (e >> f); +static const int _46 = (c << d); +static const int _48 = (c | d); +static const int _50 = (c ^ d); +static const int _52 = (c & d); +#ifndef SPIRV_CROSS_CONSTANT_ID_7 +#define SPIRV_CROSS_CONSTANT_ID_7 false +#endif +static const bool g = SPIRV_CROSS_CONSTANT_ID_7; +#ifndef SPIRV_CROSS_CONSTANT_ID_8 +#define SPIRV_CROSS_CONSTANT_ID_8 true +#endif +static const bool h = SPIRV_CROSS_CONSTANT_ID_8; +static const bool _58 = (g || h); +static const bool _60 = (g && h); +static const bool _62 = (!g); +static const bool _64 = (g == h); +static const bool _66 = (g != h); +static const bool _68 = (c == d); +static const bool _70 = (c != d); +static const bool _72 = (c < d); +static const bool _74 = (e < f); +static const bool _76 = (c > d); +static const bool _78 = (e > f); +static const bool _80 = (c <= d); +static const bool _82 = (e <= f); +static const bool _84 = (c >= d); +static const bool _86 = (e >= f); +static const int _92 = int(e + 0u); +static const bool _94 = (c != int(0u)); +static const bool _96 = (e != 0u); +static const int _100 = int(g); +static const uint _103 = uint(g); +static const int _111 = (c + 3); +static const int _118 = (c + 2); +static const int _124 = (d + 2); + +struct Foo +{ + float elems[_124]; +}; + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float t0 = a; + float t1 = b; + uint c0 = _18; + int c1 = _21; + int c2 = _23; + int c3 = _26; + int c4 = _28; + int c5 = _30; + int c6 = _32; + uint c7 = _36; + int c8 = _38; + uint c9 = _40; + int c10 = _42; + uint c11 = _44; + int c12 = _46; + int c13 = _48; + int c14 = _50; + int c15 = _52; + bool c16 = _58; + bool c17 = _60; + bool c18 = _62; + bool c19 = _64; + bool c20 = _66; + bool c21 = _68; + bool c22 = _70; + bool c23 = _72; + bool c24 = _74; + bool c25 = _76; + bool c26 = _78; + bool c27 = _80; + bool c28 = _82; + bool c29 = _84; + bool c30 = _86; + int c31 = c8 + c3; + int c32 = _92; + bool c33 = _94; + bool c34 = _96; + int c35 = _100; + uint c36 = _103; + float c37 = float(g); + float vec0[_111][8]; + vec0[0][0] = 10.0f; + float vec1[_118]; + vec1[0] = 20.0f; + Foo foo; + foo.elems[c] = 10.0f; + FragColor = (((t0 + t1).xxxx + vec0[0][0].xxxx) + vec1[0].xxxx) + foo.elems[c].xxxx; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/ubo-offset-out-of-order.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/ubo-offset-out-of-order.frag new file mode 100644 index 0000000..4ea3e0d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/ubo-offset-out-of-order.frag @@ -0,0 +1,33 @@ +cbuffer UBO : register(b0) +{ + row_major float4x4 _13_m : packoffset(c1); + float4 _13_v : packoffset(c0); +}; + + +static float4 FragColor; +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = mul(vColor, _13_m) + _13_v; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/variables.zero-initialize.frag b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/variables.zero-initialize.frag new file mode 100644 index 0000000..7fe47df --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/frag/variables.zero-initialize.frag @@ -0,0 +1,45 @@ +struct Foo +{ + int a; +}; + +static float4 vColor; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +static int uninit_int = 0; +static int4 uninit_vector = int4(0, 0, 0, 0); +static float4x4 uninit_matrix = float4x4(0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx); +static Foo uninit_foo = { 0 }; + +void frag_main() +{ + int uninit_function_int = 0; + if (vColor.x > 10.0f) + { + uninit_function_int = 10; + } + else + { + uninit_function_int = 20; + } + FragColor = vColor; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/vert/flatten-matrix-input.flatten-matrix-vertex-input.vert b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/vert/flatten-matrix-input.flatten-matrix-vertex-input.vert new file mode 100644 index 0000000..906b0ed --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/vert/flatten-matrix-input.flatten-matrix-vertex-input.vert @@ -0,0 +1,51 @@ +static float4 gl_Position; +static float4x4 m4; +static float4 v; +static float3x3 m3; +static float2x2 m2; + +struct SPIRV_Cross_Input +{ + float4 m4_0 : TEXCOORD0; + float4 m4_1 : TEXCOORD1; + float4 m4_2 : TEXCOORD2; + float4 m4_3 : TEXCOORD3; + float3 m3_0 : TEXCOORD4; + float3 m3_1 : TEXCOORD5; + float3 m3_2 : TEXCOORD6; + float2 m2_0 : TEXCOORD7; + float2 m2_1 : TEXCOORD8; + float4 v : TEXCOORD9; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = mul(v, m4); + float3 _37 = gl_Position.xyz + mul(v.xyz, m3); + gl_Position = float4(_37.x, _37.y, _37.z, gl_Position.w); + float2 _52 = gl_Position.xy + mul(v.xy, m2); + gl_Position = float4(_52.x, _52.y, gl_Position.z, gl_Position.w); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + m4[0] = stage_input.m4_0; + m4[1] = stage_input.m4_1; + m4[2] = stage_input.m4_2; + m4[3] = stage_input.m4_3; + v = stage_input.v; + m3[0] = stage_input.m3_0; + m3[1] = stage_input.m3_1; + m3[2] = stage_input.m3_2; + m2[0] = stage_input.m2_0; + m2[1] = stage_input.m2_1; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl-no-opt/vert/pass-array-by-value.vert b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/vert/pass-array-by-value.vert new file mode 100644 index 0000000..20afdb5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl-no-opt/vert/pass-array-by-value.vert @@ -0,0 +1,48 @@ +static const float4 _68[4] = { 0.0f.xxxx, 1.0f.xxxx, 2.0f.xxxx, 3.0f.xxxx }; + +static float4 gl_Position; +static int Index1; +static int Index2; + +struct SPIRV_Cross_Input +{ + int Index1 : TEXCOORD0; + int Index2 : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +float4 consume_constant_arrays2(float4 positions[4], float4 positions2[4]) +{ + float4 indexable[4] = positions; + float4 indexable_1[4] = positions2; + return indexable[Index1] + indexable_1[Index2]; +} + +float4 consume_constant_arrays(float4 positions[4], float4 positions2[4]) +{ + return consume_constant_arrays2(positions, positions2); +} + +void vert_main() +{ + float4 LUT2[4]; + LUT2[0] = 10.0f.xxxx; + LUT2[1] = 11.0f.xxxx; + LUT2[2] = 12.0f.xxxx; + LUT2[3] = 13.0f.xxxx; + gl_Position = consume_constant_arrays(_68, LUT2); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + Index1 = stage_input.Index1; + Index2 = stage_input.Index2; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp new file mode 100644 index 0000000..b8265fc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp @@ -0,0 +1,19 @@ +RWByteAddressBuffer _4 : register(u0); + +void comp_main() +{ + uint _21 = _4.Load(_4.Load(0) * 4 + 4); + for (uint _23 = 0u; _23 < 64u; ) + { + _4.Store(_23 * 4 + 4, 0u); + _23++; + continue; + } + _4.Store(_4.Load(0) * 4 + 4, _21); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..919fe9b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,24 @@ +RWByteAddressBuffer u0_counter : register(u1); +RWBuffer u0 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + uint _29; + u0_counter.InterlockedAdd(0, -1, _29); + float4 r0; + r0.x = asfloat(_29); + u0[(uint(asint(r0.x)) * 1u) + (uint(0) >> 2u)] = uint(int(gl_GlobalInvocationID.x)).x; +} + +[numthreads(4, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..35960b0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,24 @@ +RWByteAddressBuffer u0_counter : register(u1); +RWBuffer u0 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + uint _29; + u0_counter.InterlockedAdd(0, 1, _29); + float4 r0; + r0.x = asfloat(_29); + u0[(uint(asint(r0.x)) * 1u) + (uint(0) >> 2u)] = uint(int(gl_GlobalInvocationID.x)).x; +} + +[numthreads(4, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..35143a4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,28 @@ +RWByteAddressBuffer _5 : register(u0); +RWByteAddressBuffer _6 : register(u1); + +void comp_main() +{ + bool4 _31 = bool4(int(_5.Load4(16).x) < int4(_5.Load4(0)).x, int(_5.Load4(16).y) < int4(_5.Load4(0)).y, int(_5.Load4(16).z) < int4(_5.Load4(0)).z, int(_5.Load4(16).w) < int4(_5.Load4(0)).w); + bool4 _32 = bool4(int(_5.Load4(16).x) <= int4(_5.Load4(0)).x, int(_5.Load4(16).y) <= int4(_5.Load4(0)).y, int(_5.Load4(16).z) <= int4(_5.Load4(0)).z, int(_5.Load4(16).w) <= int4(_5.Load4(0)).w); + bool4 _33 = bool4(_5.Load4(16).x < uint(int4(_5.Load4(0)).x), _5.Load4(16).y < uint(int4(_5.Load4(0)).y), _5.Load4(16).z < uint(int4(_5.Load4(0)).z), _5.Load4(16).w < uint(int4(_5.Load4(0)).w)); + bool4 _34 = bool4(_5.Load4(16).x <= uint(int4(_5.Load4(0)).x), _5.Load4(16).y <= uint(int4(_5.Load4(0)).y), _5.Load4(16).z <= uint(int4(_5.Load4(0)).z), _5.Load4(16).w <= uint(int4(_5.Load4(0)).w)); + bool4 _35 = bool4(int(_5.Load4(16).x) > int4(_5.Load4(0)).x, int(_5.Load4(16).y) > int4(_5.Load4(0)).y, int(_5.Load4(16).z) > int4(_5.Load4(0)).z, int(_5.Load4(16).w) > int4(_5.Load4(0)).w); + bool4 _36 = bool4(int(_5.Load4(16).x) >= int4(_5.Load4(0)).x, int(_5.Load4(16).y) >= int4(_5.Load4(0)).y, int(_5.Load4(16).z) >= int4(_5.Load4(0)).z, int(_5.Load4(16).w) >= int4(_5.Load4(0)).w); + bool4 _37 = bool4(_5.Load4(16).x > uint(int4(_5.Load4(0)).x), _5.Load4(16).y > uint(int4(_5.Load4(0)).y), _5.Load4(16).z > uint(int4(_5.Load4(0)).z), _5.Load4(16).w > uint(int4(_5.Load4(0)).w)); + bool4 _38 = bool4(_5.Load4(16).x >= uint(int4(_5.Load4(0)).x), _5.Load4(16).y >= uint(int4(_5.Load4(0)).y), _5.Load4(16).z >= uint(int4(_5.Load4(0)).z), _5.Load4(16).w >= uint(int4(_5.Load4(0)).w)); + _6.Store4(0, uint4(_31.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _31.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _31.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _31.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_32.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _32.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _32.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _32.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_33.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _33.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _33.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _33.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_34.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _34.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _34.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _34.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_35.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _35.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _35.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _35.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_36.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _36.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _36.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _36.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_37.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _37.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _37.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _37.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); + _6.Store4(0, uint4(_38.x ? uint4(1u, 1u, 1u, 1u).x : uint4(0u, 0u, 0u, 0u).x, _38.y ? uint4(1u, 1u, 1u, 1u).y : uint4(0u, 0u, 0u, 0u).y, _38.z ? uint4(1u, 1u, 1u, 1u).z : uint4(0u, 0u, 0u, 0u).z, _38.w ? uint4(1u, 1u, 1u, 1u).w : uint4(0u, 0u, 0u, 0u).w)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..e184e03 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,39 @@ +struct A +{ + int a; + int b; +}; + +RWByteAddressBuffer C1 : register(u1); +cbuffer C2 : register(b2) +{ + A C2_1_Data[1024] : packoffset(c0); +}; + +RWByteAddressBuffer C3 : register(u0); +cbuffer B : register(b3) +{ + A C4_Data[1024] : packoffset(c0); +}; + + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + C1.Store(gl_GlobalInvocationID.x * 8 + 0, uint(C2_1_Data[gl_GlobalInvocationID.x].a)); + C1.Store(gl_GlobalInvocationID.x * 8 + 4, uint(C2_1_Data[gl_GlobalInvocationID.x].b)); + C3.Store(gl_GlobalInvocationID.x * 8 + 0, uint(C4_Data[gl_GlobalInvocationID.x].a)); + C3.Store(gl_GlobalInvocationID.x * 8 + 4, uint(C4_Data[gl_GlobalInvocationID.x].b)); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000..9700100 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,41 @@ +RWByteAddressBuffer bar : register(u0); +RWByteAddressBuffer foo : register(u1); + +void _main() +{ + [unroll] + for (int i = 0; i < 16; i++) + { + bar.Store4(i * 16 + 0, asuint(asfloat(foo.Load4(i * 16 + 0)))); + } + [loop] + for (int i_1 = 0; i_1 < 16; i_1++) + { + bar.Store4((15 - i_1) * 16 + 0, asuint(asfloat(foo.Load4(i_1 * 16 + 0)))); + } + float v = asfloat(bar.Load(160)); + float w = asfloat(foo.Load(160)); + [branch] + if (v > 10.0f) + { + foo.Store4(320, asuint(5.0f.xxxx)); + } + float value = 20.0f; + [flatten] + if (w > 40.0f) + { + value = 20.0f; + } + foo.Store4(320, asuint(value.xxxx)); +} + +void comp_main() +{ + _main(); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..44bde09 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,33 @@ +ByteAddressBuffer ssbo : register(t1); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void Load(uint size) +{ + int byteAddrTemp = int(size >> uint(2)); + uint4 data = uint4(ssbo.Load(byteAddrTemp * 4 + 0), ssbo.Load((byteAddrTemp + 1) * 4 + 0), ssbo.Load((byteAddrTemp + 2) * 4 + 0), ssbo.Load((byteAddrTemp + 3) * 4 + 0)); +} + +void _main(uint3 id) +{ + uint param = 4u; + Load(param); +} + +void comp_main() +{ + uint3 id = gl_GlobalInvocationID; + uint3 param = id; + _main(param); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp new file mode 100644 index 0000000..a6e8868 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp @@ -0,0 +1,84 @@ +RWByteAddressBuffer _4 : register(u0); + +void comp_main() +{ + _4.Store(0, asuint(isnan(asfloat(_4.Load(96))) ? asfloat(_4.Load(48)) : (isnan(asfloat(_4.Load(48))) ? asfloat(_4.Load(96)) : min(asfloat(_4.Load(48)), asfloat(_4.Load(96)))))); + bool2 _145 = isnan(asfloat(_4.Load2(56))); + bool2 _146 = isnan(asfloat(_4.Load2(104))); + float2 _147 = min(asfloat(_4.Load2(56)), asfloat(_4.Load2(104))); + float2 _148 = float2(_145.x ? asfloat(_4.Load2(104)).x : _147.x, _145.y ? asfloat(_4.Load2(104)).y : _147.y); + _4.Store2(8, asuint(float2(_146.x ? asfloat(_4.Load2(56)).x : _148.x, _146.y ? asfloat(_4.Load2(56)).y : _148.y))); + bool3 _150 = isnan(asfloat(_4.Load3(64))); + bool3 _151 = isnan(asfloat(_4.Load3(112))); + float3 _152 = min(asfloat(_4.Load3(64)), asfloat(_4.Load3(112))); + float3 _153 = float3(_150.x ? asfloat(_4.Load3(112)).x : _152.x, _150.y ? asfloat(_4.Load3(112)).y : _152.y, _150.z ? asfloat(_4.Load3(112)).z : _152.z); + _4.Store3(16, asuint(float3(_151.x ? asfloat(_4.Load3(64)).x : _153.x, _151.y ? asfloat(_4.Load3(64)).y : _153.y, _151.z ? asfloat(_4.Load3(64)).z : _153.z))); + bool4 _155 = isnan(asfloat(_4.Load4(80))); + bool4 _156 = isnan(asfloat(_4.Load4(128))); + float4 _157 = min(asfloat(_4.Load4(80)), asfloat(_4.Load4(128))); + float4 _158 = float4(_155.x ? asfloat(_4.Load4(128)).x : _157.x, _155.y ? asfloat(_4.Load4(128)).y : _157.y, _155.z ? asfloat(_4.Load4(128)).z : _157.z, _155.w ? asfloat(_4.Load4(128)).w : _157.w); + _4.Store4(32, asuint(float4(_156.x ? asfloat(_4.Load4(80)).x : _158.x, _156.y ? asfloat(_4.Load4(80)).y : _158.y, _156.z ? asfloat(_4.Load4(80)).z : _158.z, _156.w ? asfloat(_4.Load4(80)).w : _158.w))); + _4.Store(0, asuint(isnan(asfloat(_4.Load(96))) ? asfloat(_4.Load(48)) : (isnan(asfloat(_4.Load(48))) ? asfloat(_4.Load(96)) : max(asfloat(_4.Load(48)), asfloat(_4.Load(96)))))); + bool2 _165 = isnan(asfloat(_4.Load2(56))); + bool2 _166 = isnan(asfloat(_4.Load2(104))); + float2 _167 = max(asfloat(_4.Load2(56)), asfloat(_4.Load2(104))); + float2 _168 = float2(_165.x ? asfloat(_4.Load2(104)).x : _167.x, _165.y ? asfloat(_4.Load2(104)).y : _167.y); + _4.Store2(8, asuint(float2(_166.x ? asfloat(_4.Load2(56)).x : _168.x, _166.y ? asfloat(_4.Load2(56)).y : _168.y))); + bool3 _170 = isnan(asfloat(_4.Load3(64))); + bool3 _171 = isnan(asfloat(_4.Load3(112))); + float3 _172 = max(asfloat(_4.Load3(64)), asfloat(_4.Load3(112))); + float3 _173 = float3(_170.x ? asfloat(_4.Load3(112)).x : _172.x, _170.y ? asfloat(_4.Load3(112)).y : _172.y, _170.z ? asfloat(_4.Load3(112)).z : _172.z); + _4.Store3(16, asuint(float3(_171.x ? asfloat(_4.Load3(64)).x : _173.x, _171.y ? asfloat(_4.Load3(64)).y : _173.y, _171.z ? asfloat(_4.Load3(64)).z : _173.z))); + bool4 _175 = isnan(asfloat(_4.Load4(80))); + bool4 _176 = isnan(asfloat(_4.Load4(128))); + float4 _177 = max(asfloat(_4.Load4(80)), asfloat(_4.Load4(128))); + float4 _178 = float4(_175.x ? asfloat(_4.Load4(128)).x : _177.x, _175.y ? asfloat(_4.Load4(128)).y : _177.y, _175.z ? asfloat(_4.Load4(128)).z : _177.z, _175.w ? asfloat(_4.Load4(128)).w : _177.w); + _4.Store4(32, asuint(float4(_176.x ? asfloat(_4.Load4(80)).x : _178.x, _176.y ? asfloat(_4.Load4(80)).y : _178.y, _176.z ? asfloat(_4.Load4(80)).z : _178.z, _176.w ? asfloat(_4.Load4(80)).w : _178.w))); + float _179 = isnan(asfloat(_4.Load(48))) ? asfloat(_4.Load(0)) : (isnan(asfloat(_4.Load(0))) ? asfloat(_4.Load(48)) : max(asfloat(_4.Load(0)), asfloat(_4.Load(48)))); + _4.Store(0, asuint(isnan(asfloat(_4.Load(96))) ? _179 : (isnan(_179) ? asfloat(_4.Load(96)) : min(_179, asfloat(_4.Load(96)))))); + bool2 _192 = isnan(asfloat(_4.Load2(8))); + bool2 _193 = isnan(asfloat(_4.Load2(56))); + float2 _194 = max(asfloat(_4.Load2(8)), asfloat(_4.Load2(56))); + float2 _195 = float2(_192.x ? asfloat(_4.Load2(56)).x : _194.x, _192.y ? asfloat(_4.Load2(56)).y : _194.y); + float2 _190 = float2(_193.x ? asfloat(_4.Load2(8)).x : _195.x, _193.y ? asfloat(_4.Load2(8)).y : _195.y); + bool2 _197 = isnan(_190); + bool2 _198 = isnan(asfloat(_4.Load2(104))); + float2 _199 = min(_190, asfloat(_4.Load2(104))); + float2 _200 = float2(_197.x ? asfloat(_4.Load2(104)).x : _199.x, _197.y ? asfloat(_4.Load2(104)).y : _199.y); + _4.Store2(8, asuint(float2(_198.x ? _190.x : _200.x, _198.y ? _190.y : _200.y))); + bool3 _203 = isnan(asfloat(_4.Load3(16))); + bool3 _204 = isnan(asfloat(_4.Load3(64))); + float3 _205 = max(asfloat(_4.Load3(16)), asfloat(_4.Load3(64))); + float3 _206 = float3(_203.x ? asfloat(_4.Load3(64)).x : _205.x, _203.y ? asfloat(_4.Load3(64)).y : _205.y, _203.z ? asfloat(_4.Load3(64)).z : _205.z); + float3 _201 = float3(_204.x ? asfloat(_4.Load3(16)).x : _206.x, _204.y ? asfloat(_4.Load3(16)).y : _206.y, _204.z ? asfloat(_4.Load3(16)).z : _206.z); + bool3 _208 = isnan(_201); + bool3 _209 = isnan(asfloat(_4.Load3(112))); + float3 _210 = min(_201, asfloat(_4.Load3(112))); + float3 _211 = float3(_208.x ? asfloat(_4.Load3(112)).x : _210.x, _208.y ? asfloat(_4.Load3(112)).y : _210.y, _208.z ? asfloat(_4.Load3(112)).z : _210.z); + _4.Store3(16, asuint(float3(_209.x ? _201.x : _211.x, _209.y ? _201.y : _211.y, _209.z ? _201.z : _211.z))); + bool4 _214 = isnan(asfloat(_4.Load4(32))); + bool4 _215 = isnan(asfloat(_4.Load4(80))); + float4 _216 = max(asfloat(_4.Load4(32)), asfloat(_4.Load4(80))); + float4 _217 = float4(_214.x ? asfloat(_4.Load4(80)).x : _216.x, _214.y ? asfloat(_4.Load4(80)).y : _216.y, _214.z ? asfloat(_4.Load4(80)).z : _216.z, _214.w ? asfloat(_4.Load4(80)).w : _216.w); + float4 _212 = float4(_215.x ? asfloat(_4.Load4(32)).x : _217.x, _215.y ? asfloat(_4.Load4(32)).y : _217.y, _215.z ? asfloat(_4.Load4(32)).z : _217.z, _215.w ? asfloat(_4.Load4(32)).w : _217.w); + bool4 _219 = isnan(_212); + bool4 _220 = isnan(asfloat(_4.Load4(128))); + float4 _221 = min(_212, asfloat(_4.Load4(128))); + float4 _222 = float4(_219.x ? asfloat(_4.Load4(128)).x : _221.x, _219.y ? asfloat(_4.Load4(128)).y : _221.y, _219.z ? asfloat(_4.Load4(128)).z : _221.z, _219.w ? asfloat(_4.Load4(128)).w : _221.w); + _4.Store4(32, asuint(float4(_220.x ? _212.x : _222.x, _220.y ? _212.y : _222.y, _220.z ? _212.z : _222.z, _220.w ? _212.w : _222.w))); + float _223; + for (int i = 0; i < 2; i++, _223 = isnan(asfloat(_4.Load(56))) ? asfloat(_4.Load(0)) : (isnan(asfloat(_4.Load(0))) ? asfloat(_4.Load(56)) : max(asfloat(_4.Load(0)), asfloat(_4.Load(56)))), _4.Store(0, asuint(isnan(asfloat(_4.Load(60))) ? _223 : (isnan(_223) ? asfloat(_4.Load(60)) : min(_223, asfloat(_4.Load(60))))))) + { + bool2 _235 = isnan(asfloat(_4.Load2(56))); + bool2 _236 = isnan(asfloat(_4.Load2(104))); + float2 _237 = min(asfloat(_4.Load2(56)), asfloat(_4.Load2(104))); + float2 _238 = float2(_235.x ? asfloat(_4.Load2(104)).x : _237.x, _235.y ? asfloat(_4.Load2(104)).y : _237.y); + _4.Store2(8, asuint(float2(_236.x ? asfloat(_4.Load2(56)).x : _238.x, _236.y ? asfloat(_4.Load2(56)).y : _238.y))); + } +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag new file mode 100644 index 0000000..3d5d628 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag @@ -0,0 +1,32 @@ +cbuffer _4_5 : register(b0) +{ + column_major float2x4 _5_m0 : packoffset(c0); + float4 _5_m1 : packoffset(c4); +}; + + +static float2 _3; + +struct SPIRV_Cross_Output +{ + float2 _3 : SV_Target0; +}; + +float2 _23() +{ + float2 _25 = mul(_5_m0, _5_m1); + return _25; +} + +void frag_main() +{ + _3 = _23(); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output._3 = _3; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag new file mode 100644 index 0000000..3951fd5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag @@ -0,0 +1,30 @@ +Texture2D uTex : register(t1); +SamplerState uSampler : register(s0); + +static float4 FragColor; +static float2 vUV; + +struct SPIRV_Cross_Input +{ + float2 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uTex.Sample(uSampler, vUV); + FragColor += uTex.Sample(uSampler, vUV, int2(1, 1)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..38d12cd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/empty-struct.asm.frag @@ -0,0 +1,27 @@ +struct EmptyStructTest +{ + int empty_struct_member; +}; + +float GetValue(EmptyStructTest self) +{ + return 0.0f; +} + +float GetValue_1(EmptyStructTest self) +{ + return 0.0f; +} + +void frag_main() +{ + EmptyStructTest _23 = { 0 }; + EmptyStructTest emptyStruct; + float value = GetValue(emptyStruct); + value = GetValue_1(_23); +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/frem.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/frem.asm.frag new file mode 100644 index 0000000..67998c5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/frem.asm.frag @@ -0,0 +1,29 @@ +static float4 FragColor; +static float4 vA; +static float4 vB; + +struct SPIRV_Cross_Input +{ + float4 vA : TEXCOORD0; + float4 vB : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = fmod(vA, vB); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vA = stage_input.vA; + vB = stage_input.vB; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..e8978b6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,47 @@ +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +float4 foo(float4 foo_1) +{ + return foo_1 + 1.0f.xxxx; +} + +float4 foo(float3 foo_1) +{ + return foo_1.xyzz + 1.0f.xxxx; +} + +float4 foo_1(float4 foo_2) +{ + return foo_2 + 2.0f.xxxx; +} + +float4 foo(float2 foo_2) +{ + return foo_2.xyxy + 2.0f.xxxx; +} + +void frag_main() +{ + float4 foo_3 = 1.0f.xxxx; + float4 foo_2 = foo(foo_3); + float3 foo_5 = 1.0f.xxx; + float4 foo_4 = foo(foo_5); + float4 foo_7 = 1.0f.xxxx; + float4 foo_6 = foo_1(foo_7); + float2 foo_9 = 1.0f.xx; + float4 foo_8 = foo(foo_9); + FragColor = ((foo_2 + foo_4) + foo_6) + foo_8; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..ed53720 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,31 @@ +Texture2D uTexture : register(t0); +SamplerState _uTexture_sampler : register(s0); + +static int2 Size; + +struct SPIRV_Cross_Output +{ + int2 Size : SV_Target0; +}; + +uint2 SPIRV_Cross_textureSize(Texture2D Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(Level, ret.x, ret.y, Param); + return ret; +} + +void frag_main() +{ + uint _19_dummy_parameter; + uint _20_dummy_parameter; + Size = int2(SPIRV_Cross_textureSize(uTexture, uint(0), _19_dummy_parameter)) + int2(SPIRV_Cross_textureSize(uTexture, uint(1), _20_dummy_parameter)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.Size = Size; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..f668d63 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,56 @@ +Texture2D uImage : register(t0); +SamplerState _uImage_sampler : register(s0); + +static float4 v0; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 v0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int i = 0; + float phi; + float4 _36; + phi = 1.0f; + _36 = float4(1.0f, 2.0f, 1.0f, 2.0f); + for (;;) + { + FragColor = _36; + if (i < 4) + { + if (v0[i] > 0.0f) + { + float2 _48 = phi.xx; + i++; + phi += 2.0f; + _36 = uImage.SampleLevel(_uImage_sampler, _48, 0.0f); + continue; + } + else + { + break; + } + } + else + { + break; + } + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + v0 = stage_input.v0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..d20cf99 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,19 @@ +static float3 FragColor; + +struct SPIRV_Cross_Output +{ + float3 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float3(asfloat(0x7f800000u), asfloat(0xff800000u), asfloat(0x7fc00000u)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..f0d99f7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,86 @@ +static float FragColor; +static float vColor; + +struct SPIRV_Cross_Input +{ + float vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +#line 6 "test.frag" +void func() +{ +#line 8 "test.frag" + FragColor = 1.0f; +#line 9 "test.frag" + FragColor = 2.0f; +#line 10 "test.frag" + if (vColor < 0.0f) + { +#line 12 "test.frag" + FragColor = 3.0f; + } + else + { +#line 16 "test.frag" + FragColor = 4.0f; + } +#line 19 "test.frag" + for (int i = 0; float(i) < (40.0f + vColor); i += (int(vColor) + 5)) + { +#line 21 "test.frag" + FragColor += 0.20000000298023223876953125f; +#line 22 "test.frag" + FragColor += 0.300000011920928955078125f; + } +#line 25 "test.frag" + switch (int(vColor)) + { + case 0: + { +#line 28 "test.frag" + FragColor += 0.20000000298023223876953125f; +#line 29 "test.frag" + break; + } + case 1: + { +#line 32 "test.frag" + FragColor += 0.4000000059604644775390625f; +#line 33 "test.frag" + break; + } + default: + { +#line 36 "test.frag" + FragColor += 0.800000011920928955078125f; +#line 37 "test.frag" + break; + } + } + do + { +#line 42 "test.frag" + FragColor += (10.0f + vColor); + } while (FragColor < 100.0f); +} + +#line 46 "test.frag" +void frag_main() +{ +#line 48 "test.frag" + func(); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..a7aec01 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,55 @@ +static const float _46[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f }; +static const float4 _76[4] = { 0.0f.xxxx, 1.0f.xxxx, 8.0f.xxxx, 5.0f.xxxx }; +static const float4 _90[4] = { 20.0f.xxxx, 30.0f.xxxx, 50.0f.xxxx, 60.0f.xxxx }; + +static float FragColor; +static int index; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 foobar[4] = _76; + float4 baz[4] = _76; + FragColor = _46[index]; + if (index < 10) + { + FragColor += _46[index ^ 1]; + } + else + { + FragColor += _46[index & 1]; + } + if (index > 30) + { + FragColor += _76[index & 3].y; + } + else + { + FragColor += _76[index & 1].x; + } + if (index > 30) + { + foobar[1].z = 20.0f; + } + FragColor += foobar[index & 3].z; + baz = _90; + FragColor += baz[index & 3].z; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag new file mode 100644 index 0000000..525a917 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag @@ -0,0 +1,34 @@ +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +uint64_t SPIRV_Cross_packUint2x32(uint2 value) +{ + return (uint64_t(value.y) << 32) | uint64_t(value.x); +} + +uint2 SPIRV_Cross_unpackUint2x32(uint64_t value) +{ + uint2 Unpacked; + Unpacked.x = uint(value & 0xffffffff); + Unpacked.y = uint(value >> 32); + return Unpacked; +} + +void frag_main() +{ + uint64_t _packed = SPIRV_Cross_packUint2x32(uint2(18u, 52u)); + uint2 unpacked = SPIRV_Cross_unpackUint2x32(_packed); + FragColor = float4(float(unpacked.x), float(unpacked.y), 1.0f, 1.0f); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..ab0471a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,30 @@ +cbuffer Registers +{ + float registers_foo : packoffset(c0); +}; + + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +float add_value(float v, float w) +{ + return v + w; +} + +void frag_main() +{ + FragColor = add_value(10.0f, registers_foo); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..41dfc08 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,30 @@ +Texture2D g_Texture : register(t0); +SamplerState g_Sampler : register(s0); +SamplerComparisonState g_CompareSampler : register(s1); + +static float2 in_var_TEXCOORD0; +static float out_var_SV_Target; + +struct SPIRV_Cross_Input +{ + float2 in_var_TEXCOORD0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float out_var_SV_Target : SV_Target0; +}; + +void frag_main() +{ + out_var_SV_Target = g_Texture.Sample(g_Sampler, in_var_TEXCOORD0).x + g_Texture.SampleCmpLevelZero(g_CompareSampler, in_var_TEXCOORD0, 0.5f); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + in_var_TEXCOORD0 = stage_input.in_var_TEXCOORD0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.out_var_SV_Target = out_var_SV_Target; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..9c71d08 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,66 @@ +struct myType +{ + float data; +}; + +static const myType _18 = { 0.0f }; +static const myType _20 = { 1.0f }; +static const myType _21[5] = { { 0.0f }, { 1.0f }, { 0.0f }, { 1.0f }, { 0.0f } }; + +static float4 gl_FragCoord; +static float4 o_color; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 o_color : SV_Target0; +}; + +float mod(float x, float y) +{ + return x - y * floor(x / y); +} + +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x / y); +} + +float3 mod(float3 x, float3 y) +{ + return x - y * floor(x / y); +} + +float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} + +void frag_main() +{ + float2 uv = gl_FragCoord.xy; + int index = int(mod(uv.x, 4.0f)); + myType elt = _21[index]; + if (elt.data > 0.0f) + { + o_color = float4(0.0f, 1.0f, 0.0f, 1.0f); + } + else + { + o_color = float4(1.0f, 0.0f, 0.0f, 1.0f); + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.o_color = o_color; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/srem.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/srem.asm.frag new file mode 100644 index 0000000..db5e717 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/srem.asm.frag @@ -0,0 +1,29 @@ +static float4 FragColor; +static int4 vA; +static int4 vB; + +struct SPIRV_Cross_Input +{ + nointerpolation int4 vA : TEXCOORD0; + nointerpolation int4 vB : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float4(vA - vB * (vA / vB)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vA = stage_input.vA; + vB = stage_input.vB; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..1905b5e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,23 @@ +static const float4 _20[2] = { float4(1.0f, 2.0f, 3.0f, 4.0f), 10.0f.xxxx }; + +static float4 FragColors[2] = _20; +static float4 FragColor = 5.0f.xxxx; + +struct SPIRV_Cross_Output +{ + float4 FragColors[2] : SV_Target0; + float4 FragColor : SV_Target2; +}; + +void frag_main() +{ +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColors = FragColors; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..74c1294 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,30 @@ +Texture2D uTexture : register(t0); +SamplerState _uTexture_sampler : register(s0); + +static float4 gl_FragCoord; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uTexture.Load(int3(int2(gl_FragCoord.xy), 0)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag new file mode 100644 index 0000000..322c86b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag @@ -0,0 +1,29 @@ +Texture2D uTexture : register(t0); +SamplerState _uTexture_sampler : register(s0); + +static min16float4 FragColor; +static min16float2 UV; + +struct SPIRV_Cross_Input +{ + min16float2 UV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + min16float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = min16float4(uTexture.Sample(_uTexture_sampler, UV)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + UV = stage_input.UV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000..027dd00 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,41 @@ +Texture2D uShadow : register(t0); +SamplerComparisonState _uShadow_sampler : register(s0); +Texture2D uTexture : register(t1); +SamplerComparisonState uSampler : register(s2); + +static float3 vUV; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float3 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +float sample_combined() +{ + return uShadow.SampleCmp(_uShadow_sampler, vUV.xy, vUV.z); +} + +float sample_separate() +{ + return uTexture.SampleCmp(uSampler, vUV.xy, vUV.z); +} + +void frag_main() +{ + FragColor = sample_combined() + sample_separate(); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..86c1daa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/frag/unreachable.asm.frag @@ -0,0 +1,44 @@ +static int counter; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + nointerpolation int counter : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +static float4 _21; + +void frag_main() +{ + float4 _24; + _24 = _21; + float4 _33; + for (;;) + { + if (counter == 10) + { + _33 = 10.0f.xxxx; + break; + } + else + { + _33 = 30.0f.xxxx; + break; + } + } + FragColor = _33; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + counter = stage_input.counter; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..d5bf9e7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,67 @@ +struct V2F +{ + float4 Position; + float4 Color; +}; + +struct InstanceData +{ + column_major float4x4 MATRIX_MVP; + float4 Color; +}; + +cbuffer gInstanceData : register(b0) +{ + InstanceData gInstanceData_1_data[32] : packoffset(c0); +}; + + +static float4 gl_Position; +static int gl_InstanceIndex; +static float3 PosL; +static float4 _entryPointOutput_Color; + +struct SPIRV_Cross_Input +{ + float3 PosL : TEXCOORD0; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 _entryPointOutput_Color : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +V2F _VS(float3 PosL_1, uint instanceID) +{ + InstanceData instData; + instData.MATRIX_MVP = gInstanceData_1_data[instanceID].MATRIX_MVP; + instData.Color = gInstanceData_1_data[instanceID].Color; + V2F v2f; + v2f.Position = mul(float4(PosL_1, 1.0f), instData.MATRIX_MVP); + v2f.Color = instData.Color; + return v2f; +} + +void vert_main() +{ + float3 PosL_1 = PosL; + uint instanceID = uint(gl_InstanceIndex); + float3 param = PosL_1; + uint param_1 = instanceID; + V2F flattenTemp = _VS(param, param_1); + gl_Position = flattenTemp.Position; + _entryPointOutput_Color = flattenTemp.Color; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + PosL = stage_input.PosL; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output._entryPointOutput_Color = _entryPointOutput_Color; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert b/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert new file mode 100644 index 0000000..84b91b6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert @@ -0,0 +1,50 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_201 +#define SPIRV_CROSS_CONSTANT_ID_201 -10 +#endif +static const int _7 = SPIRV_CROSS_CONSTANT_ID_201; +static const int _20 = (_7 + 2); +#ifndef SPIRV_CROSS_CONSTANT_ID_202 +#define SPIRV_CROSS_CONSTANT_ID_202 100u +#endif +static const uint _8 = SPIRV_CROSS_CONSTANT_ID_202; +static const uint _25 = (_8 % 5u); +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 int4(20, 30, _20, _20) +#endif +static const int4 _30 = SPIRV_CROSS_CONSTANT_ID_0; +static const int2 _32 = int2(_30.y, _30.x); +static const int _33 = _30.y; +#ifndef SPIRV_CROSS_CONSTANT_ID_200 +#define SPIRV_CROSS_CONSTANT_ID_200 3.141590118408203125f +#endif +static const float _9 = SPIRV_CROSS_CONSTANT_ID_200; + +static float4 gl_Position; +static int _4; + +struct SPIRV_Cross_Output +{ + nointerpolation int _4 : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + float4 pos = 0.0f.xxxx; + pos.y += float(_20); + pos.z += float(_25); + pos += float4(_30); + float2 _56 = pos.xy + float2(_32); + pos = float4(_56.x, _56.y, pos.z, pos.w); + gl_Position = pos; + _4 = _33; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output._4 = _4; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..a18c6e7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,37 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +float4 _main(uint vid, uint iid) +{ + return float(vid + iid).xxxx; +} + +void vert_main() +{ + uint vid = uint(gl_VertexIndex); + uint iid = uint(gl_InstanceIndex); + uint param = vid; + uint param_1 = iid; + gl_Position = _main(param, param_1); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert b/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..48b2df2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = float(gl_VertexIndex + gl_InstanceIndex).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chain-load-composite.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chain-load-composite.comp new file mode 100644 index 0000000..1c40160 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chain-load-composite.comp @@ -0,0 +1,164 @@ +struct Baz +{ + float c; +}; + +struct Bar +{ + float d[2][4]; + Baz baz[2]; +}; + +struct Foo +{ + column_major float2x2 a; + float2 b; + Bar c[5]; +}; + +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _31 : register(u0); + +void comp_main() +{ + Foo _36; + _36.a = asfloat(uint2x2(_31.Load(0), _31.Load(8), _31.Load(4), _31.Load(12))); + _36.b = asfloat(_31.Load2(16)); + [unroll] + for (int _4ident = 0; _4ident < 5; _4ident++) + { + [unroll] + for (int _5ident = 0; _5ident < 2; _5ident++) + { + [unroll] + for (int _6ident = 0; _6ident < 4; _6ident++) + { + _36.c[_4ident].d[_5ident][_6ident] = asfloat(_31.Load(_6ident * 4 + _5ident * 16 + _4ident * 40 + 24)); + } + } + [unroll] + for (int _7ident = 0; _7ident < 2; _7ident++) + { + _36.c[_4ident].baz[_7ident].c = asfloat(_31.Load(_7ident * 4 + _4ident * 40 + 56)); + } + } + Foo f; + f.a = _36.a; + f.b = _36.b; + f.c[0].d[0][0] = _36.c[0].d[0][0]; + f.c[0].d[0][1] = _36.c[0].d[0][1]; + f.c[0].d[0][2] = _36.c[0].d[0][2]; + f.c[0].d[0][3] = _36.c[0].d[0][3]; + f.c[0].d[1][0] = _36.c[0].d[1][0]; + f.c[0].d[1][1] = _36.c[0].d[1][1]; + f.c[0].d[1][2] = _36.c[0].d[1][2]; + f.c[0].d[1][3] = _36.c[0].d[1][3]; + f.c[0].baz[0].c = _36.c[0].baz[0].c; + f.c[0].baz[1].c = _36.c[0].baz[1].c; + f.c[1].d[0][0] = _36.c[1].d[0][0]; + f.c[1].d[0][1] = _36.c[1].d[0][1]; + f.c[1].d[0][2] = _36.c[1].d[0][2]; + f.c[1].d[0][3] = _36.c[1].d[0][3]; + f.c[1].d[1][0] = _36.c[1].d[1][0]; + f.c[1].d[1][1] = _36.c[1].d[1][1]; + f.c[1].d[1][2] = _36.c[1].d[1][2]; + f.c[1].d[1][3] = _36.c[1].d[1][3]; + f.c[1].baz[0].c = _36.c[1].baz[0].c; + f.c[1].baz[1].c = _36.c[1].baz[1].c; + f.c[2].d[0][0] = _36.c[2].d[0][0]; + f.c[2].d[0][1] = _36.c[2].d[0][1]; + f.c[2].d[0][2] = _36.c[2].d[0][2]; + f.c[2].d[0][3] = _36.c[2].d[0][3]; + f.c[2].d[1][0] = _36.c[2].d[1][0]; + f.c[2].d[1][1] = _36.c[2].d[1][1]; + f.c[2].d[1][2] = _36.c[2].d[1][2]; + f.c[2].d[1][3] = _36.c[2].d[1][3]; + f.c[2].baz[0].c = _36.c[2].baz[0].c; + f.c[2].baz[1].c = _36.c[2].baz[1].c; + f.c[3].d[0][0] = _36.c[3].d[0][0]; + f.c[3].d[0][1] = _36.c[3].d[0][1]; + f.c[3].d[0][2] = _36.c[3].d[0][2]; + f.c[3].d[0][3] = _36.c[3].d[0][3]; + f.c[3].d[1][0] = _36.c[3].d[1][0]; + f.c[3].d[1][1] = _36.c[3].d[1][1]; + f.c[3].d[1][2] = _36.c[3].d[1][2]; + f.c[3].d[1][3] = _36.c[3].d[1][3]; + f.c[3].baz[0].c = _36.c[3].baz[0].c; + f.c[3].baz[1].c = _36.c[3].baz[1].c; + f.c[4].d[0][0] = _36.c[4].d[0][0]; + f.c[4].d[0][1] = _36.c[4].d[0][1]; + f.c[4].d[0][2] = _36.c[4].d[0][2]; + f.c[4].d[0][3] = _36.c[4].d[0][3]; + f.c[4].d[1][0] = _36.c[4].d[1][0]; + f.c[4].d[1][1] = _36.c[4].d[1][1]; + f.c[4].d[1][2] = _36.c[4].d[1][2]; + f.c[4].d[1][3] = _36.c[4].d[1][3]; + f.c[4].baz[0].c = _36.c[4].baz[0].c; + f.c[4].baz[1].c = _36.c[4].baz[1].c; + float2 _229 = 1.0f.xx; + f.a = float2x2(f.a[0] + _229, f.a[1] + _229); + f.b += 2.0f.xx; + f.c[3].d[1][1] += 5.0f; + _31.Store(224, asuint(f.a[0].x)); + _31.Store(228, asuint(f.a[1].x)); + _31.Store(232, asuint(f.a[0].y)); + _31.Store(236, asuint(f.a[1].y)); + _31.Store2(240, asuint(f.b)); + _31.Store(248, asuint(f.c[0].d[0][0])); + _31.Store(252, asuint(f.c[0].d[0][1])); + _31.Store(256, asuint(f.c[0].d[0][2])); + _31.Store(260, asuint(f.c[0].d[0][3])); + _31.Store(264, asuint(f.c[0].d[1][0])); + _31.Store(268, asuint(f.c[0].d[1][1])); + _31.Store(272, asuint(f.c[0].d[1][2])); + _31.Store(276, asuint(f.c[0].d[1][3])); + _31.Store(280, asuint(f.c[0].baz[0].c)); + _31.Store(284, asuint(f.c[0].baz[1].c)); + _31.Store(288, asuint(f.c[1].d[0][0])); + _31.Store(292, asuint(f.c[1].d[0][1])); + _31.Store(296, asuint(f.c[1].d[0][2])); + _31.Store(300, asuint(f.c[1].d[0][3])); + _31.Store(304, asuint(f.c[1].d[1][0])); + _31.Store(308, asuint(f.c[1].d[1][1])); + _31.Store(312, asuint(f.c[1].d[1][2])); + _31.Store(316, asuint(f.c[1].d[1][3])); + _31.Store(320, asuint(f.c[1].baz[0].c)); + _31.Store(324, asuint(f.c[1].baz[1].c)); + _31.Store(328, asuint(f.c[2].d[0][0])); + _31.Store(332, asuint(f.c[2].d[0][1])); + _31.Store(336, asuint(f.c[2].d[0][2])); + _31.Store(340, asuint(f.c[2].d[0][3])); + _31.Store(344, asuint(f.c[2].d[1][0])); + _31.Store(348, asuint(f.c[2].d[1][1])); + _31.Store(352, asuint(f.c[2].d[1][2])); + _31.Store(356, asuint(f.c[2].d[1][3])); + _31.Store(360, asuint(f.c[2].baz[0].c)); + _31.Store(364, asuint(f.c[2].baz[1].c)); + _31.Store(368, asuint(f.c[3].d[0][0])); + _31.Store(372, asuint(f.c[3].d[0][1])); + _31.Store(376, asuint(f.c[3].d[0][2])); + _31.Store(380, asuint(f.c[3].d[0][3])); + _31.Store(384, asuint(f.c[3].d[1][0])); + _31.Store(388, asuint(f.c[3].d[1][1])); + _31.Store(392, asuint(f.c[3].d[1][2])); + _31.Store(396, asuint(f.c[3].d[1][3])); + _31.Store(400, asuint(f.c[3].baz[0].c)); + _31.Store(404, asuint(f.c[3].baz[1].c)); + _31.Store(408, asuint(f.c[4].d[0][0])); + _31.Store(412, asuint(f.c[4].d[0][1])); + _31.Store(416, asuint(f.c[4].d[0][2])); + _31.Store(420, asuint(f.c[4].d[0][3])); + _31.Store(424, asuint(f.c[4].d[1][0])); + _31.Store(428, asuint(f.c[4].d[1][1])); + _31.Store(432, asuint(f.c[4].d[1][2])); + _31.Store(436, asuint(f.c[4].d[1][3])); + _31.Store(440, asuint(f.c[4].baz[0].c)); + _31.Store(444, asuint(f.c[4].baz[1].c)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chains.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chains.comp new file mode 100644 index 0000000..c748200 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chains.comp @@ -0,0 +1,23 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer wo : register(u1); +ByteAddressBuffer ro : register(t0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + wo.Store4(gl_GlobalInvocationID.x * 64 + 272, asuint(asfloat(ro.Load4(gl_GlobalInvocationID.x * 64 + 160)))); + wo.Store4(gl_GlobalInvocationID.x * 16 + 480, asuint(asfloat(ro.Load4(gl_GlobalInvocationID.x * 16 + 480)))); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chains.force-uav.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chains.force-uav.comp new file mode 100644 index 0000000..97d046d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/access-chains.force-uav.comp @@ -0,0 +1,23 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer wo : register(u1); +RWByteAddressBuffer ro : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + wo.Store4(gl_GlobalInvocationID.x * 64 + 272, asuint(asfloat(ro.Load4(gl_GlobalInvocationID.x * 64 + 160)))); + wo.Store4(gl_GlobalInvocationID.x * 16 + 480, asuint(asfloat(ro.Load4(gl_GlobalInvocationID.x * 16 + 480)))); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/address-buffers.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/address-buffers.comp new file mode 100644 index 0000000..7f1c797 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/address-buffers.comp @@ -0,0 +1,17 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer WriteOnly : register(u2); +ByteAddressBuffer ReadOnly : register(t0); +RWByteAddressBuffer ReadWrite : register(u1); + +void comp_main() +{ + WriteOnly.Store4(0, asuint(asfloat(ReadOnly.Load4(0)))); + ReadWrite.Store4(0, asuint(asfloat(ReadWrite.Load4(0)) + 10.0f.xxxx)); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/atomic.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/atomic.comp new file mode 100644 index 0000000..e6ff891 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/atomic.comp @@ -0,0 +1,91 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer ssbo : register(u2); +RWTexture2D uImage : register(u0); +RWTexture2D iImage : register(u1); + +groupshared int int_atomic; +groupshared uint uint_atomic; +groupshared int int_atomic_array[1]; +groupshared uint uint_atomic_array[1]; + +void comp_main() +{ + uint _19; + InterlockedAdd(uImage[int2(1, 5)], 1u, _19); + uint _27; + InterlockedAdd(uImage[int2(1, 5)], 1u, _27); + iImage[int2(1, 6)] = int(_27).x; + uint _32; + InterlockedOr(uImage[int2(1, 5)], 1u, _32); + uint _34; + InterlockedXor(uImage[int2(1, 5)], 1u, _34); + uint _36; + InterlockedAnd(uImage[int2(1, 5)], 1u, _36); + uint _38; + InterlockedMin(uImage[int2(1, 5)], 1u, _38); + uint _40; + InterlockedMax(uImage[int2(1, 5)], 1u, _40); + uint _44; + InterlockedCompareExchange(uImage[int2(1, 5)], 10u, 2u, _44); + int _47; + InterlockedAdd(iImage[int2(1, 6)], 1, _47); + int _49; + InterlockedOr(iImage[int2(1, 6)], 1, _49); + int _51; + InterlockedXor(iImage[int2(1, 6)], 1, _51); + int _53; + InterlockedAnd(iImage[int2(1, 6)], 1, _53); + int _55; + InterlockedMin(iImage[int2(1, 6)], 1, _55); + int _57; + InterlockedMax(iImage[int2(1, 6)], 1, _57); + int _61; + InterlockedCompareExchange(iImage[int2(1, 5)], 10, 2, _61); + uint _68; + ssbo.InterlockedAdd(0, 1u, _68); + uint _70; + ssbo.InterlockedOr(0, 1u, _70); + uint _72; + ssbo.InterlockedXor(0, 1u, _72); + uint _74; + ssbo.InterlockedAnd(0, 1u, _74); + uint _76; + ssbo.InterlockedMin(0, 1u, _76); + uint _78; + ssbo.InterlockedMax(0, 1u, _78); + uint _80; + ssbo.InterlockedExchange(0, 1u, _80); + uint _82; + ssbo.InterlockedCompareExchange(0, 10u, 2u, _82); + int _85; + ssbo.InterlockedAdd(4, 1, _85); + int _87; + ssbo.InterlockedOr(4, 1, _87); + int _89; + ssbo.InterlockedXor(4, 1, _89); + int _91; + ssbo.InterlockedAnd(4, 1, _91); + int _93; + ssbo.InterlockedMin(4, 1, _93); + int _95; + ssbo.InterlockedMax(4, 1, _95); + int _97; + ssbo.InterlockedExchange(4, 1, _97); + int _99; + ssbo.InterlockedCompareExchange(4, 10, 2, _99); + int _102; + InterlockedAdd(int_atomic, 10, _102); + uint _105; + InterlockedAdd(uint_atomic, 10u, _105); + int _110; + InterlockedAdd(int_atomic_array[0], 10, _110); + uint _115; + InterlockedAdd(uint_atomic_array[0], 10u, _115); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/barriers.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/barriers.comp new file mode 100644 index 0000000..15af8ac --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/barriers.comp @@ -0,0 +1,81 @@ +static const uint3 gl_WorkGroupSize = uint3(4u, 1u, 1u); + +void barrier_shared() +{ + GroupMemoryBarrier(); +} + +void full_barrier() +{ + AllMemoryBarrier(); +} + +void image_barrier() +{ + DeviceMemoryBarrier(); +} + +void buffer_barrier() +{ + DeviceMemoryBarrier(); +} + +void group_barrier() +{ + AllMemoryBarrier(); +} + +void barrier_shared_exec() +{ + GroupMemoryBarrierWithGroupSync(); +} + +void full_barrier_exec() +{ + AllMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); +} + +void image_barrier_exec() +{ + DeviceMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); +} + +void buffer_barrier_exec() +{ + DeviceMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); +} + +void group_barrier_exec() +{ + AllMemoryBarrier(); + GroupMemoryBarrierWithGroupSync(); +} + +void exec_barrier() +{ + GroupMemoryBarrierWithGroupSync(); +} + +void comp_main() +{ + barrier_shared(); + full_barrier(); + image_barrier(); + buffer_barrier(); + group_barrier(); + barrier_shared_exec(); + full_barrier_exec(); + image_barrier_exec(); + buffer_barrier_exec(); + group_barrier_exec(); + exec_barrier(); +} + +[numthreads(4, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/builtins.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/builtins.comp new file mode 100644 index 0000000..5d84883 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/builtins.comp @@ -0,0 +1,32 @@ +static const uint3 gl_WorkGroupSize = uint3(8u, 4u, 2u); + +static uint3 gl_WorkGroupID; +static uint3 gl_LocalInvocationID; +static uint3 gl_GlobalInvocationID; +static uint gl_LocalInvocationIndex; +struct SPIRV_Cross_Input +{ + uint3 gl_WorkGroupID : SV_GroupID; + uint3 gl_LocalInvocationID : SV_GroupThreadID; + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; + uint gl_LocalInvocationIndex : SV_GroupIndex; +}; + +void comp_main() +{ + uint3 local_id = gl_LocalInvocationID; + uint3 global_id = gl_GlobalInvocationID; + uint local_index = gl_LocalInvocationIndex; + uint3 work_group_size = gl_WorkGroupSize; + uint3 work_group_id = gl_WorkGroupID; +} + +[numthreads(8, 4, 2)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_WorkGroupID = stage_input.gl_WorkGroupID; + gl_LocalInvocationID = stage_input.gl_LocalInvocationID; + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + gl_LocalInvocationIndex = stage_input.gl_LocalInvocationIndex; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/composite-array-initialization.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/composite-array-initialization.comp new file mode 100644 index 0000000..dbd8cf7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/composite-array-initialization.comp @@ -0,0 +1,63 @@ +struct Data +{ + float a; + float b; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 4.0f +#endif +static const float X = SPIRV_CROSS_CONSTANT_ID_0; + +static const uint3 gl_WorkGroupSize = uint3(2u, 1u, 1u); + +static const Data _21 = { 1.0f, 2.0f }; +static const Data _24 = { 3.0f, 4.0f }; +static const Data _25[2] = { { 1.0f, 2.0f }, { 3.0f, 4.0f } }; +static const Data _30 = { 3.0f, 5.0f }; + +RWByteAddressBuffer _61 : register(u0); + +static uint3 gl_WorkGroupID; +static uint3 gl_LocalInvocationID; +static uint gl_LocalInvocationIndex; +struct SPIRV_Cross_Input +{ + uint3 gl_WorkGroupID : SV_GroupID; + uint3 gl_LocalInvocationID : SV_GroupThreadID; + uint gl_LocalInvocationIndex : SV_GroupIndex; +}; + +static Data data[2]; +static Data data2[2]; + +Data combine(Data a, Data b) +{ + Data _46 = { a.a + b.a, a.b + b.b }; + return _46; +} + +void comp_main() +{ + data = _25; + Data _28 = { X, 2.0f }; + Data _31[2] = { _28, _30 }; + data2 = _31; + if (gl_LocalInvocationIndex == 0u) + { + Data param = data[gl_LocalInvocationID.x]; + Data param_1 = data2[gl_LocalInvocationID.x]; + Data _79 = combine(param, param_1); + _61.Store(gl_WorkGroupID.x * 8 + 0, asuint(_79.a)); + _61.Store(gl_WorkGroupID.x * 8 + 4, asuint(_79.b)); + } +} + +[numthreads(2, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_WorkGroupID = stage_input.gl_WorkGroupID; + gl_LocalInvocationID = stage_input.gl_LocalInvocationID; + gl_LocalInvocationIndex = stage_input.gl_LocalInvocationIndex; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/globallycoherent.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000..236f341 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,20 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +globallycoherent RWByteAddressBuffer _29 : register(u3); +ByteAddressBuffer _33 : register(t2); +RWTexture2D uImageIn : register(u0); +globallycoherent RWTexture2D uImageOut : register(u1); + +void comp_main() +{ + int2 coord = int2(9, 7); + float4 indata = uImageIn[coord].xxxx; + uImageOut[coord] = indata.x; + _29.Store(0, asuint(asfloat(_33.Load(0)))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/image.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/image.comp new file mode 100644 index 0000000..89a9940 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/image.comp @@ -0,0 +1,73 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWTexture2D uImageInF : register(u0); +RWTexture2D uImageOutF : register(u1); +RWTexture2D uImageInI : register(u2); +RWTexture2D uImageOutI : register(u3); +RWTexture2D uImageInU : register(u4); +RWTexture2D uImageOutU : register(u5); +RWBuffer uImageInBuffer : register(u6); +RWBuffer uImageOutBuffer : register(u7); +RWTexture2D uImageInF2 : register(u8); +RWTexture2D uImageOutF2 : register(u9); +RWTexture2D uImageInI2 : register(u10); +RWTexture2D uImageOutI2 : register(u11); +RWTexture2D uImageInU2 : register(u12); +RWTexture2D uImageOutU2 : register(u13); +RWBuffer uImageInBuffer2 : register(u14); +RWBuffer uImageOutBuffer2 : register(u15); +RWTexture2D uImageInF4 : register(u16); +RWTexture2D uImageOutF4 : register(u17); +RWTexture2D uImageInI4 : register(u18); +RWTexture2D uImageOutI4 : register(u19); +RWTexture2D uImageInU4 : register(u20); +RWTexture2D uImageOutU4 : register(u21); +RWBuffer uImageInBuffer4 : register(u22); +RWBuffer uImageOutBuffer4 : register(u23); +RWTexture2D uImageNoFmtF : register(u24); +RWTexture2D uImageNoFmtU : register(u25); +RWTexture2D uImageNoFmtI : register(u26); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + float4 f = uImageInF[int2(gl_GlobalInvocationID.xy)].xxxx; + uImageOutF[int2(gl_GlobalInvocationID.xy)] = f.x; + int4 i = uImageInI[int2(gl_GlobalInvocationID.xy)].xxxx; + uImageOutI[int2(gl_GlobalInvocationID.xy)] = i.x; + uint4 u = uImageInU[int2(gl_GlobalInvocationID.xy)].xxxx; + uImageOutU[int2(gl_GlobalInvocationID.xy)] = u.x; + float4 b = uImageInBuffer[int(gl_GlobalInvocationID.x)].xxxx; + uImageOutBuffer[int(gl_GlobalInvocationID.x)] = b.x; + float4 f2 = uImageInF2[int2(gl_GlobalInvocationID.xy)].xyyy; + uImageOutF2[int2(gl_GlobalInvocationID.xy)] = f2.xy; + int4 i2 = uImageInI2[int2(gl_GlobalInvocationID.xy)].xyyy; + uImageOutI2[int2(gl_GlobalInvocationID.xy)] = i2.xy; + uint4 u2 = uImageInU2[int2(gl_GlobalInvocationID.xy)].xyyy; + uImageOutU2[int2(gl_GlobalInvocationID.xy)] = u2.xy; + float4 b2 = uImageInBuffer2[int(gl_GlobalInvocationID.x)].xyyy; + uImageOutBuffer2[int(gl_GlobalInvocationID.x)] = b2.xy; + float4 f4 = uImageInF4[int2(gl_GlobalInvocationID.xy)]; + uImageOutF4[int2(gl_GlobalInvocationID.xy)] = f4; + int4 i4 = uImageInI4[int2(gl_GlobalInvocationID.xy)]; + uImageOutI4[int2(gl_GlobalInvocationID.xy)] = i4; + uint4 u4 = uImageInU4[int2(gl_GlobalInvocationID.xy)]; + uImageOutU4[int2(gl_GlobalInvocationID.xy)] = u4; + float4 b4 = uImageInBuffer4[int(gl_GlobalInvocationID.x)]; + uImageOutBuffer4[int(gl_GlobalInvocationID.x)] = b4; + uImageNoFmtF[int2(gl_GlobalInvocationID.xy)] = b2; + uImageNoFmtU[int2(gl_GlobalInvocationID.xy)] = u4; + uImageNoFmtI[int2(gl_GlobalInvocationID.xy)] = i4; +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/image.nonwritable-uav-texture.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/image.nonwritable-uav-texture.comp new file mode 100644 index 0000000..1e11b08 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/image.nonwritable-uav-texture.comp @@ -0,0 +1,73 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +Texture2D uImageInF : register(t0); +RWTexture2D uImageOutF : register(u1); +Texture2D uImageInI : register(t2); +RWTexture2D uImageOutI : register(u3); +Texture2D uImageInU : register(t4); +RWTexture2D uImageOutU : register(u5); +Buffer uImageInBuffer : register(t6); +RWBuffer uImageOutBuffer : register(u7); +Texture2D uImageInF2 : register(t8); +RWTexture2D uImageOutF2 : register(u9); +Texture2D uImageInI2 : register(t10); +RWTexture2D uImageOutI2 : register(u11); +Texture2D uImageInU2 : register(t12); +RWTexture2D uImageOutU2 : register(u13); +Buffer uImageInBuffer2 : register(t14); +RWBuffer uImageOutBuffer2 : register(u15); +Texture2D uImageInF4 : register(t16); +RWTexture2D uImageOutF4 : register(u17); +Texture2D uImageInI4 : register(t18); +RWTexture2D uImageOutI4 : register(u19); +Texture2D uImageInU4 : register(t20); +RWTexture2D uImageOutU4 : register(u21); +Buffer uImageInBuffer4 : register(t22); +RWBuffer uImageOutBuffer4 : register(u23); +RWTexture2D uImageNoFmtF : register(u24); +RWTexture2D uImageNoFmtU : register(u25); +RWTexture2D uImageNoFmtI : register(u26); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + float4 f = uImageInF[int2(gl_GlobalInvocationID.xy)]; + uImageOutF[int2(gl_GlobalInvocationID.xy)] = f.x; + int4 i = uImageInI[int2(gl_GlobalInvocationID.xy)]; + uImageOutI[int2(gl_GlobalInvocationID.xy)] = i.x; + uint4 u = uImageInU[int2(gl_GlobalInvocationID.xy)]; + uImageOutU[int2(gl_GlobalInvocationID.xy)] = u.x; + float4 b = uImageInBuffer[int(gl_GlobalInvocationID.x)]; + uImageOutBuffer[int(gl_GlobalInvocationID.x)] = b.x; + float4 f2 = uImageInF2[int2(gl_GlobalInvocationID.xy)]; + uImageOutF2[int2(gl_GlobalInvocationID.xy)] = f2.xy; + int4 i2 = uImageInI2[int2(gl_GlobalInvocationID.xy)]; + uImageOutI2[int2(gl_GlobalInvocationID.xy)] = i2.xy; + uint4 u2 = uImageInU2[int2(gl_GlobalInvocationID.xy)]; + uImageOutU2[int2(gl_GlobalInvocationID.xy)] = u2.xy; + float4 b2 = uImageInBuffer2[int(gl_GlobalInvocationID.x)]; + uImageOutBuffer2[int(gl_GlobalInvocationID.x)] = b2.xy; + float4 f4 = uImageInF4[int2(gl_GlobalInvocationID.xy)]; + uImageOutF4[int2(gl_GlobalInvocationID.xy)] = f4; + int4 i4 = uImageInI4[int2(gl_GlobalInvocationID.xy)]; + uImageOutI4[int2(gl_GlobalInvocationID.xy)] = i4; + uint4 u4 = uImageInU4[int2(gl_GlobalInvocationID.xy)]; + uImageOutU4[int2(gl_GlobalInvocationID.xy)] = u4; + float4 b4 = uImageInBuffer4[int(gl_GlobalInvocationID.x)]; + uImageOutBuffer4[int(gl_GlobalInvocationID.x)] = b4; + uImageNoFmtF[int2(gl_GlobalInvocationID.xy)] = b2; + uImageNoFmtU[int2(gl_GlobalInvocationID.xy)] = u4; + uImageNoFmtI[int2(gl_GlobalInvocationID.xy)] = i4; +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/inverse.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/inverse.comp new file mode 100644 index 0000000..f9ec89a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/inverse.comp @@ -0,0 +1,124 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _15 : register(u0); +ByteAddressBuffer _20 : register(t1); + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float2x2 SPIRV_Cross_Inverse(float2x2 m) +{ + float2x2 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = m[1][1]; + adj[0][1] = -m[0][1]; + + adj[1][0] = -m[1][0]; + adj[1][1] = m[0][0]; + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the determinant of a 2x2 matrix. +float SPIRV_Cross_Det2x2(float a1, float a2, float b1, float b2) +{ + return a1 * b2 - b1 * a2; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float3x3 SPIRV_Cross_Inverse(float3x3 m) +{ + float3x3 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = SPIRV_Cross_Det2x2(m[1][1], m[1][2], m[2][1], m[2][2]); + adj[0][1] = -SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[2][1], m[2][2]); + adj[0][2] = SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[1][1], m[1][2]); + + adj[1][0] = -SPIRV_Cross_Det2x2(m[1][0], m[1][2], m[2][0], m[2][2]); + adj[1][1] = SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[2][0], m[2][2]); + adj[1][2] = -SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[1][0], m[1][2]); + + adj[2][0] = SPIRV_Cross_Det2x2(m[1][0], m[1][1], m[2][0], m[2][1]); + adj[2][1] = -SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[2][0], m[2][1]); + adj[2][2] = SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[1][0], m[1][1]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the determinant of a 3x3 matrix. +float SPIRV_Cross_Det3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) +{ + return a1 * SPIRV_Cross_Det2x2(b2, b3, c2, c3) - b1 * SPIRV_Cross_Det2x2(a2, a3, c2, c3) + c1 * SPIRV_Cross_Det2x2(a2, a3, b2, b3); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +float4x4 SPIRV_Cross_Inverse(float4x4 m) +{ + float4x4 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = SPIRV_Cross_Det3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][1] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][2] = SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3]); + adj[0][3] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]); + + adj[1][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3]); + adj[1][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]); + + adj[2][0] = SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][1] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][2] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3]); + adj[2][3] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]); + + adj[3][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2]); + adj[3][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] * m[3][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +void comp_main() +{ + float2x2 _23 = asfloat(uint2x2(_20.Load2(0), _20.Load2(8))); + float2x2 _24 = SPIRV_Cross_Inverse(_23); + _15.Store2(0, asuint(_24[0])); + _15.Store2(8, asuint(_24[1])); + float3x3 _29 = asfloat(uint3x3(_20.Load3(16), _20.Load3(32), _20.Load3(48))); + float3x3 _30 = SPIRV_Cross_Inverse(_29); + _15.Store3(16, asuint(_30[0])); + _15.Store3(32, asuint(_30[1])); + _15.Store3(48, asuint(_30[2])); + float4x4 _35 = asfloat(uint4x4(_20.Load4(64), _20.Load4(80), _20.Load4(96), _20.Load4(112))); + float4x4 _36 = SPIRV_Cross_Inverse(_35); + _15.Store4(64, asuint(_36[0])); + _15.Store4(80, asuint(_36[1])); + _15.Store4(96, asuint(_36[2])); + _15.Store4(112, asuint(_36[3])); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/num-workgroups-alone.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/num-workgroups-alone.comp new file mode 100644 index 0000000..dc87dc8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/num-workgroups-alone.comp @@ -0,0 +1,19 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _10 : register(u0); +cbuffer SPIRV_Cross_NumWorkgroups : register(b0) +{ + uint3 SPIRV_Cross_NumWorkgroups_1_count : packoffset(c0); +}; + + +void comp_main() +{ + _10.Store3(0, SPIRV_Cross_NumWorkgroups_1_count); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/num-workgroups-with-builtins.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/num-workgroups-with-builtins.comp new file mode 100644 index 0000000..2e2ad55 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/num-workgroups-with-builtins.comp @@ -0,0 +1,26 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _10 : register(u0); +cbuffer SPIRV_Cross_NumWorkgroups : register(b0) +{ + uint3 SPIRV_Cross_NumWorkgroups_1_count : packoffset(c0); +}; + + +static uint3 gl_WorkGroupID; +struct SPIRV_Cross_Input +{ + uint3 gl_WorkGroupID : SV_GroupID; +}; + +void comp_main() +{ + _10.Store3(0, SPIRV_Cross_NumWorkgroups_1_count + gl_WorkGroupID); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_WorkGroupID = stage_input.gl_WorkGroupID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/outer-product.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/outer-product.comp new file mode 100644 index 0000000..e58c02f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/outer-product.comp @@ -0,0 +1,50 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _21 : register(u0); +ByteAddressBuffer _26 : register(t1); + +void comp_main() +{ + float2x2 _32 = float2x2(asfloat(_26.Load2(0)) * asfloat(_26.Load2(0)).x, asfloat(_26.Load2(0)) * asfloat(_26.Load2(0)).y); + _21.Store2(0, asuint(_32[0])); + _21.Store2(8, asuint(_32[1])); + float2x3 _41 = float2x3(asfloat(_26.Load3(16)) * asfloat(_26.Load2(0)).x, asfloat(_26.Load3(16)) * asfloat(_26.Load2(0)).y); + _21.Store3(16, asuint(_41[0])); + _21.Store3(32, asuint(_41[1])); + float2x4 _50 = float2x4(asfloat(_26.Load4(32)) * asfloat(_26.Load2(0)).x, asfloat(_26.Load4(32)) * asfloat(_26.Load2(0)).y); + _21.Store4(48, asuint(_50[0])); + _21.Store4(64, asuint(_50[1])); + float3x2 _58 = float3x2(asfloat(_26.Load2(0)) * asfloat(_26.Load3(16)).x, asfloat(_26.Load2(0)) * asfloat(_26.Load3(16)).y, asfloat(_26.Load2(0)) * asfloat(_26.Load3(16)).z); + _21.Store2(80, asuint(_58[0])); + _21.Store2(88, asuint(_58[1])); + _21.Store2(96, asuint(_58[2])); + float3x3 _66 = float3x3(asfloat(_26.Load3(16)) * asfloat(_26.Load3(16)).x, asfloat(_26.Load3(16)) * asfloat(_26.Load3(16)).y, asfloat(_26.Load3(16)) * asfloat(_26.Load3(16)).z); + _21.Store3(112, asuint(_66[0])); + _21.Store3(128, asuint(_66[1])); + _21.Store3(144, asuint(_66[2])); + float3x4 _74 = float3x4(asfloat(_26.Load4(32)) * asfloat(_26.Load3(16)).x, asfloat(_26.Load4(32)) * asfloat(_26.Load3(16)).y, asfloat(_26.Load4(32)) * asfloat(_26.Load3(16)).z); + _21.Store4(160, asuint(_74[0])); + _21.Store4(176, asuint(_74[1])); + _21.Store4(192, asuint(_74[2])); + float4x2 _82 = float4x2(asfloat(_26.Load2(0)) * asfloat(_26.Load4(32)).x, asfloat(_26.Load2(0)) * asfloat(_26.Load4(32)).y, asfloat(_26.Load2(0)) * asfloat(_26.Load4(32)).z, asfloat(_26.Load2(0)) * asfloat(_26.Load4(32)).w); + _21.Store2(208, asuint(_82[0])); + _21.Store2(216, asuint(_82[1])); + _21.Store2(224, asuint(_82[2])); + _21.Store2(232, asuint(_82[3])); + float4x3 _90 = float4x3(asfloat(_26.Load3(16)) * asfloat(_26.Load4(32)).x, asfloat(_26.Load3(16)) * asfloat(_26.Load4(32)).y, asfloat(_26.Load3(16)) * asfloat(_26.Load4(32)).z, asfloat(_26.Load3(16)) * asfloat(_26.Load4(32)).w); + _21.Store3(240, asuint(_90[0])); + _21.Store3(256, asuint(_90[1])); + _21.Store3(272, asuint(_90[2])); + _21.Store3(288, asuint(_90[3])); + float4x4 _98 = float4x4(asfloat(_26.Load4(32)) * asfloat(_26.Load4(32)).x, asfloat(_26.Load4(32)) * asfloat(_26.Load4(32)).y, asfloat(_26.Load4(32)) * asfloat(_26.Load4(32)).z, asfloat(_26.Load4(32)) * asfloat(_26.Load4(32)).w); + _21.Store4(304, asuint(_98[0])); + _21.Store4(320, asuint(_98[1])); + _21.Store4(336, asuint(_98[2])); + _21.Store4(352, asuint(_98[3])); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/rmw-matrix.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/rmw-matrix.comp new file mode 100644 index 0000000..30ac03f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/rmw-matrix.comp @@ -0,0 +1,22 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _11 : register(u0); + +void comp_main() +{ + _11.Store(0, asuint(asfloat(_11.Load(0)) * asfloat(_11.Load(96)))); + _11.Store4(16, asuint(asfloat(_11.Load4(16)) * asfloat(_11.Load4(112)))); + float4x4 _35 = asfloat(uint4x4(_11.Load4(128), _11.Load4(144), _11.Load4(160), _11.Load4(176))); + float4x4 _37 = asfloat(uint4x4(_11.Load4(32), _11.Load4(48), _11.Load4(64), _11.Load4(80))); + float4x4 _38 = mul(_35, _37); + _11.Store4(32, asuint(_38[0])); + _11.Store4(48, asuint(_38[1])); + _11.Store4(64, asuint(_38[2])); + _11.Store4(80, asuint(_38[3])); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/rwbuffer-matrix.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/rwbuffer-matrix.comp new file mode 100644 index 0000000..197c9a9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/rwbuffer-matrix.comp @@ -0,0 +1,139 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _28 : register(u0); +cbuffer UBO : register(b1) +{ + int _68_index0 : packoffset(c0); + int _68_index1 : packoffset(c0.y); +}; + + +void row_to_col() +{ + float4x4 _55 = asfloat(uint4x4(_28.Load(64), _28.Load(80), _28.Load(96), _28.Load(112), _28.Load(68), _28.Load(84), _28.Load(100), _28.Load(116), _28.Load(72), _28.Load(88), _28.Load(104), _28.Load(120), _28.Load(76), _28.Load(92), _28.Load(108), _28.Load(124))); + _28.Store4(0, asuint(_55[0])); + _28.Store4(16, asuint(_55[1])); + _28.Store4(32, asuint(_55[2])); + _28.Store4(48, asuint(_55[3])); + float2x2 _58 = asfloat(uint2x2(_28.Load(144), _28.Load(152), _28.Load(148), _28.Load(156))); + _28.Store2(128, asuint(_58[0])); + _28.Store2(136, asuint(_58[1])); + float2x3 _61 = asfloat(uint2x3(_28.Load(192), _28.Load(200), _28.Load(208), _28.Load(196), _28.Load(204), _28.Load(212))); + _28.Store3(160, asuint(_61[0])); + _28.Store3(176, asuint(_61[1])); + float3x2 _64 = asfloat(uint3x2(_28.Load(240), _28.Load(256), _28.Load(244), _28.Load(260), _28.Load(248), _28.Load(264))); + _28.Store2(216, asuint(_64[0])); + _28.Store2(224, asuint(_64[1])); + _28.Store2(232, asuint(_64[2])); +} + +void col_to_row() +{ + float4x4 _34 = asfloat(uint4x4(_28.Load4(0), _28.Load4(16), _28.Load4(32), _28.Load4(48))); + _28.Store(64, asuint(_34[0].x)); + _28.Store(68, asuint(_34[1].x)); + _28.Store(72, asuint(_34[2].x)); + _28.Store(76, asuint(_34[3].x)); + _28.Store(80, asuint(_34[0].y)); + _28.Store(84, asuint(_34[1].y)); + _28.Store(88, asuint(_34[2].y)); + _28.Store(92, asuint(_34[3].y)); + _28.Store(96, asuint(_34[0].z)); + _28.Store(100, asuint(_34[1].z)); + _28.Store(104, asuint(_34[2].z)); + _28.Store(108, asuint(_34[3].z)); + _28.Store(112, asuint(_34[0].w)); + _28.Store(116, asuint(_34[1].w)); + _28.Store(120, asuint(_34[2].w)); + _28.Store(124, asuint(_34[3].w)); + float2x2 _40 = asfloat(uint2x2(_28.Load2(128), _28.Load2(136))); + _28.Store(144, asuint(_40[0].x)); + _28.Store(148, asuint(_40[1].x)); + _28.Store(152, asuint(_40[0].y)); + _28.Store(156, asuint(_40[1].y)); + float2x3 _46 = asfloat(uint2x3(_28.Load3(160), _28.Load3(176))); + _28.Store(192, asuint(_46[0].x)); + _28.Store(196, asuint(_46[1].x)); + _28.Store(200, asuint(_46[0].y)); + _28.Store(204, asuint(_46[1].y)); + _28.Store(208, asuint(_46[0].z)); + _28.Store(212, asuint(_46[1].z)); + float3x2 _52 = asfloat(uint3x2(_28.Load2(216), _28.Load2(224), _28.Load2(232))); + _28.Store(240, asuint(_52[0].x)); + _28.Store(244, asuint(_52[1].x)); + _28.Store(248, asuint(_52[2].x)); + _28.Store(256, asuint(_52[0].y)); + _28.Store(260, asuint(_52[1].y)); + _28.Store(264, asuint(_52[2].y)); +} + +void write_dynamic_index_row() +{ + _28.Store(_68_index0 * 4 + _68_index1 * 16 + 64, asuint(1.0f)); + _28.Store(_68_index0 * 4 + _68_index1 * 8 + 144, asuint(2.0f)); + _28.Store(_68_index0 * 4 + _68_index1 * 8 + 192, asuint(3.0f)); + _28.Store(_68_index0 * 4 + _68_index1 * 16 + 240, asuint(4.0f)); + _28.Store(_68_index0 * 4 + 64, asuint(1.0f.x)); + _28.Store(_68_index0 * 4 + 80, asuint(1.0f.xxxx.y)); + _28.Store(_68_index0 * 4 + 96, asuint(1.0f.xxxx.z)); + _28.Store(_68_index0 * 4 + 112, asuint(1.0f.xxxx.w)); + _28.Store(_68_index0 * 4 + 144, asuint(2.0f.x)); + _28.Store(_68_index0 * 4 + 152, asuint(2.0f.xx.y)); + _28.Store(_68_index0 * 4 + 192, asuint(3.0f.x)); + _28.Store(_68_index0 * 4 + 200, asuint(3.0f.xxx.y)); + _28.Store(_68_index0 * 4 + 208, asuint(3.0f.xxx.z)); + _28.Store(_68_index0 * 4 + 240, asuint(4.0f.x)); + _28.Store(_68_index0 * 4 + 256, asuint(4.0f.xx.y)); +} + +void write_dynamic_index_col() +{ + _28.Store(_68_index0 * 16 + _68_index1 * 4 + 0, asuint(1.0f)); + _28.Store(_68_index0 * 8 + _68_index1 * 4 + 128, asuint(2.0f)); + _28.Store(_68_index0 * 16 + _68_index1 * 4 + 160, asuint(3.0f)); + _28.Store(_68_index0 * 8 + _68_index1 * 4 + 216, asuint(4.0f)); + _28.Store4(_68_index0 * 16 + 0, asuint(1.0f.xxxx)); + _28.Store2(_68_index0 * 8 + 128, asuint(2.0f.xx)); + _28.Store3(_68_index0 * 16 + 160, asuint(3.0f.xxx)); + _28.Store2(_68_index0 * 8 + 216, asuint(4.0f.xx)); +} + +void read_dynamic_index_row() +{ + float a0 = asfloat(_28.Load(_68_index0 * 4 + _68_index1 * 16 + 64)); + float a1 = asfloat(_28.Load(_68_index0 * 4 + _68_index1 * 8 + 144)); + float a2 = asfloat(_28.Load(_68_index0 * 4 + _68_index1 * 8 + 192)); + float a3 = asfloat(_28.Load(_68_index0 * 4 + _68_index1 * 16 + 240)); + float4 v0 = asfloat(uint4(_28.Load(_68_index0 * 4 + 64), _28.Load(_68_index0 * 4 + 80), _28.Load(_68_index0 * 4 + 96), _28.Load(_68_index0 * 4 + 112))); + float2 v1 = asfloat(uint2(_28.Load(_68_index0 * 4 + 144), _28.Load(_68_index0 * 4 + 152))); + float3 v2 = asfloat(uint3(_28.Load(_68_index0 * 4 + 192), _28.Load(_68_index0 * 4 + 200), _28.Load(_68_index0 * 4 + 208))); + float2 v3 = asfloat(uint2(_28.Load(_68_index0 * 4 + 240), _28.Load(_68_index0 * 4 + 256))); +} + +void read_dynamic_index_col() +{ + float a0 = asfloat(_28.Load(_68_index0 * 16 + _68_index1 * 4 + 0)); + float a1 = asfloat(_28.Load(_68_index0 * 8 + _68_index1 * 4 + 128)); + float a2 = asfloat(_28.Load(_68_index0 * 16 + _68_index1 * 4 + 160)); + float a3 = asfloat(_28.Load(_68_index0 * 8 + _68_index1 * 4 + 216)); + float4 v0 = asfloat(_28.Load4(_68_index0 * 16 + 0)); + float2 v1 = asfloat(_28.Load2(_68_index0 * 8 + 128)); + float3 v2 = asfloat(_28.Load3(_68_index0 * 16 + 160)); + float2 v3 = asfloat(_28.Load2(_68_index0 * 8 + 216)); +} + +void comp_main() +{ + row_to_col(); + col_to_row(); + write_dynamic_index_row(); + write_dynamic_index_col(); + read_dynamic_index_row(); + read_dynamic_index_col(); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..db2bbe9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,16 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _9 : register(u0); + +void comp_main() +{ + _9.Store(8, asuint(distance(asfloat(_9.Load(0)), asfloat(_9.Load(4))))); + _9.Store(12, asuint(length(asfloat(_9.Load(0))))); + _9.Store(16, asuint(sign(asfloat(_9.Load(0))))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/shared.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/shared.comp new file mode 100644 index 0000000..5d12900 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/shared.comp @@ -0,0 +1,31 @@ +static const uint3 gl_WorkGroupSize = uint3(4u, 1u, 1u); + +ByteAddressBuffer _22 : register(t0); +RWByteAddressBuffer _44 : register(u1); + +static uint3 gl_GlobalInvocationID; +static uint gl_LocalInvocationIndex; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; + uint gl_LocalInvocationIndex : SV_GroupIndex; +}; + +groupshared float sShared[4]; + +void comp_main() +{ + uint ident = gl_GlobalInvocationID.x; + float idata = asfloat(_22.Load(ident * 4 + 0)); + sShared[gl_LocalInvocationIndex] = idata; + GroupMemoryBarrierWithGroupSync(); + _44.Store(ident * 4 + 0, asuint(sShared[(4u - gl_LocalInvocationIndex) - 1u])); +} + +[numthreads(4, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + gl_LocalInvocationIndex = stage_input.gl_LocalInvocationIndex; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/spec-constant-op-member-array.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/spec-constant-op-member-array.comp new file mode 100644 index 0000000..4e7c5e6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/spec-constant-op-member-array.comp @@ -0,0 +1,51 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 100 +#endif +static const int a = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 200 +#endif +static const int b = SPIRV_CROSS_CONSTANT_ID_1; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 300 +#endif +static const int c = SPIRV_CROSS_CONSTANT_ID_2; +static const int d = (c + 50); +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 400 +#endif +static const int e = SPIRV_CROSS_CONSTANT_ID_3; +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _22 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + _22.Store(gl_GlobalInvocationID.x * 4 + 2800, uint(int(_22.Load(gl_GlobalInvocationID.x * 4 + 2800)) + (int(_22.Load(gl_GlobalInvocationID.x * 4 + 2400)) + e))); +} + +[numthreads(1, 1, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/spec-constant-work-group-size.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/spec-constant-work-group-size.comp new file mode 100644 index 0000000..55ebf32 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/spec-constant-work-group-size.comp @@ -0,0 +1,43 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 2 +#endif +static const int b = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 1 +#endif +static const int a = SPIRV_CROSS_CONSTANT_ID_0; +static const uint _26 = (uint(a) + 0u); +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 1u +#endif +static const uint _27 = SPIRV_CROSS_CONSTANT_ID_10; +static const uint3 gl_WorkGroupSize = uint3(_27, 20u, 1u); +static const uint _32 = gl_WorkGroupSize.x; +static const uint _33 = (_26 + _32); +static const uint _34 = gl_WorkGroupSize.y; +static const uint _35 = (_33 + _34); +static const int _42 = (1 - a); + +RWByteAddressBuffer _23 : register(u0); + +static uint3 gl_GlobalInvocationID; +struct SPIRV_Cross_Input +{ + uint3 gl_GlobalInvocationID : SV_DispatchThreadID; +}; + +void comp_main() +{ + int spec_const_array_size[b]; + spec_const_array_size[0] = 10; + spec_const_array_size[1] = 40; + spec_const_array_size[a] = a; + _23.Store((_35 + gl_GlobalInvocationID.x) * 4 + 0, uint(b + spec_const_array_size[_42])); +} + +[numthreads(SPIRV_CROSS_CONSTANT_ID_10, 20, 1)] +void main(SPIRV_Cross_Input stage_input) +{ + gl_GlobalInvocationID = stage_input.gl_GlobalInvocationID; + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/ssbo-array-length.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/ssbo-array-length.comp new file mode 100644 index 0000000..82657ca --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/ssbo-array-length.comp @@ -0,0 +1,17 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer _11 : register(u1); + +void comp_main() +{ + uint _14; + _11.GetDimensions(_14); + _14 = (_14 - 16) / 16; + _11.Store(0, uint(int(_14))); +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/comp/ssbo-array.comp b/third_party/spirv-cross/reference/shaders-hlsl/comp/ssbo-array.comp new file mode 100644 index 0000000..dab2032 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/comp/ssbo-array.comp @@ -0,0 +1,13 @@ +static const uint3 gl_WorkGroupSize = uint3(1u, 1u, 1u); + +RWByteAddressBuffer ssbo0 : register(u0); + +void comp_main() +{ +} + +[numthreads(1, 1, 1)] +void main() +{ + comp_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..407fa2b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,30 @@ +static const float _17[5] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f }; + +static float4 FragColor; +static float4 v0; + +struct SPIRV_Cross_Input +{ + float4 v0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + for (int i = 0; i < 4; i++, FragColor += _17[i].xxxx) + { + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + v0 = stage_input.v0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/basic-color-3comp.sm30.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/basic-color-3comp.sm30.frag new file mode 100644 index 0000000..d3697d6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/basic-color-3comp.sm30.frag @@ -0,0 +1,26 @@ +static float3 FragColor; +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : COLOR0; +}; + +void frag_main() +{ + FragColor = vColor.xyz; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = float4(FragColor, 0.0); + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/basic-color-3comp.sm50.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/basic-color-3comp.sm50.frag new file mode 100644 index 0000000..52f6fed --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/basic-color-3comp.sm50.frag @@ -0,0 +1,26 @@ +static float3 FragColor; +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float3 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = vColor.xyz; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/basic.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/basic.frag new file mode 100644 index 0000000..6d06704 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/basic.frag @@ -0,0 +1,32 @@ +Texture2D uTex : register(t0); +SamplerState _uTex_sampler : register(s0); + +static float4 FragColor; +static float4 vColor; +static float2 vTex; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; + float2 vTex : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = vColor * uTex.Sample(_uTex_sampler, vTex); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + vTex = stage_input.vTex; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/bit-conversions.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/bit-conversions.frag new file mode 100644 index 0000000..2ed359b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/bit-conversions.frag @@ -0,0 +1,27 @@ +static float2 value; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float2 value : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int i = asint(value.x); + FragColor = float4(1.0f, 0.0f, asfloat(i), 1.0f); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + value = stage_input.value; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/boolean-mix.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/boolean-mix.frag new file mode 100644 index 0000000..f3e8489 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/boolean-mix.frag @@ -0,0 +1,27 @@ +static float2 FragColor; +static float2 x0; + +struct SPIRV_Cross_Input +{ + float2 x0 : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float2 FragColor : SV_Target0; +}; + +void frag_main() +{ + bool2 _27 = (x0.x > x0.y).xx; + FragColor = float2(_27.x ? float2(1.0f, 0.0f).x : float2(0.0f, 1.0f).x, _27.y ? float2(1.0f, 0.0f).y : float2(0.0f, 1.0f).y); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + x0 = stage_input.x0; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/builtins.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/builtins.frag new file mode 100644 index 0000000..8432c42 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/builtins.frag @@ -0,0 +1,34 @@ +static float4 gl_FragCoord; +static float gl_FragDepth; +static float4 FragColor; +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; + float gl_FragDepth : SV_Depth; +}; + +void frag_main() +{ + FragColor = gl_FragCoord + vColor; + gl_FragDepth = 0.5f; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + vColor = stage_input.vColor; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_FragDepth = gl_FragDepth; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/bvec-operations.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/bvec-operations.frag new file mode 100644 index 0000000..2398d8c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/bvec-operations.frag @@ -0,0 +1,29 @@ +static float2 value; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float2 value : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + bool2 _25 = bool2(value.x == 0.0f, value.y == 0.0f); + bool2 bools1 = bool2(!_25.x, !_25.y); + bool2 bools2 = bool2(value.x <= float2(1.5f, 0.5f).x, value.y <= float2(1.5f, 0.5f).y); + FragColor = float4(1.0f, 0.0f, float(bools1.x), float(bools2.x)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + value = stage_input.value; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/clip-cull-distance.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/clip-cull-distance.frag new file mode 100644 index 0000000..52f1ac3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/clip-cull-distance.frag @@ -0,0 +1,30 @@ +static float gl_ClipDistance[2]; +static float gl_CullDistance[1]; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float2 gl_ClipDistance0 : SV_ClipDistance0; + float gl_CullDistance0 : SV_CullDistance0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = (gl_ClipDistance[0] + gl_CullDistance[0]) + gl_ClipDistance[1]; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_ClipDistance[0] = stage_input.gl_ClipDistance0.x; + gl_ClipDistance[1] = stage_input.gl_ClipDistance0.y; + gl_CullDistance[0] = stage_input.gl_CullDistance0.x; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/combined-texture-sampler-parameter.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/combined-texture-sampler-parameter.frag new file mode 100644 index 0000000..7fcff42 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/combined-texture-sampler-parameter.frag @@ -0,0 +1,44 @@ +Texture2D uSampler : register(t0); +SamplerState _uSampler_sampler : register(s0); +Texture2D uSamplerShadow : register(t1); +SamplerComparisonState _uSamplerShadow_sampler : register(s1); + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +float4 samp2(Texture2D s, SamplerState _s_sampler) +{ + return s.Sample(_s_sampler, 1.0f.xx) + s.Load(int3(int2(10, 10), 0)); +} + +float4 samp3(Texture2D s, SamplerState _s_sampler) +{ + return samp2(s, _s_sampler); +} + +float samp4(Texture2D s, SamplerComparisonState _s_sampler) +{ + return s.SampleCmp(_s_sampler, 1.0f.xxx.xy, 1.0f.xxx.z); +} + +float samp(Texture2D s0, SamplerState _s0_sampler, Texture2D s1, SamplerComparisonState _s1_sampler) +{ + return samp3(s0, _s0_sampler).x + samp4(s1, _s1_sampler); +} + +void frag_main() +{ + FragColor = samp(uSampler, _uSampler_sampler, uSamplerShadow, _uSamplerShadow_sampler); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/combined-texture-sampler-shadow.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/combined-texture-sampler-shadow.frag new file mode 100644 index 0000000..af5b0b5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/combined-texture-sampler-shadow.frag @@ -0,0 +1,40 @@ +Texture2D uDepth : register(t2); +SamplerComparisonState uSampler : register(s0); +SamplerState uSampler1 : register(s1); + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +float samp2(Texture2D t, SamplerComparisonState s) +{ + return t.SampleCmp(s, 1.0f.xxx.xy, 1.0f.xxx.z); +} + +float samp3(Texture2D t, SamplerState s) +{ + return t.Sample(s, 1.0f.xx).x; +} + +float samp(Texture2D t, SamplerComparisonState s, SamplerState s1) +{ + float r0 = samp2(t, s); + float r1 = samp3(t, s1); + return r0 + r1; +} + +void frag_main() +{ + FragColor = samp(uDepth, uSampler, uSampler1); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..b2f9954 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,41 @@ +RWByteAddressBuffer _34 : register(u0); +Texture2D Buf : register(t1); +SamplerState _Buf_sampler : register(s1); + +static float4 gl_FragCoord; +static int vIn; +static int vIn2; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + nointerpolation int vIn : TEXCOORD0; + nointerpolation int vIn2 : TEXCOORD1; + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int4 coords = Buf.Load(int3(int2(gl_FragCoord.xy), 0)); + float4 foo = asfloat(_34.Load4((coords.x % 16) * 16 + 0)); + int c = vIn * vIn; + int d = vIn2 * vIn2; + FragColor = (foo + foo) + asfloat(_34.Load4((c + d) * 16 + 0)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + vIn = stage_input.vIn; + vIn2 = stage_input.vIn2; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/constant-composites.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/constant-composites.frag new file mode 100644 index 0000000..306ca5c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/constant-composites.frag @@ -0,0 +1,38 @@ +struct Foo +{ + float a; + float b; +}; + +static const float _16[4] = { 1.0f, 4.0f, 3.0f, 2.0f }; +static const Foo _24 = { 10.0f, 20.0f }; +static const Foo _27 = { 30.0f, 40.0f }; +static const Foo _28[2] = { { 10.0f, 20.0f }, { 30.0f, 40.0f } }; + +static float4 FragColor; +static int _line; + +struct SPIRV_Cross_Input +{ + nointerpolation int _line : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _16[_line].xxxx; + FragColor += (_28[_line].a * _28[1 - _line].a).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + _line = stage_input._line; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..6a31ce0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,56 @@ +Texture2D uSampler : register(t0); +SamplerState _uSampler_sampler : register(s0); + +static float4 FragColor; +static float4 vInput; + +struct SPIRV_Cross_Input +{ + float4 vInput : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = vInput; + float4 t = uSampler.Sample(_uSampler_sampler, vInput.xy); + float4 d0 = ddx(vInput); + float4 d1 = ddy(vInput); + float4 d2 = fwidth(vInput); + float4 d3 = ddx_coarse(vInput); + float4 d4 = ddy_coarse(vInput); + float4 d5 = fwidth(vInput); + float4 d6 = ddx_fine(vInput); + float4 d7 = ddy_fine(vInput); + float4 d8 = fwidth(vInput); + float _56_tmp = uSampler.CalculateLevelOfDetail(_uSampler_sampler, vInput.zw); + float2 _56 = _56_tmp.xx; + float2 lod = _56; + if (vInput.y > 10.0f) + { + FragColor += t; + FragColor += d0; + FragColor += d1; + FragColor += d2; + FragColor += d3; + FragColor += d4; + FragColor += d5; + FragColor += d6; + FragColor += d7; + FragColor += d8; + FragColor += lod.xyxy; + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vInput = stage_input.vInput; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/demote-to-helper.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/demote-to-helper.frag new file mode 100644 index 0000000..743a422 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/demote-to-helper.frag @@ -0,0 +1,9 @@ +void frag_main() +{ + discard; +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/depth-greater-than.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/depth-greater-than.frag new file mode 100644 index 0000000..b9f50db --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/depth-greater-than.frag @@ -0,0 +1,19 @@ +static float gl_FragDepth; +struct SPIRV_Cross_Output +{ + float gl_FragDepth : SV_DepthGreaterEqual; +}; + +void frag_main() +{ + gl_FragDepth = 0.5f; +} + +[earlydepthstencil] +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_FragDepth = gl_FragDepth; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/depth-less-than.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/depth-less-than.frag new file mode 100644 index 0000000..a702fd9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/depth-less-than.frag @@ -0,0 +1,19 @@ +static float gl_FragDepth; +struct SPIRV_Cross_Output +{ + float gl_FragDepth : SV_DepthLessEqual; +}; + +void frag_main() +{ + gl_FragDepth = 0.5f; +} + +[earlydepthstencil] +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_FragDepth = gl_FragDepth; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/dual-source-blending.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/dual-source-blending.frag new file mode 100644 index 0000000..961e800 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/dual-source-blending.frag @@ -0,0 +1,23 @@ +static float4 FragColor0; +static float4 FragColor1; + +struct SPIRV_Cross_Output +{ + float4 FragColor0 : SV_Target0; + float4 FragColor1 : SV_Target1; +}; + +void frag_main() +{ + FragColor0 = 1.0f.xxxx; + FragColor1 = 2.0f.xxxx; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor0 = FragColor0; + stage_output.FragColor1 = FragColor1; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/early-fragment-test.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/early-fragment-test.frag new file mode 100644 index 0000000..ae2569d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/early-fragment-test.frag @@ -0,0 +1,9 @@ +void frag_main() +{ +} + +[earlydepthstencil] +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..12e1e7f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,44 @@ +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = 0.0f.xxxx; + int i = 0; + int _36; + for (;;) + { + if (i < 3) + { + int a = i; + FragColor[a] += float(i); + if (false) + { + _36 = 1; + } + else + { + int _41 = i; + i = _41 + 1; + _36 = _41; + } + continue; + } + else + { + break; + } + } +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/fp16-packing.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/fp16-packing.frag new file mode 100644 index 0000000..d878282 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/fp16-packing.frag @@ -0,0 +1,44 @@ +static float2 FP32Out; +static uint FP16; +static uint FP16Out; +static float2 FP32; + +struct SPIRV_Cross_Input +{ + nointerpolation uint FP16 : TEXCOORD0; + nointerpolation float2 FP32 : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float2 FP32Out : SV_Target0; + uint FP16Out : SV_Target1; +}; + +uint SPIRV_Cross_packHalf2x16(float2 value) +{ + uint2 Packed = f32tof16(value); + return Packed.x | (Packed.y << 16); +} + +float2 SPIRV_Cross_unpackHalf2x16(uint value) +{ + return f16tof32(uint2(value & 0xffff, value >> 16)); +} + +void frag_main() +{ + FP32Out = SPIRV_Cross_unpackHalf2x16(FP16); + FP16Out = SPIRV_Cross_packHalf2x16(FP32); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + FP16 = stage_input.FP16; + FP32 = stage_input.FP32; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FP32Out = FP32Out; + stage_output.FP16Out = FP16Out; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/front-facing.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/front-facing.frag new file mode 100644 index 0000000..4ed09a2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/front-facing.frag @@ -0,0 +1,39 @@ +static bool gl_FrontFacing; +static float4 FragColor; +static float4 vA; +static float4 vB; + +struct SPIRV_Cross_Input +{ + float4 vA : TEXCOORD0; + float4 vB : TEXCOORD1; + bool gl_FrontFacing : SV_IsFrontFace; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + if (gl_FrontFacing) + { + FragColor = vA; + } + else + { + FragColor = vB; + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FrontFacing = stage_input.gl_FrontFacing; + vA = stage_input.vA; + vB = stage_input.vB; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-selective.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-selective.frag new file mode 100644 index 0000000..25c12da --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-selective.frag @@ -0,0 +1,146 @@ +Texture1D uSampler1DUint : register(t0); +SamplerState _uSampler1DUint_sampler : register(s0); +Texture1D uSampler1DInt : register(t0); +SamplerState _uSampler1DInt_sampler : register(s0); +Texture1D uSampler1DFloat : register(t0); +SamplerState _uSampler1DFloat_sampler : register(s0); +Texture2DArray uSampler2DArray : register(t2); +SamplerState _uSampler2DArray_sampler : register(s2); +Texture3D uSampler3D : register(t3); +SamplerState _uSampler3D_sampler : register(s3); +TextureCube uSamplerCube : register(t4); +SamplerState _uSamplerCube_sampler : register(s4); +TextureCubeArray uSamplerCubeArray : register(t5); +SamplerState _uSamplerCubeArray_sampler : register(s5); +Buffer uSamplerBuffer : register(t6); +Texture2DMS uSamplerMS : register(t7); +SamplerState _uSamplerMS_sampler : register(s7); +Texture2DMSArray uSamplerMSArray : register(t8); +SamplerState _uSamplerMSArray_sampler : register(s8); +Texture2D uSampler2D : register(t1); +SamplerState _uSampler2D_sampler : register(s1); + +uint SPIRV_Cross_textureSize(Texture1D Tex, uint Level, out uint Param) +{ + uint ret; + Tex.GetDimensions(Level, ret.x, Param); + return ret; +} + +uint SPIRV_Cross_textureSize(Texture1D Tex, uint Level, out uint Param) +{ + uint ret; + Tex.GetDimensions(Level, ret.x, Param); + return ret; +} + +uint SPIRV_Cross_textureSize(Texture1D Tex, uint Level, out uint Param) +{ + uint ret; + Tex.GetDimensions(Level, ret.x, Param); + return ret; +} + +uint2 SPIRV_Cross_textureSize(Texture2D Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(Level, ret.x, ret.y, Param); + return ret; +} + +uint3 SPIRV_Cross_textureSize(Texture2DArray Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param); + return ret; +} + +uint3 SPIRV_Cross_textureSize(Texture3D Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param); + return ret; +} + +uint SPIRV_Cross_textureSize(Buffer Tex, uint Level, out uint Param) +{ + uint ret; + Tex.GetDimensions(ret.x); + Param = 0u; + return ret; +} + +uint2 SPIRV_Cross_textureSize(TextureCube Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(Level, ret.x, ret.y, Param); + return ret; +} + +uint3 SPIRV_Cross_textureSize(TextureCubeArray Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param); + return ret; +} + +uint2 SPIRV_Cross_textureSize(Texture2DMS Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(ret.x, ret.y, Param); + return ret; +} + +uint3 SPIRV_Cross_textureSize(Texture2DMSArray Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(ret.x, ret.y, ret.z, Param); + return ret; +} + +void frag_main() +{ + uint _17_dummy_parameter; + int a = int(SPIRV_Cross_textureSize(uSampler1DUint, uint(0), _17_dummy_parameter)); + uint _24_dummy_parameter; + a = int(SPIRV_Cross_textureSize(uSampler1DInt, uint(0), _24_dummy_parameter)); + uint _32_dummy_parameter; + a = int(SPIRV_Cross_textureSize(uSampler1DFloat, uint(0), _32_dummy_parameter)); + uint _42_dummy_parameter; + int3 c = int3(SPIRV_Cross_textureSize(uSampler2DArray, uint(0), _42_dummy_parameter)); + uint _50_dummy_parameter; + int3 d = int3(SPIRV_Cross_textureSize(uSampler3D, uint(0), _50_dummy_parameter)); + uint _60_dummy_parameter; + int2 e = int2(SPIRV_Cross_textureSize(uSamplerCube, uint(0), _60_dummy_parameter)); + uint _68_dummy_parameter; + int3 f = int3(SPIRV_Cross_textureSize(uSamplerCubeArray, uint(0), _68_dummy_parameter)); + uint _76_dummy_parameter; + int g = int(SPIRV_Cross_textureSize(uSamplerBuffer, 0u, _76_dummy_parameter)); + uint _84_dummy_parameter; + int2 h = int2(SPIRV_Cross_textureSize(uSamplerMS, 0u, _84_dummy_parameter)); + uint _92_dummy_parameter; + int3 i = int3(SPIRV_Cross_textureSize(uSamplerMSArray, 0u, _92_dummy_parameter)); + int _100; + SPIRV_Cross_textureSize(uSampler2D, 0u, _100); + int l1 = int(_100); + int _104; + SPIRV_Cross_textureSize(uSampler2DArray, 0u, _104); + int l2 = int(_104); + int _108; + SPIRV_Cross_textureSize(uSampler3D, 0u, _108); + int l3 = int(_108); + int _112; + SPIRV_Cross_textureSize(uSamplerCube, 0u, _112); + int l4 = int(_112); + int _116; + SPIRV_Cross_textureSize(uSamplerMS, 0u, _116); + int s0 = int(_116); + int _120; + SPIRV_Cross_textureSize(uSamplerMSArray, 0u, _120); + int s1 = int(_120); +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-uav.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-uav.frag new file mode 100644 index 0000000..381a955 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-uav.frag @@ -0,0 +1,64 @@ +RWTexture1D uImage1D : register(u0); +RWTexture2D uImage2D : register(u1); +RWTexture2DArray uImage2DArray : register(u2); +RWTexture3D uImage3D : register(u3); +RWBuffer uImageBuffer : register(u6); + +uint3 SPIRV_Cross_imageSize(RWTexture2DArray Tex, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(ret.x, ret.y, ret.z); + Param = 0u; + return ret; +} + +uint2 SPIRV_Cross_imageSize(RWTexture2D Tex, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(ret.x, ret.y); + Param = 0u; + return ret; +} + +uint SPIRV_Cross_imageSize(RWTexture1D Tex, out uint Param) +{ + uint ret; + Tex.GetDimensions(ret.x); + Param = 0u; + return ret; +} + +uint3 SPIRV_Cross_imageSize(RWTexture3D Tex, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(ret.x, ret.y, ret.z); + Param = 0u; + return ret; +} + +uint SPIRV_Cross_imageSize(RWBuffer Tex, out uint Param) +{ + uint ret; + Tex.GetDimensions(ret.x); + Param = 0u; + return ret; +} + +void frag_main() +{ + uint _14_dummy_parameter; + int a = int(SPIRV_Cross_imageSize(uImage1D, _14_dummy_parameter)); + uint _22_dummy_parameter; + int2 b = int2(SPIRV_Cross_imageSize(uImage2D, _22_dummy_parameter)); + uint _30_dummy_parameter; + int3 c = int3(SPIRV_Cross_imageSize(uImage2DArray, _30_dummy_parameter)); + uint _36_dummy_parameter; + int3 d = int3(SPIRV_Cross_imageSize(uImage3D, _36_dummy_parameter)); + uint _42_dummy_parameter; + int e = int(SPIRV_Cross_imageSize(uImageBuffer, _42_dummy_parameter)); +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag new file mode 100644 index 0000000..5db5d35 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag @@ -0,0 +1,63 @@ +RWTexture1D uImage1D : register(u0); +RWTexture2D uImage2D : register(u1); +Texture2DArray uImage2DArray : register(t2); +RWTexture3D uImage3D : register(u3); +RWBuffer uImageBuffer : register(u6); + +uint3 SPIRV_Cross_textureSize(Texture2DArray Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param); + return ret; +} + +uint2 SPIRV_Cross_imageSize(RWTexture2D Tex, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(ret.x, ret.y); + Param = 0u; + return ret; +} + +uint SPIRV_Cross_imageSize(RWTexture1D Tex, out uint Param) +{ + uint ret; + Tex.GetDimensions(ret.x); + Param = 0u; + return ret; +} + +uint3 SPIRV_Cross_imageSize(RWTexture3D Tex, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(ret.x, ret.y, ret.z); + Param = 0u; + return ret; +} + +uint SPIRV_Cross_imageSize(RWBuffer Tex, out uint Param) +{ + uint ret; + Tex.GetDimensions(ret.x); + Param = 0u; + return ret; +} + +void frag_main() +{ + uint _14_dummy_parameter; + int a = int(SPIRV_Cross_imageSize(uImage1D, _14_dummy_parameter)); + uint _22_dummy_parameter; + int2 b = int2(SPIRV_Cross_imageSize(uImage2D, _22_dummy_parameter)); + uint _30_dummy_parameter; + int3 c = int3(SPIRV_Cross_textureSize(uImage2DArray, 0u, _30_dummy_parameter)); + uint _36_dummy_parameter; + int3 d = int3(SPIRV_Cross_imageSize(uImage3D, _36_dummy_parameter)); + uint _42_dummy_parameter; + int e = int(SPIRV_Cross_imageSize(uImageBuffer, _42_dummy_parameter)); +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query.frag new file mode 100644 index 0000000..71cefc1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/image-query.frag @@ -0,0 +1,132 @@ +Texture1D uSampler1D : register(t0); +SamplerState _uSampler1D_sampler : register(s0); +Texture2D uSampler2D : register(t1); +SamplerState _uSampler2D_sampler : register(s1); +Texture2DArray uSampler2DArray : register(t2); +SamplerState _uSampler2DArray_sampler : register(s2); +Texture3D uSampler3D : register(t3); +SamplerState _uSampler3D_sampler : register(s3); +TextureCube uSamplerCube : register(t4); +SamplerState _uSamplerCube_sampler : register(s4); +TextureCubeArray uSamplerCubeArray : register(t5); +SamplerState _uSamplerCubeArray_sampler : register(s5); +Buffer uSamplerBuffer : register(t6); +Texture2DMS uSamplerMS : register(t7); +SamplerState _uSamplerMS_sampler : register(s7); +Texture2DMSArray uSamplerMSArray : register(t8); +SamplerState _uSamplerMSArray_sampler : register(s8); + +uint SPIRV_Cross_textureSize(Texture1D Tex, uint Level, out uint Param) +{ + uint ret; + Tex.GetDimensions(Level, ret.x, Param); + return ret; +} + +uint2 SPIRV_Cross_textureSize(Texture2D Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(Level, ret.x, ret.y, Param); + return ret; +} + +uint3 SPIRV_Cross_textureSize(Texture2DArray Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param); + return ret; +} + +uint3 SPIRV_Cross_textureSize(Texture3D Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param); + return ret; +} + +uint SPIRV_Cross_textureSize(Buffer Tex, uint Level, out uint Param) +{ + uint ret; + Tex.GetDimensions(ret.x); + Param = 0u; + return ret; +} + +uint2 SPIRV_Cross_textureSize(TextureCube Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(Level, ret.x, ret.y, Param); + return ret; +} + +uint3 SPIRV_Cross_textureSize(TextureCubeArray Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param); + return ret; +} + +uint2 SPIRV_Cross_textureSize(Texture2DMS Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(ret.x, ret.y, Param); + return ret; +} + +uint3 SPIRV_Cross_textureSize(Texture2DMSArray Tex, uint Level, out uint Param) +{ + uint3 ret; + Tex.GetDimensions(ret.x, ret.y, ret.z, Param); + return ret; +} + +void frag_main() +{ + uint _17_dummy_parameter; + int a = int(SPIRV_Cross_textureSize(uSampler1D, uint(0), _17_dummy_parameter)); + uint _27_dummy_parameter; + int2 b = int2(SPIRV_Cross_textureSize(uSampler2D, uint(0), _27_dummy_parameter)); + uint _37_dummy_parameter; + int3 c = int3(SPIRV_Cross_textureSize(uSampler2DArray, uint(0), _37_dummy_parameter)); + uint _45_dummy_parameter; + int3 d = int3(SPIRV_Cross_textureSize(uSampler3D, uint(0), _45_dummy_parameter)); + uint _53_dummy_parameter; + int2 e = int2(SPIRV_Cross_textureSize(uSamplerCube, uint(0), _53_dummy_parameter)); + uint _61_dummy_parameter; + int3 f = int3(SPIRV_Cross_textureSize(uSamplerCubeArray, uint(0), _61_dummy_parameter)); + uint _69_dummy_parameter; + int g = int(SPIRV_Cross_textureSize(uSamplerBuffer, 0u, _69_dummy_parameter)); + uint _77_dummy_parameter; + int2 h = int2(SPIRV_Cross_textureSize(uSamplerMS, 0u, _77_dummy_parameter)); + uint _85_dummy_parameter; + int3 i = int3(SPIRV_Cross_textureSize(uSamplerMSArray, 0u, _85_dummy_parameter)); + int _89; + SPIRV_Cross_textureSize(uSampler1D, 0u, _89); + int l0 = int(_89); + int _93; + SPIRV_Cross_textureSize(uSampler2D, 0u, _93); + int l1 = int(_93); + int _97; + SPIRV_Cross_textureSize(uSampler2DArray, 0u, _97); + int l2 = int(_97); + int _101; + SPIRV_Cross_textureSize(uSampler3D, 0u, _101); + int l3 = int(_101); + int _105; + SPIRV_Cross_textureSize(uSamplerCube, 0u, _105); + int l4 = int(_105); + int _109; + SPIRV_Cross_textureSize(uSamplerCubeArray, 0u, _109); + int l5 = int(_109); + int _113; + SPIRV_Cross_textureSize(uSamplerMS, 0u, _113); + int s0 = int(_113); + int _117; + SPIRV_Cross_textureSize(uSamplerMSArray, 0u, _117); + int s1 = int(_117); +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/input-attachment-ms.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/input-attachment-ms.frag new file mode 100644 index 0000000..954fa1a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/input-attachment-ms.frag @@ -0,0 +1,39 @@ +Texture2DMS uSubpass0 : register(t0); +Texture2DMS uSubpass1 : register(t1); + +static float4 gl_FragCoord; +static int gl_SampleID; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; + uint gl_SampleID : SV_SampleIndex; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +float4 load_subpasses(Texture2DMS uInput) +{ + float4 _24 = uInput.Load(int2(gl_FragCoord.xy), gl_SampleID); + return _24; +} + +void frag_main() +{ + FragColor = (uSubpass0.Load(int2(gl_FragCoord.xy), 1) + uSubpass1.Load(int2(gl_FragCoord.xy), 2)) + load_subpasses(uSubpass0); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + gl_SampleID = stage_input.gl_SampleID; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/input-attachment.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/input-attachment.frag new file mode 100644 index 0000000..b0e297c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/input-attachment.frag @@ -0,0 +1,35 @@ +Texture2D uSubpass0 : register(t0); +Texture2D uSubpass1 : register(t1); + +static float4 gl_FragCoord; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +float4 load_subpasses(Texture2D uInput) +{ + return uInput.Load(int3(int2(gl_FragCoord.xy), 0)); +} + +void frag_main() +{ + FragColor = uSubpass0.Load(int3(int2(gl_FragCoord.xy), 0)) + load_subpasses(uSubpass1); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/io-block.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/io-block.frag new file mode 100644 index 0000000..52c1f51 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/io-block.frag @@ -0,0 +1,28 @@ +static float4 FragColor; + +struct VertexOut +{ + float4 a : TEXCOORD1; + float4 b : TEXCOORD2; +}; + +static VertexOut _12; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _12.a + _12.b; +} + +SPIRV_Cross_Output main(in VertexOut stage_input_12) +{ + _12 = stage_input_12; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag new file mode 100644 index 0000000..2af0e51 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag @@ -0,0 +1,32 @@ +uniform sampler2D uSampler; + +static float4 FragColor; +static float2 vUV; + +struct SPIRV_Cross_Input +{ + float2 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : COLOR0; +}; + +void frag_main() +{ + float3 _23 = float3(vUV, 5.0f); + FragColor = tex2Dproj(uSampler, float4(_23.xy, 0.0, _23.z)); + FragColor += tex2Dbias(uSampler, float4(vUV, 0.0, 3.0f)); + FragColor += tex2Dlod(uSampler, float4(vUV, 0.0, 2.0f)); + FragColor += tex2Dgrad(uSampler, vUV, 4.0f.xx, 5.0f.xx); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = float4(FragColor); + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/lut-promotion.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000..d148bc1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/lut-promotion.frag @@ -0,0 +1,55 @@ +static const float _16[16] = { 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f, 1.0f, 2.0f, 3.0f, 4.0f }; +static const float4 _60[4] = { 0.0f.xxxx, 1.0f.xxxx, 8.0f.xxxx, 5.0f.xxxx }; +static const float4 _104[4] = { 20.0f.xxxx, 30.0f.xxxx, 50.0f.xxxx, 60.0f.xxxx }; + +static float FragColor; +static int index; + +struct SPIRV_Cross_Input +{ + nointerpolation int index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _16[index]; + if (index < 10) + { + FragColor += _16[index ^ 1]; + } + else + { + FragColor += _16[index & 1]; + } + if (index > 30) + { + FragColor += _60[index & 3].y; + } + else + { + FragColor += _60[index & 1].x; + } + float4 foobar[4] = _60; + if (index > 30) + { + foobar[1].z = 20.0f; + } + FragColor += foobar[index & 3].z; + float4 baz[4] = _60; + baz = _104; + FragColor += baz[index & 3].z; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + index = stage_input.index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/matrix-input.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/matrix-input.frag new file mode 100644 index 0000000..92d87d3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/matrix-input.frag @@ -0,0 +1,26 @@ +static float4 FragColor; +static float4x4 m; + +struct SPIRV_Cross_Input +{ + float4x4 m : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = ((m[0] + m[1]) + m[2]) + m[3]; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + m = stage_input.m; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/mod.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/mod.frag new file mode 100644 index 0000000..1da8f21 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/mod.frag @@ -0,0 +1,71 @@ +static float4 a4; +static float4 b4; +static float3 a3; +static float3 b3; +static float2 a2; +static float2 b2; +static float a1; +static float b1; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 a4 : TEXCOORD0; + float3 a3 : TEXCOORD1; + float2 a2 : TEXCOORD2; + float a1 : TEXCOORD3; + float4 b4 : TEXCOORD4; + float3 b3 : TEXCOORD5; + float2 b2 : TEXCOORD6; + float b1 : TEXCOORD7; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +float mod(float x, float y) +{ + return x - y * floor(x / y); +} + +float2 mod(float2 x, float2 y) +{ + return x - y * floor(x / y); +} + +float3 mod(float3 x, float3 y) +{ + return x - y * floor(x / y); +} + +float4 mod(float4 x, float4 y) +{ + return x - y * floor(x / y); +} + +void frag_main() +{ + float4 m0 = mod(a4, b4); + float3 m1 = mod(a3, b3); + float2 m2 = mod(a2, b2); + float m3 = mod(a1, b1); + FragColor = ((m0 + m1.xyzx) + m2.xyxy) + m3.xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + a4 = stage_input.a4; + b4 = stage_input.b4; + a3 = stage_input.a3; + b3 = stage_input.b3; + a2 = stage_input.a2; + b2 = stage_input.b2; + a1 = stage_input.a1; + b1 = stage_input.b1; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/mrt.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/mrt.frag new file mode 100644 index 0000000..e69e911 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/mrt.frag @@ -0,0 +1,31 @@ +static float4 RT0; +static float4 RT1; +static float4 RT2; +static float4 RT3; + +struct SPIRV_Cross_Output +{ + float4 RT0 : SV_Target0; + float4 RT1 : SV_Target1; + float4 RT2 : SV_Target2; + float4 RT3 : SV_Target3; +}; + +void frag_main() +{ + RT0 = 1.0f.xxxx; + RT1 = 2.0f.xxxx; + RT2 = 3.0f.xxxx; + RT3 = 4.0f.xxxx; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.RT0 = RT0; + stage_output.RT1 = RT1; + stage_output.RT2 = RT2; + stage_output.RT3 = RT3; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/no-return.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/no-return.frag new file mode 100644 index 0000000..3b50282 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/no-return.frag @@ -0,0 +1,8 @@ +void frag_main() +{ +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/no-return2.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/no-return2.frag new file mode 100644 index 0000000..a22ffa7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/no-return2.frag @@ -0,0 +1,17 @@ +static float4 vColor; + +struct SPIRV_Cross_Input +{ + float4 vColor : TEXCOORD0; +}; + +void frag_main() +{ + float4 v = vColor; +} + +void main(SPIRV_Cross_Input stage_input) +{ + vColor = stage_input.vColor; + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag new file mode 100644 index 0000000..a8594bf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag @@ -0,0 +1,52 @@ +struct UBO_1_1 +{ + float4 v[64]; +}; + +ConstantBuffer ubos[] : register(b0, space3); +ByteAddressBuffer ssbos[] : register(t0, space4); +Texture2D uSamplers[] : register(t0, space0); +SamplerState uSamps[] : register(s0, space2); +Texture2D uCombinedSamplers[] : register(t0, space1); +SamplerState _uCombinedSamplers_sampler[] : register(s0, space1); + +static int vIndex; +static float4 FragColor; +static float2 vUV; + +struct SPIRV_Cross_Input +{ + nointerpolation int vIndex : TEXCOORD0; + float2 vUV : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + int i = vIndex; + int _23 = i + 10; + int _34 = i + 40; + FragColor = uSamplers[NonUniformResourceIndex(_23)].Sample(uSamps[NonUniformResourceIndex(_34)], vUV); + int _50 = i + 10; + FragColor = uCombinedSamplers[NonUniformResourceIndex(_50)].Sample(_uCombinedSamplers_sampler[NonUniformResourceIndex(_50)], vUV); + int _66 = i + 20; + int _70 = i + 40; + FragColor += ubos[NonUniformResourceIndex(_66)].v[_70]; + int _84 = i + 50; + int _88 = i + 60; + FragColor += asfloat(ssbos[NonUniformResourceIndex(_84)].Load4(_88 * 16 + 0)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vIndex = stage_input.vIndex; + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/partial-write-preserve.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/partial-write-preserve.frag new file mode 100644 index 0000000..bb5b905 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/partial-write-preserve.frag @@ -0,0 +1,76 @@ +struct B +{ + float a; + float b; +}; + +static const B _80 = { 10.0f, 20.0f }; + +cbuffer UBO : register(b0) +{ + int _42_some_value : packoffset(c0); +}; + + +void partial_inout(inout float4 x) +{ + x.x = 10.0f; +} + +void complete_inout(out float4 x) +{ + x = 50.0f.xxxx; +} + +void branchy_inout(inout float4 v) +{ + v.y = 20.0f; + if (_42_some_value == 20) + { + v = 50.0f.xxxx; + } +} + +void branchy_inout_2(out float4 v) +{ + if (_42_some_value == 20) + { + v = 50.0f.xxxx; + } + else + { + v = 70.0f.xxxx; + } + v.y = 20.0f; +} + +void partial_inout(inout B b) +{ + b.b = 40.0f; +} + +void frag_main() +{ + float4 a = 10.0f.xxxx; + float4 param = a; + partial_inout(param); + a = param; + float4 param_1; + complete_inout(param_1); + a = param_1; + float4 param_2 = a; + branchy_inout(param_2); + a = param_2; + float4 param_3; + branchy_inout_2(param_3); + a = param_3; + B b = _80; + B param_4 = b; + partial_inout(param_4); + b = param_4; +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag new file mode 100644 index 0000000..8923f96 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag @@ -0,0 +1,24 @@ +RWByteAddressBuffer _9 : register(u6, space0); +globallycoherent RasterizerOrderedByteAddressBuffer _42 : register(u3, space0); +RasterizerOrderedByteAddressBuffer _52 : register(u4, space0); +RWTexture2D img4 : register(u5, space0); +RasterizerOrderedTexture2D img : register(u0, space0); +RasterizerOrderedTexture2D img3 : register(u2, space0); +RasterizerOrderedTexture2D img2 : register(u1, space0); + +void frag_main() +{ + _9.Store(0, uint(0)); + img4[int2(1, 1)] = float4(1.0f, 0.0f, 0.0f, 1.0f); + img[int2(0, 0)] = img3[int2(0, 0)]; + uint _39; + InterlockedAdd(img2[int2(0, 0)], 1u, _39); + _42.Store(0, uint(int(_42.Load(0)) + 42)); + uint _55; + _42.InterlockedAnd(4, _52.Load(0), _55); +} + +void main() +{ + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/point-coord-compat.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/point-coord-compat.frag new file mode 100644 index 0000000..6291539 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/point-coord-compat.frag @@ -0,0 +1,19 @@ +static float2 FragColor; + +struct SPIRV_Cross_Output +{ + float2 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float2(0.5f, 0.5f); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/query-lod.desktop.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/query-lod.desktop.frag new file mode 100644 index 0000000..a9d4bd8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/query-lod.desktop.frag @@ -0,0 +1,31 @@ +Texture2D uSampler : register(t0); +SamplerState _uSampler_sampler : register(s0); + +static float4 FragColor; +static float2 vTexCoord; + +struct SPIRV_Cross_Input +{ + float2 vTexCoord : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float _19_tmp = uSampler.CalculateLevelOfDetail(_uSampler_sampler, vTexCoord); + float2 _19 = _19_tmp.xx; + FragColor = _19.xyxy; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vTexCoord = stage_input.vTexCoord; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag new file mode 100644 index 0000000..bbe3e4a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag @@ -0,0 +1,21 @@ +globallycoherent RWByteAddressBuffer _12 : register(u0); + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = asfloat(_12.Load4(0)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/readonly-coherent-ssbo.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/readonly-coherent-ssbo.frag new file mode 100644 index 0000000..02252f9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/readonly-coherent-ssbo.frag @@ -0,0 +1,21 @@ +ByteAddressBuffer _12 : register(t0); + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = asfloat(_12.Load4(0)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/resources.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/resources.frag new file mode 100644 index 0000000..2a3b2be --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/resources.frag @@ -0,0 +1,44 @@ +cbuffer CBuffer : register(b3) +{ + float4 cbuf_a : packoffset(c0); +}; + +cbuffer PushMe +{ + float4 registers_d : packoffset(c0); +}; + +Texture2D uSampledImage : register(t4); +SamplerState _uSampledImage_sampler : register(s4); +Texture2D uTexture : register(t5); +SamplerState uSampler : register(s6); + +static float2 vTex; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float2 vTex : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 c0 = uSampledImage.Sample(_uSampledImage_sampler, vTex); + float4 c1 = uTexture.Sample(uSampler, vTex); + float4 c2 = cbuf_a + registers_d; + FragColor = (c0 + c1) + c2; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vTex = stage_input.vTex; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/row-major-layout-in-struct.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/row-major-layout-in-struct.frag new file mode 100644 index 0000000..5fc45b2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/row-major-layout-in-struct.frag @@ -0,0 +1,47 @@ +struct NonFoo +{ + float4x4 v; + float4x4 w; +}; + +struct Foo +{ + row_major float4x4 v; + row_major float4x4 w; +}; + +cbuffer UBO : register(b0) +{ + Foo _17_foo : packoffset(c0); +}; + + +static float4 FragColor; +static float4 vUV; + +struct SPIRV_Cross_Input +{ + float4 vUV : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + NonFoo f; + f.v = _17_foo.v; + f.w = _17_foo.w; + FragColor = mul(mul(vUV, f.w), f.v); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUV = stage_input.vUV; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-cmp-level-zero.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-cmp-level-zero.frag new file mode 100644 index 0000000..b6df001 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-cmp-level-zero.frag @@ -0,0 +1,51 @@ +Texture2D uSampler2D : register(t0); +SamplerComparisonState _uSampler2D_sampler : register(s0); +Texture2DArray uSampler2DArray : register(t1); +SamplerComparisonState _uSampler2DArray_sampler : register(s1); +TextureCube uSamplerCube : register(t2); +SamplerComparisonState _uSamplerCube_sampler : register(s2); +TextureCubeArray uSamplerCubeArray : register(t3); +SamplerComparisonState _uSamplerCubeArray_sampler : register(s3); + +static float3 vUVRef; +static float4 vDirRef; +static float FragColor; + +struct SPIRV_Cross_Input +{ + float3 vUVRef : TEXCOORD0; + float4 vDirRef : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + float s0 = uSampler2D.SampleCmp(_uSampler2D_sampler, vUVRef.xy, vUVRef.z, int2(-1, -1)); + float s1 = uSampler2DArray.SampleCmp(_uSampler2DArray_sampler, vDirRef.xyz, vDirRef.w, int2(-1, -1)); + float s2 = uSamplerCube.SampleCmp(_uSamplerCube_sampler, vDirRef.xyz, vDirRef.w); + float s3 = uSamplerCubeArray.SampleCmp(_uSamplerCubeArray_sampler, vDirRef, 0.5f); + float l0 = uSampler2D.SampleCmpLevelZero(_uSampler2D_sampler, vUVRef.xy, vUVRef.z, int2(-1, -1)); + float l1 = uSampler2DArray.SampleCmpLevelZero(_uSampler2DArray_sampler, vDirRef.xyz, vDirRef.w, int2(-1, -1)); + float l2 = uSamplerCube.SampleCmpLevelZero(_uSamplerCube_sampler, vDirRef.xyz, vDirRef.w); + float4 _80 = vDirRef; + _80.z = vDirRef.w; + float p0 = uSampler2D.SampleCmp(_uSampler2D_sampler, _80.xy / _80.z, vDirRef.z / _80.z, int2(1, 1)); + float4 _87 = vDirRef; + _87.z = vDirRef.w; + float p1 = uSampler2D.SampleCmpLevelZero(_uSampler2D_sampler, _87.xy / _87.z, vDirRef.z / _87.z, int2(1, 1)); + FragColor = (((((((s0 + s1) + s2) + s3) + l0) + l1) + l2) + p0) + p1; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vUVRef = stage_input.vUVRef; + vDirRef = stage_input.vDirRef; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-in-and-out.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-in-and-out.frag new file mode 100644 index 0000000..185a098 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-in-and-out.frag @@ -0,0 +1,30 @@ +static int gl_SampleMaskIn; +static int gl_SampleMask; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + uint gl_SampleMaskIn : SV_Coverage; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; + uint gl_SampleMask : SV_Coverage; +}; + +void frag_main() +{ + FragColor = 1.0f.xxxx; + gl_SampleMask = gl_SampleMaskIn; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_SampleMaskIn = stage_input.gl_SampleMaskIn; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_SampleMask = gl_SampleMask; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-in.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-in.frag new file mode 100644 index 0000000..8f6cfaf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-in.frag @@ -0,0 +1,32 @@ +static int gl_SampleID; +static int gl_SampleMaskIn; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + uint gl_SampleID : SV_SampleIndex; + uint gl_SampleMaskIn : SV_Coverage; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + if ((gl_SampleMaskIn & (1 << gl_SampleID)) != 0) + { + FragColor = 1.0f.xxxx; + } +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_SampleID = stage_input.gl_SampleID; + gl_SampleMaskIn = stage_input.gl_SampleMaskIn; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-out.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-out.frag new file mode 100644 index 0000000..a966c03 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/sample-mask-out.frag @@ -0,0 +1,23 @@ +static int gl_SampleMask; +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; + uint gl_SampleMask : SV_Coverage; +}; + +void frag_main() +{ + FragColor = 1.0f.xxxx; + gl_SampleMask = 0; +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_SampleMask = gl_SampleMask; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/sampler-array.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/sampler-array.frag new file mode 100644 index 0000000..fd08d42 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/sampler-array.frag @@ -0,0 +1,45 @@ +Texture2D uCombined[4] : register(t0); +SamplerState _uCombined_sampler[4] : register(s0); +Texture2D uTex[4] : register(t4); +SamplerState uSampler[4] : register(s8); +RWTexture2D uImage[8] : register(u12); + +static float4 gl_FragCoord; +static float2 vTex; +static int vIndex; + +struct SPIRV_Cross_Input +{ + float2 vTex : TEXCOORD0; + nointerpolation int vIndex : TEXCOORD1; + float4 gl_FragCoord : SV_Position; +}; + +float4 sample_in_function(Texture2D samp, SamplerState _samp_sampler) +{ + return samp.Sample(_samp_sampler, vTex); +} + +float4 sample_in_function2(Texture2D tex, SamplerState samp) +{ + return tex.Sample(samp, vTex); +} + +void frag_main() +{ + float4 color = uCombined[vIndex].Sample(_uCombined_sampler[vIndex], vTex); + color += uTex[vIndex].Sample(uSampler[vIndex], vTex); + int _72 = vIndex + 1; + color += sample_in_function(uCombined[_72], _uCombined_sampler[_72]); + color += sample_in_function2(uTex[vIndex + 1], uSampler[vIndex + 1]); + uImage[vIndex][int2(gl_FragCoord.xy)] = color; +} + +void main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + vTex = stage_input.vTex; + vIndex = stage_input.vIndex; + frag_main(); +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/sampler-image-arrays.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/sampler-image-arrays.frag new file mode 100644 index 0000000..856f04c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/sampler-image-arrays.frag @@ -0,0 +1,54 @@ +Texture2D uSampler[4] : register(t0); +SamplerState _uSampler_sampler[4] : register(s0); +Texture2D uTextures[4] : register(t8); +SamplerState uSamplers[4] : register(s4); + +static int vIndex; +static float2 vTex; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + nointerpolation float2 vTex : TEXCOORD0; + nointerpolation int vIndex : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +float4 sample_from_global() +{ + return uSampler[vIndex].Sample(_uSampler_sampler[vIndex], vTex + 0.100000001490116119384765625f.xx); +} + +float4 sample_from_argument(Texture2D samplers[4], SamplerState _samplers_sampler[4]) +{ + return samplers[vIndex].Sample(_samplers_sampler[vIndex], vTex + 0.20000000298023223876953125f.xx); +} + +float4 sample_single_from_argument(Texture2D samp, SamplerState _samp_sampler) +{ + return samp.Sample(_samp_sampler, vTex + 0.300000011920928955078125f.xx); +} + +void frag_main() +{ + FragColor = 0.0f.xxxx; + FragColor += uTextures[2].Sample(uSamplers[1], vTex); + FragColor += uSampler[vIndex].Sample(_uSampler_sampler[vIndex], vTex); + FragColor += sample_from_global(); + FragColor += sample_from_argument(uSampler, _uSampler_sampler); + FragColor += sample_single_from_argument(uSampler[3], _uSampler_sampler[3]); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vIndex = stage_input.vIndex; + vTex = stage_input.vTex; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..0fb694c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/scalar-refract-reflect.frag @@ -0,0 +1,49 @@ +static float FragColor; +static float3 vRefract; + +struct SPIRV_Cross_Input +{ + float3 vRefract : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +float SPIRV_Cross_Reflect(float i, float n) +{ + return i - 2.0 * dot(n, i) * n; +} + +float SPIRV_Cross_Refract(float i, float n, float eta) +{ + float NoI = n * i; + float NoI2 = NoI * NoI; + float k = 1.0 - eta * eta * (1.0 - NoI2); + if (k < 0.0) + { + return 0.0; + } + else + { + return eta * i - (eta * NoI + sqrt(k)) * n; + } +} + +void frag_main() +{ + FragColor = SPIRV_Cross_Refract(vRefract.x, vRefract.y, vRefract.z); + FragColor += SPIRV_Cross_Reflect(vRefract.x, vRefract.y); + FragColor += refract(vRefract.xy, vRefract.yz, vRefract.z).y; + FragColor += reflect(vRefract.xy, vRefract.zy).y; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vRefract = stage_input.vRefract; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag new file mode 100644 index 0000000..08d8b0e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag @@ -0,0 +1,32 @@ +uniform sampler2D uSamp; +uniform sampler2D SPIRV_Cross_CombineduTuS; + +static float4 FragColor; + +struct SPIRV_Cross_Output +{ + float4 FragColor : COLOR0; +}; + +float4 samp(sampler2D uSamp_1) +{ + return tex2D(uSamp_1, 0.5f.xx); +} + +float4 samp_1(sampler2D SPIRV_Cross_CombinedTS) +{ + return tex2D(SPIRV_Cross_CombinedTS, 0.5f.xx); +} + +void frag_main() +{ + FragColor = samp(uSamp) + samp_1(SPIRV_Cross_CombineduTuS); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = float4(FragColor); + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/spec-constant-block-size.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/spec-constant-block-size.frag new file mode 100644 index 0000000..415886d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/spec-constant-block-size.frag @@ -0,0 +1,37 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 2 +#endif +static const int Value = SPIRV_CROSS_CONSTANT_ID_10; + +cbuffer SpecConstArray : register(b0) +{ + float4 _15_samples[Value] : packoffset(c0); +}; + + +static float4 FragColor; +static int Index; + +struct SPIRV_Cross_Input +{ + nointerpolation int Index : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = _15_samples[Index]; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + Index = stage_input.Index; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/spec-constant-ternary.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000..942da54 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,26 @@ +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 10u +#endif +static const uint s = SPIRV_CROSS_CONSTANT_ID_0; +static const bool _13 = (s > 20u); +static const uint f = _13 ? 30u : 50u; + +static float FragColor; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = float(f); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/switch-unsigned-case.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..d7ec92f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/switch-unsigned-case.frag @@ -0,0 +1,38 @@ +cbuffer Buff : register(b0) +{ + uint _15_TestVal : packoffset(c0); +}; + + +static float4 fsout_Color; + +struct SPIRV_Cross_Output +{ + float4 fsout_Color : SV_Target0; +}; + +void frag_main() +{ + fsout_Color = 1.0f.xxxx; + switch (_15_TestVal) + { + case 0u: + { + fsout_Color = 0.100000001490116119384765625f.xxxx; + break; + } + case 1u: + { + fsout_Color = 0.20000000298023223876953125f.xxxx; + break; + } + } +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.fsout_Color = fsout_Color; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/swizzle-scalar.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/swizzle-scalar.frag new file mode 100644 index 0000000..ab310b8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/swizzle-scalar.frag @@ -0,0 +1,41 @@ +static float4 Float; +static float vFloat; +static int4 Int; +static int vInt; +static float4 Float2; +static int4 Int2; + +struct SPIRV_Cross_Input +{ + nointerpolation float vFloat : TEXCOORD0; + nointerpolation int vInt : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 Float : SV_Target0; + int4 Int : SV_Target1; + float4 Float2 : SV_Target2; + int4 Int2 : SV_Target3; +}; + +void frag_main() +{ + Float = vFloat.xxxx * 2.0f; + Int = vInt.xxxx * int4(2, 2, 2, 2); + Float2 = 10.0f.xxxx; + Int2 = int4(10, 10, 10, 10); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vFloat = stage_input.vFloat; + vInt = stage_input.vInt; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.Float = Float; + stage_output.Int = Int; + stage_output.Float2 = Float2; + stage_output.Int2 = Int2; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling-ms.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling-ms.frag new file mode 100644 index 0000000..854ad50 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling-ms.frag @@ -0,0 +1,33 @@ +Texture2DMS uTex : register(t0); +SamplerState _uTex_sampler : register(s0); + +static float4 gl_FragCoord; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uTex.Load(int2(gl_FragCoord.xy), 0); + FragColor += uTex.Load(int2(gl_FragCoord.xy), 1); + FragColor += uTex.Load(int2(gl_FragCoord.xy), 2); + FragColor += uTex.Load(int2(gl_FragCoord.xy), 3); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling.frag new file mode 100644 index 0000000..5832175 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling.frag @@ -0,0 +1,106 @@ +Texture1D tex1d : register(t0); +SamplerState _tex1d_sampler : register(s0); +Texture2D tex2d : register(t1); +SamplerState _tex2d_sampler : register(s1); +Texture3D tex3d : register(t2); +SamplerState _tex3d_sampler : register(s2); +TextureCube texCube : register(t3); +SamplerState _texCube_sampler : register(s3); +Texture1D tex1dShadow : register(t4); +SamplerComparisonState _tex1dShadow_sampler : register(s4); +Texture2D tex2dShadow : register(t5); +SamplerComparisonState _tex2dShadow_sampler : register(s5); +TextureCube texCubeShadow : register(t6); +SamplerComparisonState _texCubeShadow_sampler : register(s6); +Texture1DArray tex1dArray : register(t7); +SamplerState _tex1dArray_sampler : register(s7); +Texture2DArray tex2dArray : register(t8); +SamplerState _tex2dArray_sampler : register(s8); +TextureCubeArray texCubeArray : register(t9); +SamplerState _texCubeArray_sampler : register(s9); +Texture2D separateTex2d : register(t12); +SamplerState samplerNonDepth : register(s11); +Texture2D separateTex2dDepth : register(t13); +SamplerComparisonState samplerDepth : register(s10); + +static float texCoord1d; +static float2 texCoord2d; +static float3 texCoord3d; +static float4 texCoord4d; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float texCoord1d : TEXCOORD0; + float2 texCoord2d : TEXCOORD1; + float3 texCoord3d : TEXCOORD2; + float4 texCoord4d : TEXCOORD3; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 texcolor = tex1d.Sample(_tex1d_sampler, texCoord1d); + texcolor += tex1d.Sample(_tex1d_sampler, texCoord1d, 1); + texcolor += tex1d.SampleLevel(_tex1d_sampler, texCoord1d, 2.0f); + texcolor += tex1d.SampleGrad(_tex1d_sampler, texCoord1d, 1.0f, 2.0f); + float2 _41 = float2(texCoord1d, 2.0f); + texcolor += tex1d.Sample(_tex1d_sampler, _41.x / _41.y); + texcolor += tex1d.SampleBias(_tex1d_sampler, texCoord1d, 1.0f); + texcolor += tex2d.Sample(_tex2d_sampler, texCoord2d); + texcolor += tex2d.Sample(_tex2d_sampler, texCoord2d, int2(1, 2)); + texcolor += tex2d.SampleLevel(_tex2d_sampler, texCoord2d, 2.0f); + texcolor += tex2d.SampleGrad(_tex2d_sampler, texCoord2d, float2(1.0f, 2.0f), float2(3.0f, 4.0f)); + float3 _88 = float3(texCoord2d, 2.0f); + texcolor += tex2d.Sample(_tex2d_sampler, _88.xy / _88.z); + texcolor += tex2d.SampleBias(_tex2d_sampler, texCoord2d, 1.0f); + texcolor += tex3d.Sample(_tex3d_sampler, texCoord3d); + texcolor += tex3d.Sample(_tex3d_sampler, texCoord3d, int3(1, 2, 3)); + texcolor += tex3d.SampleLevel(_tex3d_sampler, texCoord3d, 2.0f); + texcolor += tex3d.SampleGrad(_tex3d_sampler, texCoord3d, float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f)); + float4 _135 = float4(texCoord3d, 2.0f); + texcolor += tex3d.Sample(_tex3d_sampler, _135.xyz / _135.w); + texcolor += tex3d.SampleBias(_tex3d_sampler, texCoord3d, 1.0f); + texcolor += texCube.Sample(_texCube_sampler, texCoord3d); + texcolor += texCube.SampleLevel(_texCube_sampler, texCoord3d, 2.0f); + texcolor += texCube.SampleBias(_texCube_sampler, texCoord3d, 1.0f); + float3 _170 = float3(texCoord1d, 0.0f, 0.0f); + texcolor.w += tex1dShadow.SampleCmp(_tex1dShadow_sampler, _170.x, _170.z); + float3 _188 = float3(texCoord2d, 0.0f); + texcolor.w += tex2dShadow.SampleCmp(_tex2dShadow_sampler, _188.xy, _188.z); + float4 _204 = float4(texCoord3d, 0.0f); + texcolor.w += texCubeShadow.SampleCmp(_texCubeShadow_sampler, _204.xyz, _204.w); + texcolor += tex1dArray.Sample(_tex1dArray_sampler, texCoord2d); + texcolor += tex2dArray.Sample(_tex2dArray_sampler, texCoord3d); + texcolor += texCubeArray.Sample(_texCubeArray_sampler, texCoord4d); + texcolor += tex2d.GatherRed(_tex2d_sampler, texCoord2d); + texcolor += tex2d.GatherRed(_tex2d_sampler, texCoord2d); + texcolor += tex2d.GatherGreen(_tex2d_sampler, texCoord2d); + texcolor += tex2d.GatherBlue(_tex2d_sampler, texCoord2d); + texcolor += tex2d.GatherAlpha(_tex2d_sampler, texCoord2d); + texcolor += tex2d.GatherRed(_tex2d_sampler, texCoord2d, int2(1, 1)); + texcolor += tex2d.GatherRed(_tex2d_sampler, texCoord2d, int2(1, 1)); + texcolor += tex2d.GatherGreen(_tex2d_sampler, texCoord2d, int2(1, 1)); + texcolor += tex2d.GatherBlue(_tex2d_sampler, texCoord2d, int2(1, 1)); + texcolor += tex2d.GatherAlpha(_tex2d_sampler, texCoord2d, int2(1, 1)); + texcolor += tex2d.Load(int3(int2(1, 2), 0)); + texcolor += separateTex2d.Sample(samplerNonDepth, texCoord2d); + texcolor.w += separateTex2dDepth.SampleCmp(samplerDepth, texCoord3d.xy, texCoord3d.z); + FragColor = texcolor; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + texCoord1d = stage_input.texCoord1d; + texCoord2d = stage_input.texCoord2d; + texCoord3d = stage_input.texCoord3d; + texCoord4d = stage_input.texCoord4d; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling.sm30.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling.sm30.frag new file mode 100644 index 0000000..10c6c06 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/tex-sampling.sm30.frag @@ -0,0 +1,83 @@ +uniform sampler1D tex1d; +uniform sampler2D tex2d; +uniform sampler3D tex3d; +uniform samplerCUBE texCube; +uniform sampler1D tex1dShadow; +uniform sampler2D tex2dShadow; + +static float texCoord1d; +static float2 texCoord2d; +static float3 texCoord3d; +static float4 FragColor; +static float4 texCoord4d; + +struct SPIRV_Cross_Input +{ + float texCoord1d : TEXCOORD0; + float2 texCoord2d : TEXCOORD1; + float3 texCoord3d : TEXCOORD2; + float4 texCoord4d : TEXCOORD3; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : COLOR0; +}; + +void frag_main() +{ + float4 texcolor = tex1D(tex1d, texCoord1d); + texcolor += tex1Dlod(tex1d, float4(texCoord1d, 0.0, 0.0, 2.0f)); + texcolor += tex1Dgrad(tex1d, texCoord1d, 1.0f, 2.0f); + float2 _34 = float2(texCoord1d, 2.0f); + texcolor += tex1Dproj(tex1d, float4(_34.x, 0.0, 0.0, _34.y)); + texcolor += tex1Dbias(tex1d, float4(texCoord1d, 0.0, 0.0, 1.0f)); + texcolor += tex2D(tex2d, texCoord2d); + texcolor += tex2Dlod(tex2d, float4(texCoord2d, 0.0, 2.0f)); + texcolor += tex2Dgrad(tex2d, texCoord2d, float2(1.0f, 2.0f), float2(3.0f, 4.0f)); + float3 _73 = float3(texCoord2d, 2.0f); + texcolor += tex2Dproj(tex2d, float4(_73.xy, 0.0, _73.z)); + texcolor += tex2Dbias(tex2d, float4(texCoord2d, 0.0, 1.0f)); + texcolor += tex3D(tex3d, texCoord3d); + texcolor += tex3Dlod(tex3d, float4(texCoord3d, 2.0f)); + texcolor += tex3Dgrad(tex3d, texCoord3d, float3(1.0f, 2.0f, 3.0f), float3(4.0f, 5.0f, 6.0f)); + float4 _112 = float4(texCoord3d, 2.0f); + texcolor += tex3Dproj(tex3d, float4(_112.xyz, _112.w)); + texcolor += tex3Dbias(tex3d, float4(texCoord3d, 1.0f)); + texcolor += texCUBE(texCube, texCoord3d); + texcolor += texCUBElod(texCube, float4(texCoord3d, 2.0f)); + texcolor += texCUBEbias(texCube, float4(texCoord3d, 1.0f)); + float3 _147 = float3(texCoord1d, 0.0f, 0.0f); + texcolor.w += tex1Dproj(tex1dShadow, float4(_147.x, 0.0, _147.z, 1.0)).x; + float3 _159 = float3(texCoord1d, 0.0f, 0.0f); + texcolor.w += tex1Dlod(tex1dShadow, float4(_159.x, 0.0, _159.z, 2.0f)).x; + float4 _168 = float4(texCoord1d, 0.0f, 0.0f, 2.0f); + float4 _171 = _168; + _171.y = _168.w; + texcolor.w += tex1Dproj(tex1dShadow, float4(_171.x, 0.0, _168.z, _171.y)).x; + float3 _179 = float3(texCoord1d, 0.0f, 0.0f); + texcolor.w += tex1Dbias(tex1dShadow, float4(_179.x, 0.0, _179.z, 1.0f)).x; + float3 _194 = float3(texCoord2d, 0.0f); + texcolor.w += tex2Dproj(tex2dShadow, float4(_194.xy, _194.z, 1.0)).x; + float3 _205 = float3(texCoord2d, 0.0f); + texcolor.w += tex2Dlod(tex2dShadow, float4(_205.xy, _205.z, 2.0f)).x; + float4 _216 = float4(texCoord2d, 0.0f, 2.0f); + float4 _219 = _216; + _219.z = _216.w; + texcolor.w += tex2Dproj(tex2dShadow, float4(_219.xy, _216.z, _219.z)).x; + float3 _229 = float3(texCoord2d, 0.0f); + texcolor.w += tex2Dbias(tex2dShadow, float4(_229.xy, _229.z, 1.0f)).x; + FragColor = texcolor; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + texCoord1d = stage_input.texCoord1d; + texCoord2d = stage_input.texCoord2d; + texCoord3d = stage_input.texCoord3d; + texCoord4d = stage_input.texCoord4d; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = float4(FragColor); + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/texel-fetch-offset.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..c7ae589 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/texel-fetch-offset.frag @@ -0,0 +1,31 @@ +Texture2D uTexture : register(t0); +SamplerState _uTexture_sampler : register(s0); + +static float4 gl_FragCoord; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float4 gl_FragCoord : SV_Position; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = uTexture.Load(int3(int2(gl_FragCoord.xy), 0), int2(1, 1)); + FragColor += uTexture.Load(int3(int2(gl_FragCoord.xy), 0), int2(-1, 1)); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_FragCoord = stage_input.gl_FragCoord; + gl_FragCoord.w = 1.0 / gl_FragCoord.w; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/texture-proj-shadow.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/texture-proj-shadow.frag new file mode 100644 index 0000000..07e0600 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/texture-proj-shadow.frag @@ -0,0 +1,51 @@ +Texture1D uShadow1D : register(t0); +SamplerComparisonState _uShadow1D_sampler : register(s0); +Texture2D uShadow2D : register(t1); +SamplerComparisonState _uShadow2D_sampler : register(s1); +Texture1D uSampler1D : register(t2); +SamplerState _uSampler1D_sampler : register(s2); +Texture2D uSampler2D : register(t3); +SamplerState _uSampler2D_sampler : register(s3); +Texture3D uSampler3D : register(t4); +SamplerState _uSampler3D_sampler : register(s4); + +static float FragColor; +static float4 vClip4; +static float2 vClip2; +static float3 vClip3; + +struct SPIRV_Cross_Input +{ + float3 vClip3 : TEXCOORD0; + float4 vClip4 : TEXCOORD1; + float2 vClip2 : TEXCOORD2; +}; + +struct SPIRV_Cross_Output +{ + float FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 _20 = vClip4; + _20.y = vClip4.w; + FragColor = uShadow1D.SampleCmp(_uShadow1D_sampler, _20.x / _20.y, vClip4.z / _20.y); + float4 _30 = vClip4; + _30.z = vClip4.w; + FragColor = uShadow2D.SampleCmp(_uShadow2D_sampler, _30.xy / _30.z, vClip4.z / _30.z); + FragColor = uSampler1D.Sample(_uSampler1D_sampler, vClip2.x / vClip2.y).x; + FragColor = uSampler2D.Sample(_uSampler2D_sampler, vClip3.xy / vClip3.z).x; + FragColor = uSampler3D.Sample(_uSampler3D_sampler, vClip4.xyz / vClip4.w).x; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vClip4 = stage_input.vClip4; + vClip2 = stage_input.vClip2; + vClip3 = stage_input.vClip3; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/texture-size-combined-image-sampler.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/texture-size-combined-image-sampler.frag new file mode 100644 index 0000000..d5c3737 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/texture-size-combined-image-sampler.frag @@ -0,0 +1,30 @@ +Texture2D uTex : register(t0); +SamplerState uSampler : register(s1); + +static int2 FooOut; + +struct SPIRV_Cross_Output +{ + int2 FooOut : SV_Target0; +}; + +uint2 SPIRV_Cross_textureSize(Texture2D Tex, uint Level, out uint Param) +{ + uint2 ret; + Tex.GetDimensions(Level, ret.x, ret.y, Param); + return ret; +} + +void frag_main() +{ + uint _23_dummy_parameter; + FooOut = int2(SPIRV_Cross_textureSize(uTex, uint(0), _23_dummy_parameter)); +} + +SPIRV_Cross_Output main() +{ + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FooOut = FooOut; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/unary-enclose.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/unary-enclose.frag new file mode 100644 index 0000000..597c57f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/unary-enclose.frag @@ -0,0 +1,32 @@ +static float4 FragColor; +static float4 vIn; +static int4 vIn1; + +struct SPIRV_Cross_Input +{ + float4 vIn : TEXCOORD0; + nointerpolation int4 vIn1 : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + FragColor = -(-vIn); + int4 a = ~(~vIn1); + bool b = false; + b = !(!b); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vIn = stage_input.vIn; + vIn1 = stage_input.vIn1; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/unorm-snorm-packing.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/unorm-snorm-packing.frag new file mode 100644 index 0000000..57b5950 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/unorm-snorm-packing.frag @@ -0,0 +1,109 @@ +static float4 FP32Out; +static uint UNORM8; +static uint SNORM8; +static uint UNORM16; +static uint SNORM16; +static uint UNORM8Out; +static float4 FP32; +static uint SNORM8Out; +static uint UNORM16Out; +static uint SNORM16Out; + +struct SPIRV_Cross_Input +{ + nointerpolation uint SNORM8 : TEXCOORD0; + nointerpolation uint UNORM8 : TEXCOORD1; + nointerpolation uint SNORM16 : TEXCOORD2; + nointerpolation uint UNORM16 : TEXCOORD3; + nointerpolation float4 FP32 : TEXCOORD4; +}; + +struct SPIRV_Cross_Output +{ + float4 FP32Out : SV_Target0; + uint UNORM8Out : SV_Target1; + uint SNORM8Out : SV_Target2; + uint UNORM16Out : SV_Target3; + uint SNORM16Out : SV_Target4; +}; + +uint SPIRV_Cross_packUnorm4x8(float4 value) +{ + uint4 Packed = uint4(round(saturate(value) * 255.0)); + return Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24); +} + +float4 SPIRV_Cross_unpackUnorm4x8(uint value) +{ + uint4 Packed = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24); + return float4(Packed) / 255.0; +} + +uint SPIRV_Cross_packSnorm4x8(float4 value) +{ + int4 Packed = int4(round(clamp(value, -1.0, 1.0) * 127.0)) & 0xff; + return uint(Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24)); +} + +float4 SPIRV_Cross_unpackSnorm4x8(uint value) +{ + int SignedValue = int(value); + int4 Packed = int4(SignedValue << 24, SignedValue << 16, SignedValue << 8, SignedValue) >> 24; + return clamp(float4(Packed) / 127.0, -1.0, 1.0); +} + +uint SPIRV_Cross_packUnorm2x16(float2 value) +{ + uint2 Packed = uint2(round(saturate(value) * 65535.0)); + return Packed.x | (Packed.y << 16); +} + +float2 SPIRV_Cross_unpackUnorm2x16(uint value) +{ + uint2 Packed = uint2(value & 0xffff, value >> 16); + return float2(Packed) / 65535.0; +} + +uint SPIRV_Cross_packSnorm2x16(float2 value) +{ + int2 Packed = int2(round(clamp(value, -1.0, 1.0) * 32767.0)) & 0xffff; + return uint(Packed.x | (Packed.y << 16)); +} + +float2 SPIRV_Cross_unpackSnorm2x16(uint value) +{ + int SignedValue = int(value); + int2 Packed = int2(SignedValue << 16, SignedValue) >> 16; + return clamp(float2(Packed) / 32767.0, -1.0, 1.0); +} + +void frag_main() +{ + FP32Out = SPIRV_Cross_unpackUnorm4x8(UNORM8); + FP32Out = SPIRV_Cross_unpackSnorm4x8(SNORM8); + float2 _21 = SPIRV_Cross_unpackUnorm2x16(UNORM16); + FP32Out = float4(_21.x, _21.y, FP32Out.z, FP32Out.w); + float2 _26 = SPIRV_Cross_unpackSnorm2x16(SNORM16); + FP32Out = float4(_26.x, _26.y, FP32Out.z, FP32Out.w); + UNORM8Out = SPIRV_Cross_packUnorm4x8(FP32); + SNORM8Out = SPIRV_Cross_packSnorm4x8(FP32); + UNORM16Out = SPIRV_Cross_packUnorm2x16(FP32.xy); + SNORM16Out = SPIRV_Cross_packSnorm2x16(FP32.zw); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + UNORM8 = stage_input.UNORM8; + SNORM8 = stage_input.SNORM8; + UNORM16 = stage_input.UNORM16; + SNORM16 = stage_input.SNORM16; + FP32 = stage_input.FP32; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FP32Out = FP32Out; + stage_output.UNORM8Out = UNORM8Out; + stage_output.SNORM8Out = SNORM8Out; + stage_output.UNORM16Out = UNORM16Out; + stage_output.SNORM16Out = SNORM16Out; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/frag/various-glsl-ops.frag b/third_party/spirv-cross/reference/shaders-hlsl/frag/various-glsl-ops.frag new file mode 100644 index 0000000..f0b3454 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/frag/various-glsl-ops.frag @@ -0,0 +1,28 @@ +static float2 interpolant; +static float4 FragColor; + +struct SPIRV_Cross_Input +{ + float2 interpolant : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float4 FragColor : SV_Target0; +}; + +void frag_main() +{ + float4 color = float4(0.0f, 0.0f, 0.0f, EvaluateAttributeSnapped(interpolant, 0.100000001490116119384765625f.xx).x); + color += float4(0.0f, 0.0f, 0.0f, ddx_coarse(interpolant.x)); + FragColor = color; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + interpolant = stage_input.interpolant; + frag_main(); + SPIRV_Cross_Output stage_output; + stage_output.FragColor = FragColor; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/basic.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/basic.vert new file mode 100644 index 0000000..e0bcebf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/basic.vert @@ -0,0 +1,39 @@ +cbuffer UBO : register(b0) +{ + row_major float4x4 _16_uMVP : packoffset(c0); +}; + + +static float4 gl_Position; +static float4 aVertex; +static float3 vNormal; +static float3 aNormal; + +struct SPIRV_Cross_Input +{ + float4 aVertex : TEXCOORD0; + float3 aNormal : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float3 vNormal : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = mul(aVertex, _16_uMVP); + vNormal = aNormal; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + aVertex = stage_input.aVertex; + aNormal = stage_input.aNormal; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vNormal = vNormal; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/clip-cull-distance.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/clip-cull-distance.vert new file mode 100644 index 0000000..7e0d104 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/clip-cull-distance.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static float gl_ClipDistance[2]; +static float gl_CullDistance[1]; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; + float2 gl_ClipDistance0 : SV_ClipDistance0; + float gl_CullDistance0 : SV_CullDistance0; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; + gl_ClipDistance[0] = 0.0f; + gl_ClipDistance[1] = 0.0f; + gl_CullDistance[0] = 4.0f; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.gl_ClipDistance0.x = gl_ClipDistance[0]; + stage_output.gl_ClipDistance0.y = gl_ClipDistance[1]; + stage_output.gl_CullDistance0.x = gl_CullDistance[0]; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/instancing.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/instancing.vert new file mode 100644 index 0000000..48b2df2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/instancing.vert @@ -0,0 +1,28 @@ +static float4 gl_Position; +static int gl_VertexIndex; +static int gl_InstanceIndex; +struct SPIRV_Cross_Input +{ + uint gl_VertexIndex : SV_VertexID; + uint gl_InstanceIndex : SV_InstanceID; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = float(gl_VertexIndex + gl_InstanceIndex).xxxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + gl_VertexIndex = int(stage_input.gl_VertexIndex); + gl_InstanceIndex = int(stage_input.gl_InstanceIndex); + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/locations.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/locations.vert new file mode 100644 index 0000000..b06b204 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/locations.vert @@ -0,0 +1,75 @@ +struct Foo +{ + float3 a; + float3 b; + float3 c; +}; + +static float4 gl_Position; +static float4 Input2; +static float4 Input4; +static float4 Input0; +static float vLocation0; +static float vLocation1; +static float vLocation2[2]; +static Foo vLocation4; +static float vLocation9; + +struct VertexOut +{ + float3 color : TEXCOORD7; + float3 foo : TEXCOORD8; +}; + +static VertexOut vout; + +struct SPIRV_Cross_Input +{ + float4 Input0 : TEXCOORD0; + float4 Input2 : TEXCOORD2; + float4 Input4 : TEXCOORD4; +}; + +struct SPIRV_Cross_Output +{ + float vLocation0 : TEXCOORD0; + float vLocation1 : TEXCOORD1; + float vLocation2[2] : TEXCOORD2; + Foo vLocation4 : TEXCOORD4; + float vLocation9 : TEXCOORD9; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = ((1.0f.xxxx + Input2) + Input4) + Input0; + vLocation0 = 0.0f; + vLocation1 = 1.0f; + vLocation2[0] = 2.0f; + vLocation2[1] = 2.0f; + Foo foo; + foo.a = 1.0f.xxx; + foo.b = 1.0f.xxx; + foo.c = 1.0f.xxx; + vLocation4 = foo; + vLocation9 = 9.0f; + vout.color = 2.0f.xxx; + vout.foo = 4.0f.xxx; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input, out VertexOut stage_outputvout) +{ + Input2 = stage_input.Input2; + Input4 = stage_input.Input4; + Input0 = stage_input.Input0; + vert_main(); + stage_outputvout = vout; + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vLocation0 = vLocation0; + stage_output.vLocation1 = vLocation1; + stage_output.vLocation2 = vLocation2; + stage_output.vLocation4 = vLocation4; + stage_output.vLocation9 = vLocation9; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/matrix-attribute.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/matrix-attribute.vert new file mode 100644 index 0000000..a3d0eef --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/matrix-attribute.vert @@ -0,0 +1,35 @@ +static float4 gl_Position; +static float4x4 m; +static float3 pos; + +struct SPIRV_Cross_Input +{ + float3 pos : TEXCOORD0; + float4 m_0 : TEXCOORD1_0; + float4 m_1 : TEXCOORD1_1; + float4 m_2 : TEXCOORD1_2; + float4 m_3 : TEXCOORD1_3; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = mul(float4(pos, 1.0f), m); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + m[0] = stage_input.m_0; + m[1] = stage_input.m_1; + m[2] = stage_input.m_2; + m[3] = stage_input.m_3; + pos = stage_input.pos; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/matrix-output.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/matrix-output.vert new file mode 100644 index 0000000..dc776cb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/matrix-output.vert @@ -0,0 +1,23 @@ +static float4 gl_Position; +static float4x4 m; + +struct SPIRV_Cross_Output +{ + float4x4 m : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; + m = float4x4(float4(1.0f, 0.0f, 0.0f, 0.0f), float4(0.0f, 1.0f, 0.0f, 0.0f), float4(0.0f, 0.0f, 1.0f, 0.0f), float4(0.0f, 0.0f, 0.0f, 1.0f)); +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.m = m; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/no-input.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/no-input.vert new file mode 100644 index 0000000..c98544d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/no-input.vert @@ -0,0 +1,18 @@ +static float4 gl_Position; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/point-size-compat.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/point-size-compat.vert new file mode 100644 index 0000000..95f45d0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/point-size-compat.vert @@ -0,0 +1,20 @@ +static float4 gl_Position; +static float gl_PointSize; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; + gl_PointSize = 1.0f; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/qualifiers.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/qualifiers.vert new file mode 100644 index 0000000..13ee2a8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/qualifiers.vert @@ -0,0 +1,50 @@ +static float4 gl_Position; +static float vFlat; +static float vCentroid; +static float vSample; +static float vNoperspective; + +struct Block +{ + nointerpolation float vFlat : TEXCOORD4; + centroid float vCentroid : TEXCOORD5; + sample float vSample : TEXCOORD6; + noperspective float vNoperspective : TEXCOORD7; +}; + +static Block vout; + +struct SPIRV_Cross_Output +{ + nointerpolation float vFlat : TEXCOORD0; + centroid float vCentroid : TEXCOORD1; + sample float vSample : TEXCOORD2; + noperspective float vNoperspective : TEXCOORD3; + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = 1.0f.xxxx; + vFlat = 0.0f; + vCentroid = 1.0f; + vSample = 2.0f; + vNoperspective = 3.0f; + vout.vFlat = 0.0f; + vout.vCentroid = 1.0f; + vout.vSample = 2.0f; + vout.vNoperspective = 3.0f; +} + +SPIRV_Cross_Output main(out Block stage_outputvout) +{ + vert_main(); + stage_outputvout = vout; + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.vFlat = vFlat; + stage_output.vCentroid = vCentroid; + stage_output.vSample = vSample; + stage_output.vNoperspective = vNoperspective; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/read-from-row-major-array.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..f656c0a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/read-from-row-major-array.vert @@ -0,0 +1,65 @@ +cbuffer Block : register(b0) +{ + column_major float2x3 _104_var[3][4] : packoffset(c0); +}; + + +static float4 gl_Position; +static float4 a_position; +static float v_vtxResult; + +struct SPIRV_Cross_Input +{ + float4 a_position : TEXCOORD0; +}; + +struct SPIRV_Cross_Output +{ + float v_vtxResult : TEXCOORD0; + float4 gl_Position : SV_Position; +}; + +float compare_float(float a, float b) +{ + return float(abs(a - b) < 0.0500000007450580596923828125f); +} + +float compare_vec3(float3 a, float3 b) +{ + float param = a.x; + float param_1 = b.x; + float param_2 = a.y; + float param_3 = b.y; + float param_4 = a.z; + float param_5 = b.z; + return (compare_float(param, param_1) * compare_float(param_2, param_3)) * compare_float(param_4, param_5); +} + +float compare_mat2x3(float2x3 a, float2x3 b) +{ + float3 param = a[0]; + float3 param_1 = b[0]; + float3 param_2 = a[1]; + float3 param_3 = b[1]; + return compare_vec3(param, param_1) * compare_vec3(param_2, param_3); +} + +void vert_main() +{ + gl_Position = a_position; + float result = 1.0f; + float2x3 param = _104_var[0][0]; + float2x3 param_1 = float2x3(float3(2.0f, 6.0f, -6.0f), float3(0.0f, 5.0f, 5.0f)); + result *= compare_mat2x3(param, param_1); + v_vtxResult = result; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + a_position = stage_input.a_position; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + stage_output.v_vtxResult = v_vtxResult; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/return-array.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/return-array.vert new file mode 100644 index 0000000..83e3a28 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/return-array.vert @@ -0,0 +1,48 @@ +static const float4 _20[2] = { 10.0f.xxxx, 20.0f.xxxx }; + +static float4 gl_Position; +static float4 vInput0; +static float4 vInput1; + +struct SPIRV_Cross_Input +{ + float4 vInput0 : TEXCOORD0; + float4 vInput1 : TEXCOORD1; +}; + +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void test(out float4 SPIRV_Cross_return_value[2]) +{ + SPIRV_Cross_return_value = _20; +} + +void test2(out float4 SPIRV_Cross_return_value[2]) +{ + float4 foobar[2]; + foobar[0] = vInput0; + foobar[1] = vInput1; + SPIRV_Cross_return_value = foobar; +} + +void vert_main() +{ + float4 _42[2]; + test(_42); + float4 _44[2]; + test2(_44); + gl_Position = _42[0] + _44[1]; +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + vInput0 = stage_input.vInput0; + vInput1 = stage_input.vInput1; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/sampler-buffers.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/sampler-buffers.vert new file mode 100644 index 0000000..a4329db --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/sampler-buffers.vert @@ -0,0 +1,27 @@ +Buffer uFloatSampler : register(t1); +Buffer uIntSampler : register(t2); +Buffer uUintSampler : register(t3); + +static float4 gl_Position; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +float4 sample_from_function(Buffer s0, Buffer s1, Buffer s2) +{ + return (s0.Load(20) + asfloat(s1.Load(40))) + asfloat(s2.Load(60)); +} + +void vert_main() +{ + gl_Position = sample_from_function(uFloatSampler, uIntSampler, uUintSampler); +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/struct-composite-decl.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/struct-composite-decl.vert new file mode 100644 index 0000000..5b2c582 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/struct-composite-decl.vert @@ -0,0 +1,50 @@ +struct VOut +{ + float4 a; + float4 b; + float4 c; + float4 d; +}; + +static VOut vout; +static float4 a; +static float4 b; +static float4 c; +static float4 d; + +struct SPIRV_Cross_Input +{ + float4 a : TEXCOORD0; + float4 b : TEXCOORD1; + float4 c : TEXCOORD2; + float4 d : TEXCOORD3; +}; + +struct SPIRV_Cross_Output +{ + VOut vout : TEXCOORD0; +}; + +void emit_result(VOut v) +{ + vout = v; +} + +void vert_main() +{ + VOut _26 = { a, b, c, d }; + VOut param = _26; + emit_result(param); +} + +SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) +{ + a = stage_input.a; + b = stage_input.b; + c = stage_input.c; + d = stage_input.d; + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.vout = vout; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-hlsl/vert/texture_buffer.vert b/third_party/spirv-cross/reference/shaders-hlsl/vert/texture_buffer.vert new file mode 100644 index 0000000..1c92f6f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-hlsl/vert/texture_buffer.vert @@ -0,0 +1,21 @@ +Buffer uSamp : register(t4); +RWBuffer uSampo : register(u5); + +static float4 gl_Position; +struct SPIRV_Cross_Output +{ + float4 gl_Position : SV_Position; +}; + +void vert_main() +{ + gl_Position = uSamp.Load(10) + uSampo[100]; +} + +SPIRV_Cross_Output main() +{ + vert_main(); + SPIRV_Cross_Output stage_output; + stage_output.gl_Position = gl_Position; + return stage_output; +} diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp new file mode 100644 index 0000000..d24b966 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct T +{ + float a; +}; + +struct T_1 +{ + float b; +}; + +struct SSBO1 +{ + T_1 foo[1]; +}; + +struct T_2 +{ + float c; + char _m0_final_padding[12]; +}; + +struct SSBO2 +{ + T_2 bar[1]; +}; + +kernel void main0(device SSBO1& _7 [[buffer(0)]], device SSBO2& _10 [[buffer(1)]]) +{ + T v = T{ 40.0 }; + _7.foo[10].b = v.a; + _10.bar[30].c = v.a; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp new file mode 100644 index 0000000..c3f9f5e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + int s32; + uint u32; + short s16; + ushort u16; + float f32; +}; + +kernel void main0(device SSBO& _4 [[buffer(0)]]) +{ + int _29 = _4.s32; + uint _30 = _4.u32; + short _31 = _4.s16; + ushort _32 = _4.u16; + float _33 = _4.f32; + _4.s32 = int(_31); + _4.u32 = uint(_31); + _4.s32 = int(short(_32)); + _4.u32 = uint(short(_32)); + _4.u32 = uint(ushort(_31)); + _4.u32 = uint(_32); + _4.s16 = short(_29); + _4.u16 = ushort(_29); + _4.s16 = short(_30); + _4.u16 = ushort(_30); + _4.u16 = ushort(_29); + _4.u16 = ushort(_30); + _4.f32 = float(_31); + _4.f32 = float(short(_32)); + _4.f32 = float(ushort(_31)); + _4.f32 = float(_32); + _4.s16 = short(_33); + _4.u16 = ushort(short(_33)); + _4.u16 = ushort(_33); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/atomic-load-store.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/atomic-load-store.asm.comp new file mode 100644 index 0000000..1015d2a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/atomic-load-store.asm.comp @@ -0,0 +1,23 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + uint a; + uint b; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _5 [[buffer(0)]]) +{ + uint _20 = atomic_load_explicit((device atomic_uint*)&_5.b, memory_order_relaxed); + uint c = _20; + atomic_store_explicit((device atomic_uint*)&_5.a, c, memory_order_relaxed); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/atomic-result-temporary.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/atomic-result-temporary.asm.comp new file mode 100644 index 0000000..4624ef0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/atomic-result-temporary.asm.comp @@ -0,0 +1,23 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + uint count; + uint data[1]; +}; + +kernel void main0(device SSBO& _5 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _24 = atomic_fetch_add_explicit((device atomic_uint*)&_5.count, 1u, memory_order_relaxed); + if (_24 < 1024u) + { + _5.data[_24] = gl_GlobalInvocationID.x; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitcast-fp16-fp32.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitcast-fp16-fp32.asm.comp new file mode 100644 index 0000000..0d63f5f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitcast-fp16-fp32.asm.comp @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + half2 a; + float b; + float c; + half2 d; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _4 [[buffer(0)]]) +{ + _4.b = as_type(_4.a); + _4.d = as_type(_4.c); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitfield-signed-operations.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitfield-signed-operations.asm.comp new file mode 100644 index 0000000..8e198a9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitfield-signed-operations.asm.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + int4 ints; + uint4 uints; +}; + +kernel void main0(device SSBO& _3 [[buffer(0)]]) +{ + int4 _19 = _3.ints; + uint4 _20 = _3.uints; + _3.ints = popcount(_19); + _3.uints = uint4(popcount(_19)); + _3.ints = int4(popcount(_20)); + _3.uints = popcount(_20); + _3.ints = reverse_bits(_19); + _3.uints = reverse_bits(_20); + _3.ints = extract_bits(_19, uint(1), 11u); + _3.uints = uint4(extract_bits(int4(_20), 11u, uint(1))); + _3.ints = int4(extract_bits(uint4(_19), uint(1), 11u)); + _3.uints = extract_bits(_20, 11u, uint(1)); + _3.ints = insert_bits(_19, _19.wzyx, uint(1), 11u); + _3.uints = insert_bits(_20, _20.wzyx, 11u, uint(1)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitscan.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitscan.asm.comp new file mode 100644 index 0000000..5634d9d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/bitscan.asm.comp @@ -0,0 +1,53 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + uint4 u; + int4 i; +}; + +// Implementation of the GLSL findLSB() function +template +inline T spvFindLSB(T x) +{ + return select(ctz(x), T(-1), x == T(0)); +} + +// Implementation of the signed GLSL findMSB() function +template +inline T spvFindSMSB(T x) +{ + T v = select(x, T(-1) - x, x < T(0)); + return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); +} + +// Implementation of the unsigned GLSL findMSB() function +template +inline T spvFindUMSB(T x) +{ + return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0)); +} + +kernel void main0(device SSBO& _4 [[buffer(0)]]) +{ + uint4 _19 = _4.u; + int4 _20 = _4.i; + _4.u = spvFindLSB(_19); + _4.i = int4(spvFindLSB(_19)); + _4.u = uint4(spvFindLSB(_20)); + _4.i = spvFindLSB(_20); + _4.u = spvFindUMSB(_19); + _4.i = int4(spvFindUMSB(_19)); + _4.u = spvFindUMSB(uint4(_20)); + _4.i = int4(spvFindUMSB(uint4(_20))); + _4.u = uint4(spvFindSMSB(int4(_19))); + _4.i = spvFindSMSB(int4(_19)); + _4.u = uint4(spvFindSMSB(_20)); + _4.i = spvFindSMSB(_20); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/composite-construct-buffer-struct.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/composite-construct-buffer-struct.asm.comp new file mode 100644 index 0000000..2fe0981 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/composite-construct-buffer-struct.asm.comp @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct Block +{ + uint2 _m0[2]; + uint2 _m1[2]; +}; + +struct SSBO +{ + Block _m0[3]; +}; + +kernel void main0(device SSBO& ssbo [[buffer(0)]]) +{ + threadgroup uint2 _18[2]; + ssbo._m0[0u] = Block{ { ssbo._m0[0u]._m1[0], ssbo._m0[0u]._m1[1] }, { ssbo._m0[0u]._m1[0], ssbo._m0[0u]._m1[1] } }; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/constant-composite-undef.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/constant-composite-undef.asm.comp new file mode 100644 index 0000000..37ed021 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/constant-composite-undef.asm.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct Block +{ + float4 f; +}; + +constant float _15 = {}; + +kernel void main0(device Block& block [[buffer(0)]]) +{ + block.f = float4(0.100000001490116119384765625, 0.20000000298023223876953125, 0.300000011920928955078125, 0.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/copy-logical-2.spv14.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/copy-logical-2.spv14.asm.comp new file mode 100644 index 0000000..09a31d6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/copy-logical-2.spv14.asm.comp @@ -0,0 +1,60 @@ +#include +#include + +using namespace metal; + +struct _11 +{ + float2x2 _m0; +}; + +struct _12 +{ + float2x4 _m0; +}; + +struct B2 +{ + float4 elem2; +}; + +struct C +{ + float4 c; + B2 b2; + B2 b2_array[4]; + _12 _m3; +}; + +struct B1 +{ + float4 elem1; +}; + +struct A +{ + float4 a; + B1 b1; + B1 b1_array[4]; + _11 _m3; +}; + +struct _8 +{ + A a_block; + C c_block; +}; + +kernel void main0(device _8& _3 [[buffer(0)]]) +{ + A _31; + _31.a = _3.c_block.c; + _31.b1.elem1 = _3.c_block.b2.elem2; + _31.b1_array[0].elem1 = _3.c_block.b2_array[0].elem2; + _31.b1_array[1].elem1 = _3.c_block.b2_array[1].elem2; + _31.b1_array[2].elem1 = _3.c_block.b2_array[2].elem2; + _31.b1_array[3].elem1 = _3.c_block.b2_array[3].elem2; + _31._m3._m0 = transpose(float2x2(_3.c_block._m3._m0[0].xy, _3.c_block._m3._m0[1].xy)); + _3.a_block = _31; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/copy-logical.spv14.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/copy-logical.spv14.asm.comp new file mode 100644 index 0000000..2225981 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/copy-logical.spv14.asm.comp @@ -0,0 +1,47 @@ +#include +#include + +using namespace metal; + +struct B2 +{ + float4 elem2; +}; + +struct C +{ + float4 c; + B2 b2; + B2 b2_array[4]; +}; + +struct B1 +{ + float4 elem1; +}; + +struct A +{ + float4 a; + B1 b1; + B1 b1_array[4]; +}; + +struct _8 +{ + A a_block; + C c_block; +}; + +kernel void main0(device _8& _3 [[buffer(0)]]) +{ + A _27; + _27.a = _3.c_block.c; + _27.b1.elem1 = _3.c_block.b2.elem2; + _27.b1_array[0].elem1 = _3.c_block.b2_array[0].elem2; + _27.b1_array[1].elem1 = _3.c_block.b2_array[1].elem2; + _27.b1_array[2].elem1 = _3.c_block.b2_array[2].elem2; + _27.b1_array[3].elem1 = _3.c_block.b2_array[3].elem2; + _3.a_block = _27; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-array-load-temporary.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-array-load-temporary.asm.comp new file mode 100644 index 0000000..1cc6d3f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-array-load-temporary.asm.comp @@ -0,0 +1,174 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Block +{ + uint2 _m0[2]; + uint2 _m1[2]; +}; + +struct SSBO +{ + Block _m0[3]; +}; + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +kernel void main0(device SSBO& ssbo [[buffer(0)]]) +{ + threadgroup uint2 _18[2]; + spvUnsafeArray _27; + spvArrayCopyFromDeviceToStack1(_27.elements, ssbo._m0[0u]._m1); + spvArrayCopyFromStackToDevice1(ssbo._m0[0u]._m0, _27.elements); + spvArrayCopyFromStackToDevice1(ssbo._m0[0u]._m0, _27.elements); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-array-load-temporary.force-native-array.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-array-load-temporary.force-native-array.asm.comp new file mode 100644 index 0000000..080160c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-array-load-temporary.force-native-array.asm.comp @@ -0,0 +1,135 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Block +{ + uint2 _m0[2]; + uint2 _m1[2]; +}; + +struct SSBO +{ + Block _m0[3]; +}; + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +kernel void main0(device SSBO& ssbo [[buffer(0)]]) +{ + threadgroup uint2 _18[2]; + uint2 _27[2]; + spvArrayCopyFromDeviceToStack1(_27, ssbo._m0[0u]._m1); + spvArrayCopyFromStackToDevice1(ssbo._m0[0u]._m0, _27); + spvArrayCopyFromStackToDevice1(ssbo._m0[0u]._m0, _27); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.asm.comp new file mode 100644 index 0000000..0a64e2e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.asm.comp @@ -0,0 +1,179 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Block +{ + uint2 _m0[2]; + uint2 _m1[2]; +}; + +struct SSBO +{ + Block _m0[3]; +}; + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +kernel void main0(device SSBO& ssbo [[buffer(0)]], constant SSBO& ubo [[buffer(1)]]) +{ + threadgroup uint2 _18[2]; + spvArrayCopyFromDeviceToDevice1(ssbo._m0[0u]._m0, ssbo._m0[0u]._m1); + spvArrayCopyFromConstantToDevice1(ssbo._m0[0u]._m0, ubo._m0[0u]._m1); + spvUnsafeArray _24; + spvArrayCopyFromStackToDevice1(ssbo._m0[0u]._m0, _24.elements); + spvArrayCopyFromThreadGroupToDevice1(ssbo._m0[0u]._m0, _18); + spvArrayCopyFromDeviceToThreadGroup1(_18, ssbo._m0[0u]._m1); + spvArrayCopyFromDeviceToStack1(_24.elements, ssbo._m0[0u]._m1); + spvArrayCopyFromConstantToThreadGroup1(_18, ubo._m0[0u]._m1); + spvArrayCopyFromConstantToStack1(_24.elements, ubo._m0[0u]._m1); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.force-native-array.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.force-native-array.asm.comp new file mode 100644 index 0000000..4156a16 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.force-native-array.asm.comp @@ -0,0 +1,140 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Block +{ + uint2 _m0[2]; + uint2 _m1[2]; +}; + +struct SSBO +{ + Block _m0[3]; +}; + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +kernel void main0(device SSBO& ssbo [[buffer(0)]], constant SSBO& ubo [[buffer(1)]]) +{ + threadgroup uint2 _18[2]; + spvArrayCopyFromDeviceToDevice1(ssbo._m0[0u]._m0, ssbo._m0[0u]._m1); + spvArrayCopyFromConstantToDevice1(ssbo._m0[0u]._m0, ubo._m0[0u]._m1); + uint2 _24[2]; + spvArrayCopyFromStackToDevice1(ssbo._m0[0u]._m0, _24); + spvArrayCopyFromThreadGroupToDevice1(ssbo._m0[0u]._m0, _18); + spvArrayCopyFromDeviceToThreadGroup1(_18, ssbo._m0[0u]._m1); + spvArrayCopyFromDeviceToStack1(_24, ssbo._m0[0u]._m1); + spvArrayCopyFromConstantToThreadGroup1(_18, ubo._m0[0u]._m1); + spvArrayCopyFromConstantToStack1(_24, ubo._m0[0u]._m1); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/glsl-signed-operations.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/glsl-signed-operations.asm.comp new file mode 100644 index 0000000..7de0020 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/glsl-signed-operations.asm.comp @@ -0,0 +1,73 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + int4 ints; + uint4 uints; +}; + +// Implementation of the signed GLSL findMSB() function +template +inline T spvFindSMSB(T x) +{ + T v = select(x, T(-1) - x, x < T(0)); + return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); +} + +// Implementation of the unsigned GLSL findMSB() function +template +inline T spvFindUMSB(T x) +{ + return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0)); +} + +// Implementation of the GLSL sign() function for integer types +template::value>::type> +inline T sign(T x) +{ + return select(select(select(x, T(0), x == T(0)), T(1), x > T(0)), T(-1), x < T(0)); +} + +kernel void main0(device SSBO& _4 [[buffer(0)]]) +{ + int4 _19 = _4.ints; + uint4 _20 = _4.uints; + _4.ints = abs(_19); + _4.uints = uint4(abs(_19)); + _4.ints = abs(int4(_20)); + _4.uints = uint4(abs(int4(_20))); + _4.ints = sign(_19); + _4.uints = uint4(sign(_19)); + _4.ints = sign(int4(_20)); + _4.uints = uint4(sign(int4(_20))); + _4.ints = spvFindSMSB(int4(_20)); + _4.uints = uint4(spvFindSMSB(int4(_20))); + _4.ints = int4(spvFindUMSB(uint4(_19))); + _4.uints = spvFindUMSB(uint4(_19)); + _4.ints = min(_19, _19); + _4.uints = uint4(min(_19, int4(_20))); + _4.ints = min(int4(_20), int4(_20)); + _4.uints = uint4(min(int4(_20), _19)); + _4.ints = int4(min(uint4(_19), _20)); + _4.uints = min(uint4(_19), _20); + _4.ints = int4(min(_20, uint4(_19))); + _4.uints = min(_20, uint4(_19)); + _4.ints = max(_19, _19); + _4.uints = uint4(max(_19, _19)); + _4.ints = max(int4(_20), _19); + _4.uints = uint4(max(int4(_20), _19)); + _4.ints = int4(max(uint4(_19), _20)); + _4.uints = max(uint4(_19), uint4(_19)); + _4.ints = int4(max(_20, uint4(_19))); + _4.uints = max(_20, uint4(_19)); + _4.ints = clamp(int4(_20), int4(_20), int4(_20)); + _4.uints = uint4(clamp(int4(_20), int4(_20), int4(_20))); + _4.ints = int4(clamp(uint4(_19), uint4(_19), uint4(_19))); + _4.uints = clamp(uint4(_19), uint4(_19), uint4(_19)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp new file mode 100644 index 0000000..0063fac --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct _8 +{ + float _m0; + float _m1; +}; + +struct _15 +{ + float _m0; + int _m1; +}; + +struct _3 +{ + float _m0; + int _m1; +}; + +kernel void main0(device _3& _4 [[buffer(0)]]) +{ + _8 _23; + _23._m0 = modf(20.0, _23._m1); + _15 _24; + _24._m0 = frexp(40.0, _24._m1); + _4._m0 = _23._m0; + _4._m0 = _23._m1; + _4._m0 = _24._m0; + _4._m1 = _24._m1; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp new file mode 100644 index 0000000..5b1ed8a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct _6 +{ + float _m0[1]; +}; + +constant uint _3_tmp [[function_constant(0)]]; +constant uint _3 = is_function_constant_defined(_3_tmp) ? _3_tmp : 1u; +constant uint _4_tmp [[function_constant(2)]]; +constant uint _4 = is_function_constant_defined(_4_tmp) ? _4_tmp : 3u; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(_3, 2u, _4); + +kernel void main0(device _6& _8 [[buffer(0)]], device _6& _9 [[buffer(1)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +{ + uint3 _23 = gl_WorkGroupSize; + _8._m0[gl_WorkGroupID.x] = _9._m0[gl_WorkGroupID.x] + _8._m0[gl_WorkGroupID.x]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/storage-buffer-pointer-argument.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/storage-buffer-pointer-argument.asm.comp new file mode 100644 index 0000000..6e97685 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/storage-buffer-pointer-argument.asm.comp @@ -0,0 +1,28 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float a; +}; + +struct SSBORead +{ + float b; +}; + +static inline __attribute__((always_inline)) +void copy_out(device float& A, device const float& B) +{ + A = B; +} + +kernel void main0(device SSBO& _7 [[buffer(0)]], const device SSBORead& _9 [[buffer(1)]]) +{ + copy_out(_7.a, _9.b); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp new file mode 100644 index 0000000..7c9718d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp @@ -0,0 +1,74 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct foo +{ + int a[128]; + uint b; + float2 c; +}; + +struct bar +{ + int d; +}; + +struct baz +{ + int e[128]; +}; + +static inline __attribute__((always_inline)) +device int* select_buffer(device foo& buf, device baz& buf2, constant bar& cb) +{ + return (cb.d != 0) ? &buf.a[0u] : &buf2.e[0u]; +} + +static inline __attribute__((always_inline)) +device int* select_buffer_null(device foo& buf, constant bar& cb) +{ + return (cb.d != 0) ? &buf.a[0u] : nullptr; +} + +static inline __attribute__((always_inline)) +threadgroup int* select_tgsm(constant bar& cb, threadgroup int (&tgsm)[128]) +{ + return (cb.d != 0) ? &tgsm[0u] : nullptr; +} + +kernel void main0(device foo& buf [[buffer(0)]], constant bar& cb [[buffer(1)]], device baz& buf2 [[buffer(2)]]) +{ + threadgroup int tgsm[128]; + device int* sbuf = select_buffer(buf, buf2, cb); + device int* sbuf2 = select_buffer_null(buf, cb); + threadgroup int* stgsm = select_tgsm(cb, tgsm); + threadgroup int* cur = stgsm; + device int* _73; + _73 = &buf.a[0u]; + threadgroup int* _76; + int _77; + for (;;) + { + _76 = cur; + _77 = *_73; + if (_77 != 0) + { + int _81 = *_76; + int _82 = _77 + _81; + *_73 = _82; + *_76 = _82; + cur = &_76[1u]; + _73 = &_73[1u]; + continue; + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/component-insert-packed-expression.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/component-insert-packed-expression.asm.frag new file mode 100644 index 0000000..2da91da --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/component-insert-packed-expression.asm.frag @@ -0,0 +1,34 @@ +#include +#include + +using namespace metal; + +struct type_Globals +{ + float4 _BorderWidths[4]; +}; + +struct main0_out +{ + float4 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Globals& _Globals [[buffer(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float2 _31 = float2(_Globals._BorderWidths[0].x, _Globals._BorderWidths[1].x); + float2 _39; + if (gl_FragCoord.x > 0.0) + { + float2 _38 = _31; + _38.x = _Globals._BorderWidths[2].x; + _39 = _38; + } + else + { + _39 = _31; + } + out.out_var_SV_Target = float4(_39, 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/empty-struct-in-struct.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/empty-struct-in-struct.asm.frag new file mode 100644 index 0000000..fdf4a92 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/empty-struct-in-struct.asm.frag @@ -0,0 +1,35 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct EmptyStructTest +{ +}; +struct EmptyStruct2Test +{ + EmptyStructTest _m0; +}; + +static inline __attribute__((always_inline)) +float GetValue(thread const EmptyStruct2Test& self) +{ + return 0.0; +} + +static inline __attribute__((always_inline)) +float GetValue_1(EmptyStruct2Test self) +{ + return 0.0; +} + +fragment void main0() +{ + EmptyStruct2Test emptyStruct; + float value = GetValue(emptyStruct); + value = GetValue_1(EmptyStruct2Test{ EmptyStructTest{ } }); + value = GetValue_1(EmptyStruct2Test{ { } }); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag new file mode 100644 index 0000000..887200f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint3 in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d Tex [[texture(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target0 = Tex.read(uint2(in.in_var_TEXCOORD0.xy), in.in_var_TEXCOORD0.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-gather.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-gather.asm.frag new file mode 100644 index 0000000..4725342 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-gather.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d g_texture [[texture(0)]], sampler g_sampler [[sampler(0)]], sampler g_comp [[sampler(1)]]) +{ + main0_out out = {}; + out.out_var_SV_Target0 = g_texture.gather(g_sampler, in.in_var_TEXCOORD0, int2(0), component::x) * g_texture.gather(g_sampler, in.in_var_TEXCOORD0, int2(0), component::y); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-type-normal-comparison-usage.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-type-normal-comparison-usage.asm.frag new file mode 100644 index 0000000..2e43ab0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/image-type-normal-comparison-usage.asm.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d ShadowMap [[texture(0)]], sampler SampleNormal [[sampler(0)]], sampler SampleShadow [[sampler(1)]]) +{ + main0_out out = {}; + float _41; + if (in.in_var_TEXCOORD0.x > 0.5) + { + _41 = float(float4(ShadowMap.sample(SampleNormal, in.in_var_TEXCOORD0)).x <= 0.5); + } + else + { + _41 = ShadowMap.sample_compare(SampleShadow, in.in_var_TEXCOORD0, 0.5, level(0.0)); + } + out.out_var_SV_Target0 = float4(_41, _41, _41, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/input-attachment-unused-frag-coord.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/input-attachment-unused-frag-coord.asm.frag new file mode 100644 index 0000000..93bbaec --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/input-attachment-unused-frag-coord.asm.frag @@ -0,0 +1,25 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 load_subpasses(thread const texture2d uInput, thread float4& gl_FragCoord) +{ + return uInput.read(uint2(gl_FragCoord.xy)); +} + +fragment main0_out main0(texture2d uSubpass0 [[texture(0)]], texture2d uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy)) + load_subpasses(uSubpass1, gl_FragCoord); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag new file mode 100644 index 0000000..910c8fa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ + float3 col; + int2 _18; + float _23; + float _21 = modf(0.1500000059604644775390625, _23); + col.x = _23; + int _24; + float _22 = frexp(0.1500000059604644775390625, _24); + _18.y = _24; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/phi.zero-initialize.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/phi.zero-initialize.asm.frag new file mode 100644 index 0000000..3218858 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/phi.zero-initialize.asm.frag @@ -0,0 +1,41 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + int a; +}; + +constant int uninit_int = {}; +constant int4 uninit_vector = {}; +constant float4x4 uninit_matrix = {}; +constant Foo uninit_foo = {}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + int _39 = {}; + if (in.vColor.x > 10.0) + { + _39 = 10; + } + else + { + _39 = 20; + } + out.FragColor = in.vColor; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-callstack.msl2.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-callstack.msl2.asm.frag new file mode 100644 index 0000000..8ceb9f4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-callstack.msl2.asm.frag @@ -0,0 +1,37 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO1 +{ + uint values1[1]; +}; + +struct SSBO0 +{ + uint values0[1]; +}; + +static inline __attribute__((always_inline)) +void callee2(thread float4& gl_FragCoord, device SSBO1& v_7) +{ + int _31 = int(gl_FragCoord.x); + v_7.values1[_31]++; +} + +static inline __attribute__((always_inline)) +void callee(thread float4& gl_FragCoord, device SSBO1& v_7, device SSBO0& v_9) +{ + int _39 = int(gl_FragCoord.x); + v_9.values0[_39]++; + callee2(gl_FragCoord, v_7); +} + +fragment void main0(device SSBO1& v_7 [[buffer(0), raster_order_group(0)]], device SSBO0& v_9 [[buffer(1)]], float4 gl_FragCoord [[position]]) +{ + callee(gl_FragCoord, v_7, v_9); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-control-flow.msl2.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-control-flow.msl2.asm.frag new file mode 100644 index 0000000..a382316 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-control-flow.msl2.asm.frag @@ -0,0 +1,52 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO1 +{ + uint values1[1]; +}; + +struct _12 +{ + uint _m0[1]; +}; + +struct SSBO0 +{ + uint values0[1]; +}; + +static inline __attribute__((always_inline)) +void callee2(thread float4& gl_FragCoord, device SSBO1& v_7) +{ + int _44 = int(gl_FragCoord.x); + v_7.values1[_44]++; +} + +static inline __attribute__((always_inline)) +void callee(thread float4& gl_FragCoord, device SSBO1& v_7, device SSBO0& v_9) +{ + int _52 = int(gl_FragCoord.x); + v_9.values0[_52]++; + callee2(gl_FragCoord, v_7); + if (true) + { + } +} + +static inline __attribute__((always_inline)) +void _35(thread float4& gl_FragCoord, device _12& v_13) +{ + v_13._m0[int(gl_FragCoord.x)] = 4u; +} + +fragment void main0(device SSBO1& v_7 [[buffer(0), raster_order_group(0)]], device _12& v_13 [[buffer(1)]], device SSBO0& v_9 [[buffer(2), raster_order_group(0)]], float4 gl_FragCoord [[position]]) +{ + callee(gl_FragCoord, v_7, v_9); + _35(gl_FragCoord, v_13); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-split-functions.msl2.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-split-functions.msl2.asm.frag new file mode 100644 index 0000000..beb2124 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/pixel-interlock-split-functions.msl2.asm.frag @@ -0,0 +1,49 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO1 +{ + uint values1[1]; +}; + +struct SSBO0 +{ + uint values0[1]; +}; + +static inline __attribute__((always_inline)) +void callee2(thread float4& gl_FragCoord, device SSBO1& v_7) +{ + int _37 = int(gl_FragCoord.x); + v_7.values1[_37]++; +} + +static inline __attribute__((always_inline)) +void callee(thread float4& gl_FragCoord, device SSBO1& v_7, device SSBO0& v_9) +{ + int _45 = int(gl_FragCoord.x); + v_9.values0[_45]++; + callee2(gl_FragCoord, v_7); +} + +static inline __attribute__((always_inline)) +void _29() +{ +} + +static inline __attribute__((always_inline)) +void _31() +{ +} + +fragment void main0(device SSBO1& v_7 [[buffer(0), raster_order_group(0)]], device SSBO0& v_9 [[buffer(1), raster_order_group(0)]], float4 gl_FragCoord [[position]]) +{ + callee(gl_FragCoord, v_7, v_9); + _29(); + _31(); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/private-initializer-direct-store.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/private-initializer-direct-store.asm.frag new file mode 100644 index 0000000..5bc3c47 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/private-initializer-direct-store.asm.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + float b = 10.0; + b = 20.0; + out.FragColor = b + b; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/subgroup-arithmetic-cast.msl21.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/subgroup-arithmetic-cast.msl21.asm.frag new file mode 100644 index 0000000..2f5cd66 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/subgroup-arithmetic-cast.msl21.asm.frag @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + uint FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + uint _17 = uint(in.index); + out.FragColor = uint(simd_min(in.index)); + out.FragColor = uint(simd_max(int(_17))); + out.FragColor = simd_min(uint(in.index)); + out.FragColor = simd_max(_17); + out.FragColor = uint(quad_min(in.index)); + out.FragColor = uint(quad_max(int(_17))); + out.FragColor = quad_min(uint(in.index)); + out.FragColor = quad_max(_17); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag new file mode 100644 index 0000000..e179b7e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag @@ -0,0 +1,75 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int vIndex [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + int i = 0; + main0_out out = {}; + int j; + int _30; + int _31; + if (in.vIndex != 0 && in.vIndex != 1 && in.vIndex != 11 && in.vIndex != 2 && in.vIndex != 3 && in.vIndex != 4 && in.vIndex != 5) + { + _30 = 2; + } + if (in.vIndex == 1 || in.vIndex == 11) + { + _31 = 1; + } + switch (in.vIndex) + { + case 0: + { + _30 = 3; + } + default: + { + j = _30; + _31 = 0; + } + case 1: + case 11: + { + j = _31; + } + case 2: + { + break; + } + case 3: + { + if (in.vIndex > 3) + { + i = 0; + break; + } + else + { + break; + } + } + case 4: + { + } + case 5: + { + i = 0; + break; + } + } + out.FragColor = float4(float(i)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/texture-access.swizzle.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/texture-access.swizzle.asm.frag new file mode 100644 index 0000000..9d71a91 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/texture-access.swizzle.asm.frag @@ -0,0 +1,190 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +// Wrapper function that swizzles texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherSwizzle(const thread Tex& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c) +{ + if (sw) + { + switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF)) + { + case spvSwizzle::none: + break; + case spvSwizzle::zero: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + case spvSwizzle::red: + return t.gather(s, spvForward(params)..., component::x); + case spvSwizzle::green: + return t.gather(s, spvForward(params)..., component::y); + case spvSwizzle::blue: + return t.gather(s, spvForward(params)..., component::z); + case spvSwizzle::alpha: + return t.gather(s, spvForward(params)..., component::w); + } + } + switch (c) + { + case component::x: + return t.gather(s, spvForward(params)..., component::x); + case component::y: + return t.gather(s, spvForward(params)..., component::y); + case component::z: + return t.gather(s, spvForward(params)..., component::z); + case component::w: + return t.gather(s, spvForward(params)..., component::w); + } +} + +// Wrapper function that swizzles depth texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherCompareSwizzle(const thread Tex& t, sampler s, uint sw, Ts... params) +{ + if (sw) + { + switch (spvSwizzle(sw & 0xFF)) + { + case spvSwizzle::none: + case spvSwizzle::red: + break; + case spvSwizzle::zero: + case spvSwizzle::green: + case spvSwizzle::blue: + case spvSwizzle::alpha: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + } + } + return t.gather_compare(s, spvForward(params)...); +} + +fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d tex1d [[texture(0)]], texture2d tex2d [[texture(1)]], texture3d tex3d [[texture(2)]], texturecube texCube [[texture(3)]], texture2d_array tex2dArray [[texture(4)]], texturecube_array texCubeArray [[texture(5)]], depth2d depth2d [[texture(6)]], depthcube depthCube [[texture(7)]], depth2d_array depth2dArray [[texture(8)]], depthcube_array depthCubeArray [[texture(9)]], texture2d texBuffer [[texture(10)]], sampler tex1dSamp [[sampler(0)]], sampler tex2dSamp [[sampler(1)]], sampler tex3dSamp [[sampler(2)]], sampler texCubeSamp [[sampler(3)]], sampler tex2dArraySamp [[sampler(4)]], sampler texCubeArraySamp [[sampler(5)]], sampler depth2dSamp [[sampler(6)]], sampler depthCubeSamp [[sampler(7)]], sampler depth2dArraySamp [[sampler(8)]], sampler depthCubeArraySamp [[sampler(9)]]) +{ + constant uint& tex1dSwzl = spvSwizzleConstants[0]; + constant uint& tex2dSwzl = spvSwizzleConstants[1]; + constant uint& tex3dSwzl = spvSwizzleConstants[2]; + constant uint& texCubeSwzl = spvSwizzleConstants[3]; + constant uint& tex2dArraySwzl = spvSwizzleConstants[4]; + constant uint& texCubeArraySwzl = spvSwizzleConstants[5]; + constant uint& depth2dSwzl = spvSwizzleConstants[6]; + constant uint& depthCubeSwzl = spvSwizzleConstants[7]; + constant uint& depth2dArraySwzl = spvSwizzleConstants[8]; + constant uint& depthCubeArraySwzl = spvSwizzleConstants[9]; + float4 c = spvTextureSwizzle(tex1d.sample(tex1dSamp, 0.0), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSamp, float2(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSamp, float3(0.0)), tex3dSwzl); + c = spvTextureSwizzle(texCube.sample(texCubeSamp, float3(0.0)), texCubeSwzl); + c = spvTextureSwizzle(tex2dArray.sample(tex2dArraySamp, float3(0.0).xy, uint(round(float3(0.0).z))), tex2dArraySwzl); + c = spvTextureSwizzle(texCubeArray.sample(texCubeArraySamp, float4(0.0).xyz, uint(round(float4(0.0).w))), texCubeArraySwzl); + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSamp, float3(0.0, 0.0, 1.0).xy, float3(0.0, 0.0, 1.0).z), depth2dSwzl); + c.x = spvTextureSwizzle(depthCube.sample_compare(depthCubeSamp, float4(0.0, 0.0, 0.0, 1.0).xyz, float4(0.0, 0.0, 0.0, 1.0).w), depthCubeSwzl); + c.x = spvTextureSwizzle(depth2dArray.sample_compare(depth2dArraySamp, float4(0.0, 0.0, 0.0, 1.0).xy, uint(round(float4(0.0, 0.0, 0.0, 1.0).z)), float4(0.0, 0.0, 0.0, 1.0).w), depth2dArraySwzl); + c.x = spvTextureSwizzle(depthCubeArray.sample_compare(depthCubeArraySamp, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0), depthCubeArraySwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSamp, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSamp, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSamp, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w), tex3dSwzl); + float4 _152 = float4(0.0, 0.0, 1.0, 1.0); + _152.z = float4(0.0, 0.0, 1.0, 1.0).w; + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSamp, _152.xy / _152.z, float4(0.0, 0.0, 1.0, 1.0).z / _152.z), depth2dSwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSamp, 0.0), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSamp, float2(0.0), level(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSamp, float3(0.0), level(0.0)), tex3dSwzl); + c = spvTextureSwizzle(texCube.sample(texCubeSamp, float3(0.0), level(0.0)), texCubeSwzl); + c = spvTextureSwizzle(tex2dArray.sample(tex2dArraySamp, float3(0.0).xy, uint(round(float3(0.0).z)), level(0.0)), tex2dArraySwzl); + c = spvTextureSwizzle(texCubeArray.sample(texCubeArraySamp, float4(0.0).xyz, uint(round(float4(0.0).w)), level(0.0)), texCubeArraySwzl); + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSamp, float3(0.0, 0.0, 1.0).xy, float3(0.0, 0.0, 1.0).z, level(0.0)), depth2dSwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSamp, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSamp, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z, level(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSamp, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w, level(0.0)), tex3dSwzl); + float4 _202 = float4(0.0, 0.0, 1.0, 1.0); + _202.z = float4(0.0, 0.0, 1.0, 1.0).w; + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSamp, _202.xy / _202.z, float4(0.0, 0.0, 1.0, 1.0).z / _202.z, level(0.0)), depth2dSwzl); + c = spvTextureSwizzle(tex1d.read(uint(0)), tex1dSwzl); + c = spvTextureSwizzle(tex2d.read(uint2(int2(0)), 0), tex2dSwzl); + c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl); + c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl); + c = texBuffer.read(spvTexelBufferCoord(0)); + c = spvGatherSwizzle(tex2d, tex2dSamp, tex2dSwzl, component::x, float2(0.0), int2(0)); + c = spvGatherSwizzle(texCube, texCubeSamp, texCubeSwzl, component::y, float3(0.0)); + c = spvGatherSwizzle(tex2dArray, tex2dArraySamp, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0)); + c = spvGatherSwizzle(texCubeArray, texCubeArraySamp, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w))); + c = spvGatherCompareSwizzle(depth2d, depth2dSamp, depth2dSwzl, float2(0.0), 1.0); + c = spvGatherCompareSwizzle(depthCube, depthCubeSamp, depthCubeSwzl, float3(0.0), 1.0); + c = spvGatherCompareSwizzle(depth2dArray, depth2dArraySamp, depth2dArraySwzl, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0); + c = spvGatherCompareSwizzle(depthCubeArray, depthCubeArraySamp, depthCubeArraySwzl, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag new file mode 100644 index 0000000..8935cd6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +constant float4 undef = {}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vFloat [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = float4(undef.x, in.vFloat.y, 0.0, in.vFloat.w) + float4(in.vFloat.z, in.vFloat.y, 0.0, in.vFloat.w); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/composite-extract-row-major.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/composite-extract-row-major.asm.comp new file mode 100644 index 0000000..d2c368b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/composite-extract-row-major.asm.comp @@ -0,0 +1,16 @@ +#include +#include + +using namespace metal; + +struct SSBORow +{ + float v; + float4x4 row_major0; +}; + +kernel void main0(device SSBORow& _4 [[buffer(0)]]) +{ + _4.v = _4.row_major0[2][1]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-2.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-2.asm.comp new file mode 100644 index 0000000..0ae12f0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-2.asm.comp @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct SSBOScalar +{ + float3 a; + float3x3 b; + float3x3 c; +}; + +kernel void main0(device SSBOScalar& _4 [[buffer(0)]]) +{ + float3x3 _20 = transpose(_4.b); + _4.b = _4.c; + _4.a = _20 * _4.a; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-3.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-3.asm.comp new file mode 100644 index 0000000..86bdd45 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-3.asm.comp @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct SSBOScalar +{ + packed_float3 a; + packed_float3 b; + packed_float3 c; +}; + +kernel void main0(device SSBOScalar& _4 [[buffer(0)]]) +{ + float3 _17 = float3(_4.b); + float3 _19 = float3(_4.c); + _4.c = _17; + _4.a = _17 * _19; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-4.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-4.asm.comp new file mode 100644 index 0000000..6694204 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-4.asm.comp @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct SSBOScalar +{ + float4 a[16]; + float4 b[16]; + float4 c[16]; +}; + +kernel void main0(device SSBOScalar& _4 [[buffer(0)]]) +{ + float2 _27 = _4.b[10].xy; + float _29 = _4.c[10].x; + (device float2&)_4.b[10] = float2(10.0, 11.0); + (device float2&)_4.a[10] = _27 * _29; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-5.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-5.asm.comp new file mode 100644 index 0000000..23d25b8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-5.asm.comp @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct SSBOScalar +{ + float2 a; + packed_float3 b; + packed_float3 c; +}; + +kernel void main0(device SSBOScalar& _4 [[buffer(0)]]) +{ + float3 _21 = float3(_4.b); + float3 _24 = float3(_4.c); + _4.b = float3(1.0); + _4.a = _21.xy * _24.yz; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding.asm.comp new file mode 100644 index 0000000..c21fcc7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding.asm.comp @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +typedef packed_float3 packed_rm_float3x3[3]; + +struct SSBOScalar +{ + packed_float3 a; + packed_rm_float3x3 b; + packed_rm_float3x3 c; +}; + +kernel void main0(device SSBOScalar& _4 [[buffer(0)]]) +{ + float3x3 _20 = transpose(float3x3(float3(_4.b[0]), float3(_4.b[1]), float3(_4.b[2]))); + _4.b[0] = float3x3(float3(_4.c[0]), float3(_4.c[1]), float3(_4.c[2]))[0]; + _4.b[1] = float3x3(float3(_4.c[0]), float3(_4.c[1]), float3(_4.c[2]))[1]; + _4.b[2] = float3x3(float3(_4.c[0]), float3(_4.c[1]), float3(_4.c[2]))[2]; + _4.a = _20 * float3(_4.a); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/packed-vector-extract-insert.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/packed-vector-extract-insert.asm.comp new file mode 100644 index 0000000..b858561 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/packed-vector-extract-insert.asm.comp @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct SSBOScalar +{ + float2 a; + packed_float3 b; + packed_float3 c; + float3 d; +}; + +kernel void main0(device SSBOScalar& _4 [[buffer(0)]]) +{ + float3 _24 = _4.b; + _24.z = 2.0; + _4.a = float2(_4.b[0], _4.b[1]) * _4.b[2]; + _4.b = _24; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/row-major-split-access-chain.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/row-major-split-access-chain.asm.comp new file mode 100644 index 0000000..f12092c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/row-major-split-access-chain.asm.comp @@ -0,0 +1,16 @@ +#include +#include + +using namespace metal; + +struct SSBORow +{ + float v; + float4x4 row_major0; +}; + +kernel void main0(device SSBORow& _4 [[buffer(0)]]) +{ + _4.v = ((device float*)&_4.row_major0[2u])[1]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float2.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float2.asm.frag new file mode 100644 index 0000000..f26e35c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float2.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float4 a[1]; + char _m1_pad[8]; + float b; +}; + +struct main0_out +{ + float2 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (Foo.a[0].xy + Foo.a[1].xy) + float2(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float3-one-element.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float3-one-element.asm.frag new file mode 100644 index 0000000..6f85465 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float3-one-element.asm.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + packed_float3 a[1]; + float b; +}; + +struct main0_out +{ + float3 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = float3(Foo.a[0]) + float3(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float3.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float3.asm.frag new file mode 100644 index 0000000..565ee64 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-array-float3.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float4 a[1]; + char _m1_pad[12]; + float b; +}; + +struct main0_out +{ + float3 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (Foo.a[0].xyz + Foo.a[1].xyz) + float3(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x2-col-major.invalid.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x2-col-major.invalid.asm.frag new file mode 100644 index 0000000..8440b2f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x2-col-major.invalid.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float4 a[1]; + char _m1_pad[8]; + float b; +}; + +struct main0_out +{ + float2 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (Foo.a[0u].xy + Foo.a[1u].xy) + float2(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x2-row-major.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x2-row-major.asm.frag new file mode 100644 index 0000000..9b34771 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x2-row-major.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float4 a[1]; + char _m1_pad[8]; + float b; +}; + +struct main0_out +{ + float2 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (float2(Foo.a[0][0u], Foo.a[1][0u]) + float2(Foo.a[0][1u], Foo.a[1][1u])) + float2(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x3-col-major.invalid.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x3-col-major.invalid.asm.frag new file mode 100644 index 0000000..cd40af1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x3-col-major.invalid.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float4 a[1]; + char _m1_pad[12]; + float b; +}; + +struct main0_out +{ + float3 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (Foo.a[0u].xyz + Foo.a[1u].xyz) + float3(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x3-row-major.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x3-row-major.asm.frag new file mode 100644 index 0000000..86dfd60 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float2x3-row-major.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float2x4 a; + char _m1_pad[8]; + float b; +}; + +struct main0_out +{ + float3 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (float3(Foo.a[0][0u], Foo.a[1][0u], Foo.a[2][0u]) + float3(Foo.a[0][1u], Foo.a[1][1u], Foo.a[2][1u])) + float3(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x2-col-major.invalid.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x2-col-major.invalid.asm.frag new file mode 100644 index 0000000..7430a55 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x2-col-major.invalid.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float2x4 a; + char _m1_pad[8]; + float b; +}; + +struct main0_out +{ + float2 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (Foo.a[0u].xy + Foo.a[1u].xy) + float2(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x2-row-major.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x2-row-major.asm.frag new file mode 100644 index 0000000..19b7f1e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x2-row-major.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float4 a[1]; + char _m1_pad[12]; + float b; +}; + +struct main0_out +{ + float2 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (float2(Foo.a[0][0u], Foo.a[1][0u]) + float2(Foo.a[0][1u], Foo.a[1][1u])) + float2(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x3-col-major.invalid.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x3-col-major.invalid.asm.frag new file mode 100644 index 0000000..f800852 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x3-col-major.invalid.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float2x4 a; + char _m1_pad[12]; + float b; +}; + +struct main0_out +{ + float3 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (Foo.a[0u].xyz + Foo.a[1u].xyz) + float3(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x3-row-major.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x3-row-major.asm.frag new file mode 100644 index 0000000..041b6e9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/packing/scalar-float3x3-row-major.asm.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct type_Foo +{ + float2x4 a; + char _m1_pad[12]; + float b; +}; + +struct main0_out +{ + float3 out_var_SV_Target [[color(0)]]; +}; + +fragment main0_out main0(constant type_Foo& Foo [[buffer(0)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = (float3(Foo.a[0][0u], Foo.a[1][0u], Foo.a[2][0u]) + float3(Foo.a[0][1u], Foo.a[1][1u], Foo.a[2][1u])) + float3(Foo.b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/temporary.zero-initialize.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/temporary.zero-initialize.asm.frag new file mode 100644 index 0000000..6fbf2ff --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/temporary.zero-initialize.asm.frag @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int vA [[user(locn0)]]; + int vB [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = float4(0.0); + int _10 = {}; + int _15 = {}; + for (int _16 = 0, _17 = 0; _16 < in.vA; _17 = _15, _16 += _10) + { + if ((in.vA + _16) == 20) + { + _15 = 50; + } + else + { + _15 = ((in.vB + _16) == 40) ? 60 : _17; + } + _10 = _15 + 10; + out.FragColor += float4(1.0); + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc new file mode 100644 index 0000000..2c83e9e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc @@ -0,0 +1,141 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct VertexOutput +{ + float4 pos; + float2 uv; +}; + +struct HSOut +{ + float4 pos; + float2 uv; +}; + +struct HSConstantOut +{ + spvUnsafeArray EdgeTess; + float InsideTess; +}; + +struct VertexOutput_1 +{ + float2 uv; +}; + +struct HSOut_1 +{ + float2 uv; +}; + +struct main0_out +{ + HSOut_1 _entryPointOutput; + float4 gl_Position; +}; + +struct main0_in +{ + float2 VertexOutput_uv [[attribute(0)]]; + float4 gl_Position [[attribute(1)]]; +}; + +static inline __attribute__((always_inline)) +HSOut _hs_main(thread const spvUnsafeArray (&p), thread const uint& i) +{ + HSOut _output; + _output.pos = p[i].pos; + _output.uv = p[i].uv; + return _output; +} + +static inline __attribute__((always_inline)) +HSConstantOut PatchHS(thread const spvUnsafeArray (&_patch)) +{ + HSConstantOut _output; + _output.EdgeTess[0] = (float2(1.0) + _patch[0].uv).x; + _output.EdgeTess[1] = (float2(1.0) + _patch[0].uv).x; + _output.EdgeTess[2] = (float2(1.0) + _patch[0].uv).x; + _output.InsideTess = (float2(1.0) + _patch[0].uv).x; + return _output; +} + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray p; + p[0].pos = gl_in[0].gl_Position; + p[0].uv = gl_in[0].VertexOutput_uv; + p[1].pos = gl_in[1].gl_Position; + p[1].uv = gl_in[1].VertexOutput_uv; + p[2].pos = gl_in[2].gl_Position; + p[2].uv = gl_in[2].VertexOutput_uv; + uint i = gl_InvocationID; + spvUnsafeArray param; + param = p; + uint param_1 = i; + HSOut flattenTemp = _hs_main(param, param_1); + gl_out[gl_InvocationID].gl_Position = flattenTemp.pos; + gl_out[gl_InvocationID]._entryPointOutput.uv = flattenTemp.uv; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (int(gl_InvocationID) == 0) + { + spvUnsafeArray param_2; + param_2 = p; + HSConstantOut _patchConstantResult = PatchHS(param_2); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(_patchConstantResult.EdgeTess[0]); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(_patchConstantResult.EdgeTess[1]); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(_patchConstantResult.EdgeTess[2]); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_patchConstantResult.InsideTess); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.multi-patch.asm.tesc b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.multi-patch.asm.tesc new file mode 100644 index 0000000..0d8a500 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.multi-patch.asm.tesc @@ -0,0 +1,140 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct VertexOutput +{ + float4 pos; + float2 uv; +}; + +struct HSOut +{ + float4 pos; + float2 uv; +}; + +struct HSConstantOut +{ + spvUnsafeArray EdgeTess; + float InsideTess; +}; + +struct VertexOutput_1 +{ + float3 uv; +}; + +struct HSOut_1 +{ + float2 uv; +}; + +struct main0_out +{ + HSOut_1 _entryPointOutput; + float4 gl_Position; +}; + +struct main0_in +{ + float3 VertexOutput_uv; + ushort2 m_172; + float4 gl_Position; +}; + +static inline __attribute__((always_inline)) +HSOut _hs_main(thread const spvUnsafeArray (&p), thread const uint& i) +{ + HSOut _output; + _output.pos = p[i].pos; + _output.uv = p[i].uv; + return _output; +} + +static inline __attribute__((always_inline)) +HSConstantOut PatchHS(thread const spvUnsafeArray (&_patch)) +{ + HSConstantOut _output; + _output.EdgeTess[0] = (float2(1.0) + _patch[0].uv).x; + _output.EdgeTess[1] = (float2(1.0) + _patch[0].uv).x; + _output.EdgeTess[2] = (float2(1.0) + _patch[0].uv).x; + _output.InsideTess = (float2(1.0) + _patch[0].uv).x; + return _output; +} + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 3]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 3; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1]); + spvUnsafeArray p; + p[0].pos = gl_in[0].gl_Position; + p[0].uv = gl_in[0].VertexOutput_uv.xy; + p[1].pos = gl_in[1].gl_Position; + p[1].uv = gl_in[1].VertexOutput_uv.xy; + p[2].pos = gl_in[2].gl_Position; + p[2].uv = gl_in[2].VertexOutput_uv.xy; + uint i = gl_InvocationID; + spvUnsafeArray param; + param = p; + uint param_1 = i; + HSOut flattenTemp = _hs_main(param, param_1); + gl_out[gl_InvocationID].gl_Position = flattenTemp.pos; + gl_out[gl_InvocationID]._entryPointOutput.uv = flattenTemp.uv; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (int(gl_InvocationID) == 0) + { + spvUnsafeArray param_2; + param_2 = p; + HSConstantOut _patchConstantResult = PatchHS(param_2); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(_patchConstantResult.EdgeTess[0]); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(_patchConstantResult.EdgeTess[1]); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(_patchConstantResult.EdgeTess[2]); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_patchConstantResult.InsideTess); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/composite-extract-physical-type-id.asm.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/composite-extract-physical-type-id.asm.vert new file mode 100644 index 0000000..ea89378 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/composite-extract-physical-type-id.asm.vert @@ -0,0 +1,31 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct type_Float2Array +{ + float4 arr[3]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +static inline __attribute__((always_inline)) +float4 src_VSMain(thread const uint& i, constant type_Float2Array& Float2Array) +{ + return float4(Float2Array.arr[i].x, Float2Array.arr[i].y, 0.0, 1.0); +} + +vertex main0_out main0(constant type_Float2Array& Float2Array [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + uint param_var_i = gl_VertexIndex; + out.gl_Position = src_VSMain(param_var_i, Float2Array); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/empty-struct-composite.asm.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/empty-struct-composite.asm.vert new file mode 100644 index 0000000..95b61a4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/empty-struct-composite.asm.vert @@ -0,0 +1,13 @@ +#include +#include + +using namespace metal; + +struct Test +{ +}; +vertex void main0() +{ + Test t = Test{ }; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag new file mode 100644 index 0000000..18d9899 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag @@ -0,0 +1,76 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant float _21 = {}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + spvUnsafeArray _23; + for (int _25 = 0; _25 < 2; ) + { + _23[_25] = 0.0; + _25++; + continue; + } + float _37; + if (as_type(3.0) != 0u) + { + _37 = _23[0]; + } + else + { + _37 = _21; + } + out.gl_Position = float4(0.0, 0.0, 0.0, _37); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/array-copy-threadgroup-memory.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/array-copy-threadgroup-memory.comp new file mode 100644 index 0000000..c849faf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/array-copy-threadgroup-memory.comp @@ -0,0 +1,172 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(8u, 1u, 1u); + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +kernel void main0(uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + threadgroup float shared_group[8][8]; + threadgroup float shared_group_alt[8][8]; + spvUnsafeArray blob; + for (int i = 0; i < 8; i++) + { + blob[i] = float(i); + } + spvArrayCopyFromStackToThreadGroup1(shared_group[gl_LocalInvocationIndex], blob.elements); + threadgroup_barrier(mem_flags::mem_threadgroup); + spvUnsafeArray copied_blob; + spvArrayCopyFromThreadGroupToStack1(copied_blob.elements, shared_group[gl_LocalInvocationIndex ^ 1u]); + spvArrayCopyFromThreadGroupToThreadGroup1(shared_group_alt[gl_LocalInvocationIndex], shared_group[gl_LocalInvocationIndex]); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/basic.dynamic-buffer.msl2.invalid.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/basic.dynamic-buffer.msl2.invalid.comp new file mode 100644 index 0000000..ae8c5b0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/basic.dynamic-buffer.msl2.invalid.comp @@ -0,0 +1,90 @@ +#include +#include + +using namespace metal; + +struct Baz +{ + int e; + int f; +}; + +struct Foo +{ + int a; + int b; +}; + +struct Bar +{ + int c; + int d; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(3u, 3u, 2u); + +struct spvDescriptorSetBuffer0 +{ + constant Foo* m_34 [[id(0)]]; + constant Bar* m_40 [[id(1)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + device Baz* baz [[id(0)]][3][3][2]; +}; + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], constant uint* spvDynamicOffsets [[buffer(23)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + constant auto& _34 = *(constant Foo* )((constant char* )spvDescriptorSet0.m_34 + spvDynamicOffsets[0]); + device Baz* baz[3][3][2] = + { + { + { + (device Baz* )((device char* )spvDescriptorSet1.baz[0][0][0] + spvDynamicOffsets[1]), + (device Baz* )((device char* )spvDescriptorSet1.baz[0][0][1] + spvDynamicOffsets[2]), + }, + { + (device Baz* )((device char* )spvDescriptorSet1.baz[0][1][0] + spvDynamicOffsets[3]), + (device Baz* )((device char* )spvDescriptorSet1.baz[0][1][1] + spvDynamicOffsets[4]), + }, + { + (device Baz* )((device char* )spvDescriptorSet1.baz[0][2][0] + spvDynamicOffsets[5]), + (device Baz* )((device char* )spvDescriptorSet1.baz[0][2][1] + spvDynamicOffsets[6]), + }, + }, + { + { + (device Baz* )((device char* )spvDescriptorSet1.baz[1][0][0] + spvDynamicOffsets[7]), + (device Baz* )((device char* )spvDescriptorSet1.baz[1][0][1] + spvDynamicOffsets[8]), + }, + { + (device Baz* )((device char* )spvDescriptorSet1.baz[1][1][0] + spvDynamicOffsets[9]), + (device Baz* )((device char* )spvDescriptorSet1.baz[1][1][1] + spvDynamicOffsets[10]), + }, + { + (device Baz* )((device char* )spvDescriptorSet1.baz[1][2][0] + spvDynamicOffsets[11]), + (device Baz* )((device char* )spvDescriptorSet1.baz[1][2][1] + spvDynamicOffsets[12]), + }, + }, + { + { + (device Baz* )((device char* )spvDescriptorSet1.baz[2][0][0] + spvDynamicOffsets[13]), + (device Baz* )((device char* )spvDescriptorSet1.baz[2][0][1] + spvDynamicOffsets[14]), + }, + { + (device Baz* )((device char* )spvDescriptorSet1.baz[2][1][0] + spvDynamicOffsets[15]), + (device Baz* )((device char* )spvDescriptorSet1.baz[2][1][1] + spvDynamicOffsets[16]), + }, + { + (device Baz* )((device char* )spvDescriptorSet1.baz[2][2][0] + spvDynamicOffsets[17]), + (device Baz* )((device char* )spvDescriptorSet1.baz[2][2][1] + spvDynamicOffsets[18]), + }, + }, + }; + + uint3 coords = gl_GlobalInvocationID; + baz[coords.x][coords.y][coords.z]->e = _34.a + (*spvDescriptorSet0.m_40).c; + baz[coords.x][coords.y][coords.z]->f = _34.b * (*spvDescriptorSet0.m_40).d; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitcast-16bit-1.invalid.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitcast-16bit-1.invalid.comp new file mode 100644 index 0000000..2e86f99 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitcast-16bit-1.invalid.comp @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct SSBO0 +{ + short4 inputs[1]; +}; + +struct SSBO1 +{ + int4 outputs[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO0& _25 [[buffer(0)]], device SSBO1& _39 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + half2 a = as_type(_25.inputs[ident].xy); + ((device int*)&_39.outputs[ident])[0u] = int(as_type(a + half2(half(1.0)))); + ((device int*)&_39.outputs[ident])[1u] = as_type(_25.inputs[ident].zw); + ((device int*)&_39.outputs[ident])[2u] = int(as_type(ushort2(_25.inputs[ident].xy))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitcast-16bit-2.invalid.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitcast-16bit-2.invalid.comp new file mode 100644 index 0000000..815599c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitcast-16bit-2.invalid.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct SSBO1 +{ + short4 outputs[1]; +}; + +struct SSBO0 +{ + int4 inputs[1]; +}; + +struct UBO +{ + half4 const0; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO1& _21 [[buffer(0)]], device SSBO0& _29 [[buffer(1)]], constant UBO& _40 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + short2 _47 = as_type(((device int*)&_29.inputs[ident])[0u]) + as_type(_40.const0.xy); + _21.outputs[ident] = short4(_47.x, _47.y, _21.outputs[ident].z, _21.outputs[ident].w); + short2 _66 = short2(as_type(uint(((device int*)&_29.inputs[ident])[1u])) - as_type(_40.const0.zw)); + _21.outputs[ident] = short4(_21.outputs[ident].x, _21.outputs[ident].y, _66.x, _66.y); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitfield.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitfield.comp new file mode 100644 index 0000000..7f797ad --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/bitfield.comp @@ -0,0 +1,47 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +// Implementation of the GLSL findLSB() function +template +inline T spvFindLSB(T x) +{ + return select(ctz(x), T(-1), x == T(0)); +} + +// Implementation of the signed GLSL findMSB() function +template +inline T spvFindSMSB(T x) +{ + T v = select(x, T(-1) - x, x < T(0)); + return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); +} + +// Implementation of the unsigned GLSL findMSB() function +template +inline T spvFindUMSB(T x) +{ + return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0)); +} + +kernel void main0() +{ + int signed_value = 0; + uint unsigned_value = 0u; + int s = extract_bits(signed_value, uint(5), uint(20)); + uint u = extract_bits(unsigned_value, uint(6), uint(21)); + s = insert_bits(s, 40, uint(5), uint(4)); + u = insert_bits(u, 60u, uint(5), uint(4)); + u = reverse_bits(u); + s = reverse_bits(s); + int v0 = int(popcount(u)); + int v1 = popcount(s); + int v2 = int(spvFindUMSB(u)); + int v3 = spvFindSMSB(s); + int v4 = int(spvFindLSB(u)); + int v5 = spvFindLSB(s); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/glsl.std450.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/glsl.std450.comp new file mode 100644 index 0000000..3c505ab --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/glsl.std450.comp @@ -0,0 +1,289 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float res; + int ires; + uint ures; + float4 f32; + int4 s32; + uint4 u32; + float2x2 m2; + float3x3 m3; + float4x4 m4; +}; + +struct ResType +{ + float _m0; + int _m1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +// Implementation of the GLSL radians() function +template +inline T radians(T d) +{ + return d * T(0.01745329251); +} + +// Implementation of the GLSL degrees() function +template +inline T degrees(T r) +{ + return r * T(57.2957795131); +} + +// Implementation of the GLSL findLSB() function +template +inline T spvFindLSB(T x) +{ + return select(ctz(x), T(-1), x == T(0)); +} + +// Implementation of the signed GLSL findMSB() function +template +inline T spvFindSMSB(T x) +{ + T v = select(x, T(-1) - x, x < T(0)); + return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); +} + +// Implementation of the unsigned GLSL findMSB() function +template +inline T spvFindUMSB(T x) +{ + return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0)); +} + +// Implementation of the GLSL sign() function for integer types +template::value>::type> +inline T sign(T x) +{ + return select(select(select(x, T(0), x == T(0)), T(1), x > T(0)), T(-1), x < T(0)); +} + +// Returns the determinant of a 2x2 matrix. +static inline __attribute__((always_inline)) +float spvDet2x2(float a1, float a2, float b1, float b2) +{ + return a1 * b2 - b1 * a2; +} + +// Returns the determinant of a 3x3 matrix. +static inline __attribute__((always_inline)) +float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) +{ + return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * spvDet2x2(a2, a3, b2, b3); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float4x4 spvInverse4x4(float4x4 m) +{ + float4x4 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3]); + adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]); + + adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3]); + adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]); + + adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3]); + adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]); + + adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2]); + adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] * m[3][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float3x3 spvInverse3x3(float3x3 m) +{ + float3x3 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = spvDet2x2(m[1][1], m[1][2], m[2][1], m[2][2]); + adj[0][1] = -spvDet2x2(m[0][1], m[0][2], m[2][1], m[2][2]); + adj[0][2] = spvDet2x2(m[0][1], m[0][2], m[1][1], m[1][2]); + + adj[1][0] = -spvDet2x2(m[1][0], m[1][2], m[2][0], m[2][2]); + adj[1][1] = spvDet2x2(m[0][0], m[0][2], m[2][0], m[2][2]); + adj[1][2] = -spvDet2x2(m[0][0], m[0][2], m[1][0], m[1][2]); + + adj[2][0] = spvDet2x2(m[1][0], m[1][1], m[2][0], m[2][1]); + adj[2][1] = -spvDet2x2(m[0][0], m[0][1], m[2][0], m[2][1]); + adj[2][2] = spvDet2x2(m[0][0], m[0][1], m[1][0], m[1][1]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float2x2 spvInverse2x2(float2x2 m) +{ + float2x2 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = m[1][1]; + adj[0][1] = -m[0][1]; + + adj[1][0] = -m[1][0]; + adj[1][1] = m[0][0]; + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +template +inline T spvReflect(T i, T n) +{ + return i - T(2) * i * n * n; +} + +template +inline T spvRefract(T i, T n, T eta) +{ + T NoI = n * i; + T NoI2 = NoI * NoI; + T k = T(1) - eta * eta * (T(1) - NoI2); + if (k < T(0)) + { + return T(0); + } + else + { + return eta * i - (eta * NoI + sqrt(k)) * n; + } +} + +template +inline T spvFaceForward(T n, T i, T nref) +{ + return i * nref < T(0) ? n : -n; +} + +kernel void main0(device SSBO& _19 [[buffer(0)]]) +{ + _19.res = round(((device float*)&_19.f32)[0u]); + _19.res = rint(((device float*)&_19.f32)[0u]); + _19.res = trunc(((device float*)&_19.f32)[0u]); + _19.res = abs(((device float*)&_19.f32)[0u]); + _19.ires = abs(((device int*)&_19.s32)[0u]); + _19.res = sign(((device float*)&_19.f32)[0u]); + _19.ires = sign(((device int*)&_19.s32)[0u]); + _19.res = floor(((device float*)&_19.f32)[0u]); + _19.res = ceil(((device float*)&_19.f32)[0u]); + _19.res = fract(((device float*)&_19.f32)[0u]); + _19.res = radians(((device float*)&_19.f32)[0u]); + _19.res = degrees(((device float*)&_19.f32)[0u]); + _19.res = sin(((device float*)&_19.f32)[0u]); + _19.res = cos(((device float*)&_19.f32)[0u]); + _19.res = tan(((device float*)&_19.f32)[0u]); + _19.res = asin(((device float*)&_19.f32)[0u]); + _19.res = acos(((device float*)&_19.f32)[0u]); + _19.res = atan(((device float*)&_19.f32)[0u]); + _19.res = sinh(((device float*)&_19.f32)[0u]); + _19.res = cosh(((device float*)&_19.f32)[0u]); + _19.res = tanh(((device float*)&_19.f32)[0u]); + _19.res = asinh(((device float*)&_19.f32)[0u]); + _19.res = acosh(((device float*)&_19.f32)[0u]); + _19.res = atanh(((device float*)&_19.f32)[0u]); + _19.res = atan2(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u]); + _19.res = pow(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u]); + _19.res = exp(((device float*)&_19.f32)[0u]); + _19.res = log(((device float*)&_19.f32)[0u]); + _19.res = exp2(((device float*)&_19.f32)[0u]); + _19.res = log2(((device float*)&_19.f32)[0u]); + _19.res = sqrt(((device float*)&_19.f32)[0u]); + _19.res = rsqrt(((device float*)&_19.f32)[0u]); + _19.res = abs(((device float*)&_19.f32)[0u]); + _19.res = abs(((device float*)&_19.f32)[0u] - ((device float*)&_19.f32)[1u]); + _19.res = sign(((device float*)&_19.f32)[0u]); + _19.res = spvFaceForward(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u], ((device float*)&_19.f32)[2u]); + _19.res = spvReflect(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u]); + _19.res = spvRefract(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u], ((device float*)&_19.f32)[2u]); + _19.res = length(_19.f32.xy); + _19.res = distance(_19.f32.xy, _19.f32.zw); + float2 v2 = normalize(_19.f32.xy); + v2 = faceforward(_19.f32.xy, _19.f32.yz, _19.f32.zw); + v2 = reflect(_19.f32.xy, _19.f32.zw); + v2 = refract(_19.f32.xy, _19.f32.yz, ((device float*)&_19.f32)[3u]); + float3 v3 = cross(_19.f32.xyz, _19.f32.yzw); + _19.res = determinant(_19.m2); + _19.res = determinant(_19.m3); + _19.res = determinant(_19.m4); + _19.m2 = spvInverse2x2(_19.m2); + _19.m3 = spvInverse3x3(_19.m3); + _19.m4 = spvInverse4x4(_19.m4); + float tmp; + float _287 = modf(((device float*)&_19.f32)[0u], tmp); + _19.res = _287; + _19.res = fast::min(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u]); + _19.ures = min(((device uint*)&_19.u32)[0u], ((device uint*)&_19.u32)[1u]); + _19.ires = min(((device int*)&_19.s32)[0u], ((device int*)&_19.s32)[1u]); + _19.res = fast::max(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u]); + _19.ures = max(((device uint*)&_19.u32)[0u], ((device uint*)&_19.u32)[1u]); + _19.ires = max(((device int*)&_19.s32)[0u], ((device int*)&_19.s32)[1u]); + _19.res = fast::clamp(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u], ((device float*)&_19.f32)[2u]); + _19.ures = clamp(((device uint*)&_19.u32)[0u], ((device uint*)&_19.u32)[1u], ((device uint*)&_19.u32)[2u]); + _19.ires = clamp(((device int*)&_19.s32)[0u], ((device int*)&_19.s32)[1u], ((device int*)&_19.s32)[2u]); + _19.res = mix(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u], ((device float*)&_19.f32)[2u]); + _19.res = step(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u]); + _19.res = smoothstep(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u], ((device float*)&_19.f32)[2u]); + _19.res = fma(((device float*)&_19.f32)[0u], ((device float*)&_19.f32)[1u], ((device float*)&_19.f32)[2u]); + ResType _387; + _387._m0 = frexp(((device float*)&_19.f32)[0u], _387._m1); + int itmp = _387._m1; + _19.res = _387._m0; + _19.res = ldexp(((device float*)&_19.f32)[0u], itmp); + _19.ures = pack_float_to_snorm4x8(_19.f32); + _19.ures = pack_float_to_unorm4x8(_19.f32); + _19.ures = pack_float_to_snorm2x16(_19.f32.xy); + _19.ures = pack_float_to_unorm2x16(_19.f32.xy); + _19.ures = as_type(half2(_19.f32.xy)); + v2 = unpack_snorm2x16_to_float(((device uint*)&_19.u32)[0u]); + v2 = unpack_unorm2x16_to_float(((device uint*)&_19.u32)[0u]); + v2 = float2(as_type(((device uint*)&_19.u32)[0u])); + float4 v4 = unpack_snorm4x8_to_float(((device uint*)&_19.u32)[0u]); + v4 = unpack_unorm4x8_to_float(((device uint*)&_19.u32)[0u]); + _19.s32 = spvFindLSB(_19.s32); + _19.s32 = int4(spvFindLSB(_19.u32)); + _19.s32 = spvFindSMSB(_19.s32); + _19.s32 = int4(spvFindUMSB(_19.u32)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/illegal-struct-name.asm.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/illegal-struct-name.asm.comp new file mode 100644 index 0000000..de1695b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/illegal-struct-name.asm.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + float _abs; +}; + +struct Foo_1 +{ + float _abs; +}; + +struct SSBO +{ + Foo_1 foo; + Foo_1 foo2; +}; + +kernel void main0(device SSBO& _7 [[buffer(0)]]) +{ + Foo f; + f._abs = _7.foo._abs; + int _abs = 10; + _7.foo2._abs = f._abs; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/int64.invalid.msl22.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/int64.invalid.msl22.comp new file mode 100644 index 0000000..d5bbbb4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/int64.invalid.msl22.comp @@ -0,0 +1,106 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct M0 +{ + long v; + spvUnsafeArray b; + ulong c; + spvUnsafeArray d; +}; + +struct SSBO0_Type +{ + long4 a; + M0 m0; +}; + +struct SSBO1_Type +{ + ulong4 b; + M0 m0; +}; + +struct SSBO2_Type +{ + spvUnsafeArray a; + spvUnsafeArray b; +}; + +struct SSBO3_Type +{ + spvUnsafeArray a; + spvUnsafeArray b; +}; + +struct SSBO +{ + int s32; + uint u32; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _96 [[buffer(0)]]) +{ + SSBO0_Type ssbo_0; + ssbo_0.a += long4(10l, 20l, 30l, 40l); + SSBO1_Type ssbo_1; + ssbo_1.b += ulong4(999999999999999999ul, 8888888888888888ul, 77777777777777777ul, 6666666666666666ul); + ssbo_0.a += long4(20l); + ssbo_0.a = abs(ssbo_0.a + long4(ssbo_1.b)); + ssbo_0.a += long4(1l); + ssbo_1.b += ulong4(long4(1l)); + ssbo_0.a -= long4(1l); + ssbo_1.b -= ulong4(long4(1l)); + SSBO2_Type ssbo_2; + ssbo_2.a[0] += 1l; + SSBO3_Type ssbo_3; + ssbo_3.a[0] += 2l; + _96.s32 = int(uint(((ulong(ssbo_0.a.x) + ssbo_1.b.y) + ulong(ssbo_2.a[1])) + ulong(ssbo_3.a[2]))); + _96.u32 = uint(((ulong(ssbo_0.a.y) + ssbo_1.b.z) + ulong(ssbo_2.a[0])) + ulong(ssbo_3.a[1])); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/loop.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/loop.comp new file mode 100644 index 0000000..34fe64b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/loop.comp @@ -0,0 +1,91 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4x4 mvp; + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _24 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + float4 idat = _24.in_data[ident]; + int k = 0; + uint i = 0u; + if (idat.y == 20.0) + { + do + { + k *= 2; + i++; + } while (i < ident); + } + switch (k) + { + case 10: + { + for (;;) + { + i++; + if (i > 10u) + { + break; + } + continue; + } + break; + } + default: + { + for (;;) + { + i += 2u; + if (i > 20u) + { + break; + } + continue; + } + break; + } + } + while (k < 10) + { + idat *= 2.0; + k++; + } + for (uint i_1 = 0u; i_1 < 16u; i_1++, k++) + { + for (uint j = 0u; j < 30u; j++) + { + idat = _24.mvp * idat; + } + } + k = 0; + for (;;) + { + k++; + if (k > 10) + { + k += 2; + } + else + { + k += 3; + continue; + } + k += 10; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/return.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/return.comp new file mode 100644 index 0000000..04cacea --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/return.comp @@ -0,0 +1,39 @@ +#include +#include + +using namespace metal; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _27 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + if (ident == 2u) + { + _27.out_data[ident] = float4(20.0); + } + else + { + if (ident == 4u) + { + _27.out_data[ident] = float4(10.0); + return; + } + } + int i = 0; + while (i < 20) + { + if (i == 10) + { + break; + } + return; + } + _27.out_data[ident] = float4(10.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/std140-array-load-composite-construct.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/std140-array-load-composite-construct.comp new file mode 100644 index 0000000..ba278cc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/std140-array-load-composite-construct.comp @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 a[16]; + float4 b[16]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _14 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _14.b[gl_GlobalInvocationID.x] = float4(_14.a[gl_GlobalInvocationID.x].x); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp new file mode 100644 index 0000000..49758ca --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp @@ -0,0 +1,151 @@ +#include +#include + +using namespace metal; + +typedef packed_float2 packed_float2x2[2]; +typedef packed_float3 packed_float2x3[2]; +typedef packed_float3 packed_rm_float3x2[2]; + +struct S0 +{ + packed_float2 a[1]; + float b; +}; + +struct S1 +{ + packed_float3 a; + float b; +}; + +struct S2 +{ + packed_float3 a[1]; + float b; +}; + +struct S3 +{ + packed_float2 a; + float b; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; +}; + +struct SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + float2x2 m0; + float2x2 m1; + packed_float2x3 m2[4]; + float3x2 m3; + float2x2 m4; + float2x2 m5[9]; + float3x2 m6[4][2]; + packed_rm_float3x2 m7; + float array[1]; +}; + +struct S0_1 +{ + float2 a[1]; + char _m1_pad[8]; + float b; + char _m0_final_padding[12]; +}; + +struct S1_1 +{ + packed_float3 a; + float b; +}; + +struct S2_1 +{ + float3 a[1]; + float b; + char _m0_final_padding[12]; +}; + +struct S3_1 +{ + float2 a; + float b; +}; + +struct Content_1 +{ + S0_1 m0s[1]; + S1_1 m1s[1]; + S2_1 m2s[1]; + S0_1 m0; + S1_1 m1; + S2_1 m2; + S3_1 m3; + float m4; + char _m0_final_padding[12]; +}; + +struct SSBO0 +{ + Content_1 content; + Content_1 content1[2]; + Content_1 content2; + float2x4 m0; + float2x4 m1; + float2x3 m2[4]; + float3x4 m3; + float2x4 m4; + float2x4 m5[9]; + float3x4 m6[4][2]; + float2x3 m7; + float4 array[1]; +}; + +struct SSBO2 +{ + float m0; + packed_float2x2 m1; + packed_rm_float3x2 m2; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO1& ssbo_scalar [[buffer(0)]], device SSBO0& ssbo_140 [[buffer(1)]], device SSBO2& ssbo_scalar2 [[buffer(2)]]) +{ + ssbo_scalar.content.m0s[0].a[0] = ssbo_140.content.m0s[0].a[0]; + ssbo_scalar.content.m0s[0].b = ssbo_140.content.m0s[0].b; + ssbo_scalar.content.m1s[0].a = float3(ssbo_140.content.m1s[0].a); + ssbo_scalar.content.m1s[0].b = ssbo_140.content.m1s[0].b; + ssbo_scalar.content.m2s[0].a[0] = ssbo_140.content.m2s[0].a[0]; + ssbo_scalar.content.m2s[0].b = ssbo_140.content.m2s[0].b; + ssbo_scalar.content.m0.a[0] = ssbo_140.content.m0.a[0]; + ssbo_scalar.content.m0.b = ssbo_140.content.m0.b; + ssbo_scalar.content.m1.a = float3(ssbo_140.content.m1.a); + ssbo_scalar.content.m1.b = ssbo_140.content.m1.b; + ssbo_scalar.content.m2.a[0] = ssbo_140.content.m2.a[0]; + ssbo_scalar.content.m2.b = ssbo_140.content.m2.b; + ssbo_scalar.content.m3.a = ssbo_140.content.m3.a; + ssbo_scalar.content.m3.b = ssbo_140.content.m3.b; + ssbo_scalar.content.m4 = ssbo_140.content.m4; + ssbo_scalar.content.m1.a = float2x3(float3(ssbo_scalar.m2[1][0]), float3(ssbo_scalar.m2[1][1])) * float2(ssbo_scalar.content.m0.a[0]); + ssbo_scalar.m0 = float2x2(float2(ssbo_scalar2.m1[0]), float2(ssbo_scalar2.m1[1])); + ssbo_scalar2.m1[0] = float2(ssbo_scalar.m4[0][0], ssbo_scalar.m4[1][0]); + ssbo_scalar2.m1[1] = float2(ssbo_scalar.m4[0][1], ssbo_scalar.m4[1][1]); + ssbo_scalar2.m2[0] = float3(ssbo_scalar.m3[0][0], ssbo_scalar.m3[1][0], ssbo_scalar.m3[2][0]); + ssbo_scalar2.m2[1] = float3(ssbo_scalar.m3[0][1], ssbo_scalar.m3[1][1], ssbo_scalar.m3[2][1]); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.comp new file mode 100644 index 0000000..6ad48b4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.comp @@ -0,0 +1,320 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float FragColor; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +template +inline T spvSubgroupBroadcast(T value, ushort lane) +{ + return simd_broadcast(value, lane); +} + +template<> +inline bool spvSubgroupBroadcast(bool value, ushort lane) +{ + return !!simd_broadcast((ushort)value, lane); +} + +template +inline vec spvSubgroupBroadcast(vec value, ushort lane) +{ + return (vec)simd_broadcast((vec)value, lane); +} + +template +inline T spvSubgroupBroadcastFirst(T value) +{ + return simd_broadcast_first(value); +} + +template<> +inline bool spvSubgroupBroadcastFirst(bool value) +{ + return !!simd_broadcast_first((ushort)value); +} + +template +inline vec spvSubgroupBroadcastFirst(vec value) +{ + return (vec)simd_broadcast_first((vec)value); +} + +inline uint4 spvSubgroupBallot(bool value) +{ + simd_vote vote = simd_ballot(value); + // simd_ballot() returns a 64-bit integer-like object, but + // SPIR-V callers expect a uint4. We must convert. + // FIXME: This won't include higher bits if Apple ever supports + // 128 lanes in an SIMD-group. + return uint4((uint)((simd_vote::vote_t)vote & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)vote >> 32) & 0xFFFFFFFF), 0, 0); +} + +inline bool spvSubgroupBallotBitExtract(uint4 ballot, uint bit) +{ + return !!extract_bits(ballot[bit / 32], bit % 32, 1); +} + +inline uint spvSubgroupBallotFindLSB(uint4 ballot, uint gl_SubgroupSize) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0)); + ballot &= mask; + return select(ctz(ballot.x), select(32 + ctz(ballot.y), select(64 + ctz(ballot.z), select(96 + ctz(ballot.w), uint(-1), ballot.w == 0), ballot.z == 0), ballot.y == 0), ballot.x == 0); +} + +inline uint spvSubgroupBallotFindMSB(uint4 ballot, uint gl_SubgroupSize) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0)); + ballot &= mask; + return select(128 - (clz(ballot.w) + 1), select(96 - (clz(ballot.z) + 1), select(64 - (clz(ballot.y) + 1), select(32 - (clz(ballot.x) + 1), uint(-1), ballot.x == 0), ballot.y == 0), ballot.z == 0), ballot.w == 0); +} + +inline uint spvPopCount4(uint4 ballot) +{ + return popcount(ballot.x) + popcount(ballot.y) + popcount(ballot.z) + popcount(ballot.w); +} + +inline uint spvSubgroupBallotBitCount(uint4 ballot, uint gl_SubgroupSize) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0)); + return spvPopCount4(ballot & mask); +} + +inline uint spvSubgroupBallotInclusiveBitCount(uint4 ballot, uint gl_SubgroupInvocationID) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID + 1 - 32, 0)), uint2(0)); + return spvPopCount4(ballot & mask); +} + +inline uint spvSubgroupBallotExclusiveBitCount(uint4 ballot, uint gl_SubgroupInvocationID) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID - 32, 0)), uint2(0)); + return spvPopCount4(ballot & mask); +} + +template +inline bool spvSubgroupAllEqual(T value) +{ + return simd_all(all(value == simd_broadcast_first(value))); +} + +template<> +inline bool spvSubgroupAllEqual(bool value) +{ + return simd_all(value) || !simd_any(value); +} + +template +inline bool spvSubgroupAllEqual(vec value) +{ + return simd_all(all(value == (vec)simd_broadcast_first((vec)value))); +} + +template +inline T spvSubgroupShuffle(T value, ushort lane) +{ + return simd_shuffle(value, lane); +} + +template<> +inline bool spvSubgroupShuffle(bool value, ushort lane) +{ + return !!simd_shuffle((ushort)value, lane); +} + +template +inline vec spvSubgroupShuffle(vec value, ushort lane) +{ + return (vec)simd_shuffle((vec)value, lane); +} + +template +inline T spvSubgroupShuffleXor(T value, ushort mask) +{ + return simd_shuffle_xor(value, mask); +} + +template<> +inline bool spvSubgroupShuffleXor(bool value, ushort mask) +{ + return !!simd_shuffle_xor((ushort)value, mask); +} + +template +inline vec spvSubgroupShuffleXor(vec value, ushort mask) +{ + return (vec)simd_shuffle_xor((vec)value, mask); +} + +template +inline T spvSubgroupShuffleUp(T value, ushort delta) +{ + return simd_shuffle_up(value, delta); +} + +template<> +inline bool spvSubgroupShuffleUp(bool value, ushort delta) +{ + return !!simd_shuffle_up((ushort)value, delta); +} + +template +inline vec spvSubgroupShuffleUp(vec value, ushort delta) +{ + return (vec)simd_shuffle_up((vec)value, delta); +} + +template +inline T spvSubgroupShuffleDown(T value, ushort delta) +{ + return simd_shuffle_down(value, delta); +} + +template<> +inline bool spvSubgroupShuffleDown(bool value, ushort delta) +{ + return !!simd_shuffle_down((ushort)value, delta); +} + +template +inline vec spvSubgroupShuffleDown(vec value, ushort delta) +{ + return (vec)simd_shuffle_down((vec)value, delta); +} + +template +inline T spvQuadBroadcast(T value, uint lane) +{ + return quad_broadcast(value, lane); +} + +template<> +inline bool spvQuadBroadcast(bool value, uint lane) +{ + return !!quad_broadcast((ushort)value, lane); +} + +template +inline vec spvQuadBroadcast(vec value, uint lane) +{ + return (vec)quad_broadcast((vec)value, lane); +} + +template +inline T spvQuadSwap(T value, uint dir) +{ + return quad_shuffle_xor(value, dir + 1); +} + +template<> +inline bool spvQuadSwap(bool value, uint dir) +{ + return !!quad_shuffle_xor((ushort)value, dir + 1); +} + +template +inline vec spvQuadSwap(vec value, uint dir) +{ + return (vec)quad_shuffle_xor((vec)value, dir + 1); +} + +kernel void main0(device SSBO& _9 [[buffer(0)]], uint gl_NumSubgroups [[simdgroups_per_threadgroup]], uint gl_SubgroupID [[simdgroup_index_in_threadgroup]], uint gl_SubgroupSize [[thread_execution_width]], uint gl_SubgroupInvocationID [[thread_index_in_simdgroup]]) +{ + uint4 gl_SubgroupEqMask = gl_SubgroupInvocationID >= 32 ? uint4(0, (1 << (gl_SubgroupInvocationID - 32)), uint2(0)) : uint4(1 << gl_SubgroupInvocationID, uint3(0)); + uint4 gl_SubgroupGeMask = uint4(insert_bits(0u, 0xFFFFFFFF, min(gl_SubgroupInvocationID, 32u), (uint)max(min((int)gl_SubgroupSize, 32) - (int)gl_SubgroupInvocationID, 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)gl_SubgroupInvocationID - 32, 0), (uint)max((int)gl_SubgroupSize - (int)max(gl_SubgroupInvocationID, 32u), 0)), uint2(0)); + uint4 gl_SubgroupGtMask = uint4(insert_bits(0u, 0xFFFFFFFF, min(gl_SubgroupInvocationID + 1, 32u), (uint)max(min((int)gl_SubgroupSize, 32) - (int)gl_SubgroupInvocationID - 1, 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)gl_SubgroupInvocationID + 1 - 32, 0), (uint)max((int)gl_SubgroupSize - (int)max(gl_SubgroupInvocationID + 1, 32u), 0)), uint2(0)); + uint4 gl_SubgroupLeMask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID + 1 - 32, 0)), uint2(0)); + uint4 gl_SubgroupLtMask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID - 32, 0)), uint2(0)); + _9.FragColor = float(gl_NumSubgroups); + _9.FragColor = float(gl_SubgroupID); + _9.FragColor = float(gl_SubgroupSize); + _9.FragColor = float(gl_SubgroupInvocationID); + simdgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); + simdgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); + simdgroup_barrier(mem_flags::mem_device); + simdgroup_barrier(mem_flags::mem_threadgroup); + simdgroup_barrier(mem_flags::mem_texture); + bool elected = simd_is_first(); + _9.FragColor = float4(gl_SubgroupEqMask).x; + _9.FragColor = float4(gl_SubgroupGeMask).x; + _9.FragColor = float4(gl_SubgroupGtMask).x; + _9.FragColor = float4(gl_SubgroupLeMask).x; + _9.FragColor = float4(gl_SubgroupLtMask).x; + float4 broadcasted = spvSubgroupBroadcast(float4(10.0), 8u); + bool2 broadcasted_bool = spvSubgroupBroadcast(bool2(true), 8u); + float3 first = spvSubgroupBroadcastFirst(float3(20.0)); + bool4 first_bool = spvSubgroupBroadcastFirst(bool4(false)); + uint4 ballot_value = spvSubgroupBallot(true); + bool inverse_ballot_value = spvSubgroupBallotBitExtract(ballot_value, gl_SubgroupInvocationID); + bool bit_extracted = spvSubgroupBallotBitExtract(uint4(10u), 8u); + uint bit_count = spvSubgroupBallotBitCount(ballot_value, gl_SubgroupSize); + uint inclusive_bit_count = spvSubgroupBallotInclusiveBitCount(ballot_value, gl_SubgroupInvocationID); + uint exclusive_bit_count = spvSubgroupBallotExclusiveBitCount(ballot_value, gl_SubgroupInvocationID); + uint lsb = spvSubgroupBallotFindLSB(ballot_value, gl_SubgroupSize); + uint msb = spvSubgroupBallotFindMSB(ballot_value, gl_SubgroupSize); + uint shuffled = spvSubgroupShuffle(10u, 8u); + bool shuffled_bool = spvSubgroupShuffle(true, 9u); + uint shuffled_xor = spvSubgroupShuffleXor(30u, 8u); + bool shuffled_xor_bool = spvSubgroupShuffleXor(false, 9u); + uint shuffled_up = spvSubgroupShuffleUp(20u, 4u); + bool shuffled_up_bool = spvSubgroupShuffleUp(true, 4u); + uint shuffled_down = spvSubgroupShuffleDown(20u, 4u); + bool shuffled_down_bool = spvSubgroupShuffleDown(false, 4u); + bool has_all = simd_all(true); + bool has_any = simd_any(true); + bool has_equal = spvSubgroupAllEqual(0); + has_equal = spvSubgroupAllEqual(true); + has_equal = spvSubgroupAllEqual(float3(0.0, 1.0, 2.0)); + has_equal = spvSubgroupAllEqual(bool4(true, true, false, true)); + float4 added = simd_sum(float4(20.0)); + int4 iadded = simd_sum(int4(20)); + float4 multiplied = simd_product(float4(20.0)); + int4 imultiplied = simd_product(int4(20)); + float4 lo = simd_min(float4(20.0)); + float4 hi = simd_max(float4(20.0)); + int4 slo = simd_min(int4(20)); + int4 shi = simd_max(int4(20)); + uint4 ulo = simd_min(uint4(20u)); + uint4 uhi = simd_max(uint4(20u)); + uint4 anded = simd_and(ballot_value); + uint4 ored = simd_or(ballot_value); + uint4 xored = simd_xor(ballot_value); + added = simd_prefix_inclusive_sum(added); + iadded = simd_prefix_inclusive_sum(iadded); + multiplied = simd_prefix_inclusive_product(multiplied); + imultiplied = simd_prefix_inclusive_product(imultiplied); + added = simd_prefix_exclusive_sum(multiplied); + multiplied = simd_prefix_exclusive_product(multiplied); + iadded = simd_prefix_exclusive_sum(imultiplied); + imultiplied = simd_prefix_exclusive_product(imultiplied); + added = quad_sum(added); + multiplied = quad_product(multiplied); + iadded = quad_sum(iadded); + imultiplied = quad_product(imultiplied); + lo = quad_min(lo); + hi = quad_max(hi); + ulo = quad_min(ulo); + uhi = quad_max(uhi); + slo = quad_min(slo); + shi = quad_max(shi); + anded = quad_and(anded); + ored = quad_or(ored); + xored = quad_xor(xored); + float4 swap_horiz = spvQuadSwap(float4(20.0), 0u); + bool4 swap_horiz_bool = spvQuadSwap(bool4(true), 0u); + float4 swap_vertical = spvQuadSwap(float4(20.0), 1u); + bool4 swap_vertical_bool = spvQuadSwap(bool4(true), 1u); + float4 swap_diagonal = spvQuadSwap(float4(20.0), 2u); + bool4 swap_diagonal_bool = spvQuadSwap(bool4(true), 2u); + float4 quad_broadcast0 = spvQuadBroadcast(float4(20.0), 3u); + bool4 quad_broadcast_bool = spvQuadBroadcast(bool4(true), 3u); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.ios.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.ios.comp new file mode 100644 index 0000000..de38c72 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.ios.comp @@ -0,0 +1,151 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float FragColor; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +template +inline T spvSubgroupShuffle(T value, ushort lane) +{ + return quad_shuffle(value, lane); +} + +template<> +inline bool spvSubgroupShuffle(bool value, ushort lane) +{ + return !!quad_shuffle((ushort)value, lane); +} + +template +inline vec spvSubgroupShuffle(vec value, ushort lane) +{ + return (vec)quad_shuffle((vec)value, lane); +} + +template +inline T spvSubgroupShuffleXor(T value, ushort mask) +{ + return quad_shuffle_xor(value, mask); +} + +template<> +inline bool spvSubgroupShuffleXor(bool value, ushort mask) +{ + return !!quad_shuffle_xor((ushort)value, mask); +} + +template +inline vec spvSubgroupShuffleXor(vec value, ushort mask) +{ + return (vec)quad_shuffle_xor((vec)value, mask); +} + +template +inline T spvSubgroupShuffleUp(T value, ushort delta) +{ + return quad_shuffle_up(value, delta); +} + +template<> +inline bool spvSubgroupShuffleUp(bool value, ushort delta) +{ + return !!quad_shuffle_up((ushort)value, delta); +} + +template +inline vec spvSubgroupShuffleUp(vec value, ushort delta) +{ + return (vec)quad_shuffle_up((vec)value, delta); +} + +template +inline T spvSubgroupShuffleDown(T value, ushort delta) +{ + return quad_shuffle_down(value, delta); +} + +template<> +inline bool spvSubgroupShuffleDown(bool value, ushort delta) +{ + return !!quad_shuffle_down((ushort)value, delta); +} + +template +inline vec spvSubgroupShuffleDown(vec value, ushort delta) +{ + return (vec)quad_shuffle_down((vec)value, delta); +} + +template +inline T spvQuadBroadcast(T value, uint lane) +{ + return quad_broadcast(value, lane); +} + +template<> +inline bool spvQuadBroadcast(bool value, uint lane) +{ + return !!quad_broadcast((ushort)value, lane); +} + +template +inline vec spvQuadBroadcast(vec value, uint lane) +{ + return (vec)quad_broadcast((vec)value, lane); +} + +template +inline T spvQuadSwap(T value, uint dir) +{ + return quad_shuffle_xor(value, dir + 1); +} + +template<> +inline bool spvQuadSwap(bool value, uint dir) +{ + return !!quad_shuffle_xor((ushort)value, dir + 1); +} + +template +inline vec spvQuadSwap(vec value, uint dir) +{ + return (vec)quad_shuffle_xor((vec)value, dir + 1); +} + +kernel void main0(device SSBO& _9 [[buffer(0)]], uint gl_NumSubgroups [[quadgroups_per_threadgroup]], uint gl_SubgroupID [[quadgroup_index_in_threadgroup]], uint gl_SubgroupSize [[thread_execution_width]], uint gl_SubgroupInvocationID [[thread_index_in_quadgroup]]) +{ + _9.FragColor = float(gl_NumSubgroups); + _9.FragColor = float(gl_SubgroupID); + _9.FragColor = float(gl_SubgroupSize); + _9.FragColor = float(gl_SubgroupInvocationID); + simdgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); + simdgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); + simdgroup_barrier(mem_flags::mem_device); + simdgroup_barrier(mem_flags::mem_threadgroup); + simdgroup_barrier(mem_flags::mem_texture); + uint shuffled = spvSubgroupShuffle(10u, 8u); + bool shuffled_bool = spvSubgroupShuffle(true, 9u); + uint shuffled_xor = spvSubgroupShuffleXor(30u, 8u); + bool shuffled_xor_bool = spvSubgroupShuffleXor(false, 9u); + uint shuffled_up = spvSubgroupShuffleUp(20u, 4u); + bool shuffled_up_bool = spvSubgroupShuffleUp(true, 4u); + uint shuffled_down = spvSubgroupShuffleDown(20u, 4u); + bool shuffled_down_bool = spvSubgroupShuffleDown(false, 4u); + float4 swap_horiz = spvQuadSwap(float4(20.0), 0u); + bool4 swap_horiz_bool = spvQuadSwap(bool4(true), 0u); + float4 swap_vertical = spvQuadSwap(float4(20.0), 1u); + bool4 swap_vertical_bool = spvQuadSwap(bool4(true), 1u); + float4 swap_diagonal = spvQuadSwap(float4(20.0), 2u); + bool4 swap_diagonal_bool = spvQuadSwap(bool4(true), 2u); + float4 quad_broadcast0 = spvQuadBroadcast(float4(20.0), 3u); + bool4 quad_broadcast_bool = spvQuadBroadcast(bool4(true), 3u); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-input-component.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-input-component.frag new file mode 100644 index 0000000..9a65918 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-input-component.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 Foo3 [[user(locn0)]]; + float Foo1 [[user(locn0_3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = float4(in.Foo3, in.Foo1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-output-component.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-output-component.frag new file mode 100644 index 0000000..7e030aa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-output-component.frag @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor0 [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + float FragColor0 = {}; + float2 FragColor1 = {}; + float FragColor3 = {}; + FragColor0 = 1.0; + FragColor1 = float2(2.0, 3.0); + FragColor3 = 4.0; + out.FragColor0.x = FragColor0; + out.FragColor0.yz = FragColor1; + out.FragColor0.w = FragColor3; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-output-component.pad-fragment.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-output-component.pad-fragment.frag new file mode 100644 index 0000000..13eb7d5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/fragment-output-component.pad-fragment.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor0 [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + float FragColor0 = {}; + float2 FragColor1 = {}; + FragColor0 = 1.0; + FragColor1 = float2(2.0, 3.0); + out.FragColor0.x = FragColor0; + out.FragColor0.yz = FragColor1; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/components/vertex-input-component.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/vertex-input-component.vert new file mode 100644 index 0000000..1aae280 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/vertex-input-component.vert @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float3 Foo [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Foo3 [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float3 Foo3 = {}; + float Foo1 = {}; + Foo3 = in.Foo3.xyz; + Foo1 = in.Foo3.w; + out.gl_Position = float4(Foo3, Foo1); + out.Foo = Foo3 + float3(Foo1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/components/vertex-output-component.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/vertex-output-component.vert new file mode 100644 index 0000000..cf135b5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/components/vertex-output-component.vert @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float3 Foo3 [[user(locn0)]]; + float Foo1 [[user(locn0_3)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vFoo [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.vFoo; + out.Foo3 = in.vFoo.xyz; + out.Foo1 = in.vFoo.w; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/16bit-constants.invalid.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/16bit-constants.invalid.frag new file mode 100644 index 0000000..542beb3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/16bit-constants.invalid.frag @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + half foo [[color(0)]]; + short bar [[color(1)]]; + ushort baz [[color(2)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.foo = half(1.0); + out.bar = short(2); + out.baz = ushort(3); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/demote-to-helper.vk.nocompat.msl21.invalid.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/demote-to-helper.vk.nocompat.msl21.invalid.frag new file mode 100644 index 0000000..0e0348b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/demote-to-helper.vk.nocompat.msl21.invalid.frag @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ + bool _9 = simd_is_helper_thread(); + bool helper = _9; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/depth-image-gather.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/depth-image-gather.asm.frag new file mode 100644 index 0000000..025e225 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/depth-image-gather.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d g_depthTexture [[texture(0)]], sampler g_sampler [[sampler(0)]], sampler g_comp [[sampler(1)]]) +{ + main0_out out = {}; + out.out_var_SV_Target0 = g_depthTexture.gather_compare(g_comp, in.in_var_TEXCOORD0, 0.5) * g_depthTexture.gather(g_sampler, in.in_var_TEXCOORD0, int2(0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/force-active-resources.msl2.argument..force-active.discrete.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/force-active-resources.msl2.argument..force-active.discrete.frag new file mode 100644 index 0000000..5f8dc72 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/force-active-resources.msl2.argument..force-active.discrete.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct spvDescriptorSetBuffer0 +{ + texture2d uTexture2 [[id(0)]]; + sampler uTexture2Smplr [[id(1)]]; + texture2d uTexture1 [[id(2)]]; + sampler uTexture1Smplr [[id(3)]]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], texture2d uTextureDiscrete2 [[texture(0)]], sampler uTextureDiscrete2Smplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = spvDescriptorSet0.uTexture2.sample(spvDescriptorSet0.uTexture2Smplr, in.vUV); + out.FragColor += uTextureDiscrete2.sample(uTextureDiscrete2Smplr, in.vUV); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/fp16.desktop.invalid.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/fp16.desktop.invalid.frag new file mode 100644 index 0000000..3bf4296 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/fp16.desktop.invalid.frag @@ -0,0 +1,185 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct ResType +{ + half4 _m0; + int4 _m1; +}; + +struct main0_in +{ + half v1 [[user(locn0)]]; + half2 v2 [[user(locn1)]]; + half3 v3 [[user(locn2)]]; + half4 v4 [[user(locn3)]]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +// Implementation of the GLSL radians() function +template +inline T radians(T d) +{ + return d * T(0.01745329251); +} + +// Implementation of the GLSL degrees() function +template +inline T degrees(T r) +{ + return r * T(57.2957795131); +} + +static inline __attribute__((always_inline)) +half2x2 test_mat2(thread const half2& a, thread const half2& b, thread const half2& c, thread const half2& d) +{ + return half2x2(half2(a), half2(b)) * half2x2(half2(c), half2(d)); +} + +static inline __attribute__((always_inline)) +half3x3 test_mat3(thread const half3& a, thread const half3& b, thread const half3& c, thread const half3& d, thread const half3& e, thread const half3& f) +{ + return half3x3(half3(a), half3(b), half3(c)) * half3x3(half3(d), half3(e), half3(f)); +} + +static inline __attribute__((always_inline)) +void test_constants() +{ + half a = half(1.0); + half b = half(1.5); + half c = half(-1.5); + half d = half(0.0 / 0.0); + half e = half(1.0 / 0.0); + half f = half(-1.0 / 0.0); + half g = half(1014.0); + half h = half(9.5367431640625e-07); +} + +static inline __attribute__((always_inline)) +half test_result() +{ + return half(1.0); +} + +static inline __attribute__((always_inline)) +void test_conversions() +{ + half one = test_result(); + int a = int(one); + uint b = uint(one); + bool c = (isunordered(one, half(0.0)) || one != half(0.0)); + float d = float(one); + half a2 = half(a); + half b2 = half(b); + half c2 = half(c); + half d2 = half(d); +} + +static inline __attribute__((always_inline)) +void test_builtins(thread half4& v4, thread half3& v3, thread half& v1) +{ + half4 res = radians(v4); + res = degrees(v4); + res = sin(v4); + res = cos(v4); + res = tan(v4); + res = asin(v4); + res = atan2(v4, v3.xyzz); + res = atan(v4); + res = sinh(v4); + res = cosh(v4); + res = tanh(v4); + res = asinh(v4); + res = acosh(v4); + res = atanh(v4); + res = pow(v4, v4); + res = exp(v4); + res = log(v4); + res = exp2(v4); + res = log2(v4); + res = sqrt(v4); + res = rsqrt(v4); + res = abs(v4); + res = sign(v4); + res = floor(v4); + res = trunc(v4); + res = round(v4); + res = rint(v4); + res = ceil(v4); + res = fract(v4); + res = mod(v4, v4); + half4 tmp; + half4 _223 = modf(v4, tmp); + res = _223; + res = min(v4, v4); + res = max(v4, v4); + res = clamp(v4, v4, v4); + res = mix(v4, v4, v4); + res = select(v4, v4, v4 < v4); + res = step(v4, v4); + res = smoothstep(v4, v4, v4); + bool4 btmp = isnan(v4); + btmp = isinf(v4); + res = fma(v4, v4, v4); + ResType _267; + _267._m0 = frexp(v4, _267._m1); + int4 itmp = _267._m1; + res = _267._m0; + res = ldexp(res, itmp); + uint pack0 = as_type(v4.xy); + uint pack1 = as_type(v4.zw); + res = half4(as_type(pack0), as_type(pack1)); + half t0 = length(v4); + t0 = distance(v4, v4); + t0 = dot(v4, v4); + half3 res3 = cross(v3, v3); + res = normalize(v4); + res = faceforward(v4, v4, v4); + res = reflect(v4, v4); + res = refract(v4, v4, v1); + btmp = v4 < v4; + btmp = v4 <= v4; + btmp = v4 > v4; + btmp = v4 >= v4; + btmp = v4 == v4; + btmp = (isunordered(v4, v4) || v4 != v4); + res = dfdx(v4); + res = dfdy(v4); + res = dfdx(v4); + res = dfdy(v4); + res = dfdx(v4); + res = dfdy(v4); + res = fwidth(v4); + res = fwidth(v4); + res = fwidth(v4); +} + +fragment void main0(main0_in in [[stage_in]]) +{ + half2 param = in.v2; + half2 param_1 = in.v2; + half2 param_2 = in.v3.xy; + half2 param_3 = in.v3.xy; + half2x2 m0 = test_mat2(param, param_1, param_2, param_3); + half3 param_4 = in.v3; + half3 param_5 = in.v3; + half3 param_6 = in.v3; + half3 param_7 = in.v4.xyz; + half3 param_8 = in.v4.xyz; + half3 param_9 = in.v4.yzw; + half3x3 m1 = test_mat3(param_4, param_5, param_6, param_7, param_8, param_9); + test_constants(); + test_conversions(); + test_builtins(in.v4, in.v3, in.v1); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/image-gather.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/image-gather.frag new file mode 100644 index 0000000..db793c1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/image-gather.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSamp [[texture(0)]], depth2d uSampShadow [[texture(1)]], sampler uSampSmplr [[sampler(0)]], sampler uSampShadowSmplr [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = uSamp.gather(uSampSmplr, in.vUV.xy, int2(0), component::x); + out.FragColor += uSamp.gather(uSampSmplr, in.vUV.xy, int2(0), component::y); + out.FragColor += uSampShadow.gather_compare(uSampShadowSmplr, in.vUV.xy, in.vUV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/in_block_assign.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/in_block_assign.frag new file mode 100644 index 0000000..427c689 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/in_block_assign.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct VOUT +{ + float4 a; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 VOUT_a [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + VOUT Clip = {}; + Clip.a = in.VOUT_a; + VOUT tmp = Clip; + tmp.a += float4(1.0); + out.FragColor = tmp.a; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/min-max-clamp.invalid.asm.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/min-max-clamp.invalid.asm.frag new file mode 100644 index 0000000..f597a6e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/min-max-clamp.invalid.asm.frag @@ -0,0 +1,69 @@ +#include +#include + +using namespace metal; + +struct main0_in +{ + float v1 [[user(locn0)]]; + float2 v2 [[user(locn1)]]; + float3 v3 [[user(locn2)]]; + float4 v4 [[user(locn3)]]; + half h1 [[user(locn4)]]; + half2 h2 [[user(locn5)]]; + half3 h3 [[user(locn6)]]; + half4 h4 [[user(locn7)]]; +}; + +fragment void main0(main0_in in [[stage_in]]) +{ + float res = fast::min(in.v1, in.v1); + res = fast::max(in.v1, in.v1); + res = fast::clamp(in.v1, in.v1, in.v1); + res = precise::min(in.v1, in.v1); + res = precise::max(in.v1, in.v1); + res = precise::clamp(in.v1, in.v1, in.v1); + float2 res2 = fast::min(in.v2, in.v2); + res2 = fast::max(in.v2, in.v2); + res2 = fast::clamp(in.v2, in.v2, in.v2); + res2 = precise::min(in.v2, in.v2); + res2 = precise::max(in.v2, in.v2); + res2 = precise::clamp(in.v2, in.v2, in.v2); + float3 res3 = fast::min(in.v3, in.v3); + res3 = fast::max(in.v3, in.v3); + res3 = fast::clamp(in.v3, in.v3, in.v3); + res3 = precise::min(in.v3, in.v3); + res3 = precise::max(in.v3, in.v3); + res3 = precise::clamp(in.v3, in.v3, in.v3); + float4 res4 = fast::min(in.v4, in.v4); + res4 = fast::max(in.v4, in.v4); + res4 = fast::clamp(in.v4, in.v4, in.v4); + res4 = precise::min(in.v4, in.v4); + res4 = precise::max(in.v4, in.v4); + res4 = precise::clamp(in.v4, in.v4, in.v4); + half hres = min(in.h1, in.h1); + hres = max(in.h1, in.h1); + hres = clamp(in.h1, in.h1, in.h1); + hres = min(in.h1, in.h1); + hres = max(in.h1, in.h1); + hres = clamp(in.h1, in.h1, in.h1); + half2 hres2 = min(in.h2, in.h2); + hres2 = max(in.h2, in.h2); + hres2 = clamp(in.h2, in.h2, in.h2); + hres2 = min(in.h2, in.h2); + hres2 = max(in.h2, in.h2); + hres2 = clamp(in.h2, in.h2, in.h2); + half3 hres3 = min(in.h3, in.h3); + hres3 = max(in.h3, in.h3); + hres3 = clamp(in.h3, in.h3, in.h3); + hres3 = min(in.h3, in.h3); + hres3 = max(in.h3, in.h3); + hres3 = clamp(in.h3, in.h3, in.h3); + half4 hres4 = min(in.h4, in.h4); + hres4 = max(in.h4, in.h4); + hres4 = clamp(in.h4, in.h4, in.h4); + hres4 = min(in.h4, in.h4); + hres4 = max(in.h4, in.h4); + hres4 = clamp(in.h4, in.h4, in.h4); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/pixel-interlock-simple-callstack.msl2.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/pixel-interlock-simple-callstack.msl2.frag new file mode 100644 index 0000000..cb01950 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/pixel-interlock-simple-callstack.msl2.frag @@ -0,0 +1,37 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO1 +{ + uint values1[1]; +}; + +struct SSBO0 +{ + uint values0[1]; +}; + +static inline __attribute__((always_inline)) +void callee2(device SSBO1& v_14, thread float4& gl_FragCoord) +{ + int _25 = int(gl_FragCoord.x); + v_14.values1[_25]++; +} + +static inline __attribute__((always_inline)) +void callee(device SSBO1& v_14, thread float4& gl_FragCoord, device SSBO0& v_35) +{ + int _38 = int(gl_FragCoord.x); + v_35.values0[_38]++; + callee2(v_14, gl_FragCoord); +} + +fragment void main0(device SSBO1& v_14 [[buffer(0), raster_order_group(0)]], device SSBO0& v_35 [[buffer(1), raster_order_group(0)]], float4 gl_FragCoord [[position]]) +{ + callee(v_14, gl_FragCoord, v_35); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag new file mode 100644 index 0000000..ec25d06 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag @@ -0,0 +1,36 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float a[1]; + float2 b[2]; +}; + +struct UBOEnhancedLayout +{ + float c[1]; + float2 d[2]; + char _m2_pad[9976]; + float e; +}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int vIndex [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _17 [[buffer(0)]], constant UBOEnhancedLayout& _30 [[buffer(1)]]) +{ + main0_out out = {}; + out.FragColor = (_17.a[in.vIndex] + _30.c[in.vIndex]) + _30.e; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/shadow-compare-global-alias.invalid.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/shadow-compare-global-alias.invalid.frag new file mode 100644 index 0000000..8f7b8ec --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/shadow-compare-global-alias.invalid.frag @@ -0,0 +1,57 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float Samp(thread const float3& uv, thread depth2d uTex, thread sampler uSamp) +{ + return uTex.sample_compare(uSamp, uv.xy, uv.z); +} + +static inline __attribute__((always_inline)) +float Samp2(thread const float3& uv, thread depth2d uSampler, thread const sampler uSamplerSmplr, thread float3& vUV) +{ + return uSampler.sample_compare(uSamplerSmplr, vUV.xy, vUV.z); +} + +static inline __attribute__((always_inline)) +float Samp3(thread const depth2d uT, thread const sampler uS, thread const float3& uv, thread float3& vUV) +{ + return uT.sample_compare(uS, vUV.xy, vUV.z); +} + +static inline __attribute__((always_inline)) +float Samp4(thread const depth2d uS, thread const sampler uSSmplr, thread const float3& uv, thread float3& vUV) +{ + return uS.sample_compare(uSSmplr, vUV.xy, vUV.z); +} + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uTex [[texture(0)]], depth2d uSampler [[texture(1)]], sampler uSamp [[sampler(0)]], sampler uSamplerSmplr [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = uSampler.sample_compare(uSamplerSmplr, in.vUV.xy, in.vUV.z); + out.FragColor += uTex.sample_compare(uSamp, in.vUV.xy, in.vUV.z); + float3 param = in.vUV; + out.FragColor += Samp(param, uTex, uSamp); + float3 param_1 = in.vUV; + out.FragColor += Samp2(param_1, uSampler, uSamplerSmplr, in.vUV); + float3 param_2 = in.vUV; + out.FragColor += Samp3(uTex, uSamp, param_2, in.vUV); + float3 param_3 = in.vUV; + out.FragColor += Samp4(uSampler, uSamplerSmplr, param_3, in.vUV); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subgroups.nocompat.invalid.vk.msl21.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subgroups.nocompat.invalid.vk.msl21.frag new file mode 100644 index 0000000..021ce82 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subgroups.nocompat.invalid.vk.msl21.frag @@ -0,0 +1,315 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +template +inline T spvSubgroupBroadcast(T value, ushort lane) +{ + return simd_broadcast(value, lane); +} + +template<> +inline bool spvSubgroupBroadcast(bool value, ushort lane) +{ + return !!simd_broadcast((ushort)value, lane); +} + +template +inline vec spvSubgroupBroadcast(vec value, ushort lane) +{ + return (vec)simd_broadcast((vec)value, lane); +} + +template +inline T spvSubgroupBroadcastFirst(T value) +{ + return simd_broadcast_first(value); +} + +template<> +inline bool spvSubgroupBroadcastFirst(bool value) +{ + return !!simd_broadcast_first((ushort)value); +} + +template +inline vec spvSubgroupBroadcastFirst(vec value) +{ + return (vec)simd_broadcast_first((vec)value); +} + +inline uint4 spvSubgroupBallot(bool value) +{ + simd_vote vote = simd_ballot(value); + // simd_ballot() returns a 64-bit integer-like object, but + // SPIR-V callers expect a uint4. We must convert. + // FIXME: This won't include higher bits if Apple ever supports + // 128 lanes in an SIMD-group. + return uint4((uint)((simd_vote::vote_t)vote & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)vote >> 32) & 0xFFFFFFFF), 0, 0); +} + +inline bool spvSubgroupBallotBitExtract(uint4 ballot, uint bit) +{ + return !!extract_bits(ballot[bit / 32], bit % 32, 1); +} + +inline uint spvSubgroupBallotFindLSB(uint4 ballot, uint gl_SubgroupSize) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0)); + ballot &= mask; + return select(ctz(ballot.x), select(32 + ctz(ballot.y), select(64 + ctz(ballot.z), select(96 + ctz(ballot.w), uint(-1), ballot.w == 0), ballot.z == 0), ballot.y == 0), ballot.x == 0); +} + +inline uint spvSubgroupBallotFindMSB(uint4 ballot, uint gl_SubgroupSize) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0)); + ballot &= mask; + return select(128 - (clz(ballot.w) + 1), select(96 - (clz(ballot.z) + 1), select(64 - (clz(ballot.y) + 1), select(32 - (clz(ballot.x) + 1), uint(-1), ballot.x == 0), ballot.y == 0), ballot.z == 0), ballot.w == 0); +} + +inline uint spvPopCount4(uint4 ballot) +{ + return popcount(ballot.x) + popcount(ballot.y) + popcount(ballot.z) + popcount(ballot.w); +} + +inline uint spvSubgroupBallotBitCount(uint4 ballot, uint gl_SubgroupSize) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0)); + return spvPopCount4(ballot & mask); +} + +inline uint spvSubgroupBallotInclusiveBitCount(uint4 ballot, uint gl_SubgroupInvocationID) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID + 1 - 32, 0)), uint2(0)); + return spvPopCount4(ballot & mask); +} + +inline uint spvSubgroupBallotExclusiveBitCount(uint4 ballot, uint gl_SubgroupInvocationID) +{ + uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID - 32, 0)), uint2(0)); + return spvPopCount4(ballot & mask); +} + +template +inline bool spvSubgroupAllEqual(T value) +{ + return simd_all(all(value == simd_broadcast_first(value))); +} + +template<> +inline bool spvSubgroupAllEqual(bool value) +{ + return simd_all(value) || !simd_any(value); +} + +template +inline bool spvSubgroupAllEqual(vec value) +{ + return simd_all(all(value == (vec)simd_broadcast_first((vec)value))); +} + +template +inline T spvSubgroupShuffle(T value, ushort lane) +{ + return simd_shuffle(value, lane); +} + +template<> +inline bool spvSubgroupShuffle(bool value, ushort lane) +{ + return !!simd_shuffle((ushort)value, lane); +} + +template +inline vec spvSubgroupShuffle(vec value, ushort lane) +{ + return (vec)simd_shuffle((vec)value, lane); +} + +template +inline T spvSubgroupShuffleXor(T value, ushort mask) +{ + return simd_shuffle_xor(value, mask); +} + +template<> +inline bool spvSubgroupShuffleXor(bool value, ushort mask) +{ + return !!simd_shuffle_xor((ushort)value, mask); +} + +template +inline vec spvSubgroupShuffleXor(vec value, ushort mask) +{ + return (vec)simd_shuffle_xor((vec)value, mask); +} + +template +inline T spvSubgroupShuffleUp(T value, ushort delta) +{ + return simd_shuffle_up(value, delta); +} + +template<> +inline bool spvSubgroupShuffleUp(bool value, ushort delta) +{ + return !!simd_shuffle_up((ushort)value, delta); +} + +template +inline vec spvSubgroupShuffleUp(vec value, ushort delta) +{ + return (vec)simd_shuffle_up((vec)value, delta); +} + +template +inline T spvSubgroupShuffleDown(T value, ushort delta) +{ + return simd_shuffle_down(value, delta); +} + +template<> +inline bool spvSubgroupShuffleDown(bool value, ushort delta) +{ + return !!simd_shuffle_down((ushort)value, delta); +} + +template +inline vec spvSubgroupShuffleDown(vec value, ushort delta) +{ + return (vec)simd_shuffle_down((vec)value, delta); +} + +template +inline T spvQuadBroadcast(T value, uint lane) +{ + return quad_broadcast(value, lane); +} + +template<> +inline bool spvQuadBroadcast(bool value, uint lane) +{ + return !!quad_broadcast((ushort)value, lane); +} + +template +inline vec spvQuadBroadcast(vec value, uint lane) +{ + return (vec)quad_broadcast((vec)value, lane); +} + +template +inline T spvQuadSwap(T value, uint dir) +{ + return quad_shuffle_xor(value, dir + 1); +} + +template<> +inline bool spvQuadSwap(bool value, uint dir) +{ + return !!quad_shuffle_xor((ushort)value, dir + 1); +} + +template +inline vec spvQuadSwap(vec value, uint dir) +{ + return (vec)quad_shuffle_xor((vec)value, dir + 1); +} + +fragment main0_out main0() +{ + main0_out out = {}; + uint gl_SubgroupSize = simd_sum(1); + uint gl_SubgroupInvocationID = simd_prefix_exclusive_sum(1); + uint4 gl_SubgroupEqMask = gl_SubgroupInvocationID >= 32 ? uint4(0, (1 << (gl_SubgroupInvocationID - 32)), uint2(0)) : uint4(1 << gl_SubgroupInvocationID, uint3(0)); + uint4 gl_SubgroupGeMask = uint4(insert_bits(0u, 0xFFFFFFFF, min(gl_SubgroupInvocationID, 32u), (uint)max(min((int)gl_SubgroupSize, 32) - (int)gl_SubgroupInvocationID, 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)gl_SubgroupInvocationID - 32, 0), (uint)max((int)gl_SubgroupSize - (int)max(gl_SubgroupInvocationID, 32u), 0)), uint2(0)); + uint4 gl_SubgroupGtMask = uint4(insert_bits(0u, 0xFFFFFFFF, min(gl_SubgroupInvocationID + 1, 32u), (uint)max(min((int)gl_SubgroupSize, 32) - (int)gl_SubgroupInvocationID - 1, 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)gl_SubgroupInvocationID + 1 - 32, 0), (uint)max((int)gl_SubgroupSize - (int)max(gl_SubgroupInvocationID + 1, 32u), 0)), uint2(0)); + uint4 gl_SubgroupLeMask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID + 1 - 32, 0)), uint2(0)); + uint4 gl_SubgroupLtMask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID - 32, 0)), uint2(0)); + out.FragColor = float(gl_SubgroupSize); + out.FragColor = float(gl_SubgroupInvocationID); + bool elected = simd_is_first(); + out.FragColor = float4(gl_SubgroupEqMask).x; + out.FragColor = float4(gl_SubgroupGeMask).x; + out.FragColor = float4(gl_SubgroupGtMask).x; + out.FragColor = float4(gl_SubgroupLeMask).x; + out.FragColor = float4(gl_SubgroupLtMask).x; + float4 broadcasted = spvSubgroupBroadcast(float4(10.0), 8u); + bool2 broadcasted_bool = spvSubgroupBroadcast(bool2(true), 8u); + float3 first = spvSubgroupBroadcastFirst(float3(20.0)); + bool4 first_bool = spvSubgroupBroadcastFirst(bool4(false)); + uint4 ballot_value = spvSubgroupBallot(true); + bool inverse_ballot_value = spvSubgroupBallotBitExtract(ballot_value, gl_SubgroupInvocationID); + bool bit_extracted = spvSubgroupBallotBitExtract(uint4(10u), 8u); + uint bit_count = spvSubgroupBallotBitCount(ballot_value, gl_SubgroupSize); + uint inclusive_bit_count = spvSubgroupBallotInclusiveBitCount(ballot_value, gl_SubgroupInvocationID); + uint exclusive_bit_count = spvSubgroupBallotExclusiveBitCount(ballot_value, gl_SubgroupInvocationID); + uint lsb = spvSubgroupBallotFindLSB(ballot_value, gl_SubgroupSize); + uint msb = spvSubgroupBallotFindMSB(ballot_value, gl_SubgroupSize); + uint shuffled = spvSubgroupShuffle(10u, 8u); + bool shuffled_bool = spvSubgroupShuffle(true, 9u); + uint shuffled_xor = spvSubgroupShuffleXor(30u, 8u); + bool shuffled_xor_bool = spvSubgroupShuffleXor(false, 9u); + uint shuffled_up = spvSubgroupShuffleUp(20u, 4u); + bool shuffled_up_bool = spvSubgroupShuffleUp(true, 4u); + uint shuffled_down = spvSubgroupShuffleDown(20u, 4u); + bool shuffled_down_bool = spvSubgroupShuffleDown(false, 4u); + bool has_all = simd_all(true); + bool has_any = simd_any(true); + bool has_equal = spvSubgroupAllEqual(0); + has_equal = spvSubgroupAllEqual(true); + has_equal = spvSubgroupAllEqual(float3(0.0, 1.0, 2.0)); + has_equal = spvSubgroupAllEqual(bool4(true, true, false, true)); + float4 added = simd_sum(float4(20.0)); + int4 iadded = simd_sum(int4(20)); + float4 multiplied = simd_product(float4(20.0)); + int4 imultiplied = simd_product(int4(20)); + float4 lo = simd_min(float4(20.0)); + float4 hi = simd_max(float4(20.0)); + int4 slo = simd_min(int4(20)); + int4 shi = simd_max(int4(20)); + uint4 ulo = simd_min(uint4(20u)); + uint4 uhi = simd_max(uint4(20u)); + uint4 anded = simd_and(ballot_value); + uint4 ored = simd_or(ballot_value); + uint4 xored = simd_xor(ballot_value); + added = simd_prefix_inclusive_sum(added); + iadded = simd_prefix_inclusive_sum(iadded); + multiplied = simd_prefix_inclusive_product(multiplied); + imultiplied = simd_prefix_inclusive_product(imultiplied); + added = simd_prefix_exclusive_sum(multiplied); + multiplied = simd_prefix_exclusive_product(multiplied); + iadded = simd_prefix_exclusive_sum(imultiplied); + imultiplied = simd_prefix_exclusive_product(imultiplied); + added = quad_sum(added); + multiplied = quad_product(multiplied); + iadded = quad_sum(iadded); + imultiplied = quad_product(imultiplied); + lo = quad_min(lo); + hi = quad_max(hi); + ulo = quad_min(ulo); + uhi = quad_max(uhi); + slo = quad_min(slo); + shi = quad_max(shi); + anded = quad_and(anded); + ored = quad_or(ored); + xored = quad_xor(xored); + float4 swap_horiz = spvQuadSwap(float4(20.0), 0u); + bool4 swap_horiz_bool = spvQuadSwap(bool4(true), 0u); + float4 swap_vertical = spvQuadSwap(float4(20.0), 1u); + bool4 swap_vertical_bool = spvQuadSwap(bool4(true), 1u); + float4 swap_diagonal = spvQuadSwap(float4(20.0), 2u); + bool4 swap_diagonal_bool = spvQuadSwap(bool4(true), 2u); + float4 quad_broadcast0 = spvQuadBroadcast(float4(20.0), 3u); + bool4 quad_broadcast_bool = spvQuadBroadcast(bool4(true), 3u); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl20.ios.framebuffer-fetch.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl20.ios.framebuffer-fetch.frag new file mode 100644 index 0000000..c679848 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl20.ios.framebuffer-fetch.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(1)]]; +}; + +fragment main0_out main0(float4 uInput [[color(1)]]) +{ + main0_out out = {}; + out.FragColor = uInput; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl23.framebuffer-fetch.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl23.framebuffer-fetch.frag new file mode 100644 index 0000000..c679848 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl23.framebuffer-fetch.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(1)]]; +}; + +fragment main0_out main0(float4 uInput [[color(1)]]) +{ + main0_out out = {}; + out.FragColor = uInput; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.ios.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.ios.frag new file mode 100644 index 0000000..950895d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.ios.frag @@ -0,0 +1,37 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 samp3(float4 uS) +{ + return uS; +} + +static inline __attribute__((always_inline)) +float4 samp(float4 uSub) +{ + return uSub + samp3(uSub); +} + +static inline __attribute__((always_inline)) +float4 samp2(float4 uS) +{ + return uS + samp3(uS); +} + +fragment main0_out main0(float4 uSub [[color(0)]]) +{ + main0_out out = {}; + out.FragColor = samp(uSub) + samp2(uSub); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.msl23.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.msl23.frag new file mode 100644 index 0000000..950895d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.msl23.frag @@ -0,0 +1,37 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 samp3(float4 uS) +{ + return uS; +} + +static inline __attribute__((always_inline)) +float4 samp(float4 uSub) +{ + return uSub + samp3(uSub); +} + +static inline __attribute__((always_inline)) +float4 samp2(float4 uS) +{ + return uS + samp3(uS); +} + +fragment main0_out main0(float4 uSub [[color(0)]]) +{ + main0_out out = {}; + out.FragColor = samp(uSub) + samp2(uSub); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-int.swizzle.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-int.swizzle.frag new file mode 100644 index 0000000..ff4b8a9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-int.swizzle.frag @@ -0,0 +1,148 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +// Wrapper function that swizzles texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherSwizzle(const thread Tex& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c) +{ + if (sw) + { + switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF)) + { + case spvSwizzle::none: + break; + case spvSwizzle::zero: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + case spvSwizzle::red: + return t.gather(s, spvForward(params)..., component::x); + case spvSwizzle::green: + return t.gather(s, spvForward(params)..., component::y); + case spvSwizzle::blue: + return t.gather(s, spvForward(params)..., component::z); + case spvSwizzle::alpha: + return t.gather(s, spvForward(params)..., component::w); + } + } + switch (c) + { + case component::x: + return t.gather(s, spvForward(params)..., component::x); + case component::y: + return t.gather(s, spvForward(params)..., component::y); + case component::z: + return t.gather(s, spvForward(params)..., component::z); + case component::w: + return t.gather(s, spvForward(params)..., component::w); + } +} + +fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d tex1d [[texture(0)]], texture2d tex2d [[texture(1)]], texture3d tex3d [[texture(2)]], texturecube texCube [[texture(3)]], texture2d_array tex2dArray [[texture(4)]], texturecube_array texCubeArray [[texture(5)]], texture2d texBuffer [[texture(6)]], sampler tex1dSmplr [[sampler(0)]], sampler tex2dSmplr [[sampler(1)]], sampler tex3dSmplr [[sampler(2)]], sampler texCubeSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]]) +{ + constant uint& tex1dSwzl = spvSwizzleConstants[0]; + constant uint& tex2dSwzl = spvSwizzleConstants[1]; + constant uint& tex3dSwzl = spvSwizzleConstants[2]; + constant uint& texCubeSwzl = spvSwizzleConstants[3]; + constant uint& tex2dArraySwzl = spvSwizzleConstants[4]; + constant uint& texCubeArraySwzl = spvSwizzleConstants[5]; + float4 c = float4(spvTextureSwizzle(tex1d.sample(tex1dSmplr, 0.0), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.sample(tex2dSmplr, float2(0.0)), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.sample(tex3dSmplr, float3(0.0)), tex3dSwzl)); + c = float4(spvTextureSwizzle(texCube.sample(texCubeSmplr, float3(0.0)), texCubeSwzl)); + c = float4(spvTextureSwizzle(tex2dArray.sample(tex2dArraySmplr, float3(0.0).xy, uint(round(float3(0.0).z))), tex2dArraySwzl)); + c = float4(spvTextureSwizzle(texCubeArray.sample(texCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w))), texCubeArraySwzl)); + c = float4(spvTextureSwizzle(tex1d.sample(tex1dSmplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.sample(tex2dSmplr, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.sample(tex3dSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w), tex3dSwzl)); + c = float4(spvTextureSwizzle(tex1d.sample(tex1dSmplr, 0.0), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.sample(tex2dSmplr, float2(0.0), level(0.0)), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.sample(tex3dSmplr, float3(0.0), level(0.0)), tex3dSwzl)); + c = float4(spvTextureSwizzle(texCube.sample(texCubeSmplr, float3(0.0), level(0.0)), texCubeSwzl)); + c = float4(spvTextureSwizzle(tex2dArray.sample(tex2dArraySmplr, float3(0.0).xy, uint(round(float3(0.0).z)), level(0.0)), tex2dArraySwzl)); + c = float4(spvTextureSwizzle(texCubeArray.sample(texCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w)), level(0.0)), texCubeArraySwzl)); + c = float4(spvTextureSwizzle(tex1d.sample(tex1dSmplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.sample(tex2dSmplr, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z, level(0.0)), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.sample(tex3dSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w, level(0.0)), tex3dSwzl)); + c = float4(spvTextureSwizzle(tex1d.read(uint(0)), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.read(uint2(int2(0)), 0), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl)); + c = float4(spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl)); + c = float4(texBuffer.read(spvTexelBufferCoord(0))); + c = float4(spvGatherSwizzle(tex2d, tex2dSmplr, tex2dSwzl, component::x, float2(0.0), int2(0))); + c = float4(spvGatherSwizzle(texCube, texCubeSmplr, texCubeSwzl, component::y, float3(0.0))); + c = float4(spvGatherSwizzle(tex2dArray, tex2dArraySmplr, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0))); + c = float4(spvGatherSwizzle(texCubeArray, texCubeArraySmplr, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w)))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-leaf.swizzle.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-leaf.swizzle.frag new file mode 100644 index 0000000..730728c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-leaf.swizzle.frag @@ -0,0 +1,197 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +// Wrapper function that swizzles texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherSwizzle(const thread Tex& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c) +{ + if (sw) + { + switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF)) + { + case spvSwizzle::none: + break; + case spvSwizzle::zero: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + case spvSwizzle::red: + return t.gather(s, spvForward(params)..., component::x); + case spvSwizzle::green: + return t.gather(s, spvForward(params)..., component::y); + case spvSwizzle::blue: + return t.gather(s, spvForward(params)..., component::z); + case spvSwizzle::alpha: + return t.gather(s, spvForward(params)..., component::w); + } + } + switch (c) + { + case component::x: + return t.gather(s, spvForward(params)..., component::x); + case component::y: + return t.gather(s, spvForward(params)..., component::y); + case component::z: + return t.gather(s, spvForward(params)..., component::z); + case component::w: + return t.gather(s, spvForward(params)..., component::w); + } +} + +// Wrapper function that swizzles depth texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherCompareSwizzle(const thread Tex& t, sampler s, uint sw, Ts... params) +{ + if (sw) + { + switch (spvSwizzle(sw & 0xFF)) + { + case spvSwizzle::none: + case spvSwizzle::red: + break; + case spvSwizzle::zero: + case spvSwizzle::green: + case spvSwizzle::blue: + case spvSwizzle::alpha: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + } + } + return t.gather_compare(s, spvForward(params)...); +} + +static inline __attribute__((always_inline)) +float4 doSwizzle(thread texture1d tex1d, thread const sampler tex1dSmplr, constant uint& tex1dSwzl, thread texture2d tex2d, thread const sampler tex2dSmplr, constant uint& tex2dSwzl, thread texture3d tex3d, thread const sampler tex3dSmplr, constant uint& tex3dSwzl, thread texturecube texCube, thread const sampler texCubeSmplr, constant uint& texCubeSwzl, thread texture2d_array tex2dArray, thread const sampler tex2dArraySmplr, constant uint& tex2dArraySwzl, thread texturecube_array texCubeArray, thread const sampler texCubeArraySmplr, constant uint& texCubeArraySwzl, thread depth2d depth2d, thread const sampler depth2dSmplr, constant uint& depth2dSwzl, thread depthcube depthCube, thread const sampler depthCubeSmplr, constant uint& depthCubeSwzl, thread depth2d_array depth2dArray, thread const sampler depth2dArraySmplr, constant uint& depth2dArraySwzl, thread depthcube_array depthCubeArray, thread const sampler depthCubeArraySmplr, constant uint& depthCubeArraySwzl, thread texture2d texBuffer) +{ + float4 c = spvTextureSwizzle(tex1d.sample(tex1dSmplr, 0.0), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSmplr, float2(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSmplr, float3(0.0)), tex3dSwzl); + c = spvTextureSwizzle(texCube.sample(texCubeSmplr, float3(0.0)), texCubeSwzl); + c = spvTextureSwizzle(tex2dArray.sample(tex2dArraySmplr, float3(0.0).xy, uint(round(float3(0.0).z))), tex2dArraySwzl); + c = spvTextureSwizzle(texCubeArray.sample(texCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w))), texCubeArraySwzl); + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSmplr, float3(0.0, 0.0, 1.0).xy, float3(0.0, 0.0, 1.0).z), depth2dSwzl); + c.x = spvTextureSwizzle(depthCube.sample_compare(depthCubeSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz, float4(0.0, 0.0, 0.0, 1.0).w), depthCubeSwzl); + c.x = spvTextureSwizzle(depth2dArray.sample_compare(depth2dArraySmplr, float4(0.0, 0.0, 0.0, 1.0).xy, uint(round(float4(0.0, 0.0, 0.0, 1.0).z)), float4(0.0, 0.0, 0.0, 1.0).w), depth2dArraySwzl); + c.x = spvTextureSwizzle(depthCubeArray.sample_compare(depthCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0), depthCubeArraySwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSmplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSmplr, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w), tex3dSwzl); + float4 _103 = float4(0.0, 0.0, 1.0, 1.0); + _103.z = float4(0.0, 0.0, 1.0, 1.0).w; + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSmplr, _103.xy / _103.z, float4(0.0, 0.0, 1.0, 1.0).z / _103.z), depth2dSwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSmplr, 0.0), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSmplr, float2(0.0), level(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSmplr, float3(0.0), level(0.0)), tex3dSwzl); + c = spvTextureSwizzle(texCube.sample(texCubeSmplr, float3(0.0), level(0.0)), texCubeSwzl); + c = spvTextureSwizzle(tex2dArray.sample(tex2dArraySmplr, float3(0.0).xy, uint(round(float3(0.0).z)), level(0.0)), tex2dArraySwzl); + c = spvTextureSwizzle(texCubeArray.sample(texCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w)), level(0.0)), texCubeArraySwzl); + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSmplr, float3(0.0, 0.0, 1.0).xy, float3(0.0, 0.0, 1.0).z, level(0.0)), depth2dSwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSmplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSmplr, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z, level(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w, level(0.0)), tex3dSwzl); + float4 _131 = float4(0.0, 0.0, 1.0, 1.0); + _131.z = float4(0.0, 0.0, 1.0, 1.0).w; + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSmplr, _131.xy / _131.z, float4(0.0, 0.0, 1.0, 1.0).z / _131.z, level(0.0)), depth2dSwzl); + c = spvTextureSwizzle(tex1d.read(uint(0)), tex1dSwzl); + c = spvTextureSwizzle(tex2d.read(uint2(int2(0)), 0), tex2dSwzl); + c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl); + c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl); + c = texBuffer.read(spvTexelBufferCoord(0)); + c = spvGatherSwizzle(tex2d, tex2dSmplr, tex2dSwzl, component::x, float2(0.0), int2(0)); + c = spvGatherSwizzle(texCube, texCubeSmplr, texCubeSwzl, component::y, float3(0.0)); + c = spvGatherSwizzle(tex2dArray, tex2dArraySmplr, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0)); + c = spvGatherSwizzle(texCubeArray, texCubeArraySmplr, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w))); + c = spvGatherCompareSwizzle(depth2d, depth2dSmplr, depth2dSwzl, float2(0.0), 1.0); + c = spvGatherCompareSwizzle(depthCube, depthCubeSmplr, depthCubeSwzl, float3(0.0), 1.0); + c = spvGatherCompareSwizzle(depth2dArray, depth2dArraySmplr, depth2dArraySwzl, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0); + c = spvGatherCompareSwizzle(depthCubeArray, depthCubeArraySmplr, depthCubeArraySwzl, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0); + return c; +} + +fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d tex1d [[texture(0)]], texture2d tex2d [[texture(1)]], texture3d tex3d [[texture(2)]], texturecube texCube [[texture(3)]], texture2d_array tex2dArray [[texture(4)]], texturecube_array texCubeArray [[texture(5)]], depth2d depth2d [[texture(6)]], depthcube depthCube [[texture(7)]], depth2d_array depth2dArray [[texture(8)]], depthcube_array depthCubeArray [[texture(9)]], texture2d texBuffer [[texture(10)]], sampler tex1dSmplr [[sampler(0)]], sampler tex2dSmplr [[sampler(1)]], sampler tex3dSmplr [[sampler(2)]], sampler texCubeSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]], sampler depth2dSmplr [[sampler(6)]], sampler depthCubeSmplr [[sampler(7)]], sampler depth2dArraySmplr [[sampler(8)]], sampler depthCubeArraySmplr [[sampler(9)]]) +{ + constant uint& tex1dSwzl = spvSwizzleConstants[0]; + constant uint& tex2dSwzl = spvSwizzleConstants[1]; + constant uint& tex3dSwzl = spvSwizzleConstants[2]; + constant uint& texCubeSwzl = spvSwizzleConstants[3]; + constant uint& tex2dArraySwzl = spvSwizzleConstants[4]; + constant uint& texCubeArraySwzl = spvSwizzleConstants[5]; + constant uint& depth2dSwzl = spvSwizzleConstants[6]; + constant uint& depthCubeSwzl = spvSwizzleConstants[7]; + constant uint& depth2dArraySwzl = spvSwizzleConstants[8]; + constant uint& depthCubeArraySwzl = spvSwizzleConstants[9]; + float4 c = doSwizzle(tex1d, tex1dSmplr, tex1dSwzl, tex2d, tex2dSmplr, tex2dSwzl, tex3d, tex3dSmplr, tex3dSwzl, texCube, texCubeSmplr, texCubeSwzl, tex2dArray, tex2dArraySmplr, tex2dArraySwzl, texCubeArray, texCubeArraySmplr, texCubeArraySwzl, depth2d, depth2dSmplr, depth2dSwzl, depthCube, depthCubeSmplr, depthCubeSwzl, depth2dArray, depth2dArraySmplr, depth2dArraySwzl, depthCubeArray, depthCubeArraySmplr, depthCubeArraySwzl, texBuffer); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-uint.swizzle.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-uint.swizzle.frag new file mode 100644 index 0000000..0ec278f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access-uint.swizzle.frag @@ -0,0 +1,148 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +// Wrapper function that swizzles texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherSwizzle(const thread Tex& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c) +{ + if (sw) + { + switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF)) + { + case spvSwizzle::none: + break; + case spvSwizzle::zero: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + case spvSwizzle::red: + return t.gather(s, spvForward(params)..., component::x); + case spvSwizzle::green: + return t.gather(s, spvForward(params)..., component::y); + case spvSwizzle::blue: + return t.gather(s, spvForward(params)..., component::z); + case spvSwizzle::alpha: + return t.gather(s, spvForward(params)..., component::w); + } + } + switch (c) + { + case component::x: + return t.gather(s, spvForward(params)..., component::x); + case component::y: + return t.gather(s, spvForward(params)..., component::y); + case component::z: + return t.gather(s, spvForward(params)..., component::z); + case component::w: + return t.gather(s, spvForward(params)..., component::w); + } +} + +fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d tex1d [[texture(0)]], texture2d tex2d [[texture(1)]], texture3d tex3d [[texture(2)]], texturecube texCube [[texture(3)]], texture2d_array tex2dArray [[texture(4)]], texturecube_array texCubeArray [[texture(5)]], texture2d texBuffer [[texture(6)]], sampler tex1dSmplr [[sampler(0)]], sampler tex2dSmplr [[sampler(1)]], sampler tex3dSmplr [[sampler(2)]], sampler texCubeSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]]) +{ + constant uint& tex1dSwzl = spvSwizzleConstants[0]; + constant uint& tex2dSwzl = spvSwizzleConstants[1]; + constant uint& tex3dSwzl = spvSwizzleConstants[2]; + constant uint& texCubeSwzl = spvSwizzleConstants[3]; + constant uint& tex2dArraySwzl = spvSwizzleConstants[4]; + constant uint& texCubeArraySwzl = spvSwizzleConstants[5]; + float4 c = float4(spvTextureSwizzle(tex1d.sample(tex1dSmplr, 0.0), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.sample(tex2dSmplr, float2(0.0)), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.sample(tex3dSmplr, float3(0.0)), tex3dSwzl)); + c = float4(spvTextureSwizzle(texCube.sample(texCubeSmplr, float3(0.0)), texCubeSwzl)); + c = float4(spvTextureSwizzle(tex2dArray.sample(tex2dArraySmplr, float3(0.0).xy, uint(round(float3(0.0).z))), tex2dArraySwzl)); + c = float4(spvTextureSwizzle(texCubeArray.sample(texCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w))), texCubeArraySwzl)); + c = float4(spvTextureSwizzle(tex1d.sample(tex1dSmplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.sample(tex2dSmplr, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.sample(tex3dSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w), tex3dSwzl)); + c = float4(spvTextureSwizzle(tex1d.sample(tex1dSmplr, 0.0), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.sample(tex2dSmplr, float2(0.0), level(0.0)), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.sample(tex3dSmplr, float3(0.0), level(0.0)), tex3dSwzl)); + c = float4(spvTextureSwizzle(texCube.sample(texCubeSmplr, float3(0.0), level(0.0)), texCubeSwzl)); + c = float4(spvTextureSwizzle(tex2dArray.sample(tex2dArraySmplr, float3(0.0).xy, uint(round(float3(0.0).z)), level(0.0)), tex2dArraySwzl)); + c = float4(spvTextureSwizzle(texCubeArray.sample(texCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w)), level(0.0)), texCubeArraySwzl)); + c = float4(spvTextureSwizzle(tex1d.sample(tex1dSmplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.sample(tex2dSmplr, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z, level(0.0)), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.sample(tex3dSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w, level(0.0)), tex3dSwzl)); + c = float4(spvTextureSwizzle(tex1d.read(uint(0)), tex1dSwzl)); + c = float4(spvTextureSwizzle(tex2d.read(uint2(int2(0)), 0), tex2dSwzl)); + c = float4(spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl)); + c = float4(spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl)); + c = float4(texBuffer.read(spvTexelBufferCoord(0))); + c = float4(spvGatherSwizzle(tex2d, tex2dSmplr, tex2dSwzl, component::x, float2(0.0), int2(0))); + c = float4(spvGatherSwizzle(texCube, texCubeSmplr, texCubeSwzl, component::y, float3(0.0))); + c = float4(spvGatherSwizzle(tex2dArray, tex2dArraySmplr, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0))); + c = float4(spvGatherSwizzle(texCubeArray, texCubeArraySmplr, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w)))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access.swizzle.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access.swizzle.frag new file mode 100644 index 0000000..c31d5d7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/texture-access.swizzle.frag @@ -0,0 +1,190 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +// Wrapper function that swizzles texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherSwizzle(const thread Tex& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c) +{ + if (sw) + { + switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF)) + { + case spvSwizzle::none: + break; + case spvSwizzle::zero: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + case spvSwizzle::red: + return t.gather(s, spvForward(params)..., component::x); + case spvSwizzle::green: + return t.gather(s, spvForward(params)..., component::y); + case spvSwizzle::blue: + return t.gather(s, spvForward(params)..., component::z); + case spvSwizzle::alpha: + return t.gather(s, spvForward(params)..., component::w); + } + } + switch (c) + { + case component::x: + return t.gather(s, spvForward(params)..., component::x); + case component::y: + return t.gather(s, spvForward(params)..., component::y); + case component::z: + return t.gather(s, spvForward(params)..., component::z); + case component::w: + return t.gather(s, spvForward(params)..., component::w); + } +} + +// Wrapper function that swizzles depth texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherCompareSwizzle(const thread Tex& t, sampler s, uint sw, Ts... params) +{ + if (sw) + { + switch (spvSwizzle(sw & 0xFF)) + { + case spvSwizzle::none: + case spvSwizzle::red: + break; + case spvSwizzle::zero: + case spvSwizzle::green: + case spvSwizzle::blue: + case spvSwizzle::alpha: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + } + } + return t.gather_compare(s, spvForward(params)...); +} + +fragment void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d tex1d [[texture(0)]], texture2d tex2d [[texture(1)]], texture3d tex3d [[texture(2)]], texturecube texCube [[texture(3)]], texture2d_array tex2dArray [[texture(4)]], texturecube_array texCubeArray [[texture(5)]], depth2d depth2d [[texture(6)]], depthcube depthCube [[texture(7)]], depth2d_array depth2dArray [[texture(8)]], depthcube_array depthCubeArray [[texture(9)]], texture2d texBuffer [[texture(10)]], sampler tex1dSmplr [[sampler(0)]], sampler tex2dSmplr [[sampler(1)]], sampler tex3dSmplr [[sampler(2)]], sampler texCubeSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]], sampler depth2dSmplr [[sampler(6)]], sampler depthCubeSmplr [[sampler(7)]], sampler depth2dArraySmplr [[sampler(8)]], sampler depthCubeArraySmplr [[sampler(9)]]) +{ + constant uint& tex1dSwzl = spvSwizzleConstants[0]; + constant uint& tex2dSwzl = spvSwizzleConstants[1]; + constant uint& tex3dSwzl = spvSwizzleConstants[2]; + constant uint& texCubeSwzl = spvSwizzleConstants[3]; + constant uint& tex2dArraySwzl = spvSwizzleConstants[4]; + constant uint& texCubeArraySwzl = spvSwizzleConstants[5]; + constant uint& depth2dSwzl = spvSwizzleConstants[6]; + constant uint& depthCubeSwzl = spvSwizzleConstants[7]; + constant uint& depth2dArraySwzl = spvSwizzleConstants[8]; + constant uint& depthCubeArraySwzl = spvSwizzleConstants[9]; + float4 c = spvTextureSwizzle(tex1d.sample(tex1dSmplr, 0.0), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSmplr, float2(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSmplr, float3(0.0)), tex3dSwzl); + c = spvTextureSwizzle(texCube.sample(texCubeSmplr, float3(0.0)), texCubeSwzl); + c = spvTextureSwizzle(tex2dArray.sample(tex2dArraySmplr, float3(0.0).xy, uint(round(float3(0.0).z))), tex2dArraySwzl); + c = spvTextureSwizzle(texCubeArray.sample(texCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w))), texCubeArraySwzl); + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSmplr, float3(0.0, 0.0, 1.0).xy, float3(0.0, 0.0, 1.0).z), depth2dSwzl); + c.x = spvTextureSwizzle(depthCube.sample_compare(depthCubeSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz, float4(0.0, 0.0, 0.0, 1.0).w), depthCubeSwzl); + c.x = spvTextureSwizzle(depth2dArray.sample_compare(depth2dArraySmplr, float4(0.0, 0.0, 0.0, 1.0).xy, uint(round(float4(0.0, 0.0, 0.0, 1.0).z)), float4(0.0, 0.0, 0.0, 1.0).w), depth2dArraySwzl); + c.x = spvTextureSwizzle(depthCubeArray.sample_compare(depthCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0), depthCubeArraySwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSmplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSmplr, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w), tex3dSwzl); + float4 _100 = float4(0.0, 0.0, 1.0, 1.0); + _100.z = float4(0.0, 0.0, 1.0, 1.0).w; + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSmplr, _100.xy / _100.z, float4(0.0, 0.0, 1.0, 1.0).z / _100.z), depth2dSwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSmplr, 0.0), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSmplr, float2(0.0), level(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSmplr, float3(0.0), level(0.0)), tex3dSwzl); + c = spvTextureSwizzle(texCube.sample(texCubeSmplr, float3(0.0), level(0.0)), texCubeSwzl); + c = spvTextureSwizzle(tex2dArray.sample(tex2dArraySmplr, float3(0.0).xy, uint(round(float3(0.0).z)), level(0.0)), tex2dArraySwzl); + c = spvTextureSwizzle(texCubeArray.sample(texCubeArraySmplr, float4(0.0).xyz, uint(round(float4(0.0).w)), level(0.0)), texCubeArraySwzl); + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSmplr, float3(0.0, 0.0, 1.0).xy, float3(0.0, 0.0, 1.0).z, level(0.0)), depth2dSwzl); + c = spvTextureSwizzle(tex1d.sample(tex1dSmplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), tex1dSwzl); + c = spvTextureSwizzle(tex2d.sample(tex2dSmplr, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z, level(0.0)), tex2dSwzl); + c = spvTextureSwizzle(tex3d.sample(tex3dSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w, level(0.0)), tex3dSwzl); + float4 _128 = float4(0.0, 0.0, 1.0, 1.0); + _128.z = float4(0.0, 0.0, 1.0, 1.0).w; + c.x = spvTextureSwizzle(depth2d.sample_compare(depth2dSmplr, _128.xy / _128.z, float4(0.0, 0.0, 1.0, 1.0).z / _128.z, level(0.0)), depth2dSwzl); + c = spvTextureSwizzle(tex1d.read(uint(0)), tex1dSwzl); + c = spvTextureSwizzle(tex2d.read(uint2(int2(0)), 0), tex2dSwzl); + c = spvTextureSwizzle(tex3d.read(uint3(int3(0)), 0), tex3dSwzl); + c = spvTextureSwizzle(tex2dArray.read(uint2(int3(0).xy), uint(int3(0).z), 0), tex2dArraySwzl); + c = texBuffer.read(spvTexelBufferCoord(0)); + c = spvGatherSwizzle(tex2d, tex2dSmplr, tex2dSwzl, component::x, float2(0.0), int2(0)); + c = spvGatherSwizzle(texCube, texCubeSmplr, texCubeSwzl, component::y, float3(0.0)); + c = spvGatherSwizzle(tex2dArray, tex2dArraySmplr, tex2dArraySwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0)); + c = spvGatherSwizzle(texCubeArray, texCubeArraySmplr, texCubeArraySwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w))); + c = spvGatherCompareSwizzle(depth2d, depth2dSmplr, depth2dSwzl, float2(0.0), 1.0); + c = spvGatherCompareSwizzle(depthCube, depthCubeSmplr, depthCubeSwzl, float3(0.0), 1.0); + c = spvGatherCompareSwizzle(depth2dArray, depth2dArraySmplr, depth2dArraySwzl, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0); + c = spvGatherCompareSwizzle(depthCubeArray, depthCubeArraySmplr, depthCubeArraySwzl, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.argument.msl2.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.argument.msl2.frag new file mode 100644 index 0000000..7c60182 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.argument.msl2.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + float4 v; +}; + +struct UBO +{ + Foo foo; +}; + +struct spvDescriptorSetBuffer0 +{ + constant UBO* ubos [[id(0)]][2]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = spvDescriptorSet0.ubos[1]->foo.v; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.frag new file mode 100644 index 0000000..0b1ca91 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.frag @@ -0,0 +1,33 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + float4 v; +}; + +struct UBO +{ + Foo foo; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant UBO* ubos_0 [[buffer(0)]], constant UBO* ubos_1 [[buffer(1)]]) +{ + constant UBO* ubos[] = + { + ubos_0, + ubos_1, + }; + + main0_out out = {}; + out.FragColor = ubos[1]->foo.v; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-offset-out-of-order.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-offset-out-of-order.frag new file mode 100644 index 0000000..ce3291a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/ubo-offset-out-of-order.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 v; + float4x4 m; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _13 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = (_13.m * in.vColor) + _13.v; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/variables.zero-initialize.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/variables.zero-initialize.frag new file mode 100644 index 0000000..0720087 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/frag/variables.zero-initialize.frag @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + int a; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + int uninit_function_int = {}; + int uninit_int = {}; + int4 uninit_vector = {}; + float4x4 uninit_matrix = {}; + Foo uninit_foo = {}; + if (in.vColor.x > 10.0) + { + uninit_function_int = 10; + } + else + { + uninit_function_int = 20; + } + out.FragColor = in.vColor; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/array-of-vec3.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/array-of-vec3.comp new file mode 100644 index 0000000..0dd52ab --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/array-of-vec3.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + packed_float3 v[16]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _13 [[buffer(0)]]) +{ + _13.v[1] = float3(_13.v[0]); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/array-of-vec4.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/array-of-vec4.comp new file mode 100644 index 0000000..025cd42 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/array-of-vec4.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 v[16]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _13 [[buffer(0)]]) +{ + _13.v[1] = _13.v[0]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/isolated-scalar-access.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/isolated-scalar-access.comp new file mode 100644 index 0000000..f1a3719 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/isolated-scalar-access.comp @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 v; + float4x4 cm; + float4x4 rm; + packed_float3 v3; + float f; +}; + +kernel void main0(device SSBO& _12 [[buffer(0)]]) +{ + threadgroup float4 shared_vec4; + threadgroup float3 shared_vec3; + ((device float*)&_12.v)[0u] = 10.0; + _12.v3[1u] = 40.0; + ((device float*)&_12.cm[1])[2u] = 20.0; + ((device float*)&_12.rm[1u])[3] = 30.0; + ((threadgroup float*)&shared_vec4)[2u] = 40.0; + ((threadgroup float*)&shared_vec3)[1u] = 1.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/load-store-col-rows.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/load-store-col-rows.comp new file mode 100644 index 0000000..020ccae --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/load-store-col-rows.comp @@ -0,0 +1,76 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +typedef packed_float3 packed_float2x3[2]; +typedef packed_float3 packed_rm_float3x2[2]; + +struct SSBO1 +{ + float2x4 a; + float2x4 a2; +}; + +struct SSBO2 +{ + packed_float2x3 b; + packed_rm_float3x2 b2; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_column(device SSBO1& v_21) +{ + float2 u = v_21.a[0].xy; + float2 v = v_21.a[1].xy; + u += v; + (device float2&)v_21.a[0] = u; + (device float2&)v_21.a[1] = v; +} + +static inline __attribute__((always_inline)) +void load_store_row(device SSBO1& v_21) +{ + float2 u = float2(v_21.a2[0][0], v_21.a2[1][0]); + float2 v = float2(v_21.a2[0][1], v_21.a2[1][1]); + u += v; + ((device float*)&v_21.a2[0])[0] = u.x; + ((device float*)&v_21.a2[1])[0] = u.y; + ((device float*)&v_21.a2[0])[1] = v.x; + ((device float*)&v_21.a2[1])[1] = v.y; +} + +static inline __attribute__((always_inline)) +void load_store_packed_column(device SSBO2& v_58) +{ + float3 u = float3(v_58.b[0]); + float3 v = float3(v_58.b[1]); + u += v; + v_58.b[0] = u; + v_58.b[1] = v; +} + +static inline __attribute__((always_inline)) +void load_store_packed_row(device SSBO2& v_58) +{ + float2 u = float2(v_58.b2[0][0], v_58.b2[1][0]); + float2 v = float2(v_58.b2[0][1], v_58.b2[1][1]); + u += v; + ((device float*)&v_58.b2[0])[0] = u.x; + ((device float*)&v_58.b2[1])[0] = u.y; + ((device float*)&v_58.b2[0])[1] = v.x; + ((device float*)&v_58.b2[1])[1] = v.y; +} + +kernel void main0(device SSBO1& v_21 [[buffer(0)]], device SSBO2& v_58 [[buffer(1)]]) +{ + load_store_column(v_21); + load_store_row(v_21); + load_store_packed_column(v_58); + load_store_packed_row(v_58); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-scalar.comp new file mode 100644 index 0000000..a00a679 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-scalar.comp @@ -0,0 +1,86 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float2x2 col_major0; + float2x2 col_major1; +}; + +struct SSBORow +{ + float2x2 row_major0; + float2x2 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x2 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x2 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-std140.comp new file mode 100644 index 0000000..fd81f3a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-std140.comp @@ -0,0 +1,92 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float2x4 col_major0; + float2x4 col_major1; +}; + +struct SSBORow +{ + float2x4 row_major0; + float2x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x2 loaded = float2x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy); + (device float2&)v_29.col_major1[0] = loaded[0]; + (device float2&)v_29.col_major1[1] = loaded[1]; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x2 loaded = transpose(float2x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy)); + (device float2&)v_41.row_major0[0] = float2(loaded[0][0], loaded[1][0]); + (device float2&)v_41.row_major0[1] = float2(loaded[0][1], loaded[1][1]); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + (device float2&)v_29.col_major0[0] = float2x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy)[0]; + (device float2&)v_29.col_major0[1] = float2x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy)[1]; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_41.row_major0[0] = float2(float2x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy)[0][0], float2x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy)[1][0]); + (device float2&)v_41.row_major0[1] = float2(float2x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy)[0][1], float2x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy)[1][1]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_29.col_major0[0] = float2(float2x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy)[0][0], float2x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy)[1][0]); + (device float2&)v_29.col_major0[1] = float2(float2x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy)[0][1], float2x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy)[1][1]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + (device float2&)v_41.row_major0[0] = float2x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy)[0]; + (device float2&)v_41.row_major0[1] = float2x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy)[1]; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + ((device float*)&v_41.row_major0[0])[1] = v_29.col_major0[1].x; + ((device float*)&v_41.row_major0[1])[1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-std430.comp new file mode 100644 index 0000000..a00a679 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x2-std430.comp @@ -0,0 +1,86 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float2x2 col_major0; + float2x2 col_major1; +}; + +struct SSBORow +{ + float2x2 row_major0; + float2x2 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x2 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x2 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-scalar.comp new file mode 100644 index 0000000..963ec39 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-scalar.comp @@ -0,0 +1,92 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +typedef packed_float3 packed_float2x3[2]; + +struct SSBOCol +{ + packed_float2x3 col_major0; + packed_float2x3 col_major1; +}; + +struct SSBORow +{ + float3x2 row_major0; + float3x2 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x3 loaded = float2x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1])); + v_29.col_major1[0] = loaded[0]; + v_29.col_major1[1] = loaded[1]; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x3 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0[0] = float2x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]))[0]; + v_29.col_major0[1] = float2x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]))[1]; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(float2x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]))); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[0] = float3(v_41.row_major0[0][0], v_41.row_major0[1][0], v_41.row_major0[2][0]); + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + v_41.row_major0[0][1] = v_29.col_major0[1][0]; + v_41.row_major0[1][1] = v_29.col_major0[1][1]; + v_41.row_major0[2][1] = v_29.col_major0[1][2]; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[0][1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = v_29.col_major0[0][1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-std140.comp new file mode 100644 index 0000000..d20a4a7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-std140.comp @@ -0,0 +1,93 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float2x3 col_major0; + float2x3 col_major1; +}; + +struct SSBORow +{ + float3x4 row_major0; + float3x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x3 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x3 loaded = transpose(float3x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy, v_41.row_major0[2].xy)); + (device float2&)v_41.row_major0[0] = float2(loaded[0][0], loaded[1][0]); + (device float2&)v_41.row_major0[1] = float2(loaded[0][1], loaded[1][1]); + (device float2&)v_41.row_major0[2] = float2(loaded[0][2], loaded[1][2]); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_41.row_major0[0] = float2(v_29.col_major0[0][0], v_29.col_major0[1][0]); + (device float2&)v_41.row_major0[1] = float2(v_29.col_major0[0][1], v_29.col_major0[1][1]); + (device float2&)v_41.row_major0[2] = float2(v_29.col_major0[0][2], v_29.col_major0[1][2]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(float3x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy, v_41.row_major0[2].xy)); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + (device float2&)v_41.row_major0[0] = float3x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy, v_41.row_major1[2].xy)[0]; + (device float2&)v_41.row_major0[1] = float3x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy, v_41.row_major1[2].xy)[1]; + (device float2&)v_41.row_major0[2] = float3x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy, v_41.row_major1[2].xy)[2]; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + ((device float*)&v_41.row_major0[0])[1] = v_29.col_major0[1].x; + ((device float*)&v_41.row_major0[1])[1] = v_29.col_major0[1].y; + ((device float*)&v_41.row_major0[2])[1] = v_29.col_major0[1].z; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-std430.comp new file mode 100644 index 0000000..240111b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x3-std430.comp @@ -0,0 +1,87 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float2x3 col_major0; + float2x3 col_major1; +}; + +struct SSBORow +{ + float3x2 row_major0; + float3x2 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x3 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x3 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-scalar.comp new file mode 100644 index 0000000..d9e8cca --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-scalar.comp @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float2x4 col_major0; + float2x4 col_major1; +}; + +struct SSBORow +{ + float4x2 row_major0; + float4x2 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x4 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; + v_41.row_major0[3][1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-std140.comp new file mode 100644 index 0000000..e1adc22 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-std140.comp @@ -0,0 +1,97 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float2x4 col_major0; + float2x4 col_major1; +}; + +struct SSBORow +{ + float4x4 row_major0; + float4x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x4 loaded = transpose(float4x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy, v_41.row_major0[2].xy, v_41.row_major0[3].xy)); + (device float2&)v_41.row_major0[0] = float2(loaded[0][0], loaded[1][0]); + (device float2&)v_41.row_major0[1] = float2(loaded[0][1], loaded[1][1]); + (device float2&)v_41.row_major0[2] = float2(loaded[0][2], loaded[1][2]); + (device float2&)v_41.row_major0[3] = float2(loaded[0][3], loaded[1][3]); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_41.row_major0[0] = float2(v_29.col_major0[0][0], v_29.col_major0[1][0]); + (device float2&)v_41.row_major0[1] = float2(v_29.col_major0[0][1], v_29.col_major0[1][1]); + (device float2&)v_41.row_major0[2] = float2(v_29.col_major0[0][2], v_29.col_major0[1][2]); + (device float2&)v_41.row_major0[3] = float2(v_29.col_major0[0][3], v_29.col_major0[1][3]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(float4x2(v_41.row_major0[0].xy, v_41.row_major0[1].xy, v_41.row_major0[2].xy, v_41.row_major0[3].xy)); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + (device float2&)v_41.row_major0[0] = float4x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy, v_41.row_major1[2].xy, v_41.row_major1[3].xy)[0]; + (device float2&)v_41.row_major0[1] = float4x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy, v_41.row_major1[2].xy, v_41.row_major1[3].xy)[1]; + (device float2&)v_41.row_major0[2] = float4x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy, v_41.row_major1[2].xy, v_41.row_major1[3].xy)[2]; + (device float2&)v_41.row_major0[3] = float4x2(v_41.row_major1[0].xy, v_41.row_major1[1].xy, v_41.row_major1[2].xy, v_41.row_major1[3].xy)[3]; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + ((device float*)&v_41.row_major0[0])[1] = v_29.col_major0[1].x; + ((device float*)&v_41.row_major0[1])[1] = v_29.col_major0[1].y; + ((device float*)&v_41.row_major0[2])[1] = v_29.col_major0[1].z; + ((device float*)&v_41.row_major0[3])[1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-std430.comp new file mode 100644 index 0000000..d9e8cca --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-2x4-std430.comp @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float2x4 col_major0; + float2x4 col_major1; +}; + +struct SSBORow +{ + float4x2 row_major0; + float4x2 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float2x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float2x4 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; + v_41.row_major0[3][1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-scalar.comp new file mode 100644 index 0000000..86be094 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-scalar.comp @@ -0,0 +1,91 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +typedef packed_float3 packed_rm_float3x2[2]; + +struct SSBOCol +{ + float3x2 col_major0; + float3x2 col_major1; +}; + +struct SSBORow +{ + packed_rm_float3x2 row_major0; + packed_rm_float3x2 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x2 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x2 loaded = transpose(float2x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]))); + v_41.row_major0[0] = float3(loaded[0][0], loaded[1][0], loaded[2][0]); + v_41.row_major0[1] = float3(loaded[0][1], loaded[1][1], loaded[2][1]); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0[0] = float3(v_29.col_major0[0][0], v_29.col_major0[1][0], v_29.col_major0[2][0]); + v_41.row_major0[1] = float3(v_29.col_major0[0][1], v_29.col_major0[1][1], v_29.col_major0[2][1]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(float2x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]))); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0[0] = float2x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]))[0]; + v_41.row_major0[1] = float2x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]))[1]; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + ((device float*)&v_41.row_major0[0])[1] = v_29.col_major0[1].x; + ((device float*)&v_41.row_major0[1])[1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = v_41.row_major0[1u][0]; + v_41.row_major0[1u][0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-std140.comp new file mode 100644 index 0000000..9144272 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-std140.comp @@ -0,0 +1,92 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float3x4 col_major0; + float3x4 col_major1; +}; + +struct SSBORow +{ + float2x3 row_major0; + float2x3 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x2 loaded = float3x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy, v_29.col_major0[2].xy); + (device float2&)v_29.col_major1[0] = loaded[0]; + (device float2&)v_29.col_major1[1] = loaded[1]; + (device float2&)v_29.col_major1[2] = loaded[2]; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x2 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + (device float2&)v_29.col_major0[0] = float3x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy, v_29.col_major1[2].xy)[0]; + (device float2&)v_29.col_major0[1] = float3x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy, v_29.col_major1[2].xy)[1]; + (device float2&)v_29.col_major0[2] = float3x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy, v_29.col_major1[2].xy)[2]; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(float3x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy, v_29.col_major0[2].xy)); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_29.col_major0[0] = float2(v_41.row_major0[0][0], v_41.row_major0[1][0]); + (device float2&)v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + (device float2&)v_29.col_major0[2] = float2(v_41.row_major0[0][2], v_41.row_major0[1][2]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-std430.comp new file mode 100644 index 0000000..3266e6c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x2-std430.comp @@ -0,0 +1,86 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float3x2 col_major0; + float3x2 col_major1; +}; + +struct SSBORow +{ + float2x3 row_major0; + float2x3 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x2 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x2 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-scalar.comp new file mode 100644 index 0000000..593a013 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-scalar.comp @@ -0,0 +1,102 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +typedef packed_float3 packed_float3x3[3]; +typedef packed_float3 packed_rm_float3x3[3]; + +struct SSBOCol +{ + packed_float3x3 col_major0; + packed_float3x3 col_major1; +}; + +struct SSBORow +{ + packed_rm_float3x3 row_major0; + packed_rm_float3x3 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x3 loaded = float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2])); + v_29.col_major1[0] = loaded[0]; + v_29.col_major1[1] = loaded[1]; + v_29.col_major1[2] = loaded[2]; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x3 loaded = transpose(float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))); + v_41.row_major0[0] = float3(loaded[0][0], loaded[1][0], loaded[2][0]); + v_41.row_major0[1] = float3(loaded[0][1], loaded[1][1], loaded[2][1]); + v_41.row_major0[2] = float3(loaded[0][2], loaded[1][2], loaded[2][2]); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0[0] = float3x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]), float3(v_29.col_major1[2]))[0]; + v_29.col_major0[1] = float3x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]), float3(v_29.col_major1[2]))[1]; + v_29.col_major0[2] = float3x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]), float3(v_29.col_major1[2]))[2]; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0[0] = float3(float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[0][0], float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[1][0], float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[2][0]); + v_41.row_major0[1] = float3(float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[0][1], float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[1][1], float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[2][1]); + v_41.row_major0[2] = float3(float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[0][2], float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[1][2], float3x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]))[2][2]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[0] = float3(float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[0][0], float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[1][0], float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[2][0]); + v_29.col_major0[1] = float3(float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[0][1], float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[1][1], float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[2][1]); + v_29.col_major0[2] = float3(float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[0][2], float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[1][2], float3x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]))[2][2]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0[0] = float3x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]), float3(v_41.row_major1[2]))[0]; + v_41.row_major0[1] = float3x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]), float3(v_41.row_major1[2]))[1]; + v_41.row_major0[2] = float3x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]), float3(v_41.row_major1[2]))[2]; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + ((device float*)&v_41.row_major0[0])[1] = v_29.col_major0[1][0]; + ((device float*)&v_41.row_major0[1])[1] = v_29.col_major0[1][1]; + ((device float*)&v_41.row_major0[2])[1] = v_29.col_major0[1][2]; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[0][1u] = v_41.row_major0[1u][0]; + v_41.row_major0[1u][0] = v_29.col_major0[0][1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-std140.comp new file mode 100644 index 0000000..e2d4adb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-std140.comp @@ -0,0 +1,87 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float3x3 col_major0; + float3x3 col_major1; +}; + +struct SSBORow +{ + float3x3 row_major0; + float3x3 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x3 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x3 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-std430.comp new file mode 100644 index 0000000..e2d4adb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x3-std430.comp @@ -0,0 +1,87 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float3x3 col_major0; + float3x3 col_major1; +}; + +struct SSBORow +{ + float3x3 row_major0; + float3x3 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x3 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x3 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-scalar.comp new file mode 100644 index 0000000..360ef46 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-scalar.comp @@ -0,0 +1,99 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +typedef packed_float3 packed_rm_float3x4[4]; + +struct SSBOCol +{ + float3x4 col_major0; + float3x4 col_major1; +}; + +struct SSBORow +{ + packed_rm_float3x4 row_major0; + packed_rm_float3x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x4 loaded = transpose(float4x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]), float3(v_41.row_major0[3]))); + v_41.row_major0[0] = float3(loaded[0][0], loaded[1][0], loaded[2][0]); + v_41.row_major0[1] = float3(loaded[0][1], loaded[1][1], loaded[2][1]); + v_41.row_major0[2] = float3(loaded[0][2], loaded[1][2], loaded[2][2]); + v_41.row_major0[3] = float3(loaded[0][3], loaded[1][3], loaded[2][3]); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0[0] = float3(v_29.col_major0[0][0], v_29.col_major0[1][0], v_29.col_major0[2][0]); + v_41.row_major0[1] = float3(v_29.col_major0[0][1], v_29.col_major0[1][1], v_29.col_major0[2][1]); + v_41.row_major0[2] = float3(v_29.col_major0[0][2], v_29.col_major0[1][2], v_29.col_major0[2][2]); + v_41.row_major0[3] = float3(v_29.col_major0[0][3], v_29.col_major0[1][3], v_29.col_major0[2][3]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(float4x3(float3(v_41.row_major0[0]), float3(v_41.row_major0[1]), float3(v_41.row_major0[2]), float3(v_41.row_major0[3]))); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0[0] = float4x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]), float3(v_41.row_major1[2]), float3(v_41.row_major1[3]))[0]; + v_41.row_major0[1] = float4x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]), float3(v_41.row_major1[2]), float3(v_41.row_major1[3]))[1]; + v_41.row_major0[2] = float4x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]), float3(v_41.row_major1[2]), float3(v_41.row_major1[3]))[2]; + v_41.row_major0[3] = float4x3(float3(v_41.row_major1[0]), float3(v_41.row_major1[1]), float3(v_41.row_major1[2]), float3(v_41.row_major1[3]))[3]; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + ((device float*)&v_41.row_major0[0])[1] = v_29.col_major0[1].x; + ((device float*)&v_41.row_major0[1])[1] = v_29.col_major0[1].y; + ((device float*)&v_41.row_major0[2])[1] = v_29.col_major0[1].z; + ((device float*)&v_41.row_major0[3])[1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = v_41.row_major0[1u][0]; + v_41.row_major0[1u][0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-std140.comp new file mode 100644 index 0000000..f18917d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-std140.comp @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float3x4 col_major0; + float3x4 col_major1; +}; + +struct SSBORow +{ + float4x3 row_major0; + float4x3 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x4 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; + v_41.row_major0[3][1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-std430.comp new file mode 100644 index 0000000..f18917d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-3x4-std430.comp @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float3x4 col_major0; + float3x4 col_major1; +}; + +struct SSBORow +{ + float4x3 row_major0; + float4x3 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float3x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float3x4 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; + v_41.row_major0[3][1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-scalar.comp new file mode 100644 index 0000000..d98613e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-scalar.comp @@ -0,0 +1,86 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float4x2 col_major0; + float4x2 col_major1; +}; + +struct SSBORow +{ + float2x4 row_major0; + float2x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x2 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x2 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-std140.comp new file mode 100644 index 0000000..6c231cd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-std140.comp @@ -0,0 +1,95 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float4x4 col_major0; + float4x4 col_major1; +}; + +struct SSBORow +{ + float2x4 row_major0; + float2x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x2 loaded = float4x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy, v_29.col_major0[2].xy, v_29.col_major0[3].xy); + (device float2&)v_29.col_major1[0] = loaded[0]; + (device float2&)v_29.col_major1[1] = loaded[1]; + (device float2&)v_29.col_major1[2] = loaded[2]; + (device float2&)v_29.col_major1[3] = loaded[3]; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x2 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + (device float2&)v_29.col_major0[0] = float4x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy, v_29.col_major1[2].xy, v_29.col_major1[3].xy)[0]; + (device float2&)v_29.col_major0[1] = float4x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy, v_29.col_major1[2].xy, v_29.col_major1[3].xy)[1]; + (device float2&)v_29.col_major0[2] = float4x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy, v_29.col_major1[2].xy, v_29.col_major1[3].xy)[2]; + (device float2&)v_29.col_major0[3] = float4x2(v_29.col_major1[0].xy, v_29.col_major1[1].xy, v_29.col_major1[2].xy, v_29.col_major1[3].xy)[3]; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(float4x2(v_29.col_major0[0].xy, v_29.col_major0[1].xy, v_29.col_major0[2].xy, v_29.col_major0[3].xy)); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_29.col_major0[0] = float2(v_41.row_major0[0][0], v_41.row_major0[1][0]); + (device float2&)v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + (device float2&)v_29.col_major0[2] = float2(v_41.row_major0[0][2], v_41.row_major0[1][2]); + (device float2&)v_29.col_major0[3] = float2(v_41.row_major0[0][3], v_41.row_major0[1][3]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + (device float2&)v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-std430.comp new file mode 100644 index 0000000..d98613e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x2-std430.comp @@ -0,0 +1,86 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float4x2 col_major0; + float4x2 col_major1; +}; + +struct SSBORow +{ + float2x4 row_major0; + float2x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x2 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x2 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float2(v_41.row_major0[0][1], v_41.row_major0[1][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-scalar.comp new file mode 100644 index 0000000..7a156f8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-scalar.comp @@ -0,0 +1,98 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +typedef packed_float3 packed_float4x3[4]; + +struct SSBOCol +{ + packed_float4x3 col_major0; + packed_float4x3 col_major1; +}; + +struct SSBORow +{ + float3x4 row_major0; + float3x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x3 loaded = float4x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]), float3(v_29.col_major0[3])); + v_29.col_major1[0] = loaded[0]; + v_29.col_major1[1] = loaded[1]; + v_29.col_major1[2] = loaded[2]; + v_29.col_major1[3] = loaded[3]; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x3 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0[0] = float4x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]), float3(v_29.col_major1[2]), float3(v_29.col_major1[3]))[0]; + v_29.col_major0[1] = float4x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]), float3(v_29.col_major1[2]), float3(v_29.col_major1[3]))[1]; + v_29.col_major0[2] = float4x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]), float3(v_29.col_major1[2]), float3(v_29.col_major1[3]))[2]; + v_29.col_major0[3] = float4x3(float3(v_29.col_major1[0]), float3(v_29.col_major1[1]), float3(v_29.col_major1[2]), float3(v_29.col_major1[3]))[3]; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(float4x3(float3(v_29.col_major0[0]), float3(v_29.col_major0[1]), float3(v_29.col_major0[2]), float3(v_29.col_major0[3]))); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[0] = float3(v_41.row_major0[0][0], v_41.row_major0[1][0], v_41.row_major0[2][0]); + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + v_29.col_major0[2] = float3(v_41.row_major0[0][2], v_41.row_major0[1][2], v_41.row_major0[2][2]); + v_29.col_major0[3] = float3(v_41.row_major0[0][3], v_41.row_major0[1][3], v_41.row_major0[2][3]); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + v_41.row_major0[0][1] = v_29.col_major0[1][0]; + v_41.row_major0[1][1] = v_29.col_major0[1][1]; + v_41.row_major0[2][1] = v_29.col_major0[1][2]; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[0][1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = v_29.col_major0[0][1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-std140.comp new file mode 100644 index 0000000..0964f84 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-std140.comp @@ -0,0 +1,87 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float4x3 col_major0; + float4x3 col_major1; +}; + +struct SSBORow +{ + float3x4 row_major0; + float3x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x3 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x3 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-std430.comp new file mode 100644 index 0000000..0964f84 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x3-std430.comp @@ -0,0 +1,87 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float4x3 col_major0; + float4x3 col_major1; +}; + +struct SSBORow +{ + float3x4 row_major0; + float3x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x3 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x3 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float3(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-scalar.comp new file mode 100644 index 0000000..865cc19 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-scalar.comp @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float4x4 col_major0; + float4x4 col_major1; +}; + +struct SSBORow +{ + float4x4 row_major0; + float4x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x4 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; + v_41.row_major0[3][1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-std140.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-std140.comp new file mode 100644 index 0000000..865cc19 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-std140.comp @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float4x4 col_major0; + float4x4 col_major1; +}; + +struct SSBORow +{ + float4x4 row_major0; + float4x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x4 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; + v_41.row_major0[3][1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-std430.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-std430.comp new file mode 100644 index 0000000..865cc19 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-4x4-std430.comp @@ -0,0 +1,88 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBOCol +{ + float4x4 col_major0; + float4x4 col_major1; +}; + +struct SSBORow +{ + float4x4 row_major0; + float4x4 row_major1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void load_store_to_variable_col_major(device SSBOCol& v_29) +{ + float4x4 loaded = v_29.col_major0; + v_29.col_major1 = loaded; +} + +static inline __attribute__((always_inline)) +void load_store_to_variable_row_major(device SSBORow& v_41) +{ + float4x4 loaded = transpose(v_41.row_major0); + v_41.row_major0 = transpose(loaded); +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_col_major(device SSBOCol& v_29) +{ + v_29.col_major0 = v_29.col_major1; +} + +static inline __attribute__((always_inline)) +void copy_col_major_to_row_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_41.row_major0 = transpose(v_29.col_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_col_major(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0 = transpose(v_41.row_major0); +} + +static inline __attribute__((always_inline)) +void copy_row_major_to_row_major(device SSBORow& v_41) +{ + v_41.row_major0 = v_41.row_major1; +} + +static inline __attribute__((always_inline)) +void copy_columns(device SSBOCol& v_29, device SSBORow& v_41) +{ + v_29.col_major0[1] = float4(v_41.row_major0[0][1], v_41.row_major0[1][1], v_41.row_major0[2][1], v_41.row_major0[3][1]); + v_41.row_major0[0][1] = v_29.col_major0[1].x; + v_41.row_major0[1][1] = v_29.col_major0[1].y; + v_41.row_major0[2][1] = v_29.col_major0[1].z; + v_41.row_major0[3][1] = v_29.col_major0[1].w; +} + +static inline __attribute__((always_inline)) +void copy_elements(device SSBOCol& v_29, device SSBORow& v_41) +{ + ((device float*)&v_29.col_major0[0])[1u] = ((device float*)&v_41.row_major0[1u])[0]; + ((device float*)&v_41.row_major0[1u])[0] = ((device float*)&v_29.col_major0[0])[1u]; +} + +kernel void main0(device SSBOCol& v_29 [[buffer(0)]], device SSBORow& v_41 [[buffer(1)]]) +{ + load_store_to_variable_col_major(v_29); + load_store_to_variable_row_major(v_41); + copy_col_major_to_col_major(v_29); + copy_col_major_to_row_major(v_29, v_41); + copy_row_major_to_col_major(v_29, v_41); + copy_row_major_to_row_major(v_41); + copy_columns(v_29, v_41); + copy_elements(v_29, v_41); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-row-major.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-row-major.comp new file mode 100644 index 0000000..2384e36 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-row-major.comp @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float3x3 m0; + float3x3 m1; + float3 v0; + float3 v1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _11 [[buffer(0)]]) +{ + _11.v0 = _11.v1 * (_11.m1 * _11.m0); + _11.v0 = (_11.v1 * _11.m1) * _11.m0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major-2.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major-2.comp new file mode 100644 index 0000000..3fb36e0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major-2.comp @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +typedef packed_float3 packed_float3x3[3]; + +struct SSBO +{ + packed_float3x3 m0; + packed_float3x3 m1; + packed_float3 v0; + packed_float3 v1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _11 [[buffer(0)]]) +{ + _11.v0 = (float3x3(float3(_11.m0[0]), float3(_11.m0[1]), float3(_11.m0[2])) * float3x3(float3(_11.m1[0]), float3(_11.m1[1]), float3(_11.m1[2]))) * float3(_11.v1); + _11.v0 = float3x3(float3(_11.m0[0]), float3(_11.m0[1]), float3(_11.m0[2])) * (float3x3(float3(_11.m1[0]), float3(_11.m1[1]), float3(_11.m1[2])) * float3(_11.v1)); + _11.v0 = (float3(_11.v1) * float3x3(float3(_11.m0[0]), float3(_11.m0[1]), float3(_11.m0[2]))) * float3x3(float3(_11.m1[0]), float3(_11.m1[1]), float3(_11.m1[2])); + _11.v0 = float3(_11.v1) * (float3x3(float3(_11.m0[0]), float3(_11.m0[1]), float3(_11.m0[2])) * float3x3(float3(_11.m1[0]), float3(_11.m1[1]), float3(_11.m1[2]))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major.comp new file mode 100644 index 0000000..40f0088 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major.comp @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float2x4 m0; + float2x4 m1; + float2 v0; + float2 v1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _11 [[buffer(0)]]) +{ + _11.v0 = (float2x2(_11.m0[0].xy, _11.m0[1].xy) * float2x2(_11.m1[0].xy, _11.m1[1].xy)) * _11.v1; + _11.v0 = float2x2(_11.m0[0].xy, _11.m0[1].xy) * (float2x2(_11.m1[0].xy, _11.m1[1].xy) * _11.v1); + _11.v0 = (_11.v1 * float2x2(_11.m0[0].xy, _11.m0[1].xy)) * float2x2(_11.m1[0].xy, _11.m1[1].xy); + _11.v0 = _11.v1 * (float2x2(_11.m0[0].xy, _11.m0[1].xy) * float2x2(_11.m1[0].xy, _11.m1[1].xy)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major-2.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major-2.comp new file mode 100644 index 0000000..7130c9a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major-2.comp @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +typedef packed_float3 packed_rm_float3x3[3]; + +struct SSBO +{ + packed_rm_float3x3 m0; + packed_rm_float3x3 m1; + packed_float3 v0; + packed_float3 v1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _11 [[buffer(0)]]) +{ + _11.v0 = float3(_11.v1) * (float3x3(float3(_11.m1[0]), float3(_11.m1[1]), float3(_11.m1[2])) * float3x3(float3(_11.m0[0]), float3(_11.m0[1]), float3(_11.m0[2]))); + _11.v0 = (float3(_11.v1) * float3x3(float3(_11.m1[0]), float3(_11.m1[1]), float3(_11.m1[2]))) * float3x3(float3(_11.m0[0]), float3(_11.m0[1]), float3(_11.m0[2])); + _11.v0 = float3x3(float3(_11.m1[0]), float3(_11.m1[1]), float3(_11.m1[2])) * (float3x3(float3(_11.m0[0]), float3(_11.m0[1]), float3(_11.m0[2])) * float3(_11.v1)); + _11.v0 = (float3x3(float3(_11.m1[0]), float3(_11.m1[1]), float3(_11.m1[2])) * float3x3(float3(_11.m0[0]), float3(_11.m0[1]), float3(_11.m0[2]))) * float3(_11.v1); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major.comp new file mode 100644 index 0000000..f061dd6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major.comp @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float2x4 m0; + float2x4 m1; + float2 v0; + float2 v1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _11 [[buffer(0)]]) +{ + _11.v0 = _11.v1 * (float2x2(_11.m1[0].xy, _11.m1[1].xy) * float2x2(_11.m0[0].xy, _11.m0[1].xy)); + _11.v0 = (_11.v1 * float2x2(_11.m1[0].xy, _11.m1[1].xy)) * float2x2(_11.m0[0].xy, _11.m0[1].xy); + _11.v0 = float2x2(_11.m1[0].xy, _11.m1[1].xy) * (float2x2(_11.m0[0].xy, _11.m0[1].xy) * _11.v1); + _11.v0 = (float2x2(_11.m1[0].xy, _11.m1[1].xy) * float2x2(_11.m0[0].xy, _11.m0[1].xy)) * _11.v1; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/member-padding.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/member-padding.comp new file mode 100644 index 0000000..4f653ec --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/member-padding.comp @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + char _m0_pad[16]; + float a; + char _m1_pad[20]; + float b; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _9 [[buffer(0)]]) +{ + _9.a = 10.0; + _9.b = 20.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/std140-array-of-vectors.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/std140-array-of-vectors.comp new file mode 100644 index 0000000..4d5ba32 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/std140-array-of-vectors.comp @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 v1[4]; + float4 v2[4]; + float3 v3[4]; + float4 v4[4]; + float4 v1_array_of_array[4][4]; + float4 v2_array_of_array[4][4]; + float3 v3_array_of_array[4][4]; + float4 v4_array_of_array[4][4]; + float4 v_unsized[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _29 [[buffer(0)]]) +{ + float loaded1 = _29.v1[1].x; + (device float&)_29.v1[2] = loaded1; + float2 loaded2 = _29.v2[1].xy; + (device float2&)_29.v2[2] = loaded2; + float3 loaded3 = _29.v3[1]; + _29.v3[2] = loaded3; + float4 loaded4 = _29.v4[1]; + _29.v4[2] = loaded4; + loaded1 = _29.v1_array_of_array[1][2].x; + (device float&)_29.v1_array_of_array[2][3] = loaded1; + loaded2 = _29.v2_array_of_array[1][2].xy; + (device float2&)_29.v2_array_of_array[2][3] = loaded2; + loaded3 = _29.v3_array_of_array[1][2]; + _29.v3_array_of_array[2][3] = loaded3; + loaded4 = _29.v4_array_of_array[1][2]; + _29.v4_array_of_array[2][3] = loaded4; + loaded1 = _29.v_unsized[1].x; + (device float&)_29.v_unsized[2] = loaded1; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-alignment.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-alignment.comp new file mode 100644 index 0000000..34647b4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-alignment.comp @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + packed_float3 a; + float b; +}; + +struct SSBO +{ + float2 a; + float b; + char _m2_pad[4]; + Foo foo; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _12 [[buffer(0)]]) +{ + ((device float*)&_12.a)[0u] = 10.0; + _12.b = 20.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing-array-of-scalar.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing-array-of-scalar.comp new file mode 100644 index 0000000..587ee4a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing-array-of-scalar.comp @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + packed_float3 a; +}; + +struct SSBOScalar +{ + Foo v[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBOScalar& buffer_scalar [[buffer(0)]]) +{ + buffer_scalar.v[1].a[1u] = 1.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing-recursive.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing-recursive.comp new file mode 100644 index 0000000..e0652b9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing-recursive.comp @@ -0,0 +1,33 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + packed_float4 a; +}; + +struct Bar +{ + Foo a; +}; + +struct Baz +{ + Bar a; +}; + +struct SSBOScalar +{ + float v; + Baz baz; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBOScalar& buffer_scalar [[buffer(0)]]) +{ + buffer_scalar.baz.a.a.a[3u] = 10.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing.comp new file mode 100644 index 0000000..a86809f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-packing.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + packed_float3 a; +}; + +struct Bar +{ + packed_float3 a; +}; + +struct SSBOScalar +{ + Foo foo; + Bar bar; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBOScalar& buffer_scalar [[buffer(0)]]) +{ + buffer_scalar.foo.a[0u] = 10.0; + buffer_scalar.bar.a[0u] = 20.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-size-padding-array-of-array.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-size-padding-array-of-array.comp new file mode 100644 index 0000000..c30fd07 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-size-padding-array-of-array.comp @@ -0,0 +1,54 @@ +#include +#include + +using namespace metal; + +struct A +{ + float v; + char _m0_final_padding[12]; +}; + +struct B +{ + float2 v; + char _m0_final_padding[8]; +}; + +struct C +{ + float3 v; +}; + +struct D +{ + float4 v; +}; + +struct E +{ + float4 a; + float2 b; + char _m0_final_padding[8]; +}; + +struct SSBO +{ + A a[2][4]; + B b[2][4]; + C c[2][4]; + D d[2][4]; + float2x4 e[2][4]; + E f[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _32 [[buffer(0)]]) +{ + _32.f[0].a = float4(2.0); + float2x2 tmp = float2x2(_32.e[0][1][0].xy, _32.e[0][1][1].xy); + (device float2&)_32.e[1][2][0] = tmp[0]; + (device float2&)_32.e[1][2][1] = tmp[1]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-size-padding.comp b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-size-padding.comp new file mode 100644 index 0000000..98f039f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/packing/struct-size-padding.comp @@ -0,0 +1,54 @@ +#include +#include + +using namespace metal; + +struct A +{ + float v; + char _m0_final_padding[12]; +}; + +struct B +{ + float2 v; + char _m0_final_padding[8]; +}; + +struct C +{ + float3 v; +}; + +struct D +{ + float4 v; +}; + +struct E +{ + float4 a; + float2 b; + char _m0_final_padding[8]; +}; + +struct SSBO +{ + A a[4]; + B b[4]; + C c[4]; + D d[4]; + float2x4 e[4]; + E f[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _26 [[buffer(0)]]) +{ + _26.f[0].a = float4(2.0); + float2x2 tmp = float2x2(_26.e[1][0].xy, _26.e[1][1].xy); + (device float2&)_26.e[2][0] = tmp[0]; + (device float2&)_26.e[2][1] = tmp[1]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/functions_nested.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/functions_nested.vert new file mode 100644 index 0000000..98c55aa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/functions_nested.vert @@ -0,0 +1,199 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct attr_desc +{ + int type; + int attribute_size; + int starting_offset; + int stride; + int swap_bytes; + int is_volatile; +}; + +struct VertexBuffer +{ + float4x4 scale_offset_mat; + uint vertex_base_index; + int4 input_attributes[16]; +}; + +struct VertexConstantsBuffer +{ + float4 vc[16]; +}; + +struct main0_out +{ + float4 tc0 [[user(locn0)]]; + float4 back_color [[user(locn10)]]; + float4 gl_Position [[position]]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +static inline __attribute__((always_inline)) +attr_desc fetch_desc(thread const int& location, constant VertexBuffer& v_227) +{ + int attribute_flags = v_227.input_attributes[location].w; + attr_desc result; + result.type = v_227.input_attributes[location].x; + result.attribute_size = v_227.input_attributes[location].y; + result.starting_offset = v_227.input_attributes[location].z; + result.stride = attribute_flags & 255; + result.swap_bytes = (attribute_flags >> 8) & 1; + result.is_volatile = (attribute_flags >> 9) & 1; + return result; +} + +static inline __attribute__((always_inline)) +uint get_bits(thread const uint4& v, thread const int& swap) +{ + if (swap != 0) + { + return ((v.w | (v.z << uint(8))) | (v.y << uint(16))) | (v.x << uint(24)); + } + return ((v.x | (v.y << uint(8))) | (v.z << uint(16))) | (v.w << uint(24)); +} + +static inline __attribute__((always_inline)) +float4 fetch_attr(thread const attr_desc& desc, thread const int& vertex_id, thread const texture2d input_stream) +{ + float4 result = float4(0.0, 0.0, 0.0, 1.0); + bool reverse_order = false; + int first_byte = (vertex_id * desc.stride) + desc.starting_offset; + uint4 tmp; + for (int n = 0; n < 4; n++) + { + if (n == desc.attribute_size) + { + break; + } + switch (desc.type) + { + case 0: + { + int _131 = first_byte; + first_byte = _131 + 1; + tmp.x = input_stream.read(spvTexelBufferCoord(_131)).x; + int _138 = first_byte; + first_byte = _138 + 1; + tmp.y = input_stream.read(spvTexelBufferCoord(_138)).x; + uint4 param = tmp; + int param_1 = desc.swap_bytes; + result[n] = float(get_bits(param, param_1)); + break; + } + case 1: + { + int _156 = first_byte; + first_byte = _156 + 1; + tmp.x = input_stream.read(spvTexelBufferCoord(_156)).x; + int _163 = first_byte; + first_byte = _163 + 1; + tmp.y = input_stream.read(spvTexelBufferCoord(_163)).x; + int _170 = first_byte; + first_byte = _170 + 1; + tmp.z = input_stream.read(spvTexelBufferCoord(_170)).x; + int _177 = first_byte; + first_byte = _177 + 1; + tmp.w = input_stream.read(spvTexelBufferCoord(_177)).x; + uint4 param_2 = tmp; + int param_3 = desc.swap_bytes; + result[n] = as_type(get_bits(param_2, param_3)); + break; + } + case 2: + { + int _195 = first_byte; + first_byte = _195 + 1; + result[n] = float(input_stream.read(spvTexelBufferCoord(_195)).x); + reverse_order = desc.swap_bytes != 0; + break; + } + } + } + float4 _210; + if (reverse_order) + { + _210 = result.wzyx; + } + else + { + _210 = result; + } + return _210; +} + +static inline __attribute__((always_inline)) +float4 read_location(thread const int& location, constant VertexBuffer& v_227, thread uint& gl_VertexIndex, thread texture2d buff_in_2, thread texture2d buff_in_1) +{ + int param = location; + attr_desc desc = fetch_desc(param, v_227); + int vertex_id = int(gl_VertexIndex) - int(v_227.vertex_base_index); + if (desc.is_volatile != 0) + { + attr_desc param_1 = desc; + int param_2 = vertex_id; + return fetch_attr(param_1, param_2, buff_in_2); + } + else + { + attr_desc param_3 = desc; + int param_4 = vertex_id; + return fetch_attr(param_3, param_4, buff_in_1); + } +} + +static inline __attribute__((always_inline)) +void vs_adjust(thread float4& dst_reg0, thread float4& dst_reg1, thread float4& dst_reg7, constant VertexBuffer& v_227, thread uint& gl_VertexIndex, thread texture2d buff_in_2, thread texture2d buff_in_1, constant VertexConstantsBuffer& v_309) +{ + int param = 3; + float4 in_diff_color = read_location(param, v_227, gl_VertexIndex, buff_in_2, buff_in_1); + int param_1 = 0; + float4 in_pos = read_location(param_1, v_227, gl_VertexIndex, buff_in_2, buff_in_1); + int param_2 = 8; + float4 in_tc0 = read_location(param_2, v_227, gl_VertexIndex, buff_in_2, buff_in_1); + dst_reg1 = in_diff_color * v_309.vc[13]; + float4 tmp0; + tmp0.x = float4(dot(float4(in_pos.xyz, 1.0), v_309.vc[4])).x; + tmp0.y = float4(dot(float4(in_pos.xyz, 1.0), v_309.vc[5])).y; + tmp0.z = float4(dot(float4(in_pos.xyz, 1.0), v_309.vc[6])).z; + float4 tmp1; + tmp1 = float4(in_tc0.xy.x, in_tc0.xy.y, tmp1.z, tmp1.w); + tmp1.z = v_309.vc[15].x; + dst_reg7.y = float4(dot(float4(tmp1.xyz, 1.0), v_309.vc[8])).y; + dst_reg7.x = float4(dot(float4(tmp1.xyz, 1.0), v_309.vc[7])).x; + dst_reg0.y = float4(dot(float4(tmp0.xyz, 1.0), v_309.vc[1])).y; + dst_reg0.x = float4(dot(float4(tmp0.xyz, 1.0), v_309.vc[0])).x; +} + +vertex main0_out main0(constant VertexBuffer& v_227 [[buffer(0)]], constant VertexConstantsBuffer& v_309 [[buffer(1)]], texture2d buff_in_2 [[texture(0)]], texture2d buff_in_1 [[texture(1)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + float4 dst_reg0 = float4(0.0, 0.0, 0.0, 1.0); + float4 dst_reg1 = float4(0.0); + float4 dst_reg7 = float4(0.0); + float4 param = dst_reg0; + float4 param_1 = dst_reg1; + float4 param_2 = dst_reg7; + vs_adjust(param, param_1, param_2, v_227, gl_VertexIndex, buff_in_2, buff_in_1, v_309); + dst_reg0 = param; + dst_reg1 = param_1; + dst_reg7 = param_2; + out.gl_Position = dst_reg0; + out.back_color = dst_reg1; + out.tc0 = dst_reg7; + out.gl_Position *= v_227.scale_offset_mat; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/layer.msl11.invalid.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/layer.msl11.invalid.vert new file mode 100644 index 0000000..b6f39dc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/layer.msl11.invalid.vert @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 coord [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.coord; + out.gl_Layer = uint(int(in.coord.z)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/pass-array-by-value.force-native-array.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/pass-array-by-value.force-native-array.vert new file mode 100644 index 0000000..81202c9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/pass-array-by-value.force-native-array.vert @@ -0,0 +1,157 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +constant float4 _68[4] = { float4(0.0), float4(1.0), float4(2.0), float4(3.0) }; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + int Index1 [[attribute(0)]]; + int Index2 [[attribute(1)]]; +}; + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +static inline __attribute__((always_inline)) +float4 consume_constant_arrays2(thread const float4 (&positions)[4], thread const float4 (&positions2)[4], thread int& Index1, thread int& Index2) +{ + float4 indexable[4]; + spvArrayCopyFromStackToStack1(indexable, positions); + float4 indexable_1[4]; + spvArrayCopyFromStackToStack1(indexable_1, positions2); + return indexable[Index1] + indexable_1[Index2]; +} + +static inline __attribute__((always_inline)) +float4 consume_constant_arrays(thread const float4 (&positions)[4], thread const float4 (&positions2)[4], thread int& Index1, thread int& Index2) +{ + return consume_constant_arrays2(positions, positions2, Index1, Index2); +} + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + float4 _68_array_copy[4] = { float4(0.0), float4(1.0), float4(2.0), float4(3.0) }; + main0_out out = {}; + float4 LUT2[4]; + LUT2[0] = float4(10.0); + LUT2[1] = float4(11.0); + LUT2[2] = float4(12.0); + LUT2[3] = float4(13.0); + out.gl_Position = consume_constant_arrays(_68_array_copy, LUT2, in.Index1, in.Index2); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/pass-array-by-value.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/pass-array-by-value.vert new file mode 100644 index 0000000..f6e3efb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/pass-array-by-value.vert @@ -0,0 +1,87 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _68 = spvUnsafeArray({ float4(0.0), float4(1.0), float4(2.0), float4(3.0) }); + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + int Index1 [[attribute(0)]]; + int Index2 [[attribute(1)]]; +}; + +static inline __attribute__((always_inline)) +float4 consume_constant_arrays2(spvUnsafeArray positions, spvUnsafeArray positions2, thread int& Index1, thread int& Index2) +{ + spvUnsafeArray indexable; + indexable = positions; + spvUnsafeArray indexable_1; + indexable_1 = positions2; + return indexable[Index1] + indexable_1[Index2]; +} + +static inline __attribute__((always_inline)) +float4 consume_constant_arrays(spvUnsafeArray positions, spvUnsafeArray positions2, thread int& Index1, thread int& Index2) +{ + return consume_constant_arrays2(positions, positions2, Index1, Index2); +} + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray LUT2; + LUT2[0] = float4(10.0); + LUT2[1] = float4(11.0); + LUT2[2] = float4(12.0); + LUT2[3] = float4(13.0); + out.gl_Position = consume_constant_arrays(_68, LUT2, in.Index1, in.Index2); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/viewport-index.msl2.invalid.vert b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/viewport-index.msl2.invalid.vert new file mode 100644 index 0000000..e5316c0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/vert/viewport-index.msl2.invalid.vert @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_ViewportIndex [[viewport_array_index]]; +}; + +struct main0_in +{ + float4 coord [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.coord; + out.gl_ViewportIndex = uint(int(in.coord.z)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl-no-opt/vulkan/frag/texture-access-function.swizzle.vk.frag b/third_party/spirv-cross/reference/shaders-msl-no-opt/vulkan/frag/texture-access-function.swizzle.vk.frag new file mode 100644 index 0000000..d4f70e0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl-no-opt/vulkan/frag/texture-access-function.swizzle.vk.frag @@ -0,0 +1,204 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 fragColor [[color(0)]]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +// Wrapper function that swizzles texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherSwizzle(const thread Tex& t, sampler s, uint sw, component c, Ts... params) METAL_CONST_ARG(c) +{ + if (sw) + { + switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF)) + { + case spvSwizzle::none: + break; + case spvSwizzle::zero: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + case spvSwizzle::red: + return t.gather(s, spvForward(params)..., component::x); + case spvSwizzle::green: + return t.gather(s, spvForward(params)..., component::y); + case spvSwizzle::blue: + return t.gather(s, spvForward(params)..., component::z); + case spvSwizzle::alpha: + return t.gather(s, spvForward(params)..., component::w); + } + } + switch (c) + { + case component::x: + return t.gather(s, spvForward(params)..., component::x); + case component::y: + return t.gather(s, spvForward(params)..., component::y); + case component::z: + return t.gather(s, spvForward(params)..., component::z); + case component::w: + return t.gather(s, spvForward(params)..., component::w); + } +} + +// Wrapper function that swizzles depth texture gathers. +template class Tex, typename... Ts> +inline vec spvGatherCompareSwizzle(const thread Tex& t, sampler s, uint sw, Ts... params) +{ + if (sw) + { + switch (spvSwizzle(sw & 0xFF)) + { + case spvSwizzle::none: + case spvSwizzle::red: + break; + case spvSwizzle::zero: + case spvSwizzle::green: + case spvSwizzle::blue: + case spvSwizzle::alpha: + return vec(0, 0, 0, 0); + case spvSwizzle::one: + return vec(1, 1, 1, 1); + } + } + return t.gather_compare(s, spvForward(params)...); +} + +static inline __attribute__((always_inline)) +float4 do_samples(thread const texture1d t1, thread const sampler t1Smplr, constant uint& t1Swzl, thread const texture2d t2, constant uint& t2Swzl, thread const texture3d t3, thread const sampler t3Smplr, constant uint& t3Swzl, thread const texturecube tc, constant uint& tcSwzl, thread const texture2d_array t2a, thread const sampler t2aSmplr, constant uint& t2aSwzl, thread const texturecube_array tca, thread const sampler tcaSmplr, constant uint& tcaSwzl, thread const texture2d tb, thread const depth2d d2, thread const sampler d2Smplr, constant uint& d2Swzl, thread const depthcube dc, thread const sampler dcSmplr, constant uint& dcSwzl, thread const depth2d_array d2a, constant uint& d2aSwzl, thread const depthcube_array dca, thread const sampler dcaSmplr, constant uint& dcaSwzl, thread sampler defaultSampler, thread sampler shadowSampler) +{ + float4 c = spvTextureSwizzle(t1.sample(t1Smplr, 0.0), t1Swzl); + c = spvTextureSwizzle(t2.sample(defaultSampler, float2(0.0)), t2Swzl); + c = spvTextureSwizzle(t3.sample(t3Smplr, float3(0.0)), t3Swzl); + c = spvTextureSwizzle(tc.sample(defaultSampler, float3(0.0)), tcSwzl); + c = spvTextureSwizzle(t2a.sample(t2aSmplr, float3(0.0).xy, uint(round(float3(0.0).z))), t2aSwzl); + c = spvTextureSwizzle(tca.sample(tcaSmplr, float4(0.0).xyz, uint(round(float4(0.0).w))), tcaSwzl); + c.x = spvTextureSwizzle(d2.sample_compare(d2Smplr, float3(0.0, 0.0, 1.0).xy, float3(0.0, 0.0, 1.0).z), d2Swzl); + c.x = spvTextureSwizzle(dc.sample_compare(dcSmplr, float4(0.0, 0.0, 0.0, 1.0).xyz, float4(0.0, 0.0, 0.0, 1.0).w), dcSwzl); + c.x = spvTextureSwizzle(d2a.sample_compare(shadowSampler, float4(0.0, 0.0, 0.0, 1.0).xy, uint(round(float4(0.0, 0.0, 0.0, 1.0).z)), float4(0.0, 0.0, 0.0, 1.0).w), d2aSwzl); + c.x = spvTextureSwizzle(dca.sample_compare(dcaSmplr, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0), dcaSwzl); + c = spvTextureSwizzle(t1.sample(t1Smplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), t1Swzl); + c = spvTextureSwizzle(t2.sample(defaultSampler, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z), t2Swzl); + c = spvTextureSwizzle(t3.sample(t3Smplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w), t3Swzl); + float4 _119 = float4(0.0, 0.0, 1.0, 1.0); + _119.z = float4(0.0, 0.0, 1.0, 1.0).w; + c.x = spvTextureSwizzle(d2.sample_compare(d2Smplr, _119.xy / _119.z, float4(0.0, 0.0, 1.0, 1.0).z / _119.z), d2Swzl); + c = spvTextureSwizzle(t1.sample(t1Smplr, 0.0), t1Swzl); + c = spvTextureSwizzle(t2.sample(defaultSampler, float2(0.0), level(0.0)), t2Swzl); + c = spvTextureSwizzle(t3.sample(t3Smplr, float3(0.0), level(0.0)), t3Swzl); + c = spvTextureSwizzle(tc.sample(defaultSampler, float3(0.0), level(0.0)), tcSwzl); + c = spvTextureSwizzle(t2a.sample(t2aSmplr, float3(0.0).xy, uint(round(float3(0.0).z)), level(0.0)), t2aSwzl); + c = spvTextureSwizzle(tca.sample(tcaSmplr, float4(0.0).xyz, uint(round(float4(0.0).w)), level(0.0)), tcaSwzl); + c.x = spvTextureSwizzle(d2.sample_compare(d2Smplr, float3(0.0, 0.0, 1.0).xy, float3(0.0, 0.0, 1.0).z, level(0.0)), d2Swzl); + c = spvTextureSwizzle(t1.sample(t1Smplr, float2(0.0, 1.0).x / float2(0.0, 1.0).y), t1Swzl); + c = spvTextureSwizzle(t2.sample(defaultSampler, float3(0.0, 0.0, 1.0).xy / float3(0.0, 0.0, 1.0).z, level(0.0)), t2Swzl); + c = spvTextureSwizzle(t3.sample(t3Smplr, float4(0.0, 0.0, 0.0, 1.0).xyz / float4(0.0, 0.0, 0.0, 1.0).w, level(0.0)), t3Swzl); + float4 _153 = float4(0.0, 0.0, 1.0, 1.0); + _153.z = float4(0.0, 0.0, 1.0, 1.0).w; + c.x = spvTextureSwizzle(d2.sample_compare(d2Smplr, _153.xy / _153.z, float4(0.0, 0.0, 1.0, 1.0).z / _153.z, level(0.0)), d2Swzl); + c = spvTextureSwizzle(t1.read(uint(0)), t1Swzl); + c = spvTextureSwizzle(t2.read(uint2(int2(0)), 0), t2Swzl); + c = spvTextureSwizzle(t3.read(uint3(int3(0)), 0), t3Swzl); + c = spvTextureSwizzle(t2a.read(uint2(int3(0).xy), uint(int3(0).z), 0), t2aSwzl); + c = tb.read(spvTexelBufferCoord(0)); + c = spvGatherSwizzle(t2, defaultSampler, t2Swzl, component::x, float2(0.0), int2(0)); + c = spvGatherSwizzle(tc, defaultSampler, tcSwzl, component::y, float3(0.0)); + c = spvGatherSwizzle(t2a, t2aSmplr, t2aSwzl, component::z, float3(0.0).xy, uint(round(float3(0.0).z)), int2(0)); + c = spvGatherSwizzle(tca, tcaSmplr, tcaSwzl, component::w, float4(0.0).xyz, uint(round(float4(0.0).w))); + c = spvGatherCompareSwizzle(d2, d2Smplr, d2Swzl, float2(0.0), 1.0); + c = spvGatherCompareSwizzle(dc, dcSmplr, dcSwzl, float3(0.0), 1.0); + c = spvGatherCompareSwizzle(d2a, shadowSampler, d2aSwzl, float3(0.0).xy, uint(round(float3(0.0).z)), 1.0); + c = spvGatherCompareSwizzle(dca, dcaSmplr, dcaSwzl, float4(0.0).xyz, uint(round(float4(0.0).w)), 1.0); + return c; +} + +fragment main0_out main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture1d tex1d [[texture(0)]], texture2d tex2d [[texture(1)]], texture3d tex3d [[texture(2)]], texturecube texCube [[texture(3)]], texture2d_array tex2dArray [[texture(4)]], texturecube_array texCubeArray [[texture(5)]], texture2d texBuffer [[texture(6)]], depth2d depth2d [[texture(7)]], depthcube depthCube [[texture(8)]], depth2d_array depth2dArray [[texture(9)]], depthcube_array depthCubeArray [[texture(10)]], sampler defaultSampler [[sampler(0)]], sampler shadowSampler [[sampler(1)]], sampler tex1dSmplr [[sampler(2)]], sampler tex3dSmplr [[sampler(3)]], sampler tex2dArraySmplr [[sampler(4)]], sampler texCubeArraySmplr [[sampler(5)]], sampler depth2dSmplr [[sampler(6)]], sampler depthCubeSmplr [[sampler(7)]], sampler depthCubeArraySmplr [[sampler(8)]]) +{ + main0_out out = {}; + constant uint& tex1dSwzl = spvSwizzleConstants[0]; + constant uint& tex2dSwzl = spvSwizzleConstants[1]; + constant uint& tex3dSwzl = spvSwizzleConstants[2]; + constant uint& texCubeSwzl = spvSwizzleConstants[3]; + constant uint& tex2dArraySwzl = spvSwizzleConstants[4]; + constant uint& texCubeArraySwzl = spvSwizzleConstants[5]; + constant uint& depth2dSwzl = spvSwizzleConstants[7]; + constant uint& depthCubeSwzl = spvSwizzleConstants[8]; + constant uint& depth2dArraySwzl = spvSwizzleConstants[9]; + constant uint& depthCubeArraySwzl = spvSwizzleConstants[10]; + out.fragColor = do_samples(tex1d, tex1dSmplr, tex1dSwzl, tex2d, tex2dSwzl, tex3d, tex3dSmplr, tex3dSwzl, texCube, texCubeSwzl, tex2dArray, tex2dArraySmplr, tex2dArraySwzl, texCubeArray, texCubeArraySmplr, texCubeArraySwzl, texBuffer, depth2d, depth2dSmplr, depth2dSwzl, depthCube, depthCubeSmplr, depthCubeSwzl, depth2dArray, depth2dArraySwzl, depthCubeArray, depthCubeArraySmplr, depthCubeArraySwzl, defaultSampler, shadowSampler); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/amd/shader_trinary_minmax.msl21.comp b/third_party/spirv-cross/reference/shaders-msl/amd/shader_trinary_minmax.msl21.comp new file mode 100644 index 0000000..1440471 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/amd/shader_trinary_minmax.msl21.comp @@ -0,0 +1,14 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(64u, 1u, 1u); + +kernel void main0() +{ + int t11 = min3(0, 3, 2); + int t12 = max3(0, 3, 2); + int t13 = median3(0, 3, 2); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..e250770 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,29 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct u0_counters +{ + uint c; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +kernel void main0(device u0_counters& u0_counter [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _29 = atomic_fetch_sub_explicit((device atomic_uint*)&u0_counter.c, 1, memory_order_relaxed); + float4 r0; + r0.x = as_type(_29); + u0.write(uint4(uint(int(gl_GlobalInvocationID.x))), spvTexelBufferCoord(((uint(as_type(r0.x)) * 1u) + (uint(0) >> 2u)))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..22c104f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,29 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct u0_counters +{ + uint c; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +kernel void main0(device u0_counters& u0_counter [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _29 = atomic_fetch_add_explicit((device atomic_uint*)&u0_counter.c, 1, memory_order_relaxed); + float4 r0; + r0.x = as_type(_29); + u0.write(uint4(uint(int(gl_GlobalInvocationID.x))), spvTexelBufferCoord(((uint(as_type(r0.x)) * 1u) + (uint(0) >> 2u)))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_iadd.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_iadd.asm.comp new file mode 100644 index 0000000..ad61d75 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_iadd.asm.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& restrict _5 [[buffer(0)]], device _4& restrict _6 [[buffer(1)]]) +{ + _6._m0 = _5._m1 + uint4(_5._m0); + _6._m0 = uint4(_5._m0) + _5._m1; + _6._m0 = _5._m1 + _5._m1; + _6._m0 = uint4(_5._m0 + _5._m0); + _6._m1 = int4(_5._m1 + _5._m1); + _6._m1 = _5._m0 + _5._m0; + _6._m1 = int4(_5._m1) + _5._m0; + _6._m1 = _5._m0 + int4(_5._m1); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..31c71da --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& restrict _5 [[buffer(0)]], device _4& restrict _6 [[buffer(1)]]) +{ + _6._m0 = select(uint4(0u), uint4(1u), int4(_5._m1) < _5._m0); + _6._m0 = select(uint4(0u), uint4(1u), int4(_5._m1) <= _5._m0); + _6._m0 = select(uint4(0u), uint4(1u), _5._m1 < uint4(_5._m0)); + _6._m0 = select(uint4(0u), uint4(1u), _5._m1 <= uint4(_5._m0)); + _6._m0 = select(uint4(0u), uint4(1u), int4(_5._m1) > _5._m0); + _6._m0 = select(uint4(0u), uint4(1u), int4(_5._m1) >= _5._m0); + _6._m0 = select(uint4(0u), uint4(1u), _5._m1 > uint4(_5._m0)); + _6._m0 = select(uint4(0u), uint4(1u), _5._m1 >= uint4(_5._m0)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_sar.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_sar.asm.comp new file mode 100644 index 0000000..4176830 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_sar.asm.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& _5 [[buffer(0)]], device _4& _6 [[buffer(1)]]) +{ + int4 _22 = _5._m0; + uint4 _23 = _5._m1; + _6._m0 = uint4(int4(_23) >> _22); + _6._m0 = uint4(_22 >> int4(_23)); + _6._m0 = uint4(int4(_23) >> int4(_23)); + _6._m0 = uint4(_22 >> _22); + _6._m1 = int4(_23) >> int4(_23); + _6._m1 = _22 >> _22; + _6._m1 = int4(_23) >> _22; + _6._m1 = _22 >> int4(_23); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_sdiv.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_sdiv.asm.comp new file mode 100644 index 0000000..6b80dff --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_sdiv.asm.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& _5 [[buffer(0)]], device _4& _6 [[buffer(1)]]) +{ + int4 _22 = _5._m0; + uint4 _23 = _5._m1; + _6._m0 = uint4(int4(_23) / _22); + _6._m0 = uint4(_22 / int4(_23)); + _6._m0 = uint4(int4(_23) / int4(_23)); + _6._m0 = uint4(_22 / _22); + _6._m1 = int4(_23) / int4(_23); + _6._m1 = _22 / _22; + _6._m1 = int4(_23) / _22; + _6._m1 = _22 / int4(_23); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_slr.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_slr.asm.comp new file mode 100644 index 0000000..1dfca39 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/bitcast_slr.asm.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct _3 +{ + int4 _m0; + uint4 _m1; +}; + +struct _4 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _3& _5 [[buffer(0)]], device _4& _6 [[buffer(1)]]) +{ + int4 _22 = _5._m0; + uint4 _23 = _5._m1; + _6._m0 = _23 >> uint4(_22); + _6._m0 = uint4(_22) >> _23; + _6._m0 = _23 >> _23; + _6._m0 = uint4(_22) >> uint4(_22); + _6._m1 = int4(_23 >> _23); + _6._m1 = int4(uint4(_22) >> uint4(_22)); + _6._m1 = int4(_23 >> uint4(_22)); + _6._m1 = int4(uint4(_22) >> _23); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..6dcc14e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,46 @@ +#include +#include + +using namespace metal; + +struct A +{ + int a; + int b; +}; + +struct A_1 +{ + A Data[1]; +}; + +struct A_2 +{ + int a; + int b; + char _m0_final_padding[8]; +}; + +struct A_3 +{ + A_2 Data[1024]; +}; + +struct B +{ + A Data[1]; +}; + +struct B_1 +{ + A_2 Data[1024]; +}; + +kernel void main0(device A_1& C1 [[buffer(0)]], constant A_3& C2 [[buffer(1)]], device B& C3 [[buffer(2)]], constant B_1& C4 [[buffer(3)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + C1.Data[gl_GlobalInvocationID.x].a = C2.Data[gl_GlobalInvocationID.x].a; + C1.Data[gl_GlobalInvocationID.x].b = C2.Data[gl_GlobalInvocationID.x].b; + C3.Data[gl_GlobalInvocationID.x].a = C4.Data[gl_GlobalInvocationID.x].a; + C3.Data[gl_GlobalInvocationID.x].b = C4.Data[gl_GlobalInvocationID.x].b; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp new file mode 100644 index 0000000..57d2205 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp @@ -0,0 +1,32 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct cb5_struct +{ + float4 _m0[5]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +kernel void main0(constant cb5_struct& cb0_5 [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + float4 r0; + r0.x = as_type(int(gl_LocalInvocationID.x) << 4); + r0.y = as_type(int(gl_LocalInvocationID.x)); + uint _44 = as_type(r0.x) >> 2u; + uint4 _51 = as_type(cb0_5._m0[uint(as_type(r0.y)) + 1u]); + u0.write(_51.xxxx, spvTexelBufferCoord(_44)); + u0.write(_51.yyyy, spvTexelBufferCoord((_44 + 1u))); + u0.write(_51.zzzz, spvTexelBufferCoord((_44 + 2u))); + u0.write(_51.wwww, spvTexelBufferCoord((_44 + 3u))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/buffer-write.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/buffer-write.asm.comp new file mode 100644 index 0000000..8c9e23a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/buffer-write.asm.comp @@ -0,0 +1,24 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct cb +{ + float value; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +kernel void main0(constant cb& _6 [[buffer(0)]], texture2d _buffer [[texture(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + _buffer.write(float4(_6.value), spvTexelBufferCoord(((32u * gl_WorkGroupID.x) + gl_LocalInvocationIndex))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..ec1b2a2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,33 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct ssbo +{ + uint _data[1]; +}; + +static inline __attribute__((always_inline)) +void Load(thread const uint& size, const device ssbo& ssbo_1) +{ + int byteAddrTemp = int(size >> uint(2)); + uint4 data = uint4(ssbo_1._data[byteAddrTemp], ssbo_1._data[byteAddrTemp + 1], ssbo_1._data[byteAddrTemp + 2], ssbo_1._data[byteAddrTemp + 3]); +} + +static inline __attribute__((always_inline)) +void _main(thread const uint3& id, const device ssbo& ssbo_1) +{ + uint param = 4u; + Load(param, ssbo_1); +} + +kernel void main0(const device ssbo& ssbo_1 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint3 id = gl_GlobalInvocationID; + uint3 param = id; + _main(param, ssbo_1); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp new file mode 100644 index 0000000..fca572e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp @@ -0,0 +1,22 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +static inline __attribute__((always_inline)) +void _main(thread const uint3& id, thread texture2d TargetTexture) +{ + float2 loaded = TargetTexture.read(uint2(id.xy)).xy; + float2 storeTemp = loaded + float2(1.0); + TargetTexture.write(storeTemp.xyyy, uint2((id.xy + uint2(1u)))); +} + +kernel void main0(texture2d TargetTexture [[texture(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +{ + uint3 id = gl_WorkGroupID; + uint3 param = id; + _main(param, TargetTexture); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/multiple-entry.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/multiple-entry.asm.comp new file mode 100644 index 0000000..25ccf62 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/multiple-entry.asm.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct _6 +{ + int4 _m0; + uint4 _m1; +}; + +struct _7 +{ + uint4 _m0; + int4 _m1; +}; + +kernel void main0(device _6& restrict _8 [[buffer(0)]], device _7& restrict _9 [[buffer(1)]]) +{ + _9._m0 = _8._m1 + uint4(_8._m0); + _9._m0 = uint4(_8._m0) + _8._m1; + _9._m0 = _8._m1 + _8._m1; + _9._m0 = uint4(_8._m0 + _8._m0); + _9._m1 = int4(_8._m1 + _8._m1); + _9._m1 = _8._m0 + _8._m0; + _9._m1 = int4(_8._m1) + _8._m0; + _9._m1 = _8._m0 + int4(_8._m1); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/quantize.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/quantize.asm.comp new file mode 100644 index 0000000..1839ec7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/quantize.asm.comp @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct SSBO0 +{ + float scalar; + float2 vec2_val; + float3 vec3_val; + float4 vec4_val; +}; + +kernel void main0(device SSBO0& _4 [[buffer(0)]]) +{ + _4.scalar = float(half(_4.scalar)); + _4.vec2_val = float2(half2(_4.vec2_val)); + _4.vec3_val = float3(half3(_4.vec3_val)); + _4.vec4_val = float4(half4(_4.vec4_val)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/relaxed-block-layout.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/relaxed-block-layout.asm.comp new file mode 100644 index 0000000..6728a4e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/relaxed-block-layout.asm.comp @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct foo +{ + uint bar; + packed_float3 baz; + uchar quux; + packed_uchar4 blah; + packed_half2 wibble; +}; + +kernel void main0(device foo& _8 [[buffer(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_NumWorkGroups [[threadgroups_per_grid]]) +{ + _8.bar = gl_LocalInvocationID.x; + _8.baz = float3(gl_GlobalInvocationID); + _8.blah = uchar4(uint4(uint4(uchar4(_8.blah)).xyz + gl_WorkGroupID, 0u)); + _8.wibble = half2(float2(half2(_8.wibble)) * float2(gl_NumWorkGroups.xy)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp new file mode 100644 index 0000000..1e2880f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float a; +}; + +constant uint _5_tmp [[function_constant(10)]]; +constant uint _5 = is_function_constant_defined(_5_tmp) ? _5_tmp : 9u; +constant uint _6_tmp [[function_constant(12)]]; +constant uint _6 = is_function_constant_defined(_6_tmp) ? _6_tmp : 4u; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(_5, 20u, _6); + +kernel void main0(device SSBO& _4 [[buffer(0)]]) +{ + _4.a += 1.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp new file mode 100644 index 0000000..fa7d269 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp @@ -0,0 +1,24 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct bufA +{ + uint _data[1]; +}; + +static inline __attribute__((always_inline)) +void _main(device bufA& bufA_1, device bufA& bufB) +{ + bufA_1._data[0] = 0u; + bufB._data[0] = 0u; +} + +kernel void main0(device bufA& bufA_1 [[buffer(0)]], device bufA& bufB [[buffer(1)]]) +{ + _main(bufA_1, bufB); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/variable-pointers-2.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/variable-pointers-2.asm.comp new file mode 100644 index 0000000..dafd6d5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/variable-pointers-2.asm.comp @@ -0,0 +1,63 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct foo +{ + int a[128]; + uint b; + float2 c; +}; + +struct bar +{ + int d; +}; + +static inline __attribute__((always_inline)) +device foo* select_buffer(device foo& a, constant bar& cb) +{ + return (cb.d != 0) ? &a : nullptr; +} + +static inline __attribute__((always_inline)) +thread uint3* select_input(thread uint3& gl_GlobalInvocationID, thread uint3& gl_LocalInvocationID, constant bar& cb) +{ + return (cb.d != 0) ? &gl_GlobalInvocationID : &gl_LocalInvocationID; +} + +kernel void main0(device foo& buf [[buffer(0)]], constant bar& cb [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + device foo* _46 = select_buffer(buf, cb); + device foo* _45 = _46; + thread uint3* _47 = select_input(gl_GlobalInvocationID, gl_LocalInvocationID, cb); + device foo* _48 = _45; + device int* _52; + device int* _55; + _52 = &_48->a[0u]; + _55 = &buf.a[0u]; + int _57; + int _58; + for (;;) + { + _57 = *_52; + _58 = *_55; + if (_57 != _58) + { + int _66 = (_57 + _58) + int((*_47).x); + *_52 = _66; + *_55 = _66; + _52 = &_52[1u]; + _55 = &_55[1u]; + continue; + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp new file mode 100644 index 0000000..00c490c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp @@ -0,0 +1,32 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct foo +{ + int a; +}; + +struct bar +{ + int b; +}; + +static inline __attribute__((always_inline)) +device int* _24(device foo& a, device bar& b, thread uint3& gl_GlobalInvocationID) +{ + return (gl_GlobalInvocationID.x != 0u) ? &a.a : &b.b; +} + +kernel void main0(device foo& x [[buffer(0)]], device bar& y [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + device int* _34 = _24(x, y, gl_GlobalInvocationID); + device int* _33 = _34; + int _37 = x.a; + *_33 = 0; + y.b = _37 + _37; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp new file mode 100644 index 0000000..9fb68a6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp @@ -0,0 +1,34 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct cb1_struct +{ + float4 _RESERVED_IDENTIFIER_FIXUP_m0[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(16u, 16u, 1u); + +static inline __attribute__((always_inline)) +int2 get_texcoord(thread const int2& base, thread const int2& index, thread uint3& gl_LocalInvocationID) +{ + return (base * int3(gl_LocalInvocationID).xy) + index; +} + +kernel void main0(constant cb1_struct& cb0_1 [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + int2 r0 = int2(int2(u0.get_width(), u0.get_height()) >> int2(uint2(4u))); + for (int i = 0; i < r0.y; i++) + { + for (int j = 0; j < r0.x; j++) + { + int2 param = r0; + int2 param_1 = int2(i, j); + u0.write(cb0_1._RESERVED_IDENTIFIER_FIXUP_m0[0].xxxx, uint2(get_texcoord(param, param_1, gl_LocalInvocationID))); + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp b/third_party/spirv-cross/reference/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp new file mode 100644 index 0000000..405556b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct cb1_struct +{ + float4 _RESERVED_IDENTIFIER_FIXUP_m0[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(16u, 16u, 1u); + +kernel void main0(constant cb1_struct& cb0_1 [[buffer(0)]], texture2d u0 [[texture(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + int2 r0 = int2(int2(u0.get_width(), u0.get_height()) >> int2(uint2(4u))); + for (int i = 0; i < r0.y; i++) + { + for (int j = 0; j < r0.x; j++) + { + u0.write(cb0_1._RESERVED_IDENTIFIER_FIXUP_m0[0].xxxx, uint2(((r0 * int3(gl_LocalInvocationID).xy) + int2(i, j)))); + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag new file mode 100644 index 0000000..703dd0a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTex [[texture(0)]], sampler uSampler [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample(uSampler, in.vUV); + out.FragColor += uTex.sample(uSampler, in.vUV, int2(1)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/default-member-names.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/default-member-names.asm.frag new file mode 100644 index 0000000..3628c4e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/default-member-names.asm.frag @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +struct _9 +{ + float _m0; +}; + +struct _10 +{ + float _m0; + float _m1; + float _m2; + float _m3; + float _m4; + float _m5; + float _m6; + float _m7; + float _m8; + float _m9; + float _m10; + float _m11; + _9 _m12; +}; + +struct main0_out +{ + float4 m_3 [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + _10 _21; + out.m_3 = float4(_21._m0, _21._m1, _21._m2, _21._m3); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag new file mode 100644 index 0000000..1870f67 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag @@ -0,0 +1,48 @@ +#include +#include + +using namespace metal; + +struct _4 +{ + float4 _m0; +}; + +struct _6 +{ + int _m0; +}; + +struct _7 +{ + float4 _m0; +}; + +struct main0_out +{ + float4 m_3 [[color(0)]]; +}; + +fragment main0_out main0(const device _4* _5_0 [[buffer(0)]], const device _4* _5_1 [[buffer(1)]], const device _4* _5_2 [[buffer(2)]], const device _4* _5_3 [[buffer(3)]], constant _6& _20 [[buffer(4)]], constant _7* _8_0 [[buffer(5)]], constant _7* _8_1 [[buffer(6)]], constant _7* _8_2 [[buffer(7)]], constant _7* _8_3 [[buffer(8)]]) +{ + const device _4* _5[] = + { + _5_0, + _5_1, + _5_2, + _5_3, + }; + + constant _7* _8[] = + { + _8_0, + _8_1, + _8_2, + _8_3, + }; + + main0_out out = {}; + out.m_3 = _5[_20._m0]->_m0 + (_8[_20._m0]->_m0 * float4(0.20000000298023223876953125)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag new file mode 100644 index 0000000..b64ccab --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 o1 [[color(1)]]; + float4 o3 [[color(3)]]; + float4 o6 [[color(6)]]; + float4 o7 [[color(7)]]; +}; + +fragment main0_out main0() +{ + float4 o0; + float4 o2; + float4 o4; + float4 o5; + float gl_FragDepth; + int gl_FragStencilRefARB; + main0_out out = {}; + o0 = float4(0.0, 0.0, 0.0, 1.0); + out.o1 = float4(1.0, 0.0, 0.0, 1.0); + o2 = float4(0.0, 1.0, 0.0, 1.0); + out.o3 = float4(0.0, 0.0, 1.0, 1.0); + o4 = float4(1.0, 0.0, 1.0, 0.5); + o5 = float4(0.25); + out.o6 = float4(0.75); + out.o7 = float4(1.0); + gl_FragDepth = 0.89999997615814208984375; + gl_FragStencilRefARB = uint(127); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..e30c586 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/empty-struct.asm.frag @@ -0,0 +1,29 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct EmptyStructTest +{ +}; +static inline __attribute__((always_inline)) +float GetValue(thread const EmptyStructTest& self) +{ + return 0.0; +} + +static inline __attribute__((always_inline)) +float GetValue_1(EmptyStructTest self) +{ + return 0.0; +} + +fragment void main0() +{ + EmptyStructTest emptyStruct; + float value = GetValue(emptyStruct); + value = GetValue_1(EmptyStructTest{ }); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag new file mode 100644 index 0000000..e926bc5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag @@ -0,0 +1,49 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Foo +{ + float3 a; + float b; +}; + +struct Foo_1 +{ + packed_float3 a; + float b; +}; + +struct buf +{ + Foo_1 results[16]; + float4 bar; +}; + +struct main0_out +{ + float4 _entryPointOutput [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 _main(thread const float4& pos, constant buf& v_11) +{ + int _46 = int(pos.x) % 16; + Foo foo; + foo.a = float3(v_11.results[_46].a); + foo.b = v_11.results[_46].b; + return float4(dot(foo.a, v_11.bar.xyz), foo.b, 0.0, 0.0); +} + +fragment main0_out main0(constant buf& v_11 [[buffer(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float4 pos = gl_FragCoord; + float4 param = pos; + out._entryPointOutput = _main(param, v_11); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/frem.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/frem.asm.frag new file mode 100644 index 0000000..ebc73d5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/frem.asm.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vA [[user(locn0)]]; + float4 vB [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = fmod(in.vA, in.vB); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..40fe5c5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,51 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 foo(thread const float4& foo_1) +{ + return foo_1 + float4(1.0); +} + +static inline __attribute__((always_inline)) +float4 foo(thread const float3& foo_1) +{ + return foo_1.xyzz + float4(1.0); +} + +static inline __attribute__((always_inline)) +float4 foo_1(thread const float4& foo_2) +{ + return foo_2 + float4(2.0); +} + +static inline __attribute__((always_inline)) +float4 foo(thread const float2& foo_2) +{ + return foo_2.xyxy + float4(2.0); +} + +fragment main0_out main0() +{ + main0_out out = {}; + float4 foo_3 = float4(1.0); + float4 foo_2 = foo(foo_3); + float3 foo_5 = float3(1.0); + float4 foo_4 = foo(foo_5); + float4 foo_7 = float4(1.0); + float4 foo_6 = foo_1(foo_7); + float2 foo_9 = float2(1.0); + float4 foo_8 = foo(foo_9); + out.FragColor = ((foo_2 + foo_4) + foo_6) + foo_8; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..0d691b3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + int2 Size [[color(0)]]; +}; + +fragment main0_out main0(texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.Size = int2(uTexture.get_width(), uTexture.get_height()) + int2(uTexture.get_width(1), uTexture.get_height(1)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..830df0c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,49 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 v0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uImage [[texture(0)]], sampler uImageSmplr [[sampler(0)]]) +{ + main0_out out = {}; + int i = 0; + float phi; + float4 _36; + phi = 1.0; + _36 = float4(1.0, 2.0, 1.0, 2.0); + for (;;) + { + out.FragColor = _36; + if (i < 4) + { + if (in.v0[i] > 0.0) + { + float2 _48 = float2(phi); + i++; + phi += 2.0; + _36 = uImage.sample(uImageSmplr, _48, level(0.0)); + continue; + } + else + { + break; + } + } + else + { + break; + } + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..8537dac --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float3 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float3(as_type(0x7f800000u), as_type(0xff800000u), as_type(0x7fc00000u)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag new file mode 100644 index 0000000..41472ad --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag @@ -0,0 +1,47 @@ +#include +#include + +using namespace metal; + +struct Input +{ + float2 v0; + float2 v1; + float3 v2; + float4 v3; + float v4; + float v5; + float v6; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 Input_v0 [[user(locn0)]]; + float2 Input_v1 [[user(locn1), center_no_perspective]]; + float3 Input_v2 [[user(locn2), centroid_perspective]]; + float4 Input_v3 [[user(locn3), centroid_no_perspective]]; + float Input_v4 [[user(locn4), sample_perspective]]; + float Input_v5 [[user(locn5), sample_no_perspective]]; + float Input_v6 [[user(locn6), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Input inp = {}; + inp.v0 = in.Input_v0; + inp.v1 = in.Input_v1; + inp.v2 = in.Input_v2; + inp.v3 = in.Input_v3; + inp.v4 = in.Input_v4; + inp.v5 = in.Input_v5; + inp.v6 = in.Input_v6; + out.FragColor = float4(inp.v0.x + inp.v1.y, inp.v2.xy, ((inp.v3.w * inp.v4) + inp.v5) - inp.v6); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..38fbd27 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,85 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float vColor [[user(locn0)]]; +}; + +#line 6 "test.frag" +static inline __attribute__((always_inline)) +void func(thread float& FragColor, thread float& vColor) +{ +#line 8 "test.frag" + FragColor = 1.0; +#line 9 "test.frag" + FragColor = 2.0; +#line 10 "test.frag" + if (vColor < 0.0) + { +#line 12 "test.frag" + FragColor = 3.0; + } + else + { +#line 16 "test.frag" + FragColor = 4.0; + } +#line 19 "test.frag" + for (int i = 0; float(i) < (40.0 + vColor); i += (int(vColor) + 5)) + { +#line 21 "test.frag" + FragColor += 0.20000000298023223876953125; +#line 22 "test.frag" + FragColor += 0.300000011920928955078125; + } +#line 25 "test.frag" + switch (int(vColor)) + { + case 0: + { +#line 28 "test.frag" + FragColor += 0.20000000298023223876953125; +#line 29 "test.frag" + break; + } + case 1: + { +#line 32 "test.frag" + FragColor += 0.4000000059604644775390625; +#line 33 "test.frag" + break; + } + default: + { +#line 36 "test.frag" + FragColor += 0.800000011920928955078125; +#line 37 "test.frag" + break; + } + } + do + { +#line 42 "test.frag" + FragColor += (10.0 + vColor); + } while (FragColor < 100.0); +} + +#line 46 "test.frag" +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; +#line 48 "test.frag" + func(out.FragColor, in.vColor); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/locations-components.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/locations-components.asm.frag new file mode 100644 index 0000000..2cebdec --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/locations-components.asm.frag @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 o0 [[color(0)]]; +}; + +struct main0_in +{ + float2 m_2 [[user(locn1)]]; + float m_3 [[user(locn1_2)]]; + float m_4 [[user(locn2), flat]]; + uint m_5 [[user(locn2_1)]]; + uint m_6 [[user(locn2_2)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 v1; + v1 = float4(in.m_2.x, in.m_2.y, v1.z, v1.w); + v1.z = in.m_3; + float4 v2; + v2.x = in.m_4; + v2.y = as_type(in.m_5); + v2.z = as_type(in.m_6); + float4 r0; + r0.x = as_type(as_type(v2.y) + as_type(v2.z)); + out.o0.y = float(as_type(r0.x)); + out.o0.x = v1.y + v2.x; + out.o0 = float4(out.o0.x, out.o0.y, v1.z, v1.x); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..4119353 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,92 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _46 = spvUnsafeArray({ 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0 }); +constant spvUnsafeArray _76 = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); +constant spvUnsafeArray _90 = spvUnsafeArray({ float4(20.0), float4(30.0), float4(50.0), float4(60.0) }); + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + spvUnsafeArray foobar = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); + spvUnsafeArray baz = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); + main0_out out = {}; + out.FragColor = _46[in.index]; + if (in.index < 10) + { + out.FragColor += _46[in.index ^ 1]; + } + else + { + out.FragColor += _46[in.index & 1]; + } + if (in.index > 30) + { + out.FragColor += _76[in.index & 3].y; + } + else + { + out.FragColor += _76[in.index & 1].x; + } + if (in.index > 30) + { + foobar[1].z = 20.0; + } + out.FragColor += foobar[in.index & 3].z; + baz = _90; + out.FragColor += baz[in.index & 3].z; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/min-lod.msl22.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/min-lod.msl22.asm.frag new file mode 100644 index 0000000..5193b2c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/min-lod.msl22.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uSampler.sample(uSamplerSmplr, in.vUV, min_lod_clamp(4.0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/op-constant-null.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/op-constant-null.asm.frag new file mode 100644 index 0000000..f8104f8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/op-constant-null.asm.frag @@ -0,0 +1,70 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct D +{ + float4 a; + float b; +}; + +constant spvUnsafeArray _14 = spvUnsafeArray({ float4(0.0), float4(0.0), float4(0.0), float4(0.0) }); + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + float a = 0.0; + float4 b = float4(0.0); + float2x3 c = float2x3(float3(0.0), float3(0.0)); + D d = D{ float4(0.0), 0.0 }; + out.FragColor = a; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/op-image-sampled-image.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/op-image-sampled-image.asm.frag new file mode 100644 index 0000000..972daee --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/op-image-sampled-image.asm.frag @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct push_cb +{ + float4 cb0[1]; +}; + +struct main0_out +{ + float4 o0 [[color(0)]]; +}; + +fragment main0_out main0(constant push_cb& _19 [[buffer(0)]], texture2d t0 [[texture(0)]], sampler dummy_sampler [[sampler(0)]]) +{ + main0_out out = {}; + float4 r0; + r0 = float4(_19.cb0[0u].z, _19.cb0[0u].w, r0.z, r0.w); + r0 = float4(r0.x, r0.y, float2(0.0).x, float2(0.0).y); + out.o0 = t0.read(uint2(as_type(r0.xy)) + uint2(int2(-1, -2)), as_type(r0.w)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..46648f6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,30 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Registers +{ + float foo; +}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float add_value(float v, float w) +{ + return v + w; +} + +fragment main0_out main0(constant Registers& registers [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = add_value(10.0, registers.foo); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/phi-loop-variable.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/phi-loop-variable.asm.frag new file mode 100644 index 0000000..036774d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/phi-loop-variable.asm.frag @@ -0,0 +1,12 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ + for (int _22 = 35; _22 >= 0; _22--) + { + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag new file mode 100644 index 0000000..b721eb2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag @@ -0,0 +1,187 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct _13 +{ + float4 x; + float4 y; + float4 z; + spvUnsafeArray u; + spvUnsafeArray v; + spvUnsafeArray w; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + interpolant foo [[user(locn0)]]; + interpolant bar [[user(locn1)]]; + interpolant baz [[user(locn2)]]; + int sid [[user(locn3)]]; + interpolant a_0 [[user(locn4)]]; + interpolant a_1 [[user(locn5)]]; + interpolant b_0 [[user(locn6)]]; + interpolant b_1 [[user(locn7)]]; + interpolant c_0 [[user(locn8)]]; + interpolant c_1 [[user(locn9)]]; + interpolant m_13_x [[user(locn10)]]; + interpolant m_13_y [[user(locn11)]]; + interpolant m_13_z [[user(locn12)]]; + interpolant m_13_u_0 [[user(locn13)]]; + interpolant m_13_u_1 [[user(locn14)]]; + interpolant m_13_v_0 [[user(locn15)]]; + interpolant m_13_v_1 [[user(locn16)]]; + interpolant m_13_w_0 [[user(locn17)]]; + interpolant m_13_w_1 [[user(locn18)]]; + interpolant m_13_w_2 [[user(locn19)]]; +}; + +static inline __attribute__((always_inline)) +void func(thread float4& FragColor, thread float2 baz, thread spvUnsafeArray (&a), thread _13& s, thread main0_in& in) +{ + float2 _237 = FragColor.xy + baz; + FragColor = float4(_237.x, _237.y, FragColor.z, FragColor.w); + FragColor.x += in.baz.interpolate_at_centroid().x; + FragColor.y += in.baz.interpolate_at_sample(3).y; + FragColor.z += in.baz.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375).y; + float2 _262 = FragColor.xy + in.a_1.interpolate_at_centroid(); + FragColor = float4(_262.x, _262.y, FragColor.z, FragColor.w); + float2 _269 = FragColor.xy + in.a_0.interpolate_at_sample(2); + FragColor = float4(_269.x, _269.y, FragColor.z, FragColor.w); + float2 _276 = FragColor.xy + in.a_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + FragColor = float4(_276.x, _276.y, FragColor.z, FragColor.w); + FragColor += s.z; + float2 _288 = FragColor.xy + in.m_13_z.interpolate_at_centroid().yy; + FragColor = float4(_288.x, _288.y, FragColor.z, FragColor.w); + float2 _296 = FragColor.yz + in.m_13_z.interpolate_at_sample(3).xy; + FragColor = float4(FragColor.x, _296.x, _296.y, FragColor.w); + float2 _304 = FragColor.zw + in.m_13_z.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375).wx; + FragColor = float4(FragColor.x, FragColor.y, _304.x, _304.y); + FragColor += s.u[0]; + FragColor += in.m_13_u_1.interpolate_at_centroid(); + FragColor += in.m_13_u_0.interpolate_at_sample(2); + FragColor += in.m_13_u_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); +} + +fragment main0_out main0(main0_in in [[stage_in]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + spvUnsafeArray a = {}; + _13 s = {}; + spvUnsafeArray b = {}; + spvUnsafeArray c = {}; + a[0] = in.a_0.interpolate_at_center(); + a[1] = in.a_1.interpolate_at_center(); + s.x = in.m_13_x.interpolate_at_center(); + s.y = in.m_13_y.interpolate_at_centroid(); + s.z = in.m_13_z.interpolate_at_sample(gl_SampleID); + s.u[0] = in.m_13_u_0.interpolate_at_centroid(); + s.u[1] = in.m_13_u_1.interpolate_at_centroid(); + s.v[0] = in.m_13_v_0.interpolate_at_sample(gl_SampleID); + s.v[1] = in.m_13_v_1.interpolate_at_sample(gl_SampleID); + s.w[0] = in.m_13_w_0.interpolate_at_center(); + s.w[1] = in.m_13_w_1.interpolate_at_center(); + s.w[2] = in.m_13_w_2.interpolate_at_center(); + b[0] = in.b_0.interpolate_at_centroid(); + b[1] = in.b_1.interpolate_at_centroid(); + c[0] = in.c_0.interpolate_at_sample(gl_SampleID); + c[1] = in.c_1.interpolate_at_sample(gl_SampleID); + out.FragColor = in.foo.interpolate_at_center(); + out.FragColor += in.foo.interpolate_at_centroid(); + out.FragColor += in.foo.interpolate_at_sample(in.sid); + out.FragColor += in.foo.interpolate_at_offset(float2(0.100000001490116119384765625) + 0.4375); + float3 _65 = out.FragColor.xyz + in.bar.interpolate_at_centroid(); + out.FragColor = float4(_65.x, _65.y, _65.z, out.FragColor.w); + float3 _71 = out.FragColor.xyz + in.bar.interpolate_at_centroid(); + out.FragColor = float4(_71.x, _71.y, _71.z, out.FragColor.w); + float3 _78 = out.FragColor.xyz + in.bar.interpolate_at_sample(in.sid); + out.FragColor = float4(_78.x, _78.y, _78.z, out.FragColor.w); + float3 _84 = out.FragColor.xyz + in.bar.interpolate_at_offset(float2(-0.100000001490116119384765625) + 0.4375); + out.FragColor = float4(_84.x, _84.y, _84.z, out.FragColor.w); + float2 _91 = out.FragColor.xy + b[0]; + out.FragColor = float4(_91.x, _91.y, out.FragColor.z, out.FragColor.w); + float2 _98 = out.FragColor.xy + in.b_1.interpolate_at_centroid(); + out.FragColor = float4(_98.x, _98.y, out.FragColor.z, out.FragColor.w); + float2 _105 = out.FragColor.xy + in.b_0.interpolate_at_sample(2); + out.FragColor = float4(_105.x, _105.y, out.FragColor.z, out.FragColor.w); + float2 _112 = out.FragColor.xy + in.b_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + out.FragColor = float4(_112.x, _112.y, out.FragColor.z, out.FragColor.w); + float2 _119 = out.FragColor.xy + c[0]; + out.FragColor = float4(_119.x, _119.y, out.FragColor.z, out.FragColor.w); + float2 _127 = out.FragColor.xy + in.c_1.interpolate_at_centroid().xy; + out.FragColor = float4(_127.x, _127.y, out.FragColor.z, out.FragColor.w); + float2 _135 = out.FragColor.xy + in.c_0.interpolate_at_sample(2).yx; + out.FragColor = float4(_135.x, _135.y, out.FragColor.z, out.FragColor.w); + float2 _143 = out.FragColor.xy + in.c_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375).xx; + out.FragColor = float4(_143.x, _143.y, out.FragColor.z, out.FragColor.w); + out.FragColor += s.x; + out.FragColor += in.m_13_x.interpolate_at_centroid(); + out.FragColor += in.m_13_x.interpolate_at_sample(in.sid); + out.FragColor += in.m_13_x.interpolate_at_offset(float2(0.100000001490116119384765625) + 0.4375); + out.FragColor += s.y; + out.FragColor += in.m_13_y.interpolate_at_centroid(); + out.FragColor += in.m_13_y.interpolate_at_sample(in.sid); + out.FragColor += in.m_13_y.interpolate_at_offset(float2(-0.100000001490116119384765625) + 0.4375); + float2 _184 = out.FragColor.xy + s.v[0]; + out.FragColor = float4(_184.x, _184.y, out.FragColor.z, out.FragColor.w); + float2 _191 = out.FragColor.xy + in.m_13_v_1.interpolate_at_centroid(); + out.FragColor = float4(_191.x, _191.y, out.FragColor.z, out.FragColor.w); + float2 _198 = out.FragColor.xy + in.m_13_v_0.interpolate_at_sample(2); + out.FragColor = float4(_198.x, _198.y, out.FragColor.z, out.FragColor.w); + float2 _205 = out.FragColor.xy + in.m_13_v_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + out.FragColor = float4(_205.x, _205.y, out.FragColor.z, out.FragColor.w); + out.FragColor.x += s.w[0]; + out.FragColor.x += in.m_13_w_1.interpolate_at_centroid(); + out.FragColor.x += in.m_13_w_0.interpolate_at_sample(2); + out.FragColor.x += in.m_13_w_1.interpolate_at_offset(float2(-0.100000001490116119384765625, 0.100000001490116119384765625) + 0.4375); + func(out.FragColor, in.baz.interpolate_at_sample(gl_SampleID), a, s, in); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..aed8fd3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float out_var_SV_Target [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d g_Texture [[texture(0)]], sampler g_Sampler [[sampler(0)]], sampler g_CompareSampler [[sampler(1)]]) +{ + main0_out out = {}; + out.out_var_SV_Target = float4(g_Texture.sample(g_Sampler, in.in_var_TEXCOORD0)).x + g_Texture.sample_compare(g_CompareSampler, in.in_var_TEXCOORD0, 0.5, level(0.0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..5432505 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,82 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct myType +{ + float data; +}; + +struct main0_out +{ + float4 o_color [[color(0)]]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +fragment main0_out main0(float4 gl_FragCoord [[position]]) +{ + spvUnsafeArray _21 = spvUnsafeArray({ myType{ 0.0 }, myType{ 1.0 }, myType{ 0.0 }, myType{ 1.0 }, myType{ 0.0 } }); + + main0_out out = {}; + float2 uv = gl_FragCoord.xy; + int index = int(mod(uv.x, 4.0)); + myType elt = _21[index]; + if (elt.data > 0.0) + { + out.o_color = float4(0.0, 1.0, 0.0, 1.0); + } + else + { + out.o_color = float4(1.0, 0.0, 0.0, 1.0); + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/srem.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/srem.asm.frag new file mode 100644 index 0000000..f0cdd57 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/srem.asm.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int4 vA [[user(locn0)]]; + int4 vB [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = float4(in.vA - in.vB * (in.vA / in.vB)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..d59013d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,65 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _20 = spvUnsafeArray({ float4(1.0, 2.0, 3.0, 4.0), float4(10.0) }); + +struct main0_out +{ + float4 FragColors_0 [[color(0)]]; + float4 FragColors_1 [[color(1)]]; + float4 FragColor [[color(2)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + spvUnsafeArray FragColors = spvUnsafeArray({ float4(1.0, 2.0, 3.0, 4.0), float4(10.0) }); + out.FragColor = float4(5.0); + out.FragColors_0 = FragColors[0]; + out.FragColors_1 = FragColors[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..dd308c3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = uTexture.read(uint2(int2(gl_FragCoord.xy)), 0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-atomics.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-atomics.asm.frag new file mode 100644 index 0000000..ab5be64 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-atomics.asm.frag @@ -0,0 +1,121 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_StructuredBuffer_v4float +{ + spvUnsafeArray _m0; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_v4float& CulledObjectBoxBounds [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d RWShadowTileNumCulledObjects [[texture(2)]], device atomic_uint* RWShadowTileNumCulledObjects_atomic [[buffer(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float((_Globals.ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2(_Globals.ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _107 = _103 + 1u; + if (all(CulledObjectBoxBounds._m0[_107].xy > _96.xy) && all(CulledObjectBoxBounds._m0[_103].xyz < _102)) + { + float _122 = _96.x; + float _123 = _96.y; + spvUnsafeArray _73; + _73[0] = float3(_122, _123, -1000.0); + float _126 = _100.x; + _73[1] = float3(_126, _123, -1000.0); + float _129 = _100.y; + _73[2] = float3(_122, _129, -1000.0); + _73[3] = float3(_126, _129, -1000.0); + _73[4] = float3(_122, _123, 1.0); + _73[5] = float3(_126, _123, 1.0); + _73[6] = float3(_122, _129, 1.0); + _73[7] = float3(_126, _129, 1.0); + float3 _155; + float3 _158; + _155 = float3(-500000.0); + _158 = float3(500000.0); + for (int _160 = 0; _160 < 8; ) + { + float3 _166 = _73[_160] - (float3(0.5) * (CulledObjectBoxBounds._m0[_103].xyz + CulledObjectBoxBounds._m0[_107].xyz)); + float3 _170 = float3(dot(_166, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + _155 = fast::max(_155, _170); + _158 = fast::min(_158, _170); + _160++; + continue; + } + if (all(_158 < float3(1.0)) && all(_155 > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&RWShadowTileNumCulledObjects_atomic[(_78 * _Globals.ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-atomics.asm.graphics-robust-access.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-atomics.asm.graphics-robust-access.frag new file mode 100644 index 0000000..ab5be64 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-atomics.asm.graphics-robust-access.frag @@ -0,0 +1,121 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_StructuredBuffer_v4float +{ + spvUnsafeArray _m0; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_v4float& CulledObjectBoxBounds [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d RWShadowTileNumCulledObjects [[texture(2)]], device atomic_uint* RWShadowTileNumCulledObjects_atomic [[buffer(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float((_Globals.ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2(_Globals.ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _107 = _103 + 1u; + if (all(CulledObjectBoxBounds._m0[_107].xy > _96.xy) && all(CulledObjectBoxBounds._m0[_103].xyz < _102)) + { + float _122 = _96.x; + float _123 = _96.y; + spvUnsafeArray _73; + _73[0] = float3(_122, _123, -1000.0); + float _126 = _100.x; + _73[1] = float3(_126, _123, -1000.0); + float _129 = _100.y; + _73[2] = float3(_122, _129, -1000.0); + _73[3] = float3(_126, _129, -1000.0); + _73[4] = float3(_122, _123, 1.0); + _73[5] = float3(_126, _123, 1.0); + _73[6] = float3(_122, _129, 1.0); + _73[7] = float3(_126, _129, 1.0); + float3 _155; + float3 _158; + _155 = float3(-500000.0); + _158 = float3(500000.0); + for (int _160 = 0; _160 < 8; ) + { + float3 _166 = _73[_160] - (float3(0.5) * (CulledObjectBoxBounds._m0[_103].xyz + CulledObjectBoxBounds._m0[_107].xyz)); + float3 _170 = float3(dot(_166, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + _155 = fast::max(_155, _170); + _158 = fast::min(_158, _170); + _160++; + continue; + } + if (all(_158 < float3(1.0)) && all(_155 > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&RWShadowTileNumCulledObjects_atomic[(_78 * _Globals.ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag new file mode 100644 index 0000000..9734582 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + half4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + half2 UV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = half4(uTexture.sample(uTextureSmplr, float2(in.UV))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/undef-variable-store.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/undef-variable-store.asm.frag new file mode 100644 index 0000000..2cefeb6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/undef-variable-store.asm.frag @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +constant float4 _38 = {}; +constant float4 _47 = {}; + +struct main0_out +{ + float4 _entryPointOutput [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + float4 _27; + do + { + float2 _26 = float2(0.0); + if (_26.x != 0.0) + { + _27 = float4(1.0, 0.0, 0.0, 1.0); + break; + } + else + { + _27 = float4(1.0, 1.0, 0.0, 1.0); + break; + } + _27 = _38; + break; + } while (false); + out._entryPointOutput = _27; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000..01a0e5a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,36 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float sample_combined(thread float3& vUV, thread depth2d uShadow, thread const sampler uShadowSmplr) +{ + return uShadow.sample_compare(uShadowSmplr, vUV.xy, vUV.z); +} + +static inline __attribute__((always_inline)) +float sample_separate(thread float3& vUV, thread depth2d uTexture, thread sampler uSampler) +{ + return uTexture.sample_compare(uSampler, vUV.xy, vUV.z); +} + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uShadow [[texture(0)]], depth2d uTexture [[texture(1)]], sampler uShadowSmplr [[sampler(0)]], sampler uSampler [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = sample_combined(in.vUV, uShadow, uShadowSmplr) + sample_separate(in.vUV, uTexture, uSampler); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/unord-relational-op.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/unord-relational-op.asm.frag new file mode 100644 index 0000000..8df57c5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/unord-relational-op.asm.frag @@ -0,0 +1,58 @@ +#include +#include + +using namespace metal; + +constant float a_tmp [[function_constant(1)]]; +constant float a = is_function_constant_defined(a_tmp) ? a_tmp : 1.0; +constant float b_tmp [[function_constant(2)]]; +constant float b = is_function_constant_defined(b_tmp) ? b_tmp : 2.0; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 c [[user(locn2)]]; + float2 d [[user(locn3)]]; + float3 e [[user(locn4)]]; + float3 f [[user(locn5)]]; + float4 g [[user(locn6)]]; + float4 h [[user(locn7)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float t0 = a; + float t1 = b; + bool c1 = (isunordered(a, b) || a == b); + bool c2 = (isunordered(a, b) || a != b); + bool c3 = (isunordered(a, b) || a < b); + bool c4 = (isunordered(a, b) || a > b); + bool c5 = (isunordered(a, b) || a <= b); + bool c6 = (isunordered(a, b) || a >= b); + bool2 c7 = (isunordered(in.c, in.d) || in.c == in.d); + bool2 c8 = (isunordered(in.c, in.d) || in.c != in.d); + bool2 c9 = (isunordered(in.c, in.d) || in.c < in.d); + bool2 c10 = (isunordered(in.c, in.d) || in.c > in.d); + bool2 c11 = (isunordered(in.c, in.d) || in.c <= in.d); + bool2 c12 = (isunordered(in.c, in.d) || in.c >= in.d); + bool3 c13 = (isunordered(in.e, in.f) || in.e == in.f); + bool3 c14 = (isunordered(in.e, in.f) || in.e != in.f); + bool3 c15 = (isunordered(in.e, in.f) || in.e < in.f); + bool3 c16 = (isunordered(in.e, in.f) || in.e > in.f); + bool3 c17 = (isunordered(in.e, in.f) || in.e <= in.f); + bool3 c18 = (isunordered(in.e, in.f) || in.e >= in.f); + bool4 c19 = (isunordered(in.g, in.h) || in.g == in.h); + bool4 c20 = (isunordered(in.g, in.h) || in.g != in.h); + bool4 c21 = (isunordered(in.g, in.h) || in.g < in.h); + bool4 c22 = (isunordered(in.g, in.h) || in.g > in.h); + bool4 c23 = (isunordered(in.g, in.h) || in.g <= in.h); + bool4 c24 = (isunordered(in.g, in.h) || in.g >= in.h); + out.FragColor = float4(t0 + t1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..7ae4aa5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/unreachable.asm.frag @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +constant float4 _21 = {}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int counter [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 _24; + _24 = _21; + float4 _33; + for (;;) + { + if (in.counter == 10) + { + _33 = float4(10.0); + break; + } + else + { + _33 = float4(30.0); + break; + } + } + out.FragColor = _33; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag b/third_party/spirv-cross/reference/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag new file mode 100644 index 0000000..e23fa81 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag @@ -0,0 +1,351 @@ +#include +#include + +using namespace metal; + +struct _28 +{ + float4 _m0; +}; + +struct _6 +{ + float4 _m0; + float _m1; + float4 _m2; +}; + +struct _10 +{ + float3 _m0; + packed_float3 _m1; + float _m2; + packed_float3 _m3; + float _m4; + packed_float3 _m5; + float _m6; + packed_float3 _m7; + float _m8; + packed_float3 _m9; + float _m10; + packed_float3 _m11; + float _m12; + float2 _m13; + float2 _m14; + packed_float3 _m15; + float _m16; + float _m17; + float _m18; + float _m19; + float _m20; + float4 _m21; + float4 _m22; + float4x4 _m23; + float4 _m24; +}; + +struct _18 +{ + float4x4 _m0; + float4x4 _m1; + float4x4 _m2; + float4x4 _m3; + float4 _m4; + float4 _m5; + float _m6; + float _m7; + float _m8; + float _m9; + packed_float3 _m10; + float _m11; + packed_float3 _m12; + float _m13; + packed_float3 _m14; + float _m15; + packed_float3 _m16; + float _m17; + float _m18; + float _m19; + float2 _m20; + float2 _m21; + float2 _m22; + float4 _m23; + float2 _m24; + float2 _m25; + float2 _m26; + char _m27_pad[8]; + packed_float3 _m27; + float _m28; + float _m29; + float _m30; + float _m31; + float _m32; + float2 _m33; + float _m34; + float _m35; + float3 _m36; + float4x4 _m37[2]; + float4 _m38[2]; +}; + +struct _20 +{ + float4 _m0; + float4 _m1; + float2 _m2; + float2 _m3; + float3 _m4; + float _m5; + float3 _m6; + float _m7; + float4 _m8; + float4 _m9; + float4 _m10; + float3 _m11; + float _m12; + float3 _m13; + float _m14; + float3 _m15; + float4 _m16; + float3 _m17; + float _m18; + float3 _m19; + float2 _m20; +}; + +struct _21 +{ + float4 _m0; +}; + +constant _28 _74 = {}; + +struct main0_out +{ + float4 m_5 [[color(0)]]; +}; + +fragment main0_out main0(constant _6& _7 [[buffer(0)]], constant _10& _11 [[buffer(1)]], constant _18& _19 [[buffer(2)]], texture2d _8 [[texture(0)]], texture2d _12 [[texture(1)]], texture2d _14 [[texture(2)]], sampler _9 [[sampler(0)]], sampler _13 [[sampler(1)]], sampler _15 [[sampler(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + _28 _77 = _74; + _77._m0 = float4(0.0); + float2 _82 = gl_FragCoord.xy * _19._m23.xy; + float4 _88 = _7._m2 * _7._m0.xyxy; + float2 _97 = fast::clamp(_82 + (float3(0.0, -2.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _109 = float3(_11._m5) * fast::clamp(_8.sample(_9, _97, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _113 = _12.sample(_13, _97, level(0.0)); + float3 _129; + if (_113.y > 0.0) + { + _129 = _109 + (_14.sample(_15, _97, level(0.0)).xyz * fast::clamp(_113.y * _113.z, 0.0, 1.0)); + } + else + { + _129 = _109; + } + float3 _133 = float4(0.0).xyz + (_129 * 0.5); + float4 _134 = float4(_133.x, _133.y, _133.z, float4(0.0).w); + _28 _135 = _77; + _135._m0 = _134; + float2 _144 = fast::clamp(_82 + (float3(-1.0, -1.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _156 = float3(_11._m5) * fast::clamp(_8.sample(_9, _144, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _160 = _12.sample(_13, _144, level(0.0)); + float3 _176; + if (_160.y > 0.0) + { + _176 = _156 + (_14.sample(_15, _144, level(0.0)).xyz * fast::clamp(_160.y * _160.z, 0.0, 1.0)); + } + else + { + _176 = _156; + } + float3 _180 = _134.xyz + (_176 * 0.5); + float4 _181 = float4(_180.x, _180.y, _180.z, _134.w); + _28 _182 = _135; + _182._m0 = _181; + float2 _191 = fast::clamp(_82 + (float3(0.0, -1.0, 0.75).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _203 = float3(_11._m5) * fast::clamp(_8.sample(_9, _191, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _207 = _12.sample(_13, _191, level(0.0)); + float3 _223; + if (_207.y > 0.0) + { + _223 = _203 + (_14.sample(_15, _191, level(0.0)).xyz * fast::clamp(_207.y * _207.z, 0.0, 1.0)); + } + else + { + _223 = _203; + } + float3 _227 = _181.xyz + (_223 * 0.75); + float4 _228 = float4(_227.x, _227.y, _227.z, _181.w); + _28 _229 = _182; + _229._m0 = _228; + float2 _238 = fast::clamp(_82 + (float3(1.0, -1.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _250 = float3(_11._m5) * fast::clamp(_8.sample(_9, _238, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _254 = _12.sample(_13, _238, level(0.0)); + float3 _270; + if (_254.y > 0.0) + { + _270 = _250 + (_14.sample(_15, _238, level(0.0)).xyz * fast::clamp(_254.y * _254.z, 0.0, 1.0)); + } + else + { + _270 = _250; + } + float3 _274 = _228.xyz + (_270 * 0.5); + float4 _275 = float4(_274.x, _274.y, _274.z, _228.w); + _28 _276 = _229; + _276._m0 = _275; + float2 _285 = fast::clamp(_82 + (float3(-2.0, 0.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _297 = float3(_11._m5) * fast::clamp(_8.sample(_9, _285, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _301 = _12.sample(_13, _285, level(0.0)); + float3 _317; + if (_301.y > 0.0) + { + _317 = _297 + (_14.sample(_15, _285, level(0.0)).xyz * fast::clamp(_301.y * _301.z, 0.0, 1.0)); + } + else + { + _317 = _297; + } + float3 _321 = _275.xyz + (_317 * 0.5); + float4 _322 = float4(_321.x, _321.y, _321.z, _275.w); + _28 _323 = _276; + _323._m0 = _322; + float2 _332 = fast::clamp(_82 + (float3(-1.0, 0.0, 0.75).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _344 = float3(_11._m5) * fast::clamp(_8.sample(_9, _332, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _348 = _12.sample(_13, _332, level(0.0)); + float3 _364; + if (_348.y > 0.0) + { + _364 = _344 + (_14.sample(_15, _332, level(0.0)).xyz * fast::clamp(_348.y * _348.z, 0.0, 1.0)); + } + else + { + _364 = _344; + } + float3 _368 = _322.xyz + (_364 * 0.75); + float4 _369 = float4(_368.x, _368.y, _368.z, _322.w); + _28 _370 = _323; + _370._m0 = _369; + float2 _379 = fast::clamp(_82 + (float3(0.0, 0.0, 1.0).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _391 = float3(_11._m5) * fast::clamp(_8.sample(_9, _379, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _395 = _12.sample(_13, _379, level(0.0)); + float3 _411; + if (_395.y > 0.0) + { + _411 = _391 + (_14.sample(_15, _379, level(0.0)).xyz * fast::clamp(_395.y * _395.z, 0.0, 1.0)); + } + else + { + _411 = _391; + } + float3 _415 = _369.xyz + (_411 * 1.0); + float4 _416 = float4(_415.x, _415.y, _415.z, _369.w); + _28 _417 = _370; + _417._m0 = _416; + float2 _426 = fast::clamp(_82 + (float3(1.0, 0.0, 0.75).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _438 = float3(_11._m5) * fast::clamp(_8.sample(_9, _426, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _442 = _12.sample(_13, _426, level(0.0)); + float3 _458; + if (_442.y > 0.0) + { + _458 = _438 + (_14.sample(_15, _426, level(0.0)).xyz * fast::clamp(_442.y * _442.z, 0.0, 1.0)); + } + else + { + _458 = _438; + } + float3 _462 = _416.xyz + (_458 * 0.75); + float4 _463 = float4(_462.x, _462.y, _462.z, _416.w); + _28 _464 = _417; + _464._m0 = _463; + float2 _473 = fast::clamp(_82 + (float3(2.0, 0.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _485 = float3(_11._m5) * fast::clamp(_8.sample(_9, _473, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _489 = _12.sample(_13, _473, level(0.0)); + float3 _505; + if (_489.y > 0.0) + { + _505 = _485 + (_14.sample(_15, _473, level(0.0)).xyz * fast::clamp(_489.y * _489.z, 0.0, 1.0)); + } + else + { + _505 = _485; + } + float3 _509 = _463.xyz + (_505 * 0.5); + float4 _510 = float4(_509.x, _509.y, _509.z, _463.w); + _28 _511 = _464; + _511._m0 = _510; + float2 _520 = fast::clamp(_82 + (float3(-1.0, 1.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _532 = float3(_11._m5) * fast::clamp(_8.sample(_9, _520, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _536 = _12.sample(_13, _520, level(0.0)); + float3 _552; + if (_536.y > 0.0) + { + _552 = _532 + (_14.sample(_15, _520, level(0.0)).xyz * fast::clamp(_536.y * _536.z, 0.0, 1.0)); + } + else + { + _552 = _532; + } + float3 _556 = _510.xyz + (_552 * 0.5); + float4 _557 = float4(_556.x, _556.y, _556.z, _510.w); + _28 _558 = _511; + _558._m0 = _557; + float2 _567 = fast::clamp(_82 + (float3(0.0, 1.0, 0.75).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _579 = float3(_11._m5) * fast::clamp(_8.sample(_9, _567, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _583 = _12.sample(_13, _567, level(0.0)); + float3 _599; + if (_583.y > 0.0) + { + _599 = _579 + (_14.sample(_15, _567, level(0.0)).xyz * fast::clamp(_583.y * _583.z, 0.0, 1.0)); + } + else + { + _599 = _579; + } + float3 _603 = _557.xyz + (_599 * 0.75); + float4 _604 = float4(_603.x, _603.y, _603.z, _557.w); + _28 _605 = _558; + _605._m0 = _604; + float2 _614 = fast::clamp(_82 + (float3(1.0, 1.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _626 = float3(_11._m5) * fast::clamp(_8.sample(_9, _614, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _630 = _12.sample(_13, _614, level(0.0)); + float3 _646; + if (_630.y > 0.0) + { + _646 = _626 + (_14.sample(_15, _614, level(0.0)).xyz * fast::clamp(_630.y * _630.z, 0.0, 1.0)); + } + else + { + _646 = _626; + } + float3 _650 = _604.xyz + (_646 * 0.5); + float4 _651 = float4(_650.x, _650.y, _650.z, _604.w); + _28 _652 = _605; + _652._m0 = _651; + float2 _661 = fast::clamp(_82 + (float3(0.0, 2.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + float3 _673 = float3(_11._m5) * fast::clamp(_8.sample(_9, _661, level(0.0)).w * _7._m1, 0.0, 1.0); + float4 _677 = _12.sample(_13, _661, level(0.0)); + float3 _693; + if (_677.y > 0.0) + { + _693 = _673 + (_14.sample(_15, _661, level(0.0)).xyz * fast::clamp(_677.y * _677.z, 0.0, 1.0)); + } + else + { + _693 = _673; + } + float3 _697 = _651.xyz + (_693 * 0.5); + float4 _698 = float4(_697.x, _697.y, _697.z, _651.w); + _28 _699 = _652; + _699._m0 = _698; + float3 _702 = _698.xyz / float3(((((((((((((0.0 + 0.5) + 0.5) + 0.75) + 0.5) + 0.5) + 0.75) + 1.0) + 0.75) + 0.5) + 0.5) + 0.75) + 0.5) + 0.5); + _28 _704 = _699; + _704._m0 = float4(_702.x, _702.y, _702.z, _698.w); + _28 _705 = _704; + _705._m0.w = 1.0; + out.m_5 = _705._m0; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc b/third_party/spirv-cross/reference/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc new file mode 100644 index 0000000..6c9903e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct TessLevels +{ + float inner0; + float inner1; + float outer0; + float outer1; + float outer2; + float outer3; +}; + +kernel void main0(const device TessLevels& sb_levels [[buffer(0)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]]) +{ + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(sb_levels.inner0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(sb_levels.outer0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(sb_levels.outer1); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(sb_levels.outer2); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc b/third_party/spirv-cross/reference/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc new file mode 100644 index 0000000..a5f316d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct TessLevels +{ + float inner0; + float inner1; + float outer0; + float outer1; + float outer2; + float outer3; +}; + +kernel void main0(const device TessLevels& sb_levels [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]]) +{ + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1]); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(sb_levels.inner0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(sb_levels.outer0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(sb_levels.outer1); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(sb_levels.outer2); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese b/third_party/spirv-cross/reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese new file mode 100644 index 0000000..83ef729 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float2 gl_TessLevelInner [[attribute(0)]]; + float4 gl_TessLevelOuter [[attribute(1)]]; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + out.gl_Position = float4(((gl_TessCoord.x * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.x) + (((1.0 - gl_TessCoord.x) * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.z), ((gl_TessCoord.y * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.y) + (((1.0 - gl_TessCoord.y) * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.w), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert new file mode 100644 index 0000000..ae42798 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert @@ -0,0 +1,49 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct VSOut +{ + float4 pos; + float2 clip; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +struct main0_in +{ + float4 pos [[attribute(0)]]; +}; + +static inline __attribute__((always_inline)) +VSOut _main(thread const float4& pos) +{ + VSOut vout; + vout.pos = pos; + vout.clip = pos.xy; + return vout; +} + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 pos = in.pos; + float4 param = pos; + VSOut flattenTemp = _main(param); + out.gl_Position = flattenTemp.pos; + out.gl_ClipDistance[0] = flattenTemp.clip.x; + out.gl_ClipDistance[1] = flattenTemp.clip.y; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert new file mode 100644 index 0000000..c1e59b9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert @@ -0,0 +1,45 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct VSOut +{ + float4 pos; + float2 clip; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; +}; + +struct main0_in +{ + float4 pos [[attribute(0)]]; +}; + +static inline __attribute__((always_inline)) +VSOut _main(thread const float4& pos) +{ + VSOut vout; + vout.pos = pos; + vout.clip = pos.xy; + return vout; +} + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 pos = in.pos; + float4 param = pos; + VSOut flattenTemp = _main(param); + out.gl_Position = flattenTemp.pos; + out.gl_ClipDistance[0] = flattenTemp.clip.x; + out.gl_ClipDistance[1] = flattenTemp.clip.y; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/copy-memory-interface.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/copy-memory-interface.asm.vert new file mode 100644 index 0000000..63ab796 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/copy-memory-interface.asm.vert @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 o1 [[user(locn1)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 v0 [[attribute(0)]]; + float4 v1 [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.v0; + out.o1 = in.v1; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..b94687a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,66 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct V2F +{ + float4 Position; + float4 Color; +}; + +struct InstanceData +{ + float4x4 MATRIX_MVP; + float4 Color; +}; + +struct InstanceData_1 +{ + float4x4 MATRIX_MVP; + float4 Color; +}; + +struct gInstanceData +{ + InstanceData_1 _data[1]; +}; + +struct main0_out +{ + float4 _entryPointOutput_Color [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 PosL [[attribute(0)]]; +}; + +static inline __attribute__((always_inline)) +V2F _VS(thread const float3& PosL, thread const uint& instanceID, const device gInstanceData& gInstanceData_1) +{ + InstanceData instData; + instData.MATRIX_MVP = transpose(gInstanceData_1._data[instanceID].MATRIX_MVP); + instData.Color = gInstanceData_1._data[instanceID].Color; + V2F v2f; + v2f.Position = instData.MATRIX_MVP * float4(PosL, 1.0); + v2f.Color = instData.Color; + return v2f; +} + +vertex main0_out main0(main0_in in [[stage_in]], const device gInstanceData& gInstanceData_1 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + float3 PosL = in.PosL; + uint instanceID = gl_InstanceIndex; + float3 param = PosL; + uint param_1 = instanceID; + V2F flattenTemp = _VS(param, param_1, gInstanceData_1); + out.gl_Position = flattenTemp.Position; + out._entryPointOutput_Color = flattenTemp.Color; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/fake-builtin-input.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/fake-builtin-input.asm.vert new file mode 100644 index 0000000..f9fcbc8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/fake-builtin-input.asm.vert @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float2 in_var_POSITION [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = float4(in.in_var_POSITION, 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/invariant.msl21.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/invariant.msl21.asm.vert new file mode 100644 index 0000000..d74c43b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/invariant.msl21.asm.vert @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position, invariant]]; +}; + +static inline __attribute__((always_inline)) +float4 _main() +{ + return float4(1.0); +} + +vertex main0_out main0() +{ + main0_out out = {}; + float4 _14 = _main(); + out.gl_Position = _14; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert new file mode 100644 index 0000000..1926ff9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Struct +{ + uint flags[1]; +}; + +struct defaultUniformsVS +{ + Struct flags; + float4 uquad[4]; + float4x4 umatrix; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant defaultUniformsVS& _9 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.gl_Position = _9.umatrix * float4(_9.uquad[int(gl_VertexIndex)].x, _9.uquad[int(gl_VertexIndex)].y, in.a_position.z, in.a_position.w); + if (_9.flags.flags[0] != 0u) + { + out.gl_Position.z = 0.0; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert new file mode 100644 index 0000000..ee20638 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Struct +{ + uint2 flags[1]; +}; + +struct defaultUniformsVS +{ + Struct flags; + float4 uquad[4]; + float4x4 umatrix; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant defaultUniformsVS& _9 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.gl_Position = _9.umatrix * float4(_9.uquad[int(gl_VertexIndex)].x, _9.uquad[int(gl_VertexIndex)].y, in.a_position.z, in.a_position.w); + if (_9.flags.flags[0].x != 0u) + { + out.gl_Position.z = 0.0; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/packing-test.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/packing-test.asm.vert new file mode 100644 index 0000000..ac2d30c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/packing-test.asm.vert @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct TestStruct +{ + float4x4 transforms[6]; +}; + +struct CB0 +{ + TestStruct CB0[16]; +}; + +vertex void main0() +{ +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert new file mode 100644 index 0000000..05a69e4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +constant int _7_tmp [[function_constant(201)]]; +constant int _7 = is_function_constant_defined(_7_tmp) ? _7_tmp : -10; +constant int _20 = (_7 + 2); +constant uint _8_tmp [[function_constant(202)]]; +constant uint _8 = is_function_constant_defined(_8_tmp) ? _8_tmp : 100u; +constant uint _25 = (_8 % 5u); +constant int4 _30 = int4(20, 30, _20, _20); +constant int2 _32 = int2(_30.y, _30.x); +constant int _33 = _30.y; +constant float _9_tmp [[function_constant(200)]]; +constant float _9 = is_function_constant_defined(_9_tmp) ? _9_tmp : 3.141590118408203125; + +struct main0_out +{ + int m_4 [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + float4 pos = float4(0.0); + pos.y += float(_20); + pos.z += float(_25); + pos += float4(_30); + float2 _56 = pos.xy + float2(_32); + pos = float4(_56.x, _56.y, pos.z, pos.w); + out.gl_Position = pos; + out.m_4 = _33; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..30df905 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,29 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +static inline __attribute__((always_inline)) +float4 _main(thread const uint& vid, thread const uint& iid) +{ + return float4(float(vid + iid)); +} + +vertex main0_out main0(uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + uint vid = gl_VertexIndex; + uint iid = gl_InstanceIndex; + uint param = vid; + uint param_1 = iid; + out.gl_Position = _main(param, param_1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/access-private-workgroup-in-function.comp b/third_party/spirv-cross/reference/shaders-msl/comp/access-private-workgroup-in-function.comp new file mode 100644 index 0000000..85185e6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/access-private-workgroup-in-function.comp @@ -0,0 +1,38 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +void set_f(thread int& f) +{ + f = 40; +} + +static inline __attribute__((always_inline)) +void set_shared_u(threadgroup int& u) +{ + u = 50; +} + +kernel void main0(uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + threadgroup int u; + int f; + set_f(f); + set_shared_u(u); + if (gl_LocalInvocationIndex == 0u) + { + f = 10; + } + else + { + f = 30; + u = 20; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp b/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp new file mode 100644 index 0000000..18cfd68 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct SSBO3 +{ + float4 v; +}; + +struct SSBO0 +{ + float4 v; +}; + +struct SSBO1 +{ + float4 v; +}; + +struct SSBO2 +{ + float4 v; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +struct spvDescriptorSetBuffer0 +{ + const device SSBO0* ssbo0 [[id(0)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + const device SSBO1* ssbo1 [[id(0)]]; +}; + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], device SSBO3& ssbo3 [[buffer(2)]], const device SSBO2& ssbo2 [[buffer(3)]]) +{ + ssbo3.v = ((*spvDescriptorSet0.ssbo0).v + (*spvDescriptorSet1.ssbo1).v) + ssbo2.v; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp b/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp new file mode 100644 index 0000000..c23a9d1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp @@ -0,0 +1,11 @@ +#include +#include + +using namespace metal; + +kernel void main0(texture2d uImage [[texture(0)]], texture2d uImageRead [[texture(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + int2 coord = int2(gl_GlobalInvocationID.xy); + uImage.write(uImageRead.read(uint2(coord)), uint2(coord)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp b/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp new file mode 100644 index 0000000..9bd15ac --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct spvDescriptorSetBuffer0 +{ + texture2d uImage [[id(0)]]; + texture2d uImageRead [[id(1)]]; +}; + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + int2 coord = int2(gl_GlobalInvocationID.xy); + spvDescriptorSet0.uImage.write(spvDescriptorSet0.uImageRead.read(uint2(coord)), uint2(coord)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/array-length.comp b/third_party/spirv-cross/reference/shaders-msl/comp/array-length.comp new file mode 100644 index 0000000..9ac8c82 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/array-length.comp @@ -0,0 +1,39 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + uint size; + float4 v[1]; +}; + +struct SSBO1 +{ + float bz[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +uint get_size(device SSBO& v_14, constant uint& v_14BufferSize, device SSBO1* (&ssbos)[2], constant uint* ssbosBufferSize) +{ + return uint(int((v_14BufferSize - 16) / 16) + int((ssbosBufferSize[1] - 0) / 4)); +} + +kernel void main0(constant uint* spvBufferSizeConstants [[buffer(25)]], device SSBO& v_14 [[buffer(0)]], device SSBO1* ssbos_0 [[buffer(1)]], device SSBO1* ssbos_1 [[buffer(2)]]) +{ + device SSBO1* ssbos[] = + { + ssbos_0, + ssbos_1, + }; + + constant uint& v_14BufferSize = spvBufferSizeConstants[0]; + constant uint* ssbosBufferSize = &spvBufferSizeConstants[1]; + v_14.size = get_size(v_14, v_14BufferSize, ssbos, ssbosBufferSize); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/array-length.msl2.argument.discrete.comp b/third_party/spirv-cross/reference/shaders-msl/comp/array-length.msl2.argument.discrete.comp new file mode 100644 index 0000000..aa1f442 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/array-length.msl2.argument.discrete.comp @@ -0,0 +1,68 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + uint size; + float4 v[1]; +}; + +struct SSBO1 +{ + float bz[1]; +}; + +struct SSBO2 +{ + uint size2; + float4 w[1]; +}; + +struct SSBO3 +{ + float bz[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +struct spvDescriptorSetBuffer0 +{ + device SSBO* v_16 [[id(0)]]; + constant uint* spvBufferSizeConstants [[id(1)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + device SSBO1* ssbos [[id(0)]][2]; + constant uint* spvBufferSizeConstants [[id(2)]]; +}; + +static inline __attribute__((always_inline)) +uint get_size(device SSBO& v_16, constant uint& v_16BufferSize, device SSBO1* constant (&ssbos)[2], constant uint* ssbosBufferSize, device SSBO2& v_38, constant uint& v_38BufferSize, device SSBO3* (&ssbos2)[2], constant uint* ssbos2BufferSize) +{ + uint len = uint(int((v_16BufferSize - 16) / 16)); + len += uint(int((ssbosBufferSize[1] - 0) / 4)); + len += uint(int((v_38BufferSize - 16) / 16)); + len += uint(int((ssbos2BufferSize[0] - 0) / 4)); + return len; +} + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], constant uint* spvBufferSizeConstants [[buffer(25)]], device SSBO2& v_38 [[buffer(2)]], device SSBO3* ssbos2_0 [[buffer(3)]], device SSBO3* ssbos2_1 [[buffer(4)]]) +{ + device SSBO3* ssbos2[] = + { + ssbos2_0, + ssbos2_1, + }; + + constant uint& spvDescriptorSet0_v_16BufferSize = spvDescriptorSet0.spvBufferSizeConstants[0]; + constant uint* spvDescriptorSet1_ssbosBufferSize = &spvDescriptorSet1.spvBufferSizeConstants[0]; + constant uint& v_38BufferSize = spvBufferSizeConstants[2]; + constant uint* ssbos2BufferSize = &spvBufferSizeConstants[3]; + (*spvDescriptorSet0.v_16).size = get_size((*spvDescriptorSet0.v_16), spvDescriptorSet0_v_16BufferSize, spvDescriptorSet1.ssbos, spvDescriptorSet1_ssbosBufferSize, v_38, v_38BufferSize, ssbos2, ssbos2BufferSize); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/atomic.comp b/third_party/spirv-cross/reference/shaders-msl/comp/atomic.comp new file mode 100644 index 0000000..fca72bf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/atomic.comp @@ -0,0 +1,72 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + uint u32; + int i32; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& ssbo [[buffer(0)]]) +{ + threadgroup uint shared_u32; + threadgroup int shared_i32; + uint _16 = atomic_fetch_add_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _18 = atomic_fetch_or_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _20 = atomic_fetch_xor_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _22 = atomic_fetch_and_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _24 = atomic_fetch_min_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _26 = atomic_fetch_max_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _28 = atomic_exchange_explicit((device atomic_uint*)&ssbo.u32, 1u, memory_order_relaxed); + uint _32; + do + { + _32 = 10u; + } while (!atomic_compare_exchange_weak_explicit((device atomic_uint*)&ssbo.u32, &_32, 2u, memory_order_relaxed, memory_order_relaxed) && _32 == 10u); + int _36 = atomic_fetch_add_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _38 = atomic_fetch_or_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _40 = atomic_fetch_xor_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _42 = atomic_fetch_and_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _44 = atomic_fetch_min_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _46 = atomic_fetch_max_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _48 = atomic_exchange_explicit((device atomic_int*)&ssbo.i32, 1, memory_order_relaxed); + int _52; + do + { + _52 = 10; + } while (!atomic_compare_exchange_weak_explicit((device atomic_int*)&ssbo.i32, &_52, 2, memory_order_relaxed, memory_order_relaxed) && _52 == 10); + shared_u32 = 10u; + shared_i32 = 10; + uint _57 = atomic_fetch_add_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _58 = atomic_fetch_or_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _59 = atomic_fetch_xor_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _60 = atomic_fetch_and_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _61 = atomic_fetch_min_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _62 = atomic_fetch_max_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _63 = atomic_exchange_explicit((threadgroup atomic_uint*)&shared_u32, 1u, memory_order_relaxed); + uint _64; + do + { + _64 = 10u; + } while (!atomic_compare_exchange_weak_explicit((threadgroup atomic_uint*)&shared_u32, &_64, 2u, memory_order_relaxed, memory_order_relaxed) && _64 == 10u); + int _65 = atomic_fetch_add_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _66 = atomic_fetch_or_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _67 = atomic_fetch_xor_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _68 = atomic_fetch_and_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _69 = atomic_fetch_min_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _70 = atomic_fetch_max_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _71 = atomic_exchange_explicit((threadgroup atomic_int*)&shared_i32, 1, memory_order_relaxed); + int _72; + do + { + _72 = 10; + } while (!atomic_compare_exchange_weak_explicit((threadgroup atomic_int*)&shared_i32, &_72, 2, memory_order_relaxed, memory_order_relaxed) && _72 == 10); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/barriers.comp b/third_party/spirv-cross/reference/shaders-msl/comp/barriers.comp new file mode 100644 index 0000000..8281390 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/barriers.comp @@ -0,0 +1,90 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(4u, 1u, 1u); + +static inline __attribute__((always_inline)) +void barrier_shared() +{ + threadgroup_barrier(mem_flags::mem_threadgroup); +} + +static inline __attribute__((always_inline)) +void full_barrier() +{ + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); +} + +static inline __attribute__((always_inline)) +void image_barrier() +{ + threadgroup_barrier(mem_flags::mem_texture); +} + +static inline __attribute__((always_inline)) +void buffer_barrier() +{ + threadgroup_barrier(mem_flags::mem_device); +} + +static inline __attribute__((always_inline)) +void group_barrier() +{ + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); +} + +static inline __attribute__((always_inline)) +void barrier_shared_exec() +{ + threadgroup_barrier(mem_flags::mem_threadgroup); +} + +static inline __attribute__((always_inline)) +void full_barrier_exec() +{ + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); +} + +static inline __attribute__((always_inline)) +void image_barrier_exec() +{ + threadgroup_barrier(mem_flags::mem_texture); +} + +static inline __attribute__((always_inline)) +void buffer_barrier_exec() +{ + threadgroup_barrier(mem_flags::mem_device); +} + +static inline __attribute__((always_inline)) +void group_barrier_exec() +{ + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup | mem_flags::mem_texture); +} + +static inline __attribute__((always_inline)) +void exec_barrier() +{ + threadgroup_barrier(mem_flags::mem_threadgroup); +} + +kernel void main0() +{ + barrier_shared(); + full_barrier(); + image_barrier(); + buffer_barrier(); + group_barrier(); + barrier_shared_exec(); + full_barrier_exec(); + image_barrier_exec(); + buffer_barrier_exec(); + group_barrier_exec(); + exec_barrier(); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/basic.comp b/third_party/spirv-cross/reference/shaders-msl/comp/basic.comp new file mode 100644 index 0000000..42518f0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/basic.comp @@ -0,0 +1,36 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +struct SSBO3 +{ + uint counter; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _23 [[buffer(0)]], device SSBO2& _45 [[buffer(1)]], device SSBO3& _48 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + float4 idata = _23.in_data[ident]; + if (dot(idata, float4(1.0, 5.0, 6.0, 2.0)) > 8.19999980926513671875) + { + uint _52 = atomic_fetch_add_explicit((device atomic_uint*)&_48.counter, 1u, memory_order_relaxed); + _45.out_data[_52] = idata; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/basic.dispatchbase.comp b/third_party/spirv-cross/reference/shaders-msl/comp/basic.dispatchbase.comp new file mode 100644 index 0000000..92d517c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/basic.dispatchbase.comp @@ -0,0 +1,41 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +struct SSBO3 +{ + uint counter; +}; + +constant uint _59_tmp [[function_constant(10)]]; +constant uint _59 = is_function_constant_defined(_59_tmp) ? _59_tmp : 1u; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(_59, 1u, 1u); + +kernel void main0(const device SSBO& _27 [[buffer(0)]], device SSBO2& _49 [[buffer(1)]], device SSBO3& _52 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 spvDispatchBase [[grid_origin]]) +{ + gl_GlobalInvocationID += spvDispatchBase * gl_WorkGroupSize; + gl_WorkGroupID += spvDispatchBase; + uint ident = gl_GlobalInvocationID.x; + uint workgroup = gl_WorkGroupID.x; + float4 idata = _27.in_data[ident]; + if (dot(idata, float4(1.0, 5.0, 6.0, 2.0)) > 8.19999980926513671875) + { + uint _56 = atomic_fetch_add_explicit((device atomic_uint*)&_52.counter, 1u, memory_order_relaxed); + _49.out_data[_56] = idata; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/basic.dispatchbase.msl11.comp b/third_party/spirv-cross/reference/shaders-msl/comp/basic.dispatchbase.msl11.comp new file mode 100644 index 0000000..87b0b44 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/basic.dispatchbase.msl11.comp @@ -0,0 +1,39 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +struct SSBO3 +{ + uint counter; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(constant uint3& spvDispatchBase [[buffer(29)]], const device SSBO& _27 [[buffer(0)]], device SSBO2& _49 [[buffer(1)]], device SSBO3& _52 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +{ + gl_GlobalInvocationID += spvDispatchBase * gl_WorkGroupSize; + gl_WorkGroupID += spvDispatchBase; + uint ident = gl_GlobalInvocationID.x; + uint workgroup = gl_WorkGroupID.x; + float4 idata = _27.in_data[ident]; + if (dot(idata, float4(1.0, 5.0, 6.0, 2.0)) > 8.19999980926513671875) + { + uint _56 = atomic_fetch_add_explicit((device atomic_uint*)&_52.counter, 1u, memory_order_relaxed); + _49.out_data[_56] = idata; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/basic.inline-block.msl2.comp b/third_party/spirv-cross/reference/shaders-msl/comp/basic.inline-block.msl2.comp new file mode 100644 index 0000000..337b1b7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/basic.inline-block.msl2.comp @@ -0,0 +1,54 @@ +#include +#include + +using namespace metal; + +typedef packed_float4 packed_float4x4[4]; + +struct Baz +{ + int f; + int g; +}; + +struct X +{ + int x; + int y; + float z; +}; + +struct Foo +{ + int a; + int b; + packed_float4x4 c; + X x[2]; +}; + +struct Bar +{ + int d; + int e; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(3u, 3u, 2u); + +struct spvDescriptorSetBuffer0 +{ + constant Bar* m_38 [[id(0)]]; + Foo m_32 [[id(1)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + device Baz* baz [[id(0)]][3]; +}; + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint3 coords = gl_GlobalInvocationID; + spvDescriptorSet1.baz[coords.x]->f = spvDescriptorSet0.m_32.a + (*spvDescriptorSet0.m_38).d; + spvDescriptorSet1.baz[coords.x]->g = spvDescriptorSet0.m_32.b * (*spvDescriptorSet0.m_38).e; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/builtins.comp b/third_party/spirv-cross/reference/shaders-msl/comp/builtins.comp new file mode 100644 index 0000000..4f03b09 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/builtins.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(8u, 4u, 2u); + +kernel void main0(uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]], uint3 gl_NumWorkGroups [[threadgroups_per_grid]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +{ + uint3 local_id = gl_LocalInvocationID; + uint3 global_id = gl_GlobalInvocationID; + uint local_index = gl_LocalInvocationIndex; + uint3 work_group_size = gl_WorkGroupSize; + uint3 num_work_groups = gl_NumWorkGroups; + uint3 work_group_id = gl_WorkGroupID; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/cfg-preserve-parameter.comp b/third_party/spirv-cross/reference/shaders-msl/comp/cfg-preserve-parameter.comp new file mode 100644 index 0000000..ce1bef3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/cfg-preserve-parameter.comp @@ -0,0 +1,82 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +static inline __attribute__((always_inline)) +void out_test_0(thread const int& cond, thread int& i) +{ + if (cond == 0) + { + i = 40; + } + else + { + i = 60; + } +} + +static inline __attribute__((always_inline)) +void out_test_1(thread const int& cond, thread int& i) +{ + switch (cond) + { + case 40: + { + i = 40; + break; + } + default: + { + i = 70; + break; + } + } +} + +static inline __attribute__((always_inline)) +void inout_test_0(thread const int& cond, thread int& i) +{ + if (cond == 0) + { + i = 40; + } +} + +static inline __attribute__((always_inline)) +void inout_test_1(thread const int& cond, thread int& i) +{ + switch (cond) + { + case 40: + { + i = 40; + break; + } + } +} + +kernel void main0() +{ + int cond = 40; + int i = 50; + int param = cond; + int param_1 = i; + out_test_0(param, param_1); + i = param_1; + int param_2 = cond; + int param_3 = i; + out_test_1(param_2, param_3); + i = param_3; + int param_4 = cond; + int param_5 = i; + inout_test_0(param_4, param_5); + i = param_5; + int param_6 = cond; + int param_7 = i; + inout_test_1(param_6, param_7); + i = param_7; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/coherent-block.comp b/third_party/spirv-cross/reference/shaders-msl/comp/coherent-block.comp new file mode 100644 index 0000000..58bbacb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/coherent-block.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 value; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(volatile device SSBO& _10 [[buffer(0)]]) +{ + _10.value = float4(20.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/coherent-image.comp b/third_party/spirv-cross/reference/shaders-msl/comp/coherent-image.comp new file mode 100644 index 0000000..5090484 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/coherent-image.comp @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + int4 value; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(volatile device SSBO& _10 [[buffer(0)]], texture2d uImage [[texture(0)]]) +{ + _10.value = uImage.read(uint2(int2(10))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/complex-composite-constant-array.comp b/third_party/spirv-cross/reference/shaders-msl/comp/complex-composite-constant-array.comp new file mode 100644 index 0000000..2f5549f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/complex-composite-constant-array.comp @@ -0,0 +1,65 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct SSBO +{ + float4x4 a; + uint index; +}; + +constant spvUnsafeArray _32 = spvUnsafeArray({ float4x4(float4(1.0, 0.0, 0.0, 0.0), float4(0.0, 1.0, 0.0, 0.0), float4(0.0, 0.0, 1.0, 0.0), float4(0.0, 0.0, 0.0, 1.0)), float4x4(float4(2.0, 0.0, 0.0, 0.0), float4(0.0, 2.0, 0.0, 0.0), float4(0.0, 0.0, 2.0, 0.0), float4(0.0, 0.0, 0.0, 2.0)) }); + +static inline __attribute__((always_inline)) +void write_global(device SSBO& v_14) +{ + v_14.a = _32[v_14.index]; +} + +kernel void main0(device SSBO& v_14 [[buffer(0)]]) +{ + write_global(v_14); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/complex-type-alias.comp b/third_party/spirv-cross/reference/shaders-msl/comp/complex-type-alias.comp new file mode 100644 index 0000000..fc0d575 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/complex-type-alias.comp @@ -0,0 +1,69 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Foo0 +{ + float a; +}; + +struct Foo1 +{ + Foo0 a; +}; + +struct Foo2 +{ + Foo1 a; + float weight; +}; + +struct Foo0_1 +{ + float a; +}; + +struct Foo1_1 +{ + Foo0_1 a; +}; + +struct Foo2_1 +{ + Foo1_1 a; + float weight; +}; + +struct SSBO +{ + Foo2_1 outputs[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(8u, 8u, 1u); + +static inline __attribute__((always_inline)) +void Zero(thread Foo0& v) +{ + v.a = 0.0; +} + +kernel void main0(device SSBO& _53 [[buffer(0)]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]]) +{ + threadgroup Foo2 coeffs[64]; + Foo2 data; + data.weight = 0.0; + Foo0 param; + Zero(param); + data.a.a = param; + coeffs[gl_LocalInvocationIndex] = data; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_LocalInvocationIndex == 0u) + { + _53.outputs[gl_WorkGroupID.x].a.a.a = coeffs[0].a.a.a; + _53.outputs[gl_WorkGroupID.x].weight = coeffs[0].weight; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/composite-array-initialization.comp b/third_party/spirv-cross/reference/shaders-msl/comp/composite-array-initialization.comp new file mode 100644 index 0000000..c6c17b1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/composite-array-initialization.comp @@ -0,0 +1,89 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Data +{ + float a; + float b; +}; + +constant float X_tmp [[function_constant(0)]]; +constant float X = is_function_constant_defined(X_tmp) ? X_tmp : 4.0; + +struct Data_1 +{ + float a; + float b; +}; + +struct SSBO +{ + Data_1 outdata[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(2u, 1u, 1u); + +constant spvUnsafeArray _25 = spvUnsafeArray({ Data{ 1.0, 2.0 }, Data{ 3.0, 4.0 } }); + +static inline __attribute__((always_inline)) +Data combine(thread const Data& a, thread const Data& b) +{ + return Data{ a.a + b.a, a.b + b.b }; +} + +kernel void main0(device SSBO& _53 [[buffer(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + spvUnsafeArray data = spvUnsafeArray({ Data{ 1.0, 2.0 }, Data{ 3.0, 4.0 } }); + spvUnsafeArray _31 = spvUnsafeArray({ Data{ X, 2.0 }, Data{ 3.0, 5.0 } }); + spvUnsafeArray data2; + data2 = _31; + Data param = data[gl_LocalInvocationID.x]; + Data param_1 = data2[gl_LocalInvocationID.x]; + Data _73 = combine(param, param_1); + _53.outdata[gl_WorkGroupID.x].a = _73.a; + _53.outdata[gl_WorkGroupID.x].b = _73.b; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/composite-array-initialization.force-native-array.comp b/third_party/spirv-cross/reference/shaders-msl/comp/composite-array-initialization.force-native-array.comp new file mode 100644 index 0000000..7320219 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/composite-array-initialization.force-native-array.comp @@ -0,0 +1,158 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Data +{ + float a; + float b; +}; + +constant float X_tmp [[function_constant(0)]]; +constant float X = is_function_constant_defined(X_tmp) ? X_tmp : 4.0; + +struct Data_1 +{ + float a; + float b; +}; + +struct SSBO +{ + Data_1 outdata[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(2u, 1u, 1u); + +constant Data _25[2] = { Data{ 1.0, 2.0 }, Data{ 3.0, 4.0 } }; + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +static inline __attribute__((always_inline)) +Data combine(thread const Data& a, thread const Data& b) +{ + return Data{ a.a + b.a, a.b + b.b }; +} + +kernel void main0(device SSBO& _53 [[buffer(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + Data data[2] = { Data{ 1.0, 2.0 }, Data{ 3.0, 4.0 } }; + Data _31[2] = { Data{ X, 2.0 }, Data{ 3.0, 5.0 } }; + Data data2[2]; + spvArrayCopyFromStackToStack1(data2, _31); + Data param = data[gl_LocalInvocationID.x]; + Data param_1 = data2[gl_LocalInvocationID.x]; + Data _73 = combine(param, param_1); + _53.outdata[gl_WorkGroupID.x].a = _73.a; + _53.outdata[gl_WorkGroupID.x].b = _73.b; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/composite-construct.comp b/third_party/spirv-cross/reference/shaders-msl/comp/composite-construct.comp new file mode 100644 index 0000000..aada82f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/composite-construct.comp @@ -0,0 +1,76 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct SSBO0 +{ + float4 as[1]; +}; + +struct SSBO1 +{ + float4 bs[1]; +}; + +struct Composite +{ + float4 a; + float4 b; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +constant spvUnsafeArray _43 = spvUnsafeArray({ float4(20.0), float4(40.0) }); + +kernel void main0(device SSBO0& _16 [[buffer(0)]], device SSBO1& _32 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + spvUnsafeArray _37 = spvUnsafeArray({ _16.as[gl_GlobalInvocationID.x], _32.bs[gl_GlobalInvocationID.x] }); + spvUnsafeArray values; + values = _37; + Composite c = Composite{ values[0], _43[1] }; + _16.as[0] = values[gl_LocalInvocationIndex]; + _32.bs[1] = c.b; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/copy-array-of-arrays.comp b/third_party/spirv-cross/reference/shaders-msl/comp/copy-array-of-arrays.comp new file mode 100644 index 0000000..21fb9b3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/copy-array-of-arrays.comp @@ -0,0 +1,79 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct BUF +{ + int a; + float b; + float c; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +constant spvUnsafeArray _16 = spvUnsafeArray({ 1.0, 2.0 }); +constant spvUnsafeArray _19 = spvUnsafeArray({ 3.0, 4.0 }); +constant spvUnsafeArray, 2> _20 = spvUnsafeArray, 2>({ spvUnsafeArray({ 1.0, 2.0 }), spvUnsafeArray({ 3.0, 4.0 }) }); +constant spvUnsafeArray, 2>, 2> _21 = spvUnsafeArray, 2>, 2>({ spvUnsafeArray, 2>({ spvUnsafeArray({ 1.0, 2.0 }), spvUnsafeArray({ 3.0, 4.0 }) }), spvUnsafeArray, 2>({ spvUnsafeArray({ 1.0, 2.0 }), spvUnsafeArray({ 3.0, 4.0 }) }) }); + +kernel void main0(device BUF& o [[buffer(0)]]) +{ + spvUnsafeArray, 2>, 2> c; + c = _21; + o.a = int(c[1][1][1]); + spvUnsafeArray _43 = spvUnsafeArray({ o.b, o.c }); + spvUnsafeArray _48 = spvUnsafeArray({ o.b, o.b }); + spvUnsafeArray, 2> _49 = spvUnsafeArray, 2>({ _43, _48 }); + spvUnsafeArray _54 = spvUnsafeArray({ o.c, o.c }); + spvUnsafeArray _59 = spvUnsafeArray({ o.c, o.b }); + spvUnsafeArray, 2> _60 = spvUnsafeArray, 2>({ _54, _59 }); + spvUnsafeArray, 2>, 2> _61 = spvUnsafeArray, 2>, 2>({ _49, _60 }); + spvUnsafeArray, 2>, 2> d; + d = _61; + spvUnsafeArray, 2>, 2> e; + e = d; + o.b = e[1][0][1]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp b/third_party/spirv-cross/reference/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp new file mode 100644 index 0000000..67078d8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp @@ -0,0 +1,364 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct BUF +{ + int a; + float b; + float c; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +constant float _16[2] = { 1.0, 2.0 }; +constant float _19[2] = { 3.0, 4.0 }; +constant float _20[2][2] = { { 1.0, 2.0 }, { 3.0, 4.0 } }; +constant float _21[2][2][2] = { { { 1.0, 2.0 }, { 3.0, 4.0 } }, { { 1.0, 2.0 }, { 3.0, 4.0 } } }; + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToStack2(thread T (&dst)[A][B], constant T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromConstantToStack1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup2(threadgroup T (&dst)[A][B], constant T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromConstantToThreadGroup1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromStackToStack2(thread T (&dst)[A][B], thread const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromStackToStack1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup2(threadgroup T (&dst)[A][B], thread const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromStackToThreadGroup1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack2(thread T (&dst)[A][B], threadgroup const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromThreadGroupToStack1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup2(threadgroup T (&dst)[A][B], threadgroup const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromThreadGroupToThreadGroup1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromDeviceToDevice2(device T (&dst)[A][B], device const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromDeviceToDevice1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromConstantToDevice2(device T (&dst)[A][B], constant T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromConstantToDevice1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromStackToDevice2(device T (&dst)[A][B], thread const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromStackToDevice1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice2(device T (&dst)[A][B], threadgroup const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromThreadGroupToDevice1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromDeviceToStack2(thread T (&dst)[A][B], device const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromDeviceToStack1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup2(threadgroup T (&dst)[A][B], device const T (&src)[A][B]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromDeviceToThreadGroup1(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromConstantToStack3(thread T (&dst)[A][B][C], constant T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromConstantToStack2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup3(threadgroup T (&dst)[A][B][C], constant T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromConstantToThreadGroup2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromStackToStack3(thread T (&dst)[A][B][C], thread const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromStackToStack2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup3(threadgroup T (&dst)[A][B][C], thread const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromStackToThreadGroup2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack3(thread T (&dst)[A][B][C], threadgroup const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromThreadGroupToStack2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup3(threadgroup T (&dst)[A][B][C], threadgroup const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromThreadGroupToThreadGroup2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromDeviceToDevice3(device T (&dst)[A][B][C], device const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromDeviceToDevice2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromConstantToDevice3(device T (&dst)[A][B][C], constant T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromConstantToDevice2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromStackToDevice3(device T (&dst)[A][B][C], thread const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromStackToDevice2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice3(device T (&dst)[A][B][C], threadgroup const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromThreadGroupToDevice2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromDeviceToStack3(thread T (&dst)[A][B][C], device const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromDeviceToStack2(dst[i], src[i]); + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup3(threadgroup T (&dst)[A][B][C], device const T (&src)[A][B][C]) +{ + for (uint i = 0; i < A; i++) + { + spvArrayCopyFromDeviceToThreadGroup2(dst[i], src[i]); + } +} + +kernel void main0(device BUF& o [[buffer(0)]]) +{ + float c[2][2][2]; + spvArrayCopyFromConstantToStack3(c, _21); + o.a = int(c[1][1][1]); + float _43[2] = { o.b, o.c }; + float _48[2] = { o.b, o.b }; + float _49[2][2] = { { _43[0], _43[1] }, { _48[0], _48[1] } }; + float _54[2] = { o.c, o.c }; + float _59[2] = { o.c, o.b }; + float _60[2][2] = { { _54[0], _54[1] }, { _59[0], _59[1] } }; + float _61[2][2][2] = { { { _49[0][0], _49[0][1] }, { _49[1][0], _49[1][1] } }, { { _60[0][0], _60[0][1] }, { _60[1][0], _60[1][1] } } }; + float d[2][2][2]; + spvArrayCopyFromStackToStack3(d, _61); + float e[2][2][2]; + spvArrayCopyFromStackToStack3(e, d); + o.b = e[1][0][1]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/culling.comp b/third_party/spirv-cross/reference/shaders-msl/comp/culling.comp new file mode 100644 index 0000000..1357836 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/culling.comp @@ -0,0 +1,36 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float in_data[1]; +}; + +struct SSBO2 +{ + float out_data[1]; +}; + +struct SSBO3 +{ + uint count; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(4u, 1u, 1u); + +kernel void main0(const device SSBO& _22 [[buffer(0)]], device SSBO2& _38 [[buffer(1)]], device SSBO3& _41 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + float idata = _22.in_data[ident]; + if (idata > 12.0) + { + uint _45 = atomic_fetch_add_explicit((device atomic_uint*)&_41.count, 1u, memory_order_relaxed); + _38.out_data[_45] = idata; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/defer-parens.comp b/third_party/spirv-cross/reference/shaders-msl/comp/defer-parens.comp new file mode 100644 index 0000000..9a567fa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/defer-parens.comp @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 data; + int index; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _13 [[buffer(0)]]) +{ + float4 d = _13.data; + _13.data = float4(d.x, d.yz + float2(10.0), d.w); + _13.data = (d + d) + d; + _13.data = (d.yz + float2(10.0)).xxyy; + float t = (d.yz + float2(10.0)).y; + _13.data = float4(t); + t = (d.zw + float2(10.0))[_13.index]; + _13.data = float4(t); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/dowhile.comp b/third_party/spirv-cross/reference/shaders-msl/comp/dowhile.comp new file mode 100644 index 0000000..2b4de9e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/dowhile.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4x4 mvp; + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _28 [[buffer(0)]], device SSBO2& _52 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + int i = 0; + float4 idat = _28.in_data[ident]; + do + { + idat = _28.mvp * idat; + i++; + } while (i < 16); + _52.out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/force-recompile-hooks.swizzle.comp b/third_party/spirv-cross/reference/shaders-msl/comp/force-recompile-hooks.swizzle.comp new file mode 100644 index 0000000..856efea --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/force-recompile-hooks.swizzle.comp @@ -0,0 +1,74 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +kernel void main0(constant uint* spvSwizzleConstants [[buffer(30)]], texture2d foo [[texture(0)]], texture2d bar [[texture(1)]], sampler fooSmplr [[sampler(0)]]) +{ + constant uint& fooSwzl = spvSwizzleConstants[0]; + float4 a = spvTextureSwizzle(foo.sample(fooSmplr, float2(1.0), level(0.0)), fooSwzl); + bar.write(a, uint2(int2(0))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/functions.comp b/third_party/spirv-cross/reference/shaders-msl/comp/functions.comp new file mode 100644 index 0000000..b107260 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/functions.comp @@ -0,0 +1,19 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +static inline __attribute__((always_inline)) +void myfunc(threadgroup int (&foo)[1337]) +{ + foo[0] = 13; +} + +kernel void main0() +{ + threadgroup int foo[1337]; + myfunc(foo); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp b/third_party/spirv-cross/reference/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp new file mode 100644 index 0000000..dbc5886 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp @@ -0,0 +1,32 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b[1]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +static inline __attribute__((always_inline)) +float getB(device myBlock& myStorage, thread uint3& gl_GlobalInvocationID) +{ + return myStorage.b[gl_GlobalInvocationID.x]; +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_GlobalInvocationID.x] = mod(getB(myStorage, gl_GlobalInvocationID) + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/global-invocation-id.comp b/third_party/spirv-cross/reference/shaders-msl/comp/global-invocation-id.comp new file mode 100644 index 0000000..9900b59 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/global-invocation-id.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b[1]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_GlobalInvocationID.x] = mod(myStorage.b[gl_GlobalInvocationID.x] + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp b/third_party/spirv-cross/reference/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp new file mode 100644 index 0000000..7c0e5dd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp @@ -0,0 +1,38 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 outdata; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +struct spvDescriptorSetBuffer0 +{ + texture2d uImage [[id(0)]]; + device atomic_uint* uImage_atomic [[id(1)]]; + device SSBO* m_31 [[id(2)]]; + texture2d uTexture [[id(3)]]; + sampler uTextureSmplr [[id(4)]]; +}; + +// The required alignment of a linear texture of R32Uint format. +constant uint spvLinearTextureAlignmentOverride [[function_constant(65535)]]; +constant uint spvLinearTextureAlignment = is_function_constant_defined(spvLinearTextureAlignmentOverride) ? spvLinearTextureAlignmentOverride : 4; +// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics +#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + spvLinearTextureAlignment / 4 - 1) & ~( spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x) + +kernel void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _26 = atomic_fetch_add_explicit((device atomic_uint*)&spvDescriptorSet0.uImage_atomic[spvImage2DAtomicCoord(int2(gl_GlobalInvocationID.xy), spvDescriptorSet0.uImage)], 10u, memory_order_relaxed); + uint ret = _26; + (*spvDescriptorSet0.m_31).outdata = spvDescriptorSet0.uTexture.sample(spvDescriptorSet0.uTextureSmplr, float2(gl_GlobalInvocationID.xy), level(0.0)) + float4(float(ret)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/image-atomic-automatic-bindings.comp b/third_party/spirv-cross/reference/shaders-msl/comp/image-atomic-automatic-bindings.comp new file mode 100644 index 0000000..8e49754 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/image-atomic-automatic-bindings.comp @@ -0,0 +1,29 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 outdata; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +// The required alignment of a linear texture of R32Uint format. +constant uint spvLinearTextureAlignmentOverride [[function_constant(65535)]]; +constant uint spvLinearTextureAlignment = is_function_constant_defined(spvLinearTextureAlignmentOverride) ? spvLinearTextureAlignmentOverride : 4; +// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics +#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + spvLinearTextureAlignment / 4 - 1) & ~( spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x) + +kernel void main0(device SSBO& _31 [[buffer(1)]], texture2d uImage [[texture(0)]], device atomic_uint* uImage_atomic [[buffer(0)]], texture2d uTexture [[texture(1)]], sampler uTextureSmplr [[sampler(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint _26 = atomic_fetch_add_explicit((device atomic_uint*)&uImage_atomic[spvImage2DAtomicCoord(int2(gl_GlobalInvocationID.xy), uImage)], 10u, memory_order_relaxed); + uint ret = _26; + _31.outdata = uTexture.sample(uTextureSmplr, float2(gl_GlobalInvocationID.xy), level(0.0)) + float4(float(ret)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/image-cube-array-load-store.comp b/third_party/spirv-cross/reference/shaders-msl/comp/image-cube-array-load-store.comp new file mode 100644 index 0000000..c0b83c4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/image-cube-array-load-store.comp @@ -0,0 +1,14 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(texturecube_array uImageIn [[texture(0)]], texturecube_array uImageOut [[texture(1)]]) +{ + int3 coord = int3(9, 7, 11); + float4 indata = uImageIn.read(uint2(coord.xy), uint(coord.z) % 6u, uint(coord.z) / 6u); + uImageOut.write(indata, uint2(coord.xy), uint(coord.z) % 6u, uint(coord.z) / 6u); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/image.comp b/third_party/spirv-cross/reference/shaders-msl/comp/image.comp new file mode 100644 index 0000000..e7c9c76 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/image.comp @@ -0,0 +1,13 @@ +#include +#include + +using namespace metal; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(texture2d uImageIn [[texture(0)]], texture2d uImageOut [[texture(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float4 v = uImageIn.read(uint2((int2(gl_GlobalInvocationID.xy) + int2(uImageIn.get_width(), uImageIn.get_height())))); + uImageOut.write(v, uint2(int2(gl_GlobalInvocationID.xy))); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/insert.comp b/third_party/spirv-cross/reference/shaders-msl/comp/insert.comp new file mode 100644 index 0000000..c4611ba --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/insert.comp @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _27 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float4 v; + v.x = 10.0; + v.y = 30.0; + v.z = 70.0; + v.w = 90.0; + _27.out_data[gl_GlobalInvocationID.x] = v; + ((device float*)&_27.out_data[gl_GlobalInvocationID.x])[1u] = 20.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/inverse.comp b/third_party/spirv-cross/reference/shaders-msl/comp/inverse.comp new file mode 100644 index 0000000..33aed46 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/inverse.comp @@ -0,0 +1,130 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct MatrixOut +{ + float2x2 m2out; + float3x3 m3out; + float4x4 m4out; +}; + +struct MatrixIn +{ + float2x2 m2in; + float3x3 m3in; + float4x4 m4in; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +// Returns the determinant of a 2x2 matrix. +static inline __attribute__((always_inline)) +float spvDet2x2(float a1, float a2, float b1, float b2) +{ + return a1 * b2 - b1 * a2; +} + +// Returns the determinant of a 3x3 matrix. +static inline __attribute__((always_inline)) +float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) +{ + return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * spvDet2x2(a2, a3, b2, b3); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float4x4 spvInverse4x4(float4x4 m) +{ + float4x4 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3]); + adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]); + + adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3]); + adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]); + + adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3]); + adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]); + + adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2]); + adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] * m[3][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float3x3 spvInverse3x3(float3x3 m) +{ + float3x3 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = spvDet2x2(m[1][1], m[1][2], m[2][1], m[2][2]); + adj[0][1] = -spvDet2x2(m[0][1], m[0][2], m[2][1], m[2][2]); + adj[0][2] = spvDet2x2(m[0][1], m[0][2], m[1][1], m[1][2]); + + adj[1][0] = -spvDet2x2(m[1][0], m[1][2], m[2][0], m[2][2]); + adj[1][1] = spvDet2x2(m[0][0], m[0][2], m[2][0], m[2][2]); + adj[1][2] = -spvDet2x2(m[0][0], m[0][2], m[1][0], m[1][2]); + + adj[2][0] = spvDet2x2(m[1][0], m[1][1], m[2][0], m[2][1]); + adj[2][1] = -spvDet2x2(m[0][0], m[0][1], m[2][0], m[2][1]); + adj[2][2] = spvDet2x2(m[0][0], m[0][1], m[1][0], m[1][1]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float2x2 spvInverse2x2(float2x2 m) +{ + float2x2 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = m[1][1]; + adj[0][1] = -m[0][1]; + + adj[1][0] = -m[1][0]; + adj[1][1] = m[0][0]; + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +kernel void main0(device MatrixOut& _15 [[buffer(0)]], const device MatrixIn& _20 [[buffer(1)]]) +{ + _15.m2out = spvInverse2x2(_20.m2in); + _15.m3out = spvInverse3x3(_20.m3in); + _15.m4out = spvInverse4x4(_20.m4in); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/local-invocation-id.comp b/third_party/spirv-cross/reference/shaders-msl/comp/local-invocation-id.comp new file mode 100644 index 0000000..362aaa3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/local-invocation-id.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b[1]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_LocalInvocationID.x] = mod(myStorage.b[gl_LocalInvocationID.x] + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/local-invocation-index.comp b/third_party/spirv-cross/reference/shaders-msl/comp/local-invocation-index.comp new file mode 100644 index 0000000..f8c1550 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/local-invocation-index.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b[1]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_LocalInvocationIndex] = mod(myStorage.b[gl_LocalInvocationIndex] + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/mat3.comp b/third_party/spirv-cross/reference/shaders-msl/comp/mat3.comp new file mode 100644 index 0000000..fcb8f7a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/mat3.comp @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct SSBO2 +{ + float3x3 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _22 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + _22.out_data[ident] = float3x3(float3(10.0), float3(20.0), float3(40.0)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/mod.comp b/third_party/spirv-cross/reference/shaders-msl/comp/mod.comp new file mode 100644 index 0000000..96264b0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/mod.comp @@ -0,0 +1,37 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(const device SSBO& _23 [[buffer(0)]], device SSBO2& _33 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + float4 v = mod(_23.in_data[ident], _33.out_data[ident]); + _33.out_data[ident] = v; + uint4 vu = as_type(_23.in_data[ident]) % as_type(_33.out_data[ident]); + _33.out_data[ident] = as_type(vu); + int4 vi = as_type(_23.in_data[ident]) % as_type(_33.out_data[ident]); + _33.out_data[ident] = as_type(vi); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/modf.comp b/third_party/spirv-cross/reference/shaders-msl/comp/modf.comp new file mode 100644 index 0000000..5a5ac3d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/modf.comp @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _23 [[buffer(0)]], device SSBO2& _35 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + float4 i; + float4 _31 = modf(_23.in_data[ident], i); + float4 v = _31; + _35.out_data[ident] = v; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/outer-product.comp b/third_party/spirv-cross/reference/shaders-msl/comp/outer-product.comp new file mode 100644 index 0000000..e589642 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/outer-product.comp @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float2x2 m22; + float2x3 m23; + float2x4 m24; + float3x2 m32; + float3x3 m33; + float3x4 m34; + float4x2 m42; + float4x3 m43; + float4x4 m44; +}; + +struct ReadSSBO +{ + float2 v2; + float3 v3; + float4 v4; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _21 [[buffer(0)]], const device ReadSSBO& _26 [[buffer(1)]]) +{ + _21.m22 = float2x2(_26.v2 * _26.v2.x, _26.v2 * _26.v2.y); + _21.m23 = float2x3(_26.v3 * _26.v2.x, _26.v3 * _26.v2.y); + _21.m24 = float2x4(_26.v4 * _26.v2.x, _26.v4 * _26.v2.y); + _21.m32 = float3x2(_26.v2 * _26.v3.x, _26.v2 * _26.v3.y, _26.v2 * _26.v3.z); + _21.m33 = float3x3(_26.v3 * _26.v3.x, _26.v3 * _26.v3.y, _26.v3 * _26.v3.z); + _21.m34 = float3x4(_26.v4 * _26.v3.x, _26.v4 * _26.v3.y, _26.v4 * _26.v3.z); + _21.m42 = float4x2(_26.v2 * _26.v4.x, _26.v2 * _26.v4.y, _26.v2 * _26.v4.z, _26.v2 * _26.v4.w); + _21.m43 = float4x3(_26.v3 * _26.v4.x, _26.v3 * _26.v4.y, _26.v3 * _26.v4.z, _26.v3 * _26.v4.w); + _21.m44 = float4x4(_26.v4 * _26.v4.x, _26.v4 * _26.v4.y, _26.v4 * _26.v4.z, _26.v4 * _26.v4.w); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/packing-test-1.comp b/third_party/spirv-cross/reference/shaders-msl/comp/packing-test-1.comp new file mode 100644 index 0000000..6129742 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/packing-test-1.comp @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct T1 +{ + float3 a; + float b; +}; + +struct T1_1 +{ + packed_float3 a; + float b; +}; + +struct Buffer0 +{ + T1_1 buf0[1]; +}; + +struct Buffer1 +{ + float buf1[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(32u, 1u, 1u); + +kernel void main0(device Buffer0& _15 [[buffer(0)]], device Buffer1& _34 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + T1 v; + v.a = float3(_15.buf0[0].a); + v.b = _15.buf0[0].b; + float x = v.b; + _34.buf1[gl_GlobalInvocationID.x] = x; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/packing-test-2.comp b/third_party/spirv-cross/reference/shaders-msl/comp/packing-test-2.comp new file mode 100644 index 0000000..4b91f3c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/packing-test-2.comp @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct T1 +{ + packed_float3 a; + float b; +}; + +struct Buffer0 +{ + T1 buf0[1]; +}; + +struct Buffer1 +{ + float buf1[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(32u, 1u, 1u); + +kernel void main0(device Buffer0& _14 [[buffer(0)]], device Buffer1& _24 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + float x = _14.buf0[0].b; + _24.buf1[gl_GlobalInvocationID.x] = x; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/read-write-only.comp b/third_party/spirv-cross/reference/shaders-msl/comp/read-write-only.comp new file mode 100644 index 0000000..0cf8d8e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/read-write-only.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct SSBO2 +{ + float4 data4; + float4 data5; +}; + +struct SSBO0 +{ + float4 data0; + float4 data1; +}; + +struct SSBO1 +{ + float4 data2; + float4 data3; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _10 [[buffer(0)]], const device SSBO0& _15 [[buffer(1)]], device SSBO1& _21 [[buffer(2)]]) +{ + _10.data4 = _15.data0 + _21.data2; + _10.data5 = _15.data1 + _21.data3; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/rmw-matrix.comp b/third_party/spirv-cross/reference/shaders-msl/comp/rmw-matrix.comp new file mode 100644 index 0000000..b53a3a7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/rmw-matrix.comp @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float a; + float4 b; + float4x4 c; + float a1; + float4 b1; + float4x4 c1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _11 [[buffer(0)]]) +{ + _11.a *= _11.a1; + _11.b *= _11.b1; + _11.c = _11.c * _11.c1; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/rmw-opt.comp b/third_party/spirv-cross/reference/shaders-msl/comp/rmw-opt.comp new file mode 100644 index 0000000..229154f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/rmw-opt.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + int a; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _9 [[buffer(0)]]) +{ + _9.a += 10; + _9.a -= 10; + _9.a *= 10; + _9.a /= 10; + _9.a = _9.a << 2; + _9.a = _9.a >> 3; + _9.a &= 40; + _9.a ^= 10; + _9.a %= 40; + _9.a |= 1; + bool c = false; + bool d = true; + c = c && d; + d = d || c; + _9.a = int(c && d); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/reference/shaders-msl/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..9bf8781 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float a; + float b; + float c; + float d; + float e; + float f; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _9 [[buffer(0)]]) +{ + _9.c = abs(_9.a - _9.b); + _9.d = abs(_9.a); + _9.e = sign(_9.a); + _9.f = abs((_9.a - 1.0) - (_9.b - 2.0)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/shared-array-of-arrays.comp b/third_party/spirv-cross/reference/shaders-msl/comp/shared-array-of-arrays.comp new file mode 100644 index 0000000..8b53236 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/shared-array-of-arrays.comp @@ -0,0 +1,33 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(4u, 4u, 1u); + +static inline __attribute__((always_inline)) +void work(threadgroup float (&foo)[4][4], thread uint3& gl_LocalInvocationID, thread uint& gl_LocalInvocationIndex, device SSBO& v_67, thread uint3& gl_GlobalInvocationID) +{ + foo[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = float(gl_LocalInvocationIndex); + threadgroup_barrier(mem_flags::mem_threadgroup); + float x = 0.0; + x += foo[gl_LocalInvocationID.x][0]; + x += foo[gl_LocalInvocationID.x][1]; + x += foo[gl_LocalInvocationID.x][2]; + x += foo[gl_LocalInvocationID.x][3]; + v_67.out_data[gl_GlobalInvocationID.x] = x; +} + +kernel void main0(device SSBO& v_67 [[buffer(0)]], uint3 gl_LocalInvocationID [[thread_position_in_threadgroup]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + threadgroup float foo[4][4]; + work(foo, gl_LocalInvocationID, gl_LocalInvocationIndex, v_67, gl_GlobalInvocationID); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/shared.comp b/third_party/spirv-cross/reference/shaders-msl/comp/shared.comp new file mode 100644 index 0000000..c9ffde1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/shared.comp @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float in_data[1]; +}; + +struct SSBO2 +{ + float out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(4u, 1u, 1u); + +kernel void main0(const device SSBO& _22 [[buffer(0)]], device SSBO2& _44 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint gl_LocalInvocationIndex [[thread_index_in_threadgroup]]) +{ + threadgroup float sShared[4]; + uint ident = gl_GlobalInvocationID.x; + float idata = _22.in_data[ident]; + sShared[gl_LocalInvocationIndex] = idata; + threadgroup_barrier(mem_flags::mem_threadgroup); + _44.out_data[ident] = sShared[(4u - gl_LocalInvocationIndex) - 1u]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/spec-constant-op-member-array.comp b/third_party/spirv-cross/reference/shaders-msl/comp/spec-constant-op-member-array.comp new file mode 100644 index 0000000..8f54f05 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/spec-constant-op-member-array.comp @@ -0,0 +1,49 @@ +#include +#include + +using namespace metal; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 100 +#endif +constant int a = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 200 +#endif +constant int b = SPIRV_CROSS_CONSTANT_ID_1; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 300 +#endif +constant int c = SPIRV_CROSS_CONSTANT_ID_2; +constant int d = (c + 50); + +struct SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +}; + +constant int e_tmp [[function_constant(3)]]; +constant int e = is_function_constant_defined(e_tmp) ? e_tmp : 400; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _22 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _22.w[gl_GlobalInvocationID.x] += (_22.v[gl_GlobalInvocationID.x] + e); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/spec-constant-work-group-size.comp b/third_party/spirv-cross/reference/shaders-msl/comp/spec-constant-work-group-size.comp new file mode 100644 index 0000000..de30ede --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/spec-constant-work-group-size.comp @@ -0,0 +1,75 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 2 +#endif +constant int b = SPIRV_CROSS_CONSTANT_ID_1; +constant int a_tmp [[function_constant(0)]]; +constant int a = is_function_constant_defined(a_tmp) ? a_tmp : 1; + +struct SSBO +{ + int v[1]; +}; + +constant uint _21 = (uint(a) + 0u); +constant uint _22_tmp [[function_constant(10)]]; +constant uint _22 = is_function_constant_defined(_22_tmp) ? _22_tmp : 1u; +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(_22, 20u, 1u); +constant uint _27 = gl_WorkGroupSize.x; +constant uint _28 = (_21 + _27); +constant uint _29 = gl_WorkGroupSize.y; +constant uint _30 = (_28 + _29); +constant int _32 = (1 - a); + +kernel void main0(device SSBO& _17 [[buffer(0)]]) +{ + spvUnsafeArray spec_const_array_size; + spec_const_array_size[a] = a; + _17.v[_30] = b + spec_const_array_size[_32]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/storage-buffer-std140-vector-array.comp b/third_party/spirv-cross/reference/shaders-msl/comp/storage-buffer-std140-vector-array.comp new file mode 100644 index 0000000..5593629 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/storage-buffer-std140-vector-array.comp @@ -0,0 +1,96 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Sub +{ + spvUnsafeArray f; + spvUnsafeArray f2; + spvUnsafeArray f3; + spvUnsafeArray f4; +}; + +struct Sub_1 +{ + float4 f[2]; + float4 f2[2]; + float3 f3[2]; + float4 f4[2]; +}; + +struct SSBO +{ + Sub_1 sub[2]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO& _27 [[buffer(0)]], uint3 gl_WorkGroupID [[threadgroup_position_in_grid]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + Sub foo; + foo.f[0] = _27.sub[gl_WorkGroupID.x].f[0].x; + foo.f[1] = _27.sub[gl_WorkGroupID.x].f[1].x; + foo.f2[0] = _27.sub[gl_WorkGroupID.x].f2[0].xy; + foo.f2[1] = _27.sub[gl_WorkGroupID.x].f2[1].xy; + foo.f3[0] = _27.sub[gl_WorkGroupID.x].f3[0]; + foo.f3[1] = _27.sub[gl_WorkGroupID.x].f3[1]; + foo.f4[0] = _27.sub[gl_WorkGroupID.x].f4[0]; + foo.f4[1] = _27.sub[gl_WorkGroupID.x].f4[1]; + foo.f[gl_GlobalInvocationID.x] += 1.0; + foo.f2[gl_GlobalInvocationID.x] += float2(2.0); + foo.f3[gl_GlobalInvocationID.x] += float3(3.0); + foo.f4[gl_GlobalInvocationID.x] += float4(4.0); + (device float&)_27.sub[gl_WorkGroupID.x].f[0] = foo.f[0]; + (device float&)_27.sub[gl_WorkGroupID.x].f[1] = foo.f[1]; + (device float2&)_27.sub[gl_WorkGroupID.x].f2[0] = foo.f2[0]; + (device float2&)_27.sub[gl_WorkGroupID.x].f2[1] = foo.f2[1]; + _27.sub[gl_WorkGroupID.x].f3[0] = foo.f3[0]; + _27.sub[gl_WorkGroupID.x].f3[1] = foo.f3[1]; + _27.sub[gl_WorkGroupID.x].f4[0] = foo.f4[0]; + _27.sub[gl_WorkGroupID.x].f4[1] = foo.f4[1]; + (device float&)_27.sub[0].f[0] = _27.sub[0].f[0].x + 5.0; + (device float2&)_27.sub[0].f2[1] = _27.sub[0].f2[1].xy + float2(5.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/struct-layout.comp b/third_party/spirv-cross/reference/shaders-msl/comp/struct-layout.comp new file mode 100644 index 0000000..8f2ab2d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/struct-layout.comp @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct Foo +{ + float4x4 m; +}; + +struct SSBO2 +{ + Foo out_data[1]; +}; + +struct SSBO +{ + Foo in_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _23 [[buffer(0)]], const device SSBO& _30 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + _23.out_data[ident].m = _30.in_data[ident].m * _30.in_data[ident].m; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/struct-nested.comp b/third_party/spirv-cross/reference/shaders-msl/comp/struct-nested.comp new file mode 100644 index 0000000..e3d04be --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/struct-nested.comp @@ -0,0 +1,39 @@ +#include +#include + +using namespace metal; + +struct s1 +{ + int a; +}; + +struct s2 +{ + s1 b; +}; + +struct s1_1 +{ + int a; +}; + +struct s2_1 +{ + s1_1 b; +}; + +struct dstbuffer +{ + s2_1 test[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device dstbuffer& _19 [[buffer(0)]]) +{ + s2 testVal; + testVal.b.a = 0; + _19.test[0].b.a = testVal.b.a; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/struct-packing.comp b/third_party/spirv-cross/reference/shaders-msl/comp/struct-packing.comp new file mode 100644 index 0000000..dc16543 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/struct-packing.comp @@ -0,0 +1,152 @@ +#include +#include + +using namespace metal; + +struct S0 +{ + float2 a[1]; + float b; + char _m0_final_padding[4]; +}; + +struct S1 +{ + packed_float3 a; + float b; +}; + +struct S2 +{ + float3 a[1]; + float b; + char _m0_final_padding[12]; +}; + +struct S3 +{ + float2 a; + float b; +}; + +struct S4 +{ + float2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + S4 m3s[8]; + char _m0_final_padding[8]; +}; + +struct SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + float2x2 m0; + float2x2 m1; + float2x3 m2[4]; + float3x2 m3; + float2x2 m4; + float2x2 m5[9]; + float3x2 m6[4][2]; + float2x3 m7; + float array[1]; +}; + +struct S0_1 +{ + float2 a[1]; + char _m1_pad[8]; + float b; + char _m0_final_padding[12]; +}; + +struct S1_1 +{ + packed_float3 a; + float b; +}; + +struct S2_1 +{ + float3 a[1]; + float b; + char _m0_final_padding[12]; +}; + +struct S3_1 +{ + float2 a; + float b; +}; + +struct S4_1 +{ + float2 c; + char _m0_final_padding[8]; +}; + +struct Content_1 +{ + S0_1 m0s[1]; + S1_1 m1s[1]; + S2_1 m2s[1]; + S0_1 m0; + S1_1 m1; + S2_1 m2; + S3_1 m3; + float m4; + char _m8_pad[8]; + S4_1 m3s[8]; +}; + +struct SSBO0 +{ + Content_1 content; + Content_1 content1[2]; + Content_1 content2; + float4 array[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO1& ssbo_430 [[buffer(0)]], device SSBO0& ssbo_140 [[buffer(1)]]) +{ + Content_1 _60 = ssbo_140.content; + ssbo_430.content.m0s[0].a[0] = _60.m0s[0].a[0]; + ssbo_430.content.m0s[0].b = _60.m0s[0].b; + ssbo_430.content.m1s[0].a = float3(_60.m1s[0].a); + ssbo_430.content.m1s[0].b = _60.m1s[0].b; + ssbo_430.content.m2s[0].a[0] = _60.m2s[0].a[0]; + ssbo_430.content.m2s[0].b = _60.m2s[0].b; + ssbo_430.content.m0.a[0] = _60.m0.a[0]; + ssbo_430.content.m0.b = _60.m0.b; + ssbo_430.content.m1.a = float3(_60.m1.a); + ssbo_430.content.m1.b = _60.m1.b; + ssbo_430.content.m2.a[0] = _60.m2.a[0]; + ssbo_430.content.m2.b = _60.m2.b; + ssbo_430.content.m3.a = _60.m3.a; + ssbo_430.content.m3.b = _60.m3.b; + ssbo_430.content.m4 = _60.m4; + ssbo_430.content.m3s[0].c = _60.m3s[0].c; + ssbo_430.content.m3s[1].c = _60.m3s[1].c; + ssbo_430.content.m3s[2].c = _60.m3s[2].c; + ssbo_430.content.m3s[3].c = _60.m3s[3].c; + ssbo_430.content.m3s[4].c = _60.m3s[4].c; + ssbo_430.content.m3s[5].c = _60.m3s[5].c; + ssbo_430.content.m3s[6].c = _60.m3s[6].c; + ssbo_430.content.m3s[7].c = _60.m3s[7].c; + ssbo_430.content.m1.a = ssbo_430.content.m3.a * ssbo_430.m6[1][1]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/torture-loop.comp b/third_party/spirv-cross/reference/shaders-msl/comp/torture-loop.comp new file mode 100644 index 0000000..e92e71d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/torture-loop.comp @@ -0,0 +1,53 @@ +#include +#include + +using namespace metal; + +struct SSBO +{ + float4x4 mvp; + float4 in_data[1]; +}; + +struct SSBO2 +{ + float4 out_data[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(const device SSBO& _24 [[buffer(0)]], device SSBO2& _89 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + uint ident = gl_GlobalInvocationID.x; + float4 idat = _24.in_data[ident]; + int k = 0; + for (;;) + { + int _39 = k; + int _40 = _39 + 1; + k = _40; + if (_40 < 10) + { + idat *= 2.0; + k++; + continue; + } + else + { + break; + } + } + for (uint i = 0u; i < 16u; i++, k++) + { + for (uint j = 0u; j < 30u; j++) + { + idat = _24.mvp * idat; + } + } + do + { + k++; + } while (k > 10); + _89.out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/type-alias.comp b/third_party/spirv-cross/reference/shaders-msl/comp/type-alias.comp new file mode 100644 index 0000000..e3ac031 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/type-alias.comp @@ -0,0 +1,67 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct S0 +{ + float4 a; +}; + +struct S1 +{ + float4 a; +}; + +struct S0_1 +{ + float4 a; +}; + +struct SSBO0 +{ + S0_1 s0s[1]; +}; + +struct S1_1 +{ + float4 a; +}; + +struct SSBO1 +{ + S1_1 s1s[1]; +}; + +struct SSBO2 +{ + float4 outputs[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +static inline __attribute__((always_inline)) +float4 overload(thread const S0& s0) +{ + return s0.a; +} + +static inline __attribute__((always_inline)) +float4 overload(thread const S1& s1) +{ + return s1.a; +} + +kernel void main0(device SSBO0& _36 [[buffer(0)]], device SSBO1& _55 [[buffer(1)]], device SSBO2& _66 [[buffer(2)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + S0 s0; + s0.a = _36.s0s[gl_GlobalInvocationID.x].a; + S1 s1; + s1.a = _55.s1s[gl_GlobalInvocationID.x].a; + S0 param = s0; + S1 param_1 = s1; + _66.outputs[gl_GlobalInvocationID.x] = overload(param) + overload(param_1); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/udiv.comp b/third_party/spirv-cross/reference/shaders-msl/comp/udiv.comp new file mode 100644 index 0000000..7f7315b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/udiv.comp @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct SSBO2 +{ + uint outputs[1]; +}; + +struct SSBO +{ + uint inputs[1]; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBO2& _10 [[buffer(0)]], device SSBO& _23 [[buffer(1)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]]) +{ + _10.outputs[gl_GlobalInvocationID.x] = _23.inputs[gl_GlobalInvocationID.x] / 29u; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/comp/writable-ssbo.comp b/third_party/spirv-cross/reference/shaders-msl/comp/writable-ssbo.comp new file mode 100644 index 0000000..dcec81a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/comp/writable-ssbo.comp @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct myBlock +{ + int a; + float b; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +kernel void main0(device myBlock& myStorage [[buffer(0)]]) +{ + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b = mod(myStorage.b + 0.0199999995529651641845703125, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp b/third_party/spirv-cross/reference/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp new file mode 100644 index 0000000..cea1298 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp @@ -0,0 +1,179 @@ +#include +#include + +using namespace metal; + +struct SSBOUint +{ + uint a; + uint b; + uint c; + uint d; + uint2 a2; + uint2 b2; + uint2 c2; + uint2 d2; + uint3 a3; + uint3 b3; + uint3 c3; + uint3 d3; + uint4 a4; + uint4 b4; + uint4 c4; + uint4 d4; +}; + +struct ResType +{ + uint _m0; + uint _m1; +}; + +struct ResType_1 +{ + uint2 _m0; + uint2 _m1; +}; + +struct ResType_2 +{ + uint3 _m0; + uint3 _m1; +}; + +struct ResType_3 +{ + uint4 _m0; + uint4 _m1; +}; + +struct SSBOInt +{ + int a; + int b; + int c; + int d; + int2 a2; + int2 b2; + int2 c2; + int2 d2; + int3 a3; + int3 b3; + int3 c3; + int3 d3; + int4 a4; + int4 b4; + int4 c4; + int4 d4; +}; + +struct ResType_4 +{ + int _m0; + int _m1; +}; + +struct ResType_5 +{ + int2 _m0; + int2 _m1; +}; + +struct ResType_6 +{ + int3 _m0; + int3 _m1; +}; + +struct ResType_7 +{ + int4 _m0; + int4 _m1; +}; + +constant uint3 gl_WorkGroupSize [[maybe_unused]] = uint3(1u); + +kernel void main0(device SSBOUint& u [[buffer(0)]], device SSBOInt& i [[buffer(1)]]) +{ + ResType _25; + _25._m0 = u.a + u.b; + _25._m1 = select(uint(1), uint(0), _25._m0 >= max(u.a, u.b)); + u.d = _25._m1; + u.c = _25._m0; + ResType_1 _40; + _40._m0 = u.a2 + u.b2; + _40._m1 = select(uint2(1), uint2(0), _40._m0 >= max(u.a2, u.b2)); + u.d2 = _40._m1; + u.c2 = _40._m0; + ResType_2 _55; + _55._m0 = u.a3 + u.b3; + _55._m1 = select(uint3(1), uint3(0), _55._m0 >= max(u.a3, u.b3)); + u.d3 = _55._m1; + u.c3 = _55._m0; + ResType_3 _70; + _70._m0 = u.a4 + u.b4; + _70._m1 = select(uint4(1), uint4(0), _70._m0 >= max(u.a4, u.b4)); + u.d4 = _70._m1; + u.c4 = _70._m0; + ResType _79; + _79._m0 = u.a - u.b; + _79._m1 = select(uint(1), uint(0), u.a >= u.b); + u.d = _79._m1; + u.c = _79._m0; + ResType_1 _88; + _88._m0 = u.a2 - u.b2; + _88._m1 = select(uint2(1), uint2(0), u.a2 >= u.b2); + u.d2 = _88._m1; + u.c2 = _88._m0; + ResType_2 _97; + _97._m0 = u.a3 - u.b3; + _97._m1 = select(uint3(1), uint3(0), u.a3 >= u.b3); + u.d3 = _97._m1; + u.c3 = _97._m0; + ResType_3 _106; + _106._m0 = u.a4 - u.b4; + _106._m1 = select(uint4(1), uint4(0), u.a4 >= u.b4); + u.d4 = _106._m1; + u.c4 = _106._m0; + ResType _116; + _116._m0 = u.a * u.b; + _116._m1 = mulhi(u.a, u.b); + u.d = _116._m0; + u.c = _116._m1; + ResType_1 _125; + _125._m0 = u.a2 * u.b2; + _125._m1 = mulhi(u.a2, u.b2); + u.d2 = _125._m0; + u.c2 = _125._m1; + ResType_2 _134; + _134._m0 = u.a3 * u.b3; + _134._m1 = mulhi(u.a3, u.b3); + u.d3 = _134._m0; + u.c3 = _134._m1; + ResType_3 _143; + _143._m0 = u.a4 * u.b4; + _143._m1 = mulhi(u.a4, u.b4); + u.d4 = _143._m0; + u.c4 = _143._m1; + ResType_4 _160; + _160._m0 = i.a * i.b; + _160._m1 = mulhi(i.a, i.b); + i.d = _160._m0; + i.c = _160._m1; + ResType_5 _171; + _171._m0 = i.a2 * i.b2; + _171._m1 = mulhi(i.a2, i.b2); + i.d2 = _171._m0; + i.c2 = _171._m1; + ResType_6 _182; + _182._m0 = i.a3 * i.b3; + _182._m1 = mulhi(i.a3, i.b3); + i.d3 = _182._m0; + i.c3 = _182._m1; + ResType_7 _193; + _193._m0 = i.a4 * i.b4; + _193._m1 = mulhi(i.a4, i.b4); + i.d4 = _193._m0; + i.c4 = _193._m1; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/image-ms.desktop.frag b/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/image-ms.desktop.frag new file mode 100644 index 0000000..7957b20 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/image-ms.desktop.frag @@ -0,0 +1,13 @@ +#include +#include + +using namespace metal; + +fragment void main0(texture2d_ms uImageMS [[texture(0)]], texture2d_array uImageArray [[texture(1)]], texture2d uImage [[texture(2)]]) +{ + float4 a = uImageMS.read(uint2(int2(1, 2)), 2); + float4 b = uImageArray.read(uint2(int3(1, 2, 4).xy), uint(int3(1, 2, 4).z)); + uImage.write(a, uint2(int2(2, 3))); + uImageArray.write(b, uint2(int3(2, 3, 7).xy), uint(int3(2, 3, 7).z)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/query-levels.desktop.frag b/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/query-levels.desktop.frag new file mode 100644 index 0000000..922796b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/query-levels.desktop.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = float4(float(int(uSampler.get_num_mip_levels()))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag b/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag new file mode 100644 index 0000000..330bc64 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_ms uSampler [[texture(0)]], texture2d_ms uImage [[texture(1)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = float4(float(int(uSampler.get_num_samples()) + int(uImage.get_num_samples()))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc new file mode 100644 index 0000000..01fceeb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc @@ -0,0 +1,85 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float3 vVertex; +}; + +struct main0_patchOut +{ + spvUnsafeArray vPatch; +}; + +struct main0_in +{ + float3 vInput [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + gl_out[gl_InvocationID].vVertex = gl_in[gl_InvocationID].vInput + gl_in[gl_InvocationID ^ 1].vInput; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0) + { + patchOut.vPatch[0] = float3(10.0); + patchOut.vPatch[1] = float3(20.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(2.0); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc new file mode 100644 index 0000000..f4b1479 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc @@ -0,0 +1,47 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +struct main0_patchOut +{ + float3 vFoo; +}; + +struct main0_in +{ + uint3 m_78; + ushort2 m_82; + float4 gl_Position; +}; + +static inline __attribute__((always_inline)) +void set_position(device main0_out* thread & gl_out, thread uint& gl_InvocationID, device main0_in* thread & gl_in) +{ + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 1]; + device main0_patchOut& patchOut = spvPatchOut[gl_GlobalInvocationID.x / 1]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 1; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1]); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.900000095367431640625); + patchOut.vFoo = float3(1.0); + set_position(gl_out, gl_InvocationID, gl_in); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc new file mode 100644 index 0000000..054b4e7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc @@ -0,0 +1,47 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +struct main0_patchOut +{ + float3 vFoo; +}; + +struct main0_in +{ + float4 gl_Position [[attribute(0)]]; +}; + +static inline __attribute__((always_inline)) +void set_position(device main0_out* thread & gl_out, thread uint& gl_InvocationID, threadgroup main0_in* thread & gl_in) +{ + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 1]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 1) + return; + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.900000095367431640625); + patchOut.vFoo = float3(1.0); + set_position(gl_out, gl_InvocationID, gl_in); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc new file mode 100644 index 0000000..e47d56a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Boo +{ + float3 a; + uint3 b; +}; + +struct main0_out +{ + Boo vVertex; +}; + +struct main0_in +{ + float3 Boo_a; + uint3 Boo_b; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + Boo _26 = Boo{ gl_in[gl_InvocationID].Boo_a, gl_in[gl_InvocationID].Boo_b }; + gl_out[gl_InvocationID].vVertex = _26; + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(2.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc new file mode 100644 index 0000000..e7e184a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +struct Boo +{ + float3 a; + float3 b; +}; + +struct main0_out +{ + Boo vVertex; +}; + +struct main0_in +{ + float3 Boo_a [[attribute(0)]]; + float3 Boo_b [[attribute(1)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + Boo _25 = Boo{ gl_in[gl_InvocationID].Boo_a, gl_in[gl_InvocationID].Boo_b }; + gl_out[gl_InvocationID].vVertex = _25; + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(2.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese new file mode 100644 index 0000000..dff8cb7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 gl_Position [[attribute(0)]]; +}; + +struct main0_patchIn +{ + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + out.gl_Position = ((patchIn.gl_in[0].gl_Position * gl_TessCoord.x) + (patchIn.gl_in[1].gl_Position * gl_TessCoord.y)) + (patchIn.gl_in[2].gl_Position * gl_TessCoord.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/basic.desktop.sso.vert b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/basic.desktop.sso.vert new file mode 100644 index 0000000..ffb4357 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/basic.desktop.sso.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert new file mode 100644 index 0000000..a414c98 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + out.gl_Position = float4(10.0); + out.gl_ClipDistance[0] = 1.0; + out.gl_ClipDistance[1] = 4.0; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert new file mode 100644 index 0000000..2d98929 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + out.gl_Position = float4(10.0); + out.gl_ClipDistance[0] = 1.0; + out.gl_ClipDistance[1] = 4.0; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert new file mode 100644 index 0000000..b3c8b6b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvStageInputSize [[grid_size]], uint3 spvDispatchBase [[grid_origin]], device main0_out* spvOut [[buffer(28)]]) +{ + device main0_out& out = spvOut[gl_GlobalInvocationID.y * spvStageInputSize.x + gl_GlobalInvocationID.x]; + if (any(gl_GlobalInvocationID >= spvStageInputSize)) + return; + uint gl_BaseVertex = spvDispatchBase.x; + uint gl_BaseInstance = spvDispatchBase.y; + out.gl_Position = float4(float(int(gl_BaseVertex)), float(int(gl_BaseInstance)), 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert new file mode 100644 index 0000000..a32c194 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(uint gl_BaseVertex [[base_vertex]], uint gl_BaseInstance [[base_instance]]) +{ + main0_out out = {}; + out.gl_Position = float4(float(int(gl_BaseVertex)), float(int(gl_BaseInstance)), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/flatten/basic.flatten.vert b/third_party/spirv-cross/reference/shaders-msl/flatten/basic.flatten.vert new file mode 100644 index 0000000..ffb4357 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/flatten/basic.flatten.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/flatten/multiindex.flatten.vert b/third_party/spirv-cross/reference/shaders-msl/flatten/multiindex.flatten.vert new file mode 100644 index 0000000..f4549ab --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/flatten/multiindex.flatten.vert @@ -0,0 +1,27 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 Data[3][5]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + int2 aIndex [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _20 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _20.Data[in.aIndex.x][in.aIndex.y]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/flatten/push-constant.flatten.vert b/third_party/spirv-cross/reference/shaders-msl/flatten/push-constant.flatten.vert new file mode 100644 index 0000000..8f2e8c1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/flatten/push-constant.flatten.vert @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct PushMe +{ + float4x4 MVP; + float2x2 Rot; + float Arr[4]; +}; + +struct main0_out +{ + float2 vRot [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float2 Rot [[attribute(0)]]; + float4 Pos [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant PushMe& registers [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = registers.MVP * in.Pos; + out.vRot = (registers.Rot * in.Rot) + float2(registers.Arr[2]); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/flatten/rowmajor.flatten.vert b/third_party/spirv-cross/reference/shaders-msl/flatten/rowmajor.flatten.vert new file mode 100644 index 0000000..3ea30e6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/flatten/rowmajor.flatten.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVPR; + float4x4 uMVPC; + float4x4 uMVP; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) +{ + main0_out out = {}; + float2 v = float4x2(_18.uMVP[0].xy, _18.uMVP[1].xy, _18.uMVP[2].xy, _18.uMVP[3].xy) * in.aVertex; + out.gl_Position = (_18.uMVPR * in.aVertex) + (in.aVertex * _18.uMVPC); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/flatten/struct.flatten.vert b/third_party/spirv-cross/reference/shaders-msl/flatten/struct.flatten.vert new file mode 100644 index 0000000..954f925 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/flatten/struct.flatten.vert @@ -0,0 +1,40 @@ +#include +#include + +using namespace metal; + +struct Light +{ + packed_float3 Position; + float Radius; + float4 Color; +}; + +struct UBO +{ + float4x4 uMVP; + Light light; +}; + +struct main0_out +{ + float4 vColor [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _18.uMVP * in.aVertex; + out.vColor = float4(0.0); + float3 L = in.aVertex.xyz - float3(_18.light.Position); + out.vColor += ((_18.light.Color * fast::clamp(1.0 - (length(L) / _18.light.Radius), 0.0, 1.0)) * dot(in.aNormal, normalize(L))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/flatten/swizzle.flatten.vert b/third_party/spirv-cross/reference/shaders-msl/flatten/swizzle.flatten.vert new file mode 100644 index 0000000..05a6bba --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/flatten/swizzle.flatten.vert @@ -0,0 +1,47 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 A; + float2 B0; + float2 B1; + float C0; + float3 C1; + packed_float3 D0; + float D1; + float E0; + float E1; + float E2; + float E3; + float F0; + float2 F1; + float F2; +}; + +struct main0_out +{ + float4 oA [[user(locn0)]]; + float4 oB [[user(locn1)]]; + float4 oC [[user(locn2)]]; + float4 oD [[user(locn3)]]; + float4 oE [[user(locn4)]]; + float4 oF [[user(locn5)]]; + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(constant UBO& _22 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = float4(0.0); + out.oA = _22.A; + out.oB = float4(_22.B0, _22.B1); + out.oC = float4(_22.C0, _22.C1) + float4(_22.C1.xy, _22.C1.z, _22.C0); + out.oD = float4(_22.D0[0], _22.D0[1], _22.D0[2], _22.D1) + float4(float2(_22.D0[0], _22.D0[1]), _22.D0[2u], _22.D1); + out.oE = float4(_22.E0, _22.E1, _22.E2, _22.E3); + out.oF = float4(_22.F0, _22.F1, _22.F2); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/flatten/types.flatten.frag b/third_party/spirv-cross/reference/shaders-msl/flatten/types.flatten.frag new file mode 100644 index 0000000..cee53d9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/flatten/types.flatten.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct UBO1 +{ + int4 c; + int4 d; +}; + +struct UBO2 +{ + uint4 e; + uint4 f; +}; + +struct UBO0 +{ + float4 a; + float4 b; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant UBO1& _14 [[buffer(0)]], constant UBO2& _29 [[buffer(1)]], constant UBO0& _41 [[buffer(2)]]) +{ + main0_out out = {}; + out.FragColor = ((((float4(_14.c) + float4(_14.d)) + float4(_29.e)) + float4(_29.f)) + _41.a) + _41.b; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/argument-buffers.msl2.argument.frag b/third_party/spirv-cross/reference/shaders-msl/frag/argument-buffers.msl2.argument.frag new file mode 100644 index 0000000..b7005ff --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/argument-buffers.msl2.argument.frag @@ -0,0 +1,96 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 ssbo; +}; + +struct SSBOs +{ + float4 ssbo; +}; + +struct Push +{ + float4 push; +}; + +struct UBO +{ + float4 ubo; +}; + +struct UBOs +{ + float4 ubo; +}; + +struct spvDescriptorSetBuffer0 +{ + texture2d uTexture [[id(0)]]; + sampler uTextureSmplr [[id(1)]]; + array, 2> uTextures [[id(2)]]; + array uTexturesSmplr [[id(4)]]; + constant UBO* v_90 [[id(6)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + array, 4> uTexture2 [[id(0)]]; + array uSampler [[id(4)]]; + device SSBO* v_60 [[id(6)]]; + const device SSBOs* ssbos [[id(7)]][2]; +}; + +struct spvDescriptorSetBuffer2 +{ + constant UBOs* ubos [[id(0)]][4]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float4 sample_in_function2(thread texture2d uTexture, thread const sampler uTextureSmplr, thread float2& vUV, thread const array, 4> uTexture2, thread const array uSampler, thread const array, 2> uTextures, thread const array uTexturesSmplr, device SSBO& v_60, const device SSBOs* constant (&ssbos)[2], constant Push& registers) +{ + float4 ret = uTexture.sample(uTextureSmplr, vUV); + ret += uTexture2[2].sample(uSampler[1], vUV); + ret += uTextures[1].sample(uTexturesSmplr[1], vUV); + ret += v_60.ssbo; + ret += ssbos[0]->ssbo; + ret += registers.push; + return ret; +} + +static inline __attribute__((always_inline)) +float4 sample_in_function(thread texture2d uTexture, thread const sampler uTextureSmplr, thread float2& vUV, thread const array, 4> uTexture2, thread const array uSampler, thread const array, 2> uTextures, thread const array uTexturesSmplr, device SSBO& v_60, const device SSBOs* constant (&ssbos)[2], constant Push& registers, constant UBO& v_90, constant UBOs* constant (&ubos)[4]) +{ + float4 ret = sample_in_function2(uTexture, uTextureSmplr, vUV, uTexture2, uSampler, uTextures, uTexturesSmplr, v_60, ssbos, registers); + ret += v_90.ubo; + ret += ubos[0]->ubo; + return ret; +} + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], constant spvDescriptorSetBuffer2& spvDescriptorSet2 [[buffer(2)]], constant Push& registers [[buffer(3)]]) +{ + main0_out out = {}; + out.FragColor = sample_in_function(spvDescriptorSet0.uTexture, spvDescriptorSet0.uTextureSmplr, in.vUV, spvDescriptorSet1.uTexture2, spvDescriptorSet1.uSampler, spvDescriptorSet0.uTextures, spvDescriptorSet0.uTexturesSmplr, (*spvDescriptorSet1.v_60), spvDescriptorSet1.ssbos, registers, (*spvDescriptorSet0.v_90), spvDescriptorSet2.ubos); + out.FragColor += (*spvDescriptorSet0.v_90).ubo; + out.FragColor += (*spvDescriptorSet1.v_60).ssbo; + out.FragColor += spvDescriptorSet2.ubos[1]->ubo; + out.FragColor += registers.push; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/reference/shaders-msl/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..cdedd73 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,62 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _17 = spvUnsafeArray({ 1.0, 2.0, 3.0, 4.0, 5.0 }); + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + for (int i = 0; i < 4; i++, out.FragColor += float4(_17[i])) + { + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/array-of-array-lut.frag b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-array-lut.frag new file mode 100644 index 0000000..ba55382 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-array-lut.frag @@ -0,0 +1,68 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _17 = spvUnsafeArray({ 1.0, 2.0, 3.0 }); +constant spvUnsafeArray _21 = spvUnsafeArray({ 4.0, 5.0, 6.0 }); +constant spvUnsafeArray, 2> _22 = spvUnsafeArray, 2>({ spvUnsafeArray({ 1.0, 2.0, 3.0 }), spvUnsafeArray({ 4.0, 5.0, 6.0 }) }); + +struct main0_out +{ + float vOutput [[color(0)]]; +}; + +struct main0_in +{ + int vIndex1 [[user(locn0)]]; + int vIndex2 [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.vOutput = _22[in.vIndex1][in.vIndex2]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag new file mode 100644 index 0000000..b2340ad --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag @@ -0,0 +1,117 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + uint index; +}; + +struct UBO2 +{ + uint index2; +}; + +struct spvDescriptorSetBuffer0 +{ + array, 4> uSampler [[id(0)]]; + array uSamplerSmplr [[id(4)]]; + constant UBO* uUBO [[id(8)]]; + constant UBO2* m_50 [[id(9)]]; + constant uint* spvSwizzleConstants [[id(10)]]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +static inline __attribute__((always_inline)) +float4 sample_in_func(thread const array, 4> uSampler, thread const array uSamplerSmplr, constant uint* uSamplerSwzl, constant UBO& uUBO, thread float2& vUV) +{ + return spvTextureSwizzle(uSampler[uUBO.index].sample(uSamplerSmplr[uUBO.index], vUV), uSamplerSwzl[uUBO.index]); +} + +static inline __attribute__((always_inline)) +float4 sample_single_in_func(thread const texture2d s, thread const sampler sSmplr, constant uint& sSwzl, thread float2& vUV) +{ + return spvTextureSwizzle(s.sample(sSmplr, vUV), sSwzl); +} + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant uint* spvSwizzleConstants [[buffer(30)]]) +{ + main0_out out = {}; + constant uint* spvDescriptorSet0_uSamplerSwzl = &spvDescriptorSet0.spvSwizzleConstants[0]; + out.FragColor = sample_in_func(spvDescriptorSet0.uSampler, spvDescriptorSet0.uSamplerSmplr, spvDescriptorSet0_uSamplerSwzl, (*spvDescriptorSet0.uUBO), in.vUV); + out.FragColor += sample_single_in_func(spvDescriptorSet0.uSampler[(*spvDescriptorSet0.m_50).index2], spvDescriptorSet0.uSamplerSmplr[(*spvDescriptorSet0.m_50).index2], spvDescriptorSet0_uSamplerSwzl[(*spvDescriptorSet0.m_50).index2], in.vUV); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag new file mode 100644 index 0000000..82da43f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag @@ -0,0 +1,108 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + uint index; +}; + +struct UBO2 +{ + uint index2; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +static inline __attribute__((always_inline)) +float4 sample_in_func(thread const array, 4> uSampler, thread const array uSamplerSmplr, constant uint* uSamplerSwzl, constant UBO& uUBO, thread float2& vUV) +{ + return spvTextureSwizzle(uSampler[uUBO.index].sample(uSamplerSmplr[uUBO.index], vUV), uSamplerSwzl[uUBO.index]); +} + +static inline __attribute__((always_inline)) +float4 sample_single_in_func(thread const texture2d s, thread const sampler sSmplr, constant uint& sSwzl, thread float2& vUV) +{ + return spvTextureSwizzle(s.sample(sSmplr, vUV), sSwzl); +} + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvSwizzleConstants [[buffer(30)]], constant UBO& uUBO [[buffer(0)]], constant UBO2& _50 [[buffer(1)]], array, 4> uSampler [[texture(0)]], array uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + constant uint* uSamplerSwzl = &spvSwizzleConstants[0]; + out.FragColor = sample_in_func(uSampler, uSamplerSmplr, uSamplerSwzl, uUBO, in.vUV); + out.FragColor += sample_single_in_func(uSampler[_50.index2], uSamplerSmplr[_50.index2], uSamplerSwzl[_50.index2], in.vUV); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag new file mode 100644 index 0000000..833ddec --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag @@ -0,0 +1,114 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct spvDescriptorSetBuffer0 +{ + array, 4> uSampler0 [[id(0)]]; + array uSampler0Smplr [[id(4)]]; + constant uint* spvSwizzleConstants [[id(8)]]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +static inline __attribute__((always_inline)) +float4 sample_in_func_1(thread const array, 4> uSampler0, thread const array uSampler0Smplr, constant uint* uSampler0Swzl, thread float2& vUV) +{ + return spvTextureSwizzle(uSampler0[2].sample(uSampler0Smplr[2], vUV), uSampler0Swzl[2]); +} + +static inline __attribute__((always_inline)) +float4 sample_in_func_2(thread float2& vUV, thread texture2d uSampler1, thread const sampler uSampler1Smplr, constant uint& uSampler1Swzl) +{ + return spvTextureSwizzle(uSampler1.sample(uSampler1Smplr, vUV), uSampler1Swzl); +} + +static inline __attribute__((always_inline)) +float4 sample_single_in_func(thread const texture2d s, thread const sampler sSmplr, constant uint& sSwzl, thread float2& vUV) +{ + return spvTextureSwizzle(s.sample(sSmplr, vUV), sSwzl); +} + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], constant uint* spvSwizzleConstants [[buffer(30)]], texture2d uSampler1 [[texture(0)]], sampler uSampler1Smplr [[sampler(0)]]) +{ + main0_out out = {}; + constant uint* spvDescriptorSet0_uSampler0Swzl = &spvDescriptorSet0.spvSwizzleConstants[0]; + constant uint& uSampler1Swzl = spvSwizzleConstants[0]; + out.FragColor = sample_in_func_1(spvDescriptorSet0.uSampler0, spvDescriptorSet0.uSampler0Smplr, spvDescriptorSet0_uSampler0Swzl, in.vUV); + out.FragColor += sample_in_func_2(in.vUV, uSampler1, uSampler1Smplr, uSampler1Swzl); + out.FragColor += sample_single_in_func(spvDescriptorSet0.uSampler0[1], spvDescriptorSet0.uSampler0Smplr[1], spvDescriptorSet0_uSampler0Swzl[1], in.vUV); + out.FragColor += sample_single_in_func(uSampler1, uSampler1Smplr, uSampler1Swzl, in.vUV); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag new file mode 100644 index 0000000..64b361e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag @@ -0,0 +1,98 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template struct spvRemoveReference { typedef T type; }; +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type& x) +{ + return static_cast(x); +} +template inline constexpr thread T&& spvForward(thread typename spvRemoveReference::type&& x) +{ + return static_cast(x); +} + +enum class spvSwizzle : uint +{ + none = 0, + zero, + one, + red, + green, + blue, + alpha +}; + +template +inline T spvGetSwizzle(vec x, T c, spvSwizzle s) +{ + switch (s) + { + case spvSwizzle::none: + return c; + case spvSwizzle::zero: + return 0; + case spvSwizzle::one: + return 1; + case spvSwizzle::red: + return x.r; + case spvSwizzle::green: + return x.g; + case spvSwizzle::blue: + return x.b; + case spvSwizzle::alpha: + return x.a; + } +} + +// Wrapper function that swizzles texture samples and fetches. +template +inline vec spvTextureSwizzle(vec x, uint s) +{ + if (!s) + return x; + return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) & 0xFF)), spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF))); +} + +template +inline T spvTextureSwizzle(T x, uint s) +{ + return spvTextureSwizzle(vec(x, 0, 0, 1), s).x; +} + +static inline __attribute__((always_inline)) +float4 sample_in_func(thread const array, 4> uSampler, thread const array uSamplerSmplr, constant uint* uSamplerSwzl, thread float2& vUV) +{ + return spvTextureSwizzle(uSampler[2].sample(uSamplerSmplr[2], vUV), uSamplerSwzl[2]); +} + +static inline __attribute__((always_inline)) +float4 sample_single_in_func(thread const texture2d s, thread const sampler sSmplr, constant uint& sSwzl, thread float2& vUV) +{ + return spvTextureSwizzle(s.sample(sSmplr, vUV), sSwzl); +} + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvSwizzleConstants [[buffer(30)]], array, 4> uSampler [[texture(0)]], array uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + constant uint* uSamplerSwzl = &spvSwizzleConstants[0]; + out.FragColor = sample_in_func(uSampler, uSamplerSmplr, uSamplerSwzl, in.vUV); + out.FragColor += sample_single_in_func(uSampler[1], uSamplerSmplr[1], uSamplerSwzl[1], in.vUV); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag b/third_party/spirv-cross/reference/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag new file mode 100644 index 0000000..ef19fbf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct Vertices +{ + float2 uvs[1]; +}; + +struct main0_out +{ + float2 value [[color(0)]]; +}; + +struct main0_in +{ + float3 gl_BaryCoordNoPerspNV [[barycentric_coord, center_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]]) +{ + main0_out out = {}; + int prim = int(gl_PrimitiveID); + float2 uv0 = _19.uvs[(3 * prim) + 0]; + float2 uv1 = _19.uvs[(3 * prim) + 1]; + float2 uv2 = _19.uvs[(3 * prim) + 2]; + out.value = ((uv0 * in.gl_BaryCoordNoPerspNV.x) + (uv1 * in.gl_BaryCoordNoPerspNV.y)) + (uv2 * in.gl_BaryCoordNoPerspNV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/barycentric-nv.msl22.frag b/third_party/spirv-cross/reference/shaders-msl/frag/barycentric-nv.msl22.frag new file mode 100644 index 0000000..1d2e4c2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/barycentric-nv.msl22.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct Vertices +{ + float2 uvs[1]; +}; + +struct main0_out +{ + float2 value [[color(0)]]; +}; + +struct main0_in +{ + float3 gl_BaryCoordNV [[barycentric_coord, center_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device Vertices& _19 [[buffer(0)]], uint gl_PrimitiveID [[primitive_id]]) +{ + main0_out out = {}; + int prim = int(gl_PrimitiveID); + float2 uv0 = _19.uvs[(3 * prim) + 0]; + float2 uv1 = _19.uvs[(3 * prim) + 1]; + float2 uv2 = _19.uvs[(3 * prim) + 2]; + out.value = ((uv0 * in.gl_BaryCoordNV.x) + (uv1 * in.gl_BaryCoordNV.y)) + (uv2 * in.gl_BaryCoordNV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/basic.frag b/third_party/spirv-cross/reference/shaders-msl/frag/basic.frag new file mode 100644 index 0000000..f33db61 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/basic.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, in.vTex); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/binary-func-unpack-pack-arguments.frag b/third_party/spirv-cross/reference/shaders-msl/frag/binary-func-unpack-pack-arguments.frag new file mode 100644 index 0000000..134cfe1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/binary-func-unpack-pack-arguments.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + packed_float3 color; + float v; +}; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vIn [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = dot(in.vIn, float3(_15.color)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/binary-unpack-pack-arguments.frag b/third_party/spirv-cross/reference/shaders-msl/frag/binary-unpack-pack-arguments.frag new file mode 100644 index 0000000..8bd538b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/binary-unpack-pack-arguments.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + packed_float3 color; + float v; +}; + +struct main0_out +{ + float3 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vIn [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant UBO& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = cross(in.vIn, float3(_15.color) - in.vIn); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/bitcasting.1d-as-2d.frag b/third_party/spirv-cross/reference/shaders-msl/frag/bitcasting.1d-as-2d.frag new file mode 100644 index 0000000..ea49c06 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/bitcasting.1d-as-2d.frag @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor0 [[color(0)]]; + float4 FragColor1 [[color(1)]]; +}; + +struct main0_in +{ + float4 VertGeom [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d TextureBase [[texture(0)]], texture2d TextureDetail [[texture(1)]], sampler TextureBaseSmplr [[sampler(0)]], sampler TextureDetailSmplr [[sampler(1)]]) +{ + main0_out out = {}; + float4 texSample0 = TextureBase.sample(TextureBaseSmplr, float2(in.VertGeom.x, 0.5)); + float4 texSample1 = TextureDetail.sample(TextureDetailSmplr, float2(in.VertGeom.x, 0.5), int2(3, 0)); + int4 iResult0 = as_type(texSample0); + int4 iResult1 = as_type(texSample1); + out.FragColor0 = as_type(iResult0) * as_type(iResult1); + uint4 uResult0 = as_type(texSample0); + uint4 uResult1 = as_type(texSample1); + out.FragColor1 = as_type(uResult0) * as_type(uResult1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/bitcasting.frag b/third_party/spirv-cross/reference/shaders-msl/frag/bitcasting.frag new file mode 100644 index 0000000..475b573 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/bitcasting.frag @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor0 [[color(0)]]; + float4 FragColor1 [[color(1)]]; +}; + +struct main0_in +{ + float4 VertGeom [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d TextureBase [[texture(0)]], texture2d TextureDetail [[texture(1)]], sampler TextureBaseSmplr [[sampler(0)]], sampler TextureDetailSmplr [[sampler(1)]]) +{ + main0_out out = {}; + float4 texSample0 = TextureBase.sample(TextureBaseSmplr, in.VertGeom.xy); + float4 texSample1 = TextureDetail.sample(TextureDetailSmplr, in.VertGeom.xy, int2(3, 2)); + int4 iResult0 = as_type(texSample0); + int4 iResult1 = as_type(texSample1); + out.FragColor0 = as_type(iResult0) * as_type(iResult1); + uint4 uResult0 = as_type(texSample0); + uint4 uResult1 = as_type(texSample1); + out.FragColor1 = as_type(uResult0) * as_type(uResult1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/buffer-read-write.frag b/third_party/spirv-cross/reference/shaders-msl/frag/buffer-read-write.frag new file mode 100644 index 0000000..166d431 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/buffer-read-write.frag @@ -0,0 +1,27 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +fragment main0_out main0(texture2d buf [[texture(0)]], texture2d bufOut [[texture(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = buf.read(spvTexelBufferCoord(0)); + bufOut.write(out.FragColor, spvTexelBufferCoord(int(gl_FragCoord.x))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag b/third_party/spirv-cross/reference/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag new file mode 100644 index 0000000..71496a4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture_buffer buf [[texture(0)]], texture_buffer bufOut [[texture(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = buf.read(uint(0)); + bufOut.write(out.FragColor, uint(int(gl_FragCoord.x))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/builtins.frag b/third_party/spirv-cross/reference/shaders-msl/frag/builtins.frag new file mode 100644 index 0000000..f908525 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/builtins.frag @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + float gl_FragDepth [[depth(any)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = gl_FragCoord + in.vColor; + out.gl_FragDepth = 0.5; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/clip-distance-varying.frag b/third_party/spirv-cross/reference/shaders-msl/frag/clip-distance-varying.frag new file mode 100644 index 0000000..9a72d5b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/clip-distance-varying.frag @@ -0,0 +1,67 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray gl_ClipDistance = {}; + gl_ClipDistance[0] = in.gl_ClipDistance_0; + gl_ClipDistance[1] = in.gl_ClipDistance_1; + out.FragColor = float4((1.0 - gl_ClipDistance[0]) - gl_ClipDistance[1]); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/reference/shaders-msl/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..9cd8bdc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 results[1024]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int vIn [[user(locn0)]]; + int vIn2 [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], device UBO& _34 [[buffer(0)]], texture2d Buf [[texture(0)]], sampler BufSmplr [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + int4 coords = Buf.read(uint2(int2(gl_FragCoord.xy)), 0); + float4 foo = _34.results[coords.x % 16]; + int c = in.vIn * in.vIn; + int d = in.vIn2 * in.vIn2; + out.FragColor = (foo + foo) + _34.results[c + d]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/composite-extract-forced-temporary.frag b/third_party/spirv-cross/reference/shaders-msl/frag/composite-extract-forced-temporary.frag new file mode 100644 index 0000000..dfab4d2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/composite-extract-forced-temporary.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vTexCoord [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d Texture [[texture(0)]], sampler TextureSmplr [[sampler(0)]]) +{ + main0_out out = {}; + float f = Texture.sample(TextureSmplr, in.vTexCoord).x; + out.FragColor = float4(f * f); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/constant-array.frag b/third_party/spirv-cross/reference/shaders-msl/frag/constant-array.frag new file mode 100644 index 0000000..9908607 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/constant-array.frag @@ -0,0 +1,83 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Foobar +{ + float a; + float b; +}; + +constant spvUnsafeArray _37 = spvUnsafeArray({ float4(1.0), float4(2.0), float4(3.0) }); +constant spvUnsafeArray _49 = spvUnsafeArray({ float4(1.0), float4(2.0) }); +constant spvUnsafeArray _54 = spvUnsafeArray({ float4(8.0), float4(10.0) }); +constant spvUnsafeArray, 2> _55 = spvUnsafeArray, 2>({ spvUnsafeArray({ float4(1.0), float4(2.0) }), spvUnsafeArray({ float4(8.0), float4(10.0) }) }); +constant spvUnsafeArray _75 = spvUnsafeArray({ Foobar{ 10.0, 40.0 }, Foobar{ 90.0, 70.0 } }); + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float4 resolve(thread const Foobar& f) +{ + return float4(f.a + f.b); +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Foobar param = Foobar{ 10.0, 20.0 }; + Foobar param_1 = _75[in.index]; + out.FragColor = ((_37[in.index] + _55[in.index][in.index + 1]) + resolve(param)) + resolve(param_1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/constant-composites.frag b/third_party/spirv-cross/reference/shaders-msl/frag/constant-composites.frag new file mode 100644 index 0000000..e0fa980 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/constant-composites.frag @@ -0,0 +1,74 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Foo +{ + float a; + float b; +}; + +constant spvUnsafeArray _16 = spvUnsafeArray({ 1.0, 4.0, 3.0, 2.0 }); + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int line [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + spvUnsafeArray _28 = spvUnsafeArray({ Foo{ 10.0, 20.0 }, Foo{ 30.0, 40.0 } }); + + main0_out out = {}; + out.FragColor = float4(_16[in.line]); + out.FragColor += float4(_28[in.line].a * _28[1 - in.line].a); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/reference/shaders-msl/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..4d10167 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,45 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vInput [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = in.vInput; + float4 t = uSampler.sample(uSamplerSmplr, in.vInput.xy); + float4 d0 = dfdx(in.vInput); + float4 d1 = dfdy(in.vInput); + float4 d2 = fwidth(in.vInput); + float4 d3 = dfdx(in.vInput); + float4 d4 = dfdy(in.vInput); + float4 d5 = fwidth(in.vInput); + float4 d6 = dfdx(in.vInput); + float4 d7 = dfdy(in.vInput); + float4 d8 = fwidth(in.vInput); + if (in.vInput.y > 10.0) + { + out.FragColor += t; + out.FragColor += d0; + out.FragColor += d1; + out.FragColor += d2; + out.FragColor += d3; + out.FragColor += d4; + out.FragColor += d5; + out.FragColor += d6; + out.FragColor += d7; + out.FragColor += d8; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/depth-greater-than.frag b/third_party/spirv-cross/reference/shaders-msl/frag/depth-greater-than.frag new file mode 100644 index 0000000..5861509 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/depth-greater-than.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float gl_FragDepth [[depth(greater)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.gl_FragDepth = 0.5; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/depth-less-than.frag b/third_party/spirv-cross/reference/shaders-msl/frag/depth-less-than.frag new file mode 100644 index 0000000..f1177fa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/depth-less-than.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float gl_FragDepth [[depth(less)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.gl_FragDepth = 0.5; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/disable-frag-output.frag-output.frag b/third_party/spirv-cross/reference/shaders-msl/frag/disable-frag-output.frag-output.frag new file mode 100644 index 0000000..63bc45b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/disable-frag-output.frag-output.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 buf1 [[color(1)]]; + float4 buf3 [[color(3)]]; + float4 buf6 [[color(6)]]; + float4 buf7 [[color(7)]]; +}; + +fragment main0_out main0() +{ + float4 buf0; + float4 buf2; + float4 buf4; + float4 buf5; + float gl_FragDepth; + int gl_FragStencilRefARB; + main0_out out = {}; + buf0 = float4(0.0, 0.0, 0.0, 1.0); + out.buf1 = float4(1.0, 0.0, 0.0, 1.0); + buf2 = float4(0.0, 1.0, 0.0, 1.0); + out.buf3 = float4(0.0, 0.0, 1.0, 1.0); + buf4 = float4(1.0, 0.0, 1.0, 0.5); + buf5 = float4(0.25); + out.buf6 = float4(0.75); + out.buf7 = float4(1.0); + gl_FragDepth = 0.89999997615814208984375; + gl_FragStencilRefARB = uint(127); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/dual-source-blending.frag b/third_party/spirv-cross/reference/shaders-msl/frag/dual-source-blending.frag new file mode 100644 index 0000000..37938bf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/dual-source-blending.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor0 [[color(0), index(0)]]; + float4 FragColor1 [[color(0), index(1)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor0 = float4(1.0); + out.FragColor1 = float4(2.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/early-fragment-tests.frag b/third_party/spirv-cross/reference/shaders-msl/frag/early-fragment-tests.frag new file mode 100644 index 0000000..850fdc9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/early-fragment-tests.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +[[ early_fragment_tests ]] fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/false-loop-init.frag b/third_party/spirv-cross/reference/shaders-msl/frag/false-loop-init.frag new file mode 100644 index 0000000..7a4d6d5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/false-loop-init.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 result [[color(0)]]; +}; + +struct main0_in +{ + float4 accum [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.result = float4(0.0); + uint j; + for (int i = 0; i < 4; i += int(j)) + { + if (in.accum.y > 10.0) + { + j = 40u; + } + else + { + j = 30u; + } + out.result += in.accum; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/flush_params.frag b/third_party/spirv-cross/reference/shaders-msl/frag/flush_params.frag new file mode 100644 index 0000000..905a179 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/flush_params.frag @@ -0,0 +1,40 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Structy +{ + float4 c; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +void foo2(thread Structy& f) +{ + f.c = float4(10.0); +} + +static inline __attribute__((always_inline)) +Structy foo() +{ + Structy param; + foo2(param); + Structy f = param; + return f; +} + +fragment main0_out main0() +{ + main0_out out = {}; + Structy s = foo(); + out.FragColor = s.c; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/reference/shaders-msl/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..847ec65 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(0.0); + int i = 0; + int _36; + for (;;) + { + if (i < 3) + { + int a = i; + out.FragColor[a] += float(i); + if (false) + { + _36 = 1; + } + else + { + int _41 = i; + i = _41 + 1; + _36 = _41; + } + continue; + } + else + { + break; + } + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/for-loop-init.frag b/third_party/spirv-cross/reference/shaders-msl/frag/for-loop-init.frag new file mode 100644 index 0000000..9f3191b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/for-loop-init.frag @@ -0,0 +1,58 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + int FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = 16; + for (int i = 0; i < 25; i++) + { + out.FragColor += 10; + } + for (int i_1 = 1, j = 4; i_1 < 30; i_1++, j += 4) + { + out.FragColor += 11; + } + int k = 0; + for (; k < 20; k++) + { + out.FragColor += 12; + } + k += 3; + out.FragColor += k; + int l; + if (k == 40) + { + l = 0; + for (; l < 40; l++) + { + out.FragColor += 13; + } + return out; + } + else + { + l = k; + out.FragColor += l; + } + int2 i_2 = int2(0); + for (; i_2.x < 10; i_2.x += 4) + { + out.FragColor += i_2.y; + } + int o = k; + for (int m = k; m < 40; m++) + { + out.FragColor += m; + } + out.FragColor += o; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/fp16-packing.frag b/third_party/spirv-cross/reference/shaders-msl/frag/fp16-packing.frag new file mode 100644 index 0000000..e21feb4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/fp16-packing.frag @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float2 FP32Out [[color(0)]]; + uint FP16Out [[color(1)]]; +}; + +struct main0_in +{ + uint FP16 [[user(locn0)]]; + float2 FP32 [[user(locn1), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FP32Out = float2(as_type(in.FP16)); + out.FP16Out = as_type(half2(in.FP32)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/fragment-component-padding.pad-fragment.frag b/third_party/spirv-cross/reference/shaders-msl/frag/fragment-component-padding.pad-fragment.frag new file mode 100644 index 0000000..9a8e14d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/fragment-component-padding.pad-fragment.frag @@ -0,0 +1,82 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColors_0 [[color(0)]]; + float4 FragColors_1 [[color(1)]]; + float4 FragColor2 [[color(2)]]; + float4 FragColor3 [[color(3)]]; +}; + +struct main0_in +{ + float3 vColor [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +void set_globals(thread spvUnsafeArray (&FragColors), thread float3& vColor, thread float2& FragColor2, thread float3& FragColor3) +{ + FragColors[0] = vColor.x; + FragColors[1] = vColor.y; + FragColor2 = vColor.xz; + FragColor3 = vColor.zzz; +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray FragColors = {}; + float2 FragColor2 = {}; + float3 FragColor3 = {}; + set_globals(FragColors, in.vColor, FragColor2, FragColor3); + out.FragColors_0 = float4(FragColors[0]); + out.FragColors_1 = float4(FragColors[1]); + out.FragColor2.xy = FragColor2; + out.FragColor3.xyz = FragColor3; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/front-facing.frag b/third_party/spirv-cross/reference/shaders-msl/frag/front-facing.frag new file mode 100644 index 0000000..2f83642 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/front-facing.frag @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vA [[user(locn0)]]; + float4 vB [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], bool gl_FrontFacing [[front_facing]]) +{ + main0_out out = {}; + if (gl_FrontFacing) + { + out.FragColor = in.vA; + } + else + { + out.FragColor = in.vB; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/gather-dref.frag b/third_party/spirv-cross/reference/shaders-msl/frag/gather-dref.frag new file mode 100644 index 0000000..c5c5ccf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/gather-dref.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uT [[texture(0)]], sampler uTSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uT.gather_compare(uTSmplr, in.vUV.xy, in.vUV.z); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/gather-offset.frag b/third_party/spirv-cross/reference/shaders-msl/frag/gather-offset.frag new file mode 100644 index 0000000..02b8019 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/gather-offset.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uT [[texture(0)]], sampler uTSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uT.gather(uTSmplr, float2(0.5), int2(0), component::w); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/helper-invocation.msl21.frag b/third_party/spirv-cross/reference/shaders-msl/frag/helper-invocation.msl21.frag new file mode 100644 index 0000000..97d69e1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/helper-invocation.msl21.frag @@ -0,0 +1,40 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float4 foo(thread bool& gl_HelperInvocation, thread texture2d uSampler, thread const sampler uSamplerSmplr, thread float2& vUV) +{ + float4 color; + if (!gl_HelperInvocation) + { + color = uSampler.sample(uSamplerSmplr, vUV, level(0.0)); + } + else + { + color = float4(1.0); + } + return color; +} + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + bool gl_HelperInvocation = simd_is_helper_thread(); + out.FragColor = foo(gl_HelperInvocation, uSampler, uSamplerSmplr, in.vUV); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag b/third_party/spirv-cross/reference/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag new file mode 100644 index 0000000..0e35c24 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag @@ -0,0 +1,58 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4 v; +}; + +struct spvDescriptorSetBuffer0 +{ + array, 10000> uSamplers [[id(0)]]; + array uSamplersSmplr [[id(10000)]]; +}; + +struct spvDescriptorSetBuffer1 +{ + constant UBO* vs [[id(0)]][10000]; +}; + +struct spvDescriptorSetBuffer2 +{ + texture2d uSampler [[id(0)]]; + sampler uSamplerSmplr [[id(1)]]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float4 samp_array(thread const array, 10000> uSamplers, thread const array uSamplersSmplr, thread float2& vUV, constant UBO* const device (&vs)[10000]) +{ + return uSamplers[9999].sample(uSamplersSmplr[9999], vUV) + vs[5000]->v; +} + +static inline __attribute__((always_inline)) +float4 samp_single(thread float2& vUV, thread texture2d uSampler, thread const sampler uSamplerSmplr) +{ + return uSampler.sample(uSamplerSmplr, vUV); +} + +fragment main0_out main0(main0_in in [[stage_in]], const device spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], const device spvDescriptorSetBuffer1& spvDescriptorSet1 [[buffer(1)]], constant spvDescriptorSetBuffer2& spvDescriptorSet2 [[buffer(2)]]) +{ + main0_out out = {}; + out.FragColor = samp_array(spvDescriptorSet0.uSamplers, spvDescriptorSet0.uSamplersSmplr, in.vUV, spvDescriptorSet1.vs) + samp_single(in.vUV, spvDescriptorSet2.uSampler, spvDescriptorSet2.uSamplerSmplr); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/illegal-name-test-0.frag b/third_party/spirv-cross/reference/shaders-msl/frag/illegal-name-test-0.frag new file mode 100644 index 0000000..6b209b4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/illegal-name-test-0.frag @@ -0,0 +1,21 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + float4 fragment0 = float4(10.0); + float4 compute0 = float4(10.0); + float4 kernel0 = float4(10.0); + float4 vertex0 = float4(10.0); + out.FragColor = ((fragment0 + compute0) + kernel0) + vertex0; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/image-query-lod.msl22.frag b/third_party/spirv-cross/reference/shaders-msl/frag/image-query-lod.msl22.frag new file mode 100644 index 0000000..6e7991f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/image-query-lod.msl22.frag @@ -0,0 +1,78 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float2 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +void from_function(thread float2& FragColor, thread texture2d uSampler2D, thread const sampler uSampler2DSmplr, thread float3& vUV, thread texture3d uSampler3D, thread const sampler uSampler3DSmplr, thread texturecube uSamplerCube, thread const sampler uSamplerCubeSmplr, thread texture2d uTexture2D, thread sampler uSampler, thread texture3d uTexture3D, thread texturecube uTextureCube) +{ + float2 _22; + _22.x = uSampler2D.calculate_clamped_lod(uSampler2DSmplr, vUV.xy); + _22.y = uSampler2D.calculate_unclamped_lod(uSampler2DSmplr, vUV.xy); + FragColor += _22; + float2 _31; + _31.x = uSampler3D.calculate_clamped_lod(uSampler3DSmplr, vUV); + _31.y = uSampler3D.calculate_unclamped_lod(uSampler3DSmplr, vUV); + FragColor += _31; + float2 _40; + _40.x = uSamplerCube.calculate_clamped_lod(uSamplerCubeSmplr, vUV); + _40.y = uSamplerCube.calculate_unclamped_lod(uSamplerCubeSmplr, vUV); + FragColor += _40; + float2 _53; + _53.x = uTexture2D.calculate_clamped_lod(uSampler, vUV.xy); + _53.y = uTexture2D.calculate_unclamped_lod(uSampler, vUV.xy); + FragColor += _53; + float2 _62; + _62.x = uTexture3D.calculate_clamped_lod(uSampler, vUV); + _62.y = uTexture3D.calculate_unclamped_lod(uSampler, vUV); + FragColor += _62; + float2 _71; + _71.x = uTextureCube.calculate_clamped_lod(uSampler, vUV); + _71.y = uTextureCube.calculate_unclamped_lod(uSampler, vUV); + FragColor += _71; +} + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler2D [[texture(0)]], texture3d uSampler3D [[texture(1)]], texturecube uSamplerCube [[texture(2)]], texture2d uTexture2D [[texture(3)]], texture3d uTexture3D [[texture(4)]], texturecube uTextureCube [[texture(5)]], sampler uSampler2DSmplr [[sampler(0)]], sampler uSampler3DSmplr [[sampler(1)]], sampler uSamplerCubeSmplr [[sampler(2)]], sampler uSampler [[sampler(3)]]) +{ + main0_out out = {}; + out.FragColor = float2(0.0); + float2 _79; + _79.x = uSampler2D.calculate_clamped_lod(uSampler2DSmplr, in.vUV.xy); + _79.y = uSampler2D.calculate_unclamped_lod(uSampler2DSmplr, in.vUV.xy); + out.FragColor += _79; + float2 _84; + _84.x = uSampler3D.calculate_clamped_lod(uSampler3DSmplr, in.vUV); + _84.y = uSampler3D.calculate_unclamped_lod(uSampler3DSmplr, in.vUV); + out.FragColor += _84; + float2 _89; + _89.x = uSamplerCube.calculate_clamped_lod(uSamplerCubeSmplr, in.vUV); + _89.y = uSamplerCube.calculate_unclamped_lod(uSamplerCubeSmplr, in.vUV); + out.FragColor += _89; + float2 _97; + _97.x = uTexture2D.calculate_clamped_lod(uSampler, in.vUV.xy); + _97.y = uTexture2D.calculate_unclamped_lod(uSampler, in.vUV.xy); + out.FragColor += _97; + float2 _104; + _104.x = uTexture3D.calculate_clamped_lod(uSampler, in.vUV); + _104.y = uTexture3D.calculate_unclamped_lod(uSampler, in.vUV); + out.FragColor += _104; + float2 _111; + _111.x = uTextureCube.calculate_clamped_lod(uSampler, in.vUV); + _111.y = uTextureCube.calculate_unclamped_lod(uSampler, in.vUV); + out.FragColor += _111; + from_function(out.FragColor, uSampler2D, uSampler2DSmplr, in.vUV, uSampler3D, uSampler3DSmplr, uSamplerCube, uSamplerCubeSmplr, uTexture2D, uSampler, uTexture3D, uTextureCube); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/in_block.frag b/third_party/spirv-cross/reference/shaders-msl/frag/in_block.frag new file mode 100644 index 0000000..8178c9a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/in_block.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +struct VertexOut +{ + float4 color; + float4 color2; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 VertexOut_color [[user(locn2)]]; + float4 VertexOut_color2 [[user(locn3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + VertexOut inputs = {}; + inputs.color = in.VertexOut_color; + inputs.color2 = in.VertexOut_color2; + out.FragColor = inputs.color + inputs.color2; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/in_mat.frag b/third_party/spirv-cross/reference/shaders-msl/frag/in_mat.frag new file mode 100644 index 0000000..70ff486 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/in_mat.frag @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 outFragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 inPos [[user(locn0)]]; + float3 inNormal [[user(locn1)]]; + float4 inInvModelView_0 [[user(locn2)]]; + float4 inInvModelView_1 [[user(locn3)]]; + float4 inInvModelView_2 [[user(locn4)]]; + float4 inInvModelView_3 [[user(locn5)]]; + float inLodBias [[user(locn6)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texturecube samplerColor [[texture(0)]], sampler samplerColorSmplr [[sampler(0)]]) +{ + main0_out out = {}; + float4x4 inInvModelView = {}; + inInvModelView[0] = in.inInvModelView_0; + inInvModelView[1] = in.inInvModelView_1; + inInvModelView[2] = in.inInvModelView_2; + inInvModelView[3] = in.inInvModelView_3; + float3 cI = normalize(in.inPos); + float3 cR = reflect(cI, normalize(in.inNormal)); + cR = float3((inInvModelView * float4(cR, 0.0)).xyz); + cR.x *= (-1.0); + out.outFragColor = samplerColor.sample(samplerColorSmplr, cR, bias(in.inLodBias)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag new file mode 100644 index 0000000..30ad95b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 load_subpasses(thread const texture2d_ms_array uInput, thread uint& gl_SampleID, thread float4& gl_FragCoord, thread uint& gl_Layer) +{ + float4 _24 = uInput.read(uint2(gl_FragCoord.xy), gl_Layer, gl_SampleID); + return _24; +} + +fragment main0_out main0(texture2d_ms_array uSubpass0 [[texture(0)]], texture2d_ms_array uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]], uint gl_Layer [[render_target_array_index]]) +{ + main0_out out = {}; + out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), gl_Layer, 1) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_Layer, 2)) + load_subpasses(uSubpass0, gl_SampleID, gl_FragCoord, gl_Layer); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.frag b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.frag new file mode 100644 index 0000000..25d2e7d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.frag @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 load_subpasses(thread const texture2d_ms uInput, thread uint& gl_SampleID, thread float4& gl_FragCoord) +{ + float4 _24 = uInput.read(uint2(gl_FragCoord.xy), gl_SampleID); + return _24; +} + +fragment main0_out main0(texture2d_ms uSubpass0 [[texture(0)]], texture2d_ms uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), 1) + uSubpass1.read(uint2(gl_FragCoord.xy), 2)) + load_subpasses(uSubpass0, gl_SampleID, gl_FragCoord); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag new file mode 100644 index 0000000..a2e5ef7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag @@ -0,0 +1,27 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 load_subpasses(thread const texture2d_ms_array uInput, thread uint& gl_SampleID, thread float4& gl_FragCoord, thread uint& gl_ViewIndex) +{ + float4 _24 = uInput.read(uint2(gl_FragCoord.xy), gl_ViewIndex, gl_SampleID); + return _24; +} + +fragment main0_out main0(constant uint* spvViewMask [[buffer(24)]], texture2d_ms_array uSubpass0 [[texture(0)]], texture2d_ms_array uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]], uint gl_ViewIndex [[render_target_array_index]]) +{ + main0_out out = {}; + gl_ViewIndex += spvViewMask[0]; + out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), gl_ViewIndex, 1) + uSubpass1.read(uint2(gl_FragCoord.xy), gl_ViewIndex, 2)) + load_subpasses(uSubpass0, gl_SampleID, gl_FragCoord, gl_ViewIndex); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.arrayed-subpass.frag b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.arrayed-subpass.frag new file mode 100644 index 0000000..76d0a3c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.arrayed-subpass.frag @@ -0,0 +1,25 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 load_subpasses(thread const texture2d_array uInput, thread float4& gl_FragCoord, thread uint& gl_Layer) +{ + return uInput.read(uint2(gl_FragCoord.xy), gl_Layer); +} + +fragment main0_out main0(texture2d_array uSubpass0 [[texture(0)]], texture2d_array uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]], uint gl_Layer [[render_target_array_index]]) +{ + main0_out out = {}; + out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy), gl_Layer) + load_subpasses(uSubpass1, gl_FragCoord, gl_Layer); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.frag b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.frag new file mode 100644 index 0000000..93bbaec --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.frag @@ -0,0 +1,25 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 load_subpasses(thread const texture2d uInput, thread float4& gl_FragCoord) +{ + return uInput.read(uint2(gl_FragCoord.xy)); +} + +fragment main0_out main0(texture2d uSubpass0 [[texture(0)]], texture2d uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy)) + load_subpasses(uSubpass1, gl_FragCoord); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.multiview.frag b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.multiview.frag new file mode 100644 index 0000000..931790b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/input-attachment.multiview.frag @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 load_subpasses(thread const texture2d_array uInput, thread float4& gl_FragCoord, thread uint& gl_ViewIndex) +{ + return uInput.read(uint2(gl_FragCoord.xy), gl_ViewIndex); +} + +fragment main0_out main0(constant uint* spvViewMask [[buffer(24)]], texture2d_array uSubpass0 [[texture(0)]], texture2d_array uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]], uint gl_ViewIndex [[render_target_array_index]]) +{ + main0_out out = {}; + gl_ViewIndex += spvViewMask[0]; + out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy), gl_ViewIndex) + load_subpasses(uSubpass1, gl_FragCoord, gl_ViewIndex); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/interpolation-qualifiers-block.frag b/third_party/spirv-cross/reference/shaders-msl/frag/interpolation-qualifiers-block.frag new file mode 100644 index 0000000..2b42019 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/interpolation-qualifiers-block.frag @@ -0,0 +1,47 @@ +#include +#include + +using namespace metal; + +struct Input +{ + float2 v0; + float2 v1; + float3 v2; + float4 v3; + float v4; + float v5; + float v6; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 Input_v0 [[user(locn0), centroid_no_perspective]]; + float2 Input_v1 [[user(locn1), centroid_no_perspective]]; + float3 Input_v2 [[user(locn2), centroid_no_perspective]]; + float4 Input_v3 [[user(locn3), centroid_no_perspective]]; + float Input_v4 [[user(locn4), centroid_no_perspective]]; + float Input_v5 [[user(locn5), centroid_no_perspective]]; + float Input_v6 [[user(locn6), centroid_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Input inp = {}; + inp.v0 = in.Input_v0; + inp.v1 = in.Input_v1; + inp.v2 = in.Input_v2; + inp.v3 = in.Input_v3; + inp.v4 = in.Input_v4; + inp.v5 = in.Input_v5; + inp.v6 = in.Input_v6; + out.FragColor = float4(inp.v0.x + inp.v1.y, inp.v2.xy, ((inp.v3.w * inp.v4) + inp.v5) - inp.v6); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/interpolation-qualifiers.frag b/third_party/spirv-cross/reference/shaders-msl/frag/interpolation-qualifiers.frag new file mode 100644 index 0000000..aff6e1b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/interpolation-qualifiers.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 v0 [[user(locn0)]]; + float2 v1 [[user(locn1), center_no_perspective]]; + float3 v2 [[user(locn2), centroid_perspective]]; + float4 v3 [[user(locn3), centroid_no_perspective]]; + float v4 [[user(locn4), sample_perspective]]; + float v5 [[user(locn5), sample_no_perspective]]; + float v6 [[user(locn6), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = float4(in.v0.x + in.v1.y, in.v2.xy, ((in.v3.w * in.v4) + in.v5) - in.v6); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/lut-promotion.frag b/third_party/spirv-cross/reference/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000..b1e0e73 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/lut-promotion.frag @@ -0,0 +1,92 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _16 = spvUnsafeArray({ 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0 }); +constant spvUnsafeArray _60 = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); +constant spvUnsafeArray _104 = spvUnsafeArray({ float4(20.0), float4(30.0), float4(50.0), float4(60.0) }); + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = _16[in.index]; + if (in.index < 10) + { + out.FragColor += _16[in.index ^ 1]; + } + else + { + out.FragColor += _16[in.index & 1]; + } + if (in.index > 30) + { + out.FragColor += _60[in.index & 3].y; + } + else + { + out.FragColor += _60[in.index & 1].x; + } + spvUnsafeArray foobar = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); + if (in.index > 30) + { + foobar[1].z = 20.0; + } + out.FragColor += foobar[in.index & 3].z; + spvUnsafeArray baz = spvUnsafeArray({ float4(0.0), float4(1.0), float4(8.0), float4(5.0) }); + baz = _104; + out.FragColor += baz[in.index & 3].z; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/mix.frag b/third_party/spirv-cross/reference/shaders-msl/frag/mix.frag new file mode 100644 index 0000000..ee28bf9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/mix.frag @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vIn0 [[user(locn0)]]; + float4 vIn1 [[user(locn1)]]; + float vIn2 [[user(locn2)]]; + float vIn3 [[user(locn3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + bool4 l = bool4(false, true, false, false); + out.FragColor = select(in.vIn0, in.vIn1, l); + bool f = true; + out.FragColor = float4(f ? in.vIn3 : in.vIn2); + out.FragColor = select(in.vIn1, in.vIn0, bool4(f)); + out.FragColor = float4(f ? in.vIn2 : in.vIn3); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/mrt-array.frag b/third_party/spirv-cross/reference/shaders-msl/frag/mrt-array.frag new file mode 100644 index 0000000..2746ad6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/mrt-array.frag @@ -0,0 +1,94 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor_0 [[color(0)]]; + float4 FragColor_1 [[color(1)]]; + float4 FragColor_2 [[color(2)]]; + float4 FragColor_3 [[color(3)]]; +}; + +struct main0_in +{ + float4 vA [[user(locn0)]]; + float4 vB [[user(locn1)]]; +}; + +// Implementation of the GLSL mod() function, which is slightly different than Metal fmod() +template +inline Tx mod(Tx x, Ty y) +{ + return x - y * floor(x / y); +} + +static inline __attribute__((always_inline)) +void write_deeper_in_function(thread spvUnsafeArray (&FragColor), thread float4& vA, thread float4& vB) +{ + FragColor[3] = vA * vB; +} + +static inline __attribute__((always_inline)) +void write_in_function(thread spvUnsafeArray (&FragColor), thread float4& vA, thread float4& vB) +{ + FragColor[2] = vA - vB; + write_deeper_in_function(FragColor, vA, vB); +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray FragColor = {}; + FragColor[0] = mod(in.vA, in.vB); + FragColor[1] = in.vA + in.vB; + write_in_function(FragColor, in.vA, in.vB); + out.FragColor_0 = FragColor[0]; + out.FragColor_1 = FragColor[1]; + out.FragColor_2 = FragColor[2]; + out.FragColor_3 = FragColor[3]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/nonuniform-qualifier.msl2.frag b/third_party/spirv-cross/reference/shaders-msl/frag/nonuniform-qualifier.msl2.frag new file mode 100644 index 0000000..e896c2d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/nonuniform-qualifier.msl2.frag @@ -0,0 +1,56 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4 v[64]; +}; + +struct SSBO +{ + float4 v[1]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int vIndex [[user(locn0)]]; + float2 vUV [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant UBO* ubos_0 [[buffer(0)]], constant UBO* ubos_1 [[buffer(1)]], const device SSBO* ssbos_0 [[buffer(2)]], const device SSBO* ssbos_1 [[buffer(3)]], array, 8> uSamplers [[texture(0)]], array, 8> uCombinedSamplers [[texture(8)]], array uSamps [[sampler(0)]], array uCombinedSamplersSmplr [[sampler(7)]]) +{ + constant UBO* ubos[] = + { + ubos_0, + ubos_1, + }; + + const device SSBO* ssbos[] = + { + ssbos_0, + ssbos_1, + }; + + main0_out out = {}; + int i = in.vIndex; + int _25 = i + 10; + int _37 = i + 40; + out.FragColor = uSamplers[_25].sample(uSamps[_37], in.vUV); + int _53 = i + 10; + out.FragColor = uCombinedSamplers[_53].sample(uCombinedSamplersSmplr[_53], in.vUV); + int _69 = i + 20; + int _73 = i + 40; + out.FragColor += ubos[(_69)]->v[_73]; + int _87 = i + 50; + int _91 = i + 60; + out.FragColor += ssbos[(_87)]->v[_91]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/packed-expression-vector-shuffle.frag b/third_party/spirv-cross/reference/shaders-msl/frag/packed-expression-vector-shuffle.frag new file mode 100644 index 0000000..dc89474 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/packed-expression-vector-shuffle.frag @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + packed_float3 color; + float v; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant UBO& _15 [[buffer(0)]]) +{ + main0_out out = {}; + float4 f = float4(1.0); + f = float4(_15.color[0], _15.color[1], _15.color[2], f.w); + out.FragColor = f; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/packing-test-3.frag b/third_party/spirv-cross/reference/shaders-msl/frag/packing-test-3.frag new file mode 100644 index 0000000..f82d8a5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/packing-test-3.frag @@ -0,0 +1,55 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct VertexOutput +{ + float4 HPosition; +}; + +struct TestStruct +{ + float3 position; + float radius; +}; + +struct TestStruct_1 +{ + packed_float3 position; + float radius; +}; + +struct CB0 +{ + TestStruct_1 CB0[16]; +}; + +struct main0_out +{ + float4 _entryPointOutput [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 _main(thread const VertexOutput& IN, constant CB0& _RESERVED_IDENTIFIER_FIXUP_24) +{ + TestStruct st; + st.position = float3(_RESERVED_IDENTIFIER_FIXUP_24.CB0[1].position); + st.radius = _RESERVED_IDENTIFIER_FIXUP_24.CB0[1].radius; + float4 col = float4(st.position, st.radius); + return col; +} + +fragment main0_out main0(constant CB0& _RESERVED_IDENTIFIER_FIXUP_24 [[buffer(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + VertexOutput IN; + IN.HPosition = gl_FragCoord; + VertexOutput param = IN; + VertexOutput param_1 = param; + out._entryPointOutput = _main(param_1, _RESERVED_IDENTIFIER_FIXUP_24); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag b/third_party/spirv-cross/reference/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag new file mode 100644 index 0000000..adea453 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag @@ -0,0 +1,53 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct Buffer3 +{ + int baz; +}; + +struct Buffer +{ + int foo; + uint bar; +}; + +struct Buffer2 +{ + uint quux; +}; + +struct spvDescriptorSetBuffer0 +{ + device Buffer3* m_9 [[id(0)]]; + texture2d img4 [[id(1)]]; + texture2d img [[id(2), raster_order_group(0)]]; + texture2d img3 [[id(3), raster_order_group(0)]]; + texture2d img2 [[id(4), raster_order_group(0)]]; + device atomic_uint* img2_atomic [[id(5), raster_order_group(0)]]; + volatile device Buffer* m_42 [[id(6), raster_order_group(0)]]; + device Buffer2* m_52 [[id(7), raster_order_group(0)]]; +}; + +// The required alignment of a linear texture of R32Uint format. +constant uint spvLinearTextureAlignmentOverride [[function_constant(65535)]]; +constant uint spvLinearTextureAlignment = is_function_constant_defined(spvLinearTextureAlignmentOverride) ? spvLinearTextureAlignmentOverride : 4; +// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics +#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + spvLinearTextureAlignment / 4 - 1) & ~( spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x) + +fragment void main0(constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]]) +{ + (*spvDescriptorSet0.m_9).baz = 0; + spvDescriptorSet0.img4.write(float4(1.0, 0.0, 0.0, 1.0), uint2(int2(1))); + spvDescriptorSet0.img.write(spvDescriptorSet0.img3.read(uint2(int2(0))), uint2(int2(0))); + uint _39 = atomic_fetch_add_explicit((device atomic_uint*)&spvDescriptorSet0.img2_atomic[spvImage2DAtomicCoord(int2(0), spvDescriptorSet0.img2)], 1u, memory_order_relaxed); + (*spvDescriptorSet0.m_42).foo += 42; + uint _55 = atomic_fetch_and_explicit((volatile device atomic_uint*)&(*spvDescriptorSet0.m_42).bar, (*spvDescriptorSet0.m_52).quux, memory_order_relaxed); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/pixel-interlock-ordered.msl2.frag b/third_party/spirv-cross/reference/shaders-msl/frag/pixel-interlock-ordered.msl2.frag new file mode 100644 index 0000000..e409ea0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/pixel-interlock-ordered.msl2.frag @@ -0,0 +1,41 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct Buffer3 +{ + int baz; +}; + +struct Buffer +{ + int foo; + uint bar; +}; + +struct Buffer2 +{ + uint quux; +}; + +// The required alignment of a linear texture of R32Uint format. +constant uint spvLinearTextureAlignmentOverride [[function_constant(65535)]]; +constant uint spvLinearTextureAlignment = is_function_constant_defined(spvLinearTextureAlignmentOverride) ? spvLinearTextureAlignmentOverride : 4; +// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics +#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + spvLinearTextureAlignment / 4 - 1) & ~( spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x) + +fragment void main0(device Buffer3& _9 [[buffer(0)]], volatile device Buffer& _42 [[buffer(2), raster_order_group(0)]], device Buffer2& _52 [[buffer(3), raster_order_group(0)]], texture2d img4 [[texture(0)]], texture2d img [[texture(1), raster_order_group(0)]], texture2d img3 [[texture(2), raster_order_group(0)]], texture2d img2 [[texture(3), raster_order_group(0)]], device atomic_uint* img2_atomic [[buffer(1), raster_order_group(0)]]) +{ + _9.baz = 0; + img4.write(float4(1.0, 0.0, 0.0, 1.0), uint2(int2(1))); + img.write(img3.read(uint2(int2(0))), uint2(int2(0))); + uint _39 = atomic_fetch_add_explicit((device atomic_uint*)&img2_atomic[spvImage2DAtomicCoord(int2(0), img2)], 1u, memory_order_relaxed); + _42.foo += 42; + uint _55 = atomic_fetch_and_explicit((volatile device atomic_uint*)&_42.bar, _52.quux, memory_order_relaxed); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/pls.frag b/third_party/spirv-cross/reference/shaders-msl/frag/pls.frag new file mode 100644 index 0000000..ee774a0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/pls.frag @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 PLSOut0 [[color(0)]]; + float4 PLSOut1 [[color(1)]]; + float4 PLSOut2 [[color(2)]]; + float4 PLSOut3 [[color(3)]]; +}; + +struct main0_in +{ + float4 PLSIn0 [[user(locn0)]]; + float4 PLSIn1 [[user(locn1)]]; + float4 PLSIn2 [[user(locn2)]]; + float4 PLSIn3 [[user(locn3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.PLSOut0 = in.PLSIn0 * 2.0; + out.PLSOut1 = in.PLSIn1 * 6.0; + out.PLSOut2 = in.PLSIn2 * 7.0; + out.PLSOut3 = in.PLSIn3 * 4.0; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/post-depth-coverage.ios.msl2.frag b/third_party/spirv-cross/reference/shaders-msl/frag/post-depth-coverage.ios.msl2.frag new file mode 100644 index 0000000..3b2885e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/post-depth-coverage.ios.msl2.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +[[ early_fragment_tests ]] fragment main0_out main0(uint gl_SampleMaskIn [[sample_mask, post_depth_coverage]]) +{ + main0_out out = {}; + out.FragColor = float4(float(gl_SampleMaskIn)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/post-depth-coverage.msl23.frag b/third_party/spirv-cross/reference/shaders-msl/frag/post-depth-coverage.msl23.frag new file mode 100644 index 0000000..3b2885e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/post-depth-coverage.msl23.frag @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +[[ early_fragment_tests ]] fragment main0_out main0(uint gl_SampleMaskIn [[sample_mask, post_depth_coverage]]) +{ + main0_out out = {}; + out.FragColor = float4(float(gl_SampleMaskIn)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/private-variable-prototype-declaration.frag b/third_party/spirv-cross/reference/shaders-msl/frag/private-variable-prototype-declaration.frag new file mode 100644 index 0000000..7c11a93 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/private-variable-prototype-declaration.frag @@ -0,0 +1,41 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct AStruct +{ + float4 foobar; +}; + +struct main0_out +{ + float3 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +void someFunction(thread AStruct& s) +{ + s.foobar = float4(1.0); +} + +static inline __attribute__((always_inline)) +void otherFunction(thread float3& global_variable) +{ + global_variable = float3(1.0); +} + +fragment main0_out main0() +{ + main0_out out = {}; + AStruct param; + someFunction(param); + AStruct inputs = param; + float3 global_variable; + otherFunction(global_variable); + out.FragColor = global_variable; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/readonly-ssbo.frag b/third_party/spirv-cross/reference/shaders-msl/frag/readonly-ssbo.frag new file mode 100644 index 0000000..7d73da7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/readonly-ssbo.frag @@ -0,0 +1,30 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + float4 v; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 read_from_function(const device SSBO& v_13) +{ + return v_13.v; +} + +fragment main0_out main0(const device SSBO& v_13 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = v_13.v + read_from_function(v_13); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag new file mode 100644 index 0000000..a093d3f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag @@ -0,0 +1,43 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float sample_normal2(thread const depth2d tex, thread sampler uSampler, thread float3& vUV) +{ + return float4(tex.sample(uSampler, vUV.xy)).x; +} + +static inline __attribute__((always_inline)) +float sample_normal(thread const depth2d tex, thread sampler uSampler, thread float3& vUV) +{ + return sample_normal2(tex, uSampler, vUV); +} + +static inline __attribute__((always_inline)) +float sample_comp(thread const depth2d tex, thread float3& vUV, thread sampler uSamplerShadow) +{ + return tex.sample_compare(uSamplerShadow, vUV.xy, vUV.z); +} + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uTexture [[texture(0)]], sampler uSampler [[sampler(0)]], sampler uSamplerShadow [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = sample_normal(uTexture, uSampler, in.vUV); + out.FragColor += sample_comp(uTexture, in.vUV, uSamplerShadow); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sample-depth-separate-image-sampler.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sample-depth-separate-image-sampler.frag new file mode 100644 index 0000000..d285941 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sample-depth-separate-image-sampler.frag @@ -0,0 +1,31 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float sample_depth_from_function(thread const depth2d uT, thread const sampler uS) +{ + return uT.sample_compare(uS, float3(0.5).xy, float3(0.5).z); +} + +static inline __attribute__((always_inline)) +float sample_color_from_function(thread const texture2d uT, thread const sampler uS) +{ + return uT.sample(uS, float2(0.5)).x; +} + +fragment main0_out main0(depth2d uDepth [[texture(0)]], texture2d uColor [[texture(1)]], sampler uSamplerShadow [[sampler(0)]], sampler uSampler [[sampler(1)]]) +{ + main0_out out = {}; + out.FragColor = sample_depth_from_function(uDepth, uSamplerShadow) + sample_color_from_function(uColor, uSampler); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag new file mode 100644 index 0000000..21ca717 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +fragment main0_out main0(uint gl_SampleMaskIn [[sample_mask]]) +{ + main0_out out = {}; + out.FragColor = float4(1.0); + out.gl_SampleMask = gl_SampleMaskIn; + out.gl_SampleMask &= 34; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag new file mode 100644 index 0000000..040c641 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(1.0); + out.gl_SampleMask = 34; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask.fixed-sample-mask.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask.fixed-sample-mask.frag new file mode 100644 index 0000000..2044477 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask.fixed-sample-mask.frag @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(1.0); + out.gl_SampleMask = 0; + out.gl_SampleMask &= 34; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask.frag new file mode 100644 index 0000000..6a28239 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sample-mask.frag @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float4(1.0); + out.gl_SampleMask = 0; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sample-position-func.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sample-position-func.frag new file mode 100644 index 0000000..025f874 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sample-position-func.frag @@ -0,0 +1,32 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int index [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float4 getColor(thread const int& i, thread float2& gl_SamplePosition) +{ + return float4(gl_SamplePosition, float(i), 1.0); +} + +fragment main0_out main0(main0_in in [[stage_in]], uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + float2 gl_SamplePosition = get_sample_position(gl_SampleID); + int param = in.index; + out.FragColor = getColor(param, gl_SamplePosition); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sample-position.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sample-position.frag new file mode 100644 index 0000000..8d26acb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sample-position.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(uint gl_SampleID [[sample_id]]) +{ + main0_out out = {}; + float2 gl_SamplePosition = get_sample_position(gl_SampleID); + out.FragColor = float4(gl_SamplePosition, float(gl_SampleID), 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag new file mode 100644 index 0000000..70278b1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float vTex [[user(locn0), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor += ((uSampler.sample(uSamplerSmplr, float2(in.vTex, 0.5), bias(2.0)) + uSampler.sample(uSamplerSmplr, float2(in.vTex, 0.5), level(3.0))) + uSampler.sample(uSamplerSmplr, float2(in.vTex, 0.5), gradient2d(5.0, 8.0))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler-1d-lod.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-1d-lod.frag new file mode 100644 index 0000000..96914f8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-1d-lod.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float vTex [[user(locn0), flat]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture1d uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor += ((uSampler.sample(uSamplerSmplr, in.vTex) + uSampler.sample(uSamplerSmplr, in.vTex)) + uSampler.sample(uSamplerSmplr, in.vTex)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag new file mode 100644 index 0000000..6aaffe5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, float2(in.vUV.x, 0.5), uint(round(in.vUV.y)), in.vUV.z, bias(1.0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.frag new file mode 100644 index 0000000..630511b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, level(0)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag new file mode 100644 index 0000000..a29ebf0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, gradient2d(float2(0.0), float2(0.0))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag new file mode 100644 index 0000000..0784569 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d_array uTex [[texture(0)]], sampler uShadow [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, level(0)) + uTex.sample_compare(uShadow, in.vUV.xy, uint(round(in.vUV.z)), in.vUV.w, gradient2d(float2(1.0), float2(1.0))); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler-image-arrays.msl2.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-image-arrays.msl2.frag new file mode 100644 index 0000000..dec6d05 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-image-arrays.msl2.frag @@ -0,0 +1,48 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vTex [[user(locn0), flat]]; + int vIndex [[user(locn1)]]; +}; + +static inline __attribute__((always_inline)) +float4 sample_from_global(thread int& vIndex, thread float2& vTex, thread const array, 4> uSampler, thread const array uSamplerSmplr) +{ + return uSampler[vIndex].sample(uSamplerSmplr[vIndex], (vTex + float2(0.100000001490116119384765625))); +} + +static inline __attribute__((always_inline)) +float4 sample_from_argument(thread const array, 4> samplers, thread const array samplersSmplr, thread int& vIndex, thread float2& vTex) +{ + return samplers[vIndex].sample(samplersSmplr[vIndex], (vTex + float2(0.20000000298023223876953125))); +} + +static inline __attribute__((always_inline)) +float4 sample_single_from_argument(thread const texture2d samp, thread const sampler sampSmplr, thread float2& vTex) +{ + return samp.sample(sampSmplr, (vTex + float2(0.300000011920928955078125))); +} + +fragment main0_out main0(main0_in in [[stage_in]], array, 4> uSampler [[texture(0)]], array, 4> uTextures [[texture(4)]], array uSamplerSmplr [[sampler(0)]], array uSamplers [[sampler(4)]]) +{ + main0_out out = {}; + out.FragColor = float4(0.0); + out.FragColor += uTextures[2].sample(uSamplers[1], in.vTex); + out.FragColor += uSampler[in.vIndex].sample(uSamplerSmplr[in.vIndex], in.vTex); + out.FragColor += sample_from_global(in.vIndex, in.vTex, uSampler, uSamplerSmplr); + out.FragColor += sample_from_argument(uSampler, uSamplerSmplr, in.vIndex, in.vTex); + out.FragColor += sample_single_from_argument(uSampler[3], uSamplerSmplr[3], in.vTex); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler-ms.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-ms.frag new file mode 100644 index 0000000..1ceb3f9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler-ms.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d_ms uSampler [[texture(0)]], sampler uSamplerSmplr [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + int2 coord = int2(gl_FragCoord.xy); + out.FragColor = ((uSampler.read(uint2(coord), 0) + uSampler.read(uint2(coord), 1)) + uSampler.read(uint2(coord), 2)) + uSampler.read(uint2(coord), 3); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/sampler.frag b/third_party/spirv-cross/reference/shaders-msl/frag/sampler.frag new file mode 100644 index 0000000..6484161 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/sampler.frag @@ -0,0 +1,32 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex [[user(locn1)]]; +}; + +static inline __attribute__((always_inline)) +float4 sample_texture(thread const texture2d tex, thread const sampler texSmplr, thread const float2& uv) +{ + return tex.sample(texSmplr, uv); +} + +fragment main0_out main0(main0_in in [[stage_in]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]]) +{ + main0_out out = {}; + float2 param = in.vTex; + out.FragColor = in.vColor * sample_texture(uTex, uTexSmplr, param); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/reference/shaders-msl/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..592d445 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/scalar-refract-reflect.frag @@ -0,0 +1,49 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vRefract [[user(locn0)]]; +}; + +template +inline T spvReflect(T i, T n) +{ + return i - T(2) * i * n * n; +} + +template +inline T spvRefract(T i, T n, T eta) +{ + T NoI = n * i; + T NoI2 = NoI * NoI; + T k = T(1) - eta * eta * (T(1) - NoI2); + if (k < T(0)) + { + return T(0); + } + else + { + return eta * i - (eta * NoI + sqrt(k)) * n; + } +} + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = spvRefract(in.vRefract.x, in.vRefract.y, in.vRefract.z); + out.FragColor += spvReflect(in.vRefract.x, in.vRefract.y); + out.FragColor += refract(in.vRefract.xy, in.vRefract.yz, in.vRefract.z).y; + out.FragColor += reflect(in.vRefract.xy, in.vRefract.zy).y; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/separate-image-sampler-argument.frag b/third_party/spirv-cross/reference/shaders-msl/frag/separate-image-sampler-argument.frag new file mode 100644 index 0000000..d196243 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/separate-image-sampler-argument.frag @@ -0,0 +1,25 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +static inline __attribute__((always_inline)) +float4 samp(thread const texture2d t, thread const sampler s) +{ + return t.sample(s, float2(0.5)); +} + +fragment main0_out main0(texture2d uDepth [[texture(0)]], sampler uSampler [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = samp(uDepth, uSampler); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/shader-arithmetic-8bit.frag b/third_party/spirv-cross/reference/shaders-msl/frag/shader-arithmetic-8bit.frag new file mode 100644 index 0000000..e9694aa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/shader-arithmetic-8bit.frag @@ -0,0 +1,102 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct SSBO +{ + char i8[16]; + uchar u8[16]; +}; + +struct Push +{ + char i8; + uchar u8; +}; + +struct UBO +{ + char i8; + uchar u8; +}; + +struct main0_out +{ + int4 FragColorInt [[color(0)]]; + uint4 FragColorUint [[color(1)]]; +}; + +struct main0_in +{ + int4 vColor [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +void packing_int8(device SSBO& ssbo) +{ + short i16 = short(10); + int i32 = 20; + char2 i8_2 = as_type(i16); + char4 i8_4 = as_type(i32); + i16 = as_type(i8_2); + i32 = as_type(i8_4); + ssbo.i8[0] = i8_4.x; + ssbo.i8[1] = i8_4.y; + ssbo.i8[2] = i8_4.z; + ssbo.i8[3] = i8_4.w; +} + +static inline __attribute__((always_inline)) +void packing_uint8(device SSBO& ssbo) +{ + ushort u16 = ushort(10); + uint u32 = 20u; + uchar2 u8_2 = as_type(u16); + uchar4 u8_4 = as_type(u32); + u16 = as_type(u8_2); + u32 = as_type(u8_4); + ssbo.u8[0] = u8_4.x; + ssbo.u8[1] = u8_4.y; + ssbo.u8[2] = u8_4.z; + ssbo.u8[3] = u8_4.w; +} + +static inline __attribute__((always_inline)) +void compute_int8(device SSBO& ssbo, thread int4& vColor, constant Push& registers, constant UBO& ubo, thread int4& FragColorInt) +{ + char4 tmp = char4(vColor); + tmp += char4(registers.i8); + tmp += char4(char(-40)); + tmp += char4(-50); + tmp += char4(char(10), char(20), char(30), char(40)); + tmp += char4(ssbo.i8[4]); + tmp += char4(ubo.i8); + FragColorInt = int4(tmp); +} + +static inline __attribute__((always_inline)) +void compute_uint8(device SSBO& ssbo, thread int4& vColor, constant Push& registers, constant UBO& ubo, thread uint4& FragColorUint) +{ + uchar4 tmp = uchar4(char4(vColor)); + tmp += uchar4(registers.u8); + tmp += uchar4(uchar(216)); + tmp += uchar4(206); + tmp += uchar4(uchar(10), uchar(20), uchar(30), uchar(40)); + tmp += uchar4(ssbo.u8[4]); + tmp += uchar4(ubo.u8); + FragColorUint = uint4(tmp); +} + +fragment main0_out main0(main0_in in [[stage_in]], device SSBO& ssbo [[buffer(0)]], constant Push& registers [[buffer(1)]], constant UBO& ubo [[buffer(2)]]) +{ + main0_out out = {}; + packing_int8(ssbo); + packing_uint8(ssbo); + compute_int8(ssbo, in.vColor, registers, ubo, out.FragColorInt); + compute_uint8(ssbo, in.vColor, registers, ubo, out.FragColorUint); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/spec-constant-block-size.frag b/third_party/spirv-cross/reference/shaders-msl/frag/spec-constant-block-size.frag new file mode 100644 index 0000000..36456b8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/spec-constant-block-size.frag @@ -0,0 +1,32 @@ +#include +#include + +using namespace metal; + +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 2 +#endif +constant int Value = SPIRV_CROSS_CONSTANT_ID_10; + +struct SpecConstArray +{ + float4 samples[Value]; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int Index [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant SpecConstArray& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = _15.samples[in.Index]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/spec-constant-ternary.frag b/third_party/spirv-cross/reference/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000..0590065 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +constant uint s_tmp [[function_constant(0)]]; +constant uint s = is_function_constant_defined(s_tmp) ? s_tmp : 10u; +constant bool _13 = (s > 20u); +constant uint f = _13 ? 30u : 50u; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = float(f); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/stencil-export.msl21.frag b/third_party/spirv-cross/reference/shaders-msl/frag/stencil-export.msl21.frag new file mode 100644 index 0000000..f3629e1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/stencil-export.msl21.frag @@ -0,0 +1,30 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 MRT0 [[color(0)]]; + float4 MRT1 [[color(1)]]; + uint gl_FragStencilRefARB [[stencil]]; +}; + +static inline __attribute__((always_inline)) +void update_stencil(thread uint& gl_FragStencilRefARB) +{ + gl_FragStencilRefARB = uint(int(gl_FragStencilRefARB) + 10); +} + +fragment main0_out main0() +{ + main0_out out = {}; + out.MRT0 = float4(1.0); + out.MRT1 = float4(1.0, 0.0, 1.0, 1.0); + out.gl_FragStencilRefARB = uint(100); + update_stencil(out.gl_FragStencilRefARB); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/subgroup-builtins.msl22.frag b/third_party/spirv-cross/reference/shaders-msl/frag/subgroup-builtins.msl22.frag new file mode 100644 index 0000000..03a536f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/subgroup-builtins.msl22.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + uint2 FragColor [[color(0)]]; +}; + +fragment main0_out main0(uint gl_SubgroupSize [[threads_per_simdgroup]], uint gl_SubgroupInvocationID [[thread_index_in_simdgroup]]) +{ + main0_out out = {}; + out.FragColor.x = gl_SubgroupSize; + out.FragColor.y = gl_SubgroupInvocationID; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/switch-unsigned-case.frag b/third_party/spirv-cross/reference/shaders-msl/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..4cd2b68 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/switch-unsigned-case.frag @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct Buff +{ + uint TestVal; +}; + +struct main0_out +{ + float4 fsout_Color [[color(0)]]; +}; + +fragment main0_out main0(constant Buff& _15 [[buffer(0)]]) +{ + main0_out out = {}; + out.fsout_Color = float4(1.0); + switch (_15.TestVal) + { + case 0u: + { + out.fsout_Color = float4(0.100000001490116119384765625); + break; + } + case 1u: + { + out.fsout_Color = float4(0.20000000298023223876953125); + break; + } + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/swizzle.frag b/third_party/spirv-cross/reference/shaders-msl/frag/swizzle.frag new file mode 100644 index 0000000..7a0494e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/swizzle.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vNormal [[user(locn1)]]; + float2 vUV [[user(locn2)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d samp [[texture(0)]], sampler sampSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = float4(samp.sample(sampSmplr, in.vUV).xyz, 1.0); + out.FragColor = float4(samp.sample(sampSmplr, in.vUV).xz, 1.0, 4.0); + out.FragColor = float4(samp.sample(sampSmplr, in.vUV).xx, samp.sample(sampSmplr, (in.vUV + float2(0.100000001490116119384765625))).yy); + out.FragColor = float4(in.vNormal, 1.0); + out.FragColor = float4(in.vNormal + float3(1.7999999523162841796875), 1.0); + out.FragColor = float4(in.vUV, in.vUV + float2(1.7999999523162841796875)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag b/third_party/spirv-cross/reference/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag new file mode 100644 index 0000000..98b9bb7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uTexture [[texture(0)]], texture2d uTexture2 [[texture(1)]], sampler uTextureSmplr [[sampler(0)]], sampler uTexture2Smplr [[sampler(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = uTexture.read(uint2(int2(gl_FragCoord.xy)) + uint2(int2(1)), 0); + out.FragColor += uTexture2.read(uint2(uint(int(gl_FragCoord.x)), 0) + uint2(uint(-1), 0), 0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/texel-fetch-offset.frag b/third_party/spirv-cross/reference/shaders-msl/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..4d4301e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/texel-fetch-offset.frag @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(texture2d uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + out.FragColor = uTexture.read(uint2(int2(gl_FragCoord.xy)) + uint2(int2(1)), 0); + out.FragColor += uTexture.read(uint2(int2(gl_FragCoord.xy)) + uint2(int2(-1, 1)), 0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/texture-cube-array.frag b/third_party/spirv-cross/reference/shaders-msl/frag/texture-cube-array.frag new file mode 100644 index 0000000..9f1832e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/texture-cube-array.frag @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texturecube cubeSampler [[texture(0)]], texturecube_array cubeArraySampler [[texture(1)]], texture2d_array texArraySampler [[texture(2)]], sampler cubeSamplerSmplr [[sampler(0)]], sampler cubeArraySamplerSmplr [[sampler(1)]], sampler texArraySamplerSmplr [[sampler(2)]]) +{ + main0_out out = {}; + float4 a = cubeSampler.sample(cubeSamplerSmplr, in.vUV.xyz); + float4 b = cubeArraySampler.sample(cubeArraySamplerSmplr, in.vUV.xyz, uint(round(in.vUV.w))); + float4 c = texArraySampler.sample(texArraySamplerSmplr, in.vUV.xyz.xy, uint(round(in.vUV.xyz.z))); + out.FragColor = (a + b) + c; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag b/third_party/spirv-cross/reference/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag new file mode 100644 index 0000000..3c4d0ad --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag @@ -0,0 +1,61 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vUV [[user(locn0)]]; +}; + +static inline __attribute__((always_inline)) +float3 spvCubemapTo2DArrayFace(float3 P) +{ + float3 Coords = abs(P.xyz); + float CubeFace = 0; + float ProjectionAxis = 0; + float u = 0; + float v = 0; + if (Coords.x >= Coords.y && Coords.x >= Coords.z) + { + CubeFace = P.x >= 0 ? 0 : 1; + ProjectionAxis = Coords.x; + u = P.x >= 0 ? -P.z : P.z; + v = -P.y; + } + else if (Coords.y >= Coords.x && Coords.y >= Coords.z) + { + CubeFace = P.y >= 0 ? 2 : 3; + ProjectionAxis = Coords.y; + u = P.x; + v = P.y >= 0 ? P.z : -P.z; + } + else + { + CubeFace = P.z >= 0 ? 4 : 5; + ProjectionAxis = Coords.z; + u = P.z >= 0 ? P.x : -P.x; + v = -P.y; + } + u = 0.5 * (u/ProjectionAxis + 1); + v = 0.5 * (v/ProjectionAxis + 1); + return float3(u, v, CubeFace); +} + +fragment main0_out main0(main0_in in [[stage_in]], texturecube cubeSampler [[texture(0)]], texture2d_array cubeArraySampler [[texture(1)]], texture2d_array texArraySampler [[texture(2)]], sampler cubeSamplerSmplr [[sampler(0)]], sampler cubeArraySamplerSmplr [[sampler(1)]], sampler texArraySamplerSmplr [[sampler(2)]]) +{ + main0_out out = {}; + float4 a = cubeSampler.sample(cubeSamplerSmplr, in.vUV.xyz); + float4 b = cubeArraySampler.sample(cubeArraySamplerSmplr, spvCubemapTo2DArrayFace(in.vUV.xyz).xy, uint(spvCubemapTo2DArrayFace(in.vUV.xyz).z) + (uint(round(in.vUV.w)) * 6u)); + float4 c = texArraySampler.sample(texArraySamplerSmplr, in.vUV.xyz.xy, uint(round(in.vUV.xyz.z))); + out.FragColor = (a + b) + c; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/texture-multisample-array.msl21.frag b/third_party/spirv-cross/reference/shaders-msl/frag/texture-multisample-array.msl21.frag new file mode 100644 index 0000000..ed1e81f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/texture-multisample-array.msl21.frag @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + int3 vCoord [[user(locn0)]]; + int vSample [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], texture2d_ms_array uTexture [[texture(0)]], sampler uTextureSmplr [[sampler(0)]]) +{ + main0_out out = {}; + out.FragColor = uTexture.read(uint2(in.vCoord.xy), uint(in.vCoord.z), in.vSample); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/texture-proj-shadow.frag b/third_party/spirv-cross/reference/shaders-msl/frag/texture-proj-shadow.frag new file mode 100644 index 0000000..52d4a02 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/texture-proj-shadow.frag @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vClip3 [[user(locn0)]]; + float4 vClip4 [[user(locn1)]]; + float2 vClip2 [[user(locn2)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], depth2d uShadow2D [[texture(0)]], texture1d uSampler1D [[texture(1)]], texture2d uSampler2D [[texture(2)]], texture3d uSampler3D [[texture(3)]], sampler uShadow2DSmplr [[sampler(0)]], sampler uSampler1DSmplr [[sampler(1)]], sampler uSampler2DSmplr [[sampler(2)]], sampler uSampler3DSmplr [[sampler(3)]]) +{ + main0_out out = {}; + float4 _20 = in.vClip4; + _20.z = in.vClip4.w; + out.FragColor = uShadow2D.sample_compare(uShadow2DSmplr, _20.xy / _20.z, in.vClip4.z / _20.z); + out.FragColor = uSampler1D.sample(uSampler1DSmplr, in.vClip2.x / in.vClip2.y).x; + out.FragColor = uSampler2D.sample(uSampler2DSmplr, in.vClip3.xy / in.vClip3.z).x; + out.FragColor = uSampler3D.sample(uSampler3DSmplr, in.vClip4.xyz / in.vClip4.w).x; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/ubo_layout.frag b/third_party/spirv-cross/reference/shaders-msl/frag/ubo_layout.frag new file mode 100644 index 0000000..4ca603d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/ubo_layout.frag @@ -0,0 +1,37 @@ +#include +#include + +using namespace metal; + +struct Str +{ + float4x4 foo; +}; + +struct UBO1 +{ + Str foo; +}; + +struct Str_1 +{ + float4x4 foo; +}; + +struct UBO2 +{ + Str_1 foo; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0(constant UBO1& ubo1 [[buffer(0)]], constant UBO2& ubo0 [[buffer(1)]]) +{ + main0_out out = {}; + out.FragColor = float4(ubo1.foo.foo[0][0], ubo1.foo.foo[1][0], ubo1.foo.foo[2][0], ubo1.foo.foo[3][0]) + ubo0.foo.foo[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/unary-enclose.frag b/third_party/spirv-cross/reference/shaders-msl/frag/unary-enclose.frag new file mode 100644 index 0000000..c33269f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/unary-enclose.frag @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vIn [[user(locn0)]]; + int4 vIn1 [[user(locn1)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.FragColor = -(-in.vIn); + int4 a = ~(~in.vIn1); + bool b = false; + b = !(!b); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag b/third_party/spirv-cross/reference/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag new file mode 100644 index 0000000..6116dea --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag @@ -0,0 +1,75 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + ushort2 a [[user(locn0)]]; + uint3 b [[user(locn1)]]; + ushort c_0 [[user(locn2)]]; + ushort c_1 [[user(locn3)]]; + uint4 e_0 [[user(locn4)]]; + uint4 e_1 [[user(locn5)]]; + float4 d [[user(locn6)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray c = {}; + spvUnsafeArray e = {}; + c[0] = in.c_0; + c[1] = in.c_1; + e[0] = in.e_0; + e[1] = in.e_1; + out.FragColor = float4(float(int(in.a.x)), float(in.b.x), float2(float(uint(c[1])), float(e[0].w)) + in.d.xy); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/frag/write-depth-in-function.frag b/third_party/spirv-cross/reference/shaders-msl/frag/write-depth-in-function.frag new file mode 100644 index 0000000..c3ab221 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/frag/write-depth-in-function.frag @@ -0,0 +1,27 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float FragColor [[color(0)]]; + float gl_FragDepth [[depth(any)]]; +}; + +static inline __attribute__((always_inline)) +void set_output_depth(thread float& gl_FragDepth) +{ + gl_FragDepth = 0.20000000298023223876953125; +} + +fragment main0_out main0() +{ + main0_out out = {}; + out.FragColor = 1.0; + set_output_depth(out.gl_FragDepth); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/intel/shader-integer-functions2.asm.comp b/third_party/spirv-cross/reference/shaders-msl/intel/shader-integer-functions2.asm.comp new file mode 100644 index 0000000..1e5d889 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/intel/shader-integer-functions2.asm.comp @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct foo +{ + uint a; + uint b; + int c; + int d; +}; + +kernel void main0(device foo& _4 [[buffer(0)]]) +{ + _4.a = clz(_4.a); + _4.a = ctz(_4.a); + _4.a = absdiff(_4.c, _4.d); + _4.a = absdiff(_4.a, _4.b); + _4.c = addsat(_4.c, _4.d); + _4.a = addsat(_4.a, _4.b); + _4.c = hadd(_4.c, _4.d); + _4.a = hadd(_4.a, _4.b); + _4.c = rhadd(_4.c, _4.d); + _4.a = rhadd(_4.a, _4.b); + _4.c = subsat(_4.c, _4.d); + _4.a = subsat(_4.a, _4.b); + _4.c = int(short(_4.c)) * int(short(_4.d)); + _4.a = uint(ushort(_4.a)) * uint(ushort(_4.b)); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/legacy/vert/transpose.legacy.vert b/third_party/spirv-cross/reference/shaders-msl/legacy/vert/transpose.legacy.vert new file mode 100644 index 0000000..3837c8b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/legacy/vert/transpose.legacy.vert @@ -0,0 +1,33 @@ +#include +#include + +using namespace metal; + +struct Buffer +{ + float4x4 MVPRowMajor; + float4x4 MVPColMajor; + float4x4 M; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant Buffer& _13 [[buffer(0)]]) +{ + main0_out out = {}; + float4 c0 = _13.M * (in.Position * _13.MVPRowMajor); + float4 c1 = _13.M * (_13.MVPColMajor * in.Position); + float4 c2 = _13.M * (_13.MVPRowMajor * in.Position); + float4 c3 = _13.M * (in.Position * _13.MVPColMajor); + out.gl_Position = ((c0 + c1) + c2) + c3; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/basic.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/basic.multi-patch.tesc new file mode 100644 index 0000000..b1403a3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/basic.multi-patch.tesc @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct main0_patchOut +{ + float3 vFoo; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]]) +{ + device main0_patchOut& patchOut = spvPatchOut[gl_GlobalInvocationID.x / 1]; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1]); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.900000095367431640625); + patchOut.vFoo = float3(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/basic.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/basic.tesc new file mode 100644 index 0000000..a9ff5b3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/basic.tesc @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_patchOut +{ + float3 vFoo; +}; + +kernel void main0(uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]]) +{ + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(8.8999996185302734375); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(6.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(3.900000095367431640625); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(4.900000095367431640625); + patchOut.vFoo = float3(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc new file mode 100644 index 0000000..4f4cf0b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc @@ -0,0 +1,68 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4x4 vOutputs; +}; + +struct main0_in +{ + float4x4 vInputs; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + spvUnsafeArray _16 = spvUnsafeArray({ gl_in[0].vInputs, gl_in[1].vInputs, gl_in[2].vInputs, gl_in[3].vInputs, gl_in[4].vInputs, gl_in[5].vInputs, gl_in[6].vInputs, gl_in[7].vInputs, gl_in[8].vInputs, gl_in[9].vInputs, gl_in[10].vInputs, gl_in[11].vInputs, gl_in[12].vInputs, gl_in[13].vInputs, gl_in[14].vInputs, gl_in[15].vInputs, gl_in[16].vInputs, gl_in[17].vInputs, gl_in[18].vInputs, gl_in[19].vInputs, gl_in[20].vInputs, gl_in[21].vInputs, gl_in[22].vInputs, gl_in[23].vInputs, gl_in[24].vInputs, gl_in[25].vInputs, gl_in[26].vInputs, gl_in[27].vInputs, gl_in[28].vInputs, gl_in[29].vInputs, gl_in[30].vInputs, gl_in[31].vInputs }); + spvUnsafeArray tmp; + tmp = _16; + gl_out[gl_InvocationID].vOutputs = tmp[gl_InvocationID]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-matrix.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-matrix.tesc new file mode 100644 index 0000000..46d4b4a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-matrix.tesc @@ -0,0 +1,73 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4x4 vOutputs; +}; + +struct main0_in +{ + float4 vInputs_0 [[attribute(0)]]; + float4 vInputs_1 [[attribute(1)]]; + float4 vInputs_2 [[attribute(2)]]; + float4 vInputs_3 [[attribute(3)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + spvUnsafeArray _16 = spvUnsafeArray({ float4x4(gl_in[0].vInputs_0, gl_in[0].vInputs_1, gl_in[0].vInputs_2, gl_in[0].vInputs_3), float4x4(gl_in[1].vInputs_0, gl_in[1].vInputs_1, gl_in[1].vInputs_2, gl_in[1].vInputs_3), float4x4(gl_in[2].vInputs_0, gl_in[2].vInputs_1, gl_in[2].vInputs_2, gl_in[2].vInputs_3), float4x4(gl_in[3].vInputs_0, gl_in[3].vInputs_1, gl_in[3].vInputs_2, gl_in[3].vInputs_3), float4x4(gl_in[4].vInputs_0, gl_in[4].vInputs_1, gl_in[4].vInputs_2, gl_in[4].vInputs_3), float4x4(gl_in[5].vInputs_0, gl_in[5].vInputs_1, gl_in[5].vInputs_2, gl_in[5].vInputs_3), float4x4(gl_in[6].vInputs_0, gl_in[6].vInputs_1, gl_in[6].vInputs_2, gl_in[6].vInputs_3), float4x4(gl_in[7].vInputs_0, gl_in[7].vInputs_1, gl_in[7].vInputs_2, gl_in[7].vInputs_3), float4x4(gl_in[8].vInputs_0, gl_in[8].vInputs_1, gl_in[8].vInputs_2, gl_in[8].vInputs_3), float4x4(gl_in[9].vInputs_0, gl_in[9].vInputs_1, gl_in[9].vInputs_2, gl_in[9].vInputs_3), float4x4(gl_in[10].vInputs_0, gl_in[10].vInputs_1, gl_in[10].vInputs_2, gl_in[10].vInputs_3), float4x4(gl_in[11].vInputs_0, gl_in[11].vInputs_1, gl_in[11].vInputs_2, gl_in[11].vInputs_3), float4x4(gl_in[12].vInputs_0, gl_in[12].vInputs_1, gl_in[12].vInputs_2, gl_in[12].vInputs_3), float4x4(gl_in[13].vInputs_0, gl_in[13].vInputs_1, gl_in[13].vInputs_2, gl_in[13].vInputs_3), float4x4(gl_in[14].vInputs_0, gl_in[14].vInputs_1, gl_in[14].vInputs_2, gl_in[14].vInputs_3), float4x4(gl_in[15].vInputs_0, gl_in[15].vInputs_1, gl_in[15].vInputs_2, gl_in[15].vInputs_3), float4x4(gl_in[16].vInputs_0, gl_in[16].vInputs_1, gl_in[16].vInputs_2, gl_in[16].vInputs_3), float4x4(gl_in[17].vInputs_0, gl_in[17].vInputs_1, gl_in[17].vInputs_2, gl_in[17].vInputs_3), float4x4(gl_in[18].vInputs_0, gl_in[18].vInputs_1, gl_in[18].vInputs_2, gl_in[18].vInputs_3), float4x4(gl_in[19].vInputs_0, gl_in[19].vInputs_1, gl_in[19].vInputs_2, gl_in[19].vInputs_3), float4x4(gl_in[20].vInputs_0, gl_in[20].vInputs_1, gl_in[20].vInputs_2, gl_in[20].vInputs_3), float4x4(gl_in[21].vInputs_0, gl_in[21].vInputs_1, gl_in[21].vInputs_2, gl_in[21].vInputs_3), float4x4(gl_in[22].vInputs_0, gl_in[22].vInputs_1, gl_in[22].vInputs_2, gl_in[22].vInputs_3), float4x4(gl_in[23].vInputs_0, gl_in[23].vInputs_1, gl_in[23].vInputs_2, gl_in[23].vInputs_3), float4x4(gl_in[24].vInputs_0, gl_in[24].vInputs_1, gl_in[24].vInputs_2, gl_in[24].vInputs_3), float4x4(gl_in[25].vInputs_0, gl_in[25].vInputs_1, gl_in[25].vInputs_2, gl_in[25].vInputs_3), float4x4(gl_in[26].vInputs_0, gl_in[26].vInputs_1, gl_in[26].vInputs_2, gl_in[26].vInputs_3), float4x4(gl_in[27].vInputs_0, gl_in[27].vInputs_1, gl_in[27].vInputs_2, gl_in[27].vInputs_3), float4x4(gl_in[28].vInputs_0, gl_in[28].vInputs_1, gl_in[28].vInputs_2, gl_in[28].vInputs_3), float4x4(gl_in[29].vInputs_0, gl_in[29].vInputs_1, gl_in[29].vInputs_2, gl_in[29].vInputs_3), float4x4(gl_in[30].vInputs_0, gl_in[30].vInputs_1, gl_in[30].vInputs_2, gl_in[30].vInputs_3), float4x4(gl_in[31].vInputs_0, gl_in[31].vInputs_1, gl_in[31].vInputs_2, gl_in[31].vInputs_3) }); + spvUnsafeArray tmp; + tmp = _16; + gl_out[gl_InvocationID].vOutputs = tmp[gl_InvocationID]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc new file mode 100644 index 0000000..ad23ea7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc @@ -0,0 +1,80 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct VertexData +{ + float4x4 a; + spvUnsafeArray b; + float4 c; +}; + +struct main0_out +{ + float4 vOutputs; +}; + +struct main0_in +{ + float4x4 VertexData_a; + spvUnsafeArray VertexData_b; + float4 VertexData_c; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + spvUnsafeArray _19 = spvUnsafeArray({ VertexData{ gl_in[0].VertexData_a, spvUnsafeArray({ gl_in[0].VertexData_b[0], gl_in[0].VertexData_b[1] }), gl_in[0].VertexData_c }, VertexData{ gl_in[1].VertexData_a, spvUnsafeArray({ gl_in[1].VertexData_b[0], gl_in[1].VertexData_b[1] }), gl_in[1].VertexData_c }, VertexData{ gl_in[2].VertexData_a, spvUnsafeArray({ gl_in[2].VertexData_b[0], gl_in[2].VertexData_b[1] }), gl_in[2].VertexData_c }, VertexData{ gl_in[3].VertexData_a, spvUnsafeArray({ gl_in[3].VertexData_b[0], gl_in[3].VertexData_b[1] }), gl_in[3].VertexData_c }, VertexData{ gl_in[4].VertexData_a, spvUnsafeArray({ gl_in[4].VertexData_b[0], gl_in[4].VertexData_b[1] }), gl_in[4].VertexData_c }, VertexData{ gl_in[5].VertexData_a, spvUnsafeArray({ gl_in[5].VertexData_b[0], gl_in[5].VertexData_b[1] }), gl_in[5].VertexData_c }, VertexData{ gl_in[6].VertexData_a, spvUnsafeArray({ gl_in[6].VertexData_b[0], gl_in[6].VertexData_b[1] }), gl_in[6].VertexData_c }, VertexData{ gl_in[7].VertexData_a, spvUnsafeArray({ gl_in[7].VertexData_b[0], gl_in[7].VertexData_b[1] }), gl_in[7].VertexData_c }, VertexData{ gl_in[8].VertexData_a, spvUnsafeArray({ gl_in[8].VertexData_b[0], gl_in[8].VertexData_b[1] }), gl_in[8].VertexData_c }, VertexData{ gl_in[9].VertexData_a, spvUnsafeArray({ gl_in[9].VertexData_b[0], gl_in[9].VertexData_b[1] }), gl_in[9].VertexData_c }, VertexData{ gl_in[10].VertexData_a, spvUnsafeArray({ gl_in[10].VertexData_b[0], gl_in[10].VertexData_b[1] }), gl_in[10].VertexData_c }, VertexData{ gl_in[11].VertexData_a, spvUnsafeArray({ gl_in[11].VertexData_b[0], gl_in[11].VertexData_b[1] }), gl_in[11].VertexData_c }, VertexData{ gl_in[12].VertexData_a, spvUnsafeArray({ gl_in[12].VertexData_b[0], gl_in[12].VertexData_b[1] }), gl_in[12].VertexData_c }, VertexData{ gl_in[13].VertexData_a, spvUnsafeArray({ gl_in[13].VertexData_b[0], gl_in[13].VertexData_b[1] }), gl_in[13].VertexData_c }, VertexData{ gl_in[14].VertexData_a, spvUnsafeArray({ gl_in[14].VertexData_b[0], gl_in[14].VertexData_b[1] }), gl_in[14].VertexData_c }, VertexData{ gl_in[15].VertexData_a, spvUnsafeArray({ gl_in[15].VertexData_b[0], gl_in[15].VertexData_b[1] }), gl_in[15].VertexData_c }, VertexData{ gl_in[16].VertexData_a, spvUnsafeArray({ gl_in[16].VertexData_b[0], gl_in[16].VertexData_b[1] }), gl_in[16].VertexData_c }, VertexData{ gl_in[17].VertexData_a, spvUnsafeArray({ gl_in[17].VertexData_b[0], gl_in[17].VertexData_b[1] }), gl_in[17].VertexData_c }, VertexData{ gl_in[18].VertexData_a, spvUnsafeArray({ gl_in[18].VertexData_b[0], gl_in[18].VertexData_b[1] }), gl_in[18].VertexData_c }, VertexData{ gl_in[19].VertexData_a, spvUnsafeArray({ gl_in[19].VertexData_b[0], gl_in[19].VertexData_b[1] }), gl_in[19].VertexData_c }, VertexData{ gl_in[20].VertexData_a, spvUnsafeArray({ gl_in[20].VertexData_b[0], gl_in[20].VertexData_b[1] }), gl_in[20].VertexData_c }, VertexData{ gl_in[21].VertexData_a, spvUnsafeArray({ gl_in[21].VertexData_b[0], gl_in[21].VertexData_b[1] }), gl_in[21].VertexData_c }, VertexData{ gl_in[22].VertexData_a, spvUnsafeArray({ gl_in[22].VertexData_b[0], gl_in[22].VertexData_b[1] }), gl_in[22].VertexData_c }, VertexData{ gl_in[23].VertexData_a, spvUnsafeArray({ gl_in[23].VertexData_b[0], gl_in[23].VertexData_b[1] }), gl_in[23].VertexData_c }, VertexData{ gl_in[24].VertexData_a, spvUnsafeArray({ gl_in[24].VertexData_b[0], gl_in[24].VertexData_b[1] }), gl_in[24].VertexData_c }, VertexData{ gl_in[25].VertexData_a, spvUnsafeArray({ gl_in[25].VertexData_b[0], gl_in[25].VertexData_b[1] }), gl_in[25].VertexData_c }, VertexData{ gl_in[26].VertexData_a, spvUnsafeArray({ gl_in[26].VertexData_b[0], gl_in[26].VertexData_b[1] }), gl_in[26].VertexData_c }, VertexData{ gl_in[27].VertexData_a, spvUnsafeArray({ gl_in[27].VertexData_b[0], gl_in[27].VertexData_b[1] }), gl_in[27].VertexData_c }, VertexData{ gl_in[28].VertexData_a, spvUnsafeArray({ gl_in[28].VertexData_b[0], gl_in[28].VertexData_b[1] }), gl_in[28].VertexData_c }, VertexData{ gl_in[29].VertexData_a, spvUnsafeArray({ gl_in[29].VertexData_b[0], gl_in[29].VertexData_b[1] }), gl_in[29].VertexData_c }, VertexData{ gl_in[30].VertexData_a, spvUnsafeArray({ gl_in[30].VertexData_b[0], gl_in[30].VertexData_b[1] }), gl_in[30].VertexData_c }, VertexData{ gl_in[31].VertexData_a, spvUnsafeArray({ gl_in[31].VertexData_b[0], gl_in[31].VertexData_b[1] }), gl_in[31].VertexData_c } }); + spvUnsafeArray tmp; + tmp = _19; + int _27 = gl_InvocationID ^ 1; + VertexData _30 = VertexData{ gl_in[_27].VertexData_a, spvUnsafeArray({ gl_in[_27].VertexData_b[0], gl_in[_27].VertexData_b[1] }), gl_in[_27].VertexData_c }; + VertexData tmp_single = _30; + gl_out[gl_InvocationID].vOutputs = ((tmp[gl_InvocationID].a[1] + tmp[gl_InvocationID].b[1]) + tmp[gl_InvocationID].c) + tmp_single.c; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-struct.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-struct.tesc new file mode 100644 index 0000000..91d3521 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array-of-struct.tesc @@ -0,0 +1,86 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct VertexData +{ + float4x4 a; + spvUnsafeArray b; + float4 c; +}; + +struct main0_out +{ + float4 vOutputs; +}; + +struct main0_in +{ + float4 VertexData_a_0 [[attribute(0)]]; + float4 VertexData_a_1 [[attribute(1)]]; + float4 VertexData_a_2 [[attribute(2)]]; + float4 VertexData_a_3 [[attribute(3)]]; + float4 VertexData_b_0 [[attribute(4)]]; + float4 VertexData_b_1 [[attribute(5)]]; + float4 VertexData_c [[attribute(6)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + spvUnsafeArray _19 = spvUnsafeArray({ VertexData{ float4x4(gl_in[0].VertexData_a_0, gl_in[0].VertexData_a_1, gl_in[0].VertexData_a_2, gl_in[0].VertexData_a_3), spvUnsafeArray({ gl_in[0].VertexData_b_0, gl_in[0].VertexData_b_1 }), gl_in[0].VertexData_c }, VertexData{ float4x4(gl_in[1].VertexData_a_0, gl_in[1].VertexData_a_1, gl_in[1].VertexData_a_2, gl_in[1].VertexData_a_3), spvUnsafeArray({ gl_in[1].VertexData_b_0, gl_in[1].VertexData_b_1 }), gl_in[1].VertexData_c }, VertexData{ float4x4(gl_in[2].VertexData_a_0, gl_in[2].VertexData_a_1, gl_in[2].VertexData_a_2, gl_in[2].VertexData_a_3), spvUnsafeArray({ gl_in[2].VertexData_b_0, gl_in[2].VertexData_b_1 }), gl_in[2].VertexData_c }, VertexData{ float4x4(gl_in[3].VertexData_a_0, gl_in[3].VertexData_a_1, gl_in[3].VertexData_a_2, gl_in[3].VertexData_a_3), spvUnsafeArray({ gl_in[3].VertexData_b_0, gl_in[3].VertexData_b_1 }), gl_in[3].VertexData_c }, VertexData{ float4x4(gl_in[4].VertexData_a_0, gl_in[4].VertexData_a_1, gl_in[4].VertexData_a_2, gl_in[4].VertexData_a_3), spvUnsafeArray({ gl_in[4].VertexData_b_0, gl_in[4].VertexData_b_1 }), gl_in[4].VertexData_c }, VertexData{ float4x4(gl_in[5].VertexData_a_0, gl_in[5].VertexData_a_1, gl_in[5].VertexData_a_2, gl_in[5].VertexData_a_3), spvUnsafeArray({ gl_in[5].VertexData_b_0, gl_in[5].VertexData_b_1 }), gl_in[5].VertexData_c }, VertexData{ float4x4(gl_in[6].VertexData_a_0, gl_in[6].VertexData_a_1, gl_in[6].VertexData_a_2, gl_in[6].VertexData_a_3), spvUnsafeArray({ gl_in[6].VertexData_b_0, gl_in[6].VertexData_b_1 }), gl_in[6].VertexData_c }, VertexData{ float4x4(gl_in[7].VertexData_a_0, gl_in[7].VertexData_a_1, gl_in[7].VertexData_a_2, gl_in[7].VertexData_a_3), spvUnsafeArray({ gl_in[7].VertexData_b_0, gl_in[7].VertexData_b_1 }), gl_in[7].VertexData_c }, VertexData{ float4x4(gl_in[8].VertexData_a_0, gl_in[8].VertexData_a_1, gl_in[8].VertexData_a_2, gl_in[8].VertexData_a_3), spvUnsafeArray({ gl_in[8].VertexData_b_0, gl_in[8].VertexData_b_1 }), gl_in[8].VertexData_c }, VertexData{ float4x4(gl_in[9].VertexData_a_0, gl_in[9].VertexData_a_1, gl_in[9].VertexData_a_2, gl_in[9].VertexData_a_3), spvUnsafeArray({ gl_in[9].VertexData_b_0, gl_in[9].VertexData_b_1 }), gl_in[9].VertexData_c }, VertexData{ float4x4(gl_in[10].VertexData_a_0, gl_in[10].VertexData_a_1, gl_in[10].VertexData_a_2, gl_in[10].VertexData_a_3), spvUnsafeArray({ gl_in[10].VertexData_b_0, gl_in[10].VertexData_b_1 }), gl_in[10].VertexData_c }, VertexData{ float4x4(gl_in[11].VertexData_a_0, gl_in[11].VertexData_a_1, gl_in[11].VertexData_a_2, gl_in[11].VertexData_a_3), spvUnsafeArray({ gl_in[11].VertexData_b_0, gl_in[11].VertexData_b_1 }), gl_in[11].VertexData_c }, VertexData{ float4x4(gl_in[12].VertexData_a_0, gl_in[12].VertexData_a_1, gl_in[12].VertexData_a_2, gl_in[12].VertexData_a_3), spvUnsafeArray({ gl_in[12].VertexData_b_0, gl_in[12].VertexData_b_1 }), gl_in[12].VertexData_c }, VertexData{ float4x4(gl_in[13].VertexData_a_0, gl_in[13].VertexData_a_1, gl_in[13].VertexData_a_2, gl_in[13].VertexData_a_3), spvUnsafeArray({ gl_in[13].VertexData_b_0, gl_in[13].VertexData_b_1 }), gl_in[13].VertexData_c }, VertexData{ float4x4(gl_in[14].VertexData_a_0, gl_in[14].VertexData_a_1, gl_in[14].VertexData_a_2, gl_in[14].VertexData_a_3), spvUnsafeArray({ gl_in[14].VertexData_b_0, gl_in[14].VertexData_b_1 }), gl_in[14].VertexData_c }, VertexData{ float4x4(gl_in[15].VertexData_a_0, gl_in[15].VertexData_a_1, gl_in[15].VertexData_a_2, gl_in[15].VertexData_a_3), spvUnsafeArray({ gl_in[15].VertexData_b_0, gl_in[15].VertexData_b_1 }), gl_in[15].VertexData_c }, VertexData{ float4x4(gl_in[16].VertexData_a_0, gl_in[16].VertexData_a_1, gl_in[16].VertexData_a_2, gl_in[16].VertexData_a_3), spvUnsafeArray({ gl_in[16].VertexData_b_0, gl_in[16].VertexData_b_1 }), gl_in[16].VertexData_c }, VertexData{ float4x4(gl_in[17].VertexData_a_0, gl_in[17].VertexData_a_1, gl_in[17].VertexData_a_2, gl_in[17].VertexData_a_3), spvUnsafeArray({ gl_in[17].VertexData_b_0, gl_in[17].VertexData_b_1 }), gl_in[17].VertexData_c }, VertexData{ float4x4(gl_in[18].VertexData_a_0, gl_in[18].VertexData_a_1, gl_in[18].VertexData_a_2, gl_in[18].VertexData_a_3), spvUnsafeArray({ gl_in[18].VertexData_b_0, gl_in[18].VertexData_b_1 }), gl_in[18].VertexData_c }, VertexData{ float4x4(gl_in[19].VertexData_a_0, gl_in[19].VertexData_a_1, gl_in[19].VertexData_a_2, gl_in[19].VertexData_a_3), spvUnsafeArray({ gl_in[19].VertexData_b_0, gl_in[19].VertexData_b_1 }), gl_in[19].VertexData_c }, VertexData{ float4x4(gl_in[20].VertexData_a_0, gl_in[20].VertexData_a_1, gl_in[20].VertexData_a_2, gl_in[20].VertexData_a_3), spvUnsafeArray({ gl_in[20].VertexData_b_0, gl_in[20].VertexData_b_1 }), gl_in[20].VertexData_c }, VertexData{ float4x4(gl_in[21].VertexData_a_0, gl_in[21].VertexData_a_1, gl_in[21].VertexData_a_2, gl_in[21].VertexData_a_3), spvUnsafeArray({ gl_in[21].VertexData_b_0, gl_in[21].VertexData_b_1 }), gl_in[21].VertexData_c }, VertexData{ float4x4(gl_in[22].VertexData_a_0, gl_in[22].VertexData_a_1, gl_in[22].VertexData_a_2, gl_in[22].VertexData_a_3), spvUnsafeArray({ gl_in[22].VertexData_b_0, gl_in[22].VertexData_b_1 }), gl_in[22].VertexData_c }, VertexData{ float4x4(gl_in[23].VertexData_a_0, gl_in[23].VertexData_a_1, gl_in[23].VertexData_a_2, gl_in[23].VertexData_a_3), spvUnsafeArray({ gl_in[23].VertexData_b_0, gl_in[23].VertexData_b_1 }), gl_in[23].VertexData_c }, VertexData{ float4x4(gl_in[24].VertexData_a_0, gl_in[24].VertexData_a_1, gl_in[24].VertexData_a_2, gl_in[24].VertexData_a_3), spvUnsafeArray({ gl_in[24].VertexData_b_0, gl_in[24].VertexData_b_1 }), gl_in[24].VertexData_c }, VertexData{ float4x4(gl_in[25].VertexData_a_0, gl_in[25].VertexData_a_1, gl_in[25].VertexData_a_2, gl_in[25].VertexData_a_3), spvUnsafeArray({ gl_in[25].VertexData_b_0, gl_in[25].VertexData_b_1 }), gl_in[25].VertexData_c }, VertexData{ float4x4(gl_in[26].VertexData_a_0, gl_in[26].VertexData_a_1, gl_in[26].VertexData_a_2, gl_in[26].VertexData_a_3), spvUnsafeArray({ gl_in[26].VertexData_b_0, gl_in[26].VertexData_b_1 }), gl_in[26].VertexData_c }, VertexData{ float4x4(gl_in[27].VertexData_a_0, gl_in[27].VertexData_a_1, gl_in[27].VertexData_a_2, gl_in[27].VertexData_a_3), spvUnsafeArray({ gl_in[27].VertexData_b_0, gl_in[27].VertexData_b_1 }), gl_in[27].VertexData_c }, VertexData{ float4x4(gl_in[28].VertexData_a_0, gl_in[28].VertexData_a_1, gl_in[28].VertexData_a_2, gl_in[28].VertexData_a_3), spvUnsafeArray({ gl_in[28].VertexData_b_0, gl_in[28].VertexData_b_1 }), gl_in[28].VertexData_c }, VertexData{ float4x4(gl_in[29].VertexData_a_0, gl_in[29].VertexData_a_1, gl_in[29].VertexData_a_2, gl_in[29].VertexData_a_3), spvUnsafeArray({ gl_in[29].VertexData_b_0, gl_in[29].VertexData_b_1 }), gl_in[29].VertexData_c }, VertexData{ float4x4(gl_in[30].VertexData_a_0, gl_in[30].VertexData_a_1, gl_in[30].VertexData_a_2, gl_in[30].VertexData_a_3), spvUnsafeArray({ gl_in[30].VertexData_b_0, gl_in[30].VertexData_b_1 }), gl_in[30].VertexData_c }, VertexData{ float4x4(gl_in[31].VertexData_a_0, gl_in[31].VertexData_a_1, gl_in[31].VertexData_a_2, gl_in[31].VertexData_a_3), spvUnsafeArray({ gl_in[31].VertexData_b_0, gl_in[31].VertexData_b_1 }), gl_in[31].VertexData_c } }); + spvUnsafeArray tmp; + tmp = _19; + int _27 = gl_InvocationID ^ 1; + VertexData _30 = VertexData{ float4x4(gl_in[_27].VertexData_a_0, gl_in[_27].VertexData_a_1, gl_in[_27].VertexData_a_2, gl_in[_27].VertexData_a_3), spvUnsafeArray({ gl_in[_27].VertexData_b_0, gl_in[_27].VertexData_b_1 }), gl_in[_27].VertexData_c }; + VertexData tmp_single = _30; + gl_out[gl_InvocationID].vOutputs = ((tmp[gl_InvocationID].a[1] + tmp[gl_InvocationID].b[1]) + tmp[gl_InvocationID].c) + tmp_single.c; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array.multi-patch.tesc new file mode 100644 index 0000000..416bc68 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array.multi-patch.tesc @@ -0,0 +1,69 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 vOutputs; +}; + +struct main0_in +{ + float4 vInputs; + ushort2 m_43; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + spvUnsafeArray _15 = spvUnsafeArray({ gl_in[0].vInputs, gl_in[1].vInputs, gl_in[2].vInputs, gl_in[3].vInputs, gl_in[4].vInputs, gl_in[5].vInputs, gl_in[6].vInputs, gl_in[7].vInputs, gl_in[8].vInputs, gl_in[9].vInputs, gl_in[10].vInputs, gl_in[11].vInputs, gl_in[12].vInputs, gl_in[13].vInputs, gl_in[14].vInputs, gl_in[15].vInputs, gl_in[16].vInputs, gl_in[17].vInputs, gl_in[18].vInputs, gl_in[19].vInputs, gl_in[20].vInputs, gl_in[21].vInputs, gl_in[22].vInputs, gl_in[23].vInputs, gl_in[24].vInputs, gl_in[25].vInputs, gl_in[26].vInputs, gl_in[27].vInputs, gl_in[28].vInputs, gl_in[29].vInputs, gl_in[30].vInputs, gl_in[31].vInputs }); + spvUnsafeArray tmp; + tmp = _15; + gl_out[gl_InvocationID].vOutputs = tmp[gl_InvocationID]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array.tesc new file mode 100644 index 0000000..d04571a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/load-control-point-array.tesc @@ -0,0 +1,70 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 vOutputs; +}; + +struct main0_in +{ + float4 vInputs [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + spvUnsafeArray _15 = spvUnsafeArray({ gl_in[0].vInputs, gl_in[1].vInputs, gl_in[2].vInputs, gl_in[3].vInputs, gl_in[4].vInputs, gl_in[5].vInputs, gl_in[6].vInputs, gl_in[7].vInputs, gl_in[8].vInputs, gl_in[9].vInputs, gl_in[10].vInputs, gl_in[11].vInputs, gl_in[12].vInputs, gl_in[13].vInputs, gl_in[14].vInputs, gl_in[15].vInputs, gl_in[16].vInputs, gl_in[17].vInputs, gl_in[18].vInputs, gl_in[19].vInputs, gl_in[20].vInputs, gl_in[21].vInputs, gl_in[22].vInputs, gl_in[23].vInputs, gl_in[24].vInputs, gl_in[25].vInputs, gl_in[26].vInputs, gl_in[27].vInputs, gl_in[28].vInputs, gl_in[29].vInputs, gl_in[30].vInputs, gl_in[31].vInputs }); + spvUnsafeArray tmp; + tmp = _15; + gl_out[gl_InvocationID].vOutputs = tmp[gl_InvocationID]; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/matrix-output.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/matrix-output.multi-patch.tesc new file mode 100644 index 0000000..3e045ee --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/matrix-output.multi-patch.tesc @@ -0,0 +1,39 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float in_te_attr; + float4x3 in_te_data0; + float4x3 in_te_data1; +}; + +struct main0_in +{ + float3 in_tc_attr; + ushort2 m_103; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 3]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 3; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1]); + float _15 = float(gl_InvocationID); + float4x3 d = float4x3(float3(_15, 0.0, 0.0), float3(0.0, _15, 0.0), float3(0.0, 0.0, _15), float3(0.0)); + gl_out[gl_InvocationID].in_te_data0 = d; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + int _42 = (gl_InvocationID + 1) % 3; + gl_out[gl_InvocationID].in_te_data1 = float4x3(d[0] + gl_out[_42].in_te_data0[0], d[1] + gl_out[_42].in_te_data0[1], d[2] + gl_out[_42].in_te_data0[2], d[3] + gl_out[_42].in_te_data0[3]); + gl_out[gl_InvocationID].in_te_attr = gl_in[gl_InvocationID].in_tc_attr.x; + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/reload-tess-level.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/reload-tess-level.multi-patch.tesc new file mode 100644 index 0000000..a55755e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/reload-tess-level.multi-patch.tesc @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +struct main0_in +{ + uint3 m_82; + ushort2 m_86; + float4 gl_Position; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 4]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 4; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 4, spvIndirectParams[1]); + if (gl_InvocationID == 0) + { + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(4.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(5.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(mix(float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0]), float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3]), 0.5)); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(mix(float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2]), float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1]), 0.5)); + } + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/reload-tess-level.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/reload-tess-level.tesc new file mode 100644 index 0000000..eafc506 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/reload-tess-level.tesc @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position; +}; + +struct main0_in +{ + float4 gl_Position [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 4]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 4) + return; + if (gl_InvocationID == 0) + { + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(2.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(3.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(4.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(5.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(mix(float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0]), float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3]), 0.5)); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(mix(float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2]), float(spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1]), 0.5)); + } + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/struct-output.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/struct-output.multi-patch.tesc new file mode 100644 index 0000000..7b5307b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/struct-output.multi-patch.tesc @@ -0,0 +1,45 @@ +#include +#include + +using namespace metal; + +struct te_data +{ + float a; + float b; + uint c; +}; + +struct main0_out +{ + float in_te_attr; + te_data in_te_data0; + te_data in_te_data1; +}; + +struct main0_in +{ + float3 in_tc_attr; + ushort2 m_107; +}; + +kernel void main0(uint3 gl_GlobalInvocationID [[thread_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_out* gl_out = &spvOut[gl_GlobalInvocationID.x - gl_GlobalInvocationID.x % 3]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_InvocationID = gl_GlobalInvocationID.x % 3; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 3, spvIndirectParams[1]); + te_data d = te_data{ float(gl_InvocationID), float(gl_InvocationID + 1), uint(gl_InvocationID) }; + gl_out[gl_InvocationID].in_te_data0 = d; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + te_data e = gl_out[(gl_InvocationID + 1) % 3].in_te_data0; + gl_out[gl_InvocationID].in_te_data1 = te_data{ d.a + e.a, d.b + e.b, d.c + e.c }; + gl_out[gl_InvocationID].in_te_attr = gl_in[gl_InvocationID].in_tc_attr.x; + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/water_tess.multi-patch.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/water_tess.multi-patch.tesc new file mode 100644 index 0000000..b1fd2ed --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/water_tess.multi-patch.tesc @@ -0,0 +1,135 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4 uScale; + float3 uCamPos; + float2 uPatchSize; + float2 uMaxTessLevel; + float uDistanceMod; + float4 uFrustum[6]; +}; + +struct main0_patchOut +{ + float2 vOutPatchPosBase; + float4 vPatchLods; +}; + +struct main0_in +{ + float3 vPatchPosBase; + ushort2 m_430; +}; + +static inline __attribute__((always_inline)) +bool frustum_cull(thread const float2& p0, constant UBO& v_41) +{ + float2 min_xz = (p0 - float2(10.0)) * v_41.uScale.xy; + float2 max_xz = ((p0 + v_41.uPatchSize) + float2(10.0)) * v_41.uScale.xy; + float3 bb_min = float3(min_xz.x, -10.0, min_xz.y); + float3 bb_max = float3(max_xz.x, 10.0, max_xz.y); + float3 center = (bb_min + bb_max) * 0.5; + float radius = 0.5 * length(bb_max - bb_min); + float3 f0 = float3(dot(v_41.uFrustum[0], float4(center, 1.0)), dot(v_41.uFrustum[1], float4(center, 1.0)), dot(v_41.uFrustum[2], float4(center, 1.0))); + float3 f1 = float3(dot(v_41.uFrustum[3], float4(center, 1.0)), dot(v_41.uFrustum[4], float4(center, 1.0)), dot(v_41.uFrustum[5], float4(center, 1.0))); + bool _205 = any(f0 <= float3(-radius)); + bool _215; + if (!_205) + { + _215 = any(f1 <= float3(-radius)); + } + else + { + _215 = _205; + } + return !_215; +} + +static inline __attribute__((always_inline)) +float lod_factor(thread const float2& pos_, constant UBO& v_41) +{ + float2 pos = pos_ * v_41.uScale.xy; + float3 dist_to_cam = v_41.uCamPos - float3(pos.x, 0.0, pos.y); + float level0 = log2((length(dist_to_cam) + 9.9999997473787516355514526367188e-05) * v_41.uDistanceMod); + return fast::clamp(level0, 0.0, v_41.uMaxTessLevel.x); +} + +static inline __attribute__((always_inline)) +float4 tess_level(thread const float4& lod, constant UBO& v_41) +{ + return exp2(-lod) * v_41.uMaxTessLevel.y; +} + +static inline __attribute__((always_inline)) +float tess_level(thread const float& lod, constant UBO& v_41) +{ + return v_41.uMaxTessLevel.y * exp2(-lod); +} + +static inline __attribute__((always_inline)) +void compute_tess_levels(thread const float2& p0, constant UBO& v_41, device float2& vOutPatchPosBase, device float4& vPatchLods, device half (&gl_TessLevelOuter)[4], device half (&gl_TessLevelInner)[2]) +{ + vOutPatchPosBase = p0; + float2 param = p0 + (float2(-0.5) * v_41.uPatchSize); + float l00 = lod_factor(param, v_41); + float2 param_1 = p0 + (float2(0.5, -0.5) * v_41.uPatchSize); + float l10 = lod_factor(param_1, v_41); + float2 param_2 = p0 + (float2(1.5, -0.5) * v_41.uPatchSize); + float l20 = lod_factor(param_2, v_41); + float2 param_3 = p0 + (float2(-0.5, 0.5) * v_41.uPatchSize); + float l01 = lod_factor(param_3, v_41); + float2 param_4 = p0 + (float2(0.5) * v_41.uPatchSize); + float l11 = lod_factor(param_4, v_41); + float2 param_5 = p0 + (float2(1.5, 0.5) * v_41.uPatchSize); + float l21 = lod_factor(param_5, v_41); + float2 param_6 = p0 + (float2(-0.5, 1.5) * v_41.uPatchSize); + float l02 = lod_factor(param_6, v_41); + float2 param_7 = p0 + (float2(0.5, 1.5) * v_41.uPatchSize); + float l12 = lod_factor(param_7, v_41); + float2 param_8 = p0 + (float2(1.5) * v_41.uPatchSize); + float l22 = lod_factor(param_8, v_41); + float4 lods = float4(dot(float4(l01, l11, l02, l12), float4(0.25)), dot(float4(l00, l10, l01, l11), float4(0.25)), dot(float4(l10, l20, l11, l21), float4(0.25)), dot(float4(l11, l21, l12, l22), float4(0.25))); + vPatchLods = lods; + float4 outer_lods = fast::min(lods, lods.yzwx); + float4 param_9 = outer_lods; + float4 levels = tess_level(param_9, v_41); + gl_TessLevelOuter[0] = half(levels.x); + gl_TessLevelOuter[1] = half(levels.y); + gl_TessLevelOuter[2] = half(levels.z); + gl_TessLevelOuter[3] = half(levels.w); + float min_lod = fast::min(fast::min(lods.x, lods.y), fast::min(lods.z, lods.w)); + float param_10 = fast::min(min_lod, l11); + float inner = tess_level(param_10, v_41); + gl_TessLevelInner[0] = half(inner); + gl_TessLevelInner[1] = half(inner); +} + +kernel void main0(constant UBO& v_41 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], device main0_in* spvIn [[buffer(22)]]) +{ + device main0_patchOut& patchOut = spvPatchOut[gl_GlobalInvocationID.x / 1]; + device main0_in* gl_in = &spvIn[min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1] - 1) * spvIndirectParams[0]]; + uint gl_PrimitiveID = min(gl_GlobalInvocationID.x / 1, spvIndirectParams[1]); + float2 p0 = gl_in[0].vPatchPosBase.xy; + float2 param = p0; + if (!frustum_cull(param, v_41)) + { + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(-1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(-1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(-1.0); + } + else + { + float2 param_1 = p0; + compute_tess_levels(param_1, v_41, patchOut.vOutPatchPosBase, patchOut.vPatchLods, spvTessLevel[gl_PrimitiveID].edgeTessellationFactor, spvTessLevel[gl_PrimitiveID].insideTessellationFactor); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tesc/water_tess.tesc b/third_party/spirv-cross/reference/shaders-msl/tesc/water_tess.tesc new file mode 100644 index 0000000..0287df0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tesc/water_tess.tesc @@ -0,0 +1,137 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4 uScale; + float3 uCamPos; + float2 uPatchSize; + float2 uMaxTessLevel; + float uDistanceMod; + float4 uFrustum[6]; +}; + +struct main0_patchOut +{ + float2 vOutPatchPosBase; + float4 vPatchLods; +}; + +struct main0_in +{ + float2 vPatchPosBase [[attribute(0)]]; +}; + +static inline __attribute__((always_inline)) +bool frustum_cull(thread const float2& p0, constant UBO& v_41) +{ + float2 min_xz = (p0 - float2(10.0)) * v_41.uScale.xy; + float2 max_xz = ((p0 + v_41.uPatchSize) + float2(10.0)) * v_41.uScale.xy; + float3 bb_min = float3(min_xz.x, -10.0, min_xz.y); + float3 bb_max = float3(max_xz.x, 10.0, max_xz.y); + float3 center = (bb_min + bb_max) * 0.5; + float radius = 0.5 * length(bb_max - bb_min); + float3 f0 = float3(dot(v_41.uFrustum[0], float4(center, 1.0)), dot(v_41.uFrustum[1], float4(center, 1.0)), dot(v_41.uFrustum[2], float4(center, 1.0))); + float3 f1 = float3(dot(v_41.uFrustum[3], float4(center, 1.0)), dot(v_41.uFrustum[4], float4(center, 1.0)), dot(v_41.uFrustum[5], float4(center, 1.0))); + bool _205 = any(f0 <= float3(-radius)); + bool _215; + if (!_205) + { + _215 = any(f1 <= float3(-radius)); + } + else + { + _215 = _205; + } + return !_215; +} + +static inline __attribute__((always_inline)) +float lod_factor(thread const float2& pos_, constant UBO& v_41) +{ + float2 pos = pos_ * v_41.uScale.xy; + float3 dist_to_cam = v_41.uCamPos - float3(pos.x, 0.0, pos.y); + float level0 = log2((length(dist_to_cam) + 9.9999997473787516355514526367188e-05) * v_41.uDistanceMod); + return fast::clamp(level0, 0.0, v_41.uMaxTessLevel.x); +} + +static inline __attribute__((always_inline)) +float4 tess_level(thread const float4& lod, constant UBO& v_41) +{ + return exp2(-lod) * v_41.uMaxTessLevel.y; +} + +static inline __attribute__((always_inline)) +float tess_level(thread const float& lod, constant UBO& v_41) +{ + return v_41.uMaxTessLevel.y * exp2(-lod); +} + +static inline __attribute__((always_inline)) +void compute_tess_levels(thread const float2& p0, constant UBO& v_41, device float2& vOutPatchPosBase, device float4& vPatchLods, device half (&gl_TessLevelOuter)[4], device half (&gl_TessLevelInner)[2]) +{ + vOutPatchPosBase = p0; + float2 param = p0 + (float2(-0.5) * v_41.uPatchSize); + float l00 = lod_factor(param, v_41); + float2 param_1 = p0 + (float2(0.5, -0.5) * v_41.uPatchSize); + float l10 = lod_factor(param_1, v_41); + float2 param_2 = p0 + (float2(1.5, -0.5) * v_41.uPatchSize); + float l20 = lod_factor(param_2, v_41); + float2 param_3 = p0 + (float2(-0.5, 0.5) * v_41.uPatchSize); + float l01 = lod_factor(param_3, v_41); + float2 param_4 = p0 + (float2(0.5) * v_41.uPatchSize); + float l11 = lod_factor(param_4, v_41); + float2 param_5 = p0 + (float2(1.5, 0.5) * v_41.uPatchSize); + float l21 = lod_factor(param_5, v_41); + float2 param_6 = p0 + (float2(-0.5, 1.5) * v_41.uPatchSize); + float l02 = lod_factor(param_6, v_41); + float2 param_7 = p0 + (float2(0.5, 1.5) * v_41.uPatchSize); + float l12 = lod_factor(param_7, v_41); + float2 param_8 = p0 + (float2(1.5) * v_41.uPatchSize); + float l22 = lod_factor(param_8, v_41); + float4 lods = float4(dot(float4(l01, l11, l02, l12), float4(0.25)), dot(float4(l00, l10, l01, l11), float4(0.25)), dot(float4(l10, l20, l11, l21), float4(0.25)), dot(float4(l11, l21, l12, l22), float4(0.25))); + vPatchLods = lods; + float4 outer_lods = fast::min(lods, lods.yzwx); + float4 param_9 = outer_lods; + float4 levels = tess_level(param_9, v_41); + gl_TessLevelOuter[0] = half(levels.x); + gl_TessLevelOuter[1] = half(levels.y); + gl_TessLevelOuter[2] = half(levels.z); + gl_TessLevelOuter[3] = half(levels.w); + float min_lod = fast::min(fast::min(lods.x, lods.y), fast::min(lods.z, lods.w)); + float param_10 = fast::min(min_lod, l11); + float inner = tess_level(param_10, v_41); + gl_TessLevelInner[0] = half(inner); + gl_TessLevelInner[1] = half(inner); +} + +kernel void main0(main0_in in [[stage_in]], constant UBO& v_41 [[buffer(0)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 1) + return; + float2 p0 = gl_in[0].vPatchPosBase; + float2 param = p0; + if (!frustum_cull(param, v_41)) + { + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2] = half(-1.0); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3] = half(-1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0] = half(-1.0); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1] = half(-1.0); + } + else + { + float2 param_1 = p0; + compute_tess_levels(param_1, v_41, patchOut.vOutPatchPosBase, patchOut.vPatchLods, spvTessLevel[gl_PrimitiveID].edgeTessellationFactor, spvTessLevel[gl_PrimitiveID].insideTessellationFactor); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/input-array.tese b/third_party/spirv-cross/reference/shaders-msl/tese/input-array.tese new file mode 100644 index 0000000..f6cfa2f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/input-array.tese @@ -0,0 +1,36 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Floats [[attribute(0)]]; + float4 Floats2 [[attribute(2)]]; +}; + +struct main0_patchIn +{ + patch_control_point gl_in; +}; + +static inline __attribute__((always_inline)) +void set_position(thread float4& gl_Position, thread patch_control_point& gl_in, thread float2& gl_TessCoord) +{ + gl_Position = (gl_in[0].Floats * gl_TessCoord.x) + (gl_in[1].Floats2 * gl_TessCoord.y); +} + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + set_position(out.gl_Position, patchIn.gl_in, gl_TessCoord); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/input-types.tese b/third_party/spirv-cross/reference/shaders-msl/tese/input-types.tese new file mode 100644 index 0000000..af6db46 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/input-types.tese @@ -0,0 +1,87 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Block +{ + float4 a; + float4 b; +}; + +struct PatchBlock +{ + float4 a; + float4 b; +}; + +struct Foo +{ + float4 a; + float4 b; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vColor [[attribute(0)]]; + float4 Block_a [[attribute(4)]]; + float4 Block_b [[attribute(5)]]; + float4 Foo_a [[attribute(14)]]; + float4 Foo_b [[attribute(15)]]; +}; + +struct main0_patchIn +{ + float4 vColors [[attribute(1)]]; + float4 PatchBlock_a [[attribute(6)]]; + float4 PatchBlock_b [[attribute(7)]]; + float4 Foo_a [[attribute(8)]]; + float4 Foo_b [[attribute(9)]]; + patch_control_point gl_in; +}; + +static inline __attribute__((always_inline)) +void set_from_function(thread float4& gl_Position, thread patch_control_point& gl_in, thread PatchBlock& patch_block, thread float4& vColors, thread Foo& vFoo) +{ + gl_Position = gl_in[0].Block_a; + gl_Position += gl_in[0].Block_b; + gl_Position += gl_in[1].Block_a; + gl_Position += gl_in[1].Block_b; + gl_Position += patch_block.a; + gl_Position += patch_block.b; + gl_Position += gl_in[0].vColor; + gl_Position += gl_in[1].vColor; + gl_Position += vColors; + Foo foo = vFoo; + gl_Position += foo.a; + gl_Position += foo.b; + Foo _106 = Foo{ gl_in[0].Foo_a, gl_in[0].Foo_b }; + foo = _106; + gl_Position += foo.a; + gl_Position += foo.b; + Foo _120 = Foo{ gl_in[1].Foo_a, gl_in[1].Foo_b }; + foo = _120; + gl_Position += foo.a; + gl_Position += foo.b; +} + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]]) +{ + main0_out out = {}; + PatchBlock patch_block = {}; + Foo vFoo = {}; + patch_block.a = patchIn.PatchBlock_a; + patch_block.b = patchIn.PatchBlock_b; + vFoo.a = patchIn.Foo_a; + vFoo.b = patchIn.Foo_b; + set_from_function(out.gl_Position, patchIn.gl_in, patch_block, patchIn.vColors, vFoo); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/load-control-point-array-of-matrix.tese b/third_party/spirv-cross/reference/shaders-msl/tese/load-control-point-array-of-matrix.tese new file mode 100644 index 0000000..162874a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/load-control-point-array-of-matrix.tese @@ -0,0 +1,84 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vInputs_0 [[attribute(0)]]; + float4 vInputs_1 [[attribute(1)]]; + float4 vInputs_2 [[attribute(2)]]; + float4 vInputs_3 [[attribute(3)]]; +}; + +struct main0_patchIn +{ + float4 vBoo_0 [[attribute(4)]]; + float4 vBoo_1 [[attribute(5)]]; + float4 vBoo_2 [[attribute(6)]]; + float4 vBoo_3 [[attribute(7)]]; + int vIndex [[attribute(8)]]; + patch_control_point gl_in; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray vBoo = {}; + vBoo[0] = patchIn.vBoo_0; + vBoo[1] = patchIn.vBoo_1; + vBoo[2] = patchIn.vBoo_2; + vBoo[3] = patchIn.vBoo_3; + spvUnsafeArray _16 = spvUnsafeArray({ float4x4(patchIn.gl_in[0].vInputs_0, patchIn.gl_in[0].vInputs_1, patchIn.gl_in[0].vInputs_2, patchIn.gl_in[0].vInputs_3), float4x4(patchIn.gl_in[1].vInputs_0, patchIn.gl_in[1].vInputs_1, patchIn.gl_in[1].vInputs_2, patchIn.gl_in[1].vInputs_3), float4x4(patchIn.gl_in[2].vInputs_0, patchIn.gl_in[2].vInputs_1, patchIn.gl_in[2].vInputs_2, patchIn.gl_in[2].vInputs_3), float4x4(patchIn.gl_in[3].vInputs_0, patchIn.gl_in[3].vInputs_1, patchIn.gl_in[3].vInputs_2, patchIn.gl_in[3].vInputs_3), float4x4(patchIn.gl_in[4].vInputs_0, patchIn.gl_in[4].vInputs_1, patchIn.gl_in[4].vInputs_2, patchIn.gl_in[4].vInputs_3), float4x4(patchIn.gl_in[5].vInputs_0, patchIn.gl_in[5].vInputs_1, patchIn.gl_in[5].vInputs_2, patchIn.gl_in[5].vInputs_3), float4x4(patchIn.gl_in[6].vInputs_0, patchIn.gl_in[6].vInputs_1, patchIn.gl_in[6].vInputs_2, patchIn.gl_in[6].vInputs_3), float4x4(patchIn.gl_in[7].vInputs_0, patchIn.gl_in[7].vInputs_1, patchIn.gl_in[7].vInputs_2, patchIn.gl_in[7].vInputs_3), float4x4(patchIn.gl_in[8].vInputs_0, patchIn.gl_in[8].vInputs_1, patchIn.gl_in[8].vInputs_2, patchIn.gl_in[8].vInputs_3), float4x4(patchIn.gl_in[9].vInputs_0, patchIn.gl_in[9].vInputs_1, patchIn.gl_in[9].vInputs_2, patchIn.gl_in[9].vInputs_3), float4x4(patchIn.gl_in[10].vInputs_0, patchIn.gl_in[10].vInputs_1, patchIn.gl_in[10].vInputs_2, patchIn.gl_in[10].vInputs_3), float4x4(patchIn.gl_in[11].vInputs_0, patchIn.gl_in[11].vInputs_1, patchIn.gl_in[11].vInputs_2, patchIn.gl_in[11].vInputs_3), float4x4(patchIn.gl_in[12].vInputs_0, patchIn.gl_in[12].vInputs_1, patchIn.gl_in[12].vInputs_2, patchIn.gl_in[12].vInputs_3), float4x4(patchIn.gl_in[13].vInputs_0, patchIn.gl_in[13].vInputs_1, patchIn.gl_in[13].vInputs_2, patchIn.gl_in[13].vInputs_3), float4x4(patchIn.gl_in[14].vInputs_0, patchIn.gl_in[14].vInputs_1, patchIn.gl_in[14].vInputs_2, patchIn.gl_in[14].vInputs_3), float4x4(patchIn.gl_in[15].vInputs_0, patchIn.gl_in[15].vInputs_1, patchIn.gl_in[15].vInputs_2, patchIn.gl_in[15].vInputs_3), float4x4(patchIn.gl_in[16].vInputs_0, patchIn.gl_in[16].vInputs_1, patchIn.gl_in[16].vInputs_2, patchIn.gl_in[16].vInputs_3), float4x4(patchIn.gl_in[17].vInputs_0, patchIn.gl_in[17].vInputs_1, patchIn.gl_in[17].vInputs_2, patchIn.gl_in[17].vInputs_3), float4x4(patchIn.gl_in[18].vInputs_0, patchIn.gl_in[18].vInputs_1, patchIn.gl_in[18].vInputs_2, patchIn.gl_in[18].vInputs_3), float4x4(patchIn.gl_in[19].vInputs_0, patchIn.gl_in[19].vInputs_1, patchIn.gl_in[19].vInputs_2, patchIn.gl_in[19].vInputs_3), float4x4(patchIn.gl_in[20].vInputs_0, patchIn.gl_in[20].vInputs_1, patchIn.gl_in[20].vInputs_2, patchIn.gl_in[20].vInputs_3), float4x4(patchIn.gl_in[21].vInputs_0, patchIn.gl_in[21].vInputs_1, patchIn.gl_in[21].vInputs_2, patchIn.gl_in[21].vInputs_3), float4x4(patchIn.gl_in[22].vInputs_0, patchIn.gl_in[22].vInputs_1, patchIn.gl_in[22].vInputs_2, patchIn.gl_in[22].vInputs_3), float4x4(patchIn.gl_in[23].vInputs_0, patchIn.gl_in[23].vInputs_1, patchIn.gl_in[23].vInputs_2, patchIn.gl_in[23].vInputs_3), float4x4(patchIn.gl_in[24].vInputs_0, patchIn.gl_in[24].vInputs_1, patchIn.gl_in[24].vInputs_2, patchIn.gl_in[24].vInputs_3), float4x4(patchIn.gl_in[25].vInputs_0, patchIn.gl_in[25].vInputs_1, patchIn.gl_in[25].vInputs_2, patchIn.gl_in[25].vInputs_3), float4x4(patchIn.gl_in[26].vInputs_0, patchIn.gl_in[26].vInputs_1, patchIn.gl_in[26].vInputs_2, patchIn.gl_in[26].vInputs_3), float4x4(patchIn.gl_in[27].vInputs_0, patchIn.gl_in[27].vInputs_1, patchIn.gl_in[27].vInputs_2, patchIn.gl_in[27].vInputs_3), float4x4(patchIn.gl_in[28].vInputs_0, patchIn.gl_in[28].vInputs_1, patchIn.gl_in[28].vInputs_2, patchIn.gl_in[28].vInputs_3), float4x4(patchIn.gl_in[29].vInputs_0, patchIn.gl_in[29].vInputs_1, patchIn.gl_in[29].vInputs_2, patchIn.gl_in[29].vInputs_3), float4x4(patchIn.gl_in[30].vInputs_0, patchIn.gl_in[30].vInputs_1, patchIn.gl_in[30].vInputs_2, patchIn.gl_in[30].vInputs_3), float4x4(patchIn.gl_in[31].vInputs_0, patchIn.gl_in[31].vInputs_1, patchIn.gl_in[31].vInputs_2, patchIn.gl_in[31].vInputs_3) }); + spvUnsafeArray tmp; + tmp = _16; + out.gl_Position = (tmp[0][patchIn.vIndex] + tmp[1][patchIn.vIndex]) + vBoo[patchIn.vIndex]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/load-control-point-array.tese b/third_party/spirv-cross/reference/shaders-msl/tese/load-control-point-array.tese new file mode 100644 index 0000000..09c19cb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/load-control-point-array.tese @@ -0,0 +1,81 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vInputs [[attribute(0)]]; +}; + +struct main0_patchIn +{ + float4 vBoo_0 [[attribute(1)]]; + float4 vBoo_1 [[attribute(2)]]; + float4 vBoo_2 [[attribute(3)]]; + float4 vBoo_3 [[attribute(4)]]; + int vIndex [[attribute(5)]]; + patch_control_point gl_in; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray vBoo = {}; + vBoo[0] = patchIn.vBoo_0; + vBoo[1] = patchIn.vBoo_1; + vBoo[2] = patchIn.vBoo_2; + vBoo[3] = patchIn.vBoo_3; + spvUnsafeArray _15 = spvUnsafeArray({ patchIn.gl_in[0].vInputs, patchIn.gl_in[1].vInputs, patchIn.gl_in[2].vInputs, patchIn.gl_in[3].vInputs, patchIn.gl_in[4].vInputs, patchIn.gl_in[5].vInputs, patchIn.gl_in[6].vInputs, patchIn.gl_in[7].vInputs, patchIn.gl_in[8].vInputs, patchIn.gl_in[9].vInputs, patchIn.gl_in[10].vInputs, patchIn.gl_in[11].vInputs, patchIn.gl_in[12].vInputs, patchIn.gl_in[13].vInputs, patchIn.gl_in[14].vInputs, patchIn.gl_in[15].vInputs, patchIn.gl_in[16].vInputs, patchIn.gl_in[17].vInputs, patchIn.gl_in[18].vInputs, patchIn.gl_in[19].vInputs, patchIn.gl_in[20].vInputs, patchIn.gl_in[21].vInputs, patchIn.gl_in[22].vInputs, patchIn.gl_in[23].vInputs, patchIn.gl_in[24].vInputs, patchIn.gl_in[25].vInputs, patchIn.gl_in[26].vInputs, patchIn.gl_in[27].vInputs, patchIn.gl_in[28].vInputs, patchIn.gl_in[29].vInputs, patchIn.gl_in[30].vInputs, patchIn.gl_in[31].vInputs }); + spvUnsafeArray tmp; + tmp = _15; + out.gl_Position = (tmp[0] + tmp[1]) + vBoo[patchIn.vIndex]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/quad.domain.tese b/third_party/spirv-cross/reference/shaders-msl/tese/quad.domain.tese new file mode 100644 index 0000000..78b58ab --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/quad.domain.tese @@ -0,0 +1,24 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float2 gl_TessLevelInner [[attribute(0)]]; + float4 gl_TessLevelOuter [[attribute(1)]]; +}; + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + gl_TessCoord.y = 1.0 - gl_TessCoord.y; + out.gl_Position = float4(((gl_TessCoord.x * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.x) + (((1.0 - gl_TessCoord.x) * patchIn.gl_TessLevelInner.x) * patchIn.gl_TessLevelOuter.z), ((gl_TessCoord.y * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.w) + (((1.0 - gl_TessCoord.y) * patchIn.gl_TessLevelInner.y) * patchIn.gl_TessLevelOuter.y), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/quad.tese b/third_party/spirv-cross/reference/shaders-msl/tese/quad.tese new file mode 100644 index 0000000..4027358 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/quad.tese @@ -0,0 +1,31 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float2 gl_TessLevelInner [[attribute(0)]]; + float4 gl_TessLevelOuter [[attribute(1)]]; +}; + +static inline __attribute__((always_inline)) +void set_position(thread float4& gl_Position, thread float2& gl_TessCoord, thread float2& gl_TessLevelInner, thread float4& gl_TessLevelOuter) +{ + gl_Position = float4(((gl_TessCoord.x * gl_TessLevelInner.x) * gl_TessLevelOuter.x) + (((1.0 - gl_TessCoord.x) * gl_TessLevelInner.x) * gl_TessLevelOuter.z), ((gl_TessCoord.y * gl_TessLevelInner.y) * gl_TessLevelOuter.y) + (((1.0 - gl_TessCoord.y) * gl_TessLevelInner.y) * gl_TessLevelOuter.w), 0.0, 1.0); +} + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + set_position(out.gl_Position, gl_TessCoord, patchIn.gl_TessLevelInner, patchIn.gl_TessLevelOuter); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/set-from-function.tese b/third_party/spirv-cross/reference/shaders-msl/tese/set-from-function.tese new file mode 100644 index 0000000..30bb720 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/set-from-function.tese @@ -0,0 +1,63 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Block +{ + float4 a; + float4 b; +}; + +struct Foo +{ + float4 a; + float4 b; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vColor [[attribute(0)]]; + float4 Block_a [[attribute(2)]]; + float4 Block_b [[attribute(3)]]; +}; + +struct main0_patchIn +{ + float4 vColors [[attribute(1)]]; + float4 Foo_a [[attribute(4)]]; + float4 Foo_b [[attribute(5)]]; + patch_control_point gl_in; +}; + +static inline __attribute__((always_inline)) +void set_from_function(thread float4& gl_Position, thread patch_control_point& gl_in, thread float4& vColors, thread Foo& vFoo) +{ + gl_Position = gl_in[0].Block_a; + gl_Position += gl_in[0].Block_b; + gl_Position += gl_in[1].Block_a; + gl_Position += gl_in[1].Block_b; + gl_Position += gl_in[0].vColor; + gl_Position += gl_in[1].vColor; + gl_Position += vColors; + gl_Position += vFoo.a; + gl_Position += vFoo.b; +} + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]]) +{ + main0_out out = {}; + Foo vFoo = {}; + vFoo.a = patchIn.Foo_a; + vFoo.b = patchIn.Foo_b; + set_from_function(out.gl_Position, patchIn.gl_in, patchIn.vColors, vFoo); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/triangle-tess-level.tese b/third_party/spirv-cross/reference/shaders-msl/tese/triangle-tess-level.tese new file mode 100644 index 0000000..6930e14 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/triangle-tess-level.tese @@ -0,0 +1,69 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float4 gl_TessLevel [[attribute(0)]]; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray gl_TessLevelInner = {}; + spvUnsafeArray gl_TessLevelOuter = {}; + gl_TessLevelInner[0] = patchIn.gl_TessLevel.w; + gl_TessLevelOuter[0] = patchIn.gl_TessLevel.x; + gl_TessLevelOuter[1] = patchIn.gl_TessLevel.y; + gl_TessLevelOuter[2] = patchIn.gl_TessLevel.z; + out.gl_Position = float4((gl_TessCoord.x * gl_TessLevelInner[0]) * gl_TessLevelOuter[0], (gl_TessCoord.y * gl_TessLevelInner[0]) * gl_TessLevelOuter[1], (gl_TessCoord.z * gl_TessLevelInner[0]) * gl_TessLevelOuter[2], 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/triangle.tese b/third_party/spirv-cross/reference/shaders-msl/tese/triangle.tese new file mode 100644 index 0000000..12715be --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/triangle.tese @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0() +{ + main0_out out = {}; + out.gl_Position = float4(1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/tese/water_tess.tese b/third_party/spirv-cross/reference/shaders-msl/tese/water_tess.tese new file mode 100644 index 0000000..1295c45 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/tese/water_tess.tese @@ -0,0 +1,75 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; + float4 uScale; + float2 uInvScale; + float3 uCamPos; + float2 uPatchSize; + float2 uInvHeightmapSize; +}; + +struct main0_out +{ + float3 vWorld [[user(locn0)]]; + float4 vGradNormalTex [[user(locn1)]]; + float4 gl_Position [[position]]; +}; + +struct main0_patchIn +{ + float2 vOutPatchPosBase [[attribute(0)]]; + float4 vPatchLods [[attribute(1)]]; +}; + +static inline __attribute__((always_inline)) +float2 lerp_vertex(thread const float2& tess_coord, thread float2& vOutPatchPosBase, constant UBO& v_31) +{ + return vOutPatchPosBase + (tess_coord * v_31.uPatchSize); +} + +static inline __attribute__((always_inline)) +float2 lod_factor(thread const float2& tess_coord, thread float4& vPatchLods) +{ + float2 x = mix(vPatchLods.yx, vPatchLods.zw, float2(tess_coord.x)); + float level0 = mix(x.x, x.y, tess_coord.y); + float floor_level = floor(level0); + float fract_level = level0 - floor_level; + return float2(floor_level, fract_level); +} + +static inline __attribute__((always_inline)) +float3 sample_height_displacement(thread const float2& uv, thread const float2& off, thread const float2& lod, thread texture2d uHeightmapDisplacement, thread const sampler uHeightmapDisplacementSmplr) +{ + return mix(uHeightmapDisplacement.sample(uHeightmapDisplacementSmplr, (uv + (off * 0.5)), level(lod.x)).xyz, uHeightmapDisplacement.sample(uHeightmapDisplacementSmplr, (uv + (off * 1.0)), level(lod.x + 1.0)).xyz, float3(lod.y)); +} + +[[ patch(quad, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant UBO& v_31 [[buffer(0)]], texture2d uHeightmapDisplacement [[texture(0)]], sampler uHeightmapDisplacementSmplr [[sampler(0)]], float2 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + float2 tess_coord = float3(gl_TessCoord, 0).xy; + float2 param = tess_coord; + float2 pos = lerp_vertex(param, patchIn.vOutPatchPosBase, v_31); + float2 param_1 = tess_coord; + float2 lod = lod_factor(param_1, patchIn.vPatchLods); + float2 tex = pos * v_31.uInvHeightmapSize; + pos *= v_31.uScale.xy; + float delta_mod = exp2(lod.x); + float2 off = v_31.uInvHeightmapSize * delta_mod; + out.vGradNormalTex = float4(tex + (v_31.uInvHeightmapSize * 0.5), tex * v_31.uScale.zw); + float2 param_2 = tex; + float2 param_3 = off; + float2 param_4 = lod; + float3 height_displacement = sample_height_displacement(param_2, param_3, param_4, uHeightmapDisplacement, uHeightmapDisplacementSmplr); + pos += height_displacement.yz; + out.vWorld = float3(pos.x, height_displacement.x, pos.y); + out.gl_Position = v_31.uMVP * float4(out.vWorld, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/basic.capture.vert b/third_party/spirv-cross/reference/shaders-msl/vert/basic.capture.vert new file mode 100644 index 0000000..a4177d3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/basic.capture.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex void main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]], uint gl_BaseVertex [[base_vertex]], uint gl_InstanceIndex [[instance_id]], uint gl_BaseInstance [[base_instance]], device main0_out* spvOut [[buffer(28)]], device uint* spvIndirectParams [[buffer(29)]]) +{ + device main0_out& out = spvOut[(gl_InstanceIndex - gl_BaseInstance) * spvIndirectParams[0] + gl_VertexIndex - gl_BaseVertex]; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/basic.for-tess.vert b/third_party/spirv-cross/reference/shaders-msl/vert/basic.for-tess.vert new file mode 100644 index 0000000..c99a95a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/basic.for-tess.vert @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal; + float4 gl_Position; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvStageInputSize [[grid_size]], device main0_out* spvOut [[buffer(28)]]) +{ + device main0_out& out = spvOut[gl_GlobalInvocationID.y * spvStageInputSize.x + gl_GlobalInvocationID.x]; + if (any(gl_GlobalInvocationID >= spvStageInputSize)) + return; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/basic.vert b/third_party/spirv-cross/reference/shaders-msl/vert/basic.vert new file mode 100644 index 0000000..ffb4357 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/basic.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _16.uMVP * in.aVertex; + out.vNormal = in.aNormal; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/clip-distance-block.no-user-varying.vert b/third_party/spirv-cross/reference/shaders-msl/vert/clip-distance-block.no-user-varying.vert new file mode 100644 index 0000000..c78105e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/clip-distance-block.no-user-varying.vert @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.Position; + out.gl_ClipDistance[0] = in.Position.x; + out.gl_ClipDistance[1] = in.Position.y; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/clip-distance-block.vert b/third_party/spirv-cross/reference/shaders-msl/vert/clip-distance-block.vert new file mode 100644 index 0000000..af58f35 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/clip-distance-block.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_ClipDistance [[clip_distance]] [2]; + float gl_ClipDistance_0 [[user(clip0)]]; + float gl_ClipDistance_1 [[user(clip1)]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = in.Position; + out.gl_ClipDistance[0] = in.Position.x; + out.gl_ClipDistance[1] = in.Position.y; + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + out.gl_ClipDistance_1 = out.gl_ClipDistance[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/copy.flatten.vert b/third_party/spirv-cross/reference/shaders-msl/vert/copy.flatten.vert new file mode 100644 index 0000000..a762f7e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/copy.flatten.vert @@ -0,0 +1,54 @@ +#include +#include + +using namespace metal; + +struct Light +{ + packed_float3 Position; + float Radius; + float4 Color; +}; + +struct UBO +{ + float4x4 uMVP; + Light lights[4]; +}; + +struct Light_1 +{ + float3 Position; + float Radius; + float4 Color; +}; + +struct main0_out +{ + float4 vColor [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _21 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _21.uMVP * in.aVertex; + out.vColor = float4(0.0); + Light_1 light; + for (int i = 0; i < 4; i++) + { + light.Position = float3(_21.lights[i].Position); + light.Radius = _21.lights[i].Radius; + light.Color = _21.lights[i].Color; + float3 L = in.aVertex.xyz - light.Position; + out.vColor += ((_21.lights[i].Color * fast::clamp(1.0 - (length(L) / light.Radius), 0.0, 1.0)) * dot(in.aNormal, normalize(L))); + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/dynamic.flatten.vert b/third_party/spirv-cross/reference/shaders-msl/vert/dynamic.flatten.vert new file mode 100644 index 0000000..c285f3c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/dynamic.flatten.vert @@ -0,0 +1,43 @@ +#include +#include + +using namespace metal; + +struct Light +{ + packed_float3 Position; + float Radius; + float4 Color; +}; + +struct UBO +{ + float4x4 uMVP; + Light lights[4]; +}; + +struct main0_out +{ + float4 vColor [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _21 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _21.uMVP * in.aVertex; + out.vColor = float4(0.0); + for (int i = 0; i < 4; i++) + { + float3 L = in.aVertex.xyz - float3(_21.lights[i].Position); + out.vColor += ((_21.lights[i].Color * fast::clamp(1.0 - (length(L) / _21.lights[i].Radius), 0.0, 1.0)) * dot(in.aNormal, normalize(L))); + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/float-math.invariant-float-math.vert b/third_party/spirv-cross/reference/shaders-msl/vert/float-math.invariant-float-math.vert new file mode 100644 index 0000000..d603884 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/float-math.invariant-float-math.vert @@ -0,0 +1,136 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Matrices +{ + float4x4 vpMatrix; + float4x4 wMatrix; + float4x3 wMatrix4x3; + float3x4 wMatrix3x4; +}; + +struct main0_out +{ + float3 OutNormal [[user(locn0)]]; + float4 OutWorldPos_0 [[user(locn1)]]; + float4 OutWorldPos_1 [[user(locn2)]]; + float4 OutWorldPos_2 [[user(locn3)]]; + float4 OutWorldPos_3 [[user(locn4)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 InPos [[attribute(0)]]; + float3 InNormal [[attribute(1)]]; +}; + +template +T spvFMul(T l, T r) +{ + return fma(l, r, T(0)); +} + +template +vec spvFMulVectorMatrix(vec v, matrix m) +{ + vec res = vec(0); + for (uint i = Rows; i > 0; --i) + { + vec tmp(0); + for (uint j = 0; j < Cols; ++j) + { + tmp[j] = m[j][i - 1]; + } + res = fma(tmp, vec(v[i - 1]), res); + } + return res; +} + +template +vec spvFMulMatrixVector(matrix m, vec v) +{ + vec res = vec(0); + for (uint i = Cols; i > 0; --i) + { + res = fma(m[i - 1], vec(v[i - 1]), res); + } + return res; +} + +template +matrix spvFMulMatrixMatrix(matrix l, matrix r) +{ + matrix res; + for (uint i = 0; i < RCols; i++) + { + vec tmp(0); + for (uint j = 0; j < LCols; j++) + { + tmp = fma(vec(r[i][j]), l[j], tmp); + } + res[i] = tmp; + } + return res; +} + +vertex main0_out main0(main0_in in [[stage_in]], constant Matrices& _22 [[buffer(0)]]) +{ + main0_out out = {}; + spvUnsafeArray OutWorldPos = {}; + out.gl_Position = spvFMulMatrixVector(spvFMulMatrixMatrix(_22.vpMatrix, _22.wMatrix), float4(in.InPos, 1.0)); + OutWorldPos[0] = spvFMulMatrixVector(_22.wMatrix, float4(in.InPos, 1.0)); + OutWorldPos[1] = spvFMulVectorMatrix(float4(in.InPos, 1.0), _22.wMatrix); + OutWorldPos[2] = spvFMulMatrixVector(_22.wMatrix3x4, in.InPos); + OutWorldPos[3] = spvFMulVectorMatrix(in.InPos, _22.wMatrix4x3); + out.OutNormal = spvFMulMatrixVector(_22.wMatrix, float4(in.InNormal, 0.0)).xyz; + out.OutWorldPos_0 = OutWorldPos[0]; + out.OutWorldPos_1 = OutWorldPos[1]; + out.OutWorldPos_2 = OutWorldPos[2]; + out.OutWorldPos_3 = OutWorldPos[3]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/float-math.vert b/third_party/spirv-cross/reference/shaders-msl/vert/float-math.vert new file mode 100644 index 0000000..e96fdae --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/float-math.vert @@ -0,0 +1,87 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Matrices +{ + float4x4 vpMatrix; + float4x4 wMatrix; + float4x3 wMatrix4x3; + float3x4 wMatrix3x4; +}; + +struct main0_out +{ + float3 OutNormal [[user(locn0)]]; + float4 OutWorldPos_0 [[user(locn1)]]; + float4 OutWorldPos_1 [[user(locn2)]]; + float4 OutWorldPos_2 [[user(locn3)]]; + float4 OutWorldPos_3 [[user(locn4)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 InPos [[attribute(0)]]; + float3 InNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant Matrices& _22 [[buffer(0)]]) +{ + main0_out out = {}; + spvUnsafeArray OutWorldPos = {}; + out.gl_Position = (_22.vpMatrix * _22.wMatrix) * float4(in.InPos, 1.0); + OutWorldPos[0] = _22.wMatrix * float4(in.InPos, 1.0); + OutWorldPos[1] = float4(in.InPos, 1.0) * _22.wMatrix; + OutWorldPos[2] = _22.wMatrix3x4 * in.InPos; + OutWorldPos[3] = in.InPos * _22.wMatrix4x3; + out.OutNormal = (_22.wMatrix * float4(in.InNormal, 0.0)).xyz; + out.OutWorldPos_0 = OutWorldPos[0]; + out.OutWorldPos_1 = OutWorldPos[1]; + out.OutWorldPos_2 = OutWorldPos[2]; + out.OutWorldPos_3 = OutWorldPos[3]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/functions.vert b/third_party/spirv-cross/reference/shaders-msl/vert/functions.vert new file mode 100644 index 0000000..73eaa8b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/functions.vert @@ -0,0 +1,122 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; + float3 rotDeg; + float3 rotRad; + int2 bits; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float3 vRotDeg [[user(locn1)]]; + float3 vRotRad [[user(locn2)]]; + int2 vLSB [[user(locn3)]]; + int2 vMSB [[user(locn4)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +// Implementation of the GLSL radians() function +template +inline T radians(T d) +{ + return d * T(0.01745329251); +} + +// Implementation of the GLSL degrees() function +template +inline T degrees(T r) +{ + return r * T(57.2957795131); +} + +// Implementation of the GLSL findLSB() function +template +inline T spvFindLSB(T x) +{ + return select(ctz(x), T(-1), x == T(0)); +} + +// Implementation of the signed GLSL findMSB() function +template +inline T spvFindSMSB(T x) +{ + T v = select(x, T(-1) - x, x < T(0)); + return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0)); +} + +// Returns the determinant of a 2x2 matrix. +static inline __attribute__((always_inline)) +float spvDet2x2(float a1, float a2, float b1, float b2) +{ + return a1 * b2 - b1 * a2; +} + +// Returns the determinant of a 3x3 matrix. +static inline __attribute__((always_inline)) +float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3) +{ + return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * spvDet2x2(a2, a3, b2, b3); +} + +// Returns the inverse of a matrix, by using the algorithm of calculating the classical +// adjoint and dividing by the determinant. The contents of the matrix are changed. +static inline __attribute__((always_inline)) +float4x4 spvInverse4x4(float4x4 m) +{ + float4x4 adj; // The adjoint matrix (inverse after dividing by determinant) + + // Create the transpose of the cofactors, as the classical adjoint of the matrix. + adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], m[3][3]); + adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], m[3][3]); + adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3]); + + adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], m[3][3]); + adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], m[3][3]); + adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3]); + + adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], m[3][3]); + adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], m[3][3]); + adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3]); + + adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], m[3][2]); + adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], m[3][2]); + adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2]); + + // Calculate the determinant as a combination of the cofactors of the first row. + float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] * m[3][0]); + + // Divide the classical adjoint matrix by the determinant. + // If determinant is zero, matrix is not invertable, so leave it unchanged. + return (det != 0.0f) ? (adj * (1.0f / det)) : m; +} + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = spvInverse4x4(_18.uMVP) * in.aVertex; + out.vNormal = in.aNormal; + out.vRotDeg = degrees(_18.rotRad); + out.vRotRad = radians(_18.rotDeg); + out.vLSB = spvFindLSB(_18.bits); + out.vMSB = spvFindSMSB(_18.bits); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/in_out_array_mat.vert b/third_party/spirv-cross/reference/shaders-msl/vert/in_out_array_mat.vert new file mode 100644 index 0000000..4fd1365 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/in_out_array_mat.vert @@ -0,0 +1,119 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct UBO +{ + float4x4 projection; + float4x4 model; + float lodBias; +}; + +struct main0_out +{ + float3 outPos [[user(locn0)]]; + float3 outNormal [[user(locn1)]]; + float4 outTransModel_0 [[user(locn2)]]; + float4 outTransModel_1 [[user(locn3)]]; + float4 outTransModel_2 [[user(locn4)]]; + float4 outTransModel_3 [[user(locn5)]]; + float outLodBias [[user(locn6)]]; + float4 color [[user(locn7)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 inPos [[attribute(0)]]; + float4 colors_0 [[attribute(1)]]; + float4 colors_1 [[attribute(2)]]; + float4 colors_2 [[attribute(3)]]; + float3 inNormal [[attribute(4)]]; + float4 inViewMat_0 [[attribute(5)]]; + float4 inViewMat_1 [[attribute(6)]]; + float4 inViewMat_2 [[attribute(7)]]; + float4 inViewMat_3 [[attribute(8)]]; +}; + +static inline __attribute__((always_inline)) +void write_deeper_in_function(thread float4x4& outTransModel, constant UBO& ubo, thread float4& color, thread spvUnsafeArray (&colors)) +{ + outTransModel[1].y = ubo.lodBias; + color = colors[2]; +} + +static inline __attribute__((always_inline)) +void write_in_function(thread float4x4& outTransModel, constant UBO& ubo, thread float4& color, thread spvUnsafeArray (&colors), thread float3& inNormal) +{ + outTransModel[2] = float4(inNormal, 1.0); + write_deeper_in_function(outTransModel, ubo, color, colors); +} + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& ubo [[buffer(0)]]) +{ + main0_out out = {}; + float4x4 outTransModel = {}; + spvUnsafeArray colors = {}; + float4x4 inViewMat = {}; + colors[0] = in.colors_0; + colors[1] = in.colors_1; + colors[2] = in.colors_2; + inViewMat[0] = in.inViewMat_0; + inViewMat[1] = in.inViewMat_1; + inViewMat[2] = in.inViewMat_2; + inViewMat[3] = in.inViewMat_3; + out.gl_Position = (ubo.projection * ubo.model) * float4(in.inPos, 1.0); + out.outPos = float3((ubo.model * float4(in.inPos, 1.0)).xyz); + out.outNormal = float3x3(float3(float3(ubo.model[0].x, ubo.model[0].y, ubo.model[0].z)), float3(float3(ubo.model[1].x, ubo.model[1].y, ubo.model[1].z)), float3(float3(ubo.model[2].x, ubo.model[2].y, ubo.model[2].z))) * in.inNormal; + out.outLodBias = ubo.lodBias; + outTransModel = transpose(ubo.model) * inViewMat; + write_in_function(outTransModel, ubo, out.color, colors, in.inNormal); + out.outTransModel_0 = outTransModel[0]; + out.outTransModel_1 = outTransModel[1]; + out.outTransModel_2 = outTransModel[2]; + out.outTransModel_3 = outTransModel[3]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/interface-block-block-composites.frag b/third_party/spirv-cross/reference/shaders-msl/vert/interface-block-block-composites.frag new file mode 100644 index 0000000..c160ade --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/interface-block-block-composites.frag @@ -0,0 +1,97 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Vert +{ + float3x3 wMatrix; + float4 wTmp; + spvUnsafeArray arr; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float3 vMatrix_0 [[user(locn0)]]; + float3 vMatrix_1 [[user(locn1)]]; + float3 vMatrix_2 [[user(locn2)]]; + float3 Vert_wMatrix_0 [[user(locn4)]]; + float3 Vert_wMatrix_1 [[user(locn5)]]; + float3 Vert_wMatrix_2 [[user(locn6)]]; + float4 Vert_wTmp [[user(locn7)]]; + float Vert_arr_0 [[user(locn8)]]; + float Vert_arr_1 [[user(locn9)]]; + float Vert_arr_2 [[user(locn10)]]; + float Vert_arr_3 [[user(locn11)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Vert _17 = {}; + float3x3 vMatrix = {}; + _17.wMatrix[0] = in.Vert_wMatrix_0; + _17.wMatrix[1] = in.Vert_wMatrix_1; + _17.wMatrix[2] = in.Vert_wMatrix_2; + _17.wTmp = in.Vert_wTmp; + _17.arr[0] = in.Vert_arr_0; + _17.arr[1] = in.Vert_arr_1; + _17.arr[2] = in.Vert_arr_2; + _17.arr[3] = in.Vert_arr_3; + vMatrix[0] = in.vMatrix_0; + vMatrix[1] = in.vMatrix_1; + vMatrix[2] = in.vMatrix_2; + out.FragColor = (_17.wMatrix[0].xxyy + _17.wTmp) + vMatrix[1].yyzz; + for (int i = 0; i < 4; i++) + { + out.FragColor += float4(_17.arr[i]); + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/interface-block-block-composites.vert b/third_party/spirv-cross/reference/shaders-msl/vert/interface-block-block-composites.vert new file mode 100644 index 0000000..69f271a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/interface-block-block-composites.vert @@ -0,0 +1,105 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct Vert +{ + spvUnsafeArray arr; + float3x3 wMatrix; + float4 wTmp; +}; + +struct main0_out +{ + float3 vMatrix_0 [[user(locn0)]]; + float3 vMatrix_1 [[user(locn1)]]; + float3 vMatrix_2 [[user(locn2)]]; + float Vert_arr_0 [[user(locn4)]]; + float Vert_arr_1 [[user(locn5)]]; + float Vert_arr_2 [[user(locn6)]]; + float3 Vert_wMatrix_0 [[user(locn7)]]; + float3 Vert_wMatrix_1 [[user(locn8)]]; + float3 Vert_wMatrix_2 [[user(locn9)]]; + float4 Vert_wTmp [[user(locn10)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 Matrix_0 [[attribute(0)]]; + float3 Matrix_1 [[attribute(1)]]; + float3 Matrix_2 [[attribute(2)]]; + float4 Pos [[attribute(4)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float3x3 vMatrix = {}; + Vert _20 = {}; + float3x3 Matrix = {}; + Matrix[0] = in.Matrix_0; + Matrix[1] = in.Matrix_1; + Matrix[2] = in.Matrix_2; + vMatrix = Matrix; + _20.wMatrix = Matrix; + _20.arr[0] = 1.0; + _20.arr[1] = 2.0; + _20.arr[2] = 3.0; + _20.wTmp = in.Pos; + out.gl_Position = in.Pos; + out.vMatrix_0 = vMatrix[0]; + out.vMatrix_1 = vMatrix[1]; + out.vMatrix_2 = vMatrix[2]; + out.Vert_arr_0 = _20.arr[0]; + out.Vert_arr_1 = _20.arr[1]; + out.Vert_arr_2 = _20.arr[2]; + out.Vert_wMatrix_0 = _20.wMatrix[0]; + out.Vert_wMatrix_1 = _20.wMatrix[1]; + out.Vert_wMatrix_2 = _20.wMatrix[2]; + out.Vert_wTmp = _20.wTmp; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/interpolation-qualifiers-block.vert b/third_party/spirv-cross/reference/shaders-msl/vert/interpolation-qualifiers-block.vert new file mode 100644 index 0000000..4206623 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/interpolation-qualifiers-block.vert @@ -0,0 +1,55 @@ +#include +#include + +using namespace metal; + +struct Output +{ + float2 v0; + float2 v1; + float3 v2; + float4 v3; + float v4; + float v5; + float v6; +}; + +struct main0_out +{ + float2 Output_v0 [[user(locn0)]]; + float2 Output_v1 [[user(locn1)]]; + float3 Output_v2 [[user(locn2)]]; + float4 Output_v3 [[user(locn3)]]; + float Output_v4 [[user(locn4)]]; + float Output_v5 [[user(locn5)]]; + float Output_v6 [[user(locn6)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + Output outp = {}; + outp.v0 = in.Position.xy; + outp.v1 = in.Position.zw; + outp.v2 = float3(in.Position.x, in.Position.z * in.Position.y, in.Position.x); + outp.v3 = in.Position.xxyy; + outp.v4 = in.Position.w; + outp.v5 = in.Position.y; + outp.v6 = in.Position.x * in.Position.w; + out.gl_Position = in.Position; + out.Output_v0 = outp.v0; + out.Output_v1 = outp.v1; + out.Output_v2 = outp.v2; + out.Output_v3 = outp.v3; + out.Output_v4 = outp.v4; + out.Output_v5 = outp.v5; + out.Output_v6 = outp.v6; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/interpolation-qualifiers.vert b/third_party/spirv-cross/reference/shaders-msl/vert/interpolation-qualifiers.vert new file mode 100644 index 0000000..ba2c4fb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/interpolation-qualifiers.vert @@ -0,0 +1,36 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float2 v0 [[user(locn0)]]; + float2 v1 [[user(locn1)]]; + float3 v2 [[user(locn2)]]; + float4 v3 [[user(locn3)]]; + float v4 [[user(locn4)]]; + float v5 [[user(locn5)]]; + float v6 [[user(locn6)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.v0 = in.Position.xy; + out.v1 = in.Position.zw; + out.v2 = float3(in.Position.x, in.Position.z * in.Position.y, in.Position.x); + out.v3 = in.Position.xxyy; + out.v4 = in.Position.w; + out.v5 = in.Position.y; + out.v6 = in.Position.x * in.Position.w; + out.gl_Position = in.Position; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/invariant.msl21.vert b/third_party/spirv-cross/reference/shaders-msl/vert/invariant.msl21.vert new file mode 100644 index 0000000..73b0ec7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/invariant.msl21.vert @@ -0,0 +1,26 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position, invariant]]; +}; + +struct main0_in +{ + float4 vInput0 [[attribute(0)]]; + float4 vInput1 [[attribute(1)]]; + float4 vInput2 [[attribute(2)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 _20 = in.vInput1 * in.vInput2; + float4 _21 = in.vInput0 + _20; + out.gl_Position = _21; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/leaf-function.capture.vert b/third_party/spirv-cross/reference/shaders-msl/vert/leaf-function.capture.vert new file mode 100644 index 0000000..6519e56 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/leaf-function.capture.vert @@ -0,0 +1,37 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +static inline __attribute__((always_inline)) +void set_output(device float4& gl_Position, constant UBO& v_18, thread float4& aVertex, device float3& vNormal, thread float3& aNormal) +{ + gl_Position = v_18.uMVP * aVertex; + vNormal = aNormal; +} + +vertex void main0(main0_in in [[stage_in]], constant UBO& v_18 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]], uint gl_BaseVertex [[base_vertex]], uint gl_InstanceIndex [[instance_id]], uint gl_BaseInstance [[base_instance]], device main0_out* spvOut [[buffer(28)]], device uint* spvIndirectParams [[buffer(29)]]) +{ + device main0_out& out = spvOut[(gl_InstanceIndex - gl_BaseInstance) * spvIndirectParams[0] + gl_VertexIndex - gl_BaseVertex]; + set_output(out.gl_Position, v_18, in.aVertex, out.vNormal, in.aNormal); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/leaf-function.for-tess.vert b/third_party/spirv-cross/reference/shaders-msl/vert/leaf-function.for-tess.vert new file mode 100644 index 0000000..5a960e5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/leaf-function.for-tess.vert @@ -0,0 +1,39 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; +}; + +struct main0_out +{ + float3 vNormal; + float4 gl_Position; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +static inline __attribute__((always_inline)) +void set_output(device float4& gl_Position, constant UBO& v_18, thread float4& aVertex, device float3& vNormal, thread float3& aNormal) +{ + gl_Position = v_18.uMVP * aVertex; + vNormal = aNormal; +} + +kernel void main0(main0_in in [[stage_in]], constant UBO& v_18 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvStageInputSize [[grid_size]], device main0_out* spvOut [[buffer(28)]]) +{ + device main0_out& out = spvOut[gl_GlobalInvocationID.y * spvStageInputSize.x + gl_GlobalInvocationID.x]; + if (any(gl_GlobalInvocationID >= spvStageInputSize)) + return; + set_output(out.gl_Position, v_18, in.aVertex, out.vNormal, in.aNormal); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/no-disable-vertex-out.frag-output.vert b/third_party/spirv-cross/reference/shaders-msl/vert/no-disable-vertex-out.frag-output.vert new file mode 100644 index 0000000..14cc949 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/no-disable-vertex-out.frag-output.vert @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct buf +{ + float4x4 MVP; + float4 position[36]; + float4 attr[36]; +}; + +struct main0_out +{ + float4 texcoord [[user(locn0)]]; + float3 frag_pos [[user(locn1)]]; + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(constant buf& ubuf [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.texcoord = ubuf.attr[int(gl_VertexIndex)]; + out.gl_Position = ubuf.MVP * ubuf.position[int(gl_VertexIndex)]; + out.frag_pos = out.gl_Position.xyz; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.for-tess.vert b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.for-tess.vert new file mode 100644 index 0000000..984e832 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.for-tess.vert @@ -0,0 +1,23 @@ +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_10_12 +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_m0[1024]; +}; + +struct main0_in +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_19 [[attribute(0)]]; +}; + +kernel void main0(main0_in in [[stage_in]], device _RESERVED_IDENTIFIER_FIXUP_10_12& _RESERVED_IDENTIFIER_FIXUP_12 [[buffer(0)]], uint3 gl_GlobalInvocationID [[thread_position_in_grid]], uint3 spvStageInputSize [[grid_size]], uint3 spvDispatchBase [[grid_origin]]) +{ + if (any(gl_GlobalInvocationID >= spvStageInputSize)) + return; + uint gl_VertexIndex = gl_GlobalInvocationID.x + spvDispatchBase.x; + _RESERVED_IDENTIFIER_FIXUP_12._RESERVED_IDENTIFIER_FIXUP_m0[int(gl_VertexIndex)] = in._RESERVED_IDENTIFIER_FIXUP_19; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.vert b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.vert new file mode 100644 index 0000000..e804da6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.vert @@ -0,0 +1,20 @@ +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_10_12 +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_m0[1024]; +}; + +struct main0_in +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_19 [[attribute(0)]]; +}; + +vertex void main0(main0_in in [[stage_in]], device _RESERVED_IDENTIFIER_FIXUP_10_12& _RESERVED_IDENTIFIER_FIXUP_12 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + _RESERVED_IDENTIFIER_FIXUP_12._RESERVED_IDENTIFIER_FIXUP_m0[int(gl_VertexIndex)] = in._RESERVED_IDENTIFIER_FIXUP_19; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_buff.vert b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_buff.vert new file mode 100644 index 0000000..fb8060f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_buff.vert @@ -0,0 +1,35 @@ +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_33_35 +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_m0[1024]; +}; + +struct _RESERVED_IDENTIFIER_FIXUP_38_40 +{ + uint4 _RESERVED_IDENTIFIER_FIXUP_m0[1024]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 _RESERVED_IDENTIFIER_FIXUP_14 [[attribute(0)]]; +}; + +vertex void main0(main0_in in [[stage_in]], device _RESERVED_IDENTIFIER_FIXUP_33_35& _RESERVED_IDENTIFIER_FIXUP_35 [[buffer(0)]], constant _RESERVED_IDENTIFIER_FIXUP_38_40& _RESERVED_IDENTIFIER_FIXUP_40 [[buffer(1)]]) +{ + main0_out out = {}; + out.gl_Position = in._RESERVED_IDENTIFIER_FIXUP_14; + for (int _RESERVED_IDENTIFIER_FIXUP_19 = 0; _RESERVED_IDENTIFIER_FIXUP_19 < 1024; _RESERVED_IDENTIFIER_FIXUP_19++) + { + _RESERVED_IDENTIFIER_FIXUP_35._RESERVED_IDENTIFIER_FIXUP_m0[_RESERVED_IDENTIFIER_FIXUP_19] = _RESERVED_IDENTIFIER_FIXUP_40._RESERVED_IDENTIFIER_FIXUP_m0[_RESERVED_IDENTIFIER_FIXUP_19]; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_buff_atomic.vert b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_buff_atomic.vert new file mode 100644 index 0000000..68c649e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_buff_atomic.vert @@ -0,0 +1,31 @@ +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_19_21 +{ + uint _RESERVED_IDENTIFIER_FIXUP_m0; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 _RESERVED_IDENTIFIER_FIXUP_14 [[attribute(0)]]; +}; + +vertex void main0(main0_in in [[stage_in]], volatile device _RESERVED_IDENTIFIER_FIXUP_19_21& _RESERVED_IDENTIFIER_FIXUP_21 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = in._RESERVED_IDENTIFIER_FIXUP_14; + uint _29 = atomic_fetch_add_explicit((volatile device atomic_uint*)&_RESERVED_IDENTIFIER_FIXUP_21._RESERVED_IDENTIFIER_FIXUP_m0, 1u, memory_order_relaxed); + uint _RESERVED_IDENTIFIER_FIXUP_26 = _29; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_tex.vert b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_tex.vert new file mode 100644 index 0000000..dfe1c32 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/no_stage_out.write_tex.vert @@ -0,0 +1,25 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 _RESERVED_IDENTIFIER_FIXUP_14 [[attribute(0)]]; +}; + +vertex void main0(main0_in in [[stage_in]], texture1d _RESERVED_IDENTIFIER_FIXUP_32 [[texture(0)]], texture1d _RESERVED_IDENTIFIER_FIXUP_35 [[texture(1)]]) +{ + main0_out out = {}; + out.gl_Position = in._RESERVED_IDENTIFIER_FIXUP_14; + for (int _RESERVED_IDENTIFIER_FIXUP_19 = 0; _RESERVED_IDENTIFIER_FIXUP_19 < 128; _RESERVED_IDENTIFIER_FIXUP_19++) + { + _RESERVED_IDENTIFIER_FIXUP_32.write(_RESERVED_IDENTIFIER_FIXUP_35.read(uint(_RESERVED_IDENTIFIER_FIXUP_19)), uint(_RESERVED_IDENTIFIER_FIXUP_19)); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/out_block.vert b/third_party/spirv-cross/reference/shaders-msl/vert/out_block.vert new file mode 100644 index 0000000..45b8970 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/out_block.vert @@ -0,0 +1,41 @@ +#include +#include + +using namespace metal; + +struct Transform +{ + float4x4 transform; +}; + +struct VertexOut +{ + float4 color; + float4 color2; +}; + +struct main0_out +{ + float4 VertexOut_color [[user(locn2)]]; + float4 VertexOut_color2 [[user(locn3)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float3 position [[attribute(0)]]; + float4 color [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant Transform& block [[buffer(0)]]) +{ + main0_out out = {}; + VertexOut outputs = {}; + out.gl_Position = block.transform * float4(in.position, 1.0); + outputs.color = in.color; + outputs.color2 = in.color + float4(1.0); + out.VertexOut_color = outputs.color; + out.VertexOut_color2 = outputs.color2; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/packed-bool-to-uint.vert b/third_party/spirv-cross/reference/shaders-msl/vert/packed-bool-to-uint.vert new file mode 100644 index 0000000..6cc5520 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/packed-bool-to-uint.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Struct +{ + uint flags[1]; +}; + +struct defaultUniformsVS +{ + Struct flags; + float4 uquad[4]; + float4x4 umatrix; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant defaultUniformsVS& _24 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.gl_Position = _24.umatrix * float4(_24.uquad[int(gl_VertexIndex)].x, _24.uquad[int(gl_VertexIndex)].y, in.a_position.z, in.a_position.w); + if (_24.flags.flags[0] != 0u) + { + out.gl_Position.z = 0.0; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/packed-bool2-to-packed_uint2.vert b/third_party/spirv-cross/reference/shaders-msl/vert/packed-bool2-to-packed_uint2.vert new file mode 100644 index 0000000..4c46aae --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/packed-bool2-to-packed_uint2.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct Struct +{ + uint2 flags[1]; +}; + +struct defaultUniformsVS +{ + Struct flags; + float4 uquad[4]; + float4x4 umatrix; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant defaultUniformsVS& _25 [[buffer(0)]], uint gl_VertexIndex [[vertex_id]]) +{ + main0_out out = {}; + out.gl_Position = _25.umatrix * float4(_25.uquad[int(gl_VertexIndex)].x, _25.uquad[int(gl_VertexIndex)].y, in.a_position.z, in.a_position.w); + if (_25.flags.flags[0].x != 0u) + { + out.gl_Position.z = 0.0; + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/packed_matrix.vert b/third_party/spirv-cross/reference/shaders-msl/vert/packed_matrix.vert new file mode 100644 index 0000000..9cc416a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/packed_matrix.vert @@ -0,0 +1,55 @@ +#include +#include + +using namespace metal; + +struct _RESERVED_IDENTIFIER_FIXUP_1365_18812 +{ + float3x4 _RESERVED_IDENTIFIER_FIXUP_m0; + float3x4 _RESERVED_IDENTIFIER_FIXUP_m1; +}; + +struct _RESERVED_IDENTIFIER_FIXUP_1126_22044 +{ + float4x4 _RESERVED_IDENTIFIER_FIXUP_m0; + float4x4 _RESERVED_IDENTIFIER_FIXUP_m1; + float _RESERVED_IDENTIFIER_FIXUP_m9; + char _m3_pad[12]; + packed_float3 _RESERVED_IDENTIFIER_FIXUP_m10; + float _RESERVED_IDENTIFIER_FIXUP_m11; + packed_float3 _RESERVED_IDENTIFIER_FIXUP_m12; + float _RESERVED_IDENTIFIER_FIXUP_m17; + float _RESERVED_IDENTIFIER_FIXUP_m18; + float _RESERVED_IDENTIFIER_FIXUP_m19; + float2 _RESERVED_IDENTIFIER_FIXUP_m20; +}; + +struct main0_out +{ + float3 _RESERVED_IDENTIFIER_FIXUP_3976 [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 _RESERVED_IDENTIFIER_FIXUP_5275 [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant _RESERVED_IDENTIFIER_FIXUP_1365_18812& _RESERVED_IDENTIFIER_FIXUP_18812 [[buffer(0)]], constant _RESERVED_IDENTIFIER_FIXUP_1126_22044& _RESERVED_IDENTIFIER_FIXUP_22044 [[buffer(1)]]) +{ + main0_out out = {}; + float3 _RESERVED_IDENTIFIER_FIXUP_2; + float3 _RESERVED_IDENTIFIER_FIXUP_23783; + for (;;) + { + _RESERVED_IDENTIFIER_FIXUP_23783 = normalize(float4(in._RESERVED_IDENTIFIER_FIXUP_5275.xyz, 0.0) * _RESERVED_IDENTIFIER_FIXUP_18812._RESERVED_IDENTIFIER_FIXUP_m1); + break; + } + float4 _RESERVED_IDENTIFIER_FIXUP_14995 = _RESERVED_IDENTIFIER_FIXUP_22044._RESERVED_IDENTIFIER_FIXUP_m0 * float4(float3(_RESERVED_IDENTIFIER_FIXUP_22044._RESERVED_IDENTIFIER_FIXUP_m10) + (in._RESERVED_IDENTIFIER_FIXUP_5275.xyz * (_RESERVED_IDENTIFIER_FIXUP_22044._RESERVED_IDENTIFIER_FIXUP_m17 + _RESERVED_IDENTIFIER_FIXUP_22044._RESERVED_IDENTIFIER_FIXUP_m18)), 1.0); + out._RESERVED_IDENTIFIER_FIXUP_3976 = _RESERVED_IDENTIFIER_FIXUP_23783; + float4 _RESERVED_IDENTIFIER_FIXUP_6282 = _RESERVED_IDENTIFIER_FIXUP_14995; + _RESERVED_IDENTIFIER_FIXUP_6282.y = -_RESERVED_IDENTIFIER_FIXUP_14995.y; + out.gl_Position = _RESERVED_IDENTIFIER_FIXUP_6282; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/pointsize.vert b/third_party/spirv-cross/reference/shaders-msl/vert/pointsize.vert new file mode 100644 index 0000000..8e5782b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/pointsize.vert @@ -0,0 +1,33 @@ +#include +#include + +using namespace metal; + +struct params +{ + float4x4 mvp; + float psize; +}; + +struct main0_out +{ + float4 color [[user(locn0)]]; + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; +}; + +struct main0_in +{ + float4 position [[attribute(0)]]; + float4 color0 [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant params& _19 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _19.mvp * in.position; + out.gl_PointSize = _19.psize; + out.color = in.color0; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/read-from-row-major-array.vert b/third_party/spirv-cross/reference/shaders-msl/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..d6ade7c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/read-from-row-major-array.vert @@ -0,0 +1,63 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct Block +{ + float3x4 var[3][4]; +}; + +struct main0_out +{ + float v_vtxResult [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 a_position [[attribute(0)]]; +}; + +static inline __attribute__((always_inline)) +float compare_float(thread const float& a, thread const float& b) +{ + return float(abs(a - b) < 0.0500000007450580596923828125); +} + +static inline __attribute__((always_inline)) +float compare_vec3(thread const float3& a, thread const float3& b) +{ + float param = a.x; + float param_1 = b.x; + float param_2 = a.y; + float param_3 = b.y; + float param_4 = a.z; + float param_5 = b.z; + return (compare_float(param, param_1) * compare_float(param_2, param_3)) * compare_float(param_4, param_5); +} + +static inline __attribute__((always_inline)) +float compare_mat2x3(thread const float2x3& a, thread const float2x3& b) +{ + float3 param = a[0]; + float3 param_1 = b[0]; + float3 param_2 = a[1]; + float3 param_3 = b[1]; + return compare_vec3(param, param_1) * compare_vec3(param_2, param_3); +} + +vertex main0_out main0(main0_in in [[stage_in]], constant Block& _104 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = in.a_position; + float result = 1.0; + float2x3 param = transpose(float3x2(_104.var[0][0][0].xy, _104.var[0][0][1].xy, _104.var[0][0][2].xy)); + float2x3 param_1 = float2x3(float3(2.0, 6.0, -6.0), float3(0.0, 5.0, 5.0)); + result *= compare_mat2x3(param, param_1); + out.v_vtxResult = result; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/resource-arrays-leaf.ios.vert b/third_party/spirv-cross/reference/shaders-msl/vert/resource-arrays-leaf.ios.vert new file mode 100644 index 0000000..8ab252f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/resource-arrays-leaf.ios.vert @@ -0,0 +1,50 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct storage_block +{ + uint4 baz; + int2 quux; +}; + +struct constant_block +{ + float4 foo; + int bar; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 3 +#endif +constant int arraySize = SPIRV_CROSS_CONSTANT_ID_0; + +static inline __attribute__((always_inline)) +void doWork(device storage_block* (&storage)[2], constant constant_block* (&constants)[4], thread const array, 3> images) +{ + storage[0]->baz = uint4(constants[3]->foo); + storage[1]->quux = images[2].read(uint2(int2(constants[1]->bar))).xy; +} + +vertex void main0(device storage_block* storage_0 [[buffer(0)]], device storage_block* storage_1 [[buffer(1)]], constant constant_block* constants_0 [[buffer(2)]], constant constant_block* constants_1 [[buffer(3)]], constant constant_block* constants_2 [[buffer(4)]], constant constant_block* constants_3 [[buffer(5)]], array, 3> images [[texture(0)]]) +{ + device storage_block* storage[] = + { + storage_0, + storage_1, + }; + + constant constant_block* constants[] = + { + constants_0, + constants_1, + constants_2, + constants_3, + }; + + doWork(storage, constants, images); +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/resource-arrays.ios.vert b/third_party/spirv-cross/reference/shaders-msl/vert/resource-arrays.ios.vert new file mode 100644 index 0000000..91be3a0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/resource-arrays.ios.vert @@ -0,0 +1,42 @@ +#include +#include + +using namespace metal; + +struct storage_block +{ + uint4 baz; + int2 quux; +}; + +struct constant_block +{ + float4 foo; + int bar; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 3 +#endif +constant int arraySize = SPIRV_CROSS_CONSTANT_ID_0; + +vertex void main0(device storage_block* storage_0 [[buffer(0)]], device storage_block* storage_1 [[buffer(1)]], constant constant_block* constants_0 [[buffer(2)]], constant constant_block* constants_1 [[buffer(3)]], constant constant_block* constants_2 [[buffer(4)]], constant constant_block* constants_3 [[buffer(5)]], array, 3> images [[texture(0)]]) +{ + device storage_block* storage[] = + { + storage_0, + storage_1, + }; + + constant constant_block* constants[] = + { + constants_0, + constants_1, + constants_2, + constants_3, + }; + + storage[0]->baz = uint4(constants[3]->foo); + storage[1]->quux = images[2].read(uint2(int2(constants[1]->bar))).xy; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/return-array.force-native-array.vert b/third_party/spirv-cross/reference/shaders-msl/vert/return-array.force-native-array.vert new file mode 100644 index 0000000..a3b5f7c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/return-array.force-native-array.vert @@ -0,0 +1,154 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +constant float4 _20[2] = { float4(10.0), float4(20.0) }; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vInput0 [[attribute(0)]]; + float4 vInput1 [[attribute(1)]]; +}; + +template +inline void spvArrayCopyFromConstantToStack1(thread T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToThreadGroup1(threadgroup T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToStack1(thread T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToThreadGroup1(threadgroup T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToStack1(thread T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToThreadGroup1(threadgroup T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToDevice1(device T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromConstantToDevice1(device T (&dst)[A], constant T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromStackToDevice1(device T (&dst)[A], thread const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromThreadGroupToDevice1(device T (&dst)[A], threadgroup const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToStack1(thread T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +template +inline void spvArrayCopyFromDeviceToThreadGroup1(threadgroup T (&dst)[A], device const T (&src)[A]) +{ + for (uint i = 0; i < A; i++) + { + dst[i] = src[i]; + } +} + +static inline __attribute__((always_inline)) +void test(thread float4 (&SPIRV_Cross_return_value)[2]) +{ + spvArrayCopyFromConstantToStack1(SPIRV_Cross_return_value, _20); +} + +static inline __attribute__((always_inline)) +void test2(thread float4 (&SPIRV_Cross_return_value)[2], thread float4& vInput0, thread float4& vInput1) +{ + float4 foobar[2]; + foobar[0] = vInput0; + foobar[1] = vInput1; + spvArrayCopyFromStackToStack1(SPIRV_Cross_return_value, foobar); +} + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + float4 _42[2]; + test(_42); + float4 _44[2]; + test2(_44, in.vInput0, in.vInput1); + out.gl_Position = _42[0] + _44[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/return-array.vert b/third_party/spirv-cross/reference/shaders-msl/vert/return-array.vert new file mode 100644 index 0000000..dacb0ba --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/return-array.vert @@ -0,0 +1,81 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +constant spvUnsafeArray _20 = spvUnsafeArray({ float4(10.0), float4(20.0) }); + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 vInput0 [[attribute(0)]]; + float4 vInput1 [[attribute(1)]]; +}; + +static inline __attribute__((always_inline)) +spvUnsafeArray test() +{ + return _20; +} + +static inline __attribute__((always_inline)) +spvUnsafeArray test2(thread float4& vInput0, thread float4& vInput1) +{ + spvUnsafeArray foobar; + foobar[0] = vInput0; + foobar[1] = vInput1; + return foobar; +} + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + out.gl_Position = test()[0] + test2(in.vInput0, in.vInput1)[1]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/set_builtin_in_func.vert b/third_party/spirv-cross/reference/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000..91057da --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,27 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; + float gl_PointSize [[point_size]]; +}; + +static inline __attribute__((always_inline)) +void write_outblock(thread float4& gl_Position, thread float& gl_PointSize) +{ + gl_PointSize = 1.0; + gl_Position = float4(gl_PointSize); +} + +vertex main0_out main0() +{ + main0_out out = {}; + write_outblock(out.gl_Position, out.gl_PointSize); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/sign-int-types.vert b/third_party/spirv-cross/reference/shaders-msl/vert/sign-int-types.vert new file mode 100644 index 0000000..a510645 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/sign-int-types.vert @@ -0,0 +1,60 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 uMVP; + float4 uFloatVec4; + float3 uFloatVec3; + float2 uFloatVec2; + float uFloat; + int4 uIntVec4; + int3 uIntVec3; + int2 uIntVec2; + int uInt; +}; + +struct main0_out +{ + float4 vFloatVec4 [[user(locn0)]]; + float3 vFloatVec3 [[user(locn1)]]; + float2 vFloatVec2 [[user(locn2)]]; + float vFloat [[user(locn3)]]; + int4 vIntVec4 [[user(locn4)]]; + int3 vIntVec3 [[user(locn5)]]; + int2 vIntVec2 [[user(locn6)]]; + int vInt [[user(locn7)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; +}; + +// Implementation of the GLSL sign() function for integer types +template::value>::type> +inline T sign(T x) +{ + return select(select(select(x, T(0), x == T(0)), T(1), x > T(0)), T(-1), x < T(0)); +} + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _21 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _21.uMVP * in.aVertex; + out.vFloatVec4 = sign(_21.uFloatVec4); + out.vFloatVec3 = sign(_21.uFloatVec3); + out.vFloatVec2 = sign(_21.uFloatVec2); + out.vFloat = sign(_21.uFloat); + out.vIntVec4 = sign(_21.uIntVec4); + out.vIntVec3 = sign(_21.uIntVec3); + out.vIntVec2 = sign(_21.uIntVec2); + out.vInt = sign(_21.uInt); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/signedness-mismatch.shader-inputs.vert b/third_party/spirv-cross/reference/shaders-msl/vert/signedness-mismatch.shader-inputs.vert new file mode 100644 index 0000000..d6e056b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/signedness-mismatch.shader-inputs.vert @@ -0,0 +1,74 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + ushort2 a [[attribute(0)]]; + uint3 b [[attribute(1)]]; + ushort c_0 [[attribute(2)]]; + ushort c_1 [[attribute(3)]]; + uint4 d_0 [[attribute(4)]]; + uint4 d_1 [[attribute(5)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]]) +{ + main0_out out = {}; + spvUnsafeArray c = {}; + spvUnsafeArray d = {}; + c[0] = in.c_0; + c[1] = in.c_1; + d[0] = in.d_0; + d[1] = in.d_1; + out.gl_Position = float4(float(int(in.a.x)), float(in.b.x), float(uint(c[1])), float(d[0].w)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert b/third_party/spirv-cross/reference/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert new file mode 100644 index 0000000..33c077c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(texture_buffer uSamp [[texture(0)]], texture_buffer uSampo [[texture(1)]]) +{ + main0_out out = {}; + out.gl_Position = uSamp.read(uint(10)) + uSampo.read(uint(100)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/texture_buffer.vert b/third_party/spirv-cross/reference/shaders-msl/vert/texture_buffer.vert new file mode 100644 index 0000000..3b3d92b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/texture_buffer.vert @@ -0,0 +1,26 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +vertex main0_out main0(texture2d uSamp [[texture(0)]], texture2d uSampo [[texture(1)]]) +{ + main0_out out = {}; + out.gl_Position = uSamp.read(spvTexelBufferCoord(10)) + uSampo.read(spvTexelBufferCoord(100)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/ubo.alignment.vert b/third_party/spirv-cross/reference/shaders-msl/vert/ubo.alignment.vert new file mode 100644 index 0000000..c48111e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/ubo.alignment.vert @@ -0,0 +1,38 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 mvp; + float2 targSize; + char _m2_pad[8]; + packed_float3 color; + float opacity; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float3 vColor [[user(locn1)]]; + float2 vSize [[user(locn2)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _18 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _18.mvp * in.aVertex; + out.vNormal = in.aNormal; + out.vColor = float3(_18.color) * _18.opacity; + out.vSize = _18.targSize * _18.opacity; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vert/ubo.vert b/third_party/spirv-cross/reference/shaders-msl/vert/ubo.vert new file mode 100644 index 0000000..86ba1e9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vert/ubo.vert @@ -0,0 +1,30 @@ +#include +#include + +using namespace metal; + +struct UBO +{ + float4x4 mvp; +}; + +struct main0_out +{ + float3 vNormal [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 aVertex [[attribute(0)]]; + float3 aNormal [[attribute(1)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant UBO& _16 [[buffer(0)]]) +{ + main0_out out = {}; + out.gl_Position = _16.mvp * in.aVertex; + out.vNormal = in.aNormal; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag new file mode 100644 index 0000000..f0935f6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag @@ -0,0 +1,73 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex_0 [[user(locn1)]]; + float2 vTex_1 [[user(locn2)]]; + float2 vTex_2 [[user(locn3)]]; + float2 vTex_3 [[user(locn4)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]]) +{ + main0_out out = {}; + spvUnsafeArray vTex = {}; + vTex[0] = in.vTex_0; + vTex[1] = in.vTex_1; + vTex[2] = in.vTex_2; + vTex[3] = in.vTex_3; + const uint gl_ViewIndex = spvViewMask[0]; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, vTex[int(gl_ViewIndex)]); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag new file mode 100644 index 0000000..67895e3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag @@ -0,0 +1,73 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; + float2 vTex_0 [[user(locn1)]]; + float2 vTex_1 [[user(locn2)]]; + float2 vTex_2 [[user(locn3)]]; + float2 vTex_3 [[user(locn4)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], texture2d uTex [[texture(0)]], sampler uTexSmplr [[sampler(0)]], uint gl_ViewIndex [[render_target_array_index]]) +{ + main0_out out = {}; + spvUnsafeArray vTex = {}; + vTex[0] = in.vTex_0; + vTex[1] = in.vTex_1; + vTex[2] = in.vTex_2; + vTex[3] = in.vTex_3; + gl_ViewIndex += spvViewMask[0]; + out.FragColor = in.vColor * uTex.sample(uTexSmplr, vTex[int(gl_ViewIndex)]); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag new file mode 100644 index 0000000..0992a76 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag @@ -0,0 +1,22 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + bool _15 = simd_is_helper_thread(); + discard_fragment(); + if (!_15) + { + out.FragColor = float4(1.0, 0.0, 0.0, 1.0); + } + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag new file mode 100644 index 0000000..4bfd06f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag @@ -0,0 +1,12 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ + discard_fragment(); + bool _9 = simd_is_helper_thread(); + bool helper = _9; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag new file mode 100644 index 0000000..4bfd06f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag @@ -0,0 +1,12 @@ +#include +#include + +using namespace metal; + +fragment void main0() +{ + discard_fragment(); + bool _9 = simd_is_helper_thread(); + bool helper = _9; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/push-constant.vk.frag b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/push-constant.vk.frag new file mode 100644 index 0000000..7b8c502 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/push-constant.vk.frag @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct PushConstants +{ + float4 value0; + float4 value1; +}; + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +struct main0_in +{ + float4 vColor [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant PushConstants& push [[buffer(0)]]) +{ + main0_out out = {}; + out.FragColor = (in.vColor + push.value0) + push.value1; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag new file mode 100644 index 0000000..74fe26e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag @@ -0,0 +1,126 @@ +#include +#include + +using namespace metal; + +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 1.0 +#endif +constant float a = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 2.0 +#endif +constant float b = SPIRV_CROSS_CONSTANT_ID_2; +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 3 +#endif +constant int c = SPIRV_CROSS_CONSTANT_ID_3; +constant uint _18 = (uint(c) + 0u); +constant int _21 = (-c); +constant int _23 = (~c); +#ifndef SPIRV_CROSS_CONSTANT_ID_4 +#define SPIRV_CROSS_CONSTANT_ID_4 4 +#endif +constant int d = SPIRV_CROSS_CONSTANT_ID_4; +constant int _26 = (c + d); +constant int _28 = (c - d); +constant int _30 = (c * d); +constant int _32 = (c / d); +#ifndef SPIRV_CROSS_CONSTANT_ID_5 +#define SPIRV_CROSS_CONSTANT_ID_5 5u +#endif +constant uint e = SPIRV_CROSS_CONSTANT_ID_5; +#ifndef SPIRV_CROSS_CONSTANT_ID_6 +#define SPIRV_CROSS_CONSTANT_ID_6 6u +#endif +constant uint f = SPIRV_CROSS_CONSTANT_ID_6; +constant uint _36 = (e / f); +constant int _38 = (c % d); +constant uint _40 = (e % f); +constant int _42 = (c >> d); +constant uint _44 = (e >> f); +constant int _46 = (c << d); +constant int _48 = (c | d); +constant int _50 = (c ^ d); +constant int _52 = (c & d); +#ifndef SPIRV_CROSS_CONSTANT_ID_7 +#define SPIRV_CROSS_CONSTANT_ID_7 false +#endif +constant bool g = SPIRV_CROSS_CONSTANT_ID_7; +#ifndef SPIRV_CROSS_CONSTANT_ID_8 +#define SPIRV_CROSS_CONSTANT_ID_8 true +#endif +constant bool h = SPIRV_CROSS_CONSTANT_ID_8; +constant bool _58 = (g || h); +constant bool _60 = (g && h); +constant bool _62 = (!g); +constant bool _64 = (g == h); +constant bool _66 = (g != h); +constant bool _68 = (c == d); +constant bool _70 = (c != d); +constant bool _72 = (c < d); +constant bool _74 = (e < f); +constant bool _76 = (c > d); +constant bool _78 = (e > f); +constant bool _80 = (c <= d); +constant bool _82 = (e <= f); +constant bool _84 = (c >= d); +constant bool _86 = (e >= f); +constant int _92 = int(e + 0u); +constant bool _94 = (c != int(0u)); +constant bool _96 = (e != 0u); +constant int _100 = int(g); +constant uint _103 = uint(g); + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + float t0 = a; + float t1 = b; + uint c0 = _18; + int c1 = _21; + int c2 = _23; + int c3 = _26; + int c4 = _28; + int c5 = _30; + int c6 = _32; + uint c7 = _36; + int c8 = _38; + uint c9 = _40; + int c10 = _42; + uint c11 = _44; + int c12 = _46; + int c13 = _48; + int c14 = _50; + int c15 = _52; + bool c16 = _58; + bool c17 = _60; + bool c18 = _62; + bool c19 = _64; + bool c20 = _66; + bool c21 = _68; + bool c22 = _70; + bool c23 = _72; + bool c24 = _74; + bool c25 = _76; + bool c26 = _78; + bool c27 = _80; + bool c28 = _82; + bool c29 = _84; + bool c30 = _86; + int c31 = c8 + c3; + int c32 = _92; + bool c33 = _94; + bool c34 = _96; + int c35 = _100; + uint c36 = _103; + float c37 = float(g); + out.FragColor = float4(t0 + t1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/spec-constant.vk.frag b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/spec-constant.vk.frag new file mode 100644 index 0000000..aa10a50 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/frag/spec-constant.vk.frag @@ -0,0 +1,110 @@ +#include +#include + +using namespace metal; + +constant float a_tmp [[function_constant(1)]]; +constant float a = is_function_constant_defined(a_tmp) ? a_tmp : 1.0; +constant float b_tmp [[function_constant(2)]]; +constant float b = is_function_constant_defined(b_tmp) ? b_tmp : 2.0; +constant int c_tmp [[function_constant(3)]]; +constant int c = is_function_constant_defined(c_tmp) ? c_tmp : 3; +constant uint _18 = (uint(c) + 0u); +constant int _21 = (-c); +constant int _23 = (~c); +constant int d_tmp [[function_constant(4)]]; +constant int d = is_function_constant_defined(d_tmp) ? d_tmp : 4; +constant int _26 = (c + d); +constant int _28 = (c - d); +constant int _30 = (c * d); +constant int _32 = (c / d); +constant uint e_tmp [[function_constant(5)]]; +constant uint e = is_function_constant_defined(e_tmp) ? e_tmp : 5u; +constant uint f_tmp [[function_constant(6)]]; +constant uint f = is_function_constant_defined(f_tmp) ? f_tmp : 6u; +constant uint _36 = (e / f); +constant int _38 = (c % d); +constant uint _40 = (e % f); +constant int _42 = (c >> d); +constant uint _44 = (e >> f); +constant int _46 = (c << d); +constant int _48 = (c | d); +constant int _50 = (c ^ d); +constant int _52 = (c & d); +constant bool g_tmp [[function_constant(7)]]; +constant bool g = is_function_constant_defined(g_tmp) ? g_tmp : false; +constant bool h_tmp [[function_constant(8)]]; +constant bool h = is_function_constant_defined(h_tmp) ? h_tmp : true; +constant bool _58 = (g || h); +constant bool _60 = (g && h); +constant bool _62 = (!g); +constant bool _64 = (g == h); +constant bool _66 = (g != h); +constant bool _68 = (c == d); +constant bool _70 = (c != d); +constant bool _72 = (c < d); +constant bool _74 = (e < f); +constant bool _76 = (c > d); +constant bool _78 = (e > f); +constant bool _80 = (c <= d); +constant bool _82 = (e <= f); +constant bool _84 = (c >= d); +constant bool _86 = (e >= f); +constant int _92 = int(e + 0u); +constant bool _94 = (c != int(0u)); +constant bool _96 = (e != 0u); +constant int _100 = int(g); +constant uint _103 = uint(g); + +struct main0_out +{ + float4 FragColor [[color(0)]]; +}; + +fragment main0_out main0() +{ + main0_out out = {}; + float t0 = a; + float t1 = b; + uint c0 = _18; + int c1 = _21; + int c2 = _23; + int c3 = _26; + int c4 = _28; + int c5 = _30; + int c6 = _32; + uint c7 = _36; + int c8 = _38; + uint c9 = _40; + int c10 = _42; + uint c11 = _44; + int c12 = _46; + int c13 = _48; + int c14 = _50; + int c15 = _52; + bool c16 = _58; + bool c17 = _60; + bool c18 = _62; + bool c19 = _64; + bool c20 = _66; + bool c21 = _68; + bool c22 = _70; + bool c23 = _72; + bool c24 = _74; + bool c25 = _76; + bool c26 = _78; + bool c27 = _80; + bool c28 = _82; + bool c29 = _84; + bool c30 = _86; + int c31 = c8 + c3; + int c32 = _92; + bool c33 = _94; + bool c34 = _96; + int c35 = _100; + uint c36 = _103; + float c37 = float(g); + out.FragColor = float4(t0 + t1); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert new file mode 100644 index 0000000..e36576b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert @@ -0,0 +1,19 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + const int gl_DeviceIndex = 0; + const uint gl_ViewIndex = 0; + out.gl_Position = float4(float(gl_DeviceIndex), float(int(gl_ViewIndex)), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert new file mode 100644 index 0000000..cc4bcc4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert @@ -0,0 +1,18 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0() +{ + main0_out out = {}; + const int gl_DeviceIndex = 0; + out.gl_Position = float4(float(gl_DeviceIndex)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert new file mode 100644 index 0000000..8959afe --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert @@ -0,0 +1,28 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], constant MVPs& _19 [[buffer(0)]]) +{ + main0_out out = {}; + const uint gl_ViewIndex = spvViewMask[0]; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert new file mode 100644 index 0000000..20eff0a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert @@ -0,0 +1,31 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant uint* spvViewMask [[buffer(24)]], constant MVPs& _19 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]], uint gl_BaseInstance [[base_instance]]) +{ + main0_out out = {}; + uint gl_ViewIndex = spvViewMask[0] + (gl_InstanceIndex - gl_BaseInstance) % spvViewMask[1]; + gl_InstanceIndex = (gl_InstanceIndex - gl_BaseInstance) / spvViewMask[1] + gl_BaseInstance; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + out.gl_Layer = gl_ViewIndex - spvViewMask[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert new file mode 100644 index 0000000..5152b62 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert @@ -0,0 +1,29 @@ +#include +#include + +using namespace metal; + +struct MVPs +{ + float4x4 MVP[2]; +}; + +struct main0_out +{ + float4 gl_Position [[position]]; + uint gl_Layer [[render_target_array_index]]; +}; + +struct main0_in +{ + float4 Position [[attribute(0)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant MVPs& _19 [[buffer(0)]], uint gl_InstanceIndex [[instance_id]], uint gl_BaseInstance [[base_instance]]) +{ + main0_out out = {}; + const uint gl_ViewIndex = 0; + out.gl_Position = _19.MVP[int(gl_ViewIndex)] * in.Position; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/small-storage.vk.vert b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/small-storage.vk.vert new file mode 100644 index 0000000..c9ef91b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/small-storage.vk.vert @@ -0,0 +1,48 @@ +#include +#include + +using namespace metal; + +struct block +{ + short2 a; + ushort2 b; + char2 c; + uchar2 d; + half2 e; +}; + +struct storage +{ + short3 f; + ushort3 g; + char3 h; + uchar3 i; + half3 j; +}; + +struct main0_out +{ + short4 p [[user(locn0)]]; + ushort4 q [[user(locn1)]]; + half4 r [[user(locn2)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + short foo [[attribute(0)]]; + ushort bar [[attribute(1)]]; + half baz [[attribute(2)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant block& _26 [[buffer(0)]], const device storage& _53 [[buffer(1)]]) +{ + main0_out out = {}; + out.p = short4((int4(int(in.foo)) + int4(int2(_26.a), int2(_26.c))) - int4(int3(_53.f) / int3(_53.h), 1)); + out.q = ushort4((uint4(uint(in.bar)) + uint4(uint2(_26.b), uint2(_26.d))) - uint4(uint3(_53.g) / uint3(_53.i), 1u)); + out.r = half4((float4(float(in.baz)) + float4(float2(_26.e), 0.0, 1.0)) - float4(float3(_53.j), 1.0)); + out.gl_Position = float4(0.0, 0.0, 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert new file mode 100644 index 0000000..86a0cea --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert @@ -0,0 +1,17 @@ +#include +#include + +using namespace metal; + +struct main0_out +{ + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + out.gl_Position = float4(1.0, 2.0, 3.0, 4.0) * float(int(gl_VertexIndex) + int(gl_InstanceIndex)); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp new file mode 100644 index 0000000..c27bef6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp @@ -0,0 +1,30 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + int values[]; +} _4; + +void main() +{ + int _17 = 0; + for (;;) + { + if (_17 < 100) + { + int _24 = _4.values[_17]; + _4.values[_24] = _17; + int _26 = _24 + 1; + int _18 = _4.values[_26]; + _4.values[_17] = _18; + _17 = _18; + continue; + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp new file mode 100644 index 0000000..0517ec4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp @@ -0,0 +1,27 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + int values[]; +} _4; + +void main() +{ + int _17 = 0; + for (;;) + { + if (_17 < 100) + { + int _24 = _4.values[_17]; + _4.values[_24] = _17; + _17 = _4.values[_24 + 1]; + continue; + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-tracking-function-call-result.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-tracking-function-call-result.asm.comp new file mode 100644 index 0000000..e4dfdb8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/access-tracking-function-call-result.asm.comp @@ -0,0 +1,24 @@ +#version 460 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer Output +{ + int myout; +} _5; + +int foo() +{ + return 12; +} + +void main() +{ + int _17 = foo(); + while (true) + { + _5.myout = _17; + return; + } + _5.myout = _17; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp new file mode 100644 index 0000000..d36f543 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp @@ -0,0 +1,25 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct T +{ + float c; +}; + +layout(binding = 0, std430) buffer SSBO1 +{ + T foo[]; +} _7; + +layout(binding = 1, std140) buffer SSBO2 +{ + T bar[]; +} _10; + +void main() +{ + T v = T(40.0); + _7.foo[10].c = v.c; + _10.bar[30].c = v.c; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp.vk new file mode 100644 index 0000000..5f48072 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp.vk @@ -0,0 +1,46 @@ +#version 450 +#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require +#extension GL_EXT_shader_16bit_storage : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(set = 0, binding = 0, std430) buffer SSBO +{ + int s32; + uint u32; + int16_t s16; + uint16_t u16; + float f32; +} _4; + +void main() +{ + int _29 = _4.s32; + uint _30 = _4.u32; + int16_t _31 = _4.s16; + uint16_t _32 = _4.u16; + float _33 = _4.f32; + _4.s32 = int(_31); + _4.u32 = uint(_31); + _4.s32 = int(int16_t(_32)); + _4.u32 = uint(int16_t(_32)); + _4.u32 = uint(uint16_t(_31)); + _4.u32 = uint(_32); + _4.s16 = int16_t(_29); + _4.u16 = uint16_t(_29); + _4.s16 = int16_t(_30); + _4.u16 = uint16_t(_30); + _4.u16 = uint16_t(_29); + _4.u16 = uint16_t(_30); + _4.f32 = float(_31); + _4.f32 = float(int16_t(_32)); + _4.f32 = float(_29); + _4.f32 = float(int(_30)); + _4.f32 = float(uint16_t(_31)); + _4.f32 = float(_32); + _4.f32 = float(uint(_29)); + _4.f32 = float(_30); + _4.s16 = int16_t(_33); + _4.u16 = uint16_t(int16_t(_33)); + _4.u16 = uint16_t(_33); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/atomic-load-store.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/atomic-load-store.asm.comp new file mode 100644 index 0000000..10a54fc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/atomic-load-store.asm.comp @@ -0,0 +1,16 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + uint a; + uint b; +} _5; + +void main() +{ + uint _20 = atomicAdd(_5.b, 0u); + uint c = _20; + atomicExchange(_5.a, c); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/atomic-result-temporary.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/atomic-result-temporary.asm.comp new file mode 100644 index 0000000..b51c6c5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/atomic-result-temporary.asm.comp @@ -0,0 +1,18 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + uint count; + uint data[]; +} _5; + +void main() +{ + uint _24 = atomicAdd(_5.count, 1u); + if (_24 < 1024u) + { + _5.data[_24] = gl_GlobalInvocationID.x; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp new file mode 100644 index 0000000..66a70f1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp @@ -0,0 +1,24 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + f16vec2 a; + float b; + float c; + f16vec2 d; +} _4; + +void main() +{ + _4.b = uintBitsToFloat(packFloat2x16(_4.a)); + _4.d = unpackFloat2x16(floatBitsToUint(_4.c)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp.vk new file mode 100644 index 0000000..09eccf4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp.vk @@ -0,0 +1,25 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_EXT_shader_explicit_arithmetic_types_float16) +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require +#else +#error No extension available for FP16. +#endif +#extension GL_EXT_shader_16bit_storage : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(set = 0, binding = 0, std430) buffer SSBO +{ + f16vec2 a; + float b; + float c; + f16vec2 d; +} _4; + +void main() +{ + _4.b = uintBitsToFloat(packFloat2x16(_4.a)); + _4.d = unpackFloat2x16(floatBitsToUint(_4.c)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitfield-signed-operations.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitfield-signed-operations.asm.comp new file mode 100644 index 0000000..f535ba7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitfield-signed-operations.asm.comp @@ -0,0 +1,27 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + ivec4 ints; + uvec4 uints; +} _3; + +void main() +{ + ivec4 _19 = _3.ints; + uvec4 _20 = _3.uints; + _3.ints = bitCount(_19); + _3.uints = uvec4(bitCount(_19)); + _3.ints = bitCount(_20); + _3.uints = uvec4(bitCount(_20)); + _3.ints = bitfieldReverse(_19); + _3.uints = bitfieldReverse(_20); + _3.ints = bitfieldExtract(_19, 1, int(11u)); + _3.uints = uvec4(bitfieldExtract(ivec4(_20), int(11u), 1)); + _3.ints = ivec4(bitfieldExtract(uvec4(_19), 1, int(11u))); + _3.uints = bitfieldExtract(_20, int(11u), 1); + _3.ints = bitfieldInsert(_19, _19.wzyx, 1, int(11u)); + _3.uints = bitfieldInsert(_20, _20.wzyx, int(11u), 1); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitscan.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitscan.asm.comp new file mode 100644 index 0000000..31a6234 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/bitscan.asm.comp @@ -0,0 +1,27 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + uvec4 u; + ivec4 i; +} _4; + +void main() +{ + uvec4 _19 = _4.u; + ivec4 _20 = _4.i; + _4.u = uvec4(findLSB(_19)); + _4.i = findLSB(_19); + _4.u = uvec4(findLSB(_20)); + _4.i = findLSB(_20); + _4.u = uvec4(findMSB(_19)); + _4.i = findMSB(_19); + _4.u = uvec4(findMSB(uvec4(_20))); + _4.i = findMSB(uvec4(_20)); + _4.u = uvec4(findMSB(ivec4(_19))); + _4.i = findMSB(ivec4(_19)); + _4.u = uvec4(findMSB(_20)); + _4.i = findMSB(_20); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-atomic-nonuniform.vk.nocompat.asm.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-atomic-nonuniform.vk.nocompat.asm.comp.vk new file mode 100644 index 0000000..d700d61 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-atomic-nonuniform.vk.nocompat.asm.comp.vk @@ -0,0 +1,15 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(set = 0, binding = 0, std430) buffer SSBO +{ + uint v; +} ssbos[]; + +void main() +{ + uint _24 = gl_GlobalInvocationID.z; + uint _25 = atomicAdd(ssbos[nonuniformEXT(_24)].v, 1u); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer-2.asm.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer-2.asm.nocompat.vk.comp.vk new file mode 100644 index 0000000..0288931 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer-2.asm.nocompat.vk.comp.vk @@ -0,0 +1,21 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(buffer_reference) buffer uintPointer +{ + uint value; +}; + +layout(push_constant, std430) uniform _4_12 +{ + uint64_t _m0; +} _12; + +void main() +{ + uintPointer _3 = uintPointer(_12._m0); + _3.value = 20u; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer.asm.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer.asm.nocompat.vk.comp.vk new file mode 100644 index 0000000..9553199 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer.asm.nocompat.vk.comp.vk @@ -0,0 +1,21 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(buffer_reference) buffer uint0_Pointer +{ + uint value[]; +}; + +layout(push_constant, std430) uniform _6_14 +{ + uint64_t _m0; +} _14; + +void main() +{ + uint0_Pointer _5 = uint0_Pointer(_14._m0); + _5.value[10] = 20u; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/constant-composite-undef.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/constant-composite-undef.asm.comp new file mode 100644 index 0000000..279dede --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/constant-composite-undef.asm.comp @@ -0,0 +1,15 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer Block +{ + vec4 f; +} block; + +float _15; + +void main() +{ + block.f = vec4(0.100000001490116119384765625, 0.20000000298023223876953125, 0.300000011920928955078125, 0.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/copy-logical.spv14.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/copy-logical.spv14.asm.comp new file mode 100644 index 0000000..28b2d1d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/copy-logical.spv14.asm.comp @@ -0,0 +1,45 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct B2 +{ + vec4 elem2; +}; + +struct C +{ + vec4 c; + B2 b2; + B2 b2_array[4]; +}; + +struct B1 +{ + vec4 elem1; +}; + +struct A +{ + vec4 a; + B1 b1; + B1 b1_array[4]; +}; + +layout(binding = 0, std430) buffer _8_3 +{ + A a_block; + C c_block; +} _3; + +void main() +{ + A _27; + _27.a = _3.c_block.c; + _27.b1.elem1 = _3.c_block.b2.elem2; + _27.b1_array[0].elem1 = _3.c_block.b2_array[0].elem2; + _27.b1_array[1].elem1 = _3.c_block.b2_array[1].elem2; + _27.b1_array[2].elem1 = _3.c_block.b2_array[2].elem2; + _27.b1_array[3].elem1 = _3.c_block.b2_array[3].elem2; + _3.a_block = _27; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/extended-debug-extinst.invalid.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/extended-debug-extinst.invalid.asm.comp new file mode 100644 index 0000000..7755593 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/extended-debug-extinst.invalid.asm.comp @@ -0,0 +1,18 @@ +#version 430 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _8_9 +{ + float _m0[]; +} _9; + +layout(binding = 1, std430) buffer _8_10 +{ + float _m0[]; +} _10; + +void main() +{ + _10._m0[gl_GlobalInvocationID.x] = -_9._m0[gl_GlobalInvocationID.x]; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/glsl-signed-operations.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/glsl-signed-operations.asm.comp new file mode 100644 index 0000000..35edd4d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/glsl-signed-operations.asm.comp @@ -0,0 +1,47 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + ivec4 ints; + uvec4 uints; +} _4; + +void main() +{ + ivec4 _19 = _4.ints; + uvec4 _20 = _4.uints; + _4.ints = abs(_19); + _4.uints = uvec4(abs(_19)); + _4.ints = abs(ivec4(_20)); + _4.uints = uvec4(abs(ivec4(_20))); + _4.ints = sign(_19); + _4.uints = uvec4(sign(_19)); + _4.ints = sign(ivec4(_20)); + _4.uints = uvec4(sign(ivec4(_20))); + _4.ints = findMSB(ivec4(_20)); + _4.uints = uvec4(findMSB(ivec4(_20))); + _4.ints = findMSB(uvec4(_19)); + _4.uints = uvec4(findMSB(uvec4(_19))); + _4.ints = min(_19, _19); + _4.uints = uvec4(min(_19, ivec4(_20))); + _4.ints = min(ivec4(_20), ivec4(_20)); + _4.uints = uvec4(min(ivec4(_20), _19)); + _4.ints = ivec4(min(uvec4(_19), _20)); + _4.uints = min(uvec4(_19), _20); + _4.ints = ivec4(min(_20, uvec4(_19))); + _4.uints = min(_20, uvec4(_19)); + _4.ints = max(_19, _19); + _4.uints = uvec4(max(_19, _19)); + _4.ints = max(ivec4(_20), _19); + _4.uints = uvec4(max(ivec4(_20), _19)); + _4.ints = ivec4(max(uvec4(_19), _20)); + _4.uints = max(uvec4(_19), uvec4(_19)); + _4.ints = ivec4(max(_20, uvec4(_19))); + _4.uints = max(_20, uvec4(_19)); + _4.ints = clamp(ivec4(_20), ivec4(_20), ivec4(_20)); + _4.uints = uvec4(clamp(ivec4(_20), ivec4(_20), ivec4(_20))); + _4.ints = ivec4(clamp(uvec4(_19), uvec4(_19), uvec4(_19))); + _4.uints = clamp(uvec4(_19), uvec4(_19), uvec4(_19)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp new file mode 100644 index 0000000..dc0956c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp @@ -0,0 +1,33 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct _8 +{ + float _m0; + float _m1; +}; + +struct _15 +{ + float _m0; + int _m1; +}; + +layout(binding = 0, std430) buffer _3_4 +{ + float _m0; + int _m1; +} _4; + +void main() +{ + _8 _23; + _23._m0 = modf(20.0, _23._m1); + _15 _24; + _24._m0 = frexp(40.0, _24._m1); + _4._m0 = _23._m0; + _4._m0 = _23._m1; + _4._m0 = _24._m0; + _4._m1 = _24._m1; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/image-atomic-nonuniform.vk.nocompat.asm.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/image-atomic-nonuniform.vk.nocompat.asm.comp.vk new file mode 100644 index 0000000..c6c1ea3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/image-atomic-nonuniform.vk.nocompat.asm.comp.vk @@ -0,0 +1,12 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(set = 0, binding = 0, r32ui) uniform uimage2D uImage[]; + +void main() +{ + uint _26 = gl_GlobalInvocationID.z; + uint _31 = imageAtomicAdd(uImage[nonuniformEXT(_26)], ivec2(gl_GlobalInvocationID.xy), 1u); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/loop-variable-with-initializer.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/loop-variable-with-initializer.asm.comp new file mode 100644 index 0000000..ca8e58c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/loop-variable-with-initializer.asm.comp @@ -0,0 +1,12 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + uint i = 0u; + for (;;) + { + break; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/nonuniform-bracket-handling.vk.nocompat.asm.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/nonuniform-bracket-handling.vk.nocompat.asm.comp.vk new file mode 100644 index 0000000..1a093c4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/nonuniform-bracket-handling.vk.nocompat.asm.comp.vk @@ -0,0 +1,55 @@ +#version 450 +#extension GL_EXT_buffer_reference : require +#extension GL_EXT_nonuniform_qualifier : require +#extension GL_KHR_shader_subgroup_ballot : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(set = 0, binding = 0, std430) restrict readonly buffer SSBO_Offsets +{ + uvec2 _m0[]; +} _7; + +layout(push_constant, std430) uniform RootConstants +{ + uint _m0; + uint _m1; + uint _m2; + uint _m3; + uint _m4; + uint _m5; + uint _m6; + uint _m7; +} registers; + +layout(set = 1, binding = 0) uniform samplerBuffer _8[]; +layout(set = 4, binding = 0, r32f) uniform imageBuffer _9[]; +layout(set = 4, binding = 0, r32ui) uniform uimageBuffer _10[]; + +void main() +{ + uint _60 = registers._m4 + 2u; + uint _63 = subgroupBroadcastFirst(_60); + uint _70 = subgroupBroadcastFirst(registers._m4); + uint _75 = registers._m1 + 1u; + uint _78 = subgroupBroadcastFirst(_75); + uint _87 = gl_GlobalInvocationID.x + 4u; + uint _98 = gl_GlobalInvocationID.x + 1024u; + imageStore(_9[registers._m4], int((_98 < _7._m0[_70].y) ? (_98 + _7._m0[_70].x) : 4294967295u), vec4(imageLoad(_9[registers._m4], int((_87 < _7._m0[_70].y) ? (_87 + _7._m0[_70].x) : 4294967295u)))); + uint _105 = gl_GlobalInvocationID.x + 2u; + uint _116 = gl_GlobalInvocationID.x + 2048u; + imageStore(_9[registers._m4], int((_116 < _7._m0[_70].y) ? (_116 + _7._m0[_70].x) : 4294967295u), vec4(texelFetch(_8[_75], int((_105 < _7._m0[_78].y) ? (_105 + _7._m0[_78].x) : 4294967295u)))); + uint _129 = imageAtomicAdd(_10[_60], int((gl_GlobalInvocationID.x < _7._m0[_63].y) ? (gl_GlobalInvocationID.x + _7._m0[_63].x) : 4294967295u), 40u); + uint _136 = imageAtomicCompSwap(_10[_60], int((gl_GlobalInvocationID.y < _7._m0[_63].y) ? (gl_GlobalInvocationID.y + _7._m0[_63].x) : 4294967295u), 40u, 50u); + imageStore(_9[registers._m4], int((0u < _7._m0[_70].y) ? (0u + _7._m0[_70].x) : 4294967295u), vec4(float(_7._m0[_70].y))); + imageStore(_9[registers._m4], int((1u < _7._m0[_70].y) ? (1u + _7._m0[_70].x) : 4294967295u), vec4(float(_7._m0[_78].y))); + uint _11 = registers._m4 + (gl_GlobalInvocationID.z + 0u); + imageStore(_9[nonuniformEXT(_11)], int((_98 < _7._m0[_11].y) ? (_98 + _7._m0[_11].x) : 4294967295u), vec4(imageLoad(_9[nonuniformEXT(_11)], int((_87 < _7._m0[_11].y) ? (_87 + _7._m0[_11].x) : 4294967295u)))); + uint _13 = registers._m1 + (gl_GlobalInvocationID.z + 0u); + imageStore(_9[nonuniformEXT(_11)], int((_116 < _7._m0[_11].y) ? (_116 + _7._m0[_11].x) : 4294967295u), vec4(texelFetch(_8[nonuniformEXT(_13)], int((_87 < _7._m0[_13].y) ? (_87 + _7._m0[_13].x) : 4294967295u)))); + uint _15 = registers._m4 + (gl_GlobalInvocationID.z + 0u); + uint _208 = imageAtomicAdd(_10[nonuniformEXT(_15)], int((gl_GlobalInvocationID.y < _7._m0[_15].y) ? (gl_GlobalInvocationID.y + _7._m0[_15].x) : 4294967295u), 40u); + uint _215 = imageAtomicCompSwap(_10[nonuniformEXT(_15)], int((gl_GlobalInvocationID.y < _7._m0[_15].y) ? (gl_GlobalInvocationID.y + _7._m0[_15].x) : 4294967295u), 40u, 70u); + imageStore(_9[registers._m4], int((2u < _7._m0[_70].y) ? (2u + _7._m0[_70].x) : 4294967295u), vec4(float(_7._m0[_11].y))); + imageStore(_9[registers._m4], int((3u < _7._m0[_70].y) ? (3u + _7._m0[_70].x) : 4294967295u), vec4(float(_7._m0[_13].y))); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/phi-temporary-copy-loop-variable.asm.invalid.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/phi-temporary-copy-loop-variable.asm.invalid.comp new file mode 100644 index 0000000..f8650b5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/phi-temporary-copy-loop-variable.asm.invalid.comp @@ -0,0 +1,20 @@ +#version 450 +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(binding = 1, rgba32f) uniform writeonly image2D outImageTexture; + +void main() +{ + int _27_copy; + int _30; + _30 = 7; + for (int _27 = 7; _27 >= 0; _27_copy = _27, _27--, _30 = _27_copy) + { + if (5.0 > float(_27)) + { + break; + } + } + imageStore(outImageTexture, ivec2(gl_GlobalInvocationID.xy), vec4(float(_30 - 1), float(_30), 1.0, 1.0)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp new file mode 100644 index 0000000..c6aa711 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp @@ -0,0 +1,33 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 1 +#endif +const int ConstantInt = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 2u +#endif +const uint ConstantUint = SPIRV_CROSS_CONSTANT_ID_1; +const int64_t ConstantInt64_1 = int64_t(ConstantInt); +const int64_t ConstantInt64_2 = int64_t(int(ConstantUint)); +const uint64_t ConstantUint64_1 = uint64_t(ConstantInt); +const uint64_t ConstantUint64_2 = uint64_t(int(ConstantUint)); +const int64_t _20 = (ConstantInt64_1 + ConstantInt64_2); +const uint64_t _21 = (ConstantUint64_1 + ConstantUint64_2); +const int _22 = int(_20); +const uint _23 = uint(_21); + +layout(binding = 0, std430) buffer SSBO +{ + int s64; + uint u64; +} _4; + +void main() +{ + _4.s64 = _22; + _4.u64 = _23; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp new file mode 100644 index 0000000..482cfd8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp @@ -0,0 +1,28 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 1u +#endif +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 3u +#endif + +layout(local_size_x = SPIRV_CROSS_CONSTANT_ID_0, local_size_y = 2, local_size_z = SPIRV_CROSS_CONSTANT_ID_2) in; + +layout(binding = 0, std430) buffer _6_8 +{ + float _m0[]; +} _8; + +layout(binding = 1, std430) buffer _6_9 +{ + float _m0[]; +} _9; + +uvec3 _22 = gl_WorkGroupSize; + +void main() +{ + _8._m0[gl_WorkGroupID.x] = _9._m0[gl_WorkGroupID.x] + _8._m0[gl_WorkGroupID.x]; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/combined-image-sampler-dxc-min16float.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/combined-image-sampler-dxc-min16float.asm.invalid.frag new file mode 100644 index 0000000..5fa822b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/combined-image-sampler-dxc-min16float.asm.invalid.frag @@ -0,0 +1,28 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct PSInput +{ + highp vec4 color; + highp vec2 uv; +}; + +uniform mediump sampler2D SPIRV_Cross_CombinedtexSamp; + +layout(location = 0) in highp vec4 in_var_COLOR; +layout(location = 1) in highp vec2 in_var_TEXCOORD0; +layout(location = 0) out highp vec4 out_var_SV_TARGET; + +highp vec4 src_PSMain(PSInput _input) +{ + vec4 a = _input.color * texture(SPIRV_Cross_CombinedtexSamp, _input.uv); + return a; +} + +void main() +{ + PSInput param_var_input = PSInput(in_var_COLOR, in_var_TEXCOORD0); + out_var_SV_TARGET = src_PSMain(param_var_input); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/demote-impure-function-call.vk.nocompat.asm.frag.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/demote-impure-function-call.vk.nocompat.asm.frag.vk new file mode 100644 index 0000000..adde5fc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/demote-impure-function-call.vk.nocompat.asm.frag.vk @@ -0,0 +1,22 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +layout(location = 0) flat in int vA; +layout(location = 0) out vec4 FragColor; + +vec4 foobar(int a) +{ + if (a < 0) + { + demote; + } + return vec4(10.0); +} + +void main() +{ + int param = vA; + vec4 _25 = foobar(param); + FragColor = vec4(10.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/discard-impure-function-call.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/discard-impure-function-call.asm.frag new file mode 100644 index 0000000..0fe71f6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/discard-impure-function-call.asm.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) flat in int vA; +layout(location = 0) out vec4 FragColor; + +vec4 foobar(int a) +{ + if (a < 0) + { + discard; + } + return vec4(10.0); +} + +void main() +{ + int param = vA; + vec4 _25 = foobar(param); + FragColor = vec4(10.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/do-while-continue-phi.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/do-while-continue-phi.asm.invalid.frag new file mode 100644 index 0000000..2024c30 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/do-while-continue-phi.asm.invalid.frag @@ -0,0 +1,37 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out highp vec4 _GLF_color; + +void main() +{ + for (;;) + { + bool _32; + for (;;) + { + if (gl_FragCoord.x != gl_FragCoord.x) + { + _32 = true; + break; + } + if (false) + { + continue; + } + else + { + _32 = false; + break; + } + } + if (_32) + { + break; + } + _GLF_color = vec4(1.0, 0.0, 0.0, 1.0); + break; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/do-while-loop-inverted-test.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/do-while-loop-inverted-test.asm.frag new file mode 100644 index 0000000..6758776 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/do-while-loop-inverted-test.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +void main() +{ + int j = 0; + int i = 0; + do + { + j = ((j + i) + 1) * j; + i++; + } while (!(i == 20)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/early-conditional-return-switch.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/early-conditional-return-switch.asm.frag new file mode 100644 index 0000000..34fffb8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/early-conditional-return-switch.asm.frag @@ -0,0 +1,67 @@ +#version 450 + +layout(binding = 0, std140) uniform type_gCBuffarrayIndex +{ + uint gArrayIndex; +} gCBuffarrayIndex; + +uniform sampler2D SPIRV_Cross_Combinedg_textureArray0SPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_Combinedg_textureArray1SPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_Combinedg_textureArray2SPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_Combinedg_textureArray3SPIRV_Cross_DummySampler; + +layout(location = 0) out vec4 out_var_SV_TARGET; + +vec4 _32; + +void main() +{ + vec4 _80; + do + { + vec4 _77; + bool _78; + switch (gCBuffarrayIndex.gArrayIndex) + { + case 0u: + { + _77 = texelFetch(SPIRV_Cross_Combinedg_textureArray0SPIRV_Cross_DummySampler, ivec3(int(gl_FragCoord.x), int(gl_FragCoord.y), 0).xy, 0); + _78 = true; + break; + } + case 1u: + { + _77 = texelFetch(SPIRV_Cross_Combinedg_textureArray1SPIRV_Cross_DummySampler, ivec3(int(gl_FragCoord.x), int(gl_FragCoord.y), 0).xy, 0); + _78 = true; + break; + } + case 2u: + { + _77 = texelFetch(SPIRV_Cross_Combinedg_textureArray2SPIRV_Cross_DummySampler, ivec3(int(gl_FragCoord.x), int(gl_FragCoord.y), 0).xy, 0); + _78 = true; + break; + } + case 3u: + { + _77 = texelFetch(SPIRV_Cross_Combinedg_textureArray3SPIRV_Cross_DummySampler, ivec3(int(gl_FragCoord.x), int(gl_FragCoord.y), 0).xy, 0); + _78 = true; + break; + } + default: + { + _77 = _32; + _78 = false; + break; + } + } + if (_78) + { + _80 = _77; + break; + } + _80 = vec4(0.0, 1.0, 0.0, 1.0); + break; + } while(false); + out_var_SV_TARGET = _80; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/empty-struct-in-struct.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/empty-struct-in-struct.asm.frag new file mode 100644 index 0000000..0d3958b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/empty-struct-in-struct.asm.frag @@ -0,0 +1,31 @@ +#version 450 + +struct EmptyStructTest +{ + int empty_struct_member; +}; + +struct EmptyStruct2Test +{ + EmptyStructTest _m0; +}; + +float GetValue(EmptyStruct2Test self) +{ + return 0.0; +} + +float GetValue_1(EmptyStruct2Test self) +{ + return 0.0; +} + +void main() +{ + EmptyStructTest _25 = EmptyStructTest(0); + EmptyStruct2Test emptyStruct; + float value = GetValue(emptyStruct); + value = GetValue_1(EmptyStruct2Test(_25)); + value = GetValue_1(EmptyStruct2Test(EmptyStructTest(0))); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-inverted.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-inverted.asm.invalid.frag new file mode 100644 index 0000000..16c7dc3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-inverted.asm.invalid.frag @@ -0,0 +1,11 @@ +#version 450 + +void main() +{ + int _13; + for (int _12 = 0; !(_12 == 16); _12 = _13) + { + _13 = _12 + 1; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-non-inverted.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-non-inverted.asm.invalid.frag new file mode 100644 index 0000000..b4c0631 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-non-inverted.asm.invalid.frag @@ -0,0 +1,11 @@ +#version 450 + +void main() +{ + int _13; + for (int _12 = 0; _12 != 16; _12 = _13) + { + _13 = _12 + 1; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-inverted-test.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-inverted-test.asm.frag new file mode 100644 index 0000000..16c7dc3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/for-loop-inverted-test.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +void main() +{ + int _13; + for (int _12 = 0; !(_12 == 16); _12 = _13) + { + _13 = _12 + 1; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/image-fetch-uint-coord.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/image-fetch-uint-coord.asm.frag new file mode 100644 index 0000000..8b8d0c8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/image-fetch-uint-coord.asm.frag @@ -0,0 +1,12 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombinedTexSPIRV_Cross_DummySampler; + +layout(location = 0) flat in uvec3 in_var_TEXCOORD0; +layout(location = 0) out vec4 out_var_SV_Target0; + +void main() +{ + out_var_SV_Target0 = texelFetch(SPIRV_Cross_CombinedTexSPIRV_Cross_DummySampler, ivec2(in_var_TEXCOORD0.xy), int(in_var_TEXCOORD0.z)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/inliner-dominator-inside-loop.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/inliner-dominator-inside-loop.asm.frag new file mode 100644 index 0000000..4049c48 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/inliner-dominator-inside-loop.asm.frag @@ -0,0 +1,230 @@ +#version 450 + +struct VertexOutput +{ + vec4 HPosition; + vec4 Uv_EdgeDistance1; + vec4 UvStuds_EdgeDistance2; + vec4 Color; + vec4 LightPosition_Fog; + vec4 View_Depth; + vec4 Normal_SpecPower; + vec3 Tangent; + vec4 PosLightSpace_Reflectance; + float studIndex; +}; + +struct Surface +{ + vec3 albedo; + vec3 normal; + float specular; + float gloss; + float reflectance; + float opacity; +}; + +struct SurfaceInput +{ + vec4 Color; + vec2 Uv; + vec2 UvStuds; +}; + +struct Globals +{ + mat4 ViewProjection; + vec4 ViewRight; + vec4 ViewUp; + vec4 ViewDir; + vec3 CameraPosition; + vec3 AmbientColor; + vec3 Lamp0Color; + vec3 Lamp0Dir; + vec3 Lamp1Color; + vec4 FogParams; + vec3 FogColor; + vec4 LightBorder; + vec4 LightConfig0; + vec4 LightConfig1; + vec4 LightConfig2; + vec4 LightConfig3; + vec4 RefractionBias_FadeDistance_GlowFactor; + vec4 OutlineBrightness_ShadowInfo; + vec4 ShadowMatrix0; + vec4 ShadowMatrix1; + vec4 ShadowMatrix2; +}; + +struct Params +{ + vec4 LqmatFarTilingFactor; +}; + +layout(binding = 0, std140) uniform CB0 +{ + Globals CB0; +} _19; + +uniform sampler2D SPIRV_Cross_CombinedDiffuseMapTextureDiffuseMapSampler; +uniform sampler2D SPIRV_Cross_CombinedNormalMapTextureNormalMapSampler; +uniform sampler2D SPIRV_Cross_CombinedNormalDetailMapTextureNormalDetailMapSampler; +uniform sampler2D SPIRV_Cross_CombinedStudsMapTextureStudsMapSampler; +uniform sampler2D SPIRV_Cross_CombinedSpecularMapTextureSpecularMapSampler; +uniform sampler3D SPIRV_Cross_CombinedLightMapTextureLightMapSampler; +uniform sampler2D SPIRV_Cross_CombinedShadowMapTextureShadowMapSampler; +uniform samplerCube SPIRV_Cross_CombinedEnvironmentMapTextureEnvironmentMapSampler; + +layout(location = 0) in vec4 IN_Uv_EdgeDistance1; +layout(location = 1) in vec4 IN_UvStuds_EdgeDistance2; +layout(location = 2) in vec4 IN_Color; +layout(location = 3) in vec4 IN_LightPosition_Fog; +layout(location = 4) in vec4 IN_View_Depth; +layout(location = 5) in vec4 IN_Normal_SpecPower; +layout(location = 6) in vec3 IN_Tangent; +layout(location = 7) in vec4 IN_PosLightSpace_Reflectance; +layout(location = 8) in float IN_studIndex; +layout(location = 0) out vec4 _entryPointOutput; + +VertexOutput _121; +SurfaceInput _122; +vec2 _123; +vec4 _124; +Surface _125; +vec4 _192; +vec4 _219; +vec4 _297; + +void main() +{ + VertexOutput _128 = _121; + _128.HPosition = gl_FragCoord; + VertexOutput _130 = _128; + _130.Uv_EdgeDistance1 = IN_Uv_EdgeDistance1; + VertexOutput _132 = _130; + _132.UvStuds_EdgeDistance2 = IN_UvStuds_EdgeDistance2; + VertexOutput _134 = _132; + _134.Color = IN_Color; + VertexOutput _136 = _134; + _136.LightPosition_Fog = IN_LightPosition_Fog; + VertexOutput _138 = _136; + _138.View_Depth = IN_View_Depth; + VertexOutput _140 = _138; + _140.Normal_SpecPower = IN_Normal_SpecPower; + VertexOutput _142 = _140; + _142.Tangent = IN_Tangent; + VertexOutput _144 = _142; + _144.PosLightSpace_Reflectance = IN_PosLightSpace_Reflectance; + VertexOutput _146 = _144; + _146.studIndex = IN_studIndex; + SurfaceInput _147 = _122; + _147.Color = IN_Color; + SurfaceInput _149 = _147; + _149.Uv = IN_Uv_EdgeDistance1.xy; + SurfaceInput _151 = _149; + _151.UvStuds = IN_UvStuds_EdgeDistance2.xy; + SurfaceInput _156 = _151; + _156.UvStuds.y = (fract(_151.UvStuds.y) + IN_studIndex) * 0.25; + float _160 = clamp(1.0 - (_146.View_Depth.w * 0.00333332992158830165863037109375), 0.0, 1.0); + float _163 = _146.View_Depth.w * _19.CB0.RefractionBias_FadeDistance_GlowFactor.y; + float _165 = clamp(1.0 - _163, 0.0, 1.0); + vec2 _166 = IN_Uv_EdgeDistance1.xy * 1.0; + bool _173; + vec4 _193; + for (;;) + { + _173 = 0.0 == 0.0; + if (_173) + { + _193 = texture(SPIRV_Cross_CombinedDiffuseMapTextureDiffuseMapSampler, _166); + break; + } + else + { + float _180 = 1.0 / (1.0 - 0.0); + _193 = mix(texture(SPIRV_Cross_CombinedDiffuseMapTextureDiffuseMapSampler, _166 * 0.25), texture(SPIRV_Cross_CombinedDiffuseMapTextureDiffuseMapSampler, _166), vec4(clamp((_160 * _180) - (0.0 * _180), 0.0, 1.0))); + break; + } + _193 = _192; + break; + } + vec4 _220; + for (;;) + { + if (_173) + { + _220 = texture(SPIRV_Cross_CombinedNormalMapTextureNormalMapSampler, _166); + break; + } + else + { + float _207 = 1.0 / (1.0 - 0.0); + _220 = mix(texture(SPIRV_Cross_CombinedNormalMapTextureNormalMapSampler, _166 * 0.25), texture(SPIRV_Cross_CombinedNormalMapTextureNormalMapSampler, _166), vec4(clamp((_165 * _207) - (0.0 * _207), 0.0, 1.0))); + break; + } + _220 = _219; + break; + } + vec2 _223 = vec2(1.0); + vec2 _224 = (_220.wy * 2.0) - _223; + vec3 _232 = vec3(_224, sqrt(clamp(1.0 + dot(-_224, _224), 0.0, 1.0))); + vec4 _237 = texture(SPIRV_Cross_CombinedNormalDetailMapTextureNormalDetailMapSampler, _166 * 0.0); + vec2 _240 = (_237.wy * 2.0) - _223; + vec2 _252 = _232.xy + (vec3(_240, sqrt(clamp(1.0 + dot(-_240, _240), 0.0, 1.0))).xy * 0.0); + vec3 _253 = vec3(_252.x, _252.y, _232.z); + vec2 _255 = _253.xy * _165; + vec3 _256 = vec3(_255.x, _255.y, _253.z); + vec4 _268 = texture(SPIRV_Cross_CombinedStudsMapTextureStudsMapSampler, _156.UvStuds); + vec3 _271 = ((IN_Color.xyz * (_193 * 1.0).xyz) * (1.0 + (_256.x * 0.300000011920928955078125))) * (_268.x * 2.0); + vec4 _298; + for (;;) + { + if (0.75 == 0.0) + { + _298 = texture(SPIRV_Cross_CombinedSpecularMapTextureSpecularMapSampler, _166); + break; + } + else + { + float _285 = 1.0 / (1.0 - 0.75); + _298 = mix(texture(SPIRV_Cross_CombinedSpecularMapTextureSpecularMapSampler, _166 * 0.25), texture(SPIRV_Cross_CombinedSpecularMapTextureSpecularMapSampler, _166), vec4(clamp((_165 * _285) - (0.75 * _285), 0.0, 1.0))); + break; + } + _298 = _297; + break; + } + vec2 _303 = mix(vec2(0.800000011920928955078125, 120.0), (_298.xy * vec2(2.0, 256.0)) + vec2(0.0, 0.00999999977648258209228515625), vec2(_165)); + Surface _304 = _125; + _304.albedo = _271; + Surface _305 = _304; + _305.normal = _256; + float _306 = _303.x; + Surface _307 = _305; + _307.specular = _306; + float _308 = _303.y; + Surface _309 = _307; + _309.gloss = _308; + float _312 = (_298.xy.y * _165) * 0.0; + Surface _313 = _309; + _313.reflectance = _312; + vec4 _318 = vec4(_271, _146.Color.w); + vec3 _329 = normalize(((IN_Tangent * _313.normal.x) + (cross(IN_Normal_SpecPower.xyz, IN_Tangent) * _313.normal.y)) + (IN_Normal_SpecPower.xyz * _313.normal.z)); + vec3 _332 = -_19.CB0.Lamp0Dir; + float _333 = dot(_329, _332); + float _357 = clamp(dot(step(_19.CB0.LightConfig3.xyz, abs(IN_LightPosition_Fog.xyz - _19.CB0.LightConfig2.xyz)), vec3(1.0)), 0.0, 1.0); + vec4 _368 = mix(texture(SPIRV_Cross_CombinedLightMapTextureLightMapSampler, IN_LightPosition_Fog.xyz.yzx - (IN_LightPosition_Fog.xyz.yzx * _357)), _19.CB0.LightBorder, vec4(_357)); + vec2 _376 = texture(SPIRV_Cross_CombinedShadowMapTextureShadowMapSampler, IN_PosLightSpace_Reflectance.xyz.xy).xy; + float _392 = (1.0 - (((step(_376.x, IN_PosLightSpace_Reflectance.xyz.z) * clamp(9.0 - (20.0 * abs(IN_PosLightSpace_Reflectance.xyz.z - 0.5)), 0.0, 1.0)) * _376.y) * _19.CB0.OutlineBrightness_ShadowInfo.w)) * _368.w; + vec3 _403 = mix(_318.xyz, texture(SPIRV_Cross_CombinedEnvironmentMapTextureEnvironmentMapSampler, reflect(-IN_View_Depth.xyz, _329)).xyz, vec3(_312)); + vec4 _404 = vec4(_403.x, _403.y, _403.z, _318.w); + vec3 _422 = (((_19.CB0.AmbientColor + (((_19.CB0.Lamp0Color * clamp(_333, 0.0, 1.0)) + (_19.CB0.Lamp1Color * max(-_333, 0.0))) * _392)) + _368.xyz) * _404.xyz) + (_19.CB0.Lamp0Color * (((step(0.0, _333) * _306) * _392) * pow(clamp(dot(_329, normalize(_332 + normalize(IN_View_Depth.xyz))), 0.0, 1.0), _308))); + vec4 _425 = vec4(_422.x, _422.y, _422.z, _124.w); + _425.w = _404.w; + vec2 _435 = min(IN_Uv_EdgeDistance1.wz, IN_UvStuds_EdgeDistance2.wz); + float _439 = min(_435.x, _435.y) / _163; + vec3 _445 = _425.xyz * clamp((clamp((_163 * _19.CB0.OutlineBrightness_ShadowInfo.x) + _19.CB0.OutlineBrightness_ShadowInfo.y, 0.0, 1.0) * (1.5 - _439)) + _439, 0.0, 1.0); + vec4 _446 = vec4(_445.x, _445.y, _445.z, _425.w); + vec3 _453 = mix(_19.CB0.FogColor, _446.xyz, vec3(clamp(_146.LightPosition_Fog.w, 0.0, 1.0))); + _entryPointOutput = vec4(_453.x, _453.y, _453.z, _446.w); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/ldexp-uint-exponent.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/ldexp-uint-exponent.asm.frag new file mode 100644 index 0000000..9acdd01 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/ldexp-uint-exponent.asm.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out highp vec4 _GLF_color; + +void main() +{ + _GLF_color = ldexp(vec4(1.0), ivec4(uvec4(bitCount(uvec4(1u))))); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/loop-merge-to-continue.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/loop-merge-to-continue.asm.invalid.frag new file mode 100644 index 0000000..55db70c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/loop-merge-to-continue.asm.invalid.frag @@ -0,0 +1,17 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 v0; + +void main() +{ + FragColor = vec4(1.0); + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + FragColor += vec4(v0[(i + j) & 3]); + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/nonuniform-bracket-handling-2.vk.nocompat.asm.frag.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/nonuniform-bracket-handling-2.vk.nocompat.asm.frag.vk new file mode 100644 index 0000000..d2f9646 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/nonuniform-bracket-handling-2.vk.nocompat.asm.frag.vk @@ -0,0 +1,20 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require + +layout(set = 0, binding = 0, std430) readonly buffer SSBO +{ + uint indices[]; +} _8; + +layout(set = 0, binding = 0) uniform sampler2D uSamplers[]; +layout(set = 1, binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; + +void main() +{ + FragColor = textureLod(uSamplers[nonuniformEXT(_8.indices[10])], vUV, 0.0); + FragColor += textureLod(uSampler, vUV, float(_8.indices[int(gl_FragCoord.y)])); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/nonuniform-qualifier-propagation.vk.nocompat.asm.frag.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/nonuniform-qualifier-propagation.vk.nocompat.asm.frag.vk new file mode 100644 index 0000000..5f7ddee --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/nonuniform-qualifier-propagation.vk.nocompat.asm.frag.vk @@ -0,0 +1,37 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require + +layout(set = 0, binding = 2, std140) uniform UBO +{ + vec4 v[64]; +} ubos[]; + +layout(set = 0, binding = 3, std430) readonly buffer SSBO +{ + vec4 v[]; +} ssbos[]; + +layout(set = 0, binding = 0) uniform texture2D uSamplers[]; +layout(set = 0, binding = 1) uniform sampler uSamps[]; +layout(set = 0, binding = 4) uniform sampler2D uCombinedSamplers[]; + +layout(location = 0) flat in int vIndex; +layout(location = 0) out vec4 FragColor; +layout(location = 1) in vec2 vUV; + +void main() +{ + int i = vIndex; + int _59 = i + 10; + int _64 = i + 40; + FragColor = texture(sampler2D(uSamplers[nonuniformEXT(_59)], uSamps[nonuniformEXT(_64)]), vUV); + int _71 = i + 10; + FragColor = texture(uCombinedSamplers[nonuniformEXT(_71)], vUV); + int _77 = i + 20; + int _80 = i + 40; + FragColor += ubos[nonuniformEXT(_77)].v[_80]; + int _87 = i + 50; + int _90 = i + 60; + FragColor += ssbos[nonuniformEXT(_87)].v[_90]; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/opaque-id-literal-alias.preserve.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/opaque-id-literal-alias.preserve.asm.frag new file mode 100644 index 0000000..7459228 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/opaque-id-literal-alias.preserve.asm.frag @@ -0,0 +1,20 @@ +#version 450 + +layout(binding = 0) uniform sampler2DMS uSampled; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; + +void main() +{ + FragColor = vec4(0.0); + if (gl_FragCoord.x < 10.0) + { + FragColor += texelFetch(uSampled, ivec2(vUV), 0); + } + else + { + FragColor += texelFetch(uSampled, ivec2(vUV), 1); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/out-of-order-struct-id.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/out-of-order-struct-id.asm.frag new file mode 100644 index 0000000..22d72a6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/out-of-order-struct-id.asm.frag @@ -0,0 +1,25 @@ +#version 450 + +struct Foo +{ + vec4 a; +}; + +struct Bar +{ + Foo foo; + Foo foo2; +}; + +layout(binding = 0, std140) uniform UBO +{ + Bar bar; +} _7; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = _7.bar.foo.a + _7.bar.foo2.a; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/phi.zero-initialize.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/phi.zero-initialize.asm.frag new file mode 100644 index 0000000..ce14c46 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/phi.zero-initialize.asm.frag @@ -0,0 +1,29 @@ +#version 450 + +struct Foo +{ + int a; +}; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +int uninit_int = 0; +ivec4 uninit_vector = ivec4(0); +mat4 uninit_matrix = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); +Foo uninit_foo = Foo(0); + +void main() +{ + int _39 = 0; + if (vColor.x > 10.0) + { + _39 = 10; + } + else + { + _39 = 20; + } + FragColor = vColor; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-callstack.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-callstack.asm.frag new file mode 100644 index 0000000..948803c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-callstack.asm.frag @@ -0,0 +1,39 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(binding = 1, std430) buffer SSBO1 +{ + uint values1[]; +} _7; + +layout(binding = 0, std430) buffer SSBO0 +{ + uint values0[]; +} _9; + +void callee2() +{ + int _31 = int(gl_FragCoord.x); + _7.values1[_31]++; +} + +void callee() +{ + int _39 = int(gl_FragCoord.x); + _9.values0[_39]++; + callee2(); +} + +void spvMainInterlockedBody() +{ + callee(); +} + +void main() +{ + // Interlocks were used in a way not compatible with GLSL, this is very slow. + beginInvocationInterlockARB(); + spvMainInterlockedBody(); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-control-flow.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-control-flow.asm.frag new file mode 100644 index 0000000..72dca0d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-control-flow.asm.frag @@ -0,0 +1,53 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(binding = 1, std430) buffer SSBO1 +{ + uint values1[]; +} _7; + +layout(binding = 2, std430) buffer _12_13 +{ + uint _m0[]; +} _13; + +layout(binding = 0, std430) buffer SSBO0 +{ + uint values0[]; +} _9; + +void callee2() +{ + int _44 = int(gl_FragCoord.x); + _7.values1[_44]++; +} + +void callee() +{ + int _52 = int(gl_FragCoord.x); + _9.values0[_52]++; + callee2(); + if (true) + { + } +} + +void _35() +{ + _13._m0[int(gl_FragCoord.x)] = 4u; +} + +void spvMainInterlockedBody() +{ + callee(); + _35(); +} + +void main() +{ + // Interlocks were used in a way not compatible with GLSL, this is very slow. + beginInvocationInterlockARB(); + spvMainInterlockedBody(); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-split-functions.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-split-functions.asm.frag new file mode 100644 index 0000000..b09eb66 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/pixel-interlock-split-functions.asm.frag @@ -0,0 +1,49 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(binding = 1, std430) buffer SSBO1 +{ + uint values1[]; +} _7; + +layout(binding = 0, std430) buffer SSBO0 +{ + uint values0[]; +} _9; + +void callee2() +{ + int _37 = int(gl_FragCoord.x); + _7.values1[_37]++; +} + +void callee() +{ + int _45 = int(gl_FragCoord.x); + _9.values0[_45]++; + callee2(); +} + +void _29() +{ +} + +void _31() +{ +} + +void spvMainInterlockedBody() +{ + callee(); + _29(); + _31(); +} + +void main() +{ + // Interlocks were used in a way not compatible with GLSL, this is very slow. + beginInvocationInterlockARB(); + spvMainInterlockedBody(); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/reserved-identifiers.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/reserved-identifiers.asm.frag new file mode 100644 index 0000000..5d75a44 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/reserved-identifiers.asm.frag @@ -0,0 +1,17 @@ +#version 450 + +layout(location = 0) out vec4 _RESERVED_IDENTIFIER_FIXUP_spvFoo; +layout(location = 1) out vec4 SPIRV_Cross_blah; +layout(location = 2) out vec4 _40Bar; +layout(location = 3) out vec4 _m40; +layout(location = 4) out vec4 _underscore_foo_bar_meep_; + +void main() +{ + _RESERVED_IDENTIFIER_FIXUP_spvFoo = vec4(0.0); + SPIRV_Cross_blah = vec4(1.0); + _40Bar = vec4(2.0); + _m40 = vec4(3.0); + _underscore_foo_bar_meep_ = vec4(4.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/selection-merge-to-continue.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/selection-merge-to-continue.asm.invalid.frag new file mode 100644 index 0000000..edbce0c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/selection-merge-to-continue.asm.invalid.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 v0; + +void main() +{ + FragColor = vec4(1.0); + for (int i = 0; i < 4; i++) + { + if (v0.x == 20.0) + { + FragColor += vec4(v0[i & 3]); + } + else + { + FragColor += vec4(v0[i & 1]); + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/sparse-texture-feedback-uint-code.asm.desktop.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/sparse-texture-feedback-uint-code.asm.desktop.frag new file mode 100644 index 0000000..540978c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/sparse-texture-feedback-uint-code.asm.desktop.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_sparse_texture2 : require + +struct ResType +{ + uint _m0; + vec4 _m1; +}; + +layout(binding = 0) uniform sampler2D uSamp; + +layout(location = 0) in vec2 vUV; + +void main() +{ + uint _30; + vec4 _31; + _30 = sparseTextureARB(uSamp, vUV, _31); + ResType _26 = ResType(_30, _31); + vec4 texel = _26._m1; + bool ret = sparseTexelsResidentARB(int(_26._m0)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/subgroup-arithmetic-cast.nocompat.vk.asm.frag.vk b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/subgroup-arithmetic-cast.nocompat.vk.asm.frag.vk new file mode 100644 index 0000000..130cab7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/subgroup-arithmetic-cast.nocompat.vk.asm.frag.vk @@ -0,0 +1,24 @@ +#version 450 +#extension GL_KHR_shader_subgroup_arithmetic : require +#extension GL_KHR_shader_subgroup_clustered : require + +layout(location = 0) flat in int index; +layout(location = 0) out uint FragColor; + +void main() +{ + uint _17 = uint(index); + FragColor = uint(subgroupMin(index)); + FragColor = uint(subgroupMax(int(_17))); + FragColor = subgroupMin(uint(index)); + FragColor = subgroupMax(_17); + FragColor = uint(subgroupInclusiveMax(index)); + FragColor = uint(subgroupInclusiveMin(int(_17))); + FragColor = subgroupExclusiveMax(uint(index)); + FragColor = subgroupExclusiveMin(_17); + FragColor = uint(subgroupClusteredMin(index, 4u)); + FragColor = uint(subgroupClusteredMax(int(_17), 4u)); + FragColor = subgroupClusteredMin(uint(index), 4u); + FragColor = subgroupClusteredMax(_17, 4u); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag new file mode 100644 index 0000000..de8090e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag @@ -0,0 +1,63 @@ +#version 450 + +layout(location = 0) flat in int vIndex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + int i; + int j; + int _30; + int _31; + if (vIndex != 0 && vIndex != 1 && vIndex != 11 && vIndex != 2 && vIndex != 3 && vIndex != 4 && vIndex != 5) + { + _30 = 2; + } + if (vIndex == 1 || vIndex == 11) + { + _31 = 1; + } + switch (vIndex) + { + case 0: + { + _30 = 3; + } + default: + { + j = _30; + _31 = 0; + } + case 1: + case 11: + { + j = _31; + } + case 2: + { + break; + } + case 3: + { + if (vIndex > 3) + { + i = 0; + break; + } + else + { + break; + } + } + case 4: + { + } + case 5: + { + i = 0; + break; + } + } + FragColor = vec4(float(i)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-merge-to-continue.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-merge-to-continue.asm.invalid.frag new file mode 100644 index 0000000..88f76cf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-merge-to-continue.asm.invalid.frag @@ -0,0 +1,30 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); + for (int i = 0; i < 4; i++) + { + switch (i) + { + case 0: + { + FragColor.x += 1.0; + break; + } + case 1: + { + FragColor.y += 3.0; + break; + } + default: + { + FragColor.z += 3.0; + break; + } + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-single-case-multiple-exit-cfg.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-single-case-multiple-exit-cfg.asm.frag new file mode 100644 index 0000000..2f7fee6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/switch-single-case-multiple-exit-cfg.asm.frag @@ -0,0 +1,26 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out highp vec4 _GLF_color; + +vec2 _19; + +void main() +{ + highp vec2 _30; + do + { + if (gl_FragCoord.x != gl_FragCoord.x) + { + _30 = _19; + break; + } + highp vec2 _29 = _19; + _29.y = _19.y; + _30 = _29; + break; + } while(false); + _GLF_color = vec4(_30, 1.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/unordered-compare.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/unordered-compare.asm.frag new file mode 100644 index 0000000..2cc8129 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/unordered-compare.asm.frag @@ -0,0 +1,33 @@ +#version 450 + +layout(location = 0) in vec4 A; +layout(location = 1) in vec4 B; +layout(location = 0) out vec4 FragColor; + +vec4 test_vector() +{ + bvec4 le = not(greaterThanEqual(A, B)); + bvec4 leq = not(greaterThan(A, B)); + bvec4 ge = not(lessThanEqual(A, B)); + bvec4 geq = not(lessThan(A, B)); + bvec4 eq = not(notEqual(A, B)); + bvec4 neq = not(equal(A, B)); + return ((((mix(vec4(0.0), vec4(1.0), le) + mix(vec4(0.0), vec4(1.0), leq)) + mix(vec4(0.0), vec4(1.0), ge)) + mix(vec4(0.0), vec4(1.0), geq)) + mix(vec4(0.0), vec4(1.0), eq)) + mix(vec4(0.0), vec4(1.0), neq); +} + +float test_scalar() +{ + bool le = !(A.x >= B.x); + bool leq = !(A.x > B.x); + bool ge = !(A.x <= B.x); + bool geq = !(A.x < B.x); + bool eq = !(A.x != B.x); + bool neq = !(A.x == B.x); + return ((((float(le) + float(leq)) + float(ge)) + float(geq)) + float(eq)) + float(neq); +} + +void main() +{ + FragColor = test_vector() + vec4(test_scalar()); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/vector-extract-dynamic-spec-constant.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/vector-extract-dynamic-spec-constant.asm.frag new file mode 100644 index 0000000..d4f3aca --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/vector-extract-dynamic-spec-constant.asm.frag @@ -0,0 +1,27 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 0 +#endif +const int omap_r = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 1 +#endif +const int omap_g = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 2 +#endif +const int omap_b = SPIRV_CROSS_CONSTANT_ID_2; +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 3 +#endif +const int omap_a = SPIRV_CROSS_CONSTANT_ID_3; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = vec4(vColor[omap_r], vColor[omap_g], vColor[omap_b], vColor[omap_a]); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag new file mode 100644 index 0000000..b32d187 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vFloat; + +vec4 undef; + +void main() +{ + FragColor = vec4(undef.x, vFloat.y, 0.0, vFloat.w) + vec4(vFloat.z, vFloat.y, 0.0, vFloat.w); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/while-loop-inverted-test.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/while-loop-inverted-test.asm.frag new file mode 100644 index 0000000..0dd9869 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/frag/while-loop-inverted-test.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +void main() +{ + int i = 0; + int j = 0; + while (!(i == 20)) + { + j = ((j + i) + 1) * j; + i++; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/geom/store-uint-layer.invalid.asm.geom b/third_party/spirv-cross/reference/shaders-no-opt/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000..c768d5d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/geom/store-uint-layer.invalid.asm.geom @@ -0,0 +1,41 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +struct VertexOutput +{ + vec4 pos; +}; + +struct GeometryOutput +{ + vec4 pos; + uint layer; +}; + +void _main(VertexOutput _input[3], GeometryOutput stream) +{ + GeometryOutput _output; + _output.layer = 1u; + for (int v = 0; v < 3; v++) + { + _output.pos = _input[v].pos; + gl_Position = _output.pos; + gl_Layer = int(_output.layer); + EmitVertex(); + } + EndPrimitive(); +} + +void main() +{ + VertexOutput _input[3]; + _input[0].pos = gl_in[0].gl_Position; + _input[1].pos = gl_in[1].gl_Position; + _input[2].pos = gl_in[2].gl_Position; + VertexOutput param[3] = _input; + GeometryOutput param_1; + _main(param, param_1); + GeometryOutput stream = param_1; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/loop-header-self-continue-break.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/asm/loop-header-self-continue-break.asm.comp new file mode 100644 index 0000000..bd2a3c2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/loop-header-self-continue-break.asm.comp @@ -0,0 +1,89 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 1u +#endif +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 1u +#endif +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 1u +#endif + +layout(local_size_x = SPIRV_CROSS_CONSTANT_ID_0, local_size_y = SPIRV_CROSS_CONSTANT_ID_1, local_size_z = SPIRV_CROSS_CONSTANT_ID_2) in; + +layout(binding = 0, std430) buffer _4_6 +{ + float _m0[]; +} _6; + +layout(binding = 1, std430) buffer _4_7 +{ + float _m0[]; +} _7; + +uvec3 _28 = gl_WorkGroupSize; + +void main() +{ + float _44_copy; + float _46; + uint _47; + float _63; + uint _65; + float _36 = _6._m0[0u]; + uint _39 = 0u; + float _44; + for (;;) + { + _44 = _36; + _46 = _6._m0[35u]; + _47 = 0u; + for (;;) + { + uint _48 = _47 + 1u; + float _45 = _6._m0[_48]; + _6._m0[_47] = ((_46 + _44) + _45) / 3.0; + if (!(_47 < 34u)) + { + break; + } + else + { + _44_copy = _44; + _44 = _45; + _46 = _44_copy; + _47 = _48; + } + } + _6._m0[35u] = (_36 + (_44 + _6._m0[35u])) / 3.0; + if (!(_39 < 5u)) + { + _63 = _6._m0[0u]; + _65 = 1u; + break; + } + else + { + _36 = _6._m0[0u]; + _39++; + continue; + } + } + float _64; + for (;;) + { + _64 = (_63 < _6._m0[_65]) ? _6._m0[_65] : _63; + if (!(_65 < 35u)) + { + break; + } + else + { + _63 = _64; + _65++; + } + } + _7._m0[gl_GlobalInvocationID.x] = _64; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/temporary.zero-initialize.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/asm/temporary.zero-initialize.asm.frag new file mode 100644 index 0000000..1b8e8cd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/temporary.zero-initialize.asm.frag @@ -0,0 +1,28 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int vA; +layout(location = 1) flat in mediump int vB; + +void main() +{ + FragColor = vec4(0.0); + mediump int _10 = 0; + mediump int _15 = 0; + for (mediump int _16 = 0, _17 = 0; _16 < vA; _17 = _15, _16 += _10) + { + if ((vA + _16) == 20) + { + _15 = 50; + } + else + { + _15 = ((vB + _16) == 40) ? 60 : _17; + } + _10 = _15 + 10; + FragColor += vec4(1.0); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc b/third_party/spirv-cross/reference/shaders-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc new file mode 100644 index 0000000..8cb7a4e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc @@ -0,0 +1,79 @@ +#version 450 +layout(vertices = 3) out; + +struct VertexOutput +{ + vec4 pos; + vec2 uv; +}; + +struct HSOut +{ + vec4 pos; + vec2 uv; +}; + +struct HSConstantOut +{ + float EdgeTess[3]; + float InsideTess; +}; + +struct VertexOutput_1 +{ + vec2 uv; +}; + +struct HSOut_1 +{ + vec2 uv; +}; + +layout(location = 0) in VertexOutput_1 p[]; +layout(location = 0) out HSOut_1 _entryPointOutput[3]; + +HSOut _hs_main(VertexOutput p_1[3], uint i) +{ + HSOut _output; + _output.pos = p_1[i].pos; + _output.uv = p_1[i].uv; + return _output; +} + +HSConstantOut PatchHS(VertexOutput _patch[3]) +{ + HSConstantOut _output; + _output.EdgeTess[0] = (vec2(1.0) + _patch[0].uv).x; + _output.EdgeTess[1] = (vec2(1.0) + _patch[0].uv).x; + _output.EdgeTess[2] = (vec2(1.0) + _patch[0].uv).x; + _output.InsideTess = (vec2(1.0) + _patch[0].uv).x; + return _output; +} + +void main() +{ + VertexOutput p_1[3]; + p_1[0].pos = gl_in[0].gl_Position; + p_1[0].uv = p[0].uv; + p_1[1].pos = gl_in[1].gl_Position; + p_1[1].uv = p[1].uv; + p_1[2].pos = gl_in[2].gl_Position; + p_1[2].uv = p[2].uv; + uint i = gl_InvocationID; + VertexOutput param[3] = p_1; + uint param_1 = i; + HSOut flattenTemp = _hs_main(param, param_1); + gl_out[gl_InvocationID].gl_Position = flattenTemp.pos; + _entryPointOutput[gl_InvocationID].uv = flattenTemp.uv; + barrier(); + if (int(gl_InvocationID) == 0) + { + VertexOutput param_2[3] = p_1; + HSConstantOut _patchConstantResult = PatchHS(param_2); + gl_TessLevelOuter[0] = _patchConstantResult.EdgeTess[0]; + gl_TessLevelOuter[1] = _patchConstantResult.EdgeTess[1]; + gl_TessLevelOuter[2] = _patchConstantResult.EdgeTess[2]; + gl_TessLevelInner[0] = _patchConstantResult.InsideTess; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/complex-link-by-name.asm.vert b/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/complex-link-by-name.asm.vert new file mode 100644 index 0000000..0327140 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/complex-link-by-name.asm.vert @@ -0,0 +1,35 @@ +#version 450 + +struct Struct_vec4 +{ + vec4 m0; +}; + +layout(binding = 0, std140) uniform UBO +{ + Struct_vec4 m0; + Struct_vec4 m1; +} ubo_binding_0; + +layout(location = 0) out VertexOut +{ + Struct_vec4 m0; + Struct_vec4 m1; +} output_location_0; + +layout(location = 2) out Struct_vec4 output_location_2; +layout(location = 3) out Struct_vec4 output_location_3; + +void main() +{ + Struct_vec4 c; + c.m0 = ubo_binding_0.m0.m0; + Struct_vec4 b; + b.m0 = ubo_binding_0.m1.m0; + gl_Position = c.m0 + b.m0; + output_location_0.m0 = c; + output_location_0.m1 = b; + output_location_2 = c; + output_location_3 = b; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/complex-link-by-name.force-flattened-io.legacy.asm.vert b/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/complex-link-by-name.force-flattened-io.legacy.asm.vert new file mode 100644 index 0000000..280399b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/complex-link-by-name.force-flattened-io.legacy.asm.vert @@ -0,0 +1,33 @@ +#version 100 + +struct Struct_vec4 +{ + vec4 m0; +}; + +struct UBO +{ + Struct_vec4 m0; + Struct_vec4 m1; +}; + +uniform UBO ubo_binding_0; + +varying vec4 output_location_0_m0_m0; +varying vec4 output_location_0_m1_m0; +varying vec4 output_location_2_m0; +varying vec4 output_location_3_m0; + +void main() +{ + Struct_vec4 c; + c.m0 = ubo_binding_0.m0.m0; + Struct_vec4 b; + b.m0 = ubo_binding_0.m1.m0; + gl_Position = c.m0 + b.m0; + output_location_0_m0_m0 = c.m0; + output_location_0_m1_m0 = b.m0; + output_location_2_m0 = c.m0; + output_location_3_m0 = b.m0; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/empty-struct-composite.asm.vert b/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/empty-struct-composite.asm.vert new file mode 100644 index 0000000..8f786d4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/empty-struct-composite.asm.vert @@ -0,0 +1,13 @@ +#version 450 + +struct Test +{ + int empty_struct_member; +}; + +void main() +{ + Test _14 = Test(0); + Test t = _14; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/semantic-decoration.asm.vert b/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/semantic-decoration.asm.vert new file mode 100644 index 0000000..9af0e24 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/asm/vert/semantic-decoration.asm.vert @@ -0,0 +1,25 @@ +#version 450 + +struct VOut +{ + vec4 p; + vec4 c; +}; + +layout(location = 0) out vec4 _entryPointOutput_c; + +VOut _main() +{ + VOut v; + v.p = vec4(1.0); + v.c = vec4(2.0); + return v; +} + +void main() +{ + VOut flattenTemp = _main(); + gl_Position = flattenTemp.p; + _entryPointOutput_c = flattenTemp.c; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/bitcast-16bit-1.invalid.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/bitcast-16bit-1.invalid.comp new file mode 100644 index 0000000..501f979 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/bitcast-16bit-1.invalid.comp @@ -0,0 +1,34 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif +#if defined(GL_AMD_gpu_shader_int16) +#extension GL_AMD_gpu_shader_int16 : require +#else +#error No extension available for Int16. +#endif +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + i16vec4 inputs[]; +} _25; + +layout(binding = 1, std430) buffer SSBO1 +{ + ivec4 outputs[]; +} _39; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + f16vec2 a = int16BitsToFloat16(_25.inputs[ident].xy); + _39.outputs[ident].x = int(packFloat2x16(a + f16vec2(float16_t(1.0)))); + _39.outputs[ident].y = packInt2x16(_25.inputs[ident].zw); + _39.outputs[ident].z = int(packUint2x16(u16vec2(_25.inputs[ident].xy))); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/bitcast-16bit-2.invalid.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/bitcast-16bit-2.invalid.comp new file mode 100644 index 0000000..bddc16d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/bitcast-16bit-2.invalid.comp @@ -0,0 +1,39 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_int16) +#extension GL_AMD_gpu_shader_int16 : require +#else +#error No extension available for Int16. +#endif +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) buffer SSBO1 +{ + i16vec4 outputs[]; +} _21; + +layout(binding = 0, std430) buffer SSBO0 +{ + ivec4 inputs[]; +} _29; + +layout(binding = 2, std140) uniform UBO +{ + f16vec4 const0; +} _40; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + i16vec2 _47 = unpackInt2x16(_29.inputs[ident].x) + float16BitsToInt16(_40.const0.xy); + _21.outputs[ident] = i16vec4(_47.x, _47.y, _21.outputs[ident].z, _21.outputs[ident].w); + i16vec2 _66 = i16vec2(unpackUint2x16(uint(_29.inputs[ident].y)) - float16BitsToUint16(_40.const0.zw)); + _21.outputs[ident] = i16vec4(_21.outputs[ident].x, _21.outputs[ident].y, _66.x, _66.y); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/bitfield.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/bitfield.comp new file mode 100644 index 0000000..49bbddb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/bitfield.comp @@ -0,0 +1,19 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + int signed_value = 0; + uint unsigned_value = 0u; + int s = bitfieldExtract(signed_value, 5, 20); + uint u = bitfieldExtract(unsigned_value, 6, 21); + s = bitfieldInsert(s, 40, 5, 4); + u = bitfieldInsert(u, 60u, 5, 4); + u = bitfieldReverse(u); + s = bitfieldReverse(s); + int v0 = bitCount(u); + int v1 = bitCount(s); + int v2 = findMSB(u); + int v3 = findLSB(s); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/glsl.std450.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/glsl.std450.comp new file mode 100644 index 0000000..d2628a9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/glsl.std450.comp @@ -0,0 +1,112 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct ResType +{ + float _m0; + int _m1; +}; + +layout(binding = 0, std430) buffer SSBO +{ + float res; + int ires; + uint ures; + vec4 f32; + ivec4 s32; + uvec4 u32; + mat2 m2; + mat3 m3; + mat4 m4; +} _19; + +void main() +{ + _19.res = round(_19.f32.x); + _19.res = roundEven(_19.f32.x); + _19.res = trunc(_19.f32.x); + _19.res = abs(_19.f32.x); + _19.ires = abs(_19.s32.x); + _19.res = sign(_19.f32.x); + _19.ires = sign(_19.s32.x); + _19.res = floor(_19.f32.x); + _19.res = ceil(_19.f32.x); + _19.res = fract(_19.f32.x); + _19.res = radians(_19.f32.x); + _19.res = degrees(_19.f32.x); + _19.res = sin(_19.f32.x); + _19.res = cos(_19.f32.x); + _19.res = tan(_19.f32.x); + _19.res = asin(_19.f32.x); + _19.res = acos(_19.f32.x); + _19.res = atan(_19.f32.x); + _19.res = sinh(_19.f32.x); + _19.res = cosh(_19.f32.x); + _19.res = tanh(_19.f32.x); + _19.res = asinh(_19.f32.x); + _19.res = acosh(_19.f32.x); + _19.res = atanh(_19.f32.x); + _19.res = atan(_19.f32.x, _19.f32.y); + _19.res = pow(_19.f32.x, _19.f32.y); + _19.res = exp(_19.f32.x); + _19.res = log(_19.f32.x); + _19.res = exp2(_19.f32.x); + _19.res = log2(_19.f32.x); + _19.res = sqrt(_19.f32.x); + _19.res = inversesqrt(_19.f32.x); + _19.res = length(_19.f32.x); + _19.res = distance(_19.f32.x, _19.f32.y); + _19.res = normalize(_19.f32.x); + _19.res = faceforward(_19.f32.x, _19.f32.y, _19.f32.z); + _19.res = reflect(_19.f32.x, _19.f32.y); + _19.res = refract(_19.f32.x, _19.f32.y, _19.f32.z); + _19.res = length(_19.f32.xy); + _19.res = distance(_19.f32.xy, _19.f32.zw); + vec2 v2 = normalize(_19.f32.xy); + v2 = faceforward(_19.f32.xy, _19.f32.yz, _19.f32.zw); + v2 = reflect(_19.f32.xy, _19.f32.zw); + v2 = refract(_19.f32.xy, _19.f32.yz, _19.f32.w); + vec3 v3 = cross(_19.f32.xyz, _19.f32.yzw); + _19.res = determinant(_19.m2); + _19.res = determinant(_19.m3); + _19.res = determinant(_19.m4); + _19.m2 = inverse(_19.m2); + _19.m3 = inverse(_19.m3); + _19.m4 = inverse(_19.m4); + float tmp; + float _287 = modf(_19.f32.x, tmp); + _19.res = _287; + _19.res = min(_19.f32.x, _19.f32.y); + _19.ures = min(_19.u32.x, _19.u32.y); + _19.ires = min(_19.s32.x, _19.s32.y); + _19.res = max(_19.f32.x, _19.f32.y); + _19.ures = max(_19.u32.x, _19.u32.y); + _19.ires = max(_19.s32.x, _19.s32.y); + _19.res = clamp(_19.f32.x, _19.f32.y, _19.f32.z); + _19.ures = clamp(_19.u32.x, _19.u32.y, _19.u32.z); + _19.ires = clamp(_19.s32.x, _19.s32.y, _19.s32.z); + _19.res = mix(_19.f32.x, _19.f32.y, _19.f32.z); + _19.res = step(_19.f32.x, _19.f32.y); + _19.res = smoothstep(_19.f32.x, _19.f32.y, _19.f32.z); + _19.res = fma(_19.f32.x, _19.f32.y, _19.f32.z); + ResType _387; + _387._m0 = frexp(_19.f32.x, _387._m1); + int itmp = _387._m1; + _19.res = _387._m0; + _19.res = ldexp(_19.f32.x, itmp); + _19.ures = packSnorm4x8(_19.f32); + _19.ures = packUnorm4x8(_19.f32); + _19.ures = packSnorm2x16(_19.f32.xy); + _19.ures = packUnorm2x16(_19.f32.xy); + _19.ures = packHalf2x16(_19.f32.xy); + v2 = unpackSnorm2x16(_19.u32.x); + v2 = unpackUnorm2x16(_19.u32.x); + v2 = unpackHalf2x16(_19.u32.x); + vec4 v4 = unpackSnorm4x8(_19.u32.x); + v4 = unpackUnorm4x8(_19.u32.x); + _19.s32 = findLSB(_19.s32); + _19.s32 = findLSB(_19.u32); + _19.s32 = findMSB(_19.s32); + _19.s32 = findMSB(_19.u32); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/illegal-struct-name.asm.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/illegal-struct-name.asm.comp new file mode 100644 index 0000000..885dcb3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/illegal-struct-name.asm.comp @@ -0,0 +1,22 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct Foo +{ + float _abs; +}; + +layout(binding = 0, std430) buffer SSBO +{ + Foo foo; + Foo foo2; +} _7; + +void main() +{ + Foo f; + f._abs = _7.foo._abs; + int _abs = 10; + _7.foo2._abs = f._abs; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/inout-struct.invalid.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/inout-struct.invalid.comp new file mode 100644 index 0000000..640e25b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/inout-struct.invalid.comp @@ -0,0 +1,65 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct Foo +{ + vec4 a; + vec4 b; + vec4 c; + vec4 d; +}; + +layout(binding = 1, std430) readonly buffer SSBO2 +{ + vec4 data[]; +} indata; + +layout(binding = 0, std430) writeonly buffer SSBO +{ + vec4 data[]; +} outdata; + +layout(binding = 2, std430) readonly buffer SSBO3 +{ + Foo foos[]; +} foobar; + +void baz(inout Foo foo) +{ + uint ident = gl_GlobalInvocationID.x; + foo.a = indata.data[(4u * ident) + 0u]; + foo.b = indata.data[(4u * ident) + 1u]; + foo.c = indata.data[(4u * ident) + 2u]; + foo.d = indata.data[(4u * ident) + 3u]; +} + +void meow(inout Foo foo) +{ + foo.a += vec4(10.0); + foo.b += vec4(20.0); + foo.c += vec4(30.0); + foo.d += vec4(40.0); +} + +vec4 bar(Foo foo) +{ + return ((foo.a + foo.b) + foo.c) + foo.d; +} + +void main() +{ + Foo param; + baz(param); + Foo foo = param; + Foo param_1 = foo; + meow(param_1); + foo = param_1; + Foo param_2 = foo; + Foo param_3; + param_3.a = foobar.foos[gl_GlobalInvocationID.x].a; + param_3.b = foobar.foos[gl_GlobalInvocationID.x].b; + param_3.c = foobar.foos[gl_GlobalInvocationID.x].c; + param_3.d = foobar.foos[gl_GlobalInvocationID.x].d; + outdata.data[gl_GlobalInvocationID.x] = bar(param_2) + bar(param_3); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/loop.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/loop.comp new file mode 100644 index 0000000..2ba731c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/loop.comp @@ -0,0 +1,82 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +} _24; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idat = _24.in_data[ident]; + int k = 0; + uint i = 0u; + if (idat.y == 20.0) + { + do + { + k *= 2; + i++; + } while (i < ident); + } + switch (k) + { + case 10: + { + for (;;) + { + i++; + if (i > 10u) + { + break; + } + continue; + } + break; + } + default: + { + for (;;) + { + i += 2u; + if (i > 20u) + { + break; + } + continue; + } + break; + } + } + while (k < 10) + { + idat *= 2.0; + k++; + } + for (uint i_1 = 0u; i_1 < 16u; i_1++, k++) + { + for (uint j = 0u; j < 30u; j++) + { + idat = _24.mvp * idat; + } + } + k = 0; + for (;;) + { + k++; + if (k > 10) + { + k += 2; + } + else + { + k += 3; + continue; + } + k += 10; + continue; + } +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/return.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/return.comp new file mode 100644 index 0000000..4802be2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/return.comp @@ -0,0 +1,35 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _27; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + if (ident == 2u) + { + _27.out_data[ident] = vec4(20.0); + } + else + { + if (ident == 4u) + { + _27.out_data[ident] = vec4(10.0); + return; + } + } + int i = 0; + while (i < 20) + { + if (i == 10) + { + break; + } + return; + } + _27.out_data[ident] = vec4(10.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/shader_ballot_nonuniform_invocations.invalid.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/shader_ballot_nonuniform_invocations.invalid.comp new file mode 100644 index 0000000..a14343a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/shader_ballot_nonuniform_invocations.invalid.comp @@ -0,0 +1,11 @@ +#version 450 +#extension GL_AMD_shader_ballot : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + float addInvocations = addInvocationsNonUniformAMD(0.0); + int minInvocations = minInvocationsNonUniformAMD(1); + uint maxInvocations = uint(maxInvocationsNonUniformAMD(4)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/specialization-constant-evaluation.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/specialization-constant-evaluation.comp new file mode 100644 index 0000000..6958359 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/specialization-constant-evaluation.comp @@ -0,0 +1,321 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 1 +#endif +const int SONE = SPIRV_CROSS_CONSTANT_ID_2; +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 2 +#endif +const int STWO = SPIRV_CROSS_CONSTANT_ID_3; +const int _10 = (SONE + STWO); +const uint _13 = (uint(_10) + 0u); +#ifndef SPIRV_CROSS_CONSTANT_ID_5 +#define SPIRV_CROSS_CONSTANT_ID_5 1u +#endif +const uint UONE = SPIRV_CROSS_CONSTANT_ID_5; +const uint _15 = (_13 + UONE); +#ifndef SPIRV_CROSS_CONSTANT_ID_6 +#define SPIRV_CROSS_CONSTANT_ID_6 2u +#endif +const uint UTWO = SPIRV_CROSS_CONSTANT_ID_6; +const uint IADD = (_15 + UTWO); +const uint _19 = (IADD - 5u); +const uint _28 = (uint(SONE) + 0u); +const uint ISUB = (UTWO - _28); +const uint IMUL = (UTWO * UTWO); +const uint _37 = (IMUL - 3u); +const uint UDIV = (UTWO / UTWO); +#ifndef SPIRV_CROSS_CONSTANT_ID_4 +#define SPIRV_CROSS_CONSTANT_ID_4 -2 +#endif +const int SNEG_TWO = SPIRV_CROSS_CONSTANT_ID_4; +const int SDIV = (STWO / SNEG_TWO); +const int _52 = (SDIV + 2); +#ifndef SPIRV_CROSS_CONSTANT_ID_7 +#define SPIRV_CROSS_CONSTANT_ID_7 -3 +#endif +const int SNEG_THREE = SPIRV_CROSS_CONSTANT_ID_7; +const int SMOD = (STWO % SNEG_THREE); +const int _66 = (SMOD + 2); +const uint UMOD = (IADD % IMUL); +const uint _73 = (UMOD - 1u); +const uint LSHL = (IADD << ISUB); +const uint _81 = (LSHL - 11u); +const uint RSHL = (IADD >> ISUB); +const uint _89 = (RSHL - 2u); +const int _95 = int(IADD + 0u); +const int _96 = (-_95); +const int _97 = (-SDIV); +const int RSHA = (_96 >> _97); +const int _100 = (RSHA + 4); +const bool IEQ = (IADD == ISUB); +const int _109 = IEQ ? 2 : 1; +const bool INEQ = (IADD != ISUB); +const int _116 = INEQ ? 1 : 2; +const bool ULT = (IADD < ISUB); +const int _123 = ULT ? 2 : 1; +const bool ULE = (IADD <= ISUB); +const int _130 = ULE ? 2 : 1; +const bool UGT = (IADD > ISUB); +const int _137 = UGT ? 1 : 2; +const bool UGE = (IADD >= ISUB); +const int _144 = UGE ? 1 : 2; +const bool SLT = (SMOD < 1); +const int _151 = SLT ? 1 : 2; +const bool SLE = (SMOD <= 1); +const int _158 = SLE ? 1 : 2; +const bool SGT = (SMOD > 1); +const int _165 = SGT ? 2 : 1; +const bool SGE = (SMOD >= 1); +const int _172 = SGE ? 2 : 1; +const bool LOR = (IEQ || SLT); +const int _179 = LOR ? 1 : 2; +const bool LAND = (IEQ && SLT); +const int _186 = LAND ? 2 : 1; +const bool LNOT = (!LOR); +const int _193 = LNOT ? 2 : 1; +const uint AND = (IADD & IADD); +const uint _200 = (AND - 5u); +const uint OR = (IADD | ISUB); +const uint _208 = (OR - 6u); +const uint XOR = (IADD ^ IADD); +const uint _215 = (XOR + 1u); +const uint NOT = (~XOR); +const uint _223 = (NOT - 4294967294u); +const bool LEQ = (LAND == LNOT); +const int _230 = LEQ ? 1 : 2; +const bool LNEQ = (LAND != LNOT); +const int _237 = LNEQ ? 2 : 1; +const uint SEL = IEQ ? IADD : ISUB; +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 true +#endif +const bool TRUE = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 false +#endif +const bool FALSE = SPIRV_CROSS_CONSTANT_ID_1; + +layout(binding = 0, std430) buffer SSBO_IAdd +{ + float val[_19]; + float dummy; +} IAdd; + +layout(binding = 1, std430) buffer SSBO_ISub +{ + float val[ISUB]; + float dummy; +} ISub; + +layout(binding = 2, std430) buffer SSBO_IMul +{ + float val[_37]; + float dummy; +} IMul; + +layout(binding = 3, std430) buffer SSBO_UDiv +{ + float val[UDIV]; + float dummy; +} UDiv; + +layout(binding = 4, std430) buffer SSBO_SDiv +{ + float val[_52]; + float dummy; +} SDiv; + +layout(binding = 5, std430) buffer SSBO_SRem +{ + float val[1]; + float dummy; +} SRem; + +layout(binding = 6, std430) buffer SSBO_SMod +{ + float val[_66]; + float dummy; +} SMod; + +layout(binding = 7, std430) buffer SSBO_UMod +{ + float val[_73]; + float dummy; +} UMod; + +layout(binding = 8, std430) buffer SSBO_LShl +{ + float val[_81]; + float dummy; +} LShl; + +layout(binding = 9, std430) buffer SSBO_RShl +{ + float val[_89]; + float dummy; +} RShl; + +layout(binding = 10, std430) buffer SSBO_RSha +{ + float val[_100]; + float dummy; +} RSha; + +layout(binding = 11, std430) buffer SSBO_IEq +{ + float val[_109]; + float dummy; +} IEq; + +layout(binding = 12, std430) buffer SSBO_INeq +{ + float val[_116]; + float dummy; +} INeq; + +layout(binding = 13, std430) buffer SSBO_Ult +{ + float val[_123]; + float dummy; +} Ult; + +layout(binding = 14, std430) buffer SSBO_Ule +{ + float val[_130]; + float dummy; +} Ule; + +layout(binding = 15, std430) buffer SSBO_Ugt +{ + float val[_137]; + float dummy; +} Ugt; + +layout(binding = 16, std430) buffer SSBO_Uge +{ + float val[_144]; + float dummy; +} Uge; + +layout(binding = 17, std430) buffer SSBO_Slt +{ + float val[_151]; + float dummy; +} Slt; + +layout(binding = 18, std430) buffer SSBO_Sle +{ + float val[_158]; + float dummy; +} Sle; + +layout(binding = 19, std430) buffer SSBO_Sgt +{ + float val[_165]; + float dummy; +} Sgt; + +layout(binding = 20, std430) buffer SSBO_Sge +{ + float val[_172]; + float dummy; +} Sge; + +layout(binding = 21, std430) buffer SSBO_Lor +{ + float val[_179]; + float dummy; +} Lor; + +layout(binding = 22, std430) buffer SSBO_Land +{ + float val[_186]; + float dummy; +} Land; + +layout(binding = 23, std430) buffer SSBO_Lnot +{ + float val[_193]; + float dummy; +} Lnot; + +layout(binding = 24, std430) buffer SSBO_And +{ + float val[_200]; + float dummy; +} And; + +layout(binding = 24, std430) buffer SSBO_Or +{ + float val[_208]; + float dummy; +} Or; + +layout(binding = 24, std430) buffer SSBO_Xor +{ + float val[_215]; + float dummy; +} Xor; + +layout(binding = 25, std430) buffer SSBO_Not +{ + float val[_223]; + float dummy; +} Not; + +layout(binding = 26, std430) buffer SSBO_Leq +{ + float val[_230]; + float dummy; +} Leq; + +layout(binding = 27, std430) buffer SSBO_Lneq +{ + float val[_237]; + float dummy; +} Lneq; + +layout(binding = 28, std430) buffer SSBO_Sel +{ + float val[SEL]; + float dummy; +} Sel; + +void main() +{ + IAdd.val[0] = 0.0; + ISub.val[0] = 0.0; + IMul.val[0] = 0.0; + UDiv.val[0] = 0.0; + SDiv.val[0] = 0.0; + SRem.val[0] = 0.0; + SMod.val[0] = 0.0; + UMod.val[0] = 0.0; + LShl.val[0] = 0.0; + RShl.val[0] = 0.0; + RSha.val[0] = 0.0; + IEq.val[0] = 0.0; + INeq.val[0] = 0.0; + Ult.val[0] = 0.0; + Ule.val[0] = 0.0; + Ugt.val[0] = 0.0; + Uge.val[0] = 0.0; + Slt.val[0] = 0.0; + Sle.val[0] = 0.0; + Sgt.val[0] = 0.0; + Sge.val[0] = 0.0; + Lor.val[0] = 0.0; + Land.val[0] = 0.0; + Lnot.val[0] = 0.0; + And.val[0] = 0.0; + Or.val[0] = 0.0; + Xor.val[0] = 0.0; + Not.val[0] = 0.0; + Leq.val[0] = 0.0; + Lneq.val[0] = 0.0; + Sel.val[0] = 0.0; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp.vk new file mode 100644 index 0000000..a037b30 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp.vk @@ -0,0 +1,105 @@ +#version 310 es +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + S4 m3s[8]; +}; + +layout(set = 0, binding = 1, scalar) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + mat2 m0; + mat2 m1; + mat2x3 m2[4]; + mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(set = 0, binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + mat2 m0; + mat2 m1; + mat2x3 m2[4]; + mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content.m0s[0].a[0] = ssbo_140.content.m0s[0].a[0]; + ssbo_430.content.m0s[0].b = ssbo_140.content.m0s[0].b; + ssbo_430.content.m1s[0].a = ssbo_140.content.m1s[0].a; + ssbo_430.content.m1s[0].b = ssbo_140.content.m1s[0].b; + ssbo_430.content.m2s[0].a[0] = ssbo_140.content.m2s[0].a[0]; + ssbo_430.content.m2s[0].b = ssbo_140.content.m2s[0].b; + ssbo_430.content.m0.a[0] = ssbo_140.content.m0.a[0]; + ssbo_430.content.m0.b = ssbo_140.content.m0.b; + ssbo_430.content.m1.a = ssbo_140.content.m1.a; + ssbo_430.content.m1.b = ssbo_140.content.m1.b; + ssbo_430.content.m2.a[0] = ssbo_140.content.m2.a[0]; + ssbo_430.content.m2.b = ssbo_140.content.m2.b; + ssbo_430.content.m3.a = ssbo_140.content.m3.a; + ssbo_430.content.m3.b = ssbo_140.content.m3.b; + ssbo_430.content.m4 = ssbo_140.content.m4; + ssbo_430.content.m3s[0].c = ssbo_140.content.m3s[0].c; + ssbo_430.content.m3s[1].c = ssbo_140.content.m3s[1].c; + ssbo_430.content.m3s[2].c = ssbo_140.content.m3s[2].c; + ssbo_430.content.m3s[3].c = ssbo_140.content.m3s[3].c; + ssbo_430.content.m3s[4].c = ssbo_140.content.m3s[4].c; + ssbo_430.content.m3s[5].c = ssbo_140.content.m3s[5].c; + ssbo_430.content.m3s[6].c = ssbo_140.content.m3s[6].c; + ssbo_430.content.m3s[7].c = ssbo_140.content.m3s[7].c; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups.nocompat.invalid.vk.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups.nocompat.invalid.vk.comp.vk new file mode 100644 index 0000000..6d28857 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups.nocompat.invalid.vk.comp.vk @@ -0,0 +1,110 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_ballot : require +#extension GL_KHR_shader_subgroup_shuffle : require +#extension GL_KHR_shader_subgroup_shuffle_relative : require +#extension GL_KHR_shader_subgroup_vote : require +#extension GL_KHR_shader_subgroup_arithmetic : require +#extension GL_KHR_shader_subgroup_clustered : require +#extension GL_KHR_shader_subgroup_quad : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(set = 0, binding = 0, std430) buffer SSBO +{ + float FragColor; +} _9; + +void main() +{ + _9.FragColor = float(gl_NumSubgroups); + _9.FragColor = float(gl_SubgroupID); + _9.FragColor = float(gl_SubgroupSize); + _9.FragColor = float(gl_SubgroupInvocationID); + subgroupMemoryBarrier(); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierShared(); + subgroupMemoryBarrierImage(); + bool elected = subgroupElect(); + _9.FragColor = vec4(gl_SubgroupEqMask).x; + _9.FragColor = vec4(gl_SubgroupGeMask).x; + _9.FragColor = vec4(gl_SubgroupGtMask).x; + _9.FragColor = vec4(gl_SubgroupLeMask).x; + _9.FragColor = vec4(gl_SubgroupLtMask).x; + vec4 broadcasted = subgroupBroadcast(vec4(10.0), 8u); + vec3 first = subgroupBroadcastFirst(vec3(20.0)); + uvec4 ballot_value = subgroupBallot(true); + bool inverse_ballot_value = subgroupInverseBallot(ballot_value); + bool bit_extracted = subgroupBallotBitExtract(uvec4(10u), 8u); + uint bit_count = subgroupBallotBitCount(ballot_value); + uint inclusive_bit_count = subgroupBallotInclusiveBitCount(ballot_value); + uint exclusive_bit_count = subgroupBallotExclusiveBitCount(ballot_value); + uint lsb = subgroupBallotFindLSB(ballot_value); + uint msb = subgroupBallotFindMSB(ballot_value); + uint shuffled = subgroupShuffle(10u, 8u); + uint shuffled_xor = subgroupShuffleXor(30u, 8u); + uint shuffled_up = subgroupShuffleUp(20u, 4u); + uint shuffled_down = subgroupShuffleDown(20u, 4u); + bool has_all = subgroupAll(true); + bool has_any = subgroupAny(true); + bool has_equal = subgroupAllEqual(true); + vec4 added = subgroupAdd(vec4(20.0)); + ivec4 iadded = subgroupAdd(ivec4(20)); + vec4 multiplied = subgroupMul(vec4(20.0)); + ivec4 imultiplied = subgroupMul(ivec4(20)); + vec4 lo = subgroupMin(vec4(20.0)); + vec4 hi = subgroupMax(vec4(20.0)); + ivec4 slo = subgroupMin(ivec4(20)); + ivec4 shi = subgroupMax(ivec4(20)); + uvec4 ulo = subgroupMin(uvec4(20u)); + uvec4 uhi = subgroupMax(uvec4(20u)); + uvec4 anded = subgroupAnd(ballot_value); + uvec4 ored = subgroupOr(ballot_value); + uvec4 xored = subgroupXor(ballot_value); + added = subgroupInclusiveAdd(added); + iadded = subgroupInclusiveAdd(iadded); + multiplied = subgroupInclusiveMul(multiplied); + imultiplied = subgroupInclusiveMul(imultiplied); + lo = subgroupInclusiveMin(lo); + hi = subgroupInclusiveMax(hi); + slo = subgroupInclusiveMin(slo); + shi = subgroupInclusiveMax(shi); + ulo = subgroupInclusiveMin(ulo); + uhi = subgroupInclusiveMax(uhi); + anded = subgroupInclusiveAnd(anded); + ored = subgroupInclusiveOr(ored); + xored = subgroupInclusiveXor(ored); + added = subgroupExclusiveAdd(lo); + added = subgroupExclusiveAdd(multiplied); + multiplied = subgroupExclusiveMul(multiplied); + iadded = subgroupExclusiveAdd(imultiplied); + imultiplied = subgroupExclusiveMul(imultiplied); + lo = subgroupExclusiveMin(lo); + hi = subgroupExclusiveMax(hi); + ulo = subgroupExclusiveMin(ulo); + uhi = subgroupExclusiveMax(uhi); + slo = subgroupExclusiveMin(slo); + shi = subgroupExclusiveMax(shi); + anded = subgroupExclusiveAnd(anded); + ored = subgroupExclusiveOr(ored); + xored = subgroupExclusiveXor(ored); + added = subgroupClusteredAdd(added, 4u); + multiplied = subgroupClusteredMul(multiplied, 4u); + iadded = subgroupClusteredAdd(iadded, 4u); + imultiplied = subgroupClusteredMul(imultiplied, 4u); + lo = subgroupClusteredMin(lo, 4u); + hi = subgroupClusteredMax(hi, 4u); + ulo = subgroupClusteredMin(ulo, 4u); + uhi = subgroupClusteredMax(uhi, 4u); + slo = subgroupClusteredMin(slo, 4u); + shi = subgroupClusteredMax(shi, 4u); + anded = subgroupClusteredAnd(anded, 4u); + ored = subgroupClusteredOr(ored, 4u); + xored = subgroupClusteredXor(xored, 4u); + vec4 swap_horiz = subgroupQuadSwapHorizontal(vec4(20.0)); + vec4 swap_vertical = subgroupQuadSwapVertical(vec4(20.0)); + vec4 swap_diagonal = subgroupQuadSwapDiagonal(vec4(20.0)); + vec4 quad_broadcast = subgroupQuadBroadcast(vec4(20.0), 3u); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp b/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp new file mode 100644 index 0000000..00b3fa7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp @@ -0,0 +1,401 @@ +#version 450 + +#if defined(GL_KHR_shader_subgroup_ballot) +#extension GL_KHR_shader_subgroup_ballot : require +#elif defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#elif defined(GL_ARB_shader_ballot) && defined(GL_ARB_shader_int64) +#extension GL_ARB_shader_int64 : enable +#extension GL_ARB_shader_ballot : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#extension GL_KHR_shader_subgroup_basic : require +#elif defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#elif defined(GL_ARB_shader_ballot) && defined(GL_ARB_shader_int64) +#extension GL_ARB_shader_int64 : enable +#extension GL_ARB_shader_ballot : require +#elif defined(GL_AMD_gcn_shader) && (defined(GL_AMD_gpu_shader_int64) || defined(GL_NV_gpu_shader5)) +#extension GL_AMD_gpu_shader_int64 : enable +#extension GL_NV_gpu_shader5 : enable +#extension GL_AMD_gcn_shader : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#extension GL_KHR_shader_subgroup_basic : require +#elif defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#elif defined(GL_ARB_shader_ballot) && defined(GL_ARB_shader_int64) +#extension GL_ARB_shader_int64 : enable +#extension GL_ARB_shader_ballot : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#extension GL_KHR_shader_subgroup_basic : require +#elif defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#extension GL_KHR_shader_subgroup_basic : require +#elif defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_KHR_shader_subgroup_ballot) +#extension GL_KHR_shader_subgroup_ballot : require +#elif defined(GL_ARB_shader_ballot) && defined(GL_ARB_shader_int64) +#extension GL_ARB_shader_int64 : enable +#extension GL_ARB_shader_ballot : require +#elif defined(GL_NV_shader_thread_shuffle) +#extension GL_NV_shader_thread_shuffle : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_KHR_shader_subgroup_ballot) +#extension GL_KHR_shader_subgroup_ballot : require +#elif defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#endif + +#if defined(GL_KHR_shader_subgroup_vote) +#extension GL_KHR_shader_subgroup_vote : require +#elif defined(GL_AMD_gcn_shader) && (defined(GL_AMD_gpu_shader_int64) || defined(GL_NV_gpu_shader5)) +#extension GL_AMD_gpu_shader_int64 : enable +#extension GL_NV_gpu_shader5 : enable +#extension GL_AMD_gcn_shader : require +#elif defined(GL_NV_gpu_shader_5) +#extension GL_NV_gpu_shader_5 : require +#elif defined(GL_ARB_shader_group_vote) +#extension GL_ARB_shader_group_vote : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#extension GL_KHR_shader_subgroup_basic : require +#elif defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#elif defined(GL_ARB_shader_ballot) && defined(GL_ARB_shader_int64) +#extension GL_ARB_shader_int64 : enable +#extension GL_ARB_shader_ballot : require +#elif defined(GL_AMD_gcn_shader) && (defined(GL_AMD_gpu_shader_int64) || defined(GL_NV_gpu_shader5)) +#extension GL_AMD_gpu_shader_int64 : enable +#extension GL_NV_gpu_shader5 : enable +#extension GL_AMD_gcn_shader : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#extension GL_KHR_shader_subgroup_basic : require +#endif + +#if defined(GL_KHR_shader_subgroup_ballot) +#extension GL_KHR_shader_subgroup_ballot : require +#elif defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#elif defined(GL_ARB_shader_ballot) && defined(GL_ARB_shader_int64) +#extension GL_ARB_shader_int64 : enable +#extension GL_ARB_shader_ballot : require +#else +#error No extensions available to emulate requested subgroup feature. +#endif + +#if defined(GL_NV_shader_thread_group) +#extension GL_NV_shader_thread_group : require +#endif +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float FragColor; +} _9; + +#if defined(GL_KHR_shader_subgroup_ballot) +#elif defined(GL_NV_shader_thread_group) +#define gl_SubgroupEqMask uvec4(gl_ThreadEqMaskNV, 0u, 0u, 0u) +#define gl_SubgroupGeMask uvec4(gl_ThreadGeMaskNV, 0u, 0u, 0u) +#define gl_SubgroupGtMask uvec4(gl_ThreadGtMaskNV, 0u, 0u, 0u) +#define gl_SubgroupLeMask uvec4(gl_ThreadLeMaskNV, 0u, 0u, 0u) +#define gl_SubgroupLtMask uvec4(gl_ThreadLtMaskNV, 0u, 0u, 0u) +#elif defined(GL_ARB_shader_ballot) +#define gl_SubgroupEqMask uvec4(unpackUint2x32(gl_SubGroupEqMaskARB), 0u, 0u) +#define gl_SubgroupGeMask uvec4(unpackUint2x32(gl_SubGroupGeMaskARB), 0u, 0u) +#define gl_SubgroupGtMask uvec4(unpackUint2x32(gl_SubGroupGtMaskARB), 0u, 0u) +#define gl_SubgroupLeMask uvec4(unpackUint2x32(gl_SubGroupLeMaskARB), 0u, 0u) +#define gl_SubgroupLtMask uvec4(unpackUint2x32(gl_SubGroupLtMaskARB), 0u, 0u) +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#elif defined(GL_NV_shader_thread_group) +#define gl_SubgroupSize gl_WarpSizeNV +#elif defined(GL_ARB_shader_ballot) +#define gl_SubgroupSize gl_SubGroupSizeARB +#elif defined(GL_AMD_gcn_shader) +#define gl_SubgroupSize uint(gl_SIMDGroupSizeAMD) +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#elif defined(GL_NV_shader_thread_group) +#define gl_SubgroupInvocationID gl_ThreadInWarpNV +#elif defined(GL_ARB_shader_ballot) +#define gl_SubgroupInvocationID gl_SubGroupInvocationARB +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#elif defined(GL_NV_shader_thread_group) +#define gl_SubgroupID gl_WarpIDNV +#endif + +#if defined(GL_KHR_shader_subgroup_basic) +#elif defined(GL_NV_shader_thread_group) +#define gl_NumSubgroups gl_WarpsPerSMNV +#endif + +#if defined(GL_KHR_shader_subgroup_ballot) +#elif defined(GL_ARB_shader_ballot) +int subgroupBroadcastFirst(int value) { return readFirstInvocationARB(value); } +ivec2 subgroupBroadcastFirst(ivec2 value) { return readFirstInvocationARB(value); } +ivec3 subgroupBroadcastFirst(ivec3 value) { return readFirstInvocationARB(value); } +ivec4 subgroupBroadcastFirst(ivec4 value) { return readFirstInvocationARB(value); } +uint subgroupBroadcastFirst(uint value) { return readFirstInvocationARB(value); } +uvec2 subgroupBroadcastFirst(uvec2 value) { return readFirstInvocationARB(value); } +uvec3 subgroupBroadcastFirst(uvec3 value) { return readFirstInvocationARB(value); } +uvec4 subgroupBroadcastFirst(uvec4 value) { return readFirstInvocationARB(value); } +float subgroupBroadcastFirst(float value) { return readFirstInvocationARB(value); } +vec2 subgroupBroadcastFirst(vec2 value) { return readFirstInvocationARB(value); } +vec3 subgroupBroadcastFirst(vec3 value) { return readFirstInvocationARB(value); } +vec4 subgroupBroadcastFirst(vec4 value) { return readFirstInvocationARB(value); } +double subgroupBroadcastFirst(double value) { return readFirstInvocationARB(value); } +dvec2 subgroupBroadcastFirst(dvec2 value) { return readFirstInvocationARB(value); } +dvec3 subgroupBroadcastFirst(dvec3 value) { return readFirstInvocationARB(value); } +dvec4 subgroupBroadcastFirst(dvec4 value) { return readFirstInvocationARB(value); } +int subgroupBroadcast(int value, uint id) { return readInvocationARB(value, id); } +ivec2 subgroupBroadcast(ivec2 value, uint id) { return readInvocationARB(value, id); } +ivec3 subgroupBroadcast(ivec3 value, uint id) { return readInvocationARB(value, id); } +ivec4 subgroupBroadcast(ivec4 value, uint id) { return readInvocationARB(value, id); } +uint subgroupBroadcast(uint value, uint id) { return readInvocationARB(value, id); } +uvec2 subgroupBroadcast(uvec2 value, uint id) { return readInvocationARB(value, id); } +uvec3 subgroupBroadcast(uvec3 value, uint id) { return readInvocationARB(value, id); } +uvec4 subgroupBroadcast(uvec4 value, uint id) { return readInvocationARB(value, id); } +float subgroupBroadcast(float value, uint id) { return readInvocationARB(value, id); } +vec2 subgroupBroadcast(vec2 value, uint id) { return readInvocationARB(value, id); } +vec3 subgroupBroadcast(vec3 value, uint id) { return readInvocationARB(value, id); } +vec4 subgroupBroadcast(vec4 value, uint id) { return readInvocationARB(value, id); } +double subgroupBroadcast(double value, uint id) { return readInvocationARB(value, id); } +dvec2 subgroupBroadcast(dvec2 value, uint id) { return readInvocationARB(value, id); } +dvec3 subgroupBroadcast(dvec3 value, uint id) { return readInvocationARB(value, id); } +dvec4 subgroupBroadcast(dvec4 value, uint id) { return readInvocationARB(value, id); } +#elif defined(GL_NV_shader_thread_shuffle) +int subgroupBroadcastFirst(int value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +ivec2 subgroupBroadcastFirst(ivec2 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +ivec3 subgroupBroadcastFirst(ivec3 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +ivec4 subgroupBroadcastFirst(ivec4 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +uint subgroupBroadcastFirst(uint value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +uvec2 subgroupBroadcastFirst(uvec2 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +uvec3 subgroupBroadcastFirst(uvec3 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +uvec4 subgroupBroadcastFirst(uvec4 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +float subgroupBroadcastFirst(float value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +vec2 subgroupBroadcastFirst(vec2 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +vec3 subgroupBroadcastFirst(vec3 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +vec4 subgroupBroadcastFirst(vec4 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +double subgroupBroadcastFirst(double value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +dvec2 subgroupBroadcastFirst(dvec2 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +dvec3 subgroupBroadcastFirst(dvec3 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +dvec4 subgroupBroadcastFirst(dvec4 value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); } +int subgroupBroadcast(int value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +ivec2 subgroupBroadcast(ivec2 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +ivec3 subgroupBroadcast(ivec3 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +ivec4 subgroupBroadcast(ivec4 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +uint subgroupBroadcast(uint value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +uvec2 subgroupBroadcast(uvec2 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +uvec3 subgroupBroadcast(uvec3 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +uvec4 subgroupBroadcast(uvec4 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +float subgroupBroadcast(float value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +vec2 subgroupBroadcast(vec2 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +vec3 subgroupBroadcast(vec3 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +vec4 subgroupBroadcast(vec4 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +double subgroupBroadcast(double value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +dvec2 subgroupBroadcast(dvec2 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +dvec3 subgroupBroadcast(dvec3 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +dvec4 subgroupBroadcast(dvec4 value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); } +#endif + +#if defined(GL_KHR_shader_subgroup_ballot) +#elif defined(GL_NV_shader_thread_group) +uint subgroupBallotFindLSB(uvec4 value) { return findLSB(value.x); } +uint subgroupBallotFindMSB(uvec4 value) { return findMSB(value.x); } +#else +uint subgroupBallotFindLSB(uvec4 value) +{ + int firstLive = findLSB(value.x); + return uint(firstLive != -1 ? firstLive : (findLSB(value.y) + 32)); +} +uint subgroupBallotFindMSB(uvec4 value) +{ + int firstLive = findMSB(value.y); + return uint(firstLive != -1 ? (firstLive + 32) : findMSB(value.x)); +} +#endif + +#if defined(GL_KHR_shader_subgroup_vote) +#elif defined(GL_AMD_gcn_shader) +bool subgroupAll(bool value) { return ballotAMD(value) == ballotAMD(true); } +bool subgroupAny(bool value) { return ballotAMD(value) != 0ull; } +bool subgroupAllEqual(bool value) { uint64_t b = ballotAMD(value); return b == 0ull || b == ballotAMD(true); } +#elif defined(GL_NV_gpu_shader_5) +bool subgroupAll(bool value) { return allThreadsNV(value); } +bool subgroupAny(bool value) { return anyThreadNV(value); } +bool subgroupAllEqual(bool value) { return allThreadsEqualNV(value); } +#elif defined(GL_ARB_shader_group_vote) +bool subgroupAll(bool v) { return allInvocationsARB(v); } +bool subgroupAny(bool v) { return anyInvocationARB(v); } +bool subgroupAllEqual(bool v) { return allInvocationsEqualARB(v); } +#endif + +#ifndef GL_KHR_shader_subgroup_vote +#define _SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(type) bool subgroupAllEqual(type value) { return subgroupAllEqual(subgroupBroadcastFirst(value) == value); } +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(int) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(ivec2) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(ivec3) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(ivec4) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(uint) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(uvec2) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(uvec3) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(uvec4) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(float) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(vec2) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(vec3) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(vec4) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(double) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(dvec2) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(dvec3) +_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(dvec4) +#undef _SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND +#endif + +#if defined(GL_KHR_shader_subgroup_ballot) +#elif defined(GL_NV_shader_thread_group) +uvec4 subgroupBallot(bool v) { return uvec4(ballotThreadNV(v), 0u, 0u, 0u); } +#elif defined(GL_ARB_shader_ballot) +uvec4 subgroupBallot(bool v) { return uvec4(unpackUint2x32(ballotARB(v)), 0u, 0u); } +#endif + +#ifndef GL_KHR_shader_subgroup_basic +bool subgroupElect() +{ + uvec4 activeMask = subgroupBallot(true); + uint firstLive = subgroupBallotFindLSB(activeMask); + return gl_SubgroupInvocationID == firstLive; +} +#endif + +#ifndef GL_KHR_shader_subgroup_basic +void subgroupBarrier() { memoryBarrierShared(); } +#endif + +#ifndef GL_KHR_shader_subgroup_basic +void subgroupMemoryBarrier() { groupMemoryBarrier(); } +void subgroupMemoryBarrierBuffer() { groupMemoryBarrier(); } +void subgroupMemoryBarrierShared() { memoryBarrierShared(); } +void subgroupMemoryBarrierImage() { groupMemoryBarrier(); } +#endif + +#ifndef GL_KHR_shader_subgroup_ballot +bool subgroupInverseBallot(uvec4 value) +{ + return any(notEqual(value.xy & gl_SubgroupEqMask.xy, uvec2(0u))); +} +uint subgroupBallotInclusiveBitCount(uvec4 value) +{ + uvec2 v = value.xy & gl_SubgroupLeMask.xy; + ivec2 c = bitCount(v); +#ifdef GL_NV_shader_thread_group + return uint(c.x); +#else + return uint(c.x + c.y); +#endif +} +uint subgroupBallotExclusiveBitCount(uvec4 value) +{ + uvec2 v = value.xy & gl_SubgroupLtMask.xy; + ivec2 c = bitCount(v); +#ifdef GL_NV_shader_thread_group + return uint(c.x); +#else + return uint(c.x + c.y); +#endif +} +#endif + +#ifndef GL_KHR_shader_subgroup_ballot +uint subgroupBallotBitCount(uvec4 value) +{ + ivec2 c = bitCount(value.xy); +#ifdef GL_NV_shader_thread_group + return uint(c.x); +#else + return uint(c.x + c.y); +#endif +} +#endif + +#ifndef GL_KHR_shader_subgroup_ballot +bool subgroupBallotBitExtract(uvec4 value, uint index) +{ +#ifdef GL_NV_shader_thread_group + uint shifted = value.x >> index; +#else + uint shifted = value[index >> 5u] >> (index & 0x1fu); +#endif + return (shifted & 1u) != 0u; +} +#endif + +void main() +{ + _9.FragColor = float(gl_NumSubgroups); + _9.FragColor = float(gl_SubgroupID); + _9.FragColor = float(gl_SubgroupSize); + _9.FragColor = float(gl_SubgroupInvocationID); + subgroupMemoryBarrier(); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierShared(); + subgroupMemoryBarrierImage(); + bool elected = subgroupElect(); + _9.FragColor = vec4(gl_SubgroupEqMask).x; + _9.FragColor = vec4(gl_SubgroupGeMask).x; + _9.FragColor = vec4(gl_SubgroupGtMask).x; + _9.FragColor = vec4(gl_SubgroupLeMask).x; + _9.FragColor = vec4(gl_SubgroupLtMask).x; + vec4 broadcasted = subgroupBroadcast(vec4(10.0), 8u); + vec3 first = subgroupBroadcastFirst(vec3(20.0)); + uvec4 ballot_value = subgroupBallot(true); + bool inverse_ballot_value = subgroupInverseBallot(ballot_value); + bool bit_extracted = subgroupBallotBitExtract(uvec4(10u), 8u); + uint bit_count = subgroupBallotBitCount(ballot_value); + uint inclusive_bit_count = subgroupBallotInclusiveBitCount(ballot_value); + uint exclusive_bit_count = subgroupBallotExclusiveBitCount(ballot_value); + uint lsb = subgroupBallotFindLSB(ballot_value); + uint msb = subgroupBallotFindMSB(ballot_value); + bool has_all = subgroupAll(true); + bool has_any = subgroupAny(true); + bool has_equal_bool = subgroupAllEqual(true); + bool has_equal_T = subgroupAllEqual(uvec3(5u)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp.vk b/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp.vk new file mode 100644 index 0000000..61aa2f7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp.vk @@ -0,0 +1,45 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_ballot : require +#extension GL_KHR_shader_subgroup_vote : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(set = 0, binding = 0, std430) buffer SSBO +{ + float FragColor; +} _9; + +void main() +{ + _9.FragColor = float(gl_NumSubgroups); + _9.FragColor = float(gl_SubgroupID); + _9.FragColor = float(gl_SubgroupSize); + _9.FragColor = float(gl_SubgroupInvocationID); + subgroupMemoryBarrier(); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierShared(); + subgroupMemoryBarrierImage(); + bool elected = subgroupElect(); + _9.FragColor = vec4(gl_SubgroupEqMask).x; + _9.FragColor = vec4(gl_SubgroupGeMask).x; + _9.FragColor = vec4(gl_SubgroupGtMask).x; + _9.FragColor = vec4(gl_SubgroupLeMask).x; + _9.FragColor = vec4(gl_SubgroupLtMask).x; + vec4 broadcasted = subgroupBroadcast(vec4(10.0), 8u); + vec3 first = subgroupBroadcastFirst(vec3(20.0)); + uvec4 ballot_value = subgroupBallot(true); + bool inverse_ballot_value = subgroupInverseBallot(ballot_value); + bool bit_extracted = subgroupBallotBitExtract(uvec4(10u), 8u); + uint bit_count = subgroupBallotBitCount(ballot_value); + uint inclusive_bit_count = subgroupBallotInclusiveBitCount(ballot_value); + uint exclusive_bit_count = subgroupBallotExclusiveBitCount(ballot_value); + uint lsb = subgroupBallotFindLSB(ballot_value); + uint msb = subgroupBallotFindMSB(ballot_value); + bool has_all = subgroupAll(true); + bool has_any = subgroupAny(true); + bool has_equal_bool = subgroupAllEqual(true); + bool has_equal_T = subgroupAllEqual(uvec3(5u)); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/16bit-constants.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/16bit-constants.invalid.frag new file mode 100644 index 0000000..57d8256 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/16bit-constants.invalid.frag @@ -0,0 +1,25 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif +#if defined(GL_AMD_gpu_shader_int16) +#extension GL_AMD_gpu_shader_int16 : require +#else +#error No extension available for Int16. +#endif + +layout(location = 0) out float16_t foo; +layout(location = 1) out int16_t bar; +layout(location = 2) out uint16_t baz; + +void main() +{ + foo = float16_t(1.0); + bar = 2s; + baz = 3us; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/fp16.invalid.desktop.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/fp16.invalid.desktop.frag new file mode 100644 index 0000000..55f5235 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/fp16.invalid.desktop.frag @@ -0,0 +1,159 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif + +struct ResType +{ + f16vec4 _m0; + ivec4 _m1; +}; + +layout(location = 3) in f16vec4 v4; +layout(location = 2) in f16vec3 v3; +layout(location = 0) in float16_t v1; +layout(location = 1) in f16vec2 v2; + +f16mat2 test_mat2(f16vec2 a, f16vec2 b, f16vec2 c, f16vec2 d) +{ + return f16mat2(f16vec2(a), f16vec2(b)) * f16mat2(f16vec2(c), f16vec2(d)); +} + +f16mat3 test_mat3(f16vec3 a, f16vec3 b, f16vec3 c, f16vec3 d, f16vec3 e, f16vec3 f) +{ + return f16mat3(f16vec3(a), f16vec3(b), f16vec3(c)) * f16mat3(f16vec3(d), f16vec3(e), f16vec3(f)); +} + +void test_constants() +{ + float16_t a = float16_t(1.0); + float16_t b = float16_t(1.5); + float16_t c = float16_t(-1.5); + float16_t d = float16_t(0.0 / 0.0); + float16_t e = float16_t(1.0 / 0.0); + float16_t f = float16_t(-1.0 / 0.0); + float16_t g = float16_t(1014.0); + float16_t h = float16_t(9.5367431640625e-07); +} + +float16_t test_result() +{ + return float16_t(1.0); +} + +void test_conversions() +{ + float16_t one = test_result(); + int a = int(one); + uint b = uint(one); + bool c = !(one == float16_t(0.0)); + float d = float(one); + double e = double(one); + float16_t a2 = float16_t(a); + float16_t b2 = float16_t(b); + float16_t c2 = float16_t(c); + float16_t d2 = float16_t(d); + float16_t e2 = float16_t(e); +} + +void test_builtins() +{ + f16vec4 res = radians(v4); + res = degrees(v4); + res = sin(v4); + res = cos(v4); + res = tan(v4); + res = asin(v4); + res = atan(v4, v3.xyzz); + res = atan(v4); + res = sinh(v4); + res = cosh(v4); + res = tanh(v4); + res = asinh(v4); + res = acosh(v4); + res = atanh(v4); + res = pow(v4, v4); + res = exp(v4); + res = log(v4); + res = exp2(v4); + res = log2(v4); + res = sqrt(v4); + res = inversesqrt(v4); + res = abs(v4); + res = sign(v4); + res = floor(v4); + res = trunc(v4); + res = round(v4); + res = roundEven(v4); + res = ceil(v4); + res = fract(v4); + res = mod(v4, v4); + f16vec4 tmp; + f16vec4 _231 = modf(v4, tmp); + res = _231; + res = min(v4, v4); + res = max(v4, v4); + res = clamp(v4, v4, v4); + res = mix(v4, v4, v4); + res = mix(v4, v4, lessThan(v4, v4)); + res = step(v4, v4); + res = smoothstep(v4, v4, v4); + bvec4 btmp = isnan(v4); + btmp = isinf(v4); + res = fma(v4, v4, v4); + ResType _275; + _275._m0 = frexp(v4, _275._m1); + ivec4 itmp = _275._m1; + res = _275._m0; + res = ldexp(res, itmp); + uint pack0 = packFloat2x16(v4.xy); + uint pack1 = packFloat2x16(v4.zw); + res = f16vec4(unpackFloat2x16(pack0), unpackFloat2x16(pack1)); + float16_t t0 = length(v4); + t0 = distance(v4, v4); + t0 = dot(v4, v4); + f16vec3 res3 = cross(v3, v3); + res = normalize(v4); + res = faceforward(v4, v4, v4); + res = reflect(v4, v4); + res = refract(v4, v4, v1); + btmp = lessThan(v4, v4); + btmp = lessThanEqual(v4, v4); + btmp = greaterThan(v4, v4); + btmp = greaterThanEqual(v4, v4); + btmp = equal(v4, v4); + btmp = not(equal(v4, v4)); + res = dFdx(v4); + res = dFdy(v4); + res = dFdxFine(v4); + res = dFdyFine(v4); + res = dFdxCoarse(v4); + res = dFdyCoarse(v4); + res = fwidth(v4); + res = fwidthFine(v4); + res = fwidthCoarse(v4); +} + +void main() +{ + f16vec2 param = v2; + f16vec2 param_1 = v2; + f16vec2 param_2 = v3.xy; + f16vec2 param_3 = v3.xy; + f16mat2 m0 = test_mat2(param, param_1, param_2, param_3); + f16vec3 param_4 = v3; + f16vec3 param_5 = v3; + f16vec3 param_6 = v3; + f16vec3 param_7 = v4.xyz; + f16vec3 param_8 = v4.xyz; + f16vec3 param_9 = v4.yzw; + f16mat3 m1 = test_mat3(param_4, param_5, param_6, param_7, param_8, param_9); + test_constants(); + test_conversions(); + test_builtins(); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/fragmentMaskFetch_subpassInput.vk.nocompat.invalid.frag.vk b/third_party/spirv-cross/reference/shaders-no-opt/frag/fragmentMaskFetch_subpassInput.vk.nocompat.invalid.frag.vk new file mode 100644 index 0000000..4aaf397 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/fragmentMaskFetch_subpassInput.vk.nocompat.invalid.frag.vk @@ -0,0 +1,11 @@ +#version 450 +#extension GL_AMD_shader_fragment_mask : require + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS t; + +void main() +{ + vec4 test2 = fragmentFetchAMD(t, 4u); + uint testi2 = fragmentMaskFetchAMD(t); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/fs.invalid.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/fs.invalid.frag new file mode 100644 index 0000000..aecf69e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/fs.invalid.frag @@ -0,0 +1,15 @@ +#version 450 +#extension GL_AMD_shader_fragment_mask : require +#extension GL_AMD_shader_explicit_vertex_parameter : require + +layout(binding = 0) uniform sampler2DMS texture1; + +layout(location = 0) __explicitInterpAMD in vec4 vary; + +void main() +{ + uint testi1 = fragmentMaskFetchAMD(texture1, ivec2(0)); + vec4 test1 = fragmentFetchAMD(texture1, ivec2(1), 2u); + vec4 pos = interpolateAtVertexAMD(vary, 0u); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/image-gather.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/image-gather.frag new file mode 100644 index 0000000..1baccdf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/image-gather.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSamp; +layout(binding = 1) uniform sampler2DShadow uSampShadow; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec3 vUV; + +void main() +{ + FragColor = textureGather(uSamp, vUV.xy); + FragColor += textureGather(uSamp, vUV.xy, 1); + FragColor += textureGather(uSampShadow, vUV.xy, vUV.z); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/multi-dimensional.desktop.invalid.flatten_dim.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/multi-dimensional.desktop.invalid.flatten_dim.frag new file mode 100644 index 0000000..ef6bb52 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/multi-dimensional.desktop.invalid.flatten_dim.frag @@ -0,0 +1,24 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uTextures[2 * 3 * 1]; + +layout(location = 1) in vec2 vUV; +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in int vIndex; + +void main() +{ + vec4 values3[2 * 3 * 1]; + for (int z = 0; z < 2; z++) + { + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 1; x++) + { + values3[z * 3 * 1 + y * 1 + x] = texture(uTextures[z * 3 * 1 + y * 1 + x], vUV); + } + } + } + FragColor = (values3[1 * 3 * 1 + 2 * 1 + 0] + values3[0 * 3 * 1 + 2 * 1 + 0]) + values3[(vIndex + 1) * 3 * 1 + 2 * 1 + vIndex]; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/pixel-interlock-simple-callstack.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/pixel-interlock-simple-callstack.frag new file mode 100644 index 0000000..151ed01 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/pixel-interlock-simple-callstack.frag @@ -0,0 +1,34 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(binding = 1, std430) buffer SSBO1 +{ + uint values1[]; +} _14; + +layout(binding = 0, std430) buffer SSBO0 +{ + uint values0[]; +} _35; + +void callee2() +{ + int _25 = int(gl_FragCoord.x); + _14.values1[_25]++; +} + +void callee() +{ + int _38 = int(gl_FragCoord.x); + _35.values0[_38]++; + callee2(); +} + +void main() +{ + beginInvocationInterlockARB(); + callee(); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag.vk b/third_party/spirv-cross/reference/shaders-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag.vk new file mode 100644 index 0000000..434cb3d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag.vk @@ -0,0 +1,24 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require + +layout(set = 0, binding = 0, std430) uniform UBO +{ + float a[1024]; + vec3 b[2]; +} _17; + +layout(set = 0, binding = 1, std430) uniform UBOEnhancedLayout +{ + layout(offset = 0) float c[1024]; + layout(offset = 4096) vec3 d[2]; + layout(offset = 10000) float e; +} _30; + +layout(location = 0) out float FragColor; +layout(location = 0) flat in int vIndex; + +void main() +{ + FragColor = (_17.a[vIndex] + _30.c[vIndex]) + _30.e; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/sparse-texture-clamp.desktop.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/sparse-texture-clamp.desktop.frag new file mode 100644 index 0000000..df0daa7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/sparse-texture-clamp.desktop.frag @@ -0,0 +1,46 @@ +#version 450 +#extension GL_ARB_sparse_texture2 : require +#extension GL_ARB_sparse_texture_clamp : require + +struct ResType +{ + int _m0; + vec4 _m1; +}; + +layout(binding = 0) uniform sampler2D uSamp; + +layout(location = 0) in vec2 vUV; + +void main() +{ + int _66; + vec4 _67; + _66 = sparseTextureClampARB(uSamp, vUV, 1.0, _67, 2.0); + ResType _25 = ResType(_66, _67); + vec4 texel = _25._m1; + int code = _25._m0; + texel = textureClampARB(uSamp, vUV, 1.0, 2.0); + int _68; + vec4 _69; + _68 = sparseTextureOffsetClampARB(uSamp, vUV, ivec2(1, 2), 1.0, _69, 2.0); + ResType _37 = ResType(_68, _69); + texel = _37._m1; + code = _37._m0; + texel = textureOffsetClampARB(uSamp, vUV, ivec2(1, 2), 1.0, 2.0); + int _70; + vec4 _71; + _70 = sparseTextureGradClampARB(uSamp, vUV, vec2(1.0), vec2(2.0), 1.0, _71); + ResType _47 = ResType(_70, _71); + texel = _47._m1; + code = _47._m0; + texel = textureGradClampARB(uSamp, vUV, vec2(1.0), vec2(2.0), 1.0); + int _72; + vec4 _73; + _72 = sparseTextureGradOffsetClampARB(uSamp, vUV, vec2(1.0), vec2(2.0), ivec2(-1, -2), 1.0, _73); + ResType _58 = ResType(_72, _73); + texel = _58._m1; + code = _58._m0; + texel = textureGradOffsetClampARB(uSamp, vUV, vec2(1.0), vec2(2.0), ivec2(-1, -2), 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/sparse-texture-feedback.desktop.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/sparse-texture-feedback.desktop.frag new file mode 100644 index 0000000..7faa226 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/sparse-texture-feedback.desktop.frag @@ -0,0 +1,105 @@ +#version 450 +#extension GL_ARB_sparse_texture2 : require +#extension GL_ARB_sparse_texture_clamp : require + +struct ResType +{ + int _m0; + vec4 _m1; +}; + +layout(binding = 0) uniform sampler2D uSamp; +layout(binding = 1) uniform sampler2DMS uSampMS; +layout(binding = 2, rgba8) uniform readonly image2D uImage; +layout(binding = 3, rgba8) uniform readonly image2DMS uImageMS; + +layout(location = 0) in vec2 vUV; + +void main() +{ + int _144; + vec4 _145; + _144 = sparseTextureARB(uSamp, vUV, _145); + ResType _24 = ResType(_144, _145); + vec4 texel = _24._m1; + bool ret = sparseTexelsResidentARB(_24._m0); + int _146; + vec4 _147; + _146 = sparseTextureARB(uSamp, vUV, _147, 1.10000002384185791015625); + ResType _31 = ResType(_146, _147); + texel = _31._m1; + ret = sparseTexelsResidentARB(_31._m0); + int _148; + vec4 _149; + _148 = sparseTextureLodARB(uSamp, vUV, 1.0, _149); + ResType _38 = ResType(_148, _149); + texel = _38._m1; + ret = sparseTexelsResidentARB(_38._m0); + int _150; + vec4 _151; + _150 = sparseTextureOffsetARB(uSamp, vUV, ivec2(1), _151); + ResType _47 = ResType(_150, _151); + texel = _47._m1; + ret = sparseTexelsResidentARB(_47._m0); + int _152; + vec4 _153; + _152 = sparseTextureOffsetARB(uSamp, vUV, ivec2(2), _153, 0.5); + ResType _56 = ResType(_152, _153); + texel = _56._m1; + ret = sparseTexelsResidentARB(_56._m0); + int _154; + vec4 _155; + _154 = sparseTexelFetchARB(uSamp, ivec2(vUV), 1, _155); + ResType _64 = ResType(_154, _155); + texel = _64._m1; + ret = sparseTexelsResidentARB(_64._m0); + int _156; + vec4 _157; + _156 = sparseTexelFetchARB(uSampMS, ivec2(vUV), 2, _157); + ResType _76 = ResType(_156, _157); + texel = _76._m1; + ret = sparseTexelsResidentARB(_76._m0); + int _158; + vec4 _159; + _158 = sparseTexelFetchOffsetARB(uSamp, ivec2(vUV), 1, ivec2(2, 3), _159); + ResType _86 = ResType(_158, _159); + texel = _86._m1; + ret = sparseTexelsResidentARB(_86._m0); + int _160; + vec4 _161; + _160 = sparseTextureLodOffsetARB(uSamp, vUV, 1.5, ivec2(2, 3), _161); + ResType _93 = ResType(_160, _161); + texel = _93._m1; + ret = sparseTexelsResidentARB(_93._m0); + int _162; + vec4 _163; + _162 = sparseTextureGradARB(uSamp, vUV, vec2(1.0), vec2(3.0), _163); + ResType _102 = ResType(_162, _163); + texel = _102._m1; + ret = sparseTexelsResidentARB(_102._m0); + int _164; + vec4 _165; + _164 = sparseTextureGradOffsetARB(uSamp, vUV, vec2(1.0), vec2(3.0), ivec2(-2, -3), _165); + ResType _111 = ResType(_164, _165); + texel = _111._m1; + ret = sparseTexelsResidentARB(_111._m0); + int _166; + vec4 _167; + _166 = sparseTextureClampARB(uSamp, vUV, 4.0, _167); + ResType _118 = ResType(_166, _167); + texel = _118._m1; + ret = sparseTexelsResidentARB(_118._m0); + int _168; + vec4 _169; + _168 = sparseImageLoadARB(uImage, ivec2(vUV), _169); + ResType _128 = ResType(_168, _169); + texel = _128._m1; + ret = sparseTexelsResidentARB(_128._m0); + int _170; + vec4 _171; + _170 = sparseImageLoadARB(uImageMS, ivec2(vUV), 1, _171); + ResType _138 = ResType(_170, _171); + texel = _138._m1; + ret = sparseTexelsResidentARB(_138._m0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.frag new file mode 100644 index 0000000..00a146c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.frag @@ -0,0 +1,18 @@ +#version 310 es +#extension GL_EXT_shader_framebuffer_fetch : require +precision mediump float; +precision highp int; + +mediump vec4 uSubpass0; +mediump vec4 uSubpass1; + +layout(location = 0) inout vec3 FragColor; +layout(location = 1) inout vec4 FragColor2; + +void main() +{ + uSubpass0.xyz = FragColor; + uSubpass1 = FragColor2; + FragColor = uSubpass0.xyz + uSubpass1.xyz; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.legacy.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.legacy.frag new file mode 100644 index 0000000..d1b7265 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.legacy.frag @@ -0,0 +1,16 @@ +#version 100 +#extension GL_EXT_shader_framebuffer_fetch : require +#extension GL_EXT_draw_buffers : require +precision mediump float; +precision highp int; + +mediump vec4 uSubpass0; +mediump vec4 uSubpass1; + +void main() +{ + uSubpass0 = gl_LastFragData[0]; + uSubpass1 = gl_LastFragData[1]; + gl_FragData[0] = uSubpass0.xyz + uSubpass1.xyz; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/frag/variables.zero-initialize.frag b/third_party/spirv-cross/reference/shaders-no-opt/frag/variables.zero-initialize.frag new file mode 100644 index 0000000..c902723 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/frag/variables.zero-initialize.frag @@ -0,0 +1,28 @@ +#version 450 + +struct Foo +{ + int a; +}; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; +int uninit_int = 0; +ivec4 uninit_vector = ivec4(0); +mat4 uninit_matrix = mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); +Foo uninit_foo = Foo(0); + +void main() +{ + int uninit_function_int = 0; + if (vColor.x > 10.0) + { + uninit_function_int = 10; + } + else + { + uninit_function_int = 20; + } + FragColor = vColor; +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag b/third_party/spirv-cross/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag new file mode 100644 index 0000000..cc5cb71 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag @@ -0,0 +1,24 @@ +#version 100 +precision mediump float; +precision highp int; + +vec2 _19; + +void main() +{ + highp vec2 _30; + for (int SPIRV_Cross_Dummy15 = 0; SPIRV_Cross_Dummy15 < 1; SPIRV_Cross_Dummy15++) + { + if (gl_FragCoord.x != gl_FragCoord.x) + { + _30 = _19; + break; + } + highp vec2 _29 = _19; + _29.y = _19.y; + _30 = _29; + break; + } + gl_FragData[0] = vec4(_30, 1.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/vert/io-blocks.force-flattened-io.vert b/third_party/spirv-cross/reference/shaders-no-opt/vert/io-blocks.force-flattened-io.vert new file mode 100644 index 0000000..604de8a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/vert/io-blocks.force-flattened-io.vert @@ -0,0 +1,25 @@ +#version 450 + +struct Foo +{ + vec4 bar[2]; + vec4 baz[2]; +}; + +out vec4 _14_foo_bar[2]; +out vec4 _14_foo_baz[2]; +out vec4 _14_foo2_bar[2]; +out vec4 _14_foo2_baz[2]; +out vec4 foo3_bar[2]; +out vec4 foo3_baz[2]; + +void main() +{ + _14_foo_bar[0] = vec4(1.0); + _14_foo_baz[1] = vec4(2.0); + _14_foo2_bar[0] = vec4(3.0); + _14_foo2_baz[1] = vec4(4.0); + foo3_bar[0] = vec4(5.0); + foo3_baz[1] = vec4(6.0); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/vert/pass-array-by-value.vert b/third_party/spirv-cross/reference/shaders-no-opt/vert/pass-array-by-value.vert new file mode 100644 index 0000000..45d4210 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/vert/pass-array-by-value.vert @@ -0,0 +1,27 @@ +#version 310 es + +layout(location = 0) in int Index1; +layout(location = 1) in int Index2; + +vec4 consume_constant_arrays2(vec4 positions[4], vec4 positions2[4]) +{ + vec4 indexable[4] = positions; + vec4 indexable_1[4] = positions2; + return indexable[Index1] + indexable_1[Index2]; +} + +vec4 consume_constant_arrays(vec4 positions[4], vec4 positions2[4]) +{ + return consume_constant_arrays2(positions, positions2); +} + +void main() +{ + vec4 LUT2[4]; + LUT2[0] = vec4(10.0); + LUT2[1] = vec4(11.0); + LUT2[2] = vec4(12.0); + LUT2[3] = vec4(13.0); + gl_Position = consume_constant_arrays(vec4[](vec4(0.0), vec4(1.0), vec4(2.0), vec4(3.0)), LUT2); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/spec-constant.vk.frag b/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/spec-constant.vk.frag new file mode 100644 index 0000000..2ae3b6b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/spec-constant.vk.frag @@ -0,0 +1,131 @@ +#version 310 es +precision mediump float; +precision highp int; + +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 1.0 +#endif +const float a = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 2.0 +#endif +const float b = SPIRV_CROSS_CONSTANT_ID_2; +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 3 +#endif +const int c = SPIRV_CROSS_CONSTANT_ID_3; +const uint _18 = (uint(c) + 0u); +const int _21 = (-c); +const int _23 = (~c); +#ifndef SPIRV_CROSS_CONSTANT_ID_4 +#define SPIRV_CROSS_CONSTANT_ID_4 4 +#endif +const int d = SPIRV_CROSS_CONSTANT_ID_4; +const int _26 = (c + d); +const int _28 = (c - d); +const int _30 = (c * d); +const int _32 = (c / d); +#ifndef SPIRV_CROSS_CONSTANT_ID_5 +#define SPIRV_CROSS_CONSTANT_ID_5 5u +#endif +const uint e = SPIRV_CROSS_CONSTANT_ID_5; +#ifndef SPIRV_CROSS_CONSTANT_ID_6 +#define SPIRV_CROSS_CONSTANT_ID_6 6u +#endif +const uint f = SPIRV_CROSS_CONSTANT_ID_6; +const uint _36 = (e / f); +const int _38 = (c % d); +const uint _40 = (e % f); +const int _42 = (c >> d); +const uint _44 = (e >> f); +const int _46 = (c << d); +const int _48 = (c | d); +const int _50 = (c ^ d); +const int _52 = (c & d); +#ifndef SPIRV_CROSS_CONSTANT_ID_7 +#define SPIRV_CROSS_CONSTANT_ID_7 false +#endif +const bool g = SPIRV_CROSS_CONSTANT_ID_7; +#ifndef SPIRV_CROSS_CONSTANT_ID_8 +#define SPIRV_CROSS_CONSTANT_ID_8 true +#endif +const bool h = SPIRV_CROSS_CONSTANT_ID_8; +const bool _58 = (g || h); +const bool _60 = (g && h); +const bool _62 = (!g); +const bool _64 = (g == h); +const bool _66 = (g != h); +const bool _68 = (c == d); +const bool _70 = (c != d); +const bool _72 = (c < d); +const bool _74 = (e < f); +const bool _76 = (c > d); +const bool _78 = (e > f); +const bool _80 = (c <= d); +const bool _82 = (e <= f); +const bool _84 = (c >= d); +const bool _86 = (e >= f); +const int _92 = int(e + 0u); +const bool _94 = (c != int(0u)); +const bool _96 = (e != 0u); +const int _100 = int(g); +const uint _103 = uint(g); +const int _118 = (c + 3); +const int _127 = (c + 2); +const int _135 = (d + 2); + +struct Foo +{ + float elems[_135]; +}; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + float t0 = a; + float t1 = b; + mediump uint c0 = _18; + mediump int c1 = _21; + mediump int c2 = _23; + mediump int c3 = _26; + mediump int c4 = _28; + mediump int c5 = _30; + mediump int c6 = _32; + mediump uint c7 = _36; + mediump int c8 = _38; + mediump uint c9 = _40; + mediump int c10 = _42; + mediump uint c11 = _44; + mediump int c12 = _46; + mediump int c13 = _48; + mediump int c14 = _50; + mediump int c15 = _52; + bool c16 = _58; + bool c17 = _60; + bool c18 = _62; + bool c19 = _64; + bool c20 = _66; + bool c21 = _68; + bool c22 = _70; + bool c23 = _72; + bool c24 = _74; + bool c25 = _76; + bool c26 = _78; + bool c27 = _80; + bool c28 = _82; + bool c29 = _84; + bool c30 = _86; + mediump int c31 = c8 + c3; + mediump int c32 = _92; + bool c33 = _94; + bool c34 = _96; + mediump int c35 = _100; + mediump uint c36 = _103; + float c37 = float(g); + float vec0[_118][8]; + float vec1[_127]; + Foo foo; + FragColor = ((vec4(t0 + t1) + vec4(vec0[0][0])) + vec4(vec1[0])) + vec4(foo.elems[c]); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/spec-constant.vk.frag.vk b/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/spec-constant.vk.frag.vk new file mode 100644 index 0000000..c5ae60b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/spec-constant.vk.frag.vk @@ -0,0 +1,107 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(constant_id = 1) const float a = 1.0; +layout(constant_id = 2) const float b = 2.0; +layout(constant_id = 3) const int c = 3; +const uint _18 = (uint(c) + 0u); +const int _21 = (-c); +const int _23 = (~c); +layout(constant_id = 4) const int d = 4; +const int _26 = (c + d); +const int _28 = (c - d); +const int _30 = (c * d); +const int _32 = (c / d); +layout(constant_id = 5) const uint e = 5u; +layout(constant_id = 6) const uint f = 6u; +const uint _36 = (e / f); +const int _38 = (c % d); +const uint _40 = (e % f); +const int _42 = (c >> d); +const uint _44 = (e >> f); +const int _46 = (c << d); +const int _48 = (c | d); +const int _50 = (c ^ d); +const int _52 = (c & d); +layout(constant_id = 7) const bool g = false; +layout(constant_id = 8) const bool h = true; +const bool _58 = (g || h); +const bool _60 = (g && h); +const bool _62 = (!g); +const bool _64 = (g == h); +const bool _66 = (g != h); +const bool _68 = (c == d); +const bool _70 = (c != d); +const bool _72 = (c < d); +const bool _74 = (e < f); +const bool _76 = (c > d); +const bool _78 = (e > f); +const bool _80 = (c <= d); +const bool _82 = (e <= f); +const bool _84 = (c >= d); +const bool _86 = (e >= f); +const int _92 = int(e + 0u); +const bool _94 = (c != int(0u)); +const bool _96 = (e != 0u); +const int _100 = int(g); +const uint _103 = uint(g); +const int _118 = (c + 3); +const int _127 = (c + 2); +const int _135 = (d + 2); + +struct Foo +{ + float elems[_135]; +}; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + float t0 = a; + float t1 = b; + mediump uint c0 = _18; + mediump int c1 = _21; + mediump int c2 = _23; + mediump int c3 = _26; + mediump int c4 = _28; + mediump int c5 = _30; + mediump int c6 = _32; + mediump uint c7 = _36; + mediump int c8 = _38; + mediump uint c9 = _40; + mediump int c10 = _42; + mediump uint c11 = _44; + mediump int c12 = _46; + mediump int c13 = _48; + mediump int c14 = _50; + mediump int c15 = _52; + bool c16 = _58; + bool c17 = _60; + bool c18 = _62; + bool c19 = _64; + bool c20 = _66; + bool c21 = _68; + bool c22 = _70; + bool c23 = _72; + bool c24 = _74; + bool c25 = _76; + bool c26 = _78; + bool c27 = _80; + bool c28 = _82; + bool c29 = _84; + bool c30 = _86; + mediump int c31 = c8 + c3; + mediump int c32 = _92; + bool c33 = _94; + bool c34 = _96; + mediump int c35 = _100; + mediump uint c36 = _103; + float c37 = float(g); + float vec0[_118][8]; + float vec1[_127]; + Foo foo; + FragColor = ((vec4(t0 + t1) + vec4(vec0[0][0])) + vec4(vec1[0])) + vec4(foo.elems[c]); +} + diff --git a/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/ubo-offset-out-of-order.vk.nocompat.frag.vk b/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/ubo-offset-out-of-order.vk.nocompat.frag.vk new file mode 100644 index 0000000..380b746 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-no-opt/vulkan/frag/ubo-offset-out-of-order.vk.nocompat.frag.vk @@ -0,0 +1,16 @@ +#version 450 + +layout(set = 0, binding = 0, std140) uniform UBO +{ + layout(offset = 16) mat4 m; + layout(offset = 0) vec4 v; +} _13; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = (_13.m * vColor) + _13.v; +} + diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json b/third_party/spirv-cross/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json new file mode 100644 index 0000000..666167a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/aliased-entry-point-names.asm.multi.json @@ -0,0 +1,55 @@ +{ + "entryPoints" : [ + { + "name" : "maim", + "mode" : "vert" + }, + { + "name" : "main", + "mode" : "vert" + }, + { + "name" : "maim", + "mode" : "frag" + }, + { + "name" : "main", + "mode" : "frag" + } + ], + "types" : { + "_8" : { + "name" : "_8", + "members" : [ + { + "name" : "_m0", + "type" : "vec4" + }, + { + "name" : "_m1", + "type" : "float" + }, + { + "name" : "_m2", + "type" : "float", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ] + }, + { + "name" : "_m3", + "type" : "float", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ] + } + ] + } + } +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/comp/pointer-to-array-of-physical-pointer.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/comp/pointer-to-array-of-physical-pointer.asm.comp.json new file mode 100644 index 0000000..b9224ec --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/comp/pointer-to-array-of-physical-pointer.asm.comp.json @@ -0,0 +1,71 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_3" : { + "name" : "Params", + "members" : [ + { + "name" : "x", + "type" : "float", + "offset" : 0 + }, + { + "name" : "y", + "type" : "_6", + "offset" : 16, + "physical_pointer" : true + } + ] + }, + "_4" : { + "name" : "IntBuf", + "members" : [ + { + "name" : "v", + "type" : "int", + "offset" : 0 + } + ] + }, + "_11" : { + "name" : "IntBuf", + "type" : "_4", + "physical_pointer" : true + }, + "_6" : { + "name" : "IntBuf", + "array" : [ + 3 + ], + "array_size_is_literal" : [ + true + ], + "type" : "_11", + "array_stride" : 16 + } + }, + "ubos" : [ + { + "type" : "_3", + "name" : "Params", + "block_size" : 24, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-glsl-ssbo-1.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-glsl-ssbo-1.asm.comp.json new file mode 100644 index 0000000..3b0c986 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-glsl-ssbo-1.asm.comp.json @@ -0,0 +1,46 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_5" : { + "name" : "SSBO0", + "members" : [ + { + "name" : "a", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_5", + "name" : "SSBO0", + "block_size" : 0, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-glsl-ssbo-2.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-glsl-ssbo-2.asm.comp.json new file mode 100644 index 0000000..80cf862 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-glsl-ssbo-2.asm.comp.json @@ -0,0 +1,70 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_4" : { + "name" : "SSBO0", + "members" : [ + { + "name" : "a", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + }, + "_6" : { + "name" : "SSBO1", + "members" : [ + { + "name" : "b", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_4", + "name" : "SSBO0", + "block_size" : 0, + "set" : 0, + "binding" : 0 + }, + { + "type" : "_6", + "name" : "SSBO1", + "block_size" : 0, + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-hlsl-uav-1.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-hlsl-uav-1.asm.comp.json new file mode 100644 index 0000000..b34f85b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-hlsl-uav-1.asm.comp.json @@ -0,0 +1,46 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_4" : { + "name" : "UAV0", + "members" : [ + { + "name" : "@data", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_4", + "name" : "UAV0", + "block_size" : 0, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-hlsl-uav-2.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-hlsl-uav-2.asm.comp.json new file mode 100644 index 0000000..052e3ba --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-hlsl-uav-2.asm.comp.json @@ -0,0 +1,53 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_4" : { + "name" : "UAV0", + "members" : [ + { + "name" : "@data", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_4", + "name" : "UAV0", + "block_size" : 0, + "set" : 0, + "binding" : 0 + }, + { + "type" : "_4", + "name" : "UAV1", + "block_size" : 0, + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-ssbo-1.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-ssbo-1.asm.comp.json new file mode 100644 index 0000000..3b0c986 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-ssbo-1.asm.comp.json @@ -0,0 +1,46 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_5" : { + "name" : "SSBO0", + "members" : [ + { + "name" : "a", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_5", + "name" : "SSBO0", + "block_size" : 0, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-ssbo-2.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-ssbo-2.asm.comp.json new file mode 100644 index 0000000..80cf862 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-ssbo-2.asm.comp.json @@ -0,0 +1,70 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_4" : { + "name" : "SSBO0", + "members" : [ + { + "name" : "a", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + }, + "_6" : { + "name" : "SSBO1", + "members" : [ + { + "name" : "b", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_4", + "name" : "SSBO0", + "block_size" : 0, + "set" : 0, + "binding" : 0 + }, + { + "type" : "_6", + "name" : "SSBO1", + "block_size" : 0, + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-uav-1.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-uav-1.asm.comp.json new file mode 100644 index 0000000..b34f85b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-uav-1.asm.comp.json @@ -0,0 +1,46 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_4" : { + "name" : "UAV0", + "members" : [ + { + "name" : "@data", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_4", + "name" : "UAV0", + "block_size" : 0, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-uav-2.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-uav-2.asm.comp.json new file mode 100644 index 0000000..052e3ba --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/asm/op-source-none-uav-2.asm.comp.json @@ -0,0 +1,53 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_4" : { + "name" : "UAV0", + "members" : [ + { + "name" : "@data", + "type" : "vec4", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_4", + "name" : "UAV0", + "block_size" : 0, + "set" : 0, + "binding" : 0 + }, + { + "type" : "_4", + "name" : "UAV1", + "block_size" : 0, + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/comp/array-of-physical-pointer.comp.json b/third_party/spirv-cross/reference/shaders-reflection/comp/array-of-physical-pointer.comp.json new file mode 100644 index 0000000..a5da58c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/comp/array-of-physical-pointer.comp.json @@ -0,0 +1,66 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_11" : { + "name" : "Params", + "members" : [ + { + "name" : "x", + "type" : "float", + "offset" : 0 + }, + { + "name" : "y", + "type" : "_7", + "array" : [ + 3 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 16, + "array_stride" : 16 + } + ] + }, + "_13" : { + "name" : "IntBuf", + "members" : [ + { + "name" : "v", + "type" : "int", + "offset" : 0 + } + ] + }, + "_7" : { + "name" : "IntBuf", + "type" : "_13", + "physical_pointer" : true + } + }, + "ubos" : [ + { + "type" : "_11", + "name" : "Params", + "block_size" : 64, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/comp/function-pointer.invalid.asm.comp.json b/third_party/spirv-cross/reference/shaders-reflection/comp/function-pointer.invalid.asm.comp.json new file mode 100644 index 0000000..bed5945 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/comp/function-pointer.invalid.asm.comp.json @@ -0,0 +1,18 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/comp/physical-pointer.comp.json b/third_party/spirv-cross/reference/shaders-reflection/comp/physical-pointer.comp.json new file mode 100644 index 0000000..a397d0f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/comp/physical-pointer.comp.json @@ -0,0 +1,55 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_8" : { + "name" : "Params", + "members" : [ + { + "name" : "x", + "type" : "float", + "offset" : 0 + }, + { + "name" : "y", + "type" : "_10", + "offset" : 8, + "physical_pointer" : true + } + ] + }, + "_10" : { + "name" : "IntBuf", + "members" : [ + { + "name" : "v", + "type" : "int", + "offset" : 0 + } + ] + } + }, + "ubos" : [ + { + "type" : "_8", + "name" : "Params", + "block_size" : 16, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/comp/struct-layout.comp.json b/third_party/spirv-cross/reference/shaders-reflection/comp/struct-layout.comp.json new file mode 100644 index 0000000..e9bf7ee --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/comp/struct-layout.comp.json @@ -0,0 +1,83 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_19" : { + "name" : "Foo", + "members" : [ + { + "name" : "m", + "type" : "mat4", + "offset" : 0, + "matrix_stride" : 16 + } + ] + }, + "_21" : { + "name" : "SSBO2", + "members" : [ + { + "name" : "out_data", + "type" : "_19", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 64 + } + ] + }, + "_28" : { + "name" : "SSBO", + "members" : [ + { + "name" : "in_data", + "type" : "_19", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 64 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_21", + "name" : "SSBO2", + "writeonly" : true, + "block_size" : 0, + "set" : 0, + "binding" : 1 + }, + { + "type" : "_28", + "name" : "SSBO", + "readonly" : true, + "block_size" : 0, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/comp/struct-packing.comp.json b/third_party/spirv-cross/reference/shaders-reflection/comp/struct-packing.comp.json new file mode 100644 index 0000000..12285ae --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/comp/struct-packing.comp.json @@ -0,0 +1,590 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 1, + 1, + 1 + ], + "workgroup_size_is_spec_constant_id" : [ + false, + false, + false + ] + } + ], + "types" : { + "_11" : { + "name" : "S0", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 8 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_14" : { + "name" : "S1", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 12 + } + ] + }, + "_17" : { + "name" : "S2", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_19" : { + "name" : "S3", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_20" : { + "name" : "S4", + "members" : [ + { + "name" : "c", + "type" : "vec2", + "offset" : 0 + } + ] + }, + "_23" : { + "name" : "Content", + "members" : [ + { + "name" : "m0s", + "type" : "_11", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + }, + { + "name" : "m1s", + "type" : "_14", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 16, + "array_stride" : 16 + }, + { + "name" : "m2s", + "type" : "_17", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 32, + "array_stride" : 32 + }, + { + "name" : "m0", + "type" : "_11", + "offset" : 64 + }, + { + "name" : "m1", + "type" : "_14", + "offset" : 80 + }, + { + "name" : "m2", + "type" : "_17", + "offset" : 96 + }, + { + "name" : "m3", + "type" : "_19", + "offset" : 128 + }, + { + "name" : "m4", + "type" : "float", + "offset" : 144 + }, + { + "name" : "m3s", + "type" : "_20", + "array" : [ + 8 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 152, + "array_stride" : 8 + } + ] + }, + "_36" : { + "name" : "SSBO1", + "members" : [ + { + "name" : "content", + "type" : "_23", + "offset" : 0 + }, + { + "name" : "content1", + "type" : "_23", + "array" : [ + 2 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 224, + "array_stride" : 224 + }, + { + "name" : "content2", + "type" : "_23", + "offset" : 672 + }, + { + "name" : "m0", + "type" : "mat2", + "offset" : 896, + "matrix_stride" : 8 + }, + { + "name" : "m1", + "type" : "mat2", + "offset" : 912, + "matrix_stride" : 8 + }, + { + "name" : "m2", + "type" : "mat2x3", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 928, + "array_stride" : 32, + "matrix_stride" : 16 + }, + { + "name" : "m3", + "type" : "mat3x2", + "offset" : 1056, + "matrix_stride" : 8 + }, + { + "name" : "m4", + "type" : "mat2", + "offset" : 1080, + "matrix_stride" : 8, + "row_major" : true + }, + { + "name" : "m5", + "type" : "mat2", + "array" : [ + 9 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 1096, + "array_stride" : 16, + "matrix_stride" : 8, + "row_major" : true + }, + { + "name" : "m6", + "type" : "mat2x3", + "array" : [ + 2, + 4 + ], + "array_size_is_literal" : [ + true, + true + ], + "offset" : 1240, + "array_stride" : 48, + "matrix_stride" : 8, + "row_major" : true + }, + { + "name" : "m7", + "type" : "mat3x2", + "offset" : 1440, + "matrix_stride" : 16, + "row_major" : true + }, + { + "name" : "array", + "type" : "float", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 1472, + "array_stride" : 4 + } + ] + }, + "_42" : { + "name" : "S0", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_44" : { + "name" : "S1", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 12 + } + ] + }, + "_47" : { + "name" : "S2", + "members" : [ + { + "name" : "a", + "type" : "vec3", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + }, + { + "name" : "b", + "type" : "float", + "offset" : 16 + } + ] + }, + "_49" : { + "name" : "S3", + "members" : [ + { + "name" : "a", + "type" : "vec2", + "offset" : 0 + }, + { + "name" : "b", + "type" : "float", + "offset" : 8 + } + ] + }, + "_50" : { + "name" : "S4", + "members" : [ + { + "name" : "c", + "type" : "vec2", + "offset" : 0 + } + ] + }, + "_52" : { + "name" : "Content", + "members" : [ + { + "name" : "m0s", + "type" : "_42", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 32 + }, + { + "name" : "m1s", + "type" : "_44", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 32, + "array_stride" : 16 + }, + { + "name" : "m2s", + "type" : "_47", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 48, + "array_stride" : 32 + }, + { + "name" : "m0", + "type" : "_42", + "offset" : 80 + }, + { + "name" : "m1", + "type" : "_44", + "offset" : 112 + }, + { + "name" : "m2", + "type" : "_47", + "offset" : 128 + }, + { + "name" : "m3", + "type" : "_49", + "offset" : 160 + }, + { + "name" : "m4", + "type" : "float", + "offset" : 176 + }, + { + "name" : "m3s", + "type" : "_50", + "array" : [ + 8 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 192, + "array_stride" : 16 + } + ] + }, + "_59" : { + "name" : "SSBO0", + "members" : [ + { + "name" : "content", + "type" : "_52", + "offset" : 0 + }, + { + "name" : "content1", + "type" : "_52", + "array" : [ + 2 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 320, + "array_stride" : 320 + }, + { + "name" : "content2", + "type" : "_52", + "offset" : 960 + }, + { + "name" : "m0", + "type" : "mat2", + "offset" : 1280, + "matrix_stride" : 16 + }, + { + "name" : "m1", + "type" : "mat2", + "offset" : 1312, + "matrix_stride" : 16 + }, + { + "name" : "m2", + "type" : "mat2x3", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 1344, + "array_stride" : 32, + "matrix_stride" : 16 + }, + { + "name" : "m3", + "type" : "mat3x2", + "offset" : 1472, + "matrix_stride" : 16 + }, + { + "name" : "m4", + "type" : "mat2", + "offset" : 1520, + "matrix_stride" : 16, + "row_major" : true + }, + { + "name" : "m5", + "type" : "mat2", + "array" : [ + 9 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 1552, + "array_stride" : 32, + "matrix_stride" : 16, + "row_major" : true + }, + { + "name" : "m6", + "type" : "mat2x3", + "array" : [ + 2, + 4 + ], + "array_size_is_literal" : [ + true, + true + ], + "offset" : 1840, + "array_stride" : 96, + "matrix_stride" : 16, + "row_major" : true + }, + { + "name" : "m7", + "type" : "mat3x2", + "offset" : 2224, + "matrix_stride" : 16, + "row_major" : true + }, + { + "name" : "array", + "type" : "float", + "array" : [ + 0 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 2256, + "array_stride" : 16 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_36", + "name" : "SSBO1", + "restrict" : true, + "block_size" : 1472, + "set" : 0, + "binding" : 1 + }, + { + "type" : "_59", + "name" : "SSBO0", + "restrict" : true, + "block_size" : 2256, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/comp/workgroup-size-spec-constant.comp.json b/third_party/spirv-cross/reference/shaders-reflection/comp/workgroup-size-spec-constant.comp.json new file mode 100644 index 0000000..c67d723 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/comp/workgroup-size-spec-constant.comp.json @@ -0,0 +1,62 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "comp", + "workgroup_size" : [ + 10, + 40, + 60 + ], + "workgroup_size_is_spec_constant_id" : [ + true, + true, + true + ] + } + ], + "types" : { + "_8" : { + "name" : "SSBO", + "members" : [ + { + "name" : "v", + "type" : "vec4", + "offset" : 0 + } + ] + } + }, + "ssbos" : [ + { + "type" : "_8", + "name" : "SSBO", + "block_size" : 16, + "set" : 0, + "binding" : 0 + } + ], + "specialization_constants" : [ + { + "name" : "", + "id" : 10, + "type" : "uint", + "variable_id" : 18, + "default_value" : 1 + }, + { + "name" : "", + "id" : 40, + "type" : "uint", + "variable_id" : 19, + "default_value" : 1 + }, + { + "name" : "", + "id" : 60, + "type" : "uint", + "variable_id" : 20, + "default_value" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json b/third_party/spirv-cross/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json new file mode 100644 index 0000000..5b4d3c6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag.json @@ -0,0 +1,37 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "outputs" : [ + { + "type" : "float", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uDepth", + "set" : 0, + "binding" : 2 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler", + "set" : 0, + "binding" : 0 + }, + { + "type" : "sampler", + "name" : "uSampler1", + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json b/third_party/spirv-cross/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json new file mode 100644 index 0000000..8b6a184 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/frag/combined-texture-sampler.vk.frag.json @@ -0,0 +1,50 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "inputs" : [ + { + "type" : "vec2", + "name" : "vTex", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uTexture0", + "set" : 0, + "binding" : 2 + }, + { + "type" : "texture2D", + "name" : "uTexture1", + "set" : 0, + "binding" : 3 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler0", + "set" : 0, + "binding" : 0 + }, + { + "type" : "sampler", + "name" : "uSampler1", + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json b/third_party/spirv-cross/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json new file mode 100644 index 0000000..c239527 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/frag/image-load-store-uint-coord.asm.frag.json @@ -0,0 +1,47 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "@entryPointOutput", + "location" : 0 + } + ], + "textures" : [ + { + "type" : "sampler2D", + "name" : "ROIm", + "set" : 0, + "binding" : 1 + } + ], + "separate_images" : [ + { + "type" : "samplerBuffer", + "name" : "ROBuf", + "set" : 0, + "binding" : 0 + } + ], + "images" : [ + { + "type" : "image2D", + "name" : "RWIm", + "set" : 0, + "binding" : 1, + "format" : "rgba32f" + }, + { + "type" : "imageBuffer", + "name" : "RWBuf", + "set" : 0, + "binding" : 0, + "format" : "rgba32f" + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json b/third_party/spirv-cross/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json new file mode 100644 index 0000000..5f38191 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/frag/input-attachment-ms.vk.frag.json @@ -0,0 +1,31 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "subpass_inputs" : [ + { + "type" : "subpassInputMS", + "name" : "uSubpass0", + "set" : 0, + "binding" : 0, + "input_attachment_index" : 0 + }, + { + "type" : "subpassInputMS", + "name" : "uSubpass1", + "set" : 0, + "binding" : 1, + "input_attachment_index" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/frag/input-attachment.vk.frag.json b/third_party/spirv-cross/reference/shaders-reflection/frag/input-attachment.vk.frag.json new file mode 100644 index 0000000..16ae6a4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/frag/input-attachment.vk.frag.json @@ -0,0 +1,31 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "subpass_inputs" : [ + { + "type" : "subpassInput", + "name" : "uSubpass0", + "set" : 0, + "binding" : 0, + "input_attachment_index" : 0 + }, + { + "type" : "subpassInput", + "name" : "uSubpass1", + "set" : 0, + "binding" : 1, + "input_attachment_index" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/frag/push-constant.vk.frag.json b/third_party/spirv-cross/reference/shaders-reflection/frag/push-constant.vk.frag.json new file mode 100644 index 0000000..f72a8fd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/frag/push-constant.vk.frag.json @@ -0,0 +1,46 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "types" : { + "_13" : { + "name" : "PushConstants", + "members" : [ + { + "name" : "value0", + "type" : "vec4", + "offset" : 0 + }, + { + "name" : "value1", + "type" : "vec4", + "offset" : 16 + } + ] + } + }, + "inputs" : [ + { + "type" : "vec4", + "name" : "vColor", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "push_constants" : [ + { + "type" : "_13", + "name" : "push", + "push_constant" : true + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json b/third_party/spirv-cross/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json new file mode 100644 index 0000000..e5f2f75 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/frag/separate-sampler-texture-array.vk.frag.json @@ -0,0 +1,85 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "inputs" : [ + { + "type" : "vec2", + "name" : "vTex", + "location" : 0 + }, + { + "type" : "vec3", + "name" : "vTex3", + "location" : 1 + } + ], + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "separate_images" : [ + { + "type" : "texture2D", + "name" : "uTexture", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "set" : 0, + "binding" : 1 + }, + { + "type" : "texture2DArray", + "name" : "uTextureArray", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "set" : 0, + "binding" : 4 + }, + { + "type" : "textureCube", + "name" : "uTextureCube", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "set" : 0, + "binding" : 3 + }, + { + "type" : "texture3D", + "name" : "uTexture3D", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "set" : 0, + "binding" : 2 + } + ], + "separate_samplers" : [ + { + "type" : "sampler", + "name" : "uSampler", + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/frag/spec-constant.vk.frag.json b/third_party/spirv-cross/reference/shaders-reflection/frag/spec-constant.vk.frag.json new file mode 100644 index 0000000..dd876dd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/frag/spec-constant.vk.frag.json @@ -0,0 +1,90 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "frag" + } + ], + "types" : { + "_137" : { + "name" : "Foo", + "members" : [ + { + "name" : "elems", + "type" : "float", + "array" : [ + 135 + ], + "array_size_is_literal" : [ + false + ] + } + ] + } + }, + "outputs" : [ + { + "type" : "vec4", + "name" : "FragColor", + "location" : 0 + } + ], + "specialization_constants" : [ + { + "name" : "a", + "id" : 1, + "type" : "float", + "variable_id" : 9, + "default_value" : 1.5 + }, + { + "name" : "b", + "id" : 2, + "type" : "float", + "variable_id" : 11, + "default_value" : 2.5 + }, + { + "name" : "c", + "id" : 3, + "type" : "int", + "variable_id" : 16, + "default_value" : 3 + }, + { + "name" : "d", + "id" : 4, + "type" : "int", + "variable_id" : 25, + "default_value" : 4 + }, + { + "name" : "e", + "id" : 5, + "type" : "uint", + "variable_id" : 34, + "default_value" : 5 + }, + { + "name" : "f", + "id" : 6, + "type" : "uint", + "variable_id" : 35, + "default_value" : 6 + }, + { + "name" : "g", + "id" : 7, + "type" : "bool", + "variable_id" : 56, + "default_value" : false + }, + { + "name" : "h", + "id" : 8, + "type" : "bool", + "variable_id" : 57, + "default_value" : true + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/rgen/acceleration_structure.vk.rgen.json b/third_party/spirv-cross/reference/shaders-reflection/rgen/acceleration_structure.vk.rgen.json new file mode 100644 index 0000000..6574f75 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/rgen/acceleration_structure.vk.rgen.json @@ -0,0 +1,16 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "rgen" + } + ], + "acceleration_structures" : [ + { + "type" : "accelerationStructureNV", + "name" : "as", + "set" : 0, + "binding" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/vert/array-size-reflection.vert.json b/third_party/spirv-cross/reference/shaders-reflection/vert/array-size-reflection.vert.json new file mode 100644 index 0000000..87b6cb0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/vert/array-size-reflection.vert.json @@ -0,0 +1,78 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + } + ], + "types" : { + "_11" : { + "name" : "gl_PerVertex", + "members" : [ + { + "name" : "gl_Position", + "type" : "vec4" + }, + { + "name" : "gl_PointSize", + "type" : "float" + }, + { + "name" : "gl_ClipDistance", + "type" : "float", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ] + }, + { + "name" : "gl_CullDistance", + "type" : "float", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ] + } + ] + }, + "_18" : { + "name" : "u_", + "members" : [ + { + "name" : "u_0", + "type" : "vec4", + "array" : [ + 16 + ], + "array_size_is_literal" : [ + false + ], + "offset" : 0, + "array_stride" : 16 + } + ] + } + }, + "ubos" : [ + { + "type" : "_18", + "name" : "u_", + "block_size" : 16, + "set" : 1, + "binding" : 0 + } + ], + "specialization_constants" : [ + { + "name" : "ARR_SIZE", + "id" : 0, + "type" : "int", + "variable_id" : 16, + "default_value" : 1 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/vert/read-from-row-major-array.vert.json b/third_party/spirv-cross/reference/shaders-reflection/vert/read-from-row-major-array.vert.json new file mode 100644 index 0000000..cebd66b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/vert/read-from-row-major-array.vert.json @@ -0,0 +1,67 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + } + ], + "types" : { + "_89" : { + "name" : "gl_PerVertex", + "members" : [ + { + "name" : "gl_Position", + "type" : "vec4" + }, + { + "name" : "gl_PointSize", + "type" : "float" + } + ] + }, + "_102" : { + "name" : "Block", + "members" : [ + { + "name" : "var", + "type" : "mat2x3", + "array" : [ + 4, + 3 + ], + "array_size_is_literal" : [ + true, + true + ], + "offset" : 0, + "array_stride" : 192, + "matrix_stride" : 16, + "row_major" : true + } + ] + } + }, + "inputs" : [ + { + "type" : "vec4", + "name" : "a_position", + "location" : 0 + } + ], + "outputs" : [ + { + "type" : "float", + "name" : "v_vtxResult", + "location" : 0 + } + ], + "ubos" : [ + { + "type" : "_102", + "name" : "Block", + "block_size" : 576, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/vert/stride-reflection.vert.json b/third_party/spirv-cross/reference/shaders-reflection/vert/stride-reflection.vert.json new file mode 100644 index 0000000..1dd8f18 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/vert/stride-reflection.vert.json @@ -0,0 +1,96 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + } + ], + "types" : { + "_11" : { + "name" : "gl_PerVertex", + "members" : [ + { + "name" : "gl_Position", + "type" : "vec4" + }, + { + "name" : "gl_PointSize", + "type" : "float" + }, + { + "name" : "gl_ClipDistance", + "type" : "float", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ] + }, + { + "name" : "gl_CullDistance", + "type" : "float", + "array" : [ + 1 + ], + "array_size_is_literal" : [ + true + ] + } + ] + }, + "_21" : { + "name" : "U", + "members" : [ + { + "name" : "v", + "type" : "vec4", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 0, + "array_stride" : 16 + }, + { + "name" : "c", + "type" : "mat4", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 64, + "array_stride" : 64, + "matrix_stride" : 16 + }, + { + "name" : "r", + "type" : "mat4", + "array" : [ + 4 + ], + "array_size_is_literal" : [ + true + ], + "offset" : 320, + "array_stride" : 64, + "matrix_stride" : 16, + "row_major" : true + } + ] + } + }, + "ubos" : [ + { + "type" : "_21", + "name" : "U", + "block_size" : 576, + "set" : 0, + "binding" : 0 + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-reflection/vert/texture_buffer.vert.json b/third_party/spirv-cross/reference/shaders-reflection/vert/texture_buffer.vert.json new file mode 100644 index 0000000..3c69e24 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-reflection/vert/texture_buffer.vert.json @@ -0,0 +1,40 @@ +{ + "entryPoints" : [ + { + "name" : "main", + "mode" : "vert" + } + ], + "types" : { + "_8" : { + "name" : "gl_PerVertex", + "members" : [ + { + "name" : "gl_Position", + "type" : "vec4" + }, + { + "name" : "gl_PointSize", + "type" : "float" + } + ] + } + }, + "textures" : [ + { + "type" : "samplerBuffer", + "name" : "uSamp", + "set" : 0, + "binding" : 4 + } + ], + "images" : [ + { + "type" : "imageBuffer", + "name" : "uSampo", + "set" : 0, + "binding" : 5, + "format" : "rgba32f" + } + ] +} \ No newline at end of file diff --git a/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/accesschain-invalid-expression.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/accesschain-invalid-expression.asm.invalid.frag new file mode 100644 index 0000000..3a13024 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/accesschain-invalid-expression.asm.invalid.frag @@ -0,0 +1,369 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_MobileDirectionalLight +{ + float4 MobileDirectionalLight_DirectionalLightColor; + float4 MobileDirectionalLight_DirectionalLightDirectionAndShadowTransition; + float4 MobileDirectionalLight_DirectionalLightShadowSize; + float4 MobileDirectionalLight_DirectionalLightDistanceFadeMAD; + float4 MobileDirectionalLight_DirectionalLightShadowDistances; + float4x4 MobileDirectionalLight_DirectionalLightScreenToShadow[4]; +}; + +struct type_Globals +{ + int NumDynamicPointLights; + float4 LightPositionAndInvRadius[4]; + float4 LightColorAndFalloffExponent[4]; + float4 MobileReflectionParams; +}; + +constant float3 _136 = {}; +constant float4 _137 = {}; +constant float _138 = {}; +constant float3 _139 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0)]]; + float4 in_var_TEXCOORD7 [[user(locn1)]]; + float4 in_var_TEXCOORD8 [[user(locn2)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_MobileDirectionalLight& MobileDirectionalLight [[buffer(1)]], constant type_Globals& _Globals [[buffer(2)]], texture2d MobileDirectionalLight_DirectionalLightShadowTexture [[texture(0)]], texture2d Material_Texture2D_0 [[texture(1)]], texture2d Material_Texture2D_1 [[texture(2)]], texturecube ReflectionCubemap [[texture(3)]], sampler MobileDirectionalLight_DirectionalLightShadowSampler [[sampler(0)]], sampler Material_Texture2D_0Sampler [[sampler(1)]], sampler Material_Texture2D_1Sampler [[sampler(2)]], sampler ReflectionCubemapSampler [[sampler(3)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float4 _177 = float4((((gl_FragCoord.xy - View.View_ViewRectMin.xy) * View.View_ViewSizeAndInvSize.zw) - float2(0.5)) * float2(2.0, -2.0), _138, 1.0) * float4(gl_FragCoord.w); + float3 _179 = in.in_var_TEXCOORD8.xyz - float3(View.View_PreViewTranslation); + float3 _181 = normalize(-in.in_var_TEXCOORD8.xyz); + float4 _187 = Material_Texture2D_0.sample(Material_Texture2D_0Sampler, (in.in_var_TEXCOORD0 * float2(10.0))); + float2 _190 = (_187.xy * float2(2.0)) - float2(1.0); + float3 _206 = normalize(float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 1.0, 0.0), float3(0.0, 0.0, 1.0)) * (((float4(_190, sqrt(fast::clamp(1.0 - dot(_190, _190), 0.0, 1.0)), 1.0).xyz * float3(0.300000011920928955078125, 0.300000011920928955078125, 1.0)) * float3(View.View_NormalOverrideParameter.w)) + View.View_NormalOverrideParameter.xyz)); + float _208 = dot(_206, _181); + float4 _217 = Material_Texture2D_1.sample(Material_Texture2D_1Sampler, (in.in_var_TEXCOORD0 * float2(20.0))); + float _219 = mix(0.4000000059604644775390625, 1.0, _217.x); + float4 _223 = Material_Texture2D_1.sample(Material_Texture2D_1Sampler, (in.in_var_TEXCOORD0 * float2(5.0))); + float _224 = _177.w; + float _228 = fast::min(fast::max((_224 - 24.0) * 0.000666666659526526927947998046875, 0.0), 1.0); + float _229 = _223.y; + float4 _233 = Material_Texture2D_1.sample(Material_Texture2D_1Sampler, (in.in_var_TEXCOORD0 * float2(0.5))); + float _235 = _233.y; + float _253 = fast::clamp((fast::min(fast::max(mix(0.0, 0.5, _235) + mix(mix(0.699999988079071044921875, 1.0, _229), 1.0, _228), 0.0), 1.0) * View.View_RoughnessOverrideParameter.y) + View.View_RoughnessOverrideParameter.x, 0.119999997317790985107421875, 1.0); + float2 _257 = (float2(_253) * float2(-1.0, -0.0274999998509883880615234375)) + float2(1.0, 0.0425000004470348358154296875); + float _258 = _257.x; + float3 _270 = (fast::clamp(float3(mix(_219, 1.0 - _219, mix(_229, 1.0, _228)) * (mix(0.2949999868869781494140625, 0.660000026226043701171875, mix(_235 + mix(_229, 0.0, _228), 0.5, 0.5)) * 0.5)), float3(0.0), float3(1.0)) * float3(View.View_DiffuseOverrideParameter.w)) + View.View_DiffuseOverrideParameter.xyz; + float3 _275 = float3(((fast::min(_258 * _258, exp2((-9.27999973297119140625) * fast::max(_208, 0.0))) * _258) + _257.y) * View.View_SpecularOverrideParameter.w) + View.View_SpecularOverrideParameter.xyz; + float _276 = _275.x; + float4 _303; + int _286 = 0; + for (;;) + { + if (_286 < 2) + { + if (_224 < MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowDistances[uint(_286)]) + { + _303 = MobileDirectionalLight.MobileDirectionalLight_DirectionalLightScreenToShadow[_286] * float4(_177.xy, _224, 1.0); + break; + } + _286++; + continue; + } + else + { + _303 = float4(0.0); + break; + } + } + float _423; + if (_303.z > 0.0) + { + float2 _311 = _303.xy * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.xy; + float2 _312 = fract(_311); + float2 _313 = floor(_311); + float3 _320 = _139; + _320.x = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(-0.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _326 = _320; + _326.y = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(0.5, -0.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _332 = _326; + _332.z = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(1.5, -0.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _335 = float3(MobileDirectionalLight.MobileDirectionalLight_DirectionalLightDirectionAndShadowTransition.w); + float3 _337 = float3((fast::min(_303.z, 0.999989986419677734375) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightDirectionAndShadowTransition.w) - 1.0); + float3 _339 = fast::clamp((_332 * _335) - _337, float3(0.0), float3(1.0)); + float3 _345 = _139; + _345.x = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(-0.5, 0.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _351 = _345; + _351.y = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(0.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _357 = _351; + _357.z = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(1.5, 0.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _360 = fast::clamp((_357 * _335) - _337, float3(0.0), float3(1.0)); + float3 _366 = _139; + _366.x = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(-0.5, 1.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _372 = _366; + _372.y = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(0.5, 1.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _378 = _372; + _378.z = MobileDirectionalLight_DirectionalLightShadowTexture.sample(MobileDirectionalLight_DirectionalLightShadowSampler, ((_313 + float2(1.5)) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightShadowSize.zw), level(0.0)).x; + float3 _381 = fast::clamp((_378 * _335) - _337, float3(0.0), float3(1.0)); + float _383 = _312.x; + float _384 = 1.0 - _383; + float3 _399 = _136; + _399.x = ((_339.x * _384) + _339.y) + (_339.z * _383); + float3 _403 = _399; + _403.y = ((_360.x * _384) + _360.y) + (_360.z * _383); + float3 _407 = _403; + _407.z = ((_381.x * _384) + _381.y) + (_381.z * _383); + float _408 = _312.y; + float _420 = fast::clamp((_224 * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightDistanceFadeMAD.x) + MobileDirectionalLight.MobileDirectionalLight_DirectionalLightDistanceFadeMAD.y, 0.0, 1.0); + _423 = mix(fast::clamp(0.25 * dot(_407, float3(1.0 - _408, 1.0, _408)), 0.0, 1.0), 1.0, _420 * _420); + } + else + { + _423 = 1.0; + } + float3 _429 = normalize(_181 + MobileDirectionalLight.MobileDirectionalLight_DirectionalLightDirectionAndShadowTransition.xyz); + float _439 = (_253 * 0.25) + 0.25; + float3 _440 = cross(_206, _429); + float _442 = _253 * _253; + float _443 = fast::max(0.0, dot(_206, _429)) * _442; + float _446 = _442 / (dot(_440, _440) + (_443 * _443)); + bool _458 = float(_Globals.MobileReflectionParams.w > 0.0) != 0.0; + float4 _468 = ReflectionCubemap.sample(ReflectionCubemapSampler, ((-_181) + ((_206 * float3(_208)) * float3(2.0))), level(((_458 ? _Globals.MobileReflectionParams.w : View.View_ReflectionCubemapMaxMip) - 1.0) - (1.0 - (1.2000000476837158203125 * log2(_253))))); + float3 _481; + if (_458) + { + _481 = _468.xyz * View.View_SkyLightColor.xyz; + } + else + { + float3 _476 = _468.xyz * float3(_468.w * 16.0); + _481 = _476 * _476; + } + float3 _484 = float3(_276); + float3 _488; + _488 = ((float3(_423 * fast::max(0.0, dot(_206, MobileDirectionalLight.MobileDirectionalLight_DirectionalLightDirectionAndShadowTransition.xyz))) * MobileDirectionalLight.MobileDirectionalLight_DirectionalLightColor.xyz) * (_270 + float3(_276 * (_439 * fast::min(_446 * _446, 65504.0))))) + ((_481 * float3(fast::clamp(1.0, 0.0, 1.0))) * _484); + float3 _507; + float _509; + float _511; + float _537; + int _491 = 0; + for (;;) + { + if (_491 < _Globals.NumDynamicPointLights) + { + float3 _501 = _Globals.LightPositionAndInvRadius[_491].xyz - _179; + float _502 = dot(_501, _501); + float3 _505 = _501 * float3(rsqrt(_502)); + _507 = normalize(_181 + _505); + _509 = fast::max(0.0, dot(_206, _505)); + _511 = fast::max(0.0, dot(_206, _507)); + if (_Globals.LightColorAndFalloffExponent[_491].w == 0.0) + { + float _531 = _502 * (_Globals.LightPositionAndInvRadius[_491].w * _Globals.LightPositionAndInvRadius[_491].w); + float _534 = fast::clamp(1.0 - (_531 * _531), 0.0, 1.0); + _537 = (1.0 / (_502 + 1.0)) * (_534 * _534); + } + else + { + float3 _521 = _501 * float3(_Globals.LightPositionAndInvRadius[_491].w); + _537 = pow(1.0 - fast::clamp(dot(_521, _521), 0.0, 1.0), _Globals.LightColorAndFalloffExponent[_491].w); + } + float3 _544 = cross(_206, _507); + float _546 = _511 * _442; + float _549 = _442 / (dot(_544, _544) + (_546 * _546)); + _488 += fast::min(float3(65000.0), ((float3(_537 * _509) * _Globals.LightColorAndFalloffExponent[_491].xyz) * float3(0.3183098733425140380859375)) * (_270 + float3(_276 * (_439 * fast::min(_549 * _549, 65504.0))))); + _491++; + continue; + } + else + { + break; + } + } + float3 _567 = (mix(_488 + fast::max(float3(0.0), float3(0.0)), _270 + _484, float3(View.View_UnlitViewmodeMask)) * float3(in.in_var_TEXCOORD7.w)) + in.in_var_TEXCOORD7.xyz; + float4 _571 = float4(_567.x, _567.y, _567.z, _137.w); + _571.w = fast::min(in.in_var_TEXCOORD8.w, 65500.0); + out.out_var_SV_Target0 = _571; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/array-copy-error.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/array-copy-error.asm.invalid.frag new file mode 100644 index 0000000..bc40c7c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/array-copy-error.asm.invalid.frag @@ -0,0 +1,353 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_PrimitiveDither +{ + float PrimitiveDither_LODFactor; +}; + +struct type_PrimitiveFade +{ + float2 PrimitiveFade_FadeTimeScaleBias; +}; + +struct type_Material +{ + float4 Material_VectorExpressions[9]; + float4 Material_ScalarExpressions[3]; +}; + +constant float _98 = {}; +constant float _103 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; + float gl_FragDepth [[depth(less)]]; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD6 [[user(locn0)]]; + float4 in_var_TEXCOORD7 [[user(locn1)]]; + float4 in_var_TEXCOORD10_centroid [[user(locn2)]]; + float4 in_var_TEXCOORD11_centroid [[user(locn3)]]; + float4 in_var_TEXCOORD0_0 [[user(locn4)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_PrimitiveDither& PrimitiveDither [[buffer(1)]], constant type_PrimitiveFade& PrimitiveFade [[buffer(2)]], constant type_Material& Material [[buffer(3)]], texture2d Material_Texture2D_0 [[texture(0)]], texture2d Material_Texture2D_3 [[texture(1)]], sampler Material_Texture2D_0Sampler [[sampler(0)]], sampler Material_Texture2D_3Sampler [[sampler(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + spvUnsafeArray in_var_TEXCOORD0 = {}; + in_var_TEXCOORD0[0] = in.in_var_TEXCOORD0_0; + float2 _135 = gl_FragCoord.xy - View.View_ViewRectMin.xy; + float4 _140 = float4(_103, _103, gl_FragCoord.z, 1.0) * float4(gl_FragCoord.w); + float4 _144 = View.View_SVPositionToTranslatedWorld * float4(gl_FragCoord.xyz, 1.0); + float3 _148 = _144.xyz / float3(_144.w); + float3 _149 = _148 - float3(View.View_PreViewTranslation); + float3 _151 = normalize(-_148); + float3 _152 = _151 * float3x3(in.in_var_TEXCOORD10_centroid.xyz, cross(in.in_var_TEXCOORD11_centroid.xyz, in.in_var_TEXCOORD10_centroid.xyz) * float3(in.in_var_TEXCOORD11_centroid.w), in.in_var_TEXCOORD11_centroid.xyz); + float _170 = mix(Material.Material_ScalarExpressions[0].y, Material.Material_ScalarExpressions[0].z, fast::min(fast::max(abs(dot(_151, in.in_var_TEXCOORD11_centroid.xyz)), 0.0), 1.0)); + float _171 = floor(_170); + float _172 = 1.0 / _170; + float2 _174 = (float2(Material.Material_ScalarExpressions[0].x) * ((_152.xy * float2(-1.0)) / float2(_152.z))) * float2(_172); + float2 _175 = dfdx(float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y)); + float2 _176 = dfdy(float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y)); + float _180_copy; + float2 _183; + _183 = float2(0.0); + float _188; + float _211; + float2 _212; + float _180 = 1.0; + int _185 = 0; + float _187 = 1.0; + float _189 = 1.0; + for (;;) + { + if (float(_185) < (_171 + 2.0)) + { + _188 = Material_Texture2D_0.sample(Material_Texture2D_0Sampler, (float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y) + _183), gradient2d(_175, _176)).y; + if (_180 < _188) + { + float _201 = _188 - _180; + float _203 = _201 / ((_189 - _187) + _201); + _211 = (_189 * _203) + (_180 * (1.0 - _203)); + _212 = _183 - (float2(_203) * _174); + break; + } + _180_copy = _180; + _180 -= _172; + _183 += _174; + _185++; + _187 = _188; + _189 = _180_copy; + continue; + } + else + { + _211 = _98; + _212 = _183; + break; + } + } + float4 _218 = Material_Texture2D_0.sample(Material_Texture2D_0Sampler, (float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y) + _212.xy), bias(View.View_MaterialTextureMipBias)); + float2 _229 = _135 + float2(View.View_TemporalAAParams.x); + float _237 = float((uint(_229.x) + (2u * uint(_229.y))) % 5u); + float2 _238 = _135 * float2(0.015625); + float4 _242 = Material_Texture2D_3.sample(Material_Texture2D_3Sampler, _238, bias(View.View_MaterialTextureMipBias)); + float4 _254 = Material_Texture2D_3.sample(Material_Texture2D_3Sampler, _238, bias(View.View_MaterialTextureMipBias)); + float3 _272 = float3(_212, (1.0 - _211) * Material.Material_ScalarExpressions[0].x); + float2 _275 = dfdx(float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y)); + float2 _276 = abs(_275); + float3 _279 = dfdx(_149); + float2 _283 = dfdy(float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y)); + float2 _284 = abs(_283); + float3 _287 = dfdy(_149); + if (PrimitiveDither.PrimitiveDither_LODFactor != 0.0) + { + if (abs(PrimitiveDither.PrimitiveDither_LODFactor) > 0.001000000047497451305389404296875) + { + float _317 = fract(cos(dot(floor(gl_FragCoord.xy), float2(347.834503173828125, 3343.28369140625))) * 1000.0); + if ((float((PrimitiveDither.PrimitiveDither_LODFactor < 0.0) ? ((PrimitiveDither.PrimitiveDither_LODFactor + 1.0) > _317) : (PrimitiveDither.PrimitiveDither_LODFactor < _317)) - 0.001000000047497451305389404296875) < 0.0) + { + discard_fragment(); + } + } + } + if ((((_218.z + ((fast::min(fast::max(1.0 - (_218.x * Material.Material_ScalarExpressions[2].y), 0.0), 1.0) + ((_237 + (_242.x * Material.Material_ScalarExpressions[2].z)) * 0.16666667163372039794921875)) + (-0.5))) * ((fast::clamp((View.View_RealTime * PrimitiveFade.PrimitiveFade_FadeTimeScaleBias.x) + PrimitiveFade.PrimitiveFade_FadeTimeScaleBias.y, 0.0, 1.0) + ((_237 + _254.x) * 0.16666667163372039794921875)) + (-0.5))) - 0.33329999446868896484375) < 0.0) + { + discard_fragment(); + } + float2 _351 = ((((in.in_var_TEXCOORD6.xy / float2(in.in_var_TEXCOORD6.w)) - View.View_TemporalAAJitter.xy) - ((in.in_var_TEXCOORD7.xy / float2(in.in_var_TEXCOORD7.w)) - View.View_TemporalAAJitter.zw)) * float2(0.2495000064373016357421875)) + float2(0.49999237060546875); + out.gl_FragDepth = fast::min(_140.z / (_140.w + (sqrt(dot(_272, _272)) / (fast::max(sqrt(dot(_276, _276)) / sqrt(dot(_279, _279)), sqrt(dot(_284, _284)) / sqrt(dot(_287, _287))) / abs(dot(float3x3(View.View_ViewToTranslatedWorld[0].xyz, View.View_ViewToTranslatedWorld[1].xyz, View.View_ViewToTranslatedWorld[2].xyz) * float3(0.0, 0.0, 1.0), _151))))), gl_FragCoord.z); + out.out_var_SV_Target0 = float4(_351.x, _351.y, float2(0.0).x, float2(0.0).y); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/phi-variable-declaration.asm.invalid.frag b/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/phi-variable-declaration.asm.invalid.frag new file mode 100644 index 0000000..bc40c7c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/frag/phi-variable-declaration.asm.invalid.frag @@ -0,0 +1,353 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_PrimitiveDither +{ + float PrimitiveDither_LODFactor; +}; + +struct type_PrimitiveFade +{ + float2 PrimitiveFade_FadeTimeScaleBias; +}; + +struct type_Material +{ + float4 Material_VectorExpressions[9]; + float4 Material_ScalarExpressions[3]; +}; + +constant float _98 = {}; +constant float _103 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; + float gl_FragDepth [[depth(less)]]; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD6 [[user(locn0)]]; + float4 in_var_TEXCOORD7 [[user(locn1)]]; + float4 in_var_TEXCOORD10_centroid [[user(locn2)]]; + float4 in_var_TEXCOORD11_centroid [[user(locn3)]]; + float4 in_var_TEXCOORD0_0 [[user(locn4)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_PrimitiveDither& PrimitiveDither [[buffer(1)]], constant type_PrimitiveFade& PrimitiveFade [[buffer(2)]], constant type_Material& Material [[buffer(3)]], texture2d Material_Texture2D_0 [[texture(0)]], texture2d Material_Texture2D_3 [[texture(1)]], sampler Material_Texture2D_0Sampler [[sampler(0)]], sampler Material_Texture2D_3Sampler [[sampler(1)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + spvUnsafeArray in_var_TEXCOORD0 = {}; + in_var_TEXCOORD0[0] = in.in_var_TEXCOORD0_0; + float2 _135 = gl_FragCoord.xy - View.View_ViewRectMin.xy; + float4 _140 = float4(_103, _103, gl_FragCoord.z, 1.0) * float4(gl_FragCoord.w); + float4 _144 = View.View_SVPositionToTranslatedWorld * float4(gl_FragCoord.xyz, 1.0); + float3 _148 = _144.xyz / float3(_144.w); + float3 _149 = _148 - float3(View.View_PreViewTranslation); + float3 _151 = normalize(-_148); + float3 _152 = _151 * float3x3(in.in_var_TEXCOORD10_centroid.xyz, cross(in.in_var_TEXCOORD11_centroid.xyz, in.in_var_TEXCOORD10_centroid.xyz) * float3(in.in_var_TEXCOORD11_centroid.w), in.in_var_TEXCOORD11_centroid.xyz); + float _170 = mix(Material.Material_ScalarExpressions[0].y, Material.Material_ScalarExpressions[0].z, fast::min(fast::max(abs(dot(_151, in.in_var_TEXCOORD11_centroid.xyz)), 0.0), 1.0)); + float _171 = floor(_170); + float _172 = 1.0 / _170; + float2 _174 = (float2(Material.Material_ScalarExpressions[0].x) * ((_152.xy * float2(-1.0)) / float2(_152.z))) * float2(_172); + float2 _175 = dfdx(float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y)); + float2 _176 = dfdy(float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y)); + float _180_copy; + float2 _183; + _183 = float2(0.0); + float _188; + float _211; + float2 _212; + float _180 = 1.0; + int _185 = 0; + float _187 = 1.0; + float _189 = 1.0; + for (;;) + { + if (float(_185) < (_171 + 2.0)) + { + _188 = Material_Texture2D_0.sample(Material_Texture2D_0Sampler, (float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y) + _183), gradient2d(_175, _176)).y; + if (_180 < _188) + { + float _201 = _188 - _180; + float _203 = _201 / ((_189 - _187) + _201); + _211 = (_189 * _203) + (_180 * (1.0 - _203)); + _212 = _183 - (float2(_203) * _174); + break; + } + _180_copy = _180; + _180 -= _172; + _183 += _174; + _185++; + _187 = _188; + _189 = _180_copy; + continue; + } + else + { + _211 = _98; + _212 = _183; + break; + } + } + float4 _218 = Material_Texture2D_0.sample(Material_Texture2D_0Sampler, (float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y) + _212.xy), bias(View.View_MaterialTextureMipBias)); + float2 _229 = _135 + float2(View.View_TemporalAAParams.x); + float _237 = float((uint(_229.x) + (2u * uint(_229.y))) % 5u); + float2 _238 = _135 * float2(0.015625); + float4 _242 = Material_Texture2D_3.sample(Material_Texture2D_3Sampler, _238, bias(View.View_MaterialTextureMipBias)); + float4 _254 = Material_Texture2D_3.sample(Material_Texture2D_3Sampler, _238, bias(View.View_MaterialTextureMipBias)); + float3 _272 = float3(_212, (1.0 - _211) * Material.Material_ScalarExpressions[0].x); + float2 _275 = dfdx(float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y)); + float2 _276 = abs(_275); + float3 _279 = dfdx(_149); + float2 _283 = dfdy(float2(in_var_TEXCOORD0[0].x, in_var_TEXCOORD0[0].y)); + float2 _284 = abs(_283); + float3 _287 = dfdy(_149); + if (PrimitiveDither.PrimitiveDither_LODFactor != 0.0) + { + if (abs(PrimitiveDither.PrimitiveDither_LODFactor) > 0.001000000047497451305389404296875) + { + float _317 = fract(cos(dot(floor(gl_FragCoord.xy), float2(347.834503173828125, 3343.28369140625))) * 1000.0); + if ((float((PrimitiveDither.PrimitiveDither_LODFactor < 0.0) ? ((PrimitiveDither.PrimitiveDither_LODFactor + 1.0) > _317) : (PrimitiveDither.PrimitiveDither_LODFactor < _317)) - 0.001000000047497451305389404296875) < 0.0) + { + discard_fragment(); + } + } + } + if ((((_218.z + ((fast::min(fast::max(1.0 - (_218.x * Material.Material_ScalarExpressions[2].y), 0.0), 1.0) + ((_237 + (_242.x * Material.Material_ScalarExpressions[2].z)) * 0.16666667163372039794921875)) + (-0.5))) * ((fast::clamp((View.View_RealTime * PrimitiveFade.PrimitiveFade_FadeTimeScaleBias.x) + PrimitiveFade.PrimitiveFade_FadeTimeScaleBias.y, 0.0, 1.0) + ((_237 + _254.x) * 0.16666667163372039794921875)) + (-0.5))) - 0.33329999446868896484375) < 0.0) + { + discard_fragment(); + } + float2 _351 = ((((in.in_var_TEXCOORD6.xy / float2(in.in_var_TEXCOORD6.w)) - View.View_TemporalAAJitter.xy) - ((in.in_var_TEXCOORD7.xy / float2(in.in_var_TEXCOORD7.w)) - View.View_TemporalAAJitter.zw)) * float2(0.2495000064373016357421875)) + float2(0.49999237060546875); + out.gl_FragDepth = fast::min(_140.z / (_140.w + (sqrt(dot(_272, _272)) / (fast::max(sqrt(dot(_276, _276)) / sqrt(dot(_279, _279)), sqrt(dot(_284, _284)) / sqrt(dot(_287, _287))) / abs(dot(float3x3(View.View_ViewToTranslatedWorld[0].xyz, View.View_ViewToTranslatedWorld[1].xyz, View.View_ViewToTranslatedWorld[2].xyz) * float3(0.0, 0.0, 1.0), _151))))), gl_FragCoord.z); + out.out_var_SV_Target0 = float4(_351.x, _351.y, float2(0.0).x, float2(0.0).y); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/vert/loop-accesschain-writethrough.asm.invalid.vert b/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/vert/loop-accesschain-writethrough.asm.invalid.vert new file mode 100644 index 0000000..b1298b7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4-no-opt/asm/vert/loop-accesschain-writethrough.asm.invalid.vert @@ -0,0 +1,122 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_Globals +{ + float4 ViewportSize; + float ScatteringScaling; + float CocRadiusToCircumscribedRadius; +}; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct main0_out +{ + float2 out_var_TEXCOORD0 [[user(locn0)]]; + float4 out_var_TEXCOORD1 [[user(locn1)]]; + float4 out_var_TEXCOORD2 [[user(locn2)]]; + float4 out_var_TEXCOORD3 [[user(locn3)]]; + float4 out_var_TEXCOORD4 [[user(locn4)]]; + float4 out_var_TEXCOORD5 [[user(locn5)]]; + float4 out_var_TEXCOORD6 [[user(locn6)]]; + float4 gl_Position [[position]]; +}; + +vertex main0_out main0(constant type_Globals& _Globals [[buffer(0)]], const device type_StructuredBuffer_v4float& ScatterDrawList [[buffer(1)]], uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + uint _66 = gl_VertexIndex / 4u; + uint _68 = gl_VertexIndex - (_66 * 4u); + uint _70 = (16u * gl_InstanceIndex) + _66; + float _72; + _72 = 0.0; + spvUnsafeArray _61; + spvUnsafeArray _62; + spvUnsafeArray _63; + float _73; + uint _75 = 0u; + for (;;) + { + if (_75 < 4u) + { + uint _82 = ((5u * _70) + _75) + 1u; + _61[_75] = float4(ScatterDrawList._m0[_82].xyz, 0.0); + _62[_75] = ScatterDrawList._m0[_82].w; + if (_75 == 0u) + { + _73 = _62[_75]; + } + else + { + _73 = fast::max(_72, _62[_75]); + } + _63[_75].x = (-0.5) / _62[_75]; + _63[_75].y = (0.5 * _62[_75]) + 0.5; + _72 = _73; + _75++; + continue; + } + else + { + break; + } + } + float2 _144 = float2(_Globals.ScatteringScaling) * ScatterDrawList._m0[5u * _70].xy; + float2 _173 = (((float2((_72 * _Globals.CocRadiusToCircumscribedRadius) + 1.0) * ((float2(float(_68 % 2u), float(_68 / 2u)) * float2(2.0)) - float2(1.0))) + _144) + float2(0.5)) * _Globals.ViewportSize.zw; + out.out_var_TEXCOORD0 = _144; + out.out_var_TEXCOORD1 = float4(_61[0].xyz, _62[0]); + out.out_var_TEXCOORD2 = float4(_61[1].xyz, _62[1]); + out.out_var_TEXCOORD3 = float4(_61[2].xyz, _62[2]); + out.out_var_TEXCOORD4 = float4(_61[3].xyz, _62[3]); + out.out_var_TEXCOORD5 = float4(_63[0].x, _63[0].y, _63[1].x, _63[1].y); + out.out_var_TEXCOORD6 = float4(_63[2].x, _63[2].y, _63[3].x, _63[3].y); + out.gl_Position = float4((_173.x * 2.0) - 1.0, 1.0 - (_173.y * 2.0), 0.0, 1.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/depth-compare.asm.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/depth-compare.asm.frag new file mode 100644 index 0000000..bdeccc2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/depth-compare.asm.frag @@ -0,0 +1,314 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Globals +{ + float3 SoftTransitionScale; + float4x4 ShadowViewProjectionMatrices[6]; + float InvShadowmapResolution; + float ShadowFadeFraction; + float ShadowSharpen; + float4 LightPositionAndInvRadius; + float2 ProjectionDepthBiasParameters; + float4 PointLightDepthBiasAndProjParameters; +}; + +constant float4 _107 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d SceneTexturesStruct_SceneDepthTexture [[texture(0)]], texture2d SceneTexturesStruct_GBufferATexture [[texture(1)]], texture2d SceneTexturesStruct_GBufferBTexture [[texture(2)]], texture2d SceneTexturesStruct_GBufferDTexture [[texture(3)]], depthcube ShadowDepthCubeTexture [[texture(4)]], texture2d SSProfilesTexture [[texture(5)]], sampler SceneTexturesStruct_SceneDepthTextureSampler [[sampler(0)]], sampler SceneTexturesStruct_GBufferATextureSampler [[sampler(1)]], sampler SceneTexturesStruct_GBufferBTextureSampler [[sampler(2)]], sampler SceneTexturesStruct_GBufferDTextureSampler [[sampler(3)]], sampler ShadowDepthTextureSampler [[sampler(4)]], sampler ShadowDepthCubeTextureSampler [[sampler(5)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float2 _114 = gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw; + float4 _118 = SceneTexturesStruct_SceneDepthTexture.sample(SceneTexturesStruct_SceneDepthTextureSampler, _114, level(0.0)); + float _119 = _118.x; + float _133 = ((_119 * View.View_InvDeviceZToWorldZTransform.x) + View.View_InvDeviceZToWorldZTransform.y) + (1.0 / ((_119 * View.View_InvDeviceZToWorldZTransform.z) - View.View_InvDeviceZToWorldZTransform.w)); + float4 _147 = View.View_ScreenToWorld * float4(((_114 - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_133), _133, 1.0); + float3 _148 = _147.xyz; + float3 _152 = _Globals.LightPositionAndInvRadius.xyz - _148; + float _158 = length(_152); + bool _160 = (_158 * _Globals.LightPositionAndInvRadius.w) < 1.0; + float _207; + if (_160) + { + float3 _165 = abs(_152); + float _166 = _165.x; + float _167 = _165.y; + float _168 = _165.z; + float _170 = fast::max(_166, fast::max(_167, _168)); + int _189; + if (_170 == _166) + { + _189 = (_166 == _152.x) ? 0 : 1; + } + else + { + int _185; + if (_170 == _167) + { + _185 = (_167 == _152.y) ? 2 : 3; + } + else + { + _185 = (_168 == _152.z) ? 4 : 5; + } + _189 = _185; + } + float4 _196 = _Globals.ShadowViewProjectionMatrices[_189] * float4(_147.xyz, 1.0); + float _198 = _196.w; + _207 = ShadowDepthCubeTexture.sample_compare(ShadowDepthCubeTextureSampler, (_152 / float3(_158)), (_196.z / _198) + ((-_Globals.PointLightDepthBiasAndProjParameters.x) / _198), level(0.0)); + } + else + { + _207 = 1.0; + } + float _213 = fast::clamp(((_207 - 0.5) * _Globals.ShadowSharpen) + 0.5, 0.0, 1.0); + float _218 = sqrt(mix(1.0, _213 * _213, _Globals.ShadowFadeFraction)); + float4 _219 = _107; + _219.z = _218; + float3 _236 = normalize((SceneTexturesStruct_GBufferATexture.sample(SceneTexturesStruct_GBufferATextureSampler, _114, level(0.0)).xyz * float3(2.0)) - float3(1.0)); + uint _240 = uint(round(SceneTexturesStruct_GBufferBTexture.sample(SceneTexturesStruct_GBufferBTextureSampler, _114, level(0.0)).w * 255.0)); + bool _248 = (_240 & 15u) == 5u; + float _448; + if (_248) + { + float4 _260 = SSProfilesTexture.read(uint2(int3(1, int(uint((select(float4(0.0), SceneTexturesStruct_GBufferDTexture.sample(SceneTexturesStruct_GBufferDTextureSampler, _114, level(0.0)), bool4(!(((_240 & 4294967280u) & 16u) != 0u))).x * 255.0) + 0.5)), 0).xy), 0); + float _263 = _260.y * 0.5; + float _274 = pow(fast::clamp(dot(-(_152 * float3(rsqrt(dot(_152, _152)))), _236), 0.0, 1.0), 1.0); + float _445; + if (_160) + { + float3 _278 = _152 / float3(_158); + float3 _280 = normalize(cross(_278, float3(0.0, 0.0, 1.0))); + float3 _284 = float3(_Globals.InvShadowmapResolution); + float3 _285 = _280 * _284; + float3 _286 = cross(_280, _278) * _284; + float3 _287 = abs(_278); + float _288 = _287.x; + float _289 = _287.y; + float _290 = _287.z; + float _292 = fast::max(_288, fast::max(_289, _290)); + int _311; + if (_292 == _288) + { + _311 = (_288 == _278.x) ? 0 : 1; + } + else + { + int _307; + if (_292 == _289) + { + _307 = (_289 == _278.y) ? 2 : 3; + } + else + { + _307 = (_290 == _278.z) ? 4 : 5; + } + _311 = _307; + } + float4 _318 = _Globals.ShadowViewProjectionMatrices[_311] * float4(_148 - (_236 * float3(_263)), 1.0); + float _323 = _260.x * (10.0 / _Globals.LightPositionAndInvRadius.w); + float _329 = (1.0 / (((_318.z / _318.w) * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w; + float _342 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, (_278 + (_286 * float3(2.5))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + float _364 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, ((_278 + (_285 * float3(2.3776409626007080078125))) + (_286 * float3(0.77254199981689453125))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + float _387 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, ((_278 + (_285 * float3(1.46946299076080322265625))) + (_286 * float3(-2.0225429534912109375))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + float _410 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, ((_278 + (_285 * float3(-1.46946299076080322265625))) + (_286 * float3(-2.02254199981689453125))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + float _433 = (_329 - ((1.0 / ((float4(ShadowDepthCubeTexture.sample(ShadowDepthTextureSampler, ((_278 + (_285 * float3(-2.3776409626007080078125))) + (_286 * float3(0.772543013095855712890625))), level(0.0))).x * _Globals.PointLightDepthBiasAndProjParameters.z) - _Globals.PointLightDepthBiasAndProjParameters.w)) * _Globals.LightPositionAndInvRadius.w)) * _323; + _445 = (((((fast::clamp(abs((_342 > 0.0) ? (_342 + _263) : fast::max(0.0, (_342 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25) + (fast::clamp(abs((_364 > 0.0) ? (_364 + _263) : fast::max(0.0, (_364 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25)) + (fast::clamp(abs((_387 > 0.0) ? (_387 + _263) : fast::max(0.0, (_387 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25)) + (fast::clamp(abs((_410 > 0.0) ? (_410 + _263) : fast::max(0.0, (_410 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25)) + (fast::clamp(abs((_433 > 0.0) ? (_433 + _263) : fast::max(0.0, (_433 * _274) + _263)), 0.1500000059604644775390625, 5.0) + 0.25)) * 0.20000000298023223876953125; + } + else + { + _445 = 1.0; + } + _448 = 1.0 - (_445 * 0.20000000298023223876953125); + } + else + { + _448 = 1.0; + } + float4 _451 = float4(float3(1.0).x, float3(1.0).y, _219.z, float3(1.0).z); + _451.w = _248 ? sqrt(_448) : _218; + out.out_var_SV_Target0 = _451; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/global-constant-arrays.asm.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/global-constant-arrays.asm.frag new file mode 100644 index 0000000..210d4f9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/global-constant-arrays.asm.frag @@ -0,0 +1,1365 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_Globals +{ + float4 MappingPolynomial; + float3 InverseGamma; + float4 ColorMatrixR_ColorCurveCd1; + float4 ColorMatrixG_ColorCurveCd3Cm3; + float4 ColorMatrixB_ColorCurveCm2; + float4 ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3; + float4 ColorCurve_Ch1_Ch2; + float4 ColorShadow_Luma; + float4 ColorShadow_Tint1; + float4 ColorShadow_Tint2; + float FilmSlope; + float FilmToe; + float FilmShoulder; + float FilmBlackClip; + float FilmWhiteClip; + packed_float3 ColorScale; + float4 OverlayColor; + float WhiteTemp; + float WhiteTint; + float4 ColorSaturation; + float4 ColorContrast; + float4 ColorGamma; + float4 ColorGain; + float4 ColorOffset; + float4 ColorSaturationShadows; + float4 ColorContrastShadows; + float4 ColorGammaShadows; + float4 ColorGainShadows; + float4 ColorOffsetShadows; + float4 ColorSaturationMidtones; + float4 ColorContrastMidtones; + float4 ColorGammaMidtones; + float4 ColorGainMidtones; + float4 ColorOffsetMidtones; + float4 ColorSaturationHighlights; + float4 ColorContrastHighlights; + float4 ColorGammaHighlights; + float4 ColorGainHighlights; + float4 ColorOffsetHighlights; + float ColorCorrectionShadowsMax; + float ColorCorrectionHighlightsMin; + uint OutputDevice; + uint OutputGamut; + float BlueCorrection; + float ExpandGamut; +}; + +constant spvUnsafeArray _475 = spvUnsafeArray({ -4.0, -4.0, -3.1573765277862548828125, -0.485249996185302734375, 1.84773242473602294921875, 1.84773242473602294921875 }); +constant spvUnsafeArray _476 = spvUnsafeArray({ -0.718548238277435302734375, 2.0810306072235107421875, 3.66812419891357421875, 4.0, 4.0, 4.0 }); +constant spvUnsafeArray _479 = spvUnsafeArray({ -4.97062206268310546875, -3.0293781757354736328125, -2.1261999607086181640625, -1.5104999542236328125, -1.0578000545501708984375, -0.4668000042438507080078125, 0.11937999725341796875, 0.7088134288787841796875, 1.2911865711212158203125, 1.2911865711212158203125 }); +constant spvUnsafeArray _480 = spvUnsafeArray({ 0.80891323089599609375, 1.19108676910400390625, 1.5683000087738037109375, 1.94830000400543212890625, 2.308300018310546875, 2.63840007781982421875, 2.85949993133544921875, 2.9872608184814453125, 3.0127391815185546875, 3.0127391815185546875 }); +constant spvUnsafeArray _482 = spvUnsafeArray({ -2.3010299205780029296875, -2.3010299205780029296875, -1.9312000274658203125, -1.5204999446868896484375, -1.0578000545501708984375, -0.4668000042438507080078125, 0.11937999725341796875, 0.7088134288787841796875, 1.2911865711212158203125, 1.2911865711212158203125 }); +constant spvUnsafeArray _483 = spvUnsafeArray({ 0.801995217800140380859375, 1.19800484180450439453125, 1.5943000316619873046875, 1.99730002880096435546875, 2.3782999515533447265625, 2.7683999538421630859375, 3.0515000820159912109375, 3.2746293544769287109375, 3.32743072509765625, 3.32743072509765625 }); + +constant float3 _391 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0), center_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_Globals& _Globals [[buffer(0)]], uint gl_Layer [[render_target_array_index]]) +{ + main0_out out = {}; + float3x3 _546 = float3x3(float3(0.41245639324188232421875, 0.3575761020183563232421875, 0.180437505245208740234375), float3(0.21267290413379669189453125, 0.715152204036712646484375, 0.072175003588199615478515625), float3(0.01933390088379383087158203125, 0.119191996753215789794921875, 0.950304090976715087890625)) * float3x3(float3(1.01303005218505859375, 0.0061053098179399967193603515625, -0.014971000142395496368408203125), float3(0.0076982299797236919403076171875, 0.99816501140594482421875, -0.005032029934227466583251953125), float3(-0.0028413101099431514739990234375, 0.0046851597726345062255859375, 0.92450702190399169921875)); + float3x3 _547 = _546 * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)); + float3x3 _548 = float3x3(float3(0.662454187870025634765625, 0.1340042054653167724609375, 0.1561876833438873291015625), float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625), float3(-0.0055746496655046939849853515625, 0.0040607335977256298065185546875, 1.01033914089202880859375)) * float3x3(float3(0.98722398281097412109375, -0.0061132698319852352142333984375, 0.01595330052077770233154296875), float3(-0.007598360069096088409423828125, 1.00186002254486083984375, 0.0053300200961530208587646484375), float3(0.003072570078074932098388671875, -0.0050959498621523380279541015625, 1.0816800594329833984375)); + float3x3 _549 = _548 * float3x3(float3(3.2409698963165283203125, -1.53738319873809814453125, -0.4986107647418975830078125), float3(-0.96924364566802978515625, 1.875967502593994140625, 0.0415550582110881805419921875), float3(0.055630080401897430419921875, -0.2039769589900970458984375, 1.05697154998779296875)); + float3x3 _550 = float3x3(float3(0.952552378177642822265625, 0.0, 9.25), float3(0.3439664542675018310546875, 0.728166103363037109375, -0.07213254272937774658203125), float3(0.0, 0.0, 1.00882518291473388671875)) * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)); + float3x3 _551 = float3x3(float3(0.662454187870025634765625, 0.1340042054653167724609375, 0.1561876833438873291015625), float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625), float3(-0.0055746496655046939849853515625, 0.0040607335977256298065185546875, 1.01033914089202880859375)) * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625)); + float3x3 _576; + for (;;) + { + if (_Globals.OutputGamut == 1u) + { + _576 = _548 * float3x3(float3(2.493396282196044921875, -0.931345880031585693359375, -0.4026944935321807861328125), float3(-0.829486787319183349609375, 1.76265966892242431640625, 0.02362460084259510040283203125), float3(0.0358506999909877777099609375, -0.076182700693607330322265625, 0.957014024257659912109375)); + break; + } + else + { + if (_Globals.OutputGamut == 2u) + { + _576 = _548 * float3x3(float3(1.71660840511322021484375, -0.3556621074676513671875, -0.253360092639923095703125), float3(-0.666682898998260498046875, 1.61647760868072509765625, 0.01576850004494190216064453125), float3(0.017642199993133544921875, -0.04277630150318145751953125, 0.94222867488861083984375)); + break; + } + else + { + if (_Globals.OutputGamut == 3u) + { + _576 = float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625)); + break; + } + else + { + if (_Globals.OutputGamut == 4u) + { + _576 = float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 1.0, 0.0), float3(0.0, 0.0, 1.0)); + break; + } + else + { + _576 = _549; + break; + } + } + } + } + } + float3 _577 = float4((in.in_var_TEXCOORD0 - float2(0.015625)) * float2(1.03225803375244140625), float(gl_Layer) * 0.0322580635547637939453125, 0.0).xyz; + float3 _599; + if (_Globals.OutputDevice >= 3u) + { + float3 _591 = pow(_577, float3(0.0126833133399486541748046875)); + _599 = pow(fast::max(float3(0.0), _591 - float3(0.8359375)) / (float3(18.8515625) - (float3(18.6875) * _591)), float3(6.277394771575927734375)) * float3(10000.0); + } + else + { + _599 = (exp2((_577 - float3(0.434017598628997802734375)) * float3(14.0)) * float3(0.180000007152557373046875)) - (exp2(float3(-6.0762462615966796875)) * float3(0.180000007152557373046875)); + } + float _602 = _Globals.WhiteTemp * 1.00055634975433349609375; + float _616 = (_602 <= 7000.0) ? (0.24406300485134124755859375 + ((99.1100006103515625 + ((2967800.0 - (4604438528.0 / _Globals.WhiteTemp)) / _602)) / _602)) : (0.23703999817371368408203125 + ((247.4799957275390625 + ((1901800.0 - (2005284352.0 / _Globals.WhiteTemp)) / _602)) / _602)); + float _633 = ((0.860117733478546142578125 + (0.00015411825734190642833709716796875 * _Globals.WhiteTemp)) + ((1.2864121856637211749330163002014e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)) / ((1.0 + (0.0008424202096648514270782470703125 * _Globals.WhiteTemp)) + ((7.0814513719597016461193561553955e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)); + float _644 = ((0.317398726940155029296875 + (4.25 * _Globals.WhiteTemp)) + ((4.2048167614439080352894961833954e-08 * _Globals.WhiteTemp) * _Globals.WhiteTemp)) / ((1.0 - (2.8974181986995972692966461181641e-05 * _Globals.WhiteTemp)) + ((1.6145605741257895715534687042236e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)); + float _649 = ((2.0 * _633) - (8.0 * _644)) + 4.0; + float2 _653 = float2((3.0 * _633) / _649, (2.0 * _644) / _649); + float2 _660 = normalize(float2(_633, _644)); + float _665 = _633 + (((-_660.y) * _Globals.WhiteTint) * 0.0500000007450580596923828125); + float _669 = _644 + ((_660.x * _Globals.WhiteTint) * 0.0500000007450580596923828125); + float _674 = ((2.0 * _665) - (8.0 * _669)) + 4.0; + float2 _680 = select(float2(_616, ((((-3.0) * _616) * _616) + (2.86999988555908203125 * _616)) - 0.2750000059604644775390625), _653, bool2(_Globals.WhiteTemp < 4000.0)) + (float2((3.0 * _665) / _674, (2.0 * _669) / _674) - _653); + float _681 = _680.x; + float _682 = _680.y; + float _683 = fast::max(_682, 1.0000000133514319600180897396058e-10); + float3 _685 = _391; + _685.x = _681 / _683; + float3 _686 = _685; + _686.y = 1.0; + float3 _690 = _686; + _690.z = ((1.0 - _681) - _682) / _683; + float _691 = fast::max(0.328999996185302734375, 1.0000000133514319600180897396058e-10); + float3 _693 = _391; + _693.x = 0.3127000033855438232421875 / _691; + float3 _694 = _693; + _694.y = 1.0; + float3 _696 = _694; + _696.z = 0.3582999706268310546875 / _691; + float3 _697 = _690 * float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)); + float3 _698 = _696 * float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)); + float3 _717 = (_599 * ((float3x3(float3(0.41245639324188232421875, 0.3575761020183563232421875, 0.180437505245208740234375), float3(0.21267290413379669189453125, 0.715152204036712646484375, 0.072175003588199615478515625), float3(0.01933390088379383087158203125, 0.119191996753215789794921875, 0.950304090976715087890625)) * ((float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)) * float3x3(float3(_698.x / _697.x, 0.0, 0.0), float3(0.0, _698.y / _697.y, 0.0), float3(0.0, 0.0, _698.z / _697.z))) * float3x3(float3(0.986992895603179931640625, -0.14705429971218109130859375, 0.15996269881725311279296875), float3(0.4323053061962127685546875, 0.518360316753387451171875, 0.049291200935840606689453125), float3(-0.00852870009839534759521484375, 0.0400427989661693572998046875, 0.968486726284027099609375)))) * float3x3(float3(3.2409698963165283203125, -1.53738319873809814453125, -0.4986107647418975830078125), float3(-0.96924364566802978515625, 1.875967502593994140625, 0.0415550582110881805419921875), float3(0.055630080401897430419921875, -0.2039769589900970458984375, 1.05697154998779296875)))) * _547; + float3 _745; + if (_Globals.ColorShadow_Tint2.w != 0.0) + { + float _724 = dot(_717, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625)); + float3 _727 = (_717 / float3(_724)) - float3(1.0); + _745 = mix(_717, _717 * (_549 * (float3x3(float3(0.544169127941131591796875, 0.23959259688854217529296875, 0.16669429838657379150390625), float3(0.23946559429168701171875, 0.702153027057647705078125, 0.058381401002407073974609375), float3(-0.0023439000360667705535888671875, 0.0361833982169628143310546875, 1.05521833896636962890625)) * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)))), float3((1.0 - exp2((-4.0) * dot(_727, _727))) * (1.0 - exp2((((-4.0) * _Globals.ExpandGamut) * _724) * _724)))); + } + else + { + _745 = _717; + } + float _746 = dot(_745, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625)); + float4 _751 = _Globals.ColorSaturationShadows * _Globals.ColorSaturation; + float4 _756 = _Globals.ColorContrastShadows * _Globals.ColorContrast; + float4 _761 = _Globals.ColorGammaShadows * _Globals.ColorGamma; + float4 _766 = _Globals.ColorGainShadows * _Globals.ColorGain; + float4 _771 = _Globals.ColorOffsetShadows + _Globals.ColorOffset; + float3 _772 = float3(_746); + float _804 = smoothstep(0.0, _Globals.ColorCorrectionShadowsMax, _746); + float4 _808 = _Globals.ColorSaturationHighlights * _Globals.ColorSaturation; + float4 _811 = _Globals.ColorContrastHighlights * _Globals.ColorContrast; + float4 _814 = _Globals.ColorGammaHighlights * _Globals.ColorGamma; + float4 _817 = _Globals.ColorGainHighlights * _Globals.ColorGain; + float4 _820 = _Globals.ColorOffsetHighlights + _Globals.ColorOffset; + float _852 = smoothstep(_Globals.ColorCorrectionHighlightsMin, 1.0, _746); + float4 _855 = _Globals.ColorSaturationMidtones * _Globals.ColorSaturation; + float4 _858 = _Globals.ColorContrastMidtones * _Globals.ColorContrast; + float4 _861 = _Globals.ColorGammaMidtones * _Globals.ColorGamma; + float4 _864 = _Globals.ColorGainMidtones * _Globals.ColorGain; + float4 _867 = _Globals.ColorOffsetMidtones + _Globals.ColorOffset; + float3 _905 = ((((pow(pow(fast::max(float3(0.0), mix(_772, _745, _751.xyz * float3(_751.w))) * float3(5.5555553436279296875), _756.xyz * float3(_756.w)) * float3(0.180000007152557373046875), float3(1.0) / (_761.xyz * float3(_761.w))) * (_766.xyz * float3(_766.w))) + (_771.xyz + float3(_771.w))) * float3(1.0 - _804)) + (((pow(pow(fast::max(float3(0.0), mix(_772, _745, _855.xyz * float3(_855.w))) * float3(5.5555553436279296875), _858.xyz * float3(_858.w)) * float3(0.180000007152557373046875), float3(1.0) / (_861.xyz * float3(_861.w))) * (_864.xyz * float3(_864.w))) + (_867.xyz + float3(_867.w))) * float3(_804 - _852))) + (((pow(pow(fast::max(float3(0.0), mix(_772, _745, _808.xyz * float3(_808.w))) * float3(5.5555553436279296875), _811.xyz * float3(_811.w)) * float3(0.180000007152557373046875), float3(1.0) / (_814.xyz * float3(_814.w))) * (_817.xyz * float3(_817.w))) + (_820.xyz + float3(_820.w))) * float3(_852)); + float3 _906 = _905 * _549; + float3 _914 = float3(_Globals.BlueCorrection); + float3 _916 = mix(_905, _905 * ((_551 * float3x3(float3(0.940437257289886474609375, -0.01830687932670116424560546875, 0.07786960899829864501953125), float3(0.008378696627914905548095703125, 0.82866001129150390625, 0.162961304187774658203125), float3(0.0005471261101774871349334716796875, -0.00088337459601461887359619140625, 1.00033628940582275390625))) * _550), _914) * _551; + float _917 = _916.x; + float _918 = _916.y; + float _920 = _916.z; + float _923 = fast::max(fast::max(_917, _918), _920); + float _928 = (fast::max(_923, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_917, _918), _920), 1.0000000133514319600180897396058e-10)) / fast::max(_923, 0.00999999977648258209228515625); + float _941 = ((_920 + _918) + _917) + (1.75 * sqrt(((_920 * (_920 - _918)) + (_918 * (_918 - _917))) + (_917 * (_917 - _920)))); + float _942 = _941 * 0.3333333432674407958984375; + float _943 = _928 - 0.4000000059604644775390625; + float _948 = fast::max(1.0 - abs(_943 * 2.5), 0.0); + float _956 = (1.0 + (float(int(sign(_943 * 5.0))) * (1.0 - (_948 * _948)))) * 0.02500000037252902984619140625; + float _969; + if (_942 <= 0.053333334624767303466796875) + { + _969 = _956; + } + else + { + float _968; + if (_942 >= 0.1599999964237213134765625) + { + _968 = 0.0; + } + else + { + _968 = _956 * ((0.23999999463558197021484375 / _941) - 0.5); + } + _969 = _968; + } + float3 _972 = _916 * float3(1.0 + _969); + float _973 = _972.x; + float _974 = _972.y; + float _976 = _972.z; + float _990; + if ((_973 == _974) && (_974 == _976)) + { + _990 = 0.0; + } + else + { + _990 = 57.2957763671875 * atan2(sqrt(3.0) * (_974 - _976), ((2.0 * _973) - _974) - _976); + } + float _995; + if (_990 < 0.0) + { + _995 = _990 + 360.0; + } + else + { + _995 = _990; + } + float _996 = fast::clamp(_995, 0.0, 360.0); + float _1001; + if (_996 > 180.0) + { + _1001 = _996 - 360.0; + } + else + { + _1001 = _996; + } + float _1005 = smoothstep(0.0, 1.0, 1.0 - abs(_1001 * 0.01481481455266475677490234375)); + float3 _1012 = _972; + _1012.x = _973 + ((((_1005 * _1005) * _928) * (0.02999999932944774627685546875 - _973)) * 0.180000007152557373046875); + float3 _1014 = fast::max(float3(0.0), _1012 * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375))); + float _1023 = (1.0 + _Globals.FilmBlackClip) - _Globals.FilmToe; + float _1026 = 1.0 + _Globals.FilmWhiteClip; + float _1029 = _1026 - _Globals.FilmShoulder; + float _1056; + if (_Globals.FilmToe > 0.800000011920928955078125) + { + _1056 = ((0.819999992847442626953125 - _Globals.FilmToe) / _Globals.FilmSlope) + (log(0.180000007152557373046875) / log(10.0)); + } + else + { + float _1035 = (0.180000007152557373046875 + _Globals.FilmBlackClip) / _1023; + _1056 = (log(0.180000007152557373046875) / log(10.0)) - ((0.5 * log(_1035 / (2.0 - _1035))) * (_1023 / _Globals.FilmSlope)); + } + float _1061 = ((1.0 - _Globals.FilmToe) / _Globals.FilmSlope) - _1056; + float _1063 = (_Globals.FilmShoulder / _Globals.FilmSlope) - _1061; + float _1065 = log(10.0); + float3 _1067 = log(mix(float3(dot(_1014, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1014, float3(0.959999978542327880859375))) / float3(_1065); + float3 _1071 = float3(_Globals.FilmSlope) * (_1067 + float3(_1061)); + float3 _1079 = float3(_1056); + float3 _1080 = _1067 - _1079; + float3 _1092 = float3(_1063); + float3 _1106 = fast::clamp(_1080 / float3(_1063 - _1056), float3(0.0), float3(1.0)); + float3 _1110 = select(_1106, float3(1.0) - _1106, bool3(_1063 < _1056)); + float3 _1115 = mix(select(_1071, float3(-_Globals.FilmBlackClip) + (float3(2.0 * _1023) / (float3(1.0) + exp(float3(((-2.0) * _Globals.FilmSlope) / _1023) * _1080))), _1067 < _1079), select(_1071, float3(_1026) - (float3(2.0 * _1029) / (float3(1.0) + exp(float3((2.0 * _Globals.FilmSlope) / _1029) * (_1067 - _1092)))), _1067 > _1092), ((float3(3.0) - (float3(2.0) * _1110)) * _1110) * _1110); + float3 _1119 = fast::max(float3(0.0), mix(float3(dot(_1115, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1115, float3(0.930000007152557373046875))); + float3 _1189; + if (_Globals.ColorShadow_Tint2.w == 0.0) + { + float3 _1131 = _391; + _1131.x = dot(_906, _Globals.ColorMatrixR_ColorCurveCd1.xyz); + float3 _1136 = _1131; + _1136.y = dot(_906, _Globals.ColorMatrixG_ColorCurveCd3Cm3.xyz); + float3 _1141 = _1136; + _1141.z = dot(_906, _Globals.ColorMatrixB_ColorCurveCm2.xyz); + float3 _1157 = fast::max(float3(0.0), _1141 * (_Globals.ColorShadow_Tint1.xyz + (_Globals.ColorShadow_Tint2.xyz * float3(1.0 / (dot(_906, _Globals.ColorShadow_Luma.xyz) + 1.0))))); + float3 _1162 = fast::max(float3(0.0), _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.xxx - _1157); + float3 _1164 = fast::max(_1157, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.zzz); + _1189 = ((((_1164 * _Globals.ColorCurve_Ch1_Ch2.xxx) + _Globals.ColorCurve_Ch1_Ch2.yyy) * (float3(1.0) / (_1164 + _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.www))) + ((fast::clamp(_1157, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.xxx, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.zzz) * _Globals.ColorMatrixB_ColorCurveCm2.www) + (((_1162 * _Globals.ColorMatrixR_ColorCurveCd1.www) * (float3(1.0) / (_1162 + _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.yyy))) + _Globals.ColorMatrixG_ColorCurveCd3Cm3.www))) - float3(0.00200000009499490261077880859375); + } + else + { + _1189 = fast::max(float3(0.0), mix(_1119, _1119 * ((_551 * float3x3(float3(1.06317996978759765625, 0.02339559979736804962158203125, -0.08657260239124298095703125), float3(-0.010633699595928192138671875, 1.2063200473785400390625, -0.1956900060176849365234375), float3(-0.0005908869788981974124908447265625, 0.00105247995816171169281005859375, 0.999538004398345947265625))) * _550), _914) * _549); + } + float3 _1218 = pow(fast::max(float3(0.0), mix((((float3(_Globals.MappingPolynomial.x) * (_1189 * _1189)) + (float3(_Globals.MappingPolynomial.y) * _1189)) + float3(_Globals.MappingPolynomial.z)) * float3(_Globals.ColorScale), _Globals.OverlayColor.xyz, float3(_Globals.OverlayColor.w))), float3(_Globals.InverseGamma.y)); + float3 _3001; + if (_Globals.OutputDevice == 0u) + { + float _2961 = _1218.x; + float _2973; + for (;;) + { + if (_2961 < 0.00313066993840038776397705078125) + { + _2973 = _2961 * 12.9200000762939453125; + break; + } + _2973 = (pow(_2961, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _2974 = _1218.y; + float _2986; + for (;;) + { + if (_2974 < 0.00313066993840038776397705078125) + { + _2986 = _2974 * 12.9200000762939453125; + break; + } + _2986 = (pow(_2974, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _2987 = _1218.z; + float _2999; + for (;;) + { + if (_2987 < 0.00313066993840038776397705078125) + { + _2999 = _2987 * 12.9200000762939453125; + break; + } + _2999 = (pow(_2987, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + _3001 = float3(_2973, _2986, _2999); + } + else + { + float3 _2960; + if (_Globals.OutputDevice == 1u) + { + float3 _2953 = fast::max(float3(6.1035199905745685100555419921875e-05), (_1218 * _547) * _576); + _2960 = fast::min(_2953 * float3(4.5), (pow(fast::max(_2953, float3(0.017999999225139617919921875)), float3(0.449999988079071044921875)) * float3(1.09899997711181640625)) - float3(0.098999999463558197021484375)); + } + else + { + float3 _2950; + if ((_Globals.OutputDevice == 3u) || (_Globals.OutputDevice == 5u)) + { + float3 _2100 = (_906 * float3(1.5)) * (_546 * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625))); + float _2101 = _2100.x; + float _2102 = _2100.y; + float _2104 = _2100.z; + float _2107 = fast::max(fast::max(_2101, _2102), _2104); + float _2112 = (fast::max(_2107, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_2101, _2102), _2104), 1.0000000133514319600180897396058e-10)) / fast::max(_2107, 0.00999999977648258209228515625); + float _2125 = ((_2104 + _2102) + _2101) + (1.75 * sqrt(((_2104 * (_2104 - _2102)) + (_2102 * (_2102 - _2101))) + (_2101 * (_2101 - _2104)))); + float _2126 = _2125 * 0.3333333432674407958984375; + float _2127 = _2112 - 0.4000000059604644775390625; + float _2132 = fast::max(1.0 - abs(_2127 * 2.5), 0.0); + float _2140 = (1.0 + (float(int(sign(_2127 * 5.0))) * (1.0 - (_2132 * _2132)))) * 0.02500000037252902984619140625; + float _2153; + if (_2126 <= 0.053333334624767303466796875) + { + _2153 = _2140; + } + else + { + float _2152; + if (_2126 >= 0.1599999964237213134765625) + { + _2152 = 0.0; + } + else + { + _2152 = _2140 * ((0.23999999463558197021484375 / _2125) - 0.5); + } + _2153 = _2152; + } + float3 _2156 = _2100 * float3(1.0 + _2153); + float _2157 = _2156.x; + float _2158 = _2156.y; + float _2160 = _2156.z; + float _2174; + if ((_2157 == _2158) && (_2158 == _2160)) + { + _2174 = 0.0; + } + else + { + _2174 = 57.2957763671875 * atan2(sqrt(3.0) * (_2158 - _2160), ((2.0 * _2157) - _2158) - _2160); + } + float _2179; + if (_2174 < 0.0) + { + _2179 = _2174 + 360.0; + } + else + { + _2179 = _2174; + } + float _2180 = fast::clamp(_2179, 0.0, 360.0); + float _2185; + if (_2180 > 180.0) + { + _2185 = _2180 - 360.0; + } + else + { + _2185 = _2180; + } + float _2235; + if ((_2185 > (-67.5)) && (_2185 < 67.5)) + { + float _2192 = (_2185 - (-67.5)) * 0.0296296291053295135498046875; + int _2193 = int(_2192); + float _2195 = _2192 - float(_2193); + float _2196 = _2195 * _2195; + float _2197 = _2196 * _2195; + float _2234; + if (_2193 == 3) + { + _2234 = (((_2197 * (-0.16666667163372039794921875)) + (_2196 * 0.5)) + (_2195 * (-0.5))) + 0.16666667163372039794921875; + } + else + { + float _2227; + if (_2193 == 2) + { + _2227 = ((_2197 * 0.5) + (_2196 * (-1.0))) + 0.666666686534881591796875; + } + else + { + float _2222; + if (_2193 == 1) + { + _2222 = (((_2197 * (-0.5)) + (_2196 * 0.5)) + (_2195 * 0.5)) + 0.16666667163372039794921875; + } + else + { + float _2215; + if (_2193 == 0) + { + _2215 = _2197 * 0.16666667163372039794921875; + } + else + { + _2215 = 0.0; + } + _2222 = _2215; + } + _2227 = _2222; + } + _2234 = _2227; + } + _2235 = _2234; + } + else + { + _2235 = 0.0; + } + float3 _2242 = _2156; + _2242.x = _2157 + ((((_2235 * 1.5) * _2112) * (0.02999999932944774627685546875 - _2157)) * 0.180000007152557373046875); + float3 _2245 = fast::clamp(fast::clamp(_2242, float3(0.0), float3(65535.0)) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)), float3(0.0), float3(65535.0)); + float3 _2248 = mix(float3(dot(_2245, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _2245, float3(0.959999978542327880859375)); + float _2249 = _2248.x; + float _2253 = 0.17999999225139617919921875 * exp2(18.0); + float _2255 = exp2(-14.0); + float _2258 = log((_2249 <= 0.0) ? _2255 : _2249) / _1065; + float _2260 = log(0.17999999225139617919921875 * exp2(-15.0)) / _1065; + float _2327; + if (_2258 <= _2260) + { + _2327 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _2267 = log(0.180000007152557373046875) / _1065; + float _2324; + if ((_2258 > _2260) && (_2258 < _2267)) + { + float _2307 = (3.0 * (_2258 - _2260)) / (_2267 - _2260); + int _2308 = int(_2307); + float _2310 = _2307 - float(_2308); + _2324 = dot(float3(_2310 * _2310, _2310, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_2308], _475[_2308 + 1], _475[_2308 + 2])); + } + else + { + float _2275 = log(_2253) / _1065; + float _2303; + if ((_2258 >= _2267) && (_2258 < _2275)) + { + float _2286 = (3.0 * (_2258 - _2267)) / (_2275 - _2267); + int _2287 = int(_2286); + float _2289 = _2286 - float(_2287); + _2303 = dot(float3(_2289 * _2289, _2289, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2287], _476[_2287 + 1], _476[_2287 + 2])); + } + else + { + _2303 = log(10000.0) / _1065; + } + _2324 = _2303; + } + _2327 = _2324; + } + float3 _2329 = _391; + _2329.x = pow(10.0, _2327); + float _2330 = _2248.y; + float _2334 = log((_2330 <= 0.0) ? _2255 : _2330) / _1065; + float _2401; + if (_2334 <= _2260) + { + _2401 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _2341 = log(0.180000007152557373046875) / _1065; + float _2398; + if ((_2334 > _2260) && (_2334 < _2341)) + { + float _2381 = (3.0 * (_2334 - _2260)) / (_2341 - _2260); + int _2382 = int(_2381); + float _2384 = _2381 - float(_2382); + _2398 = dot(float3(_2384 * _2384, _2384, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_2382], _475[_2382 + 1], _475[_2382 + 2])); + } + else + { + float _2349 = log(_2253) / _1065; + float _2377; + if ((_2334 >= _2341) && (_2334 < _2349)) + { + float _2360 = (3.0 * (_2334 - _2341)) / (_2349 - _2341); + int _2361 = int(_2360); + float _2363 = _2360 - float(_2361); + _2377 = dot(float3(_2363 * _2363, _2363, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2361], _476[_2361 + 1], _476[_2361 + 2])); + } + else + { + _2377 = log(10000.0) / _1065; + } + _2398 = _2377; + } + _2401 = _2398; + } + float3 _2403 = _2329; + _2403.y = pow(10.0, _2401); + float _2404 = _2248.z; + float _2408 = log((_2404 <= 0.0) ? _2255 : _2404) / _1065; + float _2475; + if (_2408 <= _2260) + { + _2475 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _2415 = log(0.180000007152557373046875) / _1065; + float _2472; + if ((_2408 > _2260) && (_2408 < _2415)) + { + float _2455 = (3.0 * (_2408 - _2260)) / (_2415 - _2260); + int _2456 = int(_2455); + float _2458 = _2455 - float(_2456); + _2472 = dot(float3(_2458 * _2458, _2458, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_2456], _475[_2456 + 1], _475[_2456 + 2])); + } + else + { + float _2423 = log(_2253) / _1065; + float _2451; + if ((_2408 >= _2415) && (_2408 < _2423)) + { + float _2434 = (3.0 * (_2408 - _2415)) / (_2423 - _2415); + int _2435 = int(_2434); + float _2437 = _2434 - float(_2435); + _2451 = dot(float3(_2437 * _2437, _2437, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2435], _476[_2435 + 1], _476[_2435 + 2])); + } + else + { + _2451 = log(10000.0) / _1065; + } + _2472 = _2451; + } + _2475 = _2472; + } + float3 _2477 = _2403; + _2477.z = pow(10.0, _2475); + float3 _2479 = (_2477 * float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625))) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)); + float _2481 = 0.17999999225139617919921875 * pow(2.0, -12.0); + float _2485 = log((_2481 <= 0.0) ? _2255 : _2481) / _1065; + float _2552; + if (_2485 <= _2260) + { + _2552 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _2492 = log(0.180000007152557373046875) / _1065; + float _2549; + if ((_2485 > _2260) && (_2485 < _2492)) + { + float _2532 = (3.0 * (_2485 - _2260)) / (_2492 - _2260); + int _2533 = int(_2532); + float _2535 = _2532 - float(_2533); + _2549 = dot(float3(_2535 * _2535, _2535, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_2533], _475[_2533 + 1], _475[_2533 + 2])); + } + else + { + float _2500 = log(_2253) / _1065; + float _2528; + if ((_2485 >= _2492) && (_2485 < _2500)) + { + float _2511 = (3.0 * (_2485 - _2492)) / (_2500 - _2492); + int _2512 = int(_2511); + float _2514 = _2511 - float(_2512); + _2528 = dot(float3(_2514 * _2514, _2514, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2512], _476[_2512 + 1], _476[_2512 + 2])); + } + else + { + _2528 = log(10000.0) / _1065; + } + _2549 = _2528; + } + _2552 = _2549; + } + float _2555 = log(0.180000007152557373046875) / _1065; + float _2611; + if (_2555 <= _2260) + { + _2611 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _2608; + if ((_2555 > _2260) && (_2555 < _2555)) + { + _2608 = (float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[3], _475[4], _475[5])).z; + } + else + { + float _2568 = log(_2253) / _1065; + float _2596; + if ((_2555 >= _2555) && (_2555 < _2568)) + { + float _2579 = (3.0 * (_2555 - _2555)) / (_2568 - _2555); + int _2580 = int(_2579); + float _2582 = _2579 - float(_2580); + _2596 = dot(float3(_2582 * _2582, _2582, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2580], _476[_2580 + 1], _476[_2580 + 2])); + } + else + { + _2596 = log(10000.0) / _1065; + } + _2608 = _2596; + } + _2611 = _2608; + } + float _2612 = pow(10.0, _2611); + float _2614 = 0.17999999225139617919921875 * pow(2.0, 10.0); + float _2618 = log((_2614 <= 0.0) ? _2255 : _2614) / _1065; + float _2683; + if (_2618 <= _2260) + { + _2683 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _2680; + if ((_2618 > _2260) && (_2618 < _2555)) + { + float _2663 = (3.0 * (_2618 - _2260)) / (_2555 - _2260); + int _2664 = int(_2663); + float _2666 = _2663 - float(_2664); + _2680 = dot(float3(_2666 * _2666, _2666, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_2664], _475[_2664 + 1], _475[_2664 + 2])); + } + else + { + float _2631 = log(_2253) / _1065; + float _2659; + if ((_2618 >= _2555) && (_2618 < _2631)) + { + float _2642 = (3.0 * (_2618 - _2555)) / (_2631 - _2555); + int _2643 = int(_2642); + float _2645 = _2642 - float(_2643); + _2659 = dot(float3(_2645 * _2645, _2645, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_2643], _476[_2643 + 1], _476[_2643 + 2])); + } + else + { + _2659 = log(10000.0) / _1065; + } + _2680 = _2659; + } + _2683 = _2680; + } + float _2684 = pow(10.0, _2683); + float _2685 = _2479.x; + float _2689 = log((_2685 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2685) / _1065; + float _2690 = log(pow(10.0, _2552)); + float _2691 = _2690 / _1065; + float _2768; + if (_2689 <= _2691) + { + _2768 = (_2689 * 3.0) + ((log(9.9999997473787516355514526367188e-05) / _1065) - ((3.0 * _2690) / _1065)); + } + else + { + float _2698 = log(_2612) / _1065; + float _2760; + if ((_2689 > _2691) && (_2689 < _2698)) + { + float _2743 = (7.0 * (_2689 - _2691)) / (_2698 - _2691); + int _2744 = int(_2743); + float _2746 = _2743 - float(_2744); + _2760 = dot(float3(_2746 * _2746, _2746, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_479[_2744], _479[_2744 + 1], _479[_2744 + 2])); + } + else + { + float _2705 = log(_2684); + float _2706 = _2705 / _1065; + float _2739; + if ((_2689 >= _2698) && (_2689 < _2706)) + { + float _2722 = (7.0 * (_2689 - _2698)) / (_2706 - _2698); + int _2723 = int(_2722); + float _2725 = _2722 - float(_2723); + _2739 = dot(float3(_2725 * _2725, _2725, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_480[_2723], _480[_2723 + 1], _480[_2723 + 2])); + } + else + { + _2739 = (_2689 * 0.0599999986588954925537109375) + ((log(1000.0) / _1065) - ((0.0599999986588954925537109375 * _2705) / _1065)); + } + _2760 = _2739; + } + _2768 = _2760; + } + float3 _2770 = _391; + _2770.x = pow(10.0, _2768); + float _2771 = _2479.y; + float _2775 = log((_2771 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2771) / _1065; + float _2852; + if (_2775 <= _2691) + { + _2852 = (_2775 * 3.0) + ((log(9.9999997473787516355514526367188e-05) / _1065) - ((3.0 * _2690) / _1065)); + } + else + { + float _2782 = log(_2612) / _1065; + float _2844; + if ((_2775 > _2691) && (_2775 < _2782)) + { + float _2827 = (7.0 * (_2775 - _2691)) / (_2782 - _2691); + int _2828 = int(_2827); + float _2830 = _2827 - float(_2828); + _2844 = dot(float3(_2830 * _2830, _2830, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_479[_2828], _479[_2828 + 1], _479[_2828 + 2])); + } + else + { + float _2789 = log(_2684); + float _2790 = _2789 / _1065; + float _2823; + if ((_2775 >= _2782) && (_2775 < _2790)) + { + float _2806 = (7.0 * (_2775 - _2782)) / (_2790 - _2782); + int _2807 = int(_2806); + float _2809 = _2806 - float(_2807); + _2823 = dot(float3(_2809 * _2809, _2809, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_480[_2807], _480[_2807 + 1], _480[_2807 + 2])); + } + else + { + _2823 = (_2775 * 0.0599999986588954925537109375) + ((log(1000.0) / _1065) - ((0.0599999986588954925537109375 * _2789) / _1065)); + } + _2844 = _2823; + } + _2852 = _2844; + } + float3 _2854 = _2770; + _2854.y = pow(10.0, _2852); + float _2855 = _2479.z; + float _2859 = log((_2855 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2855) / _1065; + float _2936; + if (_2859 <= _2691) + { + _2936 = (_2859 * 3.0) + ((log(9.9999997473787516355514526367188e-05) / _1065) - ((3.0 * _2690) / _1065)); + } + else + { + float _2866 = log(_2612) / _1065; + float _2928; + if ((_2859 > _2691) && (_2859 < _2866)) + { + float _2911 = (7.0 * (_2859 - _2691)) / (_2866 - _2691); + int _2912 = int(_2911); + float _2914 = _2911 - float(_2912); + _2928 = dot(float3(_2914 * _2914, _2914, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_479[_2912], _479[_2912 + 1], _479[_2912 + 2])); + } + else + { + float _2873 = log(_2684); + float _2874 = _2873 / _1065; + float _2907; + if ((_2859 >= _2866) && (_2859 < _2874)) + { + float _2890 = (7.0 * (_2859 - _2866)) / (_2874 - _2866); + int _2891 = int(_2890); + float _2893 = _2890 - float(_2891); + _2907 = dot(float3(_2893 * _2893, _2893, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_480[_2891], _480[_2891 + 1], _480[_2891 + 2])); + } + else + { + _2907 = (_2859 * 0.0599999986588954925537109375) + ((log(1000.0) / _1065) - ((0.0599999986588954925537109375 * _2873) / _1065)); + } + _2928 = _2907; + } + _2936 = _2928; + } + float3 _2938 = _2854; + _2938.z = pow(10.0, _2936); + float3 _2942 = pow(((_2938 - float3(3.5073844628641381859779357910156e-05)) * _576) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _2950 = pow((float3(0.8359375) + (float3(18.8515625) * _2942)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _2942))), float3(78.84375)); + } + else + { + float3 _2097; + if ((_Globals.OutputDevice == 4u) || (_Globals.OutputDevice == 6u)) + { + float3 _1263 = (_906 * float3(1.5)) * (_546 * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625))); + float _1264 = _1263.x; + float _1265 = _1263.y; + float _1267 = _1263.z; + float _1270 = fast::max(fast::max(_1264, _1265), _1267); + float _1275 = (fast::max(_1270, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_1264, _1265), _1267), 1.0000000133514319600180897396058e-10)) / fast::max(_1270, 0.00999999977648258209228515625); + float _1288 = ((_1267 + _1265) + _1264) + (1.75 * sqrt(((_1267 * (_1267 - _1265)) + (_1265 * (_1265 - _1264))) + (_1264 * (_1264 - _1267)))); + float _1289 = _1288 * 0.3333333432674407958984375; + float _1290 = _1275 - 0.4000000059604644775390625; + float _1295 = fast::max(1.0 - abs(_1290 * 2.5), 0.0); + float _1303 = (1.0 + (float(int(sign(_1290 * 5.0))) * (1.0 - (_1295 * _1295)))) * 0.02500000037252902984619140625; + float _1316; + if (_1289 <= 0.053333334624767303466796875) + { + _1316 = _1303; + } + else + { + float _1315; + if (_1289 >= 0.1599999964237213134765625) + { + _1315 = 0.0; + } + else + { + _1315 = _1303 * ((0.23999999463558197021484375 / _1288) - 0.5); + } + _1316 = _1315; + } + float3 _1319 = _1263 * float3(1.0 + _1316); + float _1320 = _1319.x; + float _1321 = _1319.y; + float _1323 = _1319.z; + float _1337; + if ((_1320 == _1321) && (_1321 == _1323)) + { + _1337 = 0.0; + } + else + { + _1337 = 57.2957763671875 * atan2(sqrt(3.0) * (_1321 - _1323), ((2.0 * _1320) - _1321) - _1323); + } + float _1342; + if (_1337 < 0.0) + { + _1342 = _1337 + 360.0; + } + else + { + _1342 = _1337; + } + float _1343 = fast::clamp(_1342, 0.0, 360.0); + float _1348; + if (_1343 > 180.0) + { + _1348 = _1343 - 360.0; + } + else + { + _1348 = _1343; + } + float _1398; + if ((_1348 > (-67.5)) && (_1348 < 67.5)) + { + float _1355 = (_1348 - (-67.5)) * 0.0296296291053295135498046875; + int _1356 = int(_1355); + float _1358 = _1355 - float(_1356); + float _1359 = _1358 * _1358; + float _1360 = _1359 * _1358; + float _1397; + if (_1356 == 3) + { + _1397 = (((_1360 * (-0.16666667163372039794921875)) + (_1359 * 0.5)) + (_1358 * (-0.5))) + 0.16666667163372039794921875; + } + else + { + float _1390; + if (_1356 == 2) + { + _1390 = ((_1360 * 0.5) + (_1359 * (-1.0))) + 0.666666686534881591796875; + } + else + { + float _1385; + if (_1356 == 1) + { + _1385 = (((_1360 * (-0.5)) + (_1359 * 0.5)) + (_1358 * 0.5)) + 0.16666667163372039794921875; + } + else + { + float _1378; + if (_1356 == 0) + { + _1378 = _1360 * 0.16666667163372039794921875; + } + else + { + _1378 = 0.0; + } + _1385 = _1378; + } + _1390 = _1385; + } + _1397 = _1390; + } + _1398 = _1397; + } + else + { + _1398 = 0.0; + } + float3 _1405 = _1319; + _1405.x = _1320 + ((((_1398 * 1.5) * _1275) * (0.02999999932944774627685546875 - _1320)) * 0.180000007152557373046875); + float3 _1408 = fast::clamp(fast::clamp(_1405, float3(0.0), float3(65535.0)) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)), float3(0.0), float3(65535.0)); + float3 _1411 = mix(float3(dot(_1408, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1408, float3(0.959999978542327880859375)); + float _1412 = _1411.x; + float _1416 = 0.17999999225139617919921875 * exp2(18.0); + float _1418 = exp2(-14.0); + float _1421 = log((_1412 <= 0.0) ? _1418 : _1412) / _1065; + float _1423 = log(0.17999999225139617919921875 * exp2(-15.0)) / _1065; + float _1490; + if (_1421 <= _1423) + { + _1490 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _1430 = log(0.180000007152557373046875) / _1065; + float _1487; + if ((_1421 > _1423) && (_1421 < _1430)) + { + float _1470 = (3.0 * (_1421 - _1423)) / (_1430 - _1423); + int _1471 = int(_1470); + float _1473 = _1470 - float(_1471); + _1487 = dot(float3(_1473 * _1473, _1473, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_1471], _475[_1471 + 1], _475[_1471 + 2])); + } + else + { + float _1438 = log(_1416) / _1065; + float _1466; + if ((_1421 >= _1430) && (_1421 < _1438)) + { + float _1449 = (3.0 * (_1421 - _1430)) / (_1438 - _1430); + int _1450 = int(_1449); + float _1452 = _1449 - float(_1450); + _1466 = dot(float3(_1452 * _1452, _1452, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1450], _476[_1450 + 1], _476[_1450 + 2])); + } + else + { + _1466 = log(10000.0) / _1065; + } + _1487 = _1466; + } + _1490 = _1487; + } + float3 _1492 = _391; + _1492.x = pow(10.0, _1490); + float _1493 = _1411.y; + float _1497 = log((_1493 <= 0.0) ? _1418 : _1493) / _1065; + float _1564; + if (_1497 <= _1423) + { + _1564 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _1504 = log(0.180000007152557373046875) / _1065; + float _1561; + if ((_1497 > _1423) && (_1497 < _1504)) + { + float _1544 = (3.0 * (_1497 - _1423)) / (_1504 - _1423); + int _1545 = int(_1544); + float _1547 = _1544 - float(_1545); + _1561 = dot(float3(_1547 * _1547, _1547, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_1545], _475[_1545 + 1], _475[_1545 + 2])); + } + else + { + float _1512 = log(_1416) / _1065; + float _1540; + if ((_1497 >= _1504) && (_1497 < _1512)) + { + float _1523 = (3.0 * (_1497 - _1504)) / (_1512 - _1504); + int _1524 = int(_1523); + float _1526 = _1523 - float(_1524); + _1540 = dot(float3(_1526 * _1526, _1526, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1524], _476[_1524 + 1], _476[_1524 + 2])); + } + else + { + _1540 = log(10000.0) / _1065; + } + _1561 = _1540; + } + _1564 = _1561; + } + float3 _1566 = _1492; + _1566.y = pow(10.0, _1564); + float _1567 = _1411.z; + float _1571 = log((_1567 <= 0.0) ? _1418 : _1567) / _1065; + float _1638; + if (_1571 <= _1423) + { + _1638 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _1578 = log(0.180000007152557373046875) / _1065; + float _1635; + if ((_1571 > _1423) && (_1571 < _1578)) + { + float _1618 = (3.0 * (_1571 - _1423)) / (_1578 - _1423); + int _1619 = int(_1618); + float _1621 = _1618 - float(_1619); + _1635 = dot(float3(_1621 * _1621, _1621, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_1619], _475[_1619 + 1], _475[_1619 + 2])); + } + else + { + float _1586 = log(_1416) / _1065; + float _1614; + if ((_1571 >= _1578) && (_1571 < _1586)) + { + float _1597 = (3.0 * (_1571 - _1578)) / (_1586 - _1578); + int _1598 = int(_1597); + float _1600 = _1597 - float(_1598); + _1614 = dot(float3(_1600 * _1600, _1600, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1598], _476[_1598 + 1], _476[_1598 + 2])); + } + else + { + _1614 = log(10000.0) / _1065; + } + _1635 = _1614; + } + _1638 = _1635; + } + float3 _1640 = _1566; + _1640.z = pow(10.0, _1638); + float3 _1642 = (_1640 * float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625))) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)); + float _1644 = 0.17999999225139617919921875 * pow(2.0, -12.0); + float _1648 = log((_1644 <= 0.0) ? _1418 : _1644) / _1065; + float _1715; + if (_1648 <= _1423) + { + _1715 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _1655 = log(0.180000007152557373046875) / _1065; + float _1712; + if ((_1648 > _1423) && (_1648 < _1655)) + { + float _1695 = (3.0 * (_1648 - _1423)) / (_1655 - _1423); + int _1696 = int(_1695); + float _1698 = _1695 - float(_1696); + _1712 = dot(float3(_1698 * _1698, _1698, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_1696], _475[_1696 + 1], _475[_1696 + 2])); + } + else + { + float _1663 = log(_1416) / _1065; + float _1691; + if ((_1648 >= _1655) && (_1648 < _1663)) + { + float _1674 = (3.0 * (_1648 - _1655)) / (_1663 - _1655); + int _1675 = int(_1674); + float _1677 = _1674 - float(_1675); + _1691 = dot(float3(_1677 * _1677, _1677, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1675], _476[_1675 + 1], _476[_1675 + 2])); + } + else + { + _1691 = log(10000.0) / _1065; + } + _1712 = _1691; + } + _1715 = _1712; + } + float _1718 = log(0.180000007152557373046875) / _1065; + float _1774; + if (_1718 <= _1423) + { + _1774 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _1771; + if ((_1718 > _1423) && (_1718 < _1718)) + { + _1771 = (float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[3], _475[4], _475[5])).z; + } + else + { + float _1731 = log(_1416) / _1065; + float _1759; + if ((_1718 >= _1718) && (_1718 < _1731)) + { + float _1742 = (3.0 * (_1718 - _1718)) / (_1731 - _1718); + int _1743 = int(_1742); + float _1745 = _1742 - float(_1743); + _1759 = dot(float3(_1745 * _1745, _1745, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1743], _476[_1743 + 1], _476[_1743 + 2])); + } + else + { + _1759 = log(10000.0) / _1065; + } + _1771 = _1759; + } + _1774 = _1771; + } + float _1775 = pow(10.0, _1774); + float _1777 = 0.17999999225139617919921875 * pow(2.0, 11.0); + float _1781 = log((_1777 <= 0.0) ? _1418 : _1777) / _1065; + float _1846; + if (_1781 <= _1423) + { + _1846 = log(9.9999997473787516355514526367188e-05) / _1065; + } + else + { + float _1843; + if ((_1781 > _1423) && (_1781 < _1718)) + { + float _1826 = (3.0 * (_1781 - _1423)) / (_1718 - _1423); + int _1827 = int(_1826); + float _1829 = _1826 - float(_1827); + _1843 = dot(float3(_1829 * _1829, _1829, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_475[_1827], _475[_1827 + 1], _475[_1827 + 2])); + } + else + { + float _1794 = log(_1416) / _1065; + float _1822; + if ((_1781 >= _1718) && (_1781 < _1794)) + { + float _1805 = (3.0 * (_1781 - _1718)) / (_1794 - _1718); + int _1806 = int(_1805); + float _1808 = _1805 - float(_1806); + _1822 = dot(float3(_1808 * _1808, _1808, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_476[_1806], _476[_1806 + 1], _476[_1806 + 2])); + } + else + { + _1822 = log(10000.0) / _1065; + } + _1843 = _1822; + } + _1846 = _1843; + } + float _1847 = pow(10.0, _1846); + float _1848 = _1642.x; + float _1852 = log((_1848 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _1848) / _1065; + float _1854 = log(pow(10.0, _1715)) / _1065; + float _1926; + if (_1852 <= _1854) + { + _1926 = log(0.004999999888241291046142578125) / _1065; + } + else + { + float _1861 = log(_1775) / _1065; + float _1923; + if ((_1852 > _1854) && (_1852 < _1861)) + { + float _1906 = (7.0 * (_1852 - _1854)) / (_1861 - _1854); + int _1907 = int(_1906); + float _1909 = _1906 - float(_1907); + _1923 = dot(float3(_1909 * _1909, _1909, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_482[_1907], _482[_1907 + 1], _482[_1907 + 2])); + } + else + { + float _1868 = log(_1847); + float _1869 = _1868 / _1065; + float _1902; + if ((_1852 >= _1861) && (_1852 < _1869)) + { + float _1885 = (7.0 * (_1852 - _1861)) / (_1869 - _1861); + int _1886 = int(_1885); + float _1888 = _1885 - float(_1886); + _1902 = dot(float3(_1888 * _1888, _1888, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_483[_1886], _483[_1886 + 1], _483[_1886 + 2])); + } + else + { + _1902 = (_1852 * 0.119999997317790985107421875) + ((log(2000.0) / _1065) - ((0.119999997317790985107421875 * _1868) / _1065)); + } + _1923 = _1902; + } + _1926 = _1923; + } + float3 _1928 = _391; + _1928.x = pow(10.0, _1926); + float _1929 = _1642.y; + float _1933 = log((_1929 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _1929) / _1065; + float _2005; + if (_1933 <= _1854) + { + _2005 = log(0.004999999888241291046142578125) / _1065; + } + else + { + float _1940 = log(_1775) / _1065; + float _2002; + if ((_1933 > _1854) && (_1933 < _1940)) + { + float _1985 = (7.0 * (_1933 - _1854)) / (_1940 - _1854); + int _1986 = int(_1985); + float _1988 = _1985 - float(_1986); + _2002 = dot(float3(_1988 * _1988, _1988, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_482[_1986], _482[_1986 + 1], _482[_1986 + 2])); + } + else + { + float _1947 = log(_1847); + float _1948 = _1947 / _1065; + float _1981; + if ((_1933 >= _1940) && (_1933 < _1948)) + { + float _1964 = (7.0 * (_1933 - _1940)) / (_1948 - _1940); + int _1965 = int(_1964); + float _1967 = _1964 - float(_1965); + _1981 = dot(float3(_1967 * _1967, _1967, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_483[_1965], _483[_1965 + 1], _483[_1965 + 2])); + } + else + { + _1981 = (_1933 * 0.119999997317790985107421875) + ((log(2000.0) / _1065) - ((0.119999997317790985107421875 * _1947) / _1065)); + } + _2002 = _1981; + } + _2005 = _2002; + } + float3 _2007 = _1928; + _2007.y = pow(10.0, _2005); + float _2008 = _1642.z; + float _2012 = log((_2008 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2008) / _1065; + float _2084; + if (_2012 <= _1854) + { + _2084 = log(0.004999999888241291046142578125) / _1065; + } + else + { + float _2019 = log(_1775) / _1065; + float _2081; + if ((_2012 > _1854) && (_2012 < _2019)) + { + float _2064 = (7.0 * (_2012 - _1854)) / (_2019 - _1854); + int _2065 = int(_2064); + float _2067 = _2064 - float(_2065); + _2081 = dot(float3(_2067 * _2067, _2067, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_482[_2065], _482[_2065 + 1], _482[_2065 + 2])); + } + else + { + float _2026 = log(_1847); + float _2027 = _2026 / _1065; + float _2060; + if ((_2012 >= _2019) && (_2012 < _2027)) + { + float _2043 = (7.0 * (_2012 - _2019)) / (_2027 - _2019); + int _2044 = int(_2043); + float _2046 = _2043 - float(_2044); + _2060 = dot(float3(_2046 * _2046, _2046, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_483[_2044], _483[_2044 + 1], _483[_2044 + 2])); + } + else + { + _2060 = (_2012 * 0.119999997317790985107421875) + ((log(2000.0) / _1065) - ((0.119999997317790985107421875 * _2026) / _1065)); + } + _2081 = _2060; + } + _2084 = _2081; + } + float3 _2086 = _2007; + _2086.z = pow(10.0, _2084); + float3 _2089 = pow((_2086 * _576) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _2097 = pow((float3(0.8359375) + (float3(18.8515625) * _2089)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _2089))), float3(78.84375)); + } + else + { + float3 _1260; + if (_Globals.OutputDevice == 7u) + { + float3 _1252 = pow(((_906 * _547) * _576) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _1260 = pow((float3(0.8359375) + (float3(18.8515625) * _1252)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _1252))), float3(78.84375)); + } + else + { + _1260 = pow((_1218 * _547) * _576, float3(_Globals.InverseGamma.z)); + } + _2097 = _1260; + } + _2950 = _2097; + } + _2960 = _2950; + } + _3001 = _2960; + } + float3 _3002 = _3001 * float3(0.95238101482391357421875); + float4 _3004 = float4(_3002.x, _3002.y, _3002.z, float4(0.0).w); + _3004.w = 0.0; + out.out_var_SV_Target0 = _3004; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag new file mode 100644 index 0000000..67fa21a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag @@ -0,0 +1,1414 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_Globals +{ + float4 MappingPolynomial; + float3 InverseGamma; + float4 ColorMatrixR_ColorCurveCd1; + float4 ColorMatrixG_ColorCurveCd3Cm3; + float4 ColorMatrixB_ColorCurveCm2; + float4 ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3; + float4 ColorCurve_Ch1_Ch2; + float4 ColorShadow_Luma; + float4 ColorShadow_Tint1; + float4 ColorShadow_Tint2; + float FilmSlope; + float FilmToe; + float FilmShoulder; + float FilmBlackClip; + float FilmWhiteClip; + float4 LUTWeights[5]; + float3 ColorScale; + float4 OverlayColor; + float WhiteTemp; + float WhiteTint; + float4 ColorSaturation; + float4 ColorContrast; + float4 ColorGamma; + float4 ColorGain; + float4 ColorOffset; + float4 ColorSaturationShadows; + float4 ColorContrastShadows; + float4 ColorGammaShadows; + float4 ColorGainShadows; + float4 ColorOffsetShadows; + float4 ColorSaturationMidtones; + float4 ColorContrastMidtones; + float4 ColorGammaMidtones; + float4 ColorGainMidtones; + float4 ColorOffsetMidtones; + float4 ColorSaturationHighlights; + float4 ColorContrastHighlights; + float4 ColorGammaHighlights; + float4 ColorGainHighlights; + float4 ColorOffsetHighlights; + float ColorCorrectionShadowsMax; + float ColorCorrectionHighlightsMin; + uint OutputDevice; + uint OutputGamut; + float BlueCorrection; + float ExpandGamut; +}; + +constant spvUnsafeArray _499 = spvUnsafeArray({ -4.0, -4.0, -3.1573765277862548828125, -0.485249996185302734375, 1.84773242473602294921875, 1.84773242473602294921875 }); +constant spvUnsafeArray _500 = spvUnsafeArray({ -0.718548238277435302734375, 2.0810306072235107421875, 3.66812419891357421875, 4.0, 4.0, 4.0 }); +constant spvUnsafeArray _503 = spvUnsafeArray({ -4.97062206268310546875, -3.0293781757354736328125, -2.1261999607086181640625, -1.5104999542236328125, -1.0578000545501708984375, -0.4668000042438507080078125, 0.11937999725341796875, 0.7088134288787841796875, 1.2911865711212158203125, 1.2911865711212158203125 }); +constant spvUnsafeArray _504 = spvUnsafeArray({ 0.80891323089599609375, 1.19108676910400390625, 1.5683000087738037109375, 1.94830000400543212890625, 2.308300018310546875, 2.63840007781982421875, 2.85949993133544921875, 2.9872608184814453125, 3.0127391815185546875, 3.0127391815185546875 }); +constant spvUnsafeArray _506 = spvUnsafeArray({ -2.3010299205780029296875, -2.3010299205780029296875, -1.9312000274658203125, -1.5204999446868896484375, -1.0578000545501708984375, -0.4668000042438507080078125, 0.11937999725341796875, 0.7088134288787841796875, 1.2911865711212158203125, 1.2911865711212158203125 }); +constant spvUnsafeArray _507 = spvUnsafeArray({ 0.801995217800140380859375, 1.19800484180450439453125, 1.5943000316619873046875, 1.99730002880096435546875, 2.3782999515533447265625, 2.7683999538421630859375, 3.0515000820159912109375, 3.2746293544769287109375, 3.32743072509765625, 3.32743072509765625 }); + +constant float3 _523 = {}; +constant float3 _525 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + float2 in_var_TEXCOORD0 [[user(locn0), center_no_perspective]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_Globals& _Globals [[buffer(0)]], texture2d Texture1 [[texture(0)]], sampler Texture1Sampler [[sampler(0)]], uint gl_Layer [[render_target_array_index]]) +{ + main0_out out = {}; + float3x3 _572 = float3x3(float3(0.41245639324188232421875, 0.3575761020183563232421875, 0.180437505245208740234375), float3(0.21267290413379669189453125, 0.715152204036712646484375, 0.072175003588199615478515625), float3(0.01933390088379383087158203125, 0.119191996753215789794921875, 0.950304090976715087890625)) * float3x3(float3(1.01303005218505859375, 0.0061053098179399967193603515625, -0.014971000142395496368408203125), float3(0.0076982299797236919403076171875, 0.99816501140594482421875, -0.005032029934227466583251953125), float3(-0.0028413101099431514739990234375, 0.0046851597726345062255859375, 0.92450702190399169921875)); + float3x3 _573 = _572 * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)); + float3x3 _574 = float3x3(float3(0.662454187870025634765625, 0.1340042054653167724609375, 0.1561876833438873291015625), float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625), float3(-0.0055746496655046939849853515625, 0.0040607335977256298065185546875, 1.01033914089202880859375)) * float3x3(float3(0.98722398281097412109375, -0.0061132698319852352142333984375, 0.01595330052077770233154296875), float3(-0.007598360069096088409423828125, 1.00186002254486083984375, 0.0053300200961530208587646484375), float3(0.003072570078074932098388671875, -0.0050959498621523380279541015625, 1.0816800594329833984375)); + float3x3 _575 = _574 * float3x3(float3(3.2409698963165283203125, -1.53738319873809814453125, -0.4986107647418975830078125), float3(-0.96924364566802978515625, 1.875967502593994140625, 0.0415550582110881805419921875), float3(0.055630080401897430419921875, -0.2039769589900970458984375, 1.05697154998779296875)); + float3x3 _576 = float3x3(float3(0.952552378177642822265625, 0.0, 9.25), float3(0.3439664542675018310546875, 0.728166103363037109375, -0.07213254272937774658203125), float3(0.0, 0.0, 1.00882518291473388671875)) * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)); + float3x3 _577 = float3x3(float3(0.662454187870025634765625, 0.1340042054653167724609375, 0.1561876833438873291015625), float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625), float3(-0.0055746496655046939849853515625, 0.0040607335977256298065185546875, 1.01033914089202880859375)) * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625)); + float3x3 _602; + for (;;) + { + if (_Globals.OutputGamut == 1u) + { + _602 = _574 * float3x3(float3(2.493396282196044921875, -0.931345880031585693359375, -0.4026944935321807861328125), float3(-0.829486787319183349609375, 1.76265966892242431640625, 0.02362460084259510040283203125), float3(0.0358506999909877777099609375, -0.076182700693607330322265625, 0.957014024257659912109375)); + break; + } + else + { + if (_Globals.OutputGamut == 2u) + { + _602 = _574 * float3x3(float3(1.71660840511322021484375, -0.3556621074676513671875, -0.253360092639923095703125), float3(-0.666682898998260498046875, 1.61647760868072509765625, 0.01576850004494190216064453125), float3(0.017642199993133544921875, -0.04277630150318145751953125, 0.94222867488861083984375)); + break; + } + else + { + if (_Globals.OutputGamut == 3u) + { + _602 = float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625)); + break; + } + else + { + if (_Globals.OutputGamut == 4u) + { + _602 = float3x3(float3(1.0, 0.0, 0.0), float3(0.0, 1.0, 0.0), float3(0.0, 0.0, 1.0)); + break; + } + else + { + _602 = _575; + break; + } + } + } + } + } + float3 _603 = float4((in.in_var_TEXCOORD0 - float2(0.015625)) * float2(1.03225803375244140625), float(gl_Layer) * 0.0322580635547637939453125, 0.0).xyz; + float3 _625; + if (_Globals.OutputDevice >= 3u) + { + float3 _617 = pow(_603, float3(0.0126833133399486541748046875)); + _625 = pow(fast::max(float3(0.0), _617 - float3(0.8359375)) / (float3(18.8515625) - (float3(18.6875) * _617)), float3(6.277394771575927734375)) * float3(10000.0); + } + else + { + _625 = (exp2((_603 - float3(0.434017598628997802734375)) * float3(14.0)) * float3(0.180000007152557373046875)) - (exp2(float3(-6.0762462615966796875)) * float3(0.180000007152557373046875)); + } + float _628 = _Globals.WhiteTemp * 1.00055634975433349609375; + float _642 = (_628 <= 7000.0) ? (0.24406300485134124755859375 + ((99.1100006103515625 + ((2967800.0 - (4604438528.0 / _Globals.WhiteTemp)) / _628)) / _628)) : (0.23703999817371368408203125 + ((247.4799957275390625 + ((1901800.0 - (2005284352.0 / _Globals.WhiteTemp)) / _628)) / _628)); + float _659 = ((0.860117733478546142578125 + (0.00015411825734190642833709716796875 * _Globals.WhiteTemp)) + ((1.2864121856637211749330163002014e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)) / ((1.0 + (0.0008424202096648514270782470703125 * _Globals.WhiteTemp)) + ((7.0814513719597016461193561553955e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)); + float _670 = ((0.317398726940155029296875 + (4.25 * _Globals.WhiteTemp)) + ((4.2048167614439080352894961833954e-08 * _Globals.WhiteTemp) * _Globals.WhiteTemp)) / ((1.0 - (2.8974181986995972692966461181641e-05 * _Globals.WhiteTemp)) + ((1.6145605741257895715534687042236e-07 * _Globals.WhiteTemp) * _Globals.WhiteTemp)); + float _675 = ((2.0 * _659) - (8.0 * _670)) + 4.0; + float2 _679 = float2((3.0 * _659) / _675, (2.0 * _670) / _675); + float2 _686 = normalize(float2(_659, _670)); + float _691 = _659 + (((-_686.y) * _Globals.WhiteTint) * 0.0500000007450580596923828125); + float _695 = _670 + ((_686.x * _Globals.WhiteTint) * 0.0500000007450580596923828125); + float _700 = ((2.0 * _691) - (8.0 * _695)) + 4.0; + float2 _706 = select(float2(_642, ((((-3.0) * _642) * _642) + (2.86999988555908203125 * _642)) - 0.2750000059604644775390625), _679, bool2(_Globals.WhiteTemp < 4000.0)) + (float2((3.0 * _691) / _700, (2.0 * _695) / _700) - _679); + float _707 = _706.x; + float _708 = _706.y; + float _709 = fast::max(_708, 1.0000000133514319600180897396058e-10); + float3 _711 = _523; + _711.x = _707 / _709; + float3 _712 = _711; + _712.y = 1.0; + float3 _716 = _712; + _716.z = ((1.0 - _707) - _708) / _709; + float _717 = fast::max(0.328999996185302734375, 1.0000000133514319600180897396058e-10); + float3 _719 = _523; + _719.x = 0.3127000033855438232421875 / _717; + float3 _720 = _719; + _720.y = 1.0; + float3 _722 = _720; + _722.z = 0.3582999706268310546875 / _717; + float3 _723 = _716 * float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)); + float3 _724 = _722 * float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)); + float3 _743 = (_625 * ((float3x3(float3(0.41245639324188232421875, 0.3575761020183563232421875, 0.180437505245208740234375), float3(0.21267290413379669189453125, 0.715152204036712646484375, 0.072175003588199615478515625), float3(0.01933390088379383087158203125, 0.119191996753215789794921875, 0.950304090976715087890625)) * ((float3x3(float3(0.89509999752044677734375, 0.2664000093936920166015625, -0.16140000522136688232421875), float3(-0.750199973583221435546875, 1.71350002288818359375, 0.0366999991238117218017578125), float3(0.0388999991118907928466796875, -0.06849999725818634033203125, 1.02960002422332763671875)) * float3x3(float3(_724.x / _723.x, 0.0, 0.0), float3(0.0, _724.y / _723.y, 0.0), float3(0.0, 0.0, _724.z / _723.z))) * float3x3(float3(0.986992895603179931640625, -0.14705429971218109130859375, 0.15996269881725311279296875), float3(0.4323053061962127685546875, 0.518360316753387451171875, 0.049291200935840606689453125), float3(-0.00852870009839534759521484375, 0.0400427989661693572998046875, 0.968486726284027099609375)))) * float3x3(float3(3.2409698963165283203125, -1.53738319873809814453125, -0.4986107647418975830078125), float3(-0.96924364566802978515625, 1.875967502593994140625, 0.0415550582110881805419921875), float3(0.055630080401897430419921875, -0.2039769589900970458984375, 1.05697154998779296875)))) * _573; + float3 _771; + if (_Globals.ColorShadow_Tint2.w != 0.0) + { + float _750 = dot(_743, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625)); + float3 _753 = (_743 / float3(_750)) - float3(1.0); + _771 = mix(_743, _743 * (_575 * (float3x3(float3(0.544169127941131591796875, 0.23959259688854217529296875, 0.16669429838657379150390625), float3(0.23946559429168701171875, 0.702153027057647705078125, 0.058381401002407073974609375), float3(-0.0023439000360667705535888671875, 0.0361833982169628143310546875, 1.05521833896636962890625)) * float3x3(float3(1.6410233974456787109375, -0.324803292751312255859375, -0.23642469942569732666015625), float3(-0.663662850856781005859375, 1.6153316497802734375, 0.016756348311901092529296875), float3(0.01172189414501190185546875, -0.008284442126750946044921875, 0.98839485645294189453125)))), float3((1.0 - exp2((-4.0) * dot(_753, _753))) * (1.0 - exp2((((-4.0) * _Globals.ExpandGamut) * _750) * _750)))); + } + else + { + _771 = _743; + } + float _772 = dot(_771, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625)); + float4 _777 = _Globals.ColorSaturationShadows * _Globals.ColorSaturation; + float4 _782 = _Globals.ColorContrastShadows * _Globals.ColorContrast; + float4 _787 = _Globals.ColorGammaShadows * _Globals.ColorGamma; + float4 _792 = _Globals.ColorGainShadows * _Globals.ColorGain; + float4 _797 = _Globals.ColorOffsetShadows + _Globals.ColorOffset; + float3 _798 = float3(_772); + float _830 = smoothstep(0.0, _Globals.ColorCorrectionShadowsMax, _772); + float4 _834 = _Globals.ColorSaturationHighlights * _Globals.ColorSaturation; + float4 _837 = _Globals.ColorContrastHighlights * _Globals.ColorContrast; + float4 _840 = _Globals.ColorGammaHighlights * _Globals.ColorGamma; + float4 _843 = _Globals.ColorGainHighlights * _Globals.ColorGain; + float4 _846 = _Globals.ColorOffsetHighlights + _Globals.ColorOffset; + float _878 = smoothstep(_Globals.ColorCorrectionHighlightsMin, 1.0, _772); + float4 _881 = _Globals.ColorSaturationMidtones * _Globals.ColorSaturation; + float4 _884 = _Globals.ColorContrastMidtones * _Globals.ColorContrast; + float4 _887 = _Globals.ColorGammaMidtones * _Globals.ColorGamma; + float4 _890 = _Globals.ColorGainMidtones * _Globals.ColorGain; + float4 _893 = _Globals.ColorOffsetMidtones + _Globals.ColorOffset; + float3 _931 = ((((pow(pow(fast::max(float3(0.0), mix(_798, _771, _777.xyz * float3(_777.w))) * float3(5.5555553436279296875), _782.xyz * float3(_782.w)) * float3(0.180000007152557373046875), float3(1.0) / (_787.xyz * float3(_787.w))) * (_792.xyz * float3(_792.w))) + (_797.xyz + float3(_797.w))) * float3(1.0 - _830)) + (((pow(pow(fast::max(float3(0.0), mix(_798, _771, _881.xyz * float3(_881.w))) * float3(5.5555553436279296875), _884.xyz * float3(_884.w)) * float3(0.180000007152557373046875), float3(1.0) / (_887.xyz * float3(_887.w))) * (_890.xyz * float3(_890.w))) + (_893.xyz + float3(_893.w))) * float3(_830 - _878))) + (((pow(pow(fast::max(float3(0.0), mix(_798, _771, _834.xyz * float3(_834.w))) * float3(5.5555553436279296875), _837.xyz * float3(_837.w)) * float3(0.180000007152557373046875), float3(1.0) / (_840.xyz * float3(_840.w))) * (_843.xyz * float3(_843.w))) + (_846.xyz + float3(_846.w))) * float3(_878)); + float3 _932 = _931 * _575; + float3 _940 = float3(_Globals.BlueCorrection); + float3 _942 = mix(_931, _931 * ((_577 * float3x3(float3(0.940437257289886474609375, -0.01830687932670116424560546875, 0.07786960899829864501953125), float3(0.008378696627914905548095703125, 0.82866001129150390625, 0.162961304187774658203125), float3(0.0005471261101774871349334716796875, -0.00088337459601461887359619140625, 1.00033628940582275390625))) * _576), _940) * _577; + float _943 = _942.x; + float _944 = _942.y; + float _946 = _942.z; + float _949 = fast::max(fast::max(_943, _944), _946); + float _954 = (fast::max(_949, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_943, _944), _946), 1.0000000133514319600180897396058e-10)) / fast::max(_949, 0.00999999977648258209228515625); + float _967 = ((_946 + _944) + _943) + (1.75 * sqrt(((_946 * (_946 - _944)) + (_944 * (_944 - _943))) + (_943 * (_943 - _946)))); + float _968 = _967 * 0.3333333432674407958984375; + float _969 = _954 - 0.4000000059604644775390625; + float _974 = fast::max(1.0 - abs(_969 * 2.5), 0.0); + float _982 = (1.0 + (float(int(sign(_969 * 5.0))) * (1.0 - (_974 * _974)))) * 0.02500000037252902984619140625; + float _995; + if (_968 <= 0.053333334624767303466796875) + { + _995 = _982; + } + else + { + float _994; + if (_968 >= 0.1599999964237213134765625) + { + _994 = 0.0; + } + else + { + _994 = _982 * ((0.23999999463558197021484375 / _967) - 0.5); + } + _995 = _994; + } + float3 _998 = _942 * float3(1.0 + _995); + float _999 = _998.x; + float _1000 = _998.y; + float _1002 = _998.z; + float _1016; + if ((_999 == _1000) && (_1000 == _1002)) + { + _1016 = 0.0; + } + else + { + _1016 = 57.2957763671875 * atan2(sqrt(3.0) * (_1000 - _1002), ((2.0 * _999) - _1000) - _1002); + } + float _1021; + if (_1016 < 0.0) + { + _1021 = _1016 + 360.0; + } + else + { + _1021 = _1016; + } + float _1022 = fast::clamp(_1021, 0.0, 360.0); + float _1027; + if (_1022 > 180.0) + { + _1027 = _1022 - 360.0; + } + else + { + _1027 = _1022; + } + float _1031 = smoothstep(0.0, 1.0, 1.0 - abs(_1027 * 0.01481481455266475677490234375)); + float3 _1038 = _998; + _1038.x = _999 + ((((_1031 * _1031) * _954) * (0.02999999932944774627685546875 - _999)) * 0.180000007152557373046875); + float3 _1040 = fast::max(float3(0.0), _1038 * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375))); + float _1049 = (1.0 + _Globals.FilmBlackClip) - _Globals.FilmToe; + float _1052 = 1.0 + _Globals.FilmWhiteClip; + float _1055 = _1052 - _Globals.FilmShoulder; + float _1082; + if (_Globals.FilmToe > 0.800000011920928955078125) + { + _1082 = ((0.819999992847442626953125 - _Globals.FilmToe) / _Globals.FilmSlope) + (log(0.180000007152557373046875) / log(10.0)); + } + else + { + float _1061 = (0.180000007152557373046875 + _Globals.FilmBlackClip) / _1049; + _1082 = (log(0.180000007152557373046875) / log(10.0)) - ((0.5 * log(_1061 / (2.0 - _1061))) * (_1049 / _Globals.FilmSlope)); + } + float _1087 = ((1.0 - _Globals.FilmToe) / _Globals.FilmSlope) - _1082; + float _1089 = (_Globals.FilmShoulder / _Globals.FilmSlope) - _1087; + float _1091 = log(10.0); + float3 _1093 = log(mix(float3(dot(_1040, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1040, float3(0.959999978542327880859375))) / float3(_1091); + float3 _1097 = float3(_Globals.FilmSlope) * (_1093 + float3(_1087)); + float3 _1105 = float3(_1082); + float3 _1106 = _1093 - _1105; + float3 _1118 = float3(_1089); + float3 _1132 = fast::clamp(_1106 / float3(_1089 - _1082), float3(0.0), float3(1.0)); + float3 _1136 = select(_1132, float3(1.0) - _1132, bool3(_1089 < _1082)); + float3 _1141 = mix(select(_1097, float3(-_Globals.FilmBlackClip) + (float3(2.0 * _1049) / (float3(1.0) + exp(float3(((-2.0) * _Globals.FilmSlope) / _1049) * _1106))), _1093 < _1105), select(_1097, float3(_1052) - (float3(2.0 * _1055) / (float3(1.0) + exp(float3((2.0 * _Globals.FilmSlope) / _1055) * (_1093 - _1118)))), _1093 > _1118), ((float3(3.0) - (float3(2.0) * _1136)) * _1136) * _1136); + float3 _1145 = fast::max(float3(0.0), mix(float3(dot(_1141, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1141, float3(0.930000007152557373046875))); + float3 _1215; + if (_Globals.ColorShadow_Tint2.w == 0.0) + { + float3 _1157 = _525; + _1157.x = dot(_932, _Globals.ColorMatrixR_ColorCurveCd1.xyz); + float3 _1162 = _1157; + _1162.y = dot(_932, _Globals.ColorMatrixG_ColorCurveCd3Cm3.xyz); + float3 _1167 = _1162; + _1167.z = dot(_932, _Globals.ColorMatrixB_ColorCurveCm2.xyz); + float3 _1183 = fast::max(float3(0.0), _1167 * (_Globals.ColorShadow_Tint1.xyz + (_Globals.ColorShadow_Tint2.xyz * float3(1.0 / (dot(_932, _Globals.ColorShadow_Luma.xyz) + 1.0))))); + float3 _1188 = fast::max(float3(0.0), _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.xxx - _1183); + float3 _1190 = fast::max(_1183, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.zzz); + _1215 = ((((_1190 * _Globals.ColorCurve_Ch1_Ch2.xxx) + _Globals.ColorCurve_Ch1_Ch2.yyy) * (float3(1.0) / (_1190 + _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.www))) + ((fast::clamp(_1183, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.xxx, _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.zzz) * _Globals.ColorMatrixB_ColorCurveCm2.www) + (((_1188 * _Globals.ColorMatrixR_ColorCurveCd1.www) * (float3(1.0) / (_1188 + _Globals.ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.yyy))) + _Globals.ColorMatrixG_ColorCurveCd3Cm3.www))) - float3(0.00200000009499490261077880859375); + } + else + { + _1215 = fast::max(float3(0.0), mix(_1145, _1145 * ((_577 * float3x3(float3(1.06317996978759765625, 0.02339559979736804962158203125, -0.08657260239124298095703125), float3(-0.010633699595928192138671875, 1.2063200473785400390625, -0.1956900060176849365234375), float3(-0.0005908869788981974124908447265625, 0.00105247995816171169281005859375, 0.999538004398345947265625))) * _576), _940) * _575); + } + float3 _1216 = fast::clamp(_1215, float3(0.0), float3(1.0)); + float _1217 = _1216.x; + float _1229; + for (;;) + { + if (_1217 < 0.00313066993840038776397705078125) + { + _1229 = _1217 * 12.9200000762939453125; + break; + } + _1229 = (pow(_1217, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _1230 = _1216.y; + float _1242; + for (;;) + { + if (_1230 < 0.00313066993840038776397705078125) + { + _1242 = _1230 * 12.9200000762939453125; + break; + } + _1242 = (pow(_1230, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _1243 = _1216.z; + float _1255; + for (;;) + { + if (_1243 < 0.00313066993840038776397705078125) + { + _1255 = _1243 * 12.9200000762939453125; + break; + } + _1255 = (pow(_1243, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float3 _1256 = float3(_1229, _1242, _1255); + float3 _1258 = (_1256 * float3(0.9375)) + float3(0.03125); + float _1270 = (_1258.z * 16.0) - 0.5; + float _1271 = floor(_1270); + float _1275 = (_1258.x + _1271) * 0.0625; + float _1276 = _1258.y; + float4 _1279 = Texture1.sample(Texture1Sampler, float2(_1275, _1276)); + float4 _1283 = Texture1.sample(Texture1Sampler, float2(_1275 + 0.0625, _1276)); + float3 _1289 = fast::max(float3(6.1035199905745685100555419921875e-05), (float3(_Globals.LUTWeights[0].x) * _1256) + (float3(_Globals.LUTWeights[1].x) * mix(_1279, _1283, float4(_1270 - _1271)).xyz)); + float3 _1295 = select(_1289 * float3(0.077399380505084991455078125), pow((_1289 * float3(0.94786727428436279296875)) + float3(0.0521326996386051177978515625), float3(2.400000095367431640625)), _1289 > float3(0.040449999272823333740234375)); + float3 _1324 = pow(fast::max(float3(0.0), mix((((float3(_Globals.MappingPolynomial.x) * (_1295 * _1295)) + (float3(_Globals.MappingPolynomial.y) * _1295)) + float3(_Globals.MappingPolynomial.z)) * _Globals.ColorScale, _Globals.OverlayColor.xyz, float3(_Globals.OverlayColor.w))), float3(_Globals.InverseGamma.y)); + float3 _3103; + if (_Globals.OutputDevice == 0u) + { + float _3063 = _1324.x; + float _3075; + for (;;) + { + if (_3063 < 0.00313066993840038776397705078125) + { + _3075 = _3063 * 12.9200000762939453125; + break; + } + _3075 = (pow(_3063, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _3076 = _1324.y; + float _3088; + for (;;) + { + if (_3076 < 0.00313066993840038776397705078125) + { + _3088 = _3076 * 12.9200000762939453125; + break; + } + _3088 = (pow(_3076, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + float _3089 = _1324.z; + float _3101; + for (;;) + { + if (_3089 < 0.00313066993840038776397705078125) + { + _3101 = _3089 * 12.9200000762939453125; + break; + } + _3101 = (pow(_3089, 0.4166666567325592041015625) * 1.05499994754791259765625) - 0.054999999701976776123046875; + break; + } + _3103 = float3(_3075, _3088, _3101); + } + else + { + float3 _3062; + if (_Globals.OutputDevice == 1u) + { + float3 _3055 = fast::max(float3(6.1035199905745685100555419921875e-05), (_1324 * _573) * _602); + _3062 = fast::min(_3055 * float3(4.5), (pow(fast::max(_3055, float3(0.017999999225139617919921875)), float3(0.449999988079071044921875)) * float3(1.09899997711181640625)) - float3(0.098999999463558197021484375)); + } + else + { + float3 _3052; + if ((_Globals.OutputDevice == 3u) || (_Globals.OutputDevice == 5u)) + { + float3 _2204 = (_932 * float3(1.5)) * (_572 * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625))); + float _2205 = _2204.x; + float _2206 = _2204.y; + float _2208 = _2204.z; + float _2211 = fast::max(fast::max(_2205, _2206), _2208); + float _2216 = (fast::max(_2211, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_2205, _2206), _2208), 1.0000000133514319600180897396058e-10)) / fast::max(_2211, 0.00999999977648258209228515625); + float _2229 = ((_2208 + _2206) + _2205) + (1.75 * sqrt(((_2208 * (_2208 - _2206)) + (_2206 * (_2206 - _2205))) + (_2205 * (_2205 - _2208)))); + float _2230 = _2229 * 0.3333333432674407958984375; + float _2231 = _2216 - 0.4000000059604644775390625; + float _2236 = fast::max(1.0 - abs(_2231 * 2.5), 0.0); + float _2244 = (1.0 + (float(int(sign(_2231 * 5.0))) * (1.0 - (_2236 * _2236)))) * 0.02500000037252902984619140625; + float _2257; + if (_2230 <= 0.053333334624767303466796875) + { + _2257 = _2244; + } + else + { + float _2256; + if (_2230 >= 0.1599999964237213134765625) + { + _2256 = 0.0; + } + else + { + _2256 = _2244 * ((0.23999999463558197021484375 / _2229) - 0.5); + } + _2257 = _2256; + } + float3 _2260 = _2204 * float3(1.0 + _2257); + float _2261 = _2260.x; + float _2262 = _2260.y; + float _2264 = _2260.z; + float _2278; + if ((_2261 == _2262) && (_2262 == _2264)) + { + _2278 = 0.0; + } + else + { + _2278 = 57.2957763671875 * atan2(sqrt(3.0) * (_2262 - _2264), ((2.0 * _2261) - _2262) - _2264); + } + float _2283; + if (_2278 < 0.0) + { + _2283 = _2278 + 360.0; + } + else + { + _2283 = _2278; + } + float _2284 = fast::clamp(_2283, 0.0, 360.0); + float _2289; + if (_2284 > 180.0) + { + _2289 = _2284 - 360.0; + } + else + { + _2289 = _2284; + } + float _2339; + if ((_2289 > (-67.5)) && (_2289 < 67.5)) + { + float _2296 = (_2289 - (-67.5)) * 0.0296296291053295135498046875; + int _2297 = int(_2296); + float _2299 = _2296 - float(_2297); + float _2300 = _2299 * _2299; + float _2301 = _2300 * _2299; + float _2338; + if (_2297 == 3) + { + _2338 = (((_2301 * (-0.16666667163372039794921875)) + (_2300 * 0.5)) + (_2299 * (-0.5))) + 0.16666667163372039794921875; + } + else + { + float _2331; + if (_2297 == 2) + { + _2331 = ((_2301 * 0.5) + (_2300 * (-1.0))) + 0.666666686534881591796875; + } + else + { + float _2326; + if (_2297 == 1) + { + _2326 = (((_2301 * (-0.5)) + (_2300 * 0.5)) + (_2299 * 0.5)) + 0.16666667163372039794921875; + } + else + { + float _2319; + if (_2297 == 0) + { + _2319 = _2301 * 0.16666667163372039794921875; + } + else + { + _2319 = 0.0; + } + _2326 = _2319; + } + _2331 = _2326; + } + _2338 = _2331; + } + _2339 = _2338; + } + else + { + _2339 = 0.0; + } + float3 _2346 = _2260; + _2346.x = _2261 + ((((_2339 * 1.5) * _2216) * (0.02999999932944774627685546875 - _2261)) * 0.180000007152557373046875); + float3 _2349 = fast::clamp(fast::clamp(_2346, float3(0.0), float3(65535.0)) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)), float3(0.0), float3(65535.0)); + float3 _2352 = mix(float3(dot(_2349, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _2349, float3(0.959999978542327880859375)); + float _2353 = _2352.x; + float _2357 = 0.17999999225139617919921875 * exp2(18.0); + float _2359 = exp2(-14.0); + float _2362 = log((_2353 <= 0.0) ? _2359 : _2353) / _1091; + float _2364 = log(0.17999999225139617919921875 * exp2(-15.0)) / _1091; + float _2431; + if (_2362 <= _2364) + { + _2431 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _2371 = log(0.180000007152557373046875) / _1091; + float _2428; + if ((_2362 > _2364) && (_2362 < _2371)) + { + float _2411 = (3.0 * (_2362 - _2364)) / (_2371 - _2364); + int _2412 = int(_2411); + float _2414 = _2411 - float(_2412); + _2428 = dot(float3(_2414 * _2414, _2414, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_2412], _499[_2412 + 1], _499[_2412 + 2])); + } + else + { + float _2379 = log(_2357) / _1091; + float _2407; + if ((_2362 >= _2371) && (_2362 < _2379)) + { + float _2390 = (3.0 * (_2362 - _2371)) / (_2379 - _2371); + int _2391 = int(_2390); + float _2393 = _2390 - float(_2391); + _2407 = dot(float3(_2393 * _2393, _2393, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2391], _500[_2391 + 1], _500[_2391 + 2])); + } + else + { + _2407 = log(10000.0) / _1091; + } + _2428 = _2407; + } + _2431 = _2428; + } + float3 _2433 = _523; + _2433.x = pow(10.0, _2431); + float _2434 = _2352.y; + float _2438 = log((_2434 <= 0.0) ? _2359 : _2434) / _1091; + float _2505; + if (_2438 <= _2364) + { + _2505 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _2445 = log(0.180000007152557373046875) / _1091; + float _2502; + if ((_2438 > _2364) && (_2438 < _2445)) + { + float _2485 = (3.0 * (_2438 - _2364)) / (_2445 - _2364); + int _2486 = int(_2485); + float _2488 = _2485 - float(_2486); + _2502 = dot(float3(_2488 * _2488, _2488, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_2486], _499[_2486 + 1], _499[_2486 + 2])); + } + else + { + float _2453 = log(_2357) / _1091; + float _2481; + if ((_2438 >= _2445) && (_2438 < _2453)) + { + float _2464 = (3.0 * (_2438 - _2445)) / (_2453 - _2445); + int _2465 = int(_2464); + float _2467 = _2464 - float(_2465); + _2481 = dot(float3(_2467 * _2467, _2467, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2465], _500[_2465 + 1], _500[_2465 + 2])); + } + else + { + _2481 = log(10000.0) / _1091; + } + _2502 = _2481; + } + _2505 = _2502; + } + float3 _2507 = _2433; + _2507.y = pow(10.0, _2505); + float _2508 = _2352.z; + float _2512 = log((_2508 <= 0.0) ? _2359 : _2508) / _1091; + float _2579; + if (_2512 <= _2364) + { + _2579 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _2519 = log(0.180000007152557373046875) / _1091; + float _2576; + if ((_2512 > _2364) && (_2512 < _2519)) + { + float _2559 = (3.0 * (_2512 - _2364)) / (_2519 - _2364); + int _2560 = int(_2559); + float _2562 = _2559 - float(_2560); + _2576 = dot(float3(_2562 * _2562, _2562, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_2560], _499[_2560 + 1], _499[_2560 + 2])); + } + else + { + float _2527 = log(_2357) / _1091; + float _2555; + if ((_2512 >= _2519) && (_2512 < _2527)) + { + float _2538 = (3.0 * (_2512 - _2519)) / (_2527 - _2519); + int _2539 = int(_2538); + float _2541 = _2538 - float(_2539); + _2555 = dot(float3(_2541 * _2541, _2541, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2539], _500[_2539 + 1], _500[_2539 + 2])); + } + else + { + _2555 = log(10000.0) / _1091; + } + _2576 = _2555; + } + _2579 = _2576; + } + float3 _2581 = _2507; + _2581.z = pow(10.0, _2579); + float3 _2583 = (_2581 * float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625))) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)); + float _2585 = 0.17999999225139617919921875 * pow(2.0, -12.0); + float _2589 = log((_2585 <= 0.0) ? _2359 : _2585) / _1091; + float _2656; + if (_2589 <= _2364) + { + _2656 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _2596 = log(0.180000007152557373046875) / _1091; + float _2653; + if ((_2589 > _2364) && (_2589 < _2596)) + { + float _2636 = (3.0 * (_2589 - _2364)) / (_2596 - _2364); + int _2637 = int(_2636); + float _2639 = _2636 - float(_2637); + _2653 = dot(float3(_2639 * _2639, _2639, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_2637], _499[_2637 + 1], _499[_2637 + 2])); + } + else + { + float _2604 = log(_2357) / _1091; + float _2632; + if ((_2589 >= _2596) && (_2589 < _2604)) + { + float _2615 = (3.0 * (_2589 - _2596)) / (_2604 - _2596); + int _2616 = int(_2615); + float _2618 = _2615 - float(_2616); + _2632 = dot(float3(_2618 * _2618, _2618, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2616], _500[_2616 + 1], _500[_2616 + 2])); + } + else + { + _2632 = log(10000.0) / _1091; + } + _2653 = _2632; + } + _2656 = _2653; + } + float _2659 = log(0.180000007152557373046875) / _1091; + float _2713; + if (_2659 <= _2364) + { + _2713 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _2710; + if ((_2659 > _2364) && (_2659 < _2659)) + { + _2710 = (float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[3], _499[4], _499[5])).z; + } + else + { + float _2672 = log(_2357) / _1091; + float _2700; + if ((_2659 >= _2659) && (_2659 < _2672)) + { + float _2683 = (3.0 * (_2659 - _2659)) / (_2672 - _2659); + int _2684 = int(_2683); + float _2686 = _2683 - float(_2684); + _2700 = dot(float3(_2686 * _2686, _2686, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2684], _500[_2684 + 1], _500[_2684 + 2])); + } + else + { + _2700 = log(10000.0) / _1091; + } + _2710 = _2700; + } + _2713 = _2710; + } + float _2714 = pow(10.0, _2713); + float _2716 = 0.17999999225139617919921875 * pow(2.0, 10.0); + float _2720 = log((_2716 <= 0.0) ? _2359 : _2716) / _1091; + float _2785; + if (_2720 <= _2364) + { + _2785 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _2782; + if ((_2720 > _2364) && (_2720 < _2659)) + { + float _2765 = (3.0 * (_2720 - _2364)) / (_2659 - _2364); + int _2766 = int(_2765); + float _2768 = _2765 - float(_2766); + _2782 = dot(float3(_2768 * _2768, _2768, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_2766], _499[_2766 + 1], _499[_2766 + 2])); + } + else + { + float _2733 = log(_2357) / _1091; + float _2761; + if ((_2720 >= _2659) && (_2720 < _2733)) + { + float _2744 = (3.0 * (_2720 - _2659)) / (_2733 - _2659); + int _2745 = int(_2744); + float _2747 = _2744 - float(_2745); + _2761 = dot(float3(_2747 * _2747, _2747, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_2745], _500[_2745 + 1], _500[_2745 + 2])); + } + else + { + _2761 = log(10000.0) / _1091; + } + _2782 = _2761; + } + _2785 = _2782; + } + float _2786 = pow(10.0, _2785); + float _2787 = _2583.x; + float _2791 = log((_2787 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2787) / _1091; + float _2792 = log(pow(10.0, _2656)); + float _2793 = _2792 / _1091; + float _2870; + if (_2791 <= _2793) + { + _2870 = (_2791 * 3.0) + ((log(9.9999997473787516355514526367188e-05) / _1091) - ((3.0 * _2792) / _1091)); + } + else + { + float _2800 = log(_2714) / _1091; + float _2862; + if ((_2791 > _2793) && (_2791 < _2800)) + { + float _2845 = (7.0 * (_2791 - _2793)) / (_2800 - _2793); + int _2846 = int(_2845); + float _2848 = _2845 - float(_2846); + _2862 = dot(float3(_2848 * _2848, _2848, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_503[_2846], _503[_2846 + 1], _503[_2846 + 2])); + } + else + { + float _2807 = log(_2786); + float _2808 = _2807 / _1091; + float _2841; + if ((_2791 >= _2800) && (_2791 < _2808)) + { + float _2824 = (7.0 * (_2791 - _2800)) / (_2808 - _2800); + int _2825 = int(_2824); + float _2827 = _2824 - float(_2825); + _2841 = dot(float3(_2827 * _2827, _2827, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_504[_2825], _504[_2825 + 1], _504[_2825 + 2])); + } + else + { + _2841 = (_2791 * 0.0599999986588954925537109375) + ((log(1000.0) / _1091) - ((0.0599999986588954925537109375 * _2807) / _1091)); + } + _2862 = _2841; + } + _2870 = _2862; + } + float3 _2872 = _523; + _2872.x = pow(10.0, _2870); + float _2873 = _2583.y; + float _2877 = log((_2873 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2873) / _1091; + float _2954; + if (_2877 <= _2793) + { + _2954 = (_2877 * 3.0) + ((log(9.9999997473787516355514526367188e-05) / _1091) - ((3.0 * _2792) / _1091)); + } + else + { + float _2884 = log(_2714) / _1091; + float _2946; + if ((_2877 > _2793) && (_2877 < _2884)) + { + float _2929 = (7.0 * (_2877 - _2793)) / (_2884 - _2793); + int _2930 = int(_2929); + float _2932 = _2929 - float(_2930); + _2946 = dot(float3(_2932 * _2932, _2932, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_503[_2930], _503[_2930 + 1], _503[_2930 + 2])); + } + else + { + float _2891 = log(_2786); + float _2892 = _2891 / _1091; + float _2925; + if ((_2877 >= _2884) && (_2877 < _2892)) + { + float _2908 = (7.0 * (_2877 - _2884)) / (_2892 - _2884); + int _2909 = int(_2908); + float _2911 = _2908 - float(_2909); + _2925 = dot(float3(_2911 * _2911, _2911, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_504[_2909], _504[_2909 + 1], _504[_2909 + 2])); + } + else + { + _2925 = (_2877 * 0.0599999986588954925537109375) + ((log(1000.0) / _1091) - ((0.0599999986588954925537109375 * _2891) / _1091)); + } + _2946 = _2925; + } + _2954 = _2946; + } + float3 _2956 = _2872; + _2956.y = pow(10.0, _2954); + float _2957 = _2583.z; + float _2961 = log((_2957 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2957) / _1091; + float _3038; + if (_2961 <= _2793) + { + _3038 = (_2961 * 3.0) + ((log(9.9999997473787516355514526367188e-05) / _1091) - ((3.0 * _2792) / _1091)); + } + else + { + float _2968 = log(_2714) / _1091; + float _3030; + if ((_2961 > _2793) && (_2961 < _2968)) + { + float _3013 = (7.0 * (_2961 - _2793)) / (_2968 - _2793); + int _3014 = int(_3013); + float _3016 = _3013 - float(_3014); + _3030 = dot(float3(_3016 * _3016, _3016, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_503[_3014], _503[_3014 + 1], _503[_3014 + 2])); + } + else + { + float _2975 = log(_2786); + float _2976 = _2975 / _1091; + float _3009; + if ((_2961 >= _2968) && (_2961 < _2976)) + { + float _2992 = (7.0 * (_2961 - _2968)) / (_2976 - _2968); + int _2993 = int(_2992); + float _2995 = _2992 - float(_2993); + _3009 = dot(float3(_2995 * _2995, _2995, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_504[_2993], _504[_2993 + 1], _504[_2993 + 2])); + } + else + { + _3009 = (_2961 * 0.0599999986588954925537109375) + ((log(1000.0) / _1091) - ((0.0599999986588954925537109375 * _2975) / _1091)); + } + _3030 = _3009; + } + _3038 = _3030; + } + float3 _3040 = _2956; + _3040.z = pow(10.0, _3038); + float3 _3044 = pow(((_3040 - float3(3.5073844628641381859779357910156e-05)) * _602) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _3052 = pow((float3(0.8359375) + (float3(18.8515625) * _3044)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _3044))), float3(78.84375)); + } + else + { + float3 _2201; + if ((_Globals.OutputDevice == 4u) || (_Globals.OutputDevice == 6u)) + { + float3 _1369 = (_932 * float3(1.5)) * (_572 * float3x3(float3(1.04981100559234619140625, 0.0, -9.74845024757087230682373046875e-05), float3(-0.49590301513671875, 1.37331306934356689453125, 0.09824003279209136962890625), float3(0.0, 0.0, 0.991252005100250244140625))); + float _1370 = _1369.x; + float _1371 = _1369.y; + float _1373 = _1369.z; + float _1376 = fast::max(fast::max(_1370, _1371), _1373); + float _1381 = (fast::max(_1376, 1.0000000133514319600180897396058e-10) - fast::max(fast::min(fast::min(_1370, _1371), _1373), 1.0000000133514319600180897396058e-10)) / fast::max(_1376, 0.00999999977648258209228515625); + float _1394 = ((_1373 + _1371) + _1370) + (1.75 * sqrt(((_1373 * (_1373 - _1371)) + (_1371 * (_1371 - _1370))) + (_1370 * (_1370 - _1373)))); + float _1395 = _1394 * 0.3333333432674407958984375; + float _1396 = _1381 - 0.4000000059604644775390625; + float _1401 = fast::max(1.0 - abs(_1396 * 2.5), 0.0); + float _1409 = (1.0 + (float(int(sign(_1396 * 5.0))) * (1.0 - (_1401 * _1401)))) * 0.02500000037252902984619140625; + float _1422; + if (_1395 <= 0.053333334624767303466796875) + { + _1422 = _1409; + } + else + { + float _1421; + if (_1395 >= 0.1599999964237213134765625) + { + _1421 = 0.0; + } + else + { + _1421 = _1409 * ((0.23999999463558197021484375 / _1394) - 0.5); + } + _1422 = _1421; + } + float3 _1425 = _1369 * float3(1.0 + _1422); + float _1426 = _1425.x; + float _1427 = _1425.y; + float _1429 = _1425.z; + float _1443; + if ((_1426 == _1427) && (_1427 == _1429)) + { + _1443 = 0.0; + } + else + { + _1443 = 57.2957763671875 * atan2(sqrt(3.0) * (_1427 - _1429), ((2.0 * _1426) - _1427) - _1429); + } + float _1448; + if (_1443 < 0.0) + { + _1448 = _1443 + 360.0; + } + else + { + _1448 = _1443; + } + float _1449 = fast::clamp(_1448, 0.0, 360.0); + float _1454; + if (_1449 > 180.0) + { + _1454 = _1449 - 360.0; + } + else + { + _1454 = _1449; + } + float _1504; + if ((_1454 > (-67.5)) && (_1454 < 67.5)) + { + float _1461 = (_1454 - (-67.5)) * 0.0296296291053295135498046875; + int _1462 = int(_1461); + float _1464 = _1461 - float(_1462); + float _1465 = _1464 * _1464; + float _1466 = _1465 * _1464; + float _1503; + if (_1462 == 3) + { + _1503 = (((_1466 * (-0.16666667163372039794921875)) + (_1465 * 0.5)) + (_1464 * (-0.5))) + 0.16666667163372039794921875; + } + else + { + float _1496; + if (_1462 == 2) + { + _1496 = ((_1466 * 0.5) + (_1465 * (-1.0))) + 0.666666686534881591796875; + } + else + { + float _1491; + if (_1462 == 1) + { + _1491 = (((_1466 * (-0.5)) + (_1465 * 0.5)) + (_1464 * 0.5)) + 0.16666667163372039794921875; + } + else + { + float _1484; + if (_1462 == 0) + { + _1484 = _1466 * 0.16666667163372039794921875; + } + else + { + _1484 = 0.0; + } + _1491 = _1484; + } + _1496 = _1491; + } + _1503 = _1496; + } + _1504 = _1503; + } + else + { + _1504 = 0.0; + } + float3 _1511 = _1425; + _1511.x = _1426 + ((((_1504 * 1.5) * _1381) * (0.02999999932944774627685546875 - _1426)) * 0.180000007152557373046875); + float3 _1514 = fast::clamp(fast::clamp(_1511, float3(0.0), float3(65535.0)) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)), float3(0.0), float3(65535.0)); + float3 _1517 = mix(float3(dot(_1514, float3(0.272228717803955078125, 0.674081742763519287109375, 0.053689517080783843994140625))), _1514, float3(0.959999978542327880859375)); + float _1518 = _1517.x; + float _1522 = 0.17999999225139617919921875 * exp2(18.0); + float _1524 = exp2(-14.0); + float _1527 = log((_1518 <= 0.0) ? _1524 : _1518) / _1091; + float _1529 = log(0.17999999225139617919921875 * exp2(-15.0)) / _1091; + float _1596; + if (_1527 <= _1529) + { + _1596 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _1536 = log(0.180000007152557373046875) / _1091; + float _1593; + if ((_1527 > _1529) && (_1527 < _1536)) + { + float _1576 = (3.0 * (_1527 - _1529)) / (_1536 - _1529); + int _1577 = int(_1576); + float _1579 = _1576 - float(_1577); + _1593 = dot(float3(_1579 * _1579, _1579, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_1577], _499[_1577 + 1], _499[_1577 + 2])); + } + else + { + float _1544 = log(_1522) / _1091; + float _1572; + if ((_1527 >= _1536) && (_1527 < _1544)) + { + float _1555 = (3.0 * (_1527 - _1536)) / (_1544 - _1536); + int _1556 = int(_1555); + float _1558 = _1555 - float(_1556); + _1572 = dot(float3(_1558 * _1558, _1558, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1556], _500[_1556 + 1], _500[_1556 + 2])); + } + else + { + _1572 = log(10000.0) / _1091; + } + _1593 = _1572; + } + _1596 = _1593; + } + float3 _1598 = _523; + _1598.x = pow(10.0, _1596); + float _1599 = _1517.y; + float _1603 = log((_1599 <= 0.0) ? _1524 : _1599) / _1091; + float _1670; + if (_1603 <= _1529) + { + _1670 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _1610 = log(0.180000007152557373046875) / _1091; + float _1667; + if ((_1603 > _1529) && (_1603 < _1610)) + { + float _1650 = (3.0 * (_1603 - _1529)) / (_1610 - _1529); + int _1651 = int(_1650); + float _1653 = _1650 - float(_1651); + _1667 = dot(float3(_1653 * _1653, _1653, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_1651], _499[_1651 + 1], _499[_1651 + 2])); + } + else + { + float _1618 = log(_1522) / _1091; + float _1646; + if ((_1603 >= _1610) && (_1603 < _1618)) + { + float _1629 = (3.0 * (_1603 - _1610)) / (_1618 - _1610); + int _1630 = int(_1629); + float _1632 = _1629 - float(_1630); + _1646 = dot(float3(_1632 * _1632, _1632, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1630], _500[_1630 + 1], _500[_1630 + 2])); + } + else + { + _1646 = log(10000.0) / _1091; + } + _1667 = _1646; + } + _1670 = _1667; + } + float3 _1672 = _1598; + _1672.y = pow(10.0, _1670); + float _1673 = _1517.z; + float _1677 = log((_1673 <= 0.0) ? _1524 : _1673) / _1091; + float _1744; + if (_1677 <= _1529) + { + _1744 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _1684 = log(0.180000007152557373046875) / _1091; + float _1741; + if ((_1677 > _1529) && (_1677 < _1684)) + { + float _1724 = (3.0 * (_1677 - _1529)) / (_1684 - _1529); + int _1725 = int(_1724); + float _1727 = _1724 - float(_1725); + _1741 = dot(float3(_1727 * _1727, _1727, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_1725], _499[_1725 + 1], _499[_1725 + 2])); + } + else + { + float _1692 = log(_1522) / _1091; + float _1720; + if ((_1677 >= _1684) && (_1677 < _1692)) + { + float _1703 = (3.0 * (_1677 - _1684)) / (_1692 - _1684); + int _1704 = int(_1703); + float _1706 = _1703 - float(_1704); + _1720 = dot(float3(_1706 * _1706, _1706, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1704], _500[_1704 + 1], _500[_1704 + 2])); + } + else + { + _1720 = log(10000.0) / _1091; + } + _1741 = _1720; + } + _1744 = _1741; + } + float3 _1746 = _1672; + _1746.z = pow(10.0, _1744); + float3 _1748 = (_1746 * float3x3(float3(0.695452213287353515625, 0.140678703784942626953125, 0.16386906802654266357421875), float3(0.0447945632040500640869140625, 0.859671115875244140625, 0.095534317195415496826171875), float3(-0.0055258828215301036834716796875, 0.0040252101607620716094970703125, 1.00150072574615478515625))) * float3x3(float3(1.45143926143646240234375, -0.236510753631591796875, -0.214928567409515380859375), float3(-0.07655377686023712158203125, 1.1762297153472900390625, -0.0996759235858917236328125), float3(0.0083161480724811553955078125, -0.0060324496589601039886474609375, 0.99771630764007568359375)); + float _1750 = 0.17999999225139617919921875 * pow(2.0, -12.0); + float _1754 = log((_1750 <= 0.0) ? _1524 : _1750) / _1091; + float _1821; + if (_1754 <= _1529) + { + _1821 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _1761 = log(0.180000007152557373046875) / _1091; + float _1818; + if ((_1754 > _1529) && (_1754 < _1761)) + { + float _1801 = (3.0 * (_1754 - _1529)) / (_1761 - _1529); + int _1802 = int(_1801); + float _1804 = _1801 - float(_1802); + _1818 = dot(float3(_1804 * _1804, _1804, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_1802], _499[_1802 + 1], _499[_1802 + 2])); + } + else + { + float _1769 = log(_1522) / _1091; + float _1797; + if ((_1754 >= _1761) && (_1754 < _1769)) + { + float _1780 = (3.0 * (_1754 - _1761)) / (_1769 - _1761); + int _1781 = int(_1780); + float _1783 = _1780 - float(_1781); + _1797 = dot(float3(_1783 * _1783, _1783, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1781], _500[_1781 + 1], _500[_1781 + 2])); + } + else + { + _1797 = log(10000.0) / _1091; + } + _1818 = _1797; + } + _1821 = _1818; + } + float _1824 = log(0.180000007152557373046875) / _1091; + float _1878; + if (_1824 <= _1529) + { + _1878 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _1875; + if ((_1824 > _1529) && (_1824 < _1824)) + { + _1875 = (float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[3], _499[4], _499[5])).z; + } + else + { + float _1837 = log(_1522) / _1091; + float _1865; + if ((_1824 >= _1824) && (_1824 < _1837)) + { + float _1848 = (3.0 * (_1824 - _1824)) / (_1837 - _1824); + int _1849 = int(_1848); + float _1851 = _1848 - float(_1849); + _1865 = dot(float3(_1851 * _1851, _1851, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1849], _500[_1849 + 1], _500[_1849 + 2])); + } + else + { + _1865 = log(10000.0) / _1091; + } + _1875 = _1865; + } + _1878 = _1875; + } + float _1879 = pow(10.0, _1878); + float _1881 = 0.17999999225139617919921875 * pow(2.0, 11.0); + float _1885 = log((_1881 <= 0.0) ? _1524 : _1881) / _1091; + float _1950; + if (_1885 <= _1529) + { + _1950 = log(9.9999997473787516355514526367188e-05) / _1091; + } + else + { + float _1947; + if ((_1885 > _1529) && (_1885 < _1824)) + { + float _1930 = (3.0 * (_1885 - _1529)) / (_1824 - _1529); + int _1931 = int(_1930); + float _1933 = _1930 - float(_1931); + _1947 = dot(float3(_1933 * _1933, _1933, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_499[_1931], _499[_1931 + 1], _499[_1931 + 2])); + } + else + { + float _1898 = log(_1522) / _1091; + float _1926; + if ((_1885 >= _1824) && (_1885 < _1898)) + { + float _1909 = (3.0 * (_1885 - _1824)) / (_1898 - _1824); + int _1910 = int(_1909); + float _1912 = _1909 - float(_1910); + _1926 = dot(float3(_1912 * _1912, _1912, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_500[_1910], _500[_1910 + 1], _500[_1910 + 2])); + } + else + { + _1926 = log(10000.0) / _1091; + } + _1947 = _1926; + } + _1950 = _1947; + } + float _1951 = pow(10.0, _1950); + float _1952 = _1748.x; + float _1956 = log((_1952 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _1952) / _1091; + float _1958 = log(pow(10.0, _1821)) / _1091; + float _2030; + if (_1956 <= _1958) + { + _2030 = log(0.004999999888241291046142578125) / _1091; + } + else + { + float _1965 = log(_1879) / _1091; + float _2027; + if ((_1956 > _1958) && (_1956 < _1965)) + { + float _2010 = (7.0 * (_1956 - _1958)) / (_1965 - _1958); + int _2011 = int(_2010); + float _2013 = _2010 - float(_2011); + _2027 = dot(float3(_2013 * _2013, _2013, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_506[_2011], _506[_2011 + 1], _506[_2011 + 2])); + } + else + { + float _1972 = log(_1951); + float _1973 = _1972 / _1091; + float _2006; + if ((_1956 >= _1965) && (_1956 < _1973)) + { + float _1989 = (7.0 * (_1956 - _1965)) / (_1973 - _1965); + int _1990 = int(_1989); + float _1992 = _1989 - float(_1990); + _2006 = dot(float3(_1992 * _1992, _1992, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_507[_1990], _507[_1990 + 1], _507[_1990 + 2])); + } + else + { + _2006 = (_1956 * 0.119999997317790985107421875) + ((log(2000.0) / _1091) - ((0.119999997317790985107421875 * _1972) / _1091)); + } + _2027 = _2006; + } + _2030 = _2027; + } + float3 _2032 = _523; + _2032.x = pow(10.0, _2030); + float _2033 = _1748.y; + float _2037 = log((_2033 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2033) / _1091; + float _2109; + if (_2037 <= _1958) + { + _2109 = log(0.004999999888241291046142578125) / _1091; + } + else + { + float _2044 = log(_1879) / _1091; + float _2106; + if ((_2037 > _1958) && (_2037 < _2044)) + { + float _2089 = (7.0 * (_2037 - _1958)) / (_2044 - _1958); + int _2090 = int(_2089); + float _2092 = _2089 - float(_2090); + _2106 = dot(float3(_2092 * _2092, _2092, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_506[_2090], _506[_2090 + 1], _506[_2090 + 2])); + } + else + { + float _2051 = log(_1951); + float _2052 = _2051 / _1091; + float _2085; + if ((_2037 >= _2044) && (_2037 < _2052)) + { + float _2068 = (7.0 * (_2037 - _2044)) / (_2052 - _2044); + int _2069 = int(_2068); + float _2071 = _2068 - float(_2069); + _2085 = dot(float3(_2071 * _2071, _2071, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_507[_2069], _507[_2069 + 1], _507[_2069 + 2])); + } + else + { + _2085 = (_2037 * 0.119999997317790985107421875) + ((log(2000.0) / _1091) - ((0.119999997317790985107421875 * _2051) / _1091)); + } + _2106 = _2085; + } + _2109 = _2106; + } + float3 _2111 = _2032; + _2111.y = pow(10.0, _2109); + float _2112 = _1748.z; + float _2116 = log((_2112 <= 0.0) ? 9.9999997473787516355514526367188e-05 : _2112) / _1091; + float _2188; + if (_2116 <= _1958) + { + _2188 = log(0.004999999888241291046142578125) / _1091; + } + else + { + float _2123 = log(_1879) / _1091; + float _2185; + if ((_2116 > _1958) && (_2116 < _2123)) + { + float _2168 = (7.0 * (_2116 - _1958)) / (_2123 - _1958); + int _2169 = int(_2168); + float _2171 = _2168 - float(_2169); + _2185 = dot(float3(_2171 * _2171, _2171, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_506[_2169], _506[_2169 + 1], _506[_2169 + 2])); + } + else + { + float _2130 = log(_1951); + float _2131 = _2130 / _1091; + float _2164; + if ((_2116 >= _2123) && (_2116 < _2131)) + { + float _2147 = (7.0 * (_2116 - _2123)) / (_2131 - _2123); + int _2148 = int(_2147); + float _2150 = _2147 - float(_2148); + _2164 = dot(float3(_2150 * _2150, _2150, 1.0), float3x3(float3(0.5, -1.0, 0.5), float3(-1.0, 1.0, 0.5), float3(0.5, 0.0, 0.0)) * float3(_507[_2148], _507[_2148 + 1], _507[_2148 + 2])); + } + else + { + _2164 = (_2116 * 0.119999997317790985107421875) + ((log(2000.0) / _1091) - ((0.119999997317790985107421875 * _2130) / _1091)); + } + _2185 = _2164; + } + _2188 = _2185; + } + float3 _2190 = _2111; + _2190.z = pow(10.0, _2188); + float3 _2193 = pow((_2190 * _602) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _2201 = pow((float3(0.8359375) + (float3(18.8515625) * _2193)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _2193))), float3(78.84375)); + } + else + { + float3 _1366; + if (_Globals.OutputDevice == 7u) + { + float3 _1358 = pow(((_932 * _573) * _602) * float3(9.9999997473787516355514526367188e-05), float3(0.1593017578125)); + _1366 = pow((float3(0.8359375) + (float3(18.8515625) * _1358)) * (float3(1.0) / (float3(1.0) + (float3(18.6875) * _1358))), float3(78.84375)); + } + else + { + _1366 = pow((_1324 * _573) * _602, float3(_Globals.InverseGamma.z)); + } + _2201 = _1366; + } + _3052 = _2201; + } + _3062 = _3052; + } + _3103 = _3062; + } + float3 _3104 = _3103 * float3(0.95238101482391357421875); + float4 _3106 = float4(_3104.x, _3104.y, _3104.z, float4(0.0).w); + _3106.w = 0.0; + out.out_var_SV_Target0 = _3106; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag new file mode 100644 index 0000000..dd54893 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag @@ -0,0 +1,503 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct type_TranslucentBasePass +{ + uint TranslucentBasePass_Shared_Forward_NumLocalLights; + uint TranslucentBasePass_Shared_Forward_NumReflectionCaptures; + uint TranslucentBasePass_Shared_Forward_HasDirectionalLight; + uint TranslucentBasePass_Shared_Forward_NumGridCells; + packed_int3 TranslucentBasePass_Shared_Forward_CulledGridSize; + uint TranslucentBasePass_Shared_Forward_MaxCulledLightsPerCell; + uint TranslucentBasePass_Shared_Forward_LightGridPixelSizeShift; + uint PrePadding_TranslucentBasePass_Shared_Forward_36; + uint PrePadding_TranslucentBasePass_Shared_Forward_40; + uint PrePadding_TranslucentBasePass_Shared_Forward_44; + packed_float3 TranslucentBasePass_Shared_Forward_LightGridZParams; + float PrePadding_TranslucentBasePass_Shared_Forward_60; + packed_float3 TranslucentBasePass_Shared_Forward_DirectionalLightDirection; + float PrePadding_TranslucentBasePass_Shared_Forward_76; + packed_float3 TranslucentBasePass_Shared_Forward_DirectionalLightColor; + float TranslucentBasePass_Shared_Forward_DirectionalLightVolumetricScatteringIntensity; + uint TranslucentBasePass_Shared_Forward_DirectionalLightShadowMapChannelMask; + uint PrePadding_TranslucentBasePass_Shared_Forward_100; + float2 TranslucentBasePass_Shared_Forward_DirectionalLightDistanceFadeMAD; + uint TranslucentBasePass_Shared_Forward_NumDirectionalLightCascades; + uint PrePadding_TranslucentBasePass_Shared_Forward_116; + uint PrePadding_TranslucentBasePass_Shared_Forward_120; + uint PrePadding_TranslucentBasePass_Shared_Forward_124; + float4 TranslucentBasePass_Shared_Forward_CascadeEndDepths; + float4x4 TranslucentBasePass_Shared_Forward_DirectionalLightWorldToShadowMatrix[4]; + float4 TranslucentBasePass_Shared_Forward_DirectionalLightShadowmapMinMax[4]; + float4 TranslucentBasePass_Shared_Forward_DirectionalLightShadowmapAtlasBufferSize; + float TranslucentBasePass_Shared_Forward_DirectionalLightDepthBias; + uint TranslucentBasePass_Shared_Forward_DirectionalLightUseStaticShadowing; + uint PrePadding_TranslucentBasePass_Shared_Forward_488; + uint PrePadding_TranslucentBasePass_Shared_Forward_492; + float4 TranslucentBasePass_Shared_Forward_DirectionalLightStaticShadowBufferSize; + float4x4 TranslucentBasePass_Shared_Forward_DirectionalLightWorldToStaticShadow; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_576; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_580; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_584; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_588; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_592; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_596; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_600; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_604; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_608; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_612; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_616; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_620; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_624; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_628; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_632; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_636; + uint TranslucentBasePass_Shared_ForwardISR_NumLocalLights; + uint TranslucentBasePass_Shared_ForwardISR_NumReflectionCaptures; + uint TranslucentBasePass_Shared_ForwardISR_HasDirectionalLight; + uint TranslucentBasePass_Shared_ForwardISR_NumGridCells; + packed_int3 TranslucentBasePass_Shared_ForwardISR_CulledGridSize; + uint TranslucentBasePass_Shared_ForwardISR_MaxCulledLightsPerCell; + uint TranslucentBasePass_Shared_ForwardISR_LightGridPixelSizeShift; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_676; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_680; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_684; + packed_float3 TranslucentBasePass_Shared_ForwardISR_LightGridZParams; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_700; + packed_float3 TranslucentBasePass_Shared_ForwardISR_DirectionalLightDirection; + float PrePadding_TranslucentBasePass_Shared_ForwardISR_716; + packed_float3 TranslucentBasePass_Shared_ForwardISR_DirectionalLightColor; + float TranslucentBasePass_Shared_ForwardISR_DirectionalLightVolumetricScatteringIntensity; + uint TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowMapChannelMask; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_740; + float2 TranslucentBasePass_Shared_ForwardISR_DirectionalLightDistanceFadeMAD; + uint TranslucentBasePass_Shared_ForwardISR_NumDirectionalLightCascades; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_756; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_760; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_764; + float4 TranslucentBasePass_Shared_ForwardISR_CascadeEndDepths; + float4x4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightWorldToShadowMatrix[4]; + float4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowmapMinMax[4]; + float4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowmapAtlasBufferSize; + float TranslucentBasePass_Shared_ForwardISR_DirectionalLightDepthBias; + uint TranslucentBasePass_Shared_ForwardISR_DirectionalLightUseStaticShadowing; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_1128; + uint PrePadding_TranslucentBasePass_Shared_ForwardISR_1132; + float4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightStaticShadowBufferSize; + float4x4 TranslucentBasePass_Shared_ForwardISR_DirectionalLightWorldToStaticShadow; + float PrePadding_TranslucentBasePass_Shared_Reflection_1216; + float PrePadding_TranslucentBasePass_Shared_Reflection_1220; + float PrePadding_TranslucentBasePass_Shared_Reflection_1224; + float PrePadding_TranslucentBasePass_Shared_Reflection_1228; + float PrePadding_TranslucentBasePass_Shared_Reflection_1232; + float PrePadding_TranslucentBasePass_Shared_Reflection_1236; + float PrePadding_TranslucentBasePass_Shared_Reflection_1240; + float PrePadding_TranslucentBasePass_Shared_Reflection_1244; + float PrePadding_TranslucentBasePass_Shared_Reflection_1248; + float PrePadding_TranslucentBasePass_Shared_Reflection_1252; + float PrePadding_TranslucentBasePass_Shared_Reflection_1256; + float PrePadding_TranslucentBasePass_Shared_Reflection_1260; + float PrePadding_TranslucentBasePass_Shared_Reflection_1264; + float PrePadding_TranslucentBasePass_Shared_Reflection_1268; + float PrePadding_TranslucentBasePass_Shared_Reflection_1272; + float PrePadding_TranslucentBasePass_Shared_Reflection_1276; + float4 TranslucentBasePass_Shared_Reflection_SkyLightParameters; + float TranslucentBasePass_Shared_Reflection_SkyLightCubemapBrightness; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1300; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1304; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1308; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1312; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1316; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1320; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1324; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1328; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1332; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1336; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1340; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1344; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1348; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1352; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1356; + float4 TranslucentBasePass_Shared_PlanarReflection_ReflectionPlane; + float4 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionOrigin; + float4 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionXAxis; + float4 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionYAxis; + float3x4 TranslucentBasePass_Shared_PlanarReflection_InverseTransposeMirrorMatrix; + packed_float3 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionParameters; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1484; + float2 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionParameters2; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1496; + float PrePadding_TranslucentBasePass_Shared_PlanarReflection_1500; + float4x4 TranslucentBasePass_Shared_PlanarReflection_ProjectionWithExtraFOV[2]; + float4 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionScreenScaleBias[2]; + float2 TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionScreenBound; + uint TranslucentBasePass_Shared_PlanarReflection_bIsStereo; + float PrePadding_TranslucentBasePass_Shared_Fog_1676; + float PrePadding_TranslucentBasePass_Shared_Fog_1680; + float PrePadding_TranslucentBasePass_Shared_Fog_1684; + float PrePadding_TranslucentBasePass_Shared_Fog_1688; + float PrePadding_TranslucentBasePass_Shared_Fog_1692; + float4 TranslucentBasePass_Shared_Fog_ExponentialFogParameters; + float4 TranslucentBasePass_Shared_Fog_ExponentialFogParameters2; + float4 TranslucentBasePass_Shared_Fog_ExponentialFogColorParameter; + float4 TranslucentBasePass_Shared_Fog_ExponentialFogParameters3; + float4 TranslucentBasePass_Shared_Fog_InscatteringLightDirection; + float4 TranslucentBasePass_Shared_Fog_DirectionalInscatteringColor; + float2 TranslucentBasePass_Shared_Fog_SinCosInscatteringColorCubemapRotation; + float PrePadding_TranslucentBasePass_Shared_Fog_1800; + float PrePadding_TranslucentBasePass_Shared_Fog_1804; + packed_float3 TranslucentBasePass_Shared_Fog_FogInscatteringTextureParameters; + float TranslucentBasePass_Shared_Fog_ApplyVolumetricFog; + float PrePadding_TranslucentBasePass_1824; + float PrePadding_TranslucentBasePass_1828; + float PrePadding_TranslucentBasePass_1832; + float PrePadding_TranslucentBasePass_1836; + float PrePadding_TranslucentBasePass_1840; + float PrePadding_TranslucentBasePass_1844; + float PrePadding_TranslucentBasePass_1848; + float PrePadding_TranslucentBasePass_1852; + float PrePadding_TranslucentBasePass_1856; + float PrePadding_TranslucentBasePass_1860; + float PrePadding_TranslucentBasePass_1864; + float PrePadding_TranslucentBasePass_1868; + float PrePadding_TranslucentBasePass_1872; + float PrePadding_TranslucentBasePass_1876; + float PrePadding_TranslucentBasePass_1880; + float PrePadding_TranslucentBasePass_1884; + float PrePadding_TranslucentBasePass_1888; + float PrePadding_TranslucentBasePass_1892; + float PrePadding_TranslucentBasePass_1896; + float PrePadding_TranslucentBasePass_1900; + float PrePadding_TranslucentBasePass_1904; + float PrePadding_TranslucentBasePass_1908; + float PrePadding_TranslucentBasePass_1912; + float PrePadding_TranslucentBasePass_1916; + float PrePadding_TranslucentBasePass_1920; + float PrePadding_TranslucentBasePass_1924; + float PrePadding_TranslucentBasePass_1928; + float PrePadding_TranslucentBasePass_1932; + float PrePadding_TranslucentBasePass_1936; + float PrePadding_TranslucentBasePass_1940; + float PrePadding_TranslucentBasePass_1944; + float PrePadding_TranslucentBasePass_1948; + float PrePadding_TranslucentBasePass_1952; + float PrePadding_TranslucentBasePass_1956; + float PrePadding_TranslucentBasePass_1960; + float PrePadding_TranslucentBasePass_1964; + float PrePadding_TranslucentBasePass_1968; + float PrePadding_TranslucentBasePass_1972; + float PrePadding_TranslucentBasePass_1976; + float PrePadding_TranslucentBasePass_1980; + float PrePadding_TranslucentBasePass_1984; + float PrePadding_TranslucentBasePass_1988; + float PrePadding_TranslucentBasePass_1992; + float PrePadding_TranslucentBasePass_1996; + float PrePadding_TranslucentBasePass_2000; + float PrePadding_TranslucentBasePass_2004; + float PrePadding_TranslucentBasePass_2008; + float PrePadding_TranslucentBasePass_2012; + float PrePadding_TranslucentBasePass_2016; + float PrePadding_TranslucentBasePass_2020; + float PrePadding_TranslucentBasePass_2024; + float PrePadding_TranslucentBasePass_2028; + float PrePadding_TranslucentBasePass_2032; + float PrePadding_TranslucentBasePass_2036; + float PrePadding_TranslucentBasePass_2040; + float PrePadding_TranslucentBasePass_2044; + float PrePadding_TranslucentBasePass_2048; + float PrePadding_TranslucentBasePass_2052; + float PrePadding_TranslucentBasePass_2056; + float PrePadding_TranslucentBasePass_2060; + float PrePadding_TranslucentBasePass_2064; + float PrePadding_TranslucentBasePass_2068; + float PrePadding_TranslucentBasePass_2072; + float PrePadding_TranslucentBasePass_2076; + float PrePadding_TranslucentBasePass_2080; + float PrePadding_TranslucentBasePass_2084; + float PrePadding_TranslucentBasePass_2088; + float PrePadding_TranslucentBasePass_2092; + float PrePadding_TranslucentBasePass_2096; + float PrePadding_TranslucentBasePass_2100; + float PrePadding_TranslucentBasePass_2104; + float PrePadding_TranslucentBasePass_2108; + float PrePadding_TranslucentBasePass_2112; + float PrePadding_TranslucentBasePass_2116; + float PrePadding_TranslucentBasePass_2120; + float PrePadding_TranslucentBasePass_2124; + float PrePadding_TranslucentBasePass_2128; + float PrePadding_TranslucentBasePass_2132; + float PrePadding_TranslucentBasePass_2136; + float PrePadding_TranslucentBasePass_2140; + float4 TranslucentBasePass_HZBUvFactorAndInvFactor; + float4 TranslucentBasePass_PrevScreenPositionScaleBias; + float TranslucentBasePass_PrevSceneColorPreExposureInv; +}; + +struct type_Material +{ + float4 Material_VectorExpressions[2]; + float4 Material_ScalarExpressions[1]; +}; + +constant float _108 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; + uint gl_SampleMask [[sample_mask]]; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[user(locn0)]]; + float4 in_var_TEXCOORD11_centroid [[user(locn1)]]; + uint in_var_PRIMITIVE_ID [[user(locn2)]]; + float4 in_var_TEXCOORD7 [[user(locn3)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], const device type_StructuredBuffer_v4float& View_PrimitiveSceneData [[buffer(1)]], constant type_TranslucentBasePass& TranslucentBasePass [[buffer(2)]], constant type_Material& Material [[buffer(3)]], texture3d TranslucentBasePass_Shared_Fog_IntegratedLightScattering [[texture(0)]], sampler View_SharedBilinearClampedSampler [[sampler(0)]], float4 gl_FragCoord [[position]], uint gl_SampleMaskIn [[sample_mask]]) +{ + main0_out out = {}; + float4 _137 = View.View_SVPositionToTranslatedWorld * float4(gl_FragCoord.xyz, 1.0); + float3 _142 = (_137.xyz / float3(_137.w)) - float3(View.View_PreViewTranslation); + bool _165 = TranslucentBasePass.TranslucentBasePass_Shared_Fog_ApplyVolumetricFog > 0.0; + float4 _215; + if (_165) + { + float4 _172 = View.View_WorldToClip * float4(_142, 1.0); + float _173 = _172.w; + float4 _202; + if (_165) + { + _202 = TranslucentBasePass_Shared_Fog_IntegratedLightScattering.sample(View_SharedBilinearClampedSampler, float3(((_172.xy / float2(_173)).xy * float2(0.5, -0.5)) + float2(0.5), (log2((_173 * View.View_VolumetricFogGridZParams[0]) + View.View_VolumetricFogGridZParams[1]) * View.View_VolumetricFogGridZParams[2]) * View.View_VolumetricFogInvGridSize[2]), level(0.0)); + } + else + { + _202 = float4(0.0, 0.0, 0.0, 1.0); + } + _215 = float4(_202.xyz + (in.in_var_TEXCOORD7.xyz * float3(_202.w)), _202.w * in.in_var_TEXCOORD7.w); + } + else + { + _215 = in.in_var_TEXCOORD7; + } + float3 _216 = fast::max(Material.Material_VectorExpressions[1].xyz * float3(((1.0 + dot(float3(-1.0, -1.5, 3.0) / float3(sqrt(12.25)), normalize(float3x3(in.in_var_TEXCOORD10_centroid.xyz, cross(in.in_var_TEXCOORD11_centroid.xyz, in.in_var_TEXCOORD10_centroid.xyz) * float3(in.in_var_TEXCOORD11_centroid.w), in.in_var_TEXCOORD11_centroid.xyz) * normalize((float3(0.0, 0.0, 1.0) * float3(View.View_NormalOverrideParameter.w)) + View.View_NormalOverrideParameter.xyz)))) * 0.5) + 0.20000000298023223876953125), float3(0.0)); + float3 _246; + if (View.View_OutOfBoundsMask > 0.0) + { + uint _222 = in.in_var_PRIMITIVE_ID * 26u; + float3 _245; + if (any(abs(_142 - View_PrimitiveSceneData._m0[_222 + 5u].xyz) > (View_PrimitiveSceneData._m0[_222 + 19u].xyz + float3(1.0)))) + { + _245 = mix(float3(1.0, 1.0, 0.0), float3(0.0, 1.0, 1.0), select(float3(0.0), float3(1.0), float3(fract(dot(_142, float3(0.57700002193450927734375)) * 0.00200000009499490261077880859375)) > float3(0.5))); + } + else + { + _245 = _216; + } + _246 = _245; + } + else + { + _246 = _216; + } + float4 _256 = float4((_246 * float3(_215.w)) + _215.xyz, _108); + _256.w = 1.0; + float4 _268; + uint _269; + if (View.View_NumSceneColorMSAASamples > 1) + { + _268 = _256 * float4(float(View.View_NumSceneColorMSAASamples) * 0.25); + _269 = gl_SampleMaskIn & 15u; + } + else + { + _268 = _256; + _269 = gl_SampleMaskIn; + } + out.out_var_SV_Target0 = _268; + out.gl_SampleMask = _269; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag new file mode 100644 index 0000000..817b1cf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag @@ -0,0 +1,213 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Globals +{ + float3 SoftTransitionScale; + float4 ShadowBufferSize; + float ShadowFadeFraction; + float ShadowSharpen; + float4 LightPositionAndInvRadius; + float4x4 ScreenToShadowMatrix; + float2 ProjectionDepthBiasParameters; + float4 ModulatedShadowColor; + float4 ShadowTileOffsetAndSize; +}; + +constant float4 _58 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float4 _67 = _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData; + float _68 = _67.w; + float4 _82 = _Globals.ScreenToShadowMatrix * float4((((gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw) - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_68), _68, 1.0); + float _118 = fast::clamp(((fast::clamp((ShadowDepthTexture.sample(ShadowDepthTextureSampler, (((_82.xyz / float3(_82.w)).xy * _Globals.ShadowTileOffsetAndSize.zw).xy + _Globals.ShadowTileOffsetAndSize.xy).xy, level(0.0)).xxx * float3(_Globals.SoftTransitionScale.z)) - float3((fast::min(_82.z, 0.999989986419677734375) * _Globals.SoftTransitionScale.z) - 1.0), float3(0.0), float3(1.0)).x - 0.5) * _Globals.ShadowSharpen) + 0.5, 0.0, 1.0); + float3 _127 = mix(_Globals.ModulatedShadowColor.xyz, float3(1.0), float3(mix(1.0, _118 * _118, _Globals.ShadowFadeFraction))); + float4 _129 = float4(_127.x, _127.y, _127.z, _58.w); + _129.w = 0.0; + out.out_var_SV_Target0 = _129; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag new file mode 100644 index 0000000..817b1cf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag @@ -0,0 +1,213 @@ +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Globals +{ + float3 SoftTransitionScale; + float4 ShadowBufferSize; + float ShadowFadeFraction; + float ShadowSharpen; + float4 LightPositionAndInvRadius; + float4x4 ScreenToShadowMatrix; + float2 ProjectionDepthBiasParameters; + float4 ModulatedShadowColor; + float4 ShadowTileOffsetAndSize; +}; + +constant float4 _58 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +fragment main0_out main0(constant type_View& View [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], float4 _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData [[color(0)]], texture2d ShadowDepthTexture [[texture(0)]], sampler ShadowDepthTextureSampler [[sampler(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float4 _67 = _RESERVED_IDENTIFIER_FIXUP_gl_LastFragData; + float _68 = _67.w; + float4 _82 = _Globals.ScreenToShadowMatrix * float4((((gl_FragCoord.xy * View.View_BufferSizeAndInvSize.zw) - View.View_ScreenPositionScaleBias.wz) / View.View_ScreenPositionScaleBias.xy) * float2(_68), _68, 1.0); + float _118 = fast::clamp(((fast::clamp((ShadowDepthTexture.sample(ShadowDepthTextureSampler, (((_82.xyz / float3(_82.w)).xy * _Globals.ShadowTileOffsetAndSize.zw).xy + _Globals.ShadowTileOffsetAndSize.xy).xy, level(0.0)).xxx * float3(_Globals.SoftTransitionScale.z)) - float3((fast::min(_82.z, 0.999989986419677734375) * _Globals.SoftTransitionScale.z) - 1.0), float3(0.0), float3(1.0)).x - 0.5) * _Globals.ShadowSharpen) + 0.5, 0.0, 1.0); + float3 _127 = mix(_Globals.ModulatedShadowColor.xyz, float3(1.0), float3(mix(1.0, _118 * _118, _Globals.ShadowFadeFraction))); + float4 _129 = float4(_127.x, _127.y, _127.z, _58.w); + _129.w = 0.0; + out.out_var_SV_Target0 = _129; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag new file mode 100644 index 0000000..938bd98 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag @@ -0,0 +1,130 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +struct spvDescriptorSetBuffer0 +{ + const device type_StructuredBuffer_v4float* CulledObjectBoxBounds [[id(0)]]; + constant type_Globals* _Globals [[id(1)]]; + texture2d RWShadowTileNumCulledObjects [[id(2)]]; + device atomic_uint* RWShadowTileNumCulledObjects_atomic [[id(3)]]; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float(((*spvDescriptorSet0._Globals).ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2((*spvDescriptorSet0._Globals).ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _107 = _103 + 1u; + if (all((*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_107].xy > _96.xy) && all((*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103].xyz < _102)) + { + float3 _121 = float3(0.5) * ((*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103].xyz + (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_107].xyz); + float _122 = _96.x; + float _123 = _96.y; + spvUnsafeArray _73; + _73[0] = float3(_122, _123, -1000.0); + float _126 = _100.x; + _73[1] = float3(_126, _123, -1000.0); + float _129 = _100.y; + _73[2] = float3(_122, _129, -1000.0); + _73[3] = float3(_126, _129, -1000.0); + _73[4] = float3(_122, _123, 1.0); + _73[5] = float3(_126, _123, 1.0); + _73[6] = float3(_122, _129, 1.0); + _73[7] = float3(_126, _129, 1.0); + float3 _155; + float3 _158; + _155 = float3(-500000.0); + _158 = float3(500000.0); + for (int _160 = 0; _160 < 8; ) + { + float3 _166 = _73[_160] - _121; + float3 _170 = float3(dot(_166, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 2u].xyz), dot(_166, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 3u].xyz), dot(_166, (*spvDescriptorSet0.CulledObjectBoxBounds)._m0[_103 + 4u].xyz)); + _155 = fast::max(_155, _170); + _158 = fast::min(_158, _170); + _160++; + continue; + } + if (all(_158 < float3(1.0)) && all(_155 > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&spvDescriptorSet0.RWShadowTileNumCulledObjects_atomic[(_78 * (*spvDescriptorSet0._Globals).ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.frag new file mode 100644 index 0000000..d73c30c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.frag @@ -0,0 +1,122 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_v4float& CulledObjectBoxBounds [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d RWShadowTileNumCulledObjects [[texture(0)]], device atomic_uint* RWShadowTileNumCulledObjects_atomic [[buffer(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float((_Globals.ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2(_Globals.ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _107 = _103 + 1u; + if (all(CulledObjectBoxBounds._m0[_107].xy > _96.xy) && all(CulledObjectBoxBounds._m0[_103].xyz < _102)) + { + float3 _121 = float3(0.5) * (CulledObjectBoxBounds._m0[_103].xyz + CulledObjectBoxBounds._m0[_107].xyz); + float _122 = _96.x; + float _123 = _96.y; + spvUnsafeArray _73; + _73[0] = float3(_122, _123, -1000.0); + float _126 = _100.x; + _73[1] = float3(_126, _123, -1000.0); + float _129 = _100.y; + _73[2] = float3(_122, _129, -1000.0); + _73[3] = float3(_126, _129, -1000.0); + _73[4] = float3(_122, _123, 1.0); + _73[5] = float3(_126, _123, 1.0); + _73[6] = float3(_122, _129, 1.0); + _73[7] = float3(_126, _129, 1.0); + float3 _155; + float3 _158; + _155 = float3(-500000.0); + _158 = float3(500000.0); + for (int _160 = 0; _160 < 8; ) + { + float3 _166 = _73[_160] - _121; + float3 _170 = float3(dot(_166, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + _155 = fast::max(_155, _170); + _158 = fast::min(_158, _170); + _160++; + continue; + } + if (all(_158 < float3(1.0)) && all(_155 > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&RWShadowTileNumCulledObjects_atomic[(_78 * _Globals.ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag new file mode 100644 index 0000000..d73c30c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag @@ -0,0 +1,122 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" +#pragma clang diagnostic ignored "-Wunused-variable" + +#include +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +struct type_Globals +{ + uint2 ShadowTileListGroupSize; +}; + +constant float3 _70 = {}; + +struct main0_out +{ + float4 out_var_SV_Target0 [[color(0)]]; +}; + +struct main0_in +{ + uint in_var_TEXCOORD0 [[user(locn0)]]; +}; + +fragment main0_out main0(main0_in in [[stage_in]], const device type_StructuredBuffer_v4float& CulledObjectBoxBounds [[buffer(0)]], constant type_Globals& _Globals [[buffer(1)]], texture2d RWShadowTileNumCulledObjects [[texture(0)]], device atomic_uint* RWShadowTileNumCulledObjects_atomic [[buffer(2)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + uint2 _77 = uint2(gl_FragCoord.xy); + uint _78 = _77.y; + uint _83 = _77.x; + float2 _91 = float2(float(_83), float((_Globals.ShadowTileListGroupSize.y - 1u) - _78)); + float2 _93 = float2(_Globals.ShadowTileListGroupSize); + float2 _96 = ((_91 / _93) * float2(2.0)) - float2(1.0); + float2 _100 = (((_91 + float2(1.0)) / _93) * float2(2.0)) - float2(1.0); + float3 _102 = float3(_100.x, _100.y, _70.z); + _102.z = 1.0; + uint _103 = in.in_var_TEXCOORD0 * 5u; + uint _107 = _103 + 1u; + if (all(CulledObjectBoxBounds._m0[_107].xy > _96.xy) && all(CulledObjectBoxBounds._m0[_103].xyz < _102)) + { + float3 _121 = float3(0.5) * (CulledObjectBoxBounds._m0[_103].xyz + CulledObjectBoxBounds._m0[_107].xyz); + float _122 = _96.x; + float _123 = _96.y; + spvUnsafeArray _73; + _73[0] = float3(_122, _123, -1000.0); + float _126 = _100.x; + _73[1] = float3(_126, _123, -1000.0); + float _129 = _100.y; + _73[2] = float3(_122, _129, -1000.0); + _73[3] = float3(_126, _129, -1000.0); + _73[4] = float3(_122, _123, 1.0); + _73[5] = float3(_126, _123, 1.0); + _73[6] = float3(_122, _129, 1.0); + _73[7] = float3(_126, _129, 1.0); + float3 _155; + float3 _158; + _155 = float3(-500000.0); + _158 = float3(500000.0); + for (int _160 = 0; _160 < 8; ) + { + float3 _166 = _73[_160] - _121; + float3 _170 = float3(dot(_166, CulledObjectBoxBounds._m0[_103 + 2u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 3u].xyz), dot(_166, CulledObjectBoxBounds._m0[_103 + 4u].xyz)); + _155 = fast::max(_155, _170); + _158 = fast::min(_158, _170); + _160++; + continue; + } + if (all(_158 < float3(1.0)) && all(_155 > float3(-1.0))) + { + uint _179 = atomic_fetch_add_explicit((device atomic_uint*)&RWShadowTileNumCulledObjects_atomic[(_78 * _Globals.ShadowTileListGroupSize.x) + _83], 1u, memory_order_relaxed); + } + } + out.out_var_SV_Target0 = float4(0.0); + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc b/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc new file mode 100644 index 0000000..70d17d4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc @@ -0,0 +1,399 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct FVertexFactoryInterpolantsVSToPS +{ + float4 TangentToWorld0; + float4 TangentToWorld2; + float4 Color; + spvUnsafeArray TexCoords; + float4 LightMapCoordinate; + uint PrimitiveId; + uint LightmapDataIndex; +}; + +struct FVertexFactoryInterpolantsVSToDS +{ + FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; +}; + +struct FSharedBasePassInterpolants +{ +}; +struct FBasePassInterpolantsVSToDS +{ + FSharedBasePassInterpolants _m0; +}; + +struct FBasePassVSToDS +{ + FVertexFactoryInterpolantsVSToDS FactoryInterpolants; + FBasePassInterpolantsVSToDS BasePassInterpolants; + float4 Position; +}; + +struct FPNTessellationHSToDS +{ + FBasePassVSToDS PassSpecificData; + spvUnsafeArray WorldPosition; + float3 DisplacementScale; + float TessellationMultiplier; + float WorldDisplacementMultiplier; +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_StructuredBuffer_v4float +{ + float4 _m0[1]; +}; + +constant float4 _142 = {}; + +struct main0_out +{ + float4 out_var_COLOR0; + uint out_var_LIGHTMAP_ID; + float3 out_var_PN_DisplacementScales; + spvUnsafeArray out_var_PN_POSITION; + float out_var_PN_TessellationMultiplier; + float out_var_PN_WorldDisplacementMultiplier; + uint out_var_PRIMITIVE_ID; + spvUnsafeArray out_var_TEXCOORD0; + float4 out_var_TEXCOORD10_centroid; + float4 out_var_TEXCOORD11_centroid; + float4 out_var_TEXCOORD4; + float4 out_var_VS_To_DS_Position; +}; + +struct main0_patchOut +{ + float4 out_var_PN_POSITION9; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[attribute(0)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(1)]]; + float4 in_var_COLOR0 [[attribute(2)]]; + float4 in_var_TEXCOORD0_0 [[attribute(3)]]; + float4 in_var_TEXCOORD4 [[attribute(4)]]; + uint in_var_PRIMITIVE_ID [[attribute(5)]]; + uint in_var_LIGHTMAP_ID [[attribute(6)]]; + float4 in_var_VS_To_DS_Position [[attribute(7)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], const device type_StructuredBuffer_v4float& View_PrimitiveSceneData [[buffer(1)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + threadgroup FPNTessellationHSToDS temp_var_hullMainRetVal[3]; + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray _144 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD10_centroid, gl_in[1].in_var_TEXCOORD10_centroid, gl_in[2].in_var_TEXCOORD10_centroid, gl_in[3].in_var_TEXCOORD10_centroid, gl_in[4].in_var_TEXCOORD10_centroid, gl_in[5].in_var_TEXCOORD10_centroid, gl_in[6].in_var_TEXCOORD10_centroid, gl_in[7].in_var_TEXCOORD10_centroid, gl_in[8].in_var_TEXCOORD10_centroid, gl_in[9].in_var_TEXCOORD10_centroid, gl_in[10].in_var_TEXCOORD10_centroid, gl_in[11].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _145 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD11_centroid, gl_in[1].in_var_TEXCOORD11_centroid, gl_in[2].in_var_TEXCOORD11_centroid, gl_in[3].in_var_TEXCOORD11_centroid, gl_in[4].in_var_TEXCOORD11_centroid, gl_in[5].in_var_TEXCOORD11_centroid, gl_in[6].in_var_TEXCOORD11_centroid, gl_in[7].in_var_TEXCOORD11_centroid, gl_in[8].in_var_TEXCOORD11_centroid, gl_in[9].in_var_TEXCOORD11_centroid, gl_in[10].in_var_TEXCOORD11_centroid, gl_in[11].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _146 = spvUnsafeArray({ gl_in[0].in_var_COLOR0, gl_in[1].in_var_COLOR0, gl_in[2].in_var_COLOR0, gl_in[3].in_var_COLOR0, gl_in[4].in_var_COLOR0, gl_in[5].in_var_COLOR0, gl_in[6].in_var_COLOR0, gl_in[7].in_var_COLOR0, gl_in[8].in_var_COLOR0, gl_in[9].in_var_COLOR0, gl_in[10].in_var_COLOR0, gl_in[11].in_var_COLOR0 }); + spvUnsafeArray, 12> _147 = spvUnsafeArray, 12>({ spvUnsafeArray({ gl_in[0].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[1].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[2].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[3].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[4].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[5].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[6].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[7].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[8].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[9].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[10].in_var_TEXCOORD0_0 }), spvUnsafeArray({ gl_in[11].in_var_TEXCOORD0_0 }) }); + spvUnsafeArray _148 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD4, gl_in[1].in_var_TEXCOORD4, gl_in[2].in_var_TEXCOORD4, gl_in[3].in_var_TEXCOORD4, gl_in[4].in_var_TEXCOORD4, gl_in[5].in_var_TEXCOORD4, gl_in[6].in_var_TEXCOORD4, gl_in[7].in_var_TEXCOORD4, gl_in[8].in_var_TEXCOORD4, gl_in[9].in_var_TEXCOORD4, gl_in[10].in_var_TEXCOORD4, gl_in[11].in_var_TEXCOORD4 }); + spvUnsafeArray _149 = spvUnsafeArray({ gl_in[0].in_var_PRIMITIVE_ID, gl_in[1].in_var_PRIMITIVE_ID, gl_in[2].in_var_PRIMITIVE_ID, gl_in[3].in_var_PRIMITIVE_ID, gl_in[4].in_var_PRIMITIVE_ID, gl_in[5].in_var_PRIMITIVE_ID, gl_in[6].in_var_PRIMITIVE_ID, gl_in[7].in_var_PRIMITIVE_ID, gl_in[8].in_var_PRIMITIVE_ID, gl_in[9].in_var_PRIMITIVE_ID, gl_in[10].in_var_PRIMITIVE_ID, gl_in[11].in_var_PRIMITIVE_ID }); + spvUnsafeArray _150 = spvUnsafeArray({ gl_in[0].in_var_LIGHTMAP_ID, gl_in[1].in_var_LIGHTMAP_ID, gl_in[2].in_var_LIGHTMAP_ID, gl_in[3].in_var_LIGHTMAP_ID, gl_in[4].in_var_LIGHTMAP_ID, gl_in[5].in_var_LIGHTMAP_ID, gl_in[6].in_var_LIGHTMAP_ID, gl_in[7].in_var_LIGHTMAP_ID, gl_in[8].in_var_LIGHTMAP_ID, gl_in[9].in_var_LIGHTMAP_ID, gl_in[10].in_var_LIGHTMAP_ID, gl_in[11].in_var_LIGHTMAP_ID }); + spvUnsafeArray _259 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_Position, gl_in[1].in_var_VS_To_DS_Position, gl_in[2].in_var_VS_To_DS_Position, gl_in[3].in_var_VS_To_DS_Position, gl_in[4].in_var_VS_To_DS_Position, gl_in[5].in_var_VS_To_DS_Position, gl_in[6].in_var_VS_To_DS_Position, gl_in[7].in_var_VS_To_DS_Position, gl_in[8].in_var_VS_To_DS_Position, gl_in[9].in_var_VS_To_DS_Position, gl_in[10].in_var_VS_To_DS_Position, gl_in[11].in_var_VS_To_DS_Position }); + spvUnsafeArray _284 = spvUnsafeArray({ FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[0], _145[0], _146[0], _147[0], _148[0], _149[0], _150[0] } }, FBasePassInterpolantsVSToDS{ { } }, _259[0] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[1], _145[1], _146[1], _147[1], _148[1], _149[1], _150[1] } }, FBasePassInterpolantsVSToDS{ { } }, _259[1] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[2], _145[2], _146[2], _147[2], _148[2], _149[2], _150[2] } }, FBasePassInterpolantsVSToDS{ { } }, _259[2] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[3], _145[3], _146[3], _147[3], _148[3], _149[3], _150[3] } }, FBasePassInterpolantsVSToDS{ { } }, _259[3] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[4], _145[4], _146[4], _147[4], _148[4], _149[4], _150[4] } }, FBasePassInterpolantsVSToDS{ { } }, _259[4] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[5], _145[5], _146[5], _147[5], _148[5], _149[5], _150[5] } }, FBasePassInterpolantsVSToDS{ { } }, _259[5] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[6], _145[6], _146[6], _147[6], _148[6], _149[6], _150[6] } }, FBasePassInterpolantsVSToDS{ { } }, _259[6] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[7], _145[7], _146[7], _147[7], _148[7], _149[7], _150[7] } }, FBasePassInterpolantsVSToDS{ { } }, _259[7] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[8], _145[8], _146[8], _147[8], _148[8], _149[8], _150[8] } }, FBasePassInterpolantsVSToDS{ { } }, _259[8] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[9], _145[9], _146[9], _147[9], _148[9], _149[9], _150[9] } }, FBasePassInterpolantsVSToDS{ { } }, _259[9] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[10], _145[10], _146[10], _147[10], _148[10], _149[10], _150[10] } }, FBasePassInterpolantsVSToDS{ { } }, _259[10] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _144[11], _145[11], _146[11], _147[11], _148[11], _149[11], _150[11] } }, FBasePassInterpolantsVSToDS{ { } }, _259[11] } }); + spvUnsafeArray param_var_I; + param_var_I = _284; + float4 _301 = float4(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float3 _310 = View_PrimitiveSceneData._m0[(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.PrimitiveId * 26u) + 22u].xyz * float3x3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz, cross(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz) * float3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.w), param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz); + uint _313 = (gl_InvocationID < 2u) ? (gl_InvocationID + 1u) : 0u; + uint _314 = 2u * gl_InvocationID; + uint _315 = 3u + _314; + uint _316 = _314 + 4u; + float4 _328 = float4(param_var_I[_313].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _336 = float4(param_var_I[_315].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _344 = float4(param_var_I[_316].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + spvUnsafeArray _392 = spvUnsafeArray({ param_var_I[gl_InvocationID].Position, (((((float4(2.0) * param_var_I[gl_InvocationID].Position) + param_var_I[_313].Position) - (float4(dot(param_var_I[_313].Position - param_var_I[gl_InvocationID].Position, _301)) * _301)) * float4(0.3333333432674407958984375)) + ((((float4(2.0) * param_var_I[_315].Position) + param_var_I[_316].Position) - (float4(dot(param_var_I[_316].Position - param_var_I[_315].Position, _336)) * _336)) * float4(0.3333333432674407958984375))) * float4(0.5), (((((float4(2.0) * param_var_I[_313].Position) + param_var_I[gl_InvocationID].Position) - (float4(dot(param_var_I[gl_InvocationID].Position - param_var_I[_313].Position, _328)) * _328)) * float4(0.3333333432674407958984375)) + ((((float4(2.0) * param_var_I[_316].Position) + param_var_I[_315].Position) - (float4(dot(param_var_I[_315].Position - param_var_I[_316].Position, _344)) * _344)) * float4(0.3333333432674407958984375))) * float4(0.5) }); + gl_out[gl_InvocationID].out_var_TEXCOORD10_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + gl_out[gl_InvocationID].out_var_TEXCOORD11_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + gl_out[gl_InvocationID].out_var_COLOR0 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.Color; + gl_out[gl_InvocationID].out_var_TEXCOORD0 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TexCoords; + gl_out[gl_InvocationID].out_var_TEXCOORD4 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.LightMapCoordinate; + gl_out[gl_InvocationID].out_var_PRIMITIVE_ID = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.PrimitiveId; + gl_out[gl_InvocationID].out_var_LIGHTMAP_ID = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.LightmapDataIndex; + gl_out[gl_InvocationID].out_var_VS_To_DS_Position = param_var_I[gl_InvocationID].Position; + gl_out[gl_InvocationID].out_var_PN_POSITION = _392; + gl_out[gl_InvocationID].out_var_PN_DisplacementScales = _310; + gl_out[gl_InvocationID].out_var_PN_TessellationMultiplier = 1.0; + gl_out[gl_InvocationID].out_var_PN_WorldDisplacementMultiplier = 1.0; + temp_var_hullMainRetVal[gl_InvocationID] = FPNTessellationHSToDS{ param_var_I[gl_InvocationID], _392, _310, 1.0, 1.0 }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0u) + { + float4 _450 = (((((temp_var_hullMainRetVal[0u].WorldPosition[1] + temp_var_hullMainRetVal[0u].WorldPosition[2]) + temp_var_hullMainRetVal[1u].WorldPosition[1]) + temp_var_hullMainRetVal[1u].WorldPosition[2]) + temp_var_hullMainRetVal[2u].WorldPosition[1]) + temp_var_hullMainRetVal[2u].WorldPosition[2]) * float4(0.16666667163372039794921875); + float4 _463 = _142; + _463.x = 0.5 * (temp_var_hullMainRetVal[1u].TessellationMultiplier + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _469 = _463; + _469.y = 0.5 * (temp_var_hullMainRetVal[2u].TessellationMultiplier + temp_var_hullMainRetVal[0u].TessellationMultiplier); + float4 _474 = _469; + _474.z = 0.5 * (temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier); + float4 _481 = _474; + _481.w = 0.333000004291534423828125 * ((temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier) + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _589; + for (;;) + { + float4 _489 = View.View_ViewToClip * float4(0.0); + float4 _494 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[0u].WorldPosition[0].xyz, 1.0); + float3 _495 = _494.xyz; + float3 _496 = _489.xyz; + float _498 = _494.w; + float _499 = _489.w; + float4 _516 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[1u].WorldPosition[0].xyz, 1.0); + float3 _517 = _516.xyz; + float _519 = _516.w; + float4 _537 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[2u].WorldPosition[0].xyz, 1.0); + float3 _538 = _537.xyz; + float _540 = _537.w; + if (any((((select(int3(0), int3(1), (_495 - _496) < float3(_498 + _499)) + (int3(2) * select(int3(0), int3(1), (_495 + _496) > float3((-_498) - _499)))) | (select(int3(0), int3(1), (_517 - _496) < float3(_519 + _499)) + (int3(2) * select(int3(0), int3(1), (_517 + _496) > float3((-_519) - _499))))) | (select(int3(0), int3(1), (_538 - _496) < float3(_540 + _499)) + (int3(2) * select(int3(0), int3(1), (_538 + _496) > float3((-_540) - _499))))) != int3(3))) + { + _589 = float4(0.0); + break; + } + float3 _558 = temp_var_hullMainRetVal[0u].WorldPosition[0].xyz - temp_var_hullMainRetVal[1u].WorldPosition[0].xyz; + float3 _559 = temp_var_hullMainRetVal[1u].WorldPosition[0].xyz - temp_var_hullMainRetVal[2u].WorldPosition[0].xyz; + float3 _560 = temp_var_hullMainRetVal[2u].WorldPosition[0].xyz - temp_var_hullMainRetVal[0u].WorldPosition[0].xyz; + float3 _563 = (float3(0.5) * (temp_var_hullMainRetVal[0u].WorldPosition[0].xyz + temp_var_hullMainRetVal[1u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _566 = (float3(0.5) * (temp_var_hullMainRetVal[1u].WorldPosition[0].xyz + temp_var_hullMainRetVal[2u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _569 = (float3(0.5) * (temp_var_hullMainRetVal[2u].WorldPosition[0].xyz + temp_var_hullMainRetVal[0u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float _573 = sqrt(dot(_559, _559) / dot(_566, _566)); + float _577 = sqrt(dot(_560, _560) / dot(_569, _569)); + float _581 = sqrt(dot(_558, _558) / dot(_563, _563)); + float4 _586 = float4(_573, _577, _581, 1.0); + _586.w = 0.333000004291534423828125 * ((_573 + _577) + _581); + _589 = float4(View.View_AdaptiveTessellationFactor) * _586; + break; + } + float4 _591 = fast::clamp(_481 * _589, float4(1.0), float4(15.0)); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0u] = half(_591.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1u] = half(_591.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2u] = half(_591.z); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_591.w); + patchOut.out_var_PN_POSITION9 = _450 + ((_450 - (((temp_var_hullMainRetVal[2u].WorldPosition[0] + temp_var_hullMainRetVal[1u].WorldPosition[0]) + temp_var_hullMainRetVal[0u].WorldPosition[0]) * float4(0.3333333432674407958984375))) * float4(0.5)); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc b/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc new file mode 100644 index 0000000..2ec9306 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc @@ -0,0 +1,467 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct FVertexFactoryInterpolantsVSToPS +{ + float4 TangentToWorld0; + float4 TangentToWorld2; +}; + +struct FVertexFactoryInterpolantsVSToDS +{ + FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; +}; + +struct FHitProxyVSToDS +{ + FVertexFactoryInterpolantsVSToDS FactoryInterpolants; + float4 Position; + uint VertexID; +}; + +struct FHullShaderConstantDominantVertexData +{ + float2 UV; + float4 Normal; + float3 Tangent; +}; + +struct FHullShaderConstantDominantEdgeData +{ + float2 UV0; + float2 UV1; + float4 Normal0; + float4 Normal1; + float3 Tangent0; + float3 Tangent1; +}; + +struct FPNTessellationHSToDS +{ + FHitProxyVSToDS PassSpecificData; + spvUnsafeArray WorldPosition; + float3 DisplacementScale; + float TessellationMultiplier; + float WorldDisplacementMultiplier; + FHullShaderConstantDominantVertexData DominantVertex; + FHullShaderConstantDominantEdgeData DominantEdge; +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_DrawsVelocity; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + uint Primitive_LightingChannelMask; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightmapDataIndex; + packed_float3 Primitive_PreSkinnedLocalBounds; + int Primitive_SingleCaptureIndex; + uint Primitive_OutputVelocity; + uint PrePadding_Primitive_420; + uint PrePadding_Primitive_424; + uint PrePadding_Primitive_428; + float4 Primitive_CustomPrimitiveData[4]; +}; + +constant float4 _140 = {}; + +struct main0_out +{ + float3 out_var_PN_DisplacementScales; + float2 out_var_PN_DominantEdge; + float2 out_var_PN_DominantEdge1; + float4 out_var_PN_DominantEdge2; + float4 out_var_PN_DominantEdge3; + float3 out_var_PN_DominantEdge4; + float3 out_var_PN_DominantEdge5; + float2 out_var_PN_DominantVertex; + float4 out_var_PN_DominantVertex1; + float3 out_var_PN_DominantVertex2; + spvUnsafeArray out_var_PN_POSITION; + float out_var_PN_TessellationMultiplier; + float out_var_PN_WorldDisplacementMultiplier; + float4 out_var_TEXCOORD10_centroid; + float4 out_var_TEXCOORD11_centroid; + float4 out_var_VS_To_DS_Position; + uint out_var_VS_To_DS_VertexID; +}; + +struct main0_patchOut +{ + float4 out_var_PN_POSITION9; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[attribute(0)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(1)]]; + float4 in_var_VS_To_DS_Position [[attribute(2)]]; + uint in_var_VS_To_DS_VertexID [[attribute(3)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_Primitive& Primitive [[buffer(1)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + threadgroup FPNTessellationHSToDS temp_var_hullMainRetVal[3]; + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray _142 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD10_centroid, gl_in[1].in_var_TEXCOORD10_centroid, gl_in[2].in_var_TEXCOORD10_centroid, gl_in[3].in_var_TEXCOORD10_centroid, gl_in[4].in_var_TEXCOORD10_centroid, gl_in[5].in_var_TEXCOORD10_centroid, gl_in[6].in_var_TEXCOORD10_centroid, gl_in[7].in_var_TEXCOORD10_centroid, gl_in[8].in_var_TEXCOORD10_centroid, gl_in[9].in_var_TEXCOORD10_centroid, gl_in[10].in_var_TEXCOORD10_centroid, gl_in[11].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _143 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD11_centroid, gl_in[1].in_var_TEXCOORD11_centroid, gl_in[2].in_var_TEXCOORD11_centroid, gl_in[3].in_var_TEXCOORD11_centroid, gl_in[4].in_var_TEXCOORD11_centroid, gl_in[5].in_var_TEXCOORD11_centroid, gl_in[6].in_var_TEXCOORD11_centroid, gl_in[7].in_var_TEXCOORD11_centroid, gl_in[8].in_var_TEXCOORD11_centroid, gl_in[9].in_var_TEXCOORD11_centroid, gl_in[10].in_var_TEXCOORD11_centroid, gl_in[11].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _192 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_Position, gl_in[1].in_var_VS_To_DS_Position, gl_in[2].in_var_VS_To_DS_Position, gl_in[3].in_var_VS_To_DS_Position, gl_in[4].in_var_VS_To_DS_Position, gl_in[5].in_var_VS_To_DS_Position, gl_in[6].in_var_VS_To_DS_Position, gl_in[7].in_var_VS_To_DS_Position, gl_in[8].in_var_VS_To_DS_Position, gl_in[9].in_var_VS_To_DS_Position, gl_in[10].in_var_VS_To_DS_Position, gl_in[11].in_var_VS_To_DS_Position }); + spvUnsafeArray _193 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_VertexID, gl_in[1].in_var_VS_To_DS_VertexID, gl_in[2].in_var_VS_To_DS_VertexID, gl_in[3].in_var_VS_To_DS_VertexID, gl_in[4].in_var_VS_To_DS_VertexID, gl_in[5].in_var_VS_To_DS_VertexID, gl_in[6].in_var_VS_To_DS_VertexID, gl_in[7].in_var_VS_To_DS_VertexID, gl_in[8].in_var_VS_To_DS_VertexID, gl_in[9].in_var_VS_To_DS_VertexID, gl_in[10].in_var_VS_To_DS_VertexID, gl_in[11].in_var_VS_To_DS_VertexID }); + spvUnsafeArray _230 = spvUnsafeArray({ FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[0], _143[0] } }, _192[0], _193[0] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[1], _143[1] } }, _192[1], _193[1] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[2], _143[2] } }, _192[2], _193[2] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[3], _143[3] } }, _192[3], _193[3] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[4], _143[4] } }, _192[4], _193[4] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[5], _143[5] } }, _192[5], _193[5] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[6], _143[6] } }, _192[6], _193[6] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[7], _143[7] } }, _192[7], _193[7] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[8], _143[8] } }, _192[8], _193[8] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[9], _143[9] } }, _192[9], _193[9] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[10], _143[10] } }, _192[10], _193[10] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _142[11], _143[11] } }, _192[11], _193[11] } }); + spvUnsafeArray param_var_I; + param_var_I = _230; + float4 _247 = float4(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float3 _251 = Primitive.Primitive_NonUniformScale.xyz * float3x3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz, cross(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz) * float3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.w), param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz); + uint _254 = (gl_InvocationID < 2u) ? (gl_InvocationID + 1u) : 0u; + uint _255 = 2u * gl_InvocationID; + uint _256 = 3u + _255; + uint _257 = _255 + 4u; + uint _264 = (_254 < 2u) ? (_254 + 1u) : 0u; + uint _265 = 2u * _254; + uint _266 = 3u + _265; + uint _267 = _265 + 4u; + float4 _279 = float4(param_var_I[9u + gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _315; + float4 _316; + float4 _317; + float4 _318; + if ((param_var_I[_266].VertexID < param_var_I[_254].VertexID) || ((param_var_I[_266].VertexID == param_var_I[_254].VertexID) && (param_var_I[_267].VertexID < param_var_I[_264].VertexID))) + { + _315 = param_var_I[_267].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + _316 = param_var_I[_267].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + _317 = param_var_I[_266].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + _318 = param_var_I[_266].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + } + else + { + _315 = param_var_I[_264].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + _316 = param_var_I[_264].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + _317 = param_var_I[_254].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + _318 = param_var_I[_254].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + } + float4 _324 = float4(_318.xyz, 0.0); + float4 _328 = float4(_316.xyz, 0.0); + float4 _336 = float4(param_var_I[_254].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _344 = float4(param_var_I[_256].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _352 = float4(param_var_I[_257].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + spvUnsafeArray _402 = spvUnsafeArray({ param_var_I[gl_InvocationID].Position, (((((float4(2.0) * param_var_I[gl_InvocationID].Position) + param_var_I[_254].Position) - (float4(dot(param_var_I[_254].Position - param_var_I[gl_InvocationID].Position, _247)) * _247)) * float4(0.3333333432674407958984375)) + ((((float4(2.0) * param_var_I[_256].Position) + param_var_I[_257].Position) - (float4(dot(param_var_I[_257].Position - param_var_I[_256].Position, _344)) * _344)) * float4(0.3333333432674407958984375))) * float4(0.5), (((((float4(2.0) * param_var_I[_254].Position) + param_var_I[gl_InvocationID].Position) - (float4(dot(param_var_I[gl_InvocationID].Position - param_var_I[_254].Position, _336)) * _336)) * float4(0.3333333432674407958984375)) + ((((float4(2.0) * param_var_I[_257].Position) + param_var_I[_256].Position) - (float4(dot(param_var_I[_256].Position - param_var_I[_257].Position, _352)) * _352)) * float4(0.3333333432674407958984375))) * float4(0.5) }); + gl_out[gl_InvocationID].out_var_TEXCOORD10_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + gl_out[gl_InvocationID].out_var_TEXCOORD11_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + gl_out[gl_InvocationID].out_var_VS_To_DS_Position = param_var_I[gl_InvocationID].Position; + gl_out[gl_InvocationID].out_var_VS_To_DS_VertexID = param_var_I[gl_InvocationID].VertexID; + gl_out[gl_InvocationID].out_var_PN_POSITION = _402; + gl_out[gl_InvocationID].out_var_PN_DisplacementScales = _251; + gl_out[gl_InvocationID].out_var_PN_TessellationMultiplier = 1.0; + gl_out[gl_InvocationID].out_var_PN_WorldDisplacementMultiplier = 1.0; + gl_out[gl_InvocationID].out_var_PN_DominantVertex = float2(0.0); + gl_out[gl_InvocationID].out_var_PN_DominantVertex1 = _279; + gl_out[gl_InvocationID].out_var_PN_DominantVertex2 = param_var_I[9u + gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz; + gl_out[gl_InvocationID].out_var_PN_DominantEdge = float2(0.0); + gl_out[gl_InvocationID].out_var_PN_DominantEdge1 = float2(0.0); + gl_out[gl_InvocationID].out_var_PN_DominantEdge2 = _324; + gl_out[gl_InvocationID].out_var_PN_DominantEdge3 = _328; + gl_out[gl_InvocationID].out_var_PN_DominantEdge4 = _317.xyz; + gl_out[gl_InvocationID].out_var_PN_DominantEdge5 = _315.xyz; + temp_var_hullMainRetVal[gl_InvocationID] = FPNTessellationHSToDS{ param_var_I[gl_InvocationID], _402, _251, 1.0, 1.0, FHullShaderConstantDominantVertexData{ float2(0.0), _279, param_var_I[9u + gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz }, FHullShaderConstantDominantEdgeData{ float2(0.0), float2(0.0), _324, _328, _317.xyz, _315.xyz } }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0u) + { + float4 _461 = (((((temp_var_hullMainRetVal[0u].WorldPosition[1] + temp_var_hullMainRetVal[0u].WorldPosition[2]) + temp_var_hullMainRetVal[1u].WorldPosition[1]) + temp_var_hullMainRetVal[1u].WorldPosition[2]) + temp_var_hullMainRetVal[2u].WorldPosition[1]) + temp_var_hullMainRetVal[2u].WorldPosition[2]) * float4(0.16666667163372039794921875); + float4 _474 = _140; + _474.x = 0.5 * (temp_var_hullMainRetVal[1u].TessellationMultiplier + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _480 = _474; + _480.y = 0.5 * (temp_var_hullMainRetVal[2u].TessellationMultiplier + temp_var_hullMainRetVal[0u].TessellationMultiplier); + float4 _485 = _480; + _485.z = 0.5 * (temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier); + float4 _492 = _485; + _492.w = 0.333000004291534423828125 * ((temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier) + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _600; + for (;;) + { + float4 _500 = View.View_ViewToClip * float4(0.0); + float4 _505 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[0u].WorldPosition[0].xyz, 1.0); + float3 _506 = _505.xyz; + float3 _507 = _500.xyz; + float _509 = _505.w; + float _510 = _500.w; + float4 _527 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[1u].WorldPosition[0].xyz, 1.0); + float3 _528 = _527.xyz; + float _530 = _527.w; + float4 _548 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[2u].WorldPosition[0].xyz, 1.0); + float3 _549 = _548.xyz; + float _551 = _548.w; + if (any((((select(int3(0), int3(1), (_506 - _507) < float3(_509 + _510)) + (int3(2) * select(int3(0), int3(1), (_506 + _507) > float3((-_509) - _510)))) | (select(int3(0), int3(1), (_528 - _507) < float3(_530 + _510)) + (int3(2) * select(int3(0), int3(1), (_528 + _507) > float3((-_530) - _510))))) | (select(int3(0), int3(1), (_549 - _507) < float3(_551 + _510)) + (int3(2) * select(int3(0), int3(1), (_549 + _507) > float3((-_551) - _510))))) != int3(3))) + { + _600 = float4(0.0); + break; + } + float3 _569 = temp_var_hullMainRetVal[0u].WorldPosition[0].xyz - temp_var_hullMainRetVal[1u].WorldPosition[0].xyz; + float3 _570 = temp_var_hullMainRetVal[1u].WorldPosition[0].xyz - temp_var_hullMainRetVal[2u].WorldPosition[0].xyz; + float3 _571 = temp_var_hullMainRetVal[2u].WorldPosition[0].xyz - temp_var_hullMainRetVal[0u].WorldPosition[0].xyz; + float3 _574 = (float3(0.5) * (temp_var_hullMainRetVal[0u].WorldPosition[0].xyz + temp_var_hullMainRetVal[1u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _577 = (float3(0.5) * (temp_var_hullMainRetVal[1u].WorldPosition[0].xyz + temp_var_hullMainRetVal[2u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _580 = (float3(0.5) * (temp_var_hullMainRetVal[2u].WorldPosition[0].xyz + temp_var_hullMainRetVal[0u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float _584 = sqrt(dot(_570, _570) / dot(_577, _577)); + float _588 = sqrt(dot(_571, _571) / dot(_580, _580)); + float _592 = sqrt(dot(_569, _569) / dot(_574, _574)); + float4 _597 = float4(_584, _588, _592, 1.0); + _597.w = 0.333000004291534423828125 * ((_584 + _588) + _592); + _600 = float4(View.View_AdaptiveTessellationFactor) * _597; + break; + } + float4 _602 = fast::clamp(_492 * _600, float4(1.0), float4(15.0)); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0u] = half(_602.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1u] = half(_602.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2u] = half(_602.z); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_602.w); + patchOut.out_var_PN_POSITION9 = _461 + ((_461 - (((temp_var_hullMainRetVal[2u].WorldPosition[0] + temp_var_hullMainRetVal[1u].WorldPosition[0]) + temp_var_hullMainRetVal[0u].WorldPosition[0]) * float4(0.3333333432674407958984375))) * float4(0.5)); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc b/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc new file mode 100644 index 0000000..833122c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc @@ -0,0 +1,411 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct FVertexFactoryInterpolantsVSToPS +{ + float4 TangentToWorld0; + float4 TangentToWorld2; + float4 Color; + spvUnsafeArray TexCoords; +}; + +struct FVertexFactoryInterpolantsVSToDS +{ + FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; +}; + +struct FHitProxyVSToDS +{ + FVertexFactoryInterpolantsVSToDS FactoryInterpolants; + float4 Position; +}; + +struct FPNTessellationHSToDS +{ + FHitProxyVSToDS PassSpecificData; + spvUnsafeArray WorldPosition; + float3 DisplacementScale; + float TessellationMultiplier; + float WorldDisplacementMultiplier; +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_DrawsVelocity; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + uint Primitive_LightingChannelMask; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightmapDataIndex; + packed_float3 Primitive_PreSkinnedLocalBounds; + int Primitive_SingleCaptureIndex; + uint Primitive_OutputVelocity; + uint PrePadding_Primitive_420; + uint PrePadding_Primitive_424; + uint PrePadding_Primitive_428; + float4 Primitive_CustomPrimitiveData[4]; +}; + +constant float4 _127 = {}; + +struct main0_out +{ + float4 out_var_COLOR0; + float3 out_var_PN_DisplacementScales; + spvUnsafeArray out_var_PN_POSITION; + float out_var_PN_TessellationMultiplier; + float out_var_PN_WorldDisplacementMultiplier; + spvUnsafeArray out_var_TEXCOORD0; + float4 out_var_TEXCOORD10_centroid; + float4 out_var_TEXCOORD11_centroid; + float4 out_var_VS_To_DS_Position; +}; + +struct main0_patchOut +{ + float4 out_var_PN_POSITION9; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[attribute(0)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(1)]]; + float4 in_var_COLOR0 [[attribute(2)]]; + float2 in_var_TEXCOORD0_0 [[attribute(3)]]; + float2 in_var_TEXCOORD0_1 [[attribute(4)]]; + float4 in_var_VS_To_DS_Position [[attribute(5)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_Primitive& Primitive [[buffer(1)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device main0_patchOut* spvPatchOut [[buffer(27)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + threadgroup FPNTessellationHSToDS temp_var_hullMainRetVal[3]; + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + device main0_patchOut& patchOut = spvPatchOut[gl_PrimitiveID]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray _129 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD10_centroid, gl_in[1].in_var_TEXCOORD10_centroid, gl_in[2].in_var_TEXCOORD10_centroid, gl_in[3].in_var_TEXCOORD10_centroid, gl_in[4].in_var_TEXCOORD10_centroid, gl_in[5].in_var_TEXCOORD10_centroid, gl_in[6].in_var_TEXCOORD10_centroid, gl_in[7].in_var_TEXCOORD10_centroid, gl_in[8].in_var_TEXCOORD10_centroid, gl_in[9].in_var_TEXCOORD10_centroid, gl_in[10].in_var_TEXCOORD10_centroid, gl_in[11].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _130 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD11_centroid, gl_in[1].in_var_TEXCOORD11_centroid, gl_in[2].in_var_TEXCOORD11_centroid, gl_in[3].in_var_TEXCOORD11_centroid, gl_in[4].in_var_TEXCOORD11_centroid, gl_in[5].in_var_TEXCOORD11_centroid, gl_in[6].in_var_TEXCOORD11_centroid, gl_in[7].in_var_TEXCOORD11_centroid, gl_in[8].in_var_TEXCOORD11_centroid, gl_in[9].in_var_TEXCOORD11_centroid, gl_in[10].in_var_TEXCOORD11_centroid, gl_in[11].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _131 = spvUnsafeArray({ gl_in[0].in_var_COLOR0, gl_in[1].in_var_COLOR0, gl_in[2].in_var_COLOR0, gl_in[3].in_var_COLOR0, gl_in[4].in_var_COLOR0, gl_in[5].in_var_COLOR0, gl_in[6].in_var_COLOR0, gl_in[7].in_var_COLOR0, gl_in[8].in_var_COLOR0, gl_in[9].in_var_COLOR0, gl_in[10].in_var_COLOR0, gl_in[11].in_var_COLOR0 }); + spvUnsafeArray, 12> _132 = spvUnsafeArray, 12>({ spvUnsafeArray({ gl_in[0].in_var_TEXCOORD0_0, gl_in[0].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[1].in_var_TEXCOORD0_0, gl_in[1].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[2].in_var_TEXCOORD0_0, gl_in[2].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[3].in_var_TEXCOORD0_0, gl_in[3].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[4].in_var_TEXCOORD0_0, gl_in[4].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[5].in_var_TEXCOORD0_0, gl_in[5].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[6].in_var_TEXCOORD0_0, gl_in[6].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[7].in_var_TEXCOORD0_0, gl_in[7].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[8].in_var_TEXCOORD0_0, gl_in[8].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[9].in_var_TEXCOORD0_0, gl_in[9].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[10].in_var_TEXCOORD0_0, gl_in[10].in_var_TEXCOORD0_1 }), spvUnsafeArray({ gl_in[11].in_var_TEXCOORD0_0, gl_in[11].in_var_TEXCOORD0_1 }) }); + spvUnsafeArray _205 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_Position, gl_in[1].in_var_VS_To_DS_Position, gl_in[2].in_var_VS_To_DS_Position, gl_in[3].in_var_VS_To_DS_Position, gl_in[4].in_var_VS_To_DS_Position, gl_in[5].in_var_VS_To_DS_Position, gl_in[6].in_var_VS_To_DS_Position, gl_in[7].in_var_VS_To_DS_Position, gl_in[8].in_var_VS_To_DS_Position, gl_in[9].in_var_VS_To_DS_Position, gl_in[10].in_var_VS_To_DS_Position, gl_in[11].in_var_VS_To_DS_Position }); + spvUnsafeArray _230 = spvUnsafeArray({ FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[0], _130[0], _131[0], _132[0] } }, _205[0] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[1], _130[1], _131[1], _132[1] } }, _205[1] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[2], _130[2], _131[2], _132[2] } }, _205[2] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[3], _130[3], _131[3], _132[3] } }, _205[3] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[4], _130[4], _131[4], _132[4] } }, _205[4] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[5], _130[5], _131[5], _132[5] } }, _205[5] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[6], _130[6], _131[6], _132[6] } }, _205[6] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[7], _130[7], _131[7], _132[7] } }, _205[7] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[8], _130[8], _131[8], _132[8] } }, _205[8] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[9], _130[9], _131[9], _132[9] } }, _205[9] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[10], _130[10], _131[10], _132[10] } }, _205[10] }, FHitProxyVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _129[11], _130[11], _131[11], _132[11] } }, _205[11] } }); + spvUnsafeArray param_var_I; + param_var_I = _230; + float4 _247 = float4(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float3 _251 = Primitive.Primitive_NonUniformScale.xyz * float3x3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz, cross(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz) * float3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.w), param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz); + uint _254 = (gl_InvocationID < 2u) ? (gl_InvocationID + 1u) : 0u; + uint _255 = 2u * gl_InvocationID; + uint _256 = 3u + _255; + uint _257 = _255 + 4u; + float4 _269 = float4(param_var_I[_254].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _277 = float4(param_var_I[_256].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + float4 _285 = float4(param_var_I[_257].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, 0.0); + spvUnsafeArray _333 = spvUnsafeArray({ param_var_I[gl_InvocationID].Position, (((((float4(2.0) * param_var_I[gl_InvocationID].Position) + param_var_I[_254].Position) - (float4(dot(param_var_I[_254].Position - param_var_I[gl_InvocationID].Position, _247)) * _247)) * float4(0.3333333432674407958984375)) + ((((float4(2.0) * param_var_I[_256].Position) + param_var_I[_257].Position) - (float4(dot(param_var_I[_257].Position - param_var_I[_256].Position, _277)) * _277)) * float4(0.3333333432674407958984375))) * float4(0.5), (((((float4(2.0) * param_var_I[_254].Position) + param_var_I[gl_InvocationID].Position) - (float4(dot(param_var_I[gl_InvocationID].Position - param_var_I[_254].Position, _269)) * _269)) * float4(0.3333333432674407958984375)) + ((((float4(2.0) * param_var_I[_257].Position) + param_var_I[_256].Position) - (float4(dot(param_var_I[_256].Position - param_var_I[_257].Position, _285)) * _285)) * float4(0.3333333432674407958984375))) * float4(0.5) }); + gl_out[gl_InvocationID].out_var_TEXCOORD10_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + gl_out[gl_InvocationID].out_var_TEXCOORD11_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + gl_out[gl_InvocationID].out_var_COLOR0 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.Color; + gl_out[gl_InvocationID].out_var_TEXCOORD0 = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TexCoords; + gl_out[gl_InvocationID].out_var_VS_To_DS_Position = param_var_I[gl_InvocationID].Position; + gl_out[gl_InvocationID].out_var_PN_POSITION = _333; + gl_out[gl_InvocationID].out_var_PN_DisplacementScales = _251; + gl_out[gl_InvocationID].out_var_PN_TessellationMultiplier = 1.0; + gl_out[gl_InvocationID].out_var_PN_WorldDisplacementMultiplier = 1.0; + temp_var_hullMainRetVal[gl_InvocationID] = FPNTessellationHSToDS{ param_var_I[gl_InvocationID], _333, _251, 1.0, 1.0 }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0u) + { + float4 _385 = (((((temp_var_hullMainRetVal[0u].WorldPosition[1] + temp_var_hullMainRetVal[0u].WorldPosition[2]) + temp_var_hullMainRetVal[1u].WorldPosition[1]) + temp_var_hullMainRetVal[1u].WorldPosition[2]) + temp_var_hullMainRetVal[2u].WorldPosition[1]) + temp_var_hullMainRetVal[2u].WorldPosition[2]) * float4(0.16666667163372039794921875); + float4 _398 = _127; + _398.x = 0.5 * (temp_var_hullMainRetVal[1u].TessellationMultiplier + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _404 = _398; + _404.y = 0.5 * (temp_var_hullMainRetVal[2u].TessellationMultiplier + temp_var_hullMainRetVal[0u].TessellationMultiplier); + float4 _409 = _404; + _409.z = 0.5 * (temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier); + float4 _416 = _409; + _416.w = 0.333000004291534423828125 * ((temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier) + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _524; + for (;;) + { + float4 _424 = View.View_ViewToClip * float4(0.0); + float4 _429 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[0u].WorldPosition[0].xyz, 1.0); + float3 _430 = _429.xyz; + float3 _431 = _424.xyz; + float _433 = _429.w; + float _434 = _424.w; + float4 _451 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[1u].WorldPosition[0].xyz, 1.0); + float3 _452 = _451.xyz; + float _454 = _451.w; + float4 _472 = View.View_TranslatedWorldToClip * float4(temp_var_hullMainRetVal[2u].WorldPosition[0].xyz, 1.0); + float3 _473 = _472.xyz; + float _475 = _472.w; + if (any((((select(int3(0), int3(1), (_430 - _431) < float3(_433 + _434)) + (int3(2) * select(int3(0), int3(1), (_430 + _431) > float3((-_433) - _434)))) | (select(int3(0), int3(1), (_452 - _431) < float3(_454 + _434)) + (int3(2) * select(int3(0), int3(1), (_452 + _431) > float3((-_454) - _434))))) | (select(int3(0), int3(1), (_473 - _431) < float3(_475 + _434)) + (int3(2) * select(int3(0), int3(1), (_473 + _431) > float3((-_475) - _434))))) != int3(3))) + { + _524 = float4(0.0); + break; + } + float3 _493 = temp_var_hullMainRetVal[0u].WorldPosition[0].xyz - temp_var_hullMainRetVal[1u].WorldPosition[0].xyz; + float3 _494 = temp_var_hullMainRetVal[1u].WorldPosition[0].xyz - temp_var_hullMainRetVal[2u].WorldPosition[0].xyz; + float3 _495 = temp_var_hullMainRetVal[2u].WorldPosition[0].xyz - temp_var_hullMainRetVal[0u].WorldPosition[0].xyz; + float3 _498 = (float3(0.5) * (temp_var_hullMainRetVal[0u].WorldPosition[0].xyz + temp_var_hullMainRetVal[1u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _501 = (float3(0.5) * (temp_var_hullMainRetVal[1u].WorldPosition[0].xyz + temp_var_hullMainRetVal[2u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float3 _504 = (float3(0.5) * (temp_var_hullMainRetVal[2u].WorldPosition[0].xyz + temp_var_hullMainRetVal[0u].WorldPosition[0].xyz)) - float3(View.View_TranslatedWorldCameraOrigin); + float _508 = sqrt(dot(_494, _494) / dot(_501, _501)); + float _512 = sqrt(dot(_495, _495) / dot(_504, _504)); + float _516 = sqrt(dot(_493, _493) / dot(_498, _498)); + float4 _521 = float4(_508, _512, _516, 1.0); + _521.w = 0.333000004291534423828125 * ((_508 + _512) + _516); + _524 = float4(View.View_AdaptiveTessellationFactor) * _521; + break; + } + float4 _526 = fast::clamp(_416 * _524, float4(1.0), float4(15.0)); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0u] = half(_526.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1u] = half(_526.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2u] = half(_526.z); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_526.w); + patchOut.out_var_PN_POSITION9 = _385 + ((_385 - (((temp_var_hullMainRetVal[2u].WorldPosition[0] + temp_var_hullMainRetVal[1u].WorldPosition[0]) + temp_var_hullMainRetVal[0u].WorldPosition[0]) * float4(0.3333333432674407958984375))) * float4(0.5)); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc b/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc new file mode 100644 index 0000000..04f5a1d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc @@ -0,0 +1,178 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct FVertexFactoryInterpolantsVSToPS +{ + float4 TangentToWorld0; + float4 TangentToWorld2; +}; + +struct FVertexFactoryInterpolantsVSToDS +{ + FVertexFactoryInterpolantsVSToPS InterpolantsVSToPS; +}; + +struct FSharedBasePassInterpolants +{ +}; +struct FBasePassInterpolantsVSToDS +{ + FSharedBasePassInterpolants _m0; +}; + +struct FBasePassVSToDS +{ + FVertexFactoryInterpolantsVSToDS FactoryInterpolants; + FBasePassInterpolantsVSToDS BasePassInterpolants; + float4 Position; +}; + +struct FFlatTessellationHSToDS +{ + FBasePassVSToDS PassSpecificData; + float3 DisplacementScale; + float TessellationMultiplier; + float WorldDisplacementMultiplier; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_DrawsVelocity; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + uint Primitive_LightingChannelMask; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightmapDataIndex; + packed_float3 Primitive_PreSkinnedLocalBounds; + int Primitive_SingleCaptureIndex; + uint Primitive_OutputVelocity; + uint PrePadding_Primitive_420; + uint PrePadding_Primitive_424; + uint PrePadding_Primitive_428; + float4 Primitive_CustomPrimitiveData[4]; +}; + +struct type_Material +{ + float4 Material_VectorExpressions[3]; + float4 Material_ScalarExpressions[1]; +}; + +constant float4 _88 = {}; + +struct main0_out +{ + float3 out_var_Flat_DisplacementScales; + float out_var_Flat_TessellationMultiplier; + float out_var_Flat_WorldDisplacementMultiplier; + float4 out_var_TEXCOORD10_centroid; + float4 out_var_TEXCOORD11_centroid; + float4 out_var_VS_To_DS_Position; +}; + +struct main0_in +{ + float4 in_var_TEXCOORD10_centroid [[attribute(0)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(1)]]; + float4 in_var_VS_To_DS_Position [[attribute(2)]]; +}; + +kernel void main0(main0_in in [[stage_in]], constant type_Primitive& Primitive [[buffer(0)]], constant type_Material& Material [[buffer(1)]], uint gl_InvocationID [[thread_index_in_threadgroup]], uint gl_PrimitiveID [[threadgroup_position_in_grid]], device main0_out* spvOut [[buffer(28)]], constant uint* spvIndirectParams [[buffer(29)]], device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]], threadgroup main0_in* gl_in [[threadgroup(0)]]) +{ + threadgroup FFlatTessellationHSToDS temp_var_hullMainRetVal[3]; + device main0_out* gl_out = &spvOut[gl_PrimitiveID * 3]; + if (gl_InvocationID < spvIndirectParams[0]) + gl_in[gl_InvocationID] = in; + threadgroup_barrier(mem_flags::mem_threadgroup); + if (gl_InvocationID >= 3) + return; + spvUnsafeArray _90 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD10_centroid, gl_in[1].in_var_TEXCOORD10_centroid, gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _91 = spvUnsafeArray({ gl_in[0].in_var_TEXCOORD11_centroid, gl_in[1].in_var_TEXCOORD11_centroid, gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _104 = spvUnsafeArray({ gl_in[0].in_var_VS_To_DS_Position, gl_in[1].in_var_VS_To_DS_Position, gl_in[2].in_var_VS_To_DS_Position }); + spvUnsafeArray _111 = spvUnsafeArray({ FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _90[0], _91[0] } }, FBasePassInterpolantsVSToDS{ { } }, _104[0] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _90[1], _91[1] } }, FBasePassInterpolantsVSToDS{ { } }, _104[1] }, FBasePassVSToDS{ FVertexFactoryInterpolantsVSToDS{ FVertexFactoryInterpolantsVSToPS{ _90[2], _91[2] } }, FBasePassInterpolantsVSToDS{ { } }, _104[2] } }); + spvUnsafeArray param_var_I; + param_var_I = _111; + float3 _128 = Primitive.Primitive_NonUniformScale.xyz * float3x3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz, cross(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz, param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0.xyz) * float3(param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.w), param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2.xyz); + gl_out[gl_InvocationID].out_var_TEXCOORD10_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld0; + gl_out[gl_InvocationID].out_var_TEXCOORD11_centroid = param_var_I[gl_InvocationID].FactoryInterpolants.InterpolantsVSToPS.TangentToWorld2; + gl_out[gl_InvocationID].out_var_VS_To_DS_Position = param_var_I[gl_InvocationID].Position; + gl_out[gl_InvocationID].out_var_Flat_DisplacementScales = _128; + gl_out[gl_InvocationID].out_var_Flat_TessellationMultiplier = Material.Material_ScalarExpressions[0].x; + gl_out[gl_InvocationID].out_var_Flat_WorldDisplacementMultiplier = 1.0; + temp_var_hullMainRetVal[gl_InvocationID] = FFlatTessellationHSToDS{ param_var_I[gl_InvocationID], _128, Material.Material_ScalarExpressions[0].x, 1.0 }; + threadgroup_barrier(mem_flags::mem_device | mem_flags::mem_threadgroup); + if (gl_InvocationID == 0u) + { + float4 _154 = _88; + _154.x = 0.5 * (temp_var_hullMainRetVal[1u].TessellationMultiplier + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _160 = _154; + _160.y = 0.5 * (temp_var_hullMainRetVal[2u].TessellationMultiplier + temp_var_hullMainRetVal[0u].TessellationMultiplier); + float4 _165 = _160; + _165.z = 0.5 * (temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier); + float4 _172 = _165; + _172.w = 0.333000004291534423828125 * ((temp_var_hullMainRetVal[0u].TessellationMultiplier + temp_var_hullMainRetVal[1u].TessellationMultiplier) + temp_var_hullMainRetVal[2u].TessellationMultiplier); + float4 _173 = fast::clamp(_172, float4(1.0), float4(15.0)); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0u] = half(_173.x); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1u] = half(_173.y); + spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2u] = half(_173.z); + spvTessLevel[gl_PrimitiveID].insideTessellationFactor = half(_173.w); + } +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese b/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese new file mode 100644 index 0000000..b2e5ecc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese @@ -0,0 +1,420 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_ShadowDepthPass +{ + float PrePadding_ShadowDepthPass_LPV_0; + float PrePadding_ShadowDepthPass_LPV_4; + float PrePadding_ShadowDepthPass_LPV_8; + float PrePadding_ShadowDepthPass_LPV_12; + float PrePadding_ShadowDepthPass_LPV_16; + float PrePadding_ShadowDepthPass_LPV_20; + float PrePadding_ShadowDepthPass_LPV_24; + float PrePadding_ShadowDepthPass_LPV_28; + float PrePadding_ShadowDepthPass_LPV_32; + float PrePadding_ShadowDepthPass_LPV_36; + float PrePadding_ShadowDepthPass_LPV_40; + float PrePadding_ShadowDepthPass_LPV_44; + float PrePadding_ShadowDepthPass_LPV_48; + float PrePadding_ShadowDepthPass_LPV_52; + float PrePadding_ShadowDepthPass_LPV_56; + float PrePadding_ShadowDepthPass_LPV_60; + float PrePadding_ShadowDepthPass_LPV_64; + float PrePadding_ShadowDepthPass_LPV_68; + float PrePadding_ShadowDepthPass_LPV_72; + float PrePadding_ShadowDepthPass_LPV_76; + float PrePadding_ShadowDepthPass_LPV_80; + float PrePadding_ShadowDepthPass_LPV_84; + float PrePadding_ShadowDepthPass_LPV_88; + float PrePadding_ShadowDepthPass_LPV_92; + float PrePadding_ShadowDepthPass_LPV_96; + float PrePadding_ShadowDepthPass_LPV_100; + float PrePadding_ShadowDepthPass_LPV_104; + float PrePadding_ShadowDepthPass_LPV_108; + float PrePadding_ShadowDepthPass_LPV_112; + float PrePadding_ShadowDepthPass_LPV_116; + float PrePadding_ShadowDepthPass_LPV_120; + float PrePadding_ShadowDepthPass_LPV_124; + float PrePadding_ShadowDepthPass_LPV_128; + float PrePadding_ShadowDepthPass_LPV_132; + float PrePadding_ShadowDepthPass_LPV_136; + float PrePadding_ShadowDepthPass_LPV_140; + float PrePadding_ShadowDepthPass_LPV_144; + float PrePadding_ShadowDepthPass_LPV_148; + float PrePadding_ShadowDepthPass_LPV_152; + float PrePadding_ShadowDepthPass_LPV_156; + float PrePadding_ShadowDepthPass_LPV_160; + float PrePadding_ShadowDepthPass_LPV_164; + float PrePadding_ShadowDepthPass_LPV_168; + float PrePadding_ShadowDepthPass_LPV_172; + float PrePadding_ShadowDepthPass_LPV_176; + float PrePadding_ShadowDepthPass_LPV_180; + float PrePadding_ShadowDepthPass_LPV_184; + float PrePadding_ShadowDepthPass_LPV_188; + float PrePadding_ShadowDepthPass_LPV_192; + float PrePadding_ShadowDepthPass_LPV_196; + float PrePadding_ShadowDepthPass_LPV_200; + float PrePadding_ShadowDepthPass_LPV_204; + float PrePadding_ShadowDepthPass_LPV_208; + float PrePadding_ShadowDepthPass_LPV_212; + float PrePadding_ShadowDepthPass_LPV_216; + float PrePadding_ShadowDepthPass_LPV_220; + float PrePadding_ShadowDepthPass_LPV_224; + float PrePadding_ShadowDepthPass_LPV_228; + float PrePadding_ShadowDepthPass_LPV_232; + float PrePadding_ShadowDepthPass_LPV_236; + float PrePadding_ShadowDepthPass_LPV_240; + float PrePadding_ShadowDepthPass_LPV_244; + float PrePadding_ShadowDepthPass_LPV_248; + float PrePadding_ShadowDepthPass_LPV_252; + float PrePadding_ShadowDepthPass_LPV_256; + float PrePadding_ShadowDepthPass_LPV_260; + float PrePadding_ShadowDepthPass_LPV_264; + float PrePadding_ShadowDepthPass_LPV_268; + float4x4 ShadowDepthPass_LPV_mRsmToWorld; + float4 ShadowDepthPass_LPV_mLightColour; + float4 ShadowDepthPass_LPV_GeometryVolumeCaptureLightDirection; + float4 ShadowDepthPass_LPV_mEyePos; + packed_int3 ShadowDepthPass_LPV_mOldGridOffset; + int PrePadding_ShadowDepthPass_LPV_396; + packed_int3 ShadowDepthPass_LPV_mLpvGridOffset; + float ShadowDepthPass_LPV_ClearMultiplier; + float ShadowDepthPass_LPV_LpvScale; + float ShadowDepthPass_LPV_OneOverLpvScale; + float ShadowDepthPass_LPV_DirectionalOcclusionIntensity; + float ShadowDepthPass_LPV_DirectionalOcclusionRadius; + float ShadowDepthPass_LPV_RsmAreaIntensityMultiplier; + float ShadowDepthPass_LPV_RsmPixelToTexcoordMultiplier; + float ShadowDepthPass_LPV_SecondaryOcclusionStrength; + float ShadowDepthPass_LPV_SecondaryBounceStrength; + float ShadowDepthPass_LPV_VplInjectionBias; + float ShadowDepthPass_LPV_GeometryVolumeInjectionBias; + float ShadowDepthPass_LPV_EmissiveInjectionMultiplier; + int ShadowDepthPass_LPV_PropagationIndex; + float4x4 ShadowDepthPass_ProjectionMatrix; + float4x4 ShadowDepthPass_ViewMatrix; + float4 ShadowDepthPass_ShadowParams; + float ShadowDepthPass_bClampToNearPlane; + float PrePadding_ShadowDepthPass_612; + float PrePadding_ShadowDepthPass_616; + float PrePadding_ShadowDepthPass_620; + float4x4 ShadowDepthPass_ShadowViewProjectionMatrices[6]; + float4x4 ShadowDepthPass_ShadowViewMatrices[6]; +}; + +constant float4 _113 = {}; + +struct main0_out +{ + float4 out_var_TEXCOORD10_centroid [[user(locn0)]]; + float4 out_var_TEXCOORD11_centroid [[user(locn1)]]; + float4 out_var_COLOR0 [[user(locn2)]]; + float4 out_var_TEXCOORD0_0 [[user(locn3)]]; + uint out_var_PRIMITIVE_ID [[user(locn4)]]; + float out_var_TEXCOORD6 [[user(locn5)]]; + float out_var_TEXCOORD8 [[user(locn6)]]; + float3 out_var_TEXCOORD7 [[user(locn7)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_COLOR0 [[attribute(0)]]; + float4 in_var_PN_POSITION_0 [[attribute(2)]]; + float4 in_var_PN_POSITION_1 [[attribute(3)]]; + float4 in_var_PN_POSITION_2 [[attribute(4)]]; + float in_var_PN_WorldDisplacementMultiplier [[attribute(7)]]; + uint in_var_PRIMITIVE_ID [[attribute(8)]]; + float4 in_var_TEXCOORD0_0 [[attribute(9)]]; + float4 in_var_TEXCOORD10_centroid [[attribute(10)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(11)]]; +}; + +struct main0_patchIn +{ + float4 in_var_PN_POSITION9 [[attribute(5)]]; + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant type_View& View [[buffer(0)]], constant type_ShadowDepthPass& ShadowDepthPass [[buffer(1)]], texture2d Material_Texture2D_3 [[texture(0)]], sampler Material_Texture2D_3Sampler [[sampler(0)]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray out_var_TEXCOORD0 = {}; + spvUnsafeArray _117 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD10_centroid, patchIn.gl_in[1].in_var_TEXCOORD10_centroid, patchIn.gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _118 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD11_centroid, patchIn.gl_in[1].in_var_TEXCOORD11_centroid, patchIn.gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray _119 = spvUnsafeArray({ patchIn.gl_in[0].in_var_COLOR0, patchIn.gl_in[1].in_var_COLOR0, patchIn.gl_in[2].in_var_COLOR0 }); + spvUnsafeArray, 3> _120 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD0_0 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_TEXCOORD0_0 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_TEXCOORD0_0 }) }); + spvUnsafeArray, 3> _135 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_POSITION_0, patchIn.gl_in[0].in_var_PN_POSITION_1, patchIn.gl_in[0].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_PN_POSITION_0, patchIn.gl_in[1].in_var_PN_POSITION_1, patchIn.gl_in[1].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_PN_POSITION_0, patchIn.gl_in[2].in_var_PN_POSITION_1, patchIn.gl_in[2].in_var_PN_POSITION_2 }) }); + spvUnsafeArray _136 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_WorldDisplacementMultiplier, patchIn.gl_in[1].in_var_PN_WorldDisplacementMultiplier, patchIn.gl_in[2].in_var_PN_WorldDisplacementMultiplier }); + float _157 = gl_TessCoord.x * gl_TessCoord.x; + float _158 = gl_TessCoord.y * gl_TessCoord.y; + float _159 = gl_TessCoord.z * gl_TessCoord.z; + float4 _165 = float4(gl_TessCoord.x); + float4 _169 = float4(gl_TessCoord.y); + float4 _174 = float4(gl_TessCoord.z); + float4 _177 = float4(_157 * 3.0); + float4 _181 = float4(_158 * 3.0); + float4 _188 = float4(_159 * 3.0); + float4 _202 = ((((((((((_135[0][0] * float4(_157)) * _165) + ((_135[1][0] * float4(_158)) * _169)) + ((_135[2][0] * float4(_159)) * _174)) + ((_135[0][1] * _177) * _169)) + ((_135[0][2] * _181) * _165)) + ((_135[1][1] * _181) * _174)) + ((_135[1][2] * _188) * _169)) + ((_135[2][1] * _188) * _165)) + ((_135[2][2] * _177) * _174)) + ((((patchIn.in_var_PN_POSITION9 * float4(6.0)) * _174) * _165) * _169); + float3 _226 = ((_117[0].xyz * float3(gl_TessCoord.x)) + (_117[1].xyz * float3(gl_TessCoord.y))).xyz + (_117[2].xyz * float3(gl_TessCoord.z)); + float4 _229 = ((_118[0] * _165) + (_118[1] * _169)) + (_118[2] * _174); + float4 _231 = ((_119[0] * _165) + (_119[1] * _169)) + (_119[2] * _174); + float4 _233 = ((_120[0][0] * _165) + (_120[1][0] * _169)) + (_120[2][0] * _174); + spvUnsafeArray _234 = spvUnsafeArray({ _233 }); + float3 _236 = _229.xyz; + float3 _264 = _202.xyz + (((float3((Material_Texture2D_3.sample(Material_Texture2D_3Sampler, (float2(View.View_GameTime * 0.20000000298023223876953125, View.View_GameTime * (-0.699999988079071044921875)) + (_233.zw * float2(1.0, 2.0))), level(-1.0)).x * 10.0) * (1.0 - _231.x)) * _236) * float3(0.5)) * float3(((_136[0] * gl_TessCoord.x) + (_136[1] * gl_TessCoord.y)) + (_136[2] * gl_TessCoord.z))); + float4x4 _116 = ShadowDepthPass.ShadowDepthPass_ViewMatrix; + float4 _270 = ShadowDepthPass.ShadowDepthPass_ProjectionMatrix * float4(_264.x, _264.y, _264.z, _202.w); + float4 _281; + if ((ShadowDepthPass.ShadowDepthPass_bClampToNearPlane > 0.0) && (_270.z < 0.0)) + { + float4 _279 = _270; + _279.z = 9.9999999747524270787835121154785e-07; + float4 _280 = _279; + _280.w = 1.0; + _281 = _280; + } + else + { + _281 = _270; + } + float _290 = abs(dot(float3(_116[0u].z, _116[1u].z, _116[2u].z), _236)); + out.out_var_TEXCOORD10_centroid = float4(_226.x, _226.y, _226.z, _113.w); + out.out_var_TEXCOORD11_centroid = _229; + out.out_var_COLOR0 = _231; + out_var_TEXCOORD0 = _234; + out.out_var_PRIMITIVE_ID = patchIn.gl_in[0u].in_var_PRIMITIVE_ID; + out.out_var_TEXCOORD6 = _281.z; + out.out_var_TEXCOORD8 = (ShadowDepthPass.ShadowDepthPass_ShadowParams.y * fast::clamp((abs(_290) > 0.0) ? (sqrt(fast::clamp(1.0 - (_290 * _290), 0.0, 1.0)) / _290) : ShadowDepthPass.ShadowDepthPass_ShadowParams.z, 0.0, ShadowDepthPass.ShadowDepthPass_ShadowParams.z)) + ShadowDepthPass.ShadowDepthPass_ShadowParams.x; + out.out_var_TEXCOORD7 = _264.xyz; + out.gl_Position = _281; + out.out_var_TEXCOORD0_0 = out_var_TEXCOORD0[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese b/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese new file mode 100644 index 0000000..738b073 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese @@ -0,0 +1,417 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; + float PrePadding_View_3048; + float PrePadding_View_3052; + float4x4 View_WorldToVirtualTexture; + float4 View_VirtualTextureParams; + float4 View_XRPassthroughCameraUVs[2]; +}; + +struct type_Material +{ + float4 Material_VectorExpressions[5]; + float4 Material_ScalarExpressions[2]; +}; + +constant float4 _118 = {}; + +struct main0_out +{ + float4 out_var_TEXCOORD6 [[user(locn0)]]; + float4 out_var_TEXCOORD7 [[user(locn1)]]; + float4 out_var_TEXCOORD10_centroid [[user(locn2)]]; + float4 out_var_TEXCOORD11_centroid [[user(locn3)]]; + float gl_ClipDistance [[clip_distance]] [1]; + float gl_ClipDistance_0 [[user(clip0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_PN_DominantEdge2 [[attribute(3)]]; + float4 in_var_PN_DominantEdge3 [[attribute(4)]]; + float3 in_var_PN_DominantEdge4 [[attribute(5)]]; + float3 in_var_PN_DominantEdge5 [[attribute(6)]]; + float4 in_var_PN_DominantVertex1 [[attribute(8)]]; + float3 in_var_PN_DominantVertex2 [[attribute(9)]]; + float4 in_var_PN_POSITION_0 [[attribute(10)]]; + float4 in_var_PN_POSITION_1 [[attribute(11)]]; + float4 in_var_PN_POSITION_2 [[attribute(12)]]; + float in_var_PN_WorldDisplacementMultiplier [[attribute(15)]]; + float4 in_var_TEXCOORD10_centroid [[attribute(16)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(17)]]; + float4 in_var_TEXCOORD6 [[attribute(18)]]; + float4 in_var_TEXCOORD8 [[attribute(19)]]; +}; + +struct main0_patchIn +{ + float4 in_var_PN_POSITION9 [[attribute(13)]]; + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant type_View& View [[buffer(0)]], constant type_Material& Material [[buffer(1)]], texture3d View_GlobalDistanceFieldTexture0 [[texture(0)]], texture3d View_GlobalDistanceFieldTexture1 [[texture(1)]], texture3d View_GlobalDistanceFieldTexture2 [[texture(2)]], texture3d View_GlobalDistanceFieldTexture3 [[texture(3)]], sampler View_GlobalDistanceFieldSampler0 [[sampler(0)]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray _120 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD6, patchIn.gl_in[1].in_var_TEXCOORD6, patchIn.gl_in[2].in_var_TEXCOORD6 }); + spvUnsafeArray _121 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD8, patchIn.gl_in[1].in_var_TEXCOORD8, patchIn.gl_in[2].in_var_TEXCOORD8 }); + spvUnsafeArray _128 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD10_centroid, patchIn.gl_in[1].in_var_TEXCOORD10_centroid, patchIn.gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _129 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD11_centroid, patchIn.gl_in[1].in_var_TEXCOORD11_centroid, patchIn.gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray, 3> _136 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_POSITION_0, patchIn.gl_in[0].in_var_PN_POSITION_1, patchIn.gl_in[0].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_PN_POSITION_0, patchIn.gl_in[1].in_var_PN_POSITION_1, patchIn.gl_in[1].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_PN_POSITION_0, patchIn.gl_in[2].in_var_PN_POSITION_1, patchIn.gl_in[2].in_var_PN_POSITION_2 }) }); + spvUnsafeArray _137 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_WorldDisplacementMultiplier, patchIn.gl_in[1].in_var_PN_WorldDisplacementMultiplier, patchIn.gl_in[2].in_var_PN_WorldDisplacementMultiplier }); + spvUnsafeArray _138 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantVertex1, patchIn.gl_in[1].in_var_PN_DominantVertex1, patchIn.gl_in[2].in_var_PN_DominantVertex1 }); + spvUnsafeArray _139 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantVertex2, patchIn.gl_in[1].in_var_PN_DominantVertex2, patchIn.gl_in[2].in_var_PN_DominantVertex2 }); + spvUnsafeArray _146 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantEdge2, patchIn.gl_in[1].in_var_PN_DominantEdge2, patchIn.gl_in[2].in_var_PN_DominantEdge2 }); + spvUnsafeArray _147 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantEdge3, patchIn.gl_in[1].in_var_PN_DominantEdge3, patchIn.gl_in[2].in_var_PN_DominantEdge3 }); + spvUnsafeArray _148 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantEdge4, patchIn.gl_in[1].in_var_PN_DominantEdge4, patchIn.gl_in[2].in_var_PN_DominantEdge4 }); + spvUnsafeArray _149 = spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_DominantEdge5, patchIn.gl_in[1].in_var_PN_DominantEdge5, patchIn.gl_in[2].in_var_PN_DominantEdge5 }); + float _190 = gl_TessCoord.x * gl_TessCoord.x; + float _191 = gl_TessCoord.y * gl_TessCoord.y; + float _192 = gl_TessCoord.z * gl_TessCoord.z; + float4 _198 = float4(gl_TessCoord.x); + float4 _202 = float4(gl_TessCoord.y); + float4 _207 = float4(gl_TessCoord.z); + float4 _210 = float4(_190 * 3.0); + float4 _214 = float4(_191 * 3.0); + float4 _221 = float4(_192 * 3.0); + float4 _235 = ((((((((((_136[0][0] * float4(_190)) * _198) + ((_136[1][0] * float4(_191)) * _202)) + ((_136[2][0] * float4(_192)) * _207)) + ((_136[0][1] * _210) * _202)) + ((_136[0][2] * _214) * _198)) + ((_136[1][1] * _214) * _207)) + ((_136[1][2] * _221) * _202)) + ((_136[2][1] * _221) * _198)) + ((_136[2][2] * _210) * _207)) + ((((patchIn.in_var_PN_POSITION9 * float4(6.0)) * _207) * _198) * _202); + float3 _237 = float3(gl_TessCoord.x); + float3 _240 = float3(gl_TessCoord.y); + float3 _254 = float3(gl_TessCoord.z); + float3 _256 = ((_128[0].xyz * _237) + (_128[1].xyz * _240)).xyz + (_128[2].xyz * _254); + float4 _259 = ((_129[0] * _198) + (_129[1] * _202)) + (_129[2] * _207); + float3 _264 = _235.xyz; + float3 _265 = _256.xyz; + float3 _266 = _259.xyz; + float3 _272 = _264 + float3(View.View_WorldCameraOrigin); + float _279 = float(int(gl_TessCoord.x == 0.0)); + float _282 = float(int(gl_TessCoord.y == 0.0)); + float _285 = float(int(gl_TessCoord.z == 0.0)); + float _286 = _279 + _282; + float _287 = _286 + _285; + float4 _387; + float3 _388; + if (float(int(_287 == 2.0)) == 1.0) + { + float _363 = float(int((_282 + _285) == 2.0)); + float _367 = float(int((_285 + _279) == 2.0)); + float _370 = float(int(_286 == 2.0)); + _387 = ((float4(_363) * _138[0]) + (float4(_367) * _138[1])) + (float4(_370) * _138[2]); + _388 = ((float3(_363) * _139[0]) + (float3(_367) * _139[1])) + (float3(_370) * _139[2]); + } + else + { + float4 _358; + float3 _359; + if (float(int(_287 == 1.0)) != 0.0) + { + float4 _304 = float4(_279); + float4 _306 = float4(_282); + float4 _309 = float4(_285); + float4 _311 = ((_304 * _146[0]) + (_306 * _146[1])) + (_309 * _146[2]); + float4 _316 = ((_304 * _147[0]) + (_306 * _147[1])) + (_309 * _147[2]); + float3 _331 = float3(_279); + float3 _333 = float3(_282); + float3 _336 = float3(_285); + float3 _338 = ((_331 * _148[0]) + (_333 * _148[1])) + (_336 * _148[2]); + float3 _343 = ((_331 * _149[0]) + (_333 * _149[1])) + (_336 * _149[2]); + _358 = ((_304 * ((_202 * _311) + (_207 * _316))) + (_306 * ((_207 * _311) + (_198 * _316)))) + (_309 * ((_198 * _311) + (_202 * _316))); + _359 = ((_331 * ((_240 * _338) + (_254 * _343))) + (_333 * ((_254 * _338) + (_237 * _343)))) + (_336 * ((_237 * _338) + (_240 * _343))); + } + else + { + _358 = float4(_259.xyz, 0.0); + _359 = _265; + } + _387 = _358; + _388 = _359; + } + float3x3 _398; + if (float(int(_287 == 0.0)) == 0.0) + { + _398 = float3x3(_388, cross(_387.xyz, _388) * float3(_387.w), _387.xyz); + } + else + { + _398 = float3x3(_265, cross(_266, _265) * float3(_259.w), _266); + } + float3 _411 = fast::min(fast::max((_272 - View.View_GlobalVolumeCenterAndExtent[0].xyz) + View.View_GlobalVolumeCenterAndExtent[0].www, float3(0.0)), fast::max((View.View_GlobalVolumeCenterAndExtent[0].xyz + View.View_GlobalVolumeCenterAndExtent[0].www) - _272, float3(0.0))); + float _547; + if (fast::min(_411.x, fast::min(_411.y, _411.z)) > (View.View_GlobalVolumeCenterAndExtent[0].w * View.View_GlobalVolumeTexelSize)) + { + _547 = View_GlobalDistanceFieldTexture0.sample(View_GlobalDistanceFieldSampler0, ((_272 * View.View_GlobalVolumeWorldToUVAddAndMul[0u].www) + View.View_GlobalVolumeWorldToUVAddAndMul[0u].xyz), level(0.0)).x; + } + else + { + float3 _436 = fast::min(fast::max((_272 - View.View_GlobalVolumeCenterAndExtent[1].xyz) + View.View_GlobalVolumeCenterAndExtent[1].www, float3(0.0)), fast::max((View.View_GlobalVolumeCenterAndExtent[1].xyz + View.View_GlobalVolumeCenterAndExtent[1].www) - _272, float3(0.0))); + float _535; + if (fast::min(_436.x, fast::min(_436.y, _436.z)) > (View.View_GlobalVolumeCenterAndExtent[1].w * View.View_GlobalVolumeTexelSize)) + { + _535 = View_GlobalDistanceFieldTexture1.sample(View_GlobalDistanceFieldSampler0, ((_272 * View.View_GlobalVolumeWorldToUVAddAndMul[1u].www) + View.View_GlobalVolumeWorldToUVAddAndMul[1u].xyz), level(0.0)).x; + } + else + { + float3 _459 = fast::min(fast::max((_272 - View.View_GlobalVolumeCenterAndExtent[2].xyz) + View.View_GlobalVolumeCenterAndExtent[2].www, float3(0.0)), fast::max((View.View_GlobalVolumeCenterAndExtent[2].xyz + View.View_GlobalVolumeCenterAndExtent[2].www) - _272, float3(0.0))); + float3 _475 = fast::min(fast::max((_272 - View.View_GlobalVolumeCenterAndExtent[3].xyz) + View.View_GlobalVolumeCenterAndExtent[3].www, float3(0.0)), fast::max((View.View_GlobalVolumeCenterAndExtent[3].xyz + View.View_GlobalVolumeCenterAndExtent[3].www) - _272, float3(0.0))); + float _480 = fast::min(_475.x, fast::min(_475.y, _475.z)); + float _523; + if (fast::min(_459.x, fast::min(_459.y, _459.z)) > (View.View_GlobalVolumeCenterAndExtent[2].w * View.View_GlobalVolumeTexelSize)) + { + _523 = View_GlobalDistanceFieldTexture2.sample(View_GlobalDistanceFieldSampler0, ((_272 * View.View_GlobalVolumeWorldToUVAddAndMul[2u].www) + View.View_GlobalVolumeWorldToUVAddAndMul[2u].xyz), level(0.0)).x; + } + else + { + float _511; + if (_480 > (View.View_GlobalVolumeCenterAndExtent[3].w * View.View_GlobalVolumeTexelSize)) + { + _511 = mix(View.View_MaxGlobalDistance, View_GlobalDistanceFieldTexture3.sample(View_GlobalDistanceFieldSampler0, ((_272 * View.View_GlobalVolumeWorldToUVAddAndMul[3u].www) + View.View_GlobalVolumeWorldToUVAddAndMul[3u].xyz), level(0.0)).x, fast::clamp((_480 * 10.0) * View.View_GlobalVolumeWorldToUVAddAndMul[3].w, 0.0, 1.0)); + } + else + { + _511 = View.View_MaxGlobalDistance; + } + _523 = _511; + } + _535 = _523; + } + _547 = _535; + } + float3 _565 = _264 + ((_398[2] * float3(fast::min(_547 + Material.Material_ScalarExpressions[0].z, 0.0) * Material.Material_ScalarExpressions[0].w)) * float3(((_137[0] * gl_TessCoord.x) + (_137[1] * gl_TessCoord.y)) + (_137[2] * gl_TessCoord.z))); + float4 _574 = View.View_TranslatedWorldToClip * float4(_565.x, _565.y, _565.z, _235.w); + float4 _579 = _574; + _579.z = _574.z + (0.001000000047497451305389404296875 * _574.w); + out.gl_Position = _579; + out.out_var_TEXCOORD6 = ((_120[0] * _198) + (_120[1] * _202)) + (_120[2] * _207); + out.out_var_TEXCOORD7 = ((_121[0] * _198) + (_121[1] * _202)) + (_121[2] * _207); + out.out_var_TEXCOORD10_centroid = float4(_256.x, _256.y, _256.z, _118.w); + out.out_var_TEXCOORD11_centroid = _259; + out.gl_ClipDistance[0u] = dot(View.View_GlobalClippingPlane, float4(_565.xyz - float3(View.View_PreViewTranslation), 1.0)); + out.gl_ClipDistance_0 = out.gl_ClipDistance[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese b/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese new file mode 100644 index 0000000..d0b6fbd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese @@ -0,0 +1,217 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_ShadowDepthPass +{ + float PrePadding_ShadowDepthPass_LPV_0; + float PrePadding_ShadowDepthPass_LPV_4; + float PrePadding_ShadowDepthPass_LPV_8; + float PrePadding_ShadowDepthPass_LPV_12; + float PrePadding_ShadowDepthPass_LPV_16; + float PrePadding_ShadowDepthPass_LPV_20; + float PrePadding_ShadowDepthPass_LPV_24; + float PrePadding_ShadowDepthPass_LPV_28; + float PrePadding_ShadowDepthPass_LPV_32; + float PrePadding_ShadowDepthPass_LPV_36; + float PrePadding_ShadowDepthPass_LPV_40; + float PrePadding_ShadowDepthPass_LPV_44; + float PrePadding_ShadowDepthPass_LPV_48; + float PrePadding_ShadowDepthPass_LPV_52; + float PrePadding_ShadowDepthPass_LPV_56; + float PrePadding_ShadowDepthPass_LPV_60; + float PrePadding_ShadowDepthPass_LPV_64; + float PrePadding_ShadowDepthPass_LPV_68; + float PrePadding_ShadowDepthPass_LPV_72; + float PrePadding_ShadowDepthPass_LPV_76; + float PrePadding_ShadowDepthPass_LPV_80; + float PrePadding_ShadowDepthPass_LPV_84; + float PrePadding_ShadowDepthPass_LPV_88; + float PrePadding_ShadowDepthPass_LPV_92; + float PrePadding_ShadowDepthPass_LPV_96; + float PrePadding_ShadowDepthPass_LPV_100; + float PrePadding_ShadowDepthPass_LPV_104; + float PrePadding_ShadowDepthPass_LPV_108; + float PrePadding_ShadowDepthPass_LPV_112; + float PrePadding_ShadowDepthPass_LPV_116; + float PrePadding_ShadowDepthPass_LPV_120; + float PrePadding_ShadowDepthPass_LPV_124; + float PrePadding_ShadowDepthPass_LPV_128; + float PrePadding_ShadowDepthPass_LPV_132; + float PrePadding_ShadowDepthPass_LPV_136; + float PrePadding_ShadowDepthPass_LPV_140; + float PrePadding_ShadowDepthPass_LPV_144; + float PrePadding_ShadowDepthPass_LPV_148; + float PrePadding_ShadowDepthPass_LPV_152; + float PrePadding_ShadowDepthPass_LPV_156; + float PrePadding_ShadowDepthPass_LPV_160; + float PrePadding_ShadowDepthPass_LPV_164; + float PrePadding_ShadowDepthPass_LPV_168; + float PrePadding_ShadowDepthPass_LPV_172; + float PrePadding_ShadowDepthPass_LPV_176; + float PrePadding_ShadowDepthPass_LPV_180; + float PrePadding_ShadowDepthPass_LPV_184; + float PrePadding_ShadowDepthPass_LPV_188; + float PrePadding_ShadowDepthPass_LPV_192; + float PrePadding_ShadowDepthPass_LPV_196; + float PrePadding_ShadowDepthPass_LPV_200; + float PrePadding_ShadowDepthPass_LPV_204; + float PrePadding_ShadowDepthPass_LPV_208; + float PrePadding_ShadowDepthPass_LPV_212; + float PrePadding_ShadowDepthPass_LPV_216; + float PrePadding_ShadowDepthPass_LPV_220; + float PrePadding_ShadowDepthPass_LPV_224; + float PrePadding_ShadowDepthPass_LPV_228; + float PrePadding_ShadowDepthPass_LPV_232; + float PrePadding_ShadowDepthPass_LPV_236; + float PrePadding_ShadowDepthPass_LPV_240; + float PrePadding_ShadowDepthPass_LPV_244; + float PrePadding_ShadowDepthPass_LPV_248; + float PrePadding_ShadowDepthPass_LPV_252; + float PrePadding_ShadowDepthPass_LPV_256; + float PrePadding_ShadowDepthPass_LPV_260; + float PrePadding_ShadowDepthPass_LPV_264; + float PrePadding_ShadowDepthPass_LPV_268; + float4x4 ShadowDepthPass_LPV_mRsmToWorld; + float4 ShadowDepthPass_LPV_mLightColour; + float4 ShadowDepthPass_LPV_GeometryVolumeCaptureLightDirection; + float4 ShadowDepthPass_LPV_mEyePos; + packed_int3 ShadowDepthPass_LPV_mOldGridOffset; + int PrePadding_ShadowDepthPass_LPV_396; + packed_int3 ShadowDepthPass_LPV_mLpvGridOffset; + float ShadowDepthPass_LPV_ClearMultiplier; + float ShadowDepthPass_LPV_LpvScale; + float ShadowDepthPass_LPV_OneOverLpvScale; + float ShadowDepthPass_LPV_DirectionalOcclusionIntensity; + float ShadowDepthPass_LPV_DirectionalOcclusionRadius; + float ShadowDepthPass_LPV_RsmAreaIntensityMultiplier; + float ShadowDepthPass_LPV_RsmPixelToTexcoordMultiplier; + float ShadowDepthPass_LPV_SecondaryOcclusionStrength; + float ShadowDepthPass_LPV_SecondaryBounceStrength; + float ShadowDepthPass_LPV_VplInjectionBias; + float ShadowDepthPass_LPV_GeometryVolumeInjectionBias; + float ShadowDepthPass_LPV_EmissiveInjectionMultiplier; + int ShadowDepthPass_LPV_PropagationIndex; + float4x4 ShadowDepthPass_ProjectionMatrix; + float4x4 ShadowDepthPass_ViewMatrix; + float4 ShadowDepthPass_ShadowParams; + float ShadowDepthPass_bClampToNearPlane; + float PrePadding_ShadowDepthPass_612; + float PrePadding_ShadowDepthPass_616; + float PrePadding_ShadowDepthPass_620; + float4x4 ShadowDepthPass_ShadowViewProjectionMatrices[6]; + float4x4 ShadowDepthPass_ShadowViewMatrices[6]; +}; + +constant float4 _90 = {}; + +struct main0_out +{ + float4 out_var_TEXCOORD10_centroid [[user(locn0)]]; + float4 out_var_TEXCOORD11_centroid [[user(locn1)]]; + float out_var_TEXCOORD6 [[user(locn2)]]; + float3 out_var_TEXCOORD7 [[user(locn3)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_PN_POSITION_0 [[attribute(10)]]; + float4 in_var_PN_POSITION_1 [[attribute(11)]]; + float4 in_var_PN_POSITION_2 [[attribute(12)]]; + float4 in_var_TEXCOORD10_centroid [[attribute(16)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(17)]]; +}; + +struct main0_patchIn +{ + float4 in_var_PN_POSITION9 [[attribute(13)]]; + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant type_ShadowDepthPass& ShadowDepthPass [[buffer(0)]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray _93 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD10_centroid, patchIn.gl_in[1].in_var_TEXCOORD10_centroid, patchIn.gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _94 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD11_centroid, patchIn.gl_in[1].in_var_TEXCOORD11_centroid, patchIn.gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray, 3> _101 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_PN_POSITION_0, patchIn.gl_in[0].in_var_PN_POSITION_1, patchIn.gl_in[0].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_PN_POSITION_0, patchIn.gl_in[1].in_var_PN_POSITION_1, patchIn.gl_in[1].in_var_PN_POSITION_2 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_PN_POSITION_0, patchIn.gl_in[2].in_var_PN_POSITION_1, patchIn.gl_in[2].in_var_PN_POSITION_2 }) }); + float _119 = gl_TessCoord.x * gl_TessCoord.x; + float _120 = gl_TessCoord.y * gl_TessCoord.y; + float _121 = gl_TessCoord.z * gl_TessCoord.z; + float4 _127 = float4(gl_TessCoord.x); + float4 _131 = float4(gl_TessCoord.y); + float4 _136 = float4(gl_TessCoord.z); + float4 _139 = float4(_119 * 3.0); + float4 _143 = float4(_120 * 3.0); + float4 _150 = float4(_121 * 3.0); + float4 _164 = ((((((((((_101[0][0] * float4(_119)) * _127) + ((_101[1][0] * float4(_120)) * _131)) + ((_101[2][0] * float4(_121)) * _136)) + ((_101[0][1] * _139) * _131)) + ((_101[0][2] * _143) * _127)) + ((_101[1][1] * _143) * _136)) + ((_101[1][2] * _150) * _131)) + ((_101[2][1] * _150) * _127)) + ((_101[2][2] * _139) * _136)) + ((((patchIn.in_var_PN_POSITION9 * float4(6.0)) * _136) * _127) * _131); + float3 _179 = ((_93[0].xyz * float3(gl_TessCoord.x)) + (_93[1].xyz * float3(gl_TessCoord.y))).xyz + (_93[2].xyz * float3(gl_TessCoord.z)); + float4 _182 = ((_94[0] * _127) + (_94[1] * _131)) + (_94[2] * _136); + float4x4 _92 = ShadowDepthPass.ShadowDepthPass_ViewMatrix; + float4 _189 = ShadowDepthPass.ShadowDepthPass_ProjectionMatrix * float4(_164.x, _164.y, _164.z, _164.w); + float4 _200; + if ((ShadowDepthPass.ShadowDepthPass_bClampToNearPlane > 0.0) && (_189.z < 0.0)) + { + float4 _198 = _189; + _198.z = 9.9999999747524270787835121154785e-07; + float4 _199 = _198; + _199.w = 1.0; + _200 = _199; + } + else + { + _200 = _189; + } + float _209 = abs(dot(float3(_92[0u].z, _92[1u].z, _92[2u].z), _182.xyz)); + float4 _234 = _200; + _234.z = ((_200.z * ShadowDepthPass.ShadowDepthPass_ShadowParams.w) + ((ShadowDepthPass.ShadowDepthPass_ShadowParams.y * fast::clamp((abs(_209) > 0.0) ? (sqrt(fast::clamp(1.0 - (_209 * _209), 0.0, 1.0)) / _209) : ShadowDepthPass.ShadowDepthPass_ShadowParams.z, 0.0, ShadowDepthPass.ShadowDepthPass_ShadowParams.z)) + ShadowDepthPass.ShadowDepthPass_ShadowParams.x)) * _200.w; + out.out_var_TEXCOORD10_centroid = float4(_179.x, _179.y, _179.z, _90.w); + out.out_var_TEXCOORD11_centroid = _182; + out.out_var_TEXCOORD6 = 0.0; + out.out_var_TEXCOORD7 = _164.xyz; + out.gl_Position = _234; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese b/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese new file mode 100644 index 0000000..346d7e3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese @@ -0,0 +1,318 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_ClipToWorld; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_908; + packed_float3 View_ViewUp; + float PrePadding_View_924; + packed_float3 View_ViewRight; + float PrePadding_View_940; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_956; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_972; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_1020; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_1036; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_1052; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1068; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1724; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1740; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1756; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2076; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2148; + float PrePadding_View_2152; + float PrePadding_View_2156; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2228; + float PrePadding_View_2232; + float PrePadding_View_2236; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2268; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2412; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + float View_AtmosphericFogSunDiscHalfApexAngleRadian; + float PrePadding_View_2492; + float4 View_AtmosphericFogSunDiscLuminance; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + uint PrePadding_View_2520; + uint PrePadding_View_2524; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2584; + float PrePadding_View_2588; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2744; + float PrePadding_View_2748; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float PrePadding_View_2908; + int2 View_CursorPosition; + float View_bCheckerboardSubsurfaceProfileRendering; + float PrePadding_View_2924; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2940; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2956; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2972; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2988; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_3004; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; + float PrePadding_View_3048; + float PrePadding_View_3052; + float4x4 View_WorldToVirtualTexture; + float4 View_VirtualTextureParams; + float4 View_XRPassthroughCameraUVs[2]; +}; + +constant float4 _68 = {}; + +struct main0_out +{ + float4 out_var_TEXCOORD10_centroid [[user(locn0)]]; + float4 out_var_TEXCOORD11_centroid [[user(locn1)]]; + float4 out_var_TEXCOORD0_0 [[user(locn2)]]; + float4 out_var_COLOR1 [[user(locn3)]]; + float4 out_var_COLOR2 [[user(locn4)]]; + float4 out_var_TEXCOORD6 [[user(locn5)]]; + float3 out_var_TEXCOORD7 [[user(locn6)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_COLOR1 [[attribute(0)]]; + float4 in_var_COLOR2 [[attribute(1)]]; + float4 in_var_TEXCOORD0_0 [[attribute(5)]]; + float4 in_var_TEXCOORD10_centroid [[attribute(6)]]; + float4 in_var_TEXCOORD11_centroid [[attribute(7)]]; + float3 in_var_TEXCOORD7 [[attribute(8)]]; + float4 in_var_VS_To_DS_Position [[attribute(9)]]; +}; + +struct main0_patchIn +{ + patch_control_point gl_in; +}; + +[[ patch(triangle, 0) ]] vertex main0_out main0(main0_patchIn patchIn [[stage_in]], constant type_View& View [[buffer(0)]], float3 gl_TessCoord [[position_in_patch]]) +{ + main0_out out = {}; + spvUnsafeArray out_var_TEXCOORD0 = {}; + spvUnsafeArray _77 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD10_centroid, patchIn.gl_in[1].in_var_TEXCOORD10_centroid, patchIn.gl_in[2].in_var_TEXCOORD10_centroid }); + spvUnsafeArray _78 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD11_centroid, patchIn.gl_in[1].in_var_TEXCOORD11_centroid, patchIn.gl_in[2].in_var_TEXCOORD11_centroid }); + spvUnsafeArray, 3> _79 = spvUnsafeArray, 3>({ spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD0_0 }), spvUnsafeArray({ patchIn.gl_in[1].in_var_TEXCOORD0_0 }), spvUnsafeArray({ patchIn.gl_in[2].in_var_TEXCOORD0_0 }) }); + spvUnsafeArray _80 = spvUnsafeArray({ patchIn.gl_in[0].in_var_COLOR1, patchIn.gl_in[1].in_var_COLOR1, patchIn.gl_in[2].in_var_COLOR1 }); + spvUnsafeArray _81 = spvUnsafeArray({ patchIn.gl_in[0].in_var_COLOR2, patchIn.gl_in[1].in_var_COLOR2, patchIn.gl_in[2].in_var_COLOR2 }); + spvUnsafeArray _97 = spvUnsafeArray({ patchIn.gl_in[0].in_var_VS_To_DS_Position, patchIn.gl_in[1].in_var_VS_To_DS_Position, patchIn.gl_in[2].in_var_VS_To_DS_Position }); + spvUnsafeArray _98 = spvUnsafeArray({ patchIn.gl_in[0].in_var_TEXCOORD7, patchIn.gl_in[1].in_var_TEXCOORD7, patchIn.gl_in[2].in_var_TEXCOORD7 }); + float4 _111 = float4(gl_TessCoord.x); + float4 _113 = float4(gl_TessCoord.y); + float4 _116 = float4(gl_TessCoord.z); + float4 _118 = ((_97[0] * _111) + (_97[1] * _113)) + (_97[2] * _116); + spvUnsafeArray _72; + _72 = _79[0]; + spvUnsafeArray _71; + _71 = _79[1]; + float3 _120 = float3(gl_TessCoord.x); + float3 _123 = float3(gl_TessCoord.y); + spvUnsafeArray _73; + for (int _133 = 0; _133 < 1; ) + { + _73[_133] = (_72[_133] * _111) + (_71[_133] * _113); + _133++; + continue; + } + spvUnsafeArray _75; + _75 = _73; + spvUnsafeArray _74; + _74 = _79[2]; + float3 _155 = float3(gl_TessCoord.z); + float3 _157 = ((_77[0].xyz * _120) + (_77[1].xyz * _123)).xyz + (_77[2].xyz * _155); + spvUnsafeArray _76; + for (int _164 = 0; _164 < 1; ) + { + _76[_164] = _75[_164] + (_74[_164] * _116); + _164++; + continue; + } + float4 _181 = float4(_118.x, _118.y, _118.z, _118.w); + out.out_var_TEXCOORD10_centroid = float4(_157.x, _157.y, _157.z, _68.w); + out.out_var_TEXCOORD11_centroid = ((_78[0] * _111) + (_78[1] * _113)) + (_78[2] * _116); + out_var_TEXCOORD0 = _76; + out.out_var_COLOR1 = ((_80[0] * _111) + (_80[1] * _113)) + (_80[2] * _116); + out.out_var_COLOR2 = ((_81[0] * _111) + (_81[1] * _113)) + (_81[2] * _116); + out.out_var_TEXCOORD6 = _181; + out.out_var_TEXCOORD7 = ((_98[0] * _120) + (_98[1] * _123)) + (_98[2] * _155); + out.gl_Position = View.View_TranslatedWorldToClip * _181; + out.out_var_TEXCOORD0_0 = out_var_TEXCOORD0[0]; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/vert/array-missing-copies.asm.vert b/third_party/spirv-cross/reference/shaders-ue4/asm/vert/array-missing-copies.asm.vert new file mode 100644 index 0000000..3d3e0e5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/vert/array-missing-copies.asm.vert @@ -0,0 +1,466 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#pragma clang diagnostic ignored "-Wmissing-braces" + +#include +#include + +using namespace metal; + +template +struct spvUnsafeArray +{ + T elements[Num ? Num : 1]; + + thread T& operator [] (size_t pos) thread + { + return elements[pos]; + } + constexpr const thread T& operator [] (size_t pos) const thread + { + return elements[pos]; + } + + device T& operator [] (size_t pos) device + { + return elements[pos]; + } + constexpr const device T& operator [] (size_t pos) const device + { + return elements[pos]; + } + + constexpr const constant T& operator [] (size_t pos) const constant + { + return elements[pos]; + } + + threadgroup T& operator [] (size_t pos) threadgroup + { + return elements[pos]; + } + constexpr const threadgroup T& operator [] (size_t pos) const threadgroup + { + return elements[pos]; + } +}; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_MobileBasePass +{ + float4 MobileBasePass_Fog_ExponentialFogParameters; + float4 MobileBasePass_Fog_ExponentialFogParameters2; + float4 MobileBasePass_Fog_ExponentialFogColorParameter; + float4 MobileBasePass_Fog_ExponentialFogParameters3; + float4 MobileBasePass_Fog_InscatteringLightDirection; + float4 MobileBasePass_Fog_DirectionalInscatteringColor; + float2 MobileBasePass_Fog_SinCosInscatteringColorCubemapRotation; + float PrePadding_MobileBasePass_Fog_104; + float PrePadding_MobileBasePass_Fog_108; + packed_float3 MobileBasePass_Fog_FogInscatteringTextureParameters; + float MobileBasePass_Fog_ApplyVolumetricFog; + float PrePadding_MobileBasePass_PlanarReflection_128; + float PrePadding_MobileBasePass_PlanarReflection_132; + float PrePadding_MobileBasePass_PlanarReflection_136; + float PrePadding_MobileBasePass_PlanarReflection_140; + float PrePadding_MobileBasePass_PlanarReflection_144; + float PrePadding_MobileBasePass_PlanarReflection_148; + float PrePadding_MobileBasePass_PlanarReflection_152; + float PrePadding_MobileBasePass_PlanarReflection_156; + float4 MobileBasePass_PlanarReflection_ReflectionPlane; + float4 MobileBasePass_PlanarReflection_PlanarReflectionOrigin; + float4 MobileBasePass_PlanarReflection_PlanarReflectionXAxis; + float4 MobileBasePass_PlanarReflection_PlanarReflectionYAxis; + float3x4 MobileBasePass_PlanarReflection_InverseTransposeMirrorMatrix; + packed_float3 MobileBasePass_PlanarReflection_PlanarReflectionParameters; + float PrePadding_MobileBasePass_PlanarReflection_284; + float2 MobileBasePass_PlanarReflection_PlanarReflectionParameters2; + float PrePadding_MobileBasePass_PlanarReflection_296; + float PrePadding_MobileBasePass_PlanarReflection_300; + float4x4 MobileBasePass_PlanarReflection_ProjectionWithExtraFOV[2]; + float4 MobileBasePass_PlanarReflection_PlanarReflectionScreenScaleBias[2]; + float2 MobileBasePass_PlanarReflection_PlanarReflectionScreenBound; + uint MobileBasePass_PlanarReflection_bIsStereo; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_UseEditorDepthTest; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + float PrePadding_Primitive_380; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightingChannelMask; + uint Primitive_LightmapDataIndex; + int Primitive_SingleCaptureIndex; +}; + +struct type_LandscapeParameters +{ + float4 LandscapeParameters_HeightmapUVScaleBias; + float4 LandscapeParameters_WeightmapUVScaleBias; + float4 LandscapeParameters_LandscapeLightmapScaleBias; + float4 LandscapeParameters_SubsectionSizeVertsLayerUVPan; + float4 LandscapeParameters_SubsectionOffsetParams; + float4 LandscapeParameters_LightmapSubsectionOffsetParams; + float4x4 LandscapeParameters_LocalToWorldNoScaling; +}; + +struct type_Globals +{ + float4 LodBias; + float4 LodValues; + float4 SectionLods; + float4 NeighborSectionLod[4]; +}; + +struct main0_out +{ + float2 out_var_TEXCOORD0 [[user(locn0)]]; + float2 out_var_TEXCOORD1 [[user(locn1)]]; + float4 out_var_TEXCOORD2 [[user(locn2)]]; + float4 out_var_TEXCOORD3 [[user(locn3)]]; + float4 out_var_TEXCOORD8 [[user(locn4)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float4 in_var_ATTRIBUTE0 [[attribute(0)]]; + float4 in_var_ATTRIBUTE1_0 [[attribute(1)]]; + float4 in_var_ATTRIBUTE1_1 [[attribute(2)]]; +}; + +vertex main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_MobileBasePass& MobileBasePass [[buffer(1)]], constant type_Primitive& Primitive [[buffer(2)]], constant type_LandscapeParameters& LandscapeParameters [[buffer(3)]], constant type_Globals& _Globals [[buffer(4)]]) +{ + main0_out out = {}; + spvUnsafeArray in_var_ATTRIBUTE1 = {}; + in_var_ATTRIBUTE1[0] = in.in_var_ATTRIBUTE1_0; + in_var_ATTRIBUTE1[1] = in.in_var_ATTRIBUTE1_1; + spvUnsafeArray _97; + for (int _107 = 0; _107 < 1; ) + { + _97[_107] = float4(0.0); + _107++; + continue; + } + float4 _115 = in.in_var_ATTRIBUTE0 * float4(255.0); + float2 _116 = _115.zw; + float2 _119 = fract(_116 * float2(0.5)) * float2(2.0); + float2 _121 = (_116 - _119) * float2(0.0039215688593685626983642578125); + float2 _122 = _115.xy; + float2 _126 = _122 * float2(_Globals.LodValues.w); + float _127 = _126.y; + float _128 = _126.x; + float4 _132 = float4(_127, _128, 1.0 - _128, 1.0 - _127) * float4(2.0); + float4 _186; + if (_119.y > 0.5) + { + float4 _161; + if (_119.x > 0.5) + { + _161 = (_132 * float4(_Globals.SectionLods.w)) + ((float4(1.0) - _132) * _Globals.NeighborSectionLod[3]); + } + else + { + _161 = (_132 * float4(_Globals.SectionLods.z)) + ((float4(1.0) - _132) * _Globals.NeighborSectionLod[2]); + } + _186 = _161; + } + else + { + float4 _185; + if (_119.x > 0.5) + { + _185 = (_132 * float4(_Globals.SectionLods.y)) + ((float4(1.0) - _132) * _Globals.NeighborSectionLod[1]); + } + else + { + _185 = (_132 * float4(_Globals.SectionLods.x)) + ((float4(1.0) - _132) * _Globals.NeighborSectionLod[0]); + } + _186 = _185; + } + float _206; + if ((_128 + _127) > 1.0) + { + float _198; + if (_128 < _127) + { + _198 = _186.w; + } + else + { + _198 = _186.z; + } + _206 = _198; + } + else + { + float _205; + if (_128 < _127) + { + _205 = _186.y; + } + else + { + _205 = _186.x; + } + _206 = _205; + } + float _207 = floor(_206); + float _220 = _121.x; + float3 _235 = select(select(select(select(select(float3(0.03125, _121.yy), float3(0.0625, _220, _121.y), bool3(_207 < 5.0)), float3(0.125, in_var_ATTRIBUTE1[1].w, _220), bool3(_207 < 4.0)), float3(0.25, in_var_ATTRIBUTE1[1].zw), bool3(_207 < 3.0)), float3(0.5, in_var_ATTRIBUTE1[1].yz), bool3(_207 < 2.0)), float3(1.0, in_var_ATTRIBUTE1[1].xy), bool3(_207 < 1.0)); + float _236 = _235.x; + float _245 = (((in_var_ATTRIBUTE1[0].x * 65280.0) + (in_var_ATTRIBUTE1[0].y * 255.0)) - 32768.0) * 0.0078125; + float _252 = (((in_var_ATTRIBUTE1[0].z * 65280.0) + (in_var_ATTRIBUTE1[0].w * 255.0)) - 32768.0) * 0.0078125; + float2 _257 = floor(_122 * float2(_236)); + float2 _271 = float2((LandscapeParameters.LandscapeParameters_SubsectionSizeVertsLayerUVPan.x * _236) - 1.0, fast::max((LandscapeParameters.LandscapeParameters_SubsectionSizeVertsLayerUVPan.x * 0.5) * _236, 2.0) - 1.0) * float2(LandscapeParameters.LandscapeParameters_SubsectionSizeVertsLayerUVPan.y); + float3 _287 = mix(float3(_257 / float2(_271.x), mix(_245, _252, _235.y)), float3(floor(_257 * float2(0.5)) / float2(_271.y), mix(_245, _252, _235.z)), float3(_206 - _207)); + float2 _288 = _119.xy; + float2 _292 = _288 * LandscapeParameters.LandscapeParameters_SubsectionOffsetParams.ww; + float3 _296 = _287 + float3(_292, 0.0); + float4 _322 = float4((((Primitive.Primitive_LocalToWorld[0u].xyz * _296.xxx) + (Primitive.Primitive_LocalToWorld[1u].xyz * _296.yyy)) + (Primitive.Primitive_LocalToWorld[2u].xyz * _296.zzz)) + (Primitive.Primitive_LocalToWorld[3u].xyz + float3(View.View_PreViewTranslation)), 1.0); + float2 _323 = _287.xy; + float4 _338 = float4(_322.x, _322.y, _322.z, _322.w); + float4 _339 = View.View_TranslatedWorldToClip * _338; + float3 _341 = _322.xyz - float3(View.View_TranslatedWorldCameraOrigin); + float _345 = dot(_341, _341); + float _346 = rsqrt(_345); + float _347 = _345 * _346; + float _354 = _341.z; + float _357 = fast::max(0.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters.w); + float _393; + float _394; + float _395; + float _396; + if (_357 > 0.0) + { + float _361 = _357 * _346; + float _362 = _361 * _354; + float _365 = View.View_WorldCameraOrigin[2] + _362; + _393 = (1.0 - _361) * _347; + _394 = MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.z * exp2(-fast::max(-127.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.y * (_365 - MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.w))); + _395 = MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters3.x * exp2(-fast::max(-127.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters.y * (_365 - MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters3.y))); + _396 = _354 - _362; + } + else + { + _393 = _347; + _394 = MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.x; + _395 = MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters.x; + _396 = _354; + } + float _400 = fast::max(-127.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters.y * _396); + float _405 = log(2.0); + float _407 = 0.5 * (_405 * _405); + float _417 = fast::max(-127.0, MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters2.y * _396); + float _428 = (_395 * ((abs(_400) > 0.00999999977648258209228515625) ? ((1.0 - exp2(-_400)) / _400) : (_405 - (_407 * _400)))) + (_394 * ((abs(_417) > 0.00999999977648258209228515625) ? ((1.0 - exp2(-_417)) / _417) : (_405 - (_407 * _417)))); + float3 _459; + if (MobileBasePass.MobileBasePass_Fog_InscatteringLightDirection.w >= 0.0) + { + _459 = (MobileBasePass.MobileBasePass_Fog_DirectionalInscatteringColor.xyz * float3(pow(fast::clamp(dot(_341 * float3(_346), MobileBasePass.MobileBasePass_Fog_InscatteringLightDirection.xyz), 0.0, 1.0), MobileBasePass.MobileBasePass_Fog_DirectionalInscatteringColor.w))) * float3(1.0 - fast::clamp(exp2(-(_428 * fast::max(_393 - MobileBasePass.MobileBasePass_Fog_InscatteringLightDirection.w, 0.0))), 0.0, 1.0)); + } + else + { + _459 = float3(0.0); + } + bool _468 = (MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters3.w > 0.0) && (_347 > MobileBasePass.MobileBasePass_Fog_ExponentialFogParameters3.w); + float _471 = _468 ? 1.0 : fast::max(fast::clamp(exp2(-(_428 * _393)), 0.0, 1.0), MobileBasePass.MobileBasePass_Fog_ExponentialFogColorParameter.w); + _97[0] = float4((MobileBasePass.MobileBasePass_Fog_ExponentialFogColorParameter.xyz * float3(1.0 - _471)) + select(_459, float3(0.0), bool3(_468)), _471); + float4 _482 = _338; + _482.w = _339.w; + out.out_var_TEXCOORD0 = ((_323 + LandscapeParameters.LandscapeParameters_SubsectionSizeVertsLayerUVPan.zw) + _292).xy; + out.out_var_TEXCOORD1 = ((_323 * LandscapeParameters.LandscapeParameters_WeightmapUVScaleBias.xy) + LandscapeParameters.LandscapeParameters_WeightmapUVScaleBias.zw) + (_288 * LandscapeParameters.LandscapeParameters_SubsectionOffsetParams.zz); + out.out_var_TEXCOORD2 = float4(float4(0.0).x, float4(0.0).y, _97[0].x, _97[0].y); + out.out_var_TEXCOORD3 = float4(float4(0.0).x, float4(0.0).y, _97[0].z, _97[0].w); + out.out_var_TEXCOORD8 = _482; + out.gl_Position = _339; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders-ue4/asm/vert/texture-buffer.asm.vert b/third_party/spirv-cross/reference/shaders-ue4/asm/vert/texture-buffer.asm.vert new file mode 100644 index 0000000..6384e1b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders-ue4/asm/vert/texture-buffer.asm.vert @@ -0,0 +1,387 @@ +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct type_View +{ + float4x4 View_TranslatedWorldToClip; + float4x4 View_WorldToClip; + float4x4 View_TranslatedWorldToView; + float4x4 View_ViewToTranslatedWorld; + float4x4 View_TranslatedWorldToCameraView; + float4x4 View_CameraViewToTranslatedWorld; + float4x4 View_ViewToClip; + float4x4 View_ViewToClipNoAA; + float4x4 View_ClipToView; + float4x4 View_ClipToTranslatedWorld; + float4x4 View_SVPositionToTranslatedWorld; + float4x4 View_ScreenToWorld; + float4x4 View_ScreenToTranslatedWorld; + packed_float3 View_ViewForward; + float PrePadding_View_844; + packed_float3 View_ViewUp; + float PrePadding_View_860; + packed_float3 View_ViewRight; + float PrePadding_View_876; + packed_float3 View_HMDViewNoRollUp; + float PrePadding_View_892; + packed_float3 View_HMDViewNoRollRight; + float PrePadding_View_908; + float4 View_InvDeviceZToWorldZTransform; + float4 View_ScreenPositionScaleBias; + packed_float3 View_WorldCameraOrigin; + float PrePadding_View_956; + packed_float3 View_TranslatedWorldCameraOrigin; + float PrePadding_View_972; + packed_float3 View_WorldViewOrigin; + float PrePadding_View_988; + packed_float3 View_PreViewTranslation; + float PrePadding_View_1004; + float4x4 View_PrevProjection; + float4x4 View_PrevViewProj; + float4x4 View_PrevViewRotationProj; + float4x4 View_PrevViewToClip; + float4x4 View_PrevClipToView; + float4x4 View_PrevTranslatedWorldToClip; + float4x4 View_PrevTranslatedWorldToView; + float4x4 View_PrevViewToTranslatedWorld; + float4x4 View_PrevTranslatedWorldToCameraView; + float4x4 View_PrevCameraViewToTranslatedWorld; + packed_float3 View_PrevWorldCameraOrigin; + float PrePadding_View_1660; + packed_float3 View_PrevWorldViewOrigin; + float PrePadding_View_1676; + packed_float3 View_PrevPreViewTranslation; + float PrePadding_View_1692; + float4x4 View_PrevInvViewProj; + float4x4 View_PrevScreenToTranslatedWorld; + float4x4 View_ClipToPrevClip; + float4 View_TemporalAAJitter; + float4 View_GlobalClippingPlane; + float2 View_FieldOfViewWideAngles; + float2 View_PrevFieldOfViewWideAngles; + float4 View_ViewRectMin; + float4 View_ViewSizeAndInvSize; + float4 View_BufferSizeAndInvSize; + float4 View_BufferBilinearUVMinMax; + int View_NumSceneColorMSAASamples; + float View_PreExposure; + float View_OneOverPreExposure; + float PrePadding_View_2012; + float4 View_DiffuseOverrideParameter; + float4 View_SpecularOverrideParameter; + float4 View_NormalOverrideParameter; + float2 View_RoughnessOverrideParameter; + float View_PrevFrameGameTime; + float View_PrevFrameRealTime; + float View_OutOfBoundsMask; + float PrePadding_View_2084; + float PrePadding_View_2088; + float PrePadding_View_2092; + packed_float3 View_WorldCameraMovementSinceLastFrame; + float View_CullingSign; + float View_NearPlane; + float View_AdaptiveTessellationFactor; + float View_GameTime; + float View_RealTime; + float View_DeltaTime; + float View_MaterialTextureMipBias; + float View_MaterialTextureDerivativeMultiply; + uint View_Random; + uint View_FrameNumber; + uint View_StateFrameIndexMod8; + uint View_StateFrameIndex; + float View_CameraCut; + float View_UnlitViewmodeMask; + float PrePadding_View_2164; + float PrePadding_View_2168; + float PrePadding_View_2172; + float4 View_DirectionalLightColor; + packed_float3 View_DirectionalLightDirection; + float PrePadding_View_2204; + float4 View_TranslucencyLightingVolumeMin[2]; + float4 View_TranslucencyLightingVolumeInvSize[2]; + float4 View_TemporalAAParams; + float4 View_CircleDOFParams; + float View_DepthOfFieldSensorWidth; + float View_DepthOfFieldFocalDistance; + float View_DepthOfFieldScale; + float View_DepthOfFieldFocalLength; + float View_DepthOfFieldFocalRegion; + float View_DepthOfFieldNearTransitionRegion; + float View_DepthOfFieldFarTransitionRegion; + float View_MotionBlurNormalizedToPixel; + float View_bSubsurfacePostprocessEnabled; + float View_GeneralPurposeTweak; + float View_DemosaicVposOffset; + float PrePadding_View_2348; + packed_float3 View_IndirectLightingColorScale; + float View_HDR32bppEncodingMode; + packed_float3 View_AtmosphericFogSunDirection; + float View_AtmosphericFogSunPower; + float View_AtmosphericFogPower; + float View_AtmosphericFogDensityScale; + float View_AtmosphericFogDensityOffset; + float View_AtmosphericFogGroundOffset; + float View_AtmosphericFogDistanceScale; + float View_AtmosphericFogAltitudeScale; + float View_AtmosphericFogHeightScaleRayleigh; + float View_AtmosphericFogStartDistance; + float View_AtmosphericFogDistanceOffset; + float View_AtmosphericFogSunDiscScale; + uint View_AtmosphericFogRenderMask; + uint View_AtmosphericFogInscatterAltitudeSampleNum; + float4 View_AtmosphericFogSunColor; + packed_float3 View_NormalCurvatureToRoughnessScaleBias; + float View_RenderingReflectionCaptureMask; + float4 View_AmbientCubemapTint; + float View_AmbientCubemapIntensity; + float View_SkyLightParameters; + float PrePadding_View_2488; + float PrePadding_View_2492; + float4 View_SkyLightColor; + float4 View_SkyIrradianceEnvironmentMap[7]; + float View_MobilePreviewMode; + float View_HMDEyePaddingOffset; + float View_ReflectionCubemapMaxMip; + float View_ShowDecalsMask; + uint View_DistanceFieldAOSpecularOcclusionMode; + float View_IndirectCapsuleSelfShadowingIntensity; + float PrePadding_View_2648; + float PrePadding_View_2652; + packed_float3 View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight; + int View_StereoPassIndex; + float4 View_GlobalVolumeCenterAndExtent[4]; + float4 View_GlobalVolumeWorldToUVAddAndMul[4]; + float View_GlobalVolumeDimension; + float View_GlobalVolumeTexelSize; + float View_MaxGlobalDistance; + float View_bCheckerboardSubsurfaceProfileRendering; + packed_float3 View_VolumetricFogInvGridSize; + float PrePadding_View_2828; + packed_float3 View_VolumetricFogGridZParams; + float PrePadding_View_2844; + float2 View_VolumetricFogSVPosToVolumeUV; + float View_VolumetricFogMaxDistance; + float PrePadding_View_2860; + packed_float3 View_VolumetricLightmapWorldToUVScale; + float PrePadding_View_2876; + packed_float3 View_VolumetricLightmapWorldToUVAdd; + float PrePadding_View_2892; + packed_float3 View_VolumetricLightmapIndirectionTextureSize; + float View_VolumetricLightmapBrickSize; + packed_float3 View_VolumetricLightmapBrickTexelSize; + float View_StereoIPD; + float View_IndirectLightingCacheShowFlag; + float View_EyeToPixelSpreadAngle; +}; + +struct type_Primitive +{ + float4x4 Primitive_LocalToWorld; + float4 Primitive_InvNonUniformScaleAndDeterminantSign; + float4 Primitive_ObjectWorldPositionAndRadius; + float4x4 Primitive_WorldToLocal; + float4x4 Primitive_PreviousLocalToWorld; + float4x4 Primitive_PreviousWorldToLocal; + packed_float3 Primitive_ActorWorldPosition; + float Primitive_UseSingleSampleShadowFromStationaryLights; + packed_float3 Primitive_ObjectBounds; + float Primitive_LpvBiasMultiplier; + float Primitive_DecalReceiverMask; + float Primitive_PerObjectGBufferData; + float Primitive_UseVolumetricLightmapShadowFromStationaryLights; + float Primitive_UseEditorDepthTest; + float4 Primitive_ObjectOrientation; + float4 Primitive_NonUniformScale; + packed_float3 Primitive_LocalObjectBoundsMin; + float PrePadding_Primitive_380; + packed_float3 Primitive_LocalObjectBoundsMax; + uint Primitive_LightingChannelMask; + uint Primitive_LightmapDataIndex; + int Primitive_SingleCaptureIndex; +}; + +struct type_MobileShadowDepthPass +{ + float PrePadding_MobileShadowDepthPass_0; + float PrePadding_MobileShadowDepthPass_4; + float PrePadding_MobileShadowDepthPass_8; + float PrePadding_MobileShadowDepthPass_12; + float PrePadding_MobileShadowDepthPass_16; + float PrePadding_MobileShadowDepthPass_20; + float PrePadding_MobileShadowDepthPass_24; + float PrePadding_MobileShadowDepthPass_28; + float PrePadding_MobileShadowDepthPass_32; + float PrePadding_MobileShadowDepthPass_36; + float PrePadding_MobileShadowDepthPass_40; + float PrePadding_MobileShadowDepthPass_44; + float PrePadding_MobileShadowDepthPass_48; + float PrePadding_MobileShadowDepthPass_52; + float PrePadding_MobileShadowDepthPass_56; + float PrePadding_MobileShadowDepthPass_60; + float PrePadding_MobileShadowDepthPass_64; + float PrePadding_MobileShadowDepthPass_68; + float PrePadding_MobileShadowDepthPass_72; + float PrePadding_MobileShadowDepthPass_76; + float4x4 MobileShadowDepthPass_ProjectionMatrix; + float2 MobileShadowDepthPass_ShadowParams; + float MobileShadowDepthPass_bClampToNearPlane; + float PrePadding_MobileShadowDepthPass_156; + float4x4 MobileShadowDepthPass_ShadowViewProjectionMatrices[6]; +}; + +struct type_EmitterDynamicUniforms +{ + float2 EmitterDynamicUniforms_LocalToWorldScale; + float EmitterDynamicUniforms_EmitterInstRandom; + float PrePadding_EmitterDynamicUniforms_12; + float4 EmitterDynamicUniforms_AxisLockRight; + float4 EmitterDynamicUniforms_AxisLockUp; + float4 EmitterDynamicUniforms_DynamicColor; + float4 EmitterDynamicUniforms_MacroUVParameters; +}; + +struct type_EmitterUniforms +{ + float4 EmitterUniforms_ColorCurve; + float4 EmitterUniforms_ColorScale; + float4 EmitterUniforms_ColorBias; + float4 EmitterUniforms_MiscCurve; + float4 EmitterUniforms_MiscScale; + float4 EmitterUniforms_MiscBias; + float4 EmitterUniforms_SizeBySpeed; + float4 EmitterUniforms_SubImageSize; + float4 EmitterUniforms_TangentSelector; + packed_float3 EmitterUniforms_CameraFacingBlend; + float EmitterUniforms_RemoveHMDRoll; + float EmitterUniforms_RotationRateScale; + float EmitterUniforms_RotationBias; + float EmitterUniforms_CameraMotionBlurAmount; + float PrePadding_EmitterUniforms_172; + float2 EmitterUniforms_PivotOffset; +}; + +struct type_Globals +{ + uint ParticleIndicesOffset; +}; + +struct main0_out +{ + float out_var_TEXCOORD6 [[user(locn0)]]; + float4 gl_Position [[position]]; +}; + +struct main0_in +{ + float2 in_var_ATTRIBUTE0 [[attribute(0)]]; +}; + +// Returns 2D texture coords corresponding to 1D texel buffer coords +static inline __attribute__((always_inline)) +uint2 spvTexelBufferCoord(uint tc) +{ + return uint2(tc % 4096, tc / 4096); +} + +vertex main0_out main0(main0_in in [[stage_in]], constant type_View& View [[buffer(0)]], constant type_Primitive& Primitive [[buffer(1)]], constant type_MobileShadowDepthPass& MobileShadowDepthPass [[buffer(2)]], constant type_EmitterDynamicUniforms& EmitterDynamicUniforms [[buffer(3)]], constant type_EmitterUniforms& EmitterUniforms [[buffer(4)]], constant type_Globals& _Globals [[buffer(5)]], texture2d ParticleIndices [[texture(0)]], texture2d PositionTexture [[texture(1)]], texture2d VelocityTexture [[texture(2)]], texture2d AttributesTexture [[texture(3)]], texture2d CurveTexture [[texture(4)]], sampler PositionTextureSampler [[sampler(0)]], sampler VelocityTextureSampler [[sampler(1)]], sampler AttributesTextureSampler [[sampler(2)]], sampler CurveTextureSampler [[sampler(3)]], uint gl_VertexIndex [[vertex_id]], uint gl_InstanceIndex [[instance_id]]) +{ + main0_out out = {}; + float2 _133 = ParticleIndices.read(spvTexelBufferCoord((_Globals.ParticleIndicesOffset + ((gl_InstanceIndex * 16u) + (gl_VertexIndex / 4u))))).xy; + float4 _137 = PositionTexture.sample(PositionTextureSampler, _133, level(0.0)); + float4 _145 = AttributesTexture.sample(AttributesTextureSampler, _133, level(0.0)); + float _146 = _137.w; + float3 _158 = float3x3(Primitive.Primitive_LocalToWorld[0].xyz, Primitive.Primitive_LocalToWorld[1].xyz, Primitive.Primitive_LocalToWorld[2].xyz) * VelocityTexture.sample(VelocityTextureSampler, _133, level(0.0)).xyz; + float3 _160 = normalize(_158 + float3(0.0, 0.0, 9.9999997473787516355514526367188e-05)); + float2 _204 = ((((_145.xy + float2((_145.x < 0.5) ? 0.0 : (-0.5), (_145.y < 0.5) ? 0.0 : (-0.5))) * float2(2.0)) * (((CurveTexture.sample(CurveTextureSampler, (EmitterUniforms.EmitterUniforms_MiscCurve.xy + (EmitterUniforms.EmitterUniforms_MiscCurve.zw * float2(_146))), level(0.0)) * EmitterUniforms.EmitterUniforms_MiscScale) + EmitterUniforms.EmitterUniforms_MiscBias).xy * EmitterDynamicUniforms.EmitterDynamicUniforms_LocalToWorldScale)) * fast::min(fast::max(EmitterUniforms.EmitterUniforms_SizeBySpeed.xy * float2(length(_158)), float2(1.0)), EmitterUniforms.EmitterUniforms_SizeBySpeed.zw)) * float2(step(_146, 1.0)); + float3 _239 = float4((((Primitive.Primitive_LocalToWorld[0u].xyz * _137.xxx) + (Primitive.Primitive_LocalToWorld[1u].xyz * _137.yyy)) + (Primitive.Primitive_LocalToWorld[2u].xyz * _137.zzz)) + (Primitive.Primitive_LocalToWorld[3u].xyz + float3(View.View_PreViewTranslation)), 1.0).xyz; + float3 _242 = float3(EmitterUniforms.EmitterUniforms_RemoveHMDRoll); + float3 _251 = mix(mix(float3(View.View_ViewRight), float3(View.View_HMDViewNoRollRight), _242), EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockRight.xyz, float3(EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockRight.w)); + float3 _259 = mix(-mix(float3(View.View_ViewUp), float3(View.View_HMDViewNoRollUp), _242), EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockUp.xyz, float3(EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockUp.w)); + float3 _260 = float3(View.View_TranslatedWorldCameraOrigin) - _239; + float _261 = dot(_260, _260); + float3 _265 = _260 / float3(sqrt(fast::max(_261, 0.00999999977648258209228515625))); + float3 _335; + float3 _336; + if (EmitterUniforms.EmitterUniforms_CameraFacingBlend[0] > 0.0) + { + float3 _279 = cross(_265, float3(0.0, 0.0, 1.0)); + float3 _284 = _279 / float3(sqrt(fast::max(dot(_279, _279), 0.00999999977648258209228515625))); + float3 _286 = float3(fast::clamp((_261 * EmitterUniforms.EmitterUniforms_CameraFacingBlend[1]) - EmitterUniforms.EmitterUniforms_CameraFacingBlend[2], 0.0, 1.0)); + _335 = normalize(mix(_251, _284, _286)); + _336 = normalize(mix(_259, cross(_265, _284), _286)); + } + else + { + float3 _333; + float3 _334; + if (EmitterUniforms.EmitterUniforms_TangentSelector.y > 0.0) + { + float3 _297 = cross(_265, _160); + _333 = _297 / float3(sqrt(fast::max(dot(_297, _297), 0.00999999977648258209228515625))); + _334 = -_160; + } + else + { + float3 _331; + float3 _332; + if (EmitterUniforms.EmitterUniforms_TangentSelector.z > 0.0) + { + float3 _310 = cross(EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockRight.xyz, _265); + _331 = EmitterDynamicUniforms.EmitterDynamicUniforms_AxisLockRight.xyz; + _332 = -(_310 / float3(sqrt(fast::max(dot(_310, _310), 0.00999999977648258209228515625)))); + } + else + { + float3 _329; + float3 _330; + if (EmitterUniforms.EmitterUniforms_TangentSelector.w > 0.0) + { + float3 _322 = cross(_265, float3(0.0, 0.0, 1.0)); + float3 _327 = _322 / float3(sqrt(fast::max(dot(_322, _322), 0.00999999977648258209228515625))); + _329 = _327; + _330 = cross(_265, _327); + } + else + { + _329 = _251; + _330 = _259; + } + _331 = _329; + _332 = _330; + } + _333 = _331; + _334 = _332; + } + _335 = _333; + _336 = _334; + } + float _339 = ((_145.z + ((_145.w * EmitterUniforms.EmitterUniforms_RotationRateScale) * _146)) * 6.283185482025146484375) + EmitterUniforms.EmitterUniforms_RotationBias; + float3 _342 = float3(sin(_339)); + float3 _344 = float3(cos(_339)); + float4 _371 = float4(_239 + ((float3(_204.x * (in.in_var_ATTRIBUTE0.x + EmitterUniforms.EmitterUniforms_PivotOffset.x)) * ((_342 * _336) + (_344 * _335))) + (float3(_204.y * (in.in_var_ATTRIBUTE0.y + EmitterUniforms.EmitterUniforms_PivotOffset.y)) * ((_344 * _336) - (_342 * _335)))), 1.0); + float4 _375 = MobileShadowDepthPass.MobileShadowDepthPass_ProjectionMatrix * float4(_371.x, _371.y, _371.z, _371.w); + float4 _386; + if ((MobileShadowDepthPass.MobileShadowDepthPass_bClampToNearPlane > 0.0) && (_375.z < 0.0)) + { + float4 _384 = _375; + _384.z = 9.9999999747524270787835121154785e-07; + float4 _385 = _384; + _385.w = 1.0; + _386 = _385; + } + else + { + _386 = _375; + } + float4 _396 = _386; + _396.z = ((_386.z * MobileShadowDepthPass.MobileShadowDepthPass_ShadowParams.y) + MobileShadowDepthPass.MobileShadowDepthPass_ShadowParams.x) * _386.w; + out.out_var_TEXCOORD6 = 0.0; + out.gl_Position = _396; + return out; +} + diff --git a/third_party/spirv-cross/reference/shaders/amd/gcn_shader.comp b/third_party/spirv-cross/reference/shaders/amd/gcn_shader.comp new file mode 100644 index 0000000..1c0c5ae --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/amd/gcn_shader.comp @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_AMD_gcn_shader : require +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + float cubeFace = cubeFaceIndexAMD(vec3(0.0)); + vec2 cubeFaceCoord = cubeFaceCoordAMD(vec3(1.0)); + uint64_t time = timeAMD(); +} + diff --git a/third_party/spirv-cross/reference/shaders/amd/shader_ballot.comp b/third_party/spirv-cross/reference/shaders/amd/shader_ballot.comp new file mode 100644 index 0000000..1fade72 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/amd/shader_ballot.comp @@ -0,0 +1,32 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_ARB_shader_ballot : require +#extension GL_AMD_shader_ballot : require +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer inputData +{ + float inputDataArray[]; +} _12; + +layout(binding = 1, std430) buffer outputData +{ + float outputDataArray[]; +} _74; + +void main() +{ + float thisLaneData = _12.inputDataArray[gl_LocalInvocationID.x]; + bool laneActive = thisLaneData > 0.0; + uint thisLaneOutputSlot = mbcntAMD(packUint2x32(uvec2(uvec4(unpackUint2x32(ballotARB(laneActive)), 0u, 0u).xy))); + int firstInvocation = readFirstInvocationARB(1); + int invocation = readInvocationARB(1, 0u); + vec3 swizzleInvocations = swizzleInvocationsAMD(vec3(0.0, 2.0, 1.0), uvec4(3u)); + vec3 swizzelInvocationsMasked = swizzleInvocationsMaskedAMD(vec3(0.0, 2.0, 1.0), uvec3(2u)); + vec3 writeInvocation = writeInvocationAMD(swizzleInvocations, swizzelInvocationsMasked, 0u); + if (laneActive) + { + _74.outputDataArray[thisLaneOutputSlot] = thisLaneData; + } +} + diff --git a/third_party/spirv-cross/reference/shaders/amd/shader_group_vote.comp b/third_party/spirv-cross/reference/shaders/amd/shader_group_vote.comp new file mode 100644 index 0000000..007d9f9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/amd/shader_group_vote.comp @@ -0,0 +1,18 @@ +#version 450 +#extension GL_ARB_shader_group_vote : require +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer inputData +{ + float inputDataArray[]; +} _12; + +void main() +{ + float thisLaneData = _12.inputDataArray[gl_LocalInvocationID.x]; + bool laneActive = thisLaneData > 0.0; + bool allInvocations = allInvocationsARB(laneActive); + bool anyInvocations = anyInvocationARB(laneActive); + bool allInvocationsEqual = allInvocationsEqualARB(laneActive); +} + diff --git a/third_party/spirv-cross/reference/shaders/amd/shader_trinary_minmax.comp b/third_party/spirv-cross/reference/shaders/amd/shader_trinary_minmax.comp new file mode 100644 index 0000000..ece39b7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/amd/shader_trinary_minmax.comp @@ -0,0 +1,11 @@ +#version 450 +#extension GL_AMD_shader_trinary_minmax : require +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + int t11 = min3(0, 3, 2); + int t12 = max3(0, 3, 2); + int t13 = mid3(0, 3, 2); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..5ece825 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,18 @@ +#version 450 +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer u0_counters +{ + uint c; +} u0_counter; + +layout(binding = 0, r32ui) uniform writeonly uimageBuffer u0; + +void main() +{ + uint _29 = atomicAdd(u0_counter.c, uint(-1)); + vec4 r0; + r0.x = uintBitsToFloat(_29); + imageStore(u0, int((uint(floatBitsToInt(r0.x)) * 1u) + (uint(0) >> 2u)), uvec4(uint(int(gl_GlobalInvocationID.x)))); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..2a8f711 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,18 @@ +#version 450 +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer u0_counters +{ + uint c; +} u0_counter; + +layout(binding = 0, r32ui) uniform writeonly uimageBuffer u0; + +void main() +{ + uint _29 = atomicAdd(u0_counter.c, 1u); + vec4 r0; + r0.x = uintBitsToFloat(_29); + imageStore(u0, int((uint(floatBitsToInt(r0.x)) * 1u) + (uint(0) >> 2u)), uvec4(uint(int(gl_GlobalInvocationID.x)))); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_iadd.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_iadd.asm.comp new file mode 100644 index 0000000..bed2dff --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_iadd.asm.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) restrict buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) restrict buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + _6._m0 = _5._m1 + uvec4(_5._m0); + _6._m0 = uvec4(_5._m0) + _5._m1; + _6._m0 = _5._m1 + _5._m1; + _6._m0 = uvec4(_5._m0 + _5._m0); + _6._m1 = ivec4(_5._m1 + _5._m1); + _6._m1 = _5._m0 + _5._m0; + _6._m1 = ivec4(_5._m1) + _5._m0; + _6._m1 = _5._m0 + ivec4(_5._m1); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..bed3b90 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) restrict buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) restrict buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + _6._m0 = mix(uvec4(0u), uvec4(1u), lessThan(ivec4(_5._m1), _5._m0)); + _6._m0 = mix(uvec4(0u), uvec4(1u), lessThanEqual(ivec4(_5._m1), _5._m0)); + _6._m0 = mix(uvec4(0u), uvec4(1u), lessThan(_5._m1, uvec4(_5._m0))); + _6._m0 = mix(uvec4(0u), uvec4(1u), lessThanEqual(_5._m1, uvec4(_5._m0))); + _6._m0 = mix(uvec4(0u), uvec4(1u), greaterThan(ivec4(_5._m1), _5._m0)); + _6._m0 = mix(uvec4(0u), uvec4(1u), greaterThanEqual(ivec4(_5._m1), _5._m0)); + _6._m0 = mix(uvec4(0u), uvec4(1u), greaterThan(_5._m1, uvec4(_5._m0))); + _6._m0 = mix(uvec4(0u), uvec4(1u), greaterThanEqual(_5._m1, uvec4(_5._m0))); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_iequal.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_iequal.asm.comp new file mode 100644 index 0000000..bdb3eeb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_iequal.asm.comp @@ -0,0 +1,33 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + ivec4 _30 = _5._m0; + uvec4 _31 = _5._m1; + bvec4 _34 = equal(ivec4(_31), _30); + bvec4 _35 = equal(_30, ivec4(_31)); + bvec4 _36 = equal(_31, _31); + bvec4 _37 = equal(_30, _30); + _6._m0 = mix(uvec4(0u), uvec4(1u), _34); + _6._m0 = mix(uvec4(0u), uvec4(1u), _35); + _6._m0 = mix(uvec4(0u), uvec4(1u), _36); + _6._m0 = mix(uvec4(0u), uvec4(1u), _37); + _6._m1 = mix(ivec4(0), ivec4(1), _34); + _6._m1 = mix(ivec4(0), ivec4(1), _35); + _6._m1 = mix(ivec4(0), ivec4(1), _36); + _6._m1 = mix(ivec4(0), ivec4(1), _37); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_sar.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_sar.asm.comp new file mode 100644 index 0000000..283b444 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_sar.asm.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + ivec4 _22 = _5._m0; + uvec4 _23 = _5._m1; + _6._m0 = uvec4(ivec4(_23) >> _22); + _6._m0 = uvec4(_22 >> ivec4(_23)); + _6._m0 = uvec4(ivec4(_23) >> ivec4(_23)); + _6._m0 = uvec4(_22 >> _22); + _6._m1 = ivec4(_23) >> ivec4(_23); + _6._m1 = _22 >> _22; + _6._m1 = ivec4(_23) >> _22; + _6._m1 = _22 >> ivec4(_23); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_sdiv.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_sdiv.asm.comp new file mode 100644 index 0000000..e28c481 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_sdiv.asm.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + ivec4 _22 = _5._m0; + uvec4 _23 = _5._m1; + _6._m0 = uvec4(ivec4(_23) / _22); + _6._m0 = uvec4(_22 / ivec4(_23)); + _6._m0 = uvec4(ivec4(_23) / ivec4(_23)); + _6._m0 = uvec4(_22 / _22); + _6._m1 = ivec4(_23) / ivec4(_23); + _6._m1 = _22 / _22; + _6._m1 = ivec4(_23) / _22; + _6._m1 = _22 / ivec4(_23); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_slr.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_slr.asm.comp new file mode 100644 index 0000000..78efaf3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/bitcast_slr.asm.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_5 +{ + ivec4 _m0; + uvec4 _m1; +} _5; + +layout(binding = 1, std430) buffer _4_6 +{ + uvec4 _m0; + ivec4 _m1; +} _6; + +void main() +{ + ivec4 _22 = _5._m0; + uvec4 _23 = _5._m1; + _6._m0 = _23 >> uvec4(_22); + _6._m0 = uvec4(_22) >> _23; + _6._m0 = _23 >> _23; + _6._m0 = uvec4(_22) >> uvec4(_22); + _6._m1 = ivec4(_23 >> _23); + _6._m1 = ivec4(uvec4(_22) >> uvec4(_22)); + _6._m1 = ivec4(_23 >> uvec4(_22)); + _6._m1 = ivec4(uvec4(_22) >> _23); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..86ba0a3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,37 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct A +{ + int a; + int b; +}; + +layout(binding = 1, std430) buffer C1 +{ + A Data[]; +} C1_1; + +layout(binding = 2, std140) uniform C2 +{ + A Data[1024]; +} C2_1; + +layout(binding = 0, std430) buffer B +{ + A Data[]; +} C3; + +layout(binding = 3, std140) uniform B +{ + A Data[1024]; +} C4; + +void main() +{ + C1_1.Data[gl_GlobalInvocationID.x].a = C2_1.Data[gl_GlobalInvocationID.x].a; + C1_1.Data[gl_GlobalInvocationID.x].b = C2_1.Data[gl_GlobalInvocationID.x].b; + C3.Data[gl_GlobalInvocationID.x].a = C4.Data[gl_GlobalInvocationID.x].a; + C3.Data[gl_GlobalInvocationID.x].b = C4.Data[gl_GlobalInvocationID.x].b; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/builtin-compute-bitcast.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/builtin-compute-bitcast.asm.comp new file mode 100644 index 0000000..abb8a79 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/builtin-compute-bitcast.asm.comp @@ -0,0 +1,13 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer BUF +{ + int values[]; +} _6; + +void main() +{ + _6.values[int(gl_WorkGroupID.y)] = int(gl_GlobalInvocationID.z); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/decoration-group.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/decoration-group.asm.comp new file mode 100644 index 0000000..28ad4d4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/decoration-group.asm.comp @@ -0,0 +1,38 @@ +#version 430 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 5, std430) buffer _6_15 +{ + float _m0[]; +} _15; + +layout(binding = 0, std430) buffer _7_16 +{ + float _m0[]; +} _16; + +layout(binding = 1, std430) buffer _8_17 +{ + float _m0[]; +} _17; + +layout(binding = 2, std430) restrict readonly buffer _9_18 +{ + float _m0[]; +} _18; + +layout(binding = 3, std430) restrict readonly buffer _10_19 +{ + float _m0[]; +} _19; + +layout(binding = 4, std430) restrict readonly buffer _11_20 +{ + float _m0[]; +} _20; + +void main() +{ + _15._m0[gl_GlobalInvocationID.x] = (((_16._m0[gl_GlobalInvocationID.x] + _17._m0[gl_GlobalInvocationID.x]) + _18._m0[gl_GlobalInvocationID.x]) + _19._m0[gl_GlobalInvocationID.x]) + _20._m0[gl_GlobalInvocationID.x]; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..20db16f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,27 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) readonly buffer ssbo +{ + uint _data[]; +} ssbo_1; + +void Load(uint size) +{ + int byteAddrTemp = int(size >> uint(2)); + uvec4 data = uvec4(ssbo_1._data[byteAddrTemp], ssbo_1._data[byteAddrTemp + 1], ssbo_1._data[byteAddrTemp + 2], ssbo_1._data[byteAddrTemp + 3]); +} + +void _main(uvec3 id) +{ + uint param = 4u; + Load(param); +} + +void main() +{ + uvec3 id = gl_GlobalInvocationID; + uvec3 param = id; + _main(param); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/hlsl-functionality.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/hlsl-functionality.asm.comp new file mode 100644 index 0000000..e80f524 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/hlsl-functionality.asm.comp @@ -0,0 +1,24 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer Buf +{ + vec4 _data[]; +} Buf_1; + +layout(binding = 1, std430) buffer Buf_count +{ + int _count; +} Buf_count_1; + +void _main() +{ + int _29 = atomicAdd(Buf_count_1._count, 1); + Buf_1._data[_29] = vec4(1.0); +} + +void main() +{ + _main(); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/logical.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/logical.asm.comp new file mode 100644 index 0000000..9ae25f7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/logical.asm.comp @@ -0,0 +1,56 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + float a; + vec2 b; + vec3 c; + vec4 d; +} s0; + +layout(binding = 1, std430) buffer SSBO1 +{ + float a; + vec2 b; + vec3 c; + vec4 d; +} s1; + +bool and(bool a, bool b) +{ + return !((a && b) || b); +} + +bvec2 and(bvec2 a, bvec2 b) +{ + bvec2 _98 = bvec2(a.x && b.x, a.y && b.y); + return not(bvec2(_98.x || b.x, _98.y || b.y)); +} + +bvec3 and(bvec3 a, bvec3 b) +{ + return bvec3(a.x && b.x, a.y && b.y, a.z && b.z); +} + +bvec4 and(bvec4 a, bvec4 b) +{ + return bvec4(a.x && b.x, a.y && b.y, a.z && b.z, a.w && b.w); +} + +void main() +{ + bool param = isinf(s0.a); + bool param_1 = isnan(s1.a); + bool b0 = and(param, param_1); + bvec2 param_2 = isinf(s0.b); + bvec2 param_3 = isnan(s1.b); + bvec2 b1 = and(param_2, param_3); + bvec3 param_4 = isinf(s0.c); + bvec3 param_5 = isnan(s1.c); + bvec3 b2 = and(param_4, param_5); + bvec4 param_6 = isinf(s0.d); + bvec4 param_7 = isnan(s1.d); + bvec4 b3 = and(param_6, param_7); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/multiple-entry.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/multiple-entry.asm.comp new file mode 100644 index 0000000..6418464 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/multiple-entry.asm.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) restrict buffer _6_8 +{ + ivec4 _m0; + uvec4 _m1; +} _8; + +layout(binding = 1, std430) restrict buffer _7_9 +{ + uvec4 _m0; + ivec4 _m1; +} _9; + +void main() +{ + _9._m0 = _8._m1 + uvec4(_8._m0); + _9._m0 = uvec4(_8._m0) + _8._m1; + _9._m0 = _8._m1 + _8._m1; + _9._m0 = uvec4(_8._m0 + _8._m0); + _9._m1 = ivec4(_8._m1 + _8._m1); + _9._m1 = _8._m0 + _8._m0; + _9._m1 = ivec4(_8._m1) + _8._m0; + _9._m1 = _8._m0 + ivec4(_8._m1); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/nmin-max-clamp.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/nmin-max-clamp.asm.comp new file mode 100644 index 0000000..54c452c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/nmin-max-clamp.asm.comp @@ -0,0 +1,44 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float a1; + vec2 a2; + vec3 a3; + vec4 a4; + float b1; + vec2 b2; + vec3 b3; + vec4 b4; + float c1; + vec2 c2; + vec3 c3; + vec4 c4; +} _4; + +void main() +{ + _4.a1 = isnan(_4.c1) ? _4.b1 : (isnan(_4.b1) ? _4.c1 : min(_4.b1, _4.c1)); + _4.a2 = mix(mix(min(_4.b2, _4.c2), _4.c2, isnan(_4.b2)), _4.b2, isnan(_4.c2)); + _4.a3 = mix(mix(min(_4.b3, _4.c3), _4.c3, isnan(_4.b3)), _4.b3, isnan(_4.c3)); + _4.a4 = mix(mix(min(_4.b4, _4.c4), _4.c4, isnan(_4.b4)), _4.b4, isnan(_4.c4)); + _4.a1 = isnan(_4.c1) ? _4.b1 : (isnan(_4.b1) ? _4.c1 : max(_4.b1, _4.c1)); + _4.a2 = mix(mix(max(_4.b2, _4.c2), _4.c2, isnan(_4.b2)), _4.b2, isnan(_4.c2)); + _4.a3 = mix(mix(max(_4.b3, _4.c3), _4.c3, isnan(_4.b3)), _4.b3, isnan(_4.c3)); + _4.a4 = mix(mix(max(_4.b4, _4.c4), _4.c4, isnan(_4.b4)), _4.b4, isnan(_4.c4)); + float _179 = isnan(_4.b1) ? _4.a1 : (isnan(_4.a1) ? _4.b1 : max(_4.a1, _4.b1)); + _4.a1 = isnan(_4.c1) ? _179 : (isnan(_179) ? _4.c1 : min(_179, _4.c1)); + vec2 _190 = mix(mix(max(_4.a2, _4.b2), _4.b2, isnan(_4.a2)), _4.a2, isnan(_4.b2)); + _4.a2 = mix(mix(min(_190, _4.c2), _4.c2, isnan(_190)), _190, isnan(_4.c2)); + vec3 _201 = mix(mix(max(_4.a3, _4.b3), _4.b3, isnan(_4.a3)), _4.a3, isnan(_4.b3)); + _4.a3 = mix(mix(min(_201, _4.c3), _4.c3, isnan(_201)), _201, isnan(_4.c3)); + vec4 _212 = mix(mix(max(_4.a4, _4.b4), _4.b4, isnan(_4.a4)), _4.a4, isnan(_4.b4)); + _4.a4 = mix(mix(min(_212, _4.c4), _4.c4, isnan(_212)), _212, isnan(_4.c4)); + float _223; + for (int i = 0; i < 2; i++, _223 = isnan(_4.b2.x) ? _4.a1 : (isnan(_4.a1) ? _4.b2.x : max(_4.a1, _4.b2.x)), _4.a1 = isnan(_4.b2.y) ? _223 : (isnan(_223) ? _4.b2.y : min(_223, _4.b2.y))) + { + _4.a2 = mix(mix(min(_4.b2, _4.c2), _4.c2, isnan(_4.b2)), _4.b2, isnan(_4.c2)); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/op-phi-swap.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/op-phi-swap.asm.comp new file mode 100644 index 0000000..f8c51c0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/op-phi-swap.asm.comp @@ -0,0 +1,40 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer _3_4 +{ + float _m0[]; +} _4; + +layout(binding = 1, std430) buffer _3_5 +{ + float _m0[]; +} _5; + +void main() +{ + float _26 = 8.5; + bool _34; + float _35; + float _35_copy; + float _36; + _34 = true; + _35 = _4._m0[gl_GlobalInvocationID.x]; + _36 = _26; + for (;;) + { + if (_34) + { + _34 = false; + _35_copy = _35; + _35 = _36; + _36 = _35_copy; + } + else + { + break; + } + } + _5._m0[gl_GlobalInvocationID.x] = _35 - _36; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/quantize.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/quantize.asm.comp new file mode 100644 index 0000000..c089213 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/quantize.asm.comp @@ -0,0 +1,19 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + float scalar; + vec2 vec2_val; + vec3 vec3_val; + vec4 vec4_val; +} _4; + +void main() +{ + _4.scalar = unpackHalf2x16(packHalf2x16(vec2(_4.scalar))).x; + _4.vec2_val = unpackHalf2x16(packHalf2x16(_4.vec2_val)); + _4.vec3_val = vec3(unpackHalf2x16(packHalf2x16(_4.vec3_val.xy)), unpackHalf2x16(packHalf2x16(_4.vec3_val.zz)).x); + _4.vec4_val = vec4(unpackHalf2x16(packHalf2x16(_4.vec4_val.xy)), unpackHalf2x16(packHalf2x16(_4.vec4_val.zw))); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/recompile-block-naming.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/recompile-block-naming.asm.comp new file mode 100644 index 0000000..aeb1ed1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/recompile-block-naming.asm.comp @@ -0,0 +1,36 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer MyFirstBuffer +{ + uint _data[]; +} MyFirstBuffer_1; + +layout(binding = 0, std430) buffer MySecondBuffer +{ + uint _data[]; +} MySecondBuffer_1; + +layout(binding = 0, std430) buffer MyThirdBuffer +{ + uint _data[]; +} MyThirdBuffer_1; + +void _main() +{ + int byteAddrTemp = 0 >> 2; + uvec4 a = uvec4(MyFirstBuffer_1._data[byteAddrTemp], MyFirstBuffer_1._data[byteAddrTemp + 1], MyFirstBuffer_1._data[byteAddrTemp + 2], MyFirstBuffer_1._data[byteAddrTemp + 3]); + int byteAddrTemp_1 = 4 >> 2; + uvec4 b = uvec4(MySecondBuffer_1._data[byteAddrTemp_1], MySecondBuffer_1._data[byteAddrTemp_1 + 1], MySecondBuffer_1._data[byteAddrTemp_1 + 2], MySecondBuffer_1._data[byteAddrTemp_1 + 3]); + int byteAddrTemp_2 = 0 >> 2; + MyThirdBuffer_1._data[byteAddrTemp_2] = (a + b).x; + MyThirdBuffer_1._data[byteAddrTemp_2 + 1] = (a + b).y; + MyThirdBuffer_1._data[byteAddrTemp_2 + 2] = (a + b).z; + MyThirdBuffer_1._data[byteAddrTemp_2 + 3] = (a + b).w; +} + +void main() +{ + _main(); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/specialization-constant-workgroup.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/specialization-constant-workgroup.asm.comp new file mode 100644 index 0000000..8016eba --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/specialization-constant-workgroup.asm.comp @@ -0,0 +1,21 @@ +#version 310 es + +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 9u +#endif +#ifndef SPIRV_CROSS_CONSTANT_ID_12 +#define SPIRV_CROSS_CONSTANT_ID_12 4u +#endif + +layout(local_size_x = SPIRV_CROSS_CONSTANT_ID_10, local_size_y = 20, local_size_z = SPIRV_CROSS_CONSTANT_ID_12) in; + +layout(binding = 0, std430) buffer SSBO +{ + float a; +} _4; + +void main() +{ + _4.a += 1.0; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/comp/switch-break-ladder.asm.comp b/third_party/spirv-cross/reference/shaders/asm/comp/switch-break-ladder.asm.comp new file mode 100644 index 0000000..f326869 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/comp/switch-break-ladder.asm.comp @@ -0,0 +1,64 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer BUF +{ + int a; + int b; + int d; +} o; + +void main() +{ + int c = o.a; + int a; + for (;;) + { + bool _22_ladder_break = false; + switch (c) + { + case 5: + { + for (;;) + { + bool _30_ladder_break = false; + switch (o.d) + { + case 10: + case 20: + { + c += c; + _30_ladder_break = true; + break; + } + default: + { + continue; + } + } + if (_30_ladder_break) + { + break; + } + } + break; + } + case 1: + case 2: + case 3: + { + a = c; + _22_ladder_break = true; + break; + } + } + if (_22_ladder_break) + { + break; + } + c++; + continue; + } + o.b = a; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag new file mode 100644 index 0000000..b5e59f8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombineduTexuSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; + +void main() +{ + FragColor = texture(SPIRV_Cross_CombineduTexuSampler, vUV); + FragColor += textureOffset(SPIRV_Cross_CombineduTexuSampler, vUV, ivec2(1)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag.vk b/third_party/spirv-cross/reference/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag.vk new file mode 100644 index 0000000..bce9808 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(set = 0, binding = 1) uniform texture2D uTex; +layout(set = 0, binding = 0) uniform sampler uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; + +void main() +{ + FragColor = texture(sampler2D(uTex, uSampler), vUV); + FragColor += textureOffset(sampler2D(uTex, uSampler), vUV, ivec2(1)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/complex-name-workarounds.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/complex-name-workarounds.asm.frag new file mode 100644 index 0000000..7b12071 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/complex-name-workarounds.asm.frag @@ -0,0 +1,28 @@ +#version 450 + +layout(location = 0) in vec4 _; +layout(location = 1) in vec4 a; +layout(location = 0) out vec4 b; + +vec4 fu_nc_(vec4 a_) +{ + return a_; +} + +vec4 fu_nc_1(vec4 _0_1) +{ + return _0_1; +} + +void main() +{ + vec4 b_1 = _; + vec4 _0_1 = (_ + a) + fu_nc_(b_1); + vec4 b_3 = a; + vec4 b_2 = (_ - a) + fu_nc_1(b_3); + b = _0_1; + b = b_2; + b = _0_1; + b = b_2; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag new file mode 100644 index 0000000..c64818d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct SwizzleTest +{ + float a; + float b; +}; + +layout(location = 0) in vec2 foo; +layout(location = 0) out float FooOut; + +void main() +{ + SwizzleTest _22 = SwizzleTest(foo.x, foo.y); + FooOut = _22.a + _22.b; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/default-member-names.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/default-member-names.asm.frag new file mode 100644 index 0000000..57d4536 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/default-member-names.asm.frag @@ -0,0 +1,32 @@ +#version 450 + +struct _9 +{ + float _m0; +}; + +struct _10 +{ + float _m0; + float _m1; + float _m2; + float _m3; + float _m4; + float _m5; + float _m6; + float _m7; + float _m8; + float _m9; + float _m10; + float _m11; + _9 _m12; +}; + +layout(location = 0) out vec4 _3; + +void main() +{ + _10 _21; + _3 = vec4(_21._m0, _21._m1, _21._m2, _21._m3); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/do-while-statement-fallback.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/do-while-statement-fallback.asm.frag new file mode 100644 index 0000000..0e6947a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/do-while-statement-fallback.asm.frag @@ -0,0 +1,58 @@ +#version 450 + +layout(location = 0) out float FragColor; + +void main() +{ + float foo = 1.0; + for (;;) + { + foo = 2.0; + if (false) + { + continue; + } + else + { + break; + } + } + for (;;) + { + foo = 3.0; + if (false) + { + continue; + } + else + { + break; + } + } + for (;;) + { + foo = 4.0; + if (false) + { + continue; + } + else + { + break; + } + } + for (;;) + { + foo = 5.0; + if (false) + { + continue; + } + else + { + break; + } + } + FragColor = foo; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..7c9d393 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/empty-struct.asm.frag @@ -0,0 +1,25 @@ +#version 450 + +struct EmptyStructTest +{ + int empty_struct_member; +}; + +float GetValue(EmptyStructTest self) +{ + return 0.0; +} + +float GetValue_1(EmptyStructTest self) +{ + return 0.0; +} + +void main() +{ + EmptyStructTest _23 = EmptyStructTest(0); + EmptyStructTest emptyStruct; + float value = GetValue(emptyStruct); + value = GetValue_1(_23); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/for-loop-phi-only-continue.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/for-loop-phi-only-continue.asm.frag new file mode 100644 index 0000000..3101142 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/for-loop-phi-only-continue.asm.frag @@ -0,0 +1,18 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + float _19; + _19 = 0.0; + float _20; + int _23; + for (int _22 = 0; _22 < 16; _19 = _20, _22 = _23) + { + _20 = _19 + 1.0; + _23 = _22 + 1; + } + FragColor = vec4(_19); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/frem.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/frem.asm.frag new file mode 100644 index 0000000..1095ab0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/frem.asm.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; + +void main() +{ + FragColor = vA - vB * trunc(vA / vB); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..676f986 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,39 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; + +vec4 foo(vec4 foo_1) +{ + return foo_1 + vec4(1.0); +} + +vec4 foo(vec3 foo_1) +{ + return foo_1.xyzz + vec4(1.0); +} + +vec4 foo_1(vec4 foo_2) +{ + return foo_2 + vec4(2.0); +} + +vec4 foo(vec2 foo_2) +{ + return foo_2.xyxy + vec4(2.0); +} + +void main() +{ + highp vec4 foo_3 = vec4(1.0); + vec4 foo_2 = foo(foo_3); + highp vec3 foo_5 = vec3(1.0); + vec4 foo_4 = foo(foo_5); + highp vec4 foo_7 = vec4(1.0); + vec4 foo_6 = foo_1(foo_7); + highp vec2 foo_9 = vec2(1.0); + vec4 foo_8 = foo(foo_9); + FragColor = ((foo_2 + foo_4) + foo_6) + foo_8; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag new file mode 100644 index 0000000..3585285 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag @@ -0,0 +1,17 @@ +#version 450 + +uniform samplerCubeShadow SPIRV_Cross_CombinedpointLightShadowMapshadowSamplerPCF; + +layout(location = 0) out float _entryPointOutput; + +float _main() +{ + vec4 _33 = vec4(vec3(0.100000001490116119384765625), 0.5); + return textureGrad(SPIRV_Cross_CombinedpointLightShadowMapshadowSamplerPCF, vec4(_33.xyz, _33.w), vec3(0.0), vec3(0.0)); +} + +void main() +{ + _entryPointOutput = _main(); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag new file mode 100644 index 0000000..63856dd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag @@ -0,0 +1,27 @@ +#version 450 + +uniform sampler2DArrayShadow SPIRV_Cross_CombinedShadowMapShadowSamplerPCF; + +layout(location = 0) in vec2 texCoords; +layout(location = 1) in float cascadeIndex; +layout(location = 2) in float fragDepth; +layout(location = 0) out vec4 _entryPointOutput; + +vec4 _main(vec2 texCoords_1, float cascadeIndex_1, float fragDepth_1) +{ + vec4 _60 = vec4(vec3(texCoords_1, cascadeIndex_1), fragDepth_1); + float c = textureGrad(SPIRV_Cross_CombinedShadowMapShadowSamplerPCF, vec4(_60.xyz, _60.w), vec2(0.0), vec2(0.0)); + return vec4(c, c, c, c); +} + +void main() +{ + vec2 texCoords_1 = texCoords; + float cascadeIndex_1 = cascadeIndex; + float fragDepth_1 = fragDepth; + vec2 param = texCoords_1; + float param_1 = cascadeIndex_1; + float param_2 = fragDepth_1; + _entryPointOutput = _main(param, param_1, param_2); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..ab2749b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uTexture; + +layout(location = 0) out ivec2 Size; + +void main() +{ + Size = textureSize(uTexture, 0) + textureSize(uTexture, 1); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag b/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag new file mode 100644 index 0000000..60bb78a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag @@ -0,0 +1,38 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombinedparamSPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_CombinedparamSampler; +uniform sampler2D SPIRV_Cross_CombinedSampledImageSampler; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 sample_fetch(ivec3 UV, sampler2D SPIRV_Cross_CombinedtexSPIRV_Cross_DummySampler) +{ + return texelFetch(SPIRV_Cross_CombinedtexSPIRV_Cross_DummySampler, UV.xy, UV.z); +} + +vec4 sample_sampler(vec2 UV, sampler2D SPIRV_Cross_CombinedtexSampler) +{ + return texture(SPIRV_Cross_CombinedtexSampler, UV); +} + +vec4 _main(vec4 xIn) +{ + ivec3 coord = ivec3(int(xIn.x * 1280.0), int(xIn.y * 720.0), 0); + ivec3 param = coord; + vec4 value = sample_fetch(param, SPIRV_Cross_CombinedparamSPIRV_Cross_DummySampler); + value += texelFetch(SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler, coord.xy, coord.z); + vec2 param_1 = xIn.xy; + value += sample_sampler(param_1, SPIRV_Cross_CombinedparamSampler); + value += texture(SPIRV_Cross_CombinedSampledImageSampler, xIn.xy); + return value; +} + +void main() +{ + vec4 xIn = gl_FragCoord; + vec4 param = xIn; + _entryPointOutput = _main(param); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag.vk b/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag.vk new file mode 100644 index 0000000..2dab664 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag.vk @@ -0,0 +1,37 @@ +#version 450 +#extension GL_EXT_samplerless_texture_functions : require + +layout(set = 0, binding = 0) uniform sampler Sampler; +layout(set = 0, binding = 0) uniform texture2D SampledImage; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 sample_fetch(texture2D tex, ivec3 UV) +{ + return texelFetch(tex, UV.xy, UV.z); +} + +vec4 sample_sampler(texture2D tex, vec2 UV) +{ + return texture(sampler2D(tex, Sampler), UV); +} + +vec4 _main(vec4 xIn) +{ + ivec3 coord = ivec3(int(xIn.x * 1280.0), int(xIn.y * 720.0), 0); + ivec3 param = coord; + vec4 value = sample_fetch(SampledImage, param); + value += texelFetch(SampledImage, coord.xy, coord.z); + vec2 param_1 = xIn.xy; + value += sample_sampler(SampledImage, param_1); + value += texture(sampler2D(SampledImage, Sampler), xIn.xy); + return value; +} + +void main() +{ + vec4 xIn = gl_FragCoord; + vec4 param = xIn; + _entryPointOutput = _main(param); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag b/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag new file mode 100644 index 0000000..60bb78a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag @@ -0,0 +1,38 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombinedparamSPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler; +uniform sampler2D SPIRV_Cross_CombinedparamSampler; +uniform sampler2D SPIRV_Cross_CombinedSampledImageSampler; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 sample_fetch(ivec3 UV, sampler2D SPIRV_Cross_CombinedtexSPIRV_Cross_DummySampler) +{ + return texelFetch(SPIRV_Cross_CombinedtexSPIRV_Cross_DummySampler, UV.xy, UV.z); +} + +vec4 sample_sampler(vec2 UV, sampler2D SPIRV_Cross_CombinedtexSampler) +{ + return texture(SPIRV_Cross_CombinedtexSampler, UV); +} + +vec4 _main(vec4 xIn) +{ + ivec3 coord = ivec3(int(xIn.x * 1280.0), int(xIn.y * 720.0), 0); + ivec3 param = coord; + vec4 value = sample_fetch(param, SPIRV_Cross_CombinedparamSPIRV_Cross_DummySampler); + value += texelFetch(SPIRV_Cross_CombinedSampledImageSPIRV_Cross_DummySampler, coord.xy, coord.z); + vec2 param_1 = xIn.xy; + value += sample_sampler(param_1, SPIRV_Cross_CombinedparamSampler); + value += texture(SPIRV_Cross_CombinedSampledImageSampler, xIn.xy); + return value; +} + +void main() +{ + vec4 xIn = gl_FragCoord; + vec4 param = xIn; + _entryPointOutput = _main(param); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag.vk b/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag.vk new file mode 100644 index 0000000..e4d9fc4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag.vk @@ -0,0 +1,37 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler Sampler; +layout(set = 0, binding = 0) uniform texture2D SampledImage; +layout(set = 0, binding = 0) uniform sampler SPIRV_Cross_DummySampler; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 sample_fetch(texture2D tex, ivec3 UV) +{ + return texelFetch(sampler2D(tex, SPIRV_Cross_DummySampler), UV.xy, UV.z); +} + +vec4 sample_sampler(texture2D tex, vec2 UV) +{ + return texture(sampler2D(tex, Sampler), UV); +} + +vec4 _main(vec4 xIn) +{ + ivec3 coord = ivec3(int(xIn.x * 1280.0), int(xIn.y * 720.0), 0); + ivec3 param = coord; + vec4 value = sample_fetch(SampledImage, param); + value += texelFetch(sampler2D(SampledImage, SPIRV_Cross_DummySampler), coord.xy, coord.z); + vec2 param_1 = xIn.xy; + value += sample_sampler(SampledImage, param_1); + value += texture(sampler2D(SampledImage, Sampler), xIn.xy); + return value; +} + +void main() +{ + vec4 xIn = gl_FragCoord; + vec4 param = xIn; + _entryPointOutput = _main(param); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag new file mode 100644 index 0000000..2040dd1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombineduSampler2DSPIRV_Cross_DummySampler; +uniform sampler2DMS SPIRV_Cross_CombineduSampler2DMSSPIRV_Cross_DummySampler; + +void main() +{ + ivec2 b = textureSize(SPIRV_Cross_CombineduSampler2DSPIRV_Cross_DummySampler, 0); + ivec2 c = textureSize(SPIRV_Cross_CombineduSampler2DMSSPIRV_Cross_DummySampler); + int l1 = textureQueryLevels(SPIRV_Cross_CombineduSampler2DSPIRV_Cross_DummySampler); + int s0 = textureSamples(SPIRV_Cross_CombineduSampler2DMSSPIRV_Cross_DummySampler); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag.vk b/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag.vk new file mode 100644 index 0000000..828d2a8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(set = 0, binding = 0) uniform texture2D uSampler2D; +layout(set = 0, binding = 0) uniform texture2DMS uSampler2DMS; +layout(set = 0, binding = 0) uniform sampler SPIRV_Cross_DummySampler; + +void main() +{ + ivec2 b = textureSize(sampler2D(uSampler2D, SPIRV_Cross_DummySampler), 0); + ivec2 c = textureSize(sampler2DMS(uSampler2DMS, SPIRV_Cross_DummySampler)); + int l1 = textureQueryLevels(sampler2D(uSampler2D, SPIRV_Cross_DummySampler)); + int s0 = textureSamples(sampler2DMS(uSampler2DMS, SPIRV_Cross_DummySampler)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.vk.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.vk.asm.frag new file mode 100644 index 0000000..2040dd1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.vk.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_CombineduSampler2DSPIRV_Cross_DummySampler; +uniform sampler2DMS SPIRV_Cross_CombineduSampler2DMSSPIRV_Cross_DummySampler; + +void main() +{ + ivec2 b = textureSize(SPIRV_Cross_CombineduSampler2DSPIRV_Cross_DummySampler, 0); + ivec2 c = textureSize(SPIRV_Cross_CombineduSampler2DMSSPIRV_Cross_DummySampler); + int l1 = textureQueryLevels(SPIRV_Cross_CombineduSampler2DSPIRV_Cross_DummySampler); + int s0 = textureSamples(SPIRV_Cross_CombineduSampler2DMSSPIRV_Cross_DummySampler); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.vk.asm.frag.vk b/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.vk.asm.frag.vk new file mode 100644 index 0000000..021d3a6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/image-query-no-sampler.vk.asm.frag.vk @@ -0,0 +1,14 @@ +#version 450 +#extension GL_EXT_samplerless_texture_functions : require + +layout(set = 0, binding = 0) uniform texture2D uSampler2D; +layout(set = 0, binding = 0) uniform texture2DMS uSampler2DMS; + +void main() +{ + ivec2 b = textureSize(uSampler2D, 0); + ivec2 c = textureSize(uSampler2DMS); + int l1 = textureQueryLevels(uSampler2D); + int s0 = textureSamples(uSampler2DMS); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..6bc1be0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,39 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uImage; + +layout(location = 0) in vec4 v0; +layout(location = 0) out vec4 FragColor; + +void main() +{ + int i = 0; + float phi; + vec4 _36; + phi = 1.0; + _36 = vec4(1.0, 2.0, 1.0, 2.0); + for (;;) + { + FragColor = _36; + if (i < 4) + { + if (v0[i] > 0.0) + { + vec2 _48 = vec2(phi); + i++; + phi += 2.0; + _36 = textureLod(uImage, _48, 0.0); + continue; + } + else + { + break; + } + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/inf-nan-constant-double.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/inf-nan-constant-double.asm.frag new file mode 100644 index 0000000..d8e29aa --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/inf-nan-constant-double.asm.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require + +layout(location = 0) out vec3 FragColor; +layout(location = 0) flat in double vTmp; + +void main() +{ + FragColor = vec3(dvec3(uint64BitsToDouble(0x7ff0000000000000ul), uint64BitsToDouble(0xfff0000000000000ul), uint64BitsToDouble(0x7ff8000000000000ul)) + dvec3(vTmp)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..dd4284c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out highp vec3 FragColor; + +void main() +{ + FragColor = vec3(uintBitsToFloat(0x7f800000u), uintBitsToFloat(0xff800000u), uintBitsToFloat(0x7fc00000u)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/invalidation.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/invalidation.asm.frag new file mode 100644 index 0000000..db11818 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/invalidation.asm.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in float v0; +layout(location = 1) in float v1; +layout(location = 0) out float FragColor; + +void main() +{ + float a = v0; + float b = v1; + float _17 = a; + a = v1; + FragColor = (_17 + b) * b; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..35aa9f8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,71 @@ +#version 450 +#extension GL_GOOGLE_cpp_style_line_directive : require + +layout(location = 0) out float FragColor; +layout(location = 0) in float vColor; + +#line 6 "test.frag" +void func() +{ +#line 8 "test.frag" + FragColor = 1.0; +#line 9 "test.frag" + FragColor = 2.0; +#line 10 "test.frag" + if (vColor < 0.0) + { +#line 12 "test.frag" + FragColor = 3.0; + } + else + { +#line 16 "test.frag" + FragColor = 4.0; + } +#line 19 "test.frag" + for (int i = 0; float(i) < (40.0 + vColor); i += (int(vColor) + 5)) + { +#line 21 "test.frag" + FragColor += 0.20000000298023223876953125; +#line 22 "test.frag" + FragColor += 0.300000011920928955078125; + } +#line 25 "test.frag" + switch (int(vColor)) + { + case 0: + { +#line 28 "test.frag" + FragColor += 0.20000000298023223876953125; +#line 29 "test.frag" + break; + } + case 1: + { +#line 32 "test.frag" + FragColor += 0.4000000059604644775390625; +#line 33 "test.frag" + break; + } + default: + { +#line 36 "test.frag" + FragColor += 0.800000011920928955078125; +#line 37 "test.frag" + break; + } + } + do + { +#line 42 "test.frag" + FragColor += (10.0 + vColor); + } while (FragColor < 100.0); +} + +#line 46 "test.frag" +void main() +{ +#line 48 "test.frag" + func(); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/locations-components.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/locations-components.asm.frag new file mode 100644 index 0000000..95dcd9c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/locations-components.asm.frag @@ -0,0 +1,25 @@ +#version 450 + +layout(location = 1) in vec2 _2; +layout(location = 1, component = 2) in float _3; +layout(location = 2) flat in float _4; +layout(location = 2, component = 1) flat in uint _5; +layout(location = 2, component = 2) flat in uint _6; +layout(location = 0) out vec4 o0; +vec4 v1; +vec4 v2; + +void main() +{ + v1 = vec4(_2.x, _2.y, v1.z, v1.w); + v1.z = _3; + v2.x = _4; + v2.y = uintBitsToFloat(_5); + v2.z = uintBitsToFloat(_6); + vec4 r0; + r0.x = intBitsToFloat(floatBitsToInt(v2.y) + floatBitsToInt(v2.z)); + o0.y = float(floatBitsToUint(r0.x)); + o0.x = v1.y + v2.x; + o0 = vec4(o0.x, o0.y, v1.z, v1.x); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag new file mode 100644 index 0000000..4302113 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag @@ -0,0 +1,50 @@ +#version 450 + +layout(binding = 0, std140) uniform Foo +{ + layout(row_major) mat4 lightVP[64]; + uint shadowCascadesNum; + int test; +} _11; + +layout(location = 0) in vec3 fragWorld; +layout(location = 0) out int _entryPointOutput; + +mat4 SPIRV_Cross_workaround_load_row_major(mat4 wrap) { return wrap; } + +mat4 GetClip2TexMatrix() +{ + if (_11.test == 0) + { + return mat4(vec4(0.5, 0.0, 0.0, 0.0), vec4(0.0, 0.5, 0.0, 0.0), vec4(0.0, 0.0, 0.5, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + } + return mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); +} + +int GetCascade(vec3 fragWorldPosition) +{ + for (uint cascadeIndex = 0u; cascadeIndex < _11.shadowCascadesNum; cascadeIndex++) + { + mat4 worldToShadowMap = GetClip2TexMatrix() * SPIRV_Cross_workaround_load_row_major(_11.lightVP[cascadeIndex]); + vec4 fragShadowMapPos = worldToShadowMap * vec4(fragWorldPosition, 1.0); + if ((((fragShadowMapPos.z >= 0.0) && (fragShadowMapPos.z <= 1.0)) && (max(fragShadowMapPos.x, fragShadowMapPos.y) <= 1.0)) && (min(fragShadowMapPos.x, fragShadowMapPos.y) >= 0.0)) + { + return int(cascadeIndex); + } + } + return -1; +} + +int _main(vec3 fragWorld_1) +{ + vec3 param = fragWorld_1; + return GetCascade(param); +} + +void main() +{ + vec3 fragWorld_1 = fragWorld; + vec3 param = fragWorld_1; + _entryPointOutput = _main(param); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/loop-header-to-continue.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/loop-header-to-continue.asm.frag new file mode 100644 index 0000000..8a3b664 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/loop-header-to-continue.asm.frag @@ -0,0 +1,45 @@ +#version 450 + +struct Params +{ + vec4 TextureSize; + vec4 Params1; + vec4 Params2; + vec4 Params3; + vec4 Params4; + vec4 Bloom; +}; + +layout(binding = 1, std140) uniform CB1 +{ + Params CB1; +} _8; + +uniform sampler2D SPIRV_Cross_CombinedmapTexturemapSampler; + +layout(location = 0) in vec2 IN_uv; +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + vec2 _45 = vec2(0.0, _8.CB1.TextureSize.w); + vec4 _49 = texture(SPIRV_Cross_CombinedmapTexturemapSampler, IN_uv); + float _50 = _49.y; + float _53 = clamp((_50 * 80.0) * 0.0007999999797903001308441162109375, 7.999999797903001308441162109375e-05, 0.008000000379979610443115234375); + float _55; + float _58; + _55 = 0.0; + _58 = 0.0; + for (int _60 = -3; _60 <= 3; ) + { + float _64 = float(_60); + vec4 _72 = texture(SPIRV_Cross_CombinedmapTexturemapSampler, IN_uv + (_45 * _64)); + float _78 = exp(((-_64) * _64) * 0.2222220003604888916015625) * float(abs(_72.y - _50) < _53); + _55 += (_72.x * _78); + _58 += _78; + _60++; + continue; + } + _entryPointOutput = vec4(_55 / _58, _50, 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..c08bc2c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,40 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _46[16] = float[](1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0); +const vec4 _76[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + +layout(location = 0) out float FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + vec4 foobar[4] = _76; + vec4 baz[4] = _76; + FragColor = _46[index]; + if (index < 10) + { + FragColor += _46[index ^ 1]; + } + else + { + FragColor += _46[index & 1]; + } + if (index > 30) + { + FragColor += _76[index & 3].y; + } + else + { + FragColor += _76[index & 1].x; + } + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/multi-for-loop-init.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/multi-for-loop-init.asm.frag new file mode 100644 index 0000000..b5f30a2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/multi-for-loop-init.asm.frag @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int counter; + +void main() +{ + FragColor = vec4(0.0); + mediump int i = 0; + mediump uint j = 1u; + for (; (i < 10) && (int(j) < int(20u)); i += counter, j += uint(counter)) + { + FragColor += vec4(float(i)); + FragColor += vec4(float(j)); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/op-constant-null.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/op-constant-null.asm.frag new file mode 100644 index 0000000..f293c18 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/op-constant-null.asm.frag @@ -0,0 +1,23 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct D +{ + vec4 a; + float b; +}; + +const vec4 _14[4] = vec4[](vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); + +layout(location = 0) out float FragColor; + +void main() +{ + float a = 0.0; + vec4 b = vec4(0.0); + mat2x3 c = mat2x3(vec3(0.0), vec3(0.0)); + D d = D(vec4(0.0), 0.0); + FragColor = a; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/op-phi-swap-continue-block.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/op-phi-swap-continue-block.asm.frag new file mode 100644 index 0000000..d62b63a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/op-phi-swap-continue-block.asm.frag @@ -0,0 +1,24 @@ +#version 450 + +layout(binding = 0, std140) uniform UBO +{ + int uCount; + int uJ; + int uK; +} _5; + +layout(location = 0) out float FragColor; + +void main() +{ + int _23; + int _23_copy; + int _24; + _23 = _5.uK; + _24 = _5.uJ; + for (int _26 = 0; _26 < _5.uCount; _23_copy = _23, _23 = _24, _24 = _23_copy, _26++) + { + } + FragColor = float(_24 - _23) * float(_5.uJ * _5.uK); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/pack-and-unpack-uint2.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/pack-and-unpack-uint2.asm.frag new file mode 100644 index 0000000..d5225f2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/pack-and-unpack-uint2.asm.frag @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require + +layout(location = 0) out vec4 FragColor; + +void main() +{ + uint64_t _packed = packUint2x32(uvec2(18u, 52u)); + uvec2 unpacked = unpackUint2x32(_packed); + FragColor = vec4(float(unpacked.x), float(unpacked.y), 1.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..35ba2e1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,21 @@ +#version 450 + +struct Registers +{ + float foo; +}; + +uniform Registers registers; + +layout(location = 0) out float FragColor; + +float add_value(float v, float w) +{ + return v + w; +} + +void main() +{ + FragColor = add_value(10.0, registers.foo); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/phi-loop-variable.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/phi-loop-variable.asm.frag new file mode 100644 index 0000000..786ac74 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/phi-loop-variable.asm.frag @@ -0,0 +1,9 @@ +#version 450 + +void main() +{ + for (int _22 = 35; _22 >= 0; _22--) + { + } +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..d6489ed --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,13 @@ +#version 450 + +uniform sampler2D SPIRV_Cross_Combinedg_Textureg_Sampler; +uniform sampler2DShadow SPIRV_Cross_Combinedg_Textureg_CompareSampler; + +layout(location = 0) in vec2 in_var_TEXCOORD0; +layout(location = 0) out float out_var_SV_Target; + +void main() +{ + out_var_SV_Target = texture(SPIRV_Cross_Combinedg_Textureg_Sampler, in_var_TEXCOORD0).x + textureLod(SPIRV_Cross_Combinedg_Textureg_CompareSampler, vec3(in_var_TEXCOORD0, 0.5), 0.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag new file mode 100644 index 0000000..3dc1839 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag @@ -0,0 +1,28 @@ +#version 450 + +struct Registers +{ + int index; +}; + +uniform Registers registers; + +uniform sampler2D SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler[4]; + +layout(location = 0) out vec4 FragColor; + +vec4 sample_from_func(sampler2D SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler_1[4]) +{ + return texelFetch(SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler_1[registers.index], ivec2(4), 0); +} + +vec4 sample_one_from_func(sampler2D SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler_1) +{ + return texelFetch(SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler_1, ivec2(4), 0); +} + +void main() +{ + FragColor = (texelFetch(SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler[registers.index], ivec2(10), 0) + sample_from_func(SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler)) + sample_one_from_func(SPIRV_Cross_CombineduSamplerSPIRV_Cross_DummySampler[registers.index]); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag new file mode 100644 index 0000000..1ebf8fb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag @@ -0,0 +1,20 @@ +#version 450 + +layout(binding = 0, rgba32f) uniform writeonly imageBuffer RWTex; +layout(binding = 1) uniform samplerBuffer Tex; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 _main() +{ + vec4 storeTemp = vec4(1.0, 2.0, 3.0, 4.0); + imageStore(RWTex, 20, storeTemp); + return texelFetch(Tex, 10); +} + +void main() +{ + vec4 _28 = _main(); + _entryPointOutput = _28; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..9c45d85 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,26 @@ +#version 460 + +struct myType +{ + float data; +}; + +const myType _21[5] = myType[](myType(0.0), myType(1.0), myType(0.0), myType(1.0), myType(0.0)); + +layout(location = 0) out vec4 o_color; + +void main() +{ + vec2 uv = gl_FragCoord.xy; + int index = int(mod(uv.x, 4.0)); + myType elt = _21[index]; + if (elt.data > 0.0) + { + o_color = vec4(0.0, 1.0, 0.0, 1.0); + } + else + { + o_color = vec4(1.0, 0.0, 0.0, 1.0); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/srem.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/srem.asm.frag new file mode 100644 index 0000000..05a3d75 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/srem.asm.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in ivec4 vA; +layout(location = 1) flat in ivec4 vB; + +void main() +{ + FragColor = vec4(vA - vB * (vA / vB)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..2293587 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) out vec4 FragColors[2]; +layout(location = 2) out vec4 FragColor; + +void main() +{ + FragColors = vec4[](vec4(1.0, 2.0, 3.0, 4.0), vec4(10.0)); + FragColor = vec4(5.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag new file mode 100644 index 0000000..b2473f4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct Foo +{ + float var1; + float var2; +}; + +layout(binding = 0) uniform mediump sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; + +Foo _22; + +void main() +{ + FragColor = texture(uSampler, vec2(_22.var1, _22.var2)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/switch-label-shared-block.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000..748f96c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,32 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) flat in mediump int vIndex; +layout(location = 0) out float FragColor; + +void main() +{ + highp float _19; + switch (vIndex) + { + case 0: + case 2: + { + _19 = 1.0; + break; + } + default: + { + _19 = 3.0; + break; + } + case 8: + { + _19 = 8.0; + break; + } + } + FragColor = _19; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/temporary-name-alias.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/temporary-name-alias.asm.frag new file mode 100644 index 0000000..927c043 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/temporary-name-alias.asm.frag @@ -0,0 +1,10 @@ +#version 450 + +void main() +{ + float constituent = float(0); + mat3 _mat3 = mat3(vec3(constituent), vec3(constituent), vec3(constituent)); + float constituent_1 = float(1); + _mat3 = mat3(vec3(constituent_1), vec3(constituent_1), vec3(constituent_1)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/temporary-phi-hoisting.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/temporary-phi-hoisting.asm.frag new file mode 100644 index 0000000..4cee76c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/temporary-phi-hoisting.asm.frag @@ -0,0 +1,26 @@ +#version 450 + +struct MyStruct +{ + vec4 color; +}; + +layout(binding = 0, std140) uniform MyStruct_CB +{ + MyStruct g_MyStruct[4]; +} _6; + +layout(location = 0) out vec4 _entryPointOutput; + +void main() +{ + vec3 _28; + _28 = vec3(0.0); + vec3 _29; + for (int _31 = 0; _31 < 4; _28 = _29, _31++) + { + _29 = _28 + _6.g_MyStruct[_31].color.xyz; + } + _entryPointOutput = vec4(_28, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..6193de0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uTexture; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texelFetch(uTexture, ivec2(gl_FragCoord.xy), 0); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag b/third_party/spirv-cross/reference/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag new file mode 100644 index 0000000..e0feb49 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag @@ -0,0 +1,19 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif + +layout(binding = 0) uniform sampler2D uTexture; + +layout(location = 0) out f16vec4 FragColor; +layout(location = 0) in f16vec2 UV; + +void main() +{ + FragColor = f16vec4(texture(uTexture, UV)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag.vk b/third_party/spirv-cross/reference/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag.vk new file mode 100644 index 0000000..e13e425 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag.vk @@ -0,0 +1,20 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_EXT_shader_explicit_arithmetic_types_float16) +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require +#else +#error No extension available for FP16. +#endif +#extension GL_EXT_shader_16bit_storage : require + +layout(set = 0, binding = 0) uniform sampler2D uTexture; + +layout(location = 0) out f16vec4 FragColor; +layout(location = 0) in f16vec2 UV; + +void main() +{ + FragColor = f16vec4(texture(uTexture, UV)); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/undef-variable-store.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/undef-variable-store.asm.frag new file mode 100644 index 0000000..26ad568 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/undef-variable-store.asm.frag @@ -0,0 +1,29 @@ +#version 450 + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 _38; +vec4 _47; + +void main() +{ + vec4 _27; + do + { + vec2 _26 = vec2(0.0); + if (_26.x != 0.0) + { + _27 = vec4(1.0, 0.0, 0.0, 1.0); + break; + } + else + { + _27 = vec4(1.0, 1.0, 0.0, 1.0); + break; + } + _27 = _38; + break; + } while (false); + _entryPointOutput = _27; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/third_party/spirv-cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000..7729d30 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(binding = 0) uniform sampler2DShadow uShadow; +uniform sampler2DShadow SPIRV_Cross_CombineduTextureuSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +float sample_combined() +{ + return texture(uShadow, vec3(vUV.xy, vUV.z)); +} + +float sample_separate() +{ + return texture(SPIRV_Cross_CombineduTextureuSampler, vec3(vUV.xy, vUV.z)); +} + +void main() +{ + FragColor = sample_combined() + sample_separate(); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk b/third_party/spirv-cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk new file mode 100644 index 0000000..711fa27 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/unknown-depth-state.asm.vk.frag.vk @@ -0,0 +1,24 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2DShadow uShadow; +layout(set = 0, binding = 1) uniform texture2D uTexture; +layout(set = 0, binding = 2) uniform samplerShadow uSampler; + +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +float sample_combined() +{ + return texture(uShadow, vec3(vUV.xy, vUV.z)); +} + +float sample_separate() +{ + return texture(sampler2DShadow(uTexture, uSampler), vec3(vUV.xy, vUV.z)); +} + +void main() +{ + FragColor = sample_combined() + sample_separate(); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..8bc88b9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/unreachable.asm.frag @@ -0,0 +1,28 @@ +#version 450 + +layout(location = 0) flat in int counter; +layout(location = 0) out vec4 FragColor; + +vec4 _21; + +void main() +{ + vec4 _24; + _24 = _21; + vec4 _33; + for (;;) + { + if (counter == 10) + { + _33 = vec4(10.0); + break; + } + else + { + _33 = vec4(30.0); + break; + } + } + FragColor = _33; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/frag/vector-shuffle-oom.asm.frag b/third_party/spirv-cross/reference/shaders/asm/frag/vector-shuffle-oom.asm.frag new file mode 100644 index 0000000..1c211ca --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/frag/vector-shuffle-oom.asm.frag @@ -0,0 +1,316 @@ +#version 450 + +struct _28 +{ + vec4 _m0; +}; + +layout(binding = 0, std140) uniform _6_7 +{ + vec4 _m0; + float _m1; + vec4 _m2; +} _7; + +layout(binding = 2, std140) uniform _10_11 +{ + vec3 _m0; + vec3 _m1; + float _m2; + vec3 _m3; + float _m4; + vec3 _m5; + float _m6; + vec3 _m7; + float _m8; + vec3 _m9; + float _m10; + vec3 _m11; + float _m12; + vec2 _m13; + vec2 _m14; + vec3 _m15; + float _m16; + float _m17; + float _m18; + float _m19; + float _m20; + vec4 _m21; + vec4 _m22; + layout(row_major) mat4 _m23; + vec4 _m24; +} _11; + +layout(binding = 1, std140) uniform _18_19 +{ + layout(row_major) mat4 _m0; + layout(row_major) mat4 _m1; + layout(row_major) mat4 _m2; + layout(row_major) mat4 _m3; + vec4 _m4; + vec4 _m5; + float _m6; + float _m7; + float _m8; + float _m9; + vec3 _m10; + float _m11; + vec3 _m12; + float _m13; + vec3 _m14; + float _m15; + vec3 _m16; + float _m17; + float _m18; + float _m19; + vec2 _m20; + vec2 _m21; + vec2 _m22; + vec4 _m23; + vec2 _m24; + vec2 _m25; + vec2 _m26; + vec3 _m27; + float _m28; + float _m29; + float _m30; + float _m31; + float _m32; + vec2 _m33; + float _m34; + float _m35; + vec3 _m36; + layout(row_major) mat4 _m37[2]; + vec4 _m38[2]; +} _19; + +uniform sampler2D SPIRV_Cross_Combined; +uniform sampler2D SPIRV_Cross_Combined_1; +uniform sampler2D SPIRV_Cross_Combined_2; + +layout(location = 0) out vec4 _5; + +_28 _74; + +void main() +{ + _28 _77 = _74; + _77._m0 = vec4(0.0); + vec2 _82 = gl_FragCoord.xy * _19._m23.xy; + vec4 _88 = _7._m2 * _7._m0.xyxy; + vec2 _97 = clamp(_82 + (vec3(0.0, -2.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _109 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _97, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _113 = textureLod(SPIRV_Cross_Combined_1, _97, 0.0); + vec3 _129; + if (_113.y > 0.0) + { + _129 = _109 + (textureLod(SPIRV_Cross_Combined_2, _97, 0.0).xyz * clamp(_113.y * _113.z, 0.0, 1.0)); + } + else + { + _129 = _109; + } + vec3 _133 = vec4(0.0).xyz + (_129 * 0.5); + vec4 _134 = vec4(_133.x, _133.y, _133.z, vec4(0.0).w); + _28 _135 = _77; + _135._m0 = _134; + vec2 _144 = clamp(_82 + (vec3(-1.0, -1.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _156 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _144, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _160 = textureLod(SPIRV_Cross_Combined_1, _144, 0.0); + vec3 _176; + if (_160.y > 0.0) + { + _176 = _156 + (textureLod(SPIRV_Cross_Combined_2, _144, 0.0).xyz * clamp(_160.y * _160.z, 0.0, 1.0)); + } + else + { + _176 = _156; + } + vec3 _180 = _134.xyz + (_176 * 0.5); + vec4 _181 = vec4(_180.x, _180.y, _180.z, _134.w); + _28 _182 = _135; + _182._m0 = _181; + vec2 _191 = clamp(_82 + (vec3(0.0, -1.0, 0.75).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _203 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _191, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _207 = textureLod(SPIRV_Cross_Combined_1, _191, 0.0); + vec3 _223; + if (_207.y > 0.0) + { + _223 = _203 + (textureLod(SPIRV_Cross_Combined_2, _191, 0.0).xyz * clamp(_207.y * _207.z, 0.0, 1.0)); + } + else + { + _223 = _203; + } + vec3 _227 = _181.xyz + (_223 * 0.75); + vec4 _228 = vec4(_227.x, _227.y, _227.z, _181.w); + _28 _229 = _182; + _229._m0 = _228; + vec2 _238 = clamp(_82 + (vec3(1.0, -1.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _250 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _238, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _254 = textureLod(SPIRV_Cross_Combined_1, _238, 0.0); + vec3 _270; + if (_254.y > 0.0) + { + _270 = _250 + (textureLod(SPIRV_Cross_Combined_2, _238, 0.0).xyz * clamp(_254.y * _254.z, 0.0, 1.0)); + } + else + { + _270 = _250; + } + vec3 _274 = _228.xyz + (_270 * 0.5); + vec4 _275 = vec4(_274.x, _274.y, _274.z, _228.w); + _28 _276 = _229; + _276._m0 = _275; + vec2 _285 = clamp(_82 + (vec3(-2.0, 0.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _297 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _285, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _301 = textureLod(SPIRV_Cross_Combined_1, _285, 0.0); + vec3 _317; + if (_301.y > 0.0) + { + _317 = _297 + (textureLod(SPIRV_Cross_Combined_2, _285, 0.0).xyz * clamp(_301.y * _301.z, 0.0, 1.0)); + } + else + { + _317 = _297; + } + vec3 _321 = _275.xyz + (_317 * 0.5); + vec4 _322 = vec4(_321.x, _321.y, _321.z, _275.w); + _28 _323 = _276; + _323._m0 = _322; + vec2 _332 = clamp(_82 + (vec3(-1.0, 0.0, 0.75).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _344 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _332, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _348 = textureLod(SPIRV_Cross_Combined_1, _332, 0.0); + vec3 _364; + if (_348.y > 0.0) + { + _364 = _344 + (textureLod(SPIRV_Cross_Combined_2, _332, 0.0).xyz * clamp(_348.y * _348.z, 0.0, 1.0)); + } + else + { + _364 = _344; + } + vec3 _368 = _322.xyz + (_364 * 0.75); + vec4 _369 = vec4(_368.x, _368.y, _368.z, _322.w); + _28 _370 = _323; + _370._m0 = _369; + vec2 _379 = clamp(_82 + (vec3(0.0, 0.0, 1.0).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _391 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _379, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _395 = textureLod(SPIRV_Cross_Combined_1, _379, 0.0); + vec3 _411; + if (_395.y > 0.0) + { + _411 = _391 + (textureLod(SPIRV_Cross_Combined_2, _379, 0.0).xyz * clamp(_395.y * _395.z, 0.0, 1.0)); + } + else + { + _411 = _391; + } + vec3 _415 = _369.xyz + (_411 * 1.0); + vec4 _416 = vec4(_415.x, _415.y, _415.z, _369.w); + _28 _417 = _370; + _417._m0 = _416; + vec2 _426 = clamp(_82 + (vec3(1.0, 0.0, 0.75).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _438 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _426, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _442 = textureLod(SPIRV_Cross_Combined_1, _426, 0.0); + vec3 _458; + if (_442.y > 0.0) + { + _458 = _438 + (textureLod(SPIRV_Cross_Combined_2, _426, 0.0).xyz * clamp(_442.y * _442.z, 0.0, 1.0)); + } + else + { + _458 = _438; + } + vec3 _462 = _416.xyz + (_458 * 0.75); + vec4 _463 = vec4(_462.x, _462.y, _462.z, _416.w); + _28 _464 = _417; + _464._m0 = _463; + vec2 _473 = clamp(_82 + (vec3(2.0, 0.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _485 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _473, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _489 = textureLod(SPIRV_Cross_Combined_1, _473, 0.0); + vec3 _505; + if (_489.y > 0.0) + { + _505 = _485 + (textureLod(SPIRV_Cross_Combined_2, _473, 0.0).xyz * clamp(_489.y * _489.z, 0.0, 1.0)); + } + else + { + _505 = _485; + } + vec3 _509 = _463.xyz + (_505 * 0.5); + vec4 _510 = vec4(_509.x, _509.y, _509.z, _463.w); + _28 _511 = _464; + _511._m0 = _510; + vec2 _520 = clamp(_82 + (vec3(-1.0, 1.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _532 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _520, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _536 = textureLod(SPIRV_Cross_Combined_1, _520, 0.0); + vec3 _552; + if (_536.y > 0.0) + { + _552 = _532 + (textureLod(SPIRV_Cross_Combined_2, _520, 0.0).xyz * clamp(_536.y * _536.z, 0.0, 1.0)); + } + else + { + _552 = _532; + } + vec3 _556 = _510.xyz + (_552 * 0.5); + vec4 _557 = vec4(_556.x, _556.y, _556.z, _510.w); + _28 _558 = _511; + _558._m0 = _557; + vec2 _567 = clamp(_82 + (vec3(0.0, 1.0, 0.75).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _579 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _567, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _583 = textureLod(SPIRV_Cross_Combined_1, _567, 0.0); + vec3 _599; + if (_583.y > 0.0) + { + _599 = _579 + (textureLod(SPIRV_Cross_Combined_2, _567, 0.0).xyz * clamp(_583.y * _583.z, 0.0, 1.0)); + } + else + { + _599 = _579; + } + vec3 _603 = _557.xyz + (_599 * 0.75); + vec4 _604 = vec4(_603.x, _603.y, _603.z, _557.w); + _28 _605 = _558; + _605._m0 = _604; + vec2 _614 = clamp(_82 + (vec3(1.0, 1.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _626 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _614, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _630 = textureLod(SPIRV_Cross_Combined_1, _614, 0.0); + vec3 _646; + if (_630.y > 0.0) + { + _646 = _626 + (textureLod(SPIRV_Cross_Combined_2, _614, 0.0).xyz * clamp(_630.y * _630.z, 0.0, 1.0)); + } + else + { + _646 = _626; + } + vec3 _650 = _604.xyz + (_646 * 0.5); + vec4 _651 = vec4(_650.x, _650.y, _650.z, _604.w); + _28 _652 = _605; + _652._m0 = _651; + vec2 _661 = clamp(_82 + (vec3(0.0, 2.0, 0.5).xy * _7._m0.xy), _88.xy, _88.zw); + vec3 _673 = _11._m5 * clamp(textureLod(SPIRV_Cross_Combined, _661, 0.0).w * _7._m1, 0.0, 1.0); + vec4 _677 = textureLod(SPIRV_Cross_Combined_1, _661, 0.0); + vec3 _693; + if (_677.y > 0.0) + { + _693 = _673 + (textureLod(SPIRV_Cross_Combined_2, _661, 0.0).xyz * clamp(_677.y * _677.z, 0.0, 1.0)); + } + else + { + _693 = _673; + } + vec3 _697 = _651.xyz + (_693 * 0.5); + vec4 _698 = vec4(_697.x, _697.y, _697.z, _651.w); + _28 _699 = _652; + _699._m0 = _698; + vec3 _702 = _698.xyz / vec3(((((((((((((0.0 + 0.5) + 0.5) + 0.75) + 0.5) + 0.5) + 0.75) + 1.0) + 0.75) + 0.5) + 0.5) + 0.75) + 0.5) + 0.5); + _28 _704 = _699; + _704._m0 = vec4(_702.x, _702.y, _702.z, _698.w); + _28 _705 = _704; + _705._m0.w = 1.0; + _5 = _705._m0; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/geom/block-name-namespace.asm.geom b/third_party/spirv-cross/reference/shaders/asm/geom/block-name-namespace.asm.geom new file mode 100644 index 0000000..417cf34 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/geom/block-name-namespace.asm.geom @@ -0,0 +1,33 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 4, triangle_strip) out; + +layout(binding = 0, std140) uniform VertexInput +{ + vec4 a; +} VertexInput_1; + +layout(binding = 0, std430) buffer VertexInput +{ + vec4 b; +} VertexInput_2; + +layout(location = 0) out VertexInput +{ + vec4 vColor; +} VertexInput_3; + +layout(location = 0) in VertexInput +{ + vec4 vColor; +} vin[3]; + + +void main() +{ + vec4 VertexInput_4 = vec4(1.0); + gl_Position = (VertexInput_4 + VertexInput_1.a) + VertexInput_2.b; + VertexInput_3.vColor = vin[0].vColor; + EmitVertex(); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/geom/inout-split-access-chain-handle.asm.geom b/third_party/spirv-cross/reference/shaders/asm/geom/inout-split-access-chain-handle.asm.geom new file mode 100644 index 0000000..7108209 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/geom/inout-split-access-chain-handle.asm.geom @@ -0,0 +1,23 @@ +#version 440 +layout(triangles) in; +layout(max_vertices = 5, triangle_strip) out; + +struct Data +{ + vec4 ApiPerspectivePosition; +}; + +void Copy(inout Data inputStream[3]) +{ + inputStream[0].ApiPerspectivePosition = gl_in[0].gl_Position; +} + +void main() +{ + Data inputStream[3]; + Data param[3] = inputStream; + Copy(param); + inputStream = param; + gl_Position = inputStream[0].ApiPerspectivePosition; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/geom/split-access-chain-input.asm.geom b/third_party/spirv-cross/reference/shaders/asm/geom/split-access-chain-input.asm.geom new file mode 100644 index 0000000..511d87f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/geom/split-access-chain-input.asm.geom @@ -0,0 +1,9 @@ +#version 440 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +void main() +{ + gl_Position = gl_in[0].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/geom/unroll-glposition-load.asm.geom b/third_party/spirv-cross/reference/shaders/asm/geom/unroll-glposition-load.asm.geom new file mode 100644 index 0000000..39bc097 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/geom/unroll-glposition-load.asm.geom @@ -0,0 +1,35 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +struct SceneOut +{ + vec4 pos; +}; + +void _main(vec4 positions[3], SceneOut OUT) +{ + SceneOut o; + for (int i = 0; i < 3; i++) + { + o.pos = positions[i]; + gl_Position = o.pos; + EmitVertex(); + } + EndPrimitive(); +} + +void main() +{ + vec4 _35_unrolled[3]; + for (int i = 0; i < int(3); i++) + { + _35_unrolled[i] = gl_in[i].gl_Position; + } + vec4 positions[3] = _35_unrolled; + vec4 param[3] = positions; + SceneOut param_1; + _main(param, param_1); + SceneOut OUT = param_1; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/tese/unroll-input-array-load.asm.tese b/third_party/spirv-cross/reference/shaders/asm/tese/unroll-input-array-load.asm.tese new file mode 100644 index 0000000..34970b8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/tese/unroll-input-array-load.asm.tese @@ -0,0 +1,48 @@ +#version 450 +layout(quads) in; + +struct HS_INPUT +{ + vec4 foo; + vec4 bar; +}; + +struct ControlPoint +{ + vec4 baz; +}; + +struct DS_OUTPUT +{ + vec4 pos; +}; + +layout(location = 0) patch in vec4 input_foo; +layout(location = 1) patch in vec4 input_bar; +layout(location = 2) in ControlPoint CPData[]; + +DS_OUTPUT _main(HS_INPUT _input, vec2 uv, ControlPoint CPData_1[4]) +{ + DS_OUTPUT o; + o.pos = (((_input.foo + _input.bar) + uv.xyxy) + CPData_1[0].baz) + CPData_1[3].baz; + return o; +} + +void main() +{ + HS_INPUT _input; + _input.foo = input_foo; + _input.bar = input_bar; + vec2 uv = vec2(gl_TessCoord.xy); + ControlPoint _54_unrolled[4]; + for (int i = 0; i < int(4); i++) + { + _54_unrolled[i] = CPData[i]; + } + ControlPoint CPData_1[4] = _54_unrolled; + HS_INPUT param = _input; + vec2 param_1 = uv; + ControlPoint param_2[4] = CPData_1; + gl_Position = _main(param, param_1, param_2).pos; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/empty-io.asm.vert b/third_party/spirv-cross/reference/shaders/asm/vert/empty-io.asm.vert new file mode 100644 index 0000000..cc432cb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/empty-io.asm.vert @@ -0,0 +1,34 @@ +#version 450 + +struct VSInput +{ + vec4 position; +}; + +struct VSOutput +{ + vec4 position; +}; + +struct VSOutput_1 +{ + int empty_struct_member; +}; + +layout(location = 0) in vec4 position; + +VSOutput _main(VSInput _input) +{ + VSOutput _out; + _out.position = _input.position; + return _out; +} + +void main() +{ + VSInput _input; + _input.position = position; + VSInput param = _input; + gl_Position = _main(param).position; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/reference/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..40eef2d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,52 @@ +#version 450 +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +struct V2F +{ + vec4 Position; + vec4 Color; +}; + +struct InstanceData +{ + mat4 MATRIX_MVP; + vec4 Color; +}; + +layout(binding = 0, std430) readonly buffer gInstanceData +{ + layout(row_major) InstanceData _data[]; +} gInstanceData_1; + +layout(location = 0) in vec3 PosL; +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +layout(location = 0) out vec4 _entryPointOutput_Color; + +V2F _VS(vec3 PosL_1, uint instanceID) +{ + InstanceData instData; + instData.MATRIX_MVP = gInstanceData_1._data[instanceID].MATRIX_MVP; + instData.Color = gInstanceData_1._data[instanceID].Color; + V2F v2f; + v2f.Position = instData.MATRIX_MVP * vec4(PosL_1, 1.0); + v2f.Color = instData.Color; + return v2f; +} + +void main() +{ + vec3 PosL_1 = PosL; + uint instanceID = uint((gl_InstanceID + SPIRV_Cross_BaseInstance)); + vec3 param = PosL_1; + uint param_1 = instanceID; + V2F flattenTemp = _VS(param, param_1); + gl_Position = flattenTemp.Position; + _entryPointOutput_Color = flattenTemp.Color; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/global-builtin.sso.asm.vert b/third_party/spirv-cross/reference/shaders/asm/vert/global-builtin.sso.asm.vert new file mode 100644 index 0000000..7578827 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/global-builtin.sso.asm.vert @@ -0,0 +1,35 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +struct VSOut +{ + float a; + vec4 pos; +}; + +struct VSOut_1 +{ + float a; +}; + +layout(location = 0) out VSOut_1 _entryPointOutput; + +VSOut _main() +{ + VSOut vout; + vout.a = 40.0; + vout.pos = vec4(1.0); + return vout; +} + +void main() +{ + VSOut flattenTemp = _main(); + _entryPointOutput.a = flattenTemp.a; + gl_Position = flattenTemp.pos; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/invariant-block.asm.vert b/third_party/spirv-cross/reference/shaders/asm/vert/invariant-block.asm.vert new file mode 100644 index 0000000..9b2f05a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/invariant-block.asm.vert @@ -0,0 +1,9 @@ +#version 450 + +invariant gl_Position; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/invariant-block.sso.asm.vert b/third_party/spirv-cross/reference/shaders/asm/vert/invariant-block.sso.asm.vert new file mode 100644 index 0000000..eb88694 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/invariant-block.sso.asm.vert @@ -0,0 +1,17 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[1]; + float gl_CullDistance[1]; +}; + +invariant gl_Position; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/invariant.asm.vert b/third_party/spirv-cross/reference/shaders/asm/vert/invariant.asm.vert new file mode 100644 index 0000000..c4c6ad6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/invariant.asm.vert @@ -0,0 +1,15 @@ +#version 450 + +invariant gl_Position; + +vec4 _main() +{ + return vec4(1.0); +} + +void main() +{ + vec4 _14 = _main(); + gl_Position = _14; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/invariant.sso.asm.vert b/third_party/spirv-cross/reference/shaders/asm/vert/invariant.sso.asm.vert new file mode 100644 index 0000000..c486be1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/invariant.sso.asm.vert @@ -0,0 +1,20 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +invariant gl_Position; + +vec4 _main() +{ + return vec4(1.0); +} + +void main() +{ + vec4 _14 = _main(); + gl_Position = _14; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert b/third_party/spirv-cross/reference/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert new file mode 100644 index 0000000..fdba2a2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert @@ -0,0 +1,34 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_201 +#define SPIRV_CROSS_CONSTANT_ID_201 -10 +#endif +const int _7 = SPIRV_CROSS_CONSTANT_ID_201; +const int _20 = (_7 + 2); +#ifndef SPIRV_CROSS_CONSTANT_ID_202 +#define SPIRV_CROSS_CONSTANT_ID_202 100u +#endif +const uint _8 = SPIRV_CROSS_CONSTANT_ID_202; +const uint _25 = (_8 % 5u); +const ivec4 _30 = ivec4(20, 30, _20, _20); +const ivec2 _32 = ivec2(_30.y, _30.x); +const int _33 = _30.y; +#ifndef SPIRV_CROSS_CONSTANT_ID_200 +#define SPIRV_CROSS_CONSTANT_ID_200 3.141590118408203125 +#endif +const float _9 = SPIRV_CROSS_CONSTANT_ID_200; + +layout(location = 0) flat out int _4; + +void main() +{ + vec4 pos = vec4(0.0); + pos.y += float(_20); + pos.z += float(_25); + pos += vec4(_30); + vec2 _56 = pos.xy + vec2(_32); + pos = vec4(_56.x, _56.y, pos.z, pos.w); + gl_Position = pos; + _4 = _33; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert.vk b/third_party/spirv-cross/reference/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert.vk new file mode 100644 index 0000000..02c3e31 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert.vk @@ -0,0 +1,25 @@ +#version 450 + +layout(constant_id = 201) const int _7 = -10; +const int _20 = (_7 + 2); +layout(constant_id = 202) const uint _8 = 100u; +const uint _25 = (_8 % 5u); +const ivec4 _30 = ivec4(20, 30, _20, _20); +const ivec2 _32 = ivec2(_30.y, _30.x); +const int _33 = _30.y; +layout(constant_id = 200) const float _9 = 3.141590118408203125; + +layout(location = 0) flat out int _4; + +void main() +{ + vec4 pos = vec4(0.0); + pos.y += float(_20); + pos.z += float(_25); + pos += vec4(_30); + vec2 _56 = pos.xy + vec2(_32); + pos = vec4(_56.x, _56.y, pos.z, pos.w); + gl_Position = pos; + _4 = _33; +} + diff --git a/third_party/spirv-cross/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..d79c08f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,25 @@ +#version 450 +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif + +vec4 _main(uint vid, uint iid) +{ + return vec4(float(vid + iid)); +} + +void main() +{ + uint vid = uint(gl_VertexID); + uint iid = uint((gl_InstanceID + SPIRV_Cross_BaseInstance)); + uint param = vid; + uint param_1 = iid; + gl_Position = _main(param, param_1); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/atomic.comp b/third_party/spirv-cross/reference/shaders/comp/atomic.comp new file mode 100644 index 0000000..89b1351 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/atomic.comp @@ -0,0 +1,49 @@ +#version 310 es +#extension GL_OES_shader_image_atomic : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 2, std430) buffer SSBO +{ + uint u32; + int i32; +} ssbo; + +layout(binding = 0, r32ui) uniform highp uimage2D uImage; +layout(binding = 1, r32i) uniform highp iimage2D iImage; + +void main() +{ + uint _19 = imageAtomicAdd(uImage, ivec2(1, 5), 1u); + uint _27 = imageAtomicAdd(uImage, ivec2(1, 5), 1u); + imageStore(iImage, ivec2(1, 6), ivec4(int(_27))); + uint _32 = imageAtomicOr(uImage, ivec2(1, 5), 1u); + uint _34 = imageAtomicXor(uImage, ivec2(1, 5), 1u); + uint _36 = imageAtomicAnd(uImage, ivec2(1, 5), 1u); + uint _38 = imageAtomicMin(uImage, ivec2(1, 5), 1u); + uint _40 = imageAtomicMax(uImage, ivec2(1, 5), 1u); + uint _44 = imageAtomicCompSwap(uImage, ivec2(1, 5), 10u, 2u); + int _47 = imageAtomicAdd(iImage, ivec2(1, 6), 1); + int _49 = imageAtomicOr(iImage, ivec2(1, 6), 1); + int _51 = imageAtomicXor(iImage, ivec2(1, 6), 1); + int _53 = imageAtomicAnd(iImage, ivec2(1, 6), 1); + int _55 = imageAtomicMin(iImage, ivec2(1, 6), 1); + int _57 = imageAtomicMax(iImage, ivec2(1, 6), 1); + int _61 = imageAtomicCompSwap(iImage, ivec2(1, 5), 10, 2); + uint _68 = atomicAdd(ssbo.u32, 1u); + uint _70 = atomicOr(ssbo.u32, 1u); + uint _72 = atomicXor(ssbo.u32, 1u); + uint _74 = atomicAnd(ssbo.u32, 1u); + uint _76 = atomicMin(ssbo.u32, 1u); + uint _78 = atomicMax(ssbo.u32, 1u); + uint _80 = atomicExchange(ssbo.u32, 1u); + uint _82 = atomicCompSwap(ssbo.u32, 10u, 2u); + int _85 = atomicAdd(ssbo.i32, 1); + int _87 = atomicOr(ssbo.i32, 1); + int _89 = atomicXor(ssbo.i32, 1); + int _91 = atomicAnd(ssbo.i32, 1); + int _93 = atomicMin(ssbo.i32, 1); + int _95 = atomicMax(ssbo.i32, 1); + int _97 = atomicExchange(ssbo.i32, 1); + int _99 = atomicCompSwap(ssbo.i32, 10, 2); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/bake_gradient.comp b/third_party/spirv-cross/reference/shaders/comp/bake_gradient.comp new file mode 100644 index 0000000..49fa953 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/bake_gradient.comp @@ -0,0 +1,39 @@ +#version 310 es +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +layout(binding = 4, std140) uniform UBO +{ + vec4 uInvSize; + vec4 uScale; +} _46; + +layout(binding = 0) uniform mediump sampler2D uHeight; +layout(binding = 1) uniform mediump sampler2D uDisplacement; +layout(binding = 2, rgba16f) uniform writeonly mediump image2D iHeightDisplacement; +layout(binding = 3, rgba16f) uniform writeonly mediump image2D iGradJacobian; + +mediump float jacobian(mediump vec2 dDdx, mediump vec2 dDdy) +{ + return ((1.0 + dDdx.x) * (1.0 + dDdy.y)) - (dDdx.y * dDdy.x); +} + +void main() +{ + vec4 uv = (vec2(gl_GlobalInvocationID.xy) * _46.uInvSize.xy).xyxy + (_46.uInvSize * 0.5); + float h = textureLod(uHeight, uv.xy, 0.0).x; + float x0 = textureLodOffset(uHeight, uv.xy, 0.0, ivec2(-1, 0)).x; + float x1 = textureLodOffset(uHeight, uv.xy, 0.0, ivec2(1, 0)).x; + float y0 = textureLodOffset(uHeight, uv.xy, 0.0, ivec2(0, -1)).x; + float y1 = textureLodOffset(uHeight, uv.xy, 0.0, ivec2(0, 1)).x; + vec2 grad = (_46.uScale.xy * 0.5) * vec2(x1 - x0, y1 - y0); + vec2 displacement = textureLod(uDisplacement, uv.zw, 0.0).xy * 1.2000000476837158203125; + vec2 dDdx = (textureLodOffset(uDisplacement, uv.zw, 0.0, ivec2(1, 0)).xy - textureLodOffset(uDisplacement, uv.zw, 0.0, ivec2(-1, 0)).xy) * 0.60000002384185791015625; + vec2 dDdy = (textureLodOffset(uDisplacement, uv.zw, 0.0, ivec2(0, 1)).xy - textureLodOffset(uDisplacement, uv.zw, 0.0, ivec2(0, -1)).xy) * 0.60000002384185791015625; + mediump vec2 param = dDdx * _46.uScale.z; + mediump vec2 param_1 = dDdy * _46.uScale.z; + float j = jacobian(param, param_1); + displacement = vec2(0.0); + imageStore(iHeightDisplacement, ivec2(gl_GlobalInvocationID.xy), vec4(h, displacement, 0.0)); + imageStore(iGradJacobian, ivec2(gl_GlobalInvocationID.xy), vec4(grad, j, 0.0)); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/barriers.comp b/third_party/spirv-cross/reference/shaders/comp/barriers.comp new file mode 100644 index 0000000..1102c91 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/barriers.comp @@ -0,0 +1,77 @@ +#version 310 es +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +void barrier_shared() +{ + memoryBarrierShared(); +} + +void full_barrier() +{ + memoryBarrier(); +} + +void image_barrier() +{ + memoryBarrierImage(); +} + +void buffer_barrier() +{ + memoryBarrierBuffer(); +} + +void group_barrier() +{ + groupMemoryBarrier(); +} + +void barrier_shared_exec() +{ + barrier(); +} + +void full_barrier_exec() +{ + memoryBarrier(); + barrier(); +} + +void image_barrier_exec() +{ + memoryBarrierImage(); + barrier(); +} + +void buffer_barrier_exec() +{ + memoryBarrierBuffer(); + barrier(); +} + +void group_barrier_exec() +{ + groupMemoryBarrier(); + barrier(); +} + +void exec_barrier() +{ + barrier(); +} + +void main() +{ + barrier_shared(); + full_barrier(); + image_barrier(); + buffer_barrier(); + group_barrier(); + barrier_shared_exec(); + full_barrier_exec(); + image_barrier_exec(); + buffer_barrier_exec(); + group_barrier_exec(); + exec_barrier(); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/basic.comp b/third_party/spirv-cross/reference/shaders/comp/basic.comp new file mode 100644 index 0000000..1485089 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/basic.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + vec4 in_data[]; +} _23; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _45; + +layout(binding = 2, std430) buffer SSBO3 +{ + uint counter; +} _48; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idata = _23.in_data[ident]; + if (dot(idata, vec4(1.0, 5.0, 6.0, 2.0)) > 8.19999980926513671875) + { + uint _52 = atomicAdd(_48.counter, 1u); + _45.out_data[_52] = idata; + } +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/casts.comp b/third_party/spirv-cross/reference/shaders/comp/casts.comp new file mode 100644 index 0000000..9736686 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/casts.comp @@ -0,0 +1,19 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) buffer SSBO1 +{ + ivec4 outputs[]; +} _21; + +layout(binding = 0, std430) buffer SSBO0 +{ + ivec4 inputs[]; +} _27; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + _21.outputs[ident] = mix(ivec4(0), ivec4(1), notEqual((_27.inputs[ident] & ivec4(3)), ivec4(uvec4(0u)))); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/cfg-preserve-parameter.comp b/third_party/spirv-cross/reference/shaders/comp/cfg-preserve-parameter.comp new file mode 100644 index 0000000..72bde44 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/cfg-preserve-parameter.comp @@ -0,0 +1,74 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +void out_test_0(int cond, out int i) +{ + if (cond == 0) + { + i = 40; + } + else + { + i = 60; + } +} + +void out_test_1(int cond, out int i) +{ + switch (cond) + { + case 40: + { + i = 40; + break; + } + default: + { + i = 70; + break; + } + } +} + +void inout_test_0(int cond, inout int i) +{ + if (cond == 0) + { + i = 40; + } +} + +void inout_test_1(int cond, inout int i) +{ + switch (cond) + { + case 40: + { + i = 40; + break; + } + } +} + +void main() +{ + int cond = 40; + int i = 50; + int param = cond; + int param_1 = i; + out_test_0(param, param_1); + i = param_1; + int param_2 = cond; + int param_3 = i; + out_test_1(param_2, param_3); + i = param_3; + int param_4 = cond; + int param_5 = i; + inout_test_0(param_4, param_5); + i = param_5; + int param_6 = cond; + int param_7 = i; + inout_test_1(param_6, param_7); + i = param_7; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/cfg.comp b/third_party/spirv-cross/reference/shaders/comp/cfg.comp new file mode 100644 index 0000000..a91c873 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/cfg.comp @@ -0,0 +1,81 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float data; +} _11; + +void test() +{ + if (!(_11.data == 0.0)) + { + float tmp = 10.0; + _11.data = tmp; + } + else + { + float tmp_1 = 15.0; + _11.data = tmp_1; + } + if (!(_11.data == 0.0)) + { + float e; + if (!(_11.data == 5.0)) + { + if (!(_11.data == 6.0)) + { + e = 10.0; + } + } + else + { + e = 20.0; + } + } + switch (int(_11.data)) + { + case 0: + { + float tmp_2 = 20.0; + _11.data = tmp_2; + break; + } + case 1: + { + float tmp_3 = 30.0; + _11.data = tmp_3; + break; + } + } + float f; + switch (int(_11.data)) + { + case 0: + { + f = 30.0; + break; + } + case 1: + { + f = 40.0; + break; + } + } + float h; + for (int i = 0; i < 20; i++, h += 10.0) + { + } + _11.data = h; + float m; + do + { + } while (!(m == 20.0)); + _11.data = m; +} + +void main() +{ + test(); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/coherent-block.comp b/third_party/spirv-cross/reference/shaders/comp/coherent-block.comp new file mode 100644 index 0000000..bfab6bb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/coherent-block.comp @@ -0,0 +1,13 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) coherent restrict writeonly buffer SSBO +{ + vec4 value; +} _10; + +void main() +{ + _10.value = vec4(20.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/coherent-image.comp b/third_party/spirv-cross/reference/shaders/comp/coherent-image.comp new file mode 100644 index 0000000..b3992f2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/coherent-image.comp @@ -0,0 +1,15 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) coherent restrict writeonly buffer SSBO +{ + ivec4 value; +} _10; + +layout(binding = 3, r32i) uniform coherent restrict readonly mediump iimage2D uImage; + +void main() +{ + _10.value = imageLoad(uImage, ivec2(10)); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/composite-array-initialization.comp b/third_party/spirv-cross/reference/shaders/comp/composite-array-initialization.comp new file mode 100644 index 0000000..847a152 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/composite-array-initialization.comp @@ -0,0 +1,38 @@ +#version 310 es +layout(local_size_x = 2, local_size_y = 1, local_size_z = 1) in; + +struct Data +{ + float a; + float b; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 4.0 +#endif +const float X = SPIRV_CROSS_CONSTANT_ID_0; + +layout(binding = 0, std430) buffer SSBO +{ + Data outdata[]; +} _53; + +Data data[2]; +Data data2[2]; + +Data combine(Data a, Data b) +{ + return Data(a.a + b.a, a.b + b.b); +} + +void main() +{ + data = Data[](Data(1.0, 2.0), Data(3.0, 4.0)); + data2 = Data[](Data(X, 2.0), Data(3.0, 5.0)); + Data param = data[gl_LocalInvocationID.x]; + Data param_1 = data2[gl_LocalInvocationID.x]; + Data _73 = combine(param, param_1); + _53.outdata[gl_WorkGroupID.x].a = _73.a; + _53.outdata[gl_WorkGroupID.x].b = _73.b; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/composite-construct.comp b/third_party/spirv-cross/reference/shaders/comp/composite-construct.comp new file mode 100644 index 0000000..b397550 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/composite-construct.comp @@ -0,0 +1,40 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +const vec4 _66[2] = vec4[](vec4(10.0), vec4(30.0)); + +struct Composite +{ + vec4 a[2]; + vec4 b[2]; +}; + +const float _94[2][3] = float[][](float[](1.0, 1.0, 1.0), float[](2.0, 2.0, 2.0)); + +layout(binding = 0, std430) buffer SSBO0 +{ + vec4 as[]; +} _41; + +layout(binding = 1, std430) buffer SSBO1 +{ + vec4 bs[]; +} _55; + +vec4 summe(vec4 values[3][2]) +{ + return ((values[0][0] + values[2][1]) + values[0][1]) + values[1][0]; +} + +void main() +{ + vec4 values[2] = vec4[](_41.as[gl_GlobalInvocationID.x], _55.bs[gl_GlobalInvocationID.x]); + vec4 copy_values[2] = _66; + vec4 copy_values2[2] = values; + vec4 param[3][2] = vec4[][](values, copy_values, copy_values2); + _41.as[gl_GlobalInvocationID.x] = summe(param); + Composite c = Composite(values, copy_values); + float b = 10.0; + float values_scalar[4] = float[](b, b, b, b); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/culling.comp b/third_party/spirv-cross/reference/shaders/comp/culling.comp new file mode 100644 index 0000000..fd83bfc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/culling.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + float in_data[]; +} _22; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + float out_data[]; +} _38; + +layout(binding = 2, std430) buffer SSBO3 +{ + uint count; +} _41; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + float idata = _22.in_data[ident]; + if (idata > 12.0) + { + uint _45 = atomicAdd(_41.count, 1u); + _38.out_data[_45] = idata; + } +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/defer-parens.comp b/third_party/spirv-cross/reference/shaders/comp/defer-parens.comp new file mode 100644 index 0000000..cf98529 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/defer-parens.comp @@ -0,0 +1,21 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + vec4 data; + int index; +} _13; + +void main() +{ + vec4 d = _13.data; + _13.data = vec4(d.x, d.yz + vec2(10.0), d.w); + _13.data = (d + d) + d; + _13.data = (d.yz + vec2(10.0)).xxyy; + float t = (d.yz + vec2(10.0)).y; + _13.data = vec4(t); + t = (d.zw + vec2(10.0))[_13.index]; + _13.data = vec4(t); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/dowhile.comp b/third_party/spirv-cross/reference/shaders/comp/dowhile.comp new file mode 100644 index 0000000..e717961 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/dowhile.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +} _28; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _52; + +int i; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + i = 0; + vec4 idat = _28.in_data[ident]; + do + { + idat = _28.mvp * idat; + i++; + } while (i < 16); + _52.out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/generate_height.comp b/third_party/spirv-cross/reference/shaders/comp/generate_height.comp new file mode 100644 index 0000000..e482bf0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/generate_height.comp @@ -0,0 +1,95 @@ +#version 310 es +layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer Distribution +{ + vec2 distribution[]; +} _137; + +layout(binding = 2, std140) uniform UBO +{ + vec4 uModTime; +} _166; + +layout(binding = 1, std430) writeonly buffer HeightmapFFT +{ + uint heights[]; +} _225; + +uvec2 workaround_mix(uvec2 a, uvec2 b, bvec2 sel) +{ + uint _86; + if (sel.x) + { + _86 = b.x; + } + else + { + _86 = a.x; + } + uint _97; + if (sel.y) + { + _97 = b.y; + } + else + { + _97 = a.y; + } + return uvec2(_86, _97); +} + +vec2 alias(vec2 i, vec2 N) +{ + return mix(i, i - N, greaterThan(i, N * 0.5)); +} + +vec2 cmul(vec2 a, vec2 b) +{ + vec2 r3 = a.yx; + vec2 r1 = b.xx; + vec2 R0 = a * r1; + vec2 r2 = b.yy; + vec2 R1 = r2 * r3; + return R0 + vec2(-R1.x, R1.y); +} + +uint pack2(vec2 v) +{ + return packHalf2x16(v); +} + +void generate_heightmap() +{ + uvec2 N = uvec2(64u, 1u) * gl_NumWorkGroups.xy; + uvec2 i = gl_GlobalInvocationID.xy; + uvec2 param = N - i; + uvec2 param_1 = uvec2(0u); + bvec2 param_2 = equal(i, uvec2(0u)); + uvec2 wi = workaround_mix(param, param_1, param_2); + vec2 a = _137.distribution[(i.y * N.x) + i.x]; + vec2 b = _137.distribution[(wi.y * N.x) + wi.x]; + vec2 param_3 = vec2(i); + vec2 param_4 = vec2(N); + vec2 k = _166.uModTime.xy * alias(param_3, param_4); + float k_len = length(k); + float w = sqrt(9.81000041961669921875 * k_len) * _166.uModTime.z; + float cw = cos(w); + float sw = sin(w); + vec2 param_5 = a; + vec2 param_6 = vec2(cw, sw); + a = cmul(param_5, param_6); + vec2 param_7 = b; + vec2 param_8 = vec2(cw, sw); + b = cmul(param_7, param_8); + b = vec2(b.x, -b.y); + vec2 res = a + b; + vec2 param_9 = res; + _225.heights[(i.y * N.x) + i.x] = pack2(param_9); +} + +void main() +{ + generate_heightmap(); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/image.comp b/third_party/spirv-cross/reference/shaders/comp/image.comp new file mode 100644 index 0000000..b2bf0d6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/image.comp @@ -0,0 +1,12 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, rgba8) uniform readonly mediump image2D uImageIn; +layout(binding = 1, rgba8) uniform writeonly mediump image2D uImageOut; + +void main() +{ + vec4 v = imageLoad(uImageIn, ivec2(gl_GlobalInvocationID.xy) + imageSize(uImageIn)); + imageStore(uImageOut, ivec2(gl_GlobalInvocationID.xy), v); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/insert.comp b/third_party/spirv-cross/reference/shaders/comp/insert.comp new file mode 100644 index 0000000..cbe1e27 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/insert.comp @@ -0,0 +1,19 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) writeonly buffer SSBO +{ + vec4 out_data[]; +} _27; + +void main() +{ + vec4 v; + v.x = 10.0; + v.y = 30.0; + v.z = 70.0; + v.w = 90.0; + _27.out_data[gl_GlobalInvocationID.x] = v; + _27.out_data[gl_GlobalInvocationID.x].y = 20.0; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/mat3.comp b/third_party/spirv-cross/reference/shaders/comp/mat3.comp new file mode 100644 index 0000000..2b050f5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/mat3.comp @@ -0,0 +1,14 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + mat3 out_data[]; +} _22; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + _22.out_data[ident] = mat3(vec3(10.0), vec3(20.0), vec3(40.0)); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/mod.comp b/third_party/spirv-cross/reference/shaders/comp/mod.comp new file mode 100644 index 0000000..4be0c5f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/mod.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + vec4 in_data[]; +} _23; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _33; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 v = mod(_23.in_data[ident], _33.out_data[ident]); + _33.out_data[ident] = v; + uvec4 vu = floatBitsToUint(_23.in_data[ident]) % floatBitsToUint(_33.out_data[ident]); + _33.out_data[ident] = uintBitsToFloat(vu); + ivec4 vi = floatBitsToInt(_23.in_data[ident]) % floatBitsToInt(_33.out_data[ident]); + _33.out_data[ident] = intBitsToFloat(vi); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/modf.comp b/third_party/spirv-cross/reference/shaders/comp/modf.comp new file mode 100644 index 0000000..c92149b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/modf.comp @@ -0,0 +1,22 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + vec4 in_data[]; +} _23; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _35; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 i; + vec4 _31 = modf(_23.in_data[ident], i); + vec4 v = _31; + _35.out_data[ident] = v; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/outer-product.comp b/third_party/spirv-cross/reference/shaders/comp/outer-product.comp new file mode 100644 index 0000000..d31dad3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/outer-product.comp @@ -0,0 +1,36 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) writeonly buffer SSBO +{ + mat2 m22; + mat2x3 m23; + mat2x4 m24; + mat3x2 m32; + mat3 m33; + mat3x4 m34; + mat4x2 m42; + mat4x3 m43; + mat4 m44; +} _21; + +layout(binding = 1, std430) readonly buffer ReadSSBO +{ + vec2 v2; + vec3 v3; + vec4 v4; +} _26; + +void main() +{ + _21.m22 = outerProduct(_26.v2, _26.v2); + _21.m23 = outerProduct(_26.v3, _26.v2); + _21.m24 = outerProduct(_26.v4, _26.v2); + _21.m32 = outerProduct(_26.v2, _26.v3); + _21.m33 = outerProduct(_26.v3, _26.v3); + _21.m34 = outerProduct(_26.v4, _26.v3); + _21.m42 = outerProduct(_26.v2, _26.v4); + _21.m43 = outerProduct(_26.v3, _26.v4); + _21.m44 = outerProduct(_26.v4, _26.v4); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/read-write-only.comp b/third_party/spirv-cross/reference/shaders/comp/read-write-only.comp new file mode 100644 index 0000000..06227ee --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/read-write-only.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 2, std430) restrict writeonly buffer SSBO2 +{ + vec4 data4; + vec4 data5; +} _10; + +layout(binding = 0, std430) readonly buffer SSBO0 +{ + vec4 data0; + vec4 data1; +} _15; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + vec4 data2; + vec4 data3; +} _21; + +void main() +{ + _10.data4 = _15.data0 + _21.data2; + _10.data5 = _15.data1 + _21.data3; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/rmw-matrix.comp b/third_party/spirv-cross/reference/shaders/comp/rmw-matrix.comp new file mode 100644 index 0000000..5c4ac94 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/rmw-matrix.comp @@ -0,0 +1,20 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float a; + vec4 b; + mat4 c; + float a1; + vec4 b1; + mat4 c1; +} _11; + +void main() +{ + _11.a *= _11.a1; + _11.b *= _11.b1; + _11.c = _11.c * _11.c1; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/rmw-opt.comp b/third_party/spirv-cross/reference/shaders/comp/rmw-opt.comp new file mode 100644 index 0000000..e3fba78 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/rmw-opt.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + int a; +} _9; + +void main() +{ + _9.a += 10; + _9.a -= 10; + _9.a *= 10; + _9.a /= 10; + _9.a = _9.a << 2; + _9.a = _9.a >> 3; + _9.a &= 40; + _9.a ^= 10; + _9.a %= 40; + _9.a |= 1; + bool c = false; + bool d = true; + c = c && d; + d = d || c; + _9.a = int(c && d); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/reference/shaders/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..c28face --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,19 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float a; + float b; + float c; + float d; + float e; +} _9; + +void main() +{ + _9.c = distance(_9.a, _9.b); + _9.d = length(_9.a); + _9.e = normalize(_9.a); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/shared.comp b/third_party/spirv-cross/reference/shaders/comp/shared.comp new file mode 100644 index 0000000..545ef22 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/shared.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + float in_data[]; +} _22; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + float out_data[]; +} _44; + +shared float sShared[4]; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + float idata = _22.in_data[ident]; + sShared[gl_LocalInvocationIndex] = idata; + barrier(); + _44.out_data[ident] = sShared[(4u - gl_LocalInvocationIndex) - 1u]; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/ssbo-array-length.comp b/third_party/spirv-cross/reference/shaders/comp/ssbo-array-length.comp new file mode 100644 index 0000000..ddc666e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/ssbo-array-length.comp @@ -0,0 +1,14 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 1, std140) buffer SSBO +{ + uint size; + float v[]; +} _11; + +void main() +{ + _11.size = uint(int(uint(_11.v.length()))); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/ssbo-array.comp b/third_party/spirv-cross/reference/shaders/comp/ssbo-array.comp new file mode 100644 index 0000000..e773bd0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/ssbo-array.comp @@ -0,0 +1,14 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + vec4 data[]; +} ssbos[2]; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + ssbos[1].data[ident] = ssbos[0].data[ident]; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/struct-layout.comp b/third_party/spirv-cross/reference/shaders/comp/struct-layout.comp new file mode 100644 index 0000000..4feea8b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/struct-layout.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct Foo +{ + mat4 m; +}; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + Foo out_data[]; +} _23; + +layout(binding = 0, std430) readonly buffer SSBO +{ + Foo in_data[]; +} _30; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + _23.out_data[ident].m = _30.in_data[ident].m * _30.in_data[ident].m; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/struct-packing.comp b/third_party/spirv-cross/reference/shaders/comp/struct-packing.comp new file mode 100644 index 0000000..f4b5834 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/struct-packing.comp @@ -0,0 +1,104 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + S4 m3s[8]; +}; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + mat2 m0; + mat2 m1; + mat2x3 m2[4]; + mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + mat2 m0; + mat2 m1; + mat2x3 m2[4]; + mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content.m0s[0].a[0] = ssbo_140.content.m0s[0].a[0]; + ssbo_430.content.m0s[0].b = ssbo_140.content.m0s[0].b; + ssbo_430.content.m1s[0].a = ssbo_140.content.m1s[0].a; + ssbo_430.content.m1s[0].b = ssbo_140.content.m1s[0].b; + ssbo_430.content.m2s[0].a[0] = ssbo_140.content.m2s[0].a[0]; + ssbo_430.content.m2s[0].b = ssbo_140.content.m2s[0].b; + ssbo_430.content.m0.a[0] = ssbo_140.content.m0.a[0]; + ssbo_430.content.m0.b = ssbo_140.content.m0.b; + ssbo_430.content.m1.a = ssbo_140.content.m1.a; + ssbo_430.content.m1.b = ssbo_140.content.m1.b; + ssbo_430.content.m2.a[0] = ssbo_140.content.m2.a[0]; + ssbo_430.content.m2.b = ssbo_140.content.m2.b; + ssbo_430.content.m3.a = ssbo_140.content.m3.a; + ssbo_430.content.m3.b = ssbo_140.content.m3.b; + ssbo_430.content.m4 = ssbo_140.content.m4; + ssbo_430.content.m3s[0].c = ssbo_140.content.m3s[0].c; + ssbo_430.content.m3s[1].c = ssbo_140.content.m3s[1].c; + ssbo_430.content.m3s[2].c = ssbo_140.content.m3s[2].c; + ssbo_430.content.m3s[3].c = ssbo_140.content.m3s[3].c; + ssbo_430.content.m3s[4].c = ssbo_140.content.m3s[4].c; + ssbo_430.content.m3s[5].c = ssbo_140.content.m3s[5].c; + ssbo_430.content.m3s[6].c = ssbo_140.content.m3s[6].c; + ssbo_430.content.m3s[7].c = ssbo_140.content.m3s[7].c; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/torture-loop.comp b/third_party/spirv-cross/reference/shaders/comp/torture-loop.comp new file mode 100644 index 0000000..645af5c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/torture-loop.comp @@ -0,0 +1,49 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +} _24; + +layout(binding = 1, std430) writeonly buffer SSBO2 +{ + vec4 out_data[]; +} _89; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idat = _24.in_data[ident]; + int k = 0; + for (;;) + { + int _39 = k; + int _40 = _39 + 1; + k = _40; + if (_40 < 10) + { + idat *= 2.0; + k++; + continue; + } + else + { + break; + } + } + for (uint i = 0u; i < 16u; i++, k++) + { + for (uint j = 0u; j < 30u; j++) + { + idat = _24.mvp * idat; + } + } + do + { + k++; + } while (k > 10); + _89.out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/type-alias.comp b/third_party/spirv-cross/reference/shaders/comp/type-alias.comp new file mode 100644 index 0000000..51f3792 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/type-alias.comp @@ -0,0 +1,49 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct S0 +{ + vec4 a; +}; + +struct S1 +{ + vec4 a; +}; + +layout(binding = 0, std430) buffer SSBO0 +{ + S0 s0s[]; +} _36; + +layout(binding = 1, std430) buffer SSBO1 +{ + S1 s1s[]; +} _55; + +layout(binding = 2, std430) buffer SSBO2 +{ + vec4 outputs[]; +} _66; + +vec4 overload(S0 s0) +{ + return s0.a; +} + +vec4 overload(S1 s1) +{ + return s1.a; +} + +void main() +{ + S0 s0; + s0.a = _36.s0s[gl_GlobalInvocationID.x].a; + S1 s1; + s1.a = _55.s1s[gl_GlobalInvocationID.x].a; + S0 param = s0; + S1 param_1 = s1; + _66.outputs[gl_GlobalInvocationID.x] = overload(param) + overload(param_1); +} + diff --git a/third_party/spirv-cross/reference/shaders/comp/udiv.comp b/third_party/spirv-cross/reference/shaders/comp/udiv.comp new file mode 100644 index 0000000..0c1f926 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/comp/udiv.comp @@ -0,0 +1,18 @@ +#version 310 es +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, std430) buffer SSBO2 +{ + uint outputs[]; +} _10; + +layout(binding = 0, std430) buffer SSBO +{ + uint inputs[]; +} _23; + +void main() +{ + _10.outputs[gl_GlobalInvocationID.x] = _23.inputs[gl_GlobalInvocationID.x] / 29u; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/comp/enhanced-layouts.comp b/third_party/spirv-cross/reference/shaders/desktop-only/comp/enhanced-layouts.comp new file mode 100644 index 0000000..ba37ca2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/comp/enhanced-layouts.comp @@ -0,0 +1,40 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct Foo +{ + int a; + int b; + int c; +}; + +layout(binding = 1, std140) buffer SSBO1 +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ssbo1; + +layout(binding = 2, std430) buffer SSBO2 +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ssbo2; + +layout(binding = 0, std140) uniform UBO +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ubo; + +void main() +{ + ssbo1.a = ssbo2.a; + ssbo1.b = ubo.b; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/comp/extended-arithmetic.desktop.comp b/third_party/spirv-cross/reference/shaders/desktop-only/comp/extended-arithmetic.desktop.comp new file mode 100644 index 0000000..9c55c74 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/comp/extended-arithmetic.desktop.comp @@ -0,0 +1,159 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct ResType +{ + uint _m0; + uint _m1; +}; + +struct ResType_1 +{ + uvec2 _m0; + uvec2 _m1; +}; + +struct ResType_2 +{ + uvec3 _m0; + uvec3 _m1; +}; + +struct ResType_3 +{ + uvec4 _m0; + uvec4 _m1; +}; + +struct ResType_4 +{ + int _m0; + int _m1; +}; + +struct ResType_5 +{ + ivec2 _m0; + ivec2 _m1; +}; + +struct ResType_6 +{ + ivec3 _m0; + ivec3 _m1; +}; + +struct ResType_7 +{ + ivec4 _m0; + ivec4 _m1; +}; + +layout(binding = 0, std430) buffer SSBOUint +{ + uint a; + uint b; + uint c; + uint d; + uvec2 a2; + uvec2 b2; + uvec2 c2; + uvec2 d2; + uvec3 a3; + uvec3 b3; + uvec3 c3; + uvec3 d3; + uvec4 a4; + uvec4 b4; + uvec4 c4; + uvec4 d4; +} u; + +layout(binding = 1, std430) buffer SSBOInt +{ + int a; + int b; + int c; + int d; + ivec2 a2; + ivec2 b2; + ivec2 c2; + ivec2 d2; + ivec3 a3; + ivec3 b3; + ivec3 c3; + ivec3 d3; + ivec4 a4; + ivec4 b4; + ivec4 c4; + ivec4 d4; +} i; + +void main() +{ + ResType _25; + _25._m0 = uaddCarry(u.a, u.b, _25._m1); + u.d = _25._m1; + u.c = _25._m0; + ResType_1 _40; + _40._m0 = uaddCarry(u.a2, u.b2, _40._m1); + u.d2 = _40._m1; + u.c2 = _40._m0; + ResType_2 _55; + _55._m0 = uaddCarry(u.a3, u.b3, _55._m1); + u.d3 = _55._m1; + u.c3 = _55._m0; + ResType_3 _70; + _70._m0 = uaddCarry(u.a4, u.b4, _70._m1); + u.d4 = _70._m1; + u.c4 = _70._m0; + ResType _79; + _79._m0 = usubBorrow(u.a, u.b, _79._m1); + u.d = _79._m1; + u.c = _79._m0; + ResType_1 _88; + _88._m0 = usubBorrow(u.a2, u.b2, _88._m1); + u.d2 = _88._m1; + u.c2 = _88._m0; + ResType_2 _97; + _97._m0 = usubBorrow(u.a3, u.b3, _97._m1); + u.d3 = _97._m1; + u.c3 = _97._m0; + ResType_3 _106; + _106._m0 = usubBorrow(u.a4, u.b4, _106._m1); + u.d4 = _106._m1; + u.c4 = _106._m0; + ResType _116; + umulExtended(u.a, u.b, _116._m1, _116._m0); + u.d = _116._m0; + u.c = _116._m1; + ResType_1 _125; + umulExtended(u.a2, u.b2, _125._m1, _125._m0); + u.d2 = _125._m0; + u.c2 = _125._m1; + ResType_2 _134; + umulExtended(u.a3, u.b3, _134._m1, _134._m0); + u.d3 = _134._m0; + u.c3 = _134._m1; + ResType_3 _143; + umulExtended(u.a4, u.b4, _143._m1, _143._m0); + u.d4 = _143._m0; + u.c4 = _143._m1; + ResType_4 _160; + imulExtended(i.a, i.b, _160._m1, _160._m0); + i.d = _160._m0; + i.c = _160._m1; + ResType_5 _171; + imulExtended(i.a2, i.b2, _171._m1, _171._m0); + i.d2 = _171._m0; + i.c2 = _171._m1; + ResType_6 _182; + imulExtended(i.a3, i.b3, _182._m1, _182._m0); + i.d3 = _182._m0; + i.c3 = _182._m1; + ResType_7 _193; + imulExtended(i.a4, i.b4, _193._m1, _193._m0); + i.d4 = _193._m0; + i.c4 = _193._m1; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/comp/fp64.desktop.comp b/third_party/spirv-cross/reference/shaders/desktop-only/comp/fp64.desktop.comp new file mode 100644 index 0000000..c9e5e84 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/comp/fp64.desktop.comp @@ -0,0 +1,83 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct M0 +{ + double v; + dvec2 b[2]; + dmat2x3 c; + dmat3x2 d; +}; + +layout(binding = 0, std430) buffer SSBO0 +{ + dvec4 a; + M0 m0; + dmat4 b; +} ssbo_0; + +layout(binding = 1, std430) buffer SSBO1 +{ + dmat4 a; + dvec4 b; + M0 m0; +} ssbo_1; + +layout(binding = 2, std430) buffer SSBO2 +{ + double a[4]; + dvec2 b[4]; +} ssbo_2; + +layout(binding = 3, std140) buffer SSBO3 +{ + double a[4]; + dvec2 b[4]; +} ssbo_3; + +void main() +{ + ssbo_0.a += dvec4(10.0lf, 20.0lf, 30.0lf, 40.0lf); + ssbo_0.a += dvec4(20.0lf); + dvec4 a = ssbo_0.a; + dmat4 amat = ssbo_0.b; + ssbo_0.a = abs(a); + ssbo_0.a = sign(a); + ssbo_0.a = floor(a); + ssbo_0.a = trunc(a); + ssbo_0.a = round(a); + ssbo_0.a = roundEven(a); + ssbo_0.a = ceil(a); + ssbo_0.a = fract(a); + ssbo_0.a = mod(a, dvec4(20.0lf)); + ssbo_0.a = mod(a, a); + ssbo_0.a = min(a, a); + ssbo_0.a = max(a, a); + ssbo_0.a = clamp(a, a, a); + ssbo_0.a = mix(a, a, a); + ssbo_0.a = step(a, a); + ssbo_0.a = smoothstep(a, a, a); + bvec4 b = isnan(a); + bvec4 c = isinf(a); + double f = packDouble2x32(uvec2(10u, 40u)); + uvec2 g = unpackDouble2x32(f); + double d = length(a); + d = distance(a, a); + d = dot(a, a); + dvec3 e = cross(a.xyz, a.yzw); + a = faceforward(a, a, a); + a = reflect(a, a); + dmat4 l = dmat4(amat[0] * amat[0], amat[1] * amat[1], amat[2] * amat[2], amat[3] * amat[3]); + l = outerProduct(a, a); + l = transpose(l); + double m = determinant(l); + l = inverse(l); + bvec4 k = lessThan(a, a); + k = lessThanEqual(a, a); + k = greaterThan(a, a); + k = greaterThanEqual(a, a); + ssbo_1.b.x += 1.0lf; + ssbo_2.b[0].x += 1.0lf; + ssbo_3.b[0].x += 1.0lf; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp b/third_party/spirv-cross/reference/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp new file mode 100644 index 0000000..7a07975 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp @@ -0,0 +1,47 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(binding = 0, rgba32f) uniform readonly writeonly image2D uImg00; +layout(binding = 1, rgba16f) uniform readonly writeonly image2D uImg01; +layout(binding = 2, rg32f) uniform readonly writeonly image2D uImg02; +layout(binding = 3, rg16f) uniform readonly writeonly image2D uImg03; +layout(binding = 4, r11f_g11f_b10f) uniform readonly writeonly image2D uImg04; +layout(binding = 5, r32f) uniform readonly writeonly image2D uImg05; +layout(binding = 6, r16f) uniform readonly writeonly image2D uImg06; +layout(binding = 7, rgba16) uniform readonly writeonly image2D uImg07; +layout(binding = 8, rgb10_a2) uniform readonly writeonly image2D uImg08; +layout(binding = 9, rgba8) uniform readonly writeonly image2D uImg09; +layout(binding = 10, rg16) uniform readonly writeonly image2D uImg10; +layout(binding = 11, rg8) uniform readonly writeonly image2D uImg11; +layout(binding = 12, r16) uniform readonly writeonly image2D uImg12; +layout(binding = 13, r8) uniform readonly writeonly image2D uImg13; +layout(binding = 14, rgba16_snorm) uniform readonly writeonly image2D uImg14; +layout(binding = 15, rgba8_snorm) uniform readonly writeonly image2D uImg15; +layout(binding = 16, rg16_snorm) uniform readonly writeonly image2D uImg16; +layout(binding = 17, rg8_snorm) uniform readonly writeonly image2D uImg17; +layout(binding = 18, r16_snorm) uniform readonly writeonly image2D uImg18; +layout(binding = 19, r8_snorm) uniform readonly writeonly image2D uImg19; +layout(binding = 20, rgba32i) uniform readonly writeonly iimage2D uImage20; +layout(binding = 21, rgba16i) uniform readonly writeonly iimage2D uImage21; +layout(binding = 22, rgba8i) uniform readonly writeonly iimage2D uImage22; +layout(binding = 23, rg32i) uniform readonly writeonly iimage2D uImage23; +layout(binding = 24, rg16i) uniform readonly writeonly iimage2D uImage24; +layout(binding = 25, rg8i) uniform readonly writeonly iimage2D uImage25; +layout(binding = 26, r32i) uniform readonly writeonly iimage2D uImage26; +layout(binding = 27, r16i) uniform readonly writeonly iimage2D uImage27; +layout(binding = 28, r8i) uniform readonly writeonly iimage2D uImage28; +layout(binding = 29, rgba32ui) uniform readonly writeonly uimage2D uImage29; +layout(binding = 30, rgba16ui) uniform readonly writeonly uimage2D uImage30; +layout(binding = 31, rgb10_a2ui) uniform readonly writeonly uimage2D uImage31; +layout(binding = 32, rgba8ui) uniform readonly writeonly uimage2D uImage32; +layout(binding = 33, rg32ui) uniform readonly writeonly uimage2D uImage33; +layout(binding = 34, rg16ui) uniform readonly writeonly uimage2D uImage34; +layout(binding = 35, rg8ui) uniform readonly writeonly uimage2D uImage35; +layout(binding = 36, r32ui) uniform readonly writeonly uimage2D uImage36; +layout(binding = 37, r16ui) uniform readonly writeonly uimage2D uImage37; +layout(binding = 38, r8ui) uniform readonly writeonly uimage2D uImage38; + +void main() +{ +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/comp/int64.desktop.comp b/third_party/spirv-cross/reference/shaders/desktop-only/comp/int64.desktop.comp new file mode 100644 index 0000000..702456b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/comp/int64.desktop.comp @@ -0,0 +1,52 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct M0 +{ + int64_t v; + i64vec2 b[2]; + uint64_t c; + uint64_t d[5]; +}; + +layout(binding = 0, std430) buffer SSBO0 +{ + i64vec4 a; + M0 m0; +} ssbo_0; + +layout(binding = 1, std430) buffer SSBO1 +{ + u64vec4 b; + M0 m0; +} ssbo_1; + +layout(binding = 2, std430) buffer SSBO2 +{ + int64_t a[4]; + i64vec2 b[4]; +} ssbo_2; + +layout(binding = 3, std140) buffer SSBO3 +{ + int64_t a[4]; + i64vec2 b[4]; +} ssbo_3; + +void main() +{ + ssbo_0.a += i64vec4(10l, 20l, 30l, 40l); + ssbo_1.b += u64vec4(999999999999999999ul, 8888888888888888ul, 77777777777777777ul, 6666666666666666ul); + ssbo_0.a += i64vec4(20l); + ssbo_0.a = abs(ssbo_0.a + i64vec4(ssbo_1.b)); + ssbo_0.a += i64vec4(1l); + ssbo_1.b += u64vec4(i64vec4(1l)); + ssbo_0.a -= i64vec4(1l); + ssbo_1.b -= u64vec4(i64vec4(1l)); + ssbo_1.b = doubleBitsToUint64(int64BitsToDouble(ssbo_0.a)); + ssbo_0.a = doubleBitsToInt64(uint64BitsToDouble(ssbo_1.b)); + ssbo_2.a[0] += 1l; + ssbo_3.a[0] += 2l; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/clip-cull-distance.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/clip-cull-distance.desktop.frag new file mode 100644 index 0000000..3cc3205 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/clip-cull-distance.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +in float gl_ClipDistance[4]; +in float gl_CullDistance[3]; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = gl_ClipDistance[0] + gl_CullDistance[0]; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..391b4de --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,37 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vInput; + +void main() +{ + FragColor = vInput; + vec4 t = texture(uSampler, vInput.xy); + vec4 d0 = dFdx(vInput); + vec4 d1 = dFdy(vInput); + vec4 d2 = fwidth(vInput); + vec4 d3 = dFdxCoarse(vInput); + vec4 d4 = dFdyCoarse(vInput); + vec4 d5 = fwidthCoarse(vInput); + vec4 d6 = dFdxFine(vInput); + vec4 d7 = dFdyFine(vInput); + vec4 d8 = fwidthFine(vInput); + vec2 lod = textureQueryLod(uSampler, vInput.zw); + if (vInput.y > 10.0) + { + FragColor += t; + FragColor += d0; + FragColor += d1; + FragColor += d2; + FragColor += d3; + FragColor += d4; + FragColor += d5; + FragColor += d6; + FragColor += d7; + FragColor += d8; + FragColor += lod.xyxy; + } +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/depth-greater-than.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/depth-greater-than.desktop.frag new file mode 100644 index 0000000..8b7c296 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/depth-greater-than.desktop.frag @@ -0,0 +1,9 @@ +#version 450 +layout(depth_greater) out float gl_FragDepth; +layout(early_fragment_tests) in; + +void main() +{ + gl_FragDepth = 0.5; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/depth-less-than.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/depth-less-than.desktop.frag new file mode 100644 index 0000000..44752eb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/depth-less-than.desktop.frag @@ -0,0 +1,9 @@ +#version 450 +layout(depth_less) out float gl_FragDepth; +layout(early_fragment_tests) in; + +void main() +{ + gl_FragDepth = 0.5; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/dual-source-blending.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/dual-source-blending.desktop.frag new file mode 100644 index 0000000..3d946b0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/dual-source-blending.desktop.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0, index = 0) out vec4 FragColor0; +layout(location = 0, index = 1) out vec4 FragColor1; + +void main() +{ + FragColor0 = vec4(1.0); + FragColor1 = vec4(2.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag new file mode 100644 index 0000000..70843a8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag @@ -0,0 +1,24 @@ +#version 450 + +layout(binding = 0, std430) buffer Foobar +{ + vec4 _data[]; +} Foobar_1; + +layout(binding = 1, std430) buffer Foobaz +{ + vec4 _data[]; +} Foobaz_1; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 _main() +{ + return Foobar_1._data[0] + Foobaz_1._data[0]; +} + +void main() +{ + _entryPointOutput = _main(); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-ms.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-ms.desktop.frag new file mode 100644 index 0000000..24644be --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-ms.desktop.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(binding = 0, rgba8) uniform image2DMS uImage; +layout(binding = 1, rgba8) uniform image2DMSArray uImageArray; + +void main() +{ + vec4 a = imageLoad(uImage, ivec2(1, 2), 2); + vec4 b = imageLoad(uImageArray, ivec3(1, 2, 4), 3); + imageStore(uImage, ivec2(2, 3), 1, a); + imageStore(uImageArray, ivec3(2, 3, 7), 1, b); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-query.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-query.desktop.frag new file mode 100644 index 0000000..6f36d5d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-query.desktop.frag @@ -0,0 +1,53 @@ +#version 450 + +layout(binding = 0) uniform sampler1D uSampler1D; +layout(binding = 1) uniform sampler2D uSampler2D; +layout(binding = 2) uniform sampler2DArray uSampler2DArray; +layout(binding = 3) uniform sampler3D uSampler3D; +layout(binding = 4) uniform samplerCube uSamplerCube; +layout(binding = 5) uniform samplerCubeArray uSamplerCubeArray; +layout(binding = 6) uniform samplerBuffer uSamplerBuffer; +layout(binding = 7) uniform sampler2DMS uSamplerMS; +layout(binding = 8) uniform sampler2DMSArray uSamplerMSArray; +layout(binding = 9, r32f) uniform readonly writeonly image1D uImage1D; +layout(binding = 10, r32f) uniform readonly writeonly image2D uImage2D; +layout(binding = 11, r32f) uniform readonly writeonly image2DArray uImage2DArray; +layout(binding = 12, r32f) uniform readonly writeonly image3D uImage3D; +layout(binding = 13, r32f) uniform readonly writeonly imageCube uImageCube; +layout(binding = 14, r32f) uniform readonly writeonly imageCubeArray uImageCubeArray; +layout(binding = 15, r32f) uniform readonly writeonly imageBuffer uImageBuffer; +layout(binding = 16, r32f) uniform readonly writeonly image2DMS uImageMS; +layout(binding = 17, r32f) uniform readonly writeonly image2DMSArray uImageMSArray; + +void main() +{ + int a = textureSize(uSampler1D, 0); + ivec2 b = textureSize(uSampler2D, 0); + ivec3 c = textureSize(uSampler2DArray, 0); + ivec3 d = textureSize(uSampler3D, 0); + ivec2 e = textureSize(uSamplerCube, 0); + ivec3 f = textureSize(uSamplerCubeArray, 0); + int g = textureSize(uSamplerBuffer); + ivec2 h = textureSize(uSamplerMS); + ivec3 i = textureSize(uSamplerMSArray); + int l0 = textureQueryLevels(uSampler1D); + int l1 = textureQueryLevels(uSampler2D); + int l2 = textureQueryLevels(uSampler2DArray); + int l3 = textureQueryLevels(uSampler3D); + int l4 = textureQueryLevels(uSamplerCube); + int l5 = textureQueryLevels(uSamplerCubeArray); + a = imageSize(uImage1D); + b = imageSize(uImage2D); + c = imageSize(uImage2DArray); + d = imageSize(uImage3D); + e = imageSize(uImageCube); + f = imageSize(uImageCubeArray); + g = imageSize(uImageBuffer); + h = imageSize(uImageMS); + i = imageSize(uImageMSArray); + int s0 = textureSamples(uSamplerMS); + int s1 = textureSamples(uSamplerMSArray); + int s2 = imageSamples(uImageMS); + int s3 = imageSamples(uImageMSArray); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-size.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-size.frag new file mode 100644 index 0000000..5bb0603 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-size.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0, r32f) uniform readonly writeonly image2D uImage1; +layout(binding = 1, r32f) uniform readonly writeonly image2D uImage2; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(vec2(imageSize(uImage1)), vec2(imageSize(uImage2))); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag new file mode 100644 index 0000000..1d90620 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0, r32f) uniform image2D uImage1; +layout(binding = 1, r32f) uniform image2D uImage2; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(vec2(imageSize(uImage1)), vec2(imageSize(uImage2))); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/in-block-qualifiers.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/in-block-qualifiers.frag new file mode 100644 index 0000000..d462280 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/in-block-qualifiers.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in VertexData +{ + flat float f; + centroid vec4 g; + flat int h; + float i; +} vin; + +layout(location = 4) flat in float f; +layout(location = 5) centroid in vec4 g; +layout(location = 6) flat in int h; +layout(location = 7) sample in float i; + +void main() +{ + FragColor = ((((((vec4(vin.f) + vin.g) + vec4(float(vin.h))) + vec4(vin.i)) + vec4(f)) + g) + vec4(float(h))) + vec4(i); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/layout-component.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/layout-component.desktop.frag new file mode 100644 index 0000000..13f17fe --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/layout-component.desktop.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) out vec2 FragColor; +layout(location = 0, component = 0) in vec2 v0; +layout(location = 0, component = 2) in float v1; +in Vertex +{ + layout(location = 1, component = 2) float v3; +} _20; + + +void main() +{ + FragColor = (v0 + vec2(v1)) + vec2(_20.v3); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/query-levels.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/query-levels.desktop.frag new file mode 100644 index 0000000..4a80cbf --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/query-levels.desktop.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(textureQueryLevels(uSampler))); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/query-lod.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/query-lod.desktop.frag new file mode 100644 index 0000000..f43543b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/query-lod.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTexCoord; + +void main() +{ + FragColor = textureQueryLod(uSampler, vTexCoord).xyxy; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/sampler-ms-query.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/sampler-ms-query.desktop.frag new file mode 100644 index 0000000..4c30ed1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/sampler-ms-query.desktop.frag @@ -0,0 +1,14 @@ +#version 450 + +layout(binding = 0) uniform sampler2DMS uSampler; +layout(binding = 1) uniform sampler2DMSArray uSamplerArray; +layout(binding = 2, rgba8) uniform readonly writeonly image2DMS uImage; +layout(binding = 3, rgba8) uniform readonly writeonly image2DMSArray uImageArray; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(((textureSamples(uSampler) + textureSamples(uSamplerArray)) + imageSamples(uImage)) + imageSamples(uImageArray))); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/stencil-export.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/stencil-export.desktop.frag new file mode 100644 index 0000000..65082a8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/stencil-export.desktop.frag @@ -0,0 +1,13 @@ +#version 450 +#extension GL_ARB_shader_stencil_export : require + +layout(location = 0) out vec4 MRT0; +layout(location = 1) out vec4 MRT1; + +void main() +{ + MRT0 = vec4(1.0); + MRT1 = vec4(1.0, 0.0, 1.0, 1.0); + gl_FragStencilRefARB = 100; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag b/third_party/spirv-cross/reference/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag new file mode 100644 index 0000000..d5e45bd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag @@ -0,0 +1,26 @@ +#version 450 + +layout(binding = 0) uniform sampler1DShadow uShadow1D; +layout(binding = 1) uniform sampler2DShadow uShadow2D; +layout(binding = 2) uniform sampler1D uSampler1D; +layout(binding = 3) uniform sampler2D uSampler2D; +layout(binding = 4) uniform sampler3D uSampler3D; + +layout(location = 0) out float FragColor; +layout(location = 1) in vec4 vClip4; +layout(location = 2) in vec2 vClip2; +layout(location = 0) in vec3 vClip3; + +void main() +{ + vec4 _20 = vClip4; + _20.y = vClip4.w; + FragColor = textureProj(uShadow1D, vec4(_20.x, 0.0, vClip4.z, _20.y)); + vec4 _30 = vClip4; + _30.z = vClip4.w; + FragColor = textureProj(uShadow2D, vec4(_30.xy, vClip4.z, _30.z)); + FragColor = textureProj(uSampler1D, vClip2).x; + FragColor = textureProj(uSampler2D, vClip3).x; + FragColor = textureProj(uSampler3D, vClip4).x; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/geom/basic.desktop.sso.geom b/third_party/spirv-cross/reference/shaders/desktop-only/geom/basic.desktop.sso.geom new file mode 100644 index 0000000..f1afee6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/geom/basic.desktop.sso.geom @@ -0,0 +1,35 @@ +#version 450 +layout(invocations = 4, triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[3]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal + vec3(float(gl_InvocationID)); + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal + vec3(4.0 * float(gl_InvocationID)); + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal + vec3(2.0 * float(gl_InvocationID)); + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/geom/viewport-index.desktop.geom b/third_party/spirv-cross/reference/shaders/desktop-only/geom/viewport-index.desktop.geom new file mode 100644 index 0000000..773aeb8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/geom/viewport-index.desktop.geom @@ -0,0 +1,9 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 4, triangle_strip) out; + +void main() +{ + gl_ViewportIndex = 1; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/tesc/basic.desktop.sso.tesc b/third_party/spirv-cross/reference/shaders/desktop-only/tesc/basic.desktop.sso.tesc new file mode 100644 index 0000000..c51699d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/tesc/basic.desktop.sso.tesc @@ -0,0 +1,27 @@ +#version 450 +layout(vertices = 1) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +out gl_PerVertex +{ + vec4 gl_Position; +} gl_out[1]; + +layout(location = 0) patch out vec3 vFoo; + +void main() +{ + gl_TessLevelInner[0] = 8.8999996185302734375; + gl_TessLevelInner[1] = 6.900000095367431640625; + gl_TessLevelOuter[0] = 8.8999996185302734375; + gl_TessLevelOuter[1] = 6.900000095367431640625; + gl_TessLevelOuter[2] = 3.900000095367431640625; + gl_TessLevelOuter[3] = 4.900000095367431640625; + vFoo = vec3(1.0); + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/tese/triangle.desktop.sso.tese b/third_party/spirv-cross/reference/shaders/desktop-only/tese/triangle.desktop.sso.tese new file mode 100644 index 0000000..c9bacd4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/tese/triangle.desktop.sso.tese @@ -0,0 +1,18 @@ +#version 450 +layout(triangles, cw, fractional_even_spacing) in; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = ((gl_in[0].gl_Position * gl_TessCoord.x) + (gl_in[1].gl_Position * gl_TessCoord.y)) + (gl_in[2].gl_Position * gl_TessCoord.z); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/vert/basic.desktop.sso.vert b/third_party/spirv-cross/reference/shaders/desktop-only/vert/basic.desktop.sso.vert new file mode 100644 index 0000000..2f88039 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/vert/basic.desktop.sso.vert @@ -0,0 +1,22 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(binding = 0, std140) uniform UBO +{ + mat4 uMVP; +} _16; + +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec3 vNormal; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = _16.uMVP * aVertex; + vNormal = aNormal; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert b/third_party/spirv-cross/reference/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert new file mode 100644 index 0000000..a7c5d76 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert @@ -0,0 +1,20 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[4]; + float gl_CullDistance[3]; +}; + +void main() +{ + gl_Position = vec4(1.0); + gl_ClipDistance[0] = 0.0; + gl_ClipDistance[1] = 0.0; + gl_ClipDistance[2] = 0.0; + gl_ClipDistance[3] = 0.0; + gl_CullDistance[1] = 4.0; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/vert/clip-cull-distance.desktop.vert b/third_party/spirv-cross/reference/shaders/desktop-only/vert/clip-cull-distance.desktop.vert new file mode 100644 index 0000000..2f3d49f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/vert/clip-cull-distance.desktop.vert @@ -0,0 +1,15 @@ +#version 450 + +out float gl_ClipDistance[4]; +out float gl_CullDistance[3]; + +void main() +{ + gl_Position = vec4(1.0); + gl_ClipDistance[0] = 0.0; + gl_ClipDistance[1] = 0.0; + gl_ClipDistance[2] = 0.0; + gl_ClipDistance[3] = 0.0; + gl_CullDistance[1] = 4.0; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/vert/out-block-qualifiers.vert b/third_party/spirv-cross/reference/shaders/desktop-only/vert/out-block-qualifiers.vert new file mode 100644 index 0000000..7c73168 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/vert/out-block-qualifiers.vert @@ -0,0 +1,27 @@ +#version 450 + +layout(location = 0) out VertexData +{ + flat float f; + centroid vec4 g; + flat int h; + float i; +} vout; + +layout(location = 4) flat out float f; +layout(location = 5) centroid out vec4 g; +layout(location = 6) flat out int h; +layout(location = 7) out float i; + +void main() +{ + vout.f = 10.0; + vout.g = vec4(20.0); + vout.h = 20; + vout.i = 30.0; + f = 10.0; + g = vec4(20.0); + h = 20; + i = 30.0; +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert b/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert new file mode 100644 index 0000000..2b3c5ce --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert @@ -0,0 +1,24 @@ +#version 450 +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseVertex gl_BaseVertexARB +#else +uniform int SPIRV_Cross_BaseVertex; +#endif +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +#ifndef GL_ARB_shader_draw_parameters +#error GL_ARB_shader_draw_parameters is not supported. +#endif + +void main() +{ + gl_Position = vec4(float(SPIRV_Cross_BaseVertex), float(SPIRV_Cross_BaseInstance), float(gl_DrawIDARB), 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert.vk b/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert.vk new file mode 100644 index 0000000..6121dd8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert.vk @@ -0,0 +1,8 @@ +#version 450 +#extension GL_ARB_shader_draw_parameters : require + +void main() +{ + gl_Position = vec4(float(gl_BaseVertexARB), float(gl_BaseInstanceARB), float(gl_DrawIDARB), 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert b/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert new file mode 100644 index 0000000..bc16d04 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert @@ -0,0 +1,24 @@ +#version 460 +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseVertex gl_BaseVertexARB +#else +uniform int SPIRV_Cross_BaseVertex; +#endif +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +#ifndef GL_ARB_shader_draw_parameters +#error GL_ARB_shader_draw_parameters is not supported. +#endif + +void main() +{ + gl_Position = vec4(float(SPIRV_Cross_BaseVertex), float(SPIRV_Cross_BaseInstance), float(gl_DrawIDARB), 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert.vk b/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert.vk new file mode 100644 index 0000000..b6948fb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert.vk @@ -0,0 +1,7 @@ +#version 460 + +void main() +{ + gl_Position = vec4(float(gl_BaseVertex), float(gl_BaseInstance), float(gl_DrawID), 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/array.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/array.flatten.vert new file mode 100644 index 0000000..5afde34 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/array.flatten.vert @@ -0,0 +1,12 @@ +#version 310 es + +uniform vec4 UBO[56]; +layout(location = 0) in vec4 aVertex; + +void main() +{ + vec4 a4 = UBO[23]; + vec4 offset = (UBO[50] + UBO[45]) + vec4(UBO[54].x); + gl_Position = ((mat4(UBO[40], UBO[41], UBO[42], UBO[43]) * aVertex) + UBO[55]) + offset; +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/basic.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/basic.flatten.vert new file mode 100644 index 0000000..f7eb758 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/basic.flatten.vert @@ -0,0 +1,13 @@ +#version 310 es + +uniform vec4 UBO[4]; +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec3 vNormal; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex; + vNormal = aNormal; +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/copy.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/copy.flatten.vert new file mode 100644 index 0000000..4703d93 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/copy.flatten.vert @@ -0,0 +1,30 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + vec4 Color; +}; + +uniform vec4 UBO[12]; +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec4 vColor; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex; + vColor = vec4(0.0); + Light light; + for (int i = 0; i < 4; i++) + { + Light _52 = Light(UBO[i * 2 + 4].xyz, UBO[i * 2 + 4].w, UBO[i * 2 + 5]); + light.Position = _52.Position; + light.Radius = _52.Radius; + light.Color = _52.Color; + vec3 L = aVertex.xyz - light.Position; + vColor += ((UBO[i * 2 + 5] * clamp(1.0 - (length(L) / light.Radius), 0.0, 1.0)) * dot(aNormal, normalize(L))); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/dynamic.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/dynamic.flatten.vert new file mode 100644 index 0000000..8be397e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/dynamic.flatten.vert @@ -0,0 +1,25 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + vec4 Color; +}; + +uniform vec4 UBO[12]; +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec4 vColor; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex; + vColor = vec4(0.0); + for (int i = 0; i < 4; i++) + { + vec3 L = aVertex.xyz - UBO[i * 2 + 4].xyz; + vColor += ((UBO[i * 2 + 5] * clamp(1.0 - (length(L) / UBO[i * 2 + 4].w), 0.0, 1.0)) * dot(aNormal, normalize(L))); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/matrix-conversion.flatten.frag b/third_party/spirv-cross/reference/shaders/flatten/matrix-conversion.flatten.frag new file mode 100644 index 0000000..ee79bf5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/matrix-conversion.flatten.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform vec4 UBO[4]; +layout(location = 0) out vec3 FragColor; +layout(location = 0) flat in vec3 vNormal; + +void main() +{ + mat4 _19 = mat4(UBO[0], UBO[1], UBO[2], UBO[3]); + FragColor = mat3(_19[0].xyz, _19[1].xyz, _19[2].xyz) * vNormal; +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/matrixindex.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/matrixindex.flatten.vert new file mode 100644 index 0000000..f6d0fa4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/matrixindex.flatten.vert @@ -0,0 +1,19 @@ +#version 310 es + +uniform vec4 UBO[14]; +layout(location = 0) out vec4 oA; +layout(location = 1) out vec4 oB; +layout(location = 2) out vec4 oC; +layout(location = 3) out vec4 oD; +layout(location = 4) out vec4 oE; + +void main() +{ + gl_Position = vec4(0.0); + oA = UBO[1]; + oB = vec4(UBO[4].y, UBO[5].y, UBO[6].y, UBO[7].y); + oC = UBO[9]; + oD = vec4(UBO[10].x, UBO[11].x, UBO[12].x, UBO[13].x); + oE = vec4(UBO[1].z, UBO[6].y, UBO[9].z, UBO[12].y); +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/multiindex.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/multiindex.flatten.vert new file mode 100644 index 0000000..3850bf6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/multiindex.flatten.vert @@ -0,0 +1,10 @@ +#version 310 es + +uniform vec4 UBO[15]; +layout(location = 0) in ivec2 aIndex; + +void main() +{ + gl_Position = UBO[aIndex.x * 5 + aIndex.y * 1 + 0]; +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/push-constant.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/push-constant.flatten.vert new file mode 100644 index 0000000..216c1f9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/push-constant.flatten.vert @@ -0,0 +1,13 @@ +#version 310 es + +uniform vec4 PushMe[6]; +layout(location = 1) in vec4 Pos; +layout(location = 0) out vec2 vRot; +layout(location = 0) in vec2 Rot; + +void main() +{ + gl_Position = mat4(PushMe[0], PushMe[1], PushMe[2], PushMe[3]) * Pos; + vRot = (mat2(PushMe[4].xy, PushMe[4].zw) * Rot) + vec2(PushMe[5].z); +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/rowmajor.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/rowmajor.flatten.vert new file mode 100644 index 0000000..721c490 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/rowmajor.flatten.vert @@ -0,0 +1,11 @@ +#version 310 es + +uniform vec4 UBO[12]; +layout(location = 0) in vec4 aVertex; + +void main() +{ + vec2 v = mat4x2(UBO[8].xy, UBO[9].xy, UBO[10].xy, UBO[11].xy) * aVertex; + gl_Position = (mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex) + (aVertex * mat4(UBO[4], UBO[5], UBO[6], UBO[7])); +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/struct.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/struct.flatten.vert new file mode 100644 index 0000000..3468d52 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/struct.flatten.vert @@ -0,0 +1,22 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + vec4 Color; +}; + +uniform vec4 UBO[6]; +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec4 vColor; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = mat4(UBO[0], UBO[1], UBO[2], UBO[3]) * aVertex; + vColor = vec4(0.0); + vec3 L = aVertex.xyz - UBO[4].xyz; + vColor += ((UBO[5] * clamp(1.0 - (length(L) / UBO[4].w), 0.0, 1.0)) * dot(aNormal, normalize(L))); +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/struct.rowmajor.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/struct.rowmajor.flatten.vert new file mode 100644 index 0000000..3743849 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/struct.rowmajor.flatten.vert @@ -0,0 +1,26 @@ +#version 310 es + +struct Foo +{ + mat3x4 MVP0; + mat3x4 MVP1; +}; + +uniform vec4 UBO[8]; +layout(location = 0) in vec4 v0; +layout(location = 1) in vec4 v1; +layout(location = 0) out vec3 V0; +layout(location = 1) out vec3 V1; + +void main() +{ + Foo _20 = Foo(transpose(mat4x3(UBO[0].xyz, UBO[1].xyz, UBO[2].xyz, UBO[3].xyz)), transpose(mat4x3(UBO[4].xyz, UBO[5].xyz, UBO[6].xyz, UBO[7].xyz))); + Foo f; + f.MVP0 = _20.MVP0; + f.MVP1 = _20.MVP1; + vec3 a = v0 * f.MVP0; + vec3 b = v1 * f.MVP1; + V0 = a; + V1 = b; +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/swizzle.flatten.vert b/third_party/spirv-cross/reference/shaders/flatten/swizzle.flatten.vert new file mode 100644 index 0000000..92afb47 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/swizzle.flatten.vert @@ -0,0 +1,21 @@ +#version 310 es + +uniform vec4 UBO[8]; +layout(location = 0) out vec4 oA; +layout(location = 1) out vec4 oB; +layout(location = 2) out vec4 oC; +layout(location = 3) out vec4 oD; +layout(location = 4) out vec4 oE; +layout(location = 5) out vec4 oF; + +void main() +{ + gl_Position = vec4(0.0); + oA = UBO[0]; + oB = vec4(UBO[1].xy, UBO[1].zw); + oC = vec4(UBO[2].x, UBO[3].xyz); + oD = vec4(UBO[4].xyz, UBO[4].w); + oE = vec4(UBO[5].x, UBO[5].y, UBO[5].z, UBO[5].w); + oF = vec4(UBO[6].x, UBO[6].zw, UBO[7].x); +} + diff --git a/third_party/spirv-cross/reference/shaders/flatten/types.flatten.frag b/third_party/spirv-cross/reference/shaders/flatten/types.flatten.frag new file mode 100644 index 0000000..a74327d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/flatten/types.flatten.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump ivec4 UBO1[2]; +uniform mediump uvec4 UBO2[2]; +uniform vec4 UBO0[2]; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = ((((vec4(UBO1[0]) + vec4(UBO1[1])) + vec4(UBO2[0])) + vec4(UBO2[1])) + UBO0[0]) + UBO0[1]; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/reference/shaders/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..baf2302 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _17[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); + +layout(location = 0) out vec4 FragColor; + +void main() +{ + for (mediump int i = 0; i < 4; i++, FragColor += vec4(_17[i])) + { + } +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/avoid-expression-lowering-to-loop.frag b/third_party/spirv-cross/reference/shaders/frag/avoid-expression-lowering-to-loop.frag new file mode 100644 index 0000000..6313d89 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/avoid-expression-lowering-to-loop.frag @@ -0,0 +1,26 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 1, std140) uniform Count +{ + float count; +} _44; + +layout(binding = 0) uniform mediump sampler2D tex; + +layout(location = 0) in highp vec4 vertex; +layout(location = 0) out vec4 fragColor; + +void main() +{ + highp float size = 1.0 / float(textureSize(tex, 0).x); + float r = 0.0; + float d = dFdx(vertex.x); + for (float i = 0.0; i < _44.count; i += 1.0) + { + r += (size * d); + } + fragColor = vec4(r); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/barycentric-nv.frag b/third_party/spirv-cross/reference/shaders/frag/barycentric-nv.frag new file mode 100644 index 0000000..12d24bb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/barycentric-nv.frag @@ -0,0 +1,20 @@ +#version 450 +#extension GL_NV_fragment_shader_barycentric : require + +layout(binding = 0, std430) readonly buffer Vertices +{ + vec2 uvs[]; +} _19; + +layout(location = 0) out vec2 value; + +void main() +{ + int prim = gl_PrimitiveID; + vec2 uv0 = _19.uvs[(3 * prim) + 0]; + vec2 uv1 = _19.uvs[(3 * prim) + 1]; + vec2 uv2 = _19.uvs[(3 * prim) + 2]; + value = ((uv0 * gl_BaryCoordNV.x) + (uv1 * gl_BaryCoordNV.y)) + (uv2 * gl_BaryCoordNV.z); + value += (((uv0 * gl_BaryCoordNoPerspNV.x) + (uv1 * gl_BaryCoordNoPerspNV.y)) + (uv2 * gl_BaryCoordNoPerspNV.z)); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/basic.frag b/third_party/spirv-cross/reference/shaders/frag/basic.frag new file mode 100644 index 0000000..2a4e440 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/basic.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uTex; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; + +void main() +{ + FragColor = vColor * texture(uTex, vTex); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/reference/shaders/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..738ee6e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,24 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0, std430) buffer UBO +{ + vec4 results[1024]; +} _34; + +layout(binding = 1) uniform highp isampler2D Buf; + +layout(location = 0) flat in mediump int vIn; +layout(location = 1) flat in mediump int vIn2; +layout(location = 0) out vec4 FragColor; + +void main() +{ + mediump ivec4 coords = texelFetch(Buf, ivec2(gl_FragCoord.xy), 0); + vec4 foo = _34.results[coords.x % 16]; + mediump int c = vIn * vIn; + mediump int d = vIn2 * vIn2; + FragColor = (foo + foo) + _34.results[c + d]; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/composite-extract-forced-temporary.frag b/third_party/spirv-cross/reference/shaders/frag/composite-extract-forced-temporary.frag new file mode 100644 index 0000000..e4384f5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/composite-extract-forced-temporary.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D Texture; + +layout(location = 0) in vec2 vTexCoord; +layout(location = 0) out vec4 FragColor; + +void main() +{ + float f = texture(Texture, vTexCoord).x; + FragColor = vec4(f * f); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/constant-array.frag b/third_party/spirv-cross/reference/shaders/frag/constant-array.frag new file mode 100644 index 0000000..fac9560 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/constant-array.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct Foobar +{ + float a; + float b; +}; + +const vec4 _37[3] = vec4[](vec4(1.0), vec4(2.0), vec4(3.0)); +const vec4 _55[2][2] = vec4[][](vec4[](vec4(1.0), vec4(2.0)), vec4[](vec4(8.0), vec4(10.0))); +const Foobar _75[2] = Foobar[](Foobar(10.0, 40.0), Foobar(90.0, 70.0)); + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int index; + +vec4 resolve(Foobar f) +{ + return vec4(f.a + f.b); +} + +void main() +{ + Foobar param = Foobar(10.0, 20.0); + Foobar param_1 = _75[index]; + FragColor = ((_37[index] + _55[index][index + 1]) + resolve(param)) + resolve(param_1); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/constant-composites.frag b/third_party/spirv-cross/reference/shaders/frag/constant-composites.frag new file mode 100644 index 0000000..7813b98 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/constant-composites.frag @@ -0,0 +1,23 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _16[4] = float[](1.0, 4.0, 3.0, 2.0); + +struct Foo +{ + float a; + float b; +}; + +const Foo _28[2] = Foo[](Foo(10.0, 20.0), Foo(30.0, 40.0)); + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int line; + +void main() +{ + FragColor = vec4(_16[line]); + FragColor += vec4(_28[line].a * _28[1 - line].a); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/false-loop-init.frag b/third_party/spirv-cross/reference/shaders/frag/false-loop-init.frag new file mode 100644 index 0000000..b0ed557 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/false-loop-init.frag @@ -0,0 +1,25 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 result; +layout(location = 0) in vec4 accum; + +void main() +{ + result = vec4(0.0); + mediump uint j; + for (mediump int i = 0; i < 4; i += int(j)) + { + if (accum.y > 10.0) + { + j = 40u; + } + else + { + j = 30u; + } + result += accum; + } +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/flush_params.frag b/third_party/spirv-cross/reference/shaders/frag/flush_params.frag new file mode 100644 index 0000000..b4b36ff --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/flush_params.frag @@ -0,0 +1,30 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct Structy +{ + vec4 c; +}; + +layout(location = 0) out vec4 FragColor; + +void foo2(inout Structy f) +{ + f.c = vec4(10.0); +} + +Structy foo() +{ + Structy param; + foo2(param); + Structy f = param; + return f; +} + +void main() +{ + Structy s = foo(); + FragColor = s.c; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/reference/shaders/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..638d89b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,34 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(0.0); + int i = 0; + int _36; + for (;;) + { + if (i < 3) + { + int a = i; + FragColor[a] += float(i); + if (false) + { + _36 = 1; + } + else + { + int _41 = i; + i = _41 + 1; + _36 = _41; + } + continue; + } + else + { + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/for-loop-init.frag b/third_party/spirv-cross/reference/shaders/frag/for-loop-init.frag new file mode 100644 index 0000000..7c22e5c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/for-loop-init.frag @@ -0,0 +1,52 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out mediump int FragColor; + +void main() +{ + FragColor = 16; + for (mediump int i = 0; i < 25; i++) + { + FragColor += 10; + } + for (mediump int i_1 = 1, j = 4; i_1 < 30; i_1++, j += 4) + { + FragColor += 11; + } + mediump int k = 0; + for (; k < 20; k++) + { + FragColor += 12; + } + k += 3; + FragColor += k; + mediump int l; + if (k == 40) + { + l = 0; + for (; l < 40; l++) + { + FragColor += 13; + } + return; + } + else + { + l = k; + FragColor += l; + } + mediump ivec2 i_2 = ivec2(0); + for (; i_2.x < 10; i_2.x += 4) + { + FragColor += i_2.y; + } + mediump int o = k; + for (mediump int m = k; m < 40; m++) + { + FragColor += m; + } + FragColor += o; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/frexp-modf.frag b/third_party/spirv-cross/reference/shaders/frag/frexp-modf.frag new file mode 100644 index 0000000..e495bb3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/frexp-modf.frag @@ -0,0 +1,43 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct ResType +{ + highp float _m0; + int _m1; +}; + +struct ResType_1 +{ + highp vec2 _m0; + ivec2 _m1; +}; + +layout(location = 0) in float v0; +layout(location = 1) in vec2 v1; +layout(location = 0) out float FragColor; + +void main() +{ + ResType _16; + _16._m0 = frexp(v0, _16._m1); + mediump int e0 = _16._m1; + float f0 = _16._m0; + ResType _22; + _22._m0 = frexp(v0 + 1.0, _22._m1); + e0 = _22._m1; + f0 = _22._m0; + ResType_1 _35; + _35._m0 = frexp(v1, _35._m1); + mediump ivec2 e1 = _35._m1; + vec2 f1 = _35._m0; + float r0; + float _41 = modf(v0, r0); + float m0 = _41; + vec2 r1; + vec2 _45 = modf(v1, r1); + vec2 m1 = _45; + FragColor = ((((f0 + f1.x) + f1.y) + m0) + m1.x) + m1.y; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/front-facing.frag b/third_party/spirv-cross/reference/shaders/frag/front-facing.frag new file mode 100644 index 0000000..cc9aecc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/front-facing.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; + +void main() +{ + if (gl_FrontFacing) + { + FragColor = vA; + } + else + { + FragColor = vB; + } +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/gather-dref.frag b/third_party/spirv-cross/reference/shaders/frag/gather-dref.frag new file mode 100644 index 0000000..5416f79 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/gather-dref.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2DShadow uT; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec3 vUV; + +void main() +{ + FragColor = textureGather(uT, vUV.xy, vUV.z); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/ground.frag b/third_party/spirv-cross/reference/shaders/frag/ground.frag new file mode 100644 index 0000000..c36bb31 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/ground.frag @@ -0,0 +1,62 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 4, std140) uniform GlobalPSData +{ + vec4 g_CamPos; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_ResolutionParams; + vec4 g_TimeParams; + vec4 g_FogColor_Distance; +} _101; + +layout(binding = 2) uniform mediump sampler2D TexNormalmap; + +layout(location = 3) out vec4 LightingOut; +layout(location = 2) out vec4 NormalOut; +layout(location = 1) out vec4 SpecularOut; +layout(location = 0) out vec4 AlbedoOut; +layout(location = 0) in vec2 TexCoord; +layout(location = 1) in vec3 EyeVec; + +float saturate(float x) +{ + return clamp(x, 0.0, 1.0); +} + +void Resolve(vec3 Albedo, vec3 Normal, float Roughness, float Metallic) +{ + LightingOut = vec4(0.0); + NormalOut = vec4((Normal * 0.5) + vec3(0.5), 0.0); + SpecularOut = vec4(Roughness, Metallic, 0.0, 0.0); + AlbedoOut = vec4(Albedo, 1.0); +} + +void main() +{ + vec3 Normal = (texture(TexNormalmap, TexCoord).xyz * 2.0) - vec3(1.0); + Normal = normalize(Normal); + float param = length(EyeVec) / 1000.0; + vec2 scatter_uv; + scatter_uv.x = saturate(param); + vec3 nEye = normalize(EyeVec); + scatter_uv.y = 0.0; + vec3 Color = vec3(0.100000001490116119384765625, 0.300000011920928955078125, 0.100000001490116119384765625); + vec3 grass = vec3(0.100000001490116119384765625, 0.300000011920928955078125, 0.100000001490116119384765625); + vec3 dirt = vec3(0.100000001490116119384765625); + vec3 snow = vec3(0.800000011920928955078125); + float grass_snow = smoothstep(0.0, 0.1500000059604644775390625, (_101.g_CamPos.y + EyeVec.y) / 200.0); + vec3 base = mix(grass, snow, vec3(grass_snow)); + float edge = smoothstep(0.699999988079071044921875, 0.75, Normal.y); + Color = mix(dirt, base, vec3(edge)); + Color *= Color; + float Roughness = 1.0 - (edge * grass_snow); + vec3 param_1 = Color; + vec3 param_2 = Normal; + float param_3 = Roughness; + float param_4 = 0.0; + Resolve(param_1, param_2, param_3, param_4); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/helper-invocation.frag b/third_party/spirv-cross/reference/shaders/frag/helper-invocation.frag new file mode 100644 index 0000000..e888ad8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/helper-invocation.frag @@ -0,0 +1,28 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uSampler; + +layout(location = 0) in vec2 vUV; +layout(location = 0) out vec4 FragColor; + +vec4 foo() +{ + vec4 color; + if (!gl_HelperInvocation) + { + color = textureLod(uSampler, vUV, 0.0); + } + else + { + color = vec4(1.0); + } + return color; +} + +void main() +{ + FragColor = foo(); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag b/third_party/spirv-cross/reference/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag new file mode 100644 index 0000000..cd4f7d4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag @@ -0,0 +1,31 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int vA; +layout(location = 1) flat in mediump int vB; + +void main() +{ + FragColor = vec4(0.0); + mediump int k = 0; + mediump int j; + for (mediump int i = 0; i < vA; i += j) + { + if ((vA + i) == 20) + { + k = 50; + } + else + { + if ((vB + i) == 40) + { + k = 60; + } + } + j = k + 10; + FragColor += vec4(1.0); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/image-load-store-uint-coord.asm.frag b/third_party/spirv-cross/reference/shaders/frag/image-load-store-uint-coord.asm.frag new file mode 100644 index 0000000..414dd95 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/image-load-store-uint-coord.asm.frag @@ -0,0 +1,26 @@ +#version 450 + +layout(binding = 1, rgba32f) uniform image2D RWIm; +layout(binding = 0, rgba32f) uniform writeonly imageBuffer RWBuf; +layout(binding = 1) uniform sampler2D ROIm; +layout(binding = 0) uniform samplerBuffer ROBuf; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 _main() +{ + vec4 storeTemp = vec4(10.0, 0.5, 8.0, 2.0); + imageStore(RWIm, ivec2(uvec2(10u)), storeTemp); + vec4 v = imageLoad(RWIm, ivec2(uvec2(30u))); + imageStore(RWBuf, int(80u), v); + v += texelFetch(ROIm, ivec2(uvec2(50u, 60u)), 0); + v += texelFetch(ROBuf, int(80u)); + return v; +} + +void main() +{ + vec4 _45 = _main(); + _entryPointOutput = _45; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/inside-loop-dominated-variable-preservation.frag b/third_party/spirv-cross/reference/shaders/frag/inside-loop-dominated-variable-preservation.frag new file mode 100644 index 0000000..2947b19 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/inside-loop-dominated-variable-preservation.frag @@ -0,0 +1,30 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + bool written = false; + float v; + for (mediump int j = 0; j < 10; j++) + { + for (mediump int i = 0; i < 4; i++) + { + float w = 0.0; + if (written) + { + w += v; + } + else + { + v = 20.0; + } + v += float(i); + written = true; + } + } + FragColor = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/loop-dominator-and-switch-default.frag b/third_party/spirv-cross/reference/shaders/frag/loop-dominator-and-switch-default.frag new file mode 100644 index 0000000..df968f1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/loop-dominator-and-switch-default.frag @@ -0,0 +1,50 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 fragColor; + +void main() +{ + vec4 f4; + mediump int c = int(f4.x); + for (mediump int j = 0; j < c; j++) + { + switch (c) + { + case 0: + { + f4.y = 0.0; + break; + } + case 1: + { + f4.y = 1.0; + break; + } + default: + { + mediump int i = 0; + for (;;) + { + mediump int _48 = i; + mediump int _50 = _48 + 1; + i = _50; + if (_48 < c) + { + f4.y += 0.5; + continue; + } + else + { + break; + } + } + continue; + } + } + f4.y += 0.5; + } + fragColor = f4; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/lut-promotion.frag b/third_party/spirv-cross/reference/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000..019393f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/lut-promotion.frag @@ -0,0 +1,40 @@ +#version 310 es +precision mediump float; +precision highp int; + +const float _16[16] = float[](1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0); +const vec4 _60[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + +layout(location = 0) out float FragColor; +layout(location = 0) flat in mediump int index; + +void main() +{ + FragColor = _16[index]; + if (index < 10) + { + FragColor += _16[index ^ 1]; + } + else + { + FragColor += _16[index & 1]; + } + if (index > 30) + { + FragColor += _60[index & 3].y; + } + else + { + FragColor += _60[index & 1].x; + } + vec4 foobar[4] = _60; + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + vec4 baz[4] = _60; + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/mix.frag b/third_party/spirv-cross/reference/shaders/frag/mix.frag new file mode 100644 index 0000000..2b288df --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/mix.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vIn0; +layout(location = 1) in vec4 vIn1; +layout(location = 2) in float vIn2; +layout(location = 3) in float vIn3; + +void main() +{ + bvec4 l = bvec4(false, true, false, false); + FragColor = mix(vIn0, vIn1, l); + bool f = true; + FragColor = vec4(f ? vIn3 : vIn2); + FragColor = mix(vIn1, vIn0, bvec4(f)); + FragColor = vec4(f ? vIn2 : vIn3); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/partial-write-preserve.frag b/third_party/spirv-cross/reference/shaders/frag/partial-write-preserve.frag new file mode 100644 index 0000000..87e689a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/partial-write-preserve.frag @@ -0,0 +1,109 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct B +{ + float a; + float b; +}; + +layout(binding = 0, std140) uniform UBO +{ + mediump int some_value; +} _51; + +void partial_inout(inout vec4 x) +{ + x.x = 10.0; +} + +void complete_inout(out vec4 x) +{ + x = vec4(50.0); +} + +void branchy_inout(inout vec4 v) +{ + v.y = 20.0; + if (_51.some_value == 20) + { + v = vec4(50.0); + } +} + +void branchy_inout_2(out vec4 v) +{ + if (_51.some_value == 20) + { + v = vec4(50.0); + } + else + { + v = vec4(70.0); + } + v.y = 20.0; +} + +void partial_inout(inout B b) +{ + b.b = 40.0; +} + +void complete_inout(out B b) +{ + b = B(100.0, 200.0); +} + +void branchy_inout(inout B b) +{ + b.b = 20.0; + if (_51.some_value == 20) + { + b = B(10.0, 40.0); + } +} + +void branchy_inout_2(out B b) +{ + if (_51.some_value == 20) + { + b = B(10.0, 40.0); + } + else + { + b = B(70.0, 70.0); + } + b.b = 20.0; +} + +void main() +{ + vec4 a = vec4(10.0); + vec4 param = a; + partial_inout(param); + a = param; + vec4 param_1; + complete_inout(param_1); + a = param_1; + vec4 param_2 = a; + branchy_inout(param_2); + a = param_2; + vec4 param_3; + branchy_inout_2(param_3); + a = param_3; + B b = B(10.0, 20.0); + B param_4 = b; + partial_inout(param_4); + b = param_4; + B param_5; + complete_inout(param_5); + b = param_5; + B param_6 = b; + branchy_inout(param_6); + b = param_6; + B param_7; + branchy_inout_2(param_7); + b = param_7; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/pixel-interlock-ordered.frag b/third_party/spirv-cross/reference/shaders/frag/pixel-interlock-ordered.frag new file mode 100644 index 0000000..46cca96 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/pixel-interlock-ordered.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(binding = 2, std430) coherent buffer Buffer +{ + int foo; + uint bar; +} _30; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0), vec4(1.0, 0.0, 0.0, 1.0)); + uint _27 = imageAtomicAdd(img2, ivec2(0), 1u); + _30.foo += 42; + uint _41 = atomicAnd(_30.bar, 255u); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/pixel-interlock-unordered.frag b/third_party/spirv-cross/reference/shaders/frag/pixel-interlock-unordered.frag new file mode 100644 index 0000000..d60cd14 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/pixel-interlock-unordered.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_unordered) in; + +layout(binding = 2, std430) coherent buffer Buffer +{ + int foo; + uint bar; +} _30; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0), vec4(1.0, 0.0, 0.0, 1.0)); + uint _27 = imageAtomicAdd(img2, ivec2(0), 1u); + _30.foo += 42; + uint _41 = atomicAnd(_30.bar, 255u); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/pls.frag b/third_party/spirv-cross/reference/shaders/frag/pls.frag new file mode 100644 index 0000000..1cafdbd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/pls.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 PLSOut0; +layout(location = 0) in vec4 PLSIn0; +layout(location = 1) out vec4 PLSOut1; +layout(location = 1) in vec4 PLSIn1; +layout(location = 2) out vec4 PLSOut2; +layout(location = 2) in vec4 PLSIn2; +layout(location = 3) out vec4 PLSOut3; +layout(location = 3) in vec4 PLSIn3; + +void main() +{ + PLSOut0 = PLSIn0 * 2.0; + PLSOut1 = PLSIn1 * 6.0; + PLSOut2 = PLSIn2 * 7.0; + PLSOut3 = PLSIn3 * 4.0; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/post-depth-coverage-es.frag b/third_party/spirv-cross/reference/shaders/frag/post-depth-coverage-es.frag new file mode 100644 index 0000000..d086560 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/post-depth-coverage-es.frag @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_post_depth_coverage : require +#extension GL_OES_sample_variables : require +precision mediump float; +precision highp int; +layout(early_fragment_tests, post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(gl_SampleMaskIn[0])); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/post-depth-coverage.frag b/third_party/spirv-cross/reference/shaders/frag/post-depth-coverage.frag new file mode 100644 index 0000000..caca9c0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/post-depth-coverage.frag @@ -0,0 +1,15 @@ +#version 450 +#if defined(GL_ARB_post_depth_coverge) +#extension GL_ARB_post_depth_coverage : require +#else +#extension GL_EXT_post_depth_coverage : require +#endif +layout(early_fragment_tests, post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(gl_SampleMaskIn[0])); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/round-even.frag b/third_party/spirv-cross/reference/shaders/frag/round-even.frag new file mode 100644 index 0000000..ab6f37a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/round-even.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in float vB; + +void main() +{ + FragColor = roundEven(vA); + FragColor *= roundEven(vB); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/round.frag b/third_party/spirv-cross/reference/shaders/frag/round.frag new file mode 100644 index 0000000..0f1fc0d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/round.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in float vB; + +void main() +{ + FragColor = round(vA); + FragColor *= round(vB); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/sample-interlock-ordered.frag b/third_party/spirv-cross/reference/shaders/frag/sample-interlock-ordered.frag new file mode 100644 index 0000000..67ca556 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/sample-interlock-ordered.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(sample_interlock_ordered) in; + +layout(binding = 2, std430) coherent buffer Buffer +{ + int foo; + uint bar; +} _30; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0), vec4(1.0, 0.0, 0.0, 1.0)); + uint _27 = imageAtomicAdd(img2, ivec2(0), 1u); + _30.foo += 42; + uint _47 = atomicAnd(_30.bar, uint(gl_SampleMaskIn[0])); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/sample-interlock-unordered.frag b/third_party/spirv-cross/reference/shaders/frag/sample-interlock-unordered.frag new file mode 100644 index 0000000..ea74397 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/sample-interlock-unordered.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(sample_interlock_unordered) in; + +layout(binding = 2, std430) coherent buffer Buffer +{ + int foo; + uint bar; +} _30; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0), vec4(1.0, 0.0, 0.0, 1.0)); + uint _27 = imageAtomicAdd(img2, ivec2(0), 1u); + _30.foo += 42; + uint _41 = atomicAnd(_30.bar, 255u); + endInvocationInterlockARB(); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/sample-parameter.frag b/third_party/spirv-cross/reference/shaders/frag/sample-parameter.frag new file mode 100644 index 0000000..3c130e6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/sample-parameter.frag @@ -0,0 +1,13 @@ +#version 310 es +#extension GL_OES_sample_variables : require +precision mediump float; +precision highp int; + +layout(location = 0) out vec2 FragColor; + +void main() +{ + FragColor = (gl_SamplePosition + vec2(float(gl_SampleMaskIn[0]))) + vec2(float(gl_SampleID)); + gl_SampleMask[0] = 1; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/sampler-ms.frag b/third_party/spirv-cross/reference/shaders/frag/sampler-ms.frag new file mode 100644 index 0000000..dbab3fb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/sampler-ms.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2DMS uSampler; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + ivec2 coord = ivec2(gl_FragCoord.xy); + FragColor = ((texelFetch(uSampler, coord, 0) + texelFetch(uSampler, coord, 1)) + texelFetch(uSampler, coord, 2)) + texelFetch(uSampler, coord, 3); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/sampler-proj.frag b/third_party/spirv-cross/reference/shaders/frag/sampler-proj.frag new file mode 100644 index 0000000..865dec6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/sampler-proj.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uTex; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vTex; + +void main() +{ + highp vec4 _19 = vTex; + _19.z = vTex.w; + FragColor = textureProj(uTex, _19.xyz); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/sampler.frag b/third_party/spirv-cross/reference/shaders/frag/sampler.frag new file mode 100644 index 0000000..d62f0af --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/sampler.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uTex; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; + +vec4 sample_texture(mediump sampler2D tex, vec2 uv) +{ + return texture(tex, uv); +} + +void main() +{ + vec2 param = vTex; + FragColor = vColor * sample_texture(uTex, param); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/reference/shaders/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..94d671b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/scalar-refract-reflect.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vRefract; + +void main() +{ + FragColor = refract(vRefract.x, vRefract.y, vRefract.z); + FragColor += reflect(vRefract.x, vRefract.y); + FragColor += refract(vRefract.xy, vRefract.yz, vRefract.z).y; + FragColor += reflect(vRefract.xy, vRefract.zy).y; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/selection-block-dominator.frag b/third_party/spirv-cross/reference/shaders/frag/selection-block-dominator.frag new file mode 100644 index 0000000..a0242a0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/selection-block-dominator.frag @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) flat in int vIndex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + int v; + if (vIndex != 1) + { + FragColor = vec4(1.0); + return; + } + else + { + v = 10; + } + FragColor = vec4(float(v)); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/struct-type-unrelated-alias.frag b/third_party/spirv-cross/reference/shaders/frag/struct-type-unrelated-alias.frag new file mode 100644 index 0000000..f38d18f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/struct-type-unrelated-alias.frag @@ -0,0 +1,18 @@ +#version 450 + +struct T +{ + float a; +}; + +layout(location = 0) out float FragColor; + +void main() +{ + T foo; + foo.a = 10.0; + T bar; + bar.a = 20.0; + FragColor = foo.a + bar.a; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/switch-unsigned-case.frag b/third_party/spirv-cross/reference/shaders/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..4177f9e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/switch-unsigned-case.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0, std140) uniform Buff +{ + mediump uint TestVal; +} _15; + +layout(location = 0) out vec4 fsout_Color; + +void main() +{ + fsout_Color = vec4(1.0); + switch (_15.TestVal) + { + case 0u: + { + fsout_Color = vec4(0.100000001490116119384765625); + break; + } + case 1u: + { + fsout_Color = vec4(0.20000000298023223876953125); + break; + } + } +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/swizzle.frag b/third_party/spirv-cross/reference/shaders/frag/swizzle.frag new file mode 100644 index 0000000..a229e5b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/swizzle.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D samp; + +layout(location = 0) out vec4 FragColor; +layout(location = 2) in vec2 vUV; +layout(location = 1) in vec3 vNormal; + +void main() +{ + FragColor = vec4(texture(samp, vUV).xyz, 1.0); + FragColor = vec4(texture(samp, vUV).xz, 1.0, 4.0); + FragColor = vec4(texture(samp, vUV).xx, texture(samp, vUV + vec2(0.100000001490116119384765625)).yy); + FragColor = vec4(vNormal, 1.0); + FragColor = vec4(vNormal + vec3(1.7999999523162841796875), 1.0); + FragColor = vec4(vUV, vUV + vec2(1.7999999523162841796875)); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/texel-fetch-offset.frag b/third_party/spirv-cross/reference/shaders/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..658468a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/texel-fetch-offset.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uTexture; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(1)); + FragColor += texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(-1, 1)); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/ubo-load-row-major-workaround.frag b/third_party/spirv-cross/reference/shaders/frag/ubo-load-row-major-workaround.frag new file mode 100644 index 0000000..95b0eda --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/ubo-load-row-major-workaround.frag @@ -0,0 +1,48 @@ +#version 450 + +struct RowMajor +{ + mat4 B; +}; + +struct NestedRowMajor +{ + RowMajor rm; +}; + +layout(binding = 2, std140) uniform UBO3 +{ + layout(row_major) NestedRowMajor rm2; +} _17; + +layout(binding = 1, std140) uniform UBO2 +{ + layout(row_major) RowMajor rm; +} _35; + +layout(binding = 0, std140) uniform UBO +{ + layout(row_major) mat4 A; + mat4 C; +} _42; + +layout(binding = 3, std140) uniform UBONoWorkaround +{ + mat4 D; +} _56; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 Clip; + +NestedRowMajor SPIRV_Cross_workaround_load_row_major(NestedRowMajor wrap) { return wrap; } +mat4 SPIRV_Cross_workaround_load_row_major(mat4 wrap) { return wrap; } + +void main() +{ + NestedRowMajor rm2_loaded; + rm2_loaded.rm.B = SPIRV_Cross_workaround_load_row_major(_17.rm2).rm.B; + FragColor = (((rm2_loaded.rm.B * SPIRV_Cross_workaround_load_row_major(_35.rm.B)) * SPIRV_Cross_workaround_load_row_major(_42.A)) * SPIRV_Cross_workaround_load_row_major(_42.C)) * Clip; + FragColor += (_56.D * Clip); + FragColor += (_42.A[1] * Clip); +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/ubo_layout.frag b/third_party/spirv-cross/reference/shaders/frag/ubo_layout.frag new file mode 100644 index 0000000..bc0b01c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/ubo_layout.frag @@ -0,0 +1,26 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct Str +{ + mat4 foo; +}; + +layout(binding = 0, std140) uniform UBO1 +{ + layout(row_major) Str foo; +} ubo1; + +layout(binding = 1, std140) uniform UBO2 +{ + Str foo; +} ubo0; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = ubo1.foo.foo[0] + ubo0.foo.foo[0]; +} + diff --git a/third_party/spirv-cross/reference/shaders/frag/unary-enclose.frag b/third_party/spirv-cross/reference/shaders/frag/unary-enclose.frag new file mode 100644 index 0000000..3006e86 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/frag/unary-enclose.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vIn; +layout(location = 1) flat in mediump ivec4 vIn1; + +void main() +{ + FragColor = -(-vIn); + mediump ivec4 a = ~(~vIn1); + bool b = false; + b = !(!b); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/basic.geom b/third_party/spirv-cross/reference/shaders/geom/basic.geom new file mode 100644 index 0000000..296ce57 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/basic.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(invocations = 4, triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[3]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal + vec3(float(gl_InvocationID)); + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal + vec3(4.0 * float(gl_InvocationID)); + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal + vec3(2.0 * float(gl_InvocationID)); + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/geometry-passthrough.geom b/third_party/spirv-cross/reference/shaders/geom/geometry-passthrough.geom new file mode 100644 index 0000000..d0d8806 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/geometry-passthrough.geom @@ -0,0 +1,27 @@ +#version 450 +#extension GL_NV_geometry_shader_passthrough : require +layout(triangles) in; + +layout(passthrough) in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +layout(passthrough, location = 0) in VertexBlock +{ + int a; + int b; +} v1[3]; + +layout(location = 2) in VertexBlock2 +{ + int a; + layout(passthrough) int b; +} v2[3]; + + +void main() +{ + gl_Layer = (gl_InvocationID + v1[0].a) + v2[1].b; +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/lines-adjacency.geom b/third_party/spirv-cross/reference/shaders/geom/lines-adjacency.geom new file mode 100644 index 0000000..46a21e9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/lines-adjacency.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(lines_adjacency) in; +layout(max_vertices = 3, line_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[4]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/lines.geom b/third_party/spirv-cross/reference/shaders/geom/lines.geom new file mode 100644 index 0000000..c5aaa53 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/lines.geom @@ -0,0 +1,23 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(lines) in; +layout(max_vertices = 2, line_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[2]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/multi-stream.geom b/third_party/spirv-cross/reference/shaders/geom/multi-stream.geom new file mode 100644 index 0000000..548164d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/multi-stream.geom @@ -0,0 +1,14 @@ +#version 450 +layout(triangles) in; +layout(max_vertices = 2, points) out; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + EmitStreamVertex(0); + EndStreamPrimitive(0); + gl_Position = gl_in[0].gl_Position + vec4(2.0); + EmitStreamVertex(1); + EndStreamPrimitive(1); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/points.geom b/third_party/spirv-cross/reference/shaders/geom/points.geom new file mode 100644 index 0000000..4d59137 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/points.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(points) in; +layout(max_vertices = 3, points) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[1]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/single-invocation.geom b/third_party/spirv-cross/reference/shaders/geom/single-invocation.geom new file mode 100644 index 0000000..fdccacc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/single-invocation.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[3]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/transform-feedback-streams.geom b/third_party/spirv-cross/reference/shaders/geom/transform-feedback-streams.geom new file mode 100644 index 0000000..4d238b4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/transform-feedback-streams.geom @@ -0,0 +1,26 @@ +#version 450 +layout(points) in; +layout(max_vertices = 2, points) out; + +layout(xfb_buffer = 1, xfb_stride = 20, stream = 1) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(location = 0, xfb_buffer = 2, xfb_stride = 32, xfb_offset = 16, stream = 1) out vec4 vFoo; +layout(xfb_buffer = 3, xfb_stride = 16, stream = 2) out VertOut +{ + layout(location = 1, xfb_offset = 0) vec4 vBar; +} _23; + + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + EmitStreamVertex(1); + _23.vBar = vec4(5.0); + EmitStreamVertex(2); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/triangles-adjacency.geom b/third_party/spirv-cross/reference/shaders/geom/triangles-adjacency.geom new file mode 100644 index 0000000..e9e6857 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/triangles-adjacency.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(triangles_adjacency) in; +layout(max_vertices = 3, triangle_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[6]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/shaders/geom/triangles.geom b/third_party/spirv-cross/reference/shaders/geom/triangles.geom new file mode 100644 index 0000000..fdccacc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/geom/triangles.geom @@ -0,0 +1,26 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require +layout(triangles) in; +layout(max_vertices = 3, triangle_strip) out; + +layout(location = 0) out vec3 vNormal; +layout(location = 0) in VertexData +{ + vec3 normal; +} vin[3]; + + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + EndPrimitive(); +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/fragment/explicit-lod.legacy.frag b/third_party/spirv-cross/reference/shaders/legacy/fragment/explicit-lod.legacy.frag new file mode 100644 index 0000000..6e8dbf1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/fragment/explicit-lod.legacy.frag @@ -0,0 +1,12 @@ +#version 100 +#extension GL_EXT_shader_texture_lod : require +precision mediump float; +precision highp int; + +uniform mediump sampler2D tex; + +void main() +{ + gl_FragData[0] = texture2DLodEXT(tex, vec2(0.4000000059604644775390625, 0.60000002384185791015625), 0.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/fragment/explicit-lod.legacy.vert b/third_party/spirv-cross/reference/shaders/legacy/fragment/explicit-lod.legacy.vert new file mode 100644 index 0000000..b73faa4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/fragment/explicit-lod.legacy.vert @@ -0,0 +1,11 @@ +#version 100 + +uniform mediump sampler2D tex; + +varying mediump vec4 FragColor; + +void main() +{ + FragColor = texture2DLod(tex, vec2(0.4000000059604644775390625, 0.60000002384185791015625), 3.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/fragment/fma.legacy.frag b/third_party/spirv-cross/reference/shaders/legacy/fragment/fma.legacy.frag new file mode 100644 index 0000000..bcb2d4c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/fragment/fma.legacy.frag @@ -0,0 +1,13 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp vec4 vA; +varying highp vec4 vB; +varying highp vec4 vC; + +void main() +{ + gl_FragData[0] = vA * vB + vC; +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/fragment/io-blocks.legacy.frag b/third_party/spirv-cross/reference/shaders/legacy/fragment/io-blocks.legacy.frag new file mode 100644 index 0000000..d5a60d5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/fragment/io-blocks.legacy.frag @@ -0,0 +1,12 @@ +#version 100 +precision mediump float; +precision highp int; + +varying vec4 vin_color; +varying highp vec3 vin_normal; + +void main() +{ + gl_FragData[0] = vin_color + vin_normal.xyzz; +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag b/third_party/spirv-cross/reference/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag new file mode 100644 index 0000000..1c811d3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag @@ -0,0 +1,39 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Foo +{ + highp vec4 a; + highp vec4 b; +}; + +struct Bar +{ + highp vec4 a; + highp vec4 b; +}; + +struct Baz +{ + Foo foo; + Bar bar; +}; + +varying highp vec4 baz_foo_a; +varying highp vec4 baz_foo_b; +varying highp vec4 baz_bar_a; +varying highp vec4 baz_bar_b; +varying highp vec4 _33_a_a; +varying highp vec4 _33_a_b; +varying highp vec4 _33_b_a; +varying highp vec4 _33_b_b; + +void main() +{ + Baz bazzy = Baz(Foo(baz_foo_a, baz_foo_b), Bar(baz_bar_a, baz_bar_b)); + Foo bazzy_foo = Foo(baz_foo_a, baz_foo_b); + Bar bazzy_bar = Bar(baz_bar_a, baz_bar_b); + gl_FragData[0] = (((_33_a_a + _33_b_b) + bazzy.foo.b) + bazzy_foo.a) + bazzy_bar.b; +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/fragment/round.legacy.frag b/third_party/spirv-cross/reference/shaders/legacy/fragment/round.legacy.frag new file mode 100644 index 0000000..9033bc3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/fragment/round.legacy.frag @@ -0,0 +1,13 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp vec4 vA; +varying highp float vB; + +void main() +{ + gl_FragData[0] = floor(vA + vec4(0.5)); + gl_FragData[0] *= floor(vB + float(0.5)); +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/fragment/struct-varying.legacy.frag b/third_party/spirv-cross/reference/shaders/legacy/fragment/struct-varying.legacy.frag new file mode 100644 index 0000000..81b95dd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/fragment/struct-varying.legacy.frag @@ -0,0 +1,22 @@ +#version 100 +precision mediump float; +precision highp int; + +struct Inputs +{ + highp vec4 a; + highp vec2 b; +}; + +varying highp vec4 vin_a; +varying highp vec2 vin_b; + +void main() +{ + Inputs v0 = Inputs(vin_a, vin_b); + Inputs v1 = Inputs(vin_a, vin_b); + highp vec4 a = vin_a; + highp vec4 b = vin_b.xxyy; + gl_FragData[0] = ((((v0.a + v0.b.xxyy) + v1.a) + v1.b.yyxx) + a) + b; +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/fragment/switch.legacy.frag b/third_party/spirv-cross/reference/shaders/legacy/fragment/switch.legacy.frag new file mode 100644 index 0000000..a70168e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/fragment/switch.legacy.frag @@ -0,0 +1,78 @@ +#version 100 +precision mediump float; +precision highp int; + +varying highp float vIndexF; + +void main() +{ + int vIndex = int(vIndexF); + highp vec4 v = vec4(0.0); + for (int SPIRV_Cross_Dummy21 = 0; SPIRV_Cross_Dummy21 < 1; SPIRV_Cross_Dummy21++) + { + if (vIndex == 2) + { + v = vec4(0.0, 2.0, 3.0, 4.0); + break; + } + else if ((vIndex == 4) || (vIndex == 5)) + { + v = vec4(1.0, 2.0, 3.0, 4.0); + break; + } + else if ((vIndex == 8) || (vIndex == 9)) + { + v = vec4(40.0, 20.0, 30.0, 40.0); + break; + } + else if (vIndex == 10) + { + v = vec4(10.0); + highp vec4 _43 = v; + highp vec4 _44 = vec4(1.0); + highp vec4 _45 = _43 + _44; + v = _45; + highp vec4 _46 = v; + highp vec4 _47 = vec4(2.0); + highp vec4 _48 = _46 + _47; + v = _48; + break; + } + else if (vIndex == 11) + { + highp vec4 _43 = v; + highp vec4 _44 = vec4(1.0); + highp vec4 _45 = _43 + _44; + v = _45; + highp vec4 _46 = v; + highp vec4 _47 = vec4(2.0); + highp vec4 _48 = _46 + _47; + v = _48; + break; + } + else if (vIndex == 12) + { + highp vec4 _46 = v; + highp vec4 _47 = vec4(2.0); + highp vec4 _48 = _46 + _47; + v = _48; + break; + } + else + { + v = vec4(10.0, 20.0, 30.0, 40.0); + break; + } + } + highp vec4 w = vec4(20.0); + for (int SPIRV_Cross_Dummy165 = 0; SPIRV_Cross_Dummy165 < 1; SPIRV_Cross_Dummy165++) + { + if ((vIndex == 10) || (vIndex == 20)) + { + w = vec4(40.0); + break; + } + } + gl_FragData[0] = v + w; +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/vert/implicit-lod.legacy.vert b/third_party/spirv-cross/reference/shaders/legacy/vert/implicit-lod.legacy.vert new file mode 100644 index 0000000..2d20504 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/vert/implicit-lod.legacy.vert @@ -0,0 +1,9 @@ +#version 100 + +uniform mediump sampler2D tex; + +void main() +{ + gl_Position = texture2DLod(tex, vec2(0.4000000059604644775390625, 0.60000002384185791015625), 0.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/vert/io-block.legacy.vert b/third_party/spirv-cross/reference/shaders/legacy/vert/io-block.legacy.vert new file mode 100644 index 0000000..3c518dc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/vert/io-block.legacy.vert @@ -0,0 +1,13 @@ +#version 100 + +attribute vec4 Position; +varying vec4 vout_color; +varying vec3 vout_normal; + +void main() +{ + gl_Position = Position; + vout_color = vec4(1.0); + vout_normal = vec3(0.5); +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert b/third_party/spirv-cross/reference/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert new file mode 100644 index 0000000..fa1d643 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert @@ -0,0 +1,18 @@ +#version 100 + +struct Foo +{ + float a[4]; +}; + +varying float foo_a[4]; + +void main() +{ + gl_Position = vec4(1.0); + for (int i = 0; i < 4; i++) + { + foo_a[i] = float(i + 2); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert b/third_party/spirv-cross/reference/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert new file mode 100644 index 0000000..cf807c4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert @@ -0,0 +1,49 @@ +#version 100 + +struct Foo +{ + vec4 a; + vec4 b; +}; + +struct Bar +{ + vec4 a; + vec4 b; +}; + +struct Baz +{ + Foo foo; + Bar bar; +}; + +varying vec4 _12_a_a; +varying vec4 _12_a_b; +varying vec4 _12_b_a; +varying vec4 _12_b_b; +varying vec4 baz_foo_a; +varying vec4 baz_foo_b; +varying vec4 baz_bar_a; +varying vec4 baz_bar_b; + +void main() +{ + _12_a_a = vec4(10.0); + _12_a_b = vec4(20.0); + _12_b_a = vec4(30.0); + _12_b_b = vec4(40.0); + _12_a_a = Foo(vec4(50.0), vec4(60.0)).a; + _12_a_b = Foo(vec4(50.0), vec4(60.0)).b; + _12_b_a = Bar(vec4(50.0), vec4(60.0)).a; + _12_b_b = Bar(vec4(50.0), vec4(60.0)).b; + baz_foo_a = Foo(vec4(100.0), vec4(200.0)).a; + baz_foo_b = Foo(vec4(100.0), vec4(200.0)).b; + baz_bar_a = Bar(vec4(300.0), vec4(400.0)).a; + baz_bar_b = Bar(vec4(300.0), vec4(400.0)).b; + baz_foo_a = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))).foo.a; + baz_foo_b = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))).foo.b; + baz_bar_a = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))).bar.a; + baz_bar_b = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))).bar.b; +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/vert/struct-varying.legacy.vert b/third_party/spirv-cross/reference/shaders/legacy/vert/struct-varying.legacy.vert new file mode 100644 index 0000000..fcdeb55 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/vert/struct-varying.legacy.vert @@ -0,0 +1,26 @@ +#version 100 + +struct Output +{ + vec4 a; + vec2 b; +}; + +varying vec4 vout_a; +varying vec2 vout_b; + +void main() +{ + Output s = Output(vec4(0.5), vec2(0.25)); + vout_a = s.a; + vout_b = s.b; + vout_a = s.a; + vout_b = s.b; + Output tmp = Output(vout_a, vout_b); + vout_a = tmp.a; + vout_b = tmp.b; + vout_a.x = 1.0; + vout_b.y = 1.0; + float c = vout_a.x; +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/vert/switch-nested.legacy.vert b/third_party/spirv-cross/reference/shaders/legacy/vert/switch-nested.legacy.vert new file mode 100644 index 0000000..3ec027b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/vert/switch-nested.legacy.vert @@ -0,0 +1,41 @@ +#version 100 + +struct UBO +{ + int func_arg; + int inner_func_arg; +}; + +uniform UBO _34; + +vec4 test_inner_func(bool b) +{ + if (b) + { + return vec4(1.0); + } + else + { + return vec4(0.0); + } +} + +vec4 test_func(bool b) +{ + if (b) + { + bool param = _34.inner_func_arg != 0; + return test_inner_func(param); + } + else + { + return vec4(0.0); + } +} + +void main() +{ + bool param = _34.func_arg != 0; + gl_Position = test_func(param); +} + diff --git a/third_party/spirv-cross/reference/shaders/legacy/vert/transpose.legacy.vert b/third_party/spirv-cross/reference/shaders/legacy/vert/transpose.legacy.vert new file mode 100644 index 0000000..30700e0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/legacy/vert/transpose.legacy.vert @@ -0,0 +1,37 @@ +#version 100 + +struct Buffer +{ + mat4 MVPRowMajor; + mat4 MVPColMajor; + mat4 M; +}; + +uniform Buffer _13; + +attribute vec4 Position; + +mat4 SPIRV_Cross_workaround_load_row_major(mat4 wrap) { return wrap; } + +mat4 SPIRV_Cross_Transpose(mat4 m) +{ + return mat4(m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], m[1][2], m[2][2], m[3][2], m[0][3], m[1][3], m[2][3], m[3][3]); +} + +void main() +{ + vec4 c0 = SPIRV_Cross_workaround_load_row_major(_13.M) * (Position * _13.MVPRowMajor); + vec4 c1 = SPIRV_Cross_workaround_load_row_major(_13.M) * (SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor) * Position); + vec4 c2 = SPIRV_Cross_workaround_load_row_major(_13.M) * (_13.MVPRowMajor * Position); + vec4 c3 = SPIRV_Cross_workaround_load_row_major(_13.M) * (Position * SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor)); + vec4 c4 = _13.MVPRowMajor * Position; + vec4 c5 = Position * SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor); + vec4 c6 = Position * _13.MVPRowMajor; + vec4 c7 = SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor) * Position; + vec4 c8 = (SPIRV_Cross_Transpose(_13.MVPRowMajor) * 2.0) * Position; + vec4 c9 = (SPIRV_Cross_Transpose(SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor)) * 2.0) * Position; + vec4 c10 = Position * (SPIRV_Cross_Transpose(_13.MVPRowMajor) * 2.0); + vec4 c11 = Position * (SPIRV_Cross_Transpose(SPIRV_Cross_workaround_load_row_major(_13.MVPColMajor)) * 2.0); + gl_Position = ((((((((((c0 + c1) + c2) + c3) + c4) + c5) + c6) + c7) + c8) + c9) + c10) + c11; +} + diff --git a/third_party/spirv-cross/reference/shaders/tesc/basic.tesc b/third_party/spirv-cross/reference/shaders/tesc/basic.tesc new file mode 100644 index 0000000..6019151 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tesc/basic.tesc @@ -0,0 +1,17 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(vertices = 1) out; + +layout(location = 0) patch out vec3 vFoo; + +void main() +{ + gl_TessLevelInner[0] = 8.8999996185302734375; + gl_TessLevelInner[1] = 6.900000095367431640625; + gl_TessLevelOuter[0] = 8.8999996185302734375; + gl_TessLevelOuter[1] = 6.900000095367431640625; + gl_TessLevelOuter[2] = 3.900000095367431640625; + gl_TessLevelOuter[3] = 4.900000095367431640625; + vFoo = vec3(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/tesc/water_tess.tesc b/third_party/spirv-cross/reference/shaders/tesc/water_tess.tesc new file mode 100644 index 0000000..6f49938 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tesc/water_tess.tesc @@ -0,0 +1,116 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(vertices = 1) out; + +layout(binding = 0, std140) uniform UBO +{ + vec4 uScale; + vec3 uCamPos; + vec2 uPatchSize; + vec2 uMaxTessLevel; + float uDistanceMod; + vec4 uFrustum[6]; +} _41; + +layout(location = 1) patch out vec2 vOutPatchPosBase; +layout(location = 2) patch out vec4 vPatchLods; +layout(location = 0) in vec2 vPatchPosBase[]; + +bool frustum_cull(vec2 p0) +{ + vec2 min_xz = (p0 - vec2(10.0)) * _41.uScale.xy; + vec2 max_xz = ((p0 + _41.uPatchSize) + vec2(10.0)) * _41.uScale.xy; + vec3 bb_min = vec3(min_xz.x, -10.0, min_xz.y); + vec3 bb_max = vec3(max_xz.x, 10.0, max_xz.y); + vec3 center = (bb_min + bb_max) * 0.5; + float radius = 0.5 * length(bb_max - bb_min); + vec3 f0 = vec3(dot(_41.uFrustum[0], vec4(center, 1.0)), dot(_41.uFrustum[1], vec4(center, 1.0)), dot(_41.uFrustum[2], vec4(center, 1.0))); + vec3 f1 = vec3(dot(_41.uFrustum[3], vec4(center, 1.0)), dot(_41.uFrustum[4], vec4(center, 1.0)), dot(_41.uFrustum[5], vec4(center, 1.0))); + bool _205 = any(lessThanEqual(f0, vec3(-radius))); + bool _215; + if (!_205) + { + _215 = any(lessThanEqual(f1, vec3(-radius))); + } + else + { + _215 = _205; + } + return !_215; +} + +float lod_factor(vec2 pos_) +{ + vec2 pos = pos_ * _41.uScale.xy; + vec3 dist_to_cam = _41.uCamPos - vec3(pos.x, 0.0, pos.y); + float level = log2((length(dist_to_cam) + 9.9999997473787516355514526367188e-05) * _41.uDistanceMod); + return clamp(level, 0.0, _41.uMaxTessLevel.x); +} + +vec4 tess_level(vec4 lod) +{ + return exp2(-lod) * _41.uMaxTessLevel.y; +} + +float tess_level(float lod) +{ + return _41.uMaxTessLevel.y * exp2(-lod); +} + +void compute_tess_levels(vec2 p0) +{ + vOutPatchPosBase = p0; + vec2 param = p0 + (vec2(-0.5) * _41.uPatchSize); + float l00 = lod_factor(param); + vec2 param_1 = p0 + (vec2(0.5, -0.5) * _41.uPatchSize); + float l10 = lod_factor(param_1); + vec2 param_2 = p0 + (vec2(1.5, -0.5) * _41.uPatchSize); + float l20 = lod_factor(param_2); + vec2 param_3 = p0 + (vec2(-0.5, 0.5) * _41.uPatchSize); + float l01 = lod_factor(param_3); + vec2 param_4 = p0 + (vec2(0.5) * _41.uPatchSize); + float l11 = lod_factor(param_4); + vec2 param_5 = p0 + (vec2(1.5, 0.5) * _41.uPatchSize); + float l21 = lod_factor(param_5); + vec2 param_6 = p0 + (vec2(-0.5, 1.5) * _41.uPatchSize); + float l02 = lod_factor(param_6); + vec2 param_7 = p0 + (vec2(0.5, 1.5) * _41.uPatchSize); + float l12 = lod_factor(param_7); + vec2 param_8 = p0 + (vec2(1.5) * _41.uPatchSize); + float l22 = lod_factor(param_8); + vec4 lods = vec4(dot(vec4(l01, l11, l02, l12), vec4(0.25)), dot(vec4(l00, l10, l01, l11), vec4(0.25)), dot(vec4(l10, l20, l11, l21), vec4(0.25)), dot(vec4(l11, l21, l12, l22), vec4(0.25))); + vPatchLods = lods; + vec4 outer_lods = min(lods, lods.yzwx); + vec4 param_9 = outer_lods; + vec4 levels = tess_level(param_9); + gl_TessLevelOuter[0] = levels.x; + gl_TessLevelOuter[1] = levels.y; + gl_TessLevelOuter[2] = levels.z; + gl_TessLevelOuter[3] = levels.w; + float min_lod = min(min(lods.x, lods.y), min(lods.z, lods.w)); + float param_10 = min(min_lod, l11); + float inner = tess_level(param_10); + gl_TessLevelInner[0] = inner; + gl_TessLevelInner[1] = inner; +} + +void main() +{ + vec2 p0 = vPatchPosBase[0]; + vec2 param = p0; + if (!frustum_cull(param)) + { + gl_TessLevelOuter[0] = -1.0; + gl_TessLevelOuter[1] = -1.0; + gl_TessLevelOuter[2] = -1.0; + gl_TessLevelOuter[3] = -1.0; + gl_TessLevelInner[0] = -1.0; + gl_TessLevelInner[1] = -1.0; + } + else + { + vec2 param_1 = p0; + compute_tess_levels(param_1); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/ccw.tese b/third_party/spirv-cross/reference/shaders/tese/ccw.tese new file mode 100644 index 0000000..a2a4508 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/ccw.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, ccw, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/cw.tese b/third_party/spirv-cross/reference/shaders/tese/cw.tese new file mode 100644 index 0000000..9578149 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/cw.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/equal.tese b/third_party/spirv-cross/reference/shaders/tese/equal.tese new file mode 100644 index 0000000..6d30518 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/equal.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, equal_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/fractional_even.tese b/third_party/spirv-cross/reference/shaders/tese/fractional_even.tese new file mode 100644 index 0000000..9578149 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/fractional_even.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/fractional_odd.tese b/third_party/spirv-cross/reference/shaders/tese/fractional_odd.tese new file mode 100644 index 0000000..608c19a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/fractional_odd.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, fractional_odd_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/input-array.tese b/third_party/spirv-cross/reference/shaders/tese/input-array.tese new file mode 100644 index 0000000..8a1aaf9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/input-array.tese @@ -0,0 +1,11 @@ +#version 450 +layout(quads, ccw, fractional_odd_spacing) in; + +layout(location = 0) in vec4 Floats[]; +layout(location = 2) in vec4 Floats2[]; + +void main() +{ + gl_Position = (Floats[0] * gl_TessCoord.x) + (Floats2[1] * gl_TessCoord.y); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/line.tese b/third_party/spirv-cross/reference/shaders/tese/line.tese new file mode 100644 index 0000000..8b6ad8d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/line.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(isolines, point_mode, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/load-array-of-array.tese b/third_party/spirv-cross/reference/shaders/tese/load-array-of-array.tese new file mode 100644 index 0000000..7fab08e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/load-array-of-array.tese @@ -0,0 +1,16 @@ +#version 450 +layout(quads, ccw, equal_spacing) in; + +layout(location = 0) in vec4 vTexCoord[][1]; + +void main() +{ + vec4 _17_unrolled[32][1]; + for (int i = 0; i < int(32); i++) + { + _17_unrolled[i] = vTexCoord[i]; + } + vec4 tmp[32][1] = _17_unrolled; + gl_Position = (tmp[0][0] + tmp[2][0]) + tmp[3][0]; +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/patch-input-array.tese b/third_party/spirv-cross/reference/shaders/tese/patch-input-array.tese new file mode 100644 index 0000000..413d8b3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/patch-input-array.tese @@ -0,0 +1,10 @@ +#version 450 +layout(quads, ccw, equal_spacing) in; + +layout(location = 0) patch in float P[4]; + +void main() +{ + gl_Position = vec4(P[0], P[1], P[2], P[3]); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/triangle.tese b/third_party/spirv-cross/reference/shaders/tese/triangle.tese new file mode 100644 index 0000000..9578149 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/triangle.tese @@ -0,0 +1,9 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(triangles, cw, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/tese/water_tess.tese b/third_party/spirv-cross/reference/shaders/tese/water_tess.tese new file mode 100644 index 0000000..a0cf42d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/tese/water_tess.tese @@ -0,0 +1,61 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +layout(quads, cw, fractional_even_spacing) in; + +layout(binding = 1, std140) uniform UBO +{ + mat4 uMVP; + vec4 uScale; + vec2 uInvScale; + vec3 uCamPos; + vec2 uPatchSize; + vec2 uInvHeightmapSize; +} _31; + +layout(binding = 0) uniform mediump sampler2D uHeightmapDisplacement; + +layout(location = 0) patch in vec2 vOutPatchPosBase; +layout(location = 1) patch in vec4 vPatchLods; +layout(location = 1) out vec4 vGradNormalTex; +layout(location = 0) out vec3 vWorld; + +vec2 lerp_vertex(vec2 tess_coord) +{ + return vOutPatchPosBase + (tess_coord * _31.uPatchSize); +} + +mediump vec2 lod_factor(vec2 tess_coord) +{ + mediump vec2 x = mix(vPatchLods.yx, vPatchLods.zw, vec2(tess_coord.x)); + mediump float level = mix(x.x, x.y, tess_coord.y); + mediump float floor_level = floor(level); + mediump float fract_level = level - floor_level; + return vec2(floor_level, fract_level); +} + +mediump vec3 sample_height_displacement(vec2 uv, vec2 off, mediump vec2 lod) +{ + return mix(textureLod(uHeightmapDisplacement, uv + (off * 0.5), lod.x).xyz, textureLod(uHeightmapDisplacement, uv + (off * 1.0), lod.x + 1.0).xyz, vec3(lod.y)); +} + +void main() +{ + vec2 tess_coord = gl_TessCoord.xy; + vec2 param = tess_coord; + vec2 pos = lerp_vertex(param); + vec2 param_1 = tess_coord; + mediump vec2 lod = lod_factor(param_1); + vec2 tex = pos * _31.uInvHeightmapSize; + pos *= _31.uScale.xy; + mediump float delta_mod = exp2(lod.x); + vec2 off = _31.uInvHeightmapSize * delta_mod; + vGradNormalTex = vec4(tex + (_31.uInvHeightmapSize * 0.5), tex * _31.uScale.zw); + vec2 param_2 = tex; + vec2 param_3 = off; + mediump vec2 param_4 = lod; + vec3 height_displacement = sample_height_displacement(param_2, param_3, param_4); + pos += height_displacement.yz; + vWorld = vec3(pos.x, height_displacement.x, pos.y); + gl_Position = _31.uMVP * vec4(vWorld, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/basic.vert b/third_party/spirv-cross/reference/shaders/vert/basic.vert new file mode 100644 index 0000000..8f251cb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/basic.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(binding = 0, std140) uniform UBO +{ + mat4 uMVP; +} _16; + +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec3 vNormal; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = _16.uMVP * aVertex; + vNormal = aNormal; +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/ground.vert b/third_party/spirv-cross/reference/shaders/vert/ground.vert new file mode 100644 index 0000000..73923c4 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/ground.vert @@ -0,0 +1,116 @@ +#version 310 es +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +struct PatchData +{ + vec4 Position; + vec4 LODs; +}; + +layout(binding = 0, std140) uniform PerPatch +{ + PatchData Patches[256]; +} _53; + +layout(binding = 2, std140) uniform GlobalGround +{ + vec4 GroundScale; + vec4 GroundPosition; + vec4 InvGroundSize_PatchScale; +} _156; + +layout(binding = 0, std140) uniform GlobalVSData +{ + vec4 g_ViewProj_Row0; + vec4 g_ViewProj_Row1; + vec4 g_ViewProj_Row2; + vec4 g_ViewProj_Row3; + vec4 g_CamPos; + vec4 g_CamRight; + vec4 g_CamUp; + vec4 g_CamFront; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_TimeParams; + vec4 g_ResolutionParams; + vec4 g_CamAxisRight; + vec4 g_FogColor_Distance; + vec4 g_ShadowVP_Row0; + vec4 g_ShadowVP_Row1; + vec4 g_ShadowVP_Row2; + vec4 g_ShadowVP_Row3; +} _236; + +layout(binding = 1) uniform mediump sampler2D TexLOD; +layout(binding = 0) uniform mediump sampler2D TexHeightmap; + +layout(location = 1) in vec4 LODWeights; +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +layout(location = 0) in vec2 Position; +layout(location = 1) out vec3 EyeVec; +layout(location = 0) out vec2 TexCoord; + +vec2 warp_position() +{ + float vlod = dot(LODWeights, _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].LODs); + vlod = all(equal(LODWeights, vec4(0.0))) ? _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].Position.w : vlod; + float floor_lod = floor(vlod); + float fract_lod = vlod - floor_lod; + uint ufloor_lod = uint(floor_lod); + uvec2 uPosition = uvec2(Position); + uvec2 mask = (uvec2(1u) << uvec2(ufloor_lod, ufloor_lod + 1u)) - uvec2(1u); + uint _110; + if (uPosition.x < 32u) + { + _110 = mask.x; + } + else + { + _110 = 0u; + } + uint _120; + if (uPosition.y < 32u) + { + _120 = mask.y; + } + else + { + _120 = 0u; + } + uvec2 rounding = uvec2(_110, _120); + vec4 lower_upper_snapped = vec4((uPosition + rounding).xyxy & (~mask).xxyy); + return mix(lower_upper_snapped.xy, lower_upper_snapped.zw, vec2(fract_lod)); +} + +vec2 lod_factor(vec2 uv) +{ + float level = textureLod(TexLOD, uv, 0.0).x * 7.96875; + float floor_level = floor(level); + float fract_level = level - floor_level; + return vec2(floor_level, fract_level); +} + +void main() +{ + vec2 PatchPos = _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].Position.xz * _156.InvGroundSize_PatchScale.zw; + vec2 WarpedPos = warp_position(); + vec2 VertexPos = PatchPos + WarpedPos; + vec2 NormalizedPos = VertexPos * _156.InvGroundSize_PatchScale.xy; + vec2 param = NormalizedPos; + vec2 lod = lod_factor(param); + vec2 Offset = _156.InvGroundSize_PatchScale.xy * exp2(lod.x); + float Elevation = mix(textureLod(TexHeightmap, NormalizedPos + (Offset * 0.5), lod.x).x, textureLod(TexHeightmap, NormalizedPos + (Offset * 1.0), lod.x + 1.0).x, lod.y); + vec3 WorldPos = vec3(NormalizedPos.x, Elevation, NormalizedPos.y); + WorldPos *= _156.GroundScale.xyz; + WorldPos += _156.GroundPosition.xyz; + EyeVec = WorldPos - _236.g_CamPos.xyz; + TexCoord = NormalizedPos + (_156.InvGroundSize_PatchScale.xy * 0.5); + gl_Position = (((_236.g_ViewProj_Row0 * WorldPos.x) + (_236.g_ViewProj_Row1 * WorldPos.y)) + (_236.g_ViewProj_Row2 * WorldPos.z)) + _236.g_ViewProj_Row3; +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/invariant.vert b/third_party/spirv-cross/reference/shaders/vert/invariant.vert new file mode 100644 index 0000000..648ea29 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/invariant.vert @@ -0,0 +1,19 @@ +#version 310 es + +invariant gl_Position; + +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; +layout(location = 2) in vec4 vInput2; +layout(location = 0) invariant out vec4 vColor; + +void main() +{ + vec4 _20 = vInput1 * vInput2; + vec4 _21 = vInput0 + _20; + gl_Position = _21; + vec4 _27 = vInput0 - vInput1; + vec4 _29 = _27 * vInput2; + vColor = _29; +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/ocean.vert b/third_party/spirv-cross/reference/shaders/vert/ocean.vert new file mode 100644 index 0000000..60fa80e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/ocean.vert @@ -0,0 +1,140 @@ +#version 310 es +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +struct PatchData +{ + vec4 Position; + vec4 LODs; +}; + +layout(binding = 0, std140) uniform Offsets +{ + PatchData Patches[256]; +} _53; + +layout(binding = 4, std140) uniform GlobalOcean +{ + vec4 OceanScale; + vec4 OceanPosition; + vec4 InvOceanSize_PatchScale; + vec4 NormalTexCoordScale; +} _180; + +layout(binding = 0, std140) uniform GlobalVSData +{ + vec4 g_ViewProj_Row0; + vec4 g_ViewProj_Row1; + vec4 g_ViewProj_Row2; + vec4 g_ViewProj_Row3; + vec4 g_CamPos; + vec4 g_CamRight; + vec4 g_CamUp; + vec4 g_CamFront; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_TimeParams; + vec4 g_ResolutionParams; + vec4 g_CamAxisRight; + vec4 g_FogColor_Distance; + vec4 g_ShadowVP_Row0; + vec4 g_ShadowVP_Row1; + vec4 g_ShadowVP_Row2; + vec4 g_ShadowVP_Row3; +} _273; + +layout(binding = 1) uniform mediump sampler2D TexLOD; +layout(binding = 0) uniform mediump sampler2D TexDisplacement; + +layout(location = 1) in vec4 LODWeights; +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif +layout(location = 0) in vec4 Position; +layout(location = 0) out vec3 EyeVec; +layout(location = 1) out vec4 TexCoord; + +vec2 warp_position() +{ + float vlod = dot(LODWeights, _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].LODs); + vlod = all(equal(LODWeights, vec4(0.0))) ? _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].Position.w : vlod; + float floor_lod = floor(vlod); + float fract_lod = vlod - floor_lod; + uint ufloor_lod = uint(floor_lod); + uvec4 uPosition = uvec4(Position); + uvec2 mask = (uvec2(1u) << uvec2(ufloor_lod, ufloor_lod + 1u)) - uvec2(1u); + uint _111; + if (uPosition.x < 32u) + { + _111 = mask.x; + } + else + { + _111 = 0u; + } + uvec4 rounding; + rounding.x = _111; + uint _122; + if (uPosition.y < 32u) + { + _122 = mask.x; + } + else + { + _122 = 0u; + } + rounding.y = _122; + uint _133; + if (uPosition.x < 32u) + { + _133 = mask.y; + } + else + { + _133 = 0u; + } + rounding.z = _133; + uint _145; + if (uPosition.y < 32u) + { + _145 = mask.y; + } + else + { + _145 = 0u; + } + rounding.w = _145; + vec4 lower_upper_snapped = vec4((uPosition.xyxy + rounding) & (~mask).xxyy); + return mix(lower_upper_snapped.xy, lower_upper_snapped.zw, vec2(fract_lod)); +} + +vec2 lod_factor(vec2 uv) +{ + float level = textureLod(TexLOD, uv, 0.0).x * 7.96875; + float floor_level = floor(level); + float fract_level = level - floor_level; + return vec2(floor_level, fract_level); +} + +void main() +{ + vec2 PatchPos = _53.Patches[(gl_InstanceID + SPIRV_Cross_BaseInstance)].Position.xz * _180.InvOceanSize_PatchScale.zw; + vec2 WarpedPos = warp_position(); + vec2 VertexPos = PatchPos + WarpedPos; + vec2 NormalizedPos = VertexPos * _180.InvOceanSize_PatchScale.xy; + vec2 NormalizedTex = NormalizedPos * _180.NormalTexCoordScale.zw; + vec2 param = NormalizedPos; + vec2 lod = lod_factor(param); + vec2 Offset = (_180.InvOceanSize_PatchScale.xy * exp2(lod.x)) * _180.NormalTexCoordScale.zw; + vec3 Displacement = mix(textureLod(TexDisplacement, NormalizedTex + (Offset * 0.5), lod.x).yxz, textureLod(TexDisplacement, NormalizedTex + (Offset * 1.0), lod.x + 1.0).yxz, vec3(lod.y)); + vec3 WorldPos = vec3(NormalizedPos.x, 0.0, NormalizedPos.y) + Displacement; + WorldPos *= _180.OceanScale.xyz; + WorldPos += _180.OceanPosition.xyz; + EyeVec = WorldPos - _273.g_CamPos.xyz; + TexCoord = vec4(NormalizedTex, NormalizedTex * _180.NormalTexCoordScale.xy) + ((_180.InvOceanSize_PatchScale.xyxy * 0.5) * _180.NormalTexCoordScale.zwzw); + gl_Position = (((_273.g_ViewProj_Row0 * WorldPos.x) + (_273.g_ViewProj_Row1 * WorldPos.y)) + (_273.g_ViewProj_Row2 * WorldPos.z)) + _273.g_ViewProj_Row3; +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/read-from-row-major-array.vert b/third_party/spirv-cross/reference/shaders/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..3b77687 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/read-from-row-major-array.vert @@ -0,0 +1,47 @@ +#version 310 es + +layout(binding = 0, std140) uniform Block +{ + layout(row_major) mat2x3 var[3][4]; +} _104; + +layout(location = 0) in vec4 a_position; +layout(location = 0) out mediump float v_vtxResult; + +mat2x3 SPIRV_Cross_workaround_load_row_major(mat2x3 wrap) { return wrap; } + +mediump float compare_float(float a, float b) +{ + return float(abs(a - b) < 0.0500000007450580596923828125); +} + +mediump float compare_vec3(vec3 a, vec3 b) +{ + float param = a.x; + float param_1 = b.x; + float param_2 = a.y; + float param_3 = b.y; + float param_4 = a.z; + float param_5 = b.z; + return (compare_float(param, param_1) * compare_float(param_2, param_3)) * compare_float(param_4, param_5); +} + +mediump float compare_mat2x3(mat2x3 a, mat2x3 b) +{ + vec3 param = a[0]; + vec3 param_1 = b[0]; + vec3 param_2 = a[1]; + vec3 param_3 = b[1]; + return compare_vec3(param, param_1) * compare_vec3(param_2, param_3); +} + +void main() +{ + gl_Position = a_position; + mediump float result = 1.0; + mat2x3 param = SPIRV_Cross_workaround_load_row_major(_104.var[0][0]); + mat2x3 param_1 = mat2x3(vec3(2.0, 6.0, -6.0), vec3(0.0, 5.0, 5.0)); + result *= compare_mat2x3(param, param_1); + v_vtxResult = result; +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/return-array.vert b/third_party/spirv-cross/reference/shaders/vert/return-array.vert new file mode 100644 index 0000000..20bb440 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/return-array.vert @@ -0,0 +1,23 @@ +#version 310 es + +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; + +vec4[2] test() +{ + return vec4[](vec4(10.0), vec4(20.0)); +} + +vec4[2] test2() +{ + vec4 foobar[2]; + foobar[0] = vInput0; + foobar[1] = vInput1; + return foobar; +} + +void main() +{ + gl_Position = test()[0] + test2()[1]; +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/texture_buffer.vert b/third_party/spirv-cross/reference/shaders/vert/texture_buffer.vert new file mode 100644 index 0000000..e9442ce --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/texture_buffer.vert @@ -0,0 +1,11 @@ +#version 310 es +#extension GL_OES_texture_buffer : require + +layout(binding = 4) uniform highp samplerBuffer uSamp; +layout(binding = 5, rgba32f) uniform readonly highp imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/transform-feedback-decorations.vert b/third_party/spirv-cross/reference/shaders/vert/transform-feedback-decorations.vert new file mode 100644 index 0000000..23e7cf3 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/transform-feedback-decorations.vert @@ -0,0 +1,22 @@ +#version 450 + +layout(xfb_buffer = 1, xfb_stride = 20) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(location = 0, xfb_buffer = 2, xfb_stride = 32, xfb_offset = 16) out vec4 vFoo; +layout(xfb_buffer = 3, xfb_stride = 16) out VertOut +{ + layout(location = 1, xfb_offset = 0) vec4 vBar; +} _22; + + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + _22.vBar = vec4(5.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/vert/ubo.vert b/third_party/spirv-cross/reference/shaders/vert/ubo.vert new file mode 100644 index 0000000..4e7236b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vert/ubo.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(binding = 0, std140) uniform UBO +{ + mat4 mvp; +} _16; + +layout(location = 0) in vec4 aVertex; +layout(location = 0) out vec3 vNormal; +layout(location = 1) in vec3 aNormal; + +void main() +{ + gl_Position = _16.mvp * aVertex; + vNormal = aNormal; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp.vk new file mode 100644 index 0000000..82ebb96 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp.vk @@ -0,0 +1,25 @@ +#version 450 +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(buffer_reference) buffer Block; +layout(buffer_reference, std430) buffer Block +{ + float v; +}; + +layout(set = 0, binding = 0, std140) uniform UBO +{ + Block blocks[4]; +} ubo; + +void main() +{ + Block blocks[4]; + blocks[0] = ubo.blocks[0]; + blocks[1] = ubo.blocks[1]; + blocks[2] = ubo.blocks[2]; + blocks[3] = ubo.blocks[3]; + blocks[gl_WorkGroupID.x].v = 20.0; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp.vk new file mode 100644 index 0000000..5752f81 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp.vk @@ -0,0 +1,26 @@ +#version 450 +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(buffer_reference) buffer PtrUint; +layout(buffer_reference) buffer PtrInt; +layout(buffer_reference, std430) buffer PtrUint +{ + uint value; +}; + +layout(buffer_reference, std430) buffer PtrInt +{ + int value; +}; + +layout(set = 0, binding = 0, std430) buffer Buf +{ + PtrUint ptr; +} _11; + +void main() +{ + PtrInt(_11.ptr).value = 10; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp.vk b/third_party/spirv-cross/reference/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp.vk new file mode 100644 index 0000000..e229741 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp.vk @@ -0,0 +1,56 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(buffer_reference) buffer Node; +layout(buffer_reference, std430) buffer Node +{ + layout(offset = 0) int value; + layout(offset = 16) Node next; + layout(offset = 32) Node prev; +}; + +layout(set = 0, binding = 0, std430) restrict buffer LinkedList +{ + Node head1; + Node head2; +} _50; + +void copy_node(restrict Node dst, restrict Node a, restrict Node b) +{ + dst.value = a.value + b.value; +} + +void overwrite_node(out restrict Node dst, restrict Node src) +{ + dst = src; +} + +void main() +{ + Node _45; + if (gl_WorkGroupID.x < 4u) + { + _45 = _50.head1; + } + else + { + _45 = _50.head2; + } + restrict Node n = _45; + Node param = n.next; + Node param_1 = _50.head1; + Node param_2 = _50.head2; + copy_node(param, param_1, param_2); + Node param_4 = _50.head1; + Node param_3; + overwrite_node(param_3, param_4); + n = param_3; + int v = _50.head2.value; + n.value = 20; + n.value = v * 10; + uint64_t uptr = uint64_t(_50.head2.next); + Node unode = Node(uptr); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp b/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp new file mode 100644 index 0000000..7c4c7ed --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp @@ -0,0 +1,47 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 100 +#endif +const int a = SPIRV_CROSS_CONSTANT_ID_0; +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 200 +#endif +const int b = SPIRV_CROSS_CONSTANT_ID_1; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +#ifndef SPIRV_CROSS_CONSTANT_ID_2 +#define SPIRV_CROSS_CONSTANT_ID_2 300 +#endif +const int c = SPIRV_CROSS_CONSTANT_ID_2; +const int d = (c + 50); +#ifndef SPIRV_CROSS_CONSTANT_ID_3 +#define SPIRV_CROSS_CONSTANT_ID_3 400 +#endif +const int e = SPIRV_CROSS_CONSTANT_ID_3; + +layout(binding = 0, std430) buffer SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +} _22; + +void main() +{ + _22.w[gl_GlobalInvocationID.x] += (_22.v[gl_GlobalInvocationID.x] + e); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp.vk b/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp.vk new file mode 100644 index 0000000..b7571b7 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp.vk @@ -0,0 +1,35 @@ +#version 450 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(constant_id = 0) const int a = 100; +layout(constant_id = 1) const int b = 200; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +layout(constant_id = 2) const int c = 300; +const int d = (c + 50); +layout(constant_id = 3) const int e = 400; + +layout(set = 1, binding = 0, std430) buffer SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +} _22; + +void main() +{ + _22.w[gl_GlobalInvocationID.x] += (_22.v[gl_GlobalInvocationID.x] + e); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp b/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp new file mode 100644 index 0000000..888f4b1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp @@ -0,0 +1,34 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_1 +#define SPIRV_CROSS_CONSTANT_ID_1 2 +#endif +const int b = SPIRV_CROSS_CONSTANT_ID_1; +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 1 +#endif +const int a = SPIRV_CROSS_CONSTANT_ID_0; +const uint _21 = (uint(a) + 0u); +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 1u +#endif +const uint _27 = gl_WorkGroupSize.x; +const uint _28 = (_21 + _27); +const uint _29 = gl_WorkGroupSize.y; +const uint _30 = (_28 + _29); +const int _32 = (1 - a); + +layout(local_size_x = SPIRV_CROSS_CONSTANT_ID_10, local_size_y = 20, local_size_z = 1) in; + +layout(binding = 0, std430) writeonly buffer SSBO +{ + int v[]; +} _17; + +void main() +{ + int spec_const_array_size[b]; + spec_const_array_size[a] = a; + _17.v[_30] = b + spec_const_array_size[_32]; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp.vk b/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp.vk new file mode 100644 index 0000000..bdf72df --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp.vk @@ -0,0 +1,24 @@ +#version 450 +layout(local_size_x_id = 10, local_size_y = 20, local_size_z = 1) in; + +layout(constant_id = 1) const int b = 2; +layout(constant_id = 0) const int a = 1; +const uint _21 = (uint(a) + 0u); +const uint _27 = gl_WorkGroupSize.x; +const uint _28 = (_21 + _27); +const uint _29 = gl_WorkGroupSize.y; +const uint _30 = (_28 + _29); +const int _32 = (1 - a); + +layout(set = 1, binding = 0, std430) writeonly buffer SSBO +{ + int v[]; +} _17; + +void main() +{ + int spec_const_array_size[b]; + spec_const_array_size[a] = a; + _17.v[_30] = b + spec_const_array_size[_32]; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag new file mode 100644 index 0000000..af64fb8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag @@ -0,0 +1,31 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump sampler2DShadow SPIRV_Cross_CombineduDepthuSampler; +uniform mediump sampler2D SPIRV_Cross_CombineduDepthuSampler1; + +layout(location = 0) out float FragColor; + +float samp2(mediump sampler2DShadow SPIRV_Cross_Combinedts) +{ + return texture(SPIRV_Cross_Combinedts, vec3(vec3(1.0).xy, vec3(1.0).z)); +} + +float samp3(mediump sampler2D SPIRV_Cross_Combinedts) +{ + return texture(SPIRV_Cross_Combinedts, vec2(1.0)).x; +} + +float samp(mediump sampler2DShadow SPIRV_Cross_Combinedts, mediump sampler2D SPIRV_Cross_Combinedts1) +{ + float r0 = samp2(SPIRV_Cross_Combinedts); + float r1 = samp3(SPIRV_Cross_Combinedts1); + return r0 + r1; +} + +void main() +{ + FragColor = samp(SPIRV_Cross_CombineduDepthuSampler, SPIRV_Cross_CombineduDepthuSampler1); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag.vk new file mode 100644 index 0000000..f475ae5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag.vk @@ -0,0 +1,32 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(set = 0, binding = 2) uniform mediump texture2D uDepth; +layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; + +layout(location = 0) out float FragColor; + +float samp2(mediump texture2D t, mediump samplerShadow s) +{ + return texture(sampler2DShadow(t, s), vec3(vec3(1.0).xy, vec3(1.0).z)); +} + +float samp3(mediump texture2D t, mediump sampler s) +{ + return texture(sampler2D(t, s), vec2(1.0)).x; +} + +float samp(mediump texture2D t, mediump samplerShadow s, mediump sampler s1) +{ + float r0 = samp2(t, s); + float r1 = samp3(t, s1); + return r0 + r1; +} + +void main() +{ + FragColor = samp(uDepth, uSampler, uSampler1); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler.vk.frag new file mode 100644 index 0000000..5b9c0dd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler.vk.frag @@ -0,0 +1,48 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump sampler2D SPIRV_Cross_CombineduTexture0uSampler0; +uniform mediump sampler2D SPIRV_Cross_CombineduTexture1uSampler1; +uniform mediump sampler2D SPIRV_Cross_CombineduTexture1uSampler0; +uniform mediump sampler2D SPIRV_Cross_CombineduTexture0uSampler1; + +layout(location = 0) in vec2 vTex; +layout(location = 0) out vec4 FragColor; + +vec4 sample_dual(mediump sampler2D SPIRV_Cross_Combinedtexsamp) +{ + return texture(SPIRV_Cross_Combinedtexsamp, vTex); +} + +vec4 sample_duals() +{ + vec4 a = sample_dual(SPIRV_Cross_CombineduTexture0uSampler0); + vec4 b = sample_dual(SPIRV_Cross_CombineduTexture1uSampler1); + return a + b; +} + +vec4 sample_global_tex(mediump sampler2D SPIRV_Cross_CombineduTexture0samp, mediump sampler2D SPIRV_Cross_CombineduTexture1samp) +{ + vec4 a = texture(SPIRV_Cross_CombineduTexture0samp, vTex); + vec4 b = sample_dual(SPIRV_Cross_CombineduTexture1samp); + return a + b; +} + +vec4 sample_global_sampler(mediump sampler2D SPIRV_Cross_CombinedtexuSampler0, mediump sampler2D SPIRV_Cross_CombinedtexuSampler1) +{ + vec4 a = texture(SPIRV_Cross_CombinedtexuSampler0, vTex); + vec4 b = sample_dual(SPIRV_Cross_CombinedtexuSampler1); + return a + b; +} + +void main() +{ + vec4 c0 = sample_duals(); + vec4 c1 = sample_global_tex(SPIRV_Cross_CombineduTexture0uSampler0, SPIRV_Cross_CombineduTexture1uSampler0); + vec4 c2 = sample_global_tex(SPIRV_Cross_CombineduTexture0uSampler1, SPIRV_Cross_CombineduTexture1uSampler1); + vec4 c3 = sample_global_sampler(SPIRV_Cross_CombineduTexture0uSampler0, SPIRV_Cross_CombineduTexture0uSampler1); + vec4 c4 = sample_global_sampler(SPIRV_Cross_CombineduTexture1uSampler0, SPIRV_Cross_CombineduTexture1uSampler1); + FragColor = (((c0 + c1) + c2) + c3) + c4; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler.vk.frag.vk new file mode 100644 index 0000000..ae8df4c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/combined-texture-sampler.vk.frag.vk @@ -0,0 +1,48 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(set = 0, binding = 2) uniform mediump texture2D uTexture0; +layout(set = 0, binding = 3) uniform mediump texture2D uTexture1; +layout(set = 0, binding = 0) uniform mediump sampler uSampler0; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; + +layout(location = 0) in vec2 vTex; +layout(location = 0) out vec4 FragColor; + +vec4 sample_dual(mediump sampler samp, mediump texture2D tex) +{ + return texture(sampler2D(tex, samp), vTex); +} + +vec4 sample_duals() +{ + vec4 a = sample_dual(uSampler0, uTexture0); + vec4 b = sample_dual(uSampler1, uTexture1); + return a + b; +} + +vec4 sample_global_tex(mediump sampler samp) +{ + vec4 a = texture(sampler2D(uTexture0, samp), vTex); + vec4 b = sample_dual(samp, uTexture1); + return a + b; +} + +vec4 sample_global_sampler(mediump texture2D tex) +{ + vec4 a = texture(sampler2D(tex, uSampler0), vTex); + vec4 b = sample_dual(uSampler1, tex); + return a + b; +} + +void main() +{ + vec4 c0 = sample_duals(); + vec4 c1 = sample_global_tex(uSampler0); + vec4 c2 = sample_global_tex(uSampler1); + vec4 c3 = sample_global_sampler(uTexture0); + vec4 c4 = sample_global_sampler(uTexture1); + FragColor = (((c0 + c1) + c2) + c3) + c4; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag.vk new file mode 100644 index 0000000..1531649 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag.vk @@ -0,0 +1,15 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +layout(location = 0) out vec4 FragColor; + +void main() +{ + bool _15 = helperInvocationEXT(); + demote; + if (!_15) + { + FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag.vk new file mode 100644 index 0000000..302f835 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag.vk @@ -0,0 +1,10 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +void main() +{ + demote; + bool _9 = helperInvocationEXT(); + bool helper = _9; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/desktop-mediump.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/desktop-mediump.vk.frag new file mode 100644 index 0000000..8f7508e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/desktop-mediump.vk.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 F; +layout(location = 1) flat in ivec4 I; +layout(location = 2) flat in uvec4 U; + +void main() +{ + FragColor = (F + vec4(I)) + vec4(U); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/desktop-mediump.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/desktop-mediump.vk.frag.vk new file mode 100644 index 0000000..4c0506b --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/desktop-mediump.vk.frag.vk @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out mediump vec4 FragColor; +layout(location = 0) in mediump vec4 F; +layout(location = 1) flat in mediump ivec4 I; +layout(location = 2) flat in mediump uvec4 U; + +void main() +{ + FragColor = (F + vec4(I)) + vec4(U); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag new file mode 100644 index 0000000..ea460c1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0) uniform sampler2DMS uSubpass0; +layout(binding = 1) uniform sampler2DMS uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = (texelFetch(uSubpass0, ivec2(gl_FragCoord.xy), 1) + texelFetch(uSubpass1, ivec2(gl_FragCoord.xy), 2)) + texelFetch(uSubpass0, ivec2(gl_FragCoord.xy), gl_SampleID); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk new file mode 100644 index 0000000..462df22 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment-ms.vk.frag.vk @@ -0,0 +1,12 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = (subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2)) + subpassLoad(uSubpass0, gl_SampleID); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment.vk.frag new file mode 100644 index 0000000..8d216b2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment.vk.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2D uSubpass0; +layout(binding = 1) uniform mediump sampler2D uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = texelFetch(uSubpass0, ivec2(gl_FragCoord.xy), 0) + texelFetch(uSubpass1, ivec2(gl_FragCoord.xy), 0); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment.vk.frag.vk new file mode 100644 index 0000000..c8b5d9a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/input-attachment.vk.frag.vk @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0) + subpassLoad(uSubpass1); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag.vk new file mode 100644 index 0000000..703c5fd --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag.vk @@ -0,0 +1,37 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require + +layout(set = 0, binding = 2, std140) uniform UBO +{ + vec4 v[64]; +} ubos[]; + +layout(set = 0, binding = 3, std430) readonly buffer SSBO +{ + vec4 v[]; +} ssbos[]; + +layout(set = 0, binding = 0) uniform texture2D uSamplers[]; +layout(set = 0, binding = 1) uniform sampler uSamps[]; +layout(set = 0, binding = 4) uniform sampler2D uCombinedSamplers[]; + +layout(location = 0) flat in int vIndex; +layout(location = 0) out vec4 FragColor; +layout(location = 1) in vec2 vUV; + +void main() +{ + int i = vIndex; + int _23 = i + 10; + int _34 = i + 40; + FragColor = texture(sampler2D(uSamplers[nonuniformEXT(_23)], uSamps[nonuniformEXT(_34)]), vUV); + int _50 = i + 10; + FragColor = texture(uCombinedSamplers[nonuniformEXT(_50)], vUV); + int _66 = i + 20; + int _70 = i + 40; + FragColor += ubos[nonuniformEXT(_66)].v[_70]; + int _84 = i + 50; + int _88 = i + 60; + FragColor += ssbos[nonuniformEXT(_84)].v[_88]; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag new file mode 100644 index 0000000..21618f8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag @@ -0,0 +1,14 @@ +#version 450 + +layout(std140) uniform UBO +{ + float ubo[4]; +} _14; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = _14.ubo[1]; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag.vk new file mode 100644 index 0000000..8ca4f23 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag.vk @@ -0,0 +1,14 @@ +#version 450 + +layout(push_constant, std140) uniform UBO +{ + float ubo[4]; +} _14; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = _14.ubo[1]; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant.vk.frag new file mode 100644 index 0000000..c04a7ca --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant.vk.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; +precision highp int; + +struct PushConstants +{ + vec4 value0; + vec4 value1; +}; + +uniform PushConstants push; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = (vColor + push.value0) + push.value1; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant.vk.frag.vk new file mode 100644 index 0000000..6cec90f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/push-constant.vk.frag.vk @@ -0,0 +1,18 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = (vColor + push.value0) + push.value1; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag new file mode 100644 index 0000000..575c418 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag @@ -0,0 +1,22 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSamp; +uniform sampler2D SPIRV_Cross_CombineduTuS; + +layout(location = 0) out vec4 FragColor; + +vec4 samp(sampler2D uSamp_1) +{ + return texture(uSamp_1, vec2(0.5)); +} + +vec4 samp_1(sampler2D SPIRV_Cross_CombinedTS) +{ + return texture(SPIRV_Cross_CombinedTS, vec2(0.5)); +} + +void main() +{ + FragColor = samp(uSamp) + samp_1(SPIRV_Cross_CombineduTuS); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag.vk new file mode 100644 index 0000000..222b659 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag.vk @@ -0,0 +1,23 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2D uSamp; +layout(set = 0, binding = 1) uniform texture2D uT; +layout(set = 0, binding = 2) uniform sampler uS; + +layout(location = 0) out vec4 FragColor; + +vec4 samp(sampler2D uSamp_1) +{ + return texture(uSamp_1, vec2(0.5)); +} + +vec4 samp(texture2D T, sampler S) +{ + return texture(sampler2D(T, S), vec2(0.5)); +} + +void main() +{ + FragColor = samp(uSamp) + samp(uT, uS); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag new file mode 100644 index 0000000..8ca3085 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag @@ -0,0 +1,44 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump sampler2D SPIRV_Cross_CombineduTextureuSampler[4]; +uniform mediump sampler2DArray SPIRV_Cross_CombineduTextureArrayuSampler[4]; +uniform mediump samplerCube SPIRV_Cross_CombineduTextureCubeuSampler[4]; +uniform mediump sampler3D SPIRV_Cross_CombineduTexture3DuSampler[4]; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +vec4 sample_func(vec2 uv, mediump sampler2D SPIRV_Cross_CombineduTexturesamp[4]) +{ + return texture(SPIRV_Cross_CombineduTexturesamp[2], uv); +} + +vec4 sample_func_dual(vec2 uv, mediump sampler2D SPIRV_Cross_Combinedtexsamp) +{ + return texture(SPIRV_Cross_Combinedtexsamp, uv); +} + +vec4 sample_func_dual_array(vec2 uv, mediump sampler2D SPIRV_Cross_Combinedtexsamp[4]) +{ + return texture(SPIRV_Cross_Combinedtexsamp[1], uv); +} + +void main() +{ + vec2 off = vec2(1.0) / vec2(textureSize(SPIRV_Cross_CombineduTextureuSampler[1], 0)); + vec2 off2 = vec2(1.0) / vec2(textureSize(SPIRV_Cross_CombineduTextureuSampler[2], 1)); + vec2 param = (vTex + off) + off2; + vec4 c0 = sample_func(param, SPIRV_Cross_CombineduTextureuSampler); + vec2 param_1 = (vTex + off) + off2; + vec4 c1 = sample_func_dual(param_1, SPIRV_Cross_CombineduTextureuSampler[1]); + vec2 param_2 = (vTex + off) + off2; + vec4 c2 = sample_func_dual_array(param_2, SPIRV_Cross_CombineduTextureuSampler); + vec4 c3 = texture(SPIRV_Cross_CombineduTextureArrayuSampler[3], vTex3); + vec4 c4 = texture(SPIRV_Cross_CombineduTextureCubeuSampler[1], vTex3); + vec4 c5 = texture(SPIRV_Cross_CombineduTexture3DuSampler[2], vTex3); + FragColor = ((((c0 + c1) + c2) + c3) + c4) + c5; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag.vk new file mode 100644 index 0000000..0afa489 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag.vk @@ -0,0 +1,45 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(set = 0, binding = 1) uniform mediump texture2D uTexture[4]; +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray[4]; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube[4]; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D[4]; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture[2], samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +vec4 sample_func_dual_array(mediump sampler samp, mediump texture2D tex[4], vec2 uv) +{ + return texture(sampler2D(tex[1], samp), uv); +} + +void main() +{ + vec2 off = vec2(1.0) / vec2(textureSize(sampler2D(uTexture[1], uSampler), 0)); + vec2 off2 = vec2(1.0) / vec2(textureSize(sampler2D(uTexture[2], uSampler), 1)); + vec2 param = (vTex + off) + off2; + vec4 c0 = sample_func(uSampler, param); + vec2 param_1 = (vTex + off) + off2; + vec4 c1 = sample_func_dual(uSampler, uTexture[1], param_1); + vec2 param_2 = (vTex + off) + off2; + vec4 c2 = sample_func_dual_array(uSampler, uTexture, param_2); + vec4 c3 = texture(sampler2DArray(uTextureArray[3], uSampler), vTex3); + vec4 c4 = texture(samplerCube(uTextureCube[1], uSampler), vTex3); + vec4 c5 = texture(sampler3D(uTexture3D[2], uSampler), vTex3); + FragColor = ((((c0 + c1) + c2) + c3) + c4) + c5; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture.vk.frag new file mode 100644 index 0000000..c2530d5 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture.vk.frag @@ -0,0 +1,37 @@ +#version 310 es +precision mediump float; +precision highp int; + +uniform mediump sampler2D SPIRV_Cross_CombineduTextureuSampler; +uniform mediump sampler2DArray SPIRV_Cross_CombineduTextureArrayuSampler; +uniform mediump samplerCube SPIRV_Cross_CombineduTextureCubeuSampler; +uniform mediump sampler3D SPIRV_Cross_CombineduTexture3DuSampler; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +vec4 sample_func(vec2 uv, mediump sampler2D SPIRV_Cross_CombineduTexturesamp) +{ + return texture(SPIRV_Cross_CombineduTexturesamp, uv); +} + +vec4 sample_func_dual(vec2 uv, mediump sampler2D SPIRV_Cross_Combinedtexsamp) +{ + return texture(SPIRV_Cross_Combinedtexsamp, uv); +} + +void main() +{ + vec2 off = vec2(1.0) / vec2(textureSize(SPIRV_Cross_CombineduTextureuSampler, 0)); + vec2 off2 = vec2(1.0) / vec2(textureSize(SPIRV_Cross_CombineduTextureuSampler, 1)); + vec2 param = (vTex + off) + off2; + vec4 c0 = sample_func(param, SPIRV_Cross_CombineduTextureuSampler); + vec2 param_1 = (vTex + off) + off2; + vec4 c1 = sample_func_dual(param_1, SPIRV_Cross_CombineduTextureuSampler); + vec4 c2 = texture(SPIRV_Cross_CombineduTextureArrayuSampler, vTex3); + vec4 c3 = texture(SPIRV_Cross_CombineduTextureCubeuSampler, vTex3); + vec4 c4 = texture(SPIRV_Cross_CombineduTexture3DuSampler, vTex3); + FragColor = (((c0 + c1) + c2) + c3) + c4; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture.vk.frag.vk new file mode 100644 index 0000000..105379d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/separate-sampler-texture.vk.frag.vk @@ -0,0 +1,38 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(set = 0, binding = 1) uniform mediump texture2D uTexture; +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D; + +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; +layout(location = 0) out vec4 FragColor; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture, samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +void main() +{ + vec2 off = vec2(1.0) / vec2(textureSize(sampler2D(uTexture, uSampler), 0)); + vec2 off2 = vec2(1.0) / vec2(textureSize(sampler2D(uTexture, uSampler), 1)); + vec2 param = (vTex + off) + off2; + vec4 c0 = sample_func(uSampler, param); + vec2 param_1 = (vTex + off) + off2; + vec4 c1 = sample_func_dual(uSampler, uTexture, param_1); + vec4 c2 = texture(sampler2DArray(uTextureArray, uSampler), vTex3); + vec4 c3 = texture(samplerCube(uTextureCube, uSampler), vTex3); + vec4 c4 = texture(sampler3D(uTexture3D, uSampler), vTex3); + FragColor = (((c0 + c1) + c2) + c3) + c4; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag.vk new file mode 100644 index 0000000..634a37a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag.vk @@ -0,0 +1,88 @@ +#version 450 +#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require +#extension GL_EXT_shader_16bit_storage : require +#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require +#extension GL_EXT_shader_8bit_storage : require + +layout(set = 0, binding = 1, std430) buffer SSBO +{ + int8_t i8[16]; + uint8_t u8[16]; +} ssbo; + +layout(set = 0, binding = 0, std140) uniform UBO +{ + int8_t i8; + uint8_t u8; +} ubo; + +layout(push_constant, std430) uniform Push +{ + int8_t i8; + uint8_t u8; +} registers; + +layout(location = 0) flat in ivec4 vColor; +layout(location = 0) out ivec4 FragColorInt; +layout(location = 1) out uvec4 FragColorUint; + +void packing_int8() +{ + int16_t i16 = 10s; + int i32 = 20; + i8vec2 i8_2 = unpack8(i16); + i8vec4 i8_4 = unpack8(i32); + i16 = pack16(i8_2); + i32 = pack32(i8_4); + ssbo.i8[0] = i8_4.x; + ssbo.i8[1] = i8_4.y; + ssbo.i8[2] = i8_4.z; + ssbo.i8[3] = i8_4.w; +} + +void packing_uint8() +{ + uint16_t u16 = 10us; + uint u32 = 20u; + u8vec2 u8_2 = unpack8(u16); + u8vec4 u8_4 = unpack8(u32); + u16 = pack16(u8_2); + u32 = pack32(u8_4); + ssbo.u8[0] = u8_4.x; + ssbo.u8[1] = u8_4.y; + ssbo.u8[2] = u8_4.z; + ssbo.u8[3] = u8_4.w; +} + +void compute_int8() +{ + i8vec4 tmp = i8vec4(vColor); + tmp += i8vec4(registers.i8); + tmp += i8vec4(int8_t(-40)); + tmp += i8vec4(-50); + tmp += i8vec4(int8_t(10), int8_t(20), int8_t(30), int8_t(40)); + tmp += i8vec4(ssbo.i8[4]); + tmp += i8vec4(ubo.i8); + FragColorInt = ivec4(tmp); +} + +void compute_uint8() +{ + u8vec4 tmp = u8vec4(i8vec4(vColor)); + tmp += u8vec4(registers.u8); + tmp += u8vec4(uint8_t(216)); + tmp += u8vec4(206); + tmp += u8vec4(uint8_t(10), uint8_t(20), uint8_t(30), uint8_t(40)); + tmp += u8vec4(ssbo.u8[4]); + tmp += u8vec4(ubo.u8); + FragColorUint = uvec4(tmp); +} + +void main() +{ + packing_int8(); + packing_uint8(); + compute_int8(); + compute_uint8(); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-block-size.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-block-size.vk.frag new file mode 100644 index 0000000..19ea6ae --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-block-size.vk.frag @@ -0,0 +1,22 @@ +#version 310 es +precision mediump float; +precision highp int; + +#ifndef SPIRV_CROSS_CONSTANT_ID_10 +#define SPIRV_CROSS_CONSTANT_ID_10 2 +#endif +const int Value = SPIRV_CROSS_CONSTANT_ID_10; + +layout(binding = 0, std140) uniform SpecConstArray +{ + vec4 samples[Value]; +} _15; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int Index; + +void main() +{ + FragColor = _15.samples[Index]; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-block-size.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-block-size.vk.frag.vk new file mode 100644 index 0000000..133761a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-block-size.vk.frag.vk @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(constant_id = 10) const int Value = 2; + +layout(set = 0, binding = 0, std140) uniform SpecConstArray +{ + vec4 samples[Value]; +} _15; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in mediump int Index; + +void main() +{ + FragColor = _15.samples[Index]; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000..081206f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,16 @@ +#version 450 + +#ifndef SPIRV_CROSS_CONSTANT_ID_0 +#define SPIRV_CROSS_CONSTANT_ID_0 10u +#endif +const uint s = SPIRV_CROSS_CONSTANT_ID_0; +const bool _13 = (s > 20u); +const uint f = _13 ? 30u : 50u; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float(f); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk b/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk new file mode 100644 index 0000000..34bfea0 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/frag/spec-constant-ternary.vk.frag.vk @@ -0,0 +1,13 @@ +#version 450 + +layout(constant_id = 0) const uint s = 10u; +const bool _13 = (s > 20u); +const uint f = _13 ? 30u : 50u; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = float(f); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit.vk new file mode 100644 index 0000000..614a04d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit.vk @@ -0,0 +1,24 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Foo +{ + float a; + float b; +}; + +struct Foo2 +{ + float a; + float b; +}; + +layout(location = 0) rayPayloadInNV Foo payload; +hitAttributeNV Foo2 hit; + +void main() +{ + payload.a = hit.a; + payload.b = hit.b; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit.vk new file mode 100644 index 0000000..e747bb2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit.vk @@ -0,0 +1,29 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Foo +{ + float a; + float b; +}; + +struct Foo2 +{ + float a; + float b; +}; + +layout(location = 0) rayPayloadInNV Foo payload; +hitAttributeNV Foo2 hit; + +void in_function() +{ + payload.a = hit.a; + payload.b = hit.b; +} + +void main() +{ + in_function(); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit.vk new file mode 100644 index 0000000..908d963 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit.vk @@ -0,0 +1,11 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec2 payload; +hitAttributeNV vec2 hit; + +void main() +{ + payload = hit; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit.vk new file mode 100644 index 0000000..133bdfc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit.vk @@ -0,0 +1,17 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Foo +{ + float a; + float b; +}; + +layout(location = 0) rayPayloadInNV Foo payload; +hitAttributeNV Foo hit; + +void main() +{ + payload = hit; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit.vk new file mode 100644 index 0000000..64f79a8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_HitKindNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit.vk new file mode 100644 index 0000000..9004a00 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_HitTNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit.vk new file mode 100644 index 0000000..d17ab8c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_IncomingRayFlagsNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit.vk new file mode 100644 index 0000000..531a1fc --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = uint(gl_InstanceCustomIndexNV); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit.vk new file mode 100644 index 0000000..ff551db --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = uint(gl_InstanceID); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit.vk new file mode 100644 index 0000000..01afa0e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectRayDirectionNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit.vk new file mode 100644 index 0000000..a49e17a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectRayOriginNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit.vk new file mode 100644 index 0000000..fc2c5ed --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectToWorldNV * vec4(payload, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/payloads.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/payloads.nocompat.vk.rchit.vk new file mode 100644 index 0000000..a3ddd56 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/payloads.nocompat.vk.rchit.vk @@ -0,0 +1,20 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Payload +{ + vec4 a; +}; + +layout(location = 0) rayPayloadInNV Payload payload; + +void write_incoming_payload_in_function() +{ + payload.a = vec4(10.0); +} + +void main() +{ + write_incoming_payload_in_function(); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit.vk new file mode 100644 index 0000000..d3b0ef1 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = uint(gl_PrimitiveID); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit.vk new file mode 100644 index 0000000..769c96a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_RayTmaxNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit.vk new file mode 100644 index 0000000..2709899 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_RayTminNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit.vk new file mode 100644 index 0000000..103fd66 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = 1.0 + float(gl_InstanceID); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit.vk new file mode 100644 index 0000000..4acf03e --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldRayDirectionNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit.vk new file mode 100644 index 0000000..70241f2 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldRayOriginNV; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit.vk b/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit.vk new file mode 100644 index 0000000..0b93e38 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldToObjectNV * vec4(payload, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen.vk new file mode 100644 index 0000000..d8c5796 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen.vk @@ -0,0 +1,17 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform accelerationStructureNV as; +layout(set = 0, binding = 1, rgba32f) uniform writeonly image2D image; +layout(location = 0) rayPayloadNV vec4 payload; +layout(location = 0) callableDataNV float blend; + +void main() +{ + vec3 origin = vec3(0.0); + vec3 direction = vec3(0.0, 0.0, -1.0); + traceNV(as, 1u, 255u, 0u, 0u, 0u, origin, 0.0, direction, 100.0, 0); + executeCallableNV(0u, 0); + imageStore(image, ivec2(gl_LaunchIDNV.xy), payload + vec4(blend)); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen.vk new file mode 100644 index 0000000..f907e6f --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform writeonly image2D uImage; + +void main() +{ + imageStore(uImage, ivec2(gl_LaunchIDNV.xy), vec4(1.0)); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen.vk new file mode 100644 index 0000000..08992c6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform writeonly image2D uImage; + +void main() +{ + imageStore(uImage, ivec2(gl_LaunchSizeNV.xy) - ivec2(1), vec4(1.0)); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rgen/payloads.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/shaders/vulkan/rgen/payloads.nocompat.vk.rgen.vk new file mode 100644 index 0000000..5d6b24c --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rgen/payloads.nocompat.vk.rgen.vk @@ -0,0 +1,47 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Payload +{ + float a; + float b; +}; + +struct Block +{ + float a; + float b; + Payload c; + Payload d; +}; + +layout(set = 0, binding = 1) uniform accelerationStructureNV as; +layout(location = 1) rayPayloadNV Payload payload2; +layout(location = 0) rayPayloadNV float payload1; +layout(location = 2) rayPayloadNV Block _71; +layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image; + +vec4 trace_in_function() +{ + vec4 result = vec4(0.0); + vec3 origin = vec3(1.0, 0.0, 0.0); + vec3 direction = vec3(0.0, 1.0, 0.0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 1); + result += vec4(payload2.a); + result += vec4(payload2.b); + return result; +} + +void main() +{ + vec3 origin = vec3(1.0, 0.0, 0.0); + vec3 direction = vec3(0.0, 1.0, 0.0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 0); + vec4 result = vec4(payload1); + vec4 _62 = trace_in_function(); + result += _62; + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 2); + result += vec4(((((_71.a + _71.b) + _71.c.a) + _71.c.b) + _71.d.a) + _71.d.b); + imageStore(image, ivec2(gl_LaunchIDNV.xy), result); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen.vk new file mode 100644 index 0000000..55e301a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen.vk @@ -0,0 +1,21 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 1) uniform accelerationStructureNV as; +layout(location = 0) rayPayloadNV float payload; + +float pure_call(vec2 launchID, vec2 launchSize) +{ + vec3 origin = vec3(launchID.x / launchSize.x, launchID.y / launchSize.y, 1.0); + vec3 direction = vec3(0.0, 0.0, -1.0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 0); + return 0.0; +} + +void main() +{ + vec2 param = vec2(gl_LaunchIDNV.xy); + vec2 param_1 = vec2(gl_LaunchSizeNV.xy); + float _62 = pure_call(param, param_1); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen.vk new file mode 100644 index 0000000..9c9e484 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen.vk @@ -0,0 +1,17 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 1) uniform accelerationStructureNV as; +layout(location = 0) rayPayloadNV float payload; +layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image; + +void main() +{ + vec4 col = vec4(0.0, 0.0, 0.0, 1.0); + vec3 origin = vec3(float(gl_LaunchIDNV.x) / float(gl_LaunchSizeNV.x), float(gl_LaunchIDNV.y) / float(gl_LaunchSizeNV.y), 1.0); + vec3 direction = vec3(0.0, 0.0, -1.0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 0); + col.y = payload; + imageStore(image, ivec2(gl_LaunchIDNV.xy), col); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen.vk b/third_party/spirv-cross/reference/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen.vk new file mode 100644 index 0000000..058ccf9 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen.vk @@ -0,0 +1,17 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(shaderRecordNV, std430) buffer sbt +{ + vec3 direction; + float tmax; +} _20; + +layout(set = 0, binding = 0) uniform accelerationStructureNV as; +layout(location = 0) rayPayloadNV float payload; + +void main() +{ + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(0.0), 0.0, _20.direction, _20.tmax, 0); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss.vk b/third_party/spirv-cross/reference/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss.vk new file mode 100644 index 0000000..ec3072a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss.vk @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = 0.0; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/vert/device-group.nocompat.vk.vert.vk b/third_party/spirv-cross/reference/shaders/vulkan/vert/device-group.nocompat.vk.vert.vk new file mode 100644 index 0000000..9cadcdb --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/vert/device-group.nocompat.vk.vert.vk @@ -0,0 +1,8 @@ +#version 450 +#extension GL_EXT_device_group : require + +void main() +{ + gl_Position = vec4(float(gl_DeviceIndex)); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/vert/multiview.nocompat.vk.vert.vk b/third_party/spirv-cross/reference/shaders/vulkan/vert/multiview.nocompat.vk.vert.vk new file mode 100644 index 0000000..9005547 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/vert/multiview.nocompat.vk.vert.vk @@ -0,0 +1,15 @@ +#version 310 es +#extension GL_EXT_multiview : require + +layout(set = 0, binding = 0, std140) uniform MVPs +{ + mat4 MVP[2]; +} _19; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = _19.MVP[gl_ViewIndex] * Position; +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/vert/small-storage.vk.vert b/third_party/spirv-cross/reference/shaders/vulkan/vert/small-storage.vk.vert new file mode 100644 index 0000000..b3aafc8 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/vert/small-storage.vk.vert @@ -0,0 +1,59 @@ +#version 450 +#if defined(GL_AMD_gpu_shader_int16) +#extension GL_AMD_gpu_shader_int16 : require +#else +#error No extension available for Int16. +#endif +#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_NV_gpu_shader5) +#extension GL_NV_gpu_shader5 : require +#else +#error No extension available for FP16. +#endif + +layout(binding = 0, std140) uniform block +{ + i16vec2 a; + u16vec2 b; + i8vec2 c; + u8vec2 d; + f16vec2 e; +} _26; + +layout(binding = 1, std430) readonly buffer storage +{ + i16vec3 f; + u16vec3 g; + i8vec3 h; + u8vec3 i; + f16vec3 j; +} _53; + +struct pushconst +{ + i16vec4 k; + u16vec4 l; + i8vec4 m; + u8vec4 n; + f16vec4 o; +}; + +uniform pushconst _76; + +layout(location = 0) out i16vec4 p; +layout(location = 0, component = 0) in int16_t foo; +layout(location = 1) out u16vec4 q; +layout(location = 0, component = 1) in uint16_t bar; +layout(location = 2) out f16vec4 r; +layout(location = 1) in float16_t baz; + +void main() +{ + p = i16vec4((((ivec4(int(foo)) + ivec4(ivec2(_26.a), ivec2(_26.c))) - ivec4(ivec3(_53.f) / ivec3(_53.h), 1)) + ivec4(_76.k)) + ivec4(_76.m)); + q = u16vec4((((uvec4(uint(bar)) + uvec4(uvec2(_26.b), uvec2(_26.d))) - uvec4(uvec3(_53.g) / uvec3(_53.i), 1u)) + uvec4(_76.l)) + uvec4(_76.n)); + r = f16vec4(((vec4(float(baz)) + vec4(vec2(_26.e), 0.0, 1.0)) - vec4(vec3(_53.j), 1.0)) + vec4(_76.o)); + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/vert/small-storage.vk.vert.vk b/third_party/spirv-cross/reference/shaders/vulkan/vert/small-storage.vk.vert.vk new file mode 100644 index 0000000..caec60a --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/vert/small-storage.vk.vert.vk @@ -0,0 +1,55 @@ +#version 450 +#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require +#extension GL_EXT_shader_16bit_storage : require +#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require +#extension GL_EXT_shader_8bit_storage : require +#if defined(GL_AMD_gpu_shader_half_float) +#extension GL_AMD_gpu_shader_half_float : require +#elif defined(GL_EXT_shader_explicit_arithmetic_types_float16) +#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require +#else +#error No extension available for FP16. +#endif + +layout(set = 0, binding = 0, std140) uniform block +{ + i16vec2 a; + u16vec2 b; + i8vec2 c; + u8vec2 d; + f16vec2 e; +} _26; + +layout(set = 0, binding = 1, std430) readonly buffer storage +{ + i16vec3 f; + u16vec3 g; + i8vec3 h; + u8vec3 i; + f16vec3 j; +} _53; + +layout(push_constant, std430) uniform pushconst +{ + i16vec4 k; + u16vec4 l; + i8vec4 m; + u8vec4 n; + f16vec4 o; +} _76; + +layout(location = 0) out i16vec4 p; +layout(location = 0, component = 0) in int16_t foo; +layout(location = 1) out u16vec4 q; +layout(location = 0, component = 1) in uint16_t bar; +layout(location = 2) out f16vec4 r; +layout(location = 1) in float16_t baz; + +void main() +{ + p = i16vec4((((ivec4(int(foo)) + ivec4(ivec2(_26.a), ivec2(_26.c))) - ivec4(ivec3(_53.f) / ivec3(_53.h), 1)) + ivec4(_76.k)) + ivec4(_76.m)); + q = u16vec4((((uvec4(uint(bar)) + uvec4(uvec2(_26.b), uvec2(_26.d))) - uvec4(uvec3(_53.g) / uvec3(_53.i), 1u)) + uvec4(_76.l)) + uvec4(_76.n)); + r = f16vec4(((vec4(float(baz)) + vec4(vec2(_26.e), 0.0, 1.0)) - vec4(vec3(_53.j), 1.0)) + vec4(_76.o)); + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/vert/vulkan-vertex.vk.vert b/third_party/spirv-cross/reference/shaders/vulkan/vert/vulkan-vertex.vk.vert new file mode 100644 index 0000000..d939aa6 --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/vert/vulkan-vertex.vk.vert @@ -0,0 +1,16 @@ +#version 310 es +#ifdef GL_ARB_shader_draw_parameters +#extension GL_ARB_shader_draw_parameters : enable +#endif + +#ifdef GL_ARB_shader_draw_parameters +#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB +#else +uniform int SPIRV_Cross_BaseInstance; +#endif + +void main() +{ + gl_Position = vec4(1.0, 2.0, 3.0, 4.0) * float(gl_VertexID + (gl_InstanceID + SPIRV_Cross_BaseInstance)); +} + diff --git a/third_party/spirv-cross/reference/shaders/vulkan/vert/vulkan-vertex.vk.vert.vk b/third_party/spirv-cross/reference/shaders/vulkan/vert/vulkan-vertex.vk.vert.vk new file mode 100644 index 0000000..8c4930d --- /dev/null +++ b/third_party/spirv-cross/reference/shaders/vulkan/vert/vulkan-vertex.vk.vert.vk @@ -0,0 +1,7 @@ +#version 310 es + +void main() +{ + gl_Position = vec4(1.0, 2.0, 3.0, 4.0) * float(gl_VertexIndex + gl_InstanceIndex); +} + diff --git a/third_party/spirv-cross/samples/cpp/Makefile b/third_party/spirv-cross/samples/cpp/Makefile new file mode 100644 index 0000000..225bb3d --- /dev/null +++ b/third_party/spirv-cross/samples/cpp/Makefile @@ -0,0 +1,28 @@ +SOURCES := $(wildcard *.comp) +SPIRV := $(SOURCES:.comp=.spv) +CPP_INTERFACE := $(SOURCES:.comp=.spv.cpp) +CPP_DRIVER := $(SOURCES:.comp=.cpp) +EXECUTABLES := $(SOURCES:.comp=.shader) +OBJECTS := $(CPP_DRIVER:.cpp=.o) $(CPP_INTERFACE:.cpp=.o) + +CXXFLAGS += -std=c++11 -I../../include -I. +LDFLAGS += -pthread -lm + +all: $(EXECUTABLES) + +%.spv: %.comp + glslangValidator -V -o $@ $< + +%.spv.cpp: %.spv + ../../spirv-cross --cpp --output $@ $< + +%.o: %.cpp + $(CXX) -c -o $@ $< $(CXXFLAGS) + +%.shader: %.o %.spv.o + $(CXX) -o $@ $^ $(LDFLAGS) + +clean: + $(RM) -f $(EXECUTABLES) $(SPIRV) $(CPP_INTERFACE) $(OBJECTS) + +.PHONY: clean diff --git a/third_party/spirv-cross/samples/cpp/atomics.comp b/third_party/spirv-cross/samples/cpp/atomics.comp new file mode 100644 index 0000000..0bf6d2a --- /dev/null +++ b/third_party/spirv-cross/samples/cpp/atomics.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 64) in; + +layout(set = 0, binding = 0, std430) readonly buffer SSBO0 +{ + float inputs[]; +}; + +layout(set = 0, binding = 1, std430) writeonly buffer SSBO1 +{ + float outputs[]; +}; + +layout(set = 0, binding = 2, std430) buffer SSBO2 +{ + uint counter; +}; + +void main() +{ + // Builds a tightly packed list of all values less than 10.0. + // The output order is random. + float value = inputs[gl_GlobalInvocationID.x]; + if (value < 10.0) + { + uint output_index = atomicAdd(counter, 1u); + outputs[output_index] = value; + } +} diff --git a/third_party/spirv-cross/samples/cpp/atomics.cpp b/third_party/spirv-cross/samples/cpp/atomics.cpp new file mode 100644 index 0000000..89351a5 --- /dev/null +++ b/third_party/spirv-cross/samples/cpp/atomics.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cross/external_interface.h" +#include + +#ifndef GLM_SWIZZLE +#define GLM_SWIZZLE +#endif + +#ifndef GLM_FORCE_RADIANS +#define GLM_FORCE_RADIANS +#endif + +#include +using namespace glm; + +int main() +{ + // First, we get the C interface to the shader. + // This can be loaded from a dynamic library, or as here, + // linked in as a static library. + auto *iface = spirv_cross_get_interface(); + + // Create an instance of the shader interface. + auto *shader = iface->construct(); + +// Build some input data for our compute shader. +#define NUM_WORKGROUPS 4 + float a[64 * NUM_WORKGROUPS]; + float b[64 * NUM_WORKGROUPS] = {}; + uint32_t counter = 0; + + for (int i = 0; i < 64 * NUM_WORKGROUPS; i++) + { + a[i] = i * 0.46f; + } + + void *aptr = a; + void *bptr = b; + void *cptr = &counter; + + // Bind resources to the shader. + // For resources like samplers and buffers, we provide a list of pointers, + // since UBOs, SSBOs and samplers can be arrays, and can point to different types, + // which is especially true for samplers. + spirv_cross_set_resource(shader, 0, 0, &aptr, sizeof(aptr)); + spirv_cross_set_resource(shader, 0, 1, &bptr, sizeof(bptr)); + spirv_cross_set_resource(shader, 0, 2, &cptr, sizeof(cptr)); + + // We also have to set builtins. + // The relevant builtins will depend on the shader, + // but for compute, there are few builtins, which are gl_NumWorkGroups and gl_WorkGroupID. + // LocalInvocationID and GlobalInvocationID are inferred when executing the invocation. + uvec3 num_workgroups(NUM_WORKGROUPS, 1, 1); + uvec3 work_group_id(0, 0, 0); + spirv_cross_set_builtin(shader, SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS, &num_workgroups, sizeof(num_workgroups)); + spirv_cross_set_builtin(shader, SPIRV_CROSS_BUILTIN_WORK_GROUP_ID, &work_group_id, sizeof(work_group_id)); + + // Execute 4 work groups. + for (unsigned i = 0; i < NUM_WORKGROUPS; i++) + { + work_group_id.x = i; + iface->invoke(shader); + } + + // Call destructor. + iface->destruct(shader); + + // Verify our output. + // TODO: Implement a test framework that asserts results computed. + fprintf(stderr, "Counter = %u\n", counter); + for (unsigned i = 0; i < counter; i++) + { + fprintf(stderr, "[%3u] = %.1f\n", i, b[i]); + } +} diff --git a/third_party/spirv-cross/samples/cpp/multiply.comp b/third_party/spirv-cross/samples/cpp/multiply.comp new file mode 100644 index 0000000..1ac7869 --- /dev/null +++ b/third_party/spirv-cross/samples/cpp/multiply.comp @@ -0,0 +1,22 @@ +#version 310 es +layout(local_size_x = 64) in; + +layout(set = 0, binding = 0, std430) readonly buffer SSBO0 +{ + vec4 a[]; +}; + +layout(set = 0, binding = 1, std430) readonly buffer SSBO1 +{ + vec4 b[]; +}; + +layout(set = 0, binding = 2, std430) buffer SSBO2 +{ + vec4 c[]; +}; + +void main() +{ + c[gl_GlobalInvocationID.x] = a[gl_GlobalInvocationID.x] * b[gl_GlobalInvocationID.x]; +} diff --git a/third_party/spirv-cross/samples/cpp/multiply.cpp b/third_party/spirv-cross/samples/cpp/multiply.cpp new file mode 100644 index 0000000..daa1fc6 --- /dev/null +++ b/third_party/spirv-cross/samples/cpp/multiply.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cross/external_interface.h" +#include + +#ifndef GLM_SWIZZLE +#define GLM_SWIZZLE +#endif + +#ifndef GLM_FORCE_RADIANS +#define GLM_FORCE_RADIANS +#endif + +#include +using namespace glm; + +int main() +{ + // First, we get the C interface to the shader. + // This can be loaded from a dynamic library, or as here, + // linked in as a static library. + auto *iface = spirv_cross_get_interface(); + + // Create an instance of the shader interface. + auto *shader = iface->construct(); + +// Build some input data for our compute shader. +#define NUM_WORKGROUPS 4 + vec4 a[64 * NUM_WORKGROUPS]; + vec4 b[64 * NUM_WORKGROUPS]; + vec4 c[64 * NUM_WORKGROUPS] = {}; + + for (int i = 0; i < 64 * NUM_WORKGROUPS; i++) + { + a[i] = vec4(100 + i, 101 + i, 102 + i, 103 + i); + b[i] = vec4(100 - i, 99 - i, 98 - i, 97 - i); + } + + void *aptr = a; + void *bptr = b; + void *cptr = c; + + // Bind resources to the shader. + // For resources like samplers and buffers, we provide a list of pointers, + // since UBOs, SSBOs and samplers can be arrays, and can point to different types, + // which is especially true for samplers. + spirv_cross_set_resource(shader, 0, 0, &aptr, sizeof(aptr)); + spirv_cross_set_resource(shader, 0, 1, &bptr, sizeof(bptr)); + spirv_cross_set_resource(shader, 0, 2, &cptr, sizeof(cptr)); + + // We also have to set builtins. + // The relevant builtins will depend on the shader, + // but for compute, there are few builtins, which are gl_NumWorkGroups and gl_WorkGroupID. + // LocalInvocationID and GlobalInvocationID are inferred when executing the invocation. + uvec3 num_workgroups(NUM_WORKGROUPS, 1, 1); + uvec3 work_group_id(0, 0, 0); + spirv_cross_set_builtin(shader, SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS, &num_workgroups, sizeof(num_workgroups)); + spirv_cross_set_builtin(shader, SPIRV_CROSS_BUILTIN_WORK_GROUP_ID, &work_group_id, sizeof(work_group_id)); + + // Execute 4 work groups. + for (unsigned i = 0; i < NUM_WORKGROUPS; i++) + { + work_group_id.x = i; + iface->invoke(shader); + } + + // Call destructor. + iface->destruct(shader); + + // Verify our output. + // TODO: Implement a test framework that asserts results computed. + for (unsigned i = 0; i < 64 * NUM_WORKGROUPS; i++) + { + fprintf(stderr, "(%.1f, %.1f, %.1f, %.1f) * (%.1f, %.1f, %.1f, %.1f) => (%.1f, %.1f, %.1f, %.1f)\n", a[i].x, + a[i].y, a[i].z, a[i].w, b[i].x, b[i].y, b[i].z, b[i].w, c[i].x, c[i].y, c[i].z, c[i].w); + } +} diff --git a/third_party/spirv-cross/samples/cpp/shared.comp b/third_party/spirv-cross/samples/cpp/shared.comp new file mode 100644 index 0000000..7d59060 --- /dev/null +++ b/third_party/spirv-cross/samples/cpp/shared.comp @@ -0,0 +1,36 @@ +#version 310 es +layout(local_size_x = 64) in; + +layout(set = 0, binding = 0, std430) readonly buffer SSBO0 +{ + float inputs[]; +}; + +layout(set = 0, binding = 1, std430) writeonly buffer SSBO1 +{ + float outputs[]; +}; + +shared float tmp[gl_WorkGroupSize.x]; + +void main() +{ + uint local = gl_LocalInvocationIndex; + uint work_group = gl_WorkGroupID.x; + + // Does a trivial parallel reduction through shared memory. + tmp[local] = inputs[work_group * gl_WorkGroupSize.x * 2u + local] + inputs[work_group * gl_WorkGroupSize.x * 2u + local + gl_WorkGroupSize.x]; + memoryBarrierShared(); + barrier(); + + for (uint limit = 32u; limit > 1u; limit >>= 1u) + { + if (local < limit) + tmp[local] = tmp[local] + tmp[local + limit]; + memoryBarrierShared(); + barrier(); + } + + if (local == 0u) + outputs[work_group] = tmp[0] + tmp[1]; +} diff --git a/third_party/spirv-cross/samples/cpp/shared.cpp b/third_party/spirv-cross/samples/cpp/shared.cpp new file mode 100644 index 0000000..5be62d6 --- /dev/null +++ b/third_party/spirv-cross/samples/cpp/shared.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2015-2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cross/external_interface.h" +#include + +#ifndef GLM_SWIZZLE +#define GLM_SWIZZLE +#endif + +#ifndef GLM_FORCE_RADIANS +#define GLM_FORCE_RADIANS +#endif + +#include +using namespace glm; + +int main() +{ + // First, we get the C interface to the shader. + // This can be loaded from a dynamic library, or as here, + // linked in as a static library. + auto *iface = spirv_cross_get_interface(); + + // Create an instance of the shader interface. + auto *shader = iface->construct(); + +// Build some input data for our compute shader. +#define NUM_WORKGROUPS 4 + float a[128 * NUM_WORKGROUPS]; + float b[NUM_WORKGROUPS] = {}; + + for (int i = 0; i < 128 * NUM_WORKGROUPS; i++) + { + a[i] = float(i); + } + + void *aptr = a; + void *bptr = b; + + // Bind resources to the shader. + // For resources like samplers and buffers, we provide a list of pointers, + // since UBOs, SSBOs and samplers can be arrays, and can point to different types, + // which is especially true for samplers. + spirv_cross_set_resource(shader, 0, 0, &aptr, sizeof(aptr)); + spirv_cross_set_resource(shader, 0, 1, &bptr, sizeof(bptr)); + + // We also have to set builtins. + // The relevant builtins will depend on the shader, + // but for compute, there are few builtins, which are gl_NumWorkGroups and gl_WorkGroupID. + // LocalInvocationID and GlobalInvocationID are inferred when executing the invocation. + uvec3 num_workgroups(NUM_WORKGROUPS, 1, 1); + uvec3 work_group_id(0, 0, 0); + spirv_cross_set_builtin(shader, SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS, &num_workgroups, sizeof(num_workgroups)); + spirv_cross_set_builtin(shader, SPIRV_CROSS_BUILTIN_WORK_GROUP_ID, &work_group_id, sizeof(work_group_id)); + + // Execute 4 work groups. + for (unsigned i = 0; i < NUM_WORKGROUPS; i++) + { + work_group_id.x = i; + iface->invoke(shader); + } + + // Call destructor. + iface->destruct(shader); + + // Verify our output. + // TODO: Implement a test framework that asserts results computed. + for (unsigned i = 0; i < NUM_WORKGROUPS; i++) + { + float expected_sum = 0.0f; + for (unsigned j = i * 128; j < (i + 1) * 128; j++) + expected_sum += a[j]; + fprintf(stderr, "Sum in workgroup #%u = %.1f, expected %.1f\n", i, b[i], expected_sum); + } +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/access-chain-load-store-composite.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/access-chain-load-store-composite.asm.comp new file mode 100644 index 0000000..3371e3a --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/access-chain-load-store-composite.asm.comp @@ -0,0 +1,118 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 437 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Baz "Baz" + OpMemberName %Baz 0 "c" + OpName %Bar "Bar" + OpMemberName %Bar 0 "d" + OpMemberName %Bar 1 "baz" + OpName %Foo "Foo" + OpMemberName %Foo 0 "a" + OpMemberName %Foo 1 "b" + OpMemberName %Foo 2 "c" + OpName %Baz_0 "Baz" + OpMemberName %Baz_0 0 "c" + OpName %Bar_0 "Bar" + OpMemberName %Bar_0 0 "d" + OpMemberName %Bar_0 1 "baz" + OpName %Foo_0 "Foo" + OpMemberName %Foo_0 0 "a" + OpMemberName %Foo_0 1 "b" + OpMemberName %Foo_0 2 "c" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "foo" + OpMemberName %SSBO 1 "foo2" + OpName %_ "" + OpDecorate %_arr_float_uint_4_0 ArrayStride 4 + OpDecorate %_arr__arr_float_uint_4_0_uint_2 ArrayStride 16 + OpMemberDecorate %Baz_0 0 Offset 0 + OpDecorate %_arr_Baz_0_uint_2 ArrayStride 4 + OpMemberDecorate %Bar_0 0 Offset 0 + OpMemberDecorate %Bar_0 1 Offset 32 + OpDecorate %_arr_Bar_0_uint_5 ArrayStride 40 + OpMemberDecorate %Foo_0 0 RowMajor + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %Foo_0 0 MatrixStride 8 + OpMemberDecorate %Foo_0 1 Offset 16 + OpMemberDecorate %Foo_0 2 Offset 24 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 224 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%mat2v2float = OpTypeMatrix %v2float 2 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 + %uint_2 = OpConstant %uint 2 +%_arr__arr_float_uint_4_uint_2 = OpTypeArray %_arr_float_uint_4 %uint_2 + %Baz = OpTypeStruct %float +%_arr_Baz_uint_2 = OpTypeArray %Baz %uint_2 + %Bar = OpTypeStruct %_arr__arr_float_uint_4_uint_2 %_arr_Baz_uint_2 + %uint_5 = OpConstant %uint 5 +%_arr_Bar_uint_5 = OpTypeArray %Bar %uint_5 + %Foo = OpTypeStruct %mat2v2float %v2float %_arr_Bar_uint_5 +%_ptr_Function_Foo = OpTypePointer Function %Foo +%_arr_float_uint_4_0 = OpTypeArray %float %uint_4 +%_arr__arr_float_uint_4_0_uint_2 = OpTypeArray %_arr_float_uint_4_0 %uint_2 + %Baz_0 = OpTypeStruct %float +%_arr_Baz_0_uint_2 = OpTypeArray %Baz_0 %uint_2 + %Bar_0 = OpTypeStruct %_arr__arr_float_uint_4_0_uint_2 %_arr_Baz_0_uint_2 +%_arr_Bar_0_uint_5 = OpTypeArray %Bar_0 %uint_5 + %Foo_0 = OpTypeStruct %mat2v2float %v2float %_arr_Bar_0_uint_5 + %SSBO = OpTypeStruct %Foo_0 %Foo_0 +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Function_mat2v2float = OpTypePointer Function %mat2v2float + %int_1 = OpConstant %int 1 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %int_2 = OpConstant %int 2 +%_ptr_Function__arr_Bar_uint_5 = OpTypePointer Function %_arr_Bar_uint_5 +%_ptr_Function_Bar = OpTypePointer Function %Bar +%_ptr_Function__arr__arr_float_uint_4_uint_2 = OpTypePointer Function %_arr__arr_float_uint_4_uint_2 +%_ptr_Function__arr_float_uint_4 = OpTypePointer Function %_arr_float_uint_4 +%_ptr_Function_float = OpTypePointer Function %float + %int_3 = OpConstant %int 3 +%_ptr_Function__arr_Baz_uint_2 = OpTypePointer Function %_arr_Baz_uint_2 +%_ptr_Function_Baz = OpTypePointer Function %Baz + %int_4 = OpConstant %int 4 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_5 = OpConstant %float 5 +%_ptr_Uniform_mat2v2float = OpTypePointer Uniform %mat2v2float +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Uniform__arr_Bar_0_uint_5 = OpTypePointer Uniform %_arr_Bar_0_uint_5 +%_ptr_Uniform_Bar_0 = OpTypePointer Uniform %Bar_0 +%_ptr_Uniform__arr__arr_float_uint_4_0_uint_2 = OpTypePointer Uniform %_arr__arr_float_uint_4_0_uint_2 +%_ptr_Uniform__arr_float_uint_4_0 = OpTypePointer Uniform %_arr_float_uint_4_0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform__arr_Baz_0_uint_2 = OpTypePointer Uniform %_arr_Baz_0_uint_2 +%_ptr_Uniform_Baz_0 = OpTypePointer Uniform %Baz_0 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %ptr_load = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_0 + %ptr_store = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_1 + %loaded = OpLoad %Foo_0 %ptr_load + OpStore %ptr_store %loaded + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp new file mode 100644 index 0000000..87aee2d --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 37 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %T "T" + OpMemberName %T 0 "a" + OpName %v "v" + OpName %T_0 "T" + OpMemberName %T_0 0 "b" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "foo" + OpName %_ "" + OpName %T_1 "T" + OpMemberName %T_1 0 "c" + OpName %SSBO2 "SSBO2" + OpMemberName %SSBO2 0 "bar" + OpName %__0 "" + OpMemberDecorate %T_0 0 Offset 0 + OpDecorate %_runtimearr_T_0 ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpMemberDecorate %T_1 0 Offset 0 + OpDecorate %_runtimearr_T_1 ArrayStride 16 + OpMemberDecorate %SSBO2 0 Offset 0 + OpDecorate %SSBO2 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %T = OpTypeStruct %float +%_ptr_Function_T = OpTypePointer Function %T + %float_40 = OpConstant %float 40 + %11 = OpConstantComposite %T %float_40 + %T_0 = OpTypeStruct %float +%_runtimearr_T_0 = OpTypeRuntimeArray %T_0 + %SSBO1 = OpTypeStruct %_runtimearr_T_0 +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 +%_ptr_Uniform_T_0 = OpTypePointer Uniform %T_0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %T_1 = OpTypeStruct %float +%_runtimearr_T_1 = OpTypeRuntimeArray %T_1 + %SSBO2 = OpTypeStruct %_runtimearr_T_1 +%_ptr_Uniform_SSBO2 = OpTypePointer Uniform %SSBO2 + %__0 = OpVariable %_ptr_Uniform_SSBO2 Uniform + %int_30 = OpConstant %int 30 +%_ptr_Uniform_T_1 = OpTypePointer Uniform %T_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %v = OpVariable %_ptr_Function_T Function + OpStore %v %11 + %20 = OpLoad %T %v + %22 = OpAccessChain %_ptr_Uniform_T_0 %_ %int_0 %int_10 + %23 = OpCompositeExtract %float %20 0 + %25 = OpAccessChain %_ptr_Uniform_float %22 %int_0 + OpStore %25 %23 + %32 = OpLoad %T %v + %34 = OpAccessChain %_ptr_Uniform_T_1 %__0 %int_0 %int_30 + %35 = OpCompositeExtract %float %32 0 + %36 = OpAccessChain %_ptr_Uniform_float %34 %int_0 + OpStore %36 %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/atomic-load-store.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/atomic-load-store.asm.comp new file mode 100644 index 0000000..3f2d141 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/atomic-load-store.asm.comp @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 23 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %c "c" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpMemberName %SSBO 1 "b" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %SSBO = OpTypeStruct %uint %uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_0 = OpConstant %int 0 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %c = OpVariable %_ptr_Function_uint Function + %15 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 + %16 = OpAtomicLoad %uint %15 %int_1 %int_0 + OpStore %c %16 + %18 = OpLoad %uint %c + %19 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 + OpAtomicStore %19 %int_1 %int_0 %18 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/atomic-result-temporary.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/atomic-result-temporary.asm.comp new file mode 100644 index 0000000..a323841 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/atomic-result-temporary.asm.comp @@ -0,0 +1,59 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "count" + OpMemberName %SSBO 1 "data" + OpName %_ "" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO = OpTypeStruct %uint %_runtimearr_uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %uint_1024 = OpConstant %uint 1024 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%_ptr_Input_uint = OpTypePointer Input %uint + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 + %19 = OpAtomicIAdd %uint %16 %uint_1 %uint_0 %uint_1 + %23 = OpULessThan %bool %19 %uint_1024 + OpSelectionMerge %25 None + OpBranchConditional %23 %24 %25 + %24 = OpLabel + %32 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %33 = OpLoad %uint %32 + %34 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 %19 + OpStore %34 %33 + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/bitfield-signed-operations.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/bitfield-signed-operations.asm.comp new file mode 100644 index 0000000..435fa32 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/bitfield-signed-operations.asm.comp @@ -0,0 +1,97 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 26 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "ints" + OpMemberName %SSBO 1 "uints" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + + %int_1 = OpConstant %int 1 + %uint_11 = OpConstant %uint 11 + + %SSBO = OpTypeStruct %v4int %v4uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4int = OpTypePointer Uniform %v4int +%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint + %main = OpFunction %void None %3 + %5 = OpLabel + %ints_ptr = OpAccessChain %_ptr_Uniform_v4int %_ %int_0 + %uints_ptr = OpAccessChain %_ptr_Uniform_v4uint %_ %int_1 + %ints = OpLoad %v4int %ints_ptr + %uints = OpLoad %v4uint %uints_ptr + + %ints_alt = OpVectorShuffle %v4int %ints %ints 3 2 1 0 + %uints_alt = OpVectorShuffle %v4uint %uints %uints 3 2 1 0 + + %int_to_int_popcount = OpBitCount %v4int %ints + %int_to_uint_popcount = OpBitCount %v4uint %ints + %uint_to_int_popcount = OpBitCount %v4int %uints + %uint_to_uint_popcount = OpBitCount %v4uint %uints + + ; BitReverse must have matching types w.r.t. sign, yay. + %int_to_int_reverse = OpBitReverse %v4int %ints + ;%int_to_uint_reverse = OpBitReverse %v4uint %ints + ;%uint_to_int_reverse = OpBitReverse %v4int %uints + %uint_to_uint_reverse = OpBitReverse %v4uint %uints + + ; Base and Result must match. + %int_to_int_sbit = OpBitFieldSExtract %v4int %ints %int_1 %uint_11 + ;%int_to_uint_sbit = OpBitFieldSExtract %v4uint %ints %offset %count + ;%uint_to_int_sbit = OpBitFieldSExtract %v4int %uints %offset %count + %uint_to_uint_sbit = OpBitFieldSExtract %v4uint %uints %uint_11 %int_1 + + ; Base and Result must match. + %int_to_int_ubit = OpBitFieldUExtract %v4int %ints %int_1 %uint_11 + ;%int_to_uint_ubit = OpBitFieldUExtract %v4uint %ints %offset %count + ;%uint_to_int_ubit = OpBitFieldUExtract %v4int %uints %offset %count + %uint_to_uint_ubit = OpBitFieldUExtract %v4uint %uints %uint_11 %int_1 + + %int_to_int_insert = OpBitFieldInsert %v4int %ints %ints_alt %int_1 %uint_11 + %uint_to_uint_insert = OpBitFieldInsert %v4uint %uints %uints_alt %uint_11 %int_1 + + OpStore %ints_ptr %int_to_int_popcount + OpStore %uints_ptr %int_to_uint_popcount + OpStore %ints_ptr %uint_to_int_popcount + OpStore %uints_ptr %uint_to_uint_popcount + + OpStore %ints_ptr %int_to_int_reverse + ;OpStore %uints_ptr %int_to_uint_reverse + ;OpStore %ints_ptr %uint_to_int_reverse + OpStore %uints_ptr %uint_to_uint_reverse + + OpStore %ints_ptr %int_to_int_sbit + ;OpStore %uints_ptr %int_to_uint_sbit + ;OpStore %ints_ptr %uint_to_int_sbit + OpStore %uints_ptr %uint_to_uint_sbit + + OpStore %ints_ptr %int_to_int_ubit + ;OpStore %uints_ptr %int_to_uint_ubit + ;OpStore %ints_ptr %uint_to_int_ubit + OpStore %uints_ptr %uint_to_uint_ubit + + OpStore %ints_ptr %int_to_int_insert + OpStore %uints_ptr %uint_to_uint_insert + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/bitscan.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/bitscan.asm.comp new file mode 100644 index 0000000..e3b785c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/bitscan.asm.comp @@ -0,0 +1,72 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "u" + OpMemberName %SSBO 1 "i" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + %SSBO = OpTypeStruct %uvec4 %ivec4 +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uvec4 = OpTypePointer Uniform %uvec4 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_ivec4 = OpTypePointer Uniform %ivec4 + %main = OpFunction %void None %3 + %5 = OpLabel + %uptr = OpAccessChain %_ptr_Uniform_uvec4 %_ %int_0 + %iptr = OpAccessChain %_ptr_Uniform_ivec4 %_ %int_1 + %uvalue = OpLoad %uvec4 %uptr + %ivalue = OpLoad %ivec4 %iptr + + %lsb_uint_to_uint = OpExtInst %uvec4 %1 FindILsb %uvalue + %lsb_uint_to_int = OpExtInst %ivec4 %1 FindILsb %uvalue + %lsb_int_to_uint = OpExtInst %uvec4 %1 FindILsb %ivalue + %lsb_int_to_int = OpExtInst %ivec4 %1 FindILsb %ivalue + + %umsb_uint_to_uint = OpExtInst %uvec4 %1 FindUMsb %uvalue + %umsb_uint_to_int = OpExtInst %ivec4 %1 FindUMsb %uvalue + %umsb_int_to_uint = OpExtInst %uvec4 %1 FindUMsb %ivalue + %umsb_int_to_int = OpExtInst %ivec4 %1 FindUMsb %ivalue + + %smsb_uint_to_uint = OpExtInst %uvec4 %1 FindSMsb %uvalue + %smsb_uint_to_int = OpExtInst %ivec4 %1 FindSMsb %uvalue + %smsb_int_to_uint = OpExtInst %uvec4 %1 FindSMsb %ivalue + %smsb_int_to_int = OpExtInst %ivec4 %1 FindSMsb %ivalue + + OpStore %uptr %lsb_uint_to_uint + OpStore %iptr %lsb_uint_to_int + OpStore %uptr %lsb_int_to_uint + OpStore %iptr %lsb_int_to_int + + OpStore %uptr %umsb_uint_to_uint + OpStore %iptr %umsb_uint_to_int + OpStore %uptr %umsb_int_to_uint + OpStore %iptr %umsb_int_to_int + + OpStore %uptr %smsb_uint_to_uint + OpStore %iptr %smsb_uint_to_int + OpStore %uptr %smsb_int_to_uint + OpStore %iptr %smsb_int_to_int + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/buffer-atomic-nonuniform.asm.sm51.nonuniformresource.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/buffer-atomic-nonuniform.asm.sm51.nonuniformresource.comp new file mode 100644 index 0000000..132f38b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/buffer-atomic-nonuniform.asm.sm51.nonuniformresource.comp @@ -0,0 +1,53 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 26 +; Schema: 0 + OpCapability Shader + OpCapability ShaderNonUniform + OpCapability RuntimeDescriptorArray + OpCapability StorageBufferArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "v" + OpName %ssbos "ssbos" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %ssbos DescriptorSet 0 + OpDecorate %ssbos Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %22 NonUniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %SSBO = OpTypeStruct %uint +%_runtimearr_SSBO = OpTypeRuntimeArray %SSBO +%_ptr_Uniform__runtimearr_SSBO = OpTypePointer Uniform %_runtimearr_SSBO + %ssbos = OpVariable %_ptr_Uniform__runtimearr_SSBO Uniform + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_2 = OpConstant %uint 2 +%_ptr_Input_uint = OpTypePointer Input %uint + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_2 + %17 = OpLoad %uint %16 + %18 = OpCopyObject %uint %17 + %22 = OpAccessChain %_ptr_Uniform_uint %ssbos %18 %int_0 + %25 = OpAtomicIAdd %uint %22 %uint_1 %uint_0 %uint_1 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/constant-composite-undef.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/constant-composite-undef.asm.comp new file mode 100644 index 0000000..8997d0a --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/constant-composite-undef.asm.comp @@ -0,0 +1,40 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Block "Block" + OpMemberName %Block 0 "f" + OpName %block "block" + OpMemberDecorate %Block 0 Offset 0 + OpDecorate %Block BufferBlock + OpDecorate %block DescriptorSet 0 + OpDecorate %block Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Block = OpTypeStruct %v4float +%_ptr_Uniform_Block = OpTypePointer Uniform %Block + %block = OpVariable %_ptr_Uniform_Block Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%float_0_100000001 = OpConstant %float 0.100000001 +%float_0_200000003 = OpConstant %float 0.200000003 +%float_0_300000012 = OpConstant %float 0.300000012 + %15 = OpUndef %float + %16 = OpConstantComposite %v4float %float_0_100000001 %float_0_200000003 %float_0_300000012 %15 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %main = OpFunction %void None %6 + %18 = OpLabel + %19 = OpAccessChain %_ptr_Uniform_v4float %block %int_0 + OpStore %19 %16 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/glsl-signed-operations.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/glsl-signed-operations.asm.comp new file mode 100644 index 0000000..7da9f95 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/glsl-signed-operations.asm.comp @@ -0,0 +1,123 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 26 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "ints" + OpMemberName %SSBO 1 "uints" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + %SSBO = OpTypeStruct %v4int %v4uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4int = OpTypePointer Uniform %v4int + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint + %main = OpFunction %void None %3 + %5 = OpLabel + %ints_ptr = OpAccessChain %_ptr_Uniform_v4int %_ %int_0 + %uints_ptr = OpAccessChain %_ptr_Uniform_v4uint %_ %int_1 + %ints = OpLoad %v4int %ints_ptr + %uints = OpLoad %v4uint %uints_ptr + + %int_to_int_sabs = OpExtInst %v4int %1 SAbs %ints + %int_to_uint_sabs = OpExtInst %v4uint %1 SAbs %ints + %uint_to_int_sabs = OpExtInst %v4int %1 SAbs %uints + %uint_to_uint_sabs = OpExtInst %v4uint %1 SAbs %uints + + %int_to_int_ssign = OpExtInst %v4int %1 SSign %ints + %int_to_uint_ssign = OpExtInst %v4uint %1 SSign %ints + %uint_to_int_ssign = OpExtInst %v4int %1 SSign %uints + %uint_to_uint_ssign = OpExtInst %v4uint %1 SSign %uints + + %int_to_int_smsb = OpExtInst %v4int %1 FindSMsb %uints + %int_to_uint_smsb = OpExtInst %v4uint %1 FindSMsb %uints + %uint_to_int_umsb = OpExtInst %v4int %1 FindUMsb %ints + %uint_to_uint_umsb = OpExtInst %v4uint %1 FindUMsb %ints + + %int_to_int_smin = OpExtInst %v4int %1 SMin %ints %ints + %int_to_uint_smin = OpExtInst %v4uint %1 SMin %ints %uints + %uint_to_int_smin = OpExtInst %v4int %1 SMin %uints %uints + %uint_to_uint_smin = OpExtInst %v4uint %1 SMin %uints %ints + + %int_to_int_umin = OpExtInst %v4int %1 UMin %ints %uints + %int_to_uint_umin = OpExtInst %v4uint %1 UMin %ints %uints + %uint_to_int_umin = OpExtInst %v4int %1 UMin %uints %ints + %uint_to_uint_umin = OpExtInst %v4uint %1 UMin %uints %ints + + %int_to_int_smax = OpExtInst %v4int %1 SMax %ints %ints + %int_to_uint_smax = OpExtInst %v4uint %1 SMax %ints %ints + %uint_to_int_smax = OpExtInst %v4int %1 SMax %uints %ints + %uint_to_uint_smax = OpExtInst %v4uint %1 SMax %uints %ints + + %int_to_int_umax = OpExtInst %v4int %1 UMax %ints %uints + %int_to_uint_umax = OpExtInst %v4uint %1 UMax %ints %ints + %uint_to_int_umax = OpExtInst %v4int %1 UMax %uints %ints + %uint_to_uint_umax = OpExtInst %v4uint %1 UMax %uints %ints + + %int_to_int_sclamp = OpExtInst %v4int %1 SClamp %uints %uints %uints + %int_to_uint_sclamp = OpExtInst %v4uint %1 SClamp %uints %uints %uints + %uint_to_int_uclamp = OpExtInst %v4int %1 UClamp %ints %ints %ints + %uint_to_uint_uclamp = OpExtInst %v4uint %1 UClamp %ints %ints %ints + + OpStore %ints_ptr %int_to_int_sabs + OpStore %uints_ptr %int_to_uint_sabs + OpStore %ints_ptr %uint_to_int_sabs + OpStore %uints_ptr %uint_to_uint_sabs + + OpStore %ints_ptr %int_to_int_ssign + OpStore %uints_ptr %int_to_uint_ssign + OpStore %ints_ptr %uint_to_int_ssign + OpStore %uints_ptr %uint_to_uint_ssign + + OpStore %ints_ptr %int_to_int_smsb + OpStore %uints_ptr %int_to_uint_smsb + OpStore %ints_ptr %uint_to_int_umsb + OpStore %uints_ptr %uint_to_uint_umsb + + OpStore %ints_ptr %int_to_int_smin + OpStore %uints_ptr %int_to_uint_smin + OpStore %ints_ptr %uint_to_int_smin + OpStore %uints_ptr %uint_to_uint_smin + + OpStore %ints_ptr %int_to_int_umin + OpStore %uints_ptr %int_to_uint_umin + OpStore %ints_ptr %uint_to_int_umin + OpStore %uints_ptr %uint_to_uint_umin + + OpStore %ints_ptr %int_to_int_smax + OpStore %uints_ptr %int_to_uint_smax + OpStore %ints_ptr %uint_to_int_smax + OpStore %uints_ptr %uint_to_uint_smax + + OpStore %ints_ptr %int_to_int_umax + OpStore %uints_ptr %int_to_uint_umax + OpStore %ints_ptr %uint_to_int_umax + OpStore %uints_ptr %uint_to_uint_umax + + OpStore %ints_ptr %int_to_int_sclamp + OpStore %uints_ptr %int_to_uint_sclamp + OpStore %ints_ptr %uint_to_int_uclamp + OpStore %uints_ptr %uint_to_uint_uclamp + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.fxconly.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.fxconly.asm.comp new file mode 100644 index 0000000..30db11d --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.fxconly.asm.comp @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %ResTypeMod = OpTypeStruct %float %float +%_ptr_Function_ResTypeMod = OpTypePointer Function %ResTypeMod + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_20 = OpConstant %float 20 + %int_1 = OpConstant %int 1 +%_ptr_Function_float = OpTypePointer Function %float +%ResTypeFrexp = OpTypeStruct %float %int +%_ptr_Function_ResTypeFrexp = OpTypePointer Function %ResTypeFrexp + %float_40 = OpConstant %float 40 +%_ptr_Function_int = OpTypePointer Function %int + %SSBO = OpTypeStruct %float %int +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_int = OpTypePointer Uniform %int + %main = OpFunction %void None %3 + %5 = OpLabel + %modres = OpExtInst %ResTypeMod %1 ModfStruct %float_20 + %frexpres = OpExtInst %ResTypeFrexp %1 FrexpStruct %float_40 + + %modres_f = OpCompositeExtract %float %modres 0 + %modres_i = OpCompositeExtract %float %modres 1 + %frexpres_f = OpCompositeExtract %float %frexpres 0 + %frexpres_i = OpCompositeExtract %int %frexpres 1 + + %float_ptr = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %int_ptr = OpAccessChain %_ptr_Uniform_int %_ %int_1 + + OpStore %float_ptr %modres_f + OpStore %float_ptr %modres_i + OpStore %float_ptr %frexpres_f + OpStore %int_ptr %frexpres_i + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/image-atomic-nonuniform.asm.sm51.nonuniformresource.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/image-atomic-nonuniform.asm.sm51.nonuniformresource.comp new file mode 100644 index 0000000..5dad9dd --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/image-atomic-nonuniform.asm.sm51.nonuniformresource.comp @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 32 +; Schema: 0 + OpCapability Shader + OpCapability ShaderNonUniform + OpCapability RuntimeDescriptorArray + OpCapability StorageImageArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %uImage "uImage" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %uImage DescriptorSet 0 + OpDecorate %uImage Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %30 NonUniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpTypeImage %uint 2D 0 0 0 2 R32ui +%_runtimearr_7 = OpTypeRuntimeArray %7 +%_ptr_UniformConstant__runtimearr_7 = OpTypePointer UniformConstant %_runtimearr_7 + %uImage = OpVariable %_ptr_UniformConstant__runtimearr_7 UniformConstant + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_2 = OpConstant %uint 2 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7 + %v2uint = OpTypeVector %uint 2 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 +%_ptr_Image_uint = OpTypePointer Image %uint + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_2 + %17 = OpLoad %uint %16 + %18 = OpCopyObject %uint %17 + %20 = OpAccessChain %_ptr_UniformConstant_7 %uImage %18 + %22 = OpLoad %v3uint %gl_GlobalInvocationID + %23 = OpVectorShuffle %v2uint %22 %22 0 1 + %26 = OpBitcast %v2int %23 + %30 = OpImageTexelPointer %_ptr_Image_uint %20 %26 %uint_0 + %31 = OpAtomicIAdd %uint %30 %uint_1 %uint_0 %uint_1 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/specialization-constant-workgroup.nofxc.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/specialization-constant-workgroup.nofxc.asm.comp new file mode 100644 index 0000000..188e3fe --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/specialization-constant-workgroup.nofxc.asm.comp @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 24 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 20 1 + OpSource ESSL 310 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %19 SpecId 10 + OpDecorate %21 SpecId 12 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %SSBO = OpTypeStruct %float +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %uint = OpTypeInt 32 0 + %19 = OpSpecConstant %uint 9 + %uint_20 = OpConstant %uint 20 + %21 = OpSpecConstant %uint 4 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %19 %uint_20 %21 + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %15 = OpLoad %float %14 + %16 = OpFAdd %float %15 %float_1 + %17 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %17 %16 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/storage-buffer-basic.invalid.nofxc.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/storage-buffer-basic.invalid.nofxc.asm.comp new file mode 100644 index 0000000..edb1a05 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/comp/storage-buffer-basic.invalid.nofxc.asm.comp @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Codeplay; 0 +; Bound: 31 +; Schema: 0 + OpCapability Shader + OpCapability VariablePointers + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_KHR_variable_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %22 "main" %gl_WorkGroupID + OpSource OpenCL_C 120 + OpDecorate %15 SpecId 0 + ;OpDecorate %16 SpecId 1 + OpDecorate %17 SpecId 2 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_4 0 Offset 0 + OpDecorate %_struct_4 Block + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + OpDecorate %20 DescriptorSet 0 + OpDecorate %20 Binding 0 + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + %float = OpTypeFloat 32 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_4 = OpTypeStruct %_runtimearr_float +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %8 = OpTypeFunction %void + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Private_v3uint = OpTypePointer Private %v3uint + %uint_0 = OpConstant %uint 0 +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %15 = OpSpecConstant %uint 1 + %16 = OpConstant %uint 2 + %17 = OpSpecConstant %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %15 %16 %17 + %19 = OpVariable %_ptr_Private_v3uint Private %gl_WorkGroupSize + %20 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer + %21 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer + %22 = OpFunction %void None %8 + %23 = OpLabel + %24 = OpAccessChain %_ptr_Input_uint %gl_WorkGroupID %uint_0 + %25 = OpLoad %uint %24 + %26 = OpAccessChain %_ptr_StorageBuffer_float %21 %uint_0 %25 + %27 = OpLoad %float %26 + %28 = OpAccessChain %_ptr_StorageBuffer_float %20 %uint_0 %25 + %29 = OpLoad %float %28 + %30 = OpFAdd %float %27 %29 + OpStore %28 %30 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/empty-struct-in-struct.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/empty-struct-in-struct.asm.frag new file mode 100644 index 0000000..a9650dd --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/empty-struct-in-struct.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.2 +; Generator: Khronos; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %EntryPoint_Main "main" + OpExecutionMode %EntryPoint_Main OriginUpperLeft + OpSource Unknown 100 + OpName %EmptyStructTest "EmptyStructTest" + OpName %EmptyStruct2Test "EmptyStruct2Test" + OpName %GetValue "GetValue" + OpName %GetValue2 "GetValue" + OpName %self "self" + OpName %self2 "self" + OpName %emptyStruct "emptyStruct" + OpName %value "value" + OpName %EntryPoint_Main "EntryPoint_Main" + +%EmptyStructTest = OpTypeStruct +%EmptyStruct2Test = OpTypeStruct %EmptyStructTest +%_ptr_Function_EmptyStruct2Test = OpTypePointer Function %EmptyStruct2Test + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %5 = OpTypeFunction %float %_ptr_Function_EmptyStruct2Test + %6 = OpTypeFunction %float %EmptyStruct2Test + %void = OpTypeVoid +%_ptr_Function_void = OpTypePointer Function %void + %8 = OpTypeFunction %void %_ptr_Function_EmptyStruct2Test + %9 = OpTypeFunction %void + %float_0 = OpConstant %float 0 + %value4 = OpConstantNull %EmptyStruct2Test + + %GetValue = OpFunction %float None %5 + %self = OpFunctionParameter %_ptr_Function_EmptyStruct2Test + %13 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + + %GetValue2 = OpFunction %float None %6 + %self2 = OpFunctionParameter %EmptyStruct2Test + %14 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + +%EntryPoint_Main = OpFunction %void None %9 + %37 = OpLabel + %emptyStruct = OpVariable %_ptr_Function_EmptyStruct2Test Function + %18 = OpVariable %_ptr_Function_EmptyStruct2Test Function + %value = OpVariable %_ptr_Function_float Function + %value2 = OpCompositeConstruct %EmptyStructTest + %value3 = OpCompositeConstruct %EmptyStruct2Test %value2 + %22 = OpFunctionCall %float %GetValue %emptyStruct + %23 = OpFunctionCall %float %GetValue2 %value3 + %24 = OpFunctionCall %float %GetValue2 %value4 + OpStore %value %22 + OpStore %value %23 + OpStore %value %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag new file mode 100644 index 0000000..ca8022d --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag @@ -0,0 +1,44 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 29 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_Target0 + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %Tex "Tex" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %main "main" + OpDecorate %in_var_TEXCOORD0 Flat + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %Tex DescriptorSet 0 + OpDecorate %Tex Binding 0 + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %16 = OpTypeFunction %void + %Tex = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v3uint Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %16 + %19 = OpLabel + %20 = OpLoad %v3uint %in_var_TEXCOORD0 + %21 = OpCompositeExtract %uint %20 2 + %27 = OpLoad %type_2d_image %Tex + %28 = OpImageFetch %v4float %27 %20 Lod %21 + OpStore %out_var_SV_Target0 %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/nonuniform-bracket-handling-2.nonuniformresource.sm51.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/nonuniform-bracket-handling-2.nonuniformresource.sm51.asm.frag new file mode 100644 index 0000000..ea85ed0 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/nonuniform-bracket-handling-2.nonuniformresource.sm51.asm.frag @@ -0,0 +1,96 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 53 +; Schema: 0 + OpCapability Shader + OpCapability ShaderNonUniform + OpCapability RuntimeDescriptorArray + OpCapability SampledImageArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vUV %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uSamplers "uSamplers" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "indices" + OpName %_ "" + OpName %vUV "vUV" + OpName %uSampler "uSampler" + OpName %gl_FragCoord "gl_FragCoord" + OpDecorate %FragColor Location 0 + OpDecorate %uSamplers DescriptorSet 0 + OpDecorate %uSamplers Binding 0 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO 0 NonWritable + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %26 NonUniform + OpDecorate %28 NonUniform + OpDecorate %29 NonUniform + OpDecorate %vUV Location 0 + OpDecorate %uSampler DescriptorSet 1 + OpDecorate %uSampler Binding 0 + OpDecorate %38 NonUniform + OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_runtimearr_11 = OpTypeRuntimeArray %11 +%_ptr_UniformConstant__runtimearr_11 = OpTypePointer UniformConstant %_runtimearr_11 + %uSamplers = OpVariable %_ptr_UniformConstant__runtimearr_11 UniformConstant + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input + %float_0 = OpConstant %float 0 + %uSampler = OpVariable %_ptr_UniformConstant_11 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_1 = OpConstant %uint 1 +%_ptr_Input_float = OpTypePointer Input %float + %main = OpFunction %void None %3 + %5 = OpLabel + %24 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %int_10 + %26 = OpLoad %uint %24 + %28 = OpAccessChain %_ptr_UniformConstant_11 %uSamplers %26 + %29 = OpLoad %11 %28 + %33 = OpLoad %v2float %vUV + %35 = OpImageSampleExplicitLod %v4float %29 %33 Lod %float_0 + OpStore %FragColor %35 + %37 = OpLoad %11 %uSampler + %38 = OpCopyObject %11 %37 + %39 = OpLoad %v2float %vUV + %44 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_1 + %45 = OpLoad %float %44 + %46 = OpConvertFToS %int %45 + %47 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %46 + %48 = OpLoad %uint %47 + %49 = OpConvertUToF %float %48 + %50 = OpImageSampleExplicitLod %v4float %38 %39 Lod %49 + %51 = OpLoad %v4float %FragColor + %52 = OpFAdd %v4float %51 %50 + OpStore %FragColor %52 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/nonuniform-qualifier-propagation.nonuniformresource.sm51.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/nonuniform-qualifier-propagation.nonuniformresource.sm51.asm.frag new file mode 100644 index 0000000..5aa6884 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/nonuniform-qualifier-propagation.nonuniformresource.sm51.asm.frag @@ -0,0 +1,159 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 93 +; Schema: 0 + OpCapability Shader + OpCapability ShaderNonUniformEXT + OpCapability RuntimeDescriptorArrayEXT + OpCapability UniformBufferArrayNonUniformIndexingEXT + OpCapability SampledImageArrayNonUniformIndexingEXT + OpCapability StorageBufferArrayNonUniformIndexingEXT + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vIndex %FragColor %vUV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %i "i" + OpName %vIndex "vIndex" + OpName %FragColor "FragColor" + OpName %uSamplers "uSamplers" + OpName %uSamps "uSamps" + OpName %vUV "vUV" + OpName %uCombinedSamplers "uCombinedSamplers" + OpName %UBO "UBO" + OpMemberName %UBO 0 "v" + OpName %ubos "ubos" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "v" + OpName %ssbos "ssbos" + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + OpDecorate %FragColor Location 0 + OpDecorate %uSamplers DescriptorSet 0 + OpDecorate %uSamplers Binding 0 + + OpDecorate %sampled_image NonUniformEXT + OpDecorate %combined_sampler NonUniformEXT + OpDecorate %ubo_ptr_copy NonUniformEXT + OpDecorate %ssbo_ptr_copy NonUniformEXT + + OpDecorate %uSamps DescriptorSet 1 + OpDecorate %uSamps Binding 0 + OpDecorate %vUV Location 1 + OpDecorate %uCombinedSamplers DescriptorSet 0 + OpDecorate %uCombinedSamplers Binding 4 + OpDecorate %_arr_v4float_uint_64 ArrayStride 16 + OpMemberDecorate %UBO 0 Offset 0 + OpDecorate %UBO Block + OpDecorate %ubos DescriptorSet 2 + OpDecorate %ubos Binding 0 + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %SSBO 0 NonWritable + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %ssbos DescriptorSet 3 + OpDecorate %ssbos Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %16 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_runtimearr_16 = OpTypeRuntimeArray %16 +%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16 + %uSamplers = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant + %int_10 = OpConstant %int 10 +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 + %27 = OpTypeSampler +%_runtimearr_27 = OpTypeRuntimeArray %27 +%_ptr_UniformConstant__runtimearr_27 = OpTypePointer UniformConstant %_runtimearr_27 + %uSamps = OpVariable %_ptr_UniformConstant__runtimearr_27 UniformConstant + %int_40 = OpConstant %int 40 +%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27 + %38 = OpTypeSampledImage %16 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input +%_runtimearr_38 = OpTypeRuntimeArray %38 +%_ptr_UniformConstant__runtimearr_38 = OpTypePointer UniformConstant %_runtimearr_38 +%uCombinedSamplers = OpVariable %_ptr_UniformConstant__runtimearr_38 UniformConstant +%_ptr_UniformConstant_38 = OpTypePointer UniformConstant %38 + %uint = OpTypeInt 32 0 + %uint_64 = OpConstant %uint 64 +%_arr_v4float_uint_64 = OpTypeArray %v4float %uint_64 + %UBO = OpTypeStruct %_arr_v4float_uint_64 +%_runtimearr_UBO = OpTypeRuntimeArray %UBO +%_ptr_Uniform__runtimearr_UBO = OpTypePointer Uniform %_runtimearr_UBO + %ubos = OpVariable %_ptr_Uniform__runtimearr_UBO Uniform + %int_20 = OpConstant %int 20 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %SSBO = OpTypeStruct %_runtimearr_v4float +%_runtimearr_SSBO = OpTypeRuntimeArray %SSBO +%_ptr_Uniform__runtimearr_SSBO = OpTypePointer Uniform %_runtimearr_SSBO + %ssbos = OpVariable %_ptr_Uniform__runtimearr_SSBO Uniform + %int_50 = OpConstant %int 50 + %int_60 = OpConstant %int 60 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %11 = OpLoad %int %vIndex + OpStore %i %11 + %20 = OpLoad %int %i + %22 = OpIAdd %int %20 %int_10 + %23 = OpCopyObject %int %22 + %25 = OpAccessChain %_ptr_UniformConstant_16 %uSamplers %23 + %26 = OpLoad %16 %25 + %31 = OpLoad %int %i + %33 = OpIAdd %int %31 %int_40 + %34 = OpCopyObject %int %33 + %36 = OpAccessChain %_ptr_UniformConstant_27 %uSamps %34 + %37 = OpLoad %27 %36 + %sampled_image = OpSampledImage %38 %26 %37 + %43 = OpLoad %v2float %vUV + %44 = OpImageSampleImplicitLod %v4float %sampled_image %43 + OpStore %FragColor %44 + %48 = OpLoad %int %i + %49 = OpIAdd %int %48 %int_10 + %50 = OpCopyObject %int %49 + %52 = OpAccessChain %_ptr_UniformConstant_38 %uCombinedSamplers %50 + %combined_sampler = OpLoad %38 %52 + %54 = OpLoad %v2float %vUV + %55 = OpImageSampleImplicitLod %v4float %combined_sampler %54 + OpStore %FragColor %55 + %63 = OpLoad %int %i + %65 = OpIAdd %int %63 %int_20 + %66 = OpCopyObject %int %65 + %68 = OpLoad %int %i + %69 = OpIAdd %int %68 %int_40 + %70 = OpCopyObject %int %69 + %ubo_ptr = OpAccessChain %_ptr_Uniform_v4float %ubos %66 %int_0 %70 + %ubo_ptr_copy = OpCopyObject %_ptr_Uniform_v4float %ubo_ptr + %73 = OpLoad %v4float %ubo_ptr_copy + %74 = OpLoad %v4float %FragColor + %75 = OpFAdd %v4float %74 %73 + OpStore %FragColor %75 + %81 = OpLoad %int %i + %83 = OpIAdd %int %81 %int_50 + %84 = OpCopyObject %int %83 + %85 = OpLoad %int %i + %87 = OpIAdd %int %85 %int_60 + %88 = OpCopyObject %int %87 + %ssbo_ptr = OpAccessChain %_ptr_Uniform_v4float %ssbos %84 %int_0 %88 + %ssbo_ptr_copy = OpCopyObject %_ptr_Uniform_v4float %ssbo_ptr + %90 = OpLoad %v4float %ssbo_ptr_copy + %91 = OpLoad %v4float %FragColor + %92 = OpFAdd %v4float %91 %90 + OpStore %FragColor %92 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/phi.zero-initialize.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/phi.zero-initialize.asm.frag new file mode 100644 index 0000000..3696660 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/phi.zero-initialize.asm.frag @@ -0,0 +1,69 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 40 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vColor %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %vColor "vColor" + OpName %uninit_function_int "uninit_function_int" + OpName %FragColor "FragColor" + OpName %uninit_int "uninit_int" + OpName %uninit_vector "uninit_vector" + OpName %uninit_matrix "uninit_matrix" + OpName %Foo "Foo" + OpMemberName %Foo 0 "a" + OpName %uninit_foo "uninit_foo" + OpDecorate %vColor Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vColor = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_10 = OpConstant %int 10 + %int_20 = OpConstant %int 20 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Private_int = OpTypePointer Private %int + %uninit_int = OpUndef %int + %v4int = OpTypeVector %int 4 +%_ptr_Private_v4int = OpTypePointer Private %v4int +%uninit_vector = OpUndef %v4int +%mat4v4float = OpTypeMatrix %v4float 4 +%_ptr_Private_mat4v4float = OpTypePointer Private %mat4v4float +%uninit_matrix = OpUndef %mat4v4float + %Foo = OpTypeStruct %int +%_ptr_Private_Foo = OpTypePointer Private %Foo + %uninit_foo = OpUndef %Foo + %main = OpFunction %void None %3 + %5 = OpLabel +%uninit_function_int = OpVariable %_ptr_Function_int Function + %13 = OpAccessChain %_ptr_Input_float %vColor %uint_0 + %14 = OpLoad %float %13 + %17 = OpFOrdGreaterThan %bool %14 %float_10 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %24 + %18 = OpLabel + OpBranch %19 + %24 = OpLabel + OpBranch %19 + %19 = OpLabel + %27 = OpPhi %int %int_10 %18 %int_20 %24 + %28 = OpLoad %v4float %vColor + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-callstack.sm51.fxconly.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-callstack.sm51.fxconly.asm.frag new file mode 100644 index 0000000..ebd8d6b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-callstack.sm51.fxconly.asm.frag @@ -0,0 +1,89 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + OpReturn + OpFunctionEnd + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + OpBeginInvocationInterlockEXT + %43 = OpFunctionCall %void %callee2_ + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-control-flow.sm51.fxconly.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-control-flow.sm51.fxconly.asm.frag new file mode 100644 index 0000000..69b8f91 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-control-flow.sm51.fxconly.asm.frag @@ -0,0 +1,121 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + + OpMemberDecorate %SSBO2 0 Offset 0 + OpDecorate %SSBO2 BufferBlock + OpDecorate %ssbo2 DescriptorSet 0 + OpDecorate %ssbo2 Binding 2 + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint + %SSBO2 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 +%_ptr_Uniform_SSBO2 = OpTypePointer Uniform %SSBO2 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %ssbo2 = OpVariable %_ptr_Uniform_SSBO2 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_4 = OpConstant %uint 4 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %bool = OpTypeBool + %true = OpConstantTrue %bool +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + %callee3_res = OpFunctionCall %void %callee3_ + OpReturn + OpFunctionEnd + + %callee3_ = OpFunction %void None %3 + %calle3_block = OpLabel + %frag_coord_x_ptr = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %frag_coord_x = OpLoad %float %frag_coord_x_ptr + %frag_coord_int = OpConvertFToS %int %frag_coord_x + %ssbo_ptr = OpAccessChain %_ptr_Uniform_uint %ssbo2 %int_0 %frag_coord_int + OpStore %ssbo_ptr %uint_4 + OpReturn + OpFunctionEnd + + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + %43 = OpFunctionCall %void %callee2_ + + OpSelectionMerge %merged_block None + OpBranchConditional %true %dummy_block %merged_block + %dummy_block = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpBranch %merged_block + + %merged_block = OpLabel + OpReturn + + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-split-functions.sm51.fxconly.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-split-functions.sm51.fxconly.asm.frag new file mode 100644 index 0000000..7c0fe9a --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/pixel-interlock-split-functions.sm51.fxconly.asm.frag @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + %call3res = OpFunctionCall %void %callee3_ + %call4res = OpFunctionCall %void %callee4_ + OpReturn + OpFunctionEnd + + %callee3_ = OpFunction %void None %3 + %begin3 = OpLabel + OpBeginInvocationInterlockEXT + OpReturn + OpFunctionEnd + + %callee4_ = OpFunction %void None %3 + %begin4 = OpLabel + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + %43 = OpFunctionCall %void %callee2_ + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/struct-packing-last-element-array-matrix-rule.invalid.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/struct-packing-last-element-array-matrix-rule.invalid.asm.frag new file mode 100644 index 0000000..421e466 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/struct-packing-last-element-array-matrix-rule.invalid.asm.frag @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 33 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %Foo "Foo" + OpMemberName %Foo 0 "m" + OpMemberName %Foo 1 "v" + OpName %FooUBO "FooUBO" + OpMemberName %FooUBO 0 "foo" + OpName %_ "" + OpName %Bar "Bar" + OpMemberName %Bar 0 "m" + OpMemberName %Bar 1 "v" + OpName %BarUBO "BarUBO" + OpMemberName %BarUBO 0 "bar" + OpName %__0 "" + OpDecorate %FragColor Location 0 + OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 + OpMemberDecorate %Foo 0 ColMajor + OpMemberDecorate %Foo 0 Offset 0 + OpMemberDecorate %Foo 0 MatrixStride 16 + OpMemberDecorate %Foo 1 Offset 92 + OpMemberDecorate %FooUBO 0 Offset 0 + OpDecorate %FooUBO Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpMemberDecorate %Bar 0 ColMajor + OpMemberDecorate %Bar 0 Offset 0 + OpMemberDecorate %Bar 0 MatrixStride 16 + OpMemberDecorate %Bar 1 Offset 44 + OpMemberDecorate %BarUBO 0 Offset 0 + OpDecorate %BarUBO Block + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %v3float = OpTypeVector %float 3 +%mat3v3float = OpTypeMatrix %v3float 3 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 + %Foo = OpTypeStruct %_arr_mat3v3float_uint_2 %float + %FooUBO = OpTypeStruct %Foo +%_ptr_Uniform_FooUBO = OpTypePointer Uniform %FooUBO + %_ = OpVariable %_ptr_Uniform_FooUBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Bar = OpTypeStruct %mat3v3float %float + %BarUBO = OpTypeStruct %Bar +%_ptr_Uniform_BarUBO = OpTypePointer Uniform %BarUBO + %__0 = OpVariable %_ptr_Uniform_BarUBO Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %23 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_1 + %24 = OpLoad %float %23 + %29 = OpAccessChain %_ptr_Uniform_float %__0 %int_0 %int_1 + %30 = OpLoad %float %29 + %31 = OpFAdd %float %24 %30 + %32 = OpCompositeConstruct %v4float %31 %31 %31 %31 + OpStore %FragColor %32 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/subgroup-arithmetic-cast.invalid.nofxc.sm60.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/subgroup-arithmetic-cast.invalid.nofxc.sm60.asm.frag new file mode 100644 index 0000000..a9b3489 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/subgroup-arithmetic-cast.invalid.nofxc.sm60.asm.frag @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 78 +; Schema: 0 + OpCapability Shader + OpCapability GroupNonUniform + OpCapability GroupNonUniformArithmetic + OpCapability GroupNonUniformClustered + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %index %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_KHR_shader_subgroup_arithmetic" + OpSourceExtension "GL_KHR_shader_subgroup_basic" + OpSourceExtension "GL_KHR_shader_subgroup_clustered" + OpName %main "main" + OpName %index "index" + OpName %FragColor "FragColor" + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 +%_ptr_Output_uint = OpTypePointer Output %uint + %FragColor = OpVariable %_ptr_Output_uint Output + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpLoad %int %index + %u = OpBitcast %uint %i + %res0 = OpGroupNonUniformSMin %uint %uint_3 Reduce %i + %res1 = OpGroupNonUniformSMax %uint %uint_3 Reduce %u + %res2 = OpGroupNonUniformUMin %uint %uint_3 Reduce %i + %res3 = OpGroupNonUniformUMax %uint %uint_3 Reduce %u + ;%res4 = OpGroupNonUniformSMax %uint %uint_3 InclusiveScan %i + ;%res5 = OpGroupNonUniformSMin %uint %uint_3 InclusiveScan %u + ;%res6 = OpGroupNonUniformUMax %uint %uint_3 ExclusiveScan %i + ;%res7 = OpGroupNonUniformUMin %uint %uint_3 ExclusiveScan %u + ;%res8 = OpGroupNonUniformSMin %uint %uint_3 ClusteredReduce %i %uint_4 + ;%res9 = OpGroupNonUniformSMax %uint %uint_3 ClusteredReduce %u %uint_4 + ;%res10 = OpGroupNonUniformUMin %uint %uint_3 ClusteredReduce %i %uint_4 + ;%res11 = OpGroupNonUniformUMax %uint %uint_3 ClusteredReduce %u %uint_4 + OpStore %FragColor %res0 + OpStore %FragColor %res1 + OpStore %FragColor %res2 + OpStore %FragColor %res3 + ;OpStore %FragColor %res4 + ;OpStore %FragColor %res5 + ;OpStore %FragColor %res6 + ;OpStore %FragColor %res7 + ;OpStore %FragColor %res8 + ;OpStore %FragColor %res9 + ;OpStore %FragColor %res10 + ;OpStore %FragColor %res11 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag new file mode 100644 index 0000000..6556c3c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag @@ -0,0 +1,80 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vIndex %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %vIndex "vIndex" + OpName %FragColor "FragColor" + OpName %i "i" + OpName %j "j" + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_int = OpTypePointer Function %int + %main = OpFunction %void None %3 + %header = OpLabel + %i = OpVariable %_ptr_Function_int Function %int_0 + %j = OpVariable %_ptr_Function_int Function + %9 = OpLoad %int %vIndex + OpSelectionMerge %switch_merge None + OpSwitch %9 %default_case 100 %default_case 0 %case_0 1 %case_1 11 %case_1 2 %case_2 3 %case_3 4 %case_4 5 %case_5 + + %case_0 = OpLabel + OpBranch %default_case + + %default_case = OpLabel + %default_case_phi = OpPhi %int %int_2 %header %int_3 %case_0 + ; Test what happens when a case block dominates access to a variable. + OpStore %j %default_case_phi + OpBranch %case_1 + + %case_1 = OpLabel + ; Test phi nodes between case labels. + %case_1_phi = OpPhi %int %int_0 %default_case %int_1 %header + OpStore %j %case_1_phi + OpBranch %case_2 + + %case_2 = OpLabel + OpBranch %switch_merge + + %case_3 = OpLabel + ; Conditionally branch to another case block. This is really dumb, but it is apparently legal. + %case_3_cond = OpSGreaterThan %bool %9 %int_3 + OpBranchConditional %case_3_cond %case_4 %switch_merge + + %case_4 = OpLabel + ; When emitted from case 3, we should *not* see fallthrough behavior. + OpBranch %case_5 + + %case_5 = OpLabel + OpStore %i %int_0 + OpBranch %switch_merge + + %switch_merge = OpLabel + %26 = OpLoad %int %i + %27 = OpConvertSToF %float %26 + %28 = OpCompositeConstruct %v4float %27 %27 %27 %27 + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/unordered-compare.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/unordered-compare.asm.frag new file mode 100644 index 0000000..4ad8fc5 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/unordered-compare.asm.frag @@ -0,0 +1,177 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 132 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %A %B %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %test_vector_ "test_vector(" + OpName %test_scalar_ "test_scalar(" + OpName %le "le" + OpName %A "A" + OpName %B "B" + OpName %leq "leq" + OpName %ge "ge" + OpName %geq "geq" + OpName %eq "eq" + OpName %neq "neq" + OpName %le_0 "le" + OpName %leq_0 "leq" + OpName %ge_0 "ge" + OpName %geq_0 "geq" + OpName %eq_0 "eq" + OpName %neq_0 "neq" + OpName %FragColor "FragColor" + OpDecorate %A Location 0 + OpDecorate %B Location 1 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %11 = OpTypeFunction %float + %bool = OpTypeBool + %v4bool = OpTypeVector %bool 4 +%_ptr_Function_v4bool = OpTypePointer Function %v4bool +%_ptr_Input_v4float = OpTypePointer Input %v4float + %A = OpVariable %_ptr_Input_v4float Input + %B = OpVariable %_ptr_Input_v4float Input + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %47 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %48 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Function_bool = OpTypePointer Function %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %128 = OpFunctionCall %v4float %test_vector_ + %129 = OpFunctionCall %float %test_scalar_ + %130 = OpCompositeConstruct %v4float %129 %129 %129 %129 + %131 = OpFAdd %v4float %128 %130 + OpStore %FragColor %131 + OpReturn + OpFunctionEnd +%test_vector_ = OpFunction %v4float None %8 + %10 = OpLabel + %le = OpVariable %_ptr_Function_v4bool Function + %leq = OpVariable %_ptr_Function_v4bool Function + %ge = OpVariable %_ptr_Function_v4bool Function + %geq = OpVariable %_ptr_Function_v4bool Function + %eq = OpVariable %_ptr_Function_v4bool Function + %neq = OpVariable %_ptr_Function_v4bool Function + %20 = OpLoad %v4float %A + %22 = OpLoad %v4float %B + %23 = OpFUnordLessThan %v4bool %20 %22 + OpStore %le %23 + %25 = OpLoad %v4float %A + %26 = OpLoad %v4float %B + %27 = OpFUnordLessThanEqual %v4bool %25 %26 + OpStore %leq %27 + %29 = OpLoad %v4float %A + %30 = OpLoad %v4float %B + %31 = OpFUnordGreaterThan %v4bool %29 %30 + OpStore %ge %31 + %33 = OpLoad %v4float %A + %34 = OpLoad %v4float %B + %35 = OpFUnordGreaterThanEqual %v4bool %33 %34 + OpStore %geq %35 + %37 = OpLoad %v4float %A + %38 = OpLoad %v4float %B + %39 = OpFUnordEqual %v4bool %37 %38 + OpStore %eq %39 + %41 = OpLoad %v4float %A + %42 = OpLoad %v4float %B + %43 = OpFUnordNotEqual %v4bool %41 %42 + OpStore %neq %43 + %44 = OpLoad %v4bool %le + %49 = OpSelect %v4float %44 %48 %47 + %50 = OpLoad %v4bool %leq + %51 = OpSelect %v4float %50 %48 %47 + %52 = OpFAdd %v4float %49 %51 + %53 = OpLoad %v4bool %ge + %54 = OpSelect %v4float %53 %48 %47 + %55 = OpFAdd %v4float %52 %54 + %56 = OpLoad %v4bool %geq + %57 = OpSelect %v4float %56 %48 %47 + %58 = OpFAdd %v4float %55 %57 + %59 = OpLoad %v4bool %eq + %60 = OpSelect %v4float %59 %48 %47 + %61 = OpFAdd %v4float %58 %60 + %62 = OpLoad %v4bool %neq + %63 = OpSelect %v4float %62 %48 %47 + %64 = OpFAdd %v4float %61 %63 + OpReturnValue %64 + OpFunctionEnd +%test_scalar_ = OpFunction %float None %11 + %13 = OpLabel + %le_0 = OpVariable %_ptr_Function_bool Function + %leq_0 = OpVariable %_ptr_Function_bool Function + %ge_0 = OpVariable %_ptr_Function_bool Function + %geq_0 = OpVariable %_ptr_Function_bool Function + %eq_0 = OpVariable %_ptr_Function_bool Function + %neq_0 = OpVariable %_ptr_Function_bool Function + %72 = OpAccessChain %_ptr_Input_float %A %uint_0 + %73 = OpLoad %float %72 + %74 = OpAccessChain %_ptr_Input_float %B %uint_0 + %75 = OpLoad %float %74 + %76 = OpFUnordLessThan %bool %73 %75 + OpStore %le_0 %76 + %78 = OpAccessChain %_ptr_Input_float %A %uint_0 + %79 = OpLoad %float %78 + %80 = OpAccessChain %_ptr_Input_float %B %uint_0 + %81 = OpLoad %float %80 + %82 = OpFUnordLessThanEqual %bool %79 %81 + OpStore %leq_0 %82 + %84 = OpAccessChain %_ptr_Input_float %A %uint_0 + %85 = OpLoad %float %84 + %86 = OpAccessChain %_ptr_Input_float %B %uint_0 + %87 = OpLoad %float %86 + %88 = OpFUnordGreaterThan %bool %85 %87 + OpStore %ge_0 %88 + %90 = OpAccessChain %_ptr_Input_float %A %uint_0 + %91 = OpLoad %float %90 + %92 = OpAccessChain %_ptr_Input_float %B %uint_0 + %93 = OpLoad %float %92 + %94 = OpFUnordGreaterThanEqual %bool %91 %93 + OpStore %geq_0 %94 + %96 = OpAccessChain %_ptr_Input_float %A %uint_0 + %97 = OpLoad %float %96 + %98 = OpAccessChain %_ptr_Input_float %B %uint_0 + %99 = OpLoad %float %98 + %100 = OpFUnordEqual %bool %97 %99 + OpStore %eq_0 %100 + %102 = OpAccessChain %_ptr_Input_float %A %uint_0 + %103 = OpLoad %float %102 + %104 = OpAccessChain %_ptr_Input_float %B %uint_0 + %105 = OpLoad %float %104 + %106 = OpFUnordNotEqual %bool %103 %105 + OpStore %neq_0 %106 + %107 = OpLoad %bool %le_0 + %108 = OpSelect %float %107 %float_1 %float_0 + %109 = OpLoad %bool %leq_0 + %110 = OpSelect %float %109 %float_1 %float_0 + %111 = OpFAdd %float %108 %110 + %112 = OpLoad %bool %ge_0 + %113 = OpSelect %float %112 %float_1 %float_0 + %114 = OpFAdd %float %111 %113 + %115 = OpLoad %bool %geq_0 + %116 = OpSelect %float %115 %float_1 %float_0 + %117 = OpFAdd %float %114 %116 + %118 = OpLoad %bool %eq_0 + %119 = OpSelect %float %118 %float_1 %float_0 + %120 = OpFAdd %float %117 %119 + %121 = OpLoad %bool %neq_0 + %122 = OpSelect %float %121 %float_1 %float_0 + %123 = OpFAdd %float %120 %122 + OpReturnValue %123 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag new file mode 100644 index 0000000..22c4efc --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag @@ -0,0 +1,42 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vFloat + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vFloat "vFloat" + OpName %undef "undef" + OpDecorate %FragColor Location 0 + OpDecorate %vFloat Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vFloat = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 +%_ptr_Private_v4float = OpTypePointer Private %v4float + %undef = OpUndef %v4float + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_Private_float = OpTypePointer Private %float + %uint_3 = OpConstant %uint 3 +%_ptr_Input_float = OpTypePointer Input %float + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %v4float %vFloat + %26 = OpVectorShuffle %v4float %13 %undef 4 1 0xffffffff 3 + %27 = OpVectorShuffle %v4float %13 %13 2 1 0xffffffff 3 + %28 = OpFAdd %v4float %26 %27 + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/temporary.zero-initialize.asm.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/temporary.zero-initialize.asm.frag new file mode 100644 index 0000000..eccff08 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/temporary.zero-initialize.asm.frag @@ -0,0 +1,93 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 65 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA RelaxedPrecision + OpDecorate %vA Flat + OpDecorate %vA Location 0 + OpDecorate %25 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %vB RelaxedPrecision + OpDecorate %vB Flat + OpDecorate %vB Location 1 + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %64 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_int = OpTypePointer Input %int + %vA = OpVariable %_ptr_Input_int Input + %bool = OpTypeBool + %int_20 = OpConstant %int 20 + %int_50 = OpConstant %int 50 + %vB = OpVariable %_ptr_Input_int Input + %int_40 = OpConstant %int 40 + %int_60 = OpConstant %int 60 + %int_10 = OpConstant %int 10 + %float_1 = OpConstant %float 1 + %63 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %FragColor %11 + OpBranch %17 + %17 = OpLabel + %60 = OpPhi %int %int_0 %5 %58 %20 + %57 = OpPhi %int %int_0 %5 %56 %20 + %25 = OpLoad %int %vA + %27 = OpSLessThan %bool %57 %25 + OpLoopMerge %19 %20 None + OpBranchConditional %27 %18 %19 + %18 = OpLabel + %30 = OpIAdd %int %25 %57 + %32 = OpIEqual %bool %30 %int_20 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %36 + %33 = OpLabel + OpBranch %34 + %36 = OpLabel + %38 = OpLoad %int %vB + %40 = OpIAdd %int %38 %57 + %42 = OpIEqual %bool %40 %int_40 + %64 = OpSelect %int %42 %int_60 %60 + OpBranch %34 + %34 = OpLabel + %58 = OpPhi %int %int_50 %33 %64 %36 + %49 = OpIAdd %int %58 %int_10 + %51 = OpLoad %v4float %FragColor + %53 = OpFAdd %v4float %51 %63 + OpStore %FragColor %53 + OpBranch %20 + %20 = OpLabel + %56 = OpIAdd %int %57 %49 + OpBranch %17 + %19 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert new file mode 100644 index 0000000..94a883c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/vert/complex-link-by-name.asm.vert @@ -0,0 +1,119 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 59 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %output_location_0 %output_location_2 %output_location_3 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "Struct_vec4" + OpMemberName %Foo 0 "m0" + OpName %c "c" + OpName %Foo_0 "Struct_vec4" + OpMemberName %Foo_0 0 "m0" + OpName %Bar "Struct_vec4" + OpMemberName %Bar 0 "m0" + OpName %UBO "UBO" + OpMemberName %UBO 0 "m0" + OpMemberName %UBO 1 "m1" + OpName %ubo_binding_0 "ubo_binding_0" + OpName %Bar_0 "Struct_vec4" + OpMemberName %Bar_0 0 "m0" + OpName %b "b" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpName %VertexOut "VertexOut" + OpMemberName %VertexOut 0 "m0" + OpMemberName %VertexOut 1 "m1" + OpName %output_location_0 "output_location_0" + OpName %output_location_2 "output_location_2" + OpName %output_location_3 "output_location_3" + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %Bar 0 Offset 0 + OpMemberDecorate %UBO 0 Offset 0 + OpMemberDecorate %UBO 1 Offset 16 + OpDecorate %UBO Block + OpDecorate %ubo_binding_0 DescriptorSet 0 + OpDecorate %ubo_binding_0 Binding 0 + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpDecorate %VertexOut Block + OpDecorate %output_location_0 Location 0 + OpDecorate %output_location_2 Location 2 + OpDecorate %output_location_3 Location 3 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Foo = OpTypeStruct %v4float +%_ptr_Function_Foo = OpTypePointer Function %Foo + %Foo_0 = OpTypeStruct %v4float + %Bar = OpTypeStruct %v4float + %UBO = OpTypeStruct %Foo_0 %Bar +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO +%ubo_binding_0 = OpVariable %_ptr_Uniform_UBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %Bar_0 = OpTypeStruct %v4float +%_ptr_Function_Bar_0 = OpTypePointer Function %Bar_0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_Bar = OpTypePointer Uniform %Bar + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output +%_ptr_Output_v4float = OpTypePointer Output %v4float + %VertexOut = OpTypeStruct %Foo %Bar_0 +%_ptr_Output_VertexOut = OpTypePointer Output %VertexOut +%output_location_0 = OpVariable %_ptr_Output_VertexOut Output +%_ptr_Output_Foo = OpTypePointer Output %Foo +%_ptr_Output_Bar_0 = OpTypePointer Output %Bar_0 +%output_location_2 = OpVariable %_ptr_Output_Foo Output +%output_location_3 = OpVariable %_ptr_Output_Bar_0 Output + %main = OpFunction %void None %3 + %5 = OpLabel + %c = OpVariable %_ptr_Function_Foo Function + %b = OpVariable %_ptr_Function_Bar_0 Function + %19 = OpAccessChain %_ptr_Uniform_Foo_0 %ubo_binding_0 %int_0 + %20 = OpLoad %Foo_0 %19 + %21 = OpCompositeExtract %v4float %20 0 + %23 = OpAccessChain %_ptr_Function_v4float %c %int_0 + OpStore %23 %21 + %29 = OpAccessChain %_ptr_Uniform_Bar %ubo_binding_0 %int_1 + %30 = OpLoad %Bar %29 + %31 = OpCompositeExtract %v4float %30 0 + %32 = OpAccessChain %_ptr_Function_v4float %b %int_0 + OpStore %32 %31 + %39 = OpAccessChain %_ptr_Function_v4float %c %int_0 + %40 = OpLoad %v4float %39 + %41 = OpAccessChain %_ptr_Function_v4float %b %int_0 + %42 = OpLoad %v4float %41 + %43 = OpFAdd %v4float %40 %42 + %45 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %45 %43 + %49 = OpLoad %Foo %c + %51 = OpAccessChain %_ptr_Output_Foo %output_location_0 %int_0 + OpStore %51 %49 + %52 = OpLoad %Bar_0 %b + %54 = OpAccessChain %_ptr_Output_Bar_0 %output_location_0 %int_1 + OpStore %54 %52 + %56 = OpLoad %Foo %c + OpStore %output_location_2 %56 + %58 = OpLoad %Bar_0 %b + OpStore %output_location_3 %58 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/asm/vert/empty-struct-composite.asm.vert b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/vert/empty-struct-composite.asm.vert new file mode 100644 index 0000000..038ecaa --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/asm/vert/empty-struct-composite.asm.vert @@ -0,0 +1,36 @@ +; SPIR-V +; Version: 1.1 +; Generator: Google rspirv; 0 +; Bound: 17 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "main" + OpName %Test "Test" + OpName %t "t" + OpName %retvar "retvar" + OpName %main "main" + OpName %retvar_0 "retvar" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %Test = OpTypeStruct +%_ptr_Function_Test = OpTypePointer Function %Test +%_ptr_Function_void = OpTypePointer Function %void + %2 = OpFunction %void None %6 + %7 = OpLabel + %t = OpVariable %_ptr_Function_Test Function + %retvar = OpVariable %_ptr_Function_void Function + OpBranch %4 + %4 = OpLabel + %13 = OpCompositeConstruct %Test + OpStore %t %13 + OpReturn + OpFunctionEnd + %main = OpFunction %void None %6 + %15 = OpLabel + %retvar_0 = OpVariable %_ptr_Function_void Function + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/comp/bitfield.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/comp/bitfield.comp new file mode 100644 index 0000000..a2ef9aa --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/comp/bitfield.comp @@ -0,0 +1,44 @@ +#version 310 es + +void main() +{ + int signed_value = 0; + uint unsigned_value = 0u; + + ivec3 signed_values = ivec3(0); + uvec3 unsigned_values = uvec3(0u); + + { + int s = bitfieldExtract(signed_value, 5, 20); + uint u = bitfieldExtract(unsigned_value, 6, 21); + + s = bitfieldInsert(s, 40, 5, 4); + u = bitfieldInsert(u, 60u, 5, 4); + + u = bitfieldReverse(u); + s = bitfieldReverse(s); + + int v0 = bitCount(u); + int v1 = bitCount(s); + + int v2 = findMSB(u); + int v3 = findLSB(s); + } + + { + ivec3 s = bitfieldExtract(signed_values, 5, 20); + uvec3 u = bitfieldExtract(unsigned_values, 6, 21); + + s = bitfieldInsert(s, ivec3(40), 5, 4); + u = bitfieldInsert(u, uvec3(60u), 5, 4); + + u = bitfieldReverse(u); + s = bitfieldReverse(s); + + ivec3 v0 = bitCount(u); + ivec3 v1 = bitCount(s); + + ivec3 v2 = findMSB(u); + ivec3 v3 = findLSB(s); + } +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/comp/glsl.std450.fxconly.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/comp/glsl.std450.fxconly.comp new file mode 100644 index 0000000..83b714b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/comp/glsl.std450.fxconly.comp @@ -0,0 +1,130 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float res; + int ires; + uint ures; + + vec4 f32; + ivec4 s32; + uvec4 u32; + + mat2 m2; + mat3 m3; + mat4 m4; +}; + +void main() +{ + float tmp; + vec2 v2; + vec3 v3; + vec4 v4; + int itmp; + + res = round(f32.x); + //res = roundEven(f32.x); + res = trunc(f32.x); + res = abs(f32.x); + ires = abs(s32.x); + res = sign(f32.x); + ires = sign(s32.x); + res = floor(f32.x); + res = ceil(f32.x); + res = fract(f32.x); + res = radians(f32.x); + res = degrees(f32.x); + res = sin(f32.x); + res = cos(f32.x); + res = tan(f32.x); + res = asin(f32.x); + res = acos(f32.x); + res = atan(f32.x); + res = sinh(f32.x); + res = cosh(f32.x); + res = tanh(f32.x); + //res = asinh(f32.x); + //res = acosh(f32.x); + //res = atanh(f32.x); + res = atan(f32.x, f32.y); + res = pow(f32.x, f32.y); + res = exp(f32.x); + res = log(f32.x); + res = exp2(f32.x); + res = log2(f32.x); + res = sqrt(f32.x); + res = inversesqrt(f32.x); + + res = length(f32.x); + res = distance(f32.x, f32.y); + res = normalize(f32.x); + res = faceforward(f32.x, f32.y, f32.z); + res = reflect(f32.x, f32.y); + res = refract(f32.x, f32.y, f32.z); + + res = length(f32.xy); + res = distance(f32.xy, f32.zw); + v2 = normalize(f32.xy); + v2 = faceforward(f32.xy, f32.yz, f32.zw); + v2 = reflect(f32.xy, f32.zw); + v2 = refract(f32.xy, f32.yz, f32.w); + + v3 = cross(f32.xyz, f32.yzw); + + res = determinant(m2); + res = determinant(m3); + res = determinant(m4); + m2 = inverse(m2); + m3 = inverse(m3); + m4 = inverse(m4); + + res = modf(f32.x, tmp); + // ModfStruct + + res = min(f32.x, f32.y); + ures = min(u32.x, u32.y); + ires = min(s32.x, s32.y); + res = max(f32.x, f32.y); + ures = max(u32.x, u32.y); + ires = max(s32.x, s32.y); + + res = clamp(f32.x, f32.y, f32.z); + ures = clamp(u32.x, u32.y, u32.z); + ires = clamp(s32.x, s32.y, s32.z); + + res = mix(f32.x, f32.y, f32.z); + res = step(f32.x, f32.y); + res = smoothstep(f32.x, f32.y, f32.z); + res = fma(f32.x, f32.y, f32.z); + + res = frexp(f32.x, itmp); + + // FrexpStruct + res = ldexp(f32.x, itmp); + + ures = packSnorm4x8(f32); + ures = packUnorm4x8(f32); + ures = packSnorm2x16(f32.xy); + ures = packUnorm2x16(f32.xy); + ures = packHalf2x16(f32.xy); + // packDouble2x32 + + v2 = unpackSnorm2x16(u32.x); + v2 = unpackUnorm2x16(u32.x); + v2 = unpackHalf2x16(u32.x); + v4 = unpackSnorm4x8(u32.x); + v4 = unpackUnorm4x8(u32.x); + // unpackDouble2x32 + + s32 = findLSB(s32); + s32 = findLSB(u32); + s32 = findMSB(s32); + s32 = findMSB(u32); + + // interpolateAtSample + // interpolateAtOffset + + // NMin, NMax, NClamp +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/comp/illegal-struct-name.asm.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/comp/illegal-struct-name.asm.comp new file mode 100644 index 0000000..f7a8787 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/comp/illegal-struct-name.asm.comp @@ -0,0 +1,62 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 31 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "Foo" + OpMemberName %Foo 0 "abs" + OpName %f "f" + OpName %Foo_0 "Foo" + OpMemberName %Foo_0 0 "abs" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "foo" + OpMemberName %SSBO 1 "foo2" + OpName %_ "" + OpName %linear "abs" + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Foo = OpTypeStruct %float +%_ptr_Function_Foo = OpTypePointer Function %Foo + %Foo_0 = OpTypeStruct %float + %SSBO = OpTypeStruct %Foo_0 %Foo_0 +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Function_int = OpTypePointer Function %int + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %main = OpFunction %void None %3 + %5 = OpLabel + %f = OpVariable %_ptr_Function_Foo Function + %linear = OpVariable %_ptr_Function_int Function + %17 = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_0 + %18 = OpLoad %Foo_0 %17 + %19 = OpCompositeExtract %float %18 0 + %21 = OpAccessChain %_ptr_Function_float %f %int_0 + OpStore %21 %19 + OpStore %linear %int_10 + %26 = OpLoad %Foo %f + %27 = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_1 + %28 = OpCompositeExtract %float %26 0 + %30 = OpAccessChain %_ptr_Uniform_float %27 %int_0 + OpStore %30 %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/comp/subgroups.invalid.nofxc.sm60.comp b/third_party/spirv-cross/shaders-hlsl-no-opt/comp/subgroups.invalid.nofxc.sm60.comp new file mode 100644 index 0000000..81135e2 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/comp/subgroups.invalid.nofxc.sm60.comp @@ -0,0 +1,131 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_ballot : require +#extension GL_KHR_shader_subgroup_vote : require +#extension GL_KHR_shader_subgroup_shuffle : require +#extension GL_KHR_shader_subgroup_shuffle_relative : require +#extension GL_KHR_shader_subgroup_arithmetic : require +#extension GL_KHR_shader_subgroup_clustered : require +#extension GL_KHR_shader_subgroup_quad : require +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float FragColor; +}; + +void main() +{ + // basic + //FragColor = float(gl_NumSubgroups); + //FragColor = float(gl_SubgroupID); + FragColor = float(gl_SubgroupSize); + FragColor = float(gl_SubgroupInvocationID); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierShared(); + subgroupMemoryBarrierImage(); + bool elected = subgroupElect(); + + // ballot + FragColor = float(gl_SubgroupEqMask); + FragColor = float(gl_SubgroupGeMask); + FragColor = float(gl_SubgroupGtMask); + FragColor = float(gl_SubgroupLeMask); + FragColor = float(gl_SubgroupLtMask); + vec4 broadcasted = subgroupBroadcast(vec4(10.0), 8u); + vec3 first = subgroupBroadcastFirst(vec3(20.0)); + uvec4 ballot_value = subgroupBallot(true); + //bool inverse_ballot_value = subgroupInverseBallot(ballot_value); + //bool bit_extracted = subgroupBallotBitExtract(uvec4(10u), 8u); + uint bit_count = subgroupBallotBitCount(ballot_value); + //uint inclusive_bit_count = subgroupBallotInclusiveBitCount(ballot_value); + //uint exclusive_bit_count = subgroupBallotExclusiveBitCount(ballot_value); + //uint lsb = subgroupBallotFindLSB(ballot_value); + //uint msb = subgroupBallotFindMSB(ballot_value); + + // shuffle + //uint shuffled = subgroupShuffle(10u, 8u); + //uint shuffled_xor = subgroupShuffleXor(30u, 8u); + + // shuffle relative + //uint shuffled_up = subgroupShuffleUp(20u, 4u); + //uint shuffled_down = subgroupShuffleDown(20u, 4u); + + // vote + bool has_all = subgroupAll(true); + bool has_any = subgroupAny(true); + bool has_equal = subgroupAllEqual(true); + + // arithmetic + vec4 added = subgroupAdd(vec4(20.0)); + ivec4 iadded = subgroupAdd(ivec4(20)); + vec4 multiplied = subgroupMul(vec4(20.0)); + ivec4 imultiplied = subgroupMul(ivec4(20)); + vec4 lo = subgroupMin(vec4(20.0)); + vec4 hi = subgroupMax(vec4(20.0)); + ivec4 slo = subgroupMin(ivec4(20)); + ivec4 shi = subgroupMax(ivec4(20)); + uvec4 ulo = subgroupMin(uvec4(20)); + uvec4 uhi = subgroupMax(uvec4(20)); + uvec4 anded = subgroupAnd(ballot_value); + uvec4 ored = subgroupOr(ballot_value); + uvec4 xored = subgroupXor(ballot_value); + + added = subgroupInclusiveAdd(added); + iadded = subgroupInclusiveAdd(iadded); + multiplied = subgroupInclusiveMul(multiplied); + imultiplied = subgroupInclusiveMul(imultiplied); +#if 0 + lo = subgroupInclusiveMin(lo); + hi = subgroupInclusiveMax(hi); + slo = subgroupInclusiveMin(slo); + shi = subgroupInclusiveMax(shi); + ulo = subgroupInclusiveMin(ulo); + uhi = subgroupInclusiveMax(uhi); + anded = subgroupInclusiveAnd(anded); + ored = subgroupInclusiveOr(ored); + xored = subgroupInclusiveXor(ored); + added = subgroupExclusiveAdd(lo); +#endif + + added = subgroupExclusiveAdd(multiplied); + multiplied = subgroupExclusiveMul(multiplied); + iadded = subgroupExclusiveAdd(imultiplied); + imultiplied = subgroupExclusiveMul(imultiplied); +#if 0 + lo = subgroupExclusiveMin(lo); + hi = subgroupExclusiveMax(hi); + ulo = subgroupExclusiveMin(ulo); + uhi = subgroupExclusiveMax(uhi); + slo = subgroupExclusiveMin(slo); + shi = subgroupExclusiveMax(shi); + anded = subgroupExclusiveAnd(anded); + ored = subgroupExclusiveOr(ored); + xored = subgroupExclusiveXor(ored); +#endif + +#if 0 + // clustered + added = subgroupClusteredAdd(added, 4u); + multiplied = subgroupClusteredMul(multiplied, 4u); + iadded = subgroupClusteredAdd(iadded, 4u); + imultiplied = subgroupClusteredMul(imultiplied, 4u); + lo = subgroupClusteredMin(lo, 4u); + hi = subgroupClusteredMax(hi, 4u); + ulo = subgroupClusteredMin(ulo, 4u); + uhi = subgroupClusteredMax(uhi, 4u); + slo = subgroupClusteredMin(slo, 4u); + shi = subgroupClusteredMax(shi, 4u); + anded = subgroupClusteredAnd(anded, 4u); + ored = subgroupClusteredOr(ored, 4u); + xored = subgroupClusteredXor(xored, 4u); +#endif + + // quad + vec4 swap_horiz = subgroupQuadSwapHorizontal(vec4(20.0)); + vec4 swap_vertical = subgroupQuadSwapVertical(vec4(20.0)); + vec4 swap_diagonal = subgroupQuadSwapDiagonal(vec4(20.0)); + vec4 quad_broadcast = subgroupQuadBroadcast(vec4(20.0), 3u); +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/cbuffer-packing-straddle.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/cbuffer-packing-straddle.frag new file mode 100644 index 0000000..4f22da5 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/cbuffer-packing-straddle.frag @@ -0,0 +1,50 @@ +#version 450 + +layout(set = 0, binding = 0) uniform UBO +{ + vec4 a[2]; // 0 + vec4 b; // 32 + vec4 c; // 48 + mat4x4 d; // 64 + + float e; // 128 + vec2 f; // 136 + + float g; // 144 + vec2 h; // 152 + + float i; // 160 + vec2 j; // 168 + + float k; + vec2 l; + + float m; + float n; + float o; + + vec4 p; + vec4 q; + vec3 r; + vec4 s; + vec4 t; + vec4 u; + float v; + float w; + float x; + float y; + float z; + float aa; + float ab; + float ac; + float ad; + float ae; + vec4 ef; +}; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = a[1]; +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/constant-buffer-array.invalid.sm51.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/constant-buffer-array.invalid.sm51.frag new file mode 100644 index 0000000..d60002a --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/constant-buffer-array.invalid.sm51.frag @@ -0,0 +1,32 @@ +#version 450 + +layout(std140, binding = 4) uniform CBO +{ + vec4 a; + vec4 b; + vec4 c; + vec4 d; +} cbo[2][4]; + +layout(std430, push_constant) uniform PushMe +{ + vec4 a; + vec4 b; + vec4 c; + vec4 d; +} push; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = cbo[1][2].a; + FragColor += cbo[1][2].b; + FragColor += cbo[1][2].c; + FragColor += cbo[1][2].d; + FragColor += push.a; + FragColor += push.b; + FragColor += push.c; + FragColor += push.d; +} + diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/fp16.invalid.desktop.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/fp16.invalid.desktop.frag new file mode 100644 index 0000000..4f92e20 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/fp16.invalid.desktop.frag @@ -0,0 +1,156 @@ +#version 450 +#extension GL_AMD_gpu_shader_half_float : require + +layout(location = 0) in float16_t v1; +layout(location = 1) in f16vec2 v2; +layout(location = 2) in f16vec3 v3; +layout(location = 3) in f16vec4 v4; + +layout(location = 0) out float o1; +layout(location = 1) out vec2 o2; +layout(location = 2) out vec3 o3; +layout(location = 3) out vec4 o4; + +#if 0 +// Doesn't work on glslang yet. +f16mat2 test_mat2(f16vec2 a, f16vec2 b, f16vec2 c, f16vec2 d) +{ + return f16mat2(a, b) * f16mat2(c, d); +} + +f16mat3 test_mat3(f16vec3 a, f16vec3 b, f16vec3 c, f16vec3 d, f16vec3 e, f16vec3 f) +{ + return f16mat3(a, b, c) * f16mat3(d, e, f); +} +#endif + +void test_constants() +{ + float16_t a = 1.0hf; + float16_t b = 1.5hf; + float16_t c = -1.5hf; // Negatives + float16_t d = (0.0hf / 0.0hf); // NaN + float16_t e = (1.0hf / 0.0hf); // +Inf + float16_t f = (-1.0hf / 0.0hf); // -Inf + float16_t g = 1014.0hf; // Large. + float16_t h = 0.000001hf; // Denormal +} + +float16_t test_result() +{ + return 1.0hf; +} + +void test_conversions() +{ + float16_t one = test_result(); + int a = int(one); + uint b = uint(one); + bool c = bool(one); + float d = float(one); + double e = double(one); + float16_t a2 = float16_t(a); + float16_t b2 = float16_t(b); + float16_t c2 = float16_t(c); + float16_t d2 = float16_t(d); + float16_t e2 = float16_t(e); +} + +void test_builtins() +{ + f16vec4 res; + res = radians(v4); + res = degrees(v4); + res = sin(v4); + res = cos(v4); + res = tan(v4); + res = asin(v4); + res = atan(v4, v3.xyzz); + res = atan(v4); + res = sinh(v4); + res = cosh(v4); + res = tanh(v4); + //res = asinh(v4); + //res = acosh(v4); + //res = atanh(v4); + res = pow(v4, v4); + res = exp(v4); + res = log(v4); + res = exp2(v4); + res = log2(v4); + res = sqrt(v4); + res = inversesqrt(v4); + res = abs(v4); + res = sign(v4); + res = floor(v4); + res = trunc(v4); + res = round(v4); + //res = roundEven(v4); + res = ceil(v4); + res = fract(v4); + res = mod(v4, v4); + f16vec4 tmp; + res = modf(v4, tmp); + res = min(v4, v4); + res = max(v4, v4); + res = clamp(v4, v4, v4); + res = mix(v4, v4, v4); + res = mix(v4, v4, lessThan(v4, v4)); + res = step(v4, v4); + res = smoothstep(v4, v4, v4); + + bvec4 btmp = isnan(v4); + btmp = isinf(v4); + res = fma(v4, v4, v4); + + //ivec4 itmp; + //res = frexp(v4, itmp); + //res = ldexp(res, itmp); + + uint pack0 = packFloat2x16(v4.xy); + uint pack1 = packFloat2x16(v4.zw); + res = f16vec4(unpackFloat2x16(pack0), unpackFloat2x16(pack1)); + + float16_t t0 = length(v4); + t0 = distance(v4, v4); + t0 = dot(v4, v4); + f16vec3 res3 = cross(v3, v3); + res = normalize(v4); + res = faceforward(v4, v4, v4); + res = reflect(v4, v4); + res = refract(v4, v4, v1); + + btmp = lessThan(v4, v4); + btmp = lessThanEqual(v4, v4); + btmp = greaterThan(v4, v4); + btmp = greaterThanEqual(v4, v4); + btmp = equal(v4, v4); + btmp = notEqual(v4, v4); + + res = dFdx(v4); + res = dFdy(v4); + res = dFdxFine(v4); + res = dFdyFine(v4); + res = dFdxCoarse(v4); + res = dFdyCoarse(v4); + res = fwidth(v4); + res = fwidthFine(v4); + res = fwidthCoarse(v4); + + //res = interpolateAtCentroid(v4); + //res = interpolateAtSample(v4, 0); + //res = interpolateAtOffset(v4, f16vec2(0.1hf)); +} + +void main() +{ + // Basic matrix tests. +#if 0 + f16mat2 m0 = test_mat2(v2, v2, v3.xy, v3.xy); + f16mat3 m1 = test_mat3(v3, v3, v3, v4.xyz, v4.xyz, v4.yzw); +#endif + + test_constants(); + test_conversions(); + test_builtins(); +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/frag-coord.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/frag-coord.frag new file mode 100644 index 0000000..e688659 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/frag-coord.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location = 0) out vec3 FragColor; + +void main() +{ + FragColor = gl_FragCoord.xyz / gl_FragCoord.w; +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/native-16bit-types.fxconly.nofxc.sm62.native-16bit.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/native-16bit-types.fxconly.nofxc.sm62.native-16bit.frag new file mode 100644 index 0000000..92e6621 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/native-16bit-types.fxconly.nofxc.sm62.native-16bit.frag @@ -0,0 +1,72 @@ +#version 450 +#extension GL_EXT_shader_explicit_arithmetic_types : require + +layout(location = 0) out f16vec4 Output; +layout(location = 0) in f16vec4 Input; +layout(location = 1) out i16vec4 OutputI; +layout(location = 1) flat in i16vec4 InputI; +layout(location = 2) out u16vec4 OutputU; +layout(location = 2) flat in u16vec4 InputU; + +layout(set = 0, binding = 0) buffer Buf +{ + float16_t foo0[4]; + int16_t foo1[4]; + uint16_t foo2[4]; + + f16vec4 foo3[4]; + i16vec4 foo4[4]; + u16vec4 foo5[4]; + + f16mat2x3 foo6[4]; + layout(row_major) f16mat2x3 foo7[4]; +}; + +void main() +{ + int index = int(gl_FragCoord.x); + Output = Input + float16_t(20.0); + OutputI = InputI + int16_t(-40); + OutputU = InputU + uint16_t(20); + + // Load 16-bit scalar. + Output += foo0[index]; + OutputI += foo1[index]; + OutputU += foo2[index]; + + // Load 16-bit vector. + Output += foo3[index]; + OutputI += foo4[index]; + OutputU += foo5[index]; + + // Load 16-bit vector from ColMajor matrix. + Output += foo6[index][1].xyzz; + + // Load 16-bit vector from RowMajor matrix. + Output += foo7[index][1].xyzz; + + // Load 16-bit matrix from ColMajor. + f16mat2x3 m0 = foo6[index]; + // Load 16-bit matrix from RowMajor. + f16mat2x3 m1 = foo7[index]; + + // Store 16-bit scalar + foo0[index] = Output.x; + foo1[index] = OutputI.y; + foo2[index] = OutputU.z; + + // Store 16-bit vector + foo3[index] = Output; + foo4[index] = OutputI; + foo5[index] = OutputU; + + // Store 16-bit vector to ColMajor matrix. + foo6[index][1] = Output.xyz; + // Store 16-bit vector to RowMajor matrix. + foo7[index][1] = Output.xyz; + + // Store 16-bit matrix to ColMajor. + foo6[index] = f16mat2x3(Output.xyz, Output.wzy); + // Store 16-bit matrix to RowMajor. + foo7[index] = f16mat2x3(Output.xyz, Output.wzy); +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/pixel-interlock-simple-callstack.sm51.fxconly.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/pixel-interlock-simple-callstack.sm51.fxconly.frag new file mode 100644 index 0000000..59079fe --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/pixel-interlock-simple-callstack.sm51.fxconly.frag @@ -0,0 +1,31 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(set = 0, binding = 0, std430) buffer SSBO0 +{ + uint values0[]; +}; + +layout(set = 0, binding = 1, std430) buffer SSBO1 +{ + uint values1[]; +}; + +void callee2() +{ + values1[int(gl_FragCoord.x)] += 1; +} + +void callee() +{ + values0[int(gl_FragCoord.x)] += 1; + callee2(); +} + +void main() +{ + beginInvocationInterlockARB(); + callee(); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/spec-constant.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/spec-constant.frag new file mode 100644 index 0000000..a6c8d94 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/spec-constant.frag @@ -0,0 +1,80 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(constant_id = 1) const float a = 1.0; +layout(constant_id = 2) const float b = 2.0; +layout(constant_id = 3) const int c = 3; +layout(constant_id = 4) const int d = 4; +layout(constant_id = 5) const uint e = 5u; +layout(constant_id = 6) const uint f = 6u; +layout(constant_id = 7) const bool g = false; +layout(constant_id = 8) const bool h = true; +// glslang doesn't seem to support partial spec constants or composites yet, so only test the basics. + +struct Foo +{ + float elems[d + 2]; +}; + +void main() +{ + float t0 = a; + float t1 = b; + + uint c0 = uint(c); // OpIAdd with different types. + // FConvert, float-to-double. + int c1 = -c; // SNegate + int c2 = ~c; // OpNot + int c3 = c + d; // OpIAdd + int c4 = c - d; // OpISub + int c5 = c * d; // OpIMul + int c6 = c / d; // OpSDiv + uint c7 = e / f; // OpUDiv + int c8 = c % d; // OpSMod + uint c9 = e % f; // OpUMod + // TODO: OpSRem, any way to access this in GLSL? + int c10 = c >> d; // OpShiftRightArithmetic + uint c11 = e >> f; // OpShiftRightLogical + int c12 = c << d; // OpShiftLeftLogical + int c13 = c | d; // OpBitwiseOr + int c14 = c ^ d; // OpBitwiseXor + int c15 = c & d; // OpBitwiseAnd + // VectorShuffle, CompositeExtract, CompositeInsert, not testable atm. + bool c16 = g || h; // OpLogicalOr + bool c17 = g && h; // OpLogicalAnd + bool c18 = !g; // OpLogicalNot + bool c19 = g == h; // OpLogicalEqual + bool c20 = g != h; // OpLogicalNotEqual + // OpSelect not testable atm. + bool c21 = c == d; // OpIEqual + bool c22 = c != d; // OpINotEqual + bool c23 = c < d; // OpSLessThan + bool c24 = e < f; // OpULessThan + bool c25 = c > d; // OpSGreaterThan + bool c26 = e > f; // OpUGreaterThan + bool c27 = c <= d; // OpSLessThanEqual + bool c28 = e <= f; // OpULessThanEqual + bool c29 = c >= d; // OpSGreaterThanEqual + bool c30 = e >= f; // OpUGreaterThanEqual + // OpQuantizeToF16 not testable atm. + + int c31 = c8 + c3; + + int c32 = int(e); // OpIAdd with different types. + bool c33 = bool(c); // int -> bool + bool c34 = bool(e); // uint -> bool + int c35 = int(g); // bool -> int + uint c36 = uint(g); // bool -> uint + float c37 = float(g); // bool -> float + + // Flexible sized arrays with spec constants and spec constant ops. + float vec0[c + 3][8]; + float vec1[c + 2]; + vec0[0][0] = 10.0; + vec1[0] = 20.0; + + Foo foo; + foo.elems[c] = 10.0; + FragColor = vec4(t0 + t1) + vec0[0][0] + vec1[0] + foo.elems[c]; +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/ubo-offset-out-of-order.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/ubo-offset-out-of-order.frag new file mode 100644 index 0000000..7776052 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/ubo-offset-out-of-order.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +layout(set = 0, binding = 0) uniform UBO +{ + layout(offset = 16) mat4 m; + layout(offset = 0) vec4 v; +}; + +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = m * vColor + v; +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/frag/variables.zero-initialize.frag b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/variables.zero-initialize.frag new file mode 100644 index 0000000..41da800 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/frag/variables.zero-initialize.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +int uninit_int; +ivec4 uninit_vector; +mat4 uninit_matrix; + +struct Foo { int a; }; +Foo uninit_foo; + +void main() +{ + int uninit_function_int; + if (vColor.x > 10.0) + uninit_function_int = 10; + else + uninit_function_int = 20; + FragColor = vColor; +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/vert/flatten-matrix-input.flatten-matrix-vertex-input.vert b/third_party/spirv-cross/shaders-hlsl-no-opt/vert/flatten-matrix-input.flatten-matrix-vertex-input.vert new file mode 100644 index 0000000..b494806 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/vert/flatten-matrix-input.flatten-matrix-vertex-input.vert @@ -0,0 +1,13 @@ +#version 450 + +layout(location = 0) in mat4 m4; +layout(location = 4) in mat3 m3; +layout(location = 7) in mat2 m2; +layout(location = 9) in vec4 v; + +void main() +{ + gl_Position = m4 * v; + gl_Position.xyz += m3 * v.xyz; + gl_Position.xy += m2 * v.xy; +} diff --git a/third_party/spirv-cross/shaders-hlsl-no-opt/vert/pass-array-by-value.vert b/third_party/spirv-cross/shaders-hlsl-no-opt/vert/pass-array-by-value.vert new file mode 100644 index 0000000..2c142a7 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl-no-opt/vert/pass-array-by-value.vert @@ -0,0 +1,26 @@ +#version 310 es + +layout(location = 0) in int Index1; +layout(location = 1) in int Index2; + +vec4 consume_constant_arrays2(const vec4 positions[4], const vec4 positions2[4]) +{ + return positions[Index1] + positions2[Index2]; +} + +vec4 consume_constant_arrays(const vec4 positions[4], const vec4 positions2[4]) +{ + return consume_constant_arrays2(positions, positions2); +} + +const vec4 LUT1[] = vec4[](vec4(0.0), vec4(1.0), vec4(2.0), vec4(3.0)); + +void main() +{ + vec4 LUT2[4]; + LUT2[0] = vec4(10.0); + LUT2[1] = vec4(11.0); + LUT2[2] = vec4(12.0); + LUT2[3] = vec4(13.0); + gl_Position = consume_constant_arrays(LUT1, LUT2); +} diff --git a/third_party/spirv-cross/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp b/third_party/spirv-cross/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp new file mode 100644 index 0000000..6e6ced1 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/comp/access-chain-invalidate.asm.comp @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 7 +; Bound: 41 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "index" + OpMemberName %SSBO 1 "array" + OpName %_ "" + OpDecorate %_arr_uint_uint_64 ArrayStride 4 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_64 = OpConstant %uint 64 +%_arr_uint_uint_64 = OpTypeArray %uint %uint_64 + %SSBO = OpTypeStruct %uint %_arr_uint_uint_64 +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 + %19 = OpLoad %uint %18 + %20 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 %19 + %21 = OpLoad %uint %20 + OpBranch %24 + %24 = OpLabel + %40 = OpPhi %uint %uint_0 %5 %35 %25 + %31 = OpULessThan %bool %40 %uint_64 + OpLoopMerge %26 %25 None + OpBranchConditional %31 %25 %26 + %25 = OpLabel + %33 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 %40 + OpStore %33 %uint_0 + %35 = OpIAdd %uint %40 %int_1 + OpBranch %24 + %26 = OpLabel + %37 = OpLoad %uint %18 + %39 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 %37 + OpStore %39 %21 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/shaders-hlsl/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..f716cbb --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "main" %15 + OpExecutionMode %3 LocalSize 4 1 1 + OpName %3 "main" + OpName %8 "u0" + OpName %9 "u0_counters" + OpMemberName %9 0 "c" + OpName %11 "u0_counter" + OpName %15 "vThreadID" + OpName %19 "r0" + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 1 + OpDecorate %11 Binding 1 + OpDecorate %15 BuiltIn GlobalInvocationId + %1 = OpTypeVoid + %2 = OpTypeFunction %1 + %5 = OpTypeInt 32 0 + %6 = OpTypeImage %5 Buffer 0 0 0 2 R32ui + %7 = OpTypePointer UniformConstant %6 + %8 = OpVariable %7 UniformConstant + %9 = OpTypeStruct %5 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpTypeVector %12 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpTypeFloat 32 + %17 = OpTypeVector %16 4 + %18 = OpTypePointer Function %17 + %20 = OpTypePointer Uniform %5 + %21 = OpConstant %5 0 + %23 = OpConstant %5 1 + %26 = OpTypePointer Function %16 + %33 = OpConstant %12 0 + %34 = OpConstant %5 2 + %37 = OpTypePointer Input %12 + %41 = OpTypeVector %5 4 + %3 = OpFunction %1 None %2 + %4 = OpLabel + %19 = OpVariable %18 Function + %22 = OpAccessChain %20 %11 %21 + %24 = OpAtomicIDecrement %5 %22 %23 %21 + %25 = OpBitcast %16 %24 + %27 = OpInBoundsAccessChain %26 %19 %21 + OpStore %27 %25 + %28 = OpLoad %6 %8 + %29 = OpInBoundsAccessChain %26 %19 %21 + %30 = OpLoad %16 %29 + %31 = OpBitcast %12 %30 + %32 = OpIMul %5 %31 %23 + %35 = OpShiftRightLogical %5 %33 %34 + %36 = OpIAdd %5 %32 %35 + %38 = OpInBoundsAccessChain %37 %15 %21 + %39 = OpLoad %12 %38 + %40 = OpBitcast %5 %39 + %42 = OpCompositeConstruct %41 %40 %40 %40 %40 + OpImageWrite %28 %36 %42 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/shaders-hlsl/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..40c1de3 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "main" %15 + OpExecutionMode %3 LocalSize 4 1 1 + OpName %3 "main" + OpName %8 "u0" + OpName %9 "u0_counters" + OpMemberName %9 0 "c" + OpName %11 "u0_counter" + OpName %15 "vThreadID" + OpName %19 "r0" + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 1 + OpDecorate %11 Binding 1 + OpDecorate %15 BuiltIn GlobalInvocationId + %1 = OpTypeVoid + %2 = OpTypeFunction %1 + %5 = OpTypeInt 32 0 + %6 = OpTypeImage %5 Buffer 0 0 0 2 R32ui + %7 = OpTypePointer UniformConstant %6 + %8 = OpVariable %7 UniformConstant + %9 = OpTypeStruct %5 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpTypeVector %12 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpTypeFloat 32 + %17 = OpTypeVector %16 4 + %18 = OpTypePointer Function %17 + %20 = OpTypePointer Uniform %5 + %21 = OpConstant %5 0 + %23 = OpConstant %5 1 + %26 = OpTypePointer Function %16 + %33 = OpConstant %12 0 + %34 = OpConstant %5 2 + %37 = OpTypePointer Input %12 + %41 = OpTypeVector %5 4 + %3 = OpFunction %1 None %2 + %4 = OpLabel + %19 = OpVariable %18 Function + %22 = OpAccessChain %20 %11 %21 + %24 = OpAtomicIIncrement %5 %22 %23 %21 + %25 = OpBitcast %16 %24 + %27 = OpInBoundsAccessChain %26 %19 %21 + OpStore %27 %25 + %28 = OpLoad %6 %8 + %29 = OpInBoundsAccessChain %26 %19 %21 + %30 = OpLoad %16 %29 + %31 = OpBitcast %12 %30 + %32 = OpIMul %5 %31 %23 + %35 = OpShiftRightLogical %5 %33 %34 + %36 = OpIAdd %5 %32 %35 + %38 = OpInBoundsAccessChain %37 %15 %21 + %39 = OpLoad %12 %38 + %40 = OpBitcast %5 %39 + %42 = OpCompositeConstruct %41 %40 %40 %40 %40 + OpImageWrite %28 %36 %42 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..b7b4e0b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,101 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %inputs Restrict + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + OpDecorate %outputs Restrict + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %bool = OpTypeBool + %bvec4 = OpTypeVector %bool 4 + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + %uzero = OpConstant %uint 0 + %uone = OpConstant %uint 1 + %utrue = OpConstantComposite %uvec4 %uone %uone %uone %uone + %ufalse = OpConstantComposite %uvec4 %uzero %uzero %uzero %uzero + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + + %result_slt = OpSLessThan %bvec4 %input0 %input1 + %result_sle = OpSLessThanEqual %bvec4 %input0 %input1 + %result_ult = OpULessThan %bvec4 %input0 %input1 + %result_ule = OpULessThanEqual %bvec4 %input0 %input1 + %result_sgt = OpSGreaterThan %bvec4 %input0 %input1 + %result_sge = OpSGreaterThanEqual %bvec4 %input0 %input1 + %result_ugt = OpUGreaterThan %bvec4 %input0 %input1 + %result_uge = OpUGreaterThanEqual %bvec4 %input0 %input1 + + %int_slt = OpSelect %uvec4 %result_slt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_slt + + %int_sle = OpSelect %uvec4 %result_sle %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sle + + %int_ult = OpSelect %uvec4 %result_ult %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ult + + %int_ule = OpSelect %uvec4 %result_ule %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ule + + %int_sgt = OpSelect %uvec4 %result_sgt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sgt + + %int_sge = OpSelect %uvec4 %result_sge %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sge + + %int_ugt = OpSelect %uvec4 %result_ugt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ugt + + %int_uge = OpSelect %uvec4 %result_uge %utrue %ufalse + OpStore %output_ptr_uvec4 %int_uge + + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..85f6cc0 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,119 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 59 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "A" + OpMemberName %Foo 0 "a" + OpMemberName %Foo 1 "b" + OpName %A "A" + OpMemberName %A 0 "Data" + OpName %C1 "C1" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpName %Foo_0 "A" + OpMemberName %Foo_0 0 "a" + OpMemberName %Foo_0 1 "b" + OpName %A_0 "A" + OpMemberName %A_0 0 "Data" + OpName %C2 "C2" + OpName %B "B" + OpMemberName %B 0 "Data" + OpName %C3 "C3" + OpName %B_0 "B" + OpMemberName %B_0 0 "Data" + OpName %C4 "C4" + OpMemberDecorate %Foo 0 Offset 0 + OpMemberDecorate %Foo 1 Offset 4 + OpDecorate %_runtimearr_Foo ArrayStride 8 + OpMemberDecorate %A 0 Offset 0 + OpDecorate %A BufferBlock + OpDecorate %C1 DescriptorSet 0 + OpDecorate %C1 Binding 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %Foo_0 1 Offset 4 + OpDecorate %_arr_Foo_0_uint_1024 ArrayStride 16 + OpMemberDecorate %A_0 0 Offset 0 + OpDecorate %A_0 Block + OpDecorate %C2 DescriptorSet 0 + OpDecorate %C2 Binding 2 + OpDecorate %_runtimearr_Foo_0 ArrayStride 8 + OpMemberDecorate %B 0 Offset 0 + OpDecorate %B BufferBlock + OpDecorate %C3 DescriptorSet 0 + OpDecorate %C3 Binding 0 + OpDecorate %_arr_Foo_0_uint_1024_0 ArrayStride 16 + OpMemberDecorate %B_0 0 Offset 0 + OpDecorate %B_0 Block + OpDecorate %C4 DescriptorSet 0 + OpDecorate %C4 Binding 3 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %Foo = OpTypeStruct %int %int +%_runtimearr_Foo = OpTypeRuntimeArray %Foo + %A = OpTypeStruct %_runtimearr_Foo +%_ptr_Uniform_A = OpTypePointer Uniform %A + %C1 = OpVariable %_ptr_Uniform_A Uniform + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %Foo_0 = OpTypeStruct %int %int + %uint_1024 = OpConstant %uint 1024 +%_arr_Foo_0_uint_1024 = OpTypeArray %Foo_0 %uint_1024 + %A_0 = OpTypeStruct %_arr_Foo_0_uint_1024 +%_ptr_Uniform_A_0 = OpTypePointer Uniform %A_0 + %C2 = OpVariable %_ptr_Uniform_A_0 Uniform +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Uniform_Foo = OpTypePointer Uniform %Foo +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_1 = OpConstant %int 1 +%_runtimearr_Foo_0 = OpTypeRuntimeArray %Foo + %B = OpTypeStruct %_runtimearr_Foo_0 +%_ptr_Uniform_B = OpTypePointer Uniform %B + %C3 = OpVariable %_ptr_Uniform_B Uniform +%_arr_Foo_0_uint_1024_0 = OpTypeArray %Foo_0 %uint_1024 + %B_0 = OpTypeStruct %_arr_Foo_0_uint_1024_0 +%_ptr_Uniform_B_0 = OpTypePointer Uniform %B_0 + %C4 = OpVariable %_ptr_Uniform_B_0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %20 = OpLoad %uint %19 + %27 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %28 = OpLoad %uint %27 + %30 = OpAccessChain %_ptr_Uniform_Foo_0 %C2 %int_0 %28 + %31 = OpLoad %Foo_0 %30 + %33 = OpAccessChain %_ptr_Uniform_Foo %C1 %int_0 %20 + %34 = OpCompositeExtract %int %31 0 + %36 = OpAccessChain %_ptr_Uniform_int %33 %int_0 + OpStore %36 %34 + %37 = OpCompositeExtract %int %31 1 + %39 = OpAccessChain %_ptr_Uniform_int %33 %int_1 + OpStore %39 %37 + %44 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %45 = OpLoad %uint %44 + %50 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %51 = OpLoad %uint %50 + %52 = OpAccessChain %_ptr_Uniform_Foo_0 %C4 %int_0 %51 + %53 = OpLoad %Foo_0 %52 + %54 = OpAccessChain %_ptr_Uniform_Foo %C3 %int_0 %45 + %55 = OpCompositeExtract %int %53 0 + %56 = OpAccessChain %_ptr_Uniform_int %54 %int_0 + OpStore %56 %55 + %57 = OpCompositeExtract %int %53 1 + %58 = OpAccessChain %_ptr_Uniform_int %54 %int_1 + OpStore %58 %57 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/comp/control-flow-hints.asm.comp b/third_party/spirv-cross/shaders-hlsl/asm/comp/control-flow-hints.asm.comp new file mode 100644 index 0000000..74a1595 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/comp/control-flow-hints.asm.comp @@ -0,0 +1,146 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 85 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %i "i" + OpName %bar "bar" + OpMemberName %bar 0 "@data" + OpName %bar_0 "bar" + OpName %foo "foo" + OpName %i_0 "i" + OpName %v "v" + OpName %w "w" + OpName %value "value" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %bar 0 Offset 0 + OpDecorate %bar BufferBlock + OpDecorate %bar_0 DescriptorSet 0 + OpDecorate %bar_0 Binding 0 + OpDecorate %foo DescriptorSet 0 + OpDecorate %foo Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %bool = OpTypeBool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %bar = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_bar = OpTypePointer Uniform %bar + %bar_0 = OpVariable %_ptr_Uniform_bar Uniform + %foo = OpVariable %_ptr_Uniform_bar Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_1 = OpConstant %int 1 + %int_15 = OpConstant %int 15 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_10 = OpConstant %float 10 + %int_20 = OpConstant %int 20 + %float_5 = OpConstant %float 5 + %72 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %float_20 = OpConstant %float 20 + %float_40 = OpConstant %float 40 + %main = OpFunction %void None %3 + %5 = OpLabel + %84 = OpFunctionCall %void %_main_ + OpReturn + OpFunctionEnd + %_main_ = OpFunction %void None %3 + %7 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %i_0 = OpVariable %_ptr_Function_int Function + %v = OpVariable %_ptr_Function_float Function + %w = OpVariable %_ptr_Function_float Function + %value = OpVariable %_ptr_Function_float Function + OpStore %i %int_0 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 Unroll + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %int %i + %20 = OpSLessThan %bool %17 %int_16 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %27 = OpLoad %int %i + %29 = OpLoad %int %i + %31 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %29 + %32 = OpLoad %v4float %31 + %33 = OpAccessChain %_ptr_Uniform_v4float %bar_0 %int_0 %27 + OpStore %33 %32 + OpBranch %15 + %15 = OpLabel + %34 = OpLoad %int %i + %36 = OpIAdd %int %34 %int_1 + OpStore %i %36 + OpBranch %12 + %14 = OpLabel + OpStore %i_0 %int_0 + OpBranch %38 + %38 = OpLabel + OpLoopMerge %40 %41 DontUnroll + OpBranch %42 + %42 = OpLabel + %43 = OpLoad %int %i_0 + %44 = OpSLessThan %bool %43 %int_16 + OpBranchConditional %44 %39 %40 + %39 = OpLabel + %46 = OpLoad %int %i_0 + %47 = OpISub %int %int_15 %46 + %48 = OpLoad %int %i_0 + %49 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %48 + %50 = OpLoad %v4float %49 + %51 = OpAccessChain %_ptr_Uniform_v4float %bar_0 %int_0 %47 + OpStore %51 %50 + OpBranch %41 + %41 = OpLabel + %52 = OpLoad %int %i_0 + %53 = OpIAdd %int %52 %int_1 + OpStore %i_0 %53 + OpBranch %38 + %40 = OpLabel + %60 = OpAccessChain %_ptr_Uniform_float %bar_0 %int_0 %int_10 %uint_0 + %61 = OpLoad %float %60 + OpStore %v %61 + %63 = OpAccessChain %_ptr_Uniform_float %foo %int_0 %int_10 %uint_0 + %64 = OpLoad %float %63 + OpStore %w %64 + %65 = OpLoad %float %v + %67 = OpFOrdGreaterThan %bool %65 %float_10 + OpSelectionMerge %69 DontFlatten + OpBranchConditional %67 %68 %69 + %68 = OpLabel + %73 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %int_20 + OpStore %73 %72 + OpBranch %69 + %69 = OpLabel + OpStore %value %float_20 + %76 = OpLoad %float %w + %78 = OpFOrdGreaterThan %bool %76 %float_40 + OpSelectionMerge %80 Flatten + OpBranchConditional %78 %79 %80 + %79 = OpLabel + OpStore %value %float_20 + OpBranch %80 + %80 = OpLabel + %81 = OpLoad %float %value + %82 = OpCompositeConstruct %v4float %81 %81 %81 %81 + %83 = OpAccessChain %_ptr_Uniform_v4float %foo %int_0 %int_20 + OpStore %83 %82 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..78b1dc7 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 61 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %id_1 + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %Load_u1_ "Load(u1;" + OpName %size "size" + OpName %_main_vu3_ "@main(vu3;" + OpName %id "id" + OpName %data "data" + OpName %byteAddrTemp "byteAddrTemp" + OpName %ssbo "ssbo" + OpMemberName %ssbo 0 "@data" + OpName %ssbo_0 "ssbo" + OpName %param "param" + OpName %id_0 "id" + OpName %id_1 "id" + OpName %param_0 "param" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %ssbo 0 NonWritable + OpMemberDecorate %ssbo 0 Offset 0 + OpDecorate %ssbo BufferBlock + OpDecorate %ssbo_0 DescriptorSet 0 + OpDecorate %ssbo_0 Binding 1 + OpDecorate %id_1 BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %8 = OpTypeFunction %void %_ptr_Function_uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Function_v3uint = OpTypePointer Function %v3uint + %14 = OpTypeFunction %void %_ptr_Function_v3uint + %v4uint = OpTypeVector %uint 4 +%_ptr_Function_v4uint = OpTypePointer Function %v4uint + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_2 = OpConstant %int 2 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %ssbo = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_ssbo = OpTypePointer Uniform %ssbo + %ssbo_0 = OpVariable %_ptr_Uniform_ssbo Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 + %uint_4 = OpConstant %uint 4 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %id_1 = OpVariable %_ptr_Input_v3uint Input + %main = OpFunction %void None %3 + %5 = OpLabel + %id_0 = OpVariable %_ptr_Function_v3uint Function + %param_0 = OpVariable %_ptr_Function_v3uint Function + %57 = OpLoad %v3uint %id_1 + OpStore %id_0 %57 + %59 = OpLoad %v3uint %id_0 + OpStore %param_0 %59 + %60 = OpFunctionCall %void %_main_vu3_ %param_0 + OpReturn + OpFunctionEnd + %Load_u1_ = OpFunction %void None %8 + %size = OpFunctionParameter %_ptr_Function_uint + %11 = OpLabel + %data = OpVariable %_ptr_Function_v4uint Function +%byteAddrTemp = OpVariable %_ptr_Function_int Function + %24 = OpLoad %uint %size + %26 = OpShiftRightLogical %int %24 %int_2 + OpStore %byteAddrTemp %26 + %32 = OpLoad %int %byteAddrTemp + %34 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %32 + %35 = OpLoad %uint %34 + %36 = OpLoad %int %byteAddrTemp + %38 = OpIAdd %int %36 %int_1 + %39 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpLoad %int %byteAddrTemp + %42 = OpIAdd %int %41 %int_2 + %43 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %42 + %44 = OpLoad %uint %43 + %45 = OpLoad %int %byteAddrTemp + %47 = OpIAdd %int %45 %int_3 + %48 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %47 + %49 = OpLoad %uint %48 + %50 = OpCompositeConstruct %v4uint %35 %40 %44 %49 + OpStore %data %50 + OpReturn + OpFunctionEnd + %_main_vu3_ = OpFunction %void None %14 + %id = OpFunctionParameter %_ptr_Function_v3uint + %17 = OpLabel + %param = OpVariable %_ptr_Function_uint Function + OpStore %param %uint_4 + %53 = OpFunctionCall %void %Load_u1_ %param + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp b/third_party/spirv-cross/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp new file mode 100644 index 0000000..6c060ee --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/comp/nmin-max-clamp.asm.comp @@ -0,0 +1,203 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 139 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a1" + OpMemberName %SSBO 1 "a2" + OpMemberName %SSBO 2 "a3" + OpMemberName %SSBO 3 "a4" + OpMemberName %SSBO 4 "b1" + OpMemberName %SSBO 5 "b2" + OpMemberName %SSBO 6 "b3" + OpMemberName %SSBO 7 "b4" + OpMemberName %SSBO 8 "c1" + OpMemberName %SSBO 9 "c2" + OpMemberName %SSBO 10 "c3" + OpMemberName %SSBO 11 "c4" + OpName %_ "" + OpName %i "i" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 8 + OpMemberDecorate %SSBO 2 Offset 16 + OpMemberDecorate %SSBO 3 Offset 32 + OpMemberDecorate %SSBO 4 Offset 48 + OpMemberDecorate %SSBO 5 Offset 56 + OpMemberDecorate %SSBO 6 Offset 64 + OpMemberDecorate %SSBO 7 Offset 80 + OpMemberDecorate %SSBO 8 Offset 96 + OpMemberDecorate %SSBO 9 Offset 104 + OpMemberDecorate %SSBO 10 Offset 112 + OpMemberDecorate %SSBO 11 Offset 128 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %SSBO = OpTypeStruct %float %v2float %v3float %v4float %float %v2float %v3float %v4float %float %v2float %v3float %v4float +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %int_8 = OpConstant %int 8 + %int_1 = OpConstant %int 1 + %int_5 = OpConstant %int 5 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %int_9 = OpConstant %int 9 + %int_2 = OpConstant %int 2 + %int_6 = OpConstant %int 6 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %int_10 = OpConstant %int 10 + %int_3 = OpConstant %int 3 + %int_7 = OpConstant %int 7 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_11 = OpConstant %int 11 +%_ptr_Function_int = OpTypePointer Function %int + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %7 + %35 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %36 = OpAccessChain %_ptr_Uniform_float %_ %int_4 + %37 = OpLoad %float %36 + %38 = OpAccessChain %_ptr_Uniform_float %_ %int_8 + %39 = OpLoad %float %38 + %40 = OpExtInst %float %1 NMin %37 %39 + %41 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %41 %40 + %42 = OpAccessChain %_ptr_Uniform_v2float %_ %int_5 + %43 = OpLoad %v2float %42 + %44 = OpAccessChain %_ptr_Uniform_v2float %_ %int_9 + %45 = OpLoad %v2float %44 + %46 = OpExtInst %v2float %1 NMin %43 %45 + %47 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + OpStore %47 %46 + %48 = OpAccessChain %_ptr_Uniform_v3float %_ %int_6 + %49 = OpLoad %v3float %48 + %50 = OpAccessChain %_ptr_Uniform_v3float %_ %int_10 + %51 = OpLoad %v3float %50 + %52 = OpExtInst %v3float %1 NMin %49 %51 + %53 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + OpStore %53 %52 + %54 = OpAccessChain %_ptr_Uniform_v4float %_ %int_7 + %55 = OpLoad %v4float %54 + %56 = OpAccessChain %_ptr_Uniform_v4float %_ %int_11 + %57 = OpLoad %v4float %56 + %58 = OpExtInst %v4float %1 NMin %55 %57 + %59 = OpAccessChain %_ptr_Uniform_v4float %_ %int_3 + OpStore %59 %58 + %60 = OpAccessChain %_ptr_Uniform_float %_ %int_4 + %61 = OpLoad %float %60 + %62 = OpAccessChain %_ptr_Uniform_float %_ %int_8 + %63 = OpLoad %float %62 + %64 = OpExtInst %float %1 NMax %61 %63 + %65 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %65 %64 + %66 = OpAccessChain %_ptr_Uniform_v2float %_ %int_5 + %67 = OpLoad %v2float %66 + %68 = OpAccessChain %_ptr_Uniform_v2float %_ %int_9 + %69 = OpLoad %v2float %68 + %70 = OpExtInst %v2float %1 NMax %67 %69 + %71 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + OpStore %71 %70 + %72 = OpAccessChain %_ptr_Uniform_v3float %_ %int_6 + %73 = OpLoad %v3float %72 + %74 = OpAccessChain %_ptr_Uniform_v3float %_ %int_10 + %75 = OpLoad %v3float %74 + %76 = OpExtInst %v3float %1 NMax %73 %75 + %77 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + OpStore %77 %76 + %78 = OpAccessChain %_ptr_Uniform_v4float %_ %int_7 + %79 = OpLoad %v4float %78 + %80 = OpAccessChain %_ptr_Uniform_v4float %_ %int_11 + %81 = OpLoad %v4float %80 + %82 = OpExtInst %v4float %1 NMax %79 %81 + %83 = OpAccessChain %_ptr_Uniform_v4float %_ %int_3 + OpStore %83 %82 + %84 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %85 = OpLoad %float %84 + %86 = OpAccessChain %_ptr_Uniform_float %_ %int_4 + %87 = OpLoad %float %86 + %88 = OpAccessChain %_ptr_Uniform_float %_ %int_8 + %89 = OpLoad %float %88 + %90 = OpExtInst %float %1 NClamp %85 %87 %89 + %91 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %91 %90 + %92 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + %93 = OpLoad %v2float %92 + %94 = OpAccessChain %_ptr_Uniform_v2float %_ %int_5 + %95 = OpLoad %v2float %94 + %96 = OpAccessChain %_ptr_Uniform_v2float %_ %int_9 + %97 = OpLoad %v2float %96 + %98 = OpExtInst %v2float %1 NClamp %93 %95 %97 + %99 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + OpStore %99 %98 + %100 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + %101 = OpLoad %v3float %100 + %102 = OpAccessChain %_ptr_Uniform_v3float %_ %int_6 + %103 = OpLoad %v3float %102 + %104 = OpAccessChain %_ptr_Uniform_v3float %_ %int_10 + %105 = OpLoad %v3float %104 + %106 = OpExtInst %v3float %1 NClamp %101 %103 %105 + %107 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + OpStore %107 %106 + %108 = OpAccessChain %_ptr_Uniform_v4float %_ %int_3 + %109 = OpLoad %v4float %108 + %110 = OpAccessChain %_ptr_Uniform_v4float %_ %int_7 + %111 = OpLoad %v4float %110 + %112 = OpAccessChain %_ptr_Uniform_v4float %_ %int_11 + %113 = OpLoad %v4float %112 + %114 = OpExtInst %v4float %1 NClamp %109 %111 %113 + %115 = OpAccessChain %_ptr_Uniform_v4float %_ %int_3 + OpStore %115 %114 + OpStore %i %int_0 + OpBranch %116 + %116 = OpLabel + OpLoopMerge %117 %118 None + OpBranch %119 + %119 = OpLabel + %120 = OpLoad %int %i + %121 = OpSLessThan %bool %120 %int_2 + OpBranchConditional %121 %122 %117 + %122 = OpLabel + %123 = OpAccessChain %_ptr_Uniform_v2float %_ %int_5 + %124 = OpLoad %v2float %123 + %125 = OpAccessChain %_ptr_Uniform_v2float %_ %int_9 + %126 = OpLoad %v2float %125 + %127 = OpExtInst %v2float %1 NMin %124 %126 + %128 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + OpStore %128 %127 + OpBranch %118 + %118 = OpLabel + %129 = OpLoad %int %i + %130 = OpIAdd %int %129 %int_1 + OpStore %i %130 + %131 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %132 = OpLoad %float %131 + %133 = OpAccessChain %_ptr_Uniform_float %_ %int_5 %uint_0 + %134 = OpLoad %float %133 + %135 = OpAccessChain %_ptr_Uniform_float %_ %int_5 %uint_1 + %136 = OpLoad %float %135 + %137 = OpExtInst %float %1 NClamp %132 %134 %136 + %138 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %138 %137 + OpBranch %116 + %117 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag new file mode 100644 index 0000000..d778034 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/cbuffer-stripped.asm.frag @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 34 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpMemberDecorate %UBO 0 RowMajor + OpMemberDecorate %UBO 0 Offset 0 + OpMemberDecorate %UBO 0 MatrixStride 16 + OpMemberDecorate %UBO 1 Offset 64 + OpDecorate %UBO Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %8 = OpTypeFunction %v2float +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%mat2v4float = OpTypeMatrix %v4float 2 + %UBO = OpTypeStruct %mat2v4float %v4float +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO + %_ = OpVariable %_ptr_Uniform_UBO Uniform + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_0 = OpConstant %int 0 +%_ptr_Uniform_mat2v4float = OpTypePointer Uniform %mat2v4float +%_ptr_Output_v2float = OpTypePointer Output %v2float +%_entryPointOutput = OpVariable %_ptr_Output_v2float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %33 = OpFunctionCall %v2float %_main_ + OpStore %_entryPointOutput %33 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v2float None %8 + %10 = OpLabel + %a0 = OpVariable %_ptr_Function_v2float Function + %21 = OpAccessChain %_ptr_Uniform_v4float %_ %int_1 + %22 = OpLoad %v4float %21 + %25 = OpAccessChain %_ptr_Uniform_mat2v4float %_ %int_0 + %26 = OpLoad %mat2v4float %25 + %27 = OpVectorTimesMatrix %v2float %22 %26 + OpStore %a0 %27 + %28 = OpLoad %v2float %a0 + OpReturnValue %28 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag new file mode 100644 index 0000000..ba2f95b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/combined-sampler-reuse.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vUV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTex "uTex" + OpName %uSampler "uSampler" + OpName %vUV "vUV" + OpDecorate %FragColor Location 0 + OpDecorate %uTex DescriptorSet 0 + OpDecorate %uTex Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 0 + OpDecorate %vUV Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %uTex = OpVariable %_ptr_UniformConstant_10 UniformConstant + %14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 + %uSampler = OpVariable %_ptr_UniformConstant_14 UniformConstant + %18 = OpTypeSampledImage %10 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_1 = OpConstant %int 1 + %32 = OpConstantComposite %v2int %int_1 %int_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %10 %uTex + %17 = OpLoad %14 %uSampler + %19 = OpSampledImage %18 %13 %17 + %23 = OpLoad %v2float %vUV + %24 = OpImageSampleImplicitLod %v4float %19 %23 + OpStore %FragColor %24 + %28 = OpLoad %v2float %vUV + %33 = OpImageSampleImplicitLod %v4float %19 %28 ConstOffset %32 + %34 = OpLoad %v4float %FragColor + %35 = OpFAdd %v4float %34 %33 + OpStore %FragColor %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..0efd315 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/empty-struct.asm.frag @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.2 +; Generator: Khronos; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %EntryPoint_Main "main" + OpExecutionMode %EntryPoint_Main OriginUpperLeft + OpSource Unknown 100 + OpName %EmptyStructTest "EmptyStructTest" + OpName %GetValue "GetValue" + OpName %GetValue2 "GetValue" + OpName %self "self" + OpName %self2 "self" + OpName %emptyStruct "emptyStruct" + OpName %value "value" + OpName %EntryPoint_Main "EntryPoint_Main" + +%EmptyStructTest = OpTypeStruct +%_ptr_Function_EmptyStructTest = OpTypePointer Function %EmptyStructTest + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %5 = OpTypeFunction %float %_ptr_Function_EmptyStructTest + %6 = OpTypeFunction %float %EmptyStructTest + %void = OpTypeVoid +%_ptr_Function_void = OpTypePointer Function %void + %8 = OpTypeFunction %void %_ptr_Function_EmptyStructTest + %9 = OpTypeFunction %void + %float_0 = OpConstant %float 0 + + %GetValue = OpFunction %float None %5 + %self = OpFunctionParameter %_ptr_Function_EmptyStructTest + %13 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + + %GetValue2 = OpFunction %float None %6 + %self2 = OpFunctionParameter %EmptyStructTest + %14 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + +%EntryPoint_Main = OpFunction %void None %9 + %37 = OpLabel + %emptyStruct = OpVariable %_ptr_Function_EmptyStructTest Function + %18 = OpVariable %_ptr_Function_EmptyStructTest Function + %value = OpVariable %_ptr_Function_float Function + %value2 = OpCompositeConstruct %EmptyStructTest + %22 = OpFunctionCall %float %GetValue %emptyStruct + %23 = OpFunctionCall %float %GetValue2 %value2 + OpStore %value %22 + OpStore %value %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/frem.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/frem.asm.frag new file mode 100644 index 0000000..8350c75 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/frem.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 16 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA RelaxedPrecision + OpDecorate %vA Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %vB RelaxedPrecision + OpDecorate %vB Location 1 + OpDecorate %14 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vA = OpVariable %_ptr_Input_v4float Input + %vB = OpVariable %_ptr_Input_v4float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpLoad %v4float %vA + %14 = OpLoad %v4float %vB + %15 = OpFRem %v4float %12 %14 + OpStore %FragColor %15 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..397aa98 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,153 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 76 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %foobar_vf4_ "foo" + OpName %a "foo" + OpName %foobar_vf3_ "foo" + OpName %a_0 "foo" + OpName %foobaz_vf4_ "foo" + OpName %a_1 "foo" + OpName %foobaz_vf2_ "foo" + OpName %a_2 "foo" + OpName %a_3 "foo" + OpName %param "foo" + OpName %b "foo" + OpName %param_0 "foo" + OpName %c "foo" + OpName %param_1 "foo" + OpName %d "foo" + OpName %param_2 "foo" + OpName %FragColor "FragColor" + OpDecorate %foobar_vf4_ RelaxedPrecision + OpDecorate %a RelaxedPrecision + OpDecorate %foobar_vf3_ RelaxedPrecision + OpDecorate %a_0 RelaxedPrecision + OpDecorate %foobaz_vf4_ RelaxedPrecision + OpDecorate %a_1 RelaxedPrecision + OpDecorate %foobaz_vf2_ RelaxedPrecision + OpDecorate %a_2 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %a_3 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %b RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %c RelaxedPrecision + OpDecorate %62 RelaxedPrecision + OpDecorate %d RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %69 RelaxedPrecision + OpDecorate %70 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %74 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %9 = OpTypeFunction %v4float %_ptr_Function_v4float + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %15 = OpTypeFunction %v4float %_ptr_Function_v3float + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %24 = OpTypeFunction %v4float %_ptr_Function_v2float + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %53 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %57 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %64 = OpConstantComposite %v2float %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %a_3 = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_v4float Function + %b = OpVariable %_ptr_Function_v4float Function + %param_0 = OpVariable %_ptr_Function_v3float Function + %c = OpVariable %_ptr_Function_v4float Function + %param_1 = OpVariable %_ptr_Function_v4float Function + %d = OpVariable %_ptr_Function_v4float Function + %param_2 = OpVariable %_ptr_Function_v2float Function + OpStore %param %53 + %55 = OpFunctionCall %v4float %foobar_vf4_ %param + OpStore %a_3 %55 + OpStore %param_0 %57 + %59 = OpFunctionCall %v4float %foobar_vf3_ %param_0 + OpStore %b %59 + OpStore %param_1 %53 + %62 = OpFunctionCall %v4float %foobaz_vf4_ %param_1 + OpStore %c %62 + OpStore %param_2 %64 + %66 = OpFunctionCall %v4float %foobaz_vf2_ %param_2 + OpStore %d %66 + %69 = OpLoad %v4float %a_3 + %70 = OpLoad %v4float %b + %71 = OpFAdd %v4float %69 %70 + %72 = OpLoad %v4float %c + %73 = OpFAdd %v4float %71 %72 + %74 = OpLoad %v4float %d + %75 = OpFAdd %v4float %73 %74 + OpStore %FragColor %75 + OpReturn + OpFunctionEnd +%foobar_vf4_ = OpFunction %v4float None %9 + %a = OpFunctionParameter %_ptr_Function_v4float + %12 = OpLabel + %28 = OpLoad %v4float %a + %30 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 + %31 = OpFAdd %v4float %28 %30 + OpReturnValue %31 + OpFunctionEnd +%foobar_vf3_ = OpFunction %v4float None %15 + %a_0 = OpFunctionParameter %_ptr_Function_v3float + %18 = OpLabel + %34 = OpLoad %v3float %a_0 + %35 = OpVectorShuffle %v4float %34 %34 0 1 2 2 + %36 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 + %37 = OpFAdd %v4float %35 %36 + OpReturnValue %37 + OpFunctionEnd +%foobaz_vf4_ = OpFunction %v4float None %9 + %a_1 = OpFunctionParameter %_ptr_Function_v4float + %21 = OpLabel + %40 = OpLoad %v4float %a_1 + %42 = OpCompositeConstruct %v4float %float_2 %float_2 %float_2 %float_2 + %43 = OpFAdd %v4float %40 %42 + OpReturnValue %43 + OpFunctionEnd +%foobaz_vf2_ = OpFunction %v4float None %24 + %a_2 = OpFunctionParameter %_ptr_Function_v2float + %27 = OpLabel + %46 = OpLoad %v2float %a_2 + %47 = OpVectorShuffle %v4float %46 %46 0 1 0 1 + %48 = OpCompositeConstruct %v4float %float_2 %float_2 %float_2 %float_2 + %49 = OpFAdd %v4float %47 %48 + OpReturnValue %49 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..63c8ab5 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 19 +; Schema: 0 + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %Size + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %Size "Size" + OpName %uTexture "uTexture" + OpDecorate %Size Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%_ptr_Output_v2int = OpTypePointer Output %v2int + %Size = OpVariable %_ptr_Output_v2int Output + %float = OpTypeFloat 32 + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown + %12 = OpTypeSampledImage %11 +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_12 UniformConstant + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpLoad %12 %uTexture + %17 = OpImage %11 %15 + %18 = OpImageQuerySizeLod %v2int %17 %int_0 + %19 = OpImageQuerySizeLod %v2int %17 %int_1 + %20 = OpIAdd %v2int %18 %19 + OpStore %Size %20 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..ccdfeef --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,81 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 60 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %v0 %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %phi "phi" + OpName %i "i" + OpName %v0 "v0" + OpName %FragColor "FragColor" + OpName %uImage "uImage" + OpDecorate %v0 Location 0 + OpDecorate %FragColor Location 0 + OpDecorate %uImage DescriptorSet 0 + OpDecorate %uImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstant %float 1 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %36 = OpTypeImage %float 2D 0 0 0 1 Unknown + %37 = OpTypeSampledImage %36 +%_ptr_UniformConstant_37 = OpTypePointer UniformConstant %37 + %uImage = OpVariable %_ptr_UniformConstant_37 UniformConstant + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float_2 = OpConstant %float 2 + %int_1 = OpConstant %int 1 + %float_1_vec = OpConstantComposite %v4float %float_1 %float_2 %float_1 %float_2 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpStore %i %int_0 + OpBranch %loop_header + %loop_header = OpLabel + %phi = OpPhi %float %float_1 %5 %phi_plus_2 %continue_block + %tex_phi = OpPhi %v4float %float_1_vec %5 %texture_load_result %continue_block + OpLoopMerge %merge_block %continue_block None + OpBranch %loop_body + %loop_body = OpLabel + OpStore %FragColor %tex_phi + %19 = OpLoad %int %i + %22 = OpSLessThan %bool %19 %int_4 + OpBranchConditional %22 %15 %merge_block + %15 = OpLabel + %26 = OpLoad %int %i + %28 = OpAccessChain %_ptr_Input_float %v0 %26 + %29 = OpLoad %float %28 + %31 = OpFOrdGreaterThan %bool %29 %float_0 + OpBranchConditional %31 %continue_block %merge_block + %continue_block = OpLabel + %40 = OpLoad %37 %uImage + %43 = OpCompositeConstruct %v2float %phi %phi + %texture_load_result = OpImageSampleExplicitLod %v4float %40 %43 Lod %float_0 + %phi_plus_2 = OpFAdd %float %phi %float_2 + %54 = OpLoad %int %i + %56 = OpIAdd %int %54 %int_1 + OpStore %i %56 + OpBranch %loop_header + %merge_block = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..40e5d3a --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,29 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 14 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %FragColor = OpVariable %_ptr_Output_v3float Output +%float_0x1p_128 = OpConstant %float 0x1p+128 +%float_n0x1p_128 = OpConstant %float -0x1p+128 +%float_0x1_8p_128 = OpConstant %float 0x1.8p+128 + %13 = OpConstantComposite %v3float %float_0x1p_128 %float_n0x1p_128 %float_0x1_8p_128 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %FragColor %13 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..9e08e9a --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,221 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 7 +; Bound: 83 +; Schema: 0 + OpCapability Shader + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vColor + OpExecutionMode %main OriginUpperLeft + %1 = OpString "test.frag" + OpSource GLSL 450 %1 "// OpModuleProcessed entry-point main +// OpModuleProcessed client vulkan100 +// OpModuleProcessed target-env vulkan1.0 +// OpModuleProcessed entry-point main +#line 1 +#version 450 + +layout(location = 0) in float vColor; +layout(location = 0) out float FragColor; + +void func() +{ + FragColor = 1.0; + FragColor = 2.0; + if (vColor < 0.0) + { + FragColor = 3.0; + } + else + { + FragColor = 4.0; + } + + for (int i = 0; i < 40 + vColor; i += int(vColor) + 5) + { + FragColor += 0.2; + FragColor += 0.3; + } + + switch (int(vColor)) + { + case 0: + FragColor += 0.2; + break; + + case 1: + FragColor += 0.4; + break; + + default: + FragColor += 0.8; + break; + } + + do + { + FragColor += 10.0 + vColor; + } while(FragColor < 100.0); +} + +void main() +{ + func(); +} +" + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %func_ "func(" + OpName %FragColor "FragColor" + OpName %vColor "vColor" + OpName %i "i" + OpDecorate %FragColor Location 0 + OpDecorate %vColor Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 +%_ptr_Input_float = OpTypePointer Input %float + %vColor = OpVariable %_ptr_Input_float Input + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %int = OpTypeInt 32 1 + + ; Should be ignored + OpLine %1 5 0 + +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %float_40 = OpConstant %float 40 +%float_0_200000003 = OpConstant %float 0.200000003 +%float_0_300000012 = OpConstant %float 0.300000012 + %int_5 = OpConstant %int 5 + + ; Should be ignored + OpLine %1 5 0 + +%float_0_400000006 = OpConstant %float 0.400000006 +%float_0_800000012 = OpConstant %float 0.800000012 + %float_10 = OpConstant %float 10 + %float_100 = OpConstant %float 100 + %main = OpFunction %void None %4 + OpLine %1 46 0 + %6 = OpLabel + OpLine %1 48 0 + %82 = OpFunctionCall %void %func_ + OpReturn + OpFunctionEnd + + ; Should be ignored + OpLine %1 5 0 + + %func_ = OpFunction %void None %4 + OpLine %1 6 0 + %8 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpLine %1 8 0 + OpStore %FragColor %float_1 + OpLine %1 9 0 + OpStore %FragColor %float_2 + OpLine %1 10 0 + %16 = OpLoad %float %vColor + %19 = OpFOrdLessThan %bool %16 %float_0 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %23 + %20 = OpLabel + OpLine %1 12 0 + OpStore %FragColor %float_3 + OpBranch %21 + %23 = OpLabel + OpLine %1 16 0 + OpStore %FragColor %float_4 + OpBranch %21 + %21 = OpLabel + OpLine %1 19 0 + OpStore %i %int_0 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %int %i + %35 = OpConvertSToF %float %34 + %37 = OpLoad %float %vColor + %38 = OpFAdd %float %float_40 %37 + %39 = OpFOrdLessThan %bool %35 %38 + OpBranchConditional %39 %30 %31 + %30 = OpLabel + OpLine %1 21 0 + %41 = OpLoad %float %FragColor + %42 = OpFAdd %float %41 %float_0_200000003 + OpStore %FragColor %42 + OpLine %1 22 0 + %44 = OpLoad %float %FragColor + %45 = OpFAdd %float %44 %float_0_300000012 + OpStore %FragColor %45 + OpBranch %32 + %32 = OpLabel + OpLine %1 19 0 + %46 = OpLoad %float %vColor + %47 = OpConvertFToS %int %46 + %49 = OpIAdd %int %47 %int_5 + %50 = OpLoad %int %i + %51 = OpIAdd %int %50 %49 + OpStore %i %51 + OpBranch %29 + %31 = OpLabel + OpLine %1 25 0 + %52 = OpLoad %float %vColor + %53 = OpConvertFToS %int %52 + OpSelectionMerge %57 None + OpSwitch %53 %56 0 %54 1 %55 + %56 = OpLabel + OpLine %1 36 0 + %66 = OpLoad %float %FragColor + %67 = OpFAdd %float %66 %float_0_800000012 + OpStore %FragColor %67 + OpLine %1 37 0 + OpBranch %57 + %54 = OpLabel + OpLine %1 28 0 + %58 = OpLoad %float %FragColor + %59 = OpFAdd %float %58 %float_0_200000003 + OpStore %FragColor %59 + OpLine %1 29 0 + OpBranch %57 + %55 = OpLabel + OpLine %1 32 0 + %62 = OpLoad %float %FragColor + %63 = OpFAdd %float %62 %float_0_400000006 + OpStore %FragColor %63 + OpLine %1 33 0 + OpBranch %57 + %57 = OpLabel + OpBranch %70 + OpLine %1 43 0 + %70 = OpLabel + OpLoopMerge %72 %73 None + OpBranch %71 + %71 = OpLabel + OpLine %1 42 0 + %75 = OpLoad %float %vColor + %76 = OpFAdd %float %float_10 %75 + %77 = OpLoad %float %FragColor + %78 = OpFAdd %float %77 %76 + OpStore %FragColor %78 + OpBranch %73 + %73 = OpLabel + OpLine %1 43 0 + %79 = OpLoad %float %FragColor + %81 = OpFOrdLessThan %bool %79 %float_100 + OpBranchConditional %81 %70 %72 + %72 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..320e5eb --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,195 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 111 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %index + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %index "index" + OpName %indexable "indexable" + OpName %indexable_0 "indexable" + OpName %indexable_1 "indexable" + OpName %foo "foo" + OpName %foobar "foobar" + OpName %baz "baz" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %index RelaxedPrecision + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %20 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %foo RelaxedPrecision + OpDecorate %61 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %79 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + OpDecorate %foobar RelaxedPrecision + OpDecorate %83 RelaxedPrecision + OpDecorate %90 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %93 RelaxedPrecision + OpDecorate %94 RelaxedPrecision + OpDecorate %95 RelaxedPrecision + OpDecorate %baz RelaxedPrecision + OpDecorate %105 RelaxedPrecision + OpDecorate %106 RelaxedPrecision + OpDecorate %108 RelaxedPrecision + OpDecorate %109 RelaxedPrecision + OpDecorate %110 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %uint = OpTypeInt 32 0 + %uint_16 = OpConstant %uint 16 +%_arr_float_uint_16 = OpTypeArray %float %uint_16 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %16 = OpConstantComposite %_arr_float_uint_16 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input +%_ptr_Function__arr_float_uint_16 = OpTypePointer Function %_arr_float_uint_16 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v4float = OpTypeVector %float 4 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 + %float_0 = OpConstant %float 0 + %54 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %55 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_8 = OpConstant %float 8 + %57 = OpConstantComposite %v4float %float_8 %float_8 %float_8 %float_8 + %float_5 = OpConstant %float 5 + %59 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %60 = OpConstantComposite %_arr_v4float_uint_4 %54 %55 %57 %59 + %int_30 = OpConstant %int 30 + %int_3 = OpConstant %int 3 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %float_20 = OpConstant %float 20 + %uint_2 = OpConstant %uint 2 + %97 = OpConstantComposite %v4float %float_20 %float_20 %float_20 %float_20 + %float_30 = OpConstant %float 30 + %99 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 + %float_50 = OpConstant %float 50 + %101 = OpConstantComposite %v4float %float_50 %float_50 %float_50 %float_50 + %float_60 = OpConstant %float 60 + %103 = OpConstantComposite %v4float %float_60 %float_60 %float_60 %float_60 + %104 = OpConstantComposite %_arr_v4float_uint_4 %97 %99 %101 %103 + %main = OpFunction %void None %3 + %5 = OpLabel + %indexable = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_0 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_1 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 + %foo = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %foobar = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %baz = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %20 = OpLoad %int %index + %24 = OpAccessChain %_ptr_Function_float %indexable %20 + %25 = OpLoad %float %24 + OpStore %FragColor %25 + %26 = OpLoad %int %index + %29 = OpSLessThan %bool %26 %int_10 + OpSelectionMerge %31 None + OpBranchConditional %29 %30 %40 + %30 = OpLabel + %32 = OpLoad %int %index + %34 = OpBitwiseXor %int %32 %int_1 + %36 = OpAccessChain %_ptr_Function_float %indexable_0 %34 + %37 = OpLoad %float %36 + %38 = OpLoad %float %FragColor + %39 = OpFAdd %float %38 %37 + OpStore %FragColor %39 + OpBranch %31 + %40 = OpLabel + %41 = OpLoad %int %index + %42 = OpBitwiseAnd %int %41 %int_1 + %44 = OpAccessChain %_ptr_Function_float %indexable_1 %42 + %45 = OpLoad %float %44 + %46 = OpLoad %float %FragColor + %47 = OpFAdd %float %46 %45 + OpStore %FragColor %47 + OpBranch %31 + %31 = OpLabel + %61 = OpLoad %int %index + %63 = OpSGreaterThan %bool %61 %int_30 + OpSelectionMerge %65 None + OpBranchConditional %63 %64 %74 + %64 = OpLabel + %66 = OpLoad %int %index + %68 = OpBitwiseAnd %int %66 %int_3 + %70 = OpAccessChain %_ptr_Function_float %foo %68 %uint_1 + %71 = OpLoad %float %70 + %72 = OpLoad %float %FragColor + %73 = OpFAdd %float %72 %71 + OpStore %FragColor %73 + OpBranch %65 + %74 = OpLabel + %75 = OpLoad %int %index + %76 = OpBitwiseAnd %int %75 %int_1 + %78 = OpAccessChain %_ptr_Function_float %foo %76 %uint_0 + %79 = OpLoad %float %78 + %80 = OpLoad %float %FragColor + %81 = OpFAdd %float %80 %79 + OpStore %FragColor %81 + OpBranch %65 + %65 = OpLabel + %83 = OpLoad %int %index + %84 = OpSGreaterThan %bool %83 %int_30 + OpSelectionMerge %86 None + OpBranchConditional %84 %85 %86 + %85 = OpLabel + %89 = OpAccessChain %_ptr_Function_float %foobar %int_1 %uint_2 + OpStore %89 %float_20 + OpBranch %86 + %86 = OpLabel + %90 = OpLoad %int %index + %91 = OpBitwiseAnd %int %90 %int_3 + %92 = OpAccessChain %_ptr_Function_float %foobar %91 %uint_2 + %93 = OpLoad %float %92 + %94 = OpLoad %float %FragColor + %95 = OpFAdd %float %94 %93 + OpStore %FragColor %95 + OpStore %baz %104 + %105 = OpLoad %int %index + %106 = OpBitwiseAnd %int %105 %int_3 + %107 = OpAccessChain %_ptr_Function_float %baz %106 %uint_2 + %108 = OpLoad %float %107 + %109 = OpLoad %float %FragColor + %110 = OpFAdd %float %109 %108 + OpStore %FragColor %110 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag new file mode 100644 index 0000000..43d0970 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/pack-and-unpack-uint2.fxconly.nofxc.sm60.asm.frag @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 34 +; Schema: 0 + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_ARB_gpu_shader_int64" + OpName %main "main" + OpName %packed "packed" + OpName %unpacked "unpacked" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %ulong = OpTypeInt 64 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %uint_18 = OpConstant %uint 18 + %uint_52 = OpConstant %uint 52 + %13 = OpConstantComposite %v2uint %uint_18 %uint_52 +%_ptr_Function_v2uint = OpTypePointer Function %v2uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %uint_0 = OpConstant %uint 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_1 = OpConstant %uint 1 + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %packed = OpVariable %_ptr_Function_ulong Function + %unpacked = OpVariable %_ptr_Function_v2uint Function + %14 = OpBitcast %ulong %13 + OpStore %packed %14 + %17 = OpLoad %ulong %packed + %18 = OpBitcast %v2uint %17 + OpStore %unpacked %18 + %25 = OpAccessChain %_ptr_Function_uint %unpacked %uint_0 + %26 = OpLoad %uint %25 + %27 = OpConvertUToF %float %26 + %29 = OpAccessChain %_ptr_Function_uint %unpacked %uint_1 + %30 = OpLoad %uint %29 + %31 = OpConvertUToF %float %30 + %33 = OpCompositeConstruct %v4float %27 %31 %float_1 %float_1 + OpStore %FragColor %33 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..083c85d --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 32 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %add_value_f1_f1_ "add_value(f1;f1;" + OpName %v "v" + OpName %w "w" + OpName %FragColor "FragColor" + OpName %Registers "Registers" + OpMemberName %Registers 0 "foo" + OpName %registers "registers" + OpDecorate %FragColor Location 0 + OpMemberDecorate %Registers 0 Offset 0 + OpDecorate %Registers Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %8 = OpTypeFunction %float %float %float +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %float_10 = OpConstant %float 10 + %Registers = OpTypeStruct %float +%_ptr_PushConstant_Registers = OpTypePointer PushConstant %Registers + %registers = OpVariable %_ptr_PushConstant_Registers PushConstant + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_float = OpTypePointer PushConstant %float + %main = OpFunction %void None %3 + %5 = OpLabel + %29 = OpAccessChain %_ptr_PushConstant_float %registers %int_0 + %30 = OpLoad %float %29 + %31 = OpFunctionCall %float %add_value_f1_f1_ %float_10 %30 + OpStore %FragColor %31 + OpReturn + OpFunctionEnd +%add_value_f1_f1_ = OpFunction %float None %8 + %v = OpFunctionParameter %float + %w = OpFunctionParameter %float + %12 = OpLabel + %15 = OpFAdd %float %v %w + OpReturnValue %15 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..16dcd0d --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 32 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %g_Texture "g_Texture" + OpName %type_sampler "type.sampler" + OpName %g_Sampler "g_Sampler" + OpName %g_CompareSampler "g_CompareSampler" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %g_Texture DescriptorSet 0 + OpDecorate %g_Texture Binding 0 + OpDecorate %g_Sampler DescriptorSet 0 + OpDecorate %g_Sampler Binding 0 + OpDecorate %g_CompareSampler DescriptorSet 0 + OpDecorate %g_CompareSampler Binding 1 + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %float_0 = OpConstant %float 0 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_float = OpTypePointer Output %float + %void = OpTypeVoid + %19 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + %v4float = OpTypeVector %float 4 + %g_Texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%g_CompareSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %19 + %21 = OpLabel + %22 = OpLoad %v2float %in_var_TEXCOORD0 + %23 = OpLoad %type_2d_image %g_Texture + %24 = OpLoad %type_sampler %g_Sampler + %25 = OpSampledImage %type_sampled_image %23 %24 + %26 = OpImageSampleImplicitLod %v4float %25 %22 None + %27 = OpCompositeExtract %float %26 0 + %28 = OpLoad %type_sampler %g_CompareSampler + %29 = OpSampledImage %type_sampled_image %23 %28 + %30 = OpImageSampleDrefExplicitLod %float %29 %22 %float_0_5 Lod %float_0 + %31 = OpFAdd %float %27 %30 + OpStore %out_var_SV_Target %31 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..0d5b29c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,86 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 54 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %o_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpName %main "main" + OpName %myType "myType" + OpMemberName %myType 0 "data" + OpName %myData "myData" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %index "index" + OpName %elt "elt" + OpName %o_color "o_color" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %o_color Location 0 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %float = OpTypeFloat 32 + %myType = OpTypeStruct %float + %uint = OpTypeInt 32 0 + %uint_5 = OpConstant %uint 5 +%_arr_myType_uint_5 = OpTypeArray %myType %uint_5 +%_ptr_Private__arr_myType_uint_5 = OpTypePointer Private %_arr_myType_uint_5 + %myData = OpVariable %_ptr_Private__arr_myType_uint_5 Private + %float_0 = OpConstant %float 0 + %18 = OpConstantComposite %myType %float_0 + %float_1 = OpConstant %float 1 + %20 = OpConstantComposite %myType %float_1 + %21 = OpConstantComposite %_arr_myType_uint_5 %18 %20 %18 %20 %18 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %float_4 = OpConstant %float 4 +%_ptr_Function_myType = OpTypePointer Function %myType +%_ptr_Private_myType = OpTypePointer Private %myType + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_Output_v4float = OpTypePointer Output %v4float + %o_color = OpVariable %_ptr_Output_v4float Output + %36 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1 + %37 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %main = OpFunction %void None %11 + %38 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %index = OpVariable %_ptr_Function_int Function + %elt = OpVariable %_ptr_Function_myType Function + OpStore %myData %21 + %39 = OpLoad %v4float %gl_FragCoord + %40 = OpVectorShuffle %v2float %39 %39 0 1 + OpStore %uv %40 + %41 = OpAccessChain %_ptr_Function_float %uv %uint_0 + %42 = OpLoad %float %41 + %43 = OpFMod %float %42 %float_4 + %44 = OpConvertFToS %int %43 + OpStore %index %44 + %45 = OpLoad %int %index + %46 = OpAccessChain %_ptr_Private_myType %myData %45 + %47 = OpLoad %myType %46 + OpStore %elt %47 + %48 = OpAccessChain %_ptr_Function_float %elt %int_0 + %49 = OpLoad %float %48 + %50 = OpFOrdGreaterThan %bool %49 %float_0 + OpSelectionMerge %51 None + OpBranchConditional %50 %52 %53 + %52 = OpLabel + OpStore %o_color %36 + OpBranch %51 + %53 = OpLabel + OpStore %o_color %37 + OpBranch %51 + %51 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/srem.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/srem.asm.frag new file mode 100644 index 0000000..c6f8e27 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/srem.asm.frag @@ -0,0 +1,43 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 23 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA Flat + OpDecorate %vA Location 0 + OpDecorate %vB Flat + OpDecorate %vB Location 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 +%_ptr_Input_v4int = OpTypePointer Input %v4int + %vA = OpVariable %_ptr_Input_v4int Input + %vB = OpVariable %_ptr_Input_v4int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpLoad %v4int %vA + %16 = OpLoad %v4int %vB + %17 = OpLoad %v4int %vA + %18 = OpLoad %v4int %vB + %19 = OpSRem %v4int %17 %18 + %20 = OpConvertSToF %v4float %19 + OpStore %FragColor %20 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..7763b7c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColors %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColors "FragColors" + OpName %FragColor "FragColor" + OpDecorate %FragColors Location 0 + OpDecorate %FragColor Location 2 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %17 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 + %float_10 = OpConstant %float 10 + %19 = OpConstantComposite %v4float %float_10 %float_10 %float_10 %float_10 + %20 = OpConstantComposite %_arr_v4float_uint_2 %17 %19 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %float_5 = OpConstant %float 5 + %24 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %FragColors = OpVariable %_ptr_Output__arr_v4float_uint_2 Output %20 + %FragColor = OpVariable %_ptr_Output_v4float Output %24 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..53dc638 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,46 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 26 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTexture "uTexture" + OpName %gl_FragCoord "gl_FragCoord" + OpDecorate %FragColor Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %uTexture = OpVariable %_ptr_UniformConstant_11 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpLoad %11 %uTexture + %18 = OpLoad %v4float %gl_FragCoord + %19 = OpVectorShuffle %v2float %18 %18 0 1 + %22 = OpConvertFToS %v2int %19 + %24 = OpImage %10 %14 + %25 = OpImageFetch %v4float %24 %22 + OpStore %FragColor %25 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag new file mode 100644 index 0000000..e7e6f37 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/texture-sampling-fp16.asm.frag @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 25 +; Schema: 0 + OpCapability Shader + OpCapability StorageInputOutput16 + OpCapability Float16 + OpExtension "SPV_KHR_16bit_storage" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %UV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types_float16" + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTexture "uTexture" + OpName %UV "UV" + OpDecorate %FragColor Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + OpDecorate %UV Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %half = OpTypeFloat 16 + %float = OpTypeFloat 32 + %v4half = OpTypeVector %half 4 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4half = OpTypePointer Output %v4half + %FragColor = OpVariable %_ptr_Output_v4half Output + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown + %12 = OpTypeSampledImage %11 +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_12 UniformConstant + %v2half = OpTypeVector %half 2 +%_ptr_Input_v2half = OpTypePointer Input %v2half + %UV = OpVariable %_ptr_Input_v2half Input + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpLoad %12 %uTexture + %19 = OpLoad %v2half %UV + %23 = OpImageSampleImplicitLod %v4float %15 %19 + %24 = OpFConvert %v4half %23 + OpStore %FragColor %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000..89036f0 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/shaders-hlsl/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..e2ce2eb --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/frag/unreachable.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 47 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %counter %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %counter "counter" + OpName %FragColor "FragColor" + OpDecorate %counter Flat + OpDecorate %counter Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %counter = OpVariable %_ptr_Input_int Input + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %float_10 = OpConstant %float 10 + %21 = OpConstantComposite %v4float %float_10 %float_10 %float_10 %float_10 + %float_30 = OpConstant %float 30 + %25 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %false = OpConstantFalse %bool + %44 = OpUndef %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %33 + %33 = OpLabel + %45 = OpPhi %v4float %44 %5 %44 %35 + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel + %37 = OpLoad %int %counter + %38 = OpIEqual %bool %37 %int_10 + OpSelectionMerge %39 None + OpBranchConditional %38 %40 %41 + %40 = OpLabel + OpBranch %34 + %41 = OpLabel + OpBranch %34 + %39 = OpLabel + OpUnreachable + %35 = OpLabel + OpBranchConditional %false %33 %34 + %34 = OpLabel + %46 = OpPhi %v4float %21 %40 %25 %41 %44 %35 + OpStore %FragColor %46 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..f51d903 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,141 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 79 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %VS "main" %PosL_1 %instanceID_1 %_entryPointOutput_Position %_entryPointOutput_Color + OpSource HLSL 500 + OpName %VS "VS" + OpName %V2F "V2F" + OpMemberName %V2F 0 "Position" + OpMemberName %V2F 1 "Color" + OpName %_VS_vf3_u1_ "@VS(vf3;u1;" + OpName %PosL "PosL" + OpName %instanceID "instanceID" + OpName %InstanceData "InstanceData" + OpMemberName %InstanceData 0 "MATRIX_MVP" + OpMemberName %InstanceData 1 "Color" + OpName %instData "instData" + OpName %InstanceData_0 "InstanceData" + OpMemberName %InstanceData_0 0 "MATRIX_MVP" + OpMemberName %InstanceData_0 1 "Color" + OpName %gInstanceData "gInstanceData" + OpMemberName %gInstanceData 0 "@data" + OpName %gInstanceData_0 "gInstanceData" + OpName %v2f "v2f" + OpName %PosL_0 "PosL" + OpName %PosL_1 "PosL" + OpName %instanceID_0 "instanceID" + OpName %instanceID_1 "instanceID" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %param_0 "param" + OpName %_entryPointOutput_Position "@entryPointOutput.Position" + OpName %_entryPointOutput_Color "@entryPointOutput.Color" + OpMemberDecorate %InstanceData_0 0 RowMajor + OpMemberDecorate %InstanceData_0 0 Offset 0 + OpMemberDecorate %InstanceData_0 0 MatrixStride 16 + OpMemberDecorate %InstanceData_0 1 Offset 64 + OpDecorate %_runtimearr_InstanceData_0 ArrayStride 80 + OpMemberDecorate %gInstanceData 0 Offset 0 + OpDecorate %gInstanceData Block + OpDecorate %gInstanceData_0 DescriptorSet 1 + OpDecorate %gInstanceData_0 Binding 0 + OpDecorate %PosL_1 Location 0 + OpDecorate %instanceID_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput_Position BuiltIn Position + OpDecorate %_entryPointOutput_Color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %uint = OpTypeInt 32 0 + %int_32 = OpConstant %uint 32 +%_ptr_Function_uint = OpTypePointer Function %uint + %v4float = OpTypeVector %float 4 + %V2F = OpTypeStruct %v4float %v4float + %13 = OpTypeFunction %V2F %_ptr_Function_v3float %_ptr_Function_uint +%mat4v4float = OpTypeMatrix %v4float 4 +%InstanceData = OpTypeStruct %mat4v4float %v4float +%_ptr_Function_InstanceData = OpTypePointer Function %InstanceData +%InstanceData_0 = OpTypeStruct %mat4v4float %v4float +%_runtimearr_InstanceData_0 = OpTypeArray %InstanceData_0 %int_32 +%gInstanceData = OpTypeStruct %_runtimearr_InstanceData_0 +%_ptr_Uniform_gInstanceData = OpTypePointer Uniform %gInstanceData +%gInstanceData_0 = OpVariable %_ptr_Uniform_gInstanceData Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_InstanceData_0 = OpTypePointer Uniform %InstanceData_0 +%_ptr_Function_mat4v4float = OpTypePointer Function %mat4v4float + %int_1 = OpConstant %int 1 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_V2F = OpTypePointer Function %V2F + %float_1 = OpConstant %float 1 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %PosL_1 = OpVariable %_ptr_Input_v3float Input +%_ptr_Input_uint = OpTypePointer Input %uint +%instanceID_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Position = OpVariable %_ptr_Output_v4float Output +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output + %VS = OpFunction %void None %3 + %5 = OpLabel + %PosL_0 = OpVariable %_ptr_Function_v3float Function +%instanceID_0 = OpVariable %_ptr_Function_uint Function +%flattenTemp = OpVariable %_ptr_Function_V2F Function + %param = OpVariable %_ptr_Function_v3float Function + %param_0 = OpVariable %_ptr_Function_uint Function + %61 = OpLoad %v3float %PosL_1 + OpStore %PosL_0 %61 + %65 = OpLoad %uint %instanceID_1 + OpStore %instanceID_0 %65 + %68 = OpLoad %v3float %PosL_0 + OpStore %param %68 + %70 = OpLoad %uint %instanceID_0 + OpStore %param_0 %70 + %71 = OpFunctionCall %V2F %_VS_vf3_u1_ %param %param_0 + OpStore %flattenTemp %71 + %74 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %75 = OpLoad %v4float %74 + OpStore %_entryPointOutput_Position %75 + %77 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_1 + %78 = OpLoad %v4float %77 + OpStore %_entryPointOutput_Color %78 + OpReturn + OpFunctionEnd +%_VS_vf3_u1_ = OpFunction %V2F None %13 + %PosL = OpFunctionParameter %_ptr_Function_v3float + %instanceID = OpFunctionParameter %_ptr_Function_uint + %17 = OpLabel + %instData = OpVariable %_ptr_Function_InstanceData Function + %v2f = OpVariable %_ptr_Function_V2F Function + %29 = OpLoad %uint %instanceID + %31 = OpAccessChain %_ptr_Uniform_InstanceData_0 %gInstanceData_0 %int_0 %29 + %32 = OpLoad %InstanceData_0 %31 + %33 = OpCompositeExtract %mat4v4float %32 0 + %35 = OpAccessChain %_ptr_Function_mat4v4float %instData %int_0 + OpStore %35 %33 + %36 = OpCompositeExtract %v4float %32 1 + %39 = OpAccessChain %_ptr_Function_v4float %instData %int_1 + OpStore %39 %36 + %42 = OpAccessChain %_ptr_Function_mat4v4float %instData %int_0 + %43 = OpLoad %mat4v4float %42 + %44 = OpLoad %v3float %PosL + %46 = OpCompositeExtract %float %44 0 + %47 = OpCompositeExtract %float %44 1 + %48 = OpCompositeExtract %float %44 2 + %49 = OpCompositeConstruct %v4float %46 %47 %48 %float_1 + %50 = OpMatrixTimesVector %v4float %43 %49 + %51 = OpAccessChain %_ptr_Function_v4float %v2f %int_0 + OpStore %51 %50 + %52 = OpAccessChain %_ptr_Function_v4float %instData %int_1 + %53 = OpLoad %v4float %52 + %54 = OpAccessChain %_ptr_Function_v4float %v2f %int_1 + OpStore %54 %53 + %55 = OpLoad %V2F %v2f + OpReturnValue %55 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert b/third_party/spirv-cross/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert new file mode 100644 index 0000000..b566a3d --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/vert/spec-constant-op-composite.asm.vert @@ -0,0 +1,98 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 58 +; Schema: 0 + OpCapability Shader + OpCapability ClipDistance + OpCapability CullDistance + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %52 %output + OpSource GLSL 450 + OpName %4 "main" + OpName %9 "pos" + OpName %50 "gl_PerVertex" + OpMemberName %50 0 "gl_Position" + OpMemberName %50 1 "gl_PointSize" + OpMemberName %50 2 "gl_ClipDistance" + OpMemberName %50 3 "gl_CullDistance" + OpName %52 "" + OpDecorate %13 SpecId 201 + OpDecorate %24 SpecId 202 + OpMemberDecorate %50 0 BuiltIn Position + OpMemberDecorate %50 1 BuiltIn PointSize + OpMemberDecorate %50 2 BuiltIn ClipDistance + OpMemberDecorate %50 3 BuiltIn CullDistance + OpDecorate %50 Block + OpDecorate %57 SpecId 200 + OpDecorate %output Flat + OpDecorate %output Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstantComposite %7 %10 %10 %10 %10 + %12 = OpTypeInt 32 1 + %int_ptr = OpTypePointer Output %12 + %13 = OpSpecConstant %12 -10 + %14 = OpConstant %12 2 + %15 = OpSpecConstantOp %12 IAdd %13 %14 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 1 + %19 = OpTypePointer Function %6 + %24 = OpSpecConstant %17 100 + %25 = OpConstant %17 5 + %26 = OpSpecConstantOp %17 UMod %24 %25 + %28 = OpConstant %17 2 + %33 = OpConstant %12 20 + %34 = OpConstant %12 30 + %35 = OpTypeVector %12 4 + %36 = OpSpecConstantComposite %35 %33 %34 %15 %15 + %40 = OpTypeVector %12 2 + %41 = OpSpecConstantOp %40 VectorShuffle %36 %36 1 0 + %foo = OpSpecConstantOp %12 CompositeExtract %36 1 + %42 = OpTypeVector %6 2 + %49 = OpTypeArray %6 %18 + %50 = OpTypeStruct %7 %6 %49 %49 + %51 = OpTypePointer Output %50 + %52 = OpVariable %51 Output + %output = OpVariable %int_ptr Output + %53 = OpConstant %12 0 + %55 = OpTypePointer Output %7 + %57 = OpSpecConstant %6 3.14159 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %11 + %16 = OpConvertSToF %6 %15 + %20 = OpAccessChain %19 %9 %18 + %21 = OpLoad %6 %20 + %22 = OpFAdd %6 %21 %16 + %23 = OpAccessChain %19 %9 %18 + OpStore %23 %22 + %27 = OpConvertUToF %6 %26 + %29 = OpAccessChain %19 %9 %28 + %30 = OpLoad %6 %29 + %31 = OpFAdd %6 %30 %27 + %32 = OpAccessChain %19 %9 %28 + OpStore %32 %31 + %37 = OpConvertSToF %7 %36 + %38 = OpLoad %7 %9 + %39 = OpFAdd %7 %38 %37 + OpStore %9 %39 + %43 = OpConvertSToF %42 %41 + %44 = OpLoad %7 %9 + %45 = OpVectorShuffle %42 %44 %44 0 1 + %46 = OpFAdd %42 %45 %43 + %47 = OpLoad %7 %9 + %48 = OpVectorShuffle %7 %47 %46 4 5 2 3 + OpStore %9 %48 + %54 = OpLoad %7 %9 + %56 = OpAccessChain %55 %52 %53 + OpStore %56 %54 + OpStore %output %foo + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..29b0076 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert b/third_party/spirv-cross/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..4587fc1 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/asm/vert/vertex-id-instance-id.asm.vert @@ -0,0 +1,53 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 26 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %gl_VertexID %gl_InstanceID + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpName %gl_VertexID "gl_VertexID" + OpName %gl_InstanceID "gl_InstanceID" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpDecorate %gl_VertexID BuiltIn VertexIndex + OpDecorate %gl_InstanceID BuiltIn InstanceIndex + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexID = OpVariable %_ptr_Input_int Input +%gl_InstanceID = OpVariable %_ptr_Input_int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpLoad %int %gl_VertexID + %20 = OpLoad %int %gl_InstanceID + %21 = OpIAdd %int %18 %20 + %22 = OpConvertSToF %float %21 + %23 = OpCompositeConstruct %v4float %22 %22 %22 %22 + %25 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %25 %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-hlsl/comp/access-chain-load-composite.comp b/third_party/spirv-cross/shaders-hlsl/comp/access-chain-load-composite.comp new file mode 100644 index 0000000..69cc7a1 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/access-chain-load-composite.comp @@ -0,0 +1,35 @@ +#version 450 +layout(local_size_x = 1) in; + +struct Baz +{ + float c; +}; + +struct Bar +{ + float d[2][4]; + Baz baz[2]; +}; + +struct Foo +{ + mat2 a; + vec2 b; + Bar c[5]; +}; + +layout(row_major, std430, set = 0, binding = 0) buffer SSBO +{ + Foo foo; + Foo foo2; +}; + +void main() +{ + Foo f = foo; + f.a += 1.0; + f.b += 2.0; + f.c[3].d[1][1] += 5.0; + foo2 = f; +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/access-chains.comp b/third_party/spirv-cross/shaders-hlsl/comp/access-chains.comp new file mode 100644 index 0000000..639f3ca --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/access-chains.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1) in; + +// TODO: Read structs, matrices and arrays. + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 a[3][2][4]; + float b[3][2][4]; + vec4 unsized[]; +} ro; + +layout(std430, binding = 1) writeonly buffer SSBO1 +{ + vec4 c[3][2][4]; + float d[3][2][4]; + vec4 unsized[]; +} wo; + +void main() +{ + wo.c[2][gl_GlobalInvocationID.x][1] = ro.a[1][gl_GlobalInvocationID.x][2]; + wo.unsized[gl_GlobalInvocationID.x] = ro.unsized[gl_GlobalInvocationID.x]; +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/access-chains.force-uav.comp b/third_party/spirv-cross/shaders-hlsl/comp/access-chains.force-uav.comp new file mode 100644 index 0000000..639f3ca --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/access-chains.force-uav.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1) in; + +// TODO: Read structs, matrices and arrays. + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 a[3][2][4]; + float b[3][2][4]; + vec4 unsized[]; +} ro; + +layout(std430, binding = 1) writeonly buffer SSBO1 +{ + vec4 c[3][2][4]; + float d[3][2][4]; + vec4 unsized[]; +} wo; + +void main() +{ + wo.c[2][gl_GlobalInvocationID.x][1] = ro.a[1][gl_GlobalInvocationID.x][2]; + wo.unsized[gl_GlobalInvocationID.x] = ro.unsized[gl_GlobalInvocationID.x]; +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/address-buffers.comp b/third_party/spirv-cross/shaders-hlsl/comp/address-buffers.comp new file mode 100644 index 0000000..3ba582c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/address-buffers.comp @@ -0,0 +1,23 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 0, std430) readonly buffer ReadOnlyBuffer +{ + vec4 ro; +} ReadOnly; + +layout(binding = 1, std430) buffer ReadWriteBuffer +{ + vec4 rw; +} ReadWrite; + +layout(binding = 2, std430) buffer WriteOnlyBuffer +{ + vec4 wo; +} WriteOnly; + +void main() +{ + WriteOnly.wo = ReadOnly.ro; + ReadWrite.rw += 10.0; +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/atomic.comp b/third_party/spirv-cross/shaders-hlsl/comp/atomic.comp new file mode 100644 index 0000000..6f69ec7 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/atomic.comp @@ -0,0 +1,66 @@ +#version 310 es +#extension GL_OES_shader_image_atomic : require +layout(local_size_x = 1) in; + +layout(r32ui, binding = 0) uniform highp uimage2D uImage; +layout(r32i, binding = 1) uniform highp iimage2D iImage; +layout(binding = 2, std430) buffer SSBO +{ + uint u32; + int i32; +} ssbo; + +shared int int_atomic; +shared uint uint_atomic; +shared int int_atomic_array[1]; +shared uint uint_atomic_array[1]; + +void main() +{ + imageAtomicAdd(uImage, ivec2(1, 5), 1u); + + // Test that we do not invalidate OpImage variables which are loaded from UniformConstant + // address space. + imageStore(iImage, ivec2(1, 6), ivec4(imageAtomicAdd(uImage, ivec2(1, 5), 1u))); + + imageAtomicOr(uImage, ivec2(1, 5), 1u); + imageAtomicXor(uImage, ivec2(1, 5), 1u); + imageAtomicAnd(uImage, ivec2(1, 5), 1u); + imageAtomicMin(uImage, ivec2(1, 5), 1u); + imageAtomicMax(uImage, ivec2(1, 5), 1u); + //imageAtomicExchange(uImage, ivec2(1, 5), 1u); + imageAtomicCompSwap(uImage, ivec2(1, 5), 10u, 2u); + + imageAtomicAdd(iImage, ivec2(1, 6), 1); + imageAtomicOr(iImage, ivec2(1, 6), 1); + imageAtomicXor(iImage, ivec2(1, 6), 1); + imageAtomicAnd(iImage, ivec2(1, 6), 1); + imageAtomicMin(iImage, ivec2(1, 6), 1); + imageAtomicMax(iImage, ivec2(1, 6), 1); + //imageAtomicExchange(iImage, ivec2(1, 5), 1u); + imageAtomicCompSwap(iImage, ivec2(1, 5), 10, 2); + + atomicAdd(ssbo.u32, 1u); + atomicOr(ssbo.u32, 1u); + atomicXor(ssbo.u32, 1u); + atomicAnd(ssbo.u32, 1u); + atomicMin(ssbo.u32, 1u); + atomicMax(ssbo.u32, 1u); + atomicExchange(ssbo.u32, 1u); + atomicCompSwap(ssbo.u32, 10u, 2u); + + atomicAdd(ssbo.i32, 1); + atomicOr(ssbo.i32, 1); + atomicXor(ssbo.i32, 1); + atomicAnd(ssbo.i32, 1); + atomicMin(ssbo.i32, 1); + atomicMax(ssbo.i32, 1); + atomicExchange(ssbo.i32, 1); + atomicCompSwap(ssbo.i32, 10, 2); + + atomicAdd(int_atomic, 10); + atomicAdd(uint_atomic, 10u); + atomicAdd(int_atomic_array[0], 10); + atomicAdd(uint_atomic_array[0], 10u); +} + diff --git a/third_party/spirv-cross/shaders-hlsl/comp/barriers.comp b/third_party/spirv-cross/shaders-hlsl/comp/barriers.comp new file mode 100644 index 0000000..7e0ea42 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/barriers.comp @@ -0,0 +1,79 @@ +#version 310 es +layout(local_size_x = 4) in; + +void barrier_shared() +{ + memoryBarrierShared(); +} + +void full_barrier() +{ + memoryBarrier(); +} + +void image_barrier() +{ + memoryBarrierImage(); +} + +void buffer_barrier() +{ + memoryBarrierBuffer(); +} + +void group_barrier() +{ + groupMemoryBarrier(); +} + +void barrier_shared_exec() +{ + memoryBarrierShared(); + barrier(); +} + +void full_barrier_exec() +{ + memoryBarrier(); + barrier(); +} + +void image_barrier_exec() +{ + memoryBarrierImage(); + barrier(); +} + +void buffer_barrier_exec() +{ + memoryBarrierBuffer(); + barrier(); +} + +void group_barrier_exec() +{ + groupMemoryBarrier(); + barrier(); +} + +void exec_barrier() +{ + barrier(); +} + +void main() +{ + barrier_shared(); + full_barrier(); + image_barrier(); + buffer_barrier(); + group_barrier(); + + barrier_shared_exec(); + full_barrier_exec(); + image_barrier_exec(); + buffer_barrier_exec(); + group_barrier_exec(); + + exec_barrier(); +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/builtins.comp b/third_party/spirv-cross/shaders-hlsl/comp/builtins.comp new file mode 100644 index 0000000..b41cb53 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/builtins.comp @@ -0,0 +1,11 @@ +#version 310 es +layout(local_size_x = 8, local_size_y = 4, local_size_z = 2) in; + +void main() +{ + uvec3 local_id = gl_LocalInvocationID; + uvec3 global_id = gl_GlobalInvocationID; + uint local_index = gl_LocalInvocationIndex; + uvec3 work_group_size = gl_WorkGroupSize; + uvec3 work_group_id = gl_WorkGroupID; +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/composite-array-initialization.comp b/third_party/spirv-cross/shaders-hlsl/comp/composite-array-initialization.comp new file mode 100644 index 0000000..319f466 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/composite-array-initialization.comp @@ -0,0 +1,29 @@ +#version 450 +layout(local_size_x = 2) in; + +struct Data +{ + float a; + float b; +}; + +layout(std430, binding = 0) buffer SSBO +{ + Data outdata[]; +}; + +layout(constant_id = 0) const float X = 4.0; + +Data data[2] = Data[](Data(1.0, 2.0), Data(3.0, 4.0)); +Data data2[2] = Data[](Data(X, 2.0), Data(3.0, 5.0)); + +Data combine(Data a, Data b) +{ + return Data(a.a + b.a, a.b + b.b); +} + +void main() +{ + if (gl_LocalInvocationIndex == 0u) + outdata[gl_WorkGroupID.x] = combine(data[gl_LocalInvocationID.x], data2[gl_LocalInvocationID.x]); +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/globallycoherent.comp b/third_party/spirv-cross/shaders-hlsl/comp/globallycoherent.comp new file mode 100644 index 0000000..168b940 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/globallycoherent.comp @@ -0,0 +1,25 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(r32f, binding = 0) uniform readonly image2D uImageIn; +layout(r32f, binding = 1) uniform coherent writeonly image2D uImageOut; + +layout(set = 0, binding = 2) readonly buffer Foo +{ + float foo; +}; + +layout(set = 0, binding = 3) coherent writeonly buffer Bar +{ + float bar; +}; + +void main() +{ + ivec2 coord = ivec2(9, 7); + vec4 indata = imageLoad(uImageIn, coord); + imageStore(uImageOut, coord, indata); + + bar = foo; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/comp/image.comp b/third_party/spirv-cross/shaders-hlsl/comp/image.comp new file mode 100644 index 0000000..1d3c8b4 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/image.comp @@ -0,0 +1,77 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(r32f, binding = 0) uniform readonly image2D uImageInF; +layout(r32f, binding = 1) uniform writeonly image2D uImageOutF; +layout(r32i, binding = 2) uniform readonly iimage2D uImageInI; +layout(r32i, binding = 3) uniform writeonly iimage2D uImageOutI; +layout(r32ui, binding = 4) uniform readonly uimage2D uImageInU; +layout(r32ui, binding = 5) uniform writeonly uimage2D uImageOutU; +layout(r32f, binding = 6) uniform readonly imageBuffer uImageInBuffer; +layout(r32f, binding = 7) uniform writeonly imageBuffer uImageOutBuffer; + +layout(rg32f, binding = 8) uniform readonly image2D uImageInF2; +layout(rg32f, binding = 9) uniform writeonly image2D uImageOutF2; +layout(rg32i, binding = 10) uniform readonly iimage2D uImageInI2; +layout(rg32i, binding = 11) uniform writeonly iimage2D uImageOutI2; +layout(rg32ui, binding = 12) uniform readonly uimage2D uImageInU2; +layout(rg32ui, binding = 13) uniform writeonly uimage2D uImageOutU2; +layout(rg32f, binding = 14) uniform readonly imageBuffer uImageInBuffer2; +layout(rg32f, binding = 15) uniform writeonly imageBuffer uImageOutBuffer2; + +layout(rgba32f, binding = 16) uniform readonly image2D uImageInF4; +layout(rgba32f, binding = 17) uniform writeonly image2D uImageOutF4; +layout(rgba32i, binding = 18) uniform readonly iimage2D uImageInI4; +layout(rgba32i, binding = 19) uniform writeonly iimage2D uImageOutI4; +layout(rgba32ui, binding = 20) uniform readonly uimage2D uImageInU4; +layout(rgba32ui, binding = 21) uniform writeonly uimage2D uImageOutU4; +layout(rgba32f, binding = 22) uniform readonly imageBuffer uImageInBuffer4; +layout(rgba32f, binding = 23) uniform writeonly imageBuffer uImageOutBuffer4; + +layout(binding = 24) uniform writeonly image2D uImageNoFmtF; +layout(binding = 25) uniform writeonly uimage2D uImageNoFmtU; +layout(binding = 26) uniform writeonly iimage2D uImageNoFmtI; + +void main() +{ + vec4 f = imageLoad(uImageInF, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutF, ivec2(gl_GlobalInvocationID.xy), f); + + ivec4 i = imageLoad(uImageInI, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutI, ivec2(gl_GlobalInvocationID.xy), i); + + uvec4 u = imageLoad(uImageInU, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutU, ivec2(gl_GlobalInvocationID.xy), u); + + vec4 b = imageLoad(uImageInBuffer, int(gl_GlobalInvocationID.x)); + imageStore(uImageOutBuffer, int(gl_GlobalInvocationID.x), b); + + vec4 f2 = imageLoad(uImageInF2, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutF2, ivec2(gl_GlobalInvocationID.xy), f2); + + ivec4 i2 = imageLoad(uImageInI2, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutI2, ivec2(gl_GlobalInvocationID.xy), i2); + + uvec4 u2 = imageLoad(uImageInU2, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutU2, ivec2(gl_GlobalInvocationID.xy), u2); + + vec4 b2 = imageLoad(uImageInBuffer2, int(gl_GlobalInvocationID.x)); + imageStore(uImageOutBuffer2, int(gl_GlobalInvocationID.x), b2); + + vec4 f4 = imageLoad(uImageInF4, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutF4, ivec2(gl_GlobalInvocationID.xy), f4); + + ivec4 i4 = imageLoad(uImageInI4, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutI4, ivec2(gl_GlobalInvocationID.xy), i4); + + uvec4 u4 = imageLoad(uImageInU4, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutU4, ivec2(gl_GlobalInvocationID.xy), u4); + + vec4 b4 = imageLoad(uImageInBuffer4, int(gl_GlobalInvocationID.x)); + imageStore(uImageOutBuffer4, int(gl_GlobalInvocationID.x), b4); + + imageStore(uImageNoFmtF, ivec2(gl_GlobalInvocationID.xy), b2); + imageStore(uImageNoFmtU, ivec2(gl_GlobalInvocationID.xy), u4); + imageStore(uImageNoFmtI, ivec2(gl_GlobalInvocationID.xy), i4); +} + diff --git a/third_party/spirv-cross/shaders-hlsl/comp/image.nonwritable-uav-texture.comp b/third_party/spirv-cross/shaders-hlsl/comp/image.nonwritable-uav-texture.comp new file mode 100644 index 0000000..1d3c8b4 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/image.nonwritable-uav-texture.comp @@ -0,0 +1,77 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(r32f, binding = 0) uniform readonly image2D uImageInF; +layout(r32f, binding = 1) uniform writeonly image2D uImageOutF; +layout(r32i, binding = 2) uniform readonly iimage2D uImageInI; +layout(r32i, binding = 3) uniform writeonly iimage2D uImageOutI; +layout(r32ui, binding = 4) uniform readonly uimage2D uImageInU; +layout(r32ui, binding = 5) uniform writeonly uimage2D uImageOutU; +layout(r32f, binding = 6) uniform readonly imageBuffer uImageInBuffer; +layout(r32f, binding = 7) uniform writeonly imageBuffer uImageOutBuffer; + +layout(rg32f, binding = 8) uniform readonly image2D uImageInF2; +layout(rg32f, binding = 9) uniform writeonly image2D uImageOutF2; +layout(rg32i, binding = 10) uniform readonly iimage2D uImageInI2; +layout(rg32i, binding = 11) uniform writeonly iimage2D uImageOutI2; +layout(rg32ui, binding = 12) uniform readonly uimage2D uImageInU2; +layout(rg32ui, binding = 13) uniform writeonly uimage2D uImageOutU2; +layout(rg32f, binding = 14) uniform readonly imageBuffer uImageInBuffer2; +layout(rg32f, binding = 15) uniform writeonly imageBuffer uImageOutBuffer2; + +layout(rgba32f, binding = 16) uniform readonly image2D uImageInF4; +layout(rgba32f, binding = 17) uniform writeonly image2D uImageOutF4; +layout(rgba32i, binding = 18) uniform readonly iimage2D uImageInI4; +layout(rgba32i, binding = 19) uniform writeonly iimage2D uImageOutI4; +layout(rgba32ui, binding = 20) uniform readonly uimage2D uImageInU4; +layout(rgba32ui, binding = 21) uniform writeonly uimage2D uImageOutU4; +layout(rgba32f, binding = 22) uniform readonly imageBuffer uImageInBuffer4; +layout(rgba32f, binding = 23) uniform writeonly imageBuffer uImageOutBuffer4; + +layout(binding = 24) uniform writeonly image2D uImageNoFmtF; +layout(binding = 25) uniform writeonly uimage2D uImageNoFmtU; +layout(binding = 26) uniform writeonly iimage2D uImageNoFmtI; + +void main() +{ + vec4 f = imageLoad(uImageInF, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutF, ivec2(gl_GlobalInvocationID.xy), f); + + ivec4 i = imageLoad(uImageInI, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutI, ivec2(gl_GlobalInvocationID.xy), i); + + uvec4 u = imageLoad(uImageInU, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutU, ivec2(gl_GlobalInvocationID.xy), u); + + vec4 b = imageLoad(uImageInBuffer, int(gl_GlobalInvocationID.x)); + imageStore(uImageOutBuffer, int(gl_GlobalInvocationID.x), b); + + vec4 f2 = imageLoad(uImageInF2, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutF2, ivec2(gl_GlobalInvocationID.xy), f2); + + ivec4 i2 = imageLoad(uImageInI2, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutI2, ivec2(gl_GlobalInvocationID.xy), i2); + + uvec4 u2 = imageLoad(uImageInU2, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutU2, ivec2(gl_GlobalInvocationID.xy), u2); + + vec4 b2 = imageLoad(uImageInBuffer2, int(gl_GlobalInvocationID.x)); + imageStore(uImageOutBuffer2, int(gl_GlobalInvocationID.x), b2); + + vec4 f4 = imageLoad(uImageInF4, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutF4, ivec2(gl_GlobalInvocationID.xy), f4); + + ivec4 i4 = imageLoad(uImageInI4, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutI4, ivec2(gl_GlobalInvocationID.xy), i4); + + uvec4 u4 = imageLoad(uImageInU4, ivec2(gl_GlobalInvocationID.xy)); + imageStore(uImageOutU4, ivec2(gl_GlobalInvocationID.xy), u4); + + vec4 b4 = imageLoad(uImageInBuffer4, int(gl_GlobalInvocationID.x)); + imageStore(uImageOutBuffer4, int(gl_GlobalInvocationID.x), b4); + + imageStore(uImageNoFmtF, ivec2(gl_GlobalInvocationID.xy), b2); + imageStore(uImageNoFmtU, ivec2(gl_GlobalInvocationID.xy), u4); + imageStore(uImageNoFmtI, ivec2(gl_GlobalInvocationID.xy), i4); +} + diff --git a/third_party/spirv-cross/shaders-hlsl/comp/inverse.comp b/third_party/spirv-cross/shaders-hlsl/comp/inverse.comp new file mode 100644 index 0000000..03b06d6 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/inverse.comp @@ -0,0 +1,23 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std430, binding = 0) writeonly buffer MatrixOut +{ + mat2 m2out; + mat3 m3out; + mat4 m4out; +}; + +layout(std430, binding = 1) readonly buffer MatrixIn +{ + mat2 m2in; + mat3 m3in; + mat4 m4in; +}; + +void main() +{ + m2out = inverse(m2in); + m3out = inverse(m3in); + m4out = inverse(m4in); +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/num-workgroups-alone.comp b/third_party/spirv-cross/shaders-hlsl/comp/num-workgroups-alone.comp new file mode 100644 index 0000000..10b5817 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/num-workgroups-alone.comp @@ -0,0 +1,13 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + uvec3 outdata; +}; + +void main() +{ + outdata = gl_NumWorkGroups; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/comp/num-workgroups-with-builtins.comp b/third_party/spirv-cross/shaders-hlsl/comp/num-workgroups-with-builtins.comp new file mode 100644 index 0000000..d19a06c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/num-workgroups-with-builtins.comp @@ -0,0 +1,13 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + uvec3 outdata; +}; + +void main() +{ + outdata = gl_NumWorkGroups + gl_WorkGroupID; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/comp/outer-product.comp b/third_party/spirv-cross/shaders-hlsl/comp/outer-product.comp new file mode 100644 index 0000000..9aba2a5 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/outer-product.comp @@ -0,0 +1,37 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, std430) writeonly buffer SSBO +{ + mat2 m22; + mat2x3 m23; + mat2x4 m24; + mat3x2 m32; + mat3 m33; + mat3x4 m34; + mat4x2 m42; + mat4x3 m43; + mat4 m44; +}; + +layout(set = 0, binding = 1, std430) readonly buffer ReadSSBO +{ + vec2 v2; + vec3 v3; + vec4 v4; +}; + +void main() +{ + m22 = outerProduct(v2, v2); + m23 = outerProduct(v3, v2); + m24 = outerProduct(v4, v2); + + m32 = outerProduct(v2, v3); + m33 = outerProduct(v3, v3); + m34 = outerProduct(v4, v3); + + m42 = outerProduct(v2, v4); + m43 = outerProduct(v3, v4); + m44 = outerProduct(v4, v4); +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/rmw-matrix.comp b/third_party/spirv-cross/shaders-hlsl/comp/rmw-matrix.comp new file mode 100644 index 0000000..c158ab4 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/rmw-matrix.comp @@ -0,0 +1,20 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float a; + vec4 b; + mat4 c; + + float a1; + vec4 b1; + mat4 c1; +}; + +void main() +{ + a *= a1; + b *= b1; + c *= c1; +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/rwbuffer-matrix.comp b/third_party/spirv-cross/shaders-hlsl/comp/rwbuffer-matrix.comp new file mode 100644 index 0000000..0e722e0 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/rwbuffer-matrix.comp @@ -0,0 +1,104 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std140, binding = 1) uniform UBO +{ + int index0; + int index1; +}; + +layout(binding = 0, std430) buffer SSBO +{ + layout(column_major) mat4 mcol; + layout(row_major) mat4 mrow; + + layout(column_major) mat2 mcol2x2; + layout(row_major) mat2 mrow2x2; + + layout(column_major) mat2x3 mcol2x3; + layout(row_major) mat2x3 mrow2x3; + + layout(column_major) mat3x2 mcol3x2; + layout(row_major) mat3x2 mrow3x2; +}; + +void col_to_row() +{ + // Load column-major, store row-major. + mrow = mcol; + mrow2x2 = mcol2x2; + mrow2x3 = mcol2x3; + mrow3x2 = mcol3x2; +} + +void row_to_col() +{ + // Load row-major, store column-major. + mcol = mrow; + mcol2x2 = mrow2x2; + mcol2x3 = mrow2x3; + mcol3x2 = mrow3x2; +} + +void write_dynamic_index_row() +{ + mrow[index0][index1] = 1.0; + mrow2x2[index0][index1] = 2.0; + mrow2x3[index0][index1] = 3.0; + mrow3x2[index0][index1] = 4.0; + + mrow[index0] = vec4(1.0); + mrow2x2[index0] = vec2(2.0); + mrow2x3[index0] = vec3(3.0); + mrow3x2[index0] = vec2(4.0); +} + +void write_dynamic_index_col() +{ + mcol[index0][index1] = 1.0; + mcol2x2[index0][index1] = 2.0; + mcol2x3[index0][index1] = 3.0; + mcol3x2[index0][index1] = 4.0; + + mcol[index0] = vec4(1.0); + mcol2x2[index0] = vec2(2.0); + mcol2x3[index0] = vec3(3.0); + mcol3x2[index0] = vec2(4.0); +} + +void read_dynamic_index_row() +{ + float a0 = mrow[index0][index1]; + float a1 = mrow2x2[index0][index1]; + float a2 = mrow2x3[index0][index1]; + float a3 = mrow3x2[index0][index1]; + + vec4 v0 = mrow[index0]; + vec2 v1 = mrow2x2[index0]; + vec3 v2 = mrow2x3[index0]; + vec2 v3 = mrow3x2[index0]; +} + +void read_dynamic_index_col() +{ + float a0 = mcol[index0][index1]; + float a1 = mcol2x2[index0][index1]; + float a2 = mcol2x3[index0][index1]; + float a3 = mcol3x2[index0][index1]; + + vec4 v0 = mcol[index0]; + vec2 v1 = mcol2x2[index0]; + vec3 v2 = mcol2x3[index0]; + vec2 v3 = mcol3x2[index0]; +} + +void main() +{ + row_to_col(); + col_to_row(); + write_dynamic_index_row(); + write_dynamic_index_col(); + read_dynamic_index_row(); + read_dynamic_index_col(); +} + diff --git a/third_party/spirv-cross/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..3741473 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,18 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std430, set = 0, binding = 0) buffer SSBO +{ + float a; + float b; + float c; + float d; + float e; +}; + +void main() +{ + c = distance(a, b); + d = length(a); + e = normalize(a); +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/shared.comp b/third_party/spirv-cross/shaders-hlsl/comp/shared.comp new file mode 100644 index 0000000..4deff93 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/shared.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 4) in; + +shared float sShared[gl_WorkGroupSize.x]; + +layout(std430, binding = 0) readonly buffer SSBO +{ + float in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + float out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + float idata = in_data[ident]; + + sShared[gl_LocalInvocationIndex] = idata; + memoryBarrierShared(); + barrier(); + + out_data[ident] = sShared[gl_WorkGroupSize.x - gl_LocalInvocationIndex - 1u]; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/comp/spec-constant-op-member-array.comp b/third_party/spirv-cross/shaders-hlsl/comp/spec-constant-op-member-array.comp new file mode 100644 index 0000000..0b428eb --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/spec-constant-op-member-array.comp @@ -0,0 +1,33 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(constant_id = 0) const int a = 100; +layout(constant_id = 1) const int b = 200; +layout(constant_id = 2) const int c = 300; +const int d = c + 50; +layout(constant_id = 3) const int e = 400; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +layout(set = 1, binding = 0) buffer SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +}; + +void main() +{ + w[gl_GlobalInvocationID.x] += v[gl_GlobalInvocationID.x] + e; +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/spec-constant-work-group-size.comp b/third_party/spirv-cross/shaders-hlsl/comp/spec-constant-work-group-size.comp new file mode 100644 index 0000000..c86097e --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/spec-constant-work-group-size.comp @@ -0,0 +1,19 @@ +#version 450 +layout(local_size_x_id = 10, local_size_y = 20) in; + +layout(constant_id = 0) const int a = 1; +layout(constant_id = 1) const int b = 2; + +layout(set = 1, binding = 0) writeonly buffer SSBO +{ + int v[]; +}; + +void main() +{ + int spec_const_array_size[b]; + spec_const_array_size[0] = 10; + spec_const_array_size[1] = 40; + spec_const_array_size[a] = a; + v[a + gl_WorkGroupSize.x + gl_WorkGroupSize.y + gl_GlobalInvocationID.x] = b + spec_const_array_size[1 - a]; +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/ssbo-array-length.comp b/third_party/spirv-cross/shaders-hlsl/comp/ssbo-array-length.comp new file mode 100644 index 0000000..3ad4b95 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/ssbo-array-length.comp @@ -0,0 +1,12 @@ +#version 450 +layout(local_size_x = 1) in; +layout(set = 0, binding = 1, std140) buffer SSBO +{ + uint size; + float v[]; +}; + +void main() +{ + size = v.length(); +} diff --git a/third_party/spirv-cross/shaders-hlsl/comp/ssbo-array.comp b/third_party/spirv-cross/shaders-hlsl/comp/ssbo-array.comp new file mode 100644 index 0000000..38b56e9 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/comp/ssbo-array.comp @@ -0,0 +1,29 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + vec4 a; +} ssbo0; + +// Does not seem to work in glslang yet in HLSL output, disable for now. +#if 0 +layout(binding = 1, std430) buffer SSBO1 +{ + vec4 b; +} ssbo1[2]; + +layout(binding = 2, std430) buffer SSBO2 +{ + vec4 c; +} ssbo2[3][3]; +#endif + +void main() +{ +#if 0 + ssbo1[1].b = ssbo0.a; + ssbo2[1][2].c = ssbo0.a; +#endif +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/shaders-hlsl/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..3493e0c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 v0; + +void main() +{ + float lut[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); + for (int i = 0; i < 4; i++, FragColor += lut[i]) + { + } +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/basic-color-3comp.sm30.frag b/third_party/spirv-cross/shaders-hlsl/frag/basic-color-3comp.sm30.frag new file mode 100644 index 0000000..64211b6 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/basic-color-3comp.sm30.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec3 FragColor; + +void main() +{ + FragColor = vColor.xyz; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/basic-color-3comp.sm50.frag b/third_party/spirv-cross/shaders-hlsl/frag/basic-color-3comp.sm50.frag new file mode 100644 index 0000000..64211b6 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/basic-color-3comp.sm50.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec3 FragColor; + +void main() +{ + FragColor = vColor.xyz; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/basic.frag b/third_party/spirv-cross/shaders-hlsl/frag/basic.frag new file mode 100644 index 0000000..dd9a8f8 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/basic.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor * texture(uTex, vTex); +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/bit-conversions.frag b/third_party/spirv-cross/shaders-hlsl/frag/bit-conversions.frag new file mode 100644 index 0000000..faacdc0 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/bit-conversions.frag @@ -0,0 +1,12 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec2 value; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + int i = floatBitsToInt(value.x); + FragColor = vec4(1.0, 0.0, intBitsToFloat(i), 1.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/boolean-mix.frag b/third_party/spirv-cross/shaders-hlsl/frag/boolean-mix.frag new file mode 100644 index 0000000..9fd8ab3 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/boolean-mix.frag @@ -0,0 +1,10 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec2 x0; +layout(location = 0) out vec2 FragColor; + +void main() +{ + FragColor = x0.x > x0.y ? vec2(1.0, 0.0) : vec2(0.0, 1.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/builtins.frag b/third_party/spirv-cross/shaders-hlsl/frag/builtins.frag new file mode 100644 index 0000000..99e6e2d --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/builtins.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = gl_FragCoord + vColor; + gl_FragDepth = 0.5; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/bvec-operations.frag b/third_party/spirv-cross/shaders-hlsl/frag/bvec-operations.frag new file mode 100644 index 0000000..7221604 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/bvec-operations.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec2 value; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + bvec2 bools1 = not(bvec2(value.x == 0.0, value.y == 0.0)); + bvec2 bools2 = lessThanEqual(value, vec2(1.5, 0.5)); + FragColor = vec4(1.0, 0.0, bools1.x ? 1.0 : 0.0, bools2.x ? 1.0 : 0.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/clip-cull-distance.frag b/third_party/spirv-cross/shaders-hlsl/frag/clip-cull-distance.frag new file mode 100644 index 0000000..625a7da --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/clip-cull-distance.frag @@ -0,0 +1,12 @@ +#version 450 + +in float gl_ClipDistance[2]; +in float gl_CullDistance[1]; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = gl_ClipDistance[0] + gl_CullDistance[0] + gl_ClipDistance[1]; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/combined-texture-sampler-parameter.frag b/third_party/spirv-cross/shaders-hlsl/frag/combined-texture-sampler-parameter.frag new file mode 100644 index 0000000..e5721b9 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/combined-texture-sampler-parameter.frag @@ -0,0 +1,31 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler2D uSampler; +layout(set = 0, binding = 1) uniform mediump sampler2DShadow uSamplerShadow; +layout(location = 0) out float FragColor; + +vec4 samp2(sampler2D s) +{ + return texture(s, vec2(1.0)) + texelFetch(s, ivec2(10), 0); +} + +vec4 samp3(sampler2D s) +{ + return samp2(s); +} + +float samp4(mediump sampler2DShadow s) +{ + return texture(s, vec3(1.0)); +} + +float samp(sampler2D s0, mediump sampler2DShadow s1) +{ + return samp3(s0).x + samp4(s1); +} + +void main() +{ + FragColor = samp(uSampler, uSamplerShadow); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/combined-texture-sampler-shadow.frag b/third_party/spirv-cross/shaders-hlsl/frag/combined-texture-sampler-shadow.frag new file mode 100644 index 0000000..2fabb5e --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/combined-texture-sampler-shadow.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform texture2D uDepth; +layout(location = 0) out float FragColor; + +float samp2(texture2D t, mediump samplerShadow s) +{ + return texture(sampler2DShadow(t, s), vec3(1.0)); +} + +float samp3(texture2D t, mediump sampler s) +{ + return texture(sampler2D(t, s), vec2(1.0)).x; +} + +float samp(texture2D t, mediump samplerShadow s, mediump sampler s1) +{ + float r0 = samp2(t, s); + float r1 = samp3(t, s1); + return r0 + r1; +} + +void main() +{ + FragColor = samp(uDepth, uSampler, uSampler1); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/shaders-hlsl/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..47f9393 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; + +struct Foo +{ + vec4 a; + vec4 b; +}; + +layout(binding = 0) buffer UBO +{ + vec4 results[1024]; +}; + +layout(binding = 1) uniform highp isampler2D Buf; +layout(location = 0) flat in int vIn; +layout(location = 1) flat in int vIn2; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + ivec4 coords = texelFetch(Buf, ivec2(gl_FragCoord.xy), 0); + vec4 foo = results[coords.x % 16]; + + int c = vIn * vIn; + int d = vIn2 * vIn2; + FragColor = foo + foo + results[c + d]; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/constant-composites.frag b/third_party/spirv-cross/shaders-hlsl/frag/constant-composites.frag new file mode 100644 index 0000000..a12e22f --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/constant-composites.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; + +float lut[4] = float[](1.0, 4.0, 3.0, 2.0); + +struct Foo +{ + float a; + float b; +}; +Foo foos[2] = Foo[](Foo(10.0, 20.0), Foo(30.0, 40.0)); + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in int line; + +void main() +{ + FragColor = vec4(lut[line]); + FragColor += foos[line].a * foos[1 - line].a; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..7c75ffe --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,36 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uSampler; +layout(location = 0) in vec4 vInput; + +void main() +{ + FragColor = vInput; + vec4 t = texture(uSampler, vInput.xy); + vec4 d0 = dFdx(vInput); + vec4 d1 = dFdy(vInput); + vec4 d2 = fwidth(vInput); + vec4 d3 = dFdxCoarse(vInput); + vec4 d4 = dFdyCoarse(vInput); + vec4 d5 = fwidthCoarse(vInput); + vec4 d6 = dFdxFine(vInput); + vec4 d7 = dFdyFine(vInput); + vec4 d8 = fwidthFine(vInput); + vec2 lod = textureQueryLod(uSampler, vInput.zw); + if (vInput.y > 10.0) + { + FragColor += t; + FragColor += d0; + FragColor += d1; + FragColor += d2; + FragColor += d3; + FragColor += d4; + FragColor += d5; + FragColor += d6; + FragColor += d7; + FragColor += d8; + FragColor += lod.xyxy; + } +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/demote-to-helper.frag b/third_party/spirv-cross/shaders-hlsl/frag/demote-to-helper.frag new file mode 100644 index 0000000..bdfef6f --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/demote-to-helper.frag @@ -0,0 +1,7 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +void main() +{ + demote; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/depth-greater-than.frag b/third_party/spirv-cross/shaders-hlsl/frag/depth-greater-than.frag new file mode 100644 index 0000000..88f9a42 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/depth-greater-than.frag @@ -0,0 +1,8 @@ +#version 450 +layout(early_fragment_tests) in; +layout(depth_greater) out float gl_FragDepth; + +void main() +{ + gl_FragDepth = 0.5; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/depth-less-than.frag b/third_party/spirv-cross/shaders-hlsl/frag/depth-less-than.frag new file mode 100644 index 0000000..87fdd46 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/depth-less-than.frag @@ -0,0 +1,8 @@ +#version 450 +layout(early_fragment_tests) in; +layout(depth_less) out float gl_FragDepth; + +void main() +{ + gl_FragDepth = 0.5; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/dual-source-blending.frag b/third_party/spirv-cross/shaders-hlsl/frag/dual-source-blending.frag new file mode 100644 index 0000000..f322cf4 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/dual-source-blending.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0, index = 0) out vec4 FragColor0; +layout(location = 0, index = 1) out vec4 FragColor1; + +void main() +{ + FragColor0 = vec4(1.0); + FragColor1 = vec4(2.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/early-fragment-test.frag b/third_party/spirv-cross/shaders-hlsl/frag/early-fragment-test.frag new file mode 100644 index 0000000..9f84e09 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/early-fragment-test.frag @@ -0,0 +1,7 @@ +#version 420 + +layout(early_fragment_tests) in; + +void main() +{ +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/shaders-hlsl/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..1f91cca --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,11 @@ +#version 450 +layout(location = 0) out vec4 FragColor; +void main() +{ + FragColor = vec4(0.0); + for (int i = 0; i < 3; (0 > 1) ? 1 : i ++) + { + int a = i; + FragColor[a] += float(i); + } +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/fp16-packing.frag b/third_party/spirv-cross/shaders-hlsl/frag/fp16-packing.frag new file mode 100644 index 0000000..98ca24e --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/fp16-packing.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) flat in uint FP16; +layout(location = 1) flat in vec2 FP32; +layout(location = 0) out vec2 FP32Out; +layout(location = 1) out uint FP16Out; + +void main() +{ + FP32Out = unpackHalf2x16(FP16); + FP16Out = packHalf2x16(FP32); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/front-facing.frag b/third_party/spirv-cross/shaders-hlsl/frag/front-facing.frag new file mode 100644 index 0000000..90ca1ab --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/front-facing.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; + +void main() +{ + if (gl_FrontFacing) + FragColor = vA; + else + FragColor = vB; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/image-query-selective.frag b/third_party/spirv-cross/shaders-hlsl/frag/image-query-selective.frag new file mode 100644 index 0000000..bb595bc --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/image-query-selective.frag @@ -0,0 +1,35 @@ +#version 450 + +layout(binding = 0) uniform usampler1D uSampler1DUint; +layout(binding = 0) uniform isampler1D uSampler1DInt; +layout(binding = 0) uniform sampler1D uSampler1DFloat; +layout(binding = 1) uniform sampler2D uSampler2D; +layout(binding = 2) uniform isampler2DArray uSampler2DArray; +layout(binding = 3) uniform sampler3D uSampler3D; +layout(binding = 4) uniform samplerCube uSamplerCube; +layout(binding = 5) uniform usamplerCubeArray uSamplerCubeArray; +layout(binding = 6) uniform samplerBuffer uSamplerBuffer; +layout(binding = 7) uniform isampler2DMS uSamplerMS; +layout(binding = 8) uniform sampler2DMSArray uSamplerMSArray; + +void main() +{ + int a = textureSize(uSampler1DUint, 0); + a = textureSize(uSampler1DInt, 0); + a = textureSize(uSampler1DFloat, 0); + + ivec3 c = textureSize(uSampler2DArray, 0); + ivec3 d = textureSize(uSampler3D, 0); + ivec2 e = textureSize(uSamplerCube, 0); + ivec3 f = textureSize(uSamplerCubeArray, 0); + int g = textureSize(uSamplerBuffer); + ivec2 h = textureSize(uSamplerMS); + ivec3 i = textureSize(uSamplerMSArray); + + int l1 = textureQueryLevels(uSampler2D); + int l2 = textureQueryLevels(uSampler2DArray); + int l3 = textureQueryLevels(uSampler3D); + int l4 = textureQueryLevels(uSamplerCube); + int s0 = textureSamples(uSamplerMS); + int s1 = textureSamples(uSamplerMSArray); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/image-query-uav.frag b/third_party/spirv-cross/shaders-hlsl/frag/image-query-uav.frag new file mode 100644 index 0000000..25103e6 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/image-query-uav.frag @@ -0,0 +1,18 @@ +#version 450 + +layout(rgba32f, binding = 0) uniform writeonly image1D uImage1D; +layout(rg32f, binding = 1) uniform writeonly image2D uImage2D; +layout(r32f, binding = 2) uniform readonly image2DArray uImage2DArray; +layout(rgba8, binding = 3) uniform writeonly image3D uImage3D; +layout(rgba8_snorm, binding = 6) uniform writeonly imageBuffer uImageBuffer; + +// There is no RWTexture2DMS. + +void main() +{ + int a = imageSize(uImage1D); + ivec2 b = imageSize(uImage2D); + ivec3 c = imageSize(uImage2DArray); + ivec3 d = imageSize(uImage3D); + int e = imageSize(uImageBuffer); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag b/third_party/spirv-cross/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag new file mode 100644 index 0000000..25103e6 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/image-query-uav.nonwritable-uav-texture.frag @@ -0,0 +1,18 @@ +#version 450 + +layout(rgba32f, binding = 0) uniform writeonly image1D uImage1D; +layout(rg32f, binding = 1) uniform writeonly image2D uImage2D; +layout(r32f, binding = 2) uniform readonly image2DArray uImage2DArray; +layout(rgba8, binding = 3) uniform writeonly image3D uImage3D; +layout(rgba8_snorm, binding = 6) uniform writeonly imageBuffer uImageBuffer; + +// There is no RWTexture2DMS. + +void main() +{ + int a = imageSize(uImage1D); + ivec2 b = imageSize(uImage2D); + ivec3 c = imageSize(uImage2DArray); + ivec3 d = imageSize(uImage3D); + int e = imageSize(uImageBuffer); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/image-query.frag b/third_party/spirv-cross/shaders-hlsl/frag/image-query.frag new file mode 100644 index 0000000..8e840fb --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/image-query.frag @@ -0,0 +1,33 @@ +#version 450 + +layout(binding = 0) uniform sampler1D uSampler1D; +layout(binding = 1) uniform sampler2D uSampler2D; +layout(binding = 2) uniform sampler2DArray uSampler2DArray; +layout(binding = 3) uniform sampler3D uSampler3D; +layout(binding = 4) uniform samplerCube uSamplerCube; +layout(binding = 5) uniform samplerCubeArray uSamplerCubeArray; +layout(binding = 6) uniform samplerBuffer uSamplerBuffer; +layout(binding = 7) uniform sampler2DMS uSamplerMS; +layout(binding = 8) uniform sampler2DMSArray uSamplerMSArray; + +void main() +{ + int a = textureSize(uSampler1D, 0); + ivec2 b = textureSize(uSampler2D, 0); + ivec3 c = textureSize(uSampler2DArray, 0); + ivec3 d = textureSize(uSampler3D, 0); + ivec2 e = textureSize(uSamplerCube, 0); + ivec3 f = textureSize(uSamplerCubeArray, 0); + int g = textureSize(uSamplerBuffer); + ivec2 h = textureSize(uSamplerMS); + ivec3 i = textureSize(uSamplerMSArray); + + int l0 = textureQueryLevels(uSampler1D); + int l1 = textureQueryLevels(uSampler2D); + int l2 = textureQueryLevels(uSampler2DArray); + int l3 = textureQueryLevels(uSampler3D); + int l4 = textureQueryLevels(uSamplerCube); + int l5 = textureQueryLevels(uSamplerCubeArray); + int s0 = textureSamples(uSamplerMS); + int s1 = textureSamples(uSamplerMSArray); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/input-attachment-ms.frag b/third_party/spirv-cross/shaders-hlsl/frag/input-attachment-ms.frag new file mode 100644 index 0000000..b3d44c9 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/input-attachment-ms.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +vec4 load_subpasses(mediump subpassInputMS uInput) +{ + return subpassLoad(uInput, gl_SampleID); +} + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + load_subpasses(uSubpass0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/input-attachment.frag b/third_party/spirv-cross/shaders-hlsl/frag/input-attachment.frag new file mode 100644 index 0000000..877d052 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/input-attachment.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec4 FragColor; + +vec4 load_subpasses(mediump subpassInput uInput) +{ + return subpassLoad(uInput); +} + +void main() +{ + FragColor = subpassLoad(uSubpass0) + load_subpasses(uSubpass1); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/io-block.frag b/third_party/spirv-cross/shaders-hlsl/frag/io-block.frag new file mode 100644 index 0000000..1e3e3d7 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/io-block.frag @@ -0,0 +1,16 @@ +#version 310 es +#extension GL_EXT_shader_io_blocks : require +precision mediump float; + +layout(location = 1) in VertexOut +{ + vec4 a; + vec4 b; +}; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = a + b; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag b/third_party/spirv-cross/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag new file mode 100644 index 0000000..30b957b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/legacy-tex-modifiers.sm30.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(location = 0) in vec2 vUV; +layout(location = 0) out vec4 FragColor; +layout(set = 0, binding = 0) uniform sampler2D uSampler; + +void main() +{ + FragColor = textureProj(uSampler, vec3(vUV, 5.0)); + FragColor += texture(uSampler, vUV, 3.0); + FragColor += textureLod(uSampler, vUV, 2.0); + FragColor += textureGrad(uSampler, vUV, vec2(4.0), vec2(5.0)); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/lut-promotion.frag b/third_party/spirv-cross/shaders-hlsl/frag/lut-promotion.frag new file mode 100644 index 0000000..0cdc814 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/lut-promotion.frag @@ -0,0 +1,44 @@ +#version 310 es +precision mediump float; +layout(location = 0) out float FragColor; +layout(location = 0) flat in int index; + +const float LUT[16] = float[]( + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0); + +void main() +{ + // Try reading LUTs, both in branches and not branch. + FragColor = LUT[index]; + if (index < 10) + FragColor += LUT[index ^ 1]; + else + FragColor += LUT[index & 1]; + + // Not declared as a LUT, but can be promoted to one. + vec4 foo[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + FragColor += foo[index & 3].y; + } + else + { + FragColor += foo[index & 1].x; + } + + // Not declared as a LUT, but this cannot be promoted, because we have a partial write. + vec4 foobar[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + + // Not declared as a LUT, but this cannot be promoted, because we have two complete writes. + vec4 baz[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/matrix-input.frag b/third_party/spirv-cross/shaders-hlsl/frag/matrix-input.frag new file mode 100644 index 0000000..ffe242c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/matrix-input.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 1) in mat4 m; + +void main() +{ + FragColor = m[0] + m[1] + m[2] + m[3]; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/mod.frag b/third_party/spirv-cross/shaders-hlsl/frag/mod.frag new file mode 100644 index 0000000..32edb61 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/mod.frag @@ -0,0 +1,22 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 a4; +layout(location = 1) in vec3 a3; +layout(location = 2) in vec2 a2; +layout(location = 3) in float a1; +layout(location = 4) in vec4 b4; +layout(location = 5) in vec3 b3; +layout(location = 6) in vec2 b2; +layout(location = 7) in float b1; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 m0 = mod(a4, b4); + vec3 m1 = mod(a3, b3); + vec2 m2 = mod(a2, b2); + float m3 = mod(a1, b1); + FragColor = m0 + m1.xyzx + m2.xyxy + m3; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/mrt.frag b/third_party/spirv-cross/shaders-hlsl/frag/mrt.frag new file mode 100644 index 0000000..77a2bb2 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/mrt.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 RT0; +layout(location = 1) out vec4 RT1; +layout(location = 2) out vec4 RT2; +layout(location = 3) out vec4 RT3; + +void main() +{ + RT0 = vec4(1.0); + RT1 = vec4(2.0); + RT2 = vec4(3.0); + RT3 = vec4(4.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/no-return.frag b/third_party/spirv-cross/shaders-hlsl/frag/no-return.frag new file mode 100644 index 0000000..f0ef870 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/no-return.frag @@ -0,0 +1,5 @@ +#version 310 es + +void main() +{ +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/no-return2.frag b/third_party/spirv-cross/shaders-hlsl/frag/no-return2.frag new file mode 100644 index 0000000..46bf9fb --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/no-return2.frag @@ -0,0 +1,9 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; + +void main() +{ + vec4 v = vColor; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag b/third_party/spirv-cross/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag new file mode 100644 index 0000000..0aadd14 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/nonuniform-qualifier.nonuniformresource.sm51.frag @@ -0,0 +1,28 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require + +layout(set = 0, binding = 0) uniform texture2D uSamplers[]; +layout(set = 1, binding = 0) uniform sampler2D uCombinedSamplers[]; +layout(set = 2, binding = 0) uniform sampler uSamps[]; +layout(location = 0) flat in int vIndex; +layout(location = 1) in vec2 vUV; +layout(location = 0) out vec4 FragColor; + +layout(set = 3, binding = 0) uniform UBO +{ + vec4 v[64]; +} ubos[]; + +layout(set = 4, binding = 0) readonly buffer SSBO +{ + vec4 v[]; +} ssbos[]; + +void main() +{ + int i = vIndex; + FragColor = texture(sampler2D(uSamplers[nonuniformEXT(i + 10)], uSamps[nonuniformEXT(i + 40)]), vUV); + FragColor = texture(uCombinedSamplers[nonuniformEXT(i + 10)], vUV); + FragColor += ubos[nonuniformEXT(i + 20)].v[nonuniformEXT(i + 40)]; + FragColor += ssbos[nonuniformEXT(i + 50)].v[nonuniformEXT(i + 60)]; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/partial-write-preserve.frag b/third_party/spirv-cross/shaders-hlsl/frag/partial-write-preserve.frag new file mode 100644 index 0000000..f30270b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/partial-write-preserve.frag @@ -0,0 +1,64 @@ +#version 310 es +precision mediump float; + +layout(std140, binding = 0) uniform UBO +{ + int some_value; +}; + +struct B +{ + float a; + float b; +}; + +void partial_inout(inout vec4 x) +{ + x.x = 10.0; +} + +void partial_inout(inout B b) +{ + b.b = 40.0; +} + +// Make a complete write, but only conditionally ... +void branchy_inout(inout vec4 v) +{ + v.y = 20.0; + if (some_value == 20) + { + v = vec4(50.0); + } +} + +void branchy_inout_2(out vec4 v) +{ + if (some_value == 20) + { + v = vec4(50.0); + } + else + { + v = vec4(70.0); + } + v.y = 20.0; +} + +void complete_inout(out vec4 x) +{ + x = vec4(50.0); +} + +void main() +{ + vec4 a = vec4(10.0); + partial_inout(a); + complete_inout(a); + branchy_inout(a); + branchy_inout_2(a); + + B b = B(10.0, 20.0); + partial_inout(b); +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag b/third_party/spirv-cross/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag new file mode 100644 index 0000000..ceac8cc --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/pixel-interlock-ordered.sm51.fxconly.frag @@ -0,0 +1,36 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require + +layout(pixel_interlock_ordered) in; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; +layout(binding = 2, rgba8) uniform readonly image2D img3; +layout(binding = 3) coherent buffer Buffer +{ + int foo; + uint bar; +}; +layout(binding = 4) buffer Buffer2 +{ + uint quux; +}; + +layout(binding = 5, rgba8) uniform writeonly image2D img4; +layout(binding = 6) buffer Buffer3 +{ + int baz; +}; + +void main() +{ + // Deliberately outside the critical section to test usage tracking. + baz = 0; + imageStore(img4, ivec2(1, 1), vec4(1.0, 0.0, 0.0, 1.0)); + beginInvocationInterlockARB(); + imageStore(img, ivec2(0, 0), imageLoad(img3, ivec2(0, 0))); + imageAtomicAdd(img2, ivec2(0, 0), 1u); + foo += 42; + atomicAnd(bar, quux); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/point-coord-compat.frag b/third_party/spirv-cross/shaders-hlsl/frag/point-coord-compat.frag new file mode 100644 index 0000000..dc7d6b5 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/point-coord-compat.frag @@ -0,0 +1,10 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec2 FragColor; + +void main() +{ + FragColor = gl_PointCoord; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/query-lod.desktop.frag b/third_party/spirv-cross/shaders-hlsl/frag/query-lod.desktop.frag new file mode 100644 index 0000000..0cb1604 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/query-lod.desktop.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) in vec2 vTexCoord; +layout(binding = 0) uniform sampler2D uSampler; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureQueryLod(uSampler, vTexCoord).xyxy; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag b/third_party/spirv-cross/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag new file mode 100644 index 0000000..cd03546 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/readonly-coherent-ssbo.force-uav.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(set = 0, binding = 0) coherent readonly buffer SSBO +{ + vec4 a; +}; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = a; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/readonly-coherent-ssbo.frag b/third_party/spirv-cross/shaders-hlsl/frag/readonly-coherent-ssbo.frag new file mode 100644 index 0000000..cd03546 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/readonly-coherent-ssbo.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(set = 0, binding = 0) coherent readonly buffer SSBO +{ + vec4 a; +}; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = a; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/resources.frag b/third_party/spirv-cross/shaders-hlsl/frag/resources.frag new file mode 100644 index 0000000..16178bf --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/resources.frag @@ -0,0 +1,27 @@ +#version 310 es +precision mediump float; + +layout(binding = 3, std140) uniform CBuffer +{ + vec4 a; +} cbuf; + +layout(binding = 4) uniform sampler2D uSampledImage; +layout(binding = 5) uniform mediump texture2D uTexture; +layout(binding = 6) uniform mediump sampler uSampler; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; + +layout(std430, push_constant) uniform PushMe +{ + vec4 d; +} registers; + +void main() +{ + vec4 c0 = texture(uSampledImage, vTex); + vec4 c1 = texture(sampler2D(uTexture, uSampler), vTex); + vec4 c2 = cbuf.a + registers.d; + FragColor = c0 + c1 + c2; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/row-major-layout-in-struct.frag b/third_party/spirv-cross/shaders-hlsl/frag/row-major-layout-in-struct.frag new file mode 100644 index 0000000..3e93bb2 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/row-major-layout-in-struct.frag @@ -0,0 +1,29 @@ +#version 450 + +struct Foo +{ + mat4 v; + mat4 w; +}; + +struct NonFoo +{ + mat4 v; + mat4 w; +}; + +layout(std140, binding = 0) uniform UBO +{ + layout(column_major) Foo foo; +}; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vUV; + +void main() +{ + NonFoo f; + f.v = foo.v; + f.w = foo.w; + FragColor = f.v * (f.w * vUV); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/sample-cmp-level-zero.frag b/third_party/spirv-cross/shaders-hlsl/frag/sample-cmp-level-zero.frag new file mode 100644 index 0000000..c40d742 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/sample-cmp-level-zero.frag @@ -0,0 +1,27 @@ +#version 450 + +layout(location = 0) out float FragColor; +layout(binding = 0) uniform sampler2DShadow uSampler2D; +layout(binding = 1) uniform sampler2DArrayShadow uSampler2DArray; +layout(binding = 2) uniform samplerCubeShadow uSamplerCube; +layout(binding = 3) uniform samplerCubeArrayShadow uSamplerCubeArray; + +layout(location = 0) in vec3 vUVRef; +layout(location = 1) in vec4 vDirRef; + +void main() +{ + float s0 = textureOffset(uSampler2D, vUVRef, ivec2(-1)); + float s1 = textureOffset(uSampler2DArray, vDirRef, ivec2(-1)); + float s2 = texture(uSamplerCube, vDirRef); + float s3 = texture(uSamplerCubeArray, vDirRef, 0.5); + + float l0 = textureLodOffset(uSampler2D, vUVRef, 0.0, ivec2(-1)); + float l1 = textureGradOffset(uSampler2DArray, vDirRef, vec2(0.0), vec2(0.0), ivec2(-1)); + float l2 = textureGrad(uSamplerCube, vDirRef, vec3(0.0), vec3(0.0)); + + float p0 = textureProjOffset(uSampler2D, vDirRef, ivec2(+1)); + float p1 = textureProjLodOffset(uSampler2D, vDirRef, 0.0, ivec2(+1)); + + FragColor = s0 + s1 + s2 + s3 + l0 + l1 + l2 + p0 + p1; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-in-and-out.frag b/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-in-and-out.frag new file mode 100644 index 0000000..75ed3cc --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-in-and-out.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); + gl_SampleMask[0] = gl_SampleMaskIn[0]; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-in.frag b/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-in.frag new file mode 100644 index 0000000..16031a3 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-in.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + if ((gl_SampleMaskIn[0] & (1 << gl_SampleID)) != 0) + { + FragColor = vec4(1.0); + } +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-out.frag b/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-out.frag new file mode 100644 index 0000000..c7fb80e --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/sample-mask-out.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); + gl_SampleMask[0] = 0; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/sampler-array.frag b/third_party/spirv-cross/shaders-hlsl/frag/sampler-array.frag new file mode 100644 index 0000000..75910ed --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/sampler-array.frag @@ -0,0 +1,28 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uCombined[4]; +layout(binding = 4) uniform texture2D uTex[4]; +layout(binding = 8) uniform sampler uSampler[4]; +layout(binding = 12, rgba32f) uniform writeonly image2D uImage[8]; +layout(location = 0) in vec2 vTex; +layout(location = 1) flat in int vIndex; + +vec4 sample_in_function(sampler2D samp) +{ + return texture(samp, vTex); +} + +vec4 sample_in_function2(texture2D tex, sampler samp) +{ + return texture(sampler2D(tex, samp), vTex); +} + +void main() +{ + vec4 color = texture(uCombined[vIndex], vTex); + color += texture(sampler2D(uTex[vIndex], uSampler[vIndex]), vTex); + color += sample_in_function(uCombined[vIndex + 1]); + color += sample_in_function2(uTex[vIndex + 1], uSampler[vIndex + 1]); + + imageStore(uImage[vIndex], ivec2(gl_FragCoord.xy), color); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/sampler-image-arrays.frag b/third_party/spirv-cross/shaders-hlsl/frag/sampler-image-arrays.frag new file mode 100644 index 0000000..42370d9 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/sampler-image-arrays.frag @@ -0,0 +1,33 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in vec2 vTex; +layout(location = 1) flat in int vIndex; +layout(binding = 0) uniform sampler2D uSampler[4]; +layout(binding = 4) uniform sampler uSamplers[4]; +layout(binding = 8) uniform texture2D uTextures[4]; + +vec4 sample_from_argument(sampler2D samplers[4]) +{ + return texture(samplers[vIndex], vTex + 0.2); +} + +vec4 sample_single_from_argument(sampler2D samp) +{ + return texture(samp, vTex + 0.3); +} + +vec4 sample_from_global() +{ + return texture(uSampler[vIndex], vTex + 0.1); +} + +void main() +{ + FragColor = vec4(0.0); + FragColor += texture(sampler2D(uTextures[2], uSamplers[1]), vTex); + FragColor += texture(uSampler[vIndex], vTex); + FragColor += sample_from_global(); + FragColor += sample_from_argument(uSampler); + FragColor += sample_single_from_argument(uSampler[3]); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/shaders-hlsl/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..486ed90 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/scalar-refract-reflect.frag @@ -0,0 +1,11 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vRefract; + +void main() +{ + FragColor = refract(vRefract.x, vRefract.y, vRefract.z); + FragColor += reflect(vRefract.x, vRefract.y); + FragColor += refract(vRefract.xy, vRefract.yz, vRefract.z).y; + FragColor += reflect(vRefract.xy, vRefract.zy).y; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag b/third_party/spirv-cross/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag new file mode 100644 index 0000000..22d18a2 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/separate-combined-fake-overload.sm30.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uSamp; +layout(binding = 1) uniform texture2D uT; +layout(binding = 2) uniform sampler uS; + +vec4 samp(sampler2D uSamp) +{ + return texture(uSamp, vec2(0.5)); +} + +vec4 samp(texture2D T, sampler S) +{ + return texture(sampler2D(T, S), vec2(0.5)); +} + +void main() +{ + FragColor = samp(uSamp) + samp(uT, uS); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/spec-constant-block-size.frag b/third_party/spirv-cross/shaders-hlsl/frag/spec-constant-block-size.frag new file mode 100644 index 0000000..8d2b1f3 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/spec-constant-block-size.frag @@ -0,0 +1,17 @@ +#version 310 es +precision mediump float; + +layout(constant_id = 10) const int Value = 2; +layout(binding = 0) uniform SpecConstArray +{ + vec4 samples[Value]; +}; + +layout(location = 0) flat in int Index; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = samples[Index]; +} + diff --git a/third_party/spirv-cross/shaders-hlsl/frag/spec-constant-ternary.frag b/third_party/spirv-cross/shaders-hlsl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000..78dccbf --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/spec-constant-ternary.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/switch-unsigned-case.frag b/third_party/spirv-cross/shaders-hlsl/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..d8aee43 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/switch-unsigned-case.frag @@ -0,0 +1,26 @@ +#version 310 es +precision mediump float; + +#define ENUM_0 0u +#define ENUM_1 1u + +layout(set = 0, binding = 0) uniform Buff +{ + uint TestVal; +}; + +layout(location = 0) out vec4 fsout_Color; + +void main() +{ + fsout_Color = vec4(1.0); + switch (TestVal) + { + case ENUM_0: + fsout_Color = vec4(0.1); + break; + case ENUM_1: + fsout_Color = vec4(0.2); + break; + } +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/swizzle-scalar.frag b/third_party/spirv-cross/shaders-hlsl/frag/swizzle-scalar.frag new file mode 100644 index 0000000..c27524f --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/swizzle-scalar.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) flat in float vFloat; +layout(location = 1) flat in int vInt; +layout(location = 0) out vec4 Float; +layout(location = 1) out ivec4 Int; +layout(location = 2) out vec4 Float2; +layout(location = 3) out ivec4 Int2; + +void main() +{ + Float = vec4(vFloat) * 2.0; + Int = ivec4(vInt) * 2; + Float2 = vec4(10.0); + Int2 = ivec4(10); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/tex-sampling-ms.frag b/third_party/spirv-cross/shaders-hlsl/frag/tex-sampling-ms.frag new file mode 100644 index 0000000..7badbb1 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/tex-sampling-ms.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2DMS uTex; + +void main() +{ + FragColor = + texelFetch(uTex, ivec2(gl_FragCoord.xy), 0); + FragColor += + texelFetch(uTex, ivec2(gl_FragCoord.xy), 1); + FragColor += + texelFetch(uTex, ivec2(gl_FragCoord.xy), 2); + FragColor += + texelFetch(uTex, ivec2(gl_FragCoord.xy), 3); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/tex-sampling.frag b/third_party/spirv-cross/shaders-hlsl/frag/tex-sampling.frag new file mode 100644 index 0000000..762c60a --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/tex-sampling.frag @@ -0,0 +1,81 @@ +#version 450 + +layout(binding = 0) uniform sampler1D tex1d; +layout(binding = 1) uniform sampler2D tex2d; +layout(binding = 2) uniform sampler3D tex3d; +layout(binding = 3) uniform samplerCube texCube; + +layout(binding = 4) uniform sampler1DShadow tex1dShadow; +layout(binding = 5) uniform sampler2DShadow tex2dShadow; +layout(binding = 6) uniform samplerCubeShadow texCubeShadow; + +layout(binding = 7) uniform sampler1DArray tex1dArray; +layout(binding = 8) uniform sampler2DArray tex2dArray; +layout(binding = 9) uniform samplerCubeArray texCubeArray; + +layout(binding = 10) uniform samplerShadow samplerDepth; +layout(binding = 11) uniform sampler samplerNonDepth; +layout(binding = 12) uniform texture2D separateTex2d; +layout(binding = 13) uniform texture2D separateTex2dDepth; + +layout(location = 0) in float texCoord1d; +layout(location = 1) in vec2 texCoord2d; +layout(location = 2) in vec3 texCoord3d; +layout(location = 3) in vec4 texCoord4d; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 texcolor = texture(tex1d, texCoord1d); + texcolor += textureOffset(tex1d, texCoord1d, 1); + texcolor += textureLod(tex1d, texCoord1d, 2); + texcolor += textureGrad(tex1d, texCoord1d, 1.0, 2.0); + texcolor += textureProj(tex1d, vec2(texCoord1d, 2.0)); + texcolor += texture(tex1d, texCoord1d, 1.0); + + texcolor += texture(tex2d, texCoord2d); + texcolor += textureOffset(tex2d, texCoord2d, ivec2(1, 2)); + texcolor += textureLod(tex2d, texCoord2d, 2); + texcolor += textureGrad(tex2d, texCoord2d, vec2(1.0, 2.0), vec2(3.0, 4.0)); + texcolor += textureProj(tex2d, vec3(texCoord2d, 2.0)); + texcolor += texture(tex2d, texCoord2d, 1.0); + + texcolor += texture(tex3d, texCoord3d); + texcolor += textureOffset(tex3d, texCoord3d, ivec3(1, 2, 3)); + texcolor += textureLod(tex3d, texCoord3d, 2); + texcolor += textureGrad(tex3d, texCoord3d, vec3(1.0, 2.0, 3.0), vec3(4.0, 5.0, 6.0)); + texcolor += textureProj(tex3d, vec4(texCoord3d, 2.0)); + texcolor += texture(tex3d, texCoord3d, 1.0); + + texcolor += texture(texCube, texCoord3d); + texcolor += textureLod(texCube, texCoord3d, 2); + texcolor += texture(texCube, texCoord3d, 1.0); + + texcolor.a += texture(tex1dShadow, vec3(texCoord1d, 0.0, 0.0)); + texcolor.a += texture(tex2dShadow, vec3(texCoord2d, 0.0)); + texcolor.a += texture(texCubeShadow, vec4(texCoord3d, 0.0)); + + texcolor += texture(tex1dArray, texCoord2d); + texcolor += texture(tex2dArray, texCoord3d); + texcolor += texture(texCubeArray, texCoord4d); + + texcolor += textureGather(tex2d, texCoord2d); + texcolor += textureGather(tex2d, texCoord2d, 0); + texcolor += textureGather(tex2d, texCoord2d, 1); + texcolor += textureGather(tex2d, texCoord2d, 2); + texcolor += textureGather(tex2d, texCoord2d, 3); + + texcolor += textureGatherOffset(tex2d, texCoord2d, ivec2(1, 1)); + texcolor += textureGatherOffset(tex2d, texCoord2d, ivec2(1, 1), 0); + texcolor += textureGatherOffset(tex2d, texCoord2d, ivec2(1, 1), 1); + texcolor += textureGatherOffset(tex2d, texCoord2d, ivec2(1, 1), 2); + texcolor += textureGatherOffset(tex2d, texCoord2d, ivec2(1, 1), 3); + + texcolor += texelFetch(tex2d, ivec2(1, 2), 0); + + texcolor += texture(sampler2D(separateTex2d, samplerNonDepth), texCoord2d); + texcolor.a += texture(sampler2DShadow(separateTex2dDepth, samplerDepth), texCoord3d); + + FragColor = texcolor; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/texel-fetch-offset.frag b/third_party/spirv-cross/shaders-hlsl/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..e98748b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/texel-fetch-offset.frag @@ -0,0 +1,10 @@ +#version 310 es +precision mediump float; +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uTexture; + +void main() +{ + FragColor = texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(1, 1)); + FragColor += texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(-1, 1)); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/texture-proj-shadow.frag b/third_party/spirv-cross/shaders-hlsl/frag/texture-proj-shadow.frag new file mode 100644 index 0000000..0c4cf8f --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/texture-proj-shadow.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(binding = 0) uniform sampler1DShadow uShadow1D; +layout(binding = 1) uniform sampler2DShadow uShadow2D; +layout(binding = 2) uniform sampler1D uSampler1D; +layout(binding = 3) uniform sampler2D uSampler2D; +layout(binding = 4) uniform sampler3D uSampler3D; + +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vClip3; +layout(location = 1) in vec4 vClip4; +layout(location = 2) in vec2 vClip2; + +void main() +{ + FragColor = textureProj(uShadow1D, vClip4); + FragColor = textureProj(uShadow2D, vClip4); + FragColor = textureProj(uSampler1D, vClip2).x; + FragColor = textureProj(uSampler2D, vClip3).x; + FragColor = textureProj(uSampler3D, vClip4).x; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/texture-size-combined-image-sampler.frag b/third_party/spirv-cross/shaders-hlsl/frag/texture-size-combined-image-sampler.frag new file mode 100644 index 0000000..9488059 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/texture-size-combined-image-sampler.frag @@ -0,0 +1,9 @@ +#version 450 +layout(set = 0, binding = 0) uniform texture2D uTex; +layout(set = 0, binding = 1) uniform sampler uSampler; +layout(location = 0) out ivec2 FooOut; + +void main() +{ + FooOut = textureSize(sampler2D(uTex, uSampler), 0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/unary-enclose.frag b/third_party/spirv-cross/shaders-hlsl/frag/unary-enclose.frag new file mode 100644 index 0000000..ea502e1 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/unary-enclose.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vIn; +layout(location = 1) flat in ivec4 vIn1; + +void main() +{ + FragColor = +(-(-vIn)); + ivec4 a = ~(~vIn1); + + bool b = false; + b = !!b; +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/unorm-snorm-packing.frag b/third_party/spirv-cross/shaders-hlsl/frag/unorm-snorm-packing.frag new file mode 100644 index 0000000..c0a01aa --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/unorm-snorm-packing.frag @@ -0,0 +1,24 @@ +#version 450 + +layout(location = 0) flat in uint SNORM8; +layout(location = 1) flat in uint UNORM8; +layout(location = 2) flat in uint SNORM16; +layout(location = 3) flat in uint UNORM16; +layout(location = 4) flat in vec4 FP32; +layout(location = 0) out vec4 FP32Out; +layout(location = 1) out uint UNORM8Out; +layout(location = 2) out uint SNORM8Out; +layout(location = 3) out uint UNORM16Out; +layout(location = 4) out uint SNORM16Out; + +void main() +{ + FP32Out = unpackUnorm4x8(UNORM8); + FP32Out = unpackSnorm4x8(SNORM8); + FP32Out.xy = unpackUnorm2x16(UNORM16); + FP32Out.xy = unpackSnorm2x16(SNORM16); + UNORM8Out = packUnorm4x8(FP32); + SNORM8Out = packSnorm4x8(FP32); + UNORM16Out = packUnorm2x16(FP32.xy); + SNORM16Out = packSnorm2x16(FP32.zw); +} diff --git a/third_party/spirv-cross/shaders-hlsl/frag/various-glsl-ops.frag b/third_party/spirv-cross/shaders-hlsl/frag/various-glsl-ops.frag new file mode 100644 index 0000000..0d4af80 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/frag/various-glsl-ops.frag @@ -0,0 +1,17 @@ +#version 450 + +layout(location = 0) in vec2 interpolant; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 color = vec4(0.0, 0.0, 0.0, interpolateAtOffset(interpolant, vec2(0.1, 0.1))); + + // glslang's HLSL parser currently fails here + //color += vec4(0.0, 0.0, 0.0, interpolateAtSample(interpolant, gl_SampleID)); + //color += vec4(0.0, 0.0, 0.0, interpolateAtCentroid(interpolant)); + + color += vec4(0.0, 0.0, 0.0, dFdxCoarse(interpolant.x)); + FragColor = color; +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/basic.vert b/third_party/spirv-cross/shaders-hlsl/vert/basic.vert new file mode 100644 index 0000000..f03114f --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/basic.vert @@ -0,0 +1,15 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; +}; +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/clip-cull-distance.vert b/third_party/spirv-cross/shaders-hlsl/vert/clip-cull-distance.vert new file mode 100644 index 0000000..34f65fc --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/clip-cull-distance.vert @@ -0,0 +1,11 @@ +#version 450 +out float gl_ClipDistance[2]; +out float gl_CullDistance[1]; + +void main() +{ + gl_Position = vec4(1.0); + gl_ClipDistance[0] = 0.0; + gl_ClipDistance[1] = 0.0; + gl_CullDistance[0] = 4.0; +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/instancing.vert b/third_party/spirv-cross/shaders-hlsl/vert/instancing.vert new file mode 100644 index 0000000..0e62ef6 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/instancing.vert @@ -0,0 +1,6 @@ +#version 310 es + +void main() +{ + gl_Position = vec4(float(gl_VertexIndex + gl_InstanceIndex)); +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/locations.vert b/third_party/spirv-cross/shaders-hlsl/vert/locations.vert new file mode 100644 index 0000000..df1a44e --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/locations.vert @@ -0,0 +1,51 @@ +#version 310 es +#extension GL_EXT_shader_io_blocks : require + +struct Foo +{ + vec3 a; + vec3 b; + vec3 c; +}; + +// This will lock to input location 2. +layout(location = 2) in vec4 Input2; +// This will lock to input location 4. +layout(location = 4) in vec4 Input4; +// This will pick first available, which is 0. +layout(location = 0) in vec4 Input0; + +// Locks output 0. +layout(location = 0) out float vLocation0; +// Locks output 1. +layout(location = 1) out float vLocation1; +// Picks first available two locations, so, 2 and 3. +layout(location = 2) out float vLocation2[2]; +// Picks first available location, 4. +layout(location = 4) out Foo vLocation4; +// Picks first available location 9. +layout(location = 9) out float vLocation9; + +// Locks location 7 and 8. +layout(location = 7) out VertexOut +{ + vec3 color; + vec3 foo; +} vout; + +void main() +{ + gl_Position = vec4(1.0) + Input2 + Input4 + Input0; + vLocation0 = 0.0; + vLocation1 = 1.0; + vLocation2[0] = 2.0; + vLocation2[1] = 2.0; + Foo foo; + foo.a = vec3(1.0); + foo.b = vec3(1.0); + foo.c = vec3(1.0); + vLocation4 = foo; + vLocation9 = 9.0; + vout.color = vec3(2.0); + vout.foo = vec3(4.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/matrix-attribute.vert b/third_party/spirv-cross/shaders-hlsl/vert/matrix-attribute.vert new file mode 100644 index 0000000..8a1393f --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/matrix-attribute.vert @@ -0,0 +1,9 @@ +#version 310 es + +layout(location = 0) in vec3 pos; +layout(location = 1) in mat4 m; + +void main() +{ + gl_Position = m * vec4(pos, 1.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/matrix-output.vert b/third_party/spirv-cross/shaders-hlsl/vert/matrix-output.vert new file mode 100644 index 0000000..1151d4b --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/matrix-output.vert @@ -0,0 +1,9 @@ +#version 450 + +layout(location = 0) out mat4 m; + +void main() +{ + gl_Position = vec4(1.0); + m = mat4(1.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/no-input.vert b/third_party/spirv-cross/shaders-hlsl/vert/no-input.vert new file mode 100644 index 0000000..8de8e81 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/no-input.vert @@ -0,0 +1,6 @@ +#version 310 es + +void main() +{ + gl_Position = vec4(1.0); +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/point-size-compat.vert b/third_party/spirv-cross/shaders-hlsl/vert/point-size-compat.vert new file mode 100644 index 0000000..ed86c76 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/point-size-compat.vert @@ -0,0 +1,7 @@ +#version 310 es + +void main() +{ + gl_Position = vec4(1.0); + gl_PointSize = 1.0; +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/qualifiers.vert b/third_party/spirv-cross/shaders-hlsl/vert/qualifiers.vert new file mode 100644 index 0000000..080a709 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/qualifiers.vert @@ -0,0 +1,27 @@ +#version 450 + +layout(location = 0) flat out float vFlat; +layout(location = 1) centroid out float vCentroid; +layout(location = 2) sample out float vSample; +layout(location = 3) noperspective out float vNoperspective; + +layout(location = 4) out Block +{ + flat float vFlat; + centroid float vCentroid; + sample float vSample; + noperspective float vNoperspective; +} vout; + +void main() +{ + gl_Position = vec4(1.0); + vFlat = 0.0; + vCentroid = 1.0; + vSample = 2.0; + vNoperspective = 3.0; + vout.vFlat = 0.0; + vout.vCentroid = 1.0; + vout.vSample = 2.0; + vout.vNoperspective = 3.0; +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/read-from-row-major-array.vert b/third_party/spirv-cross/shaders-hlsl/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..792fb8e --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/read-from-row-major-array.vert @@ -0,0 +1,20 @@ +#version 310 es +layout(location = 0) in highp vec4 a_position; +layout(location = 0) out mediump float v_vtxResult; + +layout(set = 0, binding = 0, std140, row_major) uniform Block +{ + highp mat2x3 var[3][4]; +}; + +mediump float compare_float (highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; } +mediump float compare_vec3 (highp vec3 a, highp vec3 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); } +mediump float compare_mat2x3 (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1]); } + +void main (void) +{ + gl_Position = a_position; + mediump float result = 1.0; + result *= compare_mat2x3(var[0][0], mat2x3(2.0, 6.0, -6.0, 0.0, 5.0, 5.0)); + v_vtxResult = result; +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/return-array.vert b/third_party/spirv-cross/shaders-hlsl/vert/return-array.vert new file mode 100644 index 0000000..7084601 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/return-array.vert @@ -0,0 +1,22 @@ +#version 310 es + +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; + +vec4[2] test() +{ + return vec4[](vec4(10.0), vec4(20.0)); +} + +vec4[2] test2() +{ + vec4 foobar[2]; + foobar[0] = vInput0; + foobar[1] = vInput1; + return foobar; +} + +void main() +{ + gl_Position = test()[0] + test2()[1]; +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/sampler-buffers.vert b/third_party/spirv-cross/shaders-hlsl/vert/sampler-buffers.vert new file mode 100644 index 0000000..dccbf77 --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/sampler-buffers.vert @@ -0,0 +1,17 @@ +#version 450 + +layout(binding = 1) uniform samplerBuffer uFloatSampler; +layout(binding = 2) uniform isamplerBuffer uIntSampler; +layout(binding = 3) uniform usamplerBuffer uUintSampler; + +vec4 sample_from_function(samplerBuffer s0, isamplerBuffer s1, usamplerBuffer s2) +{ + return texelFetch(s0, 20) + + intBitsToFloat(texelFetch(s1, 40)) + + uintBitsToFloat(texelFetch(s2, 60)); +} + +void main() +{ + gl_Position = sample_from_function(uFloatSampler, uIntSampler, uUintSampler); +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/struct-composite-decl.vert b/third_party/spirv-cross/shaders-hlsl/vert/struct-composite-decl.vert new file mode 100644 index 0000000..c527fdf --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/struct-composite-decl.vert @@ -0,0 +1,26 @@ +#version 310 es + +layout(location = 0) in vec4 a; +layout(location = 1) in vec4 b; +layout(location = 2) in vec4 c; +layout(location = 3) in vec4 d; + +struct VOut +{ + vec4 a; + vec4 b; + vec4 c; + vec4 d; +}; + +layout(location = 0) out VOut vout; + +void emit_result(VOut v) +{ + vout = v; +} + +void main() +{ + emit_result(VOut(a, b, c, d)); +} diff --git a/third_party/spirv-cross/shaders-hlsl/vert/texture_buffer.vert b/third_party/spirv-cross/shaders-hlsl/vert/texture_buffer.vert new file mode 100644 index 0000000..b071e0c --- /dev/null +++ b/third_party/spirv-cross/shaders-hlsl/vert/texture_buffer.vert @@ -0,0 +1,9 @@ +#version 450 + +layout(binding = 4) uniform samplerBuffer uSamp; +layout(rgba32f, binding = 5) uniform readonly imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp new file mode 100644 index 0000000..87aee2d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 37 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %T "T" + OpMemberName %T 0 "a" + OpName %v "v" + OpName %T_0 "T" + OpMemberName %T_0 0 "b" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "foo" + OpName %_ "" + OpName %T_1 "T" + OpMemberName %T_1 0 "c" + OpName %SSBO2 "SSBO2" + OpMemberName %SSBO2 0 "bar" + OpName %__0 "" + OpMemberDecorate %T_0 0 Offset 0 + OpDecorate %_runtimearr_T_0 ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpMemberDecorate %T_1 0 Offset 0 + OpDecorate %_runtimearr_T_1 ArrayStride 16 + OpMemberDecorate %SSBO2 0 Offset 0 + OpDecorate %SSBO2 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %T = OpTypeStruct %float +%_ptr_Function_T = OpTypePointer Function %T + %float_40 = OpConstant %float 40 + %11 = OpConstantComposite %T %float_40 + %T_0 = OpTypeStruct %float +%_runtimearr_T_0 = OpTypeRuntimeArray %T_0 + %SSBO1 = OpTypeStruct %_runtimearr_T_0 +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 +%_ptr_Uniform_T_0 = OpTypePointer Uniform %T_0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %T_1 = OpTypeStruct %float +%_runtimearr_T_1 = OpTypeRuntimeArray %T_1 + %SSBO2 = OpTypeStruct %_runtimearr_T_1 +%_ptr_Uniform_SSBO2 = OpTypePointer Uniform %SSBO2 + %__0 = OpVariable %_ptr_Uniform_SSBO2 Uniform + %int_30 = OpConstant %int 30 +%_ptr_Uniform_T_1 = OpTypePointer Uniform %T_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %v = OpVariable %_ptr_Function_T Function + OpStore %v %11 + %20 = OpLoad %T %v + %22 = OpAccessChain %_ptr_Uniform_T_0 %_ %int_0 %int_10 + %23 = OpCompositeExtract %float %20 0 + %25 = OpAccessChain %_ptr_Uniform_float %22 %int_0 + OpStore %25 %23 + %32 = OpLoad %T %v + %34 = OpAccessChain %_ptr_Uniform_T_1 %__0 %int_0 %int_30 + %35 = OpCompositeExtract %float %32 0 + %36 = OpAccessChain %_ptr_Uniform_float %34 %int_0 + OpStore %36 %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp new file mode 100644 index 0000000..0e1ce23 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp @@ -0,0 +1,131 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 76 +; Schema: 0 + OpCapability Shader + OpCapability Int16 + OpCapability StorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types_int16" + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "s32" + OpMemberName %SSBO 1 "u32" + OpMemberName %SSBO 2 "s16" + OpMemberName %SSBO 3 "u16" + OpMemberName %SSBO 4 "f32" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpMemberDecorate %SSBO 2 Offset 8 + OpMemberDecorate %SSBO 3 Offset 10 + OpMemberDecorate %SSBO 4 Offset 12 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %short = OpTypeInt 16 1 + %ushort = OpTypeInt 16 0 + %float = OpTypeFloat 32 + %SSBO = OpTypeStruct %int %uint %short %ushort %float +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_2 = OpConstant %int 2 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_Uniform_short = OpTypePointer Uniform %short + %int_1 = OpConstant %int 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_3 = OpConstant %int 3 +%_ptr_Uniform_ushort = OpTypePointer Uniform %ushort + %int_4 = OpConstant %int 4 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %main = OpFunction %void None %3 + %5 = OpLabel + %ptr_s32 = OpAccessChain %_ptr_Uniform_int %_ %int_0 + %ptr_u32 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 + %ptr_s16 = OpAccessChain %_ptr_Uniform_short %_ %int_2 + %ptr_u16 = OpAccessChain %_ptr_Uniform_ushort %_ %int_3 + %ptr_f32 = OpAccessChain %_ptr_Uniform_float %_ %int_4 + %s32 = OpLoad %int %ptr_s32 + %u32 = OpLoad %uint %ptr_u32 + %s16 = OpLoad %short %ptr_s16 + %u16 = OpLoad %ushort %ptr_u16 + %f32 = OpLoad %float %ptr_f32 + + ; Sign-extend + %s16_to_s32_signed = OpSConvert %int %s16 + OpStore %ptr_s32 %s16_to_s32_signed + %s16_to_u32_signed = OpSConvert %uint %s16 + OpStore %ptr_u32 %s16_to_u32_signed + + %u16_to_s32_signed = OpSConvert %int %u16 + OpStore %ptr_s32 %u16_to_s32_signed + %u16_to_u32_signed = OpSConvert %uint %u16 + OpStore %ptr_u32 %u16_to_u32_signed + + ; Zero-extend + ; Result must be unsigned for OpUConvert. + ;%s16_to_s32_unsigned = OpUConvert %int %s16 + ;OpStore %ptr_s32 %s16_to_s32_unsigned + %s16_to_u32_unsigned = OpUConvert %uint %s16 + OpStore %ptr_u32 %s16_to_u32_unsigned + + ;%u16_to_s32_unsigned = OpUConvert %int %u16 + ;OpStore %ptr_s32 %u16_to_s32_unsigned + %u16_to_u32_unsigned = OpUConvert %uint %u16 + OpStore %ptr_u32 %u16_to_u32_unsigned + + ; Truncate (SConvert == UConvert) + %s32_to_s16_signed = OpSConvert %short %s32 + OpStore %ptr_s16 %s32_to_s16_signed + %s32_to_u16_signed = OpSConvert %ushort %s32 + OpStore %ptr_u16 %s32_to_u16_signed + + %u32_to_s16_signed = OpSConvert %short %u32 + OpStore %ptr_s16 %u32_to_s16_signed + %u32_to_u16_signed = OpSConvert %ushort %u32 + OpStore %ptr_u16 %u32_to_u16_signed + + ;%s32_to_s16_unsigned = OpUConvert %short %s32 + ;OpStore %ptr_s16 %s32_to_s16_unsigned + %s32_to_u16_unsigned = OpUConvert %ushort %s32 + OpStore %ptr_u16 %s32_to_u16_unsigned + + ;%u32_to_s16_unsigned = OpUConvert %short %u32 + ;OpStore %ptr_s16 %u32_to_s16_unsigned + %u32_to_u16_unsigned = OpUConvert %ushort %u32 + OpStore %ptr_u16 %u32_to_u16_unsigned + + ; SToF + %s16_to_f32_signed = OpConvertSToF %float %s16 + OpStore %ptr_f32 %s16_to_f32_signed + %u16_to_f32_signed = OpConvertSToF %float %u16 + OpStore %ptr_f32 %u16_to_f32_signed + %s16_to_f32_unsigned = OpConvertUToF %float %s16 + OpStore %ptr_f32 %s16_to_f32_unsigned + %u16_to_f32_unsigned = OpConvertUToF %float %u16 + OpStore %ptr_f32 %u16_to_f32_unsigned + + ; FToS + %f32_to_s16_signed = OpConvertFToS %short %f32 + OpStore %ptr_s16 %f32_to_s16_signed + %f32_to_u16_signed = OpConvertFToS %ushort %f32 + OpStore %ptr_u16 %f32_to_u16_signed + + ; FToU + %f32_to_u16_unsigned = OpConvertFToU %ushort %f32 + OpStore %ptr_u16 %f32_to_u16_unsigned + ; Result must be unsigned for FToU, so don't bother testing that. + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/atomic-load-store.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/atomic-load-store.asm.comp new file mode 100644 index 0000000..3f2d141 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/atomic-load-store.asm.comp @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 23 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %c "c" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpMemberName %SSBO 1 "b" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %SSBO = OpTypeStruct %uint %uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_0 = OpConstant %int 0 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %c = OpVariable %_ptr_Function_uint Function + %15 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 + %16 = OpAtomicLoad %uint %15 %int_1 %int_0 + OpStore %c %16 + %18 = OpLoad %uint %c + %19 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 + OpAtomicStore %19 %int_1 %int_0 %18 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/atomic-result-temporary.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/atomic-result-temporary.asm.comp new file mode 100644 index 0000000..a323841 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/atomic-result-temporary.asm.comp @@ -0,0 +1,59 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "count" + OpMemberName %SSBO 1 "data" + OpName %_ "" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO = OpTypeStruct %uint %_runtimearr_uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %uint_1024 = OpConstant %uint 1024 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%_ptr_Input_uint = OpTypePointer Input %uint + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 + %19 = OpAtomicIAdd %uint %16 %uint_1 %uint_0 %uint_1 + %23 = OpULessThan %bool %19 %uint_1024 + OpSelectionMerge %25 None + OpBranchConditional %23 %24 %25 + %24 = OpLabel + %32 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %33 = OpLoad %uint %32 + %34 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 %19 + OpStore %34 %33 + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitcast-fp16-fp32.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitcast-fp16-fp32.asm.comp new file mode 100644 index 0000000..3651a4d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitcast-fp16-fp32.asm.comp @@ -0,0 +1,63 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 33 +; Schema: 0 + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types" + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpMemberName %SSBO 1 "b" + OpMemberName %SSBO 2 "c" + OpMemberName %SSBO 3 "d" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpMemberDecorate %SSBO 2 Offset 8 + OpMemberDecorate %SSBO 3 Offset 12 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %half = OpTypeFloat 16 + %v2half = OpTypeVector %half 2 + %float = OpTypeFloat 32 + %SSBO = OpTypeStruct %v2half %float %float %v2half +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v2half = OpTypePointer Uniform %v2half + %uint = OpTypeInt 32 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %int_3 = OpConstant %int 3 + %int_2 = OpConstant %int 2 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Uniform_v2half %_ %int_0 + %17 = OpLoad %v2half %16 + %20 = OpBitcast %float %17 + %22 = OpAccessChain %_ptr_Uniform_float %_ %int_1 + OpStore %22 %20 + %25 = OpAccessChain %_ptr_Uniform_float %_ %int_2 + %26 = OpLoad %float %25 + %28 = OpBitcast %v2half %26 + %29 = OpAccessChain %_ptr_Uniform_v2half %_ %int_3 + OpStore %29 %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitfield-signed-operations.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitfield-signed-operations.asm.comp new file mode 100644 index 0000000..435fa32 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitfield-signed-operations.asm.comp @@ -0,0 +1,97 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 26 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "ints" + OpMemberName %SSBO 1 "uints" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + + %int_1 = OpConstant %int 1 + %uint_11 = OpConstant %uint 11 + + %SSBO = OpTypeStruct %v4int %v4uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4int = OpTypePointer Uniform %v4int +%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint + %main = OpFunction %void None %3 + %5 = OpLabel + %ints_ptr = OpAccessChain %_ptr_Uniform_v4int %_ %int_0 + %uints_ptr = OpAccessChain %_ptr_Uniform_v4uint %_ %int_1 + %ints = OpLoad %v4int %ints_ptr + %uints = OpLoad %v4uint %uints_ptr + + %ints_alt = OpVectorShuffle %v4int %ints %ints 3 2 1 0 + %uints_alt = OpVectorShuffle %v4uint %uints %uints 3 2 1 0 + + %int_to_int_popcount = OpBitCount %v4int %ints + %int_to_uint_popcount = OpBitCount %v4uint %ints + %uint_to_int_popcount = OpBitCount %v4int %uints + %uint_to_uint_popcount = OpBitCount %v4uint %uints + + ; BitReverse must have matching types w.r.t. sign, yay. + %int_to_int_reverse = OpBitReverse %v4int %ints + ;%int_to_uint_reverse = OpBitReverse %v4uint %ints + ;%uint_to_int_reverse = OpBitReverse %v4int %uints + %uint_to_uint_reverse = OpBitReverse %v4uint %uints + + ; Base and Result must match. + %int_to_int_sbit = OpBitFieldSExtract %v4int %ints %int_1 %uint_11 + ;%int_to_uint_sbit = OpBitFieldSExtract %v4uint %ints %offset %count + ;%uint_to_int_sbit = OpBitFieldSExtract %v4int %uints %offset %count + %uint_to_uint_sbit = OpBitFieldSExtract %v4uint %uints %uint_11 %int_1 + + ; Base and Result must match. + %int_to_int_ubit = OpBitFieldUExtract %v4int %ints %int_1 %uint_11 + ;%int_to_uint_ubit = OpBitFieldUExtract %v4uint %ints %offset %count + ;%uint_to_int_ubit = OpBitFieldUExtract %v4int %uints %offset %count + %uint_to_uint_ubit = OpBitFieldUExtract %v4uint %uints %uint_11 %int_1 + + %int_to_int_insert = OpBitFieldInsert %v4int %ints %ints_alt %int_1 %uint_11 + %uint_to_uint_insert = OpBitFieldInsert %v4uint %uints %uints_alt %uint_11 %int_1 + + OpStore %ints_ptr %int_to_int_popcount + OpStore %uints_ptr %int_to_uint_popcount + OpStore %ints_ptr %uint_to_int_popcount + OpStore %uints_ptr %uint_to_uint_popcount + + OpStore %ints_ptr %int_to_int_reverse + ;OpStore %uints_ptr %int_to_uint_reverse + ;OpStore %ints_ptr %uint_to_int_reverse + OpStore %uints_ptr %uint_to_uint_reverse + + OpStore %ints_ptr %int_to_int_sbit + ;OpStore %uints_ptr %int_to_uint_sbit + ;OpStore %ints_ptr %uint_to_int_sbit + OpStore %uints_ptr %uint_to_uint_sbit + + OpStore %ints_ptr %int_to_int_ubit + ;OpStore %uints_ptr %int_to_uint_ubit + ;OpStore %ints_ptr %uint_to_int_ubit + OpStore %uints_ptr %uint_to_uint_ubit + + OpStore %ints_ptr %int_to_int_insert + OpStore %uints_ptr %uint_to_uint_insert + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitscan.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitscan.asm.comp new file mode 100644 index 0000000..e3b785c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/bitscan.asm.comp @@ -0,0 +1,72 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "u" + OpMemberName %SSBO 1 "i" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + %SSBO = OpTypeStruct %uvec4 %ivec4 +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uvec4 = OpTypePointer Uniform %uvec4 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_ivec4 = OpTypePointer Uniform %ivec4 + %main = OpFunction %void None %3 + %5 = OpLabel + %uptr = OpAccessChain %_ptr_Uniform_uvec4 %_ %int_0 + %iptr = OpAccessChain %_ptr_Uniform_ivec4 %_ %int_1 + %uvalue = OpLoad %uvec4 %uptr + %ivalue = OpLoad %ivec4 %iptr + + %lsb_uint_to_uint = OpExtInst %uvec4 %1 FindILsb %uvalue + %lsb_uint_to_int = OpExtInst %ivec4 %1 FindILsb %uvalue + %lsb_int_to_uint = OpExtInst %uvec4 %1 FindILsb %ivalue + %lsb_int_to_int = OpExtInst %ivec4 %1 FindILsb %ivalue + + %umsb_uint_to_uint = OpExtInst %uvec4 %1 FindUMsb %uvalue + %umsb_uint_to_int = OpExtInst %ivec4 %1 FindUMsb %uvalue + %umsb_int_to_uint = OpExtInst %uvec4 %1 FindUMsb %ivalue + %umsb_int_to_int = OpExtInst %ivec4 %1 FindUMsb %ivalue + + %smsb_uint_to_uint = OpExtInst %uvec4 %1 FindSMsb %uvalue + %smsb_uint_to_int = OpExtInst %ivec4 %1 FindSMsb %uvalue + %smsb_int_to_uint = OpExtInst %uvec4 %1 FindSMsb %ivalue + %smsb_int_to_int = OpExtInst %ivec4 %1 FindSMsb %ivalue + + OpStore %uptr %lsb_uint_to_uint + OpStore %iptr %lsb_uint_to_int + OpStore %uptr %lsb_int_to_uint + OpStore %iptr %lsb_int_to_int + + OpStore %uptr %umsb_uint_to_uint + OpStore %iptr %umsb_uint_to_int + OpStore %uptr %umsb_int_to_uint + OpStore %iptr %umsb_int_to_int + + OpStore %uptr %smsb_uint_to_uint + OpStore %iptr %smsb_uint_to_int + OpStore %uptr %smsb_int_to_uint + OpStore %iptr %smsb_int_to_int + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/composite-construct-buffer-struct.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/composite-construct-buffer-struct.asm.comp new file mode 100644 index 0000000..c7b76a8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/composite-construct-buffer-struct.asm.comp @@ -0,0 +1,54 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpName %Block "Block" + OpName %SSBO "SSBO" + OpName %SSBO_Var "ssbo" + OpName %UBO_Var "ubo" + OpDecorate %SSBO_Var Binding 0 + OpDecorate %SSBO_Var DescriptorSet 0 + OpDecorate %UBO_Var Binding 1 + OpDecorate %UBO_Var DescriptorSet 0 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %Block 0 Offset 0 + OpMemberDecorate %Block 1 Offset 16 + OpDecorate %BlockArray ArrayStride 32 + OpDecorate %arr_uvec2_2 ArrayStride 8 + OpDecorate %SSBO Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uvec2 = OpTypeVector %uint 2 + %arr_uvec2_2 = OpTypeArray %uvec2 %uint_2 + %arr_uvec2_2_ptr = OpTypePointer StorageBuffer %arr_uvec2_2 + %arr_uvec2_2_ptr_const = OpTypePointer Uniform %arr_uvec2_2 + %arr_uvec2_2_ptr_func = OpTypePointer Function %arr_uvec2_2 + %arr_uvec2_2_ptr_workgroup = OpTypePointer Workgroup %arr_uvec2_2 + %wg = OpVariable %arr_uvec2_2_ptr_workgroup Workgroup + %Block = OpTypeStruct %arr_uvec2_2 %arr_uvec2_2 + %Block_ptr = OpTypePointer StorageBuffer %Block +%BlockArray = OpTypeArray %Block %uint_3 +%SSBO = OpTypeStruct %BlockArray +%SSBO_Ptr = OpTypePointer StorageBuffer %SSBO +%SSBO_Var = OpVariable %SSBO_Ptr StorageBuffer +%UBO_Ptr = OpTypePointer Uniform %SSBO +%UBO_Var = OpVariable %UBO_Ptr Uniform +%void = OpTypeVoid +%func_type = OpTypeFunction %void + + %main = OpFunction %void None %func_type + %25 = OpLabel + %func = OpVariable %arr_uvec2_2_ptr_func Function + + ; Copy device array to temporary. + %ptr = OpAccessChain %Block_ptr %SSBO_Var %uint_0 %uint_0 + %ptr_arr_1 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_1 + %loaded_array = OpLoad %arr_uvec2_2 %ptr_arr_1 + %constructed = OpCompositeConstruct %Block %loaded_array %loaded_array + OpStore %ptr %constructed + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/constant-composite-undef.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/constant-composite-undef.asm.comp new file mode 100644 index 0000000..8997d0a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/constant-composite-undef.asm.comp @@ -0,0 +1,40 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Block "Block" + OpMemberName %Block 0 "f" + OpName %block "block" + OpMemberDecorate %Block 0 Offset 0 + OpDecorate %Block BufferBlock + OpDecorate %block DescriptorSet 0 + OpDecorate %block Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Block = OpTypeStruct %v4float +%_ptr_Uniform_Block = OpTypePointer Uniform %Block + %block = OpVariable %_ptr_Uniform_Block Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%float_0_100000001 = OpConstant %float 0.100000001 +%float_0_200000003 = OpConstant %float 0.200000003 +%float_0_300000012 = OpConstant %float 0.300000012 + %15 = OpUndef %float + %16 = OpConstantComposite %v4float %float_0_100000001 %float_0_200000003 %float_0_300000012 %15 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %main = OpFunction %void None %6 + %18 = OpLabel + %19 = OpAccessChain %_ptr_Uniform_v4float %block %int_0 + OpStore %19 %16 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/copy-logical-2.spv14.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/copy-logical-2.spv14.asm.comp new file mode 100644 index 0000000..6a7065a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/copy-logical-2.spv14.asm.comp @@ -0,0 +1,81 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 48 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %ssbo + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %B1 "B1" + OpName %A "A" + OpName %C "C" + OpName %B2 "B2" + OpMemberName %A 0 "a" + OpMemberName %A 1 "b1" + OpMemberName %A 2 "b1_array" + OpMemberName %C 0 "c" + OpMemberName %C 1 "b2" + OpMemberName %C 2 "b2_array" + OpMemberName %B1 0 "elem1" + OpMemberName %B2 0 "elem2" + OpMemberName %SSBO 0 "a_block" + OpMemberName %SSBO 1 "c_block" + OpDecorate %B1Array ArrayStride 16 + OpDecorate %B2Array ArrayStride 16 + OpMemberDecorate %B1 0 Offset 0 + OpMemberDecorate %A 0 Offset 0 + OpMemberDecorate %A 1 Offset 16 + OpMemberDecorate %A 2 Offset 32 + OpMemberDecorate %A 3 Offset 96 + OpMemberDecorate %B2 0 Offset 0 + OpMemberDecorate %C 0 Offset 0 + OpMemberDecorate %C 1 Offset 16 + OpMemberDecorate %C 2 Offset 32 + OpMemberDecorate %C 3 Offset 96 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 112 + OpMemberDecorate %A0 0 Offset 0 + OpMemberDecorate %C0 0 Offset 0 + OpMemberDecorate %A0 0 RowMajor + OpMemberDecorate %A0 0 MatrixStride 8 + OpMemberDecorate %C0 0 ColMajor + OpMemberDecorate %C0 0 MatrixStride 16 + OpDecorate %SSBO Block + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 + %m2float = OpTypeMatrix %v2float 2 + %A0 = OpTypeStruct %m2float + %C0 = OpTypeStruct %m2float + %B2 = OpTypeStruct %v4float + %B2Array = OpTypeArray %B2 %uint_4 + %C = OpTypeStruct %v4float %B2 %B2Array %C0 + %B1 = OpTypeStruct %v4float + %B1Array = OpTypeArray %B1 %uint_4 + %A = OpTypeStruct %v4float %B1 %B1Array %A0 + %SSBO = OpTypeStruct %A %C +%_ptr_Uniform_SSBO = OpTypePointer StorageBuffer %SSBO + %ssbo = OpVariable %_ptr_Uniform_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_C = OpTypePointer StorageBuffer %C + %int_0 = OpConstant %int 0 +%_ptr_Uniform_A = OpTypePointer StorageBuffer %A + %main = OpFunction %void None %3 + %5 = OpLabel + %22 = OpAccessChain %_ptr_Uniform_C %ssbo %int_1 + %39 = OpAccessChain %_ptr_Uniform_A %ssbo %int_0 + %23 = OpLoad %C %22 + %24 = OpCopyLogical %A %23 + OpStore %39 %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/copy-logical.spv14.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/copy-logical.spv14.asm.comp new file mode 100644 index 0000000..20fa0b0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/copy-logical.spv14.asm.comp @@ -0,0 +1,69 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 48 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %ssbo + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %B1 "B1" + OpName %A "A" + OpName %C "C" + OpName %B2 "B2" + OpMemberName %A 0 "a" + OpMemberName %A 1 "b1" + OpMemberName %A 2 "b1_array" + OpMemberName %C 0 "c" + OpMemberName %C 1 "b2" + OpMemberName %C 2 "b2_array" + OpMemberName %B1 0 "elem1" + OpMemberName %B2 0 "elem2" + OpMemberName %SSBO 0 "a_block" + OpMemberName %SSBO 1 "c_block" + OpDecorate %B1Array ArrayStride 16 + OpDecorate %B2Array ArrayStride 16 + OpMemberDecorate %B1 0 Offset 0 + OpMemberDecorate %A 0 Offset 0 + OpMemberDecorate %A 1 Offset 16 + OpMemberDecorate %A 2 Offset 32 + OpMemberDecorate %B2 0 Offset 0 + OpMemberDecorate %C 0 Offset 0 + OpMemberDecorate %C 1 Offset 16 + OpMemberDecorate %C 2 Offset 32 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 96 + OpDecorate %SSBO Block + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %v4float = OpTypeVector %float 4 + %B2 = OpTypeStruct %v4float + %B2Array = OpTypeArray %B2 %uint_4 + %C = OpTypeStruct %v4float %B2 %B2Array + %B1 = OpTypeStruct %v4float + %B1Array = OpTypeArray %B1 %uint_4 + %A = OpTypeStruct %v4float %B1 %B1Array + %SSBO = OpTypeStruct %A %C +%_ptr_Uniform_SSBO = OpTypePointer StorageBuffer %SSBO + %ssbo = OpVariable %_ptr_Uniform_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_C = OpTypePointer StorageBuffer %C + %int_0 = OpConstant %int 0 +%_ptr_Uniform_A = OpTypePointer StorageBuffer %A + %main = OpFunction %void None %3 + %5 = OpLabel + %22 = OpAccessChain %_ptr_Uniform_C %ssbo %int_1 + %39 = OpAccessChain %_ptr_Uniform_A %ssbo %int_0 + %23 = OpLoad %C %22 + %24 = OpCopyLogical %A %23 + OpStore %39 %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-array-load-temporary.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-array-load-temporary.asm.comp new file mode 100644 index 0000000..d59aad3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-array-load-temporary.asm.comp @@ -0,0 +1,53 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpName %Block "Block" + OpName %SSBO "SSBO" + OpName %SSBO_Var "ssbo" + OpName %UBO_Var "ubo" + OpDecorate %SSBO_Var Binding 0 + OpDecorate %SSBO_Var DescriptorSet 0 + OpDecorate %UBO_Var Binding 1 + OpDecorate %UBO_Var DescriptorSet 0 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %Block 0 Offset 0 + OpMemberDecorate %Block 1 Offset 16 + OpDecorate %BlockArray ArrayStride 32 + OpDecorate %arr_uvec2_2 ArrayStride 8 + OpDecorate %SSBO Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uvec2 = OpTypeVector %uint 2 + %arr_uvec2_2 = OpTypeArray %uvec2 %uint_2 + %arr_uvec2_2_ptr = OpTypePointer StorageBuffer %arr_uvec2_2 + %arr_uvec2_2_ptr_const = OpTypePointer Uniform %arr_uvec2_2 + %arr_uvec2_2_ptr_func = OpTypePointer Function %arr_uvec2_2 + %arr_uvec2_2_ptr_workgroup = OpTypePointer Workgroup %arr_uvec2_2 + %wg = OpVariable %arr_uvec2_2_ptr_workgroup Workgroup + %Block = OpTypeStruct %arr_uvec2_2 %arr_uvec2_2 +%BlockArray = OpTypeArray %Block %uint_3 +%SSBO = OpTypeStruct %BlockArray +%SSBO_Ptr = OpTypePointer StorageBuffer %SSBO +%SSBO_Var = OpVariable %SSBO_Ptr StorageBuffer +%UBO_Ptr = OpTypePointer Uniform %SSBO +%UBO_Var = OpVariable %UBO_Ptr Uniform +%void = OpTypeVoid +%func_type = OpTypeFunction %void + + %main = OpFunction %void None %func_type + %25 = OpLabel + %func = OpVariable %arr_uvec2_2_ptr_func Function + + ; Copy device array to temporary. + %ptr_arr_0 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_0 + %ptr_arr_1 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_1 + %loaded_array = OpLoad %arr_uvec2_2 %ptr_arr_1 + OpStore %ptr_arr_0 %loaded_array + OpStore %ptr_arr_0 %loaded_array + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-array-load-temporary.force-native-array.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-array-load-temporary.force-native-array.asm.comp new file mode 100644 index 0000000..d59aad3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-array-load-temporary.force-native-array.asm.comp @@ -0,0 +1,53 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpName %Block "Block" + OpName %SSBO "SSBO" + OpName %SSBO_Var "ssbo" + OpName %UBO_Var "ubo" + OpDecorate %SSBO_Var Binding 0 + OpDecorate %SSBO_Var DescriptorSet 0 + OpDecorate %UBO_Var Binding 1 + OpDecorate %UBO_Var DescriptorSet 0 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %Block 0 Offset 0 + OpMemberDecorate %Block 1 Offset 16 + OpDecorate %BlockArray ArrayStride 32 + OpDecorate %arr_uvec2_2 ArrayStride 8 + OpDecorate %SSBO Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uvec2 = OpTypeVector %uint 2 + %arr_uvec2_2 = OpTypeArray %uvec2 %uint_2 + %arr_uvec2_2_ptr = OpTypePointer StorageBuffer %arr_uvec2_2 + %arr_uvec2_2_ptr_const = OpTypePointer Uniform %arr_uvec2_2 + %arr_uvec2_2_ptr_func = OpTypePointer Function %arr_uvec2_2 + %arr_uvec2_2_ptr_workgroup = OpTypePointer Workgroup %arr_uvec2_2 + %wg = OpVariable %arr_uvec2_2_ptr_workgroup Workgroup + %Block = OpTypeStruct %arr_uvec2_2 %arr_uvec2_2 +%BlockArray = OpTypeArray %Block %uint_3 +%SSBO = OpTypeStruct %BlockArray +%SSBO_Ptr = OpTypePointer StorageBuffer %SSBO +%SSBO_Var = OpVariable %SSBO_Ptr StorageBuffer +%UBO_Ptr = OpTypePointer Uniform %SSBO +%UBO_Var = OpVariable %UBO_Ptr Uniform +%void = OpTypeVoid +%func_type = OpTypeFunction %void + + %main = OpFunction %void None %func_type + %25 = OpLabel + %func = OpVariable %arr_uvec2_2_ptr_func Function + + ; Copy device array to temporary. + %ptr_arr_0 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_0 + %ptr_arr_1 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_1 + %loaded_array = OpLoad %arr_uvec2_2 %ptr_arr_1 + OpStore %ptr_arr_0 %loaded_array + OpStore %ptr_arr_0 %loaded_array + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.asm.comp new file mode 100644 index 0000000..d9d0d51 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.asm.comp @@ -0,0 +1,81 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpName %Block "Block" + OpName %SSBO "SSBO" + OpName %SSBO_Var "ssbo" + OpName %UBO_Var "ubo" + OpDecorate %SSBO_Var Binding 0 + OpDecorate %SSBO_Var DescriptorSet 0 + OpDecorate %UBO_Var Binding 1 + OpDecorate %UBO_Var DescriptorSet 0 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %Block 0 Offset 0 + OpMemberDecorate %Block 1 Offset 16 + OpDecorate %BlockArray ArrayStride 32 + OpDecorate %arr_uvec2_2 ArrayStride 8 + OpDecorate %SSBO Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uvec2 = OpTypeVector %uint 2 + %arr_uvec2_2 = OpTypeArray %uvec2 %uint_2 + %arr_uvec2_2_ptr = OpTypePointer StorageBuffer %arr_uvec2_2 + %arr_uvec2_2_ptr_const = OpTypePointer Uniform %arr_uvec2_2 + %arr_uvec2_2_ptr_func = OpTypePointer Function %arr_uvec2_2 + %arr_uvec2_2_ptr_workgroup = OpTypePointer Workgroup %arr_uvec2_2 + %wg = OpVariable %arr_uvec2_2_ptr_workgroup Workgroup + %Block = OpTypeStruct %arr_uvec2_2 %arr_uvec2_2 +%BlockArray = OpTypeArray %Block %uint_3 +%SSBO = OpTypeStruct %BlockArray +%SSBO_Ptr = OpTypePointer StorageBuffer %SSBO +%SSBO_Var = OpVariable %SSBO_Ptr StorageBuffer +%UBO_Ptr = OpTypePointer Uniform %SSBO +%UBO_Var = OpVariable %UBO_Ptr Uniform +%void = OpTypeVoid +%func_type = OpTypeFunction %void + + %main = OpFunction %void None %func_type + %25 = OpLabel + %func = OpVariable %arr_uvec2_2_ptr_func Function + + ; DeviceToDevice + %ptr_arr_0 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_0 + %ptr_arr_1 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_1 + %loaded_array = OpLoad %arr_uvec2_2 %ptr_arr_1 + OpStore %ptr_arr_0 %loaded_array + + ; ConstantToDevice + %ptr_arr_1_const = OpAccessChain %arr_uvec2_2_ptr_const %UBO_Var %uint_0 %uint_0 %uint_1 + %loaded_array_const = OpLoad %arr_uvec2_2 %ptr_arr_1_const + OpStore %ptr_arr_0 %loaded_array_const + + ; StackToDevice + %loaded_array_func = OpLoad %arr_uvec2_2 %func + OpStore %ptr_arr_0 %loaded_array_func + + ; ThreadGroupToDevice + %loaded_array_workgroup = OpLoad %arr_uvec2_2 %wg + OpStore %ptr_arr_0 %loaded_array_workgroup + + ; DeviceToThreadGroup + %loaded_array_2 = OpLoad %arr_uvec2_2 %ptr_arr_1 + OpStore %wg %loaded_array_2 + + ; DeviceToStack + %loaded_array_3 = OpLoad %arr_uvec2_2 %ptr_arr_1 + OpStore %func %loaded_array_3 + + ; ConstantToThreadGroup + %loaded_array_const_2 = OpLoad %arr_uvec2_2 %ptr_arr_1_const + OpStore %wg %loaded_array_const_2 + + ; ConstantToStack + %loaded_array_const_3 = OpLoad %arr_uvec2_2 %ptr_arr_1_const + OpStore %func %loaded_array_const_3 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.force-native-array.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.force-native-array.asm.comp new file mode 100644 index 0000000..d9d0d51 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/device-constant-array-load-store.force-native-array.asm.comp @@ -0,0 +1,81 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpName %Block "Block" + OpName %SSBO "SSBO" + OpName %SSBO_Var "ssbo" + OpName %UBO_Var "ubo" + OpDecorate %SSBO_Var Binding 0 + OpDecorate %SSBO_Var DescriptorSet 0 + OpDecorate %UBO_Var Binding 1 + OpDecorate %UBO_Var DescriptorSet 0 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %Block 0 Offset 0 + OpMemberDecorate %Block 1 Offset 16 + OpDecorate %BlockArray ArrayStride 32 + OpDecorate %arr_uvec2_2 ArrayStride 8 + OpDecorate %SSBO Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %uvec2 = OpTypeVector %uint 2 + %arr_uvec2_2 = OpTypeArray %uvec2 %uint_2 + %arr_uvec2_2_ptr = OpTypePointer StorageBuffer %arr_uvec2_2 + %arr_uvec2_2_ptr_const = OpTypePointer Uniform %arr_uvec2_2 + %arr_uvec2_2_ptr_func = OpTypePointer Function %arr_uvec2_2 + %arr_uvec2_2_ptr_workgroup = OpTypePointer Workgroup %arr_uvec2_2 + %wg = OpVariable %arr_uvec2_2_ptr_workgroup Workgroup + %Block = OpTypeStruct %arr_uvec2_2 %arr_uvec2_2 +%BlockArray = OpTypeArray %Block %uint_3 +%SSBO = OpTypeStruct %BlockArray +%SSBO_Ptr = OpTypePointer StorageBuffer %SSBO +%SSBO_Var = OpVariable %SSBO_Ptr StorageBuffer +%UBO_Ptr = OpTypePointer Uniform %SSBO +%UBO_Var = OpVariable %UBO_Ptr Uniform +%void = OpTypeVoid +%func_type = OpTypeFunction %void + + %main = OpFunction %void None %func_type + %25 = OpLabel + %func = OpVariable %arr_uvec2_2_ptr_func Function + + ; DeviceToDevice + %ptr_arr_0 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_0 + %ptr_arr_1 = OpAccessChain %arr_uvec2_2_ptr %SSBO_Var %uint_0 %uint_0 %uint_1 + %loaded_array = OpLoad %arr_uvec2_2 %ptr_arr_1 + OpStore %ptr_arr_0 %loaded_array + + ; ConstantToDevice + %ptr_arr_1_const = OpAccessChain %arr_uvec2_2_ptr_const %UBO_Var %uint_0 %uint_0 %uint_1 + %loaded_array_const = OpLoad %arr_uvec2_2 %ptr_arr_1_const + OpStore %ptr_arr_0 %loaded_array_const + + ; StackToDevice + %loaded_array_func = OpLoad %arr_uvec2_2 %func + OpStore %ptr_arr_0 %loaded_array_func + + ; ThreadGroupToDevice + %loaded_array_workgroup = OpLoad %arr_uvec2_2 %wg + OpStore %ptr_arr_0 %loaded_array_workgroup + + ; DeviceToThreadGroup + %loaded_array_2 = OpLoad %arr_uvec2_2 %ptr_arr_1 + OpStore %wg %loaded_array_2 + + ; DeviceToStack + %loaded_array_3 = OpLoad %arr_uvec2_2 %ptr_arr_1 + OpStore %func %loaded_array_3 + + ; ConstantToThreadGroup + %loaded_array_const_2 = OpLoad %arr_uvec2_2 %ptr_arr_1_const + OpStore %wg %loaded_array_const_2 + + ; ConstantToStack + %loaded_array_const_3 = OpLoad %arr_uvec2_2 %ptr_arr_1_const + OpStore %func %loaded_array_const_3 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/glsl-signed-operations.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/glsl-signed-operations.asm.comp new file mode 100644 index 0000000..7da9f95 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/glsl-signed-operations.asm.comp @@ -0,0 +1,123 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 26 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "ints" + OpMemberName %SSBO 1 "uints" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + %SSBO = OpTypeStruct %v4int %v4uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4int = OpTypePointer Uniform %v4int + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint + %main = OpFunction %void None %3 + %5 = OpLabel + %ints_ptr = OpAccessChain %_ptr_Uniform_v4int %_ %int_0 + %uints_ptr = OpAccessChain %_ptr_Uniform_v4uint %_ %int_1 + %ints = OpLoad %v4int %ints_ptr + %uints = OpLoad %v4uint %uints_ptr + + %int_to_int_sabs = OpExtInst %v4int %1 SAbs %ints + %int_to_uint_sabs = OpExtInst %v4uint %1 SAbs %ints + %uint_to_int_sabs = OpExtInst %v4int %1 SAbs %uints + %uint_to_uint_sabs = OpExtInst %v4uint %1 SAbs %uints + + %int_to_int_ssign = OpExtInst %v4int %1 SSign %ints + %int_to_uint_ssign = OpExtInst %v4uint %1 SSign %ints + %uint_to_int_ssign = OpExtInst %v4int %1 SSign %uints + %uint_to_uint_ssign = OpExtInst %v4uint %1 SSign %uints + + %int_to_int_smsb = OpExtInst %v4int %1 FindSMsb %uints + %int_to_uint_smsb = OpExtInst %v4uint %1 FindSMsb %uints + %uint_to_int_umsb = OpExtInst %v4int %1 FindUMsb %ints + %uint_to_uint_umsb = OpExtInst %v4uint %1 FindUMsb %ints + + %int_to_int_smin = OpExtInst %v4int %1 SMin %ints %ints + %int_to_uint_smin = OpExtInst %v4uint %1 SMin %ints %uints + %uint_to_int_smin = OpExtInst %v4int %1 SMin %uints %uints + %uint_to_uint_smin = OpExtInst %v4uint %1 SMin %uints %ints + + %int_to_int_umin = OpExtInst %v4int %1 UMin %ints %uints + %int_to_uint_umin = OpExtInst %v4uint %1 UMin %ints %uints + %uint_to_int_umin = OpExtInst %v4int %1 UMin %uints %ints + %uint_to_uint_umin = OpExtInst %v4uint %1 UMin %uints %ints + + %int_to_int_smax = OpExtInst %v4int %1 SMax %ints %ints + %int_to_uint_smax = OpExtInst %v4uint %1 SMax %ints %ints + %uint_to_int_smax = OpExtInst %v4int %1 SMax %uints %ints + %uint_to_uint_smax = OpExtInst %v4uint %1 SMax %uints %ints + + %int_to_int_umax = OpExtInst %v4int %1 UMax %ints %uints + %int_to_uint_umax = OpExtInst %v4uint %1 UMax %ints %ints + %uint_to_int_umax = OpExtInst %v4int %1 UMax %uints %ints + %uint_to_uint_umax = OpExtInst %v4uint %1 UMax %uints %ints + + %int_to_int_sclamp = OpExtInst %v4int %1 SClamp %uints %uints %uints + %int_to_uint_sclamp = OpExtInst %v4uint %1 SClamp %uints %uints %uints + %uint_to_int_uclamp = OpExtInst %v4int %1 UClamp %ints %ints %ints + %uint_to_uint_uclamp = OpExtInst %v4uint %1 UClamp %ints %ints %ints + + OpStore %ints_ptr %int_to_int_sabs + OpStore %uints_ptr %int_to_uint_sabs + OpStore %ints_ptr %uint_to_int_sabs + OpStore %uints_ptr %uint_to_uint_sabs + + OpStore %ints_ptr %int_to_int_ssign + OpStore %uints_ptr %int_to_uint_ssign + OpStore %ints_ptr %uint_to_int_ssign + OpStore %uints_ptr %uint_to_uint_ssign + + OpStore %ints_ptr %int_to_int_smsb + OpStore %uints_ptr %int_to_uint_smsb + OpStore %ints_ptr %uint_to_int_umsb + OpStore %uints_ptr %uint_to_uint_umsb + + OpStore %ints_ptr %int_to_int_smin + OpStore %uints_ptr %int_to_uint_smin + OpStore %ints_ptr %uint_to_int_smin + OpStore %uints_ptr %uint_to_uint_smin + + OpStore %ints_ptr %int_to_int_umin + OpStore %uints_ptr %int_to_uint_umin + OpStore %ints_ptr %uint_to_int_umin + OpStore %uints_ptr %uint_to_uint_umin + + OpStore %ints_ptr %int_to_int_smax + OpStore %uints_ptr %int_to_uint_smax + OpStore %ints_ptr %uint_to_int_smax + OpStore %uints_ptr %uint_to_uint_smax + + OpStore %ints_ptr %int_to_int_umax + OpStore %uints_ptr %int_to_uint_umax + OpStore %ints_ptr %uint_to_int_umax + OpStore %uints_ptr %uint_to_uint_umax + + OpStore %ints_ptr %int_to_int_sclamp + OpStore %uints_ptr %int_to_uint_sclamp + OpStore %ints_ptr %uint_to_int_uclamp + OpStore %uints_ptr %uint_to_uint_uclamp + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp new file mode 100644 index 0000000..30db11d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %ResTypeMod = OpTypeStruct %float %float +%_ptr_Function_ResTypeMod = OpTypePointer Function %ResTypeMod + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_20 = OpConstant %float 20 + %int_1 = OpConstant %int 1 +%_ptr_Function_float = OpTypePointer Function %float +%ResTypeFrexp = OpTypeStruct %float %int +%_ptr_Function_ResTypeFrexp = OpTypePointer Function %ResTypeFrexp + %float_40 = OpConstant %float 40 +%_ptr_Function_int = OpTypePointer Function %int + %SSBO = OpTypeStruct %float %int +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_int = OpTypePointer Uniform %int + %main = OpFunction %void None %3 + %5 = OpLabel + %modres = OpExtInst %ResTypeMod %1 ModfStruct %float_20 + %frexpres = OpExtInst %ResTypeFrexp %1 FrexpStruct %float_40 + + %modres_f = OpCompositeExtract %float %modres 0 + %modres_i = OpCompositeExtract %float %modres 1 + %frexpres_f = OpCompositeExtract %float %frexpres 0 + %frexpres_i = OpCompositeExtract %int %frexpres 1 + + %float_ptr = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %int_ptr = OpAccessChain %_ptr_Uniform_int %_ %int_1 + + OpStore %float_ptr %modres_f + OpStore %float_ptr %modres_i + OpStore %float_ptr %frexpres_f + OpStore %int_ptr %frexpres_i + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp new file mode 100644 index 0000000..bdf2027 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp @@ -0,0 +1,58 @@ +; SPIR-V +; Version: 1.0 +; Generator: Codeplay; 0 +; Bound: 31 +; Schema: 0 + OpCapability Shader + OpCapability VariablePointers + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_KHR_variable_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %22 "main" %gl_WorkGroupID + OpSource OpenCL_C 120 + OpDecorate %15 SpecId 0 + ;OpDecorate %16 SpecId 1 + OpDecorate %17 SpecId 2 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_4 0 Offset 0 + OpDecorate %_struct_4 Block + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + OpDecorate %20 DescriptorSet 0 + OpDecorate %20 Binding 0 + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %size1 = OpConstant %uint 1 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +%_runtimearr_float = OpTypeArray %float %size1 ; Runtime arrays do not work yet in MSL. + %_struct_4 = OpTypeStruct %_runtimearr_float +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 + %void = OpTypeVoid + %8 = OpTypeFunction %void + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Private_v3uint = OpTypePointer Private %v3uint + %uint_0 = OpConstant %uint 0 +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %15 = OpSpecConstant %uint 1 + %16 = OpConstant %uint 2 + %17 = OpSpecConstant %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %15 %16 %17 + %19 = OpVariable %_ptr_Private_v3uint Private %gl_WorkGroupSize + %20 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer + %21 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer + %22 = OpFunction %void None %8 + %23 = OpLabel + %24 = OpAccessChain %_ptr_Input_uint %gl_WorkGroupID %uint_0 + %25 = OpLoad %uint %24 + %26 = OpAccessChain %_ptr_StorageBuffer_float %21 %uint_0 %25 + %27 = OpLoad %float %26 + %28 = OpAccessChain %_ptr_StorageBuffer_float %20 %uint_0 %25 + %29 = OpLoad %float %28 + %30 = OpFAdd %float %27 %29 + OpStore %28 %30 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/storage-buffer-pointer-argument.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/storage-buffer-pointer-argument.asm.comp new file mode 100644 index 0000000..010d17c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/storage-buffer-pointer-argument.asm.comp @@ -0,0 +1,63 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpCapability VariablePointersStorageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %copy_out_f1_f1_ "copy_out(f1;f1;" + OpName %A "A" + OpName %B "B" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpName %_ "" + OpName %SSBORead "SSBORead" + OpMemberName %SSBORead 0 "b" + OpName %__0 "" + OpMemberDecorate %SSBO 0 NonReadable + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpMemberDecorate %SSBORead 0 NonWritable + OpMemberDecorate %SSBORead 0 Offset 0 + OpDecorate %SSBORead Block + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + %8 = OpTypeFunction %void %_ptr_StorageBuffer_float %_ptr_StorageBuffer_float + %SSBO = OpTypeStruct %float +%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO + %_ = OpVariable %_ptr_StorageBuffer_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %SSBORead = OpTypeStruct %float +%_ptr_StorageBuffer_SSBORead = OpTypePointer StorageBuffer %SSBORead + %__0 = OpVariable %_ptr_StorageBuffer_SSBORead StorageBuffer + %main = OpFunction %void None %3 + %5 = OpLabel + %param = OpVariable %_ptr_Function_float Function + %param_0 = OpVariable %_ptr_Function_float Function + %25 = OpAccessChain %_ptr_StorageBuffer_float %_ %int_0 + %26 = OpAccessChain %_ptr_StorageBuffer_float %__0 %int_0 + %27 = OpFunctionCall %void %copy_out_f1_f1_ %25 %26 + OpReturn + OpFunctionEnd +%copy_out_f1_f1_ = OpFunction %void None %8 + %A = OpFunctionParameter %_ptr_StorageBuffer_float + %B = OpFunctionParameter %_ptr_StorageBuffer_float + %12 = OpLabel + %13 = OpLoad %float %B + OpStore %A %13 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp new file mode 100644 index 0000000..ba6267c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp @@ -0,0 +1,152 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 89 +; Schema: 0 + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %foo "foo" + OpMemberName %foo 0 "a" + OpMemberName %foo 1 "b" + OpMemberName %foo 2 "c" + OpName %bar "bar" + OpMemberName %bar 0 "d" + OpName %baz "baz" + OpMemberName %baz 0 "e" + OpName %buf "buf" + OpName %buf2 "buf2" + OpName %cb "cb" + OpName %tgsm "tgsm" + OpName %sbuf "sbuf" + OpName %sbuf2 "sbuf2" + OpName %stgsm "stgsm" + OpName %select_buffer "select_buffer" + OpName %select_buffer_null "select_buffer_null" + OpName %select_tgsm "select_tgsm" + OpName %cur "cur" + OpMemberDecorate %foo 0 Offset 0 + OpMemberDecorate %foo 1 Offset 512 + OpMemberDecorate %foo 2 Offset 520 + OpMemberDecorate %bar 0 Offset 0 + OpMemberDecorate %baz 0 Offset 0 + OpDecorate %foo Block + OpDecorate %bar Block + OpDecorate %baz Block + OpDecorate %buf DescriptorSet 0 + OpDecorate %buf Binding 0 + OpDecorate %cb DescriptorSet 0 + OpDecorate %cb Binding 3 + OpDecorate %buf2 DescriptorSet 0 + OpDecorate %buf2 Binding 4 + OpDecorate %_ptr_Workgroup_int ArrayStride 4 + OpDecorate %_ptr_StorageBuffer_int ArrayStride 4 + OpDecorate %_arr_int_uint_128 ArrayStride 4 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + %void = OpTypeVoid + %22 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_128 = OpConstant %uint 128 +%_arr_int_uint_128 = OpTypeArray %int %uint_128 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %foo = OpTypeStruct %_arr_int_uint_128 %uint %v2float +%_ptr_StorageBuffer_foo = OpTypePointer StorageBuffer %foo + %buf = OpVariable %_ptr_StorageBuffer_foo StorageBuffer + %bar = OpTypeStruct %int +%_ptr_Uniform_bar = OpTypePointer Uniform %bar + %cb = OpVariable %_ptr_Uniform_bar Uniform + %baz = OpTypeStruct %_arr_int_uint_128 +%_ptr_StorageBuffer_baz = OpTypePointer StorageBuffer %baz + %buf2 = OpVariable %_ptr_StorageBuffer_baz StorageBuffer +%_ptr_Workgroup__arr_int_uint_128 = OpTypePointer Workgroup %_arr_int_uint_128 + %tgsm = OpVariable %_ptr_Workgroup__arr_int_uint_128 Workgroup +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int +%_ptr_Private__ptr_StorageBuffer_int = OpTypePointer Private %_ptr_StorageBuffer_int + %sbuf = OpVariable %_ptr_Private__ptr_StorageBuffer_int Private + %sbuf2 = OpVariable %_ptr_Private__ptr_StorageBuffer_int Private +%_ptr_Workgroup_int = OpTypePointer Workgroup %int +%_ptr_Private__ptr_Workgroup_int = OpTypePointer Private %_ptr_Workgroup_int + %stgsm = OpVariable %_ptr_Private__ptr_Workgroup_int Private + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool +%_ptr_Uniform_int = OpTypePointer Uniform %int + %44 = OpTypeFunction %_ptr_StorageBuffer_int + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %47 = OpConstantNull %_ptr_StorageBuffer_int + %48 = OpTypeFunction %_ptr_Workgroup_int + %49 = OpConstantNull %_ptr_Workgroup_int +%_ptr_Function__ptr_Workgroup_int = OpTypePointer Function %_ptr_Workgroup_int +%select_buffer = OpFunction %_ptr_StorageBuffer_int None %44 + %51 = OpLabel + %52 = OpAccessChain %_ptr_Uniform_int %cb %uint_0 + %53 = OpLoad %int %52 + %54 = OpINotEqual %bool %53 %int_0 + %55 = OpAccessChain %_ptr_StorageBuffer_int %buf %uint_0 %uint_0 + %56 = OpAccessChain %_ptr_StorageBuffer_int %buf2 %uint_0 %uint_0 + %57 = OpSelect %_ptr_StorageBuffer_int %54 %55 %56 + OpReturnValue %57 + OpFunctionEnd +%select_buffer_null = OpFunction %_ptr_StorageBuffer_int None %44 + %58 = OpLabel + %59 = OpAccessChain %_ptr_Uniform_int %cb %uint_0 + %60 = OpLoad %int %59 + %61 = OpINotEqual %bool %60 %int_0 + %62 = OpAccessChain %_ptr_StorageBuffer_int %buf %uint_0 %uint_0 + %63 = OpSelect %_ptr_StorageBuffer_int %61 %62 %47 + OpReturnValue %63 + OpFunctionEnd +%select_tgsm = OpFunction %_ptr_Workgroup_int None %48 + %64 = OpLabel + %65 = OpAccessChain %_ptr_Uniform_int %cb %uint_0 + %66 = OpLoad %int %65 + %67 = OpINotEqual %bool %66 %int_0 + %68 = OpAccessChain %_ptr_Workgroup_int %tgsm %uint_0 + %69 = OpSelect %_ptr_Workgroup_int %67 %68 %49 + OpReturnValue %69 + OpFunctionEnd + %main = OpFunction %void None %22 + %70 = OpLabel + %cur = OpVariable %_ptr_Function__ptr_Workgroup_int Function + %71 = OpFunctionCall %_ptr_StorageBuffer_int %select_buffer + OpStore %sbuf %71 + %72 = OpFunctionCall %_ptr_StorageBuffer_int %select_buffer_null + OpStore %sbuf2 %72 + %73 = OpFunctionCall %_ptr_Workgroup_int %select_tgsm + OpStore %stgsm %73 + %74 = OpAccessChain %_ptr_StorageBuffer_int %buf %uint_0 %uint_0 + %75 = OpLoad %_ptr_Workgroup_int %stgsm + %76 = OpCopyObject %_ptr_Workgroup_int %75 + OpStore %cur %76 + OpBranch %77 + %77 = OpLabel + %78 = OpPhi %_ptr_StorageBuffer_int %74 %70 %79 %80 + %81 = OpLoad %_ptr_Workgroup_int %cur + %82 = OpLoad %int %78 + %83 = OpINotEqual %bool %82 %int_0 + OpLoopMerge %85 %80 None + OpBranchConditional %83 %84 %85 + %84 = OpLabel + %86 = OpLoad %int %81 + %87 = OpIAdd %int %82 %86 + OpStore %78 %87 + OpStore %81 %87 + OpBranch %80 + %80 = OpLabel + %79 = OpPtrAccessChain %_ptr_StorageBuffer_int %78 %uint_1 + %88 = OpPtrAccessChain %_ptr_Workgroup_int %81 %uint_1 + OpStore %cur %88 + OpBranch %77 + %85 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/component-insert-packed-expression.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/component-insert-packed-expression.asm.frag new file mode 100644 index 0000000..fb7cdb0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/component-insert-packed-expression.asm.frag @@ -0,0 +1,70 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %frag "main" %gl_FragCoord %out_var_SV_Target + OpExecutionMode %frag OriginUpperLeft + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "_BorderWidths" + OpName %_Globals "$Globals" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %frag "frag" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpDecorate %_arr_float_uint_4 ArrayStride 16 + OpMemberDecorate %type__Globals 0 Offset 0 + OpDecorate %type__Globals Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %int_2 = OpConstant %int 2 + %uint = OpTypeInt 32 0 + %float_1 = OpConstant %float 1 + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%type__Globals = OpTypeStruct %_arr_float_uint_4 +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void + %v2float = OpTypeVector %float 2 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %bool = OpTypeBool + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %frag = OpFunction %void None %21 + %25 = OpLabel + %26 = OpLoad %v4float %gl_FragCoord + %27 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_0 + %28 = OpLoad %float %27 + %29 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_1 + %30 = OpLoad %float %29 + %31 = OpCompositeConstruct %v2float %28 %30 + %32 = OpCompositeExtract %float %26 0 + %33 = OpFOrdGreaterThan %bool %32 %float_0 + OpSelectionMerge %34 None + OpBranchConditional %33 %35 %34 + %35 = OpLabel + %36 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_2 + %37 = OpLoad %float %36 + %38 = OpCompositeInsert %v2float %37 %31 0 + OpBranch %34 + %34 = OpLabel + %39 = OpPhi %v2float %31 %25 %38 %35 + %40 = OpCompositeExtract %float %39 0 + %41 = OpCompositeExtract %float %39 1 + %42 = OpCompositeConstruct %v4float %40 %41 %float_0 %float_1 + OpStore %out_var_SV_Target %42 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/empty-struct-in-struct.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/empty-struct-in-struct.asm.frag new file mode 100644 index 0000000..a9650dd --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/empty-struct-in-struct.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.2 +; Generator: Khronos; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %EntryPoint_Main "main" + OpExecutionMode %EntryPoint_Main OriginUpperLeft + OpSource Unknown 100 + OpName %EmptyStructTest "EmptyStructTest" + OpName %EmptyStruct2Test "EmptyStruct2Test" + OpName %GetValue "GetValue" + OpName %GetValue2 "GetValue" + OpName %self "self" + OpName %self2 "self" + OpName %emptyStruct "emptyStruct" + OpName %value "value" + OpName %EntryPoint_Main "EntryPoint_Main" + +%EmptyStructTest = OpTypeStruct +%EmptyStruct2Test = OpTypeStruct %EmptyStructTest +%_ptr_Function_EmptyStruct2Test = OpTypePointer Function %EmptyStruct2Test + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %5 = OpTypeFunction %float %_ptr_Function_EmptyStruct2Test + %6 = OpTypeFunction %float %EmptyStruct2Test + %void = OpTypeVoid +%_ptr_Function_void = OpTypePointer Function %void + %8 = OpTypeFunction %void %_ptr_Function_EmptyStruct2Test + %9 = OpTypeFunction %void + %float_0 = OpConstant %float 0 + %value4 = OpConstantNull %EmptyStruct2Test + + %GetValue = OpFunction %float None %5 + %self = OpFunctionParameter %_ptr_Function_EmptyStruct2Test + %13 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + + %GetValue2 = OpFunction %float None %6 + %self2 = OpFunctionParameter %EmptyStruct2Test + %14 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + +%EntryPoint_Main = OpFunction %void None %9 + %37 = OpLabel + %emptyStruct = OpVariable %_ptr_Function_EmptyStruct2Test Function + %18 = OpVariable %_ptr_Function_EmptyStruct2Test Function + %value = OpVariable %_ptr_Function_float Function + %value2 = OpCompositeConstruct %EmptyStructTest + %value3 = OpCompositeConstruct %EmptyStruct2Test %value2 + %22 = OpFunctionCall %float %GetValue %emptyStruct + %23 = OpFunctionCall %float %GetValue2 %value3 + %24 = OpFunctionCall %float %GetValue2 %value4 + OpStore %value %22 + OpStore %value %23 + OpStore %value %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag new file mode 100644 index 0000000..ca8022d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-fetch-uint-coord.asm.frag @@ -0,0 +1,44 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 29 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_Target0 + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %Tex "Tex" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %main "main" + OpDecorate %in_var_TEXCOORD0 Flat + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %Tex DescriptorSet 0 + OpDecorate %Tex Binding 0 + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %16 = OpTypeFunction %void + %Tex = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v3uint Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %16 + %19 = OpLabel + %20 = OpLoad %v3uint %in_var_TEXCOORD0 + %21 = OpCompositeExtract %uint %20 2 + %27 = OpLoad %type_2d_image %Tex + %28 = OpImageFetch %v4float %27 %20 Lod %21 + OpStore %out_var_SV_Target0 %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-gather.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-gather.asm.frag new file mode 100644 index 0000000..f26bb07 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-gather.asm.frag @@ -0,0 +1,74 @@ +; SPIR-V +; Version: 1.3 +; Generator: Google spiregg; 0 +; Bound: 36 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpExtension "SPV_GOOGLE_user_type" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psMain "main" %gl_FragCoord %in_var_TEXCOORD0 %out_var_SV_Target0 + OpExecutionMode %psMain OriginUpperLeft + OpSource HLSL 500 + OpName %type_2d_image "type.2d.image" + OpName %g_texture "g_texture" + OpName %type_sampler "type.sampler" + OpName %g_sampler "g_sampler" + OpName %g_comp "g_comp" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %psMain "psMain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_texture DescriptorSet 0 + OpDecorate %g_texture Binding 0 + OpDecorate %g_sampler DescriptorSet 0 + OpDecorate %g_sampler Binding 0 + OpDecorate %g_comp DescriptorSet 0 + OpDecorate %g_comp Binding 1 + OpDecorateString %g_texture UserTypeGOOGLE "texture2d" + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %v2int = OpTypeVector %int 2 + %16 = OpConstantComposite %v2int %int_0 %int_0 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %25 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image +%g_texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %g_comp = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %psMain = OpFunction %void None %25 + %26 = OpLabel + %27 = OpLoad %v2float %in_var_TEXCOORD0 + %28 = OpLoad %type_2d_image %g_texture + %29 = OpLoad %type_sampler %g_comp + %30 = OpSampledImage %type_sampled_image %28 %29 + %32 = OpLoad %type_sampler %g_sampler + %33 = OpSampledImage %type_sampled_image %28 %32 + %31 = OpImageGather %v4float %33 %27 %int_1 ConstOffset %16 + %34 = OpImageGather %v4float %33 %27 %int_0 ConstOffset %16 + %35 = OpFMul %v4float %34 %31 + OpStore %out_var_SV_Target0 %35 + OpReturn + OpFunctionEnd + diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-type-normal-comparison-usage.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-type-normal-comparison-usage.asm.frag new file mode 100644 index 0000000..d4c2643 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/image-type-normal-comparison-usage.asm.frag @@ -0,0 +1,76 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_Target0 + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %ShadowMap "ShadowMap" + OpName %type_sampler "type.sampler" + OpName %SampleNormal "SampleNormal" + OpName %SampleShadow "SampleShadow" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %main "main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %ShadowMap DescriptorSet 0 + OpDecorate %ShadowMap Binding 0 + OpDecorate %SampleNormal DescriptorSet 0 + OpDecorate %SampleNormal Binding 0 + OpDecorate %SampleShadow DescriptorSet 0 + OpDecorate %SampleShadow Binding 1 + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %v4float = OpTypeVector %float 4 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void + %bool = OpTypeBool +%type_sampled_image = OpTypeSampledImage %type_2d_image + %ShadowMap = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%SampleNormal = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%SampleShadow = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %21 + %23 = OpLabel + %24 = OpLoad %v2float %in_var_TEXCOORD0 + %25 = OpCompositeExtract %float %24 0 + %26 = OpFOrdGreaterThan %bool %25 %float_0_5 + OpSelectionMerge %27 None + OpBranchConditional %26 %28 %29 + %28 = OpLabel + %30 = OpLoad %type_2d_image %ShadowMap + %31 = OpLoad %type_sampler %SampleNormal + %32 = OpSampledImage %type_sampled_image %30 %31 + %33 = OpImageSampleImplicitLod %v4float %32 %24 None + %34 = OpCompositeExtract %float %33 0 + %35 = OpFOrdLessThanEqual %bool %34 %float_0_5 + %36 = OpSelect %float %35 %float_1 %float_0 + OpBranch %27 + %29 = OpLabel + %37 = OpLoad %type_2d_image %ShadowMap + %38 = OpLoad %type_sampler %SampleShadow + %39 = OpSampledImage %type_sampled_image %37 %38 + %40 = OpImageSampleDrefExplicitLod %float %39 %24 %float_0_5 Lod %float_0 + OpBranch %27 + %27 = OpLabel + %41 = OpPhi %float %36 %28 %40 %29 + %42 = OpCompositeConstruct %v4float %41 %41 %41 %float_1 + OpStore %out_var_SV_Target0 %42 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/input-attachment-unused-frag-coord.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/input-attachment-unused-frag-coord.asm.frag new file mode 100644 index 0000000..518dbd8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/input-attachment-unused-frag-coord.asm.frag @@ -0,0 +1,74 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 35 +; Schema: 0 + OpCapability Shader + OpCapability InputAttachment + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %load_subpasses_IP1_ "load_subpasses(IP1;" + OpName %uInput "uInput" + OpName %FragColor "FragColor" + OpName %uSubpass0 "uSubpass0" + OpName %uSubpass1 "uSubpass1" + OpName %gl_FragCoord "gl_FragCoord" + OpDecorate %load_subpasses_IP1_ RelaxedPrecision + OpDecorate %uInput RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %uSubpass0 RelaxedPrecision + OpDecorate %uSubpass0 DescriptorSet 0 + OpDecorate %uSubpass0 Binding 0 + OpDecorate %uSubpass0 InputAttachmentIndex 0 + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %uSubpass1 RelaxedPrecision + OpDecorate %uSubpass1 DescriptorSet 0 + OpDecorate %uSubpass1 Binding 1 + OpDecorate %uSubpass1 InputAttachmentIndex 1 + OpDecorate %28 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float SubpassData 0 0 0 2 Unknown +%_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_UniformConstant_7 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v2int = OpTypeVector %int 2 + %18 = OpConstantComposite %v2int %int_0 %int_0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %uSubpass0 = OpVariable %_ptr_UniformConstant_7 UniformConstant + %uSubpass1 = OpVariable %_ptr_UniformConstant_7 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %25 = OpLoad %7 %uSubpass0 + %26 = OpImageRead %v4float %25 %18 + %28 = OpFunctionCall %v4float %load_subpasses_IP1_ %uSubpass1 + %29 = OpFAdd %v4float %26 %28 + ;%32 = OpLoad %v4float %gl_FragCoord + ;%33 = OpVectorShuffle %v4float %32 %32 0 1 0 1 + ;%34 = OpFAdd %v4float %29 %33 + OpStore %FragColor %29 + OpReturn + OpFunctionEnd +%load_subpasses_IP1_ = OpFunction %v4float None %10 + %uInput = OpFunctionParameter %_ptr_UniformConstant_7 + %13 = OpLabel + %14 = OpLoad %7 %uInput + %19 = OpImageRead %v4float %14 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag new file mode 100644 index 0000000..707fa55 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/modf-frexp-scalar-access-chain-output.asm.frag @@ -0,0 +1,36 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 17 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %col "col" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0_150000006 = OpConstant %float 0.150000006 + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %v2int = OpTypeVector %int 2 +%_ptr_Function_v2int = OpTypePointer Function %v2int +%_ptr_Function_int = OpTypePointer Function %int + %main = OpFunction %void None %3 + %5 = OpLabel + %col = OpVariable %_ptr_Function_v3float Function + %icol = OpVariable %_ptr_Function_v2int Function + %ptr_x = OpAccessChain %_ptr_Function_float %col %int_0 + %ptr_y = OpAccessChain %_ptr_Function_int %icol %int_1 + %16 = OpExtInst %float %1 Modf %float_0_150000006 %ptr_x + %17 = OpExtInst %float %1 Frexp %float_0_150000006 %ptr_y + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/phi.zero-initialize.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/phi.zero-initialize.asm.frag new file mode 100644 index 0000000..3696660 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/phi.zero-initialize.asm.frag @@ -0,0 +1,69 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 40 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vColor %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %vColor "vColor" + OpName %uninit_function_int "uninit_function_int" + OpName %FragColor "FragColor" + OpName %uninit_int "uninit_int" + OpName %uninit_vector "uninit_vector" + OpName %uninit_matrix "uninit_matrix" + OpName %Foo "Foo" + OpMemberName %Foo 0 "a" + OpName %uninit_foo "uninit_foo" + OpDecorate %vColor Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vColor = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_10 = OpConstant %int 10 + %int_20 = OpConstant %int 20 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Private_int = OpTypePointer Private %int + %uninit_int = OpUndef %int + %v4int = OpTypeVector %int 4 +%_ptr_Private_v4int = OpTypePointer Private %v4int +%uninit_vector = OpUndef %v4int +%mat4v4float = OpTypeMatrix %v4float 4 +%_ptr_Private_mat4v4float = OpTypePointer Private %mat4v4float +%uninit_matrix = OpUndef %mat4v4float + %Foo = OpTypeStruct %int +%_ptr_Private_Foo = OpTypePointer Private %Foo + %uninit_foo = OpUndef %Foo + %main = OpFunction %void None %3 + %5 = OpLabel +%uninit_function_int = OpVariable %_ptr_Function_int Function + %13 = OpAccessChain %_ptr_Input_float %vColor %uint_0 + %14 = OpLoad %float %13 + %17 = OpFOrdGreaterThan %bool %14 %float_10 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %24 + %18 = OpLabel + OpBranch %19 + %24 = OpLabel + OpBranch %19 + %19 = OpLabel + %27 = OpPhi %int %int_10 %18 %int_20 %24 + %28 = OpLoad %v4float %vColor + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-callstack.msl2.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-callstack.msl2.asm.frag new file mode 100644 index 0000000..ebd8d6b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-callstack.msl2.asm.frag @@ -0,0 +1,89 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + OpReturn + OpFunctionEnd + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + OpBeginInvocationInterlockEXT + %43 = OpFunctionCall %void %callee2_ + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-control-flow.msl2.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-control-flow.msl2.asm.frag new file mode 100644 index 0000000..69b8f91 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-control-flow.msl2.asm.frag @@ -0,0 +1,121 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + + OpMemberDecorate %SSBO2 0 Offset 0 + OpDecorate %SSBO2 BufferBlock + OpDecorate %ssbo2 DescriptorSet 0 + OpDecorate %ssbo2 Binding 2 + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint + %SSBO2 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 +%_ptr_Uniform_SSBO2 = OpTypePointer Uniform %SSBO2 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %ssbo2 = OpVariable %_ptr_Uniform_SSBO2 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_4 = OpConstant %uint 4 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %bool = OpTypeBool + %true = OpConstantTrue %bool +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + %callee3_res = OpFunctionCall %void %callee3_ + OpReturn + OpFunctionEnd + + %callee3_ = OpFunction %void None %3 + %calle3_block = OpLabel + %frag_coord_x_ptr = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %frag_coord_x = OpLoad %float %frag_coord_x_ptr + %frag_coord_int = OpConvertFToS %int %frag_coord_x + %ssbo_ptr = OpAccessChain %_ptr_Uniform_uint %ssbo2 %int_0 %frag_coord_int + OpStore %ssbo_ptr %uint_4 + OpReturn + OpFunctionEnd + + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + %43 = OpFunctionCall %void %callee2_ + + OpSelectionMerge %merged_block None + OpBranchConditional %true %dummy_block %merged_block + %dummy_block = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpBranch %merged_block + + %merged_block = OpLabel + OpReturn + + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-split-functions.msl2.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-split-functions.msl2.asm.frag new file mode 100644 index 0000000..7c0fe9a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/pixel-interlock-split-functions.msl2.asm.frag @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + %call3res = OpFunctionCall %void %callee3_ + %call4res = OpFunctionCall %void %callee4_ + OpReturn + OpFunctionEnd + + %callee3_ = OpFunction %void None %3 + %begin3 = OpLabel + OpBeginInvocationInterlockEXT + OpReturn + OpFunctionEnd + + %callee4_ = OpFunction %void None %3 + %begin4 = OpLabel + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + %43 = OpFunctionCall %void %callee2_ + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/private-initializer-direct-store.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/private-initializer-direct-store.asm.frag new file mode 100644 index 0000000..49ed960 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/private-initializer-direct-store.asm.frag @@ -0,0 +1,32 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 17 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %b "b" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float + %float_10 = OpConstant %float 10 + %float_20 = OpConstant %float 20 + %b = OpVariable %_ptr_Private_float Private %float_10 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %b %float_20 + %15 = OpLoad %float %b + %16 = OpFAdd %float %15 %15 + OpStore %FragColor %16 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/subgroup-arithmetic-cast.msl21.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/subgroup-arithmetic-cast.msl21.asm.frag new file mode 100644 index 0000000..5f07340 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/subgroup-arithmetic-cast.msl21.asm.frag @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 78 +; Schema: 0 + OpCapability Shader + OpCapability GroupNonUniform + OpCapability GroupNonUniformArithmetic + OpCapability GroupNonUniformClustered + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %index %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_KHR_shader_subgroup_arithmetic" + OpSourceExtension "GL_KHR_shader_subgroup_basic" + OpSourceExtension "GL_KHR_shader_subgroup_clustered" + OpName %main "main" + OpName %index "index" + OpName %FragColor "FragColor" + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 +%_ptr_Output_uint = OpTypePointer Output %uint + %FragColor = OpVariable %_ptr_Output_uint Output + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpLoad %int %index + %u = OpBitcast %uint %i + %res0 = OpGroupNonUniformSMin %uint %uint_3 Reduce %i + %res1 = OpGroupNonUniformSMax %uint %uint_3 Reduce %u + %res2 = OpGroupNonUniformUMin %uint %uint_3 Reduce %i + %res3 = OpGroupNonUniformUMax %uint %uint_3 Reduce %u + ;%res4 = OpGroupNonUniformSMax %uint %uint_3 InclusiveScan %i + ;%res5 = OpGroupNonUniformSMin %uint %uint_3 InclusiveScan %u + ;%res6 = OpGroupNonUniformUMax %uint %uint_3 ExclusiveScan %i + ;%res7 = OpGroupNonUniformUMin %uint %uint_3 ExclusiveScan %u + %res8 = OpGroupNonUniformSMin %uint %uint_3 ClusteredReduce %i %uint_4 + %res9 = OpGroupNonUniformSMax %uint %uint_3 ClusteredReduce %u %uint_4 + %res10 = OpGroupNonUniformUMin %uint %uint_3 ClusteredReduce %i %uint_4 + %res11 = OpGroupNonUniformUMax %uint %uint_3 ClusteredReduce %u %uint_4 + OpStore %FragColor %res0 + OpStore %FragColor %res1 + OpStore %FragColor %res2 + OpStore %FragColor %res3 + ;OpStore %FragColor %res4 + ;OpStore %FragColor %res5 + ;OpStore %FragColor %res6 + ;OpStore %FragColor %res7 + OpStore %FragColor %res8 + OpStore %FragColor %res9 + OpStore %FragColor %res10 + OpStore %FragColor %res11 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag new file mode 100644 index 0000000..6556c3c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag @@ -0,0 +1,80 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vIndex %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %vIndex "vIndex" + OpName %FragColor "FragColor" + OpName %i "i" + OpName %j "j" + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_int = OpTypePointer Function %int + %main = OpFunction %void None %3 + %header = OpLabel + %i = OpVariable %_ptr_Function_int Function %int_0 + %j = OpVariable %_ptr_Function_int Function + %9 = OpLoad %int %vIndex + OpSelectionMerge %switch_merge None + OpSwitch %9 %default_case 100 %default_case 0 %case_0 1 %case_1 11 %case_1 2 %case_2 3 %case_3 4 %case_4 5 %case_5 + + %case_0 = OpLabel + OpBranch %default_case + + %default_case = OpLabel + %default_case_phi = OpPhi %int %int_2 %header %int_3 %case_0 + ; Test what happens when a case block dominates access to a variable. + OpStore %j %default_case_phi + OpBranch %case_1 + + %case_1 = OpLabel + ; Test phi nodes between case labels. + %case_1_phi = OpPhi %int %int_0 %default_case %int_1 %header + OpStore %j %case_1_phi + OpBranch %case_2 + + %case_2 = OpLabel + OpBranch %switch_merge + + %case_3 = OpLabel + ; Conditionally branch to another case block. This is really dumb, but it is apparently legal. + %case_3_cond = OpSGreaterThan %bool %9 %int_3 + OpBranchConditional %case_3_cond %case_4 %switch_merge + + %case_4 = OpLabel + ; When emitted from case 3, we should *not* see fallthrough behavior. + OpBranch %case_5 + + %case_5 = OpLabel + OpStore %i %int_0 + OpBranch %switch_merge + + %switch_merge = OpLabel + %26 = OpLoad %int %i + %27 = OpConvertSToF %float %26 + %28 = OpCompositeConstruct %v4float %27 %27 %27 %27 + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/texture-access.swizzle.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/texture-access.swizzle.asm.frag new file mode 100644 index 0000000..4c1408b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/texture-access.swizzle.asm.frag @@ -0,0 +1,364 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 247 +; Schema: 0 + OpCapability Shader + OpCapability Sampled1D + OpCapability SampledCubeArray + OpCapability SampledBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %c "c" + OpName %tex1d "tex1d" + OpName %tex2d "tex2d" + OpName %tex3d "tex3d" + OpName %texCube "texCube" + OpName %tex2dArray "tex2dArray" + OpName %texCubeArray "texCubeArray" + OpName %depth2d "depth2d" + OpName %depthCube "depthCube" + OpName %depth2dArray "depth2dArray" + OpName %depthCubeArray "depthCubeArray" + OpName %texBuffer "texBuffer" + OpName %tex1dSamp "tex1dSamp" + OpName %tex2dSamp "tex2dSamp" + OpName %tex3dSamp "tex3dSamp" + OpName %texCubeSamp "texCubeSamp" + OpName %tex2dArraySamp "tex2dArraySamp" + OpName %texCubeArraySamp "texCubeArraySamp" + OpName %depth2dSamp "depth2dSamp" + OpName %depthCubeSamp "depthCubeSamp" + OpName %depth2dArraySamp "depth2dArraySamp" + OpName %depthCubeArraySamp "depthCubeArraySamp" + OpDecorate %tex1d DescriptorSet 0 + OpDecorate %tex1d Binding 0 + OpDecorate %tex2d DescriptorSet 0 + OpDecorate %tex2d Binding 1 + OpDecorate %tex3d DescriptorSet 0 + OpDecorate %tex3d Binding 2 + OpDecorate %texCube DescriptorSet 0 + OpDecorate %texCube Binding 3 + OpDecorate %tex2dArray DescriptorSet 0 + OpDecorate %tex2dArray Binding 4 + OpDecorate %texCubeArray DescriptorSet 0 + OpDecorate %texCubeArray Binding 5 + OpDecorate %depth2d DescriptorSet 0 + OpDecorate %depth2d Binding 7 + OpDecorate %depthCube DescriptorSet 0 + OpDecorate %depthCube Binding 8 + OpDecorate %depth2dArray DescriptorSet 0 + OpDecorate %depth2dArray Binding 9 + OpDecorate %depthCubeArray DescriptorSet 0 + OpDecorate %depthCubeArray Binding 10 + OpDecorate %texBuffer DescriptorSet 0 + OpDecorate %texBuffer Binding 6 + OpDecorate %tex1dSamp DescriptorSet 1 + OpDecorate %tex1dSamp Binding 0 + OpDecorate %tex2dSamp DescriptorSet 1 + OpDecorate %tex2dSamp Binding 1 + OpDecorate %tex3dSamp DescriptorSet 1 + OpDecorate %tex3dSamp Binding 2 + OpDecorate %texCubeSamp DescriptorSet 1 + OpDecorate %texCubeSamp Binding 3 + OpDecorate %tex2dArraySamp DescriptorSet 1 + OpDecorate %tex2dArraySamp Binding 4 + OpDecorate %texCubeArraySamp DescriptorSet 1 + OpDecorate %texCubeArraySamp Binding 5 + OpDecorate %depth2dSamp DescriptorSet 1 + OpDecorate %depth2dSamp Binding 7 + OpDecorate %depthCubeSamp DescriptorSet 1 + OpDecorate %depthCubeSamp Binding 8 + OpDecorate %depth2dArraySamp DescriptorSet 1 + OpDecorate %depth2dArraySamp Binding 9 + OpDecorate %depthCubeArraySamp DescriptorSet 1 + OpDecorate %depthCubeArraySamp Binding 10 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %10 = OpTypeImage %float 1D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 + %12 = OpTypeSampler +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %tex1d = OpVariable %_ptr_UniformConstant_10 UniformConstant +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %tex1dSamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %float_0 = OpConstant %float 0 + %17 = OpTypeImage %float 2D 0 0 0 1 Unknown + %18 = OpTypeSampledImage %17 +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 + %tex2d = OpVariable %_ptr_UniformConstant_17 UniformConstant + %tex2dSamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %v2float = OpTypeVector %float 2 + %23 = OpConstantComposite %v2float %float_0 %float_0 + %25 = OpTypeImage %float 3D 0 0 0 1 Unknown + %26 = OpTypeSampledImage %25 +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 + %tex3d = OpVariable %_ptr_UniformConstant_25 UniformConstant + %tex3dSamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %v3float = OpTypeVector %float 3 + %31 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %33 = OpTypeImage %float Cube 0 0 0 1 Unknown + %34 = OpTypeSampledImage %33 +%_ptr_UniformConstant_33 = OpTypePointer UniformConstant %33 + %texCube = OpVariable %_ptr_UniformConstant_33 UniformConstant +%texCubeSamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %39 = OpTypeImage %float 2D 0 1 0 1 Unknown + %40 = OpTypeSampledImage %39 +%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39 + %tex2dArray = OpVariable %_ptr_UniformConstant_39 UniformConstant +%tex2dArraySamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %45 = OpTypeImage %float Cube 0 1 0 1 Unknown + %46 = OpTypeSampledImage %45 +%_ptr_UniformConstant_45 = OpTypePointer UniformConstant %45 +%texCubeArray = OpVariable %_ptr_UniformConstant_45 UniformConstant +%texCubeArraySamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %50 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %52 = OpTypeImage %float 2D 1 0 0 1 Unknown + %53 = OpTypeSampledImage %52 +%_ptr_UniformConstant_52 = OpTypePointer UniformConstant %52 + %depth2d = OpVariable %_ptr_UniformConstant_52 UniformConstant +%depth2dSamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %float_1 = OpConstant %float 1 + %58 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %65 = OpTypeImage %float Cube 1 0 0 1 Unknown + %66 = OpTypeSampledImage %65 +%_ptr_UniformConstant_65 = OpTypePointer UniformConstant %65 + %depthCube = OpVariable %_ptr_UniformConstant_65 UniformConstant +%depthCubeSamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %70 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 + %74 = OpTypeImage %float 2D 1 1 0 1 Unknown + %75 = OpTypeSampledImage %74 +%_ptr_UniformConstant_74 = OpTypePointer UniformConstant %74 +%depth2dArray = OpVariable %_ptr_UniformConstant_74 UniformConstant +%depth2dArraySamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %82 = OpTypeImage %float Cube 1 1 0 1 Unknown + %83 = OpTypeSampledImage %82 +%_ptr_UniformConstant_82 = OpTypePointer UniformConstant %82 +%depthCubeArray = OpVariable %_ptr_UniformConstant_82 UniformConstant +%depthCubeArraySamp = OpVariable %_ptr_UniformConstant_12 UniformConstant + %97 = OpConstantComposite %v2float %float_0 %float_1 + %98 = OpConstantComposite %v4float %float_0 %float_0 %float_1 %float_1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v2int = OpTypeVector %int 2 + %138 = OpConstantComposite %v2int %int_0 %int_0 + %v3int = OpTypeVector %int 3 + %143 = OpConstantComposite %v3int %int_0 %int_0 %int_0 + %149 = OpTypeImage %float Buffer 0 0 0 1 Unknown +%_ptr_UniformConstant_149 = OpTypePointer UniformConstant %149 + %texBuffer = OpVariable %_ptr_UniformConstant_149 UniformConstant + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %c = OpVariable %_ptr_Function_v4float Function + %13 = OpLoad %10 %tex1d + %14 = OpLoad %12 %tex1dSamp + %15 = OpSampledImage %11 %13 %14 + %16 = OpImageSampleImplicitLod %v4float %15 %float_0 + OpStore %c %16 + %19 = OpLoad %17 %tex2d + %20 = OpLoad %12 %tex2dSamp + %21 = OpSampledImage %18 %19 %20 + %24 = OpImageSampleImplicitLod %v4float %21 %23 + OpStore %c %24 + %27 = OpLoad %25 %tex3d + %28 = OpLoad %12 %tex3dSamp + %29 = OpSampledImage %26 %27 %28 + %32 = OpImageSampleImplicitLod %v4float %29 %31 + OpStore %c %32 + %35 = OpLoad %33 %texCube + %36 = OpLoad %12 %texCubeSamp + %37 = OpSampledImage %34 %35 %36 + %38 = OpImageSampleImplicitLod %v4float %37 %31 + OpStore %c %38 + %41 = OpLoad %39 %tex2dArray + %42 = OpLoad %12 %tex2dArraySamp + %43 = OpSampledImage %40 %41 %42 + %44 = OpImageSampleImplicitLod %v4float %43 %31 + OpStore %c %44 + %47 = OpLoad %45 %texCubeArray + %48 = OpLoad %12 %texCubeArraySamp + %49 = OpSampledImage %46 %47 %48 + %51 = OpImageSampleImplicitLod %v4float %49 %50 + OpStore %c %51 + %54 = OpLoad %52 %depth2d + %55 = OpLoad %12 %depth2dSamp + %56 = OpSampledImage %53 %54 %55 + %59 = OpCompositeExtract %float %58 2 + %60 = OpImageSampleDrefImplicitLod %float %56 %58 %59 + %64 = OpAccessChain %_ptr_Function_float %c %uint_0 + OpStore %64 %60 + %67 = OpLoad %65 %depthCube + %68 = OpLoad %12 %depthCubeSamp + %69 = OpSampledImage %66 %67 %68 + %71 = OpCompositeExtract %float %70 3 + %72 = OpImageSampleDrefImplicitLod %float %69 %70 %71 + %73 = OpAccessChain %_ptr_Function_float %c %uint_0 + OpStore %73 %72 + %76 = OpLoad %74 %depth2dArray + %77 = OpLoad %12 %depth2dArraySamp + %78 = OpSampledImage %75 %76 %77 + %79 = OpCompositeExtract %float %70 3 + %80 = OpImageSampleDrefImplicitLod %float %78 %70 %79 + %81 = OpAccessChain %_ptr_Function_float %c %uint_0 + OpStore %81 %80 + %84 = OpLoad %82 %depthCubeArray + %85 = OpLoad %12 %depthCubeArraySamp + %86 = OpSampledImage %83 %84 %85 + %87 = OpImageSampleDrefImplicitLod %float %86 %50 %float_1 + %88 = OpAccessChain %_ptr_Function_float %c %uint_0 + OpStore %88 %87 + %89 = OpLoad %10 %tex1d + %90 = OpLoad %12 %tex1dSamp + %91 = OpSampledImage %11 %89 %90 + %92 = OpImageSampleProjImplicitLod %v4float %91 %97 + OpStore %c %92 + %93 = OpLoad %17 %tex2d + %94 = OpLoad %12 %tex2dSamp + %95 = OpSampledImage %18 %93 %94 + %96 = OpImageSampleProjImplicitLod %v4float %95 %58 + OpStore %c %96 + %99 = OpLoad %25 %tex3d + %100 = OpLoad %12 %tex3dSamp + %101 = OpSampledImage %26 %99 %100 + %102 = OpImageSampleProjImplicitLod %v4float %101 %70 + OpStore %c %102 + %103 = OpLoad %52 %depth2d + %104 = OpLoad %12 %depth2dSamp + %105 = OpSampledImage %53 %103 %104 + %106 = OpCompositeExtract %float %98 2 + %107 = OpCompositeExtract %float %98 3 + %108 = OpCompositeInsert %v4float %107 %98 2 + %109 = OpImageSampleProjDrefImplicitLod %float %105 %108 %106 + %110 = OpAccessChain %_ptr_Function_float %c %uint_0 + OpStore %110 %109 + %111 = OpLoad %10 %tex1d + %112 = OpLoad %12 %tex1dSamp + %113 = OpSampledImage %11 %111 %112 + %114 = OpImageSampleExplicitLod %v4float %113 %float_0 Lod %float_0 + OpStore %c %114 + %115 = OpLoad %17 %tex2d + %116 = OpLoad %12 %tex2dSamp + %117 = OpSampledImage %18 %115 %116 + %118 = OpImageSampleExplicitLod %v4float %117 %23 Lod %float_0 + OpStore %c %118 + %119 = OpLoad %25 %tex3d + %120 = OpLoad %12 %tex3dSamp + %121 = OpSampledImage %26 %119 %120 + %122 = OpImageSampleExplicitLod %v4float %121 %31 Lod %float_0 + OpStore %c %122 + %123 = OpLoad %33 %texCube + %124 = OpLoad %12 %texCubeSamp + %125 = OpSampledImage %34 %123 %124 + %126 = OpImageSampleExplicitLod %v4float %125 %31 Lod %float_0 + OpStore %c %126 + %127 = OpLoad %39 %tex2dArray + %128 = OpLoad %12 %tex2dArraySamp + %129 = OpSampledImage %40 %127 %128 + %130 = OpImageSampleExplicitLod %v4float %129 %31 Lod %float_0 + OpStore %c %130 + %131 = OpLoad %45 %texCubeArray + %132 = OpLoad %12 %texCubeArraySamp + %133 = OpSampledImage %46 %131 %132 + %134 = OpImageSampleExplicitLod %v4float %133 %50 Lod %float_0 + OpStore %c %134 + %135 = OpLoad %52 %depth2d + %136 = OpLoad %12 %depth2dSamp + %137 = OpSampledImage %53 %135 %136 + %139 = OpCompositeExtract %float %58 2 + %140 = OpImageSampleDrefExplicitLod %float %137 %58 %139 Lod %float_0 + %141 = OpAccessChain %_ptr_Function_float %c %uint_0 + OpStore %141 %140 + %142 = OpLoad %10 %tex1d + %144 = OpLoad %12 %tex1dSamp + %145 = OpSampledImage %11 %142 %144 + %146 = OpImageSampleProjExplicitLod %v4float %145 %97 Lod %float_0 + OpStore %c %146 + %147 = OpLoad %17 %tex2d + %148 = OpLoad %12 %tex2dSamp + %150 = OpSampledImage %18 %147 %148 + %151 = OpImageSampleProjExplicitLod %v4float %150 %58 Lod %float_0 + OpStore %c %151 + %152 = OpLoad %25 %tex3d + %153 = OpLoad %12 %tex3dSamp + %154 = OpSampledImage %26 %152 %153 + %155 = OpImageSampleProjExplicitLod %v4float %154 %70 Lod %float_0 + OpStore %c %155 + %156 = OpLoad %52 %depth2d + %157 = OpLoad %12 %depth2dSamp + %158 = OpSampledImage %53 %156 %157 + %159 = OpCompositeExtract %float %98 2 + %160 = OpCompositeExtract %float %98 3 + %161 = OpCompositeInsert %v4float %160 %98 2 + %162 = OpImageSampleProjDrefExplicitLod %float %158 %161 %159 Lod %float_0 + %163 = OpAccessChain %_ptr_Function_float %c %uint_0 + OpStore %163 %162 + %164 = OpLoad %10 %tex1d + %165 = OpImageFetch %v4float %164 %int_0 Lod %int_0 + OpStore %c %165 + %166 = OpLoad %17 %tex2d + %167 = OpImageFetch %v4float %166 %138 Lod %int_0 + OpStore %c %167 + %168 = OpLoad %25 %tex3d + %169 = OpImageFetch %v4float %168 %143 Lod %int_0 + OpStore %c %169 + %170 = OpLoad %39 %tex2dArray + %171 = OpImageFetch %v4float %170 %143 Lod %int_0 + OpStore %c %171 + %172 = OpLoad %149 %texBuffer + %173 = OpImageFetch %v4float %172 %int_0 + OpStore %c %173 + %174 = OpLoad %17 %tex2d + %175 = OpLoad %12 %tex2dSamp + %176 = OpSampledImage %18 %174 %175 + %177 = OpImageGather %v4float %176 %23 %int_0 + OpStore %c %177 + %178 = OpLoad %33 %texCube + %179 = OpLoad %12 %texCubeSamp + %180 = OpSampledImage %34 %178 %179 + %181 = OpImageGather %v4float %180 %31 %int_1 + OpStore %c %181 + %182 = OpLoad %39 %tex2dArray + %183 = OpLoad %12 %tex2dArraySamp + %184 = OpSampledImage %40 %182 %183 + %185 = OpImageGather %v4float %184 %31 %int_2 + OpStore %c %185 + %186 = OpLoad %45 %texCubeArray + %187 = OpLoad %12 %texCubeArraySamp + %188 = OpSampledImage %46 %186 %187 + %189 = OpImageGather %v4float %188 %50 %int_3 + OpStore %c %189 + %190 = OpLoad %52 %depth2d + %191 = OpLoad %12 %depth2dSamp + %192 = OpSampledImage %53 %190 %191 + %193 = OpImageDrefGather %v4float %192 %23 %float_1 + OpStore %c %193 + %194 = OpLoad %65 %depthCube + %195 = OpLoad %12 %depthCubeSamp + %196 = OpSampledImage %66 %194 %195 + %197 = OpImageDrefGather %v4float %196 %31 %float_1 + OpStore %c %197 + %198 = OpLoad %74 %depth2dArray + %199 = OpLoad %12 %depth2dArraySamp + %200 = OpSampledImage %75 %198 %199 + %201 = OpImageDrefGather %v4float %200 %31 %float_1 + OpStore %c %201 + %202 = OpLoad %82 %depthCubeArray + %203 = OpLoad %12 %depthCubeArraySamp + %204 = OpSampledImage %83 %202 %203 + %205 = OpImageDrefGather %v4float %204 %50 %float_1 + OpStore %c %205 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag new file mode 100644 index 0000000..22c4efc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag @@ -0,0 +1,42 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vFloat + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vFloat "vFloat" + OpName %undef "undef" + OpDecorate %FragColor Location 0 + OpDecorate %vFloat Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vFloat = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 +%_ptr_Private_v4float = OpTypePointer Private %v4float + %undef = OpUndef %v4float + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_Private_float = OpTypePointer Private %float + %uint_3 = OpConstant %uint 3 +%_ptr_Input_float = OpTypePointer Input %float + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %v4float %vFloat + %26 = OpVectorShuffle %v4float %13 %undef 4 1 0xffffffff 3 + %27 = OpVectorShuffle %v4float %13 %13 2 1 0xffffffff 3 + %28 = OpFAdd %v4float %26 %27 + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/composite-extract-row-major.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/composite-extract-row-major.asm.comp new file mode 100644 index 0000000..a37bdd9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/composite-extract-row-major.asm.comp @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 21 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBORow "SSBORow" + OpMemberName %SSBORow 0 "v" + OpMemberName %SSBORow 1 "row_major0" + OpName %_ "" + OpMemberDecorate %SSBORow 0 Offset 0 + OpMemberDecorate %SSBORow 1 RowMajor + OpMemberDecorate %SSBORow 1 Offset 16 + OpMemberDecorate %SSBORow 1 MatrixStride 16 + OpDecorate %SSBORow BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %SSBORow = OpTypeStruct %float %mat4v4float +%_ptr_Uniform_SSBORow = OpTypePointer Uniform %SSBORow + %_ = OpVariable %_ptr_Uniform_SSBORow Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %row_ptr = OpAccessChain %_ptr_Uniform_v4float %_ %int_1 %int_1 + %vec = OpLoad %v4float %row_ptr + %float_val = OpCompositeExtract %float %vec 2 + + %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %20 %float_val + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-2.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-2.asm.comp new file mode 100644 index 0000000..4c22245 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-2.asm.comp @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 23 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_scalar_block_layout" + OpName %main "main" + OpName %SSBOScalar "SSBOScalar" + OpMemberName %SSBOScalar 0 "a" + OpMemberName %SSBOScalar 1 "b" + OpMemberName %SSBOScalar 2 "c" + OpName %_ "" + OpMemberDecorate %SSBOScalar 0 Offset 0 + OpMemberDecorate %SSBOScalar 1 RowMajor + OpMemberDecorate %SSBOScalar 1 Offset 16 + OpMemberDecorate %SSBOScalar 1 MatrixStride 16 + OpMemberDecorate %SSBOScalar 2 RowMajor + OpMemberDecorate %SSBOScalar 2 Offset 64 + OpMemberDecorate %SSBOScalar 2 MatrixStride 16 + OpDecorate %SSBOScalar BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%mat3v3float = OpTypeMatrix %v3float 3 + %SSBOScalar = OpTypeStruct %v3float %mat3v3float %mat3v3float +%_ptr_Uniform_SSBOScalar = OpTypePointer Uniform %SSBOScalar + %_ = OpVariable %_ptr_Uniform_SSBOScalar Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 +%_ptr_Uniform_mat3v3float = OpTypePointer Uniform %mat3v3float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %main = OpFunction %void None %3 + %5 = OpLabel + %b_ptr = OpAccessChain %_ptr_Uniform_mat3v3float %_ %int_1 + %c_ptr = OpAccessChain %_ptr_Uniform_mat3v3float %_ %int_2 + %b = OpLoad %mat3v3float %b_ptr + %c = OpLoad %mat3v3float %c_ptr + OpStore %b_ptr %c + %19 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 + %20 = OpLoad %v3float %19 + %21 = OpMatrixTimesVector %v3float %b %20 + %22 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 + OpStore %22 %21 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-3.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-3.asm.comp new file mode 100644 index 0000000..85a220f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-3.asm.comp @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 22 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_scalar_block_layout" + OpName %main "main" + OpName %SSBOScalar "SSBOScalar" + OpMemberName %SSBOScalar 0 "a" + OpMemberName %SSBOScalar 1 "b" + OpMemberName %SSBOScalar 2 "c" + OpName %_ "" + OpMemberDecorate %SSBOScalar 0 Offset 0 + OpMemberDecorate %SSBOScalar 1 Offset 12 + OpMemberDecorate %SSBOScalar 2 Offset 24 + OpDecorate %SSBOScalar BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %SSBOScalar = OpTypeStruct %v3float %v3float %v3float +%_ptr_Uniform_SSBOScalar = OpTypePointer Uniform %SSBOScalar + %_ = OpVariable %_ptr_Uniform_SSBOScalar Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %int_2 = OpConstant %int 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpAccessChain %_ptr_Uniform_v3float %_ %int_1 + %16 = OpLoad %v3float %15 + %18 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + %19 = OpLoad %v3float %18 + OpStore %18 %16 + %20 = OpFMul %v3float %16 %19 + %21 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 + OpStore %21 %20 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-4.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-4.asm.comp new file mode 100644 index 0000000..bef3fcb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-4.asm.comp @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_scalar_block_layout" + OpName %main "main" + OpName %SSBOScalar "SSBOScalar" + OpMemberName %SSBOScalar 0 "a" + OpMemberName %SSBOScalar 1 "b" + OpMemberName %SSBOScalar 2 "c" + OpName %_ "" + OpDecorate %_arr_v2float_uint_16 ArrayStride 16 + OpDecorate %_arr_v2float_uint_16_0 ArrayStride 16 + OpDecorate %_arr_float_uint_16 ArrayStride 16 + OpMemberDecorate %SSBOScalar 0 Offset 0 + OpMemberDecorate %SSBOScalar 1 Offset 256 + OpMemberDecorate %SSBOScalar 2 Offset 512 + OpDecorate %SSBOScalar BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_16 = OpConstant %uint 16 +%_arr_v2float_uint_16 = OpTypeArray %v2float %uint_16 +%_arr_v2float_uint_16_0 = OpTypeArray %v2float %uint_16 +%_arr_float_uint_16 = OpTypeArray %float %uint_16 + %SSBOScalar = OpTypeStruct %_arr_v2float_uint_16 %_arr_v2float_uint_16_0 %_arr_float_uint_16 +%_ptr_Uniform_SSBOScalar = OpTypePointer Uniform %SSBOScalar + %_ = OpVariable %_ptr_Uniform_SSBOScalar Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %int_2 = OpConstant %int 2 + %float_10 = OpConstant %float 10.0 + %float_11 = OpConstant %float 11.0 + %float_const = OpConstantComposite %v2float %float_10 %float_11 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %main = OpFunction %void None %3 + %5 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 %int_10 + %22 = OpLoad %v2float %21 + %25 = OpAccessChain %_ptr_Uniform_float %_ %int_2 %int_10 + %26 = OpLoad %float %25 + OpStore %21 %float_const + %27 = OpVectorTimesScalar %v2float %22 %26 + %28 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %int_10 + OpStore %28 %27 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-5.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-5.asm.comp new file mode 100644 index 0000000..8de22b8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding-5.asm.comp @@ -0,0 +1,54 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_scalar_block_layout" + OpName %main "main" + OpName %SSBOScalar "SSBOScalar" + OpMemberName %SSBOScalar 0 "a" + OpMemberName %SSBOScalar 1 "b" + OpMemberName %SSBOScalar 2 "c" + OpName %_ "" + OpMemberDecorate %SSBOScalar 0 Offset 0 + OpMemberDecorate %SSBOScalar 1 Offset 8 + OpMemberDecorate %SSBOScalar 2 Offset 20 + OpDecorate %SSBOScalar BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %SSBOScalar = OpTypeStruct %v2float %v3float %v3float +%_ptr_Uniform_SSBOScalar = OpTypePointer Uniform %SSBOScalar + %_ = OpVariable %_ptr_Uniform_SSBOScalar Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %int_2 = OpConstant %int 2 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %float_1 = OpConstant %float 1 + %27 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Uniform_v3float %_ %int_1 + %17 = OpLoad %v3float %16 + %18 = OpVectorShuffle %v2float %17 %17 0 1 + %20 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + %21 = OpLoad %v3float %20 + %22 = OpVectorShuffle %v2float %21 %21 1 2 + OpStore %16 %27 + %23 = OpFMul %v2float %18 %22 + %25 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 + OpStore %25 %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding.asm.comp new file mode 100644 index 0000000..0b0ba53 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/load-packed-no-forwarding.asm.comp @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 23 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_scalar_block_layout" + OpName %main "main" + OpName %SSBOScalar "SSBOScalar" + OpMemberName %SSBOScalar 0 "a" + OpMemberName %SSBOScalar 1 "b" + OpMemberName %SSBOScalar 2 "c" + OpName %_ "" + OpMemberDecorate %SSBOScalar 0 Offset 0 + OpMemberDecorate %SSBOScalar 1 RowMajor + OpMemberDecorate %SSBOScalar 1 Offset 12 + OpMemberDecorate %SSBOScalar 1 MatrixStride 12 + OpMemberDecorate %SSBOScalar 2 RowMajor + OpMemberDecorate %SSBOScalar 2 Offset 48 + OpMemberDecorate %SSBOScalar 2 MatrixStride 12 + OpDecorate %SSBOScalar BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%mat3v3float = OpTypeMatrix %v3float 3 + %SSBOScalar = OpTypeStruct %v3float %mat3v3float %mat3v3float +%_ptr_Uniform_SSBOScalar = OpTypePointer Uniform %SSBOScalar + %_ = OpVariable %_ptr_Uniform_SSBOScalar Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 +%_ptr_Uniform_mat3v3float = OpTypePointer Uniform %mat3v3float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %main = OpFunction %void None %3 + %5 = OpLabel + %b_ptr = OpAccessChain %_ptr_Uniform_mat3v3float %_ %int_1 + %c_ptr = OpAccessChain %_ptr_Uniform_mat3v3float %_ %int_2 + %b = OpLoad %mat3v3float %b_ptr + %c = OpLoad %mat3v3float %c_ptr + OpStore %b_ptr %c + %19 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 + %20 = OpLoad %v3float %19 + %21 = OpMatrixTimesVector %v3float %b %20 + %22 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 + OpStore %22 %21 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/packed-vector-extract-insert.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/packed-vector-extract-insert.asm.comp new file mode 100644 index 0000000..70b1752 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/packed-vector-extract-insert.asm.comp @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_scalar_block_layout" + OpName %main "main" + OpName %SSBOScalar "SSBOScalar" + OpMemberName %SSBOScalar 0 "a" + OpMemberName %SSBOScalar 1 "b" + OpMemberName %SSBOScalar 2 "c" + OpMemberName %SSBOScalar 3 "d" + OpName %_ "" + OpMemberDecorate %SSBOScalar 0 Offset 0 + OpMemberDecorate %SSBOScalar 1 Offset 8 + OpMemberDecorate %SSBOScalar 2 Offset 20 + OpMemberDecorate %SSBOScalar 3 Offset 32 + OpDecorate %SSBOScalar BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %SSBOScalar = OpTypeStruct %v2float %v3float %v3float %v3float +%_ptr_Uniform_SSBOScalar = OpTypePointer Uniform %SSBOScalar + %_ = OpVariable %_ptr_Uniform_SSBOScalar Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %int_2 = OpConstant %int 2 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float_2 = OpConstant %float 2.0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %main = OpFunction %void None %3 + %5 = OpLabel + %v3_ptr = OpAccessChain %_ptr_Uniform_v3float %_ %int_1 + %v3 = OpLoad %v3float %v3_ptr + %v3_mod = OpCompositeInsert %v3float %float_2 %v3 2 + %v2 = OpVectorShuffle %v2float %v3 %v3 0 1 + %v1 = OpCompositeExtract %float %v3 2 + %v2_mul = OpVectorTimesScalar %v2float %v2 %v1 + %v2_ptr = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 + OpStore %v2_ptr %v2_mul + OpStore %v3_ptr %v3_mod + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/row-major-split-access-chain.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/row-major-split-access-chain.asm.comp new file mode 100644 index 0000000..398c8d1 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/row-major-split-access-chain.asm.comp @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 21 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBORow "SSBORow" + OpMemberName %SSBORow 0 "v" + OpMemberName %SSBORow 1 "row_major0" + OpName %_ "" + OpMemberDecorate %SSBORow 0 Offset 0 + OpMemberDecorate %SSBORow 1 RowMajor + OpMemberDecorate %SSBORow 1 Offset 16 + OpMemberDecorate %SSBORow 1 MatrixStride 16 + OpDecorate %SSBORow BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %SSBORow = OpTypeStruct %float %mat4v4float +%_ptr_Uniform_SSBORow = OpTypePointer Uniform %SSBORow + %_ = OpVariable %_ptr_Uniform_SSBORow Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %row_ptr = OpAccessChain %_ptr_Uniform_v4float %_ %int_1 %int_1 + %float_ptr = OpAccessChain %_ptr_Uniform_float %row_ptr %uint_2 + + %19 = OpLoad %float %float_ptr + %20 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %20 %19 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float2.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float2.asm.frag new file mode 100644 index 0000000..85249d9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float2.asm.frag @@ -0,0 +1,54 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 29 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpDecorate %_arr_v2float_uint_2 ArrayStride 16 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 1 Offset 24 + OpDecorate %type_Foo Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_arr_v2float_uint_2 = OpTypeArray %v2float %uint_2 + %type_Foo = OpTypeStruct %_arr_v2float_uint_2 %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v2float = OpTypePointer Output %v2float + %void = OpTypeVoid + %16 = OpTypeFunction %void +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v2float Output + %main = OpFunction %void None %16 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %int_0 + %21 = OpLoad %v2float %20 + %22 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %int_1 + %23 = OpLoad %v2float %22 + %24 = OpFAdd %v2float %21 %23 + %25 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %26 = OpLoad %float %25 + %27 = OpCompositeConstruct %v2float %26 %26 + %28 = OpFAdd %v2float %24 %27 + OpStore %out_var_SV_Target %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float3-one-element.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float3-one-element.asm.frag new file mode 100644 index 0000000..7ed32be --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float3-one-element.asm.frag @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 26 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpDecorate %_arr_v3float_uint_1 ArrayStride 16 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 1 Offset 12 + OpDecorate %type_Foo Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_arr_v3float_uint_1 = OpTypeArray %v3float %uint_1 + %type_Foo = OpTypeStruct %_arr_v3float_uint_1 %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %16 = OpTypeFunction %void +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v3float Output + %main = OpFunction %void None %16 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %int_0 + %21 = OpLoad %v3float %20 + %22 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %23 = OpLoad %float %22 + %24 = OpCompositeConstruct %v3float %23 %23 %23 + %25 = OpFAdd %v3float %21 %24 + OpStore %out_var_SV_Target %25 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float3.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float3.asm.frag new file mode 100644 index 0000000..406328b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-array-float3.asm.frag @@ -0,0 +1,54 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 29 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpDecorate %_arr_v3float_uint_2 ArrayStride 16 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 1 Offset 28 + OpDecorate %type_Foo Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_arr_v3float_uint_2 = OpTypeArray %v3float %uint_2 + %type_Foo = OpTypeStruct %_arr_v3float_uint_2 %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %16 = OpTypeFunction %void +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v3float Output + %main = OpFunction %void None %16 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %int_0 + %21 = OpLoad %v3float %20 + %22 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %int_1 + %23 = OpLoad %v3float %22 + %24 = OpFAdd %v3float %21 %23 + %25 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %26 = OpLoad %float %25 + %27 = OpCompositeConstruct %v3float %26 %26 %26 + %28 = OpFAdd %v3float %24 %27 + OpStore %out_var_SV_Target %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x2-col-major.invalid.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x2-col-major.invalid.asm.frag new file mode 100644 index 0000000..b9b4f5a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x2-col-major.invalid.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 0 MatrixStride 16 + OpMemberDecorate %type_Foo 0 ColMajor + OpMemberDecorate %type_Foo 1 Offset 24 + OpDecorate %type_Foo Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%mat2v2float = OpTypeMatrix %v2float 2 + %type_Foo = OpTypeStruct %mat2v2float %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v2float = OpTypePointer Output %v2float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v2float Output + %main = OpFunction %void None %17 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %uint_0 + %22 = OpLoad %v2float %21 + %23 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %uint_1 + %24 = OpLoad %v2float %23 + %25 = OpFAdd %v2float %22 %24 + %26 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %27 = OpLoad %float %26 + %28 = OpCompositeConstruct %v2float %27 %27 + %29 = OpFAdd %v2float %25 %28 + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x2-row-major.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x2-row-major.asm.frag new file mode 100644 index 0000000..e1830e9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x2-row-major.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 0 MatrixStride 16 + OpMemberDecorate %type_Foo 0 RowMajor + OpMemberDecorate %type_Foo 1 Offset 24 + OpDecorate %type_Foo Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%mat2v2float = OpTypeMatrix %v2float 2 + %type_Foo = OpTypeStruct %mat2v2float %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v2float = OpTypePointer Output %v2float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v2float Output + %main = OpFunction %void None %17 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %uint_0 + %22 = OpLoad %v2float %21 + %23 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %uint_1 + %24 = OpLoad %v2float %23 + %25 = OpFAdd %v2float %22 %24 + %26 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %27 = OpLoad %float %26 + %28 = OpCompositeConstruct %v2float %27 %27 + %29 = OpFAdd %v2float %25 %28 + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x3-col-major.invalid.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x3-col-major.invalid.asm.frag new file mode 100644 index 0000000..647939f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x3-col-major.invalid.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 0 MatrixStride 16 + OpMemberDecorate %type_Foo 0 ColMajor + OpMemberDecorate %type_Foo 1 Offset 28 + OpDecorate %type_Foo Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%mat2v3float = OpTypeMatrix %v3float 2 + %type_Foo = OpTypeStruct %mat2v3float %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v3float Output + %main = OpFunction %void None %17 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %uint_0 + %22 = OpLoad %v3float %21 + %23 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %uint_1 + %24 = OpLoad %v3float %23 + %25 = OpFAdd %v3float %22 %24 + %26 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %27 = OpLoad %float %26 + %28 = OpCompositeConstruct %v3float %27 %27 %27 + %29 = OpFAdd %v3float %25 %28 + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x3-row-major.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x3-row-major.asm.frag new file mode 100644 index 0000000..733465a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float2x3-row-major.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 0 MatrixStride 16 + OpMemberDecorate %type_Foo 0 RowMajor + OpMemberDecorate %type_Foo 1 Offset 40 + OpDecorate %type_Foo Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%mat2v3float = OpTypeMatrix %v3float 2 + %type_Foo = OpTypeStruct %mat2v3float %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v3float Output + %main = OpFunction %void None %17 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %uint_0 + %22 = OpLoad %v3float %21 + %23 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %uint_1 + %24 = OpLoad %v3float %23 + %25 = OpFAdd %v3float %22 %24 + %26 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %27 = OpLoad %float %26 + %28 = OpCompositeConstruct %v3float %27 %27 %27 + %29 = OpFAdd %v3float %25 %28 + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x2-col-major.invalid.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x2-col-major.invalid.asm.frag new file mode 100644 index 0000000..c97fb81 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x2-col-major.invalid.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 0 MatrixStride 16 + OpMemberDecorate %type_Foo 0 ColMajor + OpMemberDecorate %type_Foo 1 Offset 40 + OpDecorate %type_Foo Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%mat3v2float = OpTypeMatrix %v2float 3 + %type_Foo = OpTypeStruct %mat3v2float %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v2float = OpTypePointer Output %v2float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v2float Output + %main = OpFunction %void None %17 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %uint_0 + %22 = OpLoad %v2float %21 + %23 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %uint_1 + %24 = OpLoad %v2float %23 + %25 = OpFAdd %v2float %22 %24 + %26 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %27 = OpLoad %float %26 + %28 = OpCompositeConstruct %v2float %27 %27 + %29 = OpFAdd %v2float %25 %28 + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x2-row-major.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x2-row-major.asm.frag new file mode 100644 index 0000000..b1cfa56 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x2-row-major.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 0 MatrixStride 16 + OpMemberDecorate %type_Foo 0 RowMajor + OpMemberDecorate %type_Foo 1 Offset 28 + OpDecorate %type_Foo Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%mat3v2float = OpTypeMatrix %v2float 3 + %type_Foo = OpTypeStruct %mat3v2float %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v2float = OpTypePointer Output %v2float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v2float Output + %main = OpFunction %void None %17 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %uint_0 + %22 = OpLoad %v2float %21 + %23 = OpAccessChain %_ptr_Uniform_v2float %Foo %int_0 %uint_1 + %24 = OpLoad %v2float %23 + %25 = OpFAdd %v2float %22 %24 + %26 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %27 = OpLoad %float %26 + %28 = OpCompositeConstruct %v2float %27 %27 + %29 = OpFAdd %v2float %25 %28 + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x3-col-major.invalid.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x3-col-major.invalid.asm.frag new file mode 100644 index 0000000..cef8308 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x3-col-major.invalid.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 0 MatrixStride 16 + OpMemberDecorate %type_Foo 0 ColMajor + OpMemberDecorate %type_Foo 1 Offset 44 + OpDecorate %type_Foo Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%mat3v3float = OpTypeMatrix %v3float 3 + %type_Foo = OpTypeStruct %mat3v3float %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v3float Output + %main = OpFunction %void None %17 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %uint_0 + %22 = OpLoad %v3float %21 + %23 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %uint_1 + %24 = OpLoad %v3float %23 + %25 = OpFAdd %v3float %22 %24 + %26 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %27 = OpLoad %float %26 + %28 = OpCompositeConstruct %v3float %27 %27 %27 + %29 = OpFAdd %v3float %25 %28 + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x3-row-major.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x3-row-major.asm.frag new file mode 100644 index 0000000..35d7ebc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/packing/scalar-float3x3-row-major.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_Foo "type.Foo" + OpMemberName %type_Foo 0 "a" + OpMemberName %type_Foo 1 "b" + OpName %Foo "Foo" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %Foo DescriptorSet 0 + OpDecorate %Foo Binding 0 + OpMemberDecorate %type_Foo 0 Offset 0 + OpMemberDecorate %type_Foo 0 MatrixStride 16 + OpMemberDecorate %type_Foo 0 RowMajor + OpMemberDecorate %type_Foo 1 Offset 44 + OpDecorate %type_Foo Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%mat3v3float = OpTypeMatrix %v3float 3 + %type_Foo = OpTypeStruct %mat3v3float %float +%_ptr_Uniform_type_Foo = OpTypePointer Uniform %type_Foo +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Foo = OpVariable %_ptr_Uniform_type_Foo Uniform +%out_var_SV_Target = OpVariable %_ptr_Output_v3float Output + %main = OpFunction %void None %17 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %uint_0 + %22 = OpLoad %v3float %21 + %23 = OpAccessChain %_ptr_Uniform_v3float %Foo %int_0 %uint_1 + %24 = OpLoad %v3float %23 + %25 = OpFAdd %v3float %22 %24 + %26 = OpAccessChain %_ptr_Uniform_float %Foo %int_1 + %27 = OpLoad %float %26 + %28 = OpCompositeConstruct %v3float %27 %27 %27 + %29 = OpFAdd %v3float %25 %28 + OpStore %out_var_SV_Target %29 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/temporary.zero-initialize.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/temporary.zero-initialize.asm.frag new file mode 100644 index 0000000..eccff08 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/temporary.zero-initialize.asm.frag @@ -0,0 +1,93 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 65 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA RelaxedPrecision + OpDecorate %vA Flat + OpDecorate %vA Location 0 + OpDecorate %25 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %vB RelaxedPrecision + OpDecorate %vB Flat + OpDecorate %vB Location 1 + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %64 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_int = OpTypePointer Input %int + %vA = OpVariable %_ptr_Input_int Input + %bool = OpTypeBool + %int_20 = OpConstant %int 20 + %int_50 = OpConstant %int 50 + %vB = OpVariable %_ptr_Input_int Input + %int_40 = OpConstant %int 40 + %int_60 = OpConstant %int 60 + %int_10 = OpConstant %int 10 + %float_1 = OpConstant %float 1 + %63 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %FragColor %11 + OpBranch %17 + %17 = OpLabel + %60 = OpPhi %int %int_0 %5 %58 %20 + %57 = OpPhi %int %int_0 %5 %56 %20 + %25 = OpLoad %int %vA + %27 = OpSLessThan %bool %57 %25 + OpLoopMerge %19 %20 None + OpBranchConditional %27 %18 %19 + %18 = OpLabel + %30 = OpIAdd %int %25 %57 + %32 = OpIEqual %bool %30 %int_20 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %36 + %33 = OpLabel + OpBranch %34 + %36 = OpLabel + %38 = OpLoad %int %vB + %40 = OpIAdd %int %38 %57 + %42 = OpIEqual %bool %40 %int_40 + %64 = OpSelect %int %42 %int_60 %60 + OpBranch %34 + %34 = OpLabel + %58 = OpPhi %int %int_50 %33 %64 %36 + %49 = OpIAdd %int %58 %int_10 + %51 = OpLoad %v4float %FragColor + %53 = OpFAdd %v4float %51 %63 + OpStore %FragColor %53 + OpBranch %20 + %20 = OpLabel + %56 = OpIAdd %int %57 %49 + OpBranch %17 + %19 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc b/third_party/spirv-cross/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc new file mode 100644 index 0000000..0fd4dce --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc @@ -0,0 +1,248 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 162 +; Schema: 0 + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %hs_main "main" %p_pos %p_1 %i_1 %_entryPointOutput_pos %_entryPointOutput %_patchConstantOutput_EdgeTess %_patchConstantOutput_InsideTess + OpExecutionMode %hs_main OutputVertices 3 + OpExecutionMode %hs_main Triangles + OpExecutionMode %hs_main SpacingFractionalOdd + OpExecutionMode %hs_main VertexOrderCw + OpSource HLSL 500 + OpName %hs_main "hs_main" + OpName %VertexOutput "VertexOutput" + OpMemberName %VertexOutput 0 "pos" + OpMemberName %VertexOutput 1 "uv" + OpName %HSOut "HSOut" + OpMemberName %HSOut 0 "pos" + OpMemberName %HSOut 1 "uv" + OpName %_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ "@hs_main(struct-VertexOutput-vf4-vf21[3];u1;" + OpName %p "p" + OpName %i "i" + OpName %HSConstantOut "HSConstantOut" + OpMemberName %HSConstantOut 0 "EdgeTess" + OpMemberName %HSConstantOut 1 "InsideTess" + OpName %PatchHS_struct_VertexOutput_vf4_vf21_3__ "PatchHS(struct-VertexOutput-vf4-vf21[3];" + OpName %patch "patch" + OpName %output "output" + OpName %p_0 "p" + OpName %p_pos "p.pos" + OpName %VertexOutput_0 "VertexOutput" + OpMemberName %VertexOutput_0 0 "uv" + OpName %p_1 "p" + OpName %i_0 "i" + OpName %i_1 "i" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %param_0 "param" + OpName %_entryPointOutput_pos "@entryPointOutput.pos" + OpName %HSOut_0 "HSOut" + OpMemberName %HSOut_0 0 "uv" + OpName %_entryPointOutput "@entryPointOutput" + OpName %_patchConstantResult "@patchConstantResult" + OpName %param_1 "param" + OpName %_patchConstantOutput_EdgeTess "@patchConstantOutput.EdgeTess" + OpName %_patchConstantOutput_InsideTess "@patchConstantOutput.InsideTess" + OpName %output_0 "output" + OpDecorate %p_pos BuiltIn Position + OpDecorate %p_1 Location 0 + OpDecorate %i_1 BuiltIn InvocationId + OpDecorate %_entryPointOutput_pos BuiltIn Position + OpDecorate %_entryPointOutput Location 0 + OpDecorate %_patchConstantOutput_EdgeTess Patch + OpDecorate %_patchConstantOutput_EdgeTess BuiltIn TessLevelOuter + OpDecorate %_patchConstantOutput_InsideTess Patch + OpDecorate %_patchConstantOutput_InsideTess BuiltIn TessLevelInner + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%VertexOutput = OpTypeStruct %v4float %v2float + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3 +%_ptr_Function__arr_VertexOutput_uint_3 = OpTypePointer Function %_arr_VertexOutput_uint_3 +%_ptr_Function_uint = OpTypePointer Function %uint + %HSOut = OpTypeStruct %v4float %v2float + %16 = OpTypeFunction %HSOut %_ptr_Function__arr_VertexOutput_uint_3 %_ptr_Function_uint +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%HSConstantOut = OpTypeStruct %_arr_float_uint_3 %float + %23 = OpTypeFunction %HSConstantOut %_ptr_Function__arr_VertexOutput_uint_3 +%_ptr_Function_HSOut = OpTypePointer Function %HSOut + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %int_1 = OpConstant %int 1 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 + %p_pos = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%VertexOutput_0 = OpTypeStruct %v2float +%_arr_VertexOutput_0_uint_3 = OpTypeArray %VertexOutput_0 %uint_3 +%_ptr_Input__arr_VertexOutput_0_uint_3 = OpTypePointer Input %_arr_VertexOutput_0_uint_3 + %p_1 = OpVariable %_ptr_Input__arr_VertexOutput_0_uint_3 Input +%_ptr_Input_v2float = OpTypePointer Input %v2float + %int_2 = OpConstant %int 2 +%_ptr_Input_uint = OpTypePointer Input %uint + %i_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 +%_entryPointOutput_pos = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float + %HSOut_0 = OpTypeStruct %v2float +%_arr_HSOut_0_uint_3 = OpTypeArray %HSOut_0 %uint_3 +%_ptr_Output__arr_HSOut_0_uint_3 = OpTypePointer Output %_arr_HSOut_0_uint_3 +%_entryPointOutput = OpVariable %_ptr_Output__arr_HSOut_0_uint_3 Output +%_ptr_Output_v2float = OpTypePointer Output %v2float + %uint_2 = OpConstant %uint 2 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool +%_ptr_Function_HSConstantOut = OpTypePointer Function %HSConstantOut + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%_patchConstantOutput_EdgeTess = OpVariable %_ptr_Output__arr_float_uint_4 Output +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_patchConstantOutput_InsideTess = OpVariable %_ptr_Output__arr_float_uint_2 Output + %float_1 = OpConstant %float 1 + %hs_main = OpFunction %void None %3 + %5 = OpLabel + %p_0 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %i_0 = OpVariable %_ptr_Function_uint Function +%flattenTemp = OpVariable %_ptr_Function_HSOut Function + %param = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %param_0 = OpVariable %_ptr_Function_uint Function +%_patchConstantResult = OpVariable %_ptr_Function_HSConstantOut Function + %param_1 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %50 = OpAccessChain %_ptr_Input_v4float %p_pos %int_0 + %51 = OpLoad %v4float %50 + %52 = OpAccessChain %_ptr_Function_v4float %p_0 %int_0 %int_0 + OpStore %52 %51 + %58 = OpAccessChain %_ptr_Input_v2float %p_1 %int_0 %int_0 + %59 = OpLoad %v2float %58 + %60 = OpAccessChain %_ptr_Function_v2float %p_0 %int_0 %int_1 + OpStore %60 %59 + %61 = OpAccessChain %_ptr_Input_v4float %p_pos %int_1 + %62 = OpLoad %v4float %61 + %63 = OpAccessChain %_ptr_Function_v4float %p_0 %int_1 %int_0 + OpStore %63 %62 + %64 = OpAccessChain %_ptr_Input_v2float %p_1 %int_1 %int_0 + %65 = OpLoad %v2float %64 + %66 = OpAccessChain %_ptr_Function_v2float %p_0 %int_1 %int_1 + OpStore %66 %65 + %68 = OpAccessChain %_ptr_Input_v4float %p_pos %int_2 + %69 = OpLoad %v4float %68 + %70 = OpAccessChain %_ptr_Function_v4float %p_0 %int_2 %int_0 + OpStore %70 %69 + %71 = OpAccessChain %_ptr_Input_v2float %p_1 %int_2 %int_0 + %72 = OpLoad %v2float %71 + %73 = OpAccessChain %_ptr_Function_v2float %p_0 %int_2 %int_1 + OpStore %73 %72 + %77 = OpLoad %uint %i_1 + OpStore %i_0 %77 + %80 = OpLoad %_arr_VertexOutput_uint_3 %p_0 + OpStore %param %80 + %82 = OpLoad %uint %i_0 + OpStore %param_0 %82 + %83 = OpFunctionCall %HSOut %_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ %param %param_0 + OpStore %flattenTemp %83 + %86 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %87 = OpLoad %v4float %86 + %94 = OpLoad %uint %i_1 + %89 = OpAccessChain %_ptr_Output_v4float %_entryPointOutput_pos %94 + OpStore %89 %87 + %95 = OpAccessChain %_ptr_Function_v2float %flattenTemp %int_1 + %96 = OpLoad %v2float %95 + %98 = OpAccessChain %_ptr_Output_v2float %_entryPointOutput %94 %int_0 + OpStore %98 %96 + OpControlBarrier %uint_2 %uint_1 %uint_0 + %102 = OpLoad %uint %i_1 + %104 = OpIEqual %bool %102 %int_0 + OpSelectionMerge %106 None + OpBranchConditional %104 %105 %106 + %105 = OpLabel + %110 = OpLoad %_arr_VertexOutput_uint_3 %p_0 + OpStore %param_1 %110 + %111 = OpFunctionCall %HSConstantOut %PatchHS_struct_VertexOutput_vf4_vf21_3__ %param_1 + OpStore %_patchConstantResult %111 + %117 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_0 + %118 = OpLoad %float %117 + %120 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_0 + OpStore %120 %118 + %121 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_1 + %122 = OpLoad %float %121 + %123 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_1 + OpStore %123 %122 + %124 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_2 + %125 = OpLoad %float %124 + %126 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_2 + OpStore %126 %125 + %130 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_1 + %131 = OpLoad %float %130 + %132 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_InsideTess %int_0 + OpStore %132 %131 + OpBranch %106 + %106 = OpLabel + OpReturn + OpFunctionEnd +%_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ = OpFunction %HSOut None %16 + %p = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %i = OpFunctionParameter %_ptr_Function_uint + %20 = OpLabel + %output = OpVariable %_ptr_Function_HSOut Function + %31 = OpLoad %uint %i + %33 = OpAccessChain %_ptr_Function_v4float %p %31 %int_0 + %34 = OpLoad %v4float %33 + %35 = OpAccessChain %_ptr_Function_v4float %output %int_0 + OpStore %35 %34 + %37 = OpLoad %uint %i + %39 = OpAccessChain %_ptr_Function_v2float %p %37 %int_1 + %40 = OpLoad %v2float %39 + %41 = OpAccessChain %_ptr_Function_v2float %output %int_1 + OpStore %41 %40 + %42 = OpLoad %HSOut %output + OpReturnValue %42 + OpFunctionEnd +%PatchHS_struct_VertexOutput_vf4_vf21_3__ = OpFunction %HSConstantOut None %23 + %patch = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %26 = OpLabel + %output_0 = OpVariable %_ptr_Function_HSConstantOut Function + %135 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %136 = OpLoad %v2float %135 + %137 = OpCompositeConstruct %v2float %float_1 %float_1 + %138 = OpFAdd %v2float %137 %136 + %139 = OpCompositeExtract %float %138 0 + %140 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_0 + OpStore %140 %139 + %141 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %142 = OpLoad %v2float %141 + %143 = OpCompositeConstruct %v2float %float_1 %float_1 + %144 = OpFAdd %v2float %143 %142 + %145 = OpCompositeExtract %float %144 0 + %146 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_1 + OpStore %146 %145 + %147 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %148 = OpLoad %v2float %147 + %149 = OpCompositeConstruct %v2float %float_1 %float_1 + %150 = OpFAdd %v2float %149 %148 + %151 = OpCompositeExtract %float %150 0 + %152 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_2 + OpStore %152 %151 + %153 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %154 = OpLoad %v2float %153 + %155 = OpCompositeConstruct %v2float %float_1 %float_1 + %156 = OpFAdd %v2float %155 %154 + %157 = OpCompositeExtract %float %156 0 + %158 = OpAccessChain %_ptr_Function_float %output_0 %int_1 + OpStore %158 %157 + %159 = OpLoad %HSConstantOut %output_0 + OpReturnValue %159 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.multi-patch.asm.tesc b/third_party/spirv-cross/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.multi-patch.asm.tesc new file mode 100644 index 0000000..0fd4dce --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.multi-patch.asm.tesc @@ -0,0 +1,248 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 162 +; Schema: 0 + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %hs_main "main" %p_pos %p_1 %i_1 %_entryPointOutput_pos %_entryPointOutput %_patchConstantOutput_EdgeTess %_patchConstantOutput_InsideTess + OpExecutionMode %hs_main OutputVertices 3 + OpExecutionMode %hs_main Triangles + OpExecutionMode %hs_main SpacingFractionalOdd + OpExecutionMode %hs_main VertexOrderCw + OpSource HLSL 500 + OpName %hs_main "hs_main" + OpName %VertexOutput "VertexOutput" + OpMemberName %VertexOutput 0 "pos" + OpMemberName %VertexOutput 1 "uv" + OpName %HSOut "HSOut" + OpMemberName %HSOut 0 "pos" + OpMemberName %HSOut 1 "uv" + OpName %_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ "@hs_main(struct-VertexOutput-vf4-vf21[3];u1;" + OpName %p "p" + OpName %i "i" + OpName %HSConstantOut "HSConstantOut" + OpMemberName %HSConstantOut 0 "EdgeTess" + OpMemberName %HSConstantOut 1 "InsideTess" + OpName %PatchHS_struct_VertexOutput_vf4_vf21_3__ "PatchHS(struct-VertexOutput-vf4-vf21[3];" + OpName %patch "patch" + OpName %output "output" + OpName %p_0 "p" + OpName %p_pos "p.pos" + OpName %VertexOutput_0 "VertexOutput" + OpMemberName %VertexOutput_0 0 "uv" + OpName %p_1 "p" + OpName %i_0 "i" + OpName %i_1 "i" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %param_0 "param" + OpName %_entryPointOutput_pos "@entryPointOutput.pos" + OpName %HSOut_0 "HSOut" + OpMemberName %HSOut_0 0 "uv" + OpName %_entryPointOutput "@entryPointOutput" + OpName %_patchConstantResult "@patchConstantResult" + OpName %param_1 "param" + OpName %_patchConstantOutput_EdgeTess "@patchConstantOutput.EdgeTess" + OpName %_patchConstantOutput_InsideTess "@patchConstantOutput.InsideTess" + OpName %output_0 "output" + OpDecorate %p_pos BuiltIn Position + OpDecorate %p_1 Location 0 + OpDecorate %i_1 BuiltIn InvocationId + OpDecorate %_entryPointOutput_pos BuiltIn Position + OpDecorate %_entryPointOutput Location 0 + OpDecorate %_patchConstantOutput_EdgeTess Patch + OpDecorate %_patchConstantOutput_EdgeTess BuiltIn TessLevelOuter + OpDecorate %_patchConstantOutput_InsideTess Patch + OpDecorate %_patchConstantOutput_InsideTess BuiltIn TessLevelInner + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%VertexOutput = OpTypeStruct %v4float %v2float + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3 +%_ptr_Function__arr_VertexOutput_uint_3 = OpTypePointer Function %_arr_VertexOutput_uint_3 +%_ptr_Function_uint = OpTypePointer Function %uint + %HSOut = OpTypeStruct %v4float %v2float + %16 = OpTypeFunction %HSOut %_ptr_Function__arr_VertexOutput_uint_3 %_ptr_Function_uint +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%HSConstantOut = OpTypeStruct %_arr_float_uint_3 %float + %23 = OpTypeFunction %HSConstantOut %_ptr_Function__arr_VertexOutput_uint_3 +%_ptr_Function_HSOut = OpTypePointer Function %HSOut + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %int_1 = OpConstant %int 1 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 + %p_pos = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%VertexOutput_0 = OpTypeStruct %v2float +%_arr_VertexOutput_0_uint_3 = OpTypeArray %VertexOutput_0 %uint_3 +%_ptr_Input__arr_VertexOutput_0_uint_3 = OpTypePointer Input %_arr_VertexOutput_0_uint_3 + %p_1 = OpVariable %_ptr_Input__arr_VertexOutput_0_uint_3 Input +%_ptr_Input_v2float = OpTypePointer Input %v2float + %int_2 = OpConstant %int 2 +%_ptr_Input_uint = OpTypePointer Input %uint + %i_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 +%_entryPointOutput_pos = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float + %HSOut_0 = OpTypeStruct %v2float +%_arr_HSOut_0_uint_3 = OpTypeArray %HSOut_0 %uint_3 +%_ptr_Output__arr_HSOut_0_uint_3 = OpTypePointer Output %_arr_HSOut_0_uint_3 +%_entryPointOutput = OpVariable %_ptr_Output__arr_HSOut_0_uint_3 Output +%_ptr_Output_v2float = OpTypePointer Output %v2float + %uint_2 = OpConstant %uint 2 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool +%_ptr_Function_HSConstantOut = OpTypePointer Function %HSConstantOut + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%_patchConstantOutput_EdgeTess = OpVariable %_ptr_Output__arr_float_uint_4 Output +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_patchConstantOutput_InsideTess = OpVariable %_ptr_Output__arr_float_uint_2 Output + %float_1 = OpConstant %float 1 + %hs_main = OpFunction %void None %3 + %5 = OpLabel + %p_0 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %i_0 = OpVariable %_ptr_Function_uint Function +%flattenTemp = OpVariable %_ptr_Function_HSOut Function + %param = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %param_0 = OpVariable %_ptr_Function_uint Function +%_patchConstantResult = OpVariable %_ptr_Function_HSConstantOut Function + %param_1 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %50 = OpAccessChain %_ptr_Input_v4float %p_pos %int_0 + %51 = OpLoad %v4float %50 + %52 = OpAccessChain %_ptr_Function_v4float %p_0 %int_0 %int_0 + OpStore %52 %51 + %58 = OpAccessChain %_ptr_Input_v2float %p_1 %int_0 %int_0 + %59 = OpLoad %v2float %58 + %60 = OpAccessChain %_ptr_Function_v2float %p_0 %int_0 %int_1 + OpStore %60 %59 + %61 = OpAccessChain %_ptr_Input_v4float %p_pos %int_1 + %62 = OpLoad %v4float %61 + %63 = OpAccessChain %_ptr_Function_v4float %p_0 %int_1 %int_0 + OpStore %63 %62 + %64 = OpAccessChain %_ptr_Input_v2float %p_1 %int_1 %int_0 + %65 = OpLoad %v2float %64 + %66 = OpAccessChain %_ptr_Function_v2float %p_0 %int_1 %int_1 + OpStore %66 %65 + %68 = OpAccessChain %_ptr_Input_v4float %p_pos %int_2 + %69 = OpLoad %v4float %68 + %70 = OpAccessChain %_ptr_Function_v4float %p_0 %int_2 %int_0 + OpStore %70 %69 + %71 = OpAccessChain %_ptr_Input_v2float %p_1 %int_2 %int_0 + %72 = OpLoad %v2float %71 + %73 = OpAccessChain %_ptr_Function_v2float %p_0 %int_2 %int_1 + OpStore %73 %72 + %77 = OpLoad %uint %i_1 + OpStore %i_0 %77 + %80 = OpLoad %_arr_VertexOutput_uint_3 %p_0 + OpStore %param %80 + %82 = OpLoad %uint %i_0 + OpStore %param_0 %82 + %83 = OpFunctionCall %HSOut %_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ %param %param_0 + OpStore %flattenTemp %83 + %86 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %87 = OpLoad %v4float %86 + %94 = OpLoad %uint %i_1 + %89 = OpAccessChain %_ptr_Output_v4float %_entryPointOutput_pos %94 + OpStore %89 %87 + %95 = OpAccessChain %_ptr_Function_v2float %flattenTemp %int_1 + %96 = OpLoad %v2float %95 + %98 = OpAccessChain %_ptr_Output_v2float %_entryPointOutput %94 %int_0 + OpStore %98 %96 + OpControlBarrier %uint_2 %uint_1 %uint_0 + %102 = OpLoad %uint %i_1 + %104 = OpIEqual %bool %102 %int_0 + OpSelectionMerge %106 None + OpBranchConditional %104 %105 %106 + %105 = OpLabel + %110 = OpLoad %_arr_VertexOutput_uint_3 %p_0 + OpStore %param_1 %110 + %111 = OpFunctionCall %HSConstantOut %PatchHS_struct_VertexOutput_vf4_vf21_3__ %param_1 + OpStore %_patchConstantResult %111 + %117 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_0 + %118 = OpLoad %float %117 + %120 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_0 + OpStore %120 %118 + %121 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_1 + %122 = OpLoad %float %121 + %123 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_1 + OpStore %123 %122 + %124 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_2 + %125 = OpLoad %float %124 + %126 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_2 + OpStore %126 %125 + %130 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_1 + %131 = OpLoad %float %130 + %132 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_InsideTess %int_0 + OpStore %132 %131 + OpBranch %106 + %106 = OpLabel + OpReturn + OpFunctionEnd +%_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ = OpFunction %HSOut None %16 + %p = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %i = OpFunctionParameter %_ptr_Function_uint + %20 = OpLabel + %output = OpVariable %_ptr_Function_HSOut Function + %31 = OpLoad %uint %i + %33 = OpAccessChain %_ptr_Function_v4float %p %31 %int_0 + %34 = OpLoad %v4float %33 + %35 = OpAccessChain %_ptr_Function_v4float %output %int_0 + OpStore %35 %34 + %37 = OpLoad %uint %i + %39 = OpAccessChain %_ptr_Function_v2float %p %37 %int_1 + %40 = OpLoad %v2float %39 + %41 = OpAccessChain %_ptr_Function_v2float %output %int_1 + OpStore %41 %40 + %42 = OpLoad %HSOut %output + OpReturnValue %42 + OpFunctionEnd +%PatchHS_struct_VertexOutput_vf4_vf21_3__ = OpFunction %HSConstantOut None %23 + %patch = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %26 = OpLabel + %output_0 = OpVariable %_ptr_Function_HSConstantOut Function + %135 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %136 = OpLoad %v2float %135 + %137 = OpCompositeConstruct %v2float %float_1 %float_1 + %138 = OpFAdd %v2float %137 %136 + %139 = OpCompositeExtract %float %138 0 + %140 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_0 + OpStore %140 %139 + %141 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %142 = OpLoad %v2float %141 + %143 = OpCompositeConstruct %v2float %float_1 %float_1 + %144 = OpFAdd %v2float %143 %142 + %145 = OpCompositeExtract %float %144 0 + %146 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_1 + OpStore %146 %145 + %147 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %148 = OpLoad %v2float %147 + %149 = OpCompositeConstruct %v2float %float_1 %float_1 + %150 = OpFAdd %v2float %149 %148 + %151 = OpCompositeExtract %float %150 0 + %152 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_2 + OpStore %152 %151 + %153 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %154 = OpLoad %v2float %153 + %155 = OpCompositeConstruct %v2float %float_1 %float_1 + %156 = OpFAdd %v2float %155 %154 + %157 = OpCompositeExtract %float %156 0 + %158 = OpAccessChain %_ptr_Function_float %output_0 %int_1 + OpStore %158 %157 + %159 = OpLoad %HSConstantOut %output_0 + OpReturnValue %159 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/composite-extract-physical-type-id.asm.vert b/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/composite-extract-physical-type-id.asm.vert new file mode 100644 index 0000000..d44e325 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/composite-extract-physical-type-id.asm.vert @@ -0,0 +1,63 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %VSMain "main" %gl_VertexIndex %gl_Position + OpSource HLSL 600 + OpName %type_Float2Array "type.Float2Array" + OpMemberName %type_Float2Array 0 "arr" + OpName %Float2Array "Float2Array" + OpName %VSMain "VSMain" + OpName %param_var_i "param.var.i" + OpName %src_VSMain "src.VSMain" + OpName %i "i" + OpName %bb_entry "bb.entry" + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpDecorate %gl_Position BuiltIn Position + OpDecorate %Float2Array DescriptorSet 0 + OpDecorate %Float2Array Binding 0 + OpDecorate %_arr_v2float_uint_3 ArrayStride 16 + OpMemberDecorate %type_Float2Array 0 Offset 0 + OpDecorate %type_Float2Array Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %v2float = OpTypeVector %float 2 +%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3 +%type_Float2Array = OpTypeStruct %_arr_v2float_uint_3 +%_ptr_Uniform_type_Float2Array = OpTypePointer Uniform %type_Float2Array +%_ptr_Input_uint = OpTypePointer Input %uint + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %20 = OpTypeFunction %void +%_ptr_Function_uint = OpTypePointer Function %uint + %27 = OpTypeFunction %v4float %_ptr_Function_uint +%_ptr_Uniform__arr_v2float_uint_3 = OpTypePointer Uniform %_arr_v2float_uint_3 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%Float2Array = OpVariable %_ptr_Uniform_type_Float2Array Uniform +%gl_VertexIndex = OpVariable %_ptr_Input_uint Input +%gl_Position = OpVariable %_ptr_Output_v4float Output + %VSMain = OpFunction %void None %20 + %21 = OpLabel +%param_var_i = OpVariable %_ptr_Function_uint Function + %24 = OpLoad %uint %gl_VertexIndex + OpStore %param_var_i %24 + %25 = OpFunctionCall %v4float %src_VSMain %param_var_i + OpStore %gl_Position %25 + OpReturn + OpFunctionEnd + %src_VSMain = OpFunction %v4float None %27 + %i = OpFunctionParameter %_ptr_Function_uint + %bb_entry = OpLabel + %30 = OpLoad %uint %i + %32 = OpAccessChain %_ptr_Uniform__arr_v2float_uint_3 %Float2Array %int_0 + %34 = OpAccessChain %_ptr_Uniform_v2float %32 %30 + %35 = OpLoad %v2float %34 + %36 = OpCompositeExtract %float %35 0 + %37 = OpCompositeExtract %float %35 1 + %38 = OpCompositeConstruct %v4float %36 %37 %float_0 %float_1 + OpReturnValue %38 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/empty-struct-composite.asm.vert b/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/empty-struct-composite.asm.vert new file mode 100644 index 0000000..038ecaa --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/empty-struct-composite.asm.vert @@ -0,0 +1,36 @@ +; SPIR-V +; Version: 1.1 +; Generator: Google rspirv; 0 +; Bound: 17 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "main" + OpName %Test "Test" + OpName %t "t" + OpName %retvar "retvar" + OpName %main "main" + OpName %retvar_0 "retvar" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %Test = OpTypeStruct +%_ptr_Function_Test = OpTypePointer Function %Test +%_ptr_Function_void = OpTypePointer Function %void + %2 = OpFunction %void None %6 + %7 = OpLabel + %t = OpVariable %_ptr_Function_Test Function + %retvar = OpVariable %_ptr_Function_void Function + OpBranch %4 + %4 = OpLabel + %13 = OpCompositeConstruct %Test + OpStore %t %13 + OpReturn + OpFunctionEnd + %main = OpFunction %void None %6 + %15 = OpLabel + %retvar_0 = OpVariable %_ptr_Function_void Function + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag new file mode 100644 index 0000000..fecc83a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 39 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %vs_main "main" %gl_Position + OpSource HLSL 600 + OpName %vs_main "vs_main" + OpDecorate %gl_Position BuiltIn Position + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %int_1 = OpConstant %int 1 + %float_3 = OpConstant %float 3 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %15 = OpTypeFunction %void + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Function__arr_float_uint_2 = OpTypePointer Function %_arr_float_uint_2 +%_ptr_Function_float = OpTypePointer Function %float + %bool = OpTypeBool +%gl_Position = OpVariable %_ptr_Output_v4float Output + %21 = OpUndef %float + %vs_main = OpFunction %void None %15 + %22 = OpLabel + %23 = OpVariable %_ptr_Function__arr_float_uint_2 Function + OpBranch %24 + %24 = OpLabel + %25 = OpPhi %int %int_0 %22 %26 %27 + %28 = OpSLessThan %bool %25 %int_2 + OpLoopMerge %29 %27 None + OpBranchConditional %28 %27 %29 + %27 = OpLabel + %30 = OpAccessChain %_ptr_Function_float %23 %25 + OpStore %30 %float_0 + %26 = OpIAdd %int %25 %int_1 + OpBranch %24 + %29 = OpLabel + %31 = OpLoad %_arr_float_uint_2 %23 + %32 = OpBitcast %uint %float_3 + %33 = OpINotEqual %bool %32 %uint_0 + OpSelectionMerge %34 None + OpBranchConditional %33 %35 %34 + %35 = OpLabel + %36 = OpCompositeExtract %float %31 0 + OpBranch %34 + %34 = OpLabel + %37 = OpPhi %float %21 %29 %36 %35 + %38 = OpCompositeConstruct %v4float %float_0 %float_0 %float_0 %37 + OpStore %gl_Position %38 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/array-copy-threadgroup-memory.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/array-copy-threadgroup-memory.comp new file mode 100644 index 0000000..081c396 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/array-copy-threadgroup-memory.comp @@ -0,0 +1,18 @@ +#version 450 +layout(local_size_x = 8) in; + +shared float shared_group[8][8]; +shared float shared_group_alt[8][8]; + +void main() +{ + float blob[8]; + for (int i = 0; i < 8; i++) + blob[i] = float(i); + shared_group[gl_LocalInvocationIndex] = blob; + + barrier(); + + float copied_blob[8] = shared_group[gl_LocalInvocationIndex ^ 1u]; + shared_group_alt[gl_LocalInvocationIndex] = shared_group[gl_LocalInvocationIndex]; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/basic.dynamic-buffer.msl2.invalid.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/basic.dynamic-buffer.msl2.invalid.comp new file mode 100644 index 0000000..c296573 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/basic.dynamic-buffer.msl2.invalid.comp @@ -0,0 +1,27 @@ +#version 450 +layout(local_size_x = 3, local_size_y = 3, local_size_z = 2) in; + +layout(set = 0, binding = 0) uniform Foo +{ + int a; + int b; +}; + +layout(set = 0, binding = 1) uniform Bar +{ + int c; + int d; +}; + +layout(set = 1, binding = 2) buffer Baz +{ + int e; + int f; +} baz[3][3][2]; + +void main() +{ + uvec3 coords = gl_GlobalInvocationID; + baz[coords.x][coords.y][coords.z].e = a + c; + baz[coords.x][coords.y][coords.z].f = b * d; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/bitcast-16bit-1.invalid.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/bitcast-16bit-1.invalid.comp new file mode 100644 index 0000000..0c21cda --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/bitcast-16bit-1.invalid.comp @@ -0,0 +1,23 @@ +#version 450 core +#extension GL_AMD_gpu_shader_half_float : require +#extension GL_AMD_gpu_shader_int16 : require +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + i16vec4 inputs[]; +}; + +layout(binding = 1, std430) buffer SSBO1 +{ + ivec4 outputs[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + f16vec2 a = int16BitsToFloat16(inputs[ident].xy); + outputs[ident].x = int(packFloat2x16(a + f16vec2(1, 1))); + outputs[ident].y = packInt2x16(inputs[ident].zw); + outputs[ident].z = int(packUint2x16(u16vec2(inputs[ident].xy))); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/bitcast-16bit-2.invalid.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/bitcast-16bit-2.invalid.comp new file mode 100644 index 0000000..6bb6624 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/bitcast-16bit-2.invalid.comp @@ -0,0 +1,26 @@ +#version 450 core +#extension GL_AMD_gpu_shader_half_float : require +#extension GL_AMD_gpu_shader_int16 : require +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + ivec4 inputs[]; +}; + +layout(binding = 1, std430) buffer SSBO1 +{ + i16vec4 outputs[]; +}; + +layout(binding = 2) uniform UBO +{ + f16vec4 const0; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + outputs[ident].xy = unpackInt2x16(inputs[ident].x) + float16BitsToInt16(const0.xy); + outputs[ident].zw = i16vec2(unpackUint2x16(uint(inputs[ident].y)) - float16BitsToUint16(const0.zw)); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/bitfield.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/bitfield.comp new file mode 100644 index 0000000..0cac0b2 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/bitfield.comp @@ -0,0 +1,23 @@ +#version 310 es + +void main() +{ + int signed_value = 0; + uint unsigned_value = 0u; + + int s = bitfieldExtract(signed_value, 5, 20); + uint u = bitfieldExtract(unsigned_value, 6, 21); + s = bitfieldInsert(s, 40, 5, 4); + u = bitfieldInsert(u, 60u, 5, 4); + + u = bitfieldReverse(u); + s = bitfieldReverse(s); + + int v0 = bitCount(u); + int v1 = bitCount(s); + + int v2 = findMSB(u); + int v3 = findMSB(s); + int v4 = findLSB(u); + int v5 = findLSB(s); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/glsl.std450.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/glsl.std450.comp new file mode 100644 index 0000000..a17a82b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/glsl.std450.comp @@ -0,0 +1,129 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float res; + int ires; + uint ures; + + vec4 f32; + ivec4 s32; + uvec4 u32; + + mat2 m2; + mat3 m3; + mat4 m4; +}; + +void main() +{ + float tmp; + vec2 v2; + vec3 v3; + vec4 v4; + int itmp; + + res = round(f32.x); + res = roundEven(f32.x); + res = trunc(f32.x); + res = abs(f32.x); + ires = abs(s32.x); + res = sign(f32.x); + ires = sign(s32.x); + res = floor(f32.x); + res = ceil(f32.x); + res = fract(f32.x); + res = radians(f32.x); + res = degrees(f32.x); + res = sin(f32.x); + res = cos(f32.x); + res = tan(f32.x); + res = asin(f32.x); + res = acos(f32.x); + res = atan(f32.x); + res = sinh(f32.x); + res = cosh(f32.x); + res = tanh(f32.x); + res = asinh(f32.x); + res = acosh(f32.x); + res = atanh(f32.x); + res = atan(f32.x, f32.y); + res = pow(f32.x, f32.y); + res = exp(f32.x); + res = log(f32.x); + res = exp2(f32.x); + res = log2(f32.x); + res = sqrt(f32.x); + res = inversesqrt(f32.x); + + res = length(f32.x); + res = distance(f32.x, f32.y); + res = normalize(f32.x); + res = faceforward(f32.x, f32.y, f32.z); + res = reflect(f32.x, f32.y); + res = refract(f32.x, f32.y, f32.z); + + res = length(f32.xy); + res = distance(f32.xy, f32.zw); + v2 = normalize(f32.xy); + v2 = faceforward(f32.xy, f32.yz, f32.zw); + v2 = reflect(f32.xy, f32.zw); + v2 = refract(f32.xy, f32.yz, f32.w); + + v3 = cross(f32.xyz, f32.yzw); + + res = determinant(m2); + res = determinant(m3); + res = determinant(m4); + m2 = inverse(m2); + m3 = inverse(m3); + m4 = inverse(m4); + + res = modf(f32.x, tmp); + // ModfStruct + + res = min(f32.x, f32.y); + ures = min(u32.x, u32.y); + ires = min(s32.x, s32.y); + res = max(f32.x, f32.y); + ures = max(u32.x, u32.y); + ires = max(s32.x, s32.y); + + res = clamp(f32.x, f32.y, f32.z); + ures = clamp(u32.x, u32.y, u32.z); + ires = clamp(s32.x, s32.y, s32.z); + + res = mix(f32.x, f32.y, f32.z); + res = step(f32.x, f32.y); + res = smoothstep(f32.x, f32.y, f32.z); + res = fma(f32.x, f32.y, f32.z); + + res = frexp(f32.x, itmp); + // FrexpStruct + res = ldexp(f32.x, itmp); + + ures = packSnorm4x8(f32); + ures = packUnorm4x8(f32); + ures = packSnorm2x16(f32.xy); + ures = packUnorm2x16(f32.xy); + ures = packHalf2x16(f32.xy); + // packDouble2x32 + + v2 = unpackSnorm2x16(u32.x); + v2 = unpackUnorm2x16(u32.x); + v2 = unpackHalf2x16(u32.x); + v4 = unpackSnorm4x8(u32.x); + v4 = unpackUnorm4x8(u32.x); + // unpackDouble2x32 + + s32 = findLSB(s32); + s32 = findLSB(u32); + s32 = findMSB(s32); + s32 = findMSB(u32); + + // interpolateAtSample + // interpolateAtOffset + + // NMin, NMax, NClamp +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/illegal-struct-name.asm.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/illegal-struct-name.asm.comp new file mode 100644 index 0000000..f7a8787 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/illegal-struct-name.asm.comp @@ -0,0 +1,62 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 31 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "Foo" + OpMemberName %Foo 0 "abs" + OpName %f "f" + OpName %Foo_0 "Foo" + OpMemberName %Foo_0 0 "abs" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "foo" + OpMemberName %SSBO 1 "foo2" + OpName %_ "" + OpName %linear "abs" + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Foo = OpTypeStruct %float +%_ptr_Function_Foo = OpTypePointer Function %Foo + %Foo_0 = OpTypeStruct %float + %SSBO = OpTypeStruct %Foo_0 %Foo_0 +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Function_int = OpTypePointer Function %int + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %main = OpFunction %void None %3 + %5 = OpLabel + %f = OpVariable %_ptr_Function_Foo Function + %linear = OpVariable %_ptr_Function_int Function + %17 = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_0 + %18 = OpLoad %Foo_0 %17 + %19 = OpCompositeExtract %float %18 0 + %21 = OpAccessChain %_ptr_Function_float %f %int_0 + OpStore %21 %19 + OpStore %linear %int_10 + %26 = OpLoad %Foo %f + %27 = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_1 + %28 = OpCompositeExtract %float %26 0 + %30 = OpAccessChain %_ptr_Uniform_float %27 %int_0 + OpStore %30 %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/int64.invalid.msl22.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/int64.invalid.msl22.comp new file mode 100644 index 0000000..965bed4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/int64.invalid.msl22.comp @@ -0,0 +1,65 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +layout(local_size_x = 1) in; + +struct M0 +{ + int64_t v; + i64vec2 b[2]; + uint64_t c; + uint64_t d[5]; +}; + +struct SSBO0_Type +{ + i64vec4 a; + M0 m0; +}; + +struct SSBO1_Type +{ + u64vec4 b; + M0 m0; +}; + +struct SSBO2_Type +{ + int64_t a[4]; + i64vec2 b[4]; +}; + +struct SSBO3_Type +{ + int64_t a[4]; + i64vec2 b[4]; +}; + +layout(set = 0, binding = 0, std430) buffer SSBO +{ + int s32; + uint u32; +}; + +void main() +{ + SSBO0_Type ssbo_0; + SSBO1_Type ssbo_1; + SSBO2_Type ssbo_2; + SSBO3_Type ssbo_3; + + ssbo_0.a += i64vec4(10, 20, 30, 40); + ssbo_1.b += u64vec4(999999999999999999ul, 8888888888888888ul, 77777777777777777ul, 6666666666666666ul); + ssbo_0.a += 20; + ssbo_0.a = abs(ssbo_0.a + i64vec4(ssbo_1.b)); + + ssbo_0.a++; + ssbo_1.b++; + ssbo_0.a--; + ssbo_1.b--; + + ssbo_2.a[0] += 1l; + ssbo_3.a[0] += 2l; + + s32 = int(ssbo_0.a.x + ssbo_1.b.y + ssbo_2.a[1] + ssbo_3.a[2]); + u32 = uint(ssbo_0.a.y + ssbo_1.b.z + ssbo_2.a[0] + ssbo_3.a[1]); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/loop.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/loop.comp new file mode 100644 index 0000000..6d6c324 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/loop.comp @@ -0,0 +1,98 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idat = in_data[ident]; + + int k = 0; + uint i = 0u; + + if (idat.y == 20.0) + { + do + { + k = k * 2; + i++; + } while (i < ident); + } + + switch (k) + { + case 10: + for (;;) + { + i++; + if (i > 10u) + break; + } + break; + + default: + for (;;) + { + i += 2u; + if (i > 20u) + break; + } + break; + } + + while (k < 10) + { + idat *= 2.0; + k++; + } + + for (uint i = 0u; i < 16u; i++, k++) + for (uint j = 0u; j < 30u; j++) + idat = mvp * idat; + + k = 0; + for (;;) + { + k++; + if (k > 10) + { + k += 2; + } + else + { + k += 3; + continue; + } + + k += 10; + } + + k = 0; + do + { + k++; + } while (k > 10); + + int l = 0; + for (;; l++) + { + if (l == 5) + { + continue; + } + + idat += 1.0; + } + out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/return.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/return.comp new file mode 100644 index 0000000..617f437 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/return.comp @@ -0,0 +1,33 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + + if (ident == 2u) + { + out_data[ident] = vec4(20.0); + } + else if (ident == 4u) + { + out_data[ident] = vec4(10.0); + return; + } + + for (int i = 0; i < 20; i++) + { + if (i == 10) + break; + + return; + } + + out_data[ident] = vec4(10.0); +} + diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/std140-array-load-composite-construct.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/std140-array-load-composite-construct.comp new file mode 100644 index 0000000..af1c47b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/std140-array-load-composite-construct.comp @@ -0,0 +1,13 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std140, binding = 0) buffer SSBO +{ + float a[16]; + vec4 b[16]; +}; + +void main() +{ + b[gl_GlobalInvocationID.x] = vec4(a[gl_GlobalInvocationID.x]); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp new file mode 100644 index 0000000..47d8891 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp @@ -0,0 +1,100 @@ +#version 310 es +#extension GL_EXT_scalar_block_layout : require + +layout(local_size_x = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + + // glslang seems to miscompile this atm into ArrayStride of 16 even in scalar layout. + //S4 m3s[8]; +}; + +layout(binding = 2, scalar) restrict buffer SSBO2 +{ + float m0; + mat2 m1; + layout(row_major) mat3x2 m2; +} ssbo_scalar2; + +layout(binding = 1, scalar) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_scalar; + +layout(binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + + float array[]; +} ssbo_140; + +void main() +{ + ssbo_scalar.content = ssbo_140.content; + ssbo_scalar.content.m1.a = ssbo_scalar.m2[1] * ssbo_scalar.content.m0.a[0]; // test packed matrix access + ssbo_scalar.m0 = ssbo_scalar2.m1; + ssbo_scalar2.m1 = ssbo_scalar.m4; + ssbo_scalar2.m2 = ssbo_scalar.m3; +} + diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.comp new file mode 100644 index 0000000..28c5d6b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.comp @@ -0,0 +1,138 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_ballot : require +#extension GL_KHR_shader_subgroup_vote : require +#extension GL_KHR_shader_subgroup_shuffle : require +#extension GL_KHR_shader_subgroup_shuffle_relative : require +#extension GL_KHR_shader_subgroup_arithmetic : require +#extension GL_KHR_shader_subgroup_clustered : require +#extension GL_KHR_shader_subgroup_quad : require +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float FragColor; +}; + +void main() +{ + // basic + FragColor = float(gl_NumSubgroups); + FragColor = float(gl_SubgroupID); + FragColor = float(gl_SubgroupSize); + FragColor = float(gl_SubgroupInvocationID); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierShared(); + subgroupMemoryBarrierImage(); + bool elected = subgroupElect(); + + // ballot + FragColor = float(gl_SubgroupEqMask); + FragColor = float(gl_SubgroupGeMask); + FragColor = float(gl_SubgroupGtMask); + FragColor = float(gl_SubgroupLeMask); + FragColor = float(gl_SubgroupLtMask); + vec4 broadcasted = subgroupBroadcast(vec4(10.0), 8u); + bvec2 broadcasted_bool = subgroupBroadcast(bvec2(true), 8u); + vec3 first = subgroupBroadcastFirst(vec3(20.0)); + bvec4 first_bool = subgroupBroadcastFirst(bvec4(false)); + uvec4 ballot_value = subgroupBallot(true); + bool inverse_ballot_value = subgroupInverseBallot(ballot_value); + bool bit_extracted = subgroupBallotBitExtract(uvec4(10u), 8u); + uint bit_count = subgroupBallotBitCount(ballot_value); + uint inclusive_bit_count = subgroupBallotInclusiveBitCount(ballot_value); + uint exclusive_bit_count = subgroupBallotExclusiveBitCount(ballot_value); + uint lsb = subgroupBallotFindLSB(ballot_value); + uint msb = subgroupBallotFindMSB(ballot_value); + + // shuffle + uint shuffled = subgroupShuffle(10u, 8u); + bool shuffled_bool = subgroupShuffle(true, 9u); + uint shuffled_xor = subgroupShuffleXor(30u, 8u); + bool shuffled_xor_bool = subgroupShuffleXor(false, 9u); + + // shuffle relative + uint shuffled_up = subgroupShuffleUp(20u, 4u); + bool shuffled_up_bool = subgroupShuffleUp(true, 4u); + uint shuffled_down = subgroupShuffleDown(20u, 4u); + bool shuffled_down_bool = subgroupShuffleDown(false, 4u); + + // vote + bool has_all = subgroupAll(true); + bool has_any = subgroupAny(true); + bool has_equal = subgroupAllEqual(0); + has_equal = subgroupAllEqual(true); + has_equal = subgroupAllEqual(vec3(0.0, 1.0, 2.0)); + has_equal = subgroupAllEqual(bvec4(true, true, false, true)); + + // arithmetic + vec4 added = subgroupAdd(vec4(20.0)); + ivec4 iadded = subgroupAdd(ivec4(20)); + vec4 multiplied = subgroupMul(vec4(20.0)); + ivec4 imultiplied = subgroupMul(ivec4(20)); + vec4 lo = subgroupMin(vec4(20.0)); + vec4 hi = subgroupMax(vec4(20.0)); + ivec4 slo = subgroupMin(ivec4(20)); + ivec4 shi = subgroupMax(ivec4(20)); + uvec4 ulo = subgroupMin(uvec4(20)); + uvec4 uhi = subgroupMax(uvec4(20)); + uvec4 anded = subgroupAnd(ballot_value); + uvec4 ored = subgroupOr(ballot_value); + uvec4 xored = subgroupXor(ballot_value); + + added = subgroupInclusiveAdd(added); + iadded = subgroupInclusiveAdd(iadded); + multiplied = subgroupInclusiveMul(multiplied); + imultiplied = subgroupInclusiveMul(imultiplied); + //lo = subgroupInclusiveMin(lo); // FIXME: Unsupported by Metal + //hi = subgroupInclusiveMax(hi); + //slo = subgroupInclusiveMin(slo); + //shi = subgroupInclusiveMax(shi); + //ulo = subgroupInclusiveMin(ulo); + //uhi = subgroupInclusiveMax(uhi); + //anded = subgroupInclusiveAnd(anded); + //ored = subgroupInclusiveOr(ored); + //xored = subgroupInclusiveXor(ored); + //added = subgroupExclusiveAdd(lo); + + added = subgroupExclusiveAdd(multiplied); + multiplied = subgroupExclusiveMul(multiplied); + iadded = subgroupExclusiveAdd(imultiplied); + imultiplied = subgroupExclusiveMul(imultiplied); + //lo = subgroupExclusiveMin(lo); // FIXME: Unsupported by Metal + //hi = subgroupExclusiveMax(hi); + //ulo = subgroupExclusiveMin(ulo); + //uhi = subgroupExclusiveMax(uhi); + //slo = subgroupExclusiveMin(slo); + //shi = subgroupExclusiveMax(shi); + //anded = subgroupExclusiveAnd(anded); + //ored = subgroupExclusiveOr(ored); + //xored = subgroupExclusiveXor(ored); + + // clustered + added = subgroupClusteredAdd(added, 4u); + multiplied = subgroupClusteredMul(multiplied, 4u); + iadded = subgroupClusteredAdd(iadded, 4u); + imultiplied = subgroupClusteredMul(imultiplied, 4u); + lo = subgroupClusteredMin(lo, 4u); + hi = subgroupClusteredMax(hi, 4u); + ulo = subgroupClusteredMin(ulo, 4u); + uhi = subgroupClusteredMax(uhi, 4u); + slo = subgroupClusteredMin(slo, 4u); + shi = subgroupClusteredMax(shi, 4u); + anded = subgroupClusteredAnd(anded, 4u); + ored = subgroupClusteredOr(ored, 4u); + xored = subgroupClusteredXor(xored, 4u); + + // quad + vec4 swap_horiz = subgroupQuadSwapHorizontal(vec4(20.0)); + bvec4 swap_horiz_bool = subgroupQuadSwapHorizontal(bvec4(true)); + vec4 swap_vertical = subgroupQuadSwapVertical(vec4(20.0)); + bvec4 swap_vertical_bool = subgroupQuadSwapVertical(bvec4(true)); + vec4 swap_diagonal = subgroupQuadSwapDiagonal(vec4(20.0)); + bvec4 swap_diagonal_bool = subgroupQuadSwapDiagonal(bvec4(true)); + vec4 quad_broadcast = subgroupQuadBroadcast(vec4(20.0), 3u); + bvec4 quad_broadcast_bool = subgroupQuadBroadcast(bvec4(true), 3u); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.ios.comp b/third_party/spirv-cross/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.ios.comp new file mode 100644 index 0000000..a78527f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/comp/subgroups.nocompat.invalid.vk.msl21.ios.comp @@ -0,0 +1,49 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_shuffle : require +#extension GL_KHR_shader_subgroup_shuffle_relative : require +#extension GL_KHR_shader_subgroup_quad : require +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float FragColor; +}; + +// Reduced test for functionality exposed on iOS. + +void main() +{ + // basic + FragColor = float(gl_NumSubgroups); + FragColor = float(gl_SubgroupID); + FragColor = float(gl_SubgroupSize); + FragColor = float(gl_SubgroupInvocationID); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierShared(); + subgroupMemoryBarrierImage(); + + // shuffle + uint shuffled = subgroupShuffle(10u, 8u); + bool shuffled_bool = subgroupShuffle(true, 9u); + uint shuffled_xor = subgroupShuffleXor(30u, 8u); + bool shuffled_xor_bool = subgroupShuffleXor(false, 9u); + + // shuffle relative + uint shuffled_up = subgroupShuffleUp(20u, 4u); + bool shuffled_up_bool = subgroupShuffleUp(true, 4u); + uint shuffled_down = subgroupShuffleDown(20u, 4u); + bool shuffled_down_bool = subgroupShuffleDown(false, 4u); + + // quad + vec4 swap_horiz = subgroupQuadSwapHorizontal(vec4(20.0)); + bvec4 swap_horiz_bool = subgroupQuadSwapHorizontal(bvec4(true)); + vec4 swap_vertical = subgroupQuadSwapVertical(vec4(20.0)); + bvec4 swap_vertical_bool = subgroupQuadSwapVertical(bvec4(true)); + vec4 swap_diagonal = subgroupQuadSwapDiagonal(vec4(20.0)); + bvec4 swap_diagonal_bool = subgroupQuadSwapDiagonal(bvec4(true)); + vec4 quad_broadcast = subgroupQuadBroadcast(vec4(20.0), 3u); + bvec4 quad_broadcast_bool = subgroupQuadBroadcast(bvec4(true), 3u); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-input-component.frag b/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-input-component.frag new file mode 100644 index 0000000..60d48be --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-input-component.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0, component = 3) in float Foo1; +layout(location = 0, component = 0) in vec3 Foo3; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(Foo3, Foo1); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-output-component.frag b/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-output-component.frag new file mode 100644 index 0000000..29a57df --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-output-component.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0, component = 0) out float FragColor0; +layout(location = 0, component = 1) out vec2 FragColor1; +layout(location = 0, component = 3) out float FragColor3; + +void main() +{ + FragColor0 = 1.0; + FragColor1 = vec2(2.0, 3.0); + FragColor3 = 4.0; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-output-component.pad-fragment.frag b/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-output-component.pad-fragment.frag new file mode 100644 index 0000000..ae9b7f7 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/components/fragment-output-component.pad-fragment.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0, component = 0) out float FragColor0; +layout(location = 0, component = 1) out vec2 FragColor1; + +void main() +{ + FragColor0 = 1.0; + FragColor1 = vec2(2.0, 3.0); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/components/vertex-input-component.vert b/third_party/spirv-cross/shaders-msl-no-opt/components/vertex-input-component.vert new file mode 100644 index 0000000..7ba31bf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/components/vertex-input-component.vert @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0, component = 0) in vec3 Foo3; +layout(location = 0, component = 3) in float Foo1; +layout(location = 0) out vec3 Foo; + +void main() +{ + gl_Position = vec4(Foo3, Foo1); + Foo = Foo3 + Foo1; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/components/vertex-output-component.vert b/third_party/spirv-cross/shaders-msl-no-opt/components/vertex-output-component.vert new file mode 100644 index 0000000..5abd8dc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/components/vertex-output-component.vert @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) in vec4 vFoo; +layout(location = 0) out vec3 Foo3; +layout(location = 0, component = 3) out float Foo1; + +void main() +{ + gl_Position = vFoo; + Foo3 = vFoo.xyz; + Foo1 = vFoo.w; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/16bit-constants.invalid.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/16bit-constants.invalid.frag new file mode 100644 index 0000000..c53091b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/16bit-constants.invalid.frag @@ -0,0 +1,14 @@ +#version 450 core + +#extension GL_AMD_gpu_shader_int16 : require +#extension GL_AMD_gpu_shader_half_float : require + +layout(location = 0) out float16_t foo; +layout(location = 1) out int16_t bar; +layout(location = 2) out uint16_t baz; + +void main() { + foo = 1.0hf; + bar = 2s; + baz = 3us; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/demote-to-helper.vk.nocompat.msl21.invalid.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/demote-to-helper.vk.nocompat.msl21.invalid.frag new file mode 100644 index 0000000..8cce059 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/demote-to-helper.vk.nocompat.msl21.invalid.frag @@ -0,0 +1,8 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +void main() +{ + //demote; // FIXME: Not implemented for MSL + bool helper = helperInvocationEXT(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/depth-image-gather.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/depth-image-gather.asm.frag new file mode 100644 index 0000000..430899c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/depth-image-gather.asm.frag @@ -0,0 +1,72 @@ +; SPIR-V +; Version: 1.3 +; Generator: Google spiregg; 0 +; Bound: 36 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpExtension "SPV_GOOGLE_user_type" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %psMain "main" %gl_FragCoord %in_var_TEXCOORD0 %out_var_SV_Target0 + OpExecutionMode %psMain OriginUpperLeft + OpSource HLSL 500 + OpName %type_2d_image "type.2d.image" + OpName %g_depthTexture "g_depthTexture" + OpName %type_sampler "type.sampler" + OpName %g_sampler "g_sampler" + OpName %g_comp "g_comp" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %psMain "psMain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_depthTexture DescriptorSet 0 + OpDecorate %g_depthTexture Binding 0 + OpDecorate %g_sampler DescriptorSet 0 + OpDecorate %g_sampler Binding 0 + OpDecorate %g_comp DescriptorSet 0 + OpDecorate %g_comp Binding 1 + OpDecorateString %g_depthTexture UserTypeGOOGLE "texture2d" + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v2int = OpTypeVector %int 2 + %16 = OpConstantComposite %v2int %int_0 %int_0 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %25 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image +%g_depthTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %g_comp = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %psMain = OpFunction %void None %25 + %26 = OpLabel + %27 = OpLoad %v2float %in_var_TEXCOORD0 + %28 = OpLoad %type_2d_image %g_depthTexture + %29 = OpLoad %type_sampler %g_comp + %30 = OpSampledImage %type_sampled_image %28 %29 + %31 = OpImageDrefGather %v4float %30 %27 %float_0_5 None + %32 = OpLoad %type_sampler %g_sampler + %33 = OpSampledImage %type_sampled_image %28 %32 + %34 = OpImageGather %v4float %33 %27 %int_0 ConstOffset %16 + %35 = OpFMul %v4float %31 %34 + OpStore %out_var_SV_Target0 %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/force-active-resources.msl2.argument..force-active.discrete.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/force-active-resources.msl2.argument..force-active.discrete.frag new file mode 100644 index 0000000..b1b058d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/force-active-resources.msl2.argument..force-active.discrete.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in vec2 vUV; +layout(location = 0) out vec4 FragColor; + +layout(set = 0, binding = 0) uniform sampler2D uTexture1; +layout(set = 0, binding = 1) uniform sampler2D uTexture2; +layout(set = 2, binding = 0) uniform sampler2D uTextureDiscrete1; +layout(set = 2, binding = 1) uniform sampler2D uTextureDiscrete2; + +void main() +{ + FragColor = texture(uTexture2, vUV); + FragColor += texture(uTextureDiscrete2, vUV); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/fp16.desktop.invalid.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/fp16.desktop.invalid.frag new file mode 100644 index 0000000..1e4026e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/fp16.desktop.invalid.frag @@ -0,0 +1,151 @@ +#version 450 +#extension GL_AMD_gpu_shader_half_float : require + +layout(location = 0) in float16_t v1; +layout(location = 1) in f16vec2 v2; +layout(location = 2) in f16vec3 v3; +layout(location = 3) in f16vec4 v4; + +layout(location = 0) out float o1; +layout(location = 1) out vec2 o2; +layout(location = 2) out vec3 o3; +layout(location = 3) out vec4 o4; + +f16mat2 test_mat2(f16vec2 a, f16vec2 b, f16vec2 c, f16vec2 d) +{ + return f16mat2(a, b) * f16mat2(c, d); +} + +f16mat3 test_mat3(f16vec3 a, f16vec3 b, f16vec3 c, f16vec3 d, f16vec3 e, f16vec3 f) +{ + return f16mat3(a, b, c) * f16mat3(d, e, f); +} + +void test_constants() +{ + float16_t a = 1.0hf; + float16_t b = 1.5hf; + float16_t c = -1.5hf; // Negatives + float16_t d = (0.0hf / 0.0hf); // NaN + float16_t e = (1.0hf / 0.0hf); // +Inf + float16_t f = (-1.0hf / 0.0hf); // -Inf + float16_t g = 1014.0hf; // Large. + float16_t h = 0.000001hf; // Denormal +} + +float16_t test_result() +{ + return 1.0hf; +} + +void test_conversions() +{ + float16_t one = test_result(); + int a = int(one); + uint b = uint(one); + bool c = bool(one); + float d = float(one); + //double e = double(one); + float16_t a2 = float16_t(a); + float16_t b2 = float16_t(b); + float16_t c2 = float16_t(c); + float16_t d2 = float16_t(d); + //float16_t e2 = float16_t(e); +} + +void test_builtins() +{ + f16vec4 res; + res = radians(v4); + res = degrees(v4); + res = sin(v4); + res = cos(v4); + res = tan(v4); + res = asin(v4); + res = atan(v4, v3.xyzz); + res = atan(v4); + res = sinh(v4); + res = cosh(v4); + res = tanh(v4); + res = asinh(v4); + res = acosh(v4); + res = atanh(v4); + res = pow(v4, v4); + res = exp(v4); + res = log(v4); + res = exp2(v4); + res = log2(v4); + res = sqrt(v4); + res = inversesqrt(v4); + res = abs(v4); + res = sign(v4); + res = floor(v4); + res = trunc(v4); + res = round(v4); + res = roundEven(v4); + res = ceil(v4); + res = fract(v4); + res = mod(v4, v4); + f16vec4 tmp; + res = modf(v4, tmp); + res = min(v4, v4); + res = max(v4, v4); + res = clamp(v4, v4, v4); + res = mix(v4, v4, v4); + res = mix(v4, v4, lessThan(v4, v4)); + res = step(v4, v4); + res = smoothstep(v4, v4, v4); + + bvec4 btmp = isnan(v4); + btmp = isinf(v4); + res = fma(v4, v4, v4); + + ivec4 itmp; + res = frexp(v4, itmp); + res = ldexp(res, itmp); + + uint pack0 = packFloat2x16(v4.xy); + uint pack1 = packFloat2x16(v4.zw); + res = f16vec4(unpackFloat2x16(pack0), unpackFloat2x16(pack1)); + + float16_t t0 = length(v4); + t0 = distance(v4, v4); + t0 = dot(v4, v4); + f16vec3 res3 = cross(v3, v3); + res = normalize(v4); + res = faceforward(v4, v4, v4); + res = reflect(v4, v4); + res = refract(v4, v4, v1); + + btmp = lessThan(v4, v4); + btmp = lessThanEqual(v4, v4); + btmp = greaterThan(v4, v4); + btmp = greaterThanEqual(v4, v4); + btmp = equal(v4, v4); + btmp = notEqual(v4, v4); + + res = dFdx(v4); + res = dFdy(v4); + res = dFdxFine(v4); + res = dFdyFine(v4); + res = dFdxCoarse(v4); + res = dFdyCoarse(v4); + res = fwidth(v4); + res = fwidthFine(v4); + res = fwidthCoarse(v4); + + //res = interpolateAtCentroid(v4); + //res = interpolateAtSample(v4, 0); + //res = interpolateAtOffset(v4, f16vec2(0.1hf)); +} + +void main() +{ + // Basic matrix tests. + f16mat2 m0 = test_mat2(v2, v2, v3.xy, v3.xy); + f16mat3 m1 = test_mat3(v3, v3, v3, v4.xyz, v4.xyz, v4.yzw); + + test_constants(); + test_conversions(); + test_builtins(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/image-gather.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/image-gather.frag new file mode 100644 index 0000000..b492cfb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/image-gather.frag @@ -0,0 +1,14 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +layout(set = 0, binding = 0) uniform sampler2D uSamp; +layout(set = 0, binding = 1) uniform sampler2DShadow uSampShadow; +layout(location = 0) in vec3 vUV; + +void main() +{ + FragColor = textureGather(uSamp, vUV.xy, 0); + FragColor += textureGather(uSamp, vUV.xy, 1); + FragColor += textureGather(uSampShadow, vUV.xy, vUV.z); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/in_block_assign.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/in_block_assign.frag new file mode 100644 index 0000000..760a3ba --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/in_block_assign.frag @@ -0,0 +1,16 @@ +#version 450 + +struct VOUT +{ + vec4 a; +}; + +layout(location = 0) in VOUT Clip; +layout(location = 0) out vec4 FragColor; + +void main() +{ + VOUT tmp = Clip; + tmp.a += 1.0; + FragColor = tmp.a; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/min-max-clamp.invalid.asm.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/min-max-clamp.invalid.asm.frag new file mode 100644 index 0000000..ad56661 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/min-max-clamp.invalid.asm.frag @@ -0,0 +1,293 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 205 +; Schema: 0 + OpCapability Shader + OpCapability Float16 + OpExtension "SPV_AMD_gpu_shader_half_float" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %v1 %v2 %v3 %v4 %h1 %h2 %h3 %h4 + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_AMD_gpu_shader_half_float" + OpName %main "main" + OpName %res "res" + OpName %res2 "res2" + OpName %res3 "res3" + OpName %res4 "res4" + OpName %hres "hres" + OpName %hres2 "hres2" + OpName %hres3 "hres3" + OpName %hres4 "hres4" + OpName %v1 "v1" + OpName %v2 "v2" + OpName %v3 "v3" + OpName %v4 "v4" + OpName %h1 "h1" + OpName %h2 "h2" + OpName %h3 "h3" + OpName %h4 "h4" + OpDecorate %v1 Location 0 + OpDecorate %v2 Location 1 + OpDecorate %v3 Location 2 + OpDecorate %v4 Location 3 + OpDecorate %h1 Location 4 + OpDecorate %h2 Location 5 + OpDecorate %h3 Location 6 + OpDecorate %h4 Location 7 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %half = OpTypeFloat 16 + %v2half = OpTypeVector %half 2 + %v3half = OpTypeVector %half 3 + %v4half = OpTypeVector %half 4 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Function_v3float = OpTypePointer Function %v3float +%_ptr_Input_v3float = OpTypePointer Input %v3float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Function_half = OpTypePointer Function %half +%_ptr_Input_half = OpTypePointer Input %half +%_ptr_Function_v2half = OpTypePointer Function %v2half +%_ptr_Input_v2half = OpTypePointer Input %v2half +%_ptr_Function_v3half = OpTypePointer Function %v3half +%_ptr_Input_v3half = OpTypePointer Input %v3half +%_ptr_Function_v4half = OpTypePointer Function %v4half +%_ptr_Input_v4half = OpTypePointer Input %v4half + %v1 = OpVariable %_ptr_Input_float Input + %v2 = OpVariable %_ptr_Input_v2float Input + %v3 = OpVariable %_ptr_Input_v3float Input + %v4 = OpVariable %_ptr_Input_v4float Input + %h1 = OpVariable %_ptr_Input_half Input + %h2 = OpVariable %_ptr_Input_v2half Input + %h3 = OpVariable %_ptr_Input_v3half Input + %h4 = OpVariable %_ptr_Input_v4half Input + %main = OpFunction %void None %3 + %5 = OpLabel + %res = OpVariable %_ptr_Function_float Function + %46 = OpLoad %float %v1 + %47 = OpLoad %float %v1 + %48 = OpExtInst %float %1 FMin %46 %47 + OpStore %res %48 + %49 = OpLoad %float %v1 + %50 = OpLoad %float %v1 + %51 = OpExtInst %float %1 FMax %49 %50 + OpStore %res %51 + %52 = OpLoad %float %v1 + %53 = OpLoad %float %v1 + %54 = OpLoad %float %v1 + %55 = OpExtInst %float %1 FClamp %52 %53 %54 + OpStore %res %55 + %56 = OpLoad %float %v1 + %57 = OpLoad %float %v1 + %58 = OpExtInst %float %1 NMin %56 %57 + OpStore %res %58 + %59 = OpLoad %float %v1 + %60 = OpLoad %float %v1 + %61 = OpExtInst %float %1 NMax %59 %60 + OpStore %res %61 + %62 = OpLoad %float %v1 + %63 = OpLoad %float %v1 + %64 = OpLoad %float %v1 + %65 = OpExtInst %float %1 NClamp %62 %63 %64 + OpStore %res %65 + %res2 = OpVariable %_ptr_Function_v2float Function + %66 = OpLoad %v2float %v2 + %67 = OpLoad %v2float %v2 + %68 = OpExtInst %v2float %1 FMin %66 %67 + OpStore %res2 %68 + %69 = OpLoad %v2float %v2 + %70 = OpLoad %v2float %v2 + %71 = OpExtInst %v2float %1 FMax %69 %70 + OpStore %res2 %71 + %72 = OpLoad %v2float %v2 + %73 = OpLoad %v2float %v2 + %74 = OpLoad %v2float %v2 + %75 = OpExtInst %v2float %1 FClamp %72 %73 %74 + OpStore %res2 %75 + %76 = OpLoad %v2float %v2 + %77 = OpLoad %v2float %v2 + %78 = OpExtInst %v2float %1 NMin %76 %77 + OpStore %res2 %78 + %79 = OpLoad %v2float %v2 + %80 = OpLoad %v2float %v2 + %81 = OpExtInst %v2float %1 NMax %79 %80 + OpStore %res2 %81 + %82 = OpLoad %v2float %v2 + %83 = OpLoad %v2float %v2 + %84 = OpLoad %v2float %v2 + %85 = OpExtInst %v2float %1 NClamp %82 %83 %84 + OpStore %res2 %85 + %res3 = OpVariable %_ptr_Function_v3float Function + %86 = OpLoad %v3float %v3 + %87 = OpLoad %v3float %v3 + %88 = OpExtInst %v3float %1 FMin %86 %87 + OpStore %res3 %88 + %89 = OpLoad %v3float %v3 + %90 = OpLoad %v3float %v3 + %91 = OpExtInst %v3float %1 FMax %89 %90 + OpStore %res3 %91 + %92 = OpLoad %v3float %v3 + %93 = OpLoad %v3float %v3 + %94 = OpLoad %v3float %v3 + %95 = OpExtInst %v3float %1 FClamp %92 %93 %94 + OpStore %res3 %95 + %96 = OpLoad %v3float %v3 + %97 = OpLoad %v3float %v3 + %98 = OpExtInst %v3float %1 NMin %96 %97 + OpStore %res3 %98 + %99 = OpLoad %v3float %v3 + %100 = OpLoad %v3float %v3 + %101 = OpExtInst %v3float %1 NMax %99 %100 + OpStore %res3 %101 + %102 = OpLoad %v3float %v3 + %103 = OpLoad %v3float %v3 + %104 = OpLoad %v3float %v3 + %105 = OpExtInst %v3float %1 NClamp %102 %103 %104 + OpStore %res3 %105 + %res4 = OpVariable %_ptr_Function_v4float Function + %106 = OpLoad %v4float %v4 + %107 = OpLoad %v4float %v4 + %108 = OpExtInst %v4float %1 FMin %106 %107 + OpStore %res4 %108 + %109 = OpLoad %v4float %v4 + %110 = OpLoad %v4float %v4 + %111 = OpExtInst %v4float %1 FMax %109 %110 + OpStore %res4 %111 + %112 = OpLoad %v4float %v4 + %113 = OpLoad %v4float %v4 + %114 = OpLoad %v4float %v4 + %115 = OpExtInst %v4float %1 FClamp %112 %113 %114 + OpStore %res4 %115 + %116 = OpLoad %v4float %v4 + %117 = OpLoad %v4float %v4 + %118 = OpExtInst %v4float %1 NMin %116 %117 + OpStore %res4 %118 + %119 = OpLoad %v4float %v4 + %120 = OpLoad %v4float %v4 + %121 = OpExtInst %v4float %1 NMax %119 %120 + OpStore %res4 %121 + %122 = OpLoad %v4float %v4 + %123 = OpLoad %v4float %v4 + %124 = OpLoad %v4float %v4 + %125 = OpExtInst %v4float %1 NClamp %122 %123 %124 + OpStore %res4 %125 + %hres = OpVariable %_ptr_Function_half Function + %126 = OpLoad %half %h1 + %127 = OpLoad %half %h1 + %128 = OpExtInst %half %1 FMin %126 %127 + OpStore %hres %128 + %129 = OpLoad %half %h1 + %130 = OpLoad %half %h1 + %131 = OpExtInst %half %1 FMax %129 %130 + OpStore %hres %131 + %132 = OpLoad %half %h1 + %133 = OpLoad %half %h1 + %134 = OpLoad %half %h1 + %135 = OpExtInst %half %1 FClamp %132 %133 %134 + OpStore %hres %135 + %136 = OpLoad %half %h1 + %137 = OpLoad %half %h1 + %138 = OpExtInst %half %1 NMin %136 %137 + OpStore %hres %138 + %139 = OpLoad %half %h1 + %140 = OpLoad %half %h1 + %141 = OpExtInst %half %1 NMax %139 %140 + OpStore %hres %141 + %142 = OpLoad %half %h1 + %143 = OpLoad %half %h1 + %144 = OpLoad %half %h1 + %145 = OpExtInst %half %1 NClamp %142 %143 %144 + OpStore %hres %145 + %hres2 = OpVariable %_ptr_Function_v2half Function + %146 = OpLoad %v2half %h2 + %147 = OpLoad %v2half %h2 + %148 = OpExtInst %v2half %1 FMin %146 %147 + OpStore %hres2 %148 + %149 = OpLoad %v2half %h2 + %150 = OpLoad %v2half %h2 + %151 = OpExtInst %v2half %1 FMax %149 %150 + OpStore %hres2 %151 + %152 = OpLoad %v2half %h2 + %153 = OpLoad %v2half %h2 + %154 = OpLoad %v2half %h2 + %155 = OpExtInst %v2half %1 FClamp %152 %153 %154 + OpStore %hres2 %155 + %156 = OpLoad %v2half %h2 + %157 = OpLoad %v2half %h2 + %158 = OpExtInst %v2half %1 NMin %156 %157 + OpStore %hres2 %158 + %159 = OpLoad %v2half %h2 + %160 = OpLoad %v2half %h2 + %161 = OpExtInst %v2half %1 NMax %159 %160 + OpStore %hres2 %161 + %162 = OpLoad %v2half %h2 + %163 = OpLoad %v2half %h2 + %164 = OpLoad %v2half %h2 + %165 = OpExtInst %v2half %1 NClamp %162 %163 %164 + OpStore %hres2 %165 + %hres3 = OpVariable %_ptr_Function_v3half Function + %166 = OpLoad %v3half %h3 + %167 = OpLoad %v3half %h3 + %168 = OpExtInst %v3half %1 FMin %166 %167 + OpStore %hres3 %168 + %169 = OpLoad %v3half %h3 + %170 = OpLoad %v3half %h3 + %171 = OpExtInst %v3half %1 FMax %169 %170 + OpStore %hres3 %171 + %172 = OpLoad %v3half %h3 + %173 = OpLoad %v3half %h3 + %174 = OpLoad %v3half %h3 + %175 = OpExtInst %v3half %1 FClamp %172 %173 %174 + OpStore %hres3 %175 + %176 = OpLoad %v3half %h3 + %177 = OpLoad %v3half %h3 + %178 = OpExtInst %v3half %1 NMin %176 %177 + OpStore %hres3 %178 + %179 = OpLoad %v3half %h3 + %180 = OpLoad %v3half %h3 + %181 = OpExtInst %v3half %1 NMax %179 %180 + OpStore %hres3 %181 + %182 = OpLoad %v3half %h3 + %183 = OpLoad %v3half %h3 + %184 = OpLoad %v3half %h3 + %185 = OpExtInst %v3half %1 NClamp %182 %183 %184 + OpStore %hres3 %185 + %hres4 = OpVariable %_ptr_Function_v4half Function + %186 = OpLoad %v4half %h4 + %187 = OpLoad %v4half %h4 + %188 = OpExtInst %v4half %1 FMin %186 %187 + OpStore %hres4 %188 + %189 = OpLoad %v4half %h4 + %190 = OpLoad %v4half %h4 + %191 = OpExtInst %v4half %1 FMax %189 %190 + OpStore %hres4 %191 + %192 = OpLoad %v4half %h4 + %193 = OpLoad %v4half %h4 + %194 = OpLoad %v4half %h4 + %195 = OpExtInst %v4half %1 FClamp %192 %193 %194 + OpStore %hres4 %195 + %196 = OpLoad %v4half %h4 + %197 = OpLoad %v4half %h4 + %198 = OpExtInst %v4half %1 NMin %196 %197 + OpStore %hres4 %198 + %199 = OpLoad %v4half %h4 + %200 = OpLoad %v4half %h4 + %201 = OpExtInst %v4half %1 NMax %199 %200 + OpStore %hres4 %201 + %202 = OpLoad %v4half %h4 + %203 = OpLoad %v4half %h4 + %204 = OpLoad %v4half %h4 + %205 = OpExtInst %v4half %1 NClamp %202 %203 %204 + OpStore %hres4 %205 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/pixel-interlock-simple-callstack.msl2.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/pixel-interlock-simple-callstack.msl2.frag new file mode 100644 index 0000000..59079fe --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/pixel-interlock-simple-callstack.msl2.frag @@ -0,0 +1,31 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(set = 0, binding = 0, std430) buffer SSBO0 +{ + uint values0[]; +}; + +layout(set = 0, binding = 1, std430) buffer SSBO1 +{ + uint values1[]; +}; + +void callee2() +{ + values1[int(gl_FragCoord.x)] += 1; +} + +void callee() +{ + values0[int(gl_FragCoord.x)] += 1; + callee2(); +} + +void main() +{ + beginInvocationInterlockARB(); + callee(); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag new file mode 100644 index 0000000..56e809c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require + +layout(std430, binding = 0) uniform UBO +{ + float a[1]; + vec2 b[2]; +}; + +layout(std430, binding = 1) uniform UBOEnhancedLayout +{ + float c[1]; + vec2 d[2]; + layout(offset = 10000) float e; +}; + +layout(location = 0) flat in int vIndex; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = a[vIndex] + c[vIndex] + e; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/shadow-compare-global-alias.invalid.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/shadow-compare-global-alias.invalid.frag new file mode 100644 index 0000000..d885a78 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/shadow-compare-global-alias.invalid.frag @@ -0,0 +1,38 @@ +#version 450 + +layout(location = 0) out float FragColor; +layout(binding = 0) uniform sampler2DShadow uSampler; +layout(location = 0) in vec3 vUV; + +layout(binding = 1) uniform texture2D uTex; +layout(binding = 2) uniform samplerShadow uSamp; + +float Samp(vec3 uv) +{ + return texture(sampler2DShadow(uTex, uSamp), uv); +} + +float Samp2(vec3 uv) +{ + return texture(uSampler, vUV); +} + +float Samp3(texture2D uT, samplerShadow uS, vec3 uv) +{ + return texture(sampler2DShadow(uT, uS), vUV); +} + +float Samp4(sampler2DShadow uS, vec3 uv) +{ + return texture(uS, vUV); +} + +void main() +{ + FragColor = texture(uSampler, vUV); + FragColor += texture(sampler2DShadow(uTex, uSamp), vUV); + FragColor += Samp(vUV); + FragColor += Samp2(vUV); + FragColor += Samp3(uTex, uSamp, vUV); + FragColor += Samp4(uSampler, vUV); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/subgroups.nocompat.invalid.vk.msl21.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/subgroups.nocompat.invalid.vk.msl21.frag new file mode 100644 index 0000000..05aa521 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/subgroups.nocompat.invalid.vk.msl21.frag @@ -0,0 +1,131 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_ballot : require +#extension GL_KHR_shader_subgroup_vote : require +#extension GL_KHR_shader_subgroup_shuffle : require +#extension GL_KHR_shader_subgroup_shuffle_relative : require +#extension GL_KHR_shader_subgroup_arithmetic : require +#extension GL_KHR_shader_subgroup_clustered : require +#extension GL_KHR_shader_subgroup_quad : require + +layout(location = 0) out float FragColor; + +void main() +{ + // basic + FragColor = float(gl_SubgroupSize); + FragColor = float(gl_SubgroupInvocationID); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierImage(); + bool elected = subgroupElect(); + + // ballot + FragColor = float(gl_SubgroupEqMask); + FragColor = float(gl_SubgroupGeMask); + FragColor = float(gl_SubgroupGtMask); + FragColor = float(gl_SubgroupLeMask); + FragColor = float(gl_SubgroupLtMask); + vec4 broadcasted = subgroupBroadcast(vec4(10.0), 8u); + bvec2 broadcasted_bool = subgroupBroadcast(bvec2(true), 8u); + vec3 first = subgroupBroadcastFirst(vec3(20.0)); + bvec4 first_bool = subgroupBroadcastFirst(bvec4(false)); + uvec4 ballot_value = subgroupBallot(true); + bool inverse_ballot_value = subgroupInverseBallot(ballot_value); + bool bit_extracted = subgroupBallotBitExtract(uvec4(10u), 8u); + uint bit_count = subgroupBallotBitCount(ballot_value); + uint inclusive_bit_count = subgroupBallotInclusiveBitCount(ballot_value); + uint exclusive_bit_count = subgroupBallotExclusiveBitCount(ballot_value); + uint lsb = subgroupBallotFindLSB(ballot_value); + uint msb = subgroupBallotFindMSB(ballot_value); + + // shuffle + uint shuffled = subgroupShuffle(10u, 8u); + bool shuffled_bool = subgroupShuffle(true, 9u); + uint shuffled_xor = subgroupShuffleXor(30u, 8u); + bool shuffled_xor_bool = subgroupShuffleXor(false, 9u); + + // shuffle relative + uint shuffled_up = subgroupShuffleUp(20u, 4u); + bool shuffled_up_bool = subgroupShuffleUp(true, 4u); + uint shuffled_down = subgroupShuffleDown(20u, 4u); + bool shuffled_down_bool = subgroupShuffleDown(false, 4u); + + // vote + bool has_all = subgroupAll(true); + bool has_any = subgroupAny(true); + bool has_equal = subgroupAllEqual(0); + has_equal = subgroupAllEqual(true); + has_equal = subgroupAllEqual(vec3(0.0, 1.0, 2.0)); + has_equal = subgroupAllEqual(bvec4(true, true, false, true)); + + // arithmetic + vec4 added = subgroupAdd(vec4(20.0)); + ivec4 iadded = subgroupAdd(ivec4(20)); + vec4 multiplied = subgroupMul(vec4(20.0)); + ivec4 imultiplied = subgroupMul(ivec4(20)); + vec4 lo = subgroupMin(vec4(20.0)); + vec4 hi = subgroupMax(vec4(20.0)); + ivec4 slo = subgroupMin(ivec4(20)); + ivec4 shi = subgroupMax(ivec4(20)); + uvec4 ulo = subgroupMin(uvec4(20)); + uvec4 uhi = subgroupMax(uvec4(20)); + uvec4 anded = subgroupAnd(ballot_value); + uvec4 ored = subgroupOr(ballot_value); + uvec4 xored = subgroupXor(ballot_value); + + added = subgroupInclusiveAdd(added); + iadded = subgroupInclusiveAdd(iadded); + multiplied = subgroupInclusiveMul(multiplied); + imultiplied = subgroupInclusiveMul(imultiplied); + //lo = subgroupInclusiveMin(lo); // FIXME: Unsupported by Metal + //hi = subgroupInclusiveMax(hi); + //slo = subgroupInclusiveMin(slo); + //shi = subgroupInclusiveMax(shi); + //ulo = subgroupInclusiveMin(ulo); + //uhi = subgroupInclusiveMax(uhi); + //anded = subgroupInclusiveAnd(anded); + //ored = subgroupInclusiveOr(ored); + //xored = subgroupInclusiveXor(ored); + //added = subgroupExclusiveAdd(lo); + + added = subgroupExclusiveAdd(multiplied); + multiplied = subgroupExclusiveMul(multiplied); + iadded = subgroupExclusiveAdd(imultiplied); + imultiplied = subgroupExclusiveMul(imultiplied); + //lo = subgroupExclusiveMin(lo); // FIXME: Unsupported by Metal + //hi = subgroupExclusiveMax(hi); + //ulo = subgroupExclusiveMin(ulo); + //uhi = subgroupExclusiveMax(uhi); + //slo = subgroupExclusiveMin(slo); + //shi = subgroupExclusiveMax(shi); + //anded = subgroupExclusiveAnd(anded); + //ored = subgroupExclusiveOr(ored); + //xored = subgroupExclusiveXor(ored); + + // clustered + added = subgroupClusteredAdd(added, 4u); + multiplied = subgroupClusteredMul(multiplied, 4u); + iadded = subgroupClusteredAdd(iadded, 4u); + imultiplied = subgroupClusteredMul(imultiplied, 4u); + lo = subgroupClusteredMin(lo, 4u); + hi = subgroupClusteredMax(hi, 4u); + ulo = subgroupClusteredMin(ulo, 4u); + uhi = subgroupClusteredMax(uhi, 4u); + slo = subgroupClusteredMin(slo, 4u); + shi = subgroupClusteredMax(shi, 4u); + anded = subgroupClusteredAnd(anded, 4u); + ored = subgroupClusteredOr(ored, 4u); + xored = subgroupClusteredXor(xored, 4u); + + // quad + vec4 swap_horiz = subgroupQuadSwapHorizontal(vec4(20.0)); + bvec4 swap_horiz_bool = subgroupQuadSwapHorizontal(bvec4(true)); + vec4 swap_vertical = subgroupQuadSwapVertical(vec4(20.0)); + bvec4 swap_vertical_bool = subgroupQuadSwapVertical(bvec4(true)); + vec4 swap_diagonal = subgroupQuadSwapDiagonal(vec4(20.0)); + bvec4 swap_diagonal_bool = subgroupQuadSwapDiagonal(bvec4(true)); + vec4 quad_broadcast = subgroupQuadBroadcast(vec4(20.0), 3u); + bvec4 quad_broadcast_bool = subgroupQuadBroadcast(bvec4(true), 3u); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl20.ios.framebuffer-fetch.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl20.ios.framebuffer-fetch.frag new file mode 100644 index 0000000..70822ae --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl20.ios.framebuffer-fetch.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(binding = 4, input_attachment_index = 1) uniform subpassInput uInput; +layout(location = 1) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uInput); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl23.framebuffer-fetch.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl23.framebuffer-fetch.frag new file mode 100644 index 0000000..70822ae --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-attachment-index-fallback.msl23.framebuffer-fetch.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(binding = 4, input_attachment_index = 1) uniform subpassInput uInput; +layout(location = 1) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uInput); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.ios.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.ios.frag new file mode 100644 index 0000000..ef9ef77 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.ios.frag @@ -0,0 +1,24 @@ +#version 450 + +layout(set = 0, input_attachment_index = 0, binding = 0) uniform subpassInput uSub; +layout(location = 0) out vec4 FragColor; + +vec4 samp3(subpassInput uS) +{ + return subpassLoad(uS); +} + +vec4 samp2(subpassInput uS) +{ + return subpassLoad(uS) + samp3(uS); +} + +vec4 samp() +{ + return subpassLoad(uSub) + samp3(uSub); +} + +void main() +{ + FragColor = samp() + samp2(uSub); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.msl23.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.msl23.frag new file mode 100644 index 0000000..ef9ef77 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/subpass-input-function-argument.framebuffer-fetch.msl23.frag @@ -0,0 +1,24 @@ +#version 450 + +layout(set = 0, input_attachment_index = 0, binding = 0) uniform subpassInput uSub; +layout(location = 0) out vec4 FragColor; + +vec4 samp3(subpassInput uS) +{ + return subpassLoad(uS); +} + +vec4 samp2(subpassInput uS) +{ + return subpassLoad(uS) + samp3(uS); +} + +vec4 samp() +{ + return subpassLoad(uSub) + samp3(uSub); +} + +void main() +{ + FragColor = samp() + samp2(uSub); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-int.swizzle.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-int.swizzle.frag new file mode 100644 index 0000000..4b75d4f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-int.swizzle.frag @@ -0,0 +1,53 @@ +#version 450 + +layout(binding = 0) uniform isampler1D tex1d; +layout(binding = 1) uniform isampler2D tex2d; +layout(binding = 2) uniform isampler3D tex3d; +layout(binding = 3) uniform isamplerCube texCube; +layout(binding = 4) uniform isampler2DArray tex2dArray; +layout(binding = 5) uniform isamplerCubeArray texCubeArray; +layout(binding = 6) uniform isamplerBuffer texBuffer; + +void main() +{ + // OpImageSampleImplicitLod + vec4 c = texture(tex1d, 0.0); + c = texture(tex2d, vec2(0.0, 0.0)); + c = texture(tex3d, vec3(0.0, 0.0, 0.0)); + c = texture(texCube, vec3(0.0, 0.0, 0.0)); + c = texture(tex2dArray, vec3(0.0, 0.0, 0.0)); + c = texture(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0)); + + // OpImageSampleProjImplicitLod + c = textureProj(tex1d, vec2(0.0, 1.0)); + c = textureProj(tex2d, vec3(0.0, 0.0, 1.0)); + c = textureProj(tex3d, vec4(0.0, 0.0, 0.0, 1.0)); + + // OpImageSampleExplicitLod + c = textureLod(tex1d, 0.0, 0.0); + c = textureLod(tex2d, vec2(0.0, 0.0), 0.0); + c = textureLod(tex3d, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(texCube, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(tex2dArray, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 0.0); + + // OpImageSampleProjExplicitLod + c = textureProjLod(tex1d, vec2(0.0, 1.0), 0.0); + c = textureProjLod(tex2d, vec3(0.0, 0.0, 1.0), 0.0); + c = textureProjLod(tex3d, vec4(0.0, 0.0, 0.0, 1.0), 0.0); + + // OpImageFetch + c = texelFetch(tex1d, 0, 0); + c = texelFetch(tex2d, ivec2(0, 0), 0); + c = texelFetch(tex3d, ivec3(0, 0, 0), 0); + c = texelFetch(tex2dArray, ivec3(0, 0, 0), 0); + + // Show that this transformation doesn't apply to Buffer images. + c = texelFetch(texBuffer, 0); + + // OpImageGather + c = textureGather(tex2d, vec2(0.0, 0.0), 0); + c = textureGather(texCube, vec3(0.0, 0.0, 0.0), 1); + c = textureGather(tex2dArray, vec3(0.0, 0.0, 0.0), 2); + c = textureGather(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 3); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-leaf.swizzle.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-leaf.swizzle.frag new file mode 100644 index 0000000..2b7e937 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-leaf.swizzle.frag @@ -0,0 +1,86 @@ +#version 450 + +layout(binding = 0) uniform sampler1D tex1d; +layout(binding = 1) uniform sampler2D tex2d; +layout(binding = 2) uniform sampler3D tex3d; +layout(binding = 3) uniform samplerCube texCube; +layout(binding = 4) uniform sampler2DArray tex2dArray; +layout(binding = 5) uniform samplerCubeArray texCubeArray; +layout(binding = 6) uniform samplerBuffer texBuffer; + +layout(binding = 7) uniform sampler2DShadow depth2d; +layout(binding = 8) uniform samplerCubeShadow depthCube; +layout(binding = 9) uniform sampler2DArrayShadow depth2dArray; +layout(binding = 10) uniform samplerCubeArrayShadow depthCubeArray; + +vec4 doSwizzle() +{ + // OpImageSampleImplicitLod + vec4 c = texture(tex1d, 0.0); + c = texture(tex2d, vec2(0.0, 0.0)); + c = texture(tex3d, vec3(0.0, 0.0, 0.0)); + c = texture(texCube, vec3(0.0, 0.0, 0.0)); + c = texture(tex2dArray, vec3(0.0, 0.0, 0.0)); + c = texture(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0)); + + // OpImageSampleDrefImplicitLod + c.r = texture(depth2d, vec3(0.0, 0.0, 1.0)); + c.r = texture(depthCube, vec4(0.0, 0.0, 0.0, 1.0)); + c.r = texture(depth2dArray, vec4(0.0, 0.0, 0.0, 1.0)); + c.r = texture(depthCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 1.0); + + // OpImageSampleProjImplicitLod + c = textureProj(tex1d, vec2(0.0, 1.0)); + c = textureProj(tex2d, vec3(0.0, 0.0, 1.0)); + c = textureProj(tex3d, vec4(0.0, 0.0, 0.0, 1.0)); + + // OpImageSampleProjDrefImplicitLod + c.r = textureProj(depth2d, vec4(0.0, 0.0, 1.0, 1.0)); + + // OpImageSampleExplicitLod + c = textureLod(tex1d, 0.0, 0.0); + c = textureLod(tex2d, vec2(0.0, 0.0), 0.0); + c = textureLod(tex3d, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(texCube, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(tex2dArray, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 0.0); + + // OpImageSampleDrefExplicitLod + c.r = textureLod(depth2d, vec3(0.0, 0.0, 1.0), 0.0); + + // OpImageSampleProjExplicitLod + c = textureProjLod(tex1d, vec2(0.0, 1.0), 0.0); + c = textureProjLod(tex2d, vec3(0.0, 0.0, 1.0), 0.0); + c = textureProjLod(tex3d, vec4(0.0, 0.0, 0.0, 1.0), 0.0); + + // OpImageSampleProjDrefExplicitLod + c.r = textureProjLod(depth2d, vec4(0.0, 0.0, 1.0, 1.0), 0.0); + + // OpImageFetch + c = texelFetch(tex1d, 0, 0); + c = texelFetch(tex2d, ivec2(0, 0), 0); + c = texelFetch(tex3d, ivec3(0, 0, 0), 0); + c = texelFetch(tex2dArray, ivec3(0, 0, 0), 0); + + // Show that this transformation doesn't apply to Buffer images. + c = texelFetch(texBuffer, 0); + + // OpImageGather + c = textureGather(tex2d, vec2(0.0, 0.0), 0); + c = textureGather(texCube, vec3(0.0, 0.0, 0.0), 1); + c = textureGather(tex2dArray, vec3(0.0, 0.0, 0.0), 2); + c = textureGather(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 3); + + // OpImageDrefGather + c = textureGather(depth2d, vec2(0.0, 0.0), 1.0); + c = textureGather(depthCube, vec3(0.0, 0.0, 0.0), 1.0); + c = textureGather(depth2dArray, vec3(0.0, 0.0, 0.0), 1.0); + c = textureGather(depthCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 1.0); + + return c; +} + +void main() +{ + vec4 c = doSwizzle(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-uint.swizzle.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-uint.swizzle.frag new file mode 100644 index 0000000..5f0acf6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access-uint.swizzle.frag @@ -0,0 +1,53 @@ +#version 450 + +layout(binding = 0) uniform usampler1D tex1d; +layout(binding = 1) uniform usampler2D tex2d; +layout(binding = 2) uniform usampler3D tex3d; +layout(binding = 3) uniform usamplerCube texCube; +layout(binding = 4) uniform usampler2DArray tex2dArray; +layout(binding = 5) uniform usamplerCubeArray texCubeArray; +layout(binding = 6) uniform usamplerBuffer texBuffer; + +void main() +{ + // OpImageSampleImplicitLod + vec4 c = texture(tex1d, 0.0); + c = texture(tex2d, vec2(0.0, 0.0)); + c = texture(tex3d, vec3(0.0, 0.0, 0.0)); + c = texture(texCube, vec3(0.0, 0.0, 0.0)); + c = texture(tex2dArray, vec3(0.0, 0.0, 0.0)); + c = texture(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0)); + + // OpImageSampleProjImplicitLod + c = textureProj(tex1d, vec2(0.0, 1.0)); + c = textureProj(tex2d, vec3(0.0, 0.0, 1.0)); + c = textureProj(tex3d, vec4(0.0, 0.0, 0.0, 1.0)); + + // OpImageSampleExplicitLod + c = textureLod(tex1d, 0.0, 0.0); + c = textureLod(tex2d, vec2(0.0, 0.0), 0.0); + c = textureLod(tex3d, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(texCube, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(tex2dArray, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 0.0); + + // OpImageSampleProjExplicitLod + c = textureProjLod(tex1d, vec2(0.0, 1.0), 0.0); + c = textureProjLod(tex2d, vec3(0.0, 0.0, 1.0), 0.0); + c = textureProjLod(tex3d, vec4(0.0, 0.0, 0.0, 1.0), 0.0); + + // OpImageFetch + c = texelFetch(tex1d, 0, 0); + c = texelFetch(tex2d, ivec2(0, 0), 0); + c = texelFetch(tex3d, ivec3(0, 0, 0), 0); + c = texelFetch(tex2dArray, ivec3(0, 0, 0), 0); + + // Show that this transformation doesn't apply to Buffer images. + c = texelFetch(texBuffer, 0); + + // OpImageGather + c = textureGather(tex2d, vec2(0.0, 0.0), 0); + c = textureGather(texCube, vec3(0.0, 0.0, 0.0), 1); + c = textureGather(tex2dArray, vec3(0.0, 0.0, 0.0), 2); + c = textureGather(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 3); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access.swizzle.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access.swizzle.frag new file mode 100644 index 0000000..b09ebed --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/texture-access.swizzle.frag @@ -0,0 +1,79 @@ +#version 450 + +layout(binding = 0) uniform sampler1D tex1d; +layout(binding = 1) uniform sampler2D tex2d; +layout(binding = 2) uniform sampler3D tex3d; +layout(binding = 3) uniform samplerCube texCube; +layout(binding = 4) uniform sampler2DArray tex2dArray; +layout(binding = 5) uniform samplerCubeArray texCubeArray; +layout(binding = 6) uniform samplerBuffer texBuffer; + +layout(binding = 7) uniform sampler2DShadow depth2d; +layout(binding = 8) uniform samplerCubeShadow depthCube; +layout(binding = 9) uniform sampler2DArrayShadow depth2dArray; +layout(binding = 10) uniform samplerCubeArrayShadow depthCubeArray; + +void main() +{ + // OpImageSampleImplicitLod + vec4 c = texture(tex1d, 0.0); + c = texture(tex2d, vec2(0.0, 0.0)); + c = texture(tex3d, vec3(0.0, 0.0, 0.0)); + c = texture(texCube, vec3(0.0, 0.0, 0.0)); + c = texture(tex2dArray, vec3(0.0, 0.0, 0.0)); + c = texture(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0)); + + // OpImageSampleDrefImplicitLod + c.r = texture(depth2d, vec3(0.0, 0.0, 1.0)); + c.r = texture(depthCube, vec4(0.0, 0.0, 0.0, 1.0)); + c.r = texture(depth2dArray, vec4(0.0, 0.0, 0.0, 1.0)); + c.r = texture(depthCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 1.0); + + // OpImageSampleProjImplicitLod + c = textureProj(tex1d, vec2(0.0, 1.0)); + c = textureProj(tex2d, vec3(0.0, 0.0, 1.0)); + c = textureProj(tex3d, vec4(0.0, 0.0, 0.0, 1.0)); + + // OpImageSampleProjDrefImplicitLod + c.r = textureProj(depth2d, vec4(0.0, 0.0, 1.0, 1.0)); + + // OpImageSampleExplicitLod + c = textureLod(tex1d, 0.0, 0.0); + c = textureLod(tex2d, vec2(0.0, 0.0), 0.0); + c = textureLod(tex3d, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(texCube, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(tex2dArray, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 0.0); + + // OpImageSampleDrefExplicitLod + c.r = textureLod(depth2d, vec3(0.0, 0.0, 1.0), 0.0); + + // OpImageSampleProjExplicitLod + c = textureProjLod(tex1d, vec2(0.0, 1.0), 0.0); + c = textureProjLod(tex2d, vec3(0.0, 0.0, 1.0), 0.0); + c = textureProjLod(tex3d, vec4(0.0, 0.0, 0.0, 1.0), 0.0); + + // OpImageSampleProjDrefExplicitLod + c.r = textureProjLod(depth2d, vec4(0.0, 0.0, 1.0, 1.0), 0.0); + + // OpImageFetch + c = texelFetch(tex1d, 0, 0); + c = texelFetch(tex2d, ivec2(0, 0), 0); + c = texelFetch(tex3d, ivec3(0, 0, 0), 0); + c = texelFetch(tex2dArray, ivec3(0, 0, 0), 0); + + // Show that this transformation doesn't apply to Buffer images. + c = texelFetch(texBuffer, 0); + + // OpImageGather + c = textureGather(tex2d, vec2(0.0, 0.0), 0); + c = textureGather(texCube, vec3(0.0, 0.0, 0.0), 1); + c = textureGather(tex2dArray, vec3(0.0, 0.0, 0.0), 2); + c = textureGather(texCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 3); + + // OpImageDrefGather + c = textureGather(depth2d, vec2(0.0, 0.0), 1.0); + c = textureGather(depthCube, vec3(0.0, 0.0, 0.0), 1.0); + c = textureGather(depth2dArray, vec3(0.0, 0.0, 0.0), 1.0); + c = textureGather(depthCubeArray, vec4(0.0, 0.0, 0.0, 0.0), 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.argument.msl2.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.argument.msl2.frag new file mode 100644 index 0000000..f3cf0e1 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.argument.msl2.frag @@ -0,0 +1,18 @@ +#version 450 + +struct Foo +{ + vec4 v; +}; + +layout(set = 0, binding = 0) uniform UBO +{ + Foo foo; +} ubos[2]; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = ubos[1].foo.v; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.frag new file mode 100644 index 0000000..f3cf0e1 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-array-multiple-structs-access-chain.frag @@ -0,0 +1,18 @@ +#version 450 + +struct Foo +{ + vec4 v; +}; + +layout(set = 0, binding = 0) uniform UBO +{ + Foo foo; +} ubos[2]; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = ubos[1].foo.v; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-offset-out-of-order.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-offset-out-of-order.frag new file mode 100644 index 0000000..7776052 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/ubo-offset-out-of-order.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +layout(set = 0, binding = 0) uniform UBO +{ + layout(offset = 16) mat4 m; + layout(offset = 0) vec4 v; +}; + +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = m * vColor + v; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/frag/variables.zero-initialize.frag b/third_party/spirv-cross/shaders-msl-no-opt/frag/variables.zero-initialize.frag new file mode 100644 index 0000000..41da800 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/frag/variables.zero-initialize.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +int uninit_int; +ivec4 uninit_vector; +mat4 uninit_matrix; + +struct Foo { int a; }; +Foo uninit_foo; + +void main() +{ + int uninit_function_int; + if (vColor.x > 10.0) + uninit_function_int = 10; + else + uninit_function_int = 20; + FragColor = vColor; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/array-of-vec3.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/array-of-vec3.comp new file mode 100644 index 0000000..6157212 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/array-of-vec3.comp @@ -0,0 +1,13 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, scalar) buffer SSBO +{ + vec3 v[16]; +}; + +void main() +{ + v[1] = v[0]; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/array-of-vec4.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/array-of-vec4.comp new file mode 100644 index 0000000..c5bf5e8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/array-of-vec4.comp @@ -0,0 +1,13 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, scalar) buffer SSBO +{ + vec4 v[16]; +}; + +void main() +{ + v[1] = v[0]; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/isolated-scalar-access.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/isolated-scalar-access.comp new file mode 100644 index 0000000..32c9288 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/isolated-scalar-access.comp @@ -0,0 +1,25 @@ +#version 450 + +layout(set = 0, binding = 0) buffer SSBO +{ + vec4 v; + mat4 cm; + layout(row_major) mat4 rm; + + vec3 v3; + float f; +}; + +shared vec4 shared_vec4; +shared vec3 shared_vec3; + +void main() +{ + v.x = 10.0; + v3.y = 40.0; + cm[1][2] = 20.0; + rm[3][1] = 30.0; + + shared_vec4.z = 40.0; + shared_vec3.y = 1.0; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/load-store-col-rows.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/load-store-col-rows.comp new file mode 100644 index 0000000..b3f2897 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/load-store-col-rows.comp @@ -0,0 +1,59 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +layout(binding = 0, std140) buffer SSBO1 +{ + mat2 a; + layout(row_major) mat2 a2; +}; + +layout(scalar, binding = 1) buffer SSBO2 +{ + mat2x3 b; + layout(row_major) mat3x2 b2; +}; + +void load_store_column() +{ + vec2 u = a[0]; + vec2 v = a[1]; + u += v; + a[0] = u; + a[1] = v; +} + +void load_store_row() +{ + vec2 u = a2[0]; + vec2 v = a2[1]; + u += v; + a2[0] = u; + a2[1] = v; +} + +void load_store_packed_column() +{ + vec3 u = b[0]; + vec3 v = b[1]; + u += v; + b[0] = u; + b[1] = v; +} + +void load_store_packed_row() +{ + vec2 u = b2[0]; + vec2 v = b2[1]; + u += v; + b2[0] = u; + b2[1] = v; +} + +void main() +{ + load_store_column(); + load_store_row(); + load_store_packed_column(); + load_store_packed_row(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-scalar.comp new file mode 100644 index 0000000..6a94c86 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat2 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-std140.comp new file mode 100644 index 0000000..3940e5c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat2 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-std430.comp new file mode 100644 index 0000000..342c398 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x2-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat2 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-scalar.comp new file mode 100644 index 0000000..cf40f89 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat2x3 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-std140.comp new file mode 100644 index 0000000..6fbe149 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat2x3 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-std430.comp new file mode 100644 index 0000000..36a6bab --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x3-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat2x3 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-scalar.comp new file mode 100644 index 0000000..70fa474 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat2x4 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-std140.comp new file mode 100644 index 0000000..6c5d06f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat2x4 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-std430.comp new file mode 100644 index 0000000..177b966 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-2x4-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat2x4 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-scalar.comp new file mode 100644 index 0000000..296efa6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat3x2 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-std140.comp new file mode 100644 index 0000000..1334c4e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat3x2 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-std430.comp new file mode 100644 index 0000000..fe82993 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x2-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat3x2 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-scalar.comp new file mode 100644 index 0000000..0741384 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat3 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-std140.comp new file mode 100644 index 0000000..0de5d59 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat3 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-std430.comp new file mode 100644 index 0000000..8e48109 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x3-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat3 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-scalar.comp new file mode 100644 index 0000000..23297d5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat3x4 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-std140.comp new file mode 100644 index 0000000..11135ee --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat3x4 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-std430.comp new file mode 100644 index 0000000..78c577f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-3x4-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat3x4 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-scalar.comp new file mode 100644 index 0000000..412c208 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat4x2 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-std140.comp new file mode 100644 index 0000000..e130cb0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat4x2 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-std430.comp new file mode 100644 index 0000000..76aa9ae --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x2-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat4x2 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-scalar.comp new file mode 100644 index 0000000..8468b28 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat4x3 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-std140.comp new file mode 100644 index 0000000..8223eae --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat4x3 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-std430.comp new file mode 100644 index 0000000..aa4d685 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x3-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat4x3 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-scalar.comp new file mode 100644 index 0000000..6f14c07 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-scalar.comp @@ -0,0 +1,86 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +#define T mat4 +#define PACKING scalar + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-std140.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-std140.comp new file mode 100644 index 0000000..45193b3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-std140.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat4 +#define PACKING std140 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-std430.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-std430.comp new file mode 100644 index 0000000..3a1eb9f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-4x4-std430.comp @@ -0,0 +1,85 @@ +#version 450 +layout(local_size_x = 1) in; + +#define T mat4 +#define PACKING std430 + +layout(set = 0, binding = 0, PACKING) buffer SSBOCol +{ + layout(column_major) T col_major0; + layout(column_major) T col_major1; +}; + +layout(set = 0, binding = 1, PACKING) buffer SSBORow +{ + layout(row_major) T row_major0; + layout(row_major) T row_major1; +}; + +void load_store_to_variable_col_major() +{ + // Load to variable. + T loaded = col_major0; + + // Store from variable. + col_major1 = loaded; +} + +void load_store_to_variable_row_major() +{ + // Load to variable. + T loaded = row_major0; + + // Store to variable. + row_major0 = loaded; +} + +void copy_col_major_to_col_major() +{ + // Copy col -> col + col_major0 = col_major1; +} + +void copy_row_major_to_col_major() +{ + // Copy row -> col + col_major0 = row_major0; +} + +void copy_col_major_to_row_major() +{ + // Copy col -> row + row_major0 = col_major0; +} + +void copy_row_major_to_row_major() +{ + // Copy row -> row + row_major0 = row_major1; +} + +void copy_columns() +{ + // Copy columns/rows. + col_major0[1] = row_major0[1]; + row_major0[1] = col_major0[1]; +} + +void copy_elements() +{ + // Copy individual elements. + col_major0[0][1] = row_major0[0][1]; + row_major0[0][1] = col_major0[0][1]; +} + +void main() +{ + load_store_to_variable_col_major(); + load_store_to_variable_row_major(); + copy_col_major_to_col_major(); + copy_col_major_to_row_major(); + copy_row_major_to_col_major(); + copy_row_major_to_row_major(); + copy_columns(); + copy_elements(); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-row-major.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-row-major.comp new file mode 100644 index 0000000..9b7b9fc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-row-major.comp @@ -0,0 +1,16 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(row_major, set = 0, binding = 0) buffer SSBO +{ + mat3 m0; + mat3 m1; + vec3 v0; + vec3 v1; +}; + +void main() +{ + v0 = (m0 * m1) * v1; + v0 = m0 * (m1 * v1); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major-2.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major-2.comp new file mode 100644 index 0000000..cd77d24 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major-2.comp @@ -0,0 +1,19 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +layout(scalar, set = 0, binding = 0) buffer SSBO +{ + mat3 m0; + mat3 m1; + vec3 v0; + vec3 v1; +}; + +void main() +{ + v0 = (m0 * m1) * v1; + v0 = m0 * (m1 * v1); + v0 = (v1 * m0) * m1; + v0 = v1 * (m0 * m1); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major.comp new file mode 100644 index 0000000..847d2e8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-col-major.comp @@ -0,0 +1,18 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std140, set = 0, binding = 0) buffer SSBO +{ + mat2 m0; + mat2 m1; + vec2 v0; + vec2 v1; +}; + +void main() +{ + v0 = (m0 * m1) * v1; + v0 = m0 * (m1 * v1); + v0 = (v1 * m0) * m1; + v0 = v1 * (m0 * m1); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major-2.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major-2.comp new file mode 100644 index 0000000..60a3da0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major-2.comp @@ -0,0 +1,19 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +layout(scalar, row_major, set = 0, binding = 0) buffer SSBO +{ + mat3 m0; + mat3 m1; + vec3 v0; + vec3 v1; +}; + +void main() +{ + v0 = (m0 * m1) * v1; + v0 = m0 * (m1 * v1); + v0 = (v1 * m0) * m1; + v0 = v1 * (m0 * m1); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major.comp new file mode 100644 index 0000000..5b71ae9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/matrix-multiply-unpacked-row-major.comp @@ -0,0 +1,18 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std140, row_major, set = 0, binding = 0) buffer SSBO +{ + mat2 m0; + mat2 m1; + vec2 v0; + vec2 v1; +}; + +void main() +{ + v0 = (m0 * m1) * v1; + v0 = m0 * (m1 * v1); + v0 = (v1 * m0) * m1; + v0 = v1 * (m0 * m1); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/member-padding.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/member-padding.comp new file mode 100644 index 0000000..a413662 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/member-padding.comp @@ -0,0 +1,14 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std140, set = 0, binding = 0) buffer SSBO +{ + layout(offset = 16) float a; + layout(offset = 40) float b; +}; + +void main() +{ + a = 10.0; + b = 20.0; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/std140-array-of-vectors.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/std140-array-of-vectors.comp new file mode 100644 index 0000000..260a498 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/std140-array-of-vectors.comp @@ -0,0 +1,47 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std140, set = 0, binding = 0) buffer SSBO +{ + float v1[4]; + vec2 v2[4]; + vec3 v3[4]; + vec4 v4[4]; + + float v1_array_of_array[4][4]; + vec2 v2_array_of_array[4][4]; + vec3 v3_array_of_array[4][4]; + vec4 v4_array_of_array[4][4]; + + float v_unsized[]; +}; + +void main() +{ + float loaded1 = v1[1]; + v1[2] = loaded1; + + vec2 loaded2 = v2[1]; + v2[2] = loaded2; + + vec3 loaded3 = v3[1]; + v3[2] = loaded3; + + vec4 loaded4 = v4[1]; + v4[2] = loaded4; + + loaded1 = v1_array_of_array[1][2]; + v1_array_of_array[2][3] = loaded1; + + loaded2 = v2_array_of_array[1][2]; + v2_array_of_array[2][3] = loaded2; + + loaded3 = v3_array_of_array[1][2]; + v3_array_of_array[2][3] = loaded3; + + loaded4 = v4_array_of_array[1][2]; + v4_array_of_array[2][3] = loaded4; + + loaded1 = v_unsized[1]; + v_unsized[2] = loaded1; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-alignment.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-alignment.comp new file mode 100644 index 0000000..f9f58b7 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-alignment.comp @@ -0,0 +1,22 @@ +#version 450 +layout(local_size_x = 1) in; + +struct Foo +{ + vec3 a; // <- This one should become packed_float3, and the MSL alignment of the struct is now 4. + float b; +}; + +layout(std140, set = 0, binding = 0) buffer SSBO +{ + vec2 a; + float b; + // <- We expect 4 bytes of padding here since MSL alignment of Foo must be lowered to 4. + Foo foo; +}; + +void main() +{ + a.x = 10.0; + b = 20.0; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing-array-of-scalar.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing-array-of-scalar.comp new file mode 100644 index 0000000..08742d5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing-array-of-scalar.comp @@ -0,0 +1,18 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +struct Foo +{ + vec3 a; +}; + +layout(scalar, set = 0, binding = 0) buffer SSBOScalar +{ + Foo v[]; +} buffer_scalar; + +void main() +{ + buffer_scalar.v[1].a.y = 1.0; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing-recursive.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing-recursive.comp new file mode 100644 index 0000000..c3281b9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing-recursive.comp @@ -0,0 +1,29 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +struct Foo +{ + vec4 a; +}; + +struct Bar +{ + Foo a; +}; + +struct Baz +{ + Bar a; +}; + +layout(scalar, set = 0, binding = 0) buffer SSBOScalar +{ + float v; + Baz baz; +} buffer_scalar; + +void main() +{ + buffer_scalar.baz.a.a.a.a.x = 10.0; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing.comp new file mode 100644 index 0000000..69a8038 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-packing.comp @@ -0,0 +1,27 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 1) in; + +// Foo will be marked packed_float3 because offset of bar is just 12 bytes after foo. +struct Foo +{ + vec3 a; +}; + +// Bar will be marked as packed due to alignment of the struct itself cannot work without packed. +struct Bar +{ + vec3 a; +}; + +layout(scalar, set = 0, binding = 0) buffer SSBOScalar +{ + Foo foo; + Bar bar; +} buffer_scalar; + +void main() +{ + buffer_scalar.foo.a.x = 10.0; + buffer_scalar.bar.a.x = 20.0; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-size-padding-array-of-array.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-size-padding-array-of-array.comp new file mode 100644 index 0000000..ef1ba65 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-size-padding-array-of-array.comp @@ -0,0 +1,45 @@ +#version 450 +layout(local_size_x = 1) in; + +struct A +{ + float v; +}; + +struct B +{ + vec2 v; +}; + +struct C +{ + vec3 v; +}; + +struct D +{ + vec4 v; +}; + +struct E +{ + vec4 a; + vec2 b; +}; + +layout(std140, set = 0, binding = 0) buffer SSBO +{ + A a[2][4]; + B b[2][4]; + C c[2][4]; + D d[2][4]; + mat2 e[2][4]; + E f[]; +}; + +void main() +{ + f[0].a = vec4(2.0); + mat2 tmp = e[0][1]; + e[1][2] = tmp; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-size-padding.comp b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-size-padding.comp new file mode 100644 index 0000000..ad65415 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/packing/struct-size-padding.comp @@ -0,0 +1,45 @@ +#version 450 +layout(local_size_x = 1) in; + +struct A +{ + float v; +}; + +struct B +{ + vec2 v; +}; + +struct C +{ + vec3 v; +}; + +struct D +{ + vec4 v; +}; + +struct E +{ + vec4 a; + vec2 b; +}; + +layout(std140, set = 0, binding = 0) buffer SSBO +{ + A a[4]; + B b[4]; + C c[4]; + D d[4]; + mat2 e[4]; + E f[]; +}; + +void main() +{ + f[0].a = vec4(2.0); + mat2 tmp = e[1]; + e[2] = tmp; +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/vert/functions_nested.vert b/third_party/spirv-cross/shaders-msl-no-opt/vert/functions_nested.vert new file mode 100644 index 0000000..2eec5ac --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/vert/functions_nested.vert @@ -0,0 +1,132 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(std140, set = 0, binding = 0) uniform VertexBuffer +{ + mat4 scale_offset_mat; + uint vertex_base_index; + ivec4 input_attributes[16]; +}; +layout(set=0, binding=3) uniform usamplerBuffer buff_in_1; +layout(set=0, binding=4) uniform usamplerBuffer buff_in_2; + +layout(location=10) out vec4 back_color; +layout(location=0) out vec4 tc0; + +layout(std140, set=0, binding = 1) uniform VertexConstantsBuffer +{ + vec4 vc[16]; +}; + +struct attr_desc +{ + int type; + int attribute_size; + int starting_offset; + int stride; + int swap_bytes; + int is_volatile; +}; + +uint get_bits(uvec4 v, int swap) +{ + if (swap != 0) return (v.w | v.z << 8 | v.y << 16 | v.x << 24); + return (v.x | v.y << 8 | v.z << 16 | v.w << 24); +} + +vec4 fetch_attr(attr_desc desc, int vertex_id, usamplerBuffer input_stream) +{ + vec4 result = vec4(0.0f, 0.0f, 0.0f, 1.0f); + uvec4 tmp; + uint bits; + bool reverse_order = false; + + int first_byte = (vertex_id * desc.stride) + desc.starting_offset; + for (int n = 0; n < 4; n++) + { + if (n == desc.attribute_size) break; + + switch (desc.type) + { + case 0: + //signed normalized 16-bit + tmp.x = texelFetch(input_stream, first_byte++).x; + tmp.y = texelFetch(input_stream, first_byte++).x; + result[n] = get_bits(tmp, desc.swap_bytes); + break; + case 1: + //float + tmp.x = texelFetch(input_stream, first_byte++).x; + tmp.y = texelFetch(input_stream, first_byte++).x; + tmp.z = texelFetch(input_stream, first_byte++).x; + tmp.w = texelFetch(input_stream, first_byte++).x; + result[n] = uintBitsToFloat(get_bits(tmp, desc.swap_bytes)); + break; + case 2: + //unsigned byte + result[n] = texelFetch(input_stream, first_byte++).x; + reverse_order = (desc.swap_bytes != 0); + break; + } + } + + return (reverse_order)? result.wzyx: result; +} + +attr_desc fetch_desc(int location) +{ + attr_desc result; + int attribute_flags = input_attributes[location].w; + result.type = input_attributes[location].x; + result.attribute_size = input_attributes[location].y; + result.starting_offset = input_attributes[location].z; + result.stride = attribute_flags & 0xFF; + result.swap_bytes = (attribute_flags >> 8) & 0x1; + result.is_volatile = (attribute_flags >> 9) & 0x1; + return result; +} + +vec4 read_location(int location) +{ + attr_desc desc = fetch_desc(location); + + int vertex_id = gl_VertexIndex - int(vertex_base_index); + if (desc.is_volatile != 0) + return fetch_attr(desc, vertex_id, buff_in_2); + else + return fetch_attr(desc, vertex_id, buff_in_1); +} + +void vs_adjust(inout vec4 dst_reg0, inout vec4 dst_reg1, inout vec4 dst_reg7) +{ + vec4 tmp0; + vec4 tmp1; + vec4 in_diff_color= read_location(3); + vec4 in_pos= read_location(0); + vec4 in_tc0= read_location(8); + dst_reg1 = (in_diff_color * vc[13]); + tmp0.x = vec4(dot(vec4(in_pos.xyzx.xyz, 1.0), vc[4])).x; + tmp0.y = vec4(dot(vec4(in_pos.xyzx.xyz, 1.0), vc[5])).y; + tmp0.z = vec4(dot(vec4(in_pos.xyzx.xyz, 1.0), vc[6])).z; + tmp1.xy = in_tc0.xyxx.xy; + tmp1.z = vc[15].xxxx.z; + dst_reg7.y = vec4(dot(vec4(tmp1.xyzx.xyz, 1.0), vc[8])).y; + dst_reg7.x = vec4(dot(vec4(tmp1.xyzx.xyz, 1.0), vc[7])).x; + dst_reg0.y = vec4(dot(vec4(tmp0.xyzx.xyz, 1.0), vc[1])).y; + dst_reg0.x = vec4(dot(vec4(tmp0.xyzx.xyz, 1.0), vc[0])).x; +} + +void main () +{ + vec4 dst_reg0= vec4(0.0f, 0.0f, 0.0f, 1.0f); + vec4 dst_reg1= vec4(0.0, 0.0, 0.0, 0.0); + vec4 dst_reg7= vec4(0.0, 0.0, 0.0, 0.0); + + vs_adjust(dst_reg0, dst_reg1, dst_reg7); + + gl_Position = dst_reg0; + back_color = dst_reg1; + tc0 = dst_reg7; + gl_Position = gl_Position * scale_offset_mat; +} + diff --git a/third_party/spirv-cross/shaders-msl-no-opt/vert/layer.msl11.invalid.vert b/third_party/spirv-cross/shaders-msl-no-opt/vert/layer.msl11.invalid.vert new file mode 100644 index 0000000..73b918c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/vert/layer.msl11.invalid.vert @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_shader_viewport_layer_array : require + +layout(location = 0) in vec4 coord; + +void main() +{ + gl_Position = coord; + gl_Layer = int(coord.z); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/vert/pass-array-by-value.force-native-array.vert b/third_party/spirv-cross/shaders-msl-no-opt/vert/pass-array-by-value.force-native-array.vert new file mode 100644 index 0000000..2c142a7 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/vert/pass-array-by-value.force-native-array.vert @@ -0,0 +1,26 @@ +#version 310 es + +layout(location = 0) in int Index1; +layout(location = 1) in int Index2; + +vec4 consume_constant_arrays2(const vec4 positions[4], const vec4 positions2[4]) +{ + return positions[Index1] + positions2[Index2]; +} + +vec4 consume_constant_arrays(const vec4 positions[4], const vec4 positions2[4]) +{ + return consume_constant_arrays2(positions, positions2); +} + +const vec4 LUT1[] = vec4[](vec4(0.0), vec4(1.0), vec4(2.0), vec4(3.0)); + +void main() +{ + vec4 LUT2[4]; + LUT2[0] = vec4(10.0); + LUT2[1] = vec4(11.0); + LUT2[2] = vec4(12.0); + LUT2[3] = vec4(13.0); + gl_Position = consume_constant_arrays(LUT1, LUT2); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/vert/pass-array-by-value.vert b/third_party/spirv-cross/shaders-msl-no-opt/vert/pass-array-by-value.vert new file mode 100644 index 0000000..2c142a7 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/vert/pass-array-by-value.vert @@ -0,0 +1,26 @@ +#version 310 es + +layout(location = 0) in int Index1; +layout(location = 1) in int Index2; + +vec4 consume_constant_arrays2(const vec4 positions[4], const vec4 positions2[4]) +{ + return positions[Index1] + positions2[Index2]; +} + +vec4 consume_constant_arrays(const vec4 positions[4], const vec4 positions2[4]) +{ + return consume_constant_arrays2(positions, positions2); +} + +const vec4 LUT1[] = vec4[](vec4(0.0), vec4(1.0), vec4(2.0), vec4(3.0)); + +void main() +{ + vec4 LUT2[4]; + LUT2[0] = vec4(10.0); + LUT2[1] = vec4(11.0); + LUT2[2] = vec4(12.0); + LUT2[3] = vec4(13.0); + gl_Position = consume_constant_arrays(LUT1, LUT2); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/vert/viewport-index.msl2.invalid.vert b/third_party/spirv-cross/shaders-msl-no-opt/vert/viewport-index.msl2.invalid.vert new file mode 100644 index 0000000..c05c104 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/vert/viewport-index.msl2.invalid.vert @@ -0,0 +1,10 @@ +#version 450 +#extension GL_ARB_shader_viewport_layer_array : require + +layout(location = 0) in vec4 coord; + +void main() +{ + gl_Position = coord; + gl_ViewportIndex = int(coord.z); +} diff --git a/third_party/spirv-cross/shaders-msl-no-opt/vulkan/frag/texture-access-function.swizzle.vk.frag b/third_party/spirv-cross/shaders-msl-no-opt/vulkan/frag/texture-access-function.swizzle.vk.frag new file mode 100644 index 0000000..91f0866 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl-no-opt/vulkan/frag/texture-access-function.swizzle.vk.frag @@ -0,0 +1,90 @@ +#version 450 + +layout(binding = 0) uniform sampler1D tex1d; +layout(binding = 1) uniform texture2D tex2d; +layout(binding = 2) uniform sampler3D tex3d; +layout(binding = 3) uniform textureCube texCube; +layout(binding = 4) uniform sampler2DArray tex2dArray; +layout(binding = 5) uniform samplerCubeArray texCubeArray; +layout(binding = 6) uniform samplerBuffer texBuffer; + +layout(binding = 7) uniform sampler2DShadow depth2d; +layout(binding = 8) uniform samplerCubeShadow depthCube; +layout(binding = 9) uniform texture2DArray depth2dArray; +layout(binding = 10) uniform samplerCubeArrayShadow depthCubeArray; + +layout(binding = 11) uniform sampler defaultSampler; +layout(binding = 12) uniform samplerShadow shadowSampler; + +layout(location = 0) out vec4 fragColor; + +vec4 do_samples(sampler1D t1, texture2D t2, sampler3D t3, textureCube tc, sampler2DArray t2a, samplerCubeArray tca, samplerBuffer tb, sampler2DShadow d2, samplerCubeShadow dc, texture2DArray d2a, samplerCubeArrayShadow dca) +{ + // OpImageSampleImplicitLod + vec4 c = texture(t1, 0.0); + c = texture(sampler2D(t2, defaultSampler), vec2(0.0, 0.0)); + c = texture(t3, vec3(0.0, 0.0, 0.0)); + c = texture(samplerCube(tc, defaultSampler), vec3(0.0, 0.0, 0.0)); + c = texture(t2a, vec3(0.0, 0.0, 0.0)); + c = texture(tca, vec4(0.0, 0.0, 0.0, 0.0)); + + // OpImageSampleDrefImplicitLod + c.r = texture(d2, vec3(0.0, 0.0, 1.0)); + c.r = texture(dc, vec4(0.0, 0.0, 0.0, 1.0)); + c.r = texture(sampler2DArrayShadow(d2a, shadowSampler), vec4(0.0, 0.0, 0.0, 1.0)); + c.r = texture(dca, vec4(0.0, 0.0, 0.0, 0.0), 1.0); + + // OpImageSampleProjImplicitLod + c = textureProj(t1, vec2(0.0, 1.0)); + c = textureProj(sampler2D(t2, defaultSampler), vec3(0.0, 0.0, 1.0)); + c = textureProj(t3, vec4(0.0, 0.0, 0.0, 1.0)); + + // OpImageSampleProjDrefImplicitLod + c.r = textureProj(d2, vec4(0.0, 0.0, 1.0, 1.0)); + + // OpImageSampleExplicitLod + c = textureLod(t1, 0.0, 0.0); + c = textureLod(sampler2D(t2, defaultSampler), vec2(0.0, 0.0), 0.0); + c = textureLod(t3, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(samplerCube(tc, defaultSampler), vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(t2a, vec3(0.0, 0.0, 0.0), 0.0); + c = textureLod(tca, vec4(0.0, 0.0, 0.0, 0.0), 0.0); + + // OpImageSampleDrefExplicitLod + c.r = textureLod(d2, vec3(0.0, 0.0, 1.0), 0.0); + + // OpImageSampleProjExplicitLod + c = textureProjLod(t1, vec2(0.0, 1.0), 0.0); + c = textureProjLod(sampler2D(t2, defaultSampler), vec3(0.0, 0.0, 1.0), 0.0); + c = textureProjLod(t3, vec4(0.0, 0.0, 0.0, 1.0), 0.0); + + // OpImageSampleProjDrefExplicitLod + c.r = textureProjLod(d2, vec4(0.0, 0.0, 1.0, 1.0), 0.0); + + // OpImageFetch + c = texelFetch(t1, 0, 0); + c = texelFetch(sampler2D(t2, defaultSampler), ivec2(0, 0), 0); + c = texelFetch(t3, ivec3(0, 0, 0), 0); + c = texelFetch(t2a, ivec3(0, 0, 0), 0); + + // Show that this transformation doesn't apply to Buffer images. + c = texelFetch(tb, 0); + + // OpImageGather + c = textureGather(sampler2D(t2, defaultSampler), vec2(0.0, 0.0), 0); + c = textureGather(samplerCube(tc, defaultSampler), vec3(0.0, 0.0, 0.0), 1); + c = textureGather(t2a, vec3(0.0, 0.0, 0.0), 2); + c = textureGather(tca, vec4(0.0, 0.0, 0.0, 0.0), 3); + + // OpImageDrefGather + c = textureGather(d2, vec2(0.0, 0.0), 1.0); + c = textureGather(dc, vec3(0.0, 0.0, 0.0), 1.0); + c = textureGather(sampler2DArrayShadow(d2a, shadowSampler), vec3(0.0, 0.0, 0.0), 1.0); + c = textureGather(dca, vec4(0.0, 0.0, 0.0, 0.0), 1.0); + return c; +} + +void main() +{ + fragColor = do_samples(tex1d, tex2d, tex3d, texCube, tex2dArray, texCubeArray, texBuffer, depth2d, depthCube, depth2dArray, depthCubeArray); +} diff --git a/third_party/spirv-cross/shaders-msl/amd/shader_trinary_minmax.msl21.comp b/third_party/spirv-cross/shaders-msl/amd/shader_trinary_minmax.msl21.comp new file mode 100644 index 0000000..f836146 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/amd/shader_trinary_minmax.msl21.comp @@ -0,0 +1,11 @@ +#version 450 +#extension GL_AMD_shader_trinary_minmax : require + +layout (local_size_x = 64) in; + +void main () +{ + int t11 = min3(0, 3, 2); + int t12 = max3(0, 3, 2); + int t13 = mid3(0, 3, 2); +} diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..a87b931 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "main" %15 + OpExecutionMode %3 LocalSize 4 1 1 + OpName %3 "main" + OpName %8 "u0" + OpName %9 "u0_counters" + OpMemberName %9 0 "c" + OpName %11 "u0_counter" + OpName %15 "vThreadID" + OpName %19 "r0" + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 1 + OpDecorate %11 Binding 0 + OpDecorate %15 BuiltIn GlobalInvocationId + %1 = OpTypeVoid + %2 = OpTypeFunction %1 + %5 = OpTypeInt 32 0 + %6 = OpTypeImage %5 Buffer 0 0 0 2 R32ui + %7 = OpTypePointer UniformConstant %6 + %8 = OpVariable %7 UniformConstant + %9 = OpTypeStruct %5 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpTypeVector %12 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpTypeFloat 32 + %17 = OpTypeVector %16 4 + %18 = OpTypePointer Function %17 + %20 = OpTypePointer Uniform %5 + %21 = OpConstant %5 0 + %23 = OpConstant %5 1 + %26 = OpTypePointer Function %16 + %33 = OpConstant %12 0 + %34 = OpConstant %5 2 + %37 = OpTypePointer Input %12 + %41 = OpTypeVector %5 4 + %3 = OpFunction %1 None %2 + %4 = OpLabel + %19 = OpVariable %18 Function + %22 = OpAccessChain %20 %11 %21 + %24 = OpAtomicIDecrement %5 %22 %23 %21 + %25 = OpBitcast %16 %24 + %27 = OpInBoundsAccessChain %26 %19 %21 + OpStore %27 %25 + %28 = OpLoad %6 %8 + %29 = OpInBoundsAccessChain %26 %19 %21 + %30 = OpLoad %16 %29 + %31 = OpBitcast %12 %30 + %32 = OpIMul %5 %31 %23 + %35 = OpShiftRightLogical %5 %33 %34 + %36 = OpIAdd %5 %32 %35 + %38 = OpInBoundsAccessChain %37 %15 %21 + %39 = OpLoad %12 %38 + %40 = OpBitcast %5 %39 + %42 = OpCompositeConstruct %41 %40 %40 %40 %40 + OpImageWrite %28 %36 %42 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..3acb711 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "main" %15 + OpExecutionMode %3 LocalSize 4 1 1 + OpName %3 "main" + OpName %8 "u0" + OpName %9 "u0_counters" + OpMemberName %9 0 "c" + OpName %11 "u0_counter" + OpName %15 "vThreadID" + OpName %19 "r0" + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 1 + OpDecorate %11 Binding 0 + OpDecorate %15 BuiltIn GlobalInvocationId + %1 = OpTypeVoid + %2 = OpTypeFunction %1 + %5 = OpTypeInt 32 0 + %6 = OpTypeImage %5 Buffer 0 0 0 2 R32ui + %7 = OpTypePointer UniformConstant %6 + %8 = OpVariable %7 UniformConstant + %9 = OpTypeStruct %5 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpTypeVector %12 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpTypeFloat 32 + %17 = OpTypeVector %16 4 + %18 = OpTypePointer Function %17 + %20 = OpTypePointer Uniform %5 + %21 = OpConstant %5 0 + %23 = OpConstant %5 1 + %26 = OpTypePointer Function %16 + %33 = OpConstant %12 0 + %34 = OpConstant %5 2 + %37 = OpTypePointer Input %12 + %41 = OpTypeVector %5 4 + %3 = OpFunction %1 None %2 + %4 = OpLabel + %19 = OpVariable %18 Function + %22 = OpAccessChain %20 %11 %21 + %24 = OpAtomicIIncrement %5 %22 %23 %21 + %25 = OpBitcast %16 %24 + %27 = OpInBoundsAccessChain %26 %19 %21 + OpStore %27 %25 + %28 = OpLoad %6 %8 + %29 = OpInBoundsAccessChain %26 %19 %21 + %30 = OpLoad %16 %29 + %31 = OpBitcast %12 %30 + %32 = OpIMul %5 %31 %23 + %35 = OpShiftRightLogical %5 %33 %34 + %36 = OpIAdd %5 %32 %35 + %38 = OpInBoundsAccessChain %37 %15 %21 + %39 = OpLoad %12 %38 + %40 = OpBitcast %5 %39 + %42 = OpCompositeConstruct %41 %40 %40 %40 %40 + OpImageWrite %28 %36 %42 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_iadd.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_iadd.asm.comp new file mode 100644 index 0000000..3b31ab2 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_iadd.asm.comp @@ -0,0 +1,79 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %inputs Restrict + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + OpDecorate %outputs Restrict + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of IAdd + %result_iadd_0 = OpIAdd %uvec4 %input0 %input1 + %result_iadd_1 = OpIAdd %uvec4 %input1 %input0 + %result_iadd_2 = OpIAdd %uvec4 %input0 %input0 + %result_iadd_3 = OpIAdd %uvec4 %input1 %input1 + %result_iadd_4 = OpIAdd %ivec4 %input0 %input0 + %result_iadd_5 = OpIAdd %ivec4 %input1 %input1 + %result_iadd_6 = OpIAdd %ivec4 %input0 %input1 + %result_iadd_7 = OpIAdd %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..b7b4e0b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,101 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %inputs Restrict + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + OpDecorate %outputs Restrict + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %bool = OpTypeBool + %bvec4 = OpTypeVector %bool 4 + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + %uzero = OpConstant %uint 0 + %uone = OpConstant %uint 1 + %utrue = OpConstantComposite %uvec4 %uone %uone %uone %uone + %ufalse = OpConstantComposite %uvec4 %uzero %uzero %uzero %uzero + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + + %result_slt = OpSLessThan %bvec4 %input0 %input1 + %result_sle = OpSLessThanEqual %bvec4 %input0 %input1 + %result_ult = OpULessThan %bvec4 %input0 %input1 + %result_ule = OpULessThanEqual %bvec4 %input0 %input1 + %result_sgt = OpSGreaterThan %bvec4 %input0 %input1 + %result_sge = OpSGreaterThanEqual %bvec4 %input0 %input1 + %result_ugt = OpUGreaterThan %bvec4 %input0 %input1 + %result_uge = OpUGreaterThanEqual %bvec4 %input0 %input1 + + %int_slt = OpSelect %uvec4 %result_slt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_slt + + %int_sle = OpSelect %uvec4 %result_sle %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sle + + %int_ult = OpSelect %uvec4 %result_ult %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ult + + %int_ule = OpSelect %uvec4 %result_ule %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ule + + %int_sgt = OpSelect %uvec4 %result_sgt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sgt + + %int_sge = OpSelect %uvec4 %result_sge %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sge + + %int_ugt = OpSelect %uvec4 %result_ugt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ugt + + %int_uge = OpSelect %uvec4 %result_uge %utrue %ufalse + OpStore %output_ptr_uvec4 %int_uge + + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_sar.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_sar.asm.comp new file mode 100644 index 0000000..64f19fc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_sar.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of ShiftRightArithmetic + %result_iadd_0 = OpShiftRightArithmetic %uvec4 %input0 %input1 + %result_iadd_1 = OpShiftRightArithmetic %uvec4 %input1 %input0 + %result_iadd_2 = OpShiftRightArithmetic %uvec4 %input0 %input0 + %result_iadd_3 = OpShiftRightArithmetic %uvec4 %input1 %input1 + %result_iadd_4 = OpShiftRightArithmetic %ivec4 %input0 %input0 + %result_iadd_5 = OpShiftRightArithmetic %ivec4 %input1 %input1 + %result_iadd_6 = OpShiftRightArithmetic %ivec4 %input0 %input1 + %result_iadd_7 = OpShiftRightArithmetic %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_sdiv.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_sdiv.asm.comp new file mode 100644 index 0000000..ab73ec8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_sdiv.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of SDiv + %result_iadd_0 = OpSDiv %uvec4 %input0 %input1 + %result_iadd_1 = OpSDiv %uvec4 %input1 %input0 + %result_iadd_2 = OpSDiv %uvec4 %input0 %input0 + %result_iadd_3 = OpSDiv %uvec4 %input1 %input1 + %result_iadd_4 = OpSDiv %ivec4 %input0 %input0 + %result_iadd_5 = OpSDiv %ivec4 %input1 %input1 + %result_iadd_6 = OpSDiv %ivec4 %input0 %input1 + %result_iadd_7 = OpSDiv %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_slr.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_slr.asm.comp new file mode 100644 index 0000000..6741f5c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/bitcast_slr.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of ShiftRightLogical + %result_iadd_0 = OpShiftRightLogical %uvec4 %input0 %input1 + %result_iadd_1 = OpShiftRightLogical %uvec4 %input1 %input0 + %result_iadd_2 = OpShiftRightLogical %uvec4 %input0 %input0 + %result_iadd_3 = OpShiftRightLogical %uvec4 %input1 %input1 + %result_iadd_4 = OpShiftRightLogical %ivec4 %input0 %input0 + %result_iadd_5 = OpShiftRightLogical %ivec4 %input1 %input1 + %result_iadd_6 = OpShiftRightLogical %ivec4 %input0 %input1 + %result_iadd_7 = OpShiftRightLogical %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..85f6cc0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,119 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 59 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "A" + OpMemberName %Foo 0 "a" + OpMemberName %Foo 1 "b" + OpName %A "A" + OpMemberName %A 0 "Data" + OpName %C1 "C1" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpName %Foo_0 "A" + OpMemberName %Foo_0 0 "a" + OpMemberName %Foo_0 1 "b" + OpName %A_0 "A" + OpMemberName %A_0 0 "Data" + OpName %C2 "C2" + OpName %B "B" + OpMemberName %B 0 "Data" + OpName %C3 "C3" + OpName %B_0 "B" + OpMemberName %B_0 0 "Data" + OpName %C4 "C4" + OpMemberDecorate %Foo 0 Offset 0 + OpMemberDecorate %Foo 1 Offset 4 + OpDecorate %_runtimearr_Foo ArrayStride 8 + OpMemberDecorate %A 0 Offset 0 + OpDecorate %A BufferBlock + OpDecorate %C1 DescriptorSet 0 + OpDecorate %C1 Binding 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %Foo_0 1 Offset 4 + OpDecorate %_arr_Foo_0_uint_1024 ArrayStride 16 + OpMemberDecorate %A_0 0 Offset 0 + OpDecorate %A_0 Block + OpDecorate %C2 DescriptorSet 0 + OpDecorate %C2 Binding 2 + OpDecorate %_runtimearr_Foo_0 ArrayStride 8 + OpMemberDecorate %B 0 Offset 0 + OpDecorate %B BufferBlock + OpDecorate %C3 DescriptorSet 0 + OpDecorate %C3 Binding 0 + OpDecorate %_arr_Foo_0_uint_1024_0 ArrayStride 16 + OpMemberDecorate %B_0 0 Offset 0 + OpDecorate %B_0 Block + OpDecorate %C4 DescriptorSet 0 + OpDecorate %C4 Binding 3 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %Foo = OpTypeStruct %int %int +%_runtimearr_Foo = OpTypeRuntimeArray %Foo + %A = OpTypeStruct %_runtimearr_Foo +%_ptr_Uniform_A = OpTypePointer Uniform %A + %C1 = OpVariable %_ptr_Uniform_A Uniform + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %Foo_0 = OpTypeStruct %int %int + %uint_1024 = OpConstant %uint 1024 +%_arr_Foo_0_uint_1024 = OpTypeArray %Foo_0 %uint_1024 + %A_0 = OpTypeStruct %_arr_Foo_0_uint_1024 +%_ptr_Uniform_A_0 = OpTypePointer Uniform %A_0 + %C2 = OpVariable %_ptr_Uniform_A_0 Uniform +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Uniform_Foo = OpTypePointer Uniform %Foo +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_1 = OpConstant %int 1 +%_runtimearr_Foo_0 = OpTypeRuntimeArray %Foo + %B = OpTypeStruct %_runtimearr_Foo_0 +%_ptr_Uniform_B = OpTypePointer Uniform %B + %C3 = OpVariable %_ptr_Uniform_B Uniform +%_arr_Foo_0_uint_1024_0 = OpTypeArray %Foo_0 %uint_1024 + %B_0 = OpTypeStruct %_arr_Foo_0_uint_1024_0 +%_ptr_Uniform_B_0 = OpTypePointer Uniform %B_0 + %C4 = OpVariable %_ptr_Uniform_B_0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %20 = OpLoad %uint %19 + %27 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %28 = OpLoad %uint %27 + %30 = OpAccessChain %_ptr_Uniform_Foo_0 %C2 %int_0 %28 + %31 = OpLoad %Foo_0 %30 + %33 = OpAccessChain %_ptr_Uniform_Foo %C1 %int_0 %20 + %34 = OpCompositeExtract %int %31 0 + %36 = OpAccessChain %_ptr_Uniform_int %33 %int_0 + OpStore %36 %34 + %37 = OpCompositeExtract %int %31 1 + %39 = OpAccessChain %_ptr_Uniform_int %33 %int_1 + OpStore %39 %37 + %44 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %45 = OpLoad %uint %44 + %50 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %51 = OpLoad %uint %50 + %52 = OpAccessChain %_ptr_Uniform_Foo_0 %C4 %int_0 %51 + %53 = OpLoad %Foo_0 %52 + %54 = OpAccessChain %_ptr_Uniform_Foo %C3 %int_0 %45 + %55 = OpCompositeExtract %int %53 0 + %56 = OpAccessChain %_ptr_Uniform_int %54 %int_0 + OpStore %56 %55 + %57 = OpCompositeExtract %int %53 1 + %58 = OpAccessChain %_ptr_Uniform_int %54 %int_1 + OpStore %58 %57 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp new file mode 100644 index 0000000..400690b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/buffer-write-relative-addr.asm.comp @@ -0,0 +1,93 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 59 +; Schema: 0 + OpCapability Shader + OpCapability UniformBufferArrayDynamicIndexing + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %vThreadIDInGroup + OpExecutionMode %main LocalSize 4 1 1 + OpName %main "main" + OpName %cb5_struct "cb5_struct" + OpName %cb0_5 "cb0_5" + OpName %u0 "u0" + OpName %vThreadIDInGroup "vThreadIDInGroup" + OpName %r0 "r0" + OpDecorate %_arr_v4float_uint_5 ArrayStride 16 + OpDecorate %cb5_struct Block + OpMemberDecorate %cb5_struct 0 Offset 0 + OpDecorate %cb0_5 DescriptorSet 0 + OpDecorate %cb0_5 Binding 1 + OpDecorate %u0 DescriptorSet 0 + OpDecorate %u0 Binding 0 + OpDecorate %u0 NonReadable + OpDecorate %vThreadIDInGroup BuiltIn LocalInvocationId + %void = OpTypeVoid + %2 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_5 = OpConstant %uint 5 +%_arr_v4float_uint_5 = OpTypeArray %v4float %uint_5 + %cb5_struct = OpTypeStruct %_arr_v4float_uint_5 +%_ptr_Uniform_cb5_struct = OpTypePointer Uniform %cb5_struct + %cb0_5 = OpVariable %_ptr_Uniform_cb5_struct Uniform + %13 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %u0 = OpVariable %_ptr_UniformConstant_13 UniformConstant + %int = OpTypeInt 32 1 + %v3int = OpTypeVector %int 3 +%_ptr_Input_v3int = OpTypePointer Input %v3int +%vThreadIDInGroup = OpVariable %_ptr_Input_v3int Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_int = OpTypePointer Input %int + %uint_0 = OpConstant %uint 0 + %int_4 = OpConstant %int 4 +%_ptr_Function_float = OpTypePointer Function %float + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %v4uint = OpTypeVector %uint 4 + %uint_3 = OpConstant %uint 3 + %main = OpFunction %void None %2 + %4 = OpLabel + %r0 = OpVariable %_ptr_Function_v4float Function + %24 = OpInBoundsAccessChain %_ptr_Input_int %vThreadIDInGroup %uint_0 + %25 = OpLoad %int %24 + %27 = OpShiftLeftLogical %int %25 %int_4 + %28 = OpBitcast %float %27 + %30 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_0 + OpStore %30 %28 + %31 = OpInBoundsAccessChain %_ptr_Input_int %vThreadIDInGroup %uint_0 + %32 = OpLoad %int %31 + %33 = OpBitcast %float %32 + %35 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_1 + OpStore %35 %33 + %36 = OpLoad %13 %u0 + %37 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_0 + %38 = OpLoad %float %37 + %39 = OpBitcast %uint %38 + %41 = OpShiftRightLogical %uint %39 %uint_2 + %42 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_1 + %43 = OpLoad %float %42 + %44 = OpBitcast %int %43 + %45 = OpIAdd %uint %44 %uint_1 + %47 = OpAccessChain %_ptr_Uniform_v4float %cb0_5 %uint_0 %45 + %48 = OpLoad %v4float %47 + %50 = OpBitcast %v4uint %48 + %51 = OpVectorShuffle %v4uint %50 %50 0 0 0 0 + OpImageWrite %36 %41 %51 + %52 = OpVectorShuffle %v4uint %50 %50 1 1 1 1 + %53 = OpIAdd %uint %41 %uint_1 + OpImageWrite %36 %53 %52 + %54 = OpVectorShuffle %v4uint %50 %50 2 2 2 2 + %55 = OpIAdd %uint %41 %uint_2 + OpImageWrite %36 %55 %54 + %56 = OpVectorShuffle %v4uint %50 %50 3 3 3 3 + %58 = OpIAdd %uint %41 %uint_3 + OpImageWrite %36 %58 %56 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/buffer-write.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/buffer-write.asm.comp new file mode 100644 index 0000000..697324b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/buffer-write.asm.comp @@ -0,0 +1,59 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 63 +; Schema: 0 + OpCapability Shader + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %group_id %group_index + OpExecutionMode %main LocalSize 32 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %cb "cb" + OpMemberName %cb 0 "value" + OpName %_ "" + OpName %buffer "buffer" + OpName %group_id "group_id" + OpName %group_index "group_index" + OpMemberDecorate %cb 0 Offset 0 + OpDecorate %cb Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 7 + OpDecorate %buffer DescriptorSet 0 + OpDecorate %buffer Binding 0 + OpDecorate %group_id BuiltIn WorkgroupId + OpDecorate %group_index BuiltIn LocalInvocationIndex + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %cb = OpTypeStruct %float +%_ptr_Uniform_cb = OpTypePointer Uniform %cb + %_ = OpVariable %_ptr_Uniform_cb Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %34 = OpTypeImage %float Buffer 0 0 0 2 R32f +%_ptr_UniformConstant_34 = OpTypePointer UniformConstant %34 + %buffer = OpVariable %_ptr_UniformConstant_34 UniformConstant +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %group_id = OpVariable %_ptr_Input_v3uint Input +%_ptr_Input_uint = OpTypePointer Input %uint +%group_index = OpVariable %_ptr_Input_uint Input + %main = OpFunction %void None %3 + %5 = OpLabel + %43 = OpLoad %v3uint %group_id + %47 = OpLoad %uint %group_index + %56 = OpCompositeExtract %uint %43 0 + %57 = OpIMul %uint %uint_32 %56 + %59 = OpIAdd %uint %57 %47 + %60 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %61 = OpLoad %float %60 + %62 = OpLoad %34 %buffer + OpImageWrite %62 %59 %61 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..78b1dc7 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 61 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %id_1 + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %Load_u1_ "Load(u1;" + OpName %size "size" + OpName %_main_vu3_ "@main(vu3;" + OpName %id "id" + OpName %data "data" + OpName %byteAddrTemp "byteAddrTemp" + OpName %ssbo "ssbo" + OpMemberName %ssbo 0 "@data" + OpName %ssbo_0 "ssbo" + OpName %param "param" + OpName %id_0 "id" + OpName %id_1 "id" + OpName %param_0 "param" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %ssbo 0 NonWritable + OpMemberDecorate %ssbo 0 Offset 0 + OpDecorate %ssbo BufferBlock + OpDecorate %ssbo_0 DescriptorSet 0 + OpDecorate %ssbo_0 Binding 1 + OpDecorate %id_1 BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %8 = OpTypeFunction %void %_ptr_Function_uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Function_v3uint = OpTypePointer Function %v3uint + %14 = OpTypeFunction %void %_ptr_Function_v3uint + %v4uint = OpTypeVector %uint 4 +%_ptr_Function_v4uint = OpTypePointer Function %v4uint + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_2 = OpConstant %int 2 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %ssbo = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_ssbo = OpTypePointer Uniform %ssbo + %ssbo_0 = OpVariable %_ptr_Uniform_ssbo Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 + %uint_4 = OpConstant %uint 4 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %id_1 = OpVariable %_ptr_Input_v3uint Input + %main = OpFunction %void None %3 + %5 = OpLabel + %id_0 = OpVariable %_ptr_Function_v3uint Function + %param_0 = OpVariable %_ptr_Function_v3uint Function + %57 = OpLoad %v3uint %id_1 + OpStore %id_0 %57 + %59 = OpLoad %v3uint %id_0 + OpStore %param_0 %59 + %60 = OpFunctionCall %void %_main_vu3_ %param_0 + OpReturn + OpFunctionEnd + %Load_u1_ = OpFunction %void None %8 + %size = OpFunctionParameter %_ptr_Function_uint + %11 = OpLabel + %data = OpVariable %_ptr_Function_v4uint Function +%byteAddrTemp = OpVariable %_ptr_Function_int Function + %24 = OpLoad %uint %size + %26 = OpShiftRightLogical %int %24 %int_2 + OpStore %byteAddrTemp %26 + %32 = OpLoad %int %byteAddrTemp + %34 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %32 + %35 = OpLoad %uint %34 + %36 = OpLoad %int %byteAddrTemp + %38 = OpIAdd %int %36 %int_1 + %39 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpLoad %int %byteAddrTemp + %42 = OpIAdd %int %41 %int_2 + %43 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %42 + %44 = OpLoad %uint %43 + %45 = OpLoad %int %byteAddrTemp + %47 = OpIAdd %int %45 %int_3 + %48 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %47 + %49 = OpLoad %uint %48 + %50 = OpCompositeConstruct %v4uint %35 %40 %44 %49 + OpStore %data %50 + OpReturn + OpFunctionEnd + %_main_vu3_ = OpFunction %void None %14 + %id = OpFunctionParameter %_ptr_Function_v3uint + %17 = OpLabel + %param = OpVariable %_ptr_Function_uint Function + OpStore %param %uint_4 + %53 = OpFunctionCall %void %Load_u1_ %param + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp new file mode 100644 index 0000000..8f75929 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/image-load-store-short-vector.asm.comp @@ -0,0 +1,75 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 44 +; Schema: 0 + OpCapability Shader + OpCapability StorageImageExtendedFormats + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %id_1 + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %_main_vu3_ "@main(vu3;" + OpName %id "id" + OpName %loaded "loaded" + OpName %TargetTexture "TargetTexture" + OpName %storeTemp "storeTemp" + OpName %id_0 "id" + OpName %id_1 "id" + OpName %param "param" + OpDecorate %TargetTexture DescriptorSet 0 + OpDecorate %TargetTexture Binding 0 + OpDecorate %id_1 BuiltIn WorkgroupId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Function_v3uint = OpTypePointer Function %v3uint + %9 = OpTypeFunction %void %_ptr_Function_v3uint + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %17 = OpTypeImage %float 2D 0 0 0 2 Rg32f +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%TargetTexture = OpVariable %_ptr_UniformConstant_17 UniformConstant + %v2uint = OpTypeVector %uint 2 + %float_1 = OpConstant %float 1 + %uint_1 = OpConstant %uint 1 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %id_1 = OpVariable %_ptr_Input_v3uint Input + %main = OpFunction %void None %3 + %5 = OpLabel + %id_0 = OpVariable %_ptr_Function_v3uint Function + %param = OpVariable %_ptr_Function_v3uint Function + %40 = OpLoad %v3uint %id_1 + OpStore %id_0 %40 + %42 = OpLoad %v3uint %id_0 + OpStore %param %42 + %43 = OpFunctionCall %void %_main_vu3_ %param + OpReturn + OpFunctionEnd + %_main_vu3_ = OpFunction %void None %9 + %id = OpFunctionParameter %_ptr_Function_v3uint + %12 = OpLabel + %loaded = OpVariable %_ptr_Function_v2float Function + %storeTemp = OpVariable %_ptr_Function_v2float Function + %20 = OpLoad %17 %TargetTexture + %22 = OpLoad %v3uint %id + %23 = OpVectorShuffle %v2uint %22 %22 0 1 + %24 = OpImageRead %v2float %20 %23 + OpStore %loaded %24 + %26 = OpLoad %v2float %loaded + %28 = OpCompositeConstruct %v2float %float_1 %float_1 + %29 = OpFAdd %v2float %26 %28 + OpStore %storeTemp %29 + %30 = OpLoad %17 %TargetTexture + %31 = OpLoad %v3uint %id + %32 = OpVectorShuffle %v2uint %31 %31 0 1 + %34 = OpCompositeConstruct %v2uint %uint_1 %uint_1 + %35 = OpIAdd %v2uint %32 %34 + %36 = OpLoad %v2float %storeTemp + OpImageWrite %30 %35 %36 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/multiple-entry.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/multiple-entry.asm.comp new file mode 100644 index 0000000..9ddc07b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/multiple-entry.asm.comp @@ -0,0 +1,98 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %func_alt "main2" %frag_in %frag_out + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpExecutionMode %func_alt OriginUpperLeft + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %inputs Restrict + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + OpDecorate %outputs Restrict + OpDecorate %frag_in Location 0 + OpDecorate %frag_out Location 0 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %float = OpTypeFloat 32 + %vec4 = OpTypeVector %float 4 + %vec4_input_ptr = OpTypePointer Input %vec4 + %vec4_output_ptr = OpTypePointer Output %vec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %frag_in = OpVariable %vec4_input_ptr Input + %frag_out = OpVariable %vec4_output_ptr Output + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of IAdd + %result_iadd_0 = OpIAdd %uvec4 %input0 %input1 + %result_iadd_1 = OpIAdd %uvec4 %input1 %input0 + %result_iadd_2 = OpIAdd %uvec4 %input0 %input0 + %result_iadd_3 = OpIAdd %uvec4 %input1 %input1 + %result_iadd_4 = OpIAdd %ivec4 %input0 %input0 + %result_iadd_5 = OpIAdd %ivec4 %input1 %input1 + %result_iadd_6 = OpIAdd %ivec4 %input0 %input1 + %result_iadd_7 = OpIAdd %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd + + %func_alt = OpFunction %void None %main_func + %block_alt = OpLabel + %frag_input_value = OpLoad %vec4 %frag_in + OpStore %frag_out %frag_input_value + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/quantize.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/quantize.asm.comp new file mode 100644 index 0000000..f5afc65 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/quantize.asm.comp @@ -0,0 +1,67 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 38 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "SSBO0" + OpMemberName %10 0 "scalar" + OpMemberName %10 1 "vec2_val" + OpMemberName %10 2 "vec3_val" + OpMemberName %10 3 "vec4_val" + OpName %12 "" + OpMemberDecorate %10 0 Offset 0 + OpMemberDecorate %10 1 Offset 8 + OpMemberDecorate %10 2 Offset 16 + OpMemberDecorate %10 3 Offset 32 + OpDecorate %10 BufferBlock + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeVector %6 3 + %9 = OpTypeVector %6 4 + %10 = OpTypeStruct %6 %7 %8 %9 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %13 = OpTypeInt 32 1 + %14 = OpConstant %13 0 + %15 = OpTypePointer Uniform %6 + %20 = OpConstant %13 1 + %21 = OpTypePointer Uniform %7 + %26 = OpConstant %13 2 + %27 = OpTypePointer Uniform %8 + %32 = OpConstant %13 3 + %33 = OpTypePointer Uniform %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %16 = OpAccessChain %15 %12 %14 + %17 = OpLoad %6 %16 + %18 = OpQuantizeToF16 %6 %17 + %19 = OpAccessChain %15 %12 %14 + OpStore %19 %18 + %22 = OpAccessChain %21 %12 %20 + %23 = OpLoad %7 %22 + %24 = OpQuantizeToF16 %7 %23 + %25 = OpAccessChain %21 %12 %20 + OpStore %25 %24 + %28 = OpAccessChain %27 %12 %26 + %29 = OpLoad %8 %28 + %30 = OpQuantizeToF16 %8 %29 + %31 = OpAccessChain %27 %12 %26 + OpStore %31 %30 + %34 = OpAccessChain %33 %12 %32 + %35 = OpLoad %9 %34 + %36 = OpQuantizeToF16 %9 %35 + %37 = OpAccessChain %33 %12 %32 + OpStore %37 %36 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/relaxed-block-layout.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/relaxed-block-layout.asm.comp new file mode 100644 index 0000000..dd90942 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/relaxed-block-layout.asm.comp @@ -0,0 +1,108 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 63 +; Schema: 0 + OpCapability Shader + OpCapability StorageBuffer16BitAccess + OpCapability StorageBuffer8BitAccess + OpCapability UniformAndStorageBuffer8BitAccess + OpExtension "SPV_KHR_8bit_storage" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_LocalInvocationID %gl_GlobalInvocationID %gl_WorkGroupID %gl_NumWorkGroups + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_shader_16bit_storage" + OpSourceExtension "GL_EXT_shader_8bit_storage" + OpName %main "main" + OpName %foo "foo" + OpMemberName %foo 0 "bar" + OpMemberName %foo 1 "baz" + OpMemberName %foo 2 "quux" + OpMemberName %foo 3 "blah" + OpMemberName %foo 4 "wibble" + OpName %_ "" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpName %gl_WorkGroupID "gl_WorkGroupID" + OpName %gl_NumWorkGroups "gl_NumWorkGroups" + OpMemberDecorate %foo 0 Offset 0 + OpMemberDecorate %foo 1 Offset 4 + OpMemberDecorate %foo 2 Offset 16 + OpMemberDecorate %foo 3 Offset 17 + OpMemberDecorate %foo 4 Offset 22 + OpDecorate %foo BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %gl_NumWorkGroups BuiltIn NumWorkgroups + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %uchar = OpTypeInt 8 0 + %v4uchar = OpTypeVector %uchar 4 + %half = OpTypeFloat 16 + %v2half = OpTypeVector %half 2 + %foo = OpTypeStruct %uint %v3float %uchar %v4uchar %v2half +%_ptr_Uniform_foo = OpTypePointer Uniform %foo + %_ = OpVariable %_ptr_Uniform_foo Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_1 = OpConstant %int 1 +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %int_3 = OpConstant %int 3 +%_ptr_Uniform_v4uchar = OpTypePointer Uniform %v4uchar + %v4uint = OpTypeVector %uint 4 +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %int_4 = OpConstant %int 4 +%_ptr_Uniform_v2half = OpTypePointer Uniform %v2half + %v2float = OpTypeVector %float 2 +%gl_NumWorkGroups = OpVariable %_ptr_Input_v3uint Input + %v2uint = OpTypeVector %uint 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %23 = OpAccessChain %_ptr_Input_uint %gl_LocalInvocationID %uint_0 + %24 = OpLoad %uint %23 + %26 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 + OpStore %26 %24 + %29 = OpLoad %v3uint %gl_GlobalInvocationID + %30 = OpConvertUToF %v3float %29 + %32 = OpAccessChain %_ptr_Uniform_v3float %_ %int_1 + OpStore %32 %30 + %35 = OpAccessChain %_ptr_Uniform_v4uchar %_ %int_3 + %36 = OpLoad %v4uchar %35 + %38 = OpUConvert %v4uint %36 + %39 = OpVectorShuffle %v3uint %38 %38 0 1 2 + %41 = OpLoad %v3uint %gl_WorkGroupID + %42 = OpIAdd %v3uint %39 %41 + %43 = OpCompositeExtract %uint %42 0 + %44 = OpCompositeExtract %uint %42 1 + %45 = OpCompositeExtract %uint %42 2 + %46 = OpCompositeConstruct %v4uint %43 %44 %45 %uint_0 + %47 = OpUConvert %v4uchar %46 + %48 = OpAccessChain %_ptr_Uniform_v4uchar %_ %int_3 + OpStore %48 %47 + %51 = OpAccessChain %_ptr_Uniform_v2half %_ %int_4 + %52 = OpLoad %v2half %51 + %54 = OpFConvert %v2float %52 + %57 = OpLoad %v3uint %gl_NumWorkGroups + %58 = OpVectorShuffle %v2uint %57 %57 0 1 + %59 = OpConvertUToF %v2float %58 + %60 = OpFMul %v2float %54 %59 + %61 = OpFConvert %v2half %60 + %62 = OpAccessChain %_ptr_Uniform_v2half %_ %int_4 + OpStore %62 %61 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp new file mode 100644 index 0000000..188e3fe --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/specialization-constant-workgroup.asm.comp @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 24 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 20 1 + OpSource ESSL 310 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %19 SpecId 10 + OpDecorate %21 SpecId 12 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %SSBO = OpTypeStruct %float +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %uint = OpTypeInt 32 0 + %19 = OpSpecConstant %uint 9 + %uint_20 = OpConstant %uint 20 + %21 = OpSpecConstant %uint 4 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %19 %uint_20 %21 + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %15 = OpLoad %float %14 + %16 = OpFAdd %float %15 %float_1 + %17 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %17 %16 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp new file mode 100644 index 0000000..384da30 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/struct-resource-name-aliasing.asm.comp @@ -0,0 +1,49 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 21 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 8 8 1 + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %bufA "bufA" + OpMemberName %bufA 0 "@data" + OpName %bufA_0 "bufA" + OpName %bufB "bufB" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %bufA 0 Offset 0 + OpDecorate %bufA BufferBlock + OpDecorate %bufA_0 DescriptorSet 0 + OpDecorate %bufB DescriptorSet 0 + OpDecorate %bufA_0 Binding 0 + OpDecorate %bufB Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %bufA = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_bufA = OpTypePointer Uniform %bufA + %bufA_0 = OpVariable %_ptr_Uniform_bufA Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %bufB = OpVariable %_ptr_Uniform_bufA Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpFunctionCall %void %_main_ + OpReturn + OpFunctionEnd + %_main_ = OpFunction %void None %3 + %7 = OpLabel + %17 = OpAccessChain %_ptr_Uniform_uint %bufA_0 %int_0 %int_0 + OpStore %17 %uint_0 + %19 = OpAccessChain %_ptr_Uniform_uint %bufB %int_0 %int_0 + OpStore %19 %uint_0 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/variable-pointers-2.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/variable-pointers-2.asm.comp new file mode 100644 index 0000000..308162f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/variable-pointers-2.asm.comp @@ -0,0 +1,117 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 65 +; Schema: 0 + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID %gl_LocalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %foo "foo" + OpMemberName %foo 0 "a" + OpMemberName %foo 1 "b" + OpMemberName %foo 2 "c" + OpName %bar "bar" + OpMemberName %bar 0 "d" + OpName %buf "buf" + OpName %cb "cb" + OpName %select_buffer "select_buffer" + OpName %select_input "select_input" + OpName %a "a" + OpMemberDecorate %foo 0 Offset 0 + OpMemberDecorate %foo 1 Offset 512 + OpMemberDecorate %foo 2 Offset 520 + OpMemberDecorate %bar 0 Offset 0 + OpDecorate %foo Block + OpDecorate %bar Block + OpDecorate %buf DescriptorSet 0 + OpDecorate %buf Binding 0 + OpDecorate %cb DescriptorSet 0 + OpDecorate %cb Binding 1 + OpDecorate %_ptr_StorageBuffer_int ArrayStride 4 + OpDecorate %_arr_int_uint_128 ArrayStride 4 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + %void = OpTypeVoid + %15 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_128 = OpConstant %uint 128 +%_arr_int_uint_128 = OpTypeArray %int %uint_128 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %foo = OpTypeStruct %_arr_int_uint_128 %uint %v2float +%_ptr_StorageBuffer_foo = OpTypePointer StorageBuffer %foo + %buf = OpVariable %_ptr_StorageBuffer_foo StorageBuffer + %bar = OpTypeStruct %int +%_ptr_Uniform_bar = OpTypePointer Uniform %bar + %cb = OpVariable %_ptr_Uniform_bar Uniform + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool +%_ptr_Uniform_int = OpTypePointer Uniform %int + %28 = OpTypeFunction %_ptr_StorageBuffer_foo %_ptr_StorageBuffer_foo + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %31 = OpConstantNull %_ptr_StorageBuffer_foo + %32 = OpTypeFunction %_ptr_Input_v3uint +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int +%_ptr_Function__ptr_StorageBuffer_foo = OpTypePointer Function %_ptr_StorageBuffer_foo +%select_buffer = OpFunction %_ptr_StorageBuffer_foo None %28 + %a = OpFunctionParameter %_ptr_StorageBuffer_foo + %33 = OpLabel + %34 = OpAccessChain %_ptr_Uniform_int %cb %uint_0 + %35 = OpLoad %int %34 + %36 = OpINotEqual %bool %35 %int_0 + %37 = OpSelect %_ptr_StorageBuffer_foo %36 %a %31 + OpReturnValue %37 + OpFunctionEnd +%select_input = OpFunction %_ptr_Input_v3uint None %32 + %38 = OpLabel + %39 = OpAccessChain %_ptr_Uniform_int %cb %uint_0 + %40 = OpLoad %int %39 + %41 = OpINotEqual %bool %40 %int_0 + %42 = OpSelect %_ptr_Input_v3uint %41 %gl_GlobalInvocationID %gl_LocalInvocationID + OpReturnValue %42 + OpFunctionEnd + %main = OpFunction %void None %15 + %43 = OpLabel + %65 = OpVariable %_ptr_Function__ptr_StorageBuffer_foo Function + %44 = OpFunctionCall %_ptr_StorageBuffer_foo %select_buffer %buf + OpStore %65 %44 + %45 = OpFunctionCall %_ptr_Input_v3uint %select_input + %66 = OpLoad %_ptr_StorageBuffer_foo %65 + %46 = OpAccessChain %_ptr_StorageBuffer_int %66 %uint_0 %uint_0 + %47 = OpAccessChain %_ptr_StorageBuffer_int %buf %uint_0 %uint_0 + OpBranch %48 + %48 = OpLabel + %49 = OpPhi %_ptr_StorageBuffer_int %46 %43 %50 %51 + %52 = OpPhi %_ptr_StorageBuffer_int %47 %43 %53 %51 + %54 = OpLoad %int %49 + %55 = OpLoad %int %52 + %56 = OpINotEqual %bool %54 %55 + OpLoopMerge %58 %51 None + OpBranchConditional %56 %57 %58 + %57 = OpLabel + %59 = OpIAdd %int %54 %55 + %60 = OpLoad %v3uint %45 + %61 = OpCompositeExtract %uint %60 0 + %62 = OpBitcast %int %61 + %63 = OpIAdd %int %59 %62 + OpStore %49 %63 + OpStore %52 %63 + OpBranch %51 + %51 = OpLabel + %50 = OpPtrAccessChain %_ptr_StorageBuffer_int %49 %uint_1 + %53 = OpPtrAccessChain %_ptr_StorageBuffer_int %52 %uint_1 + OpBranch %48 + %58 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp new file mode 100644 index 0000000..3dcb04f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/variable-pointers-store-forwarding.asm.comp @@ -0,0 +1,75 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 40 +; Schema: 0 + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %foo "foo" + OpMemberName %foo 0 "a" + OpName %bar "bar" + OpMemberName %bar 0 "b" + OpName %x "x" + OpName %y "y" + OpName %a "a" + OpName %b "b" + OpMemberDecorate %foo 0 Offset 0 + OpMemberDecorate %bar 0 Offset 0 + OpDecorate %foo Block + OpDecorate %bar Block + OpDecorate %x DescriptorSet 0 + OpDecorate %x Binding 0 + OpDecorate %y DescriptorSet 0 + OpDecorate %y Binding 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + %void = OpTypeVoid + %11 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %foo = OpTypeStruct %int +%_ptr_StorageBuffer_foo = OpTypePointer StorageBuffer %foo + %x = OpVariable %_ptr_StorageBuffer_foo StorageBuffer + %bar = OpTypeStruct %int +%_ptr_StorageBuffer_bar = OpTypePointer StorageBuffer %bar + %y = OpVariable %_ptr_StorageBuffer_bar StorageBuffer + %uint_0 = OpConstant %uint 0 + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int + %22 = OpTypeFunction %_ptr_StorageBuffer_int %_ptr_StorageBuffer_foo %_ptr_StorageBuffer_bar +%_ptr_Function__ptr_StorageBuffer_int = OpTypePointer Function %_ptr_StorageBuffer_int + %24 = OpFunction %_ptr_StorageBuffer_int None %22 + %a = OpFunctionParameter %_ptr_StorageBuffer_foo + %b = OpFunctionParameter %_ptr_StorageBuffer_bar + %25 = OpLabel + %26 = OpLoad %v3uint %gl_GlobalInvocationID + %27 = OpCompositeExtract %uint %26 0 + %28 = OpINotEqual %bool %27 %uint_0 + %29 = OpAccessChain %_ptr_StorageBuffer_int %a %uint_0 + %30 = OpAccessChain %_ptr_StorageBuffer_int %b %uint_0 + %31 = OpSelect %_ptr_StorageBuffer_int %28 %29 %30 + OpReturnValue %31 + OpFunctionEnd + %main = OpFunction %void None %11 + %32 = OpLabel + %33 = OpVariable %_ptr_Function__ptr_StorageBuffer_int Function + %34 = OpFunctionCall %_ptr_StorageBuffer_int %24 %x %y + OpStore %33 %34 + %35 = OpLoad %_ptr_StorageBuffer_int %33 + %36 = OpAccessChain %_ptr_StorageBuffer_int %x %uint_0 + %37 = OpLoad %int %36 + OpStore %35 %int_0 + %38 = OpIAdd %int %37 %37 + %39 = OpAccessChain %_ptr_StorageBuffer_int %y %uint_0 + OpStore %39 %38 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp new file mode 100644 index 0000000..c01432b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/vector-builtin-type-cast-func.asm.comp @@ -0,0 +1,147 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 90 +; Schema: 0 + OpCapability Shader + OpCapability ImageQuery + OpCapability StorageImageWriteWithoutFormat + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_LocalInvocationID + OpExecutionMode %main LocalSize 16 16 1 + OpSource GLSL 450 + OpName %main "main" + OpName %get_texcoord_vi2_vi2_ "get_texcoord(vi2;vi2;" + OpName %base "base" + OpName %index "index" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %r0 "r0" + OpName %u0 "u0" + OpName %i "i" + OpName %j "j" + OpName %param "param" + OpName %param_0 "param" + OpName %cb1_struct "cb1_struct" + OpMemberName %cb1_struct 0 "_m0" + OpName %cb0_1 "cb0_1" + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %u0 DescriptorSet 0 + OpDecorate %u0 Binding 1 + OpDecorate %u0 NonReadable + OpDecorate %_arr_v4float_uint_1 ArrayStride 16 + OpMemberDecorate %cb1_struct 0 Offset 0 + OpDecorate %cb1_struct Block + OpDecorate %cb0_1 DescriptorSet 0 + OpDecorate %cb0_1 Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%_ptr_Function_v2int = OpTypePointer Function %v2int + %9 = OpTypeFunction %v2int %_ptr_Function_v2int %_ptr_Function_v2int + %v3int = OpTypeVector %int 3 +%_ptr_Input_v3int = OpTypePointer Input %v3int +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3int Input + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %float = OpTypeFloat 32 + %30 = OpTypeImage %float 2D 0 0 0 2 Unknown +%_ptr_UniformConstant_30 = OpTypePointer UniformConstant %30 + %u0 = OpVariable %_ptr_UniformConstant_30 UniformConstant + %uint_4 = OpConstant %uint 4 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 + %v4float = OpTypeVector %float 4 +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 + %cb1_struct = OpTypeStruct %_arr_v4float_uint_1 +%_ptr_Uniform_cb1_struct = OpTypePointer Uniform %cb1_struct + %cb0_1 = OpVariable %_ptr_Uniform_cb1_struct Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_1 = OpConstant %int 1 + %uint_16 = OpConstant %uint 16 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_16 %uint_16 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %r0 = OpVariable %_ptr_Function_v2int Function + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + %param = OpVariable %_ptr_Function_v2int Function + %param_0 = OpVariable %_ptr_Function_v2int Function + %33 = OpLoad %30 %u0 + %34 = OpImageQuerySize %v2int %33 + %36 = OpCompositeConstruct %v2uint %uint_4 %uint_4 + %37 = OpShiftRightArithmetic %v2int %34 %36 + %38 = OpCompositeExtract %int %37 0 + %39 = OpCompositeExtract %int %37 1 + %40 = OpCompositeConstruct %v2int %38 %39 + OpStore %r0 %40 + OpStore %i %int_0 + OpBranch %44 + %44 = OpLabel + OpLoopMerge %46 %47 None + OpBranch %48 + %48 = OpLabel + %49 = OpLoad %int %i + %51 = OpAccessChain %_ptr_Function_int %r0 %uint_1 + %52 = OpLoad %int %51 + %54 = OpSLessThan %bool %49 %52 + OpBranchConditional %54 %45 %46 + %45 = OpLabel + OpStore %j %int_0 + OpBranch %56 + %56 = OpLabel + OpLoopMerge %58 %59 None + OpBranch %60 + %60 = OpLabel + %61 = OpLoad %int %j + %63 = OpAccessChain %_ptr_Function_int %r0 %uint_0 + %64 = OpLoad %int %63 + %65 = OpSLessThan %bool %61 %64 + OpBranchConditional %65 %57 %58 + %57 = OpLabel + %66 = OpLoad %30 %u0 + %67 = OpLoad %int %i + %68 = OpLoad %int %j + %69 = OpCompositeConstruct %v2int %67 %68 + %71 = OpLoad %v2int %r0 + OpStore %param %71 + OpStore %param_0 %69 + %73 = OpFunctionCall %v2int %get_texcoord_vi2_vi2_ %param %param_0 + %80 = OpAccessChain %_ptr_Uniform_v4float %cb0_1 %int_0 %int_0 + %81 = OpLoad %v4float %80 + %82 = OpVectorShuffle %v4float %81 %81 0 0 0 0 + OpImageWrite %66 %73 %82 + OpBranch %59 + %59 = OpLabel + %83 = OpLoad %int %j + %85 = OpIAdd %int %83 %int_1 + OpStore %j %85 + OpBranch %56 + %58 = OpLabel + OpBranch %47 + %47 = OpLabel + %86 = OpLoad %int %i + %87 = OpIAdd %int %86 %int_1 + OpStore %i %87 + OpBranch %44 + %46 = OpLabel + OpReturn + OpFunctionEnd +%get_texcoord_vi2_vi2_ = OpFunction %v2int None %9 + %base = OpFunctionParameter %_ptr_Function_v2int + %index = OpFunctionParameter %_ptr_Function_v2int + %13 = OpLabel + %14 = OpLoad %v2int %base + %20 = OpLoad %v3int %gl_LocalInvocationID + %21 = OpVectorShuffle %v2int %20 %20 0 1 + %23 = OpIMul %v2int %14 %21 + %24 = OpLoad %v2int %index + %25 = OpIAdd %v2int %23 %24 + OpReturnValue %25 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp b/third_party/spirv-cross/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp new file mode 100644 index 0000000..e793540 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/comp/vector-builtin-type-cast.asm.comp @@ -0,0 +1,128 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 78 +; Schema: 0 + OpCapability Shader + OpCapability ImageQuery + OpCapability StorageImageWriteWithoutFormat + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_LocalInvocationID + OpExecutionMode %main LocalSize 16 16 1 + OpSource GLSL 450 + OpName %main "main" + OpName %r0 "r0" + OpName %u0 "u0" + OpName %i "i" + OpName %j "j" + OpName %gl_LocalInvocationID "gl_LocalInvocationID" + OpName %cb1_struct "cb1_struct" + OpMemberName %cb1_struct 0 "_m0" + OpName %cb0_1 "cb0_1" + OpDecorate %u0 DescriptorSet 0 + OpDecorate %u0 Binding 1 + OpDecorate %u0 NonReadable + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %_arr_v4float_uint_1 ArrayStride 16 + OpMemberDecorate %cb1_struct 0 Offset 0 + OpDecorate %cb1_struct Block + OpDecorate %cb0_1 DescriptorSet 0 + OpDecorate %cb0_1 Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%_ptr_Function_v2int = OpTypePointer Function %v2int + %float = OpTypeFloat 32 + %11 = OpTypeImage %float 2D 0 0 0 2 Unknown +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %u0 = OpVariable %_ptr_UniformConstant_11 UniformConstant + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %v2uint = OpTypeVector %uint 2 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 + %v3int = OpTypeVector %int 3 +%_ptr_Input_v3int = OpTypePointer Input %v3int +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3int Input + %v4float = OpTypeVector %float 4 +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 + %cb1_struct = OpTypeStruct %_arr_v4float_uint_1 +%_ptr_Uniform_cb1_struct = OpTypePointer Uniform %cb1_struct + %cb0_1 = OpVariable %_ptr_Uniform_cb1_struct Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_1 = OpConstant %int 1 + %uint_16 = OpConstant %uint 16 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_16 %uint_16 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %r0 = OpVariable %_ptr_Function_v2int Function + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + %14 = OpLoad %11 %u0 + %15 = OpImageQuerySize %v2int %14 + %19 = OpCompositeConstruct %v2uint %uint_4 %uint_4 + %20 = OpShiftRightArithmetic %v2int %15 %19 + %21 = OpCompositeExtract %int %20 0 + %22 = OpCompositeExtract %int %20 1 + %23 = OpCompositeConstruct %v2int %21 %22 + OpStore %r0 %23 + OpStore %i %int_0 + OpBranch %27 + %27 = OpLabel + OpLoopMerge %29 %30 None + OpBranch %31 + %31 = OpLabel + %32 = OpLoad %int %i + %34 = OpAccessChain %_ptr_Function_int %r0 %uint_1 + %35 = OpLoad %int %34 + %37 = OpSLessThan %bool %32 %35 + OpBranchConditional %37 %28 %29 + %28 = OpLabel + OpStore %j %int_0 + OpBranch %39 + %39 = OpLabel + OpLoopMerge %41 %42 None + OpBranch %43 + %43 = OpLabel + %44 = OpLoad %int %j + %46 = OpAccessChain %_ptr_Function_int %r0 %uint_0 + %47 = OpLoad %int %46 + %48 = OpSLessThan %bool %44 %47 + OpBranchConditional %48 %40 %41 + %40 = OpLabel + %49 = OpLoad %11 %u0 + %50 = OpLoad %v2int %r0 + %54 = OpLoad %v3int %gl_LocalInvocationID + %55 = OpVectorShuffle %v2int %54 %54 0 1 + %57 = OpIMul %v2int %50 %55 + %58 = OpLoad %int %i + %59 = OpLoad %int %j + %60 = OpCompositeConstruct %v2int %58 %59 + %61 = OpIAdd %v2int %57 %60 + %68 = OpAccessChain %_ptr_Uniform_v4float %cb0_1 %int_0 %int_0 + %69 = OpLoad %v4float %68 + %70 = OpVectorShuffle %v4float %69 %69 0 0 0 0 + OpImageWrite %49 %61 %70 + OpBranch %42 + %42 = OpLabel + %71 = OpLoad %int %j + %73 = OpIAdd %int %71 %int_1 + OpStore %j %73 + OpBranch %39 + %41 = OpLabel + OpBranch %30 + %30 = OpLabel + %74 = OpLoad %int %i + %75 = OpIAdd %int %74 %int_1 + OpStore %i %75 + OpBranch %27 + %29 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag new file mode 100644 index 0000000..ba2f95b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/combined-sampler-reuse.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vUV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTex "uTex" + OpName %uSampler "uSampler" + OpName %vUV "vUV" + OpDecorate %FragColor Location 0 + OpDecorate %uTex DescriptorSet 0 + OpDecorate %uTex Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 0 + OpDecorate %vUV Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %uTex = OpVariable %_ptr_UniformConstant_10 UniformConstant + %14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 + %uSampler = OpVariable %_ptr_UniformConstant_14 UniformConstant + %18 = OpTypeSampledImage %10 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_1 = OpConstant %int 1 + %32 = OpConstantComposite %v2int %int_1 %int_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %10 %uTex + %17 = OpLoad %14 %uSampler + %19 = OpSampledImage %18 %13 %17 + %23 = OpLoad %v2float %vUV + %24 = OpImageSampleImplicitLod %v4float %19 %23 + OpStore %FragColor %24 + %28 = OpLoad %v2float %vUV + %33 = OpImageSampleImplicitLod %v4float %19 %28 ConstOffset %32 + %34 = OpLoad %v4float %FragColor + %35 = OpFAdd %v4float %34 %33 + OpStore %FragColor %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/default-member-names.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/default-member-names.asm.frag new file mode 100644 index 0000000..ba493c0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/default-member-names.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 43 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %12 = OpTypeFunction %v4float + %_struct_5 = OpTypeStruct %float + %_struct_6 = OpTypeStruct %float %float %float %float %float %float %float %float %float %float %float %float %_struct_5 +%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_float = OpTypePointer Function %float + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %2 = OpFunction %void None %9 + %22 = OpLabel + %23 = OpVariable %_ptr_Function__struct_6 Function + %24 = OpAccessChain %_ptr_Function_float %23 %int_0 + %25 = OpLoad %float %24 + %26 = OpAccessChain %_ptr_Function_float %23 %int_1 + %27 = OpLoad %float %26 + %28 = OpAccessChain %_ptr_Function_float %23 %int_2 + %29 = OpLoad %float %28 + %30 = OpAccessChain %_ptr_Function_float %23 %int_3 + %31 = OpLoad %float %30 + %32 = OpCompositeConstruct %v4float %25 %27 %29 %31 + OpStore %3 %32 + OpReturn + OpFunctionEnd + %4 = OpFunction %v4float None %12 + %33 = OpLabel + %7 = OpVariable %_ptr_Function__struct_6 Function + %34 = OpAccessChain %_ptr_Function_float %7 %int_0 + %35 = OpLoad %float %34 + %36 = OpAccessChain %_ptr_Function_float %7 %int_1 + %37 = OpLoad %float %36 + %38 = OpAccessChain %_ptr_Function_float %7 %int_2 + %39 = OpLoad %float %38 + %40 = OpAccessChain %_ptr_Function_float %7 %int_3 + %41 = OpLoad %float %40 + %42 = OpCompositeConstruct %v4float %35 %37 %39 %41 + OpReturnValue %42 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag new file mode 100644 index 0000000..7af7605 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/descriptor-array-unnamed.asm.frag @@ -0,0 +1,63 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 39 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %FragColor Location 0 + OpMemberDecorate %SSBO 0 NonWritable + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %ssbos DescriptorSet 0 + OpDecorate %ssbos Binding 5 + OpMemberDecorate %Registers 0 Offset 0 + OpDecorate %Registers Block + OpMemberDecorate %UBO 0 Offset 0 + OpDecorate %UBO Block + OpDecorate %ubos DescriptorSet 0 + OpDecorate %ubos Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %SSBO = OpTypeStruct %v4float + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_SSBO_uint_4 = OpTypeArray %SSBO %uint_4 +%_ptr_Uniform__arr_SSBO_uint_4 = OpTypePointer Uniform %_arr_SSBO_uint_4 + %ssbos = OpVariable %_ptr_Uniform__arr_SSBO_uint_4 Uniform + %int = OpTypeInt 32 1 + %Registers = OpTypeStruct %int +%_ptr_PushConstant_Registers = OpTypePointer PushConstant %Registers + %registers = OpVariable %_ptr_PushConstant_Registers PushConstant + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_int = OpTypePointer PushConstant %int +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %UBO = OpTypeStruct %v4float +%_arr_UBO_uint_4 = OpTypeArray %UBO %uint_4 +%_ptr_Uniform__arr_UBO_uint_4 = OpTypePointer Uniform %_arr_UBO_uint_4 + %ubos = OpVariable %_ptr_Uniform__arr_UBO_uint_4 Uniform +%float_0_200000003 = OpConstant %float 0.200000003 + %36 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003 + %main = OpFunction %void None %3 + %5 = OpLabel + %22 = OpAccessChain %_ptr_PushConstant_int %registers %int_0 + %23 = OpLoad %int %22 + %25 = OpAccessChain %_ptr_Uniform_v4float %ssbos %23 %int_0 + %26 = OpLoad %v4float %25 + %31 = OpAccessChain %_ptr_PushConstant_int %registers %int_0 + %32 = OpLoad %int %31 + %33 = OpAccessChain %_ptr_Uniform_v4float %ubos %32 %int_0 + %34 = OpLoad %v4float %33 + %37 = OpFMul %v4float %34 %36 + %38 = OpFAdd %v4float %26 %37 + OpStore %FragColor %38 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag new file mode 100644 index 0000000..02d0182 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/disable-renamed-output.frag-output.asm.frag @@ -0,0 +1,83 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 37 +; Schema: 0 + OpCapability Shader + OpCapability StencilExportEXT + OpExtension "SPV_EXT_shader_stencil_export" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %o0 %o1 %o2 %o3 %o4 %o5 %o6 %o7 %oDepth %oStencil + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main DepthReplacing + OpSource GLSL 450 + OpSourceExtension "GL_ARB_shader_stencil_export" + OpName %main "main" + OpName %o0 "o0" + OpName %o1 "o1" + OpName %o2 "o2" + OpName %o3 "o3" + OpName %o4 "o4" + OpName %o5 "o5" + OpName %o6 "o6" + OpName %o7 "o7" + OpName %oDepth "oDepth" + OpName %oStencil "oStencil" + OpDecorate %o0 Location 0 + OpDecorate %o1 Location 1 + OpDecorate %o2 Location 2 + OpDecorate %o3 Location 3 + OpDecorate %o4 Location 4 + OpDecorate %o5 Location 5 + OpDecorate %o6 Location 6 + OpDecorate %o7 Location 7 + OpDecorate %oDepth BuiltIn FragDepth + OpDecorate %oStencil BuiltIn FragStencilRefEXT + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %o0 = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 + %o1 = OpVariable %_ptr_Output_v4float Output + %14 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %o2 = OpVariable %_ptr_Output_v4float Output + %16 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1 + %o3 = OpVariable %_ptr_Output_v4float Output + %18 = OpConstantComposite %v4float %float_0 %float_0 %float_1 %float_1 + %o4 = OpVariable %_ptr_Output_v4float Output + %float_0_5 = OpConstant %float 0.5 + %21 = OpConstantComposite %v4float %float_1 %float_0 %float_1 %float_0_5 + %o5 = OpVariable %_ptr_Output_v4float Output + %float_0_25 = OpConstant %float 0.25 + %24 = OpConstantComposite %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25 + %o6 = OpVariable %_ptr_Output_v4float Output + %float_0_75 = OpConstant %float 0.75 + %27 = OpConstantComposite %v4float %float_0_75 %float_0_75 %float_0_75 %float_0_75 + %o7 = OpVariable %_ptr_Output_v4float Output + %29 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_float = OpTypePointer Output %float + %oDepth = OpVariable %_ptr_Output_float Output +%float_0_899999976 = OpConstant %float 0.899999976 + %int = OpTypeInt 32 1 +%_ptr_Output_int = OpTypePointer Output %int + %oStencil = OpVariable %_ptr_Output_int Output + %int_127 = OpConstant %int 127 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %o0 %12 + OpStore %o1 %14 + OpStore %o2 %16 + OpStore %o3 %18 + OpStore %o4 %21 + OpStore %o5 %24 + OpStore %o6 %27 + OpStore %o7 %29 + OpStore %oDepth %float_0_899999976 + OpStore %oStencil %int_127 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..0efd315 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/empty-struct.asm.frag @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.2 +; Generator: Khronos; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %EntryPoint_Main "main" + OpExecutionMode %EntryPoint_Main OriginUpperLeft + OpSource Unknown 100 + OpName %EmptyStructTest "EmptyStructTest" + OpName %GetValue "GetValue" + OpName %GetValue2 "GetValue" + OpName %self "self" + OpName %self2 "self" + OpName %emptyStruct "emptyStruct" + OpName %value "value" + OpName %EntryPoint_Main "EntryPoint_Main" + +%EmptyStructTest = OpTypeStruct +%_ptr_Function_EmptyStructTest = OpTypePointer Function %EmptyStructTest + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %5 = OpTypeFunction %float %_ptr_Function_EmptyStructTest + %6 = OpTypeFunction %float %EmptyStructTest + %void = OpTypeVoid +%_ptr_Function_void = OpTypePointer Function %void + %8 = OpTypeFunction %void %_ptr_Function_EmptyStructTest + %9 = OpTypeFunction %void + %float_0 = OpConstant %float 0 + + %GetValue = OpFunction %float None %5 + %self = OpFunctionParameter %_ptr_Function_EmptyStructTest + %13 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + + %GetValue2 = OpFunction %float None %6 + %self2 = OpFunctionParameter %EmptyStructTest + %14 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + +%EntryPoint_Main = OpFunction %void None %9 + %37 = OpLabel + %emptyStruct = OpVariable %_ptr_Function_EmptyStructTest Function + %18 = OpVariable %_ptr_Function_EmptyStructTest Function + %value = OpVariable %_ptr_Function_float Function + %value2 = OpCompositeConstruct %EmptyStructTest + %22 = OpFunctionCall %float %GetValue %emptyStruct + %23 = OpFunctionCall %float %GetValue2 %value2 + OpStore %value %22 + OpStore %value %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag new file mode 100644 index 0000000..b27f598 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/extract-packed-from-composite.asm.frag @@ -0,0 +1,108 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 64 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %pos_1 %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_vf4_ "@main(vf4;" + OpName %pos "pos" + OpName %Foo "Foo" + OpMemberName %Foo 0 "a" + OpMemberName %Foo 1 "b" + OpName %foo "foo" + OpName %Foo_0 "Foo" + OpMemberName %Foo_0 0 "a" + OpMemberName %Foo_0 1 "b" + OpName %buf "buf" + OpMemberName %buf 0 "results" + OpMemberName %buf 1 "bar" + OpName %_ "" + OpName %pos_0 "pos" + OpName %pos_1 "pos" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %Foo_0 1 Offset 12 + OpDecorate %_arr_Foo_0_uint_16 ArrayStride 16 + OpMemberDecorate %buf 0 Offset 0 + OpMemberDecorate %buf 1 Offset 256 + OpDecorate %buf Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %pos_1 BuiltIn FragCoord + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %9 = OpTypeFunction %v4float %_ptr_Function_v4float + %v3float = OpTypeVector %float 3 + %Foo = OpTypeStruct %v3float %float +%_ptr_Function_Foo = OpTypePointer Function %Foo + %Foo_0 = OpTypeStruct %v3float %float + %uint = OpTypeInt 32 0 + %uint_16 = OpConstant %uint 16 +%_arr_Foo_0_uint_16 = OpTypeArray %Foo_0 %uint_16 + %buf = OpTypeStruct %_arr_Foo_0_uint_16 %v4float +%_ptr_Uniform_buf = OpTypePointer Uniform %buf + %_ = OpVariable %_ptr_Uniform_buf Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %int_16 = OpConstant %int 16 +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %float_0 = OpConstant %float 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %pos_1 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %pos_0 = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_v4float Function + %58 = OpLoad %v4float %pos_1 + OpStore %pos_0 %58 + %62 = OpLoad %v4float %pos_0 + OpStore %param %62 + %63 = OpFunctionCall %v4float %_main_vf4_ %param + OpStore %_entryPointOutput %63 + OpReturn + OpFunctionEnd + %_main_vf4_ = OpFunction %v4float None %9 + %pos = OpFunctionParameter %_ptr_Function_v4float + %12 = OpLabel + %foo = OpVariable %_ptr_Function_Foo Function + %28 = OpAccessChain %_ptr_Function_float %pos %uint_0 + %29 = OpLoad %float %28 + %30 = OpConvertFToS %int %29 + %32 = OpSMod %int %30 %int_16 + %34 = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_0 %32 + %35 = OpLoad %Foo_0 %34 + %36 = OpCompositeExtract %v3float %35 0 + %38 = OpAccessChain %_ptr_Function_v3float %foo %int_0 + OpStore %38 %36 + %39 = OpCompositeExtract %float %35 1 + %41 = OpAccessChain %_ptr_Function_float %foo %int_1 + OpStore %41 %39 + %42 = OpAccessChain %_ptr_Function_v3float %foo %int_0 + %43 = OpLoad %v3float %42 + %45 = OpAccessChain %_ptr_Uniform_v4float %_ %int_1 + %46 = OpLoad %v4float %45 + %47 = OpVectorShuffle %v3float %46 %46 0 1 2 + %48 = OpDot %float %43 %47 + %49 = OpAccessChain %_ptr_Function_float %foo %int_1 + %50 = OpLoad %float %49 + %52 = OpCompositeConstruct %v4float %48 %50 %float_0 %float_0 + OpReturnValue %52 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/frem.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/frem.asm.frag new file mode 100644 index 0000000..8350c75 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/frem.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 16 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA RelaxedPrecision + OpDecorate %vA Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %vB RelaxedPrecision + OpDecorate %vB Location 1 + OpDecorate %14 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vA = OpVariable %_ptr_Input_v4float Input + %vB = OpVariable %_ptr_Input_v4float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpLoad %v4float %vA + %14 = OpLoad %v4float %vB + %15 = OpFRem %v4float %12 %14 + OpStore %FragColor %15 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..397aa98 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,153 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 76 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %foobar_vf4_ "foo" + OpName %a "foo" + OpName %foobar_vf3_ "foo" + OpName %a_0 "foo" + OpName %foobaz_vf4_ "foo" + OpName %a_1 "foo" + OpName %foobaz_vf2_ "foo" + OpName %a_2 "foo" + OpName %a_3 "foo" + OpName %param "foo" + OpName %b "foo" + OpName %param_0 "foo" + OpName %c "foo" + OpName %param_1 "foo" + OpName %d "foo" + OpName %param_2 "foo" + OpName %FragColor "FragColor" + OpDecorate %foobar_vf4_ RelaxedPrecision + OpDecorate %a RelaxedPrecision + OpDecorate %foobar_vf3_ RelaxedPrecision + OpDecorate %a_0 RelaxedPrecision + OpDecorate %foobaz_vf4_ RelaxedPrecision + OpDecorate %a_1 RelaxedPrecision + OpDecorate %foobaz_vf2_ RelaxedPrecision + OpDecorate %a_2 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %a_3 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %b RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %c RelaxedPrecision + OpDecorate %62 RelaxedPrecision + OpDecorate %d RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %69 RelaxedPrecision + OpDecorate %70 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %74 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %9 = OpTypeFunction %v4float %_ptr_Function_v4float + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %15 = OpTypeFunction %v4float %_ptr_Function_v3float + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %24 = OpTypeFunction %v4float %_ptr_Function_v2float + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %53 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %57 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %64 = OpConstantComposite %v2float %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %a_3 = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_v4float Function + %b = OpVariable %_ptr_Function_v4float Function + %param_0 = OpVariable %_ptr_Function_v3float Function + %c = OpVariable %_ptr_Function_v4float Function + %param_1 = OpVariable %_ptr_Function_v4float Function + %d = OpVariable %_ptr_Function_v4float Function + %param_2 = OpVariable %_ptr_Function_v2float Function + OpStore %param %53 + %55 = OpFunctionCall %v4float %foobar_vf4_ %param + OpStore %a_3 %55 + OpStore %param_0 %57 + %59 = OpFunctionCall %v4float %foobar_vf3_ %param_0 + OpStore %b %59 + OpStore %param_1 %53 + %62 = OpFunctionCall %v4float %foobaz_vf4_ %param_1 + OpStore %c %62 + OpStore %param_2 %64 + %66 = OpFunctionCall %v4float %foobaz_vf2_ %param_2 + OpStore %d %66 + %69 = OpLoad %v4float %a_3 + %70 = OpLoad %v4float %b + %71 = OpFAdd %v4float %69 %70 + %72 = OpLoad %v4float %c + %73 = OpFAdd %v4float %71 %72 + %74 = OpLoad %v4float %d + %75 = OpFAdd %v4float %73 %74 + OpStore %FragColor %75 + OpReturn + OpFunctionEnd +%foobar_vf4_ = OpFunction %v4float None %9 + %a = OpFunctionParameter %_ptr_Function_v4float + %12 = OpLabel + %28 = OpLoad %v4float %a + %30 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 + %31 = OpFAdd %v4float %28 %30 + OpReturnValue %31 + OpFunctionEnd +%foobar_vf3_ = OpFunction %v4float None %15 + %a_0 = OpFunctionParameter %_ptr_Function_v3float + %18 = OpLabel + %34 = OpLoad %v3float %a_0 + %35 = OpVectorShuffle %v4float %34 %34 0 1 2 2 + %36 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 + %37 = OpFAdd %v4float %35 %36 + OpReturnValue %37 + OpFunctionEnd +%foobaz_vf4_ = OpFunction %v4float None %9 + %a_1 = OpFunctionParameter %_ptr_Function_v4float + %21 = OpLabel + %40 = OpLoad %v4float %a_1 + %42 = OpCompositeConstruct %v4float %float_2 %float_2 %float_2 %float_2 + %43 = OpFAdd %v4float %40 %42 + OpReturnValue %43 + OpFunctionEnd +%foobaz_vf2_ = OpFunction %v4float None %24 + %a_2 = OpFunctionParameter %_ptr_Function_v2float + %27 = OpLabel + %46 = OpLoad %v2float %a_2 + %47 = OpVectorShuffle %v4float %46 %46 0 1 0 1 + %48 = OpCompositeConstruct %v4float %float_2 %float_2 %float_2 %float_2 + %49 = OpFAdd %v4float %47 %48 + OpReturnValue %49 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..63c8ab5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 19 +; Schema: 0 + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %Size + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %Size "Size" + OpName %uTexture "uTexture" + OpDecorate %Size Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%_ptr_Output_v2int = OpTypePointer Output %v2int + %Size = OpVariable %_ptr_Output_v2int Output + %float = OpTypeFloat 32 + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown + %12 = OpTypeSampledImage %11 +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_12 UniformConstant + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpLoad %12 %uTexture + %17 = OpImage %11 %15 + %18 = OpImageQuerySizeLod %v2int %17 %int_0 + %19 = OpImageQuerySizeLod %v2int %17 %int_1 + %20 = OpIAdd %v2int %18 %19 + OpStore %Size %20 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..ccdfeef --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,81 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 60 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %v0 %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %phi "phi" + OpName %i "i" + OpName %v0 "v0" + OpName %FragColor "FragColor" + OpName %uImage "uImage" + OpDecorate %v0 Location 0 + OpDecorate %FragColor Location 0 + OpDecorate %uImage DescriptorSet 0 + OpDecorate %uImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstant %float 1 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %36 = OpTypeImage %float 2D 0 0 0 1 Unknown + %37 = OpTypeSampledImage %36 +%_ptr_UniformConstant_37 = OpTypePointer UniformConstant %37 + %uImage = OpVariable %_ptr_UniformConstant_37 UniformConstant + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float_2 = OpConstant %float 2 + %int_1 = OpConstant %int 1 + %float_1_vec = OpConstantComposite %v4float %float_1 %float_2 %float_1 %float_2 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpStore %i %int_0 + OpBranch %loop_header + %loop_header = OpLabel + %phi = OpPhi %float %float_1 %5 %phi_plus_2 %continue_block + %tex_phi = OpPhi %v4float %float_1_vec %5 %texture_load_result %continue_block + OpLoopMerge %merge_block %continue_block None + OpBranch %loop_body + %loop_body = OpLabel + OpStore %FragColor %tex_phi + %19 = OpLoad %int %i + %22 = OpSLessThan %bool %19 %int_4 + OpBranchConditional %22 %15 %merge_block + %15 = OpLabel + %26 = OpLoad %int %i + %28 = OpAccessChain %_ptr_Input_float %v0 %26 + %29 = OpLoad %float %28 + %31 = OpFOrdGreaterThan %bool %29 %float_0 + OpBranchConditional %31 %continue_block %merge_block + %continue_block = OpLabel + %40 = OpLoad %37 %uImage + %43 = OpCompositeConstruct %v2float %phi %phi + %texture_load_result = OpImageSampleExplicitLod %v4float %40 %43 Lod %float_0 + %phi_plus_2 = OpFAdd %float %phi %float_2 + %54 = OpLoad %int %i + %56 = OpIAdd %int %54 %int_1 + OpStore %i %56 + OpBranch %loop_header + %merge_block = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..40e5d3a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,29 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 14 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %FragColor = OpVariable %_ptr_Output_v3float Output +%float_0x1p_128 = OpConstant %float 0x1p+128 +%float_n0x1p_128 = OpConstant %float -0x1p+128 +%float_0x1_8p_128 = OpConstant %float 0x1.8p+128 + %13 = OpConstantComposite %v3float %float_0x1p_128 %float_n0x1p_128 %float_0x1_8p_128 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %FragColor %13 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag new file mode 100644 index 0000000..bd6d06f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/interpolation-qualifiers-struct.asm.frag @@ -0,0 +1,85 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 51 +; Schema: 0 + OpCapability Shader + OpCapability SampleRateShading + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %inp + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %Input "Input" + OpMemberName %Input 0 "v0" + OpMemberName %Input 1 "v1" + OpMemberName %Input 2 "v2" + OpMemberName %Input 3 "v3" + OpMemberName %Input 4 "v4" + OpMemberName %Input 5 "v5" + OpMemberName %Input 6 "v6" + OpName %inp "inp" + OpDecorate %FragColor Location 0 + OpDecorate %inp Location 0 + OpMemberDecorate %Input 1 NoPerspective + OpMemberDecorate %Input 2 Centroid + OpMemberDecorate %Input 3 Centroid + OpMemberDecorate %Input 3 NoPerspective + OpMemberDecorate %Input 4 Sample + OpMemberDecorate %Input 5 Sample + OpMemberDecorate %Input 5 NoPerspective + OpMemberDecorate %Input 6 Flat + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %Input = OpTypeStruct %v2float %v2float %v3float %v4float %float %float %float +%_ptr_Input_Input = OpTypePointer Input %Input + %inp = OpVariable %_ptr_Input_Input Input + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %int_1 = OpConstant %int 1 + %uint_1 = OpConstant %uint 1 + %int_2 = OpConstant %int 2 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %int_3 = OpConstant %int 3 + %uint_3 = OpConstant %uint 3 + %int_4 = OpConstant %int 4 + %int_5 = OpConstant %int 5 + %int_6 = OpConstant %int 6 + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_float %inp %int_0 %uint_0 + %21 = OpLoad %float %20 + %24 = OpAccessChain %_ptr_Input_float %inp %int_1 %uint_1 + %25 = OpLoad %float %24 + %26 = OpFAdd %float %21 %25 + %29 = OpAccessChain %_ptr_Input_v3float %inp %int_2 + %30 = OpLoad %v3float %29 + %31 = OpVectorShuffle %v2float %30 %30 0 1 + %34 = OpAccessChain %_ptr_Input_float %inp %int_3 %uint_3 + %35 = OpLoad %float %34 + %37 = OpAccessChain %_ptr_Input_float %inp %int_4 + %38 = OpLoad %float %37 + %39 = OpFMul %float %35 %38 + %41 = OpAccessChain %_ptr_Input_float %inp %int_5 + %42 = OpLoad %float %41 + %43 = OpFAdd %float %39 %42 + %45 = OpAccessChain %_ptr_Input_float %inp %int_6 + %46 = OpLoad %float %45 + %47 = OpFSub %float %43 %46 + %48 = OpCompositeExtract %float %31 0 + %49 = OpCompositeExtract %float %31 1 + %50 = OpCompositeConstruct %v4float %26 %48 %49 %47 + OpStore %FragColor %50 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..9e08e9a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,221 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 7 +; Bound: 83 +; Schema: 0 + OpCapability Shader + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vColor + OpExecutionMode %main OriginUpperLeft + %1 = OpString "test.frag" + OpSource GLSL 450 %1 "// OpModuleProcessed entry-point main +// OpModuleProcessed client vulkan100 +// OpModuleProcessed target-env vulkan1.0 +// OpModuleProcessed entry-point main +#line 1 +#version 450 + +layout(location = 0) in float vColor; +layout(location = 0) out float FragColor; + +void func() +{ + FragColor = 1.0; + FragColor = 2.0; + if (vColor < 0.0) + { + FragColor = 3.0; + } + else + { + FragColor = 4.0; + } + + for (int i = 0; i < 40 + vColor; i += int(vColor) + 5) + { + FragColor += 0.2; + FragColor += 0.3; + } + + switch (int(vColor)) + { + case 0: + FragColor += 0.2; + break; + + case 1: + FragColor += 0.4; + break; + + default: + FragColor += 0.8; + break; + } + + do + { + FragColor += 10.0 + vColor; + } while(FragColor < 100.0); +} + +void main() +{ + func(); +} +" + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %func_ "func(" + OpName %FragColor "FragColor" + OpName %vColor "vColor" + OpName %i "i" + OpDecorate %FragColor Location 0 + OpDecorate %vColor Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 +%_ptr_Input_float = OpTypePointer Input %float + %vColor = OpVariable %_ptr_Input_float Input + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %int = OpTypeInt 32 1 + + ; Should be ignored + OpLine %1 5 0 + +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %float_40 = OpConstant %float 40 +%float_0_200000003 = OpConstant %float 0.200000003 +%float_0_300000012 = OpConstant %float 0.300000012 + %int_5 = OpConstant %int 5 + + ; Should be ignored + OpLine %1 5 0 + +%float_0_400000006 = OpConstant %float 0.400000006 +%float_0_800000012 = OpConstant %float 0.800000012 + %float_10 = OpConstant %float 10 + %float_100 = OpConstant %float 100 + %main = OpFunction %void None %4 + OpLine %1 46 0 + %6 = OpLabel + OpLine %1 48 0 + %82 = OpFunctionCall %void %func_ + OpReturn + OpFunctionEnd + + ; Should be ignored + OpLine %1 5 0 + + %func_ = OpFunction %void None %4 + OpLine %1 6 0 + %8 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpLine %1 8 0 + OpStore %FragColor %float_1 + OpLine %1 9 0 + OpStore %FragColor %float_2 + OpLine %1 10 0 + %16 = OpLoad %float %vColor + %19 = OpFOrdLessThan %bool %16 %float_0 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %23 + %20 = OpLabel + OpLine %1 12 0 + OpStore %FragColor %float_3 + OpBranch %21 + %23 = OpLabel + OpLine %1 16 0 + OpStore %FragColor %float_4 + OpBranch %21 + %21 = OpLabel + OpLine %1 19 0 + OpStore %i %int_0 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %int %i + %35 = OpConvertSToF %float %34 + %37 = OpLoad %float %vColor + %38 = OpFAdd %float %float_40 %37 + %39 = OpFOrdLessThan %bool %35 %38 + OpBranchConditional %39 %30 %31 + %30 = OpLabel + OpLine %1 21 0 + %41 = OpLoad %float %FragColor + %42 = OpFAdd %float %41 %float_0_200000003 + OpStore %FragColor %42 + OpLine %1 22 0 + %44 = OpLoad %float %FragColor + %45 = OpFAdd %float %44 %float_0_300000012 + OpStore %FragColor %45 + OpBranch %32 + %32 = OpLabel + OpLine %1 19 0 + %46 = OpLoad %float %vColor + %47 = OpConvertFToS %int %46 + %49 = OpIAdd %int %47 %int_5 + %50 = OpLoad %int %i + %51 = OpIAdd %int %50 %49 + OpStore %i %51 + OpBranch %29 + %31 = OpLabel + OpLine %1 25 0 + %52 = OpLoad %float %vColor + %53 = OpConvertFToS %int %52 + OpSelectionMerge %57 None + OpSwitch %53 %56 0 %54 1 %55 + %56 = OpLabel + OpLine %1 36 0 + %66 = OpLoad %float %FragColor + %67 = OpFAdd %float %66 %float_0_800000012 + OpStore %FragColor %67 + OpLine %1 37 0 + OpBranch %57 + %54 = OpLabel + OpLine %1 28 0 + %58 = OpLoad %float %FragColor + %59 = OpFAdd %float %58 %float_0_200000003 + OpStore %FragColor %59 + OpLine %1 29 0 + OpBranch %57 + %55 = OpLabel + OpLine %1 32 0 + %62 = OpLoad %float %FragColor + %63 = OpFAdd %float %62 %float_0_400000006 + OpStore %FragColor %63 + OpLine %1 33 0 + OpBranch %57 + %57 = OpLabel + OpBranch %70 + OpLine %1 43 0 + %70 = OpLabel + OpLoopMerge %72 %73 None + OpBranch %71 + %71 = OpLabel + OpLine %1 42 0 + %75 = OpLoad %float %vColor + %76 = OpFAdd %float %float_10 %75 + %77 = OpLoad %float %FragColor + %78 = OpFAdd %float %77 %76 + OpStore %FragColor %78 + OpBranch %73 + %73 = OpLabel + OpLine %1 43 0 + %79 = OpLoad %float %FragColor + %81 = OpFOrdLessThan %bool %79 %float_100 + OpBranchConditional %81 %70 %72 + %72 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/locations-components.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/locations-components.asm.frag new file mode 100644 index 0000000..16bfc52 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/locations-components.asm.frag @@ -0,0 +1,103 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 67 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %8 %16 %22 %28 %33 %o0 + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %v1 "v1" + OpName %v2 "v2" + OpName %o0 "o0" + OpName %r0 "r0" + OpDecorate %8 Location 1 + OpDecorate %16 Location 1 + OpDecorate %16 Component 2 + OpDecorate %22 Location 2 + OpDecorate %22 Flat + OpDecorate %28 Location 2 + OpDecorate %28 Component 1 + OpDecorate %28 Flat + OpDecorate %33 Location 2 + OpDecorate %33 Component 2 + OpDecorate %33 Flat + OpDecorate %o0 Location 0 + %void = OpTypeVoid + %2 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %8 = OpVariable %_ptr_Input_v2float Input + %v4float = OpTypeVector %float 4 +%_ptr_Private_v4float = OpTypePointer Private %v4float + %v1 = OpVariable %_ptr_Private_v4float Private +%_ptr_Input_float = OpTypePointer Input %float + %16 = OpVariable %_ptr_Input_float Input +%_ptr_Private_float = OpTypePointer Private %float + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %22 = OpVariable %_ptr_Input_float Input + %v2 = OpVariable %_ptr_Private_v4float Private + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %28 = OpVariable %_ptr_Input_uint Input + %uint_1 = OpConstant %uint 1 + %33 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %o0 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %int = OpTypeInt 32 1 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float + %main = OpFunction %void None %2 + %4 = OpLabel + %r0 = OpVariable %_ptr_Function_v4float Function + %12 = OpLoad %v2float %8 + %13 = OpLoad %v4float %v1 + %14 = OpVectorShuffle %v4float %13 %12 4 5 2 3 + OpStore %v1 %14 + %17 = OpLoad %float %16 + %21 = OpInBoundsAccessChain %_ptr_Private_float %v1 %uint_2 + OpStore %21 %17 + %24 = OpLoad %float %22 + %26 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_0 + OpStore %26 %24 + %29 = OpLoad %uint %28 + %30 = OpBitcast %float %29 + %32 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_1 + OpStore %32 %30 + %34 = OpLoad %uint %33 + %35 = OpBitcast %float %34 + %36 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_2 + OpStore %36 %35 + %42 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_1 + %43 = OpLoad %float %42 + %44 = OpBitcast %int %43 + %45 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_2 + %46 = OpLoad %float %45 + %47 = OpBitcast %int %46 + %48 = OpIAdd %int %44 %47 + %49 = OpBitcast %float %48 + %51 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_0 + OpStore %51 %49 + %52 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_0 + %53 = OpLoad %float %52 + %54 = OpBitcast %uint %53 + %55 = OpConvertUToF %float %54 + %57 = OpInBoundsAccessChain %_ptr_Output_float %o0 %uint_1 + OpStore %57 %55 + %58 = OpInBoundsAccessChain %_ptr_Private_float %v1 %uint_1 + %59 = OpLoad %float %58 + %60 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_0 + %61 = OpLoad %float %60 + %62 = OpFAdd %float %59 %61 + %63 = OpInBoundsAccessChain %_ptr_Output_float %o0 %uint_0 + OpStore %63 %62 + %64 = OpLoad %v4float %v1 + %65 = OpLoad %v4float %o0 + %66 = OpVectorShuffle %v4float %65 %64 0 1 6 4 + OpStore %o0 %66 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..320e5eb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,195 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 111 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %index + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %index "index" + OpName %indexable "indexable" + OpName %indexable_0 "indexable" + OpName %indexable_1 "indexable" + OpName %foo "foo" + OpName %foobar "foobar" + OpName %baz "baz" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %index RelaxedPrecision + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %20 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %foo RelaxedPrecision + OpDecorate %61 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %79 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + OpDecorate %foobar RelaxedPrecision + OpDecorate %83 RelaxedPrecision + OpDecorate %90 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %93 RelaxedPrecision + OpDecorate %94 RelaxedPrecision + OpDecorate %95 RelaxedPrecision + OpDecorate %baz RelaxedPrecision + OpDecorate %105 RelaxedPrecision + OpDecorate %106 RelaxedPrecision + OpDecorate %108 RelaxedPrecision + OpDecorate %109 RelaxedPrecision + OpDecorate %110 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %uint = OpTypeInt 32 0 + %uint_16 = OpConstant %uint 16 +%_arr_float_uint_16 = OpTypeArray %float %uint_16 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %16 = OpConstantComposite %_arr_float_uint_16 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input +%_ptr_Function__arr_float_uint_16 = OpTypePointer Function %_arr_float_uint_16 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v4float = OpTypeVector %float 4 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 + %float_0 = OpConstant %float 0 + %54 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %55 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_8 = OpConstant %float 8 + %57 = OpConstantComposite %v4float %float_8 %float_8 %float_8 %float_8 + %float_5 = OpConstant %float 5 + %59 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %60 = OpConstantComposite %_arr_v4float_uint_4 %54 %55 %57 %59 + %int_30 = OpConstant %int 30 + %int_3 = OpConstant %int 3 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %float_20 = OpConstant %float 20 + %uint_2 = OpConstant %uint 2 + %97 = OpConstantComposite %v4float %float_20 %float_20 %float_20 %float_20 + %float_30 = OpConstant %float 30 + %99 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 + %float_50 = OpConstant %float 50 + %101 = OpConstantComposite %v4float %float_50 %float_50 %float_50 %float_50 + %float_60 = OpConstant %float 60 + %103 = OpConstantComposite %v4float %float_60 %float_60 %float_60 %float_60 + %104 = OpConstantComposite %_arr_v4float_uint_4 %97 %99 %101 %103 + %main = OpFunction %void None %3 + %5 = OpLabel + %indexable = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_0 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_1 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 + %foo = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %foobar = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %baz = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %20 = OpLoad %int %index + %24 = OpAccessChain %_ptr_Function_float %indexable %20 + %25 = OpLoad %float %24 + OpStore %FragColor %25 + %26 = OpLoad %int %index + %29 = OpSLessThan %bool %26 %int_10 + OpSelectionMerge %31 None + OpBranchConditional %29 %30 %40 + %30 = OpLabel + %32 = OpLoad %int %index + %34 = OpBitwiseXor %int %32 %int_1 + %36 = OpAccessChain %_ptr_Function_float %indexable_0 %34 + %37 = OpLoad %float %36 + %38 = OpLoad %float %FragColor + %39 = OpFAdd %float %38 %37 + OpStore %FragColor %39 + OpBranch %31 + %40 = OpLabel + %41 = OpLoad %int %index + %42 = OpBitwiseAnd %int %41 %int_1 + %44 = OpAccessChain %_ptr_Function_float %indexable_1 %42 + %45 = OpLoad %float %44 + %46 = OpLoad %float %FragColor + %47 = OpFAdd %float %46 %45 + OpStore %FragColor %47 + OpBranch %31 + %31 = OpLabel + %61 = OpLoad %int %index + %63 = OpSGreaterThan %bool %61 %int_30 + OpSelectionMerge %65 None + OpBranchConditional %63 %64 %74 + %64 = OpLabel + %66 = OpLoad %int %index + %68 = OpBitwiseAnd %int %66 %int_3 + %70 = OpAccessChain %_ptr_Function_float %foo %68 %uint_1 + %71 = OpLoad %float %70 + %72 = OpLoad %float %FragColor + %73 = OpFAdd %float %72 %71 + OpStore %FragColor %73 + OpBranch %65 + %74 = OpLabel + %75 = OpLoad %int %index + %76 = OpBitwiseAnd %int %75 %int_1 + %78 = OpAccessChain %_ptr_Function_float %foo %76 %uint_0 + %79 = OpLoad %float %78 + %80 = OpLoad %float %FragColor + %81 = OpFAdd %float %80 %79 + OpStore %FragColor %81 + OpBranch %65 + %65 = OpLabel + %83 = OpLoad %int %index + %84 = OpSGreaterThan %bool %83 %int_30 + OpSelectionMerge %86 None + OpBranchConditional %84 %85 %86 + %85 = OpLabel + %89 = OpAccessChain %_ptr_Function_float %foobar %int_1 %uint_2 + OpStore %89 %float_20 + OpBranch %86 + %86 = OpLabel + %90 = OpLoad %int %index + %91 = OpBitwiseAnd %int %90 %int_3 + %92 = OpAccessChain %_ptr_Function_float %foobar %91 %uint_2 + %93 = OpLoad %float %92 + %94 = OpLoad %float %FragColor + %95 = OpFAdd %float %94 %93 + OpStore %FragColor %95 + OpStore %baz %104 + %105 = OpLoad %int %index + %106 = OpBitwiseAnd %int %105 %int_3 + %107 = OpAccessChain %_ptr_Function_float %baz %106 %uint_2 + %108 = OpLoad %float %107 + %109 = OpLoad %float %FragColor + %110 = OpFAdd %float %109 %108 + OpStore %FragColor %110 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/min-lod.msl22.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/min-lod.msl22.asm.frag new file mode 100644 index 0000000..0d30e16 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/min-lod.msl22.asm.frag @@ -0,0 +1,42 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 21 +; Schema: 0 + OpCapability Shader + OpCapability MinLod + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vUV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uSampler "uSampler" + OpName %vUV "vUV" + OpDecorate %FragColor Location 0 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 0 + OpDecorate %vUV Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %uSampler = OpVariable %_ptr_UniformConstant_11 UniformConstant + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input + %float_4 = OpConstant %float 4 + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpLoad %11 %uSampler + %18 = OpLoad %v2float %vUV + %20 = OpImageSampleImplicitLod %v4float %14 %18 MinLod %float_4 + OpStore %FragColor %20 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/op-constant-null.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/op-constant-null.asm.frag new file mode 100644 index 0000000..61d2e57 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/op-constant-null.asm.frag @@ -0,0 +1,85 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 45 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %a "a" + OpName %b "b" + OpName %c "c" + OpName %D "D" + OpMemberName %D 0 "a" + OpMemberName %D 1 "b" + OpName %d "d" + OpName %e "e" + OpName %FragColor "FragColor" + OpDecorate %a RelaxedPrecision + OpDecorate %b RelaxedPrecision + OpDecorate %c RelaxedPrecision + OpMemberDecorate %D 0 RelaxedPrecision + OpMemberDecorate %D 1 RelaxedPrecision + OpDecorate %e RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %44 RelaxedPrecision + OpDecorate %float_1 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstantNull %float + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %float_2 = OpConstantNull %float + %14 = OpConstantNull %v4float + %v3float = OpTypeVector %float 3 +%mat2v3float = OpTypeMatrix %v3float 2 +%_ptr_Function_mat2v3float = OpTypePointer Function %mat2v3float + %float_4 = OpConstantNull %float + %20 = OpConstantNull %v3float + %float_5 = OpConstantNull %float + %22 = OpConstantNull %v3float + %23 = OpConstantNull %mat2v3float + %D = OpTypeStruct %v4float %float +%_ptr_Function_D = OpTypePointer Function %D + %27 = OpConstantNull %D + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 + %float_10 = OpConstantNull %float + %34 = OpConstantNull %v4float + %float_11 = OpConstantNull %float + %36 = OpConstantNull %v4float + %float_12 = OpConstantNull %float + %38 = OpConstantNull %v4float + %float_13 = OpConstantNull %float + %40 = OpConstantNull %v4float + %41 = OpConstantNull %_arr_v4float_uint_4 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %a = OpVariable %_ptr_Function_float Function + %b = OpVariable %_ptr_Function_v4float Function + %c = OpVariable %_ptr_Function_mat2v3float Function + %d = OpVariable %_ptr_Function_D Function + %e = OpVariable %_ptr_Function__arr_v4float_uint_4 Function + OpStore %a %float_1 + OpStore %b %14 + OpStore %c %23 + OpStore %d %27 + OpStore %e %41 + %44 = OpLoad %float %a + OpStore %FragColor %44 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/op-image-sampled-image.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/op-image-sampled-image.asm.frag new file mode 100644 index 0000000..bf7ec56 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/op-image-sampled-image.asm.frag @@ -0,0 +1,82 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 54 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %o0 + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %t0 "t0" + OpName %o0 "o0" + OpName %r0 "r0" + OpName %push_cb "push_cb" + OpMemberName %push_cb 0 "cb0" + OpName %dummy_sampler "dummy_sampler" + OpDecorate %t0 DescriptorSet 0 + OpDecorate %t0 Binding 2 + OpDecorate %o0 Location 0 + OpDecorate %_arr_v4float_uint_1 ArrayStride 16 + OpDecorate %push_cb Block + OpMemberDecorate %push_cb 0 Offset 0 + OpDecorate %dummy_sampler DescriptorSet 0 + OpDecorate %dummy_sampler Binding 4 + %void = OpTypeVoid + %2 = OpTypeFunction %void + %float = OpTypeFloat 32 + %6 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6 + %t0 = OpVariable %_ptr_UniformConstant_6 UniformConstant + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %o0 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 + %push_cb = OpTypeStruct %_arr_v4float_uint_1 +%_ptr_PushConstant_push_cb = OpTypePointer PushConstant %push_cb + %19 = OpVariable %_ptr_PushConstant_push_cb PushConstant + %uint_0 = OpConstant %uint 0 +%_ptr_PushConstant_v4float = OpTypePointer PushConstant %v4float +%_ptr_PushConstant_float = OpTypePointer PushConstant %float + %int = OpTypeInt 32 1 + %v2float = OpTypeVector %float 2 + %float_0 = OpConstant %float 0 + %30 = OpConstantComposite %v2float %float_0 %float_0 + %33 = OpTypeSampler +%_ptr_UniformConstant_33 = OpTypePointer UniformConstant %33 +%dummy_sampler = OpVariable %_ptr_UniformConstant_33 UniformConstant + %38 = OpTypeSampledImage %6 + %v2int = OpTypeVector %int 2 +%_ptr_Function_float = OpTypePointer Function %float + %uint_3 = OpConstant %uint 3 + %int_n1 = OpConstant %int -1 + %int_n2 = OpConstant %int -2 + %52 = OpConstantComposite %v2int %int_n1 %int_n2 + %main = OpFunction %void None %2 + %4 = OpLabel + %r0 = OpVariable %_ptr_Function_v4float Function + %23 = OpAccessChain %_ptr_PushConstant_v4float %19 %uint_0 %uint_0 + %25 = OpLoad %v4float %23 + %26 = OpLoad %v4float %r0 + %27 = OpVectorShuffle %v4float %26 %25 6 7 2 3 + OpStore %r0 %27 + %31 = OpLoad %v4float %r0 + %32 = OpVectorShuffle %v4float %31 %30 0 1 4 5 + OpStore %r0 %32 + %36 = OpLoad %6 %t0 + %37 = OpLoad %33 %dummy_sampler + %39 = OpSampledImage %38 %36 %37 + %40 = OpImage %6 %39 + %41 = OpLoad %v4float %r0 + %42 = OpVectorShuffle %v2float %41 %41 0 1 + %44 = OpBitcast %v2int %42 + %47 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_3 + %48 = OpLoad %float %47 + %49 = OpBitcast %int %48 + %54 = OpImageFetch %v4float %40 %44 Lod|ConstOffset %49 %52 + OpStore %o0 %54 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..083c85d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 32 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %add_value_f1_f1_ "add_value(f1;f1;" + OpName %v "v" + OpName %w "w" + OpName %FragColor "FragColor" + OpName %Registers "Registers" + OpMemberName %Registers 0 "foo" + OpName %registers "registers" + OpDecorate %FragColor Location 0 + OpMemberDecorate %Registers 0 Offset 0 + OpDecorate %Registers Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %8 = OpTypeFunction %float %float %float +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %float_10 = OpConstant %float 10 + %Registers = OpTypeStruct %float +%_ptr_PushConstant_Registers = OpTypePointer PushConstant %Registers + %registers = OpVariable %_ptr_PushConstant_Registers PushConstant + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_float = OpTypePointer PushConstant %float + %main = OpFunction %void None %3 + %5 = OpLabel + %29 = OpAccessChain %_ptr_PushConstant_float %registers %int_0 + %30 = OpLoad %float %29 + %31 = OpFunctionCall %float %add_value_f1_f1_ %float_10 %30 + OpStore %FragColor %31 + OpReturn + OpFunctionEnd +%add_value_f1_f1_ = OpFunction %float None %8 + %v = OpFunctionParameter %float + %w = OpFunctionParameter %float + %12 = OpLabel + %15 = OpFAdd %float %v %w + OpReturnValue %15 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/phi-loop-variable.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/phi-loop-variable.asm.frag new file mode 100644 index 0000000..74c46b4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/phi-loop-variable.asm.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 59 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%mat2v2float = OpTypeMatrix %v2float 2 +%_ptr_Function_mat2v2float = OpTypePointer Function %mat2v2float + %v3float = OpTypeVector %float 3 + %11 = OpTypeFunction %v3float %_ptr_Function_mat2v2float +%_ptr_Function_v3float = OpTypePointer Function %v3float + %float_1 = OpConstant %float 1 + %18 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_35 = OpConstant %int 35 + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %4 = OpFunction %void None %3 + %5 = OpLabel + OpBranch %48 + %48 = OpLabel + %58 = OpPhi %int %int_35 %5 %56 %50 + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %53 = OpSGreaterThanEqual %bool %58 %int_0 + OpBranchConditional %53 %54 %49 + %54 = OpLabel + OpBranch %50 + %50 = OpLabel + %56 = OpISub %int %58 %int_1 + OpBranch %48 + %49 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %v3float None %11 + %12 = OpFunctionParameter %_ptr_Function_mat2v2float + %14 = OpLabel + %16 = OpVariable %_ptr_Function_v3float Function + %21 = OpVariable %_ptr_Function_int Function + OpStore %16 %18 + OpStore %21 %int_35 + OpBranch %23 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %28 = OpLoad %int %21 + %31 = OpSGreaterThanEqual %bool %28 %int_0 + OpBranchConditional %31 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %32 = OpLoad %int %21 + %34 = OpISub %int %32 %int_1 + OpStore %21 %34 + OpBranch %23 + %25 = OpLabel + %35 = OpLoad %v3float %16 + OpReturnValue %35 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag b/third_party/spirv-cross/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag new file mode 100644 index 0000000..9f03d77 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/pull-model-interpolation.asm.msl23.frag @@ -0,0 +1,425 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 325 +; Schema: 0 + OpCapability Shader + OpCapability SampleRateShading + OpCapability InterpolationFunction + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %baz %a %s %foo %sid %bar %b %c + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %func_ "func(" + OpName %FragColor "FragColor" + OpName %baz "baz" + OpName %a "a" + OpName %_ "" + OpMemberName %_ 0 "x" + OpMemberName %_ 1 "y" + OpMemberName %_ 2 "z" + OpMemberName %_ 3 "u" + OpMemberName %_ 4 "v" + OpMemberName %_ 5 "w" + OpName %s "s" + OpName %foo "foo" + OpName %sid "sid" + OpName %bar "bar" + OpName %b "b" + OpName %c "c" + OpDecorate %FragColor Location 0 + OpDecorate %baz Sample + OpDecorate %baz Location 2 + OpDecorate %a Location 4 + OpDecorate %s Location 10 + OpDecorate %foo NoPerspective + OpDecorate %foo Location 0 + OpDecorate %sid Flat + OpDecorate %sid Location 3 + OpDecorate %bar Centroid + OpDecorate %bar Location 1 + OpDecorate %b Centroid + OpDecorate %b Location 6 + OpDecorate %c Sample + OpDecorate %c Location 8 + OpMemberDecorate %_ 1 Centroid + OpMemberDecorate %_ 1 NoPerspective + OpMemberDecorate %_ 2 Sample + OpMemberDecorate %_ 3 Centroid + OpMemberDecorate %_ 4 Sample + OpMemberDecorate %_ 4 NoPerspective + %void = OpTypeVoid + %15 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %baz = OpVariable %_ptr_Input_v2float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_float = OpTypePointer Output %float + %uint_1 = OpConstant %uint 1 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 +%float_n0_100000001 = OpConstant %float -0.100000001 +%float_0_100000001 = OpConstant %float 0.100000001 + %30 = OpConstantComposite %v2float %float_n0_100000001 %float_0_100000001 + %uint_2 = OpConstant %uint 2 +%_arr_v2float_uint_2 = OpTypeArray %v2float %uint_2 +%_ptr_Input__arr_v2float_uint_2 = OpTypePointer Input %_arr_v2float_uint_2 + %a = OpVariable %_ptr_Input__arr_v2float_uint_2 Input + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_ptr_Input__arr_v4float_uint_2 = OpTypePointer Input %_arr_v4float_uint_2 + %uint_3 = OpConstant %uint 3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 + %_ = OpTypeStruct %v4float %v4float %v4float %_arr_v4float_uint_2 %_arr_v2float_uint_2 %_arr_float_uint_3 +%_ptr_Input__ = OpTypePointer Input %_ + %s = OpVariable %_ptr_Input__ Input +%_ptr_Input_v4float = OpTypePointer Input %v4float + %foo = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_int = OpTypePointer Input %int + %sid = OpVariable %_ptr_Input_int Input + %44 = OpConstantComposite %v2float %float_0_100000001 %float_0_100000001 + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %bar = OpVariable %_ptr_Input_v3float Input + %47 = OpConstantComposite %v2float %float_n0_100000001 %float_n0_100000001 + %b = OpVariable %_ptr_Input__arr_v2float_uint_2 Input + %c = OpVariable %_ptr_Input__arr_v2float_uint_2 Input + %int_4 = OpConstant %int 4 + %int_5 = OpConstant %int 5 + %main = OpFunction %void None %15 + %50 = OpLabel + %51 = OpLoad %v4float %foo + OpStore %FragColor %51 + %52 = OpExtInst %v4float %1 InterpolateAtCentroid %foo + %53 = OpLoad %v4float %FragColor + %54 = OpFAdd %v4float %53 %52 + OpStore %FragColor %54 + %55 = OpLoad %int %sid + %56 = OpExtInst %v4float %1 InterpolateAtSample %foo %55 + %57 = OpLoad %v4float %FragColor + %58 = OpFAdd %v4float %57 %56 + OpStore %FragColor %58 + %59 = OpExtInst %v4float %1 InterpolateAtOffset %foo %44 + %60 = OpLoad %v4float %FragColor + %61 = OpFAdd %v4float %60 %59 + OpStore %FragColor %61 + %62 = OpLoad %v3float %bar + %63 = OpLoad %v4float %FragColor + %64 = OpVectorShuffle %v3float %63 %63 0 1 2 + %65 = OpFAdd %v3float %64 %62 + %66 = OpLoad %v4float %FragColor + %67 = OpVectorShuffle %v4float %66 %65 4 5 6 3 + OpStore %FragColor %67 + %68 = OpExtInst %v3float %1 InterpolateAtCentroid %bar + %69 = OpLoad %v4float %FragColor + %70 = OpVectorShuffle %v3float %69 %69 0 1 2 + %71 = OpFAdd %v3float %70 %68 + %72 = OpLoad %v4float %FragColor + %73 = OpVectorShuffle %v4float %72 %71 4 5 6 3 + OpStore %FragColor %73 + %74 = OpLoad %int %sid + %75 = OpExtInst %v3float %1 InterpolateAtSample %bar %74 + %76 = OpLoad %v4float %FragColor + %77 = OpVectorShuffle %v3float %76 %76 0 1 2 + %78 = OpFAdd %v3float %77 %75 + %79 = OpLoad %v4float %FragColor + %80 = OpVectorShuffle %v4float %79 %78 4 5 6 3 + OpStore %FragColor %80 + %81 = OpExtInst %v3float %1 InterpolateAtOffset %bar %47 + %82 = OpLoad %v4float %FragColor + %83 = OpVectorShuffle %v3float %82 %82 0 1 2 + %84 = OpFAdd %v3float %83 %81 + %85 = OpLoad %v4float %FragColor + %86 = OpVectorShuffle %v4float %85 %84 4 5 6 3 + OpStore %FragColor %86 + %87 = OpAccessChain %_ptr_Input_v2float %b %int_0 + %88 = OpLoad %v2float %87 + %89 = OpLoad %v4float %FragColor + %90 = OpVectorShuffle %v2float %89 %89 0 1 + %91 = OpFAdd %v2float %90 %88 + %92 = OpLoad %v4float %FragColor + %93 = OpVectorShuffle %v4float %92 %91 4 5 2 3 + OpStore %FragColor %93 + %94 = OpAccessChain %_ptr_Input_v2float %b %int_1 + %95 = OpExtInst %v2float %1 InterpolateAtCentroid %94 + %96 = OpLoad %v4float %FragColor + %97 = OpVectorShuffle %v2float %96 %96 0 1 + %98 = OpFAdd %v2float %97 %95 + %99 = OpLoad %v4float %FragColor + %100 = OpVectorShuffle %v4float %99 %98 4 5 2 3 + OpStore %FragColor %100 + %101 = OpAccessChain %_ptr_Input_v2float %b %int_0 + %102 = OpExtInst %v2float %1 InterpolateAtSample %101 %int_2 + %103 = OpLoad %v4float %FragColor + %104 = OpVectorShuffle %v2float %103 %103 0 1 + %105 = OpFAdd %v2float %104 %102 + %106 = OpLoad %v4float %FragColor + %107 = OpVectorShuffle %v4float %106 %105 4 5 2 3 + OpStore %FragColor %107 + %108 = OpAccessChain %_ptr_Input_v2float %b %int_1 + %109 = OpExtInst %v2float %1 InterpolateAtOffset %108 %30 + %110 = OpLoad %v4float %FragColor + %111 = OpVectorShuffle %v2float %110 %110 0 1 + %112 = OpFAdd %v2float %111 %109 + %113 = OpLoad %v4float %FragColor + %114 = OpVectorShuffle %v4float %113 %112 4 5 2 3 + OpStore %FragColor %114 + %115 = OpAccessChain %_ptr_Input_v2float %c %int_0 + %116 = OpLoad %v2float %115 + %117 = OpLoad %v4float %FragColor + %118 = OpVectorShuffle %v2float %117 %117 0 1 + %119 = OpFAdd %v2float %118 %116 + %120 = OpLoad %v4float %FragColor + %121 = OpVectorShuffle %v4float %120 %119 4 5 2 3 + OpStore %FragColor %121 + %122 = OpAccessChain %_ptr_Input_v2float %c %int_1 + %123 = OpExtInst %v2float %1 InterpolateAtCentroid %122 + %124 = OpVectorShuffle %v2float %123 %123 0 1 + %125 = OpLoad %v4float %FragColor + %126 = OpVectorShuffle %v2float %125 %125 0 1 + %127 = OpFAdd %v2float %126 %124 + %128 = OpLoad %v4float %FragColor + %129 = OpVectorShuffle %v4float %128 %127 4 5 2 3 + OpStore %FragColor %129 + %130 = OpAccessChain %_ptr_Input_v2float %c %int_0 + %131 = OpExtInst %v2float %1 InterpolateAtSample %130 %int_2 + %132 = OpVectorShuffle %v2float %131 %131 1 0 + %133 = OpLoad %v4float %FragColor + %134 = OpVectorShuffle %v2float %133 %133 0 1 + %135 = OpFAdd %v2float %134 %132 + %136 = OpLoad %v4float %FragColor + %137 = OpVectorShuffle %v4float %136 %135 4 5 2 3 + OpStore %FragColor %137 + %138 = OpAccessChain %_ptr_Input_v2float %c %int_1 + %139 = OpExtInst %v2float %1 InterpolateAtOffset %138 %30 + %140 = OpVectorShuffle %v2float %139 %139 0 0 + %141 = OpLoad %v4float %FragColor + %142 = OpVectorShuffle %v2float %141 %141 0 1 + %143 = OpFAdd %v2float %142 %140 + %144 = OpLoad %v4float %FragColor + %145 = OpVectorShuffle %v4float %144 %143 4 5 2 3 + OpStore %FragColor %145 + %146 = OpAccessChain %_ptr_Input_v4float %s %int_0 + %147 = OpLoad %v4float %146 + %148 = OpLoad %v4float %FragColor + %149 = OpFAdd %v4float %148 %147 + OpStore %FragColor %149 + %150 = OpAccessChain %_ptr_Input_v4float %s %int_0 + %151 = OpExtInst %v4float %1 InterpolateAtCentroid %150 + %152 = OpLoad %v4float %FragColor + %153 = OpFAdd %v4float %152 %151 + OpStore %FragColor %153 + %154 = OpAccessChain %_ptr_Input_v4float %s %int_0 + %155 = OpLoad %int %sid + %156 = OpExtInst %v4float %1 InterpolateAtSample %154 %155 + %157 = OpLoad %v4float %FragColor + %158 = OpFAdd %v4float %157 %156 + OpStore %FragColor %158 + %159 = OpAccessChain %_ptr_Input_v4float %s %int_0 + %160 = OpExtInst %v4float %1 InterpolateAtOffset %159 %44 + %161 = OpLoad %v4float %FragColor + %162 = OpFAdd %v4float %161 %160 + OpStore %FragColor %162 + %163 = OpAccessChain %_ptr_Input_v4float %s %int_1 + %164 = OpLoad %v4float %163 + %165 = OpLoad %v4float %FragColor + %166 = OpFAdd %v4float %165 %164 + OpStore %FragColor %166 + %167 = OpAccessChain %_ptr_Input_v4float %s %int_1 + %168 = OpExtInst %v4float %1 InterpolateAtCentroid %167 + %169 = OpLoad %v4float %FragColor + %170 = OpFAdd %v4float %169 %168 + OpStore %FragColor %170 + %171 = OpAccessChain %_ptr_Input_v4float %s %int_1 + %172 = OpLoad %int %sid + %173 = OpExtInst %v4float %1 InterpolateAtSample %171 %172 + %174 = OpLoad %v4float %FragColor + %175 = OpFAdd %v4float %174 %173 + OpStore %FragColor %175 + %176 = OpAccessChain %_ptr_Input_v4float %s %int_1 + %177 = OpExtInst %v4float %1 InterpolateAtOffset %176 %47 + %178 = OpLoad %v4float %FragColor + %179 = OpFAdd %v4float %178 %177 + OpStore %FragColor %179 + %180 = OpAccessChain %_ptr_Input_v2float %s %int_4 %int_0 + %181 = OpLoad %v2float %180 + %182 = OpLoad %v4float %FragColor + %183 = OpVectorShuffle %v2float %182 %182 0 1 + %184 = OpFAdd %v2float %183 %181 + %185 = OpLoad %v4float %FragColor + %186 = OpVectorShuffle %v4float %185 %184 4 5 2 3 + OpStore %FragColor %186 + %187 = OpAccessChain %_ptr_Input_v2float %s %int_4 %int_1 + %188 = OpExtInst %v2float %1 InterpolateAtCentroid %187 + %189 = OpLoad %v4float %FragColor + %190 = OpVectorShuffle %v2float %189 %189 0 1 + %191 = OpFAdd %v2float %190 %188 + %192 = OpLoad %v4float %FragColor + %193 = OpVectorShuffle %v4float %192 %191 4 5 2 3 + OpStore %FragColor %193 + %194 = OpAccessChain %_ptr_Input_v2float %s %int_4 %int_0 + %195 = OpExtInst %v2float %1 InterpolateAtSample %194 %int_2 + %196 = OpLoad %v4float %FragColor + %197 = OpVectorShuffle %v2float %196 %196 0 1 + %198 = OpFAdd %v2float %197 %195 + %199 = OpLoad %v4float %FragColor + %200 = OpVectorShuffle %v4float %199 %198 4 5 2 3 + OpStore %FragColor %200 + %201 = OpAccessChain %_ptr_Input_v2float %s %int_4 %int_1 + %202 = OpExtInst %v2float %1 InterpolateAtOffset %201 %30 + %203 = OpLoad %v4float %FragColor + %204 = OpVectorShuffle %v2float %203 %203 0 1 + %205 = OpFAdd %v2float %204 %202 + %206 = OpLoad %v4float %FragColor + %207 = OpVectorShuffle %v4float %206 %205 4 5 2 3 + OpStore %FragColor %207 + %208 = OpAccessChain %_ptr_Input_float %s %int_5 %int_0 + %209 = OpLoad %float %208 + %210 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + %211 = OpLoad %float %210 + %212 = OpFAdd %float %211 %209 + %213 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + OpStore %213 %212 + %214 = OpAccessChain %_ptr_Input_float %s %int_5 %int_1 + %215 = OpExtInst %float %1 InterpolateAtCentroid %214 + %216 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + %217 = OpLoad %float %216 + %218 = OpFAdd %float %217 %215 + %219 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + OpStore %219 %218 + %220 = OpAccessChain %_ptr_Input_float %s %int_5 %int_0 + %221 = OpExtInst %float %1 InterpolateAtSample %220 %int_2 + %222 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + %223 = OpLoad %float %222 + %224 = OpFAdd %float %223 %221 + %225 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + OpStore %225 %224 + %226 = OpAccessChain %_ptr_Input_float %s %int_5 %int_1 + %227 = OpExtInst %float %1 InterpolateAtOffset %226 %30 + %228 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + %229 = OpLoad %float %228 + %230 = OpFAdd %float %229 %227 + %231 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + OpStore %231 %230 + %232 = OpFunctionCall %void %func_ + OpReturn + OpFunctionEnd + %func_ = OpFunction %void None %15 + %233 = OpLabel + %234 = OpLoad %v2float %baz + %235 = OpLoad %v4float %FragColor + %236 = OpVectorShuffle %v2float %235 %235 0 1 + %237 = OpFAdd %v2float %236 %234 + %238 = OpLoad %v4float %FragColor + %239 = OpVectorShuffle %v4float %238 %237 4 5 2 3 + OpStore %FragColor %239 + %240 = OpAccessChain %_ptr_Input_float %baz %uint_0 + %241 = OpExtInst %float %1 InterpolateAtCentroid %240 + %242 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + %243 = OpLoad %float %242 + %244 = OpFAdd %float %243 %241 + %245 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + OpStore %245 %244 + %246 = OpAccessChain %_ptr_Input_float %baz %uint_1 + %247 = OpExtInst %float %1 InterpolateAtSample %246 %int_3 + %248 = OpAccessChain %_ptr_Output_float %FragColor %uint_1 + %249 = OpLoad %float %248 + %250 = OpFAdd %float %249 %247 + %251 = OpAccessChain %_ptr_Output_float %FragColor %uint_1 + OpStore %251 %250 + %252 = OpAccessChain %_ptr_Input_float %baz %uint_1 + %253 = OpExtInst %float %1 InterpolateAtOffset %252 %30 + %254 = OpAccessChain %_ptr_Output_float %FragColor %uint_2 + %255 = OpLoad %float %254 + %256 = OpFAdd %float %255 %253 + %257 = OpAccessChain %_ptr_Output_float %FragColor %uint_2 + OpStore %257 %256 + %258 = OpAccessChain %_ptr_Input_v2float %a %int_1 + %259 = OpExtInst %v2float %1 InterpolateAtCentroid %258 + %260 = OpLoad %v4float %FragColor + %261 = OpVectorShuffle %v2float %260 %260 0 1 + %262 = OpFAdd %v2float %261 %259 + %263 = OpLoad %v4float %FragColor + %264 = OpVectorShuffle %v4float %263 %262 4 5 2 3 + OpStore %FragColor %264 + %265 = OpAccessChain %_ptr_Input_v2float %a %int_0 + %266 = OpExtInst %v2float %1 InterpolateAtSample %265 %int_2 + %267 = OpLoad %v4float %FragColor + %268 = OpVectorShuffle %v2float %267 %267 0 1 + %269 = OpFAdd %v2float %268 %266 + %270 = OpLoad %v4float %FragColor + %271 = OpVectorShuffle %v4float %270 %269 4 5 2 3 + OpStore %FragColor %271 + %272 = OpAccessChain %_ptr_Input_v2float %a %int_1 + %273 = OpExtInst %v2float %1 InterpolateAtOffset %272 %30 + %274 = OpLoad %v4float %FragColor + %275 = OpVectorShuffle %v2float %274 %274 0 1 + %276 = OpFAdd %v2float %275 %273 + %277 = OpLoad %v4float %FragColor + %278 = OpVectorShuffle %v4float %277 %276 4 5 2 3 + OpStore %FragColor %278 + %279 = OpAccessChain %_ptr_Input_v4float %s %int_2 + %280 = OpLoad %v4float %279 + %281 = OpLoad %v4float %FragColor + %282 = OpFAdd %v4float %281 %280 + OpStore %FragColor %282 + %283 = OpAccessChain %_ptr_Input_v4float %s %int_2 + %284 = OpExtInst %v4float %1 InterpolateAtCentroid %283 + %285 = OpVectorShuffle %v2float %284 %284 1 1 + %286 = OpLoad %v4float %FragColor + %287 = OpVectorShuffle %v2float %286 %286 0 1 + %288 = OpFAdd %v2float %287 %285 + %289 = OpLoad %v4float %FragColor + %290 = OpVectorShuffle %v4float %289 %288 4 5 2 3 + OpStore %FragColor %290 + %291 = OpAccessChain %_ptr_Input_v4float %s %int_2 + %292 = OpExtInst %v4float %1 InterpolateAtSample %291 %int_3 + %293 = OpVectorShuffle %v2float %292 %292 0 1 + %294 = OpLoad %v4float %FragColor + %295 = OpVectorShuffle %v2float %294 %294 1 2 + %296 = OpFAdd %v2float %295 %293 + %297 = OpLoad %v4float %FragColor + %298 = OpVectorShuffle %v4float %297 %296 0 4 5 3 + OpStore %FragColor %298 + %299 = OpAccessChain %_ptr_Input_v4float %s %int_2 + %300 = OpExtInst %v4float %1 InterpolateAtOffset %299 %30 + %301 = OpVectorShuffle %v2float %300 %300 3 0 + %302 = OpLoad %v4float %FragColor + %303 = OpVectorShuffle %v2float %302 %302 2 3 + %304 = OpFAdd %v2float %303 %301 + %305 = OpLoad %v4float %FragColor + %306 = OpVectorShuffle %v4float %305 %304 0 1 4 5 + OpStore %FragColor %306 + %308 = OpAccessChain %_ptr_Input_v4float %s %int_3 %int_0 + %309 = OpLoad %v4float %308 + %310 = OpLoad %v4float %FragColor + %311 = OpFAdd %v4float %310 %309 + OpStore %FragColor %311 + %312 = OpAccessChain %_ptr_Input__arr_v4float_uint_2 %s %int_3 + %313 = OpAccessChain %_ptr_Input_v4float %312 %int_1 + %314 = OpExtInst %v4float %1 InterpolateAtCentroid %313 + %315 = OpLoad %v4float %FragColor + %316 = OpFAdd %v4float %315 %314 + OpStore %FragColor %316 + %317 = OpAccessChain %_ptr_Input_v4float %s %int_3 %int_0 + %318 = OpExtInst %v4float %1 InterpolateAtSample %317 %int_2 + %319 = OpLoad %v4float %FragColor + %320 = OpFAdd %v4float %319 %318 + OpStore %FragColor %320 + %321 = OpAccessChain %_ptr_Input_v4float %s %int_3 %int_1 + %322 = OpExtInst %v4float %1 InterpolateAtOffset %321 %30 + %323 = OpLoad %v4float %FragColor + %324 = OpFAdd %v4float %323 %322 + OpStore %FragColor %324 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..16dcd0d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 32 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %g_Texture "g_Texture" + OpName %type_sampler "type.sampler" + OpName %g_Sampler "g_Sampler" + OpName %g_CompareSampler "g_CompareSampler" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %g_Texture DescriptorSet 0 + OpDecorate %g_Texture Binding 0 + OpDecorate %g_Sampler DescriptorSet 0 + OpDecorate %g_Sampler Binding 0 + OpDecorate %g_CompareSampler DescriptorSet 0 + OpDecorate %g_CompareSampler Binding 1 + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %float_0 = OpConstant %float 0 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_float = OpTypePointer Output %float + %void = OpTypeVoid + %19 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + %v4float = OpTypeVector %float 4 + %g_Texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%g_CompareSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %19 + %21 = OpLabel + %22 = OpLoad %v2float %in_var_TEXCOORD0 + %23 = OpLoad %type_2d_image %g_Texture + %24 = OpLoad %type_sampler %g_Sampler + %25 = OpSampledImage %type_sampled_image %23 %24 + %26 = OpImageSampleImplicitLod %v4float %25 %22 None + %27 = OpCompositeExtract %float %26 0 + %28 = OpLoad %type_sampler %g_CompareSampler + %29 = OpSampledImage %type_sampled_image %23 %28 + %30 = OpImageSampleDrefExplicitLod %float %29 %22 %float_0_5 Lod %float_0 + %31 = OpFAdd %float %27 %30 + OpStore %out_var_SV_Target %31 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..0d5b29c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,86 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 54 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %o_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpName %main "main" + OpName %myType "myType" + OpMemberName %myType 0 "data" + OpName %myData "myData" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %index "index" + OpName %elt "elt" + OpName %o_color "o_color" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %o_color Location 0 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %float = OpTypeFloat 32 + %myType = OpTypeStruct %float + %uint = OpTypeInt 32 0 + %uint_5 = OpConstant %uint 5 +%_arr_myType_uint_5 = OpTypeArray %myType %uint_5 +%_ptr_Private__arr_myType_uint_5 = OpTypePointer Private %_arr_myType_uint_5 + %myData = OpVariable %_ptr_Private__arr_myType_uint_5 Private + %float_0 = OpConstant %float 0 + %18 = OpConstantComposite %myType %float_0 + %float_1 = OpConstant %float 1 + %20 = OpConstantComposite %myType %float_1 + %21 = OpConstantComposite %_arr_myType_uint_5 %18 %20 %18 %20 %18 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %float_4 = OpConstant %float 4 +%_ptr_Function_myType = OpTypePointer Function %myType +%_ptr_Private_myType = OpTypePointer Private %myType + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_Output_v4float = OpTypePointer Output %v4float + %o_color = OpVariable %_ptr_Output_v4float Output + %36 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1 + %37 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %main = OpFunction %void None %11 + %38 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %index = OpVariable %_ptr_Function_int Function + %elt = OpVariable %_ptr_Function_myType Function + OpStore %myData %21 + %39 = OpLoad %v4float %gl_FragCoord + %40 = OpVectorShuffle %v2float %39 %39 0 1 + OpStore %uv %40 + %41 = OpAccessChain %_ptr_Function_float %uv %uint_0 + %42 = OpLoad %float %41 + %43 = OpFMod %float %42 %float_4 + %44 = OpConvertFToS %int %43 + OpStore %index %44 + %45 = OpLoad %int %index + %46 = OpAccessChain %_ptr_Private_myType %myData %45 + %47 = OpLoad %myType %46 + OpStore %elt %47 + %48 = OpAccessChain %_ptr_Function_float %elt %int_0 + %49 = OpLoad %float %48 + %50 = OpFOrdGreaterThan %bool %49 %float_0 + OpSelectionMerge %51 None + OpBranchConditional %50 %52 %53 + %52 = OpLabel + OpStore %o_color %36 + OpBranch %51 + %53 = OpLabel + OpStore %o_color %37 + OpBranch %51 + %51 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/srem.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/srem.asm.frag new file mode 100644 index 0000000..c6f8e27 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/srem.asm.frag @@ -0,0 +1,43 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 23 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA Flat + OpDecorate %vA Location 0 + OpDecorate %vB Flat + OpDecorate %vB Location 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 +%_ptr_Input_v4int = OpTypePointer Input %v4int + %vA = OpVariable %_ptr_Input_v4int Input + %vB = OpVariable %_ptr_Input_v4int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpLoad %v4int %vA + %16 = OpLoad %v4int %vB + %17 = OpLoad %v4int %vA + %18 = OpLoad %v4int %vB + %19 = OpSRem %v4int %17 %18 + %20 = OpConvertSToF %v4float %19 + OpStore %FragColor %20 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..7763b7c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColors %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColors "FragColors" + OpName %FragColor "FragColor" + OpDecorate %FragColors Location 0 + OpDecorate %FragColor Location 2 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %17 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 + %float_10 = OpConstant %float 10 + %19 = OpConstantComposite %v4float %float_10 %float_10 %float_10 %float_10 + %20 = OpConstantComposite %_arr_v4float_uint_2 %17 %19 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %float_5 = OpConstant %float 5 + %24 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %FragColors = OpVariable %_ptr_Output__arr_v4float_uint_2 Output %20 + %FragColor = OpVariable %_ptr_Output_v4float Output %24 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..53dc638 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,46 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 26 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTexture "uTexture" + OpName %gl_FragCoord "gl_FragCoord" + OpDecorate %FragColor Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %uTexture = OpVariable %_ptr_UniformConstant_11 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpLoad %11 %uTexture + %18 = OpLoad %v4float %gl_FragCoord + %19 = OpVectorShuffle %v2float %18 %18 0 1 + %22 = OpConvertFToS %v2int %19 + %24 = OpImage %10 %14 + %25 = OpImageFetch %v4float %24 %22 + OpStore %FragColor %25 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag new file mode 100644 index 0000000..e7e6f37 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/texture-sampling-fp16.asm.frag @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 25 +; Schema: 0 + OpCapability Shader + OpCapability StorageInputOutput16 + OpCapability Float16 + OpExtension "SPV_KHR_16bit_storage" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %UV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types_float16" + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTexture "uTexture" + OpName %UV "UV" + OpDecorate %FragColor Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + OpDecorate %UV Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %half = OpTypeFloat 16 + %float = OpTypeFloat 32 + %v4half = OpTypeVector %half 4 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4half = OpTypePointer Output %v4half + %FragColor = OpVariable %_ptr_Output_v4half Output + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown + %12 = OpTypeSampledImage %11 +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_12 UniformConstant + %v2half = OpTypeVector %half 2 +%_ptr_Input_v2half = OpTypePointer Input %v2half + %UV = OpVariable %_ptr_Input_v2half Input + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpLoad %12 %uTexture + %19 = OpLoad %v2half %UV + %23 = OpImageSampleImplicitLod %v4float %15 %19 + %24 = OpFConvert %v4half %23 + OpStore %FragColor %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/undef-variable-store.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/undef-variable-store.asm.frag new file mode 100644 index 0000000..966c2d9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/undef-variable-store.asm.frag @@ -0,0 +1,85 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 50 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %fragmentProgram "main" %_entryPointOutput + OpExecutionMode %fragmentProgram OriginUpperLeft + OpSource HLSL 500 + OpName %fragmentProgram "fragmentProgram" + OpName %_fragmentProgram_ "@fragmentProgram(" + OpName %uv "uv" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %float_0 = OpConstant %float 0 + %15 = OpConstantComposite %v2float %float_0 %float_0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %bool = OpTypeBool + %float_1 = OpConstant %float 1 + %26 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %29 = OpConstantComposite %v4float %float_1 %float_1 %float_0 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %false = OpConstantFalse %bool +%fragmentProgram = OpFunction %void None %3 + %5 = OpLabel + %35 = OpVariable %_ptr_Function_v2float Function + %37 = OpVariable %_ptr_Function_v4float Function + OpBranch %38 + %38 = OpLabel + OpLoopMerge %39 %40 None + OpBranch %41 + %41 = OpLabel + OpStore %35 %15 + %42 = OpAccessChain %_ptr_Function_float %35 %uint_0 + %43 = OpLoad %float %42 + %44 = OpFOrdNotEqual %bool %43 %float_0 + OpSelectionMerge %45 None + OpBranchConditional %44 %46 %47 + %46 = OpLabel + OpStore %37 %26 + OpBranch %39 + %47 = OpLabel + OpStore %37 %29 + OpBranch %39 + %45 = OpLabel + %48 = OpUndef %v4float + OpStore %37 %48 + OpBranch %39 + %40 = OpLabel + OpBranchConditional %false %38 %39 + %39 = OpLabel + %34 = OpLoad %v4float %37 + OpStore %_entryPointOutput %34 + OpReturn + OpFunctionEnd +%_fragmentProgram_ = OpFunction %v4float None %8 + %10 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + OpStore %uv %15 + %19 = OpAccessChain %_ptr_Function_float %uv %uint_0 + %20 = OpLoad %float %19 + %22 = OpFOrdNotEqual %bool %20 %float_0 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %28 + %23 = OpLabel + OpReturnValue %26 + %28 = OpLabel + OpReturnValue %29 + %24 = OpLabel + %31 = OpUndef %v4float + OpReturnValue %31 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/unknown-depth-state.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/unknown-depth-state.asm.frag new file mode 100644 index 0000000..89036f0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/unknown-depth-state.asm.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/unord-relational-op.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/unord-relational-op.asm.frag new file mode 100644 index 0000000..3e4cd6c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/unord-relational-op.asm.frag @@ -0,0 +1,205 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 122 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %c %d %e %f %g %h %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpName %main "main" + OpName %t0 "t0" + OpName %a "a" + OpName %t1 "t1" + OpName %b "b" + OpName %c1 "c1" + OpName %c2 "c2" + OpName %c3 "c3" + OpName %c4 "c4" + OpName %c5 "c5" + OpName %c6 "c6" + OpName %c7 "c7" + OpName %c "c" + OpName %d "d" + OpName %c8 "c8" + OpName %c9 "c9" + OpName %c10 "c10" + OpName %c11 "c11" + OpName %c12 "c12" + OpName %c13 "c13" + OpName %e "e" + OpName %f "f" + OpName %c14 "c14" + OpName %c15 "c15" + OpName %c16 "c16" + OpName %c17 "c17" + OpName %c18 "c18" + OpName %c19 "c19" + OpName %g "g" + OpName %h "h" + OpName %c20 "c20" + OpName %c21 "c21" + OpName %c22 "c22" + OpName %c23 "c23" + OpName %c24 "c24" + OpName %FragColor "FragColor" + OpDecorate %a SpecId 1 + OpDecorate %b SpecId 2 + OpDecorate %c Location 2 + OpDecorate %d Location 3 + OpDecorate %e Location 4 + OpDecorate %f Location 5 + OpDecorate %g Location 6 + OpDecorate %h Location 7 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %a = OpSpecConstant %float 1 + %b = OpSpecConstant %float 2 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %v2bool = OpTypeVector %bool 2 +%_ptr_Function_v2bool = OpTypePointer Function %v2bool + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %c = OpVariable %_ptr_Input_v2float Input + %d = OpVariable %_ptr_Input_v2float Input + %v3bool = OpTypeVector %bool 3 +%_ptr_Function_v3bool = OpTypePointer Function %v3bool + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %e = OpVariable %_ptr_Input_v3float Input + %f = OpVariable %_ptr_Input_v3float Input + %v4bool = OpTypeVector %bool 4 +%_ptr_Function_v4bool = OpTypePointer Function %v4bool + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %g = OpVariable %_ptr_Input_v4float Input + %h = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %t0 = OpVariable %_ptr_Function_float Function + %t1 = OpVariable %_ptr_Function_float Function + %c1 = OpVariable %_ptr_Function_bool Function + %c2 = OpVariable %_ptr_Function_bool Function + %c3 = OpVariable %_ptr_Function_bool Function + %c4 = OpVariable %_ptr_Function_bool Function + %c5 = OpVariable %_ptr_Function_bool Function + %c6 = OpVariable %_ptr_Function_bool Function + %c7 = OpVariable %_ptr_Function_v2bool Function + %c8 = OpVariable %_ptr_Function_v2bool Function + %c9 = OpVariable %_ptr_Function_v2bool Function + %c10 = OpVariable %_ptr_Function_v2bool Function + %c11 = OpVariable %_ptr_Function_v2bool Function + %c12 = OpVariable %_ptr_Function_v2bool Function + %c13 = OpVariable %_ptr_Function_v3bool Function + %c14 = OpVariable %_ptr_Function_v3bool Function + %c15 = OpVariable %_ptr_Function_v3bool Function + %c16 = OpVariable %_ptr_Function_v3bool Function + %c17 = OpVariable %_ptr_Function_v3bool Function + %c18 = OpVariable %_ptr_Function_v3bool Function + %c19 = OpVariable %_ptr_Function_v4bool Function + %c20 = OpVariable %_ptr_Function_v4bool Function + %c21 = OpVariable %_ptr_Function_v4bool Function + %c22 = OpVariable %_ptr_Function_v4bool Function + %c23 = OpVariable %_ptr_Function_v4bool Function + %c24 = OpVariable %_ptr_Function_v4bool Function + OpStore %t0 %a + OpStore %t1 %b + %15 = OpFUnordEqual %bool %a %b + OpStore %c1 %15 + %17 = OpFUnordNotEqual %bool %a %b + OpStore %c2 %17 + %19 = OpFUnordLessThan %bool %a %b + OpStore %c3 %19 + %21 = OpFUnordGreaterThan %bool %a %b + OpStore %c4 %21 + %23 = OpFUnordLessThanEqual %bool %a %b + OpStore %c5 %23 + %25 = OpFUnordGreaterThanEqual %bool %a %b + OpStore %c6 %25 + %32 = OpLoad %v2float %c + %34 = OpLoad %v2float %d + %35 = OpFUnordEqual %v2bool %32 %34 + OpStore %c7 %35 + %37 = OpLoad %v2float %c + %38 = OpLoad %v2float %d + %39 = OpFUnordNotEqual %v2bool %37 %38 + OpStore %c8 %39 + %41 = OpLoad %v2float %c + %42 = OpLoad %v2float %d + %43 = OpFUnordLessThan %v2bool %41 %42 + OpStore %c9 %43 + %45 = OpLoad %v2float %c + %46 = OpLoad %v2float %d + %47 = OpFUnordGreaterThan %v2bool %45 %46 + OpStore %c10 %47 + %49 = OpLoad %v2float %c + %50 = OpLoad %v2float %d + %51 = OpFUnordLessThanEqual %v2bool %49 %50 + OpStore %c11 %51 + %53 = OpLoad %v2float %c + %54 = OpLoad %v2float %d + %55 = OpFUnordGreaterThanEqual %v2bool %53 %54 + OpStore %c12 %55 + %62 = OpLoad %v3float %e + %64 = OpLoad %v3float %f + %65 = OpFUnordEqual %v3bool %62 %64 + OpStore %c13 %65 + %67 = OpLoad %v3float %e + %68 = OpLoad %v3float %f + %69 = OpFUnordNotEqual %v3bool %67 %68 + OpStore %c14 %69 + %71 = OpLoad %v3float %e + %72 = OpLoad %v3float %f + %73 = OpFUnordLessThan %v3bool %71 %72 + OpStore %c15 %73 + %75 = OpLoad %v3float %e + %76 = OpLoad %v3float %f + %77 = OpFUnordGreaterThan %v3bool %75 %76 + OpStore %c16 %77 + %79 = OpLoad %v3float %e + %80 = OpLoad %v3float %f + %81 = OpFUnordLessThanEqual %v3bool %79 %80 + OpStore %c17 %81 + %83 = OpLoad %v3float %e + %84 = OpLoad %v3float %f + %85 = OpFUnordGreaterThanEqual %v3bool %83 %84 + OpStore %c18 %85 + %92 = OpLoad %v4float %g + %94 = OpLoad %v4float %h + %95 = OpFUnordEqual %v4bool %92 %94 + OpStore %c19 %95 + %97 = OpLoad %v4float %g + %98 = OpLoad %v4float %h + %99 = OpFUnordNotEqual %v4bool %97 %98 + OpStore %c20 %99 + %101 = OpLoad %v4float %g + %102 = OpLoad %v4float %h + %103 = OpFUnordLessThan %v4bool %101 %102 + OpStore %c21 %103 + %105 = OpLoad %v4float %g + %106 = OpLoad %v4float %h + %107 = OpFUnordGreaterThan %v4bool %105 %106 + OpStore %c22 %107 + %109 = OpLoad %v4float %g + %110 = OpLoad %v4float %h + %111 = OpFUnordLessThanEqual %v4bool %109 %110 + OpStore %c23 %111 + %113 = OpLoad %v4float %g + %114 = OpLoad %v4float %h + %115 = OpFUnordGreaterThanEqual %v4bool %113 %114 + OpStore %c24 %115 + %118 = OpLoad %float %t0 + %119 = OpLoad %float %t1 + %120 = OpFAdd %float %118 %119 + %121 = OpCompositeConstruct %v4float %120 %120 %120 %120 + OpStore %FragColor %121 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..e2ce2eb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/unreachable.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 47 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %counter %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %counter "counter" + OpName %FragColor "FragColor" + OpDecorate %counter Flat + OpDecorate %counter Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %counter = OpVariable %_ptr_Input_int Input + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %float_10 = OpConstant %float 10 + %21 = OpConstantComposite %v4float %float_10 %float_10 %float_10 %float_10 + %float_30 = OpConstant %float 30 + %25 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %false = OpConstantFalse %bool + %44 = OpUndef %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %33 + %33 = OpLabel + %45 = OpPhi %v4float %44 %5 %44 %35 + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel + %37 = OpLoad %int %counter + %38 = OpIEqual %bool %37 %int_10 + OpSelectionMerge %39 None + OpBranchConditional %38 %40 %41 + %40 = OpLabel + OpBranch %34 + %41 = OpLabel + OpBranch %34 + %39 = OpLabel + OpUnreachable + %35 = OpLabel + OpBranchConditional %false %33 %34 + %34 = OpLabel + %46 = OpPhi %v4float %21 %40 %25 %41 %44 %35 + OpStore %FragColor %46 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag b/third_party/spirv-cross/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag new file mode 100644 index 0000000..9265216 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/frag/vector-shuffle-oom.asm.frag @@ -0,0 +1,886 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 25007 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %5663 "main" %5800 %gl_FragCoord %4317 + OpExecutionMode %5663 OriginUpperLeft + OpMemberDecorate %_struct_1116 0 Offset 0 + OpMemberDecorate %_struct_1116 1 Offset 16 + OpMemberDecorate %_struct_1116 2 Offset 32 + OpDecorate %_struct_1116 Block + OpDecorate %22044 DescriptorSet 0 + OpDecorate %22044 Binding 0 + OpDecorate %5785 DescriptorSet 0 + OpDecorate %5785 Binding 14 + OpDecorate %5688 DescriptorSet 0 + OpDecorate %5688 Binding 6 + OpMemberDecorate %_struct_994 0 Offset 0 + OpMemberDecorate %_struct_994 1 Offset 16 + OpMemberDecorate %_struct_994 2 Offset 28 + OpMemberDecorate %_struct_994 3 Offset 32 + OpMemberDecorate %_struct_994 4 Offset 44 + OpMemberDecorate %_struct_994 5 Offset 48 + OpMemberDecorate %_struct_994 6 Offset 60 + OpMemberDecorate %_struct_994 7 Offset 64 + OpMemberDecorate %_struct_994 8 Offset 76 + OpMemberDecorate %_struct_994 9 Offset 80 + OpMemberDecorate %_struct_994 10 Offset 92 + OpMemberDecorate %_struct_994 11 Offset 96 + OpMemberDecorate %_struct_994 12 Offset 108 + OpMemberDecorate %_struct_994 13 Offset 112 + OpMemberDecorate %_struct_994 14 Offset 120 + OpMemberDecorate %_struct_994 15 Offset 128 + OpMemberDecorate %_struct_994 16 Offset 140 + OpMemberDecorate %_struct_994 17 Offset 144 + OpMemberDecorate %_struct_994 18 Offset 148 + OpMemberDecorate %_struct_994 19 Offset 152 + OpMemberDecorate %_struct_994 20 Offset 156 + OpMemberDecorate %_struct_994 21 Offset 160 + OpMemberDecorate %_struct_994 22 Offset 176 + OpMemberDecorate %_struct_994 23 RowMajor + OpMemberDecorate %_struct_994 23 Offset 192 + OpMemberDecorate %_struct_994 23 MatrixStride 16 + OpMemberDecorate %_struct_994 24 Offset 256 + OpDecorate %_struct_994 Block + OpDecorate %12348 DescriptorSet 0 + OpDecorate %12348 Binding 2 + OpDecorate %3312 DescriptorSet 0 + OpDecorate %3312 Binding 13 + OpDecorate %4646 DescriptorSet 0 + OpDecorate %4646 Binding 5 + OpDecorate %4862 DescriptorSet 0 + OpDecorate %4862 Binding 4 + OpDecorate %3594 DescriptorSet 0 + OpDecorate %3594 Binding 3 + OpDecorate %_arr_mat4v4float_uint_2 ArrayStride 64 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpMemberDecorate %_struct_408 0 RowMajor + OpMemberDecorate %_struct_408 0 Offset 0 + OpMemberDecorate %_struct_408 0 MatrixStride 16 + OpMemberDecorate %_struct_408 1 RowMajor + OpMemberDecorate %_struct_408 1 Offset 64 + OpMemberDecorate %_struct_408 1 MatrixStride 16 + OpMemberDecorate %_struct_408 2 RowMajor + OpMemberDecorate %_struct_408 2 Offset 128 + OpMemberDecorate %_struct_408 2 MatrixStride 16 + OpMemberDecorate %_struct_408 3 RowMajor + OpMemberDecorate %_struct_408 3 Offset 192 + OpMemberDecorate %_struct_408 3 MatrixStride 16 + OpMemberDecorate %_struct_408 4 Offset 256 + OpMemberDecorate %_struct_408 5 Offset 272 + OpMemberDecorate %_struct_408 6 Offset 288 + OpMemberDecorate %_struct_408 7 Offset 292 + OpMemberDecorate %_struct_408 8 Offset 296 + OpMemberDecorate %_struct_408 9 Offset 300 + OpMemberDecorate %_struct_408 10 Offset 304 + OpMemberDecorate %_struct_408 11 Offset 316 + OpMemberDecorate %_struct_408 12 Offset 320 + OpMemberDecorate %_struct_408 13 Offset 332 + OpMemberDecorate %_struct_408 14 Offset 336 + OpMemberDecorate %_struct_408 15 Offset 348 + OpMemberDecorate %_struct_408 16 Offset 352 + OpMemberDecorate %_struct_408 17 Offset 364 + OpMemberDecorate %_struct_408 18 Offset 368 + OpMemberDecorate %_struct_408 19 Offset 372 + OpMemberDecorate %_struct_408 20 Offset 376 + OpMemberDecorate %_struct_408 21 Offset 384 + OpMemberDecorate %_struct_408 22 Offset 392 + OpMemberDecorate %_struct_408 23 Offset 400 + OpMemberDecorate %_struct_408 24 Offset 416 + OpMemberDecorate %_struct_408 25 Offset 424 + OpMemberDecorate %_struct_408 26 Offset 432 + OpMemberDecorate %_struct_408 27 Offset 448 + OpMemberDecorate %_struct_408 28 Offset 460 + OpMemberDecorate %_struct_408 29 Offset 464 + OpMemberDecorate %_struct_408 30 Offset 468 + OpMemberDecorate %_struct_408 31 Offset 472 + OpMemberDecorate %_struct_408 32 Offset 476 + OpMemberDecorate %_struct_408 33 Offset 480 + OpMemberDecorate %_struct_408 34 Offset 488 + OpMemberDecorate %_struct_408 35 Offset 492 + OpMemberDecorate %_struct_408 36 Offset 496 + OpMemberDecorate %_struct_408 37 RowMajor + OpMemberDecorate %_struct_408 37 Offset 512 + OpMemberDecorate %_struct_408 37 MatrixStride 16 + OpMemberDecorate %_struct_408 38 Offset 640 + OpDecorate %_struct_408 Block + OpDecorate %15259 DescriptorSet 0 + OpDecorate %15259 Binding 1 + OpDecorate %5800 Location 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %4317 Location 0 + OpMemberDecorate %_struct_1395 0 Offset 0 + OpMemberDecorate %_struct_1395 1 Offset 16 + OpMemberDecorate %_struct_1395 2 Offset 32 + OpMemberDecorate %_struct_1395 3 Offset 40 + OpMemberDecorate %_struct_1395 4 Offset 48 + OpMemberDecorate %_struct_1395 5 Offset 60 + OpMemberDecorate %_struct_1395 6 Offset 64 + OpMemberDecorate %_struct_1395 7 Offset 76 + OpMemberDecorate %_struct_1395 8 Offset 80 + OpMemberDecorate %_struct_1395 9 Offset 96 + OpMemberDecorate %_struct_1395 10 Offset 112 + OpMemberDecorate %_struct_1395 11 Offset 128 + OpMemberDecorate %_struct_1395 12 Offset 140 + OpMemberDecorate %_struct_1395 13 Offset 144 + OpMemberDecorate %_struct_1395 14 Offset 156 + OpMemberDecorate %_struct_1395 15 Offset 160 + OpMemberDecorate %_struct_1395 16 Offset 176 + OpMemberDecorate %_struct_1395 17 Offset 192 + OpMemberDecorate %_struct_1395 18 Offset 204 + OpMemberDecorate %_struct_1395 19 Offset 208 + OpMemberDecorate %_struct_1395 20 Offset 224 + OpDecorate %_struct_1395 Block + OpMemberDecorate %_struct_1018 0 Offset 0 + OpDecorate %_struct_1018 Block + %void = OpTypeVoid + %1282 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 +%_struct_1017 = OpTypeStruct %v4float +%_struct_1116 = OpTypeStruct %v4float %float %v4float +%_ptr_Uniform__struct_1116 = OpTypePointer Uniform %_struct_1116 + %22044 = OpVariable %_ptr_Uniform__struct_1116 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %150 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_150 = OpTypePointer UniformConstant %150 + %5785 = OpVariable %_ptr_UniformConstant_150 UniformConstant + %508 = OpTypeSampler +%_ptr_UniformConstant_508 = OpTypePointer UniformConstant %508 + %5688 = OpVariable %_ptr_UniformConstant_508 UniformConstant + %510 = OpTypeSampledImage %150 + %float_0 = OpConstant %float 0 + %uint = OpTypeInt 32 0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_1 = OpConstant %float 1 +%mat4v4float = OpTypeMatrix %v4float 4 +%_struct_994 = OpTypeStruct %v3float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v2float %v2float %v3float %float %float %float %float %float %v4float %v4float %mat4v4float %v4float +%_ptr_Uniform__struct_994 = OpTypePointer Uniform %_struct_994 + %12348 = OpVariable %_ptr_Uniform__struct_994 Uniform + %int_5 = OpConstant %int 5 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %3312 = OpVariable %_ptr_UniformConstant_150 UniformConstant + %4646 = OpVariable %_ptr_UniformConstant_508 UniformConstant + %bool = OpTypeBool + %4862 = OpVariable %_ptr_UniformConstant_150 UniformConstant + %3594 = OpVariable %_ptr_UniformConstant_508 UniformConstant + %uint_2 = OpConstant %uint 2 + %2938 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_arr_mat4v4float_uint_2 = OpTypeArray %mat4v4float %uint_2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_struct_408 = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %float %float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float %v2float %v2float %v2float %v4float %v2float %v2float %v2float %v3float %float %float %float %float %float %v2float %float %float %v3float %_arr_mat4v4float_uint_2 %_arr_v4float_uint_2 +%_ptr_Uniform__struct_408 = OpTypePointer Uniform %_struct_408 + %15259 = OpVariable %_ptr_Uniform__struct_408 Uniform + %int_23 = OpConstant %int 23 + %int_2 = OpConstant %int 2 + %float_n2 = OpConstant %float -2 + %float_0_5 = OpConstant %float 0.5 + %1196 = OpConstantComposite %v3float %float_0 %float_n2 %float_0_5 + %float_n1 = OpConstant %float -1 + %836 = OpConstantComposite %v3float %float_n1 %float_n1 %float_0_5 + %float_0_75 = OpConstant %float 0.75 + %1367 = OpConstantComposite %v3float %float_0 %float_n1 %float_0_75 + %141 = OpConstantComposite %v3float %float_1 %float_n1 %float_0_5 + %38 = OpConstantComposite %v3float %float_n2 %float_0 %float_0_5 + %95 = OpConstantComposite %v3float %float_n1 %float_0 %float_0_75 + %626 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %2411 = OpConstantComposite %v3float %float_1 %float_0 %float_0_75 + %float_2 = OpConstant %float 2 + %2354 = OpConstantComposite %v3float %float_2 %float_0 %float_0_5 + %837 = OpConstantComposite %v3float %float_n1 %float_1 %float_0_5 + %1368 = OpConstantComposite %v3float %float_0 %float_1 %float_0_75 + %142 = OpConstantComposite %v3float %float_1 %float_1 %float_0_5 + %1197 = OpConstantComposite %v3float %float_0 %float_2 %float_0_5 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %5800 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4317 = OpVariable %_ptr_Output_v4float Output +%_struct_1395 = OpTypeStruct %v4float %v4float %v2float %v2float %v3float %float %v3float %float %v4float %v4float %v4float %v3float %float %v3float %float %v3float %v4float %v3float %float %v3float %v2float +%_struct_1018 = OpTypeStruct %v4float + %10264 = OpUndef %_struct_1017 + %5663 = OpFunction %void None %1282 + %25006 = OpLabel + %17463 = OpLoad %v4float %gl_FragCoord + %13863 = OpCompositeInsert %_struct_1017 %2938 %10264 0 + %22969 = OpVectorShuffle %v2float %17463 %17463 0 1 + %13206 = OpAccessChain %_ptr_Uniform_v4float %15259 %int_23 + %10343 = OpLoad %v4float %13206 + %7422 = OpVectorShuffle %v2float %10343 %10343 0 1 + %19927 = OpFMul %v2float %22969 %7422 + %18174 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_2 + %16206 = OpLoad %v4float %18174 + %20420 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %21354 = OpLoad %v4float %20420 + %7688 = OpVectorShuffle %v4float %21354 %21354 0 1 0 1 + %17581 = OpFMul %v4float %16206 %7688 + %10673 = OpVectorShuffle %v2float %1196 %1196 0 1 + %18824 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10344 = OpLoad %v4float %18824 + %8638 = OpVectorShuffle %v2float %10344 %10344 0 1 + %9197 = OpFMul %v2float %10673 %8638 + %18505 = OpFAdd %v2float %19927 %9197 + %7011 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21058 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13149 = OpExtInst %v2float %1 FClamp %18505 %7011 %21058 + %23584 = OpLoad %150 %5785 + %10339 = OpLoad %508 %5688 + %12147 = OpSampledImage %510 %23584 %10339 + %15371 = OpImageSampleExplicitLod %v4float %12147 %13149 Lod %float_0 + %15266 = OpCompositeExtract %float %15371 3 + %12116 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12972 = OpLoad %float %12116 + %15710 = OpFMul %float %15266 %12972 + %15279 = OpExtInst %float %1 FClamp %15710 %float_0 %float_1 + %22213 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11756 = OpLoad %v3float %22213 + %12103 = OpVectorTimesScalar %v3float %11756 %15279 + %15516 = OpLoad %150 %3312 + %24569 = OpLoad %508 %4646 + %12148 = OpSampledImage %510 %15516 %24569 + %17670 = OpImageSampleExplicitLod %v4float %12148 %13149 Lod %float_0 + %16938 = OpCompositeExtract %float %17670 1 + %14185 = OpFOrdGreaterThan %bool %16938 %float_0 + OpSelectionMerge %22307 DontFlatten + OpBranchConditional %14185 %12821 %22307 + %12821 = OpLabel + %13239 = OpLoad %150 %4862 + %19960 = OpLoad %508 %3594 + %12149 = OpSampledImage %510 %13239 %19960 + %15675 = OpImageSampleExplicitLod %v4float %12149 %13149 Lod %float_0 + %13866 = OpCompositeExtract %float %17670 1 + %12427 = OpCompositeExtract %float %17670 2 + %23300 = OpFMul %float %13866 %12427 + %17612 = OpExtInst %float %1 FClamp %23300 %float_0 %float_1 + %20291 = OpVectorShuffle %v3float %15675 %15675 0 1 2 + %11186 = OpVectorTimesScalar %v3float %20291 %17612 + %15293 = OpFAdd %v3float %12103 %11186 + OpBranch %22307 + %22307 = OpLabel + %7719 = OpPhi %v3float %12103 %25006 %15293 %12821 + %23399 = OpVectorTimesScalar %v3float %7719 %float_0_5 + %9339 = OpFAdd %float %float_0 %float_0_5 + %16235 = OpVectorShuffle %v3float %2938 %2938 0 1 2 + %22177 = OpFAdd %v3float %16235 %23399 + %15527 = OpVectorShuffle %v4float %2938 %22177 4 5 6 3 + %6434 = OpCompositeInsert %_struct_1017 %15527 %13863 0 + %24572 = OpVectorShuffle %v2float %836 %836 0 1 + %13207 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10345 = OpLoad %v4float %13207 + %8639 = OpVectorShuffle %v2float %10345 %10345 0 1 + %9198 = OpFMul %v2float %24572 %8639 + %18506 = OpFAdd %v2float %19927 %9198 + %7012 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21059 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13150 = OpExtInst %v2float %1 FClamp %18506 %7012 %21059 + %23585 = OpLoad %150 %5785 + %10340 = OpLoad %508 %5688 + %12150 = OpSampledImage %510 %23585 %10340 + %15372 = OpImageSampleExplicitLod %v4float %12150 %13150 Lod %float_0 + %15267 = OpCompositeExtract %float %15372 3 + %12117 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12973 = OpLoad %float %12117 + %15711 = OpFMul %float %15267 %12973 + %15280 = OpExtInst %float %1 FClamp %15711 %float_0 %float_1 + %22214 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11757 = OpLoad %v3float %22214 + %12104 = OpVectorTimesScalar %v3float %11757 %15280 + %15517 = OpLoad %150 %3312 + %24570 = OpLoad %508 %4646 + %12151 = OpSampledImage %510 %15517 %24570 + %17671 = OpImageSampleExplicitLod %v4float %12151 %13150 Lod %float_0 + %16939 = OpCompositeExtract %float %17671 1 + %14186 = OpFOrdGreaterThan %bool %16939 %float_0 + OpSelectionMerge %22308 DontFlatten + OpBranchConditional %14186 %12822 %22308 + %12822 = OpLabel + %13240 = OpLoad %150 %4862 + %19961 = OpLoad %508 %3594 + %12152 = OpSampledImage %510 %13240 %19961 + %15676 = OpImageSampleExplicitLod %v4float %12152 %13150 Lod %float_0 + %13867 = OpCompositeExtract %float %17671 1 + %12428 = OpCompositeExtract %float %17671 2 + %23301 = OpFMul %float %13867 %12428 + %17613 = OpExtInst %float %1 FClamp %23301 %float_0 %float_1 + %20292 = OpVectorShuffle %v3float %15676 %15676 0 1 2 + %11187 = OpVectorTimesScalar %v3float %20292 %17613 + %15294 = OpFAdd %v3float %12104 %11187 + OpBranch %22308 + %22308 = OpLabel + %7720 = OpPhi %v3float %12104 %22307 %15294 %12822 + %23400 = OpVectorTimesScalar %v3float %7720 %float_0_5 + %9340 = OpFAdd %float %9339 %float_0_5 + %16236 = OpVectorShuffle %v3float %15527 %15527 0 1 2 + %22178 = OpFAdd %v3float %16236 %23400 + %15528 = OpVectorShuffle %v4float %15527 %22178 4 5 6 3 + %6435 = OpCompositeInsert %_struct_1017 %15528 %6434 0 + %24573 = OpVectorShuffle %v2float %1367 %1367 0 1 + %13208 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10346 = OpLoad %v4float %13208 + %8640 = OpVectorShuffle %v2float %10346 %10346 0 1 + %9199 = OpFMul %v2float %24573 %8640 + %18507 = OpFAdd %v2float %19927 %9199 + %7013 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21060 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13151 = OpExtInst %v2float %1 FClamp %18507 %7013 %21060 + %23586 = OpLoad %150 %5785 + %10341 = OpLoad %508 %5688 + %12153 = OpSampledImage %510 %23586 %10341 + %15373 = OpImageSampleExplicitLod %v4float %12153 %13151 Lod %float_0 + %15268 = OpCompositeExtract %float %15373 3 + %12118 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12974 = OpLoad %float %12118 + %15712 = OpFMul %float %15268 %12974 + %15281 = OpExtInst %float %1 FClamp %15712 %float_0 %float_1 + %22215 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11758 = OpLoad %v3float %22215 + %12105 = OpVectorTimesScalar %v3float %11758 %15281 + %15518 = OpLoad %150 %3312 + %24571 = OpLoad %508 %4646 + %12154 = OpSampledImage %510 %15518 %24571 + %17672 = OpImageSampleExplicitLod %v4float %12154 %13151 Lod %float_0 + %16940 = OpCompositeExtract %float %17672 1 + %14187 = OpFOrdGreaterThan %bool %16940 %float_0 + OpSelectionMerge %22309 DontFlatten + OpBranchConditional %14187 %12823 %22309 + %12823 = OpLabel + %13241 = OpLoad %150 %4862 + %19962 = OpLoad %508 %3594 + %12155 = OpSampledImage %510 %13241 %19962 + %15677 = OpImageSampleExplicitLod %v4float %12155 %13151 Lod %float_0 + %13868 = OpCompositeExtract %float %17672 1 + %12429 = OpCompositeExtract %float %17672 2 + %23302 = OpFMul %float %13868 %12429 + %17614 = OpExtInst %float %1 FClamp %23302 %float_0 %float_1 + %20293 = OpVectorShuffle %v3float %15677 %15677 0 1 2 + %11188 = OpVectorTimesScalar %v3float %20293 %17614 + %15295 = OpFAdd %v3float %12105 %11188 + OpBranch %22309 + %22309 = OpLabel + %7721 = OpPhi %v3float %12105 %22308 %15295 %12823 + %23401 = OpVectorTimesScalar %v3float %7721 %float_0_75 + %9341 = OpFAdd %float %9340 %float_0_75 + %16237 = OpVectorShuffle %v3float %15528 %15528 0 1 2 + %22179 = OpFAdd %v3float %16237 %23401 + %15529 = OpVectorShuffle %v4float %15528 %22179 4 5 6 3 + %6436 = OpCompositeInsert %_struct_1017 %15529 %6435 0 + %24574 = OpVectorShuffle %v2float %141 %141 0 1 + %13209 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10347 = OpLoad %v4float %13209 + %8641 = OpVectorShuffle %v2float %10347 %10347 0 1 + %9200 = OpFMul %v2float %24574 %8641 + %18508 = OpFAdd %v2float %19927 %9200 + %7014 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21061 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13152 = OpExtInst %v2float %1 FClamp %18508 %7014 %21061 + %23587 = OpLoad %150 %5785 + %10342 = OpLoad %508 %5688 + %12156 = OpSampledImage %510 %23587 %10342 + %15374 = OpImageSampleExplicitLod %v4float %12156 %13152 Lod %float_0 + %15269 = OpCompositeExtract %float %15374 3 + %12119 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12975 = OpLoad %float %12119 + %15713 = OpFMul %float %15269 %12975 + %15282 = OpExtInst %float %1 FClamp %15713 %float_0 %float_1 + %22216 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11759 = OpLoad %v3float %22216 + %12106 = OpVectorTimesScalar %v3float %11759 %15282 + %15519 = OpLoad %150 %3312 + %24575 = OpLoad %508 %4646 + %12157 = OpSampledImage %510 %15519 %24575 + %17673 = OpImageSampleExplicitLod %v4float %12157 %13152 Lod %float_0 + %16941 = OpCompositeExtract %float %17673 1 + %14188 = OpFOrdGreaterThan %bool %16941 %float_0 + OpSelectionMerge %22310 DontFlatten + OpBranchConditional %14188 %12824 %22310 + %12824 = OpLabel + %13242 = OpLoad %150 %4862 + %19963 = OpLoad %508 %3594 + %12158 = OpSampledImage %510 %13242 %19963 + %15678 = OpImageSampleExplicitLod %v4float %12158 %13152 Lod %float_0 + %13869 = OpCompositeExtract %float %17673 1 + %12430 = OpCompositeExtract %float %17673 2 + %23303 = OpFMul %float %13869 %12430 + %17615 = OpExtInst %float %1 FClamp %23303 %float_0 %float_1 + %20294 = OpVectorShuffle %v3float %15678 %15678 0 1 2 + %11189 = OpVectorTimesScalar %v3float %20294 %17615 + %15296 = OpFAdd %v3float %12106 %11189 + OpBranch %22310 + %22310 = OpLabel + %7722 = OpPhi %v3float %12106 %22309 %15296 %12824 + %23402 = OpVectorTimesScalar %v3float %7722 %float_0_5 + %9342 = OpFAdd %float %9341 %float_0_5 + %16238 = OpVectorShuffle %v3float %15529 %15529 0 1 2 + %22180 = OpFAdd %v3float %16238 %23402 + %15530 = OpVectorShuffle %v4float %15529 %22180 4 5 6 3 + %6437 = OpCompositeInsert %_struct_1017 %15530 %6436 0 + %24576 = OpVectorShuffle %v2float %38 %38 0 1 + %13210 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10348 = OpLoad %v4float %13210 + %8642 = OpVectorShuffle %v2float %10348 %10348 0 1 + %9201 = OpFMul %v2float %24576 %8642 + %18509 = OpFAdd %v2float %19927 %9201 + %7015 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21062 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13153 = OpExtInst %v2float %1 FClamp %18509 %7015 %21062 + %23588 = OpLoad %150 %5785 + %10349 = OpLoad %508 %5688 + %12159 = OpSampledImage %510 %23588 %10349 + %15375 = OpImageSampleExplicitLod %v4float %12159 %13153 Lod %float_0 + %15270 = OpCompositeExtract %float %15375 3 + %12120 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12976 = OpLoad %float %12120 + %15714 = OpFMul %float %15270 %12976 + %15283 = OpExtInst %float %1 FClamp %15714 %float_0 %float_1 + %22217 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11760 = OpLoad %v3float %22217 + %12107 = OpVectorTimesScalar %v3float %11760 %15283 + %15520 = OpLoad %150 %3312 + %24577 = OpLoad %508 %4646 + %12160 = OpSampledImage %510 %15520 %24577 + %17674 = OpImageSampleExplicitLod %v4float %12160 %13153 Lod %float_0 + %16942 = OpCompositeExtract %float %17674 1 + %14189 = OpFOrdGreaterThan %bool %16942 %float_0 + OpSelectionMerge %22311 DontFlatten + OpBranchConditional %14189 %12825 %22311 + %12825 = OpLabel + %13243 = OpLoad %150 %4862 + %19964 = OpLoad %508 %3594 + %12161 = OpSampledImage %510 %13243 %19964 + %15679 = OpImageSampleExplicitLod %v4float %12161 %13153 Lod %float_0 + %13870 = OpCompositeExtract %float %17674 1 + %12431 = OpCompositeExtract %float %17674 2 + %23304 = OpFMul %float %13870 %12431 + %17616 = OpExtInst %float %1 FClamp %23304 %float_0 %float_1 + %20295 = OpVectorShuffle %v3float %15679 %15679 0 1 2 + %11190 = OpVectorTimesScalar %v3float %20295 %17616 + %15297 = OpFAdd %v3float %12107 %11190 + OpBranch %22311 + %22311 = OpLabel + %7723 = OpPhi %v3float %12107 %22310 %15297 %12825 + %23403 = OpVectorTimesScalar %v3float %7723 %float_0_5 + %9343 = OpFAdd %float %9342 %float_0_5 + %16239 = OpVectorShuffle %v3float %15530 %15530 0 1 2 + %22181 = OpFAdd %v3float %16239 %23403 + %15531 = OpVectorShuffle %v4float %15530 %22181 4 5 6 3 + %6438 = OpCompositeInsert %_struct_1017 %15531 %6437 0 + %24578 = OpVectorShuffle %v2float %95 %95 0 1 + %13211 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10350 = OpLoad %v4float %13211 + %8643 = OpVectorShuffle %v2float %10350 %10350 0 1 + %9202 = OpFMul %v2float %24578 %8643 + %18510 = OpFAdd %v2float %19927 %9202 + %7016 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21063 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13154 = OpExtInst %v2float %1 FClamp %18510 %7016 %21063 + %23589 = OpLoad %150 %5785 + %10351 = OpLoad %508 %5688 + %12162 = OpSampledImage %510 %23589 %10351 + %15376 = OpImageSampleExplicitLod %v4float %12162 %13154 Lod %float_0 + %15271 = OpCompositeExtract %float %15376 3 + %12121 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12977 = OpLoad %float %12121 + %15715 = OpFMul %float %15271 %12977 + %15284 = OpExtInst %float %1 FClamp %15715 %float_0 %float_1 + %22218 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11761 = OpLoad %v3float %22218 + %12108 = OpVectorTimesScalar %v3float %11761 %15284 + %15521 = OpLoad %150 %3312 + %24579 = OpLoad %508 %4646 + %12163 = OpSampledImage %510 %15521 %24579 + %17675 = OpImageSampleExplicitLod %v4float %12163 %13154 Lod %float_0 + %16943 = OpCompositeExtract %float %17675 1 + %14190 = OpFOrdGreaterThan %bool %16943 %float_0 + OpSelectionMerge %22312 DontFlatten + OpBranchConditional %14190 %12826 %22312 + %12826 = OpLabel + %13244 = OpLoad %150 %4862 + %19965 = OpLoad %508 %3594 + %12164 = OpSampledImage %510 %13244 %19965 + %15680 = OpImageSampleExplicitLod %v4float %12164 %13154 Lod %float_0 + %13871 = OpCompositeExtract %float %17675 1 + %12432 = OpCompositeExtract %float %17675 2 + %23305 = OpFMul %float %13871 %12432 + %17617 = OpExtInst %float %1 FClamp %23305 %float_0 %float_1 + %20296 = OpVectorShuffle %v3float %15680 %15680 0 1 2 + %11191 = OpVectorTimesScalar %v3float %20296 %17617 + %15298 = OpFAdd %v3float %12108 %11191 + OpBranch %22312 + %22312 = OpLabel + %7724 = OpPhi %v3float %12108 %22311 %15298 %12826 + %23404 = OpVectorTimesScalar %v3float %7724 %float_0_75 + %9344 = OpFAdd %float %9343 %float_0_75 + %16240 = OpVectorShuffle %v3float %15531 %15531 0 1 2 + %22182 = OpFAdd %v3float %16240 %23404 + %15532 = OpVectorShuffle %v4float %15531 %22182 4 5 6 3 + %6439 = OpCompositeInsert %_struct_1017 %15532 %6438 0 + %24580 = OpVectorShuffle %v2float %626 %626 0 1 + %13212 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10352 = OpLoad %v4float %13212 + %8644 = OpVectorShuffle %v2float %10352 %10352 0 1 + %9203 = OpFMul %v2float %24580 %8644 + %18511 = OpFAdd %v2float %19927 %9203 + %7017 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21064 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13155 = OpExtInst %v2float %1 FClamp %18511 %7017 %21064 + %23590 = OpLoad %150 %5785 + %10353 = OpLoad %508 %5688 + %12165 = OpSampledImage %510 %23590 %10353 + %15377 = OpImageSampleExplicitLod %v4float %12165 %13155 Lod %float_0 + %15272 = OpCompositeExtract %float %15377 3 + %12122 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12978 = OpLoad %float %12122 + %15716 = OpFMul %float %15272 %12978 + %15285 = OpExtInst %float %1 FClamp %15716 %float_0 %float_1 + %22219 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11762 = OpLoad %v3float %22219 + %12109 = OpVectorTimesScalar %v3float %11762 %15285 + %15522 = OpLoad %150 %3312 + %24581 = OpLoad %508 %4646 + %12166 = OpSampledImage %510 %15522 %24581 + %17676 = OpImageSampleExplicitLod %v4float %12166 %13155 Lod %float_0 + %16944 = OpCompositeExtract %float %17676 1 + %14191 = OpFOrdGreaterThan %bool %16944 %float_0 + OpSelectionMerge %22313 DontFlatten + OpBranchConditional %14191 %12827 %22313 + %12827 = OpLabel + %13245 = OpLoad %150 %4862 + %19966 = OpLoad %508 %3594 + %12167 = OpSampledImage %510 %13245 %19966 + %15681 = OpImageSampleExplicitLod %v4float %12167 %13155 Lod %float_0 + %13872 = OpCompositeExtract %float %17676 1 + %12433 = OpCompositeExtract %float %17676 2 + %23306 = OpFMul %float %13872 %12433 + %17618 = OpExtInst %float %1 FClamp %23306 %float_0 %float_1 + %20297 = OpVectorShuffle %v3float %15681 %15681 0 1 2 + %11192 = OpVectorTimesScalar %v3float %20297 %17618 + %15299 = OpFAdd %v3float %12109 %11192 + OpBranch %22313 + %22313 = OpLabel + %7725 = OpPhi %v3float %12109 %22312 %15299 %12827 + %23405 = OpVectorTimesScalar %v3float %7725 %float_1 + %9345 = OpFAdd %float %9344 %float_1 + %16241 = OpVectorShuffle %v3float %15532 %15532 0 1 2 + %22183 = OpFAdd %v3float %16241 %23405 + %15533 = OpVectorShuffle %v4float %15532 %22183 4 5 6 3 + %6440 = OpCompositeInsert %_struct_1017 %15533 %6439 0 + %24582 = OpVectorShuffle %v2float %2411 %2411 0 1 + %13213 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10354 = OpLoad %v4float %13213 + %8645 = OpVectorShuffle %v2float %10354 %10354 0 1 + %9204 = OpFMul %v2float %24582 %8645 + %18512 = OpFAdd %v2float %19927 %9204 + %7018 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21065 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13156 = OpExtInst %v2float %1 FClamp %18512 %7018 %21065 + %23591 = OpLoad %150 %5785 + %10355 = OpLoad %508 %5688 + %12168 = OpSampledImage %510 %23591 %10355 + %15378 = OpImageSampleExplicitLod %v4float %12168 %13156 Lod %float_0 + %15273 = OpCompositeExtract %float %15378 3 + %12123 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12979 = OpLoad %float %12123 + %15717 = OpFMul %float %15273 %12979 + %15286 = OpExtInst %float %1 FClamp %15717 %float_0 %float_1 + %22220 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11763 = OpLoad %v3float %22220 + %12110 = OpVectorTimesScalar %v3float %11763 %15286 + %15523 = OpLoad %150 %3312 + %24583 = OpLoad %508 %4646 + %12169 = OpSampledImage %510 %15523 %24583 + %17677 = OpImageSampleExplicitLod %v4float %12169 %13156 Lod %float_0 + %16945 = OpCompositeExtract %float %17677 1 + %14192 = OpFOrdGreaterThan %bool %16945 %float_0 + OpSelectionMerge %22314 DontFlatten + OpBranchConditional %14192 %12828 %22314 + %12828 = OpLabel + %13246 = OpLoad %150 %4862 + %19967 = OpLoad %508 %3594 + %12170 = OpSampledImage %510 %13246 %19967 + %15682 = OpImageSampleExplicitLod %v4float %12170 %13156 Lod %float_0 + %13873 = OpCompositeExtract %float %17677 1 + %12434 = OpCompositeExtract %float %17677 2 + %23307 = OpFMul %float %13873 %12434 + %17619 = OpExtInst %float %1 FClamp %23307 %float_0 %float_1 + %20298 = OpVectorShuffle %v3float %15682 %15682 0 1 2 + %11193 = OpVectorTimesScalar %v3float %20298 %17619 + %15300 = OpFAdd %v3float %12110 %11193 + OpBranch %22314 + %22314 = OpLabel + %7726 = OpPhi %v3float %12110 %22313 %15300 %12828 + %23406 = OpVectorTimesScalar %v3float %7726 %float_0_75 + %9346 = OpFAdd %float %9345 %float_0_75 + %16242 = OpVectorShuffle %v3float %15533 %15533 0 1 2 + %22184 = OpFAdd %v3float %16242 %23406 + %15534 = OpVectorShuffle %v4float %15533 %22184 4 5 6 3 + %6441 = OpCompositeInsert %_struct_1017 %15534 %6440 0 + %24584 = OpVectorShuffle %v2float %2354 %2354 0 1 + %13214 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10356 = OpLoad %v4float %13214 + %8646 = OpVectorShuffle %v2float %10356 %10356 0 1 + %9205 = OpFMul %v2float %24584 %8646 + %18513 = OpFAdd %v2float %19927 %9205 + %7019 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21066 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13157 = OpExtInst %v2float %1 FClamp %18513 %7019 %21066 + %23592 = OpLoad %150 %5785 + %10357 = OpLoad %508 %5688 + %12171 = OpSampledImage %510 %23592 %10357 + %15379 = OpImageSampleExplicitLod %v4float %12171 %13157 Lod %float_0 + %15274 = OpCompositeExtract %float %15379 3 + %12124 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12980 = OpLoad %float %12124 + %15718 = OpFMul %float %15274 %12980 + %15287 = OpExtInst %float %1 FClamp %15718 %float_0 %float_1 + %22221 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11764 = OpLoad %v3float %22221 + %12111 = OpVectorTimesScalar %v3float %11764 %15287 + %15524 = OpLoad %150 %3312 + %24585 = OpLoad %508 %4646 + %12172 = OpSampledImage %510 %15524 %24585 + %17678 = OpImageSampleExplicitLod %v4float %12172 %13157 Lod %float_0 + %16946 = OpCompositeExtract %float %17678 1 + %14193 = OpFOrdGreaterThan %bool %16946 %float_0 + OpSelectionMerge %22315 DontFlatten + OpBranchConditional %14193 %12829 %22315 + %12829 = OpLabel + %13247 = OpLoad %150 %4862 + %19968 = OpLoad %508 %3594 + %12173 = OpSampledImage %510 %13247 %19968 + %15683 = OpImageSampleExplicitLod %v4float %12173 %13157 Lod %float_0 + %13874 = OpCompositeExtract %float %17678 1 + %12435 = OpCompositeExtract %float %17678 2 + %23308 = OpFMul %float %13874 %12435 + %17620 = OpExtInst %float %1 FClamp %23308 %float_0 %float_1 + %20299 = OpVectorShuffle %v3float %15683 %15683 0 1 2 + %11194 = OpVectorTimesScalar %v3float %20299 %17620 + %15301 = OpFAdd %v3float %12111 %11194 + OpBranch %22315 + %22315 = OpLabel + %7727 = OpPhi %v3float %12111 %22314 %15301 %12829 + %23407 = OpVectorTimesScalar %v3float %7727 %float_0_5 + %9347 = OpFAdd %float %9346 %float_0_5 + %16243 = OpVectorShuffle %v3float %15534 %15534 0 1 2 + %22185 = OpFAdd %v3float %16243 %23407 + %15535 = OpVectorShuffle %v4float %15534 %22185 4 5 6 3 + %6442 = OpCompositeInsert %_struct_1017 %15535 %6441 0 + %24586 = OpVectorShuffle %v2float %837 %837 0 1 + %13215 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10358 = OpLoad %v4float %13215 + %8647 = OpVectorShuffle %v2float %10358 %10358 0 1 + %9206 = OpFMul %v2float %24586 %8647 + %18514 = OpFAdd %v2float %19927 %9206 + %7020 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21067 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13158 = OpExtInst %v2float %1 FClamp %18514 %7020 %21067 + %23593 = OpLoad %150 %5785 + %10359 = OpLoad %508 %5688 + %12174 = OpSampledImage %510 %23593 %10359 + %15380 = OpImageSampleExplicitLod %v4float %12174 %13158 Lod %float_0 + %15275 = OpCompositeExtract %float %15380 3 + %12125 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12981 = OpLoad %float %12125 + %15719 = OpFMul %float %15275 %12981 + %15288 = OpExtInst %float %1 FClamp %15719 %float_0 %float_1 + %22222 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11765 = OpLoad %v3float %22222 + %12112 = OpVectorTimesScalar %v3float %11765 %15288 + %15525 = OpLoad %150 %3312 + %24587 = OpLoad %508 %4646 + %12175 = OpSampledImage %510 %15525 %24587 + %17679 = OpImageSampleExplicitLod %v4float %12175 %13158 Lod %float_0 + %16947 = OpCompositeExtract %float %17679 1 + %14194 = OpFOrdGreaterThan %bool %16947 %float_0 + OpSelectionMerge %22316 DontFlatten + OpBranchConditional %14194 %12830 %22316 + %12830 = OpLabel + %13248 = OpLoad %150 %4862 + %19969 = OpLoad %508 %3594 + %12176 = OpSampledImage %510 %13248 %19969 + %15684 = OpImageSampleExplicitLod %v4float %12176 %13158 Lod %float_0 + %13875 = OpCompositeExtract %float %17679 1 + %12436 = OpCompositeExtract %float %17679 2 + %23309 = OpFMul %float %13875 %12436 + %17621 = OpExtInst %float %1 FClamp %23309 %float_0 %float_1 + %20300 = OpVectorShuffle %v3float %15684 %15684 0 1 2 + %11195 = OpVectorTimesScalar %v3float %20300 %17621 + %15302 = OpFAdd %v3float %12112 %11195 + OpBranch %22316 + %22316 = OpLabel + %7728 = OpPhi %v3float %12112 %22315 %15302 %12830 + %23408 = OpVectorTimesScalar %v3float %7728 %float_0_5 + %9348 = OpFAdd %float %9347 %float_0_5 + %16244 = OpVectorShuffle %v3float %15535 %15535 0 1 2 + %22186 = OpFAdd %v3float %16244 %23408 + %15536 = OpVectorShuffle %v4float %15535 %22186 4 5 6 3 + %6443 = OpCompositeInsert %_struct_1017 %15536 %6442 0 + %24588 = OpVectorShuffle %v2float %1368 %1368 0 1 + %13216 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10360 = OpLoad %v4float %13216 + %8648 = OpVectorShuffle %v2float %10360 %10360 0 1 + %9207 = OpFMul %v2float %24588 %8648 + %18515 = OpFAdd %v2float %19927 %9207 + %7021 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21068 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13159 = OpExtInst %v2float %1 FClamp %18515 %7021 %21068 + %23594 = OpLoad %150 %5785 + %10361 = OpLoad %508 %5688 + %12177 = OpSampledImage %510 %23594 %10361 + %15381 = OpImageSampleExplicitLod %v4float %12177 %13159 Lod %float_0 + %15276 = OpCompositeExtract %float %15381 3 + %12126 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12982 = OpLoad %float %12126 + %15720 = OpFMul %float %15276 %12982 + %15289 = OpExtInst %float %1 FClamp %15720 %float_0 %float_1 + %22223 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11766 = OpLoad %v3float %22223 + %12113 = OpVectorTimesScalar %v3float %11766 %15289 + %15526 = OpLoad %150 %3312 + %24589 = OpLoad %508 %4646 + %12178 = OpSampledImage %510 %15526 %24589 + %17680 = OpImageSampleExplicitLod %v4float %12178 %13159 Lod %float_0 + %16948 = OpCompositeExtract %float %17680 1 + %14195 = OpFOrdGreaterThan %bool %16948 %float_0 + OpSelectionMerge %22317 DontFlatten + OpBranchConditional %14195 %12831 %22317 + %12831 = OpLabel + %13249 = OpLoad %150 %4862 + %19970 = OpLoad %508 %3594 + %12179 = OpSampledImage %510 %13249 %19970 + %15685 = OpImageSampleExplicitLod %v4float %12179 %13159 Lod %float_0 + %13876 = OpCompositeExtract %float %17680 1 + %12437 = OpCompositeExtract %float %17680 2 + %23310 = OpFMul %float %13876 %12437 + %17622 = OpExtInst %float %1 FClamp %23310 %float_0 %float_1 + %20301 = OpVectorShuffle %v3float %15685 %15685 0 1 2 + %11196 = OpVectorTimesScalar %v3float %20301 %17622 + %15303 = OpFAdd %v3float %12113 %11196 + OpBranch %22317 + %22317 = OpLabel + %7729 = OpPhi %v3float %12113 %22316 %15303 %12831 + %23409 = OpVectorTimesScalar %v3float %7729 %float_0_75 + %9349 = OpFAdd %float %9348 %float_0_75 + %16245 = OpVectorShuffle %v3float %15536 %15536 0 1 2 + %22187 = OpFAdd %v3float %16245 %23409 + %15537 = OpVectorShuffle %v4float %15536 %22187 4 5 6 3 + %6444 = OpCompositeInsert %_struct_1017 %15537 %6443 0 + %24590 = OpVectorShuffle %v2float %142 %142 0 1 + %13217 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10362 = OpLoad %v4float %13217 + %8649 = OpVectorShuffle %v2float %10362 %10362 0 1 + %9208 = OpFMul %v2float %24590 %8649 + %18516 = OpFAdd %v2float %19927 %9208 + %7022 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21069 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13160 = OpExtInst %v2float %1 FClamp %18516 %7022 %21069 + %23595 = OpLoad %150 %5785 + %10363 = OpLoad %508 %5688 + %12180 = OpSampledImage %510 %23595 %10363 + %15382 = OpImageSampleExplicitLod %v4float %12180 %13160 Lod %float_0 + %15277 = OpCompositeExtract %float %15382 3 + %12127 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12983 = OpLoad %float %12127 + %15721 = OpFMul %float %15277 %12983 + %15290 = OpExtInst %float %1 FClamp %15721 %float_0 %float_1 + %22224 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11767 = OpLoad %v3float %22224 + %12114 = OpVectorTimesScalar %v3float %11767 %15290 + %15538 = OpLoad %150 %3312 + %24591 = OpLoad %508 %4646 + %12181 = OpSampledImage %510 %15538 %24591 + %17681 = OpImageSampleExplicitLod %v4float %12181 %13160 Lod %float_0 + %16949 = OpCompositeExtract %float %17681 1 + %14196 = OpFOrdGreaterThan %bool %16949 %float_0 + OpSelectionMerge %22318 DontFlatten + OpBranchConditional %14196 %12832 %22318 + %12832 = OpLabel + %13250 = OpLoad %150 %4862 + %19971 = OpLoad %508 %3594 + %12182 = OpSampledImage %510 %13250 %19971 + %15686 = OpImageSampleExplicitLod %v4float %12182 %13160 Lod %float_0 + %13877 = OpCompositeExtract %float %17681 1 + %12438 = OpCompositeExtract %float %17681 2 + %23311 = OpFMul %float %13877 %12438 + %17623 = OpExtInst %float %1 FClamp %23311 %float_0 %float_1 + %20302 = OpVectorShuffle %v3float %15686 %15686 0 1 2 + %11197 = OpVectorTimesScalar %v3float %20302 %17623 + %15304 = OpFAdd %v3float %12114 %11197 + OpBranch %22318 + %22318 = OpLabel + %7730 = OpPhi %v3float %12114 %22317 %15304 %12832 + %23410 = OpVectorTimesScalar %v3float %7730 %float_0_5 + %9350 = OpFAdd %float %9349 %float_0_5 + %16246 = OpVectorShuffle %v3float %15537 %15537 0 1 2 + %22188 = OpFAdd %v3float %16246 %23410 + %15539 = OpVectorShuffle %v4float %15537 %22188 4 5 6 3 + %6445 = OpCompositeInsert %_struct_1017 %15539 %6444 0 + %24592 = OpVectorShuffle %v2float %1197 %1197 0 1 + %13218 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10364 = OpLoad %v4float %13218 + %8650 = OpVectorShuffle %v2float %10364 %10364 0 1 + %9209 = OpFMul %v2float %24592 %8650 + %18517 = OpFAdd %v2float %19927 %9209 + %7023 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21070 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13161 = OpExtInst %v2float %1 FClamp %18517 %7023 %21070 + %23596 = OpLoad %150 %5785 + %10365 = OpLoad %508 %5688 + %12183 = OpSampledImage %510 %23596 %10365 + %15383 = OpImageSampleExplicitLod %v4float %12183 %13161 Lod %float_0 + %15278 = OpCompositeExtract %float %15383 3 + %12128 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12984 = OpLoad %float %12128 + %15722 = OpFMul %float %15278 %12984 + %15291 = OpExtInst %float %1 FClamp %15722 %float_0 %float_1 + %22225 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11768 = OpLoad %v3float %22225 + %12115 = OpVectorTimesScalar %v3float %11768 %15291 + %15540 = OpLoad %150 %3312 + %24593 = OpLoad %508 %4646 + %12184 = OpSampledImage %510 %15540 %24593 + %17682 = OpImageSampleExplicitLod %v4float %12184 %13161 Lod %float_0 + %16950 = OpCompositeExtract %float %17682 1 + %14197 = OpFOrdGreaterThan %bool %16950 %float_0 + OpSelectionMerge %22319 DontFlatten + OpBranchConditional %14197 %12833 %22319 + %12833 = OpLabel + %13251 = OpLoad %150 %4862 + %19972 = OpLoad %508 %3594 + %12185 = OpSampledImage %510 %13251 %19972 + %15687 = OpImageSampleExplicitLod %v4float %12185 %13161 Lod %float_0 + %13878 = OpCompositeExtract %float %17682 1 + %12439 = OpCompositeExtract %float %17682 2 + %23312 = OpFMul %float %13878 %12439 + %17624 = OpExtInst %float %1 FClamp %23312 %float_0 %float_1 + %20303 = OpVectorShuffle %v3float %15687 %15687 0 1 2 + %11198 = OpVectorTimesScalar %v3float %20303 %17624 + %15305 = OpFAdd %v3float %12115 %11198 + OpBranch %22319 + %22319 = OpLabel + %7731 = OpPhi %v3float %12115 %22318 %15305 %12833 + %23411 = OpVectorTimesScalar %v3float %7731 %float_0_5 + %9351 = OpFAdd %float %9350 %float_0_5 + %16247 = OpVectorShuffle %v3float %15539 %15539 0 1 2 + %22189 = OpFAdd %v3float %16247 %23411 + %15541 = OpVectorShuffle %v4float %15539 %22189 4 5 6 3 + %6719 = OpCompositeInsert %_struct_1017 %15541 %6445 0 + %23412 = OpVectorShuffle %v3float %15541 %15541 0 1 2 + %10833 = OpCompositeConstruct %v3float %9351 %9351 %9351 + %13750 = OpFDiv %v3float %23412 %10833 + %24033 = OpVectorShuffle %v4float %15541 %13750 4 5 6 3 + %8636 = OpCompositeInsert %_struct_1017 %24033 %6719 0 + %16315 = OpCompositeInsert %_struct_1017 %float_1 %8636 0 3 + %11544 = OpCompositeExtract %v4float %16315 0 + OpStore %4317 %11544 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc b/third_party/spirv-cross/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc new file mode 100644 index 0000000..b21a2d3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/tesc/tess-level-overrun.asm.tesc @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 46 +; Schema: 0 + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %main "main" %gl_TessLevelInner %gl_TessLevelOuter + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main Triangles + OpSource ESSL 310 + OpSourceExtension "GL_EXT_shader_io_blocks" + OpSourceExtension "GL_EXT_tessellation_shader" + OpName %main "main" + OpName %gl_TessLevelInner "gl_TessLevelInner" + OpName %TessLevels "TessLevels" + OpMemberName %TessLevels 0 "inner0" + OpMemberName %TessLevels 1 "inner1" + OpMemberName %TessLevels 2 "outer0" + OpMemberName %TessLevels 3 "outer1" + OpMemberName %TessLevels 4 "outer2" + OpMemberName %TessLevels 5 "outer3" + OpName %sb_levels "sb_levels" + OpName %gl_TessLevelOuter "gl_TessLevelOuter" + OpDecorate %gl_TessLevelInner Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpMemberDecorate %TessLevels 0 Restrict + OpMemberDecorate %TessLevels 0 NonWritable + OpMemberDecorate %TessLevels 0 Offset 0 + OpMemberDecorate %TessLevels 1 Restrict + OpMemberDecorate %TessLevels 1 NonWritable + OpMemberDecorate %TessLevels 1 Offset 4 + OpMemberDecorate %TessLevels 2 Restrict + OpMemberDecorate %TessLevels 2 NonWritable + OpMemberDecorate %TessLevels 2 Offset 8 + OpMemberDecorate %TessLevels 3 Restrict + OpMemberDecorate %TessLevels 3 NonWritable + OpMemberDecorate %TessLevels 3 Offset 12 + OpMemberDecorate %TessLevels 4 Restrict + OpMemberDecorate %TessLevels 4 NonWritable + OpMemberDecorate %TessLevels 4 Offset 16 + OpMemberDecorate %TessLevels 5 Restrict + OpMemberDecorate %TessLevels 5 NonWritable + OpMemberDecorate %TessLevels 5 Offset 20 + OpDecorate %TessLevels Block + OpDecorate %sb_levels DescriptorSet 0 + OpDecorate %sb_levels Binding 0 + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %TessLevels = OpTypeStruct %float %float %float %float %float %float +%_ptr_StorageBuffer_TessLevels = OpTypePointer StorageBuffer %TessLevels + %sb_levels = OpVariable %_ptr_StorageBuffer_TessLevels StorageBuffer +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +%_ptr_Output_float = OpTypePointer Output %float + %int_1 = OpConstant %int 1 + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 + %int_4 = OpConstant %int 4 + %int_5 = OpConstant %int 5 + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_0 + %19 = OpLoad %float %18 + %21 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %int_0 + OpStore %21 %19 + %23 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_1 + %24 = OpLoad %float %23 + %25 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %int_1 + OpStore %25 %24 + %31 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_2 + %32 = OpLoad %float %31 + %33 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_0 + OpStore %33 %32 + %35 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_3 + %36 = OpLoad %float %35 + %37 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_1 + OpStore %37 %36 + %39 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_4 + %40 = OpLoad %float %39 + %41 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_2 + OpStore %41 %40 + %43 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_5 + %44 = OpLoad %float %43 + %45 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_3 + OpStore %45 %44 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc b/third_party/spirv-cross/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc new file mode 100644 index 0000000..b21a2d3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/tesc/tess-level-overrun.multi-patch.asm.tesc @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 46 +; Schema: 0 + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %main "main" %gl_TessLevelInner %gl_TessLevelOuter + OpExecutionMode %main OutputVertices 1 + OpExecutionMode %main Triangles + OpSource ESSL 310 + OpSourceExtension "GL_EXT_shader_io_blocks" + OpSourceExtension "GL_EXT_tessellation_shader" + OpName %main "main" + OpName %gl_TessLevelInner "gl_TessLevelInner" + OpName %TessLevels "TessLevels" + OpMemberName %TessLevels 0 "inner0" + OpMemberName %TessLevels 1 "inner1" + OpMemberName %TessLevels 2 "outer0" + OpMemberName %TessLevels 3 "outer1" + OpMemberName %TessLevels 4 "outer2" + OpMemberName %TessLevels 5 "outer3" + OpName %sb_levels "sb_levels" + OpName %gl_TessLevelOuter "gl_TessLevelOuter" + OpDecorate %gl_TessLevelInner Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpMemberDecorate %TessLevels 0 Restrict + OpMemberDecorate %TessLevels 0 NonWritable + OpMemberDecorate %TessLevels 0 Offset 0 + OpMemberDecorate %TessLevels 1 Restrict + OpMemberDecorate %TessLevels 1 NonWritable + OpMemberDecorate %TessLevels 1 Offset 4 + OpMemberDecorate %TessLevels 2 Restrict + OpMemberDecorate %TessLevels 2 NonWritable + OpMemberDecorate %TessLevels 2 Offset 8 + OpMemberDecorate %TessLevels 3 Restrict + OpMemberDecorate %TessLevels 3 NonWritable + OpMemberDecorate %TessLevels 3 Offset 12 + OpMemberDecorate %TessLevels 4 Restrict + OpMemberDecorate %TessLevels 4 NonWritable + OpMemberDecorate %TessLevels 4 Offset 16 + OpMemberDecorate %TessLevels 5 Restrict + OpMemberDecorate %TessLevels 5 NonWritable + OpMemberDecorate %TessLevels 5 Offset 20 + OpDecorate %TessLevels Block + OpDecorate %sb_levels DescriptorSet 0 + OpDecorate %sb_levels Binding 0 + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %TessLevels = OpTypeStruct %float %float %float %float %float %float +%_ptr_StorageBuffer_TessLevels = OpTypePointer StorageBuffer %TessLevels + %sb_levels = OpVariable %_ptr_StorageBuffer_TessLevels StorageBuffer +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +%_ptr_Output_float = OpTypePointer Output %float + %int_1 = OpConstant %int 1 + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 + %int_4 = OpConstant %int 4 + %int_5 = OpConstant %int 5 + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_0 + %19 = OpLoad %float %18 + %21 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %int_0 + OpStore %21 %19 + %23 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_1 + %24 = OpLoad %float %23 + %25 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %int_1 + OpStore %25 %24 + %31 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_2 + %32 = OpLoad %float %31 + %33 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_0 + OpStore %33 %32 + %35 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_3 + %36 = OpLoad %float %35 + %37 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_1 + OpStore %37 %36 + %39 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_4 + %40 = OpLoad %float %39 + %41 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_2 + OpStore %41 %40 + %43 = OpAccessChain %_ptr_StorageBuffer_float %sb_levels %int_5 + %44 = OpLoad %float %43 + %45 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %int_3 + OpStore %45 %44 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese b/third_party/spirv-cross/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese new file mode 100644 index 0000000..956d2a6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese @@ -0,0 +1,96 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 72 +; Schema: 0 + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %main "main" %_ %gl_TessCoord %gl_TessLevelInner %gl_TessLevelOuter + OpExecutionMode %main Quads + OpExecutionMode %main SpacingFractionalEven + OpExecutionMode %main VertexOrderCw + OpSource ESSL 310 + OpSourceExtension "GL_EXT_shader_io_blocks" + OpSourceExtension "GL_EXT_tessellation_shader" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpDecorate %gl_PerVertex Block + OpDecorate %gl_TessCoord BuiltIn TessCoord + OpDecorate %gl_TessLevelInner Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%gl_PerVertex = OpTypeStruct %v4float %float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float +%gl_TessCoord = OpVariable %_ptr_Input_v3float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Input__arr_float_uint_2 = OpTypePointer Input %_arr_float_uint_2 +%gl_TessLevelInner = OpVariable %_ptr_Input__arr_float_uint_2 Input + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Input__arr_float_uint_4 = OpTypePointer Input %_arr_float_uint_4 +%gl_TessLevelOuter = OpVariable %_ptr_Input__arr_float_uint_4 Input + %float_1 = OpConstant %float 1 + %int_2 = OpConstant %int 2 + %uint_1 = OpConstant %uint 1 + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 + %float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Input_float %gl_TessCoord %uint_0 + %20 = OpLoad %float %19 + %25 = OpAccessChain %_ptr_Input_float %gl_TessLevelInner %int_0 + %26 = OpLoad %float %25 + %27 = OpFMul %float %20 %26 + %32 = OpAccessChain %_ptr_Input_float %gl_TessLevelOuter %int_0 + %33 = OpLoad %float %32 + %34 = OpFMul %float %27 %33 + %36 = OpAccessChain %_ptr_Input_float %gl_TessCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpFSub %float %float_1 %37 + %39 = OpAccessChain %_ptr_Input_float %gl_TessLevelInner %int_0 + %40 = OpLoad %float %39 + %41 = OpFMul %float %38 %40 + %43 = OpAccessChain %_ptr_Input_float %gl_TessLevelOuter %int_2 + %44 = OpLoad %float %43 + %45 = OpFMul %float %41 %44 + %46 = OpFAdd %float %34 %45 + %48 = OpAccessChain %_ptr_Input_float %gl_TessCoord %uint_1 + %49 = OpLoad %float %48 + %51 = OpAccessChain %_ptr_Input_float %gl_TessLevelInner %int_1 + %52 = OpLoad %float %51 + %53 = OpFMul %float %49 %52 + %54 = OpAccessChain %_ptr_Input_float %gl_TessLevelOuter %int_1 + %55 = OpLoad %float %54 + %56 = OpFMul %float %53 %55 + %57 = OpAccessChain %_ptr_Input_float %gl_TessCoord %uint_1 + %58 = OpLoad %float %57 + %59 = OpFSub %float %float_1 %58 + %60 = OpAccessChain %_ptr_Input_float %gl_TessLevelInner %int_1 + %61 = OpLoad %float %60 + %62 = OpFMul %float %59 %61 + %64 = OpAccessChain %_ptr_Input_float %gl_TessLevelOuter %int_3 + %65 = OpLoad %float %64 + %66 = OpFMul %float %62 %65 + %67 = OpFAdd %float %56 %66 + %69 = OpCompositeConstruct %v4float %46 %67 %float_0 %float_1 + %71 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %71 %69 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert new file mode 100644 index 0000000..59ec3f9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/clip-distance-plain-variable.asm.vert @@ -0,0 +1,91 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 56 +; Schema: 0 + OpCapability Shader + OpCapability ClipDistance + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %pos_1 %_entryPointOutput_pos %_entryPointOutput_clip + OpSource HLSL 500 + OpName %main "main" + OpName %VSOut "VSOut" + OpMemberName %VSOut 0 "pos" + OpMemberName %VSOut 1 "clip" + OpName %_main_vf4_ "@main(vf4;" + OpName %pos "pos" + OpName %vout "vout" + OpName %pos_0 "pos" + OpName %pos_1 "pos" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %_entryPointOutput_pos "@entryPointOutput.pos" + OpName %_entryPointOutput_clip "@entryPointOutput.clip" + OpDecorate %pos_1 Location 0 + OpDecorate %_entryPointOutput_pos BuiltIn Position + OpDecorate %_entryPointOutput_clip BuiltIn ClipDistance + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %v2float = OpTypeVector %float 2 + %VSOut = OpTypeStruct %v4float %v2float + %11 = OpTypeFunction %VSOut %_ptr_Function_v4float +%_ptr_Function_VSOut = OpTypePointer Function %VSOut + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v4float = OpTypePointer Input %v4float + %pos_1 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_pos = OpVariable %_ptr_Output_v4float Output + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_entryPointOutput_clip = OpVariable %_ptr_Output__arr_float_uint_2 Output + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %pos_0 = OpVariable %_ptr_Function_v4float Function +%flattenTemp = OpVariable %_ptr_Function_VSOut Function + %param = OpVariable %_ptr_Function_v4float Function + %32 = OpLoad %v4float %pos_1 + OpStore %pos_0 %32 + %35 = OpLoad %v4float %pos_0 + OpStore %param %35 + %36 = OpFunctionCall %VSOut %_main_vf4_ %param + OpStore %flattenTemp %36 + %39 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %40 = OpLoad %v4float %39 + OpStore %_entryPointOutput_pos %40 + %48 = OpAccessChain %_ptr_Function_float %flattenTemp %int_1 %uint_0 + %49 = OpLoad %float %48 + %51 = OpAccessChain %_ptr_Output_float %_entryPointOutput_clip %int_0 + OpStore %51 %49 + %53 = OpAccessChain %_ptr_Function_float %flattenTemp %int_1 %uint_1 + %54 = OpLoad %float %53 + %55 = OpAccessChain %_ptr_Output_float %_entryPointOutput_clip %int_1 + OpStore %55 %54 + OpReturn + OpFunctionEnd + %_main_vf4_ = OpFunction %VSOut None %11 + %pos = OpFunctionParameter %_ptr_Function_v4float + %14 = OpLabel + %vout = OpVariable %_ptr_Function_VSOut Function + %19 = OpLoad %v4float %pos + %20 = OpAccessChain %_ptr_Function_v4float %vout %int_0 + OpStore %20 %19 + %22 = OpLoad %v4float %pos + %23 = OpVectorShuffle %v2float %22 %22 0 1 + %25 = OpAccessChain %_ptr_Function_v2float %vout %int_1 + OpStore %25 %23 + %26 = OpLoad %VSOut %vout + OpReturnValue %26 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert new file mode 100644 index 0000000..59ec3f9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/clip-distance-plain-variable.no-user-varying.asm.vert @@ -0,0 +1,91 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 56 +; Schema: 0 + OpCapability Shader + OpCapability ClipDistance + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %pos_1 %_entryPointOutput_pos %_entryPointOutput_clip + OpSource HLSL 500 + OpName %main "main" + OpName %VSOut "VSOut" + OpMemberName %VSOut 0 "pos" + OpMemberName %VSOut 1 "clip" + OpName %_main_vf4_ "@main(vf4;" + OpName %pos "pos" + OpName %vout "vout" + OpName %pos_0 "pos" + OpName %pos_1 "pos" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %_entryPointOutput_pos "@entryPointOutput.pos" + OpName %_entryPointOutput_clip "@entryPointOutput.clip" + OpDecorate %pos_1 Location 0 + OpDecorate %_entryPointOutput_pos BuiltIn Position + OpDecorate %_entryPointOutput_clip BuiltIn ClipDistance + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %v2float = OpTypeVector %float 2 + %VSOut = OpTypeStruct %v4float %v2float + %11 = OpTypeFunction %VSOut %_ptr_Function_v4float +%_ptr_Function_VSOut = OpTypePointer Function %VSOut + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v4float = OpTypePointer Input %v4float + %pos_1 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_pos = OpVariable %_ptr_Output_v4float Output + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_entryPointOutput_clip = OpVariable %_ptr_Output__arr_float_uint_2 Output + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %pos_0 = OpVariable %_ptr_Function_v4float Function +%flattenTemp = OpVariable %_ptr_Function_VSOut Function + %param = OpVariable %_ptr_Function_v4float Function + %32 = OpLoad %v4float %pos_1 + OpStore %pos_0 %32 + %35 = OpLoad %v4float %pos_0 + OpStore %param %35 + %36 = OpFunctionCall %VSOut %_main_vf4_ %param + OpStore %flattenTemp %36 + %39 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %40 = OpLoad %v4float %39 + OpStore %_entryPointOutput_pos %40 + %48 = OpAccessChain %_ptr_Function_float %flattenTemp %int_1 %uint_0 + %49 = OpLoad %float %48 + %51 = OpAccessChain %_ptr_Output_float %_entryPointOutput_clip %int_0 + OpStore %51 %49 + %53 = OpAccessChain %_ptr_Function_float %flattenTemp %int_1 %uint_1 + %54 = OpLoad %float %53 + %55 = OpAccessChain %_ptr_Output_float %_entryPointOutput_clip %int_1 + OpStore %55 %54 + OpReturn + OpFunctionEnd + %_main_vf4_ = OpFunction %VSOut None %11 + %pos = OpFunctionParameter %_ptr_Function_v4float + %14 = OpLabel + %vout = OpVariable %_ptr_Function_VSOut Function + %19 = OpLoad %v4float %pos + %20 = OpAccessChain %_ptr_Function_v4float %vout %int_0 + OpStore %20 %19 + %22 = OpLoad %v4float %pos + %23 = OpVectorShuffle %v2float %22 %22 0 1 + %25 = OpAccessChain %_ptr_Function_v2float %vout %int_1 + OpStore %25 %23 + %26 = OpLoad %VSOut %vout + OpReturnValue %26 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/copy-memory-interface.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/copy-memory-interface.asm.vert new file mode 100644 index 0000000..c52c9bf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/copy-memory-interface.asm.vert @@ -0,0 +1,33 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 1 +; Bound: 13 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" %8 %9 %11 %12 + OpName %1 "main" + OpName %8 "v0" + OpName %9 "v1" + OpName %11 "o0" + OpName %12 "o1" + OpDecorate %8 Location 0 + OpDecorate %9 Location 1 + OpDecorate %11 BuiltIn Position + OpDecorate %12 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %5 = OpTypeFloat 32 + %6 = OpTypeVector %5 4 + %7 = OpTypePointer Input %6 + %8 = OpVariable %7 Input + %9 = OpVariable %7 Input + %10 = OpTypePointer Output %6 + %11 = OpVariable %10 Output + %12 = OpVariable %10 Output + %1 = OpFunction %2 None %3 + %4 = OpLabel + OpCopyMemory %11 %8 + OpCopyMemory %12 %9 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..89edeaa --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,141 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 79 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %VS "main" %PosL_1 %instanceID_1 %_entryPointOutput_Position %_entryPointOutput_Color + OpSource HLSL 500 + OpName %VS "VS" + OpName %V2F "V2F" + OpMemberName %V2F 0 "Position" + OpMemberName %V2F 1 "Color" + OpName %_VS_vf3_u1_ "@VS(vf3;u1;" + OpName %PosL "PosL" + OpName %instanceID "instanceID" + OpName %InstanceData "InstanceData" + OpMemberName %InstanceData 0 "MATRIX_MVP" + OpMemberName %InstanceData 1 "Color" + OpName %instData "instData" + OpName %InstanceData_0 "InstanceData" + OpMemberName %InstanceData_0 0 "MATRIX_MVP" + OpMemberName %InstanceData_0 1 "Color" + OpName %gInstanceData "gInstanceData" + OpMemberName %gInstanceData 0 "@data" + OpName %gInstanceData_0 "gInstanceData" + OpName %v2f "v2f" + OpName %PosL_0 "PosL" + OpName %PosL_1 "PosL" + OpName %instanceID_0 "instanceID" + OpName %instanceID_1 "instanceID" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %param_0 "param" + OpName %_entryPointOutput_Position "@entryPointOutput.Position" + OpName %_entryPointOutput_Color "@entryPointOutput.Color" + OpMemberDecorate %InstanceData_0 0 RowMajor + OpMemberDecorate %InstanceData_0 0 Offset 0 + OpMemberDecorate %InstanceData_0 0 MatrixStride 16 + OpMemberDecorate %InstanceData_0 1 Offset 64 + OpDecorate %_runtimearr_InstanceData_0 ArrayStride 80 + OpMemberDecorate %gInstanceData 0 NonWritable + OpMemberDecorate %gInstanceData 0 Offset 0 + OpDecorate %gInstanceData BufferBlock + OpDecorate %gInstanceData_0 DescriptorSet 1 + OpDecorate %gInstanceData_0 Binding 0 + OpDecorate %PosL_1 Location 0 + OpDecorate %instanceID_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput_Position BuiltIn Position + OpDecorate %_entryPointOutput_Color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %v4float = OpTypeVector %float 4 + %V2F = OpTypeStruct %v4float %v4float + %13 = OpTypeFunction %V2F %_ptr_Function_v3float %_ptr_Function_uint +%mat4v4float = OpTypeMatrix %v4float 4 +%InstanceData = OpTypeStruct %mat4v4float %v4float +%_ptr_Function_InstanceData = OpTypePointer Function %InstanceData +%InstanceData_0 = OpTypeStruct %mat4v4float %v4float +%_runtimearr_InstanceData_0 = OpTypeRuntimeArray %InstanceData_0 +%gInstanceData = OpTypeStruct %_runtimearr_InstanceData_0 +%_ptr_Uniform_gInstanceData = OpTypePointer Uniform %gInstanceData +%gInstanceData_0 = OpVariable %_ptr_Uniform_gInstanceData Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_InstanceData_0 = OpTypePointer Uniform %InstanceData_0 +%_ptr_Function_mat4v4float = OpTypePointer Function %mat4v4float + %int_1 = OpConstant %int 1 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_V2F = OpTypePointer Function %V2F + %float_1 = OpConstant %float 1 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %PosL_1 = OpVariable %_ptr_Input_v3float Input +%_ptr_Input_uint = OpTypePointer Input %uint +%instanceID_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Position = OpVariable %_ptr_Output_v4float Output +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output + %VS = OpFunction %void None %3 + %5 = OpLabel + %PosL_0 = OpVariable %_ptr_Function_v3float Function +%instanceID_0 = OpVariable %_ptr_Function_uint Function +%flattenTemp = OpVariable %_ptr_Function_V2F Function + %param = OpVariable %_ptr_Function_v3float Function + %param_0 = OpVariable %_ptr_Function_uint Function + %61 = OpLoad %v3float %PosL_1 + OpStore %PosL_0 %61 + %65 = OpLoad %uint %instanceID_1 + OpStore %instanceID_0 %65 + %68 = OpLoad %v3float %PosL_0 + OpStore %param %68 + %70 = OpLoad %uint %instanceID_0 + OpStore %param_0 %70 + %71 = OpFunctionCall %V2F %_VS_vf3_u1_ %param %param_0 + OpStore %flattenTemp %71 + %74 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %75 = OpLoad %v4float %74 + OpStore %_entryPointOutput_Position %75 + %77 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_1 + %78 = OpLoad %v4float %77 + OpStore %_entryPointOutput_Color %78 + OpReturn + OpFunctionEnd +%_VS_vf3_u1_ = OpFunction %V2F None %13 + %PosL = OpFunctionParameter %_ptr_Function_v3float + %instanceID = OpFunctionParameter %_ptr_Function_uint + %17 = OpLabel + %instData = OpVariable %_ptr_Function_InstanceData Function + %v2f = OpVariable %_ptr_Function_V2F Function + %29 = OpLoad %uint %instanceID + %31 = OpAccessChain %_ptr_Uniform_InstanceData_0 %gInstanceData_0 %int_0 %29 + %32 = OpLoad %InstanceData_0 %31 + %33 = OpCompositeExtract %mat4v4float %32 0 + %35 = OpAccessChain %_ptr_Function_mat4v4float %instData %int_0 + OpStore %35 %33 + %36 = OpCompositeExtract %v4float %32 1 + %39 = OpAccessChain %_ptr_Function_v4float %instData %int_1 + OpStore %39 %36 + %42 = OpAccessChain %_ptr_Function_mat4v4float %instData %int_0 + %43 = OpLoad %mat4v4float %42 + %44 = OpLoad %v3float %PosL + %46 = OpCompositeExtract %float %44 0 + %47 = OpCompositeExtract %float %44 1 + %48 = OpCompositeExtract %float %44 2 + %49 = OpCompositeConstruct %v4float %46 %47 %48 %float_1 + %50 = OpMatrixTimesVector %v4float %43 %49 + %51 = OpAccessChain %_ptr_Function_v4float %v2f %int_0 + OpStore %51 %50 + %52 = OpAccessChain %_ptr_Function_v4float %instData %int_1 + %53 = OpLoad %v4float %52 + %54 = OpAccessChain %_ptr_Function_v4float %v2f %int_1 + OpStore %54 %53 + %55 = OpLoad %V2F %v2f + OpReturnValue %55 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/fake-builtin-input.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/fake-builtin-input.asm.vert new file mode 100644 index 0000000..4de1d19 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/fake-builtin-input.asm.vert @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.3 +; Generator: Google spiregg; 0 +; Bound: 29 +; Schema: 0 + OpCapability Shader + OpCapability Float16 + OpCapability StorageInputOutput16 + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %vertexShader "main" %in_var_POSITION %gl_Position %gl_FragCoord %out_var_SV_Target + OpEntryPoint Fragment %fragmentShader "fragmentShader" %in_var_POSITION %gl_Position %gl_FragCoord %out_var_SV_Target + OpExecutionMode %fragmentShader OriginUpperLeft + OpSource HLSL 640 + OpName %in_var_POSITION "in.var.POSITION" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %vertexShader "vertexShader" + OpName %fragmentShader "fragmentShader" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %in_var_POSITION Location 0 + OpDecorate %out_var_SV_Target Location 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %half = OpTypeFloat 16 +%half_0x1p_0 = OpConstant %half 0x1p+0 +%half_0x0p_0 = OpConstant %half 0x0p+0 + %v4half = OpTypeVector %half 4 + %14 = OpConstantComposite %v4half %half_0x1p_0 %half_0x0p_0 %half_0x1p_0 %half_0x1p_0 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4half = OpTypePointer Output %v4half + %void = OpTypeVoid + %22 = OpTypeFunction %void +%in_var_POSITION = OpVariable %_ptr_Input_v2float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4half Output +%vertexShader = OpFunction %void None %22 + %23 = OpLabel + %24 = OpLoad %v2float %in_var_POSITION + %25 = OpCompositeExtract %float %24 0 + %26 = OpCompositeExtract %float %24 1 + %27 = OpCompositeConstruct %v4float %25 %26 %float_0 %float_1 + OpStore %gl_Position %27 + OpReturn + OpFunctionEnd +%fragmentShader = OpFunction %void None %22 + %28 = OpLabel + OpStore %out_var_SV_Target %14 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/invariant.msl21.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/invariant.msl21.asm.vert new file mode 100644 index 0000000..c0d381e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/invariant.msl21.asm.vert @@ -0,0 +1,34 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 18 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %_entryPointOutput Invariant + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %float_1 = OpConstant %float 1 + %12 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %17 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + OpReturnValue %12 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert new file mode 100644 index 0000000..429d3e4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/packed-bool-to-uint.asm.vert @@ -0,0 +1,111 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 62 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %gl_VertexIndex %a_position + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpName %Struct "Struct" + OpMemberName %Struct 0 "flags" + OpName %defaultUniformsVS "defaultUniformsVS" + OpMemberName %defaultUniformsVS 0 "flags" + OpMemberName %defaultUniformsVS 1 "uquad" + OpMemberName %defaultUniformsVS 2 "umatrix" + OpName %__0 "" + OpName %gl_VertexIndex "gl_VertexIndex" + OpName %a_position "a_position" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpDecorate %_arr_uint_uint_1 ArrayStride 16 + OpMemberDecorate %Struct 0 Offset 0 + OpDecorate %_arr_v2float_uint_4 ArrayStride 16 + OpMemberDecorate %defaultUniformsVS 0 Offset 0 + OpMemberDecorate %defaultUniformsVS 1 Offset 16 + OpMemberDecorate %defaultUniformsVS 2 ColMajor + OpMemberDecorate %defaultUniformsVS 2 Offset 80 + OpMemberDecorate %defaultUniformsVS 2 MatrixStride 16 + OpDecorate %defaultUniformsVS Block + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpDecorate %a_position Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_arr_uint_uint_1 = OpTypeArray %uint %uint_1 + %Struct = OpTypeStruct %_arr_uint_uint_1 + %v2float = OpTypeVector %float 2 + %uint_4 = OpConstant %uint 4 +%_arr_v2float_uint_4 = OpTypeArray %v2float %uint_4 +%mat4v4float = OpTypeMatrix %v4float 4 +%defaultUniformsVS = OpTypeStruct %Struct %_arr_v2float_uint_4 %mat4v4float +%_ptr_Uniform_defaultUniformsVS = OpTypePointer Uniform %defaultUniformsVS + %__0 = OpVariable %_ptr_Uniform_defaultUniformsVS Uniform + %int_2 = OpConstant %int 2 +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float + %int_1 = OpConstant %int 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Input_v4float = OpTypePointer Input %v4float + %a_position = OpVariable %_ptr_Input_v4float Input + %uint_2 = OpConstant %uint 2 +%_ptr_Input_float = OpTypePointer Input %float + %uint_3 = OpConstant %uint 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 + %float_0 = OpConstant %float 0 +%_ptr_Output_float = OpTypePointer Output %float + %main = OpFunction %void None %3 + %5 = OpLabel + %27 = OpAccessChain %_ptr_Uniform_mat4v4float %__0 %int_2 + %28 = OpLoad %mat4v4float %27 + %32 = OpLoad %int %gl_VertexIndex + %34 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_1 %32 + %35 = OpLoad %v2float %34 + %40 = OpAccessChain %_ptr_Input_float %a_position %uint_2 + %41 = OpLoad %float %40 + %43 = OpAccessChain %_ptr_Input_float %a_position %uint_3 + %44 = OpLoad %float %43 + %45 = OpCompositeExtract %float %35 0 + %46 = OpCompositeExtract %float %35 1 + %47 = OpCompositeConstruct %v4float %45 %46 %41 %44 + %48 = OpMatrixTimesVector %v4float %28 %47 + %50 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %50 %48 + %52 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %int_0 %int_0 + %53 = OpLoad %uint %52 + %56 = OpINotEqual %bool %53 %uint_0 + OpSelectionMerge %58 None + OpBranchConditional %56 %57 %58 + %57 = OpLabel + %61 = OpAccessChain %_ptr_Output_float %_ %int_0 %uint_2 + OpStore %61 %float_0 + OpBranch %58 + %58 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert new file mode 100644 index 0000000..8448265 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/packed-bool2-to-packed_uint2.asm.vert @@ -0,0 +1,113 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 64 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %gl_VertexIndex %a_position + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpName %Struct "Struct" + OpMemberName %Struct 0 "flags" + OpName %defaultUniformsVS "defaultUniformsVS" + OpMemberName %defaultUniformsVS 0 "flags" + OpMemberName %defaultUniformsVS 1 "uquad" + OpMemberName %defaultUniformsVS 2 "umatrix" + OpName %__0 "" + OpName %gl_VertexIndex "gl_VertexIndex" + OpName %a_position "a_position" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpDecorate %_arr_v2uint_uint_1 ArrayStride 16 + OpMemberDecorate %Struct 0 Offset 0 + OpDecorate %_arr_v2float_uint_4 ArrayStride 16 + OpMemberDecorate %defaultUniformsVS 0 Offset 0 + OpMemberDecorate %defaultUniformsVS 1 Offset 16 + OpMemberDecorate %defaultUniformsVS 2 ColMajor + OpMemberDecorate %defaultUniformsVS 2 Offset 80 + OpMemberDecorate %defaultUniformsVS 2 MatrixStride 16 + OpDecorate %defaultUniformsVS Block + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpDecorate %a_position Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %v2uint = OpTypeVector %uint 2 +%_arr_v2uint_uint_1 = OpTypeArray %v2uint %uint_1 + %Struct = OpTypeStruct %_arr_v2uint_uint_1 + %v2float = OpTypeVector %float 2 + %uint_4 = OpConstant %uint 4 +%_arr_v2float_uint_4 = OpTypeArray %v2float %uint_4 +%mat4v4float = OpTypeMatrix %v4float 4 +%defaultUniformsVS = OpTypeStruct %Struct %_arr_v2float_uint_4 %mat4v4float +%_ptr_Uniform_defaultUniformsVS = OpTypePointer Uniform %defaultUniformsVS + %__0 = OpVariable %_ptr_Uniform_defaultUniformsVS Uniform + %int_2 = OpConstant %int 2 +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float + %int_1 = OpConstant %int 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Input_v4float = OpTypePointer Input %v4float + %a_position = OpVariable %_ptr_Input_v4float Input + %uint_2 = OpConstant %uint 2 +%_ptr_Input_float = OpTypePointer Input %float + %uint_3 = OpConstant %uint 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %bool = OpTypeBool + %v2bool = OpTypeVector %bool 2 + %uint_0 = OpConstant %uint 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %float_0 = OpConstant %float 0 +%_ptr_Output_float = OpTypePointer Output %float + %main = OpFunction %void None %3 + %5 = OpLabel + %28 = OpAccessChain %_ptr_Uniform_mat4v4float %__0 %int_2 + %29 = OpLoad %mat4v4float %28 + %33 = OpLoad %int %gl_VertexIndex + %35 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_1 %33 + %36 = OpLoad %v2float %35 + %41 = OpAccessChain %_ptr_Input_float %a_position %uint_2 + %42 = OpLoad %float %41 + %44 = OpAccessChain %_ptr_Input_float %a_position %uint_3 + %45 = OpLoad %float %44 + %46 = OpCompositeExtract %float %36 0 + %47 = OpCompositeExtract %float %36 1 + %48 = OpCompositeConstruct %v4float %46 %47 %42 %45 + %49 = OpMatrixTimesVector %v4float %29 %48 + %51 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %51 %49 + %56 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %int_0 %int_0 %uint_0 + %57 = OpLoad %uint %56 + %58 = OpINotEqual %bool %57 %uint_0 + OpSelectionMerge %60 None + OpBranchConditional %58 %59 %60 + %59 = OpLabel + %63 = OpAccessChain %_ptr_Output_float %_ %int_0 %uint_2 + OpStore %63 %float_0 + OpBranch %60 + %60 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/packing-test.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/packing-test.asm.vert new file mode 100644 index 0000000..8acdebc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/packing-test.asm.vert @@ -0,0 +1,43 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 18 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 500 + OpName %main "main" + OpName %TestStruct "TestStruct" + OpMemberName %TestStruct 0 "transforms" + OpName %CB0 "CB0" + OpMemberName %CB0 0 "CB0" + OpName %_ "" + OpDecorate %_arr_mat4v4float_uint_6 ArrayStride 64 + OpMemberDecorate %TestStruct 0 RowMajor + OpMemberDecorate %TestStruct 0 Offset 0 + OpMemberDecorate %TestStruct 0 MatrixStride 16 + OpDecorate %_arr_TestStruct_uint_16 ArrayStride 384 + OpMemberDecorate %CB0 0 Offset 0 + OpDecorate %CB0 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %uint = OpTypeInt 32 0 + %uint_6 = OpConstant %uint 6 +%_arr_mat4v4float_uint_6 = OpTypeArray %mat4v4float %uint_6 + %TestStruct = OpTypeStruct %_arr_mat4v4float_uint_6 + %uint_16 = OpConstant %uint 16 +%_arr_TestStruct_uint_16 = OpTypeArray %TestStruct %uint_16 + %CB0 = OpTypeStruct %_arr_TestStruct_uint_16 +%_ptr_Uniform_CB0 = OpTypePointer Uniform %CB0 + %_ = OpVariable %_ptr_Uniform_CB0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert new file mode 100644 index 0000000..b566a3d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/spec-constant-op-composite.asm.vert @@ -0,0 +1,98 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 58 +; Schema: 0 + OpCapability Shader + OpCapability ClipDistance + OpCapability CullDistance + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %52 %output + OpSource GLSL 450 + OpName %4 "main" + OpName %9 "pos" + OpName %50 "gl_PerVertex" + OpMemberName %50 0 "gl_Position" + OpMemberName %50 1 "gl_PointSize" + OpMemberName %50 2 "gl_ClipDistance" + OpMemberName %50 3 "gl_CullDistance" + OpName %52 "" + OpDecorate %13 SpecId 201 + OpDecorate %24 SpecId 202 + OpMemberDecorate %50 0 BuiltIn Position + OpMemberDecorate %50 1 BuiltIn PointSize + OpMemberDecorate %50 2 BuiltIn ClipDistance + OpMemberDecorate %50 3 BuiltIn CullDistance + OpDecorate %50 Block + OpDecorate %57 SpecId 200 + OpDecorate %output Flat + OpDecorate %output Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstantComposite %7 %10 %10 %10 %10 + %12 = OpTypeInt 32 1 + %int_ptr = OpTypePointer Output %12 + %13 = OpSpecConstant %12 -10 + %14 = OpConstant %12 2 + %15 = OpSpecConstantOp %12 IAdd %13 %14 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 1 + %19 = OpTypePointer Function %6 + %24 = OpSpecConstant %17 100 + %25 = OpConstant %17 5 + %26 = OpSpecConstantOp %17 UMod %24 %25 + %28 = OpConstant %17 2 + %33 = OpConstant %12 20 + %34 = OpConstant %12 30 + %35 = OpTypeVector %12 4 + %36 = OpSpecConstantComposite %35 %33 %34 %15 %15 + %40 = OpTypeVector %12 2 + %41 = OpSpecConstantOp %40 VectorShuffle %36 %36 1 0 + %foo = OpSpecConstantOp %12 CompositeExtract %36 1 + %42 = OpTypeVector %6 2 + %49 = OpTypeArray %6 %18 + %50 = OpTypeStruct %7 %6 %49 %49 + %51 = OpTypePointer Output %50 + %52 = OpVariable %51 Output + %output = OpVariable %int_ptr Output + %53 = OpConstant %12 0 + %55 = OpTypePointer Output %7 + %57 = OpSpecConstant %6 3.14159 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %11 + %16 = OpConvertSToF %6 %15 + %20 = OpAccessChain %19 %9 %18 + %21 = OpLoad %6 %20 + %22 = OpFAdd %6 %21 %16 + %23 = OpAccessChain %19 %9 %18 + OpStore %23 %22 + %27 = OpConvertUToF %6 %26 + %29 = OpAccessChain %19 %9 %28 + %30 = OpLoad %6 %29 + %31 = OpFAdd %6 %30 %27 + %32 = OpAccessChain %19 %9 %28 + OpStore %32 %31 + %37 = OpConvertSToF %7 %36 + %38 = OpLoad %7 %9 + %39 = OpFAdd %7 %38 %37 + OpStore %9 %39 + %43 = OpConvertSToF %42 %41 + %44 = OpLoad %7 %9 + %45 = OpVectorShuffle %42 %44 %44 0 1 + %46 = OpFAdd %42 %45 %43 + %47 = OpLoad %7 %9 + %48 = OpVectorShuffle %7 %47 %46 4 5 2 3 + OpStore %9 %48 + %54 = OpLoad %7 %9 + %56 = OpAccessChain %55 %52 %53 + OpStore %56 %54 + OpStore %output %foo + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..29b0076 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/comp/access-private-workgroup-in-function.comp b/third_party/spirv-cross/shaders-msl/comp/access-private-workgroup-in-function.comp new file mode 100644 index 0000000..7cb1e6f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/access-private-workgroup-in-function.comp @@ -0,0 +1,31 @@ +#version 450 +layout(local_size_x = 1) in; + +int f; +shared int u; + +void set_f() +{ + f = 40; +} + +void set_shared_u() +{ + u = 50; +} + +void main() +{ + set_f(); + set_shared_u(); + if (gl_LocalInvocationIndex == 0u) + { + f = 10; + } + else + { + f = 30; + u = 20; + } +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp b/third_party/spirv-cross/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp new file mode 100644 index 0000000..883f001 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/argument-buffers-discrete.msl2.argument.discrete.comp @@ -0,0 +1,27 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0) readonly buffer SSBO0 +{ + vec4 v; +} ssbo0; + +layout(set = 1, binding = 0) readonly buffer SSBO1 +{ + vec4 v; +} ssbo1; + +layout(set = 2, binding = 5) readonly buffer SSBO2 +{ + vec4 v; +} ssbo2; + +layout(set = 3, binding = 6) writeonly buffer SSBO3 +{ + vec4 v; +} ssbo3; + +void main() +{ + ssbo3.v = ssbo0.v + ssbo1.v + ssbo2.v; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp b/third_party/spirv-cross/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp new file mode 100644 index 0000000..72ca889 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/argument-buffers-image-load-store.ios.msl2.argument.comp @@ -0,0 +1,10 @@ +#version 450 + +layout(set = 0, binding = 1, r32f) writeonly uniform image2D uImage; +layout(set = 0, binding = 2, r32f) readonly uniform image2D uImageRead; + +void main() +{ + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + imageStore(uImage, coord, imageLoad(uImageRead, coord)); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp b/third_party/spirv-cross/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp new file mode 100644 index 0000000..72ca889 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/argument-buffers-image-load-store.msl2.argument.comp @@ -0,0 +1,10 @@ +#version 450 + +layout(set = 0, binding = 1, r32f) writeonly uniform image2D uImage; +layout(set = 0, binding = 2, r32f) readonly uniform image2D uImageRead; + +void main() +{ + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + imageStore(uImage, coord, imageLoad(uImageRead, coord)); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/array-length.comp b/third_party/spirv-cross/shaders-msl/comp/array-length.comp new file mode 100644 index 0000000..6189d35 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/array-length.comp @@ -0,0 +1,22 @@ +#version 450 +layout(local_size_x = 1) in; +layout(set = 0, binding = 1, std140) buffer SSBO +{ + uint size; + float v[]; +}; + +layout(set = 0, binding = 2, std430) buffer SSBO1 +{ + float bz[]; +} ssbos[2]; + +uint get_size() +{ + return v.length() + ssbos[1].bz.length(); +} + +void main() +{ + size = get_size(); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/array-length.msl2.argument.discrete.comp b/third_party/spirv-cross/shaders-msl/comp/array-length.msl2.argument.discrete.comp new file mode 100644 index 0000000..61e6191 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/array-length.msl2.argument.discrete.comp @@ -0,0 +1,38 @@ +#version 450 +layout(local_size_x = 1) in; +layout(set = 0, binding = 1, std140) buffer SSBO +{ + uint size; + float v[]; +}; + +layout(set = 1, binding = 2, std430) buffer SSBO1 +{ + float bz[]; +} ssbos[2]; + +layout(set = 2, binding = 5, std140) buffer SSBO2 +{ + uint size2; + float w[]; +}; + +layout(set = 3, binding = 2, std430) buffer SSBO3 +{ + float bz[]; +} ssbos2[2]; + + +uint get_size() +{ + uint len = v.length(); + len += ssbos[1].bz.length(); + len += w.length(); + len += ssbos2[0].bz.length(); + return len; +} + +void main() +{ + size = get_size(); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/atomic.comp b/third_party/spirv-cross/shaders-msl/comp/atomic.comp new file mode 100644 index 0000000..e25c4f6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/atomic.comp @@ -0,0 +1,56 @@ +#version 310 es +#extension GL_OES_shader_image_atomic : require +layout(local_size_x = 1) in; + +layout(r32ui, binding = 0) uniform highp uimage2D uImage; +layout(r32i, binding = 1) uniform highp iimage2D iImage; +layout(binding = 2, std430) buffer SSBO +{ + uint u32; + int i32; +} ssbo; + +shared uint shared_u32; +shared int shared_i32; + +void main() +{ + atomicAdd(ssbo.u32, 1u); + atomicOr(ssbo.u32, 1u); + atomicXor(ssbo.u32, 1u); + atomicAnd(ssbo.u32, 1u); + atomicMin(ssbo.u32, 1u); + atomicMax(ssbo.u32, 1u); + atomicExchange(ssbo.u32, 1u); + atomicCompSwap(ssbo.u32, 10u, 2u); + + atomicAdd(ssbo.i32, 1); + atomicOr(ssbo.i32, 1); + atomicXor(ssbo.i32, 1); + atomicAnd(ssbo.i32, 1); + atomicMin(ssbo.i32, 1); + atomicMax(ssbo.i32, 1); + atomicExchange(ssbo.i32, 1); + atomicCompSwap(ssbo.i32, 10, 2); + + shared_u32 = 10u; + shared_i32 = 10; + atomicAdd(shared_u32, 1u); + atomicOr(shared_u32, 1u); + atomicXor(shared_u32, 1u); + atomicAnd(shared_u32, 1u); + atomicMin(shared_u32, 1u); + atomicMax(shared_u32, 1u); + atomicExchange(shared_u32, 1u); + atomicCompSwap(shared_u32, 10u, 2u); + + atomicAdd(shared_i32, 1); + atomicOr(shared_i32, 1); + atomicXor(shared_i32, 1); + atomicAnd(shared_i32, 1); + atomicMin(shared_i32, 1); + atomicMax(shared_i32, 1); + atomicExchange(shared_i32, 1); + atomicCompSwap(shared_i32, 10, 2); +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/barriers.comp b/third_party/spirv-cross/shaders-msl/comp/barriers.comp new file mode 100644 index 0000000..7e0ea42 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/barriers.comp @@ -0,0 +1,79 @@ +#version 310 es +layout(local_size_x = 4) in; + +void barrier_shared() +{ + memoryBarrierShared(); +} + +void full_barrier() +{ + memoryBarrier(); +} + +void image_barrier() +{ + memoryBarrierImage(); +} + +void buffer_barrier() +{ + memoryBarrierBuffer(); +} + +void group_barrier() +{ + groupMemoryBarrier(); +} + +void barrier_shared_exec() +{ + memoryBarrierShared(); + barrier(); +} + +void full_barrier_exec() +{ + memoryBarrier(); + barrier(); +} + +void image_barrier_exec() +{ + memoryBarrierImage(); + barrier(); +} + +void buffer_barrier_exec() +{ + memoryBarrierBuffer(); + barrier(); +} + +void group_barrier_exec() +{ + groupMemoryBarrier(); + barrier(); +} + +void exec_barrier() +{ + barrier(); +} + +void main() +{ + barrier_shared(); + full_barrier(); + image_barrier(); + buffer_barrier(); + group_barrier(); + + barrier_shared_exec(); + full_barrier_exec(); + image_barrier_exec(); + buffer_barrier_exec(); + group_barrier_exec(); + + exec_barrier(); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/basic.comp b/third_party/spirv-cross/shaders-msl/comp/basic.comp new file mode 100644 index 0000000..f9bf556 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/basic.comp @@ -0,0 +1,28 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +layout(std430, binding = 2) buffer SSBO3 +{ + uint counter; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idata = in_data[ident]; + if (dot(idata, vec4(1.0, 5.0, 6.0, 2.0)) > 8.2) + { + out_data[atomicAdd(counter, 1u)] = idata; + } +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/basic.dispatchbase.comp b/third_party/spirv-cross/shaders-msl/comp/basic.dispatchbase.comp new file mode 100644 index 0000000..2c87346 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/basic.dispatchbase.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x_id = 10) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +layout(std430, binding = 2) buffer SSBO3 +{ + uint counter; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + uint workgroup = gl_WorkGroupID.x; + vec4 idata = in_data[ident]; + if (dot(idata, vec4(1.0, 5.0, 6.0, 2.0)) > 8.2) + { + out_data[atomicAdd(counter, 1u)] = idata; + } +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/basic.dispatchbase.msl11.comp b/third_party/spirv-cross/shaders-msl/comp/basic.dispatchbase.msl11.comp new file mode 100644 index 0000000..9145333 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/basic.dispatchbase.msl11.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +layout(std430, binding = 2) buffer SSBO3 +{ + uint counter; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + uint workgroup = gl_WorkGroupID.x; + vec4 idata = in_data[ident]; + if (dot(idata, vec4(1.0, 5.0, 6.0, 2.0)) > 8.2) + { + out_data[atomicAdd(counter, 1u)] = idata; + } +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/basic.inline-block.msl2.comp b/third_party/spirv-cross/shaders-msl/comp/basic.inline-block.msl2.comp new file mode 100644 index 0000000..8e1144a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/basic.inline-block.msl2.comp @@ -0,0 +1,37 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require +layout(local_size_x = 3, local_size_y = 3, local_size_z = 2) in; + +struct X +{ + int x; + int y; + float z; +}; + +layout(set = 0, binding = 0, scalar) uniform Foo +{ + int a; + int b; + mat4 c; + X x[2]; +}; + +layout(set = 0, binding = 1) uniform Bar +{ + int d; + int e; +}; + +layout(set = 1, binding = 2) buffer Baz +{ + int f; + int g; +} baz[3]; + +void main() +{ + uvec3 coords = gl_GlobalInvocationID; + baz[coords.x].f = a + d; + baz[coords.x].g = b * e; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/builtins.comp b/third_party/spirv-cross/shaders-msl/comp/builtins.comp new file mode 100644 index 0000000..88bb595 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/builtins.comp @@ -0,0 +1,12 @@ +#version 310 es +layout(local_size_x = 8, local_size_y = 4, local_size_z = 2) in; + +void main() +{ + uvec3 local_id = gl_LocalInvocationID; + uvec3 global_id = gl_GlobalInvocationID; + uint local_index = gl_LocalInvocationIndex; + uvec3 work_group_size = gl_WorkGroupSize; + uvec3 num_work_groups = gl_NumWorkGroups; + uvec3 work_group_id = gl_WorkGroupID; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/cfg-preserve-parameter.comp b/third_party/spirv-cross/shaders-msl/comp/cfg-preserve-parameter.comp new file mode 100644 index 0000000..9ef9092 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/cfg-preserve-parameter.comp @@ -0,0 +1,54 @@ +#version 310 es + +// We write in all paths (and no reads), so should just be out. +void out_test_0(int cond, inout int i) +{ + if (cond == 0) + i = 40; + else + i = 60; +} + +// We write in all paths (and no reads), so should just be out. +void out_test_1(int cond, inout int i) +{ + switch (cond) + { + case 40: + i = 40; + break; + + default: + i = 70; + break; + } +} + +// We don't write in all paths, so should be inout. +void inout_test_0(int cond, inout int i) +{ + if (cond == 0) + i = 40; +} + +void inout_test_1(int cond, inout int i) +{ + switch (cond) + { + case 40: + i = 40; + break; + } +} + + +void main() +{ + int cond = 40; + int i = 50; + + out_test_0(cond, i); + out_test_1(cond, i); + inout_test_0(cond, i); + inout_test_1(cond, i); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/coherent-block.comp b/third_party/spirv-cross/shaders-msl/comp/coherent-block.comp new file mode 100644 index 0000000..0a174e8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/coherent-block.comp @@ -0,0 +1,12 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 1) coherent restrict writeonly buffer SSBO +{ + vec4 value; +}; + +void main() +{ + value = vec4(20.0); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/coherent-image.comp b/third_party/spirv-cross/shaders-msl/comp/coherent-image.comp new file mode 100644 index 0000000..fd6e280 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/coherent-image.comp @@ -0,0 +1,14 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 1) coherent restrict writeonly buffer SSBO +{ + ivec4 value; +}; + +layout(r32i, binding = 3) coherent readonly restrict uniform mediump iimage2D uImage; + +void main() +{ + value = imageLoad(uImage, ivec2(10)); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/complex-composite-constant-array.comp b/third_party/spirv-cross/shaders-msl/comp/complex-composite-constant-array.comp new file mode 100644 index 0000000..96a3f89 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/complex-composite-constant-array.comp @@ -0,0 +1,19 @@ +#version 450 + +layout(std430, set = 0, binding = 0) buffer SSBO +{ + mat4 a; + uint index; +}; + +const mat4 as[] = mat4[](mat4(1.0), mat4(2.0)); + +void write_global() +{ + a = as[index]; +} + +void main() +{ + write_global(); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/complex-type-alias.comp b/third_party/spirv-cross/shaders-msl/comp/complex-type-alias.comp new file mode 100644 index 0000000..4b9b6ed --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/complex-type-alias.comp @@ -0,0 +1,41 @@ +#version 450 +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +struct Foo0 +{ + float a; +}; + +struct Foo1 +{ + Foo0 a; +}; + +void Zero(out Foo0 v) +{ + v.a = 0.0; +} + +struct Foo2 +{ + Foo1 a; + float weight; +}; + +layout(std430, binding = 0) buffer SSBO +{ + Foo2 outputs[]; +}; + +shared Foo2 coeffs[64]; + +void main() +{ + Foo2 data; + data.weight = 0.0; + Zero(data.a.a); + coeffs[gl_LocalInvocationIndex] = data; + barrier(); + if (gl_LocalInvocationIndex == 0u) + outputs[gl_WorkGroupID.x] = coeffs[0]; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/composite-array-initialization.comp b/third_party/spirv-cross/shaders-msl/comp/composite-array-initialization.comp new file mode 100644 index 0000000..1ecf4bc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/composite-array-initialization.comp @@ -0,0 +1,28 @@ +#version 450 +layout(local_size_x = 2) in; + +struct Data +{ + float a; + float b; +}; + +layout(std430, binding = 0) buffer SSBO +{ + Data outdata[]; +}; + +layout(constant_id = 0) const float X = 4.0; + +Data data[2] = Data[](Data(1.0, 2.0), Data(3.0, 4.0)); +Data data2[2] = Data[](Data(X, 2.0), Data(3.0, 5.0)); + +Data combine(Data a, Data b) +{ + return Data(a.a + b.a, a.b + b.b); +} + +void main() +{ + outdata[gl_WorkGroupID.x] = combine(data[gl_LocalInvocationID.x], data2[gl_LocalInvocationID.x]); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/composite-array-initialization.force-native-array.comp b/third_party/spirv-cross/shaders-msl/comp/composite-array-initialization.force-native-array.comp new file mode 100644 index 0000000..1ecf4bc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/composite-array-initialization.force-native-array.comp @@ -0,0 +1,28 @@ +#version 450 +layout(local_size_x = 2) in; + +struct Data +{ + float a; + float b; +}; + +layout(std430, binding = 0) buffer SSBO +{ + Data outdata[]; +}; + +layout(constant_id = 0) const float X = 4.0; + +Data data[2] = Data[](Data(1.0, 2.0), Data(3.0, 4.0)); +Data data2[2] = Data[](Data(X, 2.0), Data(3.0, 5.0)); + +Data combine(Data a, Data b) +{ + return Data(a.a + b.a, a.b + b.b); +} + +void main() +{ + outdata[gl_WorkGroupID.x] = combine(data[gl_LocalInvocationID.x], data2[gl_LocalInvocationID.x]); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/composite-construct.comp b/third_party/spirv-cross/shaders-msl/comp/composite-construct.comp new file mode 100644 index 0000000..3054775 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/composite-construct.comp @@ -0,0 +1,31 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO0 +{ + vec4 as[]; +}; + +layout(std430, binding = 1) buffer SSBO1 +{ + vec4 bs[]; +}; + +struct Composite +{ + vec4 a; + vec4 b; +}; + +const vec4 const_values[2] = vec4[](vec4(20.0), vec4(40.0)); + +void main() +{ + vec4 values[2] = vec4[](as[gl_GlobalInvocationID.x], bs[gl_GlobalInvocationID.x]); + vec4 copy_values[2]; + copy_values = const_values; + Composite c = Composite(values[0], copy_values[1]); + + as[0] = values[gl_LocalInvocationIndex]; + bs[1] = c.b; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/copy-array-of-arrays.comp b/third_party/spirv-cross/shaders-msl/comp/copy-array-of-arrays.comp new file mode 100644 index 0000000..edf8719 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/copy-array-of-arrays.comp @@ -0,0 +1,21 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, std430) buffer BUF +{ + int a; + float b; + float c; +} o; + +void main() +{ + const float a[2][2][2] = float[][][](float[][](float[](1.0, 2.0), float[](3.0, 4.0)), float[][](float[](1.0, 2.0), float[](3.0, 4.0))); + float b[2][2][2] = a; + float c[2][2][2] = b; + o.a = int(c[1][1][1]); + + float d[2][2][2] = float[][][](float[][](float[](o.b, o.c), float[](o.b, o.b)), float[][](float[](o.c, o.c), float[](o.c, o.b))); + float e[2][2][2] = d; + o.b = e[1][0][1]; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp b/third_party/spirv-cross/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp new file mode 100644 index 0000000..edf8719 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/copy-array-of-arrays.force-native-array.comp @@ -0,0 +1,21 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, std430) buffer BUF +{ + int a; + float b; + float c; +} o; + +void main() +{ + const float a[2][2][2] = float[][][](float[][](float[](1.0, 2.0), float[](3.0, 4.0)), float[][](float[](1.0, 2.0), float[](3.0, 4.0))); + float b[2][2][2] = a; + float c[2][2][2] = b; + o.a = int(c[1][1][1]); + + float d[2][2][2] = float[][][](float[][](float[](o.b, o.c), float[](o.b, o.b)), float[][](float[](o.c, o.c), float[](o.c, o.b))); + float e[2][2][2] = d; + o.b = e[1][0][1]; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/culling.comp b/third_party/spirv-cross/shaders-msl/comp/culling.comp new file mode 100644 index 0000000..9f8331b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/culling.comp @@ -0,0 +1,26 @@ +#version 310 es +layout(local_size_x = 4) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + float in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + float out_data[]; +}; + +layout(std430, binding = 2) buffer SSBO3 +{ + uint count; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + float idata = in_data[ident]; + if (idata > 12.0) + out_data[atomicAdd(count, 1u)] = idata; +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/defer-parens.comp b/third_party/spirv-cross/shaders-msl/comp/defer-parens.comp new file mode 100644 index 0000000..4e8ea6b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/defer-parens.comp @@ -0,0 +1,30 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + vec4 data; + int index; +}; + +void main() +{ + // Tests defer-parens behavior where a binary expression is OpCompositeExtracted chained together + // with an OpCompositeConstruct optimization. + vec4 d = data; + data = vec4(d.x, d.yz + 10.0, d.w); + + // Verify binary ops. + data = d + d + d; + + // Verify swizzles. + data = (d.yz + 10.0).xxyy; + + // OpCompositeExtract + float t = (d.yz + 10.0).y; + data = vec4(t); + + // OpVectorExtractDynamic + t = (d.zw + 10.0)[index]; + data = vec4(t); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/dowhile.comp b/third_party/spirv-cross/shaders-msl/comp/dowhile.comp new file mode 100644 index 0000000..709db75 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/dowhile.comp @@ -0,0 +1,31 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +int i; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + + i = 0; + vec4 idat = in_data[ident]; + do + { + idat = mvp * idat; + i++; + } while(i < 16); + + out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/force-recompile-hooks.swizzle.comp b/third_party/spirv-cross/shaders-msl/comp/force-recompile-hooks.swizzle.comp new file mode 100644 index 0000000..2752d30 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/force-recompile-hooks.swizzle.comp @@ -0,0 +1,9 @@ +#version 450 + +layout(binding = 0) uniform sampler2D foo; +layout(binding = 1, rgba8) uniform image2D bar; + +void main() { + vec4 a = texture(foo, vec2(1, 1)); + imageStore(bar, ivec2(0, 0), a); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/functions.comp b/third_party/spirv-cross/shaders-msl/comp/functions.comp new file mode 100644 index 0000000..478c8eb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/functions.comp @@ -0,0 +1,12 @@ +#version 450 +shared int foo[1337]; + +void myfunc() +{ + foo[0]=13; +} + +void main() +{ + myfunc(); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp b/third_party/spirv-cross/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp new file mode 100644 index 0000000..2fe074d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/global-invocation-id-writable-ssbo-in-function.comp @@ -0,0 +1,12 @@ +#version 450 +layout(set = 0, binding = 0) buffer myBlock { + int a; + float b[1]; +} myStorage; +float getB() { + return myStorage.b[gl_GlobalInvocationID.x]; +} +void main() { + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_GlobalInvocationID.x] = mod((getB() + 0.02), 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/global-invocation-id.comp b/third_party/spirv-cross/shaders-msl/comp/global-invocation-id.comp new file mode 100644 index 0000000..f484637 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/global-invocation-id.comp @@ -0,0 +1,9 @@ +#version 450 +layout(set = 0, binding = 0) buffer myBlock { + int a; + float b[1]; +} myStorage; +void main() { + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_GlobalInvocationID.x] = mod((myStorage.b[gl_GlobalInvocationID.x] + 0.02), 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp b/third_party/spirv-cross/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp new file mode 100644 index 0000000..862cd21 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/image-atomic-automatic-bindings.argument.msl2.comp @@ -0,0 +1,16 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, r32ui) uniform uimage2D uImage; +layout(set = 0, binding = 1) uniform sampler2D uTexture; + +layout(set = 0, binding = 2) buffer SSBO +{ + vec4 outdata; +}; + +void main() +{ + uint ret = imageAtomicAdd(uImage, ivec2(gl_GlobalInvocationID.xy), 10u); + outdata = textureLod(uTexture, vec2(gl_GlobalInvocationID.xy), 0.0) + float(ret); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/image-atomic-automatic-bindings.comp b/third_party/spirv-cross/shaders-msl/comp/image-atomic-automatic-bindings.comp new file mode 100644 index 0000000..862cd21 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/image-atomic-automatic-bindings.comp @@ -0,0 +1,16 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, r32ui) uniform uimage2D uImage; +layout(set = 0, binding = 1) uniform sampler2D uTexture; + +layout(set = 0, binding = 2) buffer SSBO +{ + vec4 outdata; +}; + +void main() +{ + uint ret = imageAtomicAdd(uImage, ivec2(gl_GlobalInvocationID.xy), 10u); + outdata = textureLod(uTexture, vec2(gl_GlobalInvocationID.xy), 0.0) + float(ret); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/image-cube-array-load-store.comp b/third_party/spirv-cross/shaders-msl/comp/image-cube-array-load-store.comp new file mode 100644 index 0000000..36a9ffd --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/image-cube-array-load-store.comp @@ -0,0 +1,13 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(r32f, binding = 0) uniform readonly imageCubeArray uImageIn; +layout(r32f, binding = 1) uniform writeonly imageCubeArray uImageOut; + +void main() +{ + ivec3 coord = ivec3(9, 7, 11); + vec4 indata = imageLoad(uImageIn, coord); + imageStore(uImageOut, coord, indata); +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/image.comp b/third_party/spirv-cross/shaders-msl/comp/image.comp new file mode 100644 index 0000000..e375534 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/image.comp @@ -0,0 +1,12 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(rgba8, binding = 0) uniform readonly mediump image2D uImageIn; +layout(rgba8, binding = 1) uniform writeonly mediump image2D uImageOut; + +void main() +{ + vec4 v = imageLoad(uImageIn, ivec2(gl_GlobalInvocationID.xy) + imageSize(uImageIn)); + imageStore(uImageOut, ivec2(gl_GlobalInvocationID.xy), v); +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/insert.comp b/third_party/spirv-cross/shaders-msl/comp/insert.comp new file mode 100644 index 0000000..07c1f8d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/insert.comp @@ -0,0 +1,18 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) writeonly buffer SSBO +{ + vec4 out_data[]; +}; + +void main() +{ + vec4 v; + v.x = 10.0; + v.y = 30.0; + v.z = 70.0; + v.w = 90.0; + out_data[gl_GlobalInvocationID.x] = v; + out_data[gl_GlobalInvocationID.x].y = 20.0; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/inverse.comp b/third_party/spirv-cross/shaders-msl/comp/inverse.comp new file mode 100644 index 0000000..03b06d6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/inverse.comp @@ -0,0 +1,23 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std430, binding = 0) writeonly buffer MatrixOut +{ + mat2 m2out; + mat3 m3out; + mat4 m4out; +}; + +layout(std430, binding = 1) readonly buffer MatrixIn +{ + mat2 m2in; + mat3 m3in; + mat4 m4in; +}; + +void main() +{ + m2out = inverse(m2in); + m3out = inverse(m3in); + m4out = inverse(m4in); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/local-invocation-id.comp b/third_party/spirv-cross/shaders-msl/comp/local-invocation-id.comp new file mode 100644 index 0000000..281700f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/local-invocation-id.comp @@ -0,0 +1,9 @@ +#version 450 +layout(set = 0, binding = 0) buffer myBlock { + int a; + float b[1]; +} myStorage; +void main() { + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_LocalInvocationID.x] = mod((myStorage.b[gl_LocalInvocationID.x] + 0.02), 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/local-invocation-index.comp b/third_party/spirv-cross/shaders-msl/comp/local-invocation-index.comp new file mode 100644 index 0000000..68942da --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/local-invocation-index.comp @@ -0,0 +1,9 @@ +#version 450 +layout(set = 0, binding = 0) buffer myBlock { + int a; + float b[1]; +} myStorage; +void main() { + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b[gl_LocalInvocationIndex.x] = mod((myStorage.b[gl_LocalInvocationIndex.x] + 0.02), 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/mat3.comp b/third_party/spirv-cross/shaders-msl/comp/mat3.comp new file mode 100644 index 0000000..7c5bb1e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/mat3.comp @@ -0,0 +1,14 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + mat3 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + out_data[ident] = mat3(vec3(10.0), vec3(20.0), vec3(40.0)); +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/mod.comp b/third_party/spirv-cross/shaders-msl/comp/mod.comp new file mode 100644 index 0000000..1631456 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/mod.comp @@ -0,0 +1,26 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 v = mod(in_data[ident], out_data[ident]); + out_data[ident] = v; + + uvec4 vu = floatBitsToUint(in_data[ident]) % floatBitsToUint(out_data[ident]); + out_data[ident] = uintBitsToFloat(vu); + + ivec4 vi = floatBitsToInt(in_data[ident]) % floatBitsToInt(out_data[ident]); + out_data[ident] = intBitsToFloat(vi); +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/modf.comp b/third_party/spirv-cross/shaders-msl/comp/modf.comp new file mode 100644 index 0000000..edadefc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/modf.comp @@ -0,0 +1,23 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 i; + //vec4 v = frexp(in_data[ident], i); + //out_data[ident] = ldexp(v, i); + vec4 v = modf(in_data[ident], i); + out_data[ident] = v; +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/outer-product.comp b/third_party/spirv-cross/shaders-msl/comp/outer-product.comp new file mode 100644 index 0000000..9aba2a5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/outer-product.comp @@ -0,0 +1,37 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, std430) writeonly buffer SSBO +{ + mat2 m22; + mat2x3 m23; + mat2x4 m24; + mat3x2 m32; + mat3 m33; + mat3x4 m34; + mat4x2 m42; + mat4x3 m43; + mat4 m44; +}; + +layout(set = 0, binding = 1, std430) readonly buffer ReadSSBO +{ + vec2 v2; + vec3 v3; + vec4 v4; +}; + +void main() +{ + m22 = outerProduct(v2, v2); + m23 = outerProduct(v3, v2); + m24 = outerProduct(v4, v2); + + m32 = outerProduct(v2, v3); + m33 = outerProduct(v3, v3); + m34 = outerProduct(v4, v3); + + m42 = outerProduct(v2, v4); + m43 = outerProduct(v3, v4); + m44 = outerProduct(v4, v4); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/packing-test-1.comp b/third_party/spirv-cross/shaders-msl/comp/packing-test-1.comp new file mode 100644 index 0000000..1a8a39e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/packing-test-1.comp @@ -0,0 +1,18 @@ +#version 450 +struct T1 +{ + vec3 a; + float b; +}; + +layout(std430, binding = 1) buffer Buffer0 { T1 buf0[]; }; +layout(std430, binding = 2) buffer Buffer1 { float buf1[]; }; + +layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in; +void main() +{ + // broken case in Metal! + T1 v = buf0[0]; + float x = v.b; + buf1[gl_GlobalInvocationID.x] = x; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/packing-test-2.comp b/third_party/spirv-cross/shaders-msl/comp/packing-test-2.comp new file mode 100644 index 0000000..73268be --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/packing-test-2.comp @@ -0,0 +1,16 @@ +#version 450 +struct T1 +{ + vec3 a; + float b; +}; + +layout(std430, binding = 1) buffer Buffer0 { T1 buf0[]; }; +layout(std430, binding = 2) buffer Buffer1 { float buf1[]; }; + +layout(local_size_x = 32, local_size_y = 1, local_size_z = 1) in; +void main() +{ + float x = buf0[0].b; + buf1[gl_GlobalInvocationID.x] = x; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/read-write-only.comp b/third_party/spirv-cross/shaders-msl/comp/read-write-only.comp new file mode 100644 index 0000000..b224b6f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/read-write-only.comp @@ -0,0 +1,26 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO0 +{ + vec4 data0; + vec4 data1; +}; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + vec4 data2; + vec4 data3; +}; + +layout(binding = 2, std430) restrict writeonly buffer SSBO2 +{ + vec4 data4; + vec4 data5; +}; + +void main() +{ + data4 = data0 + data2; + data5 = data1 + data3; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/rmw-matrix.comp b/third_party/spirv-cross/shaders-msl/comp/rmw-matrix.comp new file mode 100644 index 0000000..c158ab4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/rmw-matrix.comp @@ -0,0 +1,20 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float a; + vec4 b; + mat4 c; + + float a1; + vec4 b1; + mat4 c1; +}; + +void main() +{ + a *= a1; + b *= b1; + c *= c1; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/rmw-opt.comp b/third_party/spirv-cross/shaders-msl/comp/rmw-opt.comp new file mode 100644 index 0000000..a6e1e7f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/rmw-opt.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + int a; +}; + +void main() +{ + a += 10; + a -= 10; + a *= 10; + a /= 10; + a <<= 2; + a >>= 3; + a &= 40; + a ^= 10; + a %= 40; + a |= 1; + + bool c = false; + bool d = true; + c = c && d; + d = d || c; + a = c && d ? 1 : 0; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/shaders-msl/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..6354632 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,20 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std430, set = 0, binding = 0) buffer SSBO +{ + float a; + float b; + float c; + float d; + float e; + float f; +}; + +void main() +{ + c = distance(a, b); + d = length(a); + e = normalize(a); + f = distance(a-1, b-2); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/shared-array-of-arrays.comp b/third_party/spirv-cross/shaders-msl/comp/shared-array-of-arrays.comp new file mode 100644 index 0000000..009b4e4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/shared-array-of-arrays.comp @@ -0,0 +1,29 @@ +#version 310 es +layout(local_size_x = 4, local_size_y = 4) in; + +shared float foo[4][4]; + +layout(binding = 0, std430) buffer SSBO +{ + float out_data[]; +}; + +void work() +{ + foo[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = float(gl_LocalInvocationIndex); + memoryBarrierShared(); + barrier(); + + float x = 0.0; + x += foo[gl_LocalInvocationID.x][0]; + x += foo[gl_LocalInvocationID.x][1]; + x += foo[gl_LocalInvocationID.x][2]; + x += foo[gl_LocalInvocationID.x][3]; + out_data[gl_GlobalInvocationID.x] = x; +} + +void main() +{ + work(); +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/shared.comp b/third_party/spirv-cross/shaders-msl/comp/shared.comp new file mode 100644 index 0000000..4deff93 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/shared.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 4) in; + +shared float sShared[gl_WorkGroupSize.x]; + +layout(std430, binding = 0) readonly buffer SSBO +{ + float in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + float out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + float idata = in_data[ident]; + + sShared[gl_LocalInvocationIndex] = idata; + memoryBarrierShared(); + barrier(); + + out_data[ident] = sShared[gl_WorkGroupSize.x - gl_LocalInvocationIndex - 1u]; +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/spec-constant-op-member-array.comp b/third_party/spirv-cross/shaders-msl/comp/spec-constant-op-member-array.comp new file mode 100644 index 0000000..0b428eb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/spec-constant-op-member-array.comp @@ -0,0 +1,33 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(constant_id = 0) const int a = 100; +layout(constant_id = 1) const int b = 200; +layout(constant_id = 2) const int c = 300; +const int d = c + 50; +layout(constant_id = 3) const int e = 400; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +layout(set = 1, binding = 0) buffer SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +}; + +void main() +{ + w[gl_GlobalInvocationID.x] += v[gl_GlobalInvocationID.x] + e; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/spec-constant-work-group-size.comp b/third_party/spirv-cross/shaders-msl/comp/spec-constant-work-group-size.comp new file mode 100644 index 0000000..09b65dc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/spec-constant-work-group-size.comp @@ -0,0 +1,17 @@ +#version 450 +layout(local_size_x_id = 10, local_size_y = 20) in; + +layout(constant_id = 0) const int a = 1; +layout(constant_id = 1) const int b = 2; + +layout(set = 1, binding = 0) writeonly buffer SSBO +{ + int v[]; +}; + +void main() +{ + int spec_const_array_size[b]; + spec_const_array_size[a] = a; + v[a + gl_WorkGroupSize.x + gl_WorkGroupSize.y] = b + spec_const_array_size[1 - a]; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/storage-buffer-std140-vector-array.comp b/third_party/spirv-cross/shaders-msl/comp/storage-buffer-std140-vector-array.comp new file mode 100644 index 0000000..7e786ec --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/storage-buffer-std140-vector-array.comp @@ -0,0 +1,30 @@ +#version 450 +layout(local_size_x = 1) in; + +struct Sub +{ + float f[2]; + vec2 f2[2]; + vec3 f3[2]; + vec4 f4[2]; +}; + +layout(std140, binding = 0) buffer SSBO +{ + Sub sub[2]; +}; + +void main() +{ + Sub foo = sub[gl_WorkGroupID.x]; + + foo.f[gl_GlobalInvocationID.x] += 1.0; + foo.f2[gl_GlobalInvocationID.x] += 2.0; + foo.f3[gl_GlobalInvocationID.x] += 3.0; + foo.f4[gl_GlobalInvocationID.x] += 4.0; + sub[gl_WorkGroupID.x] = foo; + + sub[0].f[0] += 5.0; + sub[0].f2[1] += 5.0; +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/struct-layout.comp b/third_party/spirv-cross/shaders-msl/comp/struct-layout.comp new file mode 100644 index 0000000..5a2b780 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/struct-layout.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct Foo +{ + mat4 m; +}; + +layout(std430, binding = 0) readonly buffer SSBO +{ + Foo in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + Foo out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + out_data[ident].m = in_data[ident].m * in_data[ident].m; +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/struct-nested.comp b/third_party/spirv-cross/shaders-msl/comp/struct-nested.comp new file mode 100644 index 0000000..d9645cb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/struct-nested.comp @@ -0,0 +1,20 @@ +#version 450 + +struct s1 +{ + int a; +}; + +struct s2 +{ + s1 b; +}; + +layout(std430, binding = 1) buffer dstbuffer{ s2 test[]; }; +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +void main() +{ + s2 testVal; + testVal.b.a = 0; + test[0] = testVal; +} \ No newline at end of file diff --git a/third_party/spirv-cross/shaders-msl/comp/struct-packing.comp b/third_party/spirv-cross/shaders-msl/comp/struct-packing.comp new file mode 100644 index 0000000..5baf45c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/struct-packing.comp @@ -0,0 +1,77 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + + S4 m3s[8]; +}; + +layout(binding = 1, std430) buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(binding = 0, std140) buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content = ssbo_140.content; + ssbo_430.content.m1.a = ssbo_430.m6[1][1] * ssbo_430.content.m3.a; // test packed matrix access +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/torture-loop.comp b/third_party/spirv-cross/shaders-msl/comp/torture-loop.comp new file mode 100644 index 0000000..54a1221 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/torture-loop.comp @@ -0,0 +1,40 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idat = in_data[ident]; + + int k = 0; + + // Continue with side effects. + while (++k < 10) + { + idat *= 2.0; + k++; + } + + // Again used here ... + for (uint i = 0u; i < 16u; i++, k++) + for (uint j = 0u; j < 30u; j++) + idat = mvp * idat; + + do + { + k++; + } while (k > 10); + out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/shaders-msl/comp/type-alias.comp b/third_party/spirv-cross/shaders-msl/comp/type-alias.comp new file mode 100644 index 0000000..343d350 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/type-alias.comp @@ -0,0 +1,45 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct S0 +{ + vec4 a; +}; + +struct S1 +{ + vec4 a; +}; + +vec4 overload(S0 s0) +{ + return s0.a; +} + +vec4 overload(S1 s1) +{ + return s1.a; +} + +layout(std430, binding = 0) buffer SSBO0 +{ + S0 s0s[]; +}; + +layout(std430, binding = 1) buffer SSBO1 +{ + S1 s1s[]; +}; + +layout(std430, binding = 2) buffer SSBO2 +{ + vec4 outputs[]; +}; + + +void main() +{ + S0 s0 = s0s[gl_GlobalInvocationID.x]; + S1 s1 = s1s[gl_GlobalInvocationID.x]; + outputs[gl_GlobalInvocationID.x] = overload(s0) + overload(s1); +} diff --git a/third_party/spirv-cross/shaders-msl/comp/udiv.comp b/third_party/spirv-cross/shaders-msl/comp/udiv.comp new file mode 100644 index 0000000..d4e1133 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/udiv.comp @@ -0,0 +1,17 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + uint inputs[]; +}; + +layout(std430, binding = 1) buffer SSBO2 +{ + uint outputs[]; +}; + +void main() +{ + outputs[gl_GlobalInvocationID.x] = inputs[gl_GlobalInvocationID.x] / 29u; +} diff --git a/third_party/spirv-cross/shaders-msl/comp/writable-ssbo.comp b/third_party/spirv-cross/shaders-msl/comp/writable-ssbo.comp new file mode 100644 index 0000000..1d5128c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/comp/writable-ssbo.comp @@ -0,0 +1,9 @@ +#version 450 +layout(set = 0, binding = 0) buffer myBlock { + int a; + float b; +} myStorage; +void main() { + myStorage.a = (myStorage.a + 1) % 256; + myStorage.b = mod((myStorage.b + 0.02), 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp b/third_party/spirv-cross/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp new file mode 100644 index 0000000..9623751 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/comp/extended-arithmetic.desktop.comp @@ -0,0 +1,41 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBOUint +{ + uint a, b, c, d; + uvec2 a2, b2, c2, d2; + uvec3 a3, b3, c3, d3; + uvec4 a4, b4, c4, d4; +} u; + +layout(binding = 1, std430) buffer SSBOInt +{ + int a, b, c, d; + ivec2 a2, b2, c2, d2; + ivec3 a3, b3, c3, d3; + ivec4 a4, b4, c4, d4; +} i; + +void main() +{ + u.c = uaddCarry(u.a, u.b, u.d); + u.c2 = uaddCarry(u.a2, u.b2, u.d2); + u.c3 = uaddCarry(u.a3, u.b3, u.d3); + u.c4 = uaddCarry(u.a4, u.b4, u.d4); + + u.c = usubBorrow(u.a, u.b, u.d); + u.c2 = usubBorrow(u.a2, u.b2, u.d2); + u.c3 = usubBorrow(u.a3, u.b3, u.d3); + u.c4 = usubBorrow(u.a4, u.b4, u.d4); + + umulExtended(u.a, u.b, u.c, u.d); + umulExtended(u.a2, u.b2, u.c2, u.d2); + umulExtended(u.a3, u.b3, u.c3, u.d3); + umulExtended(u.a4, u.b4, u.c4, u.d4); + + imulExtended(i.a, i.b, i.c, i.d); + imulExtended(i.a2, i.b2, i.c2, i.d2); + imulExtended(i.a3, i.b3, i.c3, i.d3); + imulExtended(i.a4, i.b4, i.c4, i.d4); +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/frag/image-ms.desktop.frag b/third_party/spirv-cross/shaders-msl/desktop-only/frag/image-ms.desktop.frag new file mode 100644 index 0000000..e145cb8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/frag/image-ms.desktop.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(rgba8, binding = 0) uniform image2D uImage; +layout(rgba8, binding = 1) uniform image2DArray uImageArray; +layout(rgba8, binding = 2) uniform image2DMS uImageMS; + +void main() +{ + vec4 a = imageLoad(uImageMS, ivec2(1, 2), 2); + vec4 b = imageLoad(uImageArray, ivec3(1, 2, 4)); + imageStore(uImage, ivec2(2, 3), a); + imageStore(uImageArray, ivec3(2, 3, 7), b); +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/frag/query-levels.desktop.frag b/third_party/spirv-cross/shaders-msl/desktop-only/frag/query-levels.desktop.frag new file mode 100644 index 0000000..4a80cbf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/frag/query-levels.desktop.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(textureQueryLevels(uSampler))); +} + diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag b/third_party/spirv-cross/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag new file mode 100644 index 0000000..4c8dcf9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/frag/sampler-ms-query.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(binding = 0) uniform sampler2DMS uSampler; +layout(binding = 2, rgba8) uniform readonly writeonly image2DMS uImage; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(textureSamples(uSampler) + imageSamples(uImage))); +} + diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc new file mode 100644 index 0000000..e6941a3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/arrayed-output.desktop.sso.tesc @@ -0,0 +1,27 @@ +#version 450 + +layout(vertices = 4) out; +layout(location = 0) patch out vec3 vPatch[2]; +layout(location = 2) out vec3 vVertex[]; +layout(location = 0) in vec3 vInput[]; + +void main() +{ + vVertex[gl_InvocationID] = + vInput[gl_InvocationID] + + vInput[gl_InvocationID ^ 1]; + + barrier(); + + if (gl_InvocationID == 0) + { + vPatch[0] = vec3(10.0); + vPatch[1] = vec3(20.0); + gl_TessLevelOuter[0] = 1.0; + gl_TessLevelOuter[1] = 2.0; + gl_TessLevelOuter[2] = 3.0; + gl_TessLevelOuter[3] = 4.0; + gl_TessLevelInner[0] = 1.0; + gl_TessLevelInner[1] = 2.0; + } +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc new file mode 100644 index 0000000..a258afb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/basic.desktop.sso.multi-patch.tesc @@ -0,0 +1,32 @@ +#version 450 +layout(vertices = 1) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[gl_MaxPatchVertices]; + +out gl_PerVertex +{ + vec4 gl_Position; +} gl_out[1]; + +layout(location = 0) patch out vec3 vFoo; + +void set_position() +{ + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} + +void main() +{ + gl_TessLevelInner[0] = 8.9; + gl_TessLevelInner[1] = 6.9; + gl_TessLevelOuter[0] = 8.9; + gl_TessLevelOuter[1] = 6.9; + gl_TessLevelOuter[2] = 3.9; + gl_TessLevelOuter[3] = 4.9; + vFoo = vec3(1.0); + + set_position(); +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc new file mode 100644 index 0000000..a258afb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/basic.desktop.sso.tesc @@ -0,0 +1,32 @@ +#version 450 +layout(vertices = 1) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[gl_MaxPatchVertices]; + +out gl_PerVertex +{ + vec4 gl_Position; +} gl_out[1]; + +layout(location = 0) patch out vec3 vFoo; + +void set_position() +{ + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} + +void main() +{ + gl_TessLevelInner[0] = 8.9; + gl_TessLevelInner[1] = 6.9; + gl_TessLevelOuter[0] = 8.9; + gl_TessLevelOuter[1] = 6.9; + gl_TessLevelOuter[2] = 3.9; + gl_TessLevelOuter[3] = 4.9; + vFoo = vec3(1.0); + + set_position(); +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc new file mode 100644 index 0000000..78d0d00 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.multi-patch.tesc @@ -0,0 +1,22 @@ +#version 450 + +struct Boo +{ + vec3 a; + uvec3 b; +}; + +layout(vertices = 4) out; +layout(location = 0) out Boo vVertex[]; +layout(location = 0) in Boo vInput[]; + +void main() +{ + vVertex[gl_InvocationID] = vInput[gl_InvocationID]; + gl_TessLevelOuter[0] = 1.0; + gl_TessLevelOuter[1] = 2.0; + gl_TessLevelOuter[2] = 3.0; + gl_TessLevelOuter[3] = 4.0; + gl_TessLevelInner[0] = 1.0; + gl_TessLevelInner[1] = 2.0; +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc new file mode 100644 index 0000000..df2cddb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/tesc/struct-copy.desktop.sso.tesc @@ -0,0 +1,22 @@ +#version 450 + +struct Boo +{ + vec3 a; + vec3 b; +}; + +layout(vertices = 4) out; +layout(location = 0) out Boo vVertex[]; +layout(location = 0) in Boo vInput[]; + +void main() +{ + vVertex[gl_InvocationID] = vInput[gl_InvocationID]; + gl_TessLevelOuter[0] = 1.0; + gl_TessLevelOuter[1] = 2.0; + gl_TessLevelOuter[2] = 3.0; + gl_TessLevelOuter[3] = 4.0; + gl_TessLevelInner[0] = 1.0; + gl_TessLevelInner[1] = 2.0; +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese b/third_party/spirv-cross/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese new file mode 100644 index 0000000..c964fbe --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/tese/triangle.desktop.sso.tese @@ -0,0 +1,22 @@ +#version 450 + +layout(cw, triangles, fractional_even_spacing) in; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[gl_MaxPatchVertices]; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = + gl_in[0].gl_Position * gl_TessCoord.x + + gl_in[1].gl_Position * gl_TessCoord.y + + gl_in[2].gl_Position * gl_TessCoord.z; +} + diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/vert/basic.desktop.sso.vert b/third_party/spirv-cross/shaders-msl/desktop-only/vert/basic.desktop.sso.vert new file mode 100644 index 0000000..9ddab08 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/vert/basic.desktop.sso.vert @@ -0,0 +1,20 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(std140) uniform UBO +{ + mat4 uMVP; +}; +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert b/third_party/spirv-cross/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert new file mode 100644 index 0000000..9c0f1d5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/vert/clip-cull-distance..no-user-varying.desktop.vert @@ -0,0 +1,10 @@ +#version 450 + +void main() +{ + gl_Position = vec4(10.0); + gl_ClipDistance[0] = 1.0; + gl_ClipDistance[1] = 4.0; + //gl_CullDistance[0] = 4.0; + //gl_CullDistance[1] = 9.0; +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert b/third_party/spirv-cross/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert new file mode 100644 index 0000000..9c0f1d5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/vert/clip-cull-distance.desktop.vert @@ -0,0 +1,10 @@ +#version 450 + +void main() +{ + gl_Position = vec4(10.0); + gl_ClipDistance[0] = 1.0; + gl_ClipDistance[1] = 4.0; + //gl_CullDistance[0] = 4.0; + //gl_CullDistance[1] = 9.0; +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert b/third_party/spirv-cross/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert new file mode 100644 index 0000000..fadd1e7 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.for-tess.vert @@ -0,0 +1,11 @@ +#version 460 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = vec4(gl_BaseVertex, gl_BaseInstance, 0, 1); +} diff --git a/third_party/spirv-cross/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert b/third_party/spirv-cross/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert new file mode 100644 index 0000000..fadd1e7 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/desktop-only/vert/shader-draw-parameters.desktop.vert @@ -0,0 +1,11 @@ +#version 460 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = vec4(gl_BaseVertex, gl_BaseInstance, 0, 1); +} diff --git a/third_party/spirv-cross/shaders-msl/flatten/basic.flatten.vert b/third_party/spirv-cross/shaders-msl/flatten/basic.flatten.vert new file mode 100644 index 0000000..e60a906 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/flatten/basic.flatten.vert @@ -0,0 +1,16 @@ +#version 310 es + +layout(std140) uniform UBO +{ + mat4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders-msl/flatten/multiindex.flatten.vert b/third_party/spirv-cross/shaders-msl/flatten/multiindex.flatten.vert new file mode 100644 index 0000000..0b471d8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/flatten/multiindex.flatten.vert @@ -0,0 +1,13 @@ +#version 310 es + +layout(std140) uniform UBO +{ + vec4 Data[3][5]; +}; + +layout(location = 0) in ivec2 aIndex; + +void main() +{ + gl_Position = Data[aIndex.x][aIndex.y]; +} diff --git a/third_party/spirv-cross/shaders-msl/flatten/push-constant.flatten.vert b/third_party/spirv-cross/shaders-msl/flatten/push-constant.flatten.vert new file mode 100644 index 0000000..c7b1b42 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/flatten/push-constant.flatten.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(push_constant, std430) uniform PushMe +{ + mat4 MVP; + mat2 Rot; // The MatrixStride will be 8 here. + float Arr[4]; +} registers; + +layout(location = 0) in vec2 Rot; +layout(location = 1) in vec4 Pos; +layout(location = 0) out vec2 vRot; +void main() +{ + gl_Position = registers.MVP * Pos; + vRot = registers.Rot * Rot + registers.Arr[2]; // Constant access should work even if array stride is just 4 here. +} diff --git a/third_party/spirv-cross/shaders-msl/flatten/rowmajor.flatten.vert b/third_party/spirv-cross/shaders-msl/flatten/rowmajor.flatten.vert new file mode 100644 index 0000000..88c468c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/flatten/rowmajor.flatten.vert @@ -0,0 +1,16 @@ +#version 310 es + +layout(std140) uniform UBO +{ + layout(column_major) mat4 uMVPR; + layout(row_major) mat4 uMVPC; + layout(row_major) mat2x4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; + +void main() +{ + vec2 v = aVertex * uMVP; + gl_Position = uMVPR * aVertex + uMVPC * aVertex; +} diff --git a/third_party/spirv-cross/shaders-msl/flatten/struct.flatten.vert b/third_party/spirv-cross/shaders-msl/flatten/struct.flatten.vert new file mode 100644 index 0000000..936bb41 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/flatten/struct.flatten.vert @@ -0,0 +1,30 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + + vec4 Color; +}; + +layout(std140) uniform UBO +{ + mat4 uMVP; + + Light light; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec4 vColor; + +void main() +{ + gl_Position = uMVP * aVertex; + + vColor = vec4(0.0); + + vec3 L = aVertex.xyz - light.Position; + vColor += dot(aNormal, normalize(L)) * (clamp(1.0 - length(L) / light.Radius, 0.0, 1.0) * light.Color); +} diff --git a/third_party/spirv-cross/shaders-msl/flatten/swizzle.flatten.vert b/third_party/spirv-cross/shaders-msl/flatten/swizzle.flatten.vert new file mode 100644 index 0000000..e310cdf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/flatten/swizzle.flatten.vert @@ -0,0 +1,47 @@ +#version 310 es + +// comments note the 16b alignment boundaries (see GL spec 7.6.2.2 Standard Uniform Block Layout) +layout(std140) uniform UBO +{ + // 16b boundary + vec4 A; + // 16b boundary + vec2 B0; + vec2 B1; + // 16b boundary + float C0; + // 16b boundary (vec3 is aligned to 16b) + vec3 C1; + // 16b boundary + vec3 D0; + float D1; + // 16b boundary + float E0; + float E1; + float E2; + float E3; + // 16b boundary + float F0; + vec2 F1; + // 16b boundary (vec2 before us is aligned to 8b) + float F2; +}; + +layout(location = 0) out vec4 oA; +layout(location = 1) out vec4 oB; +layout(location = 2) out vec4 oC; +layout(location = 3) out vec4 oD; +layout(location = 4) out vec4 oE; +layout(location = 5) out vec4 oF; + +void main() +{ + gl_Position = vec4(0.0); + + oA = A; + oB = vec4(B0, B1); + oC = vec4(C0, C1) + vec4(C1.xy, C1.z, C0); // not packed + oD = vec4(D0, D1) + vec4(D0.xy, D0.z, D1); // packed - must convert for swizzle + oE = vec4(E0, E1, E2, E3); + oF = vec4(F0, F1, F2); +} diff --git a/third_party/spirv-cross/shaders-msl/flatten/types.flatten.frag b/third_party/spirv-cross/shaders-msl/flatten/types.flatten.frag new file mode 100644 index 0000000..c123144 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/flatten/types.flatten.frag @@ -0,0 +1,27 @@ +#version 310 es +precision mediump float; + +layout(std140, binding = 0) uniform UBO0 +{ + vec4 a; + vec4 b; +}; + +layout(std140, binding = 1) uniform UBO1 +{ + ivec4 c; + ivec4 d; +}; + +layout(std140, binding = 2) uniform UBO2 +{ + uvec4 e; + uvec4 f; +}; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(c) + vec4(d) + vec4(e) + vec4(f) + a + b; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/argument-buffers.msl2.argument.frag b/third_party/spirv-cross/shaders-msl/frag/argument-buffers.msl2.argument.frag new file mode 100644 index 0000000..0d9f6cd --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/argument-buffers.msl2.argument.frag @@ -0,0 +1,61 @@ +#version 450 + +layout(std430, push_constant) uniform Push +{ + vec4 push; +} registers; + +layout(std140, set = 0, binding = 5) uniform UBO +{ + vec4 ubo; +}; + +layout(std430, set = 1, binding = 7) buffer SSBO +{ + vec4 ssbo; +}; + +layout(std430, set = 1, binding = 8) readonly buffer SSBOs +{ + vec4 ssbo; +} ssbos[2]; + +layout(std140, set = 2, binding = 4) uniform UBOs +{ + vec4 ubo; +} ubos[4]; + +layout(set = 0, binding = 2) uniform sampler2D uTexture; +layout(set = 0, binding = 6) uniform sampler2D uTextures[2]; +layout(set = 1, binding = 3) uniform texture2D uTexture2[4]; +layout(set = 1, binding = 10) uniform sampler uSampler[2]; +layout(location = 0) in vec2 vUV; +layout(location = 0) out vec4 FragColor; + +vec4 sample_in_function2() +{ + vec4 ret = texture(uTexture, vUV); + ret += texture(sampler2D(uTexture2[2], uSampler[1]), vUV); + ret += texture(uTextures[1], vUV); + ret += ssbo; + ret += ssbos[0].ssbo; + ret += registers.push; + return ret; +} + +vec4 sample_in_function() +{ + vec4 ret = sample_in_function2(); + ret += ubo; + ret += ubos[0].ubo; + return ret; +} + +void main() +{ + FragColor = sample_in_function(); + FragColor += ubo; + FragColor += ssbo; + FragColor += ubos[1].ubo; + FragColor += registers.push; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/shaders-msl/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..3493e0c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 v0; + +void main() +{ + float lut[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); + for (int i = 0; i < 4; i++, FragColor += lut[i]) + { + } +} diff --git a/third_party/spirv-cross/shaders-msl/frag/array-of-array-lut.frag b/third_party/spirv-cross/shaders-msl/frag/array-of-array-lut.frag new file mode 100644 index 0000000..c401a3f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/array-of-array-lut.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out float vOutput; +layout(location = 0) flat in int vIndex1; +layout(location = 1) flat in int vIndex2; + +const float FOO[2][3] = float[][](float[](1.0, 2.0, 3.0), float[](4.0, 5.0, 6.0)); + +void main() +{ + vOutput = FOO[vIndex1][vIndex2]; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag b/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag new file mode 100644 index 0000000..ec25ceb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.argument.discrete.swizzle.frag @@ -0,0 +1,34 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2D uSampler[4]; +layout(set = 0, binding = 1) uniform sampler2D uSamp; + +layout(set = 0, binding = 2) uniform UBO +{ + uint index; +} uUBO; + +layout(set = 0, binding = 3) uniform UBO2 +{ + uint index2; +}; + +layout(location = 0) in vec2 vUV; + +layout(location = 0) out vec4 FragColor; + +vec4 sample_in_func() +{ + return texture(uSampler[uUBO.index], vUV); +} + +vec4 sample_single_in_func(sampler2D s) +{ + return texture(s, vUV); +} + +void main() +{ + FragColor = sample_in_func(); + FragColor += sample_single_in_func(uSampler[index2]); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag b/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag new file mode 100644 index 0000000..ec25ceb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle-nonconstant-uniform.msl2.swizzle.frag @@ -0,0 +1,34 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2D uSampler[4]; +layout(set = 0, binding = 1) uniform sampler2D uSamp; + +layout(set = 0, binding = 2) uniform UBO +{ + uint index; +} uUBO; + +layout(set = 0, binding = 3) uniform UBO2 +{ + uint index2; +}; + +layout(location = 0) in vec2 vUV; + +layout(location = 0) out vec4 FragColor; + +vec4 sample_in_func() +{ + return texture(uSampler[uUBO.index], vUV); +} + +vec4 sample_single_in_func(sampler2D s) +{ + return texture(s, vUV); +} + +void main() +{ + FragColor = sample_in_func(); + FragColor += sample_single_in_func(uSampler[index2]); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag b/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag new file mode 100644 index 0000000..556cc9c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle.msl2.argument.discrete.swizzle.frag @@ -0,0 +1,31 @@ +#version 450 + +layout(set = 0, binding = 1) uniform sampler2D uSampler0[4]; +layout(set = 2, binding = 0) uniform sampler2D uSampler1; +layout(set = 1, binding = 4) uniform sampler2D uSamp; +layout(location = 0) in vec2 vUV; + +layout(location = 0) out vec4 FragColor; + +vec4 sample_in_func_1() +{ + return texture(uSampler0[2], vUV); +} + +vec4 sample_in_func_2() +{ + return texture(uSampler1, vUV); +} + +vec4 sample_single_in_func(sampler2D s) +{ + return texture(s, vUV); +} + +void main() +{ + FragColor = sample_in_func_1(); + FragColor += sample_in_func_2(); + FragColor += sample_single_in_func(uSampler0[1]); + FragColor += sample_single_in_func(uSampler1); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag b/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag new file mode 100644 index 0000000..4694aa3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/array-of-texture-swizzle.msl2.swizzle.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(set = 0, binding = 0) uniform sampler2D uSampler[4]; +layout(set = 0, binding = 1) uniform sampler2D uSamp; +layout(location = 0) in vec2 vUV; + +layout(location = 0) out vec4 FragColor; + +vec4 sample_in_func() +{ + return texture(uSampler[2], vUV); +} + +vec4 sample_single_in_func(sampler2D s) +{ + return texture(s, vUV); +} + +void main() +{ + FragColor = sample_in_func(); + FragColor += sample_single_in_func(uSampler[1]); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag b/third_party/spirv-cross/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag new file mode 100644 index 0000000..24edb1a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/barycentric-nv-nopersp.msl22.frag @@ -0,0 +1,17 @@ +#version 450 +#extension GL_NV_fragment_shader_barycentric : require + +layout(location = 0) out vec2 value; + +layout(set = 0, binding = 0) readonly buffer Vertices +{ + vec2 uvs[]; +}; + +void main () { + int prim = gl_PrimitiveID; + vec2 uv0 = uvs[3 * prim + 0]; + vec2 uv1 = uvs[3 * prim + 1]; + vec2 uv2 = uvs[3 * prim + 2]; + value = gl_BaryCoordNoPerspNV.x * uv0 + gl_BaryCoordNoPerspNV.y * uv1 + gl_BaryCoordNoPerspNV.z * uv2; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/barycentric-nv.msl22.frag b/third_party/spirv-cross/shaders-msl/frag/barycentric-nv.msl22.frag new file mode 100644 index 0000000..7aec19f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/barycentric-nv.msl22.frag @@ -0,0 +1,17 @@ +#version 450 +#extension GL_NV_fragment_shader_barycentric : require + +layout(location = 0) out vec2 value; + +layout(set = 0, binding = 0) readonly buffer Vertices +{ + vec2 uvs[]; +}; + +void main () { + int prim = gl_PrimitiveID; + vec2 uv0 = uvs[3 * prim + 0]; + vec2 uv1 = uvs[3 * prim + 1]; + vec2 uv2 = uvs[3 * prim + 2]; + value = gl_BaryCoordNV.x * uv0 + gl_BaryCoordNV.y * uv1 + gl_BaryCoordNV.z * uv2; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/basic.frag b/third_party/spirv-cross/shaders-msl/frag/basic.frag new file mode 100644 index 0000000..dd9a8f8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/basic.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor * texture(uTex, vTex); +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/binary-func-unpack-pack-arguments.frag b/third_party/spirv-cross/shaders-msl/frag/binary-func-unpack-pack-arguments.frag new file mode 100644 index 0000000..c0e5dab --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/binary-func-unpack-pack-arguments.frag @@ -0,0 +1,15 @@ +#version 450 +layout(location = 0) out float FragColor; + +layout(binding = 0, std140) uniform UBO +{ + vec3 color; + float v; +}; + +layout(location = 0) in vec3 vIn; + +void main() +{ + FragColor = dot(vIn, color); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/binary-unpack-pack-arguments.frag b/third_party/spirv-cross/shaders-msl/frag/binary-unpack-pack-arguments.frag new file mode 100644 index 0000000..be30f84 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/binary-unpack-pack-arguments.frag @@ -0,0 +1,15 @@ +#version 450 +layout(location = 0) out vec3 FragColor; + +layout(binding = 0, std140) uniform UBO +{ + vec3 color; + float v; +}; + +layout(location = 0) in vec3 vIn; + +void main() +{ + FragColor = cross(vIn, color - vIn); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/bitcasting.1d-as-2d.frag b/third_party/spirv-cross/shaders-msl/frag/bitcasting.1d-as-2d.frag new file mode 100644 index 0000000..adaa749 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/bitcasting.1d-as-2d.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(binding = 0) uniform sampler1D TextureBase; +layout(binding = 1) uniform sampler1D TextureDetail; + +layout(location = 0) in vec4 VertGeom; + +layout(location = 0) out vec4 FragColor0; +layout(location = 1) out vec4 FragColor1; + +void main() +{ + vec4 texSample0 = texture(TextureBase, VertGeom.x); + vec4 texSample1 = textureOffset(TextureDetail, VertGeom.x, 3); + + ivec4 iResult0 = floatBitsToInt(texSample0); + ivec4 iResult1 = floatBitsToInt(texSample1); + FragColor0 = (intBitsToFloat(iResult0) * intBitsToFloat(iResult1)); + + uvec4 uResult0 = floatBitsToUint(texSample0); + uvec4 uResult1 = floatBitsToUint(texSample1); + FragColor1 = (uintBitsToFloat(uResult0) * uintBitsToFloat(uResult1)); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/bitcasting.frag b/third_party/spirv-cross/shaders-msl/frag/bitcasting.frag new file mode 100644 index 0000000..5dac78e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/bitcasting.frag @@ -0,0 +1,24 @@ +#version 310 es +precision mediump float; + +layout(binding = 0) uniform sampler2D TextureBase; +layout(binding = 1) uniform sampler2D TextureDetail; + +layout(location = 0) in vec4 VertGeom; + +layout(location = 0) out vec4 FragColor0; +layout(location = 1) out vec4 FragColor1; + +void main() +{ + vec4 texSample0 = texture(TextureBase, VertGeom.xy); + vec4 texSample1 = textureOffset(TextureDetail, VertGeom.xy, ivec2(3, 2)); + + ivec4 iResult0 = floatBitsToInt(texSample0); + ivec4 iResult1 = floatBitsToInt(texSample1); + FragColor0 = (intBitsToFloat(iResult0) * intBitsToFloat(iResult1)); + + uvec4 uResult0 = floatBitsToUint(texSample0); + uvec4 uResult1 = floatBitsToUint(texSample1); + FragColor1 = (uintBitsToFloat(uResult0) * uintBitsToFloat(uResult1)); +} \ No newline at end of file diff --git a/third_party/spirv-cross/shaders-msl/frag/buffer-read-write.frag b/third_party/spirv-cross/shaders-msl/frag/buffer-read-write.frag new file mode 100644 index 0000000..70af7d3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/buffer-read-write.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(rgba8, binding = 0) uniform readonly imageBuffer buf; +layout(rgba8, binding = 1) uniform writeonly imageBuffer bufOut; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = imageLoad(buf, 0); + imageStore(bufOut, int(gl_FragCoord.x), FragColor); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag b/third_party/spirv-cross/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag new file mode 100644 index 0000000..70af7d3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/buffer-read-write.texture-buffer-native.msl21.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(rgba8, binding = 0) uniform readonly imageBuffer buf; +layout(rgba8, binding = 1) uniform writeonly imageBuffer bufOut; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = imageLoad(buf, 0); + imageStore(bufOut, int(gl_FragCoord.x), FragColor); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/builtins.frag b/third_party/spirv-cross/shaders-msl/frag/builtins.frag new file mode 100644 index 0000000..99e6e2d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/builtins.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = gl_FragCoord + vColor; + gl_FragDepth = 0.5; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/clip-distance-varying.frag b/third_party/spirv-cross/shaders-msl/frag/clip-distance-varying.frag new file mode 100644 index 0000000..df49bd5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/clip-distance-varying.frag @@ -0,0 +1,10 @@ +#version 450 + +in float gl_ClipDistance[2]; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0 - gl_ClipDistance[0] - gl_ClipDistance[1]); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/shaders-msl/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..47f9393 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; + +struct Foo +{ + vec4 a; + vec4 b; +}; + +layout(binding = 0) buffer UBO +{ + vec4 results[1024]; +}; + +layout(binding = 1) uniform highp isampler2D Buf; +layout(location = 0) flat in int vIn; +layout(location = 1) flat in int vIn2; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + ivec4 coords = texelFetch(Buf, ivec2(gl_FragCoord.xy), 0); + vec4 foo = results[coords.x % 16]; + + int c = vIn * vIn; + int d = vIn2 * vIn2; + FragColor = foo + foo + results[c + d]; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/composite-extract-forced-temporary.frag b/third_party/spirv-cross/shaders-msl/frag/composite-extract-forced-temporary.frag new file mode 100644 index 0000000..35fdbe8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/composite-extract-forced-temporary.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +layout(binding = 0) uniform sampler2D Texture; +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTexCoord; + +void main() +{ + float f = texture(Texture, vTexCoord).x; + FragColor = vec4(f * f); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/constant-array.frag b/third_party/spirv-cross/shaders-msl/frag/constant-array.frag new file mode 100644 index 0000000..b862cb1 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/constant-array.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; +layout(location = 0) out vec4 FragColor; + +layout(location = 0) flat in int index; + +struct Foobar { float a; float b; }; + +vec4 resolve(Foobar f) +{ + return vec4(f.a + f.b); +} + +void main() +{ + const vec4 foo[3] = vec4[](vec4(1.0), vec4(2.0), vec4(3.0)); + const vec4 foobars[2][2] = vec4[][](vec4[](vec4(1.0), vec4(2.0)), vec4[](vec4(8.0), vec4(10.0))); + const Foobar foos[2] = Foobar[](Foobar(10.0, 40.0), Foobar(90.0, 70.0)); + + FragColor = foo[index] + foobars[index][index + 1] + resolve(Foobar(10.0, 20.0)) + resolve(foos[index]); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/constant-composites.frag b/third_party/spirv-cross/shaders-msl/frag/constant-composites.frag new file mode 100644 index 0000000..a12e22f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/constant-composites.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; + +float lut[4] = float[](1.0, 4.0, 3.0, 2.0); + +struct Foo +{ + float a; + float b; +}; +Foo foos[2] = Foo[](Foo(10.0, 20.0), Foo(30.0, 40.0)); + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in int line; + +void main() +{ + FragColor = vec4(lut[line]); + FragColor += foos[line].a * foos[1 - line].a; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/shaders-msl/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..1f21bef --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,34 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uSampler; +layout(location = 0) in vec4 vInput; + +void main() +{ + FragColor = vInput; + vec4 t = texture(uSampler, vInput.xy); + vec4 d0 = dFdx(vInput); + vec4 d1 = dFdy(vInput); + vec4 d2 = fwidth(vInput); + vec4 d3 = dFdxCoarse(vInput); + vec4 d4 = dFdyCoarse(vInput); + vec4 d5 = fwidthCoarse(vInput); + vec4 d6 = dFdxFine(vInput); + vec4 d7 = dFdyFine(vInput); + vec4 d8 = fwidthFine(vInput); + if (vInput.y > 10.0) + { + FragColor += t; + FragColor += d0; + FragColor += d1; + FragColor += d2; + FragColor += d3; + FragColor += d4; + FragColor += d5; + FragColor += d6; + FragColor += d7; + FragColor += d8; + } +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/depth-greater-than.frag b/third_party/spirv-cross/shaders-msl/frag/depth-greater-than.frag new file mode 100644 index 0000000..8dcbcfd --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/depth-greater-than.frag @@ -0,0 +1,7 @@ +#version 450 +layout(depth_greater) out float gl_FragDepth; + +void main() +{ + gl_FragDepth = 0.5; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/depth-less-than.frag b/third_party/spirv-cross/shaders-msl/frag/depth-less-than.frag new file mode 100644 index 0000000..cdcb80f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/depth-less-than.frag @@ -0,0 +1,7 @@ +#version 450 +layout(depth_less) out float gl_FragDepth; + +void main() +{ + gl_FragDepth = 0.5; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/disable-frag-output.frag-output.frag b/third_party/spirv-cross/shaders-msl/frag/disable-frag-output.frag-output.frag new file mode 100644 index 0000000..7e149b8 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/disable-frag-output.frag-output.frag @@ -0,0 +1,25 @@ +#version 450 +#extension GL_ARB_shader_stencil_export : require + +layout(location = 0) out vec4 buf0; +layout(location = 1) out vec4 buf1; +layout(location = 2) out vec4 buf2; +layout(location = 3) out vec4 buf3; +layout(location = 4) out vec4 buf4; +layout(location = 5) out vec4 buf5; +layout(location = 6) out vec4 buf6; +layout(location = 7) out vec4 buf7; + +void main() { + buf0 = vec4(0, 0, 0, 1); + buf1 = vec4(1, 0, 0, 1); + buf2 = vec4(0, 1, 0, 1); + buf3 = vec4(0, 0, 1, 1); + buf4 = vec4(1, 0, 1, 0.5); + buf5 = vec4(0.25, 0.25, 0.25, 0.25); + buf6 = vec4(0.75, 0.75, 0.75, 0.75); + buf7 = vec4(1, 1, 1, 1); + gl_FragDepth = 0.9; + gl_FragStencilRefARB = 127; +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/dual-source-blending.frag b/third_party/spirv-cross/shaders-msl/frag/dual-source-blending.frag new file mode 100644 index 0000000..f322cf4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/dual-source-blending.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0, index = 0) out vec4 FragColor0; +layout(location = 0, index = 1) out vec4 FragColor1; + +void main() +{ + FragColor0 = vec4(1.0); + FragColor1 = vec4(2.0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/early-fragment-tests.frag b/third_party/spirv-cross/shaders-msl/frag/early-fragment-tests.frag new file mode 100644 index 0000000..1a0acb9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/early-fragment-tests.frag @@ -0,0 +1,9 @@ +#version 450 +layout(early_fragment_tests) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/false-loop-init.frag b/third_party/spirv-cross/shaders-msl/frag/false-loop-init.frag new file mode 100644 index 0000000..7ce5b52 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/false-loop-init.frag @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 accum; +layout(location = 0) out vec4 result; + +void main() +{ + result = vec4(0.0); + uint j; + for (int i = 0; i < 4; i += int(j)) + { + if (accum.y > 10.0) + j = 40u; + else + j = 30u; + result += accum; + } +} diff --git a/third_party/spirv-cross/shaders-msl/frag/flush_params.frag b/third_party/spirv-cross/shaders-msl/frag/flush_params.frag new file mode 100644 index 0000000..8a26ad3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/flush_params.frag @@ -0,0 +1,27 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; + +struct Structy +{ + vec4 c; +}; + +void foo2(out Structy f) +{ + f.c = vec4(10.0); +} + +Structy foo() +{ + Structy f; + foo2(f); + return f; +} + +void main() +{ + Structy s = foo(); + FragColor = s.c; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/shaders-msl/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..1f91cca --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,11 @@ +#version 450 +layout(location = 0) out vec4 FragColor; +void main() +{ + FragColor = vec4(0.0); + for (int i = 0; i < 3; (0 > 1) ? 1 : i ++) + { + int a = i; + FragColor[a] += float(i); + } +} diff --git a/third_party/spirv-cross/shaders-msl/frag/for-loop-init.frag b/third_party/spirv-cross/shaders-msl/frag/for-loop-init.frag new file mode 100644 index 0000000..0cde267 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/for-loop-init.frag @@ -0,0 +1,52 @@ +#version 310 es +precision mediump float; +layout(location = 0) out int FragColor; + +void main() +{ + FragColor = 16; + + // Basic loop variable. + for (int i = 0; i < 25; i++) + FragColor += 10; + + // Multiple loop variables. + for (int i = 1, j = 4; i < 30; i++, j += 4) + FragColor += 11; + + // A potential loop variables, but we access it outside the loop, + // so cannot be one. + int k = 0; + for (; k < 20; k++) + FragColor += 12; + k += 3; + FragColor += k; + + // Potential loop variables, but the dominator is not trivial. + int l; + if (k == 40) + { + for (l = 0; l < 40; l++) + FragColor += 13; + return; + } + else + { + l = k; + FragColor += l; + } + + // Vectors cannot be loop variables + for (ivec2 i = ivec2(0); i.x < 10; i.x += 4) + { + FragColor += i.y; + } + + // Check that static expressions can be used before the loop header. + int m = 0; + m = k; + int o = m; + for (; m < 40; m++) + FragColor += m; + FragColor += o; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/fp16-packing.frag b/third_party/spirv-cross/shaders-msl/frag/fp16-packing.frag new file mode 100644 index 0000000..98ca24e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/fp16-packing.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) flat in uint FP16; +layout(location = 1) flat in vec2 FP32; +layout(location = 0) out vec2 FP32Out; +layout(location = 1) out uint FP16Out; + +void main() +{ + FP32Out = unpackHalf2x16(FP16); + FP16Out = packHalf2x16(FP32); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/fragment-component-padding.pad-fragment.frag b/third_party/spirv-cross/shaders-msl/frag/fragment-component-padding.pad-fragment.frag new file mode 100644 index 0000000..240c59b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/fragment-component-padding.pad-fragment.frag @@ -0,0 +1,18 @@ +#version 450 +layout(location = 0) out float FragColors[2]; +layout(location = 2) out vec2 FragColor2; +layout(location = 3) out vec3 FragColor3; +layout(location = 0) in vec3 vColor; + +void set_globals() +{ + FragColors[0] = vColor.x; + FragColors[1] = vColor.y; + FragColor2 = vColor.xz; + FragColor3 = vColor.zzz; +} + +void main() +{ + set_globals(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/front-facing.frag b/third_party/spirv-cross/shaders-msl/frag/front-facing.frag new file mode 100644 index 0000000..90ca1ab --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/front-facing.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; + +void main() +{ + if (gl_FrontFacing) + FragColor = vA; + else + FragColor = vB; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/gather-dref.frag b/third_party/spirv-cross/shaders-msl/frag/gather-dref.frag new file mode 100644 index 0000000..a8aac56 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/gather-dref.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(binding = 0) uniform mediump sampler2DShadow uT; +layout(location = 0) in vec3 vUV; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureGather(uT, vUV.xy, vUV.z); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/gather-offset.frag b/third_party/spirv-cross/shaders-msl/frag/gather-offset.frag new file mode 100644 index 0000000..409317a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/gather-offset.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uT; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureGather(uT, vec2(0.5), 3); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/helper-invocation.msl21.frag b/third_party/spirv-cross/shaders-msl/frag/helper-invocation.msl21.frag new file mode 100644 index 0000000..1da8c57 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/helper-invocation.msl21.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; +layout(binding = 0) uniform sampler2D uSampler; + +vec4 foo() +{ + vec4 color; + if (!gl_HelperInvocation) + color = textureLod(uSampler, vUV, 0.0); + else + color = vec4(1.0); + return color; +} + +void main() +{ + FragColor = foo(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag b/third_party/spirv-cross/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag new file mode 100644 index 0000000..28d2b4a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/huge-argument-buffer.device-argument-buffer.argument.msl2.frag @@ -0,0 +1,26 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; +layout(set = 0, binding = 0) uniform sampler2D uSamplers[10000]; +layout(set = 2, binding = 0) uniform sampler2D uSampler; + +layout(set = 1, binding = 0) uniform UBO +{ + vec4 v; +} vs[10000]; + +vec4 samp_array() +{ + return texture(uSamplers[9999], vUV) + vs[5000].v; +} + +vec4 samp_single() +{ + return texture(uSampler, vUV); +} + +void main() +{ + FragColor = samp_array() + samp_single(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/illegal-name-test-0.frag b/third_party/spirv-cross/shaders-msl/frag/illegal-name-test-0.frag new file mode 100644 index 0000000..8e6c11d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/illegal-name-test-0.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 fragment = vec4(10.0); + vec4 compute = vec4(10.0); + vec4 kernel = vec4(10.0); + vec4 vertex = vec4(10.0); + FragColor = fragment + compute + kernel + vertex; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/image-query-lod.msl22.frag b/third_party/spirv-cross/shaders-msl/frag/image-query-lod.msl22.frag new file mode 100644 index 0000000..33d5630 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/image-query-lod.msl22.frag @@ -0,0 +1,33 @@ +#version 450 + +layout(location = 0) out vec2 FragColor; +layout(set = 0, binding = 0) uniform sampler2D uSampler2D; +layout(set = 0, binding = 1) uniform sampler3D uSampler3D; +layout(set = 0, binding = 2) uniform samplerCube uSamplerCube; +layout(set = 0, binding = 3) uniform sampler uSampler; +layout(set = 0, binding = 4) uniform texture2D uTexture2D; +layout(set = 0, binding = 5) uniform texture3D uTexture3D; +layout(set = 0, binding = 6) uniform textureCube uTextureCube; +layout(location = 0) in vec3 vUV; + +void from_function() +{ + FragColor += textureQueryLod(uSampler2D, vUV.xy); + FragColor += textureQueryLod(uSampler3D, vUV); + FragColor += textureQueryLod(uSamplerCube, vUV); + FragColor += textureQueryLod(sampler2D(uTexture2D, uSampler), vUV.xy); + FragColor += textureQueryLod(sampler3D(uTexture3D, uSampler), vUV); + FragColor += textureQueryLod(samplerCube(uTextureCube, uSampler), vUV); +} + +void main() +{ + FragColor = vec2(0.0); + FragColor += textureQueryLod(uSampler2D, vUV.xy); + FragColor += textureQueryLod(uSampler3D, vUV); + FragColor += textureQueryLod(uSamplerCube, vUV); + FragColor += textureQueryLod(sampler2D(uTexture2D, uSampler), vUV.xy); + FragColor += textureQueryLod(sampler3D(uTexture3D, uSampler), vUV); + FragColor += textureQueryLod(samplerCube(uTextureCube, uSampler), vUV); + from_function(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/in_block.frag b/third_party/spirv-cross/shaders-msl/frag/in_block.frag new file mode 100644 index 0000000..59b9707 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/in_block.frag @@ -0,0 +1,14 @@ +#version 450 + +layout(location = 2) in VertexOut +{ + vec4 color; + vec4 color2; +} inputs; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = inputs.color + inputs.color2; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/in_mat.frag b/third_party/spirv-cross/shaders-msl/frag/in_mat.frag new file mode 100644 index 0000000..dd0b5d0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/in_mat.frag @@ -0,0 +1,19 @@ +#version 450 + +layout(binding = 1) uniform samplerCube samplerColor; + +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec3 inNormal; +layout(location = 2) in mat4 inInvModelView; +layout(location = 6) in float inLodBias; +layout(location = 0) out vec4 outFragColor; + +void main() +{ + vec3 cI = normalize(inPos); + vec3 cR = reflect(cI, normalize(inNormal)); + cR = vec3((inInvModelView * vec4(cR, 0.0)).xyz); + cR.x *= (-1.0); + outFragColor = texture(samplerColor, cR, inLodBias); +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag b/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag new file mode 100644 index 0000000..b3d44c9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.arrayed-subpass.msl21.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +vec4 load_subpasses(mediump subpassInputMS uInput) +{ + return subpassLoad(uInput, gl_SampleID); +} + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + load_subpasses(uSubpass0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.frag b/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.frag new file mode 100644 index 0000000..b3d44c9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +vec4 load_subpasses(mediump subpassInputMS uInput) +{ + return subpassLoad(uInput, gl_SampleID); +} + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + load_subpasses(uSubpass0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag b/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag new file mode 100644 index 0000000..b3d44c9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/input-attachment-ms.multiview.msl21.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +vec4 load_subpasses(mediump subpassInputMS uInput) +{ + return subpassLoad(uInput, gl_SampleID); +} + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + load_subpasses(uSubpass0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/input-attachment.arrayed-subpass.frag b/third_party/spirv-cross/shaders-msl/frag/input-attachment.arrayed-subpass.frag new file mode 100644 index 0000000..877d052 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/input-attachment.arrayed-subpass.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec4 FragColor; + +vec4 load_subpasses(mediump subpassInput uInput) +{ + return subpassLoad(uInput); +} + +void main() +{ + FragColor = subpassLoad(uSubpass0) + load_subpasses(uSubpass1); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/input-attachment.frag b/third_party/spirv-cross/shaders-msl/frag/input-attachment.frag new file mode 100644 index 0000000..877d052 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/input-attachment.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec4 FragColor; + +vec4 load_subpasses(mediump subpassInput uInput) +{ + return subpassLoad(uInput); +} + +void main() +{ + FragColor = subpassLoad(uSubpass0) + load_subpasses(uSubpass1); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/input-attachment.multiview.frag b/third_party/spirv-cross/shaders-msl/frag/input-attachment.multiview.frag new file mode 100644 index 0000000..877d052 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/input-attachment.multiview.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec4 FragColor; + +vec4 load_subpasses(mediump subpassInput uInput) +{ + return subpassLoad(uInput); +} + +void main() +{ + FragColor = subpassLoad(uSubpass0) + load_subpasses(uSubpass1); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/interpolation-qualifiers-block.frag b/third_party/spirv-cross/shaders-msl/frag/interpolation-qualifiers-block.frag new file mode 100644 index 0000000..4842089 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/interpolation-qualifiers-block.frag @@ -0,0 +1,19 @@ +#version 450 + +struct Input { + vec2 v0; + vec2 v1; + vec3 v2; + vec4 v3; + float v4; + float v5; + float v6; +}; + +layout(location=0) in centroid noperspective Input inp; + +layout(location=0) out vec4 FragColor; + +void main() { + FragColor = vec4(inp.v0.x + inp.v1.y, inp.v2.xy, inp.v3.w * inp.v4 + inp.v5 - inp.v6); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/interpolation-qualifiers.frag b/third_party/spirv-cross/shaders-msl/frag/interpolation-qualifiers.frag new file mode 100644 index 0000000..ef8a480 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/interpolation-qualifiers.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(location=0) in vec2 v0; +layout(location=1) in noperspective vec2 v1; +layout(location=2) in centroid vec3 v2; +layout(location=3) in centroid noperspective vec4 v3; +layout(location=4) in sample float v4; +layout(location=5) in sample noperspective float v5; +layout(location=6) in flat float v6; + +layout(location=0) out vec4 FragColor; + +void main() { + FragColor = vec4(v0.x + v1.y, v2.xy, v3.w * v4 + v5 - v6); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/lut-promotion.frag b/third_party/spirv-cross/shaders-msl/frag/lut-promotion.frag new file mode 100644 index 0000000..0cdc814 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/lut-promotion.frag @@ -0,0 +1,44 @@ +#version 310 es +precision mediump float; +layout(location = 0) out float FragColor; +layout(location = 0) flat in int index; + +const float LUT[16] = float[]( + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0); + +void main() +{ + // Try reading LUTs, both in branches and not branch. + FragColor = LUT[index]; + if (index < 10) + FragColor += LUT[index ^ 1]; + else + FragColor += LUT[index & 1]; + + // Not declared as a LUT, but can be promoted to one. + vec4 foo[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + FragColor += foo[index & 3].y; + } + else + { + FragColor += foo[index & 1].x; + } + + // Not declared as a LUT, but this cannot be promoted, because we have a partial write. + vec4 foobar[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + + // Not declared as a LUT, but this cannot be promoted, because we have two complete writes. + vec4 baz[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/mix.frag b/third_party/spirv-cross/shaders-msl/frag/mix.frag new file mode 100644 index 0000000..a5d589d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/mix.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vIn0; +layout(location = 1) in vec4 vIn1; +layout(location = 2) in float vIn2; +layout(location = 3) in float vIn3; +layout(location = 0) out vec4 FragColor; + +void main() +{ + bvec4 l = bvec4(false, true, false, false); + FragColor = mix(vIn0, vIn1, l); + + bool f = true; + FragColor = vec4(mix(vIn2, vIn3, f)); + + FragColor = f ? vIn0 : vIn1; + FragColor = vec4(f ? vIn2 : vIn3); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/mrt-array.frag b/third_party/spirv-cross/shaders-msl/frag/mrt-array.frag new file mode 100644 index 0000000..0460c72 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/mrt-array.frag @@ -0,0 +1,24 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor[4]; +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; + +void write_deeper_in_function() +{ + FragColor[3] = vA * vB; +} + +void write_in_function() +{ + FragColor[2] = vA - vB; + write_deeper_in_function(); +} + +void main() +{ + FragColor[0] = mod(vA, vB); + FragColor[1] = vA + vB; + write_in_function(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/nonuniform-qualifier.msl2.frag b/third_party/spirv-cross/shaders-msl/frag/nonuniform-qualifier.msl2.frag new file mode 100644 index 0000000..ba9dd7f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/nonuniform-qualifier.msl2.frag @@ -0,0 +1,28 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require + +layout(binding = 0) uniform texture2D uSamplers[8]; +layout(binding = 8) uniform sampler2D uCombinedSamplers[8]; +layout(binding = 1) uniform sampler uSamps[7]; +layout(location = 0) flat in int vIndex; +layout(location = 1) in vec2 vUV; +layout(location = 0) out vec4 FragColor; + +layout(set = 0, binding = 0) uniform UBO +{ + vec4 v[64]; +} ubos[2]; + +layout(set = 0, binding = 2) readonly buffer SSBO +{ + vec4 v[]; +} ssbos[2]; + +void main() +{ + int i = vIndex; + FragColor = texture(sampler2D(uSamplers[nonuniformEXT(i + 10)], uSamps[nonuniformEXT(i + 40)]), vUV); + FragColor = texture(uCombinedSamplers[nonuniformEXT(i + 10)], vUV); + FragColor += ubos[nonuniformEXT(i + 20)].v[nonuniformEXT(i + 40)]; + FragColor += ssbos[nonuniformEXT(i + 50)].v[nonuniformEXT(i + 60)]; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/packed-expression-vector-shuffle.frag b/third_party/spirv-cross/shaders-msl/frag/packed-expression-vector-shuffle.frag new file mode 100644 index 0000000..9958443 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/packed-expression-vector-shuffle.frag @@ -0,0 +1,15 @@ +#version 450 +layout(location = 0) out vec4 FragColor; + +layout(binding = 0, std140) uniform UBO +{ + vec3 color; + float v; +}; + +void main() +{ + vec4 f = vec4(1.0); + f.rgb = color; + FragColor = f; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/packing-test-3.frag b/third_party/spirv-cross/shaders-msl/frag/packing-test-3.frag new file mode 100644 index 0000000..56ad6f5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/packing-test-3.frag @@ -0,0 +1,36 @@ +#version 450 + +struct VertexOutput +{ + vec4 HPosition; +}; + +struct TestStruct +{ + vec3 position; + float radius; +}; + +layout(binding = 0, std140) uniform CB0 +{ + TestStruct CB0[16]; +} _24; + +layout(location = 0) out vec4 _entryPointOutput; + +vec4 _main(VertexOutput IN) +{ + TestStruct st; + st.position = _24.CB0[1].position; + st.radius = _24.CB0[1].radius; + vec4 col = vec4(st.position, st.radius); + return col; +} + +void main() +{ + VertexOutput IN; + IN.HPosition = gl_FragCoord; + VertexOutput param = IN; + _entryPointOutput = _main(param); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag b/third_party/spirv-cross/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag new file mode 100644 index 0000000..ceac8cc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/pixel-interlock-ordered.msl2.argument.frag @@ -0,0 +1,36 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require + +layout(pixel_interlock_ordered) in; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; +layout(binding = 2, rgba8) uniform readonly image2D img3; +layout(binding = 3) coherent buffer Buffer +{ + int foo; + uint bar; +}; +layout(binding = 4) buffer Buffer2 +{ + uint quux; +}; + +layout(binding = 5, rgba8) uniform writeonly image2D img4; +layout(binding = 6) buffer Buffer3 +{ + int baz; +}; + +void main() +{ + // Deliberately outside the critical section to test usage tracking. + baz = 0; + imageStore(img4, ivec2(1, 1), vec4(1.0, 0.0, 0.0, 1.0)); + beginInvocationInterlockARB(); + imageStore(img, ivec2(0, 0), imageLoad(img3, ivec2(0, 0))); + imageAtomicAdd(img2, ivec2(0, 0), 1u); + foo += 42; + atomicAnd(bar, quux); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/pixel-interlock-ordered.msl2.frag b/third_party/spirv-cross/shaders-msl/frag/pixel-interlock-ordered.msl2.frag new file mode 100644 index 0000000..ceac8cc --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/pixel-interlock-ordered.msl2.frag @@ -0,0 +1,36 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require + +layout(pixel_interlock_ordered) in; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; +layout(binding = 2, rgba8) uniform readonly image2D img3; +layout(binding = 3) coherent buffer Buffer +{ + int foo; + uint bar; +}; +layout(binding = 4) buffer Buffer2 +{ + uint quux; +}; + +layout(binding = 5, rgba8) uniform writeonly image2D img4; +layout(binding = 6) buffer Buffer3 +{ + int baz; +}; + +void main() +{ + // Deliberately outside the critical section to test usage tracking. + baz = 0; + imageStore(img4, ivec2(1, 1), vec4(1.0, 0.0, 0.0, 1.0)); + beginInvocationInterlockARB(); + imageStore(img, ivec2(0, 0), imageLoad(img3, ivec2(0, 0))); + imageAtomicAdd(img2, ivec2(0, 0), 1u); + foo += 42; + atomicAnd(bar, quux); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/pls.frag b/third_party/spirv-cross/shaders-msl/frag/pls.frag new file mode 100644 index 0000000..e3863e4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/pls.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 PLSIn0; +layout(location = 1) in vec4 PLSIn1; +layout(location = 2) in vec4 PLSIn2; +layout(location = 3) in vec4 PLSIn3; + +layout(location = 0) out vec4 PLSOut0; +layout(location = 1) out vec4 PLSOut1; +layout(location = 2) out vec4 PLSOut2; +layout(location = 3) out vec4 PLSOut3; + +void main() +{ + PLSOut0 = 2.0 * PLSIn0; + PLSOut1 = 6.0 * PLSIn1; + PLSOut2 = 7.0 * PLSIn2; + PLSOut3 = 4.0 * PLSIn3; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/post-depth-coverage.ios.msl2.frag b/third_party/spirv-cross/shaders-msl/frag/post-depth-coverage.ios.msl2.frag new file mode 100644 index 0000000..4f134b4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/post-depth-coverage.ios.msl2.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_post_depth_coverage : require + +layout(post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(gl_SampleMaskIn[0]); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/post-depth-coverage.msl23.frag b/third_party/spirv-cross/shaders-msl/frag/post-depth-coverage.msl23.frag new file mode 100644 index 0000000..4f134b4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/post-depth-coverage.msl23.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_post_depth_coverage : require + +layout(post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(gl_SampleMaskIn[0]); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/private-variable-prototype-declaration.frag b/third_party/spirv-cross/shaders-msl/frag/private-variable-prototype-declaration.frag new file mode 100644 index 0000000..7d2bba5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/private-variable-prototype-declaration.frag @@ -0,0 +1,20 @@ +#version 450 + +struct AStruct { vec4 foobar; }; + +void someFunction(out AStruct s) { s.foobar = vec4(1.0); } + +highp vec3 global_variable; + +void otherFunction() { + global_variable = vec3(1.0); +} + +layout(location = 0) out vec3 FragColor; + +void main() { + AStruct inputs; + someFunction(inputs); + otherFunction(); + FragColor = global_variable; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/readonly-ssbo.frag b/third_party/spirv-cross/shaders-msl/frag/readonly-ssbo.frag new file mode 100644 index 0000000..9d7cff6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/readonly-ssbo.frag @@ -0,0 +1,16 @@ +#version 450 +layout(location = 0) out vec4 FragColor; +layout(binding = 0, std430) readonly buffer SSBO +{ + vec4 v; +}; + +vec4 read_from_function() +{ + return v; +} + +void main() +{ + FragColor = v + read_from_function(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag b/third_party/spirv-cross/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag new file mode 100644 index 0000000..9a855ac --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sample-depth-propagate-state-from-resource.frag @@ -0,0 +1,29 @@ +#version 450 + +layout(set = 0, binding = 0) uniform texture2D uTexture; +layout(set = 0, binding = 1) uniform sampler uSampler; +layout(set = 0, binding = 2) uniform samplerShadow uSamplerShadow; + +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vUV; + +float sample_normal2(texture2D tex) +{ + return texture(sampler2D(tex, uSampler), vUV.xy).x; +} + +float sample_normal(texture2D tex) +{ + return sample_normal2(tex); +} + +float sample_comp(texture2D tex) +{ + return texture(sampler2DShadow(tex, uSamplerShadow), vUV); +} + +void main() +{ + FragColor = sample_normal(uTexture); + FragColor += sample_comp(uTexture); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sample-depth-separate-image-sampler.frag b/third_party/spirv-cross/shaders-msl/frag/sample-depth-separate-image-sampler.frag new file mode 100644 index 0000000..db1f5e9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sample-depth-separate-image-sampler.frag @@ -0,0 +1,22 @@ +#version 450 + +layout(set = 0, binding = 0) uniform texture2D uDepth; +layout(set = 0, binding = 1) uniform texture2D uColor; +layout(set = 0, binding = 2) uniform sampler uSampler; +layout(set = 0, binding = 3) uniform samplerShadow uSamplerShadow; +layout(location = 0) out float FragColor; + +float sample_depth_from_function(texture2D uT, samplerShadow uS) +{ + return texture(sampler2DShadow(uT, uS), vec3(0.5)); +} + +float sample_color_from_function(texture2D uT, sampler uS) +{ + return texture(sampler2D(uT, uS), vec2(0.5)).x; +} + +void main() +{ + FragColor = sample_depth_from_function(uDepth, uSamplerShadow) + sample_color_from_function(uColor, uSampler); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag b/third_party/spirv-cross/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag new file mode 100644 index 0000000..b78ee61 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sample-mask-in-and-out.fixed-sample-mask.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); + gl_SampleMask[0] = gl_SampleMaskIn[0]; +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag b/third_party/spirv-cross/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag new file mode 100644 index 0000000..c3eaf5e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sample-mask-not-used.fixed-sample-mask.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sample-mask.fixed-sample-mask.frag b/third_party/spirv-cross/shaders-msl/frag/sample-mask.fixed-sample-mask.frag new file mode 100644 index 0000000..33ff0b2 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sample-mask.fixed-sample-mask.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); + gl_SampleMask[0] = 0; +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/sample-mask.frag b/third_party/spirv-cross/shaders-msl/frag/sample-mask.frag new file mode 100644 index 0000000..33ff0b2 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sample-mask.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(1.0); + gl_SampleMask[0] = 0; +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/sample-position-func.frag b/third_party/spirv-cross/shaders-msl/frag/sample-position-func.frag new file mode 100644 index 0000000..d34b896 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sample-position-func.frag @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in flat int index; + +layout(location = 0) out vec4 FragColor; + +vec4 getColor(int i) +{ + return vec4(gl_SamplePosition, i, 1.0); +} + +void main() +{ + FragColor = getColor(index); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sample-position.frag b/third_party/spirv-cross/shaders-msl/frag/sample-position.frag new file mode 100644 index 0000000..6f75028 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sample-position.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(gl_SamplePosition, gl_SampleID, 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag b/third_party/spirv-cross/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag new file mode 100644 index 0000000..f4526f3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler-1d-lod.1d-as-2d.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in float vTex; +layout(binding = 0) uniform sampler1D uSampler; + +void main() +{ + FragColor += texture(uSampler, vTex, 2.0) + + textureLod(uSampler, vTex, 3.0) + + textureGrad(uSampler, vTex, 5.0, 8.0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler-1d-lod.frag b/third_party/spirv-cross/shaders-msl/frag/sampler-1d-lod.frag new file mode 100644 index 0000000..f4526f3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler-1d-lod.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in float vTex; +layout(binding = 0) uniform sampler1D uSampler; + +void main() +{ + FragColor += texture(uSampler, vTex, 2.0) + + textureLod(uSampler, vTex, 3.0) + + textureGrad(uSampler, vTex, 5.0, 8.0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag b/third_party/spirv-cross/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag new file mode 100644 index 0000000..158c760 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler-compare-bias.msl23.1d-as-2d.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform texture1DArray uTex; +layout(binding = 1) uniform samplerShadow uShadow; +layout(location = 0) in vec3 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = texture(sampler1DArrayShadow(uTex, uShadow), vUV, 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.frag b/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.frag new file mode 100644 index 0000000..9fd9e3c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform texture2DArray uTex; +layout(binding = 1) uniform samplerShadow uShadow; +layout(location = 0) in vec4 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = textureGrad(sampler2DArrayShadow(uTex, uShadow), vUV, vec2(0.0), vec2(0.0)); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag b/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag new file mode 100644 index 0000000..9fd9e3c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.ios.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform texture2DArray uTex; +layout(binding = 1) uniform samplerShadow uShadow; +layout(location = 0) in vec4 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = textureGrad(sampler2DArrayShadow(uTex, uShadow), vUV, vec2(0.0), vec2(0.0)); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag b/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag new file mode 100644 index 0000000..5c1c893 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler-compare-cascade-gradient.msl23.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(binding = 0) uniform texture2DArray uTex; +layout(binding = 1) uniform samplerShadow uShadow; +layout(location = 0) in vec4 vUV; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = textureGrad(sampler2DArrayShadow(uTex, uShadow), vUV, vec2(0.0), vec2(0.0)) + textureGrad(sampler2DArrayShadow(uTex, uShadow), vUV, vec2(1.0), vec2(1.0)); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler-image-arrays.msl2.frag b/third_party/spirv-cross/shaders-msl/frag/sampler-image-arrays.msl2.frag new file mode 100644 index 0000000..42370d9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler-image-arrays.msl2.frag @@ -0,0 +1,33 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in vec2 vTex; +layout(location = 1) flat in int vIndex; +layout(binding = 0) uniform sampler2D uSampler[4]; +layout(binding = 4) uniform sampler uSamplers[4]; +layout(binding = 8) uniform texture2D uTextures[4]; + +vec4 sample_from_argument(sampler2D samplers[4]) +{ + return texture(samplers[vIndex], vTex + 0.2); +} + +vec4 sample_single_from_argument(sampler2D samp) +{ + return texture(samp, vTex + 0.3); +} + +vec4 sample_from_global() +{ + return texture(uSampler[vIndex], vTex + 0.1); +} + +void main() +{ + FragColor = vec4(0.0); + FragColor += texture(sampler2D(uTextures[2], uSamplers[1]), vTex); + FragColor += texture(uSampler[vIndex], vTex); + FragColor += sample_from_global(); + FragColor += sample_from_argument(uSampler); + FragColor += sample_single_from_argument(uSampler[3]); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler-ms.frag b/third_party/spirv-cross/shaders-msl/frag/sampler-ms.frag new file mode 100644 index 0000000..6593928 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler-ms.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2DMS uSampler; +layout(location = 0) out vec4 FragColor; + +void main() +{ + ivec2 coord = ivec2(gl_FragCoord.xy); + FragColor = + texelFetch(uSampler, coord, 0) + + texelFetch(uSampler, coord, 1) + + texelFetch(uSampler, coord, 2) + + texelFetch(uSampler, coord, 3); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/sampler.frag b/third_party/spirv-cross/shaders-msl/frag/sampler.frag new file mode 100644 index 0000000..e38f768 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/sampler.frag @@ -0,0 +1,18 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +vec4 sample_texture(sampler2D tex, vec2 uv) +{ + return texture(tex, uv); +} + +void main() +{ + FragColor = vColor * sample_texture(uTex, vTex); +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/shaders-msl/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..486ed90 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/scalar-refract-reflect.frag @@ -0,0 +1,11 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vRefract; + +void main() +{ + FragColor = refract(vRefract.x, vRefract.y, vRefract.z); + FragColor += reflect(vRefract.x, vRefract.y); + FragColor += refract(vRefract.xy, vRefract.yz, vRefract.z).y; + FragColor += reflect(vRefract.xy, vRefract.zy).y; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/separate-image-sampler-argument.frag b/third_party/spirv-cross/shaders-msl/frag/separate-image-sampler-argument.frag new file mode 100644 index 0000000..0475b01 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/separate-image-sampler-argument.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 1) uniform mediump texture2D uDepth; +layout(location = 0) out vec4 FragColor; + +vec4 samp(texture2D t, mediump sampler s) +{ + return texture(sampler2D(t, s), vec2(0.5)); +} + +void main() +{ + FragColor = samp(uDepth, uSampler); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/shader-arithmetic-8bit.frag b/third_party/spirv-cross/shaders-msl/frag/shader-arithmetic-8bit.frag new file mode 100644 index 0000000..9416f5b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/shader-arithmetic-8bit.frag @@ -0,0 +1,88 @@ +#version 450 +#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require +#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require + +layout(location = 0) flat in ivec4 vColor; +layout(location = 0) out ivec4 FragColorInt; +layout(location = 1) out uvec4 FragColorUint; + +layout(push_constant, std140) uniform Push +{ + int8_t i8; + uint8_t u8; +} registers; + +layout(binding = 1, std140) uniform UBO +{ + int8_t i8; + uint8_t u8; +} ubo; + +layout(binding = 2, std430) buffer SSBO +{ + int8_t i8[16]; + uint8_t u8[16]; +} ssbo; + +void packing_int8() +{ + int16_t i16 = 10s; + int i32 = 20; + + i8vec2 i8_2 = unpack8(i16); + i8vec4 i8_4 = unpack8(i32); + i16 = pack16(i8_2); + i32 = pack32(i8_4); + ssbo.i8[0] = i8_4.x; + ssbo.i8[1] = i8_4.y; + ssbo.i8[2] = i8_4.z; + ssbo.i8[3] = i8_4.w; +} + +void packing_uint8() +{ + uint16_t u16 = 10us; + uint u32 = 20u; + + u8vec2 u8_2 = unpack8(u16); + u8vec4 u8_4 = unpack8(u32); + u16 = pack16(u8_2); + u32 = pack32(u8_4); + + ssbo.u8[0] = u8_4.x; + ssbo.u8[1] = u8_4.y; + ssbo.u8[2] = u8_4.z; + ssbo.u8[3] = u8_4.w; +} + +void compute_int8() +{ + i8vec4 tmp = i8vec4(vColor); + tmp += registers.i8; + tmp += int8_t(-40); + tmp += i8vec4(-50); + tmp += i8vec4(10, 20, 30, 40); + tmp += ssbo.i8[4]; + tmp += ubo.i8; + FragColorInt = ivec4(tmp); +} + +void compute_uint8() +{ + u8vec4 tmp = u8vec4(vColor); + tmp += registers.u8; + tmp += uint8_t(-40); + tmp += u8vec4(-50); + tmp += u8vec4(10, 20, 30, 40); + tmp += ssbo.u8[4]; + tmp += ubo.u8; + FragColorUint = uvec4(tmp); +} + +void main() +{ + packing_int8(); + packing_uint8(); + compute_int8(); + compute_uint8(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/spec-constant-block-size.frag b/third_party/spirv-cross/shaders-msl/frag/spec-constant-block-size.frag new file mode 100644 index 0000000..8d2b1f3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/spec-constant-block-size.frag @@ -0,0 +1,17 @@ +#version 310 es +precision mediump float; + +layout(constant_id = 10) const int Value = 2; +layout(binding = 0) uniform SpecConstArray +{ + vec4 samples[Value]; +}; + +layout(location = 0) flat in int Index; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = samples[Index]; +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/spec-constant-ternary.frag b/third_party/spirv-cross/shaders-msl/frag/spec-constant-ternary.frag new file mode 100644 index 0000000..78dccbf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/spec-constant-ternary.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/stencil-export.msl21.frag b/third_party/spirv-cross/shaders-msl/frag/stencil-export.msl21.frag new file mode 100644 index 0000000..73b7e53 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/stencil-export.msl21.frag @@ -0,0 +1,17 @@ +#version 450 +#extension GL_ARB_shader_stencil_export : require + +layout(location = 0) out vec4 MRT0; +layout(location = 1) out vec4 MRT1; +void update_stencil() +{ + gl_FragStencilRefARB += 10; +} + +void main() +{ + MRT0 = vec4(1.0); + MRT1 = vec4(1.0, 0.0, 1.0, 1.0); + gl_FragStencilRefARB = 100; + update_stencil(); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/subgroup-builtins.msl22.frag b/third_party/spirv-cross/shaders-msl/frag/subgroup-builtins.msl22.frag new file mode 100644 index 0000000..746438f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/subgroup-builtins.msl22.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require + +layout(location = 0) out uvec2 FragColor; + +void main() +{ + FragColor.x = gl_SubgroupSize; + FragColor.y = gl_SubgroupInvocationID; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/switch-unsigned-case.frag b/third_party/spirv-cross/shaders-msl/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..d8aee43 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/switch-unsigned-case.frag @@ -0,0 +1,26 @@ +#version 310 es +precision mediump float; + +#define ENUM_0 0u +#define ENUM_1 1u + +layout(set = 0, binding = 0) uniform Buff +{ + uint TestVal; +}; + +layout(location = 0) out vec4 fsout_Color; + +void main() +{ + fsout_Color = vec4(1.0); + switch (TestVal) + { + case ENUM_0: + fsout_Color = vec4(0.1); + break; + case ENUM_1: + fsout_Color = vec4(0.2); + break; + } +} diff --git a/third_party/spirv-cross/shaders-msl/frag/swizzle.frag b/third_party/spirv-cross/shaders-msl/frag/swizzle.frag new file mode 100644 index 0000000..af22dd6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/swizzle.frag @@ -0,0 +1,17 @@ +#version 310 es +precision mediump float; + +layout(binding = 0) uniform sampler2D samp; +layout(location = 0) out vec4 FragColor; +layout(location = 1) in vec3 vNormal; +layout(location = 2) in vec2 vUV; + +void main() +{ + FragColor = vec4(texture(samp, vUV).xyz, 1.0); + FragColor = vec4(texture(samp, vUV).xz, 1.0, 4.0); + FragColor = vec4(texture(samp, vUV).xx, texture(samp, vUV + vec2(0.1)).yy); + FragColor = vec4(vNormal, 1.0); + FragColor = vec4(vNormal + 1.8, 1.0); + FragColor = vec4(vUV, vUV + 1.8); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag b/third_party/spirv-cross/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag new file mode 100644 index 0000000..8e15e36 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/texel-fetch-offset.1d-as-2d.frag @@ -0,0 +1,10 @@ +#version 450 +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uTexture; +layout(binding = 1) uniform sampler1D uTexture2; + +void main() +{ + FragColor = texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(1, 1)); + FragColor += texelFetchOffset(uTexture2, int(gl_FragCoord.x), 0, int(-1)); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/texel-fetch-offset.frag b/third_party/spirv-cross/shaders-msl/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..e98748b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/texel-fetch-offset.frag @@ -0,0 +1,10 @@ +#version 310 es +precision mediump float; +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uTexture; + +void main() +{ + FragColor = texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(1, 1)); + FragColor += texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(-1, 1)); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/texture-cube-array.frag b/third_party/spirv-cross/shaders-msl/frag/texture-cube-array.frag new file mode 100644 index 0000000..91a55f9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/texture-cube-array.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(set = 0, binding = 0) uniform samplerCube cubeSampler; +layout(set = 0, binding = 1) uniform samplerCubeArray cubeArraySampler; +layout(set = 0, binding = 2) uniform sampler2DArray texArraySampler; + +layout(location = 0) in vec4 vUV; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 a = texture(cubeSampler, vUV.xyz); + vec4 b = texture(cubeArraySampler, vUV); + vec4 c = texture(texArraySampler, vUV.xyz); + FragColor = a + b + c; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag b/third_party/spirv-cross/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag new file mode 100644 index 0000000..91a55f9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/texture-cube-array.ios.emulate-cube-array.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(set = 0, binding = 0) uniform samplerCube cubeSampler; +layout(set = 0, binding = 1) uniform samplerCubeArray cubeArraySampler; +layout(set = 0, binding = 2) uniform sampler2DArray texArraySampler; + +layout(location = 0) in vec4 vUV; +layout(location = 0) out vec4 FragColor; + +void main() +{ + vec4 a = texture(cubeSampler, vUV.xyz); + vec4 b = texture(cubeArraySampler, vUV); + vec4 c = texture(texArraySampler, vUV.xyz); + FragColor = a + b + c; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/texture-multisample-array.msl21.frag b/third_party/spirv-cross/shaders-msl/frag/texture-multisample-array.msl21.frag new file mode 100644 index 0000000..ede809b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/texture-multisample-array.msl21.frag @@ -0,0 +1,10 @@ +#version 450 +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2DMSArray uTexture; +layout(location = 0) flat in ivec3 vCoord; +layout(location = 1) flat in int vSample; + +void main() +{ + FragColor = texelFetch(uTexture, vCoord, vSample); +} diff --git a/third_party/spirv-cross/shaders-msl/frag/texture-proj-shadow.frag b/third_party/spirv-cross/shaders-msl/frag/texture-proj-shadow.frag new file mode 100644 index 0000000..547532e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/texture-proj-shadow.frag @@ -0,0 +1,19 @@ +#version 450 + +layout(binding = 1) uniform sampler2DShadow uShadow2D; +layout(binding = 2) uniform sampler1D uSampler1D; +layout(binding = 3) uniform sampler2D uSampler2D; +layout(binding = 4) uniform sampler3D uSampler3D; + +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vClip3; +layout(location = 1) in vec4 vClip4; +layout(location = 2) in vec2 vClip2; + +void main() +{ + FragColor = textureProj(uShadow2D, vClip4); + FragColor = textureProj(uSampler1D, vClip2).x; + FragColor = textureProj(uSampler2D, vClip3).x; + FragColor = textureProj(uSampler3D, vClip4).x; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/ubo_layout.frag b/third_party/spirv-cross/shaders-msl/frag/ubo_layout.frag new file mode 100644 index 0000000..80f9f16 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/ubo_layout.frag @@ -0,0 +1,24 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; + +struct Str +{ + mat4 foo; +}; + +layout(binding = 0, std140) uniform UBO1 +{ + layout(row_major) Str foo; +} ubo1; + +layout(binding = 1, std140) uniform UBO2 +{ + layout(column_major) Str foo; +} ubo0; + +void main() +{ + FragColor = ubo1.foo.foo[0] + ubo0.foo.foo[0]; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/unary-enclose.frag b/third_party/spirv-cross/shaders-msl/frag/unary-enclose.frag new file mode 100644 index 0000000..ea502e1 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/unary-enclose.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vIn; +layout(location = 1) flat in ivec4 vIn1; + +void main() +{ + FragColor = +(-(-vIn)); + ivec4 a = ~(~vIn1); + + bool b = false; + b = !!b; +} diff --git a/third_party/spirv-cross/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag b/third_party/spirv-cross/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag new file mode 100644 index 0000000..900c5b0 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/vecsize-mismatch.shader-inputs.frag @@ -0,0 +1,17 @@ +#version 450 + +#extension GL_AMD_gpu_shader_int16 : require + +layout(location = 0) flat in int16_t a; +layout(location = 1) flat in ivec2 b; +layout(location = 2) flat in uint16_t c[2]; +layout(location = 4) flat in uvec4 e[2]; +layout(location = 6) in vec2 d; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(float(int(a)), float(b.x), vec2(uint(c[1]), float(e[0].w)) + d); +} + diff --git a/third_party/spirv-cross/shaders-msl/frag/write-depth-in-function.frag b/third_party/spirv-cross/shaders-msl/frag/write-depth-in-function.frag new file mode 100644 index 0000000..1af7f5d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/frag/write-depth-in-function.frag @@ -0,0 +1,14 @@ +#version 450 +layout(location = 0) in float v; +layout(location = 0) out float FragColor; + +void set_output_depth() +{ + gl_FragDepth = 0.2; +} + +void main() +{ + FragColor = 1.0; + set_output_depth(); +} diff --git a/third_party/spirv-cross/shaders-msl/intel/shader-integer-functions2.asm.comp b/third_party/spirv-cross/shaders-msl/intel/shader-integer-functions2.asm.comp new file mode 100644 index 0000000..9189794 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/intel/shader-integer-functions2.asm.comp @@ -0,0 +1,137 @@ +; SPIR-V +; Version: 1.4 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 97 +; Schema: 0 + OpCapability Shader + OpCapability IntegerFunctions2INTEL + OpExtension "SPV_INTEL_shader_integer_functions2" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %foo "foo" + OpMemberName %foo 0 "a" + OpMemberName %foo 1 "b" + OpMemberName %foo 2 "c" + OpMemberName %foo 3 "d" + OpName %_ "" + OpMemberDecorate %foo 0 Offset 0 + OpMemberDecorate %foo 1 Offset 4 + OpMemberDecorate %foo 2 Offset 8 + OpMemberDecorate %foo 3 Offset 12 + OpDecorate %foo Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %foo = OpTypeStruct %uint %uint %int %int +%_ptr_StorageBuffer_foo = OpTypePointer StorageBuffer %foo + %_ = OpVariable %_ptr_StorageBuffer_foo StorageBuffer + %int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int + %int_3 = OpConstant %int 3 + %main = OpFunction %void None %6 + %15 = OpLabel + %16 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %17 = OpLoad %uint %16 + %18 = OpUCountLeadingZerosINTEL %uint %17 + %19 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %19 %18 + %20 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %21 = OpLoad %uint %20 + %22 = OpUCountTrailingZerosINTEL %uint %21 + %23 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %23 %22 + %24 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + %25 = OpLoad %int %24 + %26 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_3 + %27 = OpLoad %int %26 + %28 = OpAbsISubINTEL %uint %25 %27 + %29 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %29 %28 + %30 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %31 = OpLoad %uint %30 + %32 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_1 + %33 = OpLoad %uint %32 + %34 = OpAbsUSubINTEL %uint %31 %33 + %35 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %35 %34 + %37 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + %38 = OpLoad %int %37 + %39 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_3 + %40 = OpLoad %int %39 + %41 = OpIAddSatINTEL %int %38 %40 + %42 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + OpStore %42 %41 + %43 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %44 = OpLoad %uint %43 + %45 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_1 + %46 = OpLoad %uint %45 + %47 = OpUAddSatINTEL %uint %44 %46 + %48 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %48 %47 + %49 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + %50 = OpLoad %int %49 + %51 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_3 + %52 = OpLoad %int %51 + %53 = OpIAverageINTEL %int %50 %52 + %54 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + OpStore %54 %53 + %55 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %56 = OpLoad %uint %55 + %57 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_1 + %58 = OpLoad %uint %57 + %59 = OpUAverageINTEL %uint %56 %58 + %60 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %60 %59 + %61 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + %62 = OpLoad %int %61 + %63 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_3 + %64 = OpLoad %int %63 + %65 = OpIAverageRoundedINTEL %int %62 %64 + %66 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + OpStore %66 %65 + %67 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %68 = OpLoad %uint %67 + %69 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_1 + %70 = OpLoad %uint %69 + %71 = OpUAverageRoundedINTEL %uint %68 %70 + %72 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %72 %71 + %73 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + %74 = OpLoad %int %73 + %75 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_3 + %76 = OpLoad %int %75 + %77 = OpISubSatINTEL %int %74 %76 + %78 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + OpStore %78 %77 + %79 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %80 = OpLoad %uint %79 + %81 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_1 + %82 = OpLoad %uint %81 + %83 = OpUSubSatINTEL %uint %80 %82 + %84 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %84 %83 + %85 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + %86 = OpLoad %int %85 + %87 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_3 + %88 = OpLoad %int %87 + %89 = OpIMul32x16INTEL %int %86 %88 + %90 = OpAccessChain %_ptr_StorageBuffer_int %_ %int_2 + OpStore %90 %89 + %91 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + %92 = OpLoad %uint %91 + %93 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_1 + %94 = OpLoad %uint %93 + %95 = OpUMul32x16INTEL %uint %92 %94 + %96 = OpAccessChain %_ptr_StorageBuffer_uint %_ %int_0 + OpStore %96 %95 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/legacy/vert/transpose.legacy.vert b/third_party/spirv-cross/shaders-msl/legacy/vert/transpose.legacy.vert new file mode 100644 index 0000000..84f6182 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/legacy/vert/transpose.legacy.vert @@ -0,0 +1,20 @@ +#version 310 es + +uniform Buffer +{ + layout(row_major) mat4 MVPRowMajor; + layout(column_major) mat4 MVPColMajor; + mat4 M; +}; + +layout(location = 0) in vec4 Position; + +void main() +{ + vec4 c0 = M * (MVPRowMajor * Position); + vec4 c1 = M * (MVPColMajor * Position); + vec4 c2 = M * (Position * MVPRowMajor); + vec4 c3 = M * (Position * MVPColMajor); + gl_Position = c0 + c1 + c2 + c3; +} + diff --git a/third_party/spirv-cross/shaders-msl/tesc/basic.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/tesc/basic.multi-patch.tesc new file mode 100644 index 0000000..0a41f98 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/basic.multi-patch.tesc @@ -0,0 +1,17 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(location = 0) patch out vec3 vFoo; + +layout(vertices = 1) out; + +void main() +{ + gl_TessLevelInner[0] = 8.9; + gl_TessLevelInner[1] = 6.9; + gl_TessLevelOuter[0] = 8.9; + gl_TessLevelOuter[1] = 6.9; + gl_TessLevelOuter[2] = 3.9; + gl_TessLevelOuter[3] = 4.9; + vFoo = vec3(1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/basic.tesc b/third_party/spirv-cross/shaders-msl/tesc/basic.tesc new file mode 100644 index 0000000..0a41f98 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/basic.tesc @@ -0,0 +1,17 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(location = 0) patch out vec3 vFoo; + +layout(vertices = 1) out; + +void main() +{ + gl_TessLevelInner[0] = 8.9; + gl_TessLevelInner[1] = 6.9; + gl_TessLevelOuter[0] = 8.9; + gl_TessLevelOuter[1] = 6.9; + gl_TessLevelOuter[2] = 3.9; + gl_TessLevelOuter[3] = 4.9; + vFoo = vec3(1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc new file mode 100644 index 0000000..36b1668 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-matrix.multi-patch.tesc @@ -0,0 +1,12 @@ +#version 450 + +layout(vertices = 4) out; + +layout(location = 0) in mat4 vInputs[gl_MaxPatchVertices]; +layout(location = 0) out mat4 vOutputs[4]; + +void main() +{ + mat4 tmp[gl_MaxPatchVertices] = vInputs; + vOutputs[gl_InvocationID] = tmp[gl_InvocationID]; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-matrix.tesc b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-matrix.tesc new file mode 100644 index 0000000..36b1668 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-matrix.tesc @@ -0,0 +1,12 @@ +#version 450 + +layout(vertices = 4) out; + +layout(location = 0) in mat4 vInputs[gl_MaxPatchVertices]; +layout(location = 0) out mat4 vOutputs[4]; + +void main() +{ + mat4 tmp[gl_MaxPatchVertices] = vInputs; + vOutputs[gl_InvocationID] = tmp[gl_InvocationID]; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc new file mode 100644 index 0000000..4b4d5bf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-struct.multi-patch.tesc @@ -0,0 +1,21 @@ +#version 450 + +layout(vertices = 4) out; + +struct VertexData +{ + mat4 a; + vec4 b[2]; + vec4 c; +}; + +layout(location = 0) in VertexData vInputs[gl_MaxPatchVertices]; +layout(location = 0) out vec4 vOutputs[4]; + +void main() +{ + VertexData tmp[gl_MaxPatchVertices] = vInputs; + VertexData tmp_single = vInputs[gl_InvocationID ^ 1]; + + vOutputs[gl_InvocationID] = tmp[gl_InvocationID].a[1] + tmp[gl_InvocationID].b[1] + tmp[gl_InvocationID].c + tmp_single.c; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-struct.tesc b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-struct.tesc new file mode 100644 index 0000000..4b4d5bf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array-of-struct.tesc @@ -0,0 +1,21 @@ +#version 450 + +layout(vertices = 4) out; + +struct VertexData +{ + mat4 a; + vec4 b[2]; + vec4 c; +}; + +layout(location = 0) in VertexData vInputs[gl_MaxPatchVertices]; +layout(location = 0) out vec4 vOutputs[4]; + +void main() +{ + VertexData tmp[gl_MaxPatchVertices] = vInputs; + VertexData tmp_single = vInputs[gl_InvocationID ^ 1]; + + vOutputs[gl_InvocationID] = tmp[gl_InvocationID].a[1] + tmp[gl_InvocationID].b[1] + tmp[gl_InvocationID].c + tmp_single.c; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array.multi-patch.tesc new file mode 100644 index 0000000..1a5924b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array.multi-patch.tesc @@ -0,0 +1,12 @@ +#version 450 + +layout(vertices = 4) out; + +layout(location = 0) in vec4 vInputs[gl_MaxPatchVertices]; +layout(location = 0) out vec4 vOutputs[4]; + +void main() +{ + vec4 tmp[gl_MaxPatchVertices] = vInputs; + vOutputs[gl_InvocationID] = tmp[gl_InvocationID]; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array.tesc b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array.tesc new file mode 100644 index 0000000..1a5924b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/load-control-point-array.tesc @@ -0,0 +1,12 @@ +#version 450 + +layout(vertices = 4) out; + +layout(location = 0) in vec4 vInputs[gl_MaxPatchVertices]; +layout(location = 0) out vec4 vOutputs[4]; + +void main() +{ + vec4 tmp[gl_MaxPatchVertices] = vInputs; + vOutputs[gl_InvocationID] = tmp[gl_InvocationID]; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/matrix-output.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/tesc/matrix-output.multi-patch.tesc new file mode 100644 index 0000000..0d23861 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/matrix-output.multi-patch.tesc @@ -0,0 +1,28 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(vertices = 3) out; + +layout(location = 0) in highp float in_tc_attr[]; +layout(location = 0) out highp float in_te_attr[]; + +layout(location = 1) out mediump mat4x3 in_te_data0[]; +layout(location = 5) out mediump mat4x3 in_te_data1[]; + +void main (void) +{ + mat4x3 d = mat4x3(gl_InvocationID); + in_te_data0[gl_InvocationID] = d; + barrier(); + in_te_data1[gl_InvocationID] = d + in_te_data0[(gl_InvocationID + 1) % 3]; + + in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID]; + + gl_TessLevelInner[0] = 1.0; + gl_TessLevelInner[1] = 1.0; + + gl_TessLevelOuter[0] = 1.0; + gl_TessLevelOuter[1] = 1.0; + gl_TessLevelOuter[2] = 1.0; + gl_TessLevelOuter[3] = 1.0; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/reload-tess-level.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/tesc/reload-tess-level.multi-patch.tesc new file mode 100644 index 0000000..c3f0195 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/reload-tess-level.multi-patch.tesc @@ -0,0 +1,17 @@ +#version 450 +layout(vertices = 4) out; + +void main() +{ + if (gl_InvocationID == 0) + { + gl_TessLevelOuter[0] = 2.0; + gl_TessLevelOuter[1] = 3.0; + gl_TessLevelOuter[2] = 4.0; + gl_TessLevelOuter[3] = 5.0; + gl_TessLevelInner[0] = mix(gl_TessLevelOuter[0], gl_TessLevelOuter[3], 0.5); + gl_TessLevelInner[1] = mix(gl_TessLevelOuter[2], gl_TessLevelOuter[1], 0.5); + } + + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/reload-tess-level.tesc b/third_party/spirv-cross/shaders-msl/tesc/reload-tess-level.tesc new file mode 100644 index 0000000..c3f0195 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/reload-tess-level.tesc @@ -0,0 +1,17 @@ +#version 450 +layout(vertices = 4) out; + +void main() +{ + if (gl_InvocationID == 0) + { + gl_TessLevelOuter[0] = 2.0; + gl_TessLevelOuter[1] = 3.0; + gl_TessLevelOuter[2] = 4.0; + gl_TessLevelOuter[3] = 5.0; + gl_TessLevelInner[0] = mix(gl_TessLevelOuter[0], gl_TessLevelOuter[3], 0.5); + gl_TessLevelInner[1] = mix(gl_TessLevelOuter[2], gl_TessLevelOuter[1], 0.5); + } + + gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/struct-output.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/tesc/struct-output.multi-patch.tesc new file mode 100644 index 0000000..a1511a4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/struct-output.multi-patch.tesc @@ -0,0 +1,36 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(vertices = 3) out; + +layout(location = 0) in highp float in_tc_attr[]; +layout(location = 0) out highp float in_te_attr[]; + +struct te_data +{ + mediump float a; + mediump float b; + mediump uint c; +}; + +layout(location = 1) out te_data in_te_data0[]; +layout(location = 4) out te_data in_te_data1[]; + +void main (void) +{ + te_data d = te_data(float(gl_InvocationID), float(gl_InvocationID + 1), uint(gl_InvocationID)); + in_te_data0[gl_InvocationID] = d; + barrier(); + te_data e = in_te_data0[(gl_InvocationID + 1) % 3]; + in_te_data1[gl_InvocationID] = te_data(d.a + e.a, d.b + e.b, d.c + e.c); + + in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID]; + + gl_TessLevelInner[0] = 1.0; + gl_TessLevelInner[1] = 1.0; + + gl_TessLevelOuter[0] = 1.0; + gl_TessLevelOuter[1] = 1.0; + gl_TessLevelOuter[2] = 1.0; + gl_TessLevelOuter[3] = 1.0; +} diff --git a/third_party/spirv-cross/shaders-msl/tesc/water_tess.multi-patch.tesc b/third_party/spirv-cross/shaders-msl/tesc/water_tess.multi-patch.tesc new file mode 100644 index 0000000..3ecdc3d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/water_tess.multi-patch.tesc @@ -0,0 +1,115 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(vertices = 1) out; +layout(location = 0) in vec2 vPatchPosBase[]; + +layout(std140) uniform UBO +{ + vec4 uScale; + highp vec3 uCamPos; + vec2 uPatchSize; + vec2 uMaxTessLevel; + float uDistanceMod; + vec4 uFrustum[6]; +}; + +layout(location = 1) patch out vec2 vOutPatchPosBase; +layout(location = 2) patch out vec4 vPatchLods; + +float lod_factor(vec2 pos_) +{ + vec2 pos = pos_ * uScale.xy; + vec3 dist_to_cam = uCamPos - vec3(pos.x, 0.0, pos.y); + float level = log2((length(dist_to_cam) + 0.0001) * uDistanceMod); + return clamp(level, 0.0, uMaxTessLevel.x); +} + +float tess_level(float lod) +{ + return uMaxTessLevel.y * exp2(-lod); +} + +vec4 tess_level(vec4 lod) +{ + return uMaxTessLevel.y * exp2(-lod); +} + +// Guard band for vertex displacement. +#define GUARD_BAND 10.0 +bool frustum_cull(vec2 p0) +{ + vec2 min_xz = (p0 - GUARD_BAND) * uScale.xy; + vec2 max_xz = (p0 + uPatchSize + GUARD_BAND) * uScale.xy; + + vec3 bb_min = vec3(min_xz.x, -GUARD_BAND, min_xz.y); + vec3 bb_max = vec3(max_xz.x, +GUARD_BAND, max_xz.y); + vec3 center = 0.5 * (bb_min + bb_max); + float radius = 0.5 * length(bb_max - bb_min); + + vec3 f0 = vec3( + dot(uFrustum[0], vec4(center, 1.0)), + dot(uFrustum[1], vec4(center, 1.0)), + dot(uFrustum[2], vec4(center, 1.0))); + + vec3 f1 = vec3( + dot(uFrustum[3], vec4(center, 1.0)), + dot(uFrustum[4], vec4(center, 1.0)), + dot(uFrustum[5], vec4(center, 1.0))); + + return !(any(lessThanEqual(f0, vec3(-radius))) || any(lessThanEqual(f1, vec3(-radius)))); +} + +void compute_tess_levels(vec2 p0) +{ + vOutPatchPosBase = p0; + + float l00 = lod_factor(p0 + vec2(-0.5, -0.5) * uPatchSize); + float l10 = lod_factor(p0 + vec2(+0.5, -0.5) * uPatchSize); + float l20 = lod_factor(p0 + vec2(+1.5, -0.5) * uPatchSize); + float l01 = lod_factor(p0 + vec2(-0.5, +0.5) * uPatchSize); + float l11 = lod_factor(p0 + vec2(+0.5, +0.5) * uPatchSize); + float l21 = lod_factor(p0 + vec2(+1.5, +0.5) * uPatchSize); + float l02 = lod_factor(p0 + vec2(-0.5, +1.5) * uPatchSize); + float l12 = lod_factor(p0 + vec2(+0.5, +1.5) * uPatchSize); + float l22 = lod_factor(p0 + vec2(+1.5, +1.5) * uPatchSize); + + vec4 lods = vec4( + dot(vec4(l01, l11, l02, l12), vec4(0.25)), + dot(vec4(l00, l10, l01, l11), vec4(0.25)), + dot(vec4(l10, l20, l11, l21), vec4(0.25)), + dot(vec4(l11, l21, l12, l22), vec4(0.25))); + + vPatchLods = lods; + + vec4 outer_lods = min(lods.xyzw, lods.yzwx); + vec4 levels = tess_level(outer_lods); + gl_TessLevelOuter[0] = levels.x; + gl_TessLevelOuter[1] = levels.y; + gl_TessLevelOuter[2] = levels.z; + gl_TessLevelOuter[3] = levels.w; + + float min_lod = min(min(lods.x, lods.y), min(lods.z, lods.w)); + float inner = tess_level(min(min_lod, l11)); + gl_TessLevelInner[0] = inner; + gl_TessLevelInner[1] = inner; +} + +void main() +{ + vec2 p0 = vPatchPosBase[0]; + if (!frustum_cull(p0)) + { + gl_TessLevelOuter[0] = -1.0; + gl_TessLevelOuter[1] = -1.0; + gl_TessLevelOuter[2] = -1.0; + gl_TessLevelOuter[3] = -1.0; + gl_TessLevelInner[0] = -1.0; + gl_TessLevelInner[1] = -1.0; + } + else + { + compute_tess_levels(p0); + } +} + diff --git a/third_party/spirv-cross/shaders-msl/tesc/water_tess.tesc b/third_party/spirv-cross/shaders-msl/tesc/water_tess.tesc new file mode 100644 index 0000000..3ecdc3d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tesc/water_tess.tesc @@ -0,0 +1,115 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(vertices = 1) out; +layout(location = 0) in vec2 vPatchPosBase[]; + +layout(std140) uniform UBO +{ + vec4 uScale; + highp vec3 uCamPos; + vec2 uPatchSize; + vec2 uMaxTessLevel; + float uDistanceMod; + vec4 uFrustum[6]; +}; + +layout(location = 1) patch out vec2 vOutPatchPosBase; +layout(location = 2) patch out vec4 vPatchLods; + +float lod_factor(vec2 pos_) +{ + vec2 pos = pos_ * uScale.xy; + vec3 dist_to_cam = uCamPos - vec3(pos.x, 0.0, pos.y); + float level = log2((length(dist_to_cam) + 0.0001) * uDistanceMod); + return clamp(level, 0.0, uMaxTessLevel.x); +} + +float tess_level(float lod) +{ + return uMaxTessLevel.y * exp2(-lod); +} + +vec4 tess_level(vec4 lod) +{ + return uMaxTessLevel.y * exp2(-lod); +} + +// Guard band for vertex displacement. +#define GUARD_BAND 10.0 +bool frustum_cull(vec2 p0) +{ + vec2 min_xz = (p0 - GUARD_BAND) * uScale.xy; + vec2 max_xz = (p0 + uPatchSize + GUARD_BAND) * uScale.xy; + + vec3 bb_min = vec3(min_xz.x, -GUARD_BAND, min_xz.y); + vec3 bb_max = vec3(max_xz.x, +GUARD_BAND, max_xz.y); + vec3 center = 0.5 * (bb_min + bb_max); + float radius = 0.5 * length(bb_max - bb_min); + + vec3 f0 = vec3( + dot(uFrustum[0], vec4(center, 1.0)), + dot(uFrustum[1], vec4(center, 1.0)), + dot(uFrustum[2], vec4(center, 1.0))); + + vec3 f1 = vec3( + dot(uFrustum[3], vec4(center, 1.0)), + dot(uFrustum[4], vec4(center, 1.0)), + dot(uFrustum[5], vec4(center, 1.0))); + + return !(any(lessThanEqual(f0, vec3(-radius))) || any(lessThanEqual(f1, vec3(-radius)))); +} + +void compute_tess_levels(vec2 p0) +{ + vOutPatchPosBase = p0; + + float l00 = lod_factor(p0 + vec2(-0.5, -0.5) * uPatchSize); + float l10 = lod_factor(p0 + vec2(+0.5, -0.5) * uPatchSize); + float l20 = lod_factor(p0 + vec2(+1.5, -0.5) * uPatchSize); + float l01 = lod_factor(p0 + vec2(-0.5, +0.5) * uPatchSize); + float l11 = lod_factor(p0 + vec2(+0.5, +0.5) * uPatchSize); + float l21 = lod_factor(p0 + vec2(+1.5, +0.5) * uPatchSize); + float l02 = lod_factor(p0 + vec2(-0.5, +1.5) * uPatchSize); + float l12 = lod_factor(p0 + vec2(+0.5, +1.5) * uPatchSize); + float l22 = lod_factor(p0 + vec2(+1.5, +1.5) * uPatchSize); + + vec4 lods = vec4( + dot(vec4(l01, l11, l02, l12), vec4(0.25)), + dot(vec4(l00, l10, l01, l11), vec4(0.25)), + dot(vec4(l10, l20, l11, l21), vec4(0.25)), + dot(vec4(l11, l21, l12, l22), vec4(0.25))); + + vPatchLods = lods; + + vec4 outer_lods = min(lods.xyzw, lods.yzwx); + vec4 levels = tess_level(outer_lods); + gl_TessLevelOuter[0] = levels.x; + gl_TessLevelOuter[1] = levels.y; + gl_TessLevelOuter[2] = levels.z; + gl_TessLevelOuter[3] = levels.w; + + float min_lod = min(min(lods.x, lods.y), min(lods.z, lods.w)); + float inner = tess_level(min(min_lod, l11)); + gl_TessLevelInner[0] = inner; + gl_TessLevelInner[1] = inner; +} + +void main() +{ + vec2 p0 = vPatchPosBase[0]; + if (!frustum_cull(p0)) + { + gl_TessLevelOuter[0] = -1.0; + gl_TessLevelOuter[1] = -1.0; + gl_TessLevelOuter[2] = -1.0; + gl_TessLevelOuter[3] = -1.0; + gl_TessLevelInner[0] = -1.0; + gl_TessLevelInner[1] = -1.0; + } + else + { + compute_tess_levels(p0); + } +} + diff --git a/third_party/spirv-cross/shaders-msl/tese/input-array.tese b/third_party/spirv-cross/shaders-msl/tese/input-array.tese new file mode 100644 index 0000000..8efa480 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/input-array.tese @@ -0,0 +1,15 @@ +#version 450 + +layout(ccw, quads, fractional_odd_spacing) in; +layout(location = 0) in vec4 Floats[]; +layout(location = 2) in vec4 Floats2[gl_MaxPatchVertices]; + +void set_position() +{ + gl_Position = Floats[0] * gl_TessCoord.x + Floats2[1] * gl_TessCoord.y; +} + +void main() +{ + set_position(); +} diff --git a/third_party/spirv-cross/shaders-msl/tese/input-types.tese b/third_party/spirv-cross/shaders-msl/tese/input-types.tese new file mode 100644 index 0000000..3157953 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/input-types.tese @@ -0,0 +1,75 @@ +#version 450 + +layout(ccw, quads, fractional_even_spacing) in; + +// Try to use the whole taxonomy of input methods. + +// Per-vertex vector. +layout(location = 0) in vec4 vColor[]; +// Per-patch vector. +layout(location = 1) patch in vec4 vColors; +// Per-patch vector array. +layout(location = 2) patch in vec4 vColorsArray[2]; + +// I/O blocks, per patch and per control point. +layout(location = 4) in Block +{ + vec4 a; + vec4 b; +} blocks[]; + +layout(location = 6) patch in PatchBlock +{ + vec4 a; + vec4 b; +} patch_block; + +// Composites. +struct Foo +{ + vec4 a; + vec4 b; +}; +layout(location = 8) patch in Foo vFoo; +//layout(location = 10) patch in Foo vFooArray[2]; // FIXME: Handling of array-of-struct input is broken! + +// Per-control point struct. +layout(location = 14) in Foo vFoos[]; + +void set_from_function() +{ + gl_Position = blocks[0].a; + gl_Position += blocks[0].b; + gl_Position += blocks[1].a; + gl_Position += blocks[1].b; + gl_Position += patch_block.a; + gl_Position += patch_block.b; + gl_Position += vColor[0]; + gl_Position += vColor[1]; + gl_Position += vColors; + + Foo foo = vFoo; + gl_Position += foo.a; + gl_Position += foo.b; + + /*foo = vFooArray[0]; + gl_Position += foo.a; + gl_Position += foo.b; + + foo = vFooArray[1]; + gl_Position += foo.a; + gl_Position += foo.b;*/ + + foo = vFoos[0]; + gl_Position += foo.a; + gl_Position += foo.b; + + foo = vFoos[1]; + gl_Position += foo.a; + gl_Position += foo.b; +} + +void main() +{ + set_from_function(); +} diff --git a/third_party/spirv-cross/shaders-msl/tese/load-control-point-array-of-matrix.tese b/third_party/spirv-cross/shaders-msl/tese/load-control-point-array-of-matrix.tese new file mode 100644 index 0000000..479b3e6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/load-control-point-array-of-matrix.tese @@ -0,0 +1,13 @@ +#version 450 + +layout(cw, quads) in; +layout(location = 0) in mat4 vInputs[gl_MaxPatchVertices]; +layout(location = 4) patch in vec4 vBoo[4]; +layout(location = 8) patch in int vIndex; + +void main() +{ + mat4 tmp[gl_MaxPatchVertices] = vInputs; + gl_Position = tmp[0][vIndex] + tmp[1][vIndex] + vBoo[vIndex]; + +} diff --git a/third_party/spirv-cross/shaders-msl/tese/load-control-point-array.tese b/third_party/spirv-cross/shaders-msl/tese/load-control-point-array.tese new file mode 100644 index 0000000..4fa0bb1 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/load-control-point-array.tese @@ -0,0 +1,13 @@ +#version 450 + +layout(cw, quads) in; +layout(location = 0) in vec4 vInputs[gl_MaxPatchVertices]; +layout(location = 1) patch in vec4 vBoo[4]; +layout(location = 5) patch in int vIndex; + +void main() +{ + vec4 tmp[gl_MaxPatchVertices] = vInputs; + gl_Position = tmp[0] + tmp[1] + vBoo[vIndex]; + +} diff --git a/third_party/spirv-cross/shaders-msl/tese/quad.domain.tese b/third_party/spirv-cross/shaders-msl/tese/quad.domain.tese new file mode 100644 index 0000000..83d4b0e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/quad.domain.tese @@ -0,0 +1,12 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, quads, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(gl_TessCoord.x * gl_TessLevelInner[0] * gl_TessLevelOuter[0] + (1.0 - gl_TessCoord.x) * gl_TessLevelInner[0] * gl_TessLevelOuter[2], + gl_TessCoord.y * gl_TessLevelInner[1] * gl_TessLevelOuter[3] + (1.0 - gl_TessCoord.y) * gl_TessLevelInner[1] * gl_TessLevelOuter[1], + 0, 1); +} + diff --git a/third_party/spirv-cross/shaders-msl/tese/quad.tese b/third_party/spirv-cross/shaders-msl/tese/quad.tese new file mode 100644 index 0000000..4fdb996 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/quad.tese @@ -0,0 +1,17 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, quads, fractional_even_spacing) in; + +void set_position() +{ + gl_Position = vec4(gl_TessCoord.x * gl_TessLevelInner[0] * gl_TessLevelOuter[0] + (1.0 - gl_TessCoord.x) * gl_TessLevelInner[0] * gl_TessLevelOuter[2], + gl_TessCoord.y * gl_TessLevelInner[1] * gl_TessLevelOuter[1] + (1.0 - gl_TessCoord.y) * gl_TessLevelInner[1] * gl_TessLevelOuter[3], + 0, 1); +} + +void main() +{ + set_position(); +} + diff --git a/third_party/spirv-cross/shaders-msl/tese/set-from-function.tese b/third_party/spirv-cross/shaders-msl/tese/set-from-function.tese new file mode 100644 index 0000000..6cbab9e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/set-from-function.tese @@ -0,0 +1,36 @@ +#version 450 + +layout(ccw, quads, fractional_even_spacing) in; + +layout(location = 0) in vec4 vColor[]; +layout(location = 1) patch in vec4 vColors; +layout(location = 2) in Block +{ + vec4 a; + vec4 b; +} blocks[]; + +struct Foo +{ + vec4 a; + vec4 b; +}; +layout(location = 4) patch in Foo vFoo; + +void set_from_function() +{ + gl_Position = blocks[0].a; + gl_Position += blocks[0].b; + gl_Position += blocks[1].a; + gl_Position += blocks[1].b; + gl_Position += vColor[0]; + gl_Position += vColor[1]; + gl_Position += vColors; + gl_Position += vFoo.a; + gl_Position += vFoo.b; +} + +void main() +{ + set_from_function(); +} diff --git a/third_party/spirv-cross/shaders-msl/tese/triangle-tess-level.tese b/third_party/spirv-cross/shaders-msl/tese/triangle-tess-level.tese new file mode 100644 index 0000000..5ea55af --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/triangle-tess-level.tese @@ -0,0 +1,13 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, triangles, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(gl_TessCoord.x * gl_TessLevelInner[0] * gl_TessLevelOuter[0], + gl_TessCoord.y * gl_TessLevelInner[0] * gl_TessLevelOuter[1], + gl_TessCoord.z * gl_TessLevelInner[0] * gl_TessLevelOuter[2], + 1); +} + diff --git a/third_party/spirv-cross/shaders-msl/tese/triangle.tese b/third_party/spirv-cross/shaders-msl/tese/triangle.tese new file mode 100644 index 0000000..6ce7c2d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/triangle.tese @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, triangles, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/shaders-msl/tese/water_tess.tese b/third_party/spirv-cross/shaders-msl/tese/water_tess.tese new file mode 100644 index 0000000..32d6bc9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/tese/water_tess.tese @@ -0,0 +1,65 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +precision highp int; + +layout(cw, quads, fractional_even_spacing) in; + +layout(location = 0) patch in vec2 vOutPatchPosBase; +layout(location = 1) patch in vec4 vPatchLods; + +layout(binding = 1, std140) uniform UBO +{ + mat4 uMVP; + vec4 uScale; + vec2 uInvScale; + vec3 uCamPos; + vec2 uPatchSize; + vec2 uInvHeightmapSize; +}; +layout(binding = 0) uniform mediump sampler2D uHeightmapDisplacement; + +layout(location = 0) highp out vec3 vWorld; +layout(location = 1) highp out vec4 vGradNormalTex; + +vec2 lerp_vertex(vec2 tess_coord) +{ + return vOutPatchPosBase + tess_coord * uPatchSize; +} + +mediump vec2 lod_factor(vec2 tess_coord) +{ + mediump vec2 x = mix(vPatchLods.yx, vPatchLods.zw, tess_coord.x); + mediump float level = mix(x.x, x.y, tess_coord.y); + mediump float floor_level = floor(level); + mediump float fract_level = level - floor_level; + return vec2(floor_level, fract_level); +} + +mediump vec3 sample_height_displacement(vec2 uv, vec2 off, mediump vec2 lod) +{ + return mix( + textureLod(uHeightmapDisplacement, uv + 0.5 * off, lod.x).xyz, + textureLod(uHeightmapDisplacement, uv + 1.0 * off, lod.x + 1.0).xyz, + lod.y); +} + +void main() +{ + vec2 tess_coord = gl_TessCoord.xy; + vec2 pos = lerp_vertex(tess_coord); + mediump vec2 lod = lod_factor(tess_coord); + + vec2 tex = pos * uInvHeightmapSize.xy; + pos *= uScale.xy; + + mediump float delta_mod = exp2(lod.x); + vec2 off = uInvHeightmapSize.xy * delta_mod; + + vGradNormalTex = vec4(tex + 0.5 * uInvHeightmapSize.xy, tex * uScale.zw); + vec3 height_displacement = sample_height_displacement(tex, off, lod); + + pos += height_displacement.yz; + vWorld = vec3(pos.x, height_displacement.x, pos.y); + gl_Position = uMVP * vec4(vWorld, 1.0); +} + diff --git a/third_party/spirv-cross/shaders-msl/vert/basic.capture.vert b/third_party/spirv-cross/shaders-msl/vert/basic.capture.vert new file mode 100644 index 0000000..8191dc2 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/basic.capture.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/basic.for-tess.vert b/third_party/spirv-cross/shaders-msl/vert/basic.for-tess.vert new file mode 100644 index 0000000..8191dc2 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/basic.for-tess.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/basic.vert b/third_party/spirv-cross/shaders-msl/vert/basic.vert new file mode 100644 index 0000000..8191dc2 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/basic.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/clip-distance-block.no-user-varying.vert b/third_party/spirv-cross/shaders-msl/vert/clip-distance-block.no-user-varying.vert new file mode 100644 index 0000000..93ed311 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/clip-distance-block.no-user-varying.vert @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in vec4 Position; +out gl_PerVertex +{ + vec4 gl_Position; + float gl_ClipDistance[2]; +}; + +void main() +{ + gl_Position = Position; + gl_ClipDistance[0] = Position.x; + gl_ClipDistance[1] = Position.y; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/clip-distance-block.vert b/third_party/spirv-cross/shaders-msl/vert/clip-distance-block.vert new file mode 100644 index 0000000..93ed311 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/clip-distance-block.vert @@ -0,0 +1,15 @@ +#version 450 + +layout(location = 0) in vec4 Position; +out gl_PerVertex +{ + vec4 gl_Position; + float gl_ClipDistance[2]; +}; + +void main() +{ + gl_Position = Position; + gl_ClipDistance[0] = Position.x; + gl_ClipDistance[1] = Position.y; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/copy.flatten.vert b/third_party/spirv-cross/shaders-msl/vert/copy.flatten.vert new file mode 100644 index 0000000..4f1b880 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/copy.flatten.vert @@ -0,0 +1,34 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + + vec4 Color; +}; + +layout(std140) uniform UBO +{ + mat4 uMVP; + + Light lights[4]; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec4 vColor; + +void main() +{ + gl_Position = uMVP * aVertex; + + vColor = vec4(0.0); + + for (int i = 0; i < 4; ++i) + { + Light light = lights[i]; + vec3 L = aVertex.xyz - light.Position; + vColor += dot(aNormal, normalize(L)) * (clamp(1.0 - length(L) / light.Radius, 0.0, 1.0) * lights[i].Color); + } +} diff --git a/third_party/spirv-cross/shaders-msl/vert/dynamic.flatten.vert b/third_party/spirv-cross/shaders-msl/vert/dynamic.flatten.vert new file mode 100644 index 0000000..a341d45 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/dynamic.flatten.vert @@ -0,0 +1,33 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + + vec4 Color; +}; + +layout(std140) uniform UBO +{ + mat4 uMVP; + + Light lights[4]; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec4 vColor; + +void main() +{ + gl_Position = uMVP * aVertex; + + vColor = vec4(0.0); + + for (int i = 0; i < 4; ++i) + { + vec3 L = aVertex.xyz - lights[i].Position; + vColor += dot(aNormal, normalize(L)) * (clamp(1.0 - length(L) / lights[i].Radius, 0.0, 1.0) * lights[i].Color); + } +} diff --git a/third_party/spirv-cross/shaders-msl/vert/float-math.invariant-float-math.vert b/third_party/spirv-cross/shaders-msl/vert/float-math.invariant-float-math.vert new file mode 100644 index 0000000..caa8639 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/float-math.invariant-float-math.vert @@ -0,0 +1,25 @@ +#version 450 + +layout(set = 0, binding = 0) uniform Matrices +{ + mat4 vpMatrix; + mat4 wMatrix; + mat4x3 wMatrix4x3; + mat3x4 wMatrix3x4; +}; + +layout(location = 0) in vec3 InPos; +layout(location = 1) in vec3 InNormal; + +layout(location = 0) out vec3 OutNormal; +layout(location = 1) out vec4 OutWorldPos[4]; + +void main() +{ + gl_Position = vpMatrix * wMatrix * vec4(InPos, 1); + OutWorldPos[0] = wMatrix * vec4(InPos, 1); + OutWorldPos[1] = vec4(InPos, 1) * wMatrix; + OutWorldPos[2] = wMatrix3x4 * InPos; + OutWorldPos[3] = InPos * wMatrix4x3; + OutNormal = (wMatrix * vec4(InNormal, 0)).xyz; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/float-math.vert b/third_party/spirv-cross/shaders-msl/vert/float-math.vert new file mode 100644 index 0000000..caa8639 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/float-math.vert @@ -0,0 +1,25 @@ +#version 450 + +layout(set = 0, binding = 0) uniform Matrices +{ + mat4 vpMatrix; + mat4 wMatrix; + mat4x3 wMatrix4x3; + mat3x4 wMatrix3x4; +}; + +layout(location = 0) in vec3 InPos; +layout(location = 1) in vec3 InNormal; + +layout(location = 0) out vec3 OutNormal; +layout(location = 1) out vec4 OutWorldPos[4]; + +void main() +{ + gl_Position = vpMatrix * wMatrix * vec4(InPos, 1); + OutWorldPos[0] = wMatrix * vec4(InPos, 1); + OutWorldPos[1] = vec4(InPos, 1) * wMatrix; + OutWorldPos[2] = wMatrix3x4 * InPos; + OutWorldPos[3] = InPos * wMatrix4x3; + OutNormal = (wMatrix * vec4(InNormal, 0)).xyz; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/functions.vert b/third_party/spirv-cross/shaders-msl/vert/functions.vert new file mode 100644 index 0000000..b92074f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/functions.vert @@ -0,0 +1,28 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; + uniform vec3 rotDeg; + uniform vec3 rotRad; + uniform ivec2 bits; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; + +layout(location = 0) out vec3 vNormal; +layout(location = 1) out vec3 vRotDeg; +layout(location = 2) out vec3 vRotRad; +layout(location = 3) out ivec2 vLSB; +layout(location = 4) out ivec2 vMSB; + +void main() +{ + gl_Position = inverse(uMVP) * aVertex; + vNormal = aNormal; + vRotDeg = degrees(rotRad); + vRotRad = radians(rotDeg); + vLSB = findLSB(bits); + vMSB = findMSB(bits); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/in_out_array_mat.vert b/third_party/spirv-cross/shaders-msl/vert/in_out_array_mat.vert new file mode 100644 index 0000000..bdff3d2 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/in_out_array_mat.vert @@ -0,0 +1,41 @@ +#version 450 + +layout(binding = 0, std140) uniform UBO +{ + mat4 projection; + mat4 model; + float lodBias; +} ubo; + +layout(location = 0) in vec3 inPos; +layout(location = 1) in vec4 colors[3]; +layout(location = 4) in vec3 inNormal; +layout(location = 5) in mat4 inViewMat; +layout(location = 0) out vec3 outPos; +layout(location = 1) out vec3 outNormal; +layout(location = 2) out mat4 outTransModel; +layout(location = 6) out float outLodBias; +layout(location = 7) out vec4 color; + +void write_deeper_in_function() +{ + outTransModel[1][1] = ubo.lodBias; + color = colors[2]; +} + +void write_in_function() +{ + outTransModel[2] = vec4(inNormal, 1.0); + write_deeper_in_function(); +} + +void main() +{ + gl_Position = (ubo.projection * ubo.model) * vec4(inPos, 1.0); + outPos = vec3((ubo.model * vec4(inPos, 1.0)).xyz); + outNormal = mat3(vec3(ubo.model[0].x, ubo.model[0].y, ubo.model[0].z), vec3(ubo.model[1].x, ubo.model[1].y, ubo.model[1].z), vec3(ubo.model[2].x, ubo.model[2].y, ubo.model[2].z)) * inNormal; + outLodBias = ubo.lodBias; + outTransModel = transpose(ubo.model) * inViewMat; + write_in_function(); +} + diff --git a/third_party/spirv-cross/shaders-msl/vert/interface-block-block-composites.frag b/third_party/spirv-cross/shaders-msl/vert/interface-block-block-composites.frag new file mode 100644 index 0000000..a0fb7c9 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/interface-block-block-composites.frag @@ -0,0 +1,17 @@ +#version 450 +layout(location = 0) in mat3 vMatrix; +layout(location = 4) in Vert +{ + mat3 wMatrix; + vec4 wTmp; + float arr[4]; +}; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = wMatrix[0].xxyy + wTmp + vMatrix[1].yyzz; + for (int i = 0; i < 4; i++) + FragColor += arr[i]; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/interface-block-block-composites.vert b/third_party/spirv-cross/shaders-msl/vert/interface-block-block-composites.vert new file mode 100644 index 0000000..899a852 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/interface-block-block-composites.vert @@ -0,0 +1,22 @@ +#version 450 +layout(location = 0) out mat3 vMatrix; +layout(location = 0) in mat3 Matrix; +layout(location = 4) in vec4 Pos; + +layout(location = 4) out Vert +{ + float arr[3]; + mat3 wMatrix; + vec4 wTmp; +}; + +void main() +{ + vMatrix = Matrix; + wMatrix = Matrix; + arr[0] = 1.0; + arr[1] = 2.0; + arr[2] = 3.0; + wTmp = Pos; + gl_Position = Pos; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/interpolation-qualifiers-block.vert b/third_party/spirv-cross/shaders-msl/vert/interpolation-qualifiers-block.vert new file mode 100644 index 0000000..73d6cbb --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/interpolation-qualifiers-block.vert @@ -0,0 +1,26 @@ +#version 450 + +layout(location=0) in vec4 Position; + +struct Output { + vec2 v0; + vec2 v1; + vec3 v2; + vec4 v3; + float v4; + float v5; + float v6; +}; + +layout(location=0) out centroid noperspective Output outp; + +void main() { + outp.v0 = Position.xy; + outp.v1 = Position.zw; + outp.v2 = vec3(Position.x, Position.z * Position.y, Position.x); + outp.v3 = Position.xxyy; + outp.v4 = Position.w; + outp.v5 = Position.y; + outp.v6 = Position.x * Position.w; + gl_Position = Position; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/interpolation-qualifiers.vert b/third_party/spirv-cross/shaders-msl/vert/interpolation-qualifiers.vert new file mode 100644 index 0000000..541285f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/interpolation-qualifiers.vert @@ -0,0 +1,22 @@ +#version 450 + +layout(location=0) in vec4 Position; + +layout(location=0) out vec2 v0; +layout(location=1) out noperspective vec2 v1; +layout(location=2) out centroid vec3 v2; +layout(location=3) out centroid noperspective vec4 v3; +layout(location=4) out sample float v4; +layout(location=5) out sample noperspective float v5; +layout(location=6) out flat float v6; + +void main() { + v0 = Position.xy; + v1 = Position.zw; + v2 = vec3(Position.x, Position.z * Position.y, Position.x); + v3 = Position.xxyy; + v4 = Position.w; + v5 = Position.y; + v6 = Position.x * Position.w; + gl_Position = Position; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/invariant.msl21.vert b/third_party/spirv-cross/shaders-msl/vert/invariant.msl21.vert new file mode 100644 index 0000000..f090db5 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/invariant.msl21.vert @@ -0,0 +1,11 @@ +#version 310 es + +invariant gl_Position; +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; +layout(location = 2) in vec4 vInput2; + +void main() +{ + gl_Position = vInput0 + vInput1 * vInput2; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/leaf-function.capture.vert b/third_party/spirv-cross/shaders-msl/vert/leaf-function.capture.vert new file mode 100644 index 0000000..cdb60fa --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/leaf-function.capture.vert @@ -0,0 +1,22 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; + +layout(location = 0) out vec3 vNormal; + +void set_output() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} + +void main() +{ + set_output(); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/leaf-function.for-tess.vert b/third_party/spirv-cross/shaders-msl/vert/leaf-function.for-tess.vert new file mode 100644 index 0000000..cdb60fa --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/leaf-function.for-tess.vert @@ -0,0 +1,22 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; + +layout(location = 0) out vec3 vNormal; + +void set_output() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} + +void main() +{ + set_output(); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/no-disable-vertex-out.frag-output.vert b/third_party/spirv-cross/shaders-msl/vert/no-disable-vertex-out.frag-output.vert new file mode 100644 index 0000000..7ea3790 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/no-disable-vertex-out.frag-output.vert @@ -0,0 +1,16 @@ +#version 400 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable +layout(std140, binding = 0) uniform buf { + mat4 MVP; + vec4 position[12*3]; + vec4 attr[12*3]; +} ubuf; +layout (location = 0) out vec4 texcoord; +layout (location = 1) out vec3 frag_pos; +void main() +{ + texcoord = ubuf.attr[gl_VertexIndex]; + gl_Position = ubuf.MVP * ubuf.position[gl_VertexIndex]; + frag_pos = gl_Position.xyz; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/no_stage_out.for-tess.vert b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.for-tess.vert new file mode 100644 index 0000000..3c2573a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.for-tess.vert @@ -0,0 +1,14 @@ +#version 450 + +layout(binding = 0, std430) writeonly buffer _10_12 +{ + uvec4 _m0[1024]; +} _12; + +layout(location = 0) in uvec4 _19; + +void main() +{ + _12._m0[gl_VertexIndex] = _19; +} + diff --git a/third_party/spirv-cross/shaders-msl/vert/no_stage_out.vert b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.vert new file mode 100644 index 0000000..3c2573a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.vert @@ -0,0 +1,14 @@ +#version 450 + +layout(binding = 0, std430) writeonly buffer _10_12 +{ + uvec4 _m0[1024]; +} _12; + +layout(location = 0) in uvec4 _19; + +void main() +{ + _12._m0[gl_VertexIndex] = _19; +} + diff --git a/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_buff.vert b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_buff.vert new file mode 100644 index 0000000..96ed2bd --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_buff.vert @@ -0,0 +1,23 @@ +#version 450 + +layout(binding = 1, std430) writeonly buffer _33_35 +{ + uvec4 _m0[1024]; +} _35; + +layout(binding = 0, std140) uniform _38_40 +{ + uvec4 _m0[1024]; +} _40; + +layout(location = 0) in vec4 _14; + +void main() +{ + gl_Position = _14; + for (int _19 = 0; _19 < 1024; _19++) + { + _35._m0[_19] = _40._m0[_19]; + } +} + diff --git a/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_buff_atomic.vert b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_buff_atomic.vert new file mode 100644 index 0000000..607ac9f --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_buff_atomic.vert @@ -0,0 +1,15 @@ +#version 450 + +layout(binding = 0, std430) coherent buffer _19_21 +{ + uint _m0; +} _21; + +layout(location = 0) in vec4 _14; + +void main() +{ + gl_Position = _14; + uint _26 = atomicAdd(_21._m0, 1u); +} + diff --git a/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_tex.vert b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_tex.vert new file mode 100644 index 0000000..19c6673 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/no_stage_out.write_tex.vert @@ -0,0 +1,16 @@ +#version 450 + +layout(binding = 1, r32ui) uniform writeonly uimage1D _32; +layout(binding = 0, r32ui) uniform readonly uimage1D _35; + +layout(location = 0) in vec4 _14; + +void main() +{ + gl_Position = _14; + for (int _19 = 0; _19 < 128; _19++) + { + imageStore(_32, _19, imageLoad(_35, _19)); + } +} + diff --git a/third_party/spirv-cross/shaders-msl/vert/out_block.vert b/third_party/spirv-cross/shaders-msl/vert/out_block.vert new file mode 100644 index 0000000..d7a50c7 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/out_block.vert @@ -0,0 +1,22 @@ +#version 450 + +uniform Transform +{ + mat4 transform; +} block; + +layout(location = 0) in vec3 position; +layout(location = 1) in vec4 color; + +layout(location = 2) out VertexOut +{ + vec4 color; + vec4 color2; +} outputs; + +void main() +{ + gl_Position = block.transform * vec4(position, 1.0); + outputs.color = color; + outputs.color2 = color + vec4(1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/packed-bool-to-uint.vert b/third_party/spirv-cross/shaders-msl/vert/packed-bool-to-uint.vert new file mode 100644 index 0000000..933a15e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/packed-bool-to-uint.vert @@ -0,0 +1,22 @@ +#version 450 core + +struct Struct +{ + bool flags[1]; +}; + +layout(set=0, binding=0, std140) uniform defaultUniformsVS +{ + Struct flags; + vec2 uquad[4]; + mat4 umatrix; +}; + +layout (location = 0) in vec4 a_position; + +void main() +{ + gl_Position = umatrix * vec4(uquad[gl_VertexIndex], a_position.z, a_position.w); + if (flags.flags[0]) + gl_Position.z = 0.0; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/packed-bool2-to-packed_uint2.vert b/third_party/spirv-cross/shaders-msl/vert/packed-bool2-to-packed_uint2.vert new file mode 100644 index 0000000..e3939a4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/packed-bool2-to-packed_uint2.vert @@ -0,0 +1,22 @@ +#version 450 core + +struct Struct +{ + bvec2 flags[1]; +}; + +layout(set=0, binding=0, std140) uniform defaultUniformsVS +{ + Struct flags; + vec2 uquad[4]; + mat4 umatrix; +}; + +layout (location = 0) in vec4 a_position; + +void main() +{ + gl_Position = umatrix * vec4(uquad[gl_VertexIndex], a_position.z, a_position.w); + if (flags.flags[0].x) + gl_Position.z = 0.0; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/packed_matrix.vert b/third_party/spirv-cross/shaders-msl/vert/packed_matrix.vert new file mode 100644 index 0000000..4d99d21 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/packed_matrix.vert @@ -0,0 +1,41 @@ +#version 450 + +layout(binding = 13, std140) uniform _1365_18812 +{ + layout(row_major) mat4x3 _m0; + layout(row_major) mat4x3 _m1; +} _18812; + +layout(binding = 12, std140) uniform _1126_22044 +{ + layout(row_major) mat4 _m0; + layout(row_major) mat4 _m1; + float _m9; + vec3 _m10; + float _m11; + vec3 _m12; + float _m17; + float _m18; + float _m19; + vec2 _m20; +} _22044; + +layout(location = 0) out vec3 _3976; +layout(location = 0) in vec4 _5275; + +vec3 _2; + +void main() +{ + vec3 _23783; + do + { + _23783 = normalize(_18812._m1 * vec4(_5275.xyz, 0.0)); + break; + } while (false); + vec4 _14995 = vec4(_22044._m10 + (_5275.xyz * (_22044._m17 + _22044._m18)), 1.0) * _22044._m0; + _3976 = _23783; + vec4 _6282 = _14995; + _6282.y = -_14995.y; + gl_Position = _6282; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/pointsize.vert b/third_party/spirv-cross/shaders-msl/vert/pointsize.vert new file mode 100644 index 0000000..0fc7136 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/pointsize.vert @@ -0,0 +1,15 @@ +#version 450 +uniform params { + mat4 mvp; + float psize; +}; + +layout(location = 0) in vec4 position; +layout(location = 1) in vec4 color0; +layout(location = 0) out vec4 color; + +void main() { + gl_Position = mvp * position; + gl_PointSize = psize; + color = color0; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/read-from-row-major-array.vert b/third_party/spirv-cross/shaders-msl/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..792fb8e --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/read-from-row-major-array.vert @@ -0,0 +1,20 @@ +#version 310 es +layout(location = 0) in highp vec4 a_position; +layout(location = 0) out mediump float v_vtxResult; + +layout(set = 0, binding = 0, std140, row_major) uniform Block +{ + highp mat2x3 var[3][4]; +}; + +mediump float compare_float (highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; } +mediump float compare_vec3 (highp vec3 a, highp vec3 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); } +mediump float compare_mat2x3 (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1]); } + +void main (void) +{ + gl_Position = a_position; + mediump float result = 1.0; + result *= compare_mat2x3(var[0][0], mat2x3(2.0, 6.0, -6.0, 0.0, 5.0, 5.0)); + v_vtxResult = result; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/resource-arrays-leaf.ios.vert b/third_party/spirv-cross/shaders-msl/vert/resource-arrays-leaf.ios.vert new file mode 100644 index 0000000..b35c410 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/resource-arrays-leaf.ios.vert @@ -0,0 +1,27 @@ +#version 450 + +layout(constant_id = 0) const int arraySize = 3; + +layout(binding = 0, rgba32i) uniform iimage2D images[arraySize]; +layout(binding = 4) uniform constant_block +{ + vec4 foo; + int bar; +} constants[4]; + +layout(binding = 8) buffer storage_block +{ + uvec4 baz; + ivec2 quux; +} storage[2]; + +void doWork() +{ + storage[0].baz = uvec4(constants[3].foo); + storage[1].quux = imageLoad(images[2], ivec2(constants[1].bar)).xy; +} + +void main() +{ + doWork(); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/resource-arrays.ios.vert b/third_party/spirv-cross/shaders-msl/vert/resource-arrays.ios.vert new file mode 100644 index 0000000..cddc06a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/resource-arrays.ios.vert @@ -0,0 +1,23 @@ +#version 450 + +layout(constant_id = 0) const int arraySize = 3; + +layout(binding = 0, rgba32i) uniform iimage2D images[arraySize]; + +layout(binding = 4) uniform constant_block +{ + vec4 foo; + int bar; +} constants[4]; + +layout(binding = 8) buffer storage_block +{ + uvec4 baz; + ivec2 quux; +} storage[2]; + +void main() +{ + storage[0].baz = uvec4(constants[3].foo); + storage[1].quux = imageLoad(images[2], ivec2(constants[1].bar)).xy; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/return-array.force-native-array.vert b/third_party/spirv-cross/shaders-msl/vert/return-array.force-native-array.vert new file mode 100644 index 0000000..7084601 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/return-array.force-native-array.vert @@ -0,0 +1,22 @@ +#version 310 es + +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; + +vec4[2] test() +{ + return vec4[](vec4(10.0), vec4(20.0)); +} + +vec4[2] test2() +{ + vec4 foobar[2]; + foobar[0] = vInput0; + foobar[1] = vInput1; + return foobar; +} + +void main() +{ + gl_Position = test()[0] + test2()[1]; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/return-array.vert b/third_party/spirv-cross/shaders-msl/vert/return-array.vert new file mode 100644 index 0000000..7084601 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/return-array.vert @@ -0,0 +1,22 @@ +#version 310 es + +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; + +vec4[2] test() +{ + return vec4[](vec4(10.0), vec4(20.0)); +} + +vec4[2] test2() +{ + vec4 foobar[2]; + foobar[0] = vInput0; + foobar[1] = vInput1; + return foobar; +} + +void main() +{ + gl_Position = test()[0] + test2()[1]; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/set_builtin_in_func.vert b/third_party/spirv-cross/shaders-msl/vert/set_builtin_in_func.vert new file mode 100644 index 0000000..dd991e3 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/set_builtin_in_func.vert @@ -0,0 +1,12 @@ +#version 450 + +void write_outblock() +{ + gl_PointSize = 1.0; + gl_Position = vec4(gl_PointSize); +} + +void main() +{ + write_outblock(); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/sign-int-types.vert b/third_party/spirv-cross/shaders-msl/vert/sign-int-types.vert new file mode 100644 index 0000000..f8530a4 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/sign-int-types.vert @@ -0,0 +1,38 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; + uniform vec4 uFloatVec4; + uniform vec3 uFloatVec3; + uniform vec2 uFloatVec2; + uniform float uFloat; + uniform ivec4 uIntVec4; + uniform ivec3 uIntVec3; + uniform ivec2 uIntVec2; + uniform int uInt; +}; + +layout(location = 0) in vec4 aVertex; + +layout(location = 0) out vec4 vFloatVec4; +layout(location = 1) out vec3 vFloatVec3; +layout(location = 2) out vec2 vFloatVec2; +layout(location = 3) out float vFloat; +layout(location = 4) flat out ivec4 vIntVec4; +layout(location = 5) flat out ivec3 vIntVec3; +layout(location = 6) flat out ivec2 vIntVec2; +layout(location = 7) flat out int vInt; + +void main() +{ + gl_Position = uMVP * aVertex; + vFloatVec4 = sign(uFloatVec4); + vFloatVec3 = sign(uFloatVec3); + vFloatVec2 = sign(uFloatVec2); + vFloat = sign(uFloat); + vIntVec4 = sign(uIntVec4); + vIntVec3 = sign(uIntVec3); + vIntVec2 = sign(uIntVec2); + vInt = sign(uInt); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/signedness-mismatch.shader-inputs.vert b/third_party/spirv-cross/shaders-msl/vert/signedness-mismatch.shader-inputs.vert new file mode 100644 index 0000000..dc0f7e6 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/signedness-mismatch.shader-inputs.vert @@ -0,0 +1,14 @@ +#version 450 + +#extension GL_AMD_gpu_shader_int16 : require + +layout(location = 0) in int16_t a; +layout(location = 1) in ivec2 b; +layout(location = 2) in uint16_t c[2]; +layout(location = 4) in uvec4 d[2]; + +void main() +{ + gl_Position = vec4(float(int(a)), float(b.x), float(uint(c[1])), float(d[0].w)); +} + diff --git a/third_party/spirv-cross/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert b/third_party/spirv-cross/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert new file mode 100644 index 0000000..6bc7ddf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/texture_buffer.texture-buffer-native.msl21.vert @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_OES_texture_buffer : require + +layout(binding = 4) uniform highp samplerBuffer uSamp; +layout(rgba32f, binding = 5) uniform readonly highp imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/texture_buffer.vert b/third_party/spirv-cross/shaders-msl/vert/texture_buffer.vert new file mode 100644 index 0000000..6bc7ddf --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/texture_buffer.vert @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_OES_texture_buffer : require + +layout(binding = 4) uniform highp samplerBuffer uSamp; +layout(rgba32f, binding = 5) uniform readonly highp imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} diff --git a/third_party/spirv-cross/shaders-msl/vert/ubo.alignment.vert b/third_party/spirv-cross/shaders-msl/vert/ubo.alignment.vert new file mode 100644 index 0000000..2e9d16d --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/ubo.alignment.vert @@ -0,0 +1,23 @@ +#version 310 es + +layout(binding = 0, std140) uniform UBO +{ + mat4 mvp; + vec2 targSize; + vec3 color; // vec3 following vec2 should cause MSL to add pad if float3 is packed + float opacity; // Single float following vec3 should cause MSL float3 to pack +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; +layout(location = 1) out vec3 vColor; +layout(location = 2) out vec2 vSize; + +void main() +{ + gl_Position = mvp * aVertex; + vNormal = aNormal; + vColor = color * opacity; + vSize = targSize * opacity; +} diff --git a/third_party/spirv-cross/shaders-msl/vert/ubo.vert b/third_party/spirv-cross/shaders-msl/vert/ubo.vert new file mode 100644 index 0000000..82e4626 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vert/ubo.vert @@ -0,0 +1,16 @@ +#version 310 es + +layout(binding = 0, std140) uniform UBO +{ + mat4 mvp; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = mvp * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag b/third_party/spirv-cross/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag new file mode 100644 index 0000000..963493b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/frag/basic.multiview.no-layered.nocompat.vk.frag @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex[4]; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor * texture(uTex, vTex[gl_ViewIndex]); +} + diff --git a/third_party/spirv-cross/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag b/third_party/spirv-cross/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag new file mode 100644 index 0000000..963493b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/frag/basic.multiview.nocompat.vk.frag @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex[4]; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor * texture(uTex, vTex[gl_ViewIndex]); +} + diff --git a/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag b/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag new file mode 100644 index 0000000..ba57b8c --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.msl23.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 19 +; Schema: 0 + OpCapability Shader + OpCapability DemoteToHelperInvocationEXT + OpExtension "SPV_EXT_demote_to_helper_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_demote_to_helper_invocation" + OpName %main "main" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %19 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %9 = OpIsHelperInvocationEXT %bool + OpDemoteToHelperInvocationEXT + %10 = OpLogicalNot %bool %9 + OpSelectionMerge %12 None + OpBranchConditional %10 %11 %12 + %11 = OpLabel + OpStore %FragColor %19 + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag b/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag new file mode 100644 index 0000000..8b8bb61 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.frag @@ -0,0 +1,8 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +void main() +{ + demote; + bool helper = helperInvocationEXT(); +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag b/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag new file mode 100644 index 0000000..8b8bb61 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/frag/demote-to-helper.vk.nocompat.msl23.ios.frag @@ -0,0 +1,8 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +void main() +{ + demote; + bool helper = helperInvocationEXT(); +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/frag/push-constant.vk.frag b/third_party/spirv-cross/shaders-msl/vulkan/frag/push-constant.vk.frag new file mode 100644 index 0000000..6180fab --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/frag/push-constant.vk.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor + push.value0 + push.value1; +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag b/third_party/spirv-cross/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag new file mode 100644 index 0000000..3cb75da --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/frag/spec-constant.msl11.vk.frag @@ -0,0 +1,67 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(constant_id = 1) const float a = 1.0; +layout(constant_id = 2) const float b = 2.0; +layout(constant_id = 3) const int c = 3; +layout(constant_id = 4) const int d = 4; +layout(constant_id = 5) const uint e = 5u; +layout(constant_id = 6) const uint f = 6u; +layout(constant_id = 7) const bool g = false; +layout(constant_id = 8) const bool h = true; +// glslang doesn't seem to support partial spec constants or composites yet, so only test the basics. + +void main() +{ + float t0 = a; + float t1 = b; + + uint c0 = uint(c); // OpIAdd with different types. + // FConvert, float-to-double. + int c1 = -c; // SNegate + int c2 = ~c; // OpNot + int c3 = c + d; // OpIAdd + int c4 = c - d; // OpISub + int c5 = c * d; // OpIMul + int c6 = c / d; // OpSDiv + uint c7 = e / f; // OpUDiv + int c8 = c % d; // OpSMod + uint c9 = e % f; // OpUMod + // TODO: OpSRem, any way to access this in GLSL? + int c10 = c >> d; // OpShiftRightArithmetic + uint c11 = e >> f; // OpShiftRightLogical + int c12 = c << d; // OpShiftLeftLogical + int c13 = c | d; // OpBitwiseOr + int c14 = c ^ d; // OpBitwiseXor + int c15 = c & d; // OpBitwiseAnd + // VectorShuffle, CompositeExtract, CompositeInsert, not testable atm. + bool c16 = g || h; // OpLogicalOr + bool c17 = g && h; // OpLogicalAnd + bool c18 = !g; // OpLogicalNot + bool c19 = g == h; // OpLogicalEqual + bool c20 = g != h; // OpLogicalNotEqual + // OpSelect not testable atm. + bool c21 = c == d; // OpIEqual + bool c22 = c != d; // OpINotEqual + bool c23 = c < d; // OpSLessThan + bool c24 = e < f; // OpULessThan + bool c25 = c > d; // OpSGreaterThan + bool c26 = e > f; // OpUGreaterThan + bool c27 = c <= d; // OpSLessThanEqual + bool c28 = e <= f; // OpULessThanEqual + bool c29 = c >= d; // OpSGreaterThanEqual + bool c30 = e >= f; // OpUGreaterThanEqual + // OpQuantizeToF16 not testable atm. + + int c31 = c8 + c3; + + int c32 = int(e); // OpIAdd with different types. + bool c33 = bool(c); // int -> bool + bool c34 = bool(e); // uint -> bool + int c35 = int(g); // bool -> int + uint c36 = uint(g); // bool -> uint + float c37 = float(g); // bool -> float + + FragColor = vec4(t0 + t1); +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/frag/spec-constant.vk.frag b/third_party/spirv-cross/shaders-msl/vulkan/frag/spec-constant.vk.frag new file mode 100644 index 0000000..3cb75da --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/frag/spec-constant.vk.frag @@ -0,0 +1,67 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(constant_id = 1) const float a = 1.0; +layout(constant_id = 2) const float b = 2.0; +layout(constant_id = 3) const int c = 3; +layout(constant_id = 4) const int d = 4; +layout(constant_id = 5) const uint e = 5u; +layout(constant_id = 6) const uint f = 6u; +layout(constant_id = 7) const bool g = false; +layout(constant_id = 8) const bool h = true; +// glslang doesn't seem to support partial spec constants or composites yet, so only test the basics. + +void main() +{ + float t0 = a; + float t1 = b; + + uint c0 = uint(c); // OpIAdd with different types. + // FConvert, float-to-double. + int c1 = -c; // SNegate + int c2 = ~c; // OpNot + int c3 = c + d; // OpIAdd + int c4 = c - d; // OpISub + int c5 = c * d; // OpIMul + int c6 = c / d; // OpSDiv + uint c7 = e / f; // OpUDiv + int c8 = c % d; // OpSMod + uint c9 = e % f; // OpUMod + // TODO: OpSRem, any way to access this in GLSL? + int c10 = c >> d; // OpShiftRightArithmetic + uint c11 = e >> f; // OpShiftRightLogical + int c12 = c << d; // OpShiftLeftLogical + int c13 = c | d; // OpBitwiseOr + int c14 = c ^ d; // OpBitwiseXor + int c15 = c & d; // OpBitwiseAnd + // VectorShuffle, CompositeExtract, CompositeInsert, not testable atm. + bool c16 = g || h; // OpLogicalOr + bool c17 = g && h; // OpLogicalAnd + bool c18 = !g; // OpLogicalNot + bool c19 = g == h; // OpLogicalEqual + bool c20 = g != h; // OpLogicalNotEqual + // OpSelect not testable atm. + bool c21 = c == d; // OpIEqual + bool c22 = c != d; // OpINotEqual + bool c23 = c < d; // OpSLessThan + bool c24 = e < f; // OpULessThan + bool c25 = c > d; // OpSGreaterThan + bool c26 = e > f; // OpUGreaterThan + bool c27 = c <= d; // OpSLessThanEqual + bool c28 = e <= f; // OpULessThanEqual + bool c29 = c >= d; // OpSGreaterThanEqual + bool c30 = e >= f; // OpUGreaterThanEqual + // OpQuantizeToF16 not testable atm. + + int c31 = c8 + c3; + + int c32 = int(e); // OpIAdd with different types. + bool c33 = bool(c); // int -> bool + bool c34 = bool(e); // uint -> bool + int c35 = int(g); // bool -> int + uint c36 = uint(g); // bool -> uint + float c37 = float(g); // bool -> float + + FragColor = vec4(t0 + t1); +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert b/third_party/spirv-cross/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert new file mode 100644 index 0000000..d54931a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/vert/device-group.multiview.viewfromdev.nocompat.vk.vert @@ -0,0 +1,8 @@ +#version 450 core +#extension GL_EXT_device_group : require +#extension GL_EXT_multiview : require + +void main() +{ + gl_Position = vec4(gl_DeviceIndex, gl_ViewIndex, 0.0, 1.0); +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert b/third_party/spirv-cross/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert new file mode 100644 index 0000000..16ed51b --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/vert/device-group.nocompat.vk.vert @@ -0,0 +1,7 @@ +#version 450 core +#extension GL_EXT_device_group : require + +void main() +{ + gl_Position = vec4(gl_DeviceIndex); +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert b/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert new file mode 100644 index 0000000..eb1bc76 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.multiview.no-layered.nocompat.vk.vert @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require + +layout(std140, binding = 0) uniform MVPs +{ + mat4 MVP[2]; +}; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = MVP[gl_ViewIndex] * Position; +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert b/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert new file mode 100644 index 0000000..eb1bc76 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.multiview.nocompat.vk.vert @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require + +layout(std140, binding = 0) uniform MVPs +{ + mat4 MVP[2]; +}; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = MVP[gl_ViewIndex] * Position; +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert b/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert new file mode 100644 index 0000000..eb1bc76 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/vert/multiview.nocompat.vk.vert @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require + +layout(std140, binding = 0) uniform MVPs +{ + mat4 MVP[2]; +}; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = MVP[gl_ViewIndex] * Position; +} diff --git a/third_party/spirv-cross/shaders-msl/vulkan/vert/small-storage.vk.vert b/third_party/spirv-cross/shaders-msl/vulkan/vert/small-storage.vk.vert new file mode 100644 index 0000000..0ca8144 --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/vert/small-storage.vk.vert @@ -0,0 +1,38 @@ +#version 450 core + +// GL_EXT_shader_16bit_storage doesn't support input/output. +#extension GL_EXT_shader_8bit_storage : require +#extension GL_AMD_gpu_shader_int16 : require +#extension GL_AMD_gpu_shader_half_float : require + +layout(location = 0) in int16_t foo; +layout(location = 1) in uint16_t bar; +layout(location = 2) in float16_t baz; + +layout(binding = 0) uniform block { + i16vec2 a; + u16vec2 b; + i8vec2 c; + u8vec2 d; + f16vec2 e; +}; + +layout(binding = 1) readonly buffer storage { + i16vec3 f; + u16vec3 g; + i8vec3 h; + u8vec3 i; + f16vec3 j; +}; + +layout(location = 0) out i16vec4 p; +layout(location = 1) out u16vec4 q; +layout(location = 2) out f16vec4 r; + +void main() { + p = i16vec4(int(foo) + ivec4(ivec2(a), ivec2(c)) - ivec4(ivec3(f) / ivec3(h), 1)); + q = u16vec4(uint(bar) + uvec4(uvec2(b), uvec2(d)) - uvec4(uvec3(g) / uvec3(i), 1)); + r = f16vec4(float(baz) + vec4(vec2(e), 0, 1) - vec4(vec3(j), 1)); + gl_Position = vec4(0, 0, 0, 1); +} + diff --git a/third_party/spirv-cross/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert b/third_party/spirv-cross/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert new file mode 100644 index 0000000..4d0438a --- /dev/null +++ b/third_party/spirv-cross/shaders-msl/vulkan/vert/vulkan-vertex.vk.vert @@ -0,0 +1,6 @@ +#version 310 es + +void main() +{ + gl_Position = float(gl_VertexIndex + gl_InstanceIndex) * vec4(1.0, 2.0, 3.0, 4.0); +} diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp new file mode 100644 index 0000000..8f4c957 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 52 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "values" + OpName %_ "" + OpDecorate %_runtimearr_int ArrayStride 4 + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_100 = OpConstant %int 100 + %bool = OpTypeBool +%_runtimearr_int = OpTypeRuntimeArray %int + %SSBO = OpTypeStruct %_runtimearr_int +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %32 + %32 = OpLabel + %51 = OpPhi %int %int_0 %5 %49 %loop_continue + %38 = OpSLessThan %bool %51 %int_100 + OpLoopMerge %loop_merge %loop_continue None + OpBranchConditional %38 %loop_body %loop_merge + %loop_body = OpLabel + %40 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %51 + OpBranch %loop_continue + %loop_continue = OpLabel + %41 = OpLoad %int %40 + %44 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %41 + OpStore %44 %51 + %47 = OpIAdd %int %41 %int_1 + %48 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %47 + %49 = OpLoad %int %48 + OpStore %40 %49 + OpBranch %32 + %loop_merge = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp new file mode 100644 index 0000000..b1ddd7c --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp @@ -0,0 +1,54 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 52 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "values" + OpName %_ "" + OpDecorate %_runtimearr_int ArrayStride 4 + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_100 = OpConstant %int 100 + %bool = OpTypeBool +%_runtimearr_int = OpTypeRuntimeArray %int + %SSBO = OpTypeStruct %_runtimearr_int +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %32 + %32 = OpLabel + %51 = OpPhi %int %int_0 %5 %49 %loop_continue + %38 = OpSLessThan %bool %51 %int_100 + OpLoopMerge %loop_merge %loop_continue None + OpBranchConditional %38 %loop_body %loop_merge + %loop_body = OpLabel + %40 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %51 + OpBranch %loop_continue + %loop_continue = OpLabel + %41 = OpLoad %int %40 + %44 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %41 + OpStore %44 %51 + %47 = OpIAdd %int %41 %int_1 + %48 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %47 + %49 = OpLoad %int %48 + OpBranch %32 + %loop_merge = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/access-tracking-function-call-result.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/access-tracking-function-call-result.asm.comp new file mode 100644 index 0000000..c11d4cd --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/access-tracking-function-call-result.asm.comp @@ -0,0 +1,54 @@ +; SPIR-V +; Version: 1.5 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 460 + OpName %main "main" + OpName %foo_ "foo(" + OpName %Output "Output" + OpMemberName %Output 0 "myout" + OpName %_ "" + OpMemberDecorate %Output 0 Offset 0 + OpDecorate %Output BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %9 = OpTypeFunction %int + %int_12 = OpConstant %int 12 + %bool = OpTypeBool + %true = OpConstantTrue %bool + %Output = OpTypeStruct %int +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %_ = OpVariable %_ptr_Uniform_Output Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_int = OpTypePointer Uniform %int + %main = OpFunction %void None %7 + %16 = OpLabel + %17 = OpFunctionCall %int %foo_ + OpBranch %18 + %18 = OpLabel + OpLoopMerge %19 %20 None + OpBranchConditional %true %21 %19 + %21 = OpLabel + %22 = OpAccessChain %_ptr_Uniform_int %_ %int_0 + OpStore %22 %17 + OpReturn + %20 = OpLabel + OpBranch %18 + %19 = OpLabel + %23 = OpAccessChain %_ptr_Uniform_int %_ %int_0 + OpStore %23 %17 + OpReturn + OpFunctionEnd + %foo_ = OpFunction %int None %9 + %24 = OpLabel + OpReturnValue %int_12 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp new file mode 100644 index 0000000..87aee2d --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/aliased-struct-divergent-member-name.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 37 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %T "T" + OpMemberName %T 0 "a" + OpName %v "v" + OpName %T_0 "T" + OpMemberName %T_0 0 "b" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "foo" + OpName %_ "" + OpName %T_1 "T" + OpMemberName %T_1 0 "c" + OpName %SSBO2 "SSBO2" + OpMemberName %SSBO2 0 "bar" + OpName %__0 "" + OpMemberDecorate %T_0 0 Offset 0 + OpDecorate %_runtimearr_T_0 ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpMemberDecorate %T_1 0 Offset 0 + OpDecorate %_runtimearr_T_1 ArrayStride 16 + OpMemberDecorate %SSBO2 0 Offset 0 + OpDecorate %SSBO2 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %T = OpTypeStruct %float +%_ptr_Function_T = OpTypePointer Function %T + %float_40 = OpConstant %float 40 + %11 = OpConstantComposite %T %float_40 + %T_0 = OpTypeStruct %float +%_runtimearr_T_0 = OpTypeRuntimeArray %T_0 + %SSBO1 = OpTypeStruct %_runtimearr_T_0 +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 +%_ptr_Uniform_T_0 = OpTypePointer Uniform %T_0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %T_1 = OpTypeStruct %float +%_runtimearr_T_1 = OpTypeRuntimeArray %T_1 + %SSBO2 = OpTypeStruct %_runtimearr_T_1 +%_ptr_Uniform_SSBO2 = OpTypePointer Uniform %SSBO2 + %__0 = OpVariable %_ptr_Uniform_SSBO2 Uniform + %int_30 = OpConstant %int 30 +%_ptr_Uniform_T_1 = OpTypePointer Uniform %T_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %v = OpVariable %_ptr_Function_T Function + OpStore %v %11 + %20 = OpLoad %T %v + %22 = OpAccessChain %_ptr_Uniform_T_0 %_ %int_0 %int_10 + %23 = OpCompositeExtract %float %20 0 + %25 = OpAccessChain %_ptr_Uniform_float %22 %int_0 + OpStore %25 %23 + %32 = OpLoad %T %v + %34 = OpAccessChain %_ptr_Uniform_T_1 %__0 %int_0 %int_30 + %35 = OpCompositeExtract %float %32 0 + %36 = OpAccessChain %_ptr_Uniform_float %34 %int_0 + OpStore %36 %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp new file mode 100644 index 0000000..504a954 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp @@ -0,0 +1,143 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 76 +; Schema: 0 + OpCapability Shader + OpCapability Int16 + OpCapability StorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types_int16" + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "s32" + OpMemberName %SSBO 1 "u32" + OpMemberName %SSBO 2 "s16" + OpMemberName %SSBO 3 "u16" + OpMemberName %SSBO 4 "f32" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpMemberDecorate %SSBO 2 Offset 8 + OpMemberDecorate %SSBO 3 Offset 10 + OpMemberDecorate %SSBO 4 Offset 12 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %short = OpTypeInt 16 1 + %ushort = OpTypeInt 16 0 + %float = OpTypeFloat 32 + %SSBO = OpTypeStruct %int %uint %short %ushort %float +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_2 = OpConstant %int 2 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_Uniform_short = OpTypePointer Uniform %short + %int_1 = OpConstant %int 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_3 = OpConstant %int 3 +%_ptr_Uniform_ushort = OpTypePointer Uniform %ushort + %int_4 = OpConstant %int 4 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %main = OpFunction %void None %3 + %5 = OpLabel + %ptr_s32 = OpAccessChain %_ptr_Uniform_int %_ %int_0 + %ptr_u32 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 + %ptr_s16 = OpAccessChain %_ptr_Uniform_short %_ %int_2 + %ptr_u16 = OpAccessChain %_ptr_Uniform_ushort %_ %int_3 + %ptr_f32 = OpAccessChain %_ptr_Uniform_float %_ %int_4 + %s32 = OpLoad %int %ptr_s32 + %u32 = OpLoad %uint %ptr_u32 + %s16 = OpLoad %short %ptr_s16 + %u16 = OpLoad %ushort %ptr_u16 + %f32 = OpLoad %float %ptr_f32 + + ; Sign-extend + %s16_to_s32_signed = OpSConvert %int %s16 + OpStore %ptr_s32 %s16_to_s32_signed + %s16_to_u32_signed = OpSConvert %uint %s16 + OpStore %ptr_u32 %s16_to_u32_signed + + %u16_to_s32_signed = OpSConvert %int %u16 + OpStore %ptr_s32 %u16_to_s32_signed + %u16_to_u32_signed = OpSConvert %uint %u16 + OpStore %ptr_u32 %u16_to_u32_signed + + ; Zero-extend + ; Result must be unsigned for OpUConvert. + ;%s16_to_s32_unsigned = OpUConvert %int %s16 + ;OpStore %ptr_s32 %s16_to_s32_unsigned + %s16_to_u32_unsigned = OpUConvert %uint %s16 + OpStore %ptr_u32 %s16_to_u32_unsigned + + ;%u16_to_s32_unsigned = OpUConvert %int %u16 + ;OpStore %ptr_s32 %u16_to_s32_unsigned + %u16_to_u32_unsigned = OpUConvert %uint %u16 + OpStore %ptr_u32 %u16_to_u32_unsigned + + ; Truncate (SConvert == UConvert) + %s32_to_s16_signed = OpSConvert %short %s32 + OpStore %ptr_s16 %s32_to_s16_signed + %s32_to_u16_signed = OpSConvert %ushort %s32 + OpStore %ptr_u16 %s32_to_u16_signed + + %u32_to_s16_signed = OpSConvert %short %u32 + OpStore %ptr_s16 %u32_to_s16_signed + %u32_to_u16_signed = OpSConvert %ushort %u32 + OpStore %ptr_u16 %u32_to_u16_signed + + ;%s32_to_s16_unsigned = OpUConvert %short %s32 + ;OpStore %ptr_s16 %s32_to_s16_unsigned + %s32_to_u16_unsigned = OpUConvert %ushort %s32 + OpStore %ptr_u16 %s32_to_u16_unsigned + + ;%u32_to_s16_unsigned = OpUConvert %short %u32 + ;OpStore %ptr_s16 %u32_to_s16_unsigned + %u32_to_u16_unsigned = OpUConvert %ushort %u32 + OpStore %ptr_u16 %u32_to_u16_unsigned + + ; SToF + %s16_to_f32_signed = OpConvertSToF %float %s16 + OpStore %ptr_f32 %s16_to_f32_signed + %u16_to_f32_signed = OpConvertSToF %float %u16 + OpStore %ptr_f32 %u16_to_f32_signed + + %s32_to_f32_signed = OpConvertSToF %float %s32 + OpStore %ptr_f32 %s32_to_f32_signed + %u32_to_f32_signed = OpConvertSToF %float %u32 + OpStore %ptr_f32 %u32_to_f32_signed + + ; UToF + %s16_to_f32_unsigned = OpConvertUToF %float %s16 + OpStore %ptr_f32 %s16_to_f32_unsigned + %u16_to_f32_unsigned = OpConvertUToF %float %u16 + OpStore %ptr_f32 %u16_to_f32_unsigned + + %s32_to_f32_unsigned = OpConvertUToF %float %s32 + OpStore %ptr_f32 %s32_to_f32_unsigned + %u32_to_f32_unsigned = OpConvertUToF %float %u32 + OpStore %ptr_f32 %u32_to_f32_unsigned + + ; FToS + %f32_to_s16_signed = OpConvertFToS %short %f32 + OpStore %ptr_s16 %f32_to_s16_signed + %f32_to_u16_signed = OpConvertFToS %ushort %f32 + OpStore %ptr_u16 %f32_to_u16_signed + + ; FToU + %f32_to_u16_unsigned = OpConvertFToU %ushort %f32 + OpStore %ptr_u16 %f32_to_u16_unsigned + ; Result must be unsigned for FToU, so don't bother testing that. + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/atomic-load-store.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/atomic-load-store.asm.comp new file mode 100644 index 0000000..3f2d141 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/atomic-load-store.asm.comp @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 23 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %c "c" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpMemberName %SSBO 1 "b" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %SSBO = OpTypeStruct %uint %uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_0 = OpConstant %int 0 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %c = OpVariable %_ptr_Function_uint Function + %15 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 + %16 = OpAtomicLoad %uint %15 %int_1 %int_0 + OpStore %c %16 + %18 = OpLoad %uint %c + %19 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 + OpAtomicStore %19 %int_1 %int_0 %18 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/atomic-result-temporary.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/atomic-result-temporary.asm.comp new file mode 100644 index 0000000..a323841 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/atomic-result-temporary.asm.comp @@ -0,0 +1,59 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "count" + OpMemberName %SSBO 1 "data" + OpName %_ "" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO = OpTypeStruct %uint %_runtimearr_uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %uint_1024 = OpConstant %uint 1024 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%_ptr_Input_uint = OpTypePointer Input %uint + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 + %19 = OpAtomicIAdd %uint %16 %uint_1 %uint_0 %uint_1 + %23 = OpULessThan %bool %19 %uint_1024 + OpSelectionMerge %25 None + OpBranchConditional %23 %24 %25 + %24 = OpLabel + %32 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %33 = OpLoad %uint %32 + %34 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 %19 + OpStore %34 %33 + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp new file mode 100644 index 0000000..3651a4d --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/bitcast-fp16-fp32.asm.vk.comp @@ -0,0 +1,63 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 33 +; Schema: 0 + OpCapability Shader + OpCapability Float16 + OpCapability StorageBuffer16BitAccess + OpExtension "SPV_KHR_16bit_storage" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types" + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpMemberName %SSBO 1 "b" + OpMemberName %SSBO 2 "c" + OpMemberName %SSBO 3 "d" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpMemberDecorate %SSBO 2 Offset 8 + OpMemberDecorate %SSBO 3 Offset 12 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %half = OpTypeFloat 16 + %v2half = OpTypeVector %half 2 + %float = OpTypeFloat 32 + %SSBO = OpTypeStruct %v2half %float %float %v2half +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v2half = OpTypePointer Uniform %v2half + %uint = OpTypeInt 32 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %int_3 = OpConstant %int 3 + %int_2 = OpConstant %int 2 + %v3uint = OpTypeVector %uint 3 + %uint_1 = OpConstant %uint 1 +%gl_WorkGroupSize = OpConstantComposite %v3uint %uint_1 %uint_1 %uint_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Uniform_v2half %_ %int_0 + %17 = OpLoad %v2half %16 + %20 = OpBitcast %float %17 + %22 = OpAccessChain %_ptr_Uniform_float %_ %int_1 + OpStore %22 %20 + %25 = OpAccessChain %_ptr_Uniform_float %_ %int_2 + %26 = OpLoad %float %25 + %28 = OpBitcast %v2half %26 + %29 = OpAccessChain %_ptr_Uniform_v2half %_ %int_3 + OpStore %29 %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/bitfield-signed-operations.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/bitfield-signed-operations.asm.comp new file mode 100644 index 0000000..435fa32 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/bitfield-signed-operations.asm.comp @@ -0,0 +1,97 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 26 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "ints" + OpMemberName %SSBO 1 "uints" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + + %int_1 = OpConstant %int 1 + %uint_11 = OpConstant %uint 11 + + %SSBO = OpTypeStruct %v4int %v4uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4int = OpTypePointer Uniform %v4int +%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint + %main = OpFunction %void None %3 + %5 = OpLabel + %ints_ptr = OpAccessChain %_ptr_Uniform_v4int %_ %int_0 + %uints_ptr = OpAccessChain %_ptr_Uniform_v4uint %_ %int_1 + %ints = OpLoad %v4int %ints_ptr + %uints = OpLoad %v4uint %uints_ptr + + %ints_alt = OpVectorShuffle %v4int %ints %ints 3 2 1 0 + %uints_alt = OpVectorShuffle %v4uint %uints %uints 3 2 1 0 + + %int_to_int_popcount = OpBitCount %v4int %ints + %int_to_uint_popcount = OpBitCount %v4uint %ints + %uint_to_int_popcount = OpBitCount %v4int %uints + %uint_to_uint_popcount = OpBitCount %v4uint %uints + + ; BitReverse must have matching types w.r.t. sign, yay. + %int_to_int_reverse = OpBitReverse %v4int %ints + ;%int_to_uint_reverse = OpBitReverse %v4uint %ints + ;%uint_to_int_reverse = OpBitReverse %v4int %uints + %uint_to_uint_reverse = OpBitReverse %v4uint %uints + + ; Base and Result must match. + %int_to_int_sbit = OpBitFieldSExtract %v4int %ints %int_1 %uint_11 + ;%int_to_uint_sbit = OpBitFieldSExtract %v4uint %ints %offset %count + ;%uint_to_int_sbit = OpBitFieldSExtract %v4int %uints %offset %count + %uint_to_uint_sbit = OpBitFieldSExtract %v4uint %uints %uint_11 %int_1 + + ; Base and Result must match. + %int_to_int_ubit = OpBitFieldUExtract %v4int %ints %int_1 %uint_11 + ;%int_to_uint_ubit = OpBitFieldUExtract %v4uint %ints %offset %count + ;%uint_to_int_ubit = OpBitFieldUExtract %v4int %uints %offset %count + %uint_to_uint_ubit = OpBitFieldUExtract %v4uint %uints %uint_11 %int_1 + + %int_to_int_insert = OpBitFieldInsert %v4int %ints %ints_alt %int_1 %uint_11 + %uint_to_uint_insert = OpBitFieldInsert %v4uint %uints %uints_alt %uint_11 %int_1 + + OpStore %ints_ptr %int_to_int_popcount + OpStore %uints_ptr %int_to_uint_popcount + OpStore %ints_ptr %uint_to_int_popcount + OpStore %uints_ptr %uint_to_uint_popcount + + OpStore %ints_ptr %int_to_int_reverse + ;OpStore %uints_ptr %int_to_uint_reverse + ;OpStore %ints_ptr %uint_to_int_reverse + OpStore %uints_ptr %uint_to_uint_reverse + + OpStore %ints_ptr %int_to_int_sbit + ;OpStore %uints_ptr %int_to_uint_sbit + ;OpStore %ints_ptr %uint_to_int_sbit + OpStore %uints_ptr %uint_to_uint_sbit + + OpStore %ints_ptr %int_to_int_ubit + ;OpStore %uints_ptr %int_to_uint_ubit + ;OpStore %ints_ptr %uint_to_int_ubit + OpStore %uints_ptr %uint_to_uint_ubit + + OpStore %ints_ptr %int_to_int_insert + OpStore %uints_ptr %uint_to_uint_insert + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/bitscan.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/bitscan.asm.comp new file mode 100644 index 0000000..e3b785c --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/bitscan.asm.comp @@ -0,0 +1,72 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "u" + OpMemberName %SSBO 1 "i" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + %SSBO = OpTypeStruct %uvec4 %ivec4 +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uvec4 = OpTypePointer Uniform %uvec4 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_ivec4 = OpTypePointer Uniform %ivec4 + %main = OpFunction %void None %3 + %5 = OpLabel + %uptr = OpAccessChain %_ptr_Uniform_uvec4 %_ %int_0 + %iptr = OpAccessChain %_ptr_Uniform_ivec4 %_ %int_1 + %uvalue = OpLoad %uvec4 %uptr + %ivalue = OpLoad %ivec4 %iptr + + %lsb_uint_to_uint = OpExtInst %uvec4 %1 FindILsb %uvalue + %lsb_uint_to_int = OpExtInst %ivec4 %1 FindILsb %uvalue + %lsb_int_to_uint = OpExtInst %uvec4 %1 FindILsb %ivalue + %lsb_int_to_int = OpExtInst %ivec4 %1 FindILsb %ivalue + + %umsb_uint_to_uint = OpExtInst %uvec4 %1 FindUMsb %uvalue + %umsb_uint_to_int = OpExtInst %ivec4 %1 FindUMsb %uvalue + %umsb_int_to_uint = OpExtInst %uvec4 %1 FindUMsb %ivalue + %umsb_int_to_int = OpExtInst %ivec4 %1 FindUMsb %ivalue + + %smsb_uint_to_uint = OpExtInst %uvec4 %1 FindSMsb %uvalue + %smsb_uint_to_int = OpExtInst %ivec4 %1 FindSMsb %uvalue + %smsb_int_to_uint = OpExtInst %uvec4 %1 FindSMsb %ivalue + %smsb_int_to_int = OpExtInst %ivec4 %1 FindSMsb %ivalue + + OpStore %uptr %lsb_uint_to_uint + OpStore %iptr %lsb_uint_to_int + OpStore %uptr %lsb_int_to_uint + OpStore %iptr %lsb_int_to_int + + OpStore %uptr %umsb_uint_to_uint + OpStore %iptr %umsb_uint_to_int + OpStore %uptr %umsb_int_to_uint + OpStore %iptr %umsb_int_to_int + + OpStore %uptr %smsb_uint_to_uint + OpStore %iptr %smsb_uint_to_int + OpStore %uptr %smsb_int_to_uint + OpStore %iptr %smsb_int_to_int + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-atomic-nonuniform.vk.nocompat.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-atomic-nonuniform.vk.nocompat.asm.comp new file mode 100644 index 0000000..132f38b --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-atomic-nonuniform.vk.nocompat.asm.comp @@ -0,0 +1,53 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 26 +; Schema: 0 + OpCapability Shader + OpCapability ShaderNonUniform + OpCapability RuntimeDescriptorArray + OpCapability StorageBufferArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "v" + OpName %ssbos "ssbos" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %ssbos DescriptorSet 0 + OpDecorate %ssbos Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %22 NonUniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %SSBO = OpTypeStruct %uint +%_runtimearr_SSBO = OpTypeRuntimeArray %SSBO +%_ptr_Uniform__runtimearr_SSBO = OpTypePointer Uniform %_runtimearr_SSBO + %ssbos = OpVariable %_ptr_Uniform__runtimearr_SSBO Uniform + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_2 = OpConstant %uint 2 +%_ptr_Input_uint = OpTypePointer Input %uint + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_2 + %17 = OpLoad %uint %16 + %18 = OpCopyObject %uint %17 + %22 = OpAccessChain %_ptr_Uniform_uint %ssbos %18 %int_0 + %25 = OpAtomicIAdd %uint %22 %uint_1 %uint_0 %uint_1 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer-2.asm.nocompat.vk.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer-2.asm.nocompat.vk.comp new file mode 100644 index 0000000..76894aa --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer-2.asm.nocompat.vk.comp @@ -0,0 +1,44 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 27 +; Schema: 0 + OpCapability Shader + OpCapability Int64 + OpCapability PhysicalStorageBufferAddressesEXT + OpExtension "SPV_EXT_physical_storage_buffer" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_ARB_gpu_shader_int64" + OpSourceExtension "GL_EXT_buffer_reference" + OpDecorate %ptr AliasedPointerEXT + OpMemberDecorate %Registers 0 Offset 0 + OpDecorate %Registers Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_PhysicalStorageBufferEXT_uint = OpTypePointer PhysicalStorageBufferEXT %uint +%_ptr_Function__ptr_PhysicalStorageBufferEXT_uint = OpTypePointer Function %_ptr_PhysicalStorageBufferEXT_uint + %ulong = OpTypeInt 64 0 + %Registers = OpTypeStruct %ulong +%_ptr_PushConstant_Registers = OpTypePointer PushConstant %Registers + %registers = OpVariable %_ptr_PushConstant_Registers PushConstant + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_ulong = OpTypePointer PushConstant %ulong + %int_10 = OpConstant %int 10 + %uint_20 = OpConstant %uint 20 + %main = OpFunction %void None %3 + %5 = OpLabel + %ptr = OpVariable %_ptr_Function__ptr_PhysicalStorageBufferEXT_uint Function + %19 = OpAccessChain %_ptr_PushConstant_ulong %registers %int_0 + %20 = OpLoad %ulong %19 + %21 = OpConvertUToPtr %_ptr_PhysicalStorageBufferEXT_uint %20 + OpStore %ptr %21 + %22 = OpLoad %_ptr_PhysicalStorageBufferEXT_uint %ptr + OpStore %22 %uint_20 Aligned 4 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer.asm.nocompat.vk.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer.asm.nocompat.vk.comp new file mode 100644 index 0000000..d1270d4 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/buffer-reference-synthesized-pointer.asm.nocompat.vk.comp @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 27 +; Schema: 0 + OpCapability Shader + OpCapability Int64 + OpCapability PhysicalStorageBufferAddressesEXT + OpExtension "SPV_EXT_physical_storage_buffer" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_ARB_gpu_shader_int64" + OpSourceExtension "GL_EXT_buffer_reference" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %uintPtr 0 Offset 0 + OpDecorate %uintPtr Block + OpDecorate %ptr AliasedPointerEXT + OpMemberDecorate %Registers 0 Offset 0 + OpDecorate %Registers Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %uintPtr = OpTypeStruct %_runtimearr_uint +%_ptr_PhysicalStorageBufferEXT_uint_array = OpTypePointer PhysicalStorageBufferEXT %_runtimearr_uint +%_ptr_Function__ptr_PhysicalStorageBufferEXT_uint_array = OpTypePointer Function %_ptr_PhysicalStorageBufferEXT_uint_array + %ulong = OpTypeInt 64 0 + %Registers = OpTypeStruct %ulong +%_ptr_PushConstant_Registers = OpTypePointer PushConstant %Registers + %registers = OpVariable %_ptr_PushConstant_Registers PushConstant + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_ulong = OpTypePointer PushConstant %ulong + %int_10 = OpConstant %int 10 + %uint_20 = OpConstant %uint 20 +%_ptr_PhysicalStorageBufferEXT_uint = OpTypePointer PhysicalStorageBufferEXT %uint + %main = OpFunction %void None %3 + %5 = OpLabel + %ptr = OpVariable %_ptr_Function__ptr_PhysicalStorageBufferEXT_uint_array Function + %19 = OpAccessChain %_ptr_PushConstant_ulong %registers %int_0 + %20 = OpLoad %ulong %19 + %21 = OpConvertUToPtr %_ptr_PhysicalStorageBufferEXT_uint_array %20 + OpStore %ptr %21 + %22 = OpLoad %_ptr_PhysicalStorageBufferEXT_uint_array %ptr + %26 = OpAccessChain %_ptr_PhysicalStorageBufferEXT_uint %22 %int_10 + OpStore %26 %uint_20 Aligned 4 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/constant-composite-undef.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/constant-composite-undef.asm.comp new file mode 100644 index 0000000..8997d0a --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/constant-composite-undef.asm.comp @@ -0,0 +1,40 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Block "Block" + OpMemberName %Block 0 "f" + OpName %block "block" + OpMemberDecorate %Block 0 Offset 0 + OpDecorate %Block BufferBlock + OpDecorate %block DescriptorSet 0 + OpDecorate %block Binding 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Block = OpTypeStruct %v4float +%_ptr_Uniform_Block = OpTypePointer Uniform %Block + %block = OpVariable %_ptr_Uniform_Block Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%float_0_100000001 = OpConstant %float 0.100000001 +%float_0_200000003 = OpConstant %float 0.200000003 +%float_0_300000012 = OpConstant %float 0.300000012 + %15 = OpUndef %float + %16 = OpConstantComposite %v4float %float_0_100000001 %float_0_200000003 %float_0_300000012 %15 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %main = OpFunction %void None %6 + %18 = OpLabel + %19 = OpAccessChain %_ptr_Uniform_v4float %block %int_0 + OpStore %19 %16 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/copy-logical.spv14.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/copy-logical.spv14.asm.comp new file mode 100644 index 0000000..20fa0b0 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/copy-logical.spv14.asm.comp @@ -0,0 +1,69 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 48 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %ssbo + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %B1 "B1" + OpName %A "A" + OpName %C "C" + OpName %B2 "B2" + OpMemberName %A 0 "a" + OpMemberName %A 1 "b1" + OpMemberName %A 2 "b1_array" + OpMemberName %C 0 "c" + OpMemberName %C 1 "b2" + OpMemberName %C 2 "b2_array" + OpMemberName %B1 0 "elem1" + OpMemberName %B2 0 "elem2" + OpMemberName %SSBO 0 "a_block" + OpMemberName %SSBO 1 "c_block" + OpDecorate %B1Array ArrayStride 16 + OpDecorate %B2Array ArrayStride 16 + OpMemberDecorate %B1 0 Offset 0 + OpMemberDecorate %A 0 Offset 0 + OpMemberDecorate %A 1 Offset 16 + OpMemberDecorate %A 2 Offset 32 + OpMemberDecorate %B2 0 Offset 0 + OpMemberDecorate %C 0 Offset 0 + OpMemberDecorate %C 1 Offset 16 + OpMemberDecorate %C 2 Offset 32 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 96 + OpDecorate %SSBO Block + OpDecorate %ssbo DescriptorSet 0 + OpDecorate %ssbo Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %v4float = OpTypeVector %float 4 + %B2 = OpTypeStruct %v4float + %B2Array = OpTypeArray %B2 %uint_4 + %C = OpTypeStruct %v4float %B2 %B2Array + %B1 = OpTypeStruct %v4float + %B1Array = OpTypeArray %B1 %uint_4 + %A = OpTypeStruct %v4float %B1 %B1Array + %SSBO = OpTypeStruct %A %C +%_ptr_Uniform_SSBO = OpTypePointer StorageBuffer %SSBO + %ssbo = OpVariable %_ptr_Uniform_SSBO StorageBuffer + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_C = OpTypePointer StorageBuffer %C + %int_0 = OpConstant %int 0 +%_ptr_Uniform_A = OpTypePointer StorageBuffer %A + %main = OpFunction %void None %3 + %5 = OpLabel + %22 = OpAccessChain %_ptr_Uniform_C %ssbo %int_1 + %39 = OpAccessChain %_ptr_Uniform_A %ssbo %int_0 + %23 = OpLoad %C %22 + %24 = OpCopyLogical %A %23 + OpStore %39 %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/extended-debug-extinst.invalid.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/extended-debug-extinst.invalid.asm.comp new file mode 100644 index 0000000..5b6a189 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/extended-debug-extinst.invalid.asm.comp @@ -0,0 +1,67 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 37 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "DebugInfo" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" %3 + OpExecutionMode %2 LocalSize 1 1 1 + %4 = OpString "negateInputs.comp" + %5 = OpString "negateInputs" + %6 = OpString "main" + %7 = OpString "" + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "gl_GlobalInvocationID" + OpDecorate %3 BuiltIn GlobalInvocationId + OpDecorate %8 BufferBlock + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 1 + OpDecorate %11 ArrayStride 4 + OpMemberDecorate %8 0 Offset 0 + OpLine %4 0 0 + %12 = OpTypeBool + %13 = OpTypeVoid + %14 = OpTypeFunction %13 + %15 = OpTypeInt 32 0 + %16 = OpTypeInt 32 1 + %17 = OpTypeFloat 32 + %18 = OpTypeVector %15 3 + %19 = OpTypeVector %17 3 + %20 = OpTypePointer Input %18 + %21 = OpTypePointer Uniform %16 + %22 = OpTypePointer Uniform %17 + %23 = OpTypeRuntimeArray %16 + %11 = OpTypeRuntimeArray %17 + %8 = OpTypeStruct %11 + %24 = OpTypePointer Uniform %8 + %9 = OpVariable %24 Uniform + %10 = OpVariable %24 Uniform + OpLine %4 0 1 + OpLine %5 1 0 + OpLine %4 1000 100000 + %3 = OpVariable %20 Input + %25 = OpConstant %16 0 + OpNoLine + OpLine %4 1 1 + %26 = OpExtInst %13 %1 DebugInfoNone + %27 = OpExtInst %13 %1 DebugTypeFunction %13 + %28 = OpExtInst %13 %1 DebugFunction %6 %27 %4 1 1 %4 %7 FlagIsDefinition|FlagPrototyped|FlagIsOptimized 1 %26 %26 + %2 = OpFunction %13 None %14 + %29 = OpLabel + %30 = OpExtInst %13 %1 DebugScope %28 + OpLine %4 1 1 + %31 = OpLoad %18 %3 + %32 = OpCompositeExtract %15 %31 0 + %33 = OpAccessChain %22 %9 %25 %32 + %34 = OpLoad %17 %33 + %35 = OpFNegate %17 %34 + %36 = OpAccessChain %22 %10 %25 %32 + OpStore %36 %35 + OpNoLine + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/glsl-signed-operations.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/glsl-signed-operations.asm.comp new file mode 100644 index 0000000..7da9f95 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/glsl-signed-operations.asm.comp @@ -0,0 +1,123 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 26 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "ints" + OpMemberName %SSBO 1 "uints" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 16 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + %SSBO = OpTypeStruct %v4int %v4uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4int = OpTypePointer Uniform %v4int + %int_1 = OpConstant %int 1 +%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint + %main = OpFunction %void None %3 + %5 = OpLabel + %ints_ptr = OpAccessChain %_ptr_Uniform_v4int %_ %int_0 + %uints_ptr = OpAccessChain %_ptr_Uniform_v4uint %_ %int_1 + %ints = OpLoad %v4int %ints_ptr + %uints = OpLoad %v4uint %uints_ptr + + %int_to_int_sabs = OpExtInst %v4int %1 SAbs %ints + %int_to_uint_sabs = OpExtInst %v4uint %1 SAbs %ints + %uint_to_int_sabs = OpExtInst %v4int %1 SAbs %uints + %uint_to_uint_sabs = OpExtInst %v4uint %1 SAbs %uints + + %int_to_int_ssign = OpExtInst %v4int %1 SSign %ints + %int_to_uint_ssign = OpExtInst %v4uint %1 SSign %ints + %uint_to_int_ssign = OpExtInst %v4int %1 SSign %uints + %uint_to_uint_ssign = OpExtInst %v4uint %1 SSign %uints + + %int_to_int_smsb = OpExtInst %v4int %1 FindSMsb %uints + %int_to_uint_smsb = OpExtInst %v4uint %1 FindSMsb %uints + %uint_to_int_umsb = OpExtInst %v4int %1 FindUMsb %ints + %uint_to_uint_umsb = OpExtInst %v4uint %1 FindUMsb %ints + + %int_to_int_smin = OpExtInst %v4int %1 SMin %ints %ints + %int_to_uint_smin = OpExtInst %v4uint %1 SMin %ints %uints + %uint_to_int_smin = OpExtInst %v4int %1 SMin %uints %uints + %uint_to_uint_smin = OpExtInst %v4uint %1 SMin %uints %ints + + %int_to_int_umin = OpExtInst %v4int %1 UMin %ints %uints + %int_to_uint_umin = OpExtInst %v4uint %1 UMin %ints %uints + %uint_to_int_umin = OpExtInst %v4int %1 UMin %uints %ints + %uint_to_uint_umin = OpExtInst %v4uint %1 UMin %uints %ints + + %int_to_int_smax = OpExtInst %v4int %1 SMax %ints %ints + %int_to_uint_smax = OpExtInst %v4uint %1 SMax %ints %ints + %uint_to_int_smax = OpExtInst %v4int %1 SMax %uints %ints + %uint_to_uint_smax = OpExtInst %v4uint %1 SMax %uints %ints + + %int_to_int_umax = OpExtInst %v4int %1 UMax %ints %uints + %int_to_uint_umax = OpExtInst %v4uint %1 UMax %ints %ints + %uint_to_int_umax = OpExtInst %v4int %1 UMax %uints %ints + %uint_to_uint_umax = OpExtInst %v4uint %1 UMax %uints %ints + + %int_to_int_sclamp = OpExtInst %v4int %1 SClamp %uints %uints %uints + %int_to_uint_sclamp = OpExtInst %v4uint %1 SClamp %uints %uints %uints + %uint_to_int_uclamp = OpExtInst %v4int %1 UClamp %ints %ints %ints + %uint_to_uint_uclamp = OpExtInst %v4uint %1 UClamp %ints %ints %ints + + OpStore %ints_ptr %int_to_int_sabs + OpStore %uints_ptr %int_to_uint_sabs + OpStore %ints_ptr %uint_to_int_sabs + OpStore %uints_ptr %uint_to_uint_sabs + + OpStore %ints_ptr %int_to_int_ssign + OpStore %uints_ptr %int_to_uint_ssign + OpStore %ints_ptr %uint_to_int_ssign + OpStore %uints_ptr %uint_to_uint_ssign + + OpStore %ints_ptr %int_to_int_smsb + OpStore %uints_ptr %int_to_uint_smsb + OpStore %ints_ptr %uint_to_int_umsb + OpStore %uints_ptr %uint_to_uint_umsb + + OpStore %ints_ptr %int_to_int_smin + OpStore %uints_ptr %int_to_uint_smin + OpStore %ints_ptr %uint_to_int_smin + OpStore %uints_ptr %uint_to_uint_smin + + OpStore %ints_ptr %int_to_int_umin + OpStore %uints_ptr %int_to_uint_umin + OpStore %ints_ptr %uint_to_int_umin + OpStore %uints_ptr %uint_to_uint_umin + + OpStore %ints_ptr %int_to_int_smax + OpStore %uints_ptr %int_to_uint_smax + OpStore %ints_ptr %uint_to_int_smax + OpStore %uints_ptr %uint_to_uint_smax + + OpStore %ints_ptr %int_to_int_umax + OpStore %uints_ptr %int_to_uint_umax + OpStore %ints_ptr %uint_to_int_umax + OpStore %uints_ptr %uint_to_uint_umax + + OpStore %ints_ptr %int_to_int_sclamp + OpStore %uints_ptr %int_to_uint_sclamp + OpStore %ints_ptr %uint_to_int_uclamp + OpStore %uints_ptr %uint_to_uint_uclamp + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp new file mode 100644 index 0000000..30db11d --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/glsl.std450.frexp-modf-struct.asm.comp @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %ResTypeMod = OpTypeStruct %float %float +%_ptr_Function_ResTypeMod = OpTypePointer Function %ResTypeMod + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_20 = OpConstant %float 20 + %int_1 = OpConstant %int 1 +%_ptr_Function_float = OpTypePointer Function %float +%ResTypeFrexp = OpTypeStruct %float %int +%_ptr_Function_ResTypeFrexp = OpTypePointer Function %ResTypeFrexp + %float_40 = OpConstant %float 40 +%_ptr_Function_int = OpTypePointer Function %int + %SSBO = OpTypeStruct %float %int +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_int = OpTypePointer Uniform %int + %main = OpFunction %void None %3 + %5 = OpLabel + %modres = OpExtInst %ResTypeMod %1 ModfStruct %float_20 + %frexpres = OpExtInst %ResTypeFrexp %1 FrexpStruct %float_40 + + %modres_f = OpCompositeExtract %float %modres 0 + %modres_i = OpCompositeExtract %float %modres 1 + %frexpres_f = OpCompositeExtract %float %frexpres 0 + %frexpres_i = OpCompositeExtract %int %frexpres 1 + + %float_ptr = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %int_ptr = OpAccessChain %_ptr_Uniform_int %_ %int_1 + + OpStore %float_ptr %modres_f + OpStore %float_ptr %modres_i + OpStore %float_ptr %frexpres_f + OpStore %int_ptr %frexpres_i + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/image-atomic-nonuniform.vk.nocompat.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/image-atomic-nonuniform.vk.nocompat.asm.comp new file mode 100644 index 0000000..5dad9dd --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/image-atomic-nonuniform.vk.nocompat.asm.comp @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 32 +; Schema: 0 + OpCapability Shader + OpCapability ShaderNonUniform + OpCapability RuntimeDescriptorArray + OpCapability StorageImageArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %uImage "uImage" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %uImage DescriptorSet 0 + OpDecorate %uImage Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %30 NonUniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %7 = OpTypeImage %uint 2D 0 0 0 2 R32ui +%_runtimearr_7 = OpTypeRuntimeArray %7 +%_ptr_UniformConstant__runtimearr_7 = OpTypePointer UniformConstant %_runtimearr_7 + %uImage = OpVariable %_ptr_UniformConstant__runtimearr_7 UniformConstant + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_2 = OpConstant %uint 2 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7 + %v2uint = OpTypeVector %uint 2 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 +%_ptr_Image_uint = OpTypePointer Image %uint + %main = OpFunction %void None %3 + %5 = OpLabel + %16 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_2 + %17 = OpLoad %uint %16 + %18 = OpCopyObject %uint %17 + %20 = OpAccessChain %_ptr_UniformConstant_7 %uImage %18 + %22 = OpLoad %v3uint %gl_GlobalInvocationID + %23 = OpVectorShuffle %v2uint %22 %22 0 1 + %26 = OpBitcast %v2int %23 + %30 = OpImageTexelPointer %_ptr_Image_uint %20 %26 %uint_0 + %31 = OpAtomicIAdd %uint %30 %uint_1 %uint_0 %uint_1 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/loop-variable-with-initializer.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/loop-variable-with-initializer.asm.comp new file mode 100644 index 0000000..f40377b --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/loop-variable-with-initializer.asm.comp @@ -0,0 +1,31 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos; 0 +; Bound: 62 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %i "i" + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %31 = OpConstantNull %uint + %main = OpFunction %void None %11 + %14 = OpLabel + %i = OpVariable %_ptr_Function_uint Function %31 + OpStore %i %uint_0 + OpBranch %32 + %32 = OpLabel + OpLoopMerge %33 %34 None + OpBranch %33 + %34 = OpLabel + %57 = OpLoad %uint %i + OpBranch %32 + %33 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/nonuniform-bracket-handling.vk.nocompat.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/nonuniform-bracket-handling.vk.nocompat.asm.comp new file mode 100644 index 0000000..3e36093 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/nonuniform-bracket-handling.vk.nocompat.asm.comp @@ -0,0 +1,298 @@ +; SPIR-V +; Version: 1.3 +; Generator: Unknown(30017); 21022 +; Bound: 233 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpCapability ImageQuery + OpCapability StorageImageWriteWithoutFormat + OpCapability GroupNonUniformBallot + OpCapability RuntimeDescriptorArray + OpCapability UniformTexelBufferArrayDynamicIndexing + OpCapability StorageTexelBufferArrayDynamicIndexing + OpCapability UniformTexelBufferArrayNonUniformIndexing + OpCapability StorageTexelBufferArrayNonUniformIndexing + OpCapability PhysicalStorageBufferAddresses + OpExtension "SPV_EXT_descriptor_indexing" + OpExtension "SPV_KHR_physical_storage_buffer" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %RootConstants "RootConstants" + OpName %registers "registers" + OpName %SSBO_Offsets "SSBO_Offsets" + OpDecorate %RootConstants Block + OpMemberDecorate %RootConstants 0 Offset 0 + OpMemberDecorate %RootConstants 1 Offset 4 + OpMemberDecorate %RootConstants 2 Offset 8 + OpMemberDecorate %RootConstants 3 Offset 12 + OpMemberDecorate %RootConstants 4 Offset 16 + OpMemberDecorate %RootConstants 5 Offset 20 + OpMemberDecorate %RootConstants 6 Offset 24 + OpMemberDecorate %RootConstants 7 Offset 28 + OpDecorate %_runtimearr_v2uint ArrayStride 8 + OpMemberDecorate %SSBO_Offsets 0 Offset 0 + OpDecorate %SSBO_Offsets Block + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 Binding 0 + OpDecorate %13 NonWritable + OpDecorate %13 Restrict + OpDecorate %18 DescriptorSet 1 + OpDecorate %18 Binding 0 + OpDecorate %22 DescriptorSet 4 + OpDecorate %22 Binding 0 + OpDecorate %26 DescriptorSet 4 + OpDecorate %26 Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %148 NonUniform + OpDecorate %149 NonUniform + OpDecorate %172 NonUniform + OpDecorate %173 NonUniform + OpDecorate %196 NonUniform + OpDecorate %197 NonUniform + OpDecorate %205 NonUniform + %void = OpTypeVoid + %2 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%RootConstants = OpTypeStruct %uint %uint %uint %uint %uint %uint %uint %uint +%_ptr_PushConstant_RootConstants = OpTypePointer PushConstant %RootConstants + %registers = OpVariable %_ptr_PushConstant_RootConstants PushConstant + %v2uint = OpTypeVector %uint 2 +%_runtimearr_v2uint = OpTypeRuntimeArray %v2uint +%SSBO_Offsets = OpTypeStruct %_runtimearr_v2uint +%_ptr_StorageBuffer_SSBO_Offsets = OpTypePointer StorageBuffer %SSBO_Offsets + %13 = OpVariable %_ptr_StorageBuffer_SSBO_Offsets StorageBuffer + %float = OpTypeFloat 32 + %15 = OpTypeImage %float Buffer 0 0 0 1 Unknown +%_runtimearr_15 = OpTypeRuntimeArray %15 +%_ptr_UniformConstant__runtimearr_15 = OpTypePointer UniformConstant %_runtimearr_15 + %18 = OpVariable %_ptr_UniformConstant__runtimearr_15 UniformConstant + %19 = OpTypeImage %float Buffer 0 0 0 2 R32f +%_runtimearr_19 = OpTypeRuntimeArray %19 +%_ptr_UniformConstant__runtimearr_19 = OpTypePointer UniformConstant %_runtimearr_19 + %22 = OpVariable %_ptr_UniformConstant__runtimearr_19 UniformConstant + %23 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%_runtimearr_23 = OpTypeRuntimeArray %23 +%_ptr_UniformConstant__runtimearr_23 = OpTypePointer UniformConstant %_runtimearr_23 + %26 = OpVariable %_ptr_UniformConstant__runtimearr_23 UniformConstant +%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint + %uint_4 = OpConstant %uint 4 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 +%_ptr_StorageBuffer_v2uint = OpTypePointer StorageBuffer %v2uint + %uint_0 = OpConstant %uint 0 +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%_ptr_Input_uint = OpTypePointer Input %uint + %bool = OpTypeBool +%uint_4294967295 = OpConstant %uint 4294967295 + %v4float = OpTypeVector %float 4 + %uint_1024 = OpConstant %uint 1024 + %uint_2048 = OpConstant %uint 2048 +%_ptr_Image_uint = OpTypePointer Image %uint + %uint_40 = OpConstant %uint 40 + %uint_50 = OpConstant %uint 50 + %uint_70 = OpConstant %uint 70 + %main = OpFunction %void None %2 + %4 = OpLabel + OpBranch %231 + %231 = OpLabel + %30 = OpAccessChain %_ptr_PushConstant_uint %registers %uint_4 + %32 = OpLoad %uint %30 + %33 = OpIAdd %uint %32 %uint_2 + %28 = OpAccessChain %_ptr_UniformConstant_23 %26 %33 + %35 = OpLoad %23 %28 + %36 = OpGroupNonUniformBroadcastFirst %uint %uint_3 %33 + %39 = OpAccessChain %_ptr_StorageBuffer_v2uint %13 %uint_0 %36 + %41 = OpLoad %v2uint %39 + %44 = OpAccessChain %_ptr_PushConstant_uint %registers %uint_4 + %45 = OpLoad %uint %44 + %43 = OpAccessChain %_ptr_UniformConstant_19 %22 %45 + %46 = OpLoad %19 %43 + %47 = OpGroupNonUniformBroadcastFirst %uint %uint_3 %45 + %48 = OpAccessChain %_ptr_StorageBuffer_v2uint %13 %uint_0 %47 + %49 = OpLoad %v2uint %48 + %52 = OpAccessChain %_ptr_PushConstant_uint %registers %uint_1 + %54 = OpLoad %uint %52 + %55 = OpIAdd %uint %54 %uint_1 + %51 = OpAccessChain %_ptr_UniformConstant_15 %18 %55 + %56 = OpLoad %15 %51 + %57 = OpGroupNonUniformBroadcastFirst %uint %uint_3 %55 + %58 = OpAccessChain %_ptr_StorageBuffer_v2uint %13 %uint_0 %57 + %59 = OpLoad %v2uint %58 + %64 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %65 = OpLoad %uint %64 + %66 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_1 + %67 = OpLoad %uint %66 + %68 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_2 + %69 = OpLoad %uint %68 + %70 = OpIAdd %uint %65 %uint_4 + %71 = OpCompositeExtract %uint %49 0 + %72 = OpCompositeExtract %uint %49 1 + %73 = OpIAdd %uint %70 %71 + %75 = OpULessThan %bool %70 %72 + %76 = OpSelect %uint %75 %73 %uint_4294967295 + %79 = OpImageRead %v4float %46 %76 + %80 = OpCompositeExtract %float %79 0 + %81 = OpCompositeExtract %float %79 1 + %82 = OpCompositeExtract %float %79 2 + %83 = OpCompositeExtract %float %79 3 + %84 = OpIAdd %uint %65 %uint_1024 + %86 = OpCompositeExtract %uint %49 0 + %87 = OpCompositeExtract %uint %49 1 + %88 = OpIAdd %uint %84 %86 + %89 = OpULessThan %bool %84 %87 + %90 = OpSelect %uint %89 %88 %uint_4294967295 + %91 = OpCompositeConstruct %v4float %80 %81 %82 %83 + OpImageWrite %46 %90 %91 + %92 = OpIAdd %uint %65 %uint_2 + %93 = OpCompositeExtract %uint %59 0 + %94 = OpCompositeExtract %uint %59 1 + %95 = OpIAdd %uint %92 %93 + %96 = OpULessThan %bool %92 %94 + %97 = OpSelect %uint %96 %95 %uint_4294967295 + %98 = OpImageFetch %v4float %56 %97 + %99 = OpCompositeExtract %float %98 0 + %100 = OpCompositeExtract %float %98 1 + %101 = OpCompositeExtract %float %98 2 + %102 = OpCompositeExtract %float %98 3 + %103 = OpIAdd %uint %65 %uint_2048 + %105 = OpCompositeExtract %uint %49 0 + %106 = OpCompositeExtract %uint %49 1 + %107 = OpIAdd %uint %103 %105 + %108 = OpULessThan %bool %103 %106 + %109 = OpSelect %uint %108 %107 %uint_4294967295 + %110 = OpCompositeConstruct %v4float %99 %100 %101 %102 + OpImageWrite %46 %109 %110 + %111 = OpCompositeExtract %uint %41 0 + %112 = OpCompositeExtract %uint %41 1 + %113 = OpIAdd %uint %65 %111 + %114 = OpULessThan %bool %65 %112 + %115 = OpSelect %uint %114 %113 %uint_4294967295 + %117 = OpImageTexelPointer %_ptr_Image_uint %28 %115 %uint_0 + %118 = OpAtomicIAdd %uint %117 %uint_1 %uint_0 %uint_40 + %120 = OpCompositeExtract %uint %41 0 + %121 = OpCompositeExtract %uint %41 1 + %122 = OpIAdd %uint %67 %120 + %123 = OpULessThan %bool %67 %121 + %124 = OpSelect %uint %123 %122 %uint_4294967295 + %125 = OpImageTexelPointer %_ptr_Image_uint %28 %124 %uint_0 + %126 = OpAtomicCompareExchange %uint %125 %uint_1 %uint_0 %uint_0 %uint_50 %uint_40 + %128 = OpCompositeExtract %uint %49 1 + %129 = OpConvertUToF %float %128 + %130 = OpCompositeExtract %uint %49 0 + %131 = OpCompositeExtract %uint %49 1 + %132 = OpIAdd %uint %uint_0 %130 + %133 = OpULessThan %bool %uint_0 %131 + %134 = OpSelect %uint %133 %132 %uint_4294967295 + %135 = OpCompositeConstruct %v4float %129 %129 %129 %129 + OpImageWrite %46 %134 %135 + %136 = OpCompositeExtract %uint %59 1 + %137 = OpConvertUToF %float %136 + %138 = OpCompositeExtract %uint %49 0 + %139 = OpCompositeExtract %uint %49 1 + %140 = OpIAdd %uint %uint_1 %138 + %141 = OpULessThan %bool %uint_1 %139 + %142 = OpSelect %uint %141 %140 %uint_4294967295 + %143 = OpCompositeConstruct %v4float %137 %137 %137 %137 + OpImageWrite %46 %142 %143 + %144 = OpIAdd %uint %69 %uint_0 + %146 = OpAccessChain %_ptr_PushConstant_uint %registers %uint_4 + %147 = OpLoad %uint %146 + %148 = OpIAdd %uint %147 %144 + %145 = OpAccessChain %_ptr_UniformConstant_19 %22 %148 + %149 = OpLoad %19 %145 + %150 = OpAccessChain %_ptr_StorageBuffer_v2uint %13 %uint_0 %148 + %151 = OpLoad %v2uint %150 + %152 = OpCompositeExtract %uint %151 0 + %153 = OpCompositeExtract %uint %151 1 + %154 = OpIAdd %uint %70 %152 + %155 = OpULessThan %bool %70 %153 + %156 = OpSelect %uint %155 %154 %uint_4294967295 + %157 = OpImageRead %v4float %149 %156 + %158 = OpCompositeExtract %float %157 0 + %159 = OpCompositeExtract %float %157 1 + %160 = OpCompositeExtract %float %157 2 + %161 = OpCompositeExtract %float %157 3 + %162 = OpCompositeExtract %uint %151 0 + %163 = OpCompositeExtract %uint %151 1 + %164 = OpIAdd %uint %84 %162 + %165 = OpULessThan %bool %84 %163 + %166 = OpSelect %uint %165 %164 %uint_4294967295 + %167 = OpCompositeConstruct %v4float %158 %159 %160 %161 + OpImageWrite %149 %166 %167 + %168 = OpIAdd %uint %69 %uint_0 + %170 = OpAccessChain %_ptr_PushConstant_uint %registers %uint_1 + %171 = OpLoad %uint %170 + %172 = OpIAdd %uint %171 %168 + %169 = OpAccessChain %_ptr_UniformConstant_15 %18 %172 + %173 = OpLoad %15 %169 + %174 = OpAccessChain %_ptr_StorageBuffer_v2uint %13 %uint_0 %172 + %175 = OpLoad %v2uint %174 + %176 = OpCompositeExtract %uint %175 0 + %177 = OpCompositeExtract %uint %175 1 + %178 = OpIAdd %uint %70 %176 + %179 = OpULessThan %bool %70 %177 + %180 = OpSelect %uint %179 %178 %uint_4294967295 + %181 = OpImageFetch %v4float %173 %180 + %182 = OpCompositeExtract %float %181 0 + %183 = OpCompositeExtract %float %181 1 + %184 = OpCompositeExtract %float %181 2 + %185 = OpCompositeExtract %float %181 3 + %186 = OpCompositeExtract %uint %151 0 + %187 = OpCompositeExtract %uint %151 1 + %188 = OpIAdd %uint %103 %186 + %189 = OpULessThan %bool %103 %187 + %190 = OpSelect %uint %189 %188 %uint_4294967295 + %191 = OpCompositeConstruct %v4float %182 %183 %184 %185 + OpImageWrite %149 %190 %191 + %192 = OpIAdd %uint %69 %uint_0 + %194 = OpAccessChain %_ptr_PushConstant_uint %registers %uint_4 + %195 = OpLoad %uint %194 + %196 = OpIAdd %uint %195 %192 + %193 = OpAccessChain %_ptr_UniformConstant_23 %26 %196 + %197 = OpLoad %23 %193 + %198 = OpAccessChain %_ptr_StorageBuffer_v2uint %13 %uint_0 %196 + %199 = OpLoad %v2uint %198 + %200 = OpCompositeExtract %uint %199 0 + %201 = OpCompositeExtract %uint %199 1 + %202 = OpIAdd %uint %67 %200 + %203 = OpULessThan %bool %67 %201 + %204 = OpSelect %uint %203 %202 %uint_4294967295 + %205 = OpImageTexelPointer %_ptr_Image_uint %193 %204 %uint_0 + %206 = OpAtomicIAdd %uint %205 %uint_1 %uint_0 %uint_40 + %207 = OpCompositeExtract %uint %199 0 + %208 = OpCompositeExtract %uint %199 1 + %209 = OpIAdd %uint %67 %207 + %210 = OpULessThan %bool %67 %208 + %211 = OpSelect %uint %210 %209 %uint_4294967295 + %212 = OpImageTexelPointer %_ptr_Image_uint %193 %211 %uint_0 + %213 = OpAtomicCompareExchange %uint %212 %uint_1 %uint_0 %uint_0 %uint_70 %uint_40 + %215 = OpCompositeExtract %uint %151 1 + %216 = OpConvertUToF %float %215 + %217 = OpCompositeExtract %uint %49 0 + %218 = OpCompositeExtract %uint %49 1 + %219 = OpIAdd %uint %uint_2 %217 + %220 = OpULessThan %bool %uint_2 %218 + %221 = OpSelect %uint %220 %219 %uint_4294967295 + %222 = OpCompositeConstruct %v4float %216 %216 %216 %216 + OpImageWrite %46 %221 %222 + %223 = OpCompositeExtract %uint %175 1 + %224 = OpConvertUToF %float %223 + %225 = OpCompositeExtract %uint %49 0 + %226 = OpCompositeExtract %uint %49 1 + %227 = OpIAdd %uint %uint_3 %225 + %228 = OpULessThan %bool %uint_3 %226 + %229 = OpSelect %uint %228 %227 %uint_4294967295 + %230 = OpCompositeConstruct %v4float %224 %224 %224 %224 + OpImageWrite %46 %229 %230 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/phi-temporary-copy-loop-variable.asm.invalid.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/phi-temporary-copy-loop-variable.asm.invalid.comp new file mode 100644 index 0000000..6115912 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/phi-temporary-copy-loop-variable.asm.invalid.comp @@ -0,0 +1,68 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 42 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %cs_test "main" %gl_GlobalInvocationID %gl_LocalInvocationIndex + OpExecutionMode %cs_test LocalSize 8 8 1 + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %outImageTexture "outImageTexture" + OpName %cs_test "cs_test" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex + OpDecorate %outImageTexture DescriptorSet 0 + OpDecorate %outImageTexture Binding 1 + %float = OpTypeFloat 32 + %float_5 = OpConstant %float 5 + %float_1 = OpConstant %float 1 + %int = OpTypeInt 32 1 + %int_7 = OpConstant %int 7 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%type_2d_image = OpTypeImage %float 2D 2 0 0 2 Rgba32f +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Input_uint = OpTypePointer Input %uint + %void = OpTypeVoid + %19 = OpTypeFunction %void + %v2uint = OpTypeVector %uint 2 + %v4float = OpTypeVector %float 4 + %bool = OpTypeBool +%outImageTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input + %cs_test = OpFunction %void None %19 + %23 = OpLabel + %24 = OpLoad %v3uint %gl_GlobalInvocationID + %25 = OpVectorShuffle %v2uint %24 %24 0 1 + OpBranch %26 + %26 = OpLabel + %27 = OpPhi %int %int_7 %23 %28 %29 + %30 = OpPhi %int %int_7 %23 %27 %29 + %31 = OpSGreaterThanEqual %bool %27 %int_0 + OpLoopMerge %32 %29 None + OpBranchConditional %31 %33 %32 + %33 = OpLabel + %34 = OpConvertSToF %float %27 + %35 = OpFOrdGreaterThan %bool %float_5 %34 + OpSelectionMerge %29 None + OpBranchConditional %35 %36 %29 + %36 = OpLabel + OpBranch %32 + %29 = OpLabel + %28 = OpISub %int %27 %int_1 + OpBranch %26 + %32 = OpLabel + %37 = OpISub %int %30 %int_1 + %38 = OpConvertSToF %float %37 + %39 = OpConvertSToF %float %30 + %40 = OpCompositeConstruct %v4float %38 %39 %float_1 %float_1 + %41 = OpLoad %type_2d_image %outImageTexture + OpImageWrite %41 %25 %40 None + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp new file mode 100644 index 0000000..b7ca114 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp @@ -0,0 +1,63 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_ARB_gpu_shader_int64" + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "s64" + OpMemberName %SSBO 1 "u64" + OpName %_ "" + OpName %ConstantInt "ConstantInt" + OpName %ConstantInt64_1 "ConstantInt64_1" + OpName %ConstantUint "ConstantUint" + OpName %ConstantInt64_2 "ConstantInt64_2" + OpName %ConstantUint64_1 "ConstantUint64_1" + OpName %ConstantUint64_2 "ConstantUint64_2" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %ConstantInt SpecId 0 + OpDecorate %ConstantUint SpecId 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %long = OpTypeInt 64 1 + %ulong = OpTypeInt 64 0 + %SSBO = OpTypeStruct %int %uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int_0 = OpConstant %int 0 + %ulong_0 = OpConstant %ulong 0 +%ConstantInt = OpSpecConstant %int 1 +%ConstantUint = OpSpecConstant %uint 2 +%ConstantInt64_1 = OpSpecConstantOp %long SConvert %ConstantInt +%ConstantInt64_2 = OpSpecConstantOp %long SConvert %ConstantUint +%ConstantUint64_1 = OpSpecConstantOp %ulong SConvert %ConstantInt +%ConstantUint64_2 = OpSpecConstantOp %ulong SConvert %ConstantUint + %added_long = OpSpecConstantOp %long IAdd %ConstantInt64_1 %ConstantInt64_2 + %added_ulong = OpSpecConstantOp %ulong IAdd %ConstantUint64_1 %ConstantUint64_2 + %trunc_long = OpSpecConstantOp %int SConvert %added_long + %trunc_ulong = OpSpecConstantOp %uint SConvert %added_ulong +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_1 = OpConstant %int 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %main = OpFunction %void None %3 + %5 = OpLabel + %22 = OpAccessChain %_ptr_Uniform_int %_ %int_0 + OpStore %22 %trunc_long + %29 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 + OpStore %29 %trunc_ulong + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp new file mode 100644 index 0000000..edb1a05 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/comp/storage-buffer-basic.invalid.asm.comp @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Codeplay; 0 +; Bound: 31 +; Schema: 0 + OpCapability Shader + OpCapability VariablePointers + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_KHR_variable_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %22 "main" %gl_WorkGroupID + OpSource OpenCL_C 120 + OpDecorate %15 SpecId 0 + ;OpDecorate %16 SpecId 1 + OpDecorate %17 SpecId 2 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_4 0 Offset 0 + OpDecorate %_struct_4 Block + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + OpDecorate %20 DescriptorSet 0 + OpDecorate %20 Binding 0 + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + %float = OpTypeFloat 32 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_4 = OpTypeStruct %_runtimearr_float +%_ptr_StorageBuffer__struct_4 = OpTypePointer StorageBuffer %_struct_4 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %8 = OpTypeFunction %void + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Private_v3uint = OpTypePointer Private %v3uint + %uint_0 = OpConstant %uint 0 +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %15 = OpSpecConstant %uint 1 + %16 = OpConstant %uint 2 + %17 = OpSpecConstant %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %15 %16 %17 + %19 = OpVariable %_ptr_Private_v3uint Private %gl_WorkGroupSize + %20 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer + %21 = OpVariable %_ptr_StorageBuffer__struct_4 StorageBuffer + %22 = OpFunction %void None %8 + %23 = OpLabel + %24 = OpAccessChain %_ptr_Input_uint %gl_WorkGroupID %uint_0 + %25 = OpLoad %uint %24 + %26 = OpAccessChain %_ptr_StorageBuffer_float %21 %uint_0 %25 + %27 = OpLoad %float %26 + %28 = OpAccessChain %_ptr_StorageBuffer_float %20 %uint_0 %25 + %29 = OpLoad %float %28 + %30 = OpFAdd %float %27 %29 + OpStore %28 %30 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/combined-image-sampler-dxc-min16float.asm.invalid.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/combined-image-sampler-dxc-min16float.asm.invalid.frag new file mode 100644 index 0000000..dda2f02 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/combined-image-sampler-dxc-min16float.asm.invalid.frag @@ -0,0 +1,95 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 48 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "main" %in_var_COLOR %in_var_TEXCOORD0 %out_var_SV_TARGET + OpExecutionMode %PSMain OriginUpperLeft + ; Not actually ESSL, but makes testing easier. + OpSource ESSL 310 + OpName %type_2d_image "type.2d.image" + OpName %tex "tex" + OpName %type_sampler "type.sampler" + OpName %Samp "Samp" + OpName %in_var_COLOR "in.var.COLOR" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PSMain "PSMain" + OpName %PSInput "PSInput" + OpMemberName %PSInput 0 "color" + OpMemberName %PSInput 1 "uv" + OpName %param_var_input "param.var.input" + OpName %src_PSMain "src.PSMain" + OpName %input "input" + OpName %bb_entry "bb.entry" + OpName %a "a" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %in_var_TEXCOORD0 Location 1 + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %tex DescriptorSet 0 + OpDecorate %tex Binding 0 + OpDecorate %Samp DescriptorSet 0 + OpDecorate %Samp Binding 1 + OpDecorate %tex RelaxedPrecision + OpDecorate %a RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %21 = OpTypeFunction %void + %PSInput = OpTypeStruct %v4float %v2float +%_ptr_Function_PSInput = OpTypePointer Function %PSInput + %31 = OpTypeFunction %v4float %_ptr_Function_PSInput +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image + %tex = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %Samp = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %PSMain = OpFunction %void None %21 + %22 = OpLabel +%param_var_input = OpVariable %_ptr_Function_PSInput Function + %26 = OpLoad %v4float %in_var_COLOR + %27 = OpLoad %v2float %in_var_TEXCOORD0 + %28 = OpCompositeConstruct %PSInput %26 %27 + OpStore %param_var_input %28 + %29 = OpFunctionCall %v4float %src_PSMain %param_var_input + OpStore %out_var_SV_TARGET %29 + OpReturn + OpFunctionEnd + %src_PSMain = OpFunction %v4float None %31 + %input = OpFunctionParameter %_ptr_Function_PSInput + %bb_entry = OpLabel + %a = OpVariable %_ptr_Function_v4float Function + %36 = OpAccessChain %_ptr_Function_v4float %input %int_0 + %37 = OpLoad %v4float %36 + %38 = OpLoad %type_2d_image %tex + %39 = OpLoad %type_sampler %Samp + %41 = OpAccessChain %_ptr_Function_v2float %input %int_1 + %42 = OpLoad %v2float %41 + %44 = OpSampledImage %type_sampled_image %38 %39 + %45 = OpImageSampleImplicitLod %v4float %44 %42 None + %46 = OpFMul %v4float %37 %45 + OpStore %a %46 + %47 = OpLoad %v4float %a + OpReturnValue %47 + OpFunctionEnd + diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/demote-impure-function-call.vk.nocompat.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/demote-impure-function-call.vk.nocompat.asm.frag new file mode 100644 index 0000000..9f1a457 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/demote-impure-function-call.vk.nocompat.asm.frag @@ -0,0 +1,63 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 33 +; Schema: 0 + OpCapability Shader + OpCapability DemoteToHelperInvocationEXT + OpExtension "SPV_EXT_demote_to_helper_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vA %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_demote_to_helper_invocation" + OpName %main "main" + OpName %foobar_i1_ "foobar(i1;" + OpName %a "a" + OpName %a_0 "a" + OpName %vA "vA" + OpName %param "param" + OpName %FragColor "FragColor" + OpDecorate %vA Flat + OpDecorate %vA Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %float_10 = OpConstant %float 10 + %21 = OpConstantComposite %v4float %float_10 %float_10 %float_10 %float_10 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_int = OpTypePointer Input %int + %vA = OpVariable %_ptr_Input_int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %a_0 = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_int Function + %29 = OpLoad %int %vA + OpStore %param %29 + %30 = OpFunctionCall %v4float %foobar_i1_ %param + OpStore %FragColor %21 + OpReturn + OpFunctionEnd + %foobar_i1_ = OpFunction %v4float None %10 + %a = OpFunctionParameter %_ptr_Function_int + %13 = OpLabel + %14 = OpLoad %int %a + %17 = OpSLessThan %bool %14 %int_0 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %19 + %18 = OpLabel + OpDemoteToHelperInvocationEXT + OpBranch %19 + %19 = OpLabel + OpReturnValue %21 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/discard-impure-function-call.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/discard-impure-function-call.asm.frag new file mode 100644 index 0000000..0f03916 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/discard-impure-function-call.asm.frag @@ -0,0 +1,59 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 34 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vA %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %foobar_i1_ "foobar(i1;" + OpName %a "a" + OpName %a_0 "a" + OpName %vA "vA" + OpName %param "param" + OpName %FragColor "FragColor" + OpDecorate %vA Flat + OpDecorate %vA Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %float_10 = OpConstant %float 10 + %22 = OpConstantComposite %v4float %float_10 %float_10 %float_10 %float_10 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_int = OpTypePointer Input %int + %vA = OpVariable %_ptr_Input_int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %a_0 = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_int Function + %30 = OpLoad %int %vA + OpStore %param %30 + %31 = OpFunctionCall %v4float %foobar_i1_ %param + OpStore %FragColor %22 + OpReturn + OpFunctionEnd + %foobar_i1_ = OpFunction %v4float None %10 + %a = OpFunctionParameter %_ptr_Function_int + %13 = OpLabel + %14 = OpLoad %int %a + %17 = OpSLessThan %bool %14 %int_0 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %19 + %18 = OpLabel + OpKill + %19 = OpLabel + OpReturnValue %22 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/do-while-continue-phi.asm.invalid.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/do-while-continue-phi.asm.invalid.frag new file mode 100644 index 0000000..97400df --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/do-while-continue-phi.asm.invalid.frag @@ -0,0 +1,64 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 42 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %_GLF_color + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %gl_FragCoord "gl_FragCoord" + OpName %_GLF_color "_GLF_color" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_GLF_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %bool = OpTypeBool + %false = OpConstantFalse %bool +%_ptr_Output_v4float = OpTypePointer Output %v4float + %_GLF_color = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %31 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %true = OpConstantTrue %bool + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %33 + %33 = OpLabel + OpLoopMerge %32 %35 None + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %24 None + OpBranch %7 + %7 = OpLabel + %17 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %18 = OpLoad %float %17 + %22 = OpFOrdNotEqual %bool %18 %18 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + OpBranch %8 + %24 = OpLabel + OpBranchConditional %false %6 %8 + %8 = OpLabel + %41 = OpPhi %bool %true %23 %false %24 + OpSelectionMerge %39 None + OpBranchConditional %41 %32 %39 + %39 = OpLabel + OpStore %_GLF_color %31 + OpBranch %32 + %35 = OpLabel + OpBranch %33 + %32 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/do-while-loop-inverted-test.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/do-while-loop-inverted-test.asm.frag new file mode 100644 index 0000000..93a39cf --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/do-while-loop-inverted-test.asm.frag @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %i "i" + OpName %j "j" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_20 = OpConstant %int 20 + %bool = OpTypeBool + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + OpStore %i %int_0 + OpStore %j %int_0 + OpBranch %11 + %11 = OpLabel + OpLoopMerge %13 %14 None + OpBranch %12 + %12 = OpLabel + %15 = OpLoad %int %j + %16 = OpLoad %int %i + %17 = OpIAdd %int %15 %16 + %19 = OpIAdd %int %17 %int_1 + %20 = OpLoad %int %j + %21 = OpIMul %int %19 %20 + OpStore %j %21 + %22 = OpLoad %int %i + %23 = OpIAdd %int %22 %int_1 + OpStore %i %23 + OpBranch %14 + %14 = OpLabel + %24 = OpLoad %int %i + %27 = OpIEqual %bool %24 %int_20 + OpBranchConditional %27 %13 %11 + %13 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/early-conditional-return-switch.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/early-conditional-return-switch.asm.frag new file mode 100644 index 0000000..d789ce3 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/early-conditional-return-switch.asm.frag @@ -0,0 +1,133 @@ +; SPIR-V +; Version: 1.3 +; Generator: Google spiregg; 0 +; Bound: 81 +; Schema: 0 + OpCapability Shader + OpCapability Sampled1D + OpCapability Image1D + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PsTextureLoadArray "main" %gl_FragCoord %out_var_SV_TARGET + OpExecutionMode %PsTextureLoadArray OriginUpperLeft + OpSource HLSL 500 + OpName %type_2d_image "type.2d.image" + OpName %type_gCBuffarrayIndex "type.gCBuffarrayIndex" + OpMemberName %type_gCBuffarrayIndex 0 "gArrayIndex" + OpName %gCBuffarrayIndex "gCBuffarrayIndex" + OpName %g_textureArray0 "g_textureArray0" + OpName %g_textureArray1 "g_textureArray1" + OpName %g_textureArray2 "g_textureArray2" + OpName %g_textureArray3 "g_textureArray3" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %PsTextureLoadArray "PsTextureLoadArray" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %out_var_SV_TARGET Location 0 + OpDecorate %gCBuffarrayIndex DescriptorSet 0 + OpDecorate %gCBuffarrayIndex Binding 0 + OpDecorate %g_textureArray0 DescriptorSet 0 + OpDecorate %g_textureArray0 Binding 0 + OpDecorate %g_textureArray1 DescriptorSet 0 + OpDecorate %g_textureArray1 Binding 1 + OpDecorate %g_textureArray2 DescriptorSet 0 + OpDecorate %g_textureArray2 Binding 2 + OpDecorate %g_textureArray3 DescriptorSet 0 + OpDecorate %g_textureArray3 Binding 3 + OpMemberDecorate %type_gCBuffarrayIndex 0 Offset 0 + OpDecorate %type_gCBuffarrayIndex Block + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %v4float = OpTypeVector %float 4 + %18 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_gCBuffarrayIndex = OpTypeStruct %uint +%_ptr_Uniform_type_gCBuffarrayIndex = OpTypePointer Uniform %type_gCBuffarrayIndex +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %24 = OpTypeFunction %void +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %v3int = OpTypeVector %int 3 + %v2int = OpTypeVector %int 2 +%gCBuffarrayIndex = OpVariable %_ptr_Uniform_type_gCBuffarrayIndex Uniform +%g_textureArray0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%g_textureArray1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%g_textureArray2 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%g_textureArray3 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool + %false = OpConstantFalse %bool + %true = OpConstantTrue %bool + %32 = OpUndef %v4float +%PsTextureLoadArray = OpFunction %void None %24 + %33 = OpLabel + %34 = OpLoad %v4float %gl_FragCoord + OpSelectionMerge %35 None + OpSwitch %uint_0 %36 + %36 = OpLabel + %37 = OpAccessChain %_ptr_Uniform_uint %gCBuffarrayIndex %int_0 + %38 = OpLoad %uint %37 + OpSelectionMerge %39 None + OpSwitch %38 %40 0 %41 1 %42 2 %43 3 %44 + %41 = OpLabel + %45 = OpCompositeExtract %float %34 0 + %46 = OpCompositeExtract %float %34 1 + %47 = OpConvertFToS %int %45 + %48 = OpConvertFToS %int %46 + %49 = OpCompositeConstruct %v3int %47 %48 %int_0 + %50 = OpVectorShuffle %v2int %49 %49 0 1 + %51 = OpLoad %type_2d_image %g_textureArray0 + %52 = OpImageFetch %v4float %51 %50 Lod %int_0 + OpBranch %39 + %42 = OpLabel + %53 = OpCompositeExtract %float %34 0 + %54 = OpCompositeExtract %float %34 1 + %55 = OpConvertFToS %int %53 + %56 = OpConvertFToS %int %54 + %57 = OpCompositeConstruct %v3int %55 %56 %int_0 + %58 = OpVectorShuffle %v2int %57 %57 0 1 + %59 = OpLoad %type_2d_image %g_textureArray1 + %60 = OpImageFetch %v4float %59 %58 Lod %int_0 + OpBranch %39 + %43 = OpLabel + %61 = OpCompositeExtract %float %34 0 + %62 = OpCompositeExtract %float %34 1 + %63 = OpConvertFToS %int %61 + %64 = OpConvertFToS %int %62 + %65 = OpCompositeConstruct %v3int %63 %64 %int_0 + %66 = OpVectorShuffle %v2int %65 %65 0 1 + %67 = OpLoad %type_2d_image %g_textureArray2 + %68 = OpImageFetch %v4float %67 %66 Lod %int_0 + OpBranch %39 + %44 = OpLabel + %69 = OpCompositeExtract %float %34 0 + %70 = OpCompositeExtract %float %34 1 + %71 = OpConvertFToS %int %69 + %72 = OpConvertFToS %int %70 + %73 = OpCompositeConstruct %v3int %71 %72 %int_0 + %74 = OpVectorShuffle %v2int %73 %73 0 1 + %75 = OpLoad %type_2d_image %g_textureArray3 + %76 = OpImageFetch %v4float %75 %74 Lod %int_0 + OpBranch %39 + %40 = OpLabel + OpBranch %39 + %39 = OpLabel + %77 = OpPhi %v4float %52 %41 %60 %42 %68 %43 %76 %44 %32 %40 + %78 = OpPhi %bool %true %41 %true %42 %true %43 %true %44 %false %40 + OpSelectionMerge %79 None + OpBranchConditional %78 %35 %79 + %79 = OpLabel + OpBranch %35 + %35 = OpLabel + %80 = OpPhi %v4float %77 %39 %18 %79 + OpStore %out_var_SV_TARGET %80 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/empty-struct-in-struct.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/empty-struct-in-struct.asm.frag new file mode 100644 index 0000000..a9650dd --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/empty-struct-in-struct.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.2 +; Generator: Khronos; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %EntryPoint_Main "main" + OpExecutionMode %EntryPoint_Main OriginUpperLeft + OpSource Unknown 100 + OpName %EmptyStructTest "EmptyStructTest" + OpName %EmptyStruct2Test "EmptyStruct2Test" + OpName %GetValue "GetValue" + OpName %GetValue2 "GetValue" + OpName %self "self" + OpName %self2 "self" + OpName %emptyStruct "emptyStruct" + OpName %value "value" + OpName %EntryPoint_Main "EntryPoint_Main" + +%EmptyStructTest = OpTypeStruct +%EmptyStruct2Test = OpTypeStruct %EmptyStructTest +%_ptr_Function_EmptyStruct2Test = OpTypePointer Function %EmptyStruct2Test + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %5 = OpTypeFunction %float %_ptr_Function_EmptyStruct2Test + %6 = OpTypeFunction %float %EmptyStruct2Test + %void = OpTypeVoid +%_ptr_Function_void = OpTypePointer Function %void + %8 = OpTypeFunction %void %_ptr_Function_EmptyStruct2Test + %9 = OpTypeFunction %void + %float_0 = OpConstant %float 0 + %value4 = OpConstantNull %EmptyStruct2Test + + %GetValue = OpFunction %float None %5 + %self = OpFunctionParameter %_ptr_Function_EmptyStruct2Test + %13 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + + %GetValue2 = OpFunction %float None %6 + %self2 = OpFunctionParameter %EmptyStruct2Test + %14 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + +%EntryPoint_Main = OpFunction %void None %9 + %37 = OpLabel + %emptyStruct = OpVariable %_ptr_Function_EmptyStruct2Test Function + %18 = OpVariable %_ptr_Function_EmptyStruct2Test Function + %value = OpVariable %_ptr_Function_float Function + %value2 = OpCompositeConstruct %EmptyStructTest + %value3 = OpCompositeConstruct %EmptyStruct2Test %value2 + %22 = OpFunctionCall %float %GetValue %emptyStruct + %23 = OpFunctionCall %float %GetValue2 %value3 + %24 = OpFunctionCall %float %GetValue2 %value4 + OpStore %value %22 + OpStore %value %23 + OpStore %value %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-inverted.asm.invalid.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-inverted.asm.invalid.frag new file mode 100644 index 0000000..1e67b38 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-inverted.asm.invalid.frag @@ -0,0 +1,37 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + %10 = OpPhi %int %12 %7 %int_0 %5 + OpLoopMerge %6 %7 None + OpBranch %11 + %11 = OpLabel + %16 = OpIEqual %bool %10 %int_16 + OpBranchConditional %16 %18 %19 + %18 = OpLabel + OpBranch %6 + %19 = OpLabel + OpBranch %17 + %17 = OpLabel + %21 = OpIAdd %int %10 %int_1 + OpBranch %7 + %7 = OpLabel + %12 = OpPhi %int %21 %17 + OpBranch %8 + %6 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-non-inverted.asm.invalid.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-non-inverted.asm.invalid.frag new file mode 100644 index 0000000..22c6e55 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-dedicated-merge-block-non-inverted.asm.invalid.frag @@ -0,0 +1,37 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + %10 = OpPhi %int %12 %7 %int_0 %5 + OpLoopMerge %6 %7 None + OpBranch %11 + %11 = OpLabel + %16 = OpINotEqual %bool %10 %int_16 + OpBranchConditional %16 %19 %18 + %18 = OpLabel + OpBranch %6 + %19 = OpLabel + OpBranch %17 + %17 = OpLabel + %21 = OpIAdd %int %10 %int_1 + OpBranch %7 + %7 = OpLabel + %12 = OpPhi %int %21 %17 + OpBranch %8 + %6 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-inverted-test.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-inverted-test.asm.frag new file mode 100644 index 0000000..a87ceee --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/for-loop-inverted-test.asm.frag @@ -0,0 +1,35 @@ + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + %10 = OpPhi %int %12 %7 %int_0 %5 + OpLoopMerge %6 %7 None + OpBranch %11 + %11 = OpLabel + %16 = OpIEqual %bool %10 %int_16 + OpBranchConditional %16 %6 %19 + %19 = OpLabel + OpBranch %17 + %17 = OpLabel + %21 = OpIAdd %int %10 %int_1 + OpBranch %7 + %7 = OpLabel + %12 = OpPhi %int %21 %17 + OpBranch %8 + %6 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/image-fetch-uint-coord.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/image-fetch-uint-coord.asm.frag new file mode 100644 index 0000000..ca8022d --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/image-fetch-uint-coord.asm.frag @@ -0,0 +1,44 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 29 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_Target0 + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %Tex "Tex" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %main "main" + OpDecorate %in_var_TEXCOORD0 Flat + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %Tex DescriptorSet 0 + OpDecorate %Tex Binding 0 + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %16 = OpTypeFunction %void + %Tex = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v3uint Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %16 + %19 = OpLabel + %20 = OpLoad %v3uint %in_var_TEXCOORD0 + %21 = OpCompositeExtract %uint %20 2 + %27 = OpLoad %type_2d_image %Tex + %28 = OpImageFetch %v4float %27 %20 Lod %21 + OpStore %out_var_SV_Target0 %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/inliner-dominator-inside-loop.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/inliner-dominator-inside-loop.asm.frag new file mode 100644 index 0000000..8b09e5b --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/inliner-dominator-inside-loop.asm.frag @@ -0,0 +1,646 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 1532 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %IN_HPosition %IN_Uv_EdgeDistance1 %IN_UvStuds_EdgeDistance2 %IN_Color %IN_LightPosition_Fog %IN_View_Depth %IN_Normal_SpecPower %IN_Tangent %IN_PosLightSpace_Reflectance %IN_studIndex %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %VertexOutput "VertexOutput" + OpMemberName %VertexOutput 0 "HPosition" + OpMemberName %VertexOutput 1 "Uv_EdgeDistance1" + OpMemberName %VertexOutput 2 "UvStuds_EdgeDistance2" + OpMemberName %VertexOutput 3 "Color" + OpMemberName %VertexOutput 4 "LightPosition_Fog" + OpMemberName %VertexOutput 5 "View_Depth" + OpMemberName %VertexOutput 6 "Normal_SpecPower" + OpMemberName %VertexOutput 7 "Tangent" + OpMemberName %VertexOutput 8 "PosLightSpace_Reflectance" + OpMemberName %VertexOutput 9 "studIndex" + OpName %Surface "Surface" + OpMemberName %Surface 0 "albedo" + OpMemberName %Surface 1 "normal" + OpMemberName %Surface 2 "specular" + OpMemberName %Surface 3 "gloss" + OpMemberName %Surface 4 "reflectance" + OpMemberName %Surface 5 "opacity" + OpName %SurfaceInput "SurfaceInput" + OpMemberName %SurfaceInput 0 "Color" + OpMemberName %SurfaceInput 1 "Uv" + OpMemberName %SurfaceInput 2 "UvStuds" + OpName %Globals "Globals" + OpMemberName %Globals 0 "ViewProjection" + OpMemberName %Globals 1 "ViewRight" + OpMemberName %Globals 2 "ViewUp" + OpMemberName %Globals 3 "ViewDir" + OpMemberName %Globals 4 "CameraPosition" + OpMemberName %Globals 5 "AmbientColor" + OpMemberName %Globals 6 "Lamp0Color" + OpMemberName %Globals 7 "Lamp0Dir" + OpMemberName %Globals 8 "Lamp1Color" + OpMemberName %Globals 9 "FogParams" + OpMemberName %Globals 10 "FogColor" + OpMemberName %Globals 11 "LightBorder" + OpMemberName %Globals 12 "LightConfig0" + OpMemberName %Globals 13 "LightConfig1" + OpMemberName %Globals 14 "LightConfig2" + OpMemberName %Globals 15 "LightConfig3" + OpMemberName %Globals 16 "RefractionBias_FadeDistance_GlowFactor" + OpMemberName %Globals 17 "OutlineBrightness_ShadowInfo" + OpMemberName %Globals 18 "ShadowMatrix0" + OpMemberName %Globals 19 "ShadowMatrix1" + OpMemberName %Globals 20 "ShadowMatrix2" + OpName %CB0 "CB0" + OpMemberName %CB0 0 "CB0" + OpName %_ "" + OpName %LightMapTexture "LightMapTexture" + OpName %LightMapSampler "LightMapSampler" + OpName %ShadowMapSampler "ShadowMapSampler" + OpName %ShadowMapTexture "ShadowMapTexture" + OpName %EnvironmentMapTexture "EnvironmentMapTexture" + OpName %EnvironmentMapSampler "EnvironmentMapSampler" + OpName %IN_HPosition "IN.HPosition" + OpName %IN_Uv_EdgeDistance1 "IN.Uv_EdgeDistance1" + OpName %IN_UvStuds_EdgeDistance2 "IN.UvStuds_EdgeDistance2" + OpName %IN_Color "IN.Color" + OpName %IN_LightPosition_Fog "IN.LightPosition_Fog" + OpName %IN_View_Depth "IN.View_Depth" + OpName %IN_Normal_SpecPower "IN.Normal_SpecPower" + OpName %IN_Tangent "IN.Tangent" + OpName %IN_PosLightSpace_Reflectance "IN.PosLightSpace_Reflectance" + OpName %IN_studIndex "IN.studIndex" + OpName %_entryPointOutput "@entryPointOutput" + OpName %DiffuseMapSampler "DiffuseMapSampler" + OpName %DiffuseMapTexture "DiffuseMapTexture" + OpName %NormalMapSampler "NormalMapSampler" + OpName %NormalMapTexture "NormalMapTexture" + OpName %NormalDetailMapTexture "NormalDetailMapTexture" + OpName %NormalDetailMapSampler "NormalDetailMapSampler" + OpName %StudsMapTexture "StudsMapTexture" + OpName %StudsMapSampler "StudsMapSampler" + OpName %SpecularMapSampler "SpecularMapSampler" + OpName %SpecularMapTexture "SpecularMapTexture" + OpName %Params "Params" + OpMemberName %Params 0 "LqmatFarTilingFactor" + OpName %CB2 "CB2" + OpMemberName %CB2 0 "CB2" + OpMemberDecorate %Globals 0 ColMajor + OpMemberDecorate %Globals 0 Offset 0 + OpMemberDecorate %Globals 0 MatrixStride 16 + OpMemberDecorate %Globals 1 Offset 64 + OpMemberDecorate %Globals 2 Offset 80 + OpMemberDecorate %Globals 3 Offset 96 + OpMemberDecorate %Globals 4 Offset 112 + OpMemberDecorate %Globals 5 Offset 128 + OpMemberDecorate %Globals 6 Offset 144 + OpMemberDecorate %Globals 7 Offset 160 + OpMemberDecorate %Globals 8 Offset 176 + OpMemberDecorate %Globals 9 Offset 192 + OpMemberDecorate %Globals 10 Offset 208 + OpMemberDecorate %Globals 11 Offset 224 + OpMemberDecorate %Globals 12 Offset 240 + OpMemberDecorate %Globals 13 Offset 256 + OpMemberDecorate %Globals 14 Offset 272 + OpMemberDecorate %Globals 15 Offset 288 + OpMemberDecorate %Globals 16 Offset 304 + OpMemberDecorate %Globals 17 Offset 320 + OpMemberDecorate %Globals 18 Offset 336 + OpMemberDecorate %Globals 19 Offset 352 + OpMemberDecorate %Globals 20 Offset 368 + OpMemberDecorate %CB0 0 Offset 0 + OpDecorate %CB0 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %LightMapTexture DescriptorSet 1 + OpDecorate %LightMapTexture Binding 6 + OpDecorate %LightMapSampler DescriptorSet 1 + OpDecorate %LightMapSampler Binding 6 + OpDecorate %ShadowMapSampler DescriptorSet 1 + OpDecorate %ShadowMapSampler Binding 1 + OpDecorate %ShadowMapTexture DescriptorSet 1 + OpDecorate %ShadowMapTexture Binding 1 + OpDecorate %EnvironmentMapTexture DescriptorSet 1 + OpDecorate %EnvironmentMapTexture Binding 2 + OpDecorate %EnvironmentMapSampler DescriptorSet 1 + OpDecorate %EnvironmentMapSampler Binding 2 + OpDecorate %IN_HPosition BuiltIn FragCoord + OpDecorate %IN_Uv_EdgeDistance1 Location 0 + OpDecorate %IN_UvStuds_EdgeDistance2 Location 1 + OpDecorate %IN_Color Location 2 + OpDecorate %IN_LightPosition_Fog Location 3 + OpDecorate %IN_View_Depth Location 4 + OpDecorate %IN_Normal_SpecPower Location 5 + OpDecorate %IN_Tangent Location 6 + OpDecorate %IN_PosLightSpace_Reflectance Location 7 + OpDecorate %IN_studIndex Location 8 + OpDecorate %_entryPointOutput Location 0 + OpDecorate %DiffuseMapSampler DescriptorSet 1 + OpDecorate %DiffuseMapSampler Binding 3 + OpDecorate %DiffuseMapTexture DescriptorSet 1 + OpDecorate %DiffuseMapTexture Binding 3 + OpDecorate %NormalMapSampler DescriptorSet 1 + OpDecorate %NormalMapSampler Binding 4 + OpDecorate %NormalMapTexture DescriptorSet 1 + OpDecorate %NormalMapTexture Binding 4 + OpDecorate %NormalDetailMapTexture DescriptorSet 1 + OpDecorate %NormalDetailMapTexture Binding 8 + OpDecorate %NormalDetailMapSampler DescriptorSet 1 + OpDecorate %NormalDetailMapSampler Binding 8 + OpDecorate %StudsMapTexture DescriptorSet 1 + OpDecorate %StudsMapTexture Binding 0 + OpDecorate %StudsMapSampler DescriptorSet 1 + OpDecorate %StudsMapSampler Binding 0 + OpDecorate %SpecularMapSampler DescriptorSet 1 + OpDecorate %SpecularMapSampler Binding 5 + OpDecorate %SpecularMapTexture DescriptorSet 1 + OpDecorate %SpecularMapTexture Binding 5 + OpMemberDecorate %Params 0 Offset 0 + OpMemberDecorate %CB2 0 Offset 0 + OpDecorate %CB2 Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %8 = OpTypeFunction %float %_ptr_Function_float + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %v3float = OpTypeVector %float 3 + %18 = OpTypeFunction %v3float %_ptr_Function_v4float +%_ptr_Function_v3float = OpTypePointer Function %v3float + %23 = OpTypeFunction %v4float %_ptr_Function_v3float + %27 = OpTypeFunction %float %_ptr_Function_v3float + %31 = OpTypeFunction %float %_ptr_Function_float %_ptr_Function_float + %36 = OpTypeSampler +%_ptr_Function_36 = OpTypePointer Function %36 + %38 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_Function_38 = OpTypePointer Function %38 + %40 = OpTypeFunction %float %_ptr_Function_36 %_ptr_Function_38 %_ptr_Function_v3float %_ptr_Function_float +%VertexOutput = OpTypeStruct %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v3float %v4float %float +%_ptr_Function_VertexOutput = OpTypePointer Function %VertexOutput + %Surface = OpTypeStruct %v3float %v3float %float %float %float %float + %50 = OpTypeFunction %Surface %_ptr_Function_VertexOutput + %54 = OpTypeFunction %v4float %_ptr_Function_VertexOutput + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %60 = OpTypeFunction %v4float %_ptr_Function_36 %_ptr_Function_38 %_ptr_Function_v2float %_ptr_Function_float %_ptr_Function_float +%SurfaceInput = OpTypeStruct %v4float %v2float %v2float +%_ptr_Function_SurfaceInput = OpTypePointer Function %SurfaceInput + %70 = OpTypeFunction %Surface %_ptr_Function_SurfaceInput %_ptr_Function_v2float + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 +%mat4v4float = OpTypeMatrix %v4float 4 + %Globals = OpTypeStruct %mat4v4float %v4float %v4float %v4float %v3float %v3float %v3float %v3float %v3float %v4float %v3float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float + %CB0 = OpTypeStruct %Globals +%_ptr_Uniform_CB0 = OpTypePointer Uniform %CB0 + %_ = OpVariable %_ptr_Uniform_CB0 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_15 = OpConstant %int 15 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_14 = OpConstant %int 14 + %128 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %133 = OpTypeImage %float 3D 0 0 0 1 Unknown +%_ptr_UniformConstant_133 = OpTypePointer UniformConstant %133 +%LightMapTexture = OpVariable %_ptr_UniformConstant_133 UniformConstant +%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36 +%LightMapSampler = OpVariable %_ptr_UniformConstant_36 UniformConstant + %140 = OpTypeSampledImage %133 + %int_11 = OpConstant %int 11 + %uint = OpTypeInt 32 0 + %float_9 = OpConstant %float 9 + %float_20 = OpConstant %float 20 + %float_0_5 = OpConstant %float 0.5 + %183 = OpTypeSampledImage %38 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %int_17 = OpConstant %int 17 + %uint_3 = OpConstant %uint 3 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_0_25 = OpConstant %float 0.25 + %int_5 = OpConstant %int 5 +%float_0_00333333 = OpConstant %float 0.00333333 + %int_16 = OpConstant %int 16 +%_ptr_Function_Surface = OpTypePointer Function %Surface + %int_6 = OpConstant %int 6 + %int_7 = OpConstant %int 7 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %int_8 = OpConstant %int 8 +%ShadowMapSampler = OpVariable %_ptr_UniformConstant_36 UniformConstant +%_ptr_UniformConstant_38 = OpTypePointer UniformConstant %38 +%ShadowMapTexture = OpVariable %_ptr_UniformConstant_38 UniformConstant + %367 = OpTypeImage %float Cube 0 0 0 1 Unknown +%_ptr_UniformConstant_367 = OpTypePointer UniformConstant %367 +%EnvironmentMapTexture = OpVariable %_ptr_UniformConstant_367 UniformConstant +%EnvironmentMapSampler = OpVariable %_ptr_UniformConstant_36 UniformConstant + %373 = OpTypeSampledImage %367 + %float_1_5 = OpConstant %float 1.5 + %int_10 = OpConstant %int 10 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%IN_HPosition = OpVariable %_ptr_Input_v4float Input +%IN_Uv_EdgeDistance1 = OpVariable %_ptr_Input_v4float Input +%IN_UvStuds_EdgeDistance2 = OpVariable %_ptr_Input_v4float Input + %IN_Color = OpVariable %_ptr_Input_v4float Input +%IN_LightPosition_Fog = OpVariable %_ptr_Input_v4float Input +%IN_View_Depth = OpVariable %_ptr_Input_v4float Input +%IN_Normal_SpecPower = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_v3float = OpTypePointer Input %v3float + %IN_Tangent = OpVariable %_ptr_Input_v3float Input +%IN_PosLightSpace_Reflectance = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%IN_studIndex = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %bool = OpTypeBool +%DiffuseMapSampler = OpVariable %_ptr_UniformConstant_36 UniformConstant +%DiffuseMapTexture = OpVariable %_ptr_UniformConstant_38 UniformConstant +%NormalMapSampler = OpVariable %_ptr_UniformConstant_36 UniformConstant +%NormalMapTexture = OpVariable %_ptr_UniformConstant_38 UniformConstant +%NormalDetailMapTexture = OpVariable %_ptr_UniformConstant_38 UniformConstant +%NormalDetailMapSampler = OpVariable %_ptr_UniformConstant_36 UniformConstant + %float_0_3 = OpConstant %float 0.3 +%StudsMapTexture = OpVariable %_ptr_UniformConstant_38 UniformConstant +%StudsMapSampler = OpVariable %_ptr_UniformConstant_36 UniformConstant +%SpecularMapSampler = OpVariable %_ptr_UniformConstant_36 UniformConstant +%SpecularMapTexture = OpVariable %_ptr_UniformConstant_38 UniformConstant + %float_0_75 = OpConstant %float 0.75 + %float_256 = OpConstant %float 256 + %689 = OpConstantComposite %v2float %float_2 %float_256 + %float_0_01 = OpConstant %float 0.01 + %692 = OpConstantComposite %v2float %float_0 %float_0_01 + %float_0_8 = OpConstant %float 0.8 + %float_120 = OpConstant %float 120 + %697 = OpConstantComposite %v2float %float_0_8 %float_120 + %Params = OpTypeStruct %v4float + %CB2 = OpTypeStruct %Params +%_ptr_Uniform_CB2 = OpTypePointer Uniform %CB2 + %false = OpConstantFalse %bool + %1509 = OpUndef %VertexOutput + %1510 = OpUndef %SurfaceInput + %1511 = OpUndef %v2float + %1512 = OpUndef %v4float + %1531 = OpUndef %Surface + %main = OpFunction %void None %3 + %5 = OpLabel + %501 = OpLoad %v4float %IN_HPosition + %1378 = OpCompositeInsert %VertexOutput %501 %1509 0 + %504 = OpLoad %v4float %IN_Uv_EdgeDistance1 + %1380 = OpCompositeInsert %VertexOutput %504 %1378 1 + %507 = OpLoad %v4float %IN_UvStuds_EdgeDistance2 + %1382 = OpCompositeInsert %VertexOutput %507 %1380 2 + %510 = OpLoad %v4float %IN_Color + %1384 = OpCompositeInsert %VertexOutput %510 %1382 3 + %513 = OpLoad %v4float %IN_LightPosition_Fog + %1386 = OpCompositeInsert %VertexOutput %513 %1384 4 + %516 = OpLoad %v4float %IN_View_Depth + %1388 = OpCompositeInsert %VertexOutput %516 %1386 5 + %519 = OpLoad %v4float %IN_Normal_SpecPower + %1390 = OpCompositeInsert %VertexOutput %519 %1388 6 + %523 = OpLoad %v3float %IN_Tangent + %1392 = OpCompositeInsert %VertexOutput %523 %1390 7 + %526 = OpLoad %v4float %IN_PosLightSpace_Reflectance + %1394 = OpCompositeInsert %VertexOutput %526 %1392 8 + %530 = OpLoad %float %IN_studIndex + %1396 = OpCompositeInsert %VertexOutput %530 %1394 9 + %1400 = OpCompositeInsert %SurfaceInput %510 %1510 0 + %954 = OpVectorShuffle %v2float %504 %504 0 1 + %1404 = OpCompositeInsert %SurfaceInput %954 %1400 1 + %958 = OpVectorShuffle %v2float %507 %507 0 1 + %1408 = OpCompositeInsert %SurfaceInput %958 %1404 2 + %1410 = OpCompositeExtract %float %1408 2 1 + %962 = OpExtInst %float %1 Fract %1410 + %965 = OpFAdd %float %962 %530 + %966 = OpFMul %float %965 %float_0_25 + %1414 = OpCompositeInsert %SurfaceInput %966 %1408 2 1 + %1416 = OpCompositeExtract %float %1396 5 3 + %970 = OpFMul %float %1416 %float_0_00333333 + %971 = OpFSub %float %float_1 %970 + %987 = OpExtInst %float %1 FClamp %971 %float_0 %float_1 + %976 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_16 %uint_1 + %977 = OpLoad %float %976 + %978 = OpFMul %float %1416 %977 + %979 = OpFSub %float %float_1 %978 + %990 = OpExtInst %float %1 FClamp %979 %float_0 %float_1 + %1024 = OpVectorTimesScalar %v2float %954 %float_1 + %1029 = OpLoad %36 %DiffuseMapSampler + %1030 = OpLoad %38 %DiffuseMapTexture + OpBranch %1119 + %1119 = OpLabel + OpLoopMerge %1120 %1121 None + OpBranch %1122 + %1122 = OpLabel + %1124 = OpFOrdEqual %bool %float_0 %float_0 + OpSelectionMerge %1125 None + OpBranchConditional %1124 %1126 %1127 + %1126 = OpLabel + %1130 = OpSampledImage %183 %1030 %1029 + %1132 = OpImageSampleImplicitLod %v4float %1130 %1024 + OpBranch %1120 + %1127 = OpLabel + %1134 = OpFSub %float %float_1 %float_0 + %1135 = OpFDiv %float %float_1 %1134 + %1138 = OpSampledImage %183 %1030 %1029 + %1140 = OpVectorTimesScalar %v2float %1024 %float_0_25 + %1141 = OpImageSampleImplicitLod %v4float %1138 %1140 + %1144 = OpSampledImage %183 %1030 %1029 + %1146 = OpImageSampleImplicitLod %v4float %1144 %1024 + %1149 = OpFMul %float %987 %1135 + %1152 = OpFMul %float %float_0 %1135 + %1153 = OpFSub %float %1149 %1152 + %1161 = OpExtInst %float %1 FClamp %1153 %float_0 %float_1 + %1155 = OpCompositeConstruct %v4float %1161 %1161 %1161 %1161 + %1156 = OpExtInst %v4float %1 FMix %1141 %1146 %1155 + OpBranch %1120 + %1125 = OpLabel + %1157 = OpUndef %v4float + OpBranch %1120 + %1121 = OpLabel + OpBranchConditional %false %1119 %1120 + %1120 = OpLabel + %1517 = OpPhi %v4float %1132 %1126 %1156 %1127 %1157 %1125 %1512 %1121 + %1035 = OpVectorTimesScalar %v4float %1517 %float_1 + %1036 = OpLoad %36 %NormalMapSampler + %1037 = OpLoad %38 %NormalMapTexture + OpBranch %1165 + %1165 = OpLabel + OpLoopMerge %1166 %1167 None + OpBranch %1168 + %1168 = OpLabel + OpSelectionMerge %1171 None + OpBranchConditional %1124 %1172 %1173 + %1172 = OpLabel + %1176 = OpSampledImage %183 %1037 %1036 + %1178 = OpImageSampleImplicitLod %v4float %1176 %1024 + OpBranch %1166 + %1173 = OpLabel + %1180 = OpFSub %float %float_1 %float_0 + %1181 = OpFDiv %float %float_1 %1180 + %1184 = OpSampledImage %183 %1037 %1036 + %1186 = OpVectorTimesScalar %v2float %1024 %float_0_25 + %1187 = OpImageSampleImplicitLod %v4float %1184 %1186 + %1190 = OpSampledImage %183 %1037 %1036 + %1192 = OpImageSampleImplicitLod %v4float %1190 %1024 + %1195 = OpFMul %float %990 %1181 + %1198 = OpFMul %float %float_0 %1181 + %1199 = OpFSub %float %1195 %1198 + %1206 = OpExtInst %float %1 FClamp %1199 %float_0 %float_1 + %1201 = OpCompositeConstruct %v4float %1206 %1206 %1206 %1206 + %1202 = OpExtInst %v4float %1 FMix %1187 %1192 %1201 + OpBranch %1166 + %1171 = OpLabel + %1203 = OpUndef %v4float + OpBranch %1166 + %1167 = OpLabel + OpBranchConditional %false %1165 %1166 + %1166 = OpLabel + %1523 = OpPhi %v4float %1178 %1172 %1202 %1173 %1203 %1171 %1512 %1167 + %1210 = OpVectorShuffle %v2float %1523 %1523 3 1 + %1211 = OpVectorTimesScalar %v2float %1210 %float_2 + %1212 = OpCompositeConstruct %v2float %float_1 %float_1 + %1213 = OpFSub %v2float %1211 %1212 + %1216 = OpFNegate %v2float %1213 + %1218 = OpDot %float %1216 %1213 + %1219 = OpFAdd %float %float_1 %1218 + %1220 = OpExtInst %float %1 FClamp %1219 %float_0 %float_1 + %1221 = OpExtInst %float %1 Sqrt %1220 + %1222 = OpCompositeExtract %float %1213 0 + %1223 = OpCompositeExtract %float %1213 1 + %1224 = OpCompositeConstruct %v3float %1222 %1223 %1221 + %1042 = OpLoad %38 %NormalDetailMapTexture + %1043 = OpLoad %36 %NormalDetailMapSampler + %1044 = OpSampledImage %183 %1042 %1043 + %1046 = OpVectorTimesScalar %v2float %1024 %float_0 + %1047 = OpImageSampleImplicitLod %v4float %1044 %1046 + %1228 = OpVectorShuffle %v2float %1047 %1047 3 1 + %1229 = OpVectorTimesScalar %v2float %1228 %float_2 + %1231 = OpFSub %v2float %1229 %1212 + %1234 = OpFNegate %v2float %1231 + %1236 = OpDot %float %1234 %1231 + %1237 = OpFAdd %float %float_1 %1236 + %1238 = OpExtInst %float %1 FClamp %1237 %float_0 %float_1 + %1239 = OpExtInst %float %1 Sqrt %1238 + %1240 = OpCompositeExtract %float %1231 0 + %1241 = OpCompositeExtract %float %1231 1 + %1242 = OpCompositeConstruct %v3float %1240 %1241 %1239 + %1050 = OpVectorShuffle %v2float %1242 %1242 0 1 + %1051 = OpVectorTimesScalar %v2float %1050 %float_0 + %1053 = OpVectorShuffle %v2float %1224 %1224 0 1 + %1054 = OpFAdd %v2float %1053 %1051 + %1056 = OpVectorShuffle %v3float %1224 %1054 3 4 2 + %1059 = OpVectorShuffle %v2float %1056 %1056 0 1 + %1060 = OpVectorTimesScalar %v2float %1059 %990 + %1062 = OpVectorShuffle %v3float %1056 %1060 3 4 2 + %1430 = OpCompositeExtract %float %1062 0 + %1065 = OpFMul %float %1430 %float_0_3 + %1066 = OpFAdd %float %float_1 %1065 + %1069 = OpVectorShuffle %v3float %510 %510 0 1 2 + %1071 = OpVectorShuffle %v3float %1035 %1035 0 1 2 + %1072 = OpFMul %v3float %1069 %1071 + %1074 = OpVectorTimesScalar %v3float %1072 %1066 + %1075 = OpLoad %38 %StudsMapTexture + %1076 = OpLoad %36 %StudsMapSampler + %1077 = OpSampledImage %183 %1075 %1076 + %1434 = OpCompositeExtract %v2float %1414 2 + %1080 = OpImageSampleImplicitLod %v4float %1077 %1434 + %1436 = OpCompositeExtract %float %1080 0 + %1083 = OpFMul %float %1436 %float_2 + %1085 = OpVectorTimesScalar %v3float %1074 %1083 + %1086 = OpLoad %36 %SpecularMapSampler + %1087 = OpLoad %38 %SpecularMapTexture + OpBranch %1246 + %1246 = OpLabel + OpLoopMerge %1247 %1248 None + OpBranch %1249 + %1249 = OpLabel + %1251 = OpFOrdEqual %bool %float_0_75 %float_0 + OpSelectionMerge %1252 None + OpBranchConditional %1251 %1253 %1254 + %1253 = OpLabel + %1257 = OpSampledImage %183 %1087 %1086 + %1259 = OpImageSampleImplicitLod %v4float %1257 %1024 + OpBranch %1247 + %1254 = OpLabel + %1261 = OpFSub %float %float_1 %float_0_75 + %1262 = OpFDiv %float %float_1 %1261 + %1265 = OpSampledImage %183 %1087 %1086 + %1267 = OpVectorTimesScalar %v2float %1024 %float_0_25 + %1268 = OpImageSampleImplicitLod %v4float %1265 %1267 + %1271 = OpSampledImage %183 %1087 %1086 + %1273 = OpImageSampleImplicitLod %v4float %1271 %1024 + %1276 = OpFMul %float %990 %1262 + %1279 = OpFMul %float %float_0_75 %1262 + %1280 = OpFSub %float %1276 %1279 + %1287 = OpExtInst %float %1 FClamp %1280 %float_0 %float_1 + %1282 = OpCompositeConstruct %v4float %1287 %1287 %1287 %1287 + %1283 = OpExtInst %v4float %1 FMix %1268 %1273 %1282 + OpBranch %1247 + %1252 = OpLabel + %1284 = OpUndef %v4float + OpBranch %1247 + %1248 = OpLabel + OpBranchConditional %false %1246 %1247 + %1247 = OpLabel + %1530 = OpPhi %v4float %1259 %1253 %1283 %1254 %1284 %1252 %1512 %1248 + %1091 = OpVectorShuffle %v2float %1530 %1530 0 1 + %1093 = OpFMul %v2float %1091 %689 + %1094 = OpFAdd %v2float %1093 %692 + %1097 = OpCompositeConstruct %v2float %990 %990 + %1098 = OpExtInst %v2float %1 FMix %697 %1094 %1097 + %1438 = OpCompositeInsert %Surface %1085 %1531 0 + %1440 = OpCompositeInsert %Surface %1062 %1438 1 + %1442 = OpCompositeExtract %float %1098 0 + %1444 = OpCompositeInsert %Surface %1442 %1440 2 + %1446 = OpCompositeExtract %float %1098 1 + %1448 = OpCompositeInsert %Surface %1446 %1444 3 + %1450 = OpCompositeExtract %float %1091 1 + %1112 = OpFMul %float %1450 %990 + %1113 = OpFMul %float %1112 %float_0 + %1452 = OpCompositeInsert %Surface %1113 %1448 4 + %1456 = OpCompositeExtract %float %1396 3 3 + %764 = OpCompositeExtract %float %1085 0 + %765 = OpCompositeExtract %float %1085 1 + %766 = OpCompositeExtract %float %1085 2 + %767 = OpCompositeConstruct %v4float %764 %765 %766 %1456 + %770 = OpVectorShuffle %v3float %519 %519 0 1 2 + %773 = OpExtInst %v3float %1 Cross %770 %523 + %1462 = OpCompositeExtract %float %1452 1 0 + %778 = OpVectorTimesScalar %v3float %523 %1462 + %1466 = OpCompositeExtract %float %1452 1 1 + %782 = OpVectorTimesScalar %v3float %773 %1466 + %783 = OpFAdd %v3float %778 %782 + %1468 = OpCompositeExtract %float %1452 1 2 + %789 = OpVectorTimesScalar %v3float %770 %1468 + %790 = OpFAdd %v3float %783 %789 + %791 = OpExtInst %v3float %1 Normalize %790 + %793 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 %int_7 + %794 = OpLoad %v3float %793 + %795 = OpFNegate %v3float %794 + %796 = OpDot %float %791 %795 + %1290 = OpExtInst %float %1 FClamp %796 %float_0 %float_1 + %799 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 %int_6 + %800 = OpLoad %v3float %799 + %801 = OpVectorTimesScalar %v3float %800 %1290 + %803 = OpFNegate %float %796 + %804 = OpExtInst %float %1 FMax %803 %float_0 + %805 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 %int_8 + %806 = OpLoad %v3float %805 + %807 = OpVectorTimesScalar %v3float %806 %804 + %808 = OpFAdd %v3float %801 %807 + %810 = OpExtInst %float %1 Step %float_0 %796 + %813 = OpFMul %float %810 %1442 + %820 = OpVectorShuffle %v3float %513 %513 0 1 2 + %1296 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %int_15 + %1297 = OpLoad %v4float %1296 + %1298 = OpVectorShuffle %v3float %1297 %1297 0 1 2 + %1300 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %int_14 + %1301 = OpLoad %v4float %1300 + %1302 = OpVectorShuffle %v3float %1301 %1301 0 1 2 + %1303 = OpFSub %v3float %820 %1302 + %1304 = OpExtInst %v3float %1 FAbs %1303 + %1305 = OpExtInst %v3float %1 Step %1298 %1304 + %1307 = OpDot %float %1305 %128 + %1328 = OpExtInst %float %1 FClamp %1307 %float_0 %float_1 + %1309 = OpLoad %133 %LightMapTexture + %1310 = OpLoad %36 %LightMapSampler + %1311 = OpSampledImage %140 %1309 %1310 + %1313 = OpVectorShuffle %v3float %820 %820 1 2 0 + %1317 = OpVectorTimesScalar %v3float %1313 %1328 + %1318 = OpFSub %v3float %1313 %1317 + %1319 = OpImageSampleImplicitLod %v4float %1311 %1318 + %1321 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %int_11 + %1322 = OpLoad %v4float %1321 + %1324 = OpCompositeConstruct %v4float %1328 %1328 %1328 %1328 + %1325 = OpExtInst %v4float %1 FMix %1319 %1322 %1324 + %822 = OpLoad %36 %ShadowMapSampler + %823 = OpLoad %38 %ShadowMapTexture + %826 = OpVectorShuffle %v3float %526 %526 0 1 2 + %1482 = OpCompositeExtract %float %1325 3 + %1337 = OpSampledImage %183 %823 %822 + %1339 = OpVectorShuffle %v2float %826 %826 0 1 + %1340 = OpImageSampleImplicitLod %v4float %1337 %1339 + %1341 = OpVectorShuffle %v2float %1340 %1340 0 1 + %1484 = OpCompositeExtract %float %826 2 + %1486 = OpCompositeExtract %float %1341 0 + %1363 = OpExtInst %float %1 Step %1486 %1484 + %1365 = OpFSub %float %1484 %float_0_5 + %1366 = OpExtInst %float %1 FAbs %1365 + %1367 = OpFMul %float %float_20 %1366 + %1368 = OpFSub %float %float_9 %1367 + %1369 = OpExtInst %float %1 FClamp %1368 %float_0 %float_1 + %1370 = OpFMul %float %1363 %1369 + %1488 = OpCompositeExtract %float %1341 1 + %1350 = OpFMul %float %1370 %1488 + %1351 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_17 %uint_3 + %1352 = OpLoad %float %1351 + %1353 = OpFMul %float %1350 %1352 + %1354 = OpFSub %float %float_1 %1353 + %1356 = OpFMul %float %1354 %1482 + %830 = OpLoad %367 %EnvironmentMapTexture + %831 = OpLoad %36 %EnvironmentMapSampler + %832 = OpSampledImage %373 %830 %831 + %835 = OpVectorShuffle %v3float %516 %516 0 1 2 + %836 = OpFNegate %v3float %835 + %838 = OpExtInst %v3float %1 Reflect %836 %791 + %839 = OpImageSampleImplicitLod %v4float %832 %838 + %840 = OpVectorShuffle %v3float %839 %839 0 1 2 + %842 = OpVectorShuffle %v3float %767 %767 0 1 2 + %845 = OpCompositeConstruct %v3float %1113 %1113 %1113 + %846 = OpExtInst %v3float %1 FMix %842 %840 %845 + %848 = OpVectorShuffle %v4float %767 %846 4 5 6 3 + %849 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 %int_5 + %850 = OpLoad %v3float %849 + %853 = OpVectorTimesScalar %v3float %808 %1356 + %854 = OpFAdd %v3float %850 %853 + %856 = OpVectorShuffle %v3float %1325 %1325 0 1 2 + %857 = OpFAdd %v3float %854 %856 + %859 = OpVectorShuffle %v3float %848 %848 0 1 2 + %860 = OpFMul %v3float %857 %859 + %865 = OpFMul %float %813 %1356 + %873 = OpExtInst %v3float %1 Normalize %835 + %874 = OpFAdd %v3float %795 %873 + %875 = OpExtInst %v3float %1 Normalize %874 + %876 = OpDot %float %791 %875 + %877 = OpExtInst %float %1 FClamp %876 %float_0 %float_1 + %879 = OpExtInst %float %1 Pow %877 %1446 + %880 = OpFMul %float %865 %879 + %881 = OpVectorTimesScalar %v3float %800 %880 + %884 = OpFAdd %v3float %860 %881 + %886 = OpVectorShuffle %v4float %1512 %884 4 5 6 3 + %1494 = OpCompositeExtract %float %848 3 + %1496 = OpCompositeInsert %v4float %1494 %886 3 + %896 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_17 %uint_0 + %897 = OpLoad %float %896 + %898 = OpFMul %float %978 %897 + %899 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_17 %uint_1 + %900 = OpLoad %float %899 + %901 = OpFAdd %float %898 %900 + %1373 = OpExtInst %float %1 FClamp %901 %float_0 %float_1 + %905 = OpVectorShuffle %v2float %504 %504 3 2 + %908 = OpVectorShuffle %v2float %507 %507 3 2 + %909 = OpExtInst %v2float %1 FMin %905 %908 + %1504 = OpCompositeExtract %float %909 0 + %1506 = OpCompositeExtract %float %909 1 + %914 = OpExtInst %float %1 FMin %1504 %1506 + %916 = OpFDiv %float %914 %978 + %919 = OpFSub %float %float_1_5 %916 + %920 = OpFMul %float %1373 %919 + %922 = OpFAdd %float %920 %916 + %1376 = OpExtInst %float %1 FClamp %922 %float_0 %float_1 + %925 = OpVectorShuffle %v3float %1496 %1496 0 1 2 + %926 = OpVectorTimesScalar %v3float %925 %1376 + %928 = OpVectorShuffle %v4float %1496 %926 4 5 6 3 + %1508 = OpCompositeExtract %float %1396 4 3 + %931 = OpExtInst %float %1 FClamp %1508 %float_0 %float_1 + %932 = OpAccessChain %_ptr_Uniform_v3float %_ %int_0 %int_10 + %933 = OpLoad %v3float %932 + %935 = OpVectorShuffle %v3float %928 %928 0 1 2 + %937 = OpCompositeConstruct %v3float %931 %931 %931 + %938 = OpExtInst %v3float %1 FMix %933 %935 %937 + %940 = OpVectorShuffle %v4float %928 %938 4 5 6 3 + OpStore %_entryPointOutput %940 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/ldexp-uint-exponent.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/ldexp-uint-exponent.asm.frag new file mode 100644 index 0000000..9baebc2 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/ldexp-uint-exponent.asm.frag @@ -0,0 +1,36 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_GLF_color + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %_GLF_color "_GLF_color" + OpDecorate %_GLF_color Location 0 + OpDecorate %18 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %_GLF_color = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %11 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 + %uint_1 = OpConstant %uint 1 + %15 = OpConstantComposite %v4uint %uint_1 %uint_1 %uint_1 %uint_1 + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpBitCount %v4uint %15 + %19 = OpExtInst %v4float %1 Ldexp %11 %18 + OpStore %_GLF_color %19 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/loop-merge-to-continue.asm.invalid.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/loop-merge-to-continue.asm.invalid.frag new file mode 100644 index 0000000..f2acc43 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/loop-merge-to-continue.asm.invalid.frag @@ -0,0 +1,85 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 51 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %v0 + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %i "i" + OpName %j "j" + OpName %v0 "v0" + OpDecorate %FragColor Location 0 + OpDecorate %v0 Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %11 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 + %bool = OpTypeBool +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v0 = OpVariable %_ptr_Input_v4float Input + %int_3 = OpConstant %int 3 +%_ptr_Input_float = OpTypePointer Input %float + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + OpStore %FragColor %11 + OpStore %i %int_0 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %21 = OpLoad %int %i + %24 = OpSLessThan %bool %21 %int_4 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + OpStore %j %int_0 + OpBranch %26 + %26 = OpLabel + OpLoopMerge %19 %29 None + OpBranch %30 + %30 = OpLabel + %31 = OpLoad %int %j + %32 = OpSLessThan %bool %31 %int_4 + OpBranchConditional %32 %27 %19 + %27 = OpLabel + %35 = OpLoad %int %i + %36 = OpLoad %int %j + %37 = OpIAdd %int %35 %36 + %39 = OpBitwiseAnd %int %37 %int_3 + %41 = OpAccessChain %_ptr_Input_float %v0 %39 + %42 = OpLoad %float %41 + %43 = OpLoad %v4float %FragColor + %44 = OpCompositeConstruct %v4float %42 %42 %42 %42 + %45 = OpFAdd %v4float %43 %44 + OpStore %FragColor %45 + OpBranch %29 + %29 = OpLabel + %46 = OpLoad %int %j + %48 = OpIAdd %int %46 %int_1 + OpStore %j %48 + OpBranch %26 + %19 = OpLabel + %49 = OpLoad %int %i + %50 = OpIAdd %int %49 %int_1 + OpStore %i %50 + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/nonuniform-bracket-handling-2.vk.nocompat.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/nonuniform-bracket-handling-2.vk.nocompat.asm.frag new file mode 100644 index 0000000..ea85ed0 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/nonuniform-bracket-handling-2.vk.nocompat.asm.frag @@ -0,0 +1,96 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 53 +; Schema: 0 + OpCapability Shader + OpCapability ShaderNonUniform + OpCapability RuntimeDescriptorArray + OpCapability SampledImageArrayNonUniformIndexing + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vUV %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uSamplers "uSamplers" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "indices" + OpName %_ "" + OpName %vUV "vUV" + OpName %uSampler "uSampler" + OpName %gl_FragCoord "gl_FragCoord" + OpDecorate %FragColor Location 0 + OpDecorate %uSamplers DescriptorSet 0 + OpDecorate %uSamplers Binding 0 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO 0 NonWritable + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %26 NonUniform + OpDecorate %28 NonUniform + OpDecorate %29 NonUniform + OpDecorate %vUV Location 0 + OpDecorate %uSampler DescriptorSet 1 + OpDecorate %uSampler Binding 0 + OpDecorate %38 NonUniform + OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_runtimearr_11 = OpTypeRuntimeArray %11 +%_ptr_UniformConstant__runtimearr_11 = OpTypePointer UniformConstant %_runtimearr_11 + %uSamplers = OpVariable %_ptr_UniformConstant__runtimearr_11 UniformConstant + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input + %float_0 = OpConstant %float 0 + %uSampler = OpVariable %_ptr_UniformConstant_11 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_1 = OpConstant %uint 1 +%_ptr_Input_float = OpTypePointer Input %float + %main = OpFunction %void None %3 + %5 = OpLabel + %24 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %int_10 + %26 = OpLoad %uint %24 + %28 = OpAccessChain %_ptr_UniformConstant_11 %uSamplers %26 + %29 = OpLoad %11 %28 + %33 = OpLoad %v2float %vUV + %35 = OpImageSampleExplicitLod %v4float %29 %33 Lod %float_0 + OpStore %FragColor %35 + %37 = OpLoad %11 %uSampler + %38 = OpCopyObject %11 %37 + %39 = OpLoad %v2float %vUV + %44 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_1 + %45 = OpLoad %float %44 + %46 = OpConvertFToS %int %45 + %47 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %46 + %48 = OpLoad %uint %47 + %49 = OpConvertUToF %float %48 + %50 = OpImageSampleExplicitLod %v4float %38 %39 Lod %49 + %51 = OpLoad %v4float %FragColor + %52 = OpFAdd %v4float %51 %50 + OpStore %FragColor %52 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/nonuniform-qualifier-propagation.vk.nocompat.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/nonuniform-qualifier-propagation.vk.nocompat.asm.frag new file mode 100644 index 0000000..e2d4562 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/nonuniform-qualifier-propagation.vk.nocompat.asm.frag @@ -0,0 +1,159 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 93 +; Schema: 0 + OpCapability Shader + OpCapability ShaderNonUniformEXT + OpCapability RuntimeDescriptorArrayEXT + OpCapability UniformBufferArrayNonUniformIndexingEXT + OpCapability SampledImageArrayNonUniformIndexingEXT + OpCapability StorageBufferArrayNonUniformIndexingEXT + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vIndex %FragColor %vUV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %i "i" + OpName %vIndex "vIndex" + OpName %FragColor "FragColor" + OpName %uSamplers "uSamplers" + OpName %uSamps "uSamps" + OpName %vUV "vUV" + OpName %uCombinedSamplers "uCombinedSamplers" + OpName %UBO "UBO" + OpMemberName %UBO 0 "v" + OpName %ubos "ubos" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "v" + OpName %ssbos "ssbos" + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + OpDecorate %FragColor Location 0 + OpDecorate %uSamplers DescriptorSet 0 + OpDecorate %uSamplers Binding 0 + + OpDecorate %sampled_image NonUniformEXT + OpDecorate %combined_sampler NonUniformEXT + OpDecorate %ubo_ptr_copy NonUniformEXT + OpDecorate %ssbo_ptr_copy NonUniformEXT + + OpDecorate %uSamps DescriptorSet 0 + OpDecorate %uSamps Binding 1 + OpDecorate %vUV Location 1 + OpDecorate %uCombinedSamplers DescriptorSet 0 + OpDecorate %uCombinedSamplers Binding 4 + OpDecorate %_arr_v4float_uint_64 ArrayStride 16 + OpMemberDecorate %UBO 0 Offset 0 + OpDecorate %UBO Block + OpDecorate %ubos DescriptorSet 0 + OpDecorate %ubos Binding 2 + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %SSBO 0 NonWritable + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %ssbos DescriptorSet 0 + OpDecorate %ssbos Binding 3 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %16 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_runtimearr_16 = OpTypeRuntimeArray %16 +%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16 + %uSamplers = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant + %int_10 = OpConstant %int 10 +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 + %27 = OpTypeSampler +%_runtimearr_27 = OpTypeRuntimeArray %27 +%_ptr_UniformConstant__runtimearr_27 = OpTypePointer UniformConstant %_runtimearr_27 + %uSamps = OpVariable %_ptr_UniformConstant__runtimearr_27 UniformConstant + %int_40 = OpConstant %int 40 +%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27 + %38 = OpTypeSampledImage %16 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input +%_runtimearr_38 = OpTypeRuntimeArray %38 +%_ptr_UniformConstant__runtimearr_38 = OpTypePointer UniformConstant %_runtimearr_38 +%uCombinedSamplers = OpVariable %_ptr_UniformConstant__runtimearr_38 UniformConstant +%_ptr_UniformConstant_38 = OpTypePointer UniformConstant %38 + %uint = OpTypeInt 32 0 + %uint_64 = OpConstant %uint 64 +%_arr_v4float_uint_64 = OpTypeArray %v4float %uint_64 + %UBO = OpTypeStruct %_arr_v4float_uint_64 +%_runtimearr_UBO = OpTypeRuntimeArray %UBO +%_ptr_Uniform__runtimearr_UBO = OpTypePointer Uniform %_runtimearr_UBO + %ubos = OpVariable %_ptr_Uniform__runtimearr_UBO Uniform + %int_20 = OpConstant %int 20 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %SSBO = OpTypeStruct %_runtimearr_v4float +%_runtimearr_SSBO = OpTypeRuntimeArray %SSBO +%_ptr_Uniform__runtimearr_SSBO = OpTypePointer Uniform %_runtimearr_SSBO + %ssbos = OpVariable %_ptr_Uniform__runtimearr_SSBO Uniform + %int_50 = OpConstant %int 50 + %int_60 = OpConstant %int 60 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %11 = OpLoad %int %vIndex + OpStore %i %11 + %20 = OpLoad %int %i + %22 = OpIAdd %int %20 %int_10 + %23 = OpCopyObject %int %22 + %25 = OpAccessChain %_ptr_UniformConstant_16 %uSamplers %23 + %26 = OpLoad %16 %25 + %31 = OpLoad %int %i + %33 = OpIAdd %int %31 %int_40 + %34 = OpCopyObject %int %33 + %36 = OpAccessChain %_ptr_UniformConstant_27 %uSamps %34 + %37 = OpLoad %27 %36 + %sampled_image = OpSampledImage %38 %26 %37 + %43 = OpLoad %v2float %vUV + %44 = OpImageSampleImplicitLod %v4float %sampled_image %43 + OpStore %FragColor %44 + %48 = OpLoad %int %i + %49 = OpIAdd %int %48 %int_10 + %50 = OpCopyObject %int %49 + %52 = OpAccessChain %_ptr_UniformConstant_38 %uCombinedSamplers %50 + %combined_sampler = OpLoad %38 %52 + %54 = OpLoad %v2float %vUV + %55 = OpImageSampleImplicitLod %v4float %combined_sampler %54 + OpStore %FragColor %55 + %63 = OpLoad %int %i + %65 = OpIAdd %int %63 %int_20 + %66 = OpCopyObject %int %65 + %68 = OpLoad %int %i + %69 = OpIAdd %int %68 %int_40 + %70 = OpCopyObject %int %69 + %ubo_ptr = OpAccessChain %_ptr_Uniform_v4float %ubos %66 %int_0 %70 + %ubo_ptr_copy = OpCopyObject %_ptr_Uniform_v4float %ubo_ptr + %73 = OpLoad %v4float %ubo_ptr_copy + %74 = OpLoad %v4float %FragColor + %75 = OpFAdd %v4float %74 %73 + OpStore %FragColor %75 + %81 = OpLoad %int %i + %83 = OpIAdd %int %81 %int_50 + %84 = OpCopyObject %int %83 + %85 = OpLoad %int %i + %87 = OpIAdd %int %85 %int_60 + %88 = OpCopyObject %int %87 + %ssbo_ptr = OpAccessChain %_ptr_Uniform_v4float %ssbos %84 %int_0 %88 + %ssbo_ptr_copy = OpCopyObject %_ptr_Uniform_v4float %ssbo_ptr + %90 = OpLoad %v4float %ssbo_ptr_copy + %91 = OpLoad %v4float %FragColor + %92 = OpFAdd %v4float %91 %90 + OpStore %FragColor %92 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/opaque-id-literal-alias.preserve.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/opaque-id-literal-alias.preserve.asm.frag new file mode 100644 index 0000000..c77c9a1 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/opaque-id-literal-alias.preserve.asm.frag @@ -0,0 +1,78 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 50 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %gl_FragCoord %vUV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %gl_FragCoord "gl_FragCoord" + OpName %uSampled "uSampled" + OpName %vUV "vUV" + OpDecorate %FragColor Location 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %uSampled DescriptorSet 0 + OpDecorate %uSampled Binding 0 + OpDecorate %vUV Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool + %24 = OpTypeImage %float 2D 0 0 1 1 Unknown + %25 = OpTypeSampledImage %24 +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 + %uSampled = OpVariable %_ptr_UniformConstant_25 UniformConstant + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %FragColor %11 + %17 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %18 = OpLoad %float %17 + %21 = OpFOrdLessThan %bool %18 %float_10 + OpSelectionMerge %23 None + OpBranchConditional %21 %22 %41 + %22 = OpLabel + %28 = OpLoad %25 %uSampled + %32 = OpLoad %v2float %vUV + %35 = OpConvertFToS %v2int %32 + %64 = OpImage %24 %28 + %38 = OpImageFetch %v4float %64 %35 Sample %int_0 + %39 = OpLoad %v4float %FragColor + %40 = OpFAdd %v4float %39 %38 + OpStore %FragColor %40 + OpBranch %23 + %41 = OpLabel + %42 = OpLoad %25 %uSampled + %43 = OpLoad %v2float %vUV + %44 = OpConvertFToS %v2int %43 + %46 = OpImage %24 %42 + %47 = OpImageFetch %v4float %46 %44 Sample %int_1 + %48 = OpLoad %v4float %FragColor + %49 = OpFAdd %v4float %48 %47 + OpStore %FragColor %49 + OpBranch %23 + %23 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/out-of-order-struct-id.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/out-of-order-struct-id.asm.frag new file mode 100644 index 0000000..4a7885e --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/out-of-order-struct-id.asm.frag @@ -0,0 +1,54 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 24 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %80 "Foo" + OpMemberName %80 0 "a" + OpName %79 "Bar" + OpMemberName %79 0 "foo" + OpMemberName %79 1 "foo2" + OpName %UBO "UBO" + OpMemberName %UBO 0 "bar" + OpName %_ "" + OpDecorate %FragColor Location 0 + OpMemberDecorate %80 0 Offset 0 + OpMemberDecorate %79 0 Offset 0 + OpMemberDecorate %79 1 Offset 16 + OpMemberDecorate %UBO 0 Offset 0 + OpDecorate %UBO Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %80 = OpTypeStruct %v4float + %79 = OpTypeStruct %80 %80 + %UBO = OpTypeStruct %79 +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO + %_ = OpVariable %_ptr_Uniform_UBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %int_0 %int_0 + %19 = OpLoad %v4float %18 + %21 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %int_1 %int_0 + %22 = OpLoad %v4float %21 + %23 = OpFAdd %v4float %19 %22 + OpStore %FragColor %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/phi.zero-initialize.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/phi.zero-initialize.asm.frag new file mode 100644 index 0000000..3696660 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/phi.zero-initialize.asm.frag @@ -0,0 +1,69 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 40 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vColor %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %vColor "vColor" + OpName %uninit_function_int "uninit_function_int" + OpName %FragColor "FragColor" + OpName %uninit_int "uninit_int" + OpName %uninit_vector "uninit_vector" + OpName %uninit_matrix "uninit_matrix" + OpName %Foo "Foo" + OpMemberName %Foo 0 "a" + OpName %uninit_foo "uninit_foo" + OpDecorate %vColor Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vColor = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_10 = OpConstant %int 10 + %int_20 = OpConstant %int 20 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Private_int = OpTypePointer Private %int + %uninit_int = OpUndef %int + %v4int = OpTypeVector %int 4 +%_ptr_Private_v4int = OpTypePointer Private %v4int +%uninit_vector = OpUndef %v4int +%mat4v4float = OpTypeMatrix %v4float 4 +%_ptr_Private_mat4v4float = OpTypePointer Private %mat4v4float +%uninit_matrix = OpUndef %mat4v4float + %Foo = OpTypeStruct %int +%_ptr_Private_Foo = OpTypePointer Private %Foo + %uninit_foo = OpUndef %Foo + %main = OpFunction %void None %3 + %5 = OpLabel +%uninit_function_int = OpVariable %_ptr_Function_int Function + %13 = OpAccessChain %_ptr_Input_float %vColor %uint_0 + %14 = OpLoad %float %13 + %17 = OpFOrdGreaterThan %bool %14 %float_10 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %24 + %18 = OpLabel + OpBranch %19 + %24 = OpLabel + OpBranch %19 + %19 = OpLabel + %27 = OpPhi %int %int_10 %18 %int_20 %24 + %28 = OpLoad %v4float %vColor + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-callstack.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-callstack.asm.frag new file mode 100644 index 0000000..ebd8d6b --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-callstack.asm.frag @@ -0,0 +1,89 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + OpReturn + OpFunctionEnd + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + OpBeginInvocationInterlockEXT + %43 = OpFunctionCall %void %callee2_ + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-control-flow.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-control-flow.asm.frag new file mode 100644 index 0000000..69b8f91 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-control-flow.asm.frag @@ -0,0 +1,121 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + + OpMemberDecorate %SSBO2 0 Offset 0 + OpDecorate %SSBO2 BufferBlock + OpDecorate %ssbo2 DescriptorSet 0 + OpDecorate %ssbo2 Binding 2 + + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint + %SSBO2 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 +%_ptr_Uniform_SSBO2 = OpTypePointer Uniform %SSBO2 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %ssbo2 = OpVariable %_ptr_Uniform_SSBO2 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_4 = OpConstant %uint 4 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %bool = OpTypeBool + %true = OpConstantTrue %bool +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + %callee3_res = OpFunctionCall %void %callee3_ + OpReturn + OpFunctionEnd + + %callee3_ = OpFunction %void None %3 + %calle3_block = OpLabel + %frag_coord_x_ptr = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %frag_coord_x = OpLoad %float %frag_coord_x_ptr + %frag_coord_int = OpConvertFToS %int %frag_coord_x + %ssbo_ptr = OpAccessChain %_ptr_Uniform_uint %ssbo2 %int_0 %frag_coord_int + OpStore %ssbo_ptr %uint_4 + OpReturn + OpFunctionEnd + + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + %43 = OpFunctionCall %void %callee2_ + + OpSelectionMerge %merged_block None + OpBranchConditional %true %dummy_block %merged_block + %dummy_block = OpLabel + OpBeginInvocationInterlockEXT + OpEndInvocationInterlockEXT + OpBranch %merged_block + + %merged_block = OpLabel + OpReturn + + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-split-functions.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-split-functions.asm.frag new file mode 100644 index 0000000..7c0fe9a --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/pixel-interlock-split-functions.asm.frag @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability FragmentShaderPixelInterlockEXT + OpExtension "SPV_EXT_fragment_shader_interlock" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpExecutionMode %main PixelInterlockOrderedEXT + OpSource GLSL 450 + OpSourceExtension "GL_ARB_fragment_shader_interlock" + OpName %main "main" + OpName %callee2_ "callee2(" + OpName %callee_ "callee(" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "values1" + OpName %_ "" + OpName %gl_FragCoord "gl_FragCoord" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "values0" + OpName %__0 "" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_runtimearr_uint_0 ArrayStride 4 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %SSBO1 = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %_ = OpVariable %_ptr_Uniform_SSBO1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_runtimearr_uint_0 = OpTypeRuntimeArray %uint + %SSBO0 = OpTypeStruct %_runtimearr_uint_0 +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %__0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %44 = OpFunctionCall %void %callee_ + %call3res = OpFunctionCall %void %callee3_ + %call4res = OpFunctionCall %void %callee4_ + OpReturn + OpFunctionEnd + + %callee3_ = OpFunction %void None %3 + %begin3 = OpLabel + OpBeginInvocationInterlockEXT + OpReturn + OpFunctionEnd + + %callee4_ = OpFunction %void None %3 + %begin4 = OpLabel + OpEndInvocationInterlockEXT + OpReturn + OpFunctionEnd + + %callee2_ = OpFunction %void None %3 + %7 = OpLabel + %23 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %24 = OpLoad %float %23 + %25 = OpConvertFToS %int %24 + %28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + %29 = OpLoad %uint %28 + %30 = OpIAdd %uint %29 %uint_1 + %31 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 %25 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %callee_ = OpFunction %void None %3 + %9 = OpLabel + %36 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %37 = OpLoad %float %36 + %38 = OpConvertFToS %int %37 + %39 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpIAdd %uint %40 %uint_1 + %42 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 %38 + OpStore %42 %41 + %43 = OpFunctionCall %void %callee2_ + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/reserved-identifiers.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/reserved-identifiers.asm.frag new file mode 100644 index 0000000..5015cef --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/reserved-identifiers.asm.frag @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 24 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %spvFoo %SPIRV_Cross_blah %_40 %_m40 %_underscore_foo_bar_meep + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %spvFoo "spvFoo" + OpName %SPIRV_Cross_blah "SPIRV_Cross_blah" + OpName %_40 "_40Bar" + OpName %_m40 "_m40" + OpName %_underscore_foo_bar_meep "__underscore_foo__bar_meep__" + OpDecorate %spvFoo Location 0 + OpDecorate %SPIRV_Cross_blah Location 1 + OpDecorate %_40 Location 2 + OpDecorate %_m40 Location 3 + OpDecorate %_underscore_foo_bar_meep Location 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %spvFoo = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%SPIRV_Cross_blah = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %_40 = OpVariable %_ptr_Output_v4float Output + %float_2 = OpConstant %float 2 + %17 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %_m40 = OpVariable %_ptr_Output_v4float Output + %float_3 = OpConstant %float 3 + %20 = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3 +%_underscore_foo_bar_meep = OpVariable %_ptr_Output_v4float Output + %float_4 = OpConstant %float 4 + %23 = OpConstantComposite %v4float %float_4 %float_4 %float_4 %float_4 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %spvFoo %11 + OpStore %SPIRV_Cross_blah %14 + OpStore %_40 %17 + OpStore %_m40 %20 + OpStore %_underscore_foo_bar_meep %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/selection-merge-to-continue.asm.invalid.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/selection-merge-to-continue.asm.invalid.frag new file mode 100644 index 0000000..ecc4915 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/selection-merge-to-continue.asm.invalid.frag @@ -0,0 +1,85 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 55 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %v0 + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %i "i" + OpName %v0 "v0" + OpDecorate %FragColor Location 0 + OpDecorate %v0 Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %11 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 + %bool = OpTypeBool +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v0 = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_20 = OpConstant %float 20 + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpStore %FragColor %11 + OpStore %i %int_0 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %21 = OpLoad %int %i + %24 = OpSLessThan %bool %21 %int_4 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + %30 = OpAccessChain %_ptr_Input_float %v0 %uint_0 + %31 = OpLoad %float %30 + %33 = OpFOrdEqual %bool %31 %float_20 + OpSelectionMerge %19 None + OpBranchConditional %33 %34 %44 + %34 = OpLabel + %36 = OpLoad %int %i + %38 = OpBitwiseAnd %int %36 %int_3 + %39 = OpAccessChain %_ptr_Input_float %v0 %38 + %40 = OpLoad %float %39 + %41 = OpLoad %v4float %FragColor + %42 = OpCompositeConstruct %v4float %40 %40 %40 %40 + %43 = OpFAdd %v4float %41 %42 + OpStore %FragColor %43 + OpBranch %19 + %44 = OpLabel + %45 = OpLoad %int %i + %47 = OpBitwiseAnd %int %45 %int_1 + %48 = OpAccessChain %_ptr_Input_float %v0 %47 + %49 = OpLoad %float %48 + %50 = OpLoad %v4float %FragColor + %51 = OpCompositeConstruct %v4float %49 %49 %49 %49 + %52 = OpFAdd %v4float %50 %51 + OpStore %FragColor %52 + OpBranch %19 + %19 = OpLabel + %53 = OpLoad %int %i + %54 = OpIAdd %int %53 %int_1 + OpStore %i %54 + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/sparse-texture-feedback-uint-code.asm.desktop.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/sparse-texture-feedback-uint-code.asm.desktop.frag new file mode 100644 index 0000000..9b2eb72 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/sparse-texture-feedback-uint-code.asm.desktop.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 30 +; Schema: 0 + OpCapability Shader + OpCapability SparseResidency + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_ARB_sparse_texture2" + OpSourceExtension "GL_ARB_sparse_texture_clamp" + OpName %main "main" + OpName %ret "ret" + OpName %uSamp "uSamp" + OpName %vUV "vUV" + OpName %texel "texel" + OpName %ResType "ResType" + OpName %FragColor "FragColor" + OpDecorate %uSamp DescriptorSet 0 + OpDecorate %uSamp Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %uSamp = OpVariable %_ptr_UniformConstant_11 UniformConstant + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %uint = OpTypeInt 32 0 + %ResType = OpTypeStruct %uint %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %ret = OpVariable %_ptr_Function_bool Function + %texel = OpVariable %_ptr_Function_v4float Function + %14 = OpLoad %11 %uSamp + %18 = OpLoad %v2float %vUV + %24 = OpImageSparseSampleImplicitLod %ResType %14 %18 + %25 = OpCompositeExtract %v4float %24 1 + OpStore %texel %25 + %26 = OpCompositeExtract %uint %24 0 + %27 = OpImageSparseTexelsResident %bool %26 + OpStore %ret %27 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/subgroup-arithmetic-cast.nocompat.vk.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/subgroup-arithmetic-cast.nocompat.vk.asm.frag new file mode 100644 index 0000000..a47c6b7 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/subgroup-arithmetic-cast.nocompat.vk.asm.frag @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 78 +; Schema: 0 + OpCapability Shader + OpCapability GroupNonUniform + OpCapability GroupNonUniformArithmetic + OpCapability GroupNonUniformClustered + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %index %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_KHR_shader_subgroup_arithmetic" + OpSourceExtension "GL_KHR_shader_subgroup_basic" + OpSourceExtension "GL_KHR_shader_subgroup_clustered" + OpName %main "main" + OpName %index "index" + OpName %FragColor "FragColor" + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input + %uint_3 = OpConstant %uint 3 + %uint_4 = OpConstant %uint 4 +%_ptr_Output_uint = OpTypePointer Output %uint + %FragColor = OpVariable %_ptr_Output_uint Output + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpLoad %int %index + %u = OpBitcast %uint %i + %res0 = OpGroupNonUniformSMin %uint %uint_3 Reduce %i + %res1 = OpGroupNonUniformSMax %uint %uint_3 Reduce %u + %res2 = OpGroupNonUniformUMin %uint %uint_3 Reduce %i + %res3 = OpGroupNonUniformUMax %uint %uint_3 Reduce %u + %res4 = OpGroupNonUniformSMax %uint %uint_3 InclusiveScan %i + %res5 = OpGroupNonUniformSMin %uint %uint_3 InclusiveScan %u + %res6 = OpGroupNonUniformUMax %uint %uint_3 ExclusiveScan %i + %res7 = OpGroupNonUniformUMin %uint %uint_3 ExclusiveScan %u + %res8 = OpGroupNonUniformSMin %uint %uint_3 ClusteredReduce %i %uint_4 + %res9 = OpGroupNonUniformSMax %uint %uint_3 ClusteredReduce %u %uint_4 + %res10 = OpGroupNonUniformUMin %uint %uint_3 ClusteredReduce %i %uint_4 + %res11 = OpGroupNonUniformUMax %uint %uint_3 ClusteredReduce %u %uint_4 + OpStore %FragColor %res0 + OpStore %FragColor %res1 + OpStore %FragColor %res2 + OpStore %FragColor %res3 + OpStore %FragColor %res4 + OpStore %FragColor %res5 + OpStore %FragColor %res6 + OpStore %FragColor %res7 + OpStore %FragColor %res8 + OpStore %FragColor %res9 + OpStore %FragColor %res10 + OpStore %FragColor %res11 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag new file mode 100644 index 0000000..b154679 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-block-case-fallthrough.asm.invalid.frag @@ -0,0 +1,80 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vIndex %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %vIndex "vIndex" + OpName %FragColor "FragColor" + OpName %i "i" + OpName %j "j" + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_int = OpTypePointer Function %int + %main = OpFunction %void None %3 + %header = OpLabel + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + %9 = OpLoad %int %vIndex + OpSelectionMerge %switch_merge None + OpSwitch %9 %default_case 100 %default_case 0 %case_0 1 %case_1 11 %case_1 2 %case_2 3 %case_3 4 %case_4 5 %case_5 + + %case_0 = OpLabel + OpBranch %default_case + + %default_case = OpLabel + %default_case_phi = OpPhi %int %int_2 %header %int_3 %case_0 + ; Test what happens when a case block dominates access to a variable. + OpStore %j %default_case_phi + OpBranch %case_1 + + %case_1 = OpLabel + ; Test phi nodes between case labels. + %case_1_phi = OpPhi %int %int_0 %default_case %int_1 %header + OpStore %j %case_1_phi + OpBranch %case_2 + + %case_2 = OpLabel + OpBranch %switch_merge + + %case_3 = OpLabel + ; Conditionally branch to another case block. This is really dumb, but it is apparently legal. + %case_3_cond = OpSGreaterThan %bool %9 %int_3 + OpBranchConditional %case_3_cond %case_4 %switch_merge + + %case_4 = OpLabel + ; When emitted from case 3, we should *not* see fallthrough behavior. + OpBranch %case_5 + + %case_5 = OpLabel + OpStore %i %int_0 + OpBranch %switch_merge + + %switch_merge = OpLabel + %26 = OpLoad %int %i + %27 = OpConvertSToF %float %26 + %28 = OpCompositeConstruct %v4float %27 %27 %27 %27 + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-merge-to-continue.asm.invalid.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-merge-to-continue.asm.invalid.frag new file mode 100644 index 0000000..94ef5f5 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-merge-to-continue.asm.invalid.frag @@ -0,0 +1,85 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 57 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %v0 + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %i "i" + OpName %v0 "v0" + OpDecorate %FragColor Location 0 + OpDecorate %v0 Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %11 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %float_3 = OpConstant %float 3 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v0 = OpVariable %_ptr_Input_v4float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpStore %FragColor %11 + OpStore %i %int_0 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %21 = OpLoad %int %i + %24 = OpSLessThan %bool %21 %int_4 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + %25 = OpLoad %int %i + OpSelectionMerge %19 None + OpSwitch %25 %28 0 %26 1 %27 + %28 = OpLabel + %46 = OpAccessChain %_ptr_Output_float %FragColor %uint_2 + %47 = OpLoad %float %46 + %48 = OpFAdd %float %47 %float_3 + %49 = OpAccessChain %_ptr_Output_float %FragColor %uint_2 + OpStore %49 %48 + OpBranch %19 + %26 = OpLabel + %33 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + %34 = OpLoad %float %33 + %35 = OpFAdd %float %34 %float_1 + %36 = OpAccessChain %_ptr_Output_float %FragColor %uint_0 + OpStore %36 %35 + OpBranch %19 + %27 = OpLabel + %40 = OpAccessChain %_ptr_Output_float %FragColor %uint_1 + %41 = OpLoad %float %40 + %42 = OpFAdd %float %41 %float_3 + %43 = OpAccessChain %_ptr_Output_float %FragColor %uint_1 + OpStore %43 %42 + OpBranch %19 + %19 = OpLabel + %52 = OpLoad %int %i + %54 = OpIAdd %int %52 %int_1 + OpStore %i %54 + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-single-case-multiple-exit-cfg.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-single-case-multiple-exit-cfg.asm.frag new file mode 100644 index 0000000..d2bd15a --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/switch-single-case-multiple-exit-cfg.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 54 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %_GLF_color + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %gl_FragCoord "gl_FragCoord" + OpName %_GLF_color "_GLF_color" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_GLF_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %bool = OpTypeBool + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %_GLF_color = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %52 = OpUndef %v2float + %main = OpFunction %void None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpSwitch %int_0 %8 + %8 = OpLabel + %17 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %18 = OpLoad %float %17 + %22 = OpFOrdNotEqual %bool %18 %18 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + OpBranch %9 + %24 = OpLabel + %33 = OpCompositeExtract %float %52 1 + %51 = OpCompositeInsert %v2float %33 %52 1 + OpBranch %9 + %9 = OpLabel + %53 = OpPhi %v2float %52 %23 %51 %24 + %42 = OpCompositeExtract %float %53 0 + %43 = OpCompositeExtract %float %53 1 + %48 = OpCompositeConstruct %v4float %42 %43 %float_1 %float_1 + OpStore %_GLF_color %48 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/unordered-compare.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/unordered-compare.asm.frag new file mode 100644 index 0000000..4ad8fc5 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/unordered-compare.asm.frag @@ -0,0 +1,177 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 132 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %A %B %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %test_vector_ "test_vector(" + OpName %test_scalar_ "test_scalar(" + OpName %le "le" + OpName %A "A" + OpName %B "B" + OpName %leq "leq" + OpName %ge "ge" + OpName %geq "geq" + OpName %eq "eq" + OpName %neq "neq" + OpName %le_0 "le" + OpName %leq_0 "leq" + OpName %ge_0 "ge" + OpName %geq_0 "geq" + OpName %eq_0 "eq" + OpName %neq_0 "neq" + OpName %FragColor "FragColor" + OpDecorate %A Location 0 + OpDecorate %B Location 1 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %11 = OpTypeFunction %float + %bool = OpTypeBool + %v4bool = OpTypeVector %bool 4 +%_ptr_Function_v4bool = OpTypePointer Function %v4bool +%_ptr_Input_v4float = OpTypePointer Input %v4float + %A = OpVariable %_ptr_Input_v4float Input + %B = OpVariable %_ptr_Input_v4float Input + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %47 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %48 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Function_bool = OpTypePointer Function %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %128 = OpFunctionCall %v4float %test_vector_ + %129 = OpFunctionCall %float %test_scalar_ + %130 = OpCompositeConstruct %v4float %129 %129 %129 %129 + %131 = OpFAdd %v4float %128 %130 + OpStore %FragColor %131 + OpReturn + OpFunctionEnd +%test_vector_ = OpFunction %v4float None %8 + %10 = OpLabel + %le = OpVariable %_ptr_Function_v4bool Function + %leq = OpVariable %_ptr_Function_v4bool Function + %ge = OpVariable %_ptr_Function_v4bool Function + %geq = OpVariable %_ptr_Function_v4bool Function + %eq = OpVariable %_ptr_Function_v4bool Function + %neq = OpVariable %_ptr_Function_v4bool Function + %20 = OpLoad %v4float %A + %22 = OpLoad %v4float %B + %23 = OpFUnordLessThan %v4bool %20 %22 + OpStore %le %23 + %25 = OpLoad %v4float %A + %26 = OpLoad %v4float %B + %27 = OpFUnordLessThanEqual %v4bool %25 %26 + OpStore %leq %27 + %29 = OpLoad %v4float %A + %30 = OpLoad %v4float %B + %31 = OpFUnordGreaterThan %v4bool %29 %30 + OpStore %ge %31 + %33 = OpLoad %v4float %A + %34 = OpLoad %v4float %B + %35 = OpFUnordGreaterThanEqual %v4bool %33 %34 + OpStore %geq %35 + %37 = OpLoad %v4float %A + %38 = OpLoad %v4float %B + %39 = OpFUnordEqual %v4bool %37 %38 + OpStore %eq %39 + %41 = OpLoad %v4float %A + %42 = OpLoad %v4float %B + %43 = OpFUnordNotEqual %v4bool %41 %42 + OpStore %neq %43 + %44 = OpLoad %v4bool %le + %49 = OpSelect %v4float %44 %48 %47 + %50 = OpLoad %v4bool %leq + %51 = OpSelect %v4float %50 %48 %47 + %52 = OpFAdd %v4float %49 %51 + %53 = OpLoad %v4bool %ge + %54 = OpSelect %v4float %53 %48 %47 + %55 = OpFAdd %v4float %52 %54 + %56 = OpLoad %v4bool %geq + %57 = OpSelect %v4float %56 %48 %47 + %58 = OpFAdd %v4float %55 %57 + %59 = OpLoad %v4bool %eq + %60 = OpSelect %v4float %59 %48 %47 + %61 = OpFAdd %v4float %58 %60 + %62 = OpLoad %v4bool %neq + %63 = OpSelect %v4float %62 %48 %47 + %64 = OpFAdd %v4float %61 %63 + OpReturnValue %64 + OpFunctionEnd +%test_scalar_ = OpFunction %float None %11 + %13 = OpLabel + %le_0 = OpVariable %_ptr_Function_bool Function + %leq_0 = OpVariable %_ptr_Function_bool Function + %ge_0 = OpVariable %_ptr_Function_bool Function + %geq_0 = OpVariable %_ptr_Function_bool Function + %eq_0 = OpVariable %_ptr_Function_bool Function + %neq_0 = OpVariable %_ptr_Function_bool Function + %72 = OpAccessChain %_ptr_Input_float %A %uint_0 + %73 = OpLoad %float %72 + %74 = OpAccessChain %_ptr_Input_float %B %uint_0 + %75 = OpLoad %float %74 + %76 = OpFUnordLessThan %bool %73 %75 + OpStore %le_0 %76 + %78 = OpAccessChain %_ptr_Input_float %A %uint_0 + %79 = OpLoad %float %78 + %80 = OpAccessChain %_ptr_Input_float %B %uint_0 + %81 = OpLoad %float %80 + %82 = OpFUnordLessThanEqual %bool %79 %81 + OpStore %leq_0 %82 + %84 = OpAccessChain %_ptr_Input_float %A %uint_0 + %85 = OpLoad %float %84 + %86 = OpAccessChain %_ptr_Input_float %B %uint_0 + %87 = OpLoad %float %86 + %88 = OpFUnordGreaterThan %bool %85 %87 + OpStore %ge_0 %88 + %90 = OpAccessChain %_ptr_Input_float %A %uint_0 + %91 = OpLoad %float %90 + %92 = OpAccessChain %_ptr_Input_float %B %uint_0 + %93 = OpLoad %float %92 + %94 = OpFUnordGreaterThanEqual %bool %91 %93 + OpStore %geq_0 %94 + %96 = OpAccessChain %_ptr_Input_float %A %uint_0 + %97 = OpLoad %float %96 + %98 = OpAccessChain %_ptr_Input_float %B %uint_0 + %99 = OpLoad %float %98 + %100 = OpFUnordEqual %bool %97 %99 + OpStore %eq_0 %100 + %102 = OpAccessChain %_ptr_Input_float %A %uint_0 + %103 = OpLoad %float %102 + %104 = OpAccessChain %_ptr_Input_float %B %uint_0 + %105 = OpLoad %float %104 + %106 = OpFUnordNotEqual %bool %103 %105 + OpStore %neq_0 %106 + %107 = OpLoad %bool %le_0 + %108 = OpSelect %float %107 %float_1 %float_0 + %109 = OpLoad %bool %leq_0 + %110 = OpSelect %float %109 %float_1 %float_0 + %111 = OpFAdd %float %108 %110 + %112 = OpLoad %bool %ge_0 + %113 = OpSelect %float %112 %float_1 %float_0 + %114 = OpFAdd %float %111 %113 + %115 = OpLoad %bool %geq_0 + %116 = OpSelect %float %115 %float_1 %float_0 + %117 = OpFAdd %float %114 %116 + %118 = OpLoad %bool %eq_0 + %119 = OpSelect %float %118 %float_1 %float_0 + %120 = OpFAdd %float %117 %119 + %121 = OpLoad %bool %neq_0 + %122 = OpSelect %float %121 %float_1 %float_0 + %123 = OpFAdd %float %120 %122 + OpReturnValue %123 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/vector-extract-dynamic-spec-constant.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/vector-extract-dynamic-spec-constant.asm.frag new file mode 100644 index 0000000..dda5fc4 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/vector-extract-dynamic-spec-constant.asm.frag @@ -0,0 +1,49 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 27 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vColor "vColor" + OpName %omap_r "omap_r" + OpName %omap_g "omap_g" + OpName %omap_b "omap_b" + OpName %omap_a "omap_a" + OpDecorate %FragColor Location 0 + OpDecorate %vColor Location 0 + OpDecorate %omap_r SpecId 0 + OpDecorate %omap_g SpecId 1 + OpDecorate %omap_b SpecId 2 + OpDecorate %omap_a SpecId 3 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vColor = OpVariable %_ptr_Input_v4float Input + %int = OpTypeInt 32 1 + %omap_r = OpSpecConstant %int 0 +%_ptr_Input_float = OpTypePointer Input %float + %omap_g = OpSpecConstant %int 1 + %omap_b = OpSpecConstant %int 2 + %omap_a = OpSpecConstant %int 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %loaded = OpLoad %v4float %vColor + %r = OpVectorExtractDynamic %float %loaded %omap_r + %g = OpVectorExtractDynamic %float %loaded %omap_g + %b = OpVectorExtractDynamic %float %loaded %omap_b + %a = OpVectorExtractDynamic %float %loaded %omap_a + %rgba = OpCompositeConstruct %v4float %r %g %b %a + OpStore %FragColor %rgba + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag new file mode 100644 index 0000000..22c4efc --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/vector-shuffle-undef-index.asm.frag @@ -0,0 +1,42 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vFloat + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vFloat "vFloat" + OpName %undef "undef" + OpDecorate %FragColor Location 0 + OpDecorate %vFloat Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vFloat = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 +%_ptr_Private_v4float = OpTypePointer Private %v4float + %undef = OpUndef %v4float + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_Private_float = OpTypePointer Private %float + %uint_3 = OpConstant %uint 3 +%_ptr_Input_float = OpTypePointer Input %float + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %v4float %vFloat + %26 = OpVectorShuffle %v4float %13 %undef 4 1 0xffffffff 3 + %27 = OpVectorShuffle %v4float %13 %13 2 1 0xffffffff 3 + %28 = OpFAdd %v4float %26 %27 + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/frag/while-loop-inverted-test.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/frag/while-loop-inverted-test.asm.frag new file mode 100644 index 0000000..d4ae922 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/frag/while-loop-inverted-test.asm.frag @@ -0,0 +1,53 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %i "i" + OpName %j "j" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_20 = OpConstant %int 20 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + OpStore %i %int_0 + OpStore %j %int_0 + OpBranch %11 + %11 = OpLabel + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %16 = OpLoad %int %i + %19 = OpIEqual %bool %16 %int_20 + OpBranchConditional %19 %13 %12 + %12 = OpLabel + %20 = OpLoad %int %j + %21 = OpLoad %int %i + %22 = OpIAdd %int %20 %21 + %24 = OpIAdd %int %22 %int_1 + %25 = OpLoad %int %j + %26 = OpIMul %int %24 %25 + OpStore %j %26 + %27 = OpLoad %int %i + %28 = OpIAdd %int %27 %int_1 + OpStore %i %28 + OpBranch %14 + %14 = OpLabel + OpBranch %11 + %13 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/geom/store-uint-layer.invalid.asm.geom b/third_party/spirv-cross/shaders-no-opt/asm/geom/store-uint-layer.invalid.asm.geom new file mode 100644 index 0000000..550fc4e --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/geom/store-uint-layer.invalid.asm.geom @@ -0,0 +1,130 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 74 +; Schema: 0 + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %stream_pos %stream_layer %input_pos + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 3 + OpSource HLSL 500 + OpName %main "main" + OpName %VertexOutput "VertexOutput" + OpMemberName %VertexOutput 0 "pos" + OpName %GeometryOutput "GeometryOutput" + OpMemberName %GeometryOutput 0 "pos" + OpMemberName %GeometryOutput 1 "layer" + OpName %_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ "@main(struct-VertexOutput-vf41[3];struct-GeometryOutput-vf4-u11;" + OpName %input "input" + OpName %stream "stream" + OpName %output "output" + OpName %v "v" + OpName %stream_pos "stream.pos" + OpName %stream_layer "stream.layer" + OpName %input_0 "input" + OpName %input_pos "input.pos" + OpName %stream_0 "stream" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %stream_pos BuiltIn Position + OpDecorate %stream_layer BuiltIn Layer + OpDecorate %input_pos BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%VertexOutput = OpTypeStruct %v4float + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3 +%_ptr_Function__arr_VertexOutput_uint_3 = OpTypePointer Function %_arr_VertexOutput_uint_3 +%GeometryOutput = OpTypeStruct %v4float %uint +%_ptr_Function_GeometryOutput = OpTypePointer Function %GeometryOutput + %15 = OpTypeFunction %void %_ptr_Function__arr_VertexOutput_uint_3 %_ptr_Function_GeometryOutput + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %uint_1 = OpConstant %uint 1 +%_ptr_Function_uint = OpTypePointer Function %uint +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_3 = OpConstant %int 3 + %bool = OpTypeBool +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %stream_pos = OpVariable %_ptr_Output_v4float Output +%_ptr_Output_uint = OpTypePointer Output %uint +%stream_layer = OpVariable %_ptr_Output_uint Output +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 + %input_pos = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float + %int_2 = OpConstant %int 2 + %main = OpFunction %void None %3 + %5 = OpLabel + %input_0 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %stream_0 = OpVariable %_ptr_Function_GeometryOutput Function + %param = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %param_0 = OpVariable %_ptr_Function_GeometryOutput Function + %58 = OpAccessChain %_ptr_Input_v4float %input_pos %int_0 + %59 = OpLoad %v4float %58 + %60 = OpAccessChain %_ptr_Function_v4float %input_0 %int_0 %int_0 + OpStore %60 %59 + %61 = OpAccessChain %_ptr_Input_v4float %input_pos %int_1 + %62 = OpLoad %v4float %61 + %63 = OpAccessChain %_ptr_Function_v4float %input_0 %int_1 %int_0 + OpStore %63 %62 + %65 = OpAccessChain %_ptr_Input_v4float %input_pos %int_2 + %66 = OpLoad %v4float %65 + %67 = OpAccessChain %_ptr_Function_v4float %input_0 %int_2 %int_0 + OpStore %67 %66 + %70 = OpLoad %_arr_VertexOutput_uint_3 %input_0 + OpStore %param %70 + %72 = OpFunctionCall %void %_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ %param %param_0 + %73 = OpLoad %GeometryOutput %param_0 + OpStore %stream_0 %73 + OpReturn + OpFunctionEnd +%_main_struct_VertexOutput_vf41_3__struct_GeometryOutput_vf4_u11_ = OpFunction %void None %15 + %input = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %stream = OpFunctionParameter %_ptr_Function_GeometryOutput + %19 = OpLabel + %output = OpVariable %_ptr_Function_GeometryOutput Function + %v = OpVariable %_ptr_Function_int Function + %25 = OpAccessChain %_ptr_Function_uint %output %int_1 + OpStore %25 %uint_1 + OpStore %v %int_0 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %int %v + %37 = OpSLessThan %bool %34 %int_3 + OpBranchConditional %37 %30 %31 + %30 = OpLabel + %38 = OpLoad %int %v + %40 = OpAccessChain %_ptr_Function_v4float %input %38 %int_0 + %41 = OpLoad %v4float %40 + %42 = OpAccessChain %_ptr_Function_v4float %output %int_0 + OpStore %42 %41 + %45 = OpAccessChain %_ptr_Function_v4float %output %int_0 + %46 = OpLoad %v4float %45 + OpStore %stream_pos %46 + %49 = OpAccessChain %_ptr_Function_uint %output %int_1 + %50 = OpLoad %uint %49 + OpStore %stream_layer %50 + OpEmitVertex + OpBranch %32 + %32 = OpLabel + %51 = OpLoad %int %v + %52 = OpIAdd %int %51 %int_1 + OpStore %v %52 + OpBranch %29 + %31 = OpLabel + OpEndPrimitive + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/loop-header-self-continue-break.asm.comp b/third_party/spirv-cross/shaders-no-opt/asm/loop-header-self-continue-break.asm.comp new file mode 100644 index 0000000..a38b111 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/loop-header-self-continue-break.asm.comp @@ -0,0 +1,109 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google Clspv; 0 +; Bound: 83 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %19 "main" %gl_GlobalInvocationID + OpSource OpenCL_C 120 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_struct_3 Block + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 0 + OpDecorate %16 DescriptorSet 0 + OpDecorate %16 Binding 1 + OpDecorate %10 SpecId 0 + OpDecorate %11 SpecId 1 + OpDecorate %12 SpecId 2 + %float = OpTypeFloat 32 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_3 = OpTypeStruct %_runtimearr_float +%_ptr_StorageBuffer__struct_3 = OpTypePointer StorageBuffer %_struct_3 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Private_v3uint = OpTypePointer Private %v3uint + %10 = OpSpecConstant %uint 1 + %11 = OpSpecConstant %uint 1 + %12 = OpSpecConstant %uint 1 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %10 %11 %12 + %void = OpTypeVoid + %18 = OpTypeFunction %void +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %uint_35 = OpConstant %uint 35 + %uint_1 = OpConstant %uint 1 + %float_3 = OpConstant %float 3 + %bool = OpTypeBool + %uint_34 = OpConstant %uint 34 + %uint_5 = OpConstant %uint 5 +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %14 = OpVariable %_ptr_Private_v3uint Private %gl_WorkGroupSize + %15 = OpVariable %_ptr_StorageBuffer__struct_3 StorageBuffer + %16 = OpVariable %_ptr_StorageBuffer__struct_3 StorageBuffer + %19 = OpFunction %void None %18 + %20 = OpLabel + %23 = OpAccessChain %_ptr_StorageBuffer_float %15 %uint_0 %uint_0 + %25 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %26 = OpLoad %uint %25 + %27 = OpLoad %float %23 + %29 = OpAccessChain %_ptr_StorageBuffer_float %15 %uint_0 %uint_35 + OpBranch %31 + %31 = OpLabel + %32 = OpPhi %float %27 %20 %67 %58 + %33 = OpPhi %uint %uint_0 %20 %63 %58 + %34 = OpLoad %float %29 + OpLoopMerge %69 %58 None + OpBranch %37 + %37 = OpLabel + %38 = OpPhi %float %46 %37 %32 %31 + %39 = OpPhi %float %38 %37 %34 %31 + %40 = OpPhi %uint %44 %37 %uint_0 %31 + %41 = OpAccessChain %_ptr_StorageBuffer_float %15 %uint_0 %40 + %42 = OpFAdd %float %39 %38 + %44 = OpIAdd %uint %40 %uint_1 + %45 = OpAccessChain %_ptr_StorageBuffer_float %15 %uint_0 %44 + %46 = OpLoad %float %45 + %47 = OpFAdd %float %42 %46 + %49 = OpFDiv %float %47 %float_3 + OpStore %41 %49 + %52 = OpULessThan %bool %40 %uint_34 + %53 = OpLogicalNot %bool %52 + OpLoopMerge %56 %37 None + OpBranchConditional %53 %56 %37 + %56 = OpLabel + OpBranch %58 + %58 = OpLabel + %59 = OpLoad %float %29 + %60 = OpFAdd %float %38 %59 + %61 = OpFAdd %float %32 %60 + %62 = OpFDiv %float %61 %float_3 + OpStore %29 %62 + %63 = OpIAdd %uint %33 %uint_1 + %65 = OpULessThan %bool %33 %uint_5 + %66 = OpLogicalNot %bool %65 + %67 = OpLoad %float %23 + OpBranchConditional %66 %69 %31 + %69 = OpLabel + %70 = OpPhi %float %75 %69 %67 %58 + %71 = OpPhi %uint %76 %69 %uint_1 %58 + %72 = OpAccessChain %_ptr_StorageBuffer_float %15 %uint_0 %71 + %73 = OpLoad %float %72 + %74 = OpFOrdLessThan %bool %70 %73 + %75 = OpSelect %float %74 %73 %70 + %76 = OpIAdd %uint %71 %uint_1 + %77 = OpULessThan %bool %71 %uint_35 + %78 = OpLogicalNot %bool %77 + OpLoopMerge %81 %69 None + OpBranchConditional %78 %81 %69 + %81 = OpLabel + %82 = OpAccessChain %_ptr_StorageBuffer_float %16 %uint_0 %26 + OpStore %82 %75 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/temporary.zero-initialize.asm.frag b/third_party/spirv-cross/shaders-no-opt/asm/temporary.zero-initialize.asm.frag new file mode 100644 index 0000000..eccff08 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/temporary.zero-initialize.asm.frag @@ -0,0 +1,93 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 65 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA RelaxedPrecision + OpDecorate %vA Flat + OpDecorate %vA Location 0 + OpDecorate %25 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %vB RelaxedPrecision + OpDecorate %vB Flat + OpDecorate %vB Location 1 + OpDecorate %38 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %51 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %56 RelaxedPrecision + OpDecorate %64 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %57 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_int = OpTypePointer Input %int + %vA = OpVariable %_ptr_Input_int Input + %bool = OpTypeBool + %int_20 = OpConstant %int 20 + %int_50 = OpConstant %int 50 + %vB = OpVariable %_ptr_Input_int Input + %int_40 = OpConstant %int 40 + %int_60 = OpConstant %int 60 + %int_10 = OpConstant %int 10 + %float_1 = OpConstant %float 1 + %63 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %FragColor %11 + OpBranch %17 + %17 = OpLabel + %60 = OpPhi %int %int_0 %5 %58 %20 + %57 = OpPhi %int %int_0 %5 %56 %20 + %25 = OpLoad %int %vA + %27 = OpSLessThan %bool %57 %25 + OpLoopMerge %19 %20 None + OpBranchConditional %27 %18 %19 + %18 = OpLabel + %30 = OpIAdd %int %25 %57 + %32 = OpIEqual %bool %30 %int_20 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %36 + %33 = OpLabel + OpBranch %34 + %36 = OpLabel + %38 = OpLoad %int %vB + %40 = OpIAdd %int %38 %57 + %42 = OpIEqual %bool %40 %int_40 + %64 = OpSelect %int %42 %int_60 %60 + OpBranch %34 + %34 = OpLabel + %58 = OpPhi %int %int_50 %33 %64 %36 + %49 = OpIAdd %int %58 %int_10 + %51 = OpLoad %v4float %FragColor + %53 = OpFAdd %v4float %51 %63 + OpStore %FragColor %53 + OpBranch %20 + %20 = OpLabel + %56 = OpIAdd %int %57 %49 + OpBranch %17 + %19 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc b/third_party/spirv-cross/shaders-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc new file mode 100644 index 0000000..0fd4dce --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/tesc/tess-fixed-input-array-builtin-array.invalid.asm.tesc @@ -0,0 +1,248 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 162 +; Schema: 0 + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %hs_main "main" %p_pos %p_1 %i_1 %_entryPointOutput_pos %_entryPointOutput %_patchConstantOutput_EdgeTess %_patchConstantOutput_InsideTess + OpExecutionMode %hs_main OutputVertices 3 + OpExecutionMode %hs_main Triangles + OpExecutionMode %hs_main SpacingFractionalOdd + OpExecutionMode %hs_main VertexOrderCw + OpSource HLSL 500 + OpName %hs_main "hs_main" + OpName %VertexOutput "VertexOutput" + OpMemberName %VertexOutput 0 "pos" + OpMemberName %VertexOutput 1 "uv" + OpName %HSOut "HSOut" + OpMemberName %HSOut 0 "pos" + OpMemberName %HSOut 1 "uv" + OpName %_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ "@hs_main(struct-VertexOutput-vf4-vf21[3];u1;" + OpName %p "p" + OpName %i "i" + OpName %HSConstantOut "HSConstantOut" + OpMemberName %HSConstantOut 0 "EdgeTess" + OpMemberName %HSConstantOut 1 "InsideTess" + OpName %PatchHS_struct_VertexOutput_vf4_vf21_3__ "PatchHS(struct-VertexOutput-vf4-vf21[3];" + OpName %patch "patch" + OpName %output "output" + OpName %p_0 "p" + OpName %p_pos "p.pos" + OpName %VertexOutput_0 "VertexOutput" + OpMemberName %VertexOutput_0 0 "uv" + OpName %p_1 "p" + OpName %i_0 "i" + OpName %i_1 "i" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %param_0 "param" + OpName %_entryPointOutput_pos "@entryPointOutput.pos" + OpName %HSOut_0 "HSOut" + OpMemberName %HSOut_0 0 "uv" + OpName %_entryPointOutput "@entryPointOutput" + OpName %_patchConstantResult "@patchConstantResult" + OpName %param_1 "param" + OpName %_patchConstantOutput_EdgeTess "@patchConstantOutput.EdgeTess" + OpName %_patchConstantOutput_InsideTess "@patchConstantOutput.InsideTess" + OpName %output_0 "output" + OpDecorate %p_pos BuiltIn Position + OpDecorate %p_1 Location 0 + OpDecorate %i_1 BuiltIn InvocationId + OpDecorate %_entryPointOutput_pos BuiltIn Position + OpDecorate %_entryPointOutput Location 0 + OpDecorate %_patchConstantOutput_EdgeTess Patch + OpDecorate %_patchConstantOutput_EdgeTess BuiltIn TessLevelOuter + OpDecorate %_patchConstantOutput_InsideTess Patch + OpDecorate %_patchConstantOutput_InsideTess BuiltIn TessLevelInner + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%VertexOutput = OpTypeStruct %v4float %v2float + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_VertexOutput_uint_3 = OpTypeArray %VertexOutput %uint_3 +%_ptr_Function__arr_VertexOutput_uint_3 = OpTypePointer Function %_arr_VertexOutput_uint_3 +%_ptr_Function_uint = OpTypePointer Function %uint + %HSOut = OpTypeStruct %v4float %v2float + %16 = OpTypeFunction %HSOut %_ptr_Function__arr_VertexOutput_uint_3 %_ptr_Function_uint +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%HSConstantOut = OpTypeStruct %_arr_float_uint_3 %float + %23 = OpTypeFunction %HSConstantOut %_ptr_Function__arr_VertexOutput_uint_3 +%_ptr_Function_HSOut = OpTypePointer Function %HSOut + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %int_1 = OpConstant %int 1 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 + %p_pos = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%VertexOutput_0 = OpTypeStruct %v2float +%_arr_VertexOutput_0_uint_3 = OpTypeArray %VertexOutput_0 %uint_3 +%_ptr_Input__arr_VertexOutput_0_uint_3 = OpTypePointer Input %_arr_VertexOutput_0_uint_3 + %p_1 = OpVariable %_ptr_Input__arr_VertexOutput_0_uint_3 Input +%_ptr_Input_v2float = OpTypePointer Input %v2float + %int_2 = OpConstant %int 2 +%_ptr_Input_uint = OpTypePointer Input %uint + %i_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 +%_entryPointOutput_pos = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float + %HSOut_0 = OpTypeStruct %v2float +%_arr_HSOut_0_uint_3 = OpTypeArray %HSOut_0 %uint_3 +%_ptr_Output__arr_HSOut_0_uint_3 = OpTypePointer Output %_arr_HSOut_0_uint_3 +%_entryPointOutput = OpVariable %_ptr_Output__arr_HSOut_0_uint_3 Output +%_ptr_Output_v2float = OpTypePointer Output %v2float + %uint_2 = OpConstant %uint 2 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool +%_ptr_Function_HSConstantOut = OpTypePointer Function %HSConstantOut + %uint_4 = OpConstant %uint 4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%_patchConstantOutput_EdgeTess = OpVariable %_ptr_Output__arr_float_uint_4 Output +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_patchConstantOutput_InsideTess = OpVariable %_ptr_Output__arr_float_uint_2 Output + %float_1 = OpConstant %float 1 + %hs_main = OpFunction %void None %3 + %5 = OpLabel + %p_0 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %i_0 = OpVariable %_ptr_Function_uint Function +%flattenTemp = OpVariable %_ptr_Function_HSOut Function + %param = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %param_0 = OpVariable %_ptr_Function_uint Function +%_patchConstantResult = OpVariable %_ptr_Function_HSConstantOut Function + %param_1 = OpVariable %_ptr_Function__arr_VertexOutput_uint_3 Function + %50 = OpAccessChain %_ptr_Input_v4float %p_pos %int_0 + %51 = OpLoad %v4float %50 + %52 = OpAccessChain %_ptr_Function_v4float %p_0 %int_0 %int_0 + OpStore %52 %51 + %58 = OpAccessChain %_ptr_Input_v2float %p_1 %int_0 %int_0 + %59 = OpLoad %v2float %58 + %60 = OpAccessChain %_ptr_Function_v2float %p_0 %int_0 %int_1 + OpStore %60 %59 + %61 = OpAccessChain %_ptr_Input_v4float %p_pos %int_1 + %62 = OpLoad %v4float %61 + %63 = OpAccessChain %_ptr_Function_v4float %p_0 %int_1 %int_0 + OpStore %63 %62 + %64 = OpAccessChain %_ptr_Input_v2float %p_1 %int_1 %int_0 + %65 = OpLoad %v2float %64 + %66 = OpAccessChain %_ptr_Function_v2float %p_0 %int_1 %int_1 + OpStore %66 %65 + %68 = OpAccessChain %_ptr_Input_v4float %p_pos %int_2 + %69 = OpLoad %v4float %68 + %70 = OpAccessChain %_ptr_Function_v4float %p_0 %int_2 %int_0 + OpStore %70 %69 + %71 = OpAccessChain %_ptr_Input_v2float %p_1 %int_2 %int_0 + %72 = OpLoad %v2float %71 + %73 = OpAccessChain %_ptr_Function_v2float %p_0 %int_2 %int_1 + OpStore %73 %72 + %77 = OpLoad %uint %i_1 + OpStore %i_0 %77 + %80 = OpLoad %_arr_VertexOutput_uint_3 %p_0 + OpStore %param %80 + %82 = OpLoad %uint %i_0 + OpStore %param_0 %82 + %83 = OpFunctionCall %HSOut %_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ %param %param_0 + OpStore %flattenTemp %83 + %86 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %87 = OpLoad %v4float %86 + %94 = OpLoad %uint %i_1 + %89 = OpAccessChain %_ptr_Output_v4float %_entryPointOutput_pos %94 + OpStore %89 %87 + %95 = OpAccessChain %_ptr_Function_v2float %flattenTemp %int_1 + %96 = OpLoad %v2float %95 + %98 = OpAccessChain %_ptr_Output_v2float %_entryPointOutput %94 %int_0 + OpStore %98 %96 + OpControlBarrier %uint_2 %uint_1 %uint_0 + %102 = OpLoad %uint %i_1 + %104 = OpIEqual %bool %102 %int_0 + OpSelectionMerge %106 None + OpBranchConditional %104 %105 %106 + %105 = OpLabel + %110 = OpLoad %_arr_VertexOutput_uint_3 %p_0 + OpStore %param_1 %110 + %111 = OpFunctionCall %HSConstantOut %PatchHS_struct_VertexOutput_vf4_vf21_3__ %param_1 + OpStore %_patchConstantResult %111 + %117 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_0 + %118 = OpLoad %float %117 + %120 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_0 + OpStore %120 %118 + %121 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_1 + %122 = OpLoad %float %121 + %123 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_1 + OpStore %123 %122 + %124 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_0 %int_2 + %125 = OpLoad %float %124 + %126 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_EdgeTess %int_2 + OpStore %126 %125 + %130 = OpAccessChain %_ptr_Function_float %_patchConstantResult %int_1 + %131 = OpLoad %float %130 + %132 = OpAccessChain %_ptr_Output_float %_patchConstantOutput_InsideTess %int_0 + OpStore %132 %131 + OpBranch %106 + %106 = OpLabel + OpReturn + OpFunctionEnd +%_hs_main_struct_VertexOutput_vf4_vf21_3__u1_ = OpFunction %HSOut None %16 + %p = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %i = OpFunctionParameter %_ptr_Function_uint + %20 = OpLabel + %output = OpVariable %_ptr_Function_HSOut Function + %31 = OpLoad %uint %i + %33 = OpAccessChain %_ptr_Function_v4float %p %31 %int_0 + %34 = OpLoad %v4float %33 + %35 = OpAccessChain %_ptr_Function_v4float %output %int_0 + OpStore %35 %34 + %37 = OpLoad %uint %i + %39 = OpAccessChain %_ptr_Function_v2float %p %37 %int_1 + %40 = OpLoad %v2float %39 + %41 = OpAccessChain %_ptr_Function_v2float %output %int_1 + OpStore %41 %40 + %42 = OpLoad %HSOut %output + OpReturnValue %42 + OpFunctionEnd +%PatchHS_struct_VertexOutput_vf4_vf21_3__ = OpFunction %HSConstantOut None %23 + %patch = OpFunctionParameter %_ptr_Function__arr_VertexOutput_uint_3 + %26 = OpLabel + %output_0 = OpVariable %_ptr_Function_HSConstantOut Function + %135 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %136 = OpLoad %v2float %135 + %137 = OpCompositeConstruct %v2float %float_1 %float_1 + %138 = OpFAdd %v2float %137 %136 + %139 = OpCompositeExtract %float %138 0 + %140 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_0 + OpStore %140 %139 + %141 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %142 = OpLoad %v2float %141 + %143 = OpCompositeConstruct %v2float %float_1 %float_1 + %144 = OpFAdd %v2float %143 %142 + %145 = OpCompositeExtract %float %144 0 + %146 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_1 + OpStore %146 %145 + %147 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %148 = OpLoad %v2float %147 + %149 = OpCompositeConstruct %v2float %float_1 %float_1 + %150 = OpFAdd %v2float %149 %148 + %151 = OpCompositeExtract %float %150 0 + %152 = OpAccessChain %_ptr_Function_float %output_0 %int_0 %int_2 + OpStore %152 %151 + %153 = OpAccessChain %_ptr_Function_v2float %patch %int_0 %int_1 + %154 = OpLoad %v2float %153 + %155 = OpCompositeConstruct %v2float %float_1 %float_1 + %156 = OpFAdd %v2float %155 %154 + %157 = OpCompositeExtract %float %156 0 + %158 = OpAccessChain %_ptr_Function_float %output_0 %int_1 + OpStore %158 %157 + %159 = OpLoad %HSConstantOut %output_0 + OpReturnValue %159 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/vert/complex-link-by-name.asm.vert b/third_party/spirv-cross/shaders-no-opt/asm/vert/complex-link-by-name.asm.vert new file mode 100644 index 0000000..94a883c --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/vert/complex-link-by-name.asm.vert @@ -0,0 +1,119 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 59 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %output_location_0 %output_location_2 %output_location_3 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "Struct_vec4" + OpMemberName %Foo 0 "m0" + OpName %c "c" + OpName %Foo_0 "Struct_vec4" + OpMemberName %Foo_0 0 "m0" + OpName %Bar "Struct_vec4" + OpMemberName %Bar 0 "m0" + OpName %UBO "UBO" + OpMemberName %UBO 0 "m0" + OpMemberName %UBO 1 "m1" + OpName %ubo_binding_0 "ubo_binding_0" + OpName %Bar_0 "Struct_vec4" + OpMemberName %Bar_0 0 "m0" + OpName %b "b" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpName %VertexOut "VertexOut" + OpMemberName %VertexOut 0 "m0" + OpMemberName %VertexOut 1 "m1" + OpName %output_location_0 "output_location_0" + OpName %output_location_2 "output_location_2" + OpName %output_location_3 "output_location_3" + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %Bar 0 Offset 0 + OpMemberDecorate %UBO 0 Offset 0 + OpMemberDecorate %UBO 1 Offset 16 + OpDecorate %UBO Block + OpDecorate %ubo_binding_0 DescriptorSet 0 + OpDecorate %ubo_binding_0 Binding 0 + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpDecorate %VertexOut Block + OpDecorate %output_location_0 Location 0 + OpDecorate %output_location_2 Location 2 + OpDecorate %output_location_3 Location 3 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Foo = OpTypeStruct %v4float +%_ptr_Function_Foo = OpTypePointer Function %Foo + %Foo_0 = OpTypeStruct %v4float + %Bar = OpTypeStruct %v4float + %UBO = OpTypeStruct %Foo_0 %Bar +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO +%ubo_binding_0 = OpVariable %_ptr_Uniform_UBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %Bar_0 = OpTypeStruct %v4float +%_ptr_Function_Bar_0 = OpTypePointer Function %Bar_0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_Bar = OpTypePointer Uniform %Bar + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output +%_ptr_Output_v4float = OpTypePointer Output %v4float + %VertexOut = OpTypeStruct %Foo %Bar_0 +%_ptr_Output_VertexOut = OpTypePointer Output %VertexOut +%output_location_0 = OpVariable %_ptr_Output_VertexOut Output +%_ptr_Output_Foo = OpTypePointer Output %Foo +%_ptr_Output_Bar_0 = OpTypePointer Output %Bar_0 +%output_location_2 = OpVariable %_ptr_Output_Foo Output +%output_location_3 = OpVariable %_ptr_Output_Bar_0 Output + %main = OpFunction %void None %3 + %5 = OpLabel + %c = OpVariable %_ptr_Function_Foo Function + %b = OpVariable %_ptr_Function_Bar_0 Function + %19 = OpAccessChain %_ptr_Uniform_Foo_0 %ubo_binding_0 %int_0 + %20 = OpLoad %Foo_0 %19 + %21 = OpCompositeExtract %v4float %20 0 + %23 = OpAccessChain %_ptr_Function_v4float %c %int_0 + OpStore %23 %21 + %29 = OpAccessChain %_ptr_Uniform_Bar %ubo_binding_0 %int_1 + %30 = OpLoad %Bar %29 + %31 = OpCompositeExtract %v4float %30 0 + %32 = OpAccessChain %_ptr_Function_v4float %b %int_0 + OpStore %32 %31 + %39 = OpAccessChain %_ptr_Function_v4float %c %int_0 + %40 = OpLoad %v4float %39 + %41 = OpAccessChain %_ptr_Function_v4float %b %int_0 + %42 = OpLoad %v4float %41 + %43 = OpFAdd %v4float %40 %42 + %45 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %45 %43 + %49 = OpLoad %Foo %c + %51 = OpAccessChain %_ptr_Output_Foo %output_location_0 %int_0 + OpStore %51 %49 + %52 = OpLoad %Bar_0 %b + %54 = OpAccessChain %_ptr_Output_Bar_0 %output_location_0 %int_1 + OpStore %54 %52 + %56 = OpLoad %Foo %c + OpStore %output_location_2 %56 + %58 = OpLoad %Bar_0 %b + OpStore %output_location_3 %58 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/vert/complex-link-by-name.force-flattened-io.legacy.asm.vert b/third_party/spirv-cross/shaders-no-opt/asm/vert/complex-link-by-name.force-flattened-io.legacy.asm.vert new file mode 100644 index 0000000..94a883c --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/vert/complex-link-by-name.force-flattened-io.legacy.asm.vert @@ -0,0 +1,119 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 59 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ %output_location_0 %output_location_2 %output_location_3 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "Struct_vec4" + OpMemberName %Foo 0 "m0" + OpName %c "c" + OpName %Foo_0 "Struct_vec4" + OpMemberName %Foo_0 0 "m0" + OpName %Bar "Struct_vec4" + OpMemberName %Bar 0 "m0" + OpName %UBO "UBO" + OpMemberName %UBO 0 "m0" + OpMemberName %UBO 1 "m1" + OpName %ubo_binding_0 "ubo_binding_0" + OpName %Bar_0 "Struct_vec4" + OpMemberName %Bar_0 0 "m0" + OpName %b "b" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpName %VertexOut "VertexOut" + OpMemberName %VertexOut 0 "m0" + OpMemberName %VertexOut 1 "m1" + OpName %output_location_0 "output_location_0" + OpName %output_location_2 "output_location_2" + OpName %output_location_3 "output_location_3" + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %Bar 0 Offset 0 + OpMemberDecorate %UBO 0 Offset 0 + OpMemberDecorate %UBO 1 Offset 16 + OpDecorate %UBO Block + OpDecorate %ubo_binding_0 DescriptorSet 0 + OpDecorate %ubo_binding_0 Binding 0 + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpDecorate %VertexOut Block + OpDecorate %output_location_0 Location 0 + OpDecorate %output_location_2 Location 2 + OpDecorate %output_location_3 Location 3 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Foo = OpTypeStruct %v4float +%_ptr_Function_Foo = OpTypePointer Function %Foo + %Foo_0 = OpTypeStruct %v4float + %Bar = OpTypeStruct %v4float + %UBO = OpTypeStruct %Foo_0 %Bar +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO +%ubo_binding_0 = OpVariable %_ptr_Uniform_UBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %Bar_0 = OpTypeStruct %v4float +%_ptr_Function_Bar_0 = OpTypePointer Function %Bar_0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_Bar = OpTypePointer Uniform %Bar + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output +%_ptr_Output_v4float = OpTypePointer Output %v4float + %VertexOut = OpTypeStruct %Foo %Bar_0 +%_ptr_Output_VertexOut = OpTypePointer Output %VertexOut +%output_location_0 = OpVariable %_ptr_Output_VertexOut Output +%_ptr_Output_Foo = OpTypePointer Output %Foo +%_ptr_Output_Bar_0 = OpTypePointer Output %Bar_0 +%output_location_2 = OpVariable %_ptr_Output_Foo Output +%output_location_3 = OpVariable %_ptr_Output_Bar_0 Output + %main = OpFunction %void None %3 + %5 = OpLabel + %c = OpVariable %_ptr_Function_Foo Function + %b = OpVariable %_ptr_Function_Bar_0 Function + %19 = OpAccessChain %_ptr_Uniform_Foo_0 %ubo_binding_0 %int_0 + %20 = OpLoad %Foo_0 %19 + %21 = OpCompositeExtract %v4float %20 0 + %23 = OpAccessChain %_ptr_Function_v4float %c %int_0 + OpStore %23 %21 + %29 = OpAccessChain %_ptr_Uniform_Bar %ubo_binding_0 %int_1 + %30 = OpLoad %Bar %29 + %31 = OpCompositeExtract %v4float %30 0 + %32 = OpAccessChain %_ptr_Function_v4float %b %int_0 + OpStore %32 %31 + %39 = OpAccessChain %_ptr_Function_v4float %c %int_0 + %40 = OpLoad %v4float %39 + %41 = OpAccessChain %_ptr_Function_v4float %b %int_0 + %42 = OpLoad %v4float %41 + %43 = OpFAdd %v4float %40 %42 + %45 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %45 %43 + %49 = OpLoad %Foo %c + %51 = OpAccessChain %_ptr_Output_Foo %output_location_0 %int_0 + OpStore %51 %49 + %52 = OpLoad %Bar_0 %b + %54 = OpAccessChain %_ptr_Output_Bar_0 %output_location_0 %int_1 + OpStore %54 %52 + %56 = OpLoad %Foo %c + OpStore %output_location_2 %56 + %58 = OpLoad %Bar_0 %b + OpStore %output_location_3 %58 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/vert/empty-struct-composite.asm.vert b/third_party/spirv-cross/shaders-no-opt/asm/vert/empty-struct-composite.asm.vert new file mode 100644 index 0000000..038ecaa --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/vert/empty-struct-composite.asm.vert @@ -0,0 +1,36 @@ +; SPIR-V +; Version: 1.1 +; Generator: Google rspirv; 0 +; Bound: 17 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "main" + OpName %Test "Test" + OpName %t "t" + OpName %retvar "retvar" + OpName %main "main" + OpName %retvar_0 "retvar" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %Test = OpTypeStruct +%_ptr_Function_Test = OpTypePointer Function %Test +%_ptr_Function_void = OpTypePointer Function %void + %2 = OpFunction %void None %6 + %7 = OpLabel + %t = OpVariable %_ptr_Function_Test Function + %retvar = OpVariable %_ptr_Function_void Function + OpBranch %4 + %4 = OpLabel + %13 = OpCompositeConstruct %Test + OpStore %t %13 + OpReturn + OpFunctionEnd + %main = OpFunction %void None %6 + %15 = OpLabel + %retvar_0 = OpVariable %_ptr_Function_void Function + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/asm/vert/semantic-decoration.asm.vert b/third_party/spirv-cross/shaders-no-opt/asm/vert/semantic-decoration.asm.vert new file mode 100644 index 0000000..76007c3 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/asm/vert/semantic-decoration.asm.vert @@ -0,0 +1,68 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_decorate_string" + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_entryPointOutput_p %_entryPointOutput_c + OpSource HLSL 500 + OpName %main "main" + OpName %VOut "VOut" + OpMemberName %VOut 0 "p" + OpMemberName %VOut 1 "c" + OpName %_main_ "@main(" + OpName %v "v" + OpName %flattenTemp "flattenTemp" + OpName %_entryPointOutput_p "@entryPointOutput.p" + OpName %_entryPointOutput_c "@entryPointOutput.c" + OpMemberDecorateStringGOOGLE %VOut 0 HlslSemanticGOOGLE "SV_POSITION" + OpMemberDecorateStringGOOGLE %VOut 1 HlslSemanticGOOGLE "COLOR" + OpDecorate %_entryPointOutput_p BuiltIn Position + OpDecorateStringGOOGLE %_entryPointOutput_p HlslSemanticGOOGLE "SV_POSITION" + OpDecorate %_entryPointOutput_c Location 0 + OpDecorateStringGOOGLE %_entryPointOutput_c HlslSemanticGOOGLE "COLOR" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %VOut = OpTypeStruct %v4float %v4float + %9 = OpTypeFunction %VOut +%_ptr_Function_VOut = OpTypePointer Function %VOut + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %17 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %int_1 = OpConstant %int 1 + %float_2 = OpConstant %float 2 + %22 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_p = OpVariable %_ptr_Output_v4float Output +%_entryPointOutput_c = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel +%flattenTemp = OpVariable %_ptr_Function_VOut Function + %28 = OpFunctionCall %VOut %_main_ + OpStore %flattenTemp %28 + %31 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %32 = OpLoad %v4float %31 + OpStore %_entryPointOutput_p %32 + %34 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_1 + %35 = OpLoad %v4float %34 + OpStore %_entryPointOutput_c %35 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %VOut None %9 + %11 = OpLabel + %v = OpVariable %_ptr_Function_VOut Function + %19 = OpAccessChain %_ptr_Function_v4float %v %int_0 + OpStore %19 %17 + %23 = OpAccessChain %_ptr_Function_v4float %v %int_1 + OpStore %23 %22 + %24 = OpLoad %VOut %v + OpReturnValue %24 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/comp/bitcast-16bit-1.invalid.comp b/third_party/spirv-cross/shaders-no-opt/comp/bitcast-16bit-1.invalid.comp new file mode 100644 index 0000000..0c21cda --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/bitcast-16bit-1.invalid.comp @@ -0,0 +1,23 @@ +#version 450 core +#extension GL_AMD_gpu_shader_half_float : require +#extension GL_AMD_gpu_shader_int16 : require +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + i16vec4 inputs[]; +}; + +layout(binding = 1, std430) buffer SSBO1 +{ + ivec4 outputs[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + f16vec2 a = int16BitsToFloat16(inputs[ident].xy); + outputs[ident].x = int(packFloat2x16(a + f16vec2(1, 1))); + outputs[ident].y = packInt2x16(inputs[ident].zw); + outputs[ident].z = int(packUint2x16(u16vec2(inputs[ident].xy))); +} diff --git a/third_party/spirv-cross/shaders-no-opt/comp/bitcast-16bit-2.invalid.comp b/third_party/spirv-cross/shaders-no-opt/comp/bitcast-16bit-2.invalid.comp new file mode 100644 index 0000000..6bb6624 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/bitcast-16bit-2.invalid.comp @@ -0,0 +1,26 @@ +#version 450 core +#extension GL_AMD_gpu_shader_half_float : require +#extension GL_AMD_gpu_shader_int16 : require +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + ivec4 inputs[]; +}; + +layout(binding = 1, std430) buffer SSBO1 +{ + i16vec4 outputs[]; +}; + +layout(binding = 2) uniform UBO +{ + f16vec4 const0; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + outputs[ident].xy = unpackInt2x16(inputs[ident].x) + float16BitsToInt16(const0.xy); + outputs[ident].zw = i16vec2(unpackUint2x16(uint(inputs[ident].y)) - float16BitsToUint16(const0.zw)); +} diff --git a/third_party/spirv-cross/shaders-no-opt/comp/bitfield.comp b/third_party/spirv-cross/shaders-no-opt/comp/bitfield.comp new file mode 100644 index 0000000..d75b556 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/bitfield.comp @@ -0,0 +1,21 @@ +#version 310 es + +void main() +{ + int signed_value = 0; + uint unsigned_value = 0u; + + int s = bitfieldExtract(signed_value, 5, 20); + uint u = bitfieldExtract(unsigned_value, 6, 21); + s = bitfieldInsert(s, 40, 5, 4); + u = bitfieldInsert(u, 60u, 5, 4); + + u = bitfieldReverse(u); + s = bitfieldReverse(s); + + int v0 = bitCount(u); + int v1 = bitCount(s); + + int v2 = findMSB(u); + int v3 = findLSB(s); +} diff --git a/third_party/spirv-cross/shaders-no-opt/comp/glsl.std450.comp b/third_party/spirv-cross/shaders-no-opt/comp/glsl.std450.comp new file mode 100644 index 0000000..a17a82b --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/glsl.std450.comp @@ -0,0 +1,129 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + float res; + int ires; + uint ures; + + vec4 f32; + ivec4 s32; + uvec4 u32; + + mat2 m2; + mat3 m3; + mat4 m4; +}; + +void main() +{ + float tmp; + vec2 v2; + vec3 v3; + vec4 v4; + int itmp; + + res = round(f32.x); + res = roundEven(f32.x); + res = trunc(f32.x); + res = abs(f32.x); + ires = abs(s32.x); + res = sign(f32.x); + ires = sign(s32.x); + res = floor(f32.x); + res = ceil(f32.x); + res = fract(f32.x); + res = radians(f32.x); + res = degrees(f32.x); + res = sin(f32.x); + res = cos(f32.x); + res = tan(f32.x); + res = asin(f32.x); + res = acos(f32.x); + res = atan(f32.x); + res = sinh(f32.x); + res = cosh(f32.x); + res = tanh(f32.x); + res = asinh(f32.x); + res = acosh(f32.x); + res = atanh(f32.x); + res = atan(f32.x, f32.y); + res = pow(f32.x, f32.y); + res = exp(f32.x); + res = log(f32.x); + res = exp2(f32.x); + res = log2(f32.x); + res = sqrt(f32.x); + res = inversesqrt(f32.x); + + res = length(f32.x); + res = distance(f32.x, f32.y); + res = normalize(f32.x); + res = faceforward(f32.x, f32.y, f32.z); + res = reflect(f32.x, f32.y); + res = refract(f32.x, f32.y, f32.z); + + res = length(f32.xy); + res = distance(f32.xy, f32.zw); + v2 = normalize(f32.xy); + v2 = faceforward(f32.xy, f32.yz, f32.zw); + v2 = reflect(f32.xy, f32.zw); + v2 = refract(f32.xy, f32.yz, f32.w); + + v3 = cross(f32.xyz, f32.yzw); + + res = determinant(m2); + res = determinant(m3); + res = determinant(m4); + m2 = inverse(m2); + m3 = inverse(m3); + m4 = inverse(m4); + + res = modf(f32.x, tmp); + // ModfStruct + + res = min(f32.x, f32.y); + ures = min(u32.x, u32.y); + ires = min(s32.x, s32.y); + res = max(f32.x, f32.y); + ures = max(u32.x, u32.y); + ires = max(s32.x, s32.y); + + res = clamp(f32.x, f32.y, f32.z); + ures = clamp(u32.x, u32.y, u32.z); + ires = clamp(s32.x, s32.y, s32.z); + + res = mix(f32.x, f32.y, f32.z); + res = step(f32.x, f32.y); + res = smoothstep(f32.x, f32.y, f32.z); + res = fma(f32.x, f32.y, f32.z); + + res = frexp(f32.x, itmp); + // FrexpStruct + res = ldexp(f32.x, itmp); + + ures = packSnorm4x8(f32); + ures = packUnorm4x8(f32); + ures = packSnorm2x16(f32.xy); + ures = packUnorm2x16(f32.xy); + ures = packHalf2x16(f32.xy); + // packDouble2x32 + + v2 = unpackSnorm2x16(u32.x); + v2 = unpackUnorm2x16(u32.x); + v2 = unpackHalf2x16(u32.x); + v4 = unpackSnorm4x8(u32.x); + v4 = unpackUnorm4x8(u32.x); + // unpackDouble2x32 + + s32 = findLSB(s32); + s32 = findLSB(u32); + s32 = findMSB(s32); + s32 = findMSB(u32); + + // interpolateAtSample + // interpolateAtOffset + + // NMin, NMax, NClamp +} diff --git a/third_party/spirv-cross/shaders-no-opt/comp/illegal-struct-name.asm.comp b/third_party/spirv-cross/shaders-no-opt/comp/illegal-struct-name.asm.comp new file mode 100644 index 0000000..f7a8787 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/illegal-struct-name.asm.comp @@ -0,0 +1,62 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 31 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "Foo" + OpMemberName %Foo 0 "abs" + OpName %f "f" + OpName %Foo_0 "Foo" + OpMemberName %Foo_0 0 "abs" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "foo" + OpMemberName %SSBO 1 "foo2" + OpName %_ "" + OpName %linear "abs" + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 4 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Foo = OpTypeStruct %float +%_ptr_Function_Foo = OpTypePointer Function %Foo + %Foo_0 = OpTypeStruct %float + %SSBO = OpTypeStruct %Foo_0 %Foo_0 +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Function_int = OpTypePointer Function %int + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %main = OpFunction %void None %3 + %5 = OpLabel + %f = OpVariable %_ptr_Function_Foo Function + %linear = OpVariable %_ptr_Function_int Function + %17 = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_0 + %18 = OpLoad %Foo_0 %17 + %19 = OpCompositeExtract %float %18 0 + %21 = OpAccessChain %_ptr_Function_float %f %int_0 + OpStore %21 %19 + OpStore %linear %int_10 + %26 = OpLoad %Foo %f + %27 = OpAccessChain %_ptr_Uniform_Foo_0 %_ %int_1 + %28 = OpCompositeExtract %float %26 0 + %30 = OpAccessChain %_ptr_Uniform_float %27 %int_0 + OpStore %30 %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/comp/inout-struct.invalid.comp b/third_party/spirv-cross/shaders-no-opt/comp/inout-struct.invalid.comp new file mode 100644 index 0000000..c1de959 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/inout-struct.invalid.comp @@ -0,0 +1,55 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) writeonly buffer SSBO +{ + vec4 data[]; +} outdata; + +layout(std430, binding = 1) readonly buffer SSBO2 +{ + vec4 data[]; +} indata; + +struct Foo +{ + vec4 a; + vec4 b; + vec4 c; + vec4 d; +}; + +layout(std430, binding = 2) readonly buffer SSBO3 +{ + Foo foos[]; +} foobar; + +vec4 bar(Foo foo) +{ + return foo.a + foo.b + foo.c + foo.d; +} + +void baz(out Foo foo) +{ + uint ident = gl_GlobalInvocationID.x; + foo.a = indata.data[4u * ident + 0u]; + foo.b = indata.data[4u * ident + 1u]; + foo.c = indata.data[4u * ident + 2u]; + foo.d = indata.data[4u * ident + 3u]; +} + +void meow(inout Foo foo) +{ + foo.a += 10.0; + foo.b += 20.0; + foo.c += 30.0; + foo.d += 40.0; +} + +void main() +{ + Foo foo; + baz(foo); + meow(foo); + outdata.data[gl_GlobalInvocationID.x] = bar(foo) + bar(foobar.foos[gl_GlobalInvocationID.x]); +} diff --git a/third_party/spirv-cross/shaders-no-opt/comp/loop.comp b/third_party/spirv-cross/shaders-no-opt/comp/loop.comp new file mode 100644 index 0000000..6d6c324 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/loop.comp @@ -0,0 +1,98 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idat = in_data[ident]; + + int k = 0; + uint i = 0u; + + if (idat.y == 20.0) + { + do + { + k = k * 2; + i++; + } while (i < ident); + } + + switch (k) + { + case 10: + for (;;) + { + i++; + if (i > 10u) + break; + } + break; + + default: + for (;;) + { + i += 2u; + if (i > 20u) + break; + } + break; + } + + while (k < 10) + { + idat *= 2.0; + k++; + } + + for (uint i = 0u; i < 16u; i++, k++) + for (uint j = 0u; j < 30u; j++) + idat = mvp * idat; + + k = 0; + for (;;) + { + k++; + if (k > 10) + { + k += 2; + } + else + { + k += 3; + continue; + } + + k += 10; + } + + k = 0; + do + { + k++; + } while (k > 10); + + int l = 0; + for (;; l++) + { + if (l == 5) + { + continue; + } + + idat += 1.0; + } + out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/shaders-no-opt/comp/return.comp b/third_party/spirv-cross/shaders-no-opt/comp/return.comp new file mode 100644 index 0000000..617f437 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/return.comp @@ -0,0 +1,33 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + + if (ident == 2u) + { + out_data[ident] = vec4(20.0); + } + else if (ident == 4u) + { + out_data[ident] = vec4(10.0); + return; + } + + for (int i = 0; i < 20; i++) + { + if (i == 10) + break; + + return; + } + + out_data[ident] = vec4(10.0); +} + diff --git a/third_party/spirv-cross/shaders-no-opt/comp/shader_ballot_nonuniform_invocations.invalid.comp b/third_party/spirv-cross/shaders-no-opt/comp/shader_ballot_nonuniform_invocations.invalid.comp new file mode 100644 index 0000000..afcc31d --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/shader_ballot_nonuniform_invocations.invalid.comp @@ -0,0 +1,9 @@ +#version 450 +#extension GL_AMD_shader_ballot : require + +void main () +{ + float addInvocations = addInvocationsNonUniformAMD(0.0); + int minInvocations = minInvocationsNonUniformAMD(1); + uint maxInvocations = maxInvocationsNonUniformAMD(4); +} diff --git a/third_party/spirv-cross/shaders-no-opt/comp/specialization-constant-evaluation.comp b/third_party/spirv-cross/shaders-no-opt/comp/specialization-constant-evaluation.comp new file mode 100644 index 0000000..d45d021 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/specialization-constant-evaluation.comp @@ -0,0 +1,123 @@ +#version 450 + +layout(local_size_x = 1) in; + +layout(constant_id = 0) const bool TRUE = true; +layout(constant_id = 1) const bool FALSE = false; +layout(constant_id = 2) const int SONE = 1; +layout(constant_id = 3) const int STWO = 2; +layout(constant_id = 4) const int SNEG_TWO = -2; +layout(constant_id = 5) const uint UONE = 1; +layout(constant_id = 6) const uint UTWO = 2; +layout(constant_id = 7) const int SNEG_THREE = -3; + +const uint IADD = SONE + STWO + UONE + UTWO; // 6 +const uint ISUB = UTWO - SONE; // 1 +const uint IMUL = UTWO * UTWO; // 4 +const uint UDIV = UTWO / UTWO; // 1 +const int SDIV = STWO / SNEG_TWO; // -1 +//const int SREM = STWO % SNEG_THREE; // 1 +const int SREM = 1; +const int SMOD = STWO % SNEG_THREE; // -1 +const uint UMOD = IADD % IMUL; // 2 + +const uint LSHL = IADD << ISUB; // 12 +const uint RSHL = IADD >> ISUB; // 3 +const int RSHA = (-int(IADD)) >> (-SDIV); // -3 + +const bool IEQ = IADD == ISUB; // false +const bool INEQ = IADD != ISUB; // true +const bool ULT = IADD < ISUB; // false +const bool ULE = IADD <= ISUB; // false +const bool UGT = IADD > ISUB; // true +const bool UGE = IADD >= ISUB; // true + +const bool SLT = SMOD < SREM; // true +const bool SLE = SMOD <= SREM; // true +const bool SGT = SMOD > SREM; // false +const bool SGE = SMOD >= SREM; // false + +const bool LOR = IEQ || SLT; // true +const bool LAND = IEQ && SLT; // false +const bool LNOT = !LOR; // false + +const uint AND = IADD & IADD; // 6 +const uint OR = IADD | ISUB; // 7 +const uint XOR = IADD ^ IADD; // 0 +const uint NOT = ~XOR; // UINT_MAX + +const bool LEQ = LAND == LNOT; // true +const bool LNEQ = LAND != LNOT; // false + +const uint SEL = IEQ ? IADD : ISUB; // 1 + +#define DUMMY_SSBO(name, bind, size) layout(std430, set = 0, binding = bind) buffer SSBO_##name { float val[size]; float dummy; } name + +// Normalize all sizes to 1 element so that the default offsets in glslang matches up with what we should be computing. +// If we do it right, we should get no layout(offset = N) expressions. +DUMMY_SSBO(IAdd, 0, IADD - 5); +DUMMY_SSBO(ISub, 1, ISUB); +DUMMY_SSBO(IMul, 2, IMUL - 3); +DUMMY_SSBO(UDiv, 3, UDIV); +DUMMY_SSBO(SDiv, 4, SDIV + 2); +DUMMY_SSBO(SRem, 5, SREM); +DUMMY_SSBO(SMod, 6, SMOD + 2); +DUMMY_SSBO(UMod, 7, UMOD - 1); +DUMMY_SSBO(LShl, 8, LSHL - 11); +DUMMY_SSBO(RShl, 9, RSHL - 2); +DUMMY_SSBO(RSha, 10, RSHA + 4); +DUMMY_SSBO(IEq, 11, IEQ ? 2 : 1); +DUMMY_SSBO(INeq, 12, INEQ ? 1 : 2); +DUMMY_SSBO(Ult, 13, ULT ? 2 : 1); +DUMMY_SSBO(Ule, 14, ULE ? 2 : 1); +DUMMY_SSBO(Ugt, 15, UGT ? 1 : 2); +DUMMY_SSBO(Uge, 16, UGE ? 1 : 2); +DUMMY_SSBO(Slt, 17, SLT ? 1 : 2); +DUMMY_SSBO(Sle, 18, SLE ? 1 : 2); +DUMMY_SSBO(Sgt, 19, SGT ? 2 : 1); +DUMMY_SSBO(Sge, 20, SGE ? 2 : 1); +DUMMY_SSBO(Lor, 21, LOR ? 1 : 2); +DUMMY_SSBO(Land, 22, LAND ? 2 : 1); +DUMMY_SSBO(Lnot, 23, LNOT ? 2 : 1); +DUMMY_SSBO(And, 24, AND - 5); +DUMMY_SSBO(Or, 24, OR - 6); +DUMMY_SSBO(Xor, 24, XOR + 1); +DUMMY_SSBO(Not, 25, NOT - 0xfffffffeu); +DUMMY_SSBO(Leq, 26, LEQ ? 1 : 2); +DUMMY_SSBO(Lneq, 27, LNEQ ? 2 : 1); +DUMMY_SSBO(Sel, 28, SEL); + +void main() +{ + IAdd.val[0] = 0.0; + ISub.val[0] = 0.0; + IMul.val[0] = 0.0; + UDiv.val[0] = 0.0; + SDiv.val[0] = 0.0; + SRem.val[0] = 0.0; + SMod.val[0] = 0.0; + UMod.val[0] = 0.0; + LShl.val[0] = 0.0; + RShl.val[0] = 0.0; + RSha.val[0] = 0.0; + IEq.val[0] = 0.0; + INeq.val[0] = 0.0; + Ult.val[0] = 0.0; + Ule.val[0] = 0.0; + Ugt.val[0] = 0.0; + Uge.val[0] = 0.0; + Slt.val[0] = 0.0; + Sle.val[0] = 0.0; + Sgt.val[0] = 0.0; + Sge.val[0] = 0.0; + Lor.val[0] = 0.0; + Land.val[0] = 0.0; + Lnot.val[0] = 0.0; + And.val[0] = 0.0; + Or.val[0] = 0.0; + Xor.val[0] = 0.0; + Not.val[0] = 0.0; + Leq.val[0] = 0.0; + Lneq.val[0] = 0.0; + Sel.val[0] = 0.0; +} diff --git a/third_party/spirv-cross/shaders-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp b/third_party/spirv-cross/shaders-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp new file mode 100644 index 0000000..808403d --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/struct-packing-scalar.nocompat.invalid.vk.comp @@ -0,0 +1,88 @@ +#version 310 es +#extension GL_EXT_scalar_block_layout : require + +layout(local_size_x = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + + S4 m3s[8]; +}; + +layout(binding = 1, scalar) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content = ssbo_140.content; +} + diff --git a/third_party/spirv-cross/shaders-no-opt/comp/subgroups.nocompat.invalid.vk.comp b/third_party/spirv-cross/shaders-no-opt/comp/subgroups.nocompat.invalid.vk.comp new file mode 100644 index 0000000..68fc74f --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/subgroups.nocompat.invalid.vk.comp @@ -0,0 +1,125 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_ballot : require +#extension GL_KHR_shader_subgroup_vote : require +#extension GL_KHR_shader_subgroup_shuffle : require +#extension GL_KHR_shader_subgroup_shuffle_relative : require +#extension GL_KHR_shader_subgroup_arithmetic : require +#extension GL_KHR_shader_subgroup_clustered : require +#extension GL_KHR_shader_subgroup_quad : require +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float FragColor; +}; + +void main() +{ + // basic + FragColor = float(gl_NumSubgroups); + FragColor = float(gl_SubgroupID); + FragColor = float(gl_SubgroupSize); + FragColor = float(gl_SubgroupInvocationID); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierShared(); + subgroupMemoryBarrierImage(); + bool elected = subgroupElect(); + + // ballot + FragColor = float(gl_SubgroupEqMask); + FragColor = float(gl_SubgroupGeMask); + FragColor = float(gl_SubgroupGtMask); + FragColor = float(gl_SubgroupLeMask); + FragColor = float(gl_SubgroupLtMask); + vec4 broadcasted = subgroupBroadcast(vec4(10.0), 8u); + vec3 first = subgroupBroadcastFirst(vec3(20.0)); + uvec4 ballot_value = subgroupBallot(true); + bool inverse_ballot_value = subgroupInverseBallot(ballot_value); + bool bit_extracted = subgroupBallotBitExtract(uvec4(10u), 8u); + uint bit_count = subgroupBallotBitCount(ballot_value); + uint inclusive_bit_count = subgroupBallotInclusiveBitCount(ballot_value); + uint exclusive_bit_count = subgroupBallotExclusiveBitCount(ballot_value); + uint lsb = subgroupBallotFindLSB(ballot_value); + uint msb = subgroupBallotFindMSB(ballot_value); + + // shuffle + uint shuffled = subgroupShuffle(10u, 8u); + uint shuffled_xor = subgroupShuffleXor(30u, 8u); + + // shuffle relative + uint shuffled_up = subgroupShuffleUp(20u, 4u); + uint shuffled_down = subgroupShuffleDown(20u, 4u); + + // vote + bool has_all = subgroupAll(true); + bool has_any = subgroupAny(true); + bool has_equal = subgroupAllEqual(true); + + // arithmetic + vec4 added = subgroupAdd(vec4(20.0)); + ivec4 iadded = subgroupAdd(ivec4(20)); + vec4 multiplied = subgroupMul(vec4(20.0)); + ivec4 imultiplied = subgroupMul(ivec4(20)); + vec4 lo = subgroupMin(vec4(20.0)); + vec4 hi = subgroupMax(vec4(20.0)); + ivec4 slo = subgroupMin(ivec4(20)); + ivec4 shi = subgroupMax(ivec4(20)); + uvec4 ulo = subgroupMin(uvec4(20)); + uvec4 uhi = subgroupMax(uvec4(20)); + uvec4 anded = subgroupAnd(ballot_value); + uvec4 ored = subgroupOr(ballot_value); + uvec4 xored = subgroupXor(ballot_value); + + added = subgroupInclusiveAdd(added); + iadded = subgroupInclusiveAdd(iadded); + multiplied = subgroupInclusiveMul(multiplied); + imultiplied = subgroupInclusiveMul(imultiplied); + lo = subgroupInclusiveMin(lo); + hi = subgroupInclusiveMax(hi); + slo = subgroupInclusiveMin(slo); + shi = subgroupInclusiveMax(shi); + ulo = subgroupInclusiveMin(ulo); + uhi = subgroupInclusiveMax(uhi); + anded = subgroupInclusiveAnd(anded); + ored = subgroupInclusiveOr(ored); + xored = subgroupInclusiveXor(ored); + added = subgroupExclusiveAdd(lo); + + added = subgroupExclusiveAdd(multiplied); + multiplied = subgroupExclusiveMul(multiplied); + iadded = subgroupExclusiveAdd(imultiplied); + imultiplied = subgroupExclusiveMul(imultiplied); + lo = subgroupExclusiveMin(lo); + hi = subgroupExclusiveMax(hi); + ulo = subgroupExclusiveMin(ulo); + uhi = subgroupExclusiveMax(uhi); + slo = subgroupExclusiveMin(slo); + shi = subgroupExclusiveMax(shi); + anded = subgroupExclusiveAnd(anded); + ored = subgroupExclusiveOr(ored); + xored = subgroupExclusiveXor(ored); + + // clustered + added = subgroupClusteredAdd(added, 4u); + multiplied = subgroupClusteredMul(multiplied, 4u); + iadded = subgroupClusteredAdd(iadded, 4u); + imultiplied = subgroupClusteredMul(imultiplied, 4u); + lo = subgroupClusteredMin(lo, 4u); + hi = subgroupClusteredMax(hi, 4u); + ulo = subgroupClusteredMin(ulo, 4u); + uhi = subgroupClusteredMax(uhi, 4u); + slo = subgroupClusteredMin(slo, 4u); + shi = subgroupClusteredMax(shi, 4u); + anded = subgroupClusteredAnd(anded, 4u); + ored = subgroupClusteredOr(ored, 4u); + xored = subgroupClusteredXor(xored, 4u); + + // quad + vec4 swap_horiz = subgroupQuadSwapHorizontal(vec4(20.0)); + vec4 swap_vertical = subgroupQuadSwapVertical(vec4(20.0)); + vec4 swap_diagonal = subgroupQuadSwapDiagonal(vec4(20.0)); + vec4 quad_broadcast = subgroupQuadBroadcast(vec4(20.0), 3u); +} diff --git a/third_party/spirv-cross/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp b/third_party/spirv-cross/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp new file mode 100644 index 0000000..833f430 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/comp/subgroups_basicvoteballot.vk.comp @@ -0,0 +1,49 @@ +#version 450 +#extension GL_KHR_shader_subgroup_basic : require +#extension GL_KHR_shader_subgroup_ballot : require +#extension GL_KHR_shader_subgroup_vote : require + +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float FragColor; +}; + +void main() +{ + // basic + FragColor = float(gl_NumSubgroups); + FragColor = float(gl_SubgroupID); + FragColor = float(gl_SubgroupSize); + FragColor = float(gl_SubgroupInvocationID); + subgroupBarrier(); + subgroupMemoryBarrier(); + subgroupMemoryBarrierBuffer(); + subgroupMemoryBarrierShared(); + subgroupMemoryBarrierImage(); + bool elected = subgroupElect(); + + // ballot + FragColor = float(gl_SubgroupEqMask); + FragColor = float(gl_SubgroupGeMask); + FragColor = float(gl_SubgroupGtMask); + FragColor = float(gl_SubgroupLeMask); + FragColor = float(gl_SubgroupLtMask); + vec4 broadcasted = subgroupBroadcast(vec4(10.0), 8u); + vec3 first = subgroupBroadcastFirst(vec3(20.0)); + uvec4 ballot_value = subgroupBallot(true); + bool inverse_ballot_value = subgroupInverseBallot(ballot_value); + bool bit_extracted = subgroupBallotBitExtract(uvec4(10u), 8u); + uint bit_count = subgroupBallotBitCount(ballot_value); + uint inclusive_bit_count = subgroupBallotInclusiveBitCount(ballot_value); + uint exclusive_bit_count = subgroupBallotExclusiveBitCount(ballot_value); + uint lsb = subgroupBallotFindLSB(ballot_value); + uint msb = subgroupBallotFindMSB(ballot_value); + + // vote + bool has_all = subgroupAll(true); + bool has_any = subgroupAny(true); + bool has_equal_bool = subgroupAllEqual(true); + bool has_equal_T = subgroupAllEqual(uvec3(5u)); +} \ No newline at end of file diff --git a/third_party/spirv-cross/shaders-no-opt/frag/16bit-constants.invalid.frag b/third_party/spirv-cross/shaders-no-opt/frag/16bit-constants.invalid.frag new file mode 100644 index 0000000..c53091b --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/16bit-constants.invalid.frag @@ -0,0 +1,14 @@ +#version 450 core + +#extension GL_AMD_gpu_shader_int16 : require +#extension GL_AMD_gpu_shader_half_float : require + +layout(location = 0) out float16_t foo; +layout(location = 1) out int16_t bar; +layout(location = 2) out uint16_t baz; + +void main() { + foo = 1.0hf; + bar = 2s; + baz = 3us; +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/fp16.invalid.desktop.frag b/third_party/spirv-cross/shaders-no-opt/frag/fp16.invalid.desktop.frag new file mode 100644 index 0000000..f3517a9 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/fp16.invalid.desktop.frag @@ -0,0 +1,151 @@ +#version 450 +#extension GL_AMD_gpu_shader_half_float : require + +layout(location = 0) in float16_t v1; +layout(location = 1) in f16vec2 v2; +layout(location = 2) in f16vec3 v3; +layout(location = 3) in f16vec4 v4; + +layout(location = 0) out float o1; +layout(location = 1) out vec2 o2; +layout(location = 2) out vec3 o3; +layout(location = 3) out vec4 o4; + +f16mat2 test_mat2(f16vec2 a, f16vec2 b, f16vec2 c, f16vec2 d) +{ + return f16mat2(a, b) * f16mat2(c, d); +} + +f16mat3 test_mat3(f16vec3 a, f16vec3 b, f16vec3 c, f16vec3 d, f16vec3 e, f16vec3 f) +{ + return f16mat3(a, b, c) * f16mat3(d, e, f); +} + +void test_constants() +{ + float16_t a = 1.0hf; + float16_t b = 1.5hf; + float16_t c = -1.5hf; // Negatives + float16_t d = (0.0hf / 0.0hf); // NaN + float16_t e = (1.0hf / 0.0hf); // +Inf + float16_t f = (-1.0hf / 0.0hf); // -Inf + float16_t g = 1014.0hf; // Large. + float16_t h = 0.000001hf; // Denormal +} + +float16_t test_result() +{ + return 1.0hf; +} + +void test_conversions() +{ + float16_t one = test_result(); + int a = int(one); + uint b = uint(one); + bool c = bool(one); + float d = float(one); + double e = double(one); + float16_t a2 = float16_t(a); + float16_t b2 = float16_t(b); + float16_t c2 = float16_t(c); + float16_t d2 = float16_t(d); + float16_t e2 = float16_t(e); +} + +void test_builtins() +{ + f16vec4 res; + res = radians(v4); + res = degrees(v4); + res = sin(v4); + res = cos(v4); + res = tan(v4); + res = asin(v4); + res = atan(v4, v3.xyzz); + res = atan(v4); + res = sinh(v4); + res = cosh(v4); + res = tanh(v4); + res = asinh(v4); + res = acosh(v4); + res = atanh(v4); + res = pow(v4, v4); + res = exp(v4); + res = log(v4); + res = exp2(v4); + res = log2(v4); + res = sqrt(v4); + res = inversesqrt(v4); + res = abs(v4); + res = sign(v4); + res = floor(v4); + res = trunc(v4); + res = round(v4); + res = roundEven(v4); + res = ceil(v4); + res = fract(v4); + res = mod(v4, v4); + f16vec4 tmp; + res = modf(v4, tmp); + res = min(v4, v4); + res = max(v4, v4); + res = clamp(v4, v4, v4); + res = mix(v4, v4, v4); + res = mix(v4, v4, lessThan(v4, v4)); + res = step(v4, v4); + res = smoothstep(v4, v4, v4); + + bvec4 btmp = isnan(v4); + btmp = isinf(v4); + res = fma(v4, v4, v4); + + ivec4 itmp; + res = frexp(v4, itmp); + res = ldexp(res, itmp); + + uint pack0 = packFloat2x16(v4.xy); + uint pack1 = packFloat2x16(v4.zw); + res = f16vec4(unpackFloat2x16(pack0), unpackFloat2x16(pack1)); + + float16_t t0 = length(v4); + t0 = distance(v4, v4); + t0 = dot(v4, v4); + f16vec3 res3 = cross(v3, v3); + res = normalize(v4); + res = faceforward(v4, v4, v4); + res = reflect(v4, v4); + res = refract(v4, v4, v1); + + btmp = lessThan(v4, v4); + btmp = lessThanEqual(v4, v4); + btmp = greaterThan(v4, v4); + btmp = greaterThanEqual(v4, v4); + btmp = equal(v4, v4); + btmp = notEqual(v4, v4); + + res = dFdx(v4); + res = dFdy(v4); + res = dFdxFine(v4); + res = dFdyFine(v4); + res = dFdxCoarse(v4); + res = dFdyCoarse(v4); + res = fwidth(v4); + res = fwidthFine(v4); + res = fwidthCoarse(v4); + + //res = interpolateAtCentroid(v4); + //res = interpolateAtSample(v4, 0); + //res = interpolateAtOffset(v4, f16vec2(0.1hf)); +} + +void main() +{ + // Basic matrix tests. + f16mat2 m0 = test_mat2(v2, v2, v3.xy, v3.xy); + f16mat3 m1 = test_mat3(v3, v3, v3, v4.xyz, v4.xyz, v4.yzw); + + test_constants(); + test_conversions(); + test_builtins(); +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/fragmentMaskFetch_subpassInput.vk.nocompat.invalid.frag b/third_party/spirv-cross/shaders-no-opt/frag/fragmentMaskFetch_subpassInput.vk.nocompat.invalid.frag new file mode 100644 index 0000000..a3f0366 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/fragmentMaskFetch_subpassInput.vk.nocompat.invalid.frag @@ -0,0 +1,10 @@ +#version 450 +#extension GL_AMD_shader_fragment_mask : require + +layout(input_attachment_index = 0, binding = 0) uniform subpassInputMS t; + +void main () +{ + vec4 test2 = fragmentFetchAMD(t, 4); + uint testi2 = fragmentMaskFetchAMD(t); +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/fs.invalid.frag b/third_party/spirv-cross/shaders-no-opt/frag/fs.invalid.frag new file mode 100644 index 0000000..1ff82de --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/fs.invalid.frag @@ -0,0 +1,14 @@ +#version 450 +#extension GL_AMD_shader_fragment_mask : require +#extension GL_AMD_shader_explicit_vertex_parameter : require + +layout(binding = 0) uniform sampler2DMS texture1; +layout(location = 0) __explicitInterpAMD in vec4 vary; + +void main() +{ + uint testi1 = fragmentMaskFetchAMD(texture1, ivec2(0)); + vec4 test1 = fragmentFetchAMD(texture1, ivec2(1), 2); + + vec4 pos = interpolateAtVertexAMD(vary, 0u); +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/image-gather.frag b/third_party/spirv-cross/shaders-no-opt/frag/image-gather.frag new file mode 100644 index 0000000..b492cfb --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/image-gather.frag @@ -0,0 +1,14 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +layout(set = 0, binding = 0) uniform sampler2D uSamp; +layout(set = 0, binding = 1) uniform sampler2DShadow uSampShadow; +layout(location = 0) in vec3 vUV; + +void main() +{ + FragColor = textureGather(uSamp, vUV.xy, 0); + FragColor += textureGather(uSamp, vUV.xy, 1); + FragColor += textureGather(uSampShadow, vUV.xy, vUV.z); +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/multi-dimensional.desktop.invalid.flatten_dim.frag b/third_party/spirv-cross/shaders-no-opt/frag/multi-dimensional.desktop.invalid.flatten_dim.frag new file mode 100644 index 0000000..24b2ff1 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/multi-dimensional.desktop.invalid.flatten_dim.frag @@ -0,0 +1,18 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uTextures[2][3][1]; +layout(location = 0) flat in int vIndex; +layout(location = 1) in vec2 vUV; + +void main() +{ + vec4 values3[2][3][1]; + + for (int z = 0; z < 2; z++) + for (int y = 0; y < 3; y++) + for (int x = 0; x < 1; x++) + values3[z][y][x] = texture(uTextures[z][y][x], vUV); + + FragColor = values3[1][2][0] + values3[0][2][0] + values3[vIndex + 1][2][vIndex]; +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/pixel-interlock-simple-callstack.frag b/third_party/spirv-cross/shaders-no-opt/frag/pixel-interlock-simple-callstack.frag new file mode 100644 index 0000000..59079fe --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/pixel-interlock-simple-callstack.frag @@ -0,0 +1,31 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require +layout(pixel_interlock_ordered) in; + +layout(set = 0, binding = 0, std430) buffer SSBO0 +{ + uint values0[]; +}; + +layout(set = 0, binding = 1, std430) buffer SSBO1 +{ + uint values1[]; +}; + +void callee2() +{ + values1[int(gl_FragCoord.x)] += 1; +} + +void callee() +{ + values0[int(gl_FragCoord.x)] += 1; + callee2(); +} + +void main() +{ + beginInvocationInterlockARB(); + callee(); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag b/third_party/spirv-cross/shaders-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag new file mode 100644 index 0000000..faa20fa --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/scalar-block-layout-ubo-std430.vk.nocompat.invalid.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_EXT_scalar_block_layout : require + +layout(std430, binding = 0) uniform UBO +{ + float a[1024]; + vec3 b[2]; +}; + +layout(std430, binding = 1) uniform UBOEnhancedLayout +{ + float c[1024]; + vec3 d[2]; + layout(offset = 10000) float e; +}; + +layout(location = 0) flat in int vIndex; +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = a[vIndex] + c[vIndex] + e; +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/sparse-texture-clamp.desktop.frag b/third_party/spirv-cross/shaders-no-opt/frag/sparse-texture-clamp.desktop.frag new file mode 100644 index 0000000..880e67e --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/sparse-texture-clamp.desktop.frag @@ -0,0 +1,23 @@ +#version 450 +#extension GL_ARB_sparse_texture2 : require +#extension GL_ARB_sparse_texture_clamp : require + +layout(set = 0, binding = 0) uniform sampler2D uSamp; +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; + +void main() +{ + vec4 texel; + int code; + + code = sparseTextureClampARB(uSamp, vUV, 1.0, texel, 2.0); + texel = textureClampARB(uSamp, vUV, 1.0, 2.0); + code = sparseTextureOffsetClampARB(uSamp, vUV, ivec2(1, 2), 1.0, texel, 2.0); + texel = textureOffsetClampARB(uSamp, vUV, ivec2(1, 2), 1.0, 2.0); + code = sparseTextureGradClampARB(uSamp, vUV, vec2(1.0), vec2(2.0), 1.0, texel); + texel = textureGradClampARB(uSamp, vUV, vec2(1.0), vec2(2.0), 1.0); + code = sparseTextureGradOffsetClampARB(uSamp, vUV, vec2(1.0), vec2(2.0), ivec2(-1, -2), 1.0, texel); + texel = textureGradOffsetClampARB(uSamp, vUV, vec2(1.0), vec2(2.0), ivec2(-1, -2), 1.0); +} + diff --git a/third_party/spirv-cross/shaders-no-opt/frag/sparse-texture-feedback.desktop.frag b/third_party/spirv-cross/shaders-no-opt/frag/sparse-texture-feedback.desktop.frag new file mode 100644 index 0000000..67cc5b4 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/sparse-texture-feedback.desktop.frag @@ -0,0 +1,31 @@ +#version 450 +#extension GL_ARB_sparse_texture2 : require +#extension GL_ARB_sparse_texture_clamp : require + +layout(set = 0, binding = 0) uniform sampler2D uSamp; +layout(set = 0, binding = 1) uniform sampler2DMS uSampMS; +layout(set = 0, binding = 2, rgba8) uniform image2D uImage; +layout(set = 0, binding = 3, rgba8) uniform image2DMS uImageMS; +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; + +void main() +{ + vec4 texel; + bool ret; + + ret = sparseTexelsResidentARB(sparseTextureARB(uSamp, vUV, texel)); + ret = sparseTexelsResidentARB(sparseTextureARB(uSamp, vUV, texel, 1.1)); + ret = sparseTexelsResidentARB(sparseTextureLodARB(uSamp, vUV, 1.0, texel)); + ret = sparseTexelsResidentARB(sparseTextureOffsetARB(uSamp, vUV, ivec2(1, 1), texel)); + ret = sparseTexelsResidentARB(sparseTextureOffsetARB(uSamp, vUV, ivec2(2, 2), texel, 0.5)); + ret = sparseTexelsResidentARB(sparseTexelFetchARB(uSamp, ivec2(vUV), 1, texel)); + ret = sparseTexelsResidentARB(sparseTexelFetchARB(uSampMS, ivec2(vUV), 2, texel)); + ret = sparseTexelsResidentARB(sparseTexelFetchOffsetARB(uSamp, ivec2(vUV), 1, ivec2(2, 3), texel)); + ret = sparseTexelsResidentARB(sparseTextureLodOffsetARB(uSamp, vUV, 1.5, ivec2(2, 3), texel)); + ret = sparseTexelsResidentARB(sparseTextureGradARB(uSamp, vUV, vec2(1.0), vec2(3.0), texel)); + ret = sparseTexelsResidentARB(sparseTextureGradOffsetARB(uSamp, vUV, vec2(1.0), vec2(3.0), ivec2(-2, -3), texel)); + ret = sparseTexelsResidentARB(sparseTextureClampARB(uSamp, vUV, 4.0, texel)); + ret = sparseTexelsResidentARB(sparseImageLoadARB(uImage, ivec2(vUV), texel)); + ret = sparseTexelsResidentARB(sparseImageLoadARB(uImageMS, ivec2(vUV), 1, texel)); +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.frag b/third_party/spirv-cross/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.frag new file mode 100644 index 0000000..621457a --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.frag @@ -0,0 +1,12 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec3 FragColor; +layout(location = 1) out vec4 FragColor2; + +void main() +{ + FragColor.rgb = subpassLoad(uSubpass0).rgb + subpassLoad(uSubpass1).rgb; +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.legacy.frag b/third_party/spirv-cross/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.legacy.frag new file mode 100644 index 0000000..621457a --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/subpass-input.framebuffer-fetch.nocompat.legacy.frag @@ -0,0 +1,12 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec3 FragColor; +layout(location = 1) out vec4 FragColor2; + +void main() +{ + FragColor.rgb = subpassLoad(uSubpass0).rgb + subpassLoad(uSubpass1).rgb; +} diff --git a/third_party/spirv-cross/shaders-no-opt/frag/variables.zero-initialize.frag b/third_party/spirv-cross/shaders-no-opt/frag/variables.zero-initialize.frag new file mode 100644 index 0000000..41da800 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/frag/variables.zero-initialize.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +int uninit_int; +ivec4 uninit_vector; +mat4 uninit_matrix; + +struct Foo { int a; }; +Foo uninit_foo; + +void main() +{ + int uninit_function_int; + if (vColor.x > 10.0) + uninit_function_int = 10; + else + uninit_function_int = 20; + FragColor = vColor; +} diff --git a/third_party/spirv-cross/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag b/third_party/spirv-cross/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag new file mode 100644 index 0000000..d2bd15a --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/legacy/frag/switch-single-case-multiple-exit-cfg.legacy.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 54 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %_GLF_color + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %gl_FragCoord "gl_FragCoord" + OpName %_GLF_color "_GLF_color" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %_GLF_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %bool = OpTypeBool + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %_GLF_color = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %52 = OpUndef %v2float + %main = OpFunction %void None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpSwitch %int_0 %8 + %8 = OpLabel + %17 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %18 = OpLoad %float %17 + %22 = OpFOrdNotEqual %bool %18 %18 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + OpBranch %9 + %24 = OpLabel + %33 = OpCompositeExtract %float %52 1 + %51 = OpCompositeInsert %v2float %33 %52 1 + OpBranch %9 + %9 = OpLabel + %53 = OpPhi %v2float %52 %23 %51 %24 + %42 = OpCompositeExtract %float %53 0 + %43 = OpCompositeExtract %float %53 1 + %48 = OpCompositeConstruct %v4float %42 %43 %float_1 %float_1 + OpStore %_GLF_color %48 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-no-opt/vert/io-blocks.force-flattened-io.vert b/third_party/spirv-cross/shaders-no-opt/vert/io-blocks.force-flattened-io.vert new file mode 100644 index 0000000..e308a9f --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/vert/io-blocks.force-flattened-io.vert @@ -0,0 +1,25 @@ +#version 450 + +struct Foo +{ + vec4 bar[2]; + vec4 baz[2]; +}; + +layout(location = 0) out Vertex +{ + Foo foo; + Foo foo2; +}; + +layout(location = 8) out Foo foo3; + +void main() +{ + foo.bar[0] = vec4(1.0); + foo.baz[1] = vec4(2.0); + foo2.bar[0] = vec4(3.0); + foo2.baz[1] = vec4(4.0); + foo3.bar[0] = vec4(5.0); + foo3.baz[1] = vec4(6.0); +} diff --git a/third_party/spirv-cross/shaders-no-opt/vert/pass-array-by-value.vert b/third_party/spirv-cross/shaders-no-opt/vert/pass-array-by-value.vert new file mode 100644 index 0000000..2c142a7 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/vert/pass-array-by-value.vert @@ -0,0 +1,26 @@ +#version 310 es + +layout(location = 0) in int Index1; +layout(location = 1) in int Index2; + +vec4 consume_constant_arrays2(const vec4 positions[4], const vec4 positions2[4]) +{ + return positions[Index1] + positions2[Index2]; +} + +vec4 consume_constant_arrays(const vec4 positions[4], const vec4 positions2[4]) +{ + return consume_constant_arrays2(positions, positions2); +} + +const vec4 LUT1[] = vec4[](vec4(0.0), vec4(1.0), vec4(2.0), vec4(3.0)); + +void main() +{ + vec4 LUT2[4]; + LUT2[0] = vec4(10.0); + LUT2[1] = vec4(11.0); + LUT2[2] = vec4(12.0); + LUT2[3] = vec4(13.0); + gl_Position = consume_constant_arrays(LUT1, LUT2); +} diff --git a/third_party/spirv-cross/shaders-no-opt/vulkan/frag/spec-constant.vk.frag b/third_party/spirv-cross/shaders-no-opt/vulkan/frag/spec-constant.vk.frag new file mode 100644 index 0000000..2002c12 --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/vulkan/frag/spec-constant.vk.frag @@ -0,0 +1,77 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(constant_id = 1) const float a = 1.0; +layout(constant_id = 2) const float b = 2.0; +layout(constant_id = 3) const int c = 3; +layout(constant_id = 4) const int d = 4; +layout(constant_id = 5) const uint e = 5u; +layout(constant_id = 6) const uint f = 6u; +layout(constant_id = 7) const bool g = false; +layout(constant_id = 8) const bool h = true; +// glslang doesn't seem to support partial spec constants or composites yet, so only test the basics. + +struct Foo +{ + float elems[d + 2]; +}; + +void main() +{ + float t0 = a; + float t1 = b; + + uint c0 = uint(c); // OpIAdd with different types. + // FConvert, float-to-double. + int c1 = -c; // SNegate + int c2 = ~c; // OpNot + int c3 = c + d; // OpIAdd + int c4 = c - d; // OpISub + int c5 = c * d; // OpIMul + int c6 = c / d; // OpSDiv + uint c7 = e / f; // OpUDiv + int c8 = c % d; // OpSMod + uint c9 = e % f; // OpUMod + // TODO: OpSRem, any way to access this in GLSL? + int c10 = c >> d; // OpShiftRightArithmetic + uint c11 = e >> f; // OpShiftRightLogical + int c12 = c << d; // OpShiftLeftLogical + int c13 = c | d; // OpBitwiseOr + int c14 = c ^ d; // OpBitwiseXor + int c15 = c & d; // OpBitwiseAnd + // VectorShuffle, CompositeExtract, CompositeInsert, not testable atm. + bool c16 = g || h; // OpLogicalOr + bool c17 = g && h; // OpLogicalAnd + bool c18 = !g; // OpLogicalNot + bool c19 = g == h; // OpLogicalEqual + bool c20 = g != h; // OpLogicalNotEqual + // OpSelect not testable atm. + bool c21 = c == d; // OpIEqual + bool c22 = c != d; // OpINotEqual + bool c23 = c < d; // OpSLessThan + bool c24 = e < f; // OpULessThan + bool c25 = c > d; // OpSGreaterThan + bool c26 = e > f; // OpUGreaterThan + bool c27 = c <= d; // OpSLessThanEqual + bool c28 = e <= f; // OpULessThanEqual + bool c29 = c >= d; // OpSGreaterThanEqual + bool c30 = e >= f; // OpUGreaterThanEqual + // OpQuantizeToF16 not testable atm. + + int c31 = c8 + c3; + + int c32 = int(e); // OpIAdd with different types. + bool c33 = bool(c); // int -> bool + bool c34 = bool(e); // uint -> bool + int c35 = int(g); // bool -> int + uint c36 = uint(g); // bool -> uint + float c37 = float(g); // bool -> float + + // Flexible sized arrays with spec constants and spec constant ops. + float vec0[c + 3][8]; + float vec1[c + 2]; + + Foo foo; + FragColor = vec4(t0 + t1) + vec0[0][0] + vec1[0] + foo.elems[c]; +} diff --git a/third_party/spirv-cross/shaders-no-opt/vulkan/frag/ubo-offset-out-of-order.vk.nocompat.frag b/third_party/spirv-cross/shaders-no-opt/vulkan/frag/ubo-offset-out-of-order.vk.nocompat.frag new file mode 100644 index 0000000..6d3987a --- /dev/null +++ b/third_party/spirv-cross/shaders-no-opt/vulkan/frag/ubo-offset-out-of-order.vk.nocompat.frag @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; + +layout(std140, binding = 0) uniform UBO +{ + layout(offset = 16) mat4 m; + layout(offset = 0) vec4 v; +}; + +layout(location = 0) in vec4 vColor; + +void main() +{ + FragColor = m * vColor + v; +} diff --git a/third_party/spirv-cross/shaders-other/README.md b/third_party/spirv-cross/shaders-other/README.md new file mode 100644 index 0000000..6d45481 --- /dev/null +++ b/third_party/spirv-cross/shaders-other/README.md @@ -0,0 +1,4 @@ +These shaders are not actually run yet as part of any test suite, +but are kept here because they have been used to manually test various aspects of SPIRV-Cross in the past. + +These would ideally be part of the test suite in some way. diff --git a/third_party/spirv-cross/shaders-other/aliased-entry-point-names.asm b/third_party/spirv-cross/shaders-other/aliased-entry-point-names.asm new file mode 100644 index 0000000..d60cf30 --- /dev/null +++ b/third_party/spirv-cross/shaders-other/aliased-entry-point-names.asm @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ + OpEntryPoint Vertex %main2 "main2" %_ + OpEntryPoint Fragment %main3 "main" %FragColor + OpEntryPoint Fragment %main4 "main2" %FragColor + OpSource GLSL 450 + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %FragColor Location 0 + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v4floatptr = OpTypePointer Output %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %FragColor = OpVariable %v4floatptr Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %17 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_2 = OpConstant %float 2 + %18 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %6 = OpLabel + %20 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %20 %18 + OpReturn + OpFunctionEnd + %main3 = OpFunction %void None %3 + %7 = OpLabel + OpStore %FragColor %17 + OpReturn + OpFunctionEnd + %main4 = OpFunction %void None %3 + %8 = OpLabel + OpStore %FragColor %18 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/aliased-entry-point-names.asm.multi b/third_party/spirv-cross/shaders-reflection/asm/aliased-entry-point-names.asm.multi new file mode 100644 index 0000000..4a8e60e --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/aliased-entry-point-names.asm.multi @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ + OpEntryPoint Vertex %main2 "maim" %_ + OpEntryPoint Fragment %main3 "main" %FragColor + OpEntryPoint Fragment %main4 "maim" %FragColor + OpSource GLSL 450 + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %FragColor Location 0 + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v4floatptr = OpTypePointer Output %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %FragColor = OpVariable %v4floatptr Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %17 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_2 = OpConstant %float 2 + %18 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %3 + %6 = OpLabel + %20 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %20 %18 + OpReturn + OpFunctionEnd + %main3 = OpFunction %void None %3 + %7 = OpLabel + OpStore %FragColor %17 + OpReturn + OpFunctionEnd + %main4 = OpFunction %void None %3 + %8 = OpLabel + OpStore %FragColor %18 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/comp/pointer-to-array-of-physical-pointer.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/comp/pointer-to-array-of-physical-pointer.asm.comp new file mode 100644 index 0000000..caca050 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/comp/pointer-to-array-of-physical-pointer.asm.comp @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 8 +; Bound: 17 +; Schema: 0 + OpCapability Shader + OpCapability PhysicalStorageBufferAddresses + OpExtension "SPV_EXT_physical_storage_buffer" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel PhysicalStorageBuffer64 GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 460 + OpSourceExtension "GL_EXT_buffer_reference" + OpSourceExtension "GL_EXT_buffer_reference2" + OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types_int64" + OpName %main "main" + OpName %Params "Params" + OpMemberName %Params 0 "x" + OpMemberName %Params 1 "y" + OpName %IntBuf "IntBuf" + OpMemberName %IntBuf 0 "v" + OpName %_ "" + OpDecorate %_arr_7_uint_3 ArrayStride 16 + OpMemberDecorate %Params 0 Offset 0 + OpMemberDecorate %Params 1 Offset 16 + OpDecorate %Params Block + OpMemberDecorate %IntBuf 0 Offset 0 + OpDecorate %IntBuf Block + OpDecorate %_arr__ptr_PhysicalStorageBuffer_IntBuf_uint_3 ArrayStride 16 + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_IntBuf PhysicalStorageBuffer + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_7_uint_3 = OpTypeArray %_ptr_PhysicalStorageBuffer_IntBuf %uint_3 +%ptr_array_ptr = OpTypePointer PhysicalStorageBuffer %_arr_7_uint_3 + %Params = OpTypeStruct %float %ptr_array_ptr + %int = OpTypeInt 32 1 + %IntBuf = OpTypeStruct %int +%_ptr_PhysicalStorageBuffer_IntBuf = OpTypePointer PhysicalStorageBuffer %IntBuf +%_arr__ptr_PhysicalStorageBuffer_IntBuf_uint_3 = OpTypeArray %_ptr_PhysicalStorageBuffer_IntBuf %uint_3 +%_ptr_Uniform_Params = OpTypePointer Uniform %Params + %_ = OpVariable %_ptr_Uniform_Params Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/op-source-glsl-ssbo-1.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/op-source-glsl-ssbo-1.asm.comp new file mode 100644 index 0000000..5785320 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/op-source-glsl-ssbo-1.asm.comp @@ -0,0 +1,53 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID %_ + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "a" + OpName %_ "" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %_runtimearr_v4float_0 ArrayStride 16 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %SSBO0 = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %_ = OpVariable %_ptr_Uniform_SSBO0 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %float_1 = OpConstant %float 1 + %23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_runtimearr_v4float_0 = OpTypeRuntimeArray %v4float + %float_2 = OpConstant %float 2 + %33 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %21 = OpLoad %uint %20 + %25 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %21 + OpStore %25 %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/op-source-glsl-ssbo-2.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/op-source-glsl-ssbo-2.asm.comp new file mode 100644 index 0000000..bfb627b --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/op-source-glsl-ssbo-2.asm.comp @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "a" + OpName %_ "" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "b" + OpName %__0 "" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %_runtimearr_v4float_0 ArrayStride 16 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %SSBO0 = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %_ = OpVariable %_ptr_Uniform_SSBO0 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %float_1 = OpConstant %float 1 + %23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_runtimearr_v4float_0 = OpTypeRuntimeArray %v4float + %SSBO1 = OpTypeStruct %_runtimearr_v4float_0 +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %__0 = OpVariable %_ptr_Uniform_SSBO1 Uniform + %float_2 = OpConstant %float 2 + %33 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %21 = OpLoad %uint %20 + %25 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %21 + OpStore %25 %23 + %34 = OpAccessChain %_ptr_Uniform_v4float %__0 %int_0 %21 + OpStore %34 %33 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/op-source-hlsl-uav-1.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/op-source-hlsl-uav-1.asm.comp new file mode 100644 index 0000000..06d9f89 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/op-source-hlsl-uav-1.asm.comp @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 48 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %threadId + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %UAV0 "UAV0" + OpMemberName %UAV0 0 "@data" + OpName %UAV0_0 "UAV0" + OpName %threadId "threadId" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %UAV0 0 Offset 0 + OpDecorate %UAV0 BufferBlock + OpDecorate %UAV0_0 DescriptorSet 0 + OpDecorate %UAV0_0 Binding 0 + OpDecorate %threadId BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v3int = OpTypeVector %int 3 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %UAV0 = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_UAV0 = OpTypePointer Uniform %UAV0 + %UAV0_0 = OpVariable %_ptr_Uniform_UAV0 Uniform + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %26 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %float_2 = OpConstant %float 2 + %33 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Input_v3int = OpTypePointer Input %v3int + %threadId = OpVariable %_ptr_Input_v3int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %38 = OpLoad %v3int %threadId + %43 = OpCompositeExtract %int %38 0 + %44 = OpAccessChain %_ptr_Uniform_v4float %UAV0_0 %int_0 %43 + OpStore %44 %26 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/op-source-hlsl-uav-2.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/op-source-hlsl-uav-2.asm.comp new file mode 100644 index 0000000..510957c --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/op-source-hlsl-uav-2.asm.comp @@ -0,0 +1,54 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 48 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %threadId + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %UAV0 "UAV0" + OpMemberName %UAV0 0 "@data" + OpName %UAV0_0 "UAV0" + OpName %UAV1 "UAV1" + OpName %threadId "threadId" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %UAV0 0 Offset 0 + OpDecorate %UAV0 BufferBlock + OpDecorate %UAV0_0 DescriptorSet 0 + OpDecorate %UAV0_0 Binding 0 + OpDecorate %UAV1 DescriptorSet 0 + OpDecorate %UAV1 Binding 1 + OpDecorate %threadId BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v3int = OpTypeVector %int 3 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %UAV0 = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_UAV0 = OpTypePointer Uniform %UAV0 + %UAV0_0 = OpVariable %_ptr_Uniform_UAV0 Uniform + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %26 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %UAV1 = OpVariable %_ptr_Uniform_UAV0 Uniform + %float_2 = OpConstant %float 2 + %33 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Input_v3int = OpTypePointer Input %v3int + %threadId = OpVariable %_ptr_Input_v3int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %38 = OpLoad %v3int %threadId + %43 = OpCompositeExtract %int %38 0 + %44 = OpAccessChain %_ptr_Uniform_v4float %UAV0_0 %int_0 %43 + OpStore %44 %26 + %47 = OpAccessChain %_ptr_Uniform_v4float %UAV1 %int_0 %43 + OpStore %47 %33 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/op-source-none-ssbo-1.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/op-source-none-ssbo-1.asm.comp new file mode 100644 index 0000000..8a6b6c6 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/op-source-none-ssbo-1.asm.comp @@ -0,0 +1,52 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID %_ + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "a" + OpName %_ "" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %_runtimearr_v4float_0 ArrayStride 16 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %SSBO0 = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %_ = OpVariable %_ptr_Uniform_SSBO0 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %float_1 = OpConstant %float 1 + %23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_runtimearr_v4float_0 = OpTypeRuntimeArray %v4float + %float_2 = OpConstant %float 2 + %33 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %21 = OpLoad %uint %20 + %25 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %21 + OpStore %25 %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/op-source-none-ssbo-2.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/op-source-none-ssbo-2.asm.comp new file mode 100644 index 0000000..8eaffb3 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/op-source-none-ssbo-2.asm.comp @@ -0,0 +1,64 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "a" + OpName %_ "" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "b" + OpName %__0 "" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %SSBO0 0 Offset 0 + OpDecorate %SSBO0 BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %_runtimearr_v4float_0 ArrayStride 16 + OpMemberDecorate %SSBO1 0 Offset 0 + OpDecorate %SSBO1 BufferBlock + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %SSBO0 = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %_ = OpVariable %_ptr_Uniform_SSBO0 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %float_1 = OpConstant %float 1 + %23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_runtimearr_v4float_0 = OpTypeRuntimeArray %v4float + %SSBO1 = OpTypeStruct %_runtimearr_v4float_0 +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %__0 = OpVariable %_ptr_Uniform_SSBO1 Uniform + %float_2 = OpConstant %float 2 + %33 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %21 = OpLoad %uint %20 + %25 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %21 + OpStore %25 %23 + %34 = OpAccessChain %_ptr_Uniform_v4float %__0 %int_0 %21 + OpStore %34 %33 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/op-source-none-uav-1.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/op-source-none-uav-1.asm.comp new file mode 100644 index 0000000..ef4fa8e --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/op-source-none-uav-1.asm.comp @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 48 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %threadId + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %UAV0 "UAV0" + OpMemberName %UAV0 0 "@data" + OpName %UAV0_0 "UAV0" + OpName %threadId "threadId" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %UAV0 0 Offset 0 + OpDecorate %UAV0 BufferBlock + OpDecorate %UAV0_0 DescriptorSet 0 + OpDecorate %UAV0_0 Binding 0 + OpDecorate %threadId BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v3int = OpTypeVector %int 3 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %UAV0 = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_UAV0 = OpTypePointer Uniform %UAV0 + %UAV0_0 = OpVariable %_ptr_Uniform_UAV0 Uniform + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %26 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %float_2 = OpConstant %float 2 + %33 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Input_v3int = OpTypePointer Input %v3int + %threadId = OpVariable %_ptr_Input_v3int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %38 = OpLoad %v3int %threadId + %43 = OpCompositeExtract %int %38 0 + %44 = OpAccessChain %_ptr_Uniform_v4float %UAV0_0 %int_0 %43 + OpStore %44 %26 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/asm/op-source-none-uav-2.asm.comp b/third_party/spirv-cross/shaders-reflection/asm/op-source-none-uav-2.asm.comp new file mode 100644 index 0000000..3b8d9fd --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/asm/op-source-none-uav-2.asm.comp @@ -0,0 +1,53 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 48 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %threadId + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %UAV0 "UAV0" + OpMemberName %UAV0 0 "@data" + OpName %UAV0_0 "UAV0" + OpName %UAV1 "UAV1" + OpName %threadId "threadId" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %UAV0 0 Offset 0 + OpDecorate %UAV0 BufferBlock + OpDecorate %UAV0_0 DescriptorSet 0 + OpDecorate %UAV0_0 Binding 0 + OpDecorate %UAV1 DescriptorSet 0 + OpDecorate %UAV1 Binding 1 + OpDecorate %threadId BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v3int = OpTypeVector %int 3 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %UAV0 = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_UAV0 = OpTypePointer Uniform %UAV0 + %UAV0_0 = OpVariable %_ptr_Uniform_UAV0 Uniform + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %26 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %UAV1 = OpVariable %_ptr_Uniform_UAV0 Uniform + %float_2 = OpConstant %float 2 + %33 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Input_v3int = OpTypePointer Input %v3int + %threadId = OpVariable %_ptr_Input_v3int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %38 = OpLoad %v3int %threadId + %43 = OpCompositeExtract %int %38 0 + %44 = OpAccessChain %_ptr_Uniform_v4float %UAV0_0 %int_0 %43 + OpStore %44 %26 + %47 = OpAccessChain %_ptr_Uniform_v4float %UAV1 %int_0 %43 + OpStore %47 %33 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/comp/array-of-physical-pointer.comp b/third_party/spirv-cross/shaders-reflection/comp/array-of-physical-pointer.comp new file mode 100644 index 0000000..992f6f9 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/comp/array-of-physical-pointer.comp @@ -0,0 +1,15 @@ +#version 460 +#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable +#extension GL_EXT_buffer_reference2 : enable +layout(buffer_reference, std430, buffer_reference_align = 4) buffer IntBuf +{ + int v; +}; +layout(std140, binding = 0) uniform Params +{ + float x; + IntBuf y[3]; +}; +void main() +{ +} diff --git a/third_party/spirv-cross/shaders-reflection/comp/function-pointer.invalid.asm.comp b/third_party/spirv-cross/shaders-reflection/comp/function-pointer.invalid.asm.comp new file mode 100644 index 0000000..440f331 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/comp/function-pointer.invalid.asm.comp @@ -0,0 +1,19 @@ +; SPIR-V +; Version: 1.5 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 7 +; Schema: 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "main" +OpExecutionMode %2 LocalSize 1 1 1 +OpSource GLSL 450 +OpName %2 "main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypePointer Private %4 +%2 = OpFunction %3 None %4 +%6 = OpLabel +OpReturn +OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/comp/physical-pointer.comp b/third_party/spirv-cross/shaders-reflection/comp/physical-pointer.comp new file mode 100644 index 0000000..ecd1e28 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/comp/physical-pointer.comp @@ -0,0 +1,15 @@ +#version 460 +#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable +#extension GL_EXT_buffer_reference2 : enable +layout(buffer_reference, std430, buffer_reference_align = 4) buffer IntBuf +{ + int v; +}; +layout(std140, binding = 0) uniform Params +{ + float x; + IntBuf y; +}; +void main() +{ +} diff --git a/third_party/spirv-cross/shaders-reflection/comp/struct-layout.comp b/third_party/spirv-cross/shaders-reflection/comp/struct-layout.comp new file mode 100644 index 0000000..5a2b780 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/comp/struct-layout.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct Foo +{ + mat4 m; +}; + +layout(std430, binding = 0) readonly buffer SSBO +{ + Foo in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + Foo out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + out_data[ident].m = in_data[ident].m * in_data[ident].m; +} + diff --git a/third_party/spirv-cross/shaders-reflection/comp/struct-packing.comp b/third_party/spirv-cross/shaders-reflection/comp/struct-packing.comp new file mode 100644 index 0000000..d2ffbae --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/comp/struct-packing.comp @@ -0,0 +1,87 @@ +#version 450 + +layout(local_size_x = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + + S4 m3s[8]; +}; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content = ssbo_140.content; +} + diff --git a/third_party/spirv-cross/shaders-reflection/comp/workgroup-size-spec-constant.comp b/third_party/spirv-cross/shaders-reflection/comp/workgroup-size-spec-constant.comp new file mode 100644 index 0000000..376a351 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/comp/workgroup-size-spec-constant.comp @@ -0,0 +1,13 @@ +#version 450 + +layout(local_size_x_id = 10, local_size_y_id = 40, local_size_z_id = 60) in; + +layout(std430, set = 0, binding = 0) buffer SSBO +{ + vec4 v; +}; + +void main() +{ + v = vec4(10.0); +} diff --git a/third_party/spirv-cross/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag b/third_party/spirv-cross/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag new file mode 100644 index 0000000..2fabb5e --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/frag/combined-texture-sampler-shadow.vk.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform texture2D uDepth; +layout(location = 0) out float FragColor; + +float samp2(texture2D t, mediump samplerShadow s) +{ + return texture(sampler2DShadow(t, s), vec3(1.0)); +} + +float samp3(texture2D t, mediump sampler s) +{ + return texture(sampler2D(t, s), vec2(1.0)).x; +} + +float samp(texture2D t, mediump samplerShadow s, mediump sampler s1) +{ + float r0 = samp2(t, s); + float r1 = samp3(t, s1); + return r0 + r1; +} + +void main() +{ + FragColor = samp(uDepth, uSampler, uSampler1); +} diff --git a/third_party/spirv-cross/shaders-reflection/frag/combined-texture-sampler.vk.frag b/third_party/spirv-cross/shaders-reflection/frag/combined-texture-sampler.vk.frag new file mode 100644 index 0000000..b7de8d4 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/frag/combined-texture-sampler.vk.frag @@ -0,0 +1,47 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler0; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform mediump texture2D uTexture0; +layout(set = 0, binding = 3) uniform mediump texture2D uTexture1; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; + +vec4 sample_dual(mediump sampler samp, mediump texture2D tex) +{ + return texture(sampler2D(tex, samp), vTex); +} + +vec4 sample_global_tex(mediump sampler samp) +{ + vec4 a = texture(sampler2D(uTexture0, samp), vTex); + vec4 b = sample_dual(samp, uTexture1); + return a + b; +} + +vec4 sample_global_sampler(mediump texture2D tex) +{ + vec4 a = texture(sampler2D(tex, uSampler0), vTex); + vec4 b = sample_dual(uSampler1, tex); + return a + b; +} + +vec4 sample_duals() +{ + vec4 a = sample_dual(uSampler0, uTexture0); + vec4 b = sample_dual(uSampler1, uTexture1); + return a + b; +} + +void main() +{ + vec4 c0 = sample_duals(); + vec4 c1 = sample_global_tex(uSampler0); + vec4 c2 = sample_global_tex(uSampler1); + vec4 c3 = sample_global_sampler(uTexture0); + vec4 c4 = sample_global_sampler(uTexture1); + + FragColor = c0 + c1 + c2 + c3 + c4; +} diff --git a/third_party/spirv-cross/shaders-reflection/frag/image-load-store-uint-coord.asm.frag b/third_party/spirv-cross/shaders-reflection/frag/image-load-store-uint-coord.asm.frag new file mode 100644 index 0000000..a9bf1a7 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/frag/image-load-store-uint-coord.asm.frag @@ -0,0 +1,103 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 63 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %storeTemp "storeTemp" + OpName %RWIm "RWIm" + OpName %v "v" + OpName %RWBuf "RWBuf" + OpName %ROIm "ROIm" + OpName %ROBuf "ROBuf" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %RWIm DescriptorSet 0 + OpDecorate %RWIm Binding 1 + OpDecorate %RWBuf DescriptorSet 0 + OpDecorate %RWBuf Binding 0 + OpDecorate %ROIm DescriptorSet 0 + OpDecorate %ROIm Binding 1 + OpDecorate %ROBuf DescriptorSet 0 + OpDecorate %ROBuf Binding 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float + %float_10 = OpConstant %float 10 + %float_0_5 = OpConstant %float 0.5 + %float_8 = OpConstant %float 8 + %float_2 = OpConstant %float 2 + %17 = OpConstantComposite %v4float %float_10 %float_0_5 %float_8 %float_2 + %18 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %RWIm = OpVariable %_ptr_UniformConstant_18 UniformConstant + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %uint_10 = OpConstant %uint 10 + %25 = OpConstantComposite %v2uint %uint_10 %uint_10 + %uint_30 = OpConstant %uint 30 + %30 = OpConstantComposite %v2uint %uint_30 %uint_30 + %32 = OpTypeImage %float Buffer 0 0 0 2 Rgba32f +%_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32 + %RWBuf = OpVariable %_ptr_UniformConstant_32 UniformConstant + %uint_80 = OpConstant %uint 80 + %38 = OpTypeImage %float 2D 0 0 0 1 Unknown + %SampledImage = OpTypeSampledImage %38 +%_ptr_UniformConstant_38 = OpTypePointer UniformConstant %SampledImage + %ROIm = OpVariable %_ptr_UniformConstant_38 UniformConstant + %uint_50 = OpConstant %uint 50 + %uint_60 = OpConstant %uint 60 + %44 = OpConstantComposite %v2uint %uint_50 %uint_60 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %50 = OpTypeImage %float Buffer 0 0 0 1 Rgba32f +%_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50 + %ROBuf = OpVariable %_ptr_UniformConstant_50 UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %62 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %62 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + %storeTemp = OpVariable %_ptr_Function_v4float Function + %v = OpVariable %_ptr_Function_v4float Function + OpStore %storeTemp %17 + %21 = OpLoad %18 %RWIm + %26 = OpLoad %v4float %storeTemp + OpImageWrite %21 %25 %26 + %28 = OpLoad %18 %RWIm + %31 = OpImageRead %v4float %28 %30 + OpStore %v %31 + %35 = OpLoad %32 %RWBuf + %37 = OpLoad %v4float %v + OpImageWrite %35 %uint_80 %37 + %41 = OpLoad %SampledImage %ROIm + %ROImage = OpImage %38 %41 + %47 = OpImageFetch %v4float %ROImage %44 Lod %int_0 + %48 = OpLoad %v4float %v + %49 = OpFAdd %v4float %48 %47 + OpStore %v %49 + %53 = OpLoad %50 %ROBuf + %54 = OpImageFetch %v4float %53 %uint_80 + %55 = OpLoad %v4float %v + %56 = OpFAdd %v4float %55 %54 + OpStore %v %56 + %57 = OpLoad %v4float %v + OpReturnValue %57 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-reflection/frag/input-attachment-ms.vk.frag b/third_party/spirv-cross/shaders-reflection/frag/input-attachment-ms.vk.frag new file mode 100644 index 0000000..e060738 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/frag/input-attachment-ms.vk.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + subpassLoad(uSubpass0, gl_SampleID); +} diff --git a/third_party/spirv-cross/shaders-reflection/frag/input-attachment.vk.frag b/third_party/spirv-cross/shaders-reflection/frag/input-attachment.vk.frag new file mode 100644 index 0000000..f082d15 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/frag/input-attachment.vk.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0) + subpassLoad(uSubpass1); +} diff --git a/third_party/spirv-cross/shaders-reflection/frag/push-constant.vk.frag b/third_party/spirv-cross/shaders-reflection/frag/push-constant.vk.frag new file mode 100644 index 0000000..6180fab --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/frag/push-constant.vk.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor + push.value0 + push.value1; +} diff --git a/third_party/spirv-cross/shaders-reflection/frag/separate-sampler-texture-array.vk.frag b/third_party/spirv-cross/shaders-reflection/frag/separate-sampler-texture-array.vk.frag new file mode 100644 index 0000000..b3501c1 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/frag/separate-sampler-texture-array.vk.frag @@ -0,0 +1,42 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 1) uniform mediump texture2D uTexture[4]; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D[4]; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube[4]; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray[4]; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture[2], samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +vec4 sample_func_dual_array(mediump sampler samp, mediump texture2D tex[4], vec2 uv) +{ + return texture(sampler2D(tex[1], samp), uv); +} + +void main() +{ + vec2 off = 1.0 / vec2(textureSize(sampler2D(uTexture[1], uSampler), 0)); + vec2 off2 = 1.0 / vec2(textureSize(sampler2D(uTexture[2], uSampler), 1)); + + vec4 c0 = sample_func(uSampler, vTex + off + off2); + vec4 c1 = sample_func_dual(uSampler, uTexture[1], vTex + off + off2); + vec4 c2 = sample_func_dual_array(uSampler, uTexture, vTex + off + off2); + vec4 c3 = texture(sampler2DArray(uTextureArray[3], uSampler), vTex3); + vec4 c4 = texture(samplerCube(uTextureCube[1], uSampler), vTex3); + vec4 c5 = texture(sampler3D(uTexture3D[2], uSampler), vTex3); + + FragColor = c0 + c1 + c2 + c3 + c4 + c5; +} diff --git a/third_party/spirv-cross/shaders-reflection/frag/spec-constant.vk.frag b/third_party/spirv-cross/shaders-reflection/frag/spec-constant.vk.frag new file mode 100644 index 0000000..e62a260 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/frag/spec-constant.vk.frag @@ -0,0 +1,78 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(constant_id = 1) const float a = 1.5; +layout(constant_id = 2) const float b = 2.5; +layout(constant_id = 3) const int c = 3; +layout(constant_id = 4) const int d = 4; +layout(constant_id = 5) const uint e = 5u; +layout(constant_id = 6) const uint f = 6u; +layout(constant_id = 7) const bool g = false; +layout(constant_id = 8) const bool h = true; + +// glslang doesn't seem to support partial spec constants or composites yet, so only test the basics. + +struct Foo +{ + float elems[d + 2]; +}; + +void main() +{ + float t0 = a; + float t1 = b; + + uint c0 = uint(c); // OpIAdd with different types. + // FConvert, float-to-double. + int c1 = -c; // SNegate + int c2 = ~c; // OpNot + int c3 = c + d; // OpIAdd + int c4 = c - d; // OpISub + int c5 = c * d; // OpIMul + int c6 = c / d; // OpSDiv + uint c7 = e / f; // OpUDiv + int c8 = c % d; // OpSMod + uint c9 = e % f; // OpUMod + // TODO: OpSRem, any way to access this in GLSL? + int c10 = c >> d; // OpShiftRightArithmetic + uint c11 = e >> f; // OpShiftRightLogical + int c12 = c << d; // OpShiftLeftLogical + int c13 = c | d; // OpBitwiseOr + int c14 = c ^ d; // OpBitwiseXor + int c15 = c & d; // OpBitwiseAnd + // VectorShuffle, CompositeExtract, CompositeInsert, not testable atm. + bool c16 = g || h; // OpLogicalOr + bool c17 = g && h; // OpLogicalAnd + bool c18 = !g; // OpLogicalNot + bool c19 = g == h; // OpLogicalEqual + bool c20 = g != h; // OpLogicalNotEqual + // OpSelect not testable atm. + bool c21 = c == d; // OpIEqual + bool c22 = c != d; // OpINotEqual + bool c23 = c < d; // OpSLessThan + bool c24 = e < f; // OpULessThan + bool c25 = c > d; // OpSGreaterThan + bool c26 = e > f; // OpUGreaterThan + bool c27 = c <= d; // OpSLessThanEqual + bool c28 = e <= f; // OpULessThanEqual + bool c29 = c >= d; // OpSGreaterThanEqual + bool c30 = e >= f; // OpUGreaterThanEqual + // OpQuantizeToF16 not testable atm. + + int c31 = c8 + c3; + + int c32 = int(e); // OpIAdd with different types. + bool c33 = bool(c); // int -> bool + bool c34 = bool(e); // uint -> bool + int c35 = int(g); // bool -> int + uint c36 = uint(g); // bool -> uint + float c37 = float(g); // bool -> float + + // Flexible sized arrays with spec constants and spec constant ops. + float vec0[c + 3][8]; + float vec1[c + 2]; + + Foo foo; + FragColor = vec4(t0 + t1) + vec0[0][0] + vec1[0] + foo.elems[c]; +} diff --git a/third_party/spirv-cross/shaders-reflection/rgen/acceleration_structure.vk.rgen b/third_party/spirv-cross/shaders-reflection/rgen/acceleration_structure.vk.rgen new file mode 100644 index 0000000..568c1a2 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/rgen/acceleration_structure.vk.rgen @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 1) uniform accelerationStructureNV as; + +void main() +{ + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(0.0), 0.0, vec3(1.0), 1000.0, 0); +} diff --git a/third_party/spirv-cross/shaders-reflection/vert/array-size-reflection.vert b/third_party/spirv-cross/shaders-reflection/vert/array-size-reflection.vert new file mode 100644 index 0000000..24a4a43 --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/vert/array-size-reflection.vert @@ -0,0 +1,13 @@ +#version 450 +layout(constant_id = 0) const int ARR_SIZE = 1; + +layout(binding = 0, set = 1, std140) uniform u_ +{ + vec4 u_0[ARR_SIZE]; +}; + +void main() +{ + gl_Position = u_0[0]; +} + diff --git a/third_party/spirv-cross/shaders-reflection/vert/read-from-row-major-array.vert b/third_party/spirv-cross/shaders-reflection/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..792fb8e --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/vert/read-from-row-major-array.vert @@ -0,0 +1,20 @@ +#version 310 es +layout(location = 0) in highp vec4 a_position; +layout(location = 0) out mediump float v_vtxResult; + +layout(set = 0, binding = 0, std140, row_major) uniform Block +{ + highp mat2x3 var[3][4]; +}; + +mediump float compare_float (highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; } +mediump float compare_vec3 (highp vec3 a, highp vec3 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); } +mediump float compare_mat2x3 (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1]); } + +void main (void) +{ + gl_Position = a_position; + mediump float result = 1.0; + result *= compare_mat2x3(var[0][0], mat2x3(2.0, 6.0, -6.0, 0.0, 5.0, 5.0)); + v_vtxResult = result; +} diff --git a/third_party/spirv-cross/shaders-reflection/vert/stride-reflection.vert b/third_party/spirv-cross/shaders-reflection/vert/stride-reflection.vert new file mode 100644 index 0000000..6e7d96d --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/vert/stride-reflection.vert @@ -0,0 +1,14 @@ +#version 450 + +layout(binding = 0, set = 0, std140) uniform U +{ + vec4 v[4]; + mat4 c[4]; + layout(row_major) mat4 r[4]; +}; + +void main() +{ + gl_Position = v[0]; +} + diff --git a/third_party/spirv-cross/shaders-reflection/vert/texture_buffer.vert b/third_party/spirv-cross/shaders-reflection/vert/texture_buffer.vert new file mode 100644 index 0000000..6bc7ddf --- /dev/null +++ b/third_party/spirv-cross/shaders-reflection/vert/texture_buffer.vert @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_OES_texture_buffer : require + +layout(binding = 4) uniform highp samplerBuffer uSamp; +layout(rgba32f, binding = 5) uniform readonly highp imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} diff --git a/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/accesschain-invalid-expression.asm.invalid.frag b/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/accesschain-invalid-expression.asm.invalid.frag new file mode 100644 index 0000000..fae211f --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/accesschain-invalid-expression.asm.invalid.frag @@ -0,0 +1,1087 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 572 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %Main "main" %in_var_TEXCOORD0 %in_var_TEXCOORD7 %in_var_TEXCOORD8 %gl_FragCoord %gl_FrontFacing %out_var_SV_Target0 + OpExecutionMode %Main OriginUpperLeft + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_TranslatedWorldToView" + OpMemberName %type_View 3 "View_ViewToTranslatedWorld" + OpMemberName %type_View 4 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 5 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 6 "View_ViewToClip" + OpMemberName %type_View 7 "View_ViewToClipNoAA" + OpMemberName %type_View 8 "View_ClipToView" + OpMemberName %type_View 9 "View_ClipToTranslatedWorld" + OpMemberName %type_View 10 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 11 "View_ScreenToWorld" + OpMemberName %type_View 12 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 13 "View_ViewForward" + OpMemberName %type_View 14 "PrePadding_View_844" + OpMemberName %type_View 15 "View_ViewUp" + OpMemberName %type_View 16 "PrePadding_View_860" + OpMemberName %type_View 17 "View_ViewRight" + OpMemberName %type_View 18 "PrePadding_View_876" + OpMemberName %type_View 19 "View_HMDViewNoRollUp" + OpMemberName %type_View 20 "PrePadding_View_892" + OpMemberName %type_View 21 "View_HMDViewNoRollRight" + OpMemberName %type_View 22 "PrePadding_View_908" + OpMemberName %type_View 23 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 24 "View_ScreenPositionScaleBias" + OpMemberName %type_View 25 "View_WorldCameraOrigin" + OpMemberName %type_View 26 "PrePadding_View_956" + OpMemberName %type_View 27 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 28 "PrePadding_View_972" + OpMemberName %type_View 29 "View_WorldViewOrigin" + OpMemberName %type_View 30 "PrePadding_View_988" + OpMemberName %type_View 31 "View_PreViewTranslation" + OpMemberName %type_View 32 "PrePadding_View_1004" + OpMemberName %type_View 33 "View_PrevProjection" + OpMemberName %type_View 34 "View_PrevViewProj" + OpMemberName %type_View 35 "View_PrevViewRotationProj" + OpMemberName %type_View 36 "View_PrevViewToClip" + OpMemberName %type_View 37 "View_PrevClipToView" + OpMemberName %type_View 38 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 40 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 41 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 42 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 43 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 44 "PrePadding_View_1660" + OpMemberName %type_View 45 "View_PrevWorldViewOrigin" + OpMemberName %type_View 46 "PrePadding_View_1676" + OpMemberName %type_View 47 "View_PrevPreViewTranslation" + OpMemberName %type_View 48 "PrePadding_View_1692" + OpMemberName %type_View 49 "View_PrevInvViewProj" + OpMemberName %type_View 50 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 51 "View_ClipToPrevClip" + OpMemberName %type_View 52 "View_TemporalAAJitter" + OpMemberName %type_View 53 "View_GlobalClippingPlane" + OpMemberName %type_View 54 "View_FieldOfViewWideAngles" + OpMemberName %type_View 55 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 56 "View_ViewRectMin" + OpMemberName %type_View 57 "View_ViewSizeAndInvSize" + OpMemberName %type_View 58 "View_BufferSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 60 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 61 "View_PreExposure" + OpMemberName %type_View 62 "View_OneOverPreExposure" + OpMemberName %type_View 63 "PrePadding_View_2012" + OpMemberName %type_View 64 "View_DiffuseOverrideParameter" + OpMemberName %type_View 65 "View_SpecularOverrideParameter" + OpMemberName %type_View 66 "View_NormalOverrideParameter" + OpMemberName %type_View 67 "View_RoughnessOverrideParameter" + OpMemberName %type_View 68 "View_PrevFrameGameTime" + OpMemberName %type_View 69 "View_PrevFrameRealTime" + OpMemberName %type_View 70 "View_OutOfBoundsMask" + OpMemberName %type_View 71 "PrePadding_View_2084" + OpMemberName %type_View 72 "PrePadding_View_2088" + OpMemberName %type_View 73 "PrePadding_View_2092" + OpMemberName %type_View 74 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 75 "View_CullingSign" + OpMemberName %type_View 76 "View_NearPlane" + OpMemberName %type_View 77 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 78 "View_GameTime" + OpMemberName %type_View 79 "View_RealTime" + OpMemberName %type_View 80 "View_DeltaTime" + OpMemberName %type_View 81 "View_MaterialTextureMipBias" + OpMemberName %type_View 82 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 83 "View_Random" + OpMemberName %type_View 84 "View_FrameNumber" + OpMemberName %type_View 85 "View_StateFrameIndexMod8" + OpMemberName %type_View 86 "View_StateFrameIndex" + OpMemberName %type_View 87 "View_CameraCut" + OpMemberName %type_View 88 "View_UnlitViewmodeMask" + OpMemberName %type_View 89 "PrePadding_View_2164" + OpMemberName %type_View 90 "PrePadding_View_2168" + OpMemberName %type_View 91 "PrePadding_View_2172" + OpMemberName %type_View 92 "View_DirectionalLightColor" + OpMemberName %type_View 93 "View_DirectionalLightDirection" + OpMemberName %type_View 94 "PrePadding_View_2204" + OpMemberName %type_View 95 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 97 "View_TemporalAAParams" + OpMemberName %type_View 98 "View_CircleDOFParams" + OpMemberName %type_View 99 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 100 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 101 "View_DepthOfFieldScale" + OpMemberName %type_View 102 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 103 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 104 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 105 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 106 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 107 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 108 "View_GeneralPurposeTweak" + OpMemberName %type_View 109 "View_DemosaicVposOffset" + OpMemberName %type_View 110 "PrePadding_View_2348" + OpMemberName %type_View 111 "View_IndirectLightingColorScale" + OpMemberName %type_View 112 "View_HDR32bppEncodingMode" + OpMemberName %type_View 113 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 114 "View_AtmosphericFogSunPower" + OpMemberName %type_View 115 "View_AtmosphericFogPower" + OpMemberName %type_View 116 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 117 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 118 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 119 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 120 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 121 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 122 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 123 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 124 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 125 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 126 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 127 "View_AtmosphericFogSunColor" + OpMemberName %type_View 128 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 129 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 130 "View_AmbientCubemapTint" + OpMemberName %type_View 131 "View_AmbientCubemapIntensity" + OpMemberName %type_View 132 "View_SkyLightParameters" + OpMemberName %type_View 133 "PrePadding_View_2488" + OpMemberName %type_View 134 "PrePadding_View_2492" + OpMemberName %type_View 135 "View_SkyLightColor" + OpMemberName %type_View 136 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 137 "View_MobilePreviewMode" + OpMemberName %type_View 138 "View_HMDEyePaddingOffset" + OpMemberName %type_View 139 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 140 "View_ShowDecalsMask" + OpMemberName %type_View 141 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 142 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 143 "PrePadding_View_2648" + OpMemberName %type_View 144 "PrePadding_View_2652" + OpMemberName %type_View 145 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 146 "View_StereoPassIndex" + OpMemberName %type_View 147 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 148 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 149 "View_GlobalVolumeDimension" + OpMemberName %type_View 150 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 151 "View_MaxGlobalDistance" + OpMemberName %type_View 152 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 153 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 154 "PrePadding_View_2828" + OpMemberName %type_View 155 "View_VolumetricFogGridZParams" + OpMemberName %type_View 156 "PrePadding_View_2844" + OpMemberName %type_View 157 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 158 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 159 "PrePadding_View_2860" + OpMemberName %type_View 160 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 161 "PrePadding_View_2876" + OpMemberName %type_View 162 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 163 "PrePadding_View_2892" + OpMemberName %type_View 164 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 165 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 166 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 167 "View_StereoIPD" + OpMemberName %type_View 168 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 169 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_MobileDirectionalLight "type.MobileDirectionalLight" + OpMemberName %type_MobileDirectionalLight 0 "MobileDirectionalLight_DirectionalLightColor" + OpMemberName %type_MobileDirectionalLight 1 "MobileDirectionalLight_DirectionalLightDirectionAndShadowTransition" + OpMemberName %type_MobileDirectionalLight 2 "MobileDirectionalLight_DirectionalLightShadowSize" + OpMemberName %type_MobileDirectionalLight 3 "MobileDirectionalLight_DirectionalLightDistanceFadeMAD" + OpMemberName %type_MobileDirectionalLight 4 "MobileDirectionalLight_DirectionalLightShadowDistances" + OpMemberName %type_MobileDirectionalLight 5 "MobileDirectionalLight_DirectionalLightScreenToShadow" + OpName %MobileDirectionalLight "MobileDirectionalLight" + OpName %type_2d_image "type.2d.image" + OpName %MobileDirectionalLight_DirectionalLightShadowTexture "MobileDirectionalLight_DirectionalLightShadowTexture" + OpName %type_sampler "type.sampler" + OpName %MobileDirectionalLight_DirectionalLightShadowSampler "MobileDirectionalLight_DirectionalLightShadowSampler" + OpName %Material_Texture2D_0 "Material_Texture2D_0" + OpName %Material_Texture2D_0Sampler "Material_Texture2D_0Sampler" + OpName %Material_Texture2D_1 "Material_Texture2D_1" + OpName %Material_Texture2D_1Sampler "Material_Texture2D_1Sampler" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "NumDynamicPointLights" + OpMemberName %type__Globals 1 "LightPositionAndInvRadius" + OpMemberName %type__Globals 2 "LightColorAndFalloffExponent" + OpMemberName %type__Globals 3 "MobileReflectionParams" + OpName %_Globals "$Globals" + OpName %type_cube_image "type.cube.image" + OpName %ReflectionCubemap "ReflectionCubemap" + OpName %ReflectionCubemapSampler "ReflectionCubemapSampler" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_TEXCOORD7 "in.var.TEXCOORD7" + OpName %in_var_TEXCOORD8 "in.var.TEXCOORD8" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %Main "Main" + OpName %type_sampled_image "type.sampled.image" + OpName %type_sampled_image_0 "type.sampled.image" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %in_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorateString %in_var_TEXCOORD8 UserSemantic "TEXCOORD8" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorate %gl_FrontFacing BuiltIn FrontFacing + OpDecorateString %gl_FrontFacing UserSemantic "SV_IsFrontFace" + OpDecorate %gl_FrontFacing Flat + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %in_var_TEXCOORD7 Location 1 + OpDecorate %in_var_TEXCOORD8 Location 2 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %MobileDirectionalLight DescriptorSet 0 + OpDecorate %MobileDirectionalLight Binding 1 + OpDecorate %MobileDirectionalLight_DirectionalLightShadowTexture DescriptorSet 0 + OpDecorate %MobileDirectionalLight_DirectionalLightShadowTexture Binding 0 + OpDecorate %MobileDirectionalLight_DirectionalLightShadowSampler DescriptorSet 0 + OpDecorate %MobileDirectionalLight_DirectionalLightShadowSampler Binding 0 + OpDecorate %Material_Texture2D_0 DescriptorSet 0 + OpDecorate %Material_Texture2D_0 Binding 1 + OpDecorate %Material_Texture2D_0Sampler DescriptorSet 0 + OpDecorate %Material_Texture2D_0Sampler Binding 1 + OpDecorate %Material_Texture2D_1 DescriptorSet 0 + OpDecorate %Material_Texture2D_1 Binding 2 + OpDecorate %Material_Texture2D_1Sampler DescriptorSet 0 + OpDecorate %Material_Texture2D_1Sampler Binding 2 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 2 + OpDecorate %ReflectionCubemap DescriptorSet 0 + OpDecorate %ReflectionCubemap Binding 3 + OpDecorate %ReflectionCubemapSampler DescriptorSet 0 + OpDecorate %ReflectionCubemapSampler Binding 3 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 14 Offset 844 + OpMemberDecorate %type_View 15 Offset 848 + OpMemberDecorate %type_View 16 Offset 860 + OpMemberDecorate %type_View 17 Offset 864 + OpMemberDecorate %type_View 18 Offset 876 + OpMemberDecorate %type_View 19 Offset 880 + OpMemberDecorate %type_View 20 Offset 892 + OpMemberDecorate %type_View 21 Offset 896 + OpMemberDecorate %type_View 22 Offset 908 + OpMemberDecorate %type_View 23 Offset 912 + OpMemberDecorate %type_View 24 Offset 928 + OpMemberDecorate %type_View 25 Offset 944 + OpMemberDecorate %type_View 26 Offset 956 + OpMemberDecorate %type_View 27 Offset 960 + OpMemberDecorate %type_View 28 Offset 972 + OpMemberDecorate %type_View 29 Offset 976 + OpMemberDecorate %type_View 30 Offset 988 + OpMemberDecorate %type_View 31 Offset 992 + OpMemberDecorate %type_View 32 Offset 1004 + OpMemberDecorate %type_View 33 Offset 1008 + OpMemberDecorate %type_View 33 MatrixStride 16 + OpMemberDecorate %type_View 33 ColMajor + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 44 Offset 1660 + OpMemberDecorate %type_View 45 Offset 1664 + OpMemberDecorate %type_View 46 Offset 1676 + OpMemberDecorate %type_View 47 Offset 1680 + OpMemberDecorate %type_View 48 Offset 1692 + OpMemberDecorate %type_View 49 Offset 1696 + OpMemberDecorate %type_View 49 MatrixStride 16 + OpMemberDecorate %type_View 49 ColMajor + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 53 Offset 1904 + OpMemberDecorate %type_View 54 Offset 1920 + OpMemberDecorate %type_View 55 Offset 1928 + OpMemberDecorate %type_View 56 Offset 1936 + OpMemberDecorate %type_View 57 Offset 1952 + OpMemberDecorate %type_View 58 Offset 1968 + OpMemberDecorate %type_View 59 Offset 1984 + OpMemberDecorate %type_View 60 Offset 2000 + OpMemberDecorate %type_View 61 Offset 2004 + OpMemberDecorate %type_View 62 Offset 2008 + OpMemberDecorate %type_View 63 Offset 2012 + OpMemberDecorate %type_View 64 Offset 2016 + OpMemberDecorate %type_View 65 Offset 2032 + OpMemberDecorate %type_View 66 Offset 2048 + OpMemberDecorate %type_View 67 Offset 2064 + OpMemberDecorate %type_View 68 Offset 2072 + OpMemberDecorate %type_View 69 Offset 2076 + OpMemberDecorate %type_View 70 Offset 2080 + OpMemberDecorate %type_View 71 Offset 2084 + OpMemberDecorate %type_View 72 Offset 2088 + OpMemberDecorate %type_View 73 Offset 2092 + OpMemberDecorate %type_View 74 Offset 2096 + OpMemberDecorate %type_View 75 Offset 2108 + OpMemberDecorate %type_View 76 Offset 2112 + OpMemberDecorate %type_View 77 Offset 2116 + OpMemberDecorate %type_View 78 Offset 2120 + OpMemberDecorate %type_View 79 Offset 2124 + OpMemberDecorate %type_View 80 Offset 2128 + OpMemberDecorate %type_View 81 Offset 2132 + OpMemberDecorate %type_View 82 Offset 2136 + OpMemberDecorate %type_View 83 Offset 2140 + OpMemberDecorate %type_View 84 Offset 2144 + OpMemberDecorate %type_View 85 Offset 2148 + OpMemberDecorate %type_View 86 Offset 2152 + OpMemberDecorate %type_View 87 Offset 2156 + OpMemberDecorate %type_View 88 Offset 2160 + OpMemberDecorate %type_View 89 Offset 2164 + OpMemberDecorate %type_View 90 Offset 2168 + OpMemberDecorate %type_View 91 Offset 2172 + OpMemberDecorate %type_View 92 Offset 2176 + OpMemberDecorate %type_View 93 Offset 2192 + OpMemberDecorate %type_View 94 Offset 2204 + OpMemberDecorate %type_View 95 Offset 2208 + OpMemberDecorate %type_View 96 Offset 2240 + OpMemberDecorate %type_View 97 Offset 2272 + OpMemberDecorate %type_View 98 Offset 2288 + OpMemberDecorate %type_View 99 Offset 2304 + OpMemberDecorate %type_View 100 Offset 2308 + OpMemberDecorate %type_View 101 Offset 2312 + OpMemberDecorate %type_View 102 Offset 2316 + OpMemberDecorate %type_View 103 Offset 2320 + OpMemberDecorate %type_View 104 Offset 2324 + OpMemberDecorate %type_View 105 Offset 2328 + OpMemberDecorate %type_View 106 Offset 2332 + OpMemberDecorate %type_View 107 Offset 2336 + OpMemberDecorate %type_View 108 Offset 2340 + OpMemberDecorate %type_View 109 Offset 2344 + OpMemberDecorate %type_View 110 Offset 2348 + OpMemberDecorate %type_View 111 Offset 2352 + OpMemberDecorate %type_View 112 Offset 2364 + OpMemberDecorate %type_View 113 Offset 2368 + OpMemberDecorate %type_View 114 Offset 2380 + OpMemberDecorate %type_View 115 Offset 2384 + OpMemberDecorate %type_View 116 Offset 2388 + OpMemberDecorate %type_View 117 Offset 2392 + OpMemberDecorate %type_View 118 Offset 2396 + OpMemberDecorate %type_View 119 Offset 2400 + OpMemberDecorate %type_View 120 Offset 2404 + OpMemberDecorate %type_View 121 Offset 2408 + OpMemberDecorate %type_View 122 Offset 2412 + OpMemberDecorate %type_View 123 Offset 2416 + OpMemberDecorate %type_View 124 Offset 2420 + OpMemberDecorate %type_View 125 Offset 2424 + OpMemberDecorate %type_View 126 Offset 2428 + OpMemberDecorate %type_View 127 Offset 2432 + OpMemberDecorate %type_View 128 Offset 2448 + OpMemberDecorate %type_View 129 Offset 2460 + OpMemberDecorate %type_View 130 Offset 2464 + OpMemberDecorate %type_View 131 Offset 2480 + OpMemberDecorate %type_View 132 Offset 2484 + OpMemberDecorate %type_View 133 Offset 2488 + OpMemberDecorate %type_View 134 Offset 2492 + OpMemberDecorate %type_View 135 Offset 2496 + OpMemberDecorate %type_View 136 Offset 2512 + OpMemberDecorate %type_View 137 Offset 2624 + OpMemberDecorate %type_View 138 Offset 2628 + OpMemberDecorate %type_View 139 Offset 2632 + OpMemberDecorate %type_View 140 Offset 2636 + OpMemberDecorate %type_View 141 Offset 2640 + OpMemberDecorate %type_View 142 Offset 2644 + OpMemberDecorate %type_View 143 Offset 2648 + OpMemberDecorate %type_View 144 Offset 2652 + OpMemberDecorate %type_View 145 Offset 2656 + OpMemberDecorate %type_View 146 Offset 2668 + OpMemberDecorate %type_View 147 Offset 2672 + OpMemberDecorate %type_View 148 Offset 2736 + OpMemberDecorate %type_View 149 Offset 2800 + OpMemberDecorate %type_View 150 Offset 2804 + OpMemberDecorate %type_View 151 Offset 2808 + OpMemberDecorate %type_View 152 Offset 2812 + OpMemberDecorate %type_View 153 Offset 2816 + OpMemberDecorate %type_View 154 Offset 2828 + OpMemberDecorate %type_View 155 Offset 2832 + OpMemberDecorate %type_View 156 Offset 2844 + OpMemberDecorate %type_View 157 Offset 2848 + OpMemberDecorate %type_View 158 Offset 2856 + OpMemberDecorate %type_View 159 Offset 2860 + OpMemberDecorate %type_View 160 Offset 2864 + OpMemberDecorate %type_View 161 Offset 2876 + OpMemberDecorate %type_View 162 Offset 2880 + OpMemberDecorate %type_View 163 Offset 2892 + OpMemberDecorate %type_View 164 Offset 2896 + OpMemberDecorate %type_View 165 Offset 2908 + OpMemberDecorate %type_View 166 Offset 2912 + OpMemberDecorate %type_View 167 Offset 2924 + OpMemberDecorate %type_View 168 Offset 2928 + OpMemberDecorate %type_View 169 Offset 2932 + OpDecorate %type_View Block + OpDecorate %_arr_mat4v4float_uint_4 ArrayStride 64 + OpMemberDecorate %type_MobileDirectionalLight 0 Offset 0 + OpMemberDecorate %type_MobileDirectionalLight 1 Offset 16 + OpMemberDecorate %type_MobileDirectionalLight 2 Offset 32 + OpMemberDecorate %type_MobileDirectionalLight 3 Offset 48 + OpMemberDecorate %type_MobileDirectionalLight 4 Offset 64 + OpMemberDecorate %type_MobileDirectionalLight 5 Offset 80 + OpMemberDecorate %type_MobileDirectionalLight 5 MatrixStride 16 + OpMemberDecorate %type_MobileDirectionalLight 5 ColMajor + OpDecorate %type_MobileDirectionalLight Block + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 80 + OpMemberDecorate %type__Globals 3 Offset 144 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_4 = OpConstant %int 4 + %float_0 = OpConstant %float 0 + %int_3 = OpConstant %int 3 + %47 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %float_1 = OpConstant %float 1 + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %int_5 = OpConstant %int 5 + %52 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_0_999989986 = OpConstant %float 0.999989986 +%float_65000 = OpConstant %float 65000 + %55 = OpConstantComposite %v3float %float_65000 %float_65000 %float_65000 +%float_0_318309873 = OpConstant %float 0.318309873 + %57 = OpConstantComposite %v3float %float_0_318309873 %float_0_318309873 %float_0_318309873 +%float_65500 = OpConstant %float 65500 + %float_0_5 = OpConstant %float 0.5 + %60 = OpConstantComposite %v2float %float_0_5 %float_0_5 + %float_2 = OpConstant %float 2 + %float_n2 = OpConstant %float -2 + %63 = OpConstantComposite %v2float %float_2 %float_n2 + %64 = OpConstantComposite %v3float %float_1 %float_1 %float_1 +%float_0_119999997 = OpConstant %float 0.119999997 + %float_n1 = OpConstant %float -1 +%float_n0_0274999999 = OpConstant %float -0.0274999999 + %68 = OpConstantComposite %v2float %float_n1 %float_n0_0274999999 +%float_0_0425000004 = OpConstant %float 0.0425000004 + %70 = OpConstantComposite %v2float %float_1 %float_0_0425000004 +%float_n9_27999973 = OpConstant %float -9.27999973 + %72 = OpConstantComposite %v2float %float_1 %float_1 + %float_0_25 = OpConstant %float 0.25 + %float_16 = OpConstant %float 16 + %int_31 = OpConstant %int 31 + %int_56 = OpConstant %int 56 + %int_57 = OpConstant %int 57 + %int_64 = OpConstant %int 64 + %int_65 = OpConstant %int 65 + %int_66 = OpConstant %int 66 + %int_67 = OpConstant %int 67 + %int_88 = OpConstant %int 88 + %int_135 = OpConstant %int 135 + %int_139 = OpConstant %int 139 +%mat3v3float = OpTypeMatrix %v3float 3 + %86 = OpConstantComposite %v2float %float_2 %float_2 +%float_0_300000012 = OpConstant %float 0.300000012 + %88 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_1 + %float_20 = OpConstant %float 20 + %90 = OpConstantComposite %v2float %float_20 %float_20 +%float_0_400000006 = OpConstant %float 0.400000006 + %float_24 = OpConstant %float 24 +%float_0_294999987 = OpConstant %float 0.294999987 +%float_0_660000026 = OpConstant %float 0.660000026 +%float_0_699999988 = OpConstant %float 0.699999988 +%float_65504 = OpConstant %float 65504 +%float_1_20000005 = OpConstant %float 1.20000005 + %98 = OpConstantComposite %v3float %float_2 %float_2 %float_2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%_arr_mat4v4float_uint_4 = OpTypeArray %mat4v4float %uint_4 +%type_MobileDirectionalLight = OpTypeStruct %v4float %v4float %v4float %v4float %v4float %_arr_mat4v4float_uint_4 +%_ptr_Uniform_type_MobileDirectionalLight = OpTypePointer Uniform %type_MobileDirectionalLight +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type__Globals = OpTypeStruct %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %v4float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%type_cube_image = OpTypeImage %float Cube 2 0 0 1 Unknown +%_ptr_UniformConstant_type_cube_image = OpTypePointer UniformConstant %type_cube_image +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_bool = OpTypePointer Input %bool +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %110 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_int = OpTypePointer Uniform %int +%type_sampled_image = OpTypeSampledImage %type_cube_image +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%type_sampled_image_0 = OpTypeSampledImage %type_2d_image + %View = OpVariable %_ptr_Uniform_type_View Uniform +%MobileDirectionalLight = OpVariable %_ptr_Uniform_type_MobileDirectionalLight Uniform +%MobileDirectionalLight_DirectionalLightShadowTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%MobileDirectionalLight_DirectionalLightShadowSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%Material_Texture2D_0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%Material_Texture2D_0Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%Material_Texture2D_1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%Material_Texture2D_1Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%ReflectionCubemap = OpVariable %_ptr_UniformConstant_type_cube_image UniformConstant +%ReflectionCubemapSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%in_var_TEXCOORD7 = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD8 = OpVariable %_ptr_Input_v4float Input +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%gl_FrontFacing = OpVariable %_ptr_Input_bool Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %117 = OpConstantComposite %v3float %float_1 %float_0 %float_0 + %118 = OpConstantComposite %v3float %float_0 %float_1 %float_0 + %119 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %120 = OpConstantComposite %mat3v3float %117 %118 %119 + %float_10 = OpConstant %float 10 + %122 = OpConstantComposite %v2float %float_10 %float_10 + %float_5 = OpConstant %float 5 + %124 = OpConstantComposite %v2float %float_5 %float_5 +%float_0_00066666666 = OpConstant %float 0.00066666666 + %float_n0_5 = OpConstant %float -0.5 + %127 = OpConstantComposite %v2float %float_n0_5 %float_n0_5 + %128 = OpConstantComposite %v2float %float_0_5 %float_n0_5 + %float_1_5 = OpConstant %float 1.5 + %130 = OpConstantComposite %v2float %float_1_5 %float_n0_5 + %131 = OpConstantComposite %v2float %float_n0_5 %float_0_5 + %132 = OpConstantComposite %v2float %float_1_5 %float_0_5 + %133 = OpConstantComposite %v2float %float_n0_5 %float_1_5 + %134 = OpConstantComposite %v2float %float_0_5 %float_1_5 + %135 = OpConstantComposite %v2float %float_1_5 %float_1_5 + %136 = OpUndef %v3float + %137 = OpUndef %v4float + %138 = OpUndef %float + %139 = OpUndef %v3float + %Main = OpFunction %void None %110 + %140 = OpLabel + %141 = OpLoad %v2float %in_var_TEXCOORD0 + %142 = OpLoad %v4float %in_var_TEXCOORD7 + %143 = OpLoad %v4float %in_var_TEXCOORD8 + %144 = OpLoad %v4float %gl_FragCoord + %145 = OpAccessChain %_ptr_Uniform_v3float %View %int_31 + %146 = OpLoad %v3float %145 + %147 = OpAccessChain %_ptr_Uniform_v4float %View %int_56 + %148 = OpLoad %v4float %147 + %149 = OpAccessChain %_ptr_Uniform_v4float %View %int_57 + %150 = OpLoad %v4float %149 + %151 = OpAccessChain %_ptr_Uniform_v4float %View %int_64 + %152 = OpLoad %v4float %151 + %153 = OpAccessChain %_ptr_Uniform_v4float %View %int_65 + %154 = OpLoad %v4float %153 + %155 = OpAccessChain %_ptr_Uniform_v4float %View %int_66 + %156 = OpLoad %v4float %155 + %157 = OpAccessChain %_ptr_Uniform_v2float %View %int_67 + %158 = OpLoad %v2float %157 + %159 = OpAccessChain %_ptr_Uniform_float %View %int_88 + %160 = OpLoad %float %159 + %161 = OpAccessChain %_ptr_Uniform_v4float %View %int_135 + %162 = OpLoad %v4float %161 + %163 = OpAccessChain %_ptr_Uniform_float %View %int_139 + %164 = OpLoad %float %163 + %165 = OpVectorShuffle %v2float %144 %144 0 1 + %166 = OpVectorShuffle %v2float %148 %148 0 1 + %167 = OpFSub %v2float %165 %166 + %168 = OpVectorShuffle %v2float %150 %150 2 3 + %169 = OpFMul %v2float %167 %168 + %170 = OpFSub %v2float %169 %60 + %171 = OpFMul %v2float %170 %63 + %172 = OpCompositeExtract %float %171 0 + %173 = OpCompositeExtract %float %171 1 + %174 = OpCompositeConstruct %v4float %172 %173 %138 %float_1 + %175 = OpCompositeExtract %float %144 3 + %176 = OpCompositeConstruct %v4float %175 %175 %175 %175 + %177 = OpFMul %v4float %174 %176 + %178 = OpVectorShuffle %v3float %143 %143 0 1 2 + %179 = OpFSub %v3float %178 %146 + %180 = OpFNegate %v3float %178 + %181 = OpExtInst %v3float %1 Normalize %180 + %182 = OpFMul %v2float %141 %60 + %183 = OpFMul %v2float %141 %122 + %184 = OpLoad %type_2d_image %Material_Texture2D_0 + %185 = OpLoad %type_sampler %Material_Texture2D_0Sampler + %186 = OpSampledImage %type_sampled_image_0 %184 %185 + %187 = OpImageSampleImplicitLod %v4float %186 %183 None + %188 = OpVectorShuffle %v2float %187 %187 0 1 + %189 = OpFMul %v2float %188 %86 + %190 = OpFSub %v2float %189 %72 + %191 = OpDot %float %190 %190 + %192 = OpFSub %float %float_1 %191 + %193 = OpExtInst %float %1 FClamp %192 %float_0 %float_1 + %194 = OpExtInst %float %1 Sqrt %193 + %195 = OpCompositeExtract %float %190 0 + %196 = OpCompositeExtract %float %190 1 + %197 = OpCompositeConstruct %v4float %195 %196 %194 %float_1 + %198 = OpVectorShuffle %v3float %197 %197 0 1 2 + %199 = OpFMul %v3float %198 %88 + %200 = OpVectorShuffle %v3float %156 %156 0 1 2 + %201 = OpCompositeExtract %float %156 3 + %202 = OpCompositeConstruct %v3float %201 %201 %201 + %203 = OpFMul %v3float %199 %202 + %204 = OpFAdd %v3float %203 %200 + %205 = OpMatrixTimesVector %v3float %120 %204 + %206 = OpExtInst %v3float %1 Normalize %205 + %207 = OpFNegate %v3float %181 + %208 = OpDot %float %206 %181 + %209 = OpCompositeConstruct %v3float %208 %208 %208 + %210 = OpFMul %v3float %206 %209 + %211 = OpFMul %v3float %210 %98 + %212 = OpFAdd %v3float %207 %211 + %213 = OpFMul %v2float %141 %90 + %214 = OpLoad %type_2d_image %Material_Texture2D_1 + %215 = OpLoad %type_sampler %Material_Texture2D_1Sampler + %216 = OpSampledImage %type_sampled_image_0 %214 %215 + %217 = OpImageSampleImplicitLod %v4float %216 %213 None + %218 = OpCompositeExtract %float %217 0 + %219 = OpExtInst %float %1 FMix %float_0_400000006 %float_1 %218 + %220 = OpFSub %float %float_1 %219 + %221 = OpFMul %v2float %141 %124 + %222 = OpSampledImage %type_sampled_image_0 %214 %215 + %223 = OpImageSampleImplicitLod %v4float %222 %221 None + %224 = OpCompositeExtract %float %177 3 + %225 = OpFSub %float %224 %float_24 + %226 = OpFMul %float %225 %float_0_00066666666 + %227 = OpExtInst %float %1 FMax %226 %float_0 + %228 = OpExtInst %float %1 FMin %227 %float_1 + %229 = OpCompositeExtract %float %223 1 + %230 = OpExtInst %float %1 FMix %229 %float_1 %228 + %231 = OpExtInst %float %1 FMix %219 %220 %230 + %232 = OpSampledImage %type_sampled_image_0 %214 %215 + %233 = OpImageSampleImplicitLod %v4float %232 %182 None + %234 = OpExtInst %float %1 FMix %229 %float_0 %228 + %235 = OpCompositeExtract %float %233 1 + %236 = OpFAdd %float %235 %234 + %237 = OpExtInst %float %1 FMix %236 %float_0_5 %float_0_5 + %238 = OpExtInst %float %1 FMix %float_0_294999987 %float_0_660000026 %237 + %239 = OpFMul %float %238 %float_0_5 + %240 = OpFMul %float %231 %239 + %241 = OpExtInst %float %1 FMix %float_0 %float_0_5 %235 + %242 = OpExtInst %float %1 FMix %float_0_699999988 %float_1 %229 + %243 = OpExtInst %float %1 FMix %242 %float_1 %228 + %244 = OpFAdd %float %241 %243 + %245 = OpExtInst %float %1 FMax %244 %float_0 + %246 = OpExtInst %float %1 FMin %245 %float_1 + %247 = OpCompositeConstruct %v3float %240 %240 %240 + %248 = OpExtInst %v3float %1 FClamp %247 %47 %64 + %249 = OpCompositeExtract %float %158 1 + %250 = OpFMul %float %246 %249 + %251 = OpCompositeExtract %float %158 0 + %252 = OpFAdd %float %250 %251 + %253 = OpExtInst %float %1 FClamp %252 %float_0_119999997 %float_1 + %254 = OpExtInst %float %1 FMax %208 %float_0 + %255 = OpCompositeConstruct %v2float %253 %253 + %256 = OpFMul %v2float %255 %68 + %257 = OpFAdd %v2float %256 %70 + %258 = OpCompositeExtract %float %257 0 + %259 = OpFMul %float %258 %258 + %260 = OpFMul %float %float_n9_27999973 %254 + %261 = OpExtInst %float %1 Exp2 %260 + %262 = OpExtInst %float %1 FMin %259 %261 + %263 = OpFMul %float %262 %258 + %264 = OpCompositeExtract %float %257 1 + %265 = OpFAdd %float %263 %264 + %266 = OpCompositeExtract %float %152 3 + %267 = OpCompositeConstruct %v3float %266 %266 %266 + %268 = OpFMul %v3float %248 %267 + %269 = OpVectorShuffle %v3float %152 %152 0 1 2 + %270 = OpFAdd %v3float %268 %269 + %271 = OpCompositeExtract %float %154 3 + %272 = OpFMul %float %265 %271 + %273 = OpCompositeConstruct %v3float %272 %272 %272 + %274 = OpVectorShuffle %v3float %154 %154 0 1 2 + %275 = OpFAdd %v3float %273 %274 + %276 = OpCompositeExtract %float %275 0 + %277 = OpExtInst %float %1 FClamp %float_1 %float_0 %float_1 + %278 = OpLoad %type_2d_image %MobileDirectionalLight_DirectionalLightShadowTexture + %279 = OpLoad %type_sampler %MobileDirectionalLight_DirectionalLightShadowSampler + %280 = OpAccessChain %_ptr_Uniform_v4float %MobileDirectionalLight %int_1 + %281 = OpAccessChain %_ptr_Uniform_float %MobileDirectionalLight %int_1 %int_3 + %282 = OpLoad %float %281 + %283 = OpAccessChain %_ptr_Uniform_v4float %MobileDirectionalLight %int_2 + %284 = OpLoad %v4float %283 + OpBranch %285 + %285 = OpLabel + %286 = OpPhi %int %int_0 %140 %287 %288 + %289 = OpSLessThan %bool %286 %int_2 + OpLoopMerge %290 %288 None + OpBranchConditional %289 %291 %290 + %291 = OpLabel + %292 = OpBitcast %uint %286 + %293 = OpAccessChain %_ptr_Uniform_float %MobileDirectionalLight %int_4 %292 + %294 = OpLoad %float %293 + %295 = OpFOrdLessThan %bool %224 %294 + OpSelectionMerge %288 None + OpBranchConditional %295 %296 %288 + %296 = OpLabel + %297 = OpCompositeExtract %float %177 0 + %298 = OpCompositeExtract %float %177 1 + %299 = OpCompositeConstruct %v4float %297 %298 %224 %float_1 + %300 = OpAccessChain %_ptr_Uniform_mat4v4float %MobileDirectionalLight %int_5 %286 + %301 = OpLoad %mat4v4float %300 + %302 = OpMatrixTimesVector %v4float %301 %299 + OpBranch %290 + %288 = OpLabel + %287 = OpIAdd %int %286 %int_1 + OpBranch %285 + %290 = OpLabel + %303 = OpPhi %v4float %52 %285 %302 %296 + %304 = OpCompositeExtract %float %303 2 + %305 = OpFOrdGreaterThan %bool %304 %float_0 + OpSelectionMerge %306 None + OpBranchConditional %305 %307 %306 + %307 = OpLabel + %308 = OpExtInst %float %1 FMin %304 %float_0_999989986 + %309 = OpVectorShuffle %v2float %303 %303 0 1 + %310 = OpVectorShuffle %v2float %284 %284 0 1 + %311 = OpFMul %v2float %309 %310 + %312 = OpExtInst %v2float %1 Fract %311 + %313 = OpExtInst %v2float %1 Floor %311 + %314 = OpFAdd %v2float %313 %127 + %315 = OpVectorShuffle %v2float %284 %284 2 3 + %316 = OpFMul %v2float %314 %315 + %317 = OpSampledImage %type_sampled_image_0 %278 %279 + %318 = OpImageSampleExplicitLod %v4float %317 %316 Lod %float_0 + %319 = OpCompositeExtract %float %318 0 + %320 = OpCompositeInsert %v3float %319 %139 0 + %321 = OpFAdd %v2float %313 %128 + %322 = OpFMul %v2float %321 %315 + %323 = OpSampledImage %type_sampled_image_0 %278 %279 + %324 = OpImageSampleExplicitLod %v4float %323 %322 Lod %float_0 + %325 = OpCompositeExtract %float %324 0 + %326 = OpCompositeInsert %v3float %325 %320 1 + %327 = OpFAdd %v2float %313 %130 + %328 = OpFMul %v2float %327 %315 + %329 = OpSampledImage %type_sampled_image_0 %278 %279 + %330 = OpImageSampleExplicitLod %v4float %329 %328 Lod %float_0 + %331 = OpCompositeExtract %float %330 0 + %332 = OpCompositeInsert %v3float %331 %326 2 + %333 = OpFMul %float %308 %282 + %334 = OpFSub %float %333 %float_1 + %335 = OpCompositeConstruct %v3float %282 %282 %282 + %336 = OpFMul %v3float %332 %335 + %337 = OpCompositeConstruct %v3float %334 %334 %334 + %338 = OpFSub %v3float %336 %337 + %339 = OpExtInst %v3float %1 FClamp %338 %47 %64 + %340 = OpFAdd %v2float %313 %131 + %341 = OpFMul %v2float %340 %315 + %342 = OpSampledImage %type_sampled_image_0 %278 %279 + %343 = OpImageSampleExplicitLod %v4float %342 %341 Lod %float_0 + %344 = OpCompositeExtract %float %343 0 + %345 = OpCompositeInsert %v3float %344 %139 0 + %346 = OpFAdd %v2float %313 %60 + %347 = OpFMul %v2float %346 %315 + %348 = OpSampledImage %type_sampled_image_0 %278 %279 + %349 = OpImageSampleExplicitLod %v4float %348 %347 Lod %float_0 + %350 = OpCompositeExtract %float %349 0 + %351 = OpCompositeInsert %v3float %350 %345 1 + %352 = OpFAdd %v2float %313 %132 + %353 = OpFMul %v2float %352 %315 + %354 = OpSampledImage %type_sampled_image_0 %278 %279 + %355 = OpImageSampleExplicitLod %v4float %354 %353 Lod %float_0 + %356 = OpCompositeExtract %float %355 0 + %357 = OpCompositeInsert %v3float %356 %351 2 + %358 = OpFMul %v3float %357 %335 + %359 = OpFSub %v3float %358 %337 + %360 = OpExtInst %v3float %1 FClamp %359 %47 %64 + %361 = OpFAdd %v2float %313 %133 + %362 = OpFMul %v2float %361 %315 + %363 = OpSampledImage %type_sampled_image_0 %278 %279 + %364 = OpImageSampleExplicitLod %v4float %363 %362 Lod %float_0 + %365 = OpCompositeExtract %float %364 0 + %366 = OpCompositeInsert %v3float %365 %139 0 + %367 = OpFAdd %v2float %313 %134 + %368 = OpFMul %v2float %367 %315 + %369 = OpSampledImage %type_sampled_image_0 %278 %279 + %370 = OpImageSampleExplicitLod %v4float %369 %368 Lod %float_0 + %371 = OpCompositeExtract %float %370 0 + %372 = OpCompositeInsert %v3float %371 %366 1 + %373 = OpFAdd %v2float %313 %135 + %374 = OpFMul %v2float %373 %315 + %375 = OpSampledImage %type_sampled_image_0 %278 %279 + %376 = OpImageSampleExplicitLod %v4float %375 %374 Lod %float_0 + %377 = OpCompositeExtract %float %376 0 + %378 = OpCompositeInsert %v3float %377 %372 2 + %379 = OpFMul %v3float %378 %335 + %380 = OpFSub %v3float %379 %337 + %381 = OpExtInst %v3float %1 FClamp %380 %47 %64 + %382 = OpCompositeExtract %float %339 0 + %383 = OpCompositeExtract %float %312 0 + %384 = OpFSub %float %float_1 %383 + %385 = OpFMul %float %382 %384 + %386 = OpCompositeExtract %float %360 0 + %387 = OpFMul %float %386 %384 + %388 = OpCompositeExtract %float %381 0 + %389 = OpFMul %float %388 %384 + %390 = OpCompositeExtract %float %339 1 + %391 = OpFAdd %float %385 %390 + %392 = OpCompositeExtract %float %360 1 + %393 = OpFAdd %float %387 %392 + %394 = OpCompositeExtract %float %381 1 + %395 = OpFAdd %float %389 %394 + %396 = OpCompositeExtract %float %339 2 + %397 = OpFMul %float %396 %383 + %398 = OpFAdd %float %391 %397 + %399 = OpCompositeInsert %v3float %398 %136 0 + %400 = OpCompositeExtract %float %360 2 + %401 = OpFMul %float %400 %383 + %402 = OpFAdd %float %393 %401 + %403 = OpCompositeInsert %v3float %402 %399 1 + %404 = OpCompositeExtract %float %381 2 + %405 = OpFMul %float %404 %383 + %406 = OpFAdd %float %395 %405 + %407 = OpCompositeInsert %v3float %406 %403 2 + %408 = OpCompositeExtract %float %312 1 + %409 = OpFSub %float %float_1 %408 + %410 = OpCompositeConstruct %v3float %409 %float_1 %408 + %411 = OpDot %float %407 %410 + %412 = OpFMul %float %float_0_25 %411 + %413 = OpExtInst %float %1 FClamp %412 %float_0 %float_1 + %414 = OpAccessChain %_ptr_Uniform_float %MobileDirectionalLight %int_3 %int_0 + %415 = OpLoad %float %414 + %416 = OpFMul %float %224 %415 + %417 = OpAccessChain %_ptr_Uniform_float %MobileDirectionalLight %int_3 %int_1 + %418 = OpLoad %float %417 + %419 = OpFAdd %float %416 %418 + %420 = OpExtInst %float %1 FClamp %419 %float_0 %float_1 + %421 = OpFMul %float %420 %420 + %422 = OpExtInst %float %1 FMix %413 %float_1 %421 + OpBranch %306 + %306 = OpLabel + %423 = OpPhi %float %float_1 %290 %422 %307 + %424 = OpLoad %v4float %280 + %425 = OpVectorShuffle %v3float %424 %424 0 1 2 + %426 = OpDot %float %206 %425 + %427 = OpExtInst %float %1 FMax %float_0 %426 + %428 = OpFAdd %v3float %181 %425 + %429 = OpExtInst %v3float %1 Normalize %428 + %430 = OpDot %float %206 %429 + %431 = OpExtInst %float %1 FMax %float_0 %430 + %432 = OpFMul %float %423 %427 + %433 = OpCompositeConstruct %v3float %432 %432 %432 + %434 = OpAccessChain %_ptr_Uniform_v4float %MobileDirectionalLight %int_0 + %435 = OpLoad %v4float %434 + %436 = OpVectorShuffle %v3float %435 %435 0 1 2 + %437 = OpFMul %v3float %433 %436 + %438 = OpFMul %float %253 %float_0_25 + %439 = OpFAdd %float %438 %float_0_25 + %440 = OpExtInst %v3float %1 Cross %206 %429 + %441 = OpDot %float %440 %440 + %442 = OpFMul %float %253 %253 + %443 = OpFMul %float %431 %442 + %444 = OpFMul %float %443 %443 + %445 = OpFAdd %float %441 %444 + %446 = OpFDiv %float %442 %445 + %447 = OpFMul %float %446 %446 + %448 = OpExtInst %float %1 FMin %447 %float_65504 + %449 = OpFMul %float %439 %448 + %450 = OpFMul %float %276 %449 + %451 = OpCompositeConstruct %v3float %450 %450 %450 + %452 = OpFAdd %v3float %270 %451 + %453 = OpFMul %v3float %437 %452 + %454 = OpAccessChain %_ptr_Uniform_float %_Globals %int_3 %int_3 + %455 = OpLoad %float %454 + %456 = OpFOrdGreaterThan %bool %455 %float_0 + %457 = OpSelect %float %456 %float_1 %float_0 + %458 = OpFOrdNotEqual %bool %457 %float_0 + %459 = OpSelect %float %458 %455 %164 + %460 = OpExtInst %float %1 Log2 %253 + %461 = OpFMul %float %float_1_20000005 %460 + %462 = OpFSub %float %float_1 %461 + %463 = OpFSub %float %459 %float_1 + %464 = OpFSub %float %463 %462 + %465 = OpLoad %type_cube_image %ReflectionCubemap + %466 = OpLoad %type_sampler %ReflectionCubemapSampler + %467 = OpSampledImage %type_sampled_image %465 %466 + %468 = OpImageSampleExplicitLod %v4float %467 %212 Lod %464 + OpSelectionMerge %469 None + OpBranchConditional %458 %470 %471 + %471 = OpLabel + %472 = OpVectorShuffle %v3float %468 %468 0 1 2 + %473 = OpCompositeExtract %float %468 3 + %474 = OpFMul %float %473 %float_16 + %475 = OpCompositeConstruct %v3float %474 %474 %474 + %476 = OpFMul %v3float %472 %475 + %477 = OpFMul %v3float %476 %476 + OpBranch %469 + %470 = OpLabel + %478 = OpVectorShuffle %v3float %468 %468 0 1 2 + %479 = OpVectorShuffle %v3float %162 %162 0 1 2 + %480 = OpFMul %v3float %478 %479 + OpBranch %469 + %469 = OpLabel + %481 = OpPhi %v3float %477 %471 %480 %470 + %482 = OpCompositeConstruct %v3float %277 %277 %277 + %483 = OpFMul %v3float %481 %482 + %484 = OpCompositeConstruct %v3float %276 %276 %276 + %485 = OpFMul %v3float %483 %484 + %486 = OpFAdd %v3float %453 %485 + OpBranch %487 + %487 = OpLabel + %488 = OpPhi %v3float %486 %469 %489 %490 + %491 = OpPhi %int %int_0 %469 %492 %490 + %493 = OpAccessChain %_ptr_Uniform_int %_Globals %int_0 + %494 = OpLoad %int %493 + %495 = OpSLessThan %bool %491 %494 + OpLoopMerge %496 %490 None + OpBranchConditional %495 %497 %496 + %497 = OpLabel + %498 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_1 %491 + %499 = OpLoad %v4float %498 + %500 = OpVectorShuffle %v3float %499 %499 0 1 2 + %501 = OpFSub %v3float %500 %179 + %502 = OpDot %float %501 %501 + %503 = OpExtInst %float %1 InverseSqrt %502 + %504 = OpCompositeConstruct %v3float %503 %503 %503 + %505 = OpFMul %v3float %501 %504 + %506 = OpFAdd %v3float %181 %505 + %507 = OpExtInst %v3float %1 Normalize %506 + %508 = OpDot %float %206 %505 + %509 = OpExtInst %float %1 FMax %float_0 %508 + %510 = OpDot %float %206 %507 + %511 = OpExtInst %float %1 FMax %float_0 %510 + %512 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_2 %491 + %513 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 %491 %int_3 + %514 = OpLoad %float %513 + %515 = OpFOrdEqual %bool %514 %float_0 + OpSelectionMerge %490 None + OpBranchConditional %515 %516 %517 + %517 = OpLabel + %518 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 %491 %int_3 + %519 = OpLoad %float %518 + %520 = OpCompositeConstruct %v3float %519 %519 %519 + %521 = OpFMul %v3float %501 %520 + %522 = OpDot %float %521 %521 + %523 = OpExtInst %float %1 FClamp %522 %float_0 %float_1 + %524 = OpFSub %float %float_1 %523 + %525 = OpExtInst %float %1 Pow %524 %514 + OpBranch %490 + %516 = OpLabel + %526 = OpFAdd %float %502 %float_1 + %527 = OpFDiv %float %float_1 %526 + %528 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 %491 %int_3 + %529 = OpLoad %float %528 + %530 = OpFMul %float %529 %529 + %531 = OpFMul %float %502 %530 + %532 = OpFMul %float %531 %531 + %533 = OpFSub %float %float_1 %532 + %534 = OpExtInst %float %1 FClamp %533 %float_0 %float_1 + %535 = OpFMul %float %534 %534 + %536 = OpFMul %float %527 %535 + OpBranch %490 + %490 = OpLabel + %537 = OpPhi %float %525 %517 %536 %516 + %538 = OpFMul %float %537 %509 + %539 = OpCompositeConstruct %v3float %538 %538 %538 + %540 = OpLoad %v4float %512 + %541 = OpVectorShuffle %v3float %540 %540 0 1 2 + %542 = OpFMul %v3float %539 %541 + %543 = OpFMul %v3float %542 %57 + %544 = OpExtInst %v3float %1 Cross %206 %507 + %545 = OpDot %float %544 %544 + %546 = OpFMul %float %511 %442 + %547 = OpFMul %float %546 %546 + %548 = OpFAdd %float %545 %547 + %549 = OpFDiv %float %442 %548 + %550 = OpFMul %float %549 %549 + %551 = OpExtInst %float %1 FMin %550 %float_65504 + %552 = OpFMul %float %439 %551 + %553 = OpFMul %float %276 %552 + %554 = OpCompositeConstruct %v3float %553 %553 %553 + %555 = OpFAdd %v3float %270 %554 + %556 = OpFMul %v3float %543 %555 + %557 = OpExtInst %v3float %1 FMin %55 %556 + %489 = OpFAdd %v3float %488 %557 + %492 = OpIAdd %int %491 %int_1 + OpBranch %487 + %496 = OpLabel + %558 = OpExtInst %v3float %1 FMax %47 %47 + %559 = OpFAdd %v3float %488 %558 + %560 = OpFAdd %v3float %270 %484 + %561 = OpCompositeConstruct %v3float %160 %160 %160 + %562 = OpExtInst %v3float %1 FMix %559 %560 %561 + %563 = OpCompositeExtract %float %142 3 + %564 = OpCompositeConstruct %v3float %563 %563 %563 + %565 = OpFMul %v3float %562 %564 + %566 = OpVectorShuffle %v3float %142 %142 0 1 2 + %567 = OpFAdd %v3float %565 %566 + %568 = OpVectorShuffle %v4float %137 %567 4 5 6 3 + %569 = OpCompositeExtract %float %143 3 + %570 = OpExtInst %float %1 FMin %569 %float_65500 + %571 = OpCompositeInsert %v4float %570 %568 3 + OpStore %out_var_SV_Target0 %571 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/array-copy-error.asm.invalid.frag b/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/array-copy-error.asm.invalid.frag new file mode 100644 index 0000000..eba220b --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/array-copy-error.asm.invalid.frag @@ -0,0 +1,878 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 353 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPixelShader "main" %gl_FragCoord %in_var_TEXCOORD6 %in_var_TEXCOORD7 %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_TEXCOORD0 %in_var_PRIMITIVE_ID %gl_FrontFacing %gl_FragDepth %out_var_SV_Target0 + OpExecutionMode %MainPixelShader OriginUpperLeft + OpExecutionMode %MainPixelShader DepthReplacing + OpExecutionMode %MainPixelShader DepthLess + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_ClipToWorld" + OpMemberName %type_View 3 "View_TranslatedWorldToView" + OpMemberName %type_View 4 "View_ViewToTranslatedWorld" + OpMemberName %type_View 5 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 6 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 7 "View_ViewToClip" + OpMemberName %type_View 8 "View_ViewToClipNoAA" + OpMemberName %type_View 9 "View_ClipToView" + OpMemberName %type_View 10 "View_ClipToTranslatedWorld" + OpMemberName %type_View 11 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 12 "View_ScreenToWorld" + OpMemberName %type_View 13 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 14 "View_ViewForward" + OpMemberName %type_View 15 "PrePadding_View_908" + OpMemberName %type_View 16 "View_ViewUp" + OpMemberName %type_View 17 "PrePadding_View_924" + OpMemberName %type_View 18 "View_ViewRight" + OpMemberName %type_View 19 "PrePadding_View_940" + OpMemberName %type_View 20 "View_HMDViewNoRollUp" + OpMemberName %type_View 21 "PrePadding_View_956" + OpMemberName %type_View 22 "View_HMDViewNoRollRight" + OpMemberName %type_View 23 "PrePadding_View_972" + OpMemberName %type_View 24 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 25 "View_ScreenPositionScaleBias" + OpMemberName %type_View 26 "View_WorldCameraOrigin" + OpMemberName %type_View 27 "PrePadding_View_1020" + OpMemberName %type_View 28 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 29 "PrePadding_View_1036" + OpMemberName %type_View 30 "View_WorldViewOrigin" + OpMemberName %type_View 31 "PrePadding_View_1052" + OpMemberName %type_View 32 "View_PreViewTranslation" + OpMemberName %type_View 33 "PrePadding_View_1068" + OpMemberName %type_View 34 "View_PrevProjection" + OpMemberName %type_View 35 "View_PrevViewProj" + OpMemberName %type_View 36 "View_PrevViewRotationProj" + OpMemberName %type_View 37 "View_PrevViewToClip" + OpMemberName %type_View 38 "View_PrevClipToView" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 40 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 41 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 42 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 43 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 44 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 45 "PrePadding_View_1724" + OpMemberName %type_View 46 "View_PrevWorldViewOrigin" + OpMemberName %type_View 47 "PrePadding_View_1740" + OpMemberName %type_View 48 "View_PrevPreViewTranslation" + OpMemberName %type_View 49 "PrePadding_View_1756" + OpMemberName %type_View 50 "View_PrevInvViewProj" + OpMemberName %type_View 51 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 52 "View_ClipToPrevClip" + OpMemberName %type_View 53 "View_TemporalAAJitter" + OpMemberName %type_View 54 "View_GlobalClippingPlane" + OpMemberName %type_View 55 "View_FieldOfViewWideAngles" + OpMemberName %type_View 56 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 57 "View_ViewRectMin" + OpMemberName %type_View 58 "View_ViewSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferSizeAndInvSize" + OpMemberName %type_View 60 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 61 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 62 "View_PreExposure" + OpMemberName %type_View 63 "View_OneOverPreExposure" + OpMemberName %type_View 64 "PrePadding_View_2076" + OpMemberName %type_View 65 "View_DiffuseOverrideParameter" + OpMemberName %type_View 66 "View_SpecularOverrideParameter" + OpMemberName %type_View 67 "View_NormalOverrideParameter" + OpMemberName %type_View 68 "View_RoughnessOverrideParameter" + OpMemberName %type_View 69 "View_PrevFrameGameTime" + OpMemberName %type_View 70 "View_PrevFrameRealTime" + OpMemberName %type_View 71 "View_OutOfBoundsMask" + OpMemberName %type_View 72 "PrePadding_View_2148" + OpMemberName %type_View 73 "PrePadding_View_2152" + OpMemberName %type_View 74 "PrePadding_View_2156" + OpMemberName %type_View 75 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 76 "View_CullingSign" + OpMemberName %type_View 77 "View_NearPlane" + OpMemberName %type_View 78 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 79 "View_GameTime" + OpMemberName %type_View 80 "View_RealTime" + OpMemberName %type_View 81 "View_DeltaTime" + OpMemberName %type_View 82 "View_MaterialTextureMipBias" + OpMemberName %type_View 83 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 84 "View_Random" + OpMemberName %type_View 85 "View_FrameNumber" + OpMemberName %type_View 86 "View_StateFrameIndexMod8" + OpMemberName %type_View 87 "View_StateFrameIndex" + OpMemberName %type_View 88 "View_CameraCut" + OpMemberName %type_View 89 "View_UnlitViewmodeMask" + OpMemberName %type_View 90 "PrePadding_View_2228" + OpMemberName %type_View 91 "PrePadding_View_2232" + OpMemberName %type_View 92 "PrePadding_View_2236" + OpMemberName %type_View 93 "View_DirectionalLightColor" + OpMemberName %type_View 94 "View_DirectionalLightDirection" + OpMemberName %type_View 95 "PrePadding_View_2268" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 97 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 98 "View_TemporalAAParams" + OpMemberName %type_View 99 "View_CircleDOFParams" + OpMemberName %type_View 100 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 101 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 102 "View_DepthOfFieldScale" + OpMemberName %type_View 103 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 104 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 105 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 106 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 107 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 108 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 109 "View_GeneralPurposeTweak" + OpMemberName %type_View 110 "View_DemosaicVposOffset" + OpMemberName %type_View 111 "PrePadding_View_2412" + OpMemberName %type_View 112 "View_IndirectLightingColorScale" + OpMemberName %type_View 113 "View_HDR32bppEncodingMode" + OpMemberName %type_View 114 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 115 "View_AtmosphericFogSunPower" + OpMemberName %type_View 116 "View_AtmosphericFogPower" + OpMemberName %type_View 117 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 118 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 119 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 120 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 121 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 122 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 123 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 124 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 125 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 126 "View_AtmosphericFogSunDiscHalfApexAngleRadian" + OpMemberName %type_View 127 "PrePadding_View_2492" + OpMemberName %type_View 128 "View_AtmosphericFogSunDiscLuminance" + OpMemberName %type_View 129 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 130 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 131 "PrePadding_View_2520" + OpMemberName %type_View 132 "PrePadding_View_2524" + OpMemberName %type_View 133 "View_AtmosphericFogSunColor" + OpMemberName %type_View 134 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 135 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 136 "View_AmbientCubemapTint" + OpMemberName %type_View 137 "View_AmbientCubemapIntensity" + OpMemberName %type_View 138 "View_SkyLightParameters" + OpMemberName %type_View 139 "PrePadding_View_2584" + OpMemberName %type_View 140 "PrePadding_View_2588" + OpMemberName %type_View 141 "View_SkyLightColor" + OpMemberName %type_View 142 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 143 "View_MobilePreviewMode" + OpMemberName %type_View 144 "View_HMDEyePaddingOffset" + OpMemberName %type_View 145 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 146 "View_ShowDecalsMask" + OpMemberName %type_View 147 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 148 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 149 "PrePadding_View_2744" + OpMemberName %type_View 150 "PrePadding_View_2748" + OpMemberName %type_View 151 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 152 "View_StereoPassIndex" + OpMemberName %type_View 153 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 154 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 155 "View_GlobalVolumeDimension" + OpMemberName %type_View 156 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 157 "View_MaxGlobalDistance" + OpMemberName %type_View 158 "PrePadding_View_2908" + OpMemberName %type_View 159 "View_CursorPosition" + OpMemberName %type_View 160 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 161 "PrePadding_View_2924" + OpMemberName %type_View 162 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 163 "PrePadding_View_2940" + OpMemberName %type_View 164 "View_VolumetricFogGridZParams" + OpMemberName %type_View 165 "PrePadding_View_2956" + OpMemberName %type_View 166 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 167 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 168 "PrePadding_View_2972" + OpMemberName %type_View 169 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 170 "PrePadding_View_2988" + OpMemberName %type_View 171 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 172 "PrePadding_View_3004" + OpMemberName %type_View 173 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 174 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 175 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 176 "View_StereoIPD" + OpMemberName %type_View 177 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 178 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_PrimitiveDither "type.PrimitiveDither" + OpMemberName %type_PrimitiveDither 0 "PrimitiveDither_LODFactor" + OpName %PrimitiveDither "PrimitiveDither" + OpName %type_PrimitiveFade "type.PrimitiveFade" + OpMemberName %type_PrimitiveFade 0 "PrimitiveFade_FadeTimeScaleBias" + OpName %PrimitiveFade "PrimitiveFade" + OpName %type_Material "type.Material" + OpMemberName %type_Material 0 "Material_VectorExpressions" + OpMemberName %type_Material 1 "Material_ScalarExpressions" + OpName %Material "Material" + OpName %type_2d_image "type.2d.image" + OpName %Material_Texture2D_0 "Material_Texture2D_0" + OpName %type_sampler "type.sampler" + OpName %Material_Texture2D_0Sampler "Material_Texture2D_0Sampler" + OpName %Material_Texture2D_3 "Material_Texture2D_3" + OpName %Material_Texture2D_3Sampler "Material_Texture2D_3Sampler" + OpName %in_var_TEXCOORD6 "in.var.TEXCOORD6" + OpName %in_var_TEXCOORD7 "in.var.TEXCOORD7" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_PRIMITIVE_ID "in.var.PRIMITIVE_ID" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPixelShader "MainPixelShader" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorateString %in_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorateString %in_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %in_var_PRIMITIVE_ID UserSemantic "PRIMITIVE_ID" + OpDecorate %in_var_PRIMITIVE_ID Flat + OpDecorate %gl_FrontFacing BuiltIn FrontFacing + OpDecorateString %gl_FrontFacing UserSemantic "SV_IsFrontFace" + OpDecorate %gl_FrontFacing Flat + OpDecorate %gl_FragDepth BuiltIn FragDepth + OpDecorateString %gl_FragDepth UserSemantic "SV_DepthLessEqual" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD6 Location 0 + OpDecorate %in_var_TEXCOORD7 Location 1 + OpDecorate %in_var_TEXCOORD10_centroid Location 2 + OpDecorate %in_var_TEXCOORD11_centroid Location 3 + OpDecorate %in_var_TEXCOORD0 Location 4 + OpDecorate %in_var_PRIMITIVE_ID Location 5 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %PrimitiveDither DescriptorSet 0 + OpDecorate %PrimitiveDither Binding 1 + OpDecorate %PrimitiveFade DescriptorSet 0 + OpDecorate %PrimitiveFade Binding 2 + OpDecorate %Material DescriptorSet 0 + OpDecorate %Material Binding 3 + OpDecorate %Material_Texture2D_0 DescriptorSet 0 + OpDecorate %Material_Texture2D_0 Binding 0 + OpDecorate %Material_Texture2D_0Sampler DescriptorSet 0 + OpDecorate %Material_Texture2D_0Sampler Binding 0 + OpDecorate %Material_Texture2D_3 DescriptorSet 0 + OpDecorate %Material_Texture2D_3 Binding 1 + OpDecorate %Material_Texture2D_3Sampler DescriptorSet 0 + OpDecorate %Material_Texture2D_3Sampler Binding 1 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 13 MatrixStride 16 + OpMemberDecorate %type_View 13 ColMajor + OpMemberDecorate %type_View 14 Offset 896 + OpMemberDecorate %type_View 15 Offset 908 + OpMemberDecorate %type_View 16 Offset 912 + OpMemberDecorate %type_View 17 Offset 924 + OpMemberDecorate %type_View 18 Offset 928 + OpMemberDecorate %type_View 19 Offset 940 + OpMemberDecorate %type_View 20 Offset 944 + OpMemberDecorate %type_View 21 Offset 956 + OpMemberDecorate %type_View 22 Offset 960 + OpMemberDecorate %type_View 23 Offset 972 + OpMemberDecorate %type_View 24 Offset 976 + OpMemberDecorate %type_View 25 Offset 992 + OpMemberDecorate %type_View 26 Offset 1008 + OpMemberDecorate %type_View 27 Offset 1020 + OpMemberDecorate %type_View 28 Offset 1024 + OpMemberDecorate %type_View 29 Offset 1036 + OpMemberDecorate %type_View 30 Offset 1040 + OpMemberDecorate %type_View 31 Offset 1052 + OpMemberDecorate %type_View 32 Offset 1056 + OpMemberDecorate %type_View 33 Offset 1068 + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 43 MatrixStride 16 + OpMemberDecorate %type_View 43 ColMajor + OpMemberDecorate %type_View 44 Offset 1712 + OpMemberDecorate %type_View 45 Offset 1724 + OpMemberDecorate %type_View 46 Offset 1728 + OpMemberDecorate %type_View 47 Offset 1740 + OpMemberDecorate %type_View 48 Offset 1744 + OpMemberDecorate %type_View 49 Offset 1756 + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 52 MatrixStride 16 + OpMemberDecorate %type_View 52 ColMajor + OpMemberDecorate %type_View 53 Offset 1952 + OpMemberDecorate %type_View 54 Offset 1968 + OpMemberDecorate %type_View 55 Offset 1984 + OpMemberDecorate %type_View 56 Offset 1992 + OpMemberDecorate %type_View 57 Offset 2000 + OpMemberDecorate %type_View 58 Offset 2016 + OpMemberDecorate %type_View 59 Offset 2032 + OpMemberDecorate %type_View 60 Offset 2048 + OpMemberDecorate %type_View 61 Offset 2064 + OpMemberDecorate %type_View 62 Offset 2068 + OpMemberDecorate %type_View 63 Offset 2072 + OpMemberDecorate %type_View 64 Offset 2076 + OpMemberDecorate %type_View 65 Offset 2080 + OpMemberDecorate %type_View 66 Offset 2096 + OpMemberDecorate %type_View 67 Offset 2112 + OpMemberDecorate %type_View 68 Offset 2128 + OpMemberDecorate %type_View 69 Offset 2136 + OpMemberDecorate %type_View 70 Offset 2140 + OpMemberDecorate %type_View 71 Offset 2144 + OpMemberDecorate %type_View 72 Offset 2148 + OpMemberDecorate %type_View 73 Offset 2152 + OpMemberDecorate %type_View 74 Offset 2156 + OpMemberDecorate %type_View 75 Offset 2160 + OpMemberDecorate %type_View 76 Offset 2172 + OpMemberDecorate %type_View 77 Offset 2176 + OpMemberDecorate %type_View 78 Offset 2180 + OpMemberDecorate %type_View 79 Offset 2184 + OpMemberDecorate %type_View 80 Offset 2188 + OpMemberDecorate %type_View 81 Offset 2192 + OpMemberDecorate %type_View 82 Offset 2196 + OpMemberDecorate %type_View 83 Offset 2200 + OpMemberDecorate %type_View 84 Offset 2204 + OpMemberDecorate %type_View 85 Offset 2208 + OpMemberDecorate %type_View 86 Offset 2212 + OpMemberDecorate %type_View 87 Offset 2216 + OpMemberDecorate %type_View 88 Offset 2220 + OpMemberDecorate %type_View 89 Offset 2224 + OpMemberDecorate %type_View 90 Offset 2228 + OpMemberDecorate %type_View 91 Offset 2232 + OpMemberDecorate %type_View 92 Offset 2236 + OpMemberDecorate %type_View 93 Offset 2240 + OpMemberDecorate %type_View 94 Offset 2256 + OpMemberDecorate %type_View 95 Offset 2268 + OpMemberDecorate %type_View 96 Offset 2272 + OpMemberDecorate %type_View 97 Offset 2304 + OpMemberDecorate %type_View 98 Offset 2336 + OpMemberDecorate %type_View 99 Offset 2352 + OpMemberDecorate %type_View 100 Offset 2368 + OpMemberDecorate %type_View 101 Offset 2372 + OpMemberDecorate %type_View 102 Offset 2376 + OpMemberDecorate %type_View 103 Offset 2380 + OpMemberDecorate %type_View 104 Offset 2384 + OpMemberDecorate %type_View 105 Offset 2388 + OpMemberDecorate %type_View 106 Offset 2392 + OpMemberDecorate %type_View 107 Offset 2396 + OpMemberDecorate %type_View 108 Offset 2400 + OpMemberDecorate %type_View 109 Offset 2404 + OpMemberDecorate %type_View 110 Offset 2408 + OpMemberDecorate %type_View 111 Offset 2412 + OpMemberDecorate %type_View 112 Offset 2416 + OpMemberDecorate %type_View 113 Offset 2428 + OpMemberDecorate %type_View 114 Offset 2432 + OpMemberDecorate %type_View 115 Offset 2444 + OpMemberDecorate %type_View 116 Offset 2448 + OpMemberDecorate %type_View 117 Offset 2452 + OpMemberDecorate %type_View 118 Offset 2456 + OpMemberDecorate %type_View 119 Offset 2460 + OpMemberDecorate %type_View 120 Offset 2464 + OpMemberDecorate %type_View 121 Offset 2468 + OpMemberDecorate %type_View 122 Offset 2472 + OpMemberDecorate %type_View 123 Offset 2476 + OpMemberDecorate %type_View 124 Offset 2480 + OpMemberDecorate %type_View 125 Offset 2484 + OpMemberDecorate %type_View 126 Offset 2488 + OpMemberDecorate %type_View 127 Offset 2492 + OpMemberDecorate %type_View 128 Offset 2496 + OpMemberDecorate %type_View 129 Offset 2512 + OpMemberDecorate %type_View 130 Offset 2516 + OpMemberDecorate %type_View 131 Offset 2520 + OpMemberDecorate %type_View 132 Offset 2524 + OpMemberDecorate %type_View 133 Offset 2528 + OpMemberDecorate %type_View 134 Offset 2544 + OpMemberDecorate %type_View 135 Offset 2556 + OpMemberDecorate %type_View 136 Offset 2560 + OpMemberDecorate %type_View 137 Offset 2576 + OpMemberDecorate %type_View 138 Offset 2580 + OpMemberDecorate %type_View 139 Offset 2584 + OpMemberDecorate %type_View 140 Offset 2588 + OpMemberDecorate %type_View 141 Offset 2592 + OpMemberDecorate %type_View 142 Offset 2608 + OpMemberDecorate %type_View 143 Offset 2720 + OpMemberDecorate %type_View 144 Offset 2724 + OpMemberDecorate %type_View 145 Offset 2728 + OpMemberDecorate %type_View 146 Offset 2732 + OpMemberDecorate %type_View 147 Offset 2736 + OpMemberDecorate %type_View 148 Offset 2740 + OpMemberDecorate %type_View 149 Offset 2744 + OpMemberDecorate %type_View 150 Offset 2748 + OpMemberDecorate %type_View 151 Offset 2752 + OpMemberDecorate %type_View 152 Offset 2764 + OpMemberDecorate %type_View 153 Offset 2768 + OpMemberDecorate %type_View 154 Offset 2832 + OpMemberDecorate %type_View 155 Offset 2896 + OpMemberDecorate %type_View 156 Offset 2900 + OpMemberDecorate %type_View 157 Offset 2904 + OpMemberDecorate %type_View 158 Offset 2908 + OpMemberDecorate %type_View 159 Offset 2912 + OpMemberDecorate %type_View 160 Offset 2920 + OpMemberDecorate %type_View 161 Offset 2924 + OpMemberDecorate %type_View 162 Offset 2928 + OpMemberDecorate %type_View 163 Offset 2940 + OpMemberDecorate %type_View 164 Offset 2944 + OpMemberDecorate %type_View 165 Offset 2956 + OpMemberDecorate %type_View 166 Offset 2960 + OpMemberDecorate %type_View 167 Offset 2968 + OpMemberDecorate %type_View 168 Offset 2972 + OpMemberDecorate %type_View 169 Offset 2976 + OpMemberDecorate %type_View 170 Offset 2988 + OpMemberDecorate %type_View 171 Offset 2992 + OpMemberDecorate %type_View 172 Offset 3004 + OpMemberDecorate %type_View 173 Offset 3008 + OpMemberDecorate %type_View 174 Offset 3020 + OpMemberDecorate %type_View 175 Offset 3024 + OpMemberDecorate %type_View 176 Offset 3036 + OpMemberDecorate %type_View 177 Offset 3040 + OpMemberDecorate %type_View 178 Offset 3044 + OpDecorate %type_View Block + OpMemberDecorate %type_PrimitiveDither 0 Offset 0 + OpDecorate %type_PrimitiveDither Block + OpMemberDecorate %type_PrimitiveFade 0 Offset 0 + OpDecorate %type_PrimitiveFade Block + OpDecorate %_arr_v4float_uint_9 ArrayStride 16 + OpDecorate %_arr_v4float_uint_3 ArrayStride 16 + OpMemberDecorate %type_Material 0 Offset 0 + OpMemberDecorate %type_Material 1 Offset 144 + OpDecorate %type_Material Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %v2int = OpTypeVector %int 2 +%float_0_00100000005 = OpConstant %float 0.00100000005 + %int_2 = OpConstant %int 2 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float_0 = OpConstant %float 0 + %49 = OpConstantComposite %v2float %float_0 %float_0 + %float_1 = OpConstant %float 1 + %int_4 = OpConstant %int 4 + %int_11 = OpConstant %int 11 +%float_0_249500006 = OpConstant %float 0.249500006 + %54 = OpConstantComposite %v2float %float_0_249500006 %float_0_249500006 +%float_0_499992371 = OpConstant %float 0.499992371 + %56 = OpConstantComposite %v2float %float_0_499992371 %float_0_499992371 + %int_32 = OpConstant %int 32 + %int_53 = OpConstant %int 53 + %int_57 = OpConstant %int 57 + %int_80 = OpConstant %int 80 + %int_82 = OpConstant %int 82 + %int_98 = OpConstant %int 98 + %uint_1 = OpConstant %uint 1 +%mat3v3float = OpTypeMatrix %v3float 3 + %float_2 = OpConstant %float 2 + %float_n1 = OpConstant %float -1 + %67 = OpConstantComposite %v2float %float_n1 %float_n1 + %bool = OpTypeBool + %float_n0_5 = OpConstant %float -0.5 + %70 = OpConstantComposite %v3float %float_0 %float_0 %float_1 +%float_0_333299994 = OpConstant %float 0.333299994 + %uint_5 = OpConstant %uint 5 +%float_347_834503 = OpConstant %float 347.834503 +%float_3343_28369 = OpConstant %float 3343.28369 + %75 = OpConstantComposite %v2float %float_347_834503 %float_3343_28369 + %float_1000 = OpConstant %float 1000 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %uint %uint %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v2int %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_PrimitiveDither = OpTypeStruct %float +%_ptr_Uniform_type_PrimitiveDither = OpTypePointer Uniform %type_PrimitiveDither +%type_PrimitiveFade = OpTypeStruct %v2float +%_ptr_Uniform_type_PrimitiveFade = OpTypePointer Uniform %type_PrimitiveFade + %uint_9 = OpConstant %uint 9 +%_arr_v4float_uint_9 = OpTypeArray %v4float %uint_9 + %uint_3 = OpConstant %uint 3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%type_Material = OpTypeStruct %_arr_v4float_uint_9 %_arr_v4float_uint_3 +%_ptr_Uniform_type_Material = OpTypePointer Uniform %type_Material +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%_ptr_Input__arr_v4float_uint_1 = OpTypePointer Input %_arr_v4float_uint_1 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Input_bool = OpTypePointer Input %bool +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %93 = OpTypeFunction %void +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%type_sampled_image = OpTypeSampledImage %type_2d_image + %View = OpVariable %_ptr_Uniform_type_View Uniform +%PrimitiveDither = OpVariable %_ptr_Uniform_type_PrimitiveDither Uniform +%PrimitiveFade = OpVariable %_ptr_Uniform_type_PrimitiveFade Uniform + %Material = OpVariable %_ptr_Uniform_type_Material Uniform +%Material_Texture2D_0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%Material_Texture2D_0Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%Material_Texture2D_3 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%Material_Texture2D_3Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD6 = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD7 = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input__arr_v4float_uint_1 Input +%in_var_PRIMITIVE_ID = OpVariable %_ptr_Input_uint Input +%gl_FrontFacing = OpVariable %_ptr_Input_bool Input +%gl_FragDepth = OpVariable %_ptr_Output_float Output +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %98 = OpUndef %float + %99 = OpConstantNull %v2float +%float_0_015625 = OpConstant %float 0.015625 + %101 = OpConstantComposite %v2float %float_0_015625 %float_0_015625 +%float_0_166666672 = OpConstant %float 0.166666672 + %103 = OpUndef %float + %104 = OpConstantNull %v3float +%MainPixelShader = OpFunction %void None %93 + %105 = OpLabel + %106 = OpLoad %v4float %gl_FragCoord + %107 = OpLoad %v4float %in_var_TEXCOORD6 + %108 = OpLoad %v4float %in_var_TEXCOORD7 + %109 = OpLoad %v4float %in_var_TEXCOORD10_centroid + %110 = OpLoad %v4float %in_var_TEXCOORD11_centroid + %111 = OpLoad %_arr_v4float_uint_1 %in_var_TEXCOORD0 + %112 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_4 + %113 = OpLoad %mat4v4float %112 + %114 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_11 + %115 = OpLoad %mat4v4float %114 + %116 = OpAccessChain %_ptr_Uniform_v3float %View %int_32 + %117 = OpLoad %v3float %116 + %118 = OpAccessChain %_ptr_Uniform_v4float %View %int_53 + %119 = OpLoad %v4float %118 + %120 = OpAccessChain %_ptr_Uniform_v4float %View %int_57 + %121 = OpLoad %v4float %120 + %122 = OpAccessChain %_ptr_Uniform_float %View %int_80 + %123 = OpLoad %float %122 + %124 = OpCompositeExtract %v4float %111 0 + %125 = OpVectorShuffle %v2float %99 %124 2 3 + %126 = OpVectorShuffle %v3float %109 %109 0 1 2 + %127 = OpVectorShuffle %v3float %110 %110 0 1 2 + %128 = OpExtInst %v3float %1 Cross %127 %126 + %129 = OpCompositeExtract %float %110 3 + %130 = OpCompositeConstruct %v3float %129 %129 %129 + %131 = OpFMul %v3float %128 %130 + %132 = OpCompositeConstruct %mat3v3float %126 %131 %127 + %133 = OpVectorShuffle %v2float %106 %106 0 1 + %134 = OpVectorShuffle %v2float %121 %121 0 1 + %135 = OpFSub %v2float %133 %134 + %136 = OpCompositeExtract %float %106 2 + %137 = OpCompositeConstruct %v4float %103 %103 %136 %float_1 + %138 = OpCompositeExtract %float %106 3 + %139 = OpCompositeConstruct %v4float %138 %138 %138 %138 + %140 = OpFMul %v4float %137 %139 + %141 = OpCompositeExtract %float %106 0 + %142 = OpCompositeExtract %float %106 1 + %143 = OpCompositeConstruct %v4float %141 %142 %136 %float_1 + %144 = OpMatrixTimesVector %v4float %115 %143 + %145 = OpVectorShuffle %v3float %144 %144 0 1 2 + %146 = OpCompositeExtract %float %144 3 + %147 = OpCompositeConstruct %v3float %146 %146 %146 + %148 = OpFDiv %v3float %145 %147 + %149 = OpFSub %v3float %148 %117 + %150 = OpFNegate %v3float %148 + %151 = OpExtInst %v3float %1 Normalize %150 + %152 = OpVectorTimesMatrix %v3float %151 %132 + %153 = OpVectorShuffle %v2float %152 %152 0 1 + %154 = OpFMul %v2float %153 %67 + %155 = OpCompositeExtract %float %152 2 + %156 = OpCompositeConstruct %v2float %155 %155 + %157 = OpFDiv %v2float %154 %156 + %158 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_0 + %159 = OpLoad %float %158 + %160 = OpCompositeConstruct %v2float %159 %159 + %161 = OpFMul %v2float %160 %157 + %162 = OpDot %float %151 %127 + %163 = OpExtInst %float %1 FAbs %162 + %164 = OpExtInst %float %1 FMax %163 %float_0 + %165 = OpExtInst %float %1 FMin %164 %float_1 + %166 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_1 + %167 = OpLoad %float %166 + %168 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_2 + %169 = OpLoad %float %168 + %170 = OpExtInst %float %1 FMix %167 %169 %165 + %171 = OpExtInst %float %1 Floor %170 + %172 = OpFDiv %float %float_1 %170 + %173 = OpCompositeConstruct %v2float %172 %172 + %174 = OpFMul %v2float %161 %173 + %175 = OpDPdx %v2float %125 + %176 = OpDPdy %v2float %125 + %177 = OpLoad %type_2d_image %Material_Texture2D_0 + %178 = OpLoad %type_sampler %Material_Texture2D_0Sampler + OpBranch %179 + %179 = OpLabel + %180 = OpPhi %float %float_1 %105 %181 %182 + %183 = OpPhi %v2float %49 %105 %184 %182 + %185 = OpPhi %int %int_0 %105 %186 %182 + %187 = OpPhi %float %float_1 %105 %188 %182 + %189 = OpPhi %float %float_1 %105 %180 %182 + %190 = OpConvertSToF %float %185 + %191 = OpFAdd %float %171 %float_2 + %192 = OpFOrdLessThan %bool %190 %191 + OpLoopMerge %193 %182 None + OpBranchConditional %192 %194 %193 + %194 = OpLabel + %195 = OpFAdd %v2float %125 %183 + %196 = OpSampledImage %type_sampled_image %177 %178 + %197 = OpImageSampleExplicitLod %v4float %196 %195 Grad %175 %176 + %188 = OpCompositeExtract %float %197 1 + %198 = OpFOrdLessThan %bool %180 %188 + OpSelectionMerge %182 None + OpBranchConditional %198 %199 %182 + %199 = OpLabel + %200 = OpFSub %float %189 %187 + %201 = OpFSub %float %188 %180 + %202 = OpFAdd %float %200 %201 + %203 = OpFDiv %float %201 %202 + %204 = OpFMul %float %189 %203 + %205 = OpFSub %float %float_1 %203 + %206 = OpFMul %float %180 %205 + %207 = OpFAdd %float %204 %206 + %208 = OpCompositeConstruct %v2float %203 %203 + %209 = OpFMul %v2float %208 %174 + %210 = OpFSub %v2float %183 %209 + OpBranch %193 + %182 = OpLabel + %181 = OpFSub %float %180 %172 + %184 = OpFAdd %v2float %183 %174 + %186 = OpIAdd %int %185 %int_1 + OpBranch %179 + %193 = OpLabel + %211 = OpPhi %float %98 %179 %207 %199 + %212 = OpPhi %v2float %183 %179 %210 %199 + %213 = OpVectorShuffle %v2float %212 %104 0 1 + %214 = OpFAdd %v2float %125 %213 + %215 = OpAccessChain %_ptr_Uniform_float %View %int_82 + %216 = OpLoad %float %215 + %217 = OpSampledImage %type_sampled_image %177 %178 + %218 = OpImageSampleImplicitLod %v4float %217 %214 Bias %216 + %219 = OpCompositeExtract %float %218 0 + %220 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_2 %int_1 + %221 = OpLoad %float %220 + %222 = OpFMul %float %219 %221 + %223 = OpFSub %float %float_1 %222 + %224 = OpExtInst %float %1 FMax %223 %float_0 + %225 = OpExtInst %float %1 FMin %224 %float_1 + %226 = OpAccessChain %_ptr_Uniform_float %View %int_98 %int_0 + %227 = OpLoad %float %226 + %228 = OpCompositeConstruct %v2float %227 %227 + %229 = OpFAdd %v2float %135 %228 + %230 = OpCompositeExtract %float %229 0 + %231 = OpConvertFToU %uint %230 + %232 = OpCompositeExtract %float %229 1 + %233 = OpConvertFToU %uint %232 + %234 = OpIMul %uint %uint_2 %233 + %235 = OpIAdd %uint %231 %234 + %236 = OpUMod %uint %235 %uint_5 + %237 = OpConvertUToF %float %236 + %238 = OpFMul %v2float %135 %101 + %239 = OpLoad %type_2d_image %Material_Texture2D_3 + %240 = OpLoad %type_sampler %Material_Texture2D_3Sampler + %241 = OpSampledImage %type_sampled_image %239 %240 + %242 = OpImageSampleImplicitLod %v4float %241 %238 Bias %216 + %243 = OpCompositeExtract %float %242 0 + %244 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_2 %int_2 + %245 = OpLoad %float %244 + %246 = OpFMul %float %243 %245 + %247 = OpFAdd %float %237 %246 + %248 = OpFMul %float %247 %float_0_166666672 + %249 = OpFAdd %float %225 %248 + %250 = OpFAdd %float %249 %float_n0_5 + %251 = OpCompositeExtract %float %218 2 + %252 = OpFAdd %float %251 %250 + %253 = OpSampledImage %type_sampled_image %239 %240 + %254 = OpImageSampleImplicitLod %v4float %253 %238 Bias %216 + %255 = OpCompositeExtract %float %254 0 + %256 = OpFAdd %float %237 %255 + %257 = OpFMul %float %256 %float_0_166666672 + %258 = OpAccessChain %_ptr_Uniform_float %PrimitiveFade %int_0 %int_0 + %259 = OpLoad %float %258 + %260 = OpFMul %float %123 %259 + %261 = OpAccessChain %_ptr_Uniform_float %PrimitiveFade %int_0 %int_1 + %262 = OpLoad %float %261 + %263 = OpFAdd %float %260 %262 + %264 = OpExtInst %float %1 FClamp %263 %float_0 %float_1 + %265 = OpFAdd %float %264 %257 + %266 = OpFAdd %float %265 %float_n0_5 + %267 = OpFMul %float %252 %266 + %268 = OpFSub %float %float_1 %211 + %269 = OpFMul %float %268 %159 + %270 = OpCompositeExtract %float %212 0 + %271 = OpCompositeExtract %float %212 1 + %272 = OpCompositeConstruct %v3float %270 %271 %269 + %273 = OpDot %float %272 %272 + %274 = OpExtInst %float %1 Sqrt %273 + %275 = OpDPdx %v2float %125 + %276 = OpExtInst %v2float %1 FAbs %275 + %277 = OpDot %float %276 %276 + %278 = OpExtInst %float %1 Sqrt %277 + %279 = OpDPdx %v3float %149 + %280 = OpDot %float %279 %279 + %281 = OpExtInst %float %1 Sqrt %280 + %282 = OpFDiv %float %278 %281 + %283 = OpDPdy %v2float %125 + %284 = OpExtInst %v2float %1 FAbs %283 + %285 = OpDot %float %284 %284 + %286 = OpExtInst %float %1 Sqrt %285 + %287 = OpDPdy %v3float %149 + %288 = OpDot %float %287 %287 + %289 = OpExtInst %float %1 Sqrt %288 + %290 = OpFDiv %float %286 %289 + %291 = OpExtInst %float %1 FMax %282 %290 + %292 = OpCompositeExtract %v4float %113 0 + %293 = OpVectorShuffle %v3float %292 %292 0 1 2 + %294 = OpCompositeExtract %v4float %113 1 + %295 = OpVectorShuffle %v3float %294 %294 0 1 2 + %296 = OpCompositeExtract %v4float %113 2 + %297 = OpVectorShuffle %v3float %296 %296 0 1 2 + %298 = OpCompositeConstruct %mat3v3float %293 %295 %297 + %299 = OpMatrixTimesVector %v3float %298 %70 + %300 = OpDot %float %299 %151 + %301 = OpExtInst %float %1 FAbs %300 + %302 = OpFDiv %float %291 %301 + %303 = OpFDiv %float %274 %302 + %304 = OpAccessChain %_ptr_Uniform_float %PrimitiveDither %int_0 + %305 = OpLoad %float %304 + %306 = OpFOrdNotEqual %bool %305 %float_0 + OpSelectionMerge %307 None + OpBranchConditional %306 %308 %307 + %308 = OpLabel + %309 = OpExtInst %float %1 FAbs %305 + %310 = OpFOrdGreaterThan %bool %309 %float_0_00100000005 + OpSelectionMerge %311 None + OpBranchConditional %310 %312 %311 + %312 = OpLabel + %313 = OpExtInst %v2float %1 Floor %133 + %314 = OpDot %float %313 %75 + %315 = OpExtInst %float %1 Cos %314 + %316 = OpFMul %float %315 %float_1000 + %317 = OpExtInst %float %1 Fract %316 + %318 = OpFOrdLessThan %bool %305 %float_0 + %319 = OpFAdd %float %305 %float_1 + %320 = OpFOrdGreaterThan %bool %319 %317 + %321 = OpFOrdLessThan %bool %305 %317 + %322 = OpSelect %bool %318 %320 %321 + %323 = OpSelect %float %322 %float_1 %float_0 + %324 = OpFSub %float %323 %float_0_00100000005 + %325 = OpFOrdLessThan %bool %324 %float_0 + OpSelectionMerge %326 None + OpBranchConditional %325 %327 %326 + %327 = OpLabel + OpKill + %326 = OpLabel + OpBranch %311 + %311 = OpLabel + OpBranch %307 + %307 = OpLabel + %328 = OpFSub %float %267 %float_0_333299994 + %329 = OpFOrdLessThan %bool %328 %float_0 + OpSelectionMerge %330 None + OpBranchConditional %329 %331 %330 + %331 = OpLabel + OpKill + %330 = OpLabel + %332 = OpCompositeExtract %float %140 2 + %333 = OpCompositeExtract %float %140 3 + %334 = OpFAdd %float %333 %303 + %335 = OpFDiv %float %332 %334 + %336 = OpExtInst %float %1 FMin %335 %136 + %337 = OpVectorShuffle %v2float %107 %107 0 1 + %338 = OpCompositeExtract %float %107 3 + %339 = OpCompositeConstruct %v2float %338 %338 + %340 = OpFDiv %v2float %337 %339 + %341 = OpVectorShuffle %v2float %119 %119 0 1 + %342 = OpFSub %v2float %340 %341 + %343 = OpVectorShuffle %v2float %108 %108 0 1 + %344 = OpCompositeExtract %float %108 3 + %345 = OpCompositeConstruct %v2float %344 %344 + %346 = OpFDiv %v2float %343 %345 + %347 = OpVectorShuffle %v2float %119 %119 2 3 + %348 = OpFSub %v2float %346 %347 + %349 = OpFSub %v2float %342 %348 + %350 = OpFMul %v2float %349 %54 + %351 = OpFAdd %v2float %350 %56 + %352 = OpVectorShuffle %v4float %351 %49 0 1 2 3 + OpStore %gl_FragDepth %336 + OpStore %out_var_SV_Target0 %352 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/phi-variable-declaration.asm.invalid.frag b/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/phi-variable-declaration.asm.invalid.frag new file mode 100644 index 0000000..eba220b --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4-no-opt/asm/frag/phi-variable-declaration.asm.invalid.frag @@ -0,0 +1,878 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 353 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPixelShader "main" %gl_FragCoord %in_var_TEXCOORD6 %in_var_TEXCOORD7 %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_TEXCOORD0 %in_var_PRIMITIVE_ID %gl_FrontFacing %gl_FragDepth %out_var_SV_Target0 + OpExecutionMode %MainPixelShader OriginUpperLeft + OpExecutionMode %MainPixelShader DepthReplacing + OpExecutionMode %MainPixelShader DepthLess + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_ClipToWorld" + OpMemberName %type_View 3 "View_TranslatedWorldToView" + OpMemberName %type_View 4 "View_ViewToTranslatedWorld" + OpMemberName %type_View 5 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 6 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 7 "View_ViewToClip" + OpMemberName %type_View 8 "View_ViewToClipNoAA" + OpMemberName %type_View 9 "View_ClipToView" + OpMemberName %type_View 10 "View_ClipToTranslatedWorld" + OpMemberName %type_View 11 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 12 "View_ScreenToWorld" + OpMemberName %type_View 13 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 14 "View_ViewForward" + OpMemberName %type_View 15 "PrePadding_View_908" + OpMemberName %type_View 16 "View_ViewUp" + OpMemberName %type_View 17 "PrePadding_View_924" + OpMemberName %type_View 18 "View_ViewRight" + OpMemberName %type_View 19 "PrePadding_View_940" + OpMemberName %type_View 20 "View_HMDViewNoRollUp" + OpMemberName %type_View 21 "PrePadding_View_956" + OpMemberName %type_View 22 "View_HMDViewNoRollRight" + OpMemberName %type_View 23 "PrePadding_View_972" + OpMemberName %type_View 24 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 25 "View_ScreenPositionScaleBias" + OpMemberName %type_View 26 "View_WorldCameraOrigin" + OpMemberName %type_View 27 "PrePadding_View_1020" + OpMemberName %type_View 28 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 29 "PrePadding_View_1036" + OpMemberName %type_View 30 "View_WorldViewOrigin" + OpMemberName %type_View 31 "PrePadding_View_1052" + OpMemberName %type_View 32 "View_PreViewTranslation" + OpMemberName %type_View 33 "PrePadding_View_1068" + OpMemberName %type_View 34 "View_PrevProjection" + OpMemberName %type_View 35 "View_PrevViewProj" + OpMemberName %type_View 36 "View_PrevViewRotationProj" + OpMemberName %type_View 37 "View_PrevViewToClip" + OpMemberName %type_View 38 "View_PrevClipToView" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 40 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 41 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 42 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 43 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 44 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 45 "PrePadding_View_1724" + OpMemberName %type_View 46 "View_PrevWorldViewOrigin" + OpMemberName %type_View 47 "PrePadding_View_1740" + OpMemberName %type_View 48 "View_PrevPreViewTranslation" + OpMemberName %type_View 49 "PrePadding_View_1756" + OpMemberName %type_View 50 "View_PrevInvViewProj" + OpMemberName %type_View 51 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 52 "View_ClipToPrevClip" + OpMemberName %type_View 53 "View_TemporalAAJitter" + OpMemberName %type_View 54 "View_GlobalClippingPlane" + OpMemberName %type_View 55 "View_FieldOfViewWideAngles" + OpMemberName %type_View 56 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 57 "View_ViewRectMin" + OpMemberName %type_View 58 "View_ViewSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferSizeAndInvSize" + OpMemberName %type_View 60 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 61 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 62 "View_PreExposure" + OpMemberName %type_View 63 "View_OneOverPreExposure" + OpMemberName %type_View 64 "PrePadding_View_2076" + OpMemberName %type_View 65 "View_DiffuseOverrideParameter" + OpMemberName %type_View 66 "View_SpecularOverrideParameter" + OpMemberName %type_View 67 "View_NormalOverrideParameter" + OpMemberName %type_View 68 "View_RoughnessOverrideParameter" + OpMemberName %type_View 69 "View_PrevFrameGameTime" + OpMemberName %type_View 70 "View_PrevFrameRealTime" + OpMemberName %type_View 71 "View_OutOfBoundsMask" + OpMemberName %type_View 72 "PrePadding_View_2148" + OpMemberName %type_View 73 "PrePadding_View_2152" + OpMemberName %type_View 74 "PrePadding_View_2156" + OpMemberName %type_View 75 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 76 "View_CullingSign" + OpMemberName %type_View 77 "View_NearPlane" + OpMemberName %type_View 78 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 79 "View_GameTime" + OpMemberName %type_View 80 "View_RealTime" + OpMemberName %type_View 81 "View_DeltaTime" + OpMemberName %type_View 82 "View_MaterialTextureMipBias" + OpMemberName %type_View 83 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 84 "View_Random" + OpMemberName %type_View 85 "View_FrameNumber" + OpMemberName %type_View 86 "View_StateFrameIndexMod8" + OpMemberName %type_View 87 "View_StateFrameIndex" + OpMemberName %type_View 88 "View_CameraCut" + OpMemberName %type_View 89 "View_UnlitViewmodeMask" + OpMemberName %type_View 90 "PrePadding_View_2228" + OpMemberName %type_View 91 "PrePadding_View_2232" + OpMemberName %type_View 92 "PrePadding_View_2236" + OpMemberName %type_View 93 "View_DirectionalLightColor" + OpMemberName %type_View 94 "View_DirectionalLightDirection" + OpMemberName %type_View 95 "PrePadding_View_2268" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 97 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 98 "View_TemporalAAParams" + OpMemberName %type_View 99 "View_CircleDOFParams" + OpMemberName %type_View 100 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 101 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 102 "View_DepthOfFieldScale" + OpMemberName %type_View 103 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 104 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 105 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 106 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 107 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 108 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 109 "View_GeneralPurposeTweak" + OpMemberName %type_View 110 "View_DemosaicVposOffset" + OpMemberName %type_View 111 "PrePadding_View_2412" + OpMemberName %type_View 112 "View_IndirectLightingColorScale" + OpMemberName %type_View 113 "View_HDR32bppEncodingMode" + OpMemberName %type_View 114 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 115 "View_AtmosphericFogSunPower" + OpMemberName %type_View 116 "View_AtmosphericFogPower" + OpMemberName %type_View 117 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 118 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 119 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 120 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 121 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 122 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 123 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 124 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 125 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 126 "View_AtmosphericFogSunDiscHalfApexAngleRadian" + OpMemberName %type_View 127 "PrePadding_View_2492" + OpMemberName %type_View 128 "View_AtmosphericFogSunDiscLuminance" + OpMemberName %type_View 129 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 130 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 131 "PrePadding_View_2520" + OpMemberName %type_View 132 "PrePadding_View_2524" + OpMemberName %type_View 133 "View_AtmosphericFogSunColor" + OpMemberName %type_View 134 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 135 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 136 "View_AmbientCubemapTint" + OpMemberName %type_View 137 "View_AmbientCubemapIntensity" + OpMemberName %type_View 138 "View_SkyLightParameters" + OpMemberName %type_View 139 "PrePadding_View_2584" + OpMemberName %type_View 140 "PrePadding_View_2588" + OpMemberName %type_View 141 "View_SkyLightColor" + OpMemberName %type_View 142 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 143 "View_MobilePreviewMode" + OpMemberName %type_View 144 "View_HMDEyePaddingOffset" + OpMemberName %type_View 145 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 146 "View_ShowDecalsMask" + OpMemberName %type_View 147 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 148 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 149 "PrePadding_View_2744" + OpMemberName %type_View 150 "PrePadding_View_2748" + OpMemberName %type_View 151 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 152 "View_StereoPassIndex" + OpMemberName %type_View 153 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 154 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 155 "View_GlobalVolumeDimension" + OpMemberName %type_View 156 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 157 "View_MaxGlobalDistance" + OpMemberName %type_View 158 "PrePadding_View_2908" + OpMemberName %type_View 159 "View_CursorPosition" + OpMemberName %type_View 160 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 161 "PrePadding_View_2924" + OpMemberName %type_View 162 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 163 "PrePadding_View_2940" + OpMemberName %type_View 164 "View_VolumetricFogGridZParams" + OpMemberName %type_View 165 "PrePadding_View_2956" + OpMemberName %type_View 166 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 167 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 168 "PrePadding_View_2972" + OpMemberName %type_View 169 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 170 "PrePadding_View_2988" + OpMemberName %type_View 171 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 172 "PrePadding_View_3004" + OpMemberName %type_View 173 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 174 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 175 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 176 "View_StereoIPD" + OpMemberName %type_View 177 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 178 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_PrimitiveDither "type.PrimitiveDither" + OpMemberName %type_PrimitiveDither 0 "PrimitiveDither_LODFactor" + OpName %PrimitiveDither "PrimitiveDither" + OpName %type_PrimitiveFade "type.PrimitiveFade" + OpMemberName %type_PrimitiveFade 0 "PrimitiveFade_FadeTimeScaleBias" + OpName %PrimitiveFade "PrimitiveFade" + OpName %type_Material "type.Material" + OpMemberName %type_Material 0 "Material_VectorExpressions" + OpMemberName %type_Material 1 "Material_ScalarExpressions" + OpName %Material "Material" + OpName %type_2d_image "type.2d.image" + OpName %Material_Texture2D_0 "Material_Texture2D_0" + OpName %type_sampler "type.sampler" + OpName %Material_Texture2D_0Sampler "Material_Texture2D_0Sampler" + OpName %Material_Texture2D_3 "Material_Texture2D_3" + OpName %Material_Texture2D_3Sampler "Material_Texture2D_3Sampler" + OpName %in_var_TEXCOORD6 "in.var.TEXCOORD6" + OpName %in_var_TEXCOORD7 "in.var.TEXCOORD7" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_PRIMITIVE_ID "in.var.PRIMITIVE_ID" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPixelShader "MainPixelShader" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorateString %in_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorateString %in_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %in_var_PRIMITIVE_ID UserSemantic "PRIMITIVE_ID" + OpDecorate %in_var_PRIMITIVE_ID Flat + OpDecorate %gl_FrontFacing BuiltIn FrontFacing + OpDecorateString %gl_FrontFacing UserSemantic "SV_IsFrontFace" + OpDecorate %gl_FrontFacing Flat + OpDecorate %gl_FragDepth BuiltIn FragDepth + OpDecorateString %gl_FragDepth UserSemantic "SV_DepthLessEqual" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD6 Location 0 + OpDecorate %in_var_TEXCOORD7 Location 1 + OpDecorate %in_var_TEXCOORD10_centroid Location 2 + OpDecorate %in_var_TEXCOORD11_centroid Location 3 + OpDecorate %in_var_TEXCOORD0 Location 4 + OpDecorate %in_var_PRIMITIVE_ID Location 5 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %PrimitiveDither DescriptorSet 0 + OpDecorate %PrimitiveDither Binding 1 + OpDecorate %PrimitiveFade DescriptorSet 0 + OpDecorate %PrimitiveFade Binding 2 + OpDecorate %Material DescriptorSet 0 + OpDecorate %Material Binding 3 + OpDecorate %Material_Texture2D_0 DescriptorSet 0 + OpDecorate %Material_Texture2D_0 Binding 0 + OpDecorate %Material_Texture2D_0Sampler DescriptorSet 0 + OpDecorate %Material_Texture2D_0Sampler Binding 0 + OpDecorate %Material_Texture2D_3 DescriptorSet 0 + OpDecorate %Material_Texture2D_3 Binding 1 + OpDecorate %Material_Texture2D_3Sampler DescriptorSet 0 + OpDecorate %Material_Texture2D_3Sampler Binding 1 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 13 MatrixStride 16 + OpMemberDecorate %type_View 13 ColMajor + OpMemberDecorate %type_View 14 Offset 896 + OpMemberDecorate %type_View 15 Offset 908 + OpMemberDecorate %type_View 16 Offset 912 + OpMemberDecorate %type_View 17 Offset 924 + OpMemberDecorate %type_View 18 Offset 928 + OpMemberDecorate %type_View 19 Offset 940 + OpMemberDecorate %type_View 20 Offset 944 + OpMemberDecorate %type_View 21 Offset 956 + OpMemberDecorate %type_View 22 Offset 960 + OpMemberDecorate %type_View 23 Offset 972 + OpMemberDecorate %type_View 24 Offset 976 + OpMemberDecorate %type_View 25 Offset 992 + OpMemberDecorate %type_View 26 Offset 1008 + OpMemberDecorate %type_View 27 Offset 1020 + OpMemberDecorate %type_View 28 Offset 1024 + OpMemberDecorate %type_View 29 Offset 1036 + OpMemberDecorate %type_View 30 Offset 1040 + OpMemberDecorate %type_View 31 Offset 1052 + OpMemberDecorate %type_View 32 Offset 1056 + OpMemberDecorate %type_View 33 Offset 1068 + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 43 MatrixStride 16 + OpMemberDecorate %type_View 43 ColMajor + OpMemberDecorate %type_View 44 Offset 1712 + OpMemberDecorate %type_View 45 Offset 1724 + OpMemberDecorate %type_View 46 Offset 1728 + OpMemberDecorate %type_View 47 Offset 1740 + OpMemberDecorate %type_View 48 Offset 1744 + OpMemberDecorate %type_View 49 Offset 1756 + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 52 MatrixStride 16 + OpMemberDecorate %type_View 52 ColMajor + OpMemberDecorate %type_View 53 Offset 1952 + OpMemberDecorate %type_View 54 Offset 1968 + OpMemberDecorate %type_View 55 Offset 1984 + OpMemberDecorate %type_View 56 Offset 1992 + OpMemberDecorate %type_View 57 Offset 2000 + OpMemberDecorate %type_View 58 Offset 2016 + OpMemberDecorate %type_View 59 Offset 2032 + OpMemberDecorate %type_View 60 Offset 2048 + OpMemberDecorate %type_View 61 Offset 2064 + OpMemberDecorate %type_View 62 Offset 2068 + OpMemberDecorate %type_View 63 Offset 2072 + OpMemberDecorate %type_View 64 Offset 2076 + OpMemberDecorate %type_View 65 Offset 2080 + OpMemberDecorate %type_View 66 Offset 2096 + OpMemberDecorate %type_View 67 Offset 2112 + OpMemberDecorate %type_View 68 Offset 2128 + OpMemberDecorate %type_View 69 Offset 2136 + OpMemberDecorate %type_View 70 Offset 2140 + OpMemberDecorate %type_View 71 Offset 2144 + OpMemberDecorate %type_View 72 Offset 2148 + OpMemberDecorate %type_View 73 Offset 2152 + OpMemberDecorate %type_View 74 Offset 2156 + OpMemberDecorate %type_View 75 Offset 2160 + OpMemberDecorate %type_View 76 Offset 2172 + OpMemberDecorate %type_View 77 Offset 2176 + OpMemberDecorate %type_View 78 Offset 2180 + OpMemberDecorate %type_View 79 Offset 2184 + OpMemberDecorate %type_View 80 Offset 2188 + OpMemberDecorate %type_View 81 Offset 2192 + OpMemberDecorate %type_View 82 Offset 2196 + OpMemberDecorate %type_View 83 Offset 2200 + OpMemberDecorate %type_View 84 Offset 2204 + OpMemberDecorate %type_View 85 Offset 2208 + OpMemberDecorate %type_View 86 Offset 2212 + OpMemberDecorate %type_View 87 Offset 2216 + OpMemberDecorate %type_View 88 Offset 2220 + OpMemberDecorate %type_View 89 Offset 2224 + OpMemberDecorate %type_View 90 Offset 2228 + OpMemberDecorate %type_View 91 Offset 2232 + OpMemberDecorate %type_View 92 Offset 2236 + OpMemberDecorate %type_View 93 Offset 2240 + OpMemberDecorate %type_View 94 Offset 2256 + OpMemberDecorate %type_View 95 Offset 2268 + OpMemberDecorate %type_View 96 Offset 2272 + OpMemberDecorate %type_View 97 Offset 2304 + OpMemberDecorate %type_View 98 Offset 2336 + OpMemberDecorate %type_View 99 Offset 2352 + OpMemberDecorate %type_View 100 Offset 2368 + OpMemberDecorate %type_View 101 Offset 2372 + OpMemberDecorate %type_View 102 Offset 2376 + OpMemberDecorate %type_View 103 Offset 2380 + OpMemberDecorate %type_View 104 Offset 2384 + OpMemberDecorate %type_View 105 Offset 2388 + OpMemberDecorate %type_View 106 Offset 2392 + OpMemberDecorate %type_View 107 Offset 2396 + OpMemberDecorate %type_View 108 Offset 2400 + OpMemberDecorate %type_View 109 Offset 2404 + OpMemberDecorate %type_View 110 Offset 2408 + OpMemberDecorate %type_View 111 Offset 2412 + OpMemberDecorate %type_View 112 Offset 2416 + OpMemberDecorate %type_View 113 Offset 2428 + OpMemberDecorate %type_View 114 Offset 2432 + OpMemberDecorate %type_View 115 Offset 2444 + OpMemberDecorate %type_View 116 Offset 2448 + OpMemberDecorate %type_View 117 Offset 2452 + OpMemberDecorate %type_View 118 Offset 2456 + OpMemberDecorate %type_View 119 Offset 2460 + OpMemberDecorate %type_View 120 Offset 2464 + OpMemberDecorate %type_View 121 Offset 2468 + OpMemberDecorate %type_View 122 Offset 2472 + OpMemberDecorate %type_View 123 Offset 2476 + OpMemberDecorate %type_View 124 Offset 2480 + OpMemberDecorate %type_View 125 Offset 2484 + OpMemberDecorate %type_View 126 Offset 2488 + OpMemberDecorate %type_View 127 Offset 2492 + OpMemberDecorate %type_View 128 Offset 2496 + OpMemberDecorate %type_View 129 Offset 2512 + OpMemberDecorate %type_View 130 Offset 2516 + OpMemberDecorate %type_View 131 Offset 2520 + OpMemberDecorate %type_View 132 Offset 2524 + OpMemberDecorate %type_View 133 Offset 2528 + OpMemberDecorate %type_View 134 Offset 2544 + OpMemberDecorate %type_View 135 Offset 2556 + OpMemberDecorate %type_View 136 Offset 2560 + OpMemberDecorate %type_View 137 Offset 2576 + OpMemberDecorate %type_View 138 Offset 2580 + OpMemberDecorate %type_View 139 Offset 2584 + OpMemberDecorate %type_View 140 Offset 2588 + OpMemberDecorate %type_View 141 Offset 2592 + OpMemberDecorate %type_View 142 Offset 2608 + OpMemberDecorate %type_View 143 Offset 2720 + OpMemberDecorate %type_View 144 Offset 2724 + OpMemberDecorate %type_View 145 Offset 2728 + OpMemberDecorate %type_View 146 Offset 2732 + OpMemberDecorate %type_View 147 Offset 2736 + OpMemberDecorate %type_View 148 Offset 2740 + OpMemberDecorate %type_View 149 Offset 2744 + OpMemberDecorate %type_View 150 Offset 2748 + OpMemberDecorate %type_View 151 Offset 2752 + OpMemberDecorate %type_View 152 Offset 2764 + OpMemberDecorate %type_View 153 Offset 2768 + OpMemberDecorate %type_View 154 Offset 2832 + OpMemberDecorate %type_View 155 Offset 2896 + OpMemberDecorate %type_View 156 Offset 2900 + OpMemberDecorate %type_View 157 Offset 2904 + OpMemberDecorate %type_View 158 Offset 2908 + OpMemberDecorate %type_View 159 Offset 2912 + OpMemberDecorate %type_View 160 Offset 2920 + OpMemberDecorate %type_View 161 Offset 2924 + OpMemberDecorate %type_View 162 Offset 2928 + OpMemberDecorate %type_View 163 Offset 2940 + OpMemberDecorate %type_View 164 Offset 2944 + OpMemberDecorate %type_View 165 Offset 2956 + OpMemberDecorate %type_View 166 Offset 2960 + OpMemberDecorate %type_View 167 Offset 2968 + OpMemberDecorate %type_View 168 Offset 2972 + OpMemberDecorate %type_View 169 Offset 2976 + OpMemberDecorate %type_View 170 Offset 2988 + OpMemberDecorate %type_View 171 Offset 2992 + OpMemberDecorate %type_View 172 Offset 3004 + OpMemberDecorate %type_View 173 Offset 3008 + OpMemberDecorate %type_View 174 Offset 3020 + OpMemberDecorate %type_View 175 Offset 3024 + OpMemberDecorate %type_View 176 Offset 3036 + OpMemberDecorate %type_View 177 Offset 3040 + OpMemberDecorate %type_View 178 Offset 3044 + OpDecorate %type_View Block + OpMemberDecorate %type_PrimitiveDither 0 Offset 0 + OpDecorate %type_PrimitiveDither Block + OpMemberDecorate %type_PrimitiveFade 0 Offset 0 + OpDecorate %type_PrimitiveFade Block + OpDecorate %_arr_v4float_uint_9 ArrayStride 16 + OpDecorate %_arr_v4float_uint_3 ArrayStride 16 + OpMemberDecorate %type_Material 0 Offset 0 + OpMemberDecorate %type_Material 1 Offset 144 + OpDecorate %type_Material Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %v2int = OpTypeVector %int 2 +%float_0_00100000005 = OpConstant %float 0.00100000005 + %int_2 = OpConstant %int 2 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float_0 = OpConstant %float 0 + %49 = OpConstantComposite %v2float %float_0 %float_0 + %float_1 = OpConstant %float 1 + %int_4 = OpConstant %int 4 + %int_11 = OpConstant %int 11 +%float_0_249500006 = OpConstant %float 0.249500006 + %54 = OpConstantComposite %v2float %float_0_249500006 %float_0_249500006 +%float_0_499992371 = OpConstant %float 0.499992371 + %56 = OpConstantComposite %v2float %float_0_499992371 %float_0_499992371 + %int_32 = OpConstant %int 32 + %int_53 = OpConstant %int 53 + %int_57 = OpConstant %int 57 + %int_80 = OpConstant %int 80 + %int_82 = OpConstant %int 82 + %int_98 = OpConstant %int 98 + %uint_1 = OpConstant %uint 1 +%mat3v3float = OpTypeMatrix %v3float 3 + %float_2 = OpConstant %float 2 + %float_n1 = OpConstant %float -1 + %67 = OpConstantComposite %v2float %float_n1 %float_n1 + %bool = OpTypeBool + %float_n0_5 = OpConstant %float -0.5 + %70 = OpConstantComposite %v3float %float_0 %float_0 %float_1 +%float_0_333299994 = OpConstant %float 0.333299994 + %uint_5 = OpConstant %uint 5 +%float_347_834503 = OpConstant %float 347.834503 +%float_3343_28369 = OpConstant %float 3343.28369 + %75 = OpConstantComposite %v2float %float_347_834503 %float_3343_28369 + %float_1000 = OpConstant %float 1000 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %uint %uint %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v2int %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_PrimitiveDither = OpTypeStruct %float +%_ptr_Uniform_type_PrimitiveDither = OpTypePointer Uniform %type_PrimitiveDither +%type_PrimitiveFade = OpTypeStruct %v2float +%_ptr_Uniform_type_PrimitiveFade = OpTypePointer Uniform %type_PrimitiveFade + %uint_9 = OpConstant %uint 9 +%_arr_v4float_uint_9 = OpTypeArray %v4float %uint_9 + %uint_3 = OpConstant %uint 3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%type_Material = OpTypeStruct %_arr_v4float_uint_9 %_arr_v4float_uint_3 +%_ptr_Uniform_type_Material = OpTypePointer Uniform %type_Material +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%_ptr_Input__arr_v4float_uint_1 = OpTypePointer Input %_arr_v4float_uint_1 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Input_bool = OpTypePointer Input %bool +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %93 = OpTypeFunction %void +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%type_sampled_image = OpTypeSampledImage %type_2d_image + %View = OpVariable %_ptr_Uniform_type_View Uniform +%PrimitiveDither = OpVariable %_ptr_Uniform_type_PrimitiveDither Uniform +%PrimitiveFade = OpVariable %_ptr_Uniform_type_PrimitiveFade Uniform + %Material = OpVariable %_ptr_Uniform_type_Material Uniform +%Material_Texture2D_0 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%Material_Texture2D_0Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%Material_Texture2D_3 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%Material_Texture2D_3Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD6 = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD7 = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input__arr_v4float_uint_1 Input +%in_var_PRIMITIVE_ID = OpVariable %_ptr_Input_uint Input +%gl_FrontFacing = OpVariable %_ptr_Input_bool Input +%gl_FragDepth = OpVariable %_ptr_Output_float Output +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %98 = OpUndef %float + %99 = OpConstantNull %v2float +%float_0_015625 = OpConstant %float 0.015625 + %101 = OpConstantComposite %v2float %float_0_015625 %float_0_015625 +%float_0_166666672 = OpConstant %float 0.166666672 + %103 = OpUndef %float + %104 = OpConstantNull %v3float +%MainPixelShader = OpFunction %void None %93 + %105 = OpLabel + %106 = OpLoad %v4float %gl_FragCoord + %107 = OpLoad %v4float %in_var_TEXCOORD6 + %108 = OpLoad %v4float %in_var_TEXCOORD7 + %109 = OpLoad %v4float %in_var_TEXCOORD10_centroid + %110 = OpLoad %v4float %in_var_TEXCOORD11_centroid + %111 = OpLoad %_arr_v4float_uint_1 %in_var_TEXCOORD0 + %112 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_4 + %113 = OpLoad %mat4v4float %112 + %114 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_11 + %115 = OpLoad %mat4v4float %114 + %116 = OpAccessChain %_ptr_Uniform_v3float %View %int_32 + %117 = OpLoad %v3float %116 + %118 = OpAccessChain %_ptr_Uniform_v4float %View %int_53 + %119 = OpLoad %v4float %118 + %120 = OpAccessChain %_ptr_Uniform_v4float %View %int_57 + %121 = OpLoad %v4float %120 + %122 = OpAccessChain %_ptr_Uniform_float %View %int_80 + %123 = OpLoad %float %122 + %124 = OpCompositeExtract %v4float %111 0 + %125 = OpVectorShuffle %v2float %99 %124 2 3 + %126 = OpVectorShuffle %v3float %109 %109 0 1 2 + %127 = OpVectorShuffle %v3float %110 %110 0 1 2 + %128 = OpExtInst %v3float %1 Cross %127 %126 + %129 = OpCompositeExtract %float %110 3 + %130 = OpCompositeConstruct %v3float %129 %129 %129 + %131 = OpFMul %v3float %128 %130 + %132 = OpCompositeConstruct %mat3v3float %126 %131 %127 + %133 = OpVectorShuffle %v2float %106 %106 0 1 + %134 = OpVectorShuffle %v2float %121 %121 0 1 + %135 = OpFSub %v2float %133 %134 + %136 = OpCompositeExtract %float %106 2 + %137 = OpCompositeConstruct %v4float %103 %103 %136 %float_1 + %138 = OpCompositeExtract %float %106 3 + %139 = OpCompositeConstruct %v4float %138 %138 %138 %138 + %140 = OpFMul %v4float %137 %139 + %141 = OpCompositeExtract %float %106 0 + %142 = OpCompositeExtract %float %106 1 + %143 = OpCompositeConstruct %v4float %141 %142 %136 %float_1 + %144 = OpMatrixTimesVector %v4float %115 %143 + %145 = OpVectorShuffle %v3float %144 %144 0 1 2 + %146 = OpCompositeExtract %float %144 3 + %147 = OpCompositeConstruct %v3float %146 %146 %146 + %148 = OpFDiv %v3float %145 %147 + %149 = OpFSub %v3float %148 %117 + %150 = OpFNegate %v3float %148 + %151 = OpExtInst %v3float %1 Normalize %150 + %152 = OpVectorTimesMatrix %v3float %151 %132 + %153 = OpVectorShuffle %v2float %152 %152 0 1 + %154 = OpFMul %v2float %153 %67 + %155 = OpCompositeExtract %float %152 2 + %156 = OpCompositeConstruct %v2float %155 %155 + %157 = OpFDiv %v2float %154 %156 + %158 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_0 + %159 = OpLoad %float %158 + %160 = OpCompositeConstruct %v2float %159 %159 + %161 = OpFMul %v2float %160 %157 + %162 = OpDot %float %151 %127 + %163 = OpExtInst %float %1 FAbs %162 + %164 = OpExtInst %float %1 FMax %163 %float_0 + %165 = OpExtInst %float %1 FMin %164 %float_1 + %166 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_1 + %167 = OpLoad %float %166 + %168 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_2 + %169 = OpLoad %float %168 + %170 = OpExtInst %float %1 FMix %167 %169 %165 + %171 = OpExtInst %float %1 Floor %170 + %172 = OpFDiv %float %float_1 %170 + %173 = OpCompositeConstruct %v2float %172 %172 + %174 = OpFMul %v2float %161 %173 + %175 = OpDPdx %v2float %125 + %176 = OpDPdy %v2float %125 + %177 = OpLoad %type_2d_image %Material_Texture2D_0 + %178 = OpLoad %type_sampler %Material_Texture2D_0Sampler + OpBranch %179 + %179 = OpLabel + %180 = OpPhi %float %float_1 %105 %181 %182 + %183 = OpPhi %v2float %49 %105 %184 %182 + %185 = OpPhi %int %int_0 %105 %186 %182 + %187 = OpPhi %float %float_1 %105 %188 %182 + %189 = OpPhi %float %float_1 %105 %180 %182 + %190 = OpConvertSToF %float %185 + %191 = OpFAdd %float %171 %float_2 + %192 = OpFOrdLessThan %bool %190 %191 + OpLoopMerge %193 %182 None + OpBranchConditional %192 %194 %193 + %194 = OpLabel + %195 = OpFAdd %v2float %125 %183 + %196 = OpSampledImage %type_sampled_image %177 %178 + %197 = OpImageSampleExplicitLod %v4float %196 %195 Grad %175 %176 + %188 = OpCompositeExtract %float %197 1 + %198 = OpFOrdLessThan %bool %180 %188 + OpSelectionMerge %182 None + OpBranchConditional %198 %199 %182 + %199 = OpLabel + %200 = OpFSub %float %189 %187 + %201 = OpFSub %float %188 %180 + %202 = OpFAdd %float %200 %201 + %203 = OpFDiv %float %201 %202 + %204 = OpFMul %float %189 %203 + %205 = OpFSub %float %float_1 %203 + %206 = OpFMul %float %180 %205 + %207 = OpFAdd %float %204 %206 + %208 = OpCompositeConstruct %v2float %203 %203 + %209 = OpFMul %v2float %208 %174 + %210 = OpFSub %v2float %183 %209 + OpBranch %193 + %182 = OpLabel + %181 = OpFSub %float %180 %172 + %184 = OpFAdd %v2float %183 %174 + %186 = OpIAdd %int %185 %int_1 + OpBranch %179 + %193 = OpLabel + %211 = OpPhi %float %98 %179 %207 %199 + %212 = OpPhi %v2float %183 %179 %210 %199 + %213 = OpVectorShuffle %v2float %212 %104 0 1 + %214 = OpFAdd %v2float %125 %213 + %215 = OpAccessChain %_ptr_Uniform_float %View %int_82 + %216 = OpLoad %float %215 + %217 = OpSampledImage %type_sampled_image %177 %178 + %218 = OpImageSampleImplicitLod %v4float %217 %214 Bias %216 + %219 = OpCompositeExtract %float %218 0 + %220 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_2 %int_1 + %221 = OpLoad %float %220 + %222 = OpFMul %float %219 %221 + %223 = OpFSub %float %float_1 %222 + %224 = OpExtInst %float %1 FMax %223 %float_0 + %225 = OpExtInst %float %1 FMin %224 %float_1 + %226 = OpAccessChain %_ptr_Uniform_float %View %int_98 %int_0 + %227 = OpLoad %float %226 + %228 = OpCompositeConstruct %v2float %227 %227 + %229 = OpFAdd %v2float %135 %228 + %230 = OpCompositeExtract %float %229 0 + %231 = OpConvertFToU %uint %230 + %232 = OpCompositeExtract %float %229 1 + %233 = OpConvertFToU %uint %232 + %234 = OpIMul %uint %uint_2 %233 + %235 = OpIAdd %uint %231 %234 + %236 = OpUMod %uint %235 %uint_5 + %237 = OpConvertUToF %float %236 + %238 = OpFMul %v2float %135 %101 + %239 = OpLoad %type_2d_image %Material_Texture2D_3 + %240 = OpLoad %type_sampler %Material_Texture2D_3Sampler + %241 = OpSampledImage %type_sampled_image %239 %240 + %242 = OpImageSampleImplicitLod %v4float %241 %238 Bias %216 + %243 = OpCompositeExtract %float %242 0 + %244 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_2 %int_2 + %245 = OpLoad %float %244 + %246 = OpFMul %float %243 %245 + %247 = OpFAdd %float %237 %246 + %248 = OpFMul %float %247 %float_0_166666672 + %249 = OpFAdd %float %225 %248 + %250 = OpFAdd %float %249 %float_n0_5 + %251 = OpCompositeExtract %float %218 2 + %252 = OpFAdd %float %251 %250 + %253 = OpSampledImage %type_sampled_image %239 %240 + %254 = OpImageSampleImplicitLod %v4float %253 %238 Bias %216 + %255 = OpCompositeExtract %float %254 0 + %256 = OpFAdd %float %237 %255 + %257 = OpFMul %float %256 %float_0_166666672 + %258 = OpAccessChain %_ptr_Uniform_float %PrimitiveFade %int_0 %int_0 + %259 = OpLoad %float %258 + %260 = OpFMul %float %123 %259 + %261 = OpAccessChain %_ptr_Uniform_float %PrimitiveFade %int_0 %int_1 + %262 = OpLoad %float %261 + %263 = OpFAdd %float %260 %262 + %264 = OpExtInst %float %1 FClamp %263 %float_0 %float_1 + %265 = OpFAdd %float %264 %257 + %266 = OpFAdd %float %265 %float_n0_5 + %267 = OpFMul %float %252 %266 + %268 = OpFSub %float %float_1 %211 + %269 = OpFMul %float %268 %159 + %270 = OpCompositeExtract %float %212 0 + %271 = OpCompositeExtract %float %212 1 + %272 = OpCompositeConstruct %v3float %270 %271 %269 + %273 = OpDot %float %272 %272 + %274 = OpExtInst %float %1 Sqrt %273 + %275 = OpDPdx %v2float %125 + %276 = OpExtInst %v2float %1 FAbs %275 + %277 = OpDot %float %276 %276 + %278 = OpExtInst %float %1 Sqrt %277 + %279 = OpDPdx %v3float %149 + %280 = OpDot %float %279 %279 + %281 = OpExtInst %float %1 Sqrt %280 + %282 = OpFDiv %float %278 %281 + %283 = OpDPdy %v2float %125 + %284 = OpExtInst %v2float %1 FAbs %283 + %285 = OpDot %float %284 %284 + %286 = OpExtInst %float %1 Sqrt %285 + %287 = OpDPdy %v3float %149 + %288 = OpDot %float %287 %287 + %289 = OpExtInst %float %1 Sqrt %288 + %290 = OpFDiv %float %286 %289 + %291 = OpExtInst %float %1 FMax %282 %290 + %292 = OpCompositeExtract %v4float %113 0 + %293 = OpVectorShuffle %v3float %292 %292 0 1 2 + %294 = OpCompositeExtract %v4float %113 1 + %295 = OpVectorShuffle %v3float %294 %294 0 1 2 + %296 = OpCompositeExtract %v4float %113 2 + %297 = OpVectorShuffle %v3float %296 %296 0 1 2 + %298 = OpCompositeConstruct %mat3v3float %293 %295 %297 + %299 = OpMatrixTimesVector %v3float %298 %70 + %300 = OpDot %float %299 %151 + %301 = OpExtInst %float %1 FAbs %300 + %302 = OpFDiv %float %291 %301 + %303 = OpFDiv %float %274 %302 + %304 = OpAccessChain %_ptr_Uniform_float %PrimitiveDither %int_0 + %305 = OpLoad %float %304 + %306 = OpFOrdNotEqual %bool %305 %float_0 + OpSelectionMerge %307 None + OpBranchConditional %306 %308 %307 + %308 = OpLabel + %309 = OpExtInst %float %1 FAbs %305 + %310 = OpFOrdGreaterThan %bool %309 %float_0_00100000005 + OpSelectionMerge %311 None + OpBranchConditional %310 %312 %311 + %312 = OpLabel + %313 = OpExtInst %v2float %1 Floor %133 + %314 = OpDot %float %313 %75 + %315 = OpExtInst %float %1 Cos %314 + %316 = OpFMul %float %315 %float_1000 + %317 = OpExtInst %float %1 Fract %316 + %318 = OpFOrdLessThan %bool %305 %float_0 + %319 = OpFAdd %float %305 %float_1 + %320 = OpFOrdGreaterThan %bool %319 %317 + %321 = OpFOrdLessThan %bool %305 %317 + %322 = OpSelect %bool %318 %320 %321 + %323 = OpSelect %float %322 %float_1 %float_0 + %324 = OpFSub %float %323 %float_0_00100000005 + %325 = OpFOrdLessThan %bool %324 %float_0 + OpSelectionMerge %326 None + OpBranchConditional %325 %327 %326 + %327 = OpLabel + OpKill + %326 = OpLabel + OpBranch %311 + %311 = OpLabel + OpBranch %307 + %307 = OpLabel + %328 = OpFSub %float %267 %float_0_333299994 + %329 = OpFOrdLessThan %bool %328 %float_0 + OpSelectionMerge %330 None + OpBranchConditional %329 %331 %330 + %331 = OpLabel + OpKill + %330 = OpLabel + %332 = OpCompositeExtract %float %140 2 + %333 = OpCompositeExtract %float %140 3 + %334 = OpFAdd %float %333 %303 + %335 = OpFDiv %float %332 %334 + %336 = OpExtInst %float %1 FMin %335 %136 + %337 = OpVectorShuffle %v2float %107 %107 0 1 + %338 = OpCompositeExtract %float %107 3 + %339 = OpCompositeConstruct %v2float %338 %338 + %340 = OpFDiv %v2float %337 %339 + %341 = OpVectorShuffle %v2float %119 %119 0 1 + %342 = OpFSub %v2float %340 %341 + %343 = OpVectorShuffle %v2float %108 %108 0 1 + %344 = OpCompositeExtract %float %108 3 + %345 = OpCompositeConstruct %v2float %344 %344 + %346 = OpFDiv %v2float %343 %345 + %347 = OpVectorShuffle %v2float %119 %119 2 3 + %348 = OpFSub %v2float %346 %347 + %349 = OpFSub %v2float %342 %348 + %350 = OpFMul %v2float %349 %54 + %351 = OpFAdd %v2float %350 %56 + %352 = OpVectorShuffle %v4float %351 %49 0 1 2 3 + OpStore %gl_FragDepth %336 + OpStore %out_var_SV_Target0 %352 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4-no-opt/asm/vert/loop-accesschain-writethrough.asm.invalid.vert b/third_party/spirv-cross/shaders-ue4-no-opt/asm/vert/loop-accesschain-writethrough.asm.invalid.vert new file mode 100644 index 0000000..693f16c --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4-no-opt/asm/vert/loop-accesschain-writethrough.asm.invalid.vert @@ -0,0 +1,259 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 181 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %ScatterMainVS "main" %gl_VertexIndex %gl_InstanceIndex %out_var_TEXCOORD0 %out_var_TEXCOORD1 %out_var_TEXCOORD2 %out_var_TEXCOORD3 %out_var_TEXCOORD4 %out_var_TEXCOORD5 %out_var_TEXCOORD6 %gl_Position + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "ViewportSize" + OpMemberName %type__Globals 1 "ScatteringScaling" + OpMemberName %type__Globals 2 "CocRadiusToCircumscribedRadius" + OpName %_Globals "$Globals" + OpName %type_StructuredBuffer_v4float "type.StructuredBuffer.v4float" + OpName %ScatterDrawList "ScatterDrawList" + OpName %out_var_TEXCOORD0 "out.var.TEXCOORD0" + OpName %out_var_TEXCOORD1 "out.var.TEXCOORD1" + OpName %out_var_TEXCOORD2 "out.var.TEXCOORD2" + OpName %out_var_TEXCOORD3 "out.var.TEXCOORD3" + OpName %out_var_TEXCOORD4 "out.var.TEXCOORD4" + OpName %out_var_TEXCOORD5 "out.var.TEXCOORD5" + OpName %out_var_TEXCOORD6 "out.var.TEXCOORD6" + OpName %ScatterMainVS "ScatterMainVS" + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpDecorateString %gl_VertexIndex UserSemantic "SV_VertexID" + OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex + OpDecorateString %gl_InstanceIndex UserSemantic "SV_InstanceID" + OpDecorateString %out_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %out_var_TEXCOORD1 UserSemantic "TEXCOORD1" + OpDecorateString %out_var_TEXCOORD2 UserSemantic "TEXCOORD2" + OpDecorateString %out_var_TEXCOORD3 UserSemantic "TEXCOORD3" + OpDecorateString %out_var_TEXCOORD4 UserSemantic "TEXCOORD4" + OpDecorateString %out_var_TEXCOORD5 UserSemantic "TEXCOORD5" + OpDecorateString %out_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorate %gl_Position BuiltIn Position + OpDecorateString %gl_Position UserSemantic "SV_POSITION" + OpDecorate %out_var_TEXCOORD0 Location 0 + OpDecorate %out_var_TEXCOORD1 Location 1 + OpDecorate %out_var_TEXCOORD2 Location 2 + OpDecorate %out_var_TEXCOORD3 Location 3 + OpDecorate %out_var_TEXCOORD4 Location 4 + OpDecorate %out_var_TEXCOORD5 Location 5 + OpDecorate %out_var_TEXCOORD6 Location 6 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 1 + OpDecorate %ScatterDrawList DescriptorSet 0 + OpDecorate %ScatterDrawList Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 20 + OpDecorate %type__Globals Block + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_v4float 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_v4float 0 NonWritable + OpDecorate %type_StructuredBuffer_v4float BufferBlock + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %float_0_5 = OpConstant %float 0.5 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float_1 = OpConstant %float 1 + %uint_16 = OpConstant %uint 16 + %float_0 = OpConstant %float 0 + %uint_0 = OpConstant %uint 0 + %uint_5 = OpConstant %uint 5 + %uint_1 = OpConstant %uint 1 + %int_3 = OpConstant %int 3 + %float_n0_5 = OpConstant %float -0.5 + %int_2 = OpConstant %int 2 + %float_2 = OpConstant %float 2 + %39 = OpConstantComposite %v2float %float_2 %float_2 + %40 = OpConstantComposite %v2float %float_1 %float_1 + %41 = OpConstantComposite %v2float %float_0_5 %float_0_5 +%type__Globals = OpTypeStruct %v4float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%_runtimearr_v4float = OpTypeRuntimeArray %v4float +%type_StructuredBuffer_v4float = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_type_StructuredBuffer_v4float = OpTypePointer Uniform %type_StructuredBuffer_v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v2float = OpTypePointer Output %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %48 = OpTypeFunction %void +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Function__arr_float_uint_4 = OpTypePointer Function %_arr_float_uint_4 +%_arr_v2float_uint_4 = OpTypeArray %v2float %uint_4 +%_ptr_Function__arr_v2float_uint_4 = OpTypePointer Function %_arr_v2float_uint_4 +%_ptr_Function_float = OpTypePointer Function %float + %bool = OpTypeBool +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%ScatterDrawList = OpVariable %_ptr_Uniform_type_StructuredBuffer_v4float Uniform +%gl_VertexIndex = OpVariable %_ptr_Input_uint Input +%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input +%out_var_TEXCOORD0 = OpVariable %_ptr_Output_v2float Output +%out_var_TEXCOORD1 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD2 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD3 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD4 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD5 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD6 = OpVariable %_ptr_Output_v4float Output +%gl_Position = OpVariable %_ptr_Output_v4float Output +%ScatterMainVS = OpFunction %void None %48 + %60 = OpLabel + %61 = OpVariable %_ptr_Function__arr_v4float_uint_4 Function + %62 = OpVariable %_ptr_Function__arr_float_uint_4 Function + %63 = OpVariable %_ptr_Function__arr_v2float_uint_4 Function + %64 = OpLoad %uint %gl_VertexIndex + %65 = OpLoad %uint %gl_InstanceIndex + %66 = OpUDiv %uint %64 %uint_4 + %67 = OpIMul %uint %66 %uint_4 + %68 = OpISub %uint %64 %67 + %69 = OpIMul %uint %uint_16 %65 + %70 = OpIAdd %uint %69 %66 + OpBranch %71 + %71 = OpLabel + %72 = OpPhi %float %float_0 %60 %73 %74 + %75 = OpPhi %uint %uint_0 %60 %76 %74 + %77 = OpULessThan %bool %75 %uint_4 + OpLoopMerge %78 %74 Unroll + OpBranchConditional %77 %79 %78 + %79 = OpLabel + %80 = OpIMul %uint %uint_5 %70 + %81 = OpIAdd %uint %80 %75 + %82 = OpIAdd %uint %81 %uint_1 + %83 = OpAccessChain %_ptr_Uniform_v4float %ScatterDrawList %int_0 %82 + %84 = OpLoad %v4float %83 + %85 = OpCompositeExtract %float %84 0 + %86 = OpCompositeExtract %float %84 1 + %87 = OpCompositeExtract %float %84 2 + %88 = OpCompositeConstruct %v4float %85 %86 %87 %float_0 + %89 = OpAccessChain %_ptr_Function_v4float %61 %75 + OpStore %89 %88 + %90 = OpCompositeExtract %float %84 3 + %91 = OpAccessChain %_ptr_Function_float %62 %75 + OpStore %91 %90 + %92 = OpIEqual %bool %75 %uint_0 + OpSelectionMerge %74 None + OpBranchConditional %92 %93 %94 + %93 = OpLabel + %95 = OpLoad %float %91 + OpBranch %74 + %94 = OpLabel + %96 = OpLoad %float %91 + %97 = OpExtInst %float %1 FMax %72 %96 + OpBranch %74 + %74 = OpLabel + %73 = OpPhi %float %95 %93 %97 %94 + %98 = OpLoad %float %91 + %99 = OpFDiv %float %float_n0_5 %98 + %100 = OpAccessChain %_ptr_Function_float %63 %75 %int_0 + OpStore %100 %99 + %101 = OpLoad %float %91 + %102 = OpFMul %float %float_0_5 %101 + %103 = OpFAdd %float %102 %float_0_5 + %104 = OpAccessChain %_ptr_Function_float %63 %75 %int_1 + OpStore %104 %103 + %76 = OpIAdd %uint %75 %uint_1 + OpBranch %71 + %78 = OpLabel + %105 = OpAccessChain %_ptr_Function_v4float %61 %int_0 + %106 = OpLoad %v4float %105 + %107 = OpCompositeExtract %float %106 0 + %108 = OpCompositeExtract %float %106 1 + %109 = OpCompositeExtract %float %106 2 + %110 = OpAccessChain %_ptr_Function_float %62 %int_0 + %111 = OpLoad %float %110 + %112 = OpCompositeConstruct %v4float %107 %108 %109 %111 + %113 = OpAccessChain %_ptr_Function_v4float %61 %int_1 + %114 = OpLoad %v4float %113 + %115 = OpCompositeExtract %float %114 0 + %116 = OpCompositeExtract %float %114 1 + %117 = OpCompositeExtract %float %114 2 + %118 = OpAccessChain %_ptr_Function_float %62 %int_1 + %119 = OpLoad %float %118 + %120 = OpCompositeConstruct %v4float %115 %116 %117 %119 + %121 = OpAccessChain %_ptr_Function_v4float %61 %int_2 + %122 = OpLoad %v4float %121 + %123 = OpCompositeExtract %float %122 0 + %124 = OpCompositeExtract %float %122 1 + %125 = OpCompositeExtract %float %122 2 + %126 = OpAccessChain %_ptr_Function_float %62 %int_2 + %127 = OpLoad %float %126 + %128 = OpCompositeConstruct %v4float %123 %124 %125 %127 + %129 = OpAccessChain %_ptr_Function_v4float %61 %int_3 + %130 = OpLoad %v4float %129 + %131 = OpCompositeExtract %float %130 0 + %132 = OpCompositeExtract %float %130 1 + %133 = OpCompositeExtract %float %130 2 + %134 = OpAccessChain %_ptr_Function_float %62 %int_3 + %135 = OpLoad %float %134 + %136 = OpCompositeConstruct %v4float %131 %132 %133 %135 + %137 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 + %138 = OpLoad %float %137 + %139 = OpCompositeConstruct %v2float %138 %138 + %140 = OpIMul %uint %uint_5 %70 + %141 = OpAccessChain %_ptr_Uniform_v4float %ScatterDrawList %int_0 %140 + %142 = OpLoad %v4float %141 + %143 = OpVectorShuffle %v2float %142 %142 0 1 + %144 = OpFMul %v2float %139 %143 + %145 = OpAccessChain %_ptr_Function_v2float %63 %int_0 + %146 = OpLoad %v2float %145 + %147 = OpAccessChain %_ptr_Function_v2float %63 %int_1 + %148 = OpLoad %v2float %147 + %149 = OpVectorShuffle %v4float %146 %148 0 1 2 3 + %150 = OpAccessChain %_ptr_Function_v2float %63 %int_2 + %151 = OpLoad %v2float %150 + %152 = OpAccessChain %_ptr_Function_v2float %63 %int_3 + %153 = OpLoad %v2float %152 + %154 = OpVectorShuffle %v4float %151 %153 0 1 2 3 + %155 = OpUMod %uint %68 %uint_2 + %156 = OpConvertUToF %float %155 + %157 = OpUDiv %uint %68 %uint_2 + %158 = OpConvertUToF %float %157 + %159 = OpCompositeConstruct %v2float %156 %158 + %160 = OpFMul %v2float %159 %39 + %161 = OpFSub %v2float %160 %40 + %162 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 + %163 = OpLoad %float %162 + %164 = OpFMul %float %72 %163 + %165 = OpFAdd %float %164 %float_1 + %166 = OpCompositeConstruct %v2float %165 %165 + %167 = OpFMul %v2float %166 %161 + %168 = OpFAdd %v2float %167 %144 + %169 = OpFAdd %v2float %168 %41 + %170 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_0 + %171 = OpLoad %v4float %170 + %172 = OpVectorShuffle %v2float %171 %171 2 3 + %173 = OpFMul %v2float %169 %172 + %174 = OpCompositeExtract %float %173 0 + %175 = OpFMul %float %174 %float_2 + %176 = OpFSub %float %175 %float_1 + %177 = OpCompositeExtract %float %173 1 + %178 = OpFMul %float %177 %float_2 + %179 = OpFSub %float %float_1 %178 + %180 = OpCompositeConstruct %v4float %176 %179 %float_0 %float_1 + OpStore %out_var_TEXCOORD0 %144 + OpStore %out_var_TEXCOORD1 %112 + OpStore %out_var_TEXCOORD2 %120 + OpStore %out_var_TEXCOORD3 %128 + OpStore %out_var_TEXCOORD4 %136 + OpStore %out_var_TEXCOORD5 %149 + OpStore %out_var_TEXCOORD6 %154 + OpStore %gl_Position %180 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/depth-compare.asm.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/depth-compare.asm.frag new file mode 100644 index 0000000..603d4f2 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/depth-compare.asm.frag @@ -0,0 +1,961 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 452 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainOnePassPointLightPS "main" %gl_FragCoord %out_var_SV_Target0 + OpExecutionMode %MainOnePassPointLightPS OriginUpperLeft + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_TranslatedWorldToView" + OpMemberName %type_View 3 "View_ViewToTranslatedWorld" + OpMemberName %type_View 4 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 5 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 6 "View_ViewToClip" + OpMemberName %type_View 7 "View_ViewToClipNoAA" + OpMemberName %type_View 8 "View_ClipToView" + OpMemberName %type_View 9 "View_ClipToTranslatedWorld" + OpMemberName %type_View 10 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 11 "View_ScreenToWorld" + OpMemberName %type_View 12 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 13 "View_ViewForward" + OpMemberName %type_View 14 "PrePadding_View_844" + OpMemberName %type_View 15 "View_ViewUp" + OpMemberName %type_View 16 "PrePadding_View_860" + OpMemberName %type_View 17 "View_ViewRight" + OpMemberName %type_View 18 "PrePadding_View_876" + OpMemberName %type_View 19 "View_HMDViewNoRollUp" + OpMemberName %type_View 20 "PrePadding_View_892" + OpMemberName %type_View 21 "View_HMDViewNoRollRight" + OpMemberName %type_View 22 "PrePadding_View_908" + OpMemberName %type_View 23 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 24 "View_ScreenPositionScaleBias" + OpMemberName %type_View 25 "View_WorldCameraOrigin" + OpMemberName %type_View 26 "PrePadding_View_956" + OpMemberName %type_View 27 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 28 "PrePadding_View_972" + OpMemberName %type_View 29 "View_WorldViewOrigin" + OpMemberName %type_View 30 "PrePadding_View_988" + OpMemberName %type_View 31 "View_PreViewTranslation" + OpMemberName %type_View 32 "PrePadding_View_1004" + OpMemberName %type_View 33 "View_PrevProjection" + OpMemberName %type_View 34 "View_PrevViewProj" + OpMemberName %type_View 35 "View_PrevViewRotationProj" + OpMemberName %type_View 36 "View_PrevViewToClip" + OpMemberName %type_View 37 "View_PrevClipToView" + OpMemberName %type_View 38 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 40 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 41 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 42 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 43 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 44 "PrePadding_View_1660" + OpMemberName %type_View 45 "View_PrevWorldViewOrigin" + OpMemberName %type_View 46 "PrePadding_View_1676" + OpMemberName %type_View 47 "View_PrevPreViewTranslation" + OpMemberName %type_View 48 "PrePadding_View_1692" + OpMemberName %type_View 49 "View_PrevInvViewProj" + OpMemberName %type_View 50 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 51 "View_ClipToPrevClip" + OpMemberName %type_View 52 "View_TemporalAAJitter" + OpMemberName %type_View 53 "View_GlobalClippingPlane" + OpMemberName %type_View 54 "View_FieldOfViewWideAngles" + OpMemberName %type_View 55 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 56 "View_ViewRectMin" + OpMemberName %type_View 57 "View_ViewSizeAndInvSize" + OpMemberName %type_View 58 "View_BufferSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 60 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 61 "View_PreExposure" + OpMemberName %type_View 62 "View_OneOverPreExposure" + OpMemberName %type_View 63 "PrePadding_View_2012" + OpMemberName %type_View 64 "View_DiffuseOverrideParameter" + OpMemberName %type_View 65 "View_SpecularOverrideParameter" + OpMemberName %type_View 66 "View_NormalOverrideParameter" + OpMemberName %type_View 67 "View_RoughnessOverrideParameter" + OpMemberName %type_View 68 "View_PrevFrameGameTime" + OpMemberName %type_View 69 "View_PrevFrameRealTime" + OpMemberName %type_View 70 "View_OutOfBoundsMask" + OpMemberName %type_View 71 "PrePadding_View_2084" + OpMemberName %type_View 72 "PrePadding_View_2088" + OpMemberName %type_View 73 "PrePadding_View_2092" + OpMemberName %type_View 74 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 75 "View_CullingSign" + OpMemberName %type_View 76 "View_NearPlane" + OpMemberName %type_View 77 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 78 "View_GameTime" + OpMemberName %type_View 79 "View_RealTime" + OpMemberName %type_View 80 "View_DeltaTime" + OpMemberName %type_View 81 "View_MaterialTextureMipBias" + OpMemberName %type_View 82 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 83 "View_Random" + OpMemberName %type_View 84 "View_FrameNumber" + OpMemberName %type_View 85 "View_StateFrameIndexMod8" + OpMemberName %type_View 86 "View_StateFrameIndex" + OpMemberName %type_View 87 "View_CameraCut" + OpMemberName %type_View 88 "View_UnlitViewmodeMask" + OpMemberName %type_View 89 "PrePadding_View_2164" + OpMemberName %type_View 90 "PrePadding_View_2168" + OpMemberName %type_View 91 "PrePadding_View_2172" + OpMemberName %type_View 92 "View_DirectionalLightColor" + OpMemberName %type_View 93 "View_DirectionalLightDirection" + OpMemberName %type_View 94 "PrePadding_View_2204" + OpMemberName %type_View 95 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 97 "View_TemporalAAParams" + OpMemberName %type_View 98 "View_CircleDOFParams" + OpMemberName %type_View 99 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 100 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 101 "View_DepthOfFieldScale" + OpMemberName %type_View 102 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 103 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 104 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 105 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 106 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 107 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 108 "View_GeneralPurposeTweak" + OpMemberName %type_View 109 "View_DemosaicVposOffset" + OpMemberName %type_View 110 "PrePadding_View_2348" + OpMemberName %type_View 111 "View_IndirectLightingColorScale" + OpMemberName %type_View 112 "View_HDR32bppEncodingMode" + OpMemberName %type_View 113 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 114 "View_AtmosphericFogSunPower" + OpMemberName %type_View 115 "View_AtmosphericFogPower" + OpMemberName %type_View 116 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 117 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 118 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 119 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 120 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 121 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 122 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 123 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 124 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 125 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 126 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 127 "View_AtmosphericFogSunColor" + OpMemberName %type_View 128 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 129 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 130 "View_AmbientCubemapTint" + OpMemberName %type_View 131 "View_AmbientCubemapIntensity" + OpMemberName %type_View 132 "View_SkyLightParameters" + OpMemberName %type_View 133 "PrePadding_View_2488" + OpMemberName %type_View 134 "PrePadding_View_2492" + OpMemberName %type_View 135 "View_SkyLightColor" + OpMemberName %type_View 136 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 137 "View_MobilePreviewMode" + OpMemberName %type_View 138 "View_HMDEyePaddingOffset" + OpMemberName %type_View 139 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 140 "View_ShowDecalsMask" + OpMemberName %type_View 141 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 142 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 143 "PrePadding_View_2648" + OpMemberName %type_View 144 "PrePadding_View_2652" + OpMemberName %type_View 145 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 146 "View_StereoPassIndex" + OpMemberName %type_View 147 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 148 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 149 "View_GlobalVolumeDimension" + OpMemberName %type_View 150 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 151 "View_MaxGlobalDistance" + OpMemberName %type_View 152 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 153 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 154 "PrePadding_View_2828" + OpMemberName %type_View 155 "View_VolumetricFogGridZParams" + OpMemberName %type_View 156 "PrePadding_View_2844" + OpMemberName %type_View 157 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 158 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 159 "PrePadding_View_2860" + OpMemberName %type_View 160 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 161 "PrePadding_View_2876" + OpMemberName %type_View 162 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 163 "PrePadding_View_2892" + OpMemberName %type_View 164 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 165 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 166 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 167 "View_StereoIPD" + OpMemberName %type_View 168 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 169 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_2d_image "type.2d.image" + OpName %SceneTexturesStruct_SceneDepthTexture "SceneTexturesStruct_SceneDepthTexture" + OpName %type_sampler "type.sampler" + OpName %SceneTexturesStruct_SceneDepthTextureSampler "SceneTexturesStruct_SceneDepthTextureSampler" + OpName %SceneTexturesStruct_GBufferATexture "SceneTexturesStruct_GBufferATexture" + OpName %SceneTexturesStruct_GBufferBTexture "SceneTexturesStruct_GBufferBTexture" + OpName %SceneTexturesStruct_GBufferDTexture "SceneTexturesStruct_GBufferDTexture" + OpName %SceneTexturesStruct_GBufferATextureSampler "SceneTexturesStruct_GBufferATextureSampler" + OpName %SceneTexturesStruct_GBufferBTextureSampler "SceneTexturesStruct_GBufferBTextureSampler" + OpName %SceneTexturesStruct_GBufferDTextureSampler "SceneTexturesStruct_GBufferDTextureSampler" + OpName %ShadowDepthTextureSampler "ShadowDepthTextureSampler" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "SoftTransitionScale" + OpMemberName %type__Globals 1 "ShadowViewProjectionMatrices" + OpMemberName %type__Globals 2 "InvShadowmapResolution" + OpMemberName %type__Globals 3 "ShadowFadeFraction" + OpMemberName %type__Globals 4 "ShadowSharpen" + OpMemberName %type__Globals 5 "LightPositionAndInvRadius" + OpMemberName %type__Globals 6 "ProjectionDepthBiasParameters" + OpMemberName %type__Globals 7 "PointLightDepthBiasAndProjParameters" + OpName %_Globals "$Globals" + OpName %type_cube_image "type.cube.image" + OpName %ShadowDepthCubeTexture "ShadowDepthCubeTexture" + OpName %ShadowDepthCubeTextureSampler "ShadowDepthCubeTextureSampler" + OpName %SSProfilesTexture "SSProfilesTexture" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainOnePassPointLightPS "MainOnePassPointLightPS" + OpName %type_sampled_image "type.sampled.image" + OpName %type_sampled_image_0 "type.sampled.image" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_POSITION" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %SceneTexturesStruct_SceneDepthTexture DescriptorSet 0 + OpDecorate %SceneTexturesStruct_SceneDepthTexture Binding 0 + OpDecorate %SceneTexturesStruct_SceneDepthTextureSampler DescriptorSet 0 + OpDecorate %SceneTexturesStruct_SceneDepthTextureSampler Binding 0 + OpDecorate %SceneTexturesStruct_GBufferATexture DescriptorSet 0 + OpDecorate %SceneTexturesStruct_GBufferATexture Binding 1 + OpDecorate %SceneTexturesStruct_GBufferBTexture DescriptorSet 0 + OpDecorate %SceneTexturesStruct_GBufferBTexture Binding 2 + OpDecorate %SceneTexturesStruct_GBufferDTexture DescriptorSet 0 + OpDecorate %SceneTexturesStruct_GBufferDTexture Binding 3 + OpDecorate %SceneTexturesStruct_GBufferATextureSampler DescriptorSet 0 + OpDecorate %SceneTexturesStruct_GBufferATextureSampler Binding 1 + OpDecorate %SceneTexturesStruct_GBufferBTextureSampler DescriptorSet 0 + OpDecorate %SceneTexturesStruct_GBufferBTextureSampler Binding 2 + OpDecorate %SceneTexturesStruct_GBufferDTextureSampler DescriptorSet 0 + OpDecorate %SceneTexturesStruct_GBufferDTextureSampler Binding 3 + OpDecorate %ShadowDepthTextureSampler DescriptorSet 0 + OpDecorate %ShadowDepthTextureSampler Binding 4 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 1 + OpDecorate %ShadowDepthCubeTexture DescriptorSet 0 + OpDecorate %ShadowDepthCubeTexture Binding 4 + OpDecorate %ShadowDepthCubeTextureSampler DescriptorSet 0 + OpDecorate %ShadowDepthCubeTextureSampler Binding 5 + OpDecorate %SSProfilesTexture DescriptorSet 0 + OpDecorate %SSProfilesTexture Binding 5 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 14 Offset 844 + OpMemberDecorate %type_View 15 Offset 848 + OpMemberDecorate %type_View 16 Offset 860 + OpMemberDecorate %type_View 17 Offset 864 + OpMemberDecorate %type_View 18 Offset 876 + OpMemberDecorate %type_View 19 Offset 880 + OpMemberDecorate %type_View 20 Offset 892 + OpMemberDecorate %type_View 21 Offset 896 + OpMemberDecorate %type_View 22 Offset 908 + OpMemberDecorate %type_View 23 Offset 912 + OpMemberDecorate %type_View 24 Offset 928 + OpMemberDecorate %type_View 25 Offset 944 + OpMemberDecorate %type_View 26 Offset 956 + OpMemberDecorate %type_View 27 Offset 960 + OpMemberDecorate %type_View 28 Offset 972 + OpMemberDecorate %type_View 29 Offset 976 + OpMemberDecorate %type_View 30 Offset 988 + OpMemberDecorate %type_View 31 Offset 992 + OpMemberDecorate %type_View 32 Offset 1004 + OpMemberDecorate %type_View 33 Offset 1008 + OpMemberDecorate %type_View 33 MatrixStride 16 + OpMemberDecorate %type_View 33 ColMajor + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 44 Offset 1660 + OpMemberDecorate %type_View 45 Offset 1664 + OpMemberDecorate %type_View 46 Offset 1676 + OpMemberDecorate %type_View 47 Offset 1680 + OpMemberDecorate %type_View 48 Offset 1692 + OpMemberDecorate %type_View 49 Offset 1696 + OpMemberDecorate %type_View 49 MatrixStride 16 + OpMemberDecorate %type_View 49 ColMajor + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 53 Offset 1904 + OpMemberDecorate %type_View 54 Offset 1920 + OpMemberDecorate %type_View 55 Offset 1928 + OpMemberDecorate %type_View 56 Offset 1936 + OpMemberDecorate %type_View 57 Offset 1952 + OpMemberDecorate %type_View 58 Offset 1968 + OpMemberDecorate %type_View 59 Offset 1984 + OpMemberDecorate %type_View 60 Offset 2000 + OpMemberDecorate %type_View 61 Offset 2004 + OpMemberDecorate %type_View 62 Offset 2008 + OpMemberDecorate %type_View 63 Offset 2012 + OpMemberDecorate %type_View 64 Offset 2016 + OpMemberDecorate %type_View 65 Offset 2032 + OpMemberDecorate %type_View 66 Offset 2048 + OpMemberDecorate %type_View 67 Offset 2064 + OpMemberDecorate %type_View 68 Offset 2072 + OpMemberDecorate %type_View 69 Offset 2076 + OpMemberDecorate %type_View 70 Offset 2080 + OpMemberDecorate %type_View 71 Offset 2084 + OpMemberDecorate %type_View 72 Offset 2088 + OpMemberDecorate %type_View 73 Offset 2092 + OpMemberDecorate %type_View 74 Offset 2096 + OpMemberDecorate %type_View 75 Offset 2108 + OpMemberDecorate %type_View 76 Offset 2112 + OpMemberDecorate %type_View 77 Offset 2116 + OpMemberDecorate %type_View 78 Offset 2120 + OpMemberDecorate %type_View 79 Offset 2124 + OpMemberDecorate %type_View 80 Offset 2128 + OpMemberDecorate %type_View 81 Offset 2132 + OpMemberDecorate %type_View 82 Offset 2136 + OpMemberDecorate %type_View 83 Offset 2140 + OpMemberDecorate %type_View 84 Offset 2144 + OpMemberDecorate %type_View 85 Offset 2148 + OpMemberDecorate %type_View 86 Offset 2152 + OpMemberDecorate %type_View 87 Offset 2156 + OpMemberDecorate %type_View 88 Offset 2160 + OpMemberDecorate %type_View 89 Offset 2164 + OpMemberDecorate %type_View 90 Offset 2168 + OpMemberDecorate %type_View 91 Offset 2172 + OpMemberDecorate %type_View 92 Offset 2176 + OpMemberDecorate %type_View 93 Offset 2192 + OpMemberDecorate %type_View 94 Offset 2204 + OpMemberDecorate %type_View 95 Offset 2208 + OpMemberDecorate %type_View 96 Offset 2240 + OpMemberDecorate %type_View 97 Offset 2272 + OpMemberDecorate %type_View 98 Offset 2288 + OpMemberDecorate %type_View 99 Offset 2304 + OpMemberDecorate %type_View 100 Offset 2308 + OpMemberDecorate %type_View 101 Offset 2312 + OpMemberDecorate %type_View 102 Offset 2316 + OpMemberDecorate %type_View 103 Offset 2320 + OpMemberDecorate %type_View 104 Offset 2324 + OpMemberDecorate %type_View 105 Offset 2328 + OpMemberDecorate %type_View 106 Offset 2332 + OpMemberDecorate %type_View 107 Offset 2336 + OpMemberDecorate %type_View 108 Offset 2340 + OpMemberDecorate %type_View 109 Offset 2344 + OpMemberDecorate %type_View 110 Offset 2348 + OpMemberDecorate %type_View 111 Offset 2352 + OpMemberDecorate %type_View 112 Offset 2364 + OpMemberDecorate %type_View 113 Offset 2368 + OpMemberDecorate %type_View 114 Offset 2380 + OpMemberDecorate %type_View 115 Offset 2384 + OpMemberDecorate %type_View 116 Offset 2388 + OpMemberDecorate %type_View 117 Offset 2392 + OpMemberDecorate %type_View 118 Offset 2396 + OpMemberDecorate %type_View 119 Offset 2400 + OpMemberDecorate %type_View 120 Offset 2404 + OpMemberDecorate %type_View 121 Offset 2408 + OpMemberDecorate %type_View 122 Offset 2412 + OpMemberDecorate %type_View 123 Offset 2416 + OpMemberDecorate %type_View 124 Offset 2420 + OpMemberDecorate %type_View 125 Offset 2424 + OpMemberDecorate %type_View 126 Offset 2428 + OpMemberDecorate %type_View 127 Offset 2432 + OpMemberDecorate %type_View 128 Offset 2448 + OpMemberDecorate %type_View 129 Offset 2460 + OpMemberDecorate %type_View 130 Offset 2464 + OpMemberDecorate %type_View 131 Offset 2480 + OpMemberDecorate %type_View 132 Offset 2484 + OpMemberDecorate %type_View 133 Offset 2488 + OpMemberDecorate %type_View 134 Offset 2492 + OpMemberDecorate %type_View 135 Offset 2496 + OpMemberDecorate %type_View 136 Offset 2512 + OpMemberDecorate %type_View 137 Offset 2624 + OpMemberDecorate %type_View 138 Offset 2628 + OpMemberDecorate %type_View 139 Offset 2632 + OpMemberDecorate %type_View 140 Offset 2636 + OpMemberDecorate %type_View 141 Offset 2640 + OpMemberDecorate %type_View 142 Offset 2644 + OpMemberDecorate %type_View 143 Offset 2648 + OpMemberDecorate %type_View 144 Offset 2652 + OpMemberDecorate %type_View 145 Offset 2656 + OpMemberDecorate %type_View 146 Offset 2668 + OpMemberDecorate %type_View 147 Offset 2672 + OpMemberDecorate %type_View 148 Offset 2736 + OpMemberDecorate %type_View 149 Offset 2800 + OpMemberDecorate %type_View 150 Offset 2804 + OpMemberDecorate %type_View 151 Offset 2808 + OpMemberDecorate %type_View 152 Offset 2812 + OpMemberDecorate %type_View 153 Offset 2816 + OpMemberDecorate %type_View 154 Offset 2828 + OpMemberDecorate %type_View 155 Offset 2832 + OpMemberDecorate %type_View 156 Offset 2844 + OpMemberDecorate %type_View 157 Offset 2848 + OpMemberDecorate %type_View 158 Offset 2856 + OpMemberDecorate %type_View 159 Offset 2860 + OpMemberDecorate %type_View 160 Offset 2864 + OpMemberDecorate %type_View 161 Offset 2876 + OpMemberDecorate %type_View 162 Offset 2880 + OpMemberDecorate %type_View 163 Offset 2892 + OpMemberDecorate %type_View 164 Offset 2896 + OpMemberDecorate %type_View 165 Offset 2908 + OpMemberDecorate %type_View 166 Offset 2912 + OpMemberDecorate %type_View 167 Offset 2924 + OpMemberDecorate %type_View 168 Offset 2928 + OpMemberDecorate %type_View 169 Offset 2932 + OpDecorate %type_View Block + OpDecorate %_arr_mat4v4float_uint_6 ArrayStride 64 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 1 MatrixStride 16 + OpMemberDecorate %type__Globals 1 ColMajor + OpMemberDecorate %type__Globals 2 Offset 400 + OpMemberDecorate %type__Globals 3 Offset 404 + OpMemberDecorate %type__Globals 4 Offset 408 + OpMemberDecorate %type__Globals 5 Offset 416 + OpMemberDecorate %type__Globals 6 Offset 432 + OpMemberDecorate %type__Globals 7 Offset 448 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %float_0 = OpConstant %float 0 + %float_2_5 = OpConstant %float 2.5 +%float_2_37764096 = OpConstant %float 2.37764096 +%float_0_772542 = OpConstant %float 0.772542 +%float_1_46946299 = OpConstant %float 1.46946299 +%float_n2_02254295 = OpConstant %float -2.02254295 +%float_n1_46946299 = OpConstant %float -1.46946299 +%float_n2_022542 = OpConstant %float -2.022542 +%float_n2_37764096 = OpConstant %float -2.37764096 +%float_0_772543013 = OpConstant %float 0.772543013 + %float_1 = OpConstant %float 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 + %int_7 = OpConstant %int 7 + %int_58 = OpConstant %int 58 + %int_24 = OpConstant %int 24 + %int_11 = OpConstant %int 11 + %int_5 = OpConstant %int 5 + %float_0_5 = OpConstant %float 0.5 + %int_4 = OpConstant %int 4 + %int_2 = OpConstant %int 2 + %62 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %bool = OpTypeBool + %uint_5 = OpConstant %uint 5 + %65 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %66 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %float_10 = OpConstant %float 10 + %float_5 = OpConstant %float 5 + %uint_0 = OpConstant %uint 0 + %int_23 = OpConstant %int 23 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + %uint_16 = OpConstant %uint 16 +%float_0_150000006 = OpConstant %float 0.150000006 + %float_0_25 = OpConstant %float 0.25 + %float_2 = OpConstant %float 2 + %77 = OpConstantComposite %v3float %float_2 %float_2 %float_2 + %float_255 = OpConstant %float 255 + %uint_15 = OpConstant %uint 15 +%uint_4294967280 = OpConstant %uint 4294967280 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %uint_6 = OpConstant %uint 6 +%_arr_mat4v4float_uint_6 = OpTypeArray %mat4v4float %uint_6 +%type__Globals = OpTypeStruct %v3float %_arr_mat4v4float_uint_6 %float %float %float %v4float %v2float %v4float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%type_cube_image = OpTypeImage %float Cube 2 0 0 1 Unknown +%_ptr_UniformConstant_type_cube_image = OpTypePointer UniformConstant %type_cube_image + %v2int = OpTypeVector %int 2 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %91 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%type_sampled_image = OpTypeSampledImage %type_cube_image + %v3int = OpTypeVector %int 3 +%type_sampled_image_0 = OpTypeSampledImage %type_2d_image + %v4bool = OpTypeVector %bool 4 + %View = OpVariable %_ptr_Uniform_type_View Uniform +%SceneTexturesStruct_SceneDepthTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%SceneTexturesStruct_SceneDepthTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%SceneTexturesStruct_GBufferATexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%SceneTexturesStruct_GBufferBTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%SceneTexturesStruct_GBufferDTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%SceneTexturesStruct_GBufferATextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%SceneTexturesStruct_GBufferBTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%SceneTexturesStruct_GBufferDTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%ShadowDepthTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%ShadowDepthCubeTexture = OpVariable %_ptr_UniformConstant_type_cube_image UniformConstant +%ShadowDepthCubeTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%SSProfilesTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output +%float_0_200000003 = OpConstant %float 0.200000003 + %98 = OpConstantComposite %v3float %float_2_5 %float_2_5 %float_2_5 + %99 = OpConstantComposite %v3float %float_2_37764096 %float_2_37764096 %float_2_37764096 + %100 = OpConstantComposite %v3float %float_0_772542 %float_0_772542 %float_0_772542 + %101 = OpConstantComposite %v3float %float_1_46946299 %float_1_46946299 %float_1_46946299 + %102 = OpConstantComposite %v3float %float_n2_02254295 %float_n2_02254295 %float_n2_02254295 + %103 = OpConstantComposite %v3float %float_n1_46946299 %float_n1_46946299 %float_n1_46946299 + %104 = OpConstantComposite %v3float %float_n2_022542 %float_n2_022542 %float_n2_022542 + %105 = OpConstantComposite %v3float %float_n2_37764096 %float_n2_37764096 %float_n2_37764096 + %106 = OpConstantComposite %v3float %float_0_772543013 %float_0_772543013 %float_0_772543013 + %107 = OpUndef %v4float +%MainOnePassPointLightPS = OpFunction %void None %91 + %108 = OpLabel + %109 = OpLoad %v4float %gl_FragCoord + %110 = OpVectorShuffle %v2float %109 %109 0 1 + %111 = OpAccessChain %_ptr_Uniform_v4float %View %int_58 + %112 = OpLoad %v4float %111 + %113 = OpVectorShuffle %v2float %112 %112 2 3 + %114 = OpFMul %v2float %110 %113 + %115 = OpLoad %type_2d_image %SceneTexturesStruct_SceneDepthTexture + %116 = OpLoad %type_sampler %SceneTexturesStruct_SceneDepthTextureSampler + %117 = OpSampledImage %type_sampled_image_0 %115 %116 + %118 = OpImageSampleExplicitLod %v4float %117 %114 Lod %float_0 + %119 = OpCompositeExtract %float %118 0 + %120 = OpAccessChain %_ptr_Uniform_float %View %int_23 %uint_0 + %121 = OpLoad %float %120 + %122 = OpFMul %float %119 %121 + %123 = OpAccessChain %_ptr_Uniform_float %View %int_23 %uint_1 + %124 = OpLoad %float %123 + %125 = OpFAdd %float %122 %124 + %126 = OpAccessChain %_ptr_Uniform_float %View %int_23 %uint_2 + %127 = OpLoad %float %126 + %128 = OpFMul %float %119 %127 + %129 = OpAccessChain %_ptr_Uniform_float %View %int_23 %uint_3 + %130 = OpLoad %float %129 + %131 = OpFSub %float %128 %130 + %132 = OpFDiv %float %float_1 %131 + %133 = OpFAdd %float %125 %132 + %134 = OpAccessChain %_ptr_Uniform_v4float %View %int_24 + %135 = OpLoad %v4float %134 + %136 = OpVectorShuffle %v2float %135 %135 3 2 + %137 = OpFSub %v2float %114 %136 + %138 = OpVectorShuffle %v2float %135 %135 0 1 + %139 = OpFDiv %v2float %137 %138 + %140 = OpCompositeConstruct %v2float %133 %133 + %141 = OpFMul %v2float %139 %140 + %142 = OpCompositeExtract %float %141 0 + %143 = OpCompositeExtract %float %141 1 + %144 = OpCompositeConstruct %v4float %142 %143 %133 %float_1 + %145 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_11 + %146 = OpLoad %mat4v4float %145 + %147 = OpMatrixTimesVector %v4float %146 %144 + %148 = OpVectorShuffle %v3float %147 %147 0 1 2 + %149 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_5 + %150 = OpLoad %v4float %149 + %151 = OpVectorShuffle %v3float %150 %150 0 1 2 + %152 = OpFSub %v3float %151 %148 + %153 = OpAccessChain %_ptr_Uniform_float %_Globals %int_5 %int_3 + %154 = OpLoad %float %153 + %155 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_7 + %156 = OpAccessChain %_ptr_Uniform_float %_Globals %int_7 %int_0 + %157 = OpLoad %float %156 + %158 = OpExtInst %float %1 Length %152 + %159 = OpFMul %float %158 %154 + %160 = OpFOrdLessThan %bool %159 %float_1 + OpSelectionMerge %161 DontFlatten + OpBranchConditional %160 %162 %161 + %162 = OpLabel + %163 = OpCompositeConstruct %v3float %158 %158 %158 + %164 = OpFDiv %v3float %152 %163 + %165 = OpExtInst %v3float %1 FAbs %152 + %166 = OpCompositeExtract %float %165 0 + %167 = OpCompositeExtract %float %165 1 + %168 = OpCompositeExtract %float %165 2 + %169 = OpExtInst %float %1 FMax %167 %168 + %170 = OpExtInst %float %1 FMax %166 %169 + %171 = OpFOrdEqual %bool %170 %166 + OpSelectionMerge %172 None + OpBranchConditional %171 %173 %174 + %174 = OpLabel + %175 = OpFOrdEqual %bool %170 %167 + OpSelectionMerge %176 None + OpBranchConditional %175 %177 %178 + %178 = OpLabel + %179 = OpCompositeExtract %float %152 2 + %180 = OpFOrdEqual %bool %168 %179 + %181 = OpSelect %int %180 %int_4 %int_5 + OpBranch %176 + %177 = OpLabel + %182 = OpCompositeExtract %float %152 1 + %183 = OpFOrdEqual %bool %167 %182 + %184 = OpSelect %int %183 %int_2 %int_3 + OpBranch %176 + %176 = OpLabel + %185 = OpPhi %int %184 %177 %181 %178 + OpBranch %172 + %173 = OpLabel + %186 = OpCompositeExtract %float %152 0 + %187 = OpFOrdEqual %bool %166 %186 + %188 = OpSelect %int %187 %int_0 %int_1 + OpBranch %172 + %172 = OpLabel + %189 = OpPhi %int %188 %173 %185 %176 + %190 = OpCompositeExtract %float %147 0 + %191 = OpCompositeExtract %float %147 1 + %192 = OpCompositeExtract %float %147 2 + %193 = OpCompositeConstruct %v4float %190 %191 %192 %float_1 + %194 = OpAccessChain %_ptr_Uniform_mat4v4float %_Globals %int_1 %189 + %195 = OpLoad %mat4v4float %194 + %196 = OpMatrixTimesVector %v4float %195 %193 + %197 = OpCompositeExtract %float %196 2 + %198 = OpCompositeExtract %float %196 3 + %199 = OpFDiv %float %197 %198 + %200 = OpFNegate %float %157 + %201 = OpFDiv %float %200 %198 + %202 = OpLoad %type_cube_image %ShadowDepthCubeTexture + %203 = OpLoad %type_sampler %ShadowDepthCubeTextureSampler + %204 = OpFAdd %float %199 %201 + %205 = OpSampledImage %type_sampled_image %202 %203 + %206 = OpImageSampleDrefExplicitLod %float %205 %164 %204 Lod %float_0 + OpBranch %161 + %161 = OpLabel + %207 = OpPhi %float %float_1 %108 %206 %172 + %208 = OpFSub %float %207 %float_0_5 + %209 = OpAccessChain %_ptr_Uniform_float %_Globals %int_4 + %210 = OpLoad %float %209 + %211 = OpFMul %float %208 %210 + %212 = OpFAdd %float %211 %float_0_5 + %213 = OpExtInst %float %1 FClamp %212 %float_0 %float_1 + %214 = OpFMul %float %213 %213 + %215 = OpAccessChain %_ptr_Uniform_float %_Globals %int_3 + %216 = OpLoad %float %215 + %217 = OpExtInst %float %1 FMix %float_1 %214 %216 + %218 = OpExtInst %float %1 Sqrt %217 + %219 = OpCompositeInsert %v4float %218 %107 2 + %220 = OpVectorShuffle %v4float %219 %62 4 5 2 6 + %221 = OpLoad %type_2d_image %SceneTexturesStruct_GBufferATexture + %222 = OpLoad %type_sampler %SceneTexturesStruct_GBufferATextureSampler + %223 = OpSampledImage %type_sampled_image_0 %221 %222 + %224 = OpImageSampleExplicitLod %v4float %223 %114 Lod %float_0 + %225 = OpLoad %type_2d_image %SceneTexturesStruct_GBufferBTexture + %226 = OpLoad %type_sampler %SceneTexturesStruct_GBufferBTextureSampler + %227 = OpSampledImage %type_sampled_image_0 %225 %226 + %228 = OpImageSampleExplicitLod %v4float %227 %114 Lod %float_0 + %229 = OpLoad %type_2d_image %SceneTexturesStruct_GBufferDTexture + %230 = OpLoad %type_sampler %SceneTexturesStruct_GBufferDTextureSampler + %231 = OpSampledImage %type_sampled_image_0 %229 %230 + %232 = OpImageSampleExplicitLod %v4float %231 %114 Lod %float_0 + %233 = OpVectorShuffle %v3float %224 %224 0 1 2 + %234 = OpFMul %v3float %233 %77 + %235 = OpFSub %v3float %234 %62 + %236 = OpExtInst %v3float %1 Normalize %235 + %237 = OpCompositeExtract %float %228 3 + %238 = OpFMul %float %237 %float_255 + %239 = OpExtInst %float %1 Round %238 + %240 = OpConvertFToU %uint %239 + %241 = OpBitwiseAnd %uint %240 %uint_15 + %242 = OpBitwiseAnd %uint %240 %uint_4294967280 + %243 = OpBitwiseAnd %uint %242 %uint_16 + %244 = OpINotEqual %bool %243 %uint_0 + %245 = OpLogicalNot %bool %244 + %246 = OpCompositeConstruct %v4bool %245 %245 %245 %245 + %247 = OpSelect %v4float %246 %232 %66 + %248 = OpIEqual %bool %241 %uint_5 + OpSelectionMerge %249 None + OpBranchConditional %248 %250 %249 + %250 = OpLabel + %251 = OpLoad %v4float %155 + %252 = OpCompositeExtract %float %247 0 + %253 = OpFMul %float %252 %float_255 + %254 = OpFAdd %float %253 %float_0_5 + %255 = OpConvertFToU %uint %254 + %256 = OpBitcast %int %255 + %257 = OpCompositeConstruct %v3int %int_1 %256 %int_0 + %258 = OpVectorShuffle %v2int %257 %257 0 1 + %259 = OpLoad %type_2d_image %SSProfilesTexture + %260 = OpImageFetch %v4float %259 %258 Lod %int_0 + %261 = OpCompositeExtract %float %260 0 + %262 = OpCompositeExtract %float %260 1 + %263 = OpFMul %float %262 %float_0_5 + %264 = OpCompositeConstruct %v3float %263 %263 %263 + %265 = OpFMul %v3float %236 %264 + %266 = OpFSub %v3float %148 %265 + %267 = OpDot %float %152 %152 + %268 = OpExtInst %float %1 InverseSqrt %267 + %269 = OpCompositeConstruct %v3float %268 %268 %268 + %270 = OpFMul %v3float %152 %269 + %271 = OpFNegate %v3float %270 + %272 = OpDot %float %271 %236 + %273 = OpExtInst %float %1 FClamp %272 %float_0 %float_1 + %274 = OpExtInst %float %1 Pow %273 %float_1 + OpSelectionMerge %275 DontFlatten + OpBranchConditional %160 %276 %275 + %276 = OpLabel + %277 = OpCompositeConstruct %v3float %158 %158 %158 + %278 = OpFDiv %v3float %152 %277 + %279 = OpExtInst %v3float %1 Cross %278 %65 + %280 = OpExtInst %v3float %1 Normalize %279 + %281 = OpExtInst %v3float %1 Cross %280 %278 + %282 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 + %283 = OpLoad %float %282 + %284 = OpCompositeConstruct %v3float %283 %283 %283 + %285 = OpFMul %v3float %280 %284 + %286 = OpFMul %v3float %281 %284 + %287 = OpExtInst %v3float %1 FAbs %278 + %288 = OpCompositeExtract %float %287 0 + %289 = OpCompositeExtract %float %287 1 + %290 = OpCompositeExtract %float %287 2 + %291 = OpExtInst %float %1 FMax %289 %290 + %292 = OpExtInst %float %1 FMax %288 %291 + %293 = OpFOrdEqual %bool %292 %288 + OpSelectionMerge %294 None + OpBranchConditional %293 %295 %296 + %296 = OpLabel + %297 = OpFOrdEqual %bool %292 %289 + OpSelectionMerge %298 None + OpBranchConditional %297 %299 %300 + %300 = OpLabel + %301 = OpCompositeExtract %float %278 2 + %302 = OpFOrdEqual %bool %290 %301 + %303 = OpSelect %int %302 %int_4 %int_5 + OpBranch %298 + %299 = OpLabel + %304 = OpCompositeExtract %float %278 1 + %305 = OpFOrdEqual %bool %289 %304 + %306 = OpSelect %int %305 %int_2 %int_3 + OpBranch %298 + %298 = OpLabel + %307 = OpPhi %int %306 %299 %303 %300 + OpBranch %294 + %295 = OpLabel + %308 = OpCompositeExtract %float %278 0 + %309 = OpFOrdEqual %bool %288 %308 + %310 = OpSelect %int %309 %int_0 %int_1 + OpBranch %294 + %294 = OpLabel + %311 = OpPhi %int %310 %295 %307 %298 + %312 = OpCompositeExtract %float %266 0 + %313 = OpCompositeExtract %float %266 1 + %314 = OpCompositeExtract %float %266 2 + %315 = OpCompositeConstruct %v4float %312 %313 %314 %float_1 + %316 = OpAccessChain %_ptr_Uniform_mat4v4float %_Globals %int_1 %311 + %317 = OpLoad %mat4v4float %316 + %318 = OpMatrixTimesVector %v4float %317 %315 + %319 = OpCompositeExtract %float %318 2 + %320 = OpCompositeExtract %float %318 3 + %321 = OpFDiv %float %319 %320 + %322 = OpFDiv %float %float_10 %154 + %323 = OpFMul %float %261 %322 + %324 = OpCompositeExtract %float %251 2 + %325 = OpFMul %float %321 %324 + %326 = OpCompositeExtract %float %251 3 + %327 = OpFSub %float %325 %326 + %328 = OpFDiv %float %float_1 %327 + %329 = OpFMul %float %328 %154 + %330 = OpFMul %v3float %286 %98 + %331 = OpFAdd %v3float %278 %330 + %332 = OpLoad %type_cube_image %ShadowDepthCubeTexture + %333 = OpLoad %type_sampler %ShadowDepthTextureSampler + %334 = OpSampledImage %type_sampled_image %332 %333 + %335 = OpImageSampleExplicitLod %v4float %334 %331 Lod %float_0 + %336 = OpCompositeExtract %float %335 0 + %337 = OpFMul %float %336 %324 + %338 = OpFSub %float %337 %326 + %339 = OpFDiv %float %float_1 %338 + %340 = OpFMul %float %339 %154 + %341 = OpFSub %float %329 %340 + %342 = OpFMul %float %341 %323 + %343 = OpFOrdGreaterThan %bool %342 %float_0 + %344 = OpFAdd %float %342 %263 + %345 = OpFMul %float %342 %274 + %346 = OpFAdd %float %345 %263 + %347 = OpExtInst %float %1 FMax %float_0 %346 + %348 = OpSelect %float %343 %344 %347 + %349 = OpExtInst %float %1 FAbs %348 + %350 = OpExtInst %float %1 FClamp %349 %float_0_150000006 %float_5 + %351 = OpFAdd %float %350 %float_0_25 + %352 = OpFMul %v3float %285 %99 + %353 = OpFAdd %v3float %278 %352 + %354 = OpFMul %v3float %286 %100 + %355 = OpFAdd %v3float %353 %354 + %356 = OpSampledImage %type_sampled_image %332 %333 + %357 = OpImageSampleExplicitLod %v4float %356 %355 Lod %float_0 + %358 = OpCompositeExtract %float %357 0 + %359 = OpFMul %float %358 %324 + %360 = OpFSub %float %359 %326 + %361 = OpFDiv %float %float_1 %360 + %362 = OpFMul %float %361 %154 + %363 = OpFSub %float %329 %362 + %364 = OpFMul %float %363 %323 + %365 = OpFOrdGreaterThan %bool %364 %float_0 + %366 = OpFAdd %float %364 %263 + %367 = OpFMul %float %364 %274 + %368 = OpFAdd %float %367 %263 + %369 = OpExtInst %float %1 FMax %float_0 %368 + %370 = OpSelect %float %365 %366 %369 + %371 = OpExtInst %float %1 FAbs %370 + %372 = OpExtInst %float %1 FClamp %371 %float_0_150000006 %float_5 + %373 = OpFAdd %float %372 %float_0_25 + %374 = OpFAdd %float %351 %373 + %375 = OpFMul %v3float %285 %101 + %376 = OpFAdd %v3float %278 %375 + %377 = OpFMul %v3float %286 %102 + %378 = OpFAdd %v3float %376 %377 + %379 = OpSampledImage %type_sampled_image %332 %333 + %380 = OpImageSampleExplicitLod %v4float %379 %378 Lod %float_0 + %381 = OpCompositeExtract %float %380 0 + %382 = OpFMul %float %381 %324 + %383 = OpFSub %float %382 %326 + %384 = OpFDiv %float %float_1 %383 + %385 = OpFMul %float %384 %154 + %386 = OpFSub %float %329 %385 + %387 = OpFMul %float %386 %323 + %388 = OpFOrdGreaterThan %bool %387 %float_0 + %389 = OpFAdd %float %387 %263 + %390 = OpFMul %float %387 %274 + %391 = OpFAdd %float %390 %263 + %392 = OpExtInst %float %1 FMax %float_0 %391 + %393 = OpSelect %float %388 %389 %392 + %394 = OpExtInst %float %1 FAbs %393 + %395 = OpExtInst %float %1 FClamp %394 %float_0_150000006 %float_5 + %396 = OpFAdd %float %395 %float_0_25 + %397 = OpFAdd %float %374 %396 + %398 = OpFMul %v3float %285 %103 + %399 = OpFAdd %v3float %278 %398 + %400 = OpFMul %v3float %286 %104 + %401 = OpFAdd %v3float %399 %400 + %402 = OpSampledImage %type_sampled_image %332 %333 + %403 = OpImageSampleExplicitLod %v4float %402 %401 Lod %float_0 + %404 = OpCompositeExtract %float %403 0 + %405 = OpFMul %float %404 %324 + %406 = OpFSub %float %405 %326 + %407 = OpFDiv %float %float_1 %406 + %408 = OpFMul %float %407 %154 + %409 = OpFSub %float %329 %408 + %410 = OpFMul %float %409 %323 + %411 = OpFOrdGreaterThan %bool %410 %float_0 + %412 = OpFAdd %float %410 %263 + %413 = OpFMul %float %410 %274 + %414 = OpFAdd %float %413 %263 + %415 = OpExtInst %float %1 FMax %float_0 %414 + %416 = OpSelect %float %411 %412 %415 + %417 = OpExtInst %float %1 FAbs %416 + %418 = OpExtInst %float %1 FClamp %417 %float_0_150000006 %float_5 + %419 = OpFAdd %float %418 %float_0_25 + %420 = OpFAdd %float %397 %419 + %421 = OpFMul %v3float %285 %105 + %422 = OpFAdd %v3float %278 %421 + %423 = OpFMul %v3float %286 %106 + %424 = OpFAdd %v3float %422 %423 + %425 = OpSampledImage %type_sampled_image %332 %333 + %426 = OpImageSampleExplicitLod %v4float %425 %424 Lod %float_0 + %427 = OpCompositeExtract %float %426 0 + %428 = OpFMul %float %427 %324 + %429 = OpFSub %float %428 %326 + %430 = OpFDiv %float %float_1 %429 + %431 = OpFMul %float %430 %154 + %432 = OpFSub %float %329 %431 + %433 = OpFMul %float %432 %323 + %434 = OpFOrdGreaterThan %bool %433 %float_0 + %435 = OpFAdd %float %433 %263 + %436 = OpFMul %float %433 %274 + %437 = OpFAdd %float %436 %263 + %438 = OpExtInst %float %1 FMax %float_0 %437 + %439 = OpSelect %float %434 %435 %438 + %440 = OpExtInst %float %1 FAbs %439 + %441 = OpExtInst %float %1 FClamp %440 %float_0_150000006 %float_5 + %442 = OpFAdd %float %441 %float_0_25 + %443 = OpFAdd %float %420 %442 + %444 = OpFMul %float %443 %float_0_200000003 + OpBranch %275 + %275 = OpLabel + %445 = OpPhi %float %float_1 %250 %444 %294 + %446 = OpFMul %float %445 %float_0_200000003 + %447 = OpFSub %float %float_1 %446 + OpBranch %249 + %249 = OpLabel + %448 = OpPhi %float %float_1 %161 %447 %275 + %449 = OpExtInst %float %1 Sqrt %448 + %450 = OpSelect %float %248 %449 %218 + %451 = OpCompositeInsert %v4float %450 %220 3 + OpStore %out_var_SV_Target0 %451 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/global-constant-arrays.asm.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/global-constant-arrays.asm.frag new file mode 100644 index 0000000..47db9eb --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/global-constant-arrays.asm.frag @@ -0,0 +1,3556 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 3005 +; Schema: 0 + OpCapability Shader + OpCapability Geometry + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPS "main" %in_var_TEXCOORD0 %gl_FragCoord %gl_Layer %out_var_SV_Target0 + OpExecutionMode %MainPS OriginUpperLeft + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "MappingPolynomial" + OpMemberName %type__Globals 1 "InverseGamma" + OpMemberName %type__Globals 2 "ColorMatrixR_ColorCurveCd1" + OpMemberName %type__Globals 3 "ColorMatrixG_ColorCurveCd3Cm3" + OpMemberName %type__Globals 4 "ColorMatrixB_ColorCurveCm2" + OpMemberName %type__Globals 5 "ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3" + OpMemberName %type__Globals 6 "ColorCurve_Ch1_Ch2" + OpMemberName %type__Globals 7 "ColorShadow_Luma" + OpMemberName %type__Globals 8 "ColorShadow_Tint1" + OpMemberName %type__Globals 9 "ColorShadow_Tint2" + OpMemberName %type__Globals 10 "FilmSlope" + OpMemberName %type__Globals 11 "FilmToe" + OpMemberName %type__Globals 12 "FilmShoulder" + OpMemberName %type__Globals 13 "FilmBlackClip" + OpMemberName %type__Globals 14 "FilmWhiteClip" + OpMemberName %type__Globals 15 "ColorScale" + OpMemberName %type__Globals 16 "OverlayColor" + OpMemberName %type__Globals 17 "WhiteTemp" + OpMemberName %type__Globals 18 "WhiteTint" + OpMemberName %type__Globals 19 "ColorSaturation" + OpMemberName %type__Globals 20 "ColorContrast" + OpMemberName %type__Globals 21 "ColorGamma" + OpMemberName %type__Globals 22 "ColorGain" + OpMemberName %type__Globals 23 "ColorOffset" + OpMemberName %type__Globals 24 "ColorSaturationShadows" + OpMemberName %type__Globals 25 "ColorContrastShadows" + OpMemberName %type__Globals 26 "ColorGammaShadows" + OpMemberName %type__Globals 27 "ColorGainShadows" + OpMemberName %type__Globals 28 "ColorOffsetShadows" + OpMemberName %type__Globals 29 "ColorSaturationMidtones" + OpMemberName %type__Globals 30 "ColorContrastMidtones" + OpMemberName %type__Globals 31 "ColorGammaMidtones" + OpMemberName %type__Globals 32 "ColorGainMidtones" + OpMemberName %type__Globals 33 "ColorOffsetMidtones" + OpMemberName %type__Globals 34 "ColorSaturationHighlights" + OpMemberName %type__Globals 35 "ColorContrastHighlights" + OpMemberName %type__Globals 36 "ColorGammaHighlights" + OpMemberName %type__Globals 37 "ColorGainHighlights" + OpMemberName %type__Globals 38 "ColorOffsetHighlights" + OpMemberName %type__Globals 39 "ColorCorrectionShadowsMax" + OpMemberName %type__Globals 40 "ColorCorrectionHighlightsMin" + OpMemberName %type__Globals 41 "OutputDevice" + OpMemberName %type__Globals 42 "OutputGamut" + OpMemberName %type__Globals 43 "BlueCorrection" + OpMemberName %type__Globals 44 "ExpandGamut" + OpName %_Globals "$Globals" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPS "MainPS" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorate %in_var_TEXCOORD0 NoPerspective + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_POSITION" + OpDecorate %gl_Layer BuiltIn Layer + OpDecorateString %gl_Layer UserSemantic "SV_RenderTargetArrayIndex" + OpDecorate %gl_Layer Flat + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 32 + OpMemberDecorate %type__Globals 3 Offset 48 + OpMemberDecorate %type__Globals 4 Offset 64 + OpMemberDecorate %type__Globals 5 Offset 80 + OpMemberDecorate %type__Globals 6 Offset 96 + OpMemberDecorate %type__Globals 7 Offset 112 + OpMemberDecorate %type__Globals 8 Offset 128 + OpMemberDecorate %type__Globals 9 Offset 144 + OpMemberDecorate %type__Globals 10 Offset 160 + OpMemberDecorate %type__Globals 11 Offset 164 + OpMemberDecorate %type__Globals 12 Offset 168 + OpMemberDecorate %type__Globals 13 Offset 172 + OpMemberDecorate %type__Globals 14 Offset 176 + OpMemberDecorate %type__Globals 15 Offset 180 + OpMemberDecorate %type__Globals 16 Offset 192 + OpMemberDecorate %type__Globals 17 Offset 208 + OpMemberDecorate %type__Globals 18 Offset 212 + OpMemberDecorate %type__Globals 19 Offset 224 + OpMemberDecorate %type__Globals 20 Offset 240 + OpMemberDecorate %type__Globals 21 Offset 256 + OpMemberDecorate %type__Globals 22 Offset 272 + OpMemberDecorate %type__Globals 23 Offset 288 + OpMemberDecorate %type__Globals 24 Offset 304 + OpMemberDecorate %type__Globals 25 Offset 320 + OpMemberDecorate %type__Globals 26 Offset 336 + OpMemberDecorate %type__Globals 27 Offset 352 + OpMemberDecorate %type__Globals 28 Offset 368 + OpMemberDecorate %type__Globals 29 Offset 384 + OpMemberDecorate %type__Globals 30 Offset 400 + OpMemberDecorate %type__Globals 31 Offset 416 + OpMemberDecorate %type__Globals 32 Offset 432 + OpMemberDecorate %type__Globals 33 Offset 448 + OpMemberDecorate %type__Globals 34 Offset 464 + OpMemberDecorate %type__Globals 35 Offset 480 + OpMemberDecorate %type__Globals 36 Offset 496 + OpMemberDecorate %type__Globals 37 Offset 512 + OpMemberDecorate %type__Globals 38 Offset 528 + OpMemberDecorate %type__Globals 39 Offset 544 + OpMemberDecorate %type__Globals 40 Offset 548 + OpMemberDecorate %type__Globals 41 Offset 552 + OpMemberDecorate %type__Globals 42 Offset 556 + OpMemberDecorate %type__Globals 43 Offset 560 + OpMemberDecorate %type__Globals 44 Offset 564 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 +%float_0_952552378 = OpConstant %float 0.952552378 + %float_0 = OpConstant %float 0 + +; HACK: Needed to hack this constant since MSVC and GNU libc are off by 1 ULP when converting to string (it probably still works fine though in a roundtrip ...) +%float_9_36786018en05 = OpConstant %float 9.25 + +%float_0_343966454 = OpConstant %float 0.343966454 +%float_0_728166103 = OpConstant %float 0.728166103 +%float_n0_0721325427 = OpConstant %float -0.0721325427 +%float_1_00882518 = OpConstant %float 1.00882518 +%float_1_04981101 = OpConstant %float 1.04981101 +%float_n9_74845025en05 = OpConstant %float -9.74845025e-05 +%float_n0_495903015 = OpConstant %float -0.495903015 +%float_1_37331307 = OpConstant %float 1.37331307 +%float_0_0982400328 = OpConstant %float 0.0982400328 +%float_0_991252005 = OpConstant %float 0.991252005 +%float_0_662454188 = OpConstant %float 0.662454188 +%float_0_134004205 = OpConstant %float 0.134004205 +%float_0_156187683 = OpConstant %float 0.156187683 +%float_0_272228718 = OpConstant %float 0.272228718 +%float_0_674081743 = OpConstant %float 0.674081743 +%float_0_0536895171 = OpConstant %float 0.0536895171 +%float_n0_00557464967 = OpConstant %float -0.00557464967 +%float_0_0040607336 = OpConstant %float 0.0040607336 +%float_1_01033914 = OpConstant %float 1.01033914 +%float_1_6410234 = OpConstant %float 1.6410234 +%float_n0_324803293 = OpConstant %float -0.324803293 +%float_n0_236424699 = OpConstant %float -0.236424699 +%float_n0_663662851 = OpConstant %float -0.663662851 +%float_1_61533165 = OpConstant %float 1.61533165 +%float_0_0167563483 = OpConstant %float 0.0167563483 +%float_0_0117218941 = OpConstant %float 0.0117218941 +%float_n0_00828444213 = OpConstant %float -0.00828444213 +%float_0_988394856 = OpConstant %float 0.988394856 +%float_1_45143926 = OpConstant %float 1.45143926 +%float_n0_236510754 = OpConstant %float -0.236510754 +%float_n0_214928567 = OpConstant %float -0.214928567 +%float_n0_0765537769 = OpConstant %float -0.0765537769 +%float_1_17622972 = OpConstant %float 1.17622972 +%float_n0_0996759236 = OpConstant %float -0.0996759236 +%float_0_00831614807 = OpConstant %float 0.00831614807 +%float_n0_00603244966 = OpConstant %float -0.00603244966 +%float_0_997716308 = OpConstant %float 0.997716308 +%float_0_695452213 = OpConstant %float 0.695452213 +%float_0_140678704 = OpConstant %float 0.140678704 +%float_0_163869068 = OpConstant %float 0.163869068 +%float_0_0447945632 = OpConstant %float 0.0447945632 +%float_0_859671116 = OpConstant %float 0.859671116 +%float_0_0955343172 = OpConstant %float 0.0955343172 +%float_n0_00552588282 = OpConstant %float -0.00552588282 +%float_0_00402521016 = OpConstant %float 0.00402521016 +%float_1_00150073 = OpConstant %float 1.00150073 + %67 = OpConstantComposite %v3float %float_0_272228718 %float_0_674081743 %float_0_0536895171 +%float_3_2409699 = OpConstant %float 3.2409699 +%float_n1_5373832 = OpConstant %float -1.5373832 +%float_n0_498610765 = OpConstant %float -0.498610765 +%float_n0_969243646 = OpConstant %float -0.969243646 +%float_1_8759675 = OpConstant %float 1.8759675 +%float_0_0415550582 = OpConstant %float 0.0415550582 +%float_0_0556300804 = OpConstant %float 0.0556300804 +%float_n0_203976959 = OpConstant %float -0.203976959 +%float_1_05697155 = OpConstant %float 1.05697155 +%float_0_412456393 = OpConstant %float 0.412456393 +%float_0_357576102 = OpConstant %float 0.357576102 +%float_0_180437505 = OpConstant %float 0.180437505 +%float_0_212672904 = OpConstant %float 0.212672904 +%float_0_715152204 = OpConstant %float 0.715152204 +%float_0_0721750036 = OpConstant %float 0.0721750036 +%float_0_0193339009 = OpConstant %float 0.0193339009 +%float_0_119191997 = OpConstant %float 0.119191997 +%float_0_950304091 = OpConstant %float 0.950304091 +%float_1_71660841 = OpConstant %float 1.71660841 +%float_n0_355662107 = OpConstant %float -0.355662107 +%float_n0_253360093 = OpConstant %float -0.253360093 +%float_n0_666682899 = OpConstant %float -0.666682899 +%float_1_61647761 = OpConstant %float 1.61647761 +%float_0_0157685 = OpConstant %float 0.0157685 +%float_0_0176422 = OpConstant %float 0.0176422 +%float_n0_0427763015 = OpConstant %float -0.0427763015 +%float_0_942228675 = OpConstant %float 0.942228675 +%float_2_49339628 = OpConstant %float 2.49339628 +%float_n0_93134588 = OpConstant %float -0.93134588 +%float_n0_402694494 = OpConstant %float -0.402694494 +%float_n0_829486787 = OpConstant %float -0.829486787 +%float_1_76265967 = OpConstant %float 1.76265967 +%float_0_0236246008 = OpConstant %float 0.0236246008 +%float_0_0358507 = OpConstant %float 0.0358507 +%float_n0_0761827007 = OpConstant %float -0.0761827007 +%float_0_957014024 = OpConstant %float 0.957014024 +%float_1_01303005 = OpConstant %float 1.01303005 +%float_0_00610530982 = OpConstant %float 0.00610530982 +%float_n0_0149710001 = OpConstant %float -0.0149710001 +%float_0_00769822998 = OpConstant %float 0.00769822998 +%float_0_998165011 = OpConstant %float 0.998165011 +%float_n0_00503202993 = OpConstant %float -0.00503202993 +%float_n0_00284131011 = OpConstant %float -0.00284131011 +%float_0_00468515977 = OpConstant %float 0.00468515977 +%float_0_924507022 = OpConstant %float 0.924507022 +%float_0_987223983 = OpConstant %float 0.987223983 +%float_n0_00611326983 = OpConstant %float -0.00611326983 +%float_0_0159533005 = OpConstant %float 0.0159533005 +%float_n0_00759836007 = OpConstant %float -0.00759836007 +%float_1_00186002 = OpConstant %float 1.00186002 +%float_0_0053300201 = OpConstant %float 0.0053300201 +%float_0_00307257008 = OpConstant %float 0.00307257008 +%float_n0_00509594986 = OpConstant %float -0.00509594986 +%float_1_08168006 = OpConstant %float 1.08168006 + %float_0_5 = OpConstant %float 0.5 + %float_n1 = OpConstant %float -1 + %float_1 = OpConstant %float 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%float_0_015625 = OpConstant %float 0.015625 + %128 = OpConstantComposite %v2float %float_0_015625 %float_0_015625 + %129 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int_42 = OpConstant %int 42 + %uint_3 = OpConstant %uint 3 + %132 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %int_9 = OpConstant %int 9 + %int_3 = OpConstant %int 3 + %135 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %float_n4 = OpConstant %float -4 + %int_44 = OpConstant %int 44 +%float_0_544169128 = OpConstant %float 0.544169128 +%float_0_239592597 = OpConstant %float 0.239592597 +%float_0_166694298 = OpConstant %float 0.166694298 +%float_0_239465594 = OpConstant %float 0.239465594 +%float_0_702153027 = OpConstant %float 0.702153027 +%float_0_058381401 = OpConstant %float 0.058381401 +%float_n0_00234390004 = OpConstant %float -0.00234390004 +%float_0_0361833982 = OpConstant %float 0.0361833982 +%float_1_05521834 = OpConstant %float 1.05521834 +%float_0_940437257 = OpConstant %float 0.940437257 +%float_n0_0183068793 = OpConstant %float -0.0183068793 +%float_0_077869609 = OpConstant %float 0.077869609 +%float_0_00837869663 = OpConstant %float 0.00837869663 +%float_0_828660011 = OpConstant %float 0.828660011 +%float_0_162961304 = OpConstant %float 0.162961304 +%float_0_00054712611 = OpConstant %float 0.00054712611 +%float_n0_000883374596 = OpConstant %float -0.000883374596 +%float_1_00033629 = OpConstant %float 1.00033629 +%float_1_06317997 = OpConstant %float 1.06317997 +%float_0_0233955998 = OpConstant %float 0.0233955998 +%float_n0_0865726024 = OpConstant %float -0.0865726024 +%float_n0_0106336996 = OpConstant %float -0.0106336996 +%float_1_20632005 = OpConstant %float 1.20632005 +%float_n0_195690006 = OpConstant %float -0.195690006 +%float_n0_000590886979 = OpConstant %float -0.000590886979 +%float_0_00105247996 = OpConstant %float 0.00105247996 +%float_0_999538004 = OpConstant %float 0.999538004 + %int_43 = OpConstant %int 43 + %int_15 = OpConstant %int 15 + %int_16 = OpConstant %int 16 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_5 = OpConstant %uint 5 + %uint_6 = OpConstant %uint 6 + %int_2 = OpConstant %int 2 +%mat3v3float = OpTypeMatrix %v3float 3 + %int_41 = OpConstant %int 41 +%float_0_159301758 = OpConstant %float 0.159301758 +%float_78_84375 = OpConstant %float 78.84375 +%float_0_8359375 = OpConstant %float 0.8359375 +%float_18_8515625 = OpConstant %float 18.8515625 +%float_18_6875 = OpConstant %float 18.6875 +%float_10000 = OpConstant %float 10000 +%float_0_0126833133 = OpConstant %float 0.0126833133 + %182 = OpConstantComposite %v3float %float_0_0126833133 %float_0_0126833133 %float_0_0126833133 + %183 = OpConstantComposite %v3float %float_0_8359375 %float_0_8359375 %float_0_8359375 + %184 = OpConstantComposite %v3float %float_18_8515625 %float_18_8515625 %float_18_8515625 + %185 = OpConstantComposite %v3float %float_18_6875 %float_18_6875 %float_18_6875 +%float_6_27739477 = OpConstant %float 6.27739477 + %187 = OpConstantComposite %v3float %float_6_27739477 %float_6_27739477 %float_6_27739477 + %188 = OpConstantComposite %v3float %float_10000 %float_10000 %float_10000 + %float_14 = OpConstant %float 14 +%float_0_180000007 = OpConstant %float 0.180000007 +%float_0_434017599 = OpConstant %float 0.434017599 + %192 = OpConstantComposite %v3float %float_0_434017599 %float_0_434017599 %float_0_434017599 + %193 = OpConstantComposite %v3float %float_14 %float_14 %float_14 + %194 = OpConstantComposite %v3float %float_0_180000007 %float_0_180000007 %float_0_180000007 + %int_17 = OpConstant %int 17 + %float_4000 = OpConstant %float 4000 +%float_0_312700003 = OpConstant %float 0.312700003 +%float_0_328999996 = OpConstant %float 0.328999996 + %int_18 = OpConstant %int 18 + %int_24 = OpConstant %int 24 + %int_19 = OpConstant %int 19 + %int_25 = OpConstant %int 25 + %int_20 = OpConstant %int 20 + %int_26 = OpConstant %int 26 + %int_21 = OpConstant %int 21 + %int_27 = OpConstant %int 27 + %int_22 = OpConstant %int 22 + %int_28 = OpConstant %int 28 + %int_23 = OpConstant %int 23 + %int_39 = OpConstant %int 39 + %int_34 = OpConstant %int 34 + %int_35 = OpConstant %int 35 + %int_36 = OpConstant %int 36 + %int_37 = OpConstant %int 37 + %int_38 = OpConstant %int 38 + %int_40 = OpConstant %int 40 + %int_29 = OpConstant %int 29 + %int_30 = OpConstant %int 30 + %int_31 = OpConstant %int 31 + %int_32 = OpConstant %int 32 + %int_33 = OpConstant %int 33 +%float_0_0500000007 = OpConstant %float 0.0500000007 + %float_1_75 = OpConstant %float 1.75 +%float_0_400000006 = OpConstant %float 0.400000006 +%float_0_0299999993 = OpConstant %float 0.0299999993 + %float_2 = OpConstant %float 2 +%float_0_959999979 = OpConstant %float 0.959999979 + %228 = OpConstantComposite %v3float %float_0_959999979 %float_0_959999979 %float_0_959999979 + %int_13 = OpConstant %int 13 + %int_11 = OpConstant %int 11 + %int_14 = OpConstant %int 14 + %int_12 = OpConstant %int 12 +%float_0_800000012 = OpConstant %float 0.800000012 + %int_10 = OpConstant %int 10 + %float_10 = OpConstant %float 10 + %float_n2 = OpConstant %float -2 + %float_3 = OpConstant %float 3 + %238 = OpConstantComposite %v3float %float_3 %float_3 %float_3 + %239 = OpConstantComposite %v3float %float_2 %float_2 %float_2 +%float_0_930000007 = OpConstant %float 0.930000007 + %241 = OpConstantComposite %v3float %float_0_930000007 %float_0_930000007 %float_0_930000007 + %int_4 = OpConstant %int 4 + %int_8 = OpConstant %int 8 + %int_7 = OpConstant %int 7 + %int_5 = OpConstant %int 5 + %int_6 = OpConstant %int 6 +%float_0_00200000009 = OpConstant %float 0.00200000009 + %248 = OpConstantComposite %v3float %float_0_00200000009 %float_0_00200000009 %float_0_00200000009 +%float_6_10351999en05 = OpConstant %float 6.10351999e-05 + %250 = OpConstantComposite %v3float %float_6_10351999en05 %float_6_10351999en05 %float_6_10351999en05 + %float_4_5 = OpConstant %float 4.5 + %252 = OpConstantComposite %v3float %float_4_5 %float_4_5 %float_4_5 +%float_0_0179999992 = OpConstant %float 0.0179999992 + %254 = OpConstantComposite %v3float %float_0_0179999992 %float_0_0179999992 %float_0_0179999992 +%float_0_449999988 = OpConstant %float 0.449999988 + %256 = OpConstantComposite %v3float %float_0_449999988 %float_0_449999988 %float_0_449999988 +%float_1_09899998 = OpConstant %float 1.09899998 + %258 = OpConstantComposite %v3float %float_1_09899998 %float_1_09899998 %float_1_09899998 +%float_0_0989999995 = OpConstant %float 0.0989999995 + %260 = OpConstantComposite %v3float %float_0_0989999995 %float_0_0989999995 %float_0_0989999995 + %float_1_5 = OpConstant %float 1.5 + %262 = OpConstantComposite %v3float %float_1_5 %float_1_5 %float_1_5 + %263 = OpConstantComposite %v3float %float_0_159301758 %float_0_159301758 %float_0_159301758 + %264 = OpConstantComposite %v3float %float_78_84375 %float_78_84375 %float_78_84375 +%float_1_00055635 = OpConstant %float 1.00055635 + %float_7000 = OpConstant %float 7000 +%float_0_244063005 = OpConstant %float 0.244063005 +%float_99_1100006 = OpConstant %float 99.1100006 +%float_2967800 = OpConstant %float 2967800 +%float_0_237039998 = OpConstant %float 0.237039998 +%float_247_479996 = OpConstant %float 247.479996 +%float_1901800 = OpConstant %float 1901800 + %float_n3 = OpConstant %float -3 +%float_2_86999989 = OpConstant %float 2.86999989 +%float_0_275000006 = OpConstant %float 0.275000006 +%float_0_860117733 = OpConstant %float 0.860117733 +%float_0_000154118257 = OpConstant %float 0.000154118257 +%float_1_28641219en07 = OpConstant %float 1.28641219e-07 +%float_0_00084242021 = OpConstant %float 0.00084242021 +%float_7_08145137en07 = OpConstant %float 7.08145137e-07 +%float_0_317398727 = OpConstant %float 0.317398727 + +; HACK: Needed to hack this constant since MSVC and GNU libc are off by 1 ULP when converting to string (it probably still works fine though in a roundtrip ...) +%float_4_22806261en05 = OpConstant %float 4.25 + +%float_4_20481676en08 = OpConstant %float 4.20481676e-08 +%float_2_8974182en05 = OpConstant %float 2.8974182e-05 +%float_1_61456057en07 = OpConstant %float 1.61456057e-07 + %float_8 = OpConstant %float 8 + %float_4 = OpConstant %float 4 +%float_0_895099998 = OpConstant %float 0.895099998 +%float_0_266400009 = OpConstant %float 0.266400009 +%float_n0_161400005 = OpConstant %float -0.161400005 +%float_n0_750199974 = OpConstant %float -0.750199974 +%float_1_71350002 = OpConstant %float 1.71350002 +%float_0_0366999991 = OpConstant %float 0.0366999991 +%float_0_0388999991 = OpConstant %float 0.0388999991 +%float_n0_0684999973 = OpConstant %float -0.0684999973 +%float_1_02960002 = OpConstant %float 1.02960002 +%float_0_986992896 = OpConstant %float 0.986992896 +%float_n0_1470543 = OpConstant %float -0.1470543 +%float_0_159962699 = OpConstant %float 0.159962699 +%float_0_432305306 = OpConstant %float 0.432305306 +%float_0_518360317 = OpConstant %float 0.518360317 +%float_0_0492912009 = OpConstant %float 0.0492912009 +%float_n0_0085287001 = OpConstant %float -0.0085287001 +%float_0_040042799 = OpConstant %float 0.040042799 +%float_0_968486726 = OpConstant %float 0.968486726 +%float_5_55555534 = OpConstant %float 5.55555534 + %307 = OpConstantComposite %v3float %float_5_55555534 %float_5_55555534 %float_5_55555534 +%float_1_00000001en10 = OpConstant %float 1.00000001e-10 +%float_0_00999999978 = OpConstant %float 0.00999999978 +%float_0_666666687 = OpConstant %float 0.666666687 + %float_180 = OpConstant %float 180 + %float_360 = OpConstant %float 360 +%float_65535 = OpConstant %float 65535 + %314 = OpConstantComposite %v3float %float_65535 %float_65535 %float_65535 +%float_n4_97062206 = OpConstant %float -4.97062206 +%float_n3_02937818 = OpConstant %float -3.02937818 +%float_n2_12619996 = OpConstant %float -2.12619996 +%float_n1_51049995 = OpConstant %float -1.51049995 +%float_n1_05780005 = OpConstant %float -1.05780005 +%float_n0_466800004 = OpConstant %float -0.466800004 +%float_0_119379997 = OpConstant %float 0.119379997 +%float_0_708813429 = OpConstant %float 0.708813429 +%float_1_29118657 = OpConstant %float 1.29118657 +%float_0_808913231 = OpConstant %float 0.808913231 +%float_1_19108677 = OpConstant %float 1.19108677 +%float_1_56830001 = OpConstant %float 1.56830001 +%float_1_9483 = OpConstant %float 1.9483 +%float_2_30830002 = OpConstant %float 2.30830002 +%float_2_63840008 = OpConstant %float 2.63840008 +%float_2_85949993 = OpConstant %float 2.85949993 +%float_2_98726082 = OpConstant %float 2.98726082 +%float_3_01273918 = OpConstant %float 3.01273918 +%float_0_179999992 = OpConstant %float 0.179999992 +%float_9_99999975en05 = OpConstant %float 9.99999975e-05 + %float_1000 = OpConstant %float 1000 +%float_0_0599999987 = OpConstant %float 0.0599999987 +%float_3_50738446en05 = OpConstant %float 3.50738446e-05 + %338 = OpConstantComposite %v3float %float_3_50738446en05 %float_3_50738446en05 %float_3_50738446en05 +%float_n2_30102992 = OpConstant %float -2.30102992 +%float_n1_93120003 = OpConstant %float -1.93120003 +%float_n1_52049994 = OpConstant %float -1.52049994 +%float_0_801995218 = OpConstant %float 0.801995218 +%float_1_19800484 = OpConstant %float 1.19800484 +%float_1_59430003 = OpConstant %float 1.59430003 +%float_1_99730003 = OpConstant %float 1.99730003 +%float_2_37829995 = OpConstant %float 2.37829995 +%float_2_76839995 = OpConstant %float 2.76839995 +%float_3_05150008 = OpConstant %float 3.05150008 +%float_3_27462935 = OpConstant %float 3.27462935 +%float_3_32743073 = OpConstant %float 3.32743073 +%float_0_00499999989 = OpConstant %float 0.00499999989 + %float_11 = OpConstant %float 11 + %float_2000 = OpConstant %float 2000 +%float_0_119999997 = OpConstant %float 0.119999997 +%float_0_00313066994 = OpConstant %float 0.00313066994 +%float_12_9200001 = OpConstant %float 12.9200001 +%float_0_416666657 = OpConstant %float 0.416666657 +%float_1_05499995 = OpConstant %float 1.05499995 +%float_0_0549999997 = OpConstant %float 0.0549999997 +%float_n0_166666672 = OpConstant %float -0.166666672 + %float_n0_5 = OpConstant %float -0.5 +%float_0_166666672 = OpConstant %float 0.166666672 +%float_n3_15737653 = OpConstant %float -3.15737653 +%float_n0_485249996 = OpConstant %float -0.485249996 +%float_1_84773242 = OpConstant %float 1.84773242 +%float_n0_718548238 = OpConstant %float -0.718548238 +%float_2_08103061 = OpConstant %float 2.08103061 +%float_3_6681242 = OpConstant %float 3.6681242 + %float_18 = OpConstant %float 18 + %float_7 = OpConstant %float 7 +%type__Globals = OpTypeStruct %v4float %v3float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %float %float %float %float %float %v3float %v4float %float %float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %float %float %uint %uint %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %377 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %bool = OpTypeBool +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %v2bool = OpTypeVector %bool 2 + %v3bool = OpTypeVector %bool 3 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_arr_float_uint_6 = OpTypeArray %float %uint_6 + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %gl_Layer = OpVariable %_ptr_Input_uint Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function__arr_float_uint_6 = OpTypePointer Function %_arr_float_uint_6 +%_ptr_Function__arr_float_uint_10 = OpTypePointer Function %_arr_float_uint_10 + %391 = OpUndef %v3float + %392 = OpConstantComposite %v3float %float_0_952552378 %float_0 %float_9_36786018en05 + %393 = OpConstantComposite %v3float %float_0_343966454 %float_0_728166103 %float_n0_0721325427 + %394 = OpConstantComposite %v3float %float_0 %float_0 %float_1_00882518 + %395 = OpConstantComposite %mat3v3float %392 %393 %394 + %396 = OpConstantComposite %v3float %float_1_04981101 %float_0 %float_n9_74845025en05 + %397 = OpConstantComposite %v3float %float_n0_495903015 %float_1_37331307 %float_0_0982400328 + %398 = OpConstantComposite %v3float %float_0 %float_0 %float_0_991252005 + %399 = OpConstantComposite %mat3v3float %396 %397 %398 + %400 = OpConstantComposite %v3float %float_0_662454188 %float_0_134004205 %float_0_156187683 + %401 = OpConstantComposite %v3float %float_n0_00557464967 %float_0_0040607336 %float_1_01033914 + %402 = OpConstantComposite %mat3v3float %400 %67 %401 + %403 = OpConstantComposite %v3float %float_1_6410234 %float_n0_324803293 %float_n0_236424699 + %404 = OpConstantComposite %v3float %float_n0_663662851 %float_1_61533165 %float_0_0167563483 + %405 = OpConstantComposite %v3float %float_0_0117218941 %float_n0_00828444213 %float_0_988394856 + %406 = OpConstantComposite %mat3v3float %403 %404 %405 + %407 = OpConstantComposite %v3float %float_1_45143926 %float_n0_236510754 %float_n0_214928567 + %408 = OpConstantComposite %v3float %float_n0_0765537769 %float_1_17622972 %float_n0_0996759236 + %409 = OpConstantComposite %v3float %float_0_00831614807 %float_n0_00603244966 %float_0_997716308 + %410 = OpConstantComposite %mat3v3float %407 %408 %409 + %411 = OpConstantComposite %v3float %float_0_695452213 %float_0_140678704 %float_0_163869068 + %412 = OpConstantComposite %v3float %float_0_0447945632 %float_0_859671116 %float_0_0955343172 + %413 = OpConstantComposite %v3float %float_n0_00552588282 %float_0_00402521016 %float_1_00150073 + %414 = OpConstantComposite %mat3v3float %411 %412 %413 + %415 = OpConstantComposite %v3float %float_3_2409699 %float_n1_5373832 %float_n0_498610765 + %416 = OpConstantComposite %v3float %float_n0_969243646 %float_1_8759675 %float_0_0415550582 + %417 = OpConstantComposite %v3float %float_0_0556300804 %float_n0_203976959 %float_1_05697155 + %418 = OpConstantComposite %mat3v3float %415 %416 %417 + %419 = OpConstantComposite %v3float %float_0_412456393 %float_0_357576102 %float_0_180437505 + %420 = OpConstantComposite %v3float %float_0_212672904 %float_0_715152204 %float_0_0721750036 + %421 = OpConstantComposite %v3float %float_0_0193339009 %float_0_119191997 %float_0_950304091 + %422 = OpConstantComposite %mat3v3float %419 %420 %421 + %423 = OpConstantComposite %v3float %float_1_71660841 %float_n0_355662107 %float_n0_253360093 + %424 = OpConstantComposite %v3float %float_n0_666682899 %float_1_61647761 %float_0_0157685 + %425 = OpConstantComposite %v3float %float_0_0176422 %float_n0_0427763015 %float_0_942228675 + %426 = OpConstantComposite %mat3v3float %423 %424 %425 + %427 = OpConstantComposite %v3float %float_2_49339628 %float_n0_93134588 %float_n0_402694494 + %428 = OpConstantComposite %v3float %float_n0_829486787 %float_1_76265967 %float_0_0236246008 + %429 = OpConstantComposite %v3float %float_0_0358507 %float_n0_0761827007 %float_0_957014024 + %430 = OpConstantComposite %mat3v3float %427 %428 %429 + %431 = OpConstantComposite %v3float %float_1_01303005 %float_0_00610530982 %float_n0_0149710001 + %432 = OpConstantComposite %v3float %float_0_00769822998 %float_0_998165011 %float_n0_00503202993 + %433 = OpConstantComposite %v3float %float_n0_00284131011 %float_0_00468515977 %float_0_924507022 + %434 = OpConstantComposite %mat3v3float %431 %432 %433 + %435 = OpConstantComposite %v3float %float_0_987223983 %float_n0_00611326983 %float_0_0159533005 + %436 = OpConstantComposite %v3float %float_n0_00759836007 %float_1_00186002 %float_0_0053300201 + %437 = OpConstantComposite %v3float %float_0_00307257008 %float_n0_00509594986 %float_1_08168006 + %438 = OpConstantComposite %mat3v3float %435 %436 %437 + %439 = OpConstantComposite %v3float %float_0_5 %float_n1 %float_0_5 + %440 = OpConstantComposite %v3float %float_n1 %float_1 %float_0_5 + %441 = OpConstantComposite %v3float %float_0_5 %float_0 %float_0 + %442 = OpConstantComposite %mat3v3float %439 %440 %441 + %443 = OpConstantComposite %v3float %float_1 %float_0 %float_0 + %444 = OpConstantComposite %v3float %float_0 %float_1 %float_0 + %445 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %446 = OpConstantComposite %mat3v3float %443 %444 %445 +%float_n6_07624626 = OpConstant %float -6.07624626 + %448 = OpConstantComposite %v3float %float_n6_07624626 %float_n6_07624626 %float_n6_07624626 + %449 = OpConstantComposite %v3float %float_0_895099998 %float_0_266400009 %float_n0_161400005 + %450 = OpConstantComposite %v3float %float_n0_750199974 %float_1_71350002 %float_0_0366999991 + %451 = OpConstantComposite %v3float %float_0_0388999991 %float_n0_0684999973 %float_1_02960002 + %452 = OpConstantComposite %mat3v3float %449 %450 %451 + %453 = OpConstantComposite %v3float %float_0_986992896 %float_n0_1470543 %float_0_159962699 + %454 = OpConstantComposite %v3float %float_0_432305306 %float_0_518360317 %float_0_0492912009 + %455 = OpConstantComposite %v3float %float_n0_0085287001 %float_0_040042799 %float_0_968486726 + %456 = OpConstantComposite %mat3v3float %453 %454 %455 +%float_0_358299971 = OpConstant %float 0.358299971 + %458 = OpConstantComposite %v3float %float_0_544169128 %float_0_239592597 %float_0_166694298 + %459 = OpConstantComposite %v3float %float_0_239465594 %float_0_702153027 %float_0_058381401 + %460 = OpConstantComposite %v3float %float_n0_00234390004 %float_0_0361833982 %float_1_05521834 + %461 = OpConstantComposite %mat3v3float %458 %459 %460 + %462 = OpConstantComposite %v3float %float_0_940437257 %float_n0_0183068793 %float_0_077869609 + %463 = OpConstantComposite %v3float %float_0_00837869663 %float_0_828660011 %float_0_162961304 + %464 = OpConstantComposite %v3float %float_0_00054712611 %float_n0_000883374596 %float_1_00033629 + %465 = OpConstantComposite %mat3v3float %462 %463 %464 + %466 = OpConstantComposite %v3float %float_1_06317997 %float_0_0233955998 %float_n0_0865726024 + %467 = OpConstantComposite %v3float %float_n0_0106336996 %float_1_20632005 %float_n0_195690006 + %468 = OpConstantComposite %v3float %float_n0_000590886979 %float_0_00105247996 %float_0_999538004 + %469 = OpConstantComposite %mat3v3float %466 %467 %468 +%float_0_0533333346 = OpConstant %float 0.0533333346 +%float_0_159999996 = OpConstant %float 0.159999996 +%float_57_2957764 = OpConstant %float 57.2957764 +%float_n67_5 = OpConstant %float -67.5 + %float_67_5 = OpConstant %float 67.5 + %475 = OpConstantComposite %_arr_float_uint_6 %float_n4 %float_n4 %float_n3_15737653 %float_n0_485249996 %float_1_84773242 %float_1_84773242 + %476 = OpConstantComposite %_arr_float_uint_6 %float_n0_718548238 %float_2_08103061 %float_3_6681242 %float_4 %float_4 %float_4 + %float_n15 = OpConstant %float -15 + %float_n14 = OpConstant %float -14 + %479 = OpConstantComposite %_arr_float_uint_10 %float_n4_97062206 %float_n3_02937818 %float_n2_12619996 %float_n1_51049995 %float_n1_05780005 %float_n0_466800004 %float_0_119379997 %float_0_708813429 %float_1_29118657 %float_1_29118657 + %480 = OpConstantComposite %_arr_float_uint_10 %float_0_808913231 %float_1_19108677 %float_1_56830001 %float_1_9483 %float_2_30830002 %float_2_63840008 %float_2_85949993 %float_2_98726082 %float_3_01273918 %float_3_01273918 + %float_n12 = OpConstant %float -12 + %482 = OpConstantComposite %_arr_float_uint_10 %float_n2_30102992 %float_n2_30102992 %float_n1_93120003 %float_n1_52049994 %float_n1_05780005 %float_n0_466800004 %float_0_119379997 %float_0_708813429 %float_1_29118657 %float_1_29118657 + %483 = OpConstantComposite %_arr_float_uint_10 %float_0_801995218 %float_1_19800484 %float_1_59430003 %float_1_99730003 %float_2_37829995 %float_2_76839995 %float_3_05150008 %float_3_27462935 %float_3_32743073 %float_3_32743073 +%float_0_0322580636 = OpConstant %float 0.0322580636 +%float_1_03225803 = OpConstant %float 1.03225803 + %486 = OpConstantComposite %v2float %float_1_03225803 %float_1_03225803 +%float_4_60443853e_09 = OpConstant %float 4.60443853e+09 +%float_2_00528435e_09 = OpConstant %float 2.00528435e+09 +%float_0_333333343 = OpConstant %float 0.333333343 + %float_5 = OpConstant %float 5 + %float_2_5 = OpConstant %float 2.5 +%float_0_0250000004 = OpConstant %float 0.0250000004 +%float_0_239999995 = OpConstant %float 0.239999995 +%float_0_0148148146 = OpConstant %float 0.0148148146 +%float_0_819999993 = OpConstant %float 0.819999993 + %496 = OpConstantComposite %v3float %float_9_99999975en05 %float_9_99999975en05 %float_9_99999975en05 +%float_0_0296296291 = OpConstant %float 0.0296296291 +%float_0_952381015 = OpConstant %float 0.952381015 + %499 = OpConstantComposite %v3float %float_0_952381015 %float_0_952381015 %float_0_952381015 + %MainPS = OpFunction %void None %377 + %500 = OpLabel + %501 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %502 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %503 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %504 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %505 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %506 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %507 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %508 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %509 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %510 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %511 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %512 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %513 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %514 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %515 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %516 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %517 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %518 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %519 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %520 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %521 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %522 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %523 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %524 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %525 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %526 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %527 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %528 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %529 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %530 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %531 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %532 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %533 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %534 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %535 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %536 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %537 = OpLoad %v2float %in_var_TEXCOORD0 + %538 = OpLoad %uint %gl_Layer + %539 = OpFSub %v2float %537 %128 + %540 = OpFMul %v2float %539 %486 + %541 = OpCompositeExtract %float %540 0 + %542 = OpCompositeExtract %float %540 1 + %543 = OpConvertUToF %float %538 + %544 = OpFMul %float %543 %float_0_0322580636 + %545 = OpCompositeConstruct %v4float %541 %542 %544 %float_0 + %546 = OpMatrixTimesMatrix %mat3v3float %422 %434 + %547 = OpMatrixTimesMatrix %mat3v3float %546 %406 + %548 = OpMatrixTimesMatrix %mat3v3float %402 %438 + %549 = OpMatrixTimesMatrix %mat3v3float %548 %418 + %550 = OpMatrixTimesMatrix %mat3v3float %395 %406 + %551 = OpMatrixTimesMatrix %mat3v3float %402 %399 + %552 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_42 + %553 = OpLoad %uint %552 + OpBranch %554 + %554 = OpLabel + OpLoopMerge %555 %556 None + OpBranch %557 + %557 = OpLabel + %558 = OpMatrixTimesMatrix %mat3v3float %548 %430 + %559 = OpMatrixTimesMatrix %mat3v3float %548 %426 + %560 = OpIEqual %bool %553 %uint_1 + OpSelectionMerge %561 None + OpBranchConditional %560 %562 %563 + %563 = OpLabel + %564 = OpIEqual %bool %553 %uint_2 + OpSelectionMerge %565 None + OpBranchConditional %564 %566 %567 + %567 = OpLabel + %568 = OpIEqual %bool %553 %uint_3 + OpSelectionMerge %569 None + OpBranchConditional %568 %570 %571 + %571 = OpLabel + %572 = OpIEqual %bool %553 %uint_4 + OpSelectionMerge %573 None + OpBranchConditional %572 %574 %575 + %575 = OpLabel + OpBranch %555 + %574 = OpLabel + OpBranch %555 + %573 = OpLabel + OpUnreachable + %570 = OpLabel + OpBranch %555 + %569 = OpLabel + OpUnreachable + %566 = OpLabel + OpBranch %555 + %565 = OpLabel + OpUnreachable + %562 = OpLabel + OpBranch %555 + %561 = OpLabel + OpUnreachable + %556 = OpLabel + OpBranch %554 + %555 = OpLabel + %576 = OpPhi %mat3v3float %549 %575 %446 %574 %414 %570 %559 %566 %558 %562 + %577 = OpVectorShuffle %v3float %545 %545 0 1 2 + %578 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_41 + %579 = OpLoad %uint %578 + %580 = OpUGreaterThanEqual %bool %579 %uint_3 + OpSelectionMerge %581 None + OpBranchConditional %580 %582 %583 + %583 = OpLabel + %584 = OpFSub %v3float %577 %192 + %585 = OpFMul %v3float %584 %193 + %586 = OpExtInst %v3float %1 Exp2 %585 + %587 = OpFMul %v3float %586 %194 + %588 = OpExtInst %v3float %1 Exp2 %448 + %589 = OpFMul %v3float %588 %194 + %590 = OpFSub %v3float %587 %589 + OpBranch %581 + %582 = OpLabel + %591 = OpExtInst %v3float %1 Pow %577 %182 + %592 = OpFSub %v3float %591 %183 + %593 = OpExtInst %v3float %1 FMax %132 %592 + %594 = OpFMul %v3float %185 %591 + %595 = OpFSub %v3float %184 %594 + %596 = OpFDiv %v3float %593 %595 + %597 = OpExtInst %v3float %1 Pow %596 %187 + %598 = OpFMul %v3float %597 %188 + OpBranch %581 + %581 = OpLabel + %599 = OpPhi %v3float %590 %583 %598 %582 + %600 = OpAccessChain %_ptr_Uniform_float %_Globals %int_17 + %601 = OpLoad %float %600 + %602 = OpFMul %float %601 %float_1_00055635 + %603 = OpFOrdLessThanEqual %bool %602 %float_7000 + %604 = OpFDiv %float %float_4_60443853e_09 %601 + %605 = OpFSub %float %float_2967800 %604 + %606 = OpFDiv %float %605 %602 + %607 = OpFAdd %float %float_99_1100006 %606 + %608 = OpFDiv %float %607 %602 + %609 = OpFAdd %float %float_0_244063005 %608 + %610 = OpFDiv %float %float_2_00528435e_09 %601 + %611 = OpFSub %float %float_1901800 %610 + %612 = OpFDiv %float %611 %602 + %613 = OpFAdd %float %float_247_479996 %612 + %614 = OpFDiv %float %613 %602 + %615 = OpFAdd %float %float_0_237039998 %614 + %616 = OpSelect %float %603 %609 %615 + %617 = OpFMul %float %float_n3 %616 + %618 = OpFMul %float %617 %616 + %619 = OpFMul %float %float_2_86999989 %616 + %620 = OpFAdd %float %618 %619 + %621 = OpFSub %float %620 %float_0_275000006 + %622 = OpCompositeConstruct %v2float %616 %621 + %623 = OpFMul %float %float_0_000154118257 %601 + %624 = OpFAdd %float %float_0_860117733 %623 + %625 = OpFMul %float %float_1_28641219en07 %601 + %626 = OpFMul %float %625 %601 + %627 = OpFAdd %float %624 %626 + %628 = OpFMul %float %float_0_00084242021 %601 + %629 = OpFAdd %float %float_1 %628 + %630 = OpFMul %float %float_7_08145137en07 %601 + %631 = OpFMul %float %630 %601 + %632 = OpFAdd %float %629 %631 + %633 = OpFDiv %float %627 %632 + %634 = OpFMul %float %float_4_22806261en05 %601 + %635 = OpFAdd %float %float_0_317398727 %634 + %636 = OpFMul %float %float_4_20481676en08 %601 + %637 = OpFMul %float %636 %601 + %638 = OpFAdd %float %635 %637 + %639 = OpFMul %float %float_2_8974182en05 %601 + %640 = OpFSub %float %float_1 %639 + %641 = OpFMul %float %float_1_61456057en07 %601 + %642 = OpFMul %float %641 %601 + %643 = OpFAdd %float %640 %642 + %644 = OpFDiv %float %638 %643 + %645 = OpFMul %float %float_3 %633 + %646 = OpFMul %float %float_2 %633 + %647 = OpFMul %float %float_8 %644 + %648 = OpFSub %float %646 %647 + %649 = OpFAdd %float %648 %float_4 + %650 = OpFDiv %float %645 %649 + %651 = OpFMul %float %float_2 %644 + %652 = OpFDiv %float %651 %649 + %653 = OpCompositeConstruct %v2float %650 %652 + %654 = OpFOrdLessThan %bool %601 %float_4000 + %655 = OpCompositeConstruct %v2bool %654 %654 + %656 = OpSelect %v2float %655 %653 %622 + %657 = OpAccessChain %_ptr_Uniform_float %_Globals %int_18 + %658 = OpLoad %float %657 + %659 = OpCompositeConstruct %v2float %633 %644 + %660 = OpExtInst %v2float %1 Normalize %659 + %661 = OpCompositeExtract %float %660 1 + %662 = OpFNegate %float %661 + %663 = OpFMul %float %662 %658 + %664 = OpFMul %float %663 %float_0_0500000007 + %665 = OpFAdd %float %633 %664 + %666 = OpCompositeExtract %float %660 0 + %667 = OpFMul %float %666 %658 + %668 = OpFMul %float %667 %float_0_0500000007 + %669 = OpFAdd %float %644 %668 + %670 = OpFMul %float %float_3 %665 + %671 = OpFMul %float %float_2 %665 + %672 = OpFMul %float %float_8 %669 + %673 = OpFSub %float %671 %672 + %674 = OpFAdd %float %673 %float_4 + %675 = OpFDiv %float %670 %674 + %676 = OpFMul %float %float_2 %669 + %677 = OpFDiv %float %676 %674 + %678 = OpCompositeConstruct %v2float %675 %677 + %679 = OpFSub %v2float %678 %653 + %680 = OpFAdd %v2float %656 %679 + %681 = OpCompositeExtract %float %680 0 + %682 = OpCompositeExtract %float %680 1 + %683 = OpExtInst %float %1 FMax %682 %float_1_00000001en10 + %684 = OpFDiv %float %681 %683 + %685 = OpCompositeInsert %v3float %684 %391 0 + %686 = OpCompositeInsert %v3float %float_1 %685 1 + %687 = OpFSub %float %float_1 %681 + %688 = OpFSub %float %687 %682 + %689 = OpFDiv %float %688 %683 + %690 = OpCompositeInsert %v3float %689 %686 2 + %691 = OpExtInst %float %1 FMax %float_0_328999996 %float_1_00000001en10 + %692 = OpFDiv %float %float_0_312700003 %691 + %693 = OpCompositeInsert %v3float %692 %391 0 + %694 = OpCompositeInsert %v3float %float_1 %693 1 + %695 = OpFDiv %float %float_0_358299971 %691 + %696 = OpCompositeInsert %v3float %695 %694 2 + %697 = OpVectorTimesMatrix %v3float %690 %452 + %698 = OpVectorTimesMatrix %v3float %696 %452 + %699 = OpCompositeExtract %float %698 0 + %700 = OpCompositeExtract %float %697 0 + %701 = OpFDiv %float %699 %700 + %702 = OpCompositeConstruct %v3float %701 %float_0 %float_0 + %703 = OpCompositeExtract %float %698 1 + %704 = OpCompositeExtract %float %697 1 + %705 = OpFDiv %float %703 %704 + %706 = OpCompositeConstruct %v3float %float_0 %705 %float_0 + %707 = OpCompositeExtract %float %698 2 + %708 = OpCompositeExtract %float %697 2 + %709 = OpFDiv %float %707 %708 + %710 = OpCompositeConstruct %v3float %float_0 %float_0 %709 + %711 = OpCompositeConstruct %mat3v3float %702 %706 %710 + %712 = OpMatrixTimesMatrix %mat3v3float %452 %711 + %713 = OpMatrixTimesMatrix %mat3v3float %712 %456 + %714 = OpMatrixTimesMatrix %mat3v3float %422 %713 + %715 = OpMatrixTimesMatrix %mat3v3float %714 %418 + %716 = OpVectorTimesMatrix %v3float %599 %715 + %717 = OpVectorTimesMatrix %v3float %716 %547 + %718 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_9 + %719 = OpAccessChain %_ptr_Uniform_float %_Globals %int_9 %int_3 + %720 = OpLoad %float %719 + %721 = OpFOrdNotEqual %bool %720 %float_0 + OpSelectionMerge %722 None + OpBranchConditional %721 %723 %722 + %723 = OpLabel + %724 = OpDot %float %717 %67 + %725 = OpCompositeConstruct %v3float %724 %724 %724 + %726 = OpFDiv %v3float %717 %725 + %727 = OpFSub %v3float %726 %135 + %728 = OpDot %float %727 %727 + %729 = OpFMul %float %float_n4 %728 + %730 = OpExtInst %float %1 Exp2 %729 + %731 = OpFSub %float %float_1 %730 + %732 = OpAccessChain %_ptr_Uniform_float %_Globals %int_44 + %733 = OpLoad %float %732 + %734 = OpFMul %float %float_n4 %733 + %735 = OpFMul %float %734 %724 + %736 = OpFMul %float %735 %724 + %737 = OpExtInst %float %1 Exp2 %736 + %738 = OpFSub %float %float_1 %737 + %739 = OpFMul %float %731 %738 + %740 = OpMatrixTimesMatrix %mat3v3float %461 %406 + %741 = OpMatrixTimesMatrix %mat3v3float %549 %740 + %742 = OpVectorTimesMatrix %v3float %717 %741 + %743 = OpCompositeConstruct %v3float %739 %739 %739 + %744 = OpExtInst %v3float %1 FMix %717 %742 %743 + OpBranch %722 + %722 = OpLabel + %745 = OpPhi %v3float %717 %581 %744 %723 + %746 = OpDot %float %745 %67 + %747 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_24 + %748 = OpLoad %v4float %747 + %749 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_19 + %750 = OpLoad %v4float %749 + %751 = OpFMul %v4float %748 %750 + %752 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_25 + %753 = OpLoad %v4float %752 + %754 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_20 + %755 = OpLoad %v4float %754 + %756 = OpFMul %v4float %753 %755 + %757 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_26 + %758 = OpLoad %v4float %757 + %759 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_21 + %760 = OpLoad %v4float %759 + %761 = OpFMul %v4float %758 %760 + %762 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_27 + %763 = OpLoad %v4float %762 + %764 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_22 + %765 = OpLoad %v4float %764 + %766 = OpFMul %v4float %763 %765 + %767 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_28 + %768 = OpLoad %v4float %767 + %769 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_23 + %770 = OpLoad %v4float %769 + %771 = OpFAdd %v4float %768 %770 + %772 = OpCompositeConstruct %v3float %746 %746 %746 + %773 = OpVectorShuffle %v3float %751 %751 0 1 2 + %774 = OpCompositeExtract %float %751 3 + %775 = OpCompositeConstruct %v3float %774 %774 %774 + %776 = OpFMul %v3float %773 %775 + %777 = OpExtInst %v3float %1 FMix %772 %745 %776 + %778 = OpExtInst %v3float %1 FMax %132 %777 + %779 = OpFMul %v3float %778 %307 + %780 = OpVectorShuffle %v3float %756 %756 0 1 2 + %781 = OpCompositeExtract %float %756 3 + %782 = OpCompositeConstruct %v3float %781 %781 %781 + %783 = OpFMul %v3float %780 %782 + %784 = OpExtInst %v3float %1 Pow %779 %783 + %785 = OpFMul %v3float %784 %194 + %786 = OpVectorShuffle %v3float %761 %761 0 1 2 + %787 = OpCompositeExtract %float %761 3 + %788 = OpCompositeConstruct %v3float %787 %787 %787 + %789 = OpFMul %v3float %786 %788 + %790 = OpFDiv %v3float %135 %789 + %791 = OpExtInst %v3float %1 Pow %785 %790 + %792 = OpVectorShuffle %v3float %766 %766 0 1 2 + %793 = OpCompositeExtract %float %766 3 + %794 = OpCompositeConstruct %v3float %793 %793 %793 + %795 = OpFMul %v3float %792 %794 + %796 = OpFMul %v3float %791 %795 + %797 = OpVectorShuffle %v3float %771 %771 0 1 2 + %798 = OpCompositeExtract %float %771 3 + %799 = OpCompositeConstruct %v3float %798 %798 %798 + %800 = OpFAdd %v3float %797 %799 + %801 = OpFAdd %v3float %796 %800 + %802 = OpAccessChain %_ptr_Uniform_float %_Globals %int_39 + %803 = OpLoad %float %802 + %804 = OpExtInst %float %1 SmoothStep %float_0 %803 %746 + %805 = OpFSub %float %float_1 %804 + %806 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_34 + %807 = OpLoad %v4float %806 + %808 = OpFMul %v4float %807 %750 + %809 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_35 + %810 = OpLoad %v4float %809 + %811 = OpFMul %v4float %810 %755 + %812 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_36 + %813 = OpLoad %v4float %812 + %814 = OpFMul %v4float %813 %760 + %815 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_37 + %816 = OpLoad %v4float %815 + %817 = OpFMul %v4float %816 %765 + %818 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_38 + %819 = OpLoad %v4float %818 + %820 = OpFAdd %v4float %819 %770 + %821 = OpVectorShuffle %v3float %808 %808 0 1 2 + %822 = OpCompositeExtract %float %808 3 + %823 = OpCompositeConstruct %v3float %822 %822 %822 + %824 = OpFMul %v3float %821 %823 + %825 = OpExtInst %v3float %1 FMix %772 %745 %824 + %826 = OpExtInst %v3float %1 FMax %132 %825 + %827 = OpFMul %v3float %826 %307 + %828 = OpVectorShuffle %v3float %811 %811 0 1 2 + %829 = OpCompositeExtract %float %811 3 + %830 = OpCompositeConstruct %v3float %829 %829 %829 + %831 = OpFMul %v3float %828 %830 + %832 = OpExtInst %v3float %1 Pow %827 %831 + %833 = OpFMul %v3float %832 %194 + %834 = OpVectorShuffle %v3float %814 %814 0 1 2 + %835 = OpCompositeExtract %float %814 3 + %836 = OpCompositeConstruct %v3float %835 %835 %835 + %837 = OpFMul %v3float %834 %836 + %838 = OpFDiv %v3float %135 %837 + %839 = OpExtInst %v3float %1 Pow %833 %838 + %840 = OpVectorShuffle %v3float %817 %817 0 1 2 + %841 = OpCompositeExtract %float %817 3 + %842 = OpCompositeConstruct %v3float %841 %841 %841 + %843 = OpFMul %v3float %840 %842 + %844 = OpFMul %v3float %839 %843 + %845 = OpVectorShuffle %v3float %820 %820 0 1 2 + %846 = OpCompositeExtract %float %820 3 + %847 = OpCompositeConstruct %v3float %846 %846 %846 + %848 = OpFAdd %v3float %845 %847 + %849 = OpFAdd %v3float %844 %848 + %850 = OpAccessChain %_ptr_Uniform_float %_Globals %int_40 + %851 = OpLoad %float %850 + %852 = OpExtInst %float %1 SmoothStep %851 %float_1 %746 + %853 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_29 + %854 = OpLoad %v4float %853 + %855 = OpFMul %v4float %854 %750 + %856 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_30 + %857 = OpLoad %v4float %856 + %858 = OpFMul %v4float %857 %755 + %859 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_31 + %860 = OpLoad %v4float %859 + %861 = OpFMul %v4float %860 %760 + %862 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_32 + %863 = OpLoad %v4float %862 + %864 = OpFMul %v4float %863 %765 + %865 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_33 + %866 = OpLoad %v4float %865 + %867 = OpFAdd %v4float %866 %770 + %868 = OpVectorShuffle %v3float %855 %855 0 1 2 + %869 = OpCompositeExtract %float %855 3 + %870 = OpCompositeConstruct %v3float %869 %869 %869 + %871 = OpFMul %v3float %868 %870 + %872 = OpExtInst %v3float %1 FMix %772 %745 %871 + %873 = OpExtInst %v3float %1 FMax %132 %872 + %874 = OpFMul %v3float %873 %307 + %875 = OpVectorShuffle %v3float %858 %858 0 1 2 + %876 = OpCompositeExtract %float %858 3 + %877 = OpCompositeConstruct %v3float %876 %876 %876 + %878 = OpFMul %v3float %875 %877 + %879 = OpExtInst %v3float %1 Pow %874 %878 + %880 = OpFMul %v3float %879 %194 + %881 = OpVectorShuffle %v3float %861 %861 0 1 2 + %882 = OpCompositeExtract %float %861 3 + %883 = OpCompositeConstruct %v3float %882 %882 %882 + %884 = OpFMul %v3float %881 %883 + %885 = OpFDiv %v3float %135 %884 + %886 = OpExtInst %v3float %1 Pow %880 %885 + %887 = OpVectorShuffle %v3float %864 %864 0 1 2 + %888 = OpCompositeExtract %float %864 3 + %889 = OpCompositeConstruct %v3float %888 %888 %888 + %890 = OpFMul %v3float %887 %889 + %891 = OpFMul %v3float %886 %890 + %892 = OpVectorShuffle %v3float %867 %867 0 1 2 + %893 = OpCompositeExtract %float %867 3 + %894 = OpCompositeConstruct %v3float %893 %893 %893 + %895 = OpFAdd %v3float %892 %894 + %896 = OpFAdd %v3float %891 %895 + %897 = OpFSub %float %804 %852 + %898 = OpCompositeConstruct %v3float %805 %805 %805 + %899 = OpFMul %v3float %801 %898 + %900 = OpCompositeConstruct %v3float %897 %897 %897 + %901 = OpFMul %v3float %896 %900 + %902 = OpFAdd %v3float %899 %901 + %903 = OpCompositeConstruct %v3float %852 %852 %852 + %904 = OpFMul %v3float %849 %903 + %905 = OpFAdd %v3float %902 %904 + %906 = OpVectorTimesMatrix %v3float %905 %549 + %907 = OpMatrixTimesMatrix %mat3v3float %551 %465 + %908 = OpMatrixTimesMatrix %mat3v3float %907 %550 + %909 = OpMatrixTimesMatrix %mat3v3float %551 %469 + %910 = OpMatrixTimesMatrix %mat3v3float %909 %550 + %911 = OpVectorTimesMatrix %v3float %905 %908 + %912 = OpAccessChain %_ptr_Uniform_float %_Globals %int_43 + %913 = OpLoad %float %912 + %914 = OpCompositeConstruct %v3float %913 %913 %913 + %915 = OpExtInst %v3float %1 FMix %905 %911 %914 + %916 = OpVectorTimesMatrix %v3float %915 %551 + %917 = OpCompositeExtract %float %916 0 + %918 = OpCompositeExtract %float %916 1 + %919 = OpExtInst %float %1 FMin %917 %918 + %920 = OpCompositeExtract %float %916 2 + %921 = OpExtInst %float %1 FMin %919 %920 + %922 = OpExtInst %float %1 FMax %917 %918 + %923 = OpExtInst %float %1 FMax %922 %920 + %924 = OpExtInst %float %1 FMax %923 %float_1_00000001en10 + %925 = OpExtInst %float %1 FMax %921 %float_1_00000001en10 + %926 = OpFSub %float %924 %925 + %927 = OpExtInst %float %1 FMax %923 %float_0_00999999978 + %928 = OpFDiv %float %926 %927 + %929 = OpFSub %float %920 %918 + %930 = OpFMul %float %920 %929 + %931 = OpFSub %float %918 %917 + %932 = OpFMul %float %918 %931 + %933 = OpFAdd %float %930 %932 + %934 = OpFSub %float %917 %920 + %935 = OpFMul %float %917 %934 + %936 = OpFAdd %float %933 %935 + %937 = OpExtInst %float %1 Sqrt %936 + %938 = OpFAdd %float %920 %918 + %939 = OpFAdd %float %938 %917 + %940 = OpFMul %float %float_1_75 %937 + %941 = OpFAdd %float %939 %940 + %942 = OpFMul %float %941 %float_0_333333343 + %943 = OpFSub %float %928 %float_0_400000006 + %944 = OpFMul %float %943 %float_5 + %945 = OpFMul %float %943 %float_2_5 + %946 = OpExtInst %float %1 FAbs %945 + %947 = OpFSub %float %float_1 %946 + %948 = OpExtInst %float %1 FMax %947 %float_0 + %949 = OpExtInst %float %1 FSign %944 + %950 = OpConvertFToS %int %949 + %951 = OpConvertSToF %float %950 + %952 = OpFMul %float %948 %948 + %953 = OpFSub %float %float_1 %952 + %954 = OpFMul %float %951 %953 + %955 = OpFAdd %float %float_1 %954 + %956 = OpFMul %float %955 %float_0_0250000004 + %957 = OpFOrdLessThanEqual %bool %942 %float_0_0533333346 + OpSelectionMerge %958 None + OpBranchConditional %957 %959 %960 + %960 = OpLabel + %961 = OpFOrdGreaterThanEqual %bool %942 %float_0_159999996 + OpSelectionMerge %962 None + OpBranchConditional %961 %963 %964 + %964 = OpLabel + %965 = OpFDiv %float %float_0_239999995 %941 + %966 = OpFSub %float %965 %float_0_5 + %967 = OpFMul %float %956 %966 + OpBranch %962 + %963 = OpLabel + OpBranch %962 + %962 = OpLabel + %968 = OpPhi %float %967 %964 %float_0 %963 + OpBranch %958 + %959 = OpLabel + OpBranch %958 + %958 = OpLabel + %969 = OpPhi %float %968 %962 %956 %959 + %970 = OpFAdd %float %float_1 %969 + %971 = OpCompositeConstruct %v3float %970 %970 %970 + %972 = OpFMul %v3float %916 %971 + %973 = OpCompositeExtract %float %972 0 + %974 = OpCompositeExtract %float %972 1 + %975 = OpFOrdEqual %bool %973 %974 + %976 = OpCompositeExtract %float %972 2 + %977 = OpFOrdEqual %bool %974 %976 + %978 = OpLogicalAnd %bool %975 %977 + OpSelectionMerge %979 None + OpBranchConditional %978 %980 %981 + %981 = OpLabel + %982 = OpExtInst %float %1 Sqrt %float_3 + %983 = OpFSub %float %974 %976 + %984 = OpFMul %float %982 %983 + %985 = OpFMul %float %float_2 %973 + %986 = OpFSub %float %985 %974 + %987 = OpFSub %float %986 %976 + %988 = OpExtInst %float %1 Atan2 %984 %987 + %989 = OpFMul %float %float_57_2957764 %988 + OpBranch %979 + %980 = OpLabel + OpBranch %979 + %979 = OpLabel + %990 = OpPhi %float %989 %981 %float_0 %980 + %991 = OpFOrdLessThan %bool %990 %float_0 + OpSelectionMerge %992 None + OpBranchConditional %991 %993 %992 + %993 = OpLabel + %994 = OpFAdd %float %990 %float_360 + OpBranch %992 + %992 = OpLabel + %995 = OpPhi %float %990 %979 %994 %993 + %996 = OpExtInst %float %1 FClamp %995 %float_0 %float_360 + %997 = OpFOrdGreaterThan %bool %996 %float_180 + OpSelectionMerge %998 None + OpBranchConditional %997 %999 %998 + %999 = OpLabel + %1000 = OpFSub %float %996 %float_360 + OpBranch %998 + %998 = OpLabel + %1001 = OpPhi %float %996 %992 %1000 %999 + %1002 = OpFMul %float %1001 %float_0_0148148146 + %1003 = OpExtInst %float %1 FAbs %1002 + %1004 = OpFSub %float %float_1 %1003 + %1005 = OpExtInst %float %1 SmoothStep %float_0 %float_1 %1004 + %1006 = OpFMul %float %1005 %1005 + %1007 = OpFMul %float %1006 %928 + %1008 = OpFSub %float %float_0_0299999993 %973 + %1009 = OpFMul %float %1007 %1008 + %1010 = OpFMul %float %1009 %float_0_180000007 + %1011 = OpFAdd %float %973 %1010 + %1012 = OpCompositeInsert %v3float %1011 %972 0 + %1013 = OpVectorTimesMatrix %v3float %1012 %410 + %1014 = OpExtInst %v3float %1 FMax %132 %1013 + %1015 = OpDot %float %1014 %67 + %1016 = OpCompositeConstruct %v3float %1015 %1015 %1015 + %1017 = OpExtInst %v3float %1 FMix %1016 %1014 %228 + %1018 = OpAccessChain %_ptr_Uniform_float %_Globals %int_13 + %1019 = OpLoad %float %1018 + %1020 = OpFAdd %float %float_1 %1019 + %1021 = OpAccessChain %_ptr_Uniform_float %_Globals %int_11 + %1022 = OpLoad %float %1021 + %1023 = OpFSub %float %1020 %1022 + %1024 = OpAccessChain %_ptr_Uniform_float %_Globals %int_14 + %1025 = OpLoad %float %1024 + %1026 = OpFAdd %float %float_1 %1025 + %1027 = OpAccessChain %_ptr_Uniform_float %_Globals %int_12 + %1028 = OpLoad %float %1027 + %1029 = OpFSub %float %1026 %1028 + %1030 = OpFOrdGreaterThan %bool %1022 %float_0_800000012 + OpSelectionMerge %1031 None + OpBranchConditional %1030 %1032 %1033 + %1033 = OpLabel + %1034 = OpFAdd %float %float_0_180000007 %1019 + %1035 = OpFDiv %float %1034 %1023 + %1036 = OpExtInst %float %1 Log %float_0_180000007 + %1037 = OpExtInst %float %1 Log %float_10 + %1038 = OpFDiv %float %1036 %1037 + %1039 = OpFSub %float %float_2 %1035 + %1040 = OpFDiv %float %1035 %1039 + %1041 = OpExtInst %float %1 Log %1040 + %1042 = OpFMul %float %float_0_5 %1041 + %1043 = OpAccessChain %_ptr_Uniform_float %_Globals %int_10 + %1044 = OpLoad %float %1043 + %1045 = OpFDiv %float %1023 %1044 + %1046 = OpFMul %float %1042 %1045 + %1047 = OpFSub %float %1038 %1046 + OpBranch %1031 + %1032 = OpLabel + %1048 = OpFSub %float %float_0_819999993 %1022 + %1049 = OpAccessChain %_ptr_Uniform_float %_Globals %int_10 + %1050 = OpLoad %float %1049 + %1051 = OpFDiv %float %1048 %1050 + %1052 = OpExtInst %float %1 Log %float_0_180000007 + %1053 = OpExtInst %float %1 Log %float_10 + %1054 = OpFDiv %float %1052 %1053 + %1055 = OpFAdd %float %1051 %1054 + OpBranch %1031 + %1031 = OpLabel + %1056 = OpPhi %float %1047 %1033 %1055 %1032 + %1057 = OpFSub %float %float_1 %1022 + %1058 = OpAccessChain %_ptr_Uniform_float %_Globals %int_10 + %1059 = OpLoad %float %1058 + %1060 = OpFDiv %float %1057 %1059 + %1061 = OpFSub %float %1060 %1056 + %1062 = OpFDiv %float %1028 %1059 + %1063 = OpFSub %float %1062 %1061 + %1064 = OpExtInst %v3float %1 Log %1017 + %1065 = OpExtInst %float %1 Log %float_10 + %1066 = OpCompositeConstruct %v3float %1065 %1065 %1065 + %1067 = OpFDiv %v3float %1064 %1066 + %1068 = OpCompositeConstruct %v3float %1059 %1059 %1059 + %1069 = OpCompositeConstruct %v3float %1061 %1061 %1061 + %1070 = OpFAdd %v3float %1067 %1069 + %1071 = OpFMul %v3float %1068 %1070 + %1072 = OpFNegate %float %1019 + %1073 = OpCompositeConstruct %v3float %1072 %1072 %1072 + %1074 = OpFMul %float %float_2 %1023 + %1075 = OpCompositeConstruct %v3float %1074 %1074 %1074 + %1076 = OpFMul %float %float_n2 %1059 + %1077 = OpFDiv %float %1076 %1023 + %1078 = OpCompositeConstruct %v3float %1077 %1077 %1077 + %1079 = OpCompositeConstruct %v3float %1056 %1056 %1056 + %1080 = OpFSub %v3float %1067 %1079 + %1081 = OpFMul %v3float %1078 %1080 + %1082 = OpExtInst %v3float %1 Exp %1081 + %1083 = OpFAdd %v3float %135 %1082 + %1084 = OpFDiv %v3float %1075 %1083 + %1085 = OpFAdd %v3float %1073 %1084 + %1086 = OpCompositeConstruct %v3float %1026 %1026 %1026 + %1087 = OpFMul %float %float_2 %1029 + %1088 = OpCompositeConstruct %v3float %1087 %1087 %1087 + %1089 = OpFMul %float %float_2 %1059 + %1090 = OpFDiv %float %1089 %1029 + %1091 = OpCompositeConstruct %v3float %1090 %1090 %1090 + %1092 = OpCompositeConstruct %v3float %1063 %1063 %1063 + %1093 = OpFSub %v3float %1067 %1092 + %1094 = OpFMul %v3float %1091 %1093 + %1095 = OpExtInst %v3float %1 Exp %1094 + %1096 = OpFAdd %v3float %135 %1095 + %1097 = OpFDiv %v3float %1088 %1096 + %1098 = OpFSub %v3float %1086 %1097 + %1099 = OpFOrdLessThan %v3bool %1067 %1079 + %1100 = OpSelect %v3float %1099 %1085 %1071 + %1101 = OpFOrdGreaterThan %v3bool %1067 %1092 + %1102 = OpSelect %v3float %1101 %1098 %1071 + %1103 = OpFSub %float %1063 %1056 + %1104 = OpCompositeConstruct %v3float %1103 %1103 %1103 + %1105 = OpFDiv %v3float %1080 %1104 + %1106 = OpExtInst %v3float %1 FClamp %1105 %132 %135 + %1107 = OpFOrdLessThan %bool %1063 %1056 + %1108 = OpFSub %v3float %135 %1106 + %1109 = OpCompositeConstruct %v3bool %1107 %1107 %1107 + %1110 = OpSelect %v3float %1109 %1108 %1106 + %1111 = OpFMul %v3float %239 %1110 + %1112 = OpFSub %v3float %238 %1111 + %1113 = OpFMul %v3float %1112 %1110 + %1114 = OpFMul %v3float %1113 %1110 + %1115 = OpExtInst %v3float %1 FMix %1100 %1102 %1114 + %1116 = OpDot %float %1115 %67 + %1117 = OpCompositeConstruct %v3float %1116 %1116 %1116 + %1118 = OpExtInst %v3float %1 FMix %1117 %1115 %241 + %1119 = OpExtInst %v3float %1 FMax %132 %1118 + %1120 = OpVectorTimesMatrix %v3float %1119 %910 + %1121 = OpExtInst %v3float %1 FMix %1119 %1120 %914 + %1122 = OpVectorTimesMatrix %v3float %1121 %549 + %1123 = OpExtInst %v3float %1 FMax %132 %1122 + %1124 = OpFOrdEqual %bool %720 %float_0 + OpSelectionMerge %1125 DontFlatten + OpBranchConditional %1124 %1126 %1125 + %1126 = OpLabel + %1127 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_2 + %1128 = OpLoad %v4float %1127 + %1129 = OpVectorShuffle %v3float %1128 %1128 0 1 2 + %1130 = OpDot %float %906 %1129 + %1131 = OpCompositeInsert %v3float %1130 %391 0 + %1132 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_3 + %1133 = OpLoad %v4float %1132 + %1134 = OpVectorShuffle %v3float %1133 %1133 0 1 2 + %1135 = OpDot %float %906 %1134 + %1136 = OpCompositeInsert %v3float %1135 %1131 1 + %1137 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_4 + %1138 = OpLoad %v4float %1137 + %1139 = OpVectorShuffle %v3float %1138 %1138 0 1 2 + %1140 = OpDot %float %906 %1139 + %1141 = OpCompositeInsert %v3float %1140 %1136 2 + %1142 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_8 + %1143 = OpLoad %v4float %1142 + %1144 = OpVectorShuffle %v3float %1143 %1143 0 1 2 + %1145 = OpLoad %v4float %718 + %1146 = OpVectorShuffle %v3float %1145 %1145 0 1 2 + %1147 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_7 + %1148 = OpLoad %v4float %1147 + %1149 = OpVectorShuffle %v3float %1148 %1148 0 1 2 + %1150 = OpDot %float %906 %1149 + %1151 = OpFAdd %float %1150 %float_1 + %1152 = OpFDiv %float %float_1 %1151 + %1153 = OpCompositeConstruct %v3float %1152 %1152 %1152 + %1154 = OpFMul %v3float %1146 %1153 + %1155 = OpFAdd %v3float %1144 %1154 + %1156 = OpFMul %v3float %1141 %1155 + %1157 = OpExtInst %v3float %1 FMax %132 %1156 + %1158 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_5 + %1159 = OpLoad %v4float %1158 + %1160 = OpVectorShuffle %v3float %1159 %1159 0 0 0 + %1161 = OpFSub %v3float %1160 %1157 + %1162 = OpExtInst %v3float %1 FMax %132 %1161 + %1163 = OpVectorShuffle %v3float %1159 %1159 2 2 2 + %1164 = OpExtInst %v3float %1 FMax %1157 %1163 + %1165 = OpExtInst %v3float %1 FClamp %1157 %1160 %1163 + %1166 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_6 + %1167 = OpLoad %v4float %1166 + %1168 = OpVectorShuffle %v3float %1167 %1167 0 0 0 + %1169 = OpFMul %v3float %1164 %1168 + %1170 = OpVectorShuffle %v3float %1167 %1167 1 1 1 + %1171 = OpFAdd %v3float %1169 %1170 + %1172 = OpVectorShuffle %v3float %1159 %1159 3 3 3 + %1173 = OpFAdd %v3float %1164 %1172 + %1174 = OpFDiv %v3float %135 %1173 + %1175 = OpFMul %v3float %1171 %1174 + %1176 = OpVectorShuffle %v3float %1138 %1138 3 3 3 + %1177 = OpFMul %v3float %1165 %1176 + %1178 = OpVectorShuffle %v3float %1128 %1128 3 3 3 + %1179 = OpFMul %v3float %1162 %1178 + %1180 = OpVectorShuffle %v3float %1159 %1159 1 1 1 + %1181 = OpFAdd %v3float %1162 %1180 + %1182 = OpFDiv %v3float %135 %1181 + %1183 = OpFMul %v3float %1179 %1182 + %1184 = OpVectorShuffle %v3float %1133 %1133 3 3 3 + %1185 = OpFAdd %v3float %1183 %1184 + %1186 = OpFAdd %v3float %1177 %1185 + %1187 = OpFAdd %v3float %1175 %1186 + %1188 = OpFSub %v3float %1187 %248 + OpBranch %1125 + %1125 = OpLabel + %1189 = OpPhi %v3float %1123 %1031 %1188 %1126 + %1190 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_0 + %1191 = OpLoad %float %1190 + %1192 = OpCompositeConstruct %v3float %1191 %1191 %1191 + %1193 = OpFMul %v3float %1189 %1189 + %1194 = OpFMul %v3float %1192 %1193 + %1195 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_1 + %1196 = OpLoad %float %1195 + %1197 = OpCompositeConstruct %v3float %1196 %1196 %1196 + %1198 = OpFMul %v3float %1197 %1189 + %1199 = OpFAdd %v3float %1194 %1198 + %1200 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_2 + %1201 = OpLoad %float %1200 + %1202 = OpCompositeConstruct %v3float %1201 %1201 %1201 + %1203 = OpFAdd %v3float %1199 %1202 + %1204 = OpAccessChain %_ptr_Uniform_v3float %_Globals %int_15 + %1205 = OpLoad %v3float %1204 + %1206 = OpFMul %v3float %1203 %1205 + %1207 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_16 + %1208 = OpLoad %v4float %1207 + %1209 = OpVectorShuffle %v3float %1208 %1208 0 1 2 + %1210 = OpAccessChain %_ptr_Uniform_float %_Globals %int_16 %int_3 + %1211 = OpLoad %float %1210 + %1212 = OpCompositeConstruct %v3float %1211 %1211 %1211 + %1213 = OpExtInst %v3float %1 FMix %1206 %1209 %1212 + %1214 = OpExtInst %v3float %1 FMax %132 %1213 + %1215 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 %int_1 + %1216 = OpLoad %float %1215 + %1217 = OpCompositeConstruct %v3float %1216 %1216 %1216 + %1218 = OpExtInst %v3float %1 Pow %1214 %1217 + %1219 = OpIEqual %bool %579 %uint_0 + OpSelectionMerge %1220 DontFlatten + OpBranchConditional %1219 %1221 %1222 + %1222 = OpLabel + %1223 = OpIEqual %bool %579 %uint_1 + OpSelectionMerge %1224 None + OpBranchConditional %1223 %1225 %1226 + %1226 = OpLabel + %1227 = OpIEqual %bool %579 %uint_3 + %1228 = OpIEqual %bool %579 %uint_5 + %1229 = OpLogicalOr %bool %1227 %1228 + OpSelectionMerge %1230 None + OpBranchConditional %1229 %1231 %1232 + %1232 = OpLabel + %1233 = OpIEqual %bool %579 %uint_4 + %1234 = OpIEqual %bool %579 %uint_6 + %1235 = OpLogicalOr %bool %1233 %1234 + OpSelectionMerge %1236 None + OpBranchConditional %1235 %1237 %1238 + %1238 = OpLabel + %1239 = OpIEqual %bool %579 %uint_7 + OpSelectionMerge %1240 None + OpBranchConditional %1239 %1241 %1242 + %1242 = OpLabel + %1243 = OpVectorTimesMatrix %v3float %1218 %547 + %1244 = OpVectorTimesMatrix %v3float %1243 %576 + %1245 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 %int_2 + %1246 = OpLoad %float %1245 + %1247 = OpCompositeConstruct %v3float %1246 %1246 %1246 + %1248 = OpExtInst %v3float %1 Pow %1244 %1247 + OpBranch %1240 + %1241 = OpLabel + %1249 = OpVectorTimesMatrix %v3float %906 %547 + %1250 = OpVectorTimesMatrix %v3float %1249 %576 + %1251 = OpFMul %v3float %1250 %496 + %1252 = OpExtInst %v3float %1 Pow %1251 %263 + %1253 = OpFMul %v3float %184 %1252 + %1254 = OpFAdd %v3float %183 %1253 + %1255 = OpFMul %v3float %185 %1252 + %1256 = OpFAdd %v3float %135 %1255 + %1257 = OpFDiv %v3float %135 %1256 + %1258 = OpFMul %v3float %1254 %1257 + %1259 = OpExtInst %v3float %1 Pow %1258 %264 + OpBranch %1240 + %1240 = OpLabel + %1260 = OpPhi %v3float %1248 %1242 %1259 %1241 + OpBranch %1236 + %1237 = OpLabel + %1261 = OpMatrixTimesMatrix %mat3v3float %546 %399 + %1262 = OpFMul %v3float %906 %262 + %1263 = OpVectorTimesMatrix %v3float %1262 %1261 + %1264 = OpCompositeExtract %float %1263 0 + %1265 = OpCompositeExtract %float %1263 1 + %1266 = OpExtInst %float %1 FMin %1264 %1265 + %1267 = OpCompositeExtract %float %1263 2 + %1268 = OpExtInst %float %1 FMin %1266 %1267 + %1269 = OpExtInst %float %1 FMax %1264 %1265 + %1270 = OpExtInst %float %1 FMax %1269 %1267 + %1271 = OpExtInst %float %1 FMax %1270 %float_1_00000001en10 + %1272 = OpExtInst %float %1 FMax %1268 %float_1_00000001en10 + %1273 = OpFSub %float %1271 %1272 + %1274 = OpExtInst %float %1 FMax %1270 %float_0_00999999978 + %1275 = OpFDiv %float %1273 %1274 + %1276 = OpFSub %float %1267 %1265 + %1277 = OpFMul %float %1267 %1276 + %1278 = OpFSub %float %1265 %1264 + %1279 = OpFMul %float %1265 %1278 + %1280 = OpFAdd %float %1277 %1279 + %1281 = OpFSub %float %1264 %1267 + %1282 = OpFMul %float %1264 %1281 + %1283 = OpFAdd %float %1280 %1282 + %1284 = OpExtInst %float %1 Sqrt %1283 + %1285 = OpFAdd %float %1267 %1265 + %1286 = OpFAdd %float %1285 %1264 + %1287 = OpFMul %float %float_1_75 %1284 + %1288 = OpFAdd %float %1286 %1287 + %1289 = OpFMul %float %1288 %float_0_333333343 + %1290 = OpFSub %float %1275 %float_0_400000006 + %1291 = OpFMul %float %1290 %float_5 + %1292 = OpFMul %float %1290 %float_2_5 + %1293 = OpExtInst %float %1 FAbs %1292 + %1294 = OpFSub %float %float_1 %1293 + %1295 = OpExtInst %float %1 FMax %1294 %float_0 + %1296 = OpExtInst %float %1 FSign %1291 + %1297 = OpConvertFToS %int %1296 + %1298 = OpConvertSToF %float %1297 + %1299 = OpFMul %float %1295 %1295 + %1300 = OpFSub %float %float_1 %1299 + %1301 = OpFMul %float %1298 %1300 + %1302 = OpFAdd %float %float_1 %1301 + %1303 = OpFMul %float %1302 %float_0_0250000004 + %1304 = OpFOrdLessThanEqual %bool %1289 %float_0_0533333346 + OpSelectionMerge %1305 None + OpBranchConditional %1304 %1306 %1307 + %1307 = OpLabel + %1308 = OpFOrdGreaterThanEqual %bool %1289 %float_0_159999996 + OpSelectionMerge %1309 None + OpBranchConditional %1308 %1310 %1311 + %1311 = OpLabel + %1312 = OpFDiv %float %float_0_239999995 %1288 + %1313 = OpFSub %float %1312 %float_0_5 + %1314 = OpFMul %float %1303 %1313 + OpBranch %1309 + %1310 = OpLabel + OpBranch %1309 + %1309 = OpLabel + %1315 = OpPhi %float %1314 %1311 %float_0 %1310 + OpBranch %1305 + %1306 = OpLabel + OpBranch %1305 + %1305 = OpLabel + %1316 = OpPhi %float %1315 %1309 %1303 %1306 + %1317 = OpFAdd %float %float_1 %1316 + %1318 = OpCompositeConstruct %v3float %1317 %1317 %1317 + %1319 = OpFMul %v3float %1263 %1318 + %1320 = OpCompositeExtract %float %1319 0 + %1321 = OpCompositeExtract %float %1319 1 + %1322 = OpFOrdEqual %bool %1320 %1321 + %1323 = OpCompositeExtract %float %1319 2 + %1324 = OpFOrdEqual %bool %1321 %1323 + %1325 = OpLogicalAnd %bool %1322 %1324 + OpSelectionMerge %1326 None + OpBranchConditional %1325 %1327 %1328 + %1328 = OpLabel + %1329 = OpExtInst %float %1 Sqrt %float_3 + %1330 = OpFSub %float %1321 %1323 + %1331 = OpFMul %float %1329 %1330 + %1332 = OpFMul %float %float_2 %1320 + %1333 = OpFSub %float %1332 %1321 + %1334 = OpFSub %float %1333 %1323 + %1335 = OpExtInst %float %1 Atan2 %1331 %1334 + %1336 = OpFMul %float %float_57_2957764 %1335 + OpBranch %1326 + %1327 = OpLabel + OpBranch %1326 + %1326 = OpLabel + %1337 = OpPhi %float %1336 %1328 %float_0 %1327 + %1338 = OpFOrdLessThan %bool %1337 %float_0 + OpSelectionMerge %1339 None + OpBranchConditional %1338 %1340 %1339 + %1340 = OpLabel + %1341 = OpFAdd %float %1337 %float_360 + OpBranch %1339 + %1339 = OpLabel + %1342 = OpPhi %float %1337 %1326 %1341 %1340 + %1343 = OpExtInst %float %1 FClamp %1342 %float_0 %float_360 + %1344 = OpFOrdGreaterThan %bool %1343 %float_180 + OpSelectionMerge %1345 None + OpBranchConditional %1344 %1346 %1345 + %1346 = OpLabel + %1347 = OpFSub %float %1343 %float_360 + OpBranch %1345 + %1345 = OpLabel + %1348 = OpPhi %float %1343 %1339 %1347 %1346 + %1349 = OpFOrdGreaterThan %bool %1348 %float_n67_5 + %1350 = OpFOrdLessThan %bool %1348 %float_67_5 + %1351 = OpLogicalAnd %bool %1349 %1350 + OpSelectionMerge %1352 None + OpBranchConditional %1351 %1353 %1352 + %1353 = OpLabel + %1354 = OpFSub %float %1348 %float_n67_5 + %1355 = OpFMul %float %1354 %float_0_0296296291 + %1356 = OpConvertFToS %int %1355 + %1357 = OpConvertSToF %float %1356 + %1358 = OpFSub %float %1355 %1357 + %1359 = OpFMul %float %1358 %1358 + %1360 = OpFMul %float %1359 %1358 + %1361 = OpIEqual %bool %1356 %int_3 + OpSelectionMerge %1362 None + OpBranchConditional %1361 %1363 %1364 + %1364 = OpLabel + %1365 = OpIEqual %bool %1356 %int_2 + OpSelectionMerge %1366 None + OpBranchConditional %1365 %1367 %1368 + %1368 = OpLabel + %1369 = OpIEqual %bool %1356 %int_1 + OpSelectionMerge %1370 None + OpBranchConditional %1369 %1371 %1372 + %1372 = OpLabel + %1373 = OpIEqual %bool %1356 %int_0 + OpSelectionMerge %1374 None + OpBranchConditional %1373 %1375 %1376 + %1376 = OpLabel + OpBranch %1374 + %1375 = OpLabel + %1377 = OpFMul %float %1360 %float_0_166666672 + OpBranch %1374 + %1374 = OpLabel + %1378 = OpPhi %float %float_0 %1376 %1377 %1375 + OpBranch %1370 + %1371 = OpLabel + %1379 = OpFMul %float %1360 %float_n0_5 + %1380 = OpFMul %float %1359 %float_0_5 + %1381 = OpFAdd %float %1379 %1380 + %1382 = OpFMul %float %1358 %float_0_5 + %1383 = OpFAdd %float %1381 %1382 + %1384 = OpFAdd %float %1383 %float_0_166666672 + OpBranch %1370 + %1370 = OpLabel + %1385 = OpPhi %float %1378 %1374 %1384 %1371 + OpBranch %1366 + %1367 = OpLabel + %1386 = OpFMul %float %1360 %float_0_5 + %1387 = OpFMul %float %1359 %float_n1 + %1388 = OpFAdd %float %1386 %1387 + %1389 = OpFAdd %float %1388 %float_0_666666687 + OpBranch %1366 + %1366 = OpLabel + %1390 = OpPhi %float %1385 %1370 %1389 %1367 + OpBranch %1362 + %1363 = OpLabel + %1391 = OpFMul %float %1360 %float_n0_166666672 + %1392 = OpFMul %float %1359 %float_0_5 + %1393 = OpFAdd %float %1391 %1392 + %1394 = OpFMul %float %1358 %float_n0_5 + %1395 = OpFAdd %float %1393 %1394 + %1396 = OpFAdd %float %1395 %float_0_166666672 + OpBranch %1362 + %1362 = OpLabel + %1397 = OpPhi %float %1390 %1366 %1396 %1363 + OpBranch %1352 + %1352 = OpLabel + %1398 = OpPhi %float %float_0 %1345 %1397 %1362 + %1399 = OpFMul %float %1398 %float_1_5 + %1400 = OpFMul %float %1399 %1275 + %1401 = OpFSub %float %float_0_0299999993 %1320 + %1402 = OpFMul %float %1400 %1401 + %1403 = OpFMul %float %1402 %float_0_180000007 + %1404 = OpFAdd %float %1320 %1403 + %1405 = OpCompositeInsert %v3float %1404 %1319 0 + %1406 = OpExtInst %v3float %1 FClamp %1405 %132 %314 + %1407 = OpVectorTimesMatrix %v3float %1406 %410 + %1408 = OpExtInst %v3float %1 FClamp %1407 %132 %314 + %1409 = OpDot %float %1408 %67 + %1410 = OpCompositeConstruct %v3float %1409 %1409 %1409 + %1411 = OpExtInst %v3float %1 FMix %1410 %1408 %228 + %1412 = OpCompositeExtract %float %1411 0 + %1413 = OpExtInst %float %1 Exp2 %float_n15 + %1414 = OpFMul %float %float_0_179999992 %1413 + %1415 = OpExtInst %float %1 Exp2 %float_18 + %1416 = OpFMul %float %float_0_179999992 %1415 + OpStore %502 %475 + OpStore %501 %476 + %1417 = OpFOrdLessThanEqual %bool %1412 %float_0 + %1418 = OpExtInst %float %1 Exp2 %float_n14 + %1419 = OpSelect %float %1417 %1418 %1412 + %1420 = OpExtInst %float %1 Log %1419 + %1421 = OpFDiv %float %1420 %1065 + %1422 = OpExtInst %float %1 Log %1414 + %1423 = OpFDiv %float %1422 %1065 + %1424 = OpFOrdLessThanEqual %bool %1421 %1423 + OpSelectionMerge %1425 None + OpBranchConditional %1424 %1426 %1427 + %1427 = OpLabel + %1428 = OpFOrdGreaterThan %bool %1421 %1423 + %1429 = OpExtInst %float %1 Log %float_0_180000007 + %1430 = OpFDiv %float %1429 %1065 + %1431 = OpFOrdLessThan %bool %1421 %1430 + %1432 = OpLogicalAnd %bool %1428 %1431 + OpSelectionMerge %1433 None + OpBranchConditional %1432 %1434 %1435 + %1435 = OpLabel + %1436 = OpFOrdGreaterThanEqual %bool %1421 %1430 + %1437 = OpExtInst %float %1 Log %1416 + %1438 = OpFDiv %float %1437 %1065 + %1439 = OpFOrdLessThan %bool %1421 %1438 + %1440 = OpLogicalAnd %bool %1436 %1439 + OpSelectionMerge %1441 None + OpBranchConditional %1440 %1442 %1443 + %1443 = OpLabel + %1444 = OpExtInst %float %1 Log %float_10000 + %1445 = OpFDiv %float %1444 %1065 + OpBranch %1441 + %1442 = OpLabel + %1446 = OpFSub %float %1421 %1430 + %1447 = OpFMul %float %float_3 %1446 + %1448 = OpFSub %float %1438 %1430 + %1449 = OpFDiv %float %1447 %1448 + %1450 = OpConvertFToS %int %1449 + %1451 = OpConvertSToF %float %1450 + %1452 = OpFSub %float %1449 %1451 + %1453 = OpAccessChain %_ptr_Function_float %501 %1450 + %1454 = OpLoad %float %1453 + %1455 = OpIAdd %int %1450 %int_1 + %1456 = OpAccessChain %_ptr_Function_float %501 %1455 + %1457 = OpLoad %float %1456 + %1458 = OpIAdd %int %1450 %int_2 + %1459 = OpAccessChain %_ptr_Function_float %501 %1458 + %1460 = OpLoad %float %1459 + %1461 = OpCompositeConstruct %v3float %1454 %1457 %1460 + %1462 = OpFMul %float %1452 %1452 + %1463 = OpCompositeConstruct %v3float %1462 %1452 %float_1 + %1464 = OpMatrixTimesVector %v3float %442 %1461 + %1465 = OpDot %float %1463 %1464 + OpBranch %1441 + %1441 = OpLabel + %1466 = OpPhi %float %1445 %1443 %1465 %1442 + OpBranch %1433 + %1434 = OpLabel + %1467 = OpFSub %float %1421 %1423 + %1468 = OpFMul %float %float_3 %1467 + %1469 = OpFSub %float %1430 %1423 + %1470 = OpFDiv %float %1468 %1469 + %1471 = OpConvertFToS %int %1470 + %1472 = OpConvertSToF %float %1471 + %1473 = OpFSub %float %1470 %1472 + %1474 = OpAccessChain %_ptr_Function_float %502 %1471 + %1475 = OpLoad %float %1474 + %1476 = OpIAdd %int %1471 %int_1 + %1477 = OpAccessChain %_ptr_Function_float %502 %1476 + %1478 = OpLoad %float %1477 + %1479 = OpIAdd %int %1471 %int_2 + %1480 = OpAccessChain %_ptr_Function_float %502 %1479 + %1481 = OpLoad %float %1480 + %1482 = OpCompositeConstruct %v3float %1475 %1478 %1481 + %1483 = OpFMul %float %1473 %1473 + %1484 = OpCompositeConstruct %v3float %1483 %1473 %float_1 + %1485 = OpMatrixTimesVector %v3float %442 %1482 + %1486 = OpDot %float %1484 %1485 + OpBranch %1433 + %1433 = OpLabel + %1487 = OpPhi %float %1466 %1441 %1486 %1434 + OpBranch %1425 + %1426 = OpLabel + %1488 = OpExtInst %float %1 Log %float_9_99999975en05 + %1489 = OpFDiv %float %1488 %1065 + OpBranch %1425 + %1425 = OpLabel + %1490 = OpPhi %float %1487 %1433 %1489 %1426 + %1491 = OpExtInst %float %1 Pow %float_10 %1490 + %1492 = OpCompositeInsert %v3float %1491 %391 0 + %1493 = OpCompositeExtract %float %1411 1 + OpStore %504 %475 + OpStore %503 %476 + %1494 = OpFOrdLessThanEqual %bool %1493 %float_0 + %1495 = OpSelect %float %1494 %1418 %1493 + %1496 = OpExtInst %float %1 Log %1495 + %1497 = OpFDiv %float %1496 %1065 + %1498 = OpFOrdLessThanEqual %bool %1497 %1423 + OpSelectionMerge %1499 None + OpBranchConditional %1498 %1500 %1501 + %1501 = OpLabel + %1502 = OpFOrdGreaterThan %bool %1497 %1423 + %1503 = OpExtInst %float %1 Log %float_0_180000007 + %1504 = OpFDiv %float %1503 %1065 + %1505 = OpFOrdLessThan %bool %1497 %1504 + %1506 = OpLogicalAnd %bool %1502 %1505 + OpSelectionMerge %1507 None + OpBranchConditional %1506 %1508 %1509 + %1509 = OpLabel + %1510 = OpFOrdGreaterThanEqual %bool %1497 %1504 + %1511 = OpExtInst %float %1 Log %1416 + %1512 = OpFDiv %float %1511 %1065 + %1513 = OpFOrdLessThan %bool %1497 %1512 + %1514 = OpLogicalAnd %bool %1510 %1513 + OpSelectionMerge %1515 None + OpBranchConditional %1514 %1516 %1517 + %1517 = OpLabel + %1518 = OpExtInst %float %1 Log %float_10000 + %1519 = OpFDiv %float %1518 %1065 + OpBranch %1515 + %1516 = OpLabel + %1520 = OpFSub %float %1497 %1504 + %1521 = OpFMul %float %float_3 %1520 + %1522 = OpFSub %float %1512 %1504 + %1523 = OpFDiv %float %1521 %1522 + %1524 = OpConvertFToS %int %1523 + %1525 = OpConvertSToF %float %1524 + %1526 = OpFSub %float %1523 %1525 + %1527 = OpAccessChain %_ptr_Function_float %503 %1524 + %1528 = OpLoad %float %1527 + %1529 = OpIAdd %int %1524 %int_1 + %1530 = OpAccessChain %_ptr_Function_float %503 %1529 + %1531 = OpLoad %float %1530 + %1532 = OpIAdd %int %1524 %int_2 + %1533 = OpAccessChain %_ptr_Function_float %503 %1532 + %1534 = OpLoad %float %1533 + %1535 = OpCompositeConstruct %v3float %1528 %1531 %1534 + %1536 = OpFMul %float %1526 %1526 + %1537 = OpCompositeConstruct %v3float %1536 %1526 %float_1 + %1538 = OpMatrixTimesVector %v3float %442 %1535 + %1539 = OpDot %float %1537 %1538 + OpBranch %1515 + %1515 = OpLabel + %1540 = OpPhi %float %1519 %1517 %1539 %1516 + OpBranch %1507 + %1508 = OpLabel + %1541 = OpFSub %float %1497 %1423 + %1542 = OpFMul %float %float_3 %1541 + %1543 = OpFSub %float %1504 %1423 + %1544 = OpFDiv %float %1542 %1543 + %1545 = OpConvertFToS %int %1544 + %1546 = OpConvertSToF %float %1545 + %1547 = OpFSub %float %1544 %1546 + %1548 = OpAccessChain %_ptr_Function_float %504 %1545 + %1549 = OpLoad %float %1548 + %1550 = OpIAdd %int %1545 %int_1 + %1551 = OpAccessChain %_ptr_Function_float %504 %1550 + %1552 = OpLoad %float %1551 + %1553 = OpIAdd %int %1545 %int_2 + %1554 = OpAccessChain %_ptr_Function_float %504 %1553 + %1555 = OpLoad %float %1554 + %1556 = OpCompositeConstruct %v3float %1549 %1552 %1555 + %1557 = OpFMul %float %1547 %1547 + %1558 = OpCompositeConstruct %v3float %1557 %1547 %float_1 + %1559 = OpMatrixTimesVector %v3float %442 %1556 + %1560 = OpDot %float %1558 %1559 + OpBranch %1507 + %1507 = OpLabel + %1561 = OpPhi %float %1540 %1515 %1560 %1508 + OpBranch %1499 + %1500 = OpLabel + %1562 = OpExtInst %float %1 Log %float_9_99999975en05 + %1563 = OpFDiv %float %1562 %1065 + OpBranch %1499 + %1499 = OpLabel + %1564 = OpPhi %float %1561 %1507 %1563 %1500 + %1565 = OpExtInst %float %1 Pow %float_10 %1564 + %1566 = OpCompositeInsert %v3float %1565 %1492 1 + %1567 = OpCompositeExtract %float %1411 2 + OpStore %506 %475 + OpStore %505 %476 + %1568 = OpFOrdLessThanEqual %bool %1567 %float_0 + %1569 = OpSelect %float %1568 %1418 %1567 + %1570 = OpExtInst %float %1 Log %1569 + %1571 = OpFDiv %float %1570 %1065 + %1572 = OpFOrdLessThanEqual %bool %1571 %1423 + OpSelectionMerge %1573 None + OpBranchConditional %1572 %1574 %1575 + %1575 = OpLabel + %1576 = OpFOrdGreaterThan %bool %1571 %1423 + %1577 = OpExtInst %float %1 Log %float_0_180000007 + %1578 = OpFDiv %float %1577 %1065 + %1579 = OpFOrdLessThan %bool %1571 %1578 + %1580 = OpLogicalAnd %bool %1576 %1579 + OpSelectionMerge %1581 None + OpBranchConditional %1580 %1582 %1583 + %1583 = OpLabel + %1584 = OpFOrdGreaterThanEqual %bool %1571 %1578 + %1585 = OpExtInst %float %1 Log %1416 + %1586 = OpFDiv %float %1585 %1065 + %1587 = OpFOrdLessThan %bool %1571 %1586 + %1588 = OpLogicalAnd %bool %1584 %1587 + OpSelectionMerge %1589 None + OpBranchConditional %1588 %1590 %1591 + %1591 = OpLabel + %1592 = OpExtInst %float %1 Log %float_10000 + %1593 = OpFDiv %float %1592 %1065 + OpBranch %1589 + %1590 = OpLabel + %1594 = OpFSub %float %1571 %1578 + %1595 = OpFMul %float %float_3 %1594 + %1596 = OpFSub %float %1586 %1578 + %1597 = OpFDiv %float %1595 %1596 + %1598 = OpConvertFToS %int %1597 + %1599 = OpConvertSToF %float %1598 + %1600 = OpFSub %float %1597 %1599 + %1601 = OpAccessChain %_ptr_Function_float %505 %1598 + %1602 = OpLoad %float %1601 + %1603 = OpIAdd %int %1598 %int_1 + %1604 = OpAccessChain %_ptr_Function_float %505 %1603 + %1605 = OpLoad %float %1604 + %1606 = OpIAdd %int %1598 %int_2 + %1607 = OpAccessChain %_ptr_Function_float %505 %1606 + %1608 = OpLoad %float %1607 + %1609 = OpCompositeConstruct %v3float %1602 %1605 %1608 + %1610 = OpFMul %float %1600 %1600 + %1611 = OpCompositeConstruct %v3float %1610 %1600 %float_1 + %1612 = OpMatrixTimesVector %v3float %442 %1609 + %1613 = OpDot %float %1611 %1612 + OpBranch %1589 + %1589 = OpLabel + %1614 = OpPhi %float %1593 %1591 %1613 %1590 + OpBranch %1581 + %1582 = OpLabel + %1615 = OpFSub %float %1571 %1423 + %1616 = OpFMul %float %float_3 %1615 + %1617 = OpFSub %float %1578 %1423 + %1618 = OpFDiv %float %1616 %1617 + %1619 = OpConvertFToS %int %1618 + %1620 = OpConvertSToF %float %1619 + %1621 = OpFSub %float %1618 %1620 + %1622 = OpAccessChain %_ptr_Function_float %506 %1619 + %1623 = OpLoad %float %1622 + %1624 = OpIAdd %int %1619 %int_1 + %1625 = OpAccessChain %_ptr_Function_float %506 %1624 + %1626 = OpLoad %float %1625 + %1627 = OpIAdd %int %1619 %int_2 + %1628 = OpAccessChain %_ptr_Function_float %506 %1627 + %1629 = OpLoad %float %1628 + %1630 = OpCompositeConstruct %v3float %1623 %1626 %1629 + %1631 = OpFMul %float %1621 %1621 + %1632 = OpCompositeConstruct %v3float %1631 %1621 %float_1 + %1633 = OpMatrixTimesVector %v3float %442 %1630 + %1634 = OpDot %float %1632 %1633 + OpBranch %1581 + %1581 = OpLabel + %1635 = OpPhi %float %1614 %1589 %1634 %1582 + OpBranch %1573 + %1574 = OpLabel + %1636 = OpExtInst %float %1 Log %float_9_99999975en05 + %1637 = OpFDiv %float %1636 %1065 + OpBranch %1573 + %1573 = OpLabel + %1638 = OpPhi %float %1635 %1581 %1637 %1574 + %1639 = OpExtInst %float %1 Pow %float_10 %1638 + %1640 = OpCompositeInsert %v3float %1639 %1566 2 + %1641 = OpVectorTimesMatrix %v3float %1640 %414 + %1642 = OpVectorTimesMatrix %v3float %1641 %410 + %1643 = OpExtInst %float %1 Pow %float_2 %float_n12 + %1644 = OpFMul %float %float_0_179999992 %1643 + OpStore %514 %475 + OpStore %513 %476 + %1645 = OpFOrdLessThanEqual %bool %1644 %float_0 + %1646 = OpSelect %float %1645 %1418 %1644 + %1647 = OpExtInst %float %1 Log %1646 + %1648 = OpFDiv %float %1647 %1065 + %1649 = OpFOrdLessThanEqual %bool %1648 %1423 + OpSelectionMerge %1650 None + OpBranchConditional %1649 %1651 %1652 + %1652 = OpLabel + %1653 = OpFOrdGreaterThan %bool %1648 %1423 + %1654 = OpExtInst %float %1 Log %float_0_180000007 + %1655 = OpFDiv %float %1654 %1065 + %1656 = OpFOrdLessThan %bool %1648 %1655 + %1657 = OpLogicalAnd %bool %1653 %1656 + OpSelectionMerge %1658 None + OpBranchConditional %1657 %1659 %1660 + %1660 = OpLabel + %1661 = OpFOrdGreaterThanEqual %bool %1648 %1655 + %1662 = OpExtInst %float %1 Log %1416 + %1663 = OpFDiv %float %1662 %1065 + %1664 = OpFOrdLessThan %bool %1648 %1663 + %1665 = OpLogicalAnd %bool %1661 %1664 + OpSelectionMerge %1666 None + OpBranchConditional %1665 %1667 %1668 + %1668 = OpLabel + %1669 = OpExtInst %float %1 Log %float_10000 + %1670 = OpFDiv %float %1669 %1065 + OpBranch %1666 + %1667 = OpLabel + %1671 = OpFSub %float %1648 %1655 + %1672 = OpFMul %float %float_3 %1671 + %1673 = OpFSub %float %1663 %1655 + %1674 = OpFDiv %float %1672 %1673 + %1675 = OpConvertFToS %int %1674 + %1676 = OpConvertSToF %float %1675 + %1677 = OpFSub %float %1674 %1676 + %1678 = OpAccessChain %_ptr_Function_float %513 %1675 + %1679 = OpLoad %float %1678 + %1680 = OpIAdd %int %1675 %int_1 + %1681 = OpAccessChain %_ptr_Function_float %513 %1680 + %1682 = OpLoad %float %1681 + %1683 = OpIAdd %int %1675 %int_2 + %1684 = OpAccessChain %_ptr_Function_float %513 %1683 + %1685 = OpLoad %float %1684 + %1686 = OpCompositeConstruct %v3float %1679 %1682 %1685 + %1687 = OpFMul %float %1677 %1677 + %1688 = OpCompositeConstruct %v3float %1687 %1677 %float_1 + %1689 = OpMatrixTimesVector %v3float %442 %1686 + %1690 = OpDot %float %1688 %1689 + OpBranch %1666 + %1666 = OpLabel + %1691 = OpPhi %float %1670 %1668 %1690 %1667 + OpBranch %1658 + %1659 = OpLabel + %1692 = OpFSub %float %1648 %1423 + %1693 = OpFMul %float %float_3 %1692 + %1694 = OpFSub %float %1655 %1423 + %1695 = OpFDiv %float %1693 %1694 + %1696 = OpConvertFToS %int %1695 + %1697 = OpConvertSToF %float %1696 + %1698 = OpFSub %float %1695 %1697 + %1699 = OpAccessChain %_ptr_Function_float %514 %1696 + %1700 = OpLoad %float %1699 + %1701 = OpIAdd %int %1696 %int_1 + %1702 = OpAccessChain %_ptr_Function_float %514 %1701 + %1703 = OpLoad %float %1702 + %1704 = OpIAdd %int %1696 %int_2 + %1705 = OpAccessChain %_ptr_Function_float %514 %1704 + %1706 = OpLoad %float %1705 + %1707 = OpCompositeConstruct %v3float %1700 %1703 %1706 + %1708 = OpFMul %float %1698 %1698 + %1709 = OpCompositeConstruct %v3float %1708 %1698 %float_1 + %1710 = OpMatrixTimesVector %v3float %442 %1707 + %1711 = OpDot %float %1709 %1710 + OpBranch %1658 + %1658 = OpLabel + %1712 = OpPhi %float %1691 %1666 %1711 %1659 + OpBranch %1650 + %1651 = OpLabel + %1713 = OpExtInst %float %1 Log %float_9_99999975en05 + %1714 = OpFDiv %float %1713 %1065 + OpBranch %1650 + %1650 = OpLabel + %1715 = OpPhi %float %1712 %1658 %1714 %1651 + %1716 = OpExtInst %float %1 Pow %float_10 %1715 + OpStore %516 %475 + OpStore %515 %476 + %1717 = OpExtInst %float %1 Log %float_0_180000007 + %1718 = OpFDiv %float %1717 %1065 + %1719 = OpFOrdLessThanEqual %bool %1718 %1423 + OpSelectionMerge %1720 None + OpBranchConditional %1719 %1721 %1722 + %1722 = OpLabel + %1723 = OpFOrdGreaterThan %bool %1718 %1423 + %1724 = OpFOrdLessThan %bool %1718 %1718 + %1725 = OpLogicalAnd %bool %1723 %1724 + OpSelectionMerge %1726 None + OpBranchConditional %1725 %1727 %1728 + %1728 = OpLabel + %1729 = OpFOrdGreaterThanEqual %bool %1718 %1718 + %1730 = OpExtInst %float %1 Log %1416 + %1731 = OpFDiv %float %1730 %1065 + %1732 = OpFOrdLessThan %bool %1718 %1731 + %1733 = OpLogicalAnd %bool %1729 %1732 + OpSelectionMerge %1734 None + OpBranchConditional %1733 %1735 %1736 + %1736 = OpLabel + %1737 = OpExtInst %float %1 Log %float_10000 + %1738 = OpFDiv %float %1737 %1065 + OpBranch %1734 + %1735 = OpLabel + %1739 = OpFSub %float %1718 %1718 + %1740 = OpFMul %float %float_3 %1739 + %1741 = OpFSub %float %1731 %1718 + %1742 = OpFDiv %float %1740 %1741 + %1743 = OpConvertFToS %int %1742 + %1744 = OpConvertSToF %float %1743 + %1745 = OpFSub %float %1742 %1744 + %1746 = OpAccessChain %_ptr_Function_float %515 %1743 + %1747 = OpLoad %float %1746 + %1748 = OpIAdd %int %1743 %int_1 + %1749 = OpAccessChain %_ptr_Function_float %515 %1748 + %1750 = OpLoad %float %1749 + %1751 = OpIAdd %int %1743 %int_2 + %1752 = OpAccessChain %_ptr_Function_float %515 %1751 + %1753 = OpLoad %float %1752 + %1754 = OpCompositeConstruct %v3float %1747 %1750 %1753 + %1755 = OpFMul %float %1745 %1745 + %1756 = OpCompositeConstruct %v3float %1755 %1745 %float_1 + %1757 = OpMatrixTimesVector %v3float %442 %1754 + %1758 = OpDot %float %1756 %1757 + OpBranch %1734 + %1734 = OpLabel + %1759 = OpPhi %float %1738 %1736 %1758 %1735 + OpBranch %1726 + %1727 = OpLabel + %1760 = OpFSub %float %1718 %1423 + %1761 = OpFMul %float %float_3 %1760 + %1762 = OpAccessChain %_ptr_Function_float %516 %int_3 + %1763 = OpLoad %float %1762 + %1764 = OpAccessChain %_ptr_Function_float %516 %int_4 + %1765 = OpLoad %float %1764 + %1766 = OpAccessChain %_ptr_Function_float %516 %int_5 + %1767 = OpLoad %float %1766 + %1768 = OpCompositeConstruct %v3float %1763 %1765 %1767 + %1769 = OpMatrixTimesVector %v3float %442 %1768 + %1770 = OpCompositeExtract %float %1769 2 + OpBranch %1726 + %1726 = OpLabel + %1771 = OpPhi %float %1759 %1734 %1770 %1727 + OpBranch %1720 + %1721 = OpLabel + %1772 = OpExtInst %float %1 Log %float_9_99999975en05 + %1773 = OpFDiv %float %1772 %1065 + OpBranch %1720 + %1720 = OpLabel + %1774 = OpPhi %float %1771 %1726 %1773 %1721 + %1775 = OpExtInst %float %1 Pow %float_10 %1774 + %1776 = OpExtInst %float %1 Pow %float_2 %float_11 + %1777 = OpFMul %float %float_0_179999992 %1776 + OpStore %518 %475 + OpStore %517 %476 + %1778 = OpFOrdLessThanEqual %bool %1777 %float_0 + %1779 = OpSelect %float %1778 %1418 %1777 + %1780 = OpExtInst %float %1 Log %1779 + %1781 = OpFDiv %float %1780 %1065 + %1782 = OpFOrdLessThanEqual %bool %1781 %1423 + OpSelectionMerge %1783 None + OpBranchConditional %1782 %1784 %1785 + %1785 = OpLabel + %1786 = OpFOrdGreaterThan %bool %1781 %1423 + %1787 = OpFOrdLessThan %bool %1781 %1718 + %1788 = OpLogicalAnd %bool %1786 %1787 + OpSelectionMerge %1789 None + OpBranchConditional %1788 %1790 %1791 + %1791 = OpLabel + %1792 = OpFOrdGreaterThanEqual %bool %1781 %1718 + %1793 = OpExtInst %float %1 Log %1416 + %1794 = OpFDiv %float %1793 %1065 + %1795 = OpFOrdLessThan %bool %1781 %1794 + %1796 = OpLogicalAnd %bool %1792 %1795 + OpSelectionMerge %1797 None + OpBranchConditional %1796 %1798 %1799 + %1799 = OpLabel + %1800 = OpExtInst %float %1 Log %float_10000 + %1801 = OpFDiv %float %1800 %1065 + OpBranch %1797 + %1798 = OpLabel + %1802 = OpFSub %float %1781 %1718 + %1803 = OpFMul %float %float_3 %1802 + %1804 = OpFSub %float %1794 %1718 + %1805 = OpFDiv %float %1803 %1804 + %1806 = OpConvertFToS %int %1805 + %1807 = OpConvertSToF %float %1806 + %1808 = OpFSub %float %1805 %1807 + %1809 = OpAccessChain %_ptr_Function_float %517 %1806 + %1810 = OpLoad %float %1809 + %1811 = OpIAdd %int %1806 %int_1 + %1812 = OpAccessChain %_ptr_Function_float %517 %1811 + %1813 = OpLoad %float %1812 + %1814 = OpIAdd %int %1806 %int_2 + %1815 = OpAccessChain %_ptr_Function_float %517 %1814 + %1816 = OpLoad %float %1815 + %1817 = OpCompositeConstruct %v3float %1810 %1813 %1816 + %1818 = OpFMul %float %1808 %1808 + %1819 = OpCompositeConstruct %v3float %1818 %1808 %float_1 + %1820 = OpMatrixTimesVector %v3float %442 %1817 + %1821 = OpDot %float %1819 %1820 + OpBranch %1797 + %1797 = OpLabel + %1822 = OpPhi %float %1801 %1799 %1821 %1798 + OpBranch %1789 + %1790 = OpLabel + %1823 = OpFSub %float %1781 %1423 + %1824 = OpFMul %float %float_3 %1823 + %1825 = OpFSub %float %1718 %1423 + %1826 = OpFDiv %float %1824 %1825 + %1827 = OpConvertFToS %int %1826 + %1828 = OpConvertSToF %float %1827 + %1829 = OpFSub %float %1826 %1828 + %1830 = OpAccessChain %_ptr_Function_float %518 %1827 + %1831 = OpLoad %float %1830 + %1832 = OpIAdd %int %1827 %int_1 + %1833 = OpAccessChain %_ptr_Function_float %518 %1832 + %1834 = OpLoad %float %1833 + %1835 = OpIAdd %int %1827 %int_2 + %1836 = OpAccessChain %_ptr_Function_float %518 %1835 + %1837 = OpLoad %float %1836 + %1838 = OpCompositeConstruct %v3float %1831 %1834 %1837 + %1839 = OpFMul %float %1829 %1829 + %1840 = OpCompositeConstruct %v3float %1839 %1829 %float_1 + %1841 = OpMatrixTimesVector %v3float %442 %1838 + %1842 = OpDot %float %1840 %1841 + OpBranch %1789 + %1789 = OpLabel + %1843 = OpPhi %float %1822 %1797 %1842 %1790 + OpBranch %1783 + %1784 = OpLabel + %1844 = OpExtInst %float %1 Log %float_9_99999975en05 + %1845 = OpFDiv %float %1844 %1065 + OpBranch %1783 + %1783 = OpLabel + %1846 = OpPhi %float %1843 %1789 %1845 %1784 + %1847 = OpExtInst %float %1 Pow %float_10 %1846 + %1848 = OpCompositeExtract %float %1642 0 + OpStore %512 %482 + OpStore %511 %483 + %1849 = OpFOrdLessThanEqual %bool %1848 %float_0 + %1850 = OpSelect %float %1849 %float_9_99999975en05 %1848 + %1851 = OpExtInst %float %1 Log %1850 + %1852 = OpFDiv %float %1851 %1065 + %1853 = OpExtInst %float %1 Log %1716 + %1854 = OpFDiv %float %1853 %1065 + %1855 = OpFOrdLessThanEqual %bool %1852 %1854 + OpSelectionMerge %1856 None + OpBranchConditional %1855 %1857 %1858 + %1858 = OpLabel + %1859 = OpFOrdGreaterThan %bool %1852 %1854 + %1860 = OpExtInst %float %1 Log %1775 + %1861 = OpFDiv %float %1860 %1065 + %1862 = OpFOrdLessThan %bool %1852 %1861 + %1863 = OpLogicalAnd %bool %1859 %1862 + OpSelectionMerge %1864 None + OpBranchConditional %1863 %1865 %1866 + %1866 = OpLabel + %1867 = OpFOrdGreaterThanEqual %bool %1852 %1861 + %1868 = OpExtInst %float %1 Log %1847 + %1869 = OpFDiv %float %1868 %1065 + %1870 = OpFOrdLessThan %bool %1852 %1869 + %1871 = OpLogicalAnd %bool %1867 %1870 + OpSelectionMerge %1872 None + OpBranchConditional %1871 %1873 %1874 + %1874 = OpLabel + %1875 = OpFMul %float %1852 %float_0_119999997 + %1876 = OpExtInst %float %1 Log %float_2000 + %1877 = OpFDiv %float %1876 %1065 + %1878 = OpFMul %float %float_0_119999997 %1868 + %1879 = OpFDiv %float %1878 %1065 + %1880 = OpFSub %float %1877 %1879 + %1881 = OpFAdd %float %1875 %1880 + OpBranch %1872 + %1873 = OpLabel + %1882 = OpFSub %float %1852 %1861 + %1883 = OpFMul %float %float_7 %1882 + %1884 = OpFSub %float %1869 %1861 + %1885 = OpFDiv %float %1883 %1884 + %1886 = OpConvertFToS %int %1885 + %1887 = OpConvertSToF %float %1886 + %1888 = OpFSub %float %1885 %1887 + %1889 = OpAccessChain %_ptr_Function_float %511 %1886 + %1890 = OpLoad %float %1889 + %1891 = OpIAdd %int %1886 %int_1 + %1892 = OpAccessChain %_ptr_Function_float %511 %1891 + %1893 = OpLoad %float %1892 + %1894 = OpIAdd %int %1886 %int_2 + %1895 = OpAccessChain %_ptr_Function_float %511 %1894 + %1896 = OpLoad %float %1895 + %1897 = OpCompositeConstruct %v3float %1890 %1893 %1896 + %1898 = OpFMul %float %1888 %1888 + %1899 = OpCompositeConstruct %v3float %1898 %1888 %float_1 + %1900 = OpMatrixTimesVector %v3float %442 %1897 + %1901 = OpDot %float %1899 %1900 + OpBranch %1872 + %1872 = OpLabel + %1902 = OpPhi %float %1881 %1874 %1901 %1873 + OpBranch %1864 + %1865 = OpLabel + %1903 = OpFSub %float %1852 %1854 + %1904 = OpFMul %float %float_7 %1903 + %1905 = OpFSub %float %1861 %1854 + %1906 = OpFDiv %float %1904 %1905 + %1907 = OpConvertFToS %int %1906 + %1908 = OpConvertSToF %float %1907 + %1909 = OpFSub %float %1906 %1908 + %1910 = OpAccessChain %_ptr_Function_float %512 %1907 + %1911 = OpLoad %float %1910 + %1912 = OpIAdd %int %1907 %int_1 + %1913 = OpAccessChain %_ptr_Function_float %512 %1912 + %1914 = OpLoad %float %1913 + %1915 = OpIAdd %int %1907 %int_2 + %1916 = OpAccessChain %_ptr_Function_float %512 %1915 + %1917 = OpLoad %float %1916 + %1918 = OpCompositeConstruct %v3float %1911 %1914 %1917 + %1919 = OpFMul %float %1909 %1909 + %1920 = OpCompositeConstruct %v3float %1919 %1909 %float_1 + %1921 = OpMatrixTimesVector %v3float %442 %1918 + %1922 = OpDot %float %1920 %1921 + OpBranch %1864 + %1864 = OpLabel + %1923 = OpPhi %float %1902 %1872 %1922 %1865 + OpBranch %1856 + %1857 = OpLabel + %1924 = OpExtInst %float %1 Log %float_0_00499999989 + %1925 = OpFDiv %float %1924 %1065 + OpBranch %1856 + %1856 = OpLabel + %1926 = OpPhi %float %1923 %1864 %1925 %1857 + %1927 = OpExtInst %float %1 Pow %float_10 %1926 + %1928 = OpCompositeInsert %v3float %1927 %391 0 + %1929 = OpCompositeExtract %float %1642 1 + OpStore %510 %482 + OpStore %509 %483 + %1930 = OpFOrdLessThanEqual %bool %1929 %float_0 + %1931 = OpSelect %float %1930 %float_9_99999975en05 %1929 + %1932 = OpExtInst %float %1 Log %1931 + %1933 = OpFDiv %float %1932 %1065 + %1934 = OpFOrdLessThanEqual %bool %1933 %1854 + OpSelectionMerge %1935 None + OpBranchConditional %1934 %1936 %1937 + %1937 = OpLabel + %1938 = OpFOrdGreaterThan %bool %1933 %1854 + %1939 = OpExtInst %float %1 Log %1775 + %1940 = OpFDiv %float %1939 %1065 + %1941 = OpFOrdLessThan %bool %1933 %1940 + %1942 = OpLogicalAnd %bool %1938 %1941 + OpSelectionMerge %1943 None + OpBranchConditional %1942 %1944 %1945 + %1945 = OpLabel + %1946 = OpFOrdGreaterThanEqual %bool %1933 %1940 + %1947 = OpExtInst %float %1 Log %1847 + %1948 = OpFDiv %float %1947 %1065 + %1949 = OpFOrdLessThan %bool %1933 %1948 + %1950 = OpLogicalAnd %bool %1946 %1949 + OpSelectionMerge %1951 None + OpBranchConditional %1950 %1952 %1953 + %1953 = OpLabel + %1954 = OpFMul %float %1933 %float_0_119999997 + %1955 = OpExtInst %float %1 Log %float_2000 + %1956 = OpFDiv %float %1955 %1065 + %1957 = OpFMul %float %float_0_119999997 %1947 + %1958 = OpFDiv %float %1957 %1065 + %1959 = OpFSub %float %1956 %1958 + %1960 = OpFAdd %float %1954 %1959 + OpBranch %1951 + %1952 = OpLabel + %1961 = OpFSub %float %1933 %1940 + %1962 = OpFMul %float %float_7 %1961 + %1963 = OpFSub %float %1948 %1940 + %1964 = OpFDiv %float %1962 %1963 + %1965 = OpConvertFToS %int %1964 + %1966 = OpConvertSToF %float %1965 + %1967 = OpFSub %float %1964 %1966 + %1968 = OpAccessChain %_ptr_Function_float %509 %1965 + %1969 = OpLoad %float %1968 + %1970 = OpIAdd %int %1965 %int_1 + %1971 = OpAccessChain %_ptr_Function_float %509 %1970 + %1972 = OpLoad %float %1971 + %1973 = OpIAdd %int %1965 %int_2 + %1974 = OpAccessChain %_ptr_Function_float %509 %1973 + %1975 = OpLoad %float %1974 + %1976 = OpCompositeConstruct %v3float %1969 %1972 %1975 + %1977 = OpFMul %float %1967 %1967 + %1978 = OpCompositeConstruct %v3float %1977 %1967 %float_1 + %1979 = OpMatrixTimesVector %v3float %442 %1976 + %1980 = OpDot %float %1978 %1979 + OpBranch %1951 + %1951 = OpLabel + %1981 = OpPhi %float %1960 %1953 %1980 %1952 + OpBranch %1943 + %1944 = OpLabel + %1982 = OpFSub %float %1933 %1854 + %1983 = OpFMul %float %float_7 %1982 + %1984 = OpFSub %float %1940 %1854 + %1985 = OpFDiv %float %1983 %1984 + %1986 = OpConvertFToS %int %1985 + %1987 = OpConvertSToF %float %1986 + %1988 = OpFSub %float %1985 %1987 + %1989 = OpAccessChain %_ptr_Function_float %510 %1986 + %1990 = OpLoad %float %1989 + %1991 = OpIAdd %int %1986 %int_1 + %1992 = OpAccessChain %_ptr_Function_float %510 %1991 + %1993 = OpLoad %float %1992 + %1994 = OpIAdd %int %1986 %int_2 + %1995 = OpAccessChain %_ptr_Function_float %510 %1994 + %1996 = OpLoad %float %1995 + %1997 = OpCompositeConstruct %v3float %1990 %1993 %1996 + %1998 = OpFMul %float %1988 %1988 + %1999 = OpCompositeConstruct %v3float %1998 %1988 %float_1 + %2000 = OpMatrixTimesVector %v3float %442 %1997 + %2001 = OpDot %float %1999 %2000 + OpBranch %1943 + %1943 = OpLabel + %2002 = OpPhi %float %1981 %1951 %2001 %1944 + OpBranch %1935 + %1936 = OpLabel + %2003 = OpExtInst %float %1 Log %float_0_00499999989 + %2004 = OpFDiv %float %2003 %1065 + OpBranch %1935 + %1935 = OpLabel + %2005 = OpPhi %float %2002 %1943 %2004 %1936 + %2006 = OpExtInst %float %1 Pow %float_10 %2005 + %2007 = OpCompositeInsert %v3float %2006 %1928 1 + %2008 = OpCompositeExtract %float %1642 2 + OpStore %508 %482 + OpStore %507 %483 + %2009 = OpFOrdLessThanEqual %bool %2008 %float_0 + %2010 = OpSelect %float %2009 %float_9_99999975en05 %2008 + %2011 = OpExtInst %float %1 Log %2010 + %2012 = OpFDiv %float %2011 %1065 + %2013 = OpFOrdLessThanEqual %bool %2012 %1854 + OpSelectionMerge %2014 None + OpBranchConditional %2013 %2015 %2016 + %2016 = OpLabel + %2017 = OpFOrdGreaterThan %bool %2012 %1854 + %2018 = OpExtInst %float %1 Log %1775 + %2019 = OpFDiv %float %2018 %1065 + %2020 = OpFOrdLessThan %bool %2012 %2019 + %2021 = OpLogicalAnd %bool %2017 %2020 + OpSelectionMerge %2022 None + OpBranchConditional %2021 %2023 %2024 + %2024 = OpLabel + %2025 = OpFOrdGreaterThanEqual %bool %2012 %2019 + %2026 = OpExtInst %float %1 Log %1847 + %2027 = OpFDiv %float %2026 %1065 + %2028 = OpFOrdLessThan %bool %2012 %2027 + %2029 = OpLogicalAnd %bool %2025 %2028 + OpSelectionMerge %2030 None + OpBranchConditional %2029 %2031 %2032 + %2032 = OpLabel + %2033 = OpFMul %float %2012 %float_0_119999997 + %2034 = OpExtInst %float %1 Log %float_2000 + %2035 = OpFDiv %float %2034 %1065 + %2036 = OpFMul %float %float_0_119999997 %2026 + %2037 = OpFDiv %float %2036 %1065 + %2038 = OpFSub %float %2035 %2037 + %2039 = OpFAdd %float %2033 %2038 + OpBranch %2030 + %2031 = OpLabel + %2040 = OpFSub %float %2012 %2019 + %2041 = OpFMul %float %float_7 %2040 + %2042 = OpFSub %float %2027 %2019 + %2043 = OpFDiv %float %2041 %2042 + %2044 = OpConvertFToS %int %2043 + %2045 = OpConvertSToF %float %2044 + %2046 = OpFSub %float %2043 %2045 + %2047 = OpAccessChain %_ptr_Function_float %507 %2044 + %2048 = OpLoad %float %2047 + %2049 = OpIAdd %int %2044 %int_1 + %2050 = OpAccessChain %_ptr_Function_float %507 %2049 + %2051 = OpLoad %float %2050 + %2052 = OpIAdd %int %2044 %int_2 + %2053 = OpAccessChain %_ptr_Function_float %507 %2052 + %2054 = OpLoad %float %2053 + %2055 = OpCompositeConstruct %v3float %2048 %2051 %2054 + %2056 = OpFMul %float %2046 %2046 + %2057 = OpCompositeConstruct %v3float %2056 %2046 %float_1 + %2058 = OpMatrixTimesVector %v3float %442 %2055 + %2059 = OpDot %float %2057 %2058 + OpBranch %2030 + %2030 = OpLabel + %2060 = OpPhi %float %2039 %2032 %2059 %2031 + OpBranch %2022 + %2023 = OpLabel + %2061 = OpFSub %float %2012 %1854 + %2062 = OpFMul %float %float_7 %2061 + %2063 = OpFSub %float %2019 %1854 + %2064 = OpFDiv %float %2062 %2063 + %2065 = OpConvertFToS %int %2064 + %2066 = OpConvertSToF %float %2065 + %2067 = OpFSub %float %2064 %2066 + %2068 = OpAccessChain %_ptr_Function_float %508 %2065 + %2069 = OpLoad %float %2068 + %2070 = OpIAdd %int %2065 %int_1 + %2071 = OpAccessChain %_ptr_Function_float %508 %2070 + %2072 = OpLoad %float %2071 + %2073 = OpIAdd %int %2065 %int_2 + %2074 = OpAccessChain %_ptr_Function_float %508 %2073 + %2075 = OpLoad %float %2074 + %2076 = OpCompositeConstruct %v3float %2069 %2072 %2075 + %2077 = OpFMul %float %2067 %2067 + %2078 = OpCompositeConstruct %v3float %2077 %2067 %float_1 + %2079 = OpMatrixTimesVector %v3float %442 %2076 + %2080 = OpDot %float %2078 %2079 + OpBranch %2022 + %2022 = OpLabel + %2081 = OpPhi %float %2060 %2030 %2080 %2023 + OpBranch %2014 + %2015 = OpLabel + %2082 = OpExtInst %float %1 Log %float_0_00499999989 + %2083 = OpFDiv %float %2082 %1065 + OpBranch %2014 + %2014 = OpLabel + %2084 = OpPhi %float %2081 %2022 %2083 %2015 + %2085 = OpExtInst %float %1 Pow %float_10 %2084 + %2086 = OpCompositeInsert %v3float %2085 %2007 2 + %2087 = OpVectorTimesMatrix %v3float %2086 %576 + %2088 = OpFMul %v3float %2087 %496 + %2089 = OpExtInst %v3float %1 Pow %2088 %263 + %2090 = OpFMul %v3float %184 %2089 + %2091 = OpFAdd %v3float %183 %2090 + %2092 = OpFMul %v3float %185 %2089 + %2093 = OpFAdd %v3float %135 %2092 + %2094 = OpFDiv %v3float %135 %2093 + %2095 = OpFMul %v3float %2091 %2094 + %2096 = OpExtInst %v3float %1 Pow %2095 %264 + OpBranch %1236 + %1236 = OpLabel + %2097 = OpPhi %v3float %1260 %1240 %2096 %2014 + OpBranch %1230 + %1231 = OpLabel + %2098 = OpMatrixTimesMatrix %mat3v3float %546 %399 + %2099 = OpFMul %v3float %906 %262 + %2100 = OpVectorTimesMatrix %v3float %2099 %2098 + %2101 = OpCompositeExtract %float %2100 0 + %2102 = OpCompositeExtract %float %2100 1 + %2103 = OpExtInst %float %1 FMin %2101 %2102 + %2104 = OpCompositeExtract %float %2100 2 + %2105 = OpExtInst %float %1 FMin %2103 %2104 + %2106 = OpExtInst %float %1 FMax %2101 %2102 + %2107 = OpExtInst %float %1 FMax %2106 %2104 + %2108 = OpExtInst %float %1 FMax %2107 %float_1_00000001en10 + %2109 = OpExtInst %float %1 FMax %2105 %float_1_00000001en10 + %2110 = OpFSub %float %2108 %2109 + %2111 = OpExtInst %float %1 FMax %2107 %float_0_00999999978 + %2112 = OpFDiv %float %2110 %2111 + %2113 = OpFSub %float %2104 %2102 + %2114 = OpFMul %float %2104 %2113 + %2115 = OpFSub %float %2102 %2101 + %2116 = OpFMul %float %2102 %2115 + %2117 = OpFAdd %float %2114 %2116 + %2118 = OpFSub %float %2101 %2104 + %2119 = OpFMul %float %2101 %2118 + %2120 = OpFAdd %float %2117 %2119 + %2121 = OpExtInst %float %1 Sqrt %2120 + %2122 = OpFAdd %float %2104 %2102 + %2123 = OpFAdd %float %2122 %2101 + %2124 = OpFMul %float %float_1_75 %2121 + %2125 = OpFAdd %float %2123 %2124 + %2126 = OpFMul %float %2125 %float_0_333333343 + %2127 = OpFSub %float %2112 %float_0_400000006 + %2128 = OpFMul %float %2127 %float_5 + %2129 = OpFMul %float %2127 %float_2_5 + %2130 = OpExtInst %float %1 FAbs %2129 + %2131 = OpFSub %float %float_1 %2130 + %2132 = OpExtInst %float %1 FMax %2131 %float_0 + %2133 = OpExtInst %float %1 FSign %2128 + %2134 = OpConvertFToS %int %2133 + %2135 = OpConvertSToF %float %2134 + %2136 = OpFMul %float %2132 %2132 + %2137 = OpFSub %float %float_1 %2136 + %2138 = OpFMul %float %2135 %2137 + %2139 = OpFAdd %float %float_1 %2138 + %2140 = OpFMul %float %2139 %float_0_0250000004 + %2141 = OpFOrdLessThanEqual %bool %2126 %float_0_0533333346 + OpSelectionMerge %2142 None + OpBranchConditional %2141 %2143 %2144 + %2144 = OpLabel + %2145 = OpFOrdGreaterThanEqual %bool %2126 %float_0_159999996 + OpSelectionMerge %2146 None + OpBranchConditional %2145 %2147 %2148 + %2148 = OpLabel + %2149 = OpFDiv %float %float_0_239999995 %2125 + %2150 = OpFSub %float %2149 %float_0_5 + %2151 = OpFMul %float %2140 %2150 + OpBranch %2146 + %2147 = OpLabel + OpBranch %2146 + %2146 = OpLabel + %2152 = OpPhi %float %2151 %2148 %float_0 %2147 + OpBranch %2142 + %2143 = OpLabel + OpBranch %2142 + %2142 = OpLabel + %2153 = OpPhi %float %2152 %2146 %2140 %2143 + %2154 = OpFAdd %float %float_1 %2153 + %2155 = OpCompositeConstruct %v3float %2154 %2154 %2154 + %2156 = OpFMul %v3float %2100 %2155 + %2157 = OpCompositeExtract %float %2156 0 + %2158 = OpCompositeExtract %float %2156 1 + %2159 = OpFOrdEqual %bool %2157 %2158 + %2160 = OpCompositeExtract %float %2156 2 + %2161 = OpFOrdEqual %bool %2158 %2160 + %2162 = OpLogicalAnd %bool %2159 %2161 + OpSelectionMerge %2163 None + OpBranchConditional %2162 %2164 %2165 + %2165 = OpLabel + %2166 = OpExtInst %float %1 Sqrt %float_3 + %2167 = OpFSub %float %2158 %2160 + %2168 = OpFMul %float %2166 %2167 + %2169 = OpFMul %float %float_2 %2157 + %2170 = OpFSub %float %2169 %2158 + %2171 = OpFSub %float %2170 %2160 + %2172 = OpExtInst %float %1 Atan2 %2168 %2171 + %2173 = OpFMul %float %float_57_2957764 %2172 + OpBranch %2163 + %2164 = OpLabel + OpBranch %2163 + %2163 = OpLabel + %2174 = OpPhi %float %2173 %2165 %float_0 %2164 + %2175 = OpFOrdLessThan %bool %2174 %float_0 + OpSelectionMerge %2176 None + OpBranchConditional %2175 %2177 %2176 + %2177 = OpLabel + %2178 = OpFAdd %float %2174 %float_360 + OpBranch %2176 + %2176 = OpLabel + %2179 = OpPhi %float %2174 %2163 %2178 %2177 + %2180 = OpExtInst %float %1 FClamp %2179 %float_0 %float_360 + %2181 = OpFOrdGreaterThan %bool %2180 %float_180 + OpSelectionMerge %2182 None + OpBranchConditional %2181 %2183 %2182 + %2183 = OpLabel + %2184 = OpFSub %float %2180 %float_360 + OpBranch %2182 + %2182 = OpLabel + %2185 = OpPhi %float %2180 %2176 %2184 %2183 + %2186 = OpFOrdGreaterThan %bool %2185 %float_n67_5 + %2187 = OpFOrdLessThan %bool %2185 %float_67_5 + %2188 = OpLogicalAnd %bool %2186 %2187 + OpSelectionMerge %2189 None + OpBranchConditional %2188 %2190 %2189 + %2190 = OpLabel + %2191 = OpFSub %float %2185 %float_n67_5 + %2192 = OpFMul %float %2191 %float_0_0296296291 + %2193 = OpConvertFToS %int %2192 + %2194 = OpConvertSToF %float %2193 + %2195 = OpFSub %float %2192 %2194 + %2196 = OpFMul %float %2195 %2195 + %2197 = OpFMul %float %2196 %2195 + %2198 = OpIEqual %bool %2193 %int_3 + OpSelectionMerge %2199 None + OpBranchConditional %2198 %2200 %2201 + %2201 = OpLabel + %2202 = OpIEqual %bool %2193 %int_2 + OpSelectionMerge %2203 None + OpBranchConditional %2202 %2204 %2205 + %2205 = OpLabel + %2206 = OpIEqual %bool %2193 %int_1 + OpSelectionMerge %2207 None + OpBranchConditional %2206 %2208 %2209 + %2209 = OpLabel + %2210 = OpIEqual %bool %2193 %int_0 + OpSelectionMerge %2211 None + OpBranchConditional %2210 %2212 %2213 + %2213 = OpLabel + OpBranch %2211 + %2212 = OpLabel + %2214 = OpFMul %float %2197 %float_0_166666672 + OpBranch %2211 + %2211 = OpLabel + %2215 = OpPhi %float %float_0 %2213 %2214 %2212 + OpBranch %2207 + %2208 = OpLabel + %2216 = OpFMul %float %2197 %float_n0_5 + %2217 = OpFMul %float %2196 %float_0_5 + %2218 = OpFAdd %float %2216 %2217 + %2219 = OpFMul %float %2195 %float_0_5 + %2220 = OpFAdd %float %2218 %2219 + %2221 = OpFAdd %float %2220 %float_0_166666672 + OpBranch %2207 + %2207 = OpLabel + %2222 = OpPhi %float %2215 %2211 %2221 %2208 + OpBranch %2203 + %2204 = OpLabel + %2223 = OpFMul %float %2197 %float_0_5 + %2224 = OpFMul %float %2196 %float_n1 + %2225 = OpFAdd %float %2223 %2224 + %2226 = OpFAdd %float %2225 %float_0_666666687 + OpBranch %2203 + %2203 = OpLabel + %2227 = OpPhi %float %2222 %2207 %2226 %2204 + OpBranch %2199 + %2200 = OpLabel + %2228 = OpFMul %float %2197 %float_n0_166666672 + %2229 = OpFMul %float %2196 %float_0_5 + %2230 = OpFAdd %float %2228 %2229 + %2231 = OpFMul %float %2195 %float_n0_5 + %2232 = OpFAdd %float %2230 %2231 + %2233 = OpFAdd %float %2232 %float_0_166666672 + OpBranch %2199 + %2199 = OpLabel + %2234 = OpPhi %float %2227 %2203 %2233 %2200 + OpBranch %2189 + %2189 = OpLabel + %2235 = OpPhi %float %float_0 %2182 %2234 %2199 + %2236 = OpFMul %float %2235 %float_1_5 + %2237 = OpFMul %float %2236 %2112 + %2238 = OpFSub %float %float_0_0299999993 %2157 + %2239 = OpFMul %float %2237 %2238 + %2240 = OpFMul %float %2239 %float_0_180000007 + %2241 = OpFAdd %float %2157 %2240 + %2242 = OpCompositeInsert %v3float %2241 %2156 0 + %2243 = OpExtInst %v3float %1 FClamp %2242 %132 %314 + %2244 = OpVectorTimesMatrix %v3float %2243 %410 + %2245 = OpExtInst %v3float %1 FClamp %2244 %132 %314 + %2246 = OpDot %float %2245 %67 + %2247 = OpCompositeConstruct %v3float %2246 %2246 %2246 + %2248 = OpExtInst %v3float %1 FMix %2247 %2245 %228 + %2249 = OpCompositeExtract %float %2248 0 + %2250 = OpExtInst %float %1 Exp2 %float_n15 + %2251 = OpFMul %float %float_0_179999992 %2250 + %2252 = OpExtInst %float %1 Exp2 %float_18 + %2253 = OpFMul %float %float_0_179999992 %2252 + OpStore %520 %475 + OpStore %519 %476 + %2254 = OpFOrdLessThanEqual %bool %2249 %float_0 + %2255 = OpExtInst %float %1 Exp2 %float_n14 + %2256 = OpSelect %float %2254 %2255 %2249 + %2257 = OpExtInst %float %1 Log %2256 + %2258 = OpFDiv %float %2257 %1065 + %2259 = OpExtInst %float %1 Log %2251 + %2260 = OpFDiv %float %2259 %1065 + %2261 = OpFOrdLessThanEqual %bool %2258 %2260 + OpSelectionMerge %2262 None + OpBranchConditional %2261 %2263 %2264 + %2264 = OpLabel + %2265 = OpFOrdGreaterThan %bool %2258 %2260 + %2266 = OpExtInst %float %1 Log %float_0_180000007 + %2267 = OpFDiv %float %2266 %1065 + %2268 = OpFOrdLessThan %bool %2258 %2267 + %2269 = OpLogicalAnd %bool %2265 %2268 + OpSelectionMerge %2270 None + OpBranchConditional %2269 %2271 %2272 + %2272 = OpLabel + %2273 = OpFOrdGreaterThanEqual %bool %2258 %2267 + %2274 = OpExtInst %float %1 Log %2253 + %2275 = OpFDiv %float %2274 %1065 + %2276 = OpFOrdLessThan %bool %2258 %2275 + %2277 = OpLogicalAnd %bool %2273 %2276 + OpSelectionMerge %2278 None + OpBranchConditional %2277 %2279 %2280 + %2280 = OpLabel + %2281 = OpExtInst %float %1 Log %float_10000 + %2282 = OpFDiv %float %2281 %1065 + OpBranch %2278 + %2279 = OpLabel + %2283 = OpFSub %float %2258 %2267 + %2284 = OpFMul %float %float_3 %2283 + %2285 = OpFSub %float %2275 %2267 + %2286 = OpFDiv %float %2284 %2285 + %2287 = OpConvertFToS %int %2286 + %2288 = OpConvertSToF %float %2287 + %2289 = OpFSub %float %2286 %2288 + %2290 = OpAccessChain %_ptr_Function_float %519 %2287 + %2291 = OpLoad %float %2290 + %2292 = OpIAdd %int %2287 %int_1 + %2293 = OpAccessChain %_ptr_Function_float %519 %2292 + %2294 = OpLoad %float %2293 + %2295 = OpIAdd %int %2287 %int_2 + %2296 = OpAccessChain %_ptr_Function_float %519 %2295 + %2297 = OpLoad %float %2296 + %2298 = OpCompositeConstruct %v3float %2291 %2294 %2297 + %2299 = OpFMul %float %2289 %2289 + %2300 = OpCompositeConstruct %v3float %2299 %2289 %float_1 + %2301 = OpMatrixTimesVector %v3float %442 %2298 + %2302 = OpDot %float %2300 %2301 + OpBranch %2278 + %2278 = OpLabel + %2303 = OpPhi %float %2282 %2280 %2302 %2279 + OpBranch %2270 + %2271 = OpLabel + %2304 = OpFSub %float %2258 %2260 + %2305 = OpFMul %float %float_3 %2304 + %2306 = OpFSub %float %2267 %2260 + %2307 = OpFDiv %float %2305 %2306 + %2308 = OpConvertFToS %int %2307 + %2309 = OpConvertSToF %float %2308 + %2310 = OpFSub %float %2307 %2309 + %2311 = OpAccessChain %_ptr_Function_float %520 %2308 + %2312 = OpLoad %float %2311 + %2313 = OpIAdd %int %2308 %int_1 + %2314 = OpAccessChain %_ptr_Function_float %520 %2313 + %2315 = OpLoad %float %2314 + %2316 = OpIAdd %int %2308 %int_2 + %2317 = OpAccessChain %_ptr_Function_float %520 %2316 + %2318 = OpLoad %float %2317 + %2319 = OpCompositeConstruct %v3float %2312 %2315 %2318 + %2320 = OpFMul %float %2310 %2310 + %2321 = OpCompositeConstruct %v3float %2320 %2310 %float_1 + %2322 = OpMatrixTimesVector %v3float %442 %2319 + %2323 = OpDot %float %2321 %2322 + OpBranch %2270 + %2270 = OpLabel + %2324 = OpPhi %float %2303 %2278 %2323 %2271 + OpBranch %2262 + %2263 = OpLabel + %2325 = OpExtInst %float %1 Log %float_9_99999975en05 + %2326 = OpFDiv %float %2325 %1065 + OpBranch %2262 + %2262 = OpLabel + %2327 = OpPhi %float %2324 %2270 %2326 %2263 + %2328 = OpExtInst %float %1 Pow %float_10 %2327 + %2329 = OpCompositeInsert %v3float %2328 %391 0 + %2330 = OpCompositeExtract %float %2248 1 + OpStore %522 %475 + OpStore %521 %476 + %2331 = OpFOrdLessThanEqual %bool %2330 %float_0 + %2332 = OpSelect %float %2331 %2255 %2330 + %2333 = OpExtInst %float %1 Log %2332 + %2334 = OpFDiv %float %2333 %1065 + %2335 = OpFOrdLessThanEqual %bool %2334 %2260 + OpSelectionMerge %2336 None + OpBranchConditional %2335 %2337 %2338 + %2338 = OpLabel + %2339 = OpFOrdGreaterThan %bool %2334 %2260 + %2340 = OpExtInst %float %1 Log %float_0_180000007 + %2341 = OpFDiv %float %2340 %1065 + %2342 = OpFOrdLessThan %bool %2334 %2341 + %2343 = OpLogicalAnd %bool %2339 %2342 + OpSelectionMerge %2344 None + OpBranchConditional %2343 %2345 %2346 + %2346 = OpLabel + %2347 = OpFOrdGreaterThanEqual %bool %2334 %2341 + %2348 = OpExtInst %float %1 Log %2253 + %2349 = OpFDiv %float %2348 %1065 + %2350 = OpFOrdLessThan %bool %2334 %2349 + %2351 = OpLogicalAnd %bool %2347 %2350 + OpSelectionMerge %2352 None + OpBranchConditional %2351 %2353 %2354 + %2354 = OpLabel + %2355 = OpExtInst %float %1 Log %float_10000 + %2356 = OpFDiv %float %2355 %1065 + OpBranch %2352 + %2353 = OpLabel + %2357 = OpFSub %float %2334 %2341 + %2358 = OpFMul %float %float_3 %2357 + %2359 = OpFSub %float %2349 %2341 + %2360 = OpFDiv %float %2358 %2359 + %2361 = OpConvertFToS %int %2360 + %2362 = OpConvertSToF %float %2361 + %2363 = OpFSub %float %2360 %2362 + %2364 = OpAccessChain %_ptr_Function_float %521 %2361 + %2365 = OpLoad %float %2364 + %2366 = OpIAdd %int %2361 %int_1 + %2367 = OpAccessChain %_ptr_Function_float %521 %2366 + %2368 = OpLoad %float %2367 + %2369 = OpIAdd %int %2361 %int_2 + %2370 = OpAccessChain %_ptr_Function_float %521 %2369 + %2371 = OpLoad %float %2370 + %2372 = OpCompositeConstruct %v3float %2365 %2368 %2371 + %2373 = OpFMul %float %2363 %2363 + %2374 = OpCompositeConstruct %v3float %2373 %2363 %float_1 + %2375 = OpMatrixTimesVector %v3float %442 %2372 + %2376 = OpDot %float %2374 %2375 + OpBranch %2352 + %2352 = OpLabel + %2377 = OpPhi %float %2356 %2354 %2376 %2353 + OpBranch %2344 + %2345 = OpLabel + %2378 = OpFSub %float %2334 %2260 + %2379 = OpFMul %float %float_3 %2378 + %2380 = OpFSub %float %2341 %2260 + %2381 = OpFDiv %float %2379 %2380 + %2382 = OpConvertFToS %int %2381 + %2383 = OpConvertSToF %float %2382 + %2384 = OpFSub %float %2381 %2383 + %2385 = OpAccessChain %_ptr_Function_float %522 %2382 + %2386 = OpLoad %float %2385 + %2387 = OpIAdd %int %2382 %int_1 + %2388 = OpAccessChain %_ptr_Function_float %522 %2387 + %2389 = OpLoad %float %2388 + %2390 = OpIAdd %int %2382 %int_2 + %2391 = OpAccessChain %_ptr_Function_float %522 %2390 + %2392 = OpLoad %float %2391 + %2393 = OpCompositeConstruct %v3float %2386 %2389 %2392 + %2394 = OpFMul %float %2384 %2384 + %2395 = OpCompositeConstruct %v3float %2394 %2384 %float_1 + %2396 = OpMatrixTimesVector %v3float %442 %2393 + %2397 = OpDot %float %2395 %2396 + OpBranch %2344 + %2344 = OpLabel + %2398 = OpPhi %float %2377 %2352 %2397 %2345 + OpBranch %2336 + %2337 = OpLabel + %2399 = OpExtInst %float %1 Log %float_9_99999975en05 + %2400 = OpFDiv %float %2399 %1065 + OpBranch %2336 + %2336 = OpLabel + %2401 = OpPhi %float %2398 %2344 %2400 %2337 + %2402 = OpExtInst %float %1 Pow %float_10 %2401 + %2403 = OpCompositeInsert %v3float %2402 %2329 1 + %2404 = OpCompositeExtract %float %2248 2 + OpStore %524 %475 + OpStore %523 %476 + %2405 = OpFOrdLessThanEqual %bool %2404 %float_0 + %2406 = OpSelect %float %2405 %2255 %2404 + %2407 = OpExtInst %float %1 Log %2406 + %2408 = OpFDiv %float %2407 %1065 + %2409 = OpFOrdLessThanEqual %bool %2408 %2260 + OpSelectionMerge %2410 None + OpBranchConditional %2409 %2411 %2412 + %2412 = OpLabel + %2413 = OpFOrdGreaterThan %bool %2408 %2260 + %2414 = OpExtInst %float %1 Log %float_0_180000007 + %2415 = OpFDiv %float %2414 %1065 + %2416 = OpFOrdLessThan %bool %2408 %2415 + %2417 = OpLogicalAnd %bool %2413 %2416 + OpSelectionMerge %2418 None + OpBranchConditional %2417 %2419 %2420 + %2420 = OpLabel + %2421 = OpFOrdGreaterThanEqual %bool %2408 %2415 + %2422 = OpExtInst %float %1 Log %2253 + %2423 = OpFDiv %float %2422 %1065 + %2424 = OpFOrdLessThan %bool %2408 %2423 + %2425 = OpLogicalAnd %bool %2421 %2424 + OpSelectionMerge %2426 None + OpBranchConditional %2425 %2427 %2428 + %2428 = OpLabel + %2429 = OpExtInst %float %1 Log %float_10000 + %2430 = OpFDiv %float %2429 %1065 + OpBranch %2426 + %2427 = OpLabel + %2431 = OpFSub %float %2408 %2415 + %2432 = OpFMul %float %float_3 %2431 + %2433 = OpFSub %float %2423 %2415 + %2434 = OpFDiv %float %2432 %2433 + %2435 = OpConvertFToS %int %2434 + %2436 = OpConvertSToF %float %2435 + %2437 = OpFSub %float %2434 %2436 + %2438 = OpAccessChain %_ptr_Function_float %523 %2435 + %2439 = OpLoad %float %2438 + %2440 = OpIAdd %int %2435 %int_1 + %2441 = OpAccessChain %_ptr_Function_float %523 %2440 + %2442 = OpLoad %float %2441 + %2443 = OpIAdd %int %2435 %int_2 + %2444 = OpAccessChain %_ptr_Function_float %523 %2443 + %2445 = OpLoad %float %2444 + %2446 = OpCompositeConstruct %v3float %2439 %2442 %2445 + %2447 = OpFMul %float %2437 %2437 + %2448 = OpCompositeConstruct %v3float %2447 %2437 %float_1 + %2449 = OpMatrixTimesVector %v3float %442 %2446 + %2450 = OpDot %float %2448 %2449 + OpBranch %2426 + %2426 = OpLabel + %2451 = OpPhi %float %2430 %2428 %2450 %2427 + OpBranch %2418 + %2419 = OpLabel + %2452 = OpFSub %float %2408 %2260 + %2453 = OpFMul %float %float_3 %2452 + %2454 = OpFSub %float %2415 %2260 + %2455 = OpFDiv %float %2453 %2454 + %2456 = OpConvertFToS %int %2455 + %2457 = OpConvertSToF %float %2456 + %2458 = OpFSub %float %2455 %2457 + %2459 = OpAccessChain %_ptr_Function_float %524 %2456 + %2460 = OpLoad %float %2459 + %2461 = OpIAdd %int %2456 %int_1 + %2462 = OpAccessChain %_ptr_Function_float %524 %2461 + %2463 = OpLoad %float %2462 + %2464 = OpIAdd %int %2456 %int_2 + %2465 = OpAccessChain %_ptr_Function_float %524 %2464 + %2466 = OpLoad %float %2465 + %2467 = OpCompositeConstruct %v3float %2460 %2463 %2466 + %2468 = OpFMul %float %2458 %2458 + %2469 = OpCompositeConstruct %v3float %2468 %2458 %float_1 + %2470 = OpMatrixTimesVector %v3float %442 %2467 + %2471 = OpDot %float %2469 %2470 + OpBranch %2418 + %2418 = OpLabel + %2472 = OpPhi %float %2451 %2426 %2471 %2419 + OpBranch %2410 + %2411 = OpLabel + %2473 = OpExtInst %float %1 Log %float_9_99999975en05 + %2474 = OpFDiv %float %2473 %1065 + OpBranch %2410 + %2410 = OpLabel + %2475 = OpPhi %float %2472 %2418 %2474 %2411 + %2476 = OpExtInst %float %1 Pow %float_10 %2475 + %2477 = OpCompositeInsert %v3float %2476 %2403 2 + %2478 = OpVectorTimesMatrix %v3float %2477 %414 + %2479 = OpVectorTimesMatrix %v3float %2478 %410 + %2480 = OpExtInst %float %1 Pow %float_2 %float_n12 + %2481 = OpFMul %float %float_0_179999992 %2480 + OpStore %532 %475 + OpStore %531 %476 + %2482 = OpFOrdLessThanEqual %bool %2481 %float_0 + %2483 = OpSelect %float %2482 %2255 %2481 + %2484 = OpExtInst %float %1 Log %2483 + %2485 = OpFDiv %float %2484 %1065 + %2486 = OpFOrdLessThanEqual %bool %2485 %2260 + OpSelectionMerge %2487 None + OpBranchConditional %2486 %2488 %2489 + %2489 = OpLabel + %2490 = OpFOrdGreaterThan %bool %2485 %2260 + %2491 = OpExtInst %float %1 Log %float_0_180000007 + %2492 = OpFDiv %float %2491 %1065 + %2493 = OpFOrdLessThan %bool %2485 %2492 + %2494 = OpLogicalAnd %bool %2490 %2493 + OpSelectionMerge %2495 None + OpBranchConditional %2494 %2496 %2497 + %2497 = OpLabel + %2498 = OpFOrdGreaterThanEqual %bool %2485 %2492 + %2499 = OpExtInst %float %1 Log %2253 + %2500 = OpFDiv %float %2499 %1065 + %2501 = OpFOrdLessThan %bool %2485 %2500 + %2502 = OpLogicalAnd %bool %2498 %2501 + OpSelectionMerge %2503 None + OpBranchConditional %2502 %2504 %2505 + %2505 = OpLabel + %2506 = OpExtInst %float %1 Log %float_10000 + %2507 = OpFDiv %float %2506 %1065 + OpBranch %2503 + %2504 = OpLabel + %2508 = OpFSub %float %2485 %2492 + %2509 = OpFMul %float %float_3 %2508 + %2510 = OpFSub %float %2500 %2492 + %2511 = OpFDiv %float %2509 %2510 + %2512 = OpConvertFToS %int %2511 + %2513 = OpConvertSToF %float %2512 + %2514 = OpFSub %float %2511 %2513 + %2515 = OpAccessChain %_ptr_Function_float %531 %2512 + %2516 = OpLoad %float %2515 + %2517 = OpIAdd %int %2512 %int_1 + %2518 = OpAccessChain %_ptr_Function_float %531 %2517 + %2519 = OpLoad %float %2518 + %2520 = OpIAdd %int %2512 %int_2 + %2521 = OpAccessChain %_ptr_Function_float %531 %2520 + %2522 = OpLoad %float %2521 + %2523 = OpCompositeConstruct %v3float %2516 %2519 %2522 + %2524 = OpFMul %float %2514 %2514 + %2525 = OpCompositeConstruct %v3float %2524 %2514 %float_1 + %2526 = OpMatrixTimesVector %v3float %442 %2523 + %2527 = OpDot %float %2525 %2526 + OpBranch %2503 + %2503 = OpLabel + %2528 = OpPhi %float %2507 %2505 %2527 %2504 + OpBranch %2495 + %2496 = OpLabel + %2529 = OpFSub %float %2485 %2260 + %2530 = OpFMul %float %float_3 %2529 + %2531 = OpFSub %float %2492 %2260 + %2532 = OpFDiv %float %2530 %2531 + %2533 = OpConvertFToS %int %2532 + %2534 = OpConvertSToF %float %2533 + %2535 = OpFSub %float %2532 %2534 + %2536 = OpAccessChain %_ptr_Function_float %532 %2533 + %2537 = OpLoad %float %2536 + %2538 = OpIAdd %int %2533 %int_1 + %2539 = OpAccessChain %_ptr_Function_float %532 %2538 + %2540 = OpLoad %float %2539 + %2541 = OpIAdd %int %2533 %int_2 + %2542 = OpAccessChain %_ptr_Function_float %532 %2541 + %2543 = OpLoad %float %2542 + %2544 = OpCompositeConstruct %v3float %2537 %2540 %2543 + %2545 = OpFMul %float %2535 %2535 + %2546 = OpCompositeConstruct %v3float %2545 %2535 %float_1 + %2547 = OpMatrixTimesVector %v3float %442 %2544 + %2548 = OpDot %float %2546 %2547 + OpBranch %2495 + %2495 = OpLabel + %2549 = OpPhi %float %2528 %2503 %2548 %2496 + OpBranch %2487 + %2488 = OpLabel + %2550 = OpExtInst %float %1 Log %float_9_99999975en05 + %2551 = OpFDiv %float %2550 %1065 + OpBranch %2487 + %2487 = OpLabel + %2552 = OpPhi %float %2549 %2495 %2551 %2488 + %2553 = OpExtInst %float %1 Pow %float_10 %2552 + OpStore %534 %475 + OpStore %533 %476 + %2554 = OpExtInst %float %1 Log %float_0_180000007 + %2555 = OpFDiv %float %2554 %1065 + %2556 = OpFOrdLessThanEqual %bool %2555 %2260 + OpSelectionMerge %2557 None + OpBranchConditional %2556 %2558 %2559 + %2559 = OpLabel + %2560 = OpFOrdGreaterThan %bool %2555 %2260 + %2561 = OpFOrdLessThan %bool %2555 %2555 + %2562 = OpLogicalAnd %bool %2560 %2561 + OpSelectionMerge %2563 None + OpBranchConditional %2562 %2564 %2565 + %2565 = OpLabel + %2566 = OpFOrdGreaterThanEqual %bool %2555 %2555 + %2567 = OpExtInst %float %1 Log %2253 + %2568 = OpFDiv %float %2567 %1065 + %2569 = OpFOrdLessThan %bool %2555 %2568 + %2570 = OpLogicalAnd %bool %2566 %2569 + OpSelectionMerge %2571 None + OpBranchConditional %2570 %2572 %2573 + %2573 = OpLabel + %2574 = OpExtInst %float %1 Log %float_10000 + %2575 = OpFDiv %float %2574 %1065 + OpBranch %2571 + %2572 = OpLabel + %2576 = OpFSub %float %2555 %2555 + %2577 = OpFMul %float %float_3 %2576 + %2578 = OpFSub %float %2568 %2555 + %2579 = OpFDiv %float %2577 %2578 + %2580 = OpConvertFToS %int %2579 + %2581 = OpConvertSToF %float %2580 + %2582 = OpFSub %float %2579 %2581 + %2583 = OpAccessChain %_ptr_Function_float %533 %2580 + %2584 = OpLoad %float %2583 + %2585 = OpIAdd %int %2580 %int_1 + %2586 = OpAccessChain %_ptr_Function_float %533 %2585 + %2587 = OpLoad %float %2586 + %2588 = OpIAdd %int %2580 %int_2 + %2589 = OpAccessChain %_ptr_Function_float %533 %2588 + %2590 = OpLoad %float %2589 + %2591 = OpCompositeConstruct %v3float %2584 %2587 %2590 + %2592 = OpFMul %float %2582 %2582 + %2593 = OpCompositeConstruct %v3float %2592 %2582 %float_1 + %2594 = OpMatrixTimesVector %v3float %442 %2591 + %2595 = OpDot %float %2593 %2594 + OpBranch %2571 + %2571 = OpLabel + %2596 = OpPhi %float %2575 %2573 %2595 %2572 + OpBranch %2563 + %2564 = OpLabel + %2597 = OpFSub %float %2555 %2260 + %2598 = OpFMul %float %float_3 %2597 + %2599 = OpAccessChain %_ptr_Function_float %534 %int_3 + %2600 = OpLoad %float %2599 + %2601 = OpAccessChain %_ptr_Function_float %534 %int_4 + %2602 = OpLoad %float %2601 + %2603 = OpAccessChain %_ptr_Function_float %534 %int_5 + %2604 = OpLoad %float %2603 + %2605 = OpCompositeConstruct %v3float %2600 %2602 %2604 + %2606 = OpMatrixTimesVector %v3float %442 %2605 + %2607 = OpCompositeExtract %float %2606 2 + OpBranch %2563 + %2563 = OpLabel + %2608 = OpPhi %float %2596 %2571 %2607 %2564 + OpBranch %2557 + %2558 = OpLabel + %2609 = OpExtInst %float %1 Log %float_9_99999975en05 + %2610 = OpFDiv %float %2609 %1065 + OpBranch %2557 + %2557 = OpLabel + %2611 = OpPhi %float %2608 %2563 %2610 %2558 + %2612 = OpExtInst %float %1 Pow %float_10 %2611 + %2613 = OpExtInst %float %1 Pow %float_2 %float_10 + %2614 = OpFMul %float %float_0_179999992 %2613 + OpStore %536 %475 + OpStore %535 %476 + %2615 = OpFOrdLessThanEqual %bool %2614 %float_0 + %2616 = OpSelect %float %2615 %2255 %2614 + %2617 = OpExtInst %float %1 Log %2616 + %2618 = OpFDiv %float %2617 %1065 + %2619 = OpFOrdLessThanEqual %bool %2618 %2260 + OpSelectionMerge %2620 None + OpBranchConditional %2619 %2621 %2622 + %2622 = OpLabel + %2623 = OpFOrdGreaterThan %bool %2618 %2260 + %2624 = OpFOrdLessThan %bool %2618 %2555 + %2625 = OpLogicalAnd %bool %2623 %2624 + OpSelectionMerge %2626 None + OpBranchConditional %2625 %2627 %2628 + %2628 = OpLabel + %2629 = OpFOrdGreaterThanEqual %bool %2618 %2555 + %2630 = OpExtInst %float %1 Log %2253 + %2631 = OpFDiv %float %2630 %1065 + %2632 = OpFOrdLessThan %bool %2618 %2631 + %2633 = OpLogicalAnd %bool %2629 %2632 + OpSelectionMerge %2634 None + OpBranchConditional %2633 %2635 %2636 + %2636 = OpLabel + %2637 = OpExtInst %float %1 Log %float_10000 + %2638 = OpFDiv %float %2637 %1065 + OpBranch %2634 + %2635 = OpLabel + %2639 = OpFSub %float %2618 %2555 + %2640 = OpFMul %float %float_3 %2639 + %2641 = OpFSub %float %2631 %2555 + %2642 = OpFDiv %float %2640 %2641 + %2643 = OpConvertFToS %int %2642 + %2644 = OpConvertSToF %float %2643 + %2645 = OpFSub %float %2642 %2644 + %2646 = OpAccessChain %_ptr_Function_float %535 %2643 + %2647 = OpLoad %float %2646 + %2648 = OpIAdd %int %2643 %int_1 + %2649 = OpAccessChain %_ptr_Function_float %535 %2648 + %2650 = OpLoad %float %2649 + %2651 = OpIAdd %int %2643 %int_2 + %2652 = OpAccessChain %_ptr_Function_float %535 %2651 + %2653 = OpLoad %float %2652 + %2654 = OpCompositeConstruct %v3float %2647 %2650 %2653 + %2655 = OpFMul %float %2645 %2645 + %2656 = OpCompositeConstruct %v3float %2655 %2645 %float_1 + %2657 = OpMatrixTimesVector %v3float %442 %2654 + %2658 = OpDot %float %2656 %2657 + OpBranch %2634 + %2634 = OpLabel + %2659 = OpPhi %float %2638 %2636 %2658 %2635 + OpBranch %2626 + %2627 = OpLabel + %2660 = OpFSub %float %2618 %2260 + %2661 = OpFMul %float %float_3 %2660 + %2662 = OpFSub %float %2555 %2260 + %2663 = OpFDiv %float %2661 %2662 + %2664 = OpConvertFToS %int %2663 + %2665 = OpConvertSToF %float %2664 + %2666 = OpFSub %float %2663 %2665 + %2667 = OpAccessChain %_ptr_Function_float %536 %2664 + %2668 = OpLoad %float %2667 + %2669 = OpIAdd %int %2664 %int_1 + %2670 = OpAccessChain %_ptr_Function_float %536 %2669 + %2671 = OpLoad %float %2670 + %2672 = OpIAdd %int %2664 %int_2 + %2673 = OpAccessChain %_ptr_Function_float %536 %2672 + %2674 = OpLoad %float %2673 + %2675 = OpCompositeConstruct %v3float %2668 %2671 %2674 + %2676 = OpFMul %float %2666 %2666 + %2677 = OpCompositeConstruct %v3float %2676 %2666 %float_1 + %2678 = OpMatrixTimesVector %v3float %442 %2675 + %2679 = OpDot %float %2677 %2678 + OpBranch %2626 + %2626 = OpLabel + %2680 = OpPhi %float %2659 %2634 %2679 %2627 + OpBranch %2620 + %2621 = OpLabel + %2681 = OpExtInst %float %1 Log %float_9_99999975en05 + %2682 = OpFDiv %float %2681 %1065 + OpBranch %2620 + %2620 = OpLabel + %2683 = OpPhi %float %2680 %2626 %2682 %2621 + %2684 = OpExtInst %float %1 Pow %float_10 %2683 + %2685 = OpCompositeExtract %float %2479 0 + OpStore %530 %479 + OpStore %529 %480 + %2686 = OpFOrdLessThanEqual %bool %2685 %float_0 + %2687 = OpSelect %float %2686 %float_9_99999975en05 %2685 + %2688 = OpExtInst %float %1 Log %2687 + %2689 = OpFDiv %float %2688 %1065 + %2690 = OpExtInst %float %1 Log %2553 + %2691 = OpFDiv %float %2690 %1065 + %2692 = OpFOrdLessThanEqual %bool %2689 %2691 + OpSelectionMerge %2693 None + OpBranchConditional %2692 %2694 %2695 + %2695 = OpLabel + %2696 = OpFOrdGreaterThan %bool %2689 %2691 + %2697 = OpExtInst %float %1 Log %2612 + %2698 = OpFDiv %float %2697 %1065 + %2699 = OpFOrdLessThan %bool %2689 %2698 + %2700 = OpLogicalAnd %bool %2696 %2699 + OpSelectionMerge %2701 None + OpBranchConditional %2700 %2702 %2703 + %2703 = OpLabel + %2704 = OpFOrdGreaterThanEqual %bool %2689 %2698 + %2705 = OpExtInst %float %1 Log %2684 + %2706 = OpFDiv %float %2705 %1065 + %2707 = OpFOrdLessThan %bool %2689 %2706 + %2708 = OpLogicalAnd %bool %2704 %2707 + OpSelectionMerge %2709 None + OpBranchConditional %2708 %2710 %2711 + %2711 = OpLabel + %2712 = OpFMul %float %2689 %float_0_0599999987 + %2713 = OpExtInst %float %1 Log %float_1000 + %2714 = OpFDiv %float %2713 %1065 + %2715 = OpFMul %float %float_0_0599999987 %2705 + %2716 = OpFDiv %float %2715 %1065 + %2717 = OpFSub %float %2714 %2716 + %2718 = OpFAdd %float %2712 %2717 + OpBranch %2709 + %2710 = OpLabel + %2719 = OpFSub %float %2689 %2698 + %2720 = OpFMul %float %float_7 %2719 + %2721 = OpFSub %float %2706 %2698 + %2722 = OpFDiv %float %2720 %2721 + %2723 = OpConvertFToS %int %2722 + %2724 = OpConvertSToF %float %2723 + %2725 = OpFSub %float %2722 %2724 + %2726 = OpAccessChain %_ptr_Function_float %529 %2723 + %2727 = OpLoad %float %2726 + %2728 = OpIAdd %int %2723 %int_1 + %2729 = OpAccessChain %_ptr_Function_float %529 %2728 + %2730 = OpLoad %float %2729 + %2731 = OpIAdd %int %2723 %int_2 + %2732 = OpAccessChain %_ptr_Function_float %529 %2731 + %2733 = OpLoad %float %2732 + %2734 = OpCompositeConstruct %v3float %2727 %2730 %2733 + %2735 = OpFMul %float %2725 %2725 + %2736 = OpCompositeConstruct %v3float %2735 %2725 %float_1 + %2737 = OpMatrixTimesVector %v3float %442 %2734 + %2738 = OpDot %float %2736 %2737 + OpBranch %2709 + %2709 = OpLabel + %2739 = OpPhi %float %2718 %2711 %2738 %2710 + OpBranch %2701 + %2702 = OpLabel + %2740 = OpFSub %float %2689 %2691 + %2741 = OpFMul %float %float_7 %2740 + %2742 = OpFSub %float %2698 %2691 + %2743 = OpFDiv %float %2741 %2742 + %2744 = OpConvertFToS %int %2743 + %2745 = OpConvertSToF %float %2744 + %2746 = OpFSub %float %2743 %2745 + %2747 = OpAccessChain %_ptr_Function_float %530 %2744 + %2748 = OpLoad %float %2747 + %2749 = OpIAdd %int %2744 %int_1 + %2750 = OpAccessChain %_ptr_Function_float %530 %2749 + %2751 = OpLoad %float %2750 + %2752 = OpIAdd %int %2744 %int_2 + %2753 = OpAccessChain %_ptr_Function_float %530 %2752 + %2754 = OpLoad %float %2753 + %2755 = OpCompositeConstruct %v3float %2748 %2751 %2754 + %2756 = OpFMul %float %2746 %2746 + %2757 = OpCompositeConstruct %v3float %2756 %2746 %float_1 + %2758 = OpMatrixTimesVector %v3float %442 %2755 + %2759 = OpDot %float %2757 %2758 + OpBranch %2701 + %2701 = OpLabel + %2760 = OpPhi %float %2739 %2709 %2759 %2702 + OpBranch %2693 + %2694 = OpLabel + %2761 = OpFMul %float %2689 %float_3 + %2762 = OpExtInst %float %1 Log %float_9_99999975en05 + %2763 = OpFDiv %float %2762 %1065 + %2764 = OpFMul %float %float_3 %2690 + %2765 = OpFDiv %float %2764 %1065 + %2766 = OpFSub %float %2763 %2765 + %2767 = OpFAdd %float %2761 %2766 + OpBranch %2693 + %2693 = OpLabel + %2768 = OpPhi %float %2760 %2701 %2767 %2694 + %2769 = OpExtInst %float %1 Pow %float_10 %2768 + %2770 = OpCompositeInsert %v3float %2769 %391 0 + %2771 = OpCompositeExtract %float %2479 1 + OpStore %528 %479 + OpStore %527 %480 + %2772 = OpFOrdLessThanEqual %bool %2771 %float_0 + %2773 = OpSelect %float %2772 %float_9_99999975en05 %2771 + %2774 = OpExtInst %float %1 Log %2773 + %2775 = OpFDiv %float %2774 %1065 + %2776 = OpFOrdLessThanEqual %bool %2775 %2691 + OpSelectionMerge %2777 None + OpBranchConditional %2776 %2778 %2779 + %2779 = OpLabel + %2780 = OpFOrdGreaterThan %bool %2775 %2691 + %2781 = OpExtInst %float %1 Log %2612 + %2782 = OpFDiv %float %2781 %1065 + %2783 = OpFOrdLessThan %bool %2775 %2782 + %2784 = OpLogicalAnd %bool %2780 %2783 + OpSelectionMerge %2785 None + OpBranchConditional %2784 %2786 %2787 + %2787 = OpLabel + %2788 = OpFOrdGreaterThanEqual %bool %2775 %2782 + %2789 = OpExtInst %float %1 Log %2684 + %2790 = OpFDiv %float %2789 %1065 + %2791 = OpFOrdLessThan %bool %2775 %2790 + %2792 = OpLogicalAnd %bool %2788 %2791 + OpSelectionMerge %2793 None + OpBranchConditional %2792 %2794 %2795 + %2795 = OpLabel + %2796 = OpFMul %float %2775 %float_0_0599999987 + %2797 = OpExtInst %float %1 Log %float_1000 + %2798 = OpFDiv %float %2797 %1065 + %2799 = OpFMul %float %float_0_0599999987 %2789 + %2800 = OpFDiv %float %2799 %1065 + %2801 = OpFSub %float %2798 %2800 + %2802 = OpFAdd %float %2796 %2801 + OpBranch %2793 + %2794 = OpLabel + %2803 = OpFSub %float %2775 %2782 + %2804 = OpFMul %float %float_7 %2803 + %2805 = OpFSub %float %2790 %2782 + %2806 = OpFDiv %float %2804 %2805 + %2807 = OpConvertFToS %int %2806 + %2808 = OpConvertSToF %float %2807 + %2809 = OpFSub %float %2806 %2808 + %2810 = OpAccessChain %_ptr_Function_float %527 %2807 + %2811 = OpLoad %float %2810 + %2812 = OpIAdd %int %2807 %int_1 + %2813 = OpAccessChain %_ptr_Function_float %527 %2812 + %2814 = OpLoad %float %2813 + %2815 = OpIAdd %int %2807 %int_2 + %2816 = OpAccessChain %_ptr_Function_float %527 %2815 + %2817 = OpLoad %float %2816 + %2818 = OpCompositeConstruct %v3float %2811 %2814 %2817 + %2819 = OpFMul %float %2809 %2809 + %2820 = OpCompositeConstruct %v3float %2819 %2809 %float_1 + %2821 = OpMatrixTimesVector %v3float %442 %2818 + %2822 = OpDot %float %2820 %2821 + OpBranch %2793 + %2793 = OpLabel + %2823 = OpPhi %float %2802 %2795 %2822 %2794 + OpBranch %2785 + %2786 = OpLabel + %2824 = OpFSub %float %2775 %2691 + %2825 = OpFMul %float %float_7 %2824 + %2826 = OpFSub %float %2782 %2691 + %2827 = OpFDiv %float %2825 %2826 + %2828 = OpConvertFToS %int %2827 + %2829 = OpConvertSToF %float %2828 + %2830 = OpFSub %float %2827 %2829 + %2831 = OpAccessChain %_ptr_Function_float %528 %2828 + %2832 = OpLoad %float %2831 + %2833 = OpIAdd %int %2828 %int_1 + %2834 = OpAccessChain %_ptr_Function_float %528 %2833 + %2835 = OpLoad %float %2834 + %2836 = OpIAdd %int %2828 %int_2 + %2837 = OpAccessChain %_ptr_Function_float %528 %2836 + %2838 = OpLoad %float %2837 + %2839 = OpCompositeConstruct %v3float %2832 %2835 %2838 + %2840 = OpFMul %float %2830 %2830 + %2841 = OpCompositeConstruct %v3float %2840 %2830 %float_1 + %2842 = OpMatrixTimesVector %v3float %442 %2839 + %2843 = OpDot %float %2841 %2842 + OpBranch %2785 + %2785 = OpLabel + %2844 = OpPhi %float %2823 %2793 %2843 %2786 + OpBranch %2777 + %2778 = OpLabel + %2845 = OpFMul %float %2775 %float_3 + %2846 = OpExtInst %float %1 Log %float_9_99999975en05 + %2847 = OpFDiv %float %2846 %1065 + %2848 = OpFMul %float %float_3 %2690 + %2849 = OpFDiv %float %2848 %1065 + %2850 = OpFSub %float %2847 %2849 + %2851 = OpFAdd %float %2845 %2850 + OpBranch %2777 + %2777 = OpLabel + %2852 = OpPhi %float %2844 %2785 %2851 %2778 + %2853 = OpExtInst %float %1 Pow %float_10 %2852 + %2854 = OpCompositeInsert %v3float %2853 %2770 1 + %2855 = OpCompositeExtract %float %2479 2 + OpStore %526 %479 + OpStore %525 %480 + %2856 = OpFOrdLessThanEqual %bool %2855 %float_0 + %2857 = OpSelect %float %2856 %float_9_99999975en05 %2855 + %2858 = OpExtInst %float %1 Log %2857 + %2859 = OpFDiv %float %2858 %1065 + %2860 = OpFOrdLessThanEqual %bool %2859 %2691 + OpSelectionMerge %2861 None + OpBranchConditional %2860 %2862 %2863 + %2863 = OpLabel + %2864 = OpFOrdGreaterThan %bool %2859 %2691 + %2865 = OpExtInst %float %1 Log %2612 + %2866 = OpFDiv %float %2865 %1065 + %2867 = OpFOrdLessThan %bool %2859 %2866 + %2868 = OpLogicalAnd %bool %2864 %2867 + OpSelectionMerge %2869 None + OpBranchConditional %2868 %2870 %2871 + %2871 = OpLabel + %2872 = OpFOrdGreaterThanEqual %bool %2859 %2866 + %2873 = OpExtInst %float %1 Log %2684 + %2874 = OpFDiv %float %2873 %1065 + %2875 = OpFOrdLessThan %bool %2859 %2874 + %2876 = OpLogicalAnd %bool %2872 %2875 + OpSelectionMerge %2877 None + OpBranchConditional %2876 %2878 %2879 + %2879 = OpLabel + %2880 = OpFMul %float %2859 %float_0_0599999987 + %2881 = OpExtInst %float %1 Log %float_1000 + %2882 = OpFDiv %float %2881 %1065 + %2883 = OpFMul %float %float_0_0599999987 %2873 + %2884 = OpFDiv %float %2883 %1065 + %2885 = OpFSub %float %2882 %2884 + %2886 = OpFAdd %float %2880 %2885 + OpBranch %2877 + %2878 = OpLabel + %2887 = OpFSub %float %2859 %2866 + %2888 = OpFMul %float %float_7 %2887 + %2889 = OpFSub %float %2874 %2866 + %2890 = OpFDiv %float %2888 %2889 + %2891 = OpConvertFToS %int %2890 + %2892 = OpConvertSToF %float %2891 + %2893 = OpFSub %float %2890 %2892 + %2894 = OpAccessChain %_ptr_Function_float %525 %2891 + %2895 = OpLoad %float %2894 + %2896 = OpIAdd %int %2891 %int_1 + %2897 = OpAccessChain %_ptr_Function_float %525 %2896 + %2898 = OpLoad %float %2897 + %2899 = OpIAdd %int %2891 %int_2 + %2900 = OpAccessChain %_ptr_Function_float %525 %2899 + %2901 = OpLoad %float %2900 + %2902 = OpCompositeConstruct %v3float %2895 %2898 %2901 + %2903 = OpFMul %float %2893 %2893 + %2904 = OpCompositeConstruct %v3float %2903 %2893 %float_1 + %2905 = OpMatrixTimesVector %v3float %442 %2902 + %2906 = OpDot %float %2904 %2905 + OpBranch %2877 + %2877 = OpLabel + %2907 = OpPhi %float %2886 %2879 %2906 %2878 + OpBranch %2869 + %2870 = OpLabel + %2908 = OpFSub %float %2859 %2691 + %2909 = OpFMul %float %float_7 %2908 + %2910 = OpFSub %float %2866 %2691 + %2911 = OpFDiv %float %2909 %2910 + %2912 = OpConvertFToS %int %2911 + %2913 = OpConvertSToF %float %2912 + %2914 = OpFSub %float %2911 %2913 + %2915 = OpAccessChain %_ptr_Function_float %526 %2912 + %2916 = OpLoad %float %2915 + %2917 = OpIAdd %int %2912 %int_1 + %2918 = OpAccessChain %_ptr_Function_float %526 %2917 + %2919 = OpLoad %float %2918 + %2920 = OpIAdd %int %2912 %int_2 + %2921 = OpAccessChain %_ptr_Function_float %526 %2920 + %2922 = OpLoad %float %2921 + %2923 = OpCompositeConstruct %v3float %2916 %2919 %2922 + %2924 = OpFMul %float %2914 %2914 + %2925 = OpCompositeConstruct %v3float %2924 %2914 %float_1 + %2926 = OpMatrixTimesVector %v3float %442 %2923 + %2927 = OpDot %float %2925 %2926 + OpBranch %2869 + %2869 = OpLabel + %2928 = OpPhi %float %2907 %2877 %2927 %2870 + OpBranch %2861 + %2862 = OpLabel + %2929 = OpFMul %float %2859 %float_3 + %2930 = OpExtInst %float %1 Log %float_9_99999975en05 + %2931 = OpFDiv %float %2930 %1065 + %2932 = OpFMul %float %float_3 %2690 + %2933 = OpFDiv %float %2932 %1065 + %2934 = OpFSub %float %2931 %2933 + %2935 = OpFAdd %float %2929 %2934 + OpBranch %2861 + %2861 = OpLabel + %2936 = OpPhi %float %2928 %2869 %2935 %2862 + %2937 = OpExtInst %float %1 Pow %float_10 %2936 + %2938 = OpCompositeInsert %v3float %2937 %2854 2 + %2939 = OpFSub %v3float %2938 %338 + %2940 = OpVectorTimesMatrix %v3float %2939 %576 + %2941 = OpFMul %v3float %2940 %496 + %2942 = OpExtInst %v3float %1 Pow %2941 %263 + %2943 = OpFMul %v3float %184 %2942 + %2944 = OpFAdd %v3float %183 %2943 + %2945 = OpFMul %v3float %185 %2942 + %2946 = OpFAdd %v3float %135 %2945 + %2947 = OpFDiv %v3float %135 %2946 + %2948 = OpFMul %v3float %2944 %2947 + %2949 = OpExtInst %v3float %1 Pow %2948 %264 + OpBranch %1230 + %1230 = OpLabel + %2950 = OpPhi %v3float %2097 %1236 %2949 %2861 + OpBranch %1224 + %1225 = OpLabel + %2951 = OpVectorTimesMatrix %v3float %1218 %547 + %2952 = OpVectorTimesMatrix %v3float %2951 %576 + %2953 = OpExtInst %v3float %1 FMax %250 %2952 + %2954 = OpFMul %v3float %2953 %252 + %2955 = OpExtInst %v3float %1 FMax %2953 %254 + %2956 = OpExtInst %v3float %1 Pow %2955 %256 + %2957 = OpFMul %v3float %2956 %258 + %2958 = OpFSub %v3float %2957 %260 + %2959 = OpExtInst %v3float %1 FMin %2954 %2958 + OpBranch %1224 + %1224 = OpLabel + %2960 = OpPhi %v3float %2950 %1230 %2959 %1225 + OpBranch %1220 + %1221 = OpLabel + %2961 = OpCompositeExtract %float %1218 0 + OpBranch %2962 + %2962 = OpLabel + OpLoopMerge %2963 %2964 None + OpBranch %2965 + %2965 = OpLabel + %2966 = OpFOrdLessThan %bool %2961 %float_0_00313066994 + OpSelectionMerge %2967 None + OpBranchConditional %2966 %2968 %2967 + %2968 = OpLabel + %2969 = OpFMul %float %2961 %float_12_9200001 + OpBranch %2963 + %2967 = OpLabel + %2970 = OpExtInst %float %1 Pow %2961 %float_0_416666657 + %2971 = OpFMul %float %2970 %float_1_05499995 + %2972 = OpFSub %float %2971 %float_0_0549999997 + OpBranch %2963 + %2964 = OpLabel + OpBranch %2962 + %2963 = OpLabel + %2973 = OpPhi %float %2969 %2968 %2972 %2967 + %2974 = OpCompositeExtract %float %1218 1 + OpBranch %2975 + %2975 = OpLabel + OpLoopMerge %2976 %2977 None + OpBranch %2978 + %2978 = OpLabel + %2979 = OpFOrdLessThan %bool %2974 %float_0_00313066994 + OpSelectionMerge %2980 None + OpBranchConditional %2979 %2981 %2980 + %2981 = OpLabel + %2982 = OpFMul %float %2974 %float_12_9200001 + OpBranch %2976 + %2980 = OpLabel + %2983 = OpExtInst %float %1 Pow %2974 %float_0_416666657 + %2984 = OpFMul %float %2983 %float_1_05499995 + %2985 = OpFSub %float %2984 %float_0_0549999997 + OpBranch %2976 + %2977 = OpLabel + OpBranch %2975 + %2976 = OpLabel + %2986 = OpPhi %float %2982 %2981 %2985 %2980 + %2987 = OpCompositeExtract %float %1218 2 + OpBranch %2988 + %2988 = OpLabel + OpLoopMerge %2989 %2990 None + OpBranch %2991 + %2991 = OpLabel + %2992 = OpFOrdLessThan %bool %2987 %float_0_00313066994 + OpSelectionMerge %2993 None + OpBranchConditional %2992 %2994 %2993 + %2994 = OpLabel + %2995 = OpFMul %float %2987 %float_12_9200001 + OpBranch %2989 + %2993 = OpLabel + %2996 = OpExtInst %float %1 Pow %2987 %float_0_416666657 + %2997 = OpFMul %float %2996 %float_1_05499995 + %2998 = OpFSub %float %2997 %float_0_0549999997 + OpBranch %2989 + %2990 = OpLabel + OpBranch %2988 + %2989 = OpLabel + %2999 = OpPhi %float %2995 %2994 %2998 %2993 + %3000 = OpCompositeConstruct %v3float %2973 %2986 %2999 + OpBranch %1220 + %1220 = OpLabel + %3001 = OpPhi %v3float %2960 %1224 %3000 %2989 + %3002 = OpFMul %v3float %3001 %499 + %3003 = OpVectorShuffle %v4float %129 %3002 4 5 6 3 + %3004 = OpCompositeInsert %v4float %float_0 %3003 3 + OpStore %out_var_SV_Target0 %3004 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag new file mode 100644 index 0000000..e0359bf --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/padded-float-array-member-defef.asm.frag @@ -0,0 +1,3694 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 3107 +; Schema: 0 + OpCapability Shader + OpCapability Geometry + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPS "main" %in_var_TEXCOORD0 %gl_FragCoord %gl_Layer %out_var_SV_Target0 + OpExecutionMode %MainPS OriginUpperLeft + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "MappingPolynomial" + OpMemberName %type__Globals 1 "InverseGamma" + OpMemberName %type__Globals 2 "ColorMatrixR_ColorCurveCd1" + OpMemberName %type__Globals 3 "ColorMatrixG_ColorCurveCd3Cm3" + OpMemberName %type__Globals 4 "ColorMatrixB_ColorCurveCm2" + OpMemberName %type__Globals 5 "ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3" + OpMemberName %type__Globals 6 "ColorCurve_Ch1_Ch2" + OpMemberName %type__Globals 7 "ColorShadow_Luma" + OpMemberName %type__Globals 8 "ColorShadow_Tint1" + OpMemberName %type__Globals 9 "ColorShadow_Tint2" + OpMemberName %type__Globals 10 "FilmSlope" + OpMemberName %type__Globals 11 "FilmToe" + OpMemberName %type__Globals 12 "FilmShoulder" + OpMemberName %type__Globals 13 "FilmBlackClip" + OpMemberName %type__Globals 14 "FilmWhiteClip" + OpMemberName %type__Globals 15 "LUTWeights" + OpMemberName %type__Globals 16 "ColorScale" + OpMemberName %type__Globals 17 "OverlayColor" + OpMemberName %type__Globals 18 "WhiteTemp" + OpMemberName %type__Globals 19 "WhiteTint" + OpMemberName %type__Globals 20 "ColorSaturation" + OpMemberName %type__Globals 21 "ColorContrast" + OpMemberName %type__Globals 22 "ColorGamma" + OpMemberName %type__Globals 23 "ColorGain" + OpMemberName %type__Globals 24 "ColorOffset" + OpMemberName %type__Globals 25 "ColorSaturationShadows" + OpMemberName %type__Globals 26 "ColorContrastShadows" + OpMemberName %type__Globals 27 "ColorGammaShadows" + OpMemberName %type__Globals 28 "ColorGainShadows" + OpMemberName %type__Globals 29 "ColorOffsetShadows" + OpMemberName %type__Globals 30 "ColorSaturationMidtones" + OpMemberName %type__Globals 31 "ColorContrastMidtones" + OpMemberName %type__Globals 32 "ColorGammaMidtones" + OpMemberName %type__Globals 33 "ColorGainMidtones" + OpMemberName %type__Globals 34 "ColorOffsetMidtones" + OpMemberName %type__Globals 35 "ColorSaturationHighlights" + OpMemberName %type__Globals 36 "ColorContrastHighlights" + OpMemberName %type__Globals 37 "ColorGammaHighlights" + OpMemberName %type__Globals 38 "ColorGainHighlights" + OpMemberName %type__Globals 39 "ColorOffsetHighlights" + OpMemberName %type__Globals 40 "ColorCorrectionShadowsMax" + OpMemberName %type__Globals 41 "ColorCorrectionHighlightsMin" + OpMemberName %type__Globals 42 "OutputDevice" + OpMemberName %type__Globals 43 "OutputGamut" + OpMemberName %type__Globals 44 "BlueCorrection" + OpMemberName %type__Globals 45 "ExpandGamut" + OpName %_Globals "$Globals" + OpName %type_2d_image "type.2d.image" + OpName %Texture1 "Texture1" + OpName %type_sampler "type.sampler" + OpName %Texture1Sampler "Texture1Sampler" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPS "MainPS" + OpName %type_sampled_image "type.sampled.image" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorate %in_var_TEXCOORD0 NoPerspective + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_POSITION" + OpDecorate %gl_Layer BuiltIn Layer + OpDecorateString %gl_Layer UserSemantic "SV_RenderTargetArrayIndex" + OpDecorate %gl_Layer Flat + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpDecorate %Texture1 DescriptorSet 0 + OpDecorate %Texture1 Binding 0 + OpDecorate %Texture1Sampler DescriptorSet 0 + OpDecorate %Texture1Sampler Binding 0 + OpDecorate %_arr_float_uint_5 ArrayStride 16 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 32 + OpMemberDecorate %type__Globals 3 Offset 48 + OpMemberDecorate %type__Globals 4 Offset 64 + OpMemberDecorate %type__Globals 5 Offset 80 + OpMemberDecorate %type__Globals 6 Offset 96 + OpMemberDecorate %type__Globals 7 Offset 112 + OpMemberDecorate %type__Globals 8 Offset 128 + OpMemberDecorate %type__Globals 9 Offset 144 + OpMemberDecorate %type__Globals 10 Offset 160 + OpMemberDecorate %type__Globals 11 Offset 164 + OpMemberDecorate %type__Globals 12 Offset 168 + OpMemberDecorate %type__Globals 13 Offset 172 + OpMemberDecorate %type__Globals 14 Offset 176 + OpMemberDecorate %type__Globals 15 Offset 192 + OpMemberDecorate %type__Globals 16 Offset 272 + OpMemberDecorate %type__Globals 17 Offset 288 + OpMemberDecorate %type__Globals 18 Offset 304 + OpMemberDecorate %type__Globals 19 Offset 308 + OpMemberDecorate %type__Globals 20 Offset 320 + OpMemberDecorate %type__Globals 21 Offset 336 + OpMemberDecorate %type__Globals 22 Offset 352 + OpMemberDecorate %type__Globals 23 Offset 368 + OpMemberDecorate %type__Globals 24 Offset 384 + OpMemberDecorate %type__Globals 25 Offset 400 + OpMemberDecorate %type__Globals 26 Offset 416 + OpMemberDecorate %type__Globals 27 Offset 432 + OpMemberDecorate %type__Globals 28 Offset 448 + OpMemberDecorate %type__Globals 29 Offset 464 + OpMemberDecorate %type__Globals 30 Offset 480 + OpMemberDecorate %type__Globals 31 Offset 496 + OpMemberDecorate %type__Globals 32 Offset 512 + OpMemberDecorate %type__Globals 33 Offset 528 + OpMemberDecorate %type__Globals 34 Offset 544 + OpMemberDecorate %type__Globals 35 Offset 560 + OpMemberDecorate %type__Globals 36 Offset 576 + OpMemberDecorate %type__Globals 37 Offset 592 + OpMemberDecorate %type__Globals 38 Offset 608 + OpMemberDecorate %type__Globals 39 Offset 624 + OpMemberDecorate %type__Globals 40 Offset 640 + OpMemberDecorate %type__Globals 41 Offset 644 + OpMemberDecorate %type__Globals 42 Offset 648 + OpMemberDecorate %type__Globals 43 Offset 652 + OpMemberDecorate %type__Globals 44 Offset 656 + OpMemberDecorate %type__Globals 45 Offset 660 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 +%float_0_952552378 = OpConstant %float 0.952552378 + %float_0 = OpConstant %float 0 + +; HACK: Needed to hack this constant since MSVC and GNU libc are off by 1 ULP when converting to string (it probably still works fine though in a roundtrip ...) +%float_9_36786018en05 = OpConstant %float 9.25 + +%float_0_343966454 = OpConstant %float 0.343966454 +%float_0_728166103 = OpConstant %float 0.728166103 +%float_n0_0721325427 = OpConstant %float -0.0721325427 +%float_1_00882518 = OpConstant %float 1.00882518 +%float_1_04981101 = OpConstant %float 1.04981101 +%float_n9_74845025en05 = OpConstant %float -9.74845025e-05 +%float_n0_495903015 = OpConstant %float -0.495903015 +%float_1_37331307 = OpConstant %float 1.37331307 +%float_0_0982400328 = OpConstant %float 0.0982400328 +%float_0_991252005 = OpConstant %float 0.991252005 +%float_0_662454188 = OpConstant %float 0.662454188 +%float_0_134004205 = OpConstant %float 0.134004205 +%float_0_156187683 = OpConstant %float 0.156187683 +%float_0_272228718 = OpConstant %float 0.272228718 +%float_0_674081743 = OpConstant %float 0.674081743 +%float_0_0536895171 = OpConstant %float 0.0536895171 +%float_n0_00557464967 = OpConstant %float -0.00557464967 +%float_0_0040607336 = OpConstant %float 0.0040607336 +%float_1_01033914 = OpConstant %float 1.01033914 +%float_1_6410234 = OpConstant %float 1.6410234 +%float_n0_324803293 = OpConstant %float -0.324803293 +%float_n0_236424699 = OpConstant %float -0.236424699 +%float_n0_663662851 = OpConstant %float -0.663662851 +%float_1_61533165 = OpConstant %float 1.61533165 +%float_0_0167563483 = OpConstant %float 0.0167563483 +%float_0_0117218941 = OpConstant %float 0.0117218941 +%float_n0_00828444213 = OpConstant %float -0.00828444213 +%float_0_988394856 = OpConstant %float 0.988394856 +%float_1_45143926 = OpConstant %float 1.45143926 +%float_n0_236510754 = OpConstant %float -0.236510754 +%float_n0_214928567 = OpConstant %float -0.214928567 +%float_n0_0765537769 = OpConstant %float -0.0765537769 +%float_1_17622972 = OpConstant %float 1.17622972 +%float_n0_0996759236 = OpConstant %float -0.0996759236 +%float_0_00831614807 = OpConstant %float 0.00831614807 +%float_n0_00603244966 = OpConstant %float -0.00603244966 +%float_0_997716308 = OpConstant %float 0.997716308 +%float_0_695452213 = OpConstant %float 0.695452213 +%float_0_140678704 = OpConstant %float 0.140678704 +%float_0_163869068 = OpConstant %float 0.163869068 +%float_0_0447945632 = OpConstant %float 0.0447945632 +%float_0_859671116 = OpConstant %float 0.859671116 +%float_0_0955343172 = OpConstant %float 0.0955343172 +%float_n0_00552588282 = OpConstant %float -0.00552588282 +%float_0_00402521016 = OpConstant %float 0.00402521016 +%float_1_00150073 = OpConstant %float 1.00150073 + %73 = OpConstantComposite %v3float %float_0_272228718 %float_0_674081743 %float_0_0536895171 +%float_3_2409699 = OpConstant %float 3.2409699 +%float_n1_5373832 = OpConstant %float -1.5373832 +%float_n0_498610765 = OpConstant %float -0.498610765 +%float_n0_969243646 = OpConstant %float -0.969243646 +%float_1_8759675 = OpConstant %float 1.8759675 +%float_0_0415550582 = OpConstant %float 0.0415550582 +%float_0_0556300804 = OpConstant %float 0.0556300804 +%float_n0_203976959 = OpConstant %float -0.203976959 +%float_1_05697155 = OpConstant %float 1.05697155 +%float_0_412456393 = OpConstant %float 0.412456393 +%float_0_357576102 = OpConstant %float 0.357576102 +%float_0_180437505 = OpConstant %float 0.180437505 +%float_0_212672904 = OpConstant %float 0.212672904 +%float_0_715152204 = OpConstant %float 0.715152204 +%float_0_0721750036 = OpConstant %float 0.0721750036 +%float_0_0193339009 = OpConstant %float 0.0193339009 +%float_0_119191997 = OpConstant %float 0.119191997 +%float_0_950304091 = OpConstant %float 0.950304091 +%float_1_71660841 = OpConstant %float 1.71660841 +%float_n0_355662107 = OpConstant %float -0.355662107 +%float_n0_253360093 = OpConstant %float -0.253360093 +%float_n0_666682899 = OpConstant %float -0.666682899 +%float_1_61647761 = OpConstant %float 1.61647761 +%float_0_0157685 = OpConstant %float 0.0157685 +%float_0_0176422 = OpConstant %float 0.0176422 +%float_n0_0427763015 = OpConstant %float -0.0427763015 +%float_0_942228675 = OpConstant %float 0.942228675 +%float_2_49339628 = OpConstant %float 2.49339628 +%float_n0_93134588 = OpConstant %float -0.93134588 +%float_n0_402694494 = OpConstant %float -0.402694494 +%float_n0_829486787 = OpConstant %float -0.829486787 +%float_1_76265967 = OpConstant %float 1.76265967 +%float_0_0236246008 = OpConstant %float 0.0236246008 +%float_0_0358507 = OpConstant %float 0.0358507 +%float_n0_0761827007 = OpConstant %float -0.0761827007 +%float_0_957014024 = OpConstant %float 0.957014024 +%float_1_01303005 = OpConstant %float 1.01303005 +%float_0_00610530982 = OpConstant %float 0.00610530982 +%float_n0_0149710001 = OpConstant %float -0.0149710001 +%float_0_00769822998 = OpConstant %float 0.00769822998 +%float_0_998165011 = OpConstant %float 0.998165011 +%float_n0_00503202993 = OpConstant %float -0.00503202993 +%float_n0_00284131011 = OpConstant %float -0.00284131011 +%float_0_00468515977 = OpConstant %float 0.00468515977 +%float_0_924507022 = OpConstant %float 0.924507022 +%float_0_987223983 = OpConstant %float 0.987223983 +%float_n0_00611326983 = OpConstant %float -0.00611326983 +%float_0_0159533005 = OpConstant %float 0.0159533005 +%float_n0_00759836007 = OpConstant %float -0.00759836007 +%float_1_00186002 = OpConstant %float 1.00186002 +%float_0_0053300201 = OpConstant %float 0.0053300201 +%float_0_00307257008 = OpConstant %float 0.00307257008 +%float_n0_00509594986 = OpConstant %float -0.00509594986 +%float_1_08168006 = OpConstant %float 1.08168006 + %float_0_5 = OpConstant %float 0.5 + %float_n1 = OpConstant %float -1 + %float_1 = OpConstant %float 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%float_0_015625 = OpConstant %float 0.015625 + %134 = OpConstantComposite %v2float %float_0_015625 %float_0_015625 + %135 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int_43 = OpConstant %int 43 + %uint_3 = OpConstant %uint 3 + %138 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %int_9 = OpConstant %int 9 + %int_3 = OpConstant %int 3 + %141 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %float_n4 = OpConstant %float -4 + %int_45 = OpConstant %int 45 +%float_0_544169128 = OpConstant %float 0.544169128 +%float_0_239592597 = OpConstant %float 0.239592597 +%float_0_166694298 = OpConstant %float 0.166694298 +%float_0_239465594 = OpConstant %float 0.239465594 +%float_0_702153027 = OpConstant %float 0.702153027 +%float_0_058381401 = OpConstant %float 0.058381401 +%float_n0_00234390004 = OpConstant %float -0.00234390004 +%float_0_0361833982 = OpConstant %float 0.0361833982 +%float_1_05521834 = OpConstant %float 1.05521834 +%float_0_940437257 = OpConstant %float 0.940437257 +%float_n0_0183068793 = OpConstant %float -0.0183068793 +%float_0_077869609 = OpConstant %float 0.077869609 +%float_0_00837869663 = OpConstant %float 0.00837869663 +%float_0_828660011 = OpConstant %float 0.828660011 +%float_0_162961304 = OpConstant %float 0.162961304 +%float_0_00054712611 = OpConstant %float 0.00054712611 +%float_n0_000883374596 = OpConstant %float -0.000883374596 +%float_1_00033629 = OpConstant %float 1.00033629 +%float_1_06317997 = OpConstant %float 1.06317997 +%float_0_0233955998 = OpConstant %float 0.0233955998 +%float_n0_0865726024 = OpConstant %float -0.0865726024 +%float_n0_0106336996 = OpConstant %float -0.0106336996 +%float_1_20632005 = OpConstant %float 1.20632005 +%float_n0_195690006 = OpConstant %float -0.195690006 +%float_n0_000590886979 = OpConstant %float -0.000590886979 +%float_0_00105247996 = OpConstant %float 0.00105247996 +%float_0_999538004 = OpConstant %float 0.999538004 + %int_44 = OpConstant %int 44 +%float_0_9375 = OpConstant %float 0.9375 + %173 = OpConstantComposite %v3float %float_0_9375 %float_0_9375 %float_0_9375 +%float_0_03125 = OpConstant %float 0.03125 + %175 = OpConstantComposite %v3float %float_0_03125 %float_0_03125 %float_0_03125 + %int_15 = OpConstant %int 15 + %float_16 = OpConstant %float 16 + %int_16 = OpConstant %int 16 + %int_17 = OpConstant %int 17 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_5 = OpConstant %uint 5 + %uint_6 = OpConstant %uint 6 + %int_2 = OpConstant %int 2 +%mat3v3float = OpTypeMatrix %v3float 3 + %int_42 = OpConstant %int 42 +%float_0_159301758 = OpConstant %float 0.159301758 +%float_78_84375 = OpConstant %float 78.84375 +%float_0_8359375 = OpConstant %float 0.8359375 +%float_18_8515625 = OpConstant %float 18.8515625 +%float_18_6875 = OpConstant %float 18.6875 +%float_10000 = OpConstant %float 10000 +%float_0_0126833133 = OpConstant %float 0.0126833133 + %194 = OpConstantComposite %v3float %float_0_0126833133 %float_0_0126833133 %float_0_0126833133 + %195 = OpConstantComposite %v3float %float_0_8359375 %float_0_8359375 %float_0_8359375 + %196 = OpConstantComposite %v3float %float_18_8515625 %float_18_8515625 %float_18_8515625 + %197 = OpConstantComposite %v3float %float_18_6875 %float_18_6875 %float_18_6875 +%float_6_27739477 = OpConstant %float 6.27739477 + %199 = OpConstantComposite %v3float %float_6_27739477 %float_6_27739477 %float_6_27739477 + %200 = OpConstantComposite %v3float %float_10000 %float_10000 %float_10000 + %float_14 = OpConstant %float 14 +%float_0_180000007 = OpConstant %float 0.180000007 +%float_0_434017599 = OpConstant %float 0.434017599 + %204 = OpConstantComposite %v3float %float_0_434017599 %float_0_434017599 %float_0_434017599 + %205 = OpConstantComposite %v3float %float_14 %float_14 %float_14 + %206 = OpConstantComposite %v3float %float_0_180000007 %float_0_180000007 %float_0_180000007 + %int_18 = OpConstant %int 18 + %float_4000 = OpConstant %float 4000 +%float_0_312700003 = OpConstant %float 0.312700003 +%float_0_328999996 = OpConstant %float 0.328999996 + %int_19 = OpConstant %int 19 + %int_25 = OpConstant %int 25 + %int_20 = OpConstant %int 20 + %int_26 = OpConstant %int 26 + %int_21 = OpConstant %int 21 + %int_27 = OpConstant %int 27 + %int_22 = OpConstant %int 22 + %int_28 = OpConstant %int 28 + %int_23 = OpConstant %int 23 + %int_29 = OpConstant %int 29 + %int_24 = OpConstant %int 24 + %int_40 = OpConstant %int 40 + %int_35 = OpConstant %int 35 + %int_36 = OpConstant %int 36 + %int_37 = OpConstant %int 37 + %int_38 = OpConstant %int 38 + %int_39 = OpConstant %int 39 + %int_41 = OpConstant %int 41 + %int_30 = OpConstant %int 30 + %int_31 = OpConstant %int 31 + %int_32 = OpConstant %int 32 + %int_33 = OpConstant %int 33 + %int_34 = OpConstant %int 34 +%float_0_0500000007 = OpConstant %float 0.0500000007 + %float_1_75 = OpConstant %float 1.75 +%float_0_400000006 = OpConstant %float 0.400000006 +%float_0_819999993 = OpConstant %float 0.819999993 +%float_0_0299999993 = OpConstant %float 0.0299999993 + %float_2 = OpConstant %float 2 +%float_0_959999979 = OpConstant %float 0.959999979 + %241 = OpConstantComposite %v3float %float_0_959999979 %float_0_959999979 %float_0_959999979 + %int_13 = OpConstant %int 13 + %int_11 = OpConstant %int 11 + %int_14 = OpConstant %int 14 + %int_12 = OpConstant %int 12 +%float_0_800000012 = OpConstant %float 0.800000012 + %int_10 = OpConstant %int 10 + %float_10 = OpConstant %float 10 + %float_n2 = OpConstant %float -2 + %float_3 = OpConstant %float 3 + %251 = OpConstantComposite %v3float %float_3 %float_3 %float_3 + %252 = OpConstantComposite %v3float %float_2 %float_2 %float_2 +%float_0_930000007 = OpConstant %float 0.930000007 + %254 = OpConstantComposite %v3float %float_0_930000007 %float_0_930000007 %float_0_930000007 + %int_4 = OpConstant %int 4 + %int_8 = OpConstant %int 8 + %int_7 = OpConstant %int 7 + %int_5 = OpConstant %int 5 + %int_6 = OpConstant %int 6 +%float_0_00200000009 = OpConstant %float 0.00200000009 + %261 = OpConstantComposite %v3float %float_0_00200000009 %float_0_00200000009 %float_0_00200000009 +%float_6_10351999en05 = OpConstant %float 6.10351999e-05 + %263 = OpConstantComposite %v3float %float_6_10351999en05 %float_6_10351999en05 %float_6_10351999en05 +%float_0_0404499993 = OpConstant %float 0.0404499993 + %265 = OpConstantComposite %v3float %float_0_0404499993 %float_0_0404499993 %float_0_0404499993 +%float_0_947867274 = OpConstant %float 0.947867274 + %267 = OpConstantComposite %v3float %float_0_947867274 %float_0_947867274 %float_0_947867274 +%float_0_0521326996 = OpConstant %float 0.0521326996 + %269 = OpConstantComposite %v3float %float_0_0521326996 %float_0_0521326996 %float_0_0521326996 +%float_2_4000001 = OpConstant %float 2.4000001 + %271 = OpConstantComposite %v3float %float_2_4000001 %float_2_4000001 %float_2_4000001 +%float_0_0773993805 = OpConstant %float 0.0773993805 + %273 = OpConstantComposite %v3float %float_0_0773993805 %float_0_0773993805 %float_0_0773993805 + %float_4_5 = OpConstant %float 4.5 + %275 = OpConstantComposite %v3float %float_4_5 %float_4_5 %float_4_5 +%float_0_0179999992 = OpConstant %float 0.0179999992 + %277 = OpConstantComposite %v3float %float_0_0179999992 %float_0_0179999992 %float_0_0179999992 +%float_0_449999988 = OpConstant %float 0.449999988 + %279 = OpConstantComposite %v3float %float_0_449999988 %float_0_449999988 %float_0_449999988 +%float_1_09899998 = OpConstant %float 1.09899998 + %281 = OpConstantComposite %v3float %float_1_09899998 %float_1_09899998 %float_1_09899998 +%float_0_0989999995 = OpConstant %float 0.0989999995 + %283 = OpConstantComposite %v3float %float_0_0989999995 %float_0_0989999995 %float_0_0989999995 + %float_1_5 = OpConstant %float 1.5 + %285 = OpConstantComposite %v3float %float_1_5 %float_1_5 %float_1_5 + %286 = OpConstantComposite %v3float %float_0_159301758 %float_0_159301758 %float_0_159301758 + %287 = OpConstantComposite %v3float %float_78_84375 %float_78_84375 %float_78_84375 +%float_1_00055635 = OpConstant %float 1.00055635 + %float_7000 = OpConstant %float 7000 +%float_0_244063005 = OpConstant %float 0.244063005 +%float_99_1100006 = OpConstant %float 99.1100006 +%float_2967800 = OpConstant %float 2967800 +%float_0_237039998 = OpConstant %float 0.237039998 +%float_247_479996 = OpConstant %float 247.479996 +%float_1901800 = OpConstant %float 1901800 + %float_n3 = OpConstant %float -3 +%float_2_86999989 = OpConstant %float 2.86999989 +%float_0_275000006 = OpConstant %float 0.275000006 +%float_0_860117733 = OpConstant %float 0.860117733 +%float_0_000154118257 = OpConstant %float 0.000154118257 +%float_1_28641219en07 = OpConstant %float 1.28641219e-07 +%float_0_00084242021 = OpConstant %float 0.00084242021 +%float_7_08145137en07 = OpConstant %float 7.08145137e-07 +%float_0_317398727 = OpConstant %float 0.317398727 + +; HACK: Needed to hack this constant since MSVC and GNU libc are off by 1 ULP when converting to string (it probably still works fine though in a roundtrip ...) +%float_4_22806261en05 = OpConstant %float 4.25 + +%float_4_20481676en08 = OpConstant %float 4.20481676e-08 +%float_2_8974182en05 = OpConstant %float 2.8974182e-05 +%float_1_61456057en07 = OpConstant %float 1.61456057e-07 + %float_8 = OpConstant %float 8 + %float_4 = OpConstant %float 4 +%float_0_895099998 = OpConstant %float 0.895099998 +%float_0_266400009 = OpConstant %float 0.266400009 +%float_n0_161400005 = OpConstant %float -0.161400005 +%float_n0_750199974 = OpConstant %float -0.750199974 +%float_1_71350002 = OpConstant %float 1.71350002 +%float_0_0366999991 = OpConstant %float 0.0366999991 +%float_0_0388999991 = OpConstant %float 0.0388999991 +%float_n0_0684999973 = OpConstant %float -0.0684999973 +%float_1_02960002 = OpConstant %float 1.02960002 +%float_0_986992896 = OpConstant %float 0.986992896 +%float_n0_1470543 = OpConstant %float -0.1470543 +%float_0_159962699 = OpConstant %float 0.159962699 +%float_0_432305306 = OpConstant %float 0.432305306 +%float_0_518360317 = OpConstant %float 0.518360317 +%float_0_0492912009 = OpConstant %float 0.0492912009 +%float_n0_0085287001 = OpConstant %float -0.0085287001 +%float_0_040042799 = OpConstant %float 0.040042799 +%float_0_968486726 = OpConstant %float 0.968486726 +%float_5_55555534 = OpConstant %float 5.55555534 + %330 = OpConstantComposite %v3float %float_5_55555534 %float_5_55555534 %float_5_55555534 +%float_1_00000001en10 = OpConstant %float 1.00000001e-10 +%float_0_00999999978 = OpConstant %float 0.00999999978 +%float_0_666666687 = OpConstant %float 0.666666687 + %float_180 = OpConstant %float 180 + %float_360 = OpConstant %float 360 +%float_65535 = OpConstant %float 65535 + %337 = OpConstantComposite %v3float %float_65535 %float_65535 %float_65535 +%float_n4_97062206 = OpConstant %float -4.97062206 +%float_n3_02937818 = OpConstant %float -3.02937818 +%float_n2_12619996 = OpConstant %float -2.12619996 +%float_n1_51049995 = OpConstant %float -1.51049995 +%float_n1_05780005 = OpConstant %float -1.05780005 +%float_n0_466800004 = OpConstant %float -0.466800004 +%float_0_119379997 = OpConstant %float 0.119379997 +%float_0_708813429 = OpConstant %float 0.708813429 +%float_1_29118657 = OpConstant %float 1.29118657 +%float_0_808913231 = OpConstant %float 0.808913231 +%float_1_19108677 = OpConstant %float 1.19108677 +%float_1_56830001 = OpConstant %float 1.56830001 +%float_1_9483 = OpConstant %float 1.9483 +%float_2_30830002 = OpConstant %float 2.30830002 +%float_2_63840008 = OpConstant %float 2.63840008 +%float_2_85949993 = OpConstant %float 2.85949993 +%float_2_98726082 = OpConstant %float 2.98726082 +%float_3_01273918 = OpConstant %float 3.01273918 +%float_0_179999992 = OpConstant %float 0.179999992 +%float_9_99999975en05 = OpConstant %float 9.99999975e-05 + %float_1000 = OpConstant %float 1000 +%float_0_0599999987 = OpConstant %float 0.0599999987 +%float_3_50738446en05 = OpConstant %float 3.50738446e-05 + %361 = OpConstantComposite %v3float %float_3_50738446en05 %float_3_50738446en05 %float_3_50738446en05 +%float_n2_30102992 = OpConstant %float -2.30102992 +%float_n1_93120003 = OpConstant %float -1.93120003 +%float_n1_52049994 = OpConstant %float -1.52049994 +%float_0_801995218 = OpConstant %float 0.801995218 +%float_1_19800484 = OpConstant %float 1.19800484 +%float_1_59430003 = OpConstant %float 1.59430003 +%float_1_99730003 = OpConstant %float 1.99730003 +%float_2_37829995 = OpConstant %float 2.37829995 +%float_2_76839995 = OpConstant %float 2.76839995 +%float_3_05150008 = OpConstant %float 3.05150008 +%float_3_27462935 = OpConstant %float 3.27462935 +%float_3_32743073 = OpConstant %float 3.32743073 +%float_0_00499999989 = OpConstant %float 0.00499999989 + %float_11 = OpConstant %float 11 + %float_2000 = OpConstant %float 2000 +%float_0_119999997 = OpConstant %float 0.119999997 +%float_0_00313066994 = OpConstant %float 0.00313066994 +%float_12_9200001 = OpConstant %float 12.9200001 +%float_0_416666657 = OpConstant %float 0.416666657 +%float_1_05499995 = OpConstant %float 1.05499995 +%float_0_0549999997 = OpConstant %float 0.0549999997 +%float_n0_166666672 = OpConstant %float -0.166666672 + %float_n0_5 = OpConstant %float -0.5 +%float_0_166666672 = OpConstant %float 0.166666672 +%float_n3_15737653 = OpConstant %float -3.15737653 +%float_n0_485249996 = OpConstant %float -0.485249996 +%float_1_84773242 = OpConstant %float 1.84773242 +%float_n0_718548238 = OpConstant %float -0.718548238 +%float_2_08103061 = OpConstant %float 2.08103061 +%float_3_6681242 = OpConstant %float 3.6681242 + %float_18 = OpConstant %float 18 + %float_7 = OpConstant %float 7 +%_arr_float_uint_5 = OpTypeArray %float %uint_5 +%type__Globals = OpTypeStruct %v4float %v3float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %float %float %float %float %float %_arr_float_uint_5 %v3float %v4float %float %float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %float %float %uint %uint %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %402 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %bool = OpTypeBool +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %v2bool = OpTypeVector %bool 2 + %v3bool = OpTypeVector %bool 3 +%type_sampled_image = OpTypeSampledImage %type_2d_image + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_arr_float_uint_6 = OpTypeArray %float %uint_6 + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %Texture1 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%Texture1Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %gl_Layer = OpVariable %_ptr_Input_uint Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function__arr_float_uint_6 = OpTypePointer Function %_arr_float_uint_6 +%_ptr_Function__arr_float_uint_10 = OpTypePointer Function %_arr_float_uint_10 + %416 = OpConstantComposite %v3float %float_0_952552378 %float_0 %float_9_36786018en05 + %417 = OpConstantComposite %v3float %float_0_343966454 %float_0_728166103 %float_n0_0721325427 + %418 = OpConstantComposite %v3float %float_0 %float_0 %float_1_00882518 + %419 = OpConstantComposite %mat3v3float %416 %417 %418 + %420 = OpConstantComposite %v3float %float_1_04981101 %float_0 %float_n9_74845025en05 + %421 = OpConstantComposite %v3float %float_n0_495903015 %float_1_37331307 %float_0_0982400328 + %422 = OpConstantComposite %v3float %float_0 %float_0 %float_0_991252005 + %423 = OpConstantComposite %mat3v3float %420 %421 %422 + %424 = OpConstantComposite %v3float %float_0_662454188 %float_0_134004205 %float_0_156187683 + %425 = OpConstantComposite %v3float %float_n0_00557464967 %float_0_0040607336 %float_1_01033914 + %426 = OpConstantComposite %mat3v3float %424 %73 %425 + %427 = OpConstantComposite %v3float %float_1_6410234 %float_n0_324803293 %float_n0_236424699 + %428 = OpConstantComposite %v3float %float_n0_663662851 %float_1_61533165 %float_0_0167563483 + %429 = OpConstantComposite %v3float %float_0_0117218941 %float_n0_00828444213 %float_0_988394856 + %430 = OpConstantComposite %mat3v3float %427 %428 %429 + %431 = OpConstantComposite %v3float %float_1_45143926 %float_n0_236510754 %float_n0_214928567 + %432 = OpConstantComposite %v3float %float_n0_0765537769 %float_1_17622972 %float_n0_0996759236 + %433 = OpConstantComposite %v3float %float_0_00831614807 %float_n0_00603244966 %float_0_997716308 + %434 = OpConstantComposite %mat3v3float %431 %432 %433 + %435 = OpConstantComposite %v3float %float_0_695452213 %float_0_140678704 %float_0_163869068 + %436 = OpConstantComposite %v3float %float_0_0447945632 %float_0_859671116 %float_0_0955343172 + %437 = OpConstantComposite %v3float %float_n0_00552588282 %float_0_00402521016 %float_1_00150073 + %438 = OpConstantComposite %mat3v3float %435 %436 %437 + %439 = OpConstantComposite %v3float %float_3_2409699 %float_n1_5373832 %float_n0_498610765 + %440 = OpConstantComposite %v3float %float_n0_969243646 %float_1_8759675 %float_0_0415550582 + %441 = OpConstantComposite %v3float %float_0_0556300804 %float_n0_203976959 %float_1_05697155 + %442 = OpConstantComposite %mat3v3float %439 %440 %441 + %443 = OpConstantComposite %v3float %float_0_412456393 %float_0_357576102 %float_0_180437505 + %444 = OpConstantComposite %v3float %float_0_212672904 %float_0_715152204 %float_0_0721750036 + %445 = OpConstantComposite %v3float %float_0_0193339009 %float_0_119191997 %float_0_950304091 + %446 = OpConstantComposite %mat3v3float %443 %444 %445 + %447 = OpConstantComposite %v3float %float_1_71660841 %float_n0_355662107 %float_n0_253360093 + %448 = OpConstantComposite %v3float %float_n0_666682899 %float_1_61647761 %float_0_0157685 + %449 = OpConstantComposite %v3float %float_0_0176422 %float_n0_0427763015 %float_0_942228675 + %450 = OpConstantComposite %mat3v3float %447 %448 %449 + %451 = OpConstantComposite %v3float %float_2_49339628 %float_n0_93134588 %float_n0_402694494 + %452 = OpConstantComposite %v3float %float_n0_829486787 %float_1_76265967 %float_0_0236246008 + %453 = OpConstantComposite %v3float %float_0_0358507 %float_n0_0761827007 %float_0_957014024 + %454 = OpConstantComposite %mat3v3float %451 %452 %453 + %455 = OpConstantComposite %v3float %float_1_01303005 %float_0_00610530982 %float_n0_0149710001 + %456 = OpConstantComposite %v3float %float_0_00769822998 %float_0_998165011 %float_n0_00503202993 + %457 = OpConstantComposite %v3float %float_n0_00284131011 %float_0_00468515977 %float_0_924507022 + %458 = OpConstantComposite %mat3v3float %455 %456 %457 + %459 = OpConstantComposite %v3float %float_0_987223983 %float_n0_00611326983 %float_0_0159533005 + %460 = OpConstantComposite %v3float %float_n0_00759836007 %float_1_00186002 %float_0_0053300201 + %461 = OpConstantComposite %v3float %float_0_00307257008 %float_n0_00509594986 %float_1_08168006 + %462 = OpConstantComposite %mat3v3float %459 %460 %461 + %463 = OpConstantComposite %v3float %float_0_5 %float_n1 %float_0_5 + %464 = OpConstantComposite %v3float %float_n1 %float_1 %float_0_5 + %465 = OpConstantComposite %v3float %float_0_5 %float_0 %float_0 + %466 = OpConstantComposite %mat3v3float %463 %464 %465 + %467 = OpConstantComposite %v3float %float_1 %float_0 %float_0 + %468 = OpConstantComposite %v3float %float_0 %float_1 %float_0 + %469 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %470 = OpConstantComposite %mat3v3float %467 %468 %469 +%float_n6_07624626 = OpConstant %float -6.07624626 + %472 = OpConstantComposite %v3float %float_n6_07624626 %float_n6_07624626 %float_n6_07624626 + %473 = OpConstantComposite %v3float %float_0_895099998 %float_0_266400009 %float_n0_161400005 + %474 = OpConstantComposite %v3float %float_n0_750199974 %float_1_71350002 %float_0_0366999991 + %475 = OpConstantComposite %v3float %float_0_0388999991 %float_n0_0684999973 %float_1_02960002 + %476 = OpConstantComposite %mat3v3float %473 %474 %475 + %477 = OpConstantComposite %v3float %float_0_986992896 %float_n0_1470543 %float_0_159962699 + %478 = OpConstantComposite %v3float %float_0_432305306 %float_0_518360317 %float_0_0492912009 + %479 = OpConstantComposite %v3float %float_n0_0085287001 %float_0_040042799 %float_0_968486726 + %480 = OpConstantComposite %mat3v3float %477 %478 %479 + %481 = OpConstantComposite %v3float %float_0_544169128 %float_0_239592597 %float_0_166694298 + %482 = OpConstantComposite %v3float %float_0_239465594 %float_0_702153027 %float_0_058381401 + %483 = OpConstantComposite %v3float %float_n0_00234390004 %float_0_0361833982 %float_1_05521834 + %484 = OpConstantComposite %mat3v3float %481 %482 %483 + %485 = OpConstantComposite %v3float %float_0_940437257 %float_n0_0183068793 %float_0_077869609 + %486 = OpConstantComposite %v3float %float_0_00837869663 %float_0_828660011 %float_0_162961304 + %487 = OpConstantComposite %v3float %float_0_00054712611 %float_n0_000883374596 %float_1_00033629 + %488 = OpConstantComposite %mat3v3float %485 %486 %487 + %489 = OpConstantComposite %v3float %float_1_06317997 %float_0_0233955998 %float_n0_0865726024 + %490 = OpConstantComposite %v3float %float_n0_0106336996 %float_1_20632005 %float_n0_195690006 + %491 = OpConstantComposite %v3float %float_n0_000590886979 %float_0_00105247996 %float_0_999538004 + %492 = OpConstantComposite %mat3v3float %489 %490 %491 +%float_0_0533333346 = OpConstant %float 0.0533333346 +%float_0_159999996 = OpConstant %float 0.159999996 +%float_57_2957764 = OpConstant %float 57.2957764 +%float_0_0625 = OpConstant %float 0.0625 +%float_n67_5 = OpConstant %float -67.5 + %float_67_5 = OpConstant %float 67.5 + %499 = OpConstantComposite %_arr_float_uint_6 %float_n4 %float_n4 %float_n3_15737653 %float_n0_485249996 %float_1_84773242 %float_1_84773242 + %500 = OpConstantComposite %_arr_float_uint_6 %float_n0_718548238 %float_2_08103061 %float_3_6681242 %float_4 %float_4 %float_4 + %float_n15 = OpConstant %float -15 + %float_n14 = OpConstant %float -14 + %503 = OpConstantComposite %_arr_float_uint_10 %float_n4_97062206 %float_n3_02937818 %float_n2_12619996 %float_n1_51049995 %float_n1_05780005 %float_n0_466800004 %float_0_119379997 %float_0_708813429 %float_1_29118657 %float_1_29118657 + %504 = OpConstantComposite %_arr_float_uint_10 %float_0_808913231 %float_1_19108677 %float_1_56830001 %float_1_9483 %float_2_30830002 %float_2_63840008 %float_2_85949993 %float_2_98726082 %float_3_01273918 %float_3_01273918 + %float_n12 = OpConstant %float -12 + %506 = OpConstantComposite %_arr_float_uint_10 %float_n2_30102992 %float_n2_30102992 %float_n1_93120003 %float_n1_52049994 %float_n1_05780005 %float_n0_466800004 %float_0_119379997 %float_0_708813429 %float_1_29118657 %float_1_29118657 + %507 = OpConstantComposite %_arr_float_uint_10 %float_0_801995218 %float_1_19800484 %float_1_59430003 %float_1_99730003 %float_2_37829995 %float_2_76839995 %float_3_05150008 %float_3_27462935 %float_3_32743073 %float_3_32743073 +%float_0_0322580636 = OpConstant %float 0.0322580636 +%float_1_03225803 = OpConstant %float 1.03225803 + %510 = OpConstantComposite %v2float %float_1_03225803 %float_1_03225803 +%float_4_60443853e_09 = OpConstant %float 4.60443853e+09 +%float_2_00528435e_09 = OpConstant %float 2.00528435e+09 +%float_0_333333343 = OpConstant %float 0.333333343 + %float_5 = OpConstant %float 5 + %float_2_5 = OpConstant %float 2.5 +%float_0_0250000004 = OpConstant %float 0.0250000004 +%float_0_239999995 = OpConstant %float 0.239999995 +%float_0_0148148146 = OpConstant %float 0.0148148146 + %519 = OpConstantComposite %v3float %float_9_99999975en05 %float_9_99999975en05 %float_9_99999975en05 +%float_0_0296296291 = OpConstant %float 0.0296296291 +%float_0_952381015 = OpConstant %float 0.952381015 + %522 = OpConstantComposite %v3float %float_0_952381015 %float_0_952381015 %float_0_952381015 + %523 = OpUndef %v3float +%float_0_358299971 = OpConstant %float 0.358299971 + %525 = OpUndef %v3float + %MainPS = OpFunction %void None %402 + %526 = OpLabel + %527 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %528 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %529 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %530 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %531 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %532 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %533 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %534 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %535 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %536 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %537 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %538 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %539 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %540 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %541 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %542 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %543 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %544 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %545 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %546 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %547 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %548 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %549 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %550 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %551 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %552 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %553 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %554 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %555 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %556 = OpVariable %_ptr_Function__arr_float_uint_10 Function + %557 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %558 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %559 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %560 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %561 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %562 = OpVariable %_ptr_Function__arr_float_uint_6 Function + %563 = OpLoad %v2float %in_var_TEXCOORD0 + %564 = OpLoad %uint %gl_Layer + %565 = OpFSub %v2float %563 %134 + %566 = OpFMul %v2float %565 %510 + %567 = OpCompositeExtract %float %566 0 + %568 = OpCompositeExtract %float %566 1 + %569 = OpConvertUToF %float %564 + %570 = OpFMul %float %569 %float_0_0322580636 + %571 = OpCompositeConstruct %v4float %567 %568 %570 %float_0 + %572 = OpMatrixTimesMatrix %mat3v3float %446 %458 + %573 = OpMatrixTimesMatrix %mat3v3float %572 %430 + %574 = OpMatrixTimesMatrix %mat3v3float %426 %462 + %575 = OpMatrixTimesMatrix %mat3v3float %574 %442 + %576 = OpMatrixTimesMatrix %mat3v3float %419 %430 + %577 = OpMatrixTimesMatrix %mat3v3float %426 %423 + %578 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_43 + %579 = OpLoad %uint %578 + OpBranch %580 + %580 = OpLabel + OpLoopMerge %581 %582 None + OpBranch %583 + %583 = OpLabel + %584 = OpMatrixTimesMatrix %mat3v3float %574 %454 + %585 = OpMatrixTimesMatrix %mat3v3float %574 %450 + %586 = OpIEqual %bool %579 %uint_1 + OpSelectionMerge %587 None + OpBranchConditional %586 %588 %589 + %589 = OpLabel + %590 = OpIEqual %bool %579 %uint_2 + OpSelectionMerge %591 None + OpBranchConditional %590 %592 %593 + %593 = OpLabel + %594 = OpIEqual %bool %579 %uint_3 + OpSelectionMerge %595 None + OpBranchConditional %594 %596 %597 + %597 = OpLabel + %598 = OpIEqual %bool %579 %uint_4 + OpSelectionMerge %599 None + OpBranchConditional %598 %600 %601 + %601 = OpLabel + OpBranch %581 + %600 = OpLabel + OpBranch %581 + %599 = OpLabel + OpUnreachable + %596 = OpLabel + OpBranch %581 + %595 = OpLabel + OpUnreachable + %592 = OpLabel + OpBranch %581 + %591 = OpLabel + OpUnreachable + %588 = OpLabel + OpBranch %581 + %587 = OpLabel + OpUnreachable + %582 = OpLabel + OpBranch %580 + %581 = OpLabel + %602 = OpPhi %mat3v3float %575 %601 %470 %600 %438 %596 %585 %592 %584 %588 + %603 = OpVectorShuffle %v3float %571 %571 0 1 2 + %604 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_42 + %605 = OpLoad %uint %604 + %606 = OpUGreaterThanEqual %bool %605 %uint_3 + OpSelectionMerge %607 None + OpBranchConditional %606 %608 %609 + %609 = OpLabel + %610 = OpFSub %v3float %603 %204 + %611 = OpFMul %v3float %610 %205 + %612 = OpExtInst %v3float %1 Exp2 %611 + %613 = OpFMul %v3float %612 %206 + %614 = OpExtInst %v3float %1 Exp2 %472 + %615 = OpFMul %v3float %614 %206 + %616 = OpFSub %v3float %613 %615 + OpBranch %607 + %608 = OpLabel + %617 = OpExtInst %v3float %1 Pow %603 %194 + %618 = OpFSub %v3float %617 %195 + %619 = OpExtInst %v3float %1 FMax %138 %618 + %620 = OpFMul %v3float %197 %617 + %621 = OpFSub %v3float %196 %620 + %622 = OpFDiv %v3float %619 %621 + %623 = OpExtInst %v3float %1 Pow %622 %199 + %624 = OpFMul %v3float %623 %200 + OpBranch %607 + %607 = OpLabel + %625 = OpPhi %v3float %616 %609 %624 %608 + %626 = OpAccessChain %_ptr_Uniform_float %_Globals %int_18 + %627 = OpLoad %float %626 + %628 = OpFMul %float %627 %float_1_00055635 + %629 = OpFOrdLessThanEqual %bool %628 %float_7000 + %630 = OpFDiv %float %float_4_60443853e_09 %627 + %631 = OpFSub %float %float_2967800 %630 + %632 = OpFDiv %float %631 %628 + %633 = OpFAdd %float %float_99_1100006 %632 + %634 = OpFDiv %float %633 %628 + %635 = OpFAdd %float %float_0_244063005 %634 + %636 = OpFDiv %float %float_2_00528435e_09 %627 + %637 = OpFSub %float %float_1901800 %636 + %638 = OpFDiv %float %637 %628 + %639 = OpFAdd %float %float_247_479996 %638 + %640 = OpFDiv %float %639 %628 + %641 = OpFAdd %float %float_0_237039998 %640 + %642 = OpSelect %float %629 %635 %641 + %643 = OpFMul %float %float_n3 %642 + %644 = OpFMul %float %643 %642 + %645 = OpFMul %float %float_2_86999989 %642 + %646 = OpFAdd %float %644 %645 + %647 = OpFSub %float %646 %float_0_275000006 + %648 = OpCompositeConstruct %v2float %642 %647 + %649 = OpFMul %float %float_0_000154118257 %627 + %650 = OpFAdd %float %float_0_860117733 %649 + %651 = OpFMul %float %float_1_28641219en07 %627 + %652 = OpFMul %float %651 %627 + %653 = OpFAdd %float %650 %652 + %654 = OpFMul %float %float_0_00084242021 %627 + %655 = OpFAdd %float %float_1 %654 + %656 = OpFMul %float %float_7_08145137en07 %627 + %657 = OpFMul %float %656 %627 + %658 = OpFAdd %float %655 %657 + %659 = OpFDiv %float %653 %658 + %660 = OpFMul %float %float_4_22806261en05 %627 + %661 = OpFAdd %float %float_0_317398727 %660 + %662 = OpFMul %float %float_4_20481676en08 %627 + %663 = OpFMul %float %662 %627 + %664 = OpFAdd %float %661 %663 + %665 = OpFMul %float %float_2_8974182en05 %627 + %666 = OpFSub %float %float_1 %665 + %667 = OpFMul %float %float_1_61456057en07 %627 + %668 = OpFMul %float %667 %627 + %669 = OpFAdd %float %666 %668 + %670 = OpFDiv %float %664 %669 + %671 = OpFMul %float %float_3 %659 + %672 = OpFMul %float %float_2 %659 + %673 = OpFMul %float %float_8 %670 + %674 = OpFSub %float %672 %673 + %675 = OpFAdd %float %674 %float_4 + %676 = OpFDiv %float %671 %675 + %677 = OpFMul %float %float_2 %670 + %678 = OpFDiv %float %677 %675 + %679 = OpCompositeConstruct %v2float %676 %678 + %680 = OpFOrdLessThan %bool %627 %float_4000 + %681 = OpCompositeConstruct %v2bool %680 %680 + %682 = OpSelect %v2float %681 %679 %648 + %683 = OpAccessChain %_ptr_Uniform_float %_Globals %int_19 + %684 = OpLoad %float %683 + %685 = OpCompositeConstruct %v2float %659 %670 + %686 = OpExtInst %v2float %1 Normalize %685 + %687 = OpCompositeExtract %float %686 1 + %688 = OpFNegate %float %687 + %689 = OpFMul %float %688 %684 + %690 = OpFMul %float %689 %float_0_0500000007 + %691 = OpFAdd %float %659 %690 + %692 = OpCompositeExtract %float %686 0 + %693 = OpFMul %float %692 %684 + %694 = OpFMul %float %693 %float_0_0500000007 + %695 = OpFAdd %float %670 %694 + %696 = OpFMul %float %float_3 %691 + %697 = OpFMul %float %float_2 %691 + %698 = OpFMul %float %float_8 %695 + %699 = OpFSub %float %697 %698 + %700 = OpFAdd %float %699 %float_4 + %701 = OpFDiv %float %696 %700 + %702 = OpFMul %float %float_2 %695 + %703 = OpFDiv %float %702 %700 + %704 = OpCompositeConstruct %v2float %701 %703 + %705 = OpFSub %v2float %704 %679 + %706 = OpFAdd %v2float %682 %705 + %707 = OpCompositeExtract %float %706 0 + %708 = OpCompositeExtract %float %706 1 + %709 = OpExtInst %float %1 FMax %708 %float_1_00000001en10 + %710 = OpFDiv %float %707 %709 + %711 = OpCompositeInsert %v3float %710 %523 0 + %712 = OpCompositeInsert %v3float %float_1 %711 1 + %713 = OpFSub %float %float_1 %707 + %714 = OpFSub %float %713 %708 + %715 = OpFDiv %float %714 %709 + %716 = OpCompositeInsert %v3float %715 %712 2 + %717 = OpExtInst %float %1 FMax %float_0_328999996 %float_1_00000001en10 + %718 = OpFDiv %float %float_0_312700003 %717 + %719 = OpCompositeInsert %v3float %718 %523 0 + %720 = OpCompositeInsert %v3float %float_1 %719 1 + %721 = OpFDiv %float %float_0_358299971 %717 + %722 = OpCompositeInsert %v3float %721 %720 2 + %723 = OpVectorTimesMatrix %v3float %716 %476 + %724 = OpVectorTimesMatrix %v3float %722 %476 + %725 = OpCompositeExtract %float %724 0 + %726 = OpCompositeExtract %float %723 0 + %727 = OpFDiv %float %725 %726 + %728 = OpCompositeConstruct %v3float %727 %float_0 %float_0 + %729 = OpCompositeExtract %float %724 1 + %730 = OpCompositeExtract %float %723 1 + %731 = OpFDiv %float %729 %730 + %732 = OpCompositeConstruct %v3float %float_0 %731 %float_0 + %733 = OpCompositeExtract %float %724 2 + %734 = OpCompositeExtract %float %723 2 + %735 = OpFDiv %float %733 %734 + %736 = OpCompositeConstruct %v3float %float_0 %float_0 %735 + %737 = OpCompositeConstruct %mat3v3float %728 %732 %736 + %738 = OpMatrixTimesMatrix %mat3v3float %476 %737 + %739 = OpMatrixTimesMatrix %mat3v3float %738 %480 + %740 = OpMatrixTimesMatrix %mat3v3float %446 %739 + %741 = OpMatrixTimesMatrix %mat3v3float %740 %442 + %742 = OpVectorTimesMatrix %v3float %625 %741 + %743 = OpVectorTimesMatrix %v3float %742 %573 + %744 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_9 + %745 = OpAccessChain %_ptr_Uniform_float %_Globals %int_9 %int_3 + %746 = OpLoad %float %745 + %747 = OpFOrdNotEqual %bool %746 %float_0 + OpSelectionMerge %748 None + OpBranchConditional %747 %749 %748 + %749 = OpLabel + %750 = OpDot %float %743 %73 + %751 = OpCompositeConstruct %v3float %750 %750 %750 + %752 = OpFDiv %v3float %743 %751 + %753 = OpFSub %v3float %752 %141 + %754 = OpDot %float %753 %753 + %755 = OpFMul %float %float_n4 %754 + %756 = OpExtInst %float %1 Exp2 %755 + %757 = OpFSub %float %float_1 %756 + %758 = OpAccessChain %_ptr_Uniform_float %_Globals %int_45 + %759 = OpLoad %float %758 + %760 = OpFMul %float %float_n4 %759 + %761 = OpFMul %float %760 %750 + %762 = OpFMul %float %761 %750 + %763 = OpExtInst %float %1 Exp2 %762 + %764 = OpFSub %float %float_1 %763 + %765 = OpFMul %float %757 %764 + %766 = OpMatrixTimesMatrix %mat3v3float %484 %430 + %767 = OpMatrixTimesMatrix %mat3v3float %575 %766 + %768 = OpVectorTimesMatrix %v3float %743 %767 + %769 = OpCompositeConstruct %v3float %765 %765 %765 + %770 = OpExtInst %v3float %1 FMix %743 %768 %769 + OpBranch %748 + %748 = OpLabel + %771 = OpPhi %v3float %743 %607 %770 %749 + %772 = OpDot %float %771 %73 + %773 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_25 + %774 = OpLoad %v4float %773 + %775 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_20 + %776 = OpLoad %v4float %775 + %777 = OpFMul %v4float %774 %776 + %778 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_26 + %779 = OpLoad %v4float %778 + %780 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_21 + %781 = OpLoad %v4float %780 + %782 = OpFMul %v4float %779 %781 + %783 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_27 + %784 = OpLoad %v4float %783 + %785 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_22 + %786 = OpLoad %v4float %785 + %787 = OpFMul %v4float %784 %786 + %788 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_28 + %789 = OpLoad %v4float %788 + %790 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_23 + %791 = OpLoad %v4float %790 + %792 = OpFMul %v4float %789 %791 + %793 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_29 + %794 = OpLoad %v4float %793 + %795 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_24 + %796 = OpLoad %v4float %795 + %797 = OpFAdd %v4float %794 %796 + %798 = OpCompositeConstruct %v3float %772 %772 %772 + %799 = OpVectorShuffle %v3float %777 %777 0 1 2 + %800 = OpCompositeExtract %float %777 3 + %801 = OpCompositeConstruct %v3float %800 %800 %800 + %802 = OpFMul %v3float %799 %801 + %803 = OpExtInst %v3float %1 FMix %798 %771 %802 + %804 = OpExtInst %v3float %1 FMax %138 %803 + %805 = OpFMul %v3float %804 %330 + %806 = OpVectorShuffle %v3float %782 %782 0 1 2 + %807 = OpCompositeExtract %float %782 3 + %808 = OpCompositeConstruct %v3float %807 %807 %807 + %809 = OpFMul %v3float %806 %808 + %810 = OpExtInst %v3float %1 Pow %805 %809 + %811 = OpFMul %v3float %810 %206 + %812 = OpVectorShuffle %v3float %787 %787 0 1 2 + %813 = OpCompositeExtract %float %787 3 + %814 = OpCompositeConstruct %v3float %813 %813 %813 + %815 = OpFMul %v3float %812 %814 + %816 = OpFDiv %v3float %141 %815 + %817 = OpExtInst %v3float %1 Pow %811 %816 + %818 = OpVectorShuffle %v3float %792 %792 0 1 2 + %819 = OpCompositeExtract %float %792 3 + %820 = OpCompositeConstruct %v3float %819 %819 %819 + %821 = OpFMul %v3float %818 %820 + %822 = OpFMul %v3float %817 %821 + %823 = OpVectorShuffle %v3float %797 %797 0 1 2 + %824 = OpCompositeExtract %float %797 3 + %825 = OpCompositeConstruct %v3float %824 %824 %824 + %826 = OpFAdd %v3float %823 %825 + %827 = OpFAdd %v3float %822 %826 + %828 = OpAccessChain %_ptr_Uniform_float %_Globals %int_40 + %829 = OpLoad %float %828 + %830 = OpExtInst %float %1 SmoothStep %float_0 %829 %772 + %831 = OpFSub %float %float_1 %830 + %832 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_35 + %833 = OpLoad %v4float %832 + %834 = OpFMul %v4float %833 %776 + %835 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_36 + %836 = OpLoad %v4float %835 + %837 = OpFMul %v4float %836 %781 + %838 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_37 + %839 = OpLoad %v4float %838 + %840 = OpFMul %v4float %839 %786 + %841 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_38 + %842 = OpLoad %v4float %841 + %843 = OpFMul %v4float %842 %791 + %844 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_39 + %845 = OpLoad %v4float %844 + %846 = OpFAdd %v4float %845 %796 + %847 = OpVectorShuffle %v3float %834 %834 0 1 2 + %848 = OpCompositeExtract %float %834 3 + %849 = OpCompositeConstruct %v3float %848 %848 %848 + %850 = OpFMul %v3float %847 %849 + %851 = OpExtInst %v3float %1 FMix %798 %771 %850 + %852 = OpExtInst %v3float %1 FMax %138 %851 + %853 = OpFMul %v3float %852 %330 + %854 = OpVectorShuffle %v3float %837 %837 0 1 2 + %855 = OpCompositeExtract %float %837 3 + %856 = OpCompositeConstruct %v3float %855 %855 %855 + %857 = OpFMul %v3float %854 %856 + %858 = OpExtInst %v3float %1 Pow %853 %857 + %859 = OpFMul %v3float %858 %206 + %860 = OpVectorShuffle %v3float %840 %840 0 1 2 + %861 = OpCompositeExtract %float %840 3 + %862 = OpCompositeConstruct %v3float %861 %861 %861 + %863 = OpFMul %v3float %860 %862 + %864 = OpFDiv %v3float %141 %863 + %865 = OpExtInst %v3float %1 Pow %859 %864 + %866 = OpVectorShuffle %v3float %843 %843 0 1 2 + %867 = OpCompositeExtract %float %843 3 + %868 = OpCompositeConstruct %v3float %867 %867 %867 + %869 = OpFMul %v3float %866 %868 + %870 = OpFMul %v3float %865 %869 + %871 = OpVectorShuffle %v3float %846 %846 0 1 2 + %872 = OpCompositeExtract %float %846 3 + %873 = OpCompositeConstruct %v3float %872 %872 %872 + %874 = OpFAdd %v3float %871 %873 + %875 = OpFAdd %v3float %870 %874 + %876 = OpAccessChain %_ptr_Uniform_float %_Globals %int_41 + %877 = OpLoad %float %876 + %878 = OpExtInst %float %1 SmoothStep %877 %float_1 %772 + %879 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_30 + %880 = OpLoad %v4float %879 + %881 = OpFMul %v4float %880 %776 + %882 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_31 + %883 = OpLoad %v4float %882 + %884 = OpFMul %v4float %883 %781 + %885 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_32 + %886 = OpLoad %v4float %885 + %887 = OpFMul %v4float %886 %786 + %888 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_33 + %889 = OpLoad %v4float %888 + %890 = OpFMul %v4float %889 %791 + %891 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_34 + %892 = OpLoad %v4float %891 + %893 = OpFAdd %v4float %892 %796 + %894 = OpVectorShuffle %v3float %881 %881 0 1 2 + %895 = OpCompositeExtract %float %881 3 + %896 = OpCompositeConstruct %v3float %895 %895 %895 + %897 = OpFMul %v3float %894 %896 + %898 = OpExtInst %v3float %1 FMix %798 %771 %897 + %899 = OpExtInst %v3float %1 FMax %138 %898 + %900 = OpFMul %v3float %899 %330 + %901 = OpVectorShuffle %v3float %884 %884 0 1 2 + %902 = OpCompositeExtract %float %884 3 + %903 = OpCompositeConstruct %v3float %902 %902 %902 + %904 = OpFMul %v3float %901 %903 + %905 = OpExtInst %v3float %1 Pow %900 %904 + %906 = OpFMul %v3float %905 %206 + %907 = OpVectorShuffle %v3float %887 %887 0 1 2 + %908 = OpCompositeExtract %float %887 3 + %909 = OpCompositeConstruct %v3float %908 %908 %908 + %910 = OpFMul %v3float %907 %909 + %911 = OpFDiv %v3float %141 %910 + %912 = OpExtInst %v3float %1 Pow %906 %911 + %913 = OpVectorShuffle %v3float %890 %890 0 1 2 + %914 = OpCompositeExtract %float %890 3 + %915 = OpCompositeConstruct %v3float %914 %914 %914 + %916 = OpFMul %v3float %913 %915 + %917 = OpFMul %v3float %912 %916 + %918 = OpVectorShuffle %v3float %893 %893 0 1 2 + %919 = OpCompositeExtract %float %893 3 + %920 = OpCompositeConstruct %v3float %919 %919 %919 + %921 = OpFAdd %v3float %918 %920 + %922 = OpFAdd %v3float %917 %921 + %923 = OpFSub %float %830 %878 + %924 = OpCompositeConstruct %v3float %831 %831 %831 + %925 = OpFMul %v3float %827 %924 + %926 = OpCompositeConstruct %v3float %923 %923 %923 + %927 = OpFMul %v3float %922 %926 + %928 = OpFAdd %v3float %925 %927 + %929 = OpCompositeConstruct %v3float %878 %878 %878 + %930 = OpFMul %v3float %875 %929 + %931 = OpFAdd %v3float %928 %930 + %932 = OpVectorTimesMatrix %v3float %931 %575 + %933 = OpMatrixTimesMatrix %mat3v3float %577 %488 + %934 = OpMatrixTimesMatrix %mat3v3float %933 %576 + %935 = OpMatrixTimesMatrix %mat3v3float %577 %492 + %936 = OpMatrixTimesMatrix %mat3v3float %935 %576 + %937 = OpVectorTimesMatrix %v3float %931 %934 + %938 = OpAccessChain %_ptr_Uniform_float %_Globals %int_44 + %939 = OpLoad %float %938 + %940 = OpCompositeConstruct %v3float %939 %939 %939 + %941 = OpExtInst %v3float %1 FMix %931 %937 %940 + %942 = OpVectorTimesMatrix %v3float %941 %577 + %943 = OpCompositeExtract %float %942 0 + %944 = OpCompositeExtract %float %942 1 + %945 = OpExtInst %float %1 FMin %943 %944 + %946 = OpCompositeExtract %float %942 2 + %947 = OpExtInst %float %1 FMin %945 %946 + %948 = OpExtInst %float %1 FMax %943 %944 + %949 = OpExtInst %float %1 FMax %948 %946 + %950 = OpExtInst %float %1 FMax %949 %float_1_00000001en10 + %951 = OpExtInst %float %1 FMax %947 %float_1_00000001en10 + %952 = OpFSub %float %950 %951 + %953 = OpExtInst %float %1 FMax %949 %float_0_00999999978 + %954 = OpFDiv %float %952 %953 + %955 = OpFSub %float %946 %944 + %956 = OpFMul %float %946 %955 + %957 = OpFSub %float %944 %943 + %958 = OpFMul %float %944 %957 + %959 = OpFAdd %float %956 %958 + %960 = OpFSub %float %943 %946 + %961 = OpFMul %float %943 %960 + %962 = OpFAdd %float %959 %961 + %963 = OpExtInst %float %1 Sqrt %962 + %964 = OpFAdd %float %946 %944 + %965 = OpFAdd %float %964 %943 + %966 = OpFMul %float %float_1_75 %963 + %967 = OpFAdd %float %965 %966 + %968 = OpFMul %float %967 %float_0_333333343 + %969 = OpFSub %float %954 %float_0_400000006 + %970 = OpFMul %float %969 %float_5 + %971 = OpFMul %float %969 %float_2_5 + %972 = OpExtInst %float %1 FAbs %971 + %973 = OpFSub %float %float_1 %972 + %974 = OpExtInst %float %1 FMax %973 %float_0 + %975 = OpExtInst %float %1 FSign %970 + %976 = OpConvertFToS %int %975 + %977 = OpConvertSToF %float %976 + %978 = OpFMul %float %974 %974 + %979 = OpFSub %float %float_1 %978 + %980 = OpFMul %float %977 %979 + %981 = OpFAdd %float %float_1 %980 + %982 = OpFMul %float %981 %float_0_0250000004 + %983 = OpFOrdLessThanEqual %bool %968 %float_0_0533333346 + OpSelectionMerge %984 None + OpBranchConditional %983 %985 %986 + %986 = OpLabel + %987 = OpFOrdGreaterThanEqual %bool %968 %float_0_159999996 + OpSelectionMerge %988 None + OpBranchConditional %987 %989 %990 + %990 = OpLabel + %991 = OpFDiv %float %float_0_239999995 %967 + %992 = OpFSub %float %991 %float_0_5 + %993 = OpFMul %float %982 %992 + OpBranch %988 + %989 = OpLabel + OpBranch %988 + %988 = OpLabel + %994 = OpPhi %float %993 %990 %float_0 %989 + OpBranch %984 + %985 = OpLabel + OpBranch %984 + %984 = OpLabel + %995 = OpPhi %float %994 %988 %982 %985 + %996 = OpFAdd %float %float_1 %995 + %997 = OpCompositeConstruct %v3float %996 %996 %996 + %998 = OpFMul %v3float %942 %997 + %999 = OpCompositeExtract %float %998 0 + %1000 = OpCompositeExtract %float %998 1 + %1001 = OpFOrdEqual %bool %999 %1000 + %1002 = OpCompositeExtract %float %998 2 + %1003 = OpFOrdEqual %bool %1000 %1002 + %1004 = OpLogicalAnd %bool %1001 %1003 + OpSelectionMerge %1005 None + OpBranchConditional %1004 %1006 %1007 + %1007 = OpLabel + %1008 = OpExtInst %float %1 Sqrt %float_3 + %1009 = OpFSub %float %1000 %1002 + %1010 = OpFMul %float %1008 %1009 + %1011 = OpFMul %float %float_2 %999 + %1012 = OpFSub %float %1011 %1000 + %1013 = OpFSub %float %1012 %1002 + %1014 = OpExtInst %float %1 Atan2 %1010 %1013 + %1015 = OpFMul %float %float_57_2957764 %1014 + OpBranch %1005 + %1006 = OpLabel + OpBranch %1005 + %1005 = OpLabel + %1016 = OpPhi %float %1015 %1007 %float_0 %1006 + %1017 = OpFOrdLessThan %bool %1016 %float_0 + OpSelectionMerge %1018 None + OpBranchConditional %1017 %1019 %1018 + %1019 = OpLabel + %1020 = OpFAdd %float %1016 %float_360 + OpBranch %1018 + %1018 = OpLabel + %1021 = OpPhi %float %1016 %1005 %1020 %1019 + %1022 = OpExtInst %float %1 FClamp %1021 %float_0 %float_360 + %1023 = OpFOrdGreaterThan %bool %1022 %float_180 + OpSelectionMerge %1024 None + OpBranchConditional %1023 %1025 %1024 + %1025 = OpLabel + %1026 = OpFSub %float %1022 %float_360 + OpBranch %1024 + %1024 = OpLabel + %1027 = OpPhi %float %1022 %1018 %1026 %1025 + %1028 = OpFMul %float %1027 %float_0_0148148146 + %1029 = OpExtInst %float %1 FAbs %1028 + %1030 = OpFSub %float %float_1 %1029 + %1031 = OpExtInst %float %1 SmoothStep %float_0 %float_1 %1030 + %1032 = OpFMul %float %1031 %1031 + %1033 = OpFMul %float %1032 %954 + %1034 = OpFSub %float %float_0_0299999993 %999 + %1035 = OpFMul %float %1033 %1034 + %1036 = OpFMul %float %1035 %float_0_180000007 + %1037 = OpFAdd %float %999 %1036 + %1038 = OpCompositeInsert %v3float %1037 %998 0 + %1039 = OpVectorTimesMatrix %v3float %1038 %434 + %1040 = OpExtInst %v3float %1 FMax %138 %1039 + %1041 = OpDot %float %1040 %73 + %1042 = OpCompositeConstruct %v3float %1041 %1041 %1041 + %1043 = OpExtInst %v3float %1 FMix %1042 %1040 %241 + %1044 = OpAccessChain %_ptr_Uniform_float %_Globals %int_13 + %1045 = OpLoad %float %1044 + %1046 = OpFAdd %float %float_1 %1045 + %1047 = OpAccessChain %_ptr_Uniform_float %_Globals %int_11 + %1048 = OpLoad %float %1047 + %1049 = OpFSub %float %1046 %1048 + %1050 = OpAccessChain %_ptr_Uniform_float %_Globals %int_14 + %1051 = OpLoad %float %1050 + %1052 = OpFAdd %float %float_1 %1051 + %1053 = OpAccessChain %_ptr_Uniform_float %_Globals %int_12 + %1054 = OpLoad %float %1053 + %1055 = OpFSub %float %1052 %1054 + %1056 = OpFOrdGreaterThan %bool %1048 %float_0_800000012 + OpSelectionMerge %1057 None + OpBranchConditional %1056 %1058 %1059 + %1059 = OpLabel + %1060 = OpFAdd %float %float_0_180000007 %1045 + %1061 = OpFDiv %float %1060 %1049 + %1062 = OpExtInst %float %1 Log %float_0_180000007 + %1063 = OpExtInst %float %1 Log %float_10 + %1064 = OpFDiv %float %1062 %1063 + %1065 = OpFSub %float %float_2 %1061 + %1066 = OpFDiv %float %1061 %1065 + %1067 = OpExtInst %float %1 Log %1066 + %1068 = OpFMul %float %float_0_5 %1067 + %1069 = OpAccessChain %_ptr_Uniform_float %_Globals %int_10 + %1070 = OpLoad %float %1069 + %1071 = OpFDiv %float %1049 %1070 + %1072 = OpFMul %float %1068 %1071 + %1073 = OpFSub %float %1064 %1072 + OpBranch %1057 + %1058 = OpLabel + %1074 = OpFSub %float %float_0_819999993 %1048 + %1075 = OpAccessChain %_ptr_Uniform_float %_Globals %int_10 + %1076 = OpLoad %float %1075 + %1077 = OpFDiv %float %1074 %1076 + %1078 = OpExtInst %float %1 Log %float_0_180000007 + %1079 = OpExtInst %float %1 Log %float_10 + %1080 = OpFDiv %float %1078 %1079 + %1081 = OpFAdd %float %1077 %1080 + OpBranch %1057 + %1057 = OpLabel + %1082 = OpPhi %float %1073 %1059 %1081 %1058 + %1083 = OpFSub %float %float_1 %1048 + %1084 = OpAccessChain %_ptr_Uniform_float %_Globals %int_10 + %1085 = OpLoad %float %1084 + %1086 = OpFDiv %float %1083 %1085 + %1087 = OpFSub %float %1086 %1082 + %1088 = OpFDiv %float %1054 %1085 + %1089 = OpFSub %float %1088 %1087 + %1090 = OpExtInst %v3float %1 Log %1043 + %1091 = OpExtInst %float %1 Log %float_10 + %1092 = OpCompositeConstruct %v3float %1091 %1091 %1091 + %1093 = OpFDiv %v3float %1090 %1092 + %1094 = OpCompositeConstruct %v3float %1085 %1085 %1085 + %1095 = OpCompositeConstruct %v3float %1087 %1087 %1087 + %1096 = OpFAdd %v3float %1093 %1095 + %1097 = OpFMul %v3float %1094 %1096 + %1098 = OpFNegate %float %1045 + %1099 = OpCompositeConstruct %v3float %1098 %1098 %1098 + %1100 = OpFMul %float %float_2 %1049 + %1101 = OpCompositeConstruct %v3float %1100 %1100 %1100 + %1102 = OpFMul %float %float_n2 %1085 + %1103 = OpFDiv %float %1102 %1049 + %1104 = OpCompositeConstruct %v3float %1103 %1103 %1103 + %1105 = OpCompositeConstruct %v3float %1082 %1082 %1082 + %1106 = OpFSub %v3float %1093 %1105 + %1107 = OpFMul %v3float %1104 %1106 + %1108 = OpExtInst %v3float %1 Exp %1107 + %1109 = OpFAdd %v3float %141 %1108 + %1110 = OpFDiv %v3float %1101 %1109 + %1111 = OpFAdd %v3float %1099 %1110 + %1112 = OpCompositeConstruct %v3float %1052 %1052 %1052 + %1113 = OpFMul %float %float_2 %1055 + %1114 = OpCompositeConstruct %v3float %1113 %1113 %1113 + %1115 = OpFMul %float %float_2 %1085 + %1116 = OpFDiv %float %1115 %1055 + %1117 = OpCompositeConstruct %v3float %1116 %1116 %1116 + %1118 = OpCompositeConstruct %v3float %1089 %1089 %1089 + %1119 = OpFSub %v3float %1093 %1118 + %1120 = OpFMul %v3float %1117 %1119 + %1121 = OpExtInst %v3float %1 Exp %1120 + %1122 = OpFAdd %v3float %141 %1121 + %1123 = OpFDiv %v3float %1114 %1122 + %1124 = OpFSub %v3float %1112 %1123 + %1125 = OpFOrdLessThan %v3bool %1093 %1105 + %1126 = OpSelect %v3float %1125 %1111 %1097 + %1127 = OpFOrdGreaterThan %v3bool %1093 %1118 + %1128 = OpSelect %v3float %1127 %1124 %1097 + %1129 = OpFSub %float %1089 %1082 + %1130 = OpCompositeConstruct %v3float %1129 %1129 %1129 + %1131 = OpFDiv %v3float %1106 %1130 + %1132 = OpExtInst %v3float %1 FClamp %1131 %138 %141 + %1133 = OpFOrdLessThan %bool %1089 %1082 + %1134 = OpFSub %v3float %141 %1132 + %1135 = OpCompositeConstruct %v3bool %1133 %1133 %1133 + %1136 = OpSelect %v3float %1135 %1134 %1132 + %1137 = OpFMul %v3float %252 %1136 + %1138 = OpFSub %v3float %251 %1137 + %1139 = OpFMul %v3float %1138 %1136 + %1140 = OpFMul %v3float %1139 %1136 + %1141 = OpExtInst %v3float %1 FMix %1126 %1128 %1140 + %1142 = OpDot %float %1141 %73 + %1143 = OpCompositeConstruct %v3float %1142 %1142 %1142 + %1144 = OpExtInst %v3float %1 FMix %1143 %1141 %254 + %1145 = OpExtInst %v3float %1 FMax %138 %1144 + %1146 = OpVectorTimesMatrix %v3float %1145 %936 + %1147 = OpExtInst %v3float %1 FMix %1145 %1146 %940 + %1148 = OpVectorTimesMatrix %v3float %1147 %575 + %1149 = OpExtInst %v3float %1 FMax %138 %1148 + %1150 = OpFOrdEqual %bool %746 %float_0 + OpSelectionMerge %1151 DontFlatten + OpBranchConditional %1150 %1152 %1151 + %1152 = OpLabel + %1153 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_2 + %1154 = OpLoad %v4float %1153 + %1155 = OpVectorShuffle %v3float %1154 %1154 0 1 2 + %1156 = OpDot %float %932 %1155 + %1157 = OpCompositeInsert %v3float %1156 %525 0 + %1158 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_3 + %1159 = OpLoad %v4float %1158 + %1160 = OpVectorShuffle %v3float %1159 %1159 0 1 2 + %1161 = OpDot %float %932 %1160 + %1162 = OpCompositeInsert %v3float %1161 %1157 1 + %1163 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_4 + %1164 = OpLoad %v4float %1163 + %1165 = OpVectorShuffle %v3float %1164 %1164 0 1 2 + %1166 = OpDot %float %932 %1165 + %1167 = OpCompositeInsert %v3float %1166 %1162 2 + %1168 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_8 + %1169 = OpLoad %v4float %1168 + %1170 = OpVectorShuffle %v3float %1169 %1169 0 1 2 + %1171 = OpLoad %v4float %744 + %1172 = OpVectorShuffle %v3float %1171 %1171 0 1 2 + %1173 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_7 + %1174 = OpLoad %v4float %1173 + %1175 = OpVectorShuffle %v3float %1174 %1174 0 1 2 + %1176 = OpDot %float %932 %1175 + %1177 = OpFAdd %float %1176 %float_1 + %1178 = OpFDiv %float %float_1 %1177 + %1179 = OpCompositeConstruct %v3float %1178 %1178 %1178 + %1180 = OpFMul %v3float %1172 %1179 + %1181 = OpFAdd %v3float %1170 %1180 + %1182 = OpFMul %v3float %1167 %1181 + %1183 = OpExtInst %v3float %1 FMax %138 %1182 + %1184 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_5 + %1185 = OpLoad %v4float %1184 + %1186 = OpVectorShuffle %v3float %1185 %1185 0 0 0 + %1187 = OpFSub %v3float %1186 %1183 + %1188 = OpExtInst %v3float %1 FMax %138 %1187 + %1189 = OpVectorShuffle %v3float %1185 %1185 2 2 2 + %1190 = OpExtInst %v3float %1 FMax %1183 %1189 + %1191 = OpExtInst %v3float %1 FClamp %1183 %1186 %1189 + %1192 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_6 + %1193 = OpLoad %v4float %1192 + %1194 = OpVectorShuffle %v3float %1193 %1193 0 0 0 + %1195 = OpFMul %v3float %1190 %1194 + %1196 = OpVectorShuffle %v3float %1193 %1193 1 1 1 + %1197 = OpFAdd %v3float %1195 %1196 + %1198 = OpVectorShuffle %v3float %1185 %1185 3 3 3 + %1199 = OpFAdd %v3float %1190 %1198 + %1200 = OpFDiv %v3float %141 %1199 + %1201 = OpFMul %v3float %1197 %1200 + %1202 = OpVectorShuffle %v3float %1164 %1164 3 3 3 + %1203 = OpFMul %v3float %1191 %1202 + %1204 = OpVectorShuffle %v3float %1154 %1154 3 3 3 + %1205 = OpFMul %v3float %1188 %1204 + %1206 = OpVectorShuffle %v3float %1185 %1185 1 1 1 + %1207 = OpFAdd %v3float %1188 %1206 + %1208 = OpFDiv %v3float %141 %1207 + %1209 = OpFMul %v3float %1205 %1208 + %1210 = OpVectorShuffle %v3float %1159 %1159 3 3 3 + %1211 = OpFAdd %v3float %1209 %1210 + %1212 = OpFAdd %v3float %1203 %1211 + %1213 = OpFAdd %v3float %1201 %1212 + %1214 = OpFSub %v3float %1213 %261 + OpBranch %1151 + %1151 = OpLabel + %1215 = OpPhi %v3float %1149 %1057 %1214 %1152 + %1216 = OpExtInst %v3float %1 FClamp %1215 %138 %141 + %1217 = OpCompositeExtract %float %1216 0 + OpBranch %1218 + %1218 = OpLabel + OpLoopMerge %1219 %1220 None + OpBranch %1221 + %1221 = OpLabel + %1222 = OpFOrdLessThan %bool %1217 %float_0_00313066994 + OpSelectionMerge %1223 None + OpBranchConditional %1222 %1224 %1223 + %1224 = OpLabel + %1225 = OpFMul %float %1217 %float_12_9200001 + OpBranch %1219 + %1223 = OpLabel + %1226 = OpExtInst %float %1 Pow %1217 %float_0_416666657 + %1227 = OpFMul %float %1226 %float_1_05499995 + %1228 = OpFSub %float %1227 %float_0_0549999997 + OpBranch %1219 + %1220 = OpLabel + OpBranch %1218 + %1219 = OpLabel + %1229 = OpPhi %float %1225 %1224 %1228 %1223 + %1230 = OpCompositeExtract %float %1216 1 + OpBranch %1231 + %1231 = OpLabel + OpLoopMerge %1232 %1233 None + OpBranch %1234 + %1234 = OpLabel + %1235 = OpFOrdLessThan %bool %1230 %float_0_00313066994 + OpSelectionMerge %1236 None + OpBranchConditional %1235 %1237 %1236 + %1237 = OpLabel + %1238 = OpFMul %float %1230 %float_12_9200001 + OpBranch %1232 + %1236 = OpLabel + %1239 = OpExtInst %float %1 Pow %1230 %float_0_416666657 + %1240 = OpFMul %float %1239 %float_1_05499995 + %1241 = OpFSub %float %1240 %float_0_0549999997 + OpBranch %1232 + %1233 = OpLabel + OpBranch %1231 + %1232 = OpLabel + %1242 = OpPhi %float %1238 %1237 %1241 %1236 + %1243 = OpCompositeExtract %float %1216 2 + OpBranch %1244 + %1244 = OpLabel + OpLoopMerge %1245 %1246 None + OpBranch %1247 + %1247 = OpLabel + %1248 = OpFOrdLessThan %bool %1243 %float_0_00313066994 + OpSelectionMerge %1249 None + OpBranchConditional %1248 %1250 %1249 + %1250 = OpLabel + %1251 = OpFMul %float %1243 %float_12_9200001 + OpBranch %1245 + %1249 = OpLabel + %1252 = OpExtInst %float %1 Pow %1243 %float_0_416666657 + %1253 = OpFMul %float %1252 %float_1_05499995 + %1254 = OpFSub %float %1253 %float_0_0549999997 + OpBranch %1245 + %1246 = OpLabel + OpBranch %1244 + %1245 = OpLabel + %1255 = OpPhi %float %1251 %1250 %1254 %1249 + %1256 = OpCompositeConstruct %v3float %1229 %1242 %1255 + %1257 = OpFMul %v3float %1256 %173 + %1258 = OpFAdd %v3float %1257 %175 + %1259 = OpAccessChain %_ptr_Uniform_float %_Globals %int_15 %int_0 + %1260 = OpLoad %float %1259 + %1261 = OpCompositeConstruct %v3float %1260 %1260 %1260 + %1262 = OpFMul %v3float %1261 %1256 + %1263 = OpAccessChain %_ptr_Uniform_float %_Globals %int_15 %int_1 + %1264 = OpLoad %float %1263 + %1265 = OpCompositeConstruct %v3float %1264 %1264 %1264 + %1266 = OpLoad %type_2d_image %Texture1 + %1267 = OpLoad %type_sampler %Texture1Sampler + %1268 = OpCompositeExtract %float %1258 2 + %1269 = OpFMul %float %1268 %float_16 + %1270 = OpFSub %float %1269 %float_0_5 + %1271 = OpExtInst %float %1 Floor %1270 + %1272 = OpFSub %float %1270 %1271 + %1273 = OpCompositeExtract %float %1258 0 + %1274 = OpFAdd %float %1273 %1271 + %1275 = OpFMul %float %1274 %float_0_0625 + %1276 = OpCompositeExtract %float %1258 1 + %1277 = OpCompositeConstruct %v2float %1275 %1276 + %1278 = OpSampledImage %type_sampled_image %1266 %1267 + %1279 = OpImageSampleImplicitLod %v4float %1278 %1277 None + %1280 = OpFAdd %float %1275 %float_0_0625 + %1281 = OpCompositeConstruct %v2float %1280 %1276 + %1282 = OpSampledImage %type_sampled_image %1266 %1267 + %1283 = OpImageSampleImplicitLod %v4float %1282 %1281 None + %1284 = OpCompositeConstruct %v4float %1272 %1272 %1272 %1272 + %1285 = OpExtInst %v4float %1 FMix %1279 %1283 %1284 + %1286 = OpVectorShuffle %v3float %1285 %1285 0 1 2 + %1287 = OpFMul %v3float %1265 %1286 + %1288 = OpFAdd %v3float %1262 %1287 + %1289 = OpExtInst %v3float %1 FMax %263 %1288 + %1290 = OpFOrdGreaterThan %v3bool %1289 %265 + %1291 = OpFMul %v3float %1289 %267 + %1292 = OpFAdd %v3float %1291 %269 + %1293 = OpExtInst %v3float %1 Pow %1292 %271 + %1294 = OpFMul %v3float %1289 %273 + %1295 = OpSelect %v3float %1290 %1293 %1294 + %1296 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_0 + %1297 = OpLoad %float %1296 + %1298 = OpCompositeConstruct %v3float %1297 %1297 %1297 + %1299 = OpFMul %v3float %1295 %1295 + %1300 = OpFMul %v3float %1298 %1299 + %1301 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_1 + %1302 = OpLoad %float %1301 + %1303 = OpCompositeConstruct %v3float %1302 %1302 %1302 + %1304 = OpFMul %v3float %1303 %1295 + %1305 = OpFAdd %v3float %1300 %1304 + %1306 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_2 + %1307 = OpLoad %float %1306 + %1308 = OpCompositeConstruct %v3float %1307 %1307 %1307 + %1309 = OpFAdd %v3float %1305 %1308 + %1310 = OpAccessChain %_ptr_Uniform_v3float %_Globals %int_16 + %1311 = OpLoad %v3float %1310 + %1312 = OpFMul %v3float %1309 %1311 + %1313 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_17 + %1314 = OpLoad %v4float %1313 + %1315 = OpVectorShuffle %v3float %1314 %1314 0 1 2 + %1316 = OpAccessChain %_ptr_Uniform_float %_Globals %int_17 %int_3 + %1317 = OpLoad %float %1316 + %1318 = OpCompositeConstruct %v3float %1317 %1317 %1317 + %1319 = OpExtInst %v3float %1 FMix %1312 %1315 %1318 + %1320 = OpExtInst %v3float %1 FMax %138 %1319 + %1321 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 %int_1 + %1322 = OpLoad %float %1321 + %1323 = OpCompositeConstruct %v3float %1322 %1322 %1322 + %1324 = OpExtInst %v3float %1 Pow %1320 %1323 + %1325 = OpIEqual %bool %605 %uint_0 + OpSelectionMerge %1326 DontFlatten + OpBranchConditional %1325 %1327 %1328 + %1328 = OpLabel + %1329 = OpIEqual %bool %605 %uint_1 + OpSelectionMerge %1330 None + OpBranchConditional %1329 %1331 %1332 + %1332 = OpLabel + %1333 = OpIEqual %bool %605 %uint_3 + %1334 = OpIEqual %bool %605 %uint_5 + %1335 = OpLogicalOr %bool %1333 %1334 + OpSelectionMerge %1336 None + OpBranchConditional %1335 %1337 %1338 + %1338 = OpLabel + %1339 = OpIEqual %bool %605 %uint_4 + %1340 = OpIEqual %bool %605 %uint_6 + %1341 = OpLogicalOr %bool %1339 %1340 + OpSelectionMerge %1342 None + OpBranchConditional %1341 %1343 %1344 + %1344 = OpLabel + %1345 = OpIEqual %bool %605 %uint_7 + OpSelectionMerge %1346 None + OpBranchConditional %1345 %1347 %1348 + %1348 = OpLabel + %1349 = OpVectorTimesMatrix %v3float %1324 %573 + %1350 = OpVectorTimesMatrix %v3float %1349 %602 + %1351 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 %int_2 + %1352 = OpLoad %float %1351 + %1353 = OpCompositeConstruct %v3float %1352 %1352 %1352 + %1354 = OpExtInst %v3float %1 Pow %1350 %1353 + OpBranch %1346 + %1347 = OpLabel + %1355 = OpVectorTimesMatrix %v3float %932 %573 + %1356 = OpVectorTimesMatrix %v3float %1355 %602 + %1357 = OpFMul %v3float %1356 %519 + %1358 = OpExtInst %v3float %1 Pow %1357 %286 + %1359 = OpFMul %v3float %196 %1358 + %1360 = OpFAdd %v3float %195 %1359 + %1361 = OpFMul %v3float %197 %1358 + %1362 = OpFAdd %v3float %141 %1361 + %1363 = OpFDiv %v3float %141 %1362 + %1364 = OpFMul %v3float %1360 %1363 + %1365 = OpExtInst %v3float %1 Pow %1364 %287 + OpBranch %1346 + %1346 = OpLabel + %1366 = OpPhi %v3float %1354 %1348 %1365 %1347 + OpBranch %1342 + %1343 = OpLabel + %1367 = OpMatrixTimesMatrix %mat3v3float %572 %423 + %1368 = OpFMul %v3float %932 %285 + %1369 = OpVectorTimesMatrix %v3float %1368 %1367 + %1370 = OpCompositeExtract %float %1369 0 + %1371 = OpCompositeExtract %float %1369 1 + %1372 = OpExtInst %float %1 FMin %1370 %1371 + %1373 = OpCompositeExtract %float %1369 2 + %1374 = OpExtInst %float %1 FMin %1372 %1373 + %1375 = OpExtInst %float %1 FMax %1370 %1371 + %1376 = OpExtInst %float %1 FMax %1375 %1373 + %1377 = OpExtInst %float %1 FMax %1376 %float_1_00000001en10 + %1378 = OpExtInst %float %1 FMax %1374 %float_1_00000001en10 + %1379 = OpFSub %float %1377 %1378 + %1380 = OpExtInst %float %1 FMax %1376 %float_0_00999999978 + %1381 = OpFDiv %float %1379 %1380 + %1382 = OpFSub %float %1373 %1371 + %1383 = OpFMul %float %1373 %1382 + %1384 = OpFSub %float %1371 %1370 + %1385 = OpFMul %float %1371 %1384 + %1386 = OpFAdd %float %1383 %1385 + %1387 = OpFSub %float %1370 %1373 + %1388 = OpFMul %float %1370 %1387 + %1389 = OpFAdd %float %1386 %1388 + %1390 = OpExtInst %float %1 Sqrt %1389 + %1391 = OpFAdd %float %1373 %1371 + %1392 = OpFAdd %float %1391 %1370 + %1393 = OpFMul %float %float_1_75 %1390 + %1394 = OpFAdd %float %1392 %1393 + %1395 = OpFMul %float %1394 %float_0_333333343 + %1396 = OpFSub %float %1381 %float_0_400000006 + %1397 = OpFMul %float %1396 %float_5 + %1398 = OpFMul %float %1396 %float_2_5 + %1399 = OpExtInst %float %1 FAbs %1398 + %1400 = OpFSub %float %float_1 %1399 + %1401 = OpExtInst %float %1 FMax %1400 %float_0 + %1402 = OpExtInst %float %1 FSign %1397 + %1403 = OpConvertFToS %int %1402 + %1404 = OpConvertSToF %float %1403 + %1405 = OpFMul %float %1401 %1401 + %1406 = OpFSub %float %float_1 %1405 + %1407 = OpFMul %float %1404 %1406 + %1408 = OpFAdd %float %float_1 %1407 + %1409 = OpFMul %float %1408 %float_0_0250000004 + %1410 = OpFOrdLessThanEqual %bool %1395 %float_0_0533333346 + OpSelectionMerge %1411 None + OpBranchConditional %1410 %1412 %1413 + %1413 = OpLabel + %1414 = OpFOrdGreaterThanEqual %bool %1395 %float_0_159999996 + OpSelectionMerge %1415 None + OpBranchConditional %1414 %1416 %1417 + %1417 = OpLabel + %1418 = OpFDiv %float %float_0_239999995 %1394 + %1419 = OpFSub %float %1418 %float_0_5 + %1420 = OpFMul %float %1409 %1419 + OpBranch %1415 + %1416 = OpLabel + OpBranch %1415 + %1415 = OpLabel + %1421 = OpPhi %float %1420 %1417 %float_0 %1416 + OpBranch %1411 + %1412 = OpLabel + OpBranch %1411 + %1411 = OpLabel + %1422 = OpPhi %float %1421 %1415 %1409 %1412 + %1423 = OpFAdd %float %float_1 %1422 + %1424 = OpCompositeConstruct %v3float %1423 %1423 %1423 + %1425 = OpFMul %v3float %1369 %1424 + %1426 = OpCompositeExtract %float %1425 0 + %1427 = OpCompositeExtract %float %1425 1 + %1428 = OpFOrdEqual %bool %1426 %1427 + %1429 = OpCompositeExtract %float %1425 2 + %1430 = OpFOrdEqual %bool %1427 %1429 + %1431 = OpLogicalAnd %bool %1428 %1430 + OpSelectionMerge %1432 None + OpBranchConditional %1431 %1433 %1434 + %1434 = OpLabel + %1435 = OpExtInst %float %1 Sqrt %float_3 + %1436 = OpFSub %float %1427 %1429 + %1437 = OpFMul %float %1435 %1436 + %1438 = OpFMul %float %float_2 %1426 + %1439 = OpFSub %float %1438 %1427 + %1440 = OpFSub %float %1439 %1429 + %1441 = OpExtInst %float %1 Atan2 %1437 %1440 + %1442 = OpFMul %float %float_57_2957764 %1441 + OpBranch %1432 + %1433 = OpLabel + OpBranch %1432 + %1432 = OpLabel + %1443 = OpPhi %float %1442 %1434 %float_0 %1433 + %1444 = OpFOrdLessThan %bool %1443 %float_0 + OpSelectionMerge %1445 None + OpBranchConditional %1444 %1446 %1445 + %1446 = OpLabel + %1447 = OpFAdd %float %1443 %float_360 + OpBranch %1445 + %1445 = OpLabel + %1448 = OpPhi %float %1443 %1432 %1447 %1446 + %1449 = OpExtInst %float %1 FClamp %1448 %float_0 %float_360 + %1450 = OpFOrdGreaterThan %bool %1449 %float_180 + OpSelectionMerge %1451 None + OpBranchConditional %1450 %1452 %1451 + %1452 = OpLabel + %1453 = OpFSub %float %1449 %float_360 + OpBranch %1451 + %1451 = OpLabel + %1454 = OpPhi %float %1449 %1445 %1453 %1452 + %1455 = OpFOrdGreaterThan %bool %1454 %float_n67_5 + %1456 = OpFOrdLessThan %bool %1454 %float_67_5 + %1457 = OpLogicalAnd %bool %1455 %1456 + OpSelectionMerge %1458 None + OpBranchConditional %1457 %1459 %1458 + %1459 = OpLabel + %1460 = OpFSub %float %1454 %float_n67_5 + %1461 = OpFMul %float %1460 %float_0_0296296291 + %1462 = OpConvertFToS %int %1461 + %1463 = OpConvertSToF %float %1462 + %1464 = OpFSub %float %1461 %1463 + %1465 = OpFMul %float %1464 %1464 + %1466 = OpFMul %float %1465 %1464 + %1467 = OpIEqual %bool %1462 %int_3 + OpSelectionMerge %1468 None + OpBranchConditional %1467 %1469 %1470 + %1470 = OpLabel + %1471 = OpIEqual %bool %1462 %int_2 + OpSelectionMerge %1472 None + OpBranchConditional %1471 %1473 %1474 + %1474 = OpLabel + %1475 = OpIEqual %bool %1462 %int_1 + OpSelectionMerge %1476 None + OpBranchConditional %1475 %1477 %1478 + %1478 = OpLabel + %1479 = OpIEqual %bool %1462 %int_0 + OpSelectionMerge %1480 None + OpBranchConditional %1479 %1481 %1482 + %1482 = OpLabel + OpBranch %1480 + %1481 = OpLabel + %1483 = OpFMul %float %1466 %float_0_166666672 + OpBranch %1480 + %1480 = OpLabel + %1484 = OpPhi %float %float_0 %1482 %1483 %1481 + OpBranch %1476 + %1477 = OpLabel + %1485 = OpFMul %float %1466 %float_n0_5 + %1486 = OpFMul %float %1465 %float_0_5 + %1487 = OpFAdd %float %1485 %1486 + %1488 = OpFMul %float %1464 %float_0_5 + %1489 = OpFAdd %float %1487 %1488 + %1490 = OpFAdd %float %1489 %float_0_166666672 + OpBranch %1476 + %1476 = OpLabel + %1491 = OpPhi %float %1484 %1480 %1490 %1477 + OpBranch %1472 + %1473 = OpLabel + %1492 = OpFMul %float %1466 %float_0_5 + %1493 = OpFMul %float %1465 %float_n1 + %1494 = OpFAdd %float %1492 %1493 + %1495 = OpFAdd %float %1494 %float_0_666666687 + OpBranch %1472 + %1472 = OpLabel + %1496 = OpPhi %float %1491 %1476 %1495 %1473 + OpBranch %1468 + %1469 = OpLabel + %1497 = OpFMul %float %1466 %float_n0_166666672 + %1498 = OpFMul %float %1465 %float_0_5 + %1499 = OpFAdd %float %1497 %1498 + %1500 = OpFMul %float %1464 %float_n0_5 + %1501 = OpFAdd %float %1499 %1500 + %1502 = OpFAdd %float %1501 %float_0_166666672 + OpBranch %1468 + %1468 = OpLabel + %1503 = OpPhi %float %1496 %1472 %1502 %1469 + OpBranch %1458 + %1458 = OpLabel + %1504 = OpPhi %float %float_0 %1451 %1503 %1468 + %1505 = OpFMul %float %1504 %float_1_5 + %1506 = OpFMul %float %1505 %1381 + %1507 = OpFSub %float %float_0_0299999993 %1426 + %1508 = OpFMul %float %1506 %1507 + %1509 = OpFMul %float %1508 %float_0_180000007 + %1510 = OpFAdd %float %1426 %1509 + %1511 = OpCompositeInsert %v3float %1510 %1425 0 + %1512 = OpExtInst %v3float %1 FClamp %1511 %138 %337 + %1513 = OpVectorTimesMatrix %v3float %1512 %434 + %1514 = OpExtInst %v3float %1 FClamp %1513 %138 %337 + %1515 = OpDot %float %1514 %73 + %1516 = OpCompositeConstruct %v3float %1515 %1515 %1515 + %1517 = OpExtInst %v3float %1 FMix %1516 %1514 %241 + %1518 = OpCompositeExtract %float %1517 0 + %1519 = OpExtInst %float %1 Exp2 %float_n15 + %1520 = OpFMul %float %float_0_179999992 %1519 + %1521 = OpExtInst %float %1 Exp2 %float_18 + %1522 = OpFMul %float %float_0_179999992 %1521 + OpStore %528 %499 + OpStore %527 %500 + %1523 = OpFOrdLessThanEqual %bool %1518 %float_0 + %1524 = OpExtInst %float %1 Exp2 %float_n14 + %1525 = OpSelect %float %1523 %1524 %1518 + %1526 = OpExtInst %float %1 Log %1525 + %1527 = OpFDiv %float %1526 %1091 + %1528 = OpExtInst %float %1 Log %1520 + %1529 = OpFDiv %float %1528 %1091 + %1530 = OpFOrdLessThanEqual %bool %1527 %1529 + OpSelectionMerge %1531 None + OpBranchConditional %1530 %1532 %1533 + %1533 = OpLabel + %1534 = OpFOrdGreaterThan %bool %1527 %1529 + %1535 = OpExtInst %float %1 Log %float_0_180000007 + %1536 = OpFDiv %float %1535 %1091 + %1537 = OpFOrdLessThan %bool %1527 %1536 + %1538 = OpLogicalAnd %bool %1534 %1537 + OpSelectionMerge %1539 None + OpBranchConditional %1538 %1540 %1541 + %1541 = OpLabel + %1542 = OpFOrdGreaterThanEqual %bool %1527 %1536 + %1543 = OpExtInst %float %1 Log %1522 + %1544 = OpFDiv %float %1543 %1091 + %1545 = OpFOrdLessThan %bool %1527 %1544 + %1546 = OpLogicalAnd %bool %1542 %1545 + OpSelectionMerge %1547 None + OpBranchConditional %1546 %1548 %1549 + %1549 = OpLabel + %1550 = OpExtInst %float %1 Log %float_10000 + %1551 = OpFDiv %float %1550 %1091 + OpBranch %1547 + %1548 = OpLabel + %1552 = OpFSub %float %1527 %1536 + %1553 = OpFMul %float %float_3 %1552 + %1554 = OpFSub %float %1544 %1536 + %1555 = OpFDiv %float %1553 %1554 + %1556 = OpConvertFToS %int %1555 + %1557 = OpConvertSToF %float %1556 + %1558 = OpFSub %float %1555 %1557 + %1559 = OpAccessChain %_ptr_Function_float %527 %1556 + %1560 = OpLoad %float %1559 + %1561 = OpIAdd %int %1556 %int_1 + %1562 = OpAccessChain %_ptr_Function_float %527 %1561 + %1563 = OpLoad %float %1562 + %1564 = OpIAdd %int %1556 %int_2 + %1565 = OpAccessChain %_ptr_Function_float %527 %1564 + %1566 = OpLoad %float %1565 + %1567 = OpCompositeConstruct %v3float %1560 %1563 %1566 + %1568 = OpFMul %float %1558 %1558 + %1569 = OpCompositeConstruct %v3float %1568 %1558 %float_1 + %1570 = OpMatrixTimesVector %v3float %466 %1567 + %1571 = OpDot %float %1569 %1570 + OpBranch %1547 + %1547 = OpLabel + %1572 = OpPhi %float %1551 %1549 %1571 %1548 + OpBranch %1539 + %1540 = OpLabel + %1573 = OpFSub %float %1527 %1529 + %1574 = OpFMul %float %float_3 %1573 + %1575 = OpFSub %float %1536 %1529 + %1576 = OpFDiv %float %1574 %1575 + %1577 = OpConvertFToS %int %1576 + %1578 = OpConvertSToF %float %1577 + %1579 = OpFSub %float %1576 %1578 + %1580 = OpAccessChain %_ptr_Function_float %528 %1577 + %1581 = OpLoad %float %1580 + %1582 = OpIAdd %int %1577 %int_1 + %1583 = OpAccessChain %_ptr_Function_float %528 %1582 + %1584 = OpLoad %float %1583 + %1585 = OpIAdd %int %1577 %int_2 + %1586 = OpAccessChain %_ptr_Function_float %528 %1585 + %1587 = OpLoad %float %1586 + %1588 = OpCompositeConstruct %v3float %1581 %1584 %1587 + %1589 = OpFMul %float %1579 %1579 + %1590 = OpCompositeConstruct %v3float %1589 %1579 %float_1 + %1591 = OpMatrixTimesVector %v3float %466 %1588 + %1592 = OpDot %float %1590 %1591 + OpBranch %1539 + %1539 = OpLabel + %1593 = OpPhi %float %1572 %1547 %1592 %1540 + OpBranch %1531 + %1532 = OpLabel + %1594 = OpExtInst %float %1 Log %float_9_99999975en05 + %1595 = OpFDiv %float %1594 %1091 + OpBranch %1531 + %1531 = OpLabel + %1596 = OpPhi %float %1593 %1539 %1595 %1532 + %1597 = OpExtInst %float %1 Pow %float_10 %1596 + %1598 = OpCompositeInsert %v3float %1597 %523 0 + %1599 = OpCompositeExtract %float %1517 1 + OpStore %530 %499 + OpStore %529 %500 + %1600 = OpFOrdLessThanEqual %bool %1599 %float_0 + %1601 = OpSelect %float %1600 %1524 %1599 + %1602 = OpExtInst %float %1 Log %1601 + %1603 = OpFDiv %float %1602 %1091 + %1604 = OpFOrdLessThanEqual %bool %1603 %1529 + OpSelectionMerge %1605 None + OpBranchConditional %1604 %1606 %1607 + %1607 = OpLabel + %1608 = OpFOrdGreaterThan %bool %1603 %1529 + %1609 = OpExtInst %float %1 Log %float_0_180000007 + %1610 = OpFDiv %float %1609 %1091 + %1611 = OpFOrdLessThan %bool %1603 %1610 + %1612 = OpLogicalAnd %bool %1608 %1611 + OpSelectionMerge %1613 None + OpBranchConditional %1612 %1614 %1615 + %1615 = OpLabel + %1616 = OpFOrdGreaterThanEqual %bool %1603 %1610 + %1617 = OpExtInst %float %1 Log %1522 + %1618 = OpFDiv %float %1617 %1091 + %1619 = OpFOrdLessThan %bool %1603 %1618 + %1620 = OpLogicalAnd %bool %1616 %1619 + OpSelectionMerge %1621 None + OpBranchConditional %1620 %1622 %1623 + %1623 = OpLabel + %1624 = OpExtInst %float %1 Log %float_10000 + %1625 = OpFDiv %float %1624 %1091 + OpBranch %1621 + %1622 = OpLabel + %1626 = OpFSub %float %1603 %1610 + %1627 = OpFMul %float %float_3 %1626 + %1628 = OpFSub %float %1618 %1610 + %1629 = OpFDiv %float %1627 %1628 + %1630 = OpConvertFToS %int %1629 + %1631 = OpConvertSToF %float %1630 + %1632 = OpFSub %float %1629 %1631 + %1633 = OpAccessChain %_ptr_Function_float %529 %1630 + %1634 = OpLoad %float %1633 + %1635 = OpIAdd %int %1630 %int_1 + %1636 = OpAccessChain %_ptr_Function_float %529 %1635 + %1637 = OpLoad %float %1636 + %1638 = OpIAdd %int %1630 %int_2 + %1639 = OpAccessChain %_ptr_Function_float %529 %1638 + %1640 = OpLoad %float %1639 + %1641 = OpCompositeConstruct %v3float %1634 %1637 %1640 + %1642 = OpFMul %float %1632 %1632 + %1643 = OpCompositeConstruct %v3float %1642 %1632 %float_1 + %1644 = OpMatrixTimesVector %v3float %466 %1641 + %1645 = OpDot %float %1643 %1644 + OpBranch %1621 + %1621 = OpLabel + %1646 = OpPhi %float %1625 %1623 %1645 %1622 + OpBranch %1613 + %1614 = OpLabel + %1647 = OpFSub %float %1603 %1529 + %1648 = OpFMul %float %float_3 %1647 + %1649 = OpFSub %float %1610 %1529 + %1650 = OpFDiv %float %1648 %1649 + %1651 = OpConvertFToS %int %1650 + %1652 = OpConvertSToF %float %1651 + %1653 = OpFSub %float %1650 %1652 + %1654 = OpAccessChain %_ptr_Function_float %530 %1651 + %1655 = OpLoad %float %1654 + %1656 = OpIAdd %int %1651 %int_1 + %1657 = OpAccessChain %_ptr_Function_float %530 %1656 + %1658 = OpLoad %float %1657 + %1659 = OpIAdd %int %1651 %int_2 + %1660 = OpAccessChain %_ptr_Function_float %530 %1659 + %1661 = OpLoad %float %1660 + %1662 = OpCompositeConstruct %v3float %1655 %1658 %1661 + %1663 = OpFMul %float %1653 %1653 + %1664 = OpCompositeConstruct %v3float %1663 %1653 %float_1 + %1665 = OpMatrixTimesVector %v3float %466 %1662 + %1666 = OpDot %float %1664 %1665 + OpBranch %1613 + %1613 = OpLabel + %1667 = OpPhi %float %1646 %1621 %1666 %1614 + OpBranch %1605 + %1606 = OpLabel + %1668 = OpExtInst %float %1 Log %float_9_99999975en05 + %1669 = OpFDiv %float %1668 %1091 + OpBranch %1605 + %1605 = OpLabel + %1670 = OpPhi %float %1667 %1613 %1669 %1606 + %1671 = OpExtInst %float %1 Pow %float_10 %1670 + %1672 = OpCompositeInsert %v3float %1671 %1598 1 + %1673 = OpCompositeExtract %float %1517 2 + OpStore %532 %499 + OpStore %531 %500 + %1674 = OpFOrdLessThanEqual %bool %1673 %float_0 + %1675 = OpSelect %float %1674 %1524 %1673 + %1676 = OpExtInst %float %1 Log %1675 + %1677 = OpFDiv %float %1676 %1091 + %1678 = OpFOrdLessThanEqual %bool %1677 %1529 + OpSelectionMerge %1679 None + OpBranchConditional %1678 %1680 %1681 + %1681 = OpLabel + %1682 = OpFOrdGreaterThan %bool %1677 %1529 + %1683 = OpExtInst %float %1 Log %float_0_180000007 + %1684 = OpFDiv %float %1683 %1091 + %1685 = OpFOrdLessThan %bool %1677 %1684 + %1686 = OpLogicalAnd %bool %1682 %1685 + OpSelectionMerge %1687 None + OpBranchConditional %1686 %1688 %1689 + %1689 = OpLabel + %1690 = OpFOrdGreaterThanEqual %bool %1677 %1684 + %1691 = OpExtInst %float %1 Log %1522 + %1692 = OpFDiv %float %1691 %1091 + %1693 = OpFOrdLessThan %bool %1677 %1692 + %1694 = OpLogicalAnd %bool %1690 %1693 + OpSelectionMerge %1695 None + OpBranchConditional %1694 %1696 %1697 + %1697 = OpLabel + %1698 = OpExtInst %float %1 Log %float_10000 + %1699 = OpFDiv %float %1698 %1091 + OpBranch %1695 + %1696 = OpLabel + %1700 = OpFSub %float %1677 %1684 + %1701 = OpFMul %float %float_3 %1700 + %1702 = OpFSub %float %1692 %1684 + %1703 = OpFDiv %float %1701 %1702 + %1704 = OpConvertFToS %int %1703 + %1705 = OpConvertSToF %float %1704 + %1706 = OpFSub %float %1703 %1705 + %1707 = OpAccessChain %_ptr_Function_float %531 %1704 + %1708 = OpLoad %float %1707 + %1709 = OpIAdd %int %1704 %int_1 + %1710 = OpAccessChain %_ptr_Function_float %531 %1709 + %1711 = OpLoad %float %1710 + %1712 = OpIAdd %int %1704 %int_2 + %1713 = OpAccessChain %_ptr_Function_float %531 %1712 + %1714 = OpLoad %float %1713 + %1715 = OpCompositeConstruct %v3float %1708 %1711 %1714 + %1716 = OpFMul %float %1706 %1706 + %1717 = OpCompositeConstruct %v3float %1716 %1706 %float_1 + %1718 = OpMatrixTimesVector %v3float %466 %1715 + %1719 = OpDot %float %1717 %1718 + OpBranch %1695 + %1695 = OpLabel + %1720 = OpPhi %float %1699 %1697 %1719 %1696 + OpBranch %1687 + %1688 = OpLabel + %1721 = OpFSub %float %1677 %1529 + %1722 = OpFMul %float %float_3 %1721 + %1723 = OpFSub %float %1684 %1529 + %1724 = OpFDiv %float %1722 %1723 + %1725 = OpConvertFToS %int %1724 + %1726 = OpConvertSToF %float %1725 + %1727 = OpFSub %float %1724 %1726 + %1728 = OpAccessChain %_ptr_Function_float %532 %1725 + %1729 = OpLoad %float %1728 + %1730 = OpIAdd %int %1725 %int_1 + %1731 = OpAccessChain %_ptr_Function_float %532 %1730 + %1732 = OpLoad %float %1731 + %1733 = OpIAdd %int %1725 %int_2 + %1734 = OpAccessChain %_ptr_Function_float %532 %1733 + %1735 = OpLoad %float %1734 + %1736 = OpCompositeConstruct %v3float %1729 %1732 %1735 + %1737 = OpFMul %float %1727 %1727 + %1738 = OpCompositeConstruct %v3float %1737 %1727 %float_1 + %1739 = OpMatrixTimesVector %v3float %466 %1736 + %1740 = OpDot %float %1738 %1739 + OpBranch %1687 + %1687 = OpLabel + %1741 = OpPhi %float %1720 %1695 %1740 %1688 + OpBranch %1679 + %1680 = OpLabel + %1742 = OpExtInst %float %1 Log %float_9_99999975en05 + %1743 = OpFDiv %float %1742 %1091 + OpBranch %1679 + %1679 = OpLabel + %1744 = OpPhi %float %1741 %1687 %1743 %1680 + %1745 = OpExtInst %float %1 Pow %float_10 %1744 + %1746 = OpCompositeInsert %v3float %1745 %1672 2 + %1747 = OpVectorTimesMatrix %v3float %1746 %438 + %1748 = OpVectorTimesMatrix %v3float %1747 %434 + %1749 = OpExtInst %float %1 Pow %float_2 %float_n12 + %1750 = OpFMul %float %float_0_179999992 %1749 + OpStore %540 %499 + OpStore %539 %500 + %1751 = OpFOrdLessThanEqual %bool %1750 %float_0 + %1752 = OpSelect %float %1751 %1524 %1750 + %1753 = OpExtInst %float %1 Log %1752 + %1754 = OpFDiv %float %1753 %1091 + %1755 = OpFOrdLessThanEqual %bool %1754 %1529 + OpSelectionMerge %1756 None + OpBranchConditional %1755 %1757 %1758 + %1758 = OpLabel + %1759 = OpFOrdGreaterThan %bool %1754 %1529 + %1760 = OpExtInst %float %1 Log %float_0_180000007 + %1761 = OpFDiv %float %1760 %1091 + %1762 = OpFOrdLessThan %bool %1754 %1761 + %1763 = OpLogicalAnd %bool %1759 %1762 + OpSelectionMerge %1764 None + OpBranchConditional %1763 %1765 %1766 + %1766 = OpLabel + %1767 = OpFOrdGreaterThanEqual %bool %1754 %1761 + %1768 = OpExtInst %float %1 Log %1522 + %1769 = OpFDiv %float %1768 %1091 + %1770 = OpFOrdLessThan %bool %1754 %1769 + %1771 = OpLogicalAnd %bool %1767 %1770 + OpSelectionMerge %1772 None + OpBranchConditional %1771 %1773 %1774 + %1774 = OpLabel + %1775 = OpExtInst %float %1 Log %float_10000 + %1776 = OpFDiv %float %1775 %1091 + OpBranch %1772 + %1773 = OpLabel + %1777 = OpFSub %float %1754 %1761 + %1778 = OpFMul %float %float_3 %1777 + %1779 = OpFSub %float %1769 %1761 + %1780 = OpFDiv %float %1778 %1779 + %1781 = OpConvertFToS %int %1780 + %1782 = OpConvertSToF %float %1781 + %1783 = OpFSub %float %1780 %1782 + %1784 = OpAccessChain %_ptr_Function_float %539 %1781 + %1785 = OpLoad %float %1784 + %1786 = OpIAdd %int %1781 %int_1 + %1787 = OpAccessChain %_ptr_Function_float %539 %1786 + %1788 = OpLoad %float %1787 + %1789 = OpIAdd %int %1781 %int_2 + %1790 = OpAccessChain %_ptr_Function_float %539 %1789 + %1791 = OpLoad %float %1790 + %1792 = OpCompositeConstruct %v3float %1785 %1788 %1791 + %1793 = OpFMul %float %1783 %1783 + %1794 = OpCompositeConstruct %v3float %1793 %1783 %float_1 + %1795 = OpMatrixTimesVector %v3float %466 %1792 + %1796 = OpDot %float %1794 %1795 + OpBranch %1772 + %1772 = OpLabel + %1797 = OpPhi %float %1776 %1774 %1796 %1773 + OpBranch %1764 + %1765 = OpLabel + %1798 = OpFSub %float %1754 %1529 + %1799 = OpFMul %float %float_3 %1798 + %1800 = OpFSub %float %1761 %1529 + %1801 = OpFDiv %float %1799 %1800 + %1802 = OpConvertFToS %int %1801 + %1803 = OpConvertSToF %float %1802 + %1804 = OpFSub %float %1801 %1803 + %1805 = OpAccessChain %_ptr_Function_float %540 %1802 + %1806 = OpLoad %float %1805 + %1807 = OpIAdd %int %1802 %int_1 + %1808 = OpAccessChain %_ptr_Function_float %540 %1807 + %1809 = OpLoad %float %1808 + %1810 = OpIAdd %int %1802 %int_2 + %1811 = OpAccessChain %_ptr_Function_float %540 %1810 + %1812 = OpLoad %float %1811 + %1813 = OpCompositeConstruct %v3float %1806 %1809 %1812 + %1814 = OpFMul %float %1804 %1804 + %1815 = OpCompositeConstruct %v3float %1814 %1804 %float_1 + %1816 = OpMatrixTimesVector %v3float %466 %1813 + %1817 = OpDot %float %1815 %1816 + OpBranch %1764 + %1764 = OpLabel + %1818 = OpPhi %float %1797 %1772 %1817 %1765 + OpBranch %1756 + %1757 = OpLabel + %1819 = OpExtInst %float %1 Log %float_9_99999975en05 + %1820 = OpFDiv %float %1819 %1091 + OpBranch %1756 + %1756 = OpLabel + %1821 = OpPhi %float %1818 %1764 %1820 %1757 + %1822 = OpExtInst %float %1 Pow %float_10 %1821 + OpStore %542 %499 + OpStore %541 %500 + %1823 = OpExtInst %float %1 Log %float_0_180000007 + %1824 = OpFDiv %float %1823 %1091 + %1825 = OpFOrdLessThanEqual %bool %1824 %1529 + OpSelectionMerge %1826 None + OpBranchConditional %1825 %1827 %1828 + %1828 = OpLabel + %1829 = OpFOrdGreaterThan %bool %1824 %1529 + %1830 = OpFOrdLessThan %bool %1824 %1824 + %1831 = OpLogicalAnd %bool %1829 %1830 + OpSelectionMerge %1832 None + OpBranchConditional %1831 %1833 %1834 + %1834 = OpLabel + %1835 = OpFOrdGreaterThanEqual %bool %1824 %1824 + %1836 = OpExtInst %float %1 Log %1522 + %1837 = OpFDiv %float %1836 %1091 + %1838 = OpFOrdLessThan %bool %1824 %1837 + %1839 = OpLogicalAnd %bool %1835 %1838 + OpSelectionMerge %1840 None + OpBranchConditional %1839 %1841 %1842 + %1842 = OpLabel + %1843 = OpExtInst %float %1 Log %float_10000 + %1844 = OpFDiv %float %1843 %1091 + OpBranch %1840 + %1841 = OpLabel + %1845 = OpFSub %float %1824 %1824 + %1846 = OpFMul %float %float_3 %1845 + %1847 = OpFSub %float %1837 %1824 + %1848 = OpFDiv %float %1846 %1847 + %1849 = OpConvertFToS %int %1848 + %1850 = OpConvertSToF %float %1849 + %1851 = OpFSub %float %1848 %1850 + %1852 = OpAccessChain %_ptr_Function_float %541 %1849 + %1853 = OpLoad %float %1852 + %1854 = OpIAdd %int %1849 %int_1 + %1855 = OpAccessChain %_ptr_Function_float %541 %1854 + %1856 = OpLoad %float %1855 + %1857 = OpIAdd %int %1849 %int_2 + %1858 = OpAccessChain %_ptr_Function_float %541 %1857 + %1859 = OpLoad %float %1858 + %1860 = OpCompositeConstruct %v3float %1853 %1856 %1859 + %1861 = OpFMul %float %1851 %1851 + %1862 = OpCompositeConstruct %v3float %1861 %1851 %float_1 + %1863 = OpMatrixTimesVector %v3float %466 %1860 + %1864 = OpDot %float %1862 %1863 + OpBranch %1840 + %1840 = OpLabel + %1865 = OpPhi %float %1844 %1842 %1864 %1841 + OpBranch %1832 + %1833 = OpLabel + %1866 = OpAccessChain %_ptr_Function_float %542 %int_3 + %1867 = OpLoad %float %1866 + %1868 = OpAccessChain %_ptr_Function_float %542 %int_4 + %1869 = OpLoad %float %1868 + %1870 = OpAccessChain %_ptr_Function_float %542 %int_5 + %1871 = OpLoad %float %1870 + %1872 = OpCompositeConstruct %v3float %1867 %1869 %1871 + %1873 = OpMatrixTimesVector %v3float %466 %1872 + %1874 = OpCompositeExtract %float %1873 2 + OpBranch %1832 + %1832 = OpLabel + %1875 = OpPhi %float %1865 %1840 %1874 %1833 + OpBranch %1826 + %1827 = OpLabel + %1876 = OpExtInst %float %1 Log %float_9_99999975en05 + %1877 = OpFDiv %float %1876 %1091 + OpBranch %1826 + %1826 = OpLabel + %1878 = OpPhi %float %1875 %1832 %1877 %1827 + %1879 = OpExtInst %float %1 Pow %float_10 %1878 + %1880 = OpExtInst %float %1 Pow %float_2 %float_11 + %1881 = OpFMul %float %float_0_179999992 %1880 + OpStore %544 %499 + OpStore %543 %500 + %1882 = OpFOrdLessThanEqual %bool %1881 %float_0 + %1883 = OpSelect %float %1882 %1524 %1881 + %1884 = OpExtInst %float %1 Log %1883 + %1885 = OpFDiv %float %1884 %1091 + %1886 = OpFOrdLessThanEqual %bool %1885 %1529 + OpSelectionMerge %1887 None + OpBranchConditional %1886 %1888 %1889 + %1889 = OpLabel + %1890 = OpFOrdGreaterThan %bool %1885 %1529 + %1891 = OpFOrdLessThan %bool %1885 %1824 + %1892 = OpLogicalAnd %bool %1890 %1891 + OpSelectionMerge %1893 None + OpBranchConditional %1892 %1894 %1895 + %1895 = OpLabel + %1896 = OpFOrdGreaterThanEqual %bool %1885 %1824 + %1897 = OpExtInst %float %1 Log %1522 + %1898 = OpFDiv %float %1897 %1091 + %1899 = OpFOrdLessThan %bool %1885 %1898 + %1900 = OpLogicalAnd %bool %1896 %1899 + OpSelectionMerge %1901 None + OpBranchConditional %1900 %1902 %1903 + %1903 = OpLabel + %1904 = OpExtInst %float %1 Log %float_10000 + %1905 = OpFDiv %float %1904 %1091 + OpBranch %1901 + %1902 = OpLabel + %1906 = OpFSub %float %1885 %1824 + %1907 = OpFMul %float %float_3 %1906 + %1908 = OpFSub %float %1898 %1824 + %1909 = OpFDiv %float %1907 %1908 + %1910 = OpConvertFToS %int %1909 + %1911 = OpConvertSToF %float %1910 + %1912 = OpFSub %float %1909 %1911 + %1913 = OpAccessChain %_ptr_Function_float %543 %1910 + %1914 = OpLoad %float %1913 + %1915 = OpIAdd %int %1910 %int_1 + %1916 = OpAccessChain %_ptr_Function_float %543 %1915 + %1917 = OpLoad %float %1916 + %1918 = OpIAdd %int %1910 %int_2 + %1919 = OpAccessChain %_ptr_Function_float %543 %1918 + %1920 = OpLoad %float %1919 + %1921 = OpCompositeConstruct %v3float %1914 %1917 %1920 + %1922 = OpFMul %float %1912 %1912 + %1923 = OpCompositeConstruct %v3float %1922 %1912 %float_1 + %1924 = OpMatrixTimesVector %v3float %466 %1921 + %1925 = OpDot %float %1923 %1924 + OpBranch %1901 + %1901 = OpLabel + %1926 = OpPhi %float %1905 %1903 %1925 %1902 + OpBranch %1893 + %1894 = OpLabel + %1927 = OpFSub %float %1885 %1529 + %1928 = OpFMul %float %float_3 %1927 + %1929 = OpFSub %float %1824 %1529 + %1930 = OpFDiv %float %1928 %1929 + %1931 = OpConvertFToS %int %1930 + %1932 = OpConvertSToF %float %1931 + %1933 = OpFSub %float %1930 %1932 + %1934 = OpAccessChain %_ptr_Function_float %544 %1931 + %1935 = OpLoad %float %1934 + %1936 = OpIAdd %int %1931 %int_1 + %1937 = OpAccessChain %_ptr_Function_float %544 %1936 + %1938 = OpLoad %float %1937 + %1939 = OpIAdd %int %1931 %int_2 + %1940 = OpAccessChain %_ptr_Function_float %544 %1939 + %1941 = OpLoad %float %1940 + %1942 = OpCompositeConstruct %v3float %1935 %1938 %1941 + %1943 = OpFMul %float %1933 %1933 + %1944 = OpCompositeConstruct %v3float %1943 %1933 %float_1 + %1945 = OpMatrixTimesVector %v3float %466 %1942 + %1946 = OpDot %float %1944 %1945 + OpBranch %1893 + %1893 = OpLabel + %1947 = OpPhi %float %1926 %1901 %1946 %1894 + OpBranch %1887 + %1888 = OpLabel + %1948 = OpExtInst %float %1 Log %float_9_99999975en05 + %1949 = OpFDiv %float %1948 %1091 + OpBranch %1887 + %1887 = OpLabel + %1950 = OpPhi %float %1947 %1893 %1949 %1888 + %1951 = OpExtInst %float %1 Pow %float_10 %1950 + %1952 = OpCompositeExtract %float %1748 0 + OpStore %538 %506 + OpStore %537 %507 + %1953 = OpFOrdLessThanEqual %bool %1952 %float_0 + %1954 = OpSelect %float %1953 %float_9_99999975en05 %1952 + %1955 = OpExtInst %float %1 Log %1954 + %1956 = OpFDiv %float %1955 %1091 + %1957 = OpExtInst %float %1 Log %1822 + %1958 = OpFDiv %float %1957 %1091 + %1959 = OpFOrdLessThanEqual %bool %1956 %1958 + OpSelectionMerge %1960 None + OpBranchConditional %1959 %1961 %1962 + %1962 = OpLabel + %1963 = OpFOrdGreaterThan %bool %1956 %1958 + %1964 = OpExtInst %float %1 Log %1879 + %1965 = OpFDiv %float %1964 %1091 + %1966 = OpFOrdLessThan %bool %1956 %1965 + %1967 = OpLogicalAnd %bool %1963 %1966 + OpSelectionMerge %1968 None + OpBranchConditional %1967 %1969 %1970 + %1970 = OpLabel + %1971 = OpFOrdGreaterThanEqual %bool %1956 %1965 + %1972 = OpExtInst %float %1 Log %1951 + %1973 = OpFDiv %float %1972 %1091 + %1974 = OpFOrdLessThan %bool %1956 %1973 + %1975 = OpLogicalAnd %bool %1971 %1974 + OpSelectionMerge %1976 None + OpBranchConditional %1975 %1977 %1978 + %1978 = OpLabel + %1979 = OpFMul %float %1956 %float_0_119999997 + %1980 = OpExtInst %float %1 Log %float_2000 + %1981 = OpFDiv %float %1980 %1091 + %1982 = OpFMul %float %float_0_119999997 %1972 + %1983 = OpFDiv %float %1982 %1091 + %1984 = OpFSub %float %1981 %1983 + %1985 = OpFAdd %float %1979 %1984 + OpBranch %1976 + %1977 = OpLabel + %1986 = OpFSub %float %1956 %1965 + %1987 = OpFMul %float %float_7 %1986 + %1988 = OpFSub %float %1973 %1965 + %1989 = OpFDiv %float %1987 %1988 + %1990 = OpConvertFToS %int %1989 + %1991 = OpConvertSToF %float %1990 + %1992 = OpFSub %float %1989 %1991 + %1993 = OpAccessChain %_ptr_Function_float %537 %1990 + %1994 = OpLoad %float %1993 + %1995 = OpIAdd %int %1990 %int_1 + %1996 = OpAccessChain %_ptr_Function_float %537 %1995 + %1997 = OpLoad %float %1996 + %1998 = OpIAdd %int %1990 %int_2 + %1999 = OpAccessChain %_ptr_Function_float %537 %1998 + %2000 = OpLoad %float %1999 + %2001 = OpCompositeConstruct %v3float %1994 %1997 %2000 + %2002 = OpFMul %float %1992 %1992 + %2003 = OpCompositeConstruct %v3float %2002 %1992 %float_1 + %2004 = OpMatrixTimesVector %v3float %466 %2001 + %2005 = OpDot %float %2003 %2004 + OpBranch %1976 + %1976 = OpLabel + %2006 = OpPhi %float %1985 %1978 %2005 %1977 + OpBranch %1968 + %1969 = OpLabel + %2007 = OpFSub %float %1956 %1958 + %2008 = OpFMul %float %float_7 %2007 + %2009 = OpFSub %float %1965 %1958 + %2010 = OpFDiv %float %2008 %2009 + %2011 = OpConvertFToS %int %2010 + %2012 = OpConvertSToF %float %2011 + %2013 = OpFSub %float %2010 %2012 + %2014 = OpAccessChain %_ptr_Function_float %538 %2011 + %2015 = OpLoad %float %2014 + %2016 = OpIAdd %int %2011 %int_1 + %2017 = OpAccessChain %_ptr_Function_float %538 %2016 + %2018 = OpLoad %float %2017 + %2019 = OpIAdd %int %2011 %int_2 + %2020 = OpAccessChain %_ptr_Function_float %538 %2019 + %2021 = OpLoad %float %2020 + %2022 = OpCompositeConstruct %v3float %2015 %2018 %2021 + %2023 = OpFMul %float %2013 %2013 + %2024 = OpCompositeConstruct %v3float %2023 %2013 %float_1 + %2025 = OpMatrixTimesVector %v3float %466 %2022 + %2026 = OpDot %float %2024 %2025 + OpBranch %1968 + %1968 = OpLabel + %2027 = OpPhi %float %2006 %1976 %2026 %1969 + OpBranch %1960 + %1961 = OpLabel + %2028 = OpExtInst %float %1 Log %float_0_00499999989 + %2029 = OpFDiv %float %2028 %1091 + OpBranch %1960 + %1960 = OpLabel + %2030 = OpPhi %float %2027 %1968 %2029 %1961 + %2031 = OpExtInst %float %1 Pow %float_10 %2030 + %2032 = OpCompositeInsert %v3float %2031 %523 0 + %2033 = OpCompositeExtract %float %1748 1 + OpStore %536 %506 + OpStore %535 %507 + %2034 = OpFOrdLessThanEqual %bool %2033 %float_0 + %2035 = OpSelect %float %2034 %float_9_99999975en05 %2033 + %2036 = OpExtInst %float %1 Log %2035 + %2037 = OpFDiv %float %2036 %1091 + %2038 = OpFOrdLessThanEqual %bool %2037 %1958 + OpSelectionMerge %2039 None + OpBranchConditional %2038 %2040 %2041 + %2041 = OpLabel + %2042 = OpFOrdGreaterThan %bool %2037 %1958 + %2043 = OpExtInst %float %1 Log %1879 + %2044 = OpFDiv %float %2043 %1091 + %2045 = OpFOrdLessThan %bool %2037 %2044 + %2046 = OpLogicalAnd %bool %2042 %2045 + OpSelectionMerge %2047 None + OpBranchConditional %2046 %2048 %2049 + %2049 = OpLabel + %2050 = OpFOrdGreaterThanEqual %bool %2037 %2044 + %2051 = OpExtInst %float %1 Log %1951 + %2052 = OpFDiv %float %2051 %1091 + %2053 = OpFOrdLessThan %bool %2037 %2052 + %2054 = OpLogicalAnd %bool %2050 %2053 + OpSelectionMerge %2055 None + OpBranchConditional %2054 %2056 %2057 + %2057 = OpLabel + %2058 = OpFMul %float %2037 %float_0_119999997 + %2059 = OpExtInst %float %1 Log %float_2000 + %2060 = OpFDiv %float %2059 %1091 + %2061 = OpFMul %float %float_0_119999997 %2051 + %2062 = OpFDiv %float %2061 %1091 + %2063 = OpFSub %float %2060 %2062 + %2064 = OpFAdd %float %2058 %2063 + OpBranch %2055 + %2056 = OpLabel + %2065 = OpFSub %float %2037 %2044 + %2066 = OpFMul %float %float_7 %2065 + %2067 = OpFSub %float %2052 %2044 + %2068 = OpFDiv %float %2066 %2067 + %2069 = OpConvertFToS %int %2068 + %2070 = OpConvertSToF %float %2069 + %2071 = OpFSub %float %2068 %2070 + %2072 = OpAccessChain %_ptr_Function_float %535 %2069 + %2073 = OpLoad %float %2072 + %2074 = OpIAdd %int %2069 %int_1 + %2075 = OpAccessChain %_ptr_Function_float %535 %2074 + %2076 = OpLoad %float %2075 + %2077 = OpIAdd %int %2069 %int_2 + %2078 = OpAccessChain %_ptr_Function_float %535 %2077 + %2079 = OpLoad %float %2078 + %2080 = OpCompositeConstruct %v3float %2073 %2076 %2079 + %2081 = OpFMul %float %2071 %2071 + %2082 = OpCompositeConstruct %v3float %2081 %2071 %float_1 + %2083 = OpMatrixTimesVector %v3float %466 %2080 + %2084 = OpDot %float %2082 %2083 + OpBranch %2055 + %2055 = OpLabel + %2085 = OpPhi %float %2064 %2057 %2084 %2056 + OpBranch %2047 + %2048 = OpLabel + %2086 = OpFSub %float %2037 %1958 + %2087 = OpFMul %float %float_7 %2086 + %2088 = OpFSub %float %2044 %1958 + %2089 = OpFDiv %float %2087 %2088 + %2090 = OpConvertFToS %int %2089 + %2091 = OpConvertSToF %float %2090 + %2092 = OpFSub %float %2089 %2091 + %2093 = OpAccessChain %_ptr_Function_float %536 %2090 + %2094 = OpLoad %float %2093 + %2095 = OpIAdd %int %2090 %int_1 + %2096 = OpAccessChain %_ptr_Function_float %536 %2095 + %2097 = OpLoad %float %2096 + %2098 = OpIAdd %int %2090 %int_2 + %2099 = OpAccessChain %_ptr_Function_float %536 %2098 + %2100 = OpLoad %float %2099 + %2101 = OpCompositeConstruct %v3float %2094 %2097 %2100 + %2102 = OpFMul %float %2092 %2092 + %2103 = OpCompositeConstruct %v3float %2102 %2092 %float_1 + %2104 = OpMatrixTimesVector %v3float %466 %2101 + %2105 = OpDot %float %2103 %2104 + OpBranch %2047 + %2047 = OpLabel + %2106 = OpPhi %float %2085 %2055 %2105 %2048 + OpBranch %2039 + %2040 = OpLabel + %2107 = OpExtInst %float %1 Log %float_0_00499999989 + %2108 = OpFDiv %float %2107 %1091 + OpBranch %2039 + %2039 = OpLabel + %2109 = OpPhi %float %2106 %2047 %2108 %2040 + %2110 = OpExtInst %float %1 Pow %float_10 %2109 + %2111 = OpCompositeInsert %v3float %2110 %2032 1 + %2112 = OpCompositeExtract %float %1748 2 + OpStore %534 %506 + OpStore %533 %507 + %2113 = OpFOrdLessThanEqual %bool %2112 %float_0 + %2114 = OpSelect %float %2113 %float_9_99999975en05 %2112 + %2115 = OpExtInst %float %1 Log %2114 + %2116 = OpFDiv %float %2115 %1091 + %2117 = OpFOrdLessThanEqual %bool %2116 %1958 + OpSelectionMerge %2118 None + OpBranchConditional %2117 %2119 %2120 + %2120 = OpLabel + %2121 = OpFOrdGreaterThan %bool %2116 %1958 + %2122 = OpExtInst %float %1 Log %1879 + %2123 = OpFDiv %float %2122 %1091 + %2124 = OpFOrdLessThan %bool %2116 %2123 + %2125 = OpLogicalAnd %bool %2121 %2124 + OpSelectionMerge %2126 None + OpBranchConditional %2125 %2127 %2128 + %2128 = OpLabel + %2129 = OpFOrdGreaterThanEqual %bool %2116 %2123 + %2130 = OpExtInst %float %1 Log %1951 + %2131 = OpFDiv %float %2130 %1091 + %2132 = OpFOrdLessThan %bool %2116 %2131 + %2133 = OpLogicalAnd %bool %2129 %2132 + OpSelectionMerge %2134 None + OpBranchConditional %2133 %2135 %2136 + %2136 = OpLabel + %2137 = OpFMul %float %2116 %float_0_119999997 + %2138 = OpExtInst %float %1 Log %float_2000 + %2139 = OpFDiv %float %2138 %1091 + %2140 = OpFMul %float %float_0_119999997 %2130 + %2141 = OpFDiv %float %2140 %1091 + %2142 = OpFSub %float %2139 %2141 + %2143 = OpFAdd %float %2137 %2142 + OpBranch %2134 + %2135 = OpLabel + %2144 = OpFSub %float %2116 %2123 + %2145 = OpFMul %float %float_7 %2144 + %2146 = OpFSub %float %2131 %2123 + %2147 = OpFDiv %float %2145 %2146 + %2148 = OpConvertFToS %int %2147 + %2149 = OpConvertSToF %float %2148 + %2150 = OpFSub %float %2147 %2149 + %2151 = OpAccessChain %_ptr_Function_float %533 %2148 + %2152 = OpLoad %float %2151 + %2153 = OpIAdd %int %2148 %int_1 + %2154 = OpAccessChain %_ptr_Function_float %533 %2153 + %2155 = OpLoad %float %2154 + %2156 = OpIAdd %int %2148 %int_2 + %2157 = OpAccessChain %_ptr_Function_float %533 %2156 + %2158 = OpLoad %float %2157 + %2159 = OpCompositeConstruct %v3float %2152 %2155 %2158 + %2160 = OpFMul %float %2150 %2150 + %2161 = OpCompositeConstruct %v3float %2160 %2150 %float_1 + %2162 = OpMatrixTimesVector %v3float %466 %2159 + %2163 = OpDot %float %2161 %2162 + OpBranch %2134 + %2134 = OpLabel + %2164 = OpPhi %float %2143 %2136 %2163 %2135 + OpBranch %2126 + %2127 = OpLabel + %2165 = OpFSub %float %2116 %1958 + %2166 = OpFMul %float %float_7 %2165 + %2167 = OpFSub %float %2123 %1958 + %2168 = OpFDiv %float %2166 %2167 + %2169 = OpConvertFToS %int %2168 + %2170 = OpConvertSToF %float %2169 + %2171 = OpFSub %float %2168 %2170 + %2172 = OpAccessChain %_ptr_Function_float %534 %2169 + %2173 = OpLoad %float %2172 + %2174 = OpIAdd %int %2169 %int_1 + %2175 = OpAccessChain %_ptr_Function_float %534 %2174 + %2176 = OpLoad %float %2175 + %2177 = OpIAdd %int %2169 %int_2 + %2178 = OpAccessChain %_ptr_Function_float %534 %2177 + %2179 = OpLoad %float %2178 + %2180 = OpCompositeConstruct %v3float %2173 %2176 %2179 + %2181 = OpFMul %float %2171 %2171 + %2182 = OpCompositeConstruct %v3float %2181 %2171 %float_1 + %2183 = OpMatrixTimesVector %v3float %466 %2180 + %2184 = OpDot %float %2182 %2183 + OpBranch %2126 + %2126 = OpLabel + %2185 = OpPhi %float %2164 %2134 %2184 %2127 + OpBranch %2118 + %2119 = OpLabel + %2186 = OpExtInst %float %1 Log %float_0_00499999989 + %2187 = OpFDiv %float %2186 %1091 + OpBranch %2118 + %2118 = OpLabel + %2188 = OpPhi %float %2185 %2126 %2187 %2119 + %2189 = OpExtInst %float %1 Pow %float_10 %2188 + %2190 = OpCompositeInsert %v3float %2189 %2111 2 + %2191 = OpVectorTimesMatrix %v3float %2190 %602 + %2192 = OpFMul %v3float %2191 %519 + %2193 = OpExtInst %v3float %1 Pow %2192 %286 + %2194 = OpFMul %v3float %196 %2193 + %2195 = OpFAdd %v3float %195 %2194 + %2196 = OpFMul %v3float %197 %2193 + %2197 = OpFAdd %v3float %141 %2196 + %2198 = OpFDiv %v3float %141 %2197 + %2199 = OpFMul %v3float %2195 %2198 + %2200 = OpExtInst %v3float %1 Pow %2199 %287 + OpBranch %1342 + %1342 = OpLabel + %2201 = OpPhi %v3float %1366 %1346 %2200 %2118 + OpBranch %1336 + %1337 = OpLabel + %2202 = OpMatrixTimesMatrix %mat3v3float %572 %423 + %2203 = OpFMul %v3float %932 %285 + %2204 = OpVectorTimesMatrix %v3float %2203 %2202 + %2205 = OpCompositeExtract %float %2204 0 + %2206 = OpCompositeExtract %float %2204 1 + %2207 = OpExtInst %float %1 FMin %2205 %2206 + %2208 = OpCompositeExtract %float %2204 2 + %2209 = OpExtInst %float %1 FMin %2207 %2208 + %2210 = OpExtInst %float %1 FMax %2205 %2206 + %2211 = OpExtInst %float %1 FMax %2210 %2208 + %2212 = OpExtInst %float %1 FMax %2211 %float_1_00000001en10 + %2213 = OpExtInst %float %1 FMax %2209 %float_1_00000001en10 + %2214 = OpFSub %float %2212 %2213 + %2215 = OpExtInst %float %1 FMax %2211 %float_0_00999999978 + %2216 = OpFDiv %float %2214 %2215 + %2217 = OpFSub %float %2208 %2206 + %2218 = OpFMul %float %2208 %2217 + %2219 = OpFSub %float %2206 %2205 + %2220 = OpFMul %float %2206 %2219 + %2221 = OpFAdd %float %2218 %2220 + %2222 = OpFSub %float %2205 %2208 + %2223 = OpFMul %float %2205 %2222 + %2224 = OpFAdd %float %2221 %2223 + %2225 = OpExtInst %float %1 Sqrt %2224 + %2226 = OpFAdd %float %2208 %2206 + %2227 = OpFAdd %float %2226 %2205 + %2228 = OpFMul %float %float_1_75 %2225 + %2229 = OpFAdd %float %2227 %2228 + %2230 = OpFMul %float %2229 %float_0_333333343 + %2231 = OpFSub %float %2216 %float_0_400000006 + %2232 = OpFMul %float %2231 %float_5 + %2233 = OpFMul %float %2231 %float_2_5 + %2234 = OpExtInst %float %1 FAbs %2233 + %2235 = OpFSub %float %float_1 %2234 + %2236 = OpExtInst %float %1 FMax %2235 %float_0 + %2237 = OpExtInst %float %1 FSign %2232 + %2238 = OpConvertFToS %int %2237 + %2239 = OpConvertSToF %float %2238 + %2240 = OpFMul %float %2236 %2236 + %2241 = OpFSub %float %float_1 %2240 + %2242 = OpFMul %float %2239 %2241 + %2243 = OpFAdd %float %float_1 %2242 + %2244 = OpFMul %float %2243 %float_0_0250000004 + %2245 = OpFOrdLessThanEqual %bool %2230 %float_0_0533333346 + OpSelectionMerge %2246 None + OpBranchConditional %2245 %2247 %2248 + %2248 = OpLabel + %2249 = OpFOrdGreaterThanEqual %bool %2230 %float_0_159999996 + OpSelectionMerge %2250 None + OpBranchConditional %2249 %2251 %2252 + %2252 = OpLabel + %2253 = OpFDiv %float %float_0_239999995 %2229 + %2254 = OpFSub %float %2253 %float_0_5 + %2255 = OpFMul %float %2244 %2254 + OpBranch %2250 + %2251 = OpLabel + OpBranch %2250 + %2250 = OpLabel + %2256 = OpPhi %float %2255 %2252 %float_0 %2251 + OpBranch %2246 + %2247 = OpLabel + OpBranch %2246 + %2246 = OpLabel + %2257 = OpPhi %float %2256 %2250 %2244 %2247 + %2258 = OpFAdd %float %float_1 %2257 + %2259 = OpCompositeConstruct %v3float %2258 %2258 %2258 + %2260 = OpFMul %v3float %2204 %2259 + %2261 = OpCompositeExtract %float %2260 0 + %2262 = OpCompositeExtract %float %2260 1 + %2263 = OpFOrdEqual %bool %2261 %2262 + %2264 = OpCompositeExtract %float %2260 2 + %2265 = OpFOrdEqual %bool %2262 %2264 + %2266 = OpLogicalAnd %bool %2263 %2265 + OpSelectionMerge %2267 None + OpBranchConditional %2266 %2268 %2269 + %2269 = OpLabel + %2270 = OpExtInst %float %1 Sqrt %float_3 + %2271 = OpFSub %float %2262 %2264 + %2272 = OpFMul %float %2270 %2271 + %2273 = OpFMul %float %float_2 %2261 + %2274 = OpFSub %float %2273 %2262 + %2275 = OpFSub %float %2274 %2264 + %2276 = OpExtInst %float %1 Atan2 %2272 %2275 + %2277 = OpFMul %float %float_57_2957764 %2276 + OpBranch %2267 + %2268 = OpLabel + OpBranch %2267 + %2267 = OpLabel + %2278 = OpPhi %float %2277 %2269 %float_0 %2268 + %2279 = OpFOrdLessThan %bool %2278 %float_0 + OpSelectionMerge %2280 None + OpBranchConditional %2279 %2281 %2280 + %2281 = OpLabel + %2282 = OpFAdd %float %2278 %float_360 + OpBranch %2280 + %2280 = OpLabel + %2283 = OpPhi %float %2278 %2267 %2282 %2281 + %2284 = OpExtInst %float %1 FClamp %2283 %float_0 %float_360 + %2285 = OpFOrdGreaterThan %bool %2284 %float_180 + OpSelectionMerge %2286 None + OpBranchConditional %2285 %2287 %2286 + %2287 = OpLabel + %2288 = OpFSub %float %2284 %float_360 + OpBranch %2286 + %2286 = OpLabel + %2289 = OpPhi %float %2284 %2280 %2288 %2287 + %2290 = OpFOrdGreaterThan %bool %2289 %float_n67_5 + %2291 = OpFOrdLessThan %bool %2289 %float_67_5 + %2292 = OpLogicalAnd %bool %2290 %2291 + OpSelectionMerge %2293 None + OpBranchConditional %2292 %2294 %2293 + %2294 = OpLabel + %2295 = OpFSub %float %2289 %float_n67_5 + %2296 = OpFMul %float %2295 %float_0_0296296291 + %2297 = OpConvertFToS %int %2296 + %2298 = OpConvertSToF %float %2297 + %2299 = OpFSub %float %2296 %2298 + %2300 = OpFMul %float %2299 %2299 + %2301 = OpFMul %float %2300 %2299 + %2302 = OpIEqual %bool %2297 %int_3 + OpSelectionMerge %2303 None + OpBranchConditional %2302 %2304 %2305 + %2305 = OpLabel + %2306 = OpIEqual %bool %2297 %int_2 + OpSelectionMerge %2307 None + OpBranchConditional %2306 %2308 %2309 + %2309 = OpLabel + %2310 = OpIEqual %bool %2297 %int_1 + OpSelectionMerge %2311 None + OpBranchConditional %2310 %2312 %2313 + %2313 = OpLabel + %2314 = OpIEqual %bool %2297 %int_0 + OpSelectionMerge %2315 None + OpBranchConditional %2314 %2316 %2317 + %2317 = OpLabel + OpBranch %2315 + %2316 = OpLabel + %2318 = OpFMul %float %2301 %float_0_166666672 + OpBranch %2315 + %2315 = OpLabel + %2319 = OpPhi %float %float_0 %2317 %2318 %2316 + OpBranch %2311 + %2312 = OpLabel + %2320 = OpFMul %float %2301 %float_n0_5 + %2321 = OpFMul %float %2300 %float_0_5 + %2322 = OpFAdd %float %2320 %2321 + %2323 = OpFMul %float %2299 %float_0_5 + %2324 = OpFAdd %float %2322 %2323 + %2325 = OpFAdd %float %2324 %float_0_166666672 + OpBranch %2311 + %2311 = OpLabel + %2326 = OpPhi %float %2319 %2315 %2325 %2312 + OpBranch %2307 + %2308 = OpLabel + %2327 = OpFMul %float %2301 %float_0_5 + %2328 = OpFMul %float %2300 %float_n1 + %2329 = OpFAdd %float %2327 %2328 + %2330 = OpFAdd %float %2329 %float_0_666666687 + OpBranch %2307 + %2307 = OpLabel + %2331 = OpPhi %float %2326 %2311 %2330 %2308 + OpBranch %2303 + %2304 = OpLabel + %2332 = OpFMul %float %2301 %float_n0_166666672 + %2333 = OpFMul %float %2300 %float_0_5 + %2334 = OpFAdd %float %2332 %2333 + %2335 = OpFMul %float %2299 %float_n0_5 + %2336 = OpFAdd %float %2334 %2335 + %2337 = OpFAdd %float %2336 %float_0_166666672 + OpBranch %2303 + %2303 = OpLabel + %2338 = OpPhi %float %2331 %2307 %2337 %2304 + OpBranch %2293 + %2293 = OpLabel + %2339 = OpPhi %float %float_0 %2286 %2338 %2303 + %2340 = OpFMul %float %2339 %float_1_5 + %2341 = OpFMul %float %2340 %2216 + %2342 = OpFSub %float %float_0_0299999993 %2261 + %2343 = OpFMul %float %2341 %2342 + %2344 = OpFMul %float %2343 %float_0_180000007 + %2345 = OpFAdd %float %2261 %2344 + %2346 = OpCompositeInsert %v3float %2345 %2260 0 + %2347 = OpExtInst %v3float %1 FClamp %2346 %138 %337 + %2348 = OpVectorTimesMatrix %v3float %2347 %434 + %2349 = OpExtInst %v3float %1 FClamp %2348 %138 %337 + %2350 = OpDot %float %2349 %73 + %2351 = OpCompositeConstruct %v3float %2350 %2350 %2350 + %2352 = OpExtInst %v3float %1 FMix %2351 %2349 %241 + %2353 = OpCompositeExtract %float %2352 0 + %2354 = OpExtInst %float %1 Exp2 %float_n15 + %2355 = OpFMul %float %float_0_179999992 %2354 + %2356 = OpExtInst %float %1 Exp2 %float_18 + %2357 = OpFMul %float %float_0_179999992 %2356 + OpStore %546 %499 + OpStore %545 %500 + %2358 = OpFOrdLessThanEqual %bool %2353 %float_0 + %2359 = OpExtInst %float %1 Exp2 %float_n14 + %2360 = OpSelect %float %2358 %2359 %2353 + %2361 = OpExtInst %float %1 Log %2360 + %2362 = OpFDiv %float %2361 %1091 + %2363 = OpExtInst %float %1 Log %2355 + %2364 = OpFDiv %float %2363 %1091 + %2365 = OpFOrdLessThanEqual %bool %2362 %2364 + OpSelectionMerge %2366 None + OpBranchConditional %2365 %2367 %2368 + %2368 = OpLabel + %2369 = OpFOrdGreaterThan %bool %2362 %2364 + %2370 = OpExtInst %float %1 Log %float_0_180000007 + %2371 = OpFDiv %float %2370 %1091 + %2372 = OpFOrdLessThan %bool %2362 %2371 + %2373 = OpLogicalAnd %bool %2369 %2372 + OpSelectionMerge %2374 None + OpBranchConditional %2373 %2375 %2376 + %2376 = OpLabel + %2377 = OpFOrdGreaterThanEqual %bool %2362 %2371 + %2378 = OpExtInst %float %1 Log %2357 + %2379 = OpFDiv %float %2378 %1091 + %2380 = OpFOrdLessThan %bool %2362 %2379 + %2381 = OpLogicalAnd %bool %2377 %2380 + OpSelectionMerge %2382 None + OpBranchConditional %2381 %2383 %2384 + %2384 = OpLabel + %2385 = OpExtInst %float %1 Log %float_10000 + %2386 = OpFDiv %float %2385 %1091 + OpBranch %2382 + %2383 = OpLabel + %2387 = OpFSub %float %2362 %2371 + %2388 = OpFMul %float %float_3 %2387 + %2389 = OpFSub %float %2379 %2371 + %2390 = OpFDiv %float %2388 %2389 + %2391 = OpConvertFToS %int %2390 + %2392 = OpConvertSToF %float %2391 + %2393 = OpFSub %float %2390 %2392 + %2394 = OpAccessChain %_ptr_Function_float %545 %2391 + %2395 = OpLoad %float %2394 + %2396 = OpIAdd %int %2391 %int_1 + %2397 = OpAccessChain %_ptr_Function_float %545 %2396 + %2398 = OpLoad %float %2397 + %2399 = OpIAdd %int %2391 %int_2 + %2400 = OpAccessChain %_ptr_Function_float %545 %2399 + %2401 = OpLoad %float %2400 + %2402 = OpCompositeConstruct %v3float %2395 %2398 %2401 + %2403 = OpFMul %float %2393 %2393 + %2404 = OpCompositeConstruct %v3float %2403 %2393 %float_1 + %2405 = OpMatrixTimesVector %v3float %466 %2402 + %2406 = OpDot %float %2404 %2405 + OpBranch %2382 + %2382 = OpLabel + %2407 = OpPhi %float %2386 %2384 %2406 %2383 + OpBranch %2374 + %2375 = OpLabel + %2408 = OpFSub %float %2362 %2364 + %2409 = OpFMul %float %float_3 %2408 + %2410 = OpFSub %float %2371 %2364 + %2411 = OpFDiv %float %2409 %2410 + %2412 = OpConvertFToS %int %2411 + %2413 = OpConvertSToF %float %2412 + %2414 = OpFSub %float %2411 %2413 + %2415 = OpAccessChain %_ptr_Function_float %546 %2412 + %2416 = OpLoad %float %2415 + %2417 = OpIAdd %int %2412 %int_1 + %2418 = OpAccessChain %_ptr_Function_float %546 %2417 + %2419 = OpLoad %float %2418 + %2420 = OpIAdd %int %2412 %int_2 + %2421 = OpAccessChain %_ptr_Function_float %546 %2420 + %2422 = OpLoad %float %2421 + %2423 = OpCompositeConstruct %v3float %2416 %2419 %2422 + %2424 = OpFMul %float %2414 %2414 + %2425 = OpCompositeConstruct %v3float %2424 %2414 %float_1 + %2426 = OpMatrixTimesVector %v3float %466 %2423 + %2427 = OpDot %float %2425 %2426 + OpBranch %2374 + %2374 = OpLabel + %2428 = OpPhi %float %2407 %2382 %2427 %2375 + OpBranch %2366 + %2367 = OpLabel + %2429 = OpExtInst %float %1 Log %float_9_99999975en05 + %2430 = OpFDiv %float %2429 %1091 + OpBranch %2366 + %2366 = OpLabel + %2431 = OpPhi %float %2428 %2374 %2430 %2367 + %2432 = OpExtInst %float %1 Pow %float_10 %2431 + %2433 = OpCompositeInsert %v3float %2432 %523 0 + %2434 = OpCompositeExtract %float %2352 1 + OpStore %548 %499 + OpStore %547 %500 + %2435 = OpFOrdLessThanEqual %bool %2434 %float_0 + %2436 = OpSelect %float %2435 %2359 %2434 + %2437 = OpExtInst %float %1 Log %2436 + %2438 = OpFDiv %float %2437 %1091 + %2439 = OpFOrdLessThanEqual %bool %2438 %2364 + OpSelectionMerge %2440 None + OpBranchConditional %2439 %2441 %2442 + %2442 = OpLabel + %2443 = OpFOrdGreaterThan %bool %2438 %2364 + %2444 = OpExtInst %float %1 Log %float_0_180000007 + %2445 = OpFDiv %float %2444 %1091 + %2446 = OpFOrdLessThan %bool %2438 %2445 + %2447 = OpLogicalAnd %bool %2443 %2446 + OpSelectionMerge %2448 None + OpBranchConditional %2447 %2449 %2450 + %2450 = OpLabel + %2451 = OpFOrdGreaterThanEqual %bool %2438 %2445 + %2452 = OpExtInst %float %1 Log %2357 + %2453 = OpFDiv %float %2452 %1091 + %2454 = OpFOrdLessThan %bool %2438 %2453 + %2455 = OpLogicalAnd %bool %2451 %2454 + OpSelectionMerge %2456 None + OpBranchConditional %2455 %2457 %2458 + %2458 = OpLabel + %2459 = OpExtInst %float %1 Log %float_10000 + %2460 = OpFDiv %float %2459 %1091 + OpBranch %2456 + %2457 = OpLabel + %2461 = OpFSub %float %2438 %2445 + %2462 = OpFMul %float %float_3 %2461 + %2463 = OpFSub %float %2453 %2445 + %2464 = OpFDiv %float %2462 %2463 + %2465 = OpConvertFToS %int %2464 + %2466 = OpConvertSToF %float %2465 + %2467 = OpFSub %float %2464 %2466 + %2468 = OpAccessChain %_ptr_Function_float %547 %2465 + %2469 = OpLoad %float %2468 + %2470 = OpIAdd %int %2465 %int_1 + %2471 = OpAccessChain %_ptr_Function_float %547 %2470 + %2472 = OpLoad %float %2471 + %2473 = OpIAdd %int %2465 %int_2 + %2474 = OpAccessChain %_ptr_Function_float %547 %2473 + %2475 = OpLoad %float %2474 + %2476 = OpCompositeConstruct %v3float %2469 %2472 %2475 + %2477 = OpFMul %float %2467 %2467 + %2478 = OpCompositeConstruct %v3float %2477 %2467 %float_1 + %2479 = OpMatrixTimesVector %v3float %466 %2476 + %2480 = OpDot %float %2478 %2479 + OpBranch %2456 + %2456 = OpLabel + %2481 = OpPhi %float %2460 %2458 %2480 %2457 + OpBranch %2448 + %2449 = OpLabel + %2482 = OpFSub %float %2438 %2364 + %2483 = OpFMul %float %float_3 %2482 + %2484 = OpFSub %float %2445 %2364 + %2485 = OpFDiv %float %2483 %2484 + %2486 = OpConvertFToS %int %2485 + %2487 = OpConvertSToF %float %2486 + %2488 = OpFSub %float %2485 %2487 + %2489 = OpAccessChain %_ptr_Function_float %548 %2486 + %2490 = OpLoad %float %2489 + %2491 = OpIAdd %int %2486 %int_1 + %2492 = OpAccessChain %_ptr_Function_float %548 %2491 + %2493 = OpLoad %float %2492 + %2494 = OpIAdd %int %2486 %int_2 + %2495 = OpAccessChain %_ptr_Function_float %548 %2494 + %2496 = OpLoad %float %2495 + %2497 = OpCompositeConstruct %v3float %2490 %2493 %2496 + %2498 = OpFMul %float %2488 %2488 + %2499 = OpCompositeConstruct %v3float %2498 %2488 %float_1 + %2500 = OpMatrixTimesVector %v3float %466 %2497 + %2501 = OpDot %float %2499 %2500 + OpBranch %2448 + %2448 = OpLabel + %2502 = OpPhi %float %2481 %2456 %2501 %2449 + OpBranch %2440 + %2441 = OpLabel + %2503 = OpExtInst %float %1 Log %float_9_99999975en05 + %2504 = OpFDiv %float %2503 %1091 + OpBranch %2440 + %2440 = OpLabel + %2505 = OpPhi %float %2502 %2448 %2504 %2441 + %2506 = OpExtInst %float %1 Pow %float_10 %2505 + %2507 = OpCompositeInsert %v3float %2506 %2433 1 + %2508 = OpCompositeExtract %float %2352 2 + OpStore %550 %499 + OpStore %549 %500 + %2509 = OpFOrdLessThanEqual %bool %2508 %float_0 + %2510 = OpSelect %float %2509 %2359 %2508 + %2511 = OpExtInst %float %1 Log %2510 + %2512 = OpFDiv %float %2511 %1091 + %2513 = OpFOrdLessThanEqual %bool %2512 %2364 + OpSelectionMerge %2514 None + OpBranchConditional %2513 %2515 %2516 + %2516 = OpLabel + %2517 = OpFOrdGreaterThan %bool %2512 %2364 + %2518 = OpExtInst %float %1 Log %float_0_180000007 + %2519 = OpFDiv %float %2518 %1091 + %2520 = OpFOrdLessThan %bool %2512 %2519 + %2521 = OpLogicalAnd %bool %2517 %2520 + OpSelectionMerge %2522 None + OpBranchConditional %2521 %2523 %2524 + %2524 = OpLabel + %2525 = OpFOrdGreaterThanEqual %bool %2512 %2519 + %2526 = OpExtInst %float %1 Log %2357 + %2527 = OpFDiv %float %2526 %1091 + %2528 = OpFOrdLessThan %bool %2512 %2527 + %2529 = OpLogicalAnd %bool %2525 %2528 + OpSelectionMerge %2530 None + OpBranchConditional %2529 %2531 %2532 + %2532 = OpLabel + %2533 = OpExtInst %float %1 Log %float_10000 + %2534 = OpFDiv %float %2533 %1091 + OpBranch %2530 + %2531 = OpLabel + %2535 = OpFSub %float %2512 %2519 + %2536 = OpFMul %float %float_3 %2535 + %2537 = OpFSub %float %2527 %2519 + %2538 = OpFDiv %float %2536 %2537 + %2539 = OpConvertFToS %int %2538 + %2540 = OpConvertSToF %float %2539 + %2541 = OpFSub %float %2538 %2540 + %2542 = OpAccessChain %_ptr_Function_float %549 %2539 + %2543 = OpLoad %float %2542 + %2544 = OpIAdd %int %2539 %int_1 + %2545 = OpAccessChain %_ptr_Function_float %549 %2544 + %2546 = OpLoad %float %2545 + %2547 = OpIAdd %int %2539 %int_2 + %2548 = OpAccessChain %_ptr_Function_float %549 %2547 + %2549 = OpLoad %float %2548 + %2550 = OpCompositeConstruct %v3float %2543 %2546 %2549 + %2551 = OpFMul %float %2541 %2541 + %2552 = OpCompositeConstruct %v3float %2551 %2541 %float_1 + %2553 = OpMatrixTimesVector %v3float %466 %2550 + %2554 = OpDot %float %2552 %2553 + OpBranch %2530 + %2530 = OpLabel + %2555 = OpPhi %float %2534 %2532 %2554 %2531 + OpBranch %2522 + %2523 = OpLabel + %2556 = OpFSub %float %2512 %2364 + %2557 = OpFMul %float %float_3 %2556 + %2558 = OpFSub %float %2519 %2364 + %2559 = OpFDiv %float %2557 %2558 + %2560 = OpConvertFToS %int %2559 + %2561 = OpConvertSToF %float %2560 + %2562 = OpFSub %float %2559 %2561 + %2563 = OpAccessChain %_ptr_Function_float %550 %2560 + %2564 = OpLoad %float %2563 + %2565 = OpIAdd %int %2560 %int_1 + %2566 = OpAccessChain %_ptr_Function_float %550 %2565 + %2567 = OpLoad %float %2566 + %2568 = OpIAdd %int %2560 %int_2 + %2569 = OpAccessChain %_ptr_Function_float %550 %2568 + %2570 = OpLoad %float %2569 + %2571 = OpCompositeConstruct %v3float %2564 %2567 %2570 + %2572 = OpFMul %float %2562 %2562 + %2573 = OpCompositeConstruct %v3float %2572 %2562 %float_1 + %2574 = OpMatrixTimesVector %v3float %466 %2571 + %2575 = OpDot %float %2573 %2574 + OpBranch %2522 + %2522 = OpLabel + %2576 = OpPhi %float %2555 %2530 %2575 %2523 + OpBranch %2514 + %2515 = OpLabel + %2577 = OpExtInst %float %1 Log %float_9_99999975en05 + %2578 = OpFDiv %float %2577 %1091 + OpBranch %2514 + %2514 = OpLabel + %2579 = OpPhi %float %2576 %2522 %2578 %2515 + %2580 = OpExtInst %float %1 Pow %float_10 %2579 + %2581 = OpCompositeInsert %v3float %2580 %2507 2 + %2582 = OpVectorTimesMatrix %v3float %2581 %438 + %2583 = OpVectorTimesMatrix %v3float %2582 %434 + %2584 = OpExtInst %float %1 Pow %float_2 %float_n12 + %2585 = OpFMul %float %float_0_179999992 %2584 + OpStore %558 %499 + OpStore %557 %500 + %2586 = OpFOrdLessThanEqual %bool %2585 %float_0 + %2587 = OpSelect %float %2586 %2359 %2585 + %2588 = OpExtInst %float %1 Log %2587 + %2589 = OpFDiv %float %2588 %1091 + %2590 = OpFOrdLessThanEqual %bool %2589 %2364 + OpSelectionMerge %2591 None + OpBranchConditional %2590 %2592 %2593 + %2593 = OpLabel + %2594 = OpFOrdGreaterThan %bool %2589 %2364 + %2595 = OpExtInst %float %1 Log %float_0_180000007 + %2596 = OpFDiv %float %2595 %1091 + %2597 = OpFOrdLessThan %bool %2589 %2596 + %2598 = OpLogicalAnd %bool %2594 %2597 + OpSelectionMerge %2599 None + OpBranchConditional %2598 %2600 %2601 + %2601 = OpLabel + %2602 = OpFOrdGreaterThanEqual %bool %2589 %2596 + %2603 = OpExtInst %float %1 Log %2357 + %2604 = OpFDiv %float %2603 %1091 + %2605 = OpFOrdLessThan %bool %2589 %2604 + %2606 = OpLogicalAnd %bool %2602 %2605 + OpSelectionMerge %2607 None + OpBranchConditional %2606 %2608 %2609 + %2609 = OpLabel + %2610 = OpExtInst %float %1 Log %float_10000 + %2611 = OpFDiv %float %2610 %1091 + OpBranch %2607 + %2608 = OpLabel + %2612 = OpFSub %float %2589 %2596 + %2613 = OpFMul %float %float_3 %2612 + %2614 = OpFSub %float %2604 %2596 + %2615 = OpFDiv %float %2613 %2614 + %2616 = OpConvertFToS %int %2615 + %2617 = OpConvertSToF %float %2616 + %2618 = OpFSub %float %2615 %2617 + %2619 = OpAccessChain %_ptr_Function_float %557 %2616 + %2620 = OpLoad %float %2619 + %2621 = OpIAdd %int %2616 %int_1 + %2622 = OpAccessChain %_ptr_Function_float %557 %2621 + %2623 = OpLoad %float %2622 + %2624 = OpIAdd %int %2616 %int_2 + %2625 = OpAccessChain %_ptr_Function_float %557 %2624 + %2626 = OpLoad %float %2625 + %2627 = OpCompositeConstruct %v3float %2620 %2623 %2626 + %2628 = OpFMul %float %2618 %2618 + %2629 = OpCompositeConstruct %v3float %2628 %2618 %float_1 + %2630 = OpMatrixTimesVector %v3float %466 %2627 + %2631 = OpDot %float %2629 %2630 + OpBranch %2607 + %2607 = OpLabel + %2632 = OpPhi %float %2611 %2609 %2631 %2608 + OpBranch %2599 + %2600 = OpLabel + %2633 = OpFSub %float %2589 %2364 + %2634 = OpFMul %float %float_3 %2633 + %2635 = OpFSub %float %2596 %2364 + %2636 = OpFDiv %float %2634 %2635 + %2637 = OpConvertFToS %int %2636 + %2638 = OpConvertSToF %float %2637 + %2639 = OpFSub %float %2636 %2638 + %2640 = OpAccessChain %_ptr_Function_float %558 %2637 + %2641 = OpLoad %float %2640 + %2642 = OpIAdd %int %2637 %int_1 + %2643 = OpAccessChain %_ptr_Function_float %558 %2642 + %2644 = OpLoad %float %2643 + %2645 = OpIAdd %int %2637 %int_2 + %2646 = OpAccessChain %_ptr_Function_float %558 %2645 + %2647 = OpLoad %float %2646 + %2648 = OpCompositeConstruct %v3float %2641 %2644 %2647 + %2649 = OpFMul %float %2639 %2639 + %2650 = OpCompositeConstruct %v3float %2649 %2639 %float_1 + %2651 = OpMatrixTimesVector %v3float %466 %2648 + %2652 = OpDot %float %2650 %2651 + OpBranch %2599 + %2599 = OpLabel + %2653 = OpPhi %float %2632 %2607 %2652 %2600 + OpBranch %2591 + %2592 = OpLabel + %2654 = OpExtInst %float %1 Log %float_9_99999975en05 + %2655 = OpFDiv %float %2654 %1091 + OpBranch %2591 + %2591 = OpLabel + %2656 = OpPhi %float %2653 %2599 %2655 %2592 + %2657 = OpExtInst %float %1 Pow %float_10 %2656 + OpStore %560 %499 + OpStore %559 %500 + %2658 = OpExtInst %float %1 Log %float_0_180000007 + %2659 = OpFDiv %float %2658 %1091 + %2660 = OpFOrdLessThanEqual %bool %2659 %2364 + OpSelectionMerge %2661 None + OpBranchConditional %2660 %2662 %2663 + %2663 = OpLabel + %2664 = OpFOrdGreaterThan %bool %2659 %2364 + %2665 = OpFOrdLessThan %bool %2659 %2659 + %2666 = OpLogicalAnd %bool %2664 %2665 + OpSelectionMerge %2667 None + OpBranchConditional %2666 %2668 %2669 + %2669 = OpLabel + %2670 = OpFOrdGreaterThanEqual %bool %2659 %2659 + %2671 = OpExtInst %float %1 Log %2357 + %2672 = OpFDiv %float %2671 %1091 + %2673 = OpFOrdLessThan %bool %2659 %2672 + %2674 = OpLogicalAnd %bool %2670 %2673 + OpSelectionMerge %2675 None + OpBranchConditional %2674 %2676 %2677 + %2677 = OpLabel + %2678 = OpExtInst %float %1 Log %float_10000 + %2679 = OpFDiv %float %2678 %1091 + OpBranch %2675 + %2676 = OpLabel + %2680 = OpFSub %float %2659 %2659 + %2681 = OpFMul %float %float_3 %2680 + %2682 = OpFSub %float %2672 %2659 + %2683 = OpFDiv %float %2681 %2682 + %2684 = OpConvertFToS %int %2683 + %2685 = OpConvertSToF %float %2684 + %2686 = OpFSub %float %2683 %2685 + %2687 = OpAccessChain %_ptr_Function_float %559 %2684 + %2688 = OpLoad %float %2687 + %2689 = OpIAdd %int %2684 %int_1 + %2690 = OpAccessChain %_ptr_Function_float %559 %2689 + %2691 = OpLoad %float %2690 + %2692 = OpIAdd %int %2684 %int_2 + %2693 = OpAccessChain %_ptr_Function_float %559 %2692 + %2694 = OpLoad %float %2693 + %2695 = OpCompositeConstruct %v3float %2688 %2691 %2694 + %2696 = OpFMul %float %2686 %2686 + %2697 = OpCompositeConstruct %v3float %2696 %2686 %float_1 + %2698 = OpMatrixTimesVector %v3float %466 %2695 + %2699 = OpDot %float %2697 %2698 + OpBranch %2675 + %2675 = OpLabel + %2700 = OpPhi %float %2679 %2677 %2699 %2676 + OpBranch %2667 + %2668 = OpLabel + %2701 = OpAccessChain %_ptr_Function_float %560 %int_3 + %2702 = OpLoad %float %2701 + %2703 = OpAccessChain %_ptr_Function_float %560 %int_4 + %2704 = OpLoad %float %2703 + %2705 = OpAccessChain %_ptr_Function_float %560 %int_5 + %2706 = OpLoad %float %2705 + %2707 = OpCompositeConstruct %v3float %2702 %2704 %2706 + %2708 = OpMatrixTimesVector %v3float %466 %2707 + %2709 = OpCompositeExtract %float %2708 2 + OpBranch %2667 + %2667 = OpLabel + %2710 = OpPhi %float %2700 %2675 %2709 %2668 + OpBranch %2661 + %2662 = OpLabel + %2711 = OpExtInst %float %1 Log %float_9_99999975en05 + %2712 = OpFDiv %float %2711 %1091 + OpBranch %2661 + %2661 = OpLabel + %2713 = OpPhi %float %2710 %2667 %2712 %2662 + %2714 = OpExtInst %float %1 Pow %float_10 %2713 + %2715 = OpExtInst %float %1 Pow %float_2 %float_10 + %2716 = OpFMul %float %float_0_179999992 %2715 + OpStore %562 %499 + OpStore %561 %500 + %2717 = OpFOrdLessThanEqual %bool %2716 %float_0 + %2718 = OpSelect %float %2717 %2359 %2716 + %2719 = OpExtInst %float %1 Log %2718 + %2720 = OpFDiv %float %2719 %1091 + %2721 = OpFOrdLessThanEqual %bool %2720 %2364 + OpSelectionMerge %2722 None + OpBranchConditional %2721 %2723 %2724 + %2724 = OpLabel + %2725 = OpFOrdGreaterThan %bool %2720 %2364 + %2726 = OpFOrdLessThan %bool %2720 %2659 + %2727 = OpLogicalAnd %bool %2725 %2726 + OpSelectionMerge %2728 None + OpBranchConditional %2727 %2729 %2730 + %2730 = OpLabel + %2731 = OpFOrdGreaterThanEqual %bool %2720 %2659 + %2732 = OpExtInst %float %1 Log %2357 + %2733 = OpFDiv %float %2732 %1091 + %2734 = OpFOrdLessThan %bool %2720 %2733 + %2735 = OpLogicalAnd %bool %2731 %2734 + OpSelectionMerge %2736 None + OpBranchConditional %2735 %2737 %2738 + %2738 = OpLabel + %2739 = OpExtInst %float %1 Log %float_10000 + %2740 = OpFDiv %float %2739 %1091 + OpBranch %2736 + %2737 = OpLabel + %2741 = OpFSub %float %2720 %2659 + %2742 = OpFMul %float %float_3 %2741 + %2743 = OpFSub %float %2733 %2659 + %2744 = OpFDiv %float %2742 %2743 + %2745 = OpConvertFToS %int %2744 + %2746 = OpConvertSToF %float %2745 + %2747 = OpFSub %float %2744 %2746 + %2748 = OpAccessChain %_ptr_Function_float %561 %2745 + %2749 = OpLoad %float %2748 + %2750 = OpIAdd %int %2745 %int_1 + %2751 = OpAccessChain %_ptr_Function_float %561 %2750 + %2752 = OpLoad %float %2751 + %2753 = OpIAdd %int %2745 %int_2 + %2754 = OpAccessChain %_ptr_Function_float %561 %2753 + %2755 = OpLoad %float %2754 + %2756 = OpCompositeConstruct %v3float %2749 %2752 %2755 + %2757 = OpFMul %float %2747 %2747 + %2758 = OpCompositeConstruct %v3float %2757 %2747 %float_1 + %2759 = OpMatrixTimesVector %v3float %466 %2756 + %2760 = OpDot %float %2758 %2759 + OpBranch %2736 + %2736 = OpLabel + %2761 = OpPhi %float %2740 %2738 %2760 %2737 + OpBranch %2728 + %2729 = OpLabel + %2762 = OpFSub %float %2720 %2364 + %2763 = OpFMul %float %float_3 %2762 + %2764 = OpFSub %float %2659 %2364 + %2765 = OpFDiv %float %2763 %2764 + %2766 = OpConvertFToS %int %2765 + %2767 = OpConvertSToF %float %2766 + %2768 = OpFSub %float %2765 %2767 + %2769 = OpAccessChain %_ptr_Function_float %562 %2766 + %2770 = OpLoad %float %2769 + %2771 = OpIAdd %int %2766 %int_1 + %2772 = OpAccessChain %_ptr_Function_float %562 %2771 + %2773 = OpLoad %float %2772 + %2774 = OpIAdd %int %2766 %int_2 + %2775 = OpAccessChain %_ptr_Function_float %562 %2774 + %2776 = OpLoad %float %2775 + %2777 = OpCompositeConstruct %v3float %2770 %2773 %2776 + %2778 = OpFMul %float %2768 %2768 + %2779 = OpCompositeConstruct %v3float %2778 %2768 %float_1 + %2780 = OpMatrixTimesVector %v3float %466 %2777 + %2781 = OpDot %float %2779 %2780 + OpBranch %2728 + %2728 = OpLabel + %2782 = OpPhi %float %2761 %2736 %2781 %2729 + OpBranch %2722 + %2723 = OpLabel + %2783 = OpExtInst %float %1 Log %float_9_99999975en05 + %2784 = OpFDiv %float %2783 %1091 + OpBranch %2722 + %2722 = OpLabel + %2785 = OpPhi %float %2782 %2728 %2784 %2723 + %2786 = OpExtInst %float %1 Pow %float_10 %2785 + %2787 = OpCompositeExtract %float %2583 0 + OpStore %556 %503 + OpStore %555 %504 + %2788 = OpFOrdLessThanEqual %bool %2787 %float_0 + %2789 = OpSelect %float %2788 %float_9_99999975en05 %2787 + %2790 = OpExtInst %float %1 Log %2789 + %2791 = OpFDiv %float %2790 %1091 + %2792 = OpExtInst %float %1 Log %2657 + %2793 = OpFDiv %float %2792 %1091 + %2794 = OpFOrdLessThanEqual %bool %2791 %2793 + OpSelectionMerge %2795 None + OpBranchConditional %2794 %2796 %2797 + %2797 = OpLabel + %2798 = OpFOrdGreaterThan %bool %2791 %2793 + %2799 = OpExtInst %float %1 Log %2714 + %2800 = OpFDiv %float %2799 %1091 + %2801 = OpFOrdLessThan %bool %2791 %2800 + %2802 = OpLogicalAnd %bool %2798 %2801 + OpSelectionMerge %2803 None + OpBranchConditional %2802 %2804 %2805 + %2805 = OpLabel + %2806 = OpFOrdGreaterThanEqual %bool %2791 %2800 + %2807 = OpExtInst %float %1 Log %2786 + %2808 = OpFDiv %float %2807 %1091 + %2809 = OpFOrdLessThan %bool %2791 %2808 + %2810 = OpLogicalAnd %bool %2806 %2809 + OpSelectionMerge %2811 None + OpBranchConditional %2810 %2812 %2813 + %2813 = OpLabel + %2814 = OpFMul %float %2791 %float_0_0599999987 + %2815 = OpExtInst %float %1 Log %float_1000 + %2816 = OpFDiv %float %2815 %1091 + %2817 = OpFMul %float %float_0_0599999987 %2807 + %2818 = OpFDiv %float %2817 %1091 + %2819 = OpFSub %float %2816 %2818 + %2820 = OpFAdd %float %2814 %2819 + OpBranch %2811 + %2812 = OpLabel + %2821 = OpFSub %float %2791 %2800 + %2822 = OpFMul %float %float_7 %2821 + %2823 = OpFSub %float %2808 %2800 + %2824 = OpFDiv %float %2822 %2823 + %2825 = OpConvertFToS %int %2824 + %2826 = OpConvertSToF %float %2825 + %2827 = OpFSub %float %2824 %2826 + %2828 = OpAccessChain %_ptr_Function_float %555 %2825 + %2829 = OpLoad %float %2828 + %2830 = OpIAdd %int %2825 %int_1 + %2831 = OpAccessChain %_ptr_Function_float %555 %2830 + %2832 = OpLoad %float %2831 + %2833 = OpIAdd %int %2825 %int_2 + %2834 = OpAccessChain %_ptr_Function_float %555 %2833 + %2835 = OpLoad %float %2834 + %2836 = OpCompositeConstruct %v3float %2829 %2832 %2835 + %2837 = OpFMul %float %2827 %2827 + %2838 = OpCompositeConstruct %v3float %2837 %2827 %float_1 + %2839 = OpMatrixTimesVector %v3float %466 %2836 + %2840 = OpDot %float %2838 %2839 + OpBranch %2811 + %2811 = OpLabel + %2841 = OpPhi %float %2820 %2813 %2840 %2812 + OpBranch %2803 + %2804 = OpLabel + %2842 = OpFSub %float %2791 %2793 + %2843 = OpFMul %float %float_7 %2842 + %2844 = OpFSub %float %2800 %2793 + %2845 = OpFDiv %float %2843 %2844 + %2846 = OpConvertFToS %int %2845 + %2847 = OpConvertSToF %float %2846 + %2848 = OpFSub %float %2845 %2847 + %2849 = OpAccessChain %_ptr_Function_float %556 %2846 + %2850 = OpLoad %float %2849 + %2851 = OpIAdd %int %2846 %int_1 + %2852 = OpAccessChain %_ptr_Function_float %556 %2851 + %2853 = OpLoad %float %2852 + %2854 = OpIAdd %int %2846 %int_2 + %2855 = OpAccessChain %_ptr_Function_float %556 %2854 + %2856 = OpLoad %float %2855 + %2857 = OpCompositeConstruct %v3float %2850 %2853 %2856 + %2858 = OpFMul %float %2848 %2848 + %2859 = OpCompositeConstruct %v3float %2858 %2848 %float_1 + %2860 = OpMatrixTimesVector %v3float %466 %2857 + %2861 = OpDot %float %2859 %2860 + OpBranch %2803 + %2803 = OpLabel + %2862 = OpPhi %float %2841 %2811 %2861 %2804 + OpBranch %2795 + %2796 = OpLabel + %2863 = OpFMul %float %2791 %float_3 + %2864 = OpExtInst %float %1 Log %float_9_99999975en05 + %2865 = OpFDiv %float %2864 %1091 + %2866 = OpFMul %float %float_3 %2792 + %2867 = OpFDiv %float %2866 %1091 + %2868 = OpFSub %float %2865 %2867 + %2869 = OpFAdd %float %2863 %2868 + OpBranch %2795 + %2795 = OpLabel + %2870 = OpPhi %float %2862 %2803 %2869 %2796 + %2871 = OpExtInst %float %1 Pow %float_10 %2870 + %2872 = OpCompositeInsert %v3float %2871 %523 0 + %2873 = OpCompositeExtract %float %2583 1 + OpStore %554 %503 + OpStore %553 %504 + %2874 = OpFOrdLessThanEqual %bool %2873 %float_0 + %2875 = OpSelect %float %2874 %float_9_99999975en05 %2873 + %2876 = OpExtInst %float %1 Log %2875 + %2877 = OpFDiv %float %2876 %1091 + %2878 = OpFOrdLessThanEqual %bool %2877 %2793 + OpSelectionMerge %2879 None + OpBranchConditional %2878 %2880 %2881 + %2881 = OpLabel + %2882 = OpFOrdGreaterThan %bool %2877 %2793 + %2883 = OpExtInst %float %1 Log %2714 + %2884 = OpFDiv %float %2883 %1091 + %2885 = OpFOrdLessThan %bool %2877 %2884 + %2886 = OpLogicalAnd %bool %2882 %2885 + OpSelectionMerge %2887 None + OpBranchConditional %2886 %2888 %2889 + %2889 = OpLabel + %2890 = OpFOrdGreaterThanEqual %bool %2877 %2884 + %2891 = OpExtInst %float %1 Log %2786 + %2892 = OpFDiv %float %2891 %1091 + %2893 = OpFOrdLessThan %bool %2877 %2892 + %2894 = OpLogicalAnd %bool %2890 %2893 + OpSelectionMerge %2895 None + OpBranchConditional %2894 %2896 %2897 + %2897 = OpLabel + %2898 = OpFMul %float %2877 %float_0_0599999987 + %2899 = OpExtInst %float %1 Log %float_1000 + %2900 = OpFDiv %float %2899 %1091 + %2901 = OpFMul %float %float_0_0599999987 %2891 + %2902 = OpFDiv %float %2901 %1091 + %2903 = OpFSub %float %2900 %2902 + %2904 = OpFAdd %float %2898 %2903 + OpBranch %2895 + %2896 = OpLabel + %2905 = OpFSub %float %2877 %2884 + %2906 = OpFMul %float %float_7 %2905 + %2907 = OpFSub %float %2892 %2884 + %2908 = OpFDiv %float %2906 %2907 + %2909 = OpConvertFToS %int %2908 + %2910 = OpConvertSToF %float %2909 + %2911 = OpFSub %float %2908 %2910 + %2912 = OpAccessChain %_ptr_Function_float %553 %2909 + %2913 = OpLoad %float %2912 + %2914 = OpIAdd %int %2909 %int_1 + %2915 = OpAccessChain %_ptr_Function_float %553 %2914 + %2916 = OpLoad %float %2915 + %2917 = OpIAdd %int %2909 %int_2 + %2918 = OpAccessChain %_ptr_Function_float %553 %2917 + %2919 = OpLoad %float %2918 + %2920 = OpCompositeConstruct %v3float %2913 %2916 %2919 + %2921 = OpFMul %float %2911 %2911 + %2922 = OpCompositeConstruct %v3float %2921 %2911 %float_1 + %2923 = OpMatrixTimesVector %v3float %466 %2920 + %2924 = OpDot %float %2922 %2923 + OpBranch %2895 + %2895 = OpLabel + %2925 = OpPhi %float %2904 %2897 %2924 %2896 + OpBranch %2887 + %2888 = OpLabel + %2926 = OpFSub %float %2877 %2793 + %2927 = OpFMul %float %float_7 %2926 + %2928 = OpFSub %float %2884 %2793 + %2929 = OpFDiv %float %2927 %2928 + %2930 = OpConvertFToS %int %2929 + %2931 = OpConvertSToF %float %2930 + %2932 = OpFSub %float %2929 %2931 + %2933 = OpAccessChain %_ptr_Function_float %554 %2930 + %2934 = OpLoad %float %2933 + %2935 = OpIAdd %int %2930 %int_1 + %2936 = OpAccessChain %_ptr_Function_float %554 %2935 + %2937 = OpLoad %float %2936 + %2938 = OpIAdd %int %2930 %int_2 + %2939 = OpAccessChain %_ptr_Function_float %554 %2938 + %2940 = OpLoad %float %2939 + %2941 = OpCompositeConstruct %v3float %2934 %2937 %2940 + %2942 = OpFMul %float %2932 %2932 + %2943 = OpCompositeConstruct %v3float %2942 %2932 %float_1 + %2944 = OpMatrixTimesVector %v3float %466 %2941 + %2945 = OpDot %float %2943 %2944 + OpBranch %2887 + %2887 = OpLabel + %2946 = OpPhi %float %2925 %2895 %2945 %2888 + OpBranch %2879 + %2880 = OpLabel + %2947 = OpFMul %float %2877 %float_3 + %2948 = OpExtInst %float %1 Log %float_9_99999975en05 + %2949 = OpFDiv %float %2948 %1091 + %2950 = OpFMul %float %float_3 %2792 + %2951 = OpFDiv %float %2950 %1091 + %2952 = OpFSub %float %2949 %2951 + %2953 = OpFAdd %float %2947 %2952 + OpBranch %2879 + %2879 = OpLabel + %2954 = OpPhi %float %2946 %2887 %2953 %2880 + %2955 = OpExtInst %float %1 Pow %float_10 %2954 + %2956 = OpCompositeInsert %v3float %2955 %2872 1 + %2957 = OpCompositeExtract %float %2583 2 + OpStore %552 %503 + OpStore %551 %504 + %2958 = OpFOrdLessThanEqual %bool %2957 %float_0 + %2959 = OpSelect %float %2958 %float_9_99999975en05 %2957 + %2960 = OpExtInst %float %1 Log %2959 + %2961 = OpFDiv %float %2960 %1091 + %2962 = OpFOrdLessThanEqual %bool %2961 %2793 + OpSelectionMerge %2963 None + OpBranchConditional %2962 %2964 %2965 + %2965 = OpLabel + %2966 = OpFOrdGreaterThan %bool %2961 %2793 + %2967 = OpExtInst %float %1 Log %2714 + %2968 = OpFDiv %float %2967 %1091 + %2969 = OpFOrdLessThan %bool %2961 %2968 + %2970 = OpLogicalAnd %bool %2966 %2969 + OpSelectionMerge %2971 None + OpBranchConditional %2970 %2972 %2973 + %2973 = OpLabel + %2974 = OpFOrdGreaterThanEqual %bool %2961 %2968 + %2975 = OpExtInst %float %1 Log %2786 + %2976 = OpFDiv %float %2975 %1091 + %2977 = OpFOrdLessThan %bool %2961 %2976 + %2978 = OpLogicalAnd %bool %2974 %2977 + OpSelectionMerge %2979 None + OpBranchConditional %2978 %2980 %2981 + %2981 = OpLabel + %2982 = OpFMul %float %2961 %float_0_0599999987 + %2983 = OpExtInst %float %1 Log %float_1000 + %2984 = OpFDiv %float %2983 %1091 + %2985 = OpFMul %float %float_0_0599999987 %2975 + %2986 = OpFDiv %float %2985 %1091 + %2987 = OpFSub %float %2984 %2986 + %2988 = OpFAdd %float %2982 %2987 + OpBranch %2979 + %2980 = OpLabel + %2989 = OpFSub %float %2961 %2968 + %2990 = OpFMul %float %float_7 %2989 + %2991 = OpFSub %float %2976 %2968 + %2992 = OpFDiv %float %2990 %2991 + %2993 = OpConvertFToS %int %2992 + %2994 = OpConvertSToF %float %2993 + %2995 = OpFSub %float %2992 %2994 + %2996 = OpAccessChain %_ptr_Function_float %551 %2993 + %2997 = OpLoad %float %2996 + %2998 = OpIAdd %int %2993 %int_1 + %2999 = OpAccessChain %_ptr_Function_float %551 %2998 + %3000 = OpLoad %float %2999 + %3001 = OpIAdd %int %2993 %int_2 + %3002 = OpAccessChain %_ptr_Function_float %551 %3001 + %3003 = OpLoad %float %3002 + %3004 = OpCompositeConstruct %v3float %2997 %3000 %3003 + %3005 = OpFMul %float %2995 %2995 + %3006 = OpCompositeConstruct %v3float %3005 %2995 %float_1 + %3007 = OpMatrixTimesVector %v3float %466 %3004 + %3008 = OpDot %float %3006 %3007 + OpBranch %2979 + %2979 = OpLabel + %3009 = OpPhi %float %2988 %2981 %3008 %2980 + OpBranch %2971 + %2972 = OpLabel + %3010 = OpFSub %float %2961 %2793 + %3011 = OpFMul %float %float_7 %3010 + %3012 = OpFSub %float %2968 %2793 + %3013 = OpFDiv %float %3011 %3012 + %3014 = OpConvertFToS %int %3013 + %3015 = OpConvertSToF %float %3014 + %3016 = OpFSub %float %3013 %3015 + %3017 = OpAccessChain %_ptr_Function_float %552 %3014 + %3018 = OpLoad %float %3017 + %3019 = OpIAdd %int %3014 %int_1 + %3020 = OpAccessChain %_ptr_Function_float %552 %3019 + %3021 = OpLoad %float %3020 + %3022 = OpIAdd %int %3014 %int_2 + %3023 = OpAccessChain %_ptr_Function_float %552 %3022 + %3024 = OpLoad %float %3023 + %3025 = OpCompositeConstruct %v3float %3018 %3021 %3024 + %3026 = OpFMul %float %3016 %3016 + %3027 = OpCompositeConstruct %v3float %3026 %3016 %float_1 + %3028 = OpMatrixTimesVector %v3float %466 %3025 + %3029 = OpDot %float %3027 %3028 + OpBranch %2971 + %2971 = OpLabel + %3030 = OpPhi %float %3009 %2979 %3029 %2972 + OpBranch %2963 + %2964 = OpLabel + %3031 = OpFMul %float %2961 %float_3 + %3032 = OpExtInst %float %1 Log %float_9_99999975en05 + %3033 = OpFDiv %float %3032 %1091 + %3034 = OpFMul %float %float_3 %2792 + %3035 = OpFDiv %float %3034 %1091 + %3036 = OpFSub %float %3033 %3035 + %3037 = OpFAdd %float %3031 %3036 + OpBranch %2963 + %2963 = OpLabel + %3038 = OpPhi %float %3030 %2971 %3037 %2964 + %3039 = OpExtInst %float %1 Pow %float_10 %3038 + %3040 = OpCompositeInsert %v3float %3039 %2956 2 + %3041 = OpFSub %v3float %3040 %361 + %3042 = OpVectorTimesMatrix %v3float %3041 %602 + %3043 = OpFMul %v3float %3042 %519 + %3044 = OpExtInst %v3float %1 Pow %3043 %286 + %3045 = OpFMul %v3float %196 %3044 + %3046 = OpFAdd %v3float %195 %3045 + %3047 = OpFMul %v3float %197 %3044 + %3048 = OpFAdd %v3float %141 %3047 + %3049 = OpFDiv %v3float %141 %3048 + %3050 = OpFMul %v3float %3046 %3049 + %3051 = OpExtInst %v3float %1 Pow %3050 %287 + OpBranch %1336 + %1336 = OpLabel + %3052 = OpPhi %v3float %2201 %1342 %3051 %2963 + OpBranch %1330 + %1331 = OpLabel + %3053 = OpVectorTimesMatrix %v3float %1324 %573 + %3054 = OpVectorTimesMatrix %v3float %3053 %602 + %3055 = OpExtInst %v3float %1 FMax %263 %3054 + %3056 = OpFMul %v3float %3055 %275 + %3057 = OpExtInst %v3float %1 FMax %3055 %277 + %3058 = OpExtInst %v3float %1 Pow %3057 %279 + %3059 = OpFMul %v3float %3058 %281 + %3060 = OpFSub %v3float %3059 %283 + %3061 = OpExtInst %v3float %1 FMin %3056 %3060 + OpBranch %1330 + %1330 = OpLabel + %3062 = OpPhi %v3float %3052 %1336 %3061 %1331 + OpBranch %1326 + %1327 = OpLabel + %3063 = OpCompositeExtract %float %1324 0 + OpBranch %3064 + %3064 = OpLabel + OpLoopMerge %3065 %3066 None + OpBranch %3067 + %3067 = OpLabel + %3068 = OpFOrdLessThan %bool %3063 %float_0_00313066994 + OpSelectionMerge %3069 None + OpBranchConditional %3068 %3070 %3069 + %3070 = OpLabel + %3071 = OpFMul %float %3063 %float_12_9200001 + OpBranch %3065 + %3069 = OpLabel + %3072 = OpExtInst %float %1 Pow %3063 %float_0_416666657 + %3073 = OpFMul %float %3072 %float_1_05499995 + %3074 = OpFSub %float %3073 %float_0_0549999997 + OpBranch %3065 + %3066 = OpLabel + OpBranch %3064 + %3065 = OpLabel + %3075 = OpPhi %float %3071 %3070 %3074 %3069 + %3076 = OpCompositeExtract %float %1324 1 + OpBranch %3077 + %3077 = OpLabel + OpLoopMerge %3078 %3079 None + OpBranch %3080 + %3080 = OpLabel + %3081 = OpFOrdLessThan %bool %3076 %float_0_00313066994 + OpSelectionMerge %3082 None + OpBranchConditional %3081 %3083 %3082 + %3083 = OpLabel + %3084 = OpFMul %float %3076 %float_12_9200001 + OpBranch %3078 + %3082 = OpLabel + %3085 = OpExtInst %float %1 Pow %3076 %float_0_416666657 + %3086 = OpFMul %float %3085 %float_1_05499995 + %3087 = OpFSub %float %3086 %float_0_0549999997 + OpBranch %3078 + %3079 = OpLabel + OpBranch %3077 + %3078 = OpLabel + %3088 = OpPhi %float %3084 %3083 %3087 %3082 + %3089 = OpCompositeExtract %float %1324 2 + OpBranch %3090 + %3090 = OpLabel + OpLoopMerge %3091 %3092 None + OpBranch %3093 + %3093 = OpLabel + %3094 = OpFOrdLessThan %bool %3089 %float_0_00313066994 + OpSelectionMerge %3095 None + OpBranchConditional %3094 %3096 %3095 + %3096 = OpLabel + %3097 = OpFMul %float %3089 %float_12_9200001 + OpBranch %3091 + %3095 = OpLabel + %3098 = OpExtInst %float %1 Pow %3089 %float_0_416666657 + %3099 = OpFMul %float %3098 %float_1_05499995 + %3100 = OpFSub %float %3099 %float_0_0549999997 + OpBranch %3091 + %3092 = OpLabel + OpBranch %3090 + %3091 = OpLabel + %3101 = OpPhi %float %3097 %3096 %3100 %3095 + %3102 = OpCompositeConstruct %v3float %3075 %3088 %3101 + OpBranch %1326 + %1326 = OpLabel + %3103 = OpPhi %v3float %3062 %1330 %3102 %3091 + %3104 = OpFMul %v3float %3103 %522 + %3105 = OpVectorShuffle %v4float %135 %3104 4 5 6 3 + %3106 = OpCompositeInsert %v4float %float_0 %3105 3 + OpStore %out_var_SV_Target0 %3106 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag new file mode 100644 index 0000000..097eb63 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/sample-mask-not-array.asm.frag @@ -0,0 +1,1230 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 271 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPS "main" %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_PRIMITIVE_ID %in_var_TEXCOORD7 %gl_FragCoord %gl_FrontFacing %gl_SampleMask %out_var_SV_Target0 %gl_SampleMask_0 + OpExecutionMode %MainPS OriginUpperLeft + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_TranslatedWorldToView" + OpMemberName %type_View 3 "View_ViewToTranslatedWorld" + OpMemberName %type_View 4 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 5 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 6 "View_ViewToClip" + OpMemberName %type_View 7 "View_ViewToClipNoAA" + OpMemberName %type_View 8 "View_ClipToView" + OpMemberName %type_View 9 "View_ClipToTranslatedWorld" + OpMemberName %type_View 10 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 11 "View_ScreenToWorld" + OpMemberName %type_View 12 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 13 "View_ViewForward" + OpMemberName %type_View 14 "PrePadding_View_844" + OpMemberName %type_View 15 "View_ViewUp" + OpMemberName %type_View 16 "PrePadding_View_860" + OpMemberName %type_View 17 "View_ViewRight" + OpMemberName %type_View 18 "PrePadding_View_876" + OpMemberName %type_View 19 "View_HMDViewNoRollUp" + OpMemberName %type_View 20 "PrePadding_View_892" + OpMemberName %type_View 21 "View_HMDViewNoRollRight" + OpMemberName %type_View 22 "PrePadding_View_908" + OpMemberName %type_View 23 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 24 "View_ScreenPositionScaleBias" + OpMemberName %type_View 25 "View_WorldCameraOrigin" + OpMemberName %type_View 26 "PrePadding_View_956" + OpMemberName %type_View 27 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 28 "PrePadding_View_972" + OpMemberName %type_View 29 "View_WorldViewOrigin" + OpMemberName %type_View 30 "PrePadding_View_988" + OpMemberName %type_View 31 "View_PreViewTranslation" + OpMemberName %type_View 32 "PrePadding_View_1004" + OpMemberName %type_View 33 "View_PrevProjection" + OpMemberName %type_View 34 "View_PrevViewProj" + OpMemberName %type_View 35 "View_PrevViewRotationProj" + OpMemberName %type_View 36 "View_PrevViewToClip" + OpMemberName %type_View 37 "View_PrevClipToView" + OpMemberName %type_View 38 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 40 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 41 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 42 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 43 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 44 "PrePadding_View_1660" + OpMemberName %type_View 45 "View_PrevWorldViewOrigin" + OpMemberName %type_View 46 "PrePadding_View_1676" + OpMemberName %type_View 47 "View_PrevPreViewTranslation" + OpMemberName %type_View 48 "PrePadding_View_1692" + OpMemberName %type_View 49 "View_PrevInvViewProj" + OpMemberName %type_View 50 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 51 "View_ClipToPrevClip" + OpMemberName %type_View 52 "View_TemporalAAJitter" + OpMemberName %type_View 53 "View_GlobalClippingPlane" + OpMemberName %type_View 54 "View_FieldOfViewWideAngles" + OpMemberName %type_View 55 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 56 "View_ViewRectMin" + OpMemberName %type_View 57 "View_ViewSizeAndInvSize" + OpMemberName %type_View 58 "View_BufferSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 60 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 61 "View_PreExposure" + OpMemberName %type_View 62 "View_OneOverPreExposure" + OpMemberName %type_View 63 "PrePadding_View_2012" + OpMemberName %type_View 64 "View_DiffuseOverrideParameter" + OpMemberName %type_View 65 "View_SpecularOverrideParameter" + OpMemberName %type_View 66 "View_NormalOverrideParameter" + OpMemberName %type_View 67 "View_RoughnessOverrideParameter" + OpMemberName %type_View 68 "View_PrevFrameGameTime" + OpMemberName %type_View 69 "View_PrevFrameRealTime" + OpMemberName %type_View 70 "View_OutOfBoundsMask" + OpMemberName %type_View 71 "PrePadding_View_2084" + OpMemberName %type_View 72 "PrePadding_View_2088" + OpMemberName %type_View 73 "PrePadding_View_2092" + OpMemberName %type_View 74 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 75 "View_CullingSign" + OpMemberName %type_View 76 "View_NearPlane" + OpMemberName %type_View 77 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 78 "View_GameTime" + OpMemberName %type_View 79 "View_RealTime" + OpMemberName %type_View 80 "View_DeltaTime" + OpMemberName %type_View 81 "View_MaterialTextureMipBias" + OpMemberName %type_View 82 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 83 "View_Random" + OpMemberName %type_View 84 "View_FrameNumber" + OpMemberName %type_View 85 "View_StateFrameIndexMod8" + OpMemberName %type_View 86 "View_StateFrameIndex" + OpMemberName %type_View 87 "View_CameraCut" + OpMemberName %type_View 88 "View_UnlitViewmodeMask" + OpMemberName %type_View 89 "PrePadding_View_2164" + OpMemberName %type_View 90 "PrePadding_View_2168" + OpMemberName %type_View 91 "PrePadding_View_2172" + OpMemberName %type_View 92 "View_DirectionalLightColor" + OpMemberName %type_View 93 "View_DirectionalLightDirection" + OpMemberName %type_View 94 "PrePadding_View_2204" + OpMemberName %type_View 95 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 97 "View_TemporalAAParams" + OpMemberName %type_View 98 "View_CircleDOFParams" + OpMemberName %type_View 99 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 100 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 101 "View_DepthOfFieldScale" + OpMemberName %type_View 102 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 103 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 104 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 105 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 106 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 107 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 108 "View_GeneralPurposeTweak" + OpMemberName %type_View 109 "View_DemosaicVposOffset" + OpMemberName %type_View 110 "PrePadding_View_2348" + OpMemberName %type_View 111 "View_IndirectLightingColorScale" + OpMemberName %type_View 112 "View_HDR32bppEncodingMode" + OpMemberName %type_View 113 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 114 "View_AtmosphericFogSunPower" + OpMemberName %type_View 115 "View_AtmosphericFogPower" + OpMemberName %type_View 116 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 117 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 118 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 119 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 120 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 121 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 122 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 123 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 124 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 125 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 126 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 127 "View_AtmosphericFogSunColor" + OpMemberName %type_View 128 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 129 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 130 "View_AmbientCubemapTint" + OpMemberName %type_View 131 "View_AmbientCubemapIntensity" + OpMemberName %type_View 132 "View_SkyLightParameters" + OpMemberName %type_View 133 "PrePadding_View_2488" + OpMemberName %type_View 134 "PrePadding_View_2492" + OpMemberName %type_View 135 "View_SkyLightColor" + OpMemberName %type_View 136 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 137 "View_MobilePreviewMode" + OpMemberName %type_View 138 "View_HMDEyePaddingOffset" + OpMemberName %type_View 139 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 140 "View_ShowDecalsMask" + OpMemberName %type_View 141 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 142 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 143 "PrePadding_View_2648" + OpMemberName %type_View 144 "PrePadding_View_2652" + OpMemberName %type_View 145 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 146 "View_StereoPassIndex" + OpMemberName %type_View 147 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 148 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 149 "View_GlobalVolumeDimension" + OpMemberName %type_View 150 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 151 "View_MaxGlobalDistance" + OpMemberName %type_View 152 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 153 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 154 "PrePadding_View_2828" + OpMemberName %type_View 155 "View_VolumetricFogGridZParams" + OpMemberName %type_View 156 "PrePadding_View_2844" + OpMemberName %type_View 157 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 158 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 159 "PrePadding_View_2860" + OpMemberName %type_View 160 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 161 "PrePadding_View_2876" + OpMemberName %type_View 162 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 163 "PrePadding_View_2892" + OpMemberName %type_View 164 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 165 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 166 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 167 "View_StereoIPD" + OpMemberName %type_View 168 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 169 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_3d_image "type.3d.image" + OpName %type_sampler "type.sampler" + OpName %View_SharedBilinearClampedSampler "View_SharedBilinearClampedSampler" + OpName %type_StructuredBuffer_v4float "type.StructuredBuffer.v4float" + OpName %View_PrimitiveSceneData "View_PrimitiveSceneData" + OpName %type_TranslucentBasePass "type.TranslucentBasePass" + OpMemberName %type_TranslucentBasePass 0 "TranslucentBasePass_Shared_Forward_NumLocalLights" + OpMemberName %type_TranslucentBasePass 1 "TranslucentBasePass_Shared_Forward_NumReflectionCaptures" + OpMemberName %type_TranslucentBasePass 2 "TranslucentBasePass_Shared_Forward_HasDirectionalLight" + OpMemberName %type_TranslucentBasePass 3 "TranslucentBasePass_Shared_Forward_NumGridCells" + OpMemberName %type_TranslucentBasePass 4 "TranslucentBasePass_Shared_Forward_CulledGridSize" + OpMemberName %type_TranslucentBasePass 5 "TranslucentBasePass_Shared_Forward_MaxCulledLightsPerCell" + OpMemberName %type_TranslucentBasePass 6 "TranslucentBasePass_Shared_Forward_LightGridPixelSizeShift" + OpMemberName %type_TranslucentBasePass 7 "PrePadding_TranslucentBasePass_Shared_Forward_36" + OpMemberName %type_TranslucentBasePass 8 "PrePadding_TranslucentBasePass_Shared_Forward_40" + OpMemberName %type_TranslucentBasePass 9 "PrePadding_TranslucentBasePass_Shared_Forward_44" + OpMemberName %type_TranslucentBasePass 10 "TranslucentBasePass_Shared_Forward_LightGridZParams" + OpMemberName %type_TranslucentBasePass 11 "PrePadding_TranslucentBasePass_Shared_Forward_60" + OpMemberName %type_TranslucentBasePass 12 "TranslucentBasePass_Shared_Forward_DirectionalLightDirection" + OpMemberName %type_TranslucentBasePass 13 "PrePadding_TranslucentBasePass_Shared_Forward_76" + OpMemberName %type_TranslucentBasePass 14 "TranslucentBasePass_Shared_Forward_DirectionalLightColor" + OpMemberName %type_TranslucentBasePass 15 "TranslucentBasePass_Shared_Forward_DirectionalLightVolumetricScatteringIntensity" + OpMemberName %type_TranslucentBasePass 16 "TranslucentBasePass_Shared_Forward_DirectionalLightShadowMapChannelMask" + OpMemberName %type_TranslucentBasePass 17 "PrePadding_TranslucentBasePass_Shared_Forward_100" + OpMemberName %type_TranslucentBasePass 18 "TranslucentBasePass_Shared_Forward_DirectionalLightDistanceFadeMAD" + OpMemberName %type_TranslucentBasePass 19 "TranslucentBasePass_Shared_Forward_NumDirectionalLightCascades" + OpMemberName %type_TranslucentBasePass 20 "PrePadding_TranslucentBasePass_Shared_Forward_116" + OpMemberName %type_TranslucentBasePass 21 "PrePadding_TranslucentBasePass_Shared_Forward_120" + OpMemberName %type_TranslucentBasePass 22 "PrePadding_TranslucentBasePass_Shared_Forward_124" + OpMemberName %type_TranslucentBasePass 23 "TranslucentBasePass_Shared_Forward_CascadeEndDepths" + OpMemberName %type_TranslucentBasePass 24 "TranslucentBasePass_Shared_Forward_DirectionalLightWorldToShadowMatrix" + OpMemberName %type_TranslucentBasePass 25 "TranslucentBasePass_Shared_Forward_DirectionalLightShadowmapMinMax" + OpMemberName %type_TranslucentBasePass 26 "TranslucentBasePass_Shared_Forward_DirectionalLightShadowmapAtlasBufferSize" + OpMemberName %type_TranslucentBasePass 27 "TranslucentBasePass_Shared_Forward_DirectionalLightDepthBias" + OpMemberName %type_TranslucentBasePass 28 "TranslucentBasePass_Shared_Forward_DirectionalLightUseStaticShadowing" + OpMemberName %type_TranslucentBasePass 29 "PrePadding_TranslucentBasePass_Shared_Forward_488" + OpMemberName %type_TranslucentBasePass 30 "PrePadding_TranslucentBasePass_Shared_Forward_492" + OpMemberName %type_TranslucentBasePass 31 "TranslucentBasePass_Shared_Forward_DirectionalLightStaticShadowBufferSize" + OpMemberName %type_TranslucentBasePass 32 "TranslucentBasePass_Shared_Forward_DirectionalLightWorldToStaticShadow" + OpMemberName %type_TranslucentBasePass 33 "PrePadding_TranslucentBasePass_Shared_ForwardISR_576" + OpMemberName %type_TranslucentBasePass 34 "PrePadding_TranslucentBasePass_Shared_ForwardISR_580" + OpMemberName %type_TranslucentBasePass 35 "PrePadding_TranslucentBasePass_Shared_ForwardISR_584" + OpMemberName %type_TranslucentBasePass 36 "PrePadding_TranslucentBasePass_Shared_ForwardISR_588" + OpMemberName %type_TranslucentBasePass 37 "PrePadding_TranslucentBasePass_Shared_ForwardISR_592" + OpMemberName %type_TranslucentBasePass 38 "PrePadding_TranslucentBasePass_Shared_ForwardISR_596" + OpMemberName %type_TranslucentBasePass 39 "PrePadding_TranslucentBasePass_Shared_ForwardISR_600" + OpMemberName %type_TranslucentBasePass 40 "PrePadding_TranslucentBasePass_Shared_ForwardISR_604" + OpMemberName %type_TranslucentBasePass 41 "PrePadding_TranslucentBasePass_Shared_ForwardISR_608" + OpMemberName %type_TranslucentBasePass 42 "PrePadding_TranslucentBasePass_Shared_ForwardISR_612" + OpMemberName %type_TranslucentBasePass 43 "PrePadding_TranslucentBasePass_Shared_ForwardISR_616" + OpMemberName %type_TranslucentBasePass 44 "PrePadding_TranslucentBasePass_Shared_ForwardISR_620" + OpMemberName %type_TranslucentBasePass 45 "PrePadding_TranslucentBasePass_Shared_ForwardISR_624" + OpMemberName %type_TranslucentBasePass 46 "PrePadding_TranslucentBasePass_Shared_ForwardISR_628" + OpMemberName %type_TranslucentBasePass 47 "PrePadding_TranslucentBasePass_Shared_ForwardISR_632" + OpMemberName %type_TranslucentBasePass 48 "PrePadding_TranslucentBasePass_Shared_ForwardISR_636" + OpMemberName %type_TranslucentBasePass 49 "TranslucentBasePass_Shared_ForwardISR_NumLocalLights" + OpMemberName %type_TranslucentBasePass 50 "TranslucentBasePass_Shared_ForwardISR_NumReflectionCaptures" + OpMemberName %type_TranslucentBasePass 51 "TranslucentBasePass_Shared_ForwardISR_HasDirectionalLight" + OpMemberName %type_TranslucentBasePass 52 "TranslucentBasePass_Shared_ForwardISR_NumGridCells" + OpMemberName %type_TranslucentBasePass 53 "TranslucentBasePass_Shared_ForwardISR_CulledGridSize" + OpMemberName %type_TranslucentBasePass 54 "TranslucentBasePass_Shared_ForwardISR_MaxCulledLightsPerCell" + OpMemberName %type_TranslucentBasePass 55 "TranslucentBasePass_Shared_ForwardISR_LightGridPixelSizeShift" + OpMemberName %type_TranslucentBasePass 56 "PrePadding_TranslucentBasePass_Shared_ForwardISR_676" + OpMemberName %type_TranslucentBasePass 57 "PrePadding_TranslucentBasePass_Shared_ForwardISR_680" + OpMemberName %type_TranslucentBasePass 58 "PrePadding_TranslucentBasePass_Shared_ForwardISR_684" + OpMemberName %type_TranslucentBasePass 59 "TranslucentBasePass_Shared_ForwardISR_LightGridZParams" + OpMemberName %type_TranslucentBasePass 60 "PrePadding_TranslucentBasePass_Shared_ForwardISR_700" + OpMemberName %type_TranslucentBasePass 61 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightDirection" + OpMemberName %type_TranslucentBasePass 62 "PrePadding_TranslucentBasePass_Shared_ForwardISR_716" + OpMemberName %type_TranslucentBasePass 63 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightColor" + OpMemberName %type_TranslucentBasePass 64 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightVolumetricScatteringIntensity" + OpMemberName %type_TranslucentBasePass 65 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowMapChannelMask" + OpMemberName %type_TranslucentBasePass 66 "PrePadding_TranslucentBasePass_Shared_ForwardISR_740" + OpMemberName %type_TranslucentBasePass 67 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightDistanceFadeMAD" + OpMemberName %type_TranslucentBasePass 68 "TranslucentBasePass_Shared_ForwardISR_NumDirectionalLightCascades" + OpMemberName %type_TranslucentBasePass 69 "PrePadding_TranslucentBasePass_Shared_ForwardISR_756" + OpMemberName %type_TranslucentBasePass 70 "PrePadding_TranslucentBasePass_Shared_ForwardISR_760" + OpMemberName %type_TranslucentBasePass 71 "PrePadding_TranslucentBasePass_Shared_ForwardISR_764" + OpMemberName %type_TranslucentBasePass 72 "TranslucentBasePass_Shared_ForwardISR_CascadeEndDepths" + OpMemberName %type_TranslucentBasePass 73 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightWorldToShadowMatrix" + OpMemberName %type_TranslucentBasePass 74 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowmapMinMax" + OpMemberName %type_TranslucentBasePass 75 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightShadowmapAtlasBufferSize" + OpMemberName %type_TranslucentBasePass 76 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightDepthBias" + OpMemberName %type_TranslucentBasePass 77 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightUseStaticShadowing" + OpMemberName %type_TranslucentBasePass 78 "PrePadding_TranslucentBasePass_Shared_ForwardISR_1128" + OpMemberName %type_TranslucentBasePass 79 "PrePadding_TranslucentBasePass_Shared_ForwardISR_1132" + OpMemberName %type_TranslucentBasePass 80 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightStaticShadowBufferSize" + OpMemberName %type_TranslucentBasePass 81 "TranslucentBasePass_Shared_ForwardISR_DirectionalLightWorldToStaticShadow" + OpMemberName %type_TranslucentBasePass 82 "PrePadding_TranslucentBasePass_Shared_Reflection_1216" + OpMemberName %type_TranslucentBasePass 83 "PrePadding_TranslucentBasePass_Shared_Reflection_1220" + OpMemberName %type_TranslucentBasePass 84 "PrePadding_TranslucentBasePass_Shared_Reflection_1224" + OpMemberName %type_TranslucentBasePass 85 "PrePadding_TranslucentBasePass_Shared_Reflection_1228" + OpMemberName %type_TranslucentBasePass 86 "PrePadding_TranslucentBasePass_Shared_Reflection_1232" + OpMemberName %type_TranslucentBasePass 87 "PrePadding_TranslucentBasePass_Shared_Reflection_1236" + OpMemberName %type_TranslucentBasePass 88 "PrePadding_TranslucentBasePass_Shared_Reflection_1240" + OpMemberName %type_TranslucentBasePass 89 "PrePadding_TranslucentBasePass_Shared_Reflection_1244" + OpMemberName %type_TranslucentBasePass 90 "PrePadding_TranslucentBasePass_Shared_Reflection_1248" + OpMemberName %type_TranslucentBasePass 91 "PrePadding_TranslucentBasePass_Shared_Reflection_1252" + OpMemberName %type_TranslucentBasePass 92 "PrePadding_TranslucentBasePass_Shared_Reflection_1256" + OpMemberName %type_TranslucentBasePass 93 "PrePadding_TranslucentBasePass_Shared_Reflection_1260" + OpMemberName %type_TranslucentBasePass 94 "PrePadding_TranslucentBasePass_Shared_Reflection_1264" + OpMemberName %type_TranslucentBasePass 95 "PrePadding_TranslucentBasePass_Shared_Reflection_1268" + OpMemberName %type_TranslucentBasePass 96 "PrePadding_TranslucentBasePass_Shared_Reflection_1272" + OpMemberName %type_TranslucentBasePass 97 "PrePadding_TranslucentBasePass_Shared_Reflection_1276" + OpMemberName %type_TranslucentBasePass 98 "TranslucentBasePass_Shared_Reflection_SkyLightParameters" + OpMemberName %type_TranslucentBasePass 99 "TranslucentBasePass_Shared_Reflection_SkyLightCubemapBrightness" + OpMemberName %type_TranslucentBasePass 100 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1300" + OpMemberName %type_TranslucentBasePass 101 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1304" + OpMemberName %type_TranslucentBasePass 102 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1308" + OpMemberName %type_TranslucentBasePass 103 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1312" + OpMemberName %type_TranslucentBasePass 104 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1316" + OpMemberName %type_TranslucentBasePass 105 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1320" + OpMemberName %type_TranslucentBasePass 106 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1324" + OpMemberName %type_TranslucentBasePass 107 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1328" + OpMemberName %type_TranslucentBasePass 108 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1332" + OpMemberName %type_TranslucentBasePass 109 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1336" + OpMemberName %type_TranslucentBasePass 110 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1340" + OpMemberName %type_TranslucentBasePass 111 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1344" + OpMemberName %type_TranslucentBasePass 112 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1348" + OpMemberName %type_TranslucentBasePass 113 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1352" + OpMemberName %type_TranslucentBasePass 114 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1356" + OpMemberName %type_TranslucentBasePass 115 "TranslucentBasePass_Shared_PlanarReflection_ReflectionPlane" + OpMemberName %type_TranslucentBasePass 116 "TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionOrigin" + OpMemberName %type_TranslucentBasePass 117 "TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionXAxis" + OpMemberName %type_TranslucentBasePass 118 "TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionYAxis" + OpMemberName %type_TranslucentBasePass 119 "TranslucentBasePass_Shared_PlanarReflection_InverseTransposeMirrorMatrix" + OpMemberName %type_TranslucentBasePass 120 "TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionParameters" + OpMemberName %type_TranslucentBasePass 121 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1484" + OpMemberName %type_TranslucentBasePass 122 "TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionParameters2" + OpMemberName %type_TranslucentBasePass 123 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1496" + OpMemberName %type_TranslucentBasePass 124 "PrePadding_TranslucentBasePass_Shared_PlanarReflection_1500" + OpMemberName %type_TranslucentBasePass 125 "TranslucentBasePass_Shared_PlanarReflection_ProjectionWithExtraFOV" + OpMemberName %type_TranslucentBasePass 126 "TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionScreenScaleBias" + OpMemberName %type_TranslucentBasePass 127 "TranslucentBasePass_Shared_PlanarReflection_PlanarReflectionScreenBound" + OpMemberName %type_TranslucentBasePass 128 "TranslucentBasePass_Shared_PlanarReflection_bIsStereo" + OpMemberName %type_TranslucentBasePass 129 "PrePadding_TranslucentBasePass_Shared_Fog_1676" + OpMemberName %type_TranslucentBasePass 130 "PrePadding_TranslucentBasePass_Shared_Fog_1680" + OpMemberName %type_TranslucentBasePass 131 "PrePadding_TranslucentBasePass_Shared_Fog_1684" + OpMemberName %type_TranslucentBasePass 132 "PrePadding_TranslucentBasePass_Shared_Fog_1688" + OpMemberName %type_TranslucentBasePass 133 "PrePadding_TranslucentBasePass_Shared_Fog_1692" + OpMemberName %type_TranslucentBasePass 134 "TranslucentBasePass_Shared_Fog_ExponentialFogParameters" + OpMemberName %type_TranslucentBasePass 135 "TranslucentBasePass_Shared_Fog_ExponentialFogParameters2" + OpMemberName %type_TranslucentBasePass 136 "TranslucentBasePass_Shared_Fog_ExponentialFogColorParameter" + OpMemberName %type_TranslucentBasePass 137 "TranslucentBasePass_Shared_Fog_ExponentialFogParameters3" + OpMemberName %type_TranslucentBasePass 138 "TranslucentBasePass_Shared_Fog_InscatteringLightDirection" + OpMemberName %type_TranslucentBasePass 139 "TranslucentBasePass_Shared_Fog_DirectionalInscatteringColor" + OpMemberName %type_TranslucentBasePass 140 "TranslucentBasePass_Shared_Fog_SinCosInscatteringColorCubemapRotation" + OpMemberName %type_TranslucentBasePass 141 "PrePadding_TranslucentBasePass_Shared_Fog_1800" + OpMemberName %type_TranslucentBasePass 142 "PrePadding_TranslucentBasePass_Shared_Fog_1804" + OpMemberName %type_TranslucentBasePass 143 "TranslucentBasePass_Shared_Fog_FogInscatteringTextureParameters" + OpMemberName %type_TranslucentBasePass 144 "TranslucentBasePass_Shared_Fog_ApplyVolumetricFog" + OpMemberName %type_TranslucentBasePass 145 "PrePadding_TranslucentBasePass_1824" + OpMemberName %type_TranslucentBasePass 146 "PrePadding_TranslucentBasePass_1828" + OpMemberName %type_TranslucentBasePass 147 "PrePadding_TranslucentBasePass_1832" + OpMemberName %type_TranslucentBasePass 148 "PrePadding_TranslucentBasePass_1836" + OpMemberName %type_TranslucentBasePass 149 "PrePadding_TranslucentBasePass_1840" + OpMemberName %type_TranslucentBasePass 150 "PrePadding_TranslucentBasePass_1844" + OpMemberName %type_TranslucentBasePass 151 "PrePadding_TranslucentBasePass_1848" + OpMemberName %type_TranslucentBasePass 152 "PrePadding_TranslucentBasePass_1852" + OpMemberName %type_TranslucentBasePass 153 "PrePadding_TranslucentBasePass_1856" + OpMemberName %type_TranslucentBasePass 154 "PrePadding_TranslucentBasePass_1860" + OpMemberName %type_TranslucentBasePass 155 "PrePadding_TranslucentBasePass_1864" + OpMemberName %type_TranslucentBasePass 156 "PrePadding_TranslucentBasePass_1868" + OpMemberName %type_TranslucentBasePass 157 "PrePadding_TranslucentBasePass_1872" + OpMemberName %type_TranslucentBasePass 158 "PrePadding_TranslucentBasePass_1876" + OpMemberName %type_TranslucentBasePass 159 "PrePadding_TranslucentBasePass_1880" + OpMemberName %type_TranslucentBasePass 160 "PrePadding_TranslucentBasePass_1884" + OpMemberName %type_TranslucentBasePass 161 "PrePadding_TranslucentBasePass_1888" + OpMemberName %type_TranslucentBasePass 162 "PrePadding_TranslucentBasePass_1892" + OpMemberName %type_TranslucentBasePass 163 "PrePadding_TranslucentBasePass_1896" + OpMemberName %type_TranslucentBasePass 164 "PrePadding_TranslucentBasePass_1900" + OpMemberName %type_TranslucentBasePass 165 "PrePadding_TranslucentBasePass_1904" + OpMemberName %type_TranslucentBasePass 166 "PrePadding_TranslucentBasePass_1908" + OpMemberName %type_TranslucentBasePass 167 "PrePadding_TranslucentBasePass_1912" + OpMemberName %type_TranslucentBasePass 168 "PrePadding_TranslucentBasePass_1916" + OpMemberName %type_TranslucentBasePass 169 "PrePadding_TranslucentBasePass_1920" + OpMemberName %type_TranslucentBasePass 170 "PrePadding_TranslucentBasePass_1924" + OpMemberName %type_TranslucentBasePass 171 "PrePadding_TranslucentBasePass_1928" + OpMemberName %type_TranslucentBasePass 172 "PrePadding_TranslucentBasePass_1932" + OpMemberName %type_TranslucentBasePass 173 "PrePadding_TranslucentBasePass_1936" + OpMemberName %type_TranslucentBasePass 174 "PrePadding_TranslucentBasePass_1940" + OpMemberName %type_TranslucentBasePass 175 "PrePadding_TranslucentBasePass_1944" + OpMemberName %type_TranslucentBasePass 176 "PrePadding_TranslucentBasePass_1948" + OpMemberName %type_TranslucentBasePass 177 "PrePadding_TranslucentBasePass_1952" + OpMemberName %type_TranslucentBasePass 178 "PrePadding_TranslucentBasePass_1956" + OpMemberName %type_TranslucentBasePass 179 "PrePadding_TranslucentBasePass_1960" + OpMemberName %type_TranslucentBasePass 180 "PrePadding_TranslucentBasePass_1964" + OpMemberName %type_TranslucentBasePass 181 "PrePadding_TranslucentBasePass_1968" + OpMemberName %type_TranslucentBasePass 182 "PrePadding_TranslucentBasePass_1972" + OpMemberName %type_TranslucentBasePass 183 "PrePadding_TranslucentBasePass_1976" + OpMemberName %type_TranslucentBasePass 184 "PrePadding_TranslucentBasePass_1980" + OpMemberName %type_TranslucentBasePass 185 "PrePadding_TranslucentBasePass_1984" + OpMemberName %type_TranslucentBasePass 186 "PrePadding_TranslucentBasePass_1988" + OpMemberName %type_TranslucentBasePass 187 "PrePadding_TranslucentBasePass_1992" + OpMemberName %type_TranslucentBasePass 188 "PrePadding_TranslucentBasePass_1996" + OpMemberName %type_TranslucentBasePass 189 "PrePadding_TranslucentBasePass_2000" + OpMemberName %type_TranslucentBasePass 190 "PrePadding_TranslucentBasePass_2004" + OpMemberName %type_TranslucentBasePass 191 "PrePadding_TranslucentBasePass_2008" + OpMemberName %type_TranslucentBasePass 192 "PrePadding_TranslucentBasePass_2012" + OpMemberName %type_TranslucentBasePass 193 "PrePadding_TranslucentBasePass_2016" + OpMemberName %type_TranslucentBasePass 194 "PrePadding_TranslucentBasePass_2020" + OpMemberName %type_TranslucentBasePass 195 "PrePadding_TranslucentBasePass_2024" + OpMemberName %type_TranslucentBasePass 196 "PrePadding_TranslucentBasePass_2028" + OpMemberName %type_TranslucentBasePass 197 "PrePadding_TranslucentBasePass_2032" + OpMemberName %type_TranslucentBasePass 198 "PrePadding_TranslucentBasePass_2036" + OpMemberName %type_TranslucentBasePass 199 "PrePadding_TranslucentBasePass_2040" + OpMemberName %type_TranslucentBasePass 200 "PrePadding_TranslucentBasePass_2044" + OpMemberName %type_TranslucentBasePass 201 "PrePadding_TranslucentBasePass_2048" + OpMemberName %type_TranslucentBasePass 202 "PrePadding_TranslucentBasePass_2052" + OpMemberName %type_TranslucentBasePass 203 "PrePadding_TranslucentBasePass_2056" + OpMemberName %type_TranslucentBasePass 204 "PrePadding_TranslucentBasePass_2060" + OpMemberName %type_TranslucentBasePass 205 "PrePadding_TranslucentBasePass_2064" + OpMemberName %type_TranslucentBasePass 206 "PrePadding_TranslucentBasePass_2068" + OpMemberName %type_TranslucentBasePass 207 "PrePadding_TranslucentBasePass_2072" + OpMemberName %type_TranslucentBasePass 208 "PrePadding_TranslucentBasePass_2076" + OpMemberName %type_TranslucentBasePass 209 "PrePadding_TranslucentBasePass_2080" + OpMemberName %type_TranslucentBasePass 210 "PrePadding_TranslucentBasePass_2084" + OpMemberName %type_TranslucentBasePass 211 "PrePadding_TranslucentBasePass_2088" + OpMemberName %type_TranslucentBasePass 212 "PrePadding_TranslucentBasePass_2092" + OpMemberName %type_TranslucentBasePass 213 "PrePadding_TranslucentBasePass_2096" + OpMemberName %type_TranslucentBasePass 214 "PrePadding_TranslucentBasePass_2100" + OpMemberName %type_TranslucentBasePass 215 "PrePadding_TranslucentBasePass_2104" + OpMemberName %type_TranslucentBasePass 216 "PrePadding_TranslucentBasePass_2108" + OpMemberName %type_TranslucentBasePass 217 "PrePadding_TranslucentBasePass_2112" + OpMemberName %type_TranslucentBasePass 218 "PrePadding_TranslucentBasePass_2116" + OpMemberName %type_TranslucentBasePass 219 "PrePadding_TranslucentBasePass_2120" + OpMemberName %type_TranslucentBasePass 220 "PrePadding_TranslucentBasePass_2124" + OpMemberName %type_TranslucentBasePass 221 "PrePadding_TranslucentBasePass_2128" + OpMemberName %type_TranslucentBasePass 222 "PrePadding_TranslucentBasePass_2132" + OpMemberName %type_TranslucentBasePass 223 "PrePadding_TranslucentBasePass_2136" + OpMemberName %type_TranslucentBasePass 224 "PrePadding_TranslucentBasePass_2140" + OpMemberName %type_TranslucentBasePass 225 "TranslucentBasePass_HZBUvFactorAndInvFactor" + OpMemberName %type_TranslucentBasePass 226 "TranslucentBasePass_PrevScreenPositionScaleBias" + OpMemberName %type_TranslucentBasePass 227 "TranslucentBasePass_PrevSceneColorPreExposureInv" + OpName %TranslucentBasePass "TranslucentBasePass" + OpName %TranslucentBasePass_Shared_Fog_IntegratedLightScattering "TranslucentBasePass_Shared_Fog_IntegratedLightScattering" + OpName %type_Material "type.Material" + OpMemberName %type_Material 0 "Material_VectorExpressions" + OpMemberName %type_Material 1 "Material_ScalarExpressions" + OpName %Material "Material" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_PRIMITIVE_ID "in.var.PRIMITIVE_ID" + OpName %in_var_TEXCOORD7 "in.var.TEXCOORD7" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPS "MainPS" + OpName %type_sampled_image "type.sampled.image" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_PRIMITIVE_ID UserSemantic "PRIMITIVE_ID" + OpDecorate %in_var_PRIMITIVE_ID Flat + OpDecorateString %in_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_Position" + OpDecorate %gl_FrontFacing BuiltIn FrontFacing + OpDecorateString %gl_FrontFacing UserSemantic "SV_IsFrontFace" + OpDecorate %gl_FrontFacing Flat + OpDecorate %gl_SampleMask BuiltIn SampleMask + OpDecorateString %gl_SampleMask UserSemantic "SV_Coverage" + OpDecorate %gl_SampleMask Flat + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %gl_SampleMask_0 BuiltIn SampleMask + OpDecorateString %gl_SampleMask_0 UserSemantic "SV_Coverage" + OpDecorate %in_var_TEXCOORD10_centroid Location 0 + OpDecorate %in_var_TEXCOORD11_centroid Location 1 + OpDecorate %in_var_PRIMITIVE_ID Location 2 + OpDecorate %in_var_TEXCOORD7 Location 3 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 1 + OpDecorate %View_SharedBilinearClampedSampler DescriptorSet 0 + OpDecorate %View_SharedBilinearClampedSampler Binding 0 + OpDecorate %View_PrimitiveSceneData DescriptorSet 0 + OpDecorate %View_PrimitiveSceneData Binding 0 + OpDecorate %TranslucentBasePass DescriptorSet 0 + OpDecorate %TranslucentBasePass Binding 2 + OpDecorate %TranslucentBasePass_Shared_Fog_IntegratedLightScattering DescriptorSet 0 + OpDecorate %TranslucentBasePass_Shared_Fog_IntegratedLightScattering Binding 0 + OpDecorate %Material DescriptorSet 0 + OpDecorate %Material Binding 3 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 14 Offset 844 + OpMemberDecorate %type_View 15 Offset 848 + OpMemberDecorate %type_View 16 Offset 860 + OpMemberDecorate %type_View 17 Offset 864 + OpMemberDecorate %type_View 18 Offset 876 + OpMemberDecorate %type_View 19 Offset 880 + OpMemberDecorate %type_View 20 Offset 892 + OpMemberDecorate %type_View 21 Offset 896 + OpMemberDecorate %type_View 22 Offset 908 + OpMemberDecorate %type_View 23 Offset 912 + OpMemberDecorate %type_View 24 Offset 928 + OpMemberDecorate %type_View 25 Offset 944 + OpMemberDecorate %type_View 26 Offset 956 + OpMemberDecorate %type_View 27 Offset 960 + OpMemberDecorate %type_View 28 Offset 972 + OpMemberDecorate %type_View 29 Offset 976 + OpMemberDecorate %type_View 30 Offset 988 + OpMemberDecorate %type_View 31 Offset 992 + OpMemberDecorate %type_View 32 Offset 1004 + OpMemberDecorate %type_View 33 Offset 1008 + OpMemberDecorate %type_View 33 MatrixStride 16 + OpMemberDecorate %type_View 33 ColMajor + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 44 Offset 1660 + OpMemberDecorate %type_View 45 Offset 1664 + OpMemberDecorate %type_View 46 Offset 1676 + OpMemberDecorate %type_View 47 Offset 1680 + OpMemberDecorate %type_View 48 Offset 1692 + OpMemberDecorate %type_View 49 Offset 1696 + OpMemberDecorate %type_View 49 MatrixStride 16 + OpMemberDecorate %type_View 49 ColMajor + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 53 Offset 1904 + OpMemberDecorate %type_View 54 Offset 1920 + OpMemberDecorate %type_View 55 Offset 1928 + OpMemberDecorate %type_View 56 Offset 1936 + OpMemberDecorate %type_View 57 Offset 1952 + OpMemberDecorate %type_View 58 Offset 1968 + OpMemberDecorate %type_View 59 Offset 1984 + OpMemberDecorate %type_View 60 Offset 2000 + OpMemberDecorate %type_View 61 Offset 2004 + OpMemberDecorate %type_View 62 Offset 2008 + OpMemberDecorate %type_View 63 Offset 2012 + OpMemberDecorate %type_View 64 Offset 2016 + OpMemberDecorate %type_View 65 Offset 2032 + OpMemberDecorate %type_View 66 Offset 2048 + OpMemberDecorate %type_View 67 Offset 2064 + OpMemberDecorate %type_View 68 Offset 2072 + OpMemberDecorate %type_View 69 Offset 2076 + OpMemberDecorate %type_View 70 Offset 2080 + OpMemberDecorate %type_View 71 Offset 2084 + OpMemberDecorate %type_View 72 Offset 2088 + OpMemberDecorate %type_View 73 Offset 2092 + OpMemberDecorate %type_View 74 Offset 2096 + OpMemberDecorate %type_View 75 Offset 2108 + OpMemberDecorate %type_View 76 Offset 2112 + OpMemberDecorate %type_View 77 Offset 2116 + OpMemberDecorate %type_View 78 Offset 2120 + OpMemberDecorate %type_View 79 Offset 2124 + OpMemberDecorate %type_View 80 Offset 2128 + OpMemberDecorate %type_View 81 Offset 2132 + OpMemberDecorate %type_View 82 Offset 2136 + OpMemberDecorate %type_View 83 Offset 2140 + OpMemberDecorate %type_View 84 Offset 2144 + OpMemberDecorate %type_View 85 Offset 2148 + OpMemberDecorate %type_View 86 Offset 2152 + OpMemberDecorate %type_View 87 Offset 2156 + OpMemberDecorate %type_View 88 Offset 2160 + OpMemberDecorate %type_View 89 Offset 2164 + OpMemberDecorate %type_View 90 Offset 2168 + OpMemberDecorate %type_View 91 Offset 2172 + OpMemberDecorate %type_View 92 Offset 2176 + OpMemberDecorate %type_View 93 Offset 2192 + OpMemberDecorate %type_View 94 Offset 2204 + OpMemberDecorate %type_View 95 Offset 2208 + OpMemberDecorate %type_View 96 Offset 2240 + OpMemberDecorate %type_View 97 Offset 2272 + OpMemberDecorate %type_View 98 Offset 2288 + OpMemberDecorate %type_View 99 Offset 2304 + OpMemberDecorate %type_View 100 Offset 2308 + OpMemberDecorate %type_View 101 Offset 2312 + OpMemberDecorate %type_View 102 Offset 2316 + OpMemberDecorate %type_View 103 Offset 2320 + OpMemberDecorate %type_View 104 Offset 2324 + OpMemberDecorate %type_View 105 Offset 2328 + OpMemberDecorate %type_View 106 Offset 2332 + OpMemberDecorate %type_View 107 Offset 2336 + OpMemberDecorate %type_View 108 Offset 2340 + OpMemberDecorate %type_View 109 Offset 2344 + OpMemberDecorate %type_View 110 Offset 2348 + OpMemberDecorate %type_View 111 Offset 2352 + OpMemberDecorate %type_View 112 Offset 2364 + OpMemberDecorate %type_View 113 Offset 2368 + OpMemberDecorate %type_View 114 Offset 2380 + OpMemberDecorate %type_View 115 Offset 2384 + OpMemberDecorate %type_View 116 Offset 2388 + OpMemberDecorate %type_View 117 Offset 2392 + OpMemberDecorate %type_View 118 Offset 2396 + OpMemberDecorate %type_View 119 Offset 2400 + OpMemberDecorate %type_View 120 Offset 2404 + OpMemberDecorate %type_View 121 Offset 2408 + OpMemberDecorate %type_View 122 Offset 2412 + OpMemberDecorate %type_View 123 Offset 2416 + OpMemberDecorate %type_View 124 Offset 2420 + OpMemberDecorate %type_View 125 Offset 2424 + OpMemberDecorate %type_View 126 Offset 2428 + OpMemberDecorate %type_View 127 Offset 2432 + OpMemberDecorate %type_View 128 Offset 2448 + OpMemberDecorate %type_View 129 Offset 2460 + OpMemberDecorate %type_View 130 Offset 2464 + OpMemberDecorate %type_View 131 Offset 2480 + OpMemberDecorate %type_View 132 Offset 2484 + OpMemberDecorate %type_View 133 Offset 2488 + OpMemberDecorate %type_View 134 Offset 2492 + OpMemberDecorate %type_View 135 Offset 2496 + OpMemberDecorate %type_View 136 Offset 2512 + OpMemberDecorate %type_View 137 Offset 2624 + OpMemberDecorate %type_View 138 Offset 2628 + OpMemberDecorate %type_View 139 Offset 2632 + OpMemberDecorate %type_View 140 Offset 2636 + OpMemberDecorate %type_View 141 Offset 2640 + OpMemberDecorate %type_View 142 Offset 2644 + OpMemberDecorate %type_View 143 Offset 2648 + OpMemberDecorate %type_View 144 Offset 2652 + OpMemberDecorate %type_View 145 Offset 2656 + OpMemberDecorate %type_View 146 Offset 2668 + OpMemberDecorate %type_View 147 Offset 2672 + OpMemberDecorate %type_View 148 Offset 2736 + OpMemberDecorate %type_View 149 Offset 2800 + OpMemberDecorate %type_View 150 Offset 2804 + OpMemberDecorate %type_View 151 Offset 2808 + OpMemberDecorate %type_View 152 Offset 2812 + OpMemberDecorate %type_View 153 Offset 2816 + OpMemberDecorate %type_View 154 Offset 2828 + OpMemberDecorate %type_View 155 Offset 2832 + OpMemberDecorate %type_View 156 Offset 2844 + OpMemberDecorate %type_View 157 Offset 2848 + OpMemberDecorate %type_View 158 Offset 2856 + OpMemberDecorate %type_View 159 Offset 2860 + OpMemberDecorate %type_View 160 Offset 2864 + OpMemberDecorate %type_View 161 Offset 2876 + OpMemberDecorate %type_View 162 Offset 2880 + OpMemberDecorate %type_View 163 Offset 2892 + OpMemberDecorate %type_View 164 Offset 2896 + OpMemberDecorate %type_View 165 Offset 2908 + OpMemberDecorate %type_View 166 Offset 2912 + OpMemberDecorate %type_View 167 Offset 2924 + OpMemberDecorate %type_View 168 Offset 2928 + OpMemberDecorate %type_View 169 Offset 2932 + OpDecorate %type_View Block + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_v4float 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_v4float 0 NonWritable + OpDecorate %type_StructuredBuffer_v4float BufferBlock + OpDecorate %_arr_mat4v4float_uint_4 ArrayStride 64 + OpDecorate %_arr_mat4v4float_uint_2 ArrayStride 64 + OpMemberDecorate %type_TranslucentBasePass 0 Offset 0 + OpMemberDecorate %type_TranslucentBasePass 1 Offset 4 + OpMemberDecorate %type_TranslucentBasePass 2 Offset 8 + OpMemberDecorate %type_TranslucentBasePass 3 Offset 12 + OpMemberDecorate %type_TranslucentBasePass 4 Offset 16 + OpMemberDecorate %type_TranslucentBasePass 5 Offset 28 + OpMemberDecorate %type_TranslucentBasePass 6 Offset 32 + OpMemberDecorate %type_TranslucentBasePass 7 Offset 36 + OpMemberDecorate %type_TranslucentBasePass 8 Offset 40 + OpMemberDecorate %type_TranslucentBasePass 9 Offset 44 + OpMemberDecorate %type_TranslucentBasePass 10 Offset 48 + OpMemberDecorate %type_TranslucentBasePass 11 Offset 60 + OpMemberDecorate %type_TranslucentBasePass 12 Offset 64 + OpMemberDecorate %type_TranslucentBasePass 13 Offset 76 + OpMemberDecorate %type_TranslucentBasePass 14 Offset 80 + OpMemberDecorate %type_TranslucentBasePass 15 Offset 92 + OpMemberDecorate %type_TranslucentBasePass 16 Offset 96 + OpMemberDecorate %type_TranslucentBasePass 17 Offset 100 + OpMemberDecorate %type_TranslucentBasePass 18 Offset 104 + OpMemberDecorate %type_TranslucentBasePass 19 Offset 112 + OpMemberDecorate %type_TranslucentBasePass 20 Offset 116 + OpMemberDecorate %type_TranslucentBasePass 21 Offset 120 + OpMemberDecorate %type_TranslucentBasePass 22 Offset 124 + OpMemberDecorate %type_TranslucentBasePass 23 Offset 128 + OpMemberDecorate %type_TranslucentBasePass 24 Offset 144 + OpMemberDecorate %type_TranslucentBasePass 24 MatrixStride 16 + OpMemberDecorate %type_TranslucentBasePass 24 ColMajor + OpMemberDecorate %type_TranslucentBasePass 25 Offset 400 + OpMemberDecorate %type_TranslucentBasePass 26 Offset 464 + OpMemberDecorate %type_TranslucentBasePass 27 Offset 480 + OpMemberDecorate %type_TranslucentBasePass 28 Offset 484 + OpMemberDecorate %type_TranslucentBasePass 29 Offset 488 + OpMemberDecorate %type_TranslucentBasePass 30 Offset 492 + OpMemberDecorate %type_TranslucentBasePass 31 Offset 496 + OpMemberDecorate %type_TranslucentBasePass 32 Offset 512 + OpMemberDecorate %type_TranslucentBasePass 32 MatrixStride 16 + OpMemberDecorate %type_TranslucentBasePass 32 ColMajor + OpMemberDecorate %type_TranslucentBasePass 33 Offset 576 + OpMemberDecorate %type_TranslucentBasePass 34 Offset 580 + OpMemberDecorate %type_TranslucentBasePass 35 Offset 584 + OpMemberDecorate %type_TranslucentBasePass 36 Offset 588 + OpMemberDecorate %type_TranslucentBasePass 37 Offset 592 + OpMemberDecorate %type_TranslucentBasePass 38 Offset 596 + OpMemberDecorate %type_TranslucentBasePass 39 Offset 600 + OpMemberDecorate %type_TranslucentBasePass 40 Offset 604 + OpMemberDecorate %type_TranslucentBasePass 41 Offset 608 + OpMemberDecorate %type_TranslucentBasePass 42 Offset 612 + OpMemberDecorate %type_TranslucentBasePass 43 Offset 616 + OpMemberDecorate %type_TranslucentBasePass 44 Offset 620 + OpMemberDecorate %type_TranslucentBasePass 45 Offset 624 + OpMemberDecorate %type_TranslucentBasePass 46 Offset 628 + OpMemberDecorate %type_TranslucentBasePass 47 Offset 632 + OpMemberDecorate %type_TranslucentBasePass 48 Offset 636 + OpMemberDecorate %type_TranslucentBasePass 49 Offset 640 + OpMemberDecorate %type_TranslucentBasePass 50 Offset 644 + OpMemberDecorate %type_TranslucentBasePass 51 Offset 648 + OpMemberDecorate %type_TranslucentBasePass 52 Offset 652 + OpMemberDecorate %type_TranslucentBasePass 53 Offset 656 + OpMemberDecorate %type_TranslucentBasePass 54 Offset 668 + OpMemberDecorate %type_TranslucentBasePass 55 Offset 672 + OpMemberDecorate %type_TranslucentBasePass 56 Offset 676 + OpMemberDecorate %type_TranslucentBasePass 57 Offset 680 + OpMemberDecorate %type_TranslucentBasePass 58 Offset 684 + OpMemberDecorate %type_TranslucentBasePass 59 Offset 688 + OpMemberDecorate %type_TranslucentBasePass 60 Offset 700 + OpMemberDecorate %type_TranslucentBasePass 61 Offset 704 + OpMemberDecorate %type_TranslucentBasePass 62 Offset 716 + OpMemberDecorate %type_TranslucentBasePass 63 Offset 720 + OpMemberDecorate %type_TranslucentBasePass 64 Offset 732 + OpMemberDecorate %type_TranslucentBasePass 65 Offset 736 + OpMemberDecorate %type_TranslucentBasePass 66 Offset 740 + OpMemberDecorate %type_TranslucentBasePass 67 Offset 744 + OpMemberDecorate %type_TranslucentBasePass 68 Offset 752 + OpMemberDecorate %type_TranslucentBasePass 69 Offset 756 + OpMemberDecorate %type_TranslucentBasePass 70 Offset 760 + OpMemberDecorate %type_TranslucentBasePass 71 Offset 764 + OpMemberDecorate %type_TranslucentBasePass 72 Offset 768 + OpMemberDecorate %type_TranslucentBasePass 73 Offset 784 + OpMemberDecorate %type_TranslucentBasePass 73 MatrixStride 16 + OpMemberDecorate %type_TranslucentBasePass 73 ColMajor + OpMemberDecorate %type_TranslucentBasePass 74 Offset 1040 + OpMemberDecorate %type_TranslucentBasePass 75 Offset 1104 + OpMemberDecorate %type_TranslucentBasePass 76 Offset 1120 + OpMemberDecorate %type_TranslucentBasePass 77 Offset 1124 + OpMemberDecorate %type_TranslucentBasePass 78 Offset 1128 + OpMemberDecorate %type_TranslucentBasePass 79 Offset 1132 + OpMemberDecorate %type_TranslucentBasePass 80 Offset 1136 + OpMemberDecorate %type_TranslucentBasePass 81 Offset 1152 + OpMemberDecorate %type_TranslucentBasePass 81 MatrixStride 16 + OpMemberDecorate %type_TranslucentBasePass 81 ColMajor + OpMemberDecorate %type_TranslucentBasePass 82 Offset 1216 + OpMemberDecorate %type_TranslucentBasePass 83 Offset 1220 + OpMemberDecorate %type_TranslucentBasePass 84 Offset 1224 + OpMemberDecorate %type_TranslucentBasePass 85 Offset 1228 + OpMemberDecorate %type_TranslucentBasePass 86 Offset 1232 + OpMemberDecorate %type_TranslucentBasePass 87 Offset 1236 + OpMemberDecorate %type_TranslucentBasePass 88 Offset 1240 + OpMemberDecorate %type_TranslucentBasePass 89 Offset 1244 + OpMemberDecorate %type_TranslucentBasePass 90 Offset 1248 + OpMemberDecorate %type_TranslucentBasePass 91 Offset 1252 + OpMemberDecorate %type_TranslucentBasePass 92 Offset 1256 + OpMemberDecorate %type_TranslucentBasePass 93 Offset 1260 + OpMemberDecorate %type_TranslucentBasePass 94 Offset 1264 + OpMemberDecorate %type_TranslucentBasePass 95 Offset 1268 + OpMemberDecorate %type_TranslucentBasePass 96 Offset 1272 + OpMemberDecorate %type_TranslucentBasePass 97 Offset 1276 + OpMemberDecorate %type_TranslucentBasePass 98 Offset 1280 + OpMemberDecorate %type_TranslucentBasePass 99 Offset 1296 + OpMemberDecorate %type_TranslucentBasePass 100 Offset 1300 + OpMemberDecorate %type_TranslucentBasePass 101 Offset 1304 + OpMemberDecorate %type_TranslucentBasePass 102 Offset 1308 + OpMemberDecorate %type_TranslucentBasePass 103 Offset 1312 + OpMemberDecorate %type_TranslucentBasePass 104 Offset 1316 + OpMemberDecorate %type_TranslucentBasePass 105 Offset 1320 + OpMemberDecorate %type_TranslucentBasePass 106 Offset 1324 + OpMemberDecorate %type_TranslucentBasePass 107 Offset 1328 + OpMemberDecorate %type_TranslucentBasePass 108 Offset 1332 + OpMemberDecorate %type_TranslucentBasePass 109 Offset 1336 + OpMemberDecorate %type_TranslucentBasePass 110 Offset 1340 + OpMemberDecorate %type_TranslucentBasePass 111 Offset 1344 + OpMemberDecorate %type_TranslucentBasePass 112 Offset 1348 + OpMemberDecorate %type_TranslucentBasePass 113 Offset 1352 + OpMemberDecorate %type_TranslucentBasePass 114 Offset 1356 + OpMemberDecorate %type_TranslucentBasePass 115 Offset 1360 + OpMemberDecorate %type_TranslucentBasePass 116 Offset 1376 + OpMemberDecorate %type_TranslucentBasePass 117 Offset 1392 + OpMemberDecorate %type_TranslucentBasePass 118 Offset 1408 + OpMemberDecorate %type_TranslucentBasePass 119 Offset 1424 + OpMemberDecorate %type_TranslucentBasePass 119 MatrixStride 16 + OpMemberDecorate %type_TranslucentBasePass 119 ColMajor + OpMemberDecorate %type_TranslucentBasePass 120 Offset 1472 + OpMemberDecorate %type_TranslucentBasePass 121 Offset 1484 + OpMemberDecorate %type_TranslucentBasePass 122 Offset 1488 + OpMemberDecorate %type_TranslucentBasePass 123 Offset 1496 + OpMemberDecorate %type_TranslucentBasePass 124 Offset 1500 + OpMemberDecorate %type_TranslucentBasePass 125 Offset 1504 + OpMemberDecorate %type_TranslucentBasePass 125 MatrixStride 16 + OpMemberDecorate %type_TranslucentBasePass 125 ColMajor + OpMemberDecorate %type_TranslucentBasePass 126 Offset 1632 + OpMemberDecorate %type_TranslucentBasePass 127 Offset 1664 + OpMemberDecorate %type_TranslucentBasePass 128 Offset 1672 + OpMemberDecorate %type_TranslucentBasePass 129 Offset 1676 + OpMemberDecorate %type_TranslucentBasePass 130 Offset 1680 + OpMemberDecorate %type_TranslucentBasePass 131 Offset 1684 + OpMemberDecorate %type_TranslucentBasePass 132 Offset 1688 + OpMemberDecorate %type_TranslucentBasePass 133 Offset 1692 + OpMemberDecorate %type_TranslucentBasePass 134 Offset 1696 + OpMemberDecorate %type_TranslucentBasePass 135 Offset 1712 + OpMemberDecorate %type_TranslucentBasePass 136 Offset 1728 + OpMemberDecorate %type_TranslucentBasePass 137 Offset 1744 + OpMemberDecorate %type_TranslucentBasePass 138 Offset 1760 + OpMemberDecorate %type_TranslucentBasePass 139 Offset 1776 + OpMemberDecorate %type_TranslucentBasePass 140 Offset 1792 + OpMemberDecorate %type_TranslucentBasePass 141 Offset 1800 + OpMemberDecorate %type_TranslucentBasePass 142 Offset 1804 + OpMemberDecorate %type_TranslucentBasePass 143 Offset 1808 + OpMemberDecorate %type_TranslucentBasePass 144 Offset 1820 + OpMemberDecorate %type_TranslucentBasePass 145 Offset 1824 + OpMemberDecorate %type_TranslucentBasePass 146 Offset 1828 + OpMemberDecorate %type_TranslucentBasePass 147 Offset 1832 + OpMemberDecorate %type_TranslucentBasePass 148 Offset 1836 + OpMemberDecorate %type_TranslucentBasePass 149 Offset 1840 + OpMemberDecorate %type_TranslucentBasePass 150 Offset 1844 + OpMemberDecorate %type_TranslucentBasePass 151 Offset 1848 + OpMemberDecorate %type_TranslucentBasePass 152 Offset 1852 + OpMemberDecorate %type_TranslucentBasePass 153 Offset 1856 + OpMemberDecorate %type_TranslucentBasePass 154 Offset 1860 + OpMemberDecorate %type_TranslucentBasePass 155 Offset 1864 + OpMemberDecorate %type_TranslucentBasePass 156 Offset 1868 + OpMemberDecorate %type_TranslucentBasePass 157 Offset 1872 + OpMemberDecorate %type_TranslucentBasePass 158 Offset 1876 + OpMemberDecorate %type_TranslucentBasePass 159 Offset 1880 + OpMemberDecorate %type_TranslucentBasePass 160 Offset 1884 + OpMemberDecorate %type_TranslucentBasePass 161 Offset 1888 + OpMemberDecorate %type_TranslucentBasePass 162 Offset 1892 + OpMemberDecorate %type_TranslucentBasePass 163 Offset 1896 + OpMemberDecorate %type_TranslucentBasePass 164 Offset 1900 + OpMemberDecorate %type_TranslucentBasePass 165 Offset 1904 + OpMemberDecorate %type_TranslucentBasePass 166 Offset 1908 + OpMemberDecorate %type_TranslucentBasePass 167 Offset 1912 + OpMemberDecorate %type_TranslucentBasePass 168 Offset 1916 + OpMemberDecorate %type_TranslucentBasePass 169 Offset 1920 + OpMemberDecorate %type_TranslucentBasePass 170 Offset 1924 + OpMemberDecorate %type_TranslucentBasePass 171 Offset 1928 + OpMemberDecorate %type_TranslucentBasePass 172 Offset 1932 + OpMemberDecorate %type_TranslucentBasePass 173 Offset 1936 + OpMemberDecorate %type_TranslucentBasePass 174 Offset 1940 + OpMemberDecorate %type_TranslucentBasePass 175 Offset 1944 + OpMemberDecorate %type_TranslucentBasePass 176 Offset 1948 + OpMemberDecorate %type_TranslucentBasePass 177 Offset 1952 + OpMemberDecorate %type_TranslucentBasePass 178 Offset 1956 + OpMemberDecorate %type_TranslucentBasePass 179 Offset 1960 + OpMemberDecorate %type_TranslucentBasePass 180 Offset 1964 + OpMemberDecorate %type_TranslucentBasePass 181 Offset 1968 + OpMemberDecorate %type_TranslucentBasePass 182 Offset 1972 + OpMemberDecorate %type_TranslucentBasePass 183 Offset 1976 + OpMemberDecorate %type_TranslucentBasePass 184 Offset 1980 + OpMemberDecorate %type_TranslucentBasePass 185 Offset 1984 + OpMemberDecorate %type_TranslucentBasePass 186 Offset 1988 + OpMemberDecorate %type_TranslucentBasePass 187 Offset 1992 + OpMemberDecorate %type_TranslucentBasePass 188 Offset 1996 + OpMemberDecorate %type_TranslucentBasePass 189 Offset 2000 + OpMemberDecorate %type_TranslucentBasePass 190 Offset 2004 + OpMemberDecorate %type_TranslucentBasePass 191 Offset 2008 + OpMemberDecorate %type_TranslucentBasePass 192 Offset 2012 + OpMemberDecorate %type_TranslucentBasePass 193 Offset 2016 + OpMemberDecorate %type_TranslucentBasePass 194 Offset 2020 + OpMemberDecorate %type_TranslucentBasePass 195 Offset 2024 + OpMemberDecorate %type_TranslucentBasePass 196 Offset 2028 + OpMemberDecorate %type_TranslucentBasePass 197 Offset 2032 + OpMemberDecorate %type_TranslucentBasePass 198 Offset 2036 + OpMemberDecorate %type_TranslucentBasePass 199 Offset 2040 + OpMemberDecorate %type_TranslucentBasePass 200 Offset 2044 + OpMemberDecorate %type_TranslucentBasePass 201 Offset 2048 + OpMemberDecorate %type_TranslucentBasePass 202 Offset 2052 + OpMemberDecorate %type_TranslucentBasePass 203 Offset 2056 + OpMemberDecorate %type_TranslucentBasePass 204 Offset 2060 + OpMemberDecorate %type_TranslucentBasePass 205 Offset 2064 + OpMemberDecorate %type_TranslucentBasePass 206 Offset 2068 + OpMemberDecorate %type_TranslucentBasePass 207 Offset 2072 + OpMemberDecorate %type_TranslucentBasePass 208 Offset 2076 + OpMemberDecorate %type_TranslucentBasePass 209 Offset 2080 + OpMemberDecorate %type_TranslucentBasePass 210 Offset 2084 + OpMemberDecorate %type_TranslucentBasePass 211 Offset 2088 + OpMemberDecorate %type_TranslucentBasePass 212 Offset 2092 + OpMemberDecorate %type_TranslucentBasePass 213 Offset 2096 + OpMemberDecorate %type_TranslucentBasePass 214 Offset 2100 + OpMemberDecorate %type_TranslucentBasePass 215 Offset 2104 + OpMemberDecorate %type_TranslucentBasePass 216 Offset 2108 + OpMemberDecorate %type_TranslucentBasePass 217 Offset 2112 + OpMemberDecorate %type_TranslucentBasePass 218 Offset 2116 + OpMemberDecorate %type_TranslucentBasePass 219 Offset 2120 + OpMemberDecorate %type_TranslucentBasePass 220 Offset 2124 + OpMemberDecorate %type_TranslucentBasePass 221 Offset 2128 + OpMemberDecorate %type_TranslucentBasePass 222 Offset 2132 + OpMemberDecorate %type_TranslucentBasePass 223 Offset 2136 + OpMemberDecorate %type_TranslucentBasePass 224 Offset 2140 + OpMemberDecorate %type_TranslucentBasePass 225 Offset 2144 + OpMemberDecorate %type_TranslucentBasePass 226 Offset 2160 + OpMemberDecorate %type_TranslucentBasePass 227 Offset 2176 + OpDecorate %type_TranslucentBasePass Block + OpDecorate %_arr_v4float_uint_1 ArrayStride 16 + OpMemberDecorate %type_Material 0 Offset 0 + OpMemberDecorate %type_Material 1 Offset 32 + OpDecorate %type_Material Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 + %float_0 = OpConstant %float 0 + %48 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %int_10 = OpConstant %int 10 + %int_144 = OpConstant %int 144 + %int_70 = OpConstant %int 70 + %float_1 = OpConstant %float 1 + %53 = OpConstantComposite %v3float %float_1 %float_1 %float_1 +%float_0_577000022 = OpConstant %float 0.577000022 + %55 = OpConstantComposite %v3float %float_0_577000022 %float_0_577000022 %float_0_577000022 + %56 = OpConstantComposite %v3float %float_1 %float_1 %float_0 + %57 = OpConstantComposite %v3float %float_0 %float_1 %float_1 + %float_0_5 = OpConstant %float 0.5 + %59 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %int_60 = OpConstant %int 60 + %61 = OpConstantComposite %v2float %float_0_5 %float_0_5 + %uint_26 = OpConstant %uint 26 + %uint_1 = OpConstant %uint 1 + %uint_5 = OpConstant %uint 5 + %uint_19 = OpConstant %uint 19 + %float_n0_5 = OpConstant %float -0.5 + %67 = OpConstantComposite %v2float %float_0_5 %float_n0_5 + %68 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 + %float_0_25 = OpConstant %float 0.25 + %int_31 = OpConstant %int 31 + %int_66 = OpConstant %int 66 + %int_153 = OpConstant %int 153 + %int_155 = OpConstant %int 155 +%mat3v3float = OpTypeMatrix %v3float 3 + %75 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %float_n1 = OpConstant %float -1 +%float_0_200000003 = OpConstant %float 0.200000003 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_3d_image = OpTypeImage %float 3D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_3d_image = OpTypePointer UniformConstant %type_3d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_runtimearr_v4float = OpTypeRuntimeArray %v4float +%type_StructuredBuffer_v4float = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_type_StructuredBuffer_v4float = OpTypePointer Uniform %type_StructuredBuffer_v4float + %v3int = OpTypeVector %int 3 +%_arr_mat4v4float_uint_4 = OpTypeArray %mat4v4float %uint_4 +%mat3v4float = OpTypeMatrix %v4float 3 +%_arr_mat4v4float_uint_2 = OpTypeArray %mat4v4float %uint_2 +%type_TranslucentBasePass = OpTypeStruct %uint %uint %uint %uint %v3int %uint %uint %uint %uint %uint %v3float %float %v3float %float %v3float %float %uint %uint %v2float %uint %uint %uint %uint %v4float %_arr_mat4v4float_uint_4 %_arr_v4float_uint_4 %v4float %float %uint %uint %uint %v4float %mat4v4float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %v3int %uint %uint %uint %uint %uint %v3float %float %v3float %float %v3float %float %uint %uint %v2float %uint %uint %uint %uint %v4float %_arr_mat4v4float_uint_4 %_arr_v4float_uint_4 %v4float %float %uint %uint %uint %v4float %mat4v4float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %v4float %v4float %v4float %mat3v4float %v3float %float %v2float %float %float %_arr_mat4v4float_uint_2 %_arr_v4float_uint_2 %v2float %uint %float %float %float %float %float %v4float %v4float %v4float %v4float %v4float %v4float %v2float %float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %v4float %float +%_ptr_Uniform_type_TranslucentBasePass = OpTypePointer Uniform %type_TranslucentBasePass +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%type_Material = OpTypeStruct %_arr_v4float_uint_2 %_arr_v4float_uint_1 +%_ptr_Uniform_type_Material = OpTypePointer Uniform %type_Material +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Input_bool = OpTypePointer Input %bool +%_arr_uint_uint_1 = OpTypeArray %uint %uint_1 +%_ptr_Input__arr_uint_uint_1 = OpTypePointer Input %_arr_uint_uint_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Output__arr_uint_uint_1 = OpTypePointer Output %_arr_uint_uint_1 + %void = OpTypeVoid + %94 = OpTypeFunction %void +%_ptr_Output_uint = OpTypePointer Output %uint +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %v3bool = OpTypeVector %bool 3 +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%type_sampled_image = OpTypeSampledImage %type_3d_image + %View = OpVariable %_ptr_Uniform_type_View Uniform +%View_SharedBilinearClampedSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%View_PrimitiveSceneData = OpVariable %_ptr_Uniform_type_StructuredBuffer_v4float Uniform +%TranslucentBasePass = OpVariable %_ptr_Uniform_type_TranslucentBasePass Uniform +%TranslucentBasePass_Shared_Fog_IntegratedLightScattering = OpVariable %_ptr_UniformConstant_type_3d_image UniformConstant + %Material = OpVariable %_ptr_Uniform_type_Material Uniform +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input_v4float Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input_v4float Input +%in_var_PRIMITIVE_ID = OpVariable %_ptr_Input_uint Input +%in_var_TEXCOORD7 = OpVariable %_ptr_Input_v4float Input +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%gl_FrontFacing = OpVariable %_ptr_Input_bool Input +%gl_SampleMask = OpVariable %_ptr_Input__arr_uint_uint_1 Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output +%gl_SampleMask_0 = OpVariable %_ptr_Output__arr_uint_uint_1 Output + %102 = OpConstantNull %v4float + %float_n1_5 = OpConstant %float -1.5 + %float_3 = OpConstant %float 3 + %105 = OpConstantComposite %v3float %float_n1 %float_n1_5 %float_3 +%float_12_25 = OpConstant %float 12.25 +%float_0_00200000009 = OpConstant %float 0.00200000009 + %108 = OpUndef %float + %uint_15 = OpConstant %uint 15 + %MainPS = OpFunction %void None %94 + %110 = OpLabel + %111 = OpLoad %v4float %in_var_TEXCOORD10_centroid + %112 = OpLoad %v4float %in_var_TEXCOORD11_centroid + %113 = OpLoad %uint %in_var_PRIMITIVE_ID + %114 = OpLoad %v4float %in_var_TEXCOORD7 + %115 = OpLoad %v4float %gl_FragCoord + %116 = OpLoad %_arr_uint_uint_1 %gl_SampleMask + %117 = OpCompositeExtract %uint %116 0 + %118 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_1 + %119 = OpLoad %mat4v4float %118 + %120 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_10 + %121 = OpLoad %mat4v4float %120 + %122 = OpAccessChain %_ptr_Uniform_v3float %View %int_31 + %123 = OpLoad %v3float %122 + %124 = OpAccessChain %_ptr_Uniform_v4float %View %int_66 + %125 = OpLoad %v4float %124 + %126 = OpVectorShuffle %v3float %111 %111 0 1 2 + %127 = OpVectorShuffle %v3float %112 %112 0 1 2 + %128 = OpExtInst %v3float %1 Cross %127 %126 + %129 = OpCompositeExtract %float %112 3 + %130 = OpCompositeConstruct %v3float %129 %129 %129 + %131 = OpFMul %v3float %128 %130 + %132 = OpCompositeConstruct %mat3v3float %126 %131 %127 + %133 = OpCompositeExtract %float %115 0 + %134 = OpCompositeExtract %float %115 1 + %135 = OpCompositeExtract %float %115 2 + %136 = OpCompositeConstruct %v4float %133 %134 %135 %float_1 + %137 = OpMatrixTimesVector %v4float %121 %136 + %138 = OpVectorShuffle %v3float %137 %137 0 1 2 + %139 = OpCompositeExtract %float %137 3 + %140 = OpCompositeConstruct %v3float %139 %139 %139 + %141 = OpFDiv %v3float %138 %140 + %142 = OpFSub %v3float %141 %123 + %143 = OpVectorShuffle %v3float %125 %125 0 1 2 + %144 = OpCompositeExtract %float %125 3 + %145 = OpCompositeConstruct %v3float %144 %144 %144 + %146 = OpFMul %v3float %75 %145 + %147 = OpFAdd %v3float %146 %143 + %148 = OpExtInst %v3float %1 Normalize %147 + %149 = OpMatrixTimesVector %v3float %132 %148 + %150 = OpExtInst %v3float %1 Normalize %149 + %151 = OpExtInst %float %1 Sqrt %float_12_25 + %152 = OpCompositeConstruct %v3float %151 %151 %151 + %153 = OpFDiv %v3float %105 %152 + %154 = OpDot %float %153 %150 + %155 = OpFAdd %float %float_1 %154 + %156 = OpFMul %float %155 %float_0_5 + %157 = OpFAdd %float %156 %float_0_200000003 + %158 = OpAccessChain %_ptr_Uniform_v4float %Material %int_0 %int_1 + %159 = OpLoad %v4float %158 + %160 = OpVectorShuffle %v3float %159 %159 0 1 2 + %161 = OpCompositeConstruct %v3float %157 %157 %157 + %162 = OpFMul %v3float %160 %161 + %163 = OpAccessChain %_ptr_Uniform_float %TranslucentBasePass %int_144 + %164 = OpLoad %float %163 + %165 = OpFOrdGreaterThan %bool %164 %float_0 + OpSelectionMerge %166 None + OpBranchConditional %165 %167 %166 + %167 = OpLabel + %168 = OpCompositeExtract %float %142 0 + %169 = OpCompositeExtract %float %142 1 + %170 = OpCompositeExtract %float %142 2 + %171 = OpCompositeConstruct %v4float %168 %169 %170 %float_1 + %172 = OpMatrixTimesVector %v4float %119 %171 + %173 = OpCompositeExtract %float %172 3 + %174 = OpCompositeConstruct %v2float %173 %173 + %175 = OpVectorShuffle %v2float %172 %172 0 1 + %176 = OpFDiv %v2float %175 %174 + %177 = OpVectorShuffle %v2float %176 %102 0 1 + %178 = OpFMul %v2float %177 %67 + %179 = OpFAdd %v2float %178 %61 + %180 = OpCompositeExtract %float %179 0 + %181 = OpCompositeExtract %float %179 1 + %182 = OpAccessChain %_ptr_Uniform_float %View %int_155 %int_0 + %183 = OpLoad %float %182 + %184 = OpFMul %float %173 %183 + %185 = OpAccessChain %_ptr_Uniform_float %View %int_155 %int_1 + %186 = OpLoad %float %185 + %187 = OpFAdd %float %184 %186 + %188 = OpExtInst %float %1 Log2 %187 + %189 = OpAccessChain %_ptr_Uniform_float %View %int_155 %int_2 + %190 = OpLoad %float %189 + %191 = OpFMul %float %188 %190 + %192 = OpAccessChain %_ptr_Uniform_float %View %int_153 %int_2 + %193 = OpLoad %float %192 + %194 = OpFMul %float %191 %193 + %195 = OpCompositeConstruct %v3float %180 %181 %194 + OpSelectionMerge %196 None + OpBranchConditional %165 %197 %196 + %197 = OpLabel + %198 = OpLoad %type_3d_image %TranslucentBasePass_Shared_Fog_IntegratedLightScattering + %199 = OpLoad %type_sampler %View_SharedBilinearClampedSampler + %200 = OpSampledImage %type_sampled_image %198 %199 + %201 = OpImageSampleExplicitLod %v4float %200 %195 Lod %float_0 + OpBranch %196 + %196 = OpLabel + %202 = OpPhi %v4float %68 %167 %201 %197 + %203 = OpVectorShuffle %v3float %202 %202 0 1 2 + %204 = OpVectorShuffle %v3float %114 %114 0 1 2 + %205 = OpCompositeExtract %float %202 3 + %206 = OpCompositeConstruct %v3float %205 %205 %205 + %207 = OpFMul %v3float %204 %206 + %208 = OpFAdd %v3float %203 %207 + %209 = OpCompositeExtract %float %208 0 + %210 = OpCompositeExtract %float %208 1 + %211 = OpCompositeExtract %float %208 2 + %212 = OpCompositeExtract %float %114 3 + %213 = OpFMul %float %205 %212 + %214 = OpCompositeConstruct %v4float %209 %210 %211 %213 + OpBranch %166 + %166 = OpLabel + %215 = OpPhi %v4float %114 %110 %214 %196 + %216 = OpExtInst %v3float %1 FMax %162 %48 + %217 = OpAccessChain %_ptr_Uniform_float %View %int_70 + %218 = OpLoad %float %217 + %219 = OpFOrdGreaterThan %bool %218 %float_0 + OpSelectionMerge %220 DontFlatten + OpBranchConditional %219 %221 %220 + %221 = OpLabel + %222 = OpIMul %uint %113 %uint_26 + %223 = OpIAdd %uint %222 %uint_5 + %224 = OpAccessChain %_ptr_Uniform_v4float %View_PrimitiveSceneData %int_0 %223 + %225 = OpLoad %v4float %224 + %226 = OpVectorShuffle %v3float %225 %225 0 1 2 + %227 = OpFSub %v3float %142 %226 + %228 = OpExtInst %v3float %1 FAbs %227 + %229 = OpIAdd %uint %222 %uint_19 + %230 = OpAccessChain %_ptr_Uniform_v4float %View_PrimitiveSceneData %int_0 %229 + %231 = OpLoad %v4float %230 + %232 = OpVectorShuffle %v3float %231 %231 0 1 2 + %233 = OpFAdd %v3float %232 %53 + %234 = OpFOrdGreaterThan %v3bool %228 %233 + %235 = OpAny %bool %234 + OpSelectionMerge %236 None + OpBranchConditional %235 %237 %236 + %237 = OpLabel + %238 = OpDot %float %142 %55 + %239 = OpFMul %float %238 %float_0_00200000009 + %240 = OpExtInst %float %1 Fract %239 + %241 = OpCompositeConstruct %v3float %240 %240 %240 + %242 = OpFOrdGreaterThan %v3bool %241 %59 + %243 = OpSelect %v3float %242 %53 %48 + %244 = OpExtInst %v3float %1 FMix %56 %57 %243 + OpBranch %236 + %236 = OpLabel + %245 = OpPhi %v3float %216 %221 %244 %237 + OpBranch %220 + %220 = OpLabel + %246 = OpPhi %v3float %216 %166 %245 %236 + %247 = OpCompositeExtract %float %215 3 + %248 = OpCompositeConstruct %v3float %247 %247 %247 + %249 = OpFMul %v3float %246 %248 + %250 = OpVectorShuffle %v3float %215 %215 0 1 2 + %251 = OpFAdd %v3float %249 %250 + %252 = OpCompositeExtract %float %251 0 + %253 = OpCompositeExtract %float %251 1 + %254 = OpCompositeExtract %float %251 2 + %255 = OpCompositeConstruct %v4float %252 %253 %254 %108 + %256 = OpCompositeInsert %v4float %float_1 %255 3 + %257 = OpAccessChain %_ptr_Uniform_int %View %int_60 + %258 = OpLoad %int %257 + %259 = OpSGreaterThan %bool %258 %int_1 + OpSelectionMerge %260 None + OpBranchConditional %259 %261 %262 + %262 = OpLabel + OpBranch %260 + %261 = OpLabel + %263 = OpConvertSToF %float %258 + %264 = OpFMul %float %263 %float_0_25 + %265 = OpCompositeConstruct %v4float %264 %264 %264 %264 + %266 = OpFMul %v4float %256 %265 + %267 = OpBitwiseAnd %uint %117 %uint_15 + OpBranch %260 + %260 = OpLabel + %268 = OpPhi %v4float %266 %261 %256 %262 + %269 = OpPhi %uint %267 %261 %117 %262 + OpStore %out_var_SV_Target0 %268 + %270 = OpAccessChain %_ptr_Output_uint %gl_SampleMask_0 %uint_0 + OpStore %270 %269 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag new file mode 100644 index 0000000..e656587 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/subpass-input.ios.framebuffer-fetch.asm.frag @@ -0,0 +1,589 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 130 +; Schema: 0 + OpCapability Shader + OpCapability InputAttachment + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %Main "main" %gl_FragCoord %out_var_SV_Target0 + OpExecutionMode %Main OriginUpperLeft + OpSource HLSL 600 + OpName %type_subpass_image "type.subpass.image" + OpName %gl_LastFragData "gl_LastFragData" + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_TranslatedWorldToView" + OpMemberName %type_View 3 "View_ViewToTranslatedWorld" + OpMemberName %type_View 4 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 5 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 6 "View_ViewToClip" + OpMemberName %type_View 7 "View_ViewToClipNoAA" + OpMemberName %type_View 8 "View_ClipToView" + OpMemberName %type_View 9 "View_ClipToTranslatedWorld" + OpMemberName %type_View 10 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 11 "View_ScreenToWorld" + OpMemberName %type_View 12 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 13 "View_ViewForward" + OpMemberName %type_View 14 "PrePadding_View_844" + OpMemberName %type_View 15 "View_ViewUp" + OpMemberName %type_View 16 "PrePadding_View_860" + OpMemberName %type_View 17 "View_ViewRight" + OpMemberName %type_View 18 "PrePadding_View_876" + OpMemberName %type_View 19 "View_HMDViewNoRollUp" + OpMemberName %type_View 20 "PrePadding_View_892" + OpMemberName %type_View 21 "View_HMDViewNoRollRight" + OpMemberName %type_View 22 "PrePadding_View_908" + OpMemberName %type_View 23 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 24 "View_ScreenPositionScaleBias" + OpMemberName %type_View 25 "View_WorldCameraOrigin" + OpMemberName %type_View 26 "PrePadding_View_956" + OpMemberName %type_View 27 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 28 "PrePadding_View_972" + OpMemberName %type_View 29 "View_WorldViewOrigin" + OpMemberName %type_View 30 "PrePadding_View_988" + OpMemberName %type_View 31 "View_PreViewTranslation" + OpMemberName %type_View 32 "PrePadding_View_1004" + OpMemberName %type_View 33 "View_PrevProjection" + OpMemberName %type_View 34 "View_PrevViewProj" + OpMemberName %type_View 35 "View_PrevViewRotationProj" + OpMemberName %type_View 36 "View_PrevViewToClip" + OpMemberName %type_View 37 "View_PrevClipToView" + OpMemberName %type_View 38 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 40 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 41 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 42 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 43 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 44 "PrePadding_View_1660" + OpMemberName %type_View 45 "View_PrevWorldViewOrigin" + OpMemberName %type_View 46 "PrePadding_View_1676" + OpMemberName %type_View 47 "View_PrevPreViewTranslation" + OpMemberName %type_View 48 "PrePadding_View_1692" + OpMemberName %type_View 49 "View_PrevInvViewProj" + OpMemberName %type_View 50 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 51 "View_ClipToPrevClip" + OpMemberName %type_View 52 "View_TemporalAAJitter" + OpMemberName %type_View 53 "View_GlobalClippingPlane" + OpMemberName %type_View 54 "View_FieldOfViewWideAngles" + OpMemberName %type_View 55 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 56 "View_ViewRectMin" + OpMemberName %type_View 57 "View_ViewSizeAndInvSize" + OpMemberName %type_View 58 "View_BufferSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 60 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 61 "View_PreExposure" + OpMemberName %type_View 62 "View_OneOverPreExposure" + OpMemberName %type_View 63 "PrePadding_View_2012" + OpMemberName %type_View 64 "View_DiffuseOverrideParameter" + OpMemberName %type_View 65 "View_SpecularOverrideParameter" + OpMemberName %type_View 66 "View_NormalOverrideParameter" + OpMemberName %type_View 67 "View_RoughnessOverrideParameter" + OpMemberName %type_View 68 "View_PrevFrameGameTime" + OpMemberName %type_View 69 "View_PrevFrameRealTime" + OpMemberName %type_View 70 "View_OutOfBoundsMask" + OpMemberName %type_View 71 "PrePadding_View_2084" + OpMemberName %type_View 72 "PrePadding_View_2088" + OpMemberName %type_View 73 "PrePadding_View_2092" + OpMemberName %type_View 74 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 75 "View_CullingSign" + OpMemberName %type_View 76 "View_NearPlane" + OpMemberName %type_View 77 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 78 "View_GameTime" + OpMemberName %type_View 79 "View_RealTime" + OpMemberName %type_View 80 "View_DeltaTime" + OpMemberName %type_View 81 "View_MaterialTextureMipBias" + OpMemberName %type_View 82 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 83 "View_Random" + OpMemberName %type_View 84 "View_FrameNumber" + OpMemberName %type_View 85 "View_StateFrameIndexMod8" + OpMemberName %type_View 86 "View_StateFrameIndex" + OpMemberName %type_View 87 "View_CameraCut" + OpMemberName %type_View 88 "View_UnlitViewmodeMask" + OpMemberName %type_View 89 "PrePadding_View_2164" + OpMemberName %type_View 90 "PrePadding_View_2168" + OpMemberName %type_View 91 "PrePadding_View_2172" + OpMemberName %type_View 92 "View_DirectionalLightColor" + OpMemberName %type_View 93 "View_DirectionalLightDirection" + OpMemberName %type_View 94 "PrePadding_View_2204" + OpMemberName %type_View 95 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 97 "View_TemporalAAParams" + OpMemberName %type_View 98 "View_CircleDOFParams" + OpMemberName %type_View 99 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 100 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 101 "View_DepthOfFieldScale" + OpMemberName %type_View 102 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 103 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 104 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 105 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 106 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 107 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 108 "View_GeneralPurposeTweak" + OpMemberName %type_View 109 "View_DemosaicVposOffset" + OpMemberName %type_View 110 "PrePadding_View_2348" + OpMemberName %type_View 111 "View_IndirectLightingColorScale" + OpMemberName %type_View 112 "View_HDR32bppEncodingMode" + OpMemberName %type_View 113 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 114 "View_AtmosphericFogSunPower" + OpMemberName %type_View 115 "View_AtmosphericFogPower" + OpMemberName %type_View 116 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 117 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 118 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 119 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 120 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 121 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 122 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 123 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 124 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 125 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 126 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 127 "View_AtmosphericFogSunColor" + OpMemberName %type_View 128 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 129 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 130 "View_AmbientCubemapTint" + OpMemberName %type_View 131 "View_AmbientCubemapIntensity" + OpMemberName %type_View 132 "View_SkyLightParameters" + OpMemberName %type_View 133 "PrePadding_View_2488" + OpMemberName %type_View 134 "PrePadding_View_2492" + OpMemberName %type_View 135 "View_SkyLightColor" + OpMemberName %type_View 136 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 137 "View_MobilePreviewMode" + OpMemberName %type_View 138 "View_HMDEyePaddingOffset" + OpMemberName %type_View 139 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 140 "View_ShowDecalsMask" + OpMemberName %type_View 141 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 142 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 143 "PrePadding_View_2648" + OpMemberName %type_View 144 "PrePadding_View_2652" + OpMemberName %type_View 145 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 146 "View_StereoPassIndex" + OpMemberName %type_View 147 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 148 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 149 "View_GlobalVolumeDimension" + OpMemberName %type_View 150 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 151 "View_MaxGlobalDistance" + OpMemberName %type_View 152 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 153 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 154 "PrePadding_View_2828" + OpMemberName %type_View 155 "View_VolumetricFogGridZParams" + OpMemberName %type_View 156 "PrePadding_View_2844" + OpMemberName %type_View 157 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 158 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 159 "PrePadding_View_2860" + OpMemberName %type_View 160 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 161 "PrePadding_View_2876" + OpMemberName %type_View 162 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 163 "PrePadding_View_2892" + OpMemberName %type_View 164 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 165 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 166 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 167 "View_StereoIPD" + OpMemberName %type_View 168 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 169 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_sampler "type.sampler" + OpName %type_2d_image "type.2d.image" + OpName %ShadowDepthTexture "ShadowDepthTexture" + OpName %ShadowDepthTextureSampler "ShadowDepthTextureSampler" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "SoftTransitionScale" + OpMemberName %type__Globals 1 "ShadowBufferSize" + OpMemberName %type__Globals 2 "ShadowFadeFraction" + OpMemberName %type__Globals 3 "ShadowSharpen" + OpMemberName %type__Globals 4 "LightPositionAndInvRadius" + OpMemberName %type__Globals 5 "ScreenToShadowMatrix" + OpMemberName %type__Globals 6 "ProjectionDepthBiasParameters" + OpMemberName %type__Globals 7 "ModulatedShadowColor" + OpMemberName %type__Globals 8 "ShadowTileOffsetAndSize" + OpName %_Globals "$Globals" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %Main "Main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_LastFragData InputAttachmentIndex 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_POSITION" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %gl_LastFragData DescriptorSet 0 + OpDecorate %gl_LastFragData Binding 0 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %ShadowDepthTexture DescriptorSet 0 + OpDecorate %ShadowDepthTexture Binding 0 + OpDecorate %ShadowDepthTextureSampler DescriptorSet 0 + OpDecorate %ShadowDepthTextureSampler Binding 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 1 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 14 Offset 844 + OpMemberDecorate %type_View 15 Offset 848 + OpMemberDecorate %type_View 16 Offset 860 + OpMemberDecorate %type_View 17 Offset 864 + OpMemberDecorate %type_View 18 Offset 876 + OpMemberDecorate %type_View 19 Offset 880 + OpMemberDecorate %type_View 20 Offset 892 + OpMemberDecorate %type_View 21 Offset 896 + OpMemberDecorate %type_View 22 Offset 908 + OpMemberDecorate %type_View 23 Offset 912 + OpMemberDecorate %type_View 24 Offset 928 + OpMemberDecorate %type_View 25 Offset 944 + OpMemberDecorate %type_View 26 Offset 956 + OpMemberDecorate %type_View 27 Offset 960 + OpMemberDecorate %type_View 28 Offset 972 + OpMemberDecorate %type_View 29 Offset 976 + OpMemberDecorate %type_View 30 Offset 988 + OpMemberDecorate %type_View 31 Offset 992 + OpMemberDecorate %type_View 32 Offset 1004 + OpMemberDecorate %type_View 33 Offset 1008 + OpMemberDecorate %type_View 33 MatrixStride 16 + OpMemberDecorate %type_View 33 ColMajor + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 44 Offset 1660 + OpMemberDecorate %type_View 45 Offset 1664 + OpMemberDecorate %type_View 46 Offset 1676 + OpMemberDecorate %type_View 47 Offset 1680 + OpMemberDecorate %type_View 48 Offset 1692 + OpMemberDecorate %type_View 49 Offset 1696 + OpMemberDecorate %type_View 49 MatrixStride 16 + OpMemberDecorate %type_View 49 ColMajor + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 53 Offset 1904 + OpMemberDecorate %type_View 54 Offset 1920 + OpMemberDecorate %type_View 55 Offset 1928 + OpMemberDecorate %type_View 56 Offset 1936 + OpMemberDecorate %type_View 57 Offset 1952 + OpMemberDecorate %type_View 58 Offset 1968 + OpMemberDecorate %type_View 59 Offset 1984 + OpMemberDecorate %type_View 60 Offset 2000 + OpMemberDecorate %type_View 61 Offset 2004 + OpMemberDecorate %type_View 62 Offset 2008 + OpMemberDecorate %type_View 63 Offset 2012 + OpMemberDecorate %type_View 64 Offset 2016 + OpMemberDecorate %type_View 65 Offset 2032 + OpMemberDecorate %type_View 66 Offset 2048 + OpMemberDecorate %type_View 67 Offset 2064 + OpMemberDecorate %type_View 68 Offset 2072 + OpMemberDecorate %type_View 69 Offset 2076 + OpMemberDecorate %type_View 70 Offset 2080 + OpMemberDecorate %type_View 71 Offset 2084 + OpMemberDecorate %type_View 72 Offset 2088 + OpMemberDecorate %type_View 73 Offset 2092 + OpMemberDecorate %type_View 74 Offset 2096 + OpMemberDecorate %type_View 75 Offset 2108 + OpMemberDecorate %type_View 76 Offset 2112 + OpMemberDecorate %type_View 77 Offset 2116 + OpMemberDecorate %type_View 78 Offset 2120 + OpMemberDecorate %type_View 79 Offset 2124 + OpMemberDecorate %type_View 80 Offset 2128 + OpMemberDecorate %type_View 81 Offset 2132 + OpMemberDecorate %type_View 82 Offset 2136 + OpMemberDecorate %type_View 83 Offset 2140 + OpMemberDecorate %type_View 84 Offset 2144 + OpMemberDecorate %type_View 85 Offset 2148 + OpMemberDecorate %type_View 86 Offset 2152 + OpMemberDecorate %type_View 87 Offset 2156 + OpMemberDecorate %type_View 88 Offset 2160 + OpMemberDecorate %type_View 89 Offset 2164 + OpMemberDecorate %type_View 90 Offset 2168 + OpMemberDecorate %type_View 91 Offset 2172 + OpMemberDecorate %type_View 92 Offset 2176 + OpMemberDecorate %type_View 93 Offset 2192 + OpMemberDecorate %type_View 94 Offset 2204 + OpMemberDecorate %type_View 95 Offset 2208 + OpMemberDecorate %type_View 96 Offset 2240 + OpMemberDecorate %type_View 97 Offset 2272 + OpMemberDecorate %type_View 98 Offset 2288 + OpMemberDecorate %type_View 99 Offset 2304 + OpMemberDecorate %type_View 100 Offset 2308 + OpMemberDecorate %type_View 101 Offset 2312 + OpMemberDecorate %type_View 102 Offset 2316 + OpMemberDecorate %type_View 103 Offset 2320 + OpMemberDecorate %type_View 104 Offset 2324 + OpMemberDecorate %type_View 105 Offset 2328 + OpMemberDecorate %type_View 106 Offset 2332 + OpMemberDecorate %type_View 107 Offset 2336 + OpMemberDecorate %type_View 108 Offset 2340 + OpMemberDecorate %type_View 109 Offset 2344 + OpMemberDecorate %type_View 110 Offset 2348 + OpMemberDecorate %type_View 111 Offset 2352 + OpMemberDecorate %type_View 112 Offset 2364 + OpMemberDecorate %type_View 113 Offset 2368 + OpMemberDecorate %type_View 114 Offset 2380 + OpMemberDecorate %type_View 115 Offset 2384 + OpMemberDecorate %type_View 116 Offset 2388 + OpMemberDecorate %type_View 117 Offset 2392 + OpMemberDecorate %type_View 118 Offset 2396 + OpMemberDecorate %type_View 119 Offset 2400 + OpMemberDecorate %type_View 120 Offset 2404 + OpMemberDecorate %type_View 121 Offset 2408 + OpMemberDecorate %type_View 122 Offset 2412 + OpMemberDecorate %type_View 123 Offset 2416 + OpMemberDecorate %type_View 124 Offset 2420 + OpMemberDecorate %type_View 125 Offset 2424 + OpMemberDecorate %type_View 126 Offset 2428 + OpMemberDecorate %type_View 127 Offset 2432 + OpMemberDecorate %type_View 128 Offset 2448 + OpMemberDecorate %type_View 129 Offset 2460 + OpMemberDecorate %type_View 130 Offset 2464 + OpMemberDecorate %type_View 131 Offset 2480 + OpMemberDecorate %type_View 132 Offset 2484 + OpMemberDecorate %type_View 133 Offset 2488 + OpMemberDecorate %type_View 134 Offset 2492 + OpMemberDecorate %type_View 135 Offset 2496 + OpMemberDecorate %type_View 136 Offset 2512 + OpMemberDecorate %type_View 137 Offset 2624 + OpMemberDecorate %type_View 138 Offset 2628 + OpMemberDecorate %type_View 139 Offset 2632 + OpMemberDecorate %type_View 140 Offset 2636 + OpMemberDecorate %type_View 141 Offset 2640 + OpMemberDecorate %type_View 142 Offset 2644 + OpMemberDecorate %type_View 143 Offset 2648 + OpMemberDecorate %type_View 144 Offset 2652 + OpMemberDecorate %type_View 145 Offset 2656 + OpMemberDecorate %type_View 146 Offset 2668 + OpMemberDecorate %type_View 147 Offset 2672 + OpMemberDecorate %type_View 148 Offset 2736 + OpMemberDecorate %type_View 149 Offset 2800 + OpMemberDecorate %type_View 150 Offset 2804 + OpMemberDecorate %type_View 151 Offset 2808 + OpMemberDecorate %type_View 152 Offset 2812 + OpMemberDecorate %type_View 153 Offset 2816 + OpMemberDecorate %type_View 154 Offset 2828 + OpMemberDecorate %type_View 155 Offset 2832 + OpMemberDecorate %type_View 156 Offset 2844 + OpMemberDecorate %type_View 157 Offset 2848 + OpMemberDecorate %type_View 158 Offset 2856 + OpMemberDecorate %type_View 159 Offset 2860 + OpMemberDecorate %type_View 160 Offset 2864 + OpMemberDecorate %type_View 161 Offset 2876 + OpMemberDecorate %type_View 162 Offset 2880 + OpMemberDecorate %type_View 163 Offset 2892 + OpMemberDecorate %type_View 164 Offset 2896 + OpMemberDecorate %type_View 165 Offset 2908 + OpMemberDecorate %type_View 166 Offset 2912 + OpMemberDecorate %type_View 167 Offset 2924 + OpMemberDecorate %type_View 168 Offset 2928 + OpMemberDecorate %type_View 169 Offset 2932 + OpDecorate %type_View Block + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 32 + OpMemberDecorate %type__Globals 3 Offset 36 + OpMemberDecorate %type__Globals 4 Offset 48 + OpMemberDecorate %type__Globals 5 Offset 64 + OpMemberDecorate %type__Globals 5 MatrixStride 16 + OpMemberDecorate %type__Globals 5 ColMajor + OpMemberDecorate %type__Globals 6 Offset 128 + OpMemberDecorate %type__Globals 7 Offset 144 + OpMemberDecorate %type__Globals 8 Offset 160 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %float_1 = OpConstant %float 1 + %int_58 = OpConstant %int 58 + %int_24 = OpConstant %int 24 + %int_5 = OpConstant %int 5 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 + %int_8 = OpConstant %int 8 +%float_0_999989986 = OpConstant %float 0.999989986 + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %int_7 = OpConstant %int 7 + %float_0_5 = OpConstant %float 0.5 + %41 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %v2int = OpTypeVector %int 2 + %43 = OpConstantComposite %v2int %int_0 %int_0 + %44 = OpConstantComposite %v3float %float_0 %float_0 %float_0 +%type_subpass_image = OpTypeImage %float SubpassData 2 0 0 2 Unknown +%_ptr_UniformConstant_type_subpass_image = OpTypePointer UniformConstant %type_subpass_image +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type__Globals = OpTypeStruct %v3float %v4float %float %float %v4float %mat4v4float %v2float %v4float %v4float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %53 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%gl_LastFragData = OpVariable %_ptr_UniformConstant_type_subpass_image UniformConstant + %View = OpVariable %_ptr_Uniform_type_View Uniform +%ShadowDepthTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%ShadowDepthTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %57 = OpConstantNull %v4float + %58 = OpUndef %v4float + %Main = OpFunction %void None %53 + %59 = OpLabel + %60 = OpLoad %v4float %gl_FragCoord + %61 = OpVectorShuffle %v2float %60 %60 0 1 + %62 = OpAccessChain %_ptr_Uniform_v4float %View %int_58 + %63 = OpLoad %v4float %62 + %64 = OpVectorShuffle %v2float %63 %63 2 3 + %65 = OpFMul %v2float %61 %64 + %66 = OpLoad %type_subpass_image %gl_LastFragData + %67 = OpImageRead %v4float %66 %43 None + %68 = OpCompositeExtract %float %67 3 + %69 = OpAccessChain %_ptr_Uniform_v4float %View %int_24 + %70 = OpLoad %v4float %69 + %71 = OpVectorShuffle %v2float %70 %70 3 2 + %72 = OpFSub %v2float %65 %71 + %73 = OpVectorShuffle %v2float %70 %70 0 1 + %74 = OpFDiv %v2float %72 %73 + %75 = OpCompositeConstruct %v2float %68 %68 + %76 = OpFMul %v2float %74 %75 + %77 = OpCompositeExtract %float %76 0 + %78 = OpCompositeExtract %float %76 1 + %79 = OpCompositeConstruct %v4float %77 %78 %68 %float_1 + %80 = OpAccessChain %_ptr_Uniform_mat4v4float %_Globals %int_5 + %81 = OpLoad %mat4v4float %80 + %82 = OpMatrixTimesVector %v4float %81 %79 + %83 = OpCompositeExtract %float %82 2 + %84 = OpCompositeExtract %float %82 3 + %85 = OpCompositeConstruct %v3float %84 %84 %84 + %86 = OpVectorShuffle %v3float %82 %82 0 1 2 + %87 = OpFDiv %v3float %86 %85 + %88 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_8 + %89 = OpLoad %v4float %88 + %90 = OpVectorShuffle %v2float %89 %89 2 3 + %91 = OpVectorShuffle %v2float %87 %57 0 1 + %92 = OpFMul %v2float %91 %90 + %93 = OpVectorShuffle %v2float %89 %89 0 1 + %94 = OpVectorShuffle %v2float %92 %57 0 1 + %95 = OpFAdd %v2float %94 %93 + %96 = OpExtInst %float %1 FMin %83 %float_0_999989986 + %97 = OpLoad %type_2d_image %ShadowDepthTexture + %98 = OpLoad %type_sampler %ShadowDepthTextureSampler + %99 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_2 + %100 = OpLoad %float %99 + %101 = OpVectorShuffle %v2float %95 %57 0 1 + %102 = OpSampledImage %type_sampled_image %97 %98 + %103 = OpImageSampleExplicitLod %v4float %102 %101 Lod %float_0 + %104 = OpVectorShuffle %v3float %103 %103 0 0 0 + %105 = OpFMul %float %96 %100 + %106 = OpFSub %float %105 %float_1 + %107 = OpCompositeConstruct %v3float %100 %100 %100 + %108 = OpFMul %v3float %104 %107 + %109 = OpCompositeConstruct %v3float %106 %106 %106 + %110 = OpFSub %v3float %108 %109 + %111 = OpExtInst %v3float %1 FClamp %110 %44 %41 + %112 = OpCompositeExtract %float %111 0 + %113 = OpFSub %float %112 %float_0_5 + %114 = OpAccessChain %_ptr_Uniform_float %_Globals %int_3 + %115 = OpLoad %float %114 + %116 = OpFMul %float %113 %115 + %117 = OpFAdd %float %116 %float_0_5 + %118 = OpExtInst %float %1 FClamp %117 %float_0 %float_1 + %119 = OpFMul %float %118 %118 + %120 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 + %121 = OpLoad %float %120 + %122 = OpExtInst %float %1 FMix %float_1 %119 %121 + %123 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_7 + %124 = OpLoad %v4float %123 + %125 = OpVectorShuffle %v3float %124 %124 0 1 2 + %126 = OpCompositeConstruct %v3float %122 %122 %122 + %127 = OpExtInst %v3float %1 FMix %125 %41 %126 + %128 = OpVectorShuffle %v4float %58 %127 4 5 6 3 + %129 = OpCompositeInsert %v4float %float_0 %128 3 + OpStore %out_var_SV_Target0 %129 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag new file mode 100644 index 0000000..e656587 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/subpass-input.msl23.framebuffer-fetch.asm.frag @@ -0,0 +1,589 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 130 +; Schema: 0 + OpCapability Shader + OpCapability InputAttachment + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %Main "main" %gl_FragCoord %out_var_SV_Target0 + OpExecutionMode %Main OriginUpperLeft + OpSource HLSL 600 + OpName %type_subpass_image "type.subpass.image" + OpName %gl_LastFragData "gl_LastFragData" + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_TranslatedWorldToView" + OpMemberName %type_View 3 "View_ViewToTranslatedWorld" + OpMemberName %type_View 4 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 5 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 6 "View_ViewToClip" + OpMemberName %type_View 7 "View_ViewToClipNoAA" + OpMemberName %type_View 8 "View_ClipToView" + OpMemberName %type_View 9 "View_ClipToTranslatedWorld" + OpMemberName %type_View 10 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 11 "View_ScreenToWorld" + OpMemberName %type_View 12 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 13 "View_ViewForward" + OpMemberName %type_View 14 "PrePadding_View_844" + OpMemberName %type_View 15 "View_ViewUp" + OpMemberName %type_View 16 "PrePadding_View_860" + OpMemberName %type_View 17 "View_ViewRight" + OpMemberName %type_View 18 "PrePadding_View_876" + OpMemberName %type_View 19 "View_HMDViewNoRollUp" + OpMemberName %type_View 20 "PrePadding_View_892" + OpMemberName %type_View 21 "View_HMDViewNoRollRight" + OpMemberName %type_View 22 "PrePadding_View_908" + OpMemberName %type_View 23 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 24 "View_ScreenPositionScaleBias" + OpMemberName %type_View 25 "View_WorldCameraOrigin" + OpMemberName %type_View 26 "PrePadding_View_956" + OpMemberName %type_View 27 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 28 "PrePadding_View_972" + OpMemberName %type_View 29 "View_WorldViewOrigin" + OpMemberName %type_View 30 "PrePadding_View_988" + OpMemberName %type_View 31 "View_PreViewTranslation" + OpMemberName %type_View 32 "PrePadding_View_1004" + OpMemberName %type_View 33 "View_PrevProjection" + OpMemberName %type_View 34 "View_PrevViewProj" + OpMemberName %type_View 35 "View_PrevViewRotationProj" + OpMemberName %type_View 36 "View_PrevViewToClip" + OpMemberName %type_View 37 "View_PrevClipToView" + OpMemberName %type_View 38 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 40 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 41 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 42 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 43 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 44 "PrePadding_View_1660" + OpMemberName %type_View 45 "View_PrevWorldViewOrigin" + OpMemberName %type_View 46 "PrePadding_View_1676" + OpMemberName %type_View 47 "View_PrevPreViewTranslation" + OpMemberName %type_View 48 "PrePadding_View_1692" + OpMemberName %type_View 49 "View_PrevInvViewProj" + OpMemberName %type_View 50 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 51 "View_ClipToPrevClip" + OpMemberName %type_View 52 "View_TemporalAAJitter" + OpMemberName %type_View 53 "View_GlobalClippingPlane" + OpMemberName %type_View 54 "View_FieldOfViewWideAngles" + OpMemberName %type_View 55 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 56 "View_ViewRectMin" + OpMemberName %type_View 57 "View_ViewSizeAndInvSize" + OpMemberName %type_View 58 "View_BufferSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 60 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 61 "View_PreExposure" + OpMemberName %type_View 62 "View_OneOverPreExposure" + OpMemberName %type_View 63 "PrePadding_View_2012" + OpMemberName %type_View 64 "View_DiffuseOverrideParameter" + OpMemberName %type_View 65 "View_SpecularOverrideParameter" + OpMemberName %type_View 66 "View_NormalOverrideParameter" + OpMemberName %type_View 67 "View_RoughnessOverrideParameter" + OpMemberName %type_View 68 "View_PrevFrameGameTime" + OpMemberName %type_View 69 "View_PrevFrameRealTime" + OpMemberName %type_View 70 "View_OutOfBoundsMask" + OpMemberName %type_View 71 "PrePadding_View_2084" + OpMemberName %type_View 72 "PrePadding_View_2088" + OpMemberName %type_View 73 "PrePadding_View_2092" + OpMemberName %type_View 74 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 75 "View_CullingSign" + OpMemberName %type_View 76 "View_NearPlane" + OpMemberName %type_View 77 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 78 "View_GameTime" + OpMemberName %type_View 79 "View_RealTime" + OpMemberName %type_View 80 "View_DeltaTime" + OpMemberName %type_View 81 "View_MaterialTextureMipBias" + OpMemberName %type_View 82 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 83 "View_Random" + OpMemberName %type_View 84 "View_FrameNumber" + OpMemberName %type_View 85 "View_StateFrameIndexMod8" + OpMemberName %type_View 86 "View_StateFrameIndex" + OpMemberName %type_View 87 "View_CameraCut" + OpMemberName %type_View 88 "View_UnlitViewmodeMask" + OpMemberName %type_View 89 "PrePadding_View_2164" + OpMemberName %type_View 90 "PrePadding_View_2168" + OpMemberName %type_View 91 "PrePadding_View_2172" + OpMemberName %type_View 92 "View_DirectionalLightColor" + OpMemberName %type_View 93 "View_DirectionalLightDirection" + OpMemberName %type_View 94 "PrePadding_View_2204" + OpMemberName %type_View 95 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 97 "View_TemporalAAParams" + OpMemberName %type_View 98 "View_CircleDOFParams" + OpMemberName %type_View 99 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 100 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 101 "View_DepthOfFieldScale" + OpMemberName %type_View 102 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 103 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 104 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 105 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 106 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 107 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 108 "View_GeneralPurposeTweak" + OpMemberName %type_View 109 "View_DemosaicVposOffset" + OpMemberName %type_View 110 "PrePadding_View_2348" + OpMemberName %type_View 111 "View_IndirectLightingColorScale" + OpMemberName %type_View 112 "View_HDR32bppEncodingMode" + OpMemberName %type_View 113 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 114 "View_AtmosphericFogSunPower" + OpMemberName %type_View 115 "View_AtmosphericFogPower" + OpMemberName %type_View 116 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 117 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 118 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 119 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 120 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 121 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 122 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 123 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 124 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 125 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 126 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 127 "View_AtmosphericFogSunColor" + OpMemberName %type_View 128 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 129 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 130 "View_AmbientCubemapTint" + OpMemberName %type_View 131 "View_AmbientCubemapIntensity" + OpMemberName %type_View 132 "View_SkyLightParameters" + OpMemberName %type_View 133 "PrePadding_View_2488" + OpMemberName %type_View 134 "PrePadding_View_2492" + OpMemberName %type_View 135 "View_SkyLightColor" + OpMemberName %type_View 136 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 137 "View_MobilePreviewMode" + OpMemberName %type_View 138 "View_HMDEyePaddingOffset" + OpMemberName %type_View 139 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 140 "View_ShowDecalsMask" + OpMemberName %type_View 141 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 142 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 143 "PrePadding_View_2648" + OpMemberName %type_View 144 "PrePadding_View_2652" + OpMemberName %type_View 145 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 146 "View_StereoPassIndex" + OpMemberName %type_View 147 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 148 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 149 "View_GlobalVolumeDimension" + OpMemberName %type_View 150 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 151 "View_MaxGlobalDistance" + OpMemberName %type_View 152 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 153 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 154 "PrePadding_View_2828" + OpMemberName %type_View 155 "View_VolumetricFogGridZParams" + OpMemberName %type_View 156 "PrePadding_View_2844" + OpMemberName %type_View 157 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 158 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 159 "PrePadding_View_2860" + OpMemberName %type_View 160 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 161 "PrePadding_View_2876" + OpMemberName %type_View 162 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 163 "PrePadding_View_2892" + OpMemberName %type_View 164 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 165 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 166 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 167 "View_StereoIPD" + OpMemberName %type_View 168 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 169 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_sampler "type.sampler" + OpName %type_2d_image "type.2d.image" + OpName %ShadowDepthTexture "ShadowDepthTexture" + OpName %ShadowDepthTextureSampler "ShadowDepthTextureSampler" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "SoftTransitionScale" + OpMemberName %type__Globals 1 "ShadowBufferSize" + OpMemberName %type__Globals 2 "ShadowFadeFraction" + OpMemberName %type__Globals 3 "ShadowSharpen" + OpMemberName %type__Globals 4 "LightPositionAndInvRadius" + OpMemberName %type__Globals 5 "ScreenToShadowMatrix" + OpMemberName %type__Globals 6 "ProjectionDepthBiasParameters" + OpMemberName %type__Globals 7 "ModulatedShadowColor" + OpMemberName %type__Globals 8 "ShadowTileOffsetAndSize" + OpName %_Globals "$Globals" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %Main "Main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_LastFragData InputAttachmentIndex 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_POSITION" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %gl_LastFragData DescriptorSet 0 + OpDecorate %gl_LastFragData Binding 0 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %ShadowDepthTexture DescriptorSet 0 + OpDecorate %ShadowDepthTexture Binding 0 + OpDecorate %ShadowDepthTextureSampler DescriptorSet 0 + OpDecorate %ShadowDepthTextureSampler Binding 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 1 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 14 Offset 844 + OpMemberDecorate %type_View 15 Offset 848 + OpMemberDecorate %type_View 16 Offset 860 + OpMemberDecorate %type_View 17 Offset 864 + OpMemberDecorate %type_View 18 Offset 876 + OpMemberDecorate %type_View 19 Offset 880 + OpMemberDecorate %type_View 20 Offset 892 + OpMemberDecorate %type_View 21 Offset 896 + OpMemberDecorate %type_View 22 Offset 908 + OpMemberDecorate %type_View 23 Offset 912 + OpMemberDecorate %type_View 24 Offset 928 + OpMemberDecorate %type_View 25 Offset 944 + OpMemberDecorate %type_View 26 Offset 956 + OpMemberDecorate %type_View 27 Offset 960 + OpMemberDecorate %type_View 28 Offset 972 + OpMemberDecorate %type_View 29 Offset 976 + OpMemberDecorate %type_View 30 Offset 988 + OpMemberDecorate %type_View 31 Offset 992 + OpMemberDecorate %type_View 32 Offset 1004 + OpMemberDecorate %type_View 33 Offset 1008 + OpMemberDecorate %type_View 33 MatrixStride 16 + OpMemberDecorate %type_View 33 ColMajor + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 44 Offset 1660 + OpMemberDecorate %type_View 45 Offset 1664 + OpMemberDecorate %type_View 46 Offset 1676 + OpMemberDecorate %type_View 47 Offset 1680 + OpMemberDecorate %type_View 48 Offset 1692 + OpMemberDecorate %type_View 49 Offset 1696 + OpMemberDecorate %type_View 49 MatrixStride 16 + OpMemberDecorate %type_View 49 ColMajor + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 53 Offset 1904 + OpMemberDecorate %type_View 54 Offset 1920 + OpMemberDecorate %type_View 55 Offset 1928 + OpMemberDecorate %type_View 56 Offset 1936 + OpMemberDecorate %type_View 57 Offset 1952 + OpMemberDecorate %type_View 58 Offset 1968 + OpMemberDecorate %type_View 59 Offset 1984 + OpMemberDecorate %type_View 60 Offset 2000 + OpMemberDecorate %type_View 61 Offset 2004 + OpMemberDecorate %type_View 62 Offset 2008 + OpMemberDecorate %type_View 63 Offset 2012 + OpMemberDecorate %type_View 64 Offset 2016 + OpMemberDecorate %type_View 65 Offset 2032 + OpMemberDecorate %type_View 66 Offset 2048 + OpMemberDecorate %type_View 67 Offset 2064 + OpMemberDecorate %type_View 68 Offset 2072 + OpMemberDecorate %type_View 69 Offset 2076 + OpMemberDecorate %type_View 70 Offset 2080 + OpMemberDecorate %type_View 71 Offset 2084 + OpMemberDecorate %type_View 72 Offset 2088 + OpMemberDecorate %type_View 73 Offset 2092 + OpMemberDecorate %type_View 74 Offset 2096 + OpMemberDecorate %type_View 75 Offset 2108 + OpMemberDecorate %type_View 76 Offset 2112 + OpMemberDecorate %type_View 77 Offset 2116 + OpMemberDecorate %type_View 78 Offset 2120 + OpMemberDecorate %type_View 79 Offset 2124 + OpMemberDecorate %type_View 80 Offset 2128 + OpMemberDecorate %type_View 81 Offset 2132 + OpMemberDecorate %type_View 82 Offset 2136 + OpMemberDecorate %type_View 83 Offset 2140 + OpMemberDecorate %type_View 84 Offset 2144 + OpMemberDecorate %type_View 85 Offset 2148 + OpMemberDecorate %type_View 86 Offset 2152 + OpMemberDecorate %type_View 87 Offset 2156 + OpMemberDecorate %type_View 88 Offset 2160 + OpMemberDecorate %type_View 89 Offset 2164 + OpMemberDecorate %type_View 90 Offset 2168 + OpMemberDecorate %type_View 91 Offset 2172 + OpMemberDecorate %type_View 92 Offset 2176 + OpMemberDecorate %type_View 93 Offset 2192 + OpMemberDecorate %type_View 94 Offset 2204 + OpMemberDecorate %type_View 95 Offset 2208 + OpMemberDecorate %type_View 96 Offset 2240 + OpMemberDecorate %type_View 97 Offset 2272 + OpMemberDecorate %type_View 98 Offset 2288 + OpMemberDecorate %type_View 99 Offset 2304 + OpMemberDecorate %type_View 100 Offset 2308 + OpMemberDecorate %type_View 101 Offset 2312 + OpMemberDecorate %type_View 102 Offset 2316 + OpMemberDecorate %type_View 103 Offset 2320 + OpMemberDecorate %type_View 104 Offset 2324 + OpMemberDecorate %type_View 105 Offset 2328 + OpMemberDecorate %type_View 106 Offset 2332 + OpMemberDecorate %type_View 107 Offset 2336 + OpMemberDecorate %type_View 108 Offset 2340 + OpMemberDecorate %type_View 109 Offset 2344 + OpMemberDecorate %type_View 110 Offset 2348 + OpMemberDecorate %type_View 111 Offset 2352 + OpMemberDecorate %type_View 112 Offset 2364 + OpMemberDecorate %type_View 113 Offset 2368 + OpMemberDecorate %type_View 114 Offset 2380 + OpMemberDecorate %type_View 115 Offset 2384 + OpMemberDecorate %type_View 116 Offset 2388 + OpMemberDecorate %type_View 117 Offset 2392 + OpMemberDecorate %type_View 118 Offset 2396 + OpMemberDecorate %type_View 119 Offset 2400 + OpMemberDecorate %type_View 120 Offset 2404 + OpMemberDecorate %type_View 121 Offset 2408 + OpMemberDecorate %type_View 122 Offset 2412 + OpMemberDecorate %type_View 123 Offset 2416 + OpMemberDecorate %type_View 124 Offset 2420 + OpMemberDecorate %type_View 125 Offset 2424 + OpMemberDecorate %type_View 126 Offset 2428 + OpMemberDecorate %type_View 127 Offset 2432 + OpMemberDecorate %type_View 128 Offset 2448 + OpMemberDecorate %type_View 129 Offset 2460 + OpMemberDecorate %type_View 130 Offset 2464 + OpMemberDecorate %type_View 131 Offset 2480 + OpMemberDecorate %type_View 132 Offset 2484 + OpMemberDecorate %type_View 133 Offset 2488 + OpMemberDecorate %type_View 134 Offset 2492 + OpMemberDecorate %type_View 135 Offset 2496 + OpMemberDecorate %type_View 136 Offset 2512 + OpMemberDecorate %type_View 137 Offset 2624 + OpMemberDecorate %type_View 138 Offset 2628 + OpMemberDecorate %type_View 139 Offset 2632 + OpMemberDecorate %type_View 140 Offset 2636 + OpMemberDecorate %type_View 141 Offset 2640 + OpMemberDecorate %type_View 142 Offset 2644 + OpMemberDecorate %type_View 143 Offset 2648 + OpMemberDecorate %type_View 144 Offset 2652 + OpMemberDecorate %type_View 145 Offset 2656 + OpMemberDecorate %type_View 146 Offset 2668 + OpMemberDecorate %type_View 147 Offset 2672 + OpMemberDecorate %type_View 148 Offset 2736 + OpMemberDecorate %type_View 149 Offset 2800 + OpMemberDecorate %type_View 150 Offset 2804 + OpMemberDecorate %type_View 151 Offset 2808 + OpMemberDecorate %type_View 152 Offset 2812 + OpMemberDecorate %type_View 153 Offset 2816 + OpMemberDecorate %type_View 154 Offset 2828 + OpMemberDecorate %type_View 155 Offset 2832 + OpMemberDecorate %type_View 156 Offset 2844 + OpMemberDecorate %type_View 157 Offset 2848 + OpMemberDecorate %type_View 158 Offset 2856 + OpMemberDecorate %type_View 159 Offset 2860 + OpMemberDecorate %type_View 160 Offset 2864 + OpMemberDecorate %type_View 161 Offset 2876 + OpMemberDecorate %type_View 162 Offset 2880 + OpMemberDecorate %type_View 163 Offset 2892 + OpMemberDecorate %type_View 164 Offset 2896 + OpMemberDecorate %type_View 165 Offset 2908 + OpMemberDecorate %type_View 166 Offset 2912 + OpMemberDecorate %type_View 167 Offset 2924 + OpMemberDecorate %type_View 168 Offset 2928 + OpMemberDecorate %type_View 169 Offset 2932 + OpDecorate %type_View Block + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 32 + OpMemberDecorate %type__Globals 3 Offset 36 + OpMemberDecorate %type__Globals 4 Offset 48 + OpMemberDecorate %type__Globals 5 Offset 64 + OpMemberDecorate %type__Globals 5 MatrixStride 16 + OpMemberDecorate %type__Globals 5 ColMajor + OpMemberDecorate %type__Globals 6 Offset 128 + OpMemberDecorate %type__Globals 7 Offset 144 + OpMemberDecorate %type__Globals 8 Offset 160 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %float_1 = OpConstant %float 1 + %int_58 = OpConstant %int 58 + %int_24 = OpConstant %int 24 + %int_5 = OpConstant %int 5 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 + %int_8 = OpConstant %int 8 +%float_0_999989986 = OpConstant %float 0.999989986 + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %int_7 = OpConstant %int 7 + %float_0_5 = OpConstant %float 0.5 + %41 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %v2int = OpTypeVector %int 2 + %43 = OpConstantComposite %v2int %int_0 %int_0 + %44 = OpConstantComposite %v3float %float_0 %float_0 %float_0 +%type_subpass_image = OpTypeImage %float SubpassData 2 0 0 2 Unknown +%_ptr_UniformConstant_type_subpass_image = OpTypePointer UniformConstant %type_subpass_image +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type__Globals = OpTypeStruct %v3float %v4float %float %float %v4float %mat4v4float %v2float %v4float %v4float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %53 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%gl_LastFragData = OpVariable %_ptr_UniformConstant_type_subpass_image UniformConstant + %View = OpVariable %_ptr_Uniform_type_View Uniform +%ShadowDepthTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%ShadowDepthTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %57 = OpConstantNull %v4float + %58 = OpUndef %v4float + %Main = OpFunction %void None %53 + %59 = OpLabel + %60 = OpLoad %v4float %gl_FragCoord + %61 = OpVectorShuffle %v2float %60 %60 0 1 + %62 = OpAccessChain %_ptr_Uniform_v4float %View %int_58 + %63 = OpLoad %v4float %62 + %64 = OpVectorShuffle %v2float %63 %63 2 3 + %65 = OpFMul %v2float %61 %64 + %66 = OpLoad %type_subpass_image %gl_LastFragData + %67 = OpImageRead %v4float %66 %43 None + %68 = OpCompositeExtract %float %67 3 + %69 = OpAccessChain %_ptr_Uniform_v4float %View %int_24 + %70 = OpLoad %v4float %69 + %71 = OpVectorShuffle %v2float %70 %70 3 2 + %72 = OpFSub %v2float %65 %71 + %73 = OpVectorShuffle %v2float %70 %70 0 1 + %74 = OpFDiv %v2float %72 %73 + %75 = OpCompositeConstruct %v2float %68 %68 + %76 = OpFMul %v2float %74 %75 + %77 = OpCompositeExtract %float %76 0 + %78 = OpCompositeExtract %float %76 1 + %79 = OpCompositeConstruct %v4float %77 %78 %68 %float_1 + %80 = OpAccessChain %_ptr_Uniform_mat4v4float %_Globals %int_5 + %81 = OpLoad %mat4v4float %80 + %82 = OpMatrixTimesVector %v4float %81 %79 + %83 = OpCompositeExtract %float %82 2 + %84 = OpCompositeExtract %float %82 3 + %85 = OpCompositeConstruct %v3float %84 %84 %84 + %86 = OpVectorShuffle %v3float %82 %82 0 1 2 + %87 = OpFDiv %v3float %86 %85 + %88 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_8 + %89 = OpLoad %v4float %88 + %90 = OpVectorShuffle %v2float %89 %89 2 3 + %91 = OpVectorShuffle %v2float %87 %57 0 1 + %92 = OpFMul %v2float %91 %90 + %93 = OpVectorShuffle %v2float %89 %89 0 1 + %94 = OpVectorShuffle %v2float %92 %57 0 1 + %95 = OpFAdd %v2float %94 %93 + %96 = OpExtInst %float %1 FMin %83 %float_0_999989986 + %97 = OpLoad %type_2d_image %ShadowDepthTexture + %98 = OpLoad %type_sampler %ShadowDepthTextureSampler + %99 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 %int_2 + %100 = OpLoad %float %99 + %101 = OpVectorShuffle %v2float %95 %57 0 1 + %102 = OpSampledImage %type_sampled_image %97 %98 + %103 = OpImageSampleExplicitLod %v4float %102 %101 Lod %float_0 + %104 = OpVectorShuffle %v3float %103 %103 0 0 0 + %105 = OpFMul %float %96 %100 + %106 = OpFSub %float %105 %float_1 + %107 = OpCompositeConstruct %v3float %100 %100 %100 + %108 = OpFMul %v3float %104 %107 + %109 = OpCompositeConstruct %v3float %106 %106 %106 + %110 = OpFSub %v3float %108 %109 + %111 = OpExtInst %v3float %1 FClamp %110 %44 %41 + %112 = OpCompositeExtract %float %111 0 + %113 = OpFSub %float %112 %float_0_5 + %114 = OpAccessChain %_ptr_Uniform_float %_Globals %int_3 + %115 = OpLoad %float %114 + %116 = OpFMul %float %113 %115 + %117 = OpFAdd %float %116 %float_0_5 + %118 = OpExtInst %float %1 FClamp %117 %float_0 %float_1 + %119 = OpFMul %float %118 %118 + %120 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 + %121 = OpLoad %float %120 + %122 = OpExtInst %float %1 FMix %float_1 %119 %121 + %123 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_7 + %124 = OpLoad %v4float %123 + %125 = OpVectorShuffle %v3float %124 %124 0 1 2 + %126 = OpCompositeConstruct %v3float %122 %122 %122 + %127 = OpExtInst %v3float %1 FMix %125 %41 %126 + %128 = OpVectorShuffle %v4float %58 %127 4 5 6 3 + %129 = OpCompositeInsert %v4float %float_0 %128 3 + OpStore %out_var_SV_Target0 %129 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag new file mode 100644 index 0000000..270a197 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.argument.msl2.frag @@ -0,0 +1,242 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 180 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %ShadowObjectCullPS "main" %in_var_TEXCOORD0 %gl_FragCoord %out_var_SV_Target0 + OpExecutionMode %ShadowObjectCullPS OriginUpperLeft + OpSource HLSL 600 + OpName %type_StructuredBuffer_v4float "type.StructuredBuffer.v4float" + OpName %CulledObjectBoxBounds "CulledObjectBoxBounds" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "ShadowTileListGroupSize" + OpName %_Globals "$Globals" + OpName %type_buffer_image "type.buffer.image" + OpName %RWShadowTileNumCulledObjects "RWShadowTileNumCulledObjects" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %ShadowObjectCullPS "ShadowObjectCullPS" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorate %in_var_TEXCOORD0 Flat + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_POSITION" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %CulledObjectBoxBounds DescriptorSet 0 + OpDecorate %CulledObjectBoxBounds Binding 1 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 2 + OpDecorate %RWShadowTileNumCulledObjects DescriptorSet 0 + OpDecorate %RWShadowTileNumCulledObjects Binding 0 + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_v4float 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_v4float 0 NonWritable + OpDecorate %type_StructuredBuffer_v4float BufferBlock + OpMemberDecorate %type__Globals 0 Offset 0 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_4 = OpConstant %uint 4 + %float_0 = OpConstant %float 0 + %22 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %float_2 = OpConstant %float 2 + %27 = OpConstantComposite %v2float %float_2 %float_2 + %float_1 = OpConstant %float 1 + %29 = OpConstantComposite %v2float %float_1 %float_1 +%float_n1000 = OpConstant %float -1000 + %int_2 = OpConstant %int 2 + %float_0_5 = OpConstant %float 0.5 + %33 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 +%float_500000 = OpConstant %float 500000 + %35 = OpConstantComposite %v3float %float_500000 %float_500000 %float_500000 +%float_n500000 = OpConstant %float -500000 + %37 = OpConstantComposite %v3float %float_n500000 %float_n500000 %float_n500000 + %int_3 = OpConstant %int 3 + %int_4 = OpConstant %int 4 + %int_5 = OpConstant %int 5 + %int_6 = OpConstant %int 6 + %int_7 = OpConstant %int 7 + %int_8 = OpConstant %int 8 + %44 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %float_n1 = OpConstant %float -1 + %46 = OpConstantComposite %v3float %float_n1 %float_n1 %float_n1 + %uint_5 = OpConstant %uint 5 + %uint_0 = OpConstant %uint 0 + %uint_3 = OpConstant %uint 3 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float +%type_StructuredBuffer_v4float = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_type_StructuredBuffer_v4float = OpTypePointer Uniform %type_StructuredBuffer_v4float + %v2uint = OpTypeVector %uint 2 +%type__Globals = OpTypeStruct %v2uint +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%type_buffer_image = OpTypeImage %uint Buffer 2 0 0 2 R32ui +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %58 = OpTypeFunction %void +%_ptr_Function_v3float = OpTypePointer Function %v3float + %uint_8 = OpConstant %uint 8 +%_arr_v3float_uint_8 = OpTypeArray %v3float %uint_8 +%_ptr_Function__arr_v3float_uint_8 = OpTypePointer Function %_arr_v3float_uint_8 +%_ptr_Uniform_v2uint = OpTypePointer Uniform %v2uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %bool = OpTypeBool + %v2bool = OpTypeVector %bool 2 + %v3bool = OpTypeVector %bool 3 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Image_uint = OpTypePointer Image %uint +%CulledObjectBoxBounds = OpVariable %_ptr_Uniform_type_StructuredBuffer_v4float Uniform + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%RWShadowTileNumCulledObjects = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_uint Input +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %70 = OpUndef %v3float + %71 = OpConstantNull %v3float +%ShadowObjectCullPS = OpFunction %void None %58 + %72 = OpLabel + %73 = OpVariable %_ptr_Function__arr_v3float_uint_8 Function + %74 = OpLoad %uint %in_var_TEXCOORD0 + %75 = OpLoad %v4float %gl_FragCoord + %76 = OpVectorShuffle %v2float %75 %75 0 1 + %77 = OpConvertFToU %v2uint %76 + %78 = OpCompositeExtract %uint %77 1 + %79 = OpAccessChain %_ptr_Uniform_v2uint %_Globals %int_0 + %80 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_0 %int_0 + %81 = OpLoad %uint %80 + %82 = OpIMul %uint %78 %81 + %83 = OpCompositeExtract %uint %77 0 + %84 = OpIAdd %uint %82 %83 + %85 = OpConvertUToF %float %83 + %86 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_0 %int_1 + %87 = OpLoad %uint %86 + %88 = OpISub %uint %87 %uint_1 + %89 = OpISub %uint %88 %78 + %90 = OpConvertUToF %float %89 + %91 = OpCompositeConstruct %v2float %85 %90 + %92 = OpLoad %v2uint %79 + %93 = OpConvertUToF %v2float %92 + %94 = OpFDiv %v2float %91 %93 + %95 = OpFMul %v2float %94 %27 + %96 = OpFSub %v2float %95 %29 + %97 = OpFAdd %v2float %91 %29 + %98 = OpFDiv %v2float %97 %93 + %99 = OpFMul %v2float %98 %27 + %100 = OpFSub %v2float %99 %29 + %101 = OpVectorShuffle %v3float %70 %100 3 4 2 + %102 = OpCompositeInsert %v3float %float_1 %101 2 + %103 = OpIMul %uint %74 %uint_5 + %104 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %103 + %105 = OpLoad %v4float %104 + %106 = OpVectorShuffle %v3float %105 %105 0 1 2 + %107 = OpIAdd %uint %103 %uint_1 + %108 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %107 + %109 = OpLoad %v4float %108 + %110 = OpVectorShuffle %v3float %109 %109 0 1 2 + %111 = OpVectorShuffle %v2float %109 %71 0 1 + %112 = OpVectorShuffle %v2float %96 %71 0 1 + %113 = OpFOrdGreaterThan %v2bool %111 %112 + %114 = OpAll %bool %113 + %115 = OpFOrdLessThan %v3bool %106 %102 + %116 = OpAll %bool %115 + %117 = OpLogicalAnd %bool %114 %116 + OpSelectionMerge %118 DontFlatten + OpBranchConditional %117 %119 %118 + %119 = OpLabel + %120 = OpFAdd %v3float %106 %110 + %121 = OpFMul %v3float %33 %120 + %122 = OpCompositeExtract %float %96 0 + %123 = OpCompositeExtract %float %96 1 + %124 = OpCompositeConstruct %v3float %122 %123 %float_n1000 + %125 = OpAccessChain %_ptr_Function_v3float %73 %int_0 + OpStore %125 %124 + %126 = OpCompositeExtract %float %100 0 + %127 = OpCompositeConstruct %v3float %126 %123 %float_n1000 + %128 = OpAccessChain %_ptr_Function_v3float %73 %int_1 + OpStore %128 %127 + %129 = OpCompositeExtract %float %100 1 + %130 = OpCompositeConstruct %v3float %122 %129 %float_n1000 + %131 = OpAccessChain %_ptr_Function_v3float %73 %int_2 + OpStore %131 %130 + %132 = OpCompositeConstruct %v3float %126 %129 %float_n1000 + %133 = OpAccessChain %_ptr_Function_v3float %73 %int_3 + OpStore %133 %132 + %134 = OpCompositeConstruct %v3float %122 %123 %float_1 + %135 = OpAccessChain %_ptr_Function_v3float %73 %int_4 + OpStore %135 %134 + %136 = OpCompositeConstruct %v3float %126 %123 %float_1 + %137 = OpAccessChain %_ptr_Function_v3float %73 %int_5 + OpStore %137 %136 + %138 = OpCompositeConstruct %v3float %122 %129 %float_1 + %139 = OpAccessChain %_ptr_Function_v3float %73 %int_6 + OpStore %139 %138 + %140 = OpCompositeConstruct %v3float %126 %129 %float_1 + %141 = OpAccessChain %_ptr_Function_v3float %73 %int_7 + OpStore %141 %140 + %142 = OpIAdd %uint %103 %uint_2 + %143 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %142 + %144 = OpLoad %v4float %143 + %145 = OpVectorShuffle %v3float %144 %144 0 1 2 + %146 = OpIAdd %uint %103 %uint_3 + %147 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %146 + %148 = OpLoad %v4float %147 + %149 = OpVectorShuffle %v3float %148 %148 0 1 2 + %150 = OpIAdd %uint %103 %uint_4 + %151 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %150 + %152 = OpLoad %v4float %151 + %153 = OpVectorShuffle %v3float %152 %152 0 1 2 + OpBranch %154 + %154 = OpLabel + %155 = OpPhi %v3float %37 %119 %156 %157 + %158 = OpPhi %v3float %35 %119 %159 %157 + %160 = OpPhi %int %int_0 %119 %161 %157 + %162 = OpSLessThan %bool %160 %int_8 + OpLoopMerge %163 %157 Unroll + OpBranchConditional %162 %157 %163 + %157 = OpLabel + %164 = OpAccessChain %_ptr_Function_v3float %73 %160 + %165 = OpLoad %v3float %164 + %166 = OpFSub %v3float %165 %121 + %167 = OpDot %float %166 %145 + %168 = OpDot %float %166 %149 + %169 = OpDot %float %166 %153 + %170 = OpCompositeConstruct %v3float %167 %168 %169 + %159 = OpExtInst %v3float %1 FMin %158 %170 + %156 = OpExtInst %v3float %1 FMax %155 %170 + %161 = OpIAdd %int %160 %int_1 + OpBranch %154 + %163 = OpLabel + %171 = OpFOrdLessThan %v3bool %158 %44 + %172 = OpAll %bool %171 + %173 = OpFOrdGreaterThan %v3bool %155 %46 + %174 = OpAll %bool %173 + %175 = OpLogicalAnd %bool %172 %174 + OpSelectionMerge %176 DontFlatten + OpBranchConditional %175 %177 %176 + %177 = OpLabel + %178 = OpImageTexelPointer %_ptr_Image_uint %RWShadowTileNumCulledObjects %84 %uint_0 + %179 = OpAtomicIAdd %uint %178 %uint_1 %uint_0 %uint_1 + OpBranch %176 + %176 = OpLabel + OpBranch %118 + %118 = OpLabel + OpStore %out_var_SV_Target0 %22 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.frag new file mode 100644 index 0000000..270a197 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.frag @@ -0,0 +1,242 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 180 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %ShadowObjectCullPS "main" %in_var_TEXCOORD0 %gl_FragCoord %out_var_SV_Target0 + OpExecutionMode %ShadowObjectCullPS OriginUpperLeft + OpSource HLSL 600 + OpName %type_StructuredBuffer_v4float "type.StructuredBuffer.v4float" + OpName %CulledObjectBoxBounds "CulledObjectBoxBounds" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "ShadowTileListGroupSize" + OpName %_Globals "$Globals" + OpName %type_buffer_image "type.buffer.image" + OpName %RWShadowTileNumCulledObjects "RWShadowTileNumCulledObjects" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %ShadowObjectCullPS "ShadowObjectCullPS" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorate %in_var_TEXCOORD0 Flat + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_POSITION" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %CulledObjectBoxBounds DescriptorSet 0 + OpDecorate %CulledObjectBoxBounds Binding 1 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 2 + OpDecorate %RWShadowTileNumCulledObjects DescriptorSet 0 + OpDecorate %RWShadowTileNumCulledObjects Binding 0 + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_v4float 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_v4float 0 NonWritable + OpDecorate %type_StructuredBuffer_v4float BufferBlock + OpMemberDecorate %type__Globals 0 Offset 0 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_4 = OpConstant %uint 4 + %float_0 = OpConstant %float 0 + %22 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %float_2 = OpConstant %float 2 + %27 = OpConstantComposite %v2float %float_2 %float_2 + %float_1 = OpConstant %float 1 + %29 = OpConstantComposite %v2float %float_1 %float_1 +%float_n1000 = OpConstant %float -1000 + %int_2 = OpConstant %int 2 + %float_0_5 = OpConstant %float 0.5 + %33 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 +%float_500000 = OpConstant %float 500000 + %35 = OpConstantComposite %v3float %float_500000 %float_500000 %float_500000 +%float_n500000 = OpConstant %float -500000 + %37 = OpConstantComposite %v3float %float_n500000 %float_n500000 %float_n500000 + %int_3 = OpConstant %int 3 + %int_4 = OpConstant %int 4 + %int_5 = OpConstant %int 5 + %int_6 = OpConstant %int 6 + %int_7 = OpConstant %int 7 + %int_8 = OpConstant %int 8 + %44 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %float_n1 = OpConstant %float -1 + %46 = OpConstantComposite %v3float %float_n1 %float_n1 %float_n1 + %uint_5 = OpConstant %uint 5 + %uint_0 = OpConstant %uint 0 + %uint_3 = OpConstant %uint 3 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float +%type_StructuredBuffer_v4float = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_type_StructuredBuffer_v4float = OpTypePointer Uniform %type_StructuredBuffer_v4float + %v2uint = OpTypeVector %uint 2 +%type__Globals = OpTypeStruct %v2uint +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%type_buffer_image = OpTypeImage %uint Buffer 2 0 0 2 R32ui +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %58 = OpTypeFunction %void +%_ptr_Function_v3float = OpTypePointer Function %v3float + %uint_8 = OpConstant %uint 8 +%_arr_v3float_uint_8 = OpTypeArray %v3float %uint_8 +%_ptr_Function__arr_v3float_uint_8 = OpTypePointer Function %_arr_v3float_uint_8 +%_ptr_Uniform_v2uint = OpTypePointer Uniform %v2uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %bool = OpTypeBool + %v2bool = OpTypeVector %bool 2 + %v3bool = OpTypeVector %bool 3 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Image_uint = OpTypePointer Image %uint +%CulledObjectBoxBounds = OpVariable %_ptr_Uniform_type_StructuredBuffer_v4float Uniform + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%RWShadowTileNumCulledObjects = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_uint Input +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %70 = OpUndef %v3float + %71 = OpConstantNull %v3float +%ShadowObjectCullPS = OpFunction %void None %58 + %72 = OpLabel + %73 = OpVariable %_ptr_Function__arr_v3float_uint_8 Function + %74 = OpLoad %uint %in_var_TEXCOORD0 + %75 = OpLoad %v4float %gl_FragCoord + %76 = OpVectorShuffle %v2float %75 %75 0 1 + %77 = OpConvertFToU %v2uint %76 + %78 = OpCompositeExtract %uint %77 1 + %79 = OpAccessChain %_ptr_Uniform_v2uint %_Globals %int_0 + %80 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_0 %int_0 + %81 = OpLoad %uint %80 + %82 = OpIMul %uint %78 %81 + %83 = OpCompositeExtract %uint %77 0 + %84 = OpIAdd %uint %82 %83 + %85 = OpConvertUToF %float %83 + %86 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_0 %int_1 + %87 = OpLoad %uint %86 + %88 = OpISub %uint %87 %uint_1 + %89 = OpISub %uint %88 %78 + %90 = OpConvertUToF %float %89 + %91 = OpCompositeConstruct %v2float %85 %90 + %92 = OpLoad %v2uint %79 + %93 = OpConvertUToF %v2float %92 + %94 = OpFDiv %v2float %91 %93 + %95 = OpFMul %v2float %94 %27 + %96 = OpFSub %v2float %95 %29 + %97 = OpFAdd %v2float %91 %29 + %98 = OpFDiv %v2float %97 %93 + %99 = OpFMul %v2float %98 %27 + %100 = OpFSub %v2float %99 %29 + %101 = OpVectorShuffle %v3float %70 %100 3 4 2 + %102 = OpCompositeInsert %v3float %float_1 %101 2 + %103 = OpIMul %uint %74 %uint_5 + %104 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %103 + %105 = OpLoad %v4float %104 + %106 = OpVectorShuffle %v3float %105 %105 0 1 2 + %107 = OpIAdd %uint %103 %uint_1 + %108 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %107 + %109 = OpLoad %v4float %108 + %110 = OpVectorShuffle %v3float %109 %109 0 1 2 + %111 = OpVectorShuffle %v2float %109 %71 0 1 + %112 = OpVectorShuffle %v2float %96 %71 0 1 + %113 = OpFOrdGreaterThan %v2bool %111 %112 + %114 = OpAll %bool %113 + %115 = OpFOrdLessThan %v3bool %106 %102 + %116 = OpAll %bool %115 + %117 = OpLogicalAnd %bool %114 %116 + OpSelectionMerge %118 DontFlatten + OpBranchConditional %117 %119 %118 + %119 = OpLabel + %120 = OpFAdd %v3float %106 %110 + %121 = OpFMul %v3float %33 %120 + %122 = OpCompositeExtract %float %96 0 + %123 = OpCompositeExtract %float %96 1 + %124 = OpCompositeConstruct %v3float %122 %123 %float_n1000 + %125 = OpAccessChain %_ptr_Function_v3float %73 %int_0 + OpStore %125 %124 + %126 = OpCompositeExtract %float %100 0 + %127 = OpCompositeConstruct %v3float %126 %123 %float_n1000 + %128 = OpAccessChain %_ptr_Function_v3float %73 %int_1 + OpStore %128 %127 + %129 = OpCompositeExtract %float %100 1 + %130 = OpCompositeConstruct %v3float %122 %129 %float_n1000 + %131 = OpAccessChain %_ptr_Function_v3float %73 %int_2 + OpStore %131 %130 + %132 = OpCompositeConstruct %v3float %126 %129 %float_n1000 + %133 = OpAccessChain %_ptr_Function_v3float %73 %int_3 + OpStore %133 %132 + %134 = OpCompositeConstruct %v3float %122 %123 %float_1 + %135 = OpAccessChain %_ptr_Function_v3float %73 %int_4 + OpStore %135 %134 + %136 = OpCompositeConstruct %v3float %126 %123 %float_1 + %137 = OpAccessChain %_ptr_Function_v3float %73 %int_5 + OpStore %137 %136 + %138 = OpCompositeConstruct %v3float %122 %129 %float_1 + %139 = OpAccessChain %_ptr_Function_v3float %73 %int_6 + OpStore %139 %138 + %140 = OpCompositeConstruct %v3float %126 %129 %float_1 + %141 = OpAccessChain %_ptr_Function_v3float %73 %int_7 + OpStore %141 %140 + %142 = OpIAdd %uint %103 %uint_2 + %143 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %142 + %144 = OpLoad %v4float %143 + %145 = OpVectorShuffle %v3float %144 %144 0 1 2 + %146 = OpIAdd %uint %103 %uint_3 + %147 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %146 + %148 = OpLoad %v4float %147 + %149 = OpVectorShuffle %v3float %148 %148 0 1 2 + %150 = OpIAdd %uint %103 %uint_4 + %151 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %150 + %152 = OpLoad %v4float %151 + %153 = OpVectorShuffle %v3float %152 %152 0 1 2 + OpBranch %154 + %154 = OpLabel + %155 = OpPhi %v3float %37 %119 %156 %157 + %158 = OpPhi %v3float %35 %119 %159 %157 + %160 = OpPhi %int %int_0 %119 %161 %157 + %162 = OpSLessThan %bool %160 %int_8 + OpLoopMerge %163 %157 Unroll + OpBranchConditional %162 %157 %163 + %157 = OpLabel + %164 = OpAccessChain %_ptr_Function_v3float %73 %160 + %165 = OpLoad %v3float %164 + %166 = OpFSub %v3float %165 %121 + %167 = OpDot %float %166 %145 + %168 = OpDot %float %166 %149 + %169 = OpDot %float %166 %153 + %170 = OpCompositeConstruct %v3float %167 %168 %169 + %159 = OpExtInst %v3float %1 FMin %158 %170 + %156 = OpExtInst %v3float %1 FMax %155 %170 + %161 = OpIAdd %int %160 %int_1 + OpBranch %154 + %163 = OpLabel + %171 = OpFOrdLessThan %v3bool %158 %44 + %172 = OpAll %bool %171 + %173 = OpFOrdGreaterThan %v3bool %155 %46 + %174 = OpAll %bool %173 + %175 = OpLogicalAnd %bool %172 %174 + OpSelectionMerge %176 DontFlatten + OpBranchConditional %175 %177 %176 + %177 = OpLabel + %178 = OpImageTexelPointer %_ptr_Image_uint %RWShadowTileNumCulledObjects %84 %uint_0 + %179 = OpAtomicIAdd %uint %178 %uint_1 %uint_0 %uint_1 + OpBranch %176 + %176 = OpLabel + OpBranch %118 + %118 = OpLabel + OpStore %out_var_SV_Target0 %22 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag b/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag new file mode 100644 index 0000000..270a197 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/frag/texture-atomics.asm.graphics-robust-access.frag @@ -0,0 +1,242 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 180 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %ShadowObjectCullPS "main" %in_var_TEXCOORD0 %gl_FragCoord %out_var_SV_Target0 + OpExecutionMode %ShadowObjectCullPS OriginUpperLeft + OpSource HLSL 600 + OpName %type_StructuredBuffer_v4float "type.StructuredBuffer.v4float" + OpName %CulledObjectBoxBounds "CulledObjectBoxBounds" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "ShadowTileListGroupSize" + OpName %_Globals "$Globals" + OpName %type_buffer_image "type.buffer.image" + OpName %RWShadowTileNumCulledObjects "RWShadowTileNumCulledObjects" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %ShadowObjectCullPS "ShadowObjectCullPS" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorate %in_var_TEXCOORD0 Flat + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorateString %gl_FragCoord UserSemantic "SV_POSITION" + OpDecorateString %out_var_SV_Target0 UserSemantic "SV_Target0" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %CulledObjectBoxBounds DescriptorSet 0 + OpDecorate %CulledObjectBoxBounds Binding 1 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 2 + OpDecorate %RWShadowTileNumCulledObjects DescriptorSet 0 + OpDecorate %RWShadowTileNumCulledObjects Binding 0 + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_v4float 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_v4float 0 NonWritable + OpDecorate %type_StructuredBuffer_v4float BufferBlock + OpMemberDecorate %type__Globals 0 Offset 0 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_4 = OpConstant %uint 4 + %float_0 = OpConstant %float 0 + %22 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 + %float_2 = OpConstant %float 2 + %27 = OpConstantComposite %v2float %float_2 %float_2 + %float_1 = OpConstant %float 1 + %29 = OpConstantComposite %v2float %float_1 %float_1 +%float_n1000 = OpConstant %float -1000 + %int_2 = OpConstant %int 2 + %float_0_5 = OpConstant %float 0.5 + %33 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 +%float_500000 = OpConstant %float 500000 + %35 = OpConstantComposite %v3float %float_500000 %float_500000 %float_500000 +%float_n500000 = OpConstant %float -500000 + %37 = OpConstantComposite %v3float %float_n500000 %float_n500000 %float_n500000 + %int_3 = OpConstant %int 3 + %int_4 = OpConstant %int 4 + %int_5 = OpConstant %int 5 + %int_6 = OpConstant %int 6 + %int_7 = OpConstant %int 7 + %int_8 = OpConstant %int 8 + %44 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %float_n1 = OpConstant %float -1 + %46 = OpConstantComposite %v3float %float_n1 %float_n1 %float_n1 + %uint_5 = OpConstant %uint 5 + %uint_0 = OpConstant %uint 0 + %uint_3 = OpConstant %uint 3 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float +%type_StructuredBuffer_v4float = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_type_StructuredBuffer_v4float = OpTypePointer Uniform %type_StructuredBuffer_v4float + %v2uint = OpTypeVector %uint 2 +%type__Globals = OpTypeStruct %v2uint +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%type_buffer_image = OpTypeImage %uint Buffer 2 0 0 2 R32ui +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %58 = OpTypeFunction %void +%_ptr_Function_v3float = OpTypePointer Function %v3float + %uint_8 = OpConstant %uint 8 +%_arr_v3float_uint_8 = OpTypeArray %v3float %uint_8 +%_ptr_Function__arr_v3float_uint_8 = OpTypePointer Function %_arr_v3float_uint_8 +%_ptr_Uniform_v2uint = OpTypePointer Uniform %v2uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %bool = OpTypeBool + %v2bool = OpTypeVector %bool 2 + %v3bool = OpTypeVector %bool 3 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Image_uint = OpTypePointer Image %uint +%CulledObjectBoxBounds = OpVariable %_ptr_Uniform_type_StructuredBuffer_v4float Uniform + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%RWShadowTileNumCulledObjects = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_uint Input +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %70 = OpUndef %v3float + %71 = OpConstantNull %v3float +%ShadowObjectCullPS = OpFunction %void None %58 + %72 = OpLabel + %73 = OpVariable %_ptr_Function__arr_v3float_uint_8 Function + %74 = OpLoad %uint %in_var_TEXCOORD0 + %75 = OpLoad %v4float %gl_FragCoord + %76 = OpVectorShuffle %v2float %75 %75 0 1 + %77 = OpConvertFToU %v2uint %76 + %78 = OpCompositeExtract %uint %77 1 + %79 = OpAccessChain %_ptr_Uniform_v2uint %_Globals %int_0 + %80 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_0 %int_0 + %81 = OpLoad %uint %80 + %82 = OpIMul %uint %78 %81 + %83 = OpCompositeExtract %uint %77 0 + %84 = OpIAdd %uint %82 %83 + %85 = OpConvertUToF %float %83 + %86 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_0 %int_1 + %87 = OpLoad %uint %86 + %88 = OpISub %uint %87 %uint_1 + %89 = OpISub %uint %88 %78 + %90 = OpConvertUToF %float %89 + %91 = OpCompositeConstruct %v2float %85 %90 + %92 = OpLoad %v2uint %79 + %93 = OpConvertUToF %v2float %92 + %94 = OpFDiv %v2float %91 %93 + %95 = OpFMul %v2float %94 %27 + %96 = OpFSub %v2float %95 %29 + %97 = OpFAdd %v2float %91 %29 + %98 = OpFDiv %v2float %97 %93 + %99 = OpFMul %v2float %98 %27 + %100 = OpFSub %v2float %99 %29 + %101 = OpVectorShuffle %v3float %70 %100 3 4 2 + %102 = OpCompositeInsert %v3float %float_1 %101 2 + %103 = OpIMul %uint %74 %uint_5 + %104 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %103 + %105 = OpLoad %v4float %104 + %106 = OpVectorShuffle %v3float %105 %105 0 1 2 + %107 = OpIAdd %uint %103 %uint_1 + %108 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %107 + %109 = OpLoad %v4float %108 + %110 = OpVectorShuffle %v3float %109 %109 0 1 2 + %111 = OpVectorShuffle %v2float %109 %71 0 1 + %112 = OpVectorShuffle %v2float %96 %71 0 1 + %113 = OpFOrdGreaterThan %v2bool %111 %112 + %114 = OpAll %bool %113 + %115 = OpFOrdLessThan %v3bool %106 %102 + %116 = OpAll %bool %115 + %117 = OpLogicalAnd %bool %114 %116 + OpSelectionMerge %118 DontFlatten + OpBranchConditional %117 %119 %118 + %119 = OpLabel + %120 = OpFAdd %v3float %106 %110 + %121 = OpFMul %v3float %33 %120 + %122 = OpCompositeExtract %float %96 0 + %123 = OpCompositeExtract %float %96 1 + %124 = OpCompositeConstruct %v3float %122 %123 %float_n1000 + %125 = OpAccessChain %_ptr_Function_v3float %73 %int_0 + OpStore %125 %124 + %126 = OpCompositeExtract %float %100 0 + %127 = OpCompositeConstruct %v3float %126 %123 %float_n1000 + %128 = OpAccessChain %_ptr_Function_v3float %73 %int_1 + OpStore %128 %127 + %129 = OpCompositeExtract %float %100 1 + %130 = OpCompositeConstruct %v3float %122 %129 %float_n1000 + %131 = OpAccessChain %_ptr_Function_v3float %73 %int_2 + OpStore %131 %130 + %132 = OpCompositeConstruct %v3float %126 %129 %float_n1000 + %133 = OpAccessChain %_ptr_Function_v3float %73 %int_3 + OpStore %133 %132 + %134 = OpCompositeConstruct %v3float %122 %123 %float_1 + %135 = OpAccessChain %_ptr_Function_v3float %73 %int_4 + OpStore %135 %134 + %136 = OpCompositeConstruct %v3float %126 %123 %float_1 + %137 = OpAccessChain %_ptr_Function_v3float %73 %int_5 + OpStore %137 %136 + %138 = OpCompositeConstruct %v3float %122 %129 %float_1 + %139 = OpAccessChain %_ptr_Function_v3float %73 %int_6 + OpStore %139 %138 + %140 = OpCompositeConstruct %v3float %126 %129 %float_1 + %141 = OpAccessChain %_ptr_Function_v3float %73 %int_7 + OpStore %141 %140 + %142 = OpIAdd %uint %103 %uint_2 + %143 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %142 + %144 = OpLoad %v4float %143 + %145 = OpVectorShuffle %v3float %144 %144 0 1 2 + %146 = OpIAdd %uint %103 %uint_3 + %147 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %146 + %148 = OpLoad %v4float %147 + %149 = OpVectorShuffle %v3float %148 %148 0 1 2 + %150 = OpIAdd %uint %103 %uint_4 + %151 = OpAccessChain %_ptr_Uniform_v4float %CulledObjectBoxBounds %int_0 %150 + %152 = OpLoad %v4float %151 + %153 = OpVectorShuffle %v3float %152 %152 0 1 2 + OpBranch %154 + %154 = OpLabel + %155 = OpPhi %v3float %37 %119 %156 %157 + %158 = OpPhi %v3float %35 %119 %159 %157 + %160 = OpPhi %int %int_0 %119 %161 %157 + %162 = OpSLessThan %bool %160 %int_8 + OpLoopMerge %163 %157 Unroll + OpBranchConditional %162 %157 %163 + %157 = OpLabel + %164 = OpAccessChain %_ptr_Function_v3float %73 %160 + %165 = OpLoad %v3float %164 + %166 = OpFSub %v3float %165 %121 + %167 = OpDot %float %166 %145 + %168 = OpDot %float %166 %149 + %169 = OpDot %float %166 %153 + %170 = OpCompositeConstruct %v3float %167 %168 %169 + %159 = OpExtInst %v3float %1 FMin %158 %170 + %156 = OpExtInst %v3float %1 FMax %155 %170 + %161 = OpIAdd %int %160 %int_1 + OpBranch %154 + %163 = OpLabel + %171 = OpFOrdLessThan %v3bool %158 %44 + %172 = OpAll %bool %171 + %173 = OpFOrdGreaterThan %v3bool %155 %46 + %174 = OpAll %bool %173 + %175 = OpLogicalAnd %bool %172 %174 + OpSelectionMerge %176 DontFlatten + OpBranchConditional %175 %177 %176 + %177 = OpLabel + %178 = OpImageTexelPointer %_ptr_Image_uint %RWShadowTileNumCulledObjects %84 %uint_0 + %179 = OpAtomicIAdd %uint %178 %uint_1 %uint_0 %uint_1 + OpBranch %176 + %176 = OpLabel + OpBranch %118 + %118 = OpLabel + OpStore %out_var_SV_Target0 %22 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc b/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc new file mode 100644 index 0000000..4c70e14 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-incorrect-base-type.asm.tesc @@ -0,0 +1,1158 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 598 +; Schema: 0 + OpCapability Tessellation + OpCapability SampledBuffer + OpCapability StorageImageExtendedFormats + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %MainHull "main" %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_COLOR0 %in_var_TEXCOORD0 %in_var_TEXCOORD4 %in_var_PRIMITIVE_ID %in_var_LIGHTMAP_ID %in_var_VS_To_DS_Position %gl_InvocationID %out_var_TEXCOORD10_centroid %out_var_TEXCOORD11_centroid %out_var_COLOR0 %out_var_TEXCOORD0 %out_var_TEXCOORD4 %out_var_PRIMITIVE_ID %out_var_LIGHTMAP_ID %out_var_VS_To_DS_Position %out_var_PN_POSITION %out_var_PN_DisplacementScales %out_var_PN_TessellationMultiplier %out_var_PN_WorldDisplacementMultiplier %gl_TessLevelOuter %gl_TessLevelInner %out_var_PN_POSITION9 + OpExecutionMode %MainHull Triangles + OpExecutionMode %MainHull SpacingFractionalOdd + OpExecutionMode %MainHull VertexOrderCw + OpExecutionMode %MainHull OutputVertices 3 + OpSource HLSL 600 + OpName %FPNTessellationHSToDS "FPNTessellationHSToDS" + OpMemberName %FPNTessellationHSToDS 0 "PassSpecificData" + OpMemberName %FPNTessellationHSToDS 1 "WorldPosition" + OpMemberName %FPNTessellationHSToDS 2 "DisplacementScale" + OpMemberName %FPNTessellationHSToDS 3 "TessellationMultiplier" + OpMemberName %FPNTessellationHSToDS 4 "WorldDisplacementMultiplier" + OpName %FBasePassVSToDS "FBasePassVSToDS" + OpMemberName %FBasePassVSToDS 0 "FactoryInterpolants" + OpMemberName %FBasePassVSToDS 1 "BasePassInterpolants" + OpMemberName %FBasePassVSToDS 2 "Position" + OpName %FVertexFactoryInterpolantsVSToDS "FVertexFactoryInterpolantsVSToDS" + OpMemberName %FVertexFactoryInterpolantsVSToDS 0 "InterpolantsVSToPS" + OpName %FVertexFactoryInterpolantsVSToPS "FVertexFactoryInterpolantsVSToPS" + OpMemberName %FVertexFactoryInterpolantsVSToPS 0 "TangentToWorld0" + OpMemberName %FVertexFactoryInterpolantsVSToPS 1 "TangentToWorld2" + OpMemberName %FVertexFactoryInterpolantsVSToPS 2 "Color" + OpMemberName %FVertexFactoryInterpolantsVSToPS 3 "TexCoords" + OpMemberName %FVertexFactoryInterpolantsVSToPS 4 "LightMapCoordinate" + OpMemberName %FVertexFactoryInterpolantsVSToPS 5 "PrimitiveId" + OpMemberName %FVertexFactoryInterpolantsVSToPS 6 "LightmapDataIndex" + OpName %FBasePassInterpolantsVSToDS "FBasePassInterpolantsVSToDS" + OpName %FSharedBasePassInterpolants "FSharedBasePassInterpolants" + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_TranslatedWorldToView" + OpMemberName %type_View 3 "View_ViewToTranslatedWorld" + OpMemberName %type_View 4 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 5 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 6 "View_ViewToClip" + OpMemberName %type_View 7 "View_ViewToClipNoAA" + OpMemberName %type_View 8 "View_ClipToView" + OpMemberName %type_View 9 "View_ClipToTranslatedWorld" + OpMemberName %type_View 10 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 11 "View_ScreenToWorld" + OpMemberName %type_View 12 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 13 "View_ViewForward" + OpMemberName %type_View 14 "PrePadding_View_844" + OpMemberName %type_View 15 "View_ViewUp" + OpMemberName %type_View 16 "PrePadding_View_860" + OpMemberName %type_View 17 "View_ViewRight" + OpMemberName %type_View 18 "PrePadding_View_876" + OpMemberName %type_View 19 "View_HMDViewNoRollUp" + OpMemberName %type_View 20 "PrePadding_View_892" + OpMemberName %type_View 21 "View_HMDViewNoRollRight" + OpMemberName %type_View 22 "PrePadding_View_908" + OpMemberName %type_View 23 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 24 "View_ScreenPositionScaleBias" + OpMemberName %type_View 25 "View_WorldCameraOrigin" + OpMemberName %type_View 26 "PrePadding_View_956" + OpMemberName %type_View 27 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 28 "PrePadding_View_972" + OpMemberName %type_View 29 "View_WorldViewOrigin" + OpMemberName %type_View 30 "PrePadding_View_988" + OpMemberName %type_View 31 "View_PreViewTranslation" + OpMemberName %type_View 32 "PrePadding_View_1004" + OpMemberName %type_View 33 "View_PrevProjection" + OpMemberName %type_View 34 "View_PrevViewProj" + OpMemberName %type_View 35 "View_PrevViewRotationProj" + OpMemberName %type_View 36 "View_PrevViewToClip" + OpMemberName %type_View 37 "View_PrevClipToView" + OpMemberName %type_View 38 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 40 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 41 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 42 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 43 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 44 "PrePadding_View_1660" + OpMemberName %type_View 45 "View_PrevWorldViewOrigin" + OpMemberName %type_View 46 "PrePadding_View_1676" + OpMemberName %type_View 47 "View_PrevPreViewTranslation" + OpMemberName %type_View 48 "PrePadding_View_1692" + OpMemberName %type_View 49 "View_PrevInvViewProj" + OpMemberName %type_View 50 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 51 "View_ClipToPrevClip" + OpMemberName %type_View 52 "View_TemporalAAJitter" + OpMemberName %type_View 53 "View_GlobalClippingPlane" + OpMemberName %type_View 54 "View_FieldOfViewWideAngles" + OpMemberName %type_View 55 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 56 "View_ViewRectMin" + OpMemberName %type_View 57 "View_ViewSizeAndInvSize" + OpMemberName %type_View 58 "View_BufferSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 60 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 61 "View_PreExposure" + OpMemberName %type_View 62 "View_OneOverPreExposure" + OpMemberName %type_View 63 "PrePadding_View_2012" + OpMemberName %type_View 64 "View_DiffuseOverrideParameter" + OpMemberName %type_View 65 "View_SpecularOverrideParameter" + OpMemberName %type_View 66 "View_NormalOverrideParameter" + OpMemberName %type_View 67 "View_RoughnessOverrideParameter" + OpMemberName %type_View 68 "View_PrevFrameGameTime" + OpMemberName %type_View 69 "View_PrevFrameRealTime" + OpMemberName %type_View 70 "View_OutOfBoundsMask" + OpMemberName %type_View 71 "PrePadding_View_2084" + OpMemberName %type_View 72 "PrePadding_View_2088" + OpMemberName %type_View 73 "PrePadding_View_2092" + OpMemberName %type_View 74 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 75 "View_CullingSign" + OpMemberName %type_View 76 "View_NearPlane" + OpMemberName %type_View 77 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 78 "View_GameTime" + OpMemberName %type_View 79 "View_RealTime" + OpMemberName %type_View 80 "View_DeltaTime" + OpMemberName %type_View 81 "View_MaterialTextureMipBias" + OpMemberName %type_View 82 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 83 "View_Random" + OpMemberName %type_View 84 "View_FrameNumber" + OpMemberName %type_View 85 "View_StateFrameIndexMod8" + OpMemberName %type_View 86 "View_StateFrameIndex" + OpMemberName %type_View 87 "View_CameraCut" + OpMemberName %type_View 88 "View_UnlitViewmodeMask" + OpMemberName %type_View 89 "PrePadding_View_2164" + OpMemberName %type_View 90 "PrePadding_View_2168" + OpMemberName %type_View 91 "PrePadding_View_2172" + OpMemberName %type_View 92 "View_DirectionalLightColor" + OpMemberName %type_View 93 "View_DirectionalLightDirection" + OpMemberName %type_View 94 "PrePadding_View_2204" + OpMemberName %type_View 95 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 97 "View_TemporalAAParams" + OpMemberName %type_View 98 "View_CircleDOFParams" + OpMemberName %type_View 99 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 100 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 101 "View_DepthOfFieldScale" + OpMemberName %type_View 102 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 103 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 104 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 105 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 106 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 107 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 108 "View_GeneralPurposeTweak" + OpMemberName %type_View 109 "View_DemosaicVposOffset" + OpMemberName %type_View 110 "PrePadding_View_2348" + OpMemberName %type_View 111 "View_IndirectLightingColorScale" + OpMemberName %type_View 112 "View_HDR32bppEncodingMode" + OpMemberName %type_View 113 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 114 "View_AtmosphericFogSunPower" + OpMemberName %type_View 115 "View_AtmosphericFogPower" + OpMemberName %type_View 116 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 117 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 118 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 119 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 120 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 121 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 122 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 123 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 124 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 125 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 126 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 127 "View_AtmosphericFogSunColor" + OpMemberName %type_View 128 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 129 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 130 "View_AmbientCubemapTint" + OpMemberName %type_View 131 "View_AmbientCubemapIntensity" + OpMemberName %type_View 132 "View_SkyLightParameters" + OpMemberName %type_View 133 "PrePadding_View_2488" + OpMemberName %type_View 134 "PrePadding_View_2492" + OpMemberName %type_View 135 "View_SkyLightColor" + OpMemberName %type_View 136 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 137 "View_MobilePreviewMode" + OpMemberName %type_View 138 "View_HMDEyePaddingOffset" + OpMemberName %type_View 139 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 140 "View_ShowDecalsMask" + OpMemberName %type_View 141 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 142 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 143 "PrePadding_View_2648" + OpMemberName %type_View 144 "PrePadding_View_2652" + OpMemberName %type_View 145 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 146 "View_StereoPassIndex" + OpMemberName %type_View 147 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 148 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 149 "View_GlobalVolumeDimension" + OpMemberName %type_View 150 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 151 "View_MaxGlobalDistance" + OpMemberName %type_View 152 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 153 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 154 "PrePadding_View_2828" + OpMemberName %type_View 155 "View_VolumetricFogGridZParams" + OpMemberName %type_View 156 "PrePadding_View_2844" + OpMemberName %type_View 157 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 158 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 159 "PrePadding_View_2860" + OpMemberName %type_View 160 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 161 "PrePadding_View_2876" + OpMemberName %type_View 162 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 163 "PrePadding_View_2892" + OpMemberName %type_View 164 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 165 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 166 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 167 "View_StereoIPD" + OpMemberName %type_View 168 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 169 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_StructuredBuffer_v4float "type.StructuredBuffer.v4float" + OpName %View_PrimitiveSceneData "View_PrimitiveSceneData" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_COLOR0 "in.var.COLOR0" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_TEXCOORD4 "in.var.TEXCOORD4" + OpName %in_var_PRIMITIVE_ID "in.var.PRIMITIVE_ID" + OpName %in_var_LIGHTMAP_ID "in.var.LIGHTMAP_ID" + OpName %in_var_VS_To_DS_Position "in.var.VS_To_DS_Position" + OpName %out_var_TEXCOORD10_centroid "out.var.TEXCOORD10_centroid" + OpName %out_var_TEXCOORD11_centroid "out.var.TEXCOORD11_centroid" + OpName %out_var_COLOR0 "out.var.COLOR0" + OpName %out_var_TEXCOORD0 "out.var.TEXCOORD0" + OpName %out_var_TEXCOORD4 "out.var.TEXCOORD4" + OpName %out_var_PRIMITIVE_ID "out.var.PRIMITIVE_ID" + OpName %out_var_LIGHTMAP_ID "out.var.LIGHTMAP_ID" + OpName %out_var_VS_To_DS_Position "out.var.VS_To_DS_Position" + OpName %out_var_PN_POSITION "out.var.PN_POSITION" + OpName %out_var_PN_DisplacementScales "out.var.PN_DisplacementScales" + OpName %out_var_PN_TessellationMultiplier "out.var.PN_TessellationMultiplier" + OpName %out_var_PN_WorldDisplacementMultiplier "out.var.PN_WorldDisplacementMultiplier" + OpName %out_var_PN_POSITION9 "out.var.PN_POSITION9" + OpName %MainHull "MainHull" + OpName %param_var_I "param.var.I" + OpName %temp_var_hullMainRetVal "temp.var.hullMainRetVal" + OpName %if_merge "if.merge" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_COLOR0 UserSemantic "COLOR0" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %in_var_TEXCOORD4 UserSemantic "TEXCOORD4" + OpDecorateString %in_var_PRIMITIVE_ID UserSemantic "PRIMITIVE_ID" + OpDecorateString %in_var_LIGHTMAP_ID UserSemantic "LIGHTMAP_ID" + OpDecorateString %in_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorate %gl_InvocationID BuiltIn InvocationId + OpDecorateString %gl_InvocationID UserSemantic "SV_OutputControlPointID" + OpDecorateString %out_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %out_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %out_var_COLOR0 UserSemantic "COLOR0" + OpDecorateString %out_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %out_var_TEXCOORD4 UserSemantic "TEXCOORD4" + OpDecorateString %out_var_PRIMITIVE_ID UserSemantic "PRIMITIVE_ID" + OpDecorateString %out_var_LIGHTMAP_ID UserSemantic "LIGHTMAP_ID" + OpDecorateString %out_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorateString %out_var_PN_POSITION UserSemantic "PN_POSITION" + OpDecorateString %out_var_PN_DisplacementScales UserSemantic "PN_DisplacementScales" + OpDecorateString %out_var_PN_TessellationMultiplier UserSemantic "PN_TessellationMultiplier" + OpDecorateString %out_var_PN_WorldDisplacementMultiplier UserSemantic "PN_WorldDisplacementMultiplier" + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorateString %gl_TessLevelOuter UserSemantic "SV_TessFactor" + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorateString %gl_TessLevelInner UserSemantic "SV_InsideTessFactor" + OpDecorate %gl_TessLevelInner Patch + OpDecorateString %out_var_PN_POSITION9 UserSemantic "PN_POSITION9" + OpDecorate %out_var_PN_POSITION9 Patch + OpDecorate %in_var_TEXCOORD10_centroid Location 0 + OpDecorate %in_var_TEXCOORD11_centroid Location 1 + OpDecorate %in_var_COLOR0 Location 2 + OpDecorate %in_var_TEXCOORD0 Location 3 + OpDecorate %in_var_TEXCOORD4 Location 4 + OpDecorate %in_var_PRIMITIVE_ID Location 5 + OpDecorate %in_var_LIGHTMAP_ID Location 6 + OpDecorate %in_var_VS_To_DS_Position Location 7 + OpDecorate %out_var_COLOR0 Location 0 + OpDecorate %out_var_LIGHTMAP_ID Location 1 + OpDecorate %out_var_PN_DisplacementScales Location 2 + OpDecorate %out_var_PN_POSITION Location 3 + OpDecorate %out_var_PN_POSITION9 Location 6 + OpDecorate %out_var_PN_TessellationMultiplier Location 7 + OpDecorate %out_var_PN_WorldDisplacementMultiplier Location 8 + OpDecorate %out_var_PRIMITIVE_ID Location 9 + OpDecorate %out_var_TEXCOORD0 Location 10 + OpDecorate %out_var_TEXCOORD10_centroid Location 11 + OpDecorate %out_var_TEXCOORD11_centroid Location 12 + OpDecorate %out_var_TEXCOORD4 Location 13 + OpDecorate %out_var_VS_To_DS_Position Location 14 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 1 + OpDecorate %View_PrimitiveSceneData DescriptorSet 0 + OpDecorate %View_PrimitiveSceneData Binding 0 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 14 Offset 844 + OpMemberDecorate %type_View 15 Offset 848 + OpMemberDecorate %type_View 16 Offset 860 + OpMemberDecorate %type_View 17 Offset 864 + OpMemberDecorate %type_View 18 Offset 876 + OpMemberDecorate %type_View 19 Offset 880 + OpMemberDecorate %type_View 20 Offset 892 + OpMemberDecorate %type_View 21 Offset 896 + OpMemberDecorate %type_View 22 Offset 908 + OpMemberDecorate %type_View 23 Offset 912 + OpMemberDecorate %type_View 24 Offset 928 + OpMemberDecorate %type_View 25 Offset 944 + OpMemberDecorate %type_View 26 Offset 956 + OpMemberDecorate %type_View 27 Offset 960 + OpMemberDecorate %type_View 28 Offset 972 + OpMemberDecorate %type_View 29 Offset 976 + OpMemberDecorate %type_View 30 Offset 988 + OpMemberDecorate %type_View 31 Offset 992 + OpMemberDecorate %type_View 32 Offset 1004 + OpMemberDecorate %type_View 33 Offset 1008 + OpMemberDecorate %type_View 33 MatrixStride 16 + OpMemberDecorate %type_View 33 ColMajor + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 44 Offset 1660 + OpMemberDecorate %type_View 45 Offset 1664 + OpMemberDecorate %type_View 46 Offset 1676 + OpMemberDecorate %type_View 47 Offset 1680 + OpMemberDecorate %type_View 48 Offset 1692 + OpMemberDecorate %type_View 49 Offset 1696 + OpMemberDecorate %type_View 49 MatrixStride 16 + OpMemberDecorate %type_View 49 ColMajor + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 53 Offset 1904 + OpMemberDecorate %type_View 54 Offset 1920 + OpMemberDecorate %type_View 55 Offset 1928 + OpMemberDecorate %type_View 56 Offset 1936 + OpMemberDecorate %type_View 57 Offset 1952 + OpMemberDecorate %type_View 58 Offset 1968 + OpMemberDecorate %type_View 59 Offset 1984 + OpMemberDecorate %type_View 60 Offset 2000 + OpMemberDecorate %type_View 61 Offset 2004 + OpMemberDecorate %type_View 62 Offset 2008 + OpMemberDecorate %type_View 63 Offset 2012 + OpMemberDecorate %type_View 64 Offset 2016 + OpMemberDecorate %type_View 65 Offset 2032 + OpMemberDecorate %type_View 66 Offset 2048 + OpMemberDecorate %type_View 67 Offset 2064 + OpMemberDecorate %type_View 68 Offset 2072 + OpMemberDecorate %type_View 69 Offset 2076 + OpMemberDecorate %type_View 70 Offset 2080 + OpMemberDecorate %type_View 71 Offset 2084 + OpMemberDecorate %type_View 72 Offset 2088 + OpMemberDecorate %type_View 73 Offset 2092 + OpMemberDecorate %type_View 74 Offset 2096 + OpMemberDecorate %type_View 75 Offset 2108 + OpMemberDecorate %type_View 76 Offset 2112 + OpMemberDecorate %type_View 77 Offset 2116 + OpMemberDecorate %type_View 78 Offset 2120 + OpMemberDecorate %type_View 79 Offset 2124 + OpMemberDecorate %type_View 80 Offset 2128 + OpMemberDecorate %type_View 81 Offset 2132 + OpMemberDecorate %type_View 82 Offset 2136 + OpMemberDecorate %type_View 83 Offset 2140 + OpMemberDecorate %type_View 84 Offset 2144 + OpMemberDecorate %type_View 85 Offset 2148 + OpMemberDecorate %type_View 86 Offset 2152 + OpMemberDecorate %type_View 87 Offset 2156 + OpMemberDecorate %type_View 88 Offset 2160 + OpMemberDecorate %type_View 89 Offset 2164 + OpMemberDecorate %type_View 90 Offset 2168 + OpMemberDecorate %type_View 91 Offset 2172 + OpMemberDecorate %type_View 92 Offset 2176 + OpMemberDecorate %type_View 93 Offset 2192 + OpMemberDecorate %type_View 94 Offset 2204 + OpMemberDecorate %type_View 95 Offset 2208 + OpMemberDecorate %type_View 96 Offset 2240 + OpMemberDecorate %type_View 97 Offset 2272 + OpMemberDecorate %type_View 98 Offset 2288 + OpMemberDecorate %type_View 99 Offset 2304 + OpMemberDecorate %type_View 100 Offset 2308 + OpMemberDecorate %type_View 101 Offset 2312 + OpMemberDecorate %type_View 102 Offset 2316 + OpMemberDecorate %type_View 103 Offset 2320 + OpMemberDecorate %type_View 104 Offset 2324 + OpMemberDecorate %type_View 105 Offset 2328 + OpMemberDecorate %type_View 106 Offset 2332 + OpMemberDecorate %type_View 107 Offset 2336 + OpMemberDecorate %type_View 108 Offset 2340 + OpMemberDecorate %type_View 109 Offset 2344 + OpMemberDecorate %type_View 110 Offset 2348 + OpMemberDecorate %type_View 111 Offset 2352 + OpMemberDecorate %type_View 112 Offset 2364 + OpMemberDecorate %type_View 113 Offset 2368 + OpMemberDecorate %type_View 114 Offset 2380 + OpMemberDecorate %type_View 115 Offset 2384 + OpMemberDecorate %type_View 116 Offset 2388 + OpMemberDecorate %type_View 117 Offset 2392 + OpMemberDecorate %type_View 118 Offset 2396 + OpMemberDecorate %type_View 119 Offset 2400 + OpMemberDecorate %type_View 120 Offset 2404 + OpMemberDecorate %type_View 121 Offset 2408 + OpMemberDecorate %type_View 122 Offset 2412 + OpMemberDecorate %type_View 123 Offset 2416 + OpMemberDecorate %type_View 124 Offset 2420 + OpMemberDecorate %type_View 125 Offset 2424 + OpMemberDecorate %type_View 126 Offset 2428 + OpMemberDecorate %type_View 127 Offset 2432 + OpMemberDecorate %type_View 128 Offset 2448 + OpMemberDecorate %type_View 129 Offset 2460 + OpMemberDecorate %type_View 130 Offset 2464 + OpMemberDecorate %type_View 131 Offset 2480 + OpMemberDecorate %type_View 132 Offset 2484 + OpMemberDecorate %type_View 133 Offset 2488 + OpMemberDecorate %type_View 134 Offset 2492 + OpMemberDecorate %type_View 135 Offset 2496 + OpMemberDecorate %type_View 136 Offset 2512 + OpMemberDecorate %type_View 137 Offset 2624 + OpMemberDecorate %type_View 138 Offset 2628 + OpMemberDecorate %type_View 139 Offset 2632 + OpMemberDecorate %type_View 140 Offset 2636 + OpMemberDecorate %type_View 141 Offset 2640 + OpMemberDecorate %type_View 142 Offset 2644 + OpMemberDecorate %type_View 143 Offset 2648 + OpMemberDecorate %type_View 144 Offset 2652 + OpMemberDecorate %type_View 145 Offset 2656 + OpMemberDecorate %type_View 146 Offset 2668 + OpMemberDecorate %type_View 147 Offset 2672 + OpMemberDecorate %type_View 148 Offset 2736 + OpMemberDecorate %type_View 149 Offset 2800 + OpMemberDecorate %type_View 150 Offset 2804 + OpMemberDecorate %type_View 151 Offset 2808 + OpMemberDecorate %type_View 152 Offset 2812 + OpMemberDecorate %type_View 153 Offset 2816 + OpMemberDecorate %type_View 154 Offset 2828 + OpMemberDecorate %type_View 155 Offset 2832 + OpMemberDecorate %type_View 156 Offset 2844 + OpMemberDecorate %type_View 157 Offset 2848 + OpMemberDecorate %type_View 158 Offset 2856 + OpMemberDecorate %type_View 159 Offset 2860 + OpMemberDecorate %type_View 160 Offset 2864 + OpMemberDecorate %type_View 161 Offset 2876 + OpMemberDecorate %type_View 162 Offset 2880 + OpMemberDecorate %type_View 163 Offset 2892 + OpMemberDecorate %type_View 164 Offset 2896 + OpMemberDecorate %type_View 165 Offset 2908 + OpMemberDecorate %type_View 166 Offset 2912 + OpMemberDecorate %type_View 167 Offset 2924 + OpMemberDecorate %type_View 168 Offset 2928 + OpMemberDecorate %type_View 169 Offset 2932 + OpDecorate %type_View Block + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_v4float 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_v4float 0 NonWritable + OpDecorate %type_StructuredBuffer_v4float BufferBlock + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 + %float_2 = OpConstant %float 2 + %62 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %float_0_5 = OpConstant %float 0.5 + %int_3 = OpConstant %int 3 +%float_0_333000004 = OpConstant %float 0.333000004 + %float_1 = OpConstant %float 1 + %67 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_15 = OpConstant %float 15 + %69 = OpConstantComposite %v4float %float_15 %float_15 %float_15 %float_15 +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%FVertexFactoryInterpolantsVSToPS = OpTypeStruct %v4float %v4float %v4float %_arr_v4float_uint_1 %v4float %uint %uint +%FVertexFactoryInterpolantsVSToDS = OpTypeStruct %FVertexFactoryInterpolantsVSToPS +%FSharedBasePassInterpolants = OpTypeStruct +%FBasePassInterpolantsVSToDS = OpTypeStruct %FSharedBasePassInterpolants +%FBasePassVSToDS = OpTypeStruct %FVertexFactoryInterpolantsVSToDS %FBasePassInterpolantsVSToDS %v4float +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%FPNTessellationHSToDS = OpTypeStruct %FBasePassVSToDS %_arr_v4float_uint_3 %v3float %float %float + %v3int = OpTypeVector %int 3 + %73 = OpConstantComposite %v3int %int_0 %int_0 %int_0 + %74 = OpConstantComposite %v3int %int_3 %int_3 %int_3 + %float_0 = OpConstant %float 0 + %76 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %77 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %int_77 = OpConstant %int 77 + %int_6 = OpConstant %int 6 + %int_27 = OpConstant %int 27 + %81 = OpConstantComposite %v3int %int_1 %int_1 %int_1 + %82 = OpConstantComposite %v3int %int_2 %int_2 %int_2 + %uint_26 = OpConstant %uint 26 + %uint_12 = OpConstant %uint 12 + %uint_22 = OpConstant %uint 22 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%_runtimearr_v4float = OpTypeRuntimeArray %v4float +%type_StructuredBuffer_v4float = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_type_StructuredBuffer_v4float = OpTypePointer Uniform %type_StructuredBuffer_v4float +%_arr_v4float_uint_12 = OpTypeArray %v4float %uint_12 +%_ptr_Input__arr_v4float_uint_12 = OpTypePointer Input %_arr_v4float_uint_12 +%_arr__arr_v4float_uint_1_uint_12 = OpTypeArray %_arr_v4float_uint_1 %uint_12 +%_ptr_Input__arr__arr_v4float_uint_1_uint_12 = OpTypePointer Input %_arr__arr_v4float_uint_1_uint_12 +%_arr_uint_uint_12 = OpTypeArray %uint %uint_12 +%_ptr_Input__arr_uint_uint_12 = OpTypePointer Input %_arr_uint_uint_12 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 +%_arr__arr_v4float_uint_1_uint_3 = OpTypeArray %_arr_v4float_uint_1 %uint_3 +%_ptr_Output__arr__arr_v4float_uint_1_uint_3 = OpTypePointer Output %_arr__arr_v4float_uint_1_uint_3 +%_arr_uint_uint_3 = OpTypeArray %uint %uint_3 +%_ptr_Output__arr_uint_uint_3 = OpTypePointer Output %_arr_uint_uint_3 +%_arr__arr_v4float_uint_3_uint_3 = OpTypeArray %_arr_v4float_uint_3 %uint_3 +%_ptr_Output__arr__arr_v4float_uint_3_uint_3 = OpTypePointer Output %_arr__arr_v4float_uint_3_uint_3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Output__arr_v3float_uint_3 = OpTypePointer Output %_arr_v3float_uint_3 +%_ptr_Output__arr_float_uint_3 = OpTypePointer Output %_arr_float_uint_3 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %111 = OpTypeFunction %void +%_arr_FBasePassVSToDS_uint_12 = OpTypeArray %FBasePassVSToDS %uint_12 +%_ptr_Function__arr_FBasePassVSToDS_uint_12 = OpTypePointer Function %_arr_FBasePassVSToDS_uint_12 +%_arr_FPNTessellationHSToDS_uint_3 = OpTypeArray %FPNTessellationHSToDS %uint_3 +%_ptr_Workgroup__arr_FPNTessellationHSToDS_uint_3 = OpTypePointer Workgroup %_arr_FPNTessellationHSToDS_uint_3 +%_ptr_Output__arr_v4float_uint_1 = OpTypePointer Output %_arr_v4float_uint_1 +%_ptr_Output_uint = OpTypePointer Output %uint +%_ptr_Output_v3float = OpTypePointer Output %v3float +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Workgroup_FPNTessellationHSToDS = OpTypePointer Workgroup %FPNTessellationHSToDS + %bool = OpTypeBool +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Workgroup_v4float = OpTypePointer Workgroup %v4float +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Workgroup_float = OpTypePointer Workgroup %float +%mat3v3float = OpTypeMatrix %v3float 3 +%_ptr_Function_FVertexFactoryInterpolantsVSToDS = OpTypePointer Function %FVertexFactoryInterpolantsVSToDS +%_ptr_Function_FVertexFactoryInterpolantsVSToPS = OpTypePointer Function %FVertexFactoryInterpolantsVSToPS +%_ptr_Function_FBasePassVSToDS = OpTypePointer Function %FBasePassVSToDS + %v3bool = OpTypeVector %bool 3 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %View = OpVariable %_ptr_Uniform_type_View Uniform +%View_PrimitiveSceneData = OpVariable %_ptr_Uniform_type_StructuredBuffer_v4float Uniform +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_COLOR0 = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input__arr__arr_v4float_uint_1_uint_12 Input +%in_var_TEXCOORD4 = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_PRIMITIVE_ID = OpVariable %_ptr_Input__arr_uint_uint_12 Input +%in_var_LIGHTMAP_ID = OpVariable %_ptr_Input__arr_uint_uint_12 Input +%in_var_VS_To_DS_Position = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%gl_InvocationID = OpVariable %_ptr_Input_uint Input +%out_var_TEXCOORD10_centroid = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_TEXCOORD11_centroid = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_COLOR0 = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_TEXCOORD0 = OpVariable %_ptr_Output__arr__arr_v4float_uint_1_uint_3 Output +%out_var_TEXCOORD4 = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_PRIMITIVE_ID = OpVariable %_ptr_Output__arr_uint_uint_3 Output +%out_var_LIGHTMAP_ID = OpVariable %_ptr_Output__arr_uint_uint_3 Output +%out_var_VS_To_DS_Position = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_PN_POSITION = OpVariable %_ptr_Output__arr__arr_v4float_uint_3_uint_3 Output +%out_var_PN_DisplacementScales = OpVariable %_ptr_Output__arr_v3float_uint_3 Output +%out_var_PN_TessellationMultiplier = OpVariable %_ptr_Output__arr_float_uint_3 Output +%out_var_PN_WorldDisplacementMultiplier = OpVariable %_ptr_Output__arr_float_uint_3 Output +%gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output +%gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output +%out_var_PN_POSITION9 = OpVariable %_ptr_Output_v4float Output + %133 = OpConstantNull %FSharedBasePassInterpolants + %134 = OpConstantComposite %FBasePassInterpolantsVSToDS %133 +%float_0_333333343 = OpConstant %float 0.333333343 + %136 = OpConstantComposite %v4float %float_0_333333343 %float_0_333333343 %float_0_333333343 %float_0_333333343 + %137 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +%float_0_166666672 = OpConstant %float 0.166666672 + %139 = OpConstantComposite %v4float %float_0_166666672 %float_0_166666672 %float_0_166666672 %float_0_166666672 + %140 = OpUndef %v4float + +; XXX: Original asm used Function here, which is wrong. +; This patches the SPIR-V to be correct. +%temp_var_hullMainRetVal = OpVariable %_ptr_Workgroup__arr_FPNTessellationHSToDS_uint_3 Workgroup + + %MainHull = OpFunction %void None %111 + %141 = OpLabel +%param_var_I = OpVariable %_ptr_Function__arr_FBasePassVSToDS_uint_12 Function + %142 = OpLoad %_arr_v4float_uint_12 %in_var_TEXCOORD10_centroid + %143 = OpLoad %_arr_v4float_uint_12 %in_var_TEXCOORD11_centroid + %144 = OpLoad %_arr_v4float_uint_12 %in_var_COLOR0 + %145 = OpLoad %_arr__arr_v4float_uint_1_uint_12 %in_var_TEXCOORD0 + %146 = OpLoad %_arr_v4float_uint_12 %in_var_TEXCOORD4 + %147 = OpLoad %_arr_uint_uint_12 %in_var_PRIMITIVE_ID + %148 = OpLoad %_arr_uint_uint_12 %in_var_LIGHTMAP_ID + %149 = OpCompositeExtract %v4float %142 0 + %150 = OpCompositeExtract %v4float %143 0 + %151 = OpCompositeExtract %v4float %144 0 + %152 = OpCompositeExtract %_arr_v4float_uint_1 %145 0 + %153 = OpCompositeExtract %v4float %146 0 + %154 = OpCompositeExtract %uint %147 0 + %155 = OpCompositeExtract %uint %148 0 + %156 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %149 %150 %151 %152 %153 %154 %155 + %157 = OpCompositeExtract %v4float %142 1 + %158 = OpCompositeExtract %v4float %143 1 + %159 = OpCompositeExtract %v4float %144 1 + %160 = OpCompositeExtract %_arr_v4float_uint_1 %145 1 + %161 = OpCompositeExtract %v4float %146 1 + %162 = OpCompositeExtract %uint %147 1 + %163 = OpCompositeExtract %uint %148 1 + %164 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %157 %158 %159 %160 %161 %162 %163 + %165 = OpCompositeExtract %v4float %142 2 + %166 = OpCompositeExtract %v4float %143 2 + %167 = OpCompositeExtract %v4float %144 2 + %168 = OpCompositeExtract %_arr_v4float_uint_1 %145 2 + %169 = OpCompositeExtract %v4float %146 2 + %170 = OpCompositeExtract %uint %147 2 + %171 = OpCompositeExtract %uint %148 2 + %172 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %165 %166 %167 %168 %169 %170 %171 + %173 = OpCompositeExtract %v4float %142 3 + %174 = OpCompositeExtract %v4float %143 3 + %175 = OpCompositeExtract %v4float %144 3 + %176 = OpCompositeExtract %_arr_v4float_uint_1 %145 3 + %177 = OpCompositeExtract %v4float %146 3 + %178 = OpCompositeExtract %uint %147 3 + %179 = OpCompositeExtract %uint %148 3 + %180 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %173 %174 %175 %176 %177 %178 %179 + %181 = OpCompositeExtract %v4float %142 4 + %182 = OpCompositeExtract %v4float %143 4 + %183 = OpCompositeExtract %v4float %144 4 + %184 = OpCompositeExtract %_arr_v4float_uint_1 %145 4 + %185 = OpCompositeExtract %v4float %146 4 + %186 = OpCompositeExtract %uint %147 4 + %187 = OpCompositeExtract %uint %148 4 + %188 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %181 %182 %183 %184 %185 %186 %187 + %189 = OpCompositeExtract %v4float %142 5 + %190 = OpCompositeExtract %v4float %143 5 + %191 = OpCompositeExtract %v4float %144 5 + %192 = OpCompositeExtract %_arr_v4float_uint_1 %145 5 + %193 = OpCompositeExtract %v4float %146 5 + %194 = OpCompositeExtract %uint %147 5 + %195 = OpCompositeExtract %uint %148 5 + %196 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %189 %190 %191 %192 %193 %194 %195 + %197 = OpCompositeExtract %v4float %142 6 + %198 = OpCompositeExtract %v4float %143 6 + %199 = OpCompositeExtract %v4float %144 6 + %200 = OpCompositeExtract %_arr_v4float_uint_1 %145 6 + %201 = OpCompositeExtract %v4float %146 6 + %202 = OpCompositeExtract %uint %147 6 + %203 = OpCompositeExtract %uint %148 6 + %204 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %197 %198 %199 %200 %201 %202 %203 + %205 = OpCompositeExtract %v4float %142 7 + %206 = OpCompositeExtract %v4float %143 7 + %207 = OpCompositeExtract %v4float %144 7 + %208 = OpCompositeExtract %_arr_v4float_uint_1 %145 7 + %209 = OpCompositeExtract %v4float %146 7 + %210 = OpCompositeExtract %uint %147 7 + %211 = OpCompositeExtract %uint %148 7 + %212 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %205 %206 %207 %208 %209 %210 %211 + %213 = OpCompositeExtract %v4float %142 8 + %214 = OpCompositeExtract %v4float %143 8 + %215 = OpCompositeExtract %v4float %144 8 + %216 = OpCompositeExtract %_arr_v4float_uint_1 %145 8 + %217 = OpCompositeExtract %v4float %146 8 + %218 = OpCompositeExtract %uint %147 8 + %219 = OpCompositeExtract %uint %148 8 + %220 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %213 %214 %215 %216 %217 %218 %219 + %221 = OpCompositeExtract %v4float %142 9 + %222 = OpCompositeExtract %v4float %143 9 + %223 = OpCompositeExtract %v4float %144 9 + %224 = OpCompositeExtract %_arr_v4float_uint_1 %145 9 + %225 = OpCompositeExtract %v4float %146 9 + %226 = OpCompositeExtract %uint %147 9 + %227 = OpCompositeExtract %uint %148 9 + %228 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %221 %222 %223 %224 %225 %226 %227 + %229 = OpCompositeExtract %v4float %142 10 + %230 = OpCompositeExtract %v4float %143 10 + %231 = OpCompositeExtract %v4float %144 10 + %232 = OpCompositeExtract %_arr_v4float_uint_1 %145 10 + %233 = OpCompositeExtract %v4float %146 10 + %234 = OpCompositeExtract %uint %147 10 + %235 = OpCompositeExtract %uint %148 10 + %236 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %229 %230 %231 %232 %233 %234 %235 + %237 = OpCompositeExtract %v4float %142 11 + %238 = OpCompositeExtract %v4float %143 11 + %239 = OpCompositeExtract %v4float %144 11 + %240 = OpCompositeExtract %_arr_v4float_uint_1 %145 11 + %241 = OpCompositeExtract %v4float %146 11 + %242 = OpCompositeExtract %uint %147 11 + %243 = OpCompositeExtract %uint %148 11 + %244 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %237 %238 %239 %240 %241 %242 %243 + %245 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %156 + %246 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %164 + %247 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %172 + %248 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %180 + %249 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %188 + %250 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %196 + %251 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %204 + %252 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %212 + %253 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %220 + %254 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %228 + %255 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %236 + %256 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %244 + %257 = OpLoad %_arr_v4float_uint_12 %in_var_VS_To_DS_Position + %258 = OpCompositeExtract %v4float %257 0 + %259 = OpCompositeConstruct %FBasePassVSToDS %245 %134 %258 + %260 = OpCompositeExtract %v4float %257 1 + %261 = OpCompositeConstruct %FBasePassVSToDS %246 %134 %260 + %262 = OpCompositeExtract %v4float %257 2 + %263 = OpCompositeConstruct %FBasePassVSToDS %247 %134 %262 + %264 = OpCompositeExtract %v4float %257 3 + %265 = OpCompositeConstruct %FBasePassVSToDS %248 %134 %264 + %266 = OpCompositeExtract %v4float %257 4 + %267 = OpCompositeConstruct %FBasePassVSToDS %249 %134 %266 + %268 = OpCompositeExtract %v4float %257 5 + %269 = OpCompositeConstruct %FBasePassVSToDS %250 %134 %268 + %270 = OpCompositeExtract %v4float %257 6 + %271 = OpCompositeConstruct %FBasePassVSToDS %251 %134 %270 + %272 = OpCompositeExtract %v4float %257 7 + %273 = OpCompositeConstruct %FBasePassVSToDS %252 %134 %272 + %274 = OpCompositeExtract %v4float %257 8 + %275 = OpCompositeConstruct %FBasePassVSToDS %253 %134 %274 + %276 = OpCompositeExtract %v4float %257 9 + %277 = OpCompositeConstruct %FBasePassVSToDS %254 %134 %276 + %278 = OpCompositeExtract %v4float %257 10 + %279 = OpCompositeConstruct %FBasePassVSToDS %255 %134 %278 + %280 = OpCompositeExtract %v4float %257 11 + %281 = OpCompositeConstruct %FBasePassVSToDS %256 %134 %280 + %282 = OpCompositeConstruct %_arr_FBasePassVSToDS_uint_12 %259 %261 %263 %265 %267 %269 %271 %273 %275 %277 %279 %281 + OpStore %param_var_I %282 + %283 = OpLoad %uint %gl_InvocationID + %284 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %283 %int_0 + %285 = OpLoad %FVertexFactoryInterpolantsVSToDS %284 + %286 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %285 0 + %287 = OpCompositeExtract %v4float %286 0 + %288 = OpCompositeExtract %v4float %286 1 + %289 = OpVectorShuffle %v3float %287 %287 0 1 2 + %290 = OpVectorShuffle %v3float %288 %288 0 1 2 + %291 = OpExtInst %v3float %1 Cross %290 %289 + %292 = OpCompositeExtract %float %288 3 + %293 = OpCompositeConstruct %v3float %292 %292 %292 + %294 = OpFMul %v3float %291 %293 + %295 = OpCompositeConstruct %mat3v3float %289 %294 %290 + %296 = OpCompositeExtract %float %288 0 + %297 = OpCompositeExtract %float %288 1 + %298 = OpCompositeExtract %float %288 2 + %299 = OpCompositeConstruct %v4float %296 %297 %298 %float_0 + %300 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToPS %param_var_I %283 %int_0 %int_0 + %301 = OpLoad %FVertexFactoryInterpolantsVSToPS %300 + %302 = OpCompositeExtract %uint %301 5 + %303 = OpIMul %uint %302 %uint_26 + %304 = OpIAdd %uint %303 %uint_22 + %305 = OpAccessChain %_ptr_Uniform_v4float %View_PrimitiveSceneData %int_0 %304 + %306 = OpLoad %v4float %305 + %307 = OpVectorShuffle %v3float %306 %306 0 1 2 + %308 = OpVectorTimesMatrix %v3float %307 %295 + %309 = OpULessThan %bool %283 %uint_2 + %310 = OpIAdd %uint %283 %uint_1 + %311 = OpSelect %uint %309 %310 %uint_0 + %312 = OpIMul %uint %uint_2 %283 + %313 = OpIAdd %uint %uint_3 %312 + %314 = OpIAdd %uint %312 %uint_4 + %315 = OpAccessChain %_ptr_Function_FBasePassVSToDS %param_var_I %283 + %316 = OpLoad %FBasePassVSToDS %315 + %317 = OpAccessChain %_ptr_Function_v4float %param_var_I %283 %int_2 + %318 = OpLoad %v4float %317 + %319 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %311 %int_0 + %320 = OpLoad %FVertexFactoryInterpolantsVSToDS %319 + %321 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %320 0 + %322 = OpCompositeExtract %v4float %321 1 + %323 = OpCompositeExtract %float %322 0 + %324 = OpCompositeExtract %float %322 1 + %325 = OpCompositeExtract %float %322 2 + %326 = OpCompositeConstruct %v4float %323 %324 %325 %float_0 + %327 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %313 %int_0 + %328 = OpLoad %FVertexFactoryInterpolantsVSToDS %327 + %329 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %328 0 + %330 = OpCompositeExtract %v4float %329 1 + %331 = OpCompositeExtract %float %330 0 + %332 = OpCompositeExtract %float %330 1 + %333 = OpCompositeExtract %float %330 2 + %334 = OpCompositeConstruct %v4float %331 %332 %333 %float_0 + %335 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %314 %int_0 + %336 = OpLoad %FVertexFactoryInterpolantsVSToDS %335 + %337 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %336 0 + %338 = OpCompositeExtract %v4float %337 1 + %339 = OpCompositeExtract %float %338 0 + %340 = OpCompositeExtract %float %338 1 + %341 = OpCompositeExtract %float %338 2 + %342 = OpCompositeConstruct %v4float %339 %340 %341 %float_0 + %343 = OpLoad %v4float %317 + %344 = OpAccessChain %_ptr_Function_v4float %param_var_I %311 %int_2 + %345 = OpLoad %v4float %344 + %346 = OpFMul %v4float %62 %343 + %347 = OpFAdd %v4float %346 %345 + %348 = OpFSub %v4float %345 %343 + %349 = OpDot %float %348 %299 + %350 = OpCompositeConstruct %v4float %349 %349 %349 %349 + %351 = OpFMul %v4float %350 %299 + %352 = OpFSub %v4float %347 %351 + %353 = OpFMul %v4float %352 %136 + %354 = OpAccessChain %_ptr_Function_v4float %param_var_I %313 %int_2 + %355 = OpLoad %v4float %354 + %356 = OpAccessChain %_ptr_Function_v4float %param_var_I %314 %int_2 + %357 = OpLoad %v4float %356 + %358 = OpFMul %v4float %62 %355 + %359 = OpFAdd %v4float %358 %357 + %360 = OpFSub %v4float %357 %355 + %361 = OpDot %float %360 %334 + %362 = OpCompositeConstruct %v4float %361 %361 %361 %361 + %363 = OpFMul %v4float %362 %334 + %364 = OpFSub %v4float %359 %363 + %365 = OpFMul %v4float %364 %136 + %366 = OpFAdd %v4float %353 %365 + %367 = OpFMul %v4float %366 %137 + %368 = OpLoad %v4float %344 + %369 = OpLoad %v4float %317 + %370 = OpFMul %v4float %62 %368 + %371 = OpFAdd %v4float %370 %369 + %372 = OpFSub %v4float %369 %368 + %373 = OpDot %float %372 %326 + %374 = OpCompositeConstruct %v4float %373 %373 %373 %373 + %375 = OpFMul %v4float %374 %326 + %376 = OpFSub %v4float %371 %375 + %377 = OpFMul %v4float %376 %136 + %378 = OpLoad %v4float %356 + %379 = OpLoad %v4float %354 + %380 = OpFMul %v4float %62 %378 + %381 = OpFAdd %v4float %380 %379 + %382 = OpFSub %v4float %379 %378 + %383 = OpDot %float %382 %342 + %384 = OpCompositeConstruct %v4float %383 %383 %383 %383 + %385 = OpFMul %v4float %384 %342 + %386 = OpFSub %v4float %381 %385 + %387 = OpFMul %v4float %386 %136 + %388 = OpFAdd %v4float %377 %387 + %389 = OpFMul %v4float %388 %137 + %390 = OpCompositeConstruct %_arr_v4float_uint_3 %318 %367 %389 + %391 = OpCompositeConstruct %FPNTessellationHSToDS %316 %390 %308 %float_1 %float_1 + %392 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %316 0 + %393 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %392 0 + %394 = OpCompositeExtract %v4float %393 0 + %395 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD10_centroid %283 + OpStore %395 %394 + %396 = OpCompositeExtract %v4float %393 1 + %397 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD11_centroid %283 + OpStore %397 %396 + %398 = OpCompositeExtract %v4float %393 2 + %399 = OpAccessChain %_ptr_Output_v4float %out_var_COLOR0 %283 + OpStore %399 %398 + %400 = OpCompositeExtract %_arr_v4float_uint_1 %393 3 + %401 = OpAccessChain %_ptr_Output__arr_v4float_uint_1 %out_var_TEXCOORD0 %283 + OpStore %401 %400 + %402 = OpCompositeExtract %v4float %393 4 + %403 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD4 %283 + OpStore %403 %402 + %404 = OpCompositeExtract %uint %393 5 + %405 = OpAccessChain %_ptr_Output_uint %out_var_PRIMITIVE_ID %283 + OpStore %405 %404 + %406 = OpCompositeExtract %uint %393 6 + %407 = OpAccessChain %_ptr_Output_uint %out_var_LIGHTMAP_ID %283 + OpStore %407 %406 + %408 = OpCompositeExtract %v4float %316 2 + %409 = OpAccessChain %_ptr_Output_v4float %out_var_VS_To_DS_Position %283 + OpStore %409 %408 + %410 = OpAccessChain %_ptr_Output__arr_v4float_uint_3 %out_var_PN_POSITION %283 + OpStore %410 %390 + %411 = OpAccessChain %_ptr_Output_v3float %out_var_PN_DisplacementScales %283 + OpStore %411 %308 + %412 = OpAccessChain %_ptr_Output_float %out_var_PN_TessellationMultiplier %283 + OpStore %412 %float_1 + %413 = OpAccessChain %_ptr_Output_float %out_var_PN_WorldDisplacementMultiplier %283 + OpStore %413 %float_1 + %414 = OpAccessChain %_ptr_Workgroup_FPNTessellationHSToDS %temp_var_hullMainRetVal %283 + OpStore %414 %391 + OpControlBarrier %uint_2 %uint_4 %uint_0 + %415 = OpIEqual %bool %283 %uint_0 + OpSelectionMerge %if_merge None + OpBranchConditional %415 %416 %if_merge + %416 = OpLabel + %417 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_0 + %418 = OpLoad %mat4v4float %417 + %419 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_6 + %420 = OpLoad %mat4v4float %419 + %421 = OpAccessChain %_ptr_Uniform_v3float %View %int_27 + %422 = OpLoad %v3float %421 + %423 = OpAccessChain %_ptr_Uniform_float %View %int_77 + %424 = OpLoad %float %423 + %425 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_0 + %426 = OpLoad %v4float %425 + %427 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_1 + %428 = OpLoad %v4float %427 + %429 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_2 + %430 = OpLoad %v4float %429 + %431 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_0 + %432 = OpLoad %v4float %431 + %433 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_1 + %434 = OpLoad %v4float %433 + %435 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_2 + %436 = OpLoad %v4float %435 + %437 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_0 + %438 = OpLoad %v4float %437 + %439 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_1 + %440 = OpLoad %v4float %439 + %441 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_2 + %442 = OpLoad %v4float %441 + %443 = OpFAdd %v4float %428 %430 + %444 = OpFAdd %v4float %443 %434 + %445 = OpFAdd %v4float %444 %436 + %446 = OpFAdd %v4float %445 %440 + %447 = OpFAdd %v4float %446 %442 + %448 = OpFMul %v4float %447 %139 + %449 = OpFAdd %v4float %438 %432 + %450 = OpFAdd %v4float %449 %426 + %451 = OpFMul %v4float %450 %136 + %452 = OpFSub %v4float %448 %451 + %453 = OpFMul %v4float %452 %137 + %454 = OpFAdd %v4float %448 %453 + %455 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_1 %int_3 + %456 = OpLoad %float %455 + %457 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_2 %int_3 + %458 = OpLoad %float %457 + %459 = OpFAdd %float %456 %458 + %460 = OpFMul %float %float_0_5 %459 + %461 = OpCompositeInsert %v4float %460 %140 0 + %462 = OpLoad %float %457 + %463 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_0 %int_3 + %464 = OpLoad %float %463 + %465 = OpFAdd %float %462 %464 + %466 = OpFMul %float %float_0_5 %465 + %467 = OpCompositeInsert %v4float %466 %461 1 + %468 = OpLoad %float %463 + %469 = OpLoad %float %455 + %470 = OpFAdd %float %468 %469 + %471 = OpFMul %float %float_0_5 %470 + %472 = OpCompositeInsert %v4float %471 %467 2 + %473 = OpLoad %float %463 + %474 = OpLoad %float %455 + %475 = OpFAdd %float %473 %474 + %476 = OpLoad %float %457 + %477 = OpFAdd %float %475 %476 + %478 = OpFMul %float %float_0_333000004 %477 + %479 = OpCompositeInsert %v4float %478 %472 3 + %480 = OpVectorShuffle %v3float %426 %426 0 1 2 + %481 = OpVectorShuffle %v3float %432 %432 0 1 2 + %482 = OpVectorShuffle %v3float %438 %438 0 1 2 + OpBranch %483 + %483 = OpLabel + OpLoopMerge %484 %485 None + OpBranch %486 + %486 = OpLabel + %487 = OpMatrixTimesVector %v4float %420 %76 + %488 = OpCompositeExtract %float %426 0 + %489 = OpCompositeExtract %float %426 1 + %490 = OpCompositeExtract %float %426 2 + %491 = OpCompositeConstruct %v4float %488 %489 %490 %float_1 + %492 = OpMatrixTimesVector %v4float %418 %491 + %493 = OpVectorShuffle %v3float %492 %492 0 1 2 + %494 = OpVectorShuffle %v3float %487 %487 0 1 2 + %495 = OpFSub %v3float %493 %494 + %496 = OpCompositeExtract %float %492 3 + %497 = OpCompositeExtract %float %487 3 + %498 = OpFAdd %float %496 %497 + %499 = OpCompositeConstruct %v3float %498 %498 %498 + %500 = OpFOrdLessThan %v3bool %495 %499 + %501 = OpSelect %v3int %500 %81 %73 + %502 = OpFAdd %v3float %493 %494 + %503 = OpFNegate %float %496 + %504 = OpFSub %float %503 %497 + %505 = OpCompositeConstruct %v3float %504 %504 %504 + %506 = OpFOrdGreaterThan %v3bool %502 %505 + %507 = OpSelect %v3int %506 %81 %73 + %508 = OpIMul %v3int %82 %507 + %509 = OpIAdd %v3int %501 %508 + %510 = OpCompositeExtract %float %432 0 + %511 = OpCompositeExtract %float %432 1 + %512 = OpCompositeExtract %float %432 2 + %513 = OpCompositeConstruct %v4float %510 %511 %512 %float_1 + %514 = OpMatrixTimesVector %v4float %418 %513 + %515 = OpVectorShuffle %v3float %514 %514 0 1 2 + %516 = OpFSub %v3float %515 %494 + %517 = OpCompositeExtract %float %514 3 + %518 = OpFAdd %float %517 %497 + %519 = OpCompositeConstruct %v3float %518 %518 %518 + %520 = OpFOrdLessThan %v3bool %516 %519 + %521 = OpSelect %v3int %520 %81 %73 + %522 = OpFAdd %v3float %515 %494 + %523 = OpFNegate %float %517 + %524 = OpFSub %float %523 %497 + %525 = OpCompositeConstruct %v3float %524 %524 %524 + %526 = OpFOrdGreaterThan %v3bool %522 %525 + %527 = OpSelect %v3int %526 %81 %73 + %528 = OpIMul %v3int %82 %527 + %529 = OpIAdd %v3int %521 %528 + %530 = OpBitwiseOr %v3int %509 %529 + %531 = OpCompositeExtract %float %438 0 + %532 = OpCompositeExtract %float %438 1 + %533 = OpCompositeExtract %float %438 2 + %534 = OpCompositeConstruct %v4float %531 %532 %533 %float_1 + %535 = OpMatrixTimesVector %v4float %418 %534 + %536 = OpVectorShuffle %v3float %535 %535 0 1 2 + %537 = OpFSub %v3float %536 %494 + %538 = OpCompositeExtract %float %535 3 + %539 = OpFAdd %float %538 %497 + %540 = OpCompositeConstruct %v3float %539 %539 %539 + %541 = OpFOrdLessThan %v3bool %537 %540 + %542 = OpSelect %v3int %541 %81 %73 + %543 = OpFAdd %v3float %536 %494 + %544 = OpFNegate %float %538 + %545 = OpFSub %float %544 %497 + %546 = OpCompositeConstruct %v3float %545 %545 %545 + %547 = OpFOrdGreaterThan %v3bool %543 %546 + %548 = OpSelect %v3int %547 %81 %73 + %549 = OpIMul %v3int %82 %548 + %550 = OpIAdd %v3int %542 %549 + %551 = OpBitwiseOr %v3int %530 %550 + %552 = OpINotEqual %v3bool %551 %74 + %553 = OpAny %bool %552 + OpSelectionMerge %554 None + OpBranchConditional %553 %555 %554 + %555 = OpLabel + OpBranch %484 + %554 = OpLabel + %556 = OpFSub %v3float %480 %481 + %557 = OpFSub %v3float %481 %482 + %558 = OpFSub %v3float %482 %480 + %559 = OpFAdd %v3float %480 %481 + %560 = OpFMul %v3float %77 %559 + %561 = OpFSub %v3float %560 %422 + %562 = OpFAdd %v3float %481 %482 + %563 = OpFMul %v3float %77 %562 + %564 = OpFSub %v3float %563 %422 + %565 = OpFAdd %v3float %482 %480 + %566 = OpFMul %v3float %77 %565 + %567 = OpFSub %v3float %566 %422 + %568 = OpDot %float %557 %557 + %569 = OpDot %float %564 %564 + %570 = OpFDiv %float %568 %569 + %571 = OpExtInst %float %1 Sqrt %570 + %572 = OpDot %float %558 %558 + %573 = OpDot %float %567 %567 + %574 = OpFDiv %float %572 %573 + %575 = OpExtInst %float %1 Sqrt %574 + %576 = OpDot %float %556 %556 + %577 = OpDot %float %561 %561 + %578 = OpFDiv %float %576 %577 + %579 = OpExtInst %float %1 Sqrt %578 + %580 = OpCompositeConstruct %v4float %571 %575 %579 %float_1 + %581 = OpFAdd %float %571 %575 + %582 = OpFAdd %float %581 %579 + %583 = OpFMul %float %float_0_333000004 %582 + %584 = OpCompositeInsert %v4float %583 %580 3 + %585 = OpCompositeConstruct %v4float %424 %424 %424 %424 + %586 = OpFMul %v4float %585 %584 + OpBranch %484 + %485 = OpLabel + OpBranch %483 + %484 = OpLabel + %587 = OpPhi %v4float %76 %555 %586 %554 + %588 = OpFMul %v4float %479 %587 + %589 = OpExtInst %v4float %1 FClamp %588 %67 %69 + %590 = OpCompositeExtract %float %589 0 + %591 = OpCompositeExtract %float %589 1 + %592 = OpCompositeExtract %float %589 2 + %593 = OpCompositeExtract %float %589 3 + %594 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_0 + OpStore %594 %590 + %595 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_1 + OpStore %595 %591 + %596 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_2 + OpStore %596 %592 + %597 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %uint_0 + OpStore %597 %593 + OpStore %out_var_PN_POSITION9 %454 + OpBranch %if_merge + %if_merge = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc b/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc new file mode 100644 index 0000000..a146896 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-input-array-access.asm.tesc @@ -0,0 +1,1264 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 607 +; Schema: 0 + OpCapability Tessellation + OpCapability SampledBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %MainHull "main" %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_VS_To_DS_Position %in_var_VS_To_DS_VertexID %gl_InvocationID %out_var_TEXCOORD10_centroid %out_var_TEXCOORD11_centroid %out_var_VS_To_DS_Position %out_var_VS_To_DS_VertexID %out_var_PN_POSITION %out_var_PN_DisplacementScales %out_var_PN_TessellationMultiplier %out_var_PN_WorldDisplacementMultiplier %out_var_PN_DominantVertex %out_var_PN_DominantVertex1 %out_var_PN_DominantVertex2 %out_var_PN_DominantEdge %out_var_PN_DominantEdge1 %out_var_PN_DominantEdge2 %out_var_PN_DominantEdge3 %out_var_PN_DominantEdge4 %out_var_PN_DominantEdge5 %gl_TessLevelOuter %gl_TessLevelInner %out_var_PN_POSITION9 + OpExecutionMode %MainHull Triangles + OpExecutionMode %MainHull SpacingFractionalOdd + OpExecutionMode %MainHull VertexOrderCw + OpExecutionMode %MainHull OutputVertices 3 + OpSource HLSL 600 + OpName %FPNTessellationHSToDS "FPNTessellationHSToDS" + OpMemberName %FPNTessellationHSToDS 0 "PassSpecificData" + OpMemberName %FPNTessellationHSToDS 1 "WorldPosition" + OpMemberName %FPNTessellationHSToDS 2 "DisplacementScale" + OpMemberName %FPNTessellationHSToDS 3 "TessellationMultiplier" + OpMemberName %FPNTessellationHSToDS 4 "WorldDisplacementMultiplier" + OpMemberName %FPNTessellationHSToDS 5 "DominantVertex" + OpMemberName %FPNTessellationHSToDS 6 "DominantEdge" + OpName %FHitProxyVSToDS "FHitProxyVSToDS" + OpMemberName %FHitProxyVSToDS 0 "FactoryInterpolants" + OpMemberName %FHitProxyVSToDS 1 "Position" + OpMemberName %FHitProxyVSToDS 2 "VertexID" + OpName %FVertexFactoryInterpolantsVSToDS "FVertexFactoryInterpolantsVSToDS" + OpMemberName %FVertexFactoryInterpolantsVSToDS 0 "InterpolantsVSToPS" + OpName %FVertexFactoryInterpolantsVSToPS "FVertexFactoryInterpolantsVSToPS" + OpMemberName %FVertexFactoryInterpolantsVSToPS 0 "TangentToWorld0" + OpMemberName %FVertexFactoryInterpolantsVSToPS 1 "TangentToWorld2" + OpName %FHullShaderConstantDominantVertexData "FHullShaderConstantDominantVertexData" + OpMemberName %FHullShaderConstantDominantVertexData 0 "UV" + OpMemberName %FHullShaderConstantDominantVertexData 1 "Normal" + OpMemberName %FHullShaderConstantDominantVertexData 2 "Tangent" + OpName %FHullShaderConstantDominantEdgeData "FHullShaderConstantDominantEdgeData" + OpMemberName %FHullShaderConstantDominantEdgeData 0 "UV0" + OpMemberName %FHullShaderConstantDominantEdgeData 1 "UV1" + OpMemberName %FHullShaderConstantDominantEdgeData 2 "Normal0" + OpMemberName %FHullShaderConstantDominantEdgeData 3 "Normal1" + OpMemberName %FHullShaderConstantDominantEdgeData 4 "Tangent0" + OpMemberName %FHullShaderConstantDominantEdgeData 5 "Tangent1" + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_ClipToWorld" + OpMemberName %type_View 3 "View_TranslatedWorldToView" + OpMemberName %type_View 4 "View_ViewToTranslatedWorld" + OpMemberName %type_View 5 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 6 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 7 "View_ViewToClip" + OpMemberName %type_View 8 "View_ViewToClipNoAA" + OpMemberName %type_View 9 "View_ClipToView" + OpMemberName %type_View 10 "View_ClipToTranslatedWorld" + OpMemberName %type_View 11 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 12 "View_ScreenToWorld" + OpMemberName %type_View 13 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 14 "View_ViewForward" + OpMemberName %type_View 15 "PrePadding_View_908" + OpMemberName %type_View 16 "View_ViewUp" + OpMemberName %type_View 17 "PrePadding_View_924" + OpMemberName %type_View 18 "View_ViewRight" + OpMemberName %type_View 19 "PrePadding_View_940" + OpMemberName %type_View 20 "View_HMDViewNoRollUp" + OpMemberName %type_View 21 "PrePadding_View_956" + OpMemberName %type_View 22 "View_HMDViewNoRollRight" + OpMemberName %type_View 23 "PrePadding_View_972" + OpMemberName %type_View 24 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 25 "View_ScreenPositionScaleBias" + OpMemberName %type_View 26 "View_WorldCameraOrigin" + OpMemberName %type_View 27 "PrePadding_View_1020" + OpMemberName %type_View 28 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 29 "PrePadding_View_1036" + OpMemberName %type_View 30 "View_WorldViewOrigin" + OpMemberName %type_View 31 "PrePadding_View_1052" + OpMemberName %type_View 32 "View_PreViewTranslation" + OpMemberName %type_View 33 "PrePadding_View_1068" + OpMemberName %type_View 34 "View_PrevProjection" + OpMemberName %type_View 35 "View_PrevViewProj" + OpMemberName %type_View 36 "View_PrevViewRotationProj" + OpMemberName %type_View 37 "View_PrevViewToClip" + OpMemberName %type_View 38 "View_PrevClipToView" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 40 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 41 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 42 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 43 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 44 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 45 "PrePadding_View_1724" + OpMemberName %type_View 46 "View_PrevWorldViewOrigin" + OpMemberName %type_View 47 "PrePadding_View_1740" + OpMemberName %type_View 48 "View_PrevPreViewTranslation" + OpMemberName %type_View 49 "PrePadding_View_1756" + OpMemberName %type_View 50 "View_PrevInvViewProj" + OpMemberName %type_View 51 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 52 "View_ClipToPrevClip" + OpMemberName %type_View 53 "View_TemporalAAJitter" + OpMemberName %type_View 54 "View_GlobalClippingPlane" + OpMemberName %type_View 55 "View_FieldOfViewWideAngles" + OpMemberName %type_View 56 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 57 "View_ViewRectMin" + OpMemberName %type_View 58 "View_ViewSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferSizeAndInvSize" + OpMemberName %type_View 60 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 61 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 62 "View_PreExposure" + OpMemberName %type_View 63 "View_OneOverPreExposure" + OpMemberName %type_View 64 "PrePadding_View_2076" + OpMemberName %type_View 65 "View_DiffuseOverrideParameter" + OpMemberName %type_View 66 "View_SpecularOverrideParameter" + OpMemberName %type_View 67 "View_NormalOverrideParameter" + OpMemberName %type_View 68 "View_RoughnessOverrideParameter" + OpMemberName %type_View 69 "View_PrevFrameGameTime" + OpMemberName %type_View 70 "View_PrevFrameRealTime" + OpMemberName %type_View 71 "View_OutOfBoundsMask" + OpMemberName %type_View 72 "PrePadding_View_2148" + OpMemberName %type_View 73 "PrePadding_View_2152" + OpMemberName %type_View 74 "PrePadding_View_2156" + OpMemberName %type_View 75 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 76 "View_CullingSign" + OpMemberName %type_View 77 "View_NearPlane" + OpMemberName %type_View 78 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 79 "View_GameTime" + OpMemberName %type_View 80 "View_RealTime" + OpMemberName %type_View 81 "View_DeltaTime" + OpMemberName %type_View 82 "View_MaterialTextureMipBias" + OpMemberName %type_View 83 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 84 "View_Random" + OpMemberName %type_View 85 "View_FrameNumber" + OpMemberName %type_View 86 "View_StateFrameIndexMod8" + OpMemberName %type_View 87 "View_StateFrameIndex" + OpMemberName %type_View 88 "View_CameraCut" + OpMemberName %type_View 89 "View_UnlitViewmodeMask" + OpMemberName %type_View 90 "PrePadding_View_2228" + OpMemberName %type_View 91 "PrePadding_View_2232" + OpMemberName %type_View 92 "PrePadding_View_2236" + OpMemberName %type_View 93 "View_DirectionalLightColor" + OpMemberName %type_View 94 "View_DirectionalLightDirection" + OpMemberName %type_View 95 "PrePadding_View_2268" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 97 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 98 "View_TemporalAAParams" + OpMemberName %type_View 99 "View_CircleDOFParams" + OpMemberName %type_View 100 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 101 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 102 "View_DepthOfFieldScale" + OpMemberName %type_View 103 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 104 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 105 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 106 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 107 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 108 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 109 "View_GeneralPurposeTweak" + OpMemberName %type_View 110 "View_DemosaicVposOffset" + OpMemberName %type_View 111 "PrePadding_View_2412" + OpMemberName %type_View 112 "View_IndirectLightingColorScale" + OpMemberName %type_View 113 "View_HDR32bppEncodingMode" + OpMemberName %type_View 114 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 115 "View_AtmosphericFogSunPower" + OpMemberName %type_View 116 "View_AtmosphericFogPower" + OpMemberName %type_View 117 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 118 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 119 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 120 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 121 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 122 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 123 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 124 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 125 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 126 "View_AtmosphericFogSunDiscHalfApexAngleRadian" + OpMemberName %type_View 127 "PrePadding_View_2492" + OpMemberName %type_View 128 "View_AtmosphericFogSunDiscLuminance" + OpMemberName %type_View 129 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 130 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 131 "PrePadding_View_2520" + OpMemberName %type_View 132 "PrePadding_View_2524" + OpMemberName %type_View 133 "View_AtmosphericFogSunColor" + OpMemberName %type_View 134 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 135 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 136 "View_AmbientCubemapTint" + OpMemberName %type_View 137 "View_AmbientCubemapIntensity" + OpMemberName %type_View 138 "View_SkyLightParameters" + OpMemberName %type_View 139 "PrePadding_View_2584" + OpMemberName %type_View 140 "PrePadding_View_2588" + OpMemberName %type_View 141 "View_SkyLightColor" + OpMemberName %type_View 142 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 143 "View_MobilePreviewMode" + OpMemberName %type_View 144 "View_HMDEyePaddingOffset" + OpMemberName %type_View 145 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 146 "View_ShowDecalsMask" + OpMemberName %type_View 147 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 148 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 149 "PrePadding_View_2744" + OpMemberName %type_View 150 "PrePadding_View_2748" + OpMemberName %type_View 151 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 152 "View_StereoPassIndex" + OpMemberName %type_View 153 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 154 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 155 "View_GlobalVolumeDimension" + OpMemberName %type_View 156 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 157 "View_MaxGlobalDistance" + OpMemberName %type_View 158 "PrePadding_View_2908" + OpMemberName %type_View 159 "View_CursorPosition" + OpMemberName %type_View 160 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 161 "PrePadding_View_2924" + OpMemberName %type_View 162 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 163 "PrePadding_View_2940" + OpMemberName %type_View 164 "View_VolumetricFogGridZParams" + OpMemberName %type_View 165 "PrePadding_View_2956" + OpMemberName %type_View 166 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 167 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 168 "PrePadding_View_2972" + OpMemberName %type_View 169 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 170 "PrePadding_View_2988" + OpMemberName %type_View 171 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 172 "PrePadding_View_3004" + OpMemberName %type_View 173 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 174 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 175 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 176 "View_StereoIPD" + OpMemberName %type_View 177 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 178 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_Primitive "type.Primitive" + OpMemberName %type_Primitive 0 "Primitive_LocalToWorld" + OpMemberName %type_Primitive 1 "Primitive_InvNonUniformScaleAndDeterminantSign" + OpMemberName %type_Primitive 2 "Primitive_ObjectWorldPositionAndRadius" + OpMemberName %type_Primitive 3 "Primitive_WorldToLocal" + OpMemberName %type_Primitive 4 "Primitive_PreviousLocalToWorld" + OpMemberName %type_Primitive 5 "Primitive_PreviousWorldToLocal" + OpMemberName %type_Primitive 6 "Primitive_ActorWorldPosition" + OpMemberName %type_Primitive 7 "Primitive_UseSingleSampleShadowFromStationaryLights" + OpMemberName %type_Primitive 8 "Primitive_ObjectBounds" + OpMemberName %type_Primitive 9 "Primitive_LpvBiasMultiplier" + OpMemberName %type_Primitive 10 "Primitive_DecalReceiverMask" + OpMemberName %type_Primitive 11 "Primitive_PerObjectGBufferData" + OpMemberName %type_Primitive 12 "Primitive_UseVolumetricLightmapShadowFromStationaryLights" + OpMemberName %type_Primitive 13 "Primitive_DrawsVelocity" + OpMemberName %type_Primitive 14 "Primitive_ObjectOrientation" + OpMemberName %type_Primitive 15 "Primitive_NonUniformScale" + OpMemberName %type_Primitive 16 "Primitive_LocalObjectBoundsMin" + OpMemberName %type_Primitive 17 "Primitive_LightingChannelMask" + OpMemberName %type_Primitive 18 "Primitive_LocalObjectBoundsMax" + OpMemberName %type_Primitive 19 "Primitive_LightmapDataIndex" + OpMemberName %type_Primitive 20 "Primitive_PreSkinnedLocalBounds" + OpMemberName %type_Primitive 21 "Primitive_SingleCaptureIndex" + OpMemberName %type_Primitive 22 "Primitive_OutputVelocity" + OpMemberName %type_Primitive 23 "PrePadding_Primitive_420" + OpMemberName %type_Primitive 24 "PrePadding_Primitive_424" + OpMemberName %type_Primitive 25 "PrePadding_Primitive_428" + OpMemberName %type_Primitive 26 "Primitive_CustomPrimitiveData" + OpName %Primitive "Primitive" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_VS_To_DS_Position "in.var.VS_To_DS_Position" + OpName %in_var_VS_To_DS_VertexID "in.var.VS_To_DS_VertexID" + OpName %out_var_TEXCOORD10_centroid "out.var.TEXCOORD10_centroid" + OpName %out_var_TEXCOORD11_centroid "out.var.TEXCOORD11_centroid" + OpName %out_var_VS_To_DS_Position "out.var.VS_To_DS_Position" + OpName %out_var_VS_To_DS_VertexID "out.var.VS_To_DS_VertexID" + OpName %out_var_PN_POSITION "out.var.PN_POSITION" + OpName %out_var_PN_DisplacementScales "out.var.PN_DisplacementScales" + OpName %out_var_PN_TessellationMultiplier "out.var.PN_TessellationMultiplier" + OpName %out_var_PN_WorldDisplacementMultiplier "out.var.PN_WorldDisplacementMultiplier" + OpName %out_var_PN_DominantVertex "out.var.PN_DominantVertex" + OpName %out_var_PN_DominantVertex1 "out.var.PN_DominantVertex1" + OpName %out_var_PN_DominantVertex2 "out.var.PN_DominantVertex2" + OpName %out_var_PN_DominantEdge "out.var.PN_DominantEdge" + OpName %out_var_PN_DominantEdge1 "out.var.PN_DominantEdge1" + OpName %out_var_PN_DominantEdge2 "out.var.PN_DominantEdge2" + OpName %out_var_PN_DominantEdge3 "out.var.PN_DominantEdge3" + OpName %out_var_PN_DominantEdge4 "out.var.PN_DominantEdge4" + OpName %out_var_PN_DominantEdge5 "out.var.PN_DominantEdge5" + OpName %out_var_PN_POSITION9 "out.var.PN_POSITION9" + OpName %MainHull "MainHull" + OpName %param_var_I "param.var.I" + OpName %temp_var_hullMainRetVal "temp.var.hullMainRetVal" + OpName %if_merge "if.merge" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorateString %in_var_VS_To_DS_VertexID UserSemantic "VS_To_DS_VertexID" + OpDecorate %gl_InvocationID BuiltIn InvocationId + OpDecorateString %gl_InvocationID UserSemantic "SV_OutputControlPointID" + OpDecorateString %out_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %out_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %out_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorateString %out_var_VS_To_DS_VertexID UserSemantic "VS_To_DS_VertexID" + OpDecorateString %out_var_PN_POSITION UserSemantic "PN_POSITION" + OpDecorateString %out_var_PN_DisplacementScales UserSemantic "PN_DisplacementScales" + OpDecorateString %out_var_PN_TessellationMultiplier UserSemantic "PN_TessellationMultiplier" + OpDecorateString %out_var_PN_WorldDisplacementMultiplier UserSemantic "PN_WorldDisplacementMultiplier" + OpDecorateString %out_var_PN_DominantVertex UserSemantic "PN_DominantVertex" + OpDecorateString %out_var_PN_DominantVertex1 UserSemantic "PN_DominantVertex" + OpDecorateString %out_var_PN_DominantVertex2 UserSemantic "PN_DominantVertex" + OpDecorateString %out_var_PN_DominantEdge UserSemantic "PN_DominantEdge" + OpDecorateString %out_var_PN_DominantEdge1 UserSemantic "PN_DominantEdge" + OpDecorateString %out_var_PN_DominantEdge2 UserSemantic "PN_DominantEdge" + OpDecorateString %out_var_PN_DominantEdge3 UserSemantic "PN_DominantEdge" + OpDecorateString %out_var_PN_DominantEdge4 UserSemantic "PN_DominantEdge" + OpDecorateString %out_var_PN_DominantEdge5 UserSemantic "PN_DominantEdge" + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorateString %gl_TessLevelOuter UserSemantic "SV_TessFactor" + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorateString %gl_TessLevelInner UserSemantic "SV_InsideTessFactor" + OpDecorate %gl_TessLevelInner Patch + OpDecorateString %out_var_PN_POSITION9 UserSemantic "PN_POSITION9" + OpDecorate %out_var_PN_POSITION9 Patch + OpDecorate %in_var_TEXCOORD10_centroid Location 0 + OpDecorate %in_var_TEXCOORD11_centroid Location 1 + OpDecorate %in_var_VS_To_DS_Position Location 2 + OpDecorate %in_var_VS_To_DS_VertexID Location 3 + OpDecorate %out_var_PN_DisplacementScales Location 0 + OpDecorate %out_var_PN_DominantEdge Location 1 + OpDecorate %out_var_PN_DominantEdge1 Location 2 + OpDecorate %out_var_PN_DominantEdge2 Location 3 + OpDecorate %out_var_PN_DominantEdge3 Location 4 + OpDecorate %out_var_PN_DominantEdge4 Location 5 + OpDecorate %out_var_PN_DominantEdge5 Location 6 + OpDecorate %out_var_PN_DominantVertex Location 7 + OpDecorate %out_var_PN_DominantVertex1 Location 8 + OpDecorate %out_var_PN_DominantVertex2 Location 9 + OpDecorate %out_var_PN_POSITION Location 10 + OpDecorate %out_var_PN_POSITION9 Location 13 + OpDecorate %out_var_PN_TessellationMultiplier Location 14 + OpDecorate %out_var_PN_WorldDisplacementMultiplier Location 15 + OpDecorate %out_var_TEXCOORD10_centroid Location 16 + OpDecorate %out_var_TEXCOORD11_centroid Location 17 + OpDecorate %out_var_VS_To_DS_Position Location 18 + OpDecorate %out_var_VS_To_DS_VertexID Location 19 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %Primitive DescriptorSet 0 + OpDecorate %Primitive Binding 1 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 13 MatrixStride 16 + OpMemberDecorate %type_View 13 ColMajor + OpMemberDecorate %type_View 14 Offset 896 + OpMemberDecorate %type_View 15 Offset 908 + OpMemberDecorate %type_View 16 Offset 912 + OpMemberDecorate %type_View 17 Offset 924 + OpMemberDecorate %type_View 18 Offset 928 + OpMemberDecorate %type_View 19 Offset 940 + OpMemberDecorate %type_View 20 Offset 944 + OpMemberDecorate %type_View 21 Offset 956 + OpMemberDecorate %type_View 22 Offset 960 + OpMemberDecorate %type_View 23 Offset 972 + OpMemberDecorate %type_View 24 Offset 976 + OpMemberDecorate %type_View 25 Offset 992 + OpMemberDecorate %type_View 26 Offset 1008 + OpMemberDecorate %type_View 27 Offset 1020 + OpMemberDecorate %type_View 28 Offset 1024 + OpMemberDecorate %type_View 29 Offset 1036 + OpMemberDecorate %type_View 30 Offset 1040 + OpMemberDecorate %type_View 31 Offset 1052 + OpMemberDecorate %type_View 32 Offset 1056 + OpMemberDecorate %type_View 33 Offset 1068 + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 43 MatrixStride 16 + OpMemberDecorate %type_View 43 ColMajor + OpMemberDecorate %type_View 44 Offset 1712 + OpMemberDecorate %type_View 45 Offset 1724 + OpMemberDecorate %type_View 46 Offset 1728 + OpMemberDecorate %type_View 47 Offset 1740 + OpMemberDecorate %type_View 48 Offset 1744 + OpMemberDecorate %type_View 49 Offset 1756 + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 52 MatrixStride 16 + OpMemberDecorate %type_View 52 ColMajor + OpMemberDecorate %type_View 53 Offset 1952 + OpMemberDecorate %type_View 54 Offset 1968 + OpMemberDecorate %type_View 55 Offset 1984 + OpMemberDecorate %type_View 56 Offset 1992 + OpMemberDecorate %type_View 57 Offset 2000 + OpMemberDecorate %type_View 58 Offset 2016 + OpMemberDecorate %type_View 59 Offset 2032 + OpMemberDecorate %type_View 60 Offset 2048 + OpMemberDecorate %type_View 61 Offset 2064 + OpMemberDecorate %type_View 62 Offset 2068 + OpMemberDecorate %type_View 63 Offset 2072 + OpMemberDecorate %type_View 64 Offset 2076 + OpMemberDecorate %type_View 65 Offset 2080 + OpMemberDecorate %type_View 66 Offset 2096 + OpMemberDecorate %type_View 67 Offset 2112 + OpMemberDecorate %type_View 68 Offset 2128 + OpMemberDecorate %type_View 69 Offset 2136 + OpMemberDecorate %type_View 70 Offset 2140 + OpMemberDecorate %type_View 71 Offset 2144 + OpMemberDecorate %type_View 72 Offset 2148 + OpMemberDecorate %type_View 73 Offset 2152 + OpMemberDecorate %type_View 74 Offset 2156 + OpMemberDecorate %type_View 75 Offset 2160 + OpMemberDecorate %type_View 76 Offset 2172 + OpMemberDecorate %type_View 77 Offset 2176 + OpMemberDecorate %type_View 78 Offset 2180 + OpMemberDecorate %type_View 79 Offset 2184 + OpMemberDecorate %type_View 80 Offset 2188 + OpMemberDecorate %type_View 81 Offset 2192 + OpMemberDecorate %type_View 82 Offset 2196 + OpMemberDecorate %type_View 83 Offset 2200 + OpMemberDecorate %type_View 84 Offset 2204 + OpMemberDecorate %type_View 85 Offset 2208 + OpMemberDecorate %type_View 86 Offset 2212 + OpMemberDecorate %type_View 87 Offset 2216 + OpMemberDecorate %type_View 88 Offset 2220 + OpMemberDecorate %type_View 89 Offset 2224 + OpMemberDecorate %type_View 90 Offset 2228 + OpMemberDecorate %type_View 91 Offset 2232 + OpMemberDecorate %type_View 92 Offset 2236 + OpMemberDecorate %type_View 93 Offset 2240 + OpMemberDecorate %type_View 94 Offset 2256 + OpMemberDecorate %type_View 95 Offset 2268 + OpMemberDecorate %type_View 96 Offset 2272 + OpMemberDecorate %type_View 97 Offset 2304 + OpMemberDecorate %type_View 98 Offset 2336 + OpMemberDecorate %type_View 99 Offset 2352 + OpMemberDecorate %type_View 100 Offset 2368 + OpMemberDecorate %type_View 101 Offset 2372 + OpMemberDecorate %type_View 102 Offset 2376 + OpMemberDecorate %type_View 103 Offset 2380 + OpMemberDecorate %type_View 104 Offset 2384 + OpMemberDecorate %type_View 105 Offset 2388 + OpMemberDecorate %type_View 106 Offset 2392 + OpMemberDecorate %type_View 107 Offset 2396 + OpMemberDecorate %type_View 108 Offset 2400 + OpMemberDecorate %type_View 109 Offset 2404 + OpMemberDecorate %type_View 110 Offset 2408 + OpMemberDecorate %type_View 111 Offset 2412 + OpMemberDecorate %type_View 112 Offset 2416 + OpMemberDecorate %type_View 113 Offset 2428 + OpMemberDecorate %type_View 114 Offset 2432 + OpMemberDecorate %type_View 115 Offset 2444 + OpMemberDecorate %type_View 116 Offset 2448 + OpMemberDecorate %type_View 117 Offset 2452 + OpMemberDecorate %type_View 118 Offset 2456 + OpMemberDecorate %type_View 119 Offset 2460 + OpMemberDecorate %type_View 120 Offset 2464 + OpMemberDecorate %type_View 121 Offset 2468 + OpMemberDecorate %type_View 122 Offset 2472 + OpMemberDecorate %type_View 123 Offset 2476 + OpMemberDecorate %type_View 124 Offset 2480 + OpMemberDecorate %type_View 125 Offset 2484 + OpMemberDecorate %type_View 126 Offset 2488 + OpMemberDecorate %type_View 127 Offset 2492 + OpMemberDecorate %type_View 128 Offset 2496 + OpMemberDecorate %type_View 129 Offset 2512 + OpMemberDecorate %type_View 130 Offset 2516 + OpMemberDecorate %type_View 131 Offset 2520 + OpMemberDecorate %type_View 132 Offset 2524 + OpMemberDecorate %type_View 133 Offset 2528 + OpMemberDecorate %type_View 134 Offset 2544 + OpMemberDecorate %type_View 135 Offset 2556 + OpMemberDecorate %type_View 136 Offset 2560 + OpMemberDecorate %type_View 137 Offset 2576 + OpMemberDecorate %type_View 138 Offset 2580 + OpMemberDecorate %type_View 139 Offset 2584 + OpMemberDecorate %type_View 140 Offset 2588 + OpMemberDecorate %type_View 141 Offset 2592 + OpMemberDecorate %type_View 142 Offset 2608 + OpMemberDecorate %type_View 143 Offset 2720 + OpMemberDecorate %type_View 144 Offset 2724 + OpMemberDecorate %type_View 145 Offset 2728 + OpMemberDecorate %type_View 146 Offset 2732 + OpMemberDecorate %type_View 147 Offset 2736 + OpMemberDecorate %type_View 148 Offset 2740 + OpMemberDecorate %type_View 149 Offset 2744 + OpMemberDecorate %type_View 150 Offset 2748 + OpMemberDecorate %type_View 151 Offset 2752 + OpMemberDecorate %type_View 152 Offset 2764 + OpMemberDecorate %type_View 153 Offset 2768 + OpMemberDecorate %type_View 154 Offset 2832 + OpMemberDecorate %type_View 155 Offset 2896 + OpMemberDecorate %type_View 156 Offset 2900 + OpMemberDecorate %type_View 157 Offset 2904 + OpMemberDecorate %type_View 158 Offset 2908 + OpMemberDecorate %type_View 159 Offset 2912 + OpMemberDecorate %type_View 160 Offset 2920 + OpMemberDecorate %type_View 161 Offset 2924 + OpMemberDecorate %type_View 162 Offset 2928 + OpMemberDecorate %type_View 163 Offset 2940 + OpMemberDecorate %type_View 164 Offset 2944 + OpMemberDecorate %type_View 165 Offset 2956 + OpMemberDecorate %type_View 166 Offset 2960 + OpMemberDecorate %type_View 167 Offset 2968 + OpMemberDecorate %type_View 168 Offset 2972 + OpMemberDecorate %type_View 169 Offset 2976 + OpMemberDecorate %type_View 170 Offset 2988 + OpMemberDecorate %type_View 171 Offset 2992 + OpMemberDecorate %type_View 172 Offset 3004 + OpMemberDecorate %type_View 173 Offset 3008 + OpMemberDecorate %type_View 174 Offset 3020 + OpMemberDecorate %type_View 175 Offset 3024 + OpMemberDecorate %type_View 176 Offset 3036 + OpMemberDecorate %type_View 177 Offset 3040 + OpMemberDecorate %type_View 178 Offset 3044 + OpDecorate %type_View Block + OpMemberDecorate %type_Primitive 0 Offset 0 + OpMemberDecorate %type_Primitive 0 MatrixStride 16 + OpMemberDecorate %type_Primitive 0 ColMajor + OpMemberDecorate %type_Primitive 1 Offset 64 + OpMemberDecorate %type_Primitive 2 Offset 80 + OpMemberDecorate %type_Primitive 3 Offset 96 + OpMemberDecorate %type_Primitive 3 MatrixStride 16 + OpMemberDecorate %type_Primitive 3 ColMajor + OpMemberDecorate %type_Primitive 4 Offset 160 + OpMemberDecorate %type_Primitive 4 MatrixStride 16 + OpMemberDecorate %type_Primitive 4 ColMajor + OpMemberDecorate %type_Primitive 5 Offset 224 + OpMemberDecorate %type_Primitive 5 MatrixStride 16 + OpMemberDecorate %type_Primitive 5 ColMajor + OpMemberDecorate %type_Primitive 6 Offset 288 + OpMemberDecorate %type_Primitive 7 Offset 300 + OpMemberDecorate %type_Primitive 8 Offset 304 + OpMemberDecorate %type_Primitive 9 Offset 316 + OpMemberDecorate %type_Primitive 10 Offset 320 + OpMemberDecorate %type_Primitive 11 Offset 324 + OpMemberDecorate %type_Primitive 12 Offset 328 + OpMemberDecorate %type_Primitive 13 Offset 332 + OpMemberDecorate %type_Primitive 14 Offset 336 + OpMemberDecorate %type_Primitive 15 Offset 352 + OpMemberDecorate %type_Primitive 16 Offset 368 + OpMemberDecorate %type_Primitive 17 Offset 380 + OpMemberDecorate %type_Primitive 18 Offset 384 + OpMemberDecorate %type_Primitive 19 Offset 396 + OpMemberDecorate %type_Primitive 20 Offset 400 + OpMemberDecorate %type_Primitive 21 Offset 412 + OpMemberDecorate %type_Primitive 22 Offset 416 + OpMemberDecorate %type_Primitive 23 Offset 420 + OpMemberDecorate %type_Primitive 24 Offset 424 + OpMemberDecorate %type_Primitive 25 Offset 428 + OpMemberDecorate %type_Primitive 26 Offset 432 + OpDecorate %type_Primitive Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %v2int = OpTypeVector %int 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 + %float_2 = OpConstant %float 2 + %63 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %float_0_5 = OpConstant %float 0.5 + %int_3 = OpConstant %int 3 +%float_0_333000004 = OpConstant %float 0.333000004 + %float_1 = OpConstant %float 1 + %68 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_15 = OpConstant %float 15 + %70 = OpConstantComposite %v4float %float_15 %float_15 %float_15 %float_15 +%FVertexFactoryInterpolantsVSToPS = OpTypeStruct %v4float %v4float +%FVertexFactoryInterpolantsVSToDS = OpTypeStruct %FVertexFactoryInterpolantsVSToPS +%FHitProxyVSToDS = OpTypeStruct %FVertexFactoryInterpolantsVSToDS %v4float %uint +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%FHullShaderConstantDominantVertexData = OpTypeStruct %v2float %v4float %v3float +%FHullShaderConstantDominantEdgeData = OpTypeStruct %v2float %v2float %v4float %v4float %v3float %v3float +%FPNTessellationHSToDS = OpTypeStruct %FHitProxyVSToDS %_arr_v4float_uint_3 %v3float %float %float %FHullShaderConstantDominantVertexData %FHullShaderConstantDominantEdgeData + %uint_9 = OpConstant %uint 9 + %v3int = OpTypeVector %int 3 + %74 = OpConstantComposite %v3int %int_0 %int_0 %int_0 + %75 = OpConstantComposite %v3int %int_3 %int_3 %int_3 + %float_0 = OpConstant %float 0 + %77 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %78 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %int_78 = OpConstant %int 78 + %int_15 = OpConstant %int 15 + %int_7 = OpConstant %int 7 + %int_28 = OpConstant %int 28 + %83 = OpConstantComposite %v3int %int_1 %int_1 %int_1 + %84 = OpConstantComposite %v3int %int_2 %int_2 %int_2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %uint %uint %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v2int %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_Primitive = OpTypeStruct %mat4v4float %v4float %v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %float %float %float %float %v4float %v4float %v3float %uint %v3float %uint %v3float %int %uint %uint %uint %uint %_arr_v4float_uint_4 +%_ptr_Uniform_type_Primitive = OpTypePointer Uniform %type_Primitive + %uint_12 = OpConstant %uint 12 +%_arr_v4float_uint_12 = OpTypeArray %v4float %uint_12 +%_ptr_Input__arr_v4float_uint_12 = OpTypePointer Input %_arr_v4float_uint_12 +%_arr_uint_uint_12 = OpTypeArray %uint %uint_12 +%_ptr_Input__arr_uint_uint_12 = OpTypePointer Input %_arr_uint_uint_12 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 +%_arr_uint_uint_3 = OpTypeArray %uint %uint_3 +%_ptr_Output__arr_uint_uint_3 = OpTypePointer Output %_arr_uint_uint_3 +%_arr__arr_v4float_uint_3_uint_3 = OpTypeArray %_arr_v4float_uint_3 %uint_3 +%_ptr_Output__arr__arr_v4float_uint_3_uint_3 = OpTypePointer Output %_arr__arr_v4float_uint_3_uint_3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Output__arr_v3float_uint_3 = OpTypePointer Output %_arr_v3float_uint_3 +%_ptr_Output__arr_float_uint_3 = OpTypePointer Output %_arr_float_uint_3 +%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3 +%_ptr_Output__arr_v2float_uint_3 = OpTypePointer Output %_arr_v2float_uint_3 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %109 = OpTypeFunction %void +%_arr_FHitProxyVSToDS_uint_12 = OpTypeArray %FHitProxyVSToDS %uint_12 +%_ptr_Function__arr_FHitProxyVSToDS_uint_12 = OpTypePointer Function %_arr_FHitProxyVSToDS_uint_12 +%_arr_FPNTessellationHSToDS_uint_3 = OpTypeArray %FPNTessellationHSToDS %uint_3 +%_ptr_Function__arr_FPNTessellationHSToDS_uint_3 = OpTypePointer Function %_arr_FPNTessellationHSToDS_uint_3 +%_ptr_Workgroup__arr_FPNTessellationHSToDS_uint_3 = OpTypePointer Workgroup %_arr_FPNTessellationHSToDS_uint_3 +%_ptr_Output_uint = OpTypePointer Output %uint +%_ptr_Output_v3float = OpTypePointer Output %v3float +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Output_v2float = OpTypePointer Output %v2float +%_ptr_Function_FPNTessellationHSToDS = OpTypePointer Function %FPNTessellationHSToDS +%_ptr_Workgroup_FPNTessellationHSToDS = OpTypePointer Workgroup %FPNTessellationHSToDS + %bool = OpTypeBool +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Workgroup_v4float = OpTypePointer Workgroup %v4float +%_ptr_Workgroup_float = OpTypePointer Workgroup %float +%mat3v3float = OpTypeMatrix %v3float 3 +%_ptr_Function_FVertexFactoryInterpolantsVSToDS = OpTypePointer Function %FVertexFactoryInterpolantsVSToDS +%_ptr_Function_FHitProxyVSToDS = OpTypePointer Function %FHitProxyVSToDS + %v3bool = OpTypeVector %bool 3 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %View = OpVariable %_ptr_Uniform_type_View Uniform + %Primitive = OpVariable %_ptr_Uniform_type_Primitive Uniform +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_VS_To_DS_Position = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_VS_To_DS_VertexID = OpVariable %_ptr_Input__arr_uint_uint_12 Input +%gl_InvocationID = OpVariable %_ptr_Input_uint Input +%out_var_TEXCOORD10_centroid = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_TEXCOORD11_centroid = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_VS_To_DS_Position = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_VS_To_DS_VertexID = OpVariable %_ptr_Output__arr_uint_uint_3 Output +%out_var_PN_POSITION = OpVariable %_ptr_Output__arr__arr_v4float_uint_3_uint_3 Output +%out_var_PN_DisplacementScales = OpVariable %_ptr_Output__arr_v3float_uint_3 Output +%out_var_PN_TessellationMultiplier = OpVariable %_ptr_Output__arr_float_uint_3 Output +%out_var_PN_WorldDisplacementMultiplier = OpVariable %_ptr_Output__arr_float_uint_3 Output +%out_var_PN_DominantVertex = OpVariable %_ptr_Output__arr_v2float_uint_3 Output +%out_var_PN_DominantVertex1 = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_PN_DominantVertex2 = OpVariable %_ptr_Output__arr_v3float_uint_3 Output +%out_var_PN_DominantEdge = OpVariable %_ptr_Output__arr_v2float_uint_3 Output +%out_var_PN_DominantEdge1 = OpVariable %_ptr_Output__arr_v2float_uint_3 Output +%out_var_PN_DominantEdge2 = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_PN_DominantEdge3 = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_PN_DominantEdge4 = OpVariable %_ptr_Output__arr_v3float_uint_3 Output +%out_var_PN_DominantEdge5 = OpVariable %_ptr_Output__arr_v3float_uint_3 Output +%gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output +%gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output +%out_var_PN_POSITION9 = OpVariable %_ptr_Output_v4float Output + %130 = OpConstantNull %v2float +%float_0_333333343 = OpConstant %float 0.333333343 + %132 = OpConstantComposite %v4float %float_0_333333343 %float_0_333333343 %float_0_333333343 %float_0_333333343 + %133 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +%float_0_166666672 = OpConstant %float 0.166666672 + %135 = OpConstantComposite %v4float %float_0_166666672 %float_0_166666672 %float_0_166666672 %float_0_166666672 + %136 = OpUndef %v4float + +; XXX: Original asm used Function here, which is wrong. +; This patches the SPIR-V to be correct. +%temp_var_hullMainRetVal = OpVariable %_ptr_Workgroup__arr_FPNTessellationHSToDS_uint_3 Workgroup + + %MainHull = OpFunction %void None %109 + %137 = OpLabel +%param_var_I = OpVariable %_ptr_Function__arr_FHitProxyVSToDS_uint_12 Function + %138 = OpLoad %_arr_v4float_uint_12 %in_var_TEXCOORD10_centroid + %139 = OpLoad %_arr_v4float_uint_12 %in_var_TEXCOORD11_centroid + %140 = OpCompositeExtract %v4float %138 0 + %141 = OpCompositeExtract %v4float %139 0 + %142 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %140 %141 + %143 = OpCompositeExtract %v4float %138 1 + %144 = OpCompositeExtract %v4float %139 1 + %145 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %143 %144 + %146 = OpCompositeExtract %v4float %138 2 + %147 = OpCompositeExtract %v4float %139 2 + %148 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %146 %147 + %149 = OpCompositeExtract %v4float %138 3 + %150 = OpCompositeExtract %v4float %139 3 + %151 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %149 %150 + %152 = OpCompositeExtract %v4float %138 4 + %153 = OpCompositeExtract %v4float %139 4 + %154 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %152 %153 + %155 = OpCompositeExtract %v4float %138 5 + %156 = OpCompositeExtract %v4float %139 5 + %157 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %155 %156 + %158 = OpCompositeExtract %v4float %138 6 + %159 = OpCompositeExtract %v4float %139 6 + %160 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %158 %159 + %161 = OpCompositeExtract %v4float %138 7 + %162 = OpCompositeExtract %v4float %139 7 + %163 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %161 %162 + %164 = OpCompositeExtract %v4float %138 8 + %165 = OpCompositeExtract %v4float %139 8 + %166 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %164 %165 + %167 = OpCompositeExtract %v4float %138 9 + %168 = OpCompositeExtract %v4float %139 9 + %169 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %167 %168 + %170 = OpCompositeExtract %v4float %138 10 + %171 = OpCompositeExtract %v4float %139 10 + %172 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %170 %171 + %173 = OpCompositeExtract %v4float %138 11 + %174 = OpCompositeExtract %v4float %139 11 + %175 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %173 %174 + %176 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %142 + %177 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %145 + %178 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %148 + %179 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %151 + %180 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %154 + %181 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %157 + %182 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %160 + %183 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %163 + %184 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %166 + %185 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %169 + %186 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %172 + %187 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %175 + %188 = OpLoad %_arr_v4float_uint_12 %in_var_VS_To_DS_Position + %189 = OpLoad %_arr_uint_uint_12 %in_var_VS_To_DS_VertexID + %190 = OpCompositeExtract %v4float %188 0 + %191 = OpCompositeExtract %uint %189 0 + %192 = OpCompositeConstruct %FHitProxyVSToDS %176 %190 %191 + %193 = OpCompositeExtract %v4float %188 1 + %194 = OpCompositeExtract %uint %189 1 + %195 = OpCompositeConstruct %FHitProxyVSToDS %177 %193 %194 + %196 = OpCompositeExtract %v4float %188 2 + %197 = OpCompositeExtract %uint %189 2 + %198 = OpCompositeConstruct %FHitProxyVSToDS %178 %196 %197 + %199 = OpCompositeExtract %v4float %188 3 + %200 = OpCompositeExtract %uint %189 3 + %201 = OpCompositeConstruct %FHitProxyVSToDS %179 %199 %200 + %202 = OpCompositeExtract %v4float %188 4 + %203 = OpCompositeExtract %uint %189 4 + %204 = OpCompositeConstruct %FHitProxyVSToDS %180 %202 %203 + %205 = OpCompositeExtract %v4float %188 5 + %206 = OpCompositeExtract %uint %189 5 + %207 = OpCompositeConstruct %FHitProxyVSToDS %181 %205 %206 + %208 = OpCompositeExtract %v4float %188 6 + %209 = OpCompositeExtract %uint %189 6 + %210 = OpCompositeConstruct %FHitProxyVSToDS %182 %208 %209 + %211 = OpCompositeExtract %v4float %188 7 + %212 = OpCompositeExtract %uint %189 7 + %213 = OpCompositeConstruct %FHitProxyVSToDS %183 %211 %212 + %214 = OpCompositeExtract %v4float %188 8 + %215 = OpCompositeExtract %uint %189 8 + %216 = OpCompositeConstruct %FHitProxyVSToDS %184 %214 %215 + %217 = OpCompositeExtract %v4float %188 9 + %218 = OpCompositeExtract %uint %189 9 + %219 = OpCompositeConstruct %FHitProxyVSToDS %185 %217 %218 + %220 = OpCompositeExtract %v4float %188 10 + %221 = OpCompositeExtract %uint %189 10 + %222 = OpCompositeConstruct %FHitProxyVSToDS %186 %220 %221 + %223 = OpCompositeExtract %v4float %188 11 + %224 = OpCompositeExtract %uint %189 11 + %225 = OpCompositeConstruct %FHitProxyVSToDS %187 %223 %224 + %226 = OpCompositeConstruct %_arr_FHitProxyVSToDS_uint_12 %192 %195 %198 %201 %204 %207 %210 %213 %216 %219 %222 %225 + OpStore %param_var_I %226 + %227 = OpLoad %uint %gl_InvocationID + %228 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %227 %int_0 + %229 = OpLoad %FVertexFactoryInterpolantsVSToDS %228 + %230 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %229 0 + %231 = OpCompositeExtract %v4float %230 0 + %232 = OpCompositeExtract %v4float %230 1 + %233 = OpVectorShuffle %v3float %231 %231 0 1 2 + %234 = OpVectorShuffle %v3float %232 %232 0 1 2 + %235 = OpExtInst %v3float %1 Cross %234 %233 + %236 = OpCompositeExtract %float %232 3 + %237 = OpCompositeConstruct %v3float %236 %236 %236 + %238 = OpFMul %v3float %235 %237 + %239 = OpCompositeConstruct %mat3v3float %233 %238 %234 + %240 = OpCompositeExtract %float %232 0 + %241 = OpCompositeExtract %float %232 1 + %242 = OpCompositeExtract %float %232 2 + %243 = OpCompositeConstruct %v4float %240 %241 %242 %float_0 + %244 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_15 + %245 = OpLoad %v4float %244 + %246 = OpVectorShuffle %v3float %245 %245 0 1 2 + %247 = OpVectorTimesMatrix %v3float %246 %239 + %248 = OpULessThan %bool %227 %uint_2 + %249 = OpIAdd %uint %227 %uint_1 + %250 = OpSelect %uint %248 %249 %uint_0 + %251 = OpIMul %uint %uint_2 %227 + %252 = OpIAdd %uint %uint_3 %251 + %253 = OpIAdd %uint %251 %uint_4 + %254 = OpAccessChain %_ptr_Function_FHitProxyVSToDS %param_var_I %227 + %255 = OpLoad %FHitProxyVSToDS %254 + %256 = OpAccessChain %_ptr_Function_v4float %param_var_I %227 %int_1 + %257 = OpLoad %v4float %256 + %258 = OpULessThan %bool %250 %uint_2 + %259 = OpIAdd %uint %250 %uint_1 + %260 = OpSelect %uint %258 %259 %uint_0 + %261 = OpIMul %uint %uint_2 %250 + %262 = OpIAdd %uint %uint_3 %261 + %263 = OpIAdd %uint %261 %uint_4 + %264 = OpIAdd %uint %uint_9 %227 + %265 = OpAccessChain %_ptr_Function_FHitProxyVSToDS %param_var_I %264 + %266 = OpLoad %FHitProxyVSToDS %265 + %267 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %266 0 + %268 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %267 0 + %269 = OpCompositeExtract %v4float %268 0 + %270 = OpCompositeExtract %v4float %268 1 + %271 = OpVectorShuffle %v3float %269 %269 0 1 2 + %272 = OpCompositeExtract %float %270 0 + %273 = OpCompositeExtract %float %270 1 + %274 = OpCompositeExtract %float %270 2 + %275 = OpCompositeConstruct %v4float %272 %273 %274 %float_0 + %276 = OpAccessChain %_ptr_Function_FHitProxyVSToDS %param_var_I %250 + %277 = OpLoad %FHitProxyVSToDS %276 + %278 = OpCompositeExtract %uint %277 2 + %279 = OpAccessChain %_ptr_Function_FHitProxyVSToDS %param_var_I %260 + %280 = OpLoad %FHitProxyVSToDS %279 + %281 = OpCompositeExtract %uint %280 2 + %282 = OpAccessChain %_ptr_Function_FHitProxyVSToDS %param_var_I %262 + %283 = OpLoad %FHitProxyVSToDS %282 + %284 = OpCompositeExtract %uint %283 2 + %285 = OpAccessChain %_ptr_Function_FHitProxyVSToDS %param_var_I %263 + %286 = OpLoad %FHitProxyVSToDS %285 + %287 = OpCompositeExtract %uint %286 2 + %288 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %277 0 + %289 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %288 0 + %290 = OpCompositeExtract %v4float %289 0 + %291 = OpCompositeExtract %v4float %289 1 + %292 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %280 0 + %293 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %292 0 + %294 = OpCompositeExtract %v4float %293 0 + %295 = OpCompositeExtract %v4float %293 1 + %296 = OpULessThan %bool %284 %278 + %297 = OpIEqual %bool %284 %278 + %298 = OpULessThan %bool %287 %281 + %299 = OpLogicalAnd %bool %297 %298 + %300 = OpLogicalOr %bool %296 %299 + OpSelectionMerge %301 None + OpBranchConditional %300 %302 %301 + %302 = OpLabel + %303 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %283 0 + %304 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %303 0 + %305 = OpCompositeExtract %v4float %304 0 + %306 = OpCompositeExtract %v4float %304 1 + %307 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %286 0 + %308 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %307 0 + %309 = OpCompositeExtract %v4float %308 0 + %310 = OpCompositeExtract %v4float %308 1 + OpBranch %301 + %301 = OpLabel + %311 = OpPhi %v4float %294 %137 %309 %302 + %312 = OpPhi %v4float %295 %137 %310 %302 + %313 = OpPhi %v4float %290 %137 %305 %302 + %314 = OpPhi %v4float %291 %137 %306 %302 + %315 = OpVectorShuffle %v3float %313 %313 0 1 2 + %316 = OpVectorShuffle %v3float %311 %311 0 1 2 + %317 = OpCompositeExtract %float %314 0 + %318 = OpCompositeExtract %float %314 1 + %319 = OpCompositeExtract %float %314 2 + %320 = OpCompositeConstruct %v4float %317 %318 %319 %float_0 + %321 = OpCompositeExtract %float %312 0 + %322 = OpCompositeExtract %float %312 1 + %323 = OpCompositeExtract %float %312 2 + %324 = OpCompositeConstruct %v4float %321 %322 %323 %float_0 + %325 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %250 %int_0 + %326 = OpLoad %FVertexFactoryInterpolantsVSToDS %325 + %327 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %326 0 + %328 = OpCompositeExtract %v4float %327 1 + %329 = OpCompositeExtract %float %328 0 + %330 = OpCompositeExtract %float %328 1 + %331 = OpCompositeExtract %float %328 2 + %332 = OpCompositeConstruct %v4float %329 %330 %331 %float_0 + %333 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %252 %int_0 + %334 = OpLoad %FVertexFactoryInterpolantsVSToDS %333 + %335 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %334 0 + %336 = OpCompositeExtract %v4float %335 1 + %337 = OpCompositeExtract %float %336 0 + %338 = OpCompositeExtract %float %336 1 + %339 = OpCompositeExtract %float %336 2 + %340 = OpCompositeConstruct %v4float %337 %338 %339 %float_0 + %341 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %253 %int_0 + %342 = OpLoad %FVertexFactoryInterpolantsVSToDS %341 + %343 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %342 0 + %344 = OpCompositeExtract %v4float %343 1 + %345 = OpCompositeExtract %float %344 0 + %346 = OpCompositeExtract %float %344 1 + %347 = OpCompositeExtract %float %344 2 + %348 = OpCompositeConstruct %v4float %345 %346 %347 %float_0 + %349 = OpLoad %v4float %256 + %350 = OpAccessChain %_ptr_Function_v4float %param_var_I %250 %int_1 + %351 = OpLoad %v4float %350 + %352 = OpFMul %v4float %63 %349 + %353 = OpFAdd %v4float %352 %351 + %354 = OpFSub %v4float %351 %349 + %355 = OpDot %float %354 %243 + %356 = OpCompositeConstruct %v4float %355 %355 %355 %355 + %357 = OpFMul %v4float %356 %243 + %358 = OpFSub %v4float %353 %357 + %359 = OpFMul %v4float %358 %132 + %360 = OpAccessChain %_ptr_Function_v4float %param_var_I %252 %int_1 + %361 = OpLoad %v4float %360 + %362 = OpAccessChain %_ptr_Function_v4float %param_var_I %253 %int_1 + %363 = OpLoad %v4float %362 + %364 = OpFMul %v4float %63 %361 + %365 = OpFAdd %v4float %364 %363 + %366 = OpFSub %v4float %363 %361 + %367 = OpDot %float %366 %340 + %368 = OpCompositeConstruct %v4float %367 %367 %367 %367 + %369 = OpFMul %v4float %368 %340 + %370 = OpFSub %v4float %365 %369 + %371 = OpFMul %v4float %370 %132 + %372 = OpFAdd %v4float %359 %371 + %373 = OpFMul %v4float %372 %133 + %374 = OpLoad %v4float %350 + %375 = OpLoad %v4float %256 + %376 = OpFMul %v4float %63 %374 + %377 = OpFAdd %v4float %376 %375 + %378 = OpFSub %v4float %375 %374 + %379 = OpDot %float %378 %332 + %380 = OpCompositeConstruct %v4float %379 %379 %379 %379 + %381 = OpFMul %v4float %380 %332 + %382 = OpFSub %v4float %377 %381 + %383 = OpFMul %v4float %382 %132 + %384 = OpLoad %v4float %362 + %385 = OpLoad %v4float %360 + %386 = OpFMul %v4float %63 %384 + %387 = OpFAdd %v4float %386 %385 + %388 = OpFSub %v4float %385 %384 + %389 = OpDot %float %388 %348 + %390 = OpCompositeConstruct %v4float %389 %389 %389 %389 + %391 = OpFMul %v4float %390 %348 + %392 = OpFSub %v4float %387 %391 + %393 = OpFMul %v4float %392 %132 + %394 = OpFAdd %v4float %383 %393 + %395 = OpFMul %v4float %394 %133 + %396 = OpCompositeConstruct %FHullShaderConstantDominantEdgeData %130 %130 %320 %324 %315 %316 + %397 = OpCompositeConstruct %FHullShaderConstantDominantVertexData %130 %275 %271 + %398 = OpCompositeConstruct %_arr_v4float_uint_3 %257 %373 %395 + %399 = OpCompositeConstruct %FPNTessellationHSToDS %255 %398 %247 %float_1 %float_1 %397 %396 + %400 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %255 0 + %401 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %400 0 + %402 = OpCompositeExtract %v4float %401 0 + %403 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD10_centroid %227 + OpStore %403 %402 + %404 = OpCompositeExtract %v4float %401 1 + %405 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD11_centroid %227 + OpStore %405 %404 + %406 = OpCompositeExtract %v4float %255 1 + %407 = OpAccessChain %_ptr_Output_v4float %out_var_VS_To_DS_Position %227 + OpStore %407 %406 + %408 = OpCompositeExtract %uint %255 2 + %409 = OpAccessChain %_ptr_Output_uint %out_var_VS_To_DS_VertexID %227 + OpStore %409 %408 + %410 = OpAccessChain %_ptr_Output__arr_v4float_uint_3 %out_var_PN_POSITION %227 + OpStore %410 %398 + %411 = OpAccessChain %_ptr_Output_v3float %out_var_PN_DisplacementScales %227 + OpStore %411 %247 + %412 = OpAccessChain %_ptr_Output_float %out_var_PN_TessellationMultiplier %227 + OpStore %412 %float_1 + %413 = OpAccessChain %_ptr_Output_float %out_var_PN_WorldDisplacementMultiplier %227 + OpStore %413 %float_1 + %414 = OpAccessChain %_ptr_Output_v2float %out_var_PN_DominantVertex %227 + OpStore %414 %130 + %415 = OpAccessChain %_ptr_Output_v4float %out_var_PN_DominantVertex1 %227 + OpStore %415 %275 + %416 = OpAccessChain %_ptr_Output_v3float %out_var_PN_DominantVertex2 %227 + OpStore %416 %271 + %417 = OpAccessChain %_ptr_Output_v2float %out_var_PN_DominantEdge %227 + OpStore %417 %130 + %418 = OpAccessChain %_ptr_Output_v2float %out_var_PN_DominantEdge1 %227 + OpStore %418 %130 + %419 = OpAccessChain %_ptr_Output_v4float %out_var_PN_DominantEdge2 %227 + OpStore %419 %320 + %420 = OpAccessChain %_ptr_Output_v4float %out_var_PN_DominantEdge3 %227 + OpStore %420 %324 + %421 = OpAccessChain %_ptr_Output_v3float %out_var_PN_DominantEdge4 %227 + OpStore %421 %315 + %422 = OpAccessChain %_ptr_Output_v3float %out_var_PN_DominantEdge5 %227 + OpStore %422 %316 + %423 = OpAccessChain %_ptr_Workgroup_FPNTessellationHSToDS %temp_var_hullMainRetVal %227 + OpStore %423 %399 + OpControlBarrier %uint_2 %uint_4 %uint_0 + %424 = OpIEqual %bool %227 %uint_0 + OpSelectionMerge %if_merge None + OpBranchConditional %424 %425 %if_merge + %425 = OpLabel + %426 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_0 + %427 = OpLoad %mat4v4float %426 + %428 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_7 + %429 = OpLoad %mat4v4float %428 + %430 = OpAccessChain %_ptr_Uniform_v3float %View %int_28 + %431 = OpLoad %v3float %430 + %432 = OpAccessChain %_ptr_Uniform_float %View %int_78 + %433 = OpLoad %float %432 + %434 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_0 + %435 = OpLoad %v4float %434 + %436 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_1 + %437 = OpLoad %v4float %436 + %438 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_2 + %439 = OpLoad %v4float %438 + %440 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_0 + %441 = OpLoad %v4float %440 + %442 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_1 + %443 = OpLoad %v4float %442 + %444 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_2 + %445 = OpLoad %v4float %444 + %446 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_0 + %447 = OpLoad %v4float %446 + %448 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_1 + %449 = OpLoad %v4float %448 + %450 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_2 + %451 = OpLoad %v4float %450 + %452 = OpFAdd %v4float %437 %439 + %453 = OpFAdd %v4float %452 %443 + %454 = OpFAdd %v4float %453 %445 + %455 = OpFAdd %v4float %454 %449 + %456 = OpFAdd %v4float %455 %451 + %457 = OpFMul %v4float %456 %135 + %458 = OpFAdd %v4float %447 %441 + %459 = OpFAdd %v4float %458 %435 + %460 = OpFMul %v4float %459 %132 + %461 = OpFSub %v4float %457 %460 + %462 = OpFMul %v4float %461 %133 + %463 = OpFAdd %v4float %457 %462 + %464 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_1 %int_3 + %465 = OpLoad %float %464 + %466 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_2 %int_3 + %467 = OpLoad %float %466 + %468 = OpFAdd %float %465 %467 + %469 = OpFMul %float %float_0_5 %468 + %470 = OpCompositeInsert %v4float %469 %136 0 + %471 = OpLoad %float %466 + %472 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_0 %int_3 + %473 = OpLoad %float %472 + %474 = OpFAdd %float %471 %473 + %475 = OpFMul %float %float_0_5 %474 + %476 = OpCompositeInsert %v4float %475 %470 1 + %477 = OpLoad %float %472 + %478 = OpLoad %float %464 + %479 = OpFAdd %float %477 %478 + %480 = OpFMul %float %float_0_5 %479 + %481 = OpCompositeInsert %v4float %480 %476 2 + %482 = OpLoad %float %472 + %483 = OpLoad %float %464 + %484 = OpFAdd %float %482 %483 + %485 = OpLoad %float %466 + %486 = OpFAdd %float %484 %485 + %487 = OpFMul %float %float_0_333000004 %486 + %488 = OpCompositeInsert %v4float %487 %481 3 + %489 = OpVectorShuffle %v3float %435 %435 0 1 2 + %490 = OpVectorShuffle %v3float %441 %441 0 1 2 + %491 = OpVectorShuffle %v3float %447 %447 0 1 2 + OpBranch %492 + %492 = OpLabel + OpLoopMerge %493 %494 None + OpBranch %495 + %495 = OpLabel + %496 = OpMatrixTimesVector %v4float %429 %77 + %497 = OpCompositeExtract %float %435 0 + %498 = OpCompositeExtract %float %435 1 + %499 = OpCompositeExtract %float %435 2 + %500 = OpCompositeConstruct %v4float %497 %498 %499 %float_1 + %501 = OpMatrixTimesVector %v4float %427 %500 + %502 = OpVectorShuffle %v3float %501 %501 0 1 2 + %503 = OpVectorShuffle %v3float %496 %496 0 1 2 + %504 = OpFSub %v3float %502 %503 + %505 = OpCompositeExtract %float %501 3 + %506 = OpCompositeExtract %float %496 3 + %507 = OpFAdd %float %505 %506 + %508 = OpCompositeConstruct %v3float %507 %507 %507 + %509 = OpFOrdLessThan %v3bool %504 %508 + %510 = OpSelect %v3int %509 %83 %74 + %511 = OpFAdd %v3float %502 %503 + %512 = OpFNegate %float %505 + %513 = OpFSub %float %512 %506 + %514 = OpCompositeConstruct %v3float %513 %513 %513 + %515 = OpFOrdGreaterThan %v3bool %511 %514 + %516 = OpSelect %v3int %515 %83 %74 + %517 = OpIMul %v3int %84 %516 + %518 = OpIAdd %v3int %510 %517 + %519 = OpCompositeExtract %float %441 0 + %520 = OpCompositeExtract %float %441 1 + %521 = OpCompositeExtract %float %441 2 + %522 = OpCompositeConstruct %v4float %519 %520 %521 %float_1 + %523 = OpMatrixTimesVector %v4float %427 %522 + %524 = OpVectorShuffle %v3float %523 %523 0 1 2 + %525 = OpFSub %v3float %524 %503 + %526 = OpCompositeExtract %float %523 3 + %527 = OpFAdd %float %526 %506 + %528 = OpCompositeConstruct %v3float %527 %527 %527 + %529 = OpFOrdLessThan %v3bool %525 %528 + %530 = OpSelect %v3int %529 %83 %74 + %531 = OpFAdd %v3float %524 %503 + %532 = OpFNegate %float %526 + %533 = OpFSub %float %532 %506 + %534 = OpCompositeConstruct %v3float %533 %533 %533 + %535 = OpFOrdGreaterThan %v3bool %531 %534 + %536 = OpSelect %v3int %535 %83 %74 + %537 = OpIMul %v3int %84 %536 + %538 = OpIAdd %v3int %530 %537 + %539 = OpBitwiseOr %v3int %518 %538 + %540 = OpCompositeExtract %float %447 0 + %541 = OpCompositeExtract %float %447 1 + %542 = OpCompositeExtract %float %447 2 + %543 = OpCompositeConstruct %v4float %540 %541 %542 %float_1 + %544 = OpMatrixTimesVector %v4float %427 %543 + %545 = OpVectorShuffle %v3float %544 %544 0 1 2 + %546 = OpFSub %v3float %545 %503 + %547 = OpCompositeExtract %float %544 3 + %548 = OpFAdd %float %547 %506 + %549 = OpCompositeConstruct %v3float %548 %548 %548 + %550 = OpFOrdLessThan %v3bool %546 %549 + %551 = OpSelect %v3int %550 %83 %74 + %552 = OpFAdd %v3float %545 %503 + %553 = OpFNegate %float %547 + %554 = OpFSub %float %553 %506 + %555 = OpCompositeConstruct %v3float %554 %554 %554 + %556 = OpFOrdGreaterThan %v3bool %552 %555 + %557 = OpSelect %v3int %556 %83 %74 + %558 = OpIMul %v3int %84 %557 + %559 = OpIAdd %v3int %551 %558 + %560 = OpBitwiseOr %v3int %539 %559 + %561 = OpINotEqual %v3bool %560 %75 + %562 = OpAny %bool %561 + OpSelectionMerge %563 None + OpBranchConditional %562 %564 %563 + %564 = OpLabel + OpBranch %493 + %563 = OpLabel + %565 = OpFSub %v3float %489 %490 + %566 = OpFSub %v3float %490 %491 + %567 = OpFSub %v3float %491 %489 + %568 = OpFAdd %v3float %489 %490 + %569 = OpFMul %v3float %78 %568 + %570 = OpFSub %v3float %569 %431 + %571 = OpFAdd %v3float %490 %491 + %572 = OpFMul %v3float %78 %571 + %573 = OpFSub %v3float %572 %431 + %574 = OpFAdd %v3float %491 %489 + %575 = OpFMul %v3float %78 %574 + %576 = OpFSub %v3float %575 %431 + %577 = OpDot %float %566 %566 + %578 = OpDot %float %573 %573 + %579 = OpFDiv %float %577 %578 + %580 = OpExtInst %float %1 Sqrt %579 + %581 = OpDot %float %567 %567 + %582 = OpDot %float %576 %576 + %583 = OpFDiv %float %581 %582 + %584 = OpExtInst %float %1 Sqrt %583 + %585 = OpDot %float %565 %565 + %586 = OpDot %float %570 %570 + %587 = OpFDiv %float %585 %586 + %588 = OpExtInst %float %1 Sqrt %587 + %589 = OpCompositeConstruct %v4float %580 %584 %588 %float_1 + %590 = OpFAdd %float %580 %584 + %591 = OpFAdd %float %590 %588 + %592 = OpFMul %float %float_0_333000004 %591 + %593 = OpCompositeInsert %v4float %592 %589 3 + %594 = OpCompositeConstruct %v4float %433 %433 %433 %433 + %595 = OpFMul %v4float %594 %593 + OpBranch %493 + %494 = OpLabel + OpBranch %492 + %493 = OpLabel + %596 = OpPhi %v4float %77 %564 %595 %563 + %597 = OpFMul %v4float %488 %596 + %598 = OpExtInst %v4float %1 FClamp %597 %68 %70 + %599 = OpCompositeExtract %float %598 0 + %600 = OpCompositeExtract %float %598 1 + %601 = OpCompositeExtract %float %598 2 + %602 = OpCompositeExtract %float %598 3 + %603 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_0 + OpStore %603 %599 + %604 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_1 + OpStore %604 %600 + %605 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_2 + OpStore %605 %601 + %606 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %uint_0 + OpStore %606 %602 + OpStore %out_var_PN_POSITION9 %463 + OpBranch %if_merge + %if_merge = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc b/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc new file mode 100644 index 0000000..1a9b95e --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/tesc/hs-texcoord-array.asm.tesc @@ -0,0 +1,1144 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 531 +; Schema: 0 + OpCapability Tessellation + OpCapability SampledBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %MainHull "main" %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_COLOR0 %in_var_TEXCOORD0 %in_var_VS_To_DS_Position %gl_InvocationID %out_var_TEXCOORD10_centroid %out_var_TEXCOORD11_centroid %out_var_COLOR0 %out_var_TEXCOORD0 %out_var_VS_To_DS_Position %out_var_PN_POSITION %out_var_PN_DisplacementScales %out_var_PN_TessellationMultiplier %out_var_PN_WorldDisplacementMultiplier %gl_TessLevelOuter %gl_TessLevelInner %out_var_PN_POSITION9 + OpExecutionMode %MainHull Triangles + OpExecutionMode %MainHull SpacingFractionalOdd + OpExecutionMode %MainHull VertexOrderCw + OpExecutionMode %MainHull OutputVertices 3 + OpSource HLSL 600 + OpName %FPNTessellationHSToDS "FPNTessellationHSToDS" + OpMemberName %FPNTessellationHSToDS 0 "PassSpecificData" + OpMemberName %FPNTessellationHSToDS 1 "WorldPosition" + OpMemberName %FPNTessellationHSToDS 2 "DisplacementScale" + OpMemberName %FPNTessellationHSToDS 3 "TessellationMultiplier" + OpMemberName %FPNTessellationHSToDS 4 "WorldDisplacementMultiplier" + OpName %FHitProxyVSToDS "FHitProxyVSToDS" + OpMemberName %FHitProxyVSToDS 0 "FactoryInterpolants" + OpMemberName %FHitProxyVSToDS 1 "Position" + OpName %FVertexFactoryInterpolantsVSToDS "FVertexFactoryInterpolantsVSToDS" + OpMemberName %FVertexFactoryInterpolantsVSToDS 0 "InterpolantsVSToPS" + OpName %FVertexFactoryInterpolantsVSToPS "FVertexFactoryInterpolantsVSToPS" + OpMemberName %FVertexFactoryInterpolantsVSToPS 0 "TangentToWorld0" + OpMemberName %FVertexFactoryInterpolantsVSToPS 1 "TangentToWorld2" + OpMemberName %FVertexFactoryInterpolantsVSToPS 2 "Color" + OpMemberName %FVertexFactoryInterpolantsVSToPS 3 "TexCoords" + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_ClipToWorld" + OpMemberName %type_View 3 "View_TranslatedWorldToView" + OpMemberName %type_View 4 "View_ViewToTranslatedWorld" + OpMemberName %type_View 5 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 6 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 7 "View_ViewToClip" + OpMemberName %type_View 8 "View_ViewToClipNoAA" + OpMemberName %type_View 9 "View_ClipToView" + OpMemberName %type_View 10 "View_ClipToTranslatedWorld" + OpMemberName %type_View 11 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 12 "View_ScreenToWorld" + OpMemberName %type_View 13 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 14 "View_ViewForward" + OpMemberName %type_View 15 "PrePadding_View_908" + OpMemberName %type_View 16 "View_ViewUp" + OpMemberName %type_View 17 "PrePadding_View_924" + OpMemberName %type_View 18 "View_ViewRight" + OpMemberName %type_View 19 "PrePadding_View_940" + OpMemberName %type_View 20 "View_HMDViewNoRollUp" + OpMemberName %type_View 21 "PrePadding_View_956" + OpMemberName %type_View 22 "View_HMDViewNoRollRight" + OpMemberName %type_View 23 "PrePadding_View_972" + OpMemberName %type_View 24 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 25 "View_ScreenPositionScaleBias" + OpMemberName %type_View 26 "View_WorldCameraOrigin" + OpMemberName %type_View 27 "PrePadding_View_1020" + OpMemberName %type_View 28 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 29 "PrePadding_View_1036" + OpMemberName %type_View 30 "View_WorldViewOrigin" + OpMemberName %type_View 31 "PrePadding_View_1052" + OpMemberName %type_View 32 "View_PreViewTranslation" + OpMemberName %type_View 33 "PrePadding_View_1068" + OpMemberName %type_View 34 "View_PrevProjection" + OpMemberName %type_View 35 "View_PrevViewProj" + OpMemberName %type_View 36 "View_PrevViewRotationProj" + OpMemberName %type_View 37 "View_PrevViewToClip" + OpMemberName %type_View 38 "View_PrevClipToView" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 40 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 41 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 42 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 43 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 44 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 45 "PrePadding_View_1724" + OpMemberName %type_View 46 "View_PrevWorldViewOrigin" + OpMemberName %type_View 47 "PrePadding_View_1740" + OpMemberName %type_View 48 "View_PrevPreViewTranslation" + OpMemberName %type_View 49 "PrePadding_View_1756" + OpMemberName %type_View 50 "View_PrevInvViewProj" + OpMemberName %type_View 51 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 52 "View_ClipToPrevClip" + OpMemberName %type_View 53 "View_TemporalAAJitter" + OpMemberName %type_View 54 "View_GlobalClippingPlane" + OpMemberName %type_View 55 "View_FieldOfViewWideAngles" + OpMemberName %type_View 56 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 57 "View_ViewRectMin" + OpMemberName %type_View 58 "View_ViewSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferSizeAndInvSize" + OpMemberName %type_View 60 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 61 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 62 "View_PreExposure" + OpMemberName %type_View 63 "View_OneOverPreExposure" + OpMemberName %type_View 64 "PrePadding_View_2076" + OpMemberName %type_View 65 "View_DiffuseOverrideParameter" + OpMemberName %type_View 66 "View_SpecularOverrideParameter" + OpMemberName %type_View 67 "View_NormalOverrideParameter" + OpMemberName %type_View 68 "View_RoughnessOverrideParameter" + OpMemberName %type_View 69 "View_PrevFrameGameTime" + OpMemberName %type_View 70 "View_PrevFrameRealTime" + OpMemberName %type_View 71 "View_OutOfBoundsMask" + OpMemberName %type_View 72 "PrePadding_View_2148" + OpMemberName %type_View 73 "PrePadding_View_2152" + OpMemberName %type_View 74 "PrePadding_View_2156" + OpMemberName %type_View 75 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 76 "View_CullingSign" + OpMemberName %type_View 77 "View_NearPlane" + OpMemberName %type_View 78 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 79 "View_GameTime" + OpMemberName %type_View 80 "View_RealTime" + OpMemberName %type_View 81 "View_DeltaTime" + OpMemberName %type_View 82 "View_MaterialTextureMipBias" + OpMemberName %type_View 83 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 84 "View_Random" + OpMemberName %type_View 85 "View_FrameNumber" + OpMemberName %type_View 86 "View_StateFrameIndexMod8" + OpMemberName %type_View 87 "View_StateFrameIndex" + OpMemberName %type_View 88 "View_CameraCut" + OpMemberName %type_View 89 "View_UnlitViewmodeMask" + OpMemberName %type_View 90 "PrePadding_View_2228" + OpMemberName %type_View 91 "PrePadding_View_2232" + OpMemberName %type_View 92 "PrePadding_View_2236" + OpMemberName %type_View 93 "View_DirectionalLightColor" + OpMemberName %type_View 94 "View_DirectionalLightDirection" + OpMemberName %type_View 95 "PrePadding_View_2268" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 97 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 98 "View_TemporalAAParams" + OpMemberName %type_View 99 "View_CircleDOFParams" + OpMemberName %type_View 100 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 101 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 102 "View_DepthOfFieldScale" + OpMemberName %type_View 103 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 104 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 105 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 106 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 107 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 108 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 109 "View_GeneralPurposeTweak" + OpMemberName %type_View 110 "View_DemosaicVposOffset" + OpMemberName %type_View 111 "PrePadding_View_2412" + OpMemberName %type_View 112 "View_IndirectLightingColorScale" + OpMemberName %type_View 113 "View_HDR32bppEncodingMode" + OpMemberName %type_View 114 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 115 "View_AtmosphericFogSunPower" + OpMemberName %type_View 116 "View_AtmosphericFogPower" + OpMemberName %type_View 117 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 118 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 119 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 120 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 121 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 122 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 123 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 124 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 125 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 126 "View_AtmosphericFogSunDiscHalfApexAngleRadian" + OpMemberName %type_View 127 "PrePadding_View_2492" + OpMemberName %type_View 128 "View_AtmosphericFogSunDiscLuminance" + OpMemberName %type_View 129 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 130 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 131 "PrePadding_View_2520" + OpMemberName %type_View 132 "PrePadding_View_2524" + OpMemberName %type_View 133 "View_AtmosphericFogSunColor" + OpMemberName %type_View 134 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 135 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 136 "View_AmbientCubemapTint" + OpMemberName %type_View 137 "View_AmbientCubemapIntensity" + OpMemberName %type_View 138 "View_SkyLightParameters" + OpMemberName %type_View 139 "PrePadding_View_2584" + OpMemberName %type_View 140 "PrePadding_View_2588" + OpMemberName %type_View 141 "View_SkyLightColor" + OpMemberName %type_View 142 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 143 "View_MobilePreviewMode" + OpMemberName %type_View 144 "View_HMDEyePaddingOffset" + OpMemberName %type_View 145 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 146 "View_ShowDecalsMask" + OpMemberName %type_View 147 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 148 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 149 "PrePadding_View_2744" + OpMemberName %type_View 150 "PrePadding_View_2748" + OpMemberName %type_View 151 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 152 "View_StereoPassIndex" + OpMemberName %type_View 153 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 154 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 155 "View_GlobalVolumeDimension" + OpMemberName %type_View 156 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 157 "View_MaxGlobalDistance" + OpMemberName %type_View 158 "PrePadding_View_2908" + OpMemberName %type_View 159 "View_CursorPosition" + OpMemberName %type_View 160 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 161 "PrePadding_View_2924" + OpMemberName %type_View 162 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 163 "PrePadding_View_2940" + OpMemberName %type_View 164 "View_VolumetricFogGridZParams" + OpMemberName %type_View 165 "PrePadding_View_2956" + OpMemberName %type_View 166 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 167 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 168 "PrePadding_View_2972" + OpMemberName %type_View 169 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 170 "PrePadding_View_2988" + OpMemberName %type_View 171 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 172 "PrePadding_View_3004" + OpMemberName %type_View 173 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 174 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 175 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 176 "View_StereoIPD" + OpMemberName %type_View 177 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 178 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_Primitive "type.Primitive" + OpMemberName %type_Primitive 0 "Primitive_LocalToWorld" + OpMemberName %type_Primitive 1 "Primitive_InvNonUniformScaleAndDeterminantSign" + OpMemberName %type_Primitive 2 "Primitive_ObjectWorldPositionAndRadius" + OpMemberName %type_Primitive 3 "Primitive_WorldToLocal" + OpMemberName %type_Primitive 4 "Primitive_PreviousLocalToWorld" + OpMemberName %type_Primitive 5 "Primitive_PreviousWorldToLocal" + OpMemberName %type_Primitive 6 "Primitive_ActorWorldPosition" + OpMemberName %type_Primitive 7 "Primitive_UseSingleSampleShadowFromStationaryLights" + OpMemberName %type_Primitive 8 "Primitive_ObjectBounds" + OpMemberName %type_Primitive 9 "Primitive_LpvBiasMultiplier" + OpMemberName %type_Primitive 10 "Primitive_DecalReceiverMask" + OpMemberName %type_Primitive 11 "Primitive_PerObjectGBufferData" + OpMemberName %type_Primitive 12 "Primitive_UseVolumetricLightmapShadowFromStationaryLights" + OpMemberName %type_Primitive 13 "Primitive_DrawsVelocity" + OpMemberName %type_Primitive 14 "Primitive_ObjectOrientation" + OpMemberName %type_Primitive 15 "Primitive_NonUniformScale" + OpMemberName %type_Primitive 16 "Primitive_LocalObjectBoundsMin" + OpMemberName %type_Primitive 17 "Primitive_LightingChannelMask" + OpMemberName %type_Primitive 18 "Primitive_LocalObjectBoundsMax" + OpMemberName %type_Primitive 19 "Primitive_LightmapDataIndex" + OpMemberName %type_Primitive 20 "Primitive_PreSkinnedLocalBounds" + OpMemberName %type_Primitive 21 "Primitive_SingleCaptureIndex" + OpMemberName %type_Primitive 22 "Primitive_OutputVelocity" + OpMemberName %type_Primitive 23 "PrePadding_Primitive_420" + OpMemberName %type_Primitive 24 "PrePadding_Primitive_424" + OpMemberName %type_Primitive 25 "PrePadding_Primitive_428" + OpMemberName %type_Primitive 26 "Primitive_CustomPrimitiveData" + OpName %Primitive "Primitive" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_COLOR0 "in.var.COLOR0" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_VS_To_DS_Position "in.var.VS_To_DS_Position" + OpName %out_var_TEXCOORD10_centroid "out.var.TEXCOORD10_centroid" + OpName %out_var_TEXCOORD11_centroid "out.var.TEXCOORD11_centroid" + OpName %out_var_COLOR0 "out.var.COLOR0" + OpName %out_var_TEXCOORD0 "out.var.TEXCOORD0" + OpName %out_var_VS_To_DS_Position "out.var.VS_To_DS_Position" + OpName %out_var_PN_POSITION "out.var.PN_POSITION" + OpName %out_var_PN_DisplacementScales "out.var.PN_DisplacementScales" + OpName %out_var_PN_TessellationMultiplier "out.var.PN_TessellationMultiplier" + OpName %out_var_PN_WorldDisplacementMultiplier "out.var.PN_WorldDisplacementMultiplier" + OpName %out_var_PN_POSITION9 "out.var.PN_POSITION9" + OpName %MainHull "MainHull" + OpName %param_var_I "param.var.I" + OpName %temp_var_hullMainRetVal "temp.var.hullMainRetVal" + OpName %if_merge "if.merge" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_COLOR0 UserSemantic "COLOR0" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %in_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorate %gl_InvocationID BuiltIn InvocationId + OpDecorateString %gl_InvocationID UserSemantic "SV_OutputControlPointID" + OpDecorateString %out_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %out_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %out_var_COLOR0 UserSemantic "COLOR0" + OpDecorateString %out_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %out_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorateString %out_var_PN_POSITION UserSemantic "PN_POSITION" + OpDecorateString %out_var_PN_DisplacementScales UserSemantic "PN_DisplacementScales" + OpDecorateString %out_var_PN_TessellationMultiplier UserSemantic "PN_TessellationMultiplier" + OpDecorateString %out_var_PN_WorldDisplacementMultiplier UserSemantic "PN_WorldDisplacementMultiplier" + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorateString %gl_TessLevelOuter UserSemantic "SV_TessFactor" + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorateString %gl_TessLevelInner UserSemantic "SV_InsideTessFactor" + OpDecorate %gl_TessLevelInner Patch + OpDecorateString %out_var_PN_POSITION9 UserSemantic "PN_POSITION9" + OpDecorate %out_var_PN_POSITION9 Patch + OpDecorate %in_var_TEXCOORD10_centroid Location 0 + OpDecorate %in_var_TEXCOORD11_centroid Location 1 + OpDecorate %in_var_COLOR0 Location 2 + OpDecorate %in_var_TEXCOORD0 Location 3 + OpDecorate %in_var_VS_To_DS_Position Location 5 + OpDecorate %out_var_COLOR0 Location 0 + OpDecorate %out_var_PN_DisplacementScales Location 1 + OpDecorate %out_var_PN_POSITION Location 2 + OpDecorate %out_var_PN_POSITION9 Location 5 + OpDecorate %out_var_PN_TessellationMultiplier Location 6 + OpDecorate %out_var_PN_WorldDisplacementMultiplier Location 7 + OpDecorate %out_var_TEXCOORD0 Location 8 + OpDecorate %out_var_TEXCOORD10_centroid Location 10 + OpDecorate %out_var_TEXCOORD11_centroid Location 11 + OpDecorate %out_var_VS_To_DS_Position Location 12 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %Primitive DescriptorSet 0 + OpDecorate %Primitive Binding 1 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 13 MatrixStride 16 + OpMemberDecorate %type_View 13 ColMajor + OpMemberDecorate %type_View 14 Offset 896 + OpMemberDecorate %type_View 15 Offset 908 + OpMemberDecorate %type_View 16 Offset 912 + OpMemberDecorate %type_View 17 Offset 924 + OpMemberDecorate %type_View 18 Offset 928 + OpMemberDecorate %type_View 19 Offset 940 + OpMemberDecorate %type_View 20 Offset 944 + OpMemberDecorate %type_View 21 Offset 956 + OpMemberDecorate %type_View 22 Offset 960 + OpMemberDecorate %type_View 23 Offset 972 + OpMemberDecorate %type_View 24 Offset 976 + OpMemberDecorate %type_View 25 Offset 992 + OpMemberDecorate %type_View 26 Offset 1008 + OpMemberDecorate %type_View 27 Offset 1020 + OpMemberDecorate %type_View 28 Offset 1024 + OpMemberDecorate %type_View 29 Offset 1036 + OpMemberDecorate %type_View 30 Offset 1040 + OpMemberDecorate %type_View 31 Offset 1052 + OpMemberDecorate %type_View 32 Offset 1056 + OpMemberDecorate %type_View 33 Offset 1068 + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 43 MatrixStride 16 + OpMemberDecorate %type_View 43 ColMajor + OpMemberDecorate %type_View 44 Offset 1712 + OpMemberDecorate %type_View 45 Offset 1724 + OpMemberDecorate %type_View 46 Offset 1728 + OpMemberDecorate %type_View 47 Offset 1740 + OpMemberDecorate %type_View 48 Offset 1744 + OpMemberDecorate %type_View 49 Offset 1756 + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 52 MatrixStride 16 + OpMemberDecorate %type_View 52 ColMajor + OpMemberDecorate %type_View 53 Offset 1952 + OpMemberDecorate %type_View 54 Offset 1968 + OpMemberDecorate %type_View 55 Offset 1984 + OpMemberDecorate %type_View 56 Offset 1992 + OpMemberDecorate %type_View 57 Offset 2000 + OpMemberDecorate %type_View 58 Offset 2016 + OpMemberDecorate %type_View 59 Offset 2032 + OpMemberDecorate %type_View 60 Offset 2048 + OpMemberDecorate %type_View 61 Offset 2064 + OpMemberDecorate %type_View 62 Offset 2068 + OpMemberDecorate %type_View 63 Offset 2072 + OpMemberDecorate %type_View 64 Offset 2076 + OpMemberDecorate %type_View 65 Offset 2080 + OpMemberDecorate %type_View 66 Offset 2096 + OpMemberDecorate %type_View 67 Offset 2112 + OpMemberDecorate %type_View 68 Offset 2128 + OpMemberDecorate %type_View 69 Offset 2136 + OpMemberDecorate %type_View 70 Offset 2140 + OpMemberDecorate %type_View 71 Offset 2144 + OpMemberDecorate %type_View 72 Offset 2148 + OpMemberDecorate %type_View 73 Offset 2152 + OpMemberDecorate %type_View 74 Offset 2156 + OpMemberDecorate %type_View 75 Offset 2160 + OpMemberDecorate %type_View 76 Offset 2172 + OpMemberDecorate %type_View 77 Offset 2176 + OpMemberDecorate %type_View 78 Offset 2180 + OpMemberDecorate %type_View 79 Offset 2184 + OpMemberDecorate %type_View 80 Offset 2188 + OpMemberDecorate %type_View 81 Offset 2192 + OpMemberDecorate %type_View 82 Offset 2196 + OpMemberDecorate %type_View 83 Offset 2200 + OpMemberDecorate %type_View 84 Offset 2204 + OpMemberDecorate %type_View 85 Offset 2208 + OpMemberDecorate %type_View 86 Offset 2212 + OpMemberDecorate %type_View 87 Offset 2216 + OpMemberDecorate %type_View 88 Offset 2220 + OpMemberDecorate %type_View 89 Offset 2224 + OpMemberDecorate %type_View 90 Offset 2228 + OpMemberDecorate %type_View 91 Offset 2232 + OpMemberDecorate %type_View 92 Offset 2236 + OpMemberDecorate %type_View 93 Offset 2240 + OpMemberDecorate %type_View 94 Offset 2256 + OpMemberDecorate %type_View 95 Offset 2268 + OpMemberDecorate %type_View 96 Offset 2272 + OpMemberDecorate %type_View 97 Offset 2304 + OpMemberDecorate %type_View 98 Offset 2336 + OpMemberDecorate %type_View 99 Offset 2352 + OpMemberDecorate %type_View 100 Offset 2368 + OpMemberDecorate %type_View 101 Offset 2372 + OpMemberDecorate %type_View 102 Offset 2376 + OpMemberDecorate %type_View 103 Offset 2380 + OpMemberDecorate %type_View 104 Offset 2384 + OpMemberDecorate %type_View 105 Offset 2388 + OpMemberDecorate %type_View 106 Offset 2392 + OpMemberDecorate %type_View 107 Offset 2396 + OpMemberDecorate %type_View 108 Offset 2400 + OpMemberDecorate %type_View 109 Offset 2404 + OpMemberDecorate %type_View 110 Offset 2408 + OpMemberDecorate %type_View 111 Offset 2412 + OpMemberDecorate %type_View 112 Offset 2416 + OpMemberDecorate %type_View 113 Offset 2428 + OpMemberDecorate %type_View 114 Offset 2432 + OpMemberDecorate %type_View 115 Offset 2444 + OpMemberDecorate %type_View 116 Offset 2448 + OpMemberDecorate %type_View 117 Offset 2452 + OpMemberDecorate %type_View 118 Offset 2456 + OpMemberDecorate %type_View 119 Offset 2460 + OpMemberDecorate %type_View 120 Offset 2464 + OpMemberDecorate %type_View 121 Offset 2468 + OpMemberDecorate %type_View 122 Offset 2472 + OpMemberDecorate %type_View 123 Offset 2476 + OpMemberDecorate %type_View 124 Offset 2480 + OpMemberDecorate %type_View 125 Offset 2484 + OpMemberDecorate %type_View 126 Offset 2488 + OpMemberDecorate %type_View 127 Offset 2492 + OpMemberDecorate %type_View 128 Offset 2496 + OpMemberDecorate %type_View 129 Offset 2512 + OpMemberDecorate %type_View 130 Offset 2516 + OpMemberDecorate %type_View 131 Offset 2520 + OpMemberDecorate %type_View 132 Offset 2524 + OpMemberDecorate %type_View 133 Offset 2528 + OpMemberDecorate %type_View 134 Offset 2544 + OpMemberDecorate %type_View 135 Offset 2556 + OpMemberDecorate %type_View 136 Offset 2560 + OpMemberDecorate %type_View 137 Offset 2576 + OpMemberDecorate %type_View 138 Offset 2580 + OpMemberDecorate %type_View 139 Offset 2584 + OpMemberDecorate %type_View 140 Offset 2588 + OpMemberDecorate %type_View 141 Offset 2592 + OpMemberDecorate %type_View 142 Offset 2608 + OpMemberDecorate %type_View 143 Offset 2720 + OpMemberDecorate %type_View 144 Offset 2724 + OpMemberDecorate %type_View 145 Offset 2728 + OpMemberDecorate %type_View 146 Offset 2732 + OpMemberDecorate %type_View 147 Offset 2736 + OpMemberDecorate %type_View 148 Offset 2740 + OpMemberDecorate %type_View 149 Offset 2744 + OpMemberDecorate %type_View 150 Offset 2748 + OpMemberDecorate %type_View 151 Offset 2752 + OpMemberDecorate %type_View 152 Offset 2764 + OpMemberDecorate %type_View 153 Offset 2768 + OpMemberDecorate %type_View 154 Offset 2832 + OpMemberDecorate %type_View 155 Offset 2896 + OpMemberDecorate %type_View 156 Offset 2900 + OpMemberDecorate %type_View 157 Offset 2904 + OpMemberDecorate %type_View 158 Offset 2908 + OpMemberDecorate %type_View 159 Offset 2912 + OpMemberDecorate %type_View 160 Offset 2920 + OpMemberDecorate %type_View 161 Offset 2924 + OpMemberDecorate %type_View 162 Offset 2928 + OpMemberDecorate %type_View 163 Offset 2940 + OpMemberDecorate %type_View 164 Offset 2944 + OpMemberDecorate %type_View 165 Offset 2956 + OpMemberDecorate %type_View 166 Offset 2960 + OpMemberDecorate %type_View 167 Offset 2968 + OpMemberDecorate %type_View 168 Offset 2972 + OpMemberDecorate %type_View 169 Offset 2976 + OpMemberDecorate %type_View 170 Offset 2988 + OpMemberDecorate %type_View 171 Offset 2992 + OpMemberDecorate %type_View 172 Offset 3004 + OpMemberDecorate %type_View 173 Offset 3008 + OpMemberDecorate %type_View 174 Offset 3020 + OpMemberDecorate %type_View 175 Offset 3024 + OpMemberDecorate %type_View 176 Offset 3036 + OpMemberDecorate %type_View 177 Offset 3040 + OpMemberDecorate %type_View 178 Offset 3044 + OpDecorate %type_View Block + OpMemberDecorate %type_Primitive 0 Offset 0 + OpMemberDecorate %type_Primitive 0 MatrixStride 16 + OpMemberDecorate %type_Primitive 0 ColMajor + OpMemberDecorate %type_Primitive 1 Offset 64 + OpMemberDecorate %type_Primitive 2 Offset 80 + OpMemberDecorate %type_Primitive 3 Offset 96 + OpMemberDecorate %type_Primitive 3 MatrixStride 16 + OpMemberDecorate %type_Primitive 3 ColMajor + OpMemberDecorate %type_Primitive 4 Offset 160 + OpMemberDecorate %type_Primitive 4 MatrixStride 16 + OpMemberDecorate %type_Primitive 4 ColMajor + OpMemberDecorate %type_Primitive 5 Offset 224 + OpMemberDecorate %type_Primitive 5 MatrixStride 16 + OpMemberDecorate %type_Primitive 5 ColMajor + OpMemberDecorate %type_Primitive 6 Offset 288 + OpMemberDecorate %type_Primitive 7 Offset 300 + OpMemberDecorate %type_Primitive 8 Offset 304 + OpMemberDecorate %type_Primitive 9 Offset 316 + OpMemberDecorate %type_Primitive 10 Offset 320 + OpMemberDecorate %type_Primitive 11 Offset 324 + OpMemberDecorate %type_Primitive 12 Offset 328 + OpMemberDecorate %type_Primitive 13 Offset 332 + OpMemberDecorate %type_Primitive 14 Offset 336 + OpMemberDecorate %type_Primitive 15 Offset 352 + OpMemberDecorate %type_Primitive 16 Offset 368 + OpMemberDecorate %type_Primitive 17 Offset 380 + OpMemberDecorate %type_Primitive 18 Offset 384 + OpMemberDecorate %type_Primitive 19 Offset 396 + OpMemberDecorate %type_Primitive 20 Offset 400 + OpMemberDecorate %type_Primitive 21 Offset 412 + OpMemberDecorate %type_Primitive 22 Offset 416 + OpMemberDecorate %type_Primitive 23 Offset 420 + OpMemberDecorate %type_Primitive 24 Offset 424 + OpMemberDecorate %type_Primitive 25 Offset 428 + OpMemberDecorate %type_Primitive 26 Offset 432 + OpDecorate %type_Primitive Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %v2int = OpTypeVector %int 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 + %float_2 = OpConstant %float 2 + %54 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %float_0_5 = OpConstant %float 0.5 + %int_3 = OpConstant %int 3 +%float_0_333000004 = OpConstant %float 0.333000004 + %float_1 = OpConstant %float 1 + %59 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_15 = OpConstant %float 15 + %61 = OpConstantComposite %v4float %float_15 %float_15 %float_15 %float_15 +%_arr_v2float_uint_2 = OpTypeArray %v2float %uint_2 +%FVertexFactoryInterpolantsVSToPS = OpTypeStruct %v4float %v4float %v4float %_arr_v2float_uint_2 +%FVertexFactoryInterpolantsVSToDS = OpTypeStruct %FVertexFactoryInterpolantsVSToPS +%FHitProxyVSToDS = OpTypeStruct %FVertexFactoryInterpolantsVSToDS %v4float +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%FPNTessellationHSToDS = OpTypeStruct %FHitProxyVSToDS %_arr_v4float_uint_3 %v3float %float %float + %v3int = OpTypeVector %int 3 + %65 = OpConstantComposite %v3int %int_0 %int_0 %int_0 + %66 = OpConstantComposite %v3int %int_3 %int_3 %int_3 + %float_0 = OpConstant %float 0 + %68 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %69 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %int_78 = OpConstant %int 78 + %int_15 = OpConstant %int 15 + %int_7 = OpConstant %int 7 + %int_28 = OpConstant %int 28 + %74 = OpConstantComposite %v3int %int_1 %int_1 %int_1 + %75 = OpConstantComposite %v3int %int_2 %int_2 %int_2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %uint %uint %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v2int %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_Primitive = OpTypeStruct %mat4v4float %v4float %v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %float %float %float %float %v4float %v4float %v3float %uint %v3float %uint %v3float %int %uint %uint %uint %uint %_arr_v4float_uint_4 +%_ptr_Uniform_type_Primitive = OpTypePointer Uniform %type_Primitive + %uint_12 = OpConstant %uint 12 +%_arr_v4float_uint_12 = OpTypeArray %v4float %uint_12 +%_ptr_Input__arr_v4float_uint_12 = OpTypePointer Input %_arr_v4float_uint_12 +%_arr__arr_v2float_uint_2_uint_12 = OpTypeArray %_arr_v2float_uint_2 %uint_12 +%_ptr_Input__arr__arr_v2float_uint_2_uint_12 = OpTypePointer Input %_arr__arr_v2float_uint_2_uint_12 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 +%_arr__arr_v2float_uint_2_uint_3 = OpTypeArray %_arr_v2float_uint_2 %uint_3 +%_ptr_Output__arr__arr_v2float_uint_2_uint_3 = OpTypePointer Output %_arr__arr_v2float_uint_2_uint_3 +%_arr__arr_v4float_uint_3_uint_3 = OpTypeArray %_arr_v4float_uint_3 %uint_3 +%_ptr_Output__arr__arr_v4float_uint_3_uint_3 = OpTypePointer Output %_arr__arr_v4float_uint_3_uint_3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Output__arr_v3float_uint_3 = OpTypePointer Output %_arr_v3float_uint_3 +%_ptr_Output__arr_float_uint_3 = OpTypePointer Output %_arr_float_uint_3 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %98 = OpTypeFunction %void +%_arr_FHitProxyVSToDS_uint_12 = OpTypeArray %FHitProxyVSToDS %uint_12 +%_ptr_Function__arr_FHitProxyVSToDS_uint_12 = OpTypePointer Function %_arr_FHitProxyVSToDS_uint_12 +%_arr_FPNTessellationHSToDS_uint_3 = OpTypeArray %FPNTessellationHSToDS %uint_3 +%_ptr_Function__arr_FPNTessellationHSToDS_uint_3 = OpTypePointer Function %_arr_FPNTessellationHSToDS_uint_3 +%_ptr_Workgroup__arr_FPNTessellationHSToDS_uint_3 = OpTypePointer Workgroup %_arr_FPNTessellationHSToDS_uint_3 +%_ptr_Output__arr_v2float_uint_2 = OpTypePointer Output %_arr_v2float_uint_2 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Function_FPNTessellationHSToDS = OpTypePointer Function %FPNTessellationHSToDS +%_ptr_Workgroup_FPNTessellationHSToDS = OpTypePointer Workgroup %FPNTessellationHSToDS + %bool = OpTypeBool +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Workgroup_v4float = OpTypePointer Workgroup %v4float +%_ptr_Workgroup_float = OpTypePointer Workgroup %float +%mat3v3float = OpTypeMatrix %v3float 3 +%_ptr_Function_FVertexFactoryInterpolantsVSToDS = OpTypePointer Function %FVertexFactoryInterpolantsVSToDS +%_ptr_Function_FHitProxyVSToDS = OpTypePointer Function %FHitProxyVSToDS + %v3bool = OpTypeVector %bool 3 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %View = OpVariable %_ptr_Uniform_type_View Uniform + %Primitive = OpVariable %_ptr_Uniform_type_Primitive Uniform +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_COLOR0 = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input__arr__arr_v2float_uint_2_uint_12 Input +%in_var_VS_To_DS_Position = OpVariable %_ptr_Input__arr_v4float_uint_12 Input +%gl_InvocationID = OpVariable %_ptr_Input_uint Input +%out_var_TEXCOORD10_centroid = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_TEXCOORD11_centroid = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_COLOR0 = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_TEXCOORD0 = OpVariable %_ptr_Output__arr__arr_v2float_uint_2_uint_3 Output +%out_var_VS_To_DS_Position = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%out_var_PN_POSITION = OpVariable %_ptr_Output__arr__arr_v4float_uint_3_uint_3 Output +%out_var_PN_DisplacementScales = OpVariable %_ptr_Output__arr_v3float_uint_3 Output +%out_var_PN_TessellationMultiplier = OpVariable %_ptr_Output__arr_float_uint_3 Output +%out_var_PN_WorldDisplacementMultiplier = OpVariable %_ptr_Output__arr_float_uint_3 Output +%gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output +%gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output +%out_var_PN_POSITION9 = OpVariable %_ptr_Output_v4float Output +%float_0_333333343 = OpConstant %float 0.333333343 + %119 = OpConstantComposite %v4float %float_0_333333343 %float_0_333333343 %float_0_333333343 %float_0_333333343 + %120 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +%float_0_166666672 = OpConstant %float 0.166666672 + %122 = OpConstantComposite %v4float %float_0_166666672 %float_0_166666672 %float_0_166666672 %float_0_166666672 + %123 = OpUndef %v4float + +; XXX: Original asm used Function here, which is wrong. +; This patches the SPIR-V to be correct. +%temp_var_hullMainRetVal = OpVariable %_ptr_Workgroup__arr_FPNTessellationHSToDS_uint_3 Workgroup + + %MainHull = OpFunction %void None %98 + %124 = OpLabel +%param_var_I = OpVariable %_ptr_Function__arr_FHitProxyVSToDS_uint_12 Function + %125 = OpLoad %_arr_v4float_uint_12 %in_var_TEXCOORD10_centroid + %126 = OpLoad %_arr_v4float_uint_12 %in_var_TEXCOORD11_centroid + %127 = OpLoad %_arr_v4float_uint_12 %in_var_COLOR0 + %128 = OpLoad %_arr__arr_v2float_uint_2_uint_12 %in_var_TEXCOORD0 + %129 = OpCompositeExtract %v4float %125 0 + %130 = OpCompositeExtract %v4float %126 0 + %131 = OpCompositeExtract %v4float %127 0 + %132 = OpCompositeExtract %_arr_v2float_uint_2 %128 0 + %133 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %129 %130 %131 %132 + %134 = OpCompositeExtract %v4float %125 1 + %135 = OpCompositeExtract %v4float %126 1 + %136 = OpCompositeExtract %v4float %127 1 + %137 = OpCompositeExtract %_arr_v2float_uint_2 %128 1 + %138 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %134 %135 %136 %137 + %139 = OpCompositeExtract %v4float %125 2 + %140 = OpCompositeExtract %v4float %126 2 + %141 = OpCompositeExtract %v4float %127 2 + %142 = OpCompositeExtract %_arr_v2float_uint_2 %128 2 + %143 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %139 %140 %141 %142 + %144 = OpCompositeExtract %v4float %125 3 + %145 = OpCompositeExtract %v4float %126 3 + %146 = OpCompositeExtract %v4float %127 3 + %147 = OpCompositeExtract %_arr_v2float_uint_2 %128 3 + %148 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %144 %145 %146 %147 + %149 = OpCompositeExtract %v4float %125 4 + %150 = OpCompositeExtract %v4float %126 4 + %151 = OpCompositeExtract %v4float %127 4 + %152 = OpCompositeExtract %_arr_v2float_uint_2 %128 4 + %153 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %149 %150 %151 %152 + %154 = OpCompositeExtract %v4float %125 5 + %155 = OpCompositeExtract %v4float %126 5 + %156 = OpCompositeExtract %v4float %127 5 + %157 = OpCompositeExtract %_arr_v2float_uint_2 %128 5 + %158 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %154 %155 %156 %157 + %159 = OpCompositeExtract %v4float %125 6 + %160 = OpCompositeExtract %v4float %126 6 + %161 = OpCompositeExtract %v4float %127 6 + %162 = OpCompositeExtract %_arr_v2float_uint_2 %128 6 + %163 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %159 %160 %161 %162 + %164 = OpCompositeExtract %v4float %125 7 + %165 = OpCompositeExtract %v4float %126 7 + %166 = OpCompositeExtract %v4float %127 7 + %167 = OpCompositeExtract %_arr_v2float_uint_2 %128 7 + %168 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %164 %165 %166 %167 + %169 = OpCompositeExtract %v4float %125 8 + %170 = OpCompositeExtract %v4float %126 8 + %171 = OpCompositeExtract %v4float %127 8 + %172 = OpCompositeExtract %_arr_v2float_uint_2 %128 8 + %173 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %169 %170 %171 %172 + %174 = OpCompositeExtract %v4float %125 9 + %175 = OpCompositeExtract %v4float %126 9 + %176 = OpCompositeExtract %v4float %127 9 + %177 = OpCompositeExtract %_arr_v2float_uint_2 %128 9 + %178 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %174 %175 %176 %177 + %179 = OpCompositeExtract %v4float %125 10 + %180 = OpCompositeExtract %v4float %126 10 + %181 = OpCompositeExtract %v4float %127 10 + %182 = OpCompositeExtract %_arr_v2float_uint_2 %128 10 + %183 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %179 %180 %181 %182 + %184 = OpCompositeExtract %v4float %125 11 + %185 = OpCompositeExtract %v4float %126 11 + %186 = OpCompositeExtract %v4float %127 11 + %187 = OpCompositeExtract %_arr_v2float_uint_2 %128 11 + %188 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %184 %185 %186 %187 + %189 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %133 + %190 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %138 + %191 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %143 + %192 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %148 + %193 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %153 + %194 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %158 + %195 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %163 + %196 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %168 + %197 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %173 + %198 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %178 + %199 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %183 + %200 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %188 + %201 = OpLoad %_arr_v4float_uint_12 %in_var_VS_To_DS_Position + %202 = OpCompositeExtract %v4float %201 0 + %203 = OpCompositeConstruct %FHitProxyVSToDS %189 %202 + %204 = OpCompositeExtract %v4float %201 1 + %205 = OpCompositeConstruct %FHitProxyVSToDS %190 %204 + %206 = OpCompositeExtract %v4float %201 2 + %207 = OpCompositeConstruct %FHitProxyVSToDS %191 %206 + %208 = OpCompositeExtract %v4float %201 3 + %209 = OpCompositeConstruct %FHitProxyVSToDS %192 %208 + %210 = OpCompositeExtract %v4float %201 4 + %211 = OpCompositeConstruct %FHitProxyVSToDS %193 %210 + %212 = OpCompositeExtract %v4float %201 5 + %213 = OpCompositeConstruct %FHitProxyVSToDS %194 %212 + %214 = OpCompositeExtract %v4float %201 6 + %215 = OpCompositeConstruct %FHitProxyVSToDS %195 %214 + %216 = OpCompositeExtract %v4float %201 7 + %217 = OpCompositeConstruct %FHitProxyVSToDS %196 %216 + %218 = OpCompositeExtract %v4float %201 8 + %219 = OpCompositeConstruct %FHitProxyVSToDS %197 %218 + %220 = OpCompositeExtract %v4float %201 9 + %221 = OpCompositeConstruct %FHitProxyVSToDS %198 %220 + %222 = OpCompositeExtract %v4float %201 10 + %223 = OpCompositeConstruct %FHitProxyVSToDS %199 %222 + %224 = OpCompositeExtract %v4float %201 11 + %225 = OpCompositeConstruct %FHitProxyVSToDS %200 %224 + %226 = OpCompositeConstruct %_arr_FHitProxyVSToDS_uint_12 %203 %205 %207 %209 %211 %213 %215 %217 %219 %221 %223 %225 + OpStore %param_var_I %226 + %227 = OpLoad %uint %gl_InvocationID + %228 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %227 %int_0 + %229 = OpLoad %FVertexFactoryInterpolantsVSToDS %228 + %230 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %229 0 + %231 = OpCompositeExtract %v4float %230 0 + %232 = OpCompositeExtract %v4float %230 1 + %233 = OpVectorShuffle %v3float %231 %231 0 1 2 + %234 = OpVectorShuffle %v3float %232 %232 0 1 2 + %235 = OpExtInst %v3float %1 Cross %234 %233 + %236 = OpCompositeExtract %float %232 3 + %237 = OpCompositeConstruct %v3float %236 %236 %236 + %238 = OpFMul %v3float %235 %237 + %239 = OpCompositeConstruct %mat3v3float %233 %238 %234 + %240 = OpCompositeExtract %float %232 0 + %241 = OpCompositeExtract %float %232 1 + %242 = OpCompositeExtract %float %232 2 + %243 = OpCompositeConstruct %v4float %240 %241 %242 %float_0 + %244 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_15 + %245 = OpLoad %v4float %244 + %246 = OpVectorShuffle %v3float %245 %245 0 1 2 + %247 = OpVectorTimesMatrix %v3float %246 %239 + %248 = OpULessThan %bool %227 %uint_2 + %249 = OpIAdd %uint %227 %uint_1 + %250 = OpSelect %uint %248 %249 %uint_0 + %251 = OpIMul %uint %uint_2 %227 + %252 = OpIAdd %uint %uint_3 %251 + %253 = OpIAdd %uint %251 %uint_4 + %254 = OpAccessChain %_ptr_Function_FHitProxyVSToDS %param_var_I %227 + %255 = OpLoad %FHitProxyVSToDS %254 + %256 = OpAccessChain %_ptr_Function_v4float %param_var_I %227 %int_1 + %257 = OpLoad %v4float %256 + %258 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %250 %int_0 + %259 = OpLoad %FVertexFactoryInterpolantsVSToDS %258 + %260 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %259 0 + %261 = OpCompositeExtract %v4float %260 1 + %262 = OpCompositeExtract %float %261 0 + %263 = OpCompositeExtract %float %261 1 + %264 = OpCompositeExtract %float %261 2 + %265 = OpCompositeConstruct %v4float %262 %263 %264 %float_0 + %266 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %252 %int_0 + %267 = OpLoad %FVertexFactoryInterpolantsVSToDS %266 + %268 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %267 0 + %269 = OpCompositeExtract %v4float %268 1 + %270 = OpCompositeExtract %float %269 0 + %271 = OpCompositeExtract %float %269 1 + %272 = OpCompositeExtract %float %269 2 + %273 = OpCompositeConstruct %v4float %270 %271 %272 %float_0 + %274 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %253 %int_0 + %275 = OpLoad %FVertexFactoryInterpolantsVSToDS %274 + %276 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %275 0 + %277 = OpCompositeExtract %v4float %276 1 + %278 = OpCompositeExtract %float %277 0 + %279 = OpCompositeExtract %float %277 1 + %280 = OpCompositeExtract %float %277 2 + %281 = OpCompositeConstruct %v4float %278 %279 %280 %float_0 + %282 = OpLoad %v4float %256 + %283 = OpAccessChain %_ptr_Function_v4float %param_var_I %250 %int_1 + %284 = OpLoad %v4float %283 + %285 = OpFMul %v4float %54 %282 + %286 = OpFAdd %v4float %285 %284 + %287 = OpFSub %v4float %284 %282 + %288 = OpDot %float %287 %243 + %289 = OpCompositeConstruct %v4float %288 %288 %288 %288 + %290 = OpFMul %v4float %289 %243 + %291 = OpFSub %v4float %286 %290 + %292 = OpFMul %v4float %291 %119 + %293 = OpAccessChain %_ptr_Function_v4float %param_var_I %252 %int_1 + %294 = OpLoad %v4float %293 + %295 = OpAccessChain %_ptr_Function_v4float %param_var_I %253 %int_1 + %296 = OpLoad %v4float %295 + %297 = OpFMul %v4float %54 %294 + %298 = OpFAdd %v4float %297 %296 + %299 = OpFSub %v4float %296 %294 + %300 = OpDot %float %299 %273 + %301 = OpCompositeConstruct %v4float %300 %300 %300 %300 + %302 = OpFMul %v4float %301 %273 + %303 = OpFSub %v4float %298 %302 + %304 = OpFMul %v4float %303 %119 + %305 = OpFAdd %v4float %292 %304 + %306 = OpFMul %v4float %305 %120 + %307 = OpLoad %v4float %283 + %308 = OpLoad %v4float %256 + %309 = OpFMul %v4float %54 %307 + %310 = OpFAdd %v4float %309 %308 + %311 = OpFSub %v4float %308 %307 + %312 = OpDot %float %311 %265 + %313 = OpCompositeConstruct %v4float %312 %312 %312 %312 + %314 = OpFMul %v4float %313 %265 + %315 = OpFSub %v4float %310 %314 + %316 = OpFMul %v4float %315 %119 + %317 = OpLoad %v4float %295 + %318 = OpLoad %v4float %293 + %319 = OpFMul %v4float %54 %317 + %320 = OpFAdd %v4float %319 %318 + %321 = OpFSub %v4float %318 %317 + %322 = OpDot %float %321 %281 + %323 = OpCompositeConstruct %v4float %322 %322 %322 %322 + %324 = OpFMul %v4float %323 %281 + %325 = OpFSub %v4float %320 %324 + %326 = OpFMul %v4float %325 %119 + %327 = OpFAdd %v4float %316 %326 + %328 = OpFMul %v4float %327 %120 + %329 = OpCompositeConstruct %_arr_v4float_uint_3 %257 %306 %328 + %330 = OpCompositeConstruct %FPNTessellationHSToDS %255 %329 %247 %float_1 %float_1 + %331 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %255 0 + %332 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %331 0 + %333 = OpCompositeExtract %v4float %332 0 + %334 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD10_centroid %227 + OpStore %334 %333 + %335 = OpCompositeExtract %v4float %332 1 + %336 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD11_centroid %227 + OpStore %336 %335 + %337 = OpCompositeExtract %v4float %332 2 + %338 = OpAccessChain %_ptr_Output_v4float %out_var_COLOR0 %227 + OpStore %338 %337 + %339 = OpCompositeExtract %_arr_v2float_uint_2 %332 3 + %340 = OpAccessChain %_ptr_Output__arr_v2float_uint_2 %out_var_TEXCOORD0 %227 + OpStore %340 %339 + %341 = OpCompositeExtract %v4float %255 1 + %342 = OpAccessChain %_ptr_Output_v4float %out_var_VS_To_DS_Position %227 + OpStore %342 %341 + %343 = OpAccessChain %_ptr_Output__arr_v4float_uint_3 %out_var_PN_POSITION %227 + OpStore %343 %329 + %344 = OpAccessChain %_ptr_Output_v3float %out_var_PN_DisplacementScales %227 + OpStore %344 %247 + %345 = OpAccessChain %_ptr_Output_float %out_var_PN_TessellationMultiplier %227 + OpStore %345 %float_1 + %346 = OpAccessChain %_ptr_Output_float %out_var_PN_WorldDisplacementMultiplier %227 + OpStore %346 %float_1 + %347 = OpAccessChain %_ptr_Workgroup_FPNTessellationHSToDS %temp_var_hullMainRetVal %227 + OpStore %347 %330 + OpControlBarrier %uint_2 %uint_4 %uint_0 + %348 = OpIEqual %bool %227 %uint_0 + OpSelectionMerge %if_merge None + OpBranchConditional %348 %349 %if_merge + %349 = OpLabel + %350 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_0 + %351 = OpLoad %mat4v4float %350 + %352 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_7 + %353 = OpLoad %mat4v4float %352 + %354 = OpAccessChain %_ptr_Uniform_v3float %View %int_28 + %355 = OpLoad %v3float %354 + %356 = OpAccessChain %_ptr_Uniform_float %View %int_78 + %357 = OpLoad %float %356 + %358 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_0 + %359 = OpLoad %v4float %358 + %360 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_1 + %361 = OpLoad %v4float %360 + %362 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_0 %int_1 %int_2 + %363 = OpLoad %v4float %362 + %364 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_0 + %365 = OpLoad %v4float %364 + %366 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_1 + %367 = OpLoad %v4float %366 + %368 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_1 %int_1 %int_2 + %369 = OpLoad %v4float %368 + %370 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_0 + %371 = OpLoad %v4float %370 + %372 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_1 + %373 = OpLoad %v4float %372 + %374 = OpAccessChain %_ptr_Workgroup_v4float %temp_var_hullMainRetVal %uint_2 %int_1 %int_2 + %375 = OpLoad %v4float %374 + %376 = OpFAdd %v4float %361 %363 + %377 = OpFAdd %v4float %376 %367 + %378 = OpFAdd %v4float %377 %369 + %379 = OpFAdd %v4float %378 %373 + %380 = OpFAdd %v4float %379 %375 + %381 = OpFMul %v4float %380 %122 + %382 = OpFAdd %v4float %371 %365 + %383 = OpFAdd %v4float %382 %359 + %384 = OpFMul %v4float %383 %119 + %385 = OpFSub %v4float %381 %384 + %386 = OpFMul %v4float %385 %120 + %387 = OpFAdd %v4float %381 %386 + %388 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_1 %int_3 + %389 = OpLoad %float %388 + %390 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_2 %int_3 + %391 = OpLoad %float %390 + %392 = OpFAdd %float %389 %391 + %393 = OpFMul %float %float_0_5 %392 + %394 = OpCompositeInsert %v4float %393 %123 0 + %395 = OpLoad %float %390 + %396 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_0 %int_3 + %397 = OpLoad %float %396 + %398 = OpFAdd %float %395 %397 + %399 = OpFMul %float %float_0_5 %398 + %400 = OpCompositeInsert %v4float %399 %394 1 + %401 = OpLoad %float %396 + %402 = OpLoad %float %388 + %403 = OpFAdd %float %401 %402 + %404 = OpFMul %float %float_0_5 %403 + %405 = OpCompositeInsert %v4float %404 %400 2 + %406 = OpLoad %float %396 + %407 = OpLoad %float %388 + %408 = OpFAdd %float %406 %407 + %409 = OpLoad %float %390 + %410 = OpFAdd %float %408 %409 + %411 = OpFMul %float %float_0_333000004 %410 + %412 = OpCompositeInsert %v4float %411 %405 3 + %413 = OpVectorShuffle %v3float %359 %359 0 1 2 + %414 = OpVectorShuffle %v3float %365 %365 0 1 2 + %415 = OpVectorShuffle %v3float %371 %371 0 1 2 + OpBranch %416 + %416 = OpLabel + OpLoopMerge %417 %418 None + OpBranch %419 + %419 = OpLabel + %420 = OpMatrixTimesVector %v4float %353 %68 + %421 = OpCompositeExtract %float %359 0 + %422 = OpCompositeExtract %float %359 1 + %423 = OpCompositeExtract %float %359 2 + %424 = OpCompositeConstruct %v4float %421 %422 %423 %float_1 + %425 = OpMatrixTimesVector %v4float %351 %424 + %426 = OpVectorShuffle %v3float %425 %425 0 1 2 + %427 = OpVectorShuffle %v3float %420 %420 0 1 2 + %428 = OpFSub %v3float %426 %427 + %429 = OpCompositeExtract %float %425 3 + %430 = OpCompositeExtract %float %420 3 + %431 = OpFAdd %float %429 %430 + %432 = OpCompositeConstruct %v3float %431 %431 %431 + %433 = OpFOrdLessThan %v3bool %428 %432 + %434 = OpSelect %v3int %433 %74 %65 + %435 = OpFAdd %v3float %426 %427 + %436 = OpFNegate %float %429 + %437 = OpFSub %float %436 %430 + %438 = OpCompositeConstruct %v3float %437 %437 %437 + %439 = OpFOrdGreaterThan %v3bool %435 %438 + %440 = OpSelect %v3int %439 %74 %65 + %441 = OpIMul %v3int %75 %440 + %442 = OpIAdd %v3int %434 %441 + %443 = OpCompositeExtract %float %365 0 + %444 = OpCompositeExtract %float %365 1 + %445 = OpCompositeExtract %float %365 2 + %446 = OpCompositeConstruct %v4float %443 %444 %445 %float_1 + %447 = OpMatrixTimesVector %v4float %351 %446 + %448 = OpVectorShuffle %v3float %447 %447 0 1 2 + %449 = OpFSub %v3float %448 %427 + %450 = OpCompositeExtract %float %447 3 + %451 = OpFAdd %float %450 %430 + %452 = OpCompositeConstruct %v3float %451 %451 %451 + %453 = OpFOrdLessThan %v3bool %449 %452 + %454 = OpSelect %v3int %453 %74 %65 + %455 = OpFAdd %v3float %448 %427 + %456 = OpFNegate %float %450 + %457 = OpFSub %float %456 %430 + %458 = OpCompositeConstruct %v3float %457 %457 %457 + %459 = OpFOrdGreaterThan %v3bool %455 %458 + %460 = OpSelect %v3int %459 %74 %65 + %461 = OpIMul %v3int %75 %460 + %462 = OpIAdd %v3int %454 %461 + %463 = OpBitwiseOr %v3int %442 %462 + %464 = OpCompositeExtract %float %371 0 + %465 = OpCompositeExtract %float %371 1 + %466 = OpCompositeExtract %float %371 2 + %467 = OpCompositeConstruct %v4float %464 %465 %466 %float_1 + %468 = OpMatrixTimesVector %v4float %351 %467 + %469 = OpVectorShuffle %v3float %468 %468 0 1 2 + %470 = OpFSub %v3float %469 %427 + %471 = OpCompositeExtract %float %468 3 + %472 = OpFAdd %float %471 %430 + %473 = OpCompositeConstruct %v3float %472 %472 %472 + %474 = OpFOrdLessThan %v3bool %470 %473 + %475 = OpSelect %v3int %474 %74 %65 + %476 = OpFAdd %v3float %469 %427 + %477 = OpFNegate %float %471 + %478 = OpFSub %float %477 %430 + %479 = OpCompositeConstruct %v3float %478 %478 %478 + %480 = OpFOrdGreaterThan %v3bool %476 %479 + %481 = OpSelect %v3int %480 %74 %65 + %482 = OpIMul %v3int %75 %481 + %483 = OpIAdd %v3int %475 %482 + %484 = OpBitwiseOr %v3int %463 %483 + %485 = OpINotEqual %v3bool %484 %66 + %486 = OpAny %bool %485 + OpSelectionMerge %487 None + OpBranchConditional %486 %488 %487 + %488 = OpLabel + OpBranch %417 + %487 = OpLabel + %489 = OpFSub %v3float %413 %414 + %490 = OpFSub %v3float %414 %415 + %491 = OpFSub %v3float %415 %413 + %492 = OpFAdd %v3float %413 %414 + %493 = OpFMul %v3float %69 %492 + %494 = OpFSub %v3float %493 %355 + %495 = OpFAdd %v3float %414 %415 + %496 = OpFMul %v3float %69 %495 + %497 = OpFSub %v3float %496 %355 + %498 = OpFAdd %v3float %415 %413 + %499 = OpFMul %v3float %69 %498 + %500 = OpFSub %v3float %499 %355 + %501 = OpDot %float %490 %490 + %502 = OpDot %float %497 %497 + %503 = OpFDiv %float %501 %502 + %504 = OpExtInst %float %1 Sqrt %503 + %505 = OpDot %float %491 %491 + %506 = OpDot %float %500 %500 + %507 = OpFDiv %float %505 %506 + %508 = OpExtInst %float %1 Sqrt %507 + %509 = OpDot %float %489 %489 + %510 = OpDot %float %494 %494 + %511 = OpFDiv %float %509 %510 + %512 = OpExtInst %float %1 Sqrt %511 + %513 = OpCompositeConstruct %v4float %504 %508 %512 %float_1 + %514 = OpFAdd %float %504 %508 + %515 = OpFAdd %float %514 %512 + %516 = OpFMul %float %float_0_333000004 %515 + %517 = OpCompositeInsert %v4float %516 %513 3 + %518 = OpCompositeConstruct %v4float %357 %357 %357 %357 + %519 = OpFMul %v4float %518 %517 + OpBranch %417 + %418 = OpLabel + OpBranch %416 + %417 = OpLabel + %520 = OpPhi %v4float %68 %488 %519 %487 + %521 = OpFMul %v4float %412 %520 + %522 = OpExtInst %v4float %1 FClamp %521 %59 %61 + %523 = OpCompositeExtract %float %522 0 + %524 = OpCompositeExtract %float %522 1 + %525 = OpCompositeExtract %float %522 2 + %526 = OpCompositeExtract %float %522 3 + %527 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_0 + OpStore %527 %523 + %528 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_1 + OpStore %528 %524 + %529 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_2 + OpStore %529 %525 + %530 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %uint_0 + OpStore %530 %526 + OpStore %out_var_PN_POSITION9 %387 + OpBranch %if_merge + %if_merge = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc b/third_party/spirv-cross/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc new file mode 100644 index 0000000..98216e7 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/tesc/tess-factor-must-be-threadgroup.asm.tesc @@ -0,0 +1,352 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 179 +; Schema: 0 + OpCapability Tessellation + OpCapability SampledBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %MainHull "main" %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_VS_To_DS_Position %gl_InvocationID %out_var_TEXCOORD10_centroid %out_var_TEXCOORD11_centroid %out_var_VS_To_DS_Position %out_var_Flat_DisplacementScales %out_var_Flat_TessellationMultiplier %out_var_Flat_WorldDisplacementMultiplier %gl_TessLevelOuter %gl_TessLevelInner + OpExecutionMode %MainHull Triangles + OpExecutionMode %MainHull SpacingFractionalOdd + OpExecutionMode %MainHull VertexOrderCw + OpExecutionMode %MainHull OutputVertices 3 + OpSource HLSL 600 + OpName %FFlatTessellationHSToDS "FFlatTessellationHSToDS" + OpMemberName %FFlatTessellationHSToDS 0 "PassSpecificData" + OpMemberName %FFlatTessellationHSToDS 1 "DisplacementScale" + OpMemberName %FFlatTessellationHSToDS 2 "TessellationMultiplier" + OpMemberName %FFlatTessellationHSToDS 3 "WorldDisplacementMultiplier" + OpName %FBasePassVSToDS "FBasePassVSToDS" + OpMemberName %FBasePassVSToDS 0 "FactoryInterpolants" + OpMemberName %FBasePassVSToDS 1 "BasePassInterpolants" + OpMemberName %FBasePassVSToDS 2 "Position" + OpName %FVertexFactoryInterpolantsVSToDS "FVertexFactoryInterpolantsVSToDS" + OpMemberName %FVertexFactoryInterpolantsVSToDS 0 "InterpolantsVSToPS" + OpName %FVertexFactoryInterpolantsVSToPS "FVertexFactoryInterpolantsVSToPS" + OpMemberName %FVertexFactoryInterpolantsVSToPS 0 "TangentToWorld0" + OpMemberName %FVertexFactoryInterpolantsVSToPS 1 "TangentToWorld2" + OpName %FBasePassInterpolantsVSToDS "FBasePassInterpolantsVSToDS" + OpName %FSharedBasePassInterpolants "FSharedBasePassInterpolants" + OpName %type_Primitive "type.Primitive" + OpMemberName %type_Primitive 0 "Primitive_LocalToWorld" + OpMemberName %type_Primitive 1 "Primitive_InvNonUniformScaleAndDeterminantSign" + OpMemberName %type_Primitive 2 "Primitive_ObjectWorldPositionAndRadius" + OpMemberName %type_Primitive 3 "Primitive_WorldToLocal" + OpMemberName %type_Primitive 4 "Primitive_PreviousLocalToWorld" + OpMemberName %type_Primitive 5 "Primitive_PreviousWorldToLocal" + OpMemberName %type_Primitive 6 "Primitive_ActorWorldPosition" + OpMemberName %type_Primitive 7 "Primitive_UseSingleSampleShadowFromStationaryLights" + OpMemberName %type_Primitive 8 "Primitive_ObjectBounds" + OpMemberName %type_Primitive 9 "Primitive_LpvBiasMultiplier" + OpMemberName %type_Primitive 10 "Primitive_DecalReceiverMask" + OpMemberName %type_Primitive 11 "Primitive_PerObjectGBufferData" + OpMemberName %type_Primitive 12 "Primitive_UseVolumetricLightmapShadowFromStationaryLights" + OpMemberName %type_Primitive 13 "Primitive_DrawsVelocity" + OpMemberName %type_Primitive 14 "Primitive_ObjectOrientation" + OpMemberName %type_Primitive 15 "Primitive_NonUniformScale" + OpMemberName %type_Primitive 16 "Primitive_LocalObjectBoundsMin" + OpMemberName %type_Primitive 17 "Primitive_LightingChannelMask" + OpMemberName %type_Primitive 18 "Primitive_LocalObjectBoundsMax" + OpMemberName %type_Primitive 19 "Primitive_LightmapDataIndex" + OpMemberName %type_Primitive 20 "Primitive_PreSkinnedLocalBounds" + OpMemberName %type_Primitive 21 "Primitive_SingleCaptureIndex" + OpMemberName %type_Primitive 22 "Primitive_OutputVelocity" + OpMemberName %type_Primitive 23 "PrePadding_Primitive_420" + OpMemberName %type_Primitive 24 "PrePadding_Primitive_424" + OpMemberName %type_Primitive 25 "PrePadding_Primitive_428" + OpMemberName %type_Primitive 26 "Primitive_CustomPrimitiveData" + OpName %Primitive "Primitive" + OpName %type_Material "type.Material" + OpMemberName %type_Material 0 "Material_VectorExpressions" + OpMemberName %type_Material 1 "Material_ScalarExpressions" + OpName %Material "Material" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_VS_To_DS_Position "in.var.VS_To_DS_Position" + OpName %out_var_TEXCOORD10_centroid "out.var.TEXCOORD10_centroid" + OpName %out_var_TEXCOORD11_centroid "out.var.TEXCOORD11_centroid" + OpName %out_var_VS_To_DS_Position "out.var.VS_To_DS_Position" + OpName %out_var_Flat_DisplacementScales "out.var.Flat_DisplacementScales" + OpName %out_var_Flat_TessellationMultiplier "out.var.Flat_TessellationMultiplier" + OpName %out_var_Flat_WorldDisplacementMultiplier "out.var.Flat_WorldDisplacementMultiplier" + OpName %MainHull "MainHull" + OpName %param_var_I "param.var.I" + OpName %temp_var_hullMainRetVal "temp.var.hullMainRetVal" + OpName %if_merge "if.merge" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorate %gl_InvocationID BuiltIn InvocationId + OpDecorateString %gl_InvocationID UserSemantic "SV_OutputControlPointID" + OpDecorateString %out_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %out_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %out_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorateString %out_var_Flat_DisplacementScales UserSemantic "Flat_DisplacementScales" + OpDecorateString %out_var_Flat_TessellationMultiplier UserSemantic "Flat_TessellationMultiplier" + OpDecorateString %out_var_Flat_WorldDisplacementMultiplier UserSemantic "Flat_WorldDisplacementMultiplier" + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorateString %gl_TessLevelOuter UserSemantic "SV_TessFactor" + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorateString %gl_TessLevelInner UserSemantic "SV_InsideTessFactor" + OpDecorate %gl_TessLevelInner Patch + OpDecorate %in_var_TEXCOORD10_centroid Location 0 + OpDecorate %in_var_TEXCOORD11_centroid Location 1 + OpDecorate %in_var_VS_To_DS_Position Location 2 + OpDecorate %out_var_Flat_DisplacementScales Location 0 + OpDecorate %out_var_Flat_TessellationMultiplier Location 1 + OpDecorate %out_var_Flat_WorldDisplacementMultiplier Location 2 + OpDecorate %out_var_TEXCOORD10_centroid Location 3 + OpDecorate %out_var_TEXCOORD11_centroid Location 4 + OpDecorate %out_var_VS_To_DS_Position Location 5 + OpDecorate %Primitive DescriptorSet 0 + OpDecorate %Primitive Binding 0 + OpDecorate %Material DescriptorSet 0 + OpDecorate %Material Binding 1 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_Primitive 0 Offset 0 + OpMemberDecorate %type_Primitive 0 MatrixStride 16 + OpMemberDecorate %type_Primitive 0 ColMajor + OpMemberDecorate %type_Primitive 1 Offset 64 + OpMemberDecorate %type_Primitive 2 Offset 80 + OpMemberDecorate %type_Primitive 3 Offset 96 + OpMemberDecorate %type_Primitive 3 MatrixStride 16 + OpMemberDecorate %type_Primitive 3 ColMajor + OpMemberDecorate %type_Primitive 4 Offset 160 + OpMemberDecorate %type_Primitive 4 MatrixStride 16 + OpMemberDecorate %type_Primitive 4 ColMajor + OpMemberDecorate %type_Primitive 5 Offset 224 + OpMemberDecorate %type_Primitive 5 MatrixStride 16 + OpMemberDecorate %type_Primitive 5 ColMajor + OpMemberDecorate %type_Primitive 6 Offset 288 + OpMemberDecorate %type_Primitive 7 Offset 300 + OpMemberDecorate %type_Primitive 8 Offset 304 + OpMemberDecorate %type_Primitive 9 Offset 316 + OpMemberDecorate %type_Primitive 10 Offset 320 + OpMemberDecorate %type_Primitive 11 Offset 324 + OpMemberDecorate %type_Primitive 12 Offset 328 + OpMemberDecorate %type_Primitive 13 Offset 332 + OpMemberDecorate %type_Primitive 14 Offset 336 + OpMemberDecorate %type_Primitive 15 Offset 352 + OpMemberDecorate %type_Primitive 16 Offset 368 + OpMemberDecorate %type_Primitive 17 Offset 380 + OpMemberDecorate %type_Primitive 18 Offset 384 + OpMemberDecorate %type_Primitive 19 Offset 396 + OpMemberDecorate %type_Primitive 20 Offset 400 + OpMemberDecorate %type_Primitive 21 Offset 412 + OpMemberDecorate %type_Primitive 22 Offset 416 + OpMemberDecorate %type_Primitive 23 Offset 420 + OpMemberDecorate %type_Primitive 24 Offset 424 + OpMemberDecorate %type_Primitive 25 Offset 428 + OpMemberDecorate %type_Primitive 26 Offset 432 + OpDecorate %type_Primitive Block + OpDecorate %_arr_v4float_uint_3 ArrayStride 16 + OpDecorate %_arr_v4float_uint_1 ArrayStride 16 + OpMemberDecorate %type_Material 0 Offset 0 + OpMemberDecorate %type_Material 1 Offset 48 + OpDecorate %type_Material Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_4 = OpConstant %uint 4 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 + %float_0_5 = OpConstant %float 0.5 + %int_1 = OpConstant %int 1 +%float_0_333000004 = OpConstant %float 0.333000004 + %float_1 = OpConstant %float 1 + %49 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_15 = OpConstant %float 15 + %51 = OpConstantComposite %v4float %float_15 %float_15 %float_15 %float_15 +%FVertexFactoryInterpolantsVSToPS = OpTypeStruct %v4float %v4float +%FVertexFactoryInterpolantsVSToDS = OpTypeStruct %FVertexFactoryInterpolantsVSToPS +%FSharedBasePassInterpolants = OpTypeStruct +%FBasePassInterpolantsVSToDS = OpTypeStruct %FSharedBasePassInterpolants +%FBasePassVSToDS = OpTypeStruct %FVertexFactoryInterpolantsVSToDS %FBasePassInterpolantsVSToDS %v4float +%FFlatTessellationHSToDS = OpTypeStruct %FBasePassVSToDS %v3float %float %float + %int_15 = OpConstant %int 15 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%type_Primitive = OpTypeStruct %mat4v4float %v4float %v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %float %float %float %float %v4float %v4float %v3float %uint %v3float %uint %v3float %int %uint %uint %uint %uint %_arr_v4float_uint_4 +%_ptr_Uniform_type_Primitive = OpTypePointer Uniform %type_Primitive +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%type_Material = OpTypeStruct %_arr_v4float_uint_3 %_arr_v4float_uint_1 +%_ptr_Uniform_type_Material = OpTypePointer Uniform %type_Material +%_arr_v4float_uint_3_0 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3_0 = OpTypePointer Input %_arr_v4float_uint_3_0 +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Output__arr_v4float_uint_3_0 = OpTypePointer Output %_arr_v4float_uint_3_0 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Output__arr_v3float_uint_3 = OpTypePointer Output %_arr_v3float_uint_3 +%_ptr_Output__arr_float_uint_3 = OpTypePointer Output %_arr_float_uint_3 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Output__arr_float_uint_4 = OpTypePointer Output %_arr_float_uint_4 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Output__arr_float_uint_2 = OpTypePointer Output %_arr_float_uint_2 + %void = OpTypeVoid + %67 = OpTypeFunction %void +%_arr_FBasePassVSToDS_uint_3 = OpTypeArray %FBasePassVSToDS %uint_3 +%_ptr_Function__arr_FBasePassVSToDS_uint_3 = OpTypePointer Function %_arr_FBasePassVSToDS_uint_3 +%_arr_FFlatTessellationHSToDS_uint_3 = OpTypeArray %FFlatTessellationHSToDS %uint_3 +%_ptr_Function__arr_FFlatTessellationHSToDS_uint_3 = OpTypePointer Function %_arr_FFlatTessellationHSToDS_uint_3 +%_ptr_Workgroup__arr_FFlatTessellationHSToDS_uint_3 = OpTypePointer Workgroup %_arr_FFlatTessellationHSToDS_uint_3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Output_v3float = OpTypePointer Output %v3float +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Function_FFlatTessellationHSToDS = OpTypePointer Function %FFlatTessellationHSToDS +%_ptr_Workgroup_FFlatTessellationHSToDS = OpTypePointer Workgroup %FFlatTessellationHSToDS + %bool = OpTypeBool +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Workgroup_float = OpTypePointer Workgroup %float +%mat3v3float = OpTypeMatrix %v3float 3 +%_ptr_Function_FVertexFactoryInterpolantsVSToDS = OpTypePointer Function %FVertexFactoryInterpolantsVSToDS +%_ptr_Function_FBasePassVSToDS = OpTypePointer Function %FBasePassVSToDS +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %Primitive = OpVariable %_ptr_Uniform_type_Primitive Uniform + %Material = OpVariable %_ptr_Uniform_type_Material Uniform +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3_0 Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3_0 Input +%in_var_VS_To_DS_Position = OpVariable %_ptr_Input__arr_v4float_uint_3_0 Input +%gl_InvocationID = OpVariable %_ptr_Input_uint Input +%out_var_TEXCOORD10_centroid = OpVariable %_ptr_Output__arr_v4float_uint_3_0 Output +%out_var_TEXCOORD11_centroid = OpVariable %_ptr_Output__arr_v4float_uint_3_0 Output +%out_var_VS_To_DS_Position = OpVariable %_ptr_Output__arr_v4float_uint_3_0 Output +%out_var_Flat_DisplacementScales = OpVariable %_ptr_Output__arr_v3float_uint_3 Output +%out_var_Flat_TessellationMultiplier = OpVariable %_ptr_Output__arr_float_uint_3 Output +%out_var_Flat_WorldDisplacementMultiplier = OpVariable %_ptr_Output__arr_float_uint_3 Output +%gl_TessLevelOuter = OpVariable %_ptr_Output__arr_float_uint_4 Output +%gl_TessLevelInner = OpVariable %_ptr_Output__arr_float_uint_2 Output + %83 = OpConstantNull %FSharedBasePassInterpolants + %84 = OpConstantComposite %FBasePassInterpolantsVSToDS %83 + %85 = OpUndef %v4float + +; XXX: Original asm used Function here, which is wrong. +; This patches the SPIR-V to be correct. +%temp_var_hullMainRetVal = OpVariable %_ptr_Workgroup__arr_FFlatTessellationHSToDS_uint_3 Workgroup + + %MainHull = OpFunction %void None %67 + %86 = OpLabel +%param_var_I = OpVariable %_ptr_Function__arr_FBasePassVSToDS_uint_3 Function + %87 = OpLoad %_arr_v4float_uint_3_0 %in_var_TEXCOORD10_centroid + %88 = OpLoad %_arr_v4float_uint_3_0 %in_var_TEXCOORD11_centroid + %89 = OpCompositeExtract %v4float %87 0 + %90 = OpCompositeExtract %v4float %88 0 + %91 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %89 %90 + %92 = OpCompositeExtract %v4float %87 1 + %93 = OpCompositeExtract %v4float %88 1 + %94 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %92 %93 + %95 = OpCompositeExtract %v4float %87 2 + %96 = OpCompositeExtract %v4float %88 2 + %97 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToPS %95 %96 + %98 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %91 + %99 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %94 + %100 = OpCompositeConstruct %FVertexFactoryInterpolantsVSToDS %97 + %101 = OpLoad %_arr_v4float_uint_3_0 %in_var_VS_To_DS_Position + %102 = OpCompositeExtract %v4float %101 0 + %103 = OpCompositeConstruct %FBasePassVSToDS %98 %84 %102 + %104 = OpCompositeExtract %v4float %101 1 + %105 = OpCompositeConstruct %FBasePassVSToDS %99 %84 %104 + %106 = OpCompositeExtract %v4float %101 2 + %107 = OpCompositeConstruct %FBasePassVSToDS %100 %84 %106 + %108 = OpCompositeConstruct %_arr_FBasePassVSToDS_uint_3 %103 %105 %107 + OpStore %param_var_I %108 + %109 = OpLoad %uint %gl_InvocationID + %110 = OpAccessChain %_ptr_Function_FVertexFactoryInterpolantsVSToDS %param_var_I %109 %int_0 + %111 = OpLoad %FVertexFactoryInterpolantsVSToDS %110 + %112 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %111 0 + %113 = OpCompositeExtract %v4float %112 0 + %114 = OpCompositeExtract %v4float %112 1 + %115 = OpVectorShuffle %v3float %113 %113 0 1 2 + %116 = OpVectorShuffle %v3float %114 %114 0 1 2 + %117 = OpExtInst %v3float %1 Cross %116 %115 + %118 = OpCompositeExtract %float %114 3 + %119 = OpCompositeConstruct %v3float %118 %118 %118 + %120 = OpFMul %v3float %117 %119 + %121 = OpCompositeConstruct %mat3v3float %115 %120 %116 + %122 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_15 + %123 = OpLoad %v4float %122 + %124 = OpVectorShuffle %v3float %123 %123 0 1 2 + %125 = OpVectorTimesMatrix %v3float %124 %121 + %126 = OpAccessChain %_ptr_Function_FBasePassVSToDS %param_var_I %109 + %127 = OpLoad %FBasePassVSToDS %126 + %128 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_0 + %129 = OpLoad %float %128 + %130 = OpCompositeConstruct %FFlatTessellationHSToDS %127 %125 %129 %float_1 + %131 = OpCompositeExtract %FVertexFactoryInterpolantsVSToDS %127 0 + %132 = OpCompositeExtract %FVertexFactoryInterpolantsVSToPS %131 0 + %133 = OpCompositeExtract %v4float %132 0 + %134 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD10_centroid %109 + OpStore %134 %133 + %135 = OpCompositeExtract %v4float %132 1 + %136 = OpAccessChain %_ptr_Output_v4float %out_var_TEXCOORD11_centroid %109 + OpStore %136 %135 + %137 = OpCompositeExtract %v4float %127 2 + %138 = OpAccessChain %_ptr_Output_v4float %out_var_VS_To_DS_Position %109 + OpStore %138 %137 + %139 = OpAccessChain %_ptr_Output_v3float %out_var_Flat_DisplacementScales %109 + OpStore %139 %125 + %140 = OpAccessChain %_ptr_Output_float %out_var_Flat_TessellationMultiplier %109 + OpStore %140 %129 + %141 = OpAccessChain %_ptr_Output_float %out_var_Flat_WorldDisplacementMultiplier %109 + OpStore %141 %float_1 + %142 = OpAccessChain %_ptr_Workgroup_FFlatTessellationHSToDS %temp_var_hullMainRetVal %109 + OpStore %142 %130 + OpControlBarrier %uint_2 %uint_4 %uint_0 + %143 = OpIEqual %bool %109 %uint_0 + OpSelectionMerge %if_merge None + OpBranchConditional %143 %144 %if_merge + %144 = OpLabel + %145 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_1 %int_2 + %146 = OpLoad %float %145 + %147 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_2 %int_2 + %148 = OpLoad %float %147 + %149 = OpFAdd %float %146 %148 + %150 = OpFMul %float %float_0_5 %149 + %151 = OpCompositeInsert %v4float %150 %85 0 + %152 = OpLoad %float %147 + %153 = OpAccessChain %_ptr_Workgroup_float %temp_var_hullMainRetVal %uint_0 %int_2 + %154 = OpLoad %float %153 + %155 = OpFAdd %float %152 %154 + %156 = OpFMul %float %float_0_5 %155 + %157 = OpCompositeInsert %v4float %156 %151 1 + %158 = OpLoad %float %153 + %159 = OpLoad %float %145 + %160 = OpFAdd %float %158 %159 + %161 = OpFMul %float %float_0_5 %160 + %162 = OpCompositeInsert %v4float %161 %157 2 + %163 = OpLoad %float %153 + %164 = OpLoad %float %145 + %165 = OpFAdd %float %163 %164 + %166 = OpLoad %float %147 + %167 = OpFAdd %float %165 %166 + %168 = OpFMul %float %float_0_333000004 %167 + %169 = OpCompositeInsert %v4float %168 %162 3 + %170 = OpExtInst %v4float %1 FClamp %169 %49 %51 + %171 = OpCompositeExtract %float %170 0 + %172 = OpCompositeExtract %float %170 1 + %173 = OpCompositeExtract %float %170 2 + %174 = OpCompositeExtract %float %170 3 + %175 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_0 + OpStore %175 %171 + %176 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_1 + OpStore %176 %172 + %177 = OpAccessChain %_ptr_Output_float %gl_TessLevelOuter %uint_2 + OpStore %177 %173 + %178 = OpAccessChain %_ptr_Output_float %gl_TessLevelInner %uint_0 + OpStore %178 %174 + OpBranch %if_merge + %if_merge = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese b/third_party/spirv-cross/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese new file mode 100644 index 0000000..dc543d1 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/tese/ds-double-gl-in-deref.asm.tese @@ -0,0 +1,1046 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 310 +; Schema: 0 + OpCapability Tessellation + OpCapability SampledBuffer + OpCapability StorageImageExtendedFormats + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %MainDomain "main" %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_COLOR0 %in_var_TEXCOORD0 %in_var_PRIMITIVE_ID %in_var_VS_to_DS_Position %in_var_PN_POSITION %in_var_PN_DisplacementScales %in_var_PN_TessellationMultiplier %in_var_PN_WorldDisplacementMultiplier %gl_TessLevelOuter %gl_TessLevelInner %in_var_PN_POSITION9 %gl_TessCoord %out_var_TEXCOORD10_centroid %out_var_TEXCOORD11_centroid %out_var_COLOR0 %out_var_TEXCOORD0 %out_var_PRIMITIVE_ID %out_var_TEXCOORD6 %out_var_TEXCOORD8 %out_var_TEXCOORD7 %gl_Position + OpExecutionMode %MainDomain Triangles + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_ClipToWorld" + OpMemberName %type_View 3 "View_TranslatedWorldToView" + OpMemberName %type_View 4 "View_ViewToTranslatedWorld" + OpMemberName %type_View 5 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 6 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 7 "View_ViewToClip" + OpMemberName %type_View 8 "View_ViewToClipNoAA" + OpMemberName %type_View 9 "View_ClipToView" + OpMemberName %type_View 10 "View_ClipToTranslatedWorld" + OpMemberName %type_View 11 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 12 "View_ScreenToWorld" + OpMemberName %type_View 13 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 14 "View_ViewForward" + OpMemberName %type_View 15 "PrePadding_View_908" + OpMemberName %type_View 16 "View_ViewUp" + OpMemberName %type_View 17 "PrePadding_View_924" + OpMemberName %type_View 18 "View_ViewRight" + OpMemberName %type_View 19 "PrePadding_View_940" + OpMemberName %type_View 20 "View_HMDViewNoRollUp" + OpMemberName %type_View 21 "PrePadding_View_956" + OpMemberName %type_View 22 "View_HMDViewNoRollRight" + OpMemberName %type_View 23 "PrePadding_View_972" + OpMemberName %type_View 24 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 25 "View_ScreenPositionScaleBias" + OpMemberName %type_View 26 "View_WorldCameraOrigin" + OpMemberName %type_View 27 "PrePadding_View_1020" + OpMemberName %type_View 28 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 29 "PrePadding_View_1036" + OpMemberName %type_View 30 "View_WorldViewOrigin" + OpMemberName %type_View 31 "PrePadding_View_1052" + OpMemberName %type_View 32 "View_PreViewTranslation" + OpMemberName %type_View 33 "PrePadding_View_1068" + OpMemberName %type_View 34 "View_PrevProjection" + OpMemberName %type_View 35 "View_PrevViewProj" + OpMemberName %type_View 36 "View_PrevViewRotationProj" + OpMemberName %type_View 37 "View_PrevViewToClip" + OpMemberName %type_View 38 "View_PrevClipToView" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 40 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 41 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 42 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 43 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 44 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 45 "PrePadding_View_1724" + OpMemberName %type_View 46 "View_PrevWorldViewOrigin" + OpMemberName %type_View 47 "PrePadding_View_1740" + OpMemberName %type_View 48 "View_PrevPreViewTranslation" + OpMemberName %type_View 49 "PrePadding_View_1756" + OpMemberName %type_View 50 "View_PrevInvViewProj" + OpMemberName %type_View 51 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 52 "View_ClipToPrevClip" + OpMemberName %type_View 53 "View_TemporalAAJitter" + OpMemberName %type_View 54 "View_GlobalClippingPlane" + OpMemberName %type_View 55 "View_FieldOfViewWideAngles" + OpMemberName %type_View 56 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 57 "View_ViewRectMin" + OpMemberName %type_View 58 "View_ViewSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferSizeAndInvSize" + OpMemberName %type_View 60 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 61 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 62 "View_PreExposure" + OpMemberName %type_View 63 "View_OneOverPreExposure" + OpMemberName %type_View 64 "PrePadding_View_2076" + OpMemberName %type_View 65 "View_DiffuseOverrideParameter" + OpMemberName %type_View 66 "View_SpecularOverrideParameter" + OpMemberName %type_View 67 "View_NormalOverrideParameter" + OpMemberName %type_View 68 "View_RoughnessOverrideParameter" + OpMemberName %type_View 69 "View_PrevFrameGameTime" + OpMemberName %type_View 70 "View_PrevFrameRealTime" + OpMemberName %type_View 71 "View_OutOfBoundsMask" + OpMemberName %type_View 72 "PrePadding_View_2148" + OpMemberName %type_View 73 "PrePadding_View_2152" + OpMemberName %type_View 74 "PrePadding_View_2156" + OpMemberName %type_View 75 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 76 "View_CullingSign" + OpMemberName %type_View 77 "View_NearPlane" + OpMemberName %type_View 78 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 79 "View_GameTime" + OpMemberName %type_View 80 "View_RealTime" + OpMemberName %type_View 81 "View_DeltaTime" + OpMemberName %type_View 82 "View_MaterialTextureMipBias" + OpMemberName %type_View 83 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 84 "View_Random" + OpMemberName %type_View 85 "View_FrameNumber" + OpMemberName %type_View 86 "View_StateFrameIndexMod8" + OpMemberName %type_View 87 "View_StateFrameIndex" + OpMemberName %type_View 88 "View_CameraCut" + OpMemberName %type_View 89 "View_UnlitViewmodeMask" + OpMemberName %type_View 90 "PrePadding_View_2228" + OpMemberName %type_View 91 "PrePadding_View_2232" + OpMemberName %type_View 92 "PrePadding_View_2236" + OpMemberName %type_View 93 "View_DirectionalLightColor" + OpMemberName %type_View 94 "View_DirectionalLightDirection" + OpMemberName %type_View 95 "PrePadding_View_2268" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 97 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 98 "View_TemporalAAParams" + OpMemberName %type_View 99 "View_CircleDOFParams" + OpMemberName %type_View 100 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 101 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 102 "View_DepthOfFieldScale" + OpMemberName %type_View 103 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 104 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 105 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 106 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 107 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 108 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 109 "View_GeneralPurposeTweak" + OpMemberName %type_View 110 "View_DemosaicVposOffset" + OpMemberName %type_View 111 "PrePadding_View_2412" + OpMemberName %type_View 112 "View_IndirectLightingColorScale" + OpMemberName %type_View 113 "View_HDR32bppEncodingMode" + OpMemberName %type_View 114 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 115 "View_AtmosphericFogSunPower" + OpMemberName %type_View 116 "View_AtmosphericFogPower" + OpMemberName %type_View 117 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 118 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 119 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 120 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 121 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 122 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 123 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 124 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 125 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 126 "View_AtmosphericFogSunDiscHalfApexAngleRadian" + OpMemberName %type_View 127 "PrePadding_View_2492" + OpMemberName %type_View 128 "View_AtmosphericFogSunDiscLuminance" + OpMemberName %type_View 129 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 130 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 131 "PrePadding_View_2520" + OpMemberName %type_View 132 "PrePadding_View_2524" + OpMemberName %type_View 133 "View_AtmosphericFogSunColor" + OpMemberName %type_View 134 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 135 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 136 "View_AmbientCubemapTint" + OpMemberName %type_View 137 "View_AmbientCubemapIntensity" + OpMemberName %type_View 138 "View_SkyLightParameters" + OpMemberName %type_View 139 "PrePadding_View_2584" + OpMemberName %type_View 140 "PrePadding_View_2588" + OpMemberName %type_View 141 "View_SkyLightColor" + OpMemberName %type_View 142 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 143 "View_MobilePreviewMode" + OpMemberName %type_View 144 "View_HMDEyePaddingOffset" + OpMemberName %type_View 145 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 146 "View_ShowDecalsMask" + OpMemberName %type_View 147 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 148 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 149 "PrePadding_View_2744" + OpMemberName %type_View 150 "PrePadding_View_2748" + OpMemberName %type_View 151 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 152 "View_StereoPassIndex" + OpMemberName %type_View 153 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 154 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 155 "View_GlobalVolumeDimension" + OpMemberName %type_View 156 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 157 "View_MaxGlobalDistance" + OpMemberName %type_View 158 "PrePadding_View_2908" + OpMemberName %type_View 159 "View_CursorPosition" + OpMemberName %type_View 160 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 161 "PrePadding_View_2924" + OpMemberName %type_View 162 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 163 "PrePadding_View_2940" + OpMemberName %type_View 164 "View_VolumetricFogGridZParams" + OpMemberName %type_View 165 "PrePadding_View_2956" + OpMemberName %type_View 166 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 167 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 168 "PrePadding_View_2972" + OpMemberName %type_View 169 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 170 "PrePadding_View_2988" + OpMemberName %type_View 171 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 172 "PrePadding_View_3004" + OpMemberName %type_View 173 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 174 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 175 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 176 "View_StereoIPD" + OpMemberName %type_View 177 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 178 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_sampler "type.sampler" + OpName %type_2d_image "type.2d.image" + OpName %type_ShadowDepthPass "type.ShadowDepthPass" + OpMemberName %type_ShadowDepthPass 0 "PrePadding_ShadowDepthPass_LPV_0" + OpMemberName %type_ShadowDepthPass 1 "PrePadding_ShadowDepthPass_LPV_4" + OpMemberName %type_ShadowDepthPass 2 "PrePadding_ShadowDepthPass_LPV_8" + OpMemberName %type_ShadowDepthPass 3 "PrePadding_ShadowDepthPass_LPV_12" + OpMemberName %type_ShadowDepthPass 4 "PrePadding_ShadowDepthPass_LPV_16" + OpMemberName %type_ShadowDepthPass 5 "PrePadding_ShadowDepthPass_LPV_20" + OpMemberName %type_ShadowDepthPass 6 "PrePadding_ShadowDepthPass_LPV_24" + OpMemberName %type_ShadowDepthPass 7 "PrePadding_ShadowDepthPass_LPV_28" + OpMemberName %type_ShadowDepthPass 8 "PrePadding_ShadowDepthPass_LPV_32" + OpMemberName %type_ShadowDepthPass 9 "PrePadding_ShadowDepthPass_LPV_36" + OpMemberName %type_ShadowDepthPass 10 "PrePadding_ShadowDepthPass_LPV_40" + OpMemberName %type_ShadowDepthPass 11 "PrePadding_ShadowDepthPass_LPV_44" + OpMemberName %type_ShadowDepthPass 12 "PrePadding_ShadowDepthPass_LPV_48" + OpMemberName %type_ShadowDepthPass 13 "PrePadding_ShadowDepthPass_LPV_52" + OpMemberName %type_ShadowDepthPass 14 "PrePadding_ShadowDepthPass_LPV_56" + OpMemberName %type_ShadowDepthPass 15 "PrePadding_ShadowDepthPass_LPV_60" + OpMemberName %type_ShadowDepthPass 16 "PrePadding_ShadowDepthPass_LPV_64" + OpMemberName %type_ShadowDepthPass 17 "PrePadding_ShadowDepthPass_LPV_68" + OpMemberName %type_ShadowDepthPass 18 "PrePadding_ShadowDepthPass_LPV_72" + OpMemberName %type_ShadowDepthPass 19 "PrePadding_ShadowDepthPass_LPV_76" + OpMemberName %type_ShadowDepthPass 20 "PrePadding_ShadowDepthPass_LPV_80" + OpMemberName %type_ShadowDepthPass 21 "PrePadding_ShadowDepthPass_LPV_84" + OpMemberName %type_ShadowDepthPass 22 "PrePadding_ShadowDepthPass_LPV_88" + OpMemberName %type_ShadowDepthPass 23 "PrePadding_ShadowDepthPass_LPV_92" + OpMemberName %type_ShadowDepthPass 24 "PrePadding_ShadowDepthPass_LPV_96" + OpMemberName %type_ShadowDepthPass 25 "PrePadding_ShadowDepthPass_LPV_100" + OpMemberName %type_ShadowDepthPass 26 "PrePadding_ShadowDepthPass_LPV_104" + OpMemberName %type_ShadowDepthPass 27 "PrePadding_ShadowDepthPass_LPV_108" + OpMemberName %type_ShadowDepthPass 28 "PrePadding_ShadowDepthPass_LPV_112" + OpMemberName %type_ShadowDepthPass 29 "PrePadding_ShadowDepthPass_LPV_116" + OpMemberName %type_ShadowDepthPass 30 "PrePadding_ShadowDepthPass_LPV_120" + OpMemberName %type_ShadowDepthPass 31 "PrePadding_ShadowDepthPass_LPV_124" + OpMemberName %type_ShadowDepthPass 32 "PrePadding_ShadowDepthPass_LPV_128" + OpMemberName %type_ShadowDepthPass 33 "PrePadding_ShadowDepthPass_LPV_132" + OpMemberName %type_ShadowDepthPass 34 "PrePadding_ShadowDepthPass_LPV_136" + OpMemberName %type_ShadowDepthPass 35 "PrePadding_ShadowDepthPass_LPV_140" + OpMemberName %type_ShadowDepthPass 36 "PrePadding_ShadowDepthPass_LPV_144" + OpMemberName %type_ShadowDepthPass 37 "PrePadding_ShadowDepthPass_LPV_148" + OpMemberName %type_ShadowDepthPass 38 "PrePadding_ShadowDepthPass_LPV_152" + OpMemberName %type_ShadowDepthPass 39 "PrePadding_ShadowDepthPass_LPV_156" + OpMemberName %type_ShadowDepthPass 40 "PrePadding_ShadowDepthPass_LPV_160" + OpMemberName %type_ShadowDepthPass 41 "PrePadding_ShadowDepthPass_LPV_164" + OpMemberName %type_ShadowDepthPass 42 "PrePadding_ShadowDepthPass_LPV_168" + OpMemberName %type_ShadowDepthPass 43 "PrePadding_ShadowDepthPass_LPV_172" + OpMemberName %type_ShadowDepthPass 44 "PrePadding_ShadowDepthPass_LPV_176" + OpMemberName %type_ShadowDepthPass 45 "PrePadding_ShadowDepthPass_LPV_180" + OpMemberName %type_ShadowDepthPass 46 "PrePadding_ShadowDepthPass_LPV_184" + OpMemberName %type_ShadowDepthPass 47 "PrePadding_ShadowDepthPass_LPV_188" + OpMemberName %type_ShadowDepthPass 48 "PrePadding_ShadowDepthPass_LPV_192" + OpMemberName %type_ShadowDepthPass 49 "PrePadding_ShadowDepthPass_LPV_196" + OpMemberName %type_ShadowDepthPass 50 "PrePadding_ShadowDepthPass_LPV_200" + OpMemberName %type_ShadowDepthPass 51 "PrePadding_ShadowDepthPass_LPV_204" + OpMemberName %type_ShadowDepthPass 52 "PrePadding_ShadowDepthPass_LPV_208" + OpMemberName %type_ShadowDepthPass 53 "PrePadding_ShadowDepthPass_LPV_212" + OpMemberName %type_ShadowDepthPass 54 "PrePadding_ShadowDepthPass_LPV_216" + OpMemberName %type_ShadowDepthPass 55 "PrePadding_ShadowDepthPass_LPV_220" + OpMemberName %type_ShadowDepthPass 56 "PrePadding_ShadowDepthPass_LPV_224" + OpMemberName %type_ShadowDepthPass 57 "PrePadding_ShadowDepthPass_LPV_228" + OpMemberName %type_ShadowDepthPass 58 "PrePadding_ShadowDepthPass_LPV_232" + OpMemberName %type_ShadowDepthPass 59 "PrePadding_ShadowDepthPass_LPV_236" + OpMemberName %type_ShadowDepthPass 60 "PrePadding_ShadowDepthPass_LPV_240" + OpMemberName %type_ShadowDepthPass 61 "PrePadding_ShadowDepthPass_LPV_244" + OpMemberName %type_ShadowDepthPass 62 "PrePadding_ShadowDepthPass_LPV_248" + OpMemberName %type_ShadowDepthPass 63 "PrePadding_ShadowDepthPass_LPV_252" + OpMemberName %type_ShadowDepthPass 64 "PrePadding_ShadowDepthPass_LPV_256" + OpMemberName %type_ShadowDepthPass 65 "PrePadding_ShadowDepthPass_LPV_260" + OpMemberName %type_ShadowDepthPass 66 "PrePadding_ShadowDepthPass_LPV_264" + OpMemberName %type_ShadowDepthPass 67 "PrePadding_ShadowDepthPass_LPV_268" + OpMemberName %type_ShadowDepthPass 68 "ShadowDepthPass_LPV_mRsmToWorld" + OpMemberName %type_ShadowDepthPass 69 "ShadowDepthPass_LPV_mLightColour" + OpMemberName %type_ShadowDepthPass 70 "ShadowDepthPass_LPV_GeometryVolumeCaptureLightDirection" + OpMemberName %type_ShadowDepthPass 71 "ShadowDepthPass_LPV_mEyePos" + OpMemberName %type_ShadowDepthPass 72 "ShadowDepthPass_LPV_mOldGridOffset" + OpMemberName %type_ShadowDepthPass 73 "PrePadding_ShadowDepthPass_LPV_396" + OpMemberName %type_ShadowDepthPass 74 "ShadowDepthPass_LPV_mLpvGridOffset" + OpMemberName %type_ShadowDepthPass 75 "ShadowDepthPass_LPV_ClearMultiplier" + OpMemberName %type_ShadowDepthPass 76 "ShadowDepthPass_LPV_LpvScale" + OpMemberName %type_ShadowDepthPass 77 "ShadowDepthPass_LPV_OneOverLpvScale" + OpMemberName %type_ShadowDepthPass 78 "ShadowDepthPass_LPV_DirectionalOcclusionIntensity" + OpMemberName %type_ShadowDepthPass 79 "ShadowDepthPass_LPV_DirectionalOcclusionRadius" + OpMemberName %type_ShadowDepthPass 80 "ShadowDepthPass_LPV_RsmAreaIntensityMultiplier" + OpMemberName %type_ShadowDepthPass 81 "ShadowDepthPass_LPV_RsmPixelToTexcoordMultiplier" + OpMemberName %type_ShadowDepthPass 82 "ShadowDepthPass_LPV_SecondaryOcclusionStrength" + OpMemberName %type_ShadowDepthPass 83 "ShadowDepthPass_LPV_SecondaryBounceStrength" + OpMemberName %type_ShadowDepthPass 84 "ShadowDepthPass_LPV_VplInjectionBias" + OpMemberName %type_ShadowDepthPass 85 "ShadowDepthPass_LPV_GeometryVolumeInjectionBias" + OpMemberName %type_ShadowDepthPass 86 "ShadowDepthPass_LPV_EmissiveInjectionMultiplier" + OpMemberName %type_ShadowDepthPass 87 "ShadowDepthPass_LPV_PropagationIndex" + OpMemberName %type_ShadowDepthPass 88 "ShadowDepthPass_ProjectionMatrix" + OpMemberName %type_ShadowDepthPass 89 "ShadowDepthPass_ViewMatrix" + OpMemberName %type_ShadowDepthPass 90 "ShadowDepthPass_ShadowParams" + OpMemberName %type_ShadowDepthPass 91 "ShadowDepthPass_bClampToNearPlane" + OpMemberName %type_ShadowDepthPass 92 "PrePadding_ShadowDepthPass_612" + OpMemberName %type_ShadowDepthPass 93 "PrePadding_ShadowDepthPass_616" + OpMemberName %type_ShadowDepthPass 94 "PrePadding_ShadowDepthPass_620" + OpMemberName %type_ShadowDepthPass 95 "ShadowDepthPass_ShadowViewProjectionMatrices" + OpMemberName %type_ShadowDepthPass 96 "ShadowDepthPass_ShadowViewMatrices" + OpName %ShadowDepthPass "ShadowDepthPass" + OpName %Material_Texture2D_3 "Material_Texture2D_3" + OpName %Material_Texture2D_3Sampler "Material_Texture2D_3Sampler" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_COLOR0 "in.var.COLOR0" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_PRIMITIVE_ID "in.var.PRIMITIVE_ID" + OpName %in_var_VS_to_DS_Position "in.var.VS_to_DS_Position" + OpName %in_var_PN_POSITION "in.var.PN_POSITION" + OpName %in_var_PN_DisplacementScales "in.var.PN_DisplacementScales" + OpName %in_var_PN_TessellationMultiplier "in.var.PN_TessellationMultiplier" + OpName %in_var_PN_WorldDisplacementMultiplier "in.var.PN_WorldDisplacementMultiplier" + OpName %in_var_PN_POSITION9 "in.var.PN_POSITION9" + OpName %out_var_TEXCOORD10_centroid "out.var.TEXCOORD10_centroid" + OpName %out_var_TEXCOORD11_centroid "out.var.TEXCOORD11_centroid" + OpName %out_var_COLOR0 "out.var.COLOR0" + OpName %out_var_TEXCOORD0 "out.var.TEXCOORD0" + OpName %out_var_PRIMITIVE_ID "out.var.PRIMITIVE_ID" + OpName %out_var_TEXCOORD6 "out.var.TEXCOORD6" + OpName %out_var_TEXCOORD8 "out.var.TEXCOORD8" + OpName %out_var_TEXCOORD7 "out.var.TEXCOORD7" + OpName %MainDomain "MainDomain" + OpName %type_sampled_image "type.sampled.image" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_COLOR0 UserSemantic "COLOR0" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %in_var_PRIMITIVE_ID UserSemantic "PRIMITIVE_ID" + OpDecorateString %in_var_VS_to_DS_Position UserSemantic "VS_to_DS_Position" + OpDecorateString %in_var_PN_POSITION UserSemantic "PN_POSITION" + OpDecorateString %in_var_PN_DisplacementScales UserSemantic "PN_DisplacementScales" + OpDecorateString %in_var_PN_TessellationMultiplier UserSemantic "PN_TessellationMultiplier" + OpDecorateString %in_var_PN_WorldDisplacementMultiplier UserSemantic "PN_WorldDisplacementMultiplier" + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorateString %gl_TessLevelOuter UserSemantic "SV_TessFactor" + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorateString %gl_TessLevelInner UserSemantic "SV_InsideTessFactor" + OpDecorate %gl_TessLevelInner Patch + OpDecorateString %in_var_PN_POSITION9 UserSemantic "PN_POSITION9" + OpDecorate %in_var_PN_POSITION9 Patch + OpDecorate %gl_TessCoord BuiltIn TessCoord + OpDecorateString %gl_TessCoord UserSemantic "SV_DomainLocation" + OpDecorate %gl_TessCoord Patch + OpDecorateString %out_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %out_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %out_var_COLOR0 UserSemantic "COLOR0" + OpDecorateString %out_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %out_var_PRIMITIVE_ID UserSemantic "PRIMITIVE_ID" + OpDecorateString %out_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorateString %out_var_TEXCOORD8 UserSemantic "TEXCOORD8" + OpDecorateString %out_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorate %gl_Position BuiltIn Position + OpDecorateString %gl_Position UserSemantic "SV_POSITION" + OpDecorate %in_var_COLOR0 Location 0 + OpDecorate %in_var_PN_DisplacementScales Location 1 + OpDecorate %in_var_PN_POSITION Location 2 + OpDecorate %in_var_PN_POSITION9 Location 5 + OpDecorate %in_var_PN_TessellationMultiplier Location 6 + OpDecorate %in_var_PN_WorldDisplacementMultiplier Location 7 + OpDecorate %in_var_PRIMITIVE_ID Location 8 + OpDecorate %in_var_TEXCOORD0 Location 9 + OpDecorate %in_var_TEXCOORD10_centroid Location 10 + OpDecorate %in_var_TEXCOORD11_centroid Location 11 + OpDecorate %in_var_VS_to_DS_Position Location 12 + OpDecorate %out_var_TEXCOORD10_centroid Location 0 + OpDecorate %out_var_TEXCOORD11_centroid Location 1 + OpDecorate %out_var_COLOR0 Location 2 + OpDecorate %out_var_TEXCOORD0 Location 3 + OpDecorate %out_var_PRIMITIVE_ID Location 4 + OpDecorate %out_var_TEXCOORD6 Location 5 + OpDecorate %out_var_TEXCOORD8 Location 6 + OpDecorate %out_var_TEXCOORD7 Location 7 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %ShadowDepthPass DescriptorSet 0 + OpDecorate %ShadowDepthPass Binding 1 + OpDecorate %Material_Texture2D_3 DescriptorSet 0 + OpDecorate %Material_Texture2D_3 Binding 0 + OpDecorate %Material_Texture2D_3Sampler DescriptorSet 0 + OpDecorate %Material_Texture2D_3Sampler Binding 0 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 13 MatrixStride 16 + OpMemberDecorate %type_View 13 ColMajor + OpMemberDecorate %type_View 14 Offset 896 + OpMemberDecorate %type_View 15 Offset 908 + OpMemberDecorate %type_View 16 Offset 912 + OpMemberDecorate %type_View 17 Offset 924 + OpMemberDecorate %type_View 18 Offset 928 + OpMemberDecorate %type_View 19 Offset 940 + OpMemberDecorate %type_View 20 Offset 944 + OpMemberDecorate %type_View 21 Offset 956 + OpMemberDecorate %type_View 22 Offset 960 + OpMemberDecorate %type_View 23 Offset 972 + OpMemberDecorate %type_View 24 Offset 976 + OpMemberDecorate %type_View 25 Offset 992 + OpMemberDecorate %type_View 26 Offset 1008 + OpMemberDecorate %type_View 27 Offset 1020 + OpMemberDecorate %type_View 28 Offset 1024 + OpMemberDecorate %type_View 29 Offset 1036 + OpMemberDecorate %type_View 30 Offset 1040 + OpMemberDecorate %type_View 31 Offset 1052 + OpMemberDecorate %type_View 32 Offset 1056 + OpMemberDecorate %type_View 33 Offset 1068 + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 43 MatrixStride 16 + OpMemberDecorate %type_View 43 ColMajor + OpMemberDecorate %type_View 44 Offset 1712 + OpMemberDecorate %type_View 45 Offset 1724 + OpMemberDecorate %type_View 46 Offset 1728 + OpMemberDecorate %type_View 47 Offset 1740 + OpMemberDecorate %type_View 48 Offset 1744 + OpMemberDecorate %type_View 49 Offset 1756 + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 52 MatrixStride 16 + OpMemberDecorate %type_View 52 ColMajor + OpMemberDecorate %type_View 53 Offset 1952 + OpMemberDecorate %type_View 54 Offset 1968 + OpMemberDecorate %type_View 55 Offset 1984 + OpMemberDecorate %type_View 56 Offset 1992 + OpMemberDecorate %type_View 57 Offset 2000 + OpMemberDecorate %type_View 58 Offset 2016 + OpMemberDecorate %type_View 59 Offset 2032 + OpMemberDecorate %type_View 60 Offset 2048 + OpMemberDecorate %type_View 61 Offset 2064 + OpMemberDecorate %type_View 62 Offset 2068 + OpMemberDecorate %type_View 63 Offset 2072 + OpMemberDecorate %type_View 64 Offset 2076 + OpMemberDecorate %type_View 65 Offset 2080 + OpMemberDecorate %type_View 66 Offset 2096 + OpMemberDecorate %type_View 67 Offset 2112 + OpMemberDecorate %type_View 68 Offset 2128 + OpMemberDecorate %type_View 69 Offset 2136 + OpMemberDecorate %type_View 70 Offset 2140 + OpMemberDecorate %type_View 71 Offset 2144 + OpMemberDecorate %type_View 72 Offset 2148 + OpMemberDecorate %type_View 73 Offset 2152 + OpMemberDecorate %type_View 74 Offset 2156 + OpMemberDecorate %type_View 75 Offset 2160 + OpMemberDecorate %type_View 76 Offset 2172 + OpMemberDecorate %type_View 77 Offset 2176 + OpMemberDecorate %type_View 78 Offset 2180 + OpMemberDecorate %type_View 79 Offset 2184 + OpMemberDecorate %type_View 80 Offset 2188 + OpMemberDecorate %type_View 81 Offset 2192 + OpMemberDecorate %type_View 82 Offset 2196 + OpMemberDecorate %type_View 83 Offset 2200 + OpMemberDecorate %type_View 84 Offset 2204 + OpMemberDecorate %type_View 85 Offset 2208 + OpMemberDecorate %type_View 86 Offset 2212 + OpMemberDecorate %type_View 87 Offset 2216 + OpMemberDecorate %type_View 88 Offset 2220 + OpMemberDecorate %type_View 89 Offset 2224 + OpMemberDecorate %type_View 90 Offset 2228 + OpMemberDecorate %type_View 91 Offset 2232 + OpMemberDecorate %type_View 92 Offset 2236 + OpMemberDecorate %type_View 93 Offset 2240 + OpMemberDecorate %type_View 94 Offset 2256 + OpMemberDecorate %type_View 95 Offset 2268 + OpMemberDecorate %type_View 96 Offset 2272 + OpMemberDecorate %type_View 97 Offset 2304 + OpMemberDecorate %type_View 98 Offset 2336 + OpMemberDecorate %type_View 99 Offset 2352 + OpMemberDecorate %type_View 100 Offset 2368 + OpMemberDecorate %type_View 101 Offset 2372 + OpMemberDecorate %type_View 102 Offset 2376 + OpMemberDecorate %type_View 103 Offset 2380 + OpMemberDecorate %type_View 104 Offset 2384 + OpMemberDecorate %type_View 105 Offset 2388 + OpMemberDecorate %type_View 106 Offset 2392 + OpMemberDecorate %type_View 107 Offset 2396 + OpMemberDecorate %type_View 108 Offset 2400 + OpMemberDecorate %type_View 109 Offset 2404 + OpMemberDecorate %type_View 110 Offset 2408 + OpMemberDecorate %type_View 111 Offset 2412 + OpMemberDecorate %type_View 112 Offset 2416 + OpMemberDecorate %type_View 113 Offset 2428 + OpMemberDecorate %type_View 114 Offset 2432 + OpMemberDecorate %type_View 115 Offset 2444 + OpMemberDecorate %type_View 116 Offset 2448 + OpMemberDecorate %type_View 117 Offset 2452 + OpMemberDecorate %type_View 118 Offset 2456 + OpMemberDecorate %type_View 119 Offset 2460 + OpMemberDecorate %type_View 120 Offset 2464 + OpMemberDecorate %type_View 121 Offset 2468 + OpMemberDecorate %type_View 122 Offset 2472 + OpMemberDecorate %type_View 123 Offset 2476 + OpMemberDecorate %type_View 124 Offset 2480 + OpMemberDecorate %type_View 125 Offset 2484 + OpMemberDecorate %type_View 126 Offset 2488 + OpMemberDecorate %type_View 127 Offset 2492 + OpMemberDecorate %type_View 128 Offset 2496 + OpMemberDecorate %type_View 129 Offset 2512 + OpMemberDecorate %type_View 130 Offset 2516 + OpMemberDecorate %type_View 131 Offset 2520 + OpMemberDecorate %type_View 132 Offset 2524 + OpMemberDecorate %type_View 133 Offset 2528 + OpMemberDecorate %type_View 134 Offset 2544 + OpMemberDecorate %type_View 135 Offset 2556 + OpMemberDecorate %type_View 136 Offset 2560 + OpMemberDecorate %type_View 137 Offset 2576 + OpMemberDecorate %type_View 138 Offset 2580 + OpMemberDecorate %type_View 139 Offset 2584 + OpMemberDecorate %type_View 140 Offset 2588 + OpMemberDecorate %type_View 141 Offset 2592 + OpMemberDecorate %type_View 142 Offset 2608 + OpMemberDecorate %type_View 143 Offset 2720 + OpMemberDecorate %type_View 144 Offset 2724 + OpMemberDecorate %type_View 145 Offset 2728 + OpMemberDecorate %type_View 146 Offset 2732 + OpMemberDecorate %type_View 147 Offset 2736 + OpMemberDecorate %type_View 148 Offset 2740 + OpMemberDecorate %type_View 149 Offset 2744 + OpMemberDecorate %type_View 150 Offset 2748 + OpMemberDecorate %type_View 151 Offset 2752 + OpMemberDecorate %type_View 152 Offset 2764 + OpMemberDecorate %type_View 153 Offset 2768 + OpMemberDecorate %type_View 154 Offset 2832 + OpMemberDecorate %type_View 155 Offset 2896 + OpMemberDecorate %type_View 156 Offset 2900 + OpMemberDecorate %type_View 157 Offset 2904 + OpMemberDecorate %type_View 158 Offset 2908 + OpMemberDecorate %type_View 159 Offset 2912 + OpMemberDecorate %type_View 160 Offset 2920 + OpMemberDecorate %type_View 161 Offset 2924 + OpMemberDecorate %type_View 162 Offset 2928 + OpMemberDecorate %type_View 163 Offset 2940 + OpMemberDecorate %type_View 164 Offset 2944 + OpMemberDecorate %type_View 165 Offset 2956 + OpMemberDecorate %type_View 166 Offset 2960 + OpMemberDecorate %type_View 167 Offset 2968 + OpMemberDecorate %type_View 168 Offset 2972 + OpMemberDecorate %type_View 169 Offset 2976 + OpMemberDecorate %type_View 170 Offset 2988 + OpMemberDecorate %type_View 171 Offset 2992 + OpMemberDecorate %type_View 172 Offset 3004 + OpMemberDecorate %type_View 173 Offset 3008 + OpMemberDecorate %type_View 174 Offset 3020 + OpMemberDecorate %type_View 175 Offset 3024 + OpMemberDecorate %type_View 176 Offset 3036 + OpMemberDecorate %type_View 177 Offset 3040 + OpMemberDecorate %type_View 178 Offset 3044 + OpDecorate %type_View Block + OpDecorate %_arr_mat4v4float_uint_6 ArrayStride 64 + OpMemberDecorate %type_ShadowDepthPass 0 Offset 0 + OpMemberDecorate %type_ShadowDepthPass 1 Offset 4 + OpMemberDecorate %type_ShadowDepthPass 2 Offset 8 + OpMemberDecorate %type_ShadowDepthPass 3 Offset 12 + OpMemberDecorate %type_ShadowDepthPass 4 Offset 16 + OpMemberDecorate %type_ShadowDepthPass 5 Offset 20 + OpMemberDecorate %type_ShadowDepthPass 6 Offset 24 + OpMemberDecorate %type_ShadowDepthPass 7 Offset 28 + OpMemberDecorate %type_ShadowDepthPass 8 Offset 32 + OpMemberDecorate %type_ShadowDepthPass 9 Offset 36 + OpMemberDecorate %type_ShadowDepthPass 10 Offset 40 + OpMemberDecorate %type_ShadowDepthPass 11 Offset 44 + OpMemberDecorate %type_ShadowDepthPass 12 Offset 48 + OpMemberDecorate %type_ShadowDepthPass 13 Offset 52 + OpMemberDecorate %type_ShadowDepthPass 14 Offset 56 + OpMemberDecorate %type_ShadowDepthPass 15 Offset 60 + OpMemberDecorate %type_ShadowDepthPass 16 Offset 64 + OpMemberDecorate %type_ShadowDepthPass 17 Offset 68 + OpMemberDecorate %type_ShadowDepthPass 18 Offset 72 + OpMemberDecorate %type_ShadowDepthPass 19 Offset 76 + OpMemberDecorate %type_ShadowDepthPass 20 Offset 80 + OpMemberDecorate %type_ShadowDepthPass 21 Offset 84 + OpMemberDecorate %type_ShadowDepthPass 22 Offset 88 + OpMemberDecorate %type_ShadowDepthPass 23 Offset 92 + OpMemberDecorate %type_ShadowDepthPass 24 Offset 96 + OpMemberDecorate %type_ShadowDepthPass 25 Offset 100 + OpMemberDecorate %type_ShadowDepthPass 26 Offset 104 + OpMemberDecorate %type_ShadowDepthPass 27 Offset 108 + OpMemberDecorate %type_ShadowDepthPass 28 Offset 112 + OpMemberDecorate %type_ShadowDepthPass 29 Offset 116 + OpMemberDecorate %type_ShadowDepthPass 30 Offset 120 + OpMemberDecorate %type_ShadowDepthPass 31 Offset 124 + OpMemberDecorate %type_ShadowDepthPass 32 Offset 128 + OpMemberDecorate %type_ShadowDepthPass 33 Offset 132 + OpMemberDecorate %type_ShadowDepthPass 34 Offset 136 + OpMemberDecorate %type_ShadowDepthPass 35 Offset 140 + OpMemberDecorate %type_ShadowDepthPass 36 Offset 144 + OpMemberDecorate %type_ShadowDepthPass 37 Offset 148 + OpMemberDecorate %type_ShadowDepthPass 38 Offset 152 + OpMemberDecorate %type_ShadowDepthPass 39 Offset 156 + OpMemberDecorate %type_ShadowDepthPass 40 Offset 160 + OpMemberDecorate %type_ShadowDepthPass 41 Offset 164 + OpMemberDecorate %type_ShadowDepthPass 42 Offset 168 + OpMemberDecorate %type_ShadowDepthPass 43 Offset 172 + OpMemberDecorate %type_ShadowDepthPass 44 Offset 176 + OpMemberDecorate %type_ShadowDepthPass 45 Offset 180 + OpMemberDecorate %type_ShadowDepthPass 46 Offset 184 + OpMemberDecorate %type_ShadowDepthPass 47 Offset 188 + OpMemberDecorate %type_ShadowDepthPass 48 Offset 192 + OpMemberDecorate %type_ShadowDepthPass 49 Offset 196 + OpMemberDecorate %type_ShadowDepthPass 50 Offset 200 + OpMemberDecorate %type_ShadowDepthPass 51 Offset 204 + OpMemberDecorate %type_ShadowDepthPass 52 Offset 208 + OpMemberDecorate %type_ShadowDepthPass 53 Offset 212 + OpMemberDecorate %type_ShadowDepthPass 54 Offset 216 + OpMemberDecorate %type_ShadowDepthPass 55 Offset 220 + OpMemberDecorate %type_ShadowDepthPass 56 Offset 224 + OpMemberDecorate %type_ShadowDepthPass 57 Offset 228 + OpMemberDecorate %type_ShadowDepthPass 58 Offset 232 + OpMemberDecorate %type_ShadowDepthPass 59 Offset 236 + OpMemberDecorate %type_ShadowDepthPass 60 Offset 240 + OpMemberDecorate %type_ShadowDepthPass 61 Offset 244 + OpMemberDecorate %type_ShadowDepthPass 62 Offset 248 + OpMemberDecorate %type_ShadowDepthPass 63 Offset 252 + OpMemberDecorate %type_ShadowDepthPass 64 Offset 256 + OpMemberDecorate %type_ShadowDepthPass 65 Offset 260 + OpMemberDecorate %type_ShadowDepthPass 66 Offset 264 + OpMemberDecorate %type_ShadowDepthPass 67 Offset 268 + OpMemberDecorate %type_ShadowDepthPass 68 Offset 272 + OpMemberDecorate %type_ShadowDepthPass 68 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 68 ColMajor + OpMemberDecorate %type_ShadowDepthPass 69 Offset 336 + OpMemberDecorate %type_ShadowDepthPass 70 Offset 352 + OpMemberDecorate %type_ShadowDepthPass 71 Offset 368 + OpMemberDecorate %type_ShadowDepthPass 72 Offset 384 + OpMemberDecorate %type_ShadowDepthPass 73 Offset 396 + OpMemberDecorate %type_ShadowDepthPass 74 Offset 400 + OpMemberDecorate %type_ShadowDepthPass 75 Offset 412 + OpMemberDecorate %type_ShadowDepthPass 76 Offset 416 + OpMemberDecorate %type_ShadowDepthPass 77 Offset 420 + OpMemberDecorate %type_ShadowDepthPass 78 Offset 424 + OpMemberDecorate %type_ShadowDepthPass 79 Offset 428 + OpMemberDecorate %type_ShadowDepthPass 80 Offset 432 + OpMemberDecorate %type_ShadowDepthPass 81 Offset 436 + OpMemberDecorate %type_ShadowDepthPass 82 Offset 440 + OpMemberDecorate %type_ShadowDepthPass 83 Offset 444 + OpMemberDecorate %type_ShadowDepthPass 84 Offset 448 + OpMemberDecorate %type_ShadowDepthPass 85 Offset 452 + OpMemberDecorate %type_ShadowDepthPass 86 Offset 456 + OpMemberDecorate %type_ShadowDepthPass 87 Offset 460 + OpMemberDecorate %type_ShadowDepthPass 88 Offset 464 + OpMemberDecorate %type_ShadowDepthPass 88 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 88 ColMajor + OpMemberDecorate %type_ShadowDepthPass 89 Offset 528 + OpMemberDecorate %type_ShadowDepthPass 89 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 89 ColMajor + OpMemberDecorate %type_ShadowDepthPass 90 Offset 592 + OpMemberDecorate %type_ShadowDepthPass 91 Offset 608 + OpMemberDecorate %type_ShadowDepthPass 92 Offset 612 + OpMemberDecorate %type_ShadowDepthPass 93 Offset 616 + OpMemberDecorate %type_ShadowDepthPass 94 Offset 620 + OpMemberDecorate %type_ShadowDepthPass 95 Offset 624 + OpMemberDecorate %type_ShadowDepthPass 95 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 95 ColMajor + OpMemberDecorate %type_ShadowDepthPass 96 Offset 1008 + OpMemberDecorate %type_ShadowDepthPass 96 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 96 ColMajor + OpDecorate %type_ShadowDepthPass Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %v2int = OpTypeVector %int 2 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %float_3 = OpConstant %float 3 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %float_6 = OpConstant %float 6 + %57 = OpConstantComposite %v4float %float_6 %float_6 %float_6 %float_6 + %float_1 = OpConstant %float 1 + %int_79 = OpConstant %int 79 +%float_0_200000003 = OpConstant %float 0.200000003 +%float_n0_699999988 = OpConstant %float -0.699999988 + %float_2 = OpConstant %float 2 + %63 = OpConstantComposite %v2float %float_1 %float_2 + %float_n1 = OpConstant %float -1 + %float_10 = OpConstant %float 10 + %float_0_5 = OpConstant %float 0.5 + %67 = OpConstantComposite %v3float %float_0_5 %float_0_5 %float_0_5 + %int_88 = OpConstant %int 88 + %int_89 = OpConstant %int 89 + %int_90 = OpConstant %int 90 + %int_91 = OpConstant %int 91 + %float_0 = OpConstant %float 0 +%float_9_99999997en07 = OpConstant %float 9.99999997e-07 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %uint %uint %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v2int %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %uint_6 = OpConstant %uint 6 +%_arr_mat4v4float_uint_6 = OpTypeArray %mat4v4float %uint_6 + %v3int = OpTypeVector %int 3 +%type_ShadowDepthPass = OpTypeStruct %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %mat4v4float %v4float %v4float %v4float %v3int %int %v3int %float %float %float %float %float %float %float %float %float %float %float %float %int %mat4v4float %mat4v4float %v4float %float %float %float %float %_arr_mat4v4float_uint_6 %_arr_mat4v4float_uint_6 +%_ptr_Uniform_type_ShadowDepthPass = OpTypePointer Uniform %type_ShadowDepthPass + %uint_3 = OpConstant %uint 3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%_arr__arr_v4float_uint_1_uint_3 = OpTypeArray %_arr_v4float_uint_1 %uint_3 +%_ptr_Input__arr__arr_v4float_uint_1_uint_3 = OpTypePointer Input %_arr__arr_v4float_uint_1_uint_3 +%_arr_uint_uint_3 = OpTypeArray %uint %uint_3 +%_ptr_Input__arr_uint_uint_3 = OpTypePointer Input %_arr_uint_uint_3 +%_arr__arr_v4float_uint_3_uint_3 = OpTypeArray %_arr_v4float_uint_3 %uint_3 +%_ptr_Input__arr__arr_v4float_uint_3_uint_3 = OpTypePointer Input %_arr__arr_v4float_uint_3_uint_3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%_ptr_Input__arr_float_uint_3 = OpTypePointer Input %_arr_float_uint_3 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Input__arr_float_uint_4 = OpTypePointer Input %_arr_float_uint_4 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Input__arr_float_uint_2 = OpTypePointer Input %_arr_float_uint_2 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_v3float = OpTypePointer Input %v3float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Output__arr_v4float_uint_1 = OpTypePointer Output %_arr_v4float_uint_1 +%_ptr_Output_uint = OpTypePointer Output %uint +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %106 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float + %bool = OpTypeBool +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Function_mat4v4float = OpTypePointer Function %mat4v4float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%type_sampled_image = OpTypeSampledImage %type_2d_image + %View = OpVariable %_ptr_Uniform_type_View Uniform +%ShadowDepthPass = OpVariable %_ptr_Uniform_type_ShadowDepthPass Uniform +%Material_Texture2D_3 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%Material_Texture2D_3Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_COLOR0 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input__arr__arr_v4float_uint_1_uint_3 Input +%in_var_PRIMITIVE_ID = OpVariable %_ptr_Input__arr_uint_uint_3 Input +%in_var_VS_to_DS_Position = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_PN_POSITION = OpVariable %_ptr_Input__arr__arr_v4float_uint_3_uint_3 Input +%in_var_PN_DisplacementScales = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_PN_TessellationMultiplier = OpVariable %_ptr_Input__arr_float_uint_3 Input +%in_var_PN_WorldDisplacementMultiplier = OpVariable %_ptr_Input__arr_float_uint_3 Input +%gl_TessLevelOuter = OpVariable %_ptr_Input__arr_float_uint_4 Input +%gl_TessLevelInner = OpVariable %_ptr_Input__arr_float_uint_2 Input +%in_var_PN_POSITION9 = OpVariable %_ptr_Input_v4float Input +%gl_TessCoord = OpVariable %_ptr_Input_v3float Input +%out_var_TEXCOORD10_centroid = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD11_centroid = OpVariable %_ptr_Output_v4float Output +%out_var_COLOR0 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD0 = OpVariable %_ptr_Output__arr_v4float_uint_1 Output +%out_var_PRIMITIVE_ID = OpVariable %_ptr_Output_uint Output +%out_var_TEXCOORD6 = OpVariable %_ptr_Output_float Output +%out_var_TEXCOORD8 = OpVariable %_ptr_Output_float Output +%out_var_TEXCOORD7 = OpVariable %_ptr_Output_v3float Output +%gl_Position = OpVariable %_ptr_Output_v4float Output + %112 = OpConstantNull %v4float + %113 = OpUndef %v4float +%_ptr_Input_uint = OpTypePointer Input %uint + %MainDomain = OpFunction %void None %106 + %115 = OpLabel + %116 = OpVariable %_ptr_Function_mat4v4float Function + %117 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD10_centroid + %118 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD11_centroid + %119 = OpLoad %_arr_v4float_uint_3 %in_var_COLOR0 + %120 = OpLoad %_arr__arr_v4float_uint_1_uint_3 %in_var_TEXCOORD0 + %121 = OpAccessChain %_ptr_Input_uint %in_var_PRIMITIVE_ID %uint_0 + %122 = OpLoad %uint %121 + %123 = OpCompositeExtract %v4float %117 0 + %124 = OpCompositeExtract %v4float %118 0 + %125 = OpCompositeExtract %v4float %119 0 + %126 = OpCompositeExtract %_arr_v4float_uint_1 %120 0 + %127 = OpCompositeExtract %v4float %117 1 + %128 = OpCompositeExtract %v4float %118 1 + %129 = OpCompositeExtract %v4float %119 1 + %130 = OpCompositeExtract %_arr_v4float_uint_1 %120 1 + %131 = OpCompositeExtract %v4float %117 2 + %132 = OpCompositeExtract %v4float %118 2 + %133 = OpCompositeExtract %v4float %119 2 + %134 = OpCompositeExtract %_arr_v4float_uint_1 %120 2 + %135 = OpLoad %_arr__arr_v4float_uint_3_uint_3 %in_var_PN_POSITION + %136 = OpLoad %_arr_float_uint_3 %in_var_PN_WorldDisplacementMultiplier + %137 = OpCompositeExtract %_arr_v4float_uint_3 %135 0 + %138 = OpCompositeExtract %float %136 0 + %139 = OpCompositeExtract %_arr_v4float_uint_3 %135 1 + %140 = OpCompositeExtract %float %136 1 + %141 = OpCompositeExtract %_arr_v4float_uint_3 %135 2 + %142 = OpCompositeExtract %float %136 2 + %143 = OpCompositeExtract %v4float %137 0 + %144 = OpCompositeExtract %v4float %137 1 + %145 = OpCompositeExtract %v4float %137 2 + %146 = OpCompositeExtract %v4float %139 0 + %147 = OpCompositeExtract %v4float %139 1 + %148 = OpCompositeExtract %v4float %139 2 + %149 = OpCompositeExtract %v4float %141 0 + %150 = OpCompositeExtract %v4float %141 1 + %151 = OpCompositeExtract %v4float %141 2 + %152 = OpLoad %v4float %in_var_PN_POSITION9 + %153 = OpLoad %v3float %gl_TessCoord + %154 = OpCompositeExtract %float %153 0 + %155 = OpCompositeExtract %float %153 1 + %156 = OpCompositeExtract %float %153 2 + %157 = OpFMul %float %154 %154 + %158 = OpFMul %float %155 %155 + %159 = OpFMul %float %156 %156 + %160 = OpFMul %float %157 %float_3 + %161 = OpFMul %float %158 %float_3 + %162 = OpFMul %float %159 %float_3 + %163 = OpCompositeConstruct %v4float %157 %157 %157 %157 + %164 = OpFMul %v4float %143 %163 + %165 = OpCompositeConstruct %v4float %154 %154 %154 %154 + %166 = OpFMul %v4float %164 %165 + %167 = OpCompositeConstruct %v4float %158 %158 %158 %158 + %168 = OpFMul %v4float %146 %167 + %169 = OpCompositeConstruct %v4float %155 %155 %155 %155 + %170 = OpFMul %v4float %168 %169 + %171 = OpFAdd %v4float %166 %170 + %172 = OpCompositeConstruct %v4float %159 %159 %159 %159 + %173 = OpFMul %v4float %149 %172 + %174 = OpCompositeConstruct %v4float %156 %156 %156 %156 + %175 = OpFMul %v4float %173 %174 + %176 = OpFAdd %v4float %171 %175 + %177 = OpCompositeConstruct %v4float %160 %160 %160 %160 + %178 = OpFMul %v4float %144 %177 + %179 = OpFMul %v4float %178 %169 + %180 = OpFAdd %v4float %176 %179 + %181 = OpCompositeConstruct %v4float %161 %161 %161 %161 + %182 = OpFMul %v4float %145 %181 + %183 = OpFMul %v4float %182 %165 + %184 = OpFAdd %v4float %180 %183 + %185 = OpFMul %v4float %147 %181 + %186 = OpFMul %v4float %185 %174 + %187 = OpFAdd %v4float %184 %186 + %188 = OpCompositeConstruct %v4float %162 %162 %162 %162 + %189 = OpFMul %v4float %148 %188 + %190 = OpFMul %v4float %189 %169 + %191 = OpFAdd %v4float %187 %190 + %192 = OpFMul %v4float %150 %188 + %193 = OpFMul %v4float %192 %165 + %194 = OpFAdd %v4float %191 %193 + %195 = OpFMul %v4float %151 %177 + %196 = OpFMul %v4float %195 %174 + %197 = OpFAdd %v4float %194 %196 + %198 = OpFMul %v4float %152 %57 + %199 = OpFMul %v4float %198 %174 + %200 = OpFMul %v4float %199 %165 + %201 = OpFMul %v4float %200 %169 + %202 = OpFAdd %v4float %197 %201 + %203 = OpCompositeExtract %v4float %126 0 + %204 = OpCompositeExtract %v4float %130 0 + %205 = OpVectorShuffle %v3float %123 %123 0 1 2 + %206 = OpCompositeConstruct %v3float %154 %154 %154 + %207 = OpFMul %v3float %205 %206 + %208 = OpVectorShuffle %v3float %127 %127 0 1 2 + %209 = OpCompositeConstruct %v3float %155 %155 %155 + %210 = OpFMul %v3float %208 %209 + %211 = OpFAdd %v3float %207 %210 + %212 = OpFMul %v4float %124 %165 + %213 = OpFMul %v4float %128 %169 + %214 = OpFAdd %v4float %212 %213 + %215 = OpFMul %v4float %125 %165 + %216 = OpFMul %v4float %129 %169 + %217 = OpFAdd %v4float %215 %216 + %218 = OpFMul %v4float %203 %165 + %219 = OpFMul %v4float %204 %169 + %220 = OpFAdd %v4float %218 %219 + %221 = OpCompositeExtract %v4float %134 0 + %222 = OpVectorShuffle %v3float %211 %112 0 1 2 + %223 = OpVectorShuffle %v3float %131 %131 0 1 2 + %224 = OpCompositeConstruct %v3float %156 %156 %156 + %225 = OpFMul %v3float %223 %224 + %226 = OpFAdd %v3float %222 %225 + %227 = OpVectorShuffle %v4float %113 %226 4 5 6 3 + %228 = OpFMul %v4float %132 %174 + %229 = OpFAdd %v4float %214 %228 + %230 = OpFMul %v4float %133 %174 + %231 = OpFAdd %v4float %217 %230 + %232 = OpFMul %v4float %221 %174 + %233 = OpFAdd %v4float %220 %232 + %234 = OpCompositeConstruct %_arr_v4float_uint_1 %233 + %235 = OpVectorShuffle %v2float %233 %233 2 3 + %236 = OpVectorShuffle %v3float %229 %229 0 1 2 + %237 = OpAccessChain %_ptr_Uniform_float %View %int_79 + %238 = OpLoad %float %237 + %239 = OpFMul %float %238 %float_0_200000003 + %240 = OpFMul %float %238 %float_n0_699999988 + %241 = OpFMul %v2float %235 %63 + %242 = OpCompositeConstruct %v2float %239 %240 + %243 = OpFAdd %v2float %242 %241 + %244 = OpLoad %type_2d_image %Material_Texture2D_3 + %245 = OpLoad %type_sampler %Material_Texture2D_3Sampler + %246 = OpSampledImage %type_sampled_image %244 %245 + %247 = OpImageSampleExplicitLod %v4float %246 %243 Lod %float_n1 + %248 = OpCompositeExtract %float %247 0 + %249 = OpFMul %float %248 %float_10 + %250 = OpCompositeExtract %float %231 0 + %251 = OpFSub %float %float_1 %250 + %252 = OpFMul %float %249 %251 + %253 = OpCompositeConstruct %v3float %252 %252 %252 + %254 = OpFMul %v3float %253 %236 + %255 = OpFMul %v3float %254 %67 + %256 = OpFMul %float %138 %154 + %257 = OpFMul %float %140 %155 + %258 = OpFAdd %float %256 %257 + %259 = OpFMul %float %142 %156 + %260 = OpFAdd %float %258 %259 + %261 = OpCompositeConstruct %v3float %260 %260 %260 + %262 = OpFMul %v3float %255 %261 + %263 = OpVectorShuffle %v3float %202 %202 0 1 2 + %264 = OpFAdd %v3float %263 %262 + %265 = OpVectorShuffle %v4float %202 %264 4 5 6 3 + %266 = OpAccessChain %_ptr_Uniform_mat4v4float %ShadowDepthPass %int_88 + %267 = OpLoad %mat4v4float %266 + %268 = OpAccessChain %_ptr_Uniform_mat4v4float %ShadowDepthPass %int_89 + %269 = OpLoad %mat4v4float %268 + OpStore %116 %269 + %270 = OpMatrixTimesVector %v4float %267 %265 + %271 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_91 + %272 = OpLoad %float %271 + %273 = OpFOrdGreaterThan %bool %272 %float_0 + %274 = OpCompositeExtract %float %270 2 + %275 = OpFOrdLessThan %bool %274 %float_0 + %276 = OpLogicalAnd %bool %273 %275 + OpSelectionMerge %277 None + OpBranchConditional %276 %278 %277 + %278 = OpLabel + %279 = OpCompositeInsert %v4float %float_9_99999997en07 %270 2 + %280 = OpCompositeInsert %v4float %float_1 %279 3 + OpBranch %277 + %277 = OpLabel + %281 = OpPhi %v4float %270 %115 %280 %278 + %282 = OpAccessChain %_ptr_Function_float %116 %uint_0 %int_2 + %283 = OpLoad %float %282 + %284 = OpAccessChain %_ptr_Function_float %116 %uint_1 %int_2 + %285 = OpLoad %float %284 + %286 = OpAccessChain %_ptr_Function_float %116 %uint_2 %int_2 + %287 = OpLoad %float %286 + %288 = OpCompositeConstruct %v3float %283 %285 %287 + %289 = OpDot %float %288 %236 + %290 = OpExtInst %float %1 FAbs %289 + %291 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_90 %int_2 + %292 = OpLoad %float %291 + %293 = OpExtInst %float %1 FAbs %290 + %294 = OpFOrdGreaterThan %bool %293 %float_0 + %295 = OpFMul %float %290 %290 + %296 = OpFSub %float %float_1 %295 + %297 = OpExtInst %float %1 FClamp %296 %float_0 %float_1 + %298 = OpExtInst %float %1 Sqrt %297 + %299 = OpFDiv %float %298 %290 + %300 = OpSelect %float %294 %299 %292 + %301 = OpExtInst %float %1 FClamp %300 %float_0 %292 + %302 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_90 %int_1 + %303 = OpLoad %float %302 + %304 = OpFMul %float %303 %301 + %305 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_90 %int_0 + %306 = OpLoad %float %305 + %307 = OpFAdd %float %304 %306 + %308 = OpCompositeExtract %float %281 2 + %309 = OpVectorShuffle %v3float %264 %112 0 1 2 + OpStore %out_var_TEXCOORD10_centroid %227 + OpStore %out_var_TEXCOORD11_centroid %229 + OpStore %out_var_COLOR0 %231 + OpStore %out_var_TEXCOORD0 %234 + OpStore %out_var_PRIMITIVE_ID %122 + OpStore %out_var_TEXCOORD6 %308 + OpStore %out_var_TEXCOORD8 %307 + OpStore %out_var_TEXCOORD7 %309 + OpStore %gl_Position %281 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese b/third_party/spirv-cross/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese new file mode 100644 index 0000000..cb55bb4 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/tese/ds-patch-input-fixes.asm.tese @@ -0,0 +1,1175 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 581 +; Schema: 0 + OpCapability Tessellation + OpCapability ClipDistance + OpCapability SampledBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %MainDomain "main" %gl_ClipDistance %in_var_TEXCOORD6 %in_var_TEXCOORD8 %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_VS_To_DS_Position %in_var_VS_To_DS_VertexID %in_var_PN_POSITION %in_var_PN_DisplacementScales %in_var_PN_TessellationMultiplier %in_var_PN_WorldDisplacementMultiplier %in_var_PN_DominantVertex %in_var_PN_DominantVertex1 %in_var_PN_DominantVertex2 %in_var_PN_DominantEdge %in_var_PN_DominantEdge1 %in_var_PN_DominantEdge2 %in_var_PN_DominantEdge3 %in_var_PN_DominantEdge4 %in_var_PN_DominantEdge5 %gl_TessLevelOuter %gl_TessLevelInner %in_var_PN_POSITION9 %gl_TessCoord %gl_Position %out_var_TEXCOORD6 %out_var_TEXCOORD7 %out_var_TEXCOORD10_centroid %out_var_TEXCOORD11_centroid + OpExecutionMode %MainDomain Triangles + OpExecutionMode %MainDomain SpacingFractionalOdd + OpExecutionMode %MainDomain VertexOrderCw + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_ClipToWorld" + OpMemberName %type_View 3 "View_TranslatedWorldToView" + OpMemberName %type_View 4 "View_ViewToTranslatedWorld" + OpMemberName %type_View 5 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 6 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 7 "View_ViewToClip" + OpMemberName %type_View 8 "View_ViewToClipNoAA" + OpMemberName %type_View 9 "View_ClipToView" + OpMemberName %type_View 10 "View_ClipToTranslatedWorld" + OpMemberName %type_View 11 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 12 "View_ScreenToWorld" + OpMemberName %type_View 13 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 14 "View_ViewForward" + OpMemberName %type_View 15 "PrePadding_View_908" + OpMemberName %type_View 16 "View_ViewUp" + OpMemberName %type_View 17 "PrePadding_View_924" + OpMemberName %type_View 18 "View_ViewRight" + OpMemberName %type_View 19 "PrePadding_View_940" + OpMemberName %type_View 20 "View_HMDViewNoRollUp" + OpMemberName %type_View 21 "PrePadding_View_956" + OpMemberName %type_View 22 "View_HMDViewNoRollRight" + OpMemberName %type_View 23 "PrePadding_View_972" + OpMemberName %type_View 24 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 25 "View_ScreenPositionScaleBias" + OpMemberName %type_View 26 "View_WorldCameraOrigin" + OpMemberName %type_View 27 "PrePadding_View_1020" + OpMemberName %type_View 28 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 29 "PrePadding_View_1036" + OpMemberName %type_View 30 "View_WorldViewOrigin" + OpMemberName %type_View 31 "PrePadding_View_1052" + OpMemberName %type_View 32 "View_PreViewTranslation" + OpMemberName %type_View 33 "PrePadding_View_1068" + OpMemberName %type_View 34 "View_PrevProjection" + OpMemberName %type_View 35 "View_PrevViewProj" + OpMemberName %type_View 36 "View_PrevViewRotationProj" + OpMemberName %type_View 37 "View_PrevViewToClip" + OpMemberName %type_View 38 "View_PrevClipToView" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 40 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 41 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 42 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 43 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 44 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 45 "PrePadding_View_1724" + OpMemberName %type_View 46 "View_PrevWorldViewOrigin" + OpMemberName %type_View 47 "PrePadding_View_1740" + OpMemberName %type_View 48 "View_PrevPreViewTranslation" + OpMemberName %type_View 49 "PrePadding_View_1756" + OpMemberName %type_View 50 "View_PrevInvViewProj" + OpMemberName %type_View 51 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 52 "View_ClipToPrevClip" + OpMemberName %type_View 53 "View_TemporalAAJitter" + OpMemberName %type_View 54 "View_GlobalClippingPlane" + OpMemberName %type_View 55 "View_FieldOfViewWideAngles" + OpMemberName %type_View 56 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 57 "View_ViewRectMin" + OpMemberName %type_View 58 "View_ViewSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferSizeAndInvSize" + OpMemberName %type_View 60 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 61 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 62 "View_PreExposure" + OpMemberName %type_View 63 "View_OneOverPreExposure" + OpMemberName %type_View 64 "PrePadding_View_2076" + OpMemberName %type_View 65 "View_DiffuseOverrideParameter" + OpMemberName %type_View 66 "View_SpecularOverrideParameter" + OpMemberName %type_View 67 "View_NormalOverrideParameter" + OpMemberName %type_View 68 "View_RoughnessOverrideParameter" + OpMemberName %type_View 69 "View_PrevFrameGameTime" + OpMemberName %type_View 70 "View_PrevFrameRealTime" + OpMemberName %type_View 71 "View_OutOfBoundsMask" + OpMemberName %type_View 72 "PrePadding_View_2148" + OpMemberName %type_View 73 "PrePadding_View_2152" + OpMemberName %type_View 74 "PrePadding_View_2156" + OpMemberName %type_View 75 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 76 "View_CullingSign" + OpMemberName %type_View 77 "View_NearPlane" + OpMemberName %type_View 78 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 79 "View_GameTime" + OpMemberName %type_View 80 "View_RealTime" + OpMemberName %type_View 81 "View_DeltaTime" + OpMemberName %type_View 82 "View_MaterialTextureMipBias" + OpMemberName %type_View 83 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 84 "View_Random" + OpMemberName %type_View 85 "View_FrameNumber" + OpMemberName %type_View 86 "View_StateFrameIndexMod8" + OpMemberName %type_View 87 "View_StateFrameIndex" + OpMemberName %type_View 88 "View_CameraCut" + OpMemberName %type_View 89 "View_UnlitViewmodeMask" + OpMemberName %type_View 90 "PrePadding_View_2228" + OpMemberName %type_View 91 "PrePadding_View_2232" + OpMemberName %type_View 92 "PrePadding_View_2236" + OpMemberName %type_View 93 "View_DirectionalLightColor" + OpMemberName %type_View 94 "View_DirectionalLightDirection" + OpMemberName %type_View 95 "PrePadding_View_2268" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 97 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 98 "View_TemporalAAParams" + OpMemberName %type_View 99 "View_CircleDOFParams" + OpMemberName %type_View 100 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 101 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 102 "View_DepthOfFieldScale" + OpMemberName %type_View 103 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 104 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 105 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 106 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 107 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 108 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 109 "View_GeneralPurposeTweak" + OpMemberName %type_View 110 "View_DemosaicVposOffset" + OpMemberName %type_View 111 "PrePadding_View_2412" + OpMemberName %type_View 112 "View_IndirectLightingColorScale" + OpMemberName %type_View 113 "View_HDR32bppEncodingMode" + OpMemberName %type_View 114 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 115 "View_AtmosphericFogSunPower" + OpMemberName %type_View 116 "View_AtmosphericFogPower" + OpMemberName %type_View 117 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 118 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 119 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 120 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 121 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 122 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 123 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 124 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 125 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 126 "View_AtmosphericFogSunDiscHalfApexAngleRadian" + OpMemberName %type_View 127 "PrePadding_View_2492" + OpMemberName %type_View 128 "View_AtmosphericFogSunDiscLuminance" + OpMemberName %type_View 129 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 130 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 131 "PrePadding_View_2520" + OpMemberName %type_View 132 "PrePadding_View_2524" + OpMemberName %type_View 133 "View_AtmosphericFogSunColor" + OpMemberName %type_View 134 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 135 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 136 "View_AmbientCubemapTint" + OpMemberName %type_View 137 "View_AmbientCubemapIntensity" + OpMemberName %type_View 138 "View_SkyLightParameters" + OpMemberName %type_View 139 "PrePadding_View_2584" + OpMemberName %type_View 140 "PrePadding_View_2588" + OpMemberName %type_View 141 "View_SkyLightColor" + OpMemberName %type_View 142 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 143 "View_MobilePreviewMode" + OpMemberName %type_View 144 "View_HMDEyePaddingOffset" + OpMemberName %type_View 145 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 146 "View_ShowDecalsMask" + OpMemberName %type_View 147 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 148 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 149 "PrePadding_View_2744" + OpMemberName %type_View 150 "PrePadding_View_2748" + OpMemberName %type_View 151 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 152 "View_StereoPassIndex" + OpMemberName %type_View 153 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 154 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 155 "View_GlobalVolumeDimension" + OpMemberName %type_View 156 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 157 "View_MaxGlobalDistance" + OpMemberName %type_View 158 "PrePadding_View_2908" + OpMemberName %type_View 159 "View_CursorPosition" + OpMemberName %type_View 160 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 161 "PrePadding_View_2924" + OpMemberName %type_View 162 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 163 "PrePadding_View_2940" + OpMemberName %type_View 164 "View_VolumetricFogGridZParams" + OpMemberName %type_View 165 "PrePadding_View_2956" + OpMemberName %type_View 166 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 167 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 168 "PrePadding_View_2972" + OpMemberName %type_View 169 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 170 "PrePadding_View_2988" + OpMemberName %type_View 171 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 172 "PrePadding_View_3004" + OpMemberName %type_View 173 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 174 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 175 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 176 "View_StereoIPD" + OpMemberName %type_View 177 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 178 "View_EyeToPixelSpreadAngle" + OpMemberName %type_View 179 "PrePadding_View_3048" + OpMemberName %type_View 180 "PrePadding_View_3052" + OpMemberName %type_View 181 "View_WorldToVirtualTexture" + OpMemberName %type_View 182 "View_VirtualTextureParams" + OpMemberName %type_View 183 "View_XRPassthroughCameraUVs" + OpName %View "View" + OpName %type_sampler "type.sampler" + OpName %type_3d_image "type.3d.image" + OpName %View_GlobalDistanceFieldTexture0 "View_GlobalDistanceFieldTexture0" + OpName %View_GlobalDistanceFieldSampler0 "View_GlobalDistanceFieldSampler0" + OpName %View_GlobalDistanceFieldTexture1 "View_GlobalDistanceFieldTexture1" + OpName %View_GlobalDistanceFieldTexture2 "View_GlobalDistanceFieldTexture2" + OpName %View_GlobalDistanceFieldTexture3 "View_GlobalDistanceFieldTexture3" + OpName %type_Material "type.Material" + OpMemberName %type_Material 0 "Material_VectorExpressions" + OpMemberName %type_Material 1 "Material_ScalarExpressions" + OpName %Material "Material" + OpName %in_var_TEXCOORD6 "in.var.TEXCOORD6" + OpName %in_var_TEXCOORD8 "in.var.TEXCOORD8" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_VS_To_DS_Position "in.var.VS_To_DS_Position" + OpName %in_var_VS_To_DS_VertexID "in.var.VS_To_DS_VertexID" + OpName %in_var_PN_POSITION "in.var.PN_POSITION" + OpName %in_var_PN_DisplacementScales "in.var.PN_DisplacementScales" + OpName %in_var_PN_TessellationMultiplier "in.var.PN_TessellationMultiplier" + OpName %in_var_PN_WorldDisplacementMultiplier "in.var.PN_WorldDisplacementMultiplier" + OpName %in_var_PN_DominantVertex "in.var.PN_DominantVertex" + OpName %in_var_PN_DominantVertex1 "in.var.PN_DominantVertex1" + OpName %in_var_PN_DominantVertex2 "in.var.PN_DominantVertex2" + OpName %in_var_PN_DominantEdge "in.var.PN_DominantEdge" + OpName %in_var_PN_DominantEdge1 "in.var.PN_DominantEdge1" + OpName %in_var_PN_DominantEdge2 "in.var.PN_DominantEdge2" + OpName %in_var_PN_DominantEdge3 "in.var.PN_DominantEdge3" + OpName %in_var_PN_DominantEdge4 "in.var.PN_DominantEdge4" + OpName %in_var_PN_DominantEdge5 "in.var.PN_DominantEdge5" + OpName %in_var_PN_POSITION9 "in.var.PN_POSITION9" + OpName %out_var_TEXCOORD6 "out.var.TEXCOORD6" + OpName %out_var_TEXCOORD7 "out.var.TEXCOORD7" + OpName %out_var_TEXCOORD10_centroid "out.var.TEXCOORD10_centroid" + OpName %out_var_TEXCOORD11_centroid "out.var.TEXCOORD11_centroid" + OpName %MainDomain "MainDomain" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_ClipDistance BuiltIn ClipDistance + OpDecorateString %gl_ClipDistance UserSemantic "SV_ClipDistance" + OpDecorateString %in_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorateString %in_var_TEXCOORD8 UserSemantic "TEXCOORD8" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorateString %in_var_VS_To_DS_VertexID UserSemantic "VS_To_DS_VertexID" + OpDecorateString %in_var_PN_POSITION UserSemantic "PN_POSITION" + OpDecorateString %in_var_PN_DisplacementScales UserSemantic "PN_DisplacementScales" + OpDecorateString %in_var_PN_TessellationMultiplier UserSemantic "PN_TessellationMultiplier" + OpDecorateString %in_var_PN_WorldDisplacementMultiplier UserSemantic "PN_WorldDisplacementMultiplier" + OpDecorateString %in_var_PN_DominantVertex UserSemantic "PN_DominantVertex" + OpDecorateString %in_var_PN_DominantVertex1 UserSemantic "PN_DominantVertex" + OpDecorateString %in_var_PN_DominantVertex2 UserSemantic "PN_DominantVertex" + OpDecorateString %in_var_PN_DominantEdge UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge1 UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge2 UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge3 UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge4 UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge5 UserSemantic "PN_DominantEdge" + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorateString %gl_TessLevelOuter UserSemantic "SV_TessFactor" + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorateString %gl_TessLevelInner UserSemantic "SV_InsideTessFactor" + OpDecorate %gl_TessLevelInner Patch + OpDecorateString %in_var_PN_POSITION9 UserSemantic "PN_POSITION9" + OpDecorate %in_var_PN_POSITION9 Patch + OpDecorate %gl_TessCoord BuiltIn TessCoord + OpDecorateString %gl_TessCoord UserSemantic "SV_DomainLocation" + OpDecorate %gl_TessCoord Patch + OpDecorate %gl_Position BuiltIn Position + OpDecorateString %gl_Position UserSemantic "SV_POSITION" + OpDecorateString %out_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorateString %out_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorateString %out_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %out_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorate %in_var_PN_DisplacementScales Location 0 + OpDecorate %in_var_PN_DominantEdge Location 1 + OpDecorate %in_var_PN_DominantEdge1 Location 2 + OpDecorate %in_var_PN_DominantEdge2 Location 3 + OpDecorate %in_var_PN_DominantEdge3 Location 4 + OpDecorate %in_var_PN_DominantEdge4 Location 5 + OpDecorate %in_var_PN_DominantEdge5 Location 6 + OpDecorate %in_var_PN_DominantVertex Location 7 + OpDecorate %in_var_PN_DominantVertex1 Location 8 + OpDecorate %in_var_PN_DominantVertex2 Location 9 + OpDecorate %in_var_PN_POSITION Location 10 + OpDecorate %in_var_PN_POSITION9 Location 13 + OpDecorate %in_var_PN_TessellationMultiplier Location 14 + OpDecorate %in_var_PN_WorldDisplacementMultiplier Location 15 + OpDecorate %in_var_TEXCOORD10_centroid Location 16 + OpDecorate %in_var_TEXCOORD11_centroid Location 17 + OpDecorate %in_var_TEXCOORD6 Location 18 + OpDecorate %in_var_TEXCOORD8 Location 19 + OpDecorate %in_var_VS_To_DS_Position Location 20 + OpDecorate %in_var_VS_To_DS_VertexID Location 21 + OpDecorate %out_var_TEXCOORD6 Location 0 + OpDecorate %out_var_TEXCOORD7 Location 1 + OpDecorate %out_var_TEXCOORD10_centroid Location 2 + OpDecorate %out_var_TEXCOORD11_centroid Location 3 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %View_GlobalDistanceFieldTexture0 DescriptorSet 0 + OpDecorate %View_GlobalDistanceFieldTexture0 Binding 0 + OpDecorate %View_GlobalDistanceFieldSampler0 DescriptorSet 0 + OpDecorate %View_GlobalDistanceFieldSampler0 Binding 0 + OpDecorate %View_GlobalDistanceFieldTexture1 DescriptorSet 0 + OpDecorate %View_GlobalDistanceFieldTexture1 Binding 1 + OpDecorate %View_GlobalDistanceFieldTexture2 DescriptorSet 0 + OpDecorate %View_GlobalDistanceFieldTexture2 Binding 2 + OpDecorate %View_GlobalDistanceFieldTexture3 DescriptorSet 0 + OpDecorate %View_GlobalDistanceFieldTexture3 Binding 3 + OpDecorate %Material DescriptorSet 0 + OpDecorate %Material Binding 1 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 13 MatrixStride 16 + OpMemberDecorate %type_View 13 ColMajor + OpMemberDecorate %type_View 14 Offset 896 + OpMemberDecorate %type_View 15 Offset 908 + OpMemberDecorate %type_View 16 Offset 912 + OpMemberDecorate %type_View 17 Offset 924 + OpMemberDecorate %type_View 18 Offset 928 + OpMemberDecorate %type_View 19 Offset 940 + OpMemberDecorate %type_View 20 Offset 944 + OpMemberDecorate %type_View 21 Offset 956 + OpMemberDecorate %type_View 22 Offset 960 + OpMemberDecorate %type_View 23 Offset 972 + OpMemberDecorate %type_View 24 Offset 976 + OpMemberDecorate %type_View 25 Offset 992 + OpMemberDecorate %type_View 26 Offset 1008 + OpMemberDecorate %type_View 27 Offset 1020 + OpMemberDecorate %type_View 28 Offset 1024 + OpMemberDecorate %type_View 29 Offset 1036 + OpMemberDecorate %type_View 30 Offset 1040 + OpMemberDecorate %type_View 31 Offset 1052 + OpMemberDecorate %type_View 32 Offset 1056 + OpMemberDecorate %type_View 33 Offset 1068 + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 43 MatrixStride 16 + OpMemberDecorate %type_View 43 ColMajor + OpMemberDecorate %type_View 44 Offset 1712 + OpMemberDecorate %type_View 45 Offset 1724 + OpMemberDecorate %type_View 46 Offset 1728 + OpMemberDecorate %type_View 47 Offset 1740 + OpMemberDecorate %type_View 48 Offset 1744 + OpMemberDecorate %type_View 49 Offset 1756 + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 52 MatrixStride 16 + OpMemberDecorate %type_View 52 ColMajor + OpMemberDecorate %type_View 53 Offset 1952 + OpMemberDecorate %type_View 54 Offset 1968 + OpMemberDecorate %type_View 55 Offset 1984 + OpMemberDecorate %type_View 56 Offset 1992 + OpMemberDecorate %type_View 57 Offset 2000 + OpMemberDecorate %type_View 58 Offset 2016 + OpMemberDecorate %type_View 59 Offset 2032 + OpMemberDecorate %type_View 60 Offset 2048 + OpMemberDecorate %type_View 61 Offset 2064 + OpMemberDecorate %type_View 62 Offset 2068 + OpMemberDecorate %type_View 63 Offset 2072 + OpMemberDecorate %type_View 64 Offset 2076 + OpMemberDecorate %type_View 65 Offset 2080 + OpMemberDecorate %type_View 66 Offset 2096 + OpMemberDecorate %type_View 67 Offset 2112 + OpMemberDecorate %type_View 68 Offset 2128 + OpMemberDecorate %type_View 69 Offset 2136 + OpMemberDecorate %type_View 70 Offset 2140 + OpMemberDecorate %type_View 71 Offset 2144 + OpMemberDecorate %type_View 72 Offset 2148 + OpMemberDecorate %type_View 73 Offset 2152 + OpMemberDecorate %type_View 74 Offset 2156 + OpMemberDecorate %type_View 75 Offset 2160 + OpMemberDecorate %type_View 76 Offset 2172 + OpMemberDecorate %type_View 77 Offset 2176 + OpMemberDecorate %type_View 78 Offset 2180 + OpMemberDecorate %type_View 79 Offset 2184 + OpMemberDecorate %type_View 80 Offset 2188 + OpMemberDecorate %type_View 81 Offset 2192 + OpMemberDecorate %type_View 82 Offset 2196 + OpMemberDecorate %type_View 83 Offset 2200 + OpMemberDecorate %type_View 84 Offset 2204 + OpMemberDecorate %type_View 85 Offset 2208 + OpMemberDecorate %type_View 86 Offset 2212 + OpMemberDecorate %type_View 87 Offset 2216 + OpMemberDecorate %type_View 88 Offset 2220 + OpMemberDecorate %type_View 89 Offset 2224 + OpMemberDecorate %type_View 90 Offset 2228 + OpMemberDecorate %type_View 91 Offset 2232 + OpMemberDecorate %type_View 92 Offset 2236 + OpMemberDecorate %type_View 93 Offset 2240 + OpMemberDecorate %type_View 94 Offset 2256 + OpMemberDecorate %type_View 95 Offset 2268 + OpMemberDecorate %type_View 96 Offset 2272 + OpMemberDecorate %type_View 97 Offset 2304 + OpMemberDecorate %type_View 98 Offset 2336 + OpMemberDecorate %type_View 99 Offset 2352 + OpMemberDecorate %type_View 100 Offset 2368 + OpMemberDecorate %type_View 101 Offset 2372 + OpMemberDecorate %type_View 102 Offset 2376 + OpMemberDecorate %type_View 103 Offset 2380 + OpMemberDecorate %type_View 104 Offset 2384 + OpMemberDecorate %type_View 105 Offset 2388 + OpMemberDecorate %type_View 106 Offset 2392 + OpMemberDecorate %type_View 107 Offset 2396 + OpMemberDecorate %type_View 108 Offset 2400 + OpMemberDecorate %type_View 109 Offset 2404 + OpMemberDecorate %type_View 110 Offset 2408 + OpMemberDecorate %type_View 111 Offset 2412 + OpMemberDecorate %type_View 112 Offset 2416 + OpMemberDecorate %type_View 113 Offset 2428 + OpMemberDecorate %type_View 114 Offset 2432 + OpMemberDecorate %type_View 115 Offset 2444 + OpMemberDecorate %type_View 116 Offset 2448 + OpMemberDecorate %type_View 117 Offset 2452 + OpMemberDecorate %type_View 118 Offset 2456 + OpMemberDecorate %type_View 119 Offset 2460 + OpMemberDecorate %type_View 120 Offset 2464 + OpMemberDecorate %type_View 121 Offset 2468 + OpMemberDecorate %type_View 122 Offset 2472 + OpMemberDecorate %type_View 123 Offset 2476 + OpMemberDecorate %type_View 124 Offset 2480 + OpMemberDecorate %type_View 125 Offset 2484 + OpMemberDecorate %type_View 126 Offset 2488 + OpMemberDecorate %type_View 127 Offset 2492 + OpMemberDecorate %type_View 128 Offset 2496 + OpMemberDecorate %type_View 129 Offset 2512 + OpMemberDecorate %type_View 130 Offset 2516 + OpMemberDecorate %type_View 131 Offset 2520 + OpMemberDecorate %type_View 132 Offset 2524 + OpMemberDecorate %type_View 133 Offset 2528 + OpMemberDecorate %type_View 134 Offset 2544 + OpMemberDecorate %type_View 135 Offset 2556 + OpMemberDecorate %type_View 136 Offset 2560 + OpMemberDecorate %type_View 137 Offset 2576 + OpMemberDecorate %type_View 138 Offset 2580 + OpMemberDecorate %type_View 139 Offset 2584 + OpMemberDecorate %type_View 140 Offset 2588 + OpMemberDecorate %type_View 141 Offset 2592 + OpMemberDecorate %type_View 142 Offset 2608 + OpMemberDecorate %type_View 143 Offset 2720 + OpMemberDecorate %type_View 144 Offset 2724 + OpMemberDecorate %type_View 145 Offset 2728 + OpMemberDecorate %type_View 146 Offset 2732 + OpMemberDecorate %type_View 147 Offset 2736 + OpMemberDecorate %type_View 148 Offset 2740 + OpMemberDecorate %type_View 149 Offset 2744 + OpMemberDecorate %type_View 150 Offset 2748 + OpMemberDecorate %type_View 151 Offset 2752 + OpMemberDecorate %type_View 152 Offset 2764 + OpMemberDecorate %type_View 153 Offset 2768 + OpMemberDecorate %type_View 154 Offset 2832 + OpMemberDecorate %type_View 155 Offset 2896 + OpMemberDecorate %type_View 156 Offset 2900 + OpMemberDecorate %type_View 157 Offset 2904 + OpMemberDecorate %type_View 158 Offset 2908 + OpMemberDecorate %type_View 159 Offset 2912 + OpMemberDecorate %type_View 160 Offset 2920 + OpMemberDecorate %type_View 161 Offset 2924 + OpMemberDecorate %type_View 162 Offset 2928 + OpMemberDecorate %type_View 163 Offset 2940 + OpMemberDecorate %type_View 164 Offset 2944 + OpMemberDecorate %type_View 165 Offset 2956 + OpMemberDecorate %type_View 166 Offset 2960 + OpMemberDecorate %type_View 167 Offset 2968 + OpMemberDecorate %type_View 168 Offset 2972 + OpMemberDecorate %type_View 169 Offset 2976 + OpMemberDecorate %type_View 170 Offset 2988 + OpMemberDecorate %type_View 171 Offset 2992 + OpMemberDecorate %type_View 172 Offset 3004 + OpMemberDecorate %type_View 173 Offset 3008 + OpMemberDecorate %type_View 174 Offset 3020 + OpMemberDecorate %type_View 175 Offset 3024 + OpMemberDecorate %type_View 176 Offset 3036 + OpMemberDecorate %type_View 177 Offset 3040 + OpMemberDecorate %type_View 178 Offset 3044 + OpMemberDecorate %type_View 179 Offset 3048 + OpMemberDecorate %type_View 180 Offset 3052 + OpMemberDecorate %type_View 181 Offset 3056 + OpMemberDecorate %type_View 181 MatrixStride 16 + OpMemberDecorate %type_View 181 ColMajor + OpMemberDecorate %type_View 182 Offset 3120 + OpMemberDecorate %type_View 183 Offset 3136 + OpDecorate %type_View Block + OpDecorate %_arr_v4float_uint_5 ArrayStride 16 + OpMemberDecorate %type_Material 0 Offset 0 + OpMemberDecorate %type_Material 1 Offset 80 + OpDecorate %type_Material Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %v2int = OpTypeVector %int 2 +%float_0_00100000005 = OpConstant %float 0.00100000005 + %uint_0 = OpConstant %uint 0 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %float_3 = OpConstant %float 3 + %uint_1 = OpConstant %uint 1 + %float_6 = OpConstant %float 6 + %67 = OpConstantComposite %v4float %float_6 %float_6 %float_6 %float_6 + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %int_3 = OpConstant %int 3 + %float_2 = OpConstant %float 2 + %int_26 = OpConstant %int 26 + %int_32 = OpConstant %int 32 + %int_54 = OpConstant %int 54 + %int_153 = OpConstant %int 153 + %int_154 = OpConstant %int 154 + %int_156 = OpConstant %int 156 + %int_157 = OpConstant %int 157 + %float_10 = OpConstant %float 10 + %uint_3 = OpConstant %uint 3 + %81 = OpConstantComposite %v3float %float_0 %float_0 %float_0 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %uint %uint %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v2int %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float %float %float %mat4v4float %v4float %_arr_v4float_uint_2 +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_3d_image = OpTypeImage %float 3D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_3d_image = OpTypePointer UniformConstant %type_3d_image + %uint_5 = OpConstant %uint 5 +%_arr_v4float_uint_5 = OpTypeArray %v4float %uint_5 +%type_Material = OpTypeStruct %_arr_v4float_uint_5 %_arr_v4float_uint_2 +%_ptr_Uniform_type_Material = OpTypePointer Uniform %type_Material +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%_ptr_Output__arr_float_uint_1 = OpTypePointer Output %_arr_float_uint_1 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 +%_arr_uint_uint_3 = OpTypeArray %uint %uint_3 +%_ptr_Input__arr_uint_uint_3 = OpTypePointer Input %_arr_uint_uint_3 +%_arr__arr_v4float_uint_3_uint_3 = OpTypeArray %_arr_v4float_uint_3 %uint_3 +%_ptr_Input__arr__arr_v4float_uint_3_uint_3 = OpTypePointer Input %_arr__arr_v4float_uint_3_uint_3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%_ptr_Input__arr_float_uint_3 = OpTypePointer Input %_arr_float_uint_3 +%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3 +%_ptr_Input__arr_v2float_uint_3 = OpTypePointer Input %_arr_v2float_uint_3 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Input__arr_float_uint_4 = OpTypePointer Input %_arr_float_uint_4 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Input__arr_float_uint_2 = OpTypePointer Input %_arr_float_uint_2 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_v3float = OpTypePointer Input %v3float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %109 = OpTypeFunction %void +%_ptr_Output_float = OpTypePointer Output %float +%mat3v3float = OpTypeMatrix %v3float 3 + %bool = OpTypeBool +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%type_sampled_image = OpTypeSampledImage %type_3d_image + %View = OpVariable %_ptr_Uniform_type_View Uniform +%View_GlobalDistanceFieldTexture0 = OpVariable %_ptr_UniformConstant_type_3d_image UniformConstant +%View_GlobalDistanceFieldSampler0 = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%View_GlobalDistanceFieldTexture1 = OpVariable %_ptr_UniformConstant_type_3d_image UniformConstant +%View_GlobalDistanceFieldTexture2 = OpVariable %_ptr_UniformConstant_type_3d_image UniformConstant +%View_GlobalDistanceFieldTexture3 = OpVariable %_ptr_UniformConstant_type_3d_image UniformConstant + %Material = OpVariable %_ptr_Uniform_type_Material Uniform +%gl_ClipDistance = OpVariable %_ptr_Output__arr_float_uint_1 Output +%in_var_TEXCOORD6 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD8 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_VS_To_DS_Position = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_VS_To_DS_VertexID = OpVariable %_ptr_Input__arr_uint_uint_3 Input +%in_var_PN_POSITION = OpVariable %_ptr_Input__arr__arr_v4float_uint_3_uint_3 Input +%in_var_PN_DisplacementScales = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_PN_TessellationMultiplier = OpVariable %_ptr_Input__arr_float_uint_3 Input +%in_var_PN_WorldDisplacementMultiplier = OpVariable %_ptr_Input__arr_float_uint_3 Input +%in_var_PN_DominantVertex = OpVariable %_ptr_Input__arr_v2float_uint_3 Input +%in_var_PN_DominantVertex1 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_PN_DominantVertex2 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_PN_DominantEdge = OpVariable %_ptr_Input__arr_v2float_uint_3 Input +%in_var_PN_DominantEdge1 = OpVariable %_ptr_Input__arr_v2float_uint_3 Input +%in_var_PN_DominantEdge2 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_PN_DominantEdge3 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_PN_DominantEdge4 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_PN_DominantEdge5 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%gl_TessLevelOuter = OpVariable %_ptr_Input__arr_float_uint_4 Input +%gl_TessLevelInner = OpVariable %_ptr_Input__arr_float_uint_2 Input +%in_var_PN_POSITION9 = OpVariable %_ptr_Input_v4float Input +%gl_TessCoord = OpVariable %_ptr_Input_v3float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD6 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD7 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD10_centroid = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD11_centroid = OpVariable %_ptr_Output_v4float Output + %117 = OpConstantNull %v4float + %118 = OpUndef %v4float + %MainDomain = OpFunction %void None %109 + %119 = OpLabel + %120 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD6 + %121 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD8 + %122 = OpCompositeExtract %v4float %120 0 + %123 = OpCompositeExtract %v4float %121 0 + %124 = OpCompositeExtract %v4float %120 1 + %125 = OpCompositeExtract %v4float %121 1 + %126 = OpCompositeExtract %v4float %120 2 + %127 = OpCompositeExtract %v4float %121 2 + %128 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD10_centroid + %129 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD11_centroid + %130 = OpCompositeExtract %v4float %128 0 + %131 = OpCompositeExtract %v4float %129 0 + %132 = OpCompositeExtract %v4float %128 1 + %133 = OpCompositeExtract %v4float %129 1 + %134 = OpCompositeExtract %v4float %128 2 + %135 = OpCompositeExtract %v4float %129 2 + %136 = OpLoad %_arr__arr_v4float_uint_3_uint_3 %in_var_PN_POSITION + %137 = OpLoad %_arr_float_uint_3 %in_var_PN_WorldDisplacementMultiplier + %138 = OpLoad %_arr_v4float_uint_3 %in_var_PN_DominantVertex1 + %139 = OpLoad %_arr_v3float_uint_3 %in_var_PN_DominantVertex2 + %140 = OpCompositeExtract %v4float %138 0 + %141 = OpCompositeExtract %v3float %139 0 + %142 = OpCompositeExtract %v4float %138 1 + %143 = OpCompositeExtract %v3float %139 1 + %144 = OpCompositeExtract %v4float %138 2 + %145 = OpCompositeExtract %v3float %139 2 + %146 = OpLoad %_arr_v4float_uint_3 %in_var_PN_DominantEdge2 + %147 = OpLoad %_arr_v4float_uint_3 %in_var_PN_DominantEdge3 + %148 = OpLoad %_arr_v3float_uint_3 %in_var_PN_DominantEdge4 + %149 = OpLoad %_arr_v3float_uint_3 %in_var_PN_DominantEdge5 + %150 = OpCompositeExtract %v4float %146 0 + %151 = OpCompositeExtract %v4float %147 0 + %152 = OpCompositeExtract %v3float %148 0 + %153 = OpCompositeExtract %v3float %149 0 + %154 = OpCompositeExtract %v4float %146 1 + %155 = OpCompositeExtract %v4float %147 1 + %156 = OpCompositeExtract %v3float %148 1 + %157 = OpCompositeExtract %v3float %149 1 + %158 = OpCompositeExtract %v4float %146 2 + %159 = OpCompositeExtract %v4float %147 2 + %160 = OpCompositeExtract %v3float %148 2 + %161 = OpCompositeExtract %v3float %149 2 + %162 = OpCompositeExtract %_arr_v4float_uint_3 %136 0 + %163 = OpCompositeExtract %float %137 0 + %164 = OpCompositeExtract %_arr_v4float_uint_3 %136 1 + %165 = OpCompositeExtract %float %137 1 + %166 = OpCompositeExtract %_arr_v4float_uint_3 %136 2 + %167 = OpCompositeExtract %float %137 2 + %168 = OpCompositeExtract %v4float %162 0 + %169 = OpCompositeExtract %v4float %162 1 + %170 = OpCompositeExtract %v4float %162 2 + %171 = OpCompositeExtract %v4float %164 0 + %172 = OpCompositeExtract %v4float %164 1 + %173 = OpCompositeExtract %v4float %164 2 + %174 = OpCompositeExtract %v4float %166 0 + %175 = OpCompositeExtract %v4float %166 1 + %176 = OpCompositeExtract %v4float %166 2 + %177 = OpLoad %v4float %in_var_PN_POSITION9 + %178 = OpLoad %v3float %gl_TessCoord + %179 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_0 + %180 = OpLoad %mat4v4float %179 + %181 = OpAccessChain %_ptr_Uniform_v3float %View %int_26 + %182 = OpLoad %v3float %181 + %183 = OpAccessChain %_ptr_Uniform_v3float %View %int_32 + %184 = OpLoad %v3float %183 + %185 = OpAccessChain %_ptr_Uniform_v4float %View %int_54 + %186 = OpLoad %v4float %185 + %187 = OpCompositeExtract %float %178 0 + %188 = OpCompositeExtract %float %178 1 + %189 = OpCompositeExtract %float %178 2 + %190 = OpFMul %float %187 %187 + %191 = OpFMul %float %188 %188 + %192 = OpFMul %float %189 %189 + %193 = OpFMul %float %190 %float_3 + %194 = OpFMul %float %191 %float_3 + %195 = OpFMul %float %192 %float_3 + %196 = OpCompositeConstruct %v4float %190 %190 %190 %190 + %197 = OpFMul %v4float %168 %196 + %198 = OpCompositeConstruct %v4float %187 %187 %187 %187 + %199 = OpFMul %v4float %197 %198 + %200 = OpCompositeConstruct %v4float %191 %191 %191 %191 + %201 = OpFMul %v4float %171 %200 + %202 = OpCompositeConstruct %v4float %188 %188 %188 %188 + %203 = OpFMul %v4float %201 %202 + %204 = OpFAdd %v4float %199 %203 + %205 = OpCompositeConstruct %v4float %192 %192 %192 %192 + %206 = OpFMul %v4float %174 %205 + %207 = OpCompositeConstruct %v4float %189 %189 %189 %189 + %208 = OpFMul %v4float %206 %207 + %209 = OpFAdd %v4float %204 %208 + %210 = OpCompositeConstruct %v4float %193 %193 %193 %193 + %211 = OpFMul %v4float %169 %210 + %212 = OpFMul %v4float %211 %202 + %213 = OpFAdd %v4float %209 %212 + %214 = OpCompositeConstruct %v4float %194 %194 %194 %194 + %215 = OpFMul %v4float %170 %214 + %216 = OpFMul %v4float %215 %198 + %217 = OpFAdd %v4float %213 %216 + %218 = OpFMul %v4float %172 %214 + %219 = OpFMul %v4float %218 %207 + %220 = OpFAdd %v4float %217 %219 + %221 = OpCompositeConstruct %v4float %195 %195 %195 %195 + %222 = OpFMul %v4float %173 %221 + %223 = OpFMul %v4float %222 %202 + %224 = OpFAdd %v4float %220 %223 + %225 = OpFMul %v4float %175 %221 + %226 = OpFMul %v4float %225 %198 + %227 = OpFAdd %v4float %224 %226 + %228 = OpFMul %v4float %176 %210 + %229 = OpFMul %v4float %228 %207 + %230 = OpFAdd %v4float %227 %229 + %231 = OpFMul %v4float %177 %67 + %232 = OpFMul %v4float %231 %207 + %233 = OpFMul %v4float %232 %198 + %234 = OpFMul %v4float %233 %202 + %235 = OpFAdd %v4float %230 %234 + %236 = OpVectorShuffle %v3float %130 %130 0 1 2 + %237 = OpCompositeConstruct %v3float %187 %187 %187 + %238 = OpFMul %v3float %236 %237 + %239 = OpVectorShuffle %v3float %132 %132 0 1 2 + %240 = OpCompositeConstruct %v3float %188 %188 %188 + %241 = OpFMul %v3float %239 %240 + %242 = OpFAdd %v3float %238 %241 + %243 = OpFMul %v4float %131 %198 + %244 = OpFMul %v4float %133 %202 + %245 = OpFAdd %v4float %243 %244 + %246 = OpFMul %v4float %122 %198 + %247 = OpFMul %v4float %124 %202 + %248 = OpFAdd %v4float %246 %247 + %249 = OpFMul %v4float %123 %198 + %250 = OpFMul %v4float %125 %202 + %251 = OpFAdd %v4float %249 %250 + %252 = OpVectorShuffle %v3float %242 %117 0 1 2 + %253 = OpVectorShuffle %v3float %134 %134 0 1 2 + %254 = OpCompositeConstruct %v3float %189 %189 %189 + %255 = OpFMul %v3float %253 %254 + %256 = OpFAdd %v3float %252 %255 + %257 = OpVectorShuffle %v4float %118 %256 4 5 6 3 + %258 = OpFMul %v4float %135 %207 + %259 = OpFAdd %v4float %245 %258 + %260 = OpFMul %v4float %126 %207 + %261 = OpFAdd %v4float %248 %260 + %262 = OpFMul %v4float %127 %207 + %263 = OpFAdd %v4float %251 %262 + %264 = OpVectorShuffle %v3float %235 %235 0 1 2 + %265 = OpVectorShuffle %v3float %256 %117 0 1 2 + %266 = OpVectorShuffle %v3float %259 %259 0 1 2 + %267 = OpExtInst %v3float %1 Cross %266 %265 + %268 = OpCompositeExtract %float %259 3 + %269 = OpCompositeConstruct %v3float %268 %268 %268 + %270 = OpFMul %v3float %267 %269 + %271 = OpCompositeConstruct %mat3v3float %265 %270 %266 + %272 = OpFAdd %v3float %264 %182 + %273 = OpCompositeExtract %float %259 0 + %274 = OpCompositeExtract %float %259 1 + %275 = OpCompositeExtract %float %259 2 + %276 = OpCompositeConstruct %v4float %273 %274 %275 %float_0 + %277 = OpFOrdEqual %bool %187 %float_0 + %278 = OpSelect %int %277 %int_1 %int_0 + %279 = OpConvertSToF %float %278 + %280 = OpFOrdEqual %bool %188 %float_0 + %281 = OpSelect %int %280 %int_1 %int_0 + %282 = OpConvertSToF %float %281 + %283 = OpFOrdEqual %bool %189 %float_0 + %284 = OpSelect %int %283 %int_1 %int_0 + %285 = OpConvertSToF %float %284 + %286 = OpFAdd %float %279 %282 + %287 = OpFAdd %float %286 %285 + %288 = OpFOrdEqual %bool %287 %float_2 + %289 = OpSelect %int %288 %int_1 %int_0 + %290 = OpConvertSToF %float %289 + %291 = OpFOrdEqual %bool %287 %float_1 + %292 = OpSelect %int %291 %int_1 %int_0 + %293 = OpConvertSToF %float %292 + %294 = OpFOrdEqual %bool %287 %float_0 + %295 = OpSelect %int %294 %int_1 %int_0 + %296 = OpConvertSToF %float %295 + %297 = OpFOrdEqual %bool %290 %float_1 + OpSelectionMerge %298 None + OpBranchConditional %297 %299 %300 + %300 = OpLabel + %301 = OpFOrdNotEqual %bool %293 %float_0 + OpSelectionMerge %302 None + OpBranchConditional %301 %303 %302 + %303 = OpLabel + %304 = OpCompositeConstruct %v4float %279 %279 %279 %279 + %305 = OpFMul %v4float %304 %150 + %306 = OpCompositeConstruct %v4float %282 %282 %282 %282 + %307 = OpFMul %v4float %306 %154 + %308 = OpFAdd %v4float %305 %307 + %309 = OpCompositeConstruct %v4float %285 %285 %285 %285 + %310 = OpFMul %v4float %309 %158 + %311 = OpFAdd %v4float %308 %310 + %312 = OpFMul %v4float %304 %151 + %313 = OpFMul %v4float %306 %155 + %314 = OpFAdd %v4float %312 %313 + %315 = OpFMul %v4float %309 %159 + %316 = OpFAdd %v4float %314 %315 + %317 = OpFMul %v4float %202 %311 + %318 = OpFMul %v4float %207 %316 + %319 = OpFAdd %v4float %317 %318 + %320 = OpFMul %v4float %304 %319 + %321 = OpFMul %v4float %207 %311 + %322 = OpFMul %v4float %198 %316 + %323 = OpFAdd %v4float %321 %322 + %324 = OpFMul %v4float %306 %323 + %325 = OpFAdd %v4float %320 %324 + %326 = OpFMul %v4float %198 %311 + %327 = OpFMul %v4float %202 %316 + %328 = OpFAdd %v4float %326 %327 + %329 = OpFMul %v4float %309 %328 + %330 = OpFAdd %v4float %325 %329 + %331 = OpCompositeConstruct %v3float %279 %279 %279 + %332 = OpFMul %v3float %331 %152 + %333 = OpCompositeConstruct %v3float %282 %282 %282 + %334 = OpFMul %v3float %333 %156 + %335 = OpFAdd %v3float %332 %334 + %336 = OpCompositeConstruct %v3float %285 %285 %285 + %337 = OpFMul %v3float %336 %160 + %338 = OpFAdd %v3float %335 %337 + %339 = OpFMul %v3float %331 %153 + %340 = OpFMul %v3float %333 %157 + %341 = OpFAdd %v3float %339 %340 + %342 = OpFMul %v3float %336 %161 + %343 = OpFAdd %v3float %341 %342 + %344 = OpFMul %v3float %240 %338 + %345 = OpFMul %v3float %254 %343 + %346 = OpFAdd %v3float %344 %345 + %347 = OpFMul %v3float %331 %346 + %348 = OpFMul %v3float %254 %338 + %349 = OpFMul %v3float %237 %343 + %350 = OpFAdd %v3float %348 %349 + %351 = OpFMul %v3float %333 %350 + %352 = OpFAdd %v3float %347 %351 + %353 = OpFMul %v3float %237 %338 + %354 = OpFMul %v3float %240 %343 + %355 = OpFAdd %v3float %353 %354 + %356 = OpFMul %v3float %336 %355 + %357 = OpFAdd %v3float %352 %356 + OpBranch %302 + %302 = OpLabel + %358 = OpPhi %v4float %276 %300 %330 %303 + %359 = OpPhi %v3float %265 %300 %357 %303 + OpBranch %298 + %299 = OpLabel + %360 = OpFAdd %float %282 %285 + %361 = OpFOrdEqual %bool %360 %float_2 + %362 = OpSelect %int %361 %int_1 %int_0 + %363 = OpConvertSToF %float %362 + %364 = OpFAdd %float %285 %279 + %365 = OpFOrdEqual %bool %364 %float_2 + %366 = OpSelect %int %365 %int_1 %int_0 + %367 = OpConvertSToF %float %366 + %368 = OpFOrdEqual %bool %286 %float_2 + %369 = OpSelect %int %368 %int_1 %int_0 + %370 = OpConvertSToF %float %369 + %371 = OpCompositeConstruct %v4float %363 %363 %363 %363 + %372 = OpFMul %v4float %371 %140 + %373 = OpCompositeConstruct %v4float %367 %367 %367 %367 + %374 = OpFMul %v4float %373 %142 + %375 = OpFAdd %v4float %372 %374 + %376 = OpCompositeConstruct %v4float %370 %370 %370 %370 + %377 = OpFMul %v4float %376 %144 + %378 = OpFAdd %v4float %375 %377 + %379 = OpCompositeConstruct %v3float %363 %363 %363 + %380 = OpFMul %v3float %379 %141 + %381 = OpCompositeConstruct %v3float %367 %367 %367 + %382 = OpFMul %v3float %381 %143 + %383 = OpFAdd %v3float %380 %382 + %384 = OpCompositeConstruct %v3float %370 %370 %370 + %385 = OpFMul %v3float %384 %145 + %386 = OpFAdd %v3float %383 %385 + OpBranch %298 + %298 = OpLabel + %387 = OpPhi %v4float %378 %299 %358 %302 + %388 = OpPhi %v3float %386 %299 %359 %302 + %389 = OpFOrdEqual %bool %296 %float_0 + OpSelectionMerge %390 None + OpBranchConditional %389 %391 %390 + %391 = OpLabel + %392 = OpVectorShuffle %v3float %387 %387 0 1 2 + %393 = OpExtInst %v3float %1 Cross %392 %388 + %394 = OpCompositeExtract %float %387 3 + %395 = OpCompositeConstruct %v3float %394 %394 %394 + %396 = OpFMul %v3float %393 %395 + %397 = OpCompositeConstruct %mat3v3float %388 %396 %392 + OpBranch %390 + %390 = OpLabel + %398 = OpPhi %mat3v3float %271 %298 %397 %391 + %399 = OpAccessChain %_ptr_Uniform_float %View %int_157 + %400 = OpLoad %float %399 + %401 = OpAccessChain %_ptr_Uniform_v4float %View %int_153 %int_0 + %402 = OpLoad %v4float %401 + %403 = OpVectorShuffle %v3float %402 %402 0 1 2 + %404 = OpVectorShuffle %v3float %402 %402 3 3 3 + %405 = OpFSub %v3float %272 %403 + %406 = OpFAdd %v3float %405 %404 + %407 = OpExtInst %v3float %1 FMax %406 %81 + %408 = OpFAdd %v3float %403 %404 + %409 = OpFSub %v3float %408 %272 + %410 = OpExtInst %v3float %1 FMax %409 %81 + %411 = OpExtInst %v3float %1 FMin %407 %410 + %412 = OpCompositeExtract %float %411 0 + %413 = OpCompositeExtract %float %411 1 + %414 = OpCompositeExtract %float %411 2 + %415 = OpExtInst %float %1 FMin %413 %414 + %416 = OpExtInst %float %1 FMin %412 %415 + %417 = OpAccessChain %_ptr_Uniform_float %View %int_153 %int_0 %int_3 + %418 = OpLoad %float %417 + %419 = OpAccessChain %_ptr_Uniform_float %View %int_156 + %420 = OpLoad %float %419 + %421 = OpFMul %float %418 %420 + %422 = OpFOrdGreaterThan %bool %416 %421 + OpSelectionMerge %423 DontFlatten + OpBranchConditional %422 %424 %425 + %425 = OpLabel + %426 = OpAccessChain %_ptr_Uniform_v4float %View %int_153 %int_1 + %427 = OpLoad %v4float %426 + %428 = OpVectorShuffle %v3float %427 %427 0 1 2 + %429 = OpVectorShuffle %v3float %427 %427 3 3 3 + %430 = OpFSub %v3float %272 %428 + %431 = OpFAdd %v3float %430 %429 + %432 = OpExtInst %v3float %1 FMax %431 %81 + %433 = OpFAdd %v3float %428 %429 + %434 = OpFSub %v3float %433 %272 + %435 = OpExtInst %v3float %1 FMax %434 %81 + %436 = OpExtInst %v3float %1 FMin %432 %435 + %437 = OpCompositeExtract %float %436 0 + %438 = OpCompositeExtract %float %436 1 + %439 = OpCompositeExtract %float %436 2 + %440 = OpExtInst %float %1 FMin %438 %439 + %441 = OpExtInst %float %1 FMin %437 %440 + %442 = OpAccessChain %_ptr_Uniform_float %View %int_153 %int_1 %int_3 + %443 = OpLoad %float %442 + %444 = OpFMul %float %443 %420 + %445 = OpFOrdGreaterThan %bool %441 %444 + OpSelectionMerge %446 DontFlatten + OpBranchConditional %445 %447 %448 + %448 = OpLabel + %449 = OpAccessChain %_ptr_Uniform_v4float %View %int_153 %int_2 + %450 = OpLoad %v4float %449 + %451 = OpVectorShuffle %v3float %450 %450 0 1 2 + %452 = OpVectorShuffle %v3float %450 %450 3 3 3 + %453 = OpFSub %v3float %272 %451 + %454 = OpFAdd %v3float %453 %452 + %455 = OpExtInst %v3float %1 FMax %454 %81 + %456 = OpFAdd %v3float %451 %452 + %457 = OpFSub %v3float %456 %272 + %458 = OpExtInst %v3float %1 FMax %457 %81 + %459 = OpExtInst %v3float %1 FMin %455 %458 + %460 = OpCompositeExtract %float %459 0 + %461 = OpCompositeExtract %float %459 1 + %462 = OpCompositeExtract %float %459 2 + %463 = OpExtInst %float %1 FMin %461 %462 + %464 = OpExtInst %float %1 FMin %460 %463 + %465 = OpAccessChain %_ptr_Uniform_v4float %View %int_153 %int_3 + %466 = OpLoad %v4float %465 + %467 = OpVectorShuffle %v3float %466 %466 0 1 2 + %468 = OpVectorShuffle %v3float %466 %466 3 3 3 + %469 = OpFSub %v3float %272 %467 + %470 = OpFAdd %v3float %469 %468 + %471 = OpExtInst %v3float %1 FMax %470 %81 + %472 = OpFAdd %v3float %467 %468 + %473 = OpFSub %v3float %472 %272 + %474 = OpExtInst %v3float %1 FMax %473 %81 + %475 = OpExtInst %v3float %1 FMin %471 %474 + %476 = OpCompositeExtract %float %475 0 + %477 = OpCompositeExtract %float %475 1 + %478 = OpCompositeExtract %float %475 2 + %479 = OpExtInst %float %1 FMin %477 %478 + %480 = OpExtInst %float %1 FMin %476 %479 + %481 = OpAccessChain %_ptr_Uniform_float %View %int_153 %int_2 %int_3 + %482 = OpLoad %float %481 + %483 = OpFMul %float %482 %420 + %484 = OpFOrdGreaterThan %bool %464 %483 + OpSelectionMerge %485 DontFlatten + OpBranchConditional %484 %486 %487 + %487 = OpLabel + %488 = OpAccessChain %_ptr_Uniform_float %View %int_153 %int_3 %int_3 + %489 = OpLoad %float %488 + %490 = OpFMul %float %489 %420 + %491 = OpFOrdGreaterThan %bool %480 %490 + OpSelectionMerge %492 None + OpBranchConditional %491 %493 %492 + %493 = OpLabel + %494 = OpFMul %float %480 %float_10 + %495 = OpAccessChain %_ptr_Uniform_float %View %int_154 %int_3 %int_3 + %496 = OpLoad %float %495 + %497 = OpFMul %float %494 %496 + %498 = OpExtInst %float %1 FClamp %497 %float_0 %float_1 + %499 = OpAccessChain %_ptr_Uniform_v4float %View %int_154 %uint_3 + %500 = OpLoad %v4float %499 + %501 = OpVectorShuffle %v3float %500 %500 3 3 3 + %502 = OpFMul %v3float %272 %501 + %503 = OpVectorShuffle %v3float %500 %500 0 1 2 + %504 = OpFAdd %v3float %502 %503 + %505 = OpLoad %type_3d_image %View_GlobalDistanceFieldTexture3 + %506 = OpLoad %type_sampler %View_GlobalDistanceFieldSampler0 + %507 = OpSampledImage %type_sampled_image %505 %506 + %508 = OpImageSampleExplicitLod %v4float %507 %504 Lod %float_0 + %509 = OpCompositeExtract %float %508 0 + %510 = OpExtInst %float %1 FMix %400 %509 %498 + OpBranch %492 + %492 = OpLabel + %511 = OpPhi %float %400 %487 %510 %493 + OpBranch %485 + %486 = OpLabel + %512 = OpAccessChain %_ptr_Uniform_v4float %View %int_154 %uint_2 + %513 = OpLoad %v4float %512 + %514 = OpVectorShuffle %v3float %513 %513 3 3 3 + %515 = OpFMul %v3float %272 %514 + %516 = OpVectorShuffle %v3float %513 %513 0 1 2 + %517 = OpFAdd %v3float %515 %516 + %518 = OpLoad %type_3d_image %View_GlobalDistanceFieldTexture2 + %519 = OpLoad %type_sampler %View_GlobalDistanceFieldSampler0 + %520 = OpSampledImage %type_sampled_image %518 %519 + %521 = OpImageSampleExplicitLod %v4float %520 %517 Lod %float_0 + %522 = OpCompositeExtract %float %521 0 + OpBranch %485 + %485 = OpLabel + %523 = OpPhi %float %522 %486 %511 %492 + OpBranch %446 + %447 = OpLabel + %524 = OpAccessChain %_ptr_Uniform_v4float %View %int_154 %uint_1 + %525 = OpLoad %v4float %524 + %526 = OpVectorShuffle %v3float %525 %525 3 3 3 + %527 = OpFMul %v3float %272 %526 + %528 = OpVectorShuffle %v3float %525 %525 0 1 2 + %529 = OpFAdd %v3float %527 %528 + %530 = OpLoad %type_3d_image %View_GlobalDistanceFieldTexture1 + %531 = OpLoad %type_sampler %View_GlobalDistanceFieldSampler0 + %532 = OpSampledImage %type_sampled_image %530 %531 + %533 = OpImageSampleExplicitLod %v4float %532 %529 Lod %float_0 + %534 = OpCompositeExtract %float %533 0 + OpBranch %446 + %446 = OpLabel + %535 = OpPhi %float %534 %447 %523 %485 + OpBranch %423 + %424 = OpLabel + %536 = OpAccessChain %_ptr_Uniform_v4float %View %int_154 %uint_0 + %537 = OpLoad %v4float %536 + %538 = OpVectorShuffle %v3float %537 %537 3 3 3 + %539 = OpFMul %v3float %272 %538 + %540 = OpVectorShuffle %v3float %537 %537 0 1 2 + %541 = OpFAdd %v3float %539 %540 + %542 = OpLoad %type_3d_image %View_GlobalDistanceFieldTexture0 + %543 = OpLoad %type_sampler %View_GlobalDistanceFieldSampler0 + %544 = OpSampledImage %type_sampled_image %542 %543 + %545 = OpImageSampleExplicitLod %v4float %544 %541 Lod %float_0 + %546 = OpCompositeExtract %float %545 0 + OpBranch %423 + %423 = OpLabel + %547 = OpPhi %float %546 %424 %535 %446 + %548 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_2 + %549 = OpLoad %float %548 + %550 = OpFAdd %float %547 %549 + %551 = OpExtInst %float %1 FMin %550 %float_0 + %552 = OpAccessChain %_ptr_Uniform_float %Material %int_1 %int_0 %int_3 + %553 = OpLoad %float %552 + %554 = OpFMul %float %551 %553 + %555 = OpCompositeExtract %v3float %398 2 + %556 = OpCompositeConstruct %v3float %554 %554 %554 + %557 = OpFMul %v3float %555 %556 + %558 = OpFMul %float %163 %187 + %559 = OpFMul %float %165 %188 + %560 = OpFAdd %float %558 %559 + %561 = OpFMul %float %167 %189 + %562 = OpFAdd %float %560 %561 + %563 = OpCompositeConstruct %v3float %562 %562 %562 + %564 = OpFMul %v3float %557 %563 + %565 = OpFAdd %v3float %264 %564 + %566 = OpVectorShuffle %v4float %235 %565 4 5 6 3 + %567 = OpVectorShuffle %v3float %565 %117 0 1 2 + %568 = OpFSub %v3float %567 %184 + %569 = OpCompositeExtract %float %568 0 + %570 = OpCompositeExtract %float %568 1 + %571 = OpCompositeExtract %float %568 2 + %572 = OpCompositeConstruct %v4float %569 %570 %571 %float_1 + %573 = OpDot %float %186 %572 + %574 = OpMatrixTimesVector %v4float %180 %566 + %575 = OpCompositeExtract %float %574 3 + %576 = OpFMul %float %float_0_00100000005 %575 + %577 = OpCompositeExtract %float %574 2 + %578 = OpFAdd %float %577 %576 + %579 = OpCompositeInsert %v4float %578 %574 2 + OpStore %gl_Position %579 + OpStore %out_var_TEXCOORD6 %261 + OpStore %out_var_TEXCOORD7 %263 + OpStore %out_var_TEXCOORD10_centroid %257 + OpStore %out_var_TEXCOORD11_centroid %259 + %580 = OpAccessChain %_ptr_Output_float %gl_ClipDistance %uint_0 + OpStore %580 %573 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese b/third_party/spirv-cross/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese new file mode 100644 index 0000000..e792c7e --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/tese/ds-patch-inputs.asm.tese @@ -0,0 +1,547 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 236 +; Schema: 0 + OpCapability Tessellation + OpCapability SampledBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %MainDomain "main" %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_VS_to_DS_Position %in_var_VS_To_DS_VertexID %in_var_PN_POSITION %in_var_PN_DisplacementScales %in_var_PN_TessellationMultiplier %in_var_PN_WorldDisplacementMultiplier %in_var_PN_DominantVertex %in_var_PN_DominantVertex1 %in_var_PN_DominantVertex2 %in_var_PN_DominantEdge %in_var_PN_DominantEdge1 %in_var_PN_DominantEdge2 %in_var_PN_DominantEdge3 %in_var_PN_DominantEdge4 %in_var_PN_DominantEdge5 %gl_TessLevelOuter %gl_TessLevelInner %in_var_PN_POSITION9 %gl_TessCoord %out_var_TEXCOORD10_centroid %out_var_TEXCOORD11_centroid %out_var_TEXCOORD6 %out_var_TEXCOORD7 %gl_Position + OpExecutionMode %MainDomain Triangles + OpSource HLSL 600 + OpName %type_ShadowDepthPass "type.ShadowDepthPass" + OpMemberName %type_ShadowDepthPass 0 "PrePadding_ShadowDepthPass_LPV_0" + OpMemberName %type_ShadowDepthPass 1 "PrePadding_ShadowDepthPass_LPV_4" + OpMemberName %type_ShadowDepthPass 2 "PrePadding_ShadowDepthPass_LPV_8" + OpMemberName %type_ShadowDepthPass 3 "PrePadding_ShadowDepthPass_LPV_12" + OpMemberName %type_ShadowDepthPass 4 "PrePadding_ShadowDepthPass_LPV_16" + OpMemberName %type_ShadowDepthPass 5 "PrePadding_ShadowDepthPass_LPV_20" + OpMemberName %type_ShadowDepthPass 6 "PrePadding_ShadowDepthPass_LPV_24" + OpMemberName %type_ShadowDepthPass 7 "PrePadding_ShadowDepthPass_LPV_28" + OpMemberName %type_ShadowDepthPass 8 "PrePadding_ShadowDepthPass_LPV_32" + OpMemberName %type_ShadowDepthPass 9 "PrePadding_ShadowDepthPass_LPV_36" + OpMemberName %type_ShadowDepthPass 10 "PrePadding_ShadowDepthPass_LPV_40" + OpMemberName %type_ShadowDepthPass 11 "PrePadding_ShadowDepthPass_LPV_44" + OpMemberName %type_ShadowDepthPass 12 "PrePadding_ShadowDepthPass_LPV_48" + OpMemberName %type_ShadowDepthPass 13 "PrePadding_ShadowDepthPass_LPV_52" + OpMemberName %type_ShadowDepthPass 14 "PrePadding_ShadowDepthPass_LPV_56" + OpMemberName %type_ShadowDepthPass 15 "PrePadding_ShadowDepthPass_LPV_60" + OpMemberName %type_ShadowDepthPass 16 "PrePadding_ShadowDepthPass_LPV_64" + OpMemberName %type_ShadowDepthPass 17 "PrePadding_ShadowDepthPass_LPV_68" + OpMemberName %type_ShadowDepthPass 18 "PrePadding_ShadowDepthPass_LPV_72" + OpMemberName %type_ShadowDepthPass 19 "PrePadding_ShadowDepthPass_LPV_76" + OpMemberName %type_ShadowDepthPass 20 "PrePadding_ShadowDepthPass_LPV_80" + OpMemberName %type_ShadowDepthPass 21 "PrePadding_ShadowDepthPass_LPV_84" + OpMemberName %type_ShadowDepthPass 22 "PrePadding_ShadowDepthPass_LPV_88" + OpMemberName %type_ShadowDepthPass 23 "PrePadding_ShadowDepthPass_LPV_92" + OpMemberName %type_ShadowDepthPass 24 "PrePadding_ShadowDepthPass_LPV_96" + OpMemberName %type_ShadowDepthPass 25 "PrePadding_ShadowDepthPass_LPV_100" + OpMemberName %type_ShadowDepthPass 26 "PrePadding_ShadowDepthPass_LPV_104" + OpMemberName %type_ShadowDepthPass 27 "PrePadding_ShadowDepthPass_LPV_108" + OpMemberName %type_ShadowDepthPass 28 "PrePadding_ShadowDepthPass_LPV_112" + OpMemberName %type_ShadowDepthPass 29 "PrePadding_ShadowDepthPass_LPV_116" + OpMemberName %type_ShadowDepthPass 30 "PrePadding_ShadowDepthPass_LPV_120" + OpMemberName %type_ShadowDepthPass 31 "PrePadding_ShadowDepthPass_LPV_124" + OpMemberName %type_ShadowDepthPass 32 "PrePadding_ShadowDepthPass_LPV_128" + OpMemberName %type_ShadowDepthPass 33 "PrePadding_ShadowDepthPass_LPV_132" + OpMemberName %type_ShadowDepthPass 34 "PrePadding_ShadowDepthPass_LPV_136" + OpMemberName %type_ShadowDepthPass 35 "PrePadding_ShadowDepthPass_LPV_140" + OpMemberName %type_ShadowDepthPass 36 "PrePadding_ShadowDepthPass_LPV_144" + OpMemberName %type_ShadowDepthPass 37 "PrePadding_ShadowDepthPass_LPV_148" + OpMemberName %type_ShadowDepthPass 38 "PrePadding_ShadowDepthPass_LPV_152" + OpMemberName %type_ShadowDepthPass 39 "PrePadding_ShadowDepthPass_LPV_156" + OpMemberName %type_ShadowDepthPass 40 "PrePadding_ShadowDepthPass_LPV_160" + OpMemberName %type_ShadowDepthPass 41 "PrePadding_ShadowDepthPass_LPV_164" + OpMemberName %type_ShadowDepthPass 42 "PrePadding_ShadowDepthPass_LPV_168" + OpMemberName %type_ShadowDepthPass 43 "PrePadding_ShadowDepthPass_LPV_172" + OpMemberName %type_ShadowDepthPass 44 "PrePadding_ShadowDepthPass_LPV_176" + OpMemberName %type_ShadowDepthPass 45 "PrePadding_ShadowDepthPass_LPV_180" + OpMemberName %type_ShadowDepthPass 46 "PrePadding_ShadowDepthPass_LPV_184" + OpMemberName %type_ShadowDepthPass 47 "PrePadding_ShadowDepthPass_LPV_188" + OpMemberName %type_ShadowDepthPass 48 "PrePadding_ShadowDepthPass_LPV_192" + OpMemberName %type_ShadowDepthPass 49 "PrePadding_ShadowDepthPass_LPV_196" + OpMemberName %type_ShadowDepthPass 50 "PrePadding_ShadowDepthPass_LPV_200" + OpMemberName %type_ShadowDepthPass 51 "PrePadding_ShadowDepthPass_LPV_204" + OpMemberName %type_ShadowDepthPass 52 "PrePadding_ShadowDepthPass_LPV_208" + OpMemberName %type_ShadowDepthPass 53 "PrePadding_ShadowDepthPass_LPV_212" + OpMemberName %type_ShadowDepthPass 54 "PrePadding_ShadowDepthPass_LPV_216" + OpMemberName %type_ShadowDepthPass 55 "PrePadding_ShadowDepthPass_LPV_220" + OpMemberName %type_ShadowDepthPass 56 "PrePadding_ShadowDepthPass_LPV_224" + OpMemberName %type_ShadowDepthPass 57 "PrePadding_ShadowDepthPass_LPV_228" + OpMemberName %type_ShadowDepthPass 58 "PrePadding_ShadowDepthPass_LPV_232" + OpMemberName %type_ShadowDepthPass 59 "PrePadding_ShadowDepthPass_LPV_236" + OpMemberName %type_ShadowDepthPass 60 "PrePadding_ShadowDepthPass_LPV_240" + OpMemberName %type_ShadowDepthPass 61 "PrePadding_ShadowDepthPass_LPV_244" + OpMemberName %type_ShadowDepthPass 62 "PrePadding_ShadowDepthPass_LPV_248" + OpMemberName %type_ShadowDepthPass 63 "PrePadding_ShadowDepthPass_LPV_252" + OpMemberName %type_ShadowDepthPass 64 "PrePadding_ShadowDepthPass_LPV_256" + OpMemberName %type_ShadowDepthPass 65 "PrePadding_ShadowDepthPass_LPV_260" + OpMemberName %type_ShadowDepthPass 66 "PrePadding_ShadowDepthPass_LPV_264" + OpMemberName %type_ShadowDepthPass 67 "PrePadding_ShadowDepthPass_LPV_268" + OpMemberName %type_ShadowDepthPass 68 "ShadowDepthPass_LPV_mRsmToWorld" + OpMemberName %type_ShadowDepthPass 69 "ShadowDepthPass_LPV_mLightColour" + OpMemberName %type_ShadowDepthPass 70 "ShadowDepthPass_LPV_GeometryVolumeCaptureLightDirection" + OpMemberName %type_ShadowDepthPass 71 "ShadowDepthPass_LPV_mEyePos" + OpMemberName %type_ShadowDepthPass 72 "ShadowDepthPass_LPV_mOldGridOffset" + OpMemberName %type_ShadowDepthPass 73 "PrePadding_ShadowDepthPass_LPV_396" + OpMemberName %type_ShadowDepthPass 74 "ShadowDepthPass_LPV_mLpvGridOffset" + OpMemberName %type_ShadowDepthPass 75 "ShadowDepthPass_LPV_ClearMultiplier" + OpMemberName %type_ShadowDepthPass 76 "ShadowDepthPass_LPV_LpvScale" + OpMemberName %type_ShadowDepthPass 77 "ShadowDepthPass_LPV_OneOverLpvScale" + OpMemberName %type_ShadowDepthPass 78 "ShadowDepthPass_LPV_DirectionalOcclusionIntensity" + OpMemberName %type_ShadowDepthPass 79 "ShadowDepthPass_LPV_DirectionalOcclusionRadius" + OpMemberName %type_ShadowDepthPass 80 "ShadowDepthPass_LPV_RsmAreaIntensityMultiplier" + OpMemberName %type_ShadowDepthPass 81 "ShadowDepthPass_LPV_RsmPixelToTexcoordMultiplier" + OpMemberName %type_ShadowDepthPass 82 "ShadowDepthPass_LPV_SecondaryOcclusionStrength" + OpMemberName %type_ShadowDepthPass 83 "ShadowDepthPass_LPV_SecondaryBounceStrength" + OpMemberName %type_ShadowDepthPass 84 "ShadowDepthPass_LPV_VplInjectionBias" + OpMemberName %type_ShadowDepthPass 85 "ShadowDepthPass_LPV_GeometryVolumeInjectionBias" + OpMemberName %type_ShadowDepthPass 86 "ShadowDepthPass_LPV_EmissiveInjectionMultiplier" + OpMemberName %type_ShadowDepthPass 87 "ShadowDepthPass_LPV_PropagationIndex" + OpMemberName %type_ShadowDepthPass 88 "ShadowDepthPass_ProjectionMatrix" + OpMemberName %type_ShadowDepthPass 89 "ShadowDepthPass_ViewMatrix" + OpMemberName %type_ShadowDepthPass 90 "ShadowDepthPass_ShadowParams" + OpMemberName %type_ShadowDepthPass 91 "ShadowDepthPass_bClampToNearPlane" + OpMemberName %type_ShadowDepthPass 92 "PrePadding_ShadowDepthPass_612" + OpMemberName %type_ShadowDepthPass 93 "PrePadding_ShadowDepthPass_616" + OpMemberName %type_ShadowDepthPass 94 "PrePadding_ShadowDepthPass_620" + OpMemberName %type_ShadowDepthPass 95 "ShadowDepthPass_ShadowViewProjectionMatrices" + OpMemberName %type_ShadowDepthPass 96 "ShadowDepthPass_ShadowViewMatrices" + OpName %ShadowDepthPass "ShadowDepthPass" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_VS_to_DS_Position "in.var.VS_to_DS_Position" + OpName %in_var_VS_To_DS_VertexID "in.var.VS_To_DS_VertexID" + OpName %in_var_PN_POSITION "in.var.PN_POSITION" + OpName %in_var_PN_DisplacementScales "in.var.PN_DisplacementScales" + OpName %in_var_PN_TessellationMultiplier "in.var.PN_TessellationMultiplier" + OpName %in_var_PN_WorldDisplacementMultiplier "in.var.PN_WorldDisplacementMultiplier" + OpName %in_var_PN_DominantVertex "in.var.PN_DominantVertex" + OpName %in_var_PN_DominantVertex1 "in.var.PN_DominantVertex1" + OpName %in_var_PN_DominantVertex2 "in.var.PN_DominantVertex2" + OpName %in_var_PN_DominantEdge "in.var.PN_DominantEdge" + OpName %in_var_PN_DominantEdge1 "in.var.PN_DominantEdge1" + OpName %in_var_PN_DominantEdge2 "in.var.PN_DominantEdge2" + OpName %in_var_PN_DominantEdge3 "in.var.PN_DominantEdge3" + OpName %in_var_PN_DominantEdge4 "in.var.PN_DominantEdge4" + OpName %in_var_PN_DominantEdge5 "in.var.PN_DominantEdge5" + OpName %in_var_PN_POSITION9 "in.var.PN_POSITION9" + OpName %out_var_TEXCOORD10_centroid "out.var.TEXCOORD10_centroid" + OpName %out_var_TEXCOORD11_centroid "out.var.TEXCOORD11_centroid" + OpName %out_var_TEXCOORD6 "out.var.TEXCOORD6" + OpName %out_var_TEXCOORD7 "out.var.TEXCOORD7" + OpName %MainDomain "MainDomain" + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_VS_to_DS_Position UserSemantic "VS_to_DS_Position" + OpDecorateString %in_var_VS_To_DS_VertexID UserSemantic "VS_To_DS_VertexID" + OpDecorateString %in_var_PN_POSITION UserSemantic "PN_POSITION" + OpDecorateString %in_var_PN_DisplacementScales UserSemantic "PN_DisplacementScales" + OpDecorateString %in_var_PN_TessellationMultiplier UserSemantic "PN_TessellationMultiplier" + OpDecorateString %in_var_PN_WorldDisplacementMultiplier UserSemantic "PN_WorldDisplacementMultiplier" + OpDecorateString %in_var_PN_DominantVertex UserSemantic "PN_DominantVertex" + OpDecorateString %in_var_PN_DominantVertex1 UserSemantic "PN_DominantVertex" + OpDecorateString %in_var_PN_DominantVertex2 UserSemantic "PN_DominantVertex" + OpDecorateString %in_var_PN_DominantEdge UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge1 UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge2 UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge3 UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge4 UserSemantic "PN_DominantEdge" + OpDecorateString %in_var_PN_DominantEdge5 UserSemantic "PN_DominantEdge" + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorateString %gl_TessLevelOuter UserSemantic "SV_TessFactor" + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorateString %gl_TessLevelInner UserSemantic "SV_InsideTessFactor" + OpDecorate %gl_TessLevelInner Patch + OpDecorateString %in_var_PN_POSITION9 UserSemantic "PN_POSITION9" + OpDecorate %in_var_PN_POSITION9 Patch + OpDecorate %gl_TessCoord BuiltIn TessCoord + OpDecorateString %gl_TessCoord UserSemantic "SV_DomainLocation" + OpDecorate %gl_TessCoord Patch + OpDecorateString %out_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %out_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %out_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorateString %out_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorate %gl_Position BuiltIn Position + OpDecorateString %gl_Position UserSemantic "SV_POSITION" + OpDecorate %in_var_PN_DisplacementScales Location 0 + OpDecorate %in_var_PN_DominantEdge Location 1 + OpDecorate %in_var_PN_DominantEdge1 Location 2 + OpDecorate %in_var_PN_DominantEdge2 Location 3 + OpDecorate %in_var_PN_DominantEdge3 Location 4 + OpDecorate %in_var_PN_DominantEdge4 Location 5 + OpDecorate %in_var_PN_DominantEdge5 Location 6 + OpDecorate %in_var_PN_DominantVertex Location 7 + OpDecorate %in_var_PN_DominantVertex1 Location 8 + OpDecorate %in_var_PN_DominantVertex2 Location 9 + OpDecorate %in_var_PN_POSITION Location 10 + OpDecorate %in_var_PN_POSITION9 Location 13 + OpDecorate %in_var_PN_TessellationMultiplier Location 14 + OpDecorate %in_var_PN_WorldDisplacementMultiplier Location 15 + OpDecorate %in_var_TEXCOORD10_centroid Location 16 + OpDecorate %in_var_TEXCOORD11_centroid Location 17 + OpDecorate %in_var_VS_To_DS_VertexID Location 18 + OpDecorate %in_var_VS_to_DS_Position Location 19 + OpDecorate %out_var_TEXCOORD10_centroid Location 0 + OpDecorate %out_var_TEXCOORD11_centroid Location 1 + OpDecorate %out_var_TEXCOORD6 Location 2 + OpDecorate %out_var_TEXCOORD7 Location 3 + OpDecorate %ShadowDepthPass DescriptorSet 0 + OpDecorate %ShadowDepthPass Binding 0 + OpDecorate %_arr_mat4v4float_uint_6 ArrayStride 64 + OpMemberDecorate %type_ShadowDepthPass 0 Offset 0 + OpMemberDecorate %type_ShadowDepthPass 1 Offset 4 + OpMemberDecorate %type_ShadowDepthPass 2 Offset 8 + OpMemberDecorate %type_ShadowDepthPass 3 Offset 12 + OpMemberDecorate %type_ShadowDepthPass 4 Offset 16 + OpMemberDecorate %type_ShadowDepthPass 5 Offset 20 + OpMemberDecorate %type_ShadowDepthPass 6 Offset 24 + OpMemberDecorate %type_ShadowDepthPass 7 Offset 28 + OpMemberDecorate %type_ShadowDepthPass 8 Offset 32 + OpMemberDecorate %type_ShadowDepthPass 9 Offset 36 + OpMemberDecorate %type_ShadowDepthPass 10 Offset 40 + OpMemberDecorate %type_ShadowDepthPass 11 Offset 44 + OpMemberDecorate %type_ShadowDepthPass 12 Offset 48 + OpMemberDecorate %type_ShadowDepthPass 13 Offset 52 + OpMemberDecorate %type_ShadowDepthPass 14 Offset 56 + OpMemberDecorate %type_ShadowDepthPass 15 Offset 60 + OpMemberDecorate %type_ShadowDepthPass 16 Offset 64 + OpMemberDecorate %type_ShadowDepthPass 17 Offset 68 + OpMemberDecorate %type_ShadowDepthPass 18 Offset 72 + OpMemberDecorate %type_ShadowDepthPass 19 Offset 76 + OpMemberDecorate %type_ShadowDepthPass 20 Offset 80 + OpMemberDecorate %type_ShadowDepthPass 21 Offset 84 + OpMemberDecorate %type_ShadowDepthPass 22 Offset 88 + OpMemberDecorate %type_ShadowDepthPass 23 Offset 92 + OpMemberDecorate %type_ShadowDepthPass 24 Offset 96 + OpMemberDecorate %type_ShadowDepthPass 25 Offset 100 + OpMemberDecorate %type_ShadowDepthPass 26 Offset 104 + OpMemberDecorate %type_ShadowDepthPass 27 Offset 108 + OpMemberDecorate %type_ShadowDepthPass 28 Offset 112 + OpMemberDecorate %type_ShadowDepthPass 29 Offset 116 + OpMemberDecorate %type_ShadowDepthPass 30 Offset 120 + OpMemberDecorate %type_ShadowDepthPass 31 Offset 124 + OpMemberDecorate %type_ShadowDepthPass 32 Offset 128 + OpMemberDecorate %type_ShadowDepthPass 33 Offset 132 + OpMemberDecorate %type_ShadowDepthPass 34 Offset 136 + OpMemberDecorate %type_ShadowDepthPass 35 Offset 140 + OpMemberDecorate %type_ShadowDepthPass 36 Offset 144 + OpMemberDecorate %type_ShadowDepthPass 37 Offset 148 + OpMemberDecorate %type_ShadowDepthPass 38 Offset 152 + OpMemberDecorate %type_ShadowDepthPass 39 Offset 156 + OpMemberDecorate %type_ShadowDepthPass 40 Offset 160 + OpMemberDecorate %type_ShadowDepthPass 41 Offset 164 + OpMemberDecorate %type_ShadowDepthPass 42 Offset 168 + OpMemberDecorate %type_ShadowDepthPass 43 Offset 172 + OpMemberDecorate %type_ShadowDepthPass 44 Offset 176 + OpMemberDecorate %type_ShadowDepthPass 45 Offset 180 + OpMemberDecorate %type_ShadowDepthPass 46 Offset 184 + OpMemberDecorate %type_ShadowDepthPass 47 Offset 188 + OpMemberDecorate %type_ShadowDepthPass 48 Offset 192 + OpMemberDecorate %type_ShadowDepthPass 49 Offset 196 + OpMemberDecorate %type_ShadowDepthPass 50 Offset 200 + OpMemberDecorate %type_ShadowDepthPass 51 Offset 204 + OpMemberDecorate %type_ShadowDepthPass 52 Offset 208 + OpMemberDecorate %type_ShadowDepthPass 53 Offset 212 + OpMemberDecorate %type_ShadowDepthPass 54 Offset 216 + OpMemberDecorate %type_ShadowDepthPass 55 Offset 220 + OpMemberDecorate %type_ShadowDepthPass 56 Offset 224 + OpMemberDecorate %type_ShadowDepthPass 57 Offset 228 + OpMemberDecorate %type_ShadowDepthPass 58 Offset 232 + OpMemberDecorate %type_ShadowDepthPass 59 Offset 236 + OpMemberDecorate %type_ShadowDepthPass 60 Offset 240 + OpMemberDecorate %type_ShadowDepthPass 61 Offset 244 + OpMemberDecorate %type_ShadowDepthPass 62 Offset 248 + OpMemberDecorate %type_ShadowDepthPass 63 Offset 252 + OpMemberDecorate %type_ShadowDepthPass 64 Offset 256 + OpMemberDecorate %type_ShadowDepthPass 65 Offset 260 + OpMemberDecorate %type_ShadowDepthPass 66 Offset 264 + OpMemberDecorate %type_ShadowDepthPass 67 Offset 268 + OpMemberDecorate %type_ShadowDepthPass 68 Offset 272 + OpMemberDecorate %type_ShadowDepthPass 68 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 68 ColMajor + OpMemberDecorate %type_ShadowDepthPass 69 Offset 336 + OpMemberDecorate %type_ShadowDepthPass 70 Offset 352 + OpMemberDecorate %type_ShadowDepthPass 71 Offset 368 + OpMemberDecorate %type_ShadowDepthPass 72 Offset 384 + OpMemberDecorate %type_ShadowDepthPass 73 Offset 396 + OpMemberDecorate %type_ShadowDepthPass 74 Offset 400 + OpMemberDecorate %type_ShadowDepthPass 75 Offset 412 + OpMemberDecorate %type_ShadowDepthPass 76 Offset 416 + OpMemberDecorate %type_ShadowDepthPass 77 Offset 420 + OpMemberDecorate %type_ShadowDepthPass 78 Offset 424 + OpMemberDecorate %type_ShadowDepthPass 79 Offset 428 + OpMemberDecorate %type_ShadowDepthPass 80 Offset 432 + OpMemberDecorate %type_ShadowDepthPass 81 Offset 436 + OpMemberDecorate %type_ShadowDepthPass 82 Offset 440 + OpMemberDecorate %type_ShadowDepthPass 83 Offset 444 + OpMemberDecorate %type_ShadowDepthPass 84 Offset 448 + OpMemberDecorate %type_ShadowDepthPass 85 Offset 452 + OpMemberDecorate %type_ShadowDepthPass 86 Offset 456 + OpMemberDecorate %type_ShadowDepthPass 87 Offset 460 + OpMemberDecorate %type_ShadowDepthPass 88 Offset 464 + OpMemberDecorate %type_ShadowDepthPass 88 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 88 ColMajor + OpMemberDecorate %type_ShadowDepthPass 89 Offset 528 + OpMemberDecorate %type_ShadowDepthPass 89 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 89 ColMajor + OpMemberDecorate %type_ShadowDepthPass 90 Offset 592 + OpMemberDecorate %type_ShadowDepthPass 91 Offset 608 + OpMemberDecorate %type_ShadowDepthPass 92 Offset 612 + OpMemberDecorate %type_ShadowDepthPass 93 Offset 616 + OpMemberDecorate %type_ShadowDepthPass 94 Offset 620 + OpMemberDecorate %type_ShadowDepthPass 95 Offset 624 + OpMemberDecorate %type_ShadowDepthPass 95 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 95 ColMajor + OpMemberDecorate %type_ShadowDepthPass 96 Offset 1008 + OpMemberDecorate %type_ShadowDepthPass 96 MatrixStride 16 + OpMemberDecorate %type_ShadowDepthPass 96 ColMajor + OpDecorate %type_ShadowDepthPass Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_4 = OpConstant %uint 4 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %float_3 = OpConstant %float 3 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %float_6 = OpConstant %float 6 + %48 = OpConstantComposite %v4float %float_6 %float_6 %float_6 %float_6 + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %int_3 = OpConstant %int 3 + %int_88 = OpConstant %int 88 + %int_89 = OpConstant %int 89 + %int_90 = OpConstant %int 90 + %int_91 = OpConstant %int 91 +%float_9_99999997en07 = OpConstant %float 9.99999997e-07 + %uint_6 = OpConstant %uint 6 +%_arr_mat4v4float_uint_6 = OpTypeArray %mat4v4float %uint_6 + %v3int = OpTypeVector %int 3 +%type_ShadowDepthPass = OpTypeStruct %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %mat4v4float %v4float %v4float %v4float %v3int %int %v3int %float %float %float %float %float %float %float %float %float %float %float %float %int %mat4v4float %mat4v4float %v4float %float %float %float %float %_arr_mat4v4float_uint_6 %_arr_mat4v4float_uint_6 +%_ptr_Uniform_type_ShadowDepthPass = OpTypePointer Uniform %type_ShadowDepthPass + %uint_3 = OpConstant %uint 3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 +%_arr_uint_uint_3 = OpTypeArray %uint %uint_3 +%_ptr_Input__arr_uint_uint_3 = OpTypePointer Input %_arr_uint_uint_3 +%_arr__arr_v4float_uint_3_uint_3 = OpTypeArray %_arr_v4float_uint_3 %uint_3 +%_ptr_Input__arr__arr_v4float_uint_3_uint_3 = OpTypePointer Input %_arr__arr_v4float_uint_3_uint_3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%_ptr_Input__arr_float_uint_3 = OpTypePointer Input %_arr_float_uint_3 +%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3 +%_ptr_Input__arr_v2float_uint_3 = OpTypePointer Input %_arr_v2float_uint_3 +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Input__arr_float_uint_4 = OpTypePointer Input %_arr_float_uint_4 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Input__arr_float_uint_2 = OpTypePointer Input %_arr_float_uint_2 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input_v3float = OpTypePointer Input %v3float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %83 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float + %bool = OpTypeBool +%_ptr_Function_mat4v4float = OpTypePointer Function %mat4v4float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%ShadowDepthPass = OpVariable %_ptr_Uniform_type_ShadowDepthPass Uniform +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_VS_to_DS_Position = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_VS_To_DS_VertexID = OpVariable %_ptr_Input__arr_uint_uint_3 Input +%in_var_PN_POSITION = OpVariable %_ptr_Input__arr__arr_v4float_uint_3_uint_3 Input +%in_var_PN_DisplacementScales = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_PN_TessellationMultiplier = OpVariable %_ptr_Input__arr_float_uint_3 Input +%in_var_PN_WorldDisplacementMultiplier = OpVariable %_ptr_Input__arr_float_uint_3 Input +%in_var_PN_DominantVertex = OpVariable %_ptr_Input__arr_v2float_uint_3 Input +%in_var_PN_DominantVertex1 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_PN_DominantVertex2 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_PN_DominantEdge = OpVariable %_ptr_Input__arr_v2float_uint_3 Input +%in_var_PN_DominantEdge1 = OpVariable %_ptr_Input__arr_v2float_uint_3 Input +%in_var_PN_DominantEdge2 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_PN_DominantEdge3 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_PN_DominantEdge4 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_PN_DominantEdge5 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%gl_TessLevelOuter = OpVariable %_ptr_Input__arr_float_uint_4 Input +%gl_TessLevelInner = OpVariable %_ptr_Input__arr_float_uint_2 Input +%in_var_PN_POSITION9 = OpVariable %_ptr_Input_v4float Input +%gl_TessCoord = OpVariable %_ptr_Input_v3float Input +%out_var_TEXCOORD10_centroid = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD11_centroid = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD6 = OpVariable %_ptr_Output_float Output +%out_var_TEXCOORD7 = OpVariable %_ptr_Output_v3float Output +%gl_Position = OpVariable %_ptr_Output_v4float Output + %89 = OpConstantNull %v4float + %90 = OpUndef %v4float + %MainDomain = OpFunction %void None %83 + %91 = OpLabel + %92 = OpVariable %_ptr_Function_mat4v4float Function + %93 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD10_centroid + %94 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD11_centroid + %95 = OpCompositeExtract %v4float %93 0 + %96 = OpCompositeExtract %v4float %94 0 + %97 = OpCompositeExtract %v4float %93 1 + %98 = OpCompositeExtract %v4float %94 1 + %99 = OpCompositeExtract %v4float %93 2 + %100 = OpCompositeExtract %v4float %94 2 + %101 = OpLoad %_arr__arr_v4float_uint_3_uint_3 %in_var_PN_POSITION + %102 = OpCompositeExtract %_arr_v4float_uint_3 %101 0 + %103 = OpCompositeExtract %_arr_v4float_uint_3 %101 1 + %104 = OpCompositeExtract %_arr_v4float_uint_3 %101 2 + %105 = OpCompositeExtract %v4float %102 0 + %106 = OpCompositeExtract %v4float %102 1 + %107 = OpCompositeExtract %v4float %102 2 + %108 = OpCompositeExtract %v4float %103 0 + %109 = OpCompositeExtract %v4float %103 1 + %110 = OpCompositeExtract %v4float %103 2 + %111 = OpCompositeExtract %v4float %104 0 + %112 = OpCompositeExtract %v4float %104 1 + %113 = OpCompositeExtract %v4float %104 2 + %114 = OpLoad %v4float %in_var_PN_POSITION9 + %115 = OpLoad %v3float %gl_TessCoord + %116 = OpCompositeExtract %float %115 0 + %117 = OpCompositeExtract %float %115 1 + %118 = OpCompositeExtract %float %115 2 + %119 = OpFMul %float %116 %116 + %120 = OpFMul %float %117 %117 + %121 = OpFMul %float %118 %118 + %122 = OpFMul %float %119 %float_3 + %123 = OpFMul %float %120 %float_3 + %124 = OpFMul %float %121 %float_3 + %125 = OpCompositeConstruct %v4float %119 %119 %119 %119 + %126 = OpFMul %v4float %105 %125 + %127 = OpCompositeConstruct %v4float %116 %116 %116 %116 + %128 = OpFMul %v4float %126 %127 + %129 = OpCompositeConstruct %v4float %120 %120 %120 %120 + %130 = OpFMul %v4float %108 %129 + %131 = OpCompositeConstruct %v4float %117 %117 %117 %117 + %132 = OpFMul %v4float %130 %131 + %133 = OpFAdd %v4float %128 %132 + %134 = OpCompositeConstruct %v4float %121 %121 %121 %121 + %135 = OpFMul %v4float %111 %134 + %136 = OpCompositeConstruct %v4float %118 %118 %118 %118 + %137 = OpFMul %v4float %135 %136 + %138 = OpFAdd %v4float %133 %137 + %139 = OpCompositeConstruct %v4float %122 %122 %122 %122 + %140 = OpFMul %v4float %106 %139 + %141 = OpFMul %v4float %140 %131 + %142 = OpFAdd %v4float %138 %141 + %143 = OpCompositeConstruct %v4float %123 %123 %123 %123 + %144 = OpFMul %v4float %107 %143 + %145 = OpFMul %v4float %144 %127 + %146 = OpFAdd %v4float %142 %145 + %147 = OpFMul %v4float %109 %143 + %148 = OpFMul %v4float %147 %136 + %149 = OpFAdd %v4float %146 %148 + %150 = OpCompositeConstruct %v4float %124 %124 %124 %124 + %151 = OpFMul %v4float %110 %150 + %152 = OpFMul %v4float %151 %131 + %153 = OpFAdd %v4float %149 %152 + %154 = OpFMul %v4float %112 %150 + %155 = OpFMul %v4float %154 %127 + %156 = OpFAdd %v4float %153 %155 + %157 = OpFMul %v4float %113 %139 + %158 = OpFMul %v4float %157 %136 + %159 = OpFAdd %v4float %156 %158 + %160 = OpFMul %v4float %114 %48 + %161 = OpFMul %v4float %160 %136 + %162 = OpFMul %v4float %161 %127 + %163 = OpFMul %v4float %162 %131 + %164 = OpFAdd %v4float %159 %163 + %165 = OpVectorShuffle %v3float %95 %95 0 1 2 + %166 = OpCompositeConstruct %v3float %116 %116 %116 + %167 = OpFMul %v3float %165 %166 + %168 = OpVectorShuffle %v3float %97 %97 0 1 2 + %169 = OpCompositeConstruct %v3float %117 %117 %117 + %170 = OpFMul %v3float %168 %169 + %171 = OpFAdd %v3float %167 %170 + %172 = OpFMul %v4float %96 %127 + %173 = OpFMul %v4float %98 %131 + %174 = OpFAdd %v4float %172 %173 + %175 = OpVectorShuffle %v3float %171 %89 0 1 2 + %176 = OpVectorShuffle %v3float %99 %99 0 1 2 + %177 = OpCompositeConstruct %v3float %118 %118 %118 + %178 = OpFMul %v3float %176 %177 + %179 = OpFAdd %v3float %175 %178 + %180 = OpVectorShuffle %v4float %90 %179 4 5 6 3 + %181 = OpFMul %v4float %100 %136 + %182 = OpFAdd %v4float %174 %181 + %183 = OpVectorShuffle %v3float %182 %182 0 1 2 + %184 = OpVectorShuffle %v4float %164 %164 4 5 6 3 + %185 = OpAccessChain %_ptr_Uniform_mat4v4float %ShadowDepthPass %int_88 + %186 = OpLoad %mat4v4float %185 + %187 = OpAccessChain %_ptr_Uniform_mat4v4float %ShadowDepthPass %int_89 + %188 = OpLoad %mat4v4float %187 + OpStore %92 %188 + %189 = OpMatrixTimesVector %v4float %186 %184 + %190 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_91 + %191 = OpLoad %float %190 + %192 = OpFOrdGreaterThan %bool %191 %float_0 + %193 = OpCompositeExtract %float %189 2 + %194 = OpFOrdLessThan %bool %193 %float_0 + %195 = OpLogicalAnd %bool %192 %194 + OpSelectionMerge %196 None + OpBranchConditional %195 %197 %196 + %197 = OpLabel + %198 = OpCompositeInsert %v4float %float_9_99999997en07 %189 2 + %199 = OpCompositeInsert %v4float %float_1 %198 3 + OpBranch %196 + %196 = OpLabel + %200 = OpPhi %v4float %189 %91 %199 %197 + %201 = OpAccessChain %_ptr_Function_float %92 %uint_0 %int_2 + %202 = OpLoad %float %201 + %203 = OpAccessChain %_ptr_Function_float %92 %uint_1 %int_2 + %204 = OpLoad %float %203 + %205 = OpAccessChain %_ptr_Function_float %92 %uint_2 %int_2 + %206 = OpLoad %float %205 + %207 = OpCompositeConstruct %v3float %202 %204 %206 + %208 = OpDot %float %207 %183 + %209 = OpExtInst %float %1 FAbs %208 + %210 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_90 %int_2 + %211 = OpLoad %float %210 + %212 = OpExtInst %float %1 FAbs %209 + %213 = OpFOrdGreaterThan %bool %212 %float_0 + %214 = OpFMul %float %209 %209 + %215 = OpFSub %float %float_1 %214 + %216 = OpExtInst %float %1 FClamp %215 %float_0 %float_1 + %217 = OpExtInst %float %1 Sqrt %216 + %218 = OpFDiv %float %217 %209 + %219 = OpSelect %float %213 %218 %211 + %220 = OpExtInst %float %1 FClamp %219 %float_0 %211 + %221 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_90 %int_1 + %222 = OpLoad %float %221 + %223 = OpFMul %float %222 %220 + %224 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_90 %int_0 + %225 = OpLoad %float %224 + %226 = OpFAdd %float %223 %225 + %227 = OpAccessChain %_ptr_Uniform_float %ShadowDepthPass %int_90 %int_3 + %228 = OpLoad %float %227 + %229 = OpCompositeExtract %float %200 2 + %230 = OpFMul %float %229 %228 + %231 = OpFAdd %float %230 %226 + %232 = OpCompositeExtract %float %200 3 + %233 = OpFMul %float %231 %232 + %234 = OpCompositeInsert %v4float %233 %200 2 + %235 = OpVectorShuffle %v3float %164 %89 0 1 2 + OpStore %out_var_TEXCOORD10_centroid %180 + OpStore %out_var_TEXCOORD11_centroid %182 + OpStore %out_var_TEXCOORD6 %float_0 + OpStore %out_var_TEXCOORD7 %235 + OpStore %gl_Position %234 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese b/third_party/spirv-cross/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese new file mode 100644 index 0000000..778e93d --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/tese/ds-texcoord-array.asm.tese @@ -0,0 +1,715 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 183 +; Schema: 0 + OpCapability Tessellation + OpCapability SampledBuffer + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %MainDomain "main" %gl_TessLevelOuter %gl_TessLevelInner %in_var_TEXCOORD10_centroid %in_var_TEXCOORD11_centroid %in_var_TEXCOORD0 %in_var_COLOR1 %in_var_COLOR2 %in_var_VS_To_DS_Position %in_var_TEXCOORD7 %in_var_Flat_DisplacementScales %in_var_Flat_TessellationMultiplier %in_var_Flat_WorldDisplacementMultiplier %gl_TessCoord %out_var_TEXCOORD10_centroid %out_var_TEXCOORD11_centroid %out_var_TEXCOORD0 %out_var_COLOR1 %out_var_COLOR2 %out_var_TEXCOORD6 %out_var_TEXCOORD7 %gl_Position + OpExecutionMode %MainDomain Triangles + OpExecutionMode %MainDomain SpacingFractionalOdd + OpExecutionMode %MainDomain VertexOrderCw + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_ClipToWorld" + OpMemberName %type_View 3 "View_TranslatedWorldToView" + OpMemberName %type_View 4 "View_ViewToTranslatedWorld" + OpMemberName %type_View 5 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 6 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 7 "View_ViewToClip" + OpMemberName %type_View 8 "View_ViewToClipNoAA" + OpMemberName %type_View 9 "View_ClipToView" + OpMemberName %type_View 10 "View_ClipToTranslatedWorld" + OpMemberName %type_View 11 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 12 "View_ScreenToWorld" + OpMemberName %type_View 13 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 14 "View_ViewForward" + OpMemberName %type_View 15 "PrePadding_View_908" + OpMemberName %type_View 16 "View_ViewUp" + OpMemberName %type_View 17 "PrePadding_View_924" + OpMemberName %type_View 18 "View_ViewRight" + OpMemberName %type_View 19 "PrePadding_View_940" + OpMemberName %type_View 20 "View_HMDViewNoRollUp" + OpMemberName %type_View 21 "PrePadding_View_956" + OpMemberName %type_View 22 "View_HMDViewNoRollRight" + OpMemberName %type_View 23 "PrePadding_View_972" + OpMemberName %type_View 24 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 25 "View_ScreenPositionScaleBias" + OpMemberName %type_View 26 "View_WorldCameraOrigin" + OpMemberName %type_View 27 "PrePadding_View_1020" + OpMemberName %type_View 28 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 29 "PrePadding_View_1036" + OpMemberName %type_View 30 "View_WorldViewOrigin" + OpMemberName %type_View 31 "PrePadding_View_1052" + OpMemberName %type_View 32 "View_PreViewTranslation" + OpMemberName %type_View 33 "PrePadding_View_1068" + OpMemberName %type_View 34 "View_PrevProjection" + OpMemberName %type_View 35 "View_PrevViewProj" + OpMemberName %type_View 36 "View_PrevViewRotationProj" + OpMemberName %type_View 37 "View_PrevViewToClip" + OpMemberName %type_View 38 "View_PrevClipToView" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 40 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 41 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 42 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 43 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 44 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 45 "PrePadding_View_1724" + OpMemberName %type_View 46 "View_PrevWorldViewOrigin" + OpMemberName %type_View 47 "PrePadding_View_1740" + OpMemberName %type_View 48 "View_PrevPreViewTranslation" + OpMemberName %type_View 49 "PrePadding_View_1756" + OpMemberName %type_View 50 "View_PrevInvViewProj" + OpMemberName %type_View 51 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 52 "View_ClipToPrevClip" + OpMemberName %type_View 53 "View_TemporalAAJitter" + OpMemberName %type_View 54 "View_GlobalClippingPlane" + OpMemberName %type_View 55 "View_FieldOfViewWideAngles" + OpMemberName %type_View 56 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 57 "View_ViewRectMin" + OpMemberName %type_View 58 "View_ViewSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferSizeAndInvSize" + OpMemberName %type_View 60 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 61 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 62 "View_PreExposure" + OpMemberName %type_View 63 "View_OneOverPreExposure" + OpMemberName %type_View 64 "PrePadding_View_2076" + OpMemberName %type_View 65 "View_DiffuseOverrideParameter" + OpMemberName %type_View 66 "View_SpecularOverrideParameter" + OpMemberName %type_View 67 "View_NormalOverrideParameter" + OpMemberName %type_View 68 "View_RoughnessOverrideParameter" + OpMemberName %type_View 69 "View_PrevFrameGameTime" + OpMemberName %type_View 70 "View_PrevFrameRealTime" + OpMemberName %type_View 71 "View_OutOfBoundsMask" + OpMemberName %type_View 72 "PrePadding_View_2148" + OpMemberName %type_View 73 "PrePadding_View_2152" + OpMemberName %type_View 74 "PrePadding_View_2156" + OpMemberName %type_View 75 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 76 "View_CullingSign" + OpMemberName %type_View 77 "View_NearPlane" + OpMemberName %type_View 78 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 79 "View_GameTime" + OpMemberName %type_View 80 "View_RealTime" + OpMemberName %type_View 81 "View_DeltaTime" + OpMemberName %type_View 82 "View_MaterialTextureMipBias" + OpMemberName %type_View 83 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 84 "View_Random" + OpMemberName %type_View 85 "View_FrameNumber" + OpMemberName %type_View 86 "View_StateFrameIndexMod8" + OpMemberName %type_View 87 "View_StateFrameIndex" + OpMemberName %type_View 88 "View_CameraCut" + OpMemberName %type_View 89 "View_UnlitViewmodeMask" + OpMemberName %type_View 90 "PrePadding_View_2228" + OpMemberName %type_View 91 "PrePadding_View_2232" + OpMemberName %type_View 92 "PrePadding_View_2236" + OpMemberName %type_View 93 "View_DirectionalLightColor" + OpMemberName %type_View 94 "View_DirectionalLightDirection" + OpMemberName %type_View 95 "PrePadding_View_2268" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 97 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 98 "View_TemporalAAParams" + OpMemberName %type_View 99 "View_CircleDOFParams" + OpMemberName %type_View 100 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 101 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 102 "View_DepthOfFieldScale" + OpMemberName %type_View 103 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 104 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 105 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 106 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 107 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 108 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 109 "View_GeneralPurposeTweak" + OpMemberName %type_View 110 "View_DemosaicVposOffset" + OpMemberName %type_View 111 "PrePadding_View_2412" + OpMemberName %type_View 112 "View_IndirectLightingColorScale" + OpMemberName %type_View 113 "View_HDR32bppEncodingMode" + OpMemberName %type_View 114 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 115 "View_AtmosphericFogSunPower" + OpMemberName %type_View 116 "View_AtmosphericFogPower" + OpMemberName %type_View 117 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 118 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 119 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 120 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 121 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 122 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 123 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 124 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 125 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 126 "View_AtmosphericFogSunDiscHalfApexAngleRadian" + OpMemberName %type_View 127 "PrePadding_View_2492" + OpMemberName %type_View 128 "View_AtmosphericFogSunDiscLuminance" + OpMemberName %type_View 129 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 130 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 131 "PrePadding_View_2520" + OpMemberName %type_View 132 "PrePadding_View_2524" + OpMemberName %type_View 133 "View_AtmosphericFogSunColor" + OpMemberName %type_View 134 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 135 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 136 "View_AmbientCubemapTint" + OpMemberName %type_View 137 "View_AmbientCubemapIntensity" + OpMemberName %type_View 138 "View_SkyLightParameters" + OpMemberName %type_View 139 "PrePadding_View_2584" + OpMemberName %type_View 140 "PrePadding_View_2588" + OpMemberName %type_View 141 "View_SkyLightColor" + OpMemberName %type_View 142 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 143 "View_MobilePreviewMode" + OpMemberName %type_View 144 "View_HMDEyePaddingOffset" + OpMemberName %type_View 145 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 146 "View_ShowDecalsMask" + OpMemberName %type_View 147 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 148 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 149 "PrePadding_View_2744" + OpMemberName %type_View 150 "PrePadding_View_2748" + OpMemberName %type_View 151 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 152 "View_StereoPassIndex" + OpMemberName %type_View 153 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 154 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 155 "View_GlobalVolumeDimension" + OpMemberName %type_View 156 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 157 "View_MaxGlobalDistance" + OpMemberName %type_View 158 "PrePadding_View_2908" + OpMemberName %type_View 159 "View_CursorPosition" + OpMemberName %type_View 160 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 161 "PrePadding_View_2924" + OpMemberName %type_View 162 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 163 "PrePadding_View_2940" + OpMemberName %type_View 164 "View_VolumetricFogGridZParams" + OpMemberName %type_View 165 "PrePadding_View_2956" + OpMemberName %type_View 166 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 167 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 168 "PrePadding_View_2972" + OpMemberName %type_View 169 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 170 "PrePadding_View_2988" + OpMemberName %type_View 171 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 172 "PrePadding_View_3004" + OpMemberName %type_View 173 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 174 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 175 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 176 "View_StereoIPD" + OpMemberName %type_View 177 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 178 "View_EyeToPixelSpreadAngle" + OpMemberName %type_View 179 "PrePadding_View_3048" + OpMemberName %type_View 180 "PrePadding_View_3052" + OpMemberName %type_View 181 "View_WorldToVirtualTexture" + OpMemberName %type_View 182 "View_VirtualTextureParams" + OpMemberName %type_View 183 "View_XRPassthroughCameraUVs" + OpName %View "View" + OpName %in_var_TEXCOORD10_centroid "in.var.TEXCOORD10_centroid" + OpName %in_var_TEXCOORD11_centroid "in.var.TEXCOORD11_centroid" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %in_var_COLOR1 "in.var.COLOR1" + OpName %in_var_COLOR2 "in.var.COLOR2" + OpName %in_var_VS_To_DS_Position "in.var.VS_To_DS_Position" + OpName %in_var_TEXCOORD7 "in.var.TEXCOORD7" + OpName %in_var_Flat_DisplacementScales "in.var.Flat_DisplacementScales" + OpName %in_var_Flat_TessellationMultiplier "in.var.Flat_TessellationMultiplier" + OpName %in_var_Flat_WorldDisplacementMultiplier "in.var.Flat_WorldDisplacementMultiplier" + OpName %out_var_TEXCOORD10_centroid "out.var.TEXCOORD10_centroid" + OpName %out_var_TEXCOORD11_centroid "out.var.TEXCOORD11_centroid" + OpName %out_var_TEXCOORD0 "out.var.TEXCOORD0" + OpName %out_var_COLOR1 "out.var.COLOR1" + OpName %out_var_COLOR2 "out.var.COLOR2" + OpName %out_var_TEXCOORD6 "out.var.TEXCOORD6" + OpName %out_var_TEXCOORD7 "out.var.TEXCOORD7" + OpName %MainDomain "MainDomain" + OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter + OpDecorateString %gl_TessLevelOuter UserSemantic "SV_TessFactor" + OpDecorate %gl_TessLevelOuter Patch + OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner + OpDecorateString %gl_TessLevelInner UserSemantic "SV_InsideTessFactor" + OpDecorate %gl_TessLevelInner Patch + OpDecorateString %in_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %in_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %in_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %in_var_COLOR1 UserSemantic "COLOR1" + OpDecorateString %in_var_COLOR2 UserSemantic "COLOR2" + OpDecorateString %in_var_VS_To_DS_Position UserSemantic "VS_To_DS_Position" + OpDecorateString %in_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorateString %in_var_Flat_DisplacementScales UserSemantic "Flat_DisplacementScales" + OpDecorateString %in_var_Flat_TessellationMultiplier UserSemantic "Flat_TessellationMultiplier" + OpDecorateString %in_var_Flat_WorldDisplacementMultiplier UserSemantic "Flat_WorldDisplacementMultiplier" + OpDecorate %gl_TessCoord BuiltIn TessCoord + OpDecorateString %gl_TessCoord UserSemantic "SV_DomainLocation" + OpDecorate %gl_TessCoord Patch + OpDecorateString %out_var_TEXCOORD10_centroid UserSemantic "TEXCOORD10_centroid" + OpDecorateString %out_var_TEXCOORD11_centroid UserSemantic "TEXCOORD11_centroid" + OpDecorateString %out_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %out_var_COLOR1 UserSemantic "COLOR1" + OpDecorateString %out_var_COLOR2 UserSemantic "COLOR2" + OpDecorateString %out_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorateString %out_var_TEXCOORD7 UserSemantic "TEXCOORD7" + OpDecorate %gl_Position BuiltIn Position + OpDecorateString %gl_Position UserSemantic "SV_POSITION" + OpDecorate %in_var_COLOR1 Location 0 + OpDecorate %in_var_COLOR2 Location 1 + OpDecorate %in_var_Flat_DisplacementScales Location 2 + OpDecorate %in_var_Flat_TessellationMultiplier Location 3 + OpDecorate %in_var_Flat_WorldDisplacementMultiplier Location 4 + OpDecorate %in_var_TEXCOORD0 Location 5 + OpDecorate %in_var_TEXCOORD10_centroid Location 6 + OpDecorate %in_var_TEXCOORD11_centroid Location 7 + OpDecorate %in_var_TEXCOORD7 Location 8 + OpDecorate %in_var_VS_To_DS_Position Location 9 + OpDecorate %out_var_TEXCOORD10_centroid Location 0 + OpDecorate %out_var_TEXCOORD11_centroid Location 1 + OpDecorate %out_var_TEXCOORD0 Location 2 + OpDecorate %out_var_COLOR1 Location 3 + OpDecorate %out_var_COLOR2 Location 4 + OpDecorate %out_var_TEXCOORD6 Location 5 + OpDecorate %out_var_TEXCOORD7 Location 6 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 13 MatrixStride 16 + OpMemberDecorate %type_View 13 ColMajor + OpMemberDecorate %type_View 14 Offset 896 + OpMemberDecorate %type_View 15 Offset 908 + OpMemberDecorate %type_View 16 Offset 912 + OpMemberDecorate %type_View 17 Offset 924 + OpMemberDecorate %type_View 18 Offset 928 + OpMemberDecorate %type_View 19 Offset 940 + OpMemberDecorate %type_View 20 Offset 944 + OpMemberDecorate %type_View 21 Offset 956 + OpMemberDecorate %type_View 22 Offset 960 + OpMemberDecorate %type_View 23 Offset 972 + OpMemberDecorate %type_View 24 Offset 976 + OpMemberDecorate %type_View 25 Offset 992 + OpMemberDecorate %type_View 26 Offset 1008 + OpMemberDecorate %type_View 27 Offset 1020 + OpMemberDecorate %type_View 28 Offset 1024 + OpMemberDecorate %type_View 29 Offset 1036 + OpMemberDecorate %type_View 30 Offset 1040 + OpMemberDecorate %type_View 31 Offset 1052 + OpMemberDecorate %type_View 32 Offset 1056 + OpMemberDecorate %type_View 33 Offset 1068 + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 43 MatrixStride 16 + OpMemberDecorate %type_View 43 ColMajor + OpMemberDecorate %type_View 44 Offset 1712 + OpMemberDecorate %type_View 45 Offset 1724 + OpMemberDecorate %type_View 46 Offset 1728 + OpMemberDecorate %type_View 47 Offset 1740 + OpMemberDecorate %type_View 48 Offset 1744 + OpMemberDecorate %type_View 49 Offset 1756 + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 52 MatrixStride 16 + OpMemberDecorate %type_View 52 ColMajor + OpMemberDecorate %type_View 53 Offset 1952 + OpMemberDecorate %type_View 54 Offset 1968 + OpMemberDecorate %type_View 55 Offset 1984 + OpMemberDecorate %type_View 56 Offset 1992 + OpMemberDecorate %type_View 57 Offset 2000 + OpMemberDecorate %type_View 58 Offset 2016 + OpMemberDecorate %type_View 59 Offset 2032 + OpMemberDecorate %type_View 60 Offset 2048 + OpMemberDecorate %type_View 61 Offset 2064 + OpMemberDecorate %type_View 62 Offset 2068 + OpMemberDecorate %type_View 63 Offset 2072 + OpMemberDecorate %type_View 64 Offset 2076 + OpMemberDecorate %type_View 65 Offset 2080 + OpMemberDecorate %type_View 66 Offset 2096 + OpMemberDecorate %type_View 67 Offset 2112 + OpMemberDecorate %type_View 68 Offset 2128 + OpMemberDecorate %type_View 69 Offset 2136 + OpMemberDecorate %type_View 70 Offset 2140 + OpMemberDecorate %type_View 71 Offset 2144 + OpMemberDecorate %type_View 72 Offset 2148 + OpMemberDecorate %type_View 73 Offset 2152 + OpMemberDecorate %type_View 74 Offset 2156 + OpMemberDecorate %type_View 75 Offset 2160 + OpMemberDecorate %type_View 76 Offset 2172 + OpMemberDecorate %type_View 77 Offset 2176 + OpMemberDecorate %type_View 78 Offset 2180 + OpMemberDecorate %type_View 79 Offset 2184 + OpMemberDecorate %type_View 80 Offset 2188 + OpMemberDecorate %type_View 81 Offset 2192 + OpMemberDecorate %type_View 82 Offset 2196 + OpMemberDecorate %type_View 83 Offset 2200 + OpMemberDecorate %type_View 84 Offset 2204 + OpMemberDecorate %type_View 85 Offset 2208 + OpMemberDecorate %type_View 86 Offset 2212 + OpMemberDecorate %type_View 87 Offset 2216 + OpMemberDecorate %type_View 88 Offset 2220 + OpMemberDecorate %type_View 89 Offset 2224 + OpMemberDecorate %type_View 90 Offset 2228 + OpMemberDecorate %type_View 91 Offset 2232 + OpMemberDecorate %type_View 92 Offset 2236 + OpMemberDecorate %type_View 93 Offset 2240 + OpMemberDecorate %type_View 94 Offset 2256 + OpMemberDecorate %type_View 95 Offset 2268 + OpMemberDecorate %type_View 96 Offset 2272 + OpMemberDecorate %type_View 97 Offset 2304 + OpMemberDecorate %type_View 98 Offset 2336 + OpMemberDecorate %type_View 99 Offset 2352 + OpMemberDecorate %type_View 100 Offset 2368 + OpMemberDecorate %type_View 101 Offset 2372 + OpMemberDecorate %type_View 102 Offset 2376 + OpMemberDecorate %type_View 103 Offset 2380 + OpMemberDecorate %type_View 104 Offset 2384 + OpMemberDecorate %type_View 105 Offset 2388 + OpMemberDecorate %type_View 106 Offset 2392 + OpMemberDecorate %type_View 107 Offset 2396 + OpMemberDecorate %type_View 108 Offset 2400 + OpMemberDecorate %type_View 109 Offset 2404 + OpMemberDecorate %type_View 110 Offset 2408 + OpMemberDecorate %type_View 111 Offset 2412 + OpMemberDecorate %type_View 112 Offset 2416 + OpMemberDecorate %type_View 113 Offset 2428 + OpMemberDecorate %type_View 114 Offset 2432 + OpMemberDecorate %type_View 115 Offset 2444 + OpMemberDecorate %type_View 116 Offset 2448 + OpMemberDecorate %type_View 117 Offset 2452 + OpMemberDecorate %type_View 118 Offset 2456 + OpMemberDecorate %type_View 119 Offset 2460 + OpMemberDecorate %type_View 120 Offset 2464 + OpMemberDecorate %type_View 121 Offset 2468 + OpMemberDecorate %type_View 122 Offset 2472 + OpMemberDecorate %type_View 123 Offset 2476 + OpMemberDecorate %type_View 124 Offset 2480 + OpMemberDecorate %type_View 125 Offset 2484 + OpMemberDecorate %type_View 126 Offset 2488 + OpMemberDecorate %type_View 127 Offset 2492 + OpMemberDecorate %type_View 128 Offset 2496 + OpMemberDecorate %type_View 129 Offset 2512 + OpMemberDecorate %type_View 130 Offset 2516 + OpMemberDecorate %type_View 131 Offset 2520 + OpMemberDecorate %type_View 132 Offset 2524 + OpMemberDecorate %type_View 133 Offset 2528 + OpMemberDecorate %type_View 134 Offset 2544 + OpMemberDecorate %type_View 135 Offset 2556 + OpMemberDecorate %type_View 136 Offset 2560 + OpMemberDecorate %type_View 137 Offset 2576 + OpMemberDecorate %type_View 138 Offset 2580 + OpMemberDecorate %type_View 139 Offset 2584 + OpMemberDecorate %type_View 140 Offset 2588 + OpMemberDecorate %type_View 141 Offset 2592 + OpMemberDecorate %type_View 142 Offset 2608 + OpMemberDecorate %type_View 143 Offset 2720 + OpMemberDecorate %type_View 144 Offset 2724 + OpMemberDecorate %type_View 145 Offset 2728 + OpMemberDecorate %type_View 146 Offset 2732 + OpMemberDecorate %type_View 147 Offset 2736 + OpMemberDecorate %type_View 148 Offset 2740 + OpMemberDecorate %type_View 149 Offset 2744 + OpMemberDecorate %type_View 150 Offset 2748 + OpMemberDecorate %type_View 151 Offset 2752 + OpMemberDecorate %type_View 152 Offset 2764 + OpMemberDecorate %type_View 153 Offset 2768 + OpMemberDecorate %type_View 154 Offset 2832 + OpMemberDecorate %type_View 155 Offset 2896 + OpMemberDecorate %type_View 156 Offset 2900 + OpMemberDecorate %type_View 157 Offset 2904 + OpMemberDecorate %type_View 158 Offset 2908 + OpMemberDecorate %type_View 159 Offset 2912 + OpMemberDecorate %type_View 160 Offset 2920 + OpMemberDecorate %type_View 161 Offset 2924 + OpMemberDecorate %type_View 162 Offset 2928 + OpMemberDecorate %type_View 163 Offset 2940 + OpMemberDecorate %type_View 164 Offset 2944 + OpMemberDecorate %type_View 165 Offset 2956 + OpMemberDecorate %type_View 166 Offset 2960 + OpMemberDecorate %type_View 167 Offset 2968 + OpMemberDecorate %type_View 168 Offset 2972 + OpMemberDecorate %type_View 169 Offset 2976 + OpMemberDecorate %type_View 170 Offset 2988 + OpMemberDecorate %type_View 171 Offset 2992 + OpMemberDecorate %type_View 172 Offset 3004 + OpMemberDecorate %type_View 173 Offset 3008 + OpMemberDecorate %type_View 174 Offset 3020 + OpMemberDecorate %type_View 175 Offset 3024 + OpMemberDecorate %type_View 176 Offset 3036 + OpMemberDecorate %type_View 177 Offset 3040 + OpMemberDecorate %type_View 178 Offset 3044 + OpMemberDecorate %type_View 179 Offset 3048 + OpMemberDecorate %type_View 180 Offset 3052 + OpMemberDecorate %type_View 181 Offset 3056 + OpMemberDecorate %type_View 181 MatrixStride 16 + OpMemberDecorate %type_View 181 ColMajor + OpMemberDecorate %type_View 182 Offset 3120 + OpMemberDecorate %type_View 183 Offset 3136 + OpDecorate %type_View Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %v2int = OpTypeVector %int 2 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint_1 = OpConstant %uint 1 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %float %float %v4float %uint %uint %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v2int %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float %float %float %mat4v4float %v4float %_arr_v4float_uint_2 +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%_arr_float_uint_4 = OpTypeArray %float %uint_4 +%_ptr_Input__arr_float_uint_4 = OpTypePointer Input %_arr_float_uint_4 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%_ptr_Input__arr_float_uint_2 = OpTypePointer Input %_arr_float_uint_2 + %uint_3 = OpConstant %uint 3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%_arr__arr_v4float_uint_1_uint_3 = OpTypeArray %_arr_v4float_uint_1 %uint_3 +%_ptr_Input__arr__arr_v4float_uint_1_uint_3 = OpTypePointer Input %_arr__arr_v4float_uint_1_uint_3 +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 +%_arr_float_uint_3 = OpTypeArray %float %uint_3 +%_ptr_Input__arr_float_uint_3 = OpTypePointer Input %_arr_float_uint_3 +%_ptr_Input_v3float = OpTypePointer Input %v3float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Output__arr_v4float_uint_1 = OpTypePointer Output %_arr_v4float_uint_1 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %void = OpTypeVoid + %63 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %bool = OpTypeBool +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float + %View = OpVariable %_ptr_Uniform_type_View Uniform +%gl_TessLevelOuter = OpVariable %_ptr_Input__arr_float_uint_4 Input +%gl_TessLevelInner = OpVariable %_ptr_Input__arr_float_uint_2 Input +%in_var_TEXCOORD10_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD11_centroid = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD0 = OpVariable %_ptr_Input__arr__arr_v4float_uint_1_uint_3 Input +%in_var_COLOR1 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_COLOR2 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_VS_To_DS_Position = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%in_var_TEXCOORD7 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_Flat_DisplacementScales = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%in_var_Flat_TessellationMultiplier = OpVariable %_ptr_Input__arr_float_uint_3 Input +%in_var_Flat_WorldDisplacementMultiplier = OpVariable %_ptr_Input__arr_float_uint_3 Input +%gl_TessCoord = OpVariable %_ptr_Input_v3float Input +%out_var_TEXCOORD10_centroid = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD11_centroid = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD0 = OpVariable %_ptr_Output__arr_v4float_uint_1 Output +%out_var_COLOR1 = OpVariable %_ptr_Output_v4float Output +%out_var_COLOR2 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD6 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD7 = OpVariable %_ptr_Output_v3float Output +%gl_Position = OpVariable %_ptr_Output_v4float Output +%_ptr_Function__arr_v4float_uint_1 = OpTypePointer Function %_arr_v4float_uint_1 + %68 = OpUndef %v4float + %69 = OpConstantNull %v4float + %MainDomain = OpFunction %void None %63 + %70 = OpLabel + %71 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function + %72 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function + %73 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function + %74 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function + %75 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function + %76 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function + %77 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD10_centroid + %78 = OpLoad %_arr_v4float_uint_3 %in_var_TEXCOORD11_centroid + %79 = OpLoad %_arr__arr_v4float_uint_1_uint_3 %in_var_TEXCOORD0 + %80 = OpLoad %_arr_v4float_uint_3 %in_var_COLOR1 + %81 = OpLoad %_arr_v4float_uint_3 %in_var_COLOR2 + %82 = OpCompositeExtract %v4float %77 0 + %83 = OpCompositeExtract %v4float %78 0 + %84 = OpCompositeExtract %_arr_v4float_uint_1 %79 0 + %85 = OpCompositeExtract %v4float %80 0 + %86 = OpCompositeExtract %v4float %81 0 + %87 = OpCompositeExtract %v4float %77 1 + %88 = OpCompositeExtract %v4float %78 1 + %89 = OpCompositeExtract %_arr_v4float_uint_1 %79 1 + %90 = OpCompositeExtract %v4float %80 1 + %91 = OpCompositeExtract %v4float %81 1 + %92 = OpCompositeExtract %v4float %77 2 + %93 = OpCompositeExtract %v4float %78 2 + %94 = OpCompositeExtract %_arr_v4float_uint_1 %79 2 + %95 = OpCompositeExtract %v4float %80 2 + %96 = OpCompositeExtract %v4float %81 2 + %97 = OpLoad %_arr_v4float_uint_3 %in_var_VS_To_DS_Position + %98 = OpLoad %_arr_v3float_uint_3 %in_var_TEXCOORD7 + %99 = OpCompositeExtract %v4float %97 0 + %100 = OpCompositeExtract %v3float %98 0 + %101 = OpCompositeExtract %v4float %97 1 + %102 = OpCompositeExtract %v3float %98 1 + %103 = OpCompositeExtract %v4float %97 2 + %104 = OpCompositeExtract %v3float %98 2 + %105 = OpLoad %v3float %gl_TessCoord + %106 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_0 + %107 = OpLoad %mat4v4float %106 + %108 = OpCompositeExtract %float %105 0 + %109 = OpCompositeExtract %float %105 1 + %110 = OpCompositeExtract %float %105 2 + %111 = OpCompositeConstruct %v4float %108 %108 %108 %108 + %112 = OpFMul %v4float %99 %111 + %113 = OpCompositeConstruct %v4float %109 %109 %109 %109 + %114 = OpFMul %v4float %101 %113 + %115 = OpFAdd %v4float %112 %114 + %116 = OpCompositeConstruct %v4float %110 %110 %110 %110 + %117 = OpFMul %v4float %103 %116 + %118 = OpFAdd %v4float %115 %117 + OpStore %72 %84 + OpStore %71 %89 + %119 = OpVectorShuffle %v3float %82 %82 0 1 2 + %120 = OpCompositeConstruct %v3float %108 %108 %108 + %121 = OpFMul %v3float %119 %120 + %122 = OpVectorShuffle %v3float %87 %87 0 1 2 + %123 = OpCompositeConstruct %v3float %109 %109 %109 + %124 = OpFMul %v3float %122 %123 + %125 = OpFAdd %v3float %121 %124 + %126 = OpFMul %v4float %83 %111 + %127 = OpFMul %v4float %88 %113 + %128 = OpFAdd %v4float %126 %127 + %129 = OpFMul %v4float %85 %111 + %130 = OpFMul %v4float %90 %113 + %131 = OpFAdd %v4float %129 %130 + OpBranch %132 + %132 = OpLabel + %133 = OpPhi %int %int_0 %70 %134 %135 + %136 = OpSLessThan %bool %133 %int_1 + OpLoopMerge %137 %135 None + OpBranchConditional %136 %135 %137 + %135 = OpLabel + %138 = OpAccessChain %_ptr_Function_v4float %72 %133 + %139 = OpLoad %v4float %138 + %140 = OpFMul %v4float %139 %111 + %141 = OpAccessChain %_ptr_Function_v4float %71 %133 + %142 = OpLoad %v4float %141 + %143 = OpFMul %v4float %142 %113 + %144 = OpFAdd %v4float %140 %143 + %145 = OpAccessChain %_ptr_Function_v4float %73 %133 + OpStore %145 %144 + %134 = OpIAdd %int %133 %int_1 + OpBranch %132 + %137 = OpLabel + %146 = OpFMul %v4float %86 %111 + %147 = OpFMul %v4float %91 %113 + %148 = OpFAdd %v4float %146 %147 + %149 = OpLoad %_arr_v4float_uint_1 %73 + %150 = OpFMul %v3float %100 %120 + %151 = OpFMul %v3float %102 %123 + %152 = OpFAdd %v3float %150 %151 + OpStore %75 %149 + OpStore %74 %94 + %153 = OpVectorShuffle %v3float %125 %69 0 1 2 + %154 = OpVectorShuffle %v3float %92 %92 0 1 2 + %155 = OpCompositeConstruct %v3float %110 %110 %110 + %156 = OpFMul %v3float %154 %155 + %157 = OpFAdd %v3float %153 %156 + %158 = OpVectorShuffle %v4float %68 %157 4 5 6 3 + %159 = OpFMul %v4float %93 %116 + %160 = OpFAdd %v4float %128 %159 + %161 = OpFMul %v4float %95 %116 + %162 = OpFAdd %v4float %131 %161 + OpBranch %163 + %163 = OpLabel + %164 = OpPhi %int %int_0 %137 %165 %166 + %167 = OpSLessThan %bool %164 %int_1 + OpLoopMerge %168 %166 None + OpBranchConditional %167 %166 %168 + %166 = OpLabel + %169 = OpAccessChain %_ptr_Function_v4float %75 %164 + %170 = OpLoad %v4float %169 + %171 = OpAccessChain %_ptr_Function_v4float %74 %164 + %172 = OpLoad %v4float %171 + %173 = OpFMul %v4float %172 %116 + %174 = OpFAdd %v4float %170 %173 + %175 = OpAccessChain %_ptr_Function_v4float %76 %164 + OpStore %175 %174 + %165 = OpIAdd %int %164 %int_1 + OpBranch %163 + %168 = OpLabel + %176 = OpFMul %v4float %96 %116 + %177 = OpFAdd %v4float %148 %176 + %178 = OpLoad %_arr_v4float_uint_1 %76 + %179 = OpFMul %v3float %104 %155 + %180 = OpFAdd %v3float %152 %179 + %181 = OpVectorShuffle %v4float %118 %118 4 5 6 3 + %182 = OpMatrixTimesVector %v4float %107 %181 + OpStore %out_var_TEXCOORD10_centroid %158 + OpStore %out_var_TEXCOORD11_centroid %160 + OpStore %out_var_TEXCOORD0 %178 + OpStore %out_var_COLOR1 %162 + OpStore %out_var_COLOR2 %177 + OpStore %out_var_TEXCOORD6 %181 + OpStore %out_var_TEXCOORD7 %180 + OpStore %gl_Position %182 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/vert/array-missing-copies.asm.vert b/third_party/spirv-cross/shaders-ue4/asm/vert/array-missing-copies.asm.vert new file mode 100644 index 0000000..23dc727 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/vert/array-missing-copies.asm.vert @@ -0,0 +1,1131 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 487 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %Main "main" %in_var_ATTRIBUTE0 %in_var_ATTRIBUTE1 %out_var_TEXCOORD0 %out_var_TEXCOORD1 %out_var_TEXCOORD2 %out_var_TEXCOORD3 %out_var_TEXCOORD8 %gl_Position + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_TranslatedWorldToView" + OpMemberName %type_View 3 "View_ViewToTranslatedWorld" + OpMemberName %type_View 4 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 5 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 6 "View_ViewToClip" + OpMemberName %type_View 7 "View_ViewToClipNoAA" + OpMemberName %type_View 8 "View_ClipToView" + OpMemberName %type_View 9 "View_ClipToTranslatedWorld" + OpMemberName %type_View 10 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 11 "View_ScreenToWorld" + OpMemberName %type_View 12 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 13 "View_ViewForward" + OpMemberName %type_View 14 "PrePadding_View_844" + OpMemberName %type_View 15 "View_ViewUp" + OpMemberName %type_View 16 "PrePadding_View_860" + OpMemberName %type_View 17 "View_ViewRight" + OpMemberName %type_View 18 "PrePadding_View_876" + OpMemberName %type_View 19 "View_HMDViewNoRollUp" + OpMemberName %type_View 20 "PrePadding_View_892" + OpMemberName %type_View 21 "View_HMDViewNoRollRight" + OpMemberName %type_View 22 "PrePadding_View_908" + OpMemberName %type_View 23 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 24 "View_ScreenPositionScaleBias" + OpMemberName %type_View 25 "View_WorldCameraOrigin" + OpMemberName %type_View 26 "PrePadding_View_956" + OpMemberName %type_View 27 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 28 "PrePadding_View_972" + OpMemberName %type_View 29 "View_WorldViewOrigin" + OpMemberName %type_View 30 "PrePadding_View_988" + OpMemberName %type_View 31 "View_PreViewTranslation" + OpMemberName %type_View 32 "PrePadding_View_1004" + OpMemberName %type_View 33 "View_PrevProjection" + OpMemberName %type_View 34 "View_PrevViewProj" + OpMemberName %type_View 35 "View_PrevViewRotationProj" + OpMemberName %type_View 36 "View_PrevViewToClip" + OpMemberName %type_View 37 "View_PrevClipToView" + OpMemberName %type_View 38 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 40 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 41 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 42 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 43 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 44 "PrePadding_View_1660" + OpMemberName %type_View 45 "View_PrevWorldViewOrigin" + OpMemberName %type_View 46 "PrePadding_View_1676" + OpMemberName %type_View 47 "View_PrevPreViewTranslation" + OpMemberName %type_View 48 "PrePadding_View_1692" + OpMemberName %type_View 49 "View_PrevInvViewProj" + OpMemberName %type_View 50 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 51 "View_ClipToPrevClip" + OpMemberName %type_View 52 "View_TemporalAAJitter" + OpMemberName %type_View 53 "View_GlobalClippingPlane" + OpMemberName %type_View 54 "View_FieldOfViewWideAngles" + OpMemberName %type_View 55 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 56 "View_ViewRectMin" + OpMemberName %type_View 57 "View_ViewSizeAndInvSize" + OpMemberName %type_View 58 "View_BufferSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 60 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 61 "View_PreExposure" + OpMemberName %type_View 62 "View_OneOverPreExposure" + OpMemberName %type_View 63 "PrePadding_View_2012" + OpMemberName %type_View 64 "View_DiffuseOverrideParameter" + OpMemberName %type_View 65 "View_SpecularOverrideParameter" + OpMemberName %type_View 66 "View_NormalOverrideParameter" + OpMemberName %type_View 67 "View_RoughnessOverrideParameter" + OpMemberName %type_View 68 "View_PrevFrameGameTime" + OpMemberName %type_View 69 "View_PrevFrameRealTime" + OpMemberName %type_View 70 "View_OutOfBoundsMask" + OpMemberName %type_View 71 "PrePadding_View_2084" + OpMemberName %type_View 72 "PrePadding_View_2088" + OpMemberName %type_View 73 "PrePadding_View_2092" + OpMemberName %type_View 74 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 75 "View_CullingSign" + OpMemberName %type_View 76 "View_NearPlane" + OpMemberName %type_View 77 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 78 "View_GameTime" + OpMemberName %type_View 79 "View_RealTime" + OpMemberName %type_View 80 "View_DeltaTime" + OpMemberName %type_View 81 "View_MaterialTextureMipBias" + OpMemberName %type_View 82 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 83 "View_Random" + OpMemberName %type_View 84 "View_FrameNumber" + OpMemberName %type_View 85 "View_StateFrameIndexMod8" + OpMemberName %type_View 86 "View_StateFrameIndex" + OpMemberName %type_View 87 "View_CameraCut" + OpMemberName %type_View 88 "View_UnlitViewmodeMask" + OpMemberName %type_View 89 "PrePadding_View_2164" + OpMemberName %type_View 90 "PrePadding_View_2168" + OpMemberName %type_View 91 "PrePadding_View_2172" + OpMemberName %type_View 92 "View_DirectionalLightColor" + OpMemberName %type_View 93 "View_DirectionalLightDirection" + OpMemberName %type_View 94 "PrePadding_View_2204" + OpMemberName %type_View 95 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 97 "View_TemporalAAParams" + OpMemberName %type_View 98 "View_CircleDOFParams" + OpMemberName %type_View 99 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 100 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 101 "View_DepthOfFieldScale" + OpMemberName %type_View 102 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 103 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 104 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 105 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 106 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 107 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 108 "View_GeneralPurposeTweak" + OpMemberName %type_View 109 "View_DemosaicVposOffset" + OpMemberName %type_View 110 "PrePadding_View_2348" + OpMemberName %type_View 111 "View_IndirectLightingColorScale" + OpMemberName %type_View 112 "View_HDR32bppEncodingMode" + OpMemberName %type_View 113 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 114 "View_AtmosphericFogSunPower" + OpMemberName %type_View 115 "View_AtmosphericFogPower" + OpMemberName %type_View 116 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 117 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 118 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 119 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 120 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 121 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 122 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 123 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 124 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 125 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 126 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 127 "View_AtmosphericFogSunColor" + OpMemberName %type_View 128 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 129 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 130 "View_AmbientCubemapTint" + OpMemberName %type_View 131 "View_AmbientCubemapIntensity" + OpMemberName %type_View 132 "View_SkyLightParameters" + OpMemberName %type_View 133 "PrePadding_View_2488" + OpMemberName %type_View 134 "PrePadding_View_2492" + OpMemberName %type_View 135 "View_SkyLightColor" + OpMemberName %type_View 136 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 137 "View_MobilePreviewMode" + OpMemberName %type_View 138 "View_HMDEyePaddingOffset" + OpMemberName %type_View 139 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 140 "View_ShowDecalsMask" + OpMemberName %type_View 141 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 142 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 143 "PrePadding_View_2648" + OpMemberName %type_View 144 "PrePadding_View_2652" + OpMemberName %type_View 145 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 146 "View_StereoPassIndex" + OpMemberName %type_View 147 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 148 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 149 "View_GlobalVolumeDimension" + OpMemberName %type_View 150 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 151 "View_MaxGlobalDistance" + OpMemberName %type_View 152 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 153 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 154 "PrePadding_View_2828" + OpMemberName %type_View 155 "View_VolumetricFogGridZParams" + OpMemberName %type_View 156 "PrePadding_View_2844" + OpMemberName %type_View 157 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 158 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 159 "PrePadding_View_2860" + OpMemberName %type_View 160 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 161 "PrePadding_View_2876" + OpMemberName %type_View 162 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 163 "PrePadding_View_2892" + OpMemberName %type_View 164 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 165 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 166 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 167 "View_StereoIPD" + OpMemberName %type_View 168 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 169 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_MobileBasePass "type.MobileBasePass" + OpMemberName %type_MobileBasePass 0 "MobileBasePass_Fog_ExponentialFogParameters" + OpMemberName %type_MobileBasePass 1 "MobileBasePass_Fog_ExponentialFogParameters2" + OpMemberName %type_MobileBasePass 2 "MobileBasePass_Fog_ExponentialFogColorParameter" + OpMemberName %type_MobileBasePass 3 "MobileBasePass_Fog_ExponentialFogParameters3" + OpMemberName %type_MobileBasePass 4 "MobileBasePass_Fog_InscatteringLightDirection" + OpMemberName %type_MobileBasePass 5 "MobileBasePass_Fog_DirectionalInscatteringColor" + OpMemberName %type_MobileBasePass 6 "MobileBasePass_Fog_SinCosInscatteringColorCubemapRotation" + OpMemberName %type_MobileBasePass 7 "PrePadding_MobileBasePass_Fog_104" + OpMemberName %type_MobileBasePass 8 "PrePadding_MobileBasePass_Fog_108" + OpMemberName %type_MobileBasePass 9 "MobileBasePass_Fog_FogInscatteringTextureParameters" + OpMemberName %type_MobileBasePass 10 "MobileBasePass_Fog_ApplyVolumetricFog" + OpMemberName %type_MobileBasePass 11 "PrePadding_MobileBasePass_PlanarReflection_128" + OpMemberName %type_MobileBasePass 12 "PrePadding_MobileBasePass_PlanarReflection_132" + OpMemberName %type_MobileBasePass 13 "PrePadding_MobileBasePass_PlanarReflection_136" + OpMemberName %type_MobileBasePass 14 "PrePadding_MobileBasePass_PlanarReflection_140" + OpMemberName %type_MobileBasePass 15 "PrePadding_MobileBasePass_PlanarReflection_144" + OpMemberName %type_MobileBasePass 16 "PrePadding_MobileBasePass_PlanarReflection_148" + OpMemberName %type_MobileBasePass 17 "PrePadding_MobileBasePass_PlanarReflection_152" + OpMemberName %type_MobileBasePass 18 "PrePadding_MobileBasePass_PlanarReflection_156" + OpMemberName %type_MobileBasePass 19 "MobileBasePass_PlanarReflection_ReflectionPlane" + OpMemberName %type_MobileBasePass 20 "MobileBasePass_PlanarReflection_PlanarReflectionOrigin" + OpMemberName %type_MobileBasePass 21 "MobileBasePass_PlanarReflection_PlanarReflectionXAxis" + OpMemberName %type_MobileBasePass 22 "MobileBasePass_PlanarReflection_PlanarReflectionYAxis" + OpMemberName %type_MobileBasePass 23 "MobileBasePass_PlanarReflection_InverseTransposeMirrorMatrix" + OpMemberName %type_MobileBasePass 24 "MobileBasePass_PlanarReflection_PlanarReflectionParameters" + OpMemberName %type_MobileBasePass 25 "PrePadding_MobileBasePass_PlanarReflection_284" + OpMemberName %type_MobileBasePass 26 "MobileBasePass_PlanarReflection_PlanarReflectionParameters2" + OpMemberName %type_MobileBasePass 27 "PrePadding_MobileBasePass_PlanarReflection_296" + OpMemberName %type_MobileBasePass 28 "PrePadding_MobileBasePass_PlanarReflection_300" + OpMemberName %type_MobileBasePass 29 "MobileBasePass_PlanarReflection_ProjectionWithExtraFOV" + OpMemberName %type_MobileBasePass 30 "MobileBasePass_PlanarReflection_PlanarReflectionScreenScaleBias" + OpMemberName %type_MobileBasePass 31 "MobileBasePass_PlanarReflection_PlanarReflectionScreenBound" + OpMemberName %type_MobileBasePass 32 "MobileBasePass_PlanarReflection_bIsStereo" + OpName %MobileBasePass "MobileBasePass" + OpName %type_Primitive "type.Primitive" + OpMemberName %type_Primitive 0 "Primitive_LocalToWorld" + OpMemberName %type_Primitive 1 "Primitive_InvNonUniformScaleAndDeterminantSign" + OpMemberName %type_Primitive 2 "Primitive_ObjectWorldPositionAndRadius" + OpMemberName %type_Primitive 3 "Primitive_WorldToLocal" + OpMemberName %type_Primitive 4 "Primitive_PreviousLocalToWorld" + OpMemberName %type_Primitive 5 "Primitive_PreviousWorldToLocal" + OpMemberName %type_Primitive 6 "Primitive_ActorWorldPosition" + OpMemberName %type_Primitive 7 "Primitive_UseSingleSampleShadowFromStationaryLights" + OpMemberName %type_Primitive 8 "Primitive_ObjectBounds" + OpMemberName %type_Primitive 9 "Primitive_LpvBiasMultiplier" + OpMemberName %type_Primitive 10 "Primitive_DecalReceiverMask" + OpMemberName %type_Primitive 11 "Primitive_PerObjectGBufferData" + OpMemberName %type_Primitive 12 "Primitive_UseVolumetricLightmapShadowFromStationaryLights" + OpMemberName %type_Primitive 13 "Primitive_UseEditorDepthTest" + OpMemberName %type_Primitive 14 "Primitive_ObjectOrientation" + OpMemberName %type_Primitive 15 "Primitive_NonUniformScale" + OpMemberName %type_Primitive 16 "Primitive_LocalObjectBoundsMin" + OpMemberName %type_Primitive 17 "PrePadding_Primitive_380" + OpMemberName %type_Primitive 18 "Primitive_LocalObjectBoundsMax" + OpMemberName %type_Primitive 19 "Primitive_LightingChannelMask" + OpMemberName %type_Primitive 20 "Primitive_LightmapDataIndex" + OpMemberName %type_Primitive 21 "Primitive_SingleCaptureIndex" + OpName %Primitive "Primitive" + OpName %type_LandscapeParameters "type.LandscapeParameters" + OpMemberName %type_LandscapeParameters 0 "LandscapeParameters_HeightmapUVScaleBias" + OpMemberName %type_LandscapeParameters 1 "LandscapeParameters_WeightmapUVScaleBias" + OpMemberName %type_LandscapeParameters 2 "LandscapeParameters_LandscapeLightmapScaleBias" + OpMemberName %type_LandscapeParameters 3 "LandscapeParameters_SubsectionSizeVertsLayerUVPan" + OpMemberName %type_LandscapeParameters 4 "LandscapeParameters_SubsectionOffsetParams" + OpMemberName %type_LandscapeParameters 5 "LandscapeParameters_LightmapSubsectionOffsetParams" + OpMemberName %type_LandscapeParameters 6 "LandscapeParameters_LocalToWorldNoScaling" + OpName %LandscapeParameters "LandscapeParameters" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "LodBias" + OpMemberName %type__Globals 1 "LodValues" + OpMemberName %type__Globals 2 "SectionLods" + OpMemberName %type__Globals 3 "NeighborSectionLod" + OpName %_Globals "$Globals" + OpName %in_var_ATTRIBUTE0 "in.var.ATTRIBUTE0" + OpName %in_var_ATTRIBUTE1 "in.var.ATTRIBUTE1" + OpName %out_var_TEXCOORD0 "out.var.TEXCOORD0" + OpName %out_var_TEXCOORD1 "out.var.TEXCOORD1" + OpName %out_var_TEXCOORD2 "out.var.TEXCOORD2" + OpName %out_var_TEXCOORD3 "out.var.TEXCOORD3" + OpName %out_var_TEXCOORD8 "out.var.TEXCOORD8" + OpName %Main "Main" + OpDecorateString %in_var_ATTRIBUTE0 UserSemantic "ATTRIBUTE0" + OpDecorateString %in_var_ATTRIBUTE1 UserSemantic "ATTRIBUTE1" + OpDecorateString %out_var_TEXCOORD0 UserSemantic "TEXCOORD0" + OpDecorateString %out_var_TEXCOORD1 UserSemantic "TEXCOORD1" + OpDecorateString %out_var_TEXCOORD2 UserSemantic "TEXCOORD2" + OpDecorateString %out_var_TEXCOORD3 UserSemantic "TEXCOORD3" + OpDecorateString %out_var_TEXCOORD8 UserSemantic "TEXCOORD8" + OpDecorate %gl_Position BuiltIn Position + OpDecorateString %gl_Position UserSemantic "SV_POSITION" + OpDecorate %in_var_ATTRIBUTE0 Location 0 + OpDecorate %in_var_ATTRIBUTE1 Location 1 + OpDecorate %out_var_TEXCOORD0 Location 0 + OpDecorate %out_var_TEXCOORD1 Location 1 + OpDecorate %out_var_TEXCOORD2 Location 2 + OpDecorate %out_var_TEXCOORD3 Location 3 + OpDecorate %out_var_TEXCOORD8 Location 4 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 0 + OpDecorate %MobileBasePass DescriptorSet 0 + OpDecorate %MobileBasePass Binding 1 + OpDecorate %Primitive DescriptorSet 0 + OpDecorate %Primitive Binding 2 + OpDecorate %LandscapeParameters DescriptorSet 0 + OpDecorate %LandscapeParameters Binding 3 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 4 + OpDecorate %_arr_v4float_uint_2_0 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 14 Offset 844 + OpMemberDecorate %type_View 15 Offset 848 + OpMemberDecorate %type_View 16 Offset 860 + OpMemberDecorate %type_View 17 Offset 864 + OpMemberDecorate %type_View 18 Offset 876 + OpMemberDecorate %type_View 19 Offset 880 + OpMemberDecorate %type_View 20 Offset 892 + OpMemberDecorate %type_View 21 Offset 896 + OpMemberDecorate %type_View 22 Offset 908 + OpMemberDecorate %type_View 23 Offset 912 + OpMemberDecorate %type_View 24 Offset 928 + OpMemberDecorate %type_View 25 Offset 944 + OpMemberDecorate %type_View 26 Offset 956 + OpMemberDecorate %type_View 27 Offset 960 + OpMemberDecorate %type_View 28 Offset 972 + OpMemberDecorate %type_View 29 Offset 976 + OpMemberDecorate %type_View 30 Offset 988 + OpMemberDecorate %type_View 31 Offset 992 + OpMemberDecorate %type_View 32 Offset 1004 + OpMemberDecorate %type_View 33 Offset 1008 + OpMemberDecorate %type_View 33 MatrixStride 16 + OpMemberDecorate %type_View 33 ColMajor + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 44 Offset 1660 + OpMemberDecorate %type_View 45 Offset 1664 + OpMemberDecorate %type_View 46 Offset 1676 + OpMemberDecorate %type_View 47 Offset 1680 + OpMemberDecorate %type_View 48 Offset 1692 + OpMemberDecorate %type_View 49 Offset 1696 + OpMemberDecorate %type_View 49 MatrixStride 16 + OpMemberDecorate %type_View 49 ColMajor + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 53 Offset 1904 + OpMemberDecorate %type_View 54 Offset 1920 + OpMemberDecorate %type_View 55 Offset 1928 + OpMemberDecorate %type_View 56 Offset 1936 + OpMemberDecorate %type_View 57 Offset 1952 + OpMemberDecorate %type_View 58 Offset 1968 + OpMemberDecorate %type_View 59 Offset 1984 + OpMemberDecorate %type_View 60 Offset 2000 + OpMemberDecorate %type_View 61 Offset 2004 + OpMemberDecorate %type_View 62 Offset 2008 + OpMemberDecorate %type_View 63 Offset 2012 + OpMemberDecorate %type_View 64 Offset 2016 + OpMemberDecorate %type_View 65 Offset 2032 + OpMemberDecorate %type_View 66 Offset 2048 + OpMemberDecorate %type_View 67 Offset 2064 + OpMemberDecorate %type_View 68 Offset 2072 + OpMemberDecorate %type_View 69 Offset 2076 + OpMemberDecorate %type_View 70 Offset 2080 + OpMemberDecorate %type_View 71 Offset 2084 + OpMemberDecorate %type_View 72 Offset 2088 + OpMemberDecorate %type_View 73 Offset 2092 + OpMemberDecorate %type_View 74 Offset 2096 + OpMemberDecorate %type_View 75 Offset 2108 + OpMemberDecorate %type_View 76 Offset 2112 + OpMemberDecorate %type_View 77 Offset 2116 + OpMemberDecorate %type_View 78 Offset 2120 + OpMemberDecorate %type_View 79 Offset 2124 + OpMemberDecorate %type_View 80 Offset 2128 + OpMemberDecorate %type_View 81 Offset 2132 + OpMemberDecorate %type_View 82 Offset 2136 + OpMemberDecorate %type_View 83 Offset 2140 + OpMemberDecorate %type_View 84 Offset 2144 + OpMemberDecorate %type_View 85 Offset 2148 + OpMemberDecorate %type_View 86 Offset 2152 + OpMemberDecorate %type_View 87 Offset 2156 + OpMemberDecorate %type_View 88 Offset 2160 + OpMemberDecorate %type_View 89 Offset 2164 + OpMemberDecorate %type_View 90 Offset 2168 + OpMemberDecorate %type_View 91 Offset 2172 + OpMemberDecorate %type_View 92 Offset 2176 + OpMemberDecorate %type_View 93 Offset 2192 + OpMemberDecorate %type_View 94 Offset 2204 + OpMemberDecorate %type_View 95 Offset 2208 + OpMemberDecorate %type_View 96 Offset 2240 + OpMemberDecorate %type_View 97 Offset 2272 + OpMemberDecorate %type_View 98 Offset 2288 + OpMemberDecorate %type_View 99 Offset 2304 + OpMemberDecorate %type_View 100 Offset 2308 + OpMemberDecorate %type_View 101 Offset 2312 + OpMemberDecorate %type_View 102 Offset 2316 + OpMemberDecorate %type_View 103 Offset 2320 + OpMemberDecorate %type_View 104 Offset 2324 + OpMemberDecorate %type_View 105 Offset 2328 + OpMemberDecorate %type_View 106 Offset 2332 + OpMemberDecorate %type_View 107 Offset 2336 + OpMemberDecorate %type_View 108 Offset 2340 + OpMemberDecorate %type_View 109 Offset 2344 + OpMemberDecorate %type_View 110 Offset 2348 + OpMemberDecorate %type_View 111 Offset 2352 + OpMemberDecorate %type_View 112 Offset 2364 + OpMemberDecorate %type_View 113 Offset 2368 + OpMemberDecorate %type_View 114 Offset 2380 + OpMemberDecorate %type_View 115 Offset 2384 + OpMemberDecorate %type_View 116 Offset 2388 + OpMemberDecorate %type_View 117 Offset 2392 + OpMemberDecorate %type_View 118 Offset 2396 + OpMemberDecorate %type_View 119 Offset 2400 + OpMemberDecorate %type_View 120 Offset 2404 + OpMemberDecorate %type_View 121 Offset 2408 + OpMemberDecorate %type_View 122 Offset 2412 + OpMemberDecorate %type_View 123 Offset 2416 + OpMemberDecorate %type_View 124 Offset 2420 + OpMemberDecorate %type_View 125 Offset 2424 + OpMemberDecorate %type_View 126 Offset 2428 + OpMemberDecorate %type_View 127 Offset 2432 + OpMemberDecorate %type_View 128 Offset 2448 + OpMemberDecorate %type_View 129 Offset 2460 + OpMemberDecorate %type_View 130 Offset 2464 + OpMemberDecorate %type_View 131 Offset 2480 + OpMemberDecorate %type_View 132 Offset 2484 + OpMemberDecorate %type_View 133 Offset 2488 + OpMemberDecorate %type_View 134 Offset 2492 + OpMemberDecorate %type_View 135 Offset 2496 + OpMemberDecorate %type_View 136 Offset 2512 + OpMemberDecorate %type_View 137 Offset 2624 + OpMemberDecorate %type_View 138 Offset 2628 + OpMemberDecorate %type_View 139 Offset 2632 + OpMemberDecorate %type_View 140 Offset 2636 + OpMemberDecorate %type_View 141 Offset 2640 + OpMemberDecorate %type_View 142 Offset 2644 + OpMemberDecorate %type_View 143 Offset 2648 + OpMemberDecorate %type_View 144 Offset 2652 + OpMemberDecorate %type_View 145 Offset 2656 + OpMemberDecorate %type_View 146 Offset 2668 + OpMemberDecorate %type_View 147 Offset 2672 + OpMemberDecorate %type_View 148 Offset 2736 + OpMemberDecorate %type_View 149 Offset 2800 + OpMemberDecorate %type_View 150 Offset 2804 + OpMemberDecorate %type_View 151 Offset 2808 + OpMemberDecorate %type_View 152 Offset 2812 + OpMemberDecorate %type_View 153 Offset 2816 + OpMemberDecorate %type_View 154 Offset 2828 + OpMemberDecorate %type_View 155 Offset 2832 + OpMemberDecorate %type_View 156 Offset 2844 + OpMemberDecorate %type_View 157 Offset 2848 + OpMemberDecorate %type_View 158 Offset 2856 + OpMemberDecorate %type_View 159 Offset 2860 + OpMemberDecorate %type_View 160 Offset 2864 + OpMemberDecorate %type_View 161 Offset 2876 + OpMemberDecorate %type_View 162 Offset 2880 + OpMemberDecorate %type_View 163 Offset 2892 + OpMemberDecorate %type_View 164 Offset 2896 + OpMemberDecorate %type_View 165 Offset 2908 + OpMemberDecorate %type_View 166 Offset 2912 + OpMemberDecorate %type_View 167 Offset 2924 + OpMemberDecorate %type_View 168 Offset 2928 + OpMemberDecorate %type_View 169 Offset 2932 + OpDecorate %type_View Block + OpDecorate %_arr_mat4v4float_uint_2 ArrayStride 64 + OpMemberDecorate %type_MobileBasePass 0 Offset 0 + OpMemberDecorate %type_MobileBasePass 1 Offset 16 + OpMemberDecorate %type_MobileBasePass 2 Offset 32 + OpMemberDecorate %type_MobileBasePass 3 Offset 48 + OpMemberDecorate %type_MobileBasePass 4 Offset 64 + OpMemberDecorate %type_MobileBasePass 5 Offset 80 + OpMemberDecorate %type_MobileBasePass 6 Offset 96 + OpMemberDecorate %type_MobileBasePass 7 Offset 104 + OpMemberDecorate %type_MobileBasePass 8 Offset 108 + OpMemberDecorate %type_MobileBasePass 9 Offset 112 + OpMemberDecorate %type_MobileBasePass 10 Offset 124 + OpMemberDecorate %type_MobileBasePass 11 Offset 128 + OpMemberDecorate %type_MobileBasePass 12 Offset 132 + OpMemberDecorate %type_MobileBasePass 13 Offset 136 + OpMemberDecorate %type_MobileBasePass 14 Offset 140 + OpMemberDecorate %type_MobileBasePass 15 Offset 144 + OpMemberDecorate %type_MobileBasePass 16 Offset 148 + OpMemberDecorate %type_MobileBasePass 17 Offset 152 + OpMemberDecorate %type_MobileBasePass 18 Offset 156 + OpMemberDecorate %type_MobileBasePass 19 Offset 160 + OpMemberDecorate %type_MobileBasePass 20 Offset 176 + OpMemberDecorate %type_MobileBasePass 21 Offset 192 + OpMemberDecorate %type_MobileBasePass 22 Offset 208 + OpMemberDecorate %type_MobileBasePass 23 Offset 224 + OpMemberDecorate %type_MobileBasePass 23 MatrixStride 16 + OpMemberDecorate %type_MobileBasePass 23 ColMajor + OpMemberDecorate %type_MobileBasePass 24 Offset 272 + OpMemberDecorate %type_MobileBasePass 25 Offset 284 + OpMemberDecorate %type_MobileBasePass 26 Offset 288 + OpMemberDecorate %type_MobileBasePass 27 Offset 296 + OpMemberDecorate %type_MobileBasePass 28 Offset 300 + OpMemberDecorate %type_MobileBasePass 29 Offset 304 + OpMemberDecorate %type_MobileBasePass 29 MatrixStride 16 + OpMemberDecorate %type_MobileBasePass 29 ColMajor + OpMemberDecorate %type_MobileBasePass 30 Offset 432 + OpMemberDecorate %type_MobileBasePass 31 Offset 464 + OpMemberDecorate %type_MobileBasePass 32 Offset 472 + OpDecorate %type_MobileBasePass Block + OpMemberDecorate %type_Primitive 0 Offset 0 + OpMemberDecorate %type_Primitive 0 MatrixStride 16 + OpMemberDecorate %type_Primitive 0 ColMajor + OpMemberDecorate %type_Primitive 1 Offset 64 + OpMemberDecorate %type_Primitive 2 Offset 80 + OpMemberDecorate %type_Primitive 3 Offset 96 + OpMemberDecorate %type_Primitive 3 MatrixStride 16 + OpMemberDecorate %type_Primitive 3 ColMajor + OpMemberDecorate %type_Primitive 4 Offset 160 + OpMemberDecorate %type_Primitive 4 MatrixStride 16 + OpMemberDecorate %type_Primitive 4 ColMajor + OpMemberDecorate %type_Primitive 5 Offset 224 + OpMemberDecorate %type_Primitive 5 MatrixStride 16 + OpMemberDecorate %type_Primitive 5 ColMajor + OpMemberDecorate %type_Primitive 6 Offset 288 + OpMemberDecorate %type_Primitive 7 Offset 300 + OpMemberDecorate %type_Primitive 8 Offset 304 + OpMemberDecorate %type_Primitive 9 Offset 316 + OpMemberDecorate %type_Primitive 10 Offset 320 + OpMemberDecorate %type_Primitive 11 Offset 324 + OpMemberDecorate %type_Primitive 12 Offset 328 + OpMemberDecorate %type_Primitive 13 Offset 332 + OpMemberDecorate %type_Primitive 14 Offset 336 + OpMemberDecorate %type_Primitive 15 Offset 352 + OpMemberDecorate %type_Primitive 16 Offset 368 + OpMemberDecorate %type_Primitive 17 Offset 380 + OpMemberDecorate %type_Primitive 18 Offset 384 + OpMemberDecorate %type_Primitive 19 Offset 396 + OpMemberDecorate %type_Primitive 20 Offset 400 + OpMemberDecorate %type_Primitive 21 Offset 404 + OpDecorate %type_Primitive Block + OpMemberDecorate %type_LandscapeParameters 0 Offset 0 + OpMemberDecorate %type_LandscapeParameters 1 Offset 16 + OpMemberDecorate %type_LandscapeParameters 2 Offset 32 + OpMemberDecorate %type_LandscapeParameters 3 Offset 48 + OpMemberDecorate %type_LandscapeParameters 4 Offset 64 + OpMemberDecorate %type_LandscapeParameters 5 Offset 80 + OpMemberDecorate %type_LandscapeParameters 6 Offset 96 + OpMemberDecorate %type_LandscapeParameters 6 MatrixStride 16 + OpMemberDecorate %type_LandscapeParameters 6 ColMajor + OpDecorate %type_LandscapeParameters Block + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 32 + OpMemberDecorate %type__Globals 3 Offset 48 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 +%float_0_00999999978 = OpConstant %float 0.00999999978 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float_0 = OpConstant %float 0 + %40 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 + %float_255 = OpConstant %float 255 + %44 = OpConstantComposite %v4float %float_255 %float_255 %float_255 %float_255 + %float_0_5 = OpConstant %float 0.5 + %46 = OpConstantComposite %v2float %float_0_5 %float_0_5 + %float_2 = OpConstant %float 2 + %48 = OpConstantComposite %v2float %float_2 %float_2 + %float_1 = OpConstant %float 1 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %float_3 = OpConstant %float 3 + %float_0_25 = OpConstant %float 0.25 + %uint_3 = OpConstant %uint 3 + %float_4 = OpConstant %float 4 +%float_0_125 = OpConstant %float 0.125 + %float_5 = OpConstant %float 5 +%float_0_0625 = OpConstant %float 0.0625 +%float_0_03125 = OpConstant %float 0.03125 + %60 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %int_5 = OpConstant %int 5 + %int_4 = OpConstant %int 4 + %63 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %int_25 = OpConstant %int 25 + %int_27 = OpConstant %int 27 + %int_31 = OpConstant %int 31 + %67 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%float_32768 = OpConstant %float 32768 +%_arr_v4float_uint_2_0 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2_0 %_arr_v4float_uint_2_0 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%mat3v4float = OpTypeMatrix %v4float 3 +%_arr_mat4v4float_uint_2 = OpTypeArray %mat4v4float %uint_2 +%type_MobileBasePass = OpTypeStruct %v4float %v4float %v4float %v4float %v4float %v4float %v2float %float %float %v3float %float %float %float %float %float %float %float %float %float %v4float %v4float %v4float %v4float %mat3v4float %v3float %float %v2float %float %float %_arr_mat4v4float_uint_2 %_arr_v4float_uint_2_0 %v2float %uint +%_ptr_Uniform_type_MobileBasePass = OpTypePointer Uniform %type_MobileBasePass +%type_Primitive = OpTypeStruct %mat4v4float %v4float %v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %float %float %float %float %v4float %v4float %v3float %float %v3float %uint %uint %int +%_ptr_Uniform_type_Primitive = OpTypePointer Uniform %type_Primitive +%type_LandscapeParameters = OpTypeStruct %v4float %v4float %v4float %v4float %v4float %v4float %mat4v4float +%_ptr_Uniform_type_LandscapeParameters = OpTypePointer Uniform %type_LandscapeParameters +%type__Globals = OpTypeStruct %v4float %v4float %v4float %_arr_v4float_uint_4 +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Input__arr_v4float_uint_2 = OpTypePointer Input %_arr_v4float_uint_2 +%_ptr_Output_v2float = OpTypePointer Output %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %80 = OpTypeFunction %void +%_arr_v4float_uint_1 = OpTypeArray %v4float %uint_1 +%_ptr_Function__arr_v4float_uint_1 = OpTypePointer Function %_arr_v4float_uint_1 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %bool = OpTypeBool +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %v3bool = OpTypeVector %bool 3 +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %View = OpVariable %_ptr_Uniform_type_View Uniform +%MobileBasePass = OpVariable %_ptr_Uniform_type_MobileBasePass Uniform + %Primitive = OpVariable %_ptr_Uniform_type_Primitive Uniform +%LandscapeParameters = OpVariable %_ptr_Uniform_type_LandscapeParameters Uniform + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%in_var_ATTRIBUTE0 = OpVariable %_ptr_Input_v4float Input +%in_var_ATTRIBUTE1 = OpVariable %_ptr_Input__arr_v4float_uint_2 Input +%out_var_TEXCOORD0 = OpVariable %_ptr_Output_v2float Output +%out_var_TEXCOORD1 = OpVariable %_ptr_Output_v2float Output +%out_var_TEXCOORD2 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD3 = OpVariable %_ptr_Output_v4float Output +%out_var_TEXCOORD8 = OpVariable %_ptr_Output_v4float Output +%gl_Position = OpVariable %_ptr_Output_v4float Output +%float_0_0078125 = OpConstant %float 0.0078125 + %float_n127 = OpConstant %float -127 + %92 = OpConstantNull %v4float +%float_0_00392156886 = OpConstant %float 0.00392156886 + %94 = OpConstantComposite %v2float %float_0_00392156886 %float_0_00392156886 +%float_65280 = OpConstant %float 65280 + %Main = OpFunction %void None %80 + %96 = OpLabel + %97 = OpVariable %_ptr_Function__arr_v4float_uint_1 Function + %98 = OpLoad %v4float %in_var_ATTRIBUTE0 + %99 = OpLoad %_arr_v4float_uint_2 %in_var_ATTRIBUTE1 + %100 = OpAccessChain %_ptr_Uniform_mat4v4float %View %int_0 + %101 = OpLoad %mat4v4float %100 + %102 = OpAccessChain %_ptr_Uniform_v3float %View %int_27 + %103 = OpLoad %v3float %102 + %104 = OpAccessChain %_ptr_Uniform_v3float %View %int_31 + %105 = OpLoad %v3float %104 + OpBranch %106 + %106 = OpLabel + %107 = OpPhi %int %int_0 %96 %108 %109 + %110 = OpSLessThan %bool %107 %int_1 + OpLoopMerge %111 %109 Unroll + OpBranchConditional %110 %109 %111 + %109 = OpLabel + %112 = OpAccessChain %_ptr_Function_v4float %97 %107 + OpStore %112 %40 + %108 = OpIAdd %int %107 %int_1 + OpBranch %106 + %111 = OpLabel + %113 = OpCompositeExtract %v4float %99 0 + %114 = OpCompositeExtract %v4float %99 1 + %115 = OpFMul %v4float %98 %44 + %116 = OpVectorShuffle %v2float %115 %115 2 3 + %117 = OpFMul %v2float %116 %46 + %118 = OpExtInst %v2float %1 Fract %117 + %119 = OpFMul %v2float %118 %48 + %120 = OpFSub %v2float %116 %119 + %121 = OpFMul %v2float %120 %94 + %122 = OpVectorShuffle %v2float %115 %92 0 1 + %123 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 %int_3 + %124 = OpLoad %float %123 + %125 = OpCompositeConstruct %v2float %124 %124 + %126 = OpFMul %v2float %122 %125 + %127 = OpCompositeExtract %float %126 1 + %128 = OpCompositeExtract %float %126 0 + %129 = OpFSub %float %float_1 %128 + %130 = OpFSub %float %float_1 %127 + %131 = OpCompositeConstruct %v4float %127 %128 %129 %130 + %132 = OpFMul %v4float %131 %67 + %133 = OpCompositeExtract %float %119 1 + %134 = OpFOrdGreaterThan %bool %133 %float_0_5 + OpSelectionMerge %135 None + OpBranchConditional %134 %136 %137 + %136 = OpLabel + %138 = OpCompositeExtract %float %119 0 + %139 = OpFOrdGreaterThan %bool %138 %float_0_5 + OpSelectionMerge %140 None + OpBranchConditional %139 %141 %142 + %141 = OpLabel + %143 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 %int_3 + %144 = OpLoad %float %143 + %145 = OpCompositeConstruct %v4float %144 %144 %144 %144 + %146 = OpFMul %v4float %132 %145 + %147 = OpFSub %v4float %60 %132 + %148 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_3 %int_3 + %149 = OpLoad %v4float %148 + %150 = OpFMul %v4float %147 %149 + %151 = OpFAdd %v4float %146 %150 + OpBranch %140 + %142 = OpLabel + %152 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 %int_2 + %153 = OpLoad %float %152 + %154 = OpCompositeConstruct %v4float %153 %153 %153 %153 + %155 = OpFMul %v4float %132 %154 + %156 = OpFSub %v4float %60 %132 + %157 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_3 %int_2 + %158 = OpLoad %v4float %157 + %159 = OpFMul %v4float %156 %158 + %160 = OpFAdd %v4float %155 %159 + OpBranch %140 + %140 = OpLabel + %161 = OpPhi %v4float %151 %141 %160 %142 + OpBranch %135 + %137 = OpLabel + %162 = OpCompositeExtract %float %119 0 + %163 = OpFOrdGreaterThan %bool %162 %float_0_5 + OpSelectionMerge %164 None + OpBranchConditional %163 %165 %166 + %165 = OpLabel + %167 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 %int_1 + %168 = OpLoad %float %167 + %169 = OpCompositeConstruct %v4float %168 %168 %168 %168 + %170 = OpFMul %v4float %132 %169 + %171 = OpFSub %v4float %60 %132 + %172 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_3 %int_1 + %173 = OpLoad %v4float %172 + %174 = OpFMul %v4float %171 %173 + %175 = OpFAdd %v4float %170 %174 + OpBranch %164 + %166 = OpLabel + %176 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 %int_0 + %177 = OpLoad %float %176 + %178 = OpCompositeConstruct %v4float %177 %177 %177 %177 + %179 = OpFMul %v4float %132 %178 + %180 = OpFSub %v4float %60 %132 + %181 = OpAccessChain %_ptr_Uniform_v4float %_Globals %int_3 %int_0 + %182 = OpLoad %v4float %181 + %183 = OpFMul %v4float %180 %182 + %184 = OpFAdd %v4float %179 %183 + OpBranch %164 + %164 = OpLabel + %185 = OpPhi %v4float %175 %165 %184 %166 + OpBranch %135 + %135 = OpLabel + %186 = OpPhi %v4float %161 %140 %185 %164 + %187 = OpFAdd %float %128 %127 + %188 = OpFOrdGreaterThan %bool %187 %float_1 + OpSelectionMerge %189 None + OpBranchConditional %188 %190 %191 + %190 = OpLabel + %192 = OpFOrdLessThan %bool %128 %127 + OpSelectionMerge %193 None + OpBranchConditional %192 %194 %195 + %194 = OpLabel + %196 = OpCompositeExtract %float %186 3 + OpBranch %193 + %195 = OpLabel + %197 = OpCompositeExtract %float %186 2 + OpBranch %193 + %193 = OpLabel + %198 = OpPhi %float %196 %194 %197 %195 + OpBranch %189 + %191 = OpLabel + %199 = OpFOrdLessThan %bool %128 %127 + OpSelectionMerge %200 None + OpBranchConditional %199 %201 %202 + %201 = OpLabel + %203 = OpCompositeExtract %float %186 1 + OpBranch %200 + %202 = OpLabel + %204 = OpCompositeExtract %float %186 0 + OpBranch %200 + %200 = OpLabel + %205 = OpPhi %float %203 %201 %204 %202 + OpBranch %189 + %189 = OpLabel + %206 = OpPhi %float %198 %193 %205 %200 + %207 = OpExtInst %float %1 Floor %206 + %208 = OpFSub %float %206 %207 + %209 = OpFOrdLessThan %bool %207 %float_1 + %210 = OpCompositeExtract %float %114 0 + %211 = OpCompositeExtract %float %114 1 + %212 = OpCompositeConstruct %v3float %float_1 %210 %211 + %213 = OpFOrdLessThan %bool %207 %float_2 + %214 = OpCompositeExtract %float %114 2 + %215 = OpCompositeConstruct %v3float %float_0_5 %211 %214 + %216 = OpFOrdLessThan %bool %207 %float_3 + %217 = OpCompositeExtract %float %114 3 + %218 = OpCompositeConstruct %v3float %float_0_25 %214 %217 + %219 = OpFOrdLessThan %bool %207 %float_4 + %220 = OpCompositeExtract %float %121 0 + %221 = OpCompositeConstruct %v3float %float_0_125 %217 %220 + %222 = OpFOrdLessThan %bool %207 %float_5 + %223 = OpCompositeExtract %float %121 1 + %224 = OpCompositeConstruct %v3float %float_0_0625 %220 %223 + %225 = OpCompositeConstruct %v3float %float_0_03125 %223 %223 + %226 = OpCompositeConstruct %v3bool %222 %222 %222 + %227 = OpSelect %v3float %226 %224 %225 + %228 = OpCompositeConstruct %v3bool %219 %219 %219 + %229 = OpSelect %v3float %228 %221 %227 + %230 = OpCompositeConstruct %v3bool %216 %216 %216 + %231 = OpSelect %v3float %230 %218 %229 + %232 = OpCompositeConstruct %v3bool %213 %213 %213 + %233 = OpSelect %v3float %232 %215 %231 + %234 = OpCompositeConstruct %v3bool %209 %209 %209 + %235 = OpSelect %v3float %234 %212 %233 + %236 = OpCompositeExtract %float %235 0 + %237 = OpCompositeExtract %float %235 1 + %238 = OpCompositeExtract %float %235 2 + %239 = OpCompositeExtract %float %113 0 + %240 = OpFMul %float %239 %float_65280 + %241 = OpCompositeExtract %float %113 1 + %242 = OpFMul %float %241 %float_255 + %243 = OpFAdd %float %240 %242 + %244 = OpFSub %float %243 %float_32768 + %245 = OpFMul %float %244 %float_0_0078125 + %246 = OpCompositeExtract %float %113 2 + %247 = OpFMul %float %246 %float_65280 + %248 = OpCompositeExtract %float %113 3 + %249 = OpFMul %float %248 %float_255 + %250 = OpFAdd %float %247 %249 + %251 = OpFSub %float %250 %float_32768 + %252 = OpFMul %float %251 %float_0_0078125 + %253 = OpExtInst %float %1 FMix %245 %252 %237 + %254 = OpExtInst %float %1 FMix %245 %252 %238 + %255 = OpCompositeConstruct %v2float %236 %236 + %256 = OpFMul %v2float %122 %255 + %257 = OpExtInst %v2float %1 Floor %256 + %258 = OpAccessChain %_ptr_Uniform_v4float %LandscapeParameters %int_3 + %259 = OpAccessChain %_ptr_Uniform_float %LandscapeParameters %int_3 %int_0 + %260 = OpLoad %float %259 + %261 = OpFMul %float %260 %236 + %262 = OpFSub %float %261 %float_1 + %263 = OpFMul %float %260 %float_0_5 + %264 = OpFMul %float %263 %236 + %265 = OpExtInst %float %1 FMax %264 %float_2 + %266 = OpFSub %float %265 %float_1 + %267 = OpCompositeConstruct %v2float %262 %266 + %268 = OpAccessChain %_ptr_Uniform_float %LandscapeParameters %int_3 %int_1 + %269 = OpLoad %float %268 + %270 = OpCompositeConstruct %v2float %269 %269 + %271 = OpFMul %v2float %267 %270 + %272 = OpCompositeExtract %float %271 0 + %273 = OpCompositeConstruct %v2float %272 %272 + %274 = OpFDiv %v2float %257 %273 + %275 = OpFMul %v2float %257 %46 + %276 = OpExtInst %v2float %1 Floor %275 + %277 = OpCompositeExtract %float %271 1 + %278 = OpCompositeConstruct %v2float %277 %277 + %279 = OpFDiv %v2float %276 %278 + %280 = OpCompositeExtract %float %274 0 + %281 = OpCompositeExtract %float %274 1 + %282 = OpCompositeConstruct %v3float %280 %281 %253 + %283 = OpCompositeExtract %float %279 0 + %284 = OpCompositeExtract %float %279 1 + %285 = OpCompositeConstruct %v3float %283 %284 %254 + %286 = OpCompositeConstruct %v3float %208 %208 %208 + %287 = OpExtInst %v3float %1 FMix %282 %285 %286 + %288 = OpVectorShuffle %v2float %119 %92 0 1 + %289 = OpAccessChain %_ptr_Uniform_v4float %LandscapeParameters %int_4 + %290 = OpLoad %v4float %289 + %291 = OpVectorShuffle %v2float %290 %290 3 3 + %292 = OpFMul %v2float %288 %291 + %293 = OpCompositeExtract %float %292 0 + %294 = OpCompositeExtract %float %292 1 + %295 = OpCompositeConstruct %v3float %293 %294 %float_0 + %296 = OpFAdd %v3float %287 %295 + %297 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_0 %uint_0 + %298 = OpLoad %v4float %297 + %299 = OpVectorShuffle %v3float %298 %298 0 1 2 + %300 = OpVectorShuffle %v3float %296 %296 0 0 0 + %301 = OpFMul %v3float %299 %300 + %302 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_0 %uint_1 + %303 = OpLoad %v4float %302 + %304 = OpVectorShuffle %v3float %303 %303 0 1 2 + %305 = OpVectorShuffle %v3float %296 %296 1 1 1 + %306 = OpFMul %v3float %304 %305 + %307 = OpFAdd %v3float %301 %306 + %308 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_0 %uint_2 + %309 = OpLoad %v4float %308 + %310 = OpVectorShuffle %v3float %309 %309 0 1 2 + %311 = OpVectorShuffle %v3float %296 %296 2 2 2 + %312 = OpFMul %v3float %310 %311 + %313 = OpFAdd %v3float %307 %312 + %314 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_0 %uint_3 + %315 = OpLoad %v4float %314 + %316 = OpVectorShuffle %v3float %315 %315 0 1 2 + %317 = OpFAdd %v3float %316 %105 + %318 = OpFAdd %v3float %313 %317 + %319 = OpCompositeExtract %float %318 0 + %320 = OpCompositeExtract %float %318 1 + %321 = OpCompositeExtract %float %318 2 + %322 = OpCompositeConstruct %v4float %319 %320 %321 %float_1 + %323 = OpVectorShuffle %v2float %287 %287 0 1 + %324 = OpLoad %v4float %258 + %325 = OpVectorShuffle %v2float %324 %324 2 3 + %326 = OpFAdd %v2float %323 %325 + %327 = OpFAdd %v2float %326 %292 + %328 = OpAccessChain %_ptr_Uniform_v4float %LandscapeParameters %int_1 + %329 = OpLoad %v4float %328 + %330 = OpVectorShuffle %v2float %329 %329 0 1 + %331 = OpFMul %v2float %323 %330 + %332 = OpVectorShuffle %v2float %329 %329 2 3 + %333 = OpFAdd %v2float %331 %332 + %334 = OpVectorShuffle %v2float %290 %290 2 2 + %335 = OpFMul %v2float %288 %334 + %336 = OpFAdd %v2float %333 %335 + %337 = OpVectorShuffle %v2float %327 %92 0 1 + %338 = OpVectorShuffle %v4float %322 %322 4 5 6 3 + %339 = OpMatrixTimesVector %v4float %101 %338 + %340 = OpVectorShuffle %v3float %322 %92 0 1 2 + %341 = OpFSub %v3float %340 %103 + %342 = OpAccessChain %_ptr_Uniform_v4float %MobileBasePass %int_2 + %343 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_2 %int_3 + %344 = OpLoad %float %343 + %345 = OpDot %float %341 %341 + %346 = OpExtInst %float %1 InverseSqrt %345 + %347 = OpFMul %float %345 %346 + %348 = OpCompositeConstruct %v3float %346 %346 %346 + %349 = OpFMul %v3float %341 %348 + %350 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_0 %int_0 + %351 = OpLoad %float %350 + %352 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_1 %int_0 + %353 = OpLoad %float %352 + %354 = OpCompositeExtract %float %341 2 + %355 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_0 %int_3 + %356 = OpLoad %float %355 + %357 = OpExtInst %float %1 FMax %float_0 %356 + %358 = OpFOrdGreaterThan %bool %357 %float_0 + OpSelectionMerge %359 None + OpBranchConditional %358 %360 %359 + %360 = OpLabel + %361 = OpFMul %float %357 %346 + %362 = OpFMul %float %361 %354 + %363 = OpAccessChain %_ptr_Uniform_float %View %int_25 %int_2 + %364 = OpLoad %float %363 + %365 = OpFAdd %float %364 %362 + %366 = OpFSub %float %354 %362 + %367 = OpFSub %float %float_1 %361 + %368 = OpFMul %float %367 %347 + %369 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_0 %int_1 + %370 = OpLoad %float %369 + %371 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_3 %int_1 + %372 = OpLoad %float %371 + %373 = OpFSub %float %365 %372 + %374 = OpFMul %float %370 %373 + %375 = OpExtInst %float %1 FMax %float_n127 %374 + %376 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_3 %int_0 + %377 = OpLoad %float %376 + %378 = OpFNegate %float %375 + %379 = OpExtInst %float %1 Exp2 %378 + %380 = OpFMul %float %377 %379 + %381 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_1 %int_1 + %382 = OpLoad %float %381 + %383 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_1 %int_3 + %384 = OpLoad %float %383 + %385 = OpFSub %float %365 %384 + %386 = OpFMul %float %382 %385 + %387 = OpExtInst %float %1 FMax %float_n127 %386 + %388 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_1 %int_2 + %389 = OpLoad %float %388 + %390 = OpFNegate %float %387 + %391 = OpExtInst %float %1 Exp2 %390 + %392 = OpFMul %float %389 %391 + OpBranch %359 + %359 = OpLabel + %393 = OpPhi %float %347 %189 %368 %360 + %394 = OpPhi %float %353 %189 %392 %360 + %395 = OpPhi %float %351 %189 %380 %360 + %396 = OpPhi %float %354 %189 %366 %360 + %397 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_0 %int_1 + %398 = OpLoad %float %397 + %399 = OpFMul %float %398 %396 + %400 = OpExtInst %float %1 FMax %float_n127 %399 + %401 = OpFNegate %float %400 + %402 = OpExtInst %float %1 Exp2 %401 + %403 = OpFSub %float %float_1 %402 + %404 = OpFDiv %float %403 %400 + %405 = OpExtInst %float %1 Log %float_2 + %406 = OpFMul %float %405 %405 + %407 = OpFMul %float %float_0_5 %406 + %408 = OpFMul %float %407 %400 + %409 = OpFSub %float %405 %408 + %410 = OpExtInst %float %1 FAbs %400 + %411 = OpFOrdGreaterThan %bool %410 %float_0_00999999978 + %412 = OpSelect %float %411 %404 %409 + %413 = OpFMul %float %395 %412 + %414 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_1 %int_1 + %415 = OpLoad %float %414 + %416 = OpFMul %float %415 %396 + %417 = OpExtInst %float %1 FMax %float_n127 %416 + %418 = OpFNegate %float %417 + %419 = OpExtInst %float %1 Exp2 %418 + %420 = OpFSub %float %float_1 %419 + %421 = OpFDiv %float %420 %417 + %422 = OpFMul %float %407 %417 + %423 = OpFSub %float %405 %422 + %424 = OpExtInst %float %1 FAbs %417 + %425 = OpFOrdGreaterThan %bool %424 %float_0_00999999978 + %426 = OpSelect %float %425 %421 %423 + %427 = OpFMul %float %394 %426 + %428 = OpFAdd %float %413 %427 + %429 = OpFMul %float %428 %393 + %430 = OpLoad %v4float %342 + %431 = OpVectorShuffle %v3float %430 %430 0 1 2 + %432 = OpAccessChain %_ptr_Uniform_v4float %MobileBasePass %int_4 + %433 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_4 %int_3 + %434 = OpLoad %float %433 + %435 = OpFOrdGreaterThanEqual %bool %434 %float_0 + OpSelectionMerge %436 DontFlatten + OpBranchConditional %435 %437 %436 + %437 = OpLabel + %438 = OpAccessChain %_ptr_Uniform_v4float %MobileBasePass %int_5 + %439 = OpLoad %v4float %438 + %440 = OpVectorShuffle %v3float %439 %439 0 1 2 + %441 = OpLoad %v4float %432 + %442 = OpVectorShuffle %v3float %441 %441 0 1 2 + %443 = OpDot %float %349 %442 + %444 = OpExtInst %float %1 FClamp %443 %float_0 %float_1 + %445 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_5 %int_3 + %446 = OpLoad %float %445 + %447 = OpExtInst %float %1 Pow %444 %446 + %448 = OpCompositeConstruct %v3float %447 %447 %447 + %449 = OpFMul %v3float %440 %448 + %450 = OpFSub %float %393 %434 + %451 = OpExtInst %float %1 FMax %450 %float_0 + %452 = OpFMul %float %428 %451 + %453 = OpFNegate %float %452 + %454 = OpExtInst %float %1 Exp2 %453 + %455 = OpExtInst %float %1 FClamp %454 %float_0 %float_1 + %456 = OpFSub %float %float_1 %455 + %457 = OpCompositeConstruct %v3float %456 %456 %456 + %458 = OpFMul %v3float %449 %457 + OpBranch %436 + %436 = OpLabel + %459 = OpPhi %v3float %63 %359 %458 %437 + %460 = OpFNegate %float %429 + %461 = OpExtInst %float %1 Exp2 %460 + %462 = OpExtInst %float %1 FClamp %461 %float_0 %float_1 + %463 = OpExtInst %float %1 FMax %462 %344 + %464 = OpAccessChain %_ptr_Uniform_float %MobileBasePass %int_3 %int_3 + %465 = OpLoad %float %464 + %466 = OpFOrdGreaterThan %bool %465 %float_0 + %467 = OpFOrdGreaterThan %bool %347 %465 + %468 = OpLogicalAnd %bool %466 %467 + %469 = OpCompositeConstruct %v3bool %468 %468 %468 + %470 = OpSelect %v3float %469 %63 %459 + %471 = OpSelect %float %468 %float_1 %463 + %472 = OpFSub %float %float_1 %471 + %473 = OpCompositeConstruct %v3float %472 %472 %472 + %474 = OpFMul %v3float %431 %473 + %475 = OpFAdd %v3float %474 %470 + %476 = OpCompositeExtract %float %475 0 + %477 = OpCompositeExtract %float %475 1 + %478 = OpCompositeExtract %float %475 2 + %479 = OpCompositeConstruct %v4float %476 %477 %478 %471 + %480 = OpAccessChain %_ptr_Function_v4float %97 %int_0 + OpStore %480 %479 + %481 = OpCompositeExtract %float %339 3 + %482 = OpCompositeInsert %v4float %481 %338 3 + %483 = OpLoad %_arr_v4float_uint_1 %97 + %484 = OpCompositeExtract %v4float %483 0 + %485 = OpVectorShuffle %v4float %92 %484 0 1 4 5 + %486 = OpVectorShuffle %v4float %92 %484 0 1 6 7 + OpStore %out_var_TEXCOORD0 %337 + OpStore %out_var_TEXCOORD1 %336 + OpStore %out_var_TEXCOORD2 %485 + OpStore %out_var_TEXCOORD3 %486 + OpStore %out_var_TEXCOORD8 %482 + OpStore %gl_Position %339 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders-ue4/asm/vert/texture-buffer.asm.vert b/third_party/spirv-cross/shaders-ue4/asm/vert/texture-buffer.asm.vert new file mode 100644 index 0000000..6d52623 --- /dev/null +++ b/third_party/spirv-cross/shaders-ue4/asm/vert/texture-buffer.asm.vert @@ -0,0 +1,1054 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 397 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability StorageImageExtendedFormats + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %Main "main" %gl_VertexIndex %gl_InstanceIndex %in_var_ATTRIBUTE0 %out_var_TEXCOORD6 %gl_Position + OpSource HLSL 600 + OpName %type_View "type.View" + OpMemberName %type_View 0 "View_TranslatedWorldToClip" + OpMemberName %type_View 1 "View_WorldToClip" + OpMemberName %type_View 2 "View_TranslatedWorldToView" + OpMemberName %type_View 3 "View_ViewToTranslatedWorld" + OpMemberName %type_View 4 "View_TranslatedWorldToCameraView" + OpMemberName %type_View 5 "View_CameraViewToTranslatedWorld" + OpMemberName %type_View 6 "View_ViewToClip" + OpMemberName %type_View 7 "View_ViewToClipNoAA" + OpMemberName %type_View 8 "View_ClipToView" + OpMemberName %type_View 9 "View_ClipToTranslatedWorld" + OpMemberName %type_View 10 "View_SVPositionToTranslatedWorld" + OpMemberName %type_View 11 "View_ScreenToWorld" + OpMemberName %type_View 12 "View_ScreenToTranslatedWorld" + OpMemberName %type_View 13 "View_ViewForward" + OpMemberName %type_View 14 "PrePadding_View_844" + OpMemberName %type_View 15 "View_ViewUp" + OpMemberName %type_View 16 "PrePadding_View_860" + OpMemberName %type_View 17 "View_ViewRight" + OpMemberName %type_View 18 "PrePadding_View_876" + OpMemberName %type_View 19 "View_HMDViewNoRollUp" + OpMemberName %type_View 20 "PrePadding_View_892" + OpMemberName %type_View 21 "View_HMDViewNoRollRight" + OpMemberName %type_View 22 "PrePadding_View_908" + OpMemberName %type_View 23 "View_InvDeviceZToWorldZTransform" + OpMemberName %type_View 24 "View_ScreenPositionScaleBias" + OpMemberName %type_View 25 "View_WorldCameraOrigin" + OpMemberName %type_View 26 "PrePadding_View_956" + OpMemberName %type_View 27 "View_TranslatedWorldCameraOrigin" + OpMemberName %type_View 28 "PrePadding_View_972" + OpMemberName %type_View 29 "View_WorldViewOrigin" + OpMemberName %type_View 30 "PrePadding_View_988" + OpMemberName %type_View 31 "View_PreViewTranslation" + OpMemberName %type_View 32 "PrePadding_View_1004" + OpMemberName %type_View 33 "View_PrevProjection" + OpMemberName %type_View 34 "View_PrevViewProj" + OpMemberName %type_View 35 "View_PrevViewRotationProj" + OpMemberName %type_View 36 "View_PrevViewToClip" + OpMemberName %type_View 37 "View_PrevClipToView" + OpMemberName %type_View 38 "View_PrevTranslatedWorldToClip" + OpMemberName %type_View 39 "View_PrevTranslatedWorldToView" + OpMemberName %type_View 40 "View_PrevViewToTranslatedWorld" + OpMemberName %type_View 41 "View_PrevTranslatedWorldToCameraView" + OpMemberName %type_View 42 "View_PrevCameraViewToTranslatedWorld" + OpMemberName %type_View 43 "View_PrevWorldCameraOrigin" + OpMemberName %type_View 44 "PrePadding_View_1660" + OpMemberName %type_View 45 "View_PrevWorldViewOrigin" + OpMemberName %type_View 46 "PrePadding_View_1676" + OpMemberName %type_View 47 "View_PrevPreViewTranslation" + OpMemberName %type_View 48 "PrePadding_View_1692" + OpMemberName %type_View 49 "View_PrevInvViewProj" + OpMemberName %type_View 50 "View_PrevScreenToTranslatedWorld" + OpMemberName %type_View 51 "View_ClipToPrevClip" + OpMemberName %type_View 52 "View_TemporalAAJitter" + OpMemberName %type_View 53 "View_GlobalClippingPlane" + OpMemberName %type_View 54 "View_FieldOfViewWideAngles" + OpMemberName %type_View 55 "View_PrevFieldOfViewWideAngles" + OpMemberName %type_View 56 "View_ViewRectMin" + OpMemberName %type_View 57 "View_ViewSizeAndInvSize" + OpMemberName %type_View 58 "View_BufferSizeAndInvSize" + OpMemberName %type_View 59 "View_BufferBilinearUVMinMax" + OpMemberName %type_View 60 "View_NumSceneColorMSAASamples" + OpMemberName %type_View 61 "View_PreExposure" + OpMemberName %type_View 62 "View_OneOverPreExposure" + OpMemberName %type_View 63 "PrePadding_View_2012" + OpMemberName %type_View 64 "View_DiffuseOverrideParameter" + OpMemberName %type_View 65 "View_SpecularOverrideParameter" + OpMemberName %type_View 66 "View_NormalOverrideParameter" + OpMemberName %type_View 67 "View_RoughnessOverrideParameter" + OpMemberName %type_View 68 "View_PrevFrameGameTime" + OpMemberName %type_View 69 "View_PrevFrameRealTime" + OpMemberName %type_View 70 "View_OutOfBoundsMask" + OpMemberName %type_View 71 "PrePadding_View_2084" + OpMemberName %type_View 72 "PrePadding_View_2088" + OpMemberName %type_View 73 "PrePadding_View_2092" + OpMemberName %type_View 74 "View_WorldCameraMovementSinceLastFrame" + OpMemberName %type_View 75 "View_CullingSign" + OpMemberName %type_View 76 "View_NearPlane" + OpMemberName %type_View 77 "View_AdaptiveTessellationFactor" + OpMemberName %type_View 78 "View_GameTime" + OpMemberName %type_View 79 "View_RealTime" + OpMemberName %type_View 80 "View_DeltaTime" + OpMemberName %type_View 81 "View_MaterialTextureMipBias" + OpMemberName %type_View 82 "View_MaterialTextureDerivativeMultiply" + OpMemberName %type_View 83 "View_Random" + OpMemberName %type_View 84 "View_FrameNumber" + OpMemberName %type_View 85 "View_StateFrameIndexMod8" + OpMemberName %type_View 86 "View_StateFrameIndex" + OpMemberName %type_View 87 "View_CameraCut" + OpMemberName %type_View 88 "View_UnlitViewmodeMask" + OpMemberName %type_View 89 "PrePadding_View_2164" + OpMemberName %type_View 90 "PrePadding_View_2168" + OpMemberName %type_View 91 "PrePadding_View_2172" + OpMemberName %type_View 92 "View_DirectionalLightColor" + OpMemberName %type_View 93 "View_DirectionalLightDirection" + OpMemberName %type_View 94 "PrePadding_View_2204" + OpMemberName %type_View 95 "View_TranslucencyLightingVolumeMin" + OpMemberName %type_View 96 "View_TranslucencyLightingVolumeInvSize" + OpMemberName %type_View 97 "View_TemporalAAParams" + OpMemberName %type_View 98 "View_CircleDOFParams" + OpMemberName %type_View 99 "View_DepthOfFieldSensorWidth" + OpMemberName %type_View 100 "View_DepthOfFieldFocalDistance" + OpMemberName %type_View 101 "View_DepthOfFieldScale" + OpMemberName %type_View 102 "View_DepthOfFieldFocalLength" + OpMemberName %type_View 103 "View_DepthOfFieldFocalRegion" + OpMemberName %type_View 104 "View_DepthOfFieldNearTransitionRegion" + OpMemberName %type_View 105 "View_DepthOfFieldFarTransitionRegion" + OpMemberName %type_View 106 "View_MotionBlurNormalizedToPixel" + OpMemberName %type_View 107 "View_bSubsurfacePostprocessEnabled" + OpMemberName %type_View 108 "View_GeneralPurposeTweak" + OpMemberName %type_View 109 "View_DemosaicVposOffset" + OpMemberName %type_View 110 "PrePadding_View_2348" + OpMemberName %type_View 111 "View_IndirectLightingColorScale" + OpMemberName %type_View 112 "View_HDR32bppEncodingMode" + OpMemberName %type_View 113 "View_AtmosphericFogSunDirection" + OpMemberName %type_View 114 "View_AtmosphericFogSunPower" + OpMemberName %type_View 115 "View_AtmosphericFogPower" + OpMemberName %type_View 116 "View_AtmosphericFogDensityScale" + OpMemberName %type_View 117 "View_AtmosphericFogDensityOffset" + OpMemberName %type_View 118 "View_AtmosphericFogGroundOffset" + OpMemberName %type_View 119 "View_AtmosphericFogDistanceScale" + OpMemberName %type_View 120 "View_AtmosphericFogAltitudeScale" + OpMemberName %type_View 121 "View_AtmosphericFogHeightScaleRayleigh" + OpMemberName %type_View 122 "View_AtmosphericFogStartDistance" + OpMemberName %type_View 123 "View_AtmosphericFogDistanceOffset" + OpMemberName %type_View 124 "View_AtmosphericFogSunDiscScale" + OpMemberName %type_View 125 "View_AtmosphericFogRenderMask" + OpMemberName %type_View 126 "View_AtmosphericFogInscatterAltitudeSampleNum" + OpMemberName %type_View 127 "View_AtmosphericFogSunColor" + OpMemberName %type_View 128 "View_NormalCurvatureToRoughnessScaleBias" + OpMemberName %type_View 129 "View_RenderingReflectionCaptureMask" + OpMemberName %type_View 130 "View_AmbientCubemapTint" + OpMemberName %type_View 131 "View_AmbientCubemapIntensity" + OpMemberName %type_View 132 "View_SkyLightParameters" + OpMemberName %type_View 133 "PrePadding_View_2488" + OpMemberName %type_View 134 "PrePadding_View_2492" + OpMemberName %type_View 135 "View_SkyLightColor" + OpMemberName %type_View 136 "View_SkyIrradianceEnvironmentMap" + OpMemberName %type_View 137 "View_MobilePreviewMode" + OpMemberName %type_View 138 "View_HMDEyePaddingOffset" + OpMemberName %type_View 139 "View_ReflectionCubemapMaxMip" + OpMemberName %type_View 140 "View_ShowDecalsMask" + OpMemberName %type_View 141 "View_DistanceFieldAOSpecularOcclusionMode" + OpMemberName %type_View 142 "View_IndirectCapsuleSelfShadowingIntensity" + OpMemberName %type_View 143 "PrePadding_View_2648" + OpMemberName %type_View 144 "PrePadding_View_2652" + OpMemberName %type_View 145 "View_ReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight" + OpMemberName %type_View 146 "View_StereoPassIndex" + OpMemberName %type_View 147 "View_GlobalVolumeCenterAndExtent" + OpMemberName %type_View 148 "View_GlobalVolumeWorldToUVAddAndMul" + OpMemberName %type_View 149 "View_GlobalVolumeDimension" + OpMemberName %type_View 150 "View_GlobalVolumeTexelSize" + OpMemberName %type_View 151 "View_MaxGlobalDistance" + OpMemberName %type_View 152 "View_bCheckerboardSubsurfaceProfileRendering" + OpMemberName %type_View 153 "View_VolumetricFogInvGridSize" + OpMemberName %type_View 154 "PrePadding_View_2828" + OpMemberName %type_View 155 "View_VolumetricFogGridZParams" + OpMemberName %type_View 156 "PrePadding_View_2844" + OpMemberName %type_View 157 "View_VolumetricFogSVPosToVolumeUV" + OpMemberName %type_View 158 "View_VolumetricFogMaxDistance" + OpMemberName %type_View 159 "PrePadding_View_2860" + OpMemberName %type_View 160 "View_VolumetricLightmapWorldToUVScale" + OpMemberName %type_View 161 "PrePadding_View_2876" + OpMemberName %type_View 162 "View_VolumetricLightmapWorldToUVAdd" + OpMemberName %type_View 163 "PrePadding_View_2892" + OpMemberName %type_View 164 "View_VolumetricLightmapIndirectionTextureSize" + OpMemberName %type_View 165 "View_VolumetricLightmapBrickSize" + OpMemberName %type_View 166 "View_VolumetricLightmapBrickTexelSize" + OpMemberName %type_View 167 "View_StereoIPD" + OpMemberName %type_View 168 "View_IndirectLightingCacheShowFlag" + OpMemberName %type_View 169 "View_EyeToPixelSpreadAngle" + OpName %View "View" + OpName %type_Primitive "type.Primitive" + OpMemberName %type_Primitive 0 "Primitive_LocalToWorld" + OpMemberName %type_Primitive 1 "Primitive_InvNonUniformScaleAndDeterminantSign" + OpMemberName %type_Primitive 2 "Primitive_ObjectWorldPositionAndRadius" + OpMemberName %type_Primitive 3 "Primitive_WorldToLocal" + OpMemberName %type_Primitive 4 "Primitive_PreviousLocalToWorld" + OpMemberName %type_Primitive 5 "Primitive_PreviousWorldToLocal" + OpMemberName %type_Primitive 6 "Primitive_ActorWorldPosition" + OpMemberName %type_Primitive 7 "Primitive_UseSingleSampleShadowFromStationaryLights" + OpMemberName %type_Primitive 8 "Primitive_ObjectBounds" + OpMemberName %type_Primitive 9 "Primitive_LpvBiasMultiplier" + OpMemberName %type_Primitive 10 "Primitive_DecalReceiverMask" + OpMemberName %type_Primitive 11 "Primitive_PerObjectGBufferData" + OpMemberName %type_Primitive 12 "Primitive_UseVolumetricLightmapShadowFromStationaryLights" + OpMemberName %type_Primitive 13 "Primitive_UseEditorDepthTest" + OpMemberName %type_Primitive 14 "Primitive_ObjectOrientation" + OpMemberName %type_Primitive 15 "Primitive_NonUniformScale" + OpMemberName %type_Primitive 16 "Primitive_LocalObjectBoundsMin" + OpMemberName %type_Primitive 17 "PrePadding_Primitive_380" + OpMemberName %type_Primitive 18 "Primitive_LocalObjectBoundsMax" + OpMemberName %type_Primitive 19 "Primitive_LightingChannelMask" + OpMemberName %type_Primitive 20 "Primitive_LightmapDataIndex" + OpMemberName %type_Primitive 21 "Primitive_SingleCaptureIndex" + OpName %Primitive "Primitive" + OpName %type_MobileShadowDepthPass "type.MobileShadowDepthPass" + OpMemberName %type_MobileShadowDepthPass 0 "PrePadding_MobileShadowDepthPass_0" + OpMemberName %type_MobileShadowDepthPass 1 "PrePadding_MobileShadowDepthPass_4" + OpMemberName %type_MobileShadowDepthPass 2 "PrePadding_MobileShadowDepthPass_8" + OpMemberName %type_MobileShadowDepthPass 3 "PrePadding_MobileShadowDepthPass_12" + OpMemberName %type_MobileShadowDepthPass 4 "PrePadding_MobileShadowDepthPass_16" + OpMemberName %type_MobileShadowDepthPass 5 "PrePadding_MobileShadowDepthPass_20" + OpMemberName %type_MobileShadowDepthPass 6 "PrePadding_MobileShadowDepthPass_24" + OpMemberName %type_MobileShadowDepthPass 7 "PrePadding_MobileShadowDepthPass_28" + OpMemberName %type_MobileShadowDepthPass 8 "PrePadding_MobileShadowDepthPass_32" + OpMemberName %type_MobileShadowDepthPass 9 "PrePadding_MobileShadowDepthPass_36" + OpMemberName %type_MobileShadowDepthPass 10 "PrePadding_MobileShadowDepthPass_40" + OpMemberName %type_MobileShadowDepthPass 11 "PrePadding_MobileShadowDepthPass_44" + OpMemberName %type_MobileShadowDepthPass 12 "PrePadding_MobileShadowDepthPass_48" + OpMemberName %type_MobileShadowDepthPass 13 "PrePadding_MobileShadowDepthPass_52" + OpMemberName %type_MobileShadowDepthPass 14 "PrePadding_MobileShadowDepthPass_56" + OpMemberName %type_MobileShadowDepthPass 15 "PrePadding_MobileShadowDepthPass_60" + OpMemberName %type_MobileShadowDepthPass 16 "PrePadding_MobileShadowDepthPass_64" + OpMemberName %type_MobileShadowDepthPass 17 "PrePadding_MobileShadowDepthPass_68" + OpMemberName %type_MobileShadowDepthPass 18 "PrePadding_MobileShadowDepthPass_72" + OpMemberName %type_MobileShadowDepthPass 19 "PrePadding_MobileShadowDepthPass_76" + OpMemberName %type_MobileShadowDepthPass 20 "MobileShadowDepthPass_ProjectionMatrix" + OpMemberName %type_MobileShadowDepthPass 21 "MobileShadowDepthPass_ShadowParams" + OpMemberName %type_MobileShadowDepthPass 22 "MobileShadowDepthPass_bClampToNearPlane" + OpMemberName %type_MobileShadowDepthPass 23 "PrePadding_MobileShadowDepthPass_156" + OpMemberName %type_MobileShadowDepthPass 24 "MobileShadowDepthPass_ShadowViewProjectionMatrices" + OpName %MobileShadowDepthPass "MobileShadowDepthPass" + OpName %type_EmitterDynamicUniforms "type.EmitterDynamicUniforms" + OpMemberName %type_EmitterDynamicUniforms 0 "EmitterDynamicUniforms_LocalToWorldScale" + OpMemberName %type_EmitterDynamicUniforms 1 "EmitterDynamicUniforms_EmitterInstRandom" + OpMemberName %type_EmitterDynamicUniforms 2 "PrePadding_EmitterDynamicUniforms_12" + OpMemberName %type_EmitterDynamicUniforms 3 "EmitterDynamicUniforms_AxisLockRight" + OpMemberName %type_EmitterDynamicUniforms 4 "EmitterDynamicUniforms_AxisLockUp" + OpMemberName %type_EmitterDynamicUniforms 5 "EmitterDynamicUniforms_DynamicColor" + OpMemberName %type_EmitterDynamicUniforms 6 "EmitterDynamicUniforms_MacroUVParameters" + OpName %EmitterDynamicUniforms "EmitterDynamicUniforms" + OpName %type_EmitterUniforms "type.EmitterUniforms" + OpMemberName %type_EmitterUniforms 0 "EmitterUniforms_ColorCurve" + OpMemberName %type_EmitterUniforms 1 "EmitterUniforms_ColorScale" + OpMemberName %type_EmitterUniforms 2 "EmitterUniforms_ColorBias" + OpMemberName %type_EmitterUniforms 3 "EmitterUniforms_MiscCurve" + OpMemberName %type_EmitterUniforms 4 "EmitterUniforms_MiscScale" + OpMemberName %type_EmitterUniforms 5 "EmitterUniforms_MiscBias" + OpMemberName %type_EmitterUniforms 6 "EmitterUniforms_SizeBySpeed" + OpMemberName %type_EmitterUniforms 7 "EmitterUniforms_SubImageSize" + OpMemberName %type_EmitterUniforms 8 "EmitterUniforms_TangentSelector" + OpMemberName %type_EmitterUniforms 9 "EmitterUniforms_CameraFacingBlend" + OpMemberName %type_EmitterUniforms 10 "EmitterUniforms_RemoveHMDRoll" + OpMemberName %type_EmitterUniforms 11 "EmitterUniforms_RotationRateScale" + OpMemberName %type_EmitterUniforms 12 "EmitterUniforms_RotationBias" + OpMemberName %type_EmitterUniforms 13 "EmitterUniforms_CameraMotionBlurAmount" + OpMemberName %type_EmitterUniforms 14 "PrePadding_EmitterUniforms_172" + OpMemberName %type_EmitterUniforms 15 "EmitterUniforms_PivotOffset" + OpName %EmitterUniforms "EmitterUniforms" + OpName %type_buffer_image "type.buffer.image" + OpName %ParticleIndices "ParticleIndices" + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "ParticleIndicesOffset" + OpName %_Globals "$Globals" + OpName %type_2d_image "type.2d.image" + OpName %PositionTexture "PositionTexture" + OpName %type_sampler "type.sampler" + OpName %PositionTextureSampler "PositionTextureSampler" + OpName %VelocityTexture "VelocityTexture" + OpName %VelocityTextureSampler "VelocityTextureSampler" + OpName %AttributesTexture "AttributesTexture" + OpName %AttributesTextureSampler "AttributesTextureSampler" + OpName %CurveTexture "CurveTexture" + OpName %CurveTextureSampler "CurveTextureSampler" + OpName %in_var_ATTRIBUTE0 "in.var.ATTRIBUTE0" + OpName %out_var_TEXCOORD6 "out.var.TEXCOORD6" + OpName %Main "Main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpDecorateString %gl_VertexIndex UserSemantic "SV_VertexID" + OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex + OpDecorateString %gl_InstanceIndex UserSemantic "SV_InstanceID" + OpDecorateString %in_var_ATTRIBUTE0 UserSemantic "ATTRIBUTE0" + OpDecorateString %out_var_TEXCOORD6 UserSemantic "TEXCOORD6" + OpDecorate %gl_Position BuiltIn Position + OpDecorateString %gl_Position UserSemantic "SV_POSITION" + OpDecorate %in_var_ATTRIBUTE0 Location 0 + OpDecorate %out_var_TEXCOORD6 Location 0 + OpDecorate %View DescriptorSet 0 + OpDecorate %View Binding 1 + OpDecorate %Primitive DescriptorSet 0 + OpDecorate %Primitive Binding 2 + OpDecorate %MobileShadowDepthPass DescriptorSet 0 + OpDecorate %MobileShadowDepthPass Binding 3 + OpDecorate %EmitterDynamicUniforms DescriptorSet 0 + OpDecorate %EmitterDynamicUniforms Binding 4 + OpDecorate %EmitterUniforms DescriptorSet 0 + OpDecorate %EmitterUniforms Binding 5 + OpDecorate %ParticleIndices DescriptorSet 0 + OpDecorate %ParticleIndices Binding 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 6 + OpDecorate %PositionTexture DescriptorSet 0 + OpDecorate %PositionTexture Binding 1 + OpDecorate %PositionTextureSampler DescriptorSet 0 + OpDecorate %PositionTextureSampler Binding 0 + OpDecorate %VelocityTexture DescriptorSet 0 + OpDecorate %VelocityTexture Binding 2 + OpDecorate %VelocityTextureSampler DescriptorSet 0 + OpDecorate %VelocityTextureSampler Binding 1 + OpDecorate %AttributesTexture DescriptorSet 0 + OpDecorate %AttributesTexture Binding 3 + OpDecorate %AttributesTextureSampler DescriptorSet 0 + OpDecorate %AttributesTextureSampler Binding 2 + OpDecorate %CurveTexture DescriptorSet 0 + OpDecorate %CurveTexture Binding 4 + OpDecorate %CurveTextureSampler DescriptorSet 0 + OpDecorate %CurveTextureSampler Binding 3 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr_v4float_uint_7 ArrayStride 16 + OpDecorate %_arr_v4float_uint_4 ArrayStride 16 + OpMemberDecorate %type_View 0 Offset 0 + OpMemberDecorate %type_View 0 MatrixStride 16 + OpMemberDecorate %type_View 0 ColMajor + OpMemberDecorate %type_View 1 Offset 64 + OpMemberDecorate %type_View 1 MatrixStride 16 + OpMemberDecorate %type_View 1 ColMajor + OpMemberDecorate %type_View 2 Offset 128 + OpMemberDecorate %type_View 2 MatrixStride 16 + OpMemberDecorate %type_View 2 ColMajor + OpMemberDecorate %type_View 3 Offset 192 + OpMemberDecorate %type_View 3 MatrixStride 16 + OpMemberDecorate %type_View 3 ColMajor + OpMemberDecorate %type_View 4 Offset 256 + OpMemberDecorate %type_View 4 MatrixStride 16 + OpMemberDecorate %type_View 4 ColMajor + OpMemberDecorate %type_View 5 Offset 320 + OpMemberDecorate %type_View 5 MatrixStride 16 + OpMemberDecorate %type_View 5 ColMajor + OpMemberDecorate %type_View 6 Offset 384 + OpMemberDecorate %type_View 6 MatrixStride 16 + OpMemberDecorate %type_View 6 ColMajor + OpMemberDecorate %type_View 7 Offset 448 + OpMemberDecorate %type_View 7 MatrixStride 16 + OpMemberDecorate %type_View 7 ColMajor + OpMemberDecorate %type_View 8 Offset 512 + OpMemberDecorate %type_View 8 MatrixStride 16 + OpMemberDecorate %type_View 8 ColMajor + OpMemberDecorate %type_View 9 Offset 576 + OpMemberDecorate %type_View 9 MatrixStride 16 + OpMemberDecorate %type_View 9 ColMajor + OpMemberDecorate %type_View 10 Offset 640 + OpMemberDecorate %type_View 10 MatrixStride 16 + OpMemberDecorate %type_View 10 ColMajor + OpMemberDecorate %type_View 11 Offset 704 + OpMemberDecorate %type_View 11 MatrixStride 16 + OpMemberDecorate %type_View 11 ColMajor + OpMemberDecorate %type_View 12 Offset 768 + OpMemberDecorate %type_View 12 MatrixStride 16 + OpMemberDecorate %type_View 12 ColMajor + OpMemberDecorate %type_View 13 Offset 832 + OpMemberDecorate %type_View 14 Offset 844 + OpMemberDecorate %type_View 15 Offset 848 + OpMemberDecorate %type_View 16 Offset 860 + OpMemberDecorate %type_View 17 Offset 864 + OpMemberDecorate %type_View 18 Offset 876 + OpMemberDecorate %type_View 19 Offset 880 + OpMemberDecorate %type_View 20 Offset 892 + OpMemberDecorate %type_View 21 Offset 896 + OpMemberDecorate %type_View 22 Offset 908 + OpMemberDecorate %type_View 23 Offset 912 + OpMemberDecorate %type_View 24 Offset 928 + OpMemberDecorate %type_View 25 Offset 944 + OpMemberDecorate %type_View 26 Offset 956 + OpMemberDecorate %type_View 27 Offset 960 + OpMemberDecorate %type_View 28 Offset 972 + OpMemberDecorate %type_View 29 Offset 976 + OpMemberDecorate %type_View 30 Offset 988 + OpMemberDecorate %type_View 31 Offset 992 + OpMemberDecorate %type_View 32 Offset 1004 + OpMemberDecorate %type_View 33 Offset 1008 + OpMemberDecorate %type_View 33 MatrixStride 16 + OpMemberDecorate %type_View 33 ColMajor + OpMemberDecorate %type_View 34 Offset 1072 + OpMemberDecorate %type_View 34 MatrixStride 16 + OpMemberDecorate %type_View 34 ColMajor + OpMemberDecorate %type_View 35 Offset 1136 + OpMemberDecorate %type_View 35 MatrixStride 16 + OpMemberDecorate %type_View 35 ColMajor + OpMemberDecorate %type_View 36 Offset 1200 + OpMemberDecorate %type_View 36 MatrixStride 16 + OpMemberDecorate %type_View 36 ColMajor + OpMemberDecorate %type_View 37 Offset 1264 + OpMemberDecorate %type_View 37 MatrixStride 16 + OpMemberDecorate %type_View 37 ColMajor + OpMemberDecorate %type_View 38 Offset 1328 + OpMemberDecorate %type_View 38 MatrixStride 16 + OpMemberDecorate %type_View 38 ColMajor + OpMemberDecorate %type_View 39 Offset 1392 + OpMemberDecorate %type_View 39 MatrixStride 16 + OpMemberDecorate %type_View 39 ColMajor + OpMemberDecorate %type_View 40 Offset 1456 + OpMemberDecorate %type_View 40 MatrixStride 16 + OpMemberDecorate %type_View 40 ColMajor + OpMemberDecorate %type_View 41 Offset 1520 + OpMemberDecorate %type_View 41 MatrixStride 16 + OpMemberDecorate %type_View 41 ColMajor + OpMemberDecorate %type_View 42 Offset 1584 + OpMemberDecorate %type_View 42 MatrixStride 16 + OpMemberDecorate %type_View 42 ColMajor + OpMemberDecorate %type_View 43 Offset 1648 + OpMemberDecorate %type_View 44 Offset 1660 + OpMemberDecorate %type_View 45 Offset 1664 + OpMemberDecorate %type_View 46 Offset 1676 + OpMemberDecorate %type_View 47 Offset 1680 + OpMemberDecorate %type_View 48 Offset 1692 + OpMemberDecorate %type_View 49 Offset 1696 + OpMemberDecorate %type_View 49 MatrixStride 16 + OpMemberDecorate %type_View 49 ColMajor + OpMemberDecorate %type_View 50 Offset 1760 + OpMemberDecorate %type_View 50 MatrixStride 16 + OpMemberDecorate %type_View 50 ColMajor + OpMemberDecorate %type_View 51 Offset 1824 + OpMemberDecorate %type_View 51 MatrixStride 16 + OpMemberDecorate %type_View 51 ColMajor + OpMemberDecorate %type_View 52 Offset 1888 + OpMemberDecorate %type_View 53 Offset 1904 + OpMemberDecorate %type_View 54 Offset 1920 + OpMemberDecorate %type_View 55 Offset 1928 + OpMemberDecorate %type_View 56 Offset 1936 + OpMemberDecorate %type_View 57 Offset 1952 + OpMemberDecorate %type_View 58 Offset 1968 + OpMemberDecorate %type_View 59 Offset 1984 + OpMemberDecorate %type_View 60 Offset 2000 + OpMemberDecorate %type_View 61 Offset 2004 + OpMemberDecorate %type_View 62 Offset 2008 + OpMemberDecorate %type_View 63 Offset 2012 + OpMemberDecorate %type_View 64 Offset 2016 + OpMemberDecorate %type_View 65 Offset 2032 + OpMemberDecorate %type_View 66 Offset 2048 + OpMemberDecorate %type_View 67 Offset 2064 + OpMemberDecorate %type_View 68 Offset 2072 + OpMemberDecorate %type_View 69 Offset 2076 + OpMemberDecorate %type_View 70 Offset 2080 + OpMemberDecorate %type_View 71 Offset 2084 + OpMemberDecorate %type_View 72 Offset 2088 + OpMemberDecorate %type_View 73 Offset 2092 + OpMemberDecorate %type_View 74 Offset 2096 + OpMemberDecorate %type_View 75 Offset 2108 + OpMemberDecorate %type_View 76 Offset 2112 + OpMemberDecorate %type_View 77 Offset 2116 + OpMemberDecorate %type_View 78 Offset 2120 + OpMemberDecorate %type_View 79 Offset 2124 + OpMemberDecorate %type_View 80 Offset 2128 + OpMemberDecorate %type_View 81 Offset 2132 + OpMemberDecorate %type_View 82 Offset 2136 + OpMemberDecorate %type_View 83 Offset 2140 + OpMemberDecorate %type_View 84 Offset 2144 + OpMemberDecorate %type_View 85 Offset 2148 + OpMemberDecorate %type_View 86 Offset 2152 + OpMemberDecorate %type_View 87 Offset 2156 + OpMemberDecorate %type_View 88 Offset 2160 + OpMemberDecorate %type_View 89 Offset 2164 + OpMemberDecorate %type_View 90 Offset 2168 + OpMemberDecorate %type_View 91 Offset 2172 + OpMemberDecorate %type_View 92 Offset 2176 + OpMemberDecorate %type_View 93 Offset 2192 + OpMemberDecorate %type_View 94 Offset 2204 + OpMemberDecorate %type_View 95 Offset 2208 + OpMemberDecorate %type_View 96 Offset 2240 + OpMemberDecorate %type_View 97 Offset 2272 + OpMemberDecorate %type_View 98 Offset 2288 + OpMemberDecorate %type_View 99 Offset 2304 + OpMemberDecorate %type_View 100 Offset 2308 + OpMemberDecorate %type_View 101 Offset 2312 + OpMemberDecorate %type_View 102 Offset 2316 + OpMemberDecorate %type_View 103 Offset 2320 + OpMemberDecorate %type_View 104 Offset 2324 + OpMemberDecorate %type_View 105 Offset 2328 + OpMemberDecorate %type_View 106 Offset 2332 + OpMemberDecorate %type_View 107 Offset 2336 + OpMemberDecorate %type_View 108 Offset 2340 + OpMemberDecorate %type_View 109 Offset 2344 + OpMemberDecorate %type_View 110 Offset 2348 + OpMemberDecorate %type_View 111 Offset 2352 + OpMemberDecorate %type_View 112 Offset 2364 + OpMemberDecorate %type_View 113 Offset 2368 + OpMemberDecorate %type_View 114 Offset 2380 + OpMemberDecorate %type_View 115 Offset 2384 + OpMemberDecorate %type_View 116 Offset 2388 + OpMemberDecorate %type_View 117 Offset 2392 + OpMemberDecorate %type_View 118 Offset 2396 + OpMemberDecorate %type_View 119 Offset 2400 + OpMemberDecorate %type_View 120 Offset 2404 + OpMemberDecorate %type_View 121 Offset 2408 + OpMemberDecorate %type_View 122 Offset 2412 + OpMemberDecorate %type_View 123 Offset 2416 + OpMemberDecorate %type_View 124 Offset 2420 + OpMemberDecorate %type_View 125 Offset 2424 + OpMemberDecorate %type_View 126 Offset 2428 + OpMemberDecorate %type_View 127 Offset 2432 + OpMemberDecorate %type_View 128 Offset 2448 + OpMemberDecorate %type_View 129 Offset 2460 + OpMemberDecorate %type_View 130 Offset 2464 + OpMemberDecorate %type_View 131 Offset 2480 + OpMemberDecorate %type_View 132 Offset 2484 + OpMemberDecorate %type_View 133 Offset 2488 + OpMemberDecorate %type_View 134 Offset 2492 + OpMemberDecorate %type_View 135 Offset 2496 + OpMemberDecorate %type_View 136 Offset 2512 + OpMemberDecorate %type_View 137 Offset 2624 + OpMemberDecorate %type_View 138 Offset 2628 + OpMemberDecorate %type_View 139 Offset 2632 + OpMemberDecorate %type_View 140 Offset 2636 + OpMemberDecorate %type_View 141 Offset 2640 + OpMemberDecorate %type_View 142 Offset 2644 + OpMemberDecorate %type_View 143 Offset 2648 + OpMemberDecorate %type_View 144 Offset 2652 + OpMemberDecorate %type_View 145 Offset 2656 + OpMemberDecorate %type_View 146 Offset 2668 + OpMemberDecorate %type_View 147 Offset 2672 + OpMemberDecorate %type_View 148 Offset 2736 + OpMemberDecorate %type_View 149 Offset 2800 + OpMemberDecorate %type_View 150 Offset 2804 + OpMemberDecorate %type_View 151 Offset 2808 + OpMemberDecorate %type_View 152 Offset 2812 + OpMemberDecorate %type_View 153 Offset 2816 + OpMemberDecorate %type_View 154 Offset 2828 + OpMemberDecorate %type_View 155 Offset 2832 + OpMemberDecorate %type_View 156 Offset 2844 + OpMemberDecorate %type_View 157 Offset 2848 + OpMemberDecorate %type_View 158 Offset 2856 + OpMemberDecorate %type_View 159 Offset 2860 + OpMemberDecorate %type_View 160 Offset 2864 + OpMemberDecorate %type_View 161 Offset 2876 + OpMemberDecorate %type_View 162 Offset 2880 + OpMemberDecorate %type_View 163 Offset 2892 + OpMemberDecorate %type_View 164 Offset 2896 + OpMemberDecorate %type_View 165 Offset 2908 + OpMemberDecorate %type_View 166 Offset 2912 + OpMemberDecorate %type_View 167 Offset 2924 + OpMemberDecorate %type_View 168 Offset 2928 + OpMemberDecorate %type_View 169 Offset 2932 + OpDecorate %type_View Block + OpMemberDecorate %type_Primitive 0 Offset 0 + OpMemberDecorate %type_Primitive 0 MatrixStride 16 + OpMemberDecorate %type_Primitive 0 ColMajor + OpMemberDecorate %type_Primitive 1 Offset 64 + OpMemberDecorate %type_Primitive 2 Offset 80 + OpMemberDecorate %type_Primitive 3 Offset 96 + OpMemberDecorate %type_Primitive 3 MatrixStride 16 + OpMemberDecorate %type_Primitive 3 ColMajor + OpMemberDecorate %type_Primitive 4 Offset 160 + OpMemberDecorate %type_Primitive 4 MatrixStride 16 + OpMemberDecorate %type_Primitive 4 ColMajor + OpMemberDecorate %type_Primitive 5 Offset 224 + OpMemberDecorate %type_Primitive 5 MatrixStride 16 + OpMemberDecorate %type_Primitive 5 ColMajor + OpMemberDecorate %type_Primitive 6 Offset 288 + OpMemberDecorate %type_Primitive 7 Offset 300 + OpMemberDecorate %type_Primitive 8 Offset 304 + OpMemberDecorate %type_Primitive 9 Offset 316 + OpMemberDecorate %type_Primitive 10 Offset 320 + OpMemberDecorate %type_Primitive 11 Offset 324 + OpMemberDecorate %type_Primitive 12 Offset 328 + OpMemberDecorate %type_Primitive 13 Offset 332 + OpMemberDecorate %type_Primitive 14 Offset 336 + OpMemberDecorate %type_Primitive 15 Offset 352 + OpMemberDecorate %type_Primitive 16 Offset 368 + OpMemberDecorate %type_Primitive 17 Offset 380 + OpMemberDecorate %type_Primitive 18 Offset 384 + OpMemberDecorate %type_Primitive 19 Offset 396 + OpMemberDecorate %type_Primitive 20 Offset 400 + OpMemberDecorate %type_Primitive 21 Offset 404 + OpDecorate %type_Primitive Block + OpDecorate %_arr_mat4v4float_uint_6 ArrayStride 64 + OpMemberDecorate %type_MobileShadowDepthPass 0 Offset 0 + OpMemberDecorate %type_MobileShadowDepthPass 1 Offset 4 + OpMemberDecorate %type_MobileShadowDepthPass 2 Offset 8 + OpMemberDecorate %type_MobileShadowDepthPass 3 Offset 12 + OpMemberDecorate %type_MobileShadowDepthPass 4 Offset 16 + OpMemberDecorate %type_MobileShadowDepthPass 5 Offset 20 + OpMemberDecorate %type_MobileShadowDepthPass 6 Offset 24 + OpMemberDecorate %type_MobileShadowDepthPass 7 Offset 28 + OpMemberDecorate %type_MobileShadowDepthPass 8 Offset 32 + OpMemberDecorate %type_MobileShadowDepthPass 9 Offset 36 + OpMemberDecorate %type_MobileShadowDepthPass 10 Offset 40 + OpMemberDecorate %type_MobileShadowDepthPass 11 Offset 44 + OpMemberDecorate %type_MobileShadowDepthPass 12 Offset 48 + OpMemberDecorate %type_MobileShadowDepthPass 13 Offset 52 + OpMemberDecorate %type_MobileShadowDepthPass 14 Offset 56 + OpMemberDecorate %type_MobileShadowDepthPass 15 Offset 60 + OpMemberDecorate %type_MobileShadowDepthPass 16 Offset 64 + OpMemberDecorate %type_MobileShadowDepthPass 17 Offset 68 + OpMemberDecorate %type_MobileShadowDepthPass 18 Offset 72 + OpMemberDecorate %type_MobileShadowDepthPass 19 Offset 76 + OpMemberDecorate %type_MobileShadowDepthPass 20 Offset 80 + OpMemberDecorate %type_MobileShadowDepthPass 20 MatrixStride 16 + OpMemberDecorate %type_MobileShadowDepthPass 20 ColMajor + OpMemberDecorate %type_MobileShadowDepthPass 21 Offset 144 + OpMemberDecorate %type_MobileShadowDepthPass 22 Offset 152 + OpMemberDecorate %type_MobileShadowDepthPass 23 Offset 156 + OpMemberDecorate %type_MobileShadowDepthPass 24 Offset 160 + OpMemberDecorate %type_MobileShadowDepthPass 24 MatrixStride 16 + OpMemberDecorate %type_MobileShadowDepthPass 24 ColMajor + OpDecorate %type_MobileShadowDepthPass Block + OpMemberDecorate %type_EmitterDynamicUniforms 0 Offset 0 + OpMemberDecorate %type_EmitterDynamicUniforms 1 Offset 8 + OpMemberDecorate %type_EmitterDynamicUniforms 2 Offset 12 + OpMemberDecorate %type_EmitterDynamicUniforms 3 Offset 16 + OpMemberDecorate %type_EmitterDynamicUniforms 4 Offset 32 + OpMemberDecorate %type_EmitterDynamicUniforms 5 Offset 48 + OpMemberDecorate %type_EmitterDynamicUniforms 6 Offset 64 + OpDecorate %type_EmitterDynamicUniforms Block + OpMemberDecorate %type_EmitterUniforms 0 Offset 0 + OpMemberDecorate %type_EmitterUniforms 1 Offset 16 + OpMemberDecorate %type_EmitterUniforms 2 Offset 32 + OpMemberDecorate %type_EmitterUniforms 3 Offset 48 + OpMemberDecorate %type_EmitterUniforms 4 Offset 64 + OpMemberDecorate %type_EmitterUniforms 5 Offset 80 + OpMemberDecorate %type_EmitterUniforms 6 Offset 96 + OpMemberDecorate %type_EmitterUniforms 7 Offset 112 + OpMemberDecorate %type_EmitterUniforms 8 Offset 128 + OpMemberDecorate %type_EmitterUniforms 9 Offset 144 + OpMemberDecorate %type_EmitterUniforms 10 Offset 156 + OpMemberDecorate %type_EmitterUniforms 11 Offset 160 + OpMemberDecorate %type_EmitterUniforms 12 Offset 164 + OpMemberDecorate %type_EmitterUniforms 13 Offset 168 + OpMemberDecorate %type_EmitterUniforms 14 Offset 172 + OpMemberDecorate %type_EmitterUniforms 15 Offset 176 + OpDecorate %type_EmitterUniforms Block + OpMemberDecorate %type__Globals 0 Offset 0 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %uint_7 = OpConstant %uint 7 + %uint_4 = OpConstant %uint 4 + %float_0 = OpConstant %float 0 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint_16 = OpConstant %uint 16 + %int_3 = OpConstant %int 3 + %float_1 = OpConstant %float 1 +%float_9_99999975en05 = OpConstant %float 9.99999975e-05 + %54 = OpConstantComposite %v3float %float_0 %float_0 %float_9_99999975en05 + %int_2 = OpConstant %int 2 + %int_5 = OpConstant %int 5 + %int_4 = OpConstant %int 4 + %float_0_5 = OpConstant %float 0.5 + %float_n0_5 = OpConstant %float -0.5 + %float_2 = OpConstant %float 2 + %61 = OpConstantComposite %v2float %float_2 %float_2 + %int_6 = OpConstant %int 6 + %63 = OpConstantComposite %v2float %float_1 %float_1 + %int_11 = OpConstant %int 11 + %int_15 = OpConstant %int 15 + %int_8 = OpConstant %int 8 + %int_9 = OpConstant %int 9 + %int_10 = OpConstant %int 10 + %int_12 = OpConstant %int 12 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 +%mat3v3float = OpTypeMatrix %v3float 3 + %int_20 = OpConstant %int 20 + %int_22 = OpConstant %int 22 +%float_9_99999997en07 = OpConstant %float 9.99999997e-07 + %int_21 = OpConstant %int 21 + %int_17 = OpConstant %int 17 + %int_19 = OpConstant %int 19 + %int_27 = OpConstant %int 27 + %int_31 = OpConstant %int 31 + %uint_3 = OpConstant %uint 3 + %82 = OpConstantComposite %v3float %float_0 %float_0 %float_1 +%float_0_00999999978 = OpConstant %float 0.00999999978 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr_v4float_uint_7 = OpTypeArray %v4float %uint_7 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 + %type_View = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v4float %v4float %v3float %float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %v3float %float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %v2float %v2float %v4float %v4float %v4float %v4float %int %float %float %float %v4float %v4float %v4float %v2float %float %float %float %float %float %float %v3float %float %float %float %float %float %float %float %float %uint %uint %uint %uint %float %float %float %float %float %v4float %v3float %float %_arr_v4float_uint_2 %_arr_v4float_uint_2 %v4float %v4float %float %float %float %float %float %float %float %float %float %float %float %float %v3float %float %v3float %float %float %float %float %float %float %float %float %float %float %float %uint %uint %v4float %v3float %float %v4float %float %float %float %float %v4float %_arr_v4float_uint_7 %float %float %float %float %uint %float %float %float %v3float %int %_arr_v4float_uint_4 %_arr_v4float_uint_4 %float %float %float %float %v3float %float %v3float %float %v2float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float +%_ptr_Uniform_type_View = OpTypePointer Uniform %type_View +%type_Primitive = OpTypeStruct %mat4v4float %v4float %v4float %mat4v4float %mat4v4float %mat4v4float %v3float %float %v3float %float %float %float %float %float %v4float %v4float %v3float %float %v3float %uint %uint %int +%_ptr_Uniform_type_Primitive = OpTypePointer Uniform %type_Primitive + %uint_6 = OpConstant %uint 6 +%_arr_mat4v4float_uint_6 = OpTypeArray %mat4v4float %uint_6 +%type_MobileShadowDepthPass = OpTypeStruct %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %float %mat4v4float %v2float %float %float %_arr_mat4v4float_uint_6 +%_ptr_Uniform_type_MobileShadowDepthPass = OpTypePointer Uniform %type_MobileShadowDepthPass +%type_EmitterDynamicUniforms = OpTypeStruct %v2float %float %float %v4float %v4float %v4float %v4float +%_ptr_Uniform_type_EmitterDynamicUniforms = OpTypePointer Uniform %type_EmitterDynamicUniforms +%type_EmitterUniforms = OpTypeStruct %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v4float %v3float %float %float %float %float %float %v2float +%_ptr_Uniform_type_EmitterUniforms = OpTypePointer Uniform %type_EmitterUniforms +%type_buffer_image = OpTypeImage %float Buffer 2 0 0 1 Rg32f +%_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image +%type__Globals = OpTypeStruct %uint +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_ptr_Input_uint = OpTypePointer Input %uint +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %99 = OpTypeFunction %void +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %bool = OpTypeBool +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%type_sampled_image = OpTypeSampledImage %type_2d_image + %View = OpVariable %_ptr_Uniform_type_View Uniform + %Primitive = OpVariable %_ptr_Uniform_type_Primitive Uniform +%MobileShadowDepthPass = OpVariable %_ptr_Uniform_type_MobileShadowDepthPass Uniform +%EmitterDynamicUniforms = OpVariable %_ptr_Uniform_type_EmitterDynamicUniforms Uniform +%EmitterUniforms = OpVariable %_ptr_Uniform_type_EmitterUniforms Uniform +%ParticleIndices = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%PositionTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%PositionTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%VelocityTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%VelocityTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%AttributesTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%AttributesTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%CurveTexture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +%CurveTextureSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%gl_VertexIndex = OpVariable %_ptr_Input_uint Input +%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input +%in_var_ATTRIBUTE0 = OpVariable %_ptr_Input_v2float Input +%out_var_TEXCOORD6 = OpVariable %_ptr_Output_float Output +%gl_Position = OpVariable %_ptr_Output_v4float Output +%float_6_28318548 = OpConstant %float 6.28318548 + %108 = OpConstantNull %v3float + %Main = OpFunction %void None %99 + %109 = OpLabel + %110 = OpLoad %uint %gl_VertexIndex + %111 = OpLoad %uint %gl_InstanceIndex + %112 = OpLoad %v2float %in_var_ATTRIBUTE0 + %113 = OpAccessChain %_ptr_Uniform_v3float %View %int_15 + %114 = OpLoad %v3float %113 + %115 = OpAccessChain %_ptr_Uniform_v3float %View %int_17 + %116 = OpLoad %v3float %115 + %117 = OpAccessChain %_ptr_Uniform_v3float %View %int_19 + %118 = OpLoad %v3float %117 + %119 = OpAccessChain %_ptr_Uniform_v3float %View %int_21 + %120 = OpLoad %v3float %119 + %121 = OpAccessChain %_ptr_Uniform_v3float %View %int_27 + %122 = OpLoad %v3float %121 + %123 = OpAccessChain %_ptr_Uniform_v3float %View %int_31 + %124 = OpLoad %v3float %123 + %125 = OpIMul %uint %111 %uint_16 + %126 = OpUDiv %uint %110 %uint_4 + %127 = OpIAdd %uint %125 %126 + %128 = OpAccessChain %_ptr_Uniform_uint %_Globals %int_0 + %129 = OpLoad %uint %128 + %130 = OpIAdd %uint %129 %127 + %131 = OpLoad %type_buffer_image %ParticleIndices + %132 = OpImageFetch %v4float %131 %130 None + %133 = OpVectorShuffle %v2float %132 %132 0 1 + %134 = OpLoad %type_2d_image %PositionTexture + %135 = OpLoad %type_sampler %PositionTextureSampler + %136 = OpSampledImage %type_sampled_image %134 %135 + %137 = OpImageSampleExplicitLod %v4float %136 %133 Lod %float_0 + %138 = OpLoad %type_2d_image %VelocityTexture + %139 = OpLoad %type_sampler %VelocityTextureSampler + %140 = OpSampledImage %type_sampled_image %138 %139 + %141 = OpImageSampleExplicitLod %v4float %140 %133 Lod %float_0 + %142 = OpLoad %type_2d_image %AttributesTexture + %143 = OpLoad %type_sampler %AttributesTextureSampler + %144 = OpSampledImage %type_sampled_image %142 %143 + %145 = OpImageSampleExplicitLod %v4float %144 %133 Lod %float_0 + %146 = OpCompositeExtract %float %137 3 + %147 = OpExtInst %float %1 Step %146 %float_1 + %148 = OpVectorShuffle %v3float %141 %141 0 1 2 + %149 = OpAccessChain %_ptr_Uniform_mat4v4float %Primitive %int_0 + %150 = OpLoad %mat4v4float %149 + %151 = OpCompositeExtract %v4float %150 0 + %152 = OpVectorShuffle %v3float %151 %151 0 1 2 + %153 = OpCompositeExtract %v4float %150 1 + %154 = OpVectorShuffle %v3float %153 %153 0 1 2 + %155 = OpCompositeExtract %v4float %150 2 + %156 = OpVectorShuffle %v3float %155 %155 0 1 2 + %157 = OpCompositeConstruct %mat3v3float %152 %154 %156 + %158 = OpMatrixTimesVector %v3float %157 %148 + %159 = OpFAdd %v3float %158 %54 + %160 = OpExtInst %v3float %1 Normalize %159 + %161 = OpExtInst %float %1 Length %158 + %162 = OpAccessChain %_ptr_Uniform_v4float %EmitterUniforms %int_3 + %163 = OpLoad %v4float %162 + %164 = OpVectorShuffle %v2float %163 %163 0 1 + %165 = OpVectorShuffle %v2float %163 %163 2 3 + %166 = OpCompositeConstruct %v2float %146 %146 + %167 = OpFMul %v2float %165 %166 + %168 = OpFAdd %v2float %164 %167 + %169 = OpLoad %type_2d_image %CurveTexture + %170 = OpLoad %type_sampler %CurveTextureSampler + %171 = OpSampledImage %type_sampled_image %169 %170 + %172 = OpImageSampleExplicitLod %v4float %171 %168 Lod %float_0 + %173 = OpAccessChain %_ptr_Uniform_v4float %EmitterUniforms %int_4 + %174 = OpLoad %v4float %173 + %175 = OpFMul %v4float %172 %174 + %176 = OpAccessChain %_ptr_Uniform_v4float %EmitterUniforms %int_5 + %177 = OpLoad %v4float %176 + %178 = OpFAdd %v4float %175 %177 + %179 = OpCompositeExtract %float %145 0 + %180 = OpFOrdLessThan %bool %179 %float_0_5 + %181 = OpSelect %float %180 %float_0 %float_n0_5 + %182 = OpCompositeExtract %float %145 1 + %183 = OpFOrdLessThan %bool %182 %float_0_5 + %184 = OpSelect %float %183 %float_0 %float_n0_5 + %185 = OpCompositeConstruct %v2float %181 %184 + %186 = OpVectorShuffle %v2float %145 %145 0 1 + %187 = OpFAdd %v2float %186 %185 + %188 = OpFMul %v2float %187 %61 + %189 = OpVectorShuffle %v2float %178 %178 0 1 + %190 = OpAccessChain %_ptr_Uniform_v2float %EmitterDynamicUniforms %int_0 + %191 = OpLoad %v2float %190 + %192 = OpFMul %v2float %189 %191 + %193 = OpAccessChain %_ptr_Uniform_v4float %EmitterUniforms %int_6 + %194 = OpLoad %v4float %193 + %195 = OpVectorShuffle %v2float %194 %194 0 1 + %196 = OpCompositeConstruct %v2float %161 %161 + %197 = OpFMul %v2float %195 %196 + %198 = OpExtInst %v2float %1 FMax %197 %63 + %199 = OpVectorShuffle %v2float %194 %194 2 3 + %200 = OpExtInst %v2float %1 FMin %198 %199 + %201 = OpFMul %v2float %188 %192 + %202 = OpFMul %v2float %201 %200 + %203 = OpCompositeConstruct %v2float %147 %147 + %204 = OpFMul %v2float %202 %203 + %205 = OpCompositeExtract %float %145 3 + %206 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_11 + %207 = OpLoad %float %206 + %208 = OpFMul %float %205 %207 + %209 = OpCompositeExtract %float %145 2 + %210 = OpFMul %float %208 %146 + %211 = OpFAdd %float %209 %210 + %212 = OpFMul %float %211 %float_6_28318548 + %213 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_0 %uint_0 + %214 = OpLoad %v4float %213 + %215 = OpVectorShuffle %v3float %214 %214 0 1 2 + %216 = OpVectorShuffle %v3float %137 %108 0 0 0 + %217 = OpFMul %v3float %215 %216 + %218 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_0 %uint_1 + %219 = OpLoad %v4float %218 + %220 = OpVectorShuffle %v3float %219 %219 0 1 2 + %221 = OpVectorShuffle %v3float %137 %108 1 1 1 + %222 = OpFMul %v3float %220 %221 + %223 = OpFAdd %v3float %217 %222 + %224 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_0 %uint_2 + %225 = OpLoad %v4float %224 + %226 = OpVectorShuffle %v3float %225 %225 0 1 2 + %227 = OpVectorShuffle %v3float %137 %108 2 2 2 + %228 = OpFMul %v3float %226 %227 + %229 = OpFAdd %v3float %223 %228 + %230 = OpAccessChain %_ptr_Uniform_v4float %Primitive %int_0 %uint_3 + %231 = OpLoad %v4float %230 + %232 = OpVectorShuffle %v3float %231 %231 0 1 2 + %233 = OpFAdd %v3float %232 %124 + %234 = OpFAdd %v3float %229 %233 + %235 = OpCompositeExtract %float %234 0 + %236 = OpCompositeExtract %float %234 1 + %237 = OpCompositeExtract %float %234 2 + %238 = OpCompositeConstruct %v4float %235 %236 %237 %float_1 + %239 = OpVectorShuffle %v3float %238 %238 0 1 2 + %240 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_10 + %241 = OpLoad %float %240 + %242 = OpCompositeConstruct %v3float %241 %241 %241 + %243 = OpExtInst %v3float %1 FMix %116 %120 %242 + %244 = OpExtInst %v3float %1 FMix %114 %118 %242 + %245 = OpAccessChain %_ptr_Uniform_v4float %EmitterDynamicUniforms %int_3 + %246 = OpLoad %v4float %245 + %247 = OpVectorShuffle %v3float %246 %246 0 1 2 + %248 = OpAccessChain %_ptr_Uniform_float %EmitterDynamicUniforms %int_3 %int_3 + %249 = OpLoad %float %248 + %250 = OpCompositeConstruct %v3float %249 %249 %249 + %251 = OpExtInst %v3float %1 FMix %243 %247 %250 + %252 = OpFNegate %v3float %244 + %253 = OpAccessChain %_ptr_Uniform_v4float %EmitterDynamicUniforms %int_4 + %254 = OpLoad %v4float %253 + %255 = OpVectorShuffle %v3float %254 %254 0 1 2 + %256 = OpAccessChain %_ptr_Uniform_float %EmitterDynamicUniforms %int_4 %int_3 + %257 = OpLoad %float %256 + %258 = OpCompositeConstruct %v3float %257 %257 %257 + %259 = OpExtInst %v3float %1 FMix %252 %255 %258 + %260 = OpFSub %v3float %122 %239 + %261 = OpDot %float %260 %260 + %262 = OpExtInst %float %1 FMax %261 %float_0_00999999978 + %263 = OpExtInst %float %1 Sqrt %262 + %264 = OpCompositeConstruct %v3float %263 %263 %263 + %265 = OpFDiv %v3float %260 %264 + %266 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_9 %int_0 + %267 = OpLoad %float %266 + %268 = OpFOrdGreaterThan %bool %267 %float_0 + OpSelectionMerge %269 DontFlatten + OpBranchConditional %268 %270 %271 + %270 = OpLabel + %272 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_9 %int_1 + %273 = OpLoad %float %272 + %274 = OpFMul %float %261 %273 + %275 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_9 %int_2 + %276 = OpLoad %float %275 + %277 = OpFSub %float %274 %276 + %278 = OpExtInst %float %1 FClamp %277 %float_0 %float_1 + %279 = OpExtInst %v3float %1 Cross %265 %82 + %280 = OpDot %float %279 %279 + %281 = OpExtInst %float %1 FMax %280 %float_0_00999999978 + %282 = OpExtInst %float %1 Sqrt %281 + %283 = OpCompositeConstruct %v3float %282 %282 %282 + %284 = OpFDiv %v3float %279 %283 + %285 = OpExtInst %v3float %1 Cross %265 %284 + %286 = OpCompositeConstruct %v3float %278 %278 %278 + %287 = OpExtInst %v3float %1 FMix %251 %284 %286 + %288 = OpExtInst %v3float %1 Normalize %287 + %289 = OpExtInst %v3float %1 FMix %259 %285 %286 + %290 = OpExtInst %v3float %1 Normalize %289 + OpBranch %269 + %271 = OpLabel + %291 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_8 %int_1 + %292 = OpLoad %float %291 + %293 = OpFOrdGreaterThan %bool %292 %float_0 + OpSelectionMerge %294 Flatten + OpBranchConditional %293 %295 %296 + %295 = OpLabel + %297 = OpExtInst %v3float %1 Cross %265 %160 + %298 = OpDot %float %297 %297 + %299 = OpExtInst %float %1 FMax %298 %float_0_00999999978 + %300 = OpExtInst %float %1 Sqrt %299 + %301 = OpCompositeConstruct %v3float %300 %300 %300 + %302 = OpFDiv %v3float %297 %301 + %303 = OpFNegate %v3float %160 + OpBranch %294 + %296 = OpLabel + %304 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_8 %int_2 + %305 = OpLoad %float %304 + %306 = OpFOrdGreaterThan %bool %305 %float_0 + OpSelectionMerge %307 None + OpBranchConditional %306 %308 %309 + %308 = OpLabel + %310 = OpExtInst %v3float %1 Cross %247 %265 + %311 = OpDot %float %310 %310 + %312 = OpExtInst %float %1 FMax %311 %float_0_00999999978 + %313 = OpExtInst %float %1 Sqrt %312 + %314 = OpCompositeConstruct %v3float %313 %313 %313 + %315 = OpFDiv %v3float %310 %314 + %316 = OpFNegate %v3float %315 + OpBranch %307 + %309 = OpLabel + %317 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_8 %int_3 + %318 = OpLoad %float %317 + %319 = OpFOrdGreaterThan %bool %318 %float_0 + OpSelectionMerge %320 None + OpBranchConditional %319 %321 %320 + %321 = OpLabel + %322 = OpExtInst %v3float %1 Cross %265 %82 + %323 = OpDot %float %322 %322 + %324 = OpExtInst %float %1 FMax %323 %float_0_00999999978 + %325 = OpExtInst %float %1 Sqrt %324 + %326 = OpCompositeConstruct %v3float %325 %325 %325 + %327 = OpFDiv %v3float %322 %326 + %328 = OpExtInst %v3float %1 Cross %265 %327 + OpBranch %320 + %320 = OpLabel + %329 = OpPhi %v3float %251 %309 %327 %321 + %330 = OpPhi %v3float %259 %309 %328 %321 + OpBranch %307 + %307 = OpLabel + %331 = OpPhi %v3float %247 %308 %329 %320 + %332 = OpPhi %v3float %316 %308 %330 %320 + OpBranch %294 + %294 = OpLabel + %333 = OpPhi %v3float %302 %295 %331 %307 + %334 = OpPhi %v3float %303 %295 %332 %307 + OpBranch %269 + %269 = OpLabel + %335 = OpPhi %v3float %288 %270 %333 %294 + %336 = OpPhi %v3float %290 %270 %334 %294 + %337 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_12 + %338 = OpLoad %float %337 + %339 = OpFAdd %float %212 %338 + %340 = OpExtInst %float %1 Sin %339 + %341 = OpExtInst %float %1 Cos %339 + %342 = OpCompositeConstruct %v3float %340 %340 %340 + %343 = OpFMul %v3float %342 %336 + %344 = OpCompositeConstruct %v3float %341 %341 %341 + %345 = OpFMul %v3float %344 %335 + %346 = OpFAdd %v3float %343 %345 + %347 = OpFMul %v3float %344 %336 + %348 = OpFMul %v3float %342 %335 + %349 = OpFSub %v3float %347 %348 + %350 = OpCompositeExtract %float %204 0 + %351 = OpCompositeExtract %float %112 0 + %352 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_15 %int_0 + %353 = OpLoad %float %352 + %354 = OpFAdd %float %351 %353 + %355 = OpFMul %float %350 %354 + %356 = OpCompositeConstruct %v3float %355 %355 %355 + %357 = OpFMul %v3float %356 %346 + %358 = OpCompositeExtract %float %204 1 + %359 = OpCompositeExtract %float %112 1 + %360 = OpAccessChain %_ptr_Uniform_float %EmitterUniforms %int_15 %int_1 + %361 = OpLoad %float %360 + %362 = OpFAdd %float %359 %361 + %363 = OpFMul %float %358 %362 + %364 = OpCompositeConstruct %v3float %363 %363 %363 + %365 = OpFMul %v3float %364 %349 + %366 = OpFAdd %v3float %357 %365 + %367 = OpFAdd %v3float %239 %366 + %368 = OpCompositeExtract %float %367 0 + %369 = OpCompositeExtract %float %367 1 + %370 = OpCompositeExtract %float %367 2 + %371 = OpCompositeConstruct %v4float %368 %369 %370 %float_1 + %372 = OpVectorShuffle %v4float %371 %371 4 5 6 3 + %373 = OpAccessChain %_ptr_Uniform_mat4v4float %MobileShadowDepthPass %int_20 + %374 = OpLoad %mat4v4float %373 + %375 = OpMatrixTimesVector %v4float %374 %372 + %376 = OpAccessChain %_ptr_Uniform_float %MobileShadowDepthPass %int_22 + %377 = OpLoad %float %376 + %378 = OpFOrdGreaterThan %bool %377 %float_0 + %379 = OpCompositeExtract %float %375 2 + %380 = OpFOrdLessThan %bool %379 %float_0 + %381 = OpLogicalAnd %bool %378 %380 + OpSelectionMerge %382 None + OpBranchConditional %381 %383 %382 + %383 = OpLabel + %384 = OpCompositeInsert %v4float %float_9_99999997en07 %375 2 + %385 = OpCompositeInsert %v4float %float_1 %384 3 + OpBranch %382 + %382 = OpLabel + %386 = OpPhi %v4float %375 %269 %385 %383 + %387 = OpAccessChain %_ptr_Uniform_float %MobileShadowDepthPass %int_21 %int_0 + %388 = OpLoad %float %387 + %389 = OpAccessChain %_ptr_Uniform_float %MobileShadowDepthPass %int_21 %int_1 + %390 = OpLoad %float %389 + %391 = OpCompositeExtract %float %386 2 + %392 = OpFMul %float %391 %390 + %393 = OpFAdd %float %392 %388 + %394 = OpCompositeExtract %float %386 3 + %395 = OpFMul %float %393 %394 + %396 = OpCompositeInsert %v4float %395 %386 2 + OpStore %out_var_TEXCOORD6 %float_0 + OpStore %gl_Position %396 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/amd/gcn_shader.comp b/third_party/spirv-cross/shaders/amd/gcn_shader.comp new file mode 100644 index 0000000..037cdde --- /dev/null +++ b/third_party/spirv-cross/shaders/amd/gcn_shader.comp @@ -0,0 +1,13 @@ +#version 450 +#extension GL_AMD_gcn_shader : require +#extension GL_ARB_gpu_shader_int64 : require + +layout (local_size_x = 64) in; + +void main () +{ + float cubeFace = cubeFaceIndexAMD(vec3(0.0)); + vec2 cubeFaceCoord = cubeFaceCoordAMD(vec3(1.0)); + + uint64_t time = timeAMD(); +} diff --git a/third_party/spirv-cross/shaders/amd/shader_ballot.comp b/third_party/spirv-cross/shaders/amd/shader_ballot.comp new file mode 100644 index 0000000..d2a7271 --- /dev/null +++ b/third_party/spirv-cross/shaders/amd/shader_ballot.comp @@ -0,0 +1,33 @@ +#version 450 +#extension GL_AMD_shader_ballot : require +#extension GL_ARB_shader_ballot : require + +layout (local_size_x = 64) in; +layout (std430, binding = 0) buffer inputData +{ + float inputDataArray[]; +}; + +layout (std430, binding = 1) buffer outputData +{ + float outputDataArray[]; +}; + +void main () +{ + float thisLaneData = inputDataArray [gl_LocalInvocationID.x]; + bool laneActive = (thisLaneData > 0); + + uint thisLaneOutputSlot = mbcntAMD (ballotARB (laneActive)); + + int firstInvocation = readFirstInvocationARB(1); + int invocation = readInvocationARB(1, 0); + + vec3 swizzleInvocations = swizzleInvocationsAMD(vec3(0.0, 2.0, 1.0), uvec4(3)); + vec3 swizzelInvocationsMasked = swizzleInvocationsMaskedAMD(vec3(0.0, 2.0, 1.0), uvec3(2)); + vec3 writeInvocation = writeInvocationAMD(swizzleInvocations, swizzelInvocationsMasked, 0); + + if (laneActive) { + outputDataArray[thisLaneOutputSlot] = thisLaneData; + } +} diff --git a/third_party/spirv-cross/shaders/amd/shader_group_vote.comp b/third_party/spirv-cross/shaders/amd/shader_group_vote.comp new file mode 100644 index 0000000..d24aa92 --- /dev/null +++ b/third_party/spirv-cross/shaders/amd/shader_group_vote.comp @@ -0,0 +1,18 @@ +#version 450 +#extension GL_ARB_shader_group_vote : require + +layout (local_size_x = 64) in; +layout (std430, binding = 0) buffer inputData +{ + float inputDataArray[]; +}; + +void main () +{ + float thisLaneData = inputDataArray [gl_LocalInvocationID.x]; + bool laneActive = (thisLaneData > 0); + + bool allInvocations = allInvocationsARB(laneActive); + bool anyInvocations = anyInvocationARB(laneActive); + bool allInvocationsEqual = allInvocationsEqualARB(laneActive); +} diff --git a/third_party/spirv-cross/shaders/amd/shader_trinary_minmax.comp b/third_party/spirv-cross/shaders/amd/shader_trinary_minmax.comp new file mode 100644 index 0000000..f836146 --- /dev/null +++ b/third_party/spirv-cross/shaders/amd/shader_trinary_minmax.comp @@ -0,0 +1,11 @@ +#version 450 +#extension GL_AMD_shader_trinary_minmax : require + +layout (local_size_x = 64) in; + +void main () +{ + int t11 = min3(0, 3, 2); + int t12 = max3(0, 3, 2); + int t13 = mid3(0, 3, 2); +} diff --git a/third_party/spirv-cross/shaders/asm/comp/atomic-decrement.asm.comp b/third_party/spirv-cross/shaders/asm/comp/atomic-decrement.asm.comp new file mode 100644 index 0000000..a87b931 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/atomic-decrement.asm.comp @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "main" %15 + OpExecutionMode %3 LocalSize 4 1 1 + OpName %3 "main" + OpName %8 "u0" + OpName %9 "u0_counters" + OpMemberName %9 0 "c" + OpName %11 "u0_counter" + OpName %15 "vThreadID" + OpName %19 "r0" + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 1 + OpDecorate %11 Binding 0 + OpDecorate %15 BuiltIn GlobalInvocationId + %1 = OpTypeVoid + %2 = OpTypeFunction %1 + %5 = OpTypeInt 32 0 + %6 = OpTypeImage %5 Buffer 0 0 0 2 R32ui + %7 = OpTypePointer UniformConstant %6 + %8 = OpVariable %7 UniformConstant + %9 = OpTypeStruct %5 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpTypeVector %12 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpTypeFloat 32 + %17 = OpTypeVector %16 4 + %18 = OpTypePointer Function %17 + %20 = OpTypePointer Uniform %5 + %21 = OpConstant %5 0 + %23 = OpConstant %5 1 + %26 = OpTypePointer Function %16 + %33 = OpConstant %12 0 + %34 = OpConstant %5 2 + %37 = OpTypePointer Input %12 + %41 = OpTypeVector %5 4 + %3 = OpFunction %1 None %2 + %4 = OpLabel + %19 = OpVariable %18 Function + %22 = OpAccessChain %20 %11 %21 + %24 = OpAtomicIDecrement %5 %22 %23 %21 + %25 = OpBitcast %16 %24 + %27 = OpInBoundsAccessChain %26 %19 %21 + OpStore %27 %25 + %28 = OpLoad %6 %8 + %29 = OpInBoundsAccessChain %26 %19 %21 + %30 = OpLoad %16 %29 + %31 = OpBitcast %12 %30 + %32 = OpIMul %5 %31 %23 + %35 = OpShiftRightLogical %5 %33 %34 + %36 = OpIAdd %5 %32 %35 + %38 = OpInBoundsAccessChain %37 %15 %21 + %39 = OpLoad %12 %38 + %40 = OpBitcast %5 %39 + %42 = OpCompositeConstruct %41 %40 %40 %40 %40 + OpImageWrite %28 %36 %42 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/atomic-increment.asm.comp b/third_party/spirv-cross/shaders/asm/comp/atomic-increment.asm.comp new file mode 100644 index 0000000..3acb711 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/atomic-increment.asm.comp @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "main" %15 + OpExecutionMode %3 LocalSize 4 1 1 + OpName %3 "main" + OpName %8 "u0" + OpName %9 "u0_counters" + OpMemberName %9 0 "c" + OpName %11 "u0_counter" + OpName %15 "vThreadID" + OpName %19 "r0" + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 1 + OpDecorate %11 Binding 0 + OpDecorate %15 BuiltIn GlobalInvocationId + %1 = OpTypeVoid + %2 = OpTypeFunction %1 + %5 = OpTypeInt 32 0 + %6 = OpTypeImage %5 Buffer 0 0 0 2 R32ui + %7 = OpTypePointer UniformConstant %6 + %8 = OpVariable %7 UniformConstant + %9 = OpTypeStruct %5 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpTypeVector %12 3 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %16 = OpTypeFloat 32 + %17 = OpTypeVector %16 4 + %18 = OpTypePointer Function %17 + %20 = OpTypePointer Uniform %5 + %21 = OpConstant %5 0 + %23 = OpConstant %5 1 + %26 = OpTypePointer Function %16 + %33 = OpConstant %12 0 + %34 = OpConstant %5 2 + %37 = OpTypePointer Input %12 + %41 = OpTypeVector %5 4 + %3 = OpFunction %1 None %2 + %4 = OpLabel + %19 = OpVariable %18 Function + %22 = OpAccessChain %20 %11 %21 + %24 = OpAtomicIIncrement %5 %22 %23 %21 + %25 = OpBitcast %16 %24 + %27 = OpInBoundsAccessChain %26 %19 %21 + OpStore %27 %25 + %28 = OpLoad %6 %8 + %29 = OpInBoundsAccessChain %26 %19 %21 + %30 = OpLoad %16 %29 + %31 = OpBitcast %12 %30 + %32 = OpIMul %5 %31 %23 + %35 = OpShiftRightLogical %5 %33 %34 + %36 = OpIAdd %5 %32 %35 + %38 = OpInBoundsAccessChain %37 %15 %21 + %39 = OpLoad %12 %38 + %40 = OpBitcast %5 %39 + %42 = OpCompositeConstruct %41 %40 %40 %40 %40 + OpImageWrite %28 %36 %42 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/bitcast_iadd.asm.comp b/third_party/spirv-cross/shaders/asm/comp/bitcast_iadd.asm.comp new file mode 100644 index 0000000..3b31ab2 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/bitcast_iadd.asm.comp @@ -0,0 +1,79 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %inputs Restrict + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + OpDecorate %outputs Restrict + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of IAdd + %result_iadd_0 = OpIAdd %uvec4 %input0 %input1 + %result_iadd_1 = OpIAdd %uvec4 %input1 %input0 + %result_iadd_2 = OpIAdd %uvec4 %input0 %input0 + %result_iadd_3 = OpIAdd %uvec4 %input1 %input1 + %result_iadd_4 = OpIAdd %ivec4 %input0 %input0 + %result_iadd_5 = OpIAdd %ivec4 %input1 %input1 + %result_iadd_6 = OpIAdd %ivec4 %input0 %input1 + %result_iadd_7 = OpIAdd %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/bitcast_icmp.asm.comp b/third_party/spirv-cross/shaders/asm/comp/bitcast_icmp.asm.comp new file mode 100644 index 0000000..b7b4e0b --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/bitcast_icmp.asm.comp @@ -0,0 +1,101 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %inputs Restrict + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + OpDecorate %outputs Restrict + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %bool = OpTypeBool + %bvec4 = OpTypeVector %bool 4 + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + %uzero = OpConstant %uint 0 + %uone = OpConstant %uint 1 + %utrue = OpConstantComposite %uvec4 %uone %uone %uone %uone + %ufalse = OpConstantComposite %uvec4 %uzero %uzero %uzero %uzero + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + + %result_slt = OpSLessThan %bvec4 %input0 %input1 + %result_sle = OpSLessThanEqual %bvec4 %input0 %input1 + %result_ult = OpULessThan %bvec4 %input0 %input1 + %result_ule = OpULessThanEqual %bvec4 %input0 %input1 + %result_sgt = OpSGreaterThan %bvec4 %input0 %input1 + %result_sge = OpSGreaterThanEqual %bvec4 %input0 %input1 + %result_ugt = OpUGreaterThan %bvec4 %input0 %input1 + %result_uge = OpUGreaterThanEqual %bvec4 %input0 %input1 + + %int_slt = OpSelect %uvec4 %result_slt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_slt + + %int_sle = OpSelect %uvec4 %result_sle %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sle + + %int_ult = OpSelect %uvec4 %result_ult %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ult + + %int_ule = OpSelect %uvec4 %result_ule %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ule + + %int_sgt = OpSelect %uvec4 %result_sgt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sgt + + %int_sge = OpSelect %uvec4 %result_sge %utrue %ufalse + OpStore %output_ptr_uvec4 %int_sge + + %int_ugt = OpSelect %uvec4 %result_ugt %utrue %ufalse + OpStore %output_ptr_uvec4 %int_ugt + + %int_uge = OpSelect %uvec4 %result_uge %utrue %ufalse + OpStore %output_ptr_uvec4 %int_uge + + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/bitcast_iequal.asm.comp b/third_party/spirv-cross/shaders/asm/comp/bitcast_iequal.asm.comp new file mode 100644 index 0000000..c98f52c --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/bitcast_iequal.asm.comp @@ -0,0 +1,90 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + %bool = OpTypeBool + %bvec4 = OpTypeVector %bool 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + %uone = OpConstant %uint 1 + %uzero = OpConstant %uint 0 + %uvec41 = OpConstantComposite %uvec4 %uone %uone %uone %uone + %ivec41 = OpConstantComposite %ivec4 %one %one %one %one + %uvec40 = OpConstantComposite %uvec4 %uzero %uzero %uzero %uzero + %ivec40 = OpConstantComposite %ivec4 %zero %zero %zero %zero + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of IEqual + %result_iequal0 = OpIEqual %bvec4 %input0 %input1 + %result_iequal1 = OpIEqual %bvec4 %input1 %input0 + %result_iequal2 = OpIEqual %bvec4 %input0 %input0 + %result_iequal3 = OpIEqual %bvec4 %input1 %input1 + %result_0 = OpSelect %uvec4 %result_iequal0 %uvec41 %uvec40 + %result_1 = OpSelect %uvec4 %result_iequal1 %uvec41 %uvec40 + %result_2 = OpSelect %uvec4 %result_iequal2 %uvec41 %uvec40 + %result_3 = OpSelect %uvec4 %result_iequal3 %uvec41 %uvec40 + %result_4 = OpSelect %ivec4 %result_iequal0 %ivec41 %ivec40 + %result_5 = OpSelect %ivec4 %result_iequal1 %ivec41 %ivec40 + %result_6 = OpSelect %ivec4 %result_iequal2 %ivec41 %ivec40 + %result_7 = OpSelect %ivec4 %result_iequal3 %ivec41 %ivec40 + + OpStore %output_ptr_uvec4 %result_0 + OpStore %output_ptr_uvec4 %result_1 + OpStore %output_ptr_uvec4 %result_2 + OpStore %output_ptr_uvec4 %result_3 + OpStore %output_ptr_ivec4 %result_4 + OpStore %output_ptr_ivec4 %result_5 + OpStore %output_ptr_ivec4 %result_6 + OpStore %output_ptr_ivec4 %result_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/bitcast_sar.asm.comp b/third_party/spirv-cross/shaders/asm/comp/bitcast_sar.asm.comp new file mode 100644 index 0000000..64f19fc --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/bitcast_sar.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of ShiftRightArithmetic + %result_iadd_0 = OpShiftRightArithmetic %uvec4 %input0 %input1 + %result_iadd_1 = OpShiftRightArithmetic %uvec4 %input1 %input0 + %result_iadd_2 = OpShiftRightArithmetic %uvec4 %input0 %input0 + %result_iadd_3 = OpShiftRightArithmetic %uvec4 %input1 %input1 + %result_iadd_4 = OpShiftRightArithmetic %ivec4 %input0 %input0 + %result_iadd_5 = OpShiftRightArithmetic %ivec4 %input1 %input1 + %result_iadd_6 = OpShiftRightArithmetic %ivec4 %input0 %input1 + %result_iadd_7 = OpShiftRightArithmetic %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/bitcast_sdiv.asm.comp b/third_party/spirv-cross/shaders/asm/comp/bitcast_sdiv.asm.comp new file mode 100644 index 0000000..ab73ec8 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/bitcast_sdiv.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of SDiv + %result_iadd_0 = OpSDiv %uvec4 %input0 %input1 + %result_iadd_1 = OpSDiv %uvec4 %input1 %input0 + %result_iadd_2 = OpSDiv %uvec4 %input0 %input0 + %result_iadd_3 = OpSDiv %uvec4 %input1 %input1 + %result_iadd_4 = OpSDiv %ivec4 %input0 %input0 + %result_iadd_5 = OpSDiv %ivec4 %input1 %input1 + %result_iadd_6 = OpSDiv %ivec4 %input0 %input1 + %result_iadd_7 = OpSDiv %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/bitcast_slr.asm.comp b/third_party/spirv-cross/shaders/asm/comp/bitcast_slr.asm.comp new file mode 100644 index 0000000..6741f5c --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/bitcast_slr.asm.comp @@ -0,0 +1,77 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of ShiftRightLogical + %result_iadd_0 = OpShiftRightLogical %uvec4 %input0 %input1 + %result_iadd_1 = OpShiftRightLogical %uvec4 %input1 %input0 + %result_iadd_2 = OpShiftRightLogical %uvec4 %input0 %input0 + %result_iadd_3 = OpShiftRightLogical %uvec4 %input1 %input1 + %result_iadd_4 = OpShiftRightLogical %ivec4 %input0 %input0 + %result_iadd_5 = OpShiftRightLogical %ivec4 %input1 %input1 + %result_iadd_6 = OpShiftRightLogical %ivec4 %input0 %input1 + %result_iadd_7 = OpShiftRightLogical %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/block-name-alias-global.asm.comp b/third_party/spirv-cross/shaders/asm/comp/block-name-alias-global.asm.comp new file mode 100644 index 0000000..85f6cc0 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/block-name-alias-global.asm.comp @@ -0,0 +1,119 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 59 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %Foo "A" + OpMemberName %Foo 0 "a" + OpMemberName %Foo 1 "b" + OpName %A "A" + OpMemberName %A 0 "Data" + OpName %C1 "C1" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpName %Foo_0 "A" + OpMemberName %Foo_0 0 "a" + OpMemberName %Foo_0 1 "b" + OpName %A_0 "A" + OpMemberName %A_0 0 "Data" + OpName %C2 "C2" + OpName %B "B" + OpMemberName %B 0 "Data" + OpName %C3 "C3" + OpName %B_0 "B" + OpMemberName %B_0 0 "Data" + OpName %C4 "C4" + OpMemberDecorate %Foo 0 Offset 0 + OpMemberDecorate %Foo 1 Offset 4 + OpDecorate %_runtimearr_Foo ArrayStride 8 + OpMemberDecorate %A 0 Offset 0 + OpDecorate %A BufferBlock + OpDecorate %C1 DescriptorSet 0 + OpDecorate %C1 Binding 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpMemberDecorate %Foo_0 0 Offset 0 + OpMemberDecorate %Foo_0 1 Offset 4 + OpDecorate %_arr_Foo_0_uint_1024 ArrayStride 16 + OpMemberDecorate %A_0 0 Offset 0 + OpDecorate %A_0 Block + OpDecorate %C2 DescriptorSet 0 + OpDecorate %C2 Binding 2 + OpDecorate %_runtimearr_Foo_0 ArrayStride 8 + OpMemberDecorate %B 0 Offset 0 + OpDecorate %B BufferBlock + OpDecorate %C3 DescriptorSet 0 + OpDecorate %C3 Binding 0 + OpDecorate %_arr_Foo_0_uint_1024_0 ArrayStride 16 + OpMemberDecorate %B_0 0 Offset 0 + OpDecorate %B_0 Block + OpDecorate %C4 DescriptorSet 0 + OpDecorate %C4 Binding 3 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %Foo = OpTypeStruct %int %int +%_runtimearr_Foo = OpTypeRuntimeArray %Foo + %A = OpTypeStruct %_runtimearr_Foo +%_ptr_Uniform_A = OpTypePointer Uniform %A + %C1 = OpVariable %_ptr_Uniform_A Uniform + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %Foo_0 = OpTypeStruct %int %int + %uint_1024 = OpConstant %uint 1024 +%_arr_Foo_0_uint_1024 = OpTypeArray %Foo_0 %uint_1024 + %A_0 = OpTypeStruct %_arr_Foo_0_uint_1024 +%_ptr_Uniform_A_0 = OpTypePointer Uniform %A_0 + %C2 = OpVariable %_ptr_Uniform_A_0 Uniform +%_ptr_Uniform_Foo_0 = OpTypePointer Uniform %Foo_0 +%_ptr_Uniform_Foo = OpTypePointer Uniform %Foo +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_1 = OpConstant %int 1 +%_runtimearr_Foo_0 = OpTypeRuntimeArray %Foo + %B = OpTypeStruct %_runtimearr_Foo_0 +%_ptr_Uniform_B = OpTypePointer Uniform %B + %C3 = OpVariable %_ptr_Uniform_B Uniform +%_arr_Foo_0_uint_1024_0 = OpTypeArray %Foo_0 %uint_1024 + %B_0 = OpTypeStruct %_arr_Foo_0_uint_1024_0 +%_ptr_Uniform_B_0 = OpTypePointer Uniform %B_0 + %C4 = OpVariable %_ptr_Uniform_B_0 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %20 = OpLoad %uint %19 + %27 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %28 = OpLoad %uint %27 + %30 = OpAccessChain %_ptr_Uniform_Foo_0 %C2 %int_0 %28 + %31 = OpLoad %Foo_0 %30 + %33 = OpAccessChain %_ptr_Uniform_Foo %C1 %int_0 %20 + %34 = OpCompositeExtract %int %31 0 + %36 = OpAccessChain %_ptr_Uniform_int %33 %int_0 + OpStore %36 %34 + %37 = OpCompositeExtract %int %31 1 + %39 = OpAccessChain %_ptr_Uniform_int %33 %int_1 + OpStore %39 %37 + %44 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %45 = OpLoad %uint %44 + %50 = OpAccessChain %_ptr_Input_uint %gl_GlobalInvocationID %uint_0 + %51 = OpLoad %uint %50 + %52 = OpAccessChain %_ptr_Uniform_Foo_0 %C4 %int_0 %51 + %53 = OpLoad %Foo_0 %52 + %54 = OpAccessChain %_ptr_Uniform_Foo %C3 %int_0 %45 + %55 = OpCompositeExtract %int %53 0 + %56 = OpAccessChain %_ptr_Uniform_int %54 %int_0 + OpStore %56 %55 + %57 = OpCompositeExtract %int %53 1 + %58 = OpAccessChain %_ptr_Uniform_int %54 %int_1 + OpStore %58 %57 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/builtin-compute-bitcast.asm.comp b/third_party/spirv-cross/shaders/asm/comp/builtin-compute-bitcast.asm.comp new file mode 100644 index 0000000..4bc9202 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/builtin-compute-bitcast.asm.comp @@ -0,0 +1,50 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 26 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_WorkGroupID %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %BUF "BUF" + OpMemberName %BUF 0 "values" + OpName %_ "" + OpName %gl_WorkGroupID "gl_WorkGroupID" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %_runtimearr_int ArrayStride 4 + OpMemberDecorate %BUF 0 Offset 0 + OpDecorate %BUF BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + %void = OpTypeVoid + %int = OpTypeInt 32 1 +%_runtimearr_int = OpTypeRuntimeArray %int + %3 = OpTypeFunction %void + %BUF = OpTypeStruct %_runtimearr_int +%_ptr_Uniform_BUF = OpTypePointer Uniform %BUF + %_ = OpVariable %_ptr_Uniform_BUF Uniform + %int_0 = OpConstant %int 0 + %v3int = OpTypeVector %int 3 +%_ptr_Input_v3int = OpTypePointer Input %v3int +%gl_WorkGroupID = OpVariable %_ptr_Input_v3int Input + %int_1 = OpConstant %int 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3int Input + %int_2 = OpConstant %int 2 +%_ptr_Uniform_int = OpTypePointer Uniform %int + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Input_int %gl_WorkGroupID %int_1 + %19 = OpLoad %int %18 + %22 = OpAccessChain %_ptr_Input_int %gl_GlobalInvocationID %int_2 + %23 = OpLoad %int %22 + %25 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %19 + OpStore %25 %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/decoration-group.asm.comp b/third_party/spirv-cross/shaders/asm/comp/decoration-group.asm.comp new file mode 100644 index 0000000..b597b4b --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/decoration-group.asm.comp @@ -0,0 +1,99 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 58 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" %2 + OpExecutionMode %1 LocalSize 1 1 1 + OpSource GLSL 430 + OpName %1 "main" + OpName %2 "gl_GlobalInvocationID" + OpDecorate %2 BuiltIn GlobalInvocationId + OpDecorate %3 ArrayStride 4 + OpDecorate %4 BufferBlock + OpDecorate %5 Offset 0 + %4 = OpDecorationGroup + %5 = OpDecorationGroup + OpGroupDecorate %4 %6 %7 %8 %9 %10 %11 + OpGroupMemberDecorate %5 %6 0 %7 0 %8 0 %9 0 %10 0 %11 0 + OpDecorate %12 DescriptorSet 0 + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 NonWritable + OpDecorate %13 Restrict + %14 = OpDecorationGroup + %12 = OpDecorationGroup + %13 = OpDecorationGroup + OpGroupDecorate %12 %15 + OpGroupDecorate %12 %15 + OpGroupDecorate %12 %15 + OpDecorate %15 DescriptorSet 0 + OpDecorate %15 Binding 5 + OpGroupDecorate %14 %16 + OpDecorate %16 DescriptorSet 0 + OpDecorate %16 Binding 0 + OpGroupDecorate %12 %17 + OpDecorate %17 Binding 1 + OpGroupDecorate %13 %18 %19 + OpDecorate %18 Binding 2 + OpDecorate %19 Binding 3 + OpGroupDecorate %14 %20 + OpGroupDecorate %12 %20 + OpGroupDecorate %13 %20 + OpDecorate %20 Binding 4 + %21 = OpTypeBool + %22 = OpTypeVoid + %23 = OpTypeFunction %22 + %24 = OpTypeInt 32 0 + %25 = OpTypeInt 32 1 + %26 = OpTypeFloat 32 + %27 = OpTypeVector %24 3 + %28 = OpTypeVector %26 3 + %29 = OpTypePointer Input %27 + %30 = OpTypePointer Uniform %25 + %31 = OpTypePointer Uniform %26 + %32 = OpTypeRuntimeArray %25 + %3 = OpTypeRuntimeArray %26 + %2 = OpVariable %29 Input + %33 = OpConstant %25 0 + %6 = OpTypeStruct %3 + %34 = OpTypePointer Uniform %6 + %15 = OpVariable %34 Uniform + %7 = OpTypeStruct %3 + %35 = OpTypePointer Uniform %7 + %16 = OpVariable %35 Uniform + %8 = OpTypeStruct %3 + %36 = OpTypePointer Uniform %8 + %17 = OpVariable %36 Uniform + %9 = OpTypeStruct %3 + %37 = OpTypePointer Uniform %9 + %18 = OpVariable %37 Uniform + %10 = OpTypeStruct %3 + %38 = OpTypePointer Uniform %10 + %19 = OpVariable %38 Uniform + %11 = OpTypeStruct %3 + %39 = OpTypePointer Uniform %11 + %20 = OpVariable %39 Uniform + %1 = OpFunction %22 None %23 + %40 = OpLabel + %41 = OpLoad %27 %2 + %42 = OpCompositeExtract %24 %41 0 + %43 = OpAccessChain %31 %16 %33 %42 + %44 = OpAccessChain %31 %17 %33 %42 + %45 = OpAccessChain %31 %18 %33 %42 + %46 = OpAccessChain %31 %19 %33 %42 + %47 = OpAccessChain %31 %20 %33 %42 + %48 = OpAccessChain %31 %15 %33 %42 + %49 = OpLoad %26 %43 + %50 = OpLoad %26 %44 + %51 = OpLoad %26 %45 + %52 = OpLoad %26 %46 + %53 = OpLoad %26 %47 + %54 = OpFAdd %26 %49 %50 + %55 = OpFAdd %26 %54 %51 + %56 = OpFAdd %26 %55 %52 + %57 = OpFAdd %26 %56 %53 + OpStore %48 %57 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/global-parameter-name-alias.asm.comp b/third_party/spirv-cross/shaders/asm/comp/global-parameter-name-alias.asm.comp new file mode 100644 index 0000000..78b1dc7 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/global-parameter-name-alias.asm.comp @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 61 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %id_1 + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %Load_u1_ "Load(u1;" + OpName %size "size" + OpName %_main_vu3_ "@main(vu3;" + OpName %id "id" + OpName %data "data" + OpName %byteAddrTemp "byteAddrTemp" + OpName %ssbo "ssbo" + OpMemberName %ssbo 0 "@data" + OpName %ssbo_0 "ssbo" + OpName %param "param" + OpName %id_0 "id" + OpName %id_1 "id" + OpName %param_0 "param" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %ssbo 0 NonWritable + OpMemberDecorate %ssbo 0 Offset 0 + OpDecorate %ssbo BufferBlock + OpDecorate %ssbo_0 DescriptorSet 0 + OpDecorate %ssbo_0 Binding 1 + OpDecorate %id_1 BuiltIn GlobalInvocationId + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %8 = OpTypeFunction %void %_ptr_Function_uint + %v3uint = OpTypeVector %uint 3 +%_ptr_Function_v3uint = OpTypePointer Function %v3uint + %14 = OpTypeFunction %void %_ptr_Function_v3uint + %v4uint = OpTypeVector %uint 4 +%_ptr_Function_v4uint = OpTypePointer Function %v4uint + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_2 = OpConstant %int 2 +%_runtimearr_uint = OpTypeRuntimeArray %uint + %ssbo = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_ssbo = OpTypePointer Uniform %ssbo + %ssbo_0 = OpVariable %_ptr_Uniform_ssbo Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 + %uint_4 = OpConstant %uint 4 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %id_1 = OpVariable %_ptr_Input_v3uint Input + %main = OpFunction %void None %3 + %5 = OpLabel + %id_0 = OpVariable %_ptr_Function_v3uint Function + %param_0 = OpVariable %_ptr_Function_v3uint Function + %57 = OpLoad %v3uint %id_1 + OpStore %id_0 %57 + %59 = OpLoad %v3uint %id_0 + OpStore %param_0 %59 + %60 = OpFunctionCall %void %_main_vu3_ %param_0 + OpReturn + OpFunctionEnd + %Load_u1_ = OpFunction %void None %8 + %size = OpFunctionParameter %_ptr_Function_uint + %11 = OpLabel + %data = OpVariable %_ptr_Function_v4uint Function +%byteAddrTemp = OpVariable %_ptr_Function_int Function + %24 = OpLoad %uint %size + %26 = OpShiftRightLogical %int %24 %int_2 + OpStore %byteAddrTemp %26 + %32 = OpLoad %int %byteAddrTemp + %34 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %32 + %35 = OpLoad %uint %34 + %36 = OpLoad %int %byteAddrTemp + %38 = OpIAdd %int %36 %int_1 + %39 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %38 + %40 = OpLoad %uint %39 + %41 = OpLoad %int %byteAddrTemp + %42 = OpIAdd %int %41 %int_2 + %43 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %42 + %44 = OpLoad %uint %43 + %45 = OpLoad %int %byteAddrTemp + %47 = OpIAdd %int %45 %int_3 + %48 = OpAccessChain %_ptr_Uniform_uint %ssbo_0 %int_0 %47 + %49 = OpLoad %uint %48 + %50 = OpCompositeConstruct %v4uint %35 %40 %44 %49 + OpStore %data %50 + OpReturn + OpFunctionEnd + %_main_vu3_ = OpFunction %void None %14 + %id = OpFunctionParameter %_ptr_Function_v3uint + %17 = OpLabel + %param = OpVariable %_ptr_Function_uint Function + OpStore %param %uint_4 + %53 = OpFunctionCall %void %Load_u1_ %param + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/hlsl-functionality.asm.comp b/third_party/spirv-cross/shaders/asm/comp/hlsl-functionality.asm.comp new file mode 100644 index 0000000..d431113 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/hlsl-functionality.asm.comp @@ -0,0 +1,64 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 31 +; Schema: 0 + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %Buf "Buf" + OpMemberName %Buf 0 "@data" + OpName %Buf_0 "Buf" + OpName %Buf_count "Buf@count" + OpMemberName %Buf_count 0 "@count" + OpName %Buf_count_0 "Buf@count" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %Buf 0 Offset 0 + OpDecorate %Buf BufferBlock + OpDecorate %Buf_0 DescriptorSet 0 + OpDecorate %Buf_0 Binding 0 + OpMemberDecorate %Buf_count 0 Offset 0 + OpDecorate %Buf_count BufferBlock + OpDecorate %Buf_count_0 DescriptorSet 0 + OpDecorate %Buf_count_0 Binding 1 + OpDecorateId %Buf_0 HlslCounterBufferGOOGLE %Buf_count_0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %Buf = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_Buf = OpTypePointer Uniform %Buf + %Buf_0 = OpVariable %_ptr_Uniform_Buf Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %Buf_count = OpTypeStruct %int +%_ptr_Uniform_Buf_count = OpTypePointer Uniform %Buf_count +%Buf_count_0 = OpVariable %_ptr_Uniform_Buf_count Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %float_1 = OpConstant %float 1 + %27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %30 = OpFunctionCall %void %_main_ + OpReturn + OpFunctionEnd + %_main_ = OpFunction %void None %3 + %7 = OpLabel + %20 = OpAccessChain %_ptr_Uniform_int %Buf_count_0 %int_0 + %25 = OpAtomicIAdd %int %20 %uint_1 %uint_0 %int_1 + %29 = OpAccessChain %_ptr_Uniform_v4float %Buf_0 %int_0 %25 + OpStore %29 %27 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/logical.asm.comp b/third_party/spirv-cross/shaders/asm/comp/logical.asm.comp new file mode 100644 index 0000000..4174e77 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/logical.asm.comp @@ -0,0 +1,191 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 152 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource ESSL 310 + OpName %main "main" + OpName %and_b1_b1_ "and(b1;b1;" + OpName %a "a" + OpName %b "b" + OpName %and_vb2_vb2_ "and(vb2;vb2;" + OpName %a_0 "a" + OpName %b_0 "b" + OpName %and_vb3_vb3_ "and(vb3;vb3;" + OpName %a_1 "a" + OpName %b_1 "b" + OpName %and_vb4_vb4_ "and(vb4;vb4;" + OpName %a_2 "a" + OpName %b_2 "b" + OpName %b0 "b0" + OpName %SSBO0 "SSBO0" + OpMemberName %SSBO0 0 "a" + OpMemberName %SSBO0 1 "b" + OpMemberName %SSBO0 2 "c" + OpMemberName %SSBO0 3 "d" + OpName %s0 "s0" + OpName %SSBO1 "SSBO1" + OpMemberName %SSBO1 0 "a" + OpMemberName %SSBO1 1 "b" + OpMemberName %SSBO1 2 "c" + OpMemberName %SSBO1 3 "d" + OpName %s1 "s1" + OpName %param "param" + OpName %param_0 "param" + OpName %b1 "b1" + OpName %param_1 "param" + OpName %param_2 "param" + OpName %b2 "b2" + OpName %param_3 "param" + OpName %param_4 "param" + OpName %b3 "b3" + OpName %param_5 "param" + OpName %param_6 "param" + OpMemberDecorate %SSBO0 0 Offset 0 + OpMemberDecorate %SSBO0 1 Offset 8 + OpMemberDecorate %SSBO0 2 Offset 16 + OpMemberDecorate %SSBO0 3 Offset 32 + OpDecorate %SSBO0 BufferBlock + OpDecorate %s0 DescriptorSet 0 + OpDecorate %s0 Binding 0 + OpMemberDecorate %SSBO1 0 Offset 0 + OpMemberDecorate %SSBO1 1 Offset 8 + OpMemberDecorate %SSBO1 2 Offset 16 + OpMemberDecorate %SSBO1 3 Offset 32 + OpDecorate %SSBO1 BufferBlock + OpDecorate %s1 DescriptorSet 0 + OpDecorate %s1 Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %8 = OpTypeFunction %bool %_ptr_Function_bool %_ptr_Function_bool + %v2bool = OpTypeVector %bool 2 +%_ptr_Function_v2bool = OpTypePointer Function %v2bool + %15 = OpTypeFunction %v2bool %_ptr_Function_v2bool %_ptr_Function_v2bool + %v3bool = OpTypeVector %bool 3 +%_ptr_Function_v3bool = OpTypePointer Function %v3bool + %22 = OpTypeFunction %v3bool %_ptr_Function_v3bool %_ptr_Function_v3bool + %v4bool = OpTypeVector %bool 4 +%_ptr_Function_v4bool = OpTypePointer Function %v4bool + %29 = OpTypeFunction %v4bool %_ptr_Function_v4bool %_ptr_Function_v4bool + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %SSBO0 = OpTypeStruct %float %v2float %v3float %v4float +%_ptr_Uniform_SSBO0 = OpTypePointer Uniform %SSBO0 + %s0 = OpVariable %_ptr_Uniform_SSBO0 Uniform + %int = OpTypeInt 32 1 + %102 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %SSBO1 = OpTypeStruct %float %v2float %v3float %v4float +%_ptr_Uniform_SSBO1 = OpTypePointer Uniform %SSBO1 + %s1 = OpVariable %_ptr_Uniform_SSBO1 Uniform + %117 = OpConstant %int 1 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %129 = OpConstant %int 2 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %141 = OpConstant %int 3 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %b0 = OpVariable %_ptr_Function_bool Function + %param = OpVariable %_ptr_Function_bool Function + %param_0 = OpVariable %_ptr_Function_bool Function + %b1 = OpVariable %_ptr_Function_v2bool Function + %param_1 = OpVariable %_ptr_Function_v2bool Function + %param_2 = OpVariable %_ptr_Function_v2bool Function + %b2 = OpVariable %_ptr_Function_v3bool Function + %param_3 = OpVariable %_ptr_Function_v3bool Function + %param_4 = OpVariable %_ptr_Function_v3bool Function + %b3 = OpVariable %_ptr_Function_v4bool Function + %param_5 = OpVariable %_ptr_Function_v4bool Function + %param_6 = OpVariable %_ptr_Function_v4bool Function + %104 = OpAccessChain %_ptr_Uniform_float %s0 %102 + %105 = OpLoad %float %104 + %106 = OpIsInf %bool %105 + %110 = OpAccessChain %_ptr_Uniform_float %s1 %102 + %111 = OpLoad %float %110 + %112 = OpIsNan %bool %111 + OpStore %param %106 + OpStore %param_0 %112 + %115 = OpFunctionCall %bool %and_b1_b1_ %param %param_0 + OpStore %b0 %115 + %119 = OpAccessChain %_ptr_Uniform_v2float %s0 %117 + %120 = OpLoad %v2float %119 + %121 = OpIsInf %v2bool %120 + %122 = OpAccessChain %_ptr_Uniform_v2float %s1 %117 + %123 = OpLoad %v2float %122 + %124 = OpIsNan %v2bool %123 + OpStore %param_1 %121 + OpStore %param_2 %124 + %127 = OpFunctionCall %v2bool %and_vb2_vb2_ %param_1 %param_2 + OpStore %b1 %127 + %131 = OpAccessChain %_ptr_Uniform_v3float %s0 %129 + %132 = OpLoad %v3float %131 + %133 = OpIsInf %v3bool %132 + %134 = OpAccessChain %_ptr_Uniform_v3float %s1 %129 + %135 = OpLoad %v3float %134 + %136 = OpIsNan %v3bool %135 + OpStore %param_3 %133 + OpStore %param_4 %136 + %139 = OpFunctionCall %v3bool %and_vb3_vb3_ %param_3 %param_4 + OpStore %b2 %139 + %143 = OpAccessChain %_ptr_Uniform_v4float %s0 %141 + %144 = OpLoad %v4float %143 + %145 = OpIsInf %v4bool %144 + %146 = OpAccessChain %_ptr_Uniform_v4float %s1 %141 + %147 = OpLoad %v4float %146 + %148 = OpIsNan %v4bool %147 + OpStore %param_5 %145 + OpStore %param_6 %148 + %151 = OpFunctionCall %v4bool %and_vb4_vb4_ %param_5 %param_6 + OpStore %b3 %151 + OpReturn + OpFunctionEnd + %and_b1_b1_ = OpFunction %bool None %8 + %a = OpFunctionParameter %_ptr_Function_bool + %b = OpFunctionParameter %_ptr_Function_bool + %12 = OpLabel + %34 = OpLoad %bool %a + %35 = OpLoad %bool %b + %36 = OpLogicalAnd %bool %34 %35 + %37 = OpLogicalOr %bool %36 %35 + %38 = OpLogicalNot %bool %37 + OpReturnValue %38 + OpFunctionEnd +%and_vb2_vb2_ = OpFunction %v2bool None %15 + %a_0 = OpFunctionParameter %_ptr_Function_v2bool + %b_0 = OpFunctionParameter %_ptr_Function_v2bool + %19 = OpLabel + %39 = OpLoad %v2bool %a_0 + %41 = OpLoad %v2bool %b_0 + %48 = OpLogicalAnd %v2bool %39 %41 + %49 = OpLogicalOr %v2bool %48 %41 + %50 = OpLogicalNot %v2bool %49 + OpReturnValue %50 + OpFunctionEnd +%and_vb3_vb3_ = OpFunction %v3bool None %22 + %a_1 = OpFunctionParameter %_ptr_Function_v3bool + %b_1 = OpFunctionParameter %_ptr_Function_v3bool + %26 = OpLabel + %52 = OpLoad %v3bool %a_1 + %54 = OpLoad %v3bool %b_1 + %66 = OpLogicalAnd %v3bool %52 %54 + OpReturnValue %66 + OpFunctionEnd +%and_vb4_vb4_ = OpFunction %v4bool None %29 + %a_2 = OpFunctionParameter %_ptr_Function_v4bool + %b_2 = OpFunctionParameter %_ptr_Function_v4bool + %33 = OpLabel + %70 = OpLoad %v4bool %a_2 + %72 = OpLoad %v4bool %b_2 + %74 = OpLogicalAnd %v4bool %70 %72 + OpReturnValue %74 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/multiple-entry.asm.comp b/third_party/spirv-cross/shaders/asm/comp/multiple-entry.asm.comp new file mode 100644 index 0000000..9ddc07b --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/multiple-entry.asm.comp @@ -0,0 +1,98 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 30 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %func_alt "main2" %frag_in %frag_out + OpEntryPoint GLCompute %func "main" + OpExecutionMode %func LocalSize 1 1 1 + OpExecutionMode %func_alt OriginUpperLeft + OpSource ESSL 310 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpMemberDecorate %input_struct 0 Offset 0 + OpMemberDecorate %input_struct 1 Offset 16 + OpMemberDecorate %output_struct 0 Offset 0 + OpMemberDecorate %output_struct 1 Offset 16 + OpDecorate %input_struct BufferBlock + OpDecorate %inputs DescriptorSet 0 + OpDecorate %inputs Binding 0 + OpDecorate %inputs Restrict + OpDecorate %output_struct BufferBlock + OpDecorate %outputs DescriptorSet 0 + OpDecorate %outputs Binding 1 + OpDecorate %outputs Restrict + OpDecorate %frag_in Location 0 + OpDecorate %frag_out Location 0 + + %void = OpTypeVoid + %main_func = OpTypeFunction %void + + %uint = OpTypeInt 32 0 + %uvec4 = OpTypeVector %uint 4 + + %int = OpTypeInt 32 1 + %ivec4 = OpTypeVector %int 4 + + %ivec4_ptr = OpTypePointer Uniform %ivec4 + %uvec4_ptr = OpTypePointer Uniform %uvec4 + + %float = OpTypeFloat 32 + %vec4 = OpTypeVector %float 4 + %vec4_input_ptr = OpTypePointer Input %vec4 + %vec4_output_ptr = OpTypePointer Output %vec4 + + %zero = OpConstant %int 0 + %one = OpConstant %int 1 + + %input_struct = OpTypeStruct %ivec4 %uvec4 + %input_struct_ptr = OpTypePointer Uniform %input_struct + %inputs = OpVariable %input_struct_ptr Uniform + %output_struct = OpTypeStruct %uvec4 %ivec4 + %output_struct_ptr = OpTypePointer Uniform %output_struct + %outputs = OpVariable %output_struct_ptr Uniform + + %frag_in = OpVariable %vec4_input_ptr Input + %frag_out = OpVariable %vec4_output_ptr Output + + %func = OpFunction %void None %main_func + %block = OpLabel + + %input1_ptr = OpAccessChain %ivec4_ptr %inputs %zero + %input0_ptr = OpAccessChain %uvec4_ptr %inputs %one + %input1 = OpLoad %ivec4 %input1_ptr + %input0 = OpLoad %uvec4 %input0_ptr + + %output_ptr_uvec4 = OpAccessChain %uvec4_ptr %outputs %zero + %output_ptr_ivec4 = OpAccessChain %ivec4_ptr %outputs %one + +; Test all variants of IAdd + %result_iadd_0 = OpIAdd %uvec4 %input0 %input1 + %result_iadd_1 = OpIAdd %uvec4 %input1 %input0 + %result_iadd_2 = OpIAdd %uvec4 %input0 %input0 + %result_iadd_3 = OpIAdd %uvec4 %input1 %input1 + %result_iadd_4 = OpIAdd %ivec4 %input0 %input0 + %result_iadd_5 = OpIAdd %ivec4 %input1 %input1 + %result_iadd_6 = OpIAdd %ivec4 %input0 %input1 + %result_iadd_7 = OpIAdd %ivec4 %input1 %input0 + OpStore %output_ptr_uvec4 %result_iadd_0 + OpStore %output_ptr_uvec4 %result_iadd_1 + OpStore %output_ptr_uvec4 %result_iadd_2 + OpStore %output_ptr_uvec4 %result_iadd_3 + OpStore %output_ptr_ivec4 %result_iadd_4 + OpStore %output_ptr_ivec4 %result_iadd_5 + OpStore %output_ptr_ivec4 %result_iadd_6 + OpStore %output_ptr_ivec4 %result_iadd_7 + + OpReturn + OpFunctionEnd + + %func_alt = OpFunction %void None %main_func + %block_alt = OpLabel + %frag_input_value = OpLoad %vec4 %frag_in + OpStore %frag_out %frag_input_value + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/nmin-max-clamp.asm.comp b/third_party/spirv-cross/shaders/asm/comp/nmin-max-clamp.asm.comp new file mode 100644 index 0000000..6c060ee --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/nmin-max-clamp.asm.comp @@ -0,0 +1,203 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 139 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a1" + OpMemberName %SSBO 1 "a2" + OpMemberName %SSBO 2 "a3" + OpMemberName %SSBO 3 "a4" + OpMemberName %SSBO 4 "b1" + OpMemberName %SSBO 5 "b2" + OpMemberName %SSBO 6 "b3" + OpMemberName %SSBO 7 "b4" + OpMemberName %SSBO 8 "c1" + OpMemberName %SSBO 9 "c2" + OpMemberName %SSBO 10 "c3" + OpMemberName %SSBO 11 "c4" + OpName %_ "" + OpName %i "i" + OpMemberDecorate %SSBO 0 Offset 0 + OpMemberDecorate %SSBO 1 Offset 8 + OpMemberDecorate %SSBO 2 Offset 16 + OpMemberDecorate %SSBO 3 Offset 32 + OpMemberDecorate %SSBO 4 Offset 48 + OpMemberDecorate %SSBO 5 Offset 56 + OpMemberDecorate %SSBO 6 Offset 64 + OpMemberDecorate %SSBO 7 Offset 80 + OpMemberDecorate %SSBO 8 Offset 96 + OpMemberDecorate %SSBO 9 Offset 104 + OpMemberDecorate %SSBO 10 Offset 112 + OpMemberDecorate %SSBO 11 Offset 128 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %SSBO = OpTypeStruct %float %v2float %v3float %v4float %float %v2float %v3float %v4float %float %v2float %v3float %v4float +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %int_8 = OpConstant %int 8 + %int_1 = OpConstant %int 1 + %int_5 = OpConstant %int 5 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %int_9 = OpConstant %int 9 + %int_2 = OpConstant %int 2 + %int_6 = OpConstant %int 6 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %int_10 = OpConstant %int 10 + %int_3 = OpConstant %int 3 + %int_7 = OpConstant %int 7 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_11 = OpConstant %int 11 +%_ptr_Function_int = OpTypePointer Function %int + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %main = OpFunction %void None %7 + %35 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %36 = OpAccessChain %_ptr_Uniform_float %_ %int_4 + %37 = OpLoad %float %36 + %38 = OpAccessChain %_ptr_Uniform_float %_ %int_8 + %39 = OpLoad %float %38 + %40 = OpExtInst %float %1 NMin %37 %39 + %41 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %41 %40 + %42 = OpAccessChain %_ptr_Uniform_v2float %_ %int_5 + %43 = OpLoad %v2float %42 + %44 = OpAccessChain %_ptr_Uniform_v2float %_ %int_9 + %45 = OpLoad %v2float %44 + %46 = OpExtInst %v2float %1 NMin %43 %45 + %47 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + OpStore %47 %46 + %48 = OpAccessChain %_ptr_Uniform_v3float %_ %int_6 + %49 = OpLoad %v3float %48 + %50 = OpAccessChain %_ptr_Uniform_v3float %_ %int_10 + %51 = OpLoad %v3float %50 + %52 = OpExtInst %v3float %1 NMin %49 %51 + %53 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + OpStore %53 %52 + %54 = OpAccessChain %_ptr_Uniform_v4float %_ %int_7 + %55 = OpLoad %v4float %54 + %56 = OpAccessChain %_ptr_Uniform_v4float %_ %int_11 + %57 = OpLoad %v4float %56 + %58 = OpExtInst %v4float %1 NMin %55 %57 + %59 = OpAccessChain %_ptr_Uniform_v4float %_ %int_3 + OpStore %59 %58 + %60 = OpAccessChain %_ptr_Uniform_float %_ %int_4 + %61 = OpLoad %float %60 + %62 = OpAccessChain %_ptr_Uniform_float %_ %int_8 + %63 = OpLoad %float %62 + %64 = OpExtInst %float %1 NMax %61 %63 + %65 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %65 %64 + %66 = OpAccessChain %_ptr_Uniform_v2float %_ %int_5 + %67 = OpLoad %v2float %66 + %68 = OpAccessChain %_ptr_Uniform_v2float %_ %int_9 + %69 = OpLoad %v2float %68 + %70 = OpExtInst %v2float %1 NMax %67 %69 + %71 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + OpStore %71 %70 + %72 = OpAccessChain %_ptr_Uniform_v3float %_ %int_6 + %73 = OpLoad %v3float %72 + %74 = OpAccessChain %_ptr_Uniform_v3float %_ %int_10 + %75 = OpLoad %v3float %74 + %76 = OpExtInst %v3float %1 NMax %73 %75 + %77 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + OpStore %77 %76 + %78 = OpAccessChain %_ptr_Uniform_v4float %_ %int_7 + %79 = OpLoad %v4float %78 + %80 = OpAccessChain %_ptr_Uniform_v4float %_ %int_11 + %81 = OpLoad %v4float %80 + %82 = OpExtInst %v4float %1 NMax %79 %81 + %83 = OpAccessChain %_ptr_Uniform_v4float %_ %int_3 + OpStore %83 %82 + %84 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %85 = OpLoad %float %84 + %86 = OpAccessChain %_ptr_Uniform_float %_ %int_4 + %87 = OpLoad %float %86 + %88 = OpAccessChain %_ptr_Uniform_float %_ %int_8 + %89 = OpLoad %float %88 + %90 = OpExtInst %float %1 NClamp %85 %87 %89 + %91 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %91 %90 + %92 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + %93 = OpLoad %v2float %92 + %94 = OpAccessChain %_ptr_Uniform_v2float %_ %int_5 + %95 = OpLoad %v2float %94 + %96 = OpAccessChain %_ptr_Uniform_v2float %_ %int_9 + %97 = OpLoad %v2float %96 + %98 = OpExtInst %v2float %1 NClamp %93 %95 %97 + %99 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + OpStore %99 %98 + %100 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + %101 = OpLoad %v3float %100 + %102 = OpAccessChain %_ptr_Uniform_v3float %_ %int_6 + %103 = OpLoad %v3float %102 + %104 = OpAccessChain %_ptr_Uniform_v3float %_ %int_10 + %105 = OpLoad %v3float %104 + %106 = OpExtInst %v3float %1 NClamp %101 %103 %105 + %107 = OpAccessChain %_ptr_Uniform_v3float %_ %int_2 + OpStore %107 %106 + %108 = OpAccessChain %_ptr_Uniform_v4float %_ %int_3 + %109 = OpLoad %v4float %108 + %110 = OpAccessChain %_ptr_Uniform_v4float %_ %int_7 + %111 = OpLoad %v4float %110 + %112 = OpAccessChain %_ptr_Uniform_v4float %_ %int_11 + %113 = OpLoad %v4float %112 + %114 = OpExtInst %v4float %1 NClamp %109 %111 %113 + %115 = OpAccessChain %_ptr_Uniform_v4float %_ %int_3 + OpStore %115 %114 + OpStore %i %int_0 + OpBranch %116 + %116 = OpLabel + OpLoopMerge %117 %118 None + OpBranch %119 + %119 = OpLabel + %120 = OpLoad %int %i + %121 = OpSLessThan %bool %120 %int_2 + OpBranchConditional %121 %122 %117 + %122 = OpLabel + %123 = OpAccessChain %_ptr_Uniform_v2float %_ %int_5 + %124 = OpLoad %v2float %123 + %125 = OpAccessChain %_ptr_Uniform_v2float %_ %int_9 + %126 = OpLoad %v2float %125 + %127 = OpExtInst %v2float %1 NMin %124 %126 + %128 = OpAccessChain %_ptr_Uniform_v2float %_ %int_1 + OpStore %128 %127 + OpBranch %118 + %118 = OpLabel + %129 = OpLoad %int %i + %130 = OpIAdd %int %129 %int_1 + OpStore %i %130 + %131 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %132 = OpLoad %float %131 + %133 = OpAccessChain %_ptr_Uniform_float %_ %int_5 %uint_0 + %134 = OpLoad %float %133 + %135 = OpAccessChain %_ptr_Uniform_float %_ %int_5 %uint_1 + %136 = OpLoad %float %135 + %137 = OpExtInst %float %1 NClamp %132 %134 %136 + %138 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %138 %137 + OpBranch %116 + %117 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/op-phi-swap.asm.comp b/third_party/spirv-cross/shaders/asm/comp/op-phi-swap.asm.comp new file mode 100644 index 0000000..dc18d69 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/op-phi-swap.asm.comp @@ -0,0 +1,63 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 39 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpName %main "main" + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %_struct_3 BufferBlock + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 0 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 1 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_3 0 Offset 0 + %bool = OpTypeBool + %void = OpTypeVoid + %9 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %v3uint = OpTypeVector %uint 3 + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_runtimearr_int = OpTypeRuntimeArray %int +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_3 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 + %4 = OpVariable %_ptr_Uniform__struct_3 Uniform + %5 = OpVariable %_ptr_Uniform__struct_3 Uniform +%_ptr_Function_float = OpTypePointer Function %float +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %true = OpConstantTrue %bool + %false = OpConstantFalse %bool + %int_0 = OpConstant %int 0 + %float_8_5 = OpConstant %float 8.5 + %main = OpFunction %void None %9 + %25 = OpLabel + %26 = OpVariable %_ptr_Function_float Function %float_8_5 + %27 = OpLoad %v3uint %gl_GlobalInvocationID + %28 = OpCompositeExtract %uint %27 0 + %29 = OpAccessChain %_ptr_Uniform_float %4 %int_0 %28 + %30 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %28 + %31 = OpLoad %float %29 + %32 = OpLoad %float %26 + OpBranch %33 + %33 = OpLabel + %34 = OpPhi %bool %true %25 %false %33 + %35 = OpPhi %float %31 %25 %36 %33 + %36 = OpPhi %float %32 %25 %35 %33 + OpLoopMerge %37 %33 None + OpBranchConditional %34 %33 %37 + %37 = OpLabel + %38 = OpFSub %float %35 %36 + OpStore %30 %38 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/quantize.asm.comp b/third_party/spirv-cross/shaders/asm/comp/quantize.asm.comp new file mode 100644 index 0000000..f5afc65 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/quantize.asm.comp @@ -0,0 +1,67 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 38 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "SSBO0" + OpMemberName %10 0 "scalar" + OpMemberName %10 1 "vec2_val" + OpMemberName %10 2 "vec3_val" + OpMemberName %10 3 "vec4_val" + OpName %12 "" + OpMemberDecorate %10 0 Offset 0 + OpMemberDecorate %10 1 Offset 8 + OpMemberDecorate %10 2 Offset 16 + OpMemberDecorate %10 3 Offset 32 + OpDecorate %10 BufferBlock + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeVector %6 3 + %9 = OpTypeVector %6 4 + %10 = OpTypeStruct %6 %7 %8 %9 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %13 = OpTypeInt 32 1 + %14 = OpConstant %13 0 + %15 = OpTypePointer Uniform %6 + %20 = OpConstant %13 1 + %21 = OpTypePointer Uniform %7 + %26 = OpConstant %13 2 + %27 = OpTypePointer Uniform %8 + %32 = OpConstant %13 3 + %33 = OpTypePointer Uniform %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %16 = OpAccessChain %15 %12 %14 + %17 = OpLoad %6 %16 + %18 = OpQuantizeToF16 %6 %17 + %19 = OpAccessChain %15 %12 %14 + OpStore %19 %18 + %22 = OpAccessChain %21 %12 %20 + %23 = OpLoad %7 %22 + %24 = OpQuantizeToF16 %7 %23 + %25 = OpAccessChain %21 %12 %20 + OpStore %25 %24 + %28 = OpAccessChain %27 %12 %26 + %29 = OpLoad %8 %28 + %30 = OpQuantizeToF16 %8 %29 + %31 = OpAccessChain %27 %12 %26 + OpStore %31 %30 + %34 = OpAccessChain %33 %12 %32 + %35 = OpLoad %9 %34 + %36 = OpQuantizeToF16 %9 %35 + %37 = OpAccessChain %33 %12 %32 + OpStore %37 %36 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/recompile-block-naming.asm.comp b/third_party/spirv-cross/shaders/asm/comp/recompile-block-naming.asm.comp new file mode 100644 index 0000000..227a82b --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/recompile-block-naming.asm.comp @@ -0,0 +1,140 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 97 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %a "a" + OpName %byteAddrTemp "byteAddrTemp" + OpName %MyFirstBuffer "MyFirstBuffer" + OpMemberName %MyFirstBuffer 0 "@data" + OpName %MyFirstBuffer_0 "MyFirstBuffer" + OpName %b "b" + OpName %byteAddrTemp_0 "byteAddrTemp" + OpName %MySecondBuffer "MySecondBuffer" + OpName %byteAddrTemp_1 "byteAddrTemp" + OpName %MyThirdBuffer "MyThirdBuffer" + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %MyFirstBuffer 0 Offset 0 + OpDecorate %MyFirstBuffer BufferBlock + OpDecorate %MyFirstBuffer_0 DescriptorSet 0 + OpDecorate %MyFirstBuffer_0 Binding 0 + OpDecorate %MySecondBuffer DescriptorSet 0 + OpDecorate %MySecondBuffer Binding 0 + OpDecorate %MyThirdBuffer DescriptorSet 0 + OpDecorate %MyThirdBuffer Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v4uint = OpTypeVector %uint 4 +%_ptr_Function_v4uint = OpTypePointer Function %v4uint + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%MyFirstBuffer = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_MyFirstBuffer = OpTypePointer Uniform %MyFirstBuffer +%MyFirstBuffer_0 = OpVariable %_ptr_Uniform_MyFirstBuffer Uniform +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 + %int_4 = OpConstant %int 4 +%MySecondBuffer = OpVariable %_ptr_Uniform_MyFirstBuffer Uniform +%MyThirdBuffer = OpVariable %_ptr_Uniform_MyFirstBuffer Uniform + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %96 = OpFunctionCall %void %_main_ + OpReturn + OpFunctionEnd + %_main_ = OpFunction %void None %3 + %7 = OpLabel + %a = OpVariable %_ptr_Function_v4uint Function +%byteAddrTemp = OpVariable %_ptr_Function_int Function + %b = OpVariable %_ptr_Function_v4uint Function +%byteAddrTemp_0 = OpVariable %_ptr_Function_int Function +%byteAddrTemp_1 = OpVariable %_ptr_Function_int Function + %17 = OpShiftRightArithmetic %int %int_0 %int_2 + OpStore %byteAddrTemp %17 + %22 = OpLoad %int %byteAddrTemp + %24 = OpAccessChain %_ptr_Uniform_uint %MyFirstBuffer_0 %int_0 %22 + %25 = OpLoad %uint %24 + %26 = OpLoad %int %byteAddrTemp + %28 = OpIAdd %int %26 %int_1 + %29 = OpAccessChain %_ptr_Uniform_uint %MyFirstBuffer_0 %int_0 %28 + %30 = OpLoad %uint %29 + %31 = OpLoad %int %byteAddrTemp + %32 = OpIAdd %int %31 %int_2 + %33 = OpAccessChain %_ptr_Uniform_uint %MyFirstBuffer_0 %int_0 %32 + %34 = OpLoad %uint %33 + %35 = OpLoad %int %byteAddrTemp + %37 = OpIAdd %int %35 %int_3 + %38 = OpAccessChain %_ptr_Uniform_uint %MyFirstBuffer_0 %int_0 %37 + %39 = OpLoad %uint %38 + %40 = OpCompositeConstruct %v4uint %25 %30 %34 %39 + OpStore %a %40 + %44 = OpShiftRightArithmetic %int %int_4 %int_2 + OpStore %byteAddrTemp_0 %44 + %46 = OpLoad %int %byteAddrTemp_0 + %47 = OpAccessChain %_ptr_Uniform_uint %MySecondBuffer %int_0 %46 + %48 = OpLoad %uint %47 + %49 = OpLoad %int %byteAddrTemp_0 + %50 = OpIAdd %int %49 %int_1 + %51 = OpAccessChain %_ptr_Uniform_uint %MySecondBuffer %int_0 %50 + %52 = OpLoad %uint %51 + %53 = OpLoad %int %byteAddrTemp_0 + %54 = OpIAdd %int %53 %int_2 + %55 = OpAccessChain %_ptr_Uniform_uint %MySecondBuffer %int_0 %54 + %56 = OpLoad %uint %55 + %57 = OpLoad %int %byteAddrTemp_0 + %58 = OpIAdd %int %57 %int_3 + %59 = OpAccessChain %_ptr_Uniform_uint %MySecondBuffer %int_0 %58 + %60 = OpLoad %uint %59 + %61 = OpCompositeConstruct %v4uint %48 %52 %56 %60 + OpStore %b %61 + %63 = OpShiftRightArithmetic %int %int_0 %int_2 + OpStore %byteAddrTemp_1 %63 + %65 = OpLoad %int %byteAddrTemp_1 + %66 = OpLoad %v4uint %a + %67 = OpLoad %v4uint %b + %68 = OpIAdd %v4uint %66 %67 + %70 = OpCompositeExtract %uint %68 0 + %71 = OpAccessChain %_ptr_Uniform_uint %MyThirdBuffer %int_0 %65 + OpStore %71 %70 + %72 = OpLoad %int %byteAddrTemp_1 + %73 = OpIAdd %int %72 %int_1 + %74 = OpLoad %v4uint %a + %75 = OpLoad %v4uint %b + %76 = OpIAdd %v4uint %74 %75 + %78 = OpCompositeExtract %uint %76 1 + %79 = OpAccessChain %_ptr_Uniform_uint %MyThirdBuffer %int_0 %73 + OpStore %79 %78 + %80 = OpLoad %int %byteAddrTemp_1 + %81 = OpIAdd %int %80 %int_2 + %82 = OpLoad %v4uint %a + %83 = OpLoad %v4uint %b + %84 = OpIAdd %v4uint %82 %83 + %86 = OpCompositeExtract %uint %84 2 + %87 = OpAccessChain %_ptr_Uniform_uint %MyThirdBuffer %int_0 %81 + OpStore %87 %86 + %88 = OpLoad %int %byteAddrTemp_1 + %89 = OpIAdd %int %88 %int_3 + %90 = OpLoad %v4uint %a + %91 = OpLoad %v4uint %b + %92 = OpIAdd %v4uint %90 %91 + %94 = OpCompositeExtract %uint %92 3 + %95 = OpAccessChain %_ptr_Uniform_uint %MyThirdBuffer %int_0 %89 + OpStore %95 %94 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/specialization-constant-workgroup.asm.comp b/third_party/spirv-cross/shaders/asm/comp/specialization-constant-workgroup.asm.comp new file mode 100644 index 0000000..188e3fe --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/specialization-constant-workgroup.asm.comp @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 24 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 20 1 + OpSource ESSL 310 + OpName %main "main" + OpName %SSBO "SSBO" + OpMemberName %SSBO 0 "a" + OpName %_ "" + OpMemberDecorate %SSBO 0 Offset 0 + OpDecorate %SSBO BufferBlock + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %19 SpecId 10 + OpDecorate %21 SpecId 12 + OpDecorate %gl_WorkGroupSize BuiltIn WorkgroupSize + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %SSBO = OpTypeStruct %float +%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO + %_ = OpVariable %_ptr_Uniform_SSBO Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %uint = OpTypeInt 32 0 + %19 = OpSpecConstant %uint 9 + %uint_20 = OpConstant %uint 20 + %21 = OpSpecConstant %uint 4 + %v3uint = OpTypeVector %uint 3 +%gl_WorkGroupSize = OpSpecConstantComposite %v3uint %19 %uint_20 %21 + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + %15 = OpLoad %float %14 + %16 = OpFAdd %float %15 %float_1 + %17 = OpAccessChain %_ptr_Uniform_float %_ %int_0 + OpStore %17 %16 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/comp/switch-break-ladder.asm.comp b/third_party/spirv-cross/shaders/asm/comp/switch-break-ladder.asm.comp new file mode 100644 index 0000000..a32c9ef --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/comp/switch-break-ladder.asm.comp @@ -0,0 +1,92 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 50 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + OpName %c "c" + OpName %BUF "BUF" + OpMemberName %BUF 0 "a" + OpMemberName %BUF 1 "b" + OpMemberName %BUF 2 "d" + OpName %o "o" + OpName %a "a" + OpMemberDecorate %BUF 0 Offset 0 + OpMemberDecorate %BUF 1 Offset 4 + OpMemberDecorate %BUF 2 Offset 8 + OpDecorate %BUF BufferBlock + OpDecorate %o DescriptorSet 0 + OpDecorate %o Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %BUF = OpTypeStruct %int %int %int +%_ptr_Uniform_BUF = OpTypePointer Uniform %BUF + %o = OpVariable %_ptr_Uniform_BUF Uniform + %int_0 = OpConstant %int 0 +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_2 = OpConstant %int 2 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %c = OpVariable %_ptr_Function_int Function + %a = OpVariable %_ptr_Function_int Function + %14 = OpAccessChain %_ptr_Uniform_int %o %int_0 + %15 = OpLoad %int %14 + OpStore %c %15 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %17 + %17 = OpLabel + %20 = OpLoad %int %c + OpSelectionMerge %23 None + OpSwitch %20 %23 5 %21 1 %22 2 %22 3 %22 + %21 = OpLabel + OpBranch %24 + %24 = OpLabel + OpLoopMerge %26 %27 None + OpBranch %25 + %25 = OpLabel + %29 = OpAccessChain %_ptr_Uniform_int %o %int_2 + %30 = OpLoad %int %29 + OpSelectionMerge %33 None + OpSwitch %30 %32 10 %31 20 %31 + %32 = OpLabel + OpBranch %27 + %31 = OpLabel + %34 = OpLoad %int %c + %35 = OpLoad %int %c + %36 = OpIAdd %int %35 %34 + OpStore %c %36 + OpBranch %26 + %33 = OpLabel + OpUnreachable + %27 = OpLabel + OpBranch %24 + %26 = OpLabel + OpBranch %23 + %22 = OpLabel + %42 = OpLoad %int %c + OpStore %a %42 + OpBranch %18 + %23 = OpLabel + %45 = OpLoad %int %c + %47 = OpIAdd %int %45 %int_1 + OpStore %c %47 + OpBranch %19 + %19 = OpLabel + OpBranch %16 + %18 = OpLabel + %48 = OpLoad %int %a + %49 = OpAccessChain %_ptr_Uniform_int %o %int_1 + OpStore %49 %48 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag b/third_party/spirv-cross/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag new file mode 100644 index 0000000..ba2f95b --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/combined-sampler-reuse.vk.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vUV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTex "uTex" + OpName %uSampler "uSampler" + OpName %vUV "vUV" + OpDecorate %FragColor Location 0 + OpDecorate %uTex DescriptorSet 0 + OpDecorate %uTex Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 0 + OpDecorate %vUV Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %uTex = OpVariable %_ptr_UniformConstant_10 UniformConstant + %14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 + %uSampler = OpVariable %_ptr_UniformConstant_14 UniformConstant + %18 = OpTypeSampledImage %10 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %vUV = OpVariable %_ptr_Input_v2float Input + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_1 = OpConstant %int 1 + %32 = OpConstantComposite %v2int %int_1 %int_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %10 %uTex + %17 = OpLoad %14 %uSampler + %19 = OpSampledImage %18 %13 %17 + %23 = OpLoad %v2float %vUV + %24 = OpImageSampleImplicitLod %v4float %19 %23 + OpStore %FragColor %24 + %28 = OpLoad %v2float %vUV + %33 = OpImageSampleImplicitLod %v4float %19 %28 ConstOffset %32 + %34 = OpLoad %v4float %FragColor + %35 = OpFAdd %v4float %34 %33 + OpStore %FragColor %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/complex-name-workarounds.asm.frag b/third_party/spirv-cross/shaders/asm/frag/complex-name-workarounds.asm.frag new file mode 100644 index 0000000..59a6773 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/complex-name-workarounds.asm.frag @@ -0,0 +1,81 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 47 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %a %b %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %func__vf4_ "fu__nc_" + OpName %a_ "a_" + OpName %func_2_vf4_ "fu__nc_" + OpName %a_2 "___" + OpName %c0 "___" + OpName %a "__" + OpName %b "a" + OpName %param "b" + OpName %c1 "b" + OpName %param_0 "b" + OpName %FragColor "b" + OpDecorate %a Location 0 + OpDecorate %b Location 1 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %9 = OpTypeFunction %v4float %_ptr_Function_v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float + %a = OpVariable %_ptr_Input_v4float Input + %b = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %c0 = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_v4float Function + %c1 = OpVariable %_ptr_Function_v4float Function + %param_0 = OpVariable %_ptr_Function_v4float Function + %25 = OpLoad %v4float %a + %27 = OpLoad %v4float %b + %28 = OpFAdd %v4float %25 %27 + %30 = OpLoad %v4float %a + OpStore %param %30 + %31 = OpFunctionCall %v4float %func__vf4_ %param + %32 = OpFAdd %v4float %28 %31 + OpStore %c0 %32 + %34 = OpLoad %v4float %a + %35 = OpLoad %v4float %b + %36 = OpFSub %v4float %34 %35 + %38 = OpLoad %v4float %b + OpStore %param_0 %38 + %39 = OpFunctionCall %v4float %func_2_vf4_ %param_0 + %40 = OpFAdd %v4float %36 %39 + OpStore %c1 %40 + %43 = OpLoad %v4float %c0 + OpStore %FragColor %43 + %44 = OpLoad %v4float %c1 + OpStore %FragColor %44 + %45 = OpLoad %v4float %c0 + OpStore %FragColor %45 + %46 = OpLoad %v4float %c1 + OpStore %FragColor %46 + OpReturn + OpFunctionEnd + %func__vf4_ = OpFunction %v4float None %9 + %a_ = OpFunctionParameter %_ptr_Function_v4float + %12 = OpLabel + %16 = OpLoad %v4float %a_ + OpReturnValue %16 + OpFunctionEnd +%func_2_vf4_ = OpFunction %v4float None %9 + %a_2 = OpFunctionParameter %_ptr_Function_v4float + %15 = OpLabel + %19 = OpLoad %v4float %a_2 + OpReturnValue %19 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag b/third_party/spirv-cross/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag new file mode 100644 index 0000000..f33c486 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/composite-construct-struct-no-swizzle.asm.frag @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 39 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %foo %FooOut + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %foo "foo" + OpName %SwizzleTest "SwizzleTest" + OpMemberName %SwizzleTest 0 "a" + OpMemberName %SwizzleTest 1 "b" + OpName %FooOut "FooOut" + OpDecorate %foo RelaxedPrecision + OpDecorate %foo Location 0 + OpDecorate %12 RelaxedPrecision + OpMemberDecorate %SwizzleTest 0 RelaxedPrecision + OpMemberDecorate %SwizzleTest 1 RelaxedPrecision + OpDecorate %FooOut RelaxedPrecision + OpDecorate %FooOut Location 0 + OpDecorate %34 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float + %foo = OpVariable %_ptr_Input_v2float Input +%SwizzleTest = OpTypeStruct %float %float +%_ptr_Function_SwizzleTest = OpTypePointer Function %SwizzleTest + %uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float + %FooOut = OpVariable %_ptr_Output_float Output + %int = OpTypeInt 32 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpLoad %v2float %foo + %36 = OpCompositeExtract %float %12 0 + %38 = OpCompositeExtract %float %12 1 + %test0 = OpCompositeConstruct %SwizzleTest %36 %38 + %new0 = OpCompositeExtract %float %test0 0 + %new1 = OpCompositeExtract %float %test0 1 + %34 = OpFAdd %float %new0 %new1 + OpStore %FooOut %34 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/default-member-names.asm.frag b/third_party/spirv-cross/shaders/asm/frag/default-member-names.asm.frag new file mode 100644 index 0000000..ba493c0 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/default-member-names.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 43 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpDecorate %3 Location 0 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %12 = OpTypeFunction %v4float + %_struct_5 = OpTypeStruct %float + %_struct_6 = OpTypeStruct %float %float %float %float %float %float %float %float %float %float %float %float %_struct_5 +%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_float = OpTypePointer Function %float + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %2 = OpFunction %void None %9 + %22 = OpLabel + %23 = OpVariable %_ptr_Function__struct_6 Function + %24 = OpAccessChain %_ptr_Function_float %23 %int_0 + %25 = OpLoad %float %24 + %26 = OpAccessChain %_ptr_Function_float %23 %int_1 + %27 = OpLoad %float %26 + %28 = OpAccessChain %_ptr_Function_float %23 %int_2 + %29 = OpLoad %float %28 + %30 = OpAccessChain %_ptr_Function_float %23 %int_3 + %31 = OpLoad %float %30 + %32 = OpCompositeConstruct %v4float %25 %27 %29 %31 + OpStore %3 %32 + OpReturn + OpFunctionEnd + %4 = OpFunction %v4float None %12 + %33 = OpLabel + %7 = OpVariable %_ptr_Function__struct_6 Function + %34 = OpAccessChain %_ptr_Function_float %7 %int_0 + %35 = OpLoad %float %34 + %36 = OpAccessChain %_ptr_Function_float %7 %int_1 + %37 = OpLoad %float %36 + %38 = OpAccessChain %_ptr_Function_float %7 %int_2 + %39 = OpLoad %float %38 + %40 = OpAccessChain %_ptr_Function_float %7 %int_3 + %41 = OpLoad %float %40 + %42 = OpCompositeConstruct %v4float %35 %37 %39 %41 + OpReturnValue %42 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/do-while-statement-fallback.asm.frag b/third_party/spirv-cross/shaders/asm/frag/do-while-statement-fallback.asm.frag new file mode 100644 index 0000000..08de3ef --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/do-while-statement-fallback.asm.frag @@ -0,0 +1,76 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 35 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %foo "foo" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %bool = OpTypeBool + %false = OpConstantFalse %bool + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %float_5 = OpConstant %float 5 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %foo = OpVariable %_ptr_Function_float Function + OpStore %foo %float_1 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %11 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + OpStore %foo %float_2 + OpBranchConditional %false %10 %12 + %12 = OpLabel + OpBranch %17 + %17 = OpLabel + OpLoopMerge %19 %20 None + OpBranch %18 + %18 = OpLabel + OpBranch %20 + %20 = OpLabel + OpStore %foo %float_3 + OpBranchConditional %false %17 %19 + %19 = OpLabel + OpBranch %22 + %22 = OpLabel + OpLoopMerge %24 %25 None + OpBranch %23 + %23 = OpLabel + OpBranch %25 + %25 = OpLabel + OpStore %foo %float_4 + OpBranchConditional %false %22 %24 + %24 = OpLabel + OpBranch %27 + %27 = OpLabel + OpLoopMerge %29 %30 None + OpBranch %28 + %28 = OpLabel + OpBranch %30 + %30 = OpLabel + OpStore %foo %float_5 + OpBranchConditional %false %27 %29 + %29 = OpLabel + %34 = OpLoad %float %foo + OpStore %FragColor %34 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/empty-struct.asm.frag b/third_party/spirv-cross/shaders/asm/frag/empty-struct.asm.frag new file mode 100644 index 0000000..0efd315 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/empty-struct.asm.frag @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.2 +; Generator: Khronos; 0 +; Bound: 43 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %EntryPoint_Main "main" + OpExecutionMode %EntryPoint_Main OriginUpperLeft + OpSource Unknown 100 + OpName %EmptyStructTest "EmptyStructTest" + OpName %GetValue "GetValue" + OpName %GetValue2 "GetValue" + OpName %self "self" + OpName %self2 "self" + OpName %emptyStruct "emptyStruct" + OpName %value "value" + OpName %EntryPoint_Main "EntryPoint_Main" + +%EmptyStructTest = OpTypeStruct +%_ptr_Function_EmptyStructTest = OpTypePointer Function %EmptyStructTest + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %5 = OpTypeFunction %float %_ptr_Function_EmptyStructTest + %6 = OpTypeFunction %float %EmptyStructTest + %void = OpTypeVoid +%_ptr_Function_void = OpTypePointer Function %void + %8 = OpTypeFunction %void %_ptr_Function_EmptyStructTest + %9 = OpTypeFunction %void + %float_0 = OpConstant %float 0 + + %GetValue = OpFunction %float None %5 + %self = OpFunctionParameter %_ptr_Function_EmptyStructTest + %13 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + + %GetValue2 = OpFunction %float None %6 + %self2 = OpFunctionParameter %EmptyStructTest + %14 = OpLabel + OpReturnValue %float_0 + OpFunctionEnd + +%EntryPoint_Main = OpFunction %void None %9 + %37 = OpLabel + %emptyStruct = OpVariable %_ptr_Function_EmptyStructTest Function + %18 = OpVariable %_ptr_Function_EmptyStructTest Function + %value = OpVariable %_ptr_Function_float Function + %value2 = OpCompositeConstruct %EmptyStructTest + %22 = OpFunctionCall %float %GetValue %emptyStruct + %23 = OpFunctionCall %float %GetValue2 %value2 + OpStore %value %22 + OpStore %value %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/for-loop-phi-only-continue.asm.frag b/third_party/spirv-cross/shaders/asm/frag/for-loop-phi-only-continue.asm.frag new file mode 100644 index 0000000..ae84b30 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/for-loop-phi-only-continue.asm.frag @@ -0,0 +1,48 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 51 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_16 = OpConstant %int 16 + %bool = OpTypeBool + %float_1 = OpConstant %float 1 + %int_1 = OpConstant %int 1 + %float_2 = OpConstant %float 2 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %14 + %14 = OpLabel + %50 = OpPhi %float %float_0 %5 %25 %15 + %47 = OpPhi %int %int_0 %5 %28 %15 + %22 = OpSLessThan %bool %47 %int_16 + OpLoopMerge %16 %15 None + OpBranchConditional %22 %body1 %16 + %body1 = OpLabel + %25 = OpFAdd %float %50 %float_1 + %28 = OpIAdd %int %47 %int_1 + OpBranch %15 + %15 = OpLabel + OpBranch %14 + %16 = OpLabel + %46 = OpCompositeConstruct %v4float %50 %50 %50 %50 + OpStore %FragColor %46 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/frem.asm.frag b/third_party/spirv-cross/shaders/asm/frag/frem.asm.frag new file mode 100644 index 0000000..8350c75 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/frem.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 16 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA RelaxedPrecision + OpDecorate %vA Location 0 + OpDecorate %12 RelaxedPrecision + OpDecorate %vB RelaxedPrecision + OpDecorate %vB Location 1 + OpDecorate %14 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float + %vA = OpVariable %_ptr_Input_v4float Input + %vB = OpVariable %_ptr_Input_v4float Input + %main = OpFunction %void None %3 + %5 = OpLabel + %12 = OpLoad %v4float %vA + %14 = OpLoad %v4float %vB + %15 = OpFRem %v4float %12 %14 + OpStore %FragColor %15 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/function-overload-alias.asm.frag b/third_party/spirv-cross/shaders/asm/frag/function-overload-alias.asm.frag new file mode 100644 index 0000000..397aa98 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/function-overload-alias.asm.frag @@ -0,0 +1,153 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 76 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %foobar_vf4_ "foo" + OpName %a "foo" + OpName %foobar_vf3_ "foo" + OpName %a_0 "foo" + OpName %foobaz_vf4_ "foo" + OpName %a_1 "foo" + OpName %foobaz_vf2_ "foo" + OpName %a_2 "foo" + OpName %a_3 "foo" + OpName %param "foo" + OpName %b "foo" + OpName %param_0 "foo" + OpName %c "foo" + OpName %param_1 "foo" + OpName %d "foo" + OpName %param_2 "foo" + OpName %FragColor "FragColor" + OpDecorate %foobar_vf4_ RelaxedPrecision + OpDecorate %a RelaxedPrecision + OpDecorate %foobar_vf3_ RelaxedPrecision + OpDecorate %a_0 RelaxedPrecision + OpDecorate %foobaz_vf4_ RelaxedPrecision + OpDecorate %a_1 RelaxedPrecision + OpDecorate %foobaz_vf2_ RelaxedPrecision + OpDecorate %a_2 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %a_3 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %b RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %c RelaxedPrecision + OpDecorate %62 RelaxedPrecision + OpDecorate %d RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %69 RelaxedPrecision + OpDecorate %70 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %74 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %9 = OpTypeFunction %v4float %_ptr_Function_v4float + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %15 = OpTypeFunction %v4float %_ptr_Function_v3float + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %24 = OpTypeFunction %v4float %_ptr_Function_v2float + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %53 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %57 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %64 = OpConstantComposite %v2float %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %a_3 = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_v4float Function + %b = OpVariable %_ptr_Function_v4float Function + %param_0 = OpVariable %_ptr_Function_v3float Function + %c = OpVariable %_ptr_Function_v4float Function + %param_1 = OpVariable %_ptr_Function_v4float Function + %d = OpVariable %_ptr_Function_v4float Function + %param_2 = OpVariable %_ptr_Function_v2float Function + OpStore %param %53 + %55 = OpFunctionCall %v4float %foobar_vf4_ %param + OpStore %a_3 %55 + OpStore %param_0 %57 + %59 = OpFunctionCall %v4float %foobar_vf3_ %param_0 + OpStore %b %59 + OpStore %param_1 %53 + %62 = OpFunctionCall %v4float %foobaz_vf4_ %param_1 + OpStore %c %62 + OpStore %param_2 %64 + %66 = OpFunctionCall %v4float %foobaz_vf2_ %param_2 + OpStore %d %66 + %69 = OpLoad %v4float %a_3 + %70 = OpLoad %v4float %b + %71 = OpFAdd %v4float %69 %70 + %72 = OpLoad %v4float %c + %73 = OpFAdd %v4float %71 %72 + %74 = OpLoad %v4float %d + %75 = OpFAdd %v4float %73 %74 + OpStore %FragColor %75 + OpReturn + OpFunctionEnd +%foobar_vf4_ = OpFunction %v4float None %9 + %a = OpFunctionParameter %_ptr_Function_v4float + %12 = OpLabel + %28 = OpLoad %v4float %a + %30 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 + %31 = OpFAdd %v4float %28 %30 + OpReturnValue %31 + OpFunctionEnd +%foobar_vf3_ = OpFunction %v4float None %15 + %a_0 = OpFunctionParameter %_ptr_Function_v3float + %18 = OpLabel + %34 = OpLoad %v3float %a_0 + %35 = OpVectorShuffle %v4float %34 %34 0 1 2 2 + %36 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 + %37 = OpFAdd %v4float %35 %36 + OpReturnValue %37 + OpFunctionEnd +%foobaz_vf4_ = OpFunction %v4float None %9 + %a_1 = OpFunctionParameter %_ptr_Function_v4float + %21 = OpLabel + %40 = OpLoad %v4float %a_1 + %42 = OpCompositeConstruct %v4float %float_2 %float_2 %float_2 %float_2 + %43 = OpFAdd %v4float %40 %42 + OpReturnValue %43 + OpFunctionEnd +%foobaz_vf2_ = OpFunction %v4float None %24 + %a_2 = OpFunctionParameter %_ptr_Function_v2float + %27 = OpLabel + %46 = OpLoad %v2float %a_2 + %47 = OpVectorShuffle %v4float %46 %46 0 1 0 1 + %48 = OpCompositeConstruct %v4float %float_2 %float_2 %float_2 %float_2 + %49 = OpFAdd %v4float %47 %48 + OpReturnValue %49 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag b/third_party/spirv-cross/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag new file mode 100644 index 0000000..75ce80b --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/hlsl-sample-cmp-level-zero-cube.asm.frag @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 38 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %pointLightShadowMap "pointLightShadowMap" + OpName %shadowSamplerPCF "shadowSamplerPCF" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %pointLightShadowMap DescriptorSet 0 + OpDecorate %shadowSamplerPCF DescriptorSet 0 + OpDecorate %pointLightShadowMap Binding 0 + OpDecorate %shadowSamplerPCF Binding 1 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %10 = OpTypeImage %float Cube 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 +%pointLightShadowMap = OpVariable %_ptr_UniformConstant_10 UniformConstant + %14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 +%shadowSamplerPCF = OpVariable %_ptr_UniformConstant_14 UniformConstant + %18 = OpTypeImage %float Cube 1 0 0 1 Unknown + %19 = OpTypeSampledImage %18 + %v3float = OpTypeVector %float 3 + %float_0_1 = OpConstant %float 0.1 + %23 = OpConstantComposite %v3float %float_0_1 %float_0_1 %float_0_1 + %float_0_5 = OpConstant %float 0.5 + %v4float = OpTypeVector %float 4 + %float_0 = OpConstant %float 0 +%_ptr_Output_float = OpTypePointer Output %float +%_entryPointOutput = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %37 = OpFunctionCall %float %_main_ + OpStore %_entryPointOutput %37 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %float None %7 + %9 = OpLabel + %13 = OpLoad %10 %pointLightShadowMap + %17 = OpLoad %14 %shadowSamplerPCF + %20 = OpSampledImage %19 %13 %17 + %26 = OpCompositeExtract %float %23 0 + %27 = OpCompositeExtract %float %23 1 + %28 = OpCompositeExtract %float %23 2 + %29 = OpCompositeConstruct %v4float %26 %27 %28 %float_0_5 + %31 = OpCompositeExtract %float %29 3 + %32 = OpImageSampleDrefExplicitLod %float %20 %29 %31 Lod %float_0 + OpReturnValue %32 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag b/third_party/spirv-cross/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag new file mode 100644 index 0000000..bb0a1ea --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/hlsl-sample-cmp-level-zero.asm.frag @@ -0,0 +1,115 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 70 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %texCoords_1 %cascadeIndex_1 %fragDepth_1 %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_vf2_f1_f1_ "@main(vf2;f1;f1;" + OpName %texCoords "texCoords" + OpName %cascadeIndex "cascadeIndex" + OpName %fragDepth "fragDepth" + OpName %c "c" + OpName %ShadowMap "ShadowMap" + OpName %ShadowSamplerPCF "ShadowSamplerPCF" + OpName %texCoords_0 "texCoords" + OpName %texCoords_1 "texCoords" + OpName %cascadeIndex_0 "cascadeIndex" + OpName %cascadeIndex_1 "cascadeIndex" + OpName %fragDepth_0 "fragDepth" + OpName %fragDepth_1 "fragDepth" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpName %param_1 "param" + OpDecorate %ShadowMap DescriptorSet 0 + OpDecorate %ShadowSamplerPCF DescriptorSet 0 + OpDecorate %ShadowMap Binding 0 + OpDecorate %ShadowSamplerPCF Binding 1 + OpDecorate %texCoords_1 Location 0 + OpDecorate %cascadeIndex_1 Location 1 + OpDecorate %fragDepth_1 Location 2 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Function_float = OpTypePointer Function %float + %v4float = OpTypeVector %float 4 + %11 = OpTypeFunction %v4float %_ptr_Function_v2float %_ptr_Function_float %_ptr_Function_float + %18 = OpTypeImage %float 2D 0 1 0 1 Unknown +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %ShadowMap = OpVariable %_ptr_UniformConstant_18 UniformConstant + %22 = OpTypeSampler +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 +%ShadowSamplerPCF = OpVariable %_ptr_UniformConstant_22 UniformConstant + %26 = OpTypeImage %float 2D 1 1 0 1 Unknown + %27 = OpTypeSampledImage %26 + %v3float = OpTypeVector %float 3 + %float_0 = OpConstant %float 0 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords_1 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_float = OpTypePointer Input %float +%cascadeIndex_1 = OpVariable %_ptr_Input_float Input +%fragDepth_1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel +%texCoords_0 = OpVariable %_ptr_Function_v2float Function +%cascadeIndex_0 = OpVariable %_ptr_Function_float Function +%fragDepth_0 = OpVariable %_ptr_Function_float Function + %param = OpVariable %_ptr_Function_v2float Function + %param_0 = OpVariable %_ptr_Function_float Function + %param_1 = OpVariable %_ptr_Function_float Function + %53 = OpLoad %v2float %texCoords_1 + OpStore %texCoords_0 %53 + %57 = OpLoad %float %cascadeIndex_1 + OpStore %cascadeIndex_0 %57 + %60 = OpLoad %float %fragDepth_1 + OpStore %fragDepth_0 %60 + %64 = OpLoad %v2float %texCoords_0 + OpStore %param %64 + %66 = OpLoad %float %cascadeIndex_0 + OpStore %param_0 %66 + %68 = OpLoad %float %fragDepth_0 + OpStore %param_1 %68 + %69 = OpFunctionCall %v4float %_main_vf2_f1_f1_ %param %param_0 %param_1 + OpStore %_entryPointOutput %69 + OpReturn + OpFunctionEnd +%_main_vf2_f1_f1_ = OpFunction %v4float None %11 + %texCoords = OpFunctionParameter %_ptr_Function_v2float +%cascadeIndex = OpFunctionParameter %_ptr_Function_float + %fragDepth = OpFunctionParameter %_ptr_Function_float + %16 = OpLabel + %c = OpVariable %_ptr_Function_float Function + %21 = OpLoad %18 %ShadowMap + %25 = OpLoad %22 %ShadowSamplerPCF + %28 = OpSampledImage %27 %21 %25 + %29 = OpLoad %v2float %texCoords + %30 = OpLoad %float %cascadeIndex + %32 = OpCompositeExtract %float %29 0 + %33 = OpCompositeExtract %float %29 1 + %34 = OpCompositeConstruct %v3float %32 %33 %30 + %35 = OpLoad %float %fragDepth + %36 = OpCompositeExtract %float %34 0 + %37 = OpCompositeExtract %float %34 1 + %38 = OpCompositeExtract %float %34 2 + %39 = OpCompositeConstruct %v4float %36 %37 %38 %35 + %41 = OpCompositeExtract %float %39 3 + %42 = OpImageSampleDrefExplicitLod %float %28 %39 %41 Lod %float_0 + OpStore %c %42 + %43 = OpLoad %float %c + %44 = OpLoad %float %c + %45 = OpLoad %float %c + %46 = OpLoad %float %c + %47 = OpCompositeConstruct %v4float %43 %44 %45 %46 + OpReturnValue %47 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/image-extract-reuse.asm.frag b/third_party/spirv-cross/shaders/asm/frag/image-extract-reuse.asm.frag new file mode 100644 index 0000000..63c8ab5 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/image-extract-reuse.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 19 +; Schema: 0 + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %Size + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %Size "Size" + OpName %uTexture "uTexture" + OpDecorate %Size Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%_ptr_Output_v2int = OpTypePointer Output %v2int + %Size = OpVariable %_ptr_Output_v2int Output + %float = OpTypeFloat 32 + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown + %12 = OpTypeSampledImage %11 +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_12 UniformConstant + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpLoad %12 %uTexture + %17 = OpImage %11 %15 + %18 = OpImageQuerySizeLod %v2int %17 %int_0 + %19 = OpImageQuerySizeLod %v2int %17 %int_1 + %20 = OpIAdd %v2int %18 %19 + OpStore %Size %20 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag b/third_party/spirv-cross/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag new file mode 100644 index 0000000..a3d64c0 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/image-fetch-no-sampler.asm.vk.frag @@ -0,0 +1,163 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 113 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %xIn_1 %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %sample_fetch_t21_vi3_ "sample_fetch(t21;vi3;" + OpName %tex "tex" + OpName %UV "UV" + OpName %sample_sampler_t21_vf2_ "sample_sampler(t21;vf2;" + OpName %tex_0 "tex" + OpName %UV_0 "UV" + OpName %_main_vf4_ "@main(vf4;" + OpName %xIn "xIn" + OpName %Sampler "Sampler" + OpName %coord "coord" + OpName %value "value" + OpName %SampledImage "SampledImage" + OpName %param "param" + OpName %param_0 "param" + OpName %param_1 "param" + OpName %param_2 "param" + OpName %xIn_0 "xIn" + OpName %xIn_1 "xIn" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param_3 "param" + OpDecorate %Sampler DescriptorSet 0 + OpDecorate %Sampler Binding 0 + OpDecorate %SampledImage DescriptorSet 0 + OpDecorate %SampledImage Binding 0 + OpDecorate %xIn_1 BuiltIn FragCoord + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_Function_7 = OpTypePointer Function %7 + %int = OpTypeInt 32 1 + %v3int = OpTypeVector %int 3 +%_ptr_Function_v3int = OpTypePointer Function %v3int + %v4float = OpTypeVector %float 4 + %13 = OpTypeFunction %v4float %_ptr_Function_7 %_ptr_Function_v3int + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %20 = OpTypeFunction %v4float %_ptr_Function_7 %_ptr_Function_v2float +%_ptr_Function_v4float = OpTypePointer Function %v4float + %26 = OpTypeFunction %v4float %_ptr_Function_v4float + %v2int = OpTypeVector %int 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_Function_int = OpTypePointer Function %int + %43 = OpTypeSampler +%_ptr_UniformConstant_43 = OpTypePointer UniformConstant %43 + %Sampler = OpVariable %_ptr_UniformConstant_43 UniformConstant + %47 = OpTypeSampledImage %7 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %float_1280 = OpConstant %float 1280 + %uint_1 = OpConstant %uint 1 + %float_720 = OpConstant %float 720 + %int_0 = OpConstant %int 0 +%_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7 +%SampledImage = OpVariable %_ptr_UniformConstant_7 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float + %xIn_1 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %xIn_0 = OpVariable %_ptr_Function_v4float Function + %param_3 = OpVariable %_ptr_Function_v4float Function + %107 = OpLoad %v4float %xIn_1 + OpStore %xIn_0 %107 + %111 = OpLoad %v4float %xIn_0 + OpStore %param_3 %111 + %112 = OpFunctionCall %v4float %_main_vf4_ %param_3 + OpStore %_entryPointOutput %112 + OpReturn + OpFunctionEnd +%sample_fetch_t21_vi3_ = OpFunction %v4float None %13 + %tex = OpFunctionParameter %_ptr_Function_7 + %UV = OpFunctionParameter %_ptr_Function_v3int + %17 = OpLabel + %30 = OpLoad %7 %tex + %32 = OpLoad %v3int %UV + %33 = OpVectorShuffle %v2int %32 %32 0 1 + %37 = OpAccessChain %_ptr_Function_int %UV %uint_2 + %38 = OpLoad %int %37 + %39 = OpImageFetch %v4float %30 %33 Lod %38 + OpReturnValue %39 + OpFunctionEnd +%sample_sampler_t21_vf2_ = OpFunction %v4float None %20 + %tex_0 = OpFunctionParameter %_ptr_Function_7 + %UV_0 = OpFunctionParameter %_ptr_Function_v2float + %24 = OpLabel + %42 = OpLoad %7 %tex_0 + %46 = OpLoad %43 %Sampler + %48 = OpSampledImage %47 %42 %46 + %49 = OpLoad %v2float %UV_0 + %50 = OpImageSampleImplicitLod %v4float %48 %49 + OpReturnValue %50 + OpFunctionEnd + %_main_vf4_ = OpFunction %v4float None %26 + %xIn = OpFunctionParameter %_ptr_Function_v4float + %29 = OpLabel + %coord = OpVariable %_ptr_Function_v3int Function + %value = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_7 Function + %param_0 = OpVariable %_ptr_Function_v3int Function + %param_1 = OpVariable %_ptr_Function_7 Function + %param_2 = OpVariable %_ptr_Function_v2float Function + %56 = OpAccessChain %_ptr_Function_float %xIn %uint_0 + %57 = OpLoad %float %56 + %59 = OpFMul %float %57 %float_1280 + %60 = OpConvertFToS %int %59 + %62 = OpAccessChain %_ptr_Function_float %xIn %uint_1 + %63 = OpLoad %float %62 + %65 = OpFMul %float %63 %float_720 + %66 = OpConvertFToS %int %65 + %68 = OpCompositeConstruct %v3int %60 %66 %int_0 + OpStore %coord %68 + %73 = OpLoad %7 %SampledImage + OpStore %param %73 + %75 = OpLoad %v3int %coord + OpStore %param_0 %75 + %76 = OpFunctionCall %v4float %sample_fetch_t21_vi3_ %param %param_0 + OpStore %value %76 + %77 = OpLoad %7 %SampledImage + %78 = OpLoad %v3int %coord + %79 = OpVectorShuffle %v2int %78 %78 0 1 + %80 = OpAccessChain %_ptr_Function_int %coord %uint_2 + %81 = OpLoad %int %80 + %82 = OpImageFetch %v4float %77 %79 Lod %81 + %83 = OpLoad %v4float %value + %84 = OpFAdd %v4float %83 %82 + OpStore %value %84 + %86 = OpLoad %7 %SampledImage + OpStore %param_1 %86 + %88 = OpLoad %v4float %xIn + %89 = OpVectorShuffle %v2float %88 %88 0 1 + OpStore %param_2 %89 + %90 = OpFunctionCall %v4float %sample_sampler_t21_vf2_ %param_1 %param_2 + %91 = OpLoad %v4float %value + %92 = OpFAdd %v4float %91 %90 + OpStore %value %92 + %93 = OpLoad %7 %SampledImage + %94 = OpLoad %43 %Sampler + %95 = OpSampledImage %47 %93 %94 + %96 = OpLoad %v4float %xIn + %97 = OpVectorShuffle %v2float %96 %96 0 1 + %98 = OpImageSampleImplicitLod %v4float %95 %97 + %99 = OpLoad %v4float %value + %100 = OpFAdd %v4float %99 %98 + OpStore %value %100 + %101 = OpLoad %v4float %value + OpReturnValue %101 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag b/third_party/spirv-cross/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag new file mode 100644 index 0000000..a3d64c0 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/image-fetch-no-sampler.no-samplerless.asm.vk.frag @@ -0,0 +1,163 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 113 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %xIn_1 %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %sample_fetch_t21_vi3_ "sample_fetch(t21;vi3;" + OpName %tex "tex" + OpName %UV "UV" + OpName %sample_sampler_t21_vf2_ "sample_sampler(t21;vf2;" + OpName %tex_0 "tex" + OpName %UV_0 "UV" + OpName %_main_vf4_ "@main(vf4;" + OpName %xIn "xIn" + OpName %Sampler "Sampler" + OpName %coord "coord" + OpName %value "value" + OpName %SampledImage "SampledImage" + OpName %param "param" + OpName %param_0 "param" + OpName %param_1 "param" + OpName %param_2 "param" + OpName %xIn_0 "xIn" + OpName %xIn_1 "xIn" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param_3 "param" + OpDecorate %Sampler DescriptorSet 0 + OpDecorate %Sampler Binding 0 + OpDecorate %SampledImage DescriptorSet 0 + OpDecorate %SampledImage Binding 0 + OpDecorate %xIn_1 BuiltIn FragCoord + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_Function_7 = OpTypePointer Function %7 + %int = OpTypeInt 32 1 + %v3int = OpTypeVector %int 3 +%_ptr_Function_v3int = OpTypePointer Function %v3int + %v4float = OpTypeVector %float 4 + %13 = OpTypeFunction %v4float %_ptr_Function_7 %_ptr_Function_v3int + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %20 = OpTypeFunction %v4float %_ptr_Function_7 %_ptr_Function_v2float +%_ptr_Function_v4float = OpTypePointer Function %v4float + %26 = OpTypeFunction %v4float %_ptr_Function_v4float + %v2int = OpTypeVector %int 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_ptr_Function_int = OpTypePointer Function %int + %43 = OpTypeSampler +%_ptr_UniformConstant_43 = OpTypePointer UniformConstant %43 + %Sampler = OpVariable %_ptr_UniformConstant_43 UniformConstant + %47 = OpTypeSampledImage %7 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %float_1280 = OpConstant %float 1280 + %uint_1 = OpConstant %uint 1 + %float_720 = OpConstant %float 720 + %int_0 = OpConstant %int 0 +%_ptr_UniformConstant_7 = OpTypePointer UniformConstant %7 +%SampledImage = OpVariable %_ptr_UniformConstant_7 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float + %xIn_1 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %xIn_0 = OpVariable %_ptr_Function_v4float Function + %param_3 = OpVariable %_ptr_Function_v4float Function + %107 = OpLoad %v4float %xIn_1 + OpStore %xIn_0 %107 + %111 = OpLoad %v4float %xIn_0 + OpStore %param_3 %111 + %112 = OpFunctionCall %v4float %_main_vf4_ %param_3 + OpStore %_entryPointOutput %112 + OpReturn + OpFunctionEnd +%sample_fetch_t21_vi3_ = OpFunction %v4float None %13 + %tex = OpFunctionParameter %_ptr_Function_7 + %UV = OpFunctionParameter %_ptr_Function_v3int + %17 = OpLabel + %30 = OpLoad %7 %tex + %32 = OpLoad %v3int %UV + %33 = OpVectorShuffle %v2int %32 %32 0 1 + %37 = OpAccessChain %_ptr_Function_int %UV %uint_2 + %38 = OpLoad %int %37 + %39 = OpImageFetch %v4float %30 %33 Lod %38 + OpReturnValue %39 + OpFunctionEnd +%sample_sampler_t21_vf2_ = OpFunction %v4float None %20 + %tex_0 = OpFunctionParameter %_ptr_Function_7 + %UV_0 = OpFunctionParameter %_ptr_Function_v2float + %24 = OpLabel + %42 = OpLoad %7 %tex_0 + %46 = OpLoad %43 %Sampler + %48 = OpSampledImage %47 %42 %46 + %49 = OpLoad %v2float %UV_0 + %50 = OpImageSampleImplicitLod %v4float %48 %49 + OpReturnValue %50 + OpFunctionEnd + %_main_vf4_ = OpFunction %v4float None %26 + %xIn = OpFunctionParameter %_ptr_Function_v4float + %29 = OpLabel + %coord = OpVariable %_ptr_Function_v3int Function + %value = OpVariable %_ptr_Function_v4float Function + %param = OpVariable %_ptr_Function_7 Function + %param_0 = OpVariable %_ptr_Function_v3int Function + %param_1 = OpVariable %_ptr_Function_7 Function + %param_2 = OpVariable %_ptr_Function_v2float Function + %56 = OpAccessChain %_ptr_Function_float %xIn %uint_0 + %57 = OpLoad %float %56 + %59 = OpFMul %float %57 %float_1280 + %60 = OpConvertFToS %int %59 + %62 = OpAccessChain %_ptr_Function_float %xIn %uint_1 + %63 = OpLoad %float %62 + %65 = OpFMul %float %63 %float_720 + %66 = OpConvertFToS %int %65 + %68 = OpCompositeConstruct %v3int %60 %66 %int_0 + OpStore %coord %68 + %73 = OpLoad %7 %SampledImage + OpStore %param %73 + %75 = OpLoad %v3int %coord + OpStore %param_0 %75 + %76 = OpFunctionCall %v4float %sample_fetch_t21_vi3_ %param %param_0 + OpStore %value %76 + %77 = OpLoad %7 %SampledImage + %78 = OpLoad %v3int %coord + %79 = OpVectorShuffle %v2int %78 %78 0 1 + %80 = OpAccessChain %_ptr_Function_int %coord %uint_2 + %81 = OpLoad %int %80 + %82 = OpImageFetch %v4float %77 %79 Lod %81 + %83 = OpLoad %v4float %value + %84 = OpFAdd %v4float %83 %82 + OpStore %value %84 + %86 = OpLoad %7 %SampledImage + OpStore %param_1 %86 + %88 = OpLoad %v4float %xIn + %89 = OpVectorShuffle %v2float %88 %88 0 1 + OpStore %param_2 %89 + %90 = OpFunctionCall %v4float %sample_sampler_t21_vf2_ %param_1 %param_2 + %91 = OpLoad %v4float %value + %92 = OpFAdd %v4float %91 %90 + OpStore %value %92 + %93 = OpLoad %7 %SampledImage + %94 = OpLoad %43 %Sampler + %95 = OpSampledImage %47 %93 %94 + %96 = OpLoad %v4float %xIn + %97 = OpVectorShuffle %v2float %96 %96 0 1 + %98 = OpImageSampleImplicitLod %v4float %95 %97 + %99 = OpLoad %v4float %value + %100 = OpFAdd %v4float %99 %98 + OpStore %value %100 + %101 = OpLoad %v4float %value + OpReturnValue %101 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag b/third_party/spirv-cross/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag new file mode 100644 index 0000000..a232bd4 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/image-query-no-sampler.no-samplerless.vk.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %b "b" + OpName %uSampler2D "uSampler2D" + OpName %c "c" + OpName %uSampler2DMS "uSampler2DMS" + OpName %l1 "l1" + OpName %s0 "s0" + OpDecorate %uSampler2D DescriptorSet 0 + OpDecorate %uSampler2D Binding 0 + OpDecorate %uSampler2DMS DescriptorSet 0 + OpDecorate %uSampler2DMS Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%_ptr_Function_v2int = OpTypePointer Function %v2int + %float = OpTypeFloat 32 + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %11 + %uSampler2D = OpVariable %_ptr_UniformConstant_12 UniformConstant + %int_0 = OpConstant %int 0 + %20 = OpTypeImage %float 2D 0 0 1 1 Unknown +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %20 +%uSampler2DMS = OpVariable %_ptr_UniformConstant_21 UniformConstant +%_ptr_Function_int = OpTypePointer Function %int + %main = OpFunction %void None %3 + %5 = OpLabel + %b = OpVariable %_ptr_Function_v2int Function + %c = OpVariable %_ptr_Function_v2int Function + %l1 = OpVariable %_ptr_Function_int Function + %s0 = OpVariable %_ptr_Function_int Function + %15 = OpLoad %11 %uSampler2D + %18 = OpImageQuerySizeLod %v2int %15 %int_0 + OpStore %b %18 + %24 = OpLoad %20 %uSampler2DMS + %26 = OpImageQuerySize %v2int %24 + OpStore %c %26 + %29 = OpLoad %11 %uSampler2D + %31 = OpImageQueryLevels %int %29 + OpStore %l1 %31 + %33 = OpLoad %20 %uSampler2DMS + %35 = OpImageQuerySamples %int %33 + OpStore %s0 %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/image-query-no-sampler.vk.asm.frag b/third_party/spirv-cross/shaders/asm/frag/image-query-no-sampler.vk.asm.frag new file mode 100644 index 0000000..a232bd4 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/image-query-no-sampler.vk.asm.frag @@ -0,0 +1,57 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %b "b" + OpName %uSampler2D "uSampler2D" + OpName %c "c" + OpName %uSampler2DMS "uSampler2DMS" + OpName %l1 "l1" + OpName %s0 "s0" + OpDecorate %uSampler2D DescriptorSet 0 + OpDecorate %uSampler2D Binding 0 + OpDecorate %uSampler2DMS DescriptorSet 0 + OpDecorate %uSampler2DMS Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 +%_ptr_Function_v2int = OpTypePointer Function %v2int + %float = OpTypeFloat 32 + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %11 + %uSampler2D = OpVariable %_ptr_UniformConstant_12 UniformConstant + %int_0 = OpConstant %int 0 + %20 = OpTypeImage %float 2D 0 0 1 1 Unknown +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %20 +%uSampler2DMS = OpVariable %_ptr_UniformConstant_21 UniformConstant +%_ptr_Function_int = OpTypePointer Function %int + %main = OpFunction %void None %3 + %5 = OpLabel + %b = OpVariable %_ptr_Function_v2int Function + %c = OpVariable %_ptr_Function_v2int Function + %l1 = OpVariable %_ptr_Function_int Function + %s0 = OpVariable %_ptr_Function_int Function + %15 = OpLoad %11 %uSampler2D + %18 = OpImageQuerySizeLod %v2int %15 %int_0 + OpStore %b %18 + %24 = OpLoad %20 %uSampler2DMS + %26 = OpImageQuerySize %v2int %24 + OpStore %c %26 + %29 = OpLoad %11 %uSampler2D + %31 = OpImageQueryLevels %int %29 + OpStore %l1 %31 + %33 = OpLoad %20 %uSampler2DMS + %35 = OpImageQuerySamples %int %33 + OpStore %s0 %35 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/implicit-read-dep-phi.asm.frag b/third_party/spirv-cross/shaders/asm/frag/implicit-read-dep-phi.asm.frag new file mode 100644 index 0000000..ccdfeef --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/implicit-read-dep-phi.asm.frag @@ -0,0 +1,81 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 60 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %v0 %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %phi "phi" + OpName %i "i" + OpName %v0 "v0" + OpName %FragColor "FragColor" + OpName %uImage "uImage" + OpDecorate %v0 Location 0 + OpDecorate %FragColor Location 0 + OpDecorate %uImage DescriptorSet 0 + OpDecorate %uImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstant %float 1 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 + %bool = OpTypeBool + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %36 = OpTypeImage %float 2D 0 0 0 1 Unknown + %37 = OpTypeSampledImage %36 +%_ptr_UniformConstant_37 = OpTypePointer UniformConstant %37 + %uImage = OpVariable %_ptr_UniformConstant_37 UniformConstant + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %float_2 = OpConstant %float 2 + %int_1 = OpConstant %int 1 + %float_1_vec = OpConstantComposite %v4float %float_1 %float_2 %float_1 %float_2 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpStore %i %int_0 + OpBranch %loop_header + %loop_header = OpLabel + %phi = OpPhi %float %float_1 %5 %phi_plus_2 %continue_block + %tex_phi = OpPhi %v4float %float_1_vec %5 %texture_load_result %continue_block + OpLoopMerge %merge_block %continue_block None + OpBranch %loop_body + %loop_body = OpLabel + OpStore %FragColor %tex_phi + %19 = OpLoad %int %i + %22 = OpSLessThan %bool %19 %int_4 + OpBranchConditional %22 %15 %merge_block + %15 = OpLabel + %26 = OpLoad %int %i + %28 = OpAccessChain %_ptr_Input_float %v0 %26 + %29 = OpLoad %float %28 + %31 = OpFOrdGreaterThan %bool %29 %float_0 + OpBranchConditional %31 %continue_block %merge_block + %continue_block = OpLabel + %40 = OpLoad %37 %uImage + %43 = OpCompositeConstruct %v2float %phi %phi + %texture_load_result = OpImageSampleExplicitLod %v4float %40 %43 Lod %float_0 + %phi_plus_2 = OpFAdd %float %phi %float_2 + %54 = OpLoad %int %i + %56 = OpIAdd %int %54 %int_1 + OpStore %i %56 + OpBranch %loop_header + %merge_block = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/inf-nan-constant-double.asm.frag b/third_party/spirv-cross/shaders/asm/frag/inf-nan-constant-double.asm.frag new file mode 100644 index 0000000..2d0c18a --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/inf-nan-constant-double.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 22 +; Schema: 0 + OpCapability Shader + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vTmp + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vTmp "vTmp" + OpDecorate %FragColor Location 0 + OpDecorate %vTmp Flat + OpDecorate %vTmp Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %FragColor = OpVariable %_ptr_Output_v3float Output + %double = OpTypeFloat 64 + %v3double = OpTypeVector %double 3 +%double_0x1p_1024 = OpConstant %double 0x1p+1024 +%double_n0x1p_1024 = OpConstant %double -0x1p+1024 +%double_0x1_8p_1024 = OpConstant %double 0x1.8p+1024 + %15 = OpConstantComposite %v3double %double_0x1p_1024 %double_n0x1p_1024 %double_0x1_8p_1024 +%_ptr_Input_double = OpTypePointer Input %double + %vTmp = OpVariable %_ptr_Input_double Input + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpLoad %double %vTmp + %19 = OpCompositeConstruct %v3double %18 %18 %18 + %20 = OpFAdd %v3double %15 %19 + %21 = OpFConvert %v3float %20 + OpStore %FragColor %21 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/inf-nan-constant.asm.frag b/third_party/spirv-cross/shaders/asm/frag/inf-nan-constant.asm.frag new file mode 100644 index 0000000..40e5d3a --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/inf-nan-constant.asm.frag @@ -0,0 +1,29 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 14 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Output_v3float = OpTypePointer Output %v3float + %FragColor = OpVariable %_ptr_Output_v3float Output +%float_0x1p_128 = OpConstant %float 0x1p+128 +%float_n0x1p_128 = OpConstant %float -0x1p+128 +%float_0x1_8p_128 = OpConstant %float 0x1.8p+128 + %13 = OpConstantComposite %v3float %float_0x1p_128 %float_n0x1p_128 %float_0x1_8p_128 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %FragColor %13 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/invalidation.asm.frag b/third_party/spirv-cross/shaders/asm/frag/invalidation.asm.frag new file mode 100644 index 0000000..8e753d5 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/invalidation.asm.frag @@ -0,0 +1,46 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %v0 %v1 %FragColor + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %a "a" + OpName %v0 "v0" + OpName %b "b" + OpName %v1 "v1" + OpName %FragColor "FragColor" + OpDecorate %v0 Location 0 + OpDecorate %v1 Location 1 + OpDecorate %FragColor Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %float = OpTypeFloat 32 + %pfloat = OpTypePointer Function %float + %9 = OpTypePointer Input %float + %v0 = OpVariable %9 Input + %v1 = OpVariable %9 Input + %25 = OpTypePointer Output %float + %FragColor = OpVariable %25 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %a = OpVariable %pfloat Function + %b = OpVariable %pfloat Function + %v0_tmp = OpLoad %float %v0 + %v1_tmp = OpLoad %float %v1 + OpStore %a %v0_tmp + OpStore %b %v1_tmp + + %a_tmp = OpLoad %float %a + %b_tmp = OpLoad %float %b + %res = OpFAdd %float %a_tmp %b_tmp + %res1 = OpFMul %float %res %b_tmp + OpStore %a %v1_tmp + OpStore %FragColor %res1 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/line-directive.line.asm.frag b/third_party/spirv-cross/shaders/asm/frag/line-directive.line.asm.frag new file mode 100644 index 0000000..9e08e9a --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/line-directive.line.asm.frag @@ -0,0 +1,221 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 7 +; Bound: 83 +; Schema: 0 + OpCapability Shader + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vColor + OpExecutionMode %main OriginUpperLeft + %1 = OpString "test.frag" + OpSource GLSL 450 %1 "// OpModuleProcessed entry-point main +// OpModuleProcessed client vulkan100 +// OpModuleProcessed target-env vulkan1.0 +// OpModuleProcessed entry-point main +#line 1 +#version 450 + +layout(location = 0) in float vColor; +layout(location = 0) out float FragColor; + +void func() +{ + FragColor = 1.0; + FragColor = 2.0; + if (vColor < 0.0) + { + FragColor = 3.0; + } + else + { + FragColor = 4.0; + } + + for (int i = 0; i < 40 + vColor; i += int(vColor) + 5) + { + FragColor += 0.2; + FragColor += 0.3; + } + + switch (int(vColor)) + { + case 0: + FragColor += 0.2; + break; + + case 1: + FragColor += 0.4; + break; + + default: + FragColor += 0.8; + break; + } + + do + { + FragColor += 10.0 + vColor; + } while(FragColor < 100.0); +} + +void main() +{ + func(); +} +" + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %func_ "func(" + OpName %FragColor "FragColor" + OpName %vColor "vColor" + OpName %i "i" + OpDecorate %FragColor Location 0 + OpDecorate %vColor Location 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 +%_ptr_Input_float = OpTypePointer Input %float + %vColor = OpVariable %_ptr_Input_float Input + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %int = OpTypeInt 32 1 + + ; Should be ignored + OpLine %1 5 0 + +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %float_40 = OpConstant %float 40 +%float_0_200000003 = OpConstant %float 0.200000003 +%float_0_300000012 = OpConstant %float 0.300000012 + %int_5 = OpConstant %int 5 + + ; Should be ignored + OpLine %1 5 0 + +%float_0_400000006 = OpConstant %float 0.400000006 +%float_0_800000012 = OpConstant %float 0.800000012 + %float_10 = OpConstant %float 10 + %float_100 = OpConstant %float 100 + %main = OpFunction %void None %4 + OpLine %1 46 0 + %6 = OpLabel + OpLine %1 48 0 + %82 = OpFunctionCall %void %func_ + OpReturn + OpFunctionEnd + + ; Should be ignored + OpLine %1 5 0 + + %func_ = OpFunction %void None %4 + OpLine %1 6 0 + %8 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpLine %1 8 0 + OpStore %FragColor %float_1 + OpLine %1 9 0 + OpStore %FragColor %float_2 + OpLine %1 10 0 + %16 = OpLoad %float %vColor + %19 = OpFOrdLessThan %bool %16 %float_0 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %23 + %20 = OpLabel + OpLine %1 12 0 + OpStore %FragColor %float_3 + OpBranch %21 + %23 = OpLabel + OpLine %1 16 0 + OpStore %FragColor %float_4 + OpBranch %21 + %21 = OpLabel + OpLine %1 19 0 + OpStore %i %int_0 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %int %i + %35 = OpConvertSToF %float %34 + %37 = OpLoad %float %vColor + %38 = OpFAdd %float %float_40 %37 + %39 = OpFOrdLessThan %bool %35 %38 + OpBranchConditional %39 %30 %31 + %30 = OpLabel + OpLine %1 21 0 + %41 = OpLoad %float %FragColor + %42 = OpFAdd %float %41 %float_0_200000003 + OpStore %FragColor %42 + OpLine %1 22 0 + %44 = OpLoad %float %FragColor + %45 = OpFAdd %float %44 %float_0_300000012 + OpStore %FragColor %45 + OpBranch %32 + %32 = OpLabel + OpLine %1 19 0 + %46 = OpLoad %float %vColor + %47 = OpConvertFToS %int %46 + %49 = OpIAdd %int %47 %int_5 + %50 = OpLoad %int %i + %51 = OpIAdd %int %50 %49 + OpStore %i %51 + OpBranch %29 + %31 = OpLabel + OpLine %1 25 0 + %52 = OpLoad %float %vColor + %53 = OpConvertFToS %int %52 + OpSelectionMerge %57 None + OpSwitch %53 %56 0 %54 1 %55 + %56 = OpLabel + OpLine %1 36 0 + %66 = OpLoad %float %FragColor + %67 = OpFAdd %float %66 %float_0_800000012 + OpStore %FragColor %67 + OpLine %1 37 0 + OpBranch %57 + %54 = OpLabel + OpLine %1 28 0 + %58 = OpLoad %float %FragColor + %59 = OpFAdd %float %58 %float_0_200000003 + OpStore %FragColor %59 + OpLine %1 29 0 + OpBranch %57 + %55 = OpLabel + OpLine %1 32 0 + %62 = OpLoad %float %FragColor + %63 = OpFAdd %float %62 %float_0_400000006 + OpStore %FragColor %63 + OpLine %1 33 0 + OpBranch %57 + %57 = OpLabel + OpBranch %70 + OpLine %1 43 0 + %70 = OpLabel + OpLoopMerge %72 %73 None + OpBranch %71 + %71 = OpLabel + OpLine %1 42 0 + %75 = OpLoad %float %vColor + %76 = OpFAdd %float %float_10 %75 + %77 = OpLoad %float %FragColor + %78 = OpFAdd %float %77 %76 + OpStore %FragColor %78 + OpBranch %73 + %73 = OpLabel + OpLine %1 43 0 + %79 = OpLoad %float %FragColor + %81 = OpFOrdLessThan %bool %79 %float_100 + OpBranchConditional %81 %70 %72 + %72 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/locations-components.asm.frag b/third_party/spirv-cross/shaders/asm/frag/locations-components.asm.frag new file mode 100644 index 0000000..16bfc52 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/locations-components.asm.frag @@ -0,0 +1,103 @@ +; SPIR-V +; Version: 1.0 +; Generator: Wine VKD3D Shader Compiler; 0 +; Bound: 67 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %8 %16 %22 %28 %33 %o0 + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %v1 "v1" + OpName %v2 "v2" + OpName %o0 "o0" + OpName %r0 "r0" + OpDecorate %8 Location 1 + OpDecorate %16 Location 1 + OpDecorate %16 Component 2 + OpDecorate %22 Location 2 + OpDecorate %22 Flat + OpDecorate %28 Location 2 + OpDecorate %28 Component 1 + OpDecorate %28 Flat + OpDecorate %33 Location 2 + OpDecorate %33 Component 2 + OpDecorate %33 Flat + OpDecorate %o0 Location 0 + %void = OpTypeVoid + %2 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %8 = OpVariable %_ptr_Input_v2float Input + %v4float = OpTypeVector %float 4 +%_ptr_Private_v4float = OpTypePointer Private %v4float + %v1 = OpVariable %_ptr_Private_v4float Private +%_ptr_Input_float = OpTypePointer Input %float + %16 = OpVariable %_ptr_Input_float Input +%_ptr_Private_float = OpTypePointer Private %float + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 + %22 = OpVariable %_ptr_Input_float Input + %v2 = OpVariable %_ptr_Private_v4float Private + %uint_0 = OpConstant %uint 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %28 = OpVariable %_ptr_Input_uint Input + %uint_1 = OpConstant %uint 1 + %33 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %o0 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %int = OpTypeInt 32 1 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float + %main = OpFunction %void None %2 + %4 = OpLabel + %r0 = OpVariable %_ptr_Function_v4float Function + %12 = OpLoad %v2float %8 + %13 = OpLoad %v4float %v1 + %14 = OpVectorShuffle %v4float %13 %12 4 5 2 3 + OpStore %v1 %14 + %17 = OpLoad %float %16 + %21 = OpInBoundsAccessChain %_ptr_Private_float %v1 %uint_2 + OpStore %21 %17 + %24 = OpLoad %float %22 + %26 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_0 + OpStore %26 %24 + %29 = OpLoad %uint %28 + %30 = OpBitcast %float %29 + %32 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_1 + OpStore %32 %30 + %34 = OpLoad %uint %33 + %35 = OpBitcast %float %34 + %36 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_2 + OpStore %36 %35 + %42 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_1 + %43 = OpLoad %float %42 + %44 = OpBitcast %int %43 + %45 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_2 + %46 = OpLoad %float %45 + %47 = OpBitcast %int %46 + %48 = OpIAdd %int %44 %47 + %49 = OpBitcast %float %48 + %51 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_0 + OpStore %51 %49 + %52 = OpInBoundsAccessChain %_ptr_Function_float %r0 %uint_0 + %53 = OpLoad %float %52 + %54 = OpBitcast %uint %53 + %55 = OpConvertUToF %float %54 + %57 = OpInBoundsAccessChain %_ptr_Output_float %o0 %uint_1 + OpStore %57 %55 + %58 = OpInBoundsAccessChain %_ptr_Private_float %v1 %uint_1 + %59 = OpLoad %float %58 + %60 = OpInBoundsAccessChain %_ptr_Private_float %v2 %uint_0 + %61 = OpLoad %float %60 + %62 = OpFAdd %float %59 %61 + %63 = OpInBoundsAccessChain %_ptr_Output_float %o0 %uint_0 + OpStore %63 %62 + %64 = OpLoad %v4float %v1 + %65 = OpLoad %v4float %o0 + %66 = OpVectorShuffle %v4float %65 %64 0 1 6 4 + OpStore %o0 %66 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag b/third_party/spirv-cross/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag new file mode 100644 index 0000000..fa53940 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/loop-body-dominator-continue-access.asm.frag @@ -0,0 +1,190 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 131 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %fragWorld_1 %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %GetClip2TexMatrix_ "GetClip2TexMatrix(" + OpName %GetCascade_vf3_ "GetCascade(vf3;" + OpName %fragWorldPosition "fragWorldPosition" + OpName %_main_vf3_ "@main(vf3;" + OpName %fragWorld "fragWorld" + OpName %Foo "Foo" + OpMemberName %Foo 0 "lightVP" + OpMemberName %Foo 1 "shadowCascadesNum" + OpMemberName %Foo 2 "test" + OpName %_ "" + OpName %cascadeIndex "cascadeIndex" + OpName %worldToShadowMap "worldToShadowMap" + OpName %fragShadowMapPos "fragShadowMapPos" + OpName %param "param" + OpName %fragWorld_0 "fragWorld" + OpName %fragWorld_1 "fragWorld" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param_0 "param" + OpDecorate %_arr_mat4v4float_uint_64 ArrayStride 64 + OpMemberDecorate %Foo 0 RowMajor + OpMemberDecorate %Foo 0 Offset 0 + OpMemberDecorate %Foo 0 MatrixStride 16 + OpMemberDecorate %Foo 1 Offset 4096 + OpMemberDecorate %Foo 2 Offset 4100 + OpDecorate %Foo Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %fragWorld_1 Location 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 + %9 = OpTypeFunction %mat4v4float + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %int = OpTypeInt 32 1 + %15 = OpTypeFunction %int %_ptr_Function_v3float + %uint = OpTypeInt 32 0 + %uint_64 = OpConstant %uint 64 +%_arr_mat4v4float_uint_64 = OpTypeArray %mat4v4float %uint_64 + %Foo = OpTypeStruct %_arr_mat4v4float_uint_64 %uint %int +%_ptr_Uniform_Foo = OpTypePointer Uniform %Foo + %_ = OpVariable %_ptr_Uniform_Foo Uniform + %int_2 = OpConstant %int 2 +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %float_0_5 = OpConstant %float 0.5 + %float_0 = OpConstant %float 0 + %39 = OpConstantComposite %v4float %float_0_5 %float_0 %float_0 %float_0 + %40 = OpConstantComposite %v4float %float_0 %float_0_5 %float_0 %float_0 + %41 = OpConstantComposite %v4float %float_0 %float_0 %float_0_5 %float_0 + %float_1 = OpConstant %float 1 + %43 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 + %44 = OpConstantComposite %mat4v4float %39 %40 %41 %43 + %46 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_0 + %47 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_0 + %48 = OpConstantComposite %v4float %float_0 %float_0 %float_1 %float_0 + %49 = OpConstantComposite %mat4v4float %46 %47 %48 %43 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_0 = OpConstant %uint 0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Function_mat4v4float = OpTypePointer Function %mat4v4float +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float + %uint_2 = OpConstant %uint 2 +%_ptr_Function_float = OpTypePointer Function %float + %uint_1 = OpConstant %uint 1 + %int_n1 = OpConstant %int -1 +%_ptr_Input_v3float = OpTypePointer Input %v3float +%fragWorld_1 = OpVariable %_ptr_Input_v3float Input +%_ptr_Output_int = OpTypePointer Output %int +%_entryPointOutput = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %3 + %5 = OpLabel +%fragWorld_0 = OpVariable %_ptr_Function_v3float Function + %param_0 = OpVariable %_ptr_Function_v3float Function + %125 = OpLoad %v3float %fragWorld_1 + OpStore %fragWorld_0 %125 + %129 = OpLoad %v3float %fragWorld_0 + OpStore %param_0 %129 + %130 = OpFunctionCall %int %_main_vf3_ %param_0 + OpStore %_entryPointOutput %130 + OpReturn + OpFunctionEnd +%GetClip2TexMatrix_ = OpFunction %mat4v4float None %9 + %11 = OpLabel + %30 = OpAccessChain %_ptr_Uniform_int %_ %int_2 + %31 = OpLoad %int %30 + %34 = OpIEqual %bool %31 %int_0 + OpSelectionMerge %36 None + OpBranchConditional %34 %35 %36 + %35 = OpLabel + OpReturnValue %44 + %36 = OpLabel + OpReturnValue %49 + OpFunctionEnd +%GetCascade_vf3_ = OpFunction %int None %15 +%fragWorldPosition = OpFunctionParameter %_ptr_Function_v3float + %18 = OpLabel +%cascadeIndex = OpVariable %_ptr_Function_uint Function +%worldToShadowMap = OpVariable %_ptr_Function_mat4v4float Function +%fragShadowMapPos = OpVariable %_ptr_Function_v4float Function + OpStore %cascadeIndex %uint_0 + OpBranch %55 + %55 = OpLabel + OpLoopMerge %57 %58 Unroll + OpBranch %59 + %59 = OpLabel + %60 = OpLoad %uint %cascadeIndex + %63 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 + %64 = OpLoad %uint %63 + %65 = OpULessThan %bool %60 %64 + OpBranchConditional %65 %56 %57 + %56 = OpLabel + %68 = OpFunctionCall %mat4v4float %GetClip2TexMatrix_ + %69 = OpLoad %uint %cascadeIndex + %71 = OpAccessChain %_ptr_Uniform_mat4v4float %_ %int_0 %69 + %72 = OpLoad %mat4v4float %71 + %73 = OpMatrixTimesMatrix %mat4v4float %68 %72 + OpStore %worldToShadowMap %73 + %76 = OpLoad %mat4v4float %worldToShadowMap + %77 = OpLoad %v3float %fragWorldPosition + %78 = OpCompositeExtract %float %77 0 + %79 = OpCompositeExtract %float %77 1 + %80 = OpCompositeExtract %float %77 2 + %81 = OpCompositeConstruct %v4float %78 %79 %80 %float_1 + %82 = OpMatrixTimesVector %v4float %76 %81 + OpStore %fragShadowMapPos %82 + %85 = OpAccessChain %_ptr_Function_float %fragShadowMapPos %uint_2 + %86 = OpLoad %float %85 + %87 = OpFOrdGreaterThanEqual %bool %86 %float_0 + %88 = OpAccessChain %_ptr_Function_float %fragShadowMapPos %uint_2 + %89 = OpLoad %float %88 + %90 = OpFOrdLessThanEqual %bool %89 %float_1 + %91 = OpLogicalAnd %bool %87 %90 + %92 = OpAccessChain %_ptr_Function_float %fragShadowMapPos %uint_0 + %93 = OpLoad %float %92 + %95 = OpAccessChain %_ptr_Function_float %fragShadowMapPos %uint_1 + %96 = OpLoad %float %95 + %97 = OpExtInst %float %1 FMax %93 %96 + %98 = OpFOrdLessThanEqual %bool %97 %float_1 + %99 = OpLogicalAnd %bool %91 %98 + %100 = OpAccessChain %_ptr_Function_float %fragShadowMapPos %uint_0 + %101 = OpLoad %float %100 + %102 = OpAccessChain %_ptr_Function_float %fragShadowMapPos %uint_1 + %103 = OpLoad %float %102 + %104 = OpExtInst %float %1 FMin %101 %103 + %105 = OpFOrdGreaterThanEqual %bool %104 %float_0 + %106 = OpLogicalAnd %bool %99 %105 + OpSelectionMerge %108 None + OpBranchConditional %106 %107 %108 + %107 = OpLabel + %109 = OpLoad %uint %cascadeIndex + %110 = OpBitcast %int %109 + OpReturnValue %110 + %108 = OpLabel + OpBranch %58 + %58 = OpLabel + %112 = OpLoad %uint %cascadeIndex + %113 = OpIAdd %uint %112 %int_1 + OpStore %cascadeIndex %113 + OpBranch %55 + %57 = OpLabel + OpReturnValue %int_n1 + OpFunctionEnd + %_main_vf3_ = OpFunction %int None %15 + %fragWorld = OpFunctionParameter %_ptr_Function_v3float + %21 = OpLabel + %param = OpVariable %_ptr_Function_v3float Function + %118 = OpLoad %v3float %fragWorld + OpStore %param %118 + %119 = OpFunctionCall %int %GetCascade_vf3_ %param + OpReturnValue %119 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/loop-header-to-continue.asm.frag b/third_party/spirv-cross/shaders/asm/frag/loop-header-to-continue.asm.frag new file mode 100644 index 0000000..54807d9 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/loop-header-to-continue.asm.frag @@ -0,0 +1,132 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 279 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %IN_p %IN_uv %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %Params "Params" + OpMemberName %Params 0 "TextureSize" + OpMemberName %Params 1 "Params1" + OpMemberName %Params 2 "Params2" + OpMemberName %Params 3 "Params3" + OpMemberName %Params 4 "Params4" + OpMemberName %Params 5 "Bloom" + OpName %CB1 "CB1" + OpMemberName %CB1 0 "CB1" + OpName %_ "" + OpName %mapSampler "mapSampler" + OpName %mapTexture "mapTexture" + OpName %IN_p "IN.p" + OpName %IN_uv "IN.uv" + OpName %_entryPointOutput "@entryPointOutput" + OpMemberDecorate %Params 0 Offset 0 + OpMemberDecorate %Params 1 Offset 16 + OpMemberDecorate %Params 2 Offset 32 + OpMemberDecorate %Params 3 Offset 48 + OpMemberDecorate %Params 4 Offset 64 + OpMemberDecorate %Params 5 Offset 80 + OpMemberDecorate %CB1 0 Offset 0 + OpDecorate %CB1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpDecorate %mapSampler DescriptorSet 1 + OpDecorate %mapSampler Binding 2 + OpDecorate %mapTexture DescriptorSet 1 + OpDecorate %mapTexture Binding 2 + OpDecorate %IN_p BuiltIn FragCoord + OpDecorate %IN_uv Location 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %9 = OpTypeSampler + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown + %v4float = OpTypeVector %float 4 +%float_0_222222 = OpConstant %float 0.222222 + %33 = OpTypeSampledImage %11 + %uint = OpTypeInt 32 0 + %float_80 = OpConstant %float 80 +%float_0_0008 = OpConstant %float 0.0008 +%float_8en05 = OpConstant %float 8e-05 +%float_0_008 = OpConstant %float 0.008 + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 1 + %int_n3 = OpConstant %int -3 + %int_3 = OpConstant %int 3 + %bool = OpTypeBool + %float_1 = OpConstant %float 1 + %int_1 = OpConstant %int 1 + %Params = OpTypeStruct %v4float %v4float %v4float %v4float %v4float %v4float + %CB1 = OpTypeStruct %Params +%_ptr_Uniform_CB1 = OpTypePointer Uniform %CB1 + %_ = OpVariable %_ptr_Uniform_CB1 Uniform + %int_0 = OpConstant %int 0 + %uint_3 = OpConstant %uint 3 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_UniformConstant_9 = OpTypePointer UniformConstant %9 + %mapSampler = OpVariable %_ptr_UniformConstant_9 UniformConstant +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %mapTexture = OpVariable %_ptr_UniformConstant_11 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float + %IN_p = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_v2float = OpTypePointer Input %v2float + %IN_uv = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %158 = OpLoad %v2float %IN_uv + %178 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_0 %uint_3 + %179 = OpLoad %float %178 + %180 = OpCompositeConstruct %v2float %float_0 %179 + %184 = OpLoad %9 %mapSampler + %185 = OpLoad %11 %mapTexture + %204 = OpSampledImage %33 %185 %184 + %206 = OpImageSampleImplicitLod %v4float %204 %158 + %207 = OpCompositeExtract %float %206 1 + %209 = OpFMul %float %207 %float_80 + %210 = OpFMul %float %209 %float_0_0008 + %211 = OpExtInst %float %1 FClamp %210 %float_8en05 %float_0_008 + OpBranch %212 + %212 = OpLabel + %276 = OpPhi %float %float_0 %5 %252 %218 + %277 = OpPhi %float %float_0 %5 %255 %218 + %278 = OpPhi %int %int_n3 %5 %257 %218 + %217 = OpSLessThanEqual %bool %278 %int_3 + OpLoopMerge %213 %218 None + OpBranchConditional %217 %218 %213 + %218 = OpLabel + %220 = OpConvertSToF %float %278 + %222 = OpFNegate %float %220 + %224 = OpFMul %float %222 %220 + %226 = OpFMul %float %224 %float_0_222222 + %227 = OpExtInst %float %1 Exp %226 + %230 = OpSampledImage %33 %185 %184 + %234 = OpVectorTimesScalar %v2float %180 %220 + %235 = OpFAdd %v2float %158 %234 + %236 = OpImageSampleImplicitLod %v4float %230 %235 + %273 = OpCompositeExtract %float %236 1 + %241 = OpFSub %float %273 %207 + %242 = OpExtInst %float %1 FAbs %241 + %244 = OpFOrdLessThan %bool %242 %211 + %245 = OpSelect %float %244 %float_1 %float_0 + %246 = OpFMul %float %227 %245 + %275 = OpCompositeExtract %float %236 0 + %250 = OpFMul %float %275 %246 + %252 = OpFAdd %float %276 %250 + %255 = OpFAdd %float %277 %246 + %257 = OpIAdd %int %278 %int_1 + OpBranch %212 + %213 = OpLabel + %260 = OpFDiv %float %276 %277 + %190 = OpCompositeConstruct %v4float %260 %207 %float_0 %float_1 + OpStore %_entryPointOutput %190 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/lut-promotion-initializer.asm.frag b/third_party/spirv-cross/shaders/asm/frag/lut-promotion-initializer.asm.frag new file mode 100644 index 0000000..320e5eb --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/lut-promotion-initializer.asm.frag @@ -0,0 +1,195 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 111 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %index + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %index "index" + OpName %indexable "indexable" + OpName %indexable_0 "indexable" + OpName %indexable_1 "indexable" + OpName %foo "foo" + OpName %foobar "foobar" + OpName %baz "baz" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %index RelaxedPrecision + OpDecorate %index Flat + OpDecorate %index Location 0 + OpDecorate %20 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %foo RelaxedPrecision + OpDecorate %61 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %68 RelaxedPrecision + OpDecorate %71 RelaxedPrecision + OpDecorate %72 RelaxedPrecision + OpDecorate %73 RelaxedPrecision + OpDecorate %75 RelaxedPrecision + OpDecorate %76 RelaxedPrecision + OpDecorate %79 RelaxedPrecision + OpDecorate %80 RelaxedPrecision + OpDecorate %81 RelaxedPrecision + OpDecorate %foobar RelaxedPrecision + OpDecorate %83 RelaxedPrecision + OpDecorate %90 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %93 RelaxedPrecision + OpDecorate %94 RelaxedPrecision + OpDecorate %95 RelaxedPrecision + OpDecorate %baz RelaxedPrecision + OpDecorate %105 RelaxedPrecision + OpDecorate %106 RelaxedPrecision + OpDecorate %108 RelaxedPrecision + OpDecorate %109 RelaxedPrecision + OpDecorate %110 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %uint = OpTypeInt 32 0 + %uint_16 = OpConstant %uint 16 +%_arr_float_uint_16 = OpTypeArray %float %uint_16 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %16 = OpConstantComposite %_arr_float_uint_16 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 %float_1 %float_2 %float_3 %float_4 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %index = OpVariable %_ptr_Input_int Input +%_ptr_Function__arr_float_uint_16 = OpTypePointer Function %_arr_float_uint_16 +%_ptr_Function_float = OpTypePointer Function %float + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %v4float = OpTypeVector %float 4 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 + %float_0 = OpConstant %float 0 + %54 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %55 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %float_8 = OpConstant %float 8 + %57 = OpConstantComposite %v4float %float_8 %float_8 %float_8 %float_8 + %float_5 = OpConstant %float 5 + %59 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %60 = OpConstantComposite %_arr_v4float_uint_4 %54 %55 %57 %59 + %int_30 = OpConstant %int 30 + %int_3 = OpConstant %int 3 + %uint_1 = OpConstant %uint 1 + %uint_0 = OpConstant %uint 0 + %float_20 = OpConstant %float 20 + %uint_2 = OpConstant %uint 2 + %97 = OpConstantComposite %v4float %float_20 %float_20 %float_20 %float_20 + %float_30 = OpConstant %float 30 + %99 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 + %float_50 = OpConstant %float 50 + %101 = OpConstantComposite %v4float %float_50 %float_50 %float_50 %float_50 + %float_60 = OpConstant %float 60 + %103 = OpConstantComposite %v4float %float_60 %float_60 %float_60 %float_60 + %104 = OpConstantComposite %_arr_v4float_uint_4 %97 %99 %101 %103 + %main = OpFunction %void None %3 + %5 = OpLabel + %indexable = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_0 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 +%indexable_1 = OpVariable %_ptr_Function__arr_float_uint_16 Function %16 + %foo = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %foobar = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %baz = OpVariable %_ptr_Function__arr_v4float_uint_4 Function %60 + %20 = OpLoad %int %index + %24 = OpAccessChain %_ptr_Function_float %indexable %20 + %25 = OpLoad %float %24 + OpStore %FragColor %25 + %26 = OpLoad %int %index + %29 = OpSLessThan %bool %26 %int_10 + OpSelectionMerge %31 None + OpBranchConditional %29 %30 %40 + %30 = OpLabel + %32 = OpLoad %int %index + %34 = OpBitwiseXor %int %32 %int_1 + %36 = OpAccessChain %_ptr_Function_float %indexable_0 %34 + %37 = OpLoad %float %36 + %38 = OpLoad %float %FragColor + %39 = OpFAdd %float %38 %37 + OpStore %FragColor %39 + OpBranch %31 + %40 = OpLabel + %41 = OpLoad %int %index + %42 = OpBitwiseAnd %int %41 %int_1 + %44 = OpAccessChain %_ptr_Function_float %indexable_1 %42 + %45 = OpLoad %float %44 + %46 = OpLoad %float %FragColor + %47 = OpFAdd %float %46 %45 + OpStore %FragColor %47 + OpBranch %31 + %31 = OpLabel + %61 = OpLoad %int %index + %63 = OpSGreaterThan %bool %61 %int_30 + OpSelectionMerge %65 None + OpBranchConditional %63 %64 %74 + %64 = OpLabel + %66 = OpLoad %int %index + %68 = OpBitwiseAnd %int %66 %int_3 + %70 = OpAccessChain %_ptr_Function_float %foo %68 %uint_1 + %71 = OpLoad %float %70 + %72 = OpLoad %float %FragColor + %73 = OpFAdd %float %72 %71 + OpStore %FragColor %73 + OpBranch %65 + %74 = OpLabel + %75 = OpLoad %int %index + %76 = OpBitwiseAnd %int %75 %int_1 + %78 = OpAccessChain %_ptr_Function_float %foo %76 %uint_0 + %79 = OpLoad %float %78 + %80 = OpLoad %float %FragColor + %81 = OpFAdd %float %80 %79 + OpStore %FragColor %81 + OpBranch %65 + %65 = OpLabel + %83 = OpLoad %int %index + %84 = OpSGreaterThan %bool %83 %int_30 + OpSelectionMerge %86 None + OpBranchConditional %84 %85 %86 + %85 = OpLabel + %89 = OpAccessChain %_ptr_Function_float %foobar %int_1 %uint_2 + OpStore %89 %float_20 + OpBranch %86 + %86 = OpLabel + %90 = OpLoad %int %index + %91 = OpBitwiseAnd %int %90 %int_3 + %92 = OpAccessChain %_ptr_Function_float %foobar %91 %uint_2 + %93 = OpLoad %float %92 + %94 = OpLoad %float %FragColor + %95 = OpFAdd %float %94 %93 + OpStore %FragColor %95 + OpStore %baz %104 + %105 = OpLoad %int %index + %106 = OpBitwiseAnd %int %105 %int_3 + %107 = OpAccessChain %_ptr_Function_float %baz %106 %uint_2 + %108 = OpLoad %float %107 + %109 = OpLoad %float %FragColor + %110 = OpFAdd %float %109 %108 + OpStore %FragColor %110 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/multi-for-loop-init.asm.frag b/third_party/spirv-cross/shaders/asm/frag/multi-for-loop-init.asm.frag new file mode 100644 index 0000000..d74f7ce --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/multi-for-loop-init.asm.frag @@ -0,0 +1,111 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 52 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %counter %ucounter + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %i "i" + OpName %j "j" + OpName %counter "counter" + OpName %ucounter "ucounter" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %i RelaxedPrecision + OpDecorate %j RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %32 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %counter RelaxedPrecision + OpDecorate %counter Flat + OpDecorate %counter Location 0 + OpDecorate %43 RelaxedPrecision + OpDecorate %44 RelaxedPrecision + OpDecorate %45 RelaxedPrecision + OpDecorate %46 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %ucounter RelaxedPrecision + OpDecorate %ucounter Flat + OpDecorate %ucounter Location 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %uint 1 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_20 = OpConstant %uint 20 +%_ptr_Input_int = OpTypePointer Input %int + %counter = OpVariable %_ptr_Input_int Input +%_ptr_Input_uint = OpTypePointer Input %uint + %ucounter = OpVariable %_ptr_Input_uint Input + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_uint Function + OpStore %FragColor %11 + OpStore %i %int_0 + OpStore %j %int_1 + OpBranch %18 + %18 = OpLabel + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + %23 = OpLoad %int %i + %26 = OpSLessThan %bool %23 %int_10 + %27 = OpLoad %uint %j + %29 = OpSLessThan %bool %27 %int_20 + %30 = OpLogicalAnd %bool %26 %29 + OpBranchConditional %30 %19 %20 + %19 = OpLabel + %31 = OpLoad %int %i + %32 = OpConvertSToF %float %31 + %33 = OpCompositeConstruct %v4float %32 %32 %32 %32 + %34 = OpLoad %v4float %FragColor + %35 = OpFAdd %v4float %34 %33 + OpStore %FragColor %35 + %36 = OpLoad %uint %j + %37 = OpConvertUToF %float %36 + %38 = OpCompositeConstruct %v4float %37 %37 %37 %37 + %39 = OpLoad %v4float %FragColor + %40 = OpFAdd %v4float %39 %38 + OpStore %FragColor %40 + OpBranch %21 + %21 = OpLabel + %43 = OpLoad %int %counter + %44 = OpLoad %int %i + %45 = OpIAdd %int %44 %43 + OpStore %i %45 + %46 = OpLoad %int %counter + %47 = OpLoad %uint %j + %48 = OpIAdd %uint %47 %46 + OpStore %j %48 + OpBranch %18 + %20 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/op-constant-null.asm.frag b/third_party/spirv-cross/shaders/asm/frag/op-constant-null.asm.frag new file mode 100644 index 0000000..61d2e57 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/op-constant-null.asm.frag @@ -0,0 +1,85 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 45 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %a "a" + OpName %b "b" + OpName %c "c" + OpName %D "D" + OpMemberName %D 0 "a" + OpMemberName %D 1 "b" + OpName %d "d" + OpName %e "e" + OpName %FragColor "FragColor" + OpDecorate %a RelaxedPrecision + OpDecorate %b RelaxedPrecision + OpDecorate %c RelaxedPrecision + OpMemberDecorate %D 0 RelaxedPrecision + OpMemberDecorate %D 1 RelaxedPrecision + OpDecorate %e RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %44 RelaxedPrecision + OpDecorate %float_1 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstantNull %float + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %float_2 = OpConstantNull %float + %14 = OpConstantNull %v4float + %v3float = OpTypeVector %float 3 +%mat2v3float = OpTypeMatrix %v3float 2 +%_ptr_Function_mat2v3float = OpTypePointer Function %mat2v3float + %float_4 = OpConstantNull %float + %20 = OpConstantNull %v3float + %float_5 = OpConstantNull %float + %22 = OpConstantNull %v3float + %23 = OpConstantNull %mat2v3float + %D = OpTypeStruct %v4float %float +%_ptr_Function_D = OpTypePointer Function %D + %27 = OpConstantNull %D + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_v4float_uint_4 = OpTypeArray %v4float %uint_4 +%_ptr_Function__arr_v4float_uint_4 = OpTypePointer Function %_arr_v4float_uint_4 + %float_10 = OpConstantNull %float + %34 = OpConstantNull %v4float + %float_11 = OpConstantNull %float + %36 = OpConstantNull %v4float + %float_12 = OpConstantNull %float + %38 = OpConstantNull %v4float + %float_13 = OpConstantNull %float + %40 = OpConstantNull %v4float + %41 = OpConstantNull %_arr_v4float_uint_4 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %a = OpVariable %_ptr_Function_float Function + %b = OpVariable %_ptr_Function_v4float Function + %c = OpVariable %_ptr_Function_mat2v3float Function + %d = OpVariable %_ptr_Function_D Function + %e = OpVariable %_ptr_Function__arr_v4float_uint_4 Function + OpStore %a %float_1 + OpStore %b %14 + OpStore %c %23 + OpStore %d %27 + OpStore %e %41 + %44 = OpLoad %float %a + OpStore %FragColor %44 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/op-phi-swap-continue-block.asm.frag b/third_party/spirv-cross/shaders/asm/frag/op-phi-swap-continue-block.asm.frag new file mode 100644 index 0000000..afae325 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/op-phi-swap-continue-block.asm.frag @@ -0,0 +1,69 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 55 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %UBO "UBO" + OpMemberName %UBO 0 "uCount" + OpMemberName %UBO 1 "uJ" + OpMemberName %UBO 2 "uK" + OpName %_ "" + OpName %FragColor "FragColor" + OpMemberDecorate %UBO 0 Offset 0 + OpMemberDecorate %UBO 1 Offset 4 + OpMemberDecorate %UBO 2 Offset 8 + OpDecorate %UBO Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %UBO = OpTypeStruct %int %int %int +%_ptr_Uniform_UBO = OpTypePointer Uniform %UBO + %_ = OpVariable %_ptr_Uniform_UBO Uniform + %int_1 = OpConstant %int 1 +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_2 = OpConstant %int 2 + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_int %_ %int_1 + %15 = OpLoad %int %14 + %18 = OpAccessChain %_ptr_Uniform_int %_ %int_2 + %19 = OpLoad %int %18 + OpBranch %22 + %22 = OpLabel + %54 = OpPhi %int %19 %5 %53 %23 + %53 = OpPhi %int %15 %5 %54 %23 + %52 = OpPhi %int %int_0 %5 %37 %23 + %28 = OpAccessChain %_ptr_Uniform_int %_ %int_0 + %29 = OpLoad %int %28 + %31 = OpSLessThan %bool %52 %29 + OpLoopMerge %24 %23 None + OpBranchConditional %31 %inbetween %24 + %inbetween = OpLabel + OpBranch %23 + %23 = OpLabel + %37 = OpIAdd %int %52 %int_1 + OpBranch %22 + %24 = OpLabel + %43 = OpISub %int %53 %54 + %44 = OpConvertSToF %float %43 + %49 = OpIMul %int %15 %19 + %50 = OpConvertSToF %float %49 + %51 = OpFMul %float %44 %50 + OpStore %FragColor %51 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/pack-and-unpack-uint2.asm.frag b/third_party/spirv-cross/shaders/asm/frag/pack-and-unpack-uint2.asm.frag new file mode 100644 index 0000000..43d0970 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/pack-and-unpack-uint2.asm.frag @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 34 +; Schema: 0 + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_ARB_gpu_shader_int64" + OpName %main "main" + OpName %packed "packed" + OpName %unpacked "unpacked" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %ulong = OpTypeInt 64 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %uint_18 = OpConstant %uint 18 + %uint_52 = OpConstant %uint 52 + %13 = OpConstantComposite %v2uint %uint_18 %uint_52 +%_ptr_Function_v2uint = OpTypePointer Function %v2uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %uint_0 = OpConstant %uint 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_1 = OpConstant %uint 1 + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %packed = OpVariable %_ptr_Function_ulong Function + %unpacked = OpVariable %_ptr_Function_v2uint Function + %14 = OpBitcast %ulong %13 + OpStore %packed %14 + %17 = OpLoad %ulong %packed + %18 = OpBitcast %v2uint %17 + OpStore %unpacked %18 + %25 = OpAccessChain %_ptr_Function_uint %unpacked %uint_0 + %26 = OpLoad %uint %25 + %27 = OpConvertUToF %float %26 + %29 = OpAccessChain %_ptr_Function_uint %unpacked %uint_1 + %30 = OpLoad %uint %29 + %31 = OpConvertUToF %float %30 + %33 = OpCompositeConstruct %v4float %27 %31 %float_1 %float_1 + OpStore %FragColor %33 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/pass-by-value.asm.frag b/third_party/spirv-cross/shaders/asm/frag/pass-by-value.asm.frag new file mode 100644 index 0000000..083c85d --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/pass-by-value.asm.frag @@ -0,0 +1,51 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 32 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %add_value_f1_f1_ "add_value(f1;f1;" + OpName %v "v" + OpName %w "w" + OpName %FragColor "FragColor" + OpName %Registers "Registers" + OpMemberName %Registers 0 "foo" + OpName %registers "registers" + OpDecorate %FragColor Location 0 + OpMemberDecorate %Registers 0 Offset 0 + OpDecorate %Registers Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %8 = OpTypeFunction %float %float %float +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %float_10 = OpConstant %float 10 + %Registers = OpTypeStruct %float +%_ptr_PushConstant_Registers = OpTypePointer PushConstant %Registers + %registers = OpVariable %_ptr_PushConstant_Registers PushConstant + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_float = OpTypePointer PushConstant %float + %main = OpFunction %void None %3 + %5 = OpLabel + %29 = OpAccessChain %_ptr_PushConstant_float %registers %int_0 + %30 = OpLoad %float %29 + %31 = OpFunctionCall %float %add_value_f1_f1_ %float_10 %30 + OpStore %FragColor %31 + OpReturn + OpFunctionEnd +%add_value_f1_f1_ = OpFunction %float None %8 + %v = OpFunctionParameter %float + %w = OpFunctionParameter %float + %12 = OpLabel + %15 = OpFAdd %float %v %w + OpReturnValue %15 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/phi-loop-variable.asm.frag b/third_party/spirv-cross/shaders/asm/frag/phi-loop-variable.asm.frag new file mode 100644 index 0000000..74c46b4 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/phi-loop-variable.asm.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 59 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%mat2v2float = OpTypeMatrix %v2float 2 +%_ptr_Function_mat2v2float = OpTypePointer Function %mat2v2float + %v3float = OpTypeVector %float 3 + %11 = OpTypeFunction %v3float %_ptr_Function_mat2v2float +%_ptr_Function_v3float = OpTypePointer Function %v3float + %float_1 = OpConstant %float 1 + %18 = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_35 = OpConstant %int 35 + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %4 = OpFunction %void None %3 + %5 = OpLabel + OpBranch %48 + %48 = OpLabel + %58 = OpPhi %int %int_35 %5 %56 %50 + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %53 = OpSGreaterThanEqual %bool %58 %int_0 + OpBranchConditional %53 %54 %49 + %54 = OpLabel + OpBranch %50 + %50 = OpLabel + %56 = OpISub %int %58 %int_1 + OpBranch %48 + %49 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %v3float None %11 + %12 = OpFunctionParameter %_ptr_Function_mat2v2float + %14 = OpLabel + %16 = OpVariable %_ptr_Function_v3float Function + %21 = OpVariable %_ptr_Function_int Function + OpStore %16 %18 + OpStore %21 %int_35 + OpBranch %23 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %28 = OpLoad %int %21 + %31 = OpSGreaterThanEqual %bool %28 %int_0 + OpBranchConditional %31 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %32 = OpLoad %int %21 + %34 = OpISub %int %32 %int_1 + OpStore %21 %34 + OpBranch %23 + %25 = OpLabel + %35 = OpLoad %v3float %16 + OpReturnValue %35 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/sample-and-compare.asm.frag b/third_party/spirv-cross/shaders/asm/frag/sample-and-compare.asm.frag new file mode 100644 index 0000000..16dcd0d --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/sample-and-compare.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.0 +; Generator: Google spiregg; 0 +; Bound: 32 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_TEXCOORD0 %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_2d_image "type.2d.image" + OpName %g_Texture "g_Texture" + OpName %type_sampler "type.sampler" + OpName %g_Sampler "g_Sampler" + OpName %g_CompareSampler "g_CompareSampler" + OpName %in_var_TEXCOORD0 "in.var.TEXCOORD0" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD0 Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %g_Texture DescriptorSet 0 + OpDecorate %g_Texture Binding 0 + OpDecorate %g_Sampler DescriptorSet 0 + OpDecorate %g_Sampler Binding 0 + OpDecorate %g_CompareSampler DescriptorSet 0 + OpDecorate %g_CompareSampler Binding 1 + %float = OpTypeFloat 32 + %float_0_5 = OpConstant %float 0.5 + %float_0 = OpConstant %float 0 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%_ptr_Output_float = OpTypePointer Output %float + %void = OpTypeVoid + %19 = OpTypeFunction %void +%type_sampled_image = OpTypeSampledImage %type_2d_image + %v4float = OpTypeVector %float 4 + %g_Texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_Sampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%g_CompareSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD0 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %19 + %21 = OpLabel + %22 = OpLoad %v2float %in_var_TEXCOORD0 + %23 = OpLoad %type_2d_image %g_Texture + %24 = OpLoad %type_sampler %g_Sampler + %25 = OpSampledImage %type_sampled_image %23 %24 + %26 = OpImageSampleImplicitLod %v4float %25 %22 None + %27 = OpCompositeExtract %float %26 0 + %28 = OpLoad %type_sampler %g_CompareSampler + %29 = OpSampledImage %type_sampled_image %23 %28 + %30 = OpImageSampleDrefExplicitLod %float %29 %22 %float_0_5 Lod %float_0 + %31 = OpFAdd %float %27 %30 + OpStore %out_var_SV_Target %31 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag b/third_party/spirv-cross/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag new file mode 100644 index 0000000..0c3833e --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/sampler-buffer-array-without-sampler.asm.frag @@ -0,0 +1,86 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 63 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_from_func_s21_4__ "sample_from_func(s21[4];" + OpName %uSampler "uSampler" + OpName %sample_one_from_func_s21_ "sample_one_from_func(s21;" + OpName %uSampler_0 "uSampler" + OpName %Registers "Registers" + OpMemberName %Registers 0 "index" + OpName %registers "registers" + OpName %FragColor "FragColor" + OpName %uSampler_1 "uSampler" + OpMemberDecorate %Registers 0 Offset 0 + OpDecorate %Registers Block + OpDecorate %FragColor Location 0 + OpDecorate %uSampler_1 DescriptorSet 0 + OpDecorate %uSampler_1 Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float 2D 0 0 0 1 Unknown + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_8_uint_4 = OpTypeArray %7 %uint_4 +%_ptr_UniformConstant__arr_8_uint_4 = OpTypePointer UniformConstant %_arr_8_uint_4 + %v4float = OpTypeVector %float 4 + %14 = OpTypeFunction %v4float %_ptr_UniformConstant__arr_8_uint_4 +%_ptr_UniformConstant_8 = OpTypePointer UniformConstant %7 + %19 = OpTypeFunction %v4float %_ptr_UniformConstant_8 + %int = OpTypeInt 32 1 + %Registers = OpTypeStruct %int +%_ptr_PushConstant_Registers = OpTypePointer PushConstant %Registers + %registers = OpVariable %_ptr_PushConstant_Registers PushConstant + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_int = OpTypePointer PushConstant %int + %v2int = OpTypeVector %int 2 + %int_4 = OpConstant %int 4 + %35 = OpConstantComposite %v2int %int_4 %int_4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %uSampler_1 = OpVariable %_ptr_UniformConstant__arr_8_uint_4 UniformConstant + %int_10 = OpConstant %int 10 + %53 = OpConstantComposite %v2int %int_10 %int_10 + %main = OpFunction %void None %3 + %5 = OpLabel + %48 = OpAccessChain %_ptr_PushConstant_int %registers %int_0 + %49 = OpLoad %int %48 + %50 = OpAccessChain %_ptr_UniformConstant_8 %uSampler_1 %49 + %51 = OpLoad %7 %50 + %55 = OpImageFetch %v4float %51 %53 Lod %int_0 + %56 = OpFunctionCall %v4float %sample_from_func_s21_4__ %uSampler_1 + %57 = OpFAdd %v4float %55 %56 + %58 = OpAccessChain %_ptr_PushConstant_int %registers %int_0 + %59 = OpLoad %int %58 + %60 = OpAccessChain %_ptr_UniformConstant_8 %uSampler_1 %59 + %61 = OpFunctionCall %v4float %sample_one_from_func_s21_ %60 + %62 = OpFAdd %v4float %57 %61 + OpStore %FragColor %62 + OpReturn + OpFunctionEnd +%sample_from_func_s21_4__ = OpFunction %v4float None %14 + %uSampler = OpFunctionParameter %_ptr_UniformConstant__arr_8_uint_4 + %17 = OpLabel + %29 = OpAccessChain %_ptr_PushConstant_int %registers %int_0 + %30 = OpLoad %int %29 + %31 = OpAccessChain %_ptr_UniformConstant_8 %uSampler %30 + %32 = OpLoad %7 %31 + %37 = OpImageFetch %v4float %32 %35 Lod %int_0 + OpReturnValue %37 + OpFunctionEnd +%sample_one_from_func_s21_ = OpFunction %v4float None %19 + %uSampler_0 = OpFunctionParameter %_ptr_UniformConstant_8 + %22 = OpLabel + %40 = OpLoad %7 %uSampler_0 + %42 = OpImageFetch %v4float %40 %35 Lod %int_0 + OpReturnValue %42 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag b/third_party/spirv-cross/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag new file mode 100644 index 0000000..e6776ea --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/sampler-buffer-without-sampler.asm.frag @@ -0,0 +1,62 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 36 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %_main_ "@main(" + OpName %storeTemp "storeTemp" + OpName %RWTex "RWTex" + OpName %Tex "Tex" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %RWTex DescriptorSet 0 + OpDecorate %Tex DescriptorSet 0 + OpDecorate %RWTex Binding 0 + OpDecorate %Tex Binding 1 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float + %13 = OpConstant %float 1 + %14 = OpConstant %float 2 + %15 = OpConstant %float 3 + %16 = OpConstant %float 4 + %17 = OpConstantComposite %v4float %13 %14 %15 %16 + %18 = OpTypeImage %float Buffer 0 0 0 2 Rgba32f +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %RWTex = OpVariable %_ptr_UniformConstant_18 UniformConstant + %int = OpTypeInt 32 1 + %23 = OpConstant %int 20 + %25 = OpTypeImage %float Buffer 0 0 0 1 Rgba32f +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 + %Tex = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpConstant %int 10 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %35 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + %storeTemp = OpVariable %_ptr_Function_v4float Function + OpStore %storeTemp %17 + %21 = OpLoad %18 %RWTex + %24 = OpLoad %v4float %storeTemp + OpImageWrite %21 %23 %24 + %28 = OpLoad %25 %Tex + %30 = OpImageFetch %v4float %28 %29 + OpReturnValue %30 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/single-function-private-lut.asm.frag b/third_party/spirv-cross/shaders/asm/frag/single-function-private-lut.asm.frag new file mode 100644 index 0000000..0d5b29c --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/single-function-private-lut.asm.frag @@ -0,0 +1,86 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 54 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %o_color + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 460 + OpName %main "main" + OpName %myType "myType" + OpMemberName %myType 0 "data" + OpName %myData "myData" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %index "index" + OpName %elt "elt" + OpName %o_color "o_color" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %o_color Location 0 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %float = OpTypeFloat 32 + %myType = OpTypeStruct %float + %uint = OpTypeInt 32 0 + %uint_5 = OpConstant %uint 5 +%_arr_myType_uint_5 = OpTypeArray %myType %uint_5 +%_ptr_Private__arr_myType_uint_5 = OpTypePointer Private %_arr_myType_uint_5 + %myData = OpVariable %_ptr_Private__arr_myType_uint_5 Private + %float_0 = OpConstant %float 0 + %18 = OpConstantComposite %myType %float_0 + %float_1 = OpConstant %float 1 + %20 = OpConstantComposite %myType %float_1 + %21 = OpConstantComposite %_arr_myType_uint_5 %18 %20 %18 %20 %18 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %float_4 = OpConstant %float 4 +%_ptr_Function_myType = OpTypePointer Function %myType +%_ptr_Private_myType = OpTypePointer Private %myType + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_Output_v4float = OpTypePointer Output %v4float + %o_color = OpVariable %_ptr_Output_v4float Output + %36 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1 + %37 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %main = OpFunction %void None %11 + %38 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %index = OpVariable %_ptr_Function_int Function + %elt = OpVariable %_ptr_Function_myType Function + OpStore %myData %21 + %39 = OpLoad %v4float %gl_FragCoord + %40 = OpVectorShuffle %v2float %39 %39 0 1 + OpStore %uv %40 + %41 = OpAccessChain %_ptr_Function_float %uv %uint_0 + %42 = OpLoad %float %41 + %43 = OpFMod %float %42 %float_4 + %44 = OpConvertFToS %int %43 + OpStore %index %44 + %45 = OpLoad %int %index + %46 = OpAccessChain %_ptr_Private_myType %myData %45 + %47 = OpLoad %myType %46 + OpStore %elt %47 + %48 = OpAccessChain %_ptr_Function_float %elt %int_0 + %49 = OpLoad %float %48 + %50 = OpFOrdGreaterThan %bool %49 %float_0 + OpSelectionMerge %51 None + OpBranchConditional %50 %52 %53 + %52 = OpLabel + OpStore %o_color %36 + OpBranch %51 + %53 = OpLabel + OpStore %o_color %37 + OpBranch %51 + %51 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/srem.asm.frag b/third_party/spirv-cross/shaders/asm/frag/srem.asm.frag new file mode 100644 index 0000000..c6f8e27 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/srem.asm.frag @@ -0,0 +1,43 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 23 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %vA %vB + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %vA "vA" + OpName %vB "vB" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %vA Flat + OpDecorate %vA Location 0 + OpDecorate %vB Flat + OpDecorate %vB Location 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 +%_ptr_Input_v4int = OpTypePointer Input %v4int + %vA = OpVariable %_ptr_Input_v4int Input + %vB = OpVariable %_ptr_Input_v4int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpLoad %v4int %vA + %16 = OpLoad %v4int %vB + %17 = OpLoad %v4int %vA + %18 = OpLoad %v4int %vB + %19 = OpSRem %v4int %17 %18 + %20 = OpConvertSToF %v4float %19 + OpStore %FragColor %20 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/storage-class-output-initializer.asm.frag b/third_party/spirv-cross/shaders/asm/frag/storage-class-output-initializer.asm.frag new file mode 100644 index 0000000..7763b7c --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/storage-class-output-initializer.asm.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 25 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColors %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColors "FragColors" + OpName %FragColor "FragColor" + OpDecorate %FragColors Location 0 + OpDecorate %FragColor Location 2 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_ptr_Output__arr_v4float_uint_2 = OpTypePointer Output %_arr_v4float_uint_2 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %17 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 + %float_10 = OpConstant %float 10 + %19 = OpConstantComposite %v4float %float_10 %float_10 %float_10 %float_10 + %20 = OpConstantComposite %_arr_v4float_uint_2 %17 %19 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %float_5 = OpConstant %float 5 + %24 = OpConstantComposite %v4float %float_5 %float_5 %float_5 %float_5 + %FragColors = OpVariable %_ptr_Output__arr_v4float_uint_2 Output %20 + %FragColor = OpVariable %_ptr_Output_v4float Output %24 + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag b/third_party/spirv-cross/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag new file mode 100644 index 0000000..33bd1c9 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/struct-composite-extract-swizzle.asm.frag @@ -0,0 +1,55 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 34 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uSampler "uSampler" + OpName %Foo "Foo" + OpMemberName %Foo 0 "var1" + OpMemberName %Foo 1 "var2" + OpName %foo "foo" + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + OpDecorate %uSampler RelaxedPrecision + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 0 + OpDecorate %14 RelaxedPrecision + OpMemberDecorate %Foo 0 RelaxedPrecision + OpMemberDecorate %Foo 1 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %uSampler = OpVariable %_ptr_UniformConstant_11 UniformConstant + %Foo = OpTypeStruct %float %float +%_ptr_Function_Foo = OpTypePointer Function %Foo + %int = OpTypeInt 32 1 +%_ptr_Function_float = OpTypePointer Function %float + %v2float = OpTypeVector %float 2 + %33 = OpUndef %Foo + %main = OpFunction %void None %3 + %5 = OpLabel + %foo = OpVariable %_ptr_Function_Foo Function + %14 = OpLoad %11 %uSampler + %30 = OpCompositeExtract %float %33 0 + %32 = OpCompositeExtract %float %33 1 + %27 = OpCompositeConstruct %v2float %30 %32 + %28 = OpImageSampleImplicitLod %v4float %14 %27 + OpStore %FragColor %28 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/switch-label-shared-block.asm.frag b/third_party/spirv-cross/shaders/asm/frag/switch-label-shared-block.asm.frag new file mode 100644 index 0000000..8f55bcf --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/switch-label-shared-block.asm.frag @@ -0,0 +1,45 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 28 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vIndex %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %vIndex "vIndex" + OpName %FragColor "FragColor" + OpDecorate %vIndex RelaxedPrecision + OpDecorate %vIndex Flat + OpDecorate %vIndex Location 0 + OpDecorate %13 RelaxedPrecision + OpDecorate %FragColor RelaxedPrecision + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_8 = OpConstant %float 8 + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %vIndex = OpVariable %_ptr_Input_int Input + %float_1 = OpConstant %float 1 + %float_3 = OpConstant %float 3 +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %13 = OpLoad %int %vIndex + OpSelectionMerge %17 None + OpSwitch %13 %15 0 %14 2 %14 1 %15 8 %17 + %15 = OpLabel + OpBranch %17 + %14 = OpLabel + OpBranch %17 + %17 = OpLabel + %27 = OpPhi %float %float_3 %15 %float_1 %14 %float_8 %5 + OpStore %FragColor %27 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/temporary-name-alias.asm.frag b/third_party/spirv-cross/shaders/asm/frag/temporary-name-alias.asm.frag new file mode 100644 index 0000000..6ea359b --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/temporary-name-alias.asm.frag @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.2 +; Generator: Khronos; 0 +; Bound: 51 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %EntryPoint_Main "main" + OpExecutionMode %EntryPoint_Main OriginUpperLeft + OpSource Unknown 100 + OpName %mat3 "mat3" + OpName %constituent "constituent" + OpName %constituent_0 "constituent" + OpName %constituent_1 "constituent" + OpName %constituent_2 "constituent" + OpName %constituent_3 "constituent" + OpName %constituent_4 "constituent" + OpName %constituent_5 "constituent" + OpName %constituent_6 "constituent" + OpName %EntryPoint_Main "EntryPoint_Main" + %void = OpTypeVoid +%_ptr_Function_void = OpTypePointer Function %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %v3float = OpTypeVector %float 3 +%mat3v3float = OpTypeMatrix %v3float 3 +%_ptr_Function_mat3v3float = OpTypePointer Function %mat3v3float + %14 = OpTypeFunction %void + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%EntryPoint_Main = OpFunction %void None %14 + %45 = OpLabel + %mat3 = OpVariable %_ptr_Function_mat3v3float Function +%constituent = OpConvertSToF %float %int_0 +%constituent_0 = OpCompositeConstruct %v3float %constituent %constituent %constituent +%constituent_1 = OpCompositeConstruct %v3float %constituent %constituent %constituent +%constituent_2 = OpCompositeConstruct %v3float %constituent %constituent %constituent + %25 = OpCompositeConstruct %mat3v3float %constituent_0 %constituent_1 %constituent_2 + OpStore %mat3 %25 +%constituent_3 = OpConvertSToF %float %int_1 +%constituent_4 = OpCompositeConstruct %v3float %constituent_3 %constituent_3 %constituent_3 +%constituent_5 = OpCompositeConstruct %v3float %constituent_3 %constituent_3 %constituent_3 +%constituent_6 = OpCompositeConstruct %v3float %constituent_3 %constituent_3 %constituent_3 + %30 = OpCompositeConstruct %mat3v3float %constituent_4 %constituent_5 %constituent_6 + OpStore %mat3 %30 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/temporary-phi-hoisting.asm.frag b/third_party/spirv-cross/shaders/asm/frag/temporary-phi-hoisting.asm.frag new file mode 100644 index 0000000..977c20a --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/temporary-phi-hoisting.asm.frag @@ -0,0 +1,76 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 87 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %MyStruct "MyStruct" + OpMemberName %MyStruct 0 "color" + OpName %MyStruct_CB "MyStruct_CB" + OpMemberName %MyStruct_CB 0 "g_MyStruct" + OpName %_ "" + OpName %_entryPointOutput "@entryPointOutput" + OpMemberDecorate %MyStruct 0 Offset 0 + OpDecorate %_arr_MyStruct_uint_4 ArrayStride 16 + OpMemberDecorate %MyStruct_CB 0 Offset 0 + OpDecorate %MyStruct_CB Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 + %float_0 = OpConstant %float 0 + %15 = OpConstantComposite %v3float %float_0 %float_0 %float_0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 + %bool = OpTypeBool + %MyStruct = OpTypeStruct %v4float + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_MyStruct_uint_4 = OpTypeArray %MyStruct %uint_4 +%MyStruct_CB = OpTypeStruct %_arr_MyStruct_uint_4 +%_ptr_Uniform_MyStruct_CB = OpTypePointer Uniform %MyStruct_CB + %_ = OpVariable %_ptr_Uniform_MyStruct_CB Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_1 = OpConstant %int 1 + %float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %64 + %64 = OpLabel + %85 = OpPhi %v3float %15 %5 %77 %66 + %86 = OpPhi %int %int_0 %5 %79 %66 + OpLoopMerge %65 %66 None + OpBranch %67 + %67 = OpLabel + %69 = OpSLessThan %bool %86 %int_4 + OpBranchConditional %69 %70 %65 + %70 = OpLabel + %72 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %86 %int_0 + %73 = OpLoad %v4float %72 + %74 = OpVectorShuffle %v3float %73 %73 0 1 2 + %77 = OpFAdd %v3float %85 %74 + OpBranch %66 + %66 = OpLabel + %79 = OpIAdd %int %86 %int_1 + OpBranch %64 + %65 = OpLabel + %81 = OpCompositeExtract %float %85 0 + %82 = OpCompositeExtract %float %85 1 + %83 = OpCompositeExtract %float %85 2 + %84 = OpCompositeConstruct %v4float %81 %82 %83 %float_1 + OpStore %_entryPointOutput %84 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/texel-fetch-no-lod.asm.frag b/third_party/spirv-cross/shaders/asm/frag/texel-fetch-no-lod.asm.frag new file mode 100644 index 0000000..53dc638 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/texel-fetch-no-lod.asm.frag @@ -0,0 +1,46 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 26 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %gl_FragCoord + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTexture "uTexture" + OpName %gl_FragCoord "gl_FragCoord" + OpDecorate %FragColor Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %uTexture = OpVariable %_ptr_UniformConstant_11 UniformConstant +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %v2float = OpTypeVector %float 2 + %int = OpTypeInt 32 1 + %v2int = OpTypeVector %int 2 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %14 = OpLoad %11 %uTexture + %18 = OpLoad %v4float %gl_FragCoord + %19 = OpVectorShuffle %v2float %18 %18 0 1 + %22 = OpConvertFToS %v2int %19 + %24 = OpImage %10 %14 + %25 = OpImageFetch %v4float %24 %22 + OpStore %FragColor %25 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag b/third_party/spirv-cross/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag new file mode 100644 index 0000000..e7e6f37 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/texture-sampling-fp16.asm.vk.frag @@ -0,0 +1,47 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 25 +; Schema: 0 + OpCapability Shader + OpCapability StorageInputOutput16 + OpCapability Float16 + OpExtension "SPV_KHR_16bit_storage" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor %UV + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types_float16" + OpName %main "main" + OpName %FragColor "FragColor" + OpName %uTexture "uTexture" + OpName %UV "UV" + OpDecorate %FragColor Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 0 + OpDecorate %UV Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %half = OpTypeFloat 16 + %float = OpTypeFloat 32 + %v4half = OpTypeVector %half 4 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4half = OpTypePointer Output %v4half + %FragColor = OpVariable %_ptr_Output_v4half Output + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown + %12 = OpTypeSampledImage %11 +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_12 UniformConstant + %v2half = OpTypeVector %half 2 +%_ptr_Input_v2half = OpTypePointer Input %v2half + %UV = OpVariable %_ptr_Input_v2half Input + %main = OpFunction %void None %3 + %5 = OpLabel + %15 = OpLoad %12 %uTexture + %19 = OpLoad %v2half %UV + %23 = OpImageSampleImplicitLod %v4float %15 %19 + %24 = OpFConvert %v4half %23 + OpStore %FragColor %24 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/undef-variable-store.asm.frag b/third_party/spirv-cross/shaders/asm/frag/undef-variable-store.asm.frag new file mode 100644 index 0000000..966c2d9 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/undef-variable-store.asm.frag @@ -0,0 +1,85 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 50 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %fragmentProgram "main" %_entryPointOutput + OpExecutionMode %fragmentProgram OriginUpperLeft + OpSource HLSL 500 + OpName %fragmentProgram "fragmentProgram" + OpName %_fragmentProgram_ "@fragmentProgram(" + OpName %uv "uv" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %float_0 = OpConstant %float 0 + %15 = OpConstantComposite %v2float %float_0 %float_0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float + %bool = OpTypeBool + %float_1 = OpConstant %float 1 + %26 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %29 = OpConstantComposite %v4float %float_1 %float_1 %float_0 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %false = OpConstantFalse %bool +%fragmentProgram = OpFunction %void None %3 + %5 = OpLabel + %35 = OpVariable %_ptr_Function_v2float Function + %37 = OpVariable %_ptr_Function_v4float Function + OpBranch %38 + %38 = OpLabel + OpLoopMerge %39 %40 None + OpBranch %41 + %41 = OpLabel + OpStore %35 %15 + %42 = OpAccessChain %_ptr_Function_float %35 %uint_0 + %43 = OpLoad %float %42 + %44 = OpFOrdNotEqual %bool %43 %float_0 + OpSelectionMerge %45 None + OpBranchConditional %44 %46 %47 + %46 = OpLabel + OpStore %37 %26 + OpBranch %39 + %47 = OpLabel + OpStore %37 %29 + OpBranch %39 + %45 = OpLabel + %48 = OpUndef %v4float + OpStore %37 %48 + OpBranch %39 + %40 = OpLabel + OpBranchConditional %false %38 %39 + %39 = OpLabel + %34 = OpLoad %v4float %37 + OpStore %_entryPointOutput %34 + OpReturn + OpFunctionEnd +%_fragmentProgram_ = OpFunction %v4float None %8 + %10 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + OpStore %uv %15 + %19 = OpAccessChain %_ptr_Function_float %uv %uint_0 + %20 = OpLoad %float %19 + %22 = OpFOrdNotEqual %bool %20 %float_0 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %28 + %23 = OpLabel + OpReturnValue %26 + %28 = OpLabel + OpReturnValue %29 + %24 = OpLabel + %31 = OpUndef %v4float + OpReturnValue %31 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/unknown-depth-state.asm.vk.frag b/third_party/spirv-cross/shaders/asm/frag/unknown-depth-state.asm.vk.frag new file mode 100644 index 0000000..89036f0 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/unknown-depth-state.asm.vk.frag @@ -0,0 +1,71 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 44 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %vUV %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %sample_combined_ "sample_combined(" + OpName %sample_separate_ "sample_separate(" + OpName %uShadow "uShadow" + OpName %vUV "vUV" + OpName %uTexture "uTexture" + OpName %uSampler "uSampler" + OpName %FragColor "FragColor" + OpDecorate %uShadow DescriptorSet 0 + OpDecorate %uShadow Binding 0 + OpDecorate %vUV Location 0 + OpDecorate %uTexture DescriptorSet 0 + OpDecorate %uTexture Binding 1 + OpDecorate %uSampler DescriptorSet 0 + OpDecorate %uSampler Binding 2 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float + %12 = OpTypeImage %float 2D 2 0 0 1 Unknown + %13 = OpTypeSampledImage %12 +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %uShadow = OpVariable %_ptr_UniformConstant_13 UniformConstant + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %vUV = OpVariable %_ptr_Input_v3float Input +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %12 + %uTexture = OpVariable %_ptr_UniformConstant_25 UniformConstant + %29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 + %uSampler = OpVariable %_ptr_UniformConstant_29 UniformConstant +%_ptr_Output_float = OpTypePointer Output %float + %FragColor = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %41 = OpFunctionCall %float %sample_combined_ + %42 = OpFunctionCall %float %sample_separate_ + %43 = OpFAdd %float %41 %42 + OpStore %FragColor %43 + OpReturn + OpFunctionEnd +%sample_combined_ = OpFunction %float None %7 + %9 = OpLabel + %16 = OpLoad %13 %uShadow + %20 = OpLoad %v3float %vUV + %21 = OpCompositeExtract %float %20 2 + %22 = OpImageSampleDrefImplicitLod %float %16 %20 %21 + OpReturnValue %22 + OpFunctionEnd +%sample_separate_ = OpFunction %float None %7 + %11 = OpLabel + %28 = OpLoad %12 %uTexture + %32 = OpLoad %29 %uSampler + %33 = OpSampledImage %13 %28 %32 + %34 = OpLoad %v3float %vUV + %35 = OpCompositeExtract %float %34 2 + %36 = OpImageSampleDrefImplicitLod %float %33 %34 %35 + OpReturnValue %36 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/unreachable.asm.frag b/third_party/spirv-cross/shaders/asm/frag/unreachable.asm.frag new file mode 100644 index 0000000..e2ce2eb --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/unreachable.asm.frag @@ -0,0 +1,61 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 47 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %counter %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %counter "counter" + OpName %FragColor "FragColor" + OpDecorate %counter Flat + OpDecorate %counter Location 0 + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %counter = OpVariable %_ptr_Input_int Input + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %float_10 = OpConstant %float 10 + %21 = OpConstantComposite %v4float %float_10 %float_10 %float_10 %float_10 + %float_30 = OpConstant %float 30 + %25 = OpConstantComposite %v4float %float_30 %float_30 %float_30 %float_30 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %false = OpConstantFalse %bool + %44 = OpUndef %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %33 + %33 = OpLabel + %45 = OpPhi %v4float %44 %5 %44 %35 + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel + %37 = OpLoad %int %counter + %38 = OpIEqual %bool %37 %int_10 + OpSelectionMerge %39 None + OpBranchConditional %38 %40 %41 + %40 = OpLabel + OpBranch %34 + %41 = OpLabel + OpBranch %34 + %39 = OpLabel + OpUnreachable + %35 = OpLabel + OpBranchConditional %false %33 %34 + %34 = OpLabel + %46 = OpPhi %v4float %21 %40 %25 %41 %44 %35 + OpStore %FragColor %46 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/frag/vector-shuffle-oom.asm.frag b/third_party/spirv-cross/shaders/asm/frag/vector-shuffle-oom.asm.frag new file mode 100644 index 0000000..d60c6f5 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/frag/vector-shuffle-oom.asm.frag @@ -0,0 +1,886 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 25007 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %5663 "main" %5800 %gl_FragCoord %4317 + OpExecutionMode %5663 OriginUpperLeft + OpMemberDecorate %_struct_1116 0 Offset 0 + OpMemberDecorate %_struct_1116 1 Offset 16 + OpMemberDecorate %_struct_1116 2 Offset 32 + OpDecorate %_struct_1116 Block + OpDecorate %22044 DescriptorSet 0 + OpDecorate %22044 Binding 0 + OpDecorate %5785 DescriptorSet 0 + OpDecorate %5785 Binding 140 + OpDecorate %5688 DescriptorSet 0 + OpDecorate %5688 Binding 60 + OpMemberDecorate %_struct_994 0 Offset 0 + OpMemberDecorate %_struct_994 1 Offset 16 + OpMemberDecorate %_struct_994 2 Offset 28 + OpMemberDecorate %_struct_994 3 Offset 32 + OpMemberDecorate %_struct_994 4 Offset 44 + OpMemberDecorate %_struct_994 5 Offset 48 + OpMemberDecorate %_struct_994 6 Offset 60 + OpMemberDecorate %_struct_994 7 Offset 64 + OpMemberDecorate %_struct_994 8 Offset 76 + OpMemberDecorate %_struct_994 9 Offset 80 + OpMemberDecorate %_struct_994 10 Offset 92 + OpMemberDecorate %_struct_994 11 Offset 96 + OpMemberDecorate %_struct_994 12 Offset 108 + OpMemberDecorate %_struct_994 13 Offset 112 + OpMemberDecorate %_struct_994 14 Offset 120 + OpMemberDecorate %_struct_994 15 Offset 128 + OpMemberDecorate %_struct_994 16 Offset 140 + OpMemberDecorate %_struct_994 17 Offset 144 + OpMemberDecorate %_struct_994 18 Offset 148 + OpMemberDecorate %_struct_994 19 Offset 152 + OpMemberDecorate %_struct_994 20 Offset 156 + OpMemberDecorate %_struct_994 21 Offset 160 + OpMemberDecorate %_struct_994 22 Offset 176 + OpMemberDecorate %_struct_994 23 RowMajor + OpMemberDecorate %_struct_994 23 Offset 192 + OpMemberDecorate %_struct_994 23 MatrixStride 16 + OpMemberDecorate %_struct_994 24 Offset 256 + OpDecorate %_struct_994 Block + OpDecorate %12348 DescriptorSet 0 + OpDecorate %12348 Binding 2 + OpDecorate %3312 DescriptorSet 0 + OpDecorate %3312 Binding 142 + OpDecorate %4646 DescriptorSet 0 + OpDecorate %4646 Binding 62 + OpDecorate %4862 DescriptorSet 0 + OpDecorate %4862 Binding 141 + OpDecorate %3594 DescriptorSet 0 + OpDecorate %3594 Binding 61 + OpDecorate %_arr_mat4v4float_uint_2 ArrayStride 64 + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpMemberDecorate %_struct_408 0 RowMajor + OpMemberDecorate %_struct_408 0 Offset 0 + OpMemberDecorate %_struct_408 0 MatrixStride 16 + OpMemberDecorate %_struct_408 1 RowMajor + OpMemberDecorate %_struct_408 1 Offset 64 + OpMemberDecorate %_struct_408 1 MatrixStride 16 + OpMemberDecorate %_struct_408 2 RowMajor + OpMemberDecorate %_struct_408 2 Offset 128 + OpMemberDecorate %_struct_408 2 MatrixStride 16 + OpMemberDecorate %_struct_408 3 RowMajor + OpMemberDecorate %_struct_408 3 Offset 192 + OpMemberDecorate %_struct_408 3 MatrixStride 16 + OpMemberDecorate %_struct_408 4 Offset 256 + OpMemberDecorate %_struct_408 5 Offset 272 + OpMemberDecorate %_struct_408 6 Offset 288 + OpMemberDecorate %_struct_408 7 Offset 292 + OpMemberDecorate %_struct_408 8 Offset 296 + OpMemberDecorate %_struct_408 9 Offset 300 + OpMemberDecorate %_struct_408 10 Offset 304 + OpMemberDecorate %_struct_408 11 Offset 316 + OpMemberDecorate %_struct_408 12 Offset 320 + OpMemberDecorate %_struct_408 13 Offset 332 + OpMemberDecorate %_struct_408 14 Offset 336 + OpMemberDecorate %_struct_408 15 Offset 348 + OpMemberDecorate %_struct_408 16 Offset 352 + OpMemberDecorate %_struct_408 17 Offset 364 + OpMemberDecorate %_struct_408 18 Offset 368 + OpMemberDecorate %_struct_408 19 Offset 372 + OpMemberDecorate %_struct_408 20 Offset 376 + OpMemberDecorate %_struct_408 21 Offset 384 + OpMemberDecorate %_struct_408 22 Offset 392 + OpMemberDecorate %_struct_408 23 Offset 400 + OpMemberDecorate %_struct_408 24 Offset 416 + OpMemberDecorate %_struct_408 25 Offset 424 + OpMemberDecorate %_struct_408 26 Offset 432 + OpMemberDecorate %_struct_408 27 Offset 448 + OpMemberDecorate %_struct_408 28 Offset 460 + OpMemberDecorate %_struct_408 29 Offset 464 + OpMemberDecorate %_struct_408 30 Offset 468 + OpMemberDecorate %_struct_408 31 Offset 472 + OpMemberDecorate %_struct_408 32 Offset 476 + OpMemberDecorate %_struct_408 33 Offset 480 + OpMemberDecorate %_struct_408 34 Offset 488 + OpMemberDecorate %_struct_408 35 Offset 492 + OpMemberDecorate %_struct_408 36 Offset 496 + OpMemberDecorate %_struct_408 37 RowMajor + OpMemberDecorate %_struct_408 37 Offset 512 + OpMemberDecorate %_struct_408 37 MatrixStride 16 + OpMemberDecorate %_struct_408 38 Offset 640 + OpDecorate %_struct_408 Block + OpDecorate %15259 DescriptorSet 0 + OpDecorate %15259 Binding 1 + OpDecorate %5800 Location 0 + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %4317 Location 0 + OpMemberDecorate %_struct_1395 0 Offset 0 + OpMemberDecorate %_struct_1395 1 Offset 16 + OpMemberDecorate %_struct_1395 2 Offset 32 + OpMemberDecorate %_struct_1395 3 Offset 40 + OpMemberDecorate %_struct_1395 4 Offset 48 + OpMemberDecorate %_struct_1395 5 Offset 60 + OpMemberDecorate %_struct_1395 6 Offset 64 + OpMemberDecorate %_struct_1395 7 Offset 76 + OpMemberDecorate %_struct_1395 8 Offset 80 + OpMemberDecorate %_struct_1395 9 Offset 96 + OpMemberDecorate %_struct_1395 10 Offset 112 + OpMemberDecorate %_struct_1395 11 Offset 128 + OpMemberDecorate %_struct_1395 12 Offset 140 + OpMemberDecorate %_struct_1395 13 Offset 144 + OpMemberDecorate %_struct_1395 14 Offset 156 + OpMemberDecorate %_struct_1395 15 Offset 160 + OpMemberDecorate %_struct_1395 16 Offset 176 + OpMemberDecorate %_struct_1395 17 Offset 192 + OpMemberDecorate %_struct_1395 18 Offset 204 + OpMemberDecorate %_struct_1395 19 Offset 208 + OpMemberDecorate %_struct_1395 20 Offset 224 + OpDecorate %_struct_1395 Block + OpMemberDecorate %_struct_1018 0 Offset 0 + OpDecorate %_struct_1018 Block + %void = OpTypeVoid + %1282 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 +%_struct_1017 = OpTypeStruct %v4float +%_struct_1116 = OpTypeStruct %v4float %float %v4float +%_ptr_Uniform__struct_1116 = OpTypePointer Uniform %_struct_1116 + %22044 = OpVariable %_ptr_Uniform__struct_1116 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %150 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_150 = OpTypePointer UniformConstant %150 + %5785 = OpVariable %_ptr_UniformConstant_150 UniformConstant + %508 = OpTypeSampler +%_ptr_UniformConstant_508 = OpTypePointer UniformConstant %508 + %5688 = OpVariable %_ptr_UniformConstant_508 UniformConstant + %510 = OpTypeSampledImage %150 + %float_0 = OpConstant %float 0 + %uint = OpTypeInt 32 0 + %int_1 = OpConstant %int 1 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_1 = OpConstant %float 1 +%mat4v4float = OpTypeMatrix %v4float 4 +%_struct_994 = OpTypeStruct %v3float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v3float %float %v2float %v2float %v3float %float %float %float %float %float %v4float %v4float %mat4v4float %v4float +%_ptr_Uniform__struct_994 = OpTypePointer Uniform %_struct_994 + %12348 = OpVariable %_ptr_Uniform__struct_994 Uniform + %int_5 = OpConstant %int 5 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float + %3312 = OpVariable %_ptr_UniformConstant_150 UniformConstant + %4646 = OpVariable %_ptr_UniformConstant_508 UniformConstant + %bool = OpTypeBool + %4862 = OpVariable %_ptr_UniformConstant_150 UniformConstant + %3594 = OpVariable %_ptr_UniformConstant_508 UniformConstant + %uint_2 = OpConstant %uint 2 + %2938 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_arr_mat4v4float_uint_2 = OpTypeArray %mat4v4float %uint_2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_struct_408 = OpTypeStruct %mat4v4float %mat4v4float %mat4v4float %mat4v4float %v4float %v4float %float %float %float %float %v3float %float %v3float %float %v3float %float %v3float %float %float %float %v2float %v2float %v2float %v4float %v2float %v2float %v2float %v3float %float %float %float %float %float %v2float %float %float %v3float %_arr_mat4v4float_uint_2 %_arr_v4float_uint_2 +%_ptr_Uniform__struct_408 = OpTypePointer Uniform %_struct_408 + %15259 = OpVariable %_ptr_Uniform__struct_408 Uniform + %int_23 = OpConstant %int 23 + %int_2 = OpConstant %int 2 + %float_n2 = OpConstant %float -2 + %float_0_5 = OpConstant %float 0.5 + %1196 = OpConstantComposite %v3float %float_0 %float_n2 %float_0_5 + %float_n1 = OpConstant %float -1 + %836 = OpConstantComposite %v3float %float_n1 %float_n1 %float_0_5 + %float_0_75 = OpConstant %float 0.75 + %1367 = OpConstantComposite %v3float %float_0 %float_n1 %float_0_75 + %141 = OpConstantComposite %v3float %float_1 %float_n1 %float_0_5 + %38 = OpConstantComposite %v3float %float_n2 %float_0 %float_0_5 + %95 = OpConstantComposite %v3float %float_n1 %float_0 %float_0_75 + %626 = OpConstantComposite %v3float %float_0 %float_0 %float_1 + %2411 = OpConstantComposite %v3float %float_1 %float_0 %float_0_75 + %float_2 = OpConstant %float 2 + %2354 = OpConstantComposite %v3float %float_2 %float_0 %float_0_5 + %837 = OpConstantComposite %v3float %float_n1 %float_1 %float_0_5 + %1368 = OpConstantComposite %v3float %float_0 %float_1 %float_0_75 + %142 = OpConstantComposite %v3float %float_1 %float_1 %float_0_5 + %1197 = OpConstantComposite %v3float %float_0 %float_2 %float_0_5 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %5800 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float + %4317 = OpVariable %_ptr_Output_v4float Output +%_struct_1395 = OpTypeStruct %v4float %v4float %v2float %v2float %v3float %float %v3float %float %v4float %v4float %v4float %v3float %float %v3float %float %v3float %v4float %v3float %float %v3float %v2float +%_struct_1018 = OpTypeStruct %v4float + %10264 = OpUndef %_struct_1017 + %5663 = OpFunction %void None %1282 + %25006 = OpLabel + %17463 = OpLoad %v4float %gl_FragCoord + %13863 = OpCompositeInsert %_struct_1017 %2938 %10264 0 + %22969 = OpVectorShuffle %v2float %17463 %17463 0 1 + %13206 = OpAccessChain %_ptr_Uniform_v4float %15259 %int_23 + %10343 = OpLoad %v4float %13206 + %7422 = OpVectorShuffle %v2float %10343 %10343 0 1 + %19927 = OpFMul %v2float %22969 %7422 + %18174 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_2 + %16206 = OpLoad %v4float %18174 + %20420 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %21354 = OpLoad %v4float %20420 + %7688 = OpVectorShuffle %v4float %21354 %21354 0 1 0 1 + %17581 = OpFMul %v4float %16206 %7688 + %10673 = OpVectorShuffle %v2float %1196 %1196 0 1 + %18824 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10344 = OpLoad %v4float %18824 + %8638 = OpVectorShuffle %v2float %10344 %10344 0 1 + %9197 = OpFMul %v2float %10673 %8638 + %18505 = OpFAdd %v2float %19927 %9197 + %7011 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21058 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13149 = OpExtInst %v2float %1 FClamp %18505 %7011 %21058 + %23584 = OpLoad %150 %5785 + %10339 = OpLoad %508 %5688 + %12147 = OpSampledImage %510 %23584 %10339 + %15371 = OpImageSampleExplicitLod %v4float %12147 %13149 Lod %float_0 + %15266 = OpCompositeExtract %float %15371 3 + %12116 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12972 = OpLoad %float %12116 + %15710 = OpFMul %float %15266 %12972 + %15279 = OpExtInst %float %1 FClamp %15710 %float_0 %float_1 + %22213 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11756 = OpLoad %v3float %22213 + %12103 = OpVectorTimesScalar %v3float %11756 %15279 + %15516 = OpLoad %150 %3312 + %24569 = OpLoad %508 %4646 + %12148 = OpSampledImage %510 %15516 %24569 + %17670 = OpImageSampleExplicitLod %v4float %12148 %13149 Lod %float_0 + %16938 = OpCompositeExtract %float %17670 1 + %14185 = OpFOrdGreaterThan %bool %16938 %float_0 + OpSelectionMerge %22307 DontFlatten + OpBranchConditional %14185 %12821 %22307 + %12821 = OpLabel + %13239 = OpLoad %150 %4862 + %19960 = OpLoad %508 %3594 + %12149 = OpSampledImage %510 %13239 %19960 + %15675 = OpImageSampleExplicitLod %v4float %12149 %13149 Lod %float_0 + %13866 = OpCompositeExtract %float %17670 1 + %12427 = OpCompositeExtract %float %17670 2 + %23300 = OpFMul %float %13866 %12427 + %17612 = OpExtInst %float %1 FClamp %23300 %float_0 %float_1 + %20291 = OpVectorShuffle %v3float %15675 %15675 0 1 2 + %11186 = OpVectorTimesScalar %v3float %20291 %17612 + %15293 = OpFAdd %v3float %12103 %11186 + OpBranch %22307 + %22307 = OpLabel + %7719 = OpPhi %v3float %12103 %25006 %15293 %12821 + %23399 = OpVectorTimesScalar %v3float %7719 %float_0_5 + %9339 = OpFAdd %float %float_0 %float_0_5 + %16235 = OpVectorShuffle %v3float %2938 %2938 0 1 2 + %22177 = OpFAdd %v3float %16235 %23399 + %15527 = OpVectorShuffle %v4float %2938 %22177 4 5 6 3 + %6434 = OpCompositeInsert %_struct_1017 %15527 %13863 0 + %24572 = OpVectorShuffle %v2float %836 %836 0 1 + %13207 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10345 = OpLoad %v4float %13207 + %8639 = OpVectorShuffle %v2float %10345 %10345 0 1 + %9198 = OpFMul %v2float %24572 %8639 + %18506 = OpFAdd %v2float %19927 %9198 + %7012 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21059 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13150 = OpExtInst %v2float %1 FClamp %18506 %7012 %21059 + %23585 = OpLoad %150 %5785 + %10340 = OpLoad %508 %5688 + %12150 = OpSampledImage %510 %23585 %10340 + %15372 = OpImageSampleExplicitLod %v4float %12150 %13150 Lod %float_0 + %15267 = OpCompositeExtract %float %15372 3 + %12117 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12973 = OpLoad %float %12117 + %15711 = OpFMul %float %15267 %12973 + %15280 = OpExtInst %float %1 FClamp %15711 %float_0 %float_1 + %22214 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11757 = OpLoad %v3float %22214 + %12104 = OpVectorTimesScalar %v3float %11757 %15280 + %15517 = OpLoad %150 %3312 + %24570 = OpLoad %508 %4646 + %12151 = OpSampledImage %510 %15517 %24570 + %17671 = OpImageSampleExplicitLod %v4float %12151 %13150 Lod %float_0 + %16939 = OpCompositeExtract %float %17671 1 + %14186 = OpFOrdGreaterThan %bool %16939 %float_0 + OpSelectionMerge %22308 DontFlatten + OpBranchConditional %14186 %12822 %22308 + %12822 = OpLabel + %13240 = OpLoad %150 %4862 + %19961 = OpLoad %508 %3594 + %12152 = OpSampledImage %510 %13240 %19961 + %15676 = OpImageSampleExplicitLod %v4float %12152 %13150 Lod %float_0 + %13867 = OpCompositeExtract %float %17671 1 + %12428 = OpCompositeExtract %float %17671 2 + %23301 = OpFMul %float %13867 %12428 + %17613 = OpExtInst %float %1 FClamp %23301 %float_0 %float_1 + %20292 = OpVectorShuffle %v3float %15676 %15676 0 1 2 + %11187 = OpVectorTimesScalar %v3float %20292 %17613 + %15294 = OpFAdd %v3float %12104 %11187 + OpBranch %22308 + %22308 = OpLabel + %7720 = OpPhi %v3float %12104 %22307 %15294 %12822 + %23400 = OpVectorTimesScalar %v3float %7720 %float_0_5 + %9340 = OpFAdd %float %9339 %float_0_5 + %16236 = OpVectorShuffle %v3float %15527 %15527 0 1 2 + %22178 = OpFAdd %v3float %16236 %23400 + %15528 = OpVectorShuffle %v4float %15527 %22178 4 5 6 3 + %6435 = OpCompositeInsert %_struct_1017 %15528 %6434 0 + %24573 = OpVectorShuffle %v2float %1367 %1367 0 1 + %13208 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10346 = OpLoad %v4float %13208 + %8640 = OpVectorShuffle %v2float %10346 %10346 0 1 + %9199 = OpFMul %v2float %24573 %8640 + %18507 = OpFAdd %v2float %19927 %9199 + %7013 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21060 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13151 = OpExtInst %v2float %1 FClamp %18507 %7013 %21060 + %23586 = OpLoad %150 %5785 + %10341 = OpLoad %508 %5688 + %12153 = OpSampledImage %510 %23586 %10341 + %15373 = OpImageSampleExplicitLod %v4float %12153 %13151 Lod %float_0 + %15268 = OpCompositeExtract %float %15373 3 + %12118 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12974 = OpLoad %float %12118 + %15712 = OpFMul %float %15268 %12974 + %15281 = OpExtInst %float %1 FClamp %15712 %float_0 %float_1 + %22215 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11758 = OpLoad %v3float %22215 + %12105 = OpVectorTimesScalar %v3float %11758 %15281 + %15518 = OpLoad %150 %3312 + %24571 = OpLoad %508 %4646 + %12154 = OpSampledImage %510 %15518 %24571 + %17672 = OpImageSampleExplicitLod %v4float %12154 %13151 Lod %float_0 + %16940 = OpCompositeExtract %float %17672 1 + %14187 = OpFOrdGreaterThan %bool %16940 %float_0 + OpSelectionMerge %22309 DontFlatten + OpBranchConditional %14187 %12823 %22309 + %12823 = OpLabel + %13241 = OpLoad %150 %4862 + %19962 = OpLoad %508 %3594 + %12155 = OpSampledImage %510 %13241 %19962 + %15677 = OpImageSampleExplicitLod %v4float %12155 %13151 Lod %float_0 + %13868 = OpCompositeExtract %float %17672 1 + %12429 = OpCompositeExtract %float %17672 2 + %23302 = OpFMul %float %13868 %12429 + %17614 = OpExtInst %float %1 FClamp %23302 %float_0 %float_1 + %20293 = OpVectorShuffle %v3float %15677 %15677 0 1 2 + %11188 = OpVectorTimesScalar %v3float %20293 %17614 + %15295 = OpFAdd %v3float %12105 %11188 + OpBranch %22309 + %22309 = OpLabel + %7721 = OpPhi %v3float %12105 %22308 %15295 %12823 + %23401 = OpVectorTimesScalar %v3float %7721 %float_0_75 + %9341 = OpFAdd %float %9340 %float_0_75 + %16237 = OpVectorShuffle %v3float %15528 %15528 0 1 2 + %22179 = OpFAdd %v3float %16237 %23401 + %15529 = OpVectorShuffle %v4float %15528 %22179 4 5 6 3 + %6436 = OpCompositeInsert %_struct_1017 %15529 %6435 0 + %24574 = OpVectorShuffle %v2float %141 %141 0 1 + %13209 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10347 = OpLoad %v4float %13209 + %8641 = OpVectorShuffle %v2float %10347 %10347 0 1 + %9200 = OpFMul %v2float %24574 %8641 + %18508 = OpFAdd %v2float %19927 %9200 + %7014 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21061 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13152 = OpExtInst %v2float %1 FClamp %18508 %7014 %21061 + %23587 = OpLoad %150 %5785 + %10342 = OpLoad %508 %5688 + %12156 = OpSampledImage %510 %23587 %10342 + %15374 = OpImageSampleExplicitLod %v4float %12156 %13152 Lod %float_0 + %15269 = OpCompositeExtract %float %15374 3 + %12119 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12975 = OpLoad %float %12119 + %15713 = OpFMul %float %15269 %12975 + %15282 = OpExtInst %float %1 FClamp %15713 %float_0 %float_1 + %22216 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11759 = OpLoad %v3float %22216 + %12106 = OpVectorTimesScalar %v3float %11759 %15282 + %15519 = OpLoad %150 %3312 + %24575 = OpLoad %508 %4646 + %12157 = OpSampledImage %510 %15519 %24575 + %17673 = OpImageSampleExplicitLod %v4float %12157 %13152 Lod %float_0 + %16941 = OpCompositeExtract %float %17673 1 + %14188 = OpFOrdGreaterThan %bool %16941 %float_0 + OpSelectionMerge %22310 DontFlatten + OpBranchConditional %14188 %12824 %22310 + %12824 = OpLabel + %13242 = OpLoad %150 %4862 + %19963 = OpLoad %508 %3594 + %12158 = OpSampledImage %510 %13242 %19963 + %15678 = OpImageSampleExplicitLod %v4float %12158 %13152 Lod %float_0 + %13869 = OpCompositeExtract %float %17673 1 + %12430 = OpCompositeExtract %float %17673 2 + %23303 = OpFMul %float %13869 %12430 + %17615 = OpExtInst %float %1 FClamp %23303 %float_0 %float_1 + %20294 = OpVectorShuffle %v3float %15678 %15678 0 1 2 + %11189 = OpVectorTimesScalar %v3float %20294 %17615 + %15296 = OpFAdd %v3float %12106 %11189 + OpBranch %22310 + %22310 = OpLabel + %7722 = OpPhi %v3float %12106 %22309 %15296 %12824 + %23402 = OpVectorTimesScalar %v3float %7722 %float_0_5 + %9342 = OpFAdd %float %9341 %float_0_5 + %16238 = OpVectorShuffle %v3float %15529 %15529 0 1 2 + %22180 = OpFAdd %v3float %16238 %23402 + %15530 = OpVectorShuffle %v4float %15529 %22180 4 5 6 3 + %6437 = OpCompositeInsert %_struct_1017 %15530 %6436 0 + %24576 = OpVectorShuffle %v2float %38 %38 0 1 + %13210 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10348 = OpLoad %v4float %13210 + %8642 = OpVectorShuffle %v2float %10348 %10348 0 1 + %9201 = OpFMul %v2float %24576 %8642 + %18509 = OpFAdd %v2float %19927 %9201 + %7015 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21062 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13153 = OpExtInst %v2float %1 FClamp %18509 %7015 %21062 + %23588 = OpLoad %150 %5785 + %10349 = OpLoad %508 %5688 + %12159 = OpSampledImage %510 %23588 %10349 + %15375 = OpImageSampleExplicitLod %v4float %12159 %13153 Lod %float_0 + %15270 = OpCompositeExtract %float %15375 3 + %12120 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12976 = OpLoad %float %12120 + %15714 = OpFMul %float %15270 %12976 + %15283 = OpExtInst %float %1 FClamp %15714 %float_0 %float_1 + %22217 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11760 = OpLoad %v3float %22217 + %12107 = OpVectorTimesScalar %v3float %11760 %15283 + %15520 = OpLoad %150 %3312 + %24577 = OpLoad %508 %4646 + %12160 = OpSampledImage %510 %15520 %24577 + %17674 = OpImageSampleExplicitLod %v4float %12160 %13153 Lod %float_0 + %16942 = OpCompositeExtract %float %17674 1 + %14189 = OpFOrdGreaterThan %bool %16942 %float_0 + OpSelectionMerge %22311 DontFlatten + OpBranchConditional %14189 %12825 %22311 + %12825 = OpLabel + %13243 = OpLoad %150 %4862 + %19964 = OpLoad %508 %3594 + %12161 = OpSampledImage %510 %13243 %19964 + %15679 = OpImageSampleExplicitLod %v4float %12161 %13153 Lod %float_0 + %13870 = OpCompositeExtract %float %17674 1 + %12431 = OpCompositeExtract %float %17674 2 + %23304 = OpFMul %float %13870 %12431 + %17616 = OpExtInst %float %1 FClamp %23304 %float_0 %float_1 + %20295 = OpVectorShuffle %v3float %15679 %15679 0 1 2 + %11190 = OpVectorTimesScalar %v3float %20295 %17616 + %15297 = OpFAdd %v3float %12107 %11190 + OpBranch %22311 + %22311 = OpLabel + %7723 = OpPhi %v3float %12107 %22310 %15297 %12825 + %23403 = OpVectorTimesScalar %v3float %7723 %float_0_5 + %9343 = OpFAdd %float %9342 %float_0_5 + %16239 = OpVectorShuffle %v3float %15530 %15530 0 1 2 + %22181 = OpFAdd %v3float %16239 %23403 + %15531 = OpVectorShuffle %v4float %15530 %22181 4 5 6 3 + %6438 = OpCompositeInsert %_struct_1017 %15531 %6437 0 + %24578 = OpVectorShuffle %v2float %95 %95 0 1 + %13211 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10350 = OpLoad %v4float %13211 + %8643 = OpVectorShuffle %v2float %10350 %10350 0 1 + %9202 = OpFMul %v2float %24578 %8643 + %18510 = OpFAdd %v2float %19927 %9202 + %7016 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21063 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13154 = OpExtInst %v2float %1 FClamp %18510 %7016 %21063 + %23589 = OpLoad %150 %5785 + %10351 = OpLoad %508 %5688 + %12162 = OpSampledImage %510 %23589 %10351 + %15376 = OpImageSampleExplicitLod %v4float %12162 %13154 Lod %float_0 + %15271 = OpCompositeExtract %float %15376 3 + %12121 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12977 = OpLoad %float %12121 + %15715 = OpFMul %float %15271 %12977 + %15284 = OpExtInst %float %1 FClamp %15715 %float_0 %float_1 + %22218 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11761 = OpLoad %v3float %22218 + %12108 = OpVectorTimesScalar %v3float %11761 %15284 + %15521 = OpLoad %150 %3312 + %24579 = OpLoad %508 %4646 + %12163 = OpSampledImage %510 %15521 %24579 + %17675 = OpImageSampleExplicitLod %v4float %12163 %13154 Lod %float_0 + %16943 = OpCompositeExtract %float %17675 1 + %14190 = OpFOrdGreaterThan %bool %16943 %float_0 + OpSelectionMerge %22312 DontFlatten + OpBranchConditional %14190 %12826 %22312 + %12826 = OpLabel + %13244 = OpLoad %150 %4862 + %19965 = OpLoad %508 %3594 + %12164 = OpSampledImage %510 %13244 %19965 + %15680 = OpImageSampleExplicitLod %v4float %12164 %13154 Lod %float_0 + %13871 = OpCompositeExtract %float %17675 1 + %12432 = OpCompositeExtract %float %17675 2 + %23305 = OpFMul %float %13871 %12432 + %17617 = OpExtInst %float %1 FClamp %23305 %float_0 %float_1 + %20296 = OpVectorShuffle %v3float %15680 %15680 0 1 2 + %11191 = OpVectorTimesScalar %v3float %20296 %17617 + %15298 = OpFAdd %v3float %12108 %11191 + OpBranch %22312 + %22312 = OpLabel + %7724 = OpPhi %v3float %12108 %22311 %15298 %12826 + %23404 = OpVectorTimesScalar %v3float %7724 %float_0_75 + %9344 = OpFAdd %float %9343 %float_0_75 + %16240 = OpVectorShuffle %v3float %15531 %15531 0 1 2 + %22182 = OpFAdd %v3float %16240 %23404 + %15532 = OpVectorShuffle %v4float %15531 %22182 4 5 6 3 + %6439 = OpCompositeInsert %_struct_1017 %15532 %6438 0 + %24580 = OpVectorShuffle %v2float %626 %626 0 1 + %13212 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10352 = OpLoad %v4float %13212 + %8644 = OpVectorShuffle %v2float %10352 %10352 0 1 + %9203 = OpFMul %v2float %24580 %8644 + %18511 = OpFAdd %v2float %19927 %9203 + %7017 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21064 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13155 = OpExtInst %v2float %1 FClamp %18511 %7017 %21064 + %23590 = OpLoad %150 %5785 + %10353 = OpLoad %508 %5688 + %12165 = OpSampledImage %510 %23590 %10353 + %15377 = OpImageSampleExplicitLod %v4float %12165 %13155 Lod %float_0 + %15272 = OpCompositeExtract %float %15377 3 + %12122 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12978 = OpLoad %float %12122 + %15716 = OpFMul %float %15272 %12978 + %15285 = OpExtInst %float %1 FClamp %15716 %float_0 %float_1 + %22219 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11762 = OpLoad %v3float %22219 + %12109 = OpVectorTimesScalar %v3float %11762 %15285 + %15522 = OpLoad %150 %3312 + %24581 = OpLoad %508 %4646 + %12166 = OpSampledImage %510 %15522 %24581 + %17676 = OpImageSampleExplicitLod %v4float %12166 %13155 Lod %float_0 + %16944 = OpCompositeExtract %float %17676 1 + %14191 = OpFOrdGreaterThan %bool %16944 %float_0 + OpSelectionMerge %22313 DontFlatten + OpBranchConditional %14191 %12827 %22313 + %12827 = OpLabel + %13245 = OpLoad %150 %4862 + %19966 = OpLoad %508 %3594 + %12167 = OpSampledImage %510 %13245 %19966 + %15681 = OpImageSampleExplicitLod %v4float %12167 %13155 Lod %float_0 + %13872 = OpCompositeExtract %float %17676 1 + %12433 = OpCompositeExtract %float %17676 2 + %23306 = OpFMul %float %13872 %12433 + %17618 = OpExtInst %float %1 FClamp %23306 %float_0 %float_1 + %20297 = OpVectorShuffle %v3float %15681 %15681 0 1 2 + %11192 = OpVectorTimesScalar %v3float %20297 %17618 + %15299 = OpFAdd %v3float %12109 %11192 + OpBranch %22313 + %22313 = OpLabel + %7725 = OpPhi %v3float %12109 %22312 %15299 %12827 + %23405 = OpVectorTimesScalar %v3float %7725 %float_1 + %9345 = OpFAdd %float %9344 %float_1 + %16241 = OpVectorShuffle %v3float %15532 %15532 0 1 2 + %22183 = OpFAdd %v3float %16241 %23405 + %15533 = OpVectorShuffle %v4float %15532 %22183 4 5 6 3 + %6440 = OpCompositeInsert %_struct_1017 %15533 %6439 0 + %24582 = OpVectorShuffle %v2float %2411 %2411 0 1 + %13213 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10354 = OpLoad %v4float %13213 + %8645 = OpVectorShuffle %v2float %10354 %10354 0 1 + %9204 = OpFMul %v2float %24582 %8645 + %18512 = OpFAdd %v2float %19927 %9204 + %7018 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21065 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13156 = OpExtInst %v2float %1 FClamp %18512 %7018 %21065 + %23591 = OpLoad %150 %5785 + %10355 = OpLoad %508 %5688 + %12168 = OpSampledImage %510 %23591 %10355 + %15378 = OpImageSampleExplicitLod %v4float %12168 %13156 Lod %float_0 + %15273 = OpCompositeExtract %float %15378 3 + %12123 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12979 = OpLoad %float %12123 + %15717 = OpFMul %float %15273 %12979 + %15286 = OpExtInst %float %1 FClamp %15717 %float_0 %float_1 + %22220 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11763 = OpLoad %v3float %22220 + %12110 = OpVectorTimesScalar %v3float %11763 %15286 + %15523 = OpLoad %150 %3312 + %24583 = OpLoad %508 %4646 + %12169 = OpSampledImage %510 %15523 %24583 + %17677 = OpImageSampleExplicitLod %v4float %12169 %13156 Lod %float_0 + %16945 = OpCompositeExtract %float %17677 1 + %14192 = OpFOrdGreaterThan %bool %16945 %float_0 + OpSelectionMerge %22314 DontFlatten + OpBranchConditional %14192 %12828 %22314 + %12828 = OpLabel + %13246 = OpLoad %150 %4862 + %19967 = OpLoad %508 %3594 + %12170 = OpSampledImage %510 %13246 %19967 + %15682 = OpImageSampleExplicitLod %v4float %12170 %13156 Lod %float_0 + %13873 = OpCompositeExtract %float %17677 1 + %12434 = OpCompositeExtract %float %17677 2 + %23307 = OpFMul %float %13873 %12434 + %17619 = OpExtInst %float %1 FClamp %23307 %float_0 %float_1 + %20298 = OpVectorShuffle %v3float %15682 %15682 0 1 2 + %11193 = OpVectorTimesScalar %v3float %20298 %17619 + %15300 = OpFAdd %v3float %12110 %11193 + OpBranch %22314 + %22314 = OpLabel + %7726 = OpPhi %v3float %12110 %22313 %15300 %12828 + %23406 = OpVectorTimesScalar %v3float %7726 %float_0_75 + %9346 = OpFAdd %float %9345 %float_0_75 + %16242 = OpVectorShuffle %v3float %15533 %15533 0 1 2 + %22184 = OpFAdd %v3float %16242 %23406 + %15534 = OpVectorShuffle %v4float %15533 %22184 4 5 6 3 + %6441 = OpCompositeInsert %_struct_1017 %15534 %6440 0 + %24584 = OpVectorShuffle %v2float %2354 %2354 0 1 + %13214 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10356 = OpLoad %v4float %13214 + %8646 = OpVectorShuffle %v2float %10356 %10356 0 1 + %9205 = OpFMul %v2float %24584 %8646 + %18513 = OpFAdd %v2float %19927 %9205 + %7019 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21066 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13157 = OpExtInst %v2float %1 FClamp %18513 %7019 %21066 + %23592 = OpLoad %150 %5785 + %10357 = OpLoad %508 %5688 + %12171 = OpSampledImage %510 %23592 %10357 + %15379 = OpImageSampleExplicitLod %v4float %12171 %13157 Lod %float_0 + %15274 = OpCompositeExtract %float %15379 3 + %12124 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12980 = OpLoad %float %12124 + %15718 = OpFMul %float %15274 %12980 + %15287 = OpExtInst %float %1 FClamp %15718 %float_0 %float_1 + %22221 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11764 = OpLoad %v3float %22221 + %12111 = OpVectorTimesScalar %v3float %11764 %15287 + %15524 = OpLoad %150 %3312 + %24585 = OpLoad %508 %4646 + %12172 = OpSampledImage %510 %15524 %24585 + %17678 = OpImageSampleExplicitLod %v4float %12172 %13157 Lod %float_0 + %16946 = OpCompositeExtract %float %17678 1 + %14193 = OpFOrdGreaterThan %bool %16946 %float_0 + OpSelectionMerge %22315 DontFlatten + OpBranchConditional %14193 %12829 %22315 + %12829 = OpLabel + %13247 = OpLoad %150 %4862 + %19968 = OpLoad %508 %3594 + %12173 = OpSampledImage %510 %13247 %19968 + %15683 = OpImageSampleExplicitLod %v4float %12173 %13157 Lod %float_0 + %13874 = OpCompositeExtract %float %17678 1 + %12435 = OpCompositeExtract %float %17678 2 + %23308 = OpFMul %float %13874 %12435 + %17620 = OpExtInst %float %1 FClamp %23308 %float_0 %float_1 + %20299 = OpVectorShuffle %v3float %15683 %15683 0 1 2 + %11194 = OpVectorTimesScalar %v3float %20299 %17620 + %15301 = OpFAdd %v3float %12111 %11194 + OpBranch %22315 + %22315 = OpLabel + %7727 = OpPhi %v3float %12111 %22314 %15301 %12829 + %23407 = OpVectorTimesScalar %v3float %7727 %float_0_5 + %9347 = OpFAdd %float %9346 %float_0_5 + %16243 = OpVectorShuffle %v3float %15534 %15534 0 1 2 + %22185 = OpFAdd %v3float %16243 %23407 + %15535 = OpVectorShuffle %v4float %15534 %22185 4 5 6 3 + %6442 = OpCompositeInsert %_struct_1017 %15535 %6441 0 + %24586 = OpVectorShuffle %v2float %837 %837 0 1 + %13215 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10358 = OpLoad %v4float %13215 + %8647 = OpVectorShuffle %v2float %10358 %10358 0 1 + %9206 = OpFMul %v2float %24586 %8647 + %18514 = OpFAdd %v2float %19927 %9206 + %7020 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21067 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13158 = OpExtInst %v2float %1 FClamp %18514 %7020 %21067 + %23593 = OpLoad %150 %5785 + %10359 = OpLoad %508 %5688 + %12174 = OpSampledImage %510 %23593 %10359 + %15380 = OpImageSampleExplicitLod %v4float %12174 %13158 Lod %float_0 + %15275 = OpCompositeExtract %float %15380 3 + %12125 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12981 = OpLoad %float %12125 + %15719 = OpFMul %float %15275 %12981 + %15288 = OpExtInst %float %1 FClamp %15719 %float_0 %float_1 + %22222 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11765 = OpLoad %v3float %22222 + %12112 = OpVectorTimesScalar %v3float %11765 %15288 + %15525 = OpLoad %150 %3312 + %24587 = OpLoad %508 %4646 + %12175 = OpSampledImage %510 %15525 %24587 + %17679 = OpImageSampleExplicitLod %v4float %12175 %13158 Lod %float_0 + %16947 = OpCompositeExtract %float %17679 1 + %14194 = OpFOrdGreaterThan %bool %16947 %float_0 + OpSelectionMerge %22316 DontFlatten + OpBranchConditional %14194 %12830 %22316 + %12830 = OpLabel + %13248 = OpLoad %150 %4862 + %19969 = OpLoad %508 %3594 + %12176 = OpSampledImage %510 %13248 %19969 + %15684 = OpImageSampleExplicitLod %v4float %12176 %13158 Lod %float_0 + %13875 = OpCompositeExtract %float %17679 1 + %12436 = OpCompositeExtract %float %17679 2 + %23309 = OpFMul %float %13875 %12436 + %17621 = OpExtInst %float %1 FClamp %23309 %float_0 %float_1 + %20300 = OpVectorShuffle %v3float %15684 %15684 0 1 2 + %11195 = OpVectorTimesScalar %v3float %20300 %17621 + %15302 = OpFAdd %v3float %12112 %11195 + OpBranch %22316 + %22316 = OpLabel + %7728 = OpPhi %v3float %12112 %22315 %15302 %12830 + %23408 = OpVectorTimesScalar %v3float %7728 %float_0_5 + %9348 = OpFAdd %float %9347 %float_0_5 + %16244 = OpVectorShuffle %v3float %15535 %15535 0 1 2 + %22186 = OpFAdd %v3float %16244 %23408 + %15536 = OpVectorShuffle %v4float %15535 %22186 4 5 6 3 + %6443 = OpCompositeInsert %_struct_1017 %15536 %6442 0 + %24588 = OpVectorShuffle %v2float %1368 %1368 0 1 + %13216 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10360 = OpLoad %v4float %13216 + %8648 = OpVectorShuffle %v2float %10360 %10360 0 1 + %9207 = OpFMul %v2float %24588 %8648 + %18515 = OpFAdd %v2float %19927 %9207 + %7021 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21068 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13159 = OpExtInst %v2float %1 FClamp %18515 %7021 %21068 + %23594 = OpLoad %150 %5785 + %10361 = OpLoad %508 %5688 + %12177 = OpSampledImage %510 %23594 %10361 + %15381 = OpImageSampleExplicitLod %v4float %12177 %13159 Lod %float_0 + %15276 = OpCompositeExtract %float %15381 3 + %12126 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12982 = OpLoad %float %12126 + %15720 = OpFMul %float %15276 %12982 + %15289 = OpExtInst %float %1 FClamp %15720 %float_0 %float_1 + %22223 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11766 = OpLoad %v3float %22223 + %12113 = OpVectorTimesScalar %v3float %11766 %15289 + %15526 = OpLoad %150 %3312 + %24589 = OpLoad %508 %4646 + %12178 = OpSampledImage %510 %15526 %24589 + %17680 = OpImageSampleExplicitLod %v4float %12178 %13159 Lod %float_0 + %16948 = OpCompositeExtract %float %17680 1 + %14195 = OpFOrdGreaterThan %bool %16948 %float_0 + OpSelectionMerge %22317 DontFlatten + OpBranchConditional %14195 %12831 %22317 + %12831 = OpLabel + %13249 = OpLoad %150 %4862 + %19970 = OpLoad %508 %3594 + %12179 = OpSampledImage %510 %13249 %19970 + %15685 = OpImageSampleExplicitLod %v4float %12179 %13159 Lod %float_0 + %13876 = OpCompositeExtract %float %17680 1 + %12437 = OpCompositeExtract %float %17680 2 + %23310 = OpFMul %float %13876 %12437 + %17622 = OpExtInst %float %1 FClamp %23310 %float_0 %float_1 + %20301 = OpVectorShuffle %v3float %15685 %15685 0 1 2 + %11196 = OpVectorTimesScalar %v3float %20301 %17622 + %15303 = OpFAdd %v3float %12113 %11196 + OpBranch %22317 + %22317 = OpLabel + %7729 = OpPhi %v3float %12113 %22316 %15303 %12831 + %23409 = OpVectorTimesScalar %v3float %7729 %float_0_75 + %9349 = OpFAdd %float %9348 %float_0_75 + %16245 = OpVectorShuffle %v3float %15536 %15536 0 1 2 + %22187 = OpFAdd %v3float %16245 %23409 + %15537 = OpVectorShuffle %v4float %15536 %22187 4 5 6 3 + %6444 = OpCompositeInsert %_struct_1017 %15537 %6443 0 + %24590 = OpVectorShuffle %v2float %142 %142 0 1 + %13217 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10362 = OpLoad %v4float %13217 + %8649 = OpVectorShuffle %v2float %10362 %10362 0 1 + %9208 = OpFMul %v2float %24590 %8649 + %18516 = OpFAdd %v2float %19927 %9208 + %7022 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21069 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13160 = OpExtInst %v2float %1 FClamp %18516 %7022 %21069 + %23595 = OpLoad %150 %5785 + %10363 = OpLoad %508 %5688 + %12180 = OpSampledImage %510 %23595 %10363 + %15382 = OpImageSampleExplicitLod %v4float %12180 %13160 Lod %float_0 + %15277 = OpCompositeExtract %float %15382 3 + %12127 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12983 = OpLoad %float %12127 + %15721 = OpFMul %float %15277 %12983 + %15290 = OpExtInst %float %1 FClamp %15721 %float_0 %float_1 + %22224 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11767 = OpLoad %v3float %22224 + %12114 = OpVectorTimesScalar %v3float %11767 %15290 + %15538 = OpLoad %150 %3312 + %24591 = OpLoad %508 %4646 + %12181 = OpSampledImage %510 %15538 %24591 + %17681 = OpImageSampleExplicitLod %v4float %12181 %13160 Lod %float_0 + %16949 = OpCompositeExtract %float %17681 1 + %14196 = OpFOrdGreaterThan %bool %16949 %float_0 + OpSelectionMerge %22318 DontFlatten + OpBranchConditional %14196 %12832 %22318 + %12832 = OpLabel + %13250 = OpLoad %150 %4862 + %19971 = OpLoad %508 %3594 + %12182 = OpSampledImage %510 %13250 %19971 + %15686 = OpImageSampleExplicitLod %v4float %12182 %13160 Lod %float_0 + %13877 = OpCompositeExtract %float %17681 1 + %12438 = OpCompositeExtract %float %17681 2 + %23311 = OpFMul %float %13877 %12438 + %17623 = OpExtInst %float %1 FClamp %23311 %float_0 %float_1 + %20302 = OpVectorShuffle %v3float %15686 %15686 0 1 2 + %11197 = OpVectorTimesScalar %v3float %20302 %17623 + %15304 = OpFAdd %v3float %12114 %11197 + OpBranch %22318 + %22318 = OpLabel + %7730 = OpPhi %v3float %12114 %22317 %15304 %12832 + %23410 = OpVectorTimesScalar %v3float %7730 %float_0_5 + %9350 = OpFAdd %float %9349 %float_0_5 + %16246 = OpVectorShuffle %v3float %15537 %15537 0 1 2 + %22188 = OpFAdd %v3float %16246 %23410 + %15539 = OpVectorShuffle %v4float %15537 %22188 4 5 6 3 + %6445 = OpCompositeInsert %_struct_1017 %15539 %6444 0 + %24592 = OpVectorShuffle %v2float %1197 %1197 0 1 + %13218 = OpAccessChain %_ptr_Uniform_v4float %22044 %int_0 + %10364 = OpLoad %v4float %13218 + %8650 = OpVectorShuffle %v2float %10364 %10364 0 1 + %9209 = OpFMul %v2float %24592 %8650 + %18517 = OpFAdd %v2float %19927 %9209 + %7023 = OpVectorShuffle %v2float %17581 %17581 0 1 + %21070 = OpVectorShuffle %v2float %17581 %17581 2 3 + %13161 = OpExtInst %v2float %1 FClamp %18517 %7023 %21070 + %23596 = OpLoad %150 %5785 + %10365 = OpLoad %508 %5688 + %12183 = OpSampledImage %510 %23596 %10365 + %15383 = OpImageSampleExplicitLod %v4float %12183 %13161 Lod %float_0 + %15278 = OpCompositeExtract %float %15383 3 + %12128 = OpAccessChain %_ptr_Uniform_float %22044 %int_1 + %12984 = OpLoad %float %12128 + %15722 = OpFMul %float %15278 %12984 + %15291 = OpExtInst %float %1 FClamp %15722 %float_0 %float_1 + %22225 = OpAccessChain %_ptr_Uniform_v3float %12348 %int_5 + %11768 = OpLoad %v3float %22225 + %12115 = OpVectorTimesScalar %v3float %11768 %15291 + %15540 = OpLoad %150 %3312 + %24593 = OpLoad %508 %4646 + %12184 = OpSampledImage %510 %15540 %24593 + %17682 = OpImageSampleExplicitLod %v4float %12184 %13161 Lod %float_0 + %16950 = OpCompositeExtract %float %17682 1 + %14197 = OpFOrdGreaterThan %bool %16950 %float_0 + OpSelectionMerge %22319 DontFlatten + OpBranchConditional %14197 %12833 %22319 + %12833 = OpLabel + %13251 = OpLoad %150 %4862 + %19972 = OpLoad %508 %3594 + %12185 = OpSampledImage %510 %13251 %19972 + %15687 = OpImageSampleExplicitLod %v4float %12185 %13161 Lod %float_0 + %13878 = OpCompositeExtract %float %17682 1 + %12439 = OpCompositeExtract %float %17682 2 + %23312 = OpFMul %float %13878 %12439 + %17624 = OpExtInst %float %1 FClamp %23312 %float_0 %float_1 + %20303 = OpVectorShuffle %v3float %15687 %15687 0 1 2 + %11198 = OpVectorTimesScalar %v3float %20303 %17624 + %15305 = OpFAdd %v3float %12115 %11198 + OpBranch %22319 + %22319 = OpLabel + %7731 = OpPhi %v3float %12115 %22318 %15305 %12833 + %23411 = OpVectorTimesScalar %v3float %7731 %float_0_5 + %9351 = OpFAdd %float %9350 %float_0_5 + %16247 = OpVectorShuffle %v3float %15539 %15539 0 1 2 + %22189 = OpFAdd %v3float %16247 %23411 + %15541 = OpVectorShuffle %v4float %15539 %22189 4 5 6 3 + %6719 = OpCompositeInsert %_struct_1017 %15541 %6445 0 + %23412 = OpVectorShuffle %v3float %15541 %15541 0 1 2 + %10833 = OpCompositeConstruct %v3float %9351 %9351 %9351 + %13750 = OpFDiv %v3float %23412 %10833 + %24033 = OpVectorShuffle %v4float %15541 %13750 4 5 6 3 + %8636 = OpCompositeInsert %_struct_1017 %24033 %6719 0 + %16315 = OpCompositeInsert %_struct_1017 %float_1 %8636 0 3 + %11544 = OpCompositeExtract %v4float %16315 0 + OpStore %4317 %11544 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/geom/block-name-namespace.asm.geom b/third_party/spirv-cross/shaders/asm/geom/block-name-namespace.asm.geom new file mode 100644 index 0000000..b3744a3 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/geom/block-name-namespace.asm.geom @@ -0,0 +1,103 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 48 +; Schema: 0 + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %_ %VertexOutput %vin + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 4 + OpSource GLSL 450 + OpName %main "main" + OpName %VertexInput3 "VertexInput" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpName %VertexInput "VertexInput" + OpMemberName %VertexInput 0 "a" + OpName %VertexInput4 "VertexInput" + OpName %VertexInput_0 "VertexInput" + OpMemberName %VertexInput_0 0 "b" + OpName %VertexInput2 "VertexInput" + OpName %VertexInput_1 "VertexInput" + OpMemberName %VertexInput_1 0 "vColor" + OpName %VertexOutput "VertexInput" + OpName %VertexInput_2 "VertexInput" + OpMemberName %VertexInput_2 0 "vColor" + OpName %vin "vin" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + OpMemberDecorate %VertexInput 0 Offset 0 + OpDecorate %VertexInput Block + OpDecorate %VertexInput4 DescriptorSet 0 + OpDecorate %VertexInput4 Binding 0 + OpMemberDecorate %VertexInput_0 0 Offset 0 + OpDecorate %VertexInput_0 BufferBlock + OpDecorate %VertexInput2 DescriptorSet 0 + OpDecorate %VertexInput2 Binding 0 + OpDecorate %VertexInput_1 Block + OpDecorate %VertexOutput Location 0 + OpDecorate %VertexInput_2 Block + OpDecorate %vin Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %float_1 = OpConstant %float 1 + %11 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%VertexInput = OpTypeStruct %v4float +%_ptr_Uniform_VertexInput = OpTypePointer Uniform %VertexInput +%VertexInput4 = OpVariable %_ptr_Uniform_VertexInput Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%VertexInput_0 = OpTypeStruct %v4float +%_ptr_Uniform_VertexInput_0 = OpTypePointer Uniform %VertexInput_0 +%VertexInput2 = OpVariable %_ptr_Uniform_VertexInput_0 Uniform +%_ptr_Output_v4float = OpTypePointer Output %v4float +%VertexInput_1 = OpTypeStruct %v4float +%_ptr_Output_VertexInput_1 = OpTypePointer Output %VertexInput_1 +%VertexOutput = OpVariable %_ptr_Output_VertexInput_1 Output +%VertexInput_2 = OpTypeStruct %v4float + %uint_3 = OpConstant %uint 3 +%_arr_VertexInput_2_uint_3 = OpTypeArray %VertexInput_2 %uint_3 +%_ptr_Input__arr_VertexInput_2_uint_3 = OpTypePointer Input %_arr_VertexInput_2_uint_3 + %vin = OpVariable %_ptr_Input__arr_VertexInput_2_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float + %main = OpFunction %void None %3 + %5 = OpLabel +%VertexInput3 = OpVariable %_ptr_Function_v4float Function + OpStore %VertexInput3 %11 + %20 = OpLoad %v4float %VertexInput3 + %25 = OpAccessChain %_ptr_Uniform_v4float %VertexInput4 %int_0 + %26 = OpLoad %v4float %25 + %27 = OpFAdd %v4float %20 %26 + %31 = OpAccessChain %_ptr_Uniform_v4float %VertexInput2 %int_0 + %32 = OpLoad %v4float %31 + %33 = OpFAdd %v4float %27 %32 + %35 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %35 %33 + %45 = OpAccessChain %_ptr_Input_v4float %vin %int_0 %int_0 + %46 = OpLoad %v4float %45 + %47 = OpAccessChain %_ptr_Output_v4float %VertexOutput %int_0 + OpStore %47 %46 + OpEmitVertex + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/geom/inout-split-access-chain-handle.asm.geom b/third_party/spirv-cross/shaders/asm/geom/inout-split-access-chain-handle.asm.geom new file mode 100644 index 0000000..d011cc6 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/geom/inout-split-access-chain-handle.asm.geom @@ -0,0 +1,90 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 42 +; Schema: 0 + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %gl_in %_ + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 5 + OpSource GLSL 440 + OpName %main "main" + OpName %Data "Data" + OpMemberName %Data 0 "ApiPerspectivePosition" + OpName %Copy_struct_Data_vf41_3__ "Copy(struct-Data-vf41[3];" + OpName %inputStream "inputStream" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpName %gl_in "gl_in" + OpName %inputStream_0 "inputStream" + OpName %param "param" + OpName %gl_PerVertex_0 "gl_PerVertex" + OpMemberName %gl_PerVertex_0 0 "gl_Position" + OpMemberName %gl_PerVertex_0 1 "gl_PointSize" + OpMemberName %gl_PerVertex_0 2 "gl_ClipDistance" + OpName %_ "" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpDecorate %gl_PerVertex Block + OpMemberDecorate %gl_PerVertex_0 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex_0 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex_0 2 BuiltIn ClipDistance + OpDecorate %gl_PerVertex_0 Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %Data = OpTypeStruct %v4float + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_Data_uint_3 = OpTypeArray %Data %uint_3 +%_ptr_Function__Data = OpTypePointer Function %Data +%_ptr_Function__arr_Data_uint_3 = OpTypePointer Function %_arr_Data_uint_3 + %13 = OpTypeFunction %void %_ptr_Function__arr_Data_uint_3 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 +%_arr_gl_PerVertex_uint_3 = OpTypeArray %gl_PerVertex %uint_3 +%_ptr_Input__arr_gl_PerVertex_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_uint_3 + %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_3 Input +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%gl_PerVertex_0 = OpTypeStruct %v4float %float %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex_0 = OpTypePointer Output %gl_PerVertex_0 + %_ = OpVariable %_ptr_Output_gl_PerVertex_0 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel +%inputStream_0 = OpVariable %_ptr_Function__arr_Data_uint_3 Function + %param = OpVariable %_ptr_Function__arr_Data_uint_3 Function + %32 = OpLoad %_arr_Data_uint_3 %inputStream_0 + OpStore %param %32 + %33 = OpFunctionCall %void %Copy_struct_Data_vf41_3__ %param + %34 = OpLoad %_arr_Data_uint_3 %param + OpStore %inputStream_0 %34 + %59 = OpAccessChain %_ptr_Function__Data %inputStream_0 %int_0 + %38 = OpAccessChain %_ptr_Function_v4float %59 %int_0 + %39 = OpLoad %v4float %38 + %41 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %41 %39 + OpReturn + OpFunctionEnd +%Copy_struct_Data_vf41_3__ = OpFunction %void None %13 +%inputStream = OpFunctionParameter %_ptr_Function__arr_Data_uint_3 + %16 = OpLabel + %26 = OpAccessChain %_ptr_Input_v4float %gl_in %int_0 %int_0 + %27 = OpLoad %v4float %26 + %28 = OpAccessChain %_ptr_Function__Data %inputStream %int_0 + %29 = OpAccessChain %_ptr_Function_v4float %28 %int_0 + OpStore %29 %27 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/geom/split-access-chain-input.asm.geom b/third_party/spirv-cross/shaders/asm/geom/split-access-chain-input.asm.geom new file mode 100644 index 0000000..5e477fa --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/geom/split-access-chain-input.asm.geom @@ -0,0 +1,52 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 23 +; Schema: 0 + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %gl_in %position + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 3 + OpSource GLSL 440 + OpName %main "main" + OpName %position "position" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpName %gl_in "gl_in" + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpDecorate %gl_PerVertex Block + OpDecorate %position BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Output %v4float + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 + %uint_3 = OpConstant %uint 3 +%_arr_gl_PerVertex_uint_3 = OpTypeArray %gl_PerVertex %uint_3 +%ptr_Input_gl_PerVertex = OpTypePointer Input %gl_PerVertex +%_ptr_Input__arr_gl_PerVertex_uint_3 = OpTypePointer Input %_arr_gl_PerVertex_uint_3 + %gl_in = OpVariable %_ptr_Input__arr_gl_PerVertex_uint_3 Input + %position = OpVariable %_ptr_Function_v4float Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %21 = OpAccessChain %ptr_Input_gl_PerVertex %gl_in %int_0 + %22 = OpAccessChain %_ptr_Input_v4float %21 %int_0 + %23 = OpLoad %v4float %22 + OpStore %position %23 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/geom/unroll-glposition-load.asm.geom b/third_party/spirv-cross/shaders/asm/geom/unroll-glposition-load.asm.geom new file mode 100644 index 0000000..8c10de3 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/geom/unroll-glposition-load.asm.geom @@ -0,0 +1,102 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 55 +; Schema: 0 + OpCapability Geometry + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %OUT_pos %positions_1 + OpExecutionMode %main Triangles + OpExecutionMode %main Invocations 1 + OpExecutionMode %main OutputTriangleStrip + OpExecutionMode %main OutputVertices 3 + OpSource HLSL 500 + OpName %main "main" + OpName %SceneOut "SceneOut" + OpMemberName %SceneOut 0 "pos" + OpName %_main_vf4_3__struct_SceneOut_vf41_ "@main(vf4[3];struct-SceneOut-vf41;" + OpName %positions "positions" + OpName %OUT "OUT" + OpName %i "i" + OpName %o "o" + OpName %OUT_pos "OUT.pos" + OpName %positions_0 "positions" + OpName %positions_1 "positions" + OpName %OUT_0 "OUT" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %OUT_pos BuiltIn Position + OpDecorate %positions_1 BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Function__arr_v4float_uint_3 = OpTypePointer Function %_arr_v4float_uint_3 + %SceneOut = OpTypeStruct %v4float +%_ptr_Function_SceneOut = OpTypePointer Function %SceneOut + %14 = OpTypeFunction %void %_ptr_Function__arr_v4float_uint_3 %_ptr_Function_SceneOut + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_3 = OpConstant %int 3 + %bool = OpTypeBool +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OUT_pos = OpVariable %_ptr_Output_v4float Output + %int_1 = OpConstant %int 1 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 +%positions_1 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input + %main = OpFunction %void None %3 + %5 = OpLabel +%positions_0 = OpVariable %_ptr_Function__arr_v4float_uint_3 Function + %OUT_0 = OpVariable %_ptr_Function_SceneOut Function + %param = OpVariable %_ptr_Function__arr_v4float_uint_3 Function + %param_0 = OpVariable %_ptr_Function_SceneOut Function + %48 = OpLoad %_arr_v4float_uint_3 %positions_1 + OpStore %positions_0 %48 + %51 = OpLoad %_arr_v4float_uint_3 %positions_0 + OpStore %param %51 + %53 = OpFunctionCall %void %_main_vf4_3__struct_SceneOut_vf41_ %param %param_0 + %54 = OpLoad %SceneOut %param_0 + OpStore %OUT_0 %54 + OpReturn + OpFunctionEnd +%_main_vf4_3__struct_SceneOut_vf41_ = OpFunction %void None %14 + %positions = OpFunctionParameter %_ptr_Function__arr_v4float_uint_3 + %OUT = OpFunctionParameter %_ptr_Function_SceneOut + %18 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %o = OpVariable %_ptr_Function_SceneOut Function + OpStore %i %int_0 + OpBranch %23 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %28 = OpLoad %int %i + %31 = OpSLessThan %bool %28 %int_3 + OpBranchConditional %31 %24 %25 + %24 = OpLabel + %33 = OpLoad %int %i + %35 = OpAccessChain %_ptr_Function_v4float %positions %33 + %36 = OpLoad %v4float %35 + %37 = OpAccessChain %_ptr_Function_v4float %o %int_0 + OpStore %37 %36 + %40 = OpAccessChain %_ptr_Function_v4float %o %int_0 + %41 = OpLoad %v4float %40 + OpStore %OUT_pos %41 + OpEmitVertex + OpBranch %26 + %26 = OpLabel + %42 = OpLoad %int %i + %44 = OpIAdd %int %42 %int_1 + OpStore %i %44 + OpBranch %23 + %25 = OpLabel + OpEndPrimitive + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/tese/unroll-input-array-load.asm.tese b/third_party/spirv-cross/shaders/asm/tese/unroll-input-array-load.asm.tese new file mode 100644 index 0000000..960b8fa --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/tese/unroll-input-array-load.asm.tese @@ -0,0 +1,131 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 79 +; Schema: 0 + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationEvaluation %main "main" %input_foo %input_bar %uv_1 %CPData_1 %_entryPointOutput_pos + OpExecutionMode %main Quads + OpSource HLSL 500 + OpName %main "main" + OpName %HS_INPUT "HS_INPUT" + OpMemberName %HS_INPUT 0 "foo" + OpMemberName %HS_INPUT 1 "bar" + OpName %ControlPoint "ControlPoint" + OpMemberName %ControlPoint 0 "baz" + OpName %DS_OUTPUT "DS_OUTPUT" + OpMemberName %DS_OUTPUT 0 "pos" + OpName %_main_struct_HS_INPUT_vf4_vf41_vf2_struct_ControlPoint_vf41_4__ "@main(struct-HS_INPUT-vf4-vf41;vf2;struct-ControlPoint-vf41[4];" + OpName %input "input" + OpName %uv "uv" + OpName %CPData "CPData" + OpName %o "o" + OpName %input_0 "input" + OpName %input_foo "input.foo" + OpName %input_bar "input.bar" + OpName %uv_0 "uv" + OpName %uv_1 "uv" + OpName %CPData_0 "CPData" + OpName %CPData_1 "CPData" + OpName %_entryPointOutput_pos "@entryPointOutput.pos" + OpName %param "param" + OpName %param_0 "param" + OpName %param_1 "param" + OpDecorate %input_foo Patch + OpDecorate %input_foo Location 0 + OpDecorate %input_bar Patch + OpDecorate %input_bar Location 1 + OpDecorate %uv_1 Patch + OpDecorate %uv_1 BuiltIn TessCoord + OpDecorate %CPData_1 Location 2 + OpDecorate %_entryPointOutput_pos BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %HS_INPUT = OpTypeStruct %v4float %v4float +%_ptr_Function_HS_INPUT = OpTypePointer Function %HS_INPUT + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%ControlPoint = OpTypeStruct %v4float + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 +%_arr_ControlPoint_uint_4 = OpTypeArray %ControlPoint %uint_4 +%_ptr_Function__arr_ControlPoint_uint_4 = OpTypePointer Function %_arr_ControlPoint_uint_4 + %DS_OUTPUT = OpTypeStruct %v4float + %18 = OpTypeFunction %DS_OUTPUT %_ptr_Function_HS_INPUT %_ptr_Function_v2float %_ptr_Function__arr_ControlPoint_uint_4 +%_ptr_Function_DS_OUTPUT = OpTypePointer Function %DS_OUTPUT + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %int_1 = OpConstant %int 1 + %int_3 = OpConstant %int 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %input_foo = OpVariable %_ptr_Input_v4float Input + %input_bar = OpVariable %_ptr_Input_v4float Input + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %uv_1 = OpVariable %_ptr_Input_v3float Input +%_ptr_Input__arr_ControlPoint_uint_4 = OpTypePointer Input %_arr_ControlPoint_uint_4 + %CPData_1 = OpVariable %_ptr_Input__arr_ControlPoint_uint_4 Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_pos = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %input_0 = OpVariable %_ptr_Function_HS_INPUT Function + %uv_0 = OpVariable %_ptr_Function_v2float Function + %CPData_0 = OpVariable %_ptr_Function__arr_ControlPoint_uint_4 Function + %param = OpVariable %_ptr_Function_HS_INPUT Function + %param_0 = OpVariable %_ptr_Function_v2float Function + %param_1 = OpVariable %_ptr_Function__arr_ControlPoint_uint_4 Function + %52 = OpLoad %v4float %input_foo + %53 = OpAccessChain %_ptr_Function_v4float %input_0 %int_0 + OpStore %53 %52 + %55 = OpLoad %v4float %input_bar + %56 = OpAccessChain %_ptr_Function_v4float %input_0 %int_1 + OpStore %56 %55 + %61 = OpLoad %v3float %uv_1 + %62 = OpCompositeExtract %float %61 0 + %63 = OpCompositeExtract %float %61 1 + %64 = OpCompositeConstruct %v2float %62 %63 + OpStore %uv_0 %64 + %68 = OpLoad %_arr_ControlPoint_uint_4 %CPData_1 + OpStore %CPData_0 %68 + %72 = OpLoad %HS_INPUT %input_0 + OpStore %param %72 + %74 = OpLoad %v2float %uv_0 + OpStore %param_0 %74 + %76 = OpLoad %_arr_ControlPoint_uint_4 %CPData_0 + OpStore %param_1 %76 + %77 = OpFunctionCall %DS_OUTPUT %_main_struct_HS_INPUT_vf4_vf41_vf2_struct_ControlPoint_vf41_4__ %param %param_0 %param_1 + %78 = OpCompositeExtract %v4float %77 0 + OpStore %_entryPointOutput_pos %78 + OpReturn + OpFunctionEnd +%_main_struct_HS_INPUT_vf4_vf41_vf2_struct_ControlPoint_vf41_4__ = OpFunction %DS_OUTPUT None %18 + %input = OpFunctionParameter %_ptr_Function_HS_INPUT + %uv = OpFunctionParameter %_ptr_Function_v2float + %CPData = OpFunctionParameter %_ptr_Function__arr_ControlPoint_uint_4 + %23 = OpLabel + %o = OpVariable %_ptr_Function_DS_OUTPUT Function + %29 = OpAccessChain %_ptr_Function_v4float %input %int_0 + %30 = OpLoad %v4float %29 + %32 = OpAccessChain %_ptr_Function_v4float %input %int_1 + %33 = OpLoad %v4float %32 + %34 = OpFAdd %v4float %30 %33 + %35 = OpLoad %v2float %uv + %36 = OpVectorShuffle %v4float %35 %35 0 1 0 1 + %37 = OpFAdd %v4float %34 %36 + %38 = OpAccessChain %_ptr_Function_v4float %CPData %int_0 %int_0 + %39 = OpLoad %v4float %38 + %40 = OpFAdd %v4float %37 %39 + %42 = OpAccessChain %_ptr_Function_v4float %CPData %int_3 %int_0 + %43 = OpLoad %v4float %42 + %44 = OpFAdd %v4float %40 %43 + %45 = OpAccessChain %_ptr_Function_v4float %o %int_0 + OpStore %45 %44 + %46 = OpLoad %DS_OUTPUT %o + OpReturnValue %46 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/empty-io.asm.vert b/third_party/spirv-cross/shaders/asm/vert/empty-io.asm.vert new file mode 100644 index 0000000..0ba6cb7 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/empty-io.asm.vert @@ -0,0 +1,70 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 40 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %position %_entryPointOutput_position %_entryPointOutput + OpName %main "main" + OpName %VSInput "VSInput" + OpMemberName %VSInput 0 "position" + OpName %VSOutput "VSOutput" + OpMemberName %VSOutput 0 "position" + OpName %_main_struct_VSInput_vf41_ "@main(struct-VSInput-vf41;" + OpName %_input "_input" + OpName %_out "_out" + OpName %_input_0 "_input" + OpName %position "position" + OpName %_entryPointOutput_position "@entryPointOutput_position" + OpName %param "param" + OpName %VSOutput_0 "VSOutput" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %position Location 0 + OpDecorate %_entryPointOutput_position BuiltIn Position + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %VSInput = OpTypeStruct %v4float +%_ptr_Function_VSInput = OpTypePointer Function %VSInput + %VSOutput = OpTypeStruct %v4float + %11 = OpTypeFunction %VSOutput %_ptr_Function_VSInput +%_ptr_Function_VSOutput = OpTypePointer Function %VSOutput + %int = OpTypeInt 32 1 + %18 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float + %position = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_position = OpVariable %_ptr_Output_v4float Output + %VSOutput_0 = OpTypeStruct +%_ptr_Output_VSOutput_0 = OpTypePointer Output %VSOutput_0 +%_entryPointOutput = OpVariable %_ptr_Output_VSOutput_0 Output + %main = OpFunction %void None %3 + %5 = OpLabel + %_input_0 = OpVariable %_ptr_Function_VSInput Function + %param = OpVariable %_ptr_Function_VSInput Function + %29 = OpLoad %v4float %position + %30 = OpAccessChain %_ptr_Function_v4float %_input_0 %18 + OpStore %30 %29 + %34 = OpLoad %VSInput %_input_0 + OpStore %param %34 + %35 = OpFunctionCall %VSOutput %_main_struct_VSInput_vf41_ %param + %36 = OpCompositeExtract %v4float %35 0 + OpStore %_entryPointOutput_position %36 + OpReturn + OpFunctionEnd +%_main_struct_VSInput_vf41_ = OpFunction %VSOutput None %11 + %_input = OpFunctionParameter %_ptr_Function_VSInput + %14 = OpLabel + %_out = OpVariable %_ptr_Function_VSOutput Function + %20 = OpAccessChain %_ptr_Function_v4float %_input %18 + %21 = OpLoad %v4float %20 + %22 = OpAccessChain %_ptr_Function_v4float %_out %18 + OpStore %22 %21 + %23 = OpLoad %VSOutput %_out + OpReturnValue %23 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert b/third_party/spirv-cross/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert new file mode 100644 index 0000000..89edeaa --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/extract-transposed-matrix-from-struct.asm.vert @@ -0,0 +1,141 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 79 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %VS "main" %PosL_1 %instanceID_1 %_entryPointOutput_Position %_entryPointOutput_Color + OpSource HLSL 500 + OpName %VS "VS" + OpName %V2F "V2F" + OpMemberName %V2F 0 "Position" + OpMemberName %V2F 1 "Color" + OpName %_VS_vf3_u1_ "@VS(vf3;u1;" + OpName %PosL "PosL" + OpName %instanceID "instanceID" + OpName %InstanceData "InstanceData" + OpMemberName %InstanceData 0 "MATRIX_MVP" + OpMemberName %InstanceData 1 "Color" + OpName %instData "instData" + OpName %InstanceData_0 "InstanceData" + OpMemberName %InstanceData_0 0 "MATRIX_MVP" + OpMemberName %InstanceData_0 1 "Color" + OpName %gInstanceData "gInstanceData" + OpMemberName %gInstanceData 0 "@data" + OpName %gInstanceData_0 "gInstanceData" + OpName %v2f "v2f" + OpName %PosL_0 "PosL" + OpName %PosL_1 "PosL" + OpName %instanceID_0 "instanceID" + OpName %instanceID_1 "instanceID" + OpName %flattenTemp "flattenTemp" + OpName %param "param" + OpName %param_0 "param" + OpName %_entryPointOutput_Position "@entryPointOutput.Position" + OpName %_entryPointOutput_Color "@entryPointOutput.Color" + OpMemberDecorate %InstanceData_0 0 RowMajor + OpMemberDecorate %InstanceData_0 0 Offset 0 + OpMemberDecorate %InstanceData_0 0 MatrixStride 16 + OpMemberDecorate %InstanceData_0 1 Offset 64 + OpDecorate %_runtimearr_InstanceData_0 ArrayStride 80 + OpMemberDecorate %gInstanceData 0 NonWritable + OpMemberDecorate %gInstanceData 0 Offset 0 + OpDecorate %gInstanceData BufferBlock + OpDecorate %gInstanceData_0 DescriptorSet 1 + OpDecorate %gInstanceData_0 Binding 0 + OpDecorate %PosL_1 Location 0 + OpDecorate %instanceID_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput_Position BuiltIn Position + OpDecorate %_entryPointOutput_Color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %v4float = OpTypeVector %float 4 + %V2F = OpTypeStruct %v4float %v4float + %13 = OpTypeFunction %V2F %_ptr_Function_v3float %_ptr_Function_uint +%mat4v4float = OpTypeMatrix %v4float 4 +%InstanceData = OpTypeStruct %mat4v4float %v4float +%_ptr_Function_InstanceData = OpTypePointer Function %InstanceData +%InstanceData_0 = OpTypeStruct %mat4v4float %v4float +%_runtimearr_InstanceData_0 = OpTypeRuntimeArray %InstanceData_0 +%gInstanceData = OpTypeStruct %_runtimearr_InstanceData_0 +%_ptr_Uniform_gInstanceData = OpTypePointer Uniform %gInstanceData +%gInstanceData_0 = OpVariable %_ptr_Uniform_gInstanceData Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_InstanceData_0 = OpTypePointer Uniform %InstanceData_0 +%_ptr_Function_mat4v4float = OpTypePointer Function %mat4v4float + %int_1 = OpConstant %int 1 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_V2F = OpTypePointer Function %V2F + %float_1 = OpConstant %float 1 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %PosL_1 = OpVariable %_ptr_Input_v3float Input +%_ptr_Input_uint = OpTypePointer Input %uint +%instanceID_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Position = OpVariable %_ptr_Output_v4float Output +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output + %VS = OpFunction %void None %3 + %5 = OpLabel + %PosL_0 = OpVariable %_ptr_Function_v3float Function +%instanceID_0 = OpVariable %_ptr_Function_uint Function +%flattenTemp = OpVariable %_ptr_Function_V2F Function + %param = OpVariable %_ptr_Function_v3float Function + %param_0 = OpVariable %_ptr_Function_uint Function + %61 = OpLoad %v3float %PosL_1 + OpStore %PosL_0 %61 + %65 = OpLoad %uint %instanceID_1 + OpStore %instanceID_0 %65 + %68 = OpLoad %v3float %PosL_0 + OpStore %param %68 + %70 = OpLoad %uint %instanceID_0 + OpStore %param_0 %70 + %71 = OpFunctionCall %V2F %_VS_vf3_u1_ %param %param_0 + OpStore %flattenTemp %71 + %74 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_0 + %75 = OpLoad %v4float %74 + OpStore %_entryPointOutput_Position %75 + %77 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_1 + %78 = OpLoad %v4float %77 + OpStore %_entryPointOutput_Color %78 + OpReturn + OpFunctionEnd +%_VS_vf3_u1_ = OpFunction %V2F None %13 + %PosL = OpFunctionParameter %_ptr_Function_v3float + %instanceID = OpFunctionParameter %_ptr_Function_uint + %17 = OpLabel + %instData = OpVariable %_ptr_Function_InstanceData Function + %v2f = OpVariable %_ptr_Function_V2F Function + %29 = OpLoad %uint %instanceID + %31 = OpAccessChain %_ptr_Uniform_InstanceData_0 %gInstanceData_0 %int_0 %29 + %32 = OpLoad %InstanceData_0 %31 + %33 = OpCompositeExtract %mat4v4float %32 0 + %35 = OpAccessChain %_ptr_Function_mat4v4float %instData %int_0 + OpStore %35 %33 + %36 = OpCompositeExtract %v4float %32 1 + %39 = OpAccessChain %_ptr_Function_v4float %instData %int_1 + OpStore %39 %36 + %42 = OpAccessChain %_ptr_Function_mat4v4float %instData %int_0 + %43 = OpLoad %mat4v4float %42 + %44 = OpLoad %v3float %PosL + %46 = OpCompositeExtract %float %44 0 + %47 = OpCompositeExtract %float %44 1 + %48 = OpCompositeExtract %float %44 2 + %49 = OpCompositeConstruct %v4float %46 %47 %48 %float_1 + %50 = OpMatrixTimesVector %v4float %43 %49 + %51 = OpAccessChain %_ptr_Function_v4float %v2f %int_0 + OpStore %51 %50 + %52 = OpAccessChain %_ptr_Function_v4float %instData %int_1 + %53 = OpLoad %v4float %52 + %54 = OpAccessChain %_ptr_Function_v4float %v2f %int_1 + OpStore %54 %53 + %55 = OpLoad %V2F %v2f + OpReturnValue %55 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/global-builtin.sso.asm.vert b/third_party/spirv-cross/shaders/asm/vert/global-builtin.sso.asm.vert new file mode 100644 index 0000000..d7306de --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/global-builtin.sso.asm.vert @@ -0,0 +1,68 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 40 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_entryPointOutput %_entryPointOutput_pos + OpSource HLSL 500 + OpName %main "main" + OpName %VSOut "VSOut" + OpMemberName %VSOut 0 "a" + OpMemberName %VSOut 1 "pos" + OpName %_main_ "@main(" + OpName %vout "vout" + OpName %flattenTemp "flattenTemp" + OpName %VSOut_0 "VSOut" + OpMemberName %VSOut_0 0 "a" + OpName %_entryPointOutput "@entryPointOutput" + OpName %_entryPointOutput_pos "@entryPointOutput_pos" + OpDecorate %_entryPointOutput Location 0 + OpDecorate %_entryPointOutput_pos BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %VSOut = OpTypeStruct %float %v4float + %9 = OpTypeFunction %VSOut +%_ptr_Function_VSOut = OpTypePointer Function %VSOut + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_40 = OpConstant %float 40 +%_ptr_Function_float = OpTypePointer Function %float + %int_1 = OpConstant %int 1 + %float_1 = OpConstant %float 1 + %21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %VSOut_0 = OpTypeStruct %float +%_ptr_Output_VSOut_0 = OpTypePointer Output %VSOut_0 +%_entryPointOutput = OpVariable %_ptr_Output_VSOut_0 Output +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_pos = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel +%flattenTemp = OpVariable %_ptr_Function_VSOut Function + %28 = OpFunctionCall %VSOut %_main_ + OpStore %flattenTemp %28 + %32 = OpAccessChain %_ptr_Function_float %flattenTemp %int_0 + %33 = OpLoad %float %32 + %35 = OpAccessChain %_ptr_Output_float %_entryPointOutput %int_0 + OpStore %35 %33 + %38 = OpAccessChain %_ptr_Function_v4float %flattenTemp %int_1 + %39 = OpLoad %v4float %38 + OpStore %_entryPointOutput_pos %39 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %VSOut None %9 + %11 = OpLabel + %vout = OpVariable %_ptr_Function_VSOut Function + %18 = OpAccessChain %_ptr_Function_float %vout %int_0 + OpStore %18 %float_40 + %23 = OpAccessChain %_ptr_Function_v4float %vout %int_1 + OpStore %23 %21 + %24 = OpLoad %VSOut %vout + OpReturnValue %24 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/invariant-block.asm.vert b/third_party/spirv-cross/shaders/asm/vert/invariant-block.asm.vert new file mode 100644 index 0000000..5984935 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/invariant-block.asm.vert @@ -0,0 +1,44 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpMemberDecorate %gl_PerVertex 0 Invariant + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %17 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/invariant-block.sso.asm.vert b/third_party/spirv-cross/shaders/asm/vert/invariant-block.sso.asm.vert new file mode 100644 index 0000000..5984935 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/invariant-block.sso.asm.vert @@ -0,0 +1,44 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 20 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_ + OpSource GLSL 450 + OpName %main "main" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpMemberName %gl_PerVertex 1 "gl_PointSize" + OpMemberName %gl_PerVertex 2 "gl_ClipDistance" + OpMemberName %gl_PerVertex 3 "gl_CullDistance" + OpName %_ "" + OpMemberDecorate %gl_PerVertex 0 Invariant + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize + OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance + OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float_1 = OpConstant %float 1 + %17 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %main = OpFunction %void None %3 + %5 = OpLabel + %19 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %19 %17 + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/invariant.asm.vert b/third_party/spirv-cross/shaders/asm/vert/invariant.asm.vert new file mode 100644 index 0000000..c0d381e --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/invariant.asm.vert @@ -0,0 +1,34 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 18 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %_entryPointOutput Invariant + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %float_1 = OpConstant %float 1 + %12 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %17 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + OpReturnValue %12 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/invariant.sso.asm.vert b/third_party/spirv-cross/shaders/asm/vert/invariant.sso.asm.vert new file mode 100644 index 0000000..c0d381e --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/invariant.sso.asm.vert @@ -0,0 +1,34 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 18 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %_entryPointOutput Invariant + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %float_1 = OpConstant %float 1 + %12 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %17 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + OpReturnValue %12 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert b/third_party/spirv-cross/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert new file mode 100644 index 0000000..b566a3d --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/spec-constant-op-composite.asm.vk.vert @@ -0,0 +1,98 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 1 +; Bound: 58 +; Schema: 0 + OpCapability Shader + OpCapability ClipDistance + OpCapability CullDistance + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" %52 %output + OpSource GLSL 450 + OpName %4 "main" + OpName %9 "pos" + OpName %50 "gl_PerVertex" + OpMemberName %50 0 "gl_Position" + OpMemberName %50 1 "gl_PointSize" + OpMemberName %50 2 "gl_ClipDistance" + OpMemberName %50 3 "gl_CullDistance" + OpName %52 "" + OpDecorate %13 SpecId 201 + OpDecorate %24 SpecId 202 + OpMemberDecorate %50 0 BuiltIn Position + OpMemberDecorate %50 1 BuiltIn PointSize + OpMemberDecorate %50 2 BuiltIn ClipDistance + OpMemberDecorate %50 3 BuiltIn CullDistance + OpDecorate %50 Block + OpDecorate %57 SpecId 200 + OpDecorate %output Flat + OpDecorate %output Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstantComposite %7 %10 %10 %10 %10 + %12 = OpTypeInt 32 1 + %int_ptr = OpTypePointer Output %12 + %13 = OpSpecConstant %12 -10 + %14 = OpConstant %12 2 + %15 = OpSpecConstantOp %12 IAdd %13 %14 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 1 + %19 = OpTypePointer Function %6 + %24 = OpSpecConstant %17 100 + %25 = OpConstant %17 5 + %26 = OpSpecConstantOp %17 UMod %24 %25 + %28 = OpConstant %17 2 + %33 = OpConstant %12 20 + %34 = OpConstant %12 30 + %35 = OpTypeVector %12 4 + %36 = OpSpecConstantComposite %35 %33 %34 %15 %15 + %40 = OpTypeVector %12 2 + %41 = OpSpecConstantOp %40 VectorShuffle %36 %36 1 0 + %foo = OpSpecConstantOp %12 CompositeExtract %36 1 + %42 = OpTypeVector %6 2 + %49 = OpTypeArray %6 %18 + %50 = OpTypeStruct %7 %6 %49 %49 + %51 = OpTypePointer Output %50 + %52 = OpVariable %51 Output + %output = OpVariable %int_ptr Output + %53 = OpConstant %12 0 + %55 = OpTypePointer Output %7 + %57 = OpSpecConstant %6 3.14159 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %11 + %16 = OpConvertSToF %6 %15 + %20 = OpAccessChain %19 %9 %18 + %21 = OpLoad %6 %20 + %22 = OpFAdd %6 %21 %16 + %23 = OpAccessChain %19 %9 %18 + OpStore %23 %22 + %27 = OpConvertUToF %6 %26 + %29 = OpAccessChain %19 %9 %28 + %30 = OpLoad %6 %29 + %31 = OpFAdd %6 %30 %27 + %32 = OpAccessChain %19 %9 %28 + OpStore %32 %31 + %37 = OpConvertSToF %7 %36 + %38 = OpLoad %7 %9 + %39 = OpFAdd %7 %38 %37 + OpStore %9 %39 + %43 = OpConvertSToF %42 %41 + %44 = OpLoad %7 %9 + %45 = OpVectorShuffle %42 %44 %44 0 1 + %46 = OpFAdd %42 %45 %43 + %47 = OpLoad %7 %9 + %48 = OpVectorShuffle %7 %47 %46 4 5 2 3 + OpStore %9 %48 + %54 = OpLoad %7 %9 + %56 = OpAccessChain %55 %52 %53 + OpStore %56 %54 + OpStore %output %foo + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert b/third_party/spirv-cross/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert new file mode 100644 index 0000000..29b0076 --- /dev/null +++ b/third_party/spirv-cross/shaders/asm/vert/uint-vertex-id-instance-id.asm.vert @@ -0,0 +1,65 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 6 +; Bound: 36 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %vid_1 %iid_1 %_entryPointOutput + OpSource HLSL 500 + OpName %main "main" + OpName %_main_u1_u1_ "@main(u1;u1;" + OpName %vid "vid" + OpName %iid "iid" + OpName %vid_0 "vid" + OpName %vid_1 "vid" + OpName %iid_0 "iid" + OpName %iid_1 "iid" + OpName %_entryPointOutput "@entryPointOutput" + OpName %param "param" + OpName %param_0 "param" + OpDecorate %vid_1 BuiltIn VertexIndex + OpDecorate %iid_1 BuiltIn InstanceIndex + OpDecorate %_entryPointOutput BuiltIn Position + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %10 = OpTypeFunction %v4float %_ptr_Function_uint %_ptr_Function_uint +%_ptr_Input_uint = OpTypePointer Input %uint + %vid_1 = OpVariable %_ptr_Input_uint Input + %iid_1 = OpVariable %_ptr_Input_uint Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %vid_0 = OpVariable %_ptr_Function_uint Function + %iid_0 = OpVariable %_ptr_Function_uint Function + %param = OpVariable %_ptr_Function_uint Function + %param_0 = OpVariable %_ptr_Function_uint Function + %25 = OpLoad %uint %vid_1 + OpStore %vid_0 %25 + %28 = OpLoad %uint %iid_1 + OpStore %iid_0 %28 + %32 = OpLoad %uint %vid_0 + OpStore %param %32 + %34 = OpLoad %uint %iid_0 + OpStore %param_0 %34 + %35 = OpFunctionCall %v4float %_main_u1_u1_ %param %param_0 + OpStore %_entryPointOutput %35 + OpReturn + OpFunctionEnd +%_main_u1_u1_ = OpFunction %v4float None %10 + %vid = OpFunctionParameter %_ptr_Function_uint + %iid = OpFunctionParameter %_ptr_Function_uint + %14 = OpLabel + %15 = OpLoad %uint %vid + %16 = OpLoad %uint %iid + %17 = OpIAdd %uint %15 %16 + %18 = OpConvertUToF %float %17 + %19 = OpCompositeConstruct %v4float %18 %18 %18 %18 + OpReturnValue %19 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/comp/atomic.comp b/third_party/spirv-cross/shaders/comp/atomic.comp new file mode 100644 index 0000000..703256d --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/atomic.comp @@ -0,0 +1,56 @@ +#version 310 es +#extension GL_OES_shader_image_atomic : require +layout(local_size_x = 1) in; + +layout(r32ui, binding = 0) uniform highp uimage2D uImage; +layout(r32i, binding = 1) uniform highp iimage2D iImage; +layout(binding = 2, std430) buffer SSBO +{ + uint u32; + int i32; +} ssbo; + +void main() +{ + imageAtomicAdd(uImage, ivec2(1, 5), 1u); + + // Test that we do not invalidate OpImage variables which are loaded from UniformConstant + // address space. + imageStore(iImage, ivec2(1, 6), ivec4(imageAtomicAdd(uImage, ivec2(1, 5), 1u))); + + imageAtomicOr(uImage, ivec2(1, 5), 1u); + imageAtomicXor(uImage, ivec2(1, 5), 1u); + imageAtomicAnd(uImage, ivec2(1, 5), 1u); + imageAtomicMin(uImage, ivec2(1, 5), 1u); + imageAtomicMax(uImage, ivec2(1, 5), 1u); + //imageAtomicExchange(uImage, ivec2(1, 5), 1u); + imageAtomicCompSwap(uImage, ivec2(1, 5), 10u, 2u); + + imageAtomicAdd(iImage, ivec2(1, 6), 1); + imageAtomicOr(iImage, ivec2(1, 6), 1); + imageAtomicXor(iImage, ivec2(1, 6), 1); + imageAtomicAnd(iImage, ivec2(1, 6), 1); + imageAtomicMin(iImage, ivec2(1, 6), 1); + imageAtomicMax(iImage, ivec2(1, 6), 1); + //imageAtomicExchange(iImage, ivec2(1, 5), 1u); + imageAtomicCompSwap(iImage, ivec2(1, 5), 10, 2); + + atomicAdd(ssbo.u32, 1u); + atomicOr(ssbo.u32, 1u); + atomicXor(ssbo.u32, 1u); + atomicAnd(ssbo.u32, 1u); + atomicMin(ssbo.u32, 1u); + atomicMax(ssbo.u32, 1u); + atomicExchange(ssbo.u32, 1u); + atomicCompSwap(ssbo.u32, 10u, 2u); + + atomicAdd(ssbo.i32, 1); + atomicOr(ssbo.i32, 1); + atomicXor(ssbo.i32, 1); + atomicAnd(ssbo.i32, 1); + atomicMin(ssbo.i32, 1); + atomicMax(ssbo.i32, 1); + atomicExchange(ssbo.i32, 1); + atomicCompSwap(ssbo.i32, 10, 2); +} + diff --git a/third_party/spirv-cross/shaders/comp/bake_gradient.comp b/third_party/spirv-cross/shaders/comp/bake_gradient.comp new file mode 100644 index 0000000..4885ff0 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/bake_gradient.comp @@ -0,0 +1,55 @@ +#version 310 es + +layout(local_size_x = 8, local_size_y = 8) in; + +layout(binding = 0) uniform sampler2D uHeight; +layout(binding = 1) uniform sampler2D uDisplacement; +layout(rgba16f, binding = 2) uniform writeonly mediump image2D iHeightDisplacement; +layout(rgba16f, binding = 3) uniform writeonly mediump image2D iGradJacobian; + +layout(binding = 4) uniform UBO +{ + vec4 uInvSize; + vec4 uScale; +}; + +mediump float jacobian(mediump vec2 dDdx, mediump vec2 dDdy) +{ + return (1.0 + dDdx.x) * (1.0 + dDdy.y) - dDdx.y * dDdy.x; +} +#define LAMBDA 1.2 + +void main() +{ + vec4 uv = (vec2(gl_GlobalInvocationID.xy) * uInvSize.xy).xyxy + 0.5 * uInvSize; + + float h = textureLod(uHeight, uv.xy, 0.0).x; + + // Compute the heightmap gradient by simple differentiation. + float x0 = textureLodOffset(uHeight, uv.xy, 0.0, ivec2(-1, 0)).x; + float x1 = textureLodOffset(uHeight, uv.xy, 0.0, ivec2(+1, 0)).x; + float y0 = textureLodOffset(uHeight, uv.xy, 0.0, ivec2(0, -1)).x; + float y1 = textureLodOffset(uHeight, uv.xy, 0.0, ivec2(0, +1)).x; + vec2 grad = uScale.xy * 0.5 * vec2(x1 - x0, y1 - y0); + + // Displacement map must be sampled with a different offset since it's a smaller texture. + vec2 displacement = LAMBDA * textureLod(uDisplacement, uv.zw, 0.0).xy; + + // Compute jacobian. + vec2 dDdx = 0.5 * LAMBDA * ( + textureLodOffset(uDisplacement, uv.zw, 0.0, ivec2(+1, 0)).xy - + textureLodOffset(uDisplacement, uv.zw, 0.0, ivec2(-1, 0)).xy); + vec2 dDdy = 0.5 * LAMBDA * ( + textureLodOffset(uDisplacement, uv.zw, 0.0, ivec2(0, +1)).xy - + textureLodOffset(uDisplacement, uv.zw, 0.0, ivec2(0, -1)).xy); + float j = jacobian(dDdx * uScale.z, dDdy * uScale.z); + + displacement = vec2(0.0); + + // Read by vertex shader/tess shader. + imageStore(iHeightDisplacement, ivec2(gl_GlobalInvocationID.xy), vec4(h, displacement, 0.0)); + + // Read by fragment shader. + imageStore(iGradJacobian, ivec2(gl_GlobalInvocationID.xy), vec4(grad, j, 0.0)); +} + diff --git a/third_party/spirv-cross/shaders/comp/barriers.comp b/third_party/spirv-cross/shaders/comp/barriers.comp new file mode 100644 index 0000000..7e0ea42 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/barriers.comp @@ -0,0 +1,79 @@ +#version 310 es +layout(local_size_x = 4) in; + +void barrier_shared() +{ + memoryBarrierShared(); +} + +void full_barrier() +{ + memoryBarrier(); +} + +void image_barrier() +{ + memoryBarrierImage(); +} + +void buffer_barrier() +{ + memoryBarrierBuffer(); +} + +void group_barrier() +{ + groupMemoryBarrier(); +} + +void barrier_shared_exec() +{ + memoryBarrierShared(); + barrier(); +} + +void full_barrier_exec() +{ + memoryBarrier(); + barrier(); +} + +void image_barrier_exec() +{ + memoryBarrierImage(); + barrier(); +} + +void buffer_barrier_exec() +{ + memoryBarrierBuffer(); + barrier(); +} + +void group_barrier_exec() +{ + groupMemoryBarrier(); + barrier(); +} + +void exec_barrier() +{ + barrier(); +} + +void main() +{ + barrier_shared(); + full_barrier(); + image_barrier(); + buffer_barrier(); + group_barrier(); + + barrier_shared_exec(); + full_barrier_exec(); + image_barrier_exec(); + buffer_barrier_exec(); + group_barrier_exec(); + + exec_barrier(); +} diff --git a/third_party/spirv-cross/shaders/comp/basic.comp b/third_party/spirv-cross/shaders/comp/basic.comp new file mode 100644 index 0000000..f9bf556 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/basic.comp @@ -0,0 +1,28 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +layout(std430, binding = 2) buffer SSBO3 +{ + uint counter; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idata = in_data[ident]; + if (dot(idata, vec4(1.0, 5.0, 6.0, 2.0)) > 8.2) + { + out_data[atomicAdd(counter, 1u)] = idata; + } +} + diff --git a/third_party/spirv-cross/shaders/comp/casts.comp b/third_party/spirv-cross/shaders/comp/casts.comp new file mode 100644 index 0000000..6be539d --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/casts.comp @@ -0,0 +1,18 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO0 +{ + ivec4 inputs[]; +}; + +layout(binding = 1, std430) buffer SSBO1 +{ + ivec4 outputs[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + outputs[ident] = ivec4(bvec4(inputs[ident] & 0x3)); +} diff --git a/third_party/spirv-cross/shaders/comp/cfg-preserve-parameter.comp b/third_party/spirv-cross/shaders/comp/cfg-preserve-parameter.comp new file mode 100644 index 0000000..9ef9092 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/cfg-preserve-parameter.comp @@ -0,0 +1,54 @@ +#version 310 es + +// We write in all paths (and no reads), so should just be out. +void out_test_0(int cond, inout int i) +{ + if (cond == 0) + i = 40; + else + i = 60; +} + +// We write in all paths (and no reads), so should just be out. +void out_test_1(int cond, inout int i) +{ + switch (cond) + { + case 40: + i = 40; + break; + + default: + i = 70; + break; + } +} + +// We don't write in all paths, so should be inout. +void inout_test_0(int cond, inout int i) +{ + if (cond == 0) + i = 40; +} + +void inout_test_1(int cond, inout int i) +{ + switch (cond) + { + case 40: + i = 40; + break; + } +} + + +void main() +{ + int cond = 40; + int i = 50; + + out_test_0(cond, i); + out_test_1(cond, i); + inout_test_0(cond, i); + inout_test_1(cond, i); +} diff --git a/third_party/spirv-cross/shaders/comp/cfg.comp b/third_party/spirv-cross/shaders/comp/cfg.comp new file mode 100644 index 0000000..4f4e6c0 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/cfg.comp @@ -0,0 +1,91 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float data; +}; + +void test() +{ + // Test that variables local to a scope stay local. + if (data != 0.0) + { + float tmp = 10.0; + data = tmp; + } + else + { + float tmp = 15.0; + data = tmp; + } + + // Test that variable access propagates up to dominator + if (data != 0.0) + { + float e; + if (data != 5.0) + { + if (data != 6.0) + e = 10.0; + } + else + e = 20.0; + } + + // Test that variables local to a switch block stay local. + switch (int(data)) + { + case 0: + { + float tmp = 20.0; + data = tmp; + break; + } + + case 1: + { + float tmp = 30.0; + data = tmp; + break; + } + } + + // Check that multibranches propagate up to dominator. + float f; + switch (int(data)) + { + case 0: + { + f = 30.0; + break; + } + + case 1: + { + f = 40.0; + break; + } + } + + // Check that loops work. + // Interesting case here is propagating variable access from the continue block. + float h; + for (int i = 0; i < 20; i++, h += 10.0) + ; + data = h; + + // Do the same with do-while, gotta test all the hard cases. + float m; + do + { + } while (m != 20.0); + data = m; +} + +void main() +{ + // Test that we do the CFG analysis for all functions. + test(); +} + diff --git a/third_party/spirv-cross/shaders/comp/coherent-block.comp b/third_party/spirv-cross/shaders/comp/coherent-block.comp new file mode 100644 index 0000000..0a174e8 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/coherent-block.comp @@ -0,0 +1,12 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 1) coherent restrict writeonly buffer SSBO +{ + vec4 value; +}; + +void main() +{ + value = vec4(20.0); +} diff --git a/third_party/spirv-cross/shaders/comp/coherent-image.comp b/third_party/spirv-cross/shaders/comp/coherent-image.comp new file mode 100644 index 0000000..fd6e280 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/coherent-image.comp @@ -0,0 +1,14 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 1) coherent restrict writeonly buffer SSBO +{ + ivec4 value; +}; + +layout(r32i, binding = 3) coherent readonly restrict uniform mediump iimage2D uImage; + +void main() +{ + value = imageLoad(uImage, ivec2(10)); +} diff --git a/third_party/spirv-cross/shaders/comp/composite-array-initialization.comp b/third_party/spirv-cross/shaders/comp/composite-array-initialization.comp new file mode 100644 index 0000000..fa9b611 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/composite-array-initialization.comp @@ -0,0 +1,29 @@ +#version 310 es +#extension GL_EXT_shader_non_constant_global_initializers : require +layout(local_size_x = 2) in; + +struct Data +{ + float a; + float b; +}; + +layout(std430, binding = 0) buffer SSBO +{ + Data outdata[]; +}; + +layout(constant_id = 0) const float X = 4.0; + +Data data[2] = Data[](Data(1.0, 2.0), Data(3.0, 4.0)); +Data data2[2] = Data[](Data(X, 2.0), Data(3.0, 5.0)); + +Data combine(Data a, Data b) +{ + return Data(a.a + b.a, a.b + b.b); +} + +void main() +{ + outdata[gl_WorkGroupID.x] = combine(data[gl_LocalInvocationID.x], data2[gl_LocalInvocationID.x]); +} diff --git a/third_party/spirv-cross/shaders/comp/composite-construct.comp b/third_party/spirv-cross/shaders/comp/composite-construct.comp new file mode 100644 index 0000000..859c56f --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/composite-construct.comp @@ -0,0 +1,40 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO0 +{ + vec4 as[]; +}; + +layout(std430, binding = 1) buffer SSBO1 +{ + vec4 bs[]; +}; + +vec4 summe(vec4 values[3][2]) +{ + return values[0][0] + values[2][1] + values[0][1] + values[1][0]; +} + +struct Composite +{ + vec4 a[2]; + vec4 b[2]; +}; + +void main() +{ + vec4 values[2] = vec4[](as[gl_GlobalInvocationID.x], bs[gl_GlobalInvocationID.x]); + vec4 const_values[2] = vec4[](vec4(10.0), vec4(30.0)); + vec4 copy_values[2]; + copy_values = const_values; + vec4 copy_values2[2] = values; + as[gl_GlobalInvocationID.x] = summe(vec4[][](values, copy_values, copy_values2)); + + Composite c = Composite(values, copy_values); + + float arrayofarray[2][3] = float[][](float[](1.0, 1.0, 1.0), float[](2.0, 2.0, 2.0)); + + float b = 10.0; + float values_scalar[4] = float[](b, b, b, b); +} diff --git a/third_party/spirv-cross/shaders/comp/culling.comp b/third_party/spirv-cross/shaders/comp/culling.comp new file mode 100644 index 0000000..9f8331b --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/culling.comp @@ -0,0 +1,26 @@ +#version 310 es +layout(local_size_x = 4) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + float in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + float out_data[]; +}; + +layout(std430, binding = 2) buffer SSBO3 +{ + uint count; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + float idata = in_data[ident]; + if (idata > 12.0) + out_data[atomicAdd(count, 1u)] = idata; +} + diff --git a/third_party/spirv-cross/shaders/comp/defer-parens.comp b/third_party/spirv-cross/shaders/comp/defer-parens.comp new file mode 100644 index 0000000..4e8ea6b --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/defer-parens.comp @@ -0,0 +1,30 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBO +{ + vec4 data; + int index; +}; + +void main() +{ + // Tests defer-parens behavior where a binary expression is OpCompositeExtracted chained together + // with an OpCompositeConstruct optimization. + vec4 d = data; + data = vec4(d.x, d.yz + 10.0, d.w); + + // Verify binary ops. + data = d + d + d; + + // Verify swizzles. + data = (d.yz + 10.0).xxyy; + + // OpCompositeExtract + float t = (d.yz + 10.0).y; + data = vec4(t); + + // OpVectorExtractDynamic + t = (d.zw + 10.0)[index]; + data = vec4(t); +} diff --git a/third_party/spirv-cross/shaders/comp/dowhile.comp b/third_party/spirv-cross/shaders/comp/dowhile.comp new file mode 100644 index 0000000..709db75 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/dowhile.comp @@ -0,0 +1,31 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +int i; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + + i = 0; + vec4 idat = in_data[ident]; + do + { + idat = mvp * idat; + i++; + } while(i < 16); + + out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/shaders/comp/generate_height.comp b/third_party/spirv-cross/shaders/comp/generate_height.comp new file mode 100644 index 0000000..16cef4d --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/generate_height.comp @@ -0,0 +1,97 @@ +#version 310 es + +layout(local_size_x = 64) in; + +layout(std430, binding = 0) readonly buffer Distribution +{ + vec2 distribution[]; +}; + +layout(std430, binding = 1) writeonly buffer HeightmapFFT +{ + uint heights[]; +}; + +layout(binding = 2, std140) uniform UBO +{ + vec4 uModTime; +}; + +vec2 alias(vec2 i, vec2 N) +{ + return mix(i, i - N, greaterThan(i, 0.5 * N)); +} + +vec4 cmul(vec4 a, vec4 b) +{ + vec4 r3 = a.yxwz; + vec4 r1 = b.xxzz; + vec4 R0 = a * r1; + vec4 r2 = b.yyww; + vec4 R1 = r2 * r3; + return R0 + vec4(-R1.x, R1.y, -R1.z, R1.w); +} + +vec2 cmul(vec2 a, vec2 b) +{ + vec2 r3 = a.yx; + vec2 r1 = b.xx; + vec2 R0 = a * r1; + vec2 r2 = b.yy; + vec2 R1 = r2 * r3; + return R0 + vec2(-R1.x, R1.y); +} + +uint pack2(vec2 v) +{ + return packHalf2x16(v); +} + +uvec2 pack4(vec4 v) +{ + return uvec2(packHalf2x16(v.xy), packHalf2x16(v.zw)); +} + +uvec2 workaround_mix(uvec2 a, uvec2 b, bvec2 sel) +{ + return uvec2(sel.x ? b.x : a.x, sel.y ? b.y : a.y); +} + +void generate_heightmap() +{ + uvec2 N = gl_WorkGroupSize.xy * gl_NumWorkGroups.xy; + uvec2 i = gl_GlobalInvocationID.xy; + // Pick out the negative frequency variant. + uvec2 wi = workaround_mix(N - i, uvec2(0u), equal(i, uvec2(0u))); + + // Pick out positive and negative travelling waves. + vec2 a = distribution[i.y * N.x + i.x]; + vec2 b = distribution[wi.y * N.x + wi.x]; + + vec2 k = uModTime.xy * alias(vec2(i), vec2(N)); + float k_len = length(k); + + const float G = 9.81; + + // If this sample runs for hours on end, the cosines of very large numbers will eventually become unstable. + // It is fairly easy to fix this by wrapping uTime, + // and quantizing w such that wrapping uTime does not change the result. + // See Tessendorf's paper for how to do it. + // The sqrt(G * k_len) factor represents how fast ocean waves at different frequencies propagate. + float w = sqrt(G * k_len) * uModTime.z; + float cw = cos(w); + float sw = sin(w); + + // Complex multiply to rotate our frequency samples. + a = cmul(a, vec2(cw, sw)); + b = cmul(b, vec2(cw, sw)); + b = vec2(b.x, -b.y); // Complex conjugate since we picked a frequency with the opposite direction. + vec2 res = a + b; // Sum up forward and backwards travelling waves. + heights[i.y * N.x + i.x] = pack2(res); +} + +void main() +{ + generate_heightmap(); +} + diff --git a/third_party/spirv-cross/shaders/comp/image.comp b/third_party/spirv-cross/shaders/comp/image.comp new file mode 100644 index 0000000..e375534 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/image.comp @@ -0,0 +1,12 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(rgba8, binding = 0) uniform readonly mediump image2D uImageIn; +layout(rgba8, binding = 1) uniform writeonly mediump image2D uImageOut; + +void main() +{ + vec4 v = imageLoad(uImageIn, ivec2(gl_GlobalInvocationID.xy) + imageSize(uImageIn)); + imageStore(uImageOut, ivec2(gl_GlobalInvocationID.xy), v); +} + diff --git a/third_party/spirv-cross/shaders/comp/insert.comp b/third_party/spirv-cross/shaders/comp/insert.comp new file mode 100644 index 0000000..07c1f8d --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/insert.comp @@ -0,0 +1,18 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) writeonly buffer SSBO +{ + vec4 out_data[]; +}; + +void main() +{ + vec4 v; + v.x = 10.0; + v.y = 30.0; + v.z = 70.0; + v.w = 90.0; + out_data[gl_GlobalInvocationID.x] = v; + out_data[gl_GlobalInvocationID.x].y = 20.0; +} diff --git a/third_party/spirv-cross/shaders/comp/mat3.comp b/third_party/spirv-cross/shaders/comp/mat3.comp new file mode 100644 index 0000000..7c5bb1e --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/mat3.comp @@ -0,0 +1,14 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + mat3 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + out_data[ident] = mat3(vec3(10.0), vec3(20.0), vec3(40.0)); +} + diff --git a/third_party/spirv-cross/shaders/comp/mod.comp b/third_party/spirv-cross/shaders/comp/mod.comp new file mode 100644 index 0000000..1631456 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/mod.comp @@ -0,0 +1,26 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 v = mod(in_data[ident], out_data[ident]); + out_data[ident] = v; + + uvec4 vu = floatBitsToUint(in_data[ident]) % floatBitsToUint(out_data[ident]); + out_data[ident] = uintBitsToFloat(vu); + + ivec4 vi = floatBitsToInt(in_data[ident]) % floatBitsToInt(out_data[ident]); + out_data[ident] = intBitsToFloat(vi); +} + diff --git a/third_party/spirv-cross/shaders/comp/modf.comp b/third_party/spirv-cross/shaders/comp/modf.comp new file mode 100644 index 0000000..edadefc --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/modf.comp @@ -0,0 +1,23 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 i; + //vec4 v = frexp(in_data[ident], i); + //out_data[ident] = ldexp(v, i); + vec4 v = modf(in_data[ident], i); + out_data[ident] = v; +} + diff --git a/third_party/spirv-cross/shaders/comp/outer-product.comp b/third_party/spirv-cross/shaders/comp/outer-product.comp new file mode 100644 index 0000000..9aba2a5 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/outer-product.comp @@ -0,0 +1,37 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(set = 0, binding = 0, std430) writeonly buffer SSBO +{ + mat2 m22; + mat2x3 m23; + mat2x4 m24; + mat3x2 m32; + mat3 m33; + mat3x4 m34; + mat4x2 m42; + mat4x3 m43; + mat4 m44; +}; + +layout(set = 0, binding = 1, std430) readonly buffer ReadSSBO +{ + vec2 v2; + vec3 v3; + vec4 v4; +}; + +void main() +{ + m22 = outerProduct(v2, v2); + m23 = outerProduct(v3, v2); + m24 = outerProduct(v4, v2); + + m32 = outerProduct(v2, v3); + m33 = outerProduct(v3, v3); + m34 = outerProduct(v4, v3); + + m42 = outerProduct(v2, v4); + m43 = outerProduct(v3, v4); + m44 = outerProduct(v4, v4); +} diff --git a/third_party/spirv-cross/shaders/comp/read-write-only.comp b/third_party/spirv-cross/shaders/comp/read-write-only.comp new file mode 100644 index 0000000..b224b6f --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/read-write-only.comp @@ -0,0 +1,26 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(binding = 0, std430) readonly buffer SSBO0 +{ + vec4 data0; + vec4 data1; +}; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + vec4 data2; + vec4 data3; +}; + +layout(binding = 2, std430) restrict writeonly buffer SSBO2 +{ + vec4 data4; + vec4 data5; +}; + +void main() +{ + data4 = data0 + data2; + data5 = data1 + data3; +} diff --git a/third_party/spirv-cross/shaders/comp/rmw-matrix.comp b/third_party/spirv-cross/shaders/comp/rmw-matrix.comp new file mode 100644 index 0000000..c158ab4 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/rmw-matrix.comp @@ -0,0 +1,20 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + float a; + vec4 b; + mat4 c; + + float a1; + vec4 b1; + mat4 c1; +}; + +void main() +{ + a *= a1; + b *= b1; + c *= c1; +} diff --git a/third_party/spirv-cross/shaders/comp/rmw-opt.comp b/third_party/spirv-cross/shaders/comp/rmw-opt.comp new file mode 100644 index 0000000..a6e1e7f --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/rmw-opt.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + int a; +}; + +void main() +{ + a += 10; + a -= 10; + a *= 10; + a /= 10; + a <<= 2; + a >>= 3; + a &= 40; + a ^= 10; + a %= 40; + a |= 1; + + bool c = false; + bool d = true; + c = c && d; + d = d || c; + a = c && d ? 1 : 0; +} diff --git a/third_party/spirv-cross/shaders/comp/scalar-std450-distance-length-normalize.comp b/third_party/spirv-cross/shaders/comp/scalar-std450-distance-length-normalize.comp new file mode 100644 index 0000000..3741473 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/scalar-std450-distance-length-normalize.comp @@ -0,0 +1,18 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(std430, set = 0, binding = 0) buffer SSBO +{ + float a; + float b; + float c; + float d; + float e; +}; + +void main() +{ + c = distance(a, b); + d = length(a); + e = normalize(a); +} diff --git a/third_party/spirv-cross/shaders/comp/shared.comp b/third_party/spirv-cross/shaders/comp/shared.comp new file mode 100644 index 0000000..4deff93 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/shared.comp @@ -0,0 +1,27 @@ +#version 310 es +layout(local_size_x = 4) in; + +shared float sShared[gl_WorkGroupSize.x]; + +layout(std430, binding = 0) readonly buffer SSBO +{ + float in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + float out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + float idata = in_data[ident]; + + sShared[gl_LocalInvocationIndex] = idata; + memoryBarrierShared(); + barrier(); + + out_data[ident] = sShared[gl_WorkGroupSize.x - gl_LocalInvocationIndex - 1u]; +} + diff --git a/third_party/spirv-cross/shaders/comp/ssbo-array-length.comp b/third_party/spirv-cross/shaders/comp/ssbo-array-length.comp new file mode 100644 index 0000000..3ad4b95 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/ssbo-array-length.comp @@ -0,0 +1,12 @@ +#version 450 +layout(local_size_x = 1) in; +layout(set = 0, binding = 1, std140) buffer SSBO +{ + uint size; + float v[]; +}; + +void main() +{ + size = v.length(); +} diff --git a/third_party/spirv-cross/shaders/comp/ssbo-array.comp b/third_party/spirv-cross/shaders/comp/ssbo-array.comp new file mode 100644 index 0000000..da0eae0 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/ssbo-array.comp @@ -0,0 +1,14 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + vec4 data[]; +} ssbos[2]; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + ssbos[1].data[ident] = ssbos[0].data[ident]; +} + diff --git a/third_party/spirv-cross/shaders/comp/struct-layout.comp b/third_party/spirv-cross/shaders/comp/struct-layout.comp new file mode 100644 index 0000000..5a2b780 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/struct-layout.comp @@ -0,0 +1,24 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct Foo +{ + mat4 m; +}; + +layout(std430, binding = 0) readonly buffer SSBO +{ + Foo in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + Foo out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + out_data[ident].m = in_data[ident].m * in_data[ident].m; +} + diff --git a/third_party/spirv-cross/shaders/comp/struct-packing.comp b/third_party/spirv-cross/shaders/comp/struct-packing.comp new file mode 100644 index 0000000..7a1be04 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/struct-packing.comp @@ -0,0 +1,86 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct S0 +{ + vec2 a[1]; + float b; +}; + +struct S1 +{ + vec3 a; + float b; +}; + +struct S2 +{ + vec3 a[1]; + float b; +}; + +struct S3 +{ + vec2 a; + float b; +}; + +struct S4 +{ + vec2 c; +}; + +struct Content +{ + S0 m0s[1]; + S1 m1s[1]; + S2 m2s[1]; + S0 m0; + S1 m1; + S2 m2; + S3 m3; + float m4; + + S4 m3s[8]; +}; + +layout(binding = 1, std430) restrict buffer SSBO1 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + float array[]; +} ssbo_430; + +layout(binding = 0, std140) restrict buffer SSBO0 +{ + Content content; + Content content1[2]; + Content content2; + + layout(column_major) mat2 m0; + layout(column_major) mat2 m1; + layout(column_major) mat2x3 m2[4]; + layout(column_major) mat3x2 m3; + layout(row_major) mat2 m4; + layout(row_major) mat2 m5[9]; + layout(row_major) mat2x3 m6[4][2]; + layout(row_major) mat3x2 m7; + + float array[]; +} ssbo_140; + +void main() +{ + ssbo_430.content = ssbo_140.content; +} + diff --git a/third_party/spirv-cross/shaders/comp/torture-loop.comp b/third_party/spirv-cross/shaders/comp/torture-loop.comp new file mode 100644 index 0000000..54a1221 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/torture-loop.comp @@ -0,0 +1,40 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) readonly buffer SSBO +{ + mat4 mvp; + vec4 in_data[]; +}; + +layout(std430, binding = 1) writeonly buffer SSBO2 +{ + vec4 out_data[]; +}; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + vec4 idat = in_data[ident]; + + int k = 0; + + // Continue with side effects. + while (++k < 10) + { + idat *= 2.0; + k++; + } + + // Again used here ... + for (uint i = 0u; i < 16u; i++, k++) + for (uint j = 0u; j < 30u; j++) + idat = mvp * idat; + + do + { + k++; + } while (k > 10); + out_data[ident] = idat; +} + diff --git a/third_party/spirv-cross/shaders/comp/type-alias.comp b/third_party/spirv-cross/shaders/comp/type-alias.comp new file mode 100644 index 0000000..343d350 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/type-alias.comp @@ -0,0 +1,45 @@ +#version 310 es +layout(local_size_x = 1) in; + +struct S0 +{ + vec4 a; +}; + +struct S1 +{ + vec4 a; +}; + +vec4 overload(S0 s0) +{ + return s0.a; +} + +vec4 overload(S1 s1) +{ + return s1.a; +} + +layout(std430, binding = 0) buffer SSBO0 +{ + S0 s0s[]; +}; + +layout(std430, binding = 1) buffer SSBO1 +{ + S1 s1s[]; +}; + +layout(std430, binding = 2) buffer SSBO2 +{ + vec4 outputs[]; +}; + + +void main() +{ + S0 s0 = s0s[gl_GlobalInvocationID.x]; + S1 s1 = s1s[gl_GlobalInvocationID.x]; + outputs[gl_GlobalInvocationID.x] = overload(s0) + overload(s1); +} diff --git a/third_party/spirv-cross/shaders/comp/udiv.comp b/third_party/spirv-cross/shaders/comp/udiv.comp new file mode 100644 index 0000000..33fe564 --- /dev/null +++ b/third_party/spirv-cross/shaders/comp/udiv.comp @@ -0,0 +1,17 @@ +#version 310 es +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer SSBO +{ + uint inputs[]; +}; + +layout(std430, binding = 0) buffer SSBO2 +{ + uint outputs[]; +}; + +void main() +{ + outputs[gl_GlobalInvocationID.x] = inputs[gl_GlobalInvocationID.x] / 29u; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/comp/enhanced-layouts.comp b/third_party/spirv-cross/shaders/desktop-only/comp/enhanced-layouts.comp new file mode 100644 index 0000000..470b73e --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/comp/enhanced-layouts.comp @@ -0,0 +1,39 @@ +#version 450 + +struct Foo +{ + int a; + int b; + int c; +}; + +layout(std140, binding = 0) uniform UBO +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ubo; + +layout(std140, binding = 1) buffer SSBO1 +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ssbo1; + +layout(std430, binding = 2) buffer SSBO2 +{ + layout(offset = 4) int a; + layout(offset = 8) int b; + layout(offset = 16) Foo foo; + layout(offset = 48) int c[8]; +} ssbo2; + +void main() +{ + ssbo1.a = ssbo2.a; + ssbo1.b = ubo.b; +} + diff --git a/third_party/spirv-cross/shaders/desktop-only/comp/extended-arithmetic.desktop.comp b/third_party/spirv-cross/shaders/desktop-only/comp/extended-arithmetic.desktop.comp new file mode 100644 index 0000000..9623751 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/comp/extended-arithmetic.desktop.comp @@ -0,0 +1,41 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(binding = 0, std430) buffer SSBOUint +{ + uint a, b, c, d; + uvec2 a2, b2, c2, d2; + uvec3 a3, b3, c3, d3; + uvec4 a4, b4, c4, d4; +} u; + +layout(binding = 1, std430) buffer SSBOInt +{ + int a, b, c, d; + ivec2 a2, b2, c2, d2; + ivec3 a3, b3, c3, d3; + ivec4 a4, b4, c4, d4; +} i; + +void main() +{ + u.c = uaddCarry(u.a, u.b, u.d); + u.c2 = uaddCarry(u.a2, u.b2, u.d2); + u.c3 = uaddCarry(u.a3, u.b3, u.d3); + u.c4 = uaddCarry(u.a4, u.b4, u.d4); + + u.c = usubBorrow(u.a, u.b, u.d); + u.c2 = usubBorrow(u.a2, u.b2, u.d2); + u.c3 = usubBorrow(u.a3, u.b3, u.d3); + u.c4 = usubBorrow(u.a4, u.b4, u.d4); + + umulExtended(u.a, u.b, u.c, u.d); + umulExtended(u.a2, u.b2, u.c2, u.d2); + umulExtended(u.a3, u.b3, u.c3, u.d3); + umulExtended(u.a4, u.b4, u.c4, u.d4); + + imulExtended(i.a, i.b, i.c, i.d); + imulExtended(i.a2, i.b2, i.c2, i.d2); + imulExtended(i.a3, i.b3, i.c3, i.d3); + imulExtended(i.a4, i.b4, i.c4, i.d4); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/comp/fp64.desktop.comp b/third_party/spirv-cross/shaders/desktop-only/comp/fp64.desktop.comp new file mode 100644 index 0000000..2c2d501 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/comp/fp64.desktop.comp @@ -0,0 +1,91 @@ +#version 450 +layout(local_size_x = 1) in; + +struct M0 +{ + double v; + dvec2 b[2]; + dmat2x3 c; + dmat3x2 d; +}; + +// Test buffer layout handling. +layout(std430, binding = 0) buffer SSBO0 +{ + dvec4 a; + M0 m0; + dmat4 b; +} ssbo_0; + +layout(std430, binding = 1) buffer SSBO1 +{ + dmat4 a; + dvec4 b; + M0 m0; +} ssbo_1; + +layout(std430, binding = 2) buffer SSBO2 +{ + double a[4]; + dvec2 b[4]; +} ssbo_2; + +layout(std140, binding = 3) buffer SSBO3 +{ + double a[4]; + dvec2 b[4]; +} ssbo_3; + +void main() +{ + ssbo_0.a += dvec4(10, 20, 30, 40); + ssbo_0.a += 20; + + dvec4 a = ssbo_0.a; + dmat4 amat = ssbo_0.b; + + ssbo_0.a = abs(a); + ssbo_0.a = sign(a); + ssbo_0.a = floor(a); + ssbo_0.a = trunc(a); + ssbo_0.a = round(a); + ssbo_0.a = roundEven(a); + ssbo_0.a = ceil(a); + ssbo_0.a = fract(a); + ssbo_0.a = mod(a, 20.0); + ssbo_0.a = mod(a, a); + ssbo_0.a = min(a, a); + ssbo_0.a = max(a, a); + ssbo_0.a = clamp(a, a, a); + ssbo_0.a = mix(a, a, a); + ssbo_0.a = step(a, a); + ssbo_0.a = smoothstep(a, a, a); + bvec4 b = isnan(a); + bvec4 c = isinf(a); + + double f = packDouble2x32(uvec2(10, 40)); + uvec2 g = unpackDouble2x32(f); + + double d = length(a); + d = distance(a, a); + d = dot(a, a); + dvec3 e = cross(a.xyz, a.yzw); + a = faceforward(a, a, a); + a = reflect(a, a); + //a = refract(a, a, 1.45); + + dmat4 l = matrixCompMult(amat, amat); + l = outerProduct(a, a); + l = transpose(l); + double m = determinant(l); + l = inverse(l); + + bvec4 k = lessThan(a, a); + k = lessThanEqual(a, a); + k = greaterThan(a, a); + k = greaterThanEqual(a, a); + + ssbo_1.b.x += 1.0lf; + ssbo_2.b[0].x += 1.0lf; + ssbo_3.b[0].x += 1.0lf; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp b/third_party/spirv-cross/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp new file mode 100644 index 0000000..5a70623 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/comp/image-formats.desktop.noeliminate.comp @@ -0,0 +1,48 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(rgba32f, binding = 0) uniform image2D uImg00; +layout(rgba16f, binding = 1) uniform image2D uImg01; +layout(rg32f, binding = 2) uniform image2D uImg02; +layout(rg16f, binding = 3) uniform image2D uImg03; +layout(r11f_g11f_b10f, binding = 4) uniform image2D uImg04; +layout(r32f, binding = 5) uniform image2D uImg05; +layout(r16f, binding = 6) uniform image2D uImg06; +layout(rgba16, binding = 7) uniform image2D uImg07; +layout(rgb10_a2, binding = 8) uniform image2D uImg08; +layout(rgba8, binding = 9) uniform image2D uImg09; +layout(rg16, binding = 10) uniform image2D uImg10; +layout(rg8, binding = 11) uniform image2D uImg11; +layout(r16, binding = 12) uniform image2D uImg12; +layout(r8, binding = 13) uniform image2D uImg13; +layout(rgba16_snorm, binding = 14) uniform image2D uImg14; +layout(rgba8_snorm, binding = 15) uniform image2D uImg15; +layout(rg16_snorm, binding = 16) uniform image2D uImg16; +layout(rg8_snorm, binding = 17) uniform image2D uImg17; +layout(r16_snorm, binding = 18) uniform image2D uImg18; +layout(r8_snorm, binding = 19) uniform image2D uImg19; + +layout(rgba32i, binding = 20) uniform iimage2D uImage20; +layout(rgba16i, binding = 21) uniform iimage2D uImage21; +layout(rgba8i, binding = 22) uniform iimage2D uImage22; +layout(rg32i, binding = 23) uniform iimage2D uImage23; +layout(rg16i, binding = 24) uniform iimage2D uImage24; +layout(rg8i, binding = 25) uniform iimage2D uImage25; +layout(r32i, binding = 26) uniform iimage2D uImage26; +layout(r16i, binding = 27) uniform iimage2D uImage27; +layout(r8i, binding = 28) uniform iimage2D uImage28; + +layout(rgba32ui, binding = 29) uniform uimage2D uImage29; +layout(rgba16ui, binding = 30) uniform uimage2D uImage30; +layout(rgb10_a2ui, binding = 31) uniform uimage2D uImage31; +layout(rgba8ui, binding = 32) uniform uimage2D uImage32; +layout(rg32ui, binding = 33) uniform uimage2D uImage33; +layout(rg16ui, binding = 34) uniform uimage2D uImage34; +layout(rg8ui, binding = 35) uniform uimage2D uImage35; +layout(r32ui, binding = 36) uniform uimage2D uImage36; +layout(r16ui, binding = 37) uniform uimage2D uImage37; +layout(r8ui, binding = 38) uniform uimage2D uImage38; + +void main() +{ +} diff --git a/third_party/spirv-cross/shaders/desktop-only/comp/int64.desktop.comp b/third_party/spirv-cross/shaders/desktop-only/comp/int64.desktop.comp new file mode 100644 index 0000000..81004d4 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/comp/int64.desktop.comp @@ -0,0 +1,55 @@ +#version 450 +#extension GL_ARB_gpu_shader_int64 : require +layout(local_size_x = 1) in; + +struct M0 +{ + int64_t v; + i64vec2 b[2]; + uint64_t c; + uint64_t d[5]; +}; + +// Test buffer layout handling. +layout(std430, binding = 0) buffer SSBO0 +{ + i64vec4 a; + M0 m0; +} ssbo_0; + +layout(std430, binding = 1) buffer SSBO1 +{ + u64vec4 b; + M0 m0; +} ssbo_1; + +layout(std430, binding = 2) buffer SSBO2 +{ + int64_t a[4]; + i64vec2 b[4]; +} ssbo_2; + +layout(std140, binding = 3) buffer SSBO3 +{ + int64_t a[4]; + i64vec2 b[4]; +} ssbo_3; + +void main() +{ + ssbo_0.a += i64vec4(10, 20, 30, 40); + ssbo_1.b += u64vec4(999999999999999999ul, 8888888888888888ul, 77777777777777777ul, 6666666666666666ul); + ssbo_0.a += 20; + ssbo_0.a = abs(ssbo_0.a + i64vec4(ssbo_1.b)); + + ssbo_0.a++; + ssbo_1.b++; + ssbo_0.a--; + ssbo_1.b--; + + ssbo_1.b = doubleBitsToUint64(int64BitsToDouble(ssbo_0.a)); + ssbo_0.a = doubleBitsToInt64(uint64BitsToDouble(ssbo_1.b)); + + ssbo_2.a[0] += 1l; + ssbo_3.a[0] += 2l; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/clip-cull-distance.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/clip-cull-distance.desktop.frag new file mode 100644 index 0000000..5212fd6 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/clip-cull-distance.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +in float gl_ClipDistance[4]; +in float gl_CullDistance[3]; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = gl_ClipDistance[0] + gl_CullDistance[0]; +} + diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag new file mode 100644 index 0000000..7c75ffe --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/control-dependent-in-branch.desktop.frag @@ -0,0 +1,36 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uSampler; +layout(location = 0) in vec4 vInput; + +void main() +{ + FragColor = vInput; + vec4 t = texture(uSampler, vInput.xy); + vec4 d0 = dFdx(vInput); + vec4 d1 = dFdy(vInput); + vec4 d2 = fwidth(vInput); + vec4 d3 = dFdxCoarse(vInput); + vec4 d4 = dFdyCoarse(vInput); + vec4 d5 = fwidthCoarse(vInput); + vec4 d6 = dFdxFine(vInput); + vec4 d7 = dFdyFine(vInput); + vec4 d8 = fwidthFine(vInput); + vec2 lod = textureQueryLod(uSampler, vInput.zw); + if (vInput.y > 10.0) + { + FragColor += t; + FragColor += d0; + FragColor += d1; + FragColor += d2; + FragColor += d3; + FragColor += d4; + FragColor += d5; + FragColor += d6; + FragColor += d7; + FragColor += d8; + FragColor += lod.xyxy; + } +} + diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/depth-greater-than.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/depth-greater-than.desktop.frag new file mode 100644 index 0000000..88f9a42 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/depth-greater-than.desktop.frag @@ -0,0 +1,8 @@ +#version 450 +layout(early_fragment_tests) in; +layout(depth_greater) out float gl_FragDepth; + +void main() +{ + gl_FragDepth = 0.5; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/depth-less-than.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/depth-less-than.desktop.frag new file mode 100644 index 0000000..87fdd46 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/depth-less-than.desktop.frag @@ -0,0 +1,8 @@ +#version 450 +layout(early_fragment_tests) in; +layout(depth_less) out float gl_FragDepth; + +void main() +{ + gl_FragDepth = 0.5; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/dual-source-blending.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/dual-source-blending.desktop.frag new file mode 100644 index 0000000..f322cf4 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/dual-source-blending.desktop.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0, index = 0) out vec4 FragColor0; +layout(location = 0, index = 1) out vec4 FragColor1; + +void main() +{ + FragColor0 = vec4(1.0); + FragColor1 = vec4(2.0); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag b/third_party/spirv-cross/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag new file mode 100644 index 0000000..1c6dd7b --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/hlsl-uav-block-alias.asm.frag @@ -0,0 +1,56 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 29 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %Foobar "Foobar" + OpMemberName %Foobar 0 "@data" + OpName %Foobar_0 "Foobar" + OpName %Foobaz "Foobaz" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %_runtimearr_v4float ArrayStride 16 + OpMemberDecorate %Foobar 0 Offset 0 + OpDecorate %Foobar BufferBlock + OpDecorate %Foobar_0 DescriptorSet 0 + OpDecorate %Foobar_0 Binding 0 + OpDecorate %Foobaz DescriptorSet 0 + OpDecorate %Foobaz Binding 1 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float +%_runtimearr_v4float = OpTypeRuntimeArray %v4float + %Foobar = OpTypeStruct %_runtimearr_v4float +%_ptr_Uniform_Foobar = OpTypePointer Uniform %Foobar + %Foobar_0 = OpVariable %_ptr_Uniform_Foobar Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %Foobaz = OpVariable %_ptr_Uniform_Foobar Uniform +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %28 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %28 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + %18 = OpAccessChain %_ptr_Uniform_v4float %Foobar_0 %int_0 %int_0 + %19 = OpLoad %v4float %18 + %21 = OpAccessChain %_ptr_Uniform_v4float %Foobaz %int_0 %int_0 + %22 = OpLoad %v4float %21 + %23 = OpFAdd %v4float %19 %22 + OpReturnValue %23 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/image-ms.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/image-ms.desktop.frag new file mode 100644 index 0000000..d3acc30 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/image-ms.desktop.frag @@ -0,0 +1,12 @@ +#version 450 + +layout(rgba8, binding = 0) uniform image2DMS uImage; +layout(rgba8, binding = 1) uniform image2DMSArray uImageArray; + +void main() +{ + vec4 a = imageLoad(uImage, ivec2(1, 2), 2); + vec4 b = imageLoad(uImageArray, ivec3(1, 2, 4), 3); + imageStore(uImage, ivec2(2, 3), 1, a); + imageStore(uImageArray, ivec3(2, 3, 7), 1, b); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/image-query.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/image-query.desktop.frag new file mode 100644 index 0000000..a5cbe01 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/image-query.desktop.frag @@ -0,0 +1,56 @@ +#version 450 + +layout(binding = 0) uniform sampler1D uSampler1D; +layout(binding = 1) uniform sampler2D uSampler2D; +layout(binding = 2) uniform sampler2DArray uSampler2DArray; +layout(binding = 3) uniform sampler3D uSampler3D; +layout(binding = 4) uniform samplerCube uSamplerCube; +layout(binding = 5) uniform samplerCubeArray uSamplerCubeArray; +layout(binding = 6) uniform samplerBuffer uSamplerBuffer; +layout(binding = 7) uniform sampler2DMS uSamplerMS; +layout(binding = 8) uniform sampler2DMSArray uSamplerMSArray; + +layout(r32f, binding = 9) uniform image1D uImage1D; +layout(r32f, binding = 10) uniform image2D uImage2D; +layout(r32f, binding = 11) uniform image2DArray uImage2DArray; +layout(r32f, binding = 12) uniform image3D uImage3D; +layout(r32f, binding = 13) uniform imageCube uImageCube; +layout(r32f, binding = 14) uniform imageCubeArray uImageCubeArray; +layout(r32f, binding = 15) uniform imageBuffer uImageBuffer; +layout(r32f, binding = 16) uniform image2DMS uImageMS; +layout(r32f, binding = 17) uniform image2DMSArray uImageMSArray; + +void main() +{ + int a = textureSize(uSampler1D, 0); + ivec2 b = textureSize(uSampler2D, 0); + ivec3 c = textureSize(uSampler2DArray, 0); + ivec3 d = textureSize(uSampler3D, 0); + ivec2 e = textureSize(uSamplerCube, 0); + ivec3 f = textureSize(uSamplerCubeArray, 0); + int g = textureSize(uSamplerBuffer); + ivec2 h = textureSize(uSamplerMS); + ivec3 i = textureSize(uSamplerMSArray); + + int l0 = textureQueryLevels(uSampler1D); + int l1 = textureQueryLevels(uSampler2D); + int l2 = textureQueryLevels(uSampler2DArray); + int l3 = textureQueryLevels(uSampler3D); + int l4 = textureQueryLevels(uSamplerCube); + int l5 = textureQueryLevels(uSamplerCubeArray); + + a = imageSize(uImage1D); + b = imageSize(uImage2D); + c = imageSize(uImage2DArray); + d = imageSize(uImage3D); + e = imageSize(uImageCube); + f = imageSize(uImageCubeArray); + g = imageSize(uImageBuffer); + h = imageSize(uImageMS); + i = imageSize(uImageMSArray); + + int s0 = textureSamples(uSamplerMS); + int s1 = textureSamples(uSamplerMSArray); + int s2 = imageSamples(uImageMS); + int s3 = imageSamples(uImageMSArray); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/image-size.frag b/third_party/spirv-cross/shaders/desktop-only/frag/image-size.frag new file mode 100644 index 0000000..ffd0bfa --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/image-size.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(r32f, set = 0, binding = 0) uniform image2D uImage1; +layout(r32f, set = 0, binding = 1) uniform image2D uImage2; + +void main() +{ + FragColor = vec4(imageSize(uImage1), imageSize(uImage2)); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag b/third_party/spirv-cross/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag new file mode 100644 index 0000000..ffd0bfa --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/image-size.no-qualifier-deduction.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(r32f, set = 0, binding = 0) uniform image2D uImage1; +layout(r32f, set = 0, binding = 1) uniform image2D uImage2; + +void main() +{ + FragColor = vec4(imageSize(uImage1), imageSize(uImage2)); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/in-block-qualifiers.frag b/third_party/spirv-cross/shaders/desktop-only/frag/in-block-qualifiers.frag new file mode 100644 index 0000000..f22096e --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/in-block-qualifiers.frag @@ -0,0 +1,20 @@ +#version 450 + +layout(location = 0) in VertexData { + flat float f; + centroid vec4 g; + flat int h; + float i; +} vin; + +layout(location = 4) in flat float f; +layout(location = 5) in centroid vec4 g; +layout(location = 6) in flat int h; +layout(location = 7) in sample float i; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vin.f + vin.g + float(vin.h) + vin.i + f + g + float(h) + i; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/layout-component.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/layout-component.desktop.frag new file mode 100644 index 0000000..fade67b --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/layout-component.desktop.frag @@ -0,0 +1,14 @@ +#version 450 +layout(location = 0, component = 0) in vec2 v0; +layout(location = 0, component = 2) in float v1; +layout(location = 0) out vec2 FragColor; + +in Vertex +{ + layout(location = 1, component = 2) in float v3; +}; + +void main() +{ + FragColor = v0 + v1 + v3; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/query-levels.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/query-levels.desktop.frag new file mode 100644 index 0000000..3a69776 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/query-levels.desktop.frag @@ -0,0 +1,9 @@ +#version 450 + +layout(binding = 0) uniform sampler2D uSampler; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(textureQueryLevels(uSampler)); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/query-lod.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/query-lod.desktop.frag new file mode 100644 index 0000000..0cb1604 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/query-lod.desktop.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) in vec2 vTexCoord; +layout(binding = 0) uniform sampler2D uSampler; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureQueryLod(uSampler, vTexCoord).xyxy; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/sampler-ms-query.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/sampler-ms-query.desktop.frag new file mode 100644 index 0000000..f707ed5 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/sampler-ms-query.desktop.frag @@ -0,0 +1,17 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2DMS uSampler; +layout(binding = 1) uniform sampler2DMSArray uSamplerArray; +layout(rgba8, binding = 2) uniform image2DMS uImage; +layout(rgba8, binding = 3) uniform image2DMSArray uImageArray; + +void main() +{ + FragColor = + vec4( + textureSamples(uSampler) + + textureSamples(uSamplerArray) + + imageSamples(uImage) + + imageSamples(uImageArray)); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/stencil-export.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/stencil-export.desktop.frag new file mode 100644 index 0000000..ebe753f --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/stencil-export.desktop.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_shader_stencil_export : require + +layout(location = 0) out vec4 MRT0; +layout(location = 1) out vec4 MRT1; +void main() +{ + MRT0 = vec4(1.0); + MRT1 = vec4(1.0, 0.0, 1.0, 1.0); + gl_FragStencilRefARB = 100; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag b/third_party/spirv-cross/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag new file mode 100644 index 0000000..0c4cf8f --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/frag/texture-proj-shadow.desktop.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(binding = 0) uniform sampler1DShadow uShadow1D; +layout(binding = 1) uniform sampler2DShadow uShadow2D; +layout(binding = 2) uniform sampler1D uSampler1D; +layout(binding = 3) uniform sampler2D uSampler2D; +layout(binding = 4) uniform sampler3D uSampler3D; + +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vClip3; +layout(location = 1) in vec4 vClip4; +layout(location = 2) in vec2 vClip2; + +void main() +{ + FragColor = textureProj(uShadow1D, vClip4); + FragColor = textureProj(uShadow2D, vClip4); + FragColor = textureProj(uSampler1D, vClip2).x; + FragColor = textureProj(uSampler2D, vClip3).x; + FragColor = textureProj(uSampler3D, vClip4).x; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/geom/basic.desktop.sso.geom b/third_party/spirv-cross/shaders/desktop-only/geom/basic.desktop.sso.geom new file mode 100644 index 0000000..f3d331d --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/geom/basic.desktop.sso.geom @@ -0,0 +1,37 @@ +#version 450 + +layout(triangles, invocations = 4) in; +layout(triangle_strip, max_vertices = 3) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(location = 0) in VertexData { + vec3 normal; +} vin[]; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal + float(gl_InvocationID); + EmitVertex(); + + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal + 4.0 * float(gl_InvocationID); + EmitVertex(); + + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal + 2.0 * float(gl_InvocationID); + EmitVertex(); + + EndPrimitive(); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/geom/viewport-index.desktop.geom b/third_party/spirv-cross/shaders/desktop-only/geom/viewport-index.desktop.geom new file mode 100644 index 0000000..e02e81d --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/geom/viewport-index.desktop.geom @@ -0,0 +1,11 @@ +#version 450 + +layout(triangles) in; +layout(triangle_strip) out; +layout(max_vertices = 4) out; + +void main() +{ + gl_ViewportIndex = 1; +} + diff --git a/third_party/spirv-cross/shaders/desktop-only/tesc/basic.desktop.sso.tesc b/third_party/spirv-cross/shaders/desktop-only/tesc/basic.desktop.sso.tesc new file mode 100644 index 0000000..8ff739b --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/tesc/basic.desktop.sso.tesc @@ -0,0 +1,28 @@ +#version 450 +layout(vertices = 1) out; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[gl_MaxPatchVertices]; + +out gl_PerVertex +{ + vec4 gl_Position; +} gl_out[1]; + +layout(location = 0) patch out vec3 vFoo; + + +void main() +{ + gl_TessLevelInner[0] = 8.9; + gl_TessLevelInner[1] = 6.9; + gl_TessLevelOuter[0] = 8.9; + gl_TessLevelOuter[1] = 6.9; + gl_TessLevelOuter[2] = 3.9; + gl_TessLevelOuter[3] = 4.9; + vFoo = vec3(1.0); + + gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/tese/triangle.desktop.sso.tese b/third_party/spirv-cross/shaders/desktop-only/tese/triangle.desktop.sso.tese new file mode 100644 index 0000000..c964fbe --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/tese/triangle.desktop.sso.tese @@ -0,0 +1,22 @@ +#version 450 + +layout(cw, triangles, fractional_even_spacing) in; + +in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[gl_MaxPatchVertices]; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = + gl_in[0].gl_Position * gl_TessCoord.x + + gl_in[1].gl_Position * gl_TessCoord.y + + gl_in[2].gl_Position * gl_TessCoord.z; +} + diff --git a/third_party/spirv-cross/shaders/desktop-only/vert/basic.desktop.sso.vert b/third_party/spirv-cross/shaders/desktop-only/vert/basic.desktop.sso.vert new file mode 100644 index 0000000..9ddab08 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/vert/basic.desktop.sso.vert @@ -0,0 +1,20 @@ +#version 450 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +layout(std140) uniform UBO +{ + mat4 uMVP; +}; +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert b/third_party/spirv-cross/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert new file mode 100644 index 0000000..1489cc7 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/vert/clip-cull-distance.desktop.sso.vert @@ -0,0 +1,13 @@ +#version 450 +out float gl_ClipDistance[4]; +out float gl_CullDistance[3]; + +void main() +{ + gl_Position = vec4(1.0); + gl_ClipDistance[0] = 0.0; + gl_ClipDistance[1] = 0.0; + gl_ClipDistance[2] = 0.0; + gl_ClipDistance[3] = 0.0; + gl_CullDistance[1] = 4.0; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/vert/clip-cull-distance.desktop.vert b/third_party/spirv-cross/shaders/desktop-only/vert/clip-cull-distance.desktop.vert new file mode 100644 index 0000000..1489cc7 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/vert/clip-cull-distance.desktop.vert @@ -0,0 +1,13 @@ +#version 450 +out float gl_ClipDistance[4]; +out float gl_CullDistance[3]; + +void main() +{ + gl_Position = vec4(1.0); + gl_ClipDistance[0] = 0.0; + gl_ClipDistance[1] = 0.0; + gl_ClipDistance[2] = 0.0; + gl_ClipDistance[3] = 0.0; + gl_CullDistance[1] = 4.0; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/vert/out-block-qualifiers.vert b/third_party/spirv-cross/shaders/desktop-only/vert/out-block-qualifiers.vert new file mode 100644 index 0000000..c1e409f --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/vert/out-block-qualifiers.vert @@ -0,0 +1,26 @@ +#version 450 + +layout(location = 0) out VertexData { + flat float f; + centroid vec4 g; + flat int h; + float i; +} vout; + +layout(location = 4) out flat float f; +layout(location = 5) out centroid vec4 g; +layout(location = 6) out flat int h; +layout(location = 7) out float i; + +void main() +{ + vout.f = 10.0; + vout.g = vec4(20.0); + vout.h = 20; + vout.i = 30.0; + + f = 10.0; + g = vec4(20.0); + h = 20; + i = 30.0; +} diff --git a/third_party/spirv-cross/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert b/third_party/spirv-cross/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert new file mode 100644 index 0000000..b5cde02 --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/vert/shader-draw-parameters-450.desktop.vk.vert @@ -0,0 +1,12 @@ +#version 450 +#extension GL_ARB_shader_draw_parameters : enable + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = vec4(gl_BaseVertexARB, gl_BaseInstanceARB, gl_DrawIDARB, 1); +} diff --git a/third_party/spirv-cross/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert b/third_party/spirv-cross/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert new file mode 100644 index 0000000..997804e --- /dev/null +++ b/third_party/spirv-cross/shaders/desktop-only/vert/shader-draw-parameters.desktop.vk.vert @@ -0,0 +1,11 @@ +#version 460 + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + gl_Position = vec4(gl_BaseVertex, gl_BaseInstance, gl_DrawID, 1); +} diff --git a/third_party/spirv-cross/shaders/flatten/array.flatten.vert b/third_party/spirv-cross/shaders/flatten/array.flatten.vert new file mode 100644 index 0000000..fa6da07 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/array.flatten.vert @@ -0,0 +1,19 @@ +#version 310 es + +layout(std140) uniform UBO +{ + vec4 A4[5][4][2]; + mat4 uMVP; + vec4 A1[2]; + vec4 A2[2][3]; + float A3[3]; + vec4 Offset; +}; +layout(location = 0) in vec4 aVertex; + +void main() +{ + vec4 a4 = A4[2][3][1]; // 2 * (4 * 2) + 3 * 2 + 1 = 16 + 6 + 1 = 23. + vec4 offset = A2[1][1] + A1[1] + A3[2]; + gl_Position = uMVP * aVertex + Offset + offset; +} diff --git a/third_party/spirv-cross/shaders/flatten/basic.flatten.vert b/third_party/spirv-cross/shaders/flatten/basic.flatten.vert new file mode 100644 index 0000000..e60a906 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/basic.flatten.vert @@ -0,0 +1,16 @@ +#version 310 es + +layout(std140) uniform UBO +{ + mat4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders/flatten/copy.flatten.vert b/third_party/spirv-cross/shaders/flatten/copy.flatten.vert new file mode 100644 index 0000000..4f1b880 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/copy.flatten.vert @@ -0,0 +1,34 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + + vec4 Color; +}; + +layout(std140) uniform UBO +{ + mat4 uMVP; + + Light lights[4]; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec4 vColor; + +void main() +{ + gl_Position = uMVP * aVertex; + + vColor = vec4(0.0); + + for (int i = 0; i < 4; ++i) + { + Light light = lights[i]; + vec3 L = aVertex.xyz - light.Position; + vColor += dot(aNormal, normalize(L)) * (clamp(1.0 - length(L) / light.Radius, 0.0, 1.0) * lights[i].Color); + } +} diff --git a/third_party/spirv-cross/shaders/flatten/dynamic.flatten.vert b/third_party/spirv-cross/shaders/flatten/dynamic.flatten.vert new file mode 100644 index 0000000..a341d45 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/dynamic.flatten.vert @@ -0,0 +1,33 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + + vec4 Color; +}; + +layout(std140) uniform UBO +{ + mat4 uMVP; + + Light lights[4]; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec4 vColor; + +void main() +{ + gl_Position = uMVP * aVertex; + + vColor = vec4(0.0); + + for (int i = 0; i < 4; ++i) + { + vec3 L = aVertex.xyz - lights[i].Position; + vColor += dot(aNormal, normalize(L)) * (clamp(1.0 - length(L) / lights[i].Radius, 0.0, 1.0) * lights[i].Color); + } +} diff --git a/third_party/spirv-cross/shaders/flatten/matrix-conversion.flatten.frag b/third_party/spirv-cross/shaders/flatten/matrix-conversion.flatten.frag new file mode 100644 index 0000000..427825c --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/matrix-conversion.flatten.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; +layout(location = 0) out vec3 FragColor; +layout(location = 0) flat in vec3 vNormal; + +layout(binding = 0, std140) uniform UBO +{ + mat4 m; +}; + +void main() +{ + FragColor = mat3(m) * vNormal; +} diff --git a/third_party/spirv-cross/shaders/flatten/matrixindex.flatten.vert b/third_party/spirv-cross/shaders/flatten/matrixindex.flatten.vert new file mode 100644 index 0000000..0ee7838 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/matrixindex.flatten.vert @@ -0,0 +1,25 @@ +#version 310 es + +layout(std140) uniform UBO +{ + layout(column_major) mat4 M1C; + layout(row_major) mat4 M1R; + layout(column_major) mat2x4 M2C; + layout(row_major) mat2x4 M2R; +}; + +layout(location = 0) out vec4 oA; +layout(location = 1) out vec4 oB; +layout(location = 2) out vec4 oC; +layout(location = 3) out vec4 oD; +layout(location = 4) out vec4 oE; + +void main() +{ + gl_Position = vec4(0.0); + oA = M1C[1]; + oB = M1R[1]; + oC = M2C[1]; + oD = M2R[0]; + oE = vec4(M1C[1][2], M1R[1][2], M2C[1][2], M2R[1][2]); +} diff --git a/third_party/spirv-cross/shaders/flatten/multiindex.flatten.vert b/third_party/spirv-cross/shaders/flatten/multiindex.flatten.vert new file mode 100644 index 0000000..0b471d8 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/multiindex.flatten.vert @@ -0,0 +1,13 @@ +#version 310 es + +layout(std140) uniform UBO +{ + vec4 Data[3][5]; +}; + +layout(location = 0) in ivec2 aIndex; + +void main() +{ + gl_Position = Data[aIndex.x][aIndex.y]; +} diff --git a/third_party/spirv-cross/shaders/flatten/push-constant.flatten.vert b/third_party/spirv-cross/shaders/flatten/push-constant.flatten.vert new file mode 100644 index 0000000..c7b1b42 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/push-constant.flatten.vert @@ -0,0 +1,17 @@ +#version 310 es + +layout(push_constant, std430) uniform PushMe +{ + mat4 MVP; + mat2 Rot; // The MatrixStride will be 8 here. + float Arr[4]; +} registers; + +layout(location = 0) in vec2 Rot; +layout(location = 1) in vec4 Pos; +layout(location = 0) out vec2 vRot; +void main() +{ + gl_Position = registers.MVP * Pos; + vRot = registers.Rot * Rot + registers.Arr[2]; // Constant access should work even if array stride is just 4 here. +} diff --git a/third_party/spirv-cross/shaders/flatten/rowmajor.flatten.vert b/third_party/spirv-cross/shaders/flatten/rowmajor.flatten.vert new file mode 100644 index 0000000..88c468c --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/rowmajor.flatten.vert @@ -0,0 +1,16 @@ +#version 310 es + +layout(std140) uniform UBO +{ + layout(column_major) mat4 uMVPR; + layout(row_major) mat4 uMVPC; + layout(row_major) mat2x4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; + +void main() +{ + vec2 v = aVertex * uMVP; + gl_Position = uMVPR * aVertex + uMVPC * aVertex; +} diff --git a/third_party/spirv-cross/shaders/flatten/struct.flatten.vert b/third_party/spirv-cross/shaders/flatten/struct.flatten.vert new file mode 100644 index 0000000..936bb41 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/struct.flatten.vert @@ -0,0 +1,30 @@ +#version 310 es + +struct Light +{ + vec3 Position; + float Radius; + + vec4 Color; +}; + +layout(std140) uniform UBO +{ + mat4 uMVP; + + Light light; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec4 vColor; + +void main() +{ + gl_Position = uMVP * aVertex; + + vColor = vec4(0.0); + + vec3 L = aVertex.xyz - light.Position; + vColor += dot(aNormal, normalize(L)) * (clamp(1.0 - length(L) / light.Radius, 0.0, 1.0) * light.Color); +} diff --git a/third_party/spirv-cross/shaders/flatten/struct.rowmajor.flatten.vert b/third_party/spirv-cross/shaders/flatten/struct.rowmajor.flatten.vert new file mode 100644 index 0000000..231389b --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/struct.rowmajor.flatten.vert @@ -0,0 +1,26 @@ +#version 310 es + +struct Foo +{ + mat3x4 MVP0; + mat3x4 MVP1; +}; + +layout(std140, binding = 0) uniform UBO +{ + layout(row_major) Foo foo; +}; + +layout(location = 0) in vec4 v0; +layout(location = 1) in vec4 v1; +layout(location = 0) out vec3 V0; +layout(location = 1) out vec3 V1; + +void main() +{ + Foo f = foo; + vec3 a = v0 * f.MVP0; + vec3 b = v1 * f.MVP1; + V0 = a; + V1 = b; +} diff --git a/third_party/spirv-cross/shaders/flatten/swizzle.flatten.vert b/third_party/spirv-cross/shaders/flatten/swizzle.flatten.vert new file mode 100644 index 0000000..fafff77 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/swizzle.flatten.vert @@ -0,0 +1,47 @@ +#version 310 es + +// comments note the 16b alignment boundaries (see GL spec 7.6.2.2 Standard Uniform Block Layout) +layout(std140, binding = 0) uniform UBO +{ + // 16b boundary + vec4 A; + // 16b boundary + vec2 B0; + vec2 B1; + // 16b boundary + float C0; + // 16b boundary (vec3 is aligned to 16b) + vec3 C1; + // 16b boundary + vec3 D0; + float D1; + // 16b boundary + float E0; + float E1; + float E2; + float E3; + // 16b boundary + float F0; + vec2 F1; + // 16b boundary (vec2 before us is aligned to 8b) + float F2; +}; + +layout(location = 0) out vec4 oA; +layout(location = 1) out vec4 oB; +layout(location = 2) out vec4 oC; +layout(location = 3) out vec4 oD; +layout(location = 4) out vec4 oE; +layout(location = 5) out vec4 oF; + +void main() +{ + gl_Position = vec4(0.0); + + oA = A; + oB = vec4(B0, B1); + oC = vec4(C0, C1); + oD = vec4(D0, D1); + oE = vec4(E0, E1, E2, E3); + oF = vec4(F0, F1, F2); +} diff --git a/third_party/spirv-cross/shaders/flatten/types.flatten.frag b/third_party/spirv-cross/shaders/flatten/types.flatten.frag new file mode 100644 index 0000000..faab5b7 --- /dev/null +++ b/third_party/spirv-cross/shaders/flatten/types.flatten.frag @@ -0,0 +1,27 @@ +#version 310 es +precision mediump float; + +layout(std140, binding = 0) uniform UBO0 +{ + vec4 a; + vec4 b; +}; + +layout(std140, binding = 0) uniform UBO1 +{ + ivec4 c; + ivec4 d; +}; + +layout(std140, binding = 0) uniform UBO2 +{ + uvec4 e; + uvec4 f; +}; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(c) + vec4(d) + vec4(e) + vec4(f) + a + b; +} diff --git a/third_party/spirv-cross/shaders/frag/array-lut-no-loop-variable.frag b/third_party/spirv-cross/shaders/frag/array-lut-no-loop-variable.frag new file mode 100644 index 0000000..3493e0c --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/array-lut-no-loop-variable.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 v0; + +void main() +{ + float lut[5] = float[](1.0, 2.0, 3.0, 4.0, 5.0); + for (int i = 0; i < 4; i++, FragColor += lut[i]) + { + } +} diff --git a/third_party/spirv-cross/shaders/frag/avoid-expression-lowering-to-loop.frag b/third_party/spirv-cross/shaders/frag/avoid-expression-lowering-to-loop.frag new file mode 100644 index 0000000..3473875 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/avoid-expression-lowering-to-loop.frag @@ -0,0 +1,23 @@ +#version 310 es +precision mediump float; +precision mediump int; + +layout(binding = 0) uniform mediump sampler2D tex; +layout(binding = 1) uniform Count +{ + float count; +}; + +layout(location = 0) in highp vec4 vertex; +layout(location = 0) out vec4 fragColor; + +void main() { + + highp float size = 1.0 / float(textureSize(tex, 0).x); + float r = 0.0; + float d = dFdx(vertex.x); + for (float i = 0.0; i < count ; i += 1.0) + r += size * d; + + fragColor = vec4(r); +} diff --git a/third_party/spirv-cross/shaders/frag/barycentric-nv.frag b/third_party/spirv-cross/shaders/frag/barycentric-nv.frag new file mode 100644 index 0000000..3404083 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/barycentric-nv.frag @@ -0,0 +1,18 @@ +#version 450 +#extension GL_NV_fragment_shader_barycentric : require + +layout(location = 0) out vec2 value; + +layout(set = 0, binding = 0) readonly buffer Vertices +{ + vec2 uvs[]; +}; + +void main () { + int prim = gl_PrimitiveID; + vec2 uv0 = uvs[3 * prim + 0]; + vec2 uv1 = uvs[3 * prim + 1]; + vec2 uv2 = uvs[3 * prim + 2]; + value = gl_BaryCoordNV.x * uv0 + gl_BaryCoordNV.y * uv1 + gl_BaryCoordNV.z * uv2; + value += gl_BaryCoordNoPerspNV.x * uv0 + gl_BaryCoordNoPerspNV.y * uv1 + gl_BaryCoordNoPerspNV.z * uv2; +} diff --git a/third_party/spirv-cross/shaders/frag/basic.frag b/third_party/spirv-cross/shaders/frag/basic.frag new file mode 100644 index 0000000..dd9a8f8 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/basic.frag @@ -0,0 +1,13 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor * texture(uTex, vTex); +} + diff --git a/third_party/spirv-cross/shaders/frag/complex-expression-in-access-chain.frag b/third_party/spirv-cross/shaders/frag/complex-expression-in-access-chain.frag new file mode 100644 index 0000000..47f9393 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/complex-expression-in-access-chain.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; + +struct Foo +{ + vec4 a; + vec4 b; +}; + +layout(binding = 0) buffer UBO +{ + vec4 results[1024]; +}; + +layout(binding = 1) uniform highp isampler2D Buf; +layout(location = 0) flat in int vIn; +layout(location = 1) flat in int vIn2; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + ivec4 coords = texelFetch(Buf, ivec2(gl_FragCoord.xy), 0); + vec4 foo = results[coords.x % 16]; + + int c = vIn * vIn; + int d = vIn2 * vIn2; + FragColor = foo + foo + results[c + d]; +} diff --git a/third_party/spirv-cross/shaders/frag/composite-extract-forced-temporary.frag b/third_party/spirv-cross/shaders/frag/composite-extract-forced-temporary.frag new file mode 100644 index 0000000..35fdbe8 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/composite-extract-forced-temporary.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; +layout(binding = 0) uniform sampler2D Texture; +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTexCoord; + +void main() +{ + float f = texture(Texture, vTexCoord).x; + FragColor = vec4(f * f); +} diff --git a/third_party/spirv-cross/shaders/frag/constant-array.frag b/third_party/spirv-cross/shaders/frag/constant-array.frag new file mode 100644 index 0000000..b862cb1 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/constant-array.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; +layout(location = 0) out vec4 FragColor; + +layout(location = 0) flat in int index; + +struct Foobar { float a; float b; }; + +vec4 resolve(Foobar f) +{ + return vec4(f.a + f.b); +} + +void main() +{ + const vec4 foo[3] = vec4[](vec4(1.0), vec4(2.0), vec4(3.0)); + const vec4 foobars[2][2] = vec4[][](vec4[](vec4(1.0), vec4(2.0)), vec4[](vec4(8.0), vec4(10.0))); + const Foobar foos[2] = Foobar[](Foobar(10.0, 40.0), Foobar(90.0, 70.0)); + + FragColor = foo[index] + foobars[index][index + 1] + resolve(Foobar(10.0, 20.0)) + resolve(foos[index]); +} diff --git a/third_party/spirv-cross/shaders/frag/constant-composites.frag b/third_party/spirv-cross/shaders/frag/constant-composites.frag new file mode 100644 index 0000000..a12e22f --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/constant-composites.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; + +float lut[4] = float[](1.0, 4.0, 3.0, 2.0); + +struct Foo +{ + float a; + float b; +}; +Foo foos[2] = Foo[](Foo(10.0, 20.0), Foo(30.0, 40.0)); + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in int line; + +void main() +{ + FragColor = vec4(lut[line]); + FragColor += foos[line].a * foos[1 - line].a; +} diff --git a/third_party/spirv-cross/shaders/frag/false-loop-init.frag b/third_party/spirv-cross/shaders/frag/false-loop-init.frag new file mode 100644 index 0000000..7ce5b52 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/false-loop-init.frag @@ -0,0 +1,19 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 accum; +layout(location = 0) out vec4 result; + +void main() +{ + result = vec4(0.0); + uint j; + for (int i = 0; i < 4; i += int(j)) + { + if (accum.y > 10.0) + j = 40u; + else + j = 30u; + result += accum; + } +} diff --git a/third_party/spirv-cross/shaders/frag/flush_params.frag b/third_party/spirv-cross/shaders/frag/flush_params.frag new file mode 100644 index 0000000..8a26ad3 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/flush_params.frag @@ -0,0 +1,27 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; + +struct Structy +{ + vec4 c; +}; + +void foo2(out Structy f) +{ + f.c = vec4(10.0); +} + +Structy foo() +{ + Structy f; + foo2(f); + return f; +} + +void main() +{ + Structy s = foo(); + FragColor = s.c; +} diff --git a/third_party/spirv-cross/shaders/frag/for-loop-continue-control-flow.frag b/third_party/spirv-cross/shaders/frag/for-loop-continue-control-flow.frag new file mode 100644 index 0000000..1f91cca --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/for-loop-continue-control-flow.frag @@ -0,0 +1,11 @@ +#version 450 +layout(location = 0) out vec4 FragColor; +void main() +{ + FragColor = vec4(0.0); + for (int i = 0; i < 3; (0 > 1) ? 1 : i ++) + { + int a = i; + FragColor[a] += float(i); + } +} diff --git a/third_party/spirv-cross/shaders/frag/for-loop-init.frag b/third_party/spirv-cross/shaders/frag/for-loop-init.frag new file mode 100644 index 0000000..0cde267 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/for-loop-init.frag @@ -0,0 +1,52 @@ +#version 310 es +precision mediump float; +layout(location = 0) out int FragColor; + +void main() +{ + FragColor = 16; + + // Basic loop variable. + for (int i = 0; i < 25; i++) + FragColor += 10; + + // Multiple loop variables. + for (int i = 1, j = 4; i < 30; i++, j += 4) + FragColor += 11; + + // A potential loop variables, but we access it outside the loop, + // so cannot be one. + int k = 0; + for (; k < 20; k++) + FragColor += 12; + k += 3; + FragColor += k; + + // Potential loop variables, but the dominator is not trivial. + int l; + if (k == 40) + { + for (l = 0; l < 40; l++) + FragColor += 13; + return; + } + else + { + l = k; + FragColor += l; + } + + // Vectors cannot be loop variables + for (ivec2 i = ivec2(0); i.x < 10; i.x += 4) + { + FragColor += i.y; + } + + // Check that static expressions can be used before the loop header. + int m = 0; + m = k; + int o = m; + for (; m < 40; m++) + FragColor += m; + FragColor += o; +} diff --git a/third_party/spirv-cross/shaders/frag/frexp-modf.frag b/third_party/spirv-cross/shaders/frag/frexp-modf.frag new file mode 100644 index 0000000..6a26a41 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/frexp-modf.frag @@ -0,0 +1,24 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out float FragColor; +layout(location = 0) in float v0; +layout(location = 1) in vec2 v1; + +void main() +{ + int e0; + float f0 = frexp(v0, e0); + f0 = frexp(v0 + 1.0, e0); + + ivec2 e1; + vec2 f1 = frexp(v1, e1); + + float r0; + float m0 = modf(v0, r0); + vec2 r1; + vec2 m1 = modf(v1, r1); + + FragColor = f0 + f1.x + f1.y + m0 + m1.x + m1.y; +} + diff --git a/third_party/spirv-cross/shaders/frag/front-facing.frag b/third_party/spirv-cross/shaders/frag/front-facing.frag new file mode 100644 index 0000000..90ca1ab --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/front-facing.frag @@ -0,0 +1,14 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; + +void main() +{ + if (gl_FrontFacing) + FragColor = vA; + else + FragColor = vB; +} diff --git a/third_party/spirv-cross/shaders/frag/gather-dref.frag b/third_party/spirv-cross/shaders/frag/gather-dref.frag new file mode 100644 index 0000000..a8aac56 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/gather-dref.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(binding = 0) uniform mediump sampler2DShadow uT; +layout(location = 0) in vec3 vUV; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureGather(uT, vUV.xy, vUV.z); +} diff --git a/third_party/spirv-cross/shaders/frag/ground.frag b/third_party/spirv-cross/shaders/frag/ground.frag new file mode 100755 index 0000000..d1fcfd4 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/ground.frag @@ -0,0 +1,162 @@ +#version 310 es +precision mediump float; + +#define DEBUG_NONE 0 +#define DEBUG_DIFFUSE 1 +#define DEBUG_SPECULAR 2 +#define DEBUG_LIGHTING 3 +#define DEBUG_FOG 4 +#define DEBUG DEBUG_NONE + +#define FORWARD 0 +#define DEFERRED 1 +#define DEFERRED_VTEX 2 + +float saturate(float x) { return clamp(x, 0.0, 1.0); } + +layout(std140, binding = 4) uniform GlobalPSData +{ + vec4 g_CamPos; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_ResolutionParams; + vec4 g_TimeParams; + vec4 g_FogColor_Distance; +}; + +vec4 ComputeFogFactor(vec3 WorldPos) +{ + vec4 FogData; + vec3 vEye = WorldPos - g_CamPos.xyz; + vec3 nEye = normalize(vEye); + FogData.w = exp(-dot(vEye, vEye) * g_FogColor_Distance.w * 0.75); + + float fog_sun_factor = pow(saturate(dot(nEye, g_SunDir.xyz)), 8.0); + FogData.xyz = mix(vec3(1.0, 1.0, 1.0), vec3(0.6, 0.6, 0.9), nEye.y * 0.5 + 0.5); + FogData.xyz = mix(FogData.xyz, vec3(0.95, 0.87, 0.78), fog_sun_factor); + return FogData; +} + +void ApplyFog(inout vec3 Color, vec4 FogData) +{ + Color = mix(FogData.xyz, Color, FogData.w); +} + +void ApplyLighting(inout mediump vec3 Color, mediump float DiffuseFactor) +{ + mediump vec3 DiffuseLight = g_SunColor.xyz * DiffuseFactor; + mediump vec3 AmbientLight = vec3(0.2, 0.35, 0.55) * 0.5; + mediump vec3 Lighting = DiffuseLight + AmbientLight; +#if DEBUG == DEBUG_LIGHTING + Color = Lighting; +#else + Color *= Lighting; +#endif +} + +#define SPECULAR 0 +#define GLOSSMAP 0 + +void ApplySpecular(inout mediump vec3 Color, mediump vec3 EyeVec, mediump vec3 Normal, mediump vec3 SpecularColor, mediump float Shininess, mediump float FresnelAmount) +{ + mediump vec3 HalfAngle = normalize(-EyeVec + g_SunDir.xyz); + + mediump float v_dot_h = saturate(dot(HalfAngle, -EyeVec)); + mediump float n_dot_l = saturate(dot(Normal, g_SunDir.xyz)); + mediump float n_dot_h = saturate(dot(Normal, HalfAngle)); + mediump float n_dot_v = saturate(dot(-EyeVec, Normal)); + mediump float h_dot_l = saturate(dot(g_SunDir.xyz, HalfAngle)); + + const mediump float roughness_value = 0.25; + + mediump float r_sq = roughness_value * roughness_value; + mediump float n_dot_h_sq = n_dot_h * n_dot_h; + mediump float roughness_a = 1.0 / (4.0 * r_sq * n_dot_h_sq * n_dot_h_sq); + mediump float roughness_b = n_dot_h_sq - 1.0; + mediump float roughness_c = r_sq * n_dot_h_sq; + mediump float roughness = saturate(roughness_a * exp(roughness_b / roughness_c)); + + FresnelAmount = 0.5; + mediump float fresnel_term = pow(1.0 - n_dot_v, 5.0) * (1.0 - FresnelAmount) + FresnelAmount; + + mediump float geo_numerator = 2.0 * n_dot_h; + mediump float geo_denominator = 1.0 / v_dot_h; + mediump float geo_term = min(1.0, min(n_dot_v, n_dot_l) * geo_numerator * geo_denominator); + +#if SPECULAR || GLOSSMAP + Color += SpecularColor * g_SunColor.xyz * fresnel_term * roughness * n_dot_l * geo_term / (n_dot_v * n_dot_l + 0.0001); +#endif + + //Color = vec3(0.025 * 1.0 / (n_dot_v * n_dot_l)); +} +layout(location = 0) in vec2 TexCoord; +layout(location = 1) in vec3 EyeVec; + +layout(binding = 2) uniform sampler2D TexNormalmap; +//layout(binding = 3) uniform sampler2D TexScatteringLUT; + +#define DIFFUSE_ONLY 0 +#define GLOBAL_RENDERER DEFERRED +#define OUTPUT_FEEDBACK_TEXTURE 0 + +#if DIFFUSE_ONLY +layout(location = 0) out vec4 ColorOut; +layout(location = 1) out vec4 NormalOut; +#else +layout(location = 0) out vec4 AlbedoOut; +layout(location = 1) out vec4 SpecularOut; +layout(location = 2) out vec4 NormalOut; +layout(location = 3) out vec4 LightingOut; +#endif + +void Resolve(vec3 Albedo, vec3 Normal, float Roughness, float Metallic) +{ +#if (GLOBAL_RENDERER == FORWARD) || OUTPUT_FEEDBACK_TEXTURE + float Lighting = saturate(dot(Normal, normalize(vec3(1.0, 0.5, 1.0)))); + ColorOut.xyz = Albedo * Lighting; + ColorOut.w = 1.0; +#elif DIFFUSE_ONLY + ColorOut = vec4(Albedo, 0.0); + NormalOut.xyz = Normal * 0.5 + 0.5; + NormalOut.w = 1.0; + + // linearize and map to 0..255 range + ColorOut.w = -0.003921569 / (gl_FragCoord.z - 1.003921569); + ColorOut.w = log2(1.0 + saturate(length(EyeVec.xyz) / 200.0)); + ColorOut.w -= 1.0 / 255.0; +#else + LightingOut = vec4(0.0); + NormalOut = vec4(Normal * 0.5 + 0.5, 0.0); + SpecularOut = vec4(Roughness, Metallic, 0.0, 0.0); + AlbedoOut = vec4(Albedo, 1.0); +#endif +} + +void main() +{ + vec3 Normal = texture(TexNormalmap, TexCoord).xyz * 2.0 - 1.0; + Normal = normalize(Normal); + + vec2 scatter_uv; + scatter_uv.x = saturate(length(EyeVec) / 1000.0); + + vec3 nEye = normalize(EyeVec); + scatter_uv.y = 0.0; //nEye.x * 0.5 + 0.5; + + vec3 Color = vec3(0.1, 0.3, 0.1); + vec3 grass = vec3(0.1, 0.3, 0.1); + vec3 dirt = vec3(0.1, 0.1, 0.1); + vec3 snow = vec3(0.8, 0.8, 0.8); + + float grass_snow = smoothstep(0.0, 0.15, (g_CamPos.y + EyeVec.y) / 200.0); + vec3 base = mix(grass, snow, grass_snow); + + float edge = smoothstep(0.7, 0.75, Normal.y); + Color = mix(dirt, base, edge); + Color *= Color; + + float Roughness = 1.0 - edge * grass_snow; + + Resolve(Color, Normal, Roughness, 0.0); +} + diff --git a/third_party/spirv-cross/shaders/frag/helper-invocation.frag b/third_party/spirv-cross/shaders/frag/helper-invocation.frag new file mode 100644 index 0000000..1da8c57 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/helper-invocation.frag @@ -0,0 +1,21 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vUV; +layout(binding = 0) uniform sampler2D uSampler; + +vec4 foo() +{ + vec4 color; + if (!gl_HelperInvocation) + color = textureLod(uSampler, vUV, 0.0); + else + color = vec4(1.0); + return color; +} + +void main() +{ + FragColor = foo(); +} diff --git a/third_party/spirv-cross/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag b/third_party/spirv-cross/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag new file mode 100644 index 0000000..cc8a648 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/hoisted-temporary-use-continue-block-as-value.frag @@ -0,0 +1,24 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in int vA; +layout(location = 1) flat in int vB; + +void main() +{ + FragColor = vec4(0.0); + + int k = 0; + int j; + for (int i = 0; i < vA; i += j) + { + if ((vA + i) == 20) + k = 50; + else if ((vB + i) == 40) + k = 60; + + j = k + 10; + FragColor += 1.0; + } +} diff --git a/third_party/spirv-cross/shaders/frag/image-load-store-uint-coord.asm.frag b/third_party/spirv-cross/shaders/frag/image-load-store-uint-coord.asm.frag new file mode 100644 index 0000000..a9bf1a7 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/image-load-store-uint-coord.asm.frag @@ -0,0 +1,103 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 2 +; Bound: 63 +; Schema: 0 + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_entryPointOutput + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + OpName %_main_ "@main(" + OpName %storeTemp "storeTemp" + OpName %RWIm "RWIm" + OpName %v "v" + OpName %RWBuf "RWBuf" + OpName %ROIm "ROIm" + OpName %ROBuf "ROBuf" + OpName %_entryPointOutput "@entryPointOutput" + OpDecorate %RWIm DescriptorSet 0 + OpDecorate %RWIm Binding 1 + OpDecorate %RWBuf DescriptorSet 0 + OpDecorate %RWBuf Binding 0 + OpDecorate %ROIm DescriptorSet 0 + OpDecorate %ROIm Binding 1 + OpDecorate %ROBuf DescriptorSet 0 + OpDecorate %ROBuf Binding 0 + OpDecorate %_entryPointOutput Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float + %float_10 = OpConstant %float 10 + %float_0_5 = OpConstant %float 0.5 + %float_8 = OpConstant %float 8 + %float_2 = OpConstant %float 2 + %17 = OpConstantComposite %v4float %float_10 %float_0_5 %float_8 %float_2 + %18 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 + %RWIm = OpVariable %_ptr_UniformConstant_18 UniformConstant + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %uint_10 = OpConstant %uint 10 + %25 = OpConstantComposite %v2uint %uint_10 %uint_10 + %uint_30 = OpConstant %uint 30 + %30 = OpConstantComposite %v2uint %uint_30 %uint_30 + %32 = OpTypeImage %float Buffer 0 0 0 2 Rgba32f +%_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32 + %RWBuf = OpVariable %_ptr_UniformConstant_32 UniformConstant + %uint_80 = OpConstant %uint 80 + %38 = OpTypeImage %float 2D 0 0 0 1 Unknown + %SampledImage = OpTypeSampledImage %38 +%_ptr_UniformConstant_38 = OpTypePointer UniformConstant %SampledImage + %ROIm = OpVariable %_ptr_UniformConstant_38 UniformConstant + %uint_50 = OpConstant %uint 50 + %uint_60 = OpConstant %uint 60 + %44 = OpConstantComposite %v2uint %uint_50 %uint_60 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %50 = OpTypeImage %float Buffer 0 0 0 1 Rgba32f +%_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50 + %ROBuf = OpVariable %_ptr_UniformConstant_50 UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %62 = OpFunctionCall %v4float %_main_ + OpStore %_entryPointOutput %62 + OpReturn + OpFunctionEnd + %_main_ = OpFunction %v4float None %8 + %10 = OpLabel + %storeTemp = OpVariable %_ptr_Function_v4float Function + %v = OpVariable %_ptr_Function_v4float Function + OpStore %storeTemp %17 + %21 = OpLoad %18 %RWIm + %26 = OpLoad %v4float %storeTemp + OpImageWrite %21 %25 %26 + %28 = OpLoad %18 %RWIm + %31 = OpImageRead %v4float %28 %30 + OpStore %v %31 + %35 = OpLoad %32 %RWBuf + %37 = OpLoad %v4float %v + OpImageWrite %35 %uint_80 %37 + %41 = OpLoad %SampledImage %ROIm + %ROImage = OpImage %38 %41 + %47 = OpImageFetch %v4float %ROImage %44 Lod %int_0 + %48 = OpLoad %v4float %v + %49 = OpFAdd %v4float %48 %47 + OpStore %v %49 + %53 = OpLoad %50 %ROBuf + %54 = OpImageFetch %v4float %53 %uint_80 + %55 = OpLoad %v4float %v + %56 = OpFAdd %v4float %55 %54 + OpStore %v %56 + %57 = OpLoad %v4float %v + OpReturnValue %57 + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/frag/inside-loop-dominated-variable-preservation.frag b/third_party/spirv-cross/shaders/frag/inside-loop-dominated-variable-preservation.frag new file mode 100644 index 0000000..695bcae --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/inside-loop-dominated-variable-preservation.frag @@ -0,0 +1,24 @@ +#version 310 es +precision mediump float; +layout(location = 0) out vec4 FragColor; + +void main() +{ + float v; + bool written = false; + for (int j = 0; j < 10; j++) + { + for (int i = 0; i < 4; i++) + { + float w = 0.0; + if (written) + w += v; + else + v = 20.0; + + v += float(i); + written = true; + } + } + FragColor = vec4(1.0); +} diff --git a/third_party/spirv-cross/shaders/frag/loop-dominator-and-switch-default.frag b/third_party/spirv-cross/shaders/frag/loop-dominator-and-switch-default.frag new file mode 100644 index 0000000..344d895 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/loop-dominator-and-switch-default.frag @@ -0,0 +1,34 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 fragColor; + +void main() +{ + vec4 f4; + int c = int(f4.x); + + for (int j = 0; j < c; j++) + { + switch (c) + { + case 0: + f4.y = 0.0; + break; + case 1: + f4.y = 1.0; + break; + default: + { + int i = 0; + while (i++ < c) { + f4.y += 0.5; + } + continue; + } + } + f4.y += 0.5; + } + + fragColor = f4; +} diff --git a/third_party/spirv-cross/shaders/frag/lut-promotion.frag b/third_party/spirv-cross/shaders/frag/lut-promotion.frag new file mode 100644 index 0000000..0cdc814 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/lut-promotion.frag @@ -0,0 +1,44 @@ +#version 310 es +precision mediump float; +layout(location = 0) out float FragColor; +layout(location = 0) flat in int index; + +const float LUT[16] = float[]( + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0, + 1.0, 2.0, 3.0, 4.0); + +void main() +{ + // Try reading LUTs, both in branches and not branch. + FragColor = LUT[index]; + if (index < 10) + FragColor += LUT[index ^ 1]; + else + FragColor += LUT[index & 1]; + + // Not declared as a LUT, but can be promoted to one. + vec4 foo[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + FragColor += foo[index & 3].y; + } + else + { + FragColor += foo[index & 1].x; + } + + // Not declared as a LUT, but this cannot be promoted, because we have a partial write. + vec4 foobar[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + if (index > 30) + { + foobar[1].z = 20.0; + } + FragColor += foobar[index & 3].z; + + // Not declared as a LUT, but this cannot be promoted, because we have two complete writes. + vec4 baz[4] = vec4[](vec4(0.0), vec4(1.0), vec4(8.0), vec4(5.0)); + baz = vec4[](vec4(20.0), vec4(30.0), vec4(50.0), vec4(60.0)); + FragColor += baz[index & 3].z; +} diff --git a/third_party/spirv-cross/shaders/frag/mix.frag b/third_party/spirv-cross/shaders/frag/mix.frag new file mode 100644 index 0000000..a5d589d --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/mix.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vIn0; +layout(location = 1) in vec4 vIn1; +layout(location = 2) in float vIn2; +layout(location = 3) in float vIn3; +layout(location = 0) out vec4 FragColor; + +void main() +{ + bvec4 l = bvec4(false, true, false, false); + FragColor = mix(vIn0, vIn1, l); + + bool f = true; + FragColor = vec4(mix(vIn2, vIn3, f)); + + FragColor = f ? vIn0 : vIn1; + FragColor = vec4(f ? vIn2 : vIn3); +} diff --git a/third_party/spirv-cross/shaders/frag/partial-write-preserve.frag b/third_party/spirv-cross/shaders/frag/partial-write-preserve.frag new file mode 100644 index 0000000..227df95 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/partial-write-preserve.frag @@ -0,0 +1,95 @@ +#version 310 es +precision mediump float; + +layout(std140, binding = 0) uniform UBO +{ + int some_value; +}; + +struct B +{ + float a; + float b; +}; + +void partial_inout(inout vec4 x) +{ + x.x = 10.0; +} + +void partial_inout(inout B b) +{ + b.b = 40.0; +} + +// Make a complete write, but only conditionally ... +void branchy_inout(inout vec4 v) +{ + v.y = 20.0; + if (some_value == 20) + { + v = vec4(50.0); + } +} + +void branchy_inout(inout B b) +{ + b.b = 20.0; + if (some_value == 20) + { + b = B(10.0, 40.0); + } +} + +void branchy_inout_2(out vec4 v) +{ + if (some_value == 20) + { + v = vec4(50.0); + } + else + { + v = vec4(70.0); + } + v.y = 20.0; +} + +void branchy_inout_2(out B b) +{ + if (some_value == 20) + { + b = B(10.0, 40.0); + } + else + { + b = B(70.0, 70.0); + } + b.b = 20.0; +} + + +void complete_inout(out vec4 x) +{ + x = vec4(50.0); +} + +void complete_inout(out B b) +{ + b = B(100.0, 200.0); +} + +void main() +{ + vec4 a = vec4(10.0); + partial_inout(a); + complete_inout(a); + branchy_inout(a); + branchy_inout_2(a); + + B b = B(10.0, 20.0); + partial_inout(b); + complete_inout(b); + branchy_inout(b); + branchy_inout_2(b); +} + diff --git a/third_party/spirv-cross/shaders/frag/pixel-interlock-ordered.frag b/third_party/spirv-cross/shaders/frag/pixel-interlock-ordered.frag new file mode 100644 index 0000000..4439f06 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/pixel-interlock-ordered.frag @@ -0,0 +1,22 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require + +layout(pixel_interlock_ordered) in; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; +layout(binding = 2) coherent buffer Buffer +{ + int foo; + uint bar; +}; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0, 0), vec4(1.0, 0.0, 0.0, 1.0)); + imageAtomicAdd(img2, ivec2(0, 0), 1u); + foo += 42; + atomicAnd(bar, 0xff); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders/frag/pixel-interlock-unordered.frag b/third_party/spirv-cross/shaders/frag/pixel-interlock-unordered.frag new file mode 100644 index 0000000..f8fd468 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/pixel-interlock-unordered.frag @@ -0,0 +1,22 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require + +layout(pixel_interlock_unordered) in; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; +layout(binding = 2) coherent buffer Buffer +{ + int foo; + uint bar; +}; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0, 0), vec4(1.0, 0.0, 0.0, 1.0)); + imageAtomicAdd(img2, ivec2(0, 0), 1u); + foo += 42; + atomicAnd(bar, 0xff); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders/frag/pls.frag b/third_party/spirv-cross/shaders/frag/pls.frag new file mode 100644 index 0000000..e3863e4 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/pls.frag @@ -0,0 +1,20 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 PLSIn0; +layout(location = 1) in vec4 PLSIn1; +layout(location = 2) in vec4 PLSIn2; +layout(location = 3) in vec4 PLSIn3; + +layout(location = 0) out vec4 PLSOut0; +layout(location = 1) out vec4 PLSOut1; +layout(location = 2) out vec4 PLSOut2; +layout(location = 3) out vec4 PLSOut3; + +void main() +{ + PLSOut0 = 2.0 * PLSIn0; + PLSOut1 = 6.0 * PLSIn1; + PLSOut2 = 7.0 * PLSIn2; + PLSOut3 = 4.0 * PLSIn3; +} diff --git a/third_party/spirv-cross/shaders/frag/post-depth-coverage-es.frag b/third_party/spirv-cross/shaders/frag/post-depth-coverage-es.frag new file mode 100644 index 0000000..ecc57e4 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/post-depth-coverage-es.frag @@ -0,0 +1,13 @@ +#version 310 es +#extension GL_EXT_post_depth_coverage : require +#extension GL_OES_sample_variables : require +precision mediump float; + +layout(early_fragment_tests, post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(gl_SampleMaskIn[0]); +} diff --git a/third_party/spirv-cross/shaders/frag/post-depth-coverage.frag b/third_party/spirv-cross/shaders/frag/post-depth-coverage.frag new file mode 100644 index 0000000..4f134b4 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/post-depth-coverage.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_ARB_post_depth_coverage : require + +layout(post_depth_coverage) in; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vec4(gl_SampleMaskIn[0]); +} diff --git a/third_party/spirv-cross/shaders/frag/round-even.frag b/third_party/spirv-cross/shaders/frag/round-even.frag new file mode 100644 index 0000000..594ac16 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/round-even.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec4 vA; +layout(location = 1) in float vB; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = roundEven(vA); + FragColor *= roundEven(vB); +} diff --git a/third_party/spirv-cross/shaders/frag/round.frag b/third_party/spirv-cross/shaders/frag/round.frag new file mode 100644 index 0000000..c87b0ab --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/round.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec4 vA; +layout(location = 1) in float vB; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = round(vA); + FragColor *= round(vB); +} diff --git a/third_party/spirv-cross/shaders/frag/sample-interlock-ordered.frag b/third_party/spirv-cross/shaders/frag/sample-interlock-ordered.frag new file mode 100644 index 0000000..fa80dc9 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/sample-interlock-ordered.frag @@ -0,0 +1,22 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require + +layout(sample_interlock_ordered) in; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; +layout(binding = 2) coherent buffer Buffer +{ + int foo; + uint bar; +}; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0, 0), vec4(1.0, 0.0, 0.0, 1.0)); + imageAtomicAdd(img2, ivec2(0, 0), 1u); + foo += 42; + atomicAnd(bar, gl_SampleMaskIn[0]); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders/frag/sample-interlock-unordered.frag b/third_party/spirv-cross/shaders/frag/sample-interlock-unordered.frag new file mode 100644 index 0000000..6fe5437 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/sample-interlock-unordered.frag @@ -0,0 +1,22 @@ +#version 450 +#extension GL_ARB_fragment_shader_interlock : require + +layout(sample_interlock_unordered) in; + +layout(binding = 0, rgba8) uniform writeonly image2D img; +layout(binding = 1, r32ui) uniform uimage2D img2; +layout(binding = 2) coherent buffer Buffer +{ + int foo; + uint bar; +}; + +void main() +{ + beginInvocationInterlockARB(); + imageStore(img, ivec2(0, 0), vec4(1.0, 0.0, 0.0, 1.0)); + imageAtomicAdd(img2, ivec2(0, 0), 1u); + foo += 42; + atomicAnd(bar, 0xff); + endInvocationInterlockARB(); +} diff --git a/third_party/spirv-cross/shaders/frag/sample-parameter.frag b/third_party/spirv-cross/shaders/frag/sample-parameter.frag new file mode 100644 index 0000000..8470bfd --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/sample-parameter.frag @@ -0,0 +1,11 @@ +#version 310 es +#extension GL_OES_sample_variables : require +precision mediump float; + +layout(location = 0) out vec2 FragColor; + +void main() +{ + FragColor = gl_SamplePosition + vec2(gl_SampleMaskIn[0]) + float(gl_SampleID); + gl_SampleMask[0] = 1; +} diff --git a/third_party/spirv-cross/shaders/frag/sampler-ms.frag b/third_party/spirv-cross/shaders/frag/sampler-ms.frag new file mode 100644 index 0000000..6593928 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/sampler-ms.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; +precision highp int; + +layout(binding = 0) uniform mediump sampler2DMS uSampler; +layout(location = 0) out vec4 FragColor; + +void main() +{ + ivec2 coord = ivec2(gl_FragCoord.xy); + FragColor = + texelFetch(uSampler, coord, 0) + + texelFetch(uSampler, coord, 1) + + texelFetch(uSampler, coord, 2) + + texelFetch(uSampler, coord, 3); +} diff --git a/third_party/spirv-cross/shaders/frag/sampler-proj.frag b/third_party/spirv-cross/shaders/frag/sampler-proj.frag new file mode 100644 index 0000000..21fa5c0 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/sampler-proj.frag @@ -0,0 +1,12 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vTex; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureProj(uTex, vTex); +} + diff --git a/third_party/spirv-cross/shaders/frag/sampler.frag b/third_party/spirv-cross/shaders/frag/sampler.frag new file mode 100644 index 0000000..e38f768 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/sampler.frag @@ -0,0 +1,18 @@ +#version 310 es +precision mediump float; + +layout(location = 0) in vec4 vColor; +layout(location = 1) in vec2 vTex; +layout(binding = 0) uniform sampler2D uTex; +layout(location = 0) out vec4 FragColor; + +vec4 sample_texture(sampler2D tex, vec2 uv) +{ + return texture(tex, uv); +} + +void main() +{ + FragColor = vColor * sample_texture(uTex, vTex); +} + diff --git a/third_party/spirv-cross/shaders/frag/scalar-refract-reflect.frag b/third_party/spirv-cross/shaders/frag/scalar-refract-reflect.frag new file mode 100644 index 0000000..486ed90 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/scalar-refract-reflect.frag @@ -0,0 +1,11 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(location = 0) in vec3 vRefract; + +void main() +{ + FragColor = refract(vRefract.x, vRefract.y, vRefract.z); + FragColor += reflect(vRefract.x, vRefract.y); + FragColor += refract(vRefract.xy, vRefract.yz, vRefract.z).y; + FragColor += reflect(vRefract.xy, vRefract.zy).y; +} diff --git a/third_party/spirv-cross/shaders/frag/selection-block-dominator.frag b/third_party/spirv-cross/shaders/frag/selection-block-dominator.frag new file mode 100644 index 0000000..257f4e6 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/selection-block-dominator.frag @@ -0,0 +1,18 @@ +#version 450 +layout(location = 0) out vec4 FragColor; +layout(location = 0) flat in int vIndex; + +void main() +{ + int v; + if (vIndex != 1) + { + FragColor = vec4(1.0); + return; + } + else + { + v = 10; + } + FragColor = vec4(v); +} diff --git a/third_party/spirv-cross/shaders/frag/struct-type-unrelated-alias.frag b/third_party/spirv-cross/shaders/frag/struct-type-unrelated-alias.frag new file mode 100644 index 0000000..d1c7905 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/struct-type-unrelated-alias.frag @@ -0,0 +1,19 @@ +#version 450 + +layout(location = 0) out float FragColor; + +struct T +{ + float a; +}; + +void main() +{ + T foo; + struct T { float b; }; + T bar; + + foo.a = 10.0; + bar.b = 20.0; + FragColor = foo.a + bar.b; +} diff --git a/third_party/spirv-cross/shaders/frag/switch-unsigned-case.frag b/third_party/spirv-cross/shaders/frag/switch-unsigned-case.frag new file mode 100644 index 0000000..d8aee43 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/switch-unsigned-case.frag @@ -0,0 +1,26 @@ +#version 310 es +precision mediump float; + +#define ENUM_0 0u +#define ENUM_1 1u + +layout(set = 0, binding = 0) uniform Buff +{ + uint TestVal; +}; + +layout(location = 0) out vec4 fsout_Color; + +void main() +{ + fsout_Color = vec4(1.0); + switch (TestVal) + { + case ENUM_0: + fsout_Color = vec4(0.1); + break; + case ENUM_1: + fsout_Color = vec4(0.2); + break; + } +} diff --git a/third_party/spirv-cross/shaders/frag/swizzle.frag b/third_party/spirv-cross/shaders/frag/swizzle.frag new file mode 100644 index 0000000..af22dd6 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/swizzle.frag @@ -0,0 +1,17 @@ +#version 310 es +precision mediump float; + +layout(binding = 0) uniform sampler2D samp; +layout(location = 0) out vec4 FragColor; +layout(location = 1) in vec3 vNormal; +layout(location = 2) in vec2 vUV; + +void main() +{ + FragColor = vec4(texture(samp, vUV).xyz, 1.0); + FragColor = vec4(texture(samp, vUV).xz, 1.0, 4.0); + FragColor = vec4(texture(samp, vUV).xx, texture(samp, vUV + vec2(0.1)).yy); + FragColor = vec4(vNormal, 1.0); + FragColor = vec4(vNormal + 1.8, 1.0); + FragColor = vec4(vUV, vUV + 1.8); +} diff --git a/third_party/spirv-cross/shaders/frag/texel-fetch-offset.frag b/third_party/spirv-cross/shaders/frag/texel-fetch-offset.frag new file mode 100644 index 0000000..e98748b --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/texel-fetch-offset.frag @@ -0,0 +1,10 @@ +#version 310 es +precision mediump float; +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uTexture; + +void main() +{ + FragColor = texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(1, 1)); + FragColor += texelFetchOffset(uTexture, ivec2(gl_FragCoord.xy), 0, ivec2(-1, 1)); +} diff --git a/third_party/spirv-cross/shaders/frag/ubo-load-row-major-workaround.frag b/third_party/spirv-cross/shaders/frag/ubo-load-row-major-workaround.frag new file mode 100644 index 0000000..03205ee --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/ubo-load-row-major-workaround.frag @@ -0,0 +1,44 @@ +#version 450 + +struct RowMajor +{ + mat4 B; +}; + +struct NestedRowMajor +{ + RowMajor rm; +}; + +layout(set = 0, binding = 0, row_major) uniform UBO +{ + mat4 A; + layout(column_major) mat4 C; // This should also be worked around. +}; + + +layout(set = 0, binding = 1, row_major) uniform UBO2 +{ + RowMajor rm; +}; + +layout(set = 0, binding = 2, row_major) uniform UBO3 +{ + NestedRowMajor rm2; +}; + +layout(set = 0, binding = 3) uniform UBONoWorkaround +{ + mat4 D; +}; + +layout(location = 0) in vec4 Clip; +layout(location = 0) out vec4 FragColor; + +void main() +{ + NestedRowMajor rm2_loaded = rm2; + FragColor = rm2_loaded.rm.B * rm.B * A * C * Clip; + FragColor += D * Clip; + FragColor += A[1] * Clip; +} diff --git a/third_party/spirv-cross/shaders/frag/ubo_layout.frag b/third_party/spirv-cross/shaders/frag/ubo_layout.frag new file mode 100644 index 0000000..80f9f16 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/ubo_layout.frag @@ -0,0 +1,24 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; + +struct Str +{ + mat4 foo; +}; + +layout(binding = 0, std140) uniform UBO1 +{ + layout(row_major) Str foo; +} ubo1; + +layout(binding = 1, std140) uniform UBO2 +{ + layout(column_major) Str foo; +} ubo0; + +void main() +{ + FragColor = ubo1.foo.foo[0] + ubo0.foo.foo[0]; +} diff --git a/third_party/spirv-cross/shaders/frag/unary-enclose.frag b/third_party/spirv-cross/shaders/frag/unary-enclose.frag new file mode 100644 index 0000000..ea502e1 --- /dev/null +++ b/third_party/spirv-cross/shaders/frag/unary-enclose.frag @@ -0,0 +1,15 @@ +#version 310 es +precision mediump float; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec4 vIn; +layout(location = 1) flat in ivec4 vIn1; + +void main() +{ + FragColor = +(-(-vIn)); + ivec4 a = ~(~vIn1); + + bool b = false; + b = !!b; +} diff --git a/third_party/spirv-cross/shaders/geom/basic.geom b/third_party/spirv-cross/shaders/geom/basic.geom new file mode 100644 index 0000000..80b977d --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/basic.geom @@ -0,0 +1,28 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require + +layout(triangles, invocations = 4) in; +layout(triangle_strip, max_vertices = 3) out; + +layout(location = 0) in VertexData { + vec3 normal; +} vin[]; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal + float(gl_InvocationID); + EmitVertex(); + + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal + 4.0 * float(gl_InvocationID); + EmitVertex(); + + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal + 2.0 * float(gl_InvocationID); + EmitVertex(); + + EndPrimitive(); +} diff --git a/third_party/spirv-cross/shaders/geom/geometry-passthrough.geom b/third_party/spirv-cross/shaders/geom/geometry-passthrough.geom new file mode 100644 index 0000000..7f1997c --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/geometry-passthrough.geom @@ -0,0 +1,28 @@ +#version 450 +#extension GL_NV_geometry_shader_passthrough : require + +layout(triangles) in; + +layout(passthrough) in gl_PerVertex +{ + vec4 gl_Position; +} gl_in[]; + +layout(passthrough, location = 0) in VertexBlock +{ + int a; + int b; +} v1[]; + +layout(location = 2) in VertexBlock2 +{ + int a; + layout(passthrough) int b; +} v2[]; + +layout(passthrough, location = 4) in vec4 vPoint[]; + +void main() +{ + gl_Layer = gl_InvocationID + v1[0].a + v2[1].b; +} diff --git a/third_party/spirv-cross/shaders/geom/lines-adjacency.geom b/third_party/spirv-cross/shaders/geom/lines-adjacency.geom new file mode 100644 index 0000000..4c34440 --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/lines-adjacency.geom @@ -0,0 +1,28 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require + +layout(lines_adjacency) in; +layout(line_strip, max_vertices = 3) out; + +layout(location = 0) in VertexData { + vec3 normal; +} vin[]; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + + EndPrimitive(); +} diff --git a/third_party/spirv-cross/shaders/geom/lines.geom b/third_party/spirv-cross/shaders/geom/lines.geom new file mode 100644 index 0000000..c751d5c --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/lines.geom @@ -0,0 +1,24 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require + +layout(lines) in; +layout(line_strip, max_vertices = 2) out; + +layout(location = 0) in VertexData { + vec3 normal; +} vin[]; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + + EndPrimitive(); +} diff --git a/third_party/spirv-cross/shaders/geom/multi-stream.geom b/third_party/spirv-cross/shaders/geom/multi-stream.geom new file mode 100644 index 0000000..19b3bbb --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/multi-stream.geom @@ -0,0 +1,15 @@ +#version 450 + +layout(triangles) in; +layout(points, max_vertices = 2) out; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + EmitStreamVertex(0); + EndStreamPrimitive(0); + gl_Position = gl_in[0].gl_Position + 2; + EmitStreamVertex(1); + EndStreamPrimitive(1); +} + diff --git a/third_party/spirv-cross/shaders/geom/points.geom b/third_party/spirv-cross/shaders/geom/points.geom new file mode 100644 index 0000000..f7dce10 --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/points.geom @@ -0,0 +1,28 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require + +layout(points) in; +layout(points, max_vertices = 3) out; + +layout(location = 0) in VertexData { + vec3 normal; +} vin[]; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + + EndPrimitive(); +} diff --git a/third_party/spirv-cross/shaders/geom/single-invocation.geom b/third_party/spirv-cross/shaders/geom/single-invocation.geom new file mode 100644 index 0000000..c3c8d15 --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/single-invocation.geom @@ -0,0 +1,28 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +layout(location = 0) in VertexData { + vec3 normal; +} vin[]; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + + EndPrimitive(); +} diff --git a/third_party/spirv-cross/shaders/geom/transform-feedback-streams.geom b/third_party/spirv-cross/shaders/geom/transform-feedback-streams.geom new file mode 100644 index 0000000..1e62890 --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/transform-feedback-streams.geom @@ -0,0 +1,24 @@ +#version 450 +layout(max_vertices = 2, points) out; +layout(points) in; +layout(stream = 1, xfb_stride = 32, xfb_offset = 16, xfb_buffer = 2, location = 0) out vec4 vFoo; + +layout(stream = 1, xfb_buffer = 1, xfb_stride = 20) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(stream = 2, xfb_buffer = 3) out VertOut +{ + layout(xfb_stride = 16, xfb_offset = 0, location = 1) vec4 vBar; +}; + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + EmitStreamVertex(1); + vBar = vec4(5.0); + EmitStreamVertex(2); +} diff --git a/third_party/spirv-cross/shaders/geom/triangles-adjacency.geom b/third_party/spirv-cross/shaders/geom/triangles-adjacency.geom new file mode 100644 index 0000000..017cef7 --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/triangles-adjacency.geom @@ -0,0 +1,28 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require + +layout(triangles_adjacency) in; +layout(triangle_strip, max_vertices = 3) out; + +layout(location = 0) in VertexData { + vec3 normal; +} vin[]; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + + EndPrimitive(); +} diff --git a/third_party/spirv-cross/shaders/geom/triangles.geom b/third_party/spirv-cross/shaders/geom/triangles.geom new file mode 100644 index 0000000..c3c8d15 --- /dev/null +++ b/third_party/spirv-cross/shaders/geom/triangles.geom @@ -0,0 +1,28 @@ +#version 310 es +#extension GL_EXT_geometry_shader : require + +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +layout(location = 0) in VertexData { + vec3 normal; +} vin[]; + +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = gl_in[0].gl_Position; + vNormal = vin[0].normal; + EmitVertex(); + + gl_Position = gl_in[1].gl_Position; + vNormal = vin[1].normal; + EmitVertex(); + + gl_Position = gl_in[2].gl_Position; + vNormal = vin[2].normal; + EmitVertex(); + + EndPrimitive(); +} diff --git a/third_party/spirv-cross/shaders/legacy/fragment/explicit-lod.legacy.frag b/third_party/spirv-cross/shaders/legacy/fragment/explicit-lod.legacy.frag new file mode 100644 index 0000000..abe1ef2 --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/fragment/explicit-lod.legacy.frag @@ -0,0 +1,12 @@ +#version 310 es + +precision mediump float; + +layout(binding = 0) uniform sampler2D tex; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureLod(tex, vec2(0.4, 0.6), 0.0); +} diff --git a/third_party/spirv-cross/shaders/legacy/fragment/explicit-lod.legacy.vert b/third_party/spirv-cross/shaders/legacy/fragment/explicit-lod.legacy.vert new file mode 100644 index 0000000..d2cbd5a --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/fragment/explicit-lod.legacy.vert @@ -0,0 +1,12 @@ +#version 310 es + +precision mediump float; + +layout(binding = 0) uniform sampler2D tex; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = textureLod(tex, vec2(0.4, 0.6), 3.0); +} diff --git a/third_party/spirv-cross/shaders/legacy/fragment/fma.legacy.frag b/third_party/spirv-cross/shaders/legacy/fragment/fma.legacy.frag new file mode 100644 index 0000000..33ceec6 --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/fragment/fma.legacy.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec4 vA; +layout(location = 1) in vec4 vB; +layout(location = 2) in vec4 vC; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = fma(vA, vB, vC); +} diff --git a/third_party/spirv-cross/shaders/legacy/fragment/io-blocks.legacy.frag b/third_party/spirv-cross/shaders/legacy/fragment/io-blocks.legacy.frag new file mode 100644 index 0000000..0a151dc --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/fragment/io-blocks.legacy.frag @@ -0,0 +1,16 @@ +#version 310 es +#extension GL_EXT_shader_io_blocks : require +precision mediump float; + +layout(location = 1) in VertexOut +{ + vec4 color; + highp vec3 normal; +} vin; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vin.color + vin.normal.xyzz; +} diff --git a/third_party/spirv-cross/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag b/third_party/spirv-cross/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag new file mode 100644 index 0000000..b1e7d1b --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/fragment/multiple-struct-flattening.legacy.frag @@ -0,0 +1,37 @@ +#version 450 + +struct Foo +{ + vec4 a; + vec4 b; +}; + +struct Bar +{ + vec4 a; + vec4 b; +}; + +struct Baz +{ + Foo foo; + Bar bar; +}; + +layout(location = 0) in VertexIn +{ + Foo a; + Bar b; +}; + +layout(location = 4) in Baz baz; + +layout(location = 0) out vec4 FragColor; + +void main() +{ + Baz bazzy = baz; + Foo bazzy_foo = baz.foo; + Bar bazzy_bar = baz.bar; + FragColor = a.a + b.b + bazzy.foo.b + bazzy_foo.a + bazzy_bar.b; +} diff --git a/third_party/spirv-cross/shaders/legacy/fragment/round.legacy.frag b/third_party/spirv-cross/shaders/legacy/fragment/round.legacy.frag new file mode 100644 index 0000000..c87b0ab --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/fragment/round.legacy.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in vec4 vA; +layout(location = 1) in float vB; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = round(vA); + FragColor *= round(vB); +} diff --git a/third_party/spirv-cross/shaders/legacy/fragment/struct-varying.legacy.frag b/third_party/spirv-cross/shaders/legacy/fragment/struct-varying.legacy.frag new file mode 100644 index 0000000..5df5c87 --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/fragment/struct-varying.legacy.frag @@ -0,0 +1,25 @@ +#version 310 es +precision highp float; + +struct Inputs +{ + vec4 a; + vec2 b; +}; + +layout(location = 0) in Inputs vin; +layout(location = 0) out vec4 FragColor; + +void main() +{ + // Read struct once. + Inputs v0 = vin; + // Read struct again. + Inputs v1 = vin; + + // Read members individually. + vec4 a = vin.a; + vec4 b = vin.b.xxyy; + + FragColor = v0.a + v0.b.xxyy + v1.a + v1.b.yyxx + a + b; +} diff --git a/third_party/spirv-cross/shaders/legacy/fragment/switch.legacy.frag b/third_party/spirv-cross/shaders/legacy/fragment/switch.legacy.frag new file mode 100644 index 0000000..d511798 --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/fragment/switch.legacy.frag @@ -0,0 +1,43 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in float vIndexF; + +void main() +{ + int vIndex = int(vIndexF); + vec4 v = vec4(0.0); + switch (vIndex) + { + case 2: + v = vec4(0, 2, 3, 4); + break; + case 4: + case 5: + v = vec4(1, 2, 3, 4); + break; + case 8: + case 9: + v = vec4(40, 20, 30, 40); + break; + case 10: + v = vec4(10.0); + case 11: + v += 1.0; + case 12: + v += 2.0; + break; + default: + v = vec4(10, 20, 30, 40); + break; + } + + vec4 w = vec4(20.0); + switch (vIndex) + { + case 10: + case 20: + w = vec4(40.0); + } + FragColor = v + w; +} diff --git a/third_party/spirv-cross/shaders/legacy/vert/implicit-lod.legacy.vert b/third_party/spirv-cross/shaders/legacy/vert/implicit-lod.legacy.vert new file mode 100644 index 0000000..6065694 --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/vert/implicit-lod.legacy.vert @@ -0,0 +1,8 @@ +#version 310 es + +layout(binding = 0) uniform sampler2D tex; + +void main() +{ + gl_Position = texture(tex, vec2(0.4, 0.6)); +} diff --git a/third_party/spirv-cross/shaders/legacy/vert/io-block.legacy.vert b/third_party/spirv-cross/shaders/legacy/vert/io-block.legacy.vert new file mode 100644 index 0000000..4fbc934 --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/vert/io-block.legacy.vert @@ -0,0 +1,17 @@ +#version 310 es +#extension GL_EXT_shader_io_blocks : require + +layout(location = 0) out VertexOut +{ + vec4 color; + vec3 normal; +} vout; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = Position; + vout.color = vec4(1.0); + vout.normal = vec3(0.5); +} diff --git a/third_party/spirv-cross/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert b/third_party/spirv-cross/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert new file mode 100644 index 0000000..f6ad932 --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/vert/struct-flatten-inner-array.legacy.vert @@ -0,0 +1,15 @@ +#version 450 + +struct Foo +{ + float a[4]; +}; + +layout(location = 0) out Foo foo; + +void main() +{ + gl_Position = vec4(1.0); + for (int i = 0; i < 4; i++) + foo.a[i] = float(i + 2); +} diff --git a/third_party/spirv-cross/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert b/third_party/spirv-cross/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert new file mode 100644 index 0000000..57e914c --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/vert/struct-flatten-stores-multi-dimension.legacy.vert @@ -0,0 +1,40 @@ +#version 450 + +struct Foo +{ + vec4 a; + vec4 b; +}; + +struct Bar +{ + vec4 a; + vec4 b; +}; + +struct Baz +{ + Foo foo; + Bar bar; +}; + +layout(location = 0) out VertexIn +{ + Foo a; + Bar b; +}; + +layout(location = 4) out Baz baz; + +void main() +{ + a.a = vec4(10.0); + a.b = vec4(20.0); + b.a = vec4(30.0); + b.b = vec4(40.0); + a = Foo(vec4(50.0), vec4(60.0)); + b = Bar(vec4(50.0), vec4(60.0)); + baz.foo = Foo(vec4(100.0), vec4(200.0)); + baz.bar = Bar(vec4(300.0), vec4(400.0)); + baz = Baz(Foo(vec4(1000.0), vec4(2000.0)), Bar(vec4(3000.0), vec4(4000.0))); +} diff --git a/third_party/spirv-cross/shaders/legacy/vert/struct-varying.legacy.vert b/third_party/spirv-cross/shaders/legacy/vert/struct-varying.legacy.vert new file mode 100644 index 0000000..3f491be --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/vert/struct-varying.legacy.vert @@ -0,0 +1,33 @@ +#version 310 es + +struct Output +{ + vec4 a; + vec2 b; +}; + +layout(location = 0) out Output vout; + +void main() +{ + Output s = Output(vec4(0.5), vec2(0.25)); + + // Write whole struct. + vout = s; + // Write whole struct again, checks for scoping. + vout = s; + + // Read it back. + Output tmp = vout; + + // Write elements individually. + vout.a = tmp.a; + vout.b = tmp.b; + + // Write individual elements. + vout.a.x = 1.0; + vout.b.y = 1.0; + + // Read individual elements. + float c = vout.a.x; +} diff --git a/third_party/spirv-cross/shaders/legacy/vert/switch-nested.legacy.vert b/third_party/spirv-cross/shaders/legacy/vert/switch-nested.legacy.vert new file mode 100644 index 0000000..6726c1c --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/vert/switch-nested.legacy.vert @@ -0,0 +1,28 @@ +#version 450 + +layout(set = 0, binding = 0) uniform UBO +{ + int func_arg; + int inner_func_arg; +}; + +vec4 test_inner_func(bool b) +{ + if (b) + return vec4(1.0); + else + return vec4(0.0); +} + +vec4 test_func(bool b) +{ + if (b) + return test_inner_func(inner_func_arg != 0); + else + return vec4(0.0); +} + +void main() +{ + gl_Position = test_func(func_arg != 0); +} diff --git a/third_party/spirv-cross/shaders/legacy/vert/transpose.legacy.vert b/third_party/spirv-cross/shaders/legacy/vert/transpose.legacy.vert new file mode 100644 index 0000000..588c28d --- /dev/null +++ b/third_party/spirv-cross/shaders/legacy/vert/transpose.legacy.vert @@ -0,0 +1,32 @@ +#version 310 es + +uniform Buffer +{ + layout(row_major) mat4 MVPRowMajor; + layout(column_major) mat4 MVPColMajor; + mat4 M; +}; + +layout(location = 0) in vec4 Position; + +void main() +{ + vec4 c0 = M * (MVPRowMajor * Position); + vec4 c1 = M * (MVPColMajor * Position); + vec4 c2 = M * (Position * MVPRowMajor); + vec4 c3 = M * (Position * MVPColMajor); + + vec4 c4 = transpose(MVPRowMajor) * Position; + vec4 c5 = transpose(MVPColMajor) * Position; + vec4 c6 = Position * transpose(MVPRowMajor); + vec4 c7 = Position * transpose(MVPColMajor); + + // Multiplying by scalar forces resolution of the transposition + vec4 c8 = (MVPRowMajor * 2.0) * Position; + vec4 c9 = (transpose(MVPColMajor) * 2.0) * Position; + vec4 c10 = Position * (MVPRowMajor * 2.0); + vec4 c11 = Position * (transpose(MVPColMajor) * 2.0); + + gl_Position = c0 + c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11; +} + diff --git a/third_party/spirv-cross/shaders/tesc/basic.tesc b/third_party/spirv-cross/shaders/tesc/basic.tesc new file mode 100644 index 0000000..0a41f98 --- /dev/null +++ b/third_party/spirv-cross/shaders/tesc/basic.tesc @@ -0,0 +1,17 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(location = 0) patch out vec3 vFoo; + +layout(vertices = 1) out; + +void main() +{ + gl_TessLevelInner[0] = 8.9; + gl_TessLevelInner[1] = 6.9; + gl_TessLevelOuter[0] = 8.9; + gl_TessLevelOuter[1] = 6.9; + gl_TessLevelOuter[2] = 3.9; + gl_TessLevelOuter[3] = 4.9; + vFoo = vec3(1.0); +} diff --git a/third_party/spirv-cross/shaders/tesc/water_tess.tesc b/third_party/spirv-cross/shaders/tesc/water_tess.tesc new file mode 100644 index 0000000..3ecdc3d --- /dev/null +++ b/third_party/spirv-cross/shaders/tesc/water_tess.tesc @@ -0,0 +1,115 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(vertices = 1) out; +layout(location = 0) in vec2 vPatchPosBase[]; + +layout(std140) uniform UBO +{ + vec4 uScale; + highp vec3 uCamPos; + vec2 uPatchSize; + vec2 uMaxTessLevel; + float uDistanceMod; + vec4 uFrustum[6]; +}; + +layout(location = 1) patch out vec2 vOutPatchPosBase; +layout(location = 2) patch out vec4 vPatchLods; + +float lod_factor(vec2 pos_) +{ + vec2 pos = pos_ * uScale.xy; + vec3 dist_to_cam = uCamPos - vec3(pos.x, 0.0, pos.y); + float level = log2((length(dist_to_cam) + 0.0001) * uDistanceMod); + return clamp(level, 0.0, uMaxTessLevel.x); +} + +float tess_level(float lod) +{ + return uMaxTessLevel.y * exp2(-lod); +} + +vec4 tess_level(vec4 lod) +{ + return uMaxTessLevel.y * exp2(-lod); +} + +// Guard band for vertex displacement. +#define GUARD_BAND 10.0 +bool frustum_cull(vec2 p0) +{ + vec2 min_xz = (p0 - GUARD_BAND) * uScale.xy; + vec2 max_xz = (p0 + uPatchSize + GUARD_BAND) * uScale.xy; + + vec3 bb_min = vec3(min_xz.x, -GUARD_BAND, min_xz.y); + vec3 bb_max = vec3(max_xz.x, +GUARD_BAND, max_xz.y); + vec3 center = 0.5 * (bb_min + bb_max); + float radius = 0.5 * length(bb_max - bb_min); + + vec3 f0 = vec3( + dot(uFrustum[0], vec4(center, 1.0)), + dot(uFrustum[1], vec4(center, 1.0)), + dot(uFrustum[2], vec4(center, 1.0))); + + vec3 f1 = vec3( + dot(uFrustum[3], vec4(center, 1.0)), + dot(uFrustum[4], vec4(center, 1.0)), + dot(uFrustum[5], vec4(center, 1.0))); + + return !(any(lessThanEqual(f0, vec3(-radius))) || any(lessThanEqual(f1, vec3(-radius)))); +} + +void compute_tess_levels(vec2 p0) +{ + vOutPatchPosBase = p0; + + float l00 = lod_factor(p0 + vec2(-0.5, -0.5) * uPatchSize); + float l10 = lod_factor(p0 + vec2(+0.5, -0.5) * uPatchSize); + float l20 = lod_factor(p0 + vec2(+1.5, -0.5) * uPatchSize); + float l01 = lod_factor(p0 + vec2(-0.5, +0.5) * uPatchSize); + float l11 = lod_factor(p0 + vec2(+0.5, +0.5) * uPatchSize); + float l21 = lod_factor(p0 + vec2(+1.5, +0.5) * uPatchSize); + float l02 = lod_factor(p0 + vec2(-0.5, +1.5) * uPatchSize); + float l12 = lod_factor(p0 + vec2(+0.5, +1.5) * uPatchSize); + float l22 = lod_factor(p0 + vec2(+1.5, +1.5) * uPatchSize); + + vec4 lods = vec4( + dot(vec4(l01, l11, l02, l12), vec4(0.25)), + dot(vec4(l00, l10, l01, l11), vec4(0.25)), + dot(vec4(l10, l20, l11, l21), vec4(0.25)), + dot(vec4(l11, l21, l12, l22), vec4(0.25))); + + vPatchLods = lods; + + vec4 outer_lods = min(lods.xyzw, lods.yzwx); + vec4 levels = tess_level(outer_lods); + gl_TessLevelOuter[0] = levels.x; + gl_TessLevelOuter[1] = levels.y; + gl_TessLevelOuter[2] = levels.z; + gl_TessLevelOuter[3] = levels.w; + + float min_lod = min(min(lods.x, lods.y), min(lods.z, lods.w)); + float inner = tess_level(min(min_lod, l11)); + gl_TessLevelInner[0] = inner; + gl_TessLevelInner[1] = inner; +} + +void main() +{ + vec2 p0 = vPatchPosBase[0]; + if (!frustum_cull(p0)) + { + gl_TessLevelOuter[0] = -1.0; + gl_TessLevelOuter[1] = -1.0; + gl_TessLevelOuter[2] = -1.0; + gl_TessLevelOuter[3] = -1.0; + gl_TessLevelInner[0] = -1.0; + gl_TessLevelInner[1] = -1.0; + } + else + { + compute_tess_levels(p0); + } +} + diff --git a/third_party/spirv-cross/shaders/tese/ccw.tese b/third_party/spirv-cross/shaders/tese/ccw.tese new file mode 100644 index 0000000..26e9cc6 --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/ccw.tese @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(ccw, triangles, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/shaders/tese/cw.tese b/third_party/spirv-cross/shaders/tese/cw.tese new file mode 100644 index 0000000..6ce7c2d --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/cw.tese @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, triangles, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/shaders/tese/equal.tese b/third_party/spirv-cross/shaders/tese/equal.tese new file mode 100644 index 0000000..08ab36e --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/equal.tese @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, triangles, equal_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/shaders/tese/fractional_even.tese b/third_party/spirv-cross/shaders/tese/fractional_even.tese new file mode 100644 index 0000000..6ce7c2d --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/fractional_even.tese @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, triangles, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/shaders/tese/fractional_odd.tese b/third_party/spirv-cross/shaders/tese/fractional_odd.tese new file mode 100644 index 0000000..a15a329 --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/fractional_odd.tese @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, triangles, fractional_odd_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/shaders/tese/input-array.tese b/third_party/spirv-cross/shaders/tese/input-array.tese new file mode 100644 index 0000000..f1014ca --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/input-array.tese @@ -0,0 +1,10 @@ +#version 450 + +layout(ccw, quads, fractional_odd_spacing) in; +layout(location = 0) in vec4 Floats[]; +layout(location = 2) in vec4 Floats2[gl_MaxPatchVertices]; + +void main() +{ + gl_Position = Floats[0] * gl_TessCoord.x + Floats2[1] * gl_TessCoord.y; +} diff --git a/third_party/spirv-cross/shaders/tese/line.tese b/third_party/spirv-cross/shaders/tese/line.tese new file mode 100644 index 0000000..b4237ef --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/line.tese @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(isolines, point_mode, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/shaders/tese/load-array-of-array.tese b/third_party/spirv-cross/shaders/tese/load-array-of-array.tese new file mode 100644 index 0000000..7383f70 --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/load-array-of-array.tese @@ -0,0 +1,10 @@ +#version 450 +layout(ccw, quads) in; + +layout(location = 0) in vec4 vTexCoord[][1]; + +void main() +{ + vec4 tmp[gl_MaxPatchVertices][1] = vTexCoord; + gl_Position = tmp[0][0] + tmp[2][0] + tmp[3][0]; +} diff --git a/third_party/spirv-cross/shaders/tese/patch-input-array.tese b/third_party/spirv-cross/shaders/tese/patch-input-array.tese new file mode 100644 index 0000000..741b2c3 --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/patch-input-array.tese @@ -0,0 +1,9 @@ +#version 450 + +layout(quads) in; +layout(location = 0) patch in float P[4]; + +void main() +{ + gl_Position = vec4(P[0], P[1], P[2], P[3]); +} diff --git a/third_party/spirv-cross/shaders/tese/triangle.tese b/third_party/spirv-cross/shaders/tese/triangle.tese new file mode 100644 index 0000000..6ce7c2d --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/triangle.tese @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require + +layout(cw, triangles, fractional_even_spacing) in; + +void main() +{ + gl_Position = vec4(1.0); +} + diff --git a/third_party/spirv-cross/shaders/tese/water_tess.tese b/third_party/spirv-cross/shaders/tese/water_tess.tese new file mode 100644 index 0000000..32d6bc9 --- /dev/null +++ b/third_party/spirv-cross/shaders/tese/water_tess.tese @@ -0,0 +1,65 @@ +#version 310 es +#extension GL_EXT_tessellation_shader : require +precision highp int; + +layout(cw, quads, fractional_even_spacing) in; + +layout(location = 0) patch in vec2 vOutPatchPosBase; +layout(location = 1) patch in vec4 vPatchLods; + +layout(binding = 1, std140) uniform UBO +{ + mat4 uMVP; + vec4 uScale; + vec2 uInvScale; + vec3 uCamPos; + vec2 uPatchSize; + vec2 uInvHeightmapSize; +}; +layout(binding = 0) uniform mediump sampler2D uHeightmapDisplacement; + +layout(location = 0) highp out vec3 vWorld; +layout(location = 1) highp out vec4 vGradNormalTex; + +vec2 lerp_vertex(vec2 tess_coord) +{ + return vOutPatchPosBase + tess_coord * uPatchSize; +} + +mediump vec2 lod_factor(vec2 tess_coord) +{ + mediump vec2 x = mix(vPatchLods.yx, vPatchLods.zw, tess_coord.x); + mediump float level = mix(x.x, x.y, tess_coord.y); + mediump float floor_level = floor(level); + mediump float fract_level = level - floor_level; + return vec2(floor_level, fract_level); +} + +mediump vec3 sample_height_displacement(vec2 uv, vec2 off, mediump vec2 lod) +{ + return mix( + textureLod(uHeightmapDisplacement, uv + 0.5 * off, lod.x).xyz, + textureLod(uHeightmapDisplacement, uv + 1.0 * off, lod.x + 1.0).xyz, + lod.y); +} + +void main() +{ + vec2 tess_coord = gl_TessCoord.xy; + vec2 pos = lerp_vertex(tess_coord); + mediump vec2 lod = lod_factor(tess_coord); + + vec2 tex = pos * uInvHeightmapSize.xy; + pos *= uScale.xy; + + mediump float delta_mod = exp2(lod.x); + vec2 off = uInvHeightmapSize.xy * delta_mod; + + vGradNormalTex = vec4(tex + 0.5 * uInvHeightmapSize.xy, tex * uScale.zw); + vec3 height_displacement = sample_height_displacement(tex, off, lod); + + pos += height_displacement.yz; + vWorld = vec3(pos.x, height_displacement.x, pos.y); + gl_Position = uMVP * vec4(vWorld, 1.0); +} + diff --git a/third_party/spirv-cross/shaders/vert/basic.vert b/third_party/spirv-cross/shaders/vert/basic.vert new file mode 100644 index 0000000..2c75d44 --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/basic.vert @@ -0,0 +1,16 @@ +#version 310 es + +layout(std140) uniform UBO +{ + uniform mat4 uMVP; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = uMVP * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders/vert/ground.vert b/third_party/spirv-cross/shaders/vert/ground.vert new file mode 100755 index 0000000..2deeb5a --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/ground.vert @@ -0,0 +1,202 @@ +#version 310 es + +#define YFLIP 0 +#define SPECULAR 0 +#define GLOSSMAP 0 + +#define DEBUG_NONE 0 +#define DEBUG_DIFFUSE 1 +#define DEBUG_SPECULAR 2 +#define DEBUG_LIGHTING 3 +#define DEBUG_FOG 4 +#define DEBUG DEBUG_NONE + +#define FORWARD 0 +#define DEFERRED 1 +#define DEFERRED_VTEX 2 + +float saturate(float x) { return clamp(x, 0.0, 1.0); } + +layout(std140, binding = 0) uniform GlobalVSData +{ + vec4 g_ViewProj_Row0; + vec4 g_ViewProj_Row1; + vec4 g_ViewProj_Row2; + vec4 g_ViewProj_Row3; + vec4 g_CamPos; + vec4 g_CamRight; + vec4 g_CamUp; + vec4 g_CamFront; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_TimeParams; + vec4 g_ResolutionParams; + vec4 g_CamAxisRight; + vec4 g_FogColor_Distance; + vec4 g_ShadowVP_Row0; + vec4 g_ShadowVP_Row1; + vec4 g_ShadowVP_Row2; + vec4 g_ShadowVP_Row3; +}; + +vec4 ComputeFogFactor(vec3 WorldPos) +{ + vec4 FogData; + vec3 vEye = WorldPos - g_CamPos.xyz; + vec3 nEye = normalize(vEye); + FogData.w = exp(-dot(vEye, vEye) * g_FogColor_Distance.w * 0.75); + + float fog_sun_factor = pow(saturate(dot(nEye, g_SunDir.xyz)), 8.0); + FogData.xyz = mix(vec3(1.0, 1.0, 1.0), vec3(0.6, 0.6, 0.9), nEye.y * 0.5 + 0.5); + FogData.xyz = mix(FogData.xyz, vec3(0.95, 0.87, 0.78), fog_sun_factor); + return FogData; +} + +void ApplyFog(inout vec3 Color, vec4 FogData) +{ + Color = mix(FogData.xyz, Color, FogData.w); +} + +void ApplyLighting(inout mediump vec3 Color, mediump float DiffuseFactor) +{ + mediump vec3 DiffuseLight = g_SunColor.xyz * DiffuseFactor; + mediump vec3 AmbientLight = vec3(0.2, 0.35, 0.55) * 0.5; + mediump vec3 Lighting = DiffuseLight + AmbientLight; +#if DEBUG == DEBUG_LIGHTING + Color = Lighting; +#else + Color *= Lighting; +#endif +} + +#pragma VARIANT SPECULAR +#pragma VARIANT GLOSSMAP + +void ApplySpecular(inout mediump vec3 Color, mediump vec3 EyeVec, mediump vec3 Normal, mediump vec3 SpecularColor, mediump float Shininess, mediump float FresnelAmount) +{ + mediump vec3 HalfAngle = normalize(-EyeVec + g_SunDir.xyz); + + mediump float v_dot_h = saturate(dot(HalfAngle, -EyeVec)); + mediump float n_dot_l = saturate(dot(Normal, g_SunDir.xyz)); + mediump float n_dot_h = saturate(dot(Normal, HalfAngle)); + mediump float n_dot_v = saturate(dot(-EyeVec, Normal)); + mediump float h_dot_l = saturate(dot(g_SunDir.xyz, HalfAngle)); + + const mediump float roughness_value = 0.25; + + mediump float r_sq = roughness_value * roughness_value; + mediump float n_dot_h_sq = n_dot_h * n_dot_h; + mediump float roughness_a = 1.0 / (4.0 * r_sq * n_dot_h_sq * n_dot_h_sq); + mediump float roughness_b = n_dot_h_sq - 1.0; + mediump float roughness_c = r_sq * n_dot_h_sq; + mediump float roughness = saturate(roughness_a * exp(roughness_b / roughness_c)); + + FresnelAmount = 0.5; + mediump float fresnel_term = pow(1.0 - n_dot_v, 5.0) * (1.0 - FresnelAmount) + FresnelAmount; + + mediump float geo_numerator = 2.0 * n_dot_h; + mediump float geo_denominator = 1.0 / v_dot_h; + mediump float geo_term = min(1.0, min(n_dot_v, n_dot_l) * geo_numerator * geo_denominator); + +#if SPECULAR || GLOSSMAP + Color += SpecularColor * g_SunColor.xyz * fresnel_term * roughness * n_dot_l * geo_term / (n_dot_v * n_dot_l + 0.0001); +#endif + + //Color = vec3(0.025 * 1.0 / (n_dot_v * n_dot_l)); +} + +layout(location = 0) in vec2 Position; +layout(location = 1) in vec4 LODWeights; + +layout(location = 0) out vec2 TexCoord; +layout(location = 1) out vec3 EyeVec; + +layout(std140, binding = 2) uniform GlobalGround +{ + vec4 GroundScale; + vec4 GroundPosition; + vec4 InvGroundSize_PatchScale; +}; + +struct PatchData +{ + vec4 Position; + vec4 LODs; +}; + +layout(std140, binding = 0) uniform PerPatch +{ + PatchData Patches[256]; +}; + +layout(binding = 0) uniform sampler2D TexHeightmap; +layout(binding = 1) uniform sampler2D TexLOD; + +vec2 lod_factor(vec2 uv) +{ + float level = textureLod(TexLOD, uv, 0.0).x * (255.0 / 32.0); + float floor_level = floor(level); + float fract_level = level - floor_level; + return vec2(floor_level, fract_level); +} + +#ifdef VULKAN +#define INSTANCE_ID gl_InstanceIndex +#else +#define INSTANCE_ID gl_InstanceID +#endif + +vec2 warp_position() +{ + float vlod = dot(LODWeights, Patches[INSTANCE_ID].LODs); + vlod = mix(vlod, Patches[INSTANCE_ID].Position.w, all(equal(LODWeights, vec4(0.0)))); + +#ifdef DEBUG_LOD_HEIGHT + LODFactor = vec4(vlod); +#endif + + float floor_lod = floor(vlod); + float fract_lod = vlod - floor_lod; + uint ufloor_lod = uint(floor_lod); + +#ifdef DEBUG_LOD_HEIGHT + LODFactor = vec4(fract_lod); +#endif + + uvec2 uPosition = uvec2(Position); + uvec2 mask = (uvec2(1u) << uvec2(ufloor_lod, ufloor_lod + 1u)) - 1u; + //uvec2 rounding = mix(uvec2(0u), mask, lessThan(uPosition, uvec2(32u))); + + uvec2 rounding = uvec2( + uPosition.x < 32u ? mask.x : 0u, + uPosition.y < 32u ? mask.y : 0u); + + vec4 lower_upper_snapped = vec4((uPosition + rounding).xyxy & (~mask).xxyy); + return mix(lower_upper_snapped.xy, lower_upper_snapped.zw, fract_lod); +} + +void main() +{ + vec2 PatchPos = Patches[INSTANCE_ID].Position.xz * InvGroundSize_PatchScale.zw; + vec2 WarpedPos = warp_position(); + vec2 VertexPos = PatchPos + WarpedPos; + vec2 NormalizedPos = VertexPos * InvGroundSize_PatchScale.xy; + vec2 lod = lod_factor(NormalizedPos); + + vec2 Offset = exp2(lod.x) * InvGroundSize_PatchScale.xy; + + float Elevation = + mix(textureLod(TexHeightmap, NormalizedPos + 0.5 * Offset, lod.x).x, + textureLod(TexHeightmap, NormalizedPos + 1.0 * Offset, lod.x + 1.0).x, + lod.y); + + vec3 WorldPos = vec3(NormalizedPos.x, Elevation, NormalizedPos.y); + WorldPos *= GroundScale.xyz; + WorldPos += GroundPosition.xyz; + + EyeVec = WorldPos - g_CamPos.xyz; + TexCoord = NormalizedPos + 0.5 * InvGroundSize_PatchScale.xy; + + gl_Position = WorldPos.x * g_ViewProj_Row0 + WorldPos.y * g_ViewProj_Row1 + WorldPos.z * g_ViewProj_Row2 + g_ViewProj_Row3; +} + diff --git a/third_party/spirv-cross/shaders/vert/invariant.vert b/third_party/spirv-cross/shaders/vert/invariant.vert new file mode 100644 index 0000000..239b985 --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/invariant.vert @@ -0,0 +1,13 @@ +#version 310 es + +invariant gl_Position; +layout(location = 0) invariant out vec4 vColor; +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; +layout(location = 2) in vec4 vInput2; + +void main() +{ + gl_Position = vInput0 + vInput1 * vInput2; + vColor = (vInput0 - vInput1) * vInput2; +} diff --git a/third_party/spirv-cross/shaders/vert/ocean.vert b/third_party/spirv-cross/shaders/vert/ocean.vert new file mode 100644 index 0000000..8a5677f --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/ocean.vert @@ -0,0 +1,200 @@ +#version 310 es + +#define YFLIP 0 +#define SPECULAR 0 +#define GLOSSMAP 0 + +#define DEBUG_NONE 0 +#define DEBUG_DIFFUSE 1 +#define DEBUG_SPECULAR 2 +#define DEBUG_LIGHTING 3 +#define DEBUG_FOG 4 +#define DEBUG DEBUG_NONE + +#define FORWARD 0 +#define DEFERRED 1 +#define DEFERRED_VTEX 2 + +float saturate(float x) { return clamp(x, 0.0, 1.0); } + +layout(std140, binding = 0) uniform GlobalVSData +{ + vec4 g_ViewProj_Row0; + vec4 g_ViewProj_Row1; + vec4 g_ViewProj_Row2; + vec4 g_ViewProj_Row3; + vec4 g_CamPos; + vec4 g_CamRight; + vec4 g_CamUp; + vec4 g_CamFront; + vec4 g_SunDir; + vec4 g_SunColor; + vec4 g_TimeParams; + vec4 g_ResolutionParams; + vec4 g_CamAxisRight; + vec4 g_FogColor_Distance; + vec4 g_ShadowVP_Row0; + vec4 g_ShadowVP_Row1; + vec4 g_ShadowVP_Row2; + vec4 g_ShadowVP_Row3; +}; + +vec4 ComputeFogFactor(vec3 WorldPos) +{ + vec4 FogData; + vec3 vEye = WorldPos - g_CamPos.xyz; + vec3 nEye = normalize(vEye); + FogData.w = exp(-dot(vEye, vEye) * g_FogColor_Distance.w * 0.75); + + float fog_sun_factor = pow(saturate(dot(nEye, g_SunDir.xyz)), 8.0); + FogData.xyz = mix(vec3(1.0, 1.0, 1.0), vec3(0.6, 0.6, 0.9), nEye.y * 0.5 + 0.5); + FogData.xyz = mix(FogData.xyz, vec3(0.95, 0.87, 0.78), fog_sun_factor); + return FogData; +} + +void ApplyFog(inout vec3 Color, vec4 FogData) +{ + Color = mix(FogData.xyz, Color, FogData.w); +} + +void ApplyLighting(inout mediump vec3 Color, mediump float DiffuseFactor) +{ + mediump vec3 DiffuseLight = g_SunColor.xyz * DiffuseFactor; + mediump vec3 AmbientLight = vec3(0.2, 0.35, 0.55) * 0.5; + mediump vec3 Lighting = DiffuseLight + AmbientLight; +#if DEBUG == DEBUG_LIGHTING + Color = Lighting; +#else + Color *= Lighting; +#endif +} + +void ApplySpecular(inout mediump vec3 Color, mediump vec3 EyeVec, mediump vec3 Normal, mediump vec3 SpecularColor, mediump float Shininess, mediump float FresnelAmount) +{ + mediump vec3 HalfAngle = normalize(-EyeVec + g_SunDir.xyz); + + mediump float v_dot_h = saturate(dot(HalfAngle, -EyeVec)); + mediump float n_dot_l = saturate(dot(Normal, g_SunDir.xyz)); + mediump float n_dot_h = saturate(dot(Normal, HalfAngle)); + mediump float n_dot_v = saturate(dot(-EyeVec, Normal)); + mediump float h_dot_l = saturate(dot(g_SunDir.xyz, HalfAngle)); + + const mediump float roughness_value = 0.25; + + mediump float r_sq = roughness_value * roughness_value; + mediump float n_dot_h_sq = n_dot_h * n_dot_h; + mediump float roughness_a = 1.0 / (4.0 * r_sq * n_dot_h_sq * n_dot_h_sq); + mediump float roughness_b = n_dot_h_sq - 1.0; + mediump float roughness_c = r_sq * n_dot_h_sq; + mediump float roughness = saturate(roughness_a * exp(roughness_b / roughness_c)); + + FresnelAmount = 0.5; + mediump float fresnel_term = pow(1.0 - n_dot_v, 5.0) * (1.0 - FresnelAmount) + FresnelAmount; + + mediump float geo_numerator = 2.0 * n_dot_h; + mediump float geo_denominator = 1.0 / v_dot_h; + mediump float geo_term = min(1.0, min(n_dot_v, n_dot_l) * geo_numerator * geo_denominator); + +#if SPECULAR || GLOSSMAP + Color += SpecularColor * g_SunColor.xyz * fresnel_term * roughness * n_dot_l * geo_term / (n_dot_v * n_dot_l + 0.0001); +#endif + + //Color = vec3(0.025 * 1.0 / (n_dot_v * n_dot_l)); +} + + +precision highp int; + +layout(binding = 0) uniform mediump sampler2D TexDisplacement; +layout(binding = 1) uniform mediump sampler2D TexLOD; + +layout(location = 0) in vec4 Position; +layout(location = 1) in vec4 LODWeights; + +layout(location = 0) out highp vec3 EyeVec; +layout(location = 1) out highp vec4 TexCoord; + +layout(std140, binding = 4) uniform GlobalOcean +{ + vec4 OceanScale; + vec4 OceanPosition; + vec4 InvOceanSize_PatchScale; + vec4 NormalTexCoordScale; +}; + +struct PatchData +{ + vec4 Position; + vec4 LODs; +}; + +layout(std140, binding = 0) uniform Offsets +{ + PatchData Patches[256]; +}; + +vec2 lod_factor(vec2 uv) +{ + float level = textureLod(TexLOD, uv, 0.0).x * (255.0 / 32.0); + float floor_level = floor(level); + float fract_level = level - floor_level; + return vec2(floor_level, fract_level); +} + +#ifdef VULKAN +#define INSTANCE_ID gl_InstanceIndex +#else +#define INSTANCE_ID gl_InstanceID +#endif + +vec2 warp_position() +{ + float vlod = dot(LODWeights, Patches[INSTANCE_ID].LODs); + vlod = mix(vlod, Patches[INSTANCE_ID].Position.w, all(equal(LODWeights, vec4(0.0)))); + + float floor_lod = floor(vlod); + float fract_lod = vlod - floor_lod; + uint ufloor_lod = uint(floor_lod); + + uvec4 uPosition = uvec4(Position); + uvec2 mask = (uvec2(1u) << uvec2(ufloor_lod, ufloor_lod + 1u)) - 1u; + + uvec4 rounding; + rounding.x = uPosition.x < 32u ? mask.x : 0u; + rounding.y = uPosition.y < 32u ? mask.x : 0u; + rounding.z = uPosition.x < 32u ? mask.y : 0u; + rounding.w = uPosition.y < 32u ? mask.y : 0u; + + //rounding = uPosition.xyxy * mask.xxyy; + vec4 lower_upper_snapped = vec4((uPosition.xyxy + rounding) & (~mask).xxyy); + return mix(lower_upper_snapped.xy, lower_upper_snapped.zw, fract_lod); +} + +void main() +{ + vec2 PatchPos = Patches[INSTANCE_ID].Position.xz * InvOceanSize_PatchScale.zw; + vec2 WarpedPos = warp_position(); + vec2 VertexPos = PatchPos + WarpedPos; + vec2 NormalizedPos = VertexPos * InvOceanSize_PatchScale.xy; + vec2 NormalizedTex = NormalizedPos * NormalTexCoordScale.zw; + vec2 lod = lod_factor(NormalizedPos); + vec2 Offset = exp2(lod.x) * InvOceanSize_PatchScale.xy * NormalTexCoordScale.zw; + + vec3 Displacement = + mix(textureLod(TexDisplacement, NormalizedTex + 0.5 * Offset, lod.x).yxz, + textureLod(TexDisplacement, NormalizedTex + 1.0 * Offset, lod.x + 1.0).yxz, + lod.y); + + vec3 WorldPos = vec3(NormalizedPos.x, 0.0, NormalizedPos.y) + Displacement; + WorldPos *= OceanScale.xyz; + WorldPos += OceanPosition.xyz; + + EyeVec = WorldPos - g_CamPos.xyz; + TexCoord = vec4(NormalizedTex, NormalizedTex * NormalTexCoordScale.xy) + 0.5 * InvOceanSize_PatchScale.xyxy * NormalTexCoordScale.zwzw; + + gl_Position = WorldPos.x * g_ViewProj_Row0 + WorldPos.y * g_ViewProj_Row1 + WorldPos.z * g_ViewProj_Row2 + g_ViewProj_Row3; +#if YFLIP + gl_Position *= vec4(1.0, -1.0, 1.0, 1.0); +#endif +} + diff --git a/third_party/spirv-cross/shaders/vert/read-from-row-major-array.vert b/third_party/spirv-cross/shaders/vert/read-from-row-major-array.vert new file mode 100644 index 0000000..792fb8e --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/read-from-row-major-array.vert @@ -0,0 +1,20 @@ +#version 310 es +layout(location = 0) in highp vec4 a_position; +layout(location = 0) out mediump float v_vtxResult; + +layout(set = 0, binding = 0, std140, row_major) uniform Block +{ + highp mat2x3 var[3][4]; +}; + +mediump float compare_float (highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; } +mediump float compare_vec3 (highp vec3 a, highp vec3 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); } +mediump float compare_mat2x3 (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1]); } + +void main (void) +{ + gl_Position = a_position; + mediump float result = 1.0; + result *= compare_mat2x3(var[0][0], mat2x3(2.0, 6.0, -6.0, 0.0, 5.0, 5.0)); + v_vtxResult = result; +} diff --git a/third_party/spirv-cross/shaders/vert/return-array.vert b/third_party/spirv-cross/shaders/vert/return-array.vert new file mode 100644 index 0000000..7084601 --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/return-array.vert @@ -0,0 +1,22 @@ +#version 310 es + +layout(location = 0) in vec4 vInput0; +layout(location = 1) in vec4 vInput1; + +vec4[2] test() +{ + return vec4[](vec4(10.0), vec4(20.0)); +} + +vec4[2] test2() +{ + vec4 foobar[2]; + foobar[0] = vInput0; + foobar[1] = vInput1; + return foobar; +} + +void main() +{ + gl_Position = test()[0] + test2()[1]; +} diff --git a/third_party/spirv-cross/shaders/vert/texture_buffer.vert b/third_party/spirv-cross/shaders/vert/texture_buffer.vert new file mode 100644 index 0000000..6bc7ddf --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/texture_buffer.vert @@ -0,0 +1,10 @@ +#version 310 es +#extension GL_OES_texture_buffer : require + +layout(binding = 4) uniform highp samplerBuffer uSamp; +layout(rgba32f, binding = 5) uniform readonly highp imageBuffer uSampo; + +void main() +{ + gl_Position = texelFetch(uSamp, 10) + imageLoad(uSampo, 100); +} diff --git a/third_party/spirv-cross/shaders/vert/transform-feedback-decorations.vert b/third_party/spirv-cross/shaders/vert/transform-feedback-decorations.vert new file mode 100644 index 0000000..b825dd1 --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/transform-feedback-decorations.vert @@ -0,0 +1,20 @@ +#version 450 +layout(xfb_stride = 32, xfb_offset = 16, xfb_buffer = 2, location = 0) out vec4 vFoo; + +layout(xfb_buffer = 1, xfb_stride = 20) out gl_PerVertex +{ + layout(xfb_offset = 4) vec4 gl_Position; + float gl_PointSize; +}; + +layout(xfb_buffer = 3) out VertOut +{ + layout(xfb_stride = 16, xfb_offset = 0, location = 1) vec4 vBar; +}; + +void main() +{ + gl_Position = vec4(1.0); + vFoo = vec4(3.0); + vBar = vec4(5.0); +} diff --git a/third_party/spirv-cross/shaders/vert/ubo.vert b/third_party/spirv-cross/shaders/vert/ubo.vert new file mode 100644 index 0000000..82e4626 --- /dev/null +++ b/third_party/spirv-cross/shaders/vert/ubo.vert @@ -0,0 +1,16 @@ +#version 310 es + +layout(binding = 0, std140) uniform UBO +{ + mat4 mvp; +}; + +layout(location = 0) in vec4 aVertex; +layout(location = 1) in vec3 aNormal; +layout(location = 0) out vec3 vNormal; + +void main() +{ + gl_Position = mvp * aVertex; + vNormal = aNormal; +} diff --git a/third_party/spirv-cross/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp b/third_party/spirv-cross/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp new file mode 100644 index 0000000..a1da941 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/comp/array-of-buffer-reference.nocompat.vk.comp @@ -0,0 +1,23 @@ +#version 450 +#extension GL_EXT_buffer_reference : require +layout(local_size_x = 1) in; + +layout(buffer_reference) buffer Block +{ + float v; +}; + +layout(std140, set = 0, binding = 0) uniform UBO +{ + Block blocks[4]; +} ubo; + +void main() +{ + Block blocks[4]; + blocks[0] = ubo.blocks[0]; + blocks[1] = ubo.blocks[1]; + blocks[2] = ubo.blocks[2]; + blocks[3] = ubo.blocks[3]; + blocks[gl_WorkGroupID.x].v = 20.0; +} diff --git a/third_party/spirv-cross/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp b/third_party/spirv-cross/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp new file mode 100644 index 0000000..eda904e --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/comp/buffer-reference-bitcast.nocompat.vk.comp @@ -0,0 +1,22 @@ +#version 450 +#extension GL_EXT_buffer_reference: require + +layout(buffer_reference) buffer PtrUint +{ + uint value; +}; + +layout(buffer_reference) buffer PtrInt +{ + int value; +}; + +layout(set = 0, binding = 0) buffer Buf +{ + PtrUint ptr; +}; + +void main() +{ + PtrInt(ptr).value = 10; +} diff --git a/third_party/spirv-cross/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp b/third_party/spirv-cross/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp new file mode 100644 index 0000000..f08e111 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/comp/buffer-reference.nocompat.vk.comp @@ -0,0 +1,40 @@ +#version 450 +#extension GL_EXT_buffer_reference : require +#extension GL_ARB_gpu_shader_int64 : require + +layout(buffer_reference) buffer Node; +layout(buffer_reference) buffer Node +{ + int value; + layout(offset = 16) Node next; + layout(offset = 32) Node prev; +}; + +layout(std430, set = 0, binding = 0) buffer LinkedList +{ + restrict Node head1; + restrict Node head2; +}; + +void copy_node(restrict Node dst, restrict Node a, restrict Node b) +{ + dst.value = a.value + b.value; +} + +void overwrite_node(out restrict Node dst, restrict Node src) +{ + dst = src; +} + +void main() +{ + restrict Node n = gl_WorkGroupID.x < 4u ? head1 : head2; + copy_node(n.next, head1, head2); + overwrite_node(n, head1); + int v = head2.value; + n.value = 20; + n.value = v * 10; + + uint64_t uptr = uint64_t(head2.next); + Node unode = Node(uptr); +} diff --git a/third_party/spirv-cross/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp b/third_party/spirv-cross/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp new file mode 100644 index 0000000..0b428eb --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/comp/spec-constant-op-member-array.vk.comp @@ -0,0 +1,33 @@ +#version 450 +layout(local_size_x = 1) in; + +layout(constant_id = 0) const int a = 100; +layout(constant_id = 1) const int b = 200; +layout(constant_id = 2) const int c = 300; +const int d = c + 50; +layout(constant_id = 3) const int e = 400; + +struct A +{ + int member0[a]; + int member1[b]; +}; + +struct B +{ + int member0[b]; + int member1[a]; +}; + +layout(set = 1, binding = 0) buffer SSBO +{ + A member_a; + B member_b; + int v[a]; + int w[d]; +}; + +void main() +{ + w[gl_GlobalInvocationID.x] += v[gl_GlobalInvocationID.x] + e; +} diff --git a/third_party/spirv-cross/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp b/third_party/spirv-cross/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp new file mode 100644 index 0000000..09b65dc --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/comp/spec-constant-work-group-size.vk.comp @@ -0,0 +1,17 @@ +#version 450 +layout(local_size_x_id = 10, local_size_y = 20) in; + +layout(constant_id = 0) const int a = 1; +layout(constant_id = 1) const int b = 2; + +layout(set = 1, binding = 0) writeonly buffer SSBO +{ + int v[]; +}; + +void main() +{ + int spec_const_array_size[b]; + spec_const_array_size[a] = a; + v[a + gl_WorkGroupSize.x + gl_WorkGroupSize.y] = b + spec_const_array_size[1 - a]; +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag new file mode 100644 index 0000000..2fabb5e --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/combined-texture-sampler-shadow.vk.frag @@ -0,0 +1,29 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform texture2D uDepth; +layout(location = 0) out float FragColor; + +float samp2(texture2D t, mediump samplerShadow s) +{ + return texture(sampler2DShadow(t, s), vec3(1.0)); +} + +float samp3(texture2D t, mediump sampler s) +{ + return texture(sampler2D(t, s), vec2(1.0)).x; +} + +float samp(texture2D t, mediump samplerShadow s, mediump sampler s1) +{ + float r0 = samp2(t, s); + float r1 = samp3(t, s1); + return r0 + r1; +} + +void main() +{ + FragColor = samp(uDepth, uSampler, uSampler1); +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/combined-texture-sampler.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/combined-texture-sampler.vk.frag new file mode 100644 index 0000000..b7de8d4 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/combined-texture-sampler.vk.frag @@ -0,0 +1,47 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler0; +layout(set = 0, binding = 1) uniform mediump sampler uSampler1; +layout(set = 0, binding = 2) uniform mediump texture2D uTexture0; +layout(set = 0, binding = 3) uniform mediump texture2D uTexture1; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; + +vec4 sample_dual(mediump sampler samp, mediump texture2D tex) +{ + return texture(sampler2D(tex, samp), vTex); +} + +vec4 sample_global_tex(mediump sampler samp) +{ + vec4 a = texture(sampler2D(uTexture0, samp), vTex); + vec4 b = sample_dual(samp, uTexture1); + return a + b; +} + +vec4 sample_global_sampler(mediump texture2D tex) +{ + vec4 a = texture(sampler2D(tex, uSampler0), vTex); + vec4 b = sample_dual(uSampler1, tex); + return a + b; +} + +vec4 sample_duals() +{ + vec4 a = sample_dual(uSampler0, uTexture0); + vec4 b = sample_dual(uSampler1, uTexture1); + return a + b; +} + +void main() +{ + vec4 c0 = sample_duals(); + vec4 c1 = sample_global_tex(uSampler0); + vec4 c2 = sample_global_tex(uSampler1); + vec4 c3 = sample_global_sampler(uTexture0); + vec4 c4 = sample_global_sampler(uTexture1); + + FragColor = c0 + c1 + c2 + c3 + c4; +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag b/third_party/spirv-cross/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag new file mode 100644 index 0000000..ba57b8c --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/demote-to-helper-forwarding.asm.vk.nocompat.frag @@ -0,0 +1,41 @@ +; SPIR-V +; Version: 1.3 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 19 +; Schema: 0 + OpCapability Shader + OpCapability DemoteToHelperInvocationEXT + OpExtension "SPV_EXT_demote_to_helper_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_EXT_demote_to_helper_invocation" + OpName %main "main" + OpName %FragColor "FragColor" + OpDecorate %FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %FragColor = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %19 = OpConstantComposite %v4float %float_1 %float_0 %float_0 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %9 = OpIsHelperInvocationEXT %bool + OpDemoteToHelperInvocationEXT + %10 = OpLogicalNot %bool %9 + OpSelectionMerge %12 None + OpBranchConditional %10 %11 %12 + %11 = OpLabel + OpStore %FragColor %19 + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-cross/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag b/third_party/spirv-cross/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag new file mode 100644 index 0000000..8b8bb61 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/demote-to-helper.vk.nocompat.frag @@ -0,0 +1,8 @@ +#version 450 +#extension GL_EXT_demote_to_helper_invocation : require + +void main() +{ + demote; + bool helper = helperInvocationEXT(); +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/desktop-mediump.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/desktop-mediump.vk.frag new file mode 100644 index 0000000..23fe3d3 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/desktop-mediump.vk.frag @@ -0,0 +1,11 @@ +#version 450 + +layout(location = 0) in mediump vec4 F; +layout(location = 1) flat in mediump ivec4 I; +layout(location = 2) flat in mediump uvec4 U; +layout(location = 0) out mediump vec4 FragColor; + +void main() +{ + FragColor = F + vec4(I) + vec4(U); +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/input-attachment-ms.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/input-attachment-ms.vk.frag new file mode 100644 index 0000000..e060738 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/input-attachment-ms.vk.frag @@ -0,0 +1,10 @@ +#version 450 + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + subpassLoad(uSubpass0, gl_SampleID); +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/input-attachment.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/input-attachment.vk.frag new file mode 100644 index 0000000..f082d15 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/input-attachment.vk.frag @@ -0,0 +1,11 @@ +#version 310 es +precision mediump float; + +layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0; +layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = subpassLoad(uSubpass0) + subpassLoad(uSubpass1); +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag b/third_party/spirv-cross/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag new file mode 100644 index 0000000..f59b07c --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/nonuniform-qualifier.vk.nocompat.frag @@ -0,0 +1,28 @@ +#version 450 +#extension GL_EXT_nonuniform_qualifier : require + +layout(binding = 0) uniform texture2D uSamplers[]; +layout(binding = 4) uniform sampler2D uCombinedSamplers[]; +layout(binding = 1) uniform sampler uSamps[]; +layout(location = 0) flat in int vIndex; +layout(location = 1) in vec2 vUV; +layout(location = 0) out vec4 FragColor; + +layout(set = 0, binding = 2) uniform UBO +{ + vec4 v[64]; +} ubos[]; + +layout(set = 0, binding = 3) readonly buffer SSBO +{ + vec4 v[]; +} ssbos[]; + +void main() +{ + int i = vIndex; + FragColor = texture(sampler2D(uSamplers[nonuniformEXT(i + 10)], uSamps[nonuniformEXT(i + 40)]), vUV); + FragColor = texture(uCombinedSamplers[nonuniformEXT(i + 10)], vUV); + FragColor += ubos[nonuniformEXT(i + 20)].v[nonuniformEXT(i + 40)]; + FragColor += ssbos[nonuniformEXT(i + 50)].v[nonuniformEXT(i + 60)]; +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag new file mode 100644 index 0000000..b843e26 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/push-constant-as-ubo.push-ubo.vk.frag @@ -0,0 +1,13 @@ +#version 450 + +layout(push_constant, std140) uniform UBO +{ + float ubo[4]; +}; + +layout(location = 0) out float FragColor; + +void main() +{ + FragColor = ubo[1]; +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/push-constant.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/push-constant.vk.frag new file mode 100644 index 0000000..6180fab --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/push-constant.vk.frag @@ -0,0 +1,16 @@ +#version 310 es +precision mediump float; + +layout(push_constant, std430) uniform PushConstants +{ + vec4 value0; + vec4 value1; +} push; + +layout(location = 0) in vec4 vColor; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = vColor + push.value0 + push.value1; +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag new file mode 100644 index 0000000..22d18a2 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/separate-combined-fake-overload.vk.frag @@ -0,0 +1,21 @@ +#version 450 + +layout(location = 0) out vec4 FragColor; +layout(binding = 0) uniform sampler2D uSamp; +layout(binding = 1) uniform texture2D uT; +layout(binding = 2) uniform sampler uS; + +vec4 samp(sampler2D uSamp) +{ + return texture(uSamp, vec2(0.5)); +} + +vec4 samp(texture2D T, sampler S) +{ + return texture(sampler2D(T, S), vec2(0.5)); +} + +void main() +{ + FragColor = samp(uSamp) + samp(uT, uS); +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag new file mode 100644 index 0000000..b3501c1 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/separate-sampler-texture-array.vk.frag @@ -0,0 +1,42 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 1) uniform mediump texture2D uTexture[4]; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D[4]; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube[4]; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray[4]; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture[2], samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +vec4 sample_func_dual_array(mediump sampler samp, mediump texture2D tex[4], vec2 uv) +{ + return texture(sampler2D(tex[1], samp), uv); +} + +void main() +{ + vec2 off = 1.0 / vec2(textureSize(sampler2D(uTexture[1], uSampler), 0)); + vec2 off2 = 1.0 / vec2(textureSize(sampler2D(uTexture[2], uSampler), 1)); + + vec4 c0 = sample_func(uSampler, vTex + off + off2); + vec4 c1 = sample_func_dual(uSampler, uTexture[1], vTex + off + off2); + vec4 c2 = sample_func_dual_array(uSampler, uTexture, vTex + off + off2); + vec4 c3 = texture(sampler2DArray(uTextureArray[3], uSampler), vTex3); + vec4 c4 = texture(samplerCube(uTextureCube[1], uSampler), vTex3); + vec4 c5 = texture(sampler3D(uTexture3D[2], uSampler), vTex3); + + FragColor = c0 + c1 + c2 + c3 + c4 + c5; +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/separate-sampler-texture.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/separate-sampler-texture.vk.frag new file mode 100644 index 0000000..cedf114 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/separate-sampler-texture.vk.frag @@ -0,0 +1,36 @@ +#version 310 es +precision mediump float; + +layout(set = 0, binding = 0) uniform mediump sampler uSampler; +layout(set = 0, binding = 1) uniform mediump texture2D uTexture; +layout(set = 0, binding = 2) uniform mediump texture3D uTexture3D; +layout(set = 0, binding = 3) uniform mediump textureCube uTextureCube; +layout(set = 0, binding = 4) uniform mediump texture2DArray uTextureArray; + +layout(location = 0) out vec4 FragColor; +layout(location = 0) in vec2 vTex; +layout(location = 1) in vec3 vTex3; + +vec4 sample_func(mediump sampler samp, vec2 uv) +{ + return texture(sampler2D(uTexture, samp), uv); +} + +vec4 sample_func_dual(mediump sampler samp, mediump texture2D tex, vec2 uv) +{ + return texture(sampler2D(tex, samp), uv); +} + +void main() +{ + vec2 off = 1.0 / vec2(textureSize(sampler2D(uTexture, uSampler), 0)); + vec2 off2 = 1.0 / vec2(textureSize(sampler2D(uTexture, uSampler), 1)); + + vec4 c0 = sample_func(uSampler, vTex + off + off2); + vec4 c1 = sample_func_dual(uSampler, uTexture, vTex + off + off2); + vec4 c2 = texture(sampler2DArray(uTextureArray, uSampler), vTex3); + vec4 c3 = texture(samplerCube(uTextureCube, uSampler), vTex3); + vec4 c4 = texture(sampler3D(uTexture3D, uSampler), vTex3); + + FragColor = c0 + c1 + c2 + c3 + c4; +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag new file mode 100644 index 0000000..d70b0da --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/shader-arithmetic-8bit.nocompat.vk.frag @@ -0,0 +1,88 @@ +#version 450 +#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require +#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require + +layout(location = 0) flat in ivec4 vColor; +layout(location = 0) out ivec4 FragColorInt; +layout(location = 1) out uvec4 FragColorUint; + +layout(push_constant, std140) uniform Push +{ + int8_t i8; + uint8_t u8; +} registers; + +layout(binding = 0, std140) uniform UBO +{ + int8_t i8; + uint8_t u8; +} ubo; + +layout(binding = 1, std430) buffer SSBO +{ + int8_t i8[16]; + uint8_t u8[16]; +} ssbo; + +void packing_int8() +{ + int16_t i16 = 10s; + int i32 = 20; + + i8vec2 i8_2 = unpack8(i16); + i8vec4 i8_4 = unpack8(i32); + i16 = pack16(i8_2); + i32 = pack32(i8_4); + ssbo.i8[0] = i8_4.x; + ssbo.i8[1] = i8_4.y; + ssbo.i8[2] = i8_4.z; + ssbo.i8[3] = i8_4.w; +} + +void packing_uint8() +{ + uint16_t u16 = 10us; + uint u32 = 20u; + + u8vec2 u8_2 = unpack8(u16); + u8vec4 u8_4 = unpack8(u32); + u16 = pack16(u8_2); + u32 = pack32(u8_4); + + ssbo.u8[0] = u8_4.x; + ssbo.u8[1] = u8_4.y; + ssbo.u8[2] = u8_4.z; + ssbo.u8[3] = u8_4.w; +} + +void compute_int8() +{ + i8vec4 tmp = i8vec4(vColor); + tmp += registers.i8; + tmp += int8_t(-40); + tmp += i8vec4(-50); + tmp += i8vec4(10, 20, 30, 40); + tmp += ssbo.i8[4]; + tmp += ubo.i8; + FragColorInt = ivec4(tmp); +} + +void compute_uint8() +{ + u8vec4 tmp = u8vec4(vColor); + tmp += registers.u8; + tmp += uint8_t(-40); + tmp += u8vec4(-50); + tmp += u8vec4(10, 20, 30, 40); + tmp += ssbo.u8[4]; + tmp += ubo.u8; + FragColorUint = uvec4(tmp); +} + +void main() +{ + packing_int8(); + packing_uint8(); + compute_int8(); + compute_uint8(); +} diff --git a/third_party/spirv-cross/shaders/vulkan/frag/spec-constant-block-size.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/spec-constant-block-size.vk.frag new file mode 100644 index 0000000..8d2b1f3 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/spec-constant-block-size.vk.frag @@ -0,0 +1,17 @@ +#version 310 es +precision mediump float; + +layout(constant_id = 10) const int Value = 2; +layout(binding = 0) uniform SpecConstArray +{ + vec4 samples[Value]; +}; + +layout(location = 0) flat in int Index; +layout(location = 0) out vec4 FragColor; + +void main() +{ + FragColor = samples[Index]; +} + diff --git a/third_party/spirv-cross/shaders/vulkan/frag/spec-constant-ternary.vk.frag b/third_party/spirv-cross/shaders/vulkan/frag/spec-constant-ternary.vk.frag new file mode 100644 index 0000000..78dccbf --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/frag/spec-constant-ternary.vk.frag @@ -0,0 +1,9 @@ +#version 450 +layout(location = 0) out float FragColor; +layout(constant_id = 0) const uint s = 10u; +const uint f = s > 20u ? 30u : 50u; + +void main() +{ + FragColor = float(f); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit new file mode 100644 index 0000000..8d367e3 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_block.nocompat.vk.rchit @@ -0,0 +1,11 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV Foo { float a; float b; } payload; +hitAttributeNV Foo2 { float a; float b; } hit; + +void main() +{ + payload.a = hit.a; + payload.b = hit.b; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit new file mode 100644 index 0000000..23a5c65 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_block_in_function.nocompat.vk.rchit @@ -0,0 +1,16 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV Foo { float a; float b; } payload; +hitAttributeNV Foo2 { float a; float b; } hit; + +void in_function() +{ + payload.a = hit.a; + payload.b = hit.b; +} + +void main() +{ + in_function(); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit new file mode 100644 index 0000000..cdbda9c --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_plain.nocompat.vk.rchit @@ -0,0 +1,10 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec2 payload; +hitAttributeNV vec2 hit; + +void main() +{ + payload = hit; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit new file mode 100644 index 0000000..625e125 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/hit_attribute_struct.nocompat.vk.rchit @@ -0,0 +1,12 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Foo { float a; float b; }; + +layout(location = 0) rayPayloadInNV Foo payload; +hitAttributeNV Foo hit; + +void main() +{ + payload = hit; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit new file mode 100644 index 0000000..39a088f --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/hit_kind.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_HitKindNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit new file mode 100644 index 0000000..16d6f06 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/hit_t.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_HitTNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit new file mode 100644 index 0000000..a1726d0 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/incoming_ray_flags.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_IncomingRayFlagsNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit new file mode 100644 index 0000000..02ae343 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/instance_custom_id.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_InstanceCustomIndexNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit new file mode 100644 index 0000000..d6f9966 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/instance_id.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_InstanceID; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit new file mode 100644 index 0000000..257175b --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/object_ray_direction.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectRayDirectionNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit new file mode 100644 index 0000000..8b71e7d --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/object_ray_origin.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectRayOriginNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit new file mode 100644 index 0000000..53b1406 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/object_to_world.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_ObjectToWorldNV * vec4(payload, 1.0); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/payloads.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/payloads.nocompat.vk.rchit new file mode 100644 index 0000000..61a8666 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/payloads.nocompat.vk.rchit @@ -0,0 +1,19 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +struct Payload +{ + vec4 a; +}; + +layout(location = 0) rayPayloadInNV Payload payload; + +void write_incoming_payload_in_function() +{ + payload.a = vec4(10.0); +} + +void main() +{ + write_incoming_payload_in_function(); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit new file mode 100644 index 0000000..fdfa1ff --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/primitive_id.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV uint payload; + +void main() +{ + payload = gl_PrimitiveID; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit new file mode 100644 index 0000000..c0e1387 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/ray_tmax.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_RayTmaxNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit new file mode 100644 index 0000000..896f4ff --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/ray_tmin.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = gl_RayTminNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit new file mode 100644 index 0000000..44c814d --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/ray_tracing.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = 1.0 + float(gl_InstanceID); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit new file mode 100644 index 0000000..43d14f2 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/world_ray_direction.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldRayDirectionNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit new file mode 100644 index 0000000..8b03e7d --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/world_ray_origin.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldRayOriginNV; +} diff --git a/third_party/spirv-cross/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit b/third_party/spirv-cross/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit new file mode 100644 index 0000000..dc67c4a --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rchit/world_to_object.nocompat.vk.rchit @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV vec3 payload; + +void main() +{ + payload = gl_WorldToObjectNV * vec4(payload, 1.0); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen b/third_party/spirv-cross/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen new file mode 100644 index 0000000..0cb8e95 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rgen/execute_callable.nocompat.vk.rgen @@ -0,0 +1,16 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform accelerationStructureNV as; +layout(set = 0, binding = 1, rgba32f) uniform writeonly image2D image; +layout(location = 0) rayPayloadNV vec4 payload; +layout(location = 0) callableDataNV float blend; + +void main() +{ + vec3 origin = vec3(0.0); + vec3 direction = vec3(0.0, 0.0, -1.0); + traceNV(as, gl_RayFlagsOpaqueNV, 0xFF, 0u, 0u, 0u, origin, 0.0, direction, 100.0f, 0); + executeCallableNV(0u, 0); + imageStore(image, ivec2(gl_LaunchIDNV.xy), payload + vec4(blend)); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen b/third_party/spirv-cross/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen new file mode 100644 index 0000000..b89792e --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rgen/launch_id.nocompat.vk.rgen @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform writeonly image2D uImage; + +void main() +{ + imageStore(uImage, ivec2(gl_LaunchIDNV.xy), vec4(1.0)); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen b/third_party/spirv-cross/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen new file mode 100644 index 0000000..1e1ff55 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rgen/launch_size.nocompat.vk.rgen @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0) uniform writeonly image2D uImage; + +void main() +{ + imageStore(uImage, ivec2(gl_LaunchSizeNV.xy) - 1, vec4(1.0)); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rgen/payloads.nocompat.vk.rgen b/third_party/spirv-cross/shaders/vulkan/rgen/payloads.nocompat.vk.rgen new file mode 100644 index 0000000..11c12d4 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rgen/payloads.nocompat.vk.rgen @@ -0,0 +1,49 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0, rgba8) uniform image2D image; +layout(set = 0, binding = 1) uniform accelerationStructureNV as; + +struct Payload +{ + float a, b; +}; + +// Plain payload +layout(location = 0) rayPayloadNV float payload1; +// Struct payload +layout(location = 1) rayPayloadNV Payload payload2; + +// This is syntactic sugar with the struct formulation (pretty sure), spec is kinda vague. +layout(location = 2) rayPayloadNV Block +{ + float a, b; + Payload c, d; +}; + +vec4 trace_in_function() +{ + vec4 result = vec4(0.0); + // Test that we can write to a payload in a function. + vec3 origin = vec3(1.0, 0.0, 0.0); + vec3 direction = vec3(0.0, 1.0, 0.0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 1); + result += payload2.a; + result += payload2.b; + return result; +} + +void main() +{ + vec3 origin = vec3(1.0, 0.0, 0.0); + vec3 direction = vec3(0.0, 1.0, 0.0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 0); + vec4 result = vec4(payload1); + + result += trace_in_function(); + + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 2); + result += a + b + c.a + c.b + d.a + d.b; + + imageStore(image, ivec2(gl_LaunchIDNV.xy), result); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen b/third_party/spirv-cross/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen new file mode 100644 index 0000000..3e362ed --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rgen/pure_call.nocompat.vk.rgen @@ -0,0 +1,18 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 1) uniform accelerationStructureNV as; +layout(location = 0) rayPayloadNV float payload; + +float pure_call(vec2 launchID, vec2 launchSize) +{ + vec3 origin = vec3(launchID.x / launchSize.x, launchID.y / launchSize.y, 1.0); + vec3 direction = vec3(0.0, 0.0, -1.0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 0); + return 0.0; +} + +void main() +{ + pure_call(vec2(gl_LaunchIDNV.xy), vec2(gl_LaunchSizeNV.xy)); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen b/third_party/spirv-cross/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen new file mode 100644 index 0000000..e3f7c1a --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rgen/ray_tracing.nocompat.vk.rgen @@ -0,0 +1,16 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(set = 0, binding = 0, rgba8) uniform image2D image; +layout(set = 0, binding = 1) uniform accelerationStructureNV as; +layout(location = 0) rayPayloadNV float payload; + +void main() +{ + vec4 col = vec4(0.0, 0.0, 0.0, 1.0); + vec3 origin = vec3(float(gl_LaunchIDNV.x) / float(gl_LaunchSizeNV.x), float(gl_LaunchIDNV.y) / float(gl_LaunchSizeNV.y), 1.0); + vec3 direction = vec3(0.0, 0.0, -1.0); + traceNV(as, 0u, 255u, 0u, 1u, 0u, origin, 0.0, direction, 1000.0, 0); + col.y = payload; + imageStore(image, ivec2(gl_LaunchIDNV.xy), col); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen b/third_party/spirv-cross/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen new file mode 100644 index 0000000..9cca9a1 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rgen/shader_record_buffer.nocompat.vk.rgen @@ -0,0 +1,16 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(shaderRecordNV, std430) buffer sbt +{ + vec3 direction; + float tmax; +}; + +layout(set = 0, binding = 0) uniform accelerationStructureNV as; +layout(location = 0) rayPayloadNV float payload; + +void main() +{ + traceNV(as, 0u, 255u, 0u, 1u, 0u, vec3(0.0), 0.0, direction, tmax, 0); +} diff --git a/third_party/spirv-cross/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss b/third_party/spirv-cross/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss new file mode 100644 index 0000000..ff438ce --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/rmiss/ray_tracing.nocompat.vk.rmiss @@ -0,0 +1,9 @@ +#version 460 +#extension GL_NV_ray_tracing : require + +layout(location = 0) rayPayloadInNV float payload; + +void main() +{ + payload = 0.0; +} diff --git a/third_party/spirv-cross/shaders/vulkan/vert/device-group.nocompat.vk.vert b/third_party/spirv-cross/shaders/vulkan/vert/device-group.nocompat.vk.vert new file mode 100644 index 0000000..16ed51b --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/vert/device-group.nocompat.vk.vert @@ -0,0 +1,7 @@ +#version 450 core +#extension GL_EXT_device_group : require + +void main() +{ + gl_Position = vec4(gl_DeviceIndex); +} diff --git a/third_party/spirv-cross/shaders/vulkan/vert/multiview.nocompat.vk.vert b/third_party/spirv-cross/shaders/vulkan/vert/multiview.nocompat.vk.vert new file mode 100644 index 0000000..eb1bc76 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/vert/multiview.nocompat.vk.vert @@ -0,0 +1,14 @@ +#version 310 es +#extension GL_EXT_multiview : require + +layout(std140, binding = 0) uniform MVPs +{ + mat4 MVP[2]; +}; + +layout(location = 0) in vec4 Position; + +void main() +{ + gl_Position = MVP[gl_ViewIndex] * Position; +} diff --git a/third_party/spirv-cross/shaders/vulkan/vert/small-storage.vk.vert b/third_party/spirv-cross/shaders/vulkan/vert/small-storage.vk.vert new file mode 100644 index 0000000..195f3d5 --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/vert/small-storage.vk.vert @@ -0,0 +1,46 @@ +#version 450 core + +// GL_EXT_shader_16bit_storage doesn't support input/output. +#extension GL_EXT_shader_8bit_storage : require +#extension GL_AMD_gpu_shader_int16 : require +#extension GL_AMD_gpu_shader_half_float : require + +layout(location = 0, component = 0) in int16_t foo; +layout(location = 0, component = 1) in uint16_t bar; +layout(location = 1) in float16_t baz; + +layout(binding = 0) uniform block { + i16vec2 a; + u16vec2 b; + i8vec2 c; + u8vec2 d; + f16vec2 e; +}; + +layout(binding = 1) readonly buffer storage { + i16vec3 f; + u16vec3 g; + i8vec3 h; + u8vec3 i; + f16vec3 j; +}; + +layout(push_constant) uniform pushconst { + i16vec4 k; + u16vec4 l; + i8vec4 m; + u8vec4 n; + f16vec4 o; +}; + +layout(location = 0) out i16vec4 p; +layout(location = 1) out u16vec4 q; +layout(location = 2) out f16vec4 r; + +void main() { + p = i16vec4(int(foo) + ivec4(ivec2(a), ivec2(c)) - ivec4(ivec3(f) / ivec3(h), 1) + ivec4(k) + ivec4(m)); + q = u16vec4(uint(bar) + uvec4(uvec2(b), uvec2(d)) - uvec4(uvec3(g) / uvec3(i), 1) + uvec4(l) + uvec4(n)); + r = f16vec4(float(baz) + vec4(vec2(e), 0, 1) - vec4(vec3(j), 1) + vec4(o)); + gl_Position = vec4(0, 0, 0, 1); +} + diff --git a/third_party/spirv-cross/shaders/vulkan/vert/vulkan-vertex.vk.vert b/third_party/spirv-cross/shaders/vulkan/vert/vulkan-vertex.vk.vert new file mode 100644 index 0000000..4d0438a --- /dev/null +++ b/third_party/spirv-cross/shaders/vulkan/vert/vulkan-vertex.vk.vert @@ -0,0 +1,6 @@ +#version 310 es + +void main() +{ + gl_Position = float(gl_VertexIndex + gl_InstanceIndex) * vec4(1.0, 2.0, 3.0, 4.0); +} diff --git a/third_party/spirv-cross/spirv.h b/third_party/spirv-cross/spirv.h new file mode 100644 index 0000000..dd9850d --- /dev/null +++ b/third_party/spirv-cross/spirv.h @@ -0,0 +1,2104 @@ +/* +** Copyright (c) 2014-2020 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python, C#, D +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** - C# will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10500 +#define SPV_REVISION 3 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010500; +static const unsigned int SpvRevision = 3; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelTaskNV = 5267, + SpvExecutionModelMeshNV = 5268, + SpvExecutionModelRayGenerationKHR = 5313, + SpvExecutionModelRayGenerationNV = 5313, + SpvExecutionModelIntersectionKHR = 5314, + SpvExecutionModelIntersectionNV = 5314, + SpvExecutionModelAnyHitKHR = 5315, + SpvExecutionModelAnyHitNV = 5315, + SpvExecutionModelClosestHitKHR = 5316, + SpvExecutionModelClosestHitNV = 5316, + SpvExecutionModelMissKHR = 5317, + SpvExecutionModelMissNV = 5317, + SpvExecutionModelCallableKHR = 5318, + SpvExecutionModelCallableNV = 5318, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelPhysicalStorageBuffer64 = 5348, + SpvAddressingModelPhysicalStorageBuffer64EXT = 5348, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelVulkan = 3, + SpvMemoryModelVulkanKHR = 3, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModeSubgroupsPerWorkgroupId = 37, + SpvExecutionModeLocalSizeId = 38, + SpvExecutionModeLocalSizeHintId = 39, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeDenormPreserve = 4459, + SpvExecutionModeDenormFlushToZero = 4460, + SpvExecutionModeSignedZeroInfNanPreserve = 4461, + SpvExecutionModeRoundingModeRTE = 4462, + SpvExecutionModeRoundingModeRTZ = 4463, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeOutputLinesNV = 5269, + SpvExecutionModeOutputPrimitivesNV = 5270, + SpvExecutionModeDerivativeGroupQuadsNV = 5289, + SpvExecutionModeDerivativeGroupLinearNV = 5290, + SpvExecutionModeOutputTrianglesNV = 5298, + SpvExecutionModePixelInterlockOrderedEXT = 5366, + SpvExecutionModePixelInterlockUnorderedEXT = 5367, + SpvExecutionModeSampleInterlockOrderedEXT = 5368, + SpvExecutionModeSampleInterlockUnorderedEXT = 5369, + SpvExecutionModeShadingRateInterlockOrderedEXT = 5370, + SpvExecutionModeShadingRateInterlockUnorderedEXT = 5371, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassCallableDataKHR = 5328, + SpvStorageClassCallableDataNV = 5328, + SpvStorageClassIncomingCallableDataKHR = 5329, + SpvStorageClassIncomingCallableDataNV = 5329, + SpvStorageClassRayPayloadKHR = 5338, + SpvStorageClassRayPayloadNV = 5338, + SpvStorageClassHitAttributeKHR = 5339, + SpvStorageClassHitAttributeNV = 5339, + SpvStorageClassIncomingRayPayloadKHR = 5342, + SpvStorageClassIncomingRayPayloadNV = 5342, + SpvStorageClassShaderRecordBufferKHR = 5343, + SpvStorageClassShaderRecordBufferNV = 5343, + SpvStorageClassPhysicalStorageBuffer = 5349, + SpvStorageClassPhysicalStorageBufferEXT = 5349, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMakeTexelAvailableShift = 8, + SpvImageOperandsMakeTexelAvailableKHRShift = 8, + SpvImageOperandsMakeTexelVisibleShift = 9, + SpvImageOperandsMakeTexelVisibleKHRShift = 9, + SpvImageOperandsNonPrivateTexelShift = 10, + SpvImageOperandsNonPrivateTexelKHRShift = 10, + SpvImageOperandsVolatileTexelShift = 11, + SpvImageOperandsVolatileTexelKHRShift = 11, + SpvImageOperandsSignExtendShift = 12, + SpvImageOperandsZeroExtendShift = 13, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, + SpvImageOperandsMakeTexelAvailableMask = 0x00000100, + SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + SpvImageOperandsMakeTexelVisibleMask = 0x00000200, + SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + SpvImageOperandsNonPrivateTexelMask = 0x00000400, + SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400, + SpvImageOperandsVolatileTexelMask = 0x00000800, + SpvImageOperandsVolatileTexelKHRMask = 0x00000800, + SpvImageOperandsSignExtendMask = 0x00001000, + SpvImageOperandsZeroExtendMask = 0x00002000, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationUniformId = 27, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationAlignmentId = 46, + SpvDecorationMaxByteOffsetId = 47, + SpvDecorationNoSignedWrap = 4469, + SpvDecorationNoUnsignedWrap = 4470, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationPerPrimitiveNV = 5271, + SpvDecorationPerViewNV = 5272, + SpvDecorationPerTaskNV = 5273, + SpvDecorationPerVertexNV = 5285, + SpvDecorationNonUniform = 5300, + SpvDecorationNonUniformEXT = 5300, + SpvDecorationRestrictPointer = 5355, + SpvDecorationRestrictPointerEXT = 5355, + SpvDecorationAliasedPointer = 5356, + SpvDecorationAliasedPointerEXT = 5356, + SpvDecorationCounterBuffer = 5634, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationUserSemantic = 5635, + SpvDecorationUserTypeGOOGLE = 5636, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMask = 4416, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMask = 4417, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMask = 4418, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMask = 4419, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMask = 4420, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInFullyCoveredEXT = 5264, + SpvBuiltInTaskCountNV = 5274, + SpvBuiltInPrimitiveCountNV = 5275, + SpvBuiltInPrimitiveIndicesNV = 5276, + SpvBuiltInClipDistancePerViewNV = 5277, + SpvBuiltInCullDistancePerViewNV = 5278, + SpvBuiltInLayerPerViewNV = 5279, + SpvBuiltInMeshViewCountNV = 5280, + SpvBuiltInMeshViewIndicesNV = 5281, + SpvBuiltInBaryCoordNV = 5286, + SpvBuiltInBaryCoordNoPerspNV = 5287, + SpvBuiltInFragSizeEXT = 5292, + SpvBuiltInFragmentSizeNV = 5292, + SpvBuiltInFragInvocationCountEXT = 5293, + SpvBuiltInInvocationsPerPixelNV = 5293, + SpvBuiltInLaunchIdKHR = 5319, + SpvBuiltInLaunchIdNV = 5319, + SpvBuiltInLaunchSizeKHR = 5320, + SpvBuiltInLaunchSizeNV = 5320, + SpvBuiltInWorldRayOriginKHR = 5321, + SpvBuiltInWorldRayOriginNV = 5321, + SpvBuiltInWorldRayDirectionKHR = 5322, + SpvBuiltInWorldRayDirectionNV = 5322, + SpvBuiltInObjectRayOriginKHR = 5323, + SpvBuiltInObjectRayOriginNV = 5323, + SpvBuiltInObjectRayDirectionKHR = 5324, + SpvBuiltInObjectRayDirectionNV = 5324, + SpvBuiltInRayTminKHR = 5325, + SpvBuiltInRayTminNV = 5325, + SpvBuiltInRayTmaxKHR = 5326, + SpvBuiltInRayTmaxNV = 5326, + SpvBuiltInInstanceCustomIndexKHR = 5327, + SpvBuiltInInstanceCustomIndexNV = 5327, + SpvBuiltInObjectToWorldKHR = 5330, + SpvBuiltInObjectToWorldNV = 5330, + SpvBuiltInWorldToObjectKHR = 5331, + SpvBuiltInWorldToObjectNV = 5331, + SpvBuiltInHitTKHR = 5332, + SpvBuiltInHitTNV = 5332, + SpvBuiltInHitKindKHR = 5333, + SpvBuiltInHitKindNV = 5333, + SpvBuiltInIncomingRayFlagsKHR = 5351, + SpvBuiltInIncomingRayFlagsNV = 5351, + SpvBuiltInRayGeometryIndexKHR = 5352, + SpvBuiltInWarpsPerSMNV = 5374, + SpvBuiltInSMCountNV = 5375, + SpvBuiltInWarpIDNV = 5376, + SpvBuiltInSMIDNV = 5377, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMinIterationsShift = 4, + SpvLoopControlMaxIterationsShift = 5, + SpvLoopControlIterationMultipleShift = 6, + SpvLoopControlPeelCountShift = 7, + SpvLoopControlPartialCountShift = 8, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, + SpvLoopControlMinIterationsMask = 0x00000010, + SpvLoopControlMaxIterationsMask = 0x00000020, + SpvLoopControlIterationMultipleMask = 0x00000040, + SpvLoopControlPeelCountMask = 0x00000080, + SpvLoopControlPartialCountMask = 0x00000100, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsOutputMemoryShift = 12, + SpvMemorySemanticsOutputMemoryKHRShift = 12, + SpvMemorySemanticsMakeAvailableShift = 13, + SpvMemorySemanticsMakeAvailableKHRShift = 13, + SpvMemorySemanticsMakeVisibleShift = 14, + SpvMemorySemanticsMakeVisibleKHRShift = 14, + SpvMemorySemanticsVolatileShift = 15, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, + SpvMemorySemanticsOutputMemoryMask = 0x00001000, + SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000, + SpvMemorySemanticsMakeAvailableMask = 0x00002000, + SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000, + SpvMemorySemanticsMakeVisibleMask = 0x00004000, + SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000, + SpvMemorySemanticsVolatileMask = 0x00008000, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMakePointerAvailableShift = 3, + SpvMemoryAccessMakePointerAvailableKHRShift = 3, + SpvMemoryAccessMakePointerVisibleShift = 4, + SpvMemoryAccessMakePointerVisibleKHRShift = 4, + SpvMemoryAccessNonPrivatePointerShift = 5, + SpvMemoryAccessNonPrivatePointerKHRShift = 5, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, + SpvMemoryAccessMakePointerAvailableMask = 0x00000008, + SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008, + SpvMemoryAccessMakePointerVisibleMask = 0x00000010, + SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010, + SpvMemoryAccessNonPrivatePointerMask = 0x00000020, + SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeQueueFamily = 5, + SpvScopeQueueFamilyKHR = 5, + SpvScopeShaderCallKHR = 6, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationClusteredReduce = 3, + SpvGroupOperationPartitionedReduceNV = 6, + SpvGroupOperationPartitionedInclusiveScanNV = 7, + SpvGroupOperationPartitionedExclusiveScanNV = 8, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilityGroupNonUniform = 61, + SpvCapabilityGroupNonUniformVote = 62, + SpvCapabilityGroupNonUniformArithmetic = 63, + SpvCapabilityGroupNonUniformBallot = 64, + SpvCapabilityGroupNonUniformShuffle = 65, + SpvCapabilityGroupNonUniformShuffleRelative = 66, + SpvCapabilityGroupNonUniformClustered = 67, + SpvCapabilityGroupNonUniformQuad = 68, + SpvCapabilityShaderLayer = 69, + SpvCapabilityShaderViewportIndex = 70, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityStorageBuffer8BitAccess = 4448, + SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449, + SpvCapabilityStoragePushConstant8 = 4450, + SpvCapabilityDenormPreserve = 4464, + SpvCapabilityDenormFlushToZero = 4465, + SpvCapabilitySignedZeroInfNanPreserve = 4466, + SpvCapabilityRoundingModeRTE = 4467, + SpvCapabilityRoundingModeRTZ = 4468, + SpvCapabilityRayQueryProvisionalKHR = 4471, + SpvCapabilityRayTraversalPrimitiveCullingProvisionalKHR = 4478, + SpvCapabilityFloat16ImageAMD = 5008, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilityShaderClockKHR = 5055, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilityFragmentFullyCoveredEXT = 5265, + SpvCapabilityMeshShadingNV = 5266, + SpvCapabilityImageFootprintNV = 5282, + SpvCapabilityFragmentBarycentricNV = 5284, + SpvCapabilityComputeDerivativeGroupQuadsNV = 5288, + SpvCapabilityFragmentDensityEXT = 5291, + SpvCapabilityShadingRateNV = 5291, + SpvCapabilityGroupNonUniformPartitionedNV = 5297, + SpvCapabilityShaderNonUniform = 5301, + SpvCapabilityShaderNonUniformEXT = 5301, + SpvCapabilityRuntimeDescriptorArray = 5302, + SpvCapabilityRuntimeDescriptorArrayEXT = 5302, + SpvCapabilityInputAttachmentArrayDynamicIndexing = 5303, + SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + SpvCapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + SpvCapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + SpvCapabilityUniformBufferArrayNonUniformIndexing = 5306, + SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + SpvCapabilitySampledImageArrayNonUniformIndexing = 5307, + SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + SpvCapabilityStorageBufferArrayNonUniformIndexing = 5308, + SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + SpvCapabilityStorageImageArrayNonUniformIndexing = 5309, + SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + SpvCapabilityInputAttachmentArrayNonUniformIndexing = 5310, + SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + SpvCapabilityRayTracingNV = 5340, + SpvCapabilityVulkanMemoryModel = 5345, + SpvCapabilityVulkanMemoryModelKHR = 5345, + SpvCapabilityVulkanMemoryModelDeviceScope = 5346, + SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + SpvCapabilityPhysicalStorageBufferAddresses = 5347, + SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347, + SpvCapabilityComputeDerivativeGroupLinearNV = 5350, + SpvCapabilityRayTracingProvisionalKHR = 5353, + SpvCapabilityCooperativeMatrixNV = 5357, + SpvCapabilityFragmentShaderSampleInterlockEXT = 5363, + SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372, + SpvCapabilityShaderSMBuiltinsNV = 5373, + SpvCapabilityFragmentShaderPixelInterlockEXT = 5378, + SpvCapabilityDemoteToHelperInvocationEXT = 5379, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579, + SpvCapabilityIntegerFunctions2INTEL = 5584, + SpvCapabilitySubgroupAvcMotionEstimationINTEL = 5696, + SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvRayFlagsShift_ { + SpvRayFlagsOpaqueKHRShift = 0, + SpvRayFlagsNoOpaqueKHRShift = 1, + SpvRayFlagsTerminateOnFirstHitKHRShift = 2, + SpvRayFlagsSkipClosestHitShaderKHRShift = 3, + SpvRayFlagsCullBackFacingTrianglesKHRShift = 4, + SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5, + SpvRayFlagsCullOpaqueKHRShift = 6, + SpvRayFlagsCullNoOpaqueKHRShift = 7, + SpvRayFlagsSkipTrianglesKHRShift = 8, + SpvRayFlagsSkipAABBsKHRShift = 9, + SpvRayFlagsMax = 0x7fffffff, +} SpvRayFlagsShift; + +typedef enum SpvRayFlagsMask_ { + SpvRayFlagsMaskNone = 0, + SpvRayFlagsOpaqueKHRMask = 0x00000001, + SpvRayFlagsNoOpaqueKHRMask = 0x00000002, + SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + SpvRayFlagsCullOpaqueKHRMask = 0x00000040, + SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080, + SpvRayFlagsSkipTrianglesKHRMask = 0x00000100, + SpvRayFlagsSkipAABBsKHRMask = 0x00000200, +} SpvRayFlagsMask; + +typedef enum SpvRayQueryIntersection_ { + SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + SpvRayQueryIntersectionMax = 0x7fffffff, +} SpvRayQueryIntersection; + +typedef enum SpvRayQueryCommittedIntersectionType_ { + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCommittedIntersectionType; + +typedef enum SpvRayQueryCandidateIntersectionType_ { + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCandidateIntersectionType; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpExecutionModeId = 331, + SpvOpDecorateId = 332, + SpvOpGroupNonUniformElect = 333, + SpvOpGroupNonUniformAll = 334, + SpvOpGroupNonUniformAny = 335, + SpvOpGroupNonUniformAllEqual = 336, + SpvOpGroupNonUniformBroadcast = 337, + SpvOpGroupNonUniformBroadcastFirst = 338, + SpvOpGroupNonUniformBallot = 339, + SpvOpGroupNonUniformInverseBallot = 340, + SpvOpGroupNonUniformBallotBitExtract = 341, + SpvOpGroupNonUniformBallotBitCount = 342, + SpvOpGroupNonUniformBallotFindLSB = 343, + SpvOpGroupNonUniformBallotFindMSB = 344, + SpvOpGroupNonUniformShuffle = 345, + SpvOpGroupNonUniformShuffleXor = 346, + SpvOpGroupNonUniformShuffleUp = 347, + SpvOpGroupNonUniformShuffleDown = 348, + SpvOpGroupNonUniformIAdd = 349, + SpvOpGroupNonUniformFAdd = 350, + SpvOpGroupNonUniformIMul = 351, + SpvOpGroupNonUniformFMul = 352, + SpvOpGroupNonUniformSMin = 353, + SpvOpGroupNonUniformUMin = 354, + SpvOpGroupNonUniformFMin = 355, + SpvOpGroupNonUniformSMax = 356, + SpvOpGroupNonUniformUMax = 357, + SpvOpGroupNonUniformFMax = 358, + SpvOpGroupNonUniformBitwiseAnd = 359, + SpvOpGroupNonUniformBitwiseOr = 360, + SpvOpGroupNonUniformBitwiseXor = 361, + SpvOpGroupNonUniformLogicalAnd = 362, + SpvOpGroupNonUniformLogicalOr = 363, + SpvOpGroupNonUniformLogicalXor = 364, + SpvOpGroupNonUniformQuadBroadcast = 365, + SpvOpGroupNonUniformQuadSwap = 366, + SpvOpCopyLogical = 400, + SpvOpPtrEqual = 401, + SpvOpPtrNotEqual = 402, + SpvOpPtrDiff = 403, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpTypeRayQueryProvisionalKHR = 4472, + SpvOpRayQueryInitializeKHR = 4473, + SpvOpRayQueryTerminateKHR = 4474, + SpvOpRayQueryGenerateIntersectionKHR = 4475, + SpvOpRayQueryConfirmIntersectionKHR = 4476, + SpvOpRayQueryProceedKHR = 4477, + SpvOpRayQueryGetIntersectionTypeKHR = 4479, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpReadClockKHR = 5056, + SpvOpImageSampleFootprintNV = 5283, + SpvOpGroupNonUniformPartitionNV = 5296, + SpvOpWritePackedPrimitiveIndices4x8NV = 5299, + SpvOpReportIntersectionKHR = 5334, + SpvOpReportIntersectionNV = 5334, + SpvOpIgnoreIntersectionKHR = 5335, + SpvOpIgnoreIntersectionNV = 5335, + SpvOpTerminateRayKHR = 5336, + SpvOpTerminateRayNV = 5336, + SpvOpTraceNV = 5337, + SpvOpTraceRayKHR = 5337, + SpvOpTypeAccelerationStructureKHR = 5341, + SpvOpTypeAccelerationStructureNV = 5341, + SpvOpExecuteCallableKHR = 5344, + SpvOpExecuteCallableNV = 5344, + SpvOpTypeCooperativeMatrixNV = 5358, + SpvOpCooperativeMatrixLoadNV = 5359, + SpvOpCooperativeMatrixStoreNV = 5360, + SpvOpCooperativeMatrixMulAddNV = 5361, + SpvOpCooperativeMatrixLengthNV = 5362, + SpvOpBeginInvocationInterlockEXT = 5364, + SpvOpEndInvocationInterlockEXT = 5365, + SpvOpDemoteToHelperInvocationEXT = 5380, + SpvOpIsHelperInvocationEXT = 5381, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpSubgroupImageMediaBlockReadINTEL = 5580, + SpvOpSubgroupImageMediaBlockWriteINTEL = 5581, + SpvOpUCountLeadingZerosINTEL = 5585, + SpvOpUCountTrailingZerosINTEL = 5586, + SpvOpAbsISubINTEL = 5587, + SpvOpAbsUSubINTEL = 5588, + SpvOpIAddSatINTEL = 5589, + SpvOpUAddSatINTEL = 5590, + SpvOpIAverageINTEL = 5591, + SpvOpUAverageINTEL = 5592, + SpvOpIAverageRoundedINTEL = 5593, + SpvOpUAverageRoundedINTEL = 5594, + SpvOpISubSatINTEL = 5595, + SpvOpUSubSatINTEL = 5596, + SpvOpIMul32x16INTEL = 5597, + SpvOpUMul32x16INTEL = 5598, + SpvOpDecorateString = 5632, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateString = 5633, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpVmeImageINTEL = 5699, + SpvOpTypeVmeImageINTEL = 5700, + SpvOpTypeAvcImePayloadINTEL = 5701, + SpvOpTypeAvcRefPayloadINTEL = 5702, + SpvOpTypeAvcSicPayloadINTEL = 5703, + SpvOpTypeAvcMcePayloadINTEL = 5704, + SpvOpTypeAvcMceResultINTEL = 5705, + SpvOpTypeAvcImeResultINTEL = 5706, + SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + SpvOpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + SpvOpTypeAvcImeDualReferenceStreaminINTEL = 5710, + SpvOpTypeAvcRefResultINTEL = 5711, + SpvOpTypeAvcSicResultINTEL = 5712, + SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + SpvOpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + SpvOpSubgroupAvcMceConvertToImeResultINTEL = 5733, + SpvOpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + SpvOpSubgroupAvcMceConvertToRefResultINTEL = 5735, + SpvOpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + SpvOpSubgroupAvcMceConvertToSicResultINTEL = 5737, + SpvOpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + SpvOpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + SpvOpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + SpvOpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + SpvOpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + SpvOpSubgroupAvcImeInitializeINTEL = 5747, + SpvOpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + SpvOpSubgroupAvcImeSetDualReferenceINTEL = 5749, + SpvOpSubgroupAvcImeRefWindowSizeINTEL = 5750, + SpvOpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + SpvOpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + SpvOpSubgroupAvcImeSetWeightedSadINTEL = 5756, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + SpvOpSubgroupAvcImeConvertToMceResultINTEL = 5765, + SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + SpvOpSubgroupAvcImeGetBorderReachedINTEL = 5776, + SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + SpvOpSubgroupAvcFmeInitializeINTEL = 5781, + SpvOpSubgroupAvcBmeInitializeINTEL = 5782, + SpvOpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + SpvOpSubgroupAvcRefConvertToMceResultINTEL = 5790, + SpvOpSubgroupAvcSicInitializeINTEL = 5791, + SpvOpSubgroupAvcSicConfigureSkcINTEL = 5792, + SpvOpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + SpvOpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + SpvOpSubgroupAvcSicEvaluateIpeINTEL = 5803, + SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + SpvOpSubgroupAvcSicConvertToMceResultINTEL = 5808, + SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + SpvOpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + SpvOpRayQueryGetRayTMinKHR = 6016, + SpvOpRayQueryGetRayFlagsKHR = 6017, + SpvOpRayQueryGetIntersectionTKHR = 6018, + SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020, + SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022, + SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024, + SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025, + SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + SpvOpRayQueryGetWorldRayDirectionKHR = 6029, + SpvOpRayQueryGetWorldRayOriginKHR = 6030, + SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031, + SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#ifdef SPV_ENABLE_UTILITY_CODE +inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case SpvOpNop: *hasResult = false; *hasResultType = false; break; + case SpvOpUndef: *hasResult = true; *hasResultType = true; break; + case SpvOpSourceContinued: *hasResult = false; *hasResultType = false; break; + case SpvOpSource: *hasResult = false; *hasResultType = false; break; + case SpvOpSourceExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpName: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberName: *hasResult = false; *hasResultType = false; break; + case SpvOpString: *hasResult = true; *hasResultType = false; break; + case SpvOpLine: *hasResult = false; *hasResultType = false; break; + case SpvOpExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpExtInstImport: *hasResult = true; *hasResultType = false; break; + case SpvOpExtInst: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryModel: *hasResult = false; *hasResultType = false; break; + case SpvOpEntryPoint: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionMode: *hasResult = false; *hasResultType = false; break; + case SpvOpCapability: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeVoid: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeBool: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeInt: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFloat: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeVector: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampler: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStruct: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePointer: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFunction: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeQueue: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePipe: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantSampler: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantNull: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case SpvOpFunction: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case SpvOpFunctionCall: *hasResult = true; *hasResultType = true; break; + case SpvOpVariable: *hasResult = true; *hasResultType = true; break; + case SpvOpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case SpvOpLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpStore: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemory: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case SpvOpAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpArrayLength: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case SpvOpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyObject: *hasResult = true; *hasResultType = true; break; + case SpvOpTranspose: *hasResult = true; *hasResultType = true; break; + case SpvOpSampledImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageRead: *hasResult = true; *hasResultType = true; break; + case SpvOpImageWrite: *hasResult = false; *hasResultType = false; break; + case SpvOpImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToU: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSToF: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToF: *hasResult = true; *hasResultType = true; break; + case SpvOpUConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpSConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpFConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case SpvOpBitcast: *hasResult = true; *hasResultType = true; break; + case SpvOpSNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpFNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpISub: *hasResult = true; *hasResultType = true; break; + case SpvOpFSub: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpUDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpSDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpFDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpUMod: *hasResult = true; *hasResultType = true; break; + case SpvOpSRem: *hasResult = true; *hasResultType = true; break; + case SpvOpSMod: *hasResult = true; *hasResultType = true; break; + case SpvOpFRem: *hasResult = true; *hasResultType = true; break; + case SpvOpFMod: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpOuterProduct: *hasResult = true; *hasResultType = true; break; + case SpvOpDot: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddCarry: *hasResult = true; *hasResultType = true; break; + case SpvOpISubBorrow: *hasResult = true; *hasResultType = true; break; + case SpvOpUMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpSMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpAny: *hasResult = true; *hasResultType = true; break; + case SpvOpAll: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNan: *hasResult = true; *hasResultType = true; break; + case SpvOpIsInf: *hasResult = true; *hasResultType = true; break; + case SpvOpIsFinite: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNormal: *hasResult = true; *hasResultType = true; break; + case SpvOpSignBitSet: *hasResult = true; *hasResultType = true; break; + case SpvOpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case SpvOpOrdered: *hasResult = true; *hasResultType = true; break; + case SpvOpUnordered: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNot: *hasResult = true; *hasResultType = true; break; + case SpvOpSelect: *hasResult = true; *hasResultType = true; break; + case SpvOpIEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpINotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpNot: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitReverse: *hasResult = true; *hasResultType = true; break; + case SpvOpBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdx: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdy: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidth: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyFine: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicStore: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicISub: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicOr: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicXor: *hasResult = true; *hasResultType = true; break; + case SpvOpPhi: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpLabel: *hasResult = true; *hasResultType = false; break; + case SpvOpBranch: *hasResult = false; *hasResultType = false; break; + case SpvOpBranchConditional: *hasResult = false; *hasResultType = false; break; + case SpvOpSwitch: *hasResult = false; *hasResultType = false; break; + case SpvOpKill: *hasResult = false; *hasResultType = false; break; + case SpvOpReturn: *hasResult = false; *hasResultType = false; break; + case SpvOpReturnValue: *hasResult = false; *hasResultType = false; break; + case SpvOpUnreachable: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case SpvOpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case SpvOpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case SpvOpRetainEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case SpvOpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case SpvOpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case SpvOpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case SpvOpNoLine: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case SpvOpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case SpvOpSizeOf: *hasResult = true; *hasResultType = true; break; + case SpvOpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case SpvOpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case SpvOpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorateId: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrDiff: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case SpvOpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case SpvOpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +#endif + diff --git a/third_party/spirv-cross/spirv.hpp b/third_party/spirv-cross/spirv.hpp new file mode 100644 index 0000000..dae36cf --- /dev/null +++ b/third_party/spirv-cross/spirv.hpp @@ -0,0 +1,2114 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10500 +#define SPV_REVISION 3 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010500; +static const unsigned int Revision = 3; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelTaskNV = 5267, + ExecutionModelMeshNV = 5268, + ExecutionModelRayGenerationKHR = 5313, + ExecutionModelRayGenerationNV = 5313, + ExecutionModelIntersectionKHR = 5314, + ExecutionModelIntersectionNV = 5314, + ExecutionModelAnyHitKHR = 5315, + ExecutionModelAnyHitNV = 5315, + ExecutionModelClosestHitKHR = 5316, + ExecutionModelClosestHitNV = 5316, + ExecutionModelMissKHR = 5317, + ExecutionModelMissNV = 5317, + ExecutionModelCallableKHR = 5318, + ExecutionModelCallableNV = 5318, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelPhysicalStorageBuffer64 = 5348, + AddressingModelPhysicalStorageBuffer64EXT = 5348, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelVulkan = 3, + MemoryModelVulkanKHR = 3, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeDenormPreserve = 4459, + ExecutionModeDenormFlushToZero = 4460, + ExecutionModeSignedZeroInfNanPreserve = 4461, + ExecutionModeRoundingModeRTE = 4462, + ExecutionModeRoundingModeRTZ = 4463, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeOutputLinesNV = 5269, + ExecutionModeOutputPrimitivesNV = 5270, + ExecutionModeDerivativeGroupQuadsNV = 5289, + ExecutionModeDerivativeGroupLinearNV = 5290, + ExecutionModeOutputTrianglesNV = 5298, + ExecutionModePixelInterlockOrderedEXT = 5366, + ExecutionModePixelInterlockUnorderedEXT = 5367, + ExecutionModeSampleInterlockOrderedEXT = 5368, + ExecutionModeSampleInterlockUnorderedEXT = 5369, + ExecutionModeShadingRateInterlockOrderedEXT = 5370, + ExecutionModeShadingRateInterlockUnorderedEXT = 5371, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassCallableDataKHR = 5328, + StorageClassCallableDataNV = 5328, + StorageClassIncomingCallableDataKHR = 5329, + StorageClassIncomingCallableDataNV = 5329, + StorageClassRayPayloadKHR = 5338, + StorageClassRayPayloadNV = 5338, + StorageClassHitAttributeKHR = 5339, + StorageClassHitAttributeNV = 5339, + StorageClassIncomingRayPayloadKHR = 5342, + StorageClassIncomingRayPayloadNV = 5342, + StorageClassShaderRecordBufferKHR = 5343, + StorageClassShaderRecordBufferNV = 5343, + StorageClassPhysicalStorageBuffer = 5349, + StorageClassPhysicalStorageBufferEXT = 5349, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMakeTexelAvailableShift = 8, + ImageOperandsMakeTexelAvailableKHRShift = 8, + ImageOperandsMakeTexelVisibleShift = 9, + ImageOperandsMakeTexelVisibleKHRShift = 9, + ImageOperandsNonPrivateTexelShift = 10, + ImageOperandsNonPrivateTexelKHRShift = 10, + ImageOperandsVolatileTexelShift = 11, + ImageOperandsVolatileTexelKHRShift = 11, + ImageOperandsSignExtendShift = 12, + ImageOperandsZeroExtendShift = 13, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, + ImageOperandsMakeTexelAvailableMask = 0x00000100, + ImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + ImageOperandsMakeTexelVisibleMask = 0x00000200, + ImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + ImageOperandsNonPrivateTexelMask = 0x00000400, + ImageOperandsNonPrivateTexelKHRMask = 0x00000400, + ImageOperandsVolatileTexelMask = 0x00000800, + ImageOperandsVolatileTexelKHRMask = 0x00000800, + ImageOperandsSignExtendMask = 0x00001000, + ImageOperandsZeroExtendMask = 0x00002000, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationUniformId = 27, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationNoSignedWrap = 4469, + DecorationNoUnsignedWrap = 4470, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationPerPrimitiveNV = 5271, + DecorationPerViewNV = 5272, + DecorationPerTaskNV = 5273, + DecorationPerVertexNV = 5285, + DecorationNonUniform = 5300, + DecorationNonUniformEXT = 5300, + DecorationRestrictPointer = 5355, + DecorationRestrictPointerEXT = 5355, + DecorationAliasedPointer = 5356, + DecorationAliasedPointerEXT = 5356, + DecorationCounterBuffer = 5634, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationUserSemantic = 5635, + DecorationUserTypeGOOGLE = 5636, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMask = 4416, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMask = 4417, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMask = 4418, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMask = 4419, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMask = 4420, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInFullyCoveredEXT = 5264, + BuiltInTaskCountNV = 5274, + BuiltInPrimitiveCountNV = 5275, + BuiltInPrimitiveIndicesNV = 5276, + BuiltInClipDistancePerViewNV = 5277, + BuiltInCullDistancePerViewNV = 5278, + BuiltInLayerPerViewNV = 5279, + BuiltInMeshViewCountNV = 5280, + BuiltInMeshViewIndicesNV = 5281, + BuiltInBaryCoordNV = 5286, + BuiltInBaryCoordNoPerspNV = 5287, + BuiltInFragSizeEXT = 5292, + BuiltInFragmentSizeNV = 5292, + BuiltInFragInvocationCountEXT = 5293, + BuiltInInvocationsPerPixelNV = 5293, + BuiltInLaunchIdKHR = 5319, + BuiltInLaunchIdNV = 5319, + BuiltInLaunchSizeKHR = 5320, + BuiltInLaunchSizeNV = 5320, + BuiltInWorldRayOriginKHR = 5321, + BuiltInWorldRayOriginNV = 5321, + BuiltInWorldRayDirectionKHR = 5322, + BuiltInWorldRayDirectionNV = 5322, + BuiltInObjectRayOriginKHR = 5323, + BuiltInObjectRayOriginNV = 5323, + BuiltInObjectRayDirectionKHR = 5324, + BuiltInObjectRayDirectionNV = 5324, + BuiltInRayTminKHR = 5325, + BuiltInRayTminNV = 5325, + BuiltInRayTmaxKHR = 5326, + BuiltInRayTmaxNV = 5326, + BuiltInInstanceCustomIndexKHR = 5327, + BuiltInInstanceCustomIndexNV = 5327, + BuiltInObjectToWorldKHR = 5330, + BuiltInObjectToWorldNV = 5330, + BuiltInWorldToObjectKHR = 5331, + BuiltInWorldToObjectNV = 5331, + BuiltInHitTKHR = 5332, + BuiltInHitTNV = 5332, + BuiltInHitKindKHR = 5333, + BuiltInHitKindNV = 5333, + BuiltInIncomingRayFlagsKHR = 5351, + BuiltInIncomingRayFlagsNV = 5351, + BuiltInRayGeometryIndexKHR = 5352, + BuiltInWarpsPerSMNV = 5374, + BuiltInSMCountNV = 5375, + BuiltInWarpIDNV = 5376, + BuiltInSMIDNV = 5377, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMinIterationsShift = 4, + LoopControlMaxIterationsShift = 5, + LoopControlIterationMultipleShift = 6, + LoopControlPeelCountShift = 7, + LoopControlPartialCountShift = 8, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, + LoopControlMinIterationsMask = 0x00000010, + LoopControlMaxIterationsMask = 0x00000020, + LoopControlIterationMultipleMask = 0x00000040, + LoopControlPeelCountMask = 0x00000080, + LoopControlPartialCountMask = 0x00000100, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsOutputMemoryShift = 12, + MemorySemanticsOutputMemoryKHRShift = 12, + MemorySemanticsMakeAvailableShift = 13, + MemorySemanticsMakeAvailableKHRShift = 13, + MemorySemanticsMakeVisibleShift = 14, + MemorySemanticsMakeVisibleKHRShift = 14, + MemorySemanticsVolatileShift = 15, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, + MemorySemanticsOutputMemoryMask = 0x00001000, + MemorySemanticsOutputMemoryKHRMask = 0x00001000, + MemorySemanticsMakeAvailableMask = 0x00002000, + MemorySemanticsMakeAvailableKHRMask = 0x00002000, + MemorySemanticsMakeVisibleMask = 0x00004000, + MemorySemanticsMakeVisibleKHRMask = 0x00004000, + MemorySemanticsVolatileMask = 0x00008000, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMakePointerAvailableShift = 3, + MemoryAccessMakePointerAvailableKHRShift = 3, + MemoryAccessMakePointerVisibleShift = 4, + MemoryAccessMakePointerVisibleKHRShift = 4, + MemoryAccessNonPrivatePointerShift = 5, + MemoryAccessNonPrivatePointerKHRShift = 5, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, + MemoryAccessMakePointerAvailableMask = 0x00000008, + MemoryAccessMakePointerAvailableKHRMask = 0x00000008, + MemoryAccessMakePointerVisibleMask = 0x00000010, + MemoryAccessMakePointerVisibleKHRMask = 0x00000010, + MemoryAccessNonPrivatePointerMask = 0x00000020, + MemoryAccessNonPrivatePointerKHRMask = 0x00000020, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeQueueFamily = 5, + ScopeQueueFamilyKHR = 5, + ScopeShaderCallKHR = 6, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationClusteredReduce = 3, + GroupOperationPartitionedReduceNV = 6, + GroupOperationPartitionedInclusiveScanNV = 7, + GroupOperationPartitionedExclusiveScanNV = 8, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilityGroupNonUniform = 61, + CapabilityGroupNonUniformVote = 62, + CapabilityGroupNonUniformArithmetic = 63, + CapabilityGroupNonUniformBallot = 64, + CapabilityGroupNonUniformShuffle = 65, + CapabilityGroupNonUniformShuffleRelative = 66, + CapabilityGroupNonUniformClustered = 67, + CapabilityGroupNonUniformQuad = 68, + CapabilityShaderLayer = 69, + CapabilityShaderViewportIndex = 70, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityStorageBuffer8BitAccess = 4448, + CapabilityUniformAndStorageBuffer8BitAccess = 4449, + CapabilityStoragePushConstant8 = 4450, + CapabilityDenormPreserve = 4464, + CapabilityDenormFlushToZero = 4465, + CapabilitySignedZeroInfNanPreserve = 4466, + CapabilityRoundingModeRTE = 4467, + CapabilityRoundingModeRTZ = 4468, + CapabilityRayQueryProvisionalKHR = 4471, + CapabilityRayTraversalPrimitiveCullingProvisionalKHR = 4478, + CapabilityFloat16ImageAMD = 5008, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilityShaderClockKHR = 5055, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilityFragmentFullyCoveredEXT = 5265, + CapabilityMeshShadingNV = 5266, + CapabilityImageFootprintNV = 5282, + CapabilityFragmentBarycentricNV = 5284, + CapabilityComputeDerivativeGroupQuadsNV = 5288, + CapabilityFragmentDensityEXT = 5291, + CapabilityShadingRateNV = 5291, + CapabilityGroupNonUniformPartitionedNV = 5297, + CapabilityShaderNonUniform = 5301, + CapabilityShaderNonUniformEXT = 5301, + CapabilityRuntimeDescriptorArray = 5302, + CapabilityRuntimeDescriptorArrayEXT = 5302, + CapabilityInputAttachmentArrayDynamicIndexing = 5303, + CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + CapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + CapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + CapabilityUniformBufferArrayNonUniformIndexing = 5306, + CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + CapabilitySampledImageArrayNonUniformIndexing = 5307, + CapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + CapabilityStorageBufferArrayNonUniformIndexing = 5308, + CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + CapabilityStorageImageArrayNonUniformIndexing = 5309, + CapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + CapabilityInputAttachmentArrayNonUniformIndexing = 5310, + CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + CapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + CapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + CapabilityRayTracingNV = 5340, + CapabilityVulkanMemoryModel = 5345, + CapabilityVulkanMemoryModelKHR = 5345, + CapabilityVulkanMemoryModelDeviceScope = 5346, + CapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + CapabilityPhysicalStorageBufferAddresses = 5347, + CapabilityPhysicalStorageBufferAddressesEXT = 5347, + CapabilityComputeDerivativeGroupLinearNV = 5350, + CapabilityRayTracingProvisionalKHR = 5353, + CapabilityCooperativeMatrixNV = 5357, + CapabilityFragmentShaderSampleInterlockEXT = 5363, + CapabilityFragmentShaderShadingRateInterlockEXT = 5372, + CapabilityShaderSMBuiltinsNV = 5373, + CapabilityFragmentShaderPixelInterlockEXT = 5378, + CapabilityDemoteToHelperInvocationEXT = 5379, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilitySubgroupImageMediaBlockIOINTEL = 5579, + CapabilityIntegerFunctions2INTEL = 5584, + CapabilitySubgroupAvcMotionEstimationINTEL = 5696, + CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + CapabilityMax = 0x7fffffff, +}; + +enum RayFlagsShift { + RayFlagsOpaqueKHRShift = 0, + RayFlagsNoOpaqueKHRShift = 1, + RayFlagsTerminateOnFirstHitKHRShift = 2, + RayFlagsSkipClosestHitShaderKHRShift = 3, + RayFlagsCullBackFacingTrianglesKHRShift = 4, + RayFlagsCullFrontFacingTrianglesKHRShift = 5, + RayFlagsCullOpaqueKHRShift = 6, + RayFlagsCullNoOpaqueKHRShift = 7, + RayFlagsSkipTrianglesKHRShift = 8, + RayFlagsSkipAABBsKHRShift = 9, + RayFlagsMax = 0x7fffffff, +}; + +enum RayFlagsMask { + RayFlagsMaskNone = 0, + RayFlagsOpaqueKHRMask = 0x00000001, + RayFlagsNoOpaqueKHRMask = 0x00000002, + RayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + RayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + RayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + RayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + RayFlagsCullOpaqueKHRMask = 0x00000040, + RayFlagsCullNoOpaqueKHRMask = 0x00000080, + RayFlagsSkipTrianglesKHRMask = 0x00000100, + RayFlagsSkipAABBsKHRMask = 0x00000200, +}; + +enum RayQueryIntersection { + RayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + RayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + RayQueryIntersectionMax = 0x7fffffff, +}; + +enum RayQueryCommittedIntersectionType { + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + RayQueryCommittedIntersectionTypeMax = 0x7fffffff, +}; + +enum RayQueryCandidateIntersectionType { + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + RayQueryCandidateIntersectionTypeMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpMax = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case OpNop: *hasResult = false; *hasResultType = false; break; + case OpUndef: *hasResult = true; *hasResultType = true; break; + case OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case OpSource: *hasResult = false; *hasResultType = false; break; + case OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case OpName: *hasResult = false; *hasResultType = false; break; + case OpMemberName: *hasResult = false; *hasResultType = false; break; + case OpString: *hasResult = true; *hasResultType = false; break; + case OpLine: *hasResult = false; *hasResultType = false; break; + case OpExtension: *hasResult = false; *hasResultType = false; break; + case OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case OpExtInst: *hasResult = true; *hasResultType = true; break; + case OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case OpCapability: *hasResult = false; *hasResultType = false; break; + case OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case OpTypeBool: *hasResult = true; *hasResultType = false; break; + case OpTypeInt: *hasResult = true; *hasResultType = false; break; + case OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case OpTypeVector: *hasResult = true; *hasResultType = false; break; + case OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case OpTypeImage: *hasResult = true; *hasResultType = false; break; + case OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case OpTypeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case OpTypePointer: *hasResult = true; *hasResultType = false; break; + case OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case OpTypePipe: *hasResult = true; *hasResultType = false; break; + case OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpConstant: *hasResult = true; *hasResultType = true; break; + case OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case OpConstantNull: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case OpFunction: *hasResult = true; *hasResultType = true; break; + case OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case OpVariable: *hasResult = true; *hasResultType = true; break; + case OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case OpLoad: *hasResult = true; *hasResultType = true; break; + case OpStore: *hasResult = false; *hasResultType = false; break; + case OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case OpAccessChain: *hasResult = true; *hasResultType = true; break; + case OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpArrayLength: *hasResult = true; *hasResultType = true; break; + case OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpDecorate: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case OpCopyObject: *hasResult = true; *hasResultType = true; break; + case OpTranspose: *hasResult = true; *hasResultType = true; break; + case OpSampledImage: *hasResult = true; *hasResultType = true; break; + case OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageFetch: *hasResult = true; *hasResultType = true; break; + case OpImageGather: *hasResult = true; *hasResultType = true; break; + case OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageRead: *hasResult = true; *hasResultType = true; break; + case OpImageWrite: *hasResult = false; *hasResultType = false; break; + case OpImage: *hasResult = true; *hasResultType = true; break; + case OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case OpUConvert: *hasResult = true; *hasResultType = true; break; + case OpSConvert: *hasResult = true; *hasResultType = true; break; + case OpFConvert: *hasResult = true; *hasResultType = true; break; + case OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case OpBitcast: *hasResult = true; *hasResultType = true; break; + case OpSNegate: *hasResult = true; *hasResultType = true; break; + case OpFNegate: *hasResult = true; *hasResultType = true; break; + case OpIAdd: *hasResult = true; *hasResultType = true; break; + case OpFAdd: *hasResult = true; *hasResultType = true; break; + case OpISub: *hasResult = true; *hasResultType = true; break; + case OpFSub: *hasResult = true; *hasResultType = true; break; + case OpIMul: *hasResult = true; *hasResultType = true; break; + case OpFMul: *hasResult = true; *hasResultType = true; break; + case OpUDiv: *hasResult = true; *hasResultType = true; break; + case OpSDiv: *hasResult = true; *hasResultType = true; break; + case OpFDiv: *hasResult = true; *hasResultType = true; break; + case OpUMod: *hasResult = true; *hasResultType = true; break; + case OpSRem: *hasResult = true; *hasResultType = true; break; + case OpSMod: *hasResult = true; *hasResultType = true; break; + case OpFRem: *hasResult = true; *hasResultType = true; break; + case OpFMod: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case OpDot: *hasResult = true; *hasResultType = true; break; + case OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case OpAny: *hasResult = true; *hasResultType = true; break; + case OpAll: *hasResult = true; *hasResultType = true; break; + case OpIsNan: *hasResult = true; *hasResultType = true; break; + case OpIsInf: *hasResult = true; *hasResultType = true; break; + case OpIsFinite: *hasResult = true; *hasResultType = true; break; + case OpIsNormal: *hasResult = true; *hasResultType = true; break; + case OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case OpOrdered: *hasResult = true; *hasResultType = true; break; + case OpUnordered: *hasResult = true; *hasResultType = true; break; + case OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case OpSelect: *hasResult = true; *hasResultType = true; break; + case OpIEqual: *hasResult = true; *hasResultType = true; break; + case OpINotEqual: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpULessThan: *hasResult = true; *hasResultType = true; break; + case OpSLessThan: *hasResult = true; *hasResultType = true; break; + case OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpNot: *hasResult = true; *hasResultType = true; break; + case OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case OpBitReverse: *hasResult = true; *hasResultType = true; break; + case OpBitCount: *hasResult = true; *hasResultType = true; break; + case OpDPdx: *hasResult = true; *hasResultType = true; break; + case OpDPdy: *hasResult = true; *hasResultType = true; break; + case OpFwidth: *hasResult = true; *hasResultType = true; break; + case OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case OpPhi: *hasResult = true; *hasResultType = true; break; + case OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case OpLabel: *hasResult = true; *hasResultType = false; break; + case OpBranch: *hasResult = false; *hasResultType = false; break; + case OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case OpSwitch: *hasResult = false; *hasResultType = false; break; + case OpKill: *hasResult = false; *hasResultType = false; break; + case OpReturn: *hasResult = false; *hasResultType = false; break; + case OpReturnValue: *hasResult = false; *hasResultType = false; break; + case OpUnreachable: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case OpGroupAll: *hasResult = true; *hasResultType = true; break; + case OpGroupAny: *hasResult = true; *hasResultType = true; break; + case OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case OpReadPipe: *hasResult = true; *hasResultType = true; break; + case OpWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case OpNoLine: *hasResult = false; *hasResultType = false; break; + case OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case OpSizeOf: *hasResult = true; *hasResultType = true; break; + case OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case OpDecorateId: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case OpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case OpTraceNV: *hasResult = false; *hasResultType = false; break; + case OpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; + case OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpDecorateString: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-cross/spirv_cfg.cpp b/third_party/spirv-cross/spirv_cfg.cpp new file mode 100644 index 0000000..9973fba --- /dev/null +++ b/third_party/spirv-cross/spirv_cfg.cpp @@ -0,0 +1,397 @@ +/* + * Copyright 2016-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cfg.hpp" +#include "spirv_cross.hpp" +#include +#include + +using namespace std; + +namespace SPIRV_CROSS_NAMESPACE +{ +CFG::CFG(Compiler &compiler_, const SPIRFunction &func_) + : compiler(compiler_) + , func(func_) +{ + build_post_order_visit_order(); + build_immediate_dominators(); +} + +uint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const +{ + while (a != b) + { + if (get_visit_order(a) < get_visit_order(b)) + a = get_immediate_dominator(a); + else + b = get_immediate_dominator(b); + } + return a; +} + +void CFG::build_immediate_dominators() +{ + // Traverse the post-order in reverse and build up the immediate dominator tree. + immediate_dominators.clear(); + immediate_dominators[func.entry_block] = func.entry_block; + + for (auto i = post_order.size(); i; i--) + { + uint32_t block = post_order[i - 1]; + auto &pred = preceding_edges[block]; + if (pred.empty()) // This is for the entry block, but we've already set up the dominators. + continue; + + for (auto &edge : pred) + { + if (immediate_dominators[block]) + { + assert(immediate_dominators[edge]); + immediate_dominators[block] = find_common_dominator(immediate_dominators[block], edge); + } + else + immediate_dominators[block] = edge; + } + } +} + +bool CFG::is_back_edge(uint32_t to) const +{ + // We have a back edge if the visit order is set with the temporary magic value 0. + // Crossing edges will have already been recorded with a visit order. + auto itr = visit_order.find(to); + return itr != end(visit_order) && itr->second.get() == 0; +} + +bool CFG::has_visited_forward_edge(uint32_t to) const +{ + // If > 0, we have visited the edge already, and this is not a back edge branch. + auto itr = visit_order.find(to); + return itr != end(visit_order) && itr->second.get() > 0; +} + +bool CFG::post_order_visit(uint32_t block_id) +{ + // If we have already branched to this block (back edge), stop recursion. + // If our branches are back-edges, we do not record them. + // We have to record crossing edges however. + if (has_visited_forward_edge(block_id)) + return true; + else if (is_back_edge(block_id)) + return false; + + // Block back-edges from recursively revisiting ourselves. + visit_order[block_id].get() = 0; + + auto &block = compiler.get(block_id); + + // If this is a loop header, add an implied branch to the merge target. + // This is needed to avoid annoying cases with do { ... } while(false) loops often generated by inliners. + // To the CFG, this is linear control flow, but we risk picking the do/while scope as our dominating block. + // This makes sure that if we are accessing a variable outside the do/while, we choose the loop header as dominator. + // We could use has_visited_forward_edge, but this break code-gen where the merge block is unreachable in the CFG. + + // Make a point out of visiting merge target first. This is to make sure that post visit order outside the loop + // is lower than inside the loop, which is going to be key for some traversal algorithms like post-dominance analysis. + // For selection constructs true/false blocks will end up visiting the merge block directly and it works out fine, + // but for loops, only the header might end up actually branching to merge block. + if (block.merge == SPIRBlock::MergeLoop && post_order_visit(block.merge_block)) + add_branch(block_id, block.merge_block); + + // First visit our branch targets. + switch (block.terminator) + { + case SPIRBlock::Direct: + if (post_order_visit(block.next_block)) + add_branch(block_id, block.next_block); + break; + + case SPIRBlock::Select: + if (post_order_visit(block.true_block)) + add_branch(block_id, block.true_block); + if (post_order_visit(block.false_block)) + add_branch(block_id, block.false_block); + break; + + case SPIRBlock::MultiSelect: + for (auto &target : block.cases) + { + if (post_order_visit(target.block)) + add_branch(block_id, target.block); + } + if (block.default_block && post_order_visit(block.default_block)) + add_branch(block_id, block.default_block); + break; + + default: + break; + } + + // If this is a selection merge, add an implied branch to the merge target. + // This is needed to avoid cases where an inner branch dominates the outer branch. + // This can happen if one of the branches exit early, e.g.: + // if (cond) { ...; break; } else { var = 100 } use_var(var); + // We can use the variable without a Phi since there is only one possible parent here. + // However, in this case, we need to hoist out the inner variable to outside the branch. + // Use same strategy as loops. + if (block.merge == SPIRBlock::MergeSelection && post_order_visit(block.next_block)) + { + // If there is only one preceding edge to the merge block and it's not ourselves, we need a fixup. + // Add a fake branch so any dominator in either the if (), or else () block, or a lone case statement + // will be hoisted out to outside the selection merge. + // If size > 1, the variable will be automatically hoisted, so we should not mess with it. + // The exception here is switch blocks, where we can have multiple edges to merge block, + // all coming from same scope, so be more conservative in this case. + // Adding fake branches unconditionally breaks parameter preservation analysis, + // which looks at how variables are accessed through the CFG. + auto pred_itr = preceding_edges.find(block.next_block); + if (pred_itr != end(preceding_edges)) + { + auto &pred = pred_itr->second; + auto succ_itr = succeeding_edges.find(block_id); + size_t num_succeeding_edges = 0; + if (succ_itr != end(succeeding_edges)) + num_succeeding_edges = succ_itr->second.size(); + + if (block.terminator == SPIRBlock::MultiSelect && num_succeeding_edges == 1) + { + // Multiple branches can come from the same scope due to "break;", so we need to assume that all branches + // come from same case scope in worst case, even if there are multiple preceding edges. + // If we have more than one succeeding edge from the block header, it should be impossible + // to have a dominator be inside the block. + // Only case this can go wrong is if we have 2 or more edges from block header and + // 2 or more edges to merge block, and still have dominator be inside a case label. + if (!pred.empty()) + add_branch(block_id, block.next_block); + } + else + { + if (pred.size() == 1 && *pred.begin() != block_id) + add_branch(block_id, block.next_block); + } + } + else + { + // If the merge block does not have any preceding edges, i.e. unreachable, hallucinate it. + // We're going to do code-gen for it, and domination analysis requires that we have at least one preceding edge. + add_branch(block_id, block.next_block); + } + } + + // Then visit ourselves. Start counting at one, to let 0 be a magic value for testing back vs. crossing edges. + visit_order[block_id].get() = ++visit_count; + post_order.push_back(block_id); + return true; +} + +void CFG::build_post_order_visit_order() +{ + uint32_t block = func.entry_block; + visit_count = 0; + visit_order.clear(); + post_order.clear(); + post_order_visit(block); +} + +void CFG::add_branch(uint32_t from, uint32_t to) +{ + const auto add_unique = [](SmallVector &l, uint32_t value) { + auto itr = find(begin(l), end(l), value); + if (itr == end(l)) + l.push_back(value); + }; + add_unique(preceding_edges[to], from); + add_unique(succeeding_edges[from], to); +} + +uint32_t CFG::find_loop_dominator(uint32_t block_id) const +{ + while (block_id != SPIRBlock::NoDominator) + { + auto itr = preceding_edges.find(block_id); + if (itr == end(preceding_edges)) + return SPIRBlock::NoDominator; + if (itr->second.empty()) + return SPIRBlock::NoDominator; + + uint32_t pred_block_id = SPIRBlock::NoDominator; + bool ignore_loop_header = false; + + // If we are a merge block, go directly to the header block. + // Only consider a loop dominator if we are branching from inside a block to a loop header. + // NOTE: In the CFG we forced an edge from header to merge block always to support variable scopes properly. + for (auto &pred : itr->second) + { + auto &pred_block = compiler.get(pred); + if (pred_block.merge == SPIRBlock::MergeLoop && pred_block.merge_block == ID(block_id)) + { + pred_block_id = pred; + ignore_loop_header = true; + break; + } + else if (pred_block.merge == SPIRBlock::MergeSelection && pred_block.next_block == ID(block_id)) + { + pred_block_id = pred; + break; + } + } + + // No merge block means we can just pick any edge. Loop headers dominate the inner loop, so any path we + // take will lead there. + if (pred_block_id == SPIRBlock::NoDominator) + pred_block_id = itr->second.front(); + + block_id = pred_block_id; + + if (!ignore_loop_header && block_id) + { + auto &block = compiler.get(block_id); + if (block.merge == SPIRBlock::MergeLoop) + return block_id; + } + } + + return block_id; +} + +bool CFG::node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const +{ + // Walk backwards, starting from "to" block. + // Only follow pred edges if they have a 1:1 relationship, or a merge relationship. + // If we cannot find a path to "from", we must assume that to is inside control flow in some way. + + auto &from_block = compiler.get(from); + BlockID ignore_block_id = 0; + if (from_block.merge == SPIRBlock::MergeLoop) + ignore_block_id = from_block.merge_block; + + while (to != from) + { + auto pred_itr = preceding_edges.find(to); + if (pred_itr == end(preceding_edges)) + return false; + + DominatorBuilder builder(*this); + for (auto &edge : pred_itr->second) + builder.add_block(edge); + + uint32_t dominator = builder.get_dominator(); + if (dominator == 0) + return false; + + auto &dom = compiler.get(dominator); + + bool true_path_ignore = false; + bool false_path_ignore = false; + if (ignore_block_id && dom.terminator == SPIRBlock::Select) + { + auto &true_block = compiler.get(dom.true_block); + auto &false_block = compiler.get(dom.false_block); + auto &ignore_block = compiler.get(ignore_block_id); + true_path_ignore = compiler.execution_is_branchless(true_block, ignore_block); + false_path_ignore = compiler.execution_is_branchless(false_block, ignore_block); + } + + if ((dom.merge == SPIRBlock::MergeSelection && dom.next_block == to) || + (dom.merge == SPIRBlock::MergeLoop && dom.merge_block == to) || + (dom.terminator == SPIRBlock::Direct && dom.next_block == to) || + (dom.terminator == SPIRBlock::Select && dom.true_block == to && false_path_ignore) || + (dom.terminator == SPIRBlock::Select && dom.false_block == to && true_path_ignore)) + { + // Allow walking selection constructs if the other branch reaches out of a loop construct. + // It cannot be in-scope anymore. + to = dominator; + } + else + return false; + } + + return true; +} + +DominatorBuilder::DominatorBuilder(const CFG &cfg_) + : cfg(cfg_) +{ +} + +void DominatorBuilder::add_block(uint32_t block) +{ + if (!cfg.get_immediate_dominator(block)) + { + // Unreachable block via the CFG, we will never emit this code anyways. + return; + } + + if (!dominator) + { + dominator = block; + return; + } + + if (block != dominator) + dominator = cfg.find_common_dominator(block, dominator); +} + +void DominatorBuilder::lift_continue_block_dominator() +{ + // It is possible for a continue block to be the dominator of a variable is only accessed inside the while block of a do-while loop. + // We cannot safely declare variables inside a continue block, so move any variable declared + // in a continue block to the entry block to simplify. + // It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest + // solution. + + if (!dominator) + return; + + auto &block = cfg.get_compiler().get(dominator); + auto post_order = cfg.get_visit_order(dominator); + + // If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem + // since we cannot create sensible GLSL code for this, fallback to entry block. + bool back_edge_dominator = false; + switch (block.terminator) + { + case SPIRBlock::Direct: + if (cfg.get_visit_order(block.next_block) > post_order) + back_edge_dominator = true; + break; + + case SPIRBlock::Select: + if (cfg.get_visit_order(block.true_block) > post_order) + back_edge_dominator = true; + if (cfg.get_visit_order(block.false_block) > post_order) + back_edge_dominator = true; + break; + + case SPIRBlock::MultiSelect: + for (auto &target : block.cases) + { + if (cfg.get_visit_order(target.block) > post_order) + back_edge_dominator = true; + } + if (block.default_block && cfg.get_visit_order(block.default_block) > post_order) + back_edge_dominator = true; + break; + + default: + break; + } + + if (back_edge_dominator) + dominator = cfg.get_function().entry_block; +} +} // namespace SPIRV_CROSS_NAMESPACE diff --git a/third_party/spirv-cross/spirv_cfg.hpp b/third_party/spirv-cross/spirv_cfg.hpp new file mode 100644 index 0000000..9f3e62a --- /dev/null +++ b/third_party/spirv-cross/spirv_cfg.hpp @@ -0,0 +1,156 @@ +/* + * Copyright 2016-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_CFG_HPP +#define SPIRV_CROSS_CFG_HPP + +#include "spirv_common.hpp" +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +class Compiler; +class CFG +{ +public: + CFG(Compiler &compiler, const SPIRFunction &function); + + Compiler &get_compiler() + { + return compiler; + } + + const Compiler &get_compiler() const + { + return compiler; + } + + const SPIRFunction &get_function() const + { + return func; + } + + uint32_t get_immediate_dominator(uint32_t block) const + { + auto itr = immediate_dominators.find(block); + if (itr != std::end(immediate_dominators)) + return itr->second; + else + return 0; + } + + uint32_t get_visit_order(uint32_t block) const + { + auto itr = visit_order.find(block); + assert(itr != std::end(visit_order)); + int v = itr->second.get(); + assert(v > 0); + return uint32_t(v); + } + + uint32_t find_common_dominator(uint32_t a, uint32_t b) const; + + const SmallVector &get_preceding_edges(uint32_t block) const + { + auto itr = preceding_edges.find(block); + if (itr != std::end(preceding_edges)) + return itr->second; + else + return empty_vector; + } + + const SmallVector &get_succeeding_edges(uint32_t block) const + { + auto itr = succeeding_edges.find(block); + if (itr != std::end(succeeding_edges)) + return itr->second; + else + return empty_vector; + } + + template + void walk_from(std::unordered_set &seen_blocks, uint32_t block, const Op &op) const + { + if (seen_blocks.count(block)) + return; + seen_blocks.insert(block); + + if (op(block)) + { + for (auto b : get_succeeding_edges(block)) + walk_from(seen_blocks, b, op); + } + } + + uint32_t find_loop_dominator(uint32_t block) const; + + bool node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const; + +private: + struct VisitOrder + { + int &get() + { + return v; + } + + const int &get() const + { + return v; + } + + int v = -1; + }; + + Compiler &compiler; + const SPIRFunction &func; + std::unordered_map> preceding_edges; + std::unordered_map> succeeding_edges; + std::unordered_map immediate_dominators; + std::unordered_map visit_order; + SmallVector post_order; + SmallVector empty_vector; + + void add_branch(uint32_t from, uint32_t to); + void build_post_order_visit_order(); + void build_immediate_dominators(); + bool post_order_visit(uint32_t block); + uint32_t visit_count = 0; + + bool is_back_edge(uint32_t to) const; + bool has_visited_forward_edge(uint32_t to) const; +}; + +class DominatorBuilder +{ +public: + DominatorBuilder(const CFG &cfg); + + void add_block(uint32_t block); + uint32_t get_dominator() const + { + return dominator; + } + + void lift_continue_block_dominator(); + +private: + const CFG &cfg; + uint32_t dominator = 0; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_common.hpp b/third_party/spirv-cross/spirv_common.hpp new file mode 100644 index 0000000..d13b55e --- /dev/null +++ b/third_party/spirv-cross/spirv_common.hpp @@ -0,0 +1,1798 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_COMMON_HPP +#define SPIRV_CROSS_COMMON_HPP + +#include "spirv.hpp" +#include "spirv_cross_containers.hpp" +#include "spirv_cross_error_handling.hpp" +#include + +// A bit crude, but allows projects which embed SPIRV-Cross statically to +// effectively hide all the symbols from other projects. +// There is a case where we have: +// - Project A links against SPIRV-Cross statically. +// - Project A links against Project B statically. +// - Project B links against SPIRV-Cross statically (might be a different version). +// This leads to a conflict with extremely bizarre results. +// By overriding the namespace in one of the project builds, we can work around this. +// If SPIRV-Cross is embedded in dynamic libraries, +// prefer using -fvisibility=hidden on GCC/Clang instead. +#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE +#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE +#else +#define SPIRV_CROSS_NAMESPACE spirv_cross +#endif + +namespace SPIRV_CROSS_NAMESPACE +{ +namespace inner +{ +template +void join_helper(StringStream<> &stream, T &&t) +{ + stream << std::forward(t); +} + +template +void join_helper(StringStream<> &stream, T &&t, Ts &&... ts) +{ + stream << std::forward(t); + join_helper(stream, std::forward(ts)...); +} +} // namespace inner + +class Bitset +{ +public: + Bitset() = default; + explicit inline Bitset(uint64_t lower_) + : lower(lower_) + { + } + + inline bool get(uint32_t bit) const + { + if (bit < 64) + return (lower & (1ull << bit)) != 0; + else + return higher.count(bit) != 0; + } + + inline void set(uint32_t bit) + { + if (bit < 64) + lower |= 1ull << bit; + else + higher.insert(bit); + } + + inline void clear(uint32_t bit) + { + if (bit < 64) + lower &= ~(1ull << bit); + else + higher.erase(bit); + } + + inline uint64_t get_lower() const + { + return lower; + } + + inline void reset() + { + lower = 0; + higher.clear(); + } + + inline void merge_and(const Bitset &other) + { + lower &= other.lower; + std::unordered_set tmp_set; + for (auto &v : higher) + if (other.higher.count(v) != 0) + tmp_set.insert(v); + higher = std::move(tmp_set); + } + + inline void merge_or(const Bitset &other) + { + lower |= other.lower; + for (auto &v : other.higher) + higher.insert(v); + } + + inline bool operator==(const Bitset &other) const + { + if (lower != other.lower) + return false; + + if (higher.size() != other.higher.size()) + return false; + + for (auto &v : higher) + if (other.higher.count(v) == 0) + return false; + + return true; + } + + inline bool operator!=(const Bitset &other) const + { + return !(*this == other); + } + + template + void for_each_bit(const Op &op) const + { + // TODO: Add ctz-based iteration. + for (uint32_t i = 0; i < 64; i++) + { + if (lower & (1ull << i)) + op(i); + } + + if (higher.empty()) + return; + + // Need to enforce an order here for reproducible results, + // but hitting this path should happen extremely rarely, so having this slow path is fine. + SmallVector bits; + bits.reserve(higher.size()); + for (auto &v : higher) + bits.push_back(v); + std::sort(std::begin(bits), std::end(bits)); + + for (auto &v : bits) + op(v); + } + + inline bool empty() const + { + return lower == 0 && higher.empty(); + } + +private: + // The most common bits to set are all lower than 64, + // so optimize for this case. Bits spilling outside 64 go into a slower data structure. + // In almost all cases, higher data structure will not be used. + uint64_t lower = 0; + std::unordered_set higher; +}; + +// Helper template to avoid lots of nasty string temporary munging. +template +std::string join(Ts &&... ts) +{ + StringStream<> stream; + inner::join_helper(stream, std::forward(ts)...); + return stream.str(); +} + +inline std::string merge(const SmallVector &list, const char *between = ", ") +{ + StringStream<> stream; + for (auto &elem : list) + { + stream << elem; + if (&elem != &list.back()) + stream << between; + } + return stream.str(); +} + +// Make sure we don't accidentally call this with float or doubles with SFINAE. +// Have to use the radix-aware overload. +template ::value, int>::type = 0> +inline std::string convert_to_string(const T &t) +{ + return std::to_string(t); +} + +// Allow implementations to set a convenient standard precision +#ifndef SPIRV_CROSS_FLT_FMT +#define SPIRV_CROSS_FLT_FMT "%.32g" +#endif + +// Disable sprintf and strcat warnings. +// We cannot rely on snprintf and family existing because, ..., MSVC. +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +static inline void fixup_radix_point(char *str, char radix_point) +{ + // Setting locales is a very risky business in multi-threaded program, + // so just fixup locales instead. We only need to care about the radix point. + if (radix_point != '.') + { + while (*str != '\0') + { + if (*str == radix_point) + *str = '.'; + str++; + } + } +} + +inline std::string convert_to_string(float t, char locale_radix_point) +{ + // std::to_string for floating point values is broken. + // Fallback to something more sane. + char buf[64]; + sprintf(buf, SPIRV_CROSS_FLT_FMT, t); + fixup_radix_point(buf, locale_radix_point); + + // Ensure that the literal is float. + if (!strchr(buf, '.') && !strchr(buf, 'e')) + strcat(buf, ".0"); + return buf; +} + +inline std::string convert_to_string(double t, char locale_radix_point) +{ + // std::to_string for floating point values is broken. + // Fallback to something more sane. + char buf[64]; + sprintf(buf, SPIRV_CROSS_FLT_FMT, t); + fixup_radix_point(buf, locale_radix_point); + + // Ensure that the literal is float. + if (!strchr(buf, '.') && !strchr(buf, 'e')) + strcat(buf, ".0"); + return buf; +} + +template +struct ValueSaver +{ + explicit ValueSaver(T ¤t_) + : current(current_) + , saved(current_) + { + } + + void release() + { + current = saved; + } + + ~ValueSaver() + { + release(); + } + + T ¤t; + T saved; +}; + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +struct Instruction +{ + uint16_t op = 0; + uint16_t count = 0; + uint32_t offset = 0; + uint32_t length = 0; +}; + +enum Types +{ + TypeNone, + TypeType, + TypeVariable, + TypeConstant, + TypeFunction, + TypeFunctionPrototype, + TypeBlock, + TypeExtension, + TypeExpression, + TypeConstantOp, + TypeCombinedImageSampler, + TypeAccessChain, + TypeUndef, + TypeString, + TypeCount +}; + +template +class TypedID; + +template <> +class TypedID +{ +public: + TypedID() = default; + TypedID(uint32_t id_) + : id(id_) + { + } + + template + TypedID(const TypedID &other) + { + *this = other; + } + + template + TypedID &operator=(const TypedID &other) + { + id = uint32_t(other); + return *this; + } + + // Implicit conversion to u32 is desired here. + // As long as we block implicit conversion between TypedID and TypedID we're good. + operator uint32_t() const + { + return id; + } + + template + operator TypedID() const + { + return TypedID(*this); + } + +private: + uint32_t id = 0; +}; + +template +class TypedID +{ +public: + TypedID() = default; + TypedID(uint32_t id_) + : id(id_) + { + } + + explicit TypedID(const TypedID &other) + : id(uint32_t(other)) + { + } + + operator uint32_t() const + { + return id; + } + +private: + uint32_t id = 0; +}; + +using VariableID = TypedID; +using TypeID = TypedID; +using ConstantID = TypedID; +using FunctionID = TypedID; +using BlockID = TypedID; +using ID = TypedID; + +// Helper for Variant interface. +struct IVariant +{ + virtual ~IVariant() = default; + virtual IVariant *clone(ObjectPoolBase *pool) = 0; + ID self = 0; +}; + +#define SPIRV_CROSS_DECLARE_CLONE(T) \ + IVariant *clone(ObjectPoolBase *pool) override \ + { \ + return static_cast *>(pool)->allocate(*this); \ + } + +struct SPIRUndef : IVariant +{ + enum + { + type = TypeUndef + }; + + explicit SPIRUndef(TypeID basetype_) + : basetype(basetype_) + { + } + TypeID basetype; + + SPIRV_CROSS_DECLARE_CLONE(SPIRUndef) +}; + +struct SPIRString : IVariant +{ + enum + { + type = TypeString + }; + + explicit SPIRString(std::string str_) + : str(std::move(str_)) + { + } + + std::string str; + + SPIRV_CROSS_DECLARE_CLONE(SPIRString) +}; + +// This type is only used by backends which need to access the combined image and sampler IDs separately after +// the OpSampledImage opcode. +struct SPIRCombinedImageSampler : IVariant +{ + enum + { + type = TypeCombinedImageSampler + }; + SPIRCombinedImageSampler(TypeID type_, VariableID image_, VariableID sampler_) + : combined_type(type_) + , image(image_) + , sampler(sampler_) + { + } + TypeID combined_type; + VariableID image; + VariableID sampler; + + SPIRV_CROSS_DECLARE_CLONE(SPIRCombinedImageSampler) +}; + +struct SPIRConstantOp : IVariant +{ + enum + { + type = TypeConstantOp + }; + + SPIRConstantOp(TypeID result_type, spv::Op op, const uint32_t *args, uint32_t length) + : opcode(op) + , basetype(result_type) + { + arguments.reserve(length); + for (uint32_t i = 0; i < length; i++) + arguments.push_back(args[i]); + } + + spv::Op opcode; + SmallVector arguments; + TypeID basetype; + + SPIRV_CROSS_DECLARE_CLONE(SPIRConstantOp) +}; + +struct SPIRType : IVariant +{ + enum + { + type = TypeType + }; + + enum BaseType + { + Unknown, + Void, + Boolean, + SByte, + UByte, + Short, + UShort, + Int, + UInt, + Int64, + UInt64, + AtomicCounter, + Half, + Float, + Double, + Struct, + Image, + SampledImage, + Sampler, + AccelerationStructure, + RayQuery, + + // Keep internal types at the end. + ControlPointArray, + Interpolant, + Char + }; + + // Scalar/vector/matrix support. + BaseType basetype = Unknown; + uint32_t width = 0; + uint32_t vecsize = 1; + uint32_t columns = 1; + + // Arrays, support array of arrays by having a vector of array sizes. + SmallVector array; + + // Array elements can be either specialization constants or specialization ops. + // This array determines how to interpret the array size. + // If an element is true, the element is a literal, + // otherwise, it's an expression, which must be resolved on demand. + // The actual size is not really known until runtime. + SmallVector array_size_literal; + + // Pointers + // Keep track of how many pointer layers we have. + uint32_t pointer_depth = 0; + bool pointer = false; + bool forward_pointer = false; + + spv::StorageClass storage = spv::StorageClassGeneric; + + SmallVector member_types; + + // If member order has been rewritten to handle certain scenarios with Offset, + // allow codegen to rewrite the index. + SmallVector member_type_index_redirection; + + struct ImageType + { + TypeID type; + spv::Dim dim; + bool depth; + bool arrayed; + bool ms; + uint32_t sampled; + spv::ImageFormat format; + spv::AccessQualifier access; + } image; + + // Structs can be declared multiple times if they are used as part of interface blocks. + // We want to detect this so that we only emit the struct definition once. + // Since we cannot rely on OpName to be equal, we need to figure out aliases. + TypeID type_alias = 0; + + // Denotes the type which this type is based on. + // Allows the backend to traverse how a complex type is built up during access chains. + TypeID parent_type = 0; + + // Used in backends to avoid emitting members with conflicting names. + std::unordered_set member_name_cache; + + SPIRV_CROSS_DECLARE_CLONE(SPIRType) +}; + +struct SPIRExtension : IVariant +{ + enum + { + type = TypeExtension + }; + + enum Extension + { + Unsupported, + GLSL, + SPV_debug_info, + SPV_AMD_shader_ballot, + SPV_AMD_shader_explicit_vertex_parameter, + SPV_AMD_shader_trinary_minmax, + SPV_AMD_gcn_shader + }; + + explicit SPIRExtension(Extension ext_) + : ext(ext_) + { + } + + Extension ext; + SPIRV_CROSS_DECLARE_CLONE(SPIRExtension) +}; + +// SPIREntryPoint is not a variant since its IDs are used to decorate OpFunction, +// so in order to avoid conflicts, we can't stick them in the ids array. +struct SPIREntryPoint +{ + SPIREntryPoint(FunctionID self_, spv::ExecutionModel execution_model, const std::string &entry_name) + : self(self_) + , name(entry_name) + , orig_name(entry_name) + , model(execution_model) + { + } + SPIREntryPoint() = default; + + FunctionID self = 0; + std::string name; + std::string orig_name; + SmallVector interface_variables; + + Bitset flags; + struct WorkgroupSize + { + uint32_t x = 0, y = 0, z = 0; + uint32_t constant = 0; // Workgroup size can be expressed as a constant/spec-constant instead. + } workgroup_size; + uint32_t invocations = 0; + uint32_t output_vertices = 0; + spv::ExecutionModel model = spv::ExecutionModelMax; + bool geometry_passthrough = false; +}; + +struct SPIRExpression : IVariant +{ + enum + { + type = TypeExpression + }; + + // Only created by the backend target to avoid creating tons of temporaries. + SPIRExpression(std::string expr, TypeID expression_type_, bool immutable_) + : expression(move(expr)) + , expression_type(expression_type_) + , immutable(immutable_) + { + } + + // If non-zero, prepend expression with to_expression(base_expression). + // Used in amortizing multiple calls to to_expression() + // where in certain cases that would quickly force a temporary when not needed. + ID base_expression = 0; + + std::string expression; + TypeID expression_type = 0; + + // If this expression is a forwarded load, + // allow us to reference the original variable. + ID loaded_from = 0; + + // If this expression will never change, we can avoid lots of temporaries + // in high level source. + // An expression being immutable can be speculative, + // it is assumed that this is true almost always. + bool immutable = false; + + // Before use, this expression must be transposed. + // This is needed for targets which don't support row_major layouts. + bool need_transpose = false; + + // Whether or not this is an access chain expression. + bool access_chain = false; + + // A list of expressions which this expression depends on. + SmallVector expression_dependencies; + + // By reading this expression, we implicitly read these expressions as well. + // Used by access chain Store and Load since we read multiple expressions in this case. + SmallVector implied_read_expressions; + + // The expression was emitted at a certain scope. Lets us track when an expression read means multiple reads. + uint32_t emitted_loop_level = 0; + + SPIRV_CROSS_DECLARE_CLONE(SPIRExpression) +}; + +struct SPIRFunctionPrototype : IVariant +{ + enum + { + type = TypeFunctionPrototype + }; + + explicit SPIRFunctionPrototype(TypeID return_type_) + : return_type(return_type_) + { + } + + TypeID return_type; + SmallVector parameter_types; + + SPIRV_CROSS_DECLARE_CLONE(SPIRFunctionPrototype) +}; + +struct SPIRBlock : IVariant +{ + enum + { + type = TypeBlock + }; + + enum Terminator + { + Unknown, + Direct, // Emit next block directly without a particular condition. + + Select, // Block ends with an if/else block. + MultiSelect, // Block ends with switch statement. + + Return, // Block ends with return. + Unreachable, // Noop + Kill // Discard + }; + + enum Merge + { + MergeNone, + MergeLoop, + MergeSelection + }; + + enum Hints + { + HintNone, + HintUnroll, + HintDontUnroll, + HintFlatten, + HintDontFlatten + }; + + enum Method + { + MergeToSelectForLoop, + MergeToDirectForLoop, + MergeToSelectContinueForLoop + }; + + enum ContinueBlockType + { + ContinueNone, + + // Continue block is branchless and has at least one instruction. + ForLoop, + + // Noop continue block. + WhileLoop, + + // Continue block is conditional. + DoWhileLoop, + + // Highly unlikely that anything will use this, + // since it is really awkward/impossible to express in GLSL. + ComplexLoop + }; + + enum : uint32_t + { + NoDominator = 0xffffffffu + }; + + Terminator terminator = Unknown; + Merge merge = MergeNone; + Hints hint = HintNone; + BlockID next_block = 0; + BlockID merge_block = 0; + BlockID continue_block = 0; + + ID return_value = 0; // If 0, return nothing (void). + ID condition = 0; + BlockID true_block = 0; + BlockID false_block = 0; + BlockID default_block = 0; + + SmallVector ops; + + struct Phi + { + ID local_variable; // flush local variable ... + BlockID parent; // If we're in from_block and want to branch into this block ... + VariableID function_variable; // to this function-global "phi" variable first. + }; + + // Before entering this block flush out local variables to magical "phi" variables. + SmallVector phi_variables; + + // Declare these temporaries before beginning the block. + // Used for handling complex continue blocks which have side effects. + SmallVector> declare_temporary; + + // Declare these temporaries, but only conditionally if this block turns out to be + // a complex loop header. + SmallVector> potential_declare_temporary; + + struct Case + { + uint32_t value; + BlockID block; + }; + SmallVector cases; + + // If we have tried to optimize code for this block but failed, + // keep track of this. + bool disable_block_optimization = false; + + // If the continue block is complex, fallback to "dumb" for loops. + bool complex_continue = false; + + // Do we need a ladder variable to defer breaking out of a loop construct after a switch block? + bool need_ladder_break = false; + + // If marked, we have explicitly handled Phi from this block, so skip any flushes related to that on a branch. + // Used to handle an edge case with switch and case-label fallthrough where fall-through writes to Phi. + BlockID ignore_phi_from_block = 0; + + // The dominating block which this block might be within. + // Used in continue; blocks to determine if we really need to write continue. + BlockID loop_dominator = 0; + + // All access to these variables are dominated by this block, + // so before branching anywhere we need to make sure that we declare these variables. + SmallVector dominated_variables; + + // These are variables which should be declared in a for loop header, if we + // fail to use a classic for-loop, + // we remove these variables, and fall back to regular variables outside the loop. + SmallVector loop_variables; + + // Some expressions are control-flow dependent, i.e. any instruction which relies on derivatives or + // sub-group-like operations. + // Make sure that we only use these expressions in the original block. + SmallVector invalidate_expressions; + + SPIRV_CROSS_DECLARE_CLONE(SPIRBlock) +}; + +struct SPIRFunction : IVariant +{ + enum + { + type = TypeFunction + }; + + SPIRFunction(TypeID return_type_, TypeID function_type_) + : return_type(return_type_) + , function_type(function_type_) + { + } + + struct Parameter + { + TypeID type; + ID id; + uint32_t read_count; + uint32_t write_count; + + // Set to true if this parameter aliases a global variable, + // used mostly in Metal where global variables + // have to be passed down to functions as regular arguments. + // However, for this kind of variable, we should not care about + // read and write counts as access to the function arguments + // is not local to the function in question. + bool alias_global_variable; + }; + + // When calling a function, and we're remapping separate image samplers, + // resolve these arguments into combined image samplers and pass them + // as additional arguments in this order. + // It gets more complicated as functions can pull in their own globals + // and combine them with parameters, + // so we need to distinguish if something is local parameter index + // or a global ID. + struct CombinedImageSamplerParameter + { + VariableID id; + VariableID image_id; + VariableID sampler_id; + bool global_image; + bool global_sampler; + bool depth; + }; + + TypeID return_type; + TypeID function_type; + SmallVector arguments; + + // Can be used by backends to add magic arguments. + // Currently used by combined image/sampler implementation. + + SmallVector shadow_arguments; + SmallVector local_variables; + BlockID entry_block = 0; + SmallVector blocks; + SmallVector combined_parameters; + + struct EntryLine + { + uint32_t file_id = 0; + uint32_t line_literal = 0; + }; + EntryLine entry_line; + + void add_local_variable(VariableID id) + { + local_variables.push_back(id); + } + + void add_parameter(TypeID parameter_type, ID id, bool alias_global_variable = false) + { + // Arguments are read-only until proven otherwise. + arguments.push_back({ parameter_type, id, 0u, 0u, alias_global_variable }); + } + + // Hooks to be run when the function returns. + // Mostly used for lowering internal data structures onto flattened structures. + // Need to defer this, because they might rely on things which change during compilation. + // Intentionally not a small vector, this one is rare, and std::function can be large. + Vector> fixup_hooks_out; + + // Hooks to be run when the function begins. + // Mostly used for populating internal data structures from flattened structures. + // Need to defer this, because they might rely on things which change during compilation. + // Intentionally not a small vector, this one is rare, and std::function can be large. + Vector> fixup_hooks_in; + + // On function entry, make sure to copy a constant array into thread addr space to work around + // the case where we are passing a constant array by value to a function on backends which do not + // consider arrays value types. + SmallVector constant_arrays_needed_on_stack; + + bool active = false; + bool flush_undeclared = true; + bool do_combined_parameters = true; + + SPIRV_CROSS_DECLARE_CLONE(SPIRFunction) +}; + +struct SPIRAccessChain : IVariant +{ + enum + { + type = TypeAccessChain + }; + + SPIRAccessChain(TypeID basetype_, spv::StorageClass storage_, std::string base_, std::string dynamic_index_, + int32_t static_index_) + : basetype(basetype_) + , storage(storage_) + , base(std::move(base_)) + , dynamic_index(std::move(dynamic_index_)) + , static_index(static_index_) + { + } + + // The access chain represents an offset into a buffer. + // Some backends need more complicated handling of access chains to be able to use buffers, like HLSL + // which has no usable buffer type ala GLSL SSBOs. + // StructuredBuffer is too limited, so our only option is to deal with ByteAddressBuffer which works with raw addresses. + + TypeID basetype; + spv::StorageClass storage; + std::string base; + std::string dynamic_index; + int32_t static_index; + + VariableID loaded_from = 0; + uint32_t matrix_stride = 0; + uint32_t array_stride = 0; + bool row_major_matrix = false; + bool immutable = false; + + // By reading this expression, we implicitly read these expressions as well. + // Used by access chain Store and Load since we read multiple expressions in this case. + SmallVector implied_read_expressions; + + SPIRV_CROSS_DECLARE_CLONE(SPIRAccessChain) +}; + +struct SPIRVariable : IVariant +{ + enum + { + type = TypeVariable + }; + + SPIRVariable() = default; + SPIRVariable(TypeID basetype_, spv::StorageClass storage_, ID initializer_ = 0, VariableID basevariable_ = 0) + : basetype(basetype_) + , storage(storage_) + , initializer(initializer_) + , basevariable(basevariable_) + { + } + + TypeID basetype = 0; + spv::StorageClass storage = spv::StorageClassGeneric; + uint32_t decoration = 0; + ID initializer = 0; + VariableID basevariable = 0; + + SmallVector dereference_chain; + bool compat_builtin = false; + + // If a variable is shadowed, we only statically assign to it + // and never actually emit a statement for it. + // When we read the variable as an expression, just forward + // shadowed_id as the expression. + bool statically_assigned = false; + ID static_expression = 0; + + // Temporaries which can remain forwarded as long as this variable is not modified. + SmallVector dependees; + bool forwardable = true; + + bool deferred_declaration = false; + bool phi_variable = false; + + // Used to deal with Phi variable flushes. See flush_phi(). + bool allocate_temporary_copy = false; + + bool remapped_variable = false; + uint32_t remapped_components = 0; + + // The block which dominates all access to this variable. + BlockID dominator = 0; + // If true, this variable is a loop variable, when accessing the variable + // outside a loop, + // we should statically forward it. + bool loop_variable = false; + // Set to true while we're inside the for loop. + bool loop_variable_enable = false; + + SPIRFunction::Parameter *parameter = nullptr; + + SPIRV_CROSS_DECLARE_CLONE(SPIRVariable) +}; + +struct SPIRConstant : IVariant +{ + enum + { + type = TypeConstant + }; + + union Constant + { + uint32_t u32; + int32_t i32; + float f32; + + uint64_t u64; + int64_t i64; + double f64; + }; + + struct ConstantVector + { + Constant r[4]; + // If != 0, this element is a specialization constant, and we should keep track of it as such. + ID id[4]; + uint32_t vecsize = 1; + + ConstantVector() + { + memset(r, 0, sizeof(r)); + } + }; + + struct ConstantMatrix + { + ConstantVector c[4]; + // If != 0, this column is a specialization constant, and we should keep track of it as such. + ID id[4]; + uint32_t columns = 1; + }; + + static inline float f16_to_f32(uint16_t u16_value) + { + // Based on the GLM implementation. + int s = (u16_value >> 15) & 0x1; + int e = (u16_value >> 10) & 0x1f; + int m = (u16_value >> 0) & 0x3ff; + + union + { + float f32; + uint32_t u32; + } u; + + if (e == 0) + { + if (m == 0) + { + u.u32 = uint32_t(s) << 31; + return u.f32; + } + else + { + while ((m & 0x400) == 0) + { + m <<= 1; + e--; + } + + e++; + m &= ~0x400; + } + } + else if (e == 31) + { + if (m == 0) + { + u.u32 = (uint32_t(s) << 31) | 0x7f800000u; + return u.f32; + } + else + { + u.u32 = (uint32_t(s) << 31) | 0x7f800000u | (m << 13); + return u.f32; + } + } + + e += 127 - 15; + m <<= 13; + u.u32 = (uint32_t(s) << 31) | (e << 23) | m; + return u.f32; + } + + inline uint32_t specialization_constant_id(uint32_t col, uint32_t row) const + { + return m.c[col].id[row]; + } + + inline uint32_t specialization_constant_id(uint32_t col) const + { + return m.id[col]; + } + + inline uint32_t scalar(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].u32; + } + + inline int16_t scalar_i16(uint32_t col = 0, uint32_t row = 0) const + { + return int16_t(m.c[col].r[row].u32 & 0xffffu); + } + + inline uint16_t scalar_u16(uint32_t col = 0, uint32_t row = 0) const + { + return uint16_t(m.c[col].r[row].u32 & 0xffffu); + } + + inline int8_t scalar_i8(uint32_t col = 0, uint32_t row = 0) const + { + return int8_t(m.c[col].r[row].u32 & 0xffu); + } + + inline uint8_t scalar_u8(uint32_t col = 0, uint32_t row = 0) const + { + return uint8_t(m.c[col].r[row].u32 & 0xffu); + } + + inline float scalar_f16(uint32_t col = 0, uint32_t row = 0) const + { + return f16_to_f32(scalar_u16(col, row)); + } + + inline float scalar_f32(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].f32; + } + + inline int32_t scalar_i32(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].i32; + } + + inline double scalar_f64(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].f64; + } + + inline int64_t scalar_i64(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].i64; + } + + inline uint64_t scalar_u64(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].u64; + } + + inline const ConstantVector &vector() const + { + return m.c[0]; + } + + inline uint32_t vector_size() const + { + return m.c[0].vecsize; + } + + inline uint32_t columns() const + { + return m.columns; + } + + inline void make_null(const SPIRType &constant_type_) + { + m = {}; + m.columns = constant_type_.columns; + for (auto &c : m.c) + c.vecsize = constant_type_.vecsize; + } + + inline bool constant_is_null() const + { + if (specialization) + return false; + if (!subconstants.empty()) + return false; + + for (uint32_t col = 0; col < columns(); col++) + for (uint32_t row = 0; row < vector_size(); row++) + if (scalar_u64(col, row) != 0) + return false; + + return true; + } + + explicit SPIRConstant(uint32_t constant_type_) + : constant_type(constant_type_) + { + } + + SPIRConstant() = default; + + SPIRConstant(TypeID constant_type_, const uint32_t *elements, uint32_t num_elements, bool specialized) + : constant_type(constant_type_) + , specialization(specialized) + { + subconstants.reserve(num_elements); + for (uint32_t i = 0; i < num_elements; i++) + subconstants.push_back(elements[i]); + specialization = specialized; + } + + // Construct scalar (32-bit). + SPIRConstant(TypeID constant_type_, uint32_t v0, bool specialized) + : constant_type(constant_type_) + , specialization(specialized) + { + m.c[0].r[0].u32 = v0; + m.c[0].vecsize = 1; + m.columns = 1; + } + + // Construct scalar (64-bit). + SPIRConstant(TypeID constant_type_, uint64_t v0, bool specialized) + : constant_type(constant_type_) + , specialization(specialized) + { + m.c[0].r[0].u64 = v0; + m.c[0].vecsize = 1; + m.columns = 1; + } + + // Construct vectors and matrices. + SPIRConstant(TypeID constant_type_, const SPIRConstant *const *vector_elements, uint32_t num_elements, + bool specialized) + : constant_type(constant_type_) + , specialization(specialized) + { + bool matrix = vector_elements[0]->m.c[0].vecsize > 1; + + if (matrix) + { + m.columns = num_elements; + + for (uint32_t i = 0; i < num_elements; i++) + { + m.c[i] = vector_elements[i]->m.c[0]; + if (vector_elements[i]->specialization) + m.id[i] = vector_elements[i]->self; + } + } + else + { + m.c[0].vecsize = num_elements; + m.columns = 1; + + for (uint32_t i = 0; i < num_elements; i++) + { + m.c[0].r[i] = vector_elements[i]->m.c[0].r[0]; + if (vector_elements[i]->specialization) + m.c[0].id[i] = vector_elements[i]->self; + } + } + } + + TypeID constant_type = 0; + ConstantMatrix m; + + // If this constant is a specialization constant (i.e. created with OpSpecConstant*). + bool specialization = false; + // If this constant is used as an array length which creates specialization restrictions on some backends. + bool is_used_as_array_length = false; + + // If true, this is a LUT, and should always be declared in the outer scope. + bool is_used_as_lut = false; + + // For composites which are constant arrays, etc. + SmallVector subconstants; + + // Non-Vulkan GLSL, HLSL and sometimes MSL emits defines for each specialization constant, + // and uses them to initialize the constant. This allows the user + // to still be able to specialize the value by supplying corresponding + // preprocessor directives before compiling the shader. + std::string specialization_constant_macro_name; + + SPIRV_CROSS_DECLARE_CLONE(SPIRConstant) +}; + +// Variants have a very specific allocation scheme. +struct ObjectPoolGroup +{ + std::unique_ptr pools[TypeCount]; +}; + +class Variant +{ +public: + explicit Variant(ObjectPoolGroup *group_) + : group(group_) + { + } + + ~Variant() + { + if (holder) + group->pools[type]->free_opaque(holder); + } + + // Marking custom move constructor as noexcept is important. + Variant(Variant &&other) SPIRV_CROSS_NOEXCEPT + { + *this = std::move(other); + } + + // We cannot copy from other variant without our own pool group. + // Have to explicitly copy. + Variant(const Variant &variant) = delete; + + // Marking custom move constructor as noexcept is important. + Variant &operator=(Variant &&other) SPIRV_CROSS_NOEXCEPT + { + if (this != &other) + { + if (holder) + group->pools[type]->free_opaque(holder); + holder = other.holder; + group = other.group; + type = other.type; + allow_type_rewrite = other.allow_type_rewrite; + + other.holder = nullptr; + other.type = TypeNone; + } + return *this; + } + + // This copy/clone should only be called in the Compiler constructor. + // If this is called inside ::compile(), we invalidate any references we took higher in the stack. + // This should never happen. + Variant &operator=(const Variant &other) + { +//#define SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE +#ifdef SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE + abort(); +#endif + if (this != &other) + { + if (holder) + group->pools[type]->free_opaque(holder); + + if (other.holder) + holder = other.holder->clone(group->pools[other.type].get()); + else + holder = nullptr; + + type = other.type; + allow_type_rewrite = other.allow_type_rewrite; + } + return *this; + } + + void set(IVariant *val, Types new_type) + { + if (holder) + group->pools[type]->free_opaque(holder); + holder = nullptr; + + if (!allow_type_rewrite && type != TypeNone && type != new_type) + { + if (val) + group->pools[new_type]->free_opaque(val); + SPIRV_CROSS_THROW("Overwriting a variant with new type."); + } + + holder = val; + type = new_type; + allow_type_rewrite = false; + } + + template + T *allocate_and_set(Types new_type, Ts &&... ts) + { + T *val = static_cast &>(*group->pools[new_type]).allocate(std::forward(ts)...); + set(val, new_type); + return val; + } + + template + T &get() + { + if (!holder) + SPIRV_CROSS_THROW("nullptr"); + if (static_cast(T::type) != type) + SPIRV_CROSS_THROW("Bad cast"); + return *static_cast(holder); + } + + template + const T &get() const + { + if (!holder) + SPIRV_CROSS_THROW("nullptr"); + if (static_cast(T::type) != type) + SPIRV_CROSS_THROW("Bad cast"); + return *static_cast(holder); + } + + Types get_type() const + { + return type; + } + + ID get_id() const + { + return holder ? holder->self : ID(0); + } + + bool empty() const + { + return !holder; + } + + void reset() + { + if (holder) + group->pools[type]->free_opaque(holder); + holder = nullptr; + type = TypeNone; + } + + void set_allow_type_rewrite() + { + allow_type_rewrite = true; + } + +private: + ObjectPoolGroup *group = nullptr; + IVariant *holder = nullptr; + Types type = TypeNone; + bool allow_type_rewrite = false; +}; + +template +T &variant_get(Variant &var) +{ + return var.get(); +} + +template +const T &variant_get(const Variant &var) +{ + return var.get(); +} + +template +T &variant_set(Variant &var, P &&... args) +{ + auto *ptr = var.allocate_and_set(static_cast(T::type), std::forward

(args)...); + return *ptr; +} + +struct AccessChainMeta +{ + uint32_t storage_physical_type = 0; + bool need_transpose = false; + bool storage_is_packed = false; + bool storage_is_invariant = false; + bool flattened_struct = false; +}; + +enum ExtendedDecorations +{ + // Marks if a buffer block is re-packed, i.e. member declaration might be subject to PhysicalTypeID remapping and padding. + SPIRVCrossDecorationBufferBlockRepacked = 0, + + // A type in a buffer block might be declared with a different physical type than the logical type. + // If this is not set, PhysicalTypeID == the SPIR-V type as declared. + SPIRVCrossDecorationPhysicalTypeID, + + // Marks if the physical type is to be declared with tight packing rules, i.e. packed_floatN on MSL and friends. + // If this is set, PhysicalTypeID might also be set. It can be set to same as logical type if all we're doing + // is converting float3 to packed_float3 for example. + // If this is marked on a struct, it means the struct itself must use only Packed types for all its members. + SPIRVCrossDecorationPhysicalTypePacked, + + // The padding in bytes before declaring this struct member. + // If used on a struct type, marks the target size of a struct. + SPIRVCrossDecorationPaddingTarget, + + SPIRVCrossDecorationInterfaceMemberIndex, + SPIRVCrossDecorationInterfaceOrigID, + SPIRVCrossDecorationResourceIndexPrimary, + // Used for decorations like resource indices for samplers when part of combined image samplers. + // A variable might need to hold two resource indices in this case. + SPIRVCrossDecorationResourceIndexSecondary, + // Used for resource indices for multiplanar images when part of combined image samplers. + SPIRVCrossDecorationResourceIndexTertiary, + SPIRVCrossDecorationResourceIndexQuaternary, + + // Marks a buffer block for using explicit offsets (GLSL/HLSL). + SPIRVCrossDecorationExplicitOffset, + + // Apply to a variable in the Input storage class; marks it as holding the base group passed to vkCmdDispatchBase(), + // or the base vertex and instance indices passed to vkCmdDrawIndexed(). + // In MSL, this is used to adjust the WorkgroupId and GlobalInvocationId variables in compute shaders, + // and to hold the BaseVertex and BaseInstance variables in vertex shaders. + SPIRVCrossDecorationBuiltInDispatchBase, + + // Apply to a variable that is a function parameter; marks it as being a "dynamic" + // combined image-sampler. In MSL, this is used when a function parameter might hold + // either a regular combined image-sampler or one that has an attached sampler + // Y'CbCr conversion. + SPIRVCrossDecorationDynamicImageSampler, + + // Apply to a variable in the Input storage class; marks it as holding the size of the stage + // input grid. + // In MSL, this is used to hold the vertex and instance counts in a tessellation pipeline + // vertex shader. + SPIRVCrossDecorationBuiltInStageInputSize, + + // Apply to any access chain of a tessellation I/O variable; stores the type of the sub-object + // that was chained to, as recorded in the input variable itself. This is used in case the pointer + // is itself used as the base of an access chain, to calculate the original type of the sub-object + // chained to, in case a swizzle needs to be applied. This should not happen normally with valid + // SPIR-V, but the MSL backend can change the type of input variables, necessitating the + // addition of swizzles to keep the generated code compiling. + SPIRVCrossDecorationTessIOOriginalInputTypeID, + + // Apply to any access chain of an interface variable used with pull-model interpolation, where the variable is a + // vector but the resulting pointer is a scalar; stores the component index that is to be accessed by the chain. + // This is used when emitting calls to interpolation functions on the chain in MSL: in this case, the component + // must be applied to the result, since pull-model interpolants in MSL cannot be swizzled directly, but the + // results of interpolation can. + SPIRVCrossDecorationInterpolantComponentExpr, + + SPIRVCrossDecorationCount +}; + +struct Meta +{ + struct Decoration + { + std::string alias; + std::string qualified_alias; + std::string hlsl_semantic; + Bitset decoration_flags; + spv::BuiltIn builtin_type = spv::BuiltInMax; + uint32_t location = 0; + uint32_t component = 0; + uint32_t set = 0; + uint32_t binding = 0; + uint32_t offset = 0; + uint32_t xfb_buffer = 0; + uint32_t xfb_stride = 0; + uint32_t stream = 0; + uint32_t array_stride = 0; + uint32_t matrix_stride = 0; + uint32_t input_attachment = 0; + uint32_t spec_id = 0; + uint32_t index = 0; + spv::FPRoundingMode fp_rounding_mode = spv::FPRoundingModeMax; + bool builtin = false; + + struct Extended + { + Extended() + { + // MSVC 2013 workaround to init like this. + for (auto &v : values) + v = 0; + } + + Bitset flags; + uint32_t values[SPIRVCrossDecorationCount]; + } extended; + }; + + Decoration decoration; + + // Intentionally not a SmallVector. Decoration is large and somewhat rare. + Vector members; + + std::unordered_map decoration_word_offset; + + // For SPV_GOOGLE_hlsl_functionality1. + bool hlsl_is_magic_counter_buffer = false; + // ID for the sibling counter buffer. + uint32_t hlsl_magic_counter_buffer = 0; +}; + +// A user callback that remaps the type of any variable. +// var_name is the declared name of the variable. +// name_of_type is the textual name of the type which will be used in the code unless written to by the callback. +using VariableTypeRemapCallback = + std::function; + +class Hasher +{ +public: + inline void u32(uint32_t value) + { + h = (h * 0x100000001b3ull) ^ value; + } + + inline uint64_t get() const + { + return h; + } + +private: + uint64_t h = 0xcbf29ce484222325ull; +}; + +static inline bool type_is_floating_point(const SPIRType &type) +{ + return type.basetype == SPIRType::Half || type.basetype == SPIRType::Float || type.basetype == SPIRType::Double; +} + +static inline bool type_is_integral(const SPIRType &type) +{ + return type.basetype == SPIRType::SByte || type.basetype == SPIRType::UByte || type.basetype == SPIRType::Short || + type.basetype == SPIRType::UShort || type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt || + type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64; +} + +static inline SPIRType::BaseType to_signed_basetype(uint32_t width) +{ + switch (width) + { + case 8: + return SPIRType::SByte; + case 16: + return SPIRType::Short; + case 32: + return SPIRType::Int; + case 64: + return SPIRType::Int64; + default: + SPIRV_CROSS_THROW("Invalid bit width."); + } +} + +static inline SPIRType::BaseType to_unsigned_basetype(uint32_t width) +{ + switch (width) + { + case 8: + return SPIRType::UByte; + case 16: + return SPIRType::UShort; + case 32: + return SPIRType::UInt; + case 64: + return SPIRType::UInt64; + default: + SPIRV_CROSS_THROW("Invalid bit width."); + } +} + +// Returns true if an arithmetic operation does not change behavior depending on signedness. +static inline bool opcode_is_sign_invariant(spv::Op opcode) +{ + switch (opcode) + { + case spv::OpIEqual: + case spv::OpINotEqual: + case spv::OpISub: + case spv::OpIAdd: + case spv::OpIMul: + case spv::OpShiftLeftLogical: + case spv::OpBitwiseOr: + case spv::OpBitwiseXor: + case spv::OpBitwiseAnd: + return true; + + default: + return false; + } +} + +struct SetBindingPair +{ + uint32_t desc_set; + uint32_t binding; + + inline bool operator==(const SetBindingPair &other) const + { + return desc_set == other.desc_set && binding == other.binding; + } + + inline bool operator<(const SetBindingPair &other) const + { + return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding); + } +}; + +struct StageSetBinding +{ + spv::ExecutionModel model; + uint32_t desc_set; + uint32_t binding; + + inline bool operator==(const StageSetBinding &other) const + { + return model == other.model && desc_set == other.desc_set && binding == other.binding; + } +}; + +struct InternalHasher +{ + inline size_t operator()(const SetBindingPair &value) const + { + // Quality of hash doesn't really matter here. + auto hash_set = std::hash()(value.desc_set); + auto hash_binding = std::hash()(value.binding); + return (hash_set * 0x10001b31) ^ hash_binding; + } + + inline size_t operator()(const StageSetBinding &value) const + { + // Quality of hash doesn't really matter here. + auto hash_model = std::hash()(value.model); + auto hash_set = std::hash()(value.desc_set); + auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set; + return (tmp_hash * 0x10001b31) ^ value.binding; + } +}; + +// Special constant used in a {MSL,HLSL}ResourceBinding desc_set +// element to indicate the bindings for the push constants. +static const uint32_t ResourceBindingPushConstantDescriptorSet = ~(0u); + +// Special constant used in a {MSL,HLSL}ResourceBinding binding +// element to indicate the bindings for the push constants. +static const uint32_t ResourceBindingPushConstantBinding = 0; +} // namespace SPIRV_CROSS_NAMESPACE + +namespace std +{ +template +struct hash> +{ + size_t operator()(const SPIRV_CROSS_NAMESPACE::TypedID &value) const + { + return std::hash()(value); + } +}; +} // namespace std + +#endif diff --git a/third_party/spirv-cross/spirv_cpp.cpp b/third_party/spirv-cross/spirv_cpp.cpp new file mode 100644 index 0000000..d13d600 --- /dev/null +++ b/third_party/spirv-cross/spirv_cpp.cpp @@ -0,0 +1,551 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cpp.hpp" + +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +void CompilerCPP::emit_buffer_block(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto &type = get(var.basetype); + auto instance_name = to_name(var.self); + + uint32_t descriptor_set = ir.meta[var.self].decoration.set; + uint32_t binding = ir.meta[var.self].decoration.binding; + + emit_block_struct(type); + auto buffer_name = to_name(type.self); + + statement("internal::Resource<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()"); + resource_registrations.push_back( + join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");")); + statement(""); +} + +void CompilerCPP::emit_interface_block(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto &type = get(var.basetype); + + const char *qual = var.storage == StorageClassInput ? "StageInput" : "StageOutput"; + const char *lowerqual = var.storage == StorageClassInput ? "stage_input" : "stage_output"; + auto instance_name = to_name(var.self); + uint32_t location = ir.meta[var.self].decoration.location; + + string buffer_name; + auto flags = ir.meta[type.self].decoration.decoration_flags; + if (flags.get(DecorationBlock)) + { + emit_block_struct(type); + buffer_name = to_name(type.self); + } + else + buffer_name = type_to_glsl(type); + + statement("internal::", qual, "<", buffer_name, type_to_array_glsl(type), "> ", instance_name, "__;"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()"); + resource_registrations.push_back(join("s.register_", lowerqual, "(", instance_name, "__", ", ", location, ");")); + statement(""); +} + +void CompilerCPP::emit_shared(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto instance_name = to_name(var.self); + statement(CompilerGLSL::variable_decl(var), ";"); + statement_no_indent("#define ", instance_name, " __res->", instance_name); +} + +void CompilerCPP::emit_uniform(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto &type = get(var.basetype); + auto instance_name = to_name(var.self); + + uint32_t descriptor_set = ir.meta[var.self].decoration.set; + uint32_t binding = ir.meta[var.self].decoration.binding; + uint32_t location = ir.meta[var.self].decoration.location; + + string type_name = type_to_glsl(type); + remap_variable_type_name(type, instance_name, type_name); + + if (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage || + type.basetype == SPIRType::AtomicCounter) + { + statement("internal::Resource<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()"); + resource_registrations.push_back( + join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");")); + } + else + { + statement("internal::UniformConstant<", type_name, type_to_array_glsl(type), "> ", instance_name, "__;"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()"); + resource_registrations.push_back( + join("s.register_uniform_constant(", instance_name, "__", ", ", location, ");")); + } + + statement(""); +} + +void CompilerCPP::emit_push_constant_block(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto &type = get(var.basetype); + auto &flags = ir.meta[var.self].decoration.decoration_flags; + if (flags.get(DecorationBinding) || flags.get(DecorationDescriptorSet)) + SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. " + "Remap to location with reflection API first or disable these decorations."); + + emit_block_struct(type); + auto buffer_name = to_name(type.self); + auto instance_name = to_name(var.self); + + statement("internal::PushConstant<", buffer_name, type_to_array_glsl(type), "> ", instance_name, ";"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, ".get()"); + resource_registrations.push_back(join("s.register_push_constant(", instance_name, "__", ");")); + statement(""); +} + +void CompilerCPP::emit_block_struct(SPIRType &type) +{ + // C++ can't do interface blocks, so we fake it by emitting a separate struct. + // However, these structs are not allowed to alias anything, so remove it before + // emitting the struct. + // + // The type we have here needs to be resolved to the non-pointer type so we can remove aliases. + auto &self = get(type.self); + self.type_alias = 0; + emit_struct(self); +} + +void CompilerCPP::emit_resources() +{ + for (auto &id : ir.ids) + { + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + + bool needs_declaration = c.specialization || c.is_used_as_lut; + + if (needs_declaration) + { + if (!options.vulkan_semantics && c.specialization) + { + c.specialization_constant_macro_name = + constant_value_macro_name(get_decoration(c.self, DecorationSpecId)); + } + emit_constant(c); + } + } + else if (id.get_type() == TypeConstantOp) + { + emit_specialization_constant_op(id.get()); + } + } + + // Output all basic struct types which are not Block or BufferBlock as these are declared inplace + // when such variables are instantiated. + for (auto &id : ir.ids) + { + if (id.get_type() == TypeType) + { + auto &type = id.get(); + if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer && + (!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) && + !ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock))) + { + emit_struct(type); + } + } + } + + statement("struct Resources : ", resource_type); + begin_scope(); + + // Output UBOs and SSBOs + for (auto &id : ir.ids) + { + if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + auto &type = get(var.basetype); + + if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassUniform && + !is_hidden_variable(var) && + (ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock))) + { + emit_buffer_block(var); + } + } + } + + // Output push constant blocks + for (auto &id : ir.ids) + { + if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + auto &type = get(var.basetype); + if (!is_hidden_variable(var) && var.storage != StorageClassFunction && type.pointer && + type.storage == StorageClassPushConstant) + { + emit_push_constant_block(var); + } + } + } + + // Output in/out interfaces. + for (auto &id : ir.ids) + { + if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + auto &type = get(var.basetype); + + if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer && + (var.storage == StorageClassInput || var.storage == StorageClassOutput) && + interface_variable_exists_in_entry_point(var.self)) + { + emit_interface_block(var); + } + } + } + + // Output Uniform Constants (values, samplers, images, etc). + for (auto &id : ir.ids) + { + if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + auto &type = get(var.basetype); + + if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer && + (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter)) + { + emit_uniform(var); + } + } + } + + // Global variables. + bool emitted = false; + for (auto global : global_variables) + { + auto &var = get(global); + if (var.storage == StorageClassWorkgroup) + { + emit_shared(var); + emitted = true; + } + } + + if (emitted) + statement(""); + + declare_undefined_values(); + + statement("inline void init(spirv_cross_shader& s)"); + begin_scope(); + statement(resource_type, "::init(s);"); + for (auto ® : resource_registrations) + statement(reg); + end_scope(); + resource_registrations.clear(); + + end_scope_decl(); + + statement(""); + statement("Resources* __res;"); + if (get_entry_point().model == ExecutionModelGLCompute) + statement("ComputePrivateResources __priv_res;"); + statement(""); + + // Emit regular globals which are allocated per invocation. + emitted = false; + for (auto global : global_variables) + { + auto &var = get(global); + if (var.storage == StorageClassPrivate) + { + if (var.storage == StorageClassWorkgroup) + emit_shared(var); + else + statement(CompilerGLSL::variable_decl(var), ";"); + emitted = true; + } + } + + if (emitted) + statement(""); +} + +string CompilerCPP::compile() +{ + ir.fixup_reserved_names(); + + // Do not deal with ES-isms like precision, older extensions and such. + options.es = false; + options.version = 450; + backend.float_literal_suffix = true; + backend.double_literal_suffix = false; + backend.long_long_literal_suffix = true; + backend.uint32_t_literal_suffix = true; + backend.basic_int_type = "int32_t"; + backend.basic_uint_type = "uint32_t"; + backend.swizzle_is_function = true; + backend.shared_is_implied = true; + backend.unsized_array_supported = false; + backend.explicit_struct_type = true; + backend.use_initializer_list = true; + + fixup_type_alias(); + reorder_type_alias(); + build_function_control_flow_graphs_and_analyze(); + update_active_builtins(); + + uint32_t pass_count = 0; + do + { + if (pass_count >= 3) + SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!"); + + resource_registrations.clear(); + reset(); + + // Move constructor for this type is broken on GCC 4.9 ... + buffer.reset(); + + emit_header(); + emit_resources(); + + emit_function(get(ir.default_entry_point), Bitset()); + + pass_count++; + } while (is_forcing_recompilation()); + + // Match opening scope of emit_header(). + end_scope_decl(); + // namespace + end_scope(); + + // Emit C entry points + emit_c_linkage(); + + // Entry point in CPP is always main() for the time being. + get_entry_point().name = "main"; + + return buffer.str(); +} + +void CompilerCPP::emit_c_linkage() +{ + statement(""); + + statement("spirv_cross_shader_t *spirv_cross_construct(void)"); + begin_scope(); + statement("return new ", impl_type, "();"); + end_scope(); + + statement(""); + statement("void spirv_cross_destruct(spirv_cross_shader_t *shader)"); + begin_scope(); + statement("delete static_cast<", impl_type, "*>(shader);"); + end_scope(); + + statement(""); + statement("void spirv_cross_invoke(spirv_cross_shader_t *shader)"); + begin_scope(); + statement("static_cast<", impl_type, "*>(shader)->invoke();"); + end_scope(); + + statement(""); + statement("static const struct spirv_cross_interface vtable ="); + begin_scope(); + statement("spirv_cross_construct,"); + statement("spirv_cross_destruct,"); + statement("spirv_cross_invoke,"); + end_scope_decl(); + + statement(""); + statement("const struct spirv_cross_interface *", + interface_name.empty() ? string("spirv_cross_get_interface") : interface_name, "(void)"); + begin_scope(); + statement("return &vtable;"); + end_scope(); +} + +void CompilerCPP::emit_function_prototype(SPIRFunction &func, const Bitset &) +{ + if (func.self != ir.default_entry_point) + add_function_overload(func); + + local_variable_names = resource_names; + string decl; + + auto &type = get(func.return_type); + decl += "inline "; + decl += type_to_glsl(type); + decl += " "; + + if (func.self == ir.default_entry_point) + { + decl += "main"; + processing_entry_point = true; + } + else + decl += to_name(func.self); + + decl += "("; + for (auto &arg : func.arguments) + { + add_local_variable_name(arg.id); + + decl += argument_decl(arg); + if (&arg != &func.arguments.back()) + decl += ", "; + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + decl += ")"; + statement(decl); +} + +string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg) +{ + auto &type = expression_type(arg.id); + bool constref = !type.pointer || arg.write_count == 0; + + auto &var = get(arg.id); + + string base = type_to_glsl(type); + string variable_name = to_name(var.self); + remap_variable_type_name(type, variable_name, base); + + for (uint32_t i = 0; i < type.array.size(); i++) + base = join("std::array<", base, ", ", to_array_size(type, i), ">"); + + return join(constref ? "const " : "", base, " &", variable_name); +} + +string CompilerCPP::variable_decl(const SPIRType &type, const string &name, uint32_t /* id */) +{ + string base = type_to_glsl(type); + remap_variable_type_name(type, name, base); + bool runtime = false; + + for (uint32_t i = 0; i < type.array.size(); i++) + { + auto &array = type.array[i]; + if (!array && type.array_size_literal[i]) + { + // Avoid using runtime arrays with std::array since this is undefined. + // Runtime arrays cannot be passed around as values, so this is fine. + runtime = true; + } + else + base = join("std::array<", base, ", ", to_array_size(type, i), ">"); + } + base += ' '; + return base + name + (runtime ? "[1]" : ""); +} + +void CompilerCPP::emit_header() +{ + auto &execution = get_entry_point(); + + statement("// This C++ shader is autogenerated by spirv-cross."); + statement("#include \"spirv_cross/internal_interface.hpp\""); + statement("#include \"spirv_cross/external_interface.h\""); + // Needed to properly implement GLSL-style arrays. + statement("#include "); + statement("#include "); + statement(""); + statement("using namespace spirv_cross;"); + statement("using namespace glm;"); + statement(""); + + statement("namespace Impl"); + begin_scope(); + + switch (execution.model) + { + case ExecutionModelGeometry: + case ExecutionModelTessellationControl: + case ExecutionModelTessellationEvaluation: + case ExecutionModelGLCompute: + case ExecutionModelFragment: + case ExecutionModelVertex: + statement("struct Shader"); + begin_scope(); + break; + + default: + SPIRV_CROSS_THROW("Unsupported execution model."); + } + + switch (execution.model) + { + case ExecutionModelGeometry: + impl_type = "GeometryShader"; + resource_type = "GeometryResources"; + break; + + case ExecutionModelVertex: + impl_type = "VertexShader"; + resource_type = "VertexResources"; + break; + + case ExecutionModelFragment: + impl_type = "FragmentShader"; + resource_type = "FragmentResources"; + break; + + case ExecutionModelGLCompute: + impl_type = join("ComputeShader"); + resource_type = "ComputeResources"; + break; + + case ExecutionModelTessellationControl: + impl_type = "TessControlShader"; + resource_type = "TessControlResources"; + break; + + case ExecutionModelTessellationEvaluation: + impl_type = "TessEvaluationShader"; + resource_type = "TessEvaluationResources"; + break; + + default: + SPIRV_CROSS_THROW("Unsupported execution model."); + } +} diff --git a/third_party/spirv-cross/spirv_cpp.hpp b/third_party/spirv-cross/spirv_cpp.hpp new file mode 100644 index 0000000..e78d036 --- /dev/null +++ b/third_party/spirv-cross/spirv_cpp.hpp @@ -0,0 +1,86 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_CPP_HPP +#define SPIRV_CROSS_CPP_HPP + +#include "spirv_glsl.hpp" +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +class CompilerCPP : public CompilerGLSL +{ +public: + explicit CompilerCPP(std::vector spirv_) + : CompilerGLSL(std::move(spirv_)) + { + } + + CompilerCPP(const uint32_t *ir_, size_t word_count) + : CompilerGLSL(ir_, word_count) + { + } + + explicit CompilerCPP(const ParsedIR &ir_) + : CompilerGLSL(ir_) + { + } + + explicit CompilerCPP(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) + { + } + + std::string compile() override; + + // Sets a custom symbol name that can override + // spirv_cross_get_interface. + // + // Useful when several shader interfaces are linked + // statically into the same binary. + void set_interface_name(std::string name) + { + interface_name = std::move(name); + } + +private: + void emit_header() override; + void emit_c_linkage(); + void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; + + void emit_resources(); + void emit_buffer_block(const SPIRVariable &type) override; + void emit_push_constant_block(const SPIRVariable &var) override; + void emit_interface_block(const SPIRVariable &type); + void emit_block_chain(SPIRBlock &block); + void emit_uniform(const SPIRVariable &var) override; + void emit_shared(const SPIRVariable &var); + void emit_block_struct(SPIRType &type); + std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id) override; + + std::string argument_decl(const SPIRFunction::Parameter &arg); + + SmallVector resource_registrations; + std::string impl_type; + std::string resource_type; + uint32_t shared_counter = 0; + + std::string interface_name; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_cross.cpp b/third_party/spirv-cross/spirv_cross.cpp new file mode 100644 index 0000000..21d2f2f --- /dev/null +++ b/third_party/spirv-cross/spirv_cross.cpp @@ -0,0 +1,4870 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cross.hpp" +#include "GLSL.std.450.h" +#include "spirv_cfg.hpp" +#include "spirv_common.hpp" +#include "spirv_parser.hpp" +#include +#include +#include + +using namespace std; +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; + +Compiler::Compiler(vector ir_) +{ + Parser parser(move(ir_)); + parser.parse(); + set_ir(move(parser.get_parsed_ir())); +} + +Compiler::Compiler(const uint32_t *ir_, size_t word_count) +{ + Parser parser(ir_, word_count); + parser.parse(); + set_ir(move(parser.get_parsed_ir())); +} + +Compiler::Compiler(const ParsedIR &ir_) +{ + set_ir(ir_); +} + +Compiler::Compiler(ParsedIR &&ir_) +{ + set_ir(move(ir_)); +} + +void Compiler::set_ir(ParsedIR &&ir_) +{ + ir = move(ir_); + parse_fixup(); +} + +void Compiler::set_ir(const ParsedIR &ir_) +{ + ir = ir_; + parse_fixup(); +} + +string Compiler::compile() +{ + return ""; +} + +bool Compiler::variable_storage_is_aliased(const SPIRVariable &v) +{ + auto &type = get(v.basetype); + bool ssbo = v.storage == StorageClassStorageBuffer || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + bool image = type.basetype == SPIRType::Image; + bool counter = type.basetype == SPIRType::AtomicCounter; + bool buffer_reference = type.storage == StorageClassPhysicalStorageBufferEXT; + + bool is_restrict; + if (ssbo) + is_restrict = ir.get_buffer_block_flags(v).get(DecorationRestrict); + else + is_restrict = has_decoration(v.self, DecorationRestrict); + + return !is_restrict && (ssbo || image || counter || buffer_reference); +} + +bool Compiler::block_is_pure(const SPIRBlock &block) +{ + // This is a global side effect of the function. + if (block.terminator == SPIRBlock::Kill) + return false; + + for (auto &i : block.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + switch (op) + { + case OpFunctionCall: + { + uint32_t func = ops[2]; + if (!function_is_pure(get(func))) + return false; + break; + } + + case OpCopyMemory: + case OpStore: + { + auto &type = expression_type(ops[0]); + if (type.storage != StorageClassFunction) + return false; + break; + } + + case OpImageWrite: + return false; + + // Atomics are impure. + case OpAtomicLoad: + case OpAtomicStore: + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicCompareExchangeWeak: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + return false; + + // Geometry shader builtins modify global state. + case OpEndPrimitive: + case OpEmitStreamVertex: + case OpEndStreamPrimitive: + case OpEmitVertex: + return false; + + // Barriers disallow any reordering, so we should treat blocks with barrier as writing. + case OpControlBarrier: + case OpMemoryBarrier: + return false; + + // Ray tracing builtins are impure. + case OpReportIntersectionNV: + case OpIgnoreIntersectionNV: + case OpTerminateRayNV: + case OpTraceNV: + case OpExecuteCallableNV: + return false; + + // OpExtInst is potentially impure depending on extension, but GLSL builtins are at least pure. + + case OpDemoteToHelperInvocationEXT: + // This is a global side effect of the function. + return false; + + default: + break; + } + } + + return true; +} + +string Compiler::to_name(uint32_t id, bool allow_alias) const +{ + if (allow_alias && ir.ids[id].get_type() == TypeType) + { + // If this type is a simple alias, emit the + // name of the original type instead. + // We don't want to override the meta alias + // as that can be overridden by the reflection APIs after parse. + auto &type = get(id); + if (type.type_alias) + { + // If the alias master has been specially packed, we will have emitted a clean variant as well, + // so skip the name aliasing here. + if (!has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + return to_name(type.type_alias); + } + } + + auto &alias = ir.get_name(id); + if (alias.empty()) + return join("_", id); + else + return alias; +} + +bool Compiler::function_is_pure(const SPIRFunction &func) +{ + for (auto block : func.blocks) + { + if (!block_is_pure(get(block))) + { + //fprintf(stderr, "Function %s is impure!\n", to_name(func.self).c_str()); + return false; + } + } + + //fprintf(stderr, "Function %s is pure!\n", to_name(func.self).c_str()); + return true; +} + +void Compiler::register_global_read_dependencies(const SPIRBlock &block, uint32_t id) +{ + for (auto &i : block.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + switch (op) + { + case OpFunctionCall: + { + uint32_t func = ops[2]; + register_global_read_dependencies(get(func), id); + break; + } + + case OpLoad: + case OpImageRead: + { + // If we're in a storage class which does not get invalidated, adding dependencies here is no big deal. + auto *var = maybe_get_backing_variable(ops[2]); + if (var && var->storage != StorageClassFunction) + { + auto &type = get(var->basetype); + + // InputTargets are immutable. + if (type.basetype != SPIRType::Image && type.image.dim != DimSubpassData) + var->dependees.push_back(id); + } + break; + } + + default: + break; + } + } +} + +void Compiler::register_global_read_dependencies(const SPIRFunction &func, uint32_t id) +{ + for (auto block : func.blocks) + register_global_read_dependencies(get(block), id); +} + +SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain) +{ + auto *var = maybe_get(chain); + if (!var) + { + auto *cexpr = maybe_get(chain); + if (cexpr) + var = maybe_get(cexpr->loaded_from); + + auto *access_chain = maybe_get(chain); + if (access_chain) + var = maybe_get(access_chain->loaded_from); + } + + return var; +} + +StorageClass Compiler::get_expression_effective_storage_class(uint32_t ptr) +{ + auto *var = maybe_get_backing_variable(ptr); + + // If the expression has been lowered to a temporary, we need to use the Generic storage class. + // We're looking for the effective storage class of a given expression. + // An access chain or forwarded OpLoads from such access chains + // will generally have the storage class of the underlying variable, but if the load was not forwarded + // we have lost any address space qualifiers. + bool forced_temporary = ir.ids[ptr].get_type() == TypeExpression && !get(ptr).access_chain && + (forced_temporaries.count(ptr) != 0 || forwarded_temporaries.count(ptr) == 0); + + if (var && !forced_temporary) + { + // Normalize SSBOs to StorageBuffer here. + if (var->storage == StorageClassUniform && + has_decoration(get(var->basetype).self, DecorationBufferBlock)) + return StorageClassStorageBuffer; + else + return var->storage; + } + else + return expression_type(ptr).storage; +} + +void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded) +{ + auto &e = get(expr); + auto *var = maybe_get_backing_variable(chain); + + if (var) + { + e.loaded_from = var->self; + + // If the backing variable is immutable, we do not need to depend on the variable. + if (forwarded && !is_immutable(var->self)) + var->dependees.push_back(e.self); + + // If we load from a parameter, make sure we create "inout" if we also write to the parameter. + // The default is "in" however, so we never invalidate our compilation by reading. + if (var && var->parameter) + var->parameter->read_count++; + } +} + +void Compiler::register_write(uint32_t chain) +{ + auto *var = maybe_get(chain); + if (!var) + { + // If we're storing through an access chain, invalidate the backing variable instead. + auto *expr = maybe_get(chain); + if (expr && expr->loaded_from) + var = maybe_get(expr->loaded_from); + + auto *access_chain = maybe_get(chain); + if (access_chain && access_chain->loaded_from) + var = maybe_get(access_chain->loaded_from); + } + + auto &chain_type = expression_type(chain); + + if (var) + { + bool check_argument_storage_qualifier = true; + auto &type = expression_type(chain); + + // If our variable is in a storage class which can alias with other buffers, + // invalidate all variables which depend on aliased variables. And if this is a + // variable pointer, then invalidate all variables regardless. + if (get_variable_data_type(*var).pointer) + { + flush_all_active_variables(); + + if (type.pointer_depth == 1) + { + // We have a backing variable which is a pointer-to-pointer type. + // We are storing some data through a pointer acquired through that variable, + // but we are not writing to the value of the variable itself, + // i.e., we are not modifying the pointer directly. + // If we are storing a non-pointer type (pointer_depth == 1), + // we know that we are storing some unrelated data. + // A case here would be + // void foo(Foo * const *arg) { + // Foo *bar = *arg; + // bar->unrelated = 42; + // } + // arg, the argument is constant. + check_argument_storage_qualifier = false; + } + } + + if (type.storage == StorageClassPhysicalStorageBufferEXT || variable_storage_is_aliased(*var)) + flush_all_aliased_variables(); + else if (var) + flush_dependees(*var); + + // We tried to write to a parameter which is not marked with out qualifier, force a recompile. + if (check_argument_storage_qualifier && var->parameter && var->parameter->write_count == 0) + { + var->parameter->write_count++; + force_recompile(); + } + } + else if (chain_type.pointer) + { + // If we stored through a variable pointer, then we don't know which + // variable we stored to. So *all* expressions after this point need to + // be invalidated. + // FIXME: If we can prove that the variable pointer will point to + // only certain variables, we can invalidate only those. + flush_all_active_variables(); + } + + // If chain_type.pointer is false, we're not writing to memory backed variables, but temporaries instead. + // This can happen in copy_logical_type where we unroll complex reads and writes to temporaries. +} + +void Compiler::flush_dependees(SPIRVariable &var) +{ + for (auto expr : var.dependees) + invalid_expressions.insert(expr); + var.dependees.clear(); +} + +void Compiler::flush_all_aliased_variables() +{ + for (auto aliased : aliased_variables) + flush_dependees(get(aliased)); +} + +void Compiler::flush_all_atomic_capable_variables() +{ + for (auto global : global_variables) + flush_dependees(get(global)); + flush_all_aliased_variables(); +} + +void Compiler::flush_control_dependent_expressions(uint32_t block_id) +{ + auto &block = get(block_id); + for (auto &expr : block.invalidate_expressions) + invalid_expressions.insert(expr); + block.invalidate_expressions.clear(); +} + +void Compiler::flush_all_active_variables() +{ + // Invalidate all temporaries we read from variables in this block since they were forwarded. + // Invalidate all temporaries we read from globals. + for (auto &v : current_function->local_variables) + flush_dependees(get(v)); + for (auto &arg : current_function->arguments) + flush_dependees(get(arg.id)); + for (auto global : global_variables) + flush_dependees(get(global)); + + flush_all_aliased_variables(); +} + +uint32_t Compiler::expression_type_id(uint32_t id) const +{ + switch (ir.ids[id].get_type()) + { + case TypeVariable: + return get(id).basetype; + + case TypeExpression: + return get(id).expression_type; + + case TypeConstant: + return get(id).constant_type; + + case TypeConstantOp: + return get(id).basetype; + + case TypeUndef: + return get(id).basetype; + + case TypeCombinedImageSampler: + return get(id).combined_type; + + case TypeAccessChain: + return get(id).basetype; + + default: + SPIRV_CROSS_THROW("Cannot resolve expression type."); + } +} + +const SPIRType &Compiler::expression_type(uint32_t id) const +{ + return get(expression_type_id(id)); +} + +bool Compiler::expression_is_lvalue(uint32_t id) const +{ + auto &type = expression_type(id); + switch (type.basetype) + { + case SPIRType::SampledImage: + case SPIRType::Image: + case SPIRType::Sampler: + return false; + + default: + return true; + } +} + +bool Compiler::is_immutable(uint32_t id) const +{ + if (ir.ids[id].get_type() == TypeVariable) + { + auto &var = get(id); + + // Anything we load from the UniformConstant address space is guaranteed to be immutable. + bool pointer_to_const = var.storage == StorageClassUniformConstant; + return pointer_to_const || var.phi_variable || !expression_is_lvalue(id); + } + else if (ir.ids[id].get_type() == TypeAccessChain) + return get(id).immutable; + else if (ir.ids[id].get_type() == TypeExpression) + return get(id).immutable; + else if (ir.ids[id].get_type() == TypeConstant || ir.ids[id].get_type() == TypeConstantOp || + ir.ids[id].get_type() == TypeUndef) + return true; + else + return false; +} + +static inline bool storage_class_is_interface(spv::StorageClass storage) +{ + switch (storage) + { + case StorageClassInput: + case StorageClassOutput: + case StorageClassUniform: + case StorageClassUniformConstant: + case StorageClassAtomicCounter: + case StorageClassPushConstant: + case StorageClassStorageBuffer: + return true; + + default: + return false; + } +} + +bool Compiler::is_hidden_variable(const SPIRVariable &var, bool include_builtins) const +{ + if ((is_builtin_variable(var) && !include_builtins) || var.remapped_variable) + return true; + + // Combined image samplers are always considered active as they are "magic" variables. + if (find_if(begin(combined_image_samplers), end(combined_image_samplers), [&var](const CombinedImageSampler &samp) { + return samp.combined_id == var.self; + }) != end(combined_image_samplers)) + { + return false; + } + + bool hidden = false; + if (check_active_interface_variables && storage_class_is_interface(var.storage)) + hidden = active_interface_variables.find(var.self) == end(active_interface_variables); + return hidden; +} + +bool Compiler::is_builtin_type(const SPIRType &type) const +{ + auto *type_meta = ir.find_meta(type.self); + + // We can have builtin structs as well. If one member of a struct is builtin, the struct must also be builtin. + if (type_meta) + for (auto &m : type_meta->members) + if (m.builtin) + return true; + + return false; +} + +bool Compiler::is_builtin_variable(const SPIRVariable &var) const +{ + auto *m = ir.find_meta(var.self); + + if (var.compat_builtin || (m && m->decoration.builtin)) + return true; + else + return is_builtin_type(get(var.basetype)); +} + +bool Compiler::is_member_builtin(const SPIRType &type, uint32_t index, BuiltIn *builtin) const +{ + auto *type_meta = ir.find_meta(type.self); + + if (type_meta) + { + auto &memb = type_meta->members; + if (index < memb.size() && memb[index].builtin) + { + if (builtin) + *builtin = memb[index].builtin_type; + return true; + } + } + + return false; +} + +bool Compiler::is_scalar(const SPIRType &type) const +{ + return type.basetype != SPIRType::Struct && type.vecsize == 1 && type.columns == 1; +} + +bool Compiler::is_vector(const SPIRType &type) const +{ + return type.vecsize > 1 && type.columns == 1; +} + +bool Compiler::is_matrix(const SPIRType &type) const +{ + return type.vecsize > 1 && type.columns > 1; +} + +bool Compiler::is_array(const SPIRType &type) const +{ + return !type.array.empty(); +} + +ShaderResources Compiler::get_shader_resources() const +{ + return get_shader_resources(nullptr); +} + +ShaderResources Compiler::get_shader_resources(const unordered_set &active_variables) const +{ + return get_shader_resources(&active_variables); +} + +bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + uint32_t variable = 0; + switch (opcode) + { + // Need this first, otherwise, GCC complains about unhandled switch statements. + default: + break; + + case OpFunctionCall: + { + // Invalid SPIR-V. + if (length < 3) + return false; + + uint32_t count = length - 3; + args += 3; + for (uint32_t i = 0; i < count; i++) + { + auto *var = compiler.maybe_get(args[i]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[i]); + } + break; + } + + case OpSelect: + { + // Invalid SPIR-V. + if (length < 5) + return false; + + uint32_t count = length - 3; + args += 3; + for (uint32_t i = 0; i < count; i++) + { + auto *var = compiler.maybe_get(args[i]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[i]); + } + break; + } + + case OpPhi: + { + // Invalid SPIR-V. + if (length < 2) + return false; + + uint32_t count = length - 2; + args += 2; + for (uint32_t i = 0; i < count; i += 2) + { + auto *var = compiler.maybe_get(args[i]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[i]); + } + break; + } + + case OpAtomicStore: + case OpStore: + // Invalid SPIR-V. + if (length < 1) + return false; + variable = args[0]; + break; + + case OpCopyMemory: + { + if (length < 2) + return false; + + auto *var = compiler.maybe_get(args[0]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[0]); + + var = compiler.maybe_get(args[1]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[1]); + break; + } + + case OpExtInst: + { + if (length < 5) + return false; + auto &extension_set = compiler.get(args[2]); + switch (extension_set.ext) + { + case SPIRExtension::GLSL: + { + auto op = static_cast(args[3]); + + switch (op) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + auto *var = compiler.maybe_get(args[4]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[4]); + break; + } + + default: + break; + } + break; + } + case SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter: + { + enum AMDShaderExplicitVertexParameter + { + InterpolateAtVertexAMD = 1 + }; + + auto op = static_cast(args[3]); + + switch (op) + { + case InterpolateAtVertexAMD: + { + auto *var = compiler.maybe_get(args[4]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[4]); + break; + } + + default: + break; + } + break; + } + default: + break; + } + break; + } + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + case OpLoad: + case OpCopyObject: + case OpImageTexelPointer: + case OpAtomicLoad: + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicCompareExchangeWeak: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + case OpArrayLength: + // Invalid SPIR-V. + if (length < 3) + return false; + variable = args[2]; + break; + } + + if (variable) + { + auto *var = compiler.maybe_get(variable); + if (var && storage_class_is_interface(var->storage)) + variables.insert(variable); + } + return true; +} + +unordered_set Compiler::get_active_interface_variables() const +{ + // Traverse the call graph and find all interface variables which are in use. + unordered_set variables; + InterfaceVariableAccessHandler handler(*this, variables); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + // Make sure we preserve output variables which are only initialized, but never accessed by any code. + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (var.storage == StorageClassOutput && var.initializer != ID(0)) + variables.insert(var.self); + }); + + // If we needed to create one, we'll need it. + if (dummy_sampler_id) + variables.insert(dummy_sampler_id); + + return variables; +} + +void Compiler::set_enabled_interface_variables(std::unordered_set active_variables) +{ + active_interface_variables = move(active_variables); + check_active_interface_variables = true; +} + +ShaderResources Compiler::get_shader_resources(const unordered_set *active_variables) const +{ + ShaderResources res; + + bool ssbo_instance_name = reflection_ssbo_instance_name_is_significant(); + + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + auto &type = this->get(var.basetype); + + // It is possible for uniform storage classes to be passed as function parameters, so detect + // that. To detect function parameters, check of StorageClass of variable is function scope. + if (var.storage == StorageClassFunction || !type.pointer || is_builtin_variable(var)) + return; + + if (active_variables && active_variables->find(var.self) == end(*active_variables)) + return; + + // Input + if (var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self)) + { + if (has_decoration(type.self, DecorationBlock)) + { + res.stage_inputs.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) }); + } + else + res.stage_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Subpass inputs + else if (var.storage == StorageClassUniformConstant && type.image.dim == DimSubpassData) + { + res.subpass_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Outputs + else if (var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self)) + { + if (has_decoration(type.self, DecorationBlock)) + { + res.stage_outputs.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) }); + } + else + res.stage_outputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // UBOs + else if (type.storage == StorageClassUniform && has_decoration(type.self, DecorationBlock)) + { + res.uniform_buffers.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) }); + } + // Old way to declare SSBOs. + else if (type.storage == StorageClassUniform && has_decoration(type.self, DecorationBufferBlock)) + { + res.storage_buffers.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, ssbo_instance_name) }); + } + // Modern way to declare SSBOs. + else if (type.storage == StorageClassStorageBuffer) + { + res.storage_buffers.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, ssbo_instance_name) }); + } + // Push constant blocks + else if (type.storage == StorageClassPushConstant) + { + // There can only be one push constant block, but keep the vector in case this restriction is lifted + // in the future. + res.push_constant_buffers.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Images + else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Image && + type.image.sampled == 2) + { + res.storage_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Separate images + else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Image && + type.image.sampled == 1) + { + res.separate_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Separate samplers + else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Sampler) + { + res.separate_samplers.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Textures + else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::SampledImage) + { + res.sampled_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Atomic counters + else if (type.storage == StorageClassAtomicCounter) + { + res.atomic_counters.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Acceleration structures + else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::AccelerationStructure) + { + res.acceleration_structures.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + }); + + return res; +} + +bool Compiler::type_is_block_like(const SPIRType &type) const +{ + if (type.basetype != SPIRType::Struct) + return false; + + if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock)) + { + return true; + } + + // Block-like types may have Offset decorations. + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + if (has_member_decoration(type.self, i, DecorationOffset)) + return true; + + return false; +} + +void Compiler::parse_fixup() +{ + // Figure out specialization constants for work group sizes. + for (auto id_ : ir.ids_for_constant_or_variable) + { + auto &id = ir.ids[id_]; + + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + if (ir.meta[c.self].decoration.builtin && ir.meta[c.self].decoration.builtin_type == BuiltInWorkgroupSize) + { + // In current SPIR-V, there can be just one constant like this. + // All entry points will receive the constant value. + for (auto &entry : ir.entry_points) + { + entry.second.workgroup_size.constant = c.self; + entry.second.workgroup_size.x = c.scalar(0, 0); + entry.second.workgroup_size.y = c.scalar(0, 1); + entry.second.workgroup_size.z = c.scalar(0, 2); + } + } + } + else if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + if (var.storage == StorageClassPrivate || var.storage == StorageClassWorkgroup || + var.storage == StorageClassOutput) + global_variables.push_back(var.self); + if (variable_storage_is_aliased(var)) + aliased_variables.push_back(var.self); + } + } +} + +void Compiler::update_name_cache(unordered_set &cache_primary, const unordered_set &cache_secondary, + string &name) +{ + if (name.empty()) + return; + + const auto find_name = [&](const string &n) -> bool { + if (cache_primary.find(n) != end(cache_primary)) + return true; + + if (&cache_primary != &cache_secondary) + if (cache_secondary.find(n) != end(cache_secondary)) + return true; + + return false; + }; + + const auto insert_name = [&](const string &n) { cache_primary.insert(n); }; + + if (!find_name(name)) + { + insert_name(name); + return; + } + + uint32_t counter = 0; + auto tmpname = name; + + bool use_linked_underscore = true; + + if (tmpname == "_") + { + // We cannot just append numbers, as we will end up creating internally reserved names. + // Make it like _0_ instead. + tmpname += "0"; + } + else if (tmpname.back() == '_') + { + // The last_character is an underscore, so we don't need to link in underscore. + // This would violate double underscore rules. + use_linked_underscore = false; + } + + // If there is a collision (very rare), + // keep tacking on extra identifier until it's unique. + do + { + counter++; + name = tmpname + (use_linked_underscore ? "_" : "") + convert_to_string(counter); + } while (find_name(name)); + insert_name(name); +} + +void Compiler::update_name_cache(unordered_set &cache, string &name) +{ + update_name_cache(cache, cache, name); +} + +void Compiler::set_name(ID id, const std::string &name) +{ + ir.set_name(id, name); +} + +const SPIRType &Compiler::get_type(TypeID id) const +{ + return get(id); +} + +const SPIRType &Compiler::get_type_from_variable(VariableID id) const +{ + return get(get(id).basetype); +} + +uint32_t Compiler::get_pointee_type_id(uint32_t type_id) const +{ + auto *p_type = &get(type_id); + if (p_type->pointer) + { + assert(p_type->parent_type); + type_id = p_type->parent_type; + } + return type_id; +} + +const SPIRType &Compiler::get_pointee_type(const SPIRType &type) const +{ + auto *p_type = &type; + if (p_type->pointer) + { + assert(p_type->parent_type); + p_type = &get(p_type->parent_type); + } + return *p_type; +} + +const SPIRType &Compiler::get_pointee_type(uint32_t type_id) const +{ + return get_pointee_type(get(type_id)); +} + +uint32_t Compiler::get_variable_data_type_id(const SPIRVariable &var) const +{ + if (var.phi_variable) + return var.basetype; + return get_pointee_type_id(var.basetype); +} + +SPIRType &Compiler::get_variable_data_type(const SPIRVariable &var) +{ + return get(get_variable_data_type_id(var)); +} + +const SPIRType &Compiler::get_variable_data_type(const SPIRVariable &var) const +{ + return get(get_variable_data_type_id(var)); +} + +SPIRType &Compiler::get_variable_element_type(const SPIRVariable &var) +{ + SPIRType *type = &get_variable_data_type(var); + if (is_array(*type)) + type = &get(type->parent_type); + return *type; +} + +const SPIRType &Compiler::get_variable_element_type(const SPIRVariable &var) const +{ + const SPIRType *type = &get_variable_data_type(var); + if (is_array(*type)) + type = &get(type->parent_type); + return *type; +} + +bool Compiler::is_sampled_image_type(const SPIRType &type) +{ + return (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage) && type.image.sampled == 1 && + type.image.dim != DimBuffer; +} + +void Compiler::set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration, + const std::string &argument) +{ + ir.set_member_decoration_string(id, index, decoration, argument); +} + +void Compiler::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument) +{ + ir.set_member_decoration(id, index, decoration, argument); +} + +void Compiler::set_member_name(TypeID id, uint32_t index, const std::string &name) +{ + ir.set_member_name(id, index, name); +} + +const std::string &Compiler::get_member_name(TypeID id, uint32_t index) const +{ + return ir.get_member_name(id, index); +} + +void Compiler::set_qualified_name(uint32_t id, const string &name) +{ + ir.meta[id].decoration.qualified_alias = name; +} + +void Compiler::set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name) +{ + ir.meta[type_id].members.resize(max(ir.meta[type_id].members.size(), size_t(index) + 1)); + ir.meta[type_id].members[index].qualified_alias = name; +} + +const string &Compiler::get_member_qualified_name(TypeID type_id, uint32_t index) const +{ + auto *m = ir.find_meta(type_id); + if (m && index < m->members.size()) + return m->members[index].qualified_alias; + else + return ir.get_empty_string(); +} + +uint32_t Compiler::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const +{ + return ir.get_member_decoration(id, index, decoration); +} + +const Bitset &Compiler::get_member_decoration_bitset(TypeID id, uint32_t index) const +{ + return ir.get_member_decoration_bitset(id, index); +} + +bool Compiler::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const +{ + return ir.has_member_decoration(id, index, decoration); +} + +void Compiler::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration) +{ + ir.unset_member_decoration(id, index, decoration); +} + +void Compiler::set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument) +{ + ir.set_decoration_string(id, decoration, argument); +} + +void Compiler::set_decoration(ID id, Decoration decoration, uint32_t argument) +{ + ir.set_decoration(id, decoration, argument); +} + +void Compiler::set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value) +{ + auto &dec = ir.meta[id].decoration; + dec.extended.flags.set(decoration); + dec.extended.values[decoration] = value; +} + +void Compiler::set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration, + uint32_t value) +{ + ir.meta[type].members.resize(max(ir.meta[type].members.size(), size_t(index) + 1)); + auto &dec = ir.meta[type].members[index]; + dec.extended.flags.set(decoration); + dec.extended.values[decoration] = value; +} + +static uint32_t get_default_extended_decoration(ExtendedDecorations decoration) +{ + switch (decoration) + { + case SPIRVCrossDecorationResourceIndexPrimary: + case SPIRVCrossDecorationResourceIndexSecondary: + case SPIRVCrossDecorationResourceIndexTertiary: + case SPIRVCrossDecorationResourceIndexQuaternary: + case SPIRVCrossDecorationInterfaceMemberIndex: + return ~(0u); + + default: + return 0; + } +} + +uint32_t Compiler::get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const +{ + auto *m = ir.find_meta(id); + if (!m) + return 0; + + auto &dec = m->decoration; + + if (!dec.extended.flags.get(decoration)) + return get_default_extended_decoration(decoration); + + return dec.extended.values[decoration]; +} + +uint32_t Compiler::get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const +{ + auto *m = ir.find_meta(type); + if (!m) + return 0; + + if (index >= m->members.size()) + return 0; + + auto &dec = m->members[index]; + if (!dec.extended.flags.get(decoration)) + return get_default_extended_decoration(decoration); + return dec.extended.values[decoration]; +} + +bool Compiler::has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const +{ + auto *m = ir.find_meta(id); + if (!m) + return false; + + auto &dec = m->decoration; + return dec.extended.flags.get(decoration); +} + +bool Compiler::has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const +{ + auto *m = ir.find_meta(type); + if (!m) + return false; + + if (index >= m->members.size()) + return false; + + auto &dec = m->members[index]; + return dec.extended.flags.get(decoration); +} + +void Compiler::unset_extended_decoration(uint32_t id, ExtendedDecorations decoration) +{ + auto &dec = ir.meta[id].decoration; + dec.extended.flags.clear(decoration); + dec.extended.values[decoration] = 0; +} + +void Compiler::unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) +{ + ir.meta[type].members.resize(max(ir.meta[type].members.size(), size_t(index) + 1)); + auto &dec = ir.meta[type].members[index]; + dec.extended.flags.clear(decoration); + dec.extended.values[decoration] = 0; +} + +StorageClass Compiler::get_storage_class(VariableID id) const +{ + return get(id).storage; +} + +const std::string &Compiler::get_name(ID id) const +{ + return ir.get_name(id); +} + +const std::string Compiler::get_fallback_name(ID id) const +{ + return join("_", id); +} + +const std::string Compiler::get_block_fallback_name(VariableID id) const +{ + auto &var = get(id); + if (get_name(id).empty()) + return join("_", get(var.basetype).self, "_", id); + else + return get_name(id); +} + +const Bitset &Compiler::get_decoration_bitset(ID id) const +{ + return ir.get_decoration_bitset(id); +} + +bool Compiler::has_decoration(ID id, Decoration decoration) const +{ + return ir.has_decoration(id, decoration); +} + +const string &Compiler::get_decoration_string(ID id, Decoration decoration) const +{ + return ir.get_decoration_string(id, decoration); +} + +const string &Compiler::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const +{ + return ir.get_member_decoration_string(id, index, decoration); +} + +uint32_t Compiler::get_decoration(ID id, Decoration decoration) const +{ + return ir.get_decoration(id, decoration); +} + +void Compiler::unset_decoration(ID id, Decoration decoration) +{ + ir.unset_decoration(id, decoration); +} + +bool Compiler::get_binary_offset_for_decoration(VariableID id, spv::Decoration decoration, uint32_t &word_offset) const +{ + auto *m = ir.find_meta(id); + if (!m) + return false; + + auto &word_offsets = m->decoration_word_offset; + auto itr = word_offsets.find(decoration); + if (itr == end(word_offsets)) + return false; + + word_offset = itr->second; + return true; +} + +bool Compiler::block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const +{ + // Tried and failed. + if (block.disable_block_optimization || block.complex_continue) + return false; + + if (method == SPIRBlock::MergeToSelectForLoop || method == SPIRBlock::MergeToSelectContinueForLoop) + { + // Try to detect common for loop pattern + // which the code backend can use to create cleaner code. + // for(;;) { if (cond) { some_body; } else { break; } } + // is the pattern we're looking for. + const auto *false_block = maybe_get(block.false_block); + const auto *true_block = maybe_get(block.true_block); + const auto *merge_block = maybe_get(block.merge_block); + + bool false_block_is_merge = block.false_block == block.merge_block || + (false_block && merge_block && execution_is_noop(*false_block, *merge_block)); + + bool true_block_is_merge = block.true_block == block.merge_block || + (true_block && merge_block && execution_is_noop(*true_block, *merge_block)); + + bool positive_candidate = + block.true_block != block.merge_block && block.true_block != block.self && false_block_is_merge; + + bool negative_candidate = + block.false_block != block.merge_block && block.false_block != block.self && true_block_is_merge; + + bool ret = block.terminator == SPIRBlock::Select && block.merge == SPIRBlock::MergeLoop && + (positive_candidate || negative_candidate); + + if (ret && positive_candidate && method == SPIRBlock::MergeToSelectContinueForLoop) + ret = block.true_block == block.continue_block; + else if (ret && negative_candidate && method == SPIRBlock::MergeToSelectContinueForLoop) + ret = block.false_block == block.continue_block; + + // If we have OpPhi which depends on branches which came from our own block, + // we need to flush phi variables in else block instead of a trivial break, + // so we cannot assume this is a for loop candidate. + if (ret) + { + for (auto &phi : block.phi_variables) + if (phi.parent == block.self) + return false; + + auto *merge = maybe_get(block.merge_block); + if (merge) + for (auto &phi : merge->phi_variables) + if (phi.parent == block.self) + return false; + } + return ret; + } + else if (method == SPIRBlock::MergeToDirectForLoop) + { + // Empty loop header that just sets up merge target + // and branches to loop body. + bool ret = block.terminator == SPIRBlock::Direct && block.merge == SPIRBlock::MergeLoop && block.ops.empty(); + + if (!ret) + return false; + + auto &child = get(block.next_block); + + const auto *false_block = maybe_get(child.false_block); + const auto *true_block = maybe_get(child.true_block); + const auto *merge_block = maybe_get(block.merge_block); + + bool false_block_is_merge = child.false_block == block.merge_block || + (false_block && merge_block && execution_is_noop(*false_block, *merge_block)); + + bool true_block_is_merge = child.true_block == block.merge_block || + (true_block && merge_block && execution_is_noop(*true_block, *merge_block)); + + bool positive_candidate = + child.true_block != block.merge_block && child.true_block != block.self && false_block_is_merge; + + bool negative_candidate = + child.false_block != block.merge_block && child.false_block != block.self && true_block_is_merge; + + ret = child.terminator == SPIRBlock::Select && child.merge == SPIRBlock::MergeNone && + (positive_candidate || negative_candidate); + + // If we have OpPhi which depends on branches which came from our own block, + // we need to flush phi variables in else block instead of a trivial break, + // so we cannot assume this is a for loop candidate. + if (ret) + { + for (auto &phi : block.phi_variables) + if (phi.parent == block.self || phi.parent == child.self) + return false; + + for (auto &phi : child.phi_variables) + if (phi.parent == block.self) + return false; + + auto *merge = maybe_get(block.merge_block); + if (merge) + for (auto &phi : merge->phi_variables) + if (phi.parent == block.self || phi.parent == child.false_block) + return false; + } + + return ret; + } + else + return false; +} + +bool Compiler::execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const +{ + if (!execution_is_branchless(from, to)) + return false; + + auto *start = &from; + for (;;) + { + if (start->self == to.self) + return true; + + if (!start->ops.empty()) + return false; + + auto &next = get(start->next_block); + // Flushing phi variables does not count as noop. + for (auto &phi : next.phi_variables) + if (phi.parent == start->self) + return false; + + start = &next; + } +} + +bool Compiler::execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const +{ + auto *start = &from; + for (;;) + { + if (start->self == to.self) + return true; + + if (start->terminator == SPIRBlock::Direct && start->merge == SPIRBlock::MergeNone) + start = &get(start->next_block); + else + return false; + } +} + +bool Compiler::execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const +{ + return from.terminator == SPIRBlock::Direct && from.merge == SPIRBlock::MergeNone && from.next_block == to.self; +} + +SPIRBlock::ContinueBlockType Compiler::continue_block_type(const SPIRBlock &block) const +{ + // The block was deemed too complex during code emit, pick conservative fallback paths. + if (block.complex_continue) + return SPIRBlock::ComplexLoop; + + // In older glslang output continue block can be equal to the loop header. + // In this case, execution is clearly branchless, so just assume a while loop header here. + if (block.merge == SPIRBlock::MergeLoop) + return SPIRBlock::WhileLoop; + + if (block.loop_dominator == BlockID(SPIRBlock::NoDominator)) + { + // Continue block is never reached from CFG. + return SPIRBlock::ComplexLoop; + } + + auto &dominator = get(block.loop_dominator); + + if (execution_is_noop(block, dominator)) + return SPIRBlock::WhileLoop; + else if (execution_is_branchless(block, dominator)) + return SPIRBlock::ForLoop; + else + { + const auto *false_block = maybe_get(block.false_block); + const auto *true_block = maybe_get(block.true_block); + const auto *merge_block = maybe_get(dominator.merge_block); + + // If we need to flush Phi in this block, we cannot have a DoWhile loop. + bool flush_phi_to_false = false_block && flush_phi_required(block.self, block.false_block); + bool flush_phi_to_true = true_block && flush_phi_required(block.self, block.true_block); + if (flush_phi_to_false || flush_phi_to_true) + return SPIRBlock::ComplexLoop; + + bool positive_do_while = block.true_block == dominator.self && + (block.false_block == dominator.merge_block || + (false_block && merge_block && execution_is_noop(*false_block, *merge_block))); + + bool negative_do_while = block.false_block == dominator.self && + (block.true_block == dominator.merge_block || + (true_block && merge_block && execution_is_noop(*true_block, *merge_block))); + + if (block.merge == SPIRBlock::MergeNone && block.terminator == SPIRBlock::Select && + (positive_do_while || negative_do_while)) + { + return SPIRBlock::DoWhileLoop; + } + else + return SPIRBlock::ComplexLoop; + } +} + +bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const +{ + handler.set_current_block(block); + handler.rearm_current_block(block); + + // Ideally, perhaps traverse the CFG instead of all blocks in order to eliminate dead blocks, + // but this shouldn't be a problem in practice unless the SPIR-V is doing insane things like recursing + // inside dead blocks ... + for (auto &i : block.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + if (!handler.handle(op, ops, i.length)) + return false; + + if (op == OpFunctionCall) + { + auto &func = get(ops[2]); + if (handler.follow_function_call(func)) + { + if (!handler.begin_function_scope(ops, i.length)) + return false; + if (!traverse_all_reachable_opcodes(get(ops[2]), handler)) + return false; + if (!handler.end_function_scope(ops, i.length)) + return false; + + handler.rearm_current_block(block); + } + } + } + + return true; +} + +bool Compiler::traverse_all_reachable_opcodes(const SPIRFunction &func, OpcodeHandler &handler) const +{ + for (auto block : func.blocks) + if (!traverse_all_reachable_opcodes(get(block), handler)) + return false; + + return true; +} + +uint32_t Compiler::type_struct_member_offset(const SPIRType &type, uint32_t index) const +{ + auto *type_meta = ir.find_meta(type.self); + if (type_meta) + { + // Decoration must be set in valid SPIR-V, otherwise throw. + auto &dec = type_meta->members[index]; + if (dec.decoration_flags.get(DecorationOffset)) + return dec.offset; + else + SPIRV_CROSS_THROW("Struct member does not have Offset set."); + } + else + SPIRV_CROSS_THROW("Struct member does not have Offset set."); +} + +uint32_t Compiler::type_struct_member_array_stride(const SPIRType &type, uint32_t index) const +{ + auto *type_meta = ir.find_meta(type.member_types[index]); + if (type_meta) + { + // Decoration must be set in valid SPIR-V, otherwise throw. + // ArrayStride is part of the array type not OpMemberDecorate. + auto &dec = type_meta->decoration; + if (dec.decoration_flags.get(DecorationArrayStride)) + return dec.array_stride; + else + SPIRV_CROSS_THROW("Struct member does not have ArrayStride set."); + } + else + SPIRV_CROSS_THROW("Struct member does not have ArrayStride set."); +} + +uint32_t Compiler::type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const +{ + auto *type_meta = ir.find_meta(type.self); + if (type_meta) + { + // Decoration must be set in valid SPIR-V, otherwise throw. + // MatrixStride is part of OpMemberDecorate. + auto &dec = type_meta->members[index]; + if (dec.decoration_flags.get(DecorationMatrixStride)) + return dec.matrix_stride; + else + SPIRV_CROSS_THROW("Struct member does not have MatrixStride set."); + } + else + SPIRV_CROSS_THROW("Struct member does not have MatrixStride set."); +} + +size_t Compiler::get_declared_struct_size(const SPIRType &type) const +{ + if (type.member_types.empty()) + SPIRV_CROSS_THROW("Declared struct in block cannot be empty."); + + uint32_t last = uint32_t(type.member_types.size() - 1); + size_t offset = type_struct_member_offset(type, last); + size_t size = get_declared_struct_member_size(type, last); + return offset + size; +} + +size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, size_t array_size) const +{ + if (type.member_types.empty()) + SPIRV_CROSS_THROW("Declared struct in block cannot be empty."); + + size_t size = get_declared_struct_size(type); + auto &last_type = get(type.member_types.back()); + if (!last_type.array.empty() && last_type.array_size_literal[0] && last_type.array[0] == 0) // Runtime array + size += array_size * type_struct_member_array_stride(type, uint32_t(type.member_types.size() - 1)); + + return size; +} + +uint32_t Compiler::evaluate_spec_constant_u32(const SPIRConstantOp &spec) const +{ + auto &result_type = get(spec.basetype); + if (result_type.basetype != SPIRType::UInt && result_type.basetype != SPIRType::Int && + result_type.basetype != SPIRType::Boolean) + { + SPIRV_CROSS_THROW( + "Only 32-bit integers and booleans are currently supported when evaluating specialization constants.\n"); + } + + if (!is_scalar(result_type)) + SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n"); + + uint32_t value = 0; + + const auto eval_u32 = [&](uint32_t id) -> uint32_t { + auto &type = expression_type(id); + if (type.basetype != SPIRType::UInt && type.basetype != SPIRType::Int && type.basetype != SPIRType::Boolean) + { + SPIRV_CROSS_THROW("Only 32-bit integers and booleans are currently supported when evaluating " + "specialization constants.\n"); + } + + if (!is_scalar(type)) + SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n"); + if (const auto *c = this->maybe_get(id)) + return c->scalar(); + else + return evaluate_spec_constant_u32(this->get(id)); + }; + +#define binary_spec_op(op, binary_op) \ + case Op##op: \ + value = eval_u32(spec.arguments[0]) binary_op eval_u32(spec.arguments[1]); \ + break +#define binary_spec_op_cast(op, binary_op, type) \ + case Op##op: \ + value = uint32_t(type(eval_u32(spec.arguments[0])) binary_op type(eval_u32(spec.arguments[1]))); \ + break + + // Support the basic opcodes which are typically used when computing array sizes. + switch (spec.opcode) + { + binary_spec_op(IAdd, +); + binary_spec_op(ISub, -); + binary_spec_op(IMul, *); + binary_spec_op(BitwiseAnd, &); + binary_spec_op(BitwiseOr, |); + binary_spec_op(BitwiseXor, ^); + binary_spec_op(LogicalAnd, &); + binary_spec_op(LogicalOr, |); + binary_spec_op(ShiftLeftLogical, <<); + binary_spec_op(ShiftRightLogical, >>); + binary_spec_op_cast(ShiftRightArithmetic, >>, int32_t); + binary_spec_op(LogicalEqual, ==); + binary_spec_op(LogicalNotEqual, !=); + binary_spec_op(IEqual, ==); + binary_spec_op(INotEqual, !=); + binary_spec_op(ULessThan, <); + binary_spec_op(ULessThanEqual, <=); + binary_spec_op(UGreaterThan, >); + binary_spec_op(UGreaterThanEqual, >=); + binary_spec_op_cast(SLessThan, <, int32_t); + binary_spec_op_cast(SLessThanEqual, <=, int32_t); + binary_spec_op_cast(SGreaterThan, >, int32_t); + binary_spec_op_cast(SGreaterThanEqual, >=, int32_t); +#undef binary_spec_op +#undef binary_spec_op_cast + + case OpLogicalNot: + value = uint32_t(!eval_u32(spec.arguments[0])); + break; + + case OpNot: + value = ~eval_u32(spec.arguments[0]); + break; + + case OpSNegate: + value = uint32_t(-int32_t(eval_u32(spec.arguments[0]))); + break; + + case OpSelect: + value = eval_u32(spec.arguments[0]) ? eval_u32(spec.arguments[1]) : eval_u32(spec.arguments[2]); + break; + + case OpUMod: + { + uint32_t a = eval_u32(spec.arguments[0]); + uint32_t b = eval_u32(spec.arguments[1]); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in UMod, b == 0.\n"); + value = a % b; + break; + } + + case OpSRem: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SRem, b == 0.\n"); + value = a % b; + break; + } + + case OpSMod: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SMod, b == 0.\n"); + auto v = a % b; + + // Makes sure we match the sign of b, not a. + if ((b < 0 && v > 0) || (b > 0 && v < 0)) + v += b; + value = v; + break; + } + + case OpUDiv: + { + uint32_t a = eval_u32(spec.arguments[0]); + uint32_t b = eval_u32(spec.arguments[1]); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in UDiv, b == 0.\n"); + value = a / b; + break; + } + + case OpSDiv: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SDiv, b == 0.\n"); + value = a / b; + break; + } + + default: + SPIRV_CROSS_THROW("Unsupported spec constant opcode for evaluation.\n"); + } + + return value; +} + +uint32_t Compiler::evaluate_constant_u32(uint32_t id) const +{ + if (const auto *c = maybe_get(id)) + return c->scalar(); + else + return evaluate_spec_constant_u32(get(id)); +} + +size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const +{ + if (struct_type.member_types.empty()) + SPIRV_CROSS_THROW("Declared struct in block cannot be empty."); + + auto &flags = get_member_decoration_bitset(struct_type.self, index); + auto &type = get(struct_type.member_types[index]); + + switch (type.basetype) + { + case SPIRType::Unknown: + case SPIRType::Void: + case SPIRType::Boolean: // Bools are purely logical, and cannot be used for externally visible types. + case SPIRType::AtomicCounter: + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::Sampler: + SPIRV_CROSS_THROW("Querying size for object with opaque size."); + + default: + break; + } + + if (type.pointer && type.storage == StorageClassPhysicalStorageBuffer) + { + // Check if this is a top-level pointer type, and not an array of pointers. + if (type.pointer_depth > get(type.parent_type).pointer_depth) + return 8; + } + + if (!type.array.empty()) + { + // For arrays, we can use ArrayStride to get an easy check. + bool array_size_literal = type.array_size_literal.back(); + uint32_t array_size = array_size_literal ? type.array.back() : evaluate_constant_u32(type.array.back()); + return type_struct_member_array_stride(struct_type, index) * array_size; + } + else if (type.basetype == SPIRType::Struct) + { + return get_declared_struct_size(type); + } + else + { + unsigned vecsize = type.vecsize; + unsigned columns = type.columns; + + // Vectors. + if (columns == 1) + { + size_t component_size = type.width / 8; + return vecsize * component_size; + } + else + { + uint32_t matrix_stride = type_struct_member_matrix_stride(struct_type, index); + + // Per SPIR-V spec, matrices must be tightly packed and aligned up for vec3 accesses. + if (flags.get(DecorationRowMajor)) + return matrix_stride * vecsize; + else if (flags.get(DecorationColMajor)) + return matrix_stride * columns; + else + SPIRV_CROSS_THROW("Either row-major or column-major must be declared for matrices."); + } + } +} + +bool Compiler::BufferAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + if (opcode != OpAccessChain && opcode != OpInBoundsAccessChain && opcode != OpPtrAccessChain) + return true; + + bool ptr_chain = (opcode == OpPtrAccessChain); + + // Invalid SPIR-V. + if (length < (ptr_chain ? 5u : 4u)) + return false; + + if (args[2] != id) + return true; + + // Don't bother traversing the entire access chain tree yet. + // If we access a struct member, assume we access the entire member. + uint32_t index = compiler.get(args[ptr_chain ? 4 : 3]).scalar(); + + // Seen this index already. + if (seen.find(index) != end(seen)) + return true; + seen.insert(index); + + auto &type = compiler.expression_type(id); + uint32_t offset = compiler.type_struct_member_offset(type, index); + + size_t range; + // If we have another member in the struct, deduce the range by looking at the next member. + // This is okay since structs in SPIR-V can have padding, but Offset decoration must be + // monotonically increasing. + // Of course, this doesn't take into account if the SPIR-V for some reason decided to add + // very large amounts of padding, but that's not really a big deal. + if (index + 1 < type.member_types.size()) + { + range = compiler.type_struct_member_offset(type, index + 1) - offset; + } + else + { + // No padding, so just deduce it from the size of the member directly. + range = compiler.get_declared_struct_member_size(type, index); + } + + ranges.push_back({ index, offset, range }); + return true; +} + +SmallVector Compiler::get_active_buffer_ranges(VariableID id) const +{ + SmallVector ranges; + BufferAccessHandler handler(*this, ranges, id); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + return ranges; +} + +bool Compiler::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const +{ + if (a.basetype != b.basetype) + return false; + if (a.width != b.width) + return false; + if (a.vecsize != b.vecsize) + return false; + if (a.columns != b.columns) + return false; + if (a.array.size() != b.array.size()) + return false; + + size_t array_count = a.array.size(); + if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0) + return false; + + if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage) + { + if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0) + return false; + } + + if (a.member_types.size() != b.member_types.size()) + return false; + + size_t member_types = a.member_types.size(); + for (size_t i = 0; i < member_types; i++) + { + if (!types_are_logically_equivalent(get(a.member_types[i]), get(b.member_types[i]))) + return false; + } + + return true; +} + +const Bitset &Compiler::get_execution_mode_bitset() const +{ + return get_entry_point().flags; +} + +void Compiler::set_execution_mode(ExecutionMode mode, uint32_t arg0, uint32_t arg1, uint32_t arg2) +{ + auto &execution = get_entry_point(); + + execution.flags.set(mode); + switch (mode) + { + case ExecutionModeLocalSize: + execution.workgroup_size.x = arg0; + execution.workgroup_size.y = arg1; + execution.workgroup_size.z = arg2; + break; + + case ExecutionModeInvocations: + execution.invocations = arg0; + break; + + case ExecutionModeOutputVertices: + execution.output_vertices = arg0; + break; + + default: + break; + } +} + +void Compiler::unset_execution_mode(ExecutionMode mode) +{ + auto &execution = get_entry_point(); + execution.flags.clear(mode); +} + +uint32_t Compiler::get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y, + SpecializationConstant &z) const +{ + auto &execution = get_entry_point(); + x = { 0, 0 }; + y = { 0, 0 }; + z = { 0, 0 }; + + if (execution.workgroup_size.constant != 0) + { + auto &c = get(execution.workgroup_size.constant); + + if (c.m.c[0].id[0] != ID(0)) + { + x.id = c.m.c[0].id[0]; + x.constant_id = get_decoration(c.m.c[0].id[0], DecorationSpecId); + } + + if (c.m.c[0].id[1] != ID(0)) + { + y.id = c.m.c[0].id[1]; + y.constant_id = get_decoration(c.m.c[0].id[1], DecorationSpecId); + } + + if (c.m.c[0].id[2] != ID(0)) + { + z.id = c.m.c[0].id[2]; + z.constant_id = get_decoration(c.m.c[0].id[2], DecorationSpecId); + } + } + + return execution.workgroup_size.constant; +} + +uint32_t Compiler::get_execution_mode_argument(spv::ExecutionMode mode, uint32_t index) const +{ + auto &execution = get_entry_point(); + switch (mode) + { + case ExecutionModeLocalSize: + switch (index) + { + case 0: + return execution.workgroup_size.x; + case 1: + return execution.workgroup_size.y; + case 2: + return execution.workgroup_size.z; + default: + return 0; + } + + case ExecutionModeInvocations: + return execution.invocations; + + case ExecutionModeOutputVertices: + return execution.output_vertices; + + default: + return 0; + } +} + +ExecutionModel Compiler::get_execution_model() const +{ + auto &execution = get_entry_point(); + return execution.model; +} + +bool Compiler::is_tessellation_shader(ExecutionModel model) +{ + return model == ExecutionModelTessellationControl || model == ExecutionModelTessellationEvaluation; +} + +bool Compiler::is_vertex_like_shader() const +{ + auto model = get_execution_model(); + return model == ExecutionModelVertex || model == ExecutionModelGeometry || + model == ExecutionModelTessellationControl || model == ExecutionModelTessellationEvaluation; +} + +bool Compiler::is_tessellation_shader() const +{ + return is_tessellation_shader(get_execution_model()); +} + +void Compiler::set_remapped_variable_state(VariableID id, bool remap_enable) +{ + get(id).remapped_variable = remap_enable; +} + +bool Compiler::get_remapped_variable_state(VariableID id) const +{ + return get(id).remapped_variable; +} + +void Compiler::set_subpass_input_remapped_components(VariableID id, uint32_t components) +{ + get(id).remapped_components = components; +} + +uint32_t Compiler::get_subpass_input_remapped_components(VariableID id) const +{ + return get(id).remapped_components; +} + +void Compiler::add_implied_read_expression(SPIRExpression &e, uint32_t source) +{ + auto itr = find(begin(e.implied_read_expressions), end(e.implied_read_expressions), ID(source)); + if (itr == end(e.implied_read_expressions)) + e.implied_read_expressions.push_back(source); +} + +void Compiler::add_implied_read_expression(SPIRAccessChain &e, uint32_t source) +{ + auto itr = find(begin(e.implied_read_expressions), end(e.implied_read_expressions), ID(source)); + if (itr == end(e.implied_read_expressions)) + e.implied_read_expressions.push_back(source); +} + +void Compiler::inherit_expression_dependencies(uint32_t dst, uint32_t source_expression) +{ + // Don't inherit any expression dependencies if the expression in dst + // is not a forwarded temporary. + if (forwarded_temporaries.find(dst) == end(forwarded_temporaries) || + forced_temporaries.find(dst) != end(forced_temporaries)) + { + return; + } + + auto &e = get(dst); + auto *phi = maybe_get(source_expression); + if (phi && phi->phi_variable) + { + // We have used a phi variable, which can change at the end of the block, + // so make sure we take a dependency on this phi variable. + phi->dependees.push_back(dst); + } + + auto *s = maybe_get(source_expression); + if (!s) + return; + + auto &e_deps = e.expression_dependencies; + auto &s_deps = s->expression_dependencies; + + // If we depend on a expression, we also depend on all sub-dependencies from source. + e_deps.push_back(source_expression); + e_deps.insert(end(e_deps), begin(s_deps), end(s_deps)); + + // Eliminate duplicated dependencies. + sort(begin(e_deps), end(e_deps)); + e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps)); +} + +SmallVector Compiler::get_entry_points_and_stages() const +{ + SmallVector entries; + for (auto &entry : ir.entry_points) + entries.push_back({ entry.second.orig_name, entry.second.model }); + return entries; +} + +void Compiler::rename_entry_point(const std::string &old_name, const std::string &new_name, spv::ExecutionModel model) +{ + auto &entry = get_entry_point(old_name, model); + entry.orig_name = new_name; + entry.name = new_name; +} + +void Compiler::set_entry_point(const std::string &name, spv::ExecutionModel model) +{ + auto &entry = get_entry_point(name, model); + ir.default_entry_point = entry.self; +} + +SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name) +{ + auto itr = find_if( + begin(ir.entry_points), end(ir.entry_points), + [&](const std::pair &entry) -> bool { return entry.second.orig_name == name; }); + + if (itr == end(ir.entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +const SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name) const +{ + auto itr = find_if( + begin(ir.entry_points), end(ir.entry_points), + [&](const std::pair &entry) -> bool { return entry.second.orig_name == name; }); + + if (itr == end(ir.entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model) +{ + auto itr = find_if(begin(ir.entry_points), end(ir.entry_points), + [&](const std::pair &entry) -> bool { + return entry.second.orig_name == name && entry.second.model == model; + }); + + if (itr == end(ir.entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +const SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model) const +{ + auto itr = find_if(begin(ir.entry_points), end(ir.entry_points), + [&](const std::pair &entry) -> bool { + return entry.second.orig_name == name && entry.second.model == model; + }); + + if (itr == end(ir.entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +const string &Compiler::get_cleansed_entry_point_name(const std::string &name, ExecutionModel model) const +{ + return get_entry_point(name, model).name; +} + +const SPIREntryPoint &Compiler::get_entry_point() const +{ + return ir.entry_points.find(ir.default_entry_point)->second; +} + +SPIREntryPoint &Compiler::get_entry_point() +{ + return ir.entry_points.find(ir.default_entry_point)->second; +} + +bool Compiler::interface_variable_exists_in_entry_point(uint32_t id) const +{ + auto &var = get(id); + if (var.storage != StorageClassInput && var.storage != StorageClassOutput && + var.storage != StorageClassUniformConstant) + SPIRV_CROSS_THROW("Only Input, Output variables and Uniform constants are part of a shader linking interface."); + + // This is to avoid potential problems with very old glslang versions which did + // not emit input/output interfaces properly. + // We can assume they only had a single entry point, and single entry point + // shaders could easily be assumed to use every interface variable anyways. + if (ir.entry_points.size() <= 1) + return true; + + auto &execution = get_entry_point(); + return find(begin(execution.interface_variables), end(execution.interface_variables), VariableID(id)) != + end(execution.interface_variables); +} + +void Compiler::CombinedImageSamplerHandler::push_remap_parameters(const SPIRFunction &func, const uint32_t *args, + uint32_t length) +{ + // If possible, pipe through a remapping table so that parameters know + // which variables they actually bind to in this scope. + unordered_map remapping; + for (uint32_t i = 0; i < length; i++) + remapping[func.arguments[i].id] = remap_parameter(args[i]); + parameter_remapping.push(move(remapping)); +} + +void Compiler::CombinedImageSamplerHandler::pop_remap_parameters() +{ + parameter_remapping.pop(); +} + +uint32_t Compiler::CombinedImageSamplerHandler::remap_parameter(uint32_t id) +{ + auto *var = compiler.maybe_get_backing_variable(id); + if (var) + id = var->self; + + if (parameter_remapping.empty()) + return id; + + auto &remapping = parameter_remapping.top(); + auto itr = remapping.find(id); + if (itr != end(remapping)) + return itr->second; + else + return id; +} + +bool Compiler::CombinedImageSamplerHandler::begin_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + + auto &callee = compiler.get(args[2]); + args += 3; + length -= 3; + push_remap_parameters(callee, args, length); + functions.push(&callee); + return true; +} + +bool Compiler::CombinedImageSamplerHandler::end_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + + auto &callee = compiler.get(args[2]); + args += 3; + + // There are two types of cases we have to handle, + // a callee might call sampler2D(texture2D, sampler) directly where + // one or more parameters originate from parameters. + // Alternatively, we need to provide combined image samplers to our callees, + // and in this case we need to add those as well. + + pop_remap_parameters(); + + // Our callee has now been processed at least once. + // No point in doing it again. + callee.do_combined_parameters = false; + + auto ¶ms = functions.top()->combined_parameters; + functions.pop(); + if (functions.empty()) + return true; + + auto &caller = *functions.top(); + if (caller.do_combined_parameters) + { + for (auto ¶m : params) + { + VariableID image_id = param.global_image ? param.image_id : VariableID(args[param.image_id]); + VariableID sampler_id = param.global_sampler ? param.sampler_id : VariableID(args[param.sampler_id]); + + auto *i = compiler.maybe_get_backing_variable(image_id); + auto *s = compiler.maybe_get_backing_variable(sampler_id); + if (i) + image_id = i->self; + if (s) + sampler_id = s->self; + + register_combined_image_sampler(caller, 0, image_id, sampler_id, param.depth); + } + } + + return true; +} + +void Compiler::CombinedImageSamplerHandler::register_combined_image_sampler(SPIRFunction &caller, + VariableID combined_module_id, + VariableID image_id, VariableID sampler_id, + bool depth) +{ + // We now have a texture ID and a sampler ID which will either be found as a global + // or a parameter in our own function. If both are global, they will not need a parameter, + // otherwise, add it to our list. + SPIRFunction::CombinedImageSamplerParameter param = { + 0u, image_id, sampler_id, true, true, depth, + }; + + auto texture_itr = find_if(begin(caller.arguments), end(caller.arguments), + [image_id](const SPIRFunction::Parameter &p) { return p.id == image_id; }); + auto sampler_itr = find_if(begin(caller.arguments), end(caller.arguments), + [sampler_id](const SPIRFunction::Parameter &p) { return p.id == sampler_id; }); + + if (texture_itr != end(caller.arguments)) + { + param.global_image = false; + param.image_id = uint32_t(texture_itr - begin(caller.arguments)); + } + + if (sampler_itr != end(caller.arguments)) + { + param.global_sampler = false; + param.sampler_id = uint32_t(sampler_itr - begin(caller.arguments)); + } + + if (param.global_image && param.global_sampler) + return; + + auto itr = find_if(begin(caller.combined_parameters), end(caller.combined_parameters), + [¶m](const SPIRFunction::CombinedImageSamplerParameter &p) { + return param.image_id == p.image_id && param.sampler_id == p.sampler_id && + param.global_image == p.global_image && param.global_sampler == p.global_sampler; + }); + + if (itr == end(caller.combined_parameters)) + { + uint32_t id = compiler.ir.increase_bound_by(3); + auto type_id = id + 0; + auto ptr_type_id = id + 1; + auto combined_id = id + 2; + auto &base = compiler.expression_type(image_id); + auto &type = compiler.set(type_id); + auto &ptr_type = compiler.set(ptr_type_id); + + type = base; + type.self = type_id; + type.basetype = SPIRType::SampledImage; + type.pointer = false; + type.storage = StorageClassGeneric; + type.image.depth = depth; + + ptr_type = type; + ptr_type.pointer = true; + ptr_type.storage = StorageClassUniformConstant; + ptr_type.parent_type = type_id; + + // Build new variable. + compiler.set(combined_id, ptr_type_id, StorageClassFunction, 0); + + // Inherit RelaxedPrecision. + // If any of OpSampledImage, underlying image or sampler are marked, inherit the decoration. + bool relaxed_precision = + compiler.has_decoration(sampler_id, DecorationRelaxedPrecision) || + compiler.has_decoration(image_id, DecorationRelaxedPrecision) || + (combined_module_id && compiler.has_decoration(combined_module_id, DecorationRelaxedPrecision)); + + if (relaxed_precision) + compiler.set_decoration(combined_id, DecorationRelaxedPrecision); + + param.id = combined_id; + + compiler.set_name(combined_id, + join("SPIRV_Cross_Combined", compiler.to_name(image_id), compiler.to_name(sampler_id))); + + caller.combined_parameters.push_back(param); + caller.shadow_arguments.push_back({ ptr_type_id, combined_id, 0u, 0u, true }); + } +} + +bool Compiler::DummySamplerForCombinedImageHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + if (need_dummy_sampler) + { + // No need to traverse further, we know the result. + return false; + } + + switch (opcode) + { + case OpLoad: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + bool separate_image = + type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer; + + // If not separate image, don't bother. + if (!separate_image) + return true; + + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + break; + } + + case OpImageFetch: + case OpImageQuerySizeLod: + case OpImageQuerySize: + case OpImageQueryLevels: + case OpImageQuerySamples: + { + // If we are fetching or querying LOD from a plain OpTypeImage, we must pre-combine with our dummy sampler. + auto *var = compiler.maybe_get_backing_variable(args[2]); + if (var) + { + auto &type = compiler.get(var->basetype); + if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer) + need_dummy_sampler = true; + } + + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + auto &type = compiler.get(result_type); + bool separate_image = + type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer; + if (!separate_image) + return true; + + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + + // Other backends might use SPIRAccessChain for this later. + compiler.ir.ids[id].set_allow_type_rewrite(); + break; + } + + default: + break; + } + + return true; +} + +bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + // We need to figure out where samplers and images are loaded from, so do only the bare bones compilation we need. + bool is_fetch = false; + + switch (opcode) + { + case OpLoad: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + + // If not separate image or sampler, don't bother. + if (!separate_image && !separate_sampler) + return true; + + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + return true; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + if (length < 3) + return false; + + // Technically, it is possible to have arrays of textures and arrays of samplers and combine them, but this becomes essentially + // impossible to implement, since we don't know which concrete sampler we are accessing. + // One potential way is to create a combinatorial explosion where N textures and M samplers are combined into N * M sampler2Ds, + // but this seems ridiculously complicated for a problem which is easy to work around. + // Checking access chains like this assumes we don't have samplers or textures inside uniform structs, but this makes no sense. + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + if (separate_sampler) + SPIRV_CROSS_THROW( + "Attempting to use arrays or structs of separate samplers. This is not possible to statically " + "remap to plain GLSL."); + + if (separate_image) + { + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + } + return true; + } + + case OpImageFetch: + case OpImageQuerySizeLod: + case OpImageQuerySize: + case OpImageQueryLevels: + case OpImageQuerySamples: + { + // If we are fetching from a plain OpTypeImage or querying LOD, we must pre-combine with our dummy sampler. + auto *var = compiler.maybe_get_backing_variable(args[2]); + if (!var) + return true; + + auto &type = compiler.get(var->basetype); + if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer) + { + if (compiler.dummy_sampler_id == 0) + SPIRV_CROSS_THROW("texelFetch without sampler was found, but no dummy sampler has been created with " + "build_dummy_sampler_for_combined_images()."); + + // Do it outside. + is_fetch = true; + break; + } + + return true; + } + + case OpSampledImage: + // Do it outside. + break; + + default: + return true; + } + + // Registers sampler2D calls used in case they are parameters so + // that their callees know which combined image samplers to propagate down the call stack. + if (!functions.empty()) + { + auto &callee = *functions.top(); + if (callee.do_combined_parameters) + { + uint32_t image_id = args[2]; + + auto *image = compiler.maybe_get_backing_variable(image_id); + if (image) + image_id = image->self; + + uint32_t sampler_id = is_fetch ? compiler.dummy_sampler_id : args[3]; + auto *sampler = compiler.maybe_get_backing_variable(sampler_id); + if (sampler) + sampler_id = sampler->self; + + uint32_t combined_id = args[1]; + + auto &combined_type = compiler.get(args[0]); + register_combined_image_sampler(callee, combined_id, image_id, sampler_id, combined_type.image.depth); + } + } + + // For function calls, we need to remap IDs which are function parameters into global variables. + // This information is statically known from the current place in the call stack. + // Function parameters are not necessarily pointers, so if we don't have a backing variable, remapping will know + // which backing variable the image/sample came from. + VariableID image_id = remap_parameter(args[2]); + VariableID sampler_id = is_fetch ? compiler.dummy_sampler_id : remap_parameter(args[3]); + + auto itr = find_if(begin(compiler.combined_image_samplers), end(compiler.combined_image_samplers), + [image_id, sampler_id](const CombinedImageSampler &combined) { + return combined.image_id == image_id && combined.sampler_id == sampler_id; + }); + + if (itr == end(compiler.combined_image_samplers)) + { + uint32_t sampled_type; + uint32_t combined_module_id; + if (is_fetch) + { + // Have to invent the sampled image type. + sampled_type = compiler.ir.increase_bound_by(1); + auto &type = compiler.set(sampled_type); + type = compiler.expression_type(args[2]); + type.self = sampled_type; + type.basetype = SPIRType::SampledImage; + type.image.depth = false; + combined_module_id = 0; + } + else + { + sampled_type = args[0]; + combined_module_id = args[1]; + } + + auto id = compiler.ir.increase_bound_by(2); + auto type_id = id + 0; + auto combined_id = id + 1; + + // Make a new type, pointer to OpTypeSampledImage, so we can make a variable of this type. + // We will probably have this type lying around, but it doesn't hurt to make duplicates for internal purposes. + auto &type = compiler.set(type_id); + auto &base = compiler.get(sampled_type); + type = base; + type.pointer = true; + type.storage = StorageClassUniformConstant; + type.parent_type = type_id; + + // Build new variable. + compiler.set(combined_id, type_id, StorageClassUniformConstant, 0); + + // Inherit RelaxedPrecision (and potentially other useful flags if deemed relevant). + // If any of OpSampledImage, underlying image or sampler are marked, inherit the decoration. + bool relaxed_precision = + (sampler_id && compiler.has_decoration(sampler_id, DecorationRelaxedPrecision)) || + (image_id && compiler.has_decoration(image_id, DecorationRelaxedPrecision)) || + (combined_module_id && compiler.has_decoration(combined_module_id, DecorationRelaxedPrecision)); + + if (relaxed_precision) + compiler.set_decoration(combined_id, DecorationRelaxedPrecision); + + // Propagate the array type for the original image as well. + auto *var = compiler.maybe_get_backing_variable(image_id); + if (var) + { + auto &parent_type = compiler.get(var->basetype); + type.array = parent_type.array; + type.array_size_literal = parent_type.array_size_literal; + } + + compiler.combined_image_samplers.push_back({ combined_id, image_id, sampler_id }); + } + + return true; +} + +VariableID Compiler::build_dummy_sampler_for_combined_images() +{ + DummySamplerForCombinedImageHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + if (handler.need_dummy_sampler) + { + uint32_t offset = ir.increase_bound_by(3); + auto type_id = offset + 0; + auto ptr_type_id = offset + 1; + auto var_id = offset + 2; + + SPIRType sampler_type; + auto &sampler = set(type_id); + sampler.basetype = SPIRType::Sampler; + + auto &ptr_sampler = set(ptr_type_id); + ptr_sampler = sampler; + ptr_sampler.self = type_id; + ptr_sampler.storage = StorageClassUniformConstant; + ptr_sampler.pointer = true; + ptr_sampler.parent_type = type_id; + + set(var_id, ptr_type_id, StorageClassUniformConstant, 0); + set_name(var_id, "SPIRV_Cross_DummySampler"); + dummy_sampler_id = var_id; + return var_id; + } + else + return 0; +} + +void Compiler::build_combined_image_samplers() +{ + ir.for_each_typed_id([&](uint32_t, SPIRFunction &func) { + func.combined_parameters.clear(); + func.shadow_arguments.clear(); + func.do_combined_parameters = true; + }); + + combined_image_samplers.clear(); + CombinedImageSamplerHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); +} + +SmallVector Compiler::get_specialization_constants() const +{ + SmallVector spec_consts; + ir.for_each_typed_id([&](uint32_t, const SPIRConstant &c) { + if (c.specialization && has_decoration(c.self, DecorationSpecId)) + spec_consts.push_back({ c.self, get_decoration(c.self, DecorationSpecId) }); + }); + return spec_consts; +} + +SPIRConstant &Compiler::get_constant(ConstantID id) +{ + return get(id); +} + +const SPIRConstant &Compiler::get_constant(ConstantID id) const +{ + return get(id); +} + +static bool exists_unaccessed_path_to_return(const CFG &cfg, uint32_t block, const unordered_set &blocks) +{ + // This block accesses the variable. + if (blocks.find(block) != end(blocks)) + return false; + + // We are at the end of the CFG. + if (cfg.get_succeeding_edges(block).empty()) + return true; + + // If any of our successors have a path to the end, there exists a path from block. + for (auto &succ : cfg.get_succeeding_edges(block)) + if (exists_unaccessed_path_to_return(cfg, succ, blocks)) + return true; + + return false; +} + +void Compiler::analyze_parameter_preservation( + SPIRFunction &entry, const CFG &cfg, const unordered_map> &variable_to_blocks, + const unordered_map> &complete_write_blocks) +{ + for (auto &arg : entry.arguments) + { + // Non-pointers are always inputs. + auto &type = get(arg.type); + if (!type.pointer) + continue; + + // Opaque argument types are always in + bool potential_preserve; + switch (type.basetype) + { + case SPIRType::Sampler: + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::AtomicCounter: + potential_preserve = false; + break; + + default: + potential_preserve = true; + break; + } + + if (!potential_preserve) + continue; + + auto itr = variable_to_blocks.find(arg.id); + if (itr == end(variable_to_blocks)) + { + // Variable is never accessed. + continue; + } + + // We have accessed a variable, but there was no complete writes to that variable. + // We deduce that we must preserve the argument. + itr = complete_write_blocks.find(arg.id); + if (itr == end(complete_write_blocks)) + { + arg.read_count++; + continue; + } + + // If there is a path through the CFG where no block completely writes to the variable, the variable will be in an undefined state + // when the function returns. We therefore need to implicitly preserve the variable in case there are writers in the function. + // Major case here is if a function is + // void foo(int &var) { if (cond) var = 10; } + // Using read/write counts, we will think it's just an out variable, but it really needs to be inout, + // because if we don't write anything whatever we put into the function must return back to the caller. + if (exists_unaccessed_path_to_return(cfg, entry.entry_block, itr->second)) + arg.read_count++; + } +} + +Compiler::AnalyzeVariableScopeAccessHandler::AnalyzeVariableScopeAccessHandler(Compiler &compiler_, + SPIRFunction &entry_) + : compiler(compiler_) + , entry(entry_) +{ +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::follow_function_call(const SPIRFunction &) +{ + // Only analyze within this function. + return false; +} + +void Compiler::AnalyzeVariableScopeAccessHandler::set_current_block(const SPIRBlock &block) +{ + current_block = █ + + // If we're branching to a block which uses OpPhi, in GLSL + // this will be a variable write when we branch, + // so we need to track access to these variables as well to + // have a complete picture. + const auto test_phi = [this, &block](uint32_t to) { + auto &next = compiler.get(to); + for (auto &phi : next.phi_variables) + { + if (phi.parent == block.self) + { + accessed_variables_to_block[phi.function_variable].insert(block.self); + // Phi variables are also accessed in our target branch block. + accessed_variables_to_block[phi.function_variable].insert(next.self); + + notify_variable_access(phi.local_variable, block.self); + } + } + }; + + switch (block.terminator) + { + case SPIRBlock::Direct: + notify_variable_access(block.condition, block.self); + test_phi(block.next_block); + break; + + case SPIRBlock::Select: + notify_variable_access(block.condition, block.self); + test_phi(block.true_block); + test_phi(block.false_block); + break; + + case SPIRBlock::MultiSelect: + notify_variable_access(block.condition, block.self); + for (auto &target : block.cases) + test_phi(target.block); + if (block.default_block) + test_phi(block.default_block); + break; + + default: + break; + } +} + +void Compiler::AnalyzeVariableScopeAccessHandler::notify_variable_access(uint32_t id, uint32_t block) +{ + if (id == 0) + return; + + // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. + auto itr = access_chain_children.find(id); + if (itr != end(access_chain_children)) + for (auto child_id : itr->second) + notify_variable_access(child_id, block); + + if (id_is_phi_variable(id)) + accessed_variables_to_block[id].insert(block); + else if (id_is_potential_temporary(id)) + accessed_temporaries_to_block[id].insert(block); +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_phi_variable(uint32_t id) const +{ + if (id >= compiler.get_current_id_bound()) + return false; + auto *var = compiler.maybe_get(id); + return var && var->phi_variable; +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_potential_temporary(uint32_t id) const +{ + if (id >= compiler.get_current_id_bound()) + return false; + + // Temporaries are not created before we start emitting code. + return compiler.ir.ids[id].empty() || (compiler.ir.ids[id].get_type() == TypeExpression); +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length) +{ + // Keep track of the types of temporaries, so we can hoist them out as necessary. + uint32_t result_type, result_id; + if (compiler.instruction_to_result_type(result_type, result_id, op, args, length)) + result_id_to_type[result_id] = result_type; + + switch (op) + { + case OpStore: + { + if (length < 2) + return false; + + ID ptr = args[0]; + auto *var = compiler.maybe_get_backing_variable(ptr); + + // If we store through an access chain, we have a partial write. + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + if (var->self == ptr) + complete_write_variables_to_block[var->self].insert(current_block->self); + else + partial_write_variables_to_block[var->self].insert(current_block->self); + } + + // args[0] might be an access chain we have to track use of. + notify_variable_access(args[0], current_block->self); + // Might try to store a Phi variable here. + notify_variable_access(args[1], current_block->self); + break; + } + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + { + if (length < 3) + return false; + + // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get(ptr); + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + access_chain_children[args[1]].insert(var->self); + } + + // args[2] might be another access chain we have to track use of. + for (uint32_t i = 2; i < length; i++) + { + notify_variable_access(args[i], current_block->self); + access_chain_children[args[1]].insert(args[i]); + } + + // Also keep track of the access chain pointer itself. + // In exceptionally rare cases, we can end up with a case where + // the access chain is generated in the loop body, but is consumed in continue block. + // This means we need complex loop workarounds, and we must detect this via CFG analysis. + notify_variable_access(args[1], current_block->self); + + // The result of an access chain is a fixed expression and is not really considered a temporary. + auto &e = compiler.set(args[1], "", args[0], true); + auto *backing_variable = compiler.maybe_get_backing_variable(ptr); + e.loaded_from = backing_variable ? VariableID(backing_variable->self) : VariableID(0); + + // Other backends might use SPIRAccessChain for this later. + compiler.ir.ids[args[1]].set_allow_type_rewrite(); + access_chain_expressions.insert(args[1]); + break; + } + + case OpCopyMemory: + { + if (length < 2) + return false; + + ID lhs = args[0]; + ID rhs = args[1]; + auto *var = compiler.maybe_get_backing_variable(lhs); + + // If we store through an access chain, we have a partial write. + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + if (var->self == lhs) + complete_write_variables_to_block[var->self].insert(current_block->self); + else + partial_write_variables_to_block[var->self].insert(current_block->self); + } + + // args[0:1] might be access chains we have to track use of. + for (uint32_t i = 0; i < 2; i++) + notify_variable_access(args[i], current_block->self); + + var = compiler.maybe_get_backing_variable(rhs); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + break; + } + + case OpCopyObject: + { + if (length < 3) + return false; + + auto *var = compiler.maybe_get_backing_variable(args[2]); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + + // Might be an access chain which we have to keep track of. + notify_variable_access(args[1], current_block->self); + if (access_chain_expressions.count(args[2])) + access_chain_expressions.insert(args[1]); + + // Might try to copy a Phi variable here. + notify_variable_access(args[2], current_block->self); + break; + } + + case OpLoad: + { + if (length < 3) + return false; + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + + // Loaded value is a temporary. + notify_variable_access(args[1], current_block->self); + + // Might be an access chain we have to track use of. + notify_variable_access(args[2], current_block->self); + break; + } + + case OpFunctionCall: + { + if (length < 3) + return false; + + // Return value may be a temporary. + if (compiler.get_type(args[0]).basetype != SPIRType::Void) + notify_variable_access(args[1], current_block->self); + + length -= 3; + args += 3; + + for (uint32_t i = 0; i < length; i++) + { + auto *var = compiler.maybe_get_backing_variable(args[i]); + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + // Assume we can get partial writes to this variable. + partial_write_variables_to_block[var->self].insert(current_block->self); + } + + // Cannot easily prove if argument we pass to a function is completely written. + // Usually, functions write to a dummy variable, + // which is then copied to in full to the real argument. + + // Might try to copy a Phi variable here. + notify_variable_access(args[i], current_block->self); + } + break; + } + + case OpExtInst: + { + for (uint32_t i = 4; i < length; i++) + notify_variable_access(args[i], current_block->self); + notify_variable_access(args[1], current_block->self); + break; + } + + case OpArrayLength: + case OpLine: + case OpNoLine: + // Uses literals, but cannot be a phi variable or temporary, so ignore. + break; + + // Atomics shouldn't be able to access function-local variables. + // Some GLSL builtins access a pointer. + + case OpCompositeInsert: + case OpVectorShuffle: + // Specialize for opcode which contains literals. + for (uint32_t i = 1; i < 4; i++) + notify_variable_access(args[i], current_block->self); + break; + + case OpCompositeExtract: + // Specialize for opcode which contains literals. + for (uint32_t i = 1; i < 3; i++) + notify_variable_access(args[i], current_block->self); + break; + + case OpImageWrite: + for (uint32_t i = 0; i < length; i++) + { + // Argument 3 is a literal. + if (i != 3) + notify_variable_access(args[i], current_block->self); + } + break; + + case OpImageSampleImplicitLod: + case OpImageSampleExplicitLod: + case OpImageSparseSampleImplicitLod: + case OpImageSparseSampleExplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseSampleProjExplicitLod: + case OpImageFetch: + case OpImageSparseFetch: + case OpImageRead: + case OpImageSparseRead: + for (uint32_t i = 1; i < length; i++) + { + // Argument 4 is a literal. + if (i != 4) + notify_variable_access(args[i], current_block->self); + } + break; + + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleDrefExplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + case OpImageGather: + case OpImageSparseGather: + case OpImageDrefGather: + case OpImageSparseDrefGather: + for (uint32_t i = 1; i < length; i++) + { + // Argument 5 is a literal. + if (i != 5) + notify_variable_access(args[i], current_block->self); + } + break; + + default: + { + // Rather dirty way of figuring out where Phi variables are used. + // As long as only IDs are used, we can scan through instructions and try to find any evidence that + // the ID of a variable has been used. + // There are potential false positives here where a literal is used in-place of an ID, + // but worst case, it does not affect the correctness of the compile. + // Exhaustive analysis would be better here, but it's not worth it for now. + for (uint32_t i = 0; i < length; i++) + notify_variable_access(args[i], current_block->self); + break; + } + } + return true; +} + +Compiler::StaticExpressionAccessHandler::StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_) + : compiler(compiler_) + , variable_id(variable_id_) +{ +} + +bool Compiler::StaticExpressionAccessHandler::follow_function_call(const SPIRFunction &) +{ + return false; +} + +bool Compiler::StaticExpressionAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length) +{ + switch (op) + { + case OpStore: + if (length < 2) + return false; + if (args[0] == variable_id) + { + static_expression = args[1]; + write_count++; + } + break; + + case OpLoad: + if (length < 3) + return false; + if (args[2] == variable_id && static_expression == 0) // Tried to read from variable before it was initialized. + return false; + break; + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + if (length < 3) + return false; + if (args[2] == variable_id) // If we try to access chain our candidate variable before we store to it, bail. + return false; + break; + + default: + break; + } + + return true; +} + +void Compiler::find_function_local_luts(SPIRFunction &entry, const AnalyzeVariableScopeAccessHandler &handler, + bool single_function) +{ + auto &cfg = *function_cfgs.find(entry.self)->second; + + // For each variable which is statically accessed. + for (auto &accessed_var : handler.accessed_variables_to_block) + { + auto &blocks = accessed_var.second; + auto &var = get(accessed_var.first); + auto &type = expression_type(accessed_var.first); + + // Only consider function local variables here. + // If we only have a single function in our CFG, private storage is also fine, + // since it behaves like a function local variable. + bool allow_lut = var.storage == StorageClassFunction || (single_function && var.storage == StorageClassPrivate); + if (!allow_lut) + continue; + + // We cannot be a phi variable. + if (var.phi_variable) + continue; + + // Only consider arrays here. + if (type.array.empty()) + continue; + + // If the variable has an initializer, make sure it is a constant expression. + uint32_t static_constant_expression = 0; + if (var.initializer) + { + if (ir.ids[var.initializer].get_type() != TypeConstant) + continue; + static_constant_expression = var.initializer; + + // There can be no stores to this variable, we have now proved we have a LUT. + if (handler.complete_write_variables_to_block.count(var.self) != 0 || + handler.partial_write_variables_to_block.count(var.self) != 0) + continue; + } + else + { + // We can have one, and only one write to the variable, and that write needs to be a constant. + + // No partial writes allowed. + if (handler.partial_write_variables_to_block.count(var.self) != 0) + continue; + + auto itr = handler.complete_write_variables_to_block.find(var.self); + + // No writes? + if (itr == end(handler.complete_write_variables_to_block)) + continue; + + // We write to the variable in more than one block. + auto &write_blocks = itr->second; + if (write_blocks.size() != 1) + continue; + + // The write needs to happen in the dominating block. + DominatorBuilder builder(cfg); + for (auto &block : blocks) + builder.add_block(block); + uint32_t dominator = builder.get_dominator(); + + // The complete write happened in a branch or similar, cannot deduce static expression. + if (write_blocks.count(dominator) == 0) + continue; + + // Find the static expression for this variable. + StaticExpressionAccessHandler static_expression_handler(*this, var.self); + traverse_all_reachable_opcodes(get(dominator), static_expression_handler); + + // We want one, and exactly one write + if (static_expression_handler.write_count != 1 || static_expression_handler.static_expression == 0) + continue; + + // Is it a constant expression? + if (ir.ids[static_expression_handler.static_expression].get_type() != TypeConstant) + continue; + + // We found a LUT! + static_constant_expression = static_expression_handler.static_expression; + } + + get(static_constant_expression).is_used_as_lut = true; + var.static_expression = static_constant_expression; + var.statically_assigned = true; + var.remapped_variable = true; + } +} + +void Compiler::analyze_variable_scope(SPIRFunction &entry, AnalyzeVariableScopeAccessHandler &handler) +{ + // First, we map out all variable access within a function. + // Essentially a map of block -> { variables accessed in the basic block } + traverse_all_reachable_opcodes(entry, handler); + + auto &cfg = *function_cfgs.find(entry.self)->second; + + // Analyze if there are parameters which need to be implicitly preserved with an "in" qualifier. + analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block, + handler.complete_write_variables_to_block); + + unordered_map potential_loop_variables; + + // Find the loop dominator block for each block. + for (auto &block_id : entry.blocks) + { + auto &block = get(block_id); + + auto itr = ir.continue_block_to_loop_header.find(block_id); + if (itr != end(ir.continue_block_to_loop_header) && itr->second != block_id) + { + // Continue block might be unreachable in the CFG, but we still like to know the loop dominator. + // Edge case is when continue block is also the loop header, don't set the dominator in this case. + block.loop_dominator = itr->second; + } + else + { + uint32_t loop_dominator = cfg.find_loop_dominator(block_id); + if (loop_dominator != block_id) + block.loop_dominator = loop_dominator; + else + block.loop_dominator = SPIRBlock::NoDominator; + } + } + + // For each variable which is statically accessed. + for (auto &var : handler.accessed_variables_to_block) + { + // Only deal with variables which are considered local variables in this function. + if (find(begin(entry.local_variables), end(entry.local_variables), VariableID(var.first)) == + end(entry.local_variables)) + continue; + + DominatorBuilder builder(cfg); + auto &blocks = var.second; + auto &type = expression_type(var.first); + + // Figure out which block is dominating all accesses of those variables. + for (auto &block : blocks) + { + // If we're accessing a variable inside a continue block, this variable might be a loop variable. + // We can only use loop variables with scalars, as we cannot track static expressions for vectors. + if (is_continue(block)) + { + // Potentially awkward case to check for. + // We might have a variable inside a loop, which is touched by the continue block, + // but is not actually a loop variable. + // The continue block is dominated by the inner part of the loop, which does not make sense in high-level + // language output because it will be declared before the body, + // so we will have to lift the dominator up to the relevant loop header instead. + builder.add_block(ir.continue_block_to_loop_header[block]); + + // Arrays or structs cannot be loop variables. + if (type.vecsize == 1 && type.columns == 1 && type.basetype != SPIRType::Struct && type.array.empty()) + { + // The variable is used in multiple continue blocks, this is not a loop + // candidate, signal that by setting block to -1u. + auto &potential = potential_loop_variables[var.first]; + + if (potential == 0) + potential = block; + else + potential = ~(0u); + } + } + builder.add_block(block); + } + + builder.lift_continue_block_dominator(); + + // Add it to a per-block list of variables. + BlockID dominating_block = builder.get_dominator(); + + // For variables whose dominating block is inside a loop, there is a risk that these variables + // actually need to be preserved across loop iterations. We can express this by adding + // a "read" access to the loop header. + // In the dominating block, we must see an OpStore or equivalent as the first access of an OpVariable. + // Should that fail, we look for the outermost loop header and tack on an access there. + // Phi nodes cannot have this problem. + if (dominating_block) + { + auto &variable = get(var.first); + if (!variable.phi_variable) + { + auto *block = &get(dominating_block); + bool preserve = may_read_undefined_variable_in_block(*block, var.first); + if (preserve) + { + // Find the outermost loop scope. + while (block->loop_dominator != BlockID(SPIRBlock::NoDominator)) + block = &get(block->loop_dominator); + + if (block->self != dominating_block) + { + builder.add_block(block->self); + dominating_block = builder.get_dominator(); + } + } + } + } + + // If all blocks here are dead code, this will be 0, so the variable in question + // will be completely eliminated. + if (dominating_block) + { + auto &block = get(dominating_block); + block.dominated_variables.push_back(var.first); + get(var.first).dominator = dominating_block; + } + } + + for (auto &var : handler.accessed_temporaries_to_block) + { + auto itr = handler.result_id_to_type.find(var.first); + + if (itr == end(handler.result_id_to_type)) + { + // We found a false positive ID being used, ignore. + // This should probably be an assert. + continue; + } + + // There is no point in doing domination analysis for opaque types. + auto &type = get(itr->second); + if (type_is_opaque_value(type)) + continue; + + DominatorBuilder builder(cfg); + bool force_temporary = false; + bool used_in_header_hoisted_continue_block = false; + + // Figure out which block is dominating all accesses of those temporaries. + auto &blocks = var.second; + for (auto &block : blocks) + { + builder.add_block(block); + + if (blocks.size() != 1 && is_continue(block)) + { + // The risk here is that inner loop can dominate the continue block. + // Any temporary we access in the continue block must be declared before the loop. + // This is moot for complex loops however. + auto &loop_header_block = get(ir.continue_block_to_loop_header[block]); + assert(loop_header_block.merge == SPIRBlock::MergeLoop); + builder.add_block(loop_header_block.self); + used_in_header_hoisted_continue_block = true; + } + } + + uint32_t dominating_block = builder.get_dominator(); + + if (blocks.size() != 1 && is_single_block_loop(dominating_block)) + { + // Awkward case, because the loop header is also the continue block, + // so hoisting to loop header does not help. + force_temporary = true; + } + + if (dominating_block) + { + // If we touch a variable in the dominating block, this is the expected setup. + // SPIR-V normally mandates this, but we have extra cases for temporary use inside loops. + bool first_use_is_dominator = blocks.count(dominating_block) != 0; + + if (!first_use_is_dominator || force_temporary) + { + if (handler.access_chain_expressions.count(var.first)) + { + // Exceptionally rare case. + // We cannot declare temporaries of access chains (except on MSL perhaps with pointers). + // Rather than do that, we force the indexing expressions to be declared in the right scope by + // tracking their usage to that end. There is no temporary to hoist. + // However, we still need to observe declaration order of the access chain. + + if (used_in_header_hoisted_continue_block) + { + // For this scenario, we used an access chain inside a continue block where we also registered an access to header block. + // This is a problem as we need to declare an access chain properly first with full definition. + // We cannot use temporaries for these expressions, + // so we must make sure the access chain is declared ahead of time. + // Force a complex for loop to deal with this. + // TODO: Out-of-order declaring for loops where continue blocks are emitted last might be another option. + auto &loop_header_block = get(dominating_block); + assert(loop_header_block.merge == SPIRBlock::MergeLoop); + loop_header_block.complex_continue = true; + } + } + else + { + // This should be very rare, but if we try to declare a temporary inside a loop, + // and that temporary is used outside the loop as well (spirv-opt inliner likes this) + // we should actually emit the temporary outside the loop. + hoisted_temporaries.insert(var.first); + forced_temporaries.insert(var.first); + + auto &block_temporaries = get(dominating_block).declare_temporary; + block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first); + } + } + else if (blocks.size() > 1) + { + // Keep track of the temporary as we might have to declare this temporary. + // This can happen if the loop header dominates a temporary, but we have a complex fallback loop. + // In this case, the header is actually inside the for (;;) {} block, and we have problems. + // What we need to do is hoist the temporaries outside the for (;;) {} block in case the header block + // declares the temporary. + auto &block_temporaries = get(dominating_block).potential_declare_temporary; + block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first); + } + } + } + + unordered_set seen_blocks; + + // Now, try to analyze whether or not these variables are actually loop variables. + for (auto &loop_variable : potential_loop_variables) + { + auto &var = get(loop_variable.first); + auto dominator = var.dominator; + BlockID block = loop_variable.second; + + // The variable was accessed in multiple continue blocks, ignore. + if (block == BlockID(~(0u)) || block == BlockID(0)) + continue; + + // Dead code. + if (dominator == ID(0)) + continue; + + BlockID header = 0; + + // Find the loop header for this block if we are a continue block. + { + auto itr = ir.continue_block_to_loop_header.find(block); + if (itr != end(ir.continue_block_to_loop_header)) + { + header = itr->second; + } + else if (get(block).continue_block == block) + { + // Also check for self-referential continue block. + header = block; + } + } + + assert(header); + auto &header_block = get(header); + auto &blocks = handler.accessed_variables_to_block[loop_variable.first]; + + // If a loop variable is not used before the loop, it's probably not a loop variable. + bool has_accessed_variable = blocks.count(header) != 0; + + // Now, there are two conditions we need to meet for the variable to be a loop variable. + // 1. The dominating block must have a branch-free path to the loop header, + // this way we statically know which expression should be part of the loop variable initializer. + + // Walk from the dominator, if there is one straight edge connecting + // dominator and loop header, we statically know the loop initializer. + bool static_loop_init = true; + while (dominator != header) + { + if (blocks.count(dominator) != 0) + has_accessed_variable = true; + + auto &succ = cfg.get_succeeding_edges(dominator); + if (succ.size() != 1) + { + static_loop_init = false; + break; + } + + auto &pred = cfg.get_preceding_edges(succ.front()); + if (pred.size() != 1 || pred.front() != dominator) + { + static_loop_init = false; + break; + } + + dominator = succ.front(); + } + + if (!static_loop_init || !has_accessed_variable) + continue; + + // The second condition we need to meet is that no access after the loop + // merge can occur. Walk the CFG to see if we find anything. + + seen_blocks.clear(); + cfg.walk_from(seen_blocks, header_block.merge_block, [&](uint32_t walk_block) -> bool { + // We found a block which accesses the variable outside the loop. + if (blocks.find(walk_block) != end(blocks)) + static_loop_init = false; + return true; + }); + + if (!static_loop_init) + continue; + + // We have a loop variable. + header_block.loop_variables.push_back(loop_variable.first); + // Need to sort here as variables come from an unordered container, and pushing stuff in wrong order + // will break reproducability in regression runs. + sort(begin(header_block.loop_variables), end(header_block.loop_variables)); + get(loop_variable.first).loop_variable = true; + } +} + +bool Compiler::may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var) +{ + for (auto &op : block.ops) + { + auto *ops = stream(op); + switch (op.op) + { + case OpStore: + case OpCopyMemory: + if (ops[0] == var) + return false; + break; + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + // Access chains are generally used to partially read and write. It's too hard to analyze + // if all constituents are written fully before continuing, so just assume it's preserved. + // This is the same as the parameter preservation analysis. + if (ops[2] == var) + return true; + break; + + case OpSelect: + // Variable pointers. + // We might read before writing. + if (ops[3] == var || ops[4] == var) + return true; + break; + + case OpPhi: + { + // Variable pointers. + // We might read before writing. + if (op.length < 2) + break; + + uint32_t count = op.length - 2; + for (uint32_t i = 0; i < count; i += 2) + if (ops[i + 2] == var) + return true; + break; + } + + case OpCopyObject: + case OpLoad: + if (ops[2] == var) + return true; + break; + + case OpFunctionCall: + { + if (op.length < 3) + break; + + // May read before writing. + uint32_t count = op.length - 3; + for (uint32_t i = 0; i < count; i++) + if (ops[i + 3] == var) + return true; + break; + } + + default: + break; + } + } + + // Not accessed somehow, at least not in a usual fashion. + // It's likely accessed in a branch, so assume we must preserve. + return true; +} + +Bitset Compiler::get_buffer_block_flags(VariableID id) const +{ + return ir.get_buffer_block_flags(get(id)); +} + +bool Compiler::get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type) +{ + if (type.basetype == SPIRType::Struct) + { + base_type = SPIRType::Unknown; + for (auto &member_type : type.member_types) + { + SPIRType::BaseType member_base; + if (!get_common_basic_type(get(member_type), member_base)) + return false; + + if (base_type == SPIRType::Unknown) + base_type = member_base; + else if (base_type != member_base) + return false; + } + return true; + } + else + { + base_type = type.basetype; + return true; + } +} + +void Compiler::ActiveBuiltinHandler::handle_builtin(const SPIRType &type, BuiltIn builtin, + const Bitset &decoration_flags) +{ + // If used, we will need to explicitly declare a new array size for these builtins. + + if (builtin == BuiltInClipDistance) + { + if (!type.array_size_literal[0]) + SPIRV_CROSS_THROW("Array size for ClipDistance must be a literal."); + uint32_t array_size = type.array[0]; + if (array_size == 0) + SPIRV_CROSS_THROW("Array size for ClipDistance must not be unsized."); + compiler.clip_distance_count = array_size; + } + else if (builtin == BuiltInCullDistance) + { + if (!type.array_size_literal[0]) + SPIRV_CROSS_THROW("Array size for CullDistance must be a literal."); + uint32_t array_size = type.array[0]; + if (array_size == 0) + SPIRV_CROSS_THROW("Array size for CullDistance must not be unsized."); + compiler.cull_distance_count = array_size; + } + else if (builtin == BuiltInPosition) + { + if (decoration_flags.get(DecorationInvariant)) + compiler.position_invariant = true; + } +} + +bool Compiler::ActiveBuiltinHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t length) +{ + const auto add_if_builtin = [&](uint32_t id) { + // Only handles variables here. + // Builtins which are part of a block are handled in AccessChain. + auto *var = compiler.maybe_get(id); + auto &decorations = compiler.ir.meta[id].decoration; + if (var && decorations.builtin) + { + auto &type = compiler.get(var->basetype); + auto &flags = + type.storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins; + flags.set(decorations.builtin_type); + handle_builtin(type, decorations.builtin_type, decorations.decoration_flags); + } + }; + + switch (opcode) + { + case OpStore: + if (length < 1) + return false; + + add_if_builtin(args[0]); + break; + + case OpCopyMemory: + if (length < 2) + return false; + + add_if_builtin(args[0]); + add_if_builtin(args[1]); + break; + + case OpCopyObject: + case OpLoad: + if (length < 3) + return false; + + add_if_builtin(args[2]); + break; + + case OpSelect: + if (length < 5) + return false; + + add_if_builtin(args[3]); + add_if_builtin(args[4]); + break; + + case OpPhi: + { + if (length < 2) + return false; + + uint32_t count = length - 2; + args += 2; + for (uint32_t i = 0; i < count; i += 2) + add_if_builtin(args[i]); + break; + } + + case OpFunctionCall: + { + if (length < 3) + return false; + + uint32_t count = length - 3; + args += 3; + for (uint32_t i = 0; i < count; i++) + add_if_builtin(args[i]); + break; + } + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + { + if (length < 4) + return false; + + // Only consider global variables, cannot consider variables in functions yet, or other + // access chains as they have not been created yet. + auto *var = compiler.maybe_get(args[2]); + if (!var) + break; + + // Required if we access chain into builtins like gl_GlobalInvocationID. + add_if_builtin(args[2]); + + // Start traversing type hierarchy at the proper non-pointer types. + auto *type = &compiler.get_variable_data_type(*var); + + auto &flags = + var->storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins; + + uint32_t count = length - 3; + args += 3; + for (uint32_t i = 0; i < count; i++) + { + // Pointers + if (opcode == OpPtrAccessChain && i == 0) + { + type = &compiler.get(type->parent_type); + continue; + } + + // Arrays + if (!type->array.empty()) + { + type = &compiler.get(type->parent_type); + } + // Structs + else if (type->basetype == SPIRType::Struct) + { + uint32_t index = compiler.get(args[i]).scalar(); + + if (index < uint32_t(compiler.ir.meta[type->self].members.size())) + { + auto &decorations = compiler.ir.meta[type->self].members[index]; + if (decorations.builtin) + { + flags.set(decorations.builtin_type); + handle_builtin(compiler.get(type->member_types[index]), decorations.builtin_type, + decorations.decoration_flags); + } + } + + type = &compiler.get(type->member_types[index]); + } + else + { + // No point in traversing further. We won't find any extra builtins. + break; + } + } + break; + } + + default: + break; + } + + return true; +} + +void Compiler::update_active_builtins() +{ + active_input_builtins.reset(); + active_output_builtins.reset(); + cull_distance_count = 0; + clip_distance_count = 0; + ActiveBuiltinHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); +} + +// Returns whether this shader uses a builtin of the storage class +bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage) +{ + const Bitset *flags; + switch (storage) + { + case StorageClassInput: + flags = &active_input_builtins; + break; + case StorageClassOutput: + flags = &active_output_builtins; + break; + + default: + return false; + } + return flags->get(builtin); +} + +void Compiler::analyze_image_and_sampler_usage() +{ + CombinedImageSamplerDrefHandler dref_handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), dref_handler); + + CombinedImageSamplerUsageHandler handler(*this, dref_handler.dref_combined_samplers); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + // Need to run this traversal twice. First time, we propagate any comparison sampler usage from leaf functions + // down to main(). + // In the second pass, we can propagate up forced depth state coming from main() up into leaf functions. + handler.dependency_hierarchy.clear(); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + comparison_ids = move(handler.comparison_ids); + need_subpass_input = handler.need_subpass_input; + + // Forward information from separate images and samplers into combined image samplers. + for (auto &combined : combined_image_samplers) + if (comparison_ids.count(combined.sampler_id)) + comparison_ids.insert(combined.combined_id); +} + +bool Compiler::CombinedImageSamplerDrefHandler::handle(spv::Op opcode, const uint32_t *args, uint32_t) +{ + // Mark all sampled images which are used with Dref. + switch (opcode) + { + case OpImageSampleDrefExplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + case OpImageSparseSampleDrefExplicitLod: + case OpImageDrefGather: + case OpImageSparseDrefGather: + dref_combined_samplers.insert(args[2]); + return true; + + default: + break; + } + + return true; +} + +const CFG &Compiler::get_cfg_for_current_function() const +{ + assert(current_function); + return get_cfg_for_function(current_function->self); +} + +const CFG &Compiler::get_cfg_for_function(uint32_t id) const +{ + auto cfg_itr = function_cfgs.find(id); + assert(cfg_itr != end(function_cfgs)); + assert(cfg_itr->second); + return *cfg_itr->second; +} + +void Compiler::build_function_control_flow_graphs_and_analyze() +{ + CFGBuilder handler(*this); + handler.function_cfgs[ir.default_entry_point].reset(new CFG(*this, get(ir.default_entry_point))); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + function_cfgs = move(handler.function_cfgs); + bool single_function = function_cfgs.size() <= 1; + + for (auto &f : function_cfgs) + { + auto &func = get(f.first); + AnalyzeVariableScopeAccessHandler scope_handler(*this, func); + analyze_variable_scope(func, scope_handler); + find_function_local_luts(func, scope_handler, single_function); + + // Check if we can actually use the loop variables we found in analyze_variable_scope. + // To use multiple initializers, we need the same type and qualifiers. + for (auto block : func.blocks) + { + auto &b = get(block); + if (b.loop_variables.size() < 2) + continue; + + auto &flags = get_decoration_bitset(b.loop_variables.front()); + uint32_t type = get(b.loop_variables.front()).basetype; + bool invalid_initializers = false; + for (auto loop_variable : b.loop_variables) + { + if (flags != get_decoration_bitset(loop_variable) || + type != get(b.loop_variables.front()).basetype) + { + invalid_initializers = true; + break; + } + } + + if (invalid_initializers) + { + for (auto loop_variable : b.loop_variables) + get(loop_variable).loop_variable = false; + b.loop_variables.clear(); + } + } + } +} + +Compiler::CFGBuilder::CFGBuilder(Compiler &compiler_) + : compiler(compiler_) +{ +} + +bool Compiler::CFGBuilder::handle(spv::Op, const uint32_t *, uint32_t) +{ + return true; +} + +bool Compiler::CFGBuilder::follow_function_call(const SPIRFunction &func) +{ + if (function_cfgs.find(func.self) == end(function_cfgs)) + { + function_cfgs[func.self].reset(new CFG(compiler, func)); + return true; + } + else + return false; +} + +void Compiler::CombinedImageSamplerUsageHandler::add_dependency(uint32_t dst, uint32_t src) +{ + dependency_hierarchy[dst].insert(src); + // Propagate up any comparison state if we're loading from one such variable. + if (comparison_ids.count(src)) + comparison_ids.insert(dst); +} + +bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + + auto &func = compiler.get(args[2]); + const auto *arg = &args[3]; + length -= 3; + + for (uint32_t i = 0; i < length; i++) + { + auto &argument = func.arguments[i]; + add_dependency(argument.id, arg[i]); + } + + return true; +} + +void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_ids(uint32_t id) +{ + // Traverse the variable dependency hierarchy and tag everything in its path with comparison ids. + comparison_ids.insert(id); + + for (auto &dep_id : dependency_hierarchy[id]) + add_hierarchy_to_comparison_ids(dep_id); +} + +bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + switch (opcode) + { + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + case OpLoad: + { + if (length < 3) + return false; + + add_dependency(args[1], args[2]); + + // Ideally defer this to OpImageRead, but then we'd need to track loaded IDs. + // If we load an image, we're going to use it and there is little harm in declaring an unused gl_FragCoord. + auto &type = compiler.get(args[0]); + if (type.image.dim == DimSubpassData) + need_subpass_input = true; + + // If we load a SampledImage and it will be used with Dref, propagate the state up. + if (dref_combined_samplers.count(args[1]) != 0) + add_hierarchy_to_comparison_ids(args[1]); + break; + } + + case OpSampledImage: + { + if (length < 4) + return false; + + uint32_t result_type = args[0]; + uint32_t result_id = args[1]; + auto &type = compiler.get(result_type); + + // If the underlying resource has been used for comparison then duplicate loads of that resource must be too. + // This image must be a depth image. + uint32_t image = args[2]; + uint32_t sampler = args[3]; + + if (type.image.depth || dref_combined_samplers.count(result_id) != 0) + { + add_hierarchy_to_comparison_ids(image); + + // This sampler must be a SamplerComparisonState, and not a regular SamplerState. + add_hierarchy_to_comparison_ids(sampler); + + // Mark the OpSampledImage itself as being comparison state. + comparison_ids.insert(result_id); + } + return true; + } + + default: + break; + } + + return true; +} + +bool Compiler::buffer_is_hlsl_counter_buffer(VariableID id) const +{ + auto *m = ir.find_meta(id); + return m && m->hlsl_is_magic_counter_buffer; +} + +bool Compiler::buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const +{ + auto *m = ir.find_meta(id); + + // First, check for the proper decoration. + if (m && m->hlsl_magic_counter_buffer != 0) + { + counter_id = m->hlsl_magic_counter_buffer; + return true; + } + else + return false; +} + +void Compiler::make_constant_null(uint32_t id, uint32_t type) +{ + auto &constant_type = get(type); + + if (constant_type.pointer) + { + auto &constant = set(id, type); + constant.make_null(constant_type); + } + else if (!constant_type.array.empty()) + { + assert(constant_type.parent_type); + uint32_t parent_id = ir.increase_bound_by(1); + make_constant_null(parent_id, constant_type.parent_type); + + if (!constant_type.array_size_literal.back()) + SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal."); + + SmallVector elements(constant_type.array.back()); + for (uint32_t i = 0; i < constant_type.array.back(); i++) + elements[i] = parent_id; + set(id, type, elements.data(), uint32_t(elements.size()), false); + } + else if (!constant_type.member_types.empty()) + { + uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size())); + SmallVector elements(constant_type.member_types.size()); + for (uint32_t i = 0; i < constant_type.member_types.size(); i++) + { + make_constant_null(member_ids + i, constant_type.member_types[i]); + elements[i] = member_ids + i; + } + set(id, type, elements.data(), uint32_t(elements.size()), false); + } + else + { + auto &constant = set(id, type); + constant.make_null(constant_type); + } +} + +const SmallVector &Compiler::get_declared_capabilities() const +{ + return ir.declared_capabilities; +} + +const SmallVector &Compiler::get_declared_extensions() const +{ + return ir.declared_extensions; +} + +std::string Compiler::get_remapped_declared_block_name(VariableID id) const +{ + return get_remapped_declared_block_name(id, false); +} + +std::string Compiler::get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const +{ + auto itr = declared_block_names.find(id); + if (itr != end(declared_block_names)) + { + return itr->second; + } + else + { + auto &var = get(id); + + if (fallback_prefer_instance_name) + { + return to_name(var.self); + } + else + { + auto &type = get(var.basetype); + auto *type_meta = ir.find_meta(type.self); + auto *block_name = type_meta ? &type_meta->decoration.alias : nullptr; + return (!block_name || block_name->empty()) ? get_block_fallback_name(id) : *block_name; + } + } +} + +bool Compiler::reflection_ssbo_instance_name_is_significant() const +{ + if (ir.source.known) + { + // UAVs from HLSL source tend to be declared in a way where the type is reused + // but the instance name is significant, and that's the name we should report. + // For GLSL, SSBOs each have their own block type as that's how GLSL is written. + return ir.source.hlsl; + } + + unordered_set ssbo_type_ids; + bool aliased_ssbo_types = false; + + // If we don't have any OpSource information, we need to perform some shaky heuristics. + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + auto &type = this->get(var.basetype); + if (!type.pointer || var.storage == StorageClassFunction) + return; + + bool ssbo = var.storage == StorageClassStorageBuffer || + (var.storage == StorageClassUniform && has_decoration(type.self, DecorationBufferBlock)); + + if (ssbo) + { + if (ssbo_type_ids.count(type.self)) + aliased_ssbo_types = true; + else + ssbo_type_ids.insert(type.self); + } + }); + + // If the block name is aliased, assume we have HLSL-style UAV declarations. + return aliased_ssbo_types; +} + +bool Compiler::instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args, + uint32_t length) +{ + // Most instructions follow the pattern of . + // There are some exceptions. + switch (op) + { + case OpStore: + case OpCopyMemory: + case OpCopyMemorySized: + case OpImageWrite: + case OpAtomicStore: + case OpAtomicFlagClear: + case OpEmitStreamVertex: + case OpEndStreamPrimitive: + case OpControlBarrier: + case OpMemoryBarrier: + case OpGroupWaitEvents: + case OpRetainEvent: + case OpReleaseEvent: + case OpSetUserEventStatus: + case OpCaptureEventProfilingInfo: + case OpCommitReadPipe: + case OpCommitWritePipe: + case OpGroupCommitReadPipe: + case OpGroupCommitWritePipe: + case OpLine: + case OpNoLine: + return false; + + default: + if (length > 1 && maybe_get(args[0]) != nullptr) + { + result_type = args[0]; + result_id = args[1]; + return true; + } + else + return false; + } +} + +Bitset Compiler::combined_decoration_for_member(const SPIRType &type, uint32_t index) const +{ + Bitset flags; + auto *type_meta = ir.find_meta(type.self); + + if (type_meta) + { + auto &members = type_meta->members; + if (index >= members.size()) + return flags; + auto &dec = members[index]; + + flags.merge_or(dec.decoration_flags); + + auto &member_type = get(type.member_types[index]); + + // If our member type is a struct, traverse all the child members as well recursively. + auto &member_childs = member_type.member_types; + for (uint32_t i = 0; i < member_childs.size(); i++) + { + auto &child_member_type = get(member_childs[i]); + if (!child_member_type.pointer) + flags.merge_or(combined_decoration_for_member(member_type, i)); + } + } + + return flags; +} + +bool Compiler::is_desktop_only_format(spv::ImageFormat format) +{ + switch (format) + { + // Desktop-only formats + case ImageFormatR11fG11fB10f: + case ImageFormatR16f: + case ImageFormatRgb10A2: + case ImageFormatR8: + case ImageFormatRg8: + case ImageFormatR16: + case ImageFormatRg16: + case ImageFormatRgba16: + case ImageFormatR16Snorm: + case ImageFormatRg16Snorm: + case ImageFormatRgba16Snorm: + case ImageFormatR8Snorm: + case ImageFormatRg8Snorm: + case ImageFormatR8ui: + case ImageFormatRg8ui: + case ImageFormatR16ui: + case ImageFormatRgb10a2ui: + case ImageFormatR8i: + case ImageFormatRg8i: + case ImageFormatR16i: + return true; + default: + break; + } + + return false; +} + +bool Compiler::image_is_comparison(const SPIRType &type, uint32_t id) const +{ + return type.image.depth || (comparison_ids.count(id) != 0); +} + +bool Compiler::type_is_opaque_value(const SPIRType &type) const +{ + return !type.pointer && (type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::Image || + type.basetype == SPIRType::Sampler); +} + +// Make these member functions so we can easily break on any force_recompile events. +void Compiler::force_recompile() +{ + is_force_recompile = true; +} + +bool Compiler::is_forcing_recompilation() const +{ + return is_force_recompile; +} + +void Compiler::clear_force_recompile() +{ + is_force_recompile = false; +} + +Compiler::PhysicalStorageBufferPointerHandler::PhysicalStorageBufferPointerHandler(Compiler &compiler_) + : compiler(compiler_) +{ +} + +bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t) +{ + if (op == OpConvertUToPtr || op == OpBitcast) + { + auto &type = compiler.get(args[0]); + if (type.storage == StorageClassPhysicalStorageBufferEXT && type.pointer && type.pointer_depth == 1) + { + // If we need to cast to a pointer type which is not a block, we might need to synthesize ourselves + // a block type which wraps this POD type. + if (type.basetype != SPIRType::Struct) + types.insert(args[0]); + } + } + + return true; +} + +void Compiler::analyze_non_block_pointer_types() +{ + PhysicalStorageBufferPointerHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + physical_storage_non_block_pointer_types.reserve(handler.types.size()); + for (auto type : handler.types) + physical_storage_non_block_pointer_types.push_back(type); + sort(begin(physical_storage_non_block_pointer_types), end(physical_storage_non_block_pointer_types)); +} + +bool Compiler::InterlockedResourceAccessPrepassHandler::handle(Op op, const uint32_t *, uint32_t) +{ + if (op == OpBeginInvocationInterlockEXT || op == OpEndInvocationInterlockEXT) + { + if (interlock_function_id != 0 && interlock_function_id != call_stack.back()) + { + // Most complex case, we have no sensible way of dealing with this + // other than taking the 100% conservative approach, exit early. + split_function_case = true; + return false; + } + else + { + interlock_function_id = call_stack.back(); + // If this call is performed inside control flow we have a problem. + auto &cfg = compiler.get_cfg_for_function(interlock_function_id); + + uint32_t from_block_id = compiler.get(interlock_function_id).entry_block; + bool outside_control_flow = cfg.node_terminates_control_flow_in_sub_graph(from_block_id, current_block_id); + if (!outside_control_flow) + control_flow_interlock = true; + } + } + return true; +} + +void Compiler::InterlockedResourceAccessPrepassHandler::rearm_current_block(const SPIRBlock &block) +{ + current_block_id = block.self; +} + +bool Compiler::InterlockedResourceAccessPrepassHandler::begin_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + call_stack.push_back(args[2]); + return true; +} + +bool Compiler::InterlockedResourceAccessPrepassHandler::end_function_scope(const uint32_t *, uint32_t) +{ + call_stack.pop_back(); + return true; +} + +bool Compiler::InterlockedResourceAccessHandler::begin_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + + if (args[2] == interlock_function_id) + call_stack_is_interlocked = true; + + call_stack.push_back(args[2]); + return true; +} + +bool Compiler::InterlockedResourceAccessHandler::end_function_scope(const uint32_t *, uint32_t) +{ + if (call_stack.back() == interlock_function_id) + call_stack_is_interlocked = false; + + call_stack.pop_back(); + return true; +} + +void Compiler::InterlockedResourceAccessHandler::access_potential_resource(uint32_t id) +{ + if ((use_critical_section && in_crit_sec) || (control_flow_interlock && call_stack_is_interlocked) || + split_function_case) + { + compiler.interlocked_resources.insert(id); + } +} + +bool Compiler::InterlockedResourceAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + // Only care about critical section analysis if we have simple case. + if (use_critical_section) + { + if (opcode == OpBeginInvocationInterlockEXT) + { + in_crit_sec = true; + return true; + } + + if (opcode == OpEndInvocationInterlockEXT) + { + // End critical section--nothing more to do. + return false; + } + } + + // We need to figure out where images and buffers are loaded from, so do only the bare bones compilation we need. + switch (opcode) + { + case OpLoad: + { + if (length < 3) + return false; + + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + + // We're only concerned with buffer and image memory here. + if (!var) + break; + + switch (var->storage) + { + default: + break; + + case StorageClassUniformConstant: + { + uint32_t result_type = args[0]; + uint32_t id = args[1]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + break; + } + + case StorageClassUniform: + // Must have BufferBlock; we only care about SSBOs. + if (!compiler.has_decoration(compiler.get(var->basetype).self, DecorationBufferBlock)) + break; + // fallthrough + case StorageClassStorageBuffer: + access_potential_resource(var->self); + break; + } + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + if (type.storage == StorageClassUniform || type.storage == StorageClassUniformConstant || + type.storage == StorageClassStorageBuffer) + { + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + compiler.ir.ids[id].set_allow_type_rewrite(); + } + break; + } + + case OpImageTexelPointer: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + uint32_t id = args[1]; + uint32_t ptr = args[2]; + auto &e = compiler.set(id, "", result_type, true); + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var) + e.loaded_from = var->self; + break; + } + + case OpStore: + case OpImageWrite: + case OpAtomicStore: + { + if (length < 1) + return false; + + uint32_t ptr = args[0]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var && (var->storage == StorageClassUniform || var->storage == StorageClassUniformConstant || + var->storage == StorageClassStorageBuffer)) + { + access_potential_resource(var->self); + } + + break; + } + + case OpCopyMemory: + { + if (length < 2) + return false; + + uint32_t dst = args[0]; + uint32_t src = args[1]; + auto *dst_var = compiler.maybe_get_backing_variable(dst); + auto *src_var = compiler.maybe_get_backing_variable(src); + + if (dst_var && (dst_var->storage == StorageClassUniform || dst_var->storage == StorageClassStorageBuffer)) + access_potential_resource(dst_var->self); + + if (src_var) + { + if (src_var->storage != StorageClassUniform && src_var->storage != StorageClassStorageBuffer) + break; + + if (src_var->storage == StorageClassUniform && + !compiler.has_decoration(compiler.get(src_var->basetype).self, DecorationBufferBlock)) + { + break; + } + + access_potential_resource(src_var->self); + } + + break; + } + + case OpImageRead: + case OpAtomicLoad: + { + if (length < 3) + return false; + + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + + // We're only concerned with buffer and image memory here. + if (!var) + break; + + switch (var->storage) + { + default: + break; + + case StorageClassUniform: + // Must have BufferBlock; we only care about SSBOs. + if (!compiler.has_decoration(compiler.get(var->basetype).self, DecorationBufferBlock)) + break; + // fallthrough + case StorageClassUniformConstant: + case StorageClassStorageBuffer: + access_potential_resource(var->self); + break; + } + break; + } + + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + { + if (length < 3) + return false; + + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var && (var->storage == StorageClassUniform || var->storage == StorageClassUniformConstant || + var->storage == StorageClassStorageBuffer)) + { + access_potential_resource(var->self); + } + + break; + } + + default: + break; + } + + return true; +} + +void Compiler::analyze_interlocked_resource_usage() +{ + if (get_execution_model() == ExecutionModelFragment && + (get_entry_point().flags.get(ExecutionModePixelInterlockOrderedEXT) || + get_entry_point().flags.get(ExecutionModePixelInterlockUnorderedEXT) || + get_entry_point().flags.get(ExecutionModeSampleInterlockOrderedEXT) || + get_entry_point().flags.get(ExecutionModeSampleInterlockUnorderedEXT))) + { + InterlockedResourceAccessPrepassHandler prepass_handler(*this, ir.default_entry_point); + traverse_all_reachable_opcodes(get(ir.default_entry_point), prepass_handler); + + InterlockedResourceAccessHandler handler(*this, ir.default_entry_point); + handler.interlock_function_id = prepass_handler.interlock_function_id; + handler.split_function_case = prepass_handler.split_function_case; + handler.control_flow_interlock = prepass_handler.control_flow_interlock; + handler.use_critical_section = !handler.split_function_case && !handler.control_flow_interlock; + + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + // For GLSL. If we hit any of these cases, we have to fall back to conservative approach. + interlocked_is_complex = + !handler.use_critical_section || handler.interlock_function_id != ir.default_entry_point; + } +} + +bool Compiler::type_is_array_of_pointers(const SPIRType &type) const +{ + if (!type.pointer) + return false; + + // If parent type has same pointer depth, we must have an array of pointers. + return type.pointer_depth == get(type.parent_type).pointer_depth; +} + +bool Compiler::type_is_top_level_physical_pointer(const SPIRType &type) const +{ + return type.pointer && type.storage == StorageClassPhysicalStorageBuffer && + type.pointer_depth > get(type.parent_type).pointer_depth; +} + +bool Compiler::flush_phi_required(BlockID from, BlockID to) const +{ + auto &child = get(to); + for (auto &phi : child.phi_variables) + if (phi.parent == from) + return true; + return false; +} + +void Compiler::add_loop_level() +{ + current_loop_level++; +} diff --git a/third_party/spirv-cross/spirv_cross.hpp b/third_party/spirv-cross/spirv_cross.hpp new file mode 100644 index 0000000..f20ed42 --- /dev/null +++ b/third_party/spirv-cross/spirv_cross.hpp @@ -0,0 +1,1075 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_HPP +#define SPIRV_CROSS_HPP + +#include "spirv.hpp" +#include "spirv_cfg.hpp" +#include "spirv_cross_parsed_ir.hpp" + +namespace SPIRV_CROSS_NAMESPACE +{ +struct Resource +{ + // Resources are identified with their SPIR-V ID. + // This is the ID of the OpVariable. + ID id; + + // The type ID of the variable which includes arrays and all type modifications. + // This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general + // since these modifications typically happen on the base_type_id. + TypeID type_id; + + // The base type of the declared resource. + // This type is the base type which ignores pointers and arrays of the type_id. + // This is mostly useful to parse decorations of the underlying type. + // base_type_id can also be obtained with get_type(get_type(type_id).self). + TypeID base_type_id; + + // The declared name (OpName) of the resource. + // For Buffer blocks, the name actually reflects the externally + // visible Block name. + // + // This name can be retrieved again by using either + // get_name(id) or get_name(base_type_id) depending if it's a buffer block or not. + // + // This name can be an empty string in which case get_fallback_name(id) can be + // used which obtains a suitable fallback identifier for an ID. + std::string name; +}; + +struct ShaderResources +{ + SmallVector uniform_buffers; + SmallVector storage_buffers; + SmallVector stage_inputs; + SmallVector stage_outputs; + SmallVector subpass_inputs; + SmallVector storage_images; + SmallVector sampled_images; + SmallVector atomic_counters; + SmallVector acceleration_structures; + + // There can only be one push constant block, + // but keep the vector in case this restriction is lifted in the future. + SmallVector push_constant_buffers; + + // For Vulkan GLSL and HLSL source, + // these correspond to separate texture2D and samplers respectively. + SmallVector separate_images; + SmallVector separate_samplers; +}; + +struct CombinedImageSampler +{ + // The ID of the sampler2D variable. + VariableID combined_id; + // The ID of the texture2D variable. + VariableID image_id; + // The ID of the sampler variable. + VariableID sampler_id; +}; + +struct SpecializationConstant +{ + // The ID of the specialization constant. + ConstantID id; + // The constant ID of the constant, used in Vulkan during pipeline creation. + uint32_t constant_id; +}; + +struct BufferRange +{ + unsigned index; + size_t offset; + size_t range; +}; + +enum BufferPackingStandard +{ + BufferPackingStd140, + BufferPackingStd430, + BufferPackingStd140EnhancedLayout, + BufferPackingStd430EnhancedLayout, + BufferPackingHLSLCbuffer, + BufferPackingHLSLCbufferPackOffset, + BufferPackingScalar, + BufferPackingScalarEnhancedLayout +}; + +struct EntryPoint +{ + std::string name; + spv::ExecutionModel execution_model; +}; + +class Compiler +{ +public: + friend class CFG; + friend class DominatorBuilder; + + // The constructor takes a buffer of SPIR-V words and parses it. + // It will create its own parser, parse the SPIR-V and move the parsed IR + // as if you had called the constructors taking ParsedIR directly. + explicit Compiler(std::vector ir); + Compiler(const uint32_t *ir, size_t word_count); + + // This is more modular. We can also consume a ParsedIR structure directly, either as a move, or copy. + // With copy, we can reuse the same parsed IR for multiple Compiler instances. + explicit Compiler(const ParsedIR &ir); + explicit Compiler(ParsedIR &&ir); + + virtual ~Compiler() = default; + + // After parsing, API users can modify the SPIR-V via reflection and call this + // to disassemble the SPIR-V into the desired langauage. + // Sub-classes actually implement this. + virtual std::string compile(); + + // Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned. + const std::string &get_name(ID id) const; + + // Applies a decoration to an ID. Effectively injects OpDecorate. + void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0); + void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument); + + // Overrides the identifier OpName of an ID. + // Identifiers beginning with underscores or identifiers which contain double underscores + // are reserved by the implementation. + void set_name(ID id, const std::string &name); + + // Gets a bitmask for the decorations which are applied to ID. + // I.e. (1ull << spv::DecorationFoo) | (1ull << spv::DecorationBar) + const Bitset &get_decoration_bitset(ID id) const; + + // Returns whether the decoration has been applied to the ID. + bool has_decoration(ID id, spv::Decoration decoration) const; + + // Gets the value for decorations which take arguments. + // If the decoration is a boolean (i.e. spv::DecorationNonWritable), + // 1 will be returned. + // If decoration doesn't exist or decoration is not recognized, + // 0 will be returned. + uint32_t get_decoration(ID id, spv::Decoration decoration) const; + const std::string &get_decoration_string(ID id, spv::Decoration decoration) const; + + // Removes the decoration for an ID. + void unset_decoration(ID id, spv::Decoration decoration); + + // Gets the SPIR-V type associated with ID. + // Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource. + const SPIRType &get_type(TypeID id) const; + + // Gets the SPIR-V type of a variable. + const SPIRType &get_type_from_variable(VariableID id) const; + + // Gets the underlying storage class for an OpVariable. + spv::StorageClass get_storage_class(VariableID id) const; + + // If get_name() is an empty string, get the fallback name which will be used + // instead in the disassembled source. + virtual const std::string get_fallback_name(ID id) const; + + // If get_name() of a Block struct is an empty string, get the fallback name. + // This needs to be per-variable as multiple variables can use the same block type. + virtual const std::string get_block_fallback_name(VariableID id) const; + + // Given an OpTypeStruct in ID, obtain the identifier for member number "index". + // This may be an empty string. + const std::string &get_member_name(TypeID id, uint32_t index) const; + + // Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index". + uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; + const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const; + + // Sets the member identifier for OpTypeStruct ID, member number "index". + void set_member_name(TypeID id, uint32_t index, const std::string &name); + + // Returns the qualified member identifier for OpTypeStruct ID, member number "index", + // or an empty string if no qualified alias exists + const std::string &get_member_qualified_name(TypeID type_id, uint32_t index) const; + + // Gets the decoration mask for a member of a struct, similar to get_decoration_mask. + const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; + + // Returns whether the decoration has been applied to a member of a struct. + bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; + + // Similar to set_decoration, but for struct members. + void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0); + void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration, + const std::string &argument); + + // Unsets a member decoration, similar to unset_decoration. + void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration); + + // Gets the fallback name for a member, similar to get_fallback_name. + virtual const std::string get_fallback_member_name(uint32_t index) const + { + return join("_", index); + } + + // Returns a vector of which members of a struct are potentially in use by a + // SPIR-V shader. The granularity of this analysis is per-member of a struct. + // This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks. + // ID is the Resource::id obtained from get_shader_resources(). + SmallVector get_active_buffer_ranges(VariableID id) const; + + // Returns the effective size of a buffer block. + size_t get_declared_struct_size(const SPIRType &struct_type) const; + + // Returns the effective size of a buffer block, with a given array size + // for a runtime array. + // SSBOs are typically declared as runtime arrays. get_declared_struct_size() will return 0 for the size. + // This is not very helpful for applications which might need to know the array stride of its last member. + // This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function + // to query the size of the buffer, assuming that the last member has a certain size. + // If the buffer does not contain a runtime array, array_size is ignored, and the function will behave as + // get_declared_struct_size(). + // To get the array stride of the last member, something like: + // get_declared_struct_size_runtime_array(type, 1) - get_declared_struct_size_runtime_array(type, 0) will work. + size_t get_declared_struct_size_runtime_array(const SPIRType &struct_type, size_t array_size) const; + + // Returns the effective size of a buffer block struct member. + size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const; + + // Returns a set of all global variables which are statically accessed + // by the control flow graph from the current entry point. + // Only variables which change the interface for a shader are returned, that is, + // variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter + // storage classes are returned. + // + // To use the returned set as the filter for which variables are used during compilation, + // this set can be moved to set_enabled_interface_variables(). + std::unordered_set get_active_interface_variables() const; + + // Sets the interface variables which are used during compilation. + // By default, all variables are used. + // Once set, compile() will only consider the set in active_variables. + void set_enabled_interface_variables(std::unordered_set active_variables); + + // Query shader resources, use ids with reflection interface to modify or query binding points, etc. + ShaderResources get_shader_resources() const; + + // Query shader resources, but only return the variables which are part of active_variables. + // E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically + // accessed. + ShaderResources get_shader_resources(const std::unordered_set &active_variables) const; + + // Remapped variables are considered built-in variables and a backend will + // not emit a declaration for this variable. + // This is mostly useful for making use of builtins which are dependent on extensions. + void set_remapped_variable_state(VariableID id, bool remap_enable); + bool get_remapped_variable_state(VariableID id) const; + + // For subpassInput variables which are remapped to plain variables, + // the number of components in the remapped + // variable must be specified as the backing type of subpass inputs are opaque. + void set_subpass_input_remapped_components(VariableID id, uint32_t components); + uint32_t get_subpass_input_remapped_components(VariableID id) const; + + // All operations work on the current entry point. + // Entry points can be swapped out with set_entry_point(). + // Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point. + // Resource reflection also depends on the entry point. + // By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module. + + // Some shader languages restrict the names that can be given to entry points, and the + // corresponding backend will automatically rename an entry point name, during the call + // to compile() if it is illegal. For example, the common entry point name main() is + // illegal in MSL, and is renamed to an alternate name by the MSL backend. + // Given the original entry point name contained in the SPIR-V, this function returns + // the name, as updated by the backend during the call to compile(). If the name is not + // illegal, and has not been renamed, or if this function is called before compile(), + // this function will simply return the same name. + + // New variants of entry point query and reflection. + // Names for entry points in the SPIR-V module may alias if they belong to different execution models. + // To disambiguate, we must pass along with the entry point names the execution model. + SmallVector get_entry_points_and_stages() const; + void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model); + + // Renames an entry point from old_name to new_name. + // If old_name is currently selected as the current entry point, it will continue to be the current entry point, + // albeit with a new name. + // get_entry_points() is essentially invalidated at this point. + void rename_entry_point(const std::string &old_name, const std::string &new_name, + spv::ExecutionModel execution_model); + const SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model) const; + SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model); + const std::string &get_cleansed_entry_point_name(const std::string &name, + spv::ExecutionModel execution_model) const; + + // Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader. + void update_active_builtins(); + bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage); + + // Query and modify OpExecutionMode. + const Bitset &get_execution_mode_bitset() const; + + void unset_execution_mode(spv::ExecutionMode mode); + void set_execution_mode(spv::ExecutionMode mode, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0); + + // Gets argument for an execution mode (LocalSize, Invocations, OutputVertices). + // For LocalSize, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2). + // For execution modes which do not have arguments, 0 is returned. + uint32_t get_execution_mode_argument(spv::ExecutionMode mode, uint32_t index = 0) const; + spv::ExecutionModel get_execution_model() const; + + bool is_tessellation_shader() const; + + // In SPIR-V, the compute work group size can be represented by a constant vector, in which case + // the LocalSize execution mode is ignored. + // + // This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector. + // To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified + // directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified. + // + // To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly. + // Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation. + // NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize, + // while in this interface, LocalSize is only ignored for specialization constants. + // + // The specialization constant will be written to x, y and z arguments. + // If the component is not a specialization constant, a zeroed out struct will be written. + // The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful + // for most use cases. + uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y, + SpecializationConstant &z) const; + + // Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where + // said instruction is used without a combined image sampler. + // GLSL targets do not support the use of texelFetch without a sampler. + // To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of + // texelFetch as necessary. + // + // This must be called before build_combined_image_samplers(). + // build_combined_image_samplers() may refer to the ID returned by this method if the returned ID is non-zero. + // The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object + // is required. + // + // If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile(). + // Calling this function also invalidates get_active_interface_variables(), so this should be called + // before that function. + VariableID build_dummy_sampler_for_combined_images(); + + // Analyzes all separate image and samplers used from the currently selected entry point, + // and re-routes them all to a combined image sampler instead. + // This is required to "support" separate image samplers in targets which do not natively support + // this feature, like GLSL/ESSL. + // + // This must be called before compile() if such remapping is desired. + // This call will add new sampled images to the SPIR-V, + // so it will appear in reflection if get_shader_resources() is called after build_combined_image_samplers. + // + // If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output, + // but will still appear in reflection. + // + // The resulting samplers will be void of any decorations like name, descriptor sets and binding points, + // so this can be added before compile() if desired. + // + // Combined image samplers originating from this set are always considered active variables. + // Arrays of separate samplers are not supported, but arrays of separate images are supported. + // Array of images + sampler -> Array of combined image samplers. + void build_combined_image_samplers(); + + // Gets a remapping for the combined image samplers. + const SmallVector &get_combined_image_samplers() const + { + return combined_image_samplers; + } + + // Set a new variable type remap callback. + // The type remapping is designed to allow global interface variable to assume more special types. + // A typical example here is to remap sampler2D into samplerExternalOES, which currently isn't supported + // directly by SPIR-V. + // + // In compile() while emitting code, + // for every variable that is declared, including function parameters, the callback will be called + // and the API user has a chance to change the textual representation of the type used to declare the variable. + // The API user can detect special patterns in names to guide the remapping. + void set_variable_type_remap_callback(VariableTypeRemapCallback cb) + { + variable_remap_callback = std::move(cb); + } + + // API for querying which specialization constants exist. + // To modify a specialization constant before compile(), use get_constant(constant.id), + // then update constants directly in the SPIRConstant data structure. + // For composite types, the subconstants can be iterated over and modified. + // constant_type is the SPIRType for the specialization constant, + // which can be queried to determine which fields in the unions should be poked at. + SmallVector get_specialization_constants() const; + SPIRConstant &get_constant(ConstantID id); + const SPIRConstant &get_constant(ConstantID id) const; + + uint32_t get_current_id_bound() const + { + return uint32_t(ir.ids.size()); + } + + // API for querying buffer objects. + // The type passed in here should be the base type of a resource, i.e. + // get_type(resource.base_type_id) + // as decorations are set in the basic Block type. + // The type passed in here must have these decorations set, or an exception is raised. + // Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set. + uint32_t type_struct_member_offset(const SPIRType &type, uint32_t index) const; + uint32_t type_struct_member_array_stride(const SPIRType &type, uint32_t index) const; + uint32_t type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const; + + // Gets the offset in SPIR-V words (uint32_t) for a decoration which was originally declared in the SPIR-V binary. + // The offset will point to one or more uint32_t literals which can be modified in-place before using the SPIR-V binary. + // Note that adding or removing decorations using the reflection API will not change the behavior of this function. + // If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true, + // otherwise, returns false. + // If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false. + bool get_binary_offset_for_decoration(VariableID id, spv::Decoration decoration, uint32_t &word_offset) const; + + // HLSL counter buffer reflection interface. + // Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where + // one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter. + // To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier + // which lets us link the two buffers together. + + // Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object. + + // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. + // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will + // only return true if OpSource was reported HLSL. + // To rely on this functionality, ensure that the SPIR-V module is not stripped. + + bool buffer_is_hlsl_counter_buffer(VariableID id) const; + + // Queries if a buffer object has a neighbor "counter" buffer. + // If so, the ID of that counter buffer will be returned in counter_id. + // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. + // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will + // only return true if OpSource was reported HLSL. + // To rely on this functionality, ensure that the SPIR-V module is not stripped. + bool buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const; + + // Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module. + const SmallVector &get_declared_capabilities() const; + + // Gets the list of all SPIR-V extensions which were declared in the SPIR-V module. + const SmallVector &get_declared_extensions() const; + + // When declaring buffer blocks in GLSL, the name declared in the GLSL source + // might not be the same as the name declared in the SPIR-V module due to naming conflicts. + // In this case, SPIRV-Cross needs to find a fallback-name, and it might only + // be possible to know this name after compiling to GLSL. + // This is particularly important for HLSL input and UAVs which tends to reuse the same block type + // for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself + // because it might be unique. Instead, you can use this interface to check after compilation which + // name was actually used if your input SPIR-V tends to have this problem. + // For other names like remapped names for variables, etc, it's generally enough to query the name of the variables + // after compiling, block names are an exception to this rule. + // ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type. + // + // This also applies to HLSL cbuffers. + std::string get_remapped_declared_block_name(VariableID id) const; + + // For buffer block variables, get the decorations for that variable. + // Sometimes, decorations for buffer blocks are found in member decorations instead + // of direct decorations on the variable itself. + // The most common use here is to check if a buffer is readonly or writeonly. + Bitset get_buffer_block_flags(VariableID id) const; + +protected: + const uint32_t *stream(const Instruction &instr) const + { + // If we're not going to use any arguments, just return nullptr. + // We want to avoid case where we return an out of range pointer + // that trips debug assertions on some platforms. + if (!instr.length) + return nullptr; + + if (instr.offset + instr.length > ir.spirv.size()) + SPIRV_CROSS_THROW("Compiler::stream() out of range."); + return &ir.spirv[instr.offset]; + } + + ParsedIR ir; + // Marks variables which have global scope and variables which can alias with other variables + // (SSBO, image load store, etc) + SmallVector global_variables; + SmallVector aliased_variables; + + SPIRFunction *current_function = nullptr; + SPIRBlock *current_block = nullptr; + uint32_t current_loop_level = 0; + std::unordered_set active_interface_variables; + bool check_active_interface_variables = false; + + void add_loop_level(); + + void set_initializers(SPIRExpression &e) + { + e.emitted_loop_level = current_loop_level; + } + + template + void set_initializers(const T &) + { + } + + // If our IDs are out of range here as part of opcodes, throw instead of + // undefined behavior. + template + T &set(uint32_t id, P &&... args) + { + ir.add_typed_id(static_cast(T::type), id); + auto &var = variant_set(ir.ids[id], std::forward

(args)...); + var.self = id; + set_initializers(var); + return var; + } + + template + T &get(uint32_t id) + { + return variant_get(ir.ids[id]); + } + + template + T *maybe_get(uint32_t id) + { + if (id >= ir.ids.size()) + return nullptr; + else if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } + + template + const T &get(uint32_t id) const + { + return variant_get(ir.ids[id]); + } + + template + const T *maybe_get(uint32_t id) const + { + if (id >= ir.ids.size()) + return nullptr; + else if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } + + // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer. + uint32_t get_pointee_type_id(uint32_t type_id) const; + + // Gets the SPIR-V type underlying the given type, which might be a pointer. + const SPIRType &get_pointee_type(const SPIRType &type) const; + + // Gets the SPIR-V type underlying the given type_id, which might be a pointer. + const SPIRType &get_pointee_type(uint32_t type_id) const; + + // Gets the ID of the SPIR-V type underlying a variable. + uint32_t get_variable_data_type_id(const SPIRVariable &var) const; + + // Gets the SPIR-V type underlying a variable. + SPIRType &get_variable_data_type(const SPIRVariable &var); + + // Gets the SPIR-V type underlying a variable. + const SPIRType &get_variable_data_type(const SPIRVariable &var) const; + + // Gets the SPIR-V element type underlying an array variable. + SPIRType &get_variable_element_type(const SPIRVariable &var); + + // Gets the SPIR-V element type underlying an array variable. + const SPIRType &get_variable_element_type(const SPIRVariable &var) const; + + // Sets the qualified member identifier for OpTypeStruct ID, member number "index". + void set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name); + void set_qualified_name(uint32_t id, const std::string &name); + + // Returns if the given type refers to a sampled image. + bool is_sampled_image_type(const SPIRType &type); + + const SPIREntryPoint &get_entry_point() const; + SPIREntryPoint &get_entry_point(); + static bool is_tessellation_shader(spv::ExecutionModel model); + + virtual std::string to_name(uint32_t id, bool allow_alias = true) const; + bool is_builtin_variable(const SPIRVariable &var) const; + bool is_builtin_type(const SPIRType &type) const; + bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const; + bool is_immutable(uint32_t id) const; + bool is_member_builtin(const SPIRType &type, uint32_t index, spv::BuiltIn *builtin) const; + bool is_scalar(const SPIRType &type) const; + bool is_vector(const SPIRType &type) const; + bool is_matrix(const SPIRType &type) const; + bool is_array(const SPIRType &type) const; + uint32_t expression_type_id(uint32_t id) const; + const SPIRType &expression_type(uint32_t id) const; + bool expression_is_lvalue(uint32_t id) const; + bool variable_storage_is_aliased(const SPIRVariable &var); + SPIRVariable *maybe_get_backing_variable(uint32_t chain); + spv::StorageClass get_expression_effective_storage_class(uint32_t ptr); + + void register_read(uint32_t expr, uint32_t chain, bool forwarded); + void register_write(uint32_t chain); + + inline bool is_continue(uint32_t next) const + { + return (ir.block_meta[next] & ParsedIR::BLOCK_META_CONTINUE_BIT) != 0; + } + + inline bool is_single_block_loop(uint32_t next) const + { + auto &block = get(next); + return block.merge == SPIRBlock::MergeLoop && block.continue_block == ID(next); + } + + inline bool is_break(uint32_t next) const + { + return (ir.block_meta[next] & + (ParsedIR::BLOCK_META_LOOP_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; + } + + inline bool is_loop_break(uint32_t next) const + { + return (ir.block_meta[next] & ParsedIR::BLOCK_META_LOOP_MERGE_BIT) != 0; + } + + inline bool is_conditional(uint32_t next) const + { + return (ir.block_meta[next] & + (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; + } + + // Dependency tracking for temporaries read from variables. + void flush_dependees(SPIRVariable &var); + void flush_all_active_variables(); + void flush_control_dependent_expressions(uint32_t block); + void flush_all_atomic_capable_variables(); + void flush_all_aliased_variables(); + void register_global_read_dependencies(const SPIRBlock &func, uint32_t id); + void register_global_read_dependencies(const SPIRFunction &func, uint32_t id); + std::unordered_set invalid_expressions; + + void update_name_cache(std::unordered_set &cache, std::string &name); + + // A variant which takes two sets of names. The secondary is only used to verify there are no collisions, + // but the set is not updated when we have found a new name. + // Used primarily when adding block interface names. + void update_name_cache(std::unordered_set &cache_primary, + const std::unordered_set &cache_secondary, std::string &name); + + bool function_is_pure(const SPIRFunction &func); + bool block_is_pure(const SPIRBlock &block); + + bool execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const; + bool execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const; + bool execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const; + SPIRBlock::ContinueBlockType continue_block_type(const SPIRBlock &continue_block) const; + + void force_recompile(); + void clear_force_recompile(); + bool is_forcing_recompilation() const; + bool is_force_recompile = false; + + bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const; + + bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; + void inherit_expression_dependencies(uint32_t dst, uint32_t source); + void add_implied_read_expression(SPIRExpression &e, uint32_t source); + void add_implied_read_expression(SPIRAccessChain &e, uint32_t source); + + // For proper multiple entry point support, allow querying if an Input or Output + // variable is part of that entry points interface. + bool interface_variable_exists_in_entry_point(uint32_t id) const; + + SmallVector combined_image_samplers; + + void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const + { + if (variable_remap_callback) + variable_remap_callback(type, var_name, type_name); + } + + void set_ir(const ParsedIR &parsed); + void set_ir(ParsedIR &&parsed); + void parse_fixup(); + + // Used internally to implement various traversals for queries. + struct OpcodeHandler + { + virtual ~OpcodeHandler() = default; + + // Return true if traversal should continue. + // If false, traversal will end immediately. + virtual bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) = 0; + + virtual bool follow_function_call(const SPIRFunction &) + { + return true; + } + + virtual void set_current_block(const SPIRBlock &) + { + } + + // Called after returning from a function or when entering a block, + // can be called multiple times per block, + // while set_current_block is only called on block entry. + virtual void rearm_current_block(const SPIRBlock &) + { + } + + virtual bool begin_function_scope(const uint32_t *, uint32_t) + { + return true; + } + + virtual bool end_function_scope(const uint32_t *, uint32_t) + { + return true; + } + }; + + struct BufferAccessHandler : OpcodeHandler + { + BufferAccessHandler(const Compiler &compiler_, SmallVector &ranges_, uint32_t id_) + : compiler(compiler_) + , ranges(ranges_) + , id(id_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + const Compiler &compiler; + SmallVector &ranges; + uint32_t id; + + std::unordered_set seen; + }; + + struct InterfaceVariableAccessHandler : OpcodeHandler + { + InterfaceVariableAccessHandler(const Compiler &compiler_, std::unordered_set &variables_) + : compiler(compiler_) + , variables(variables_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + const Compiler &compiler; + std::unordered_set &variables; + }; + + struct CombinedImageSamplerHandler : OpcodeHandler + { + CombinedImageSamplerHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + + // Each function in the call stack needs its own remapping for parameters so we can deduce which global variable each texture/sampler the parameter is statically bound to. + std::stack> parameter_remapping; + std::stack functions; + + uint32_t remap_parameter(uint32_t id); + void push_remap_parameters(const SPIRFunction &func, const uint32_t *args, uint32_t length); + void pop_remap_parameters(); + void register_combined_image_sampler(SPIRFunction &caller, VariableID combined_id, VariableID texture_id, + VariableID sampler_id, bool depth); + }; + + struct DummySamplerForCombinedImageHandler : OpcodeHandler + { + DummySamplerForCombinedImageHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + bool need_dummy_sampler = false; + }; + + struct ActiveBuiltinHandler : OpcodeHandler + { + ActiveBuiltinHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + + void handle_builtin(const SPIRType &type, spv::BuiltIn builtin, const Bitset &decoration_flags); + }; + + bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const; + bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const; + // This must be an ordered data structure so we always pick the same type aliases. + SmallVector global_struct_cache; + + ShaderResources get_shader_resources(const std::unordered_set *active_variables) const; + + VariableTypeRemapCallback variable_remap_callback; + + bool get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type); + + std::unordered_set forced_temporaries; + std::unordered_set forwarded_temporaries; + std::unordered_set suppressed_usage_tracking; + std::unordered_set hoisted_temporaries; + std::unordered_set forced_invariant_temporaries; + + Bitset active_input_builtins; + Bitset active_output_builtins; + uint32_t clip_distance_count = 0; + uint32_t cull_distance_count = 0; + bool position_invariant = false; + + void analyze_parameter_preservation( + SPIRFunction &entry, const CFG &cfg, + const std::unordered_map> &variable_to_blocks, + const std::unordered_map> &complete_write_blocks); + + // If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler. + // SPIR-V does not support this distinction, so we must keep track of this information outside the type system. + // There might be unrelated IDs found in this set which do not correspond to actual variables. + // This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs. + // Similar is implemented for images, as well as if subpass inputs are needed. + std::unordered_set comparison_ids; + bool need_subpass_input = false; + + // In certain backends, we will need to use a dummy sampler to be able to emit code. + // GLSL does not support texelFetch on texture2D objects, but SPIR-V does, + // so we need to workaround by having the application inject a dummy sampler. + uint32_t dummy_sampler_id = 0; + + void analyze_image_and_sampler_usage(); + + struct CombinedImageSamplerDrefHandler : OpcodeHandler + { + CombinedImageSamplerDrefHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + std::unordered_set dref_combined_samplers; + }; + + struct CombinedImageSamplerUsageHandler : OpcodeHandler + { + CombinedImageSamplerUsageHandler(Compiler &compiler_, + const std::unordered_set &dref_combined_samplers_) + : compiler(compiler_) + , dref_combined_samplers(dref_combined_samplers_) + { + } + + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + const std::unordered_set &dref_combined_samplers; + + std::unordered_map> dependency_hierarchy; + std::unordered_set comparison_ids; + + void add_hierarchy_to_comparison_ids(uint32_t ids); + bool need_subpass_input = false; + void add_dependency(uint32_t dst, uint32_t src); + }; + + void build_function_control_flow_graphs_and_analyze(); + std::unordered_map> function_cfgs; + const CFG &get_cfg_for_current_function() const; + const CFG &get_cfg_for_function(uint32_t id) const; + + struct CFGBuilder : OpcodeHandler + { + explicit CFGBuilder(Compiler &compiler_); + + bool follow_function_call(const SPIRFunction &func) override; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + std::unordered_map> function_cfgs; + }; + + struct AnalyzeVariableScopeAccessHandler : OpcodeHandler + { + AnalyzeVariableScopeAccessHandler(Compiler &compiler_, SPIRFunction &entry_); + + bool follow_function_call(const SPIRFunction &) override; + void set_current_block(const SPIRBlock &block) override; + + void notify_variable_access(uint32_t id, uint32_t block); + bool id_is_phi_variable(uint32_t id) const; + bool id_is_potential_temporary(uint32_t id) const; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + SPIRFunction &entry; + std::unordered_map> accessed_variables_to_block; + std::unordered_map> accessed_temporaries_to_block; + std::unordered_map result_id_to_type; + std::unordered_map> complete_write_variables_to_block; + std::unordered_map> partial_write_variables_to_block; + std::unordered_set access_chain_expressions; + // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. + std::unordered_map> access_chain_children; + const SPIRBlock *current_block = nullptr; + }; + + struct StaticExpressionAccessHandler : OpcodeHandler + { + StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_); + bool follow_function_call(const SPIRFunction &) override; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + uint32_t variable_id; + uint32_t static_expression = 0; + uint32_t write_count = 0; + }; + + struct PhysicalStorageBufferPointerHandler : OpcodeHandler + { + explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_); + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + Compiler &compiler; + std::unordered_set types; + }; + void analyze_non_block_pointer_types(); + SmallVector physical_storage_non_block_pointer_types; + + void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler); + void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler, + bool single_function); + bool may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var); + + // Finds all resources that are written to from inside the critical section, if present. + // The critical section is delimited by OpBeginInvocationInterlockEXT and + // OpEndInvocationInterlockEXT instructions. In MSL and HLSL, any resources written + // while inside the critical section must be placed in a raster order group. + struct InterlockedResourceAccessHandler : OpcodeHandler + { + InterlockedResourceAccessHandler(Compiler &compiler_, uint32_t entry_point_id) + : compiler(compiler_) + { + call_stack.push_back(entry_point_id); + } + + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + bool in_crit_sec = false; + + uint32_t interlock_function_id = 0; + bool split_function_case = false; + bool control_flow_interlock = false; + bool use_critical_section = false; + bool call_stack_is_interlocked = false; + SmallVector call_stack; + + void access_potential_resource(uint32_t id); + }; + + struct InterlockedResourceAccessPrepassHandler : OpcodeHandler + { + InterlockedResourceAccessPrepassHandler(Compiler &compiler_, uint32_t entry_point_id) + : compiler(compiler_) + { + call_stack.push_back(entry_point_id); + } + + void rearm_current_block(const SPIRBlock &block) override; + bool handle(spv::Op op, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + uint32_t interlock_function_id = 0; + uint32_t current_block_id = 0; + bool split_function_case = false; + bool control_flow_interlock = false; + SmallVector call_stack; + }; + + void analyze_interlocked_resource_usage(); + // The set of all resources written while inside the critical section, if present. + std::unordered_set interlocked_resources; + bool interlocked_is_complex = false; + + void make_constant_null(uint32_t id, uint32_t type); + + std::unordered_map declared_block_names; + + bool instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, spv::Op op, const uint32_t *args, + uint32_t length); + + Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const; + static bool is_desktop_only_format(spv::ImageFormat format); + + bool image_is_comparison(const SPIRType &type, uint32_t id) const; + + void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0); + uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; + bool has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; + void unset_extended_decoration(uint32_t id, ExtendedDecorations decoration); + + void set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration, + uint32_t value = 0); + uint32_t get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; + bool has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; + void unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration); + + bool type_is_array_of_pointers(const SPIRType &type) const; + bool type_is_top_level_physical_pointer(const SPIRType &type) const; + bool type_is_block_like(const SPIRType &type) const; + bool type_is_opaque_value(const SPIRType &type) const; + + bool reflection_ssbo_instance_name_is_significant() const; + std::string get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const; + + bool flush_phi_required(BlockID from, BlockID to) const; + + uint32_t evaluate_spec_constant_u32(const SPIRConstantOp &spec) const; + uint32_t evaluate_constant_u32(uint32_t id) const; + + bool is_vertex_like_shader() const; + +private: + // Used only to implement the old deprecated get_entry_point() interface. + const SPIREntryPoint &get_first_entry_point(const std::string &name) const; + SPIREntryPoint &get_first_entry_point(const std::string &name); +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_cross_c.cpp b/third_party/spirv-cross/spirv_cross_c.cpp new file mode 100644 index 0000000..5506d8d --- /dev/null +++ b/third_party/spirv-cross/spirv_cross_c.cpp @@ -0,0 +1,2491 @@ +/* + * Copyright 2019-2020 Hans-Kristian Arntzen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cross_c.h" + +#if SPIRV_CROSS_C_API_CPP +#include "spirv_cpp.hpp" +#endif +#if SPIRV_CROSS_C_API_GLSL +#include "spirv_glsl.hpp" +#else +#include "spirv_cross.hpp" +#endif +#if SPIRV_CROSS_C_API_HLSL +#include "spirv_hlsl.hpp" +#endif +#if SPIRV_CROSS_C_API_MSL +#include "spirv_msl.hpp" +#endif +#if SPIRV_CROSS_C_API_REFLECT +#include "spirv_reflect.hpp" +#endif + +#ifdef HAVE_SPIRV_CROSS_GIT_VERSION +#include "gitversion.h" +#endif + +#include "spirv_parser.hpp" +#include +#include +#include + +// clang-format off + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +#define SPVC_BEGIN_SAFE_SCOPE try +#else +#define SPVC_BEGIN_SAFE_SCOPE +#endif + +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +#define SPVC_END_SAFE_SCOPE(context, error) \ + catch (const std::exception &e) \ + { \ + (context)->report_error(e.what()); \ + return (error); \ + } +#else +#define SPVC_END_SAFE_SCOPE(context, error) +#endif + +using namespace std; +using namespace SPIRV_CROSS_NAMESPACE; + +struct ScratchMemoryAllocation +{ + virtual ~ScratchMemoryAllocation() = default; +}; + +struct StringAllocation : ScratchMemoryAllocation +{ + explicit StringAllocation(const char *name) + : str(name) + { + } + + explicit StringAllocation(std::string name) + : str(std::move(name)) + { + } + + std::string str; +}; + +template +struct TemporaryBuffer : ScratchMemoryAllocation +{ + SmallVector buffer; +}; + +template +static inline std::unique_ptr spvc_allocate(Ts &&... ts) +{ + return std::unique_ptr(new T(std::forward(ts)...)); +} + +struct spvc_context_s +{ + string last_error; + SmallVector> allocations; + const char *allocate_name(const std::string &name); + + spvc_error_callback callback = nullptr; + void *callback_userdata = nullptr; + void report_error(std::string msg); +}; + +void spvc_context_s::report_error(std::string msg) +{ + last_error = std::move(msg); + if (callback) + callback(callback_userdata, last_error.c_str()); +} + +const char *spvc_context_s::allocate_name(const std::string &name) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto alloc = spvc_allocate(name); + auto *ret = alloc->str.c_str(); + allocations.emplace_back(std::move(alloc)); + return ret; + } + SPVC_END_SAFE_SCOPE(this, nullptr) +} + +struct spvc_parsed_ir_s : ScratchMemoryAllocation +{ + spvc_context context = nullptr; + ParsedIR parsed; +}; + +struct spvc_compiler_s : ScratchMemoryAllocation +{ + spvc_context context = nullptr; + unique_ptr compiler; + spvc_backend backend = SPVC_BACKEND_NONE; +}; + +struct spvc_compiler_options_s : ScratchMemoryAllocation +{ + spvc_context context = nullptr; + uint32_t backend_flags = 0; +#if SPIRV_CROSS_C_API_GLSL + CompilerGLSL::Options glsl; +#endif +#if SPIRV_CROSS_C_API_MSL + CompilerMSL::Options msl; +#endif +#if SPIRV_CROSS_C_API_HLSL + CompilerHLSL::Options hlsl; +#endif +}; + +struct spvc_set_s : ScratchMemoryAllocation +{ + std::unordered_set set; +}; + +// Dummy-inherit to we can keep our opaque type handle type safe in C-land as well, +// and avoid just throwing void * around. +struct spvc_type_s : SPIRType +{ +}; + +struct spvc_constant_s : SPIRConstant +{ +}; + +struct spvc_resources_s : ScratchMemoryAllocation +{ + spvc_context context = nullptr; + SmallVector uniform_buffers; + SmallVector storage_buffers; + SmallVector stage_inputs; + SmallVector stage_outputs; + SmallVector subpass_inputs; + SmallVector storage_images; + SmallVector sampled_images; + SmallVector atomic_counters; + SmallVector push_constant_buffers; + SmallVector separate_images; + SmallVector separate_samplers; + SmallVector acceleration_structures; + + bool copy_resources(SmallVector &outputs, const SmallVector &inputs); + bool copy_resources(const ShaderResources &resources); +}; + +spvc_result spvc_context_create(spvc_context *context) +{ + auto *ctx = new (std::nothrow) spvc_context_s; + if (!ctx) + return SPVC_ERROR_OUT_OF_MEMORY; + + *context = ctx; + return SPVC_SUCCESS; +} + +void spvc_context_destroy(spvc_context context) +{ + delete context; +} + +void spvc_context_release_allocations(spvc_context context) +{ + context->allocations.clear(); +} + +const char *spvc_context_get_last_error_string(spvc_context context) +{ + return context->last_error.c_str(); +} + +SPVC_PUBLIC_API void spvc_context_set_error_callback(spvc_context context, spvc_error_callback cb, void *userdata) +{ + context->callback = cb; + context->callback_userdata = userdata; +} + +spvc_result spvc_context_parse_spirv(spvc_context context, const SpvId *spirv, size_t word_count, + spvc_parsed_ir *parsed_ir) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr pir(new (std::nothrow) spvc_parsed_ir_s); + if (!pir) + { + context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + pir->context = context; + Parser parser(spirv, word_count); + parser.parse(); + pir->parsed = move(parser.get_parsed_ir()); + *parsed_ir = pir.get(); + context->allocations.push_back(std::move(pir)); + } + SPVC_END_SAFE_SCOPE(context, SPVC_ERROR_INVALID_SPIRV) + return SPVC_SUCCESS; +} + +spvc_result spvc_context_create_compiler(spvc_context context, spvc_backend backend, spvc_parsed_ir parsed_ir, + spvc_capture_mode mode, spvc_compiler *compiler) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr comp(new (std::nothrow) spvc_compiler_s); + if (!comp) + { + context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + comp->backend = backend; + comp->context = context; + + if (mode != SPVC_CAPTURE_MODE_COPY && mode != SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + { + context->report_error("Invalid argument for capture mode."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + switch (backend) + { + case SPVC_BACKEND_NONE: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new Compiler(move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new Compiler(parsed_ir->parsed)); + break; + +#if SPIRV_CROSS_C_API_GLSL + case SPVC_BACKEND_GLSL: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerGLSL(move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerGLSL(parsed_ir->parsed)); + break; +#endif + +#if SPIRV_CROSS_C_API_HLSL + case SPVC_BACKEND_HLSL: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerHLSL(move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerHLSL(parsed_ir->parsed)); + break; +#endif + +#if SPIRV_CROSS_C_API_MSL + case SPVC_BACKEND_MSL: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerMSL(move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerMSL(parsed_ir->parsed)); + break; +#endif + +#if SPIRV_CROSS_C_API_CPP + case SPVC_BACKEND_CPP: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerCPP(move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerCPP(parsed_ir->parsed)); + break; +#endif + +#if SPIRV_CROSS_C_API_REFLECT + case SPVC_BACKEND_JSON: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerReflection(move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerReflection(parsed_ir->parsed)); + break; +#endif + + default: + context->report_error("Invalid backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + *compiler = comp.get(); + context->allocations.push_back(std::move(comp)); + } + SPVC_END_SAFE_SCOPE(context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_create_compiler_options(spvc_compiler compiler, spvc_compiler_options *options) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr opt(new (std::nothrow) spvc_compiler_options_s); + if (!opt) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + opt->context = compiler->context; + opt->backend_flags = 0; + switch (compiler->backend) + { +#if SPIRV_CROSS_C_API_MSL + case SPVC_BACKEND_MSL: + opt->backend_flags |= SPVC_COMPILER_OPTION_MSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT; + opt->glsl = static_cast(compiler->compiler.get())->get_common_options(); + opt->msl = static_cast(compiler->compiler.get())->get_msl_options(); + break; +#endif + +#if SPIRV_CROSS_C_API_HLSL + case SPVC_BACKEND_HLSL: + opt->backend_flags |= SPVC_COMPILER_OPTION_HLSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT; + opt->glsl = static_cast(compiler->compiler.get())->get_common_options(); + opt->hlsl = static_cast(compiler->compiler.get())->get_hlsl_options(); + break; +#endif + +#if SPIRV_CROSS_C_API_GLSL + case SPVC_BACKEND_GLSL: + opt->backend_flags |= SPVC_COMPILER_OPTION_GLSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT; + opt->glsl = static_cast(compiler->compiler.get())->get_common_options(); + break; +#endif + + default: + break; + } + + *options = opt.get(); + compiler->context->allocations.push_back(std::move(opt)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_options_set_bool(spvc_compiler_options options, spvc_compiler_option option, + spvc_bool value) +{ + return spvc_compiler_options_set_uint(options, option, value ? 1 : 0); +} + +spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_compiler_option option, unsigned value) +{ + (void)value; + (void)option; + uint32_t supported_mask = options->backend_flags; + uint32_t required_mask = option & SPVC_COMPILER_OPTION_LANG_BITS; + if ((required_mask | supported_mask) != supported_mask) + { + options->context->report_error("Option is not supported by current backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + switch (option) + { +#if SPIRV_CROSS_C_API_GLSL + case SPVC_COMPILER_OPTION_FORCE_TEMPORARY: + options->glsl.force_temporary = value != 0; + break; + case SPVC_COMPILER_OPTION_FLATTEN_MULTIDIMENSIONAL_ARRAYS: + options->glsl.flatten_multidimensional_arrays = value != 0; + break; + case SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION: + options->glsl.vertex.fixup_clipspace = value != 0; + break; + case SPVC_COMPILER_OPTION_FLIP_VERTEX_Y: + options->glsl.vertex.flip_vert_y = value != 0; + break; + case SPVC_COMPILER_OPTION_EMIT_LINE_DIRECTIVES: + options->glsl.emit_line_directives = value != 0; + break; + case SPVC_COMPILER_OPTION_ENABLE_STORAGE_IMAGE_QUALIFIER_DEDUCTION: + options->glsl.enable_storage_image_qualifier_deduction = value != 0; + break; + case SPVC_COMPILER_OPTION_FORCE_ZERO_INITIALIZED_VARIABLES: + options->glsl.force_zero_initialized_variables = value != 0; + break; + + case SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE: + options->glsl.vertex.support_nonzero_base_instance = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS: + options->glsl.separate_shader_objects = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION: + options->glsl.enable_420pack_extension = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_VERSION: + options->glsl.version = value; + break; + case SPVC_COMPILER_OPTION_GLSL_ES: + options->glsl.es = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS: + options->glsl.vulkan_semantics = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP: + options->glsl.fragment.default_float_precision = + value != 0 ? CompilerGLSL::Options::Precision::Highp : CompilerGLSL::Options::Precision::Mediump; + break; + case SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP: + options->glsl.fragment.default_int_precision = + value != 0 ? CompilerGLSL::Options::Precision::Highp : CompilerGLSL::Options::Precision::Mediump; + break; + case SPVC_COMPILER_OPTION_GLSL_EMIT_PUSH_CONSTANT_AS_UNIFORM_BUFFER: + options->glsl.emit_push_constant_as_uniform_buffer = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS: + options->glsl.emit_uniform_buffer_as_plain_uniforms = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS: + options->glsl.force_flattened_io_blocks = value != 0; + break; +#endif + +#if SPIRV_CROSS_C_API_HLSL + case SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL: + options->hlsl.shader_model = value; + break; + + case SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT: + options->hlsl.point_size_compat = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT: + options->hlsl.point_coord_compat = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE: + options->hlsl.support_nonzero_base_vertex_base_instance = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_FORCE_STORAGE_BUFFER_AS_UAV: + options->hlsl.force_storage_buffer_as_uav = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV: + options->hlsl.nonwritable_uav_texture_as_srv = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_ENABLE_16BIT_TYPES: + options->hlsl.enable_16bit_types = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS: + options->hlsl.flatten_matrix_vertex_input_semantics = value != 0; + break; +#endif + +#if SPIRV_CROSS_C_API_MSL + case SPVC_COMPILER_OPTION_MSL_VERSION: + options->msl.msl_version = value; + break; + + case SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH: + options->msl.texel_buffer_texture_width = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SWIZZLE_BUFFER_INDEX: + options->msl.swizzle_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX: + options->msl.indirect_params_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX: + options->msl.shader_output_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX: + options->msl.shader_patch_output_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX: + options->msl.shader_tess_factor_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX: + options->msl.shader_input_wg_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN: + options->msl.enable_point_size_builtin = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION: + options->msl.disable_rasterization = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER: + options->msl.capture_output_to_buffer = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES: + options->msl.swizzle_texture_samples = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS: + options->msl.pad_fragment_output_components = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT: + options->msl.tess_domain_origin_lower_left = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_PLATFORM: + options->msl.platform = static_cast(value); + break; + + case SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS: + options->msl.argument_buffers = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_TEXTURE_BUFFER_NATIVE: + options->msl.texture_buffer_native = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX: + options->msl.buffer_size_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_MULTIVIEW: + options->msl.multiview = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_VIEW_MASK_BUFFER_INDEX: + options->msl.view_mask_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_DEVICE_INDEX: + options->msl.device_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_VIEW_INDEX_FROM_DEVICE_INDEX: + options->msl.view_index_from_device_index = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_DISPATCH_BASE: + options->msl.dispatch_base = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_DYNAMIC_OFFSETS_BUFFER_INDEX: + options->msl.dynamic_offsets_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_TEXTURE_1D_AS_2D: + options->msl.texture_1D_as_2D = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_BASE_INDEX_ZERO: + options->msl.enable_base_index_zero = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS: + options->msl.use_framebuffer_fetch_subpasses = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH: + options->msl.invariant_float_math = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY: + options->msl.emulate_cube_array = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING: + options->msl.enable_decoration_binding = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES: + options->msl.force_active_argument_buffer_resources = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS: + options->msl.force_native_arrays = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK: + options->msl.enable_frag_output_mask = value; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN: + options->msl.enable_frag_depth_builtin = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN: + options->msl.enable_frag_stencil_ref_builtin = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_CLIP_DISTANCE_USER_VARYING: + options->msl.enable_clip_distance_user_varying = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_MULTI_PATCH_WORKGROUP: + options->msl.multi_patch_workgroup = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_BUFFER_INDEX: + options->msl.shader_input_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_INDEX_BUFFER_INDEX: + options->msl.shader_index_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_VERTEX_FOR_TESSELLATION: + options->msl.vertex_for_tessellation = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_VERTEX_INDEX_TYPE: + options->msl.vertex_index_type = static_cast(value); + break; + + case SPVC_COMPILER_OPTION_MSL_MULTIVIEW_LAYERED_RENDERING: + options->msl.multiview_layered_rendering = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ARRAYED_SUBPASS_INPUT: + options->msl.arrayed_subpass_input = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_R32UI_LINEAR_TEXTURE_ALIGNMENT: + options->msl.r32ui_linear_texture_alignment = value; + break; + + case SPVC_COMPILER_OPTION_MSL_R32UI_ALIGNMENT_CONSTANT_ID: + options->msl.r32ui_alignment_constant_id = value; + break; +#endif + + default: + options->context->report_error("Unknown option."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_install_compiler_options(spvc_compiler compiler, spvc_compiler_options options) +{ + (void)options; + switch (compiler->backend) + { +#if SPIRV_CROSS_C_API_GLSL + case SPVC_BACKEND_GLSL: + static_cast(*compiler->compiler).set_common_options(options->glsl); + break; +#endif + +#if SPIRV_CROSS_C_API_HLSL + case SPVC_BACKEND_HLSL: + static_cast(*compiler->compiler).set_common_options(options->glsl); + static_cast(*compiler->compiler).set_hlsl_options(options->hlsl); + break; +#endif + +#if SPIRV_CROSS_C_API_MSL + case SPVC_BACKEND_MSL: + static_cast(*compiler->compiler).set_common_options(options->glsl); + static_cast(*compiler->compiler).set_msl_options(options->msl); + break; +#endif + + default: + break; + } + + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + static_cast(compiler->compiler.get())->add_header_line(line); + return SPVC_SUCCESS; +#else + (void)line; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *line) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + static_cast(compiler->compiler.get())->require_extension(line); + return SPVC_SUCCESS; +#else + (void)line; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + static_cast(compiler->compiler.get())->flatten_buffer_block(id); + return SPVC_SUCCESS; +#else + (void)id; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + return static_cast(compiler->compiler.get())->variable_is_depth_or_compare(id) ? SPVC_TRUE : SPVC_FALSE; +#else + (void)id; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_FALSE; +#endif +} + +spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler, + const spvc_hlsl_root_constants *constant_info, + size_t count) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + vector roots; + roots.reserve(count); + for (size_t i = 0; i < count; i++) + { + RootConstants root; + root.binding = constant_info[i].binding; + root.space = constant_info[i].space; + root.start = constant_info[i].start; + root.end = constant_info[i].end; + roots.push_back(root); + } + + hlsl.set_root_constant_layouts(std::move(roots)); + return SPVC_SUCCESS; +#else + (void)constant_info; + (void)count; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_hlsl_add_vertex_attribute_remap(spvc_compiler compiler, + const spvc_hlsl_vertex_attribute_remap *remap, + size_t count) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + HLSLVertexAttributeRemap re; + auto &hlsl = *static_cast(compiler->compiler.get()); + for (size_t i = 0; i < count; i++) + { + re.location = remap[i].location; + re.semantic = remap[i].semantic; + hlsl.add_vertex_attribute_remap(re); + } + + return SPVC_SUCCESS; +#else + (void)remap; + (void)count; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return 0; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + return hlsl.remap_num_workgroups_builtin(); +#else + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return 0; +#endif +} + +spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler, + spvc_hlsl_binding_flags flags) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + hlsl.set_resource_binding_flags(flags); + return SPVC_SUCCESS; +#else + (void)flags; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + HLSLResourceBinding bind; + bind.binding = binding->binding; + bind.desc_set = binding->desc_set; + bind.stage = static_cast(binding->stage); + bind.cbv.register_binding = binding->cbv.register_binding; + bind.cbv.register_space = binding->cbv.register_space; + bind.uav.register_binding = binding->uav.register_binding; + bind.uav.register_space = binding->uav.register_space; + bind.srv.register_binding = binding->srv.register_binding; + bind.srv.register_space = binding->srv.register_space; + bind.sampler.register_binding = binding->sampler.register_binding; + bind.sampler.register_space = binding->sampler.register_space; + hlsl.add_hlsl_resource_binding(bind); + return SPVC_SUCCESS; +#else + (void)binding; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, SpvExecutionModel model, unsigned set, + unsigned binding) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_FALSE; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + return hlsl.is_hlsl_resource_binding_used(static_cast(model), set, binding) ? SPVC_TRUE : + SPVC_FALSE; +#else + (void)model; + (void)set; + (void)binding; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.get_is_rasterization_disabled() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_swizzle_buffer(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_swizzle_buffer() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_buffer_size_buffer(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_buffer_size_buffer() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_aux_buffer(spvc_compiler compiler) +{ + return spvc_compiler_msl_needs_swizzle_buffer(compiler); +} + +spvc_bool spvc_compiler_msl_needs_output_buffer(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_output_buffer() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_patch_output_buffer(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_patch_output_buffer() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_input_threadgroup_mem(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_input_threadgroup_mem() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, const spvc_msl_vertex_attribute *va) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLShaderInput attr; + attr.location = va->location; + attr.format = static_cast(va->format); + attr.builtin = static_cast(va->builtin); + msl.add_msl_shader_input(attr); + return SPVC_SUCCESS; +#else + (void)va; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, const spvc_msl_shader_input *si) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLShaderInput input; + input.location = si->location; + input.format = static_cast(si->format); + input.builtin = static_cast(si->builtin); + input.vecsize = si->vecsize; + msl.add_msl_shader_input(input); + return SPVC_SUCCESS; +#else + (void)si; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler, + const spvc_msl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLResourceBinding bind; + bind.binding = binding->binding; + bind.desc_set = binding->desc_set; + bind.stage = static_cast(binding->stage); + bind.msl_buffer = binding->msl_buffer; + bind.msl_texture = binding->msl_texture; + bind.msl_sampler = binding->msl_sampler; + msl.add_msl_resource_binding(bind); + return SPVC_SUCCESS; +#else + (void)binding; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigned desc_set, unsigned binding, unsigned index) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.add_dynamic_buffer(desc_set, binding, index); + return SPVC_SUCCESS; +#else + (void)binding; + (void)desc_set; + (void)index; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.add_inline_uniform_block(desc_set, binding); + return SPVC_SUCCESS; +#else + (void)binding; + (void)desc_set; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.add_discrete_descriptor_set(desc_set); + return SPVC_SUCCESS; +#else + (void)desc_set; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.set_argument_buffer_device_address_space(desc_set, bool(device_address)); + return SPVC_SUCCESS; +#else + (void)desc_set; + (void)device_address; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_bool spvc_compiler_msl_is_shader_input_used(spvc_compiler compiler, unsigned location) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.is_msl_shader_input_used(location) ? SPVC_TRUE : SPVC_FALSE; +#else + (void)location; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location) +{ + return spvc_compiler_msl_is_shader_input_used(compiler, location); +} + +spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler, SpvExecutionModel model, unsigned set, + unsigned binding) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.is_msl_resource_binding_used(static_cast(model), set, binding) ? SPVC_TRUE : + SPVC_FALSE; +#else + (void)model; + (void)set; + (void)binding; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_result spvc_compiler_msl_set_combined_sampler_suffix(spvc_compiler compiler, const char *suffix) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.set_combined_sampler_suffix(suffix); + return SPVC_SUCCESS; +#else + (void)suffix; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +const char *spvc_compiler_msl_get_combined_sampler_suffix(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return ""; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.get_combined_sampler_suffix(); +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return ""; +#endif +} + +#if SPIRV_CROSS_C_API_MSL +static void spvc_convert_msl_sampler(MSLConstexprSampler &samp, const spvc_msl_constexpr_sampler *sampler) +{ + samp.s_address = static_cast(sampler->s_address); + samp.t_address = static_cast(sampler->t_address); + samp.r_address = static_cast(sampler->r_address); + samp.lod_clamp_min = sampler->lod_clamp_min; + samp.lod_clamp_max = sampler->lod_clamp_max; + samp.lod_clamp_enable = sampler->lod_clamp_enable != 0; + samp.min_filter = static_cast(sampler->min_filter); + samp.mag_filter = static_cast(sampler->mag_filter); + samp.mip_filter = static_cast(sampler->mip_filter); + samp.compare_enable = sampler->compare_enable != 0; + samp.anisotropy_enable = sampler->anisotropy_enable != 0; + samp.max_anisotropy = sampler->max_anisotropy; + samp.compare_func = static_cast(sampler->compare_func); + samp.coord = static_cast(sampler->coord); + samp.border_color = static_cast(sampler->border_color); +} + +static void spvc_convert_msl_sampler_ycbcr_conversion(MSLConstexprSampler &samp, const spvc_msl_sampler_ycbcr_conversion *conv) +{ + samp.ycbcr_conversion_enable = conv != nullptr; + if (conv == nullptr) return; + samp.planes = conv->planes; + samp.resolution = static_cast(conv->resolution); + samp.chroma_filter = static_cast(conv->chroma_filter); + samp.x_chroma_offset = static_cast(conv->x_chroma_offset); + samp.y_chroma_offset = static_cast(conv->y_chroma_offset); + for (int i = 0; i < 4; i++) + samp.swizzle[i] = static_cast(conv->swizzle[i]); + samp.ycbcr_model = static_cast(conv->ycbcr_model); + samp.ycbcr_range = static_cast(conv->ycbcr_range); + samp.bpc = conv->bpc; +} +#endif + +spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, + const spvc_msl_constexpr_sampler *sampler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLConstexprSampler samp; + spvc_convert_msl_sampler(samp, sampler); + msl.remap_constexpr_sampler(id, samp); + return SPVC_SUCCESS; +#else + (void)id; + (void)sampler; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding(spvc_compiler compiler, + unsigned desc_set, unsigned binding, + const spvc_msl_constexpr_sampler *sampler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLConstexprSampler samp; + spvc_convert_msl_sampler(samp, sampler); + msl.remap_constexpr_sampler_by_binding(desc_set, binding, samp); + return SPVC_SUCCESS; +#else + (void)desc_set; + (void)binding; + (void)sampler; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_remap_constexpr_sampler_ycbcr(spvc_compiler compiler, spvc_variable_id id, + const spvc_msl_constexpr_sampler *sampler, + const spvc_msl_sampler_ycbcr_conversion *conv) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLConstexprSampler samp; + spvc_convert_msl_sampler(samp, sampler); + spvc_convert_msl_sampler_ycbcr_conversion(samp, conv); + msl.remap_constexpr_sampler(id, samp); + return SPVC_SUCCESS; +#else + (void)id; + (void)sampler; + (void)conv; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(spvc_compiler compiler, + unsigned desc_set, unsigned binding, + const spvc_msl_constexpr_sampler *sampler, + const spvc_msl_sampler_ycbcr_conversion *conv) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLConstexprSampler samp; + spvc_convert_msl_sampler(samp, sampler); + spvc_convert_msl_sampler_ycbcr_conversion(samp, conv); + msl.remap_constexpr_sampler_by_binding(desc_set, binding, samp); + return SPVC_SUCCESS; +#else + (void)desc_set; + (void)binding; + (void)sampler; + (void)conv; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, + unsigned components) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.set_fragment_output_components(location, components); + return SPVC_SUCCESS; +#else + (void)location; + (void)components; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +unsigned spvc_compiler_msl_get_automatic_resource_binding(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return uint32_t(-1); + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.get_automatic_msl_resource_binding(id); +#else + (void)id; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return uint32_t(-1); +#endif +} + +unsigned spvc_compiler_msl_get_automatic_resource_binding_secondary(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return uint32_t(-1); + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.get_automatic_msl_resource_binding_secondary(id); +#else + (void)id; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return uint32_t(-1); +#endif +} + +spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto result = compiler->compiler->compile(); + if (result.empty()) + { + compiler->context->report_error("Unsupported SPIR-V."); + return SPVC_ERROR_UNSUPPORTED_SPIRV; + } + + *source = compiler->context->allocate_name(result); + if (!*source) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + return SPVC_SUCCESS; + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_UNSUPPORTED_SPIRV) +} + +bool spvc_resources_s::copy_resources(SmallVector &outputs, + const SmallVector &inputs) +{ + for (auto &i : inputs) + { + spvc_reflected_resource r; + r.base_type_id = i.base_type_id; + r.type_id = i.type_id; + r.id = i.id; + r.name = context->allocate_name(i.name); + if (!r.name) + return false; + + outputs.push_back(r); + } + + return true; +} + +bool spvc_resources_s::copy_resources(const ShaderResources &resources) +{ + if (!copy_resources(uniform_buffers, resources.uniform_buffers)) + return false; + if (!copy_resources(storage_buffers, resources.storage_buffers)) + return false; + if (!copy_resources(stage_inputs, resources.stage_inputs)) + return false; + if (!copy_resources(stage_outputs, resources.stage_outputs)) + return false; + if (!copy_resources(subpass_inputs, resources.subpass_inputs)) + return false; + if (!copy_resources(storage_images, resources.storage_images)) + return false; + if (!copy_resources(sampled_images, resources.sampled_images)) + return false; + if (!copy_resources(atomic_counters, resources.atomic_counters)) + return false; + if (!copy_resources(push_constant_buffers, resources.push_constant_buffers)) + return false; + if (!copy_resources(separate_images, resources.separate_images)) + return false; + if (!copy_resources(separate_samplers, resources.separate_samplers)) + return false; + if (!copy_resources(acceleration_structures, resources.acceleration_structures)) + return false; + + return true; +} + +spvc_result spvc_compiler_get_active_interface_variables(spvc_compiler compiler, spvc_set *set) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr ptr(new (std::nothrow) spvc_set_s); + if (!ptr) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + auto active = compiler->compiler->get_active_interface_variables(); + ptr->set = std::move(active); + *set = ptr.get(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_set_enabled_interface_variables(spvc_compiler compiler, spvc_set set) +{ + SPVC_BEGIN_SAFE_SCOPE + { + compiler->compiler->set_enabled_interface_variables(set->set); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_create_shader_resources_for_active_variables(spvc_compiler compiler, spvc_resources *resources, + spvc_set set) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr res(new (std::nothrow) spvc_resources_s); + if (!res) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + res->context = compiler->context; + auto accessed_resources = compiler->compiler->get_shader_resources(set->set); + + if (!res->copy_resources(accessed_resources)) + { + res->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + *resources = res.get(); + compiler->context->allocations.push_back(std::move(res)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_create_shader_resources(spvc_compiler compiler, spvc_resources *resources) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr res(new (std::nothrow) spvc_resources_s); + if (!res) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + res->context = compiler->context; + auto accessed_resources = compiler->compiler->get_shader_resources(); + + if (!res->copy_resources(accessed_resources)) + { + res->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + *resources = res.get(); + compiler->context->allocations.push_back(std::move(res)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources, spvc_resource_type type, + const spvc_reflected_resource **resource_list, + size_t *resource_size) +{ + const SmallVector *list = nullptr; + switch (type) + { + case SPVC_RESOURCE_TYPE_UNIFORM_BUFFER: + list = &resources->uniform_buffers; + break; + + case SPVC_RESOURCE_TYPE_STORAGE_BUFFER: + list = &resources->storage_buffers; + break; + + case SPVC_RESOURCE_TYPE_STAGE_INPUT: + list = &resources->stage_inputs; + break; + + case SPVC_RESOURCE_TYPE_STAGE_OUTPUT: + list = &resources->stage_outputs; + break; + + case SPVC_RESOURCE_TYPE_SUBPASS_INPUT: + list = &resources->subpass_inputs; + break; + + case SPVC_RESOURCE_TYPE_STORAGE_IMAGE: + list = &resources->storage_images; + break; + + case SPVC_RESOURCE_TYPE_SAMPLED_IMAGE: + list = &resources->sampled_images; + break; + + case SPVC_RESOURCE_TYPE_ATOMIC_COUNTER: + list = &resources->atomic_counters; + break; + + case SPVC_RESOURCE_TYPE_PUSH_CONSTANT: + list = &resources->push_constant_buffers; + break; + + case SPVC_RESOURCE_TYPE_SEPARATE_IMAGE: + list = &resources->separate_images; + break; + + case SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS: + list = &resources->separate_samplers; + break; + + case SPVC_RESOURCE_TYPE_ACCELERATION_STRUCTURE: + list = &resources->acceleration_structures; + break; + + default: + break; + } + + if (!list) + { + resources->context->report_error("Invalid argument."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + *resource_size = list->size(); + *resource_list = list->data(); + return SPVC_SUCCESS; +} + +void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, unsigned argument) +{ + compiler->compiler->set_decoration(id, static_cast(decoration), argument); +} + +void spvc_compiler_set_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration, + const char *argument) +{ + compiler->compiler->set_decoration_string(id, static_cast(decoration), argument); +} + +void spvc_compiler_set_name(spvc_compiler compiler, SpvId id, const char *argument) +{ + compiler->compiler->set_name(id, argument); +} + +void spvc_compiler_set_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration, unsigned argument) +{ + compiler->compiler->set_member_decoration(id, member_index, static_cast(decoration), argument); +} + +void spvc_compiler_set_member_decoration_string(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration, const char *argument) +{ + compiler->compiler->set_member_decoration_string(id, member_index, static_cast(decoration), + argument); +} + +void spvc_compiler_set_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index, const char *argument) +{ + compiler->compiler->set_member_name(id, member_index, argument); +} + +void spvc_compiler_unset_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration) +{ + compiler->compiler->unset_decoration(id, static_cast(decoration)); +} + +void spvc_compiler_unset_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration) +{ + compiler->compiler->unset_member_decoration(id, member_index, static_cast(decoration)); +} + +spvc_bool spvc_compiler_has_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration) +{ + return compiler->compiler->has_decoration(id, static_cast(decoration)) ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_compiler_has_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration) +{ + return compiler->compiler->has_member_decoration(id, member_index, static_cast(decoration)) ? + SPVC_TRUE : + SPVC_FALSE; +} + +const char *spvc_compiler_get_name(spvc_compiler compiler, SpvId id) +{ + return compiler->compiler->get_name(id).c_str(); +} + +unsigned spvc_compiler_get_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration) +{ + return compiler->compiler->get_decoration(id, static_cast(decoration)); +} + +const char *spvc_compiler_get_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration) +{ + return compiler->compiler->get_decoration_string(id, static_cast(decoration)).c_str(); +} + +unsigned spvc_compiler_get_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration) +{ + return compiler->compiler->get_member_decoration(id, member_index, static_cast(decoration)); +} + +const char *spvc_compiler_get_member_decoration_string(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration) +{ + return compiler->compiler->get_member_decoration_string(id, member_index, static_cast(decoration)) + .c_str(); +} + +const char *spvc_compiler_get_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index) +{ + return compiler->compiler->get_member_name(id, member_index).c_str(); +} + +spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler, const spvc_entry_point **entry_points, + size_t *num_entry_points) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto entries = compiler->compiler->get_entry_points_and_stages(); + SmallVector translated; + translated.reserve(entries.size()); + + for (auto &entry : entries) + { + spvc_entry_point new_entry; + new_entry.execution_model = static_cast(entry.execution_model); + new_entry.name = compiler->context->allocate_name(entry.name); + if (!new_entry.name) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + translated.push_back(new_entry); + } + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(translated); + *entry_points = ptr->buffer.data(); + *num_entry_points = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_set_entry_point(spvc_compiler compiler, const char *name, SpvExecutionModel model) +{ + compiler->compiler->set_entry_point(name, static_cast(model)); + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_rename_entry_point(spvc_compiler compiler, const char *old_name, const char *new_name, + SpvExecutionModel model) +{ + SPVC_BEGIN_SAFE_SCOPE + { + compiler->compiler->rename_entry_point(old_name, new_name, static_cast(model)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +const char *spvc_compiler_get_cleansed_entry_point_name(spvc_compiler compiler, const char *name, + SpvExecutionModel model) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto cleansed_name = + compiler->compiler->get_cleansed_entry_point_name(name, static_cast(model)); + return compiler->context->allocate_name(cleansed_name); + } + SPVC_END_SAFE_SCOPE(compiler->context, nullptr) +} + +void spvc_compiler_set_execution_mode(spvc_compiler compiler, SpvExecutionMode mode) +{ + compiler->compiler->set_execution_mode(static_cast(mode)); +} + +void spvc_compiler_set_execution_mode_with_arguments(spvc_compiler compiler, SpvExecutionMode mode, unsigned arg0, + unsigned arg1, + unsigned arg2) +{ + compiler->compiler->set_execution_mode(static_cast(mode), arg0, arg1, arg2); +} + +void spvc_compiler_unset_execution_mode(spvc_compiler compiler, SpvExecutionMode mode) +{ + compiler->compiler->unset_execution_mode(static_cast(mode)); +} + +spvc_result spvc_compiler_get_execution_modes(spvc_compiler compiler, const SpvExecutionMode **modes, size_t *num_modes) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto ptr = spvc_allocate>(); + + compiler->compiler->get_execution_mode_bitset().for_each_bit( + [&](uint32_t bit) { ptr->buffer.push_back(static_cast(bit)); }); + + *modes = ptr->buffer.data(); + *num_modes = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler compiler, SpvExecutionMode mode) +{ + return compiler->compiler->get_execution_mode_argument(static_cast(mode)); +} + +unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler, SpvExecutionMode mode, + unsigned index) +{ + return compiler->compiler->get_execution_mode_argument(static_cast(mode), index); +} + +SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler) +{ + return static_cast(compiler->compiler->get_execution_model()); +} + +spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id) +{ + // Should only throw if an intentionally garbage ID is passed, but the IDs are not type-safe. + SPVC_BEGIN_SAFE_SCOPE + { + return static_cast(&compiler->compiler->get_type(id)); + } + SPVC_END_SAFE_SCOPE(compiler->context, nullptr) +} + +spvc_type_id spvc_type_get_base_type_id(spvc_type type) +{ + return type->self; +} + +static spvc_basetype convert_basetype(SPIRType::BaseType type) +{ + // For now the enums match up. + return static_cast(type); +} + +spvc_basetype spvc_type_get_basetype(spvc_type type) +{ + return convert_basetype(type->basetype); +} + +unsigned spvc_type_get_bit_width(spvc_type type) +{ + return type->width; +} + +unsigned spvc_type_get_vector_size(spvc_type type) +{ + return type->vecsize; +} + +unsigned spvc_type_get_columns(spvc_type type) +{ + return type->columns; +} + +unsigned spvc_type_get_num_array_dimensions(spvc_type type) +{ + return unsigned(type->array.size()); +} + +spvc_bool spvc_type_array_dimension_is_literal(spvc_type type, unsigned dimension) +{ + return type->array_size_literal[dimension] ? SPVC_TRUE : SPVC_FALSE; +} + +SpvId spvc_type_get_array_dimension(spvc_type type, unsigned dimension) +{ + return type->array[dimension]; +} + +unsigned spvc_type_get_num_member_types(spvc_type type) +{ + return unsigned(type->member_types.size()); +} + +spvc_type_id spvc_type_get_member_type(spvc_type type, unsigned index) +{ + return type->member_types[index]; +} + +SpvStorageClass spvc_type_get_storage_class(spvc_type type) +{ + return static_cast(type->storage); +} + +// Image type query. +spvc_type_id spvc_type_get_image_sampled_type(spvc_type type) +{ + return type->image.type; +} + +SpvDim spvc_type_get_image_dimension(spvc_type type) +{ + return static_cast(type->image.dim); +} + +spvc_bool spvc_type_get_image_is_depth(spvc_type type) +{ + return type->image.depth ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_type_get_image_arrayed(spvc_type type) +{ + return type->image.arrayed ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_type_get_image_multisampled(spvc_type type) +{ + return type->image.ms ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_type_get_image_is_storage(spvc_type type) +{ + return type->image.sampled == 2 ? SPVC_TRUE : SPVC_FALSE; +} + +SpvImageFormat spvc_type_get_image_storage_format(spvc_type type) +{ + return static_cast(static_cast(type)->image.format); +} + +SpvAccessQualifier spvc_type_get_image_access_qualifier(spvc_type type) +{ + return static_cast(static_cast(type)->image.access); +} + +spvc_result spvc_compiler_get_declared_struct_size(spvc_compiler compiler, spvc_type struct_type, size_t *size) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *size = compiler->compiler->get_declared_struct_size(*static_cast(struct_type)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_declared_struct_size_runtime_array(spvc_compiler compiler, spvc_type struct_type, + size_t array_size, size_t *size) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *size = compiler->compiler->get_declared_struct_size_runtime_array(*static_cast(struct_type), + array_size); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_declared_struct_member_size(spvc_compiler compiler, spvc_type struct_type, unsigned index, size_t *size) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *size = compiler->compiler->get_declared_struct_member_size(*static_cast(struct_type), index); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_type_struct_member_offset(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *offset) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *offset = compiler->compiler->type_struct_member_offset(*static_cast(type), index); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_type_struct_member_array_stride(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *stride) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *stride = compiler->compiler->type_struct_member_array_stride(*static_cast(type), index); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_type_struct_member_matrix_stride(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *stride) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *stride = compiler->compiler->type_struct_member_matrix_stride(*static_cast(type), index); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_build_dummy_sampler_for_combined_images(spvc_compiler compiler, spvc_variable_id *id) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *id = compiler->compiler->build_dummy_sampler_for_combined_images(); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_build_combined_image_samplers(spvc_compiler compiler) +{ + SPVC_BEGIN_SAFE_SCOPE + { + compiler->compiler->build_combined_image_samplers(); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_UNSUPPORTED_SPIRV) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler, + const spvc_combined_image_sampler **samplers, + size_t *num_samplers) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto combined = compiler->compiler->get_combined_image_samplers(); + SmallVector translated; + translated.reserve(combined.size()); + for (auto &c : combined) + { + spvc_combined_image_sampler trans = { c.combined_id, c.image_id, c.sampler_id }; + translated.push_back(trans); + } + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(translated); + *samplers = ptr->buffer.data(); + *num_samplers = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler, + const spvc_specialization_constant **constants, + size_t *num_constants) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto spec_constants = compiler->compiler->get_specialization_constants(); + SmallVector translated; + translated.reserve(spec_constants.size()); + for (auto &c : spec_constants) + { + spvc_specialization_constant trans = { c.id, c.constant_id }; + translated.push_back(trans); + } + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(translated); + *constants = ptr->buffer.data(); + *num_constants = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_constant spvc_compiler_get_constant_handle(spvc_compiler compiler, spvc_variable_id id) +{ + SPVC_BEGIN_SAFE_SCOPE + { + return static_cast(&compiler->compiler->get_constant(id)); + } + SPVC_END_SAFE_SCOPE(compiler->context, nullptr) +} + +spvc_constant_id spvc_compiler_get_work_group_size_specialization_constants(spvc_compiler compiler, + spvc_specialization_constant *x, + spvc_specialization_constant *y, + spvc_specialization_constant *z) +{ + SpecializationConstant tmpx; + SpecializationConstant tmpy; + SpecializationConstant tmpz; + spvc_constant_id ret = compiler->compiler->get_work_group_size_specialization_constants(tmpx, tmpy, tmpz); + x->id = tmpx.id; + x->constant_id = tmpx.constant_id; + y->id = tmpy.id; + y->constant_id = tmpy.constant_id; + z->id = tmpz.id; + z->constant_id = tmpz.constant_id; + return ret; +} + +spvc_result spvc_compiler_get_active_buffer_ranges(spvc_compiler compiler, + spvc_variable_id id, + const spvc_buffer_range **ranges, + size_t *num_ranges) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto active_ranges = compiler->compiler->get_active_buffer_ranges(id); + SmallVector translated; + translated.reserve(active_ranges.size()); + for (auto &r : active_ranges) + { + spvc_buffer_range trans = { r.index, r.offset, r.range }; + translated.push_back(trans); + } + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(translated); + *ranges = ptr->buffer.data(); + *num_ranges = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +float spvc_constant_get_scalar_fp16(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_f16(column, row); +} + +float spvc_constant_get_scalar_fp32(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_f32(column, row); +} + +double spvc_constant_get_scalar_fp64(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_f64(column, row); +} + +unsigned spvc_constant_get_scalar_u32(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar(column, row); +} + +int spvc_constant_get_scalar_i32(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_i32(column, row); +} + +unsigned spvc_constant_get_scalar_u16(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_u16(column, row); +} + +int spvc_constant_get_scalar_i16(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_i16(column, row); +} + +unsigned spvc_constant_get_scalar_u8(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_u8(column, row); +} + +int spvc_constant_get_scalar_i8(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_i8(column, row); +} + +void spvc_constant_get_subconstants(spvc_constant constant, const spvc_constant_id **constituents, size_t *count) +{ + static_assert(sizeof(spvc_constant_id) == sizeof(constant->subconstants.front()), "ID size is not consistent."); + *constituents = reinterpret_cast(constant->subconstants.data()); + *count = constant->subconstants.size(); +} + +spvc_type_id spvc_constant_get_type(spvc_constant constant) +{ + return constant->constant_type; +} + +spvc_bool spvc_compiler_get_binary_offset_for_decoration(spvc_compiler compiler, spvc_variable_id id, + SpvDecoration decoration, + unsigned *word_offset) +{ + uint32_t off = 0; + bool ret = compiler->compiler->get_binary_offset_for_decoration(id, static_cast(decoration), off); + if (ret) + { + *word_offset = off; + return SPVC_TRUE; + } + else + return SPVC_FALSE; +} + +spvc_bool spvc_compiler_buffer_is_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id) +{ + return compiler->compiler->buffer_is_hlsl_counter_buffer(id) ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_compiler_buffer_get_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id, + spvc_variable_id *counter_id) +{ + uint32_t buffer; + bool ret = compiler->compiler->buffer_get_hlsl_counter_buffer(id, buffer); + if (ret) + { + *counter_id = buffer; + return SPVC_TRUE; + } + else + return SPVC_FALSE; +} + +spvc_result spvc_compiler_get_declared_capabilities(spvc_compiler compiler, const SpvCapability **capabilities, + size_t *num_capabilities) +{ + auto &caps = compiler->compiler->get_declared_capabilities(); + static_assert(sizeof(SpvCapability) == sizeof(spv::Capability), "Enum size mismatch."); + *capabilities = reinterpret_cast(caps.data()); + *num_capabilities = caps.size(); + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const char ***extensions, + size_t *num_extensions) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto &exts = compiler->compiler->get_declared_extensions(); + SmallVector duped; + duped.reserve(exts.size()); + for (auto &ext : exts) + duped.push_back(compiler->context->allocate_name(ext)); + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(duped); + *extensions = ptr->buffer.data(); + *num_extensions = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +const char *spvc_compiler_get_remapped_declared_block_name(spvc_compiler compiler, spvc_variable_id id) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto name = compiler->compiler->get_remapped_declared_block_name(id); + return compiler->context->allocate_name(name); + } + SPVC_END_SAFE_SCOPE(compiler->context, nullptr) +} + +spvc_result spvc_compiler_get_buffer_block_decorations(spvc_compiler compiler, spvc_variable_id id, + const SpvDecoration **decorations, size_t *num_decorations) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto flags = compiler->compiler->get_buffer_block_flags(id); + auto bitset = spvc_allocate>(); + + flags.for_each_bit([&](uint32_t bit) { bitset->buffer.push_back(static_cast(bit)); }); + + *decorations = bitset->buffer.data(); + *num_decorations = bitset->buffer.size(); + compiler->context->allocations.push_back(std::move(bitset)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +unsigned spvc_msl_get_aux_buffer_struct_version(void) +{ + return SPVC_MSL_AUX_BUFFER_STRUCT_VERSION; +} + +void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr) +{ +#if SPIRV_CROSS_C_API_MSL + // Crude, but works. + MSLShaderInput attr_default; + attr->location = attr_default.location; + attr->format = static_cast(attr_default.format); + attr->builtin = static_cast(attr_default.builtin); +#else + memset(attr, 0, sizeof(*attr)); +#endif +} + +void spvc_msl_shader_input_init(spvc_msl_shader_input *input) +{ +#if SPIRV_CROSS_C_API_MSL + MSLShaderInput input_default; + input->location = input_default.location; + input->format = static_cast(input_default.format); + input->builtin = static_cast(input_default.builtin); + input->vecsize = input_default.vecsize; +#else + memset(input, 0, sizeof(*input)); +#endif +} + +void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_MSL + MSLResourceBinding binding_default; + binding->desc_set = binding_default.desc_set; + binding->binding = binding_default.binding; + binding->msl_buffer = binding_default.msl_buffer; + binding->msl_texture = binding_default.msl_texture; + binding->msl_sampler = binding_default.msl_sampler; + binding->stage = static_cast(binding_default.stage); +#else + memset(binding, 0, sizeof(*binding)); +#endif +} + +void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_HLSL + HLSLResourceBinding binding_default; + binding->desc_set = binding_default.desc_set; + binding->binding = binding_default.binding; + binding->cbv.register_binding = binding_default.cbv.register_binding; + binding->cbv.register_space = binding_default.cbv.register_space; + binding->srv.register_binding = binding_default.srv.register_binding; + binding->srv.register_space = binding_default.srv.register_space; + binding->uav.register_binding = binding_default.uav.register_binding; + binding->uav.register_space = binding_default.uav.register_space; + binding->sampler.register_binding = binding_default.sampler.register_binding; + binding->sampler.register_space = binding_default.sampler.register_space; + binding->stage = static_cast(binding_default.stage); +#else + memset(binding, 0, sizeof(*binding)); +#endif +} + +void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler) +{ +#if SPIRV_CROSS_C_API_MSL + MSLConstexprSampler defaults; + sampler->anisotropy_enable = defaults.anisotropy_enable ? SPVC_TRUE : SPVC_FALSE; + sampler->border_color = static_cast(defaults.border_color); + sampler->compare_enable = defaults.compare_enable ? SPVC_TRUE : SPVC_FALSE; + sampler->coord = static_cast(defaults.coord); + sampler->compare_func = static_cast(defaults.compare_func); + sampler->lod_clamp_enable = defaults.lod_clamp_enable ? SPVC_TRUE : SPVC_FALSE; + sampler->lod_clamp_max = defaults.lod_clamp_max; + sampler->lod_clamp_min = defaults.lod_clamp_min; + sampler->mag_filter = static_cast(defaults.mag_filter); + sampler->min_filter = static_cast(defaults.min_filter); + sampler->mip_filter = static_cast(defaults.mip_filter); + sampler->max_anisotropy = defaults.max_anisotropy; + sampler->s_address = static_cast(defaults.s_address); + sampler->t_address = static_cast(defaults.t_address); + sampler->r_address = static_cast(defaults.r_address); +#else + memset(sampler, 0, sizeof(*sampler)); +#endif +} + +void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycbcr_conversion *conv) +{ +#if SPIRV_CROSS_C_API_MSL + MSLConstexprSampler defaults; + conv->planes = defaults.planes; + conv->resolution = static_cast(defaults.resolution); + conv->chroma_filter = static_cast(defaults.chroma_filter); + conv->x_chroma_offset = static_cast(defaults.x_chroma_offset); + conv->y_chroma_offset = static_cast(defaults.y_chroma_offset); + for (int i = 0; i < 4; i++) + conv->swizzle[i] = static_cast(defaults.swizzle[i]); + conv->ycbcr_model = static_cast(defaults.ycbcr_model); + conv->ycbcr_range = static_cast(defaults.ycbcr_range); +#else + memset(conv, 0, sizeof(*conv)); +#endif +} + +unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler) +{ + return compiler->compiler->get_current_id_bound(); +} + +void spvc_get_version(unsigned *major, unsigned *minor, unsigned *patch) +{ + *major = SPVC_C_API_VERSION_MAJOR; + *minor = SPVC_C_API_VERSION_MINOR; + *patch = SPVC_C_API_VERSION_PATCH; +} + +const char *spvc_get_commit_revision_and_timestamp(void) +{ +#ifdef HAVE_SPIRV_CROSS_GIT_VERSION + return SPIRV_CROSS_GIT_REVISION; +#else + return ""; +#endif +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/third_party/spirv-cross/spirv_cross_c.h b/third_party/spirv-cross/spirv_cross_c.h new file mode 100644 index 0000000..7ccec0a --- /dev/null +++ b/third_party/spirv-cross/spirv_cross_c.h @@ -0,0 +1,980 @@ +/* + * Copyright 2019-2020 Hans-Kristian Arntzen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_C_API_H +#define SPIRV_CROSS_C_API_H + +#include +#include "spirv.h" + +/* + * C89-compatible wrapper for SPIRV-Cross' API. + * Documentation here is sparse unless the behavior does not map 1:1 with C++ API. + * It is recommended to look at the canonical C++ API for more detailed information. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Bumped if ABI or API breaks backwards compatibility. */ +#define SPVC_C_API_VERSION_MAJOR 0 +/* Bumped if APIs or enumerations are added in a backwards compatible way. */ +#define SPVC_C_API_VERSION_MINOR 42 +/* Bumped if internal implementation details change. */ +#define SPVC_C_API_VERSION_PATCH 0 + +#if !defined(SPVC_PUBLIC_API) +#if defined(SPVC_EXPORT_SYMBOLS) +/* Exports symbols. Standard C calling convention is used. */ +#if defined(__GNUC__) +#define SPVC_PUBLIC_API __attribute__((visibility("default"))) +#elif defined(_MSC_VER) +#define SPVC_PUBLIC_API __declspec(dllexport) +#else +#define SPVC_PUBLIC_API +#endif +#else +#define SPVC_PUBLIC_API +#endif +#endif + +/* + * Gets the SPVC_C_API_VERSION_* used to build this library. + * Can be used to check for ABI mismatch if so-versioning did not catch it. + */ +SPVC_PUBLIC_API void spvc_get_version(unsigned *major, unsigned *minor, unsigned *patch); + +/* Gets a human readable version string to identify which commit a particular binary was created from. */ +SPVC_PUBLIC_API const char *spvc_get_commit_revision_and_timestamp(void); + +/* These types are opaque to the user. */ +typedef struct spvc_context_s *spvc_context; +typedef struct spvc_parsed_ir_s *spvc_parsed_ir; +typedef struct spvc_compiler_s *spvc_compiler; +typedef struct spvc_compiler_options_s *spvc_compiler_options; +typedef struct spvc_resources_s *spvc_resources; +struct spvc_type_s; +typedef const struct spvc_type_s *spvc_type; +typedef struct spvc_constant_s *spvc_constant; +struct spvc_set_s; +typedef const struct spvc_set_s *spvc_set; + +/* + * Shallow typedefs. All SPIR-V IDs are plain 32-bit numbers, but this helps communicate which data is used. + * Maps to a SPIRType. + */ +typedef SpvId spvc_type_id; +/* Maps to a SPIRVariable. */ +typedef SpvId spvc_variable_id; +/* Maps to a SPIRConstant. */ +typedef SpvId spvc_constant_id; + +/* See C++ API. */ +typedef struct spvc_reflected_resource +{ + spvc_variable_id id; + spvc_type_id base_type_id; + spvc_type_id type_id; + const char *name; +} spvc_reflected_resource; + +/* See C++ API. */ +typedef struct spvc_entry_point +{ + SpvExecutionModel execution_model; + const char *name; +} spvc_entry_point; + +/* See C++ API. */ +typedef struct spvc_combined_image_sampler +{ + spvc_variable_id combined_id; + spvc_variable_id image_id; + spvc_variable_id sampler_id; +} spvc_combined_image_sampler; + +/* See C++ API. */ +typedef struct spvc_specialization_constant +{ + spvc_constant_id id; + unsigned constant_id; +} spvc_specialization_constant; + +/* See C++ API. */ +typedef struct spvc_buffer_range +{ + unsigned index; + size_t offset; + size_t range; +} spvc_buffer_range; + +/* See C++ API. */ +typedef struct spvc_hlsl_root_constants +{ + unsigned start; + unsigned end; + unsigned binding; + unsigned space; +} spvc_hlsl_root_constants; + +/* See C++ API. */ +typedef struct spvc_hlsl_vertex_attribute_remap +{ + unsigned location; + const char *semantic; +} spvc_hlsl_vertex_attribute_remap; + +/* + * Be compatible with non-C99 compilers, which do not have stdbool. + * Only recent MSVC compilers supports this for example, and ideally SPIRV-Cross should be linkable + * from a wide range of compilers in its C wrapper. + */ +typedef unsigned char spvc_bool; +#define SPVC_TRUE ((spvc_bool)1) +#define SPVC_FALSE ((spvc_bool)0) + +typedef enum spvc_result +{ + /* Success. */ + SPVC_SUCCESS = 0, + + /* The SPIR-V is invalid. Should have been caught by validation ideally. */ + SPVC_ERROR_INVALID_SPIRV = -1, + + /* The SPIR-V might be valid or invalid, but SPIRV-Cross currently cannot correctly translate this to your target language. */ + SPVC_ERROR_UNSUPPORTED_SPIRV = -2, + + /* If for some reason we hit this, new or malloc failed. */ + SPVC_ERROR_OUT_OF_MEMORY = -3, + + /* Invalid API argument. */ + SPVC_ERROR_INVALID_ARGUMENT = -4, + + SPVC_ERROR_INT_MAX = 0x7fffffff +} spvc_result; + +typedef enum spvc_capture_mode +{ + /* The Parsed IR payload will be copied, and the handle can be reused to create other compiler instances. */ + SPVC_CAPTURE_MODE_COPY = 0, + + /* + * The payload will now be owned by the compiler. + * parsed_ir should now be considered a dead blob and must not be used further. + * This is optimal for performance and should be the go-to option. + */ + SPVC_CAPTURE_MODE_TAKE_OWNERSHIP = 1, + + SPVC_CAPTURE_MODE_INT_MAX = 0x7fffffff +} spvc_capture_mode; + +typedef enum spvc_backend +{ + /* This backend can only perform reflection, no compiler options are supported. Maps to spirv_cross::Compiler. */ + SPVC_BACKEND_NONE = 0, + SPVC_BACKEND_GLSL = 1, /* spirv_cross::CompilerGLSL */ + SPVC_BACKEND_HLSL = 2, /* CompilerHLSL */ + SPVC_BACKEND_MSL = 3, /* CompilerMSL */ + SPVC_BACKEND_CPP = 4, /* CompilerCPP */ + SPVC_BACKEND_JSON = 5, /* CompilerReflection w/ JSON backend */ + SPVC_BACKEND_INT_MAX = 0x7fffffff +} spvc_backend; + +/* Maps to C++ API. */ +typedef enum spvc_resource_type +{ + SPVC_RESOURCE_TYPE_UNKNOWN = 0, + SPVC_RESOURCE_TYPE_UNIFORM_BUFFER = 1, + SPVC_RESOURCE_TYPE_STORAGE_BUFFER = 2, + SPVC_RESOURCE_TYPE_STAGE_INPUT = 3, + SPVC_RESOURCE_TYPE_STAGE_OUTPUT = 4, + SPVC_RESOURCE_TYPE_SUBPASS_INPUT = 5, + SPVC_RESOURCE_TYPE_STORAGE_IMAGE = 6, + SPVC_RESOURCE_TYPE_SAMPLED_IMAGE = 7, + SPVC_RESOURCE_TYPE_ATOMIC_COUNTER = 8, + SPVC_RESOURCE_TYPE_PUSH_CONSTANT = 9, + SPVC_RESOURCE_TYPE_SEPARATE_IMAGE = 10, + SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS = 11, + SPVC_RESOURCE_TYPE_ACCELERATION_STRUCTURE = 12, + SPVC_RESOURCE_TYPE_RAY_QUERY = 13, + SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff +} spvc_resource_type; + +/* Maps to spirv_cross::SPIRType::BaseType. */ +typedef enum spvc_basetype +{ + SPVC_BASETYPE_UNKNOWN = 0, + SPVC_BASETYPE_VOID = 1, + SPVC_BASETYPE_BOOLEAN = 2, + SPVC_BASETYPE_INT8 = 3, + SPVC_BASETYPE_UINT8 = 4, + SPVC_BASETYPE_INT16 = 5, + SPVC_BASETYPE_UINT16 = 6, + SPVC_BASETYPE_INT32 = 7, + SPVC_BASETYPE_UINT32 = 8, + SPVC_BASETYPE_INT64 = 9, + SPVC_BASETYPE_UINT64 = 10, + SPVC_BASETYPE_ATOMIC_COUNTER = 11, + SPVC_BASETYPE_FP16 = 12, + SPVC_BASETYPE_FP32 = 13, + SPVC_BASETYPE_FP64 = 14, + SPVC_BASETYPE_STRUCT = 15, + SPVC_BASETYPE_IMAGE = 16, + SPVC_BASETYPE_SAMPLED_IMAGE = 17, + SPVC_BASETYPE_SAMPLER = 18, + SPVC_BASETYPE_ACCELERATION_STRUCTURE = 19, + + SPVC_BASETYPE_INT_MAX = 0x7fffffff +} spvc_basetype; + +#define SPVC_COMPILER_OPTION_COMMON_BIT 0x1000000 +#define SPVC_COMPILER_OPTION_GLSL_BIT 0x2000000 +#define SPVC_COMPILER_OPTION_HLSL_BIT 0x4000000 +#define SPVC_COMPILER_OPTION_MSL_BIT 0x8000000 +#define SPVC_COMPILER_OPTION_LANG_BITS 0x0f000000 +#define SPVC_COMPILER_OPTION_ENUM_BITS 0xffffff + +#define SPVC_MAKE_MSL_VERSION(major, minor, patch) ((major) * 10000 + (minor) * 100 + (patch)) + +/* Maps to C++ API. */ +typedef enum spvc_msl_platform +{ + SPVC_MSL_PLATFORM_IOS = 0, + SPVC_MSL_PLATFORM_MACOS = 1, + SPVC_MSL_PLATFORM_MAX_INT = 0x7fffffff +} spvc_msl_platform; + +/* Maps to C++ API. */ +typedef enum spvc_msl_index_type +{ + SPVC_MSL_INDEX_TYPE_NONE = 0, + SPVC_MSL_INDEX_TYPE_UINT16 = 1, + SPVC_MSL_INDEX_TYPE_UINT32 = 2, + SPVC_MSL_INDEX_TYPE_MAX_INT = 0x7fffffff +} spvc_msl_index_type; + +/* Maps to C++ API. */ +typedef enum spvc_msl_shader_input_format +{ + SPVC_MSL_SHADER_INPUT_FORMAT_OTHER = 0, + SPVC_MSL_SHADER_INPUT_FORMAT_UINT8 = 1, + SPVC_MSL_SHADER_INPUT_FORMAT_UINT16 = 2, + SPVC_MSL_SHADER_INPUT_FORMAT_ANY16 = 3, + SPVC_MSL_SHADER_INPUT_FORMAT_ANY32 = 4, + + /* Deprecated names. */ + SPVC_MSL_VERTEX_FORMAT_OTHER = SPVC_MSL_SHADER_INPUT_FORMAT_OTHER, + SPVC_MSL_VERTEX_FORMAT_UINT8 = SPVC_MSL_SHADER_INPUT_FORMAT_UINT8, + SPVC_MSL_VERTEX_FORMAT_UINT16 = SPVC_MSL_SHADER_INPUT_FORMAT_UINT16, + + SPVC_MSL_SHADER_INPUT_FORMAT_INT_MAX = 0x7fffffff +} spvc_msl_shader_input_format, spvc_msl_vertex_format; + +/* Maps to C++ API. Deprecated; use spvc_msl_shader_input. */ +typedef struct spvc_msl_vertex_attribute +{ + unsigned location; + + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_buffer; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_offset; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_stride; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + spvc_bool per_instance; + + spvc_msl_vertex_format format; + SpvBuiltIn builtin; +} spvc_msl_vertex_attribute; + +/* + * Initializes the vertex attribute struct. + */ +SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr); + +/* Maps to C++ API. */ +typedef struct spvc_msl_shader_input +{ + unsigned location; + spvc_msl_vertex_format format; + SpvBuiltIn builtin; + unsigned vecsize; +} spvc_msl_shader_input; + +/* + * Initializes the shader input struct. + */ +SPVC_PUBLIC_API void spvc_msl_shader_input_init(spvc_msl_shader_input *input); + +/* Maps to C++ API. */ +typedef struct spvc_msl_resource_binding +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + unsigned msl_buffer; + unsigned msl_texture; + unsigned msl_sampler; +} spvc_msl_resource_binding; + +/* + * Initializes the resource binding struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding); + +#define SPVC_MSL_PUSH_CONSTANT_DESC_SET (~(0u)) +#define SPVC_MSL_PUSH_CONSTANT_BINDING (0) +#define SPVC_MSL_SWIZZLE_BUFFER_BINDING (~(1u)) +#define SPVC_MSL_BUFFER_SIZE_BUFFER_BINDING (~(2u)) +#define SPVC_MSL_ARGUMENT_BUFFER_BINDING (~(3u)) + +/* Obsolete. Sticks around for backwards compatibility. */ +#define SPVC_MSL_AUX_BUFFER_STRUCT_VERSION 1 + +/* Runtime check for incompatibility. Obsolete. */ +SPVC_PUBLIC_API unsigned spvc_msl_get_aux_buffer_struct_version(void); + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_coord +{ + SPVC_MSL_SAMPLER_COORD_NORMALIZED = 0, + SPVC_MSL_SAMPLER_COORD_PIXEL = 1, + SPVC_MSL_SAMPLER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_coord; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_filter +{ + SPVC_MSL_SAMPLER_FILTER_NEAREST = 0, + SPVC_MSL_SAMPLER_FILTER_LINEAR = 1, + SPVC_MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_filter; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_mip_filter +{ + SPVC_MSL_SAMPLER_MIP_FILTER_NONE = 0, + SPVC_MSL_SAMPLER_MIP_FILTER_NEAREST = 1, + SPVC_MSL_SAMPLER_MIP_FILTER_LINEAR = 2, + SPVC_MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_mip_filter; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_address +{ + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0, + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1, + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2, + SPVC_MSL_SAMPLER_ADDRESS_REPEAT = 3, + SPVC_MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4, + SPVC_MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff +} spvc_msl_sampler_address; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_compare_func +{ + SPVC_MSL_SAMPLER_COMPARE_FUNC_NEVER = 0, + SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS = 1, + SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2, + SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER = 3, + SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4, + SPVC_MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5, + SPVC_MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6, + SPVC_MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7, + SPVC_MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff +} spvc_msl_sampler_compare_func; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_border_color +{ + SPVC_MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0, + SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1, + SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2, + SPVC_MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff +} spvc_msl_sampler_border_color; + +/* Maps to C++ API. */ +typedef enum spvc_msl_format_resolution +{ + SPVC_MSL_FORMAT_RESOLUTION_444 = 0, + SPVC_MSL_FORMAT_RESOLUTION_422, + SPVC_MSL_FORMAT_RESOLUTION_420, + SPVC_MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff +} spvc_msl_format_resolution; + +/* Maps to C++ API. */ +typedef enum spvc_msl_chroma_location +{ + SPVC_MSL_CHROMA_LOCATION_COSITED_EVEN = 0, + SPVC_MSL_CHROMA_LOCATION_MIDPOINT, + SPVC_MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff +} spvc_msl_chroma_location; + +/* Maps to C++ API. */ +typedef enum spvc_msl_component_swizzle +{ + SPVC_MSL_COMPONENT_SWIZZLE_IDENTITY = 0, + SPVC_MSL_COMPONENT_SWIZZLE_ZERO, + SPVC_MSL_COMPONENT_SWIZZLE_ONE, + SPVC_MSL_COMPONENT_SWIZZLE_R, + SPVC_MSL_COMPONENT_SWIZZLE_G, + SPVC_MSL_COMPONENT_SWIZZLE_B, + SPVC_MSL_COMPONENT_SWIZZLE_A, + SPVC_MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff +} spvc_msl_component_swizzle; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_ycbcr_model_conversion +{ + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff +} spvc_msl_sampler_ycbcr_model_conversion; + +/* Maps to C+ API. */ +typedef enum spvc_msl_sampler_ycbcr_range +{ + SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, + SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW, + SPVC_MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff +} spvc_msl_sampler_ycbcr_range; + +/* Maps to C++ API. */ +typedef struct spvc_msl_constexpr_sampler +{ + spvc_msl_sampler_coord coord; + spvc_msl_sampler_filter min_filter; + spvc_msl_sampler_filter mag_filter; + spvc_msl_sampler_mip_filter mip_filter; + spvc_msl_sampler_address s_address; + spvc_msl_sampler_address t_address; + spvc_msl_sampler_address r_address; + spvc_msl_sampler_compare_func compare_func; + spvc_msl_sampler_border_color border_color; + float lod_clamp_min; + float lod_clamp_max; + int max_anisotropy; + + spvc_bool compare_enable; + spvc_bool lod_clamp_enable; + spvc_bool anisotropy_enable; +} spvc_msl_constexpr_sampler; + +/* + * Initializes the constexpr sampler struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler); + +/* Maps to the sampler Y'CbCr conversion-related portions of MSLConstexprSampler. See C++ API for defaults and details. */ +typedef struct spvc_msl_sampler_ycbcr_conversion +{ + unsigned planes; + spvc_msl_format_resolution resolution; + spvc_msl_sampler_filter chroma_filter; + spvc_msl_chroma_location x_chroma_offset; + spvc_msl_chroma_location y_chroma_offset; + spvc_msl_component_swizzle swizzle[4]; + spvc_msl_sampler_ycbcr_model_conversion ycbcr_model; + spvc_msl_sampler_ycbcr_range ycbcr_range; + unsigned bpc; +} spvc_msl_sampler_ycbcr_conversion; + +/* + * Initializes the constexpr sampler struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycbcr_conversion *conv); + +/* Maps to C++ API. */ +typedef enum spvc_hlsl_binding_flag_bits +{ + SPVC_HLSL_BINDING_AUTO_NONE_BIT = 0, + SPVC_HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, + SPVC_HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, + SPVC_HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, + SPVC_HLSL_BINDING_AUTO_UAV_BIT = 1 << 3, + SPVC_HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4, + SPVC_HLSL_BINDING_AUTO_ALL = 0x7fffffff +} spvc_hlsl_binding_flag_bits; +typedef unsigned spvc_hlsl_binding_flags; + +#define SPVC_HLSL_PUSH_CONSTANT_DESC_SET (~(0u)) +#define SPVC_HLSL_PUSH_CONSTANT_BINDING (0) + +/* Maps to C++ API. */ +typedef struct spvc_hlsl_resource_binding_mapping +{ + unsigned register_space; + unsigned register_binding; +} spvc_hlsl_resource_binding_mapping; + +typedef struct spvc_hlsl_resource_binding +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + + spvc_hlsl_resource_binding_mapping cbv, uav, srv, sampler; +} spvc_hlsl_resource_binding; + +/* + * Initializes the resource binding struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding); + +/* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */ +typedef enum spvc_compiler_option +{ + SPVC_COMPILER_OPTION_UNKNOWN = 0, + + SPVC_COMPILER_OPTION_FORCE_TEMPORARY = 1 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FLATTEN_MULTIDIMENSIONAL_ARRAYS = 2 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION = 3 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FLIP_VERTEX_Y = 4 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE = 5 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS = 6 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION = 7 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_VERSION = 8 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES = 9 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS = 10 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP = 11 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP = 12 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL = 13 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT = 14 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT = 15 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE = 16 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_VERSION = 17 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH = 18 | SPVC_COMPILER_OPTION_MSL_BIT, + + /* Obsolete, use SWIZZLE_BUFFER_INDEX instead. */ + SPVC_COMPILER_OPTION_MSL_AUX_BUFFER_INDEX = 19 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SWIZZLE_BUFFER_INDEX = 19 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX = 20 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX = 21 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX = 22 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX = 23 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX = 24 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN = 25 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION = 26 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER = 27 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES = 28 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS = 29 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT = 30 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_PLATFORM = 31 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS = 32 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_EMIT_PUSH_CONSTANT_AS_UNIFORM_BUFFER = 33 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_TEXTURE_BUFFER_NATIVE = 34 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS = 35 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX = 36 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_EMIT_LINE_DIRECTIVES = 37 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTIVIEW = 38 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VIEW_MASK_BUFFER_INDEX = 39 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DEVICE_INDEX = 40 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VIEW_INDEX_FROM_DEVICE_INDEX = 41 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DISPATCH_BASE = 42 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DYNAMIC_OFFSETS_BUFFER_INDEX = 43 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TEXTURE_1D_AS_2D = 44 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_BASE_INDEX_ZERO = 45 | SPVC_COMPILER_OPTION_MSL_BIT, + + /* Obsolete. Use MSL_FRAMEBUFFER_FETCH_SUBPASS instead. */ + SPVC_COMPILER_OPTION_MSL_IOS_FRAMEBUFFER_FETCH_SUBPASS = 46 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS = 46 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH = 47 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY = 48 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING = 49 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES = 50 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS = 51 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_ENABLE_STORAGE_IMAGE_QUALIFIER_DEDUCTION = 52 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_HLSL_FORCE_STORAGE_BUFFER_AS_UAV = 53 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_FORCE_ZERO_INITIALIZED_VARIABLES = 54 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV = 55 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK = 56 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN = 57 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN = 58 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_CLIP_DISTANCE_USER_VARYING = 59 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_ENABLE_16BIT_TYPES = 60 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTI_PATCH_WORKGROUP = 61 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_BUFFER_INDEX = 62 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INDEX_BUFFER_INDEX = 63 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VERTEX_FOR_TESSELLATION = 64 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VERTEX_INDEX_TYPE = 65 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS = 66 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTIVIEW_LAYERED_RENDERING = 67 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ARRAYED_SUBPASS_INPUT = 68 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_R32UI_LINEAR_TEXTURE_ALIGNMENT = 69 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_R32UI_ALIGNMENT_CONSTANT_ID = 70 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS = 71 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff +} spvc_compiler_option; + +/* + * Context is the highest-level API construct. + * The context owns all memory allocations made by its child object hierarchy, including various non-opaque structs and strings. + * This means that the API user only has to care about one "destroy" call ever when using the C API. + * All pointers handed out by the APIs are only valid as long as the context + * is alive and spvc_context_release_allocations has not been called. + */ +SPVC_PUBLIC_API spvc_result spvc_context_create(spvc_context *context); + +/* Frees all memory allocations and objects associated with the context and its child objects. */ +SPVC_PUBLIC_API void spvc_context_destroy(spvc_context context); + +/* Frees all memory allocations and objects associated with the context and its child objects, but keeps the context alive. */ +SPVC_PUBLIC_API void spvc_context_release_allocations(spvc_context context); + +/* Get the string for the last error which was logged. */ +SPVC_PUBLIC_API const char *spvc_context_get_last_error_string(spvc_context context); + +/* Get notified in a callback when an error triggers. Useful for debugging. */ +typedef void (*spvc_error_callback)(void *userdata, const char *error); +SPVC_PUBLIC_API void spvc_context_set_error_callback(spvc_context context, spvc_error_callback cb, void *userdata); + +/* SPIR-V parsing interface. Maps to Parser which then creates a ParsedIR, and that IR is extracted into the handle. */ +SPVC_PUBLIC_API spvc_result spvc_context_parse_spirv(spvc_context context, const SpvId *spirv, size_t word_count, + spvc_parsed_ir *parsed_ir); + +/* + * Create a compiler backend. Capture mode controls if we construct by copy or move semantics. + * It is always recommended to use SPVC_CAPTURE_MODE_TAKE_OWNERSHIP if you only intend to cross-compile the IR once. + */ +SPVC_PUBLIC_API spvc_result spvc_context_create_compiler(spvc_context context, spvc_backend backend, + spvc_parsed_ir parsed_ir, spvc_capture_mode mode, + spvc_compiler *compiler); + +/* Maps directly to C++ API. */ +SPVC_PUBLIC_API unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler); + +/* Create compiler options, which will initialize defaults. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_create_compiler_options(spvc_compiler compiler, + spvc_compiler_options *options); +/* Override options. Will return error if e.g. MSL options are used for the HLSL backend, etc. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_bool(spvc_compiler_options options, + spvc_compiler_option option, spvc_bool value); +SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, + spvc_compiler_option option, unsigned value); +/* Set compiler options. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_install_compiler_options(spvc_compiler compiler, + spvc_compiler_options options); + +/* Compile IR into a string. *source is owned by the context, and caller must not free it themselves. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source); + +/* Maps to C++ API. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line); +SPVC_PUBLIC_API spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *ext); +SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id); + +/* + * HLSL specifics. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler, + const spvc_hlsl_root_constants *constant_info, + size_t count); +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_vertex_attribute_remap(spvc_compiler compiler, + const spvc_hlsl_vertex_attribute_remap *remap, + size_t remaps); +SPVC_PUBLIC_API spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin(spvc_compiler compiler); + +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler, + spvc_hlsl_binding_flags flags); + +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding); +SPVC_PUBLIC_API spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, + SpvExecutionModel model, + unsigned set, + unsigned binding); + +/* + * MSL specifics. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler); + +/* Obsolete. Renamed to needs_swizzle_buffer. */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_aux_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_swizzle_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_buffer_size_buffer(spvc_compiler compiler); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_output_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_patch_output_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_input_threadgroup_mem(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, + const spvc_msl_vertex_attribute *attrs); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler, + const spvc_msl_resource_binding *binding); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, + const spvc_msl_shader_input *input); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address); + +/* Obsolete, use is_shader_input_used. */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_shader_input_used(spvc_compiler compiler, unsigned location); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler, + SpvExecutionModel model, + unsigned set, + unsigned binding); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_ycbcr(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler, const spvc_msl_sampler_ycbcr_conversion *conv); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler, const spvc_msl_sampler_ycbcr_conversion *conv); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, unsigned components); + +SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding_secondary(spvc_compiler compiler, spvc_variable_id id); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigned desc_set, unsigned binding, unsigned index); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_combined_sampler_suffix(spvc_compiler compiler, const char *suffix); +SPVC_PUBLIC_API const char *spvc_compiler_msl_get_combined_sampler_suffix(spvc_compiler compiler); + +/* + * Reflect resources. + * Maps almost 1:1 to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_active_interface_variables(spvc_compiler compiler, spvc_set *set); +SPVC_PUBLIC_API spvc_result spvc_compiler_set_enabled_interface_variables(spvc_compiler compiler, spvc_set set); +SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources(spvc_compiler compiler, spvc_resources *resources); +SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources_for_active_variables(spvc_compiler compiler, + spvc_resources *resources, + spvc_set active); +SPVC_PUBLIC_API spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources, spvc_resource_type type, + const spvc_reflected_resource **resource_list, + size_t *resource_size); + +/* + * Decorations. + * Maps to C++ API. + */ +SPVC_PUBLIC_API void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, + unsigned argument); +SPVC_PUBLIC_API void spvc_compiler_set_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_name(spvc_compiler compiler, SpvId id, const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration, unsigned argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_decoration_string(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_unset_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API void spvc_compiler_unset_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_has_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API spvc_bool spvc_compiler_has_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_name(spvc_compiler compiler, SpvId id); +SPVC_PUBLIC_API unsigned spvc_compiler_get_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_decoration_string(spvc_compiler compiler, SpvId id, + SpvDecoration decoration); +SPVC_PUBLIC_API unsigned spvc_compiler_get_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_member_decoration_string(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index); + +/* + * Entry points. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler, + const spvc_entry_point **entry_points, + size_t *num_entry_points); +SPVC_PUBLIC_API spvc_result spvc_compiler_set_entry_point(spvc_compiler compiler, const char *name, + SpvExecutionModel model); +SPVC_PUBLIC_API spvc_result spvc_compiler_rename_entry_point(spvc_compiler compiler, const char *old_name, + const char *new_name, SpvExecutionModel model); +SPVC_PUBLIC_API const char *spvc_compiler_get_cleansed_entry_point_name(spvc_compiler compiler, const char *name, + SpvExecutionModel model); +SPVC_PUBLIC_API void spvc_compiler_set_execution_mode(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API void spvc_compiler_unset_execution_mode(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API void spvc_compiler_set_execution_mode_with_arguments(spvc_compiler compiler, SpvExecutionMode mode, + unsigned arg0, unsigned arg1, unsigned arg2); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_execution_modes(spvc_compiler compiler, const SpvExecutionMode **modes, + size_t *num_modes); +SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler, + SpvExecutionMode mode, unsigned index); +SPVC_PUBLIC_API SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler); + +/* + * Type query interface. + * Maps to C++ API, except it's read-only. + */ +SPVC_PUBLIC_API spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id); + +/* Pulls out SPIRType::self. This effectively gives the type ID without array or pointer qualifiers. + * This is necessary when reflecting decoration/name information on members of a struct, + * which are placed in the base type, not the qualified type. + * This is similar to spvc_reflected_resource::base_type_id. */ +SPVC_PUBLIC_API spvc_type_id spvc_type_get_base_type_id(spvc_type type); + +SPVC_PUBLIC_API spvc_basetype spvc_type_get_basetype(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_bit_width(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_vector_size(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_columns(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_num_array_dimensions(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_array_dimension_is_literal(spvc_type type, unsigned dimension); +SPVC_PUBLIC_API SpvId spvc_type_get_array_dimension(spvc_type type, unsigned dimension); +SPVC_PUBLIC_API unsigned spvc_type_get_num_member_types(spvc_type type); +SPVC_PUBLIC_API spvc_type_id spvc_type_get_member_type(spvc_type type, unsigned index); +SPVC_PUBLIC_API SpvStorageClass spvc_type_get_storage_class(spvc_type type); + +/* Image type query. */ +SPVC_PUBLIC_API spvc_type_id spvc_type_get_image_sampled_type(spvc_type type); +SPVC_PUBLIC_API SpvDim spvc_type_get_image_dimension(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_depth(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_arrayed(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_multisampled(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_storage(spvc_type type); +SPVC_PUBLIC_API SpvImageFormat spvc_type_get_image_storage_format(spvc_type type); +SPVC_PUBLIC_API SpvAccessQualifier spvc_type_get_image_access_qualifier(spvc_type type); + +/* + * Buffer layout query. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size(spvc_compiler compiler, spvc_type struct_type, size_t *size); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size_runtime_array(spvc_compiler compiler, + spvc_type struct_type, size_t array_size, size_t *size); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_member_size(spvc_compiler compiler, spvc_type type, unsigned index, size_t *size); + +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_offset(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *offset); +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_array_stride(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *stride); +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_matrix_stride(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *stride); + +/* + * Workaround helper functions. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_build_dummy_sampler_for_combined_images(spvc_compiler compiler, spvc_variable_id *id); +SPVC_PUBLIC_API spvc_result spvc_compiler_build_combined_image_samplers(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler, + const spvc_combined_image_sampler **samplers, + size_t *num_samplers); + +/* + * Constants + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler, + const spvc_specialization_constant **constants, + size_t *num_constants); +SPVC_PUBLIC_API spvc_constant spvc_compiler_get_constant_handle(spvc_compiler compiler, + spvc_constant_id id); + +SPVC_PUBLIC_API spvc_constant_id spvc_compiler_get_work_group_size_specialization_constants(spvc_compiler compiler, + spvc_specialization_constant *x, + spvc_specialization_constant *y, + spvc_specialization_constant *z); + +/* + * Buffer ranges + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_active_buffer_ranges(spvc_compiler compiler, + spvc_variable_id id, + const spvc_buffer_range **ranges, + size_t *num_ranges); + +/* + * No stdint.h until C99, sigh :( + * For smaller types, the result is sign or zero-extended as appropriate. + * Maps to C++ API. + * TODO: The SPIRConstant query interface and modification interface is not quite complete. + */ +SPVC_PUBLIC_API float spvc_constant_get_scalar_fp16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API float spvc_constant_get_scalar_fp32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API double spvc_constant_get_scalar_fp64(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u8(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i8(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API void spvc_constant_get_subconstants(spvc_constant constant, const spvc_constant_id **constituents, size_t *count); +SPVC_PUBLIC_API spvc_type_id spvc_constant_get_type(spvc_constant constant); + +/* + * Misc reflection + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_get_binary_offset_for_decoration(spvc_compiler compiler, + spvc_variable_id id, + SpvDecoration decoration, + unsigned *word_offset); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_is_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_get_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id, + spvc_variable_id *counter_id); + +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_capabilities(spvc_compiler compiler, + const SpvCapability **capabilities, + size_t *num_capabilities); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const char ***extensions, + size_t *num_extensions); + +SPVC_PUBLIC_API const char *spvc_compiler_get_remapped_declared_block_name(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_buffer_block_decorations(spvc_compiler compiler, spvc_variable_id id, + const SpvDecoration **decorations, + size_t *num_decorations); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/third_party/spirv-cross/spirv_cross_containers.hpp b/third_party/spirv-cross/spirv_cross_containers.hpp new file mode 100644 index 0000000..357ae62 --- /dev/null +++ b/third_party/spirv-cross/spirv_cross_containers.hpp @@ -0,0 +1,740 @@ +/* + * Copyright 2019-2020 Hans-Kristian Arntzen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_CONTAINERS_HPP +#define SPIRV_CROSS_CONTAINERS_HPP + +#include "spirv_cross_error_handling.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE +#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE +#else +#define SPIRV_CROSS_NAMESPACE spirv_cross +#endif + +namespace SPIRV_CROSS_NAMESPACE +{ +#ifndef SPIRV_CROSS_FORCE_STL_TYPES +// std::aligned_storage does not support size == 0, so roll our own. +template +class AlignedBuffer +{ +public: + T *data() + { +#if defined(_MSC_VER) && _MSC_VER < 1900 + // MSVC 2013 workarounds, sigh ... + // Only use this workaround on MSVC 2013 due to some confusion around default initialized unions. + // Spec seems to suggest the memory will be zero-initialized, which is *not* what we want. + return reinterpret_cast(u.aligned_char); +#else + return reinterpret_cast(aligned_char); +#endif + } + +private: +#if defined(_MSC_VER) && _MSC_VER < 1900 + // MSVC 2013 workarounds, sigh ... + union + { + char aligned_char[sizeof(T) * N]; + double dummy_aligner; + } u; +#else + alignas(T) char aligned_char[sizeof(T) * N]; +#endif +}; + +template +class AlignedBuffer +{ +public: + T *data() + { + return nullptr; + } +}; + +// An immutable version of SmallVector which erases type information about storage. +template +class VectorView +{ +public: + T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT + { + return ptr[i]; + } + + const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT + { + return ptr[i]; + } + + bool empty() const SPIRV_CROSS_NOEXCEPT + { + return buffer_size == 0; + } + + size_t size() const SPIRV_CROSS_NOEXCEPT + { + return buffer_size; + } + + T *data() SPIRV_CROSS_NOEXCEPT + { + return ptr; + } + + const T *data() const SPIRV_CROSS_NOEXCEPT + { + return ptr; + } + + T *begin() SPIRV_CROSS_NOEXCEPT + { + return ptr; + } + + T *end() SPIRV_CROSS_NOEXCEPT + { + return ptr + buffer_size; + } + + const T *begin() const SPIRV_CROSS_NOEXCEPT + { + return ptr; + } + + const T *end() const SPIRV_CROSS_NOEXCEPT + { + return ptr + buffer_size; + } + + T &front() SPIRV_CROSS_NOEXCEPT + { + return ptr[0]; + } + + const T &front() const SPIRV_CROSS_NOEXCEPT + { + return ptr[0]; + } + + T &back() SPIRV_CROSS_NOEXCEPT + { + return ptr[buffer_size - 1]; + } + + const T &back() const SPIRV_CROSS_NOEXCEPT + { + return ptr[buffer_size - 1]; + } + + // Makes it easier to consume SmallVector. +#if defined(_MSC_VER) && _MSC_VER < 1900 + explicit operator std::vector() const + { + // Another MSVC 2013 workaround. It does not understand lvalue/rvalue qualified operations. + return std::vector(ptr, ptr + buffer_size); + } +#else + // Makes it easier to consume SmallVector. + explicit operator std::vector() const & + { + return std::vector(ptr, ptr + buffer_size); + } + + // If we are converting as an r-value, we can pilfer our elements. + explicit operator std::vector() && + { + return std::vector(std::make_move_iterator(ptr), std::make_move_iterator(ptr + buffer_size)); + } +#endif + + // Avoid sliced copies. Base class should only be read as a reference. + VectorView(const VectorView &) = delete; + void operator=(const VectorView &) = delete; + +protected: + VectorView() = default; + T *ptr = nullptr; + size_t buffer_size = 0; +}; + +// Simple vector which supports up to N elements inline, without malloc/free. +// We use a lot of throwaway vectors all over the place which triggers allocations. +// This class only implements the subset of std::vector we need in SPIRV-Cross. +// It is *NOT* a drop-in replacement in general projects. +template +class SmallVector : public VectorView +{ +public: + SmallVector() SPIRV_CROSS_NOEXCEPT + { + this->ptr = stack_storage.data(); + buffer_capacity = N; + } + + SmallVector(const T *arg_list_begin, const T *arg_list_end) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + auto count = size_t(arg_list_end - arg_list_begin); + reserve(count); + for (size_t i = 0; i < count; i++, arg_list_begin++) + new (&this->ptr[i]) T(*arg_list_begin); + this->buffer_size = count; + } + + SmallVector(std::initializer_list init) SPIRV_CROSS_NOEXCEPT : SmallVector(init.begin(), init.end()) + { + } + + SmallVector(SmallVector &&other) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + *this = std::move(other); + } + + SmallVector &operator=(SmallVector &&other) SPIRV_CROSS_NOEXCEPT + { + clear(); + if (other.ptr != other.stack_storage.data()) + { + // Pilfer allocated pointer. + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = other.ptr; + this->buffer_size = other.buffer_size; + buffer_capacity = other.buffer_capacity; + other.ptr = nullptr; + other.buffer_size = 0; + other.buffer_capacity = 0; + } + else + { + // Need to move the stack contents individually. + reserve(other.buffer_size); + for (size_t i = 0; i < other.buffer_size; i++) + { + new (&this->ptr[i]) T(std::move(other.ptr[i])); + other.ptr[i].~T(); + } + this->buffer_size = other.buffer_size; + other.buffer_size = 0; + } + return *this; + } + + SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + *this = other; + } + + SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT + { + if (this == &other) + return *this; + + clear(); + reserve(other.buffer_size); + for (size_t i = 0; i < other.buffer_size; i++) + new (&this->ptr[i]) T(other.ptr[i]); + this->buffer_size = other.buffer_size; + return *this; + } + + explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + resize(count); + } + + ~SmallVector() + { + clear(); + if (this->ptr != stack_storage.data()) + free(this->ptr); + } + + void clear() SPIRV_CROSS_NOEXCEPT + { + for (size_t i = 0; i < this->buffer_size; i++) + this->ptr[i].~T(); + this->buffer_size = 0; + } + + void push_back(const T &t) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(t); + this->buffer_size++; + } + + void push_back(T &&t) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(std::move(t)); + this->buffer_size++; + } + + void pop_back() SPIRV_CROSS_NOEXCEPT + { + // Work around false positive warning on GCC 8.3. + // Calling pop_back on empty vector is undefined. + if (!this->empty()) + resize(this->buffer_size - 1); + } + + template + void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(std::forward(ts)...); + this->buffer_size++; + } + + void reserve(size_t count) SPIRV_CROSS_NOEXCEPT + { + if ((count > std::numeric_limits::max() / sizeof(T)) || + (count > std::numeric_limits::max() / 2)) + { + // Only way this should ever happen is with garbage input, terminate. + std::terminate(); + } + + if (count > buffer_capacity) + { + size_t target_capacity = buffer_capacity; + if (target_capacity == 0) + target_capacity = 1; + + // Weird parens works around macro issues on Windows if NOMINMAX is not used. + target_capacity = (std::max)(target_capacity, N); + + // Need to ensure there is a POT value of target capacity which is larger than count, + // otherwise this will overflow. + while (target_capacity < count) + target_capacity <<= 1u; + + T *new_buffer = + target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); + + // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. + if (!new_buffer) + std::terminate(); + + // In case for some reason two allocations both come from same stack. + if (new_buffer != this->ptr) + { + // We don't deal with types which can throw in move constructor. + for (size_t i = 0; i < this->buffer_size; i++) + { + new (&new_buffer[i]) T(std::move(this->ptr[i])); + this->ptr[i].~T(); + } + } + + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = new_buffer; + buffer_capacity = target_capacity; + } + } + + void insert(T *itr, const T *insert_begin, const T *insert_end) SPIRV_CROSS_NOEXCEPT + { + auto count = size_t(insert_end - insert_begin); + if (itr == this->end()) + { + reserve(this->buffer_size + count); + for (size_t i = 0; i < count; i++, insert_begin++) + new (&this->ptr[this->buffer_size + i]) T(*insert_begin); + this->buffer_size += count; + } + else + { + if (this->buffer_size + count > buffer_capacity) + { + auto target_capacity = this->buffer_size + count; + if (target_capacity == 0) + target_capacity = 1; + if (target_capacity < N) + target_capacity = N; + + while (target_capacity < count) + target_capacity <<= 1u; + + // Need to allocate new buffer. Move everything to a new buffer. + T *new_buffer = + target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); + + // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. + if (!new_buffer) + std::terminate(); + + // First, move elements from source buffer to new buffer. + // We don't deal with types which can throw in move constructor. + auto *target_itr = new_buffer; + auto *original_source_itr = this->begin(); + + if (new_buffer != this->ptr) + { + while (original_source_itr != itr) + { + new (target_itr) T(std::move(*original_source_itr)); + original_source_itr->~T(); + ++original_source_itr; + ++target_itr; + } + } + + // Copy-construct new elements. + for (auto *source_itr = insert_begin; source_itr != insert_end; ++source_itr, ++target_itr) + new (target_itr) T(*source_itr); + + // Move over the other half. + if (new_buffer != this->ptr || insert_begin != insert_end) + { + while (original_source_itr != this->end()) + { + new (target_itr) T(std::move(*original_source_itr)); + original_source_itr->~T(); + ++original_source_itr; + ++target_itr; + } + } + + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = new_buffer; + buffer_capacity = target_capacity; + } + else + { + // Move in place, need to be a bit careful about which elements are constructed and which are not. + // Move the end and construct the new elements. + auto *target_itr = this->end() + count; + auto *source_itr = this->end(); + while (target_itr != this->end() && source_itr != itr) + { + --target_itr; + --source_itr; + new (target_itr) T(std::move(*source_itr)); + } + + // For already constructed elements we can move-assign. + std::move_backward(itr, source_itr, target_itr); + + // For the inserts which go to already constructed elements, we can do a plain copy. + while (itr != this->end() && insert_begin != insert_end) + *itr++ = *insert_begin++; + + // For inserts into newly allocated memory, we must copy-construct instead. + while (insert_begin != insert_end) + { + new (itr) T(*insert_begin); + ++itr; + ++insert_begin; + } + } + + this->buffer_size += count; + } + } + + void insert(T *itr, const T &value) SPIRV_CROSS_NOEXCEPT + { + insert(itr, &value, &value + 1); + } + + T *erase(T *itr) SPIRV_CROSS_NOEXCEPT + { + std::move(itr + 1, this->end(), itr); + this->ptr[--this->buffer_size].~T(); + return itr; + } + + void erase(T *start_erase, T *end_erase) SPIRV_CROSS_NOEXCEPT + { + if (end_erase == this->end()) + { + resize(size_t(start_erase - this->begin())); + } + else + { + auto new_size = this->buffer_size - (end_erase - start_erase); + std::move(end_erase, this->end(), start_erase); + resize(new_size); + } + } + + void resize(size_t new_size) SPIRV_CROSS_NOEXCEPT + { + if (new_size < this->buffer_size) + { + for (size_t i = new_size; i < this->buffer_size; i++) + this->ptr[i].~T(); + } + else if (new_size > this->buffer_size) + { + reserve(new_size); + for (size_t i = this->buffer_size; i < new_size; i++) + new (&this->ptr[i]) T(); + } + + this->buffer_size = new_size; + } + +private: + size_t buffer_capacity = 0; + AlignedBuffer stack_storage; +}; + +// A vector without stack storage. +// Could also be a typedef-ed to std::vector, +// but might as well use the one we have. +template +using Vector = SmallVector; + +#else // SPIRV_CROSS_FORCE_STL_TYPES + +template +using SmallVector = std::vector; +template +using Vector = std::vector; +template +using VectorView = std::vector; + +#endif // SPIRV_CROSS_FORCE_STL_TYPES + +// An object pool which we use for allocating IVariant-derived objects. +// We know we are going to allocate a bunch of objects of each type, +// so amortize the mallocs. +class ObjectPoolBase +{ +public: + virtual ~ObjectPoolBase() = default; + virtual void free_opaque(void *ptr) = 0; +}; + +template +class ObjectPool : public ObjectPoolBase +{ +public: + explicit ObjectPool(unsigned start_object_count_ = 16) + : start_object_count(start_object_count_) + { + } + + template + T *allocate(P &&... p) + { + if (vacants.empty()) + { + unsigned num_objects = start_object_count << memory.size(); + T *ptr = static_cast(malloc(num_objects * sizeof(T))); + if (!ptr) + return nullptr; + + for (unsigned i = 0; i < num_objects; i++) + vacants.push_back(&ptr[i]); + + memory.emplace_back(ptr); + } + + T *ptr = vacants.back(); + vacants.pop_back(); + new (ptr) T(std::forward

(p)...); + return ptr; + } + + void free(T *ptr) + { + ptr->~T(); + vacants.push_back(ptr); + } + + void free_opaque(void *ptr) override + { + free(static_cast(ptr)); + } + + void clear() + { + vacants.clear(); + memory.clear(); + } + +protected: + Vector vacants; + + struct MallocDeleter + { + void operator()(T *ptr) + { + ::free(ptr); + } + }; + + SmallVector> memory; + unsigned start_object_count; +}; + +template +class StringStream +{ +public: + StringStream() + { + reset(); + } + + ~StringStream() + { + reset(); + } + + // Disable copies and moves. Makes it easier to implement, and we don't need it. + StringStream(const StringStream &) = delete; + void operator=(const StringStream &) = delete; + + template ::value, int>::type = 0> + StringStream &operator<<(const T &t) + { + auto s = std::to_string(t); + append(s.data(), s.size()); + return *this; + } + + // Only overload this to make float/double conversions ambiguous. + StringStream &operator<<(uint32_t v) + { + auto s = std::to_string(v); + append(s.data(), s.size()); + return *this; + } + + StringStream &operator<<(char c) + { + append(&c, 1); + return *this; + } + + StringStream &operator<<(const std::string &s) + { + append(s.data(), s.size()); + return *this; + } + + StringStream &operator<<(const char *s) + { + append(s, strlen(s)); + return *this; + } + + template + StringStream &operator<<(const char (&s)[N]) + { + append(s, strlen(s)); + return *this; + } + + std::string str() const + { + std::string ret; + size_t target_size = 0; + for (auto &saved : saved_buffers) + target_size += saved.offset; + target_size += current_buffer.offset; + ret.reserve(target_size); + + for (auto &saved : saved_buffers) + ret.insert(ret.end(), saved.buffer, saved.buffer + saved.offset); + ret.insert(ret.end(), current_buffer.buffer, current_buffer.buffer + current_buffer.offset); + return ret; + } + + void reset() + { + for (auto &saved : saved_buffers) + if (saved.buffer != stack_buffer) + free(saved.buffer); + if (current_buffer.buffer != stack_buffer) + free(current_buffer.buffer); + + saved_buffers.clear(); + current_buffer.buffer = stack_buffer; + current_buffer.offset = 0; + current_buffer.size = sizeof(stack_buffer); + } + +private: + struct Buffer + { + char *buffer = nullptr; + size_t offset = 0; + size_t size = 0; + }; + Buffer current_buffer; + char stack_buffer[StackSize]; + SmallVector saved_buffers; + + void append(const char *s, size_t len) + { + size_t avail = current_buffer.size - current_buffer.offset; + if (avail < len) + { + if (avail > 0) + { + memcpy(current_buffer.buffer + current_buffer.offset, s, avail); + s += avail; + len -= avail; + current_buffer.offset += avail; + } + + saved_buffers.push_back(current_buffer); + size_t target_size = len > BlockSize ? len : BlockSize; + current_buffer.buffer = static_cast(malloc(target_size)); + if (!current_buffer.buffer) + SPIRV_CROSS_THROW("Out of memory."); + + memcpy(current_buffer.buffer, s, len); + current_buffer.offset = len; + current_buffer.size = target_size; + } + else + { + memcpy(current_buffer.buffer + current_buffer.offset, s, len); + current_buffer.offset += len; + } + } +}; + +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_cross_error_handling.hpp b/third_party/spirv-cross/spirv_cross_error_handling.hpp new file mode 100644 index 0000000..fa90610 --- /dev/null +++ b/third_party/spirv-cross/spirv_cross_error_handling.hpp @@ -0,0 +1,87 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_ERROR_HANDLING +#define SPIRV_CROSS_ERROR_HANDLING + +#include +#include +#include +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +#include +#endif + +#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE +#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE +#else +#define SPIRV_CROSS_NAMESPACE spirv_cross +#endif + +namespace SPIRV_CROSS_NAMESPACE +{ +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +#if !defined(_MSC_VER) || defined(__clang__) +[[noreturn]] +#elif defined(_MSC_VER) +__declspec(noreturn) +#endif +inline void +report_and_abort(const std::string &msg) +{ +#ifdef NDEBUG + (void)msg; +#else + fprintf(stderr, "There was a compiler error: %s\n", msg.c_str()); +#endif + fflush(stderr); + abort(); +} + +#define SPIRV_CROSS_THROW(x) report_and_abort(x) +#else +class CompilerError : public std::runtime_error +{ +public: + explicit CompilerError(const std::string &str) + : std::runtime_error(str) + { + } +}; + +#define SPIRV_CROSS_THROW(x) throw CompilerError(x) +#endif + +// MSVC 2013 does not have noexcept. We need this for Variant to get move constructor to work correctly +// instead of copy constructor. +// MSVC 2013 ignores that move constructors cannot throw in std::vector, so just don't define it. +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define SPIRV_CROSS_NOEXCEPT +#else +#define SPIRV_CROSS_NOEXCEPT noexcept +#endif + +#if __cplusplus >= 201402l +#define SPIRV_CROSS_DEPRECATED(reason) [[deprecated(reason)]] +#elif defined(__GNUC__) +#define SPIRV_CROSS_DEPRECATED(reason) __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define SPIRV_CROSS_DEPRECATED(reason) __declspec(deprecated(reason)) +#else +#define SPIRV_CROSS_DEPRECATED(reason) +#endif +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_cross_parsed_ir.cpp b/third_party/spirv-cross/spirv_cross_parsed_ir.cpp new file mode 100644 index 0000000..f409d65 --- /dev/null +++ b/third_party/spirv-cross/spirv_cross_parsed_ir.cpp @@ -0,0 +1,1044 @@ +/* + * Copyright 2018-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cross_parsed_ir.hpp" +#include +#include + +using namespace std; +using namespace spv; + +namespace SPIRV_CROSS_NAMESPACE +{ +ParsedIR::ParsedIR() +{ + // If we move ParsedIR, we need to make sure the pointer stays fixed since the child Variant objects consume a pointer to this group, + // so need an extra pointer here. + pool_group.reset(new ObjectPoolGroup); + + pool_group->pools[TypeType].reset(new ObjectPool); + pool_group->pools[TypeVariable].reset(new ObjectPool); + pool_group->pools[TypeConstant].reset(new ObjectPool); + pool_group->pools[TypeFunction].reset(new ObjectPool); + pool_group->pools[TypeFunctionPrototype].reset(new ObjectPool); + pool_group->pools[TypeBlock].reset(new ObjectPool); + pool_group->pools[TypeExtension].reset(new ObjectPool); + pool_group->pools[TypeExpression].reset(new ObjectPool); + pool_group->pools[TypeConstantOp].reset(new ObjectPool); + pool_group->pools[TypeCombinedImageSampler].reset(new ObjectPool); + pool_group->pools[TypeAccessChain].reset(new ObjectPool); + pool_group->pools[TypeUndef].reset(new ObjectPool); + pool_group->pools[TypeString].reset(new ObjectPool); +} + +// Should have been default-implemented, but need this on MSVC 2013. +ParsedIR::ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT +{ + *this = move(other); +} + +ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT +{ + if (this != &other) + { + pool_group = move(other.pool_group); + spirv = move(other.spirv); + meta = move(other.meta); + for (int i = 0; i < TypeCount; i++) + ids_for_type[i] = move(other.ids_for_type[i]); + ids_for_constant_or_type = move(other.ids_for_constant_or_type); + ids_for_constant_or_variable = move(other.ids_for_constant_or_variable); + declared_capabilities = move(other.declared_capabilities); + declared_extensions = move(other.declared_extensions); + block_meta = move(other.block_meta); + continue_block_to_loop_header = move(other.continue_block_to_loop_header); + entry_points = move(other.entry_points); + ids = move(other.ids); + addressing_model = other.addressing_model; + memory_model = other.memory_model; + + default_entry_point = other.default_entry_point; + source = other.source; + loop_iteration_depth_hard = other.loop_iteration_depth_hard; + loop_iteration_depth_soft = other.loop_iteration_depth_soft; + + meta_needing_name_fixup = std::move(other.meta_needing_name_fixup); + } + return *this; +} + +ParsedIR::ParsedIR(const ParsedIR &other) + : ParsedIR() +{ + *this = other; +} + +ParsedIR &ParsedIR::operator=(const ParsedIR &other) +{ + if (this != &other) + { + spirv = other.spirv; + meta = other.meta; + for (int i = 0; i < TypeCount; i++) + ids_for_type[i] = other.ids_for_type[i]; + ids_for_constant_or_type = other.ids_for_constant_or_type; + ids_for_constant_or_variable = other.ids_for_constant_or_variable; + declared_capabilities = other.declared_capabilities; + declared_extensions = other.declared_extensions; + block_meta = other.block_meta; + continue_block_to_loop_header = other.continue_block_to_loop_header; + entry_points = other.entry_points; + default_entry_point = other.default_entry_point; + source = other.source; + loop_iteration_depth_hard = other.loop_iteration_depth_hard; + loop_iteration_depth_soft = other.loop_iteration_depth_soft; + addressing_model = other.addressing_model; + memory_model = other.memory_model; + + meta_needing_name_fixup = other.meta_needing_name_fixup; + + // Very deliberate copying of IDs. There is no default copy constructor, nor a simple default constructor. + // Construct object first so we have the correct allocator set-up, then we can copy object into our new pool group. + ids.clear(); + ids.reserve(other.ids.size()); + for (size_t i = 0; i < other.ids.size(); i++) + { + ids.emplace_back(pool_group.get()); + ids.back() = other.ids[i]; + } + } + return *this; +} + +void ParsedIR::set_id_bounds(uint32_t bounds) +{ + ids.reserve(bounds); + while (ids.size() < bounds) + ids.emplace_back(pool_group.get()); + + block_meta.resize(bounds); +} + +// Roll our own versions of these functions to avoid potential locale shenanigans. +static bool is_alpha(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static bool is_numeric(char c) +{ + return c >= '0' && c <= '9'; +} + +static bool is_alphanumeric(char c) +{ + return is_alpha(c) || is_numeric(c); +} + +static bool is_valid_identifier(const string &name) +{ + if (name.empty()) + return true; + + if (is_numeric(name[0])) + return false; + + for (auto c : name) + if (!is_alphanumeric(c) && c != '_') + return false; + + bool saw_underscore = false; + // Two underscores in a row is not a valid identifier either. + // Technically reserved, but it's easier to treat it as invalid. + for (auto c : name) + { + bool is_underscore = c == '_'; + if (is_underscore && saw_underscore) + return false; + saw_underscore = is_underscore; + } + + return true; +} + +static bool is_reserved_prefix(const string &name) +{ + // Generic reserved identifiers used by the implementation. + return name.compare(0, 3, "gl_", 3) == 0 || + // Ignore this case for now, might rewrite internal code to always use spv prefix. + //name.compare(0, 11, "SPIRV_Cross", 11) == 0 || + name.compare(0, 3, "spv", 3) == 0; +} + +static bool is_reserved_identifier(const string &name, bool member, bool allow_reserved_prefixes) +{ + if (!allow_reserved_prefixes && is_reserved_prefix(name)) + return true; + + if (member) + { + // Reserved member identifiers come in one form: + // _m[0-9]+$. + if (name.size() < 3) + return false; + + if (name.compare(0, 2, "_m", 2) != 0) + return false; + + size_t index = 2; + while (index < name.size() && is_numeric(name[index])) + index++; + + return index == name.size(); + } + else + { + // Reserved non-member identifiers come in two forms: + // _[0-9]+$, used for temporaries which map directly to a SPIR-V ID. + // _[0-9]+_, used for auxillary temporaries which derived from a SPIR-V ID. + if (name.size() < 2) + return false; + + if (name[0] != '_' || !is_numeric(name[1])) + return false; + + size_t index = 2; + while (index < name.size() && is_numeric(name[index])) + index++; + + return index == name.size() || (index < name.size() && name[index] == '_'); + } +} + +bool ParsedIR::is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes) +{ + return is_reserved_identifier(str, false, allow_reserved_prefixes); +} + +static string make_unreserved_identifier(const string &name) +{ + if (is_reserved_prefix(name)) + return "_RESERVED_IDENTIFIER_FIXUP_" + name; + else + return "_RESERVED_IDENTIFIER_FIXUP" + name; +} + +void ParsedIR::sanitize_underscores(std::string &str) +{ + // Compact adjacent underscores to make it valid. + auto dst = str.begin(); + auto src = dst; + bool saw_underscore = false; + while (src != str.end()) + { + bool is_underscore = *src == '_'; + if (saw_underscore && is_underscore) + { + src++; + } + else + { + if (dst != src) + *dst = *src; + dst++; + src++; + saw_underscore = is_underscore; + } + } + str.erase(dst, str.end()); +} + +static string ensure_valid_identifier(const string &name) +{ + // Functions in glslangValidator are mangled with name( stuff. + // Normally, we would never see '(' in any legal identifiers, so just strip them out. + auto str = name.substr(0, name.find('(')); + + if (str.empty()) + return str; + + if (is_numeric(str[0])) + str[0] = '_'; + + for (auto &c : str) + if (!is_alphanumeric(c) && c != '_') + c = '_'; + + ParsedIR::sanitize_underscores(str); + return str; +} + +const string &ParsedIR::get_name(ID id) const +{ + auto *m = find_meta(id); + if (m) + return m->decoration.alias; + else + return empty_string; +} + +const string &ParsedIR::get_member_name(TypeID id, uint32_t index) const +{ + auto *m = find_meta(id); + if (m) + { + if (index >= m->members.size()) + return empty_string; + return m->members[index].alias; + } + else + return empty_string; +} + +void ParsedIR::sanitize_identifier(std::string &name, bool member, bool allow_reserved_prefixes) +{ + if (!is_valid_identifier(name)) + name = ensure_valid_identifier(name); + if (is_reserved_identifier(name, member, allow_reserved_prefixes)) + name = make_unreserved_identifier(name); +} + +void ParsedIR::fixup_reserved_names() +{ + for (uint32_t id : meta_needing_name_fixup) + { + auto &m = meta[id]; + sanitize_identifier(m.decoration.alias, false, false); + for (auto &memb : m.members) + sanitize_identifier(memb.alias, true, false); + } + meta_needing_name_fixup.clear(); +} + +void ParsedIR::set_name(ID id, const string &name) +{ + auto &m = meta[id]; + m.decoration.alias = name; + if (!is_valid_identifier(name) || is_reserved_identifier(name, false, false)) + meta_needing_name_fixup.insert(id); +} + +void ParsedIR::set_member_name(TypeID id, uint32_t index, const string &name) +{ + auto &m = meta[id]; + m.members.resize(max(meta[id].members.size(), size_t(index) + 1)); + m.members[index].alias = name; + if (!is_valid_identifier(name) || is_reserved_identifier(name, true, false)) + meta_needing_name_fixup.insert(id); +} + +void ParsedIR::set_decoration_string(ID id, Decoration decoration, const string &argument) +{ + auto &dec = meta[id].decoration; + dec.decoration_flags.set(decoration); + + switch (decoration) + { + case DecorationHlslSemanticGOOGLE: + dec.hlsl_semantic = argument; + break; + + default: + break; + } +} + +void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument) +{ + auto &dec = meta[id].decoration; + dec.decoration_flags.set(decoration); + + switch (decoration) + { + case DecorationBuiltIn: + dec.builtin = true; + dec.builtin_type = static_cast(argument); + break; + + case DecorationLocation: + dec.location = argument; + break; + + case DecorationComponent: + dec.component = argument; + break; + + case DecorationOffset: + dec.offset = argument; + break; + + case DecorationXfbBuffer: + dec.xfb_buffer = argument; + break; + + case DecorationXfbStride: + dec.xfb_stride = argument; + break; + + case DecorationStream: + dec.stream = argument; + break; + + case DecorationArrayStride: + dec.array_stride = argument; + break; + + case DecorationMatrixStride: + dec.matrix_stride = argument; + break; + + case DecorationBinding: + dec.binding = argument; + break; + + case DecorationDescriptorSet: + dec.set = argument; + break; + + case DecorationInputAttachmentIndex: + dec.input_attachment = argument; + break; + + case DecorationSpecId: + dec.spec_id = argument; + break; + + case DecorationIndex: + dec.index = argument; + break; + + case DecorationHlslCounterBufferGOOGLE: + meta[id].hlsl_magic_counter_buffer = argument; + meta[argument].hlsl_is_magic_counter_buffer = true; + break; + + case DecorationFPRoundingMode: + dec.fp_rounding_mode = static_cast(argument); + break; + + default: + break; + } +} + +void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument) +{ + meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1)); + auto &dec = meta[id].members[index]; + dec.decoration_flags.set(decoration); + + switch (decoration) + { + case DecorationBuiltIn: + dec.builtin = true; + dec.builtin_type = static_cast(argument); + break; + + case DecorationLocation: + dec.location = argument; + break; + + case DecorationComponent: + dec.component = argument; + break; + + case DecorationBinding: + dec.binding = argument; + break; + + case DecorationOffset: + dec.offset = argument; + break; + + case DecorationXfbBuffer: + dec.xfb_buffer = argument; + break; + + case DecorationXfbStride: + dec.xfb_stride = argument; + break; + + case DecorationStream: + dec.stream = argument; + break; + + case DecorationSpecId: + dec.spec_id = argument; + break; + + case DecorationMatrixStride: + dec.matrix_stride = argument; + break; + + case DecorationIndex: + dec.index = argument; + break; + + default: + break; + } +} + +// Recursively marks any constants referenced by the specified constant instruction as being used +// as an array length. The id must be a constant instruction (SPIRConstant or SPIRConstantOp). +void ParsedIR::mark_used_as_array_length(ID id) +{ + switch (ids[id].get_type()) + { + case TypeConstant: + get(id).is_used_as_array_length = true; + break; + + case TypeConstantOp: + { + auto &cop = get(id); + if (cop.opcode == OpCompositeExtract) + mark_used_as_array_length(cop.arguments[0]); + else if (cop.opcode == OpCompositeInsert) + { + mark_used_as_array_length(cop.arguments[0]); + mark_used_as_array_length(cop.arguments[1]); + } + else + for (uint32_t arg_id : cop.arguments) + mark_used_as_array_length(arg_id); + break; + } + + case TypeUndef: + break; + + default: + assert(0); + } +} + +Bitset ParsedIR::get_buffer_block_flags(const SPIRVariable &var) const +{ + auto &type = get(var.basetype); + assert(type.basetype == SPIRType::Struct); + + // Some flags like non-writable, non-readable are actually found + // as member decorations. If all members have a decoration set, propagate + // the decoration up as a regular variable decoration. + Bitset base_flags; + auto *m = find_meta(var.self); + if (m) + base_flags = m->decoration.decoration_flags; + + if (type.member_types.empty()) + return base_flags; + + Bitset all_members_flags = get_member_decoration_bitset(type.self, 0); + for (uint32_t i = 1; i < uint32_t(type.member_types.size()); i++) + all_members_flags.merge_and(get_member_decoration_bitset(type.self, i)); + + base_flags.merge_or(all_members_flags); + return base_flags; +} + +const Bitset &ParsedIR::get_member_decoration_bitset(TypeID id, uint32_t index) const +{ + auto *m = find_meta(id); + if (m) + { + if (index >= m->members.size()) + return cleared_bitset; + return m->members[index].decoration_flags; + } + else + return cleared_bitset; +} + +bool ParsedIR::has_decoration(ID id, Decoration decoration) const +{ + return get_decoration_bitset(id).get(decoration); +} + +uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const +{ + auto *m = find_meta(id); + if (!m) + return 0; + + auto &dec = m->decoration; + if (!dec.decoration_flags.get(decoration)) + return 0; + + switch (decoration) + { + case DecorationBuiltIn: + return dec.builtin_type; + case DecorationLocation: + return dec.location; + case DecorationComponent: + return dec.component; + case DecorationOffset: + return dec.offset; + case DecorationXfbBuffer: + return dec.xfb_buffer; + case DecorationXfbStride: + return dec.xfb_stride; + case DecorationStream: + return dec.stream; + case DecorationBinding: + return dec.binding; + case DecorationDescriptorSet: + return dec.set; + case DecorationInputAttachmentIndex: + return dec.input_attachment; + case DecorationSpecId: + return dec.spec_id; + case DecorationArrayStride: + return dec.array_stride; + case DecorationMatrixStride: + return dec.matrix_stride; + case DecorationIndex: + return dec.index; + case DecorationFPRoundingMode: + return dec.fp_rounding_mode; + default: + return 1; + } +} + +const string &ParsedIR::get_decoration_string(ID id, Decoration decoration) const +{ + auto *m = find_meta(id); + if (!m) + return empty_string; + + auto &dec = m->decoration; + + if (!dec.decoration_flags.get(decoration)) + return empty_string; + + switch (decoration) + { + case DecorationHlslSemanticGOOGLE: + return dec.hlsl_semantic; + + default: + return empty_string; + } +} + +void ParsedIR::unset_decoration(ID id, Decoration decoration) +{ + auto &dec = meta[id].decoration; + dec.decoration_flags.clear(decoration); + switch (decoration) + { + case DecorationBuiltIn: + dec.builtin = false; + break; + + case DecorationLocation: + dec.location = 0; + break; + + case DecorationComponent: + dec.component = 0; + break; + + case DecorationOffset: + dec.offset = 0; + break; + + case DecorationXfbBuffer: + dec.xfb_buffer = 0; + break; + + case DecorationXfbStride: + dec.xfb_stride = 0; + break; + + case DecorationStream: + dec.stream = 0; + break; + + case DecorationBinding: + dec.binding = 0; + break; + + case DecorationDescriptorSet: + dec.set = 0; + break; + + case DecorationInputAttachmentIndex: + dec.input_attachment = 0; + break; + + case DecorationSpecId: + dec.spec_id = 0; + break; + + case DecorationHlslSemanticGOOGLE: + dec.hlsl_semantic.clear(); + break; + + case DecorationFPRoundingMode: + dec.fp_rounding_mode = FPRoundingModeMax; + break; + + case DecorationHlslCounterBufferGOOGLE: + { + auto &counter = meta[id].hlsl_magic_counter_buffer; + if (counter) + { + meta[counter].hlsl_is_magic_counter_buffer = false; + counter = 0; + } + break; + } + + default: + break; + } +} + +bool ParsedIR::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const +{ + return get_member_decoration_bitset(id, index).get(decoration); +} + +uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const +{ + auto *m = find_meta(id); + if (!m) + return 0; + + if (index >= m->members.size()) + return 0; + + auto &dec = m->members[index]; + if (!dec.decoration_flags.get(decoration)) + return 0; + + switch (decoration) + { + case DecorationBuiltIn: + return dec.builtin_type; + case DecorationLocation: + return dec.location; + case DecorationComponent: + return dec.component; + case DecorationBinding: + return dec.binding; + case DecorationOffset: + return dec.offset; + case DecorationXfbBuffer: + return dec.xfb_buffer; + case DecorationXfbStride: + return dec.xfb_stride; + case DecorationStream: + return dec.stream; + case DecorationSpecId: + return dec.spec_id; + case DecorationIndex: + return dec.index; + default: + return 1; + } +} + +const Bitset &ParsedIR::get_decoration_bitset(ID id) const +{ + auto *m = find_meta(id); + if (m) + { + auto &dec = m->decoration; + return dec.decoration_flags; + } + else + return cleared_bitset; +} + +void ParsedIR::set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, const string &argument) +{ + meta[id].members.resize(max(meta[id].members.size(), size_t(index) + 1)); + auto &dec = meta[id].members[index]; + dec.decoration_flags.set(decoration); + + switch (decoration) + { + case DecorationHlslSemanticGOOGLE: + dec.hlsl_semantic = argument; + break; + + default: + break; + } +} + +const string &ParsedIR::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const +{ + auto *m = find_meta(id); + if (m) + { + if (!has_member_decoration(id, index, decoration)) + return empty_string; + + auto &dec = m->members[index]; + + switch (decoration) + { + case DecorationHlslSemanticGOOGLE: + return dec.hlsl_semantic; + + default: + return empty_string; + } + } + else + return empty_string; +} + +void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration) +{ + auto &m = meta[id]; + if (index >= m.members.size()) + return; + + auto &dec = m.members[index]; + + dec.decoration_flags.clear(decoration); + switch (decoration) + { + case DecorationBuiltIn: + dec.builtin = false; + break; + + case DecorationLocation: + dec.location = 0; + break; + + case DecorationComponent: + dec.component = 0; + break; + + case DecorationOffset: + dec.offset = 0; + break; + + case DecorationXfbBuffer: + dec.xfb_buffer = 0; + break; + + case DecorationXfbStride: + dec.xfb_stride = 0; + break; + + case DecorationStream: + dec.stream = 0; + break; + + case DecorationSpecId: + dec.spec_id = 0; + break; + + case DecorationHlslSemanticGOOGLE: + dec.hlsl_semantic.clear(); + break; + + default: + break; + } +} + +uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount) +{ + auto curr_bound = ids.size(); + auto new_bound = curr_bound + incr_amount; + + ids.reserve(ids.size() + incr_amount); + for (uint32_t i = 0; i < incr_amount; i++) + ids.emplace_back(pool_group.get()); + + block_meta.resize(new_bound); + return uint32_t(curr_bound); +} + +void ParsedIR::remove_typed_id(Types type, ID id) +{ + auto &type_ids = ids_for_type[type]; + type_ids.erase(remove(begin(type_ids), end(type_ids), id), end(type_ids)); +} + +void ParsedIR::reset_all_of_type(Types type) +{ + for (auto &id : ids_for_type[type]) + if (ids[id].get_type() == type) + ids[id].reset(); + + ids_for_type[type].clear(); +} + +void ParsedIR::add_typed_id(Types type, ID id) +{ + if (loop_iteration_depth_hard != 0) + SPIRV_CROSS_THROW("Cannot add typed ID while looping over it."); + + if (loop_iteration_depth_soft != 0) + { + if (!ids[id].empty()) + SPIRV_CROSS_THROW("Cannot override IDs when loop is soft locked."); + return; + } + + if (ids[id].empty() || ids[id].get_type() != type) + { + switch (type) + { + case TypeConstant: + ids_for_constant_or_variable.push_back(id); + ids_for_constant_or_type.push_back(id); + break; + + case TypeVariable: + ids_for_constant_or_variable.push_back(id); + break; + + case TypeType: + case TypeConstantOp: + ids_for_constant_or_type.push_back(id); + break; + + default: + break; + } + } + + if (ids[id].empty()) + { + ids_for_type[type].push_back(id); + } + else if (ids[id].get_type() != type) + { + remove_typed_id(ids[id].get_type(), id); + ids_for_type[type].push_back(id); + } +} + +const Meta *ParsedIR::find_meta(ID id) const +{ + auto itr = meta.find(id); + if (itr != end(meta)) + return &itr->second; + else + return nullptr; +} + +Meta *ParsedIR::find_meta(ID id) +{ + auto itr = meta.find(id); + if (itr != end(meta)) + return &itr->second; + else + return nullptr; +} + +ParsedIR::LoopLock ParsedIR::create_loop_hard_lock() const +{ + return ParsedIR::LoopLock(&loop_iteration_depth_hard); +} + +ParsedIR::LoopLock ParsedIR::create_loop_soft_lock() const +{ + return ParsedIR::LoopLock(&loop_iteration_depth_soft); +} + +ParsedIR::LoopLock::~LoopLock() +{ + if (lock) + (*lock)--; +} + +ParsedIR::LoopLock::LoopLock(uint32_t *lock_) + : lock(lock_) +{ + if (lock) + (*lock)++; +} + +ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT +{ + *this = move(other); +} + +ParsedIR::LoopLock &ParsedIR::LoopLock::operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT +{ + if (lock) + (*lock)--; + lock = other.lock; + other.lock = nullptr; + return *this; +} + +void ParsedIR::make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set) +{ + auto &constant_type = get(type); + + if (constant_type.pointer) + { + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + auto &constant = variant_set(ids[id], type); + constant.self = id; + constant.make_null(constant_type); + } + else if (!constant_type.array.empty()) + { + assert(constant_type.parent_type); + uint32_t parent_id = increase_bound_by(1); + make_constant_null(parent_id, constant_type.parent_type, add_to_typed_id_set); + + if (!constant_type.array_size_literal.back()) + SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal."); + + SmallVector elements(constant_type.array.back()); + for (uint32_t i = 0; i < constant_type.array.back(); i++) + elements[i] = parent_id; + + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + variant_set(ids[id], type, elements.data(), uint32_t(elements.size()), false).self = id; + } + else if (!constant_type.member_types.empty()) + { + uint32_t member_ids = increase_bound_by(uint32_t(constant_type.member_types.size())); + SmallVector elements(constant_type.member_types.size()); + for (uint32_t i = 0; i < constant_type.member_types.size(); i++) + { + make_constant_null(member_ids + i, constant_type.member_types[i], add_to_typed_id_set); + elements[i] = member_ids + i; + } + + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + variant_set(ids[id], type, elements.data(), uint32_t(elements.size()), false).self = id; + } + else + { + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + auto &constant = variant_set(ids[id], type); + constant.self = id; + constant.make_null(constant_type); + } +} + +} // namespace SPIRV_CROSS_NAMESPACE diff --git a/third_party/spirv-cross/spirv_cross_parsed_ir.hpp b/third_party/spirv-cross/spirv_cross_parsed_ir.hpp new file mode 100644 index 0000000..36d6ac7 --- /dev/null +++ b/third_party/spirv-cross/spirv_cross_parsed_ir.hpp @@ -0,0 +1,239 @@ +/* + * Copyright 2018-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_PARSED_IR_HPP +#define SPIRV_CROSS_PARSED_IR_HPP + +#include "spirv_common.hpp" +#include +#include + +namespace SPIRV_CROSS_NAMESPACE +{ + +// This data structure holds all information needed to perform cross-compilation and reflection. +// It is the output of the Parser, but any implementation could create this structure. +// It is intentionally very "open" and struct-like with some helper functions to deal with decorations. +// Parser is the reference implementation of how this data structure should be filled in. + +class ParsedIR +{ +private: + // This must be destroyed after the "ids" vector. + std::unique_ptr pool_group; + +public: + ParsedIR(); + + // Due to custom allocations from object pools, we cannot use a default copy constructor. + ParsedIR(const ParsedIR &other); + ParsedIR &operator=(const ParsedIR &other); + + // Moves are unproblematic, but we need to implement it anyways, since MSVC 2013 does not understand + // how to default-implement these. + ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT; + ParsedIR &operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT; + + // Resizes ids, meta and block_meta. + void set_id_bounds(uint32_t bounds); + + // The raw SPIR-V, instructions and opcodes refer to this by offset + count. + std::vector spirv; + + // Holds various data structures which inherit from IVariant. + SmallVector ids; + + // Various meta data for IDs, decorations, names, etc. + std::unordered_map meta; + + // Holds all IDs which have a certain type. + // This is needed so we can iterate through a specific kind of resource quickly, + // and in-order of module declaration. + SmallVector ids_for_type[TypeCount]; + + // Special purpose lists which contain a union of types. + // This is needed so we can declare specialization constants and structs in an interleaved fashion, + // among other things. + // Constants can be of struct type, and struct array sizes can use specialization constants. + SmallVector ids_for_constant_or_type; + SmallVector ids_for_constant_or_variable; + + // Declared capabilities and extensions in the SPIR-V module. + // Not really used except for reflection at the moment. + SmallVector declared_capabilities; + SmallVector declared_extensions; + + // Meta data about blocks. The cross-compiler needs to query if a block is either of these types. + // It is a bitset as there can be more than one tag per block. + enum BlockMetaFlagBits + { + BLOCK_META_LOOP_HEADER_BIT = 1 << 0, + BLOCK_META_CONTINUE_BIT = 1 << 1, + BLOCK_META_LOOP_MERGE_BIT = 1 << 2, + BLOCK_META_SELECTION_MERGE_BIT = 1 << 3, + BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4 + }; + using BlockMetaFlags = uint8_t; + SmallVector block_meta; + std::unordered_map continue_block_to_loop_header; + + // Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction. + // Entry points can therefore be seen as some sort of meta structure. + std::unordered_map entry_points; + FunctionID default_entry_point = 0; + + struct Source + { + uint32_t version = 0; + bool es = false; + bool known = false; + bool hlsl = false; + + Source() = default; + }; + + Source source; + + spv::AddressingModel addressing_model = spv::AddressingModelMax; + spv::MemoryModel memory_model = spv::MemoryModelMax; + + // Decoration handling methods. + // Can be useful for simple "raw" reflection. + // However, most members are here because the Parser needs most of these, + // and might as well just have the whole suite of decoration/name handling in one place. + void set_name(ID id, const std::string &name); + const std::string &get_name(ID id) const; + void set_decoration(ID id, spv::Decoration decoration, uint32_t argument = 0); + void set_decoration_string(ID id, spv::Decoration decoration, const std::string &argument); + bool has_decoration(ID id, spv::Decoration decoration) const; + uint32_t get_decoration(ID id, spv::Decoration decoration) const; + const std::string &get_decoration_string(ID id, spv::Decoration decoration) const; + const Bitset &get_decoration_bitset(ID id) const; + void unset_decoration(ID id, spv::Decoration decoration); + + // Decoration handling methods (for members of a struct). + void set_member_name(TypeID id, uint32_t index, const std::string &name); + const std::string &get_member_name(TypeID id, uint32_t index) const; + void set_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration, uint32_t argument = 0); + void set_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration, + const std::string &argument); + uint32_t get_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; + const std::string &get_member_decoration_string(TypeID id, uint32_t index, spv::Decoration decoration) const; + bool has_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration) const; + const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; + void unset_member_decoration(TypeID id, uint32_t index, spv::Decoration decoration); + + void mark_used_as_array_length(ID id); + uint32_t increase_bound_by(uint32_t count); + Bitset get_buffer_block_flags(const SPIRVariable &var) const; + + void add_typed_id(Types type, ID id); + void remove_typed_id(Types type, ID id); + + class LoopLock + { + public: + explicit LoopLock(uint32_t *counter); + LoopLock(const LoopLock &) = delete; + void operator=(const LoopLock &) = delete; + LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT; + LoopLock &operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT; + ~LoopLock(); + + private: + uint32_t *lock; + }; + + // This must be held while iterating over a type ID array. + // It is undefined if someone calls set<>() while we're iterating over a data structure, so we must + // make sure that this case is avoided. + + // If we have a hard lock, it is an error to call set<>(), and an exception is thrown. + // If we have a soft lock, we silently ignore any additions to the typed arrays. + // This should only be used for physical ID remapping where we need to create an ID, but we will never + // care about iterating over them. + LoopLock create_loop_hard_lock() const; + LoopLock create_loop_soft_lock() const; + + template + void for_each_typed_id(const Op &op) + { + auto loop_lock = create_loop_hard_lock(); + for (auto &id : ids_for_type[T::type]) + { + if (ids[id].get_type() == static_cast(T::type)) + op(id, get(id)); + } + } + + template + void for_each_typed_id(const Op &op) const + { + auto loop_lock = create_loop_hard_lock(); + for (auto &id : ids_for_type[T::type]) + { + if (ids[id].get_type() == static_cast(T::type)) + op(id, get(id)); + } + } + + template + void reset_all_of_type() + { + reset_all_of_type(static_cast(T::type)); + } + + void reset_all_of_type(Types type); + + Meta *find_meta(ID id); + const Meta *find_meta(ID id) const; + + const std::string &get_empty_string() const + { + return empty_string; + } + + void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set); + + void fixup_reserved_names(); + + static void sanitize_underscores(std::string &str); + static void sanitize_identifier(std::string &str, bool member, bool allow_reserved_prefixes); + static bool is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes); + +private: + template + T &get(uint32_t id) + { + return variant_get(ids[id]); + } + + template + const T &get(uint32_t id) const + { + return variant_get(ids[id]); + } + + mutable uint32_t loop_iteration_depth_hard = 0; + mutable uint32_t loop_iteration_depth_soft = 0; + std::string empty_string; + Bitset cleared_bitset; + + std::unordered_set meta_needing_name_fixup; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_cross_util.cpp b/third_party/spirv-cross/spirv_cross_util.cpp new file mode 100644 index 0000000..cfad676 --- /dev/null +++ b/third_party/spirv-cross/spirv_cross_util.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_cross_util.hpp" +#include "spirv_common.hpp" + +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; + +namespace spirv_cross_util +{ +void rename_interface_variable(Compiler &compiler, const SmallVector &resources, uint32_t location, + const std::string &name) +{ + for (auto &v : resources) + { + if (!compiler.has_decoration(v.id, spv::DecorationLocation)) + continue; + + auto loc = compiler.get_decoration(v.id, spv::DecorationLocation); + if (loc != location) + continue; + + auto &type = compiler.get_type(v.base_type_id); + + // This is more of a friendly variant. If we need to rename interface variables, we might have to rename + // structs as well and make sure all the names match up. + if (type.basetype == SPIRType::Struct) + { + compiler.set_name(v.base_type_id, join("SPIRV_Cross_Interface_Location", location)); + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + compiler.set_member_name(v.base_type_id, i, join("InterfaceMember", i)); + } + + compiler.set_name(v.id, name); + } +} + +void inherit_combined_sampler_bindings(Compiler &compiler) +{ + auto &samplers = compiler.get_combined_image_samplers(); + for (auto &s : samplers) + { + if (compiler.has_decoration(s.image_id, spv::DecorationDescriptorSet)) + { + uint32_t set = compiler.get_decoration(s.image_id, spv::DecorationDescriptorSet); + compiler.set_decoration(s.combined_id, spv::DecorationDescriptorSet, set); + } + + if (compiler.has_decoration(s.image_id, spv::DecorationBinding)) + { + uint32_t binding = compiler.get_decoration(s.image_id, spv::DecorationBinding); + compiler.set_decoration(s.combined_id, spv::DecorationBinding, binding); + } + } +} +} // namespace spirv_cross_util diff --git a/third_party/spirv-cross/spirv_cross_util.hpp b/third_party/spirv-cross/spirv_cross_util.hpp new file mode 100644 index 0000000..708b7cb --- /dev/null +++ b/third_party/spirv-cross/spirv_cross_util.hpp @@ -0,0 +1,30 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_UTIL_HPP +#define SPIRV_CROSS_UTIL_HPP + +#include "spirv_cross.hpp" + +namespace spirv_cross_util +{ +void rename_interface_variable(SPIRV_CROSS_NAMESPACE::Compiler &compiler, + const SPIRV_CROSS_NAMESPACE::SmallVector &resources, + uint32_t location, const std::string &name); +void inherit_combined_sampler_bindings(SPIRV_CROSS_NAMESPACE::Compiler &compiler); +} // namespace spirv_cross_util + +#endif diff --git a/third_party/spirv-cross/spirv_glsl.cpp b/third_party/spirv-cross/spirv_glsl.cpp new file mode 100644 index 0000000..79e47b3 --- /dev/null +++ b/third_party/spirv-cross/spirv_glsl.cpp @@ -0,0 +1,15278 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_glsl.hpp" +#include "GLSL.std.450.h" +#include "spirv_common.hpp" +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif +#include + +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +static bool is_unsigned_opcode(Op op) +{ + // Don't have to be exhaustive, only relevant for legacy target checking ... + switch (op) + { + case OpShiftRightLogical: + case OpUGreaterThan: + case OpUGreaterThanEqual: + case OpULessThan: + case OpULessThanEqual: + case OpUConvert: + case OpUDiv: + case OpUMod: + case OpUMulExtended: + case OpConvertUToF: + case OpConvertFToU: + return true; + + default: + return false; + } +} + +static bool is_unsigned_glsl_opcode(GLSLstd450 op) +{ + // Don't have to be exhaustive, only relevant for legacy target checking ... + switch (op) + { + case GLSLstd450UClamp: + case GLSLstd450UMin: + case GLSLstd450UMax: + case GLSLstd450FindUMsb: + return true; + + default: + return false; + } +} + +static bool packing_is_vec4_padded(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingHLSLCbuffer: + case BufferPackingHLSLCbufferPackOffset: + case BufferPackingStd140: + case BufferPackingStd140EnhancedLayout: + return true; + + default: + return false; + } +} + +static bool packing_is_hlsl(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingHLSLCbuffer: + case BufferPackingHLSLCbufferPackOffset: + return true; + + default: + return false; + } +} + +static bool packing_has_flexible_offset(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingStd140: + case BufferPackingStd430: + case BufferPackingScalar: + case BufferPackingHLSLCbuffer: + return false; + + default: + return true; + } +} + +static bool packing_is_scalar(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingScalar: + case BufferPackingScalarEnhancedLayout: + return true; + + default: + return false; + } +} + +static BufferPackingStandard packing_to_substruct_packing(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingStd140EnhancedLayout: + return BufferPackingStd140; + case BufferPackingStd430EnhancedLayout: + return BufferPackingStd430; + case BufferPackingHLSLCbufferPackOffset: + return BufferPackingHLSLCbuffer; + case BufferPackingScalarEnhancedLayout: + return BufferPackingScalar; + default: + return packing; + } +} + +void CompilerGLSL::init() +{ + if (ir.source.known) + { + options.es = ir.source.es; + options.version = ir.source.version; + } + + // Query the locale to see what the decimal point is. + // We'll rely on fixing it up ourselves in the rare case we have a comma-as-decimal locale + // rather than setting locales ourselves. Settings locales in a safe and isolated way is rather + // tricky. +#ifdef _WIN32 + // On Windows, localeconv uses thread-local storage, so it should be fine. + const struct lconv *conv = localeconv(); + if (conv && conv->decimal_point) + current_locale_radix_character = *conv->decimal_point; +#elif defined(__ANDROID__) && __ANDROID_API__ < 26 + // nl_langinfo is not supported on this platform, fall back to the worse alternative. + const struct lconv *conv = localeconv(); + if (conv && conv->decimal_point) + current_locale_radix_character = *conv->decimal_point; +#else + // localeconv, the portable function is not MT safe ... + const char *decimal_point = nl_langinfo(RADIXCHAR); + if (decimal_point && *decimal_point != '\0') + current_locale_radix_character = *decimal_point; +#endif +} + +static const char *to_pls_layout(PlsFormat format) +{ + switch (format) + { + case PlsR11FG11FB10F: + return "layout(r11f_g11f_b10f) "; + case PlsR32F: + return "layout(r32f) "; + case PlsRG16F: + return "layout(rg16f) "; + case PlsRGB10A2: + return "layout(rgb10_a2) "; + case PlsRGBA8: + return "layout(rgba8) "; + case PlsRG16: + return "layout(rg16) "; + case PlsRGBA8I: + return "layout(rgba8i)"; + case PlsRG16I: + return "layout(rg16i) "; + case PlsRGB10A2UI: + return "layout(rgb10_a2ui) "; + case PlsRGBA8UI: + return "layout(rgba8ui) "; + case PlsRG16UI: + return "layout(rg16ui) "; + case PlsR32UI: + return "layout(r32ui) "; + default: + return ""; + } +} + +static SPIRType::BaseType pls_format_to_basetype(PlsFormat format) +{ + switch (format) + { + default: + case PlsR11FG11FB10F: + case PlsR32F: + case PlsRG16F: + case PlsRGB10A2: + case PlsRGBA8: + case PlsRG16: + return SPIRType::Float; + + case PlsRGBA8I: + case PlsRG16I: + return SPIRType::Int; + + case PlsRGB10A2UI: + case PlsRGBA8UI: + case PlsRG16UI: + case PlsR32UI: + return SPIRType::UInt; + } +} + +static uint32_t pls_format_to_components(PlsFormat format) +{ + switch (format) + { + default: + case PlsR32F: + case PlsR32UI: + return 1; + + case PlsRG16F: + case PlsRG16: + case PlsRG16UI: + case PlsRG16I: + return 2; + + case PlsR11FG11FB10F: + return 3; + + case PlsRGB10A2: + case PlsRGBA8: + case PlsRGBA8I: + case PlsRGB10A2UI: + case PlsRGBA8UI: + return 4; + } +} + +const char *CompilerGLSL::vector_swizzle(int vecsize, int index) +{ + static const char *const swizzle[4][4] = { + { ".x", ".y", ".z", ".w" }, + { ".xy", ".yz", ".zw", nullptr }, + { ".xyz", ".yzw", nullptr, nullptr }, +#if defined(__GNUC__) && (__GNUC__ == 9) + // This works around a GCC 9 bug, see details in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90947. + // This array ends up being compiled as all nullptrs, tripping the assertions below. + { "", nullptr, nullptr, "$" }, +#else + { "", nullptr, nullptr, nullptr }, +#endif + }; + + assert(vecsize >= 1 && vecsize <= 4); + assert(index >= 0 && index < 4); + assert(swizzle[vecsize - 1][index]); + + return swizzle[vecsize - 1][index]; +} + +void CompilerGLSL::reset() +{ + // We do some speculative optimizations which should pretty much always work out, + // but just in case the SPIR-V is rather weird, recompile until it's happy. + // This typically only means one extra pass. + clear_force_recompile(); + + // Clear invalid expression tracking. + invalid_expressions.clear(); + current_function = nullptr; + + // Clear temporary usage tracking. + expression_usage_counts.clear(); + forwarded_temporaries.clear(); + suppressed_usage_tracking.clear(); + + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + flushed_phi_variables.clear(); + + reset_name_caches(); + + ir.for_each_typed_id([&](uint32_t, SPIRFunction &func) { + func.active = false; + func.flush_undeclared = true; + }); + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { var.dependees.clear(); }); + + ir.reset_all_of_type(); + ir.reset_all_of_type(); + + statement_count = 0; + indent = 0; + current_loop_level = 0; +} + +void CompilerGLSL::remap_pls_variables() +{ + for (auto &input : pls_inputs) + { + auto &var = get(input.id); + + bool input_is_target = false; + if (var.storage == StorageClassUniformConstant) + { + auto &type = get(var.basetype); + input_is_target = type.image.dim == DimSubpassData; + } + + if (var.storage != StorageClassInput && !input_is_target) + SPIRV_CROSS_THROW("Can only use in and target variables for PLS inputs."); + var.remapped_variable = true; + } + + for (auto &output : pls_outputs) + { + auto &var = get(output.id); + if (var.storage != StorageClassOutput) + SPIRV_CROSS_THROW("Can only use out variables for PLS outputs."); + var.remapped_variable = true; + } +} + +void CompilerGLSL::remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location) +{ + subpass_to_framebuffer_fetch_attachment.push_back({ input_attachment_index, color_location }); + inout_color_attachments.insert(color_location); +} + +void CompilerGLSL::find_static_extensions() +{ + ir.for_each_typed_id([&](uint32_t, const SPIRType &type) { + if (type.basetype == SPIRType::Double) + { + if (options.es) + SPIRV_CROSS_THROW("FP64 not supported in ES profile."); + if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_gpu_shader_fp64"); + } + else if (type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64) + { + if (options.es) + SPIRV_CROSS_THROW("64-bit integers not supported in ES profile."); + if (!options.es) + require_extension_internal("GL_ARB_gpu_shader_int64"); + } + else if (type.basetype == SPIRType::Half) + { + require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_float16"); + if (options.vulkan_semantics) + require_extension_internal("GL_EXT_shader_16bit_storage"); + } + else if (type.basetype == SPIRType::SByte || type.basetype == SPIRType::UByte) + { + require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_int8"); + if (options.vulkan_semantics) + require_extension_internal("GL_EXT_shader_8bit_storage"); + } + else if (type.basetype == SPIRType::Short || type.basetype == SPIRType::UShort) + { + require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_int16"); + if (options.vulkan_semantics) + require_extension_internal("GL_EXT_shader_16bit_storage"); + } + }); + + auto &execution = get_entry_point(); + switch (execution.model) + { + case ExecutionModelGLCompute: + if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_compute_shader"); + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("At least ESSL 3.10 required for compute shaders."); + break; + + case ExecutionModelGeometry: + if (options.es && options.version < 320) + require_extension_internal("GL_EXT_geometry_shader"); + if (!options.es && options.version < 150) + require_extension_internal("GL_ARB_geometry_shader4"); + + if (execution.flags.get(ExecutionModeInvocations) && execution.invocations != 1) + { + // Instanced GS is part of 400 core or this extension. + if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_gpu_shader5"); + } + break; + + case ExecutionModelTessellationEvaluation: + case ExecutionModelTessellationControl: + if (options.es && options.version < 320) + require_extension_internal("GL_EXT_tessellation_shader"); + if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_tessellation_shader"); + break; + + case ExecutionModelRayGenerationNV: + case ExecutionModelIntersectionNV: + case ExecutionModelAnyHitNV: + case ExecutionModelClosestHitNV: + case ExecutionModelMissNV: + case ExecutionModelCallableNV: + if (options.es || options.version < 460) + SPIRV_CROSS_THROW("Ray tracing shaders require non-es profile with version 460 or above."); + require_extension_internal("GL_NV_ray_tracing"); + break; + + default: + break; + } + + if (!pls_inputs.empty() || !pls_outputs.empty()) + { + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Can only use GL_EXT_shader_pixel_local_storage in fragment shaders."); + require_extension_internal("GL_EXT_shader_pixel_local_storage"); + } + + if (!inout_color_attachments.empty()) + { + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Can only use GL_EXT_shader_framebuffer_fetch in fragment shaders."); + if (options.vulkan_semantics) + SPIRV_CROSS_THROW("Cannot use EXT_shader_framebuffer_fetch in Vulkan GLSL."); + require_extension_internal("GL_EXT_shader_framebuffer_fetch"); + } + + if (options.separate_shader_objects && !options.es && options.version < 410) + require_extension_internal("GL_ARB_separate_shader_objects"); + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64EXT) + { + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("GL_EXT_buffer_reference is only supported in Vulkan GLSL."); + if (options.es && options.version < 320) + SPIRV_CROSS_THROW("GL_EXT_buffer_reference requires ESSL 320."); + else if (!options.es && options.version < 450) + SPIRV_CROSS_THROW("GL_EXT_buffer_reference requires GLSL 450."); + require_extension_internal("GL_EXT_buffer_reference"); + } + else if (ir.addressing_model != AddressingModelLogical) + { + SPIRV_CROSS_THROW("Only Logical and PhysicalStorageBuffer64EXT addressing models are supported."); + } + + // Check for nonuniform qualifier and passthrough. + // Instead of looping over all decorations to find this, just look at capabilities. + for (auto &cap : ir.declared_capabilities) + { + switch (cap) + { + case CapabilityShaderNonUniformEXT: + if (!options.vulkan_semantics) + require_extension_internal("GL_NV_gpu_shader5"); + else + require_extension_internal("GL_EXT_nonuniform_qualifier"); + break; + case CapabilityRuntimeDescriptorArrayEXT: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("GL_EXT_nonuniform_qualifier is only supported in Vulkan GLSL."); + require_extension_internal("GL_EXT_nonuniform_qualifier"); + break; + + case CapabilityGeometryShaderPassthroughNV: + if (execution.model == ExecutionModelGeometry) + { + require_extension_internal("GL_NV_geometry_shader_passthrough"); + execution.geometry_passthrough = true; + } + break; + + default: + break; + } + } +} + +string CompilerGLSL::compile() +{ + ir.fixup_reserved_names(); + + if (options.vulkan_semantics) + backend.allow_precision_qualifiers = true; + else + { + // only NV_gpu_shader5 supports divergent indexing on OpenGL, and it does so without extra qualifiers + backend.nonuniform_qualifier = ""; + backend.needs_row_major_load_workaround = true; + } + backend.force_gl_in_out_block = true; + backend.supports_extensions = true; + backend.use_array_constructor = true; + + if (is_legacy_es()) + backend.support_case_fallthrough = false; + + // Scan the SPIR-V to find trivial uses of extensions. + fixup_type_alias(); + reorder_type_alias(); + build_function_control_flow_graphs_and_analyze(); + find_static_extensions(); + fixup_image_load_store_access(); + update_active_builtins(); + analyze_image_and_sampler_usage(); + analyze_interlocked_resource_usage(); + if (!inout_color_attachments.empty()) + emit_inout_fragment_outputs_copy_to_subpass_inputs(); + + // Shaders might cast unrelated data to pointers of non-block types. + // Find all such instances and make sure we can cast the pointers to a synthesized block type. + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64EXT) + analyze_non_block_pointer_types(); + + uint32_t pass_count = 0; + do + { + if (pass_count >= 3) + SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!"); + + reset(); + + buffer.reset(); + + emit_header(); + emit_resources(); + emit_extension_workarounds(get_execution_model()); + + emit_function(get(ir.default_entry_point), Bitset()); + + pass_count++; + } while (is_forcing_recompilation()); + + // Implement the interlocked wrapper function at the end. + // The body was implemented in lieu of main(). + if (interlocked_is_complex) + { + statement("void main()"); + begin_scope(); + statement("// Interlocks were used in a way not compatible with GLSL, this is very slow."); + if (options.es) + statement("beginInvocationInterlockNV();"); + else + statement("beginInvocationInterlockARB();"); + statement("spvMainInterlockedBody();"); + if (options.es) + statement("endInvocationInterlockNV();"); + else + statement("endInvocationInterlockARB();"); + end_scope(); + } + + // Entry point in GLSL is always main(). + get_entry_point().name = "main"; + + return buffer.str(); +} + +std::string CompilerGLSL::get_partial_source() +{ + return buffer.str(); +} + +void CompilerGLSL::build_workgroup_size(SmallVector &arguments, const SpecializationConstant &wg_x, + const SpecializationConstant &wg_y, const SpecializationConstant &wg_z) +{ + auto &execution = get_entry_point(); + + if (wg_x.id) + { + if (options.vulkan_semantics) + arguments.push_back(join("local_size_x_id = ", wg_x.constant_id)); + else + arguments.push_back(join("local_size_x = ", get(wg_x.id).specialization_constant_macro_name)); + } + else + arguments.push_back(join("local_size_x = ", execution.workgroup_size.x)); + + if (wg_y.id) + { + if (options.vulkan_semantics) + arguments.push_back(join("local_size_y_id = ", wg_y.constant_id)); + else + arguments.push_back(join("local_size_y = ", get(wg_y.id).specialization_constant_macro_name)); + } + else + arguments.push_back(join("local_size_y = ", execution.workgroup_size.y)); + + if (wg_z.id) + { + if (options.vulkan_semantics) + arguments.push_back(join("local_size_z_id = ", wg_z.constant_id)); + else + arguments.push_back(join("local_size_z = ", get(wg_z.id).specialization_constant_macro_name)); + } + else + arguments.push_back(join("local_size_z = ", execution.workgroup_size.z)); +} + +void CompilerGLSL::request_subgroup_feature(ShaderSubgroupSupportHelper::Feature feature) +{ + if (options.vulkan_semantics) + { + auto khr_extension = ShaderSubgroupSupportHelper::get_KHR_extension_for_feature(feature); + require_extension_internal(ShaderSubgroupSupportHelper::get_extension_name(khr_extension)); + } + else + { + if (!shader_subgroup_supporter.is_feature_requested(feature)) + force_recompile(); + shader_subgroup_supporter.request_feature(feature); + } +} + +void CompilerGLSL::emit_header() +{ + auto &execution = get_entry_point(); + statement("#version ", options.version, options.es && options.version > 100 ? " es" : ""); + + if (!options.es && options.version < 420) + { + // Needed for binding = # on UBOs, etc. + if (options.enable_420pack_extension) + { + statement("#ifdef GL_ARB_shading_language_420pack"); + statement("#extension GL_ARB_shading_language_420pack : require"); + statement("#endif"); + } + // Needed for: layout(early_fragment_tests) in; + if (execution.flags.get(ExecutionModeEarlyFragmentTests)) + require_extension_internal("GL_ARB_shader_image_load_store"); + } + + // Needed for: layout(post_depth_coverage) in; + if (execution.flags.get(ExecutionModePostDepthCoverage)) + require_extension_internal("GL_ARB_post_depth_coverage"); + + // Needed for: layout({pixel,sample}_interlock_[un]ordered) in; + if (execution.flags.get(ExecutionModePixelInterlockOrderedEXT) || + execution.flags.get(ExecutionModePixelInterlockUnorderedEXT) || + execution.flags.get(ExecutionModeSampleInterlockOrderedEXT) || + execution.flags.get(ExecutionModeSampleInterlockUnorderedEXT)) + { + if (options.es) + { + if (options.version < 310) + SPIRV_CROSS_THROW("At least ESSL 3.10 required for fragment shader interlock."); + require_extension_internal("GL_NV_fragment_shader_interlock"); + } + else + { + if (options.version < 420) + require_extension_internal("GL_ARB_shader_image_load_store"); + require_extension_internal("GL_ARB_fragment_shader_interlock"); + } + } + + for (auto &ext : forced_extensions) + { + if (ext == "GL_EXT_shader_explicit_arithmetic_types_float16") + { + // Special case, this extension has a potential fallback to another vendor extension in normal GLSL. + // GL_AMD_gpu_shader_half_float is a superset, so try that first. + statement("#if defined(GL_AMD_gpu_shader_half_float)"); + statement("#extension GL_AMD_gpu_shader_half_float : require"); + if (!options.vulkan_semantics) + { + statement("#elif defined(GL_NV_gpu_shader5)"); + statement("#extension GL_NV_gpu_shader5 : require"); + } + else + { + statement("#elif defined(GL_EXT_shader_explicit_arithmetic_types_float16)"); + statement("#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require"); + } + statement("#else"); + statement("#error No extension available for FP16."); + statement("#endif"); + } + else if (ext == "GL_EXT_shader_explicit_arithmetic_types_int16") + { + if (options.vulkan_semantics) + statement("#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require"); + else + { + statement("#if defined(GL_AMD_gpu_shader_int16)"); + statement("#extension GL_AMD_gpu_shader_int16 : require"); + statement("#else"); + statement("#error No extension available for Int16."); + statement("#endif"); + } + } + else if (ext == "GL_ARB_post_depth_coverage") + { + if (options.es) + statement("#extension GL_EXT_post_depth_coverage : require"); + else + { + statement("#if defined(GL_ARB_post_depth_coverge)"); + statement("#extension GL_ARB_post_depth_coverage : require"); + statement("#else"); + statement("#extension GL_EXT_post_depth_coverage : require"); + statement("#endif"); + } + } + else if (!options.vulkan_semantics && ext == "GL_ARB_shader_draw_parameters") + { + // Soft-enable this extension on plain GLSL. + statement("#ifdef ", ext); + statement("#extension ", ext, " : enable"); + statement("#endif"); + } + else + statement("#extension ", ext, " : require"); + } + + if (!options.vulkan_semantics) + { + using Supp = ShaderSubgroupSupportHelper; + auto result = shader_subgroup_supporter.resolve(); + + for (uint32_t feature_index = 0; feature_index < Supp::FeatureCount; feature_index++) + { + auto feature = static_cast(feature_index); + if (!shader_subgroup_supporter.is_feature_requested(feature)) + continue; + + auto exts = Supp::get_candidates_for_feature(feature, result); + if (exts.empty()) + continue; + + statement(""); + + for (auto &ext : exts) + { + const char *name = Supp::get_extension_name(ext); + const char *extra_predicate = Supp::get_extra_required_extension_predicate(ext); + auto extra_names = Supp::get_extra_required_extension_names(ext); + statement(&ext != &exts.front() ? "#elif" : "#if", " defined(", name, ")", + (*extra_predicate != '\0' ? " && " : ""), extra_predicate); + for (const auto &e : extra_names) + statement("#extension ", e, " : enable"); + statement("#extension ", name, " : require"); + } + + if (!Supp::can_feature_be_implemented_without_extensions(feature)) + { + statement("#else"); + statement("#error No extensions available to emulate requested subgroup feature."); + } + + statement("#endif"); + } + } + + for (auto &header : header_lines) + statement(header); + + SmallVector inputs; + SmallVector outputs; + + switch (execution.model) + { + case ExecutionModelGeometry: + if ((execution.flags.get(ExecutionModeInvocations)) && execution.invocations != 1) + inputs.push_back(join("invocations = ", execution.invocations)); + if (execution.flags.get(ExecutionModeInputPoints)) + inputs.push_back("points"); + if (execution.flags.get(ExecutionModeInputLines)) + inputs.push_back("lines"); + if (execution.flags.get(ExecutionModeInputLinesAdjacency)) + inputs.push_back("lines_adjacency"); + if (execution.flags.get(ExecutionModeTriangles)) + inputs.push_back("triangles"); + if (execution.flags.get(ExecutionModeInputTrianglesAdjacency)) + inputs.push_back("triangles_adjacency"); + + if (!execution.geometry_passthrough) + { + // For passthrough, these are implies and cannot be declared in shader. + outputs.push_back(join("max_vertices = ", execution.output_vertices)); + if (execution.flags.get(ExecutionModeOutputTriangleStrip)) + outputs.push_back("triangle_strip"); + if (execution.flags.get(ExecutionModeOutputPoints)) + outputs.push_back("points"); + if (execution.flags.get(ExecutionModeOutputLineStrip)) + outputs.push_back("line_strip"); + } + break; + + case ExecutionModelTessellationControl: + if (execution.flags.get(ExecutionModeOutputVertices)) + outputs.push_back(join("vertices = ", execution.output_vertices)); + break; + + case ExecutionModelTessellationEvaluation: + if (execution.flags.get(ExecutionModeQuads)) + inputs.push_back("quads"); + if (execution.flags.get(ExecutionModeTriangles)) + inputs.push_back("triangles"); + if (execution.flags.get(ExecutionModeIsolines)) + inputs.push_back("isolines"); + if (execution.flags.get(ExecutionModePointMode)) + inputs.push_back("point_mode"); + + if (!execution.flags.get(ExecutionModeIsolines)) + { + if (execution.flags.get(ExecutionModeVertexOrderCw)) + inputs.push_back("cw"); + if (execution.flags.get(ExecutionModeVertexOrderCcw)) + inputs.push_back("ccw"); + } + + if (execution.flags.get(ExecutionModeSpacingFractionalEven)) + inputs.push_back("fractional_even_spacing"); + if (execution.flags.get(ExecutionModeSpacingFractionalOdd)) + inputs.push_back("fractional_odd_spacing"); + if (execution.flags.get(ExecutionModeSpacingEqual)) + inputs.push_back("equal_spacing"); + break; + + case ExecutionModelGLCompute: + { + if (execution.workgroup_size.constant != 0) + { + SpecializationConstant wg_x, wg_y, wg_z; + get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + // If there are any spec constants on legacy GLSL, defer declaration, we need to set up macro + // declarations before we can emit the work group size. + if (options.vulkan_semantics || + ((wg_x.id == ConstantID(0)) && (wg_y.id == ConstantID(0)) && (wg_z.id == ConstantID(0)))) + build_workgroup_size(inputs, wg_x, wg_y, wg_z); + } + else + { + inputs.push_back(join("local_size_x = ", execution.workgroup_size.x)); + inputs.push_back(join("local_size_y = ", execution.workgroup_size.y)); + inputs.push_back(join("local_size_z = ", execution.workgroup_size.z)); + } + break; + } + + case ExecutionModelFragment: + if (options.es) + { + switch (options.fragment.default_float_precision) + { + case Options::Lowp: + statement("precision lowp float;"); + break; + + case Options::Mediump: + statement("precision mediump float;"); + break; + + case Options::Highp: + statement("precision highp float;"); + break; + + default: + break; + } + + switch (options.fragment.default_int_precision) + { + case Options::Lowp: + statement("precision lowp int;"); + break; + + case Options::Mediump: + statement("precision mediump int;"); + break; + + case Options::Highp: + statement("precision highp int;"); + break; + + default: + break; + } + } + + if (execution.flags.get(ExecutionModeEarlyFragmentTests)) + inputs.push_back("early_fragment_tests"); + if (execution.flags.get(ExecutionModePostDepthCoverage)) + inputs.push_back("post_depth_coverage"); + + if (execution.flags.get(ExecutionModePixelInterlockOrderedEXT)) + inputs.push_back("pixel_interlock_ordered"); + else if (execution.flags.get(ExecutionModePixelInterlockUnorderedEXT)) + inputs.push_back("pixel_interlock_unordered"); + else if (execution.flags.get(ExecutionModeSampleInterlockOrderedEXT)) + inputs.push_back("sample_interlock_ordered"); + else if (execution.flags.get(ExecutionModeSampleInterlockUnorderedEXT)) + inputs.push_back("sample_interlock_unordered"); + + if (!options.es && execution.flags.get(ExecutionModeDepthGreater)) + statement("layout(depth_greater) out float gl_FragDepth;"); + else if (!options.es && execution.flags.get(ExecutionModeDepthLess)) + statement("layout(depth_less) out float gl_FragDepth;"); + + break; + + default: + break; + } + + if (!inputs.empty()) + statement("layout(", merge(inputs), ") in;"); + if (!outputs.empty()) + statement("layout(", merge(outputs), ") out;"); + + statement(""); +} + +bool CompilerGLSL::type_is_empty(const SPIRType &type) +{ + return type.basetype == SPIRType::Struct && type.member_types.empty(); +} + +void CompilerGLSL::emit_struct(SPIRType &type) +{ + // Struct types can be stamped out multiple times + // with just different offsets, matrix layouts, etc ... + // Type-punning with these types is legal, which complicates things + // when we are storing struct and array types in an SSBO for example. + // If the type master is packed however, we can no longer assume that the struct declaration will be redundant. + if (type.type_alias != TypeID(0) && + !has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + return; + + add_resource_name(type.self); + auto name = type_to_glsl(type); + + statement(!backend.explicit_struct_type ? "struct " : "", name); + begin_scope(); + + type.member_name_cache.clear(); + + uint32_t i = 0; + bool emitted = false; + for (auto &member : type.member_types) + { + add_member_name(type, i); + emit_struct_member(type, member, i); + i++; + emitted = true; + } + + // Don't declare empty structs in GLSL, this is not allowed. + if (type_is_empty(type) && !backend.supports_empty_struct) + { + statement("int empty_struct_member;"); + emitted = true; + } + + if (has_extended_decoration(type.self, SPIRVCrossDecorationPaddingTarget)) + emit_struct_padding_target(type); + + end_scope_decl(); + + if (emitted) + statement(""); +} + +string CompilerGLSL::to_interpolation_qualifiers(const Bitset &flags) +{ + string res; + //if (flags & (1ull << DecorationSmooth)) + // res += "smooth "; + if (flags.get(DecorationFlat)) + res += "flat "; + if (flags.get(DecorationNoPerspective)) + res += "noperspective "; + if (flags.get(DecorationCentroid)) + res += "centroid "; + if (flags.get(DecorationPatch)) + res += "patch "; + if (flags.get(DecorationSample)) + res += "sample "; + if (flags.get(DecorationInvariant)) + res += "invariant "; + if (flags.get(DecorationExplicitInterpAMD)) + res += "__explicitInterpAMD "; + + return res; +} + +string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index) +{ + if (is_legacy()) + return ""; + + bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + if (!is_block) + return ""; + + auto &memb = ir.meta[type.self].members; + if (index >= memb.size()) + return ""; + auto &dec = memb[index]; + + SmallVector attr; + + if (has_member_decoration(type.self, index, DecorationPassthroughNV)) + attr.push_back("passthrough"); + + // We can only apply layouts on members in block interfaces. + // This is a bit problematic because in SPIR-V decorations are applied on the struct types directly. + // This is not supported on GLSL, so we have to make the assumption that if a struct within our buffer block struct + // has a decoration, it was originally caused by a top-level layout() qualifier in GLSL. + // + // We would like to go from (SPIR-V style): + // + // struct Foo { layout(row_major) mat4 matrix; }; + // buffer UBO { Foo foo; }; + // + // to + // + // struct Foo { mat4 matrix; }; // GLSL doesn't support any layout shenanigans in raw struct declarations. + // buffer UBO { layout(row_major) Foo foo; }; // Apply the layout on top-level. + auto flags = combined_decoration_for_member(type, index); + + if (flags.get(DecorationRowMajor)) + attr.push_back("row_major"); + // We don't emit any global layouts, so column_major is default. + //if (flags & (1ull << DecorationColMajor)) + // attr.push_back("column_major"); + + if (dec.decoration_flags.get(DecorationLocation) && can_use_io_location(type.storage, true)) + attr.push_back(join("location = ", dec.location)); + + // Can only declare component if we can declare location. + if (dec.decoration_flags.get(DecorationComponent) && can_use_io_location(type.storage, true)) + { + if (!options.es) + { + if (options.version < 440 && options.version >= 140) + require_extension_internal("GL_ARB_enhanced_layouts"); + else if (options.version < 140) + SPIRV_CROSS_THROW("Component decoration is not supported in targets below GLSL 1.40."); + attr.push_back(join("component = ", dec.component)); + } + else + SPIRV_CROSS_THROW("Component decoration is not supported in ES targets."); + } + + // SPIRVCrossDecorationPacked is set by layout_for_variable earlier to mark that we need to emit offset qualifiers. + // This is only done selectively in GLSL as needed. + if (has_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset) && + dec.decoration_flags.get(DecorationOffset)) + attr.push_back(join("offset = ", dec.offset)); + else if (type.storage == StorageClassOutput && dec.decoration_flags.get(DecorationOffset)) + attr.push_back(join("xfb_offset = ", dec.offset)); + + if (attr.empty()) + return ""; + + string res = "layout("; + res += merge(attr); + res += ") "; + return res; +} + +const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format) +{ + if (options.es && is_desktop_only_format(format)) + SPIRV_CROSS_THROW("Attempting to use image format not supported in ES profile."); + + switch (format) + { + case ImageFormatRgba32f: + return "rgba32f"; + case ImageFormatRgba16f: + return "rgba16f"; + case ImageFormatR32f: + return "r32f"; + case ImageFormatRgba8: + return "rgba8"; + case ImageFormatRgba8Snorm: + return "rgba8_snorm"; + case ImageFormatRg32f: + return "rg32f"; + case ImageFormatRg16f: + return "rg16f"; + case ImageFormatRgba32i: + return "rgba32i"; + case ImageFormatRgba16i: + return "rgba16i"; + case ImageFormatR32i: + return "r32i"; + case ImageFormatRgba8i: + return "rgba8i"; + case ImageFormatRg32i: + return "rg32i"; + case ImageFormatRg16i: + return "rg16i"; + case ImageFormatRgba32ui: + return "rgba32ui"; + case ImageFormatRgba16ui: + return "rgba16ui"; + case ImageFormatR32ui: + return "r32ui"; + case ImageFormatRgba8ui: + return "rgba8ui"; + case ImageFormatRg32ui: + return "rg32ui"; + case ImageFormatRg16ui: + return "rg16ui"; + case ImageFormatR11fG11fB10f: + return "r11f_g11f_b10f"; + case ImageFormatR16f: + return "r16f"; + case ImageFormatRgb10A2: + return "rgb10_a2"; + case ImageFormatR8: + return "r8"; + case ImageFormatRg8: + return "rg8"; + case ImageFormatR16: + return "r16"; + case ImageFormatRg16: + return "rg16"; + case ImageFormatRgba16: + return "rgba16"; + case ImageFormatR16Snorm: + return "r16_snorm"; + case ImageFormatRg16Snorm: + return "rg16_snorm"; + case ImageFormatRgba16Snorm: + return "rgba16_snorm"; + case ImageFormatR8Snorm: + return "r8_snorm"; + case ImageFormatRg8Snorm: + return "rg8_snorm"; + case ImageFormatR8ui: + return "r8ui"; + case ImageFormatRg8ui: + return "rg8ui"; + case ImageFormatR16ui: + return "r16ui"; + case ImageFormatRgb10a2ui: + return "rgb10_a2ui"; + case ImageFormatR8i: + return "r8i"; + case ImageFormatRg8i: + return "rg8i"; + case ImageFormatR16i: + return "r16i"; + default: + case ImageFormatUnknown: + return nullptr; + } +} + +uint32_t CompilerGLSL::type_to_packed_base_size(const SPIRType &type, BufferPackingStandard) +{ + switch (type.basetype) + { + case SPIRType::Double: + case SPIRType::Int64: + case SPIRType::UInt64: + return 8; + case SPIRType::Float: + case SPIRType::Int: + case SPIRType::UInt: + return 4; + case SPIRType::Half: + case SPIRType::Short: + case SPIRType::UShort: + return 2; + case SPIRType::SByte: + case SPIRType::UByte: + return 1; + + default: + SPIRV_CROSS_THROW("Unrecognized type in type_to_packed_base_size."); + } +} + +uint32_t CompilerGLSL::type_to_packed_alignment(const SPIRType &type, const Bitset &flags, + BufferPackingStandard packing) +{ + // If using PhysicalStorageBufferEXT storage class, this is a pointer, + // and is 64-bit. + if (type.storage == StorageClassPhysicalStorageBufferEXT) + { + if (!type.pointer) + SPIRV_CROSS_THROW("Types in PhysicalStorageBufferEXT must be pointers."); + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64EXT) + { + if (packing_is_vec4_padded(packing) && type_is_array_of_pointers(type)) + return 16; + else + return 8; + } + else + SPIRV_CROSS_THROW("AddressingModelPhysicalStorageBuffer64EXT must be used for PhysicalStorageBufferEXT."); + } + + if (!type.array.empty()) + { + uint32_t minimum_alignment = 1; + if (packing_is_vec4_padded(packing)) + minimum_alignment = 16; + + auto *tmp = &get(type.parent_type); + while (!tmp->array.empty()) + tmp = &get(tmp->parent_type); + + // Get the alignment of the base type, then maybe round up. + return max(minimum_alignment, type_to_packed_alignment(*tmp, flags, packing)); + } + + if (type.basetype == SPIRType::Struct) + { + // Rule 9. Structs alignments are maximum alignment of its members. + uint32_t alignment = 1; + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + auto member_flags = ir.meta[type.self].members[i].decoration_flags; + alignment = + max(alignment, type_to_packed_alignment(get(type.member_types[i]), member_flags, packing)); + } + + // In std140, struct alignment is rounded up to 16. + if (packing_is_vec4_padded(packing)) + alignment = max(alignment, 16u); + + return alignment; + } + else + { + const uint32_t base_alignment = type_to_packed_base_size(type, packing); + + // Alignment requirement for scalar block layout is always the alignment for the most basic component. + if (packing_is_scalar(packing)) + return base_alignment; + + // Vectors are *not* aligned in HLSL, but there's an extra rule where vectors cannot straddle + // a vec4, this is handled outside since that part knows our current offset. + if (type.columns == 1 && packing_is_hlsl(packing)) + return base_alignment; + + // From 7.6.2.2 in GL 4.5 core spec. + // Rule 1 + if (type.vecsize == 1 && type.columns == 1) + return base_alignment; + + // Rule 2 + if ((type.vecsize == 2 || type.vecsize == 4) && type.columns == 1) + return type.vecsize * base_alignment; + + // Rule 3 + if (type.vecsize == 3 && type.columns == 1) + return 4 * base_alignment; + + // Rule 4 implied. Alignment does not change in std430. + + // Rule 5. Column-major matrices are stored as arrays of + // vectors. + if (flags.get(DecorationColMajor) && type.columns > 1) + { + if (packing_is_vec4_padded(packing)) + return 4 * base_alignment; + else if (type.vecsize == 3) + return 4 * base_alignment; + else + return type.vecsize * base_alignment; + } + + // Rule 6 implied. + + // Rule 7. + if (flags.get(DecorationRowMajor) && type.vecsize > 1) + { + if (packing_is_vec4_padded(packing)) + return 4 * base_alignment; + else if (type.columns == 3) + return 4 * base_alignment; + else + return type.columns * base_alignment; + } + + // Rule 8 implied. + } + + SPIRV_CROSS_THROW("Did not find suitable rule for type. Bogus decorations?"); +} + +uint32_t CompilerGLSL::type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, + BufferPackingStandard packing) +{ + // Array stride is equal to aligned size of the underlying type. + uint32_t parent = type.parent_type; + assert(parent); + + auto &tmp = get(parent); + + uint32_t size = type_to_packed_size(tmp, flags, packing); + uint32_t alignment = type_to_packed_alignment(type, flags, packing); + return (size + alignment - 1) & ~(alignment - 1); +} + +uint32_t CompilerGLSL::type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing) +{ + if (!type.array.empty()) + { + uint32_t packed_size = to_array_size_literal(type) * type_to_packed_array_stride(type, flags, packing); + + // For arrays of vectors and matrices in HLSL, the last element has a size which depends on its vector size, + // so that it is possible to pack other vectors into the last element. + if (packing_is_hlsl(packing) && type.basetype != SPIRType::Struct) + packed_size -= (4 - type.vecsize) * (type.width / 8); + + return packed_size; + } + + // If using PhysicalStorageBufferEXT storage class, this is a pointer, + // and is 64-bit. + if (type.storage == StorageClassPhysicalStorageBufferEXT) + { + if (!type.pointer) + SPIRV_CROSS_THROW("Types in PhysicalStorageBufferEXT must be pointers."); + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64EXT) + return 8; + else + SPIRV_CROSS_THROW("AddressingModelPhysicalStorageBuffer64EXT must be used for PhysicalStorageBufferEXT."); + } + + uint32_t size = 0; + + if (type.basetype == SPIRType::Struct) + { + uint32_t pad_alignment = 1; + + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + auto member_flags = ir.meta[type.self].members[i].decoration_flags; + auto &member_type = get(type.member_types[i]); + + uint32_t packed_alignment = type_to_packed_alignment(member_type, member_flags, packing); + uint32_t alignment = max(packed_alignment, pad_alignment); + + // The next member following a struct member is aligned to the base alignment of the struct that came before. + // GL 4.5 spec, 7.6.2.2. + if (member_type.basetype == SPIRType::Struct) + pad_alignment = packed_alignment; + else + pad_alignment = 1; + + size = (size + alignment - 1) & ~(alignment - 1); + size += type_to_packed_size(member_type, member_flags, packing); + } + } + else + { + const uint32_t base_alignment = type_to_packed_base_size(type, packing); + + if (packing_is_scalar(packing)) + { + size = type.vecsize * type.columns * base_alignment; + } + else + { + if (type.columns == 1) + size = type.vecsize * base_alignment; + + if (flags.get(DecorationColMajor) && type.columns > 1) + { + if (packing_is_vec4_padded(packing)) + size = type.columns * 4 * base_alignment; + else if (type.vecsize == 3) + size = type.columns * 4 * base_alignment; + else + size = type.columns * type.vecsize * base_alignment; + } + + if (flags.get(DecorationRowMajor) && type.vecsize > 1) + { + if (packing_is_vec4_padded(packing)) + size = type.vecsize * 4 * base_alignment; + else if (type.columns == 3) + size = type.vecsize * 4 * base_alignment; + else + size = type.vecsize * type.columns * base_alignment; + } + + // For matrices in HLSL, the last element has a size which depends on its vector size, + // so that it is possible to pack other vectors into the last element. + if (packing_is_hlsl(packing) && type.columns > 1) + size -= (4 - type.vecsize) * (type.width / 8); + } + } + + return size; +} + +bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, + uint32_t *failed_validation_index, uint32_t start_offset, + uint32_t end_offset) +{ + // This is very tricky and error prone, but try to be exhaustive and correct here. + // SPIR-V doesn't directly say if we're using std430 or std140. + // SPIR-V communicates this using Offset and ArrayStride decorations (which is what really matters), + // so we have to try to infer whether or not the original GLSL source was std140 or std430 based on this information. + // We do not have to consider shared or packed since these layouts are not allowed in Vulkan SPIR-V (they are useless anyways, and custom offsets would do the same thing). + // + // It is almost certain that we're using std430, but it gets tricky with arrays in particular. + // We will assume std430, but infer std140 if we can prove the struct is not compliant with std430. + // + // The only two differences between std140 and std430 are related to padding alignment/array stride + // in arrays and structs. In std140 they take minimum vec4 alignment. + // std430 only removes the vec4 requirement. + + uint32_t offset = 0; + uint32_t pad_alignment = 1; + + bool is_top_level_block = + has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); + + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + auto &memb_type = get(type.member_types[i]); + auto member_flags = ir.meta[type.self].members[i].decoration_flags; + + // Verify alignment rules. + uint32_t packed_alignment = type_to_packed_alignment(memb_type, member_flags, packing); + + // This is a rather dirty workaround to deal with some cases of OpSpecConstantOp used as array size, e.g: + // layout(constant_id = 0) const int s = 10; + // const int S = s + 5; // SpecConstantOp + // buffer Foo { int data[S]; }; // <-- Very hard for us to deduce a fixed value here, + // we would need full implementation of compile-time constant folding. :( + // If we are the last member of a struct, there might be cases where the actual size of that member is irrelevant + // for our analysis (e.g. unsized arrays). + // This lets us simply ignore that there are spec constant op sized arrays in our buffers. + // Querying size of this member will fail, so just don't call it unless we have to. + // + // This is likely "best effort" we can support without going into unacceptably complicated workarounds. + bool member_can_be_unsized = + is_top_level_block && size_t(i + 1) == type.member_types.size() && !memb_type.array.empty(); + + uint32_t packed_size = 0; + if (!member_can_be_unsized || packing_is_hlsl(packing)) + packed_size = type_to_packed_size(memb_type, member_flags, packing); + + // We only need to care about this if we have non-array types which can straddle the vec4 boundary. + if (packing_is_hlsl(packing)) + { + // If a member straddles across a vec4 boundary, alignment is actually vec4. + uint32_t begin_word = offset / 16; + uint32_t end_word = (offset + packed_size - 1) / 16; + if (begin_word != end_word) + packed_alignment = max(packed_alignment, 16u); + } + + uint32_t actual_offset = type_struct_member_offset(type, i); + // Field is not in the specified range anymore and we can ignore any further fields. + if (actual_offset >= end_offset) + break; + + uint32_t alignment = max(packed_alignment, pad_alignment); + offset = (offset + alignment - 1) & ~(alignment - 1); + + // The next member following a struct member is aligned to the base alignment of the struct that came before. + // GL 4.5 spec, 7.6.2.2. + if (memb_type.basetype == SPIRType::Struct && !memb_type.pointer) + pad_alignment = packed_alignment; + else + pad_alignment = 1; + + // Only care about packing if we are in the given range + if (actual_offset >= start_offset) + { + // We only care about offsets in std140, std430, etc ... + // For EnhancedLayout variants, we have the flexibility to choose our own offsets. + if (!packing_has_flexible_offset(packing)) + { + if (actual_offset != offset) // This cannot be the packing we're looking for. + { + if (failed_validation_index) + *failed_validation_index = i; + return false; + } + } + else if ((actual_offset & (alignment - 1)) != 0) + { + // We still need to verify that alignment rules are observed, even if we have explicit offset. + if (failed_validation_index) + *failed_validation_index = i; + return false; + } + + // Verify array stride rules. + if (!memb_type.array.empty() && type_to_packed_array_stride(memb_type, member_flags, packing) != + type_struct_member_array_stride(type, i)) + { + if (failed_validation_index) + *failed_validation_index = i; + return false; + } + + // Verify that sub-structs also follow packing rules. + // We cannot use enhanced layouts on substructs, so they better be up to spec. + auto substruct_packing = packing_to_substruct_packing(packing); + + if (!memb_type.pointer && !memb_type.member_types.empty() && + !buffer_is_packing_standard(memb_type, substruct_packing)) + { + if (failed_validation_index) + *failed_validation_index = i; + return false; + } + } + + // Bump size. + offset = actual_offset + packed_size; + } + + return true; +} + +bool CompilerGLSL::can_use_io_location(StorageClass storage, bool block) +{ + // Location specifiers are must have in SPIR-V, but they aren't really supported in earlier versions of GLSL. + // Be very explicit here about how to solve the issue. + if ((get_execution_model() != ExecutionModelVertex && storage == StorageClassInput) || + (get_execution_model() != ExecutionModelFragment && storage == StorageClassOutput)) + { + uint32_t minimum_desktop_version = block ? 440 : 410; + // ARB_enhanced_layouts vs ARB_separate_shader_objects ... + + if (!options.es && options.version < minimum_desktop_version && !options.separate_shader_objects) + return false; + else if (options.es && options.version < 310) + return false; + } + + if ((get_execution_model() == ExecutionModelVertex && storage == StorageClassInput) || + (get_execution_model() == ExecutionModelFragment && storage == StorageClassOutput)) + { + if (options.es && options.version < 300) + return false; + else if (!options.es && options.version < 330) + return false; + } + + if (storage == StorageClassUniform || storage == StorageClassUniformConstant || storage == StorageClassPushConstant) + { + if (options.es && options.version < 310) + return false; + else if (!options.es && options.version < 430) + return false; + } + + return true; +} + +string CompilerGLSL::layout_for_variable(const SPIRVariable &var) +{ + // FIXME: Come up with a better solution for when to disable layouts. + // Having layouts depend on extensions as well as which types + // of layouts are used. For now, the simple solution is to just disable + // layouts for legacy versions. + if (is_legacy()) + return ""; + + if (subpass_input_is_framebuffer_fetch(var.self)) + return ""; + + SmallVector attr; + + auto &type = get(var.basetype); + auto &flags = get_decoration_bitset(var.self); + auto &typeflags = get_decoration_bitset(type.self); + + if (flags.get(DecorationPassthroughNV)) + attr.push_back("passthrough"); + + if (options.vulkan_semantics && var.storage == StorageClassPushConstant) + attr.push_back("push_constant"); + else if (var.storage == StorageClassShaderRecordBufferNV) + attr.push_back("shaderRecordNV"); + + if (flags.get(DecorationRowMajor)) + attr.push_back("row_major"); + if (flags.get(DecorationColMajor)) + attr.push_back("column_major"); + + if (options.vulkan_semantics) + { + if (flags.get(DecorationInputAttachmentIndex)) + attr.push_back(join("input_attachment_index = ", get_decoration(var.self, DecorationInputAttachmentIndex))); + } + + bool is_block = has_decoration(type.self, DecorationBlock); + if (flags.get(DecorationLocation) && can_use_io_location(var.storage, is_block)) + { + Bitset combined_decoration; + for (uint32_t i = 0; i < ir.meta[type.self].members.size(); i++) + combined_decoration.merge_or(combined_decoration_for_member(type, i)); + + // If our members have location decorations, we don't need to + // emit location decorations at the top as well (looks weird). + if (!combined_decoration.get(DecorationLocation)) + attr.push_back(join("location = ", get_decoration(var.self, DecorationLocation))); + } + + // Transform feedback + bool uses_enhanced_layouts = false; + if (is_block && var.storage == StorageClassOutput) + { + // For blocks, there is a restriction where xfb_stride/xfb_buffer must only be declared on the block itself, + // since all members must match the same xfb_buffer. The only thing we will declare for members of the block + // is the xfb_offset. + uint32_t member_count = uint32_t(type.member_types.size()); + bool have_xfb_buffer_stride = false; + bool have_any_xfb_offset = false; + bool have_geom_stream = false; + uint32_t xfb_stride = 0, xfb_buffer = 0, geom_stream = 0; + + if (flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride)) + { + have_xfb_buffer_stride = true; + xfb_buffer = get_decoration(var.self, DecorationXfbBuffer); + xfb_stride = get_decoration(var.self, DecorationXfbStride); + } + + if (flags.get(DecorationStream)) + { + have_geom_stream = true; + geom_stream = get_decoration(var.self, DecorationStream); + } + + // Verify that none of the members violate our assumption. + for (uint32_t i = 0; i < member_count; i++) + { + if (has_member_decoration(type.self, i, DecorationStream)) + { + uint32_t member_geom_stream = get_member_decoration(type.self, i, DecorationStream); + if (have_geom_stream && member_geom_stream != geom_stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = member_geom_stream; + } + + // Only members with an Offset decoration participate in XFB. + if (!has_member_decoration(type.self, i, DecorationOffset)) + continue; + have_any_xfb_offset = true; + + if (has_member_decoration(type.self, i, DecorationXfbBuffer)) + { + uint32_t buffer_index = get_member_decoration(type.self, i, DecorationXfbBuffer); + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + } + + if (has_member_decoration(type.self, i, DecorationXfbStride)) + { + uint32_t stride = get_member_decoration(type.self, i, DecorationXfbStride); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbStride mismatch."); + have_xfb_buffer_stride = true; + xfb_stride = stride; + } + } + + if (have_xfb_buffer_stride && have_any_xfb_offset) + { + attr.push_back(join("xfb_buffer = ", xfb_buffer)); + attr.push_back(join("xfb_stride = ", xfb_stride)); + uses_enhanced_layouts = true; + } + + if (have_geom_stream) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", get_decoration(var.self, DecorationStream))); + } + } + else if (var.storage == StorageClassOutput) + { + if (flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride) && flags.get(DecorationOffset)) + { + // XFB for standalone variables, we can emit all decorations. + attr.push_back(join("xfb_buffer = ", get_decoration(var.self, DecorationXfbBuffer))); + attr.push_back(join("xfb_stride = ", get_decoration(var.self, DecorationXfbStride))); + attr.push_back(join("xfb_offset = ", get_decoration(var.self, DecorationOffset))); + uses_enhanced_layouts = true; + } + + if (flags.get(DecorationStream)) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", get_decoration(var.self, DecorationStream))); + } + } + + // Can only declare Component if we can declare location. + if (flags.get(DecorationComponent) && can_use_io_location(var.storage, is_block)) + { + uses_enhanced_layouts = true; + attr.push_back(join("component = ", get_decoration(var.self, DecorationComponent))); + } + + if (uses_enhanced_layouts) + { + if (!options.es) + { + if (options.version < 440 && options.version >= 140) + require_extension_internal("GL_ARB_enhanced_layouts"); + else if (options.version < 140) + SPIRV_CROSS_THROW("GL_ARB_enhanced_layouts is not supported in targets below GLSL 1.40."); + if (!options.es && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + } + else if (options.es) + SPIRV_CROSS_THROW("GL_ARB_enhanced_layouts is not supported in ESSL."); + } + + if (flags.get(DecorationIndex)) + attr.push_back(join("index = ", get_decoration(var.self, DecorationIndex))); + + // Do not emit set = decoration in regular GLSL output, but + // we need to preserve it in Vulkan GLSL mode. + if (var.storage != StorageClassPushConstant && var.storage != StorageClassShaderRecordBufferNV) + { + if (flags.get(DecorationDescriptorSet) && options.vulkan_semantics) + attr.push_back(join("set = ", get_decoration(var.self, DecorationDescriptorSet))); + } + + bool push_constant_block = options.vulkan_semantics && var.storage == StorageClassPushConstant; + bool ssbo_block = var.storage == StorageClassStorageBuffer || var.storage == StorageClassShaderRecordBufferNV || + (var.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock)); + bool emulated_ubo = var.storage == StorageClassPushConstant && options.emit_push_constant_as_uniform_buffer; + bool ubo_block = var.storage == StorageClassUniform && typeflags.get(DecorationBlock); + + // GL 3.0/GLSL 1.30 is not considered legacy, but it doesn't have UBOs ... + bool can_use_buffer_blocks = (options.es && options.version >= 300) || (!options.es && options.version >= 140); + + // pretend no UBOs when options say so + if (ubo_block && options.emit_uniform_buffer_as_plain_uniforms) + can_use_buffer_blocks = false; + + bool can_use_binding; + if (options.es) + can_use_binding = options.version >= 310; + else + can_use_binding = options.enable_420pack_extension || (options.version >= 420); + + // Make sure we don't emit binding layout for a classic uniform on GLSL 1.30. + if (!can_use_buffer_blocks && var.storage == StorageClassUniform) + can_use_binding = false; + + if (var.storage == StorageClassShaderRecordBufferNV) + can_use_binding = false; + + if (can_use_binding && flags.get(DecorationBinding)) + attr.push_back(join("binding = ", get_decoration(var.self, DecorationBinding))); + + if (var.storage != StorageClassOutput && flags.get(DecorationOffset)) + attr.push_back(join("offset = ", get_decoration(var.self, DecorationOffset))); + + // Instead of adding explicit offsets for every element here, just assume we're using std140 or std430. + // If SPIR-V does not comply with either layout, we cannot really work around it. + if (can_use_buffer_blocks && (ubo_block || emulated_ubo)) + { + attr.push_back(buffer_to_packing_standard(type, false)); + } + else if (can_use_buffer_blocks && (push_constant_block || ssbo_block)) + { + attr.push_back(buffer_to_packing_standard(type, true)); + } + + // For images, the type itself adds a layout qualifer. + // Only emit the format for storage images. + if (type.basetype == SPIRType::Image && type.image.sampled == 2) + { + const char *fmt = format_to_glsl(type.image.format); + if (fmt) + attr.push_back(fmt); + } + + if (attr.empty()) + return ""; + + string res = "layout("; + res += merge(attr); + res += ") "; + return res; +} + +string CompilerGLSL::buffer_to_packing_standard(const SPIRType &type, bool support_std430_without_scalar_layout) +{ + if (support_std430_without_scalar_layout && buffer_is_packing_standard(type, BufferPackingStd430)) + return "std430"; + else if (buffer_is_packing_standard(type, BufferPackingStd140)) + return "std140"; + else if (options.vulkan_semantics && buffer_is_packing_standard(type, BufferPackingScalar)) + { + require_extension_internal("GL_EXT_scalar_block_layout"); + return "scalar"; + } + else if (support_std430_without_scalar_layout && + buffer_is_packing_standard(type, BufferPackingStd430EnhancedLayout)) + { + if (options.es && !options.vulkan_semantics) + SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do " + "not support GL_ARB_enhanced_layouts."); + if (!options.es && !options.vulkan_semantics && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + return "std430"; + } + else if (buffer_is_packing_standard(type, BufferPackingStd140EnhancedLayout)) + { + // Fallback time. We might be able to use the ARB_enhanced_layouts to deal with this difference, + // however, we can only use layout(offset) on the block itself, not any substructs, so the substructs better be the appropriate layout. + // Enhanced layouts seem to always work in Vulkan GLSL, so no need for extensions there. + if (options.es && !options.vulkan_semantics) + SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do " + "not support GL_ARB_enhanced_layouts."); + if (!options.es && !options.vulkan_semantics && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + return "std140"; + } + else if (options.vulkan_semantics && buffer_is_packing_standard(type, BufferPackingScalarEnhancedLayout)) + { + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + require_extension_internal("GL_EXT_scalar_block_layout"); + return "scalar"; + } + else if (!support_std430_without_scalar_layout && options.vulkan_semantics && + buffer_is_packing_standard(type, BufferPackingStd430)) + { + // UBOs can support std430 with GL_EXT_scalar_block_layout. + require_extension_internal("GL_EXT_scalar_block_layout"); + return "std430"; + } + else if (!support_std430_without_scalar_layout && options.vulkan_semantics && + buffer_is_packing_standard(type, BufferPackingStd430EnhancedLayout)) + { + // UBOs can support std430 with GL_EXT_scalar_block_layout. + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + require_extension_internal("GL_EXT_scalar_block_layout"); + return "std430"; + } + else + { + SPIRV_CROSS_THROW("Buffer block cannot be expressed as any of std430, std140, scalar, even with enhanced " + "layouts. You can try flattening this block to support a more flexible layout."); + } +} + +void CompilerGLSL::emit_push_constant_block(const SPIRVariable &var) +{ + if (flattened_buffer_blocks.count(var.self)) + emit_buffer_block_flattened(var); + else if (options.vulkan_semantics) + emit_push_constant_block_vulkan(var); + else if (options.emit_push_constant_as_uniform_buffer) + emit_buffer_block_native(var); + else + emit_push_constant_block_glsl(var); +} + +void CompilerGLSL::emit_push_constant_block_vulkan(const SPIRVariable &var) +{ + emit_buffer_block(var); +} + +void CompilerGLSL::emit_push_constant_block_glsl(const SPIRVariable &var) +{ + // OpenGL has no concept of push constant blocks, implement it as a uniform struct. + auto &type = get(var.basetype); + + auto &flags = ir.meta[var.self].decoration.decoration_flags; + flags.clear(DecorationBinding); + flags.clear(DecorationDescriptorSet); + +#if 0 + if (flags & ((1ull << DecorationBinding) | (1ull << DecorationDescriptorSet))) + SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. " + "Remap to location with reflection API first or disable these decorations."); +#endif + + // We're emitting the push constant block as a regular struct, so disable the block qualifier temporarily. + // Otherwise, we will end up emitting layout() qualifiers on naked structs which is not allowed. + auto &block_flags = ir.meta[type.self].decoration.decoration_flags; + bool block_flag = block_flags.get(DecorationBlock); + block_flags.clear(DecorationBlock); + + emit_struct(type); + + if (block_flag) + block_flags.set(DecorationBlock); + + emit_uniform(var); + statement(""); +} + +void CompilerGLSL::emit_buffer_block(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + bool ubo_block = var.storage == StorageClassUniform && has_decoration(type.self, DecorationBlock); + + if (flattened_buffer_blocks.count(var.self)) + emit_buffer_block_flattened(var); + else if (is_legacy() || (!options.es && options.version == 130) || + (ubo_block && options.emit_uniform_buffer_as_plain_uniforms)) + emit_buffer_block_legacy(var); + else + emit_buffer_block_native(var); +} + +void CompilerGLSL::emit_buffer_block_legacy(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + bool ssbo = var.storage == StorageClassStorageBuffer || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + if (ssbo) + SPIRV_CROSS_THROW("SSBOs not supported in legacy targets."); + + // We're emitting the push constant block as a regular struct, so disable the block qualifier temporarily. + // Otherwise, we will end up emitting layout() qualifiers on naked structs which is not allowed. + auto &block_flags = ir.meta[type.self].decoration.decoration_flags; + bool block_flag = block_flags.get(DecorationBlock); + block_flags.clear(DecorationBlock); + emit_struct(type); + if (block_flag) + block_flags.set(DecorationBlock); + emit_uniform(var); + statement(""); +} + +void CompilerGLSL::emit_buffer_reference_block(SPIRType &type, bool forward_declaration) +{ + string buffer_name; + + if (forward_declaration) + { + // Block names should never alias, but from HLSL input they kind of can because block types are reused for UAVs ... + // Allow aliased name since we might be declaring the block twice. Once with buffer reference (forward declared) and one proper declaration. + // The names must match up. + buffer_name = to_name(type.self, false); + + // Shaders never use the block by interface name, so we don't + // have to track this other than updating name caches. + // If we have a collision for any reason, just fallback immediately. + if (ir.meta[type.self].decoration.alias.empty() || + block_ssbo_names.find(buffer_name) != end(block_ssbo_names) || + resource_names.find(buffer_name) != end(resource_names)) + { + buffer_name = join("_", type.self); + } + + // Make sure we get something unique for both global name scope and block name scope. + // See GLSL 4.5 spec: section 4.3.9 for details. + add_variable(block_ssbo_names, resource_names, buffer_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + // We cannot reuse this fallback name in neither global scope (blocked by block_names) nor block name scope. + if (buffer_name.empty()) + buffer_name = join("_", type.self); + + block_names.insert(buffer_name); + block_ssbo_names.insert(buffer_name); + } + else if (type.basetype != SPIRType::Struct) + buffer_name = type_to_glsl(type); + else + buffer_name = to_name(type.self, false); + + if (!forward_declaration) + { + if (type.basetype == SPIRType::Struct) + statement("layout(buffer_reference, ", buffer_to_packing_standard(type, true), ") buffer ", buffer_name); + else + statement("layout(buffer_reference) buffer ", buffer_name); + + begin_scope(); + + if (type.basetype == SPIRType::Struct) + { + type.member_name_cache.clear(); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + emit_struct_member(type, member, i); + i++; + } + } + else + { + auto &pointee_type = get_pointee_type(type); + statement(type_to_glsl(pointee_type), " value", type_to_array_glsl(pointee_type), ";"); + } + + end_scope_decl(); + statement(""); + } + else + { + statement("layout(buffer_reference) buffer ", buffer_name, ";"); + } +} + +void CompilerGLSL::emit_buffer_block_native(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + + Bitset flags = ir.get_buffer_block_flags(var); + bool ssbo = var.storage == StorageClassStorageBuffer || var.storage == StorageClassShaderRecordBufferNV || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + bool is_restrict = ssbo && flags.get(DecorationRestrict); + bool is_writeonly = ssbo && flags.get(DecorationNonReadable); + bool is_readonly = ssbo && flags.get(DecorationNonWritable); + bool is_coherent = ssbo && flags.get(DecorationCoherent); + + // Block names should never alias, but from HLSL input they kind of can because block types are reused for UAVs ... + auto buffer_name = to_name(type.self, false); + + auto &block_namespace = ssbo ? block_ssbo_names : block_ubo_names; + + // Shaders never use the block by interface name, so we don't + // have to track this other than updating name caches. + // If we have a collision for any reason, just fallback immediately. + if (ir.meta[type.self].decoration.alias.empty() || block_namespace.find(buffer_name) != end(block_namespace) || + resource_names.find(buffer_name) != end(resource_names)) + { + buffer_name = get_block_fallback_name(var.self); + } + + // Make sure we get something unique for both global name scope and block name scope. + // See GLSL 4.5 spec: section 4.3.9 for details. + add_variable(block_namespace, resource_names, buffer_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + // We cannot reuse this fallback name in neither global scope (blocked by block_names) nor block name scope. + if (buffer_name.empty()) + buffer_name = join("_", get(var.basetype).self, "_", var.self); + + block_names.insert(buffer_name); + block_namespace.insert(buffer_name); + + // Save for post-reflection later. + declared_block_names[var.self] = buffer_name; + + statement(layout_for_variable(var), is_coherent ? "coherent " : "", is_restrict ? "restrict " : "", + is_writeonly ? "writeonly " : "", is_readonly ? "readonly " : "", ssbo ? "buffer " : "uniform ", + buffer_name); + + begin_scope(); + + type.member_name_cache.clear(); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + emit_struct_member(type, member, i); + i++; + } + + // var.self can be used as a backup name for the block name, + // so we need to make sure we don't disturb the name here on a recompile. + // It will need to be reset if we have to recompile. + preserve_alias_on_reset(var.self); + add_resource_name(var.self); + end_scope_decl(to_name(var.self) + type_to_array_glsl(type)); + statement(""); +} + +void CompilerGLSL::emit_buffer_block_flattened(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + + // Block names should never alias. + auto buffer_name = to_name(type.self, false); + size_t buffer_size = (get_declared_struct_size(type) + 15) / 16; + + SPIRType::BaseType basic_type; + if (get_common_basic_type(type, basic_type)) + { + SPIRType tmp; + tmp.basetype = basic_type; + tmp.vecsize = 4; + if (basic_type != SPIRType::Float && basic_type != SPIRType::Int && basic_type != SPIRType::UInt) + SPIRV_CROSS_THROW("Basic types in a flattened UBO must be float, int or uint."); + + auto flags = ir.get_buffer_block_flags(var); + statement("uniform ", flags_to_qualifiers_glsl(tmp, flags), type_to_glsl(tmp), " ", buffer_name, "[", + buffer_size, "];"); + } + else + SPIRV_CROSS_THROW("All basic types in a flattened block must be the same."); +} + +const char *CompilerGLSL::to_storage_qualifiers_glsl(const SPIRVariable &var) +{ + auto &execution = get_entry_point(); + + if (subpass_input_is_framebuffer_fetch(var.self)) + return ""; + + if (var.storage == StorageClassInput || var.storage == StorageClassOutput) + { + if (is_legacy() && execution.model == ExecutionModelVertex) + return var.storage == StorageClassInput ? "attribute " : "varying "; + else if (is_legacy() && execution.model == ExecutionModelFragment) + return "varying "; // Fragment outputs are renamed so they never hit this case. + else if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput) + { + if (inout_color_attachments.count(get_decoration(var.self, DecorationLocation)) != 0) + return "inout "; + else + return "out "; + } + else + return var.storage == StorageClassInput ? "in " : "out "; + } + else if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform || + var.storage == StorageClassPushConstant) + { + return "uniform "; + } + else if (var.storage == StorageClassRayPayloadNV) + { + return "rayPayloadNV "; + } + else if (var.storage == StorageClassIncomingRayPayloadNV) + { + return "rayPayloadInNV "; + } + else if (var.storage == StorageClassHitAttributeNV) + { + return "hitAttributeNV "; + } + else if (var.storage == StorageClassCallableDataNV) + { + return "callableDataNV "; + } + else if (var.storage == StorageClassIncomingCallableDataNV) + { + return "callableDataInNV "; + } + + return ""; +} + +void CompilerGLSL::emit_flattened_io_block_member(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices) +{ + uint32_t member_type_id = type.self; + const SPIRType *member_type = &type; + const SPIRType *parent_type = nullptr; + auto flattened_name = basename; + for (auto &index : indices) + { + flattened_name += "_"; + flattened_name += to_member_name(*member_type, index); + parent_type = member_type; + member_type_id = member_type->member_types[index]; + member_type = &get(member_type_id); + } + + assert(member_type->basetype != SPIRType::Struct); + + // We're overriding struct member names, so ensure we do so on the primary type. + if (parent_type->type_alias) + parent_type = &get(parent_type->type_alias); + + // Sanitize underscores because joining the two identifiers might create more than 1 underscore in a row, + // which is not allowed. + ParsedIR::sanitize_underscores(flattened_name); + + uint32_t last_index = indices.back(); + + // Pass in the varying qualifier here so it will appear in the correct declaration order. + // Replace member name while emitting it so it encodes both struct name and member name. + auto backup_name = get_member_name(parent_type->self, last_index); + auto member_name = to_member_name(*parent_type, last_index); + set_member_name(parent_type->self, last_index, flattened_name); + emit_struct_member(*parent_type, member_type_id, last_index, qual); + // Restore member name. + set_member_name(parent_type->self, last_index, member_name); +} + +void CompilerGLSL::emit_flattened_io_block_struct(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices) +{ + auto sub_indices = indices; + sub_indices.push_back(0); + + const SPIRType *member_type = &type; + for (auto &index : indices) + member_type = &get(member_type->member_types[index]); + + assert(member_type->basetype == SPIRType::Struct); + + if (!member_type->array.empty()) + SPIRV_CROSS_THROW("Cannot flatten array of structs in I/O blocks."); + + for (uint32_t i = 0; i < uint32_t(member_type->member_types.size()); i++) + { + sub_indices.back() = i; + if (get(member_type->member_types[i]).basetype == SPIRType::Struct) + emit_flattened_io_block_struct(basename, type, qual, sub_indices); + else + emit_flattened_io_block_member(basename, type, qual, sub_indices); + } +} + +void CompilerGLSL::emit_flattened_io_block(const SPIRVariable &var, const char *qual) +{ + auto &var_type = get(var.basetype); + if (!var_type.array.empty()) + SPIRV_CROSS_THROW("Array of varying structs cannot be flattened to legacy-compatible varyings."); + + // Emit flattened types based on the type alias. Normally, we are never supposed to emit + // struct declarations for aliased types. + auto &type = var_type.type_alias ? get(var_type.type_alias) : var_type; + + auto old_flags = ir.meta[type.self].decoration.decoration_flags; + // Emit the members as if they are part of a block to get all qualifiers. + ir.meta[type.self].decoration.decoration_flags.set(DecorationBlock); + + type.member_name_cache.clear(); + + SmallVector member_indices; + member_indices.push_back(0); + auto basename = to_name(var.self); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + auto &membertype = get(member); + + member_indices.back() = i; + if (membertype.basetype == SPIRType::Struct) + emit_flattened_io_block_struct(basename, type, qual, member_indices); + else + emit_flattened_io_block_member(basename, type, qual, member_indices); + i++; + } + + ir.meta[type.self].decoration.decoration_flags = old_flags; + + // Treat this variable as fully flattened from now on. + flattened_structs[var.self] = true; +} + +void CompilerGLSL::emit_interface_block(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + + // Either make it plain in/out or in/out blocks depending on what shader is doing ... + bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + const char *qual = to_storage_qualifiers_glsl(var); + + if (block) + { + // ESSL earlier than 310 and GLSL earlier than 150 did not support + // I/O variables which are struct types. + // To support this, flatten the struct into separate varyings instead. + if (options.force_flattened_io_blocks || (options.es && options.version < 310) || + (!options.es && options.version < 150)) + { + // I/O blocks on ES require version 310 with Android Extension Pack extensions, or core version 320. + // On desktop, I/O blocks were introduced with geometry shaders in GL 3.2 (GLSL 150). + emit_flattened_io_block(var, qual); + } + else + { + if (options.es && options.version < 320) + { + // Geometry and tessellation extensions imply this extension. + if (!has_extension("GL_EXT_geometry_shader") && !has_extension("GL_EXT_tessellation_shader")) + require_extension_internal("GL_EXT_shader_io_blocks"); + } + + // Block names should never alias. + auto block_name = to_name(type.self, false); + + // The namespace for I/O blocks is separate from other variables in GLSL. + auto &block_namespace = type.storage == StorageClassInput ? block_input_names : block_output_names; + + // Shaders never use the block by interface name, so we don't + // have to track this other than updating name caches. + if (block_name.empty() || block_namespace.find(block_name) != end(block_namespace)) + block_name = get_fallback_name(type.self); + else + block_namespace.insert(block_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + if (block_name.empty()) + block_name = join("_", get(var.basetype).self, "_", var.self); + + // Instance names cannot alias block names. + resource_names.insert(block_name); + + statement(layout_for_variable(var), qual, block_name); + begin_scope(); + + type.member_name_cache.clear(); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + emit_struct_member(type, member, i); + i++; + } + + add_resource_name(var.self); + end_scope_decl(join(to_name(var.self), type_to_array_glsl(type))); + statement(""); + } + } + else + { + // ESSL earlier than 310 and GLSL earlier than 150 did not support + // I/O variables which are struct types. + // To support this, flatten the struct into separate varyings instead. + if (type.basetype == SPIRType::Struct && + (options.force_flattened_io_blocks || (options.es && options.version < 310) || + (!options.es && options.version < 150))) + { + emit_flattened_io_block(var, qual); + } + else + { + add_resource_name(var.self); + + // Tessellation control and evaluation shaders must have either gl_MaxPatchVertices or unsized arrays for input arrays. + // Opt for unsized as it's the more "correct" variant to use. + bool control_point_input_array = type.storage == StorageClassInput && !type.array.empty() && + !has_decoration(var.self, DecorationPatch) && + (get_entry_point().model == ExecutionModelTessellationControl || + get_entry_point().model == ExecutionModelTessellationEvaluation); + + uint32_t old_array_size = 0; + bool old_array_size_literal = true; + + if (control_point_input_array) + { + swap(type.array.back(), old_array_size); + swap(type.array_size_literal.back(), old_array_size_literal); + } + + statement(layout_for_variable(var), to_qualifiers_glsl(var.self), + variable_decl(type, to_name(var.self), var.self), ";"); + + if (control_point_input_array) + { + swap(type.array.back(), old_array_size); + swap(type.array_size_literal.back(), old_array_size_literal); + } + + // If a StorageClassOutput variable has an initializer, we need to initialize it in main(). + if (var.storage == StorageClassOutput && var.initializer) + { + auto &entry_func = this->get(ir.default_entry_point); + entry_func.fixup_hooks_in.push_back( + [&]() { statement(to_name(var.self), " = ", to_expression(var.initializer), ";"); }); + } + } + } +} + +void CompilerGLSL::emit_uniform(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + if (type.basetype == SPIRType::Image && type.image.sampled == 2 && type.image.dim != DimSubpassData) + { + if (!options.es && options.version < 420) + require_extension_internal("GL_ARB_shader_image_load_store"); + else if (options.es && options.version < 310) + SPIRV_CROSS_THROW("At least ESSL 3.10 required for shader image load store."); + } + + add_resource_name(var.self); + statement(layout_for_variable(var), variable_decl(var), ";"); +} + +string CompilerGLSL::constant_value_macro_name(uint32_t id) +{ + return join("SPIRV_CROSS_CONSTANT_ID_", id); +} + +void CompilerGLSL::emit_specialization_constant_op(const SPIRConstantOp &constant) +{ + auto &type = get(constant.basetype); + auto name = to_name(constant.self); + statement("const ", variable_decl(type, name), " = ", constant_op_expression(constant), ";"); +} + +void CompilerGLSL::emit_constant(const SPIRConstant &constant) +{ + auto &type = get(constant.constant_type); + auto name = to_name(constant.self); + + SpecializationConstant wg_x, wg_y, wg_z; + ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + // This specialization constant is implicitly declared by emitting layout() in; + if (constant.self == workgroup_size_id) + return; + + // These specialization constants are implicitly declared by emitting layout() in; + // In legacy GLSL, we will still need to emit macros for these, so a layout() in; declaration + // later can use macro overrides for work group size. + bool is_workgroup_size_constant = ConstantID(constant.self) == wg_x.id || ConstantID(constant.self) == wg_y.id || + ConstantID(constant.self) == wg_z.id; + + if (options.vulkan_semantics && is_workgroup_size_constant) + { + // Vulkan GLSL does not need to declare workgroup spec constants explicitly, it is handled in layout(). + return; + } + else if (!options.vulkan_semantics && is_workgroup_size_constant && + !has_decoration(constant.self, DecorationSpecId)) + { + // Only bother declaring a workgroup size if it is actually a specialization constant, because we need macros. + return; + } + + // Only scalars have constant IDs. + if (has_decoration(constant.self, DecorationSpecId)) + { + if (options.vulkan_semantics) + { + statement("layout(constant_id = ", get_decoration(constant.self, DecorationSpecId), ") const ", + variable_decl(type, name), " = ", constant_expression(constant), ";"); + } + else + { + const string ¯o_name = constant.specialization_constant_macro_name; + statement("#ifndef ", macro_name); + statement("#define ", macro_name, " ", constant_expression(constant)); + statement("#endif"); + + // For workgroup size constants, only emit the macros. + if (!is_workgroup_size_constant) + statement("const ", variable_decl(type, name), " = ", macro_name, ";"); + } + } + else + { + statement("const ", variable_decl(type, name), " = ", constant_expression(constant), ";"); + } +} + +void CompilerGLSL::emit_entry_point_declarations() +{ +} + +void CompilerGLSL::replace_illegal_names(const unordered_set &keywords) +{ + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (is_hidden_variable(var)) + return; + + auto *meta = ir.find_meta(var.self); + if (!meta) + return; + + auto &m = meta->decoration; + if (m.alias.compare(0, 3, "gl_") == 0 || keywords.find(m.alias) != end(keywords)) + m.alias = join("_", m.alias); + }); + + ir.for_each_typed_id([&](uint32_t, const SPIRType &type) { + auto *meta = ir.find_meta(type.self); + if (!meta) + return; + + auto &m = meta->decoration; + if (m.alias.compare(0, 3, "gl_") == 0 || keywords.find(m.alias) != end(keywords)) + m.alias = join("_", m.alias); + + for (auto &memb : meta->members) + if (memb.alias.compare(0, 3, "gl_") == 0 || keywords.find(memb.alias) != end(keywords)) + memb.alias = join("_", memb.alias); + }); +} + +void CompilerGLSL::replace_illegal_names() +{ + // clang-format off + static const unordered_set keywords = { + "abs", "acos", "acosh", "all", "any", "asin", "asinh", "atan", "atanh", + "atomicAdd", "atomicCompSwap", "atomicCounter", "atomicCounterDecrement", "atomicCounterIncrement", + "atomicExchange", "atomicMax", "atomicMin", "atomicOr", "atomicXor", + "bitCount", "bitfieldExtract", "bitfieldInsert", "bitfieldReverse", + "ceil", "cos", "cosh", "cross", "degrees", + "dFdx", "dFdxCoarse", "dFdxFine", + "dFdy", "dFdyCoarse", "dFdyFine", + "distance", "dot", "EmitStreamVertex", "EmitVertex", "EndPrimitive", "EndStreamPrimitive", "equal", "exp", "exp2", + "faceforward", "findLSB", "findMSB", "float16BitsToInt16", "float16BitsToUint16", "floatBitsToInt", "floatBitsToUint", "floor", "fma", "fract", + "frexp", "fwidth", "fwidthCoarse", "fwidthFine", + "greaterThan", "greaterThanEqual", "groupMemoryBarrier", + "imageAtomicAdd", "imageAtomicAnd", "imageAtomicCompSwap", "imageAtomicExchange", "imageAtomicMax", "imageAtomicMin", "imageAtomicOr", "imageAtomicXor", + "imageLoad", "imageSamples", "imageSize", "imageStore", "imulExtended", "int16BitsToFloat16", "intBitsToFloat", "interpolateAtOffset", "interpolateAtCentroid", "interpolateAtSample", + "inverse", "inversesqrt", "isinf", "isnan", "ldexp", "length", "lessThan", "lessThanEqual", "log", "log2", + "matrixCompMult", "max", "memoryBarrier", "memoryBarrierAtomicCounter", "memoryBarrierBuffer", "memoryBarrierImage", "memoryBarrierShared", + "min", "mix", "mod", "modf", "noise", "noise1", "noise2", "noise3", "noise4", "normalize", "not", "notEqual", + "outerProduct", "packDouble2x32", "packHalf2x16", "packInt2x16", "packInt4x16", "packSnorm2x16", "packSnorm4x8", + "packUint2x16", "packUint4x16", "packUnorm2x16", "packUnorm4x8", "pow", + "radians", "reflect", "refract", "round", "roundEven", "sign", "sin", "sinh", "smoothstep", "sqrt", "step", + "tan", "tanh", "texelFetch", "texelFetchOffset", "texture", "textureGather", "textureGatherOffset", "textureGatherOffsets", + "textureGrad", "textureGradOffset", "textureLod", "textureLodOffset", "textureOffset", "textureProj", "textureProjGrad", + "textureProjGradOffset", "textureProjLod", "textureProjLodOffset", "textureProjOffset", "textureQueryLevels", "textureQueryLod", "textureSamples", "textureSize", + "transpose", "trunc", "uaddCarry", "uint16BitsToFloat16", "uintBitsToFloat", "umulExtended", "unpackDouble2x32", "unpackHalf2x16", "unpackInt2x16", "unpackInt4x16", + "unpackSnorm2x16", "unpackSnorm4x8", "unpackUint2x16", "unpackUint4x16", "unpackUnorm2x16", "unpackUnorm4x8", "usubBorrow", + + "active", "asm", "atomic_uint", "attribute", "bool", "break", "buffer", + "bvec2", "bvec3", "bvec4", "case", "cast", "centroid", "class", "coherent", "common", "const", "continue", "default", "discard", + "dmat2", "dmat2x2", "dmat2x3", "dmat2x4", "dmat3", "dmat3x2", "dmat3x3", "dmat3x4", "dmat4", "dmat4x2", "dmat4x3", "dmat4x4", + "do", "double", "dvec2", "dvec3", "dvec4", "else", "enum", "extern", "external", "false", "filter", "fixed", "flat", "float", + "for", "fvec2", "fvec3", "fvec4", "goto", "half", "highp", "hvec2", "hvec3", "hvec4", "if", "iimage1D", "iimage1DArray", + "iimage2D", "iimage2DArray", "iimage2DMS", "iimage2DMSArray", "iimage2DRect", "iimage3D", "iimageBuffer", "iimageCube", + "iimageCubeArray", "image1D", "image1DArray", "image2D", "image2DArray", "image2DMS", "image2DMSArray", "image2DRect", + "image3D", "imageBuffer", "imageCube", "imageCubeArray", "in", "inline", "inout", "input", "int", "interface", "invariant", + "isampler1D", "isampler1DArray", "isampler2D", "isampler2DArray", "isampler2DMS", "isampler2DMSArray", "isampler2DRect", + "isampler3D", "isamplerBuffer", "isamplerCube", "isamplerCubeArray", "ivec2", "ivec3", "ivec4", "layout", "long", "lowp", + "mat2", "mat2x2", "mat2x3", "mat2x4", "mat3", "mat3x2", "mat3x3", "mat3x4", "mat4", "mat4x2", "mat4x3", "mat4x4", "mediump", + "namespace", "noinline", "noperspective", "out", "output", "packed", "partition", "patch", "precise", "precision", "public", "readonly", + "resource", "restrict", "return", "sample", "sampler1D", "sampler1DArray", "sampler1DArrayShadow", + "sampler1DShadow", "sampler2D", "sampler2DArray", "sampler2DArrayShadow", "sampler2DMS", "sampler2DMSArray", + "sampler2DRect", "sampler2DRectShadow", "sampler2DShadow", "sampler3D", "sampler3DRect", "samplerBuffer", + "samplerCube", "samplerCubeArray", "samplerCubeArrayShadow", "samplerCubeShadow", "shared", "short", "sizeof", "smooth", "static", + "struct", "subroutine", "superp", "switch", "template", "this", "true", "typedef", "uimage1D", "uimage1DArray", "uimage2D", + "uimage2DArray", "uimage2DMS", "uimage2DMSArray", "uimage2DRect", "uimage3D", "uimageBuffer", "uimageCube", + "uimageCubeArray", "uint", "uniform", "union", "unsigned", "usampler1D", "usampler1DArray", "usampler2D", "usampler2DArray", + "usampler2DMS", "usampler2DMSArray", "usampler2DRect", "usampler3D", "usamplerBuffer", "usamplerCube", + "usamplerCubeArray", "using", "uvec2", "uvec3", "uvec4", "varying", "vec2", "vec3", "vec4", "void", "volatile", + "while", "writeonly", + }; + // clang-format on + + replace_illegal_names(keywords); +} + +void CompilerGLSL::replace_fragment_output(SPIRVariable &var) +{ + auto &m = ir.meta[var.self].decoration; + uint32_t location = 0; + if (m.decoration_flags.get(DecorationLocation)) + location = m.location; + + // If our variable is arrayed, we must not emit the array part of this as the SPIR-V will + // do the access chain part of this for us. + auto &type = get(var.basetype); + + if (type.array.empty()) + { + // Redirect the write to a specific render target in legacy GLSL. + m.alias = join("gl_FragData[", location, "]"); + + if (is_legacy_es() && location != 0) + require_extension_internal("GL_EXT_draw_buffers"); + } + else if (type.array.size() == 1) + { + // If location is non-zero, we probably have to add an offset. + // This gets really tricky since we'd have to inject an offset in the access chain. + // FIXME: This seems like an extremely odd-ball case, so it's probably fine to leave it like this for now. + m.alias = "gl_FragData"; + if (location != 0) + SPIRV_CROSS_THROW("Arrayed output variable used, but location is not 0. " + "This is unimplemented in SPIRV-Cross."); + + if (is_legacy_es()) + require_extension_internal("GL_EXT_draw_buffers"); + } + else + SPIRV_CROSS_THROW("Array-of-array output variable used. This cannot be implemented in legacy GLSL."); + + var.compat_builtin = true; // We don't want to declare this variable, but use the name as-is. +} + +void CompilerGLSL::replace_fragment_outputs() +{ + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + if (!is_builtin_variable(var) && !var.remapped_variable && type.pointer && var.storage == StorageClassOutput) + replace_fragment_output(var); + }); +} + +string CompilerGLSL::remap_swizzle(const SPIRType &out_type, uint32_t input_components, const string &expr) +{ + if (out_type.vecsize == input_components) + return expr; + else if (input_components == 1 && !backend.can_swizzle_scalar) + return join(type_to_glsl(out_type), "(", expr, ")"); + else + { + // FIXME: This will not work with packed expressions. + auto e = enclose_expression(expr) + "."; + // Just clamp the swizzle index if we have more outputs than inputs. + for (uint32_t c = 0; c < out_type.vecsize; c++) + e += index_to_swizzle(min(c, input_components - 1)); + if (backend.swizzle_is_function && out_type.vecsize > 1) + e += "()"; + + remove_duplicate_swizzle(e); + return e; + } +} + +void CompilerGLSL::emit_pls() +{ + auto &execution = get_entry_point(); + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Pixel local storage only supported in fragment shaders."); + + if (!options.es) + SPIRV_CROSS_THROW("Pixel local storage only supported in OpenGL ES."); + + if (options.version < 300) + SPIRV_CROSS_THROW("Pixel local storage only supported in ESSL 3.0 and above."); + + if (!pls_inputs.empty()) + { + statement("__pixel_local_inEXT _PLSIn"); + begin_scope(); + for (auto &input : pls_inputs) + statement(pls_decl(input), ";"); + end_scope_decl(); + statement(""); + } + + if (!pls_outputs.empty()) + { + statement("__pixel_local_outEXT _PLSOut"); + begin_scope(); + for (auto &output : pls_outputs) + statement(pls_decl(output), ";"); + end_scope_decl(); + statement(""); + } +} + +void CompilerGLSL::fixup_image_load_store_access() +{ + if (!options.enable_storage_image_qualifier_deduction) + return; + + ir.for_each_typed_id([&](uint32_t var, const SPIRVariable &) { + auto &vartype = expression_type(var); + if (vartype.basetype == SPIRType::Image && vartype.image.sampled == 2) + { + // Very old glslangValidator and HLSL compilers do not emit required qualifiers here. + // Solve this by making the image access as restricted as possible and loosen up if we need to. + // If any no-read/no-write flags are actually set, assume that the compiler knows what it's doing. + + auto &flags = ir.meta[var].decoration.decoration_flags; + if (!flags.get(DecorationNonWritable) && !flags.get(DecorationNonReadable)) + { + flags.set(DecorationNonWritable); + flags.set(DecorationNonReadable); + } + } + }); +} + +static bool is_block_builtin(BuiltIn builtin) +{ + return builtin == BuiltInPosition || builtin == BuiltInPointSize || builtin == BuiltInClipDistance || + builtin == BuiltInCullDistance; +} + +bool CompilerGLSL::should_force_emit_builtin_block(StorageClass storage) +{ + // If the builtin block uses XFB, we need to force explicit redeclaration of the builtin block. + + if (storage != StorageClassOutput) + return false; + bool should_force = false; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (should_force) + return; + + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + if (var.storage == storage && block && is_builtin_variable(var)) + { + uint32_t member_count = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < member_count; i++) + { + if (has_member_decoration(type.self, i, DecorationBuiltIn) && + is_block_builtin(BuiltIn(get_member_decoration(type.self, i, DecorationBuiltIn))) && + has_member_decoration(type.self, i, DecorationOffset)) + { + should_force = true; + } + } + } + else if (var.storage == storage && !block && is_builtin_variable(var)) + { + if (is_block_builtin(BuiltIn(get_decoration(type.self, DecorationBuiltIn))) && + has_decoration(var.self, DecorationOffset)) + { + should_force = true; + } + } + }); + + return should_force; +} + +void CompilerGLSL::fixup_implicit_builtin_block_names() +{ + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + if ((var.storage == StorageClassOutput || var.storage == StorageClassInput) && block && + is_builtin_variable(var)) + { + // Make sure the array has a supported name in the code. + if (var.storage == StorageClassOutput) + set_name(var.self, "gl_out"); + else if (var.storage == StorageClassInput) + set_name(var.self, "gl_in"); + } + }); +} + +void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionModel model) +{ + Bitset emitted_builtins; + Bitset global_builtins; + const SPIRVariable *block_var = nullptr; + bool emitted_block = false; + bool builtin_array = false; + + // Need to use declared size in the type. + // These variables might have been declared, but not statically used, so we haven't deduced their size yet. + uint32_t cull_distance_size = 0; + uint32_t clip_distance_size = 0; + + bool have_xfb_buffer_stride = false; + bool have_geom_stream = false; + bool have_any_xfb_offset = false; + uint32_t xfb_stride = 0, xfb_buffer = 0, geom_stream = 0; + std::unordered_map builtin_xfb_offsets; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + Bitset builtins; + + if (var.storage == storage && block && is_builtin_variable(var)) + { + uint32_t index = 0; + for (auto &m : ir.meta[type.self].members) + { + if (m.builtin) + { + builtins.set(m.builtin_type); + if (m.builtin_type == BuiltInCullDistance) + cull_distance_size = to_array_size_literal(this->get(type.member_types[index])); + else if (m.builtin_type == BuiltInClipDistance) + clip_distance_size = to_array_size_literal(this->get(type.member_types[index])); + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationOffset)) + { + have_any_xfb_offset = true; + builtin_xfb_offsets[m.builtin_type] = m.offset; + } + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationStream)) + { + uint32_t stream = m.stream; + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } + } + index++; + } + + if (storage == StorageClassOutput && has_decoration(var.self, DecorationXfbBuffer) && + has_decoration(var.self, DecorationXfbStride)) + { + uint32_t buffer_index = get_decoration(var.self, DecorationXfbBuffer); + uint32_t stride = get_decoration(var.self, DecorationXfbStride); + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + xfb_stride = stride; + } + + if (storage == StorageClassOutput && has_decoration(var.self, DecorationStream)) + { + uint32_t stream = get_decoration(var.self, DecorationStream); + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } + } + else if (var.storage == storage && !block && is_builtin_variable(var)) + { + // While we're at it, collect all declared global builtins (HLSL mostly ...). + auto &m = ir.meta[var.self].decoration; + if (m.builtin) + { + global_builtins.set(m.builtin_type); + if (m.builtin_type == BuiltInCullDistance) + cull_distance_size = to_array_size_literal(type); + else if (m.builtin_type == BuiltInClipDistance) + clip_distance_size = to_array_size_literal(type); + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationXfbStride) && + m.decoration_flags.get(DecorationXfbBuffer) && m.decoration_flags.get(DecorationOffset)) + { + have_any_xfb_offset = true; + builtin_xfb_offsets[m.builtin_type] = m.offset; + uint32_t buffer_index = m.xfb_buffer; + uint32_t stride = m.xfb_stride; + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + xfb_stride = stride; + } + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationStream)) + { + uint32_t stream = get_decoration(var.self, DecorationStream); + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } + } + } + + if (builtins.empty()) + return; + + if (emitted_block) + SPIRV_CROSS_THROW("Cannot use more than one builtin I/O block."); + + emitted_builtins = builtins; + emitted_block = true; + builtin_array = !type.array.empty(); + block_var = &var; + }); + + global_builtins = + Bitset(global_builtins.get_lower() & ((1ull << BuiltInPosition) | (1ull << BuiltInPointSize) | + (1ull << BuiltInClipDistance) | (1ull << BuiltInCullDistance))); + + // Try to collect all other declared builtins. + if (!emitted_block) + emitted_builtins = global_builtins; + + // Can't declare an empty interface block. + if (emitted_builtins.empty()) + return; + + if (storage == StorageClassOutput) + { + SmallVector attr; + if (have_xfb_buffer_stride && have_any_xfb_offset) + { + if (!options.es) + { + if (options.version < 440 && options.version >= 140) + require_extension_internal("GL_ARB_enhanced_layouts"); + else if (options.version < 140) + SPIRV_CROSS_THROW("Component decoration is not supported in targets below GLSL 1.40."); + if (!options.es && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + } + else if (options.es) + SPIRV_CROSS_THROW("Need GL_ARB_enhanced_layouts for xfb_stride or xfb_buffer."); + attr.push_back(join("xfb_buffer = ", xfb_buffer, ", xfb_stride = ", xfb_stride)); + } + + if (have_geom_stream) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", geom_stream)); + } + + if (!attr.empty()) + statement("layout(", merge(attr), ") out gl_PerVertex"); + else + statement("out gl_PerVertex"); + } + else + { + // If we have passthrough, there is no way PerVertex cannot be passthrough. + if (get_entry_point().geometry_passthrough) + statement("layout(passthrough) in gl_PerVertex"); + else + statement("in gl_PerVertex"); + } + + begin_scope(); + if (emitted_builtins.get(BuiltInPosition)) + { + auto itr = builtin_xfb_offsets.find(BuiltInPosition); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") vec4 gl_Position;"); + else + statement("vec4 gl_Position;"); + } + + if (emitted_builtins.get(BuiltInPointSize)) + { + auto itr = builtin_xfb_offsets.find(BuiltInPointSize); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_PointSize;"); + else + statement("float gl_PointSize;"); + } + + if (emitted_builtins.get(BuiltInClipDistance)) + { + auto itr = builtin_xfb_offsets.find(BuiltInClipDistance); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_ClipDistance[", clip_distance_size, "];"); + else + statement("float gl_ClipDistance[", clip_distance_size, "];"); + } + + if (emitted_builtins.get(BuiltInCullDistance)) + { + auto itr = builtin_xfb_offsets.find(BuiltInCullDistance); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_CullDistance[", cull_distance_size, "];"); + else + statement("float gl_CullDistance[", cull_distance_size, "];"); + } + + if (builtin_array) + { + if (model == ExecutionModelTessellationControl && storage == StorageClassOutput) + end_scope_decl(join(to_name(block_var->self), "[", get_entry_point().output_vertices, "]")); + else + end_scope_decl(join(to_name(block_var->self), "[]")); + } + else + end_scope_decl(); + statement(""); +} + +void CompilerGLSL::declare_undefined_values() +{ + bool emitted = false; + ir.for_each_typed_id([&](uint32_t, const SPIRUndef &undef) { + auto &type = this->get(undef.basetype); + // OpUndef can be void for some reason ... + if (type.basetype == SPIRType::Void) + return; + + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(undef.basetype)); + + statement(variable_decl(type, to_name(undef.self), undef.self), initializer, ";"); + emitted = true; + }); + + if (emitted) + statement(""); +} + +bool CompilerGLSL::variable_is_lut(const SPIRVariable &var) const +{ + bool statically_assigned = var.statically_assigned && var.static_expression != ID(0) && var.remapped_variable; + + if (statically_assigned) + { + auto *constant = maybe_get(var.static_expression); + if (constant && constant->is_used_as_lut) + return true; + } + + return false; +} + +void CompilerGLSL::emit_resources() +{ + auto &execution = get_entry_point(); + + replace_illegal_names(); + + // Legacy GL uses gl_FragData[], redeclare all fragment outputs + // with builtins. + if (execution.model == ExecutionModelFragment && is_legacy()) + replace_fragment_outputs(); + + // Emit PLS blocks if we have such variables. + if (!pls_inputs.empty() || !pls_outputs.empty()) + emit_pls(); + + switch (execution.model) + { + case ExecutionModelGeometry: + case ExecutionModelTessellationControl: + case ExecutionModelTessellationEvaluation: + fixup_implicit_builtin_block_names(); + break; + + default: + break; + } + + // Emit custom gl_PerVertex for SSO compatibility. + if (options.separate_shader_objects && !options.es && execution.model != ExecutionModelFragment) + { + switch (execution.model) + { + case ExecutionModelGeometry: + case ExecutionModelTessellationControl: + case ExecutionModelTessellationEvaluation: + emit_declared_builtin_block(StorageClassInput, execution.model); + emit_declared_builtin_block(StorageClassOutput, execution.model); + break; + + case ExecutionModelVertex: + emit_declared_builtin_block(StorageClassOutput, execution.model); + break; + + default: + break; + } + } + else if (should_force_emit_builtin_block(StorageClassOutput)) + { + emit_declared_builtin_block(StorageClassOutput, execution.model); + } + else if (execution.geometry_passthrough) + { + // Need to declare gl_in with Passthrough. + // If we're doing passthrough, we cannot emit an output block, so the output block test above will never pass. + emit_declared_builtin_block(StorageClassInput, execution.model); + } + else + { + // Need to redeclare clip/cull distance with explicit size to use them. + // SPIR-V mandates these builtins have a size declared. + const char *storage = execution.model == ExecutionModelFragment ? "in" : "out"; + if (clip_distance_count != 0) + statement(storage, " float gl_ClipDistance[", clip_distance_count, "];"); + if (cull_distance_count != 0) + statement(storage, " float gl_CullDistance[", cull_distance_count, "];"); + if (clip_distance_count != 0 || cull_distance_count != 0) + statement(""); + } + + if (position_invariant) + { + statement("invariant gl_Position;"); + statement(""); + } + + bool emitted = false; + + // If emitted Vulkan GLSL, + // emit specialization constants as actual floats, + // spec op expressions will redirect to the constant name. + // + { + auto loop_lock = ir.create_loop_hard_lock(); + for (auto &id_ : ir.ids_for_constant_or_type) + { + auto &id = ir.ids[id_]; + + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + + bool needs_declaration = c.specialization || c.is_used_as_lut; + + if (needs_declaration) + { + if (!options.vulkan_semantics && c.specialization) + { + c.specialization_constant_macro_name = + constant_value_macro_name(get_decoration(c.self, DecorationSpecId)); + } + emit_constant(c); + emitted = true; + } + } + else if (id.get_type() == TypeConstantOp) + { + emit_specialization_constant_op(id.get()); + emitted = true; + } + else if (id.get_type() == TypeType) + { + auto *type = &id.get(); + + bool is_natural_struct = type->basetype == SPIRType::Struct && type->array.empty() && !type->pointer && + (!has_decoration(type->self, DecorationBlock) && + !has_decoration(type->self, DecorationBufferBlock)); + + // Special case, ray payload and hit attribute blocks are not really blocks, just regular structs. + if (type->basetype == SPIRType::Struct && type->pointer && + has_decoration(type->self, DecorationBlock) && + (type->storage == StorageClassRayPayloadNV || type->storage == StorageClassIncomingRayPayloadNV || + type->storage == StorageClassHitAttributeNV)) + { + type = &get(type->parent_type); + is_natural_struct = true; + } + + if (is_natural_struct) + { + if (emitted) + statement(""); + emitted = false; + + emit_struct(*type); + } + } + } + } + + if (emitted) + statement(""); + + // If we needed to declare work group size late, check here. + // If the work group size depends on a specialization constant, we need to declare the layout() block + // after constants (and their macros) have been declared. + if (execution.model == ExecutionModelGLCompute && !options.vulkan_semantics && + execution.workgroup_size.constant != 0) + { + SpecializationConstant wg_x, wg_y, wg_z; + get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + if ((wg_x.id != ConstantID(0)) || (wg_y.id != ConstantID(0)) || (wg_z.id != ConstantID(0))) + { + SmallVector inputs; + build_workgroup_size(inputs, wg_x, wg_y, wg_z); + statement("layout(", merge(inputs), ") in;"); + statement(""); + } + } + + emitted = false; + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64EXT) + { + for (auto type : physical_storage_non_block_pointer_types) + { + emit_buffer_reference_block(get(type), false); + } + + // Output buffer reference blocks. + // Do this in two stages, one with forward declaration, + // and one without. Buffer reference blocks can reference themselves + // to support things like linked lists. + ir.for_each_typed_id([&](uint32_t, SPIRType &type) { + bool has_block_flags = has_decoration(type.self, DecorationBlock); + if (has_block_flags && type.pointer && type.pointer_depth == 1 && !type_is_array_of_pointers(type) && + type.storage == StorageClassPhysicalStorageBufferEXT) + { + emit_buffer_reference_block(type, true); + } + }); + + ir.for_each_typed_id([&](uint32_t, SPIRType &type) { + bool has_block_flags = has_decoration(type.self, DecorationBlock); + if (has_block_flags && type.pointer && type.pointer_depth == 1 && !type_is_array_of_pointers(type) && + type.storage == StorageClassPhysicalStorageBufferEXT) + { + emit_buffer_reference_block(type, false); + } + }); + } + + // Output UBOs and SSBOs + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + bool is_block_storage = type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform || + type.storage == StorageClassShaderRecordBufferNV; + bool has_block_flags = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + + if (var.storage != StorageClassFunction && type.pointer && is_block_storage && !is_hidden_variable(var) && + has_block_flags) + { + emit_buffer_block(var); + } + }); + + // Output push constant blocks + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassPushConstant && + !is_hidden_variable(var)) + { + emit_push_constant_block(var); + } + }); + + bool skip_separate_image_sampler = !combined_image_samplers.empty() || !options.vulkan_semantics; + + // Output Uniform Constants (values, samplers, images, etc). + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + // If we're remapping separate samplers and images, only emit the combined samplers. + if (skip_separate_image_sampler) + { + // Sampler buffers are always used without a sampler, and they will also work in regular GL. + bool sampler_buffer = type.basetype == SPIRType::Image && type.image.dim == DimBuffer; + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + if (!sampler_buffer && (separate_image || separate_sampler)) + return; + } + + if (var.storage != StorageClassFunction && type.pointer && + (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter || + type.storage == StorageClassRayPayloadNV || type.storage == StorageClassIncomingRayPayloadNV || + type.storage == StorageClassCallableDataNV || type.storage == StorageClassIncomingCallableDataNV || + type.storage == StorageClassHitAttributeNV) && + !is_hidden_variable(var)) + { + emit_uniform(var); + emitted = true; + } + }); + + if (emitted) + statement(""); + emitted = false; + + bool emitted_base_instance = false; + + // Output in/out interfaces. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + bool is_hidden = is_hidden_variable(var); + + // Unused output I/O variables might still be required to implement framebuffer fetch. + if (var.storage == StorageClassOutput && !is_legacy() && + inout_color_attachments.count(get_decoration(var.self, DecorationLocation)) != 0) + { + is_hidden = false; + } + + if (var.storage != StorageClassFunction && type.pointer && + (var.storage == StorageClassInput || var.storage == StorageClassOutput) && + interface_variable_exists_in_entry_point(var.self) && !is_hidden) + { + emit_interface_block(var); + emitted = true; + } + else if (is_builtin_variable(var)) + { + auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + // For gl_InstanceIndex emulation on GLES, the API user needs to + // supply this uniform. + + // The draw parameter extension is soft-enabled on GL with some fallbacks. + if (!options.vulkan_semantics) + { + if (!emitted_base_instance && + ((options.vertex.support_nonzero_base_instance && builtin == BuiltInInstanceIndex) || + (builtin == BuiltInBaseInstance))) + { + statement("#ifdef GL_ARB_shader_draw_parameters"); + statement("#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB"); + statement("#else"); + // A crude, but simple workaround which should be good enough for non-indirect draws. + statement("uniform int SPIRV_Cross_BaseInstance;"); + statement("#endif"); + emitted = true; + emitted_base_instance = true; + } + else if (builtin == BuiltInBaseVertex) + { + statement("#ifdef GL_ARB_shader_draw_parameters"); + statement("#define SPIRV_Cross_BaseVertex gl_BaseVertexARB"); + statement("#else"); + // A crude, but simple workaround which should be good enough for non-indirect draws. + statement("uniform int SPIRV_Cross_BaseVertex;"); + statement("#endif"); + } + else if (builtin == BuiltInDrawIndex) + { + statement("#ifndef GL_ARB_shader_draw_parameters"); + // Cannot really be worked around. + statement("#error GL_ARB_shader_draw_parameters is not supported."); + statement("#endif"); + } + } + } + }); + + // Global variables. + for (auto global : global_variables) + { + auto &var = get(global); + if (var.storage != StorageClassOutput) + { + if (!variable_is_lut(var)) + { + add_resource_name(var.self); + + string initializer; + if (options.force_zero_initialized_variables && var.storage == StorageClassPrivate && + !var.initializer && !var.static_expression && type_can_zero_initialize(get_variable_data_type(var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(var))); + } + + statement(variable_decl(var), initializer, ";"); + emitted = true; + } + } + } + + if (emitted) + statement(""); + + declare_undefined_values(); +} + +void CompilerGLSL::emit_extension_workarounds(spv::ExecutionModel model) +{ + static const char *workaround_types[] = { "int", "ivec2", "ivec3", "ivec4", "uint", "uvec2", "uvec3", "uvec4", + "float", "vec2", "vec3", "vec4", "double", "dvec2", "dvec3", "dvec4" }; + + if (!options.vulkan_semantics) + { + using Supp = ShaderSubgroupSupportHelper; + auto result = shader_subgroup_supporter.resolve(); + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupMask)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupMask, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_SubgroupEqMask uvec4(gl_ThreadEqMaskNV, 0u, 0u, 0u)"); + statement("#define gl_SubgroupGeMask uvec4(gl_ThreadGeMaskNV, 0u, 0u, 0u)"); + statement("#define gl_SubgroupGtMask uvec4(gl_ThreadGtMaskNV, 0u, 0u, 0u)"); + statement("#define gl_SubgroupLeMask uvec4(gl_ThreadLeMaskNV, 0u, 0u, 0u)"); + statement("#define gl_SubgroupLtMask uvec4(gl_ThreadLtMaskNV, 0u, 0u, 0u)"); + break; + case Supp::ARB_shader_ballot: + statement("#define gl_SubgroupEqMask uvec4(unpackUint2x32(gl_SubGroupEqMaskARB), 0u, 0u)"); + statement("#define gl_SubgroupGeMask uvec4(unpackUint2x32(gl_SubGroupGeMaskARB), 0u, 0u)"); + statement("#define gl_SubgroupGtMask uvec4(unpackUint2x32(gl_SubGroupGtMaskARB), 0u, 0u)"); + statement("#define gl_SubgroupLeMask uvec4(unpackUint2x32(gl_SubGroupLeMaskARB), 0u, 0u)"); + statement("#define gl_SubgroupLtMask uvec4(unpackUint2x32(gl_SubGroupLtMaskARB), 0u, 0u)"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupSize)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupSize, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_SubgroupSize gl_WarpSizeNV"); + break; + case Supp::ARB_shader_ballot: + statement("#define gl_SubgroupSize gl_SubGroupSizeARB"); + break; + case Supp::AMD_gcn_shader: + statement("#define gl_SubgroupSize uint(gl_SIMDGroupSizeAMD)"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupInvocationID)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupInvocationID, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_SubgroupInvocationID gl_ThreadInWarpNV"); + break; + case Supp::ARB_shader_ballot: + statement("#define gl_SubgroupInvocationID gl_SubGroupInvocationARB"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupID)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupID, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_SubgroupID gl_WarpIDNV"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::NumSubgroups)) + { + auto exts = Supp::get_candidates_for_feature(Supp::NumSubgroups, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_NumSubgroups gl_WarpsPerSMNV"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBrodcast_First)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupBrodcast_First, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_shuffle: + for (const char *t : workaround_types) + { + statement(t, " subgroupBroadcastFirst(", t, + " value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); }"); + } + for (const char *t : workaround_types) + { + statement(t, " subgroupBroadcast(", t, + " value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); }"); + } + break; + case Supp::ARB_shader_ballot: + for (const char *t : workaround_types) + { + statement(t, " subgroupBroadcastFirst(", t, + " value) { return readFirstInvocationARB(value); }"); + } + for (const char *t : workaround_types) + { + statement(t, " subgroupBroadcast(", t, + " value, uint id) { return readInvocationARB(value, id); }"); + } + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBallotFindLSB_MSB)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupBallotFindLSB_MSB, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("uint subgroupBallotFindLSB(uvec4 value) { return findLSB(value.x); }"); + statement("uint subgroupBallotFindMSB(uvec4 value) { return findMSB(value.x); }"); + break; + default: + break; + } + } + statement("#else"); + statement("uint subgroupBallotFindLSB(uvec4 value)"); + begin_scope(); + statement("int firstLive = findLSB(value.x);"); + statement("return uint(firstLive != -1 ? firstLive : (findLSB(value.y) + 32));"); + end_scope(); + statement("uint subgroupBallotFindMSB(uvec4 value)"); + begin_scope(); + statement("int firstLive = findMSB(value.y);"); + statement("return uint(firstLive != -1 ? (firstLive + 32) : findMSB(value.x));"); + end_scope(); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupAll_Any_AllEqualBool)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupAll_Any_AllEqualBool, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_gpu_shader_5: + statement("bool subgroupAll(bool value) { return allThreadsNV(value); }"); + statement("bool subgroupAny(bool value) { return anyThreadNV(value); }"); + statement("bool subgroupAllEqual(bool value) { return allThreadsEqualNV(value); }"); + break; + case Supp::ARB_shader_group_vote: + statement("bool subgroupAll(bool v) { return allInvocationsARB(v); }"); + statement("bool subgroupAny(bool v) { return anyInvocationARB(v); }"); + statement("bool subgroupAllEqual(bool v) { return allInvocationsEqualARB(v); }"); + break; + case Supp::AMD_gcn_shader: + statement("bool subgroupAll(bool value) { return ballotAMD(value) == ballotAMD(true); }"); + statement("bool subgroupAny(bool value) { return ballotAMD(value) != 0ull; }"); + statement("bool subgroupAllEqual(bool value) { uint64_t b = ballotAMD(value); return b == 0ull || " + "b == ballotAMD(true); }"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupAllEqualT)) + { + statement("#ifndef GL_KHR_shader_subgroup_vote"); + statement( + "#define _SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(type) bool subgroupAllEqual(type value) { return " + "subgroupAllEqual(subgroupBroadcastFirst(value) == value); }"); + for (const char *t : workaround_types) + statement("_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(", t, ")"); + statement("#undef _SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND"); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBallot)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupBallot, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("uvec4 subgroupBallot(bool v) { return uvec4(ballotThreadNV(v), 0u, 0u, 0u); }"); + break; + case Supp::ARB_shader_ballot: + statement("uvec4 subgroupBallot(bool v) { return uvec4(unpackUint2x32(ballotARB(v)), 0u, 0u); }"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupElect)) + { + statement("#ifndef GL_KHR_shader_subgroup_basic"); + statement("bool subgroupElect()"); + begin_scope(); + statement("uvec4 activeMask = subgroupBallot(true);"); + statement("uint firstLive = subgroupBallotFindLSB(activeMask);"); + statement("return gl_SubgroupInvocationID == firstLive;"); + end_scope(); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBarrier)) + { + // Extensions we're using in place of GL_KHR_shader_subgroup_basic state + // that subgroup execute in lockstep so this barrier is implicit. + // However the GL 4.6 spec also states that `barrier` implies a shared memory barrier, + // and a specific test of optimizing scans by leveraging lock-step invocation execution, + // has shown that a `memoryBarrierShared` is needed in place of a `subgroupBarrier`. + // https://github.com/buildaworldnet/IrrlichtBAW/commit/d8536857991b89a30a6b65d29441e51b64c2c7ad#diff-9f898d27be1ea6fc79b03d9b361e299334c1a347b6e4dc344ee66110c6aa596aR19 + statement("#ifndef GL_KHR_shader_subgroup_basic"); + statement("void subgroupBarrier() { memoryBarrierShared(); }"); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupMemBarrier)) + { + if (model == spv::ExecutionModelGLCompute) + { + statement("#ifndef GL_KHR_shader_subgroup_basic"); + statement("void subgroupMemoryBarrier() { groupMemoryBarrier(); }"); + statement("void subgroupMemoryBarrierBuffer() { groupMemoryBarrier(); }"); + statement("void subgroupMemoryBarrierShared() { memoryBarrierShared(); }"); + statement("void subgroupMemoryBarrierImage() { groupMemoryBarrier(); }"); + statement("#endif"); + } + else + { + statement("#ifndef GL_KHR_shader_subgroup_basic"); + statement("void subgroupMemoryBarrier() { memoryBarrier(); }"); + statement("void subgroupMemoryBarrierBuffer() { memoryBarrierBuffer(); }"); + statement("void subgroupMemoryBarrierImage() { memoryBarrierImage(); }"); + statement("#endif"); + } + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupInverseBallot_InclBitCount_ExclBitCout)) + { + statement("#ifndef GL_KHR_shader_subgroup_ballot"); + statement("bool subgroupInverseBallot(uvec4 value)"); + begin_scope(); + statement("return any(notEqual(value.xy & gl_SubgroupEqMask.xy, uvec2(0u)));"); + end_scope(); + + statement("uint subgroupBallotInclusiveBitCount(uvec4 value)"); + begin_scope(); + statement("uvec2 v = value.xy & gl_SubgroupLeMask.xy;"); + statement("ivec2 c = bitCount(v);"); + statement_no_indent("#ifdef GL_NV_shader_thread_group"); + statement("return uint(c.x);"); + statement_no_indent("#else"); + statement("return uint(c.x + c.y);"); + statement_no_indent("#endif"); + end_scope(); + + statement("uint subgroupBallotExclusiveBitCount(uvec4 value)"); + begin_scope(); + statement("uvec2 v = value.xy & gl_SubgroupLtMask.xy;"); + statement("ivec2 c = bitCount(v);"); + statement_no_indent("#ifdef GL_NV_shader_thread_group"); + statement("return uint(c.x);"); + statement_no_indent("#else"); + statement("return uint(c.x + c.y);"); + statement_no_indent("#endif"); + end_scope(); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBallotBitCount)) + { + statement("#ifndef GL_KHR_shader_subgroup_ballot"); + statement("uint subgroupBallotBitCount(uvec4 value)"); + begin_scope(); + statement("ivec2 c = bitCount(value.xy);"); + statement_no_indent("#ifdef GL_NV_shader_thread_group"); + statement("return uint(c.x);"); + statement_no_indent("#else"); + statement("return uint(c.x + c.y);"); + statement_no_indent("#endif"); + end_scope(); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBallotBitExtract)) + { + statement("#ifndef GL_KHR_shader_subgroup_ballot"); + statement("bool subgroupBallotBitExtract(uvec4 value, uint index)"); + begin_scope(); + statement_no_indent("#ifdef GL_NV_shader_thread_group"); + statement("uint shifted = value.x >> index;"); + statement_no_indent("#else"); + statement("uint shifted = value[index >> 5u] >> (index & 0x1fu);"); + statement_no_indent("#endif"); + statement("return (shifted & 1u) != 0u;"); + end_scope(); + statement("#endif"); + statement(""); + } + } + + if (!workaround_ubo_load_overload_types.empty()) + { + for (auto &type_id : workaround_ubo_load_overload_types) + { + auto &type = get(type_id); + statement(type_to_glsl(type), " SPIRV_Cross_workaround_load_row_major(", type_to_glsl(type), + " wrap) { return wrap; }"); + } + statement(""); + } + + if (requires_transpose_2x2) + { + statement("mat2 SPIRV_Cross_Transpose(mat2 m)"); + begin_scope(); + statement("return mat2(m[0][0], m[1][0], m[0][1], m[1][1]);"); + end_scope(); + statement(""); + } + + if (requires_transpose_3x3) + { + statement("mat3 SPIRV_Cross_Transpose(mat3 m)"); + begin_scope(); + statement("return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);"); + end_scope(); + statement(""); + } + + if (requires_transpose_4x4) + { + statement("mat4 SPIRV_Cross_Transpose(mat4 m)"); + begin_scope(); + statement("return mat4(m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], " + "m[1][2], m[2][2], m[3][2], m[0][3], m[1][3], m[2][3], m[3][3]);"); + end_scope(); + statement(""); + } +} + +// Returns a string representation of the ID, usable as a function arg. +// Default is to simply return the expression representation fo the arg ID. +// Subclasses may override to modify the return value. +string CompilerGLSL::to_func_call_arg(const SPIRFunction::Parameter &, uint32_t id) +{ + // Make sure that we use the name of the original variable, and not the parameter alias. + uint32_t name_id = id; + auto *var = maybe_get(id); + if (var && var->basevariable) + name_id = var->basevariable; + return to_expression(name_id); +} + +void CompilerGLSL::handle_invalid_expression(uint32_t id) +{ + // We tried to read an invalidated expression. + // This means we need another pass at compilation, but next time, force temporary variables so that they cannot be invalidated. + forced_temporaries.insert(id); + force_recompile(); +} + +// Converts the format of the current expression from packed to unpacked, +// by wrapping the expression in a constructor of the appropriate type. +// GLSL does not support packed formats, so simply return the expression. +// Subclasses that do will override. +string CompilerGLSL::unpack_expression_type(string expr_str, const SPIRType &, uint32_t, bool, bool) +{ + return expr_str; +} + +// Sometimes we proactively enclosed an expression where it turns out we might have not needed it after all. +void CompilerGLSL::strip_enclosed_expression(string &expr) +{ + if (expr.size() < 2 || expr.front() != '(' || expr.back() != ')') + return; + + // Have to make sure that our first and last parens actually enclose everything inside it. + uint32_t paren_count = 0; + for (auto &c : expr) + { + if (c == '(') + paren_count++; + else if (c == ')') + { + paren_count--; + + // If we hit 0 and this is not the final char, our first and final parens actually don't + // enclose the expression, and we cannot strip, e.g.: (a + b) * (c + d). + if (paren_count == 0 && &c != &expr.back()) + return; + } + } + expr.erase(expr.size() - 1, 1); + expr.erase(begin(expr)); +} + +string CompilerGLSL::enclose_expression(const string &expr) +{ + bool need_parens = false; + + // If the expression starts with a unary we need to enclose to deal with cases where we have back-to-back + // unary expressions. + if (!expr.empty()) + { + auto c = expr.front(); + if (c == '-' || c == '+' || c == '!' || c == '~' || c == '&' || c == '*') + need_parens = true; + } + + if (!need_parens) + { + uint32_t paren_count = 0; + for (auto c : expr) + { + if (c == '(' || c == '[') + paren_count++; + else if (c == ')' || c == ']') + { + assert(paren_count); + paren_count--; + } + else if (c == ' ' && paren_count == 0) + { + need_parens = true; + break; + } + } + assert(paren_count == 0); + } + + // If this expression contains any spaces which are not enclosed by parentheses, + // we need to enclose it so we can treat the whole string as an expression. + // This happens when two expressions have been part of a binary op earlier. + if (need_parens) + return join('(', expr, ')'); + else + return expr; +} + +string CompilerGLSL::dereference_expression(const SPIRType &expr_type, const std::string &expr) +{ + // If this expression starts with an address-of operator ('&'), then + // just return the part after the operator. + // TODO: Strip parens if unnecessary? + if (expr.front() == '&') + return expr.substr(1); + else if (backend.native_pointers) + return join('*', expr); + else if (expr_type.storage == StorageClassPhysicalStorageBufferEXT && expr_type.basetype != SPIRType::Struct && + expr_type.pointer_depth == 1) + { + return join(enclose_expression(expr), ".value"); + } + else + return expr; +} + +string CompilerGLSL::address_of_expression(const std::string &expr) +{ + if (expr.size() > 3 && expr[0] == '(' && expr[1] == '*' && expr.back() == ')') + { + // If we have an expression which looks like (*foo), taking the address of it is the same as stripping + // the first two and last characters. We might have to enclose the expression. + // This doesn't work for cases like (*foo + 10), + // but this is an r-value expression which we cannot take the address of anyways. + return enclose_expression(expr.substr(2, expr.size() - 3)); + } + else if (expr.front() == '*') + { + // If this expression starts with a dereference operator ('*'), then + // just return the part after the operator. + return expr.substr(1); + } + else + return join('&', enclose_expression(expr)); +} + +// Just like to_expression except that we enclose the expression inside parentheses if needed. +string CompilerGLSL::to_enclosed_expression(uint32_t id, bool register_expression_read) +{ + return enclose_expression(to_expression(id, register_expression_read)); +} + +// Used explicitly when we want to read a row-major expression, but without any transpose shenanigans. +// need_transpose must be forced to false. +string CompilerGLSL::to_unpacked_row_major_matrix_expression(uint32_t id) +{ + return unpack_expression_type(to_expression(id), expression_type(id), + get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID), + has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked), true); +} + +string CompilerGLSL::to_unpacked_expression(uint32_t id, bool register_expression_read) +{ + // If we need to transpose, it will also take care of unpacking rules. + auto *e = maybe_get(id); + bool need_transpose = e && e->need_transpose; + bool is_remapped = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID); + bool is_packed = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + + if (!need_transpose && (is_remapped || is_packed)) + { + return unpack_expression_type(to_expression(id, register_expression_read), + get_pointee_type(expression_type_id(id)), + get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID), + has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked), false); + } + else + return to_expression(id, register_expression_read); +} + +string CompilerGLSL::to_enclosed_unpacked_expression(uint32_t id, bool register_expression_read) +{ + // If we need to transpose, it will also take care of unpacking rules. + auto *e = maybe_get(id); + bool need_transpose = e && e->need_transpose; + bool is_remapped = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID); + bool is_packed = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + if (!need_transpose && (is_remapped || is_packed)) + { + return unpack_expression_type(to_expression(id, register_expression_read), expression_type(id), + get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID), + has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked), false); + } + else + return to_enclosed_expression(id, register_expression_read); +} + +string CompilerGLSL::to_dereferenced_expression(uint32_t id, bool register_expression_read) +{ + auto &type = expression_type(id); + if (type.pointer && should_dereference(id)) + return dereference_expression(type, to_enclosed_expression(id, register_expression_read)); + else + return to_expression(id, register_expression_read); +} + +string CompilerGLSL::to_pointer_expression(uint32_t id, bool register_expression_read) +{ + auto &type = expression_type(id); + if (type.pointer && expression_is_lvalue(id) && !should_dereference(id)) + return address_of_expression(to_enclosed_expression(id, register_expression_read)); + else + return to_unpacked_expression(id, register_expression_read); +} + +string CompilerGLSL::to_enclosed_pointer_expression(uint32_t id, bool register_expression_read) +{ + auto &type = expression_type(id); + if (type.pointer && expression_is_lvalue(id) && !should_dereference(id)) + return address_of_expression(to_enclosed_expression(id, register_expression_read)); + else + return to_enclosed_unpacked_expression(id, register_expression_read); +} + +string CompilerGLSL::to_extract_component_expression(uint32_t id, uint32_t index) +{ + auto expr = to_enclosed_expression(id); + if (has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked)) + return join(expr, "[", index, "]"); + else + return join(expr, ".", index_to_swizzle(index)); +} + +string CompilerGLSL::to_rerolled_array_expression(const string &base_expr, const SPIRType &type) +{ + uint32_t size = to_array_size_literal(type); + auto &parent = get(type.parent_type); + string expr = "{ "; + + for (uint32_t i = 0; i < size; i++) + { + auto subexpr = join(base_expr, "[", convert_to_string(i), "]"); + if (parent.array.empty()) + expr += subexpr; + else + expr += to_rerolled_array_expression(subexpr, parent); + + if (i + 1 < size) + expr += ", "; + } + + expr += " }"; + return expr; +} + +string CompilerGLSL::to_composite_constructor_expression(uint32_t id, bool uses_buffer_offset) +{ + auto &type = expression_type(id); + + bool reroll_array = !type.array.empty() && (!backend.array_is_value_type || + (uses_buffer_offset && !backend.buffer_offset_array_is_value_type)); + + if (reroll_array) + { + // For this case, we need to "re-roll" an array initializer from a temporary. + // We cannot simply pass the array directly, since it decays to a pointer and it cannot + // participate in a struct initializer. E.g. + // float arr[2] = { 1.0, 2.0 }; + // Foo foo = { arr }; must be transformed to + // Foo foo = { { arr[0], arr[1] } }; + // The array sizes cannot be deduced from specialization constants since we cannot use any loops. + + // We're only triggering one read of the array expression, but this is fine since arrays have to be declared + // as temporaries anyways. + return to_rerolled_array_expression(to_enclosed_expression(id), type); + } + else + return to_unpacked_expression(id); +} + +string CompilerGLSL::to_expression(uint32_t id, bool register_expression_read) +{ + auto itr = invalid_expressions.find(id); + if (itr != end(invalid_expressions)) + handle_invalid_expression(id); + + if (ir.ids[id].get_type() == TypeExpression) + { + // We might have a more complex chain of dependencies. + // A possible scenario is that we + // + // %1 = OpLoad + // %2 = OpDoSomething %1 %1. here %2 will have a dependency on %1. + // %3 = OpDoSomethingAgain %2 %2. Here %3 will lose the link to %1 since we don't propagate the dependencies like that. + // OpStore %1 %foo // Here we can invalidate %1, and hence all expressions which depend on %1. Only %2 will know since it's part of invalid_expressions. + // %4 = OpDoSomethingAnotherTime %3 %3 // If we forward all expressions we will see %1 expression after store, not before. + // + // However, we can propagate up a list of depended expressions when we used %2, so we can check if %2 is invalid when reading %3 after the store, + // and see that we should not forward reads of the original variable. + auto &expr = get(id); + for (uint32_t dep : expr.expression_dependencies) + if (invalid_expressions.find(dep) != end(invalid_expressions)) + handle_invalid_expression(dep); + } + + if (register_expression_read) + track_expression_read(id); + + switch (ir.ids[id].get_type()) + { + case TypeExpression: + { + auto &e = get(id); + if (e.base_expression) + return to_enclosed_expression(e.base_expression) + e.expression; + else if (e.need_transpose) + { + // This should not be reached for access chains, since we always deal explicitly with transpose state + // when consuming an access chain expression. + uint32_t physical_type_id = get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID); + bool is_packed = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + return convert_row_major_matrix(e.expression, get(e.expression_type), physical_type_id, + is_packed); + } + else if (flattened_structs.count(id)) + { + return load_flattened_struct(e.expression, get(e.expression_type)); + } + else + { + if (is_forcing_recompilation()) + { + // During first compilation phase, certain expression patterns can trigger exponential growth of memory. + // Avoid this by returning dummy expressions during this phase. + // Do not use empty expressions here, because those are sentinels for other cases. + return "_"; + } + else + return e.expression; + } + } + + case TypeConstant: + { + auto &c = get(id); + auto &type = get(c.constant_type); + + // WorkGroupSize may be a constant. + auto &dec = ir.meta[c.self].decoration; + if (dec.builtin) + return builtin_to_glsl(dec.builtin_type, StorageClassGeneric); + else if (c.specialization) + return to_name(id); + else if (c.is_used_as_lut) + return to_name(id); + else if (type.basetype == SPIRType::Struct && !backend.can_declare_struct_inline) + return to_name(id); + else if (!type.array.empty() && !backend.can_declare_arrays_inline) + return to_name(id); + else + return constant_expression(c); + } + + case TypeConstantOp: + return to_name(id); + + case TypeVariable: + { + auto &var = get(id); + // If we try to use a loop variable before the loop header, we have to redirect it to the static expression, + // the variable has not been declared yet. + if (var.statically_assigned || (var.loop_variable && !var.loop_variable_enable)) + return to_expression(var.static_expression); + else if (var.deferred_declaration) + { + var.deferred_declaration = false; + return variable_decl(var); + } + else if (flattened_structs.count(id)) + { + return load_flattened_struct(to_name(id), get(var.basetype)); + } + else + { + auto &dec = ir.meta[var.self].decoration; + if (dec.builtin) + return builtin_to_glsl(dec.builtin_type, var.storage); + else + return to_name(id); + } + } + + case TypeCombinedImageSampler: + // This type should never be taken the expression of directly. + // The intention is that texture sampling functions will extract the image and samplers + // separately and take their expressions as needed. + // GLSL does not use this type because OpSampledImage immediately creates a combined image sampler + // expression ala sampler2D(texture, sampler). + SPIRV_CROSS_THROW("Combined image samplers have no default expression representation."); + + case TypeAccessChain: + // We cannot express this type. They only have meaning in other OpAccessChains, OpStore or OpLoad. + SPIRV_CROSS_THROW("Access chains have no default expression representation."); + + default: + return to_name(id); + } +} + +string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) +{ + auto &type = get(cop.basetype); + bool binary = false; + bool unary = false; + string op; + + if (is_legacy() && is_unsigned_opcode(cop.opcode)) + SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy targets."); + + // TODO: Find a clean way to reuse emit_instruction. + switch (cop.opcode) + { + case OpSConvert: + case OpUConvert: + case OpFConvert: + op = type_to_glsl_constructor(type); + break; + +#define GLSL_BOP(opname, x) \ + case Op##opname: \ + binary = true; \ + op = x; \ + break + +#define GLSL_UOP(opname, x) \ + case Op##opname: \ + unary = true; \ + op = x; \ + break + + GLSL_UOP(SNegate, "-"); + GLSL_UOP(Not, "~"); + GLSL_BOP(IAdd, "+"); + GLSL_BOP(ISub, "-"); + GLSL_BOP(IMul, "*"); + GLSL_BOP(SDiv, "/"); + GLSL_BOP(UDiv, "/"); + GLSL_BOP(UMod, "%"); + GLSL_BOP(SMod, "%"); + GLSL_BOP(ShiftRightLogical, ">>"); + GLSL_BOP(ShiftRightArithmetic, ">>"); + GLSL_BOP(ShiftLeftLogical, "<<"); + GLSL_BOP(BitwiseOr, "|"); + GLSL_BOP(BitwiseXor, "^"); + GLSL_BOP(BitwiseAnd, "&"); + GLSL_BOP(LogicalOr, "||"); + GLSL_BOP(LogicalAnd, "&&"); + GLSL_UOP(LogicalNot, "!"); + GLSL_BOP(LogicalEqual, "=="); + GLSL_BOP(LogicalNotEqual, "!="); + GLSL_BOP(IEqual, "=="); + GLSL_BOP(INotEqual, "!="); + GLSL_BOP(ULessThan, "<"); + GLSL_BOP(SLessThan, "<"); + GLSL_BOP(ULessThanEqual, "<="); + GLSL_BOP(SLessThanEqual, "<="); + GLSL_BOP(UGreaterThan, ">"); + GLSL_BOP(SGreaterThan, ">"); + GLSL_BOP(UGreaterThanEqual, ">="); + GLSL_BOP(SGreaterThanEqual, ">="); + + case OpSelect: + { + if (cop.arguments.size() < 3) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + + // This one is pretty annoying. It's triggered from + // uint(bool), int(bool) from spec constants. + // In order to preserve its compile-time constness in Vulkan GLSL, + // we need to reduce the OpSelect expression back to this simplified model. + // If we cannot, fail. + if (to_trivial_mix_op(type, op, cop.arguments[2], cop.arguments[1], cop.arguments[0])) + { + // Implement as a simple cast down below. + } + else + { + // Implement a ternary and pray the compiler understands it :) + return to_ternary_expression(type, cop.arguments[0], cop.arguments[1], cop.arguments[2]); + } + break; + } + + case OpVectorShuffle: + { + string expr = type_to_glsl_constructor(type); + expr += "("; + + uint32_t left_components = expression_type(cop.arguments[0]).vecsize; + string left_arg = to_enclosed_expression(cop.arguments[0]); + string right_arg = to_enclosed_expression(cop.arguments[1]); + + for (uint32_t i = 2; i < uint32_t(cop.arguments.size()); i++) + { + uint32_t index = cop.arguments[i]; + if (index >= left_components) + expr += right_arg + "." + "xyzw"[index - left_components]; + else + expr += left_arg + "." + "xyzw"[index]; + + if (i + 1 < uint32_t(cop.arguments.size())) + expr += ", "; + } + + expr += ")"; + return expr; + } + + case OpCompositeExtract: + { + auto expr = access_chain_internal(cop.arguments[0], &cop.arguments[1], uint32_t(cop.arguments.size() - 1), + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr); + return expr; + } + + case OpCompositeInsert: + SPIRV_CROSS_THROW("OpCompositeInsert spec constant op is not supported."); + + default: + // Some opcodes are unimplemented here, these are currently not possible to test from glslang. + SPIRV_CROSS_THROW("Unimplemented spec constant op."); + } + + uint32_t bit_width = 0; + if (unary || binary || cop.opcode == OpSConvert || cop.opcode == OpUConvert) + bit_width = expression_type(cop.arguments[0]).width; + + SPIRType::BaseType input_type; + bool skip_cast_if_equal_type = opcode_is_sign_invariant(cop.opcode); + + switch (cop.opcode) + { + case OpIEqual: + case OpINotEqual: + input_type = to_signed_basetype(bit_width); + break; + + case OpSLessThan: + case OpSLessThanEqual: + case OpSGreaterThan: + case OpSGreaterThanEqual: + case OpSMod: + case OpSDiv: + case OpShiftRightArithmetic: + case OpSConvert: + case OpSNegate: + input_type = to_signed_basetype(bit_width); + break; + + case OpULessThan: + case OpULessThanEqual: + case OpUGreaterThan: + case OpUGreaterThanEqual: + case OpUMod: + case OpUDiv: + case OpShiftRightLogical: + case OpUConvert: + input_type = to_unsigned_basetype(bit_width); + break; + + default: + input_type = type.basetype; + break; + } + +#undef GLSL_BOP +#undef GLSL_UOP + if (binary) + { + if (cop.arguments.size() < 2) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + + string cast_op0; + string cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, cop.arguments[0], + cop.arguments[1], skip_cast_if_equal_type); + + if (type.basetype != input_type && type.basetype != SPIRType::Boolean) + { + expected_type.basetype = input_type; + auto expr = bitcast_glsl_op(type, expected_type); + expr += '('; + expr += join(cast_op0, " ", op, " ", cast_op1); + expr += ')'; + return expr; + } + else + return join("(", cast_op0, " ", op, " ", cast_op1, ")"); + } + else if (unary) + { + if (cop.arguments.size() < 1) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + + // Auto-bitcast to result type as needed. + // Works around various casting scenarios in glslang as there is no OpBitcast for specialization constants. + return join("(", op, bitcast_glsl(type, cop.arguments[0]), ")"); + } + else if (cop.opcode == OpSConvert || cop.opcode == OpUConvert) + { + if (cop.arguments.size() < 1) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + + auto &arg_type = expression_type(cop.arguments[0]); + if (arg_type.width < type.width && input_type != arg_type.basetype) + { + auto expected = arg_type; + expected.basetype = input_type; + return join(op, "(", bitcast_glsl(expected, cop.arguments[0]), ")"); + } + else + return join(op, "(", to_expression(cop.arguments[0]), ")"); + } + else + { + if (cop.arguments.size() < 1) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + return join(op, "(", to_expression(cop.arguments[0]), ")"); + } +} + +string CompilerGLSL::constant_expression(const SPIRConstant &c) +{ + auto &type = get(c.constant_type); + + if (type.pointer) + { + return backend.null_pointer_literal; + } + else if (!c.subconstants.empty()) + { + // Handles Arrays and structures. + string res; + + // Allow Metal to use the array template to make arrays a value type + bool needs_trailing_tracket = false; + if (backend.use_initializer_list && backend.use_typed_initializer_list && type.basetype == SPIRType::Struct && + type.array.empty()) + { + res = type_to_glsl_constructor(type) + "{ "; + } + else if (backend.use_initializer_list && backend.use_typed_initializer_list && backend.array_is_value_type && + !type.array.empty()) + { + res = type_to_glsl_constructor(type) + "({ "; + needs_trailing_tracket = true; + } + else if (backend.use_initializer_list) + { + res = "{ "; + } + else + { + res = type_to_glsl_constructor(type) + "("; + } + + for (auto &elem : c.subconstants) + { + auto &subc = get(elem); + if (subc.specialization) + res += to_name(elem); + else + res += constant_expression(subc); + + if (&elem != &c.subconstants.back()) + res += ", "; + } + + res += backend.use_initializer_list ? " }" : ")"; + if (needs_trailing_tracket) + res += ")"; + + return res; + } + else if (type.basetype == SPIRType::Struct && type.member_types.size() == 0) + { + // Metal tessellation likes empty structs which are then constant expressions. + if (backend.supports_empty_struct) + return "{ }"; + else if (backend.use_typed_initializer_list) + return join(type_to_glsl(get(c.constant_type)), "{ 0 }"); + else if (backend.use_initializer_list) + return "{ 0 }"; + else + return join(type_to_glsl(get(c.constant_type)), "(0)"); + } + else if (c.columns() == 1) + { + return constant_expression_vector(c, 0); + } + else + { + string res = type_to_glsl(get(c.constant_type)) + "("; + for (uint32_t col = 0; col < c.columns(); col++) + { + if (c.specialization_constant_id(col) != 0) + res += to_name(c.specialization_constant_id(col)); + else + res += constant_expression_vector(c, col); + + if (col + 1 < c.columns()) + res += ", "; + } + res += ")"; + return res; + } +} + +#ifdef _MSC_VER +// sprintf warning. +// We cannot rely on snprintf existing because, ..., MSVC. +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +string CompilerGLSL::convert_half_to_string(const SPIRConstant &c, uint32_t col, uint32_t row) +{ + string res; + float float_value = c.scalar_f16(col, row); + + // There is no literal "hf" in GL_NV_gpu_shader5, so to avoid lots + // of complicated workarounds, just value-cast to the half type always. + if (std::isnan(float_value) || std::isinf(float_value)) + { + SPIRType type; + type.basetype = SPIRType::Half; + type.vecsize = 1; + type.columns = 1; + + if (float_value == numeric_limits::infinity()) + res = join(type_to_glsl(type), "(1.0 / 0.0)"); + else if (float_value == -numeric_limits::infinity()) + res = join(type_to_glsl(type), "(-1.0 / 0.0)"); + else if (std::isnan(float_value)) + res = join(type_to_glsl(type), "(0.0 / 0.0)"); + else + SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant."); + } + else + { + SPIRType type; + type.basetype = SPIRType::Half; + type.vecsize = 1; + type.columns = 1; + res = join(type_to_glsl(type), "(", convert_to_string(float_value, current_locale_radix_character), ")"); + } + + return res; +} + +string CompilerGLSL::convert_float_to_string(const SPIRConstant &c, uint32_t col, uint32_t row) +{ + string res; + float float_value = c.scalar_f32(col, row); + + if (std::isnan(float_value) || std::isinf(float_value)) + { + // Use special representation. + if (!is_legacy()) + { + SPIRType out_type; + SPIRType in_type; + out_type.basetype = SPIRType::Float; + in_type.basetype = SPIRType::UInt; + out_type.vecsize = 1; + in_type.vecsize = 1; + out_type.width = 32; + in_type.width = 32; + + char print_buffer[32]; + sprintf(print_buffer, "0x%xu", c.scalar(col, row)); + res = join(bitcast_glsl_op(out_type, in_type), "(", print_buffer, ")"); + } + else + { + if (float_value == numeric_limits::infinity()) + { + if (backend.float_literal_suffix) + res = "(1.0f / 0.0f)"; + else + res = "(1.0 / 0.0)"; + } + else if (float_value == -numeric_limits::infinity()) + { + if (backend.float_literal_suffix) + res = "(-1.0f / 0.0f)"; + else + res = "(-1.0 / 0.0)"; + } + else if (std::isnan(float_value)) + { + if (backend.float_literal_suffix) + res = "(0.0f / 0.0f)"; + else + res = "(0.0 / 0.0)"; + } + else + SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant."); + } + } + else + { + res = convert_to_string(float_value, current_locale_radix_character); + if (backend.float_literal_suffix) + res += "f"; + } + + return res; +} + +std::string CompilerGLSL::convert_double_to_string(const SPIRConstant &c, uint32_t col, uint32_t row) +{ + string res; + double double_value = c.scalar_f64(col, row); + + if (std::isnan(double_value) || std::isinf(double_value)) + { + // Use special representation. + if (!is_legacy()) + { + SPIRType out_type; + SPIRType in_type; + out_type.basetype = SPIRType::Double; + in_type.basetype = SPIRType::UInt64; + out_type.vecsize = 1; + in_type.vecsize = 1; + out_type.width = 64; + in_type.width = 64; + + uint64_t u64_value = c.scalar_u64(col, row); + + if (options.es) + SPIRV_CROSS_THROW("64-bit integers/float not supported in ES profile."); + require_extension_internal("GL_ARB_gpu_shader_int64"); + + char print_buffer[64]; + sprintf(print_buffer, "0x%llx%s", static_cast(u64_value), + backend.long_long_literal_suffix ? "ull" : "ul"); + res = join(bitcast_glsl_op(out_type, in_type), "(", print_buffer, ")"); + } + else + { + if (options.es) + SPIRV_CROSS_THROW("FP64 not supported in ES profile."); + if (options.version < 400) + require_extension_internal("GL_ARB_gpu_shader_fp64"); + + if (double_value == numeric_limits::infinity()) + { + if (backend.double_literal_suffix) + res = "(1.0lf / 0.0lf)"; + else + res = "(1.0 / 0.0)"; + } + else if (double_value == -numeric_limits::infinity()) + { + if (backend.double_literal_suffix) + res = "(-1.0lf / 0.0lf)"; + else + res = "(-1.0 / 0.0)"; + } + else if (std::isnan(double_value)) + { + if (backend.double_literal_suffix) + res = "(0.0lf / 0.0lf)"; + else + res = "(0.0 / 0.0)"; + } + else + SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant."); + } + } + else + { + res = convert_to_string(double_value, current_locale_radix_character); + if (backend.double_literal_suffix) + res += "lf"; + } + + return res; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +string CompilerGLSL::constant_expression_vector(const SPIRConstant &c, uint32_t vector) +{ + auto type = get(c.constant_type); + type.columns = 1; + + auto scalar_type = type; + scalar_type.vecsize = 1; + + string res; + bool splat = backend.use_constructor_splatting && c.vector_size() > 1; + bool swizzle_splat = backend.can_swizzle_scalar && c.vector_size() > 1; + + if (!type_is_floating_point(type)) + { + // Cannot swizzle literal integers as a special case. + swizzle_splat = false; + } + + if (splat || swizzle_splat) + { + // Cannot use constant splatting if we have specialization constants somewhere in the vector. + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.specialization_constant_id(vector, i) != 0) + { + splat = false; + swizzle_splat = false; + break; + } + } + } + + if (splat || swizzle_splat) + { + if (type.width == 64) + { + uint64_t ident = c.scalar_u64(vector, 0); + for (uint32_t i = 1; i < c.vector_size(); i++) + { + if (ident != c.scalar_u64(vector, i)) + { + splat = false; + swizzle_splat = false; + break; + } + } + } + else + { + uint32_t ident = c.scalar(vector, 0); + for (uint32_t i = 1; i < c.vector_size(); i++) + { + if (ident != c.scalar(vector, i)) + { + splat = false; + swizzle_splat = false; + } + } + } + } + + if (c.vector_size() > 1 && !swizzle_splat) + res += type_to_glsl(type) + "("; + + switch (type.basetype) + { + case SPIRType::Half: + if (splat || swizzle_splat) + { + res += convert_half_to_string(c, vector, 0); + if (swizzle_splat) + res = remap_swizzle(get(c.constant_type), 1, res); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + res += convert_half_to_string(c, vector, i); + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Float: + if (splat || swizzle_splat) + { + res += convert_float_to_string(c, vector, 0); + if (swizzle_splat) + res = remap_swizzle(get(c.constant_type), 1, res); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + res += convert_float_to_string(c, vector, i); + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Double: + if (splat || swizzle_splat) + { + res += convert_double_to_string(c, vector, 0); + if (swizzle_splat) + res = remap_swizzle(get(c.constant_type), 1, res); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + res += convert_double_to_string(c, vector, i); + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Int64: + if (splat) + { + res += convert_to_string(c.scalar_i64(vector, 0)); + if (backend.long_long_literal_suffix) + res += "ll"; + else + res += "l"; + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + { + res += convert_to_string(c.scalar_i64(vector, i)); + if (backend.long_long_literal_suffix) + res += "ll"; + else + res += "l"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::UInt64: + if (splat) + { + res += convert_to_string(c.scalar_u64(vector, 0)); + if (backend.long_long_literal_suffix) + res += "ull"; + else + res += "ul"; + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + { + res += convert_to_string(c.scalar_u64(vector, i)); + if (backend.long_long_literal_suffix) + res += "ull"; + else + res += "ul"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::UInt: + if (splat) + { + res += convert_to_string(c.scalar(vector, 0)); + if (is_legacy()) + { + // Fake unsigned constant literals with signed ones if possible. + // Things like array sizes, etc, tend to be unsigned even though they could just as easily be signed. + if (c.scalar_i32(vector, 0) < 0) + SPIRV_CROSS_THROW("Tried to convert uint literal into int, but this made the literal negative."); + } + else if (backend.uint32_t_literal_suffix) + res += "u"; + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + { + res += convert_to_string(c.scalar(vector, i)); + if (is_legacy()) + { + // Fake unsigned constant literals with signed ones if possible. + // Things like array sizes, etc, tend to be unsigned even though they could just as easily be signed. + if (c.scalar_i32(vector, i) < 0) + SPIRV_CROSS_THROW("Tried to convert uint literal into int, but this made " + "the literal negative."); + } + else if (backend.uint32_t_literal_suffix) + res += "u"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Int: + if (splat) + res += convert_to_string(c.scalar_i32(vector, 0)); + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + res += convert_to_string(c.scalar_i32(vector, i)); + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::UShort: + if (splat) + { + res += convert_to_string(c.scalar(vector, 0)); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + { + if (*backend.uint16_t_literal_suffix) + { + res += convert_to_string(c.scalar_u16(vector, i)); + res += backend.uint16_t_literal_suffix; + } + else + { + // If backend doesn't have a literal suffix, we need to value cast. + res += type_to_glsl(scalar_type); + res += "("; + res += convert_to_string(c.scalar_u16(vector, i)); + res += ")"; + } + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Short: + if (splat) + { + res += convert_to_string(c.scalar_i16(vector, 0)); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + { + if (*backend.int16_t_literal_suffix) + { + res += convert_to_string(c.scalar_i16(vector, i)); + res += backend.int16_t_literal_suffix; + } + else + { + // If backend doesn't have a literal suffix, we need to value cast. + res += type_to_glsl(scalar_type); + res += "("; + res += convert_to_string(c.scalar_i16(vector, i)); + res += ")"; + } + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::UByte: + if (splat) + { + res += convert_to_string(c.scalar_u8(vector, 0)); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + { + res += type_to_glsl(scalar_type); + res += "("; + res += convert_to_string(c.scalar_u8(vector, i)); + res += ")"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::SByte: + if (splat) + { + res += convert_to_string(c.scalar_i8(vector, 0)); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + { + res += type_to_glsl(scalar_type); + res += "("; + res += convert_to_string(c.scalar_i8(vector, i)); + res += ")"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Boolean: + if (splat) + res += c.scalar(vector, 0) ? "true" : "false"; + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_name(c.specialization_constant_id(vector, i)); + else + res += c.scalar(vector, i) ? "true" : "false"; + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + default: + SPIRV_CROSS_THROW("Invalid constant expression basetype."); + } + + if (c.vector_size() > 1 && !swizzle_splat) + res += ")"; + + return res; +} + +SPIRExpression &CompilerGLSL::emit_uninitialized_temporary_expression(uint32_t type, uint32_t id) +{ + forced_temporaries.insert(id); + emit_uninitialized_temporary(type, id); + return set(id, to_name(id), type, true); +} + +void CompilerGLSL::emit_uninitialized_temporary(uint32_t result_type, uint32_t result_id) +{ + // If we're declaring temporaries inside continue blocks, + // we must declare the temporary in the loop header so that the continue block can avoid declaring new variables. + if (current_continue_block && !hoisted_temporaries.count(result_id)) + { + auto &header = get(current_continue_block->loop_dominator); + if (find_if(begin(header.declare_temporary), end(header.declare_temporary), + [result_type, result_id](const pair &tmp) { + return tmp.first == result_type && tmp.second == result_id; + }) == end(header.declare_temporary)) + { + header.declare_temporary.emplace_back(result_type, result_id); + hoisted_temporaries.insert(result_id); + force_recompile(); + } + } + else if (hoisted_temporaries.count(result_id) == 0) + { + auto &type = get(result_type); + auto &flags = ir.meta[result_id].decoration.decoration_flags; + + // The result_id has not been made into an expression yet, so use flags interface. + add_local_variable_name(result_id); + + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(result_type)); + + statement(flags_to_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), initializer, ";"); + } +} + +string CompilerGLSL::declare_temporary(uint32_t result_type, uint32_t result_id) +{ + auto &type = get(result_type); + auto &flags = ir.meta[result_id].decoration.decoration_flags; + + // If we're declaring temporaries inside continue blocks, + // we must declare the temporary in the loop header so that the continue block can avoid declaring new variables. + if (current_continue_block && !hoisted_temporaries.count(result_id)) + { + auto &header = get(current_continue_block->loop_dominator); + if (find_if(begin(header.declare_temporary), end(header.declare_temporary), + [result_type, result_id](const pair &tmp) { + return tmp.first == result_type && tmp.second == result_id; + }) == end(header.declare_temporary)) + { + header.declare_temporary.emplace_back(result_type, result_id); + hoisted_temporaries.insert(result_id); + force_recompile(); + } + + return join(to_name(result_id), " = "); + } + else if (hoisted_temporaries.count(result_id)) + { + // The temporary has already been declared earlier, so just "declare" the temporary by writing to it. + return join(to_name(result_id), " = "); + } + else + { + // The result_id has not been made into an expression yet, so use flags interface. + add_local_variable_name(result_id); + return join(flags_to_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), " = "); + } +} + +bool CompilerGLSL::expression_is_forwarded(uint32_t id) const +{ + return forwarded_temporaries.count(id) != 0; +} + +bool CompilerGLSL::expression_suppresses_usage_tracking(uint32_t id) const +{ + return suppressed_usage_tracking.count(id) != 0; +} + +bool CompilerGLSL::expression_read_implies_multiple_reads(uint32_t id) const +{ + auto *expr = maybe_get(id); + if (!expr) + return false; + + // If we're emitting code at a deeper loop level than when we emitted the expression, + // we're probably reading the same expression over and over. + return current_loop_level > expr->emitted_loop_level; +} + +SPIRExpression &CompilerGLSL::emit_op(uint32_t result_type, uint32_t result_id, const string &rhs, bool forwarding, + bool suppress_usage_tracking) +{ + if (forwarding && (forced_temporaries.find(result_id) == end(forced_temporaries))) + { + // Just forward it without temporary. + // If the forward is trivial, we do not force flushing to temporary for this expression. + forwarded_temporaries.insert(result_id); + if (suppress_usage_tracking) + suppressed_usage_tracking.insert(result_id); + + return set(result_id, rhs, result_type, true); + } + else + { + // If expression isn't immutable, bind it to a temporary and make the new temporary immutable (they always are). + statement(declare_temporary(result_type, result_id), rhs, ";"); + return set(result_id, to_name(result_id), result_type, true); + } +} + +void CompilerGLSL::emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op) +{ + bool forward = should_forward(op0); + emit_op(result_type, result_id, join(op, to_enclosed_unpacked_expression(op0)), forward); + inherit_expression_dependencies(result_id, op0); +} + +void CompilerGLSL::emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1); + emit_op(result_type, result_id, + join(to_enclosed_unpacked_expression(op0), " ", op, " ", to_enclosed_unpacked_expression(op1)), forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerGLSL::emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op) +{ + auto &type = get(result_type); + auto expr = type_to_glsl_constructor(type); + expr += '('; + for (uint32_t i = 0; i < type.vecsize; i++) + { + // Make sure to call to_expression multiple times to ensure + // that these expressions are properly flushed to temporaries if needed. + expr += op; + expr += to_extract_component_expression(operand, i); + + if (i + 1 < type.vecsize) + expr += ", "; + } + expr += ')'; + emit_op(result_type, result_id, expr, should_forward(operand)); + + inherit_expression_dependencies(result_id, operand); +} + +void CompilerGLSL::emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, bool negate, SPIRType::BaseType expected_type) +{ + auto &type0 = expression_type(op0); + auto &type1 = expression_type(op1); + + SPIRType target_type0 = type0; + SPIRType target_type1 = type1; + target_type0.basetype = expected_type; + target_type1.basetype = expected_type; + target_type0.vecsize = 1; + target_type1.vecsize = 1; + + auto &type = get(result_type); + auto expr = type_to_glsl_constructor(type); + expr += '('; + for (uint32_t i = 0; i < type.vecsize; i++) + { + // Make sure to call to_expression multiple times to ensure + // that these expressions are properly flushed to temporaries if needed. + if (negate) + expr += "!("; + + if (expected_type != SPIRType::Unknown && type0.basetype != expected_type) + expr += bitcast_expression(target_type0, type0.basetype, to_extract_component_expression(op0, i)); + else + expr += to_extract_component_expression(op0, i); + + expr += ' '; + expr += op; + expr += ' '; + + if (expected_type != SPIRType::Unknown && type1.basetype != expected_type) + expr += bitcast_expression(target_type1, type1.basetype, to_extract_component_expression(op1, i)); + else + expr += to_extract_component_expression(op1, i); + + if (negate) + expr += ")"; + + if (i + 1 < type.vecsize) + expr += ", "; + } + expr += ')'; + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +SPIRType CompilerGLSL::binary_op_bitcast_helper(string &cast_op0, string &cast_op1, SPIRType::BaseType &input_type, + uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type) +{ + auto &type0 = expression_type(op0); + auto &type1 = expression_type(op1); + + // We have to bitcast if our inputs are of different type, or if our types are not equal to expected inputs. + // For some functions like OpIEqual and INotEqual, we don't care if inputs are of different types than expected + // since equality test is exactly the same. + bool cast = (type0.basetype != type1.basetype) || (!skip_cast_if_equal_type && type0.basetype != input_type); + + // Create a fake type so we can bitcast to it. + // We only deal with regular arithmetic types here like int, uints and so on. + SPIRType expected_type; + expected_type.basetype = input_type; + expected_type.vecsize = type0.vecsize; + expected_type.columns = type0.columns; + expected_type.width = type0.width; + + if (cast) + { + cast_op0 = bitcast_glsl(expected_type, op0); + cast_op1 = bitcast_glsl(expected_type, op1); + } + else + { + // If we don't cast, our actual input type is that of the first (or second) argument. + cast_op0 = to_enclosed_unpacked_expression(op0); + cast_op1 = to_enclosed_unpacked_expression(op1); + input_type = type0.basetype; + } + + return expected_type; +} + +bool CompilerGLSL::emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) +{ + // Some bitcasts may require complex casting sequences, and are implemented here. + // Otherwise a simply unary function will do with bitcast_glsl_op. + + auto &output_type = get(result_type); + auto &input_type = expression_type(op0); + string expr; + + if (output_type.basetype == SPIRType::Half && input_type.basetype == SPIRType::Float && input_type.vecsize == 1) + expr = join("unpackFloat2x16(floatBitsToUint(", to_unpacked_expression(op0), "))"); + else if (output_type.basetype == SPIRType::Float && input_type.basetype == SPIRType::Half && + input_type.vecsize == 2) + expr = join("uintBitsToFloat(packFloat2x16(", to_unpacked_expression(op0), "))"); + else + return false; + + emit_op(result_type, id, expr, should_forward(op0)); + return true; +} + +void CompilerGLSL::emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) +{ + string cast_op0, cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type); + auto &out_type = get(result_type); + + // We might have casted away from the result type, so bitcast again. + // For example, arithmetic right shift with uint inputs. + // Special case boolean outputs since relational opcodes output booleans instead of int/uint. + string expr; + if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean) + { + expected_type.basetype = input_type; + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(cast_op0, " ", op, " ", cast_op1); + expr += ')'; + } + else + expr += join(cast_op0, " ", op, " ", cast_op1); + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerGLSL::emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op) +{ + bool forward = should_forward(op0); + emit_op(result_type, result_id, join(op, "(", to_unpacked_expression(op0), ")"), forward); + inherit_expression_dependencies(result_id, op0); +} + +void CompilerGLSL::emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1); + emit_op(result_type, result_id, join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ")"), + forward); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerGLSL::emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op, + SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type) +{ + auto &out_type = get(result_type); + auto &expr_type = expression_type(op0); + auto expected_type = out_type; + + // Bit-widths might be different in unary cases because we use it for SConvert/UConvert and friends. + expected_type.basetype = input_type; + expected_type.width = expr_type.width; + string cast_op = expr_type.basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + + string expr; + if (out_type.basetype != expected_result_type) + { + expected_type.basetype = expected_result_type; + expected_type.width = out_type.width; + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0)); + inherit_expression_dependencies(result_id, op0); +} + +// Very special case. Handling bitfieldExtract requires us to deal with different bitcasts of different signs +// and different vector sizes all at once. Need a special purpose method here. +void CompilerGLSL::emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, + SPIRType::BaseType expected_result_type, + SPIRType::BaseType input_type0, SPIRType::BaseType input_type1, + SPIRType::BaseType input_type2) +{ + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type0; + + string cast_op0 = + expression_type(op0).basetype != input_type0 ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + + auto op1_expr = to_unpacked_expression(op1); + auto op2_expr = to_unpacked_expression(op2); + + // Use value casts here instead. Input must be exactly int or uint, but SPIR-V might be 16-bit. + expected_type.basetype = input_type1; + expected_type.vecsize = 1; + string cast_op1 = expression_type(op1).basetype != input_type1 ? + join(type_to_glsl_constructor(expected_type), "(", op1_expr, ")") : + op1_expr; + + expected_type.basetype = input_type2; + expected_type.vecsize = 1; + string cast_op2 = expression_type(op2).basetype != input_type2 ? + join(type_to_glsl_constructor(expected_type), "(", op2_expr, ")") : + op2_expr; + + string expr; + if (out_type.basetype != expected_result_type) + { + expected_type.vecsize = out_type.vecsize; + expected_type.basetype = expected_result_type; + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); +} + +void CompilerGLSL::emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, SPIRType::BaseType input_type) +{ + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type; + string cast_op0 = + expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + string cast_op1 = + expression_type(op1).basetype != input_type ? bitcast_glsl(expected_type, op1) : to_unpacked_expression(op1); + string cast_op2 = + expression_type(op2).basetype != input_type ? bitcast_glsl(expected_type, op2) : to_unpacked_expression(op2); + + string expr; + if (out_type.basetype != input_type) + { + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); +} + +void CompilerGLSL::emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, + uint32_t op1, const char *op, SPIRType::BaseType input_type) +{ + // Special purpose method for implementing clustered subgroup opcodes. + // Main difference is that op1 does not participate in any casting, it needs to be a literal. + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type; + string cast_op0 = + expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + + string expr; + if (out_type.basetype != input_type) + { + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", to_expression(op1), ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", to_expression(op1), ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0)); + inherit_expression_dependencies(result_id, op0); +} + +void CompilerGLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) +{ + string cast_op0, cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type); + auto &out_type = get(result_type); + + // Special case boolean outputs since relational opcodes output booleans instead of int/uint. + string expr; + if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean) + { + expected_type.basetype = input_type; + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerGLSL::emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2); + emit_op(result_type, result_id, + join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ", ", + to_unpacked_expression(op2), ")"), + forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); +} + +void CompilerGLSL::emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, uint32_t op3, const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3); + emit_op(result_type, result_id, + join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ", ", + to_unpacked_expression(op2), ", ", to_unpacked_expression(op3), ")"), + forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); + inherit_expression_dependencies(result_id, op3); +} + +void CompilerGLSL::emit_bitfield_insert_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, uint32_t op3, const char *op, + SPIRType::BaseType offset_count_type) +{ + // Only need to cast offset/count arguments. Types of base/insert must be same as result type, + // and bitfieldInsert is sign invariant. + bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3); + + auto op0_expr = to_unpacked_expression(op0); + auto op1_expr = to_unpacked_expression(op1); + auto op2_expr = to_unpacked_expression(op2); + auto op3_expr = to_unpacked_expression(op3); + + SPIRType target_type; + target_type.vecsize = 1; + target_type.basetype = offset_count_type; + + if (expression_type(op2).basetype != offset_count_type) + { + // Value-cast here. Input might be 16-bit. GLSL requires int. + op2_expr = join(type_to_glsl_constructor(target_type), "(", op2_expr, ")"); + } + + if (expression_type(op3).basetype != offset_count_type) + { + // Value-cast here. Input might be 16-bit. GLSL requires int. + op3_expr = join(type_to_glsl_constructor(target_type), "(", op3_expr, ")"); + } + + emit_op(result_type, result_id, join(op, "(", op0_expr, ", ", op1_expr, ", ", op2_expr, ", ", op3_expr, ")"), + forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); + inherit_expression_dependencies(result_id, op3); +} + +string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t tex) +{ + const char *type; + switch (imgtype.image.dim) + { + case spv::Dim1D: + type = (imgtype.image.arrayed && !options.es) ? "1DArray" : "1D"; + break; + case spv::Dim2D: + type = (imgtype.image.arrayed && !options.es) ? "2DArray" : "2D"; + break; + case spv::Dim3D: + type = "3D"; + break; + case spv::DimCube: + type = "Cube"; + break; + case spv::DimRect: + type = "2DRect"; + break; + case spv::DimBuffer: + type = "Buffer"; + break; + case spv::DimSubpassData: + type = "2D"; + break; + default: + type = ""; + break; + } + + // In legacy GLSL, an extension is required for textureLod in the fragment + // shader or textureGrad anywhere. + bool legacy_lod_ext = false; + auto &execution = get_entry_point(); + if (op == "textureGrad" || op == "textureProjGrad" || + ((op == "textureLod" || op == "textureProjLod") && execution.model != ExecutionModelVertex)) + { + if (is_legacy_es()) + { + legacy_lod_ext = true; + require_extension_internal("GL_EXT_shader_texture_lod"); + } + else if (is_legacy_desktop()) + require_extension_internal("GL_ARB_shader_texture_lod"); + } + + if (op == "textureLodOffset" || op == "textureProjLodOffset") + { + if (is_legacy_es()) + SPIRV_CROSS_THROW(join(op, " not allowed in legacy ES")); + + require_extension_internal("GL_EXT_gpu_shader4"); + } + + // GLES has very limited support for shadow samplers. + // Basically shadow2D and shadow2DProj work through EXT_shadow_samplers, + // everything else can just throw + if (image_is_comparison(imgtype, tex) && is_legacy_es()) + { + if (op == "texture" || op == "textureProj") + require_extension_internal("GL_EXT_shadow_samplers"); + else + SPIRV_CROSS_THROW(join(op, " not allowed on depth samplers in legacy ES")); + } + + bool is_es_and_depth = is_legacy_es() && image_is_comparison(imgtype, tex); + std::string type_prefix = image_is_comparison(imgtype, tex) ? "shadow" : "texture"; + + if (op == "texture") + return is_es_and_depth ? join(type_prefix, type, "EXT") : join(type_prefix, type); + else if (op == "textureLod") + return join(type_prefix, type, legacy_lod_ext ? "LodEXT" : "Lod"); + else if (op == "textureProj") + return join(type_prefix, type, is_es_and_depth ? "ProjEXT" : "Proj"); + else if (op == "textureGrad") + return join(type_prefix, type, is_legacy_es() ? "GradEXT" : is_legacy_desktop() ? "GradARB" : "Grad"); + else if (op == "textureProjLod") + return join(type_prefix, type, legacy_lod_ext ? "ProjLodEXT" : "ProjLod"); + else if (op == "textureLodOffset") + return join(type_prefix, type, "LodOffset"); + else if (op == "textureProjGrad") + return join(type_prefix, type, + is_legacy_es() ? "ProjGradEXT" : is_legacy_desktop() ? "ProjGradARB" : "ProjGrad"); + else if (op == "textureProjLodOffset") + return join(type_prefix, type, "ProjLodOffset"); + else + { + SPIRV_CROSS_THROW(join("Unsupported legacy texture op: ", op)); + } +} + +bool CompilerGLSL::to_trivial_mix_op(const SPIRType &type, string &op, uint32_t left, uint32_t right, uint32_t lerp) +{ + auto *cleft = maybe_get(left); + auto *cright = maybe_get(right); + auto &lerptype = expression_type(lerp); + + // If our targets aren't constants, we cannot use construction. + if (!cleft || !cright) + return false; + + // If our targets are spec constants, we cannot use construction. + if (cleft->specialization || cright->specialization) + return false; + + // We can only use trivial construction if we have a scalar + // (should be possible to do it for vectors as well, but that is overkill for now). + if (lerptype.basetype != SPIRType::Boolean || lerptype.vecsize > 1) + return false; + + // If our bool selects between 0 and 1, we can cast from bool instead, making our trivial constructor. + bool ret = false; + switch (type.basetype) + { + case SPIRType::Short: + case SPIRType::UShort: + ret = cleft->scalar_u16() == 0 && cright->scalar_u16() == 1; + break; + + case SPIRType::Int: + case SPIRType::UInt: + ret = cleft->scalar() == 0 && cright->scalar() == 1; + break; + + case SPIRType::Half: + ret = cleft->scalar_f16() == 0.0f && cright->scalar_f16() == 1.0f; + break; + + case SPIRType::Float: + ret = cleft->scalar_f32() == 0.0f && cright->scalar_f32() == 1.0f; + break; + + case SPIRType::Double: + ret = cleft->scalar_f64() == 0.0 && cright->scalar_f64() == 1.0; + break; + + case SPIRType::Int64: + case SPIRType::UInt64: + ret = cleft->scalar_u64() == 0 && cright->scalar_u64() == 1; + break; + + default: + break; + } + + if (ret) + op = type_to_glsl_constructor(type); + return ret; +} + +string CompilerGLSL::to_ternary_expression(const SPIRType &restype, uint32_t select, uint32_t true_value, + uint32_t false_value) +{ + string expr; + auto &lerptype = expression_type(select); + + if (lerptype.vecsize == 1) + expr = join(to_enclosed_expression(select), " ? ", to_enclosed_pointer_expression(true_value), " : ", + to_enclosed_pointer_expression(false_value)); + else + { + auto swiz = [this](uint32_t expression, uint32_t i) { return to_extract_component_expression(expression, i); }; + + expr = type_to_glsl_constructor(restype); + expr += "("; + for (uint32_t i = 0; i < restype.vecsize; i++) + { + expr += swiz(select, i); + expr += " ? "; + expr += swiz(true_value, i); + expr += " : "; + expr += swiz(false_value, i); + if (i + 1 < restype.vecsize) + expr += ", "; + } + expr += ")"; + } + + return expr; +} + +void CompilerGLSL::emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp) +{ + auto &lerptype = expression_type(lerp); + auto &restype = get(result_type); + + // If this results in a variable pointer, assume it may be written through. + if (restype.pointer) + { + register_write(left); + register_write(right); + } + + string mix_op; + bool has_boolean_mix = *backend.boolean_mix_function && + ((options.es && options.version >= 310) || (!options.es && options.version >= 450)); + bool trivial_mix = to_trivial_mix_op(restype, mix_op, left, right, lerp); + + // Cannot use boolean mix when the lerp argument is just one boolean, + // fall back to regular trinary statements. + if (lerptype.vecsize == 1) + has_boolean_mix = false; + + // If we can reduce the mix to a simple cast, do so. + // This helps for cases like int(bool), uint(bool) which is implemented with + // OpSelect bool 1 0. + if (trivial_mix) + { + emit_unary_func_op(result_type, id, lerp, mix_op.c_str()); + } + else if (!has_boolean_mix && lerptype.basetype == SPIRType::Boolean) + { + // Boolean mix not supported on desktop without extension. + // Was added in OpenGL 4.5 with ES 3.1 compat. + // + // Could use GL_EXT_shader_integer_mix on desktop at least, + // but Apple doesn't support it. :( + // Just implement it as ternary expressions. + auto expr = to_ternary_expression(get(result_type), lerp, right, left); + emit_op(result_type, id, expr, should_forward(left) && should_forward(right) && should_forward(lerp)); + inherit_expression_dependencies(id, left); + inherit_expression_dependencies(id, right); + inherit_expression_dependencies(id, lerp); + } + else if (lerptype.basetype == SPIRType::Boolean) + emit_trinary_func_op(result_type, id, left, right, lerp, backend.boolean_mix_function); + else + emit_trinary_func_op(result_type, id, left, right, lerp, "mix"); +} + +string CompilerGLSL::to_combined_image_sampler(VariableID image_id, VariableID samp_id) +{ + // Keep track of the array indices we have used to load the image. + // We'll need to use the same array index into the combined image sampler array. + auto image_expr = to_expression(image_id); + string array_expr; + auto array_index = image_expr.find_first_of('['); + if (array_index != string::npos) + array_expr = image_expr.substr(array_index, string::npos); + + auto &args = current_function->arguments; + + // For GLSL and ESSL targets, we must enumerate all possible combinations for sampler2D(texture2D, sampler) and redirect + // all possible combinations into new sampler2D uniforms. + auto *image = maybe_get_backing_variable(image_id); + auto *samp = maybe_get_backing_variable(samp_id); + if (image) + image_id = image->self; + if (samp) + samp_id = samp->self; + + auto image_itr = find_if(begin(args), end(args), + [image_id](const SPIRFunction::Parameter ¶m) { return image_id == param.id; }); + + auto sampler_itr = find_if(begin(args), end(args), + [samp_id](const SPIRFunction::Parameter ¶m) { return samp_id == param.id; }); + + if (image_itr != end(args) || sampler_itr != end(args)) + { + // If any parameter originates from a parameter, we will find it in our argument list. + bool global_image = image_itr == end(args); + bool global_sampler = sampler_itr == end(args); + VariableID iid = global_image ? image_id : VariableID(uint32_t(image_itr - begin(args))); + VariableID sid = global_sampler ? samp_id : VariableID(uint32_t(sampler_itr - begin(args))); + + auto &combined = current_function->combined_parameters; + auto itr = find_if(begin(combined), end(combined), [=](const SPIRFunction::CombinedImageSamplerParameter &p) { + return p.global_image == global_image && p.global_sampler == global_sampler && p.image_id == iid && + p.sampler_id == sid; + }); + + if (itr != end(combined)) + return to_expression(itr->id) + array_expr; + else + { + SPIRV_CROSS_THROW("Cannot find mapping for combined sampler parameter, was " + "build_combined_image_samplers() used " + "before compile() was called?"); + } + } + else + { + // For global sampler2D, look directly at the global remapping table. + auto &mapping = combined_image_samplers; + auto itr = find_if(begin(mapping), end(mapping), [image_id, samp_id](const CombinedImageSampler &combined) { + return combined.image_id == image_id && combined.sampler_id == samp_id; + }); + + if (itr != end(combined_image_samplers)) + return to_expression(itr->combined_id) + array_expr; + else + { + SPIRV_CROSS_THROW("Cannot find mapping for combined sampler, was build_combined_image_samplers() used " + "before compile() was called?"); + } + } +} + +bool CompilerGLSL::is_supported_subgroup_op_in_opengl(spv::Op op) +{ + switch (op) + { + case OpGroupNonUniformElect: + case OpGroupNonUniformBallot: + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformBroadcastFirst: + case OpGroupNonUniformAll: + case OpGroupNonUniformAny: + case OpGroupNonUniformAllEqual: + case OpControlBarrier: + case OpMemoryBarrier: + case OpGroupNonUniformBallotBitCount: + case OpGroupNonUniformBallotBitExtract: + case OpGroupNonUniformInverseBallot: + return true; + default: + return false; + } +} + +void CompilerGLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) +{ + if (options.vulkan_semantics && combined_image_samplers.empty()) + { + emit_binary_func_op(result_type, result_id, image_id, samp_id, + type_to_glsl(get(result_type), result_id).c_str()); + } + else + { + // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types. + emit_op(result_type, result_id, to_combined_image_sampler(image_id, samp_id), true, true); + } + + // Make sure to suppress usage tracking and any expression invalidation. + // It is illegal to create temporaries of opaque types. + forwarded_temporaries.erase(result_id); +} + +static inline bool image_opcode_is_sample_no_dref(Op op) +{ + switch (op) + { + case OpImageSampleExplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageFetch: + case OpImageRead: + case OpImageSparseSampleExplicitLod: + case OpImageSparseSampleImplicitLod: + case OpImageSparseSampleProjExplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseFetch: + case OpImageSparseRead: + return true; + + default: + return false; + } +} + +void CompilerGLSL::emit_sparse_feedback_temporaries(uint32_t result_type_id, uint32_t id, uint32_t &feedback_id, + uint32_t &texel_id) +{ + // Need to allocate two temporaries. + if (options.es) + SPIRV_CROSS_THROW("Sparse texture feedback is not supported on ESSL."); + require_extension_internal("GL_ARB_sparse_texture2"); + + auto &temps = extra_sub_expressions[id]; + if (temps == 0) + temps = ir.increase_bound_by(2); + + feedback_id = temps + 0; + texel_id = temps + 1; + + auto &return_type = get(result_type_id); + if (return_type.basetype != SPIRType::Struct || return_type.member_types.size() != 2) + SPIRV_CROSS_THROW("Invalid return type for sparse feedback."); + emit_uninitialized_temporary(return_type.member_types[0], feedback_id); + emit_uninitialized_temporary(return_type.member_types[1], texel_id); +} + +uint32_t CompilerGLSL::get_sparse_feedback_texel_id(uint32_t id) const +{ + auto itr = extra_sub_expressions.find(id); + if (itr == extra_sub_expressions.end()) + return 0; + else + return itr->second + 1; +} + +void CompilerGLSL::emit_texture_op(const Instruction &i, bool sparse) +{ + auto *ops = stream(i); + auto op = static_cast(i.op); + + SmallVector inherited_expressions; + + uint32_t result_type_id = ops[0]; + uint32_t id = ops[1]; + auto &return_type = get(result_type_id); + + uint32_t sparse_code_id = 0; + uint32_t sparse_texel_id = 0; + if (sparse) + emit_sparse_feedback_temporaries(result_type_id, id, sparse_code_id, sparse_texel_id); + + bool forward = false; + string expr = to_texture_op(i, sparse, &forward, inherited_expressions); + + if (sparse) + { + statement(to_expression(sparse_code_id), " = ", expr, ";"); + expr = join(type_to_glsl(return_type), "(", to_expression(sparse_code_id), ", ", to_expression(sparse_texel_id), + ")"); + forward = true; + inherited_expressions.clear(); + } + + emit_op(result_type_id, id, expr, forward); + for (auto &inherit : inherited_expressions) + inherit_expression_dependencies(id, inherit); + + // Do not register sparse ops as control dependent as they are always lowered to a temporary. + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjDrefImplicitLod: + register_control_dependent_expression(id); + break; + + default: + break; + } +} + +std::string CompilerGLSL::to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions) +{ + auto *ops = stream(i); + auto op = static_cast(i.op); + uint32_t length = i.length; + + uint32_t result_type_id = ops[0]; + VariableID img = ops[2]; + uint32_t coord = ops[3]; + uint32_t dref = 0; + uint32_t comp = 0; + bool gather = false; + bool proj = false; + bool fetch = false; + const uint32_t *opt = nullptr; + + auto &result_type = get(result_type_id); + + inherited_expressions.push_back(coord); + + // Make sure non-uniform decoration is back-propagated to where it needs to be. + if (has_decoration(img, DecorationNonUniformEXT)) + propagate_nonuniform_qualifier(img); + + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleDrefExplicitLod: + dref = ops[4]; + opt = &ops[5]; + length -= 5; + break; + + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + dref = ops[4]; + opt = &ops[5]; + length -= 5; + proj = true; + break; + + case OpImageDrefGather: + case OpImageSparseDrefGather: + dref = ops[4]; + opt = &ops[5]; + length -= 5; + gather = true; + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("textureGather requires ESSL 310."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("textureGather with depth compare requires GLSL 400."); + break; + + case OpImageGather: + case OpImageSparseGather: + comp = ops[4]; + opt = &ops[5]; + length -= 5; + gather = true; + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("textureGather requires ESSL 310."); + else if (!options.es && options.version < 400) + { + if (!expression_is_constant_null(comp)) + SPIRV_CROSS_THROW("textureGather with component requires GLSL 400."); + require_extension_internal("GL_ARB_texture_gather"); + } + break; + + case OpImageFetch: + case OpImageSparseFetch: + case OpImageRead: // Reads == fetches in Metal (other langs will not get here) + opt = &ops[4]; + length -= 4; + fetch = true; + break; + + case OpImageSampleProjImplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseSampleProjExplicitLod: + opt = &ops[4]; + length -= 4; + proj = true; + break; + + default: + opt = &ops[4]; + length -= 4; + break; + } + + // Bypass pointers because we need the real image struct + auto &type = expression_type(img); + auto &imgtype = get(type.self); + + uint32_t coord_components = 0; + switch (imgtype.image.dim) + { + case spv::Dim1D: + coord_components = 1; + break; + case spv::Dim2D: + coord_components = 2; + break; + case spv::Dim3D: + coord_components = 3; + break; + case spv::DimCube: + coord_components = 3; + break; + case spv::DimBuffer: + coord_components = 1; + break; + default: + coord_components = 2; + break; + } + + if (dref) + inherited_expressions.push_back(dref); + + if (proj) + coord_components++; + if (imgtype.image.arrayed) + coord_components++; + + uint32_t bias = 0; + uint32_t lod = 0; + uint32_t grad_x = 0; + uint32_t grad_y = 0; + uint32_t coffset = 0; + uint32_t offset = 0; + uint32_t coffsets = 0; + uint32_t sample = 0; + uint32_t minlod = 0; + uint32_t flags = 0; + + if (length) + { + flags = *opt++; + length--; + } + + auto test = [&](uint32_t &v, uint32_t flag) { + if (length && (flags & flag)) + { + v = *opt++; + inherited_expressions.push_back(v); + length--; + } + }; + + test(bias, ImageOperandsBiasMask); + test(lod, ImageOperandsLodMask); + test(grad_x, ImageOperandsGradMask); + test(grad_y, ImageOperandsGradMask); + test(coffset, ImageOperandsConstOffsetMask); + test(offset, ImageOperandsOffsetMask); + test(coffsets, ImageOperandsConstOffsetsMask); + test(sample, ImageOperandsSampleMask); + test(minlod, ImageOperandsMinLodMask); + + TextureFunctionBaseArguments base_args = {}; + base_args.img = img; + base_args.imgtype = &imgtype; + base_args.is_fetch = fetch != 0; + base_args.is_gather = gather != 0; + base_args.is_proj = proj != 0; + + string expr; + TextureFunctionNameArguments name_args = {}; + + name_args.base = base_args; + name_args.has_array_offsets = coffsets != 0; + name_args.has_offset = coffset != 0 || offset != 0; + name_args.has_grad = grad_x != 0 || grad_y != 0; + name_args.has_dref = dref != 0; + name_args.is_sparse_feedback = sparse; + name_args.has_min_lod = minlod != 0; + name_args.lod = lod; + expr += to_function_name(name_args); + expr += "("; + + uint32_t sparse_texel_id = 0; + if (sparse) + sparse_texel_id = get_sparse_feedback_texel_id(ops[1]); + + TextureFunctionArguments args = {}; + args.base = base_args; + args.coord = coord; + args.coord_components = coord_components; + args.dref = dref; + args.grad_x = grad_x; + args.grad_y = grad_y; + args.lod = lod; + args.coffset = coffset; + args.offset = offset; + args.bias = bias; + args.component = comp; + args.sample = sample; + args.sparse_texel = sparse_texel_id; + args.min_lod = minlod; + expr += to_function_args(args, forward); + expr += ")"; + + // texture(samplerXShadow) returns float. shadowX() returns vec4. Swizzle here. + if (is_legacy() && image_is_comparison(imgtype, img)) + expr += ".r"; + + // Sampling from a texture which was deduced to be a depth image, might actually return 1 component here. + // Remap back to 4 components as sampling opcodes expect. + if (backend.comparison_image_samples_scalar && image_opcode_is_sample_no_dref(op)) + { + bool image_is_depth = false; + const auto *combined = maybe_get(img); + VariableID image_id = combined ? combined->image : img; + + if (combined && image_is_comparison(imgtype, combined->image)) + image_is_depth = true; + else if (image_is_comparison(imgtype, img)) + image_is_depth = true; + + // We must also check the backing variable for the image. + // We might have loaded an OpImage, and used that handle for two different purposes. + // Once with comparison, once without. + auto *image_variable = maybe_get_backing_variable(image_id); + if (image_variable && image_is_comparison(get(image_variable->basetype), image_variable->self)) + image_is_depth = true; + + if (image_is_depth) + expr = remap_swizzle(result_type, 1, expr); + } + + if (!sparse && !backend.support_small_type_sampling_result && result_type.width < 32) + { + // Just value cast (narrowing) to expected type since we cannot rely on narrowing to work automatically. + // Hopefully compiler picks this up and converts the texturing instruction to the appropriate precision. + expr = join(type_to_glsl_constructor(result_type), "(", expr, ")"); + } + + // Deals with reads from MSL. We might need to downconvert to fewer components. + if (op == OpImageRead) + expr = remap_swizzle(result_type, 4, expr); + + return expr; +} + +bool CompilerGLSL::expression_is_constant_null(uint32_t id) const +{ + auto *c = maybe_get(id); + if (!c) + return false; + return c->constant_is_null(); +} + +bool CompilerGLSL::expression_is_non_value_type_array(uint32_t ptr) +{ + auto &type = expression_type(ptr); + if (type.array.empty()) + return false; + + if (!backend.array_is_value_type) + return true; + + auto *var = maybe_get_backing_variable(ptr); + if (!var) + return false; + + auto &backed_type = get(var->basetype); + return !backend.buffer_offset_array_is_value_type && backed_type.basetype == SPIRType::Struct && + has_member_decoration(backed_type.self, 0, DecorationOffset); +} + +// Returns the function name for a texture sampling function for the specified image and sampling characteristics. +// For some subclasses, the function is a method on the specified image. +string CompilerGLSL::to_function_name(const TextureFunctionNameArguments &args) +{ + if (args.has_min_lod) + { + if (options.es) + SPIRV_CROSS_THROW("Sparse residency is not supported in ESSL."); + require_extension_internal("GL_ARB_sparse_texture_clamp"); + } + + string fname; + auto &imgtype = *args.base.imgtype; + VariableID tex = args.base.img; + + // textureLod on sampler2DArrayShadow and samplerCubeShadow does not exist in GLSL for some reason. + // To emulate this, we will have to use textureGrad with a constant gradient of 0. + // The workaround will assert that the LOD is in fact constant 0, or we cannot emit correct code. + // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube. + bool workaround_lod_array_shadow_as_grad = false; + if (((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) && + image_is_comparison(imgtype, tex) && args.lod) + { + if (!expression_is_constant_null(args.lod)) + { + SPIRV_CROSS_THROW("textureLod on sampler2DArrayShadow is not constant 0.0. This cannot be " + "expressed in GLSL."); + } + workaround_lod_array_shadow_as_grad = true; + } + + if (args.is_sparse_feedback) + fname += "sparse"; + + if (args.base.is_fetch) + fname += args.is_sparse_feedback ? "TexelFetch" : "texelFetch"; + else + { + fname += args.is_sparse_feedback ? "Texture" : "texture"; + + if (args.base.is_gather) + fname += "Gather"; + if (args.has_array_offsets) + fname += "Offsets"; + if (args.base.is_proj) + fname += "Proj"; + if (args.has_grad || workaround_lod_array_shadow_as_grad) + fname += "Grad"; + if (args.lod != 0 && !workaround_lod_array_shadow_as_grad) + fname += "Lod"; + } + + if (args.has_offset) + fname += "Offset"; + + if (args.has_min_lod) + fname += "Clamp"; + + if (args.is_sparse_feedback || args.has_min_lod) + fname += "ARB"; + + return (is_legacy() && !args.base.is_gather) ? legacy_tex_op(fname, imgtype, tex) : fname; +} + +std::string CompilerGLSL::convert_separate_image_to_expression(uint32_t id) +{ + auto *var = maybe_get_backing_variable(id); + + // If we are fetching from a plain OpTypeImage, we must combine with a dummy sampler in GLSL. + // In Vulkan GLSL, we can make use of the newer GL_EXT_samplerless_texture_functions. + if (var) + { + auto &type = get(var->basetype); + if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer) + { + if (options.vulkan_semantics) + { + if (dummy_sampler_id) + { + // Don't need to consider Shadow state since the dummy sampler is always non-shadow. + auto sampled_type = type; + sampled_type.basetype = SPIRType::SampledImage; + return join(type_to_glsl(sampled_type), "(", to_expression(id), ", ", + to_expression(dummy_sampler_id), ")"); + } + else + { + // Newer glslang supports this extension to deal with texture2D as argument to texture functions. + require_extension_internal("GL_EXT_samplerless_texture_functions"); + } + } + else + { + if (!dummy_sampler_id) + SPIRV_CROSS_THROW("Cannot find dummy sampler ID. Was " + "build_dummy_sampler_for_combined_images() called?"); + + return to_combined_image_sampler(id, dummy_sampler_id); + } + } + } + + return to_expression(id); +} + +// Returns the function args for a texture sampling function for the specified image and sampling characteristics. +string CompilerGLSL::to_function_args(const TextureFunctionArguments &args, bool *p_forward) +{ + VariableID img = args.base.img; + auto &imgtype = *args.base.imgtype; + + string farg_str; + if (args.base.is_fetch) + farg_str = convert_separate_image_to_expression(img); + else + farg_str = to_expression(img); + + bool swizz_func = backend.swizzle_is_function; + auto swizzle = [swizz_func](uint32_t comps, uint32_t in_comps) -> const char * { + if (comps == in_comps) + return ""; + + switch (comps) + { + case 1: + return ".x"; + case 2: + return swizz_func ? ".xy()" : ".xy"; + case 3: + return swizz_func ? ".xyz()" : ".xyz"; + default: + return ""; + } + }; + + bool forward = should_forward(args.coord); + + // The IR can give us more components than we need, so chop them off as needed. + auto swizzle_expr = swizzle(args.coord_components, expression_type(args.coord).vecsize); + // Only enclose the UV expression if needed. + auto coord_expr = + (*swizzle_expr == '\0') ? to_expression(args.coord) : (to_enclosed_expression(args.coord) + swizzle_expr); + + // texelFetch only takes int, not uint. + auto &coord_type = expression_type(args.coord); + if (coord_type.basetype == SPIRType::UInt) + { + auto expected_type = coord_type; + expected_type.vecsize = args.coord_components; + expected_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(expected_type, coord_type.basetype, coord_expr); + } + + // textureLod on sampler2DArrayShadow and samplerCubeShadow does not exist in GLSL for some reason. + // To emulate this, we will have to use textureGrad with a constant gradient of 0. + // The workaround will assert that the LOD is in fact constant 0, or we cannot emit correct code. + // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube. + bool workaround_lod_array_shadow_as_grad = + ((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) && + image_is_comparison(imgtype, img) && args.lod != 0; + + if (args.dref) + { + forward = forward && should_forward(args.dref); + + // SPIR-V splits dref and coordinate. + if (args.base.is_gather || + args.coord_components == 4) // GLSL also splits the arguments in two. Same for textureGather. + { + farg_str += ", "; + farg_str += to_expression(args.coord); + farg_str += ", "; + farg_str += to_expression(args.dref); + } + else if (args.base.is_proj) + { + // Have to reshuffle so we get vec4(coord, dref, proj), special case. + // Other shading languages splits up the arguments for coord and compare value like SPIR-V. + // The coordinate type for textureProj shadow is always vec4 even for sampler1DShadow. + farg_str += ", vec4("; + + if (imgtype.image.dim == Dim1D) + { + // Could reuse coord_expr, but we will mess up the temporary usage checking. + farg_str += to_enclosed_expression(args.coord) + ".x"; + farg_str += ", "; + farg_str += "0.0, "; + farg_str += to_expression(args.dref); + farg_str += ", "; + farg_str += to_enclosed_expression(args.coord) + ".y)"; + } + else if (imgtype.image.dim == Dim2D) + { + // Could reuse coord_expr, but we will mess up the temporary usage checking. + farg_str += to_enclosed_expression(args.coord) + (swizz_func ? ".xy()" : ".xy"); + farg_str += ", "; + farg_str += to_expression(args.dref); + farg_str += ", "; + farg_str += to_enclosed_expression(args.coord) + ".z)"; + } + else + SPIRV_CROSS_THROW("Invalid type for textureProj with shadow."); + } + else + { + // Create a composite which merges coord/dref into a single vector. + auto type = expression_type(args.coord); + type.vecsize = args.coord_components + 1; + farg_str += ", "; + farg_str += type_to_glsl_constructor(type); + farg_str += "("; + farg_str += coord_expr; + farg_str += ", "; + farg_str += to_expression(args.dref); + farg_str += ")"; + } + } + else + { + farg_str += ", "; + farg_str += coord_expr; + } + + if (args.grad_x || args.grad_y) + { + forward = forward && should_forward(args.grad_x); + forward = forward && should_forward(args.grad_y); + farg_str += ", "; + farg_str += to_expression(args.grad_x); + farg_str += ", "; + farg_str += to_expression(args.grad_y); + } + + if (args.lod) + { + if (workaround_lod_array_shadow_as_grad) + { + // Implement textureGrad() instead. LOD == 0.0 is implemented as gradient of 0.0. + // Implementing this as plain texture() is not safe on some implementations. + if (imgtype.image.dim == Dim2D) + farg_str += ", vec2(0.0), vec2(0.0)"; + else if (imgtype.image.dim == DimCube) + farg_str += ", vec3(0.0), vec3(0.0)"; + } + else + { + forward = forward && should_forward(args.lod); + farg_str += ", "; + + auto &lod_expr_type = expression_type(args.lod); + + // Lod expression for TexelFetch in GLSL must be int, and only int. + if (args.base.is_fetch && imgtype.image.dim != DimBuffer && !imgtype.image.ms && + lod_expr_type.basetype != SPIRType::Int) + { + farg_str += join("int(", to_expression(args.lod), ")"); + } + else + { + farg_str += to_expression(args.lod); + } + } + } + else if (args.base.is_fetch && imgtype.image.dim != DimBuffer && !imgtype.image.ms) + { + // Lod argument is optional in OpImageFetch, but we require a LOD value, pick 0 as the default. + farg_str += ", 0"; + } + + if (args.coffset) + { + forward = forward && should_forward(args.coffset); + farg_str += ", "; + farg_str += to_expression(args.coffset); + } + else if (args.offset) + { + forward = forward && should_forward(args.offset); + farg_str += ", "; + farg_str += to_expression(args.offset); + } + + if (args.sample) + { + farg_str += ", "; + farg_str += to_expression(args.sample); + } + + if (args.min_lod) + { + farg_str += ", "; + farg_str += to_expression(args.min_lod); + } + + if (args.sparse_texel) + { + // Sparse texel output parameter comes after everything else, except it's before the optional, component/bias arguments. + farg_str += ", "; + farg_str += to_expression(args.sparse_texel); + } + + if (args.bias) + { + forward = forward && should_forward(args.bias); + farg_str += ", "; + farg_str += to_expression(args.bias); + } + + if (args.component && !expression_is_constant_null(args.component)) + { + forward = forward && should_forward(args.component); + farg_str += ", "; + farg_str += to_expression(args.component); + } + + *p_forward = forward; + + return farg_str; +} + +void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t length) +{ + auto op = static_cast(eop); + + if (is_legacy() && is_unsigned_glsl_opcode(op)) + SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy GLSL targets."); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, length); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + switch (op) + { + // FP fiddling + case GLSLstd450Round: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "round"); + else + { + auto op0 = to_enclosed_expression(args[0]); + auto &op0_type = expression_type(args[0]); + auto expr = join("floor(", op0, " + ", type_to_glsl_constructor(op0_type), "(0.5))"); + bool forward = should_forward(args[0]); + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, args[0]); + } + break; + + case GLSLstd450RoundEven: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "roundEven"); + else if (!options.es) + { + // This extension provides round() with round-to-even semantics. + require_extension_internal("GL_EXT_gpu_shader4"); + emit_unary_func_op(result_type, id, args[0], "round"); + } + else + SPIRV_CROSS_THROW("roundEven supported only in ESSL 300."); + break; + + case GLSLstd450Trunc: + emit_unary_func_op(result_type, id, args[0], "trunc"); + break; + case GLSLstd450SAbs: + emit_unary_func_op_cast(result_type, id, args[0], "abs", int_type, int_type); + break; + case GLSLstd450FAbs: + emit_unary_func_op(result_type, id, args[0], "abs"); + break; + case GLSLstd450SSign: + emit_unary_func_op_cast(result_type, id, args[0], "sign", int_type, int_type); + break; + case GLSLstd450FSign: + emit_unary_func_op(result_type, id, args[0], "sign"); + break; + case GLSLstd450Floor: + emit_unary_func_op(result_type, id, args[0], "floor"); + break; + case GLSLstd450Ceil: + emit_unary_func_op(result_type, id, args[0], "ceil"); + break; + case GLSLstd450Fract: + emit_unary_func_op(result_type, id, args[0], "fract"); + break; + case GLSLstd450Radians: + emit_unary_func_op(result_type, id, args[0], "radians"); + break; + case GLSLstd450Degrees: + emit_unary_func_op(result_type, id, args[0], "degrees"); + break; + case GLSLstd450Fma: + if ((!options.es && options.version < 400) || (options.es && options.version < 320)) + { + auto expr = join(to_enclosed_expression(args[0]), " * ", to_enclosed_expression(args[1]), " + ", + to_enclosed_expression(args[2])); + + emit_op(result_type, id, expr, + should_forward(args[0]) && should_forward(args[1]) && should_forward(args[2])); + for (uint32_t i = 0; i < 3; i++) + inherit_expression_dependencies(id, args[i]); + } + else + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fma"); + break; + case GLSLstd450Modf: + register_call_out_argument(args[1]); + forced_temporaries.insert(id); + emit_binary_func_op(result_type, id, args[0], args[1], "modf"); + break; + + case GLSLstd450ModfStruct: + { + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, id); + statement(to_expression(id), ".", to_member_name(type, 0), " = ", "modf(", to_expression(args[0]), ", ", + to_expression(id), ".", to_member_name(type, 1), ");"); + break; + } + + // Minmax + case GLSLstd450UMin: + emit_binary_func_op_cast(result_type, id, args[0], args[1], "min", uint_type, false); + break; + + case GLSLstd450SMin: + emit_binary_func_op_cast(result_type, id, args[0], args[1], "min", int_type, false); + break; + + case GLSLstd450FMin: + emit_binary_func_op(result_type, id, args[0], args[1], "min"); + break; + + case GLSLstd450FMax: + emit_binary_func_op(result_type, id, args[0], args[1], "max"); + break; + + case GLSLstd450UMax: + emit_binary_func_op_cast(result_type, id, args[0], args[1], "max", uint_type, false); + break; + + case GLSLstd450SMax: + emit_binary_func_op_cast(result_type, id, args[0], args[1], "max", int_type, false); + break; + + case GLSLstd450FClamp: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp"); + break; + + case GLSLstd450UClamp: + emit_trinary_func_op_cast(result_type, id, args[0], args[1], args[2], "clamp", uint_type); + break; + + case GLSLstd450SClamp: + emit_trinary_func_op_cast(result_type, id, args[0], args[1], args[2], "clamp", int_type); + break; + + // Trig + case GLSLstd450Sin: + emit_unary_func_op(result_type, id, args[0], "sin"); + break; + case GLSLstd450Cos: + emit_unary_func_op(result_type, id, args[0], "cos"); + break; + case GLSLstd450Tan: + emit_unary_func_op(result_type, id, args[0], "tan"); + break; + case GLSLstd450Asin: + emit_unary_func_op(result_type, id, args[0], "asin"); + break; + case GLSLstd450Acos: + emit_unary_func_op(result_type, id, args[0], "acos"); + break; + case GLSLstd450Atan: + emit_unary_func_op(result_type, id, args[0], "atan"); + break; + case GLSLstd450Sinh: + emit_unary_func_op(result_type, id, args[0], "sinh"); + break; + case GLSLstd450Cosh: + emit_unary_func_op(result_type, id, args[0], "cosh"); + break; + case GLSLstd450Tanh: + emit_unary_func_op(result_type, id, args[0], "tanh"); + break; + case GLSLstd450Asinh: + emit_unary_func_op(result_type, id, args[0], "asinh"); + break; + case GLSLstd450Acosh: + emit_unary_func_op(result_type, id, args[0], "acosh"); + break; + case GLSLstd450Atanh: + emit_unary_func_op(result_type, id, args[0], "atanh"); + break; + case GLSLstd450Atan2: + emit_binary_func_op(result_type, id, args[0], args[1], "atan"); + break; + + // Exponentials + case GLSLstd450Pow: + emit_binary_func_op(result_type, id, args[0], args[1], "pow"); + break; + case GLSLstd450Exp: + emit_unary_func_op(result_type, id, args[0], "exp"); + break; + case GLSLstd450Log: + emit_unary_func_op(result_type, id, args[0], "log"); + break; + case GLSLstd450Exp2: + emit_unary_func_op(result_type, id, args[0], "exp2"); + break; + case GLSLstd450Log2: + emit_unary_func_op(result_type, id, args[0], "log2"); + break; + case GLSLstd450Sqrt: + emit_unary_func_op(result_type, id, args[0], "sqrt"); + break; + case GLSLstd450InverseSqrt: + emit_unary_func_op(result_type, id, args[0], "inversesqrt"); + break; + + // Matrix math + case GLSLstd450Determinant: + emit_unary_func_op(result_type, id, args[0], "determinant"); + break; + case GLSLstd450MatrixInverse: + emit_unary_func_op(result_type, id, args[0], "inverse"); + break; + + // Lerping + case GLSLstd450FMix: + case GLSLstd450IMix: + { + emit_mix_op(result_type, id, args[0], args[1], args[2]); + break; + } + case GLSLstd450Step: + emit_binary_func_op(result_type, id, args[0], args[1], "step"); + break; + case GLSLstd450SmoothStep: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "smoothstep"); + break; + + // Packing + case GLSLstd450Frexp: + register_call_out_argument(args[1]); + forced_temporaries.insert(id); + emit_binary_func_op(result_type, id, args[0], args[1], "frexp"); + break; + + case GLSLstd450FrexpStruct: + { + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, id); + statement(to_expression(id), ".", to_member_name(type, 0), " = ", "frexp(", to_expression(args[0]), ", ", + to_expression(id), ".", to_member_name(type, 1), ");"); + break; + } + + case GLSLstd450Ldexp: + { + bool forward = should_forward(args[0]) && should_forward(args[1]); + + auto op0 = to_unpacked_expression(args[0]); + auto op1 = to_unpacked_expression(args[1]); + auto &op1_type = expression_type(args[1]); + if (op1_type.basetype != SPIRType::Int) + { + // Need a value cast here. + auto target_type = op1_type; + target_type.basetype = SPIRType::Int; + op1 = join(type_to_glsl_constructor(target_type), "(", op1, ")"); + } + + auto expr = join("ldexp(", op0, ", ", op1, ")"); + + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, args[0]); + inherit_expression_dependencies(id, args[1]); + break; + } + + case GLSLstd450PackSnorm4x8: + emit_unary_func_op(result_type, id, args[0], "packSnorm4x8"); + break; + case GLSLstd450PackUnorm4x8: + emit_unary_func_op(result_type, id, args[0], "packUnorm4x8"); + break; + case GLSLstd450PackSnorm2x16: + emit_unary_func_op(result_type, id, args[0], "packSnorm2x16"); + break; + case GLSLstd450PackUnorm2x16: + emit_unary_func_op(result_type, id, args[0], "packUnorm2x16"); + break; + case GLSLstd450PackHalf2x16: + emit_unary_func_op(result_type, id, args[0], "packHalf2x16"); + break; + case GLSLstd450UnpackSnorm4x8: + emit_unary_func_op(result_type, id, args[0], "unpackSnorm4x8"); + break; + case GLSLstd450UnpackUnorm4x8: + emit_unary_func_op(result_type, id, args[0], "unpackUnorm4x8"); + break; + case GLSLstd450UnpackSnorm2x16: + emit_unary_func_op(result_type, id, args[0], "unpackSnorm2x16"); + break; + case GLSLstd450UnpackUnorm2x16: + emit_unary_func_op(result_type, id, args[0], "unpackUnorm2x16"); + break; + case GLSLstd450UnpackHalf2x16: + emit_unary_func_op(result_type, id, args[0], "unpackHalf2x16"); + break; + + case GLSLstd450PackDouble2x32: + emit_unary_func_op(result_type, id, args[0], "packDouble2x32"); + break; + case GLSLstd450UnpackDouble2x32: + emit_unary_func_op(result_type, id, args[0], "unpackDouble2x32"); + break; + + // Vector math + case GLSLstd450Length: + emit_unary_func_op(result_type, id, args[0], "length"); + break; + case GLSLstd450Distance: + emit_binary_func_op(result_type, id, args[0], args[1], "distance"); + break; + case GLSLstd450Cross: + emit_binary_func_op(result_type, id, args[0], args[1], "cross"); + break; + case GLSLstd450Normalize: + emit_unary_func_op(result_type, id, args[0], "normalize"); + break; + case GLSLstd450FaceForward: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "faceforward"); + break; + case GLSLstd450Reflect: + emit_binary_func_op(result_type, id, args[0], args[1], "reflect"); + break; + case GLSLstd450Refract: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "refract"); + break; + + // Bit-fiddling + case GLSLstd450FindILsb: + // findLSB always returns int. + emit_unary_func_op_cast(result_type, id, args[0], "findLSB", expression_type(args[0]).basetype, int_type); + break; + + case GLSLstd450FindSMsb: + emit_unary_func_op_cast(result_type, id, args[0], "findMSB", int_type, int_type); + break; + + case GLSLstd450FindUMsb: + emit_unary_func_op_cast(result_type, id, args[0], "findMSB", uint_type, + int_type); // findMSB always returns int. + break; + + // Multisampled varying + case GLSLstd450InterpolateAtCentroid: + emit_unary_func_op(result_type, id, args[0], "interpolateAtCentroid"); + break; + case GLSLstd450InterpolateAtSample: + emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtSample"); + break; + case GLSLstd450InterpolateAtOffset: + emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtOffset"); + break; + + case GLSLstd450NMin: + case GLSLstd450NMax: + { + emit_nminmax_op(result_type, id, args[0], args[1], op); + break; + } + + case GLSLstd450NClamp: + { + // Make sure we have a unique ID here to avoid aliasing the extra sub-expressions between clamp and NMin sub-op. + // IDs cannot exceed 24 bits, so we can make use of the higher bits for some unique flags. + uint32_t &max_id = extra_sub_expressions[id | 0x80000000u]; + if (!max_id) + max_id = ir.increase_bound_by(1); + + // Inherit precision qualifiers. + ir.meta[max_id] = ir.meta[id]; + + emit_nminmax_op(result_type, max_id, args[0], args[1], GLSLstd450NMax); + emit_nminmax_op(result_type, id, max_id, args[2], GLSLstd450NMin); + break; + } + + default: + statement("// unimplemented GLSL op ", eop); + break; + } +} + +void CompilerGLSL::emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op) +{ + // Need to emulate this call. + uint32_t &ids = extra_sub_expressions[id]; + if (!ids) + { + ids = ir.increase_bound_by(5); + auto btype = get(result_type); + btype.basetype = SPIRType::Boolean; + set(ids, btype); + } + + uint32_t btype_id = ids + 0; + uint32_t left_nan_id = ids + 1; + uint32_t right_nan_id = ids + 2; + uint32_t tmp_id = ids + 3; + uint32_t mixed_first_id = ids + 4; + + // Inherit precision qualifiers. + ir.meta[tmp_id] = ir.meta[id]; + ir.meta[mixed_first_id] = ir.meta[id]; + + emit_unary_func_op(btype_id, left_nan_id, op0, "isnan"); + emit_unary_func_op(btype_id, right_nan_id, op1, "isnan"); + emit_binary_func_op(result_type, tmp_id, op0, op1, op == GLSLstd450NMin ? "min" : "max"); + emit_mix_op(result_type, mixed_first_id, tmp_id, op1, left_nan_id); + emit_mix_op(result_type, id, mixed_first_id, op0, right_nan_id); +} + +void CompilerGLSL::emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, + uint32_t) +{ + require_extension_internal("GL_AMD_shader_ballot"); + + enum AMDShaderBallot + { + SwizzleInvocationsAMD = 1, + SwizzleInvocationsMaskedAMD = 2, + WriteInvocationAMD = 3, + MbcntAMD = 4 + }; + + auto op = static_cast(eop); + + switch (op) + { + case SwizzleInvocationsAMD: + emit_binary_func_op(result_type, id, args[0], args[1], "swizzleInvocationsAMD"); + register_control_dependent_expression(id); + break; + + case SwizzleInvocationsMaskedAMD: + emit_binary_func_op(result_type, id, args[0], args[1], "swizzleInvocationsMaskedAMD"); + register_control_dependent_expression(id); + break; + + case WriteInvocationAMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "writeInvocationAMD"); + register_control_dependent_expression(id); + break; + + case MbcntAMD: + emit_unary_func_op(result_type, id, args[0], "mbcntAMD"); + register_control_dependent_expression(id); + break; + + default: + statement("// unimplemented SPV AMD shader ballot op ", eop); + break; + } +} + +void CompilerGLSL::emit_spv_amd_shader_explicit_vertex_parameter_op(uint32_t result_type, uint32_t id, uint32_t eop, + const uint32_t *args, uint32_t) +{ + require_extension_internal("GL_AMD_shader_explicit_vertex_parameter"); + + enum AMDShaderExplicitVertexParameter + { + InterpolateAtVertexAMD = 1 + }; + + auto op = static_cast(eop); + + switch (op) + { + case InterpolateAtVertexAMD: + emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtVertexAMD"); + break; + + default: + statement("// unimplemented SPV AMD shader explicit vertex parameter op ", eop); + break; + } +} + +void CompilerGLSL::emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t id, uint32_t eop, + const uint32_t *args, uint32_t) +{ + require_extension_internal("GL_AMD_shader_trinary_minmax"); + + enum AMDShaderTrinaryMinMax + { + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9 + }; + + auto op = static_cast(eop); + + switch (op) + { + case FMin3AMD: + case UMin3AMD: + case SMin3AMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "min3"); + break; + + case FMax3AMD: + case UMax3AMD: + case SMax3AMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "max3"); + break; + + case FMid3AMD: + case UMid3AMD: + case SMid3AMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "mid3"); + break; + + default: + statement("// unimplemented SPV AMD shader trinary minmax op ", eop); + break; + } +} + +void CompilerGLSL::emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, + uint32_t) +{ + require_extension_internal("GL_AMD_gcn_shader"); + + enum AMDGCNShader + { + CubeFaceIndexAMD = 1, + CubeFaceCoordAMD = 2, + TimeAMD = 3 + }; + + auto op = static_cast(eop); + + switch (op) + { + case CubeFaceIndexAMD: + emit_unary_func_op(result_type, id, args[0], "cubeFaceIndexAMD"); + break; + case CubeFaceCoordAMD: + emit_unary_func_op(result_type, id, args[0], "cubeFaceCoordAMD"); + break; + case TimeAMD: + { + string expr = "timeAMD()"; + emit_op(result_type, id, expr, true); + register_control_dependent_expression(id); + break; + } + + default: + statement("// unimplemented SPV AMD gcn shader op ", eop); + break; + } +} + +void CompilerGLSL::emit_subgroup_op(const Instruction &i) +{ + const uint32_t *ops = stream(i); + auto op = static_cast(i.op); + + if (!options.vulkan_semantics && !is_supported_subgroup_op_in_opengl(op)) + SPIRV_CROSS_THROW("This subgroup operation is only supported in Vulkan semantics."); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + switch (op) + { + case OpGroupNonUniformElect: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupElect); + break; + + case OpGroupNonUniformBallotBitCount: + { + const GroupOperation operation = static_cast(ops[3]); + if (operation == GroupOperationReduce) + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBallotBitCount); + else if (operation == GroupOperationInclusiveScan || operation == GroupOperationExclusiveScan) + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupInverseBallot_InclBitCount_ExclBitCout); + } + break; + + case OpGroupNonUniformBallotBitExtract: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBallotBitExtract); + break; + + case OpGroupNonUniformInverseBallot: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupInverseBallot_InclBitCount_ExclBitCout); + break; + + case OpGroupNonUniformBallot: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBallot); + break; + + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBallotFindLSB_MSB); + break; + + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformBroadcastFirst: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBrodcast_First); + break; + + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + require_extension_internal("GL_KHR_shader_subgroup_shuffle"); + break; + + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + require_extension_internal("GL_KHR_shader_subgroup_shuffle_relative"); + break; + + case OpGroupNonUniformAll: + case OpGroupNonUniformAny: + case OpGroupNonUniformAllEqual: + { + const SPIRType &type = expression_type(ops[3]); + if (type.basetype == SPIRType::BaseType::Boolean && type.vecsize == 1u) + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupAll_Any_AllEqualBool); + else + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupAllEqualT); + } + break; + + case OpGroupNonUniformFAdd: + case OpGroupNonUniformFMul: + case OpGroupNonUniformFMin: + case OpGroupNonUniformFMax: + case OpGroupNonUniformIAdd: + case OpGroupNonUniformIMul: + case OpGroupNonUniformSMin: + case OpGroupNonUniformSMax: + case OpGroupNonUniformUMin: + case OpGroupNonUniformUMax: + case OpGroupNonUniformBitwiseAnd: + case OpGroupNonUniformBitwiseOr: + case OpGroupNonUniformBitwiseXor: + { + auto operation = static_cast(ops[3]); + if (operation == GroupOperationClusteredReduce) + { + require_extension_internal("GL_KHR_shader_subgroup_clustered"); + } + else if (operation == GroupOperationExclusiveScan || operation == GroupOperationInclusiveScan || + operation == GroupOperationReduce) + { + require_extension_internal("GL_KHR_shader_subgroup_arithmetic"); + } + else + SPIRV_CROSS_THROW("Invalid group operation."); + break; + } + + case OpGroupNonUniformQuadSwap: + case OpGroupNonUniformQuadBroadcast: + require_extension_internal("GL_KHR_shader_subgroup_quad"); + break; + + default: + SPIRV_CROSS_THROW("Invalid opcode for subgroup."); + } + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto scope = static_cast(evaluate_constant_u32(ops[2])); + if (scope != ScopeSubgroup) + SPIRV_CROSS_THROW("Only subgroup scope is supported."); + + switch (op) + { + case OpGroupNonUniformElect: + emit_op(result_type, id, "subgroupElect()", true); + break; + + case OpGroupNonUniformBroadcast: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupBroadcast"); + break; + + case OpGroupNonUniformBroadcastFirst: + emit_unary_func_op(result_type, id, ops[3], "subgroupBroadcastFirst"); + break; + + case OpGroupNonUniformBallot: + emit_unary_func_op(result_type, id, ops[3], "subgroupBallot"); + break; + + case OpGroupNonUniformInverseBallot: + emit_unary_func_op(result_type, id, ops[3], "subgroupInverseBallot"); + break; + + case OpGroupNonUniformBallotBitExtract: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupBallotBitExtract"); + break; + + case OpGroupNonUniformBallotFindLSB: + emit_unary_func_op(result_type, id, ops[3], "subgroupBallotFindLSB"); + break; + + case OpGroupNonUniformBallotFindMSB: + emit_unary_func_op(result_type, id, ops[3], "subgroupBallotFindMSB"); + break; + + case OpGroupNonUniformBallotBitCount: + { + auto operation = static_cast(ops[3]); + if (operation == GroupOperationReduce) + emit_unary_func_op(result_type, id, ops[4], "subgroupBallotBitCount"); + else if (operation == GroupOperationInclusiveScan) + emit_unary_func_op(result_type, id, ops[4], "subgroupBallotInclusiveBitCount"); + else if (operation == GroupOperationExclusiveScan) + emit_unary_func_op(result_type, id, ops[4], "subgroupBallotExclusiveBitCount"); + else + SPIRV_CROSS_THROW("Invalid BitCount operation."); + break; + } + + case OpGroupNonUniformShuffle: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffle"); + break; + + case OpGroupNonUniformShuffleXor: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleXor"); + break; + + case OpGroupNonUniformShuffleUp: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleUp"); + break; + + case OpGroupNonUniformShuffleDown: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleDown"); + break; + + case OpGroupNonUniformAll: + emit_unary_func_op(result_type, id, ops[3], "subgroupAll"); + break; + + case OpGroupNonUniformAny: + emit_unary_func_op(result_type, id, ops[3], "subgroupAny"); + break; + + case OpGroupNonUniformAllEqual: + emit_unary_func_op(result_type, id, ops[3], "subgroupAllEqual"); + break; + + // clang-format off +#define GLSL_GROUP_OP(op, glsl_op) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op(result_type, id, ops[4], "subgroup" #glsl_op); \ + else if (operation == GroupOperationInclusiveScan) \ + emit_unary_func_op(result_type, id, ops[4], "subgroupInclusive" #glsl_op); \ + else if (operation == GroupOperationExclusiveScan) \ + emit_unary_func_op(result_type, id, ops[4], "subgroupExclusive" #glsl_op); \ + else if (operation == GroupOperationClusteredReduce) \ + emit_binary_func_op(result_type, id, ops[4], ops[5], "subgroupClustered" #glsl_op); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + +#define GLSL_GROUP_OP_CAST(op, glsl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroup" #glsl_op, type, type); \ + else if (operation == GroupOperationInclusiveScan) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroupInclusive" #glsl_op, type, type); \ + else if (operation == GroupOperationExclusiveScan) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroupExclusive" #glsl_op, type, type); \ + else if (operation == GroupOperationClusteredReduce) \ + emit_binary_func_op_cast_clustered(result_type, id, ops[4], ops[5], "subgroupClustered" #glsl_op, type); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + + GLSL_GROUP_OP(FAdd, Add) + GLSL_GROUP_OP(FMul, Mul) + GLSL_GROUP_OP(FMin, Min) + GLSL_GROUP_OP(FMax, Max) + GLSL_GROUP_OP(IAdd, Add) + GLSL_GROUP_OP(IMul, Mul) + GLSL_GROUP_OP_CAST(SMin, Min, int_type) + GLSL_GROUP_OP_CAST(SMax, Max, int_type) + GLSL_GROUP_OP_CAST(UMin, Min, uint_type) + GLSL_GROUP_OP_CAST(UMax, Max, uint_type) + GLSL_GROUP_OP(BitwiseAnd, And) + GLSL_GROUP_OP(BitwiseOr, Or) + GLSL_GROUP_OP(BitwiseXor, Xor) +#undef GLSL_GROUP_OP +#undef GLSL_GROUP_OP_CAST + // clang-format on + + case OpGroupNonUniformQuadSwap: + { + uint32_t direction = evaluate_constant_u32(ops[4]); + if (direction == 0) + emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapHorizontal"); + else if (direction == 1) + emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapVertical"); + else if (direction == 2) + emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapDiagonal"); + else + SPIRV_CROSS_THROW("Invalid quad swap direction."); + break; + } + + case OpGroupNonUniformQuadBroadcast: + { + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupQuadBroadcast"); + break; + } + + default: + SPIRV_CROSS_THROW("Invalid opcode for subgroup."); + } + + register_control_dependent_expression(id); +} + +string CompilerGLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) +{ + // OpBitcast can deal with pointers. + if (out_type.pointer || in_type.pointer) + return type_to_glsl(out_type); + + if (out_type.basetype == in_type.basetype) + return ""; + + assert(out_type.basetype != SPIRType::Boolean); + assert(in_type.basetype != SPIRType::Boolean); + + bool integral_cast = type_is_integral(out_type) && type_is_integral(in_type); + bool same_size_cast = out_type.width == in_type.width; + + // Trivial bitcast case, casts between integers. + if (integral_cast && same_size_cast) + return type_to_glsl(out_type); + + // Catch-all 8-bit arithmetic casts (GL_EXT_shader_explicit_arithmetic_types). + if (out_type.width == 8 && in_type.width >= 16 && integral_cast && in_type.vecsize == 1) + return "unpack8"; + else if (in_type.width == 8 && out_type.width == 16 && integral_cast && out_type.vecsize == 1) + return "pack16"; + else if (in_type.width == 8 && out_type.width == 32 && integral_cast && out_type.vecsize == 1) + return "pack32"; + + // Floating <-> Integer special casts. Just have to enumerate all cases. :( + // 16-bit, 32-bit and 64-bit floats. + if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Float) + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("Float -> Uint bitcast not supported on legacy ESSL."); + else if (!options.es && options.version < 330) + require_extension_internal("GL_ARB_shader_bit_encoding"); + return "floatBitsToUint"; + } + else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Float) + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("Float -> Int bitcast not supported on legacy ESSL."); + else if (!options.es && options.version < 330) + require_extension_internal("GL_ARB_shader_bit_encoding"); + return "floatBitsToInt"; + } + else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::UInt) + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("Uint -> Float bitcast not supported on legacy ESSL."); + else if (!options.es && options.version < 330) + require_extension_internal("GL_ARB_shader_bit_encoding"); + return "uintBitsToFloat"; + } + else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::Int) + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("Int -> Float bitcast not supported on legacy ESSL."); + else if (!options.es && options.version < 330) + require_extension_internal("GL_ARB_shader_bit_encoding"); + return "intBitsToFloat"; + } + + else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Double) + return "doubleBitsToInt64"; + else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Double) + return "doubleBitsToUint64"; + else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::Int64) + return "int64BitsToDouble"; + else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::UInt64) + return "uint64BitsToDouble"; + else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Half) + return "float16BitsToInt16"; + else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::Half) + return "float16BitsToUint16"; + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::Short) + return "int16BitsToFloat16"; + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UShort) + return "uint16BitsToFloat16"; + + // And finally, some even more special purpose casts. + if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::UInt && in_type.vecsize == 2) + return "packUint2x32"; + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::UInt64 && out_type.vecsize == 2) + return "unpackUint2x32"; + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1) + return "unpackFloat2x16"; + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Half && in_type.vecsize == 2) + return "packFloat2x16"; + else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Short && in_type.vecsize == 2) + return "packInt2x16"; + else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Int && in_type.vecsize == 1) + return "unpackInt2x16"; + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::UShort && in_type.vecsize == 2) + return "packUint2x16"; + else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1) + return "unpackUint2x16"; + else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Short && in_type.vecsize == 4) + return "packInt4x16"; + else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Int64 && in_type.vecsize == 1) + return "unpackInt4x16"; + else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::UShort && in_type.vecsize == 4) + return "packUint4x16"; + else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::UInt64 && in_type.vecsize == 1) + return "unpackUint4x16"; + + return ""; +} + +string CompilerGLSL::bitcast_glsl(const SPIRType &result_type, uint32_t argument) +{ + auto op = bitcast_glsl_op(result_type, expression_type(argument)); + if (op.empty()) + return to_enclosed_unpacked_expression(argument); + else + return join(op, "(", to_unpacked_expression(argument), ")"); +} + +std::string CompilerGLSL::bitcast_expression(SPIRType::BaseType target_type, uint32_t arg) +{ + auto expr = to_expression(arg); + auto &src_type = expression_type(arg); + if (src_type.basetype != target_type) + { + auto target = src_type; + target.basetype = target_type; + expr = join(bitcast_glsl_op(target, src_type), "(", expr, ")"); + } + + return expr; +} + +std::string CompilerGLSL::bitcast_expression(const SPIRType &target_type, SPIRType::BaseType expr_type, + const std::string &expr) +{ + if (target_type.basetype == expr_type) + return expr; + + auto src_type = target_type; + src_type.basetype = expr_type; + return join(bitcast_glsl_op(target_type, src_type), "(", expr, ")"); +} + +string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) +{ + switch (builtin) + { + case BuiltInPosition: + return "gl_Position"; + case BuiltInPointSize: + return "gl_PointSize"; + case BuiltInClipDistance: + return "gl_ClipDistance"; + case BuiltInCullDistance: + return "gl_CullDistance"; + case BuiltInVertexId: + if (options.vulkan_semantics) + SPIRV_CROSS_THROW("Cannot implement gl_VertexID in Vulkan GLSL. This shader was created " + "with GL semantics."); + return "gl_VertexID"; + case BuiltInInstanceId: + if (options.vulkan_semantics) + { + auto model = get_entry_point().model; + switch (model) + { + case spv::ExecutionModelIntersectionKHR: + case spv::ExecutionModelAnyHitKHR: + case spv::ExecutionModelClosestHitKHR: + // gl_InstanceID is allowed in these shaders. + break; + + default: + SPIRV_CROSS_THROW("Cannot implement gl_InstanceID in Vulkan GLSL. This shader was " + "created with GL semantics."); + } + } + if (!options.es && options.version < 140) + { + require_extension_internal("GL_ARB_draw_instanced"); + } + return "gl_InstanceID"; + case BuiltInVertexIndex: + if (options.vulkan_semantics) + return "gl_VertexIndex"; + else + return "gl_VertexID"; // gl_VertexID already has the base offset applied. + case BuiltInInstanceIndex: + if (options.vulkan_semantics) + return "gl_InstanceIndex"; + + if (!options.es && options.version < 140) + { + require_extension_internal("GL_ARB_draw_instanced"); + } + + if (options.vertex.support_nonzero_base_instance) + { + if (!options.vulkan_semantics) + { + // This is a soft-enable. We will opt-in to using gl_BaseInstanceARB if supported. + require_extension_internal("GL_ARB_shader_draw_parameters"); + } + return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID. + } + else + return "gl_InstanceID"; + case BuiltInPrimitiveId: + if (storage == StorageClassInput && get_entry_point().model == ExecutionModelGeometry) + return "gl_PrimitiveIDIn"; + else + return "gl_PrimitiveID"; + case BuiltInInvocationId: + return "gl_InvocationID"; + case BuiltInLayer: + return "gl_Layer"; + case BuiltInViewportIndex: + return "gl_ViewportIndex"; + case BuiltInTessLevelOuter: + return "gl_TessLevelOuter"; + case BuiltInTessLevelInner: + return "gl_TessLevelInner"; + case BuiltInTessCoord: + return "gl_TessCoord"; + case BuiltInFragCoord: + return "gl_FragCoord"; + case BuiltInPointCoord: + return "gl_PointCoord"; + case BuiltInFrontFacing: + return "gl_FrontFacing"; + case BuiltInFragDepth: + return "gl_FragDepth"; + case BuiltInNumWorkgroups: + return "gl_NumWorkGroups"; + case BuiltInWorkgroupSize: + return "gl_WorkGroupSize"; + case BuiltInWorkgroupId: + return "gl_WorkGroupID"; + case BuiltInLocalInvocationId: + return "gl_LocalInvocationID"; + case BuiltInGlobalInvocationId: + return "gl_GlobalInvocationID"; + case BuiltInLocalInvocationIndex: + return "gl_LocalInvocationIndex"; + case BuiltInHelperInvocation: + return "gl_HelperInvocation"; + + case BuiltInBaseVertex: + if (options.es) + SPIRV_CROSS_THROW("BaseVertex not supported in ES profile."); + + if (options.vulkan_semantics) + { + if (options.version < 460) + { + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "gl_BaseVertexARB"; + } + return "gl_BaseVertex"; + } + else + { + // On regular GL, this is soft-enabled and we emit ifdefs in code. + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "SPIRV_Cross_BaseVertex"; + } + break; + + case BuiltInBaseInstance: + if (options.es) + SPIRV_CROSS_THROW("BaseInstance not supported in ES profile."); + + if (options.vulkan_semantics) + { + if (options.version < 460) + { + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "gl_BaseInstanceARB"; + } + return "gl_BaseInstance"; + } + else + { + // On regular GL, this is soft-enabled and we emit ifdefs in code. + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "SPIRV_Cross_BaseInstance"; + } + break; + + case BuiltInDrawIndex: + if (options.es) + SPIRV_CROSS_THROW("DrawIndex not supported in ES profile."); + + if (options.vulkan_semantics) + { + if (options.version < 460) + { + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "gl_DrawIDARB"; + } + return "gl_DrawID"; + } + else + { + // On regular GL, this is soft-enabled and we emit ifdefs in code. + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "gl_DrawIDARB"; + } + break; + + case BuiltInSampleId: + if (options.es && options.version < 320) + require_extension_internal("GL_OES_sample_variables"); + if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("gl_SampleID not supported before GLSL 400."); + return "gl_SampleID"; + + case BuiltInSampleMask: + if (options.es && options.version < 320) + require_extension_internal("GL_OES_sample_variables"); + if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("gl_SampleMask/gl_SampleMaskIn not supported before GLSL 400."); + + if (storage == StorageClassInput) + return "gl_SampleMaskIn"; + else + return "gl_SampleMask"; + + case BuiltInSamplePosition: + if (options.es && options.version < 320) + require_extension_internal("GL_OES_sample_variables"); + if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("gl_SamplePosition not supported before GLSL 400."); + return "gl_SamplePosition"; + + case BuiltInViewIndex: + if (options.vulkan_semantics) + { + require_extension_internal("GL_EXT_multiview"); + return "gl_ViewIndex"; + } + else + { + require_extension_internal("GL_OVR_multiview2"); + return "gl_ViewID_OVR"; + } + + case BuiltInNumSubgroups: + request_subgroup_feature(ShaderSubgroupSupportHelper::NumSubgroups); + return "gl_NumSubgroups"; + + case BuiltInSubgroupId: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupID); + return "gl_SubgroupID"; + + case BuiltInSubgroupSize: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupSize); + return "gl_SubgroupSize"; + + case BuiltInSubgroupLocalInvocationId: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupInvocationID); + return "gl_SubgroupInvocationID"; + + case BuiltInSubgroupEqMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupEqMask"; + + case BuiltInSubgroupGeMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupGeMask"; + + case BuiltInSubgroupGtMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupGtMask"; + + case BuiltInSubgroupLeMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupLeMask"; + + case BuiltInSubgroupLtMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupLtMask"; + + case BuiltInLaunchIdNV: + return "gl_LaunchIDNV"; + case BuiltInLaunchSizeNV: + return "gl_LaunchSizeNV"; + case BuiltInWorldRayOriginNV: + return "gl_WorldRayOriginNV"; + case BuiltInWorldRayDirectionNV: + return "gl_WorldRayDirectionNV"; + case BuiltInObjectRayOriginNV: + return "gl_ObjectRayOriginNV"; + case BuiltInObjectRayDirectionNV: + return "gl_ObjectRayDirectionNV"; + case BuiltInRayTminNV: + return "gl_RayTminNV"; + case BuiltInRayTmaxNV: + return "gl_RayTmaxNV"; + case BuiltInInstanceCustomIndexNV: + return "gl_InstanceCustomIndexNV"; + case BuiltInObjectToWorldNV: + return "gl_ObjectToWorldNV"; + case BuiltInWorldToObjectNV: + return "gl_WorldToObjectNV"; + case BuiltInHitTNV: + return "gl_HitTNV"; + case BuiltInHitKindNV: + return "gl_HitKindNV"; + case BuiltInIncomingRayFlagsNV: + return "gl_IncomingRayFlagsNV"; + + case BuiltInBaryCoordNV: + { + if (options.es && options.version < 320) + SPIRV_CROSS_THROW("gl_BaryCoordNV requires ESSL 320."); + else if (!options.es && options.version < 450) + SPIRV_CROSS_THROW("gl_BaryCoordNV requires GLSL 450."); + require_extension_internal("GL_NV_fragment_shader_barycentric"); + return "gl_BaryCoordNV"; + } + + case BuiltInBaryCoordNoPerspNV: + { + if (options.es && options.version < 320) + SPIRV_CROSS_THROW("gl_BaryCoordNoPerspNV requires ESSL 320."); + else if (!options.es && options.version < 450) + SPIRV_CROSS_THROW("gl_BaryCoordNoPerspNV requires GLSL 450."); + require_extension_internal("GL_NV_fragment_shader_barycentric"); + return "gl_BaryCoordNoPerspNV"; + } + + case BuiltInFragStencilRefEXT: + { + if (!options.es) + { + require_extension_internal("GL_ARB_shader_stencil_export"); + return "gl_FragStencilRefARB"; + } + else + SPIRV_CROSS_THROW("Stencil export not supported in GLES."); + } + + case BuiltInDeviceIndex: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Need Vulkan semantics for device group support."); + require_extension_internal("GL_EXT_device_group"); + return "gl_DeviceIndex"; + + default: + return join("gl_BuiltIn_", convert_to_string(builtin)); + } +} + +const char *CompilerGLSL::index_to_swizzle(uint32_t index) +{ + switch (index) + { + case 0: + return "x"; + case 1: + return "y"; + case 2: + return "z"; + case 3: + return "w"; + default: + SPIRV_CROSS_THROW("Swizzle index out of range"); + } +} + +void CompilerGLSL::access_chain_internal_append_index(std::string &expr, uint32_t /*base*/, const SPIRType *type, + AccessChainFlags flags, bool & /*access_chain_is_arrayed*/, + uint32_t index) +{ + bool index_is_literal = (flags & ACCESS_CHAIN_INDEX_IS_LITERAL_BIT) != 0; + bool register_expression_read = (flags & ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT) == 0; + + expr += "["; + + // If we are indexing into an array of SSBOs or UBOs, we need to index it with a non-uniform qualifier. + bool nonuniform_index = + has_decoration(index, DecorationNonUniformEXT) && + (has_decoration(type->self, DecorationBlock) || has_decoration(type->self, DecorationBufferBlock)); + if (nonuniform_index) + { + expr += backend.nonuniform_qualifier; + expr += "("; + } + + if (index_is_literal) + expr += convert_to_string(index); + else + expr += to_expression(index, register_expression_read); + + if (nonuniform_index) + expr += ")"; + + expr += "]"; +} + +string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, + AccessChainFlags flags, AccessChainMeta *meta) +{ + string expr; + + bool index_is_literal = (flags & ACCESS_CHAIN_INDEX_IS_LITERAL_BIT) != 0; + bool msb_is_id = (flags & ACCESS_CHAIN_LITERAL_MSB_FORCE_ID) != 0; + bool chain_only = (flags & ACCESS_CHAIN_CHAIN_ONLY_BIT) != 0; + bool ptr_chain = (flags & ACCESS_CHAIN_PTR_CHAIN_BIT) != 0; + bool register_expression_read = (flags & ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT) == 0; + bool flatten_member_reference = (flags & ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT) != 0; + + if (!chain_only) + { + // We handle transpose explicitly, so don't resolve that here. + auto *e = maybe_get(base); + bool old_transpose = e && e->need_transpose; + if (e) + e->need_transpose = false; + expr = to_enclosed_expression(base, register_expression_read); + if (e) + e->need_transpose = old_transpose; + } + + // Start traversing type hierarchy at the proper non-pointer types, + // but keep type_id referencing the original pointer for use below. + uint32_t type_id = expression_type_id(base); + + if (!backend.native_pointers) + { + if (ptr_chain) + SPIRV_CROSS_THROW("Backend does not support native pointers and does not support OpPtrAccessChain."); + + // Wrapped buffer reference pointer types will need to poke into the internal "value" member before + // continuing the access chain. + if (should_dereference(base)) + { + auto &type = get(type_id); + expr = dereference_expression(type, expr); + } + } + + const auto *type = &get_pointee_type(type_id); + + bool access_chain_is_arrayed = expr.find_first_of('[') != string::npos; + bool row_major_matrix_needs_conversion = is_non_native_row_major_matrix(base); + bool is_packed = has_extended_decoration(base, SPIRVCrossDecorationPhysicalTypePacked); + uint32_t physical_type = get_extended_decoration(base, SPIRVCrossDecorationPhysicalTypeID); + bool is_invariant = has_decoration(base, DecorationInvariant); + bool pending_array_enclose = false; + bool dimension_flatten = false; + + const auto append_index = [&](uint32_t index, bool is_literal) { + AccessChainFlags mod_flags = flags; + if (!is_literal) + mod_flags &= ~ACCESS_CHAIN_INDEX_IS_LITERAL_BIT; + access_chain_internal_append_index(expr, base, type, mod_flags, access_chain_is_arrayed, index); + }; + + for (uint32_t i = 0; i < count; i++) + { + uint32_t index = indices[i]; + + bool is_literal = index_is_literal; + if (is_literal && msb_is_id && (index >> 31u) != 0u) + { + is_literal = false; + index &= 0x7fffffffu; + } + + // Pointer chains + if (ptr_chain && i == 0) + { + // If we are flattening multidimensional arrays, only create opening bracket on first + // array index. + if (options.flatten_multidimensional_arrays) + { + dimension_flatten = type->array.size() >= 1; + pending_array_enclose = dimension_flatten; + if (pending_array_enclose) + expr += "["; + } + + if (options.flatten_multidimensional_arrays && dimension_flatten) + { + // If we are flattening multidimensional arrays, do manual stride computation. + if (is_literal) + expr += convert_to_string(index); + else + expr += to_enclosed_expression(index, register_expression_read); + + for (auto j = uint32_t(type->array.size()); j; j--) + { + expr += " * "; + expr += enclose_expression(to_array_size(*type, j - 1)); + } + + if (type->array.empty()) + pending_array_enclose = false; + else + expr += " + "; + + if (!pending_array_enclose) + expr += "]"; + } + else + { + append_index(index, is_literal); + } + + if (type->basetype == SPIRType::ControlPointArray) + { + type_id = type->parent_type; + type = &get(type_id); + } + + access_chain_is_arrayed = true; + } + // Arrays + else if (!type->array.empty()) + { + // If we are flattening multidimensional arrays, only create opening bracket on first + // array index. + if (options.flatten_multidimensional_arrays && !pending_array_enclose) + { + dimension_flatten = type->array.size() > 1; + pending_array_enclose = dimension_flatten; + if (pending_array_enclose) + expr += "["; + } + + assert(type->parent_type); + + auto *var = maybe_get(base); + if (backend.force_gl_in_out_block && i == 0 && var && is_builtin_variable(*var) && + !has_decoration(type->self, DecorationBlock)) + { + // This deals with scenarios for tesc/geom where arrays of gl_Position[] are declared. + // Normally, these variables live in blocks when compiled from GLSL, + // but HLSL seems to just emit straight arrays here. + // We must pretend this access goes through gl_in/gl_out arrays + // to be able to access certain builtins as arrays. + auto builtin = ir.meta[base].decoration.builtin_type; + switch (builtin) + { + // case BuiltInCullDistance: // These are already arrays, need to figure out rules for these in tess/geom. + // case BuiltInClipDistance: + case BuiltInPosition: + case BuiltInPointSize: + if (var->storage == StorageClassInput) + expr = join("gl_in[", to_expression(index, register_expression_read), "].", expr); + else if (var->storage == StorageClassOutput) + expr = join("gl_out[", to_expression(index, register_expression_read), "].", expr); + else + append_index(index, is_literal); + break; + + default: + append_index(index, is_literal); + break; + } + } + else if (options.flatten_multidimensional_arrays && dimension_flatten) + { + // If we are flattening multidimensional arrays, do manual stride computation. + auto &parent_type = get(type->parent_type); + + if (is_literal) + expr += convert_to_string(index); + else + expr += to_enclosed_expression(index, register_expression_read); + + for (auto j = uint32_t(parent_type.array.size()); j; j--) + { + expr += " * "; + expr += enclose_expression(to_array_size(parent_type, j - 1)); + } + + if (parent_type.array.empty()) + pending_array_enclose = false; + else + expr += " + "; + + if (!pending_array_enclose) + expr += "]"; + } + // Some builtins are arrays in SPIR-V but not in other languages, e.g. gl_SampleMask[] is an array in SPIR-V but not in Metal. + // By throwing away the index, we imply the index was 0, which it must be for gl_SampleMask. + else if (!builtin_translates_to_nonarray(BuiltIn(get_decoration(base, DecorationBuiltIn)))) + { + append_index(index, is_literal); + } + + type_id = type->parent_type; + type = &get(type_id); + + access_chain_is_arrayed = true; + } + // For structs, the index refers to a constant, which indexes into the members. + // We also check if this member is a builtin, since we then replace the entire expression with the builtin one. + else if (type->basetype == SPIRType::Struct) + { + if (!is_literal) + index = evaluate_constant_u32(index); + + if (index >= type->member_types.size()) + SPIRV_CROSS_THROW("Member index is out of bounds!"); + + BuiltIn builtin; + if (is_member_builtin(*type, index, &builtin)) + { + if (access_chain_is_arrayed) + { + expr += "."; + expr += builtin_to_glsl(builtin, type->storage); + } + else + expr = builtin_to_glsl(builtin, type->storage); + } + else + { + // If the member has a qualified name, use it as the entire chain + string qual_mbr_name = get_member_qualified_name(type_id, index); + if (!qual_mbr_name.empty()) + expr = qual_mbr_name; + else if (flatten_member_reference) + expr += join("_", to_member_name(*type, index)); + else + expr += to_member_reference(base, *type, index, ptr_chain); + } + + if (has_member_decoration(type->self, index, DecorationInvariant)) + is_invariant = true; + + is_packed = member_is_packed_physical_type(*type, index); + if (member_is_remapped_physical_type(*type, index)) + physical_type = get_extended_member_decoration(type->self, index, SPIRVCrossDecorationPhysicalTypeID); + else + physical_type = 0; + + row_major_matrix_needs_conversion = member_is_non_native_row_major_matrix(*type, index); + type = &get(type->member_types[index]); + } + // Matrix -> Vector + else if (type->columns > 1) + { + // If we have a row-major matrix here, we need to defer any transpose in case this access chain + // is used to store a column. We can resolve it right here and now if we access a scalar directly, + // by flipping indexing order of the matrix. + + expr += "["; + if (is_literal) + expr += convert_to_string(index); + else + expr += to_expression(index, register_expression_read); + expr += "]"; + + type_id = type->parent_type; + type = &get(type_id); + } + // Vector -> Scalar + else if (type->vecsize > 1) + { + string deferred_index; + if (row_major_matrix_needs_conversion) + { + // Flip indexing order. + auto column_index = expr.find_last_of('['); + if (column_index != string::npos) + { + deferred_index = expr.substr(column_index); + expr.resize(column_index); + } + } + + // Internally, access chain implementation can also be used on composites, + // ignore scalar access workarounds in this case. + StorageClass effective_storage; + if (expression_type(base).pointer) + effective_storage = get_expression_effective_storage_class(base); + else + effective_storage = StorageClassGeneric; + + if (!row_major_matrix_needs_conversion) + { + // On some backends, we might not be able to safely access individual scalars in a vector. + // To work around this, we might have to cast the access chain reference to something which can, + // like a pointer to scalar, which we can then index into. + prepare_access_chain_for_scalar_access(expr, get(type->parent_type), effective_storage, + is_packed); + } + + if (is_literal && !is_packed && !row_major_matrix_needs_conversion) + { + expr += "."; + expr += index_to_swizzle(index); + } + else if (ir.ids[index].get_type() == TypeConstant && !is_packed && !row_major_matrix_needs_conversion) + { + auto &c = get(index); + if (c.specialization) + { + // If the index is a spec constant, we cannot turn extract into a swizzle. + expr += join("[", to_expression(index), "]"); + } + else + { + expr += "."; + expr += index_to_swizzle(c.scalar()); + } + } + else if (is_literal) + { + // For packed vectors, we can only access them as an array, not by swizzle. + expr += join("[", index, "]"); + } + else + { + expr += "["; + expr += to_expression(index, register_expression_read); + expr += "]"; + } + + if (row_major_matrix_needs_conversion) + { + prepare_access_chain_for_scalar_access(expr, get(type->parent_type), effective_storage, + is_packed); + } + + expr += deferred_index; + row_major_matrix_needs_conversion = false; + + is_packed = false; + physical_type = 0; + type_id = type->parent_type; + type = &get(type_id); + } + else if (!backend.allow_truncated_access_chain) + SPIRV_CROSS_THROW("Cannot subdivide a scalar value!"); + } + + if (pending_array_enclose) + { + SPIRV_CROSS_THROW("Flattening of multidimensional arrays were enabled, " + "but the access chain was terminated in the middle of a multidimensional array. " + "This is not supported."); + } + + if (meta) + { + meta->need_transpose = row_major_matrix_needs_conversion; + meta->storage_is_packed = is_packed; + meta->storage_is_invariant = is_invariant; + meta->storage_physical_type = physical_type; + } + + return expr; +} + +void CompilerGLSL::prepare_access_chain_for_scalar_access(std::string &, const SPIRType &, spv::StorageClass, bool &) +{ +} + +string CompilerGLSL::to_flattened_struct_member(const string &basename, const SPIRType &type, uint32_t index) +{ + auto ret = join(basename, "_", to_member_name(type, index)); + ParsedIR::sanitize_underscores(ret); + return ret; +} + +string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type, + AccessChainMeta *meta, bool ptr_chain) +{ + if (flattened_buffer_blocks.count(base)) + { + uint32_t matrix_stride = 0; + uint32_t array_stride = 0; + bool need_transpose = false; + flattened_access_chain_offset(expression_type(base), indices, count, 0, 16, &need_transpose, &matrix_stride, + &array_stride, ptr_chain); + + if (meta) + { + meta->need_transpose = target_type.columns > 1 && need_transpose; + meta->storage_is_packed = false; + } + + return flattened_access_chain(base, indices, count, target_type, 0, matrix_stride, array_stride, + need_transpose); + } + else if (flattened_structs.count(base) && count > 0) + { + AccessChainFlags flags = ACCESS_CHAIN_CHAIN_ONLY_BIT | ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT; + if (ptr_chain) + flags |= ACCESS_CHAIN_PTR_CHAIN_BIT; + + if (flattened_structs[base]) + { + flags |= ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT; + if (meta) + meta->flattened_struct = target_type.basetype == SPIRType::Struct; + } + + auto chain = access_chain_internal(base, indices, count, flags, nullptr).substr(1); + if (meta) + { + meta->need_transpose = false; + meta->storage_is_packed = false; + } + + auto basename = to_flattened_access_chain_expression(base); + auto ret = join(basename, "_", chain); + ParsedIR::sanitize_underscores(ret); + return ret; + } + else + { + AccessChainFlags flags = ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT; + if (ptr_chain) + flags |= ACCESS_CHAIN_PTR_CHAIN_BIT; + return access_chain_internal(base, indices, count, flags, meta); + } +} + +string CompilerGLSL::load_flattened_struct(const string &basename, const SPIRType &type) +{ + auto expr = type_to_glsl_constructor(type); + expr += '('; + + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + if (i) + expr += ", "; + + auto &member_type = get(type.member_types[i]); + if (member_type.basetype == SPIRType::Struct) + expr += load_flattened_struct(to_flattened_struct_member(basename, type, i), member_type); + else + expr += to_flattened_struct_member(basename, type, i); + } + expr += ')'; + return expr; +} + +std::string CompilerGLSL::to_flattened_access_chain_expression(uint32_t id) +{ + // Do not use to_expression as that will unflatten access chains. + string basename; + if (const auto *var = maybe_get(id)) + basename = to_name(var->self); + else if (const auto *expr = maybe_get(id)) + basename = expr->expression; + else + basename = to_expression(id); + + return basename; +} + +void CompilerGLSL::store_flattened_struct(const string &basename, uint32_t rhs_id, const SPIRType &type, + const SmallVector &indices) +{ + SmallVector sub_indices = indices; + sub_indices.push_back(0); + + auto *member_type = &type; + for (auto &index : indices) + member_type = &get(member_type->member_types[index]); + + for (uint32_t i = 0; i < uint32_t(member_type->member_types.size()); i++) + { + sub_indices.back() = i; + auto lhs = join(basename, "_", to_member_name(*member_type, i)); + ParsedIR::sanitize_underscores(lhs); + + if (get(member_type->member_types[i]).basetype == SPIRType::Struct) + { + store_flattened_struct(lhs, rhs_id, type, sub_indices); + } + else + { + auto rhs = to_expression(rhs_id) + to_multi_member_reference(type, sub_indices); + statement(lhs, " = ", rhs, ";"); + } + } +} + +void CompilerGLSL::store_flattened_struct(uint32_t lhs_id, uint32_t value) +{ + auto &type = expression_type(lhs_id); + auto basename = to_flattened_access_chain_expression(lhs_id); + store_flattened_struct(basename, value, type, {}); +} + +std::string CompilerGLSL::flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + uint32_t /* array_stride */, bool need_transpose) +{ + if (!target_type.array.empty()) + SPIRV_CROSS_THROW("Access chains that result in an array can not be flattened"); + else if (target_type.basetype == SPIRType::Struct) + return flattened_access_chain_struct(base, indices, count, target_type, offset); + else if (target_type.columns > 1) + return flattened_access_chain_matrix(base, indices, count, target_type, offset, matrix_stride, need_transpose); + else + return flattened_access_chain_vector(base, indices, count, target_type, offset, matrix_stride, need_transpose); +} + +std::string CompilerGLSL::flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset) +{ + std::string expr; + + expr += type_to_glsl_constructor(target_type); + expr += "("; + + for (uint32_t i = 0; i < uint32_t(target_type.member_types.size()); ++i) + { + if (i != 0) + expr += ", "; + + const SPIRType &member_type = get(target_type.member_types[i]); + uint32_t member_offset = type_struct_member_offset(target_type, i); + + // The access chain terminates at the struct, so we need to find matrix strides and row-major information + // ahead of time. + bool need_transpose = false; + uint32_t matrix_stride = 0; + if (member_type.columns > 1) + { + need_transpose = combined_decoration_for_member(target_type, i).get(DecorationRowMajor); + matrix_stride = type_struct_member_matrix_stride(target_type, i); + } + + auto tmp = flattened_access_chain(base, indices, count, member_type, offset + member_offset, matrix_stride, + 0 /* array_stride */, need_transpose); + + // Cannot forward transpositions, so resolve them here. + if (need_transpose) + expr += convert_row_major_matrix(tmp, member_type, 0, false); + else + expr += tmp; + } + + expr += ")"; + + return expr; +} + +std::string CompilerGLSL::flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, + uint32_t matrix_stride, bool need_transpose) +{ + assert(matrix_stride); + SPIRType tmp_type = target_type; + if (need_transpose) + swap(tmp_type.vecsize, tmp_type.columns); + + std::string expr; + + expr += type_to_glsl_constructor(tmp_type); + expr += "("; + + for (uint32_t i = 0; i < tmp_type.columns; i++) + { + if (i != 0) + expr += ", "; + + expr += flattened_access_chain_vector(base, indices, count, tmp_type, offset + i * matrix_stride, matrix_stride, + /* need_transpose= */ false); + } + + expr += ")"; + + return expr; +} + +std::string CompilerGLSL::flattened_access_chain_vector(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, + uint32_t matrix_stride, bool need_transpose) +{ + auto result = flattened_access_chain_offset(expression_type(base), indices, count, offset, 16); + + auto buffer_name = to_name(expression_type(base).self); + + if (need_transpose) + { + std::string expr; + + if (target_type.vecsize > 1) + { + expr += type_to_glsl_constructor(target_type); + expr += "("; + } + + for (uint32_t i = 0; i < target_type.vecsize; ++i) + { + if (i != 0) + expr += ", "; + + uint32_t component_offset = result.second + i * matrix_stride; + + assert(component_offset % (target_type.width / 8) == 0); + uint32_t index = component_offset / (target_type.width / 8); + + expr += buffer_name; + expr += "["; + expr += result.first; // this is a series of N1 * k1 + N2 * k2 + ... that is either empty or ends with a + + expr += convert_to_string(index / 4); + expr += "]"; + + expr += vector_swizzle(1, index % 4); + } + + if (target_type.vecsize > 1) + { + expr += ")"; + } + + return expr; + } + else + { + assert(result.second % (target_type.width / 8) == 0); + uint32_t index = result.second / (target_type.width / 8); + + std::string expr; + + expr += buffer_name; + expr += "["; + expr += result.first; // this is a series of N1 * k1 + N2 * k2 + ... that is either empty or ends with a + + expr += convert_to_string(index / 4); + expr += "]"; + + expr += vector_swizzle(target_type.vecsize, index % 4); + + return expr; + } +} + +std::pair CompilerGLSL::flattened_access_chain_offset( + const SPIRType &basetype, const uint32_t *indices, uint32_t count, uint32_t offset, uint32_t word_stride, + bool *need_transpose, uint32_t *out_matrix_stride, uint32_t *out_array_stride, bool ptr_chain) +{ + // Start traversing type hierarchy at the proper non-pointer types. + const auto *type = &get_pointee_type(basetype); + + std::string expr; + + // Inherit matrix information in case we are access chaining a vector which might have come from a row major layout. + bool row_major_matrix_needs_conversion = need_transpose ? *need_transpose : false; + uint32_t matrix_stride = out_matrix_stride ? *out_matrix_stride : 0; + uint32_t array_stride = out_array_stride ? *out_array_stride : 0; + + for (uint32_t i = 0; i < count; i++) + { + uint32_t index = indices[i]; + + // Pointers + if (ptr_chain && i == 0) + { + // Here, the pointer type will be decorated with an array stride. + array_stride = get_decoration(basetype.self, DecorationArrayStride); + if (!array_stride) + SPIRV_CROSS_THROW("SPIR-V does not define ArrayStride for buffer block."); + + auto *constant = maybe_get(index); + if (constant) + { + // Constant array access. + offset += constant->scalar() * array_stride; + } + else + { + // Dynamic array access. + if (array_stride % word_stride) + { + SPIRV_CROSS_THROW("Array stride for dynamic indexing must be divisible by the size " + "of a 4-component vector. " + "Likely culprit here is a float or vec2 array inside a push " + "constant block which is std430. " + "This cannot be flattened. Try using std140 layout instead."); + } + + expr += to_enclosed_expression(index); + expr += " * "; + expr += convert_to_string(array_stride / word_stride); + expr += " + "; + } + } + // Arrays + else if (!type->array.empty()) + { + auto *constant = maybe_get(index); + if (constant) + { + // Constant array access. + offset += constant->scalar() * array_stride; + } + else + { + // Dynamic array access. + if (array_stride % word_stride) + { + SPIRV_CROSS_THROW("Array stride for dynamic indexing must be divisible by the size " + "of a 4-component vector. " + "Likely culprit here is a float or vec2 array inside a push " + "constant block which is std430. " + "This cannot be flattened. Try using std140 layout instead."); + } + + expr += to_enclosed_expression(index, false); + expr += " * "; + expr += convert_to_string(array_stride / word_stride); + expr += " + "; + } + + uint32_t parent_type = type->parent_type; + type = &get(parent_type); + + if (!type->array.empty()) + array_stride = get_decoration(parent_type, DecorationArrayStride); + } + // For structs, the index refers to a constant, which indexes into the members. + // We also check if this member is a builtin, since we then replace the entire expression with the builtin one. + else if (type->basetype == SPIRType::Struct) + { + index = evaluate_constant_u32(index); + + if (index >= type->member_types.size()) + SPIRV_CROSS_THROW("Member index is out of bounds!"); + + offset += type_struct_member_offset(*type, index); + + auto &struct_type = *type; + type = &get(type->member_types[index]); + + if (type->columns > 1) + { + matrix_stride = type_struct_member_matrix_stride(struct_type, index); + row_major_matrix_needs_conversion = + combined_decoration_for_member(struct_type, index).get(DecorationRowMajor); + } + else + row_major_matrix_needs_conversion = false; + + if (!type->array.empty()) + array_stride = type_struct_member_array_stride(struct_type, index); + } + // Matrix -> Vector + else if (type->columns > 1) + { + auto *constant = maybe_get(index); + if (constant) + { + index = evaluate_constant_u32(index); + offset += index * (row_major_matrix_needs_conversion ? (type->width / 8) : matrix_stride); + } + else + { + uint32_t indexing_stride = row_major_matrix_needs_conversion ? (type->width / 8) : matrix_stride; + // Dynamic array access. + if (indexing_stride % word_stride) + { + SPIRV_CROSS_THROW("Matrix stride for dynamic indexing must be divisible by the size of a " + "4-component vector. " + "Likely culprit here is a row-major matrix being accessed dynamically. " + "This cannot be flattened. Try using std140 layout instead."); + } + + expr += to_enclosed_expression(index, false); + expr += " * "; + expr += convert_to_string(indexing_stride / word_stride); + expr += " + "; + } + + type = &get(type->parent_type); + } + // Vector -> Scalar + else if (type->vecsize > 1) + { + auto *constant = maybe_get(index); + if (constant) + { + index = evaluate_constant_u32(index); + offset += index * (row_major_matrix_needs_conversion ? matrix_stride : (type->width / 8)); + } + else + { + uint32_t indexing_stride = row_major_matrix_needs_conversion ? matrix_stride : (type->width / 8); + + // Dynamic array access. + if (indexing_stride % word_stride) + { + SPIRV_CROSS_THROW("Stride for dynamic vector indexing must be divisible by the " + "size of a 4-component vector. " + "This cannot be flattened in legacy targets."); + } + + expr += to_enclosed_expression(index, false); + expr += " * "; + expr += convert_to_string(indexing_stride / word_stride); + expr += " + "; + } + + type = &get(type->parent_type); + } + else + SPIRV_CROSS_THROW("Cannot subdivide a scalar value!"); + } + + if (need_transpose) + *need_transpose = row_major_matrix_needs_conversion; + if (out_matrix_stride) + *out_matrix_stride = matrix_stride; + if (out_array_stride) + *out_array_stride = array_stride; + + return std::make_pair(expr, offset); +} + +bool CompilerGLSL::should_dereference(uint32_t id) +{ + const auto &type = expression_type(id); + // Non-pointer expressions don't need to be dereferenced. + if (!type.pointer) + return false; + + // Handles shouldn't be dereferenced either. + if (!expression_is_lvalue(id)) + return false; + + // If id is a variable but not a phi variable, we should not dereference it. + if (auto *var = maybe_get(id)) + return var->phi_variable; + + // If id is an access chain, we should not dereference it. + if (auto *expr = maybe_get(id)) + return !expr->access_chain; + + // Otherwise, we should dereference this pointer expression. + return true; +} + +bool CompilerGLSL::should_forward(uint32_t id) const +{ + // If id is a variable we will try to forward it regardless of force_temporary check below + // This is important because otherwise we'll get local sampler copies (highp sampler2D foo = bar) that are invalid in OpenGL GLSL + auto *var = maybe_get(id); + if (var && var->forwardable) + return true; + + // For debugging emit temporary variables for all expressions + if (options.force_temporary) + return false; + + // Immutable expression can always be forwarded. + if (is_immutable(id)) + return true; + + return false; +} + +bool CompilerGLSL::should_suppress_usage_tracking(uint32_t id) const +{ + // Used only by opcodes which don't do any real "work", they just swizzle data in some fashion. + return !expression_is_forwarded(id) || expression_suppresses_usage_tracking(id); +} + +void CompilerGLSL::track_expression_read(uint32_t id) +{ + switch (ir.ids[id].get_type()) + { + case TypeExpression: + { + auto &e = get(id); + for (auto implied_read : e.implied_read_expressions) + track_expression_read(implied_read); + break; + } + + case TypeAccessChain: + { + auto &e = get(id); + for (auto implied_read : e.implied_read_expressions) + track_expression_read(implied_read); + break; + } + + default: + break; + } + + // If we try to read a forwarded temporary more than once we will stamp out possibly complex code twice. + // In this case, it's better to just bind the complex expression to the temporary and read that temporary twice. + if (expression_is_forwarded(id) && !expression_suppresses_usage_tracking(id)) + { + auto &v = expression_usage_counts[id]; + v++; + + // If we create an expression outside a loop, + // but access it inside a loop, we're implicitly reading it multiple times. + // If the expression in question is expensive, we should hoist it out to avoid relying on loop-invariant code motion + // working inside the backend compiler. + if (expression_read_implies_multiple_reads(id)) + v++; + + if (v >= 2) + { + //if (v == 2) + // fprintf(stderr, "ID %u was forced to temporary due to more than 1 expression use!\n", id); + + forced_temporaries.insert(id); + // Force a recompile after this pass to avoid forwarding this variable. + force_recompile(); + } + } +} + +bool CompilerGLSL::args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure) +{ + if (forced_temporaries.find(id) != end(forced_temporaries)) + return false; + + for (uint32_t i = 0; i < num_args; i++) + if (!should_forward(args[i])) + return false; + + // We need to forward globals as well. + if (!pure) + { + for (auto global : global_variables) + if (!should_forward(global)) + return false; + for (auto aliased : aliased_variables) + if (!should_forward(aliased)) + return false; + } + + return true; +} + +void CompilerGLSL::register_impure_function_call() +{ + // Impure functions can modify globals and aliased variables, so invalidate them as well. + for (auto global : global_variables) + flush_dependees(get(global)); + for (auto aliased : aliased_variables) + flush_dependees(get(aliased)); +} + +void CompilerGLSL::register_call_out_argument(uint32_t id) +{ + register_write(id); + + auto *var = maybe_get(id); + if (var) + flush_variable_declaration(var->self); +} + +string CompilerGLSL::variable_decl_function_local(SPIRVariable &var) +{ + // These variables are always function local, + // so make sure we emit the variable without storage qualifiers. + // Some backends will inject custom variables locally in a function + // with a storage qualifier which is not function-local. + auto old_storage = var.storage; + var.storage = StorageClassFunction; + auto expr = variable_decl(var); + var.storage = old_storage; + return expr; +} + +void CompilerGLSL::emit_variable_temporary_copies(const SPIRVariable &var) +{ + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + if (var.allocate_temporary_copy && !flushed_phi_variables.count(var.self)) + { + auto &type = get(var.basetype); + auto &flags = get_decoration_bitset(var.self); + statement(flags_to_qualifiers_glsl(type, flags), variable_decl(type, join("_", var.self, "_copy")), ";"); + flushed_phi_variables.insert(var.self); + } +} + +void CompilerGLSL::flush_variable_declaration(uint32_t id) +{ + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + auto *var = maybe_get(id); + if (var && var->deferred_declaration) + { + string initializer; + if (options.force_zero_initialized_variables && + (var->storage == StorageClassFunction || var->storage == StorageClassGeneric || + var->storage == StorageClassPrivate) && + !var->initializer && type_can_zero_initialize(get_variable_data_type(*var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(*var))); + } + + statement(variable_decl_function_local(*var), initializer, ";"); + var->deferred_declaration = false; + } + if (var) + { + emit_variable_temporary_copies(*var); + } +} + +bool CompilerGLSL::remove_duplicate_swizzle(string &op) +{ + auto pos = op.find_last_of('.'); + if (pos == string::npos || pos == 0) + return false; + + string final_swiz = op.substr(pos + 1, string::npos); + + if (backend.swizzle_is_function) + { + if (final_swiz.size() < 2) + return false; + + if (final_swiz.substr(final_swiz.size() - 2, string::npos) == "()") + final_swiz.erase(final_swiz.size() - 2, string::npos); + else + return false; + } + + // Check if final swizzle is of form .x, .xy, .xyz, .xyzw or similar. + // If so, and previous swizzle is of same length, + // we can drop the final swizzle altogether. + for (uint32_t i = 0; i < final_swiz.size(); i++) + { + static const char expected[] = { 'x', 'y', 'z', 'w' }; + if (i >= 4 || final_swiz[i] != expected[i]) + return false; + } + + auto prevpos = op.find_last_of('.', pos - 1); + if (prevpos == string::npos) + return false; + + prevpos++; + + // Make sure there are only swizzles here ... + for (auto i = prevpos; i < pos; i++) + { + if (op[i] < 'w' || op[i] > 'z') + { + // If swizzles are foo.xyz() like in C++ backend for example, check for that. + if (backend.swizzle_is_function && i + 2 == pos && op[i] == '(' && op[i + 1] == ')') + break; + return false; + } + } + + // If original swizzle is large enough, just carve out the components we need. + // E.g. foobar.wyx.xy will turn into foobar.wy. + if (pos - prevpos >= final_swiz.size()) + { + op.erase(prevpos + final_swiz.size(), string::npos); + + // Add back the function call ... + if (backend.swizzle_is_function) + op += "()"; + } + return true; +} + +// Optimizes away vector swizzles where we have something like +// vec3 foo; +// foo.xyz <-- swizzle expression does nothing. +// This is a very common pattern after OpCompositeCombine. +bool CompilerGLSL::remove_unity_swizzle(uint32_t base, string &op) +{ + auto pos = op.find_last_of('.'); + if (pos == string::npos || pos == 0) + return false; + + string final_swiz = op.substr(pos + 1, string::npos); + + if (backend.swizzle_is_function) + { + if (final_swiz.size() < 2) + return false; + + if (final_swiz.substr(final_swiz.size() - 2, string::npos) == "()") + final_swiz.erase(final_swiz.size() - 2, string::npos); + else + return false; + } + + // Check if final swizzle is of form .x, .xy, .xyz, .xyzw or similar. + // If so, and previous swizzle is of same length, + // we can drop the final swizzle altogether. + for (uint32_t i = 0; i < final_swiz.size(); i++) + { + static const char expected[] = { 'x', 'y', 'z', 'w' }; + if (i >= 4 || final_swiz[i] != expected[i]) + return false; + } + + auto &type = expression_type(base); + + // Sanity checking ... + assert(type.columns == 1 && type.array.empty()); + + if (type.vecsize == final_swiz.size()) + op.erase(pos, string::npos); + return true; +} + +string CompilerGLSL::build_composite_combiner(uint32_t return_type, const uint32_t *elems, uint32_t length) +{ + ID base = 0; + string op; + string subop; + + // Can only merge swizzles for vectors. + auto &type = get(return_type); + bool can_apply_swizzle_opt = type.basetype != SPIRType::Struct && type.array.empty() && type.columns == 1; + bool swizzle_optimization = false; + + for (uint32_t i = 0; i < length; i++) + { + auto *e = maybe_get(elems[i]); + + // If we're merging another scalar which belongs to the same base + // object, just merge the swizzles to avoid triggering more than 1 expression read as much as possible! + if (can_apply_swizzle_opt && e && e->base_expression && e->base_expression == base) + { + // Only supposed to be used for vector swizzle -> scalar. + assert(!e->expression.empty() && e->expression.front() == '.'); + subop += e->expression.substr(1, string::npos); + swizzle_optimization = true; + } + else + { + // We'll likely end up with duplicated swizzles, e.g. + // foobar.xyz.xyz from patterns like + // OpVectorShuffle + // OpCompositeExtract x 3 + // OpCompositeConstruct 3x + other scalar. + // Just modify op in-place. + if (swizzle_optimization) + { + if (backend.swizzle_is_function) + subop += "()"; + + // Don't attempt to remove unity swizzling if we managed to remove duplicate swizzles. + // The base "foo" might be vec4, while foo.xyz is vec3 (OpVectorShuffle) and looks like a vec3 due to the .xyz tacked on. + // We only want to remove the swizzles if we're certain that the resulting base will be the same vecsize. + // Essentially, we can only remove one set of swizzles, since that's what we have control over ... + // Case 1: + // foo.yxz.xyz: Duplicate swizzle kicks in, giving foo.yxz, we are done. + // foo.yxz was the result of OpVectorShuffle and we don't know the type of foo. + // Case 2: + // foo.xyz: Duplicate swizzle won't kick in. + // If foo is vec3, we can remove xyz, giving just foo. + if (!remove_duplicate_swizzle(subop)) + remove_unity_swizzle(base, subop); + + // Strips away redundant parens if we created them during component extraction. + strip_enclosed_expression(subop); + swizzle_optimization = false; + op += subop; + } + else + op += subop; + + if (i) + op += ", "; + + bool uses_buffer_offset = + type.basetype == SPIRType::Struct && has_member_decoration(type.self, i, DecorationOffset); + subop = to_composite_constructor_expression(elems[i], uses_buffer_offset); + } + + base = e ? e->base_expression : ID(0); + } + + if (swizzle_optimization) + { + if (backend.swizzle_is_function) + subop += "()"; + + if (!remove_duplicate_swizzle(subop)) + remove_unity_swizzle(base, subop); + // Strips away redundant parens if we created them during component extraction. + strip_enclosed_expression(subop); + } + + op += subop; + return op; +} + +bool CompilerGLSL::skip_argument(uint32_t id) const +{ + if (!combined_image_samplers.empty() || !options.vulkan_semantics) + { + auto &type = expression_type(id); + if (type.basetype == SPIRType::Sampler || (type.basetype == SPIRType::Image && type.image.sampled == 1)) + return true; + } + return false; +} + +bool CompilerGLSL::optimize_read_modify_write(const SPIRType &type, const string &lhs, const string &rhs) +{ + // Do this with strings because we have a very clear pattern we can check for and it avoids + // adding lots of special cases to the code emission. + if (rhs.size() < lhs.size() + 3) + return false; + + // Do not optimize matrices. They are a bit awkward to reason about in general + // (in which order does operation happen?), and it does not work on MSL anyways. + if (type.vecsize > 1 && type.columns > 1) + return false; + + auto index = rhs.find(lhs); + if (index != 0) + return false; + + // TODO: Shift operators, but it's not important for now. + auto op = rhs.find_first_of("+-/*%|&^", lhs.size() + 1); + if (op != lhs.size() + 1) + return false; + + // Check that the op is followed by space. This excludes && and ||. + if (rhs[op + 1] != ' ') + return false; + + char bop = rhs[op]; + auto expr = rhs.substr(lhs.size() + 3); + // Try to find increments and decrements. Makes it look neater as += 1, -= 1 is fairly rare to see in real code. + // Find some common patterns which are equivalent. + if ((bop == '+' || bop == '-') && (expr == "1" || expr == "uint(1)" || expr == "1u" || expr == "int(1u)")) + statement(lhs, bop, bop, ";"); + else + statement(lhs, " ", bop, "= ", expr, ";"); + return true; +} + +void CompilerGLSL::register_control_dependent_expression(uint32_t expr) +{ + if (forwarded_temporaries.find(expr) == end(forwarded_temporaries)) + return; + + assert(current_emitting_block); + current_emitting_block->invalidate_expressions.push_back(expr); +} + +void CompilerGLSL::emit_block_instructions(SPIRBlock &block) +{ + current_emitting_block = █ + for (auto &op : block.ops) + emit_instruction(op); + current_emitting_block = nullptr; +} + +void CompilerGLSL::disallow_forwarding_in_expression_chain(const SPIRExpression &expr) +{ + // Allow trivially forwarded expressions like OpLoad or trivial shuffles, + // these will be marked as having suppressed usage tracking. + // Our only concern is to make sure arithmetic operations are done in similar ways. + if (expression_is_forwarded(expr.self) && !expression_suppresses_usage_tracking(expr.self) && + forced_invariant_temporaries.count(expr.self) == 0) + { + forced_temporaries.insert(expr.self); + forced_invariant_temporaries.insert(expr.self); + force_recompile(); + + for (auto &dependent : expr.expression_dependencies) + disallow_forwarding_in_expression_chain(get(dependent)); + } +} + +void CompilerGLSL::handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id) +{ + // Variables or access chains marked invariant are complicated. We will need to make sure the code-gen leading up to + // this variable is consistent. The failure case for SPIRV-Cross is when an expression is forced to a temporary + // in one translation unit, but not another, e.g. due to multiple use of an expression. + // This causes variance despite the output variable being marked invariant, so the solution here is to force all dependent + // expressions to be temporaries. + // It is uncertain if this is enough to support invariant in all possible cases, but it should be good enough + // for all reasonable uses of invariant. + if (!has_decoration(store_id, DecorationInvariant)) + return; + + auto *expr = maybe_get(value_id); + if (!expr) + return; + + disallow_forwarding_in_expression_chain(*expr); +} + +void CompilerGLSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) +{ + auto rhs = to_pointer_expression(rhs_expression); + + // Statements to OpStore may be empty if it is a struct with zero members. Just forward the store to /dev/null. + if (!rhs.empty()) + { + handle_store_to_invariant_variable(lhs_expression, rhs_expression); + + auto lhs = to_dereferenced_expression(lhs_expression); + + // We might need to cast in order to store to a builtin. + cast_to_builtin_store(lhs_expression, rhs, expression_type(rhs_expression)); + + // Tries to optimize assignments like " = op expr". + // While this is purely cosmetic, this is important for legacy ESSL where loop + // variable increments must be in either i++ or i += const-expr. + // Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0. + if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs)) + statement(lhs, " = ", rhs, ";"); + register_write(lhs_expression); + } +} + +uint32_t CompilerGLSL::get_integer_width_for_instruction(const Instruction &instr) const +{ + if (instr.length < 3) + return 32; + + auto *ops = stream(instr); + + switch (instr.op) + { + case OpSConvert: + case OpConvertSToF: + case OpUConvert: + case OpConvertUToF: + case OpIEqual: + case OpINotEqual: + case OpSLessThan: + case OpSLessThanEqual: + case OpSGreaterThan: + case OpSGreaterThanEqual: + case OpULessThan: + case OpULessThanEqual: + case OpUGreaterThan: + case OpUGreaterThanEqual: + return expression_type(ops[2]).width; + + default: + { + // We can look at result type which is more robust. + auto *type = maybe_get(ops[0]); + if (type && type_is_integral(*type)) + return type->width; + else + return 32; + } + } +} + +uint32_t CompilerGLSL::get_integer_width_for_glsl_instruction(GLSLstd450 op, const uint32_t *ops, uint32_t length) const +{ + if (length < 1) + return 32; + + switch (op) + { + case GLSLstd450SAbs: + case GLSLstd450SSign: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FindSMsb: + case GLSLstd450FindUMsb: + return expression_type(ops[0]).width; + + default: + { + // We don't need to care about other opcodes, just return 32. + return 32; + } + } +} + +void CompilerGLSL::emit_instruction(const Instruction &instruction) +{ + auto ops = stream(instruction); + auto opcode = static_cast(instruction.op); + uint32_t length = instruction.length; + +#define GLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#define GLSL_BOP_CAST(op, type) \ + emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) +#define GLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) +#define GLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define GLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) +#define GLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define GLSL_BFOP_CAST(op, type) \ + emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) +#define GLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define GLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(instruction); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + switch (opcode) + { + // Dealing with memory + case OpLoad: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + + flush_variable_declaration(ptr); + + // If we're loading from memory that cannot be changed by the shader, + // just forward the expression directly to avoid needless temporaries. + // If an expression is mutable and forwardable, we speculate that it is immutable. + bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries); + + // If loading a non-native row-major matrix, mark the expression as need_transpose. + bool need_transpose = false; + bool old_need_transpose = false; + + auto *ptr_expression = maybe_get(ptr); + + if (forward) + { + // If we're forwarding the load, we're also going to forward transpose state, so don't transpose while + // taking the expression. + if (ptr_expression && ptr_expression->need_transpose) + { + old_need_transpose = true; + ptr_expression->need_transpose = false; + need_transpose = true; + } + else if (is_non_native_row_major_matrix(ptr)) + need_transpose = true; + } + + // If we are forwarding this load, + // don't register the read to access chain here, defer that to when we actually use the expression, + // using the add_implied_read_expression mechanism. + string expr; + + bool is_packed = has_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypePacked); + bool is_remapped = has_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypeID); + if (forward || (!is_packed && !is_remapped)) + { + // For the simple case, we do not need to deal with repacking. + expr = to_dereferenced_expression(ptr, false); + } + else + { + // If we are not forwarding the expression, we need to unpack and resolve any physical type remapping here before + // storing the expression to a temporary. + expr = to_unpacked_expression(ptr); + } + + auto &type = get(result_type); + auto &expr_type = expression_type(ptr); + + // If the expression has more vector components than the result type, insert + // a swizzle. This shouldn't happen normally on valid SPIR-V, but it might + // happen with e.g. the MSL backend replacing the type of an input variable. + if (expr_type.vecsize > type.vecsize) + expr = enclose_expression(expr + vector_swizzle(type.vecsize, 0)); + + // We might need to cast in order to load from a builtin. + cast_from_builtin_load(ptr, expr, type); + + // We might be trying to load a gl_Position[N], where we should be + // doing float4[](gl_in[i].gl_Position, ...) instead. + // Similar workarounds are required for input arrays in tessellation. + unroll_array_from_complex_load(id, ptr, expr); + + // Shouldn't need to check for ID, but current glslang codegen requires it in some cases + // when loading Image/Sampler descriptors. It does not hurt to check ID as well. + if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ptr, DecorationNonUniformEXT)) + { + propagate_nonuniform_qualifier(ptr); + convert_non_uniform_expression(type, expr); + } + + if (forward && ptr_expression) + ptr_expression->need_transpose = old_need_transpose; + + bool flattened = ptr_expression && flattened_buffer_blocks.count(ptr_expression->loaded_from) != 0; + + if (backend.needs_row_major_load_workaround && !is_non_native_row_major_matrix(ptr) && !flattened) + rewrite_load_for_wrapped_row_major(expr, result_type, ptr); + + // By default, suppress usage tracking since using same expression multiple times does not imply any extra work. + // However, if we try to load a complex, composite object from a flattened buffer, + // we should avoid emitting the same code over and over and lower the result to a temporary. + bool usage_tracking = flattened && (type.basetype == SPIRType::Struct || (type.columns > 1)); + + SPIRExpression *e = nullptr; + if (!forward && expression_is_non_value_type_array(ptr)) + { + // Complicated load case where we need to make a copy of ptr, but we cannot, because + // it is an array, and our backend does not support arrays as value types. + // Emit the temporary, and copy it explicitly. + e = &emit_uninitialized_temporary_expression(result_type, id); + emit_array_copy(to_expression(id), ptr, StorageClassFunction, get_expression_effective_storage_class(ptr)); + } + else + e = &emit_op(result_type, id, expr, forward, !usage_tracking); + + e->need_transpose = need_transpose; + register_read(id, ptr, forward); + + if (forward) + { + // Pass through whether the result is of a packed type and the physical type ID. + if (has_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypePacked)) + set_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + if (has_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypeID)) + { + set_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID, + get_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypeID)); + } + } + else + { + // This might have been set on an earlier compilation iteration, force it to be unset. + unset_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + unset_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID); + } + + inherit_expression_dependencies(id, ptr); + if (forward) + add_implied_read_expression(*e, ptr); + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + auto *var = maybe_get(ops[2]); + if (var) + flush_variable_declaration(var->self); + + // If the base is immutable, the access chain pointer must also be. + // If an expression is mutable and forwardable, we speculate that it is immutable. + AccessChainMeta meta; + bool ptr_chain = opcode == OpPtrAccessChain; + auto e = access_chain(ops[2], &ops[3], length - 3, get(ops[0]), &meta, ptr_chain); + + auto &expr = set(ops[1], move(e), ops[0], should_forward(ops[2])); + + auto *backing_variable = maybe_get_backing_variable(ops[2]); + expr.loaded_from = backing_variable ? backing_variable->self : ID(ops[2]); + expr.need_transpose = meta.need_transpose; + expr.access_chain = true; + + // Mark the result as being packed. Some platforms handled packed vectors differently than non-packed. + if (meta.storage_is_packed) + set_extended_decoration(ops[1], SPIRVCrossDecorationPhysicalTypePacked); + if (meta.storage_physical_type != 0) + set_extended_decoration(ops[1], SPIRVCrossDecorationPhysicalTypeID, meta.storage_physical_type); + if (meta.storage_is_invariant) + set_decoration(ops[1], DecorationInvariant); + if (meta.flattened_struct) + flattened_structs[ops[1]] = true; + + // If we have some expression dependencies in our access chain, this access chain is technically a forwarded + // temporary which could be subject to invalidation. + // Need to assume we're forwarded while calling inherit_expression_depdendencies. + forwarded_temporaries.insert(ops[1]); + // The access chain itself is never forced to a temporary, but its dependencies might. + suppressed_usage_tracking.insert(ops[1]); + + for (uint32_t i = 2; i < length; i++) + { + inherit_expression_dependencies(ops[1], ops[i]); + add_implied_read_expression(expr, ops[i]); + } + + // If we have no dependencies after all, i.e., all indices in the access chain are immutable temporaries, + // we're not forwarded after all. + if (expr.expression_dependencies.empty()) + forwarded_temporaries.erase(ops[1]); + + if (has_decoration(ops[1], DecorationNonUniformEXT)) + propagate_nonuniform_qualifier(ops[1]); + + break; + } + + case OpStore: + { + auto *var = maybe_get(ops[0]); + + if (var && var->statically_assigned) + var->static_expression = ops[1]; + else if (var && var->loop_variable && !var->loop_variable_enable) + var->static_expression = ops[1]; + else if (var && var->remapped_variable && var->static_expression) + { + // Skip the write. + } + else if (flattened_structs.count(ops[0])) + { + store_flattened_struct(ops[0], ops[1]); + register_write(ops[0]); + } + else + { + emit_store_statement(ops[0], ops[1]); + } + + // Storing a pointer results in a variable pointer, so we must conservatively assume + // we can write through it. + if (expression_type(ops[1]).pointer) + register_write(ops[1]); + break; + } + + case OpArrayLength: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto e = access_chain_internal(ops[2], &ops[3], length - 3, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr); + set(id, join(type_to_glsl(get(result_type)), "(", e, ".length())"), result_type, + true); + break; + } + + // Function calls + case OpFunctionCall: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t func = ops[2]; + const auto *arg = &ops[3]; + length -= 3; + + auto &callee = get(func); + auto &return_type = get(callee.return_type); + bool pure = function_is_pure(callee); + + bool callee_has_out_variables = false; + bool emit_return_value_as_argument = false; + + // Invalidate out variables passed to functions since they can be OpStore'd to. + for (uint32_t i = 0; i < length; i++) + { + if (callee.arguments[i].write_count) + { + register_call_out_argument(arg[i]); + callee_has_out_variables = true; + } + + flush_variable_declaration(arg[i]); + } + + if (!return_type.array.empty() && !backend.can_return_array) + { + callee_has_out_variables = true; + emit_return_value_as_argument = true; + } + + if (!pure) + register_impure_function_call(); + + string funexpr; + SmallVector arglist; + funexpr += to_name(func) + "("; + + if (emit_return_value_as_argument) + { + statement(type_to_glsl(return_type), " ", to_name(id), type_to_array_glsl(return_type), ";"); + arglist.push_back(to_name(id)); + } + + for (uint32_t i = 0; i < length; i++) + { + // Do not pass in separate images or samplers if we're remapping + // to combined image samplers. + if (skip_argument(arg[i])) + continue; + + arglist.push_back(to_func_call_arg(callee.arguments[i], arg[i])); + } + + for (auto &combined : callee.combined_parameters) + { + auto image_id = combined.global_image ? combined.image_id : VariableID(arg[combined.image_id]); + auto sampler_id = combined.global_sampler ? combined.sampler_id : VariableID(arg[combined.sampler_id]); + arglist.push_back(to_combined_image_sampler(image_id, sampler_id)); + } + + append_global_func_args(callee, length, arglist); + + funexpr += merge(arglist); + funexpr += ")"; + + // Check for function call constraints. + check_function_call_constraints(arg, length); + + if (return_type.basetype != SPIRType::Void) + { + // If the function actually writes to an out variable, + // take the conservative route and do not forward. + // The problem is that we might not read the function + // result (and emit the function) before an out variable + // is read (common case when return value is ignored! + // In order to avoid start tracking invalid variables, + // just avoid the forwarding problem altogether. + bool forward = args_will_forward(id, arg, length, pure) && !callee_has_out_variables && pure && + (forced_temporaries.find(id) == end(forced_temporaries)); + + if (emit_return_value_as_argument) + { + statement(funexpr, ";"); + set(id, to_name(id), result_type, true); + } + else + emit_op(result_type, id, funexpr, forward); + + // Function calls are implicit loads from all variables in question. + // Set dependencies for them. + for (uint32_t i = 0; i < length; i++) + register_read(id, arg[i], forward); + + // If we're going to forward the temporary result, + // put dependencies on every variable that must not change. + if (forward) + register_global_read_dependencies(callee, id); + } + else + statement(funexpr, ";"); + + break; + } + + // Composite munging + case OpCompositeConstruct: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + const auto *const elems = &ops[2]; + length -= 2; + + bool forward = true; + for (uint32_t i = 0; i < length; i++) + forward = forward && should_forward(elems[i]); + + auto &out_type = get(result_type); + auto *in_type = length > 0 ? &expression_type(elems[0]) : nullptr; + + // Only splat if we have vector constructors. + // Arrays and structs must be initialized properly in full. + bool composite = !out_type.array.empty() || out_type.basetype == SPIRType::Struct; + + bool splat = false; + bool swizzle_splat = false; + + if (in_type) + { + splat = in_type->vecsize == 1 && in_type->columns == 1 && !composite && backend.use_constructor_splatting; + swizzle_splat = in_type->vecsize == 1 && in_type->columns == 1 && backend.can_swizzle_scalar; + + if (ir.ids[elems[0]].get_type() == TypeConstant && !type_is_floating_point(*in_type)) + { + // Cannot swizzle literal integers as a special case. + swizzle_splat = false; + } + } + + if (splat || swizzle_splat) + { + uint32_t input = elems[0]; + for (uint32_t i = 0; i < length; i++) + { + if (input != elems[i]) + { + splat = false; + swizzle_splat = false; + } + } + } + + if (out_type.basetype == SPIRType::Struct && !backend.can_declare_struct_inline) + forward = false; + if (!out_type.array.empty() && !backend.can_declare_arrays_inline) + forward = false; + if (type_is_empty(out_type) && !backend.supports_empty_struct) + forward = false; + + string constructor_op; + if (backend.use_initializer_list && composite) + { + bool needs_trailing_tracket = false; + // Only use this path if we are building composites. + // This path cannot be used for arithmetic. + if (backend.use_typed_initializer_list && out_type.basetype == SPIRType::Struct && out_type.array.empty()) + constructor_op += type_to_glsl_constructor(get(result_type)); + else if (backend.use_typed_initializer_list && backend.array_is_value_type && !out_type.array.empty()) + { + // MSL path. Array constructor is baked into type here, do not use _constructor variant. + constructor_op += type_to_glsl_constructor(get(result_type)) + "("; + needs_trailing_tracket = true; + } + constructor_op += "{ "; + + if (type_is_empty(out_type) && !backend.supports_empty_struct) + constructor_op += "0"; + else if (splat) + constructor_op += to_unpacked_expression(elems[0]); + else + constructor_op += build_composite_combiner(result_type, elems, length); + constructor_op += " }"; + if (needs_trailing_tracket) + constructor_op += ")"; + } + else if (swizzle_splat && !composite) + { + constructor_op = remap_swizzle(get(result_type), 1, to_unpacked_expression(elems[0])); + } + else + { + constructor_op = type_to_glsl_constructor(get(result_type)) + "("; + if (type_is_empty(out_type) && !backend.supports_empty_struct) + constructor_op += "0"; + else if (splat) + constructor_op += to_unpacked_expression(elems[0]); + else + constructor_op += build_composite_combiner(result_type, elems, length); + constructor_op += ")"; + } + + if (!constructor_op.empty()) + { + emit_op(result_type, id, constructor_op, forward); + for (uint32_t i = 0; i < length; i++) + inherit_expression_dependencies(id, elems[i]); + } + break; + } + + case OpVectorInsertDynamic: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t vec = ops[2]; + uint32_t comp = ops[3]; + uint32_t index = ops[4]; + + flush_variable_declaration(vec); + + // Make a copy, then use access chain to store the variable. + statement(declare_temporary(result_type, id), to_expression(vec), ";"); + set(id, to_name(id), result_type, true); + auto chain = access_chain_internal(id, &index, 1, 0, nullptr); + statement(chain, " = ", to_unpacked_expression(comp), ";"); + break; + } + + case OpVectorExtractDynamic: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto expr = access_chain_internal(ops[2], &ops[3], 1, 0, nullptr); + emit_op(result_type, id, expr, should_forward(ops[2])); + inherit_expression_dependencies(id, ops[2]); + inherit_expression_dependencies(id, ops[3]); + break; + } + + case OpCompositeExtract: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + length -= 3; + + auto &type = get(result_type); + + // We can only split the expression here if our expression is forwarded as a temporary. + bool allow_base_expression = forced_temporaries.find(id) == end(forced_temporaries); + + // Do not allow base expression for struct members. We risk doing "swizzle" optimizations in this case. + auto &composite_type = expression_type(ops[2]); + if (composite_type.basetype == SPIRType::Struct || !composite_type.array.empty()) + allow_base_expression = false; + + // Packed expressions or physical ID mapped expressions cannot be split up. + if (has_extended_decoration(ops[2], SPIRVCrossDecorationPhysicalTypePacked) || + has_extended_decoration(ops[2], SPIRVCrossDecorationPhysicalTypeID)) + allow_base_expression = false; + + // Cannot use base expression for row-major matrix row-extraction since we need to interleave access pattern + // into the base expression. + if (is_non_native_row_major_matrix(ops[2])) + allow_base_expression = false; + + AccessChainMeta meta; + SPIRExpression *e = nullptr; + + // Only apply this optimization if result is scalar. + if (allow_base_expression && should_forward(ops[2]) && type.vecsize == 1 && type.columns == 1 && length == 1) + { + // We want to split the access chain from the base. + // This is so we can later combine different CompositeExtract results + // with CompositeConstruct without emitting code like + // + // vec3 temp = texture(...).xyz + // vec4(temp.x, temp.y, temp.z, 1.0). + // + // when we actually wanted to emit this + // vec4(texture(...).xyz, 1.0). + // + // Including the base will prevent this and would trigger multiple reads + // from expression causing it to be forced to an actual temporary in GLSL. + auto expr = access_chain_internal(ops[2], &ops[3], length, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_CHAIN_ONLY_BIT, &meta); + e = &emit_op(result_type, id, expr, true, should_suppress_usage_tracking(ops[2])); + inherit_expression_dependencies(id, ops[2]); + e->base_expression = ops[2]; + } + else + { + auto expr = access_chain_internal(ops[2], &ops[3], length, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &meta); + e = &emit_op(result_type, id, expr, should_forward(ops[2]), should_suppress_usage_tracking(ops[2])); + inherit_expression_dependencies(id, ops[2]); + } + + // Pass through some meta information to the loaded expression. + // We can still end up loading a buffer type to a variable, then CompositeExtract from it + // instead of loading everything through an access chain. + e->need_transpose = meta.need_transpose; + if (meta.storage_is_packed) + set_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + if (meta.storage_physical_type != 0) + set_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID, meta.storage_physical_type); + if (meta.storage_is_invariant) + set_decoration(id, DecorationInvariant); + + break; + } + + case OpCompositeInsert: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t obj = ops[2]; + uint32_t composite = ops[3]; + const auto *elems = &ops[4]; + length -= 4; + + flush_variable_declaration(composite); + + // Make a copy, then use access chain to store the variable. + statement(declare_temporary(result_type, id), to_expression(composite), ";"); + set(id, to_name(id), result_type, true); + auto chain = access_chain_internal(id, elems, length, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr); + statement(chain, " = ", to_unpacked_expression(obj), ";"); + + break; + } + + case OpCopyMemory: + { + uint32_t lhs = ops[0]; + uint32_t rhs = ops[1]; + if (lhs != rhs) + { + flush_variable_declaration(lhs); + flush_variable_declaration(rhs); + statement(to_expression(lhs), " = ", to_unpacked_expression(rhs), ";"); + register_write(lhs); + } + break; + } + + case OpCopyLogical: + { + // This is used for copying object of different types, arrays and structs. + // We need to unroll the copy, element-by-element. + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t rhs = ops[2]; + + emit_uninitialized_temporary_expression(result_type, id); + emit_copy_logical_type(id, result_type, rhs, expression_type_id(rhs), {}); + break; + } + + case OpCopyObject: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t rhs = ops[2]; + bool pointer = get(result_type).pointer; + + auto *chain = maybe_get(rhs); + auto *imgsamp = maybe_get(rhs); + if (chain) + { + // Cannot lower to a SPIRExpression, just copy the object. + auto &e = set(id, *chain); + e.self = id; + } + else if (imgsamp) + { + // Cannot lower to a SPIRExpression, just copy the object. + // GLSL does not currently use this type and will never get here, but MSL does. + // Handled here instead of CompilerMSL for better integration and general handling, + // and in case GLSL or other subclasses require it in the future. + auto &e = set(id, *imgsamp); + e.self = id; + } + else if (expression_is_lvalue(rhs) && !pointer) + { + // Need a copy. + // For pointer types, we copy the pointer itself. + statement(declare_temporary(result_type, id), to_unpacked_expression(rhs), ";"); + set(id, to_name(id), result_type, true); + } + else + { + // RHS expression is immutable, so just forward it. + // Copying these things really make no sense, but + // seems to be allowed anyways. + auto &e = set(id, to_expression(rhs), result_type, true); + if (pointer) + { + auto *var = maybe_get_backing_variable(rhs); + e.loaded_from = var ? var->self : ID(0); + } + + // If we're copying an access chain, need to inherit the read expressions. + auto *rhs_expr = maybe_get(rhs); + if (rhs_expr) + { + e.implied_read_expressions = rhs_expr->implied_read_expressions; + e.expression_dependencies = rhs_expr->expression_dependencies; + } + } + break; + } + + case OpVectorShuffle: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t vec0 = ops[2]; + uint32_t vec1 = ops[3]; + const auto *elems = &ops[4]; + length -= 4; + + auto &type0 = expression_type(vec0); + + // If we have the undefined swizzle index -1, we need to swizzle in undefined data, + // or in our case, T(0). + bool shuffle = false; + for (uint32_t i = 0; i < length; i++) + if (elems[i] >= type0.vecsize || elems[i] == 0xffffffffu) + shuffle = true; + + // Cannot use swizzles with packed expressions, force shuffle path. + if (!shuffle && has_extended_decoration(vec0, SPIRVCrossDecorationPhysicalTypePacked)) + shuffle = true; + + string expr; + bool should_fwd, trivial_forward; + + if (shuffle) + { + should_fwd = should_forward(vec0) && should_forward(vec1); + trivial_forward = should_suppress_usage_tracking(vec0) && should_suppress_usage_tracking(vec1); + + // Constructor style and shuffling from two different vectors. + SmallVector args; + for (uint32_t i = 0; i < length; i++) + { + if (elems[i] == 0xffffffffu) + { + // Use a constant 0 here. + // We could use the first component or similar, but then we risk propagating + // a value we might not need, and bog down codegen. + SPIRConstant c; + c.constant_type = type0.parent_type; + assert(type0.parent_type != ID(0)); + args.push_back(constant_expression(c)); + } + else if (elems[i] >= type0.vecsize) + args.push_back(to_extract_component_expression(vec1, elems[i] - type0.vecsize)); + else + args.push_back(to_extract_component_expression(vec0, elems[i])); + } + expr += join(type_to_glsl_constructor(get(result_type)), "(", merge(args), ")"); + } + else + { + should_fwd = should_forward(vec0); + trivial_forward = should_suppress_usage_tracking(vec0); + + // We only source from first vector, so can use swizzle. + // If the vector is packed, unpack it before applying a swizzle (needed for MSL) + expr += to_enclosed_unpacked_expression(vec0); + expr += "."; + for (uint32_t i = 0; i < length; i++) + { + assert(elems[i] != 0xffffffffu); + expr += index_to_swizzle(elems[i]); + } + + if (backend.swizzle_is_function && length > 1) + expr += "()"; + } + + // A shuffle is trivial in that it doesn't actually *do* anything. + // We inherit the forwardedness from our arguments to avoid flushing out to temporaries when it's not really needed. + + emit_op(result_type, id, expr, should_fwd, trivial_forward); + + inherit_expression_dependencies(id, vec0); + if (vec0 != vec1) + inherit_expression_dependencies(id, vec1); + break; + } + + // ALU + case OpIsNan: + GLSL_UFOP(isnan); + break; + + case OpIsInf: + GLSL_UFOP(isinf); + break; + + case OpSNegate: + case OpFNegate: + GLSL_UOP(-); + break; + + case OpIAdd: + { + // For simple arith ops, prefer the output type if there's a mismatch to avoid extra bitcasts. + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(+, type); + break; + } + + case OpFAdd: + GLSL_BOP(+); + break; + + case OpISub: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(-, type); + break; + } + + case OpFSub: + GLSL_BOP(-); + break; + + case OpIMul: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(*, type); + break; + } + + case OpVectorTimesMatrix: + case OpMatrixTimesVector: + { + // If the matrix needs transpose, just flip the multiply order. + auto *e = maybe_get(ops[opcode == OpMatrixTimesVector ? 2 : 3]); + if (e && e->need_transpose) + { + e->need_transpose = false; + string expr; + + if (opcode == OpMatrixTimesVector) + expr = join(to_enclosed_unpacked_expression(ops[3]), " * ", + enclose_expression(to_unpacked_row_major_matrix_expression(ops[2]))); + else + expr = join(enclose_expression(to_unpacked_row_major_matrix_expression(ops[3])), " * ", + to_enclosed_unpacked_expression(ops[2])); + + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + emit_op(ops[0], ops[1], expr, forward); + e->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + GLSL_BOP(*); + break; + } + + case OpMatrixTimesMatrix: + { + auto *a = maybe_get(ops[2]); + auto *b = maybe_get(ops[3]); + + // If both matrices need transpose, we can multiply in flipped order and tag the expression as transposed. + // a^T * b^T = (b * a)^T. + if (a && b && a->need_transpose && b->need_transpose) + { + a->need_transpose = false; + b->need_transpose = false; + auto expr = join(enclose_expression(to_unpacked_row_major_matrix_expression(ops[3])), " * ", + enclose_expression(to_unpacked_row_major_matrix_expression(ops[2]))); + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + auto &e = emit_op(ops[0], ops[1], expr, forward); + e.need_transpose = true; + a->need_transpose = true; + b->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + GLSL_BOP(*); + + break; + } + + case OpFMul: + case OpMatrixTimesScalar: + case OpVectorTimesScalar: + GLSL_BOP(*); + break; + + case OpOuterProduct: + GLSL_BFOP(outerProduct); + break; + + case OpDot: + GLSL_BFOP(dot); + break; + + case OpTranspose: + if (options.version < 120) // Matches GLSL 1.10 / ESSL 1.00 + { + // transpose() is not available, so instead, flip need_transpose, + // which can later be turned into an emulated transpose op by + // convert_row_major_matrix(), if necessary. + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t input = ops[2]; + + // Force need_transpose to false temporarily to prevent + // to_expression() from doing the transpose. + bool need_transpose = false; + auto *input_e = maybe_get(input); + if (input_e) + swap(need_transpose, input_e->need_transpose); + + bool forward = should_forward(input); + auto &e = emit_op(result_type, result_id, to_expression(input), forward); + e.need_transpose = !need_transpose; + + // Restore the old need_transpose flag. + if (input_e) + input_e->need_transpose = need_transpose; + } + else + GLSL_UFOP(transpose); + break; + + case OpSRem: + { + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + + // Needs special handling. + bool forward = should_forward(op0) && should_forward(op1); + auto expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", "(", + to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), ")"); + + emit_op(result_type, result_id, expr, forward); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + break; + } + + case OpSDiv: + GLSL_BOP_CAST(/, int_type); + break; + + case OpUDiv: + GLSL_BOP_CAST(/, uint_type); + break; + + case OpIAddCarry: + case OpISubBorrow: + { + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("Extended arithmetic is only available from ESSL 310."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("Extended arithmetic is only available from GLSL 400."); + + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, result_id); + const char *op = opcode == OpIAddCarry ? "uaddCarry" : "usubBorrow"; + + statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", op, "(", to_expression(op0), ", ", + to_expression(op1), ", ", to_expression(result_id), ".", to_member_name(type, 1), ");"); + break; + } + + case OpUMulExtended: + case OpSMulExtended: + { + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("Extended arithmetic is only available from ESSL 310."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("Extended arithmetic is only available from GLSL 4000."); + + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, result_id); + const char *op = opcode == OpUMulExtended ? "umulExtended" : "imulExtended"; + + statement(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(result_id), ".", + to_member_name(type, 1), ", ", to_expression(result_id), ".", to_member_name(type, 0), ");"); + break; + } + + case OpFDiv: + GLSL_BOP(/); + break; + + case OpShiftRightLogical: + GLSL_BOP_CAST(>>, uint_type); + break; + + case OpShiftRightArithmetic: + GLSL_BOP_CAST(>>, int_type); + break; + + case OpShiftLeftLogical: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(<<, type); + break; + } + + case OpBitwiseOr: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(|, type); + break; + } + + case OpBitwiseXor: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(^, type); + break; + } + + case OpBitwiseAnd: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(&, type); + break; + } + + case OpNot: + GLSL_UOP(~); + break; + + case OpUMod: + GLSL_BOP_CAST(%, uint_type); + break; + + case OpSMod: + GLSL_BOP_CAST(%, int_type); + break; + + case OpFMod: + GLSL_BFOP(mod); + break; + + case OpFRem: + { + if (is_legacy()) + SPIRV_CROSS_THROW("OpFRem requires trunc() and is only supported on non-legacy targets. A workaround is " + "needed for legacy."); + + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + + // Needs special handling. + bool forward = should_forward(op0) && should_forward(op1); + auto expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", "trunc(", + to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), ")"); + + emit_op(result_type, result_id, expr, forward); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + break; + } + + // Relational + case OpAny: + GLSL_UFOP(any); + break; + + case OpAll: + GLSL_UFOP(all); + break; + + case OpSelect: + emit_mix_op(ops[0], ops[1], ops[4], ops[3], ops[2]); + break; + + case OpLogicalOr: + { + // No vector variant in GLSL for logical OR. + auto result_type = ops[0]; + auto id = ops[1]; + auto &type = get(result_type); + + if (type.vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "||", false, SPIRType::Unknown); + else + GLSL_BOP(||); + break; + } + + case OpLogicalAnd: + { + // No vector variant in GLSL for logical AND. + auto result_type = ops[0]; + auto id = ops[1]; + auto &type = get(result_type); + + if (type.vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "&&", false, SPIRType::Unknown); + else + GLSL_BOP(&&); + break; + } + + case OpLogicalNot: + { + auto &type = get(ops[0]); + if (type.vecsize > 1) + GLSL_UFOP(not ); + else + GLSL_UOP(!); + break; + } + + case OpIEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(equal, int_type); + else + GLSL_BOP_CAST(==, int_type); + break; + } + + case OpLogicalEqual: + case OpFOrdEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(equal); + else + GLSL_BOP(==); + break; + } + + case OpINotEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(notEqual, int_type); + else + GLSL_BOP_CAST(!=, int_type); + break; + } + + case OpLogicalNotEqual: + case OpFOrdNotEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(notEqual); + else + GLSL_BOP(!=); + break; + } + + case OpUGreaterThan: + case OpSGreaterThan: + { + auto type = opcode == OpUGreaterThan ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(greaterThan, type); + else + GLSL_BOP_CAST(>, type); + break; + } + + case OpFOrdGreaterThan: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(greaterThan); + else + GLSL_BOP(>); + break; + } + + case OpUGreaterThanEqual: + case OpSGreaterThanEqual: + { + auto type = opcode == OpUGreaterThanEqual ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(greaterThanEqual, type); + else + GLSL_BOP_CAST(>=, type); + break; + } + + case OpFOrdGreaterThanEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(greaterThanEqual); + else + GLSL_BOP(>=); + break; + } + + case OpULessThan: + case OpSLessThan: + { + auto type = opcode == OpULessThan ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(lessThan, type); + else + GLSL_BOP_CAST(<, type); + break; + } + + case OpFOrdLessThan: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(lessThan); + else + GLSL_BOP(<); + break; + } + + case OpULessThanEqual: + case OpSLessThanEqual: + { + auto type = opcode == OpULessThanEqual ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(lessThanEqual, type); + else + GLSL_BOP_CAST(<=, type); + break; + } + + case OpFOrdLessThanEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(lessThanEqual); + else + GLSL_BOP(<=); + break; + } + + // Conversion + case OpSConvert: + case OpConvertSToF: + case OpUConvert: + case OpConvertUToF: + { + auto input_type = opcode == OpSConvert || opcode == OpConvertSToF ? int_type : uint_type; + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto &type = get(result_type); + auto &arg_type = expression_type(ops[2]); + auto func = type_to_glsl_constructor(type); + + if (arg_type.width < type.width || type_is_floating_point(type)) + emit_unary_func_op_cast(result_type, id, ops[2], func.c_str(), input_type, type.basetype); + else + emit_unary_func_op(result_type, id, ops[2], func.c_str()); + break; + } + + case OpConvertFToU: + case OpConvertFToS: + { + // Cast to expected arithmetic type, then potentially bitcast away to desired signedness. + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto &type = get(result_type); + auto expected_type = type; + auto &float_type = expression_type(ops[2]); + expected_type.basetype = + opcode == OpConvertFToS ? to_signed_basetype(type.width) : to_unsigned_basetype(type.width); + + auto func = type_to_glsl_constructor(expected_type); + emit_unary_func_op_cast(result_type, id, ops[2], func.c_str(), float_type.basetype, expected_type.basetype); + break; + } + + case OpFConvert: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto func = type_to_glsl_constructor(get(result_type)); + emit_unary_func_op(result_type, id, ops[2], func.c_str()); + break; + } + + case OpBitcast: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t arg = ops[2]; + + if (!emit_complex_bitcast(result_type, id, arg)) + { + auto op = bitcast_glsl_op(get(result_type), expression_type(arg)); + emit_unary_func_op(result_type, id, arg, op.c_str()); + } + break; + } + + case OpQuantizeToF16: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t arg = ops[2]; + + string op; + auto &type = get(result_type); + + switch (type.vecsize) + { + case 1: + op = join("unpackHalf2x16(packHalf2x16(vec2(", to_expression(arg), "))).x"); + break; + case 2: + op = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), "))"); + break; + case 3: + { + auto op0 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".xy))"); + auto op1 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".zz)).x"); + op = join("vec3(", op0, ", ", op1, ")"); + break; + } + case 4: + { + auto op0 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".xy))"); + auto op1 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".zw))"); + op = join("vec4(", op0, ", ", op1, ")"); + break; + } + default: + SPIRV_CROSS_THROW("Illegal argument to OpQuantizeToF16."); + } + + emit_op(result_type, id, op, should_forward(arg)); + inherit_expression_dependencies(id, arg); + break; + } + + // Derivatives + case OpDPdx: + GLSL_UFOP(dFdx); + if (is_legacy_es()) + require_extension_internal("GL_OES_standard_derivatives"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdy: + GLSL_UFOP(dFdy); + if (is_legacy_es()) + require_extension_internal("GL_OES_standard_derivatives"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdxFine: + GLSL_UFOP(dFdxFine); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdyFine: + GLSL_UFOP(dFdyFine); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdxCoarse: + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + GLSL_UFOP(dFdxCoarse); + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdyCoarse: + GLSL_UFOP(dFdyCoarse); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidth: + GLSL_UFOP(fwidth); + if (is_legacy_es()) + require_extension_internal("GL_OES_standard_derivatives"); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidthCoarse: + GLSL_UFOP(fwidthCoarse); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidthFine: + GLSL_UFOP(fwidthFine); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + // Bitfield + case OpBitFieldInsert: + { + emit_bitfield_insert_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], "bitfieldInsert", SPIRType::Int); + break; + } + + case OpBitFieldSExtract: + { + emit_trinary_func_op_bitextract(ops[0], ops[1], ops[2], ops[3], ops[4], "bitfieldExtract", int_type, int_type, + SPIRType::Int, SPIRType::Int); + break; + } + + case OpBitFieldUExtract: + { + emit_trinary_func_op_bitextract(ops[0], ops[1], ops[2], ops[3], ops[4], "bitfieldExtract", uint_type, uint_type, + SPIRType::Int, SPIRType::Int); + break; + } + + case OpBitReverse: + // BitReverse does not have issues with sign since result type must match input type. + GLSL_UFOP(bitfieldReverse); + break; + + case OpBitCount: + { + auto basetype = expression_type(ops[2]).basetype; + emit_unary_func_op_cast(ops[0], ops[1], ops[2], "bitCount", basetype, int_type); + break; + } + + // Atomics + case OpAtomicExchange: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + // Ignore semantics for now, probably only relevant to CL. + uint32_t val = ops[5]; + const char *op = check_atomic_image(ptr) ? "imageAtomicExchange" : "atomicExchange"; + forced_temporaries.insert(id); + emit_binary_func_op(result_type, id, ptr, val, op); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicCompareExchange: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t val = ops[6]; + uint32_t comp = ops[7]; + const char *op = check_atomic_image(ptr) ? "imageAtomicCompSwap" : "atomicCompSwap"; + + forced_temporaries.insert(id); + emit_trinary_func_op(result_type, id, ptr, comp, val, op); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicLoad: + { + // In plain GLSL, we have no atomic loads, so emulate this by fetch adding by 0 and hope compiler figures it out. + // Alternatively, we could rely on KHR_memory_model, but that's not very helpful for GL. + auto &type = expression_type(ops[2]); + forced_temporaries.insert(ops[1]); + bool atomic_image = check_atomic_image(ops[2]); + bool unsigned_type = (type.basetype == SPIRType::UInt) || + (atomic_image && get(type.image.type).basetype == SPIRType::UInt); + const char *op = atomic_image ? "imageAtomicAdd" : "atomicAdd"; + const char *increment = unsigned_type ? "0u" : "0"; + emit_op(ops[0], ops[1], join(op, "(", to_expression(ops[2]), ", ", increment, ")"), false); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicStore: + { + // In plain GLSL, we have no atomic stores, so emulate this with an atomic exchange where we don't consume the result. + // Alternatively, we could rely on KHR_memory_model, but that's not very helpful for GL. + uint32_t ptr = ops[0]; + // Ignore semantics for now, probably only relevant to CL. + uint32_t val = ops[3]; + const char *op = check_atomic_image(ptr) ? "imageAtomicExchange" : "atomicExchange"; + statement(op, "(", to_expression(ptr), ", ", to_expression(val), ");"); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicIIncrement: + case OpAtomicIDecrement: + { + forced_temporaries.insert(ops[1]); + auto &type = expression_type(ops[2]); + if (type.storage == StorageClassAtomicCounter) + { + // Legacy GLSL stuff, not sure if this is relevant to support. + if (opcode == OpAtomicIIncrement) + GLSL_UFOP(atomicCounterIncrement); + else + GLSL_UFOP(atomicCounterDecrement); + } + else + { + bool atomic_image = check_atomic_image(ops[2]); + bool unsigned_type = (type.basetype == SPIRType::UInt) || + (atomic_image && get(type.image.type).basetype == SPIRType::UInt); + const char *op = atomic_image ? "imageAtomicAdd" : "atomicAdd"; + + const char *increment = nullptr; + if (opcode == OpAtomicIIncrement && unsigned_type) + increment = "1u"; + else if (opcode == OpAtomicIIncrement) + increment = "1"; + else if (unsigned_type) + increment = "uint(-1)"; + else + increment = "-1"; + + emit_op(ops[0], ops[1], join(op, "(", to_expression(ops[2]), ", ", increment, ")"), false); + } + + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicIAdd: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicAdd" : "atomicAdd"; + forced_temporaries.insert(ops[1]); + emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicISub: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicAdd" : "atomicAdd"; + forced_temporaries.insert(ops[1]); + auto expr = join(op, "(", to_expression(ops[2]), ", -", to_enclosed_expression(ops[5]), ")"); + emit_op(ops[0], ops[1], expr, should_forward(ops[2]) && should_forward(ops[5])); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicSMin: + case OpAtomicUMin: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicMin" : "atomicMin"; + forced_temporaries.insert(ops[1]); + emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicSMax: + case OpAtomicUMax: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicMax" : "atomicMax"; + forced_temporaries.insert(ops[1]); + emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicAnd: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicAnd" : "atomicAnd"; + forced_temporaries.insert(ops[1]); + emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicOr: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicOr" : "atomicOr"; + forced_temporaries.insert(ops[1]); + emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op); + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicXor: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicXor" : "atomicXor"; + forced_temporaries.insert(ops[1]); + emit_binary_func_op(ops[0], ops[1], ops[2], ops[5], op); + flush_all_atomic_capable_variables(); + break; + } + + // Geometry shaders + case OpEmitVertex: + statement("EmitVertex();"); + break; + + case OpEndPrimitive: + statement("EndPrimitive();"); + break; + + case OpEmitStreamVertex: + { + if (options.es) + SPIRV_CROSS_THROW("Multi-stream geometry shaders not supported in ES."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("Multi-stream geometry shaders only supported in GLSL 400."); + + auto stream_expr = to_expression(ops[0]); + if (expression_type(ops[0]).basetype != SPIRType::Int) + stream_expr = join("int(", stream_expr, ")"); + statement("EmitStreamVertex(", stream_expr, ");"); + break; + } + + case OpEndStreamPrimitive: + { + if (options.es) + SPIRV_CROSS_THROW("Multi-stream geometry shaders not supported in ES."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("Multi-stream geometry shaders only supported in GLSL 400."); + + auto stream_expr = to_expression(ops[0]); + if (expression_type(ops[0]).basetype != SPIRType::Int) + stream_expr = join("int(", stream_expr, ")"); + statement("EndStreamPrimitive(", stream_expr, ");"); + break; + } + + // Textures + case OpImageSampleExplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageFetch: + case OpImageGather: + case OpImageDrefGather: + // Gets a bit hairy, so move this to a separate instruction. + emit_texture_op(instruction, false); + break; + + case OpImageSparseSampleExplicitLod: + case OpImageSparseSampleProjExplicitLod: + case OpImageSparseSampleDrefExplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + case OpImageSparseSampleImplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseFetch: + case OpImageSparseGather: + case OpImageSparseDrefGather: + // Gets a bit hairy, so move this to a separate instruction. + emit_texture_op(instruction, true); + break; + + case OpImageSparseTexelsResident: + if (options.es) + SPIRV_CROSS_THROW("Sparse feedback is not supported in GLSL."); + require_extension_internal("GL_ARB_sparse_texture2"); + emit_unary_func_op_cast(ops[0], ops[1], ops[2], "sparseTexelsResidentARB", int_type, SPIRType::Boolean); + break; + + case OpImage: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + // Suppress usage tracking. + auto &e = emit_op(result_type, id, to_expression(ops[2]), true, true); + + // When using the image, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(ops[2]); + e.loaded_from = var ? var->self : ID(0); + break; + } + + case OpImageQueryLod: + { + if (!options.es && options.version < 400) + { + require_extension_internal("GL_ARB_texture_query_lod"); + // For some reason, the ARB spec is all-caps. + GLSL_BFOP(textureQueryLOD); + } + else if (options.es) + SPIRV_CROSS_THROW("textureQueryLod not supported in ES profile."); + else + GLSL_BFOP(textureQueryLod); + register_control_dependent_expression(ops[1]); + break; + } + + case OpImageQueryLevels: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_texture_query_levels"); + if (options.es) + SPIRV_CROSS_THROW("textureQueryLevels not supported in ES profile."); + + auto expr = join("textureQueryLevels(", convert_separate_image_to_expression(ops[2]), ")"); + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::Int, expr); + emit_op(result_type, id, expr, true); + break; + } + + case OpImageQuerySamples: + { + auto &type = expression_type(ops[2]); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + string expr; + if (type.image.sampled == 2) + expr = join("imageSamples(", to_expression(ops[2]), ")"); + else + expr = join("textureSamples(", convert_separate_image_to_expression(ops[2]), ")"); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::Int, expr); + emit_op(result_type, id, expr, true); + break; + } + + case OpSampledImage: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_sampled_image_op(result_type, id, ops[2], ops[3]); + inherit_expression_dependencies(id, ops[2]); + inherit_expression_dependencies(id, ops[3]); + break; + } + + case OpImageQuerySizeLod: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto expr = join("textureSize(", convert_separate_image_to_expression(ops[2]), ", ", + bitcast_expression(SPIRType::Int, ops[3]), ")"); + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::Int, expr); + emit_op(result_type, id, expr, true); + break; + } + + // Image load/store + case OpImageRead: + case OpImageSparseRead: + { + // We added Nonreadable speculatively to the OpImage variable due to glslangValidator + // not adding the proper qualifiers. + // If it turns out we need to read the image after all, remove the qualifier and recompile. + auto *var = maybe_get_backing_variable(ops[2]); + if (var) + { + auto &flags = ir.meta[var->self].decoration.decoration_flags; + if (flags.get(DecorationNonReadable)) + { + flags.clear(DecorationNonReadable); + force_recompile(); + } + } + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + bool pure; + string imgexpr; + auto &type = expression_type(ops[2]); + + if (var && var->remapped_variable) // Remapped input, just read as-is without any op-code + { + if (type.image.ms) + SPIRV_CROSS_THROW("Trying to remap multisampled image to variable, this is not possible."); + + auto itr = + find_if(begin(pls_inputs), end(pls_inputs), [var](const PlsRemap &pls) { return pls.id == var->self; }); + + if (itr == end(pls_inputs)) + { + // For non-PLS inputs, we rely on subpass type remapping information to get it right + // since ImageRead always returns 4-component vectors and the backing type is opaque. + if (!var->remapped_components) + SPIRV_CROSS_THROW("subpassInput was remapped, but remap_components is not set correctly."); + imgexpr = remap_swizzle(get(result_type), var->remapped_components, to_expression(ops[2])); + } + else + { + // PLS input could have different number of components than what the SPIR expects, swizzle to + // the appropriate vector size. + uint32_t components = pls_format_to_components(itr->format); + imgexpr = remap_swizzle(get(result_type), components, to_expression(ops[2])); + } + pure = true; + } + else if (type.image.dim == DimSubpassData) + { + if (var && subpass_input_is_framebuffer_fetch(var->self)) + { + imgexpr = to_expression(var->self); + } + else if (options.vulkan_semantics) + { + // With Vulkan semantics, use the proper Vulkan GLSL construct. + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected " + "operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = join("subpassLoad(", to_expression(ops[2]), ", ", to_expression(samples), ")"); + } + else + imgexpr = join("subpassLoad(", to_expression(ops[2]), ")"); + } + else + { + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected " + "operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), ", + to_expression(samples), ")"); + } + else + { + // Implement subpass loads via texture barrier style sampling. + imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)"); + } + } + imgexpr = remap_swizzle(get(result_type), 4, imgexpr); + pure = true; + } + else + { + bool sparse = opcode == OpImageSparseRead; + uint32_t sparse_code_id = 0; + uint32_t sparse_texel_id = 0; + if (sparse) + emit_sparse_feedback_temporaries(ops[0], ops[1], sparse_code_id, sparse_texel_id); + + // imageLoad only accepts int coords, not uint. + auto coord_expr = to_expression(ops[3]); + auto target_coord_type = expression_type(ops[3]); + target_coord_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(target_coord_type, expression_type(ops[3]).basetype, coord_expr); + + // Plain image load/store. + if (sparse) + { + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected " + "operand mask was used."); + + uint32_t samples = ops[5]; + statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_expression(ops[2]), ", ", + coord_expr, ", ", to_expression(samples), ", ", to_expression(sparse_texel_id), ");"); + } + else + { + statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_expression(ops[2]), ", ", + coord_expr, ", ", to_expression(sparse_texel_id), ");"); + } + imgexpr = join(type_to_glsl(get(result_type)), "(", to_expression(sparse_code_id), ", ", + to_expression(sparse_texel_id), ")"); + } + else + { + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected " + "operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = + join("imageLoad(", to_expression(ops[2]), ", ", coord_expr, ", ", to_expression(samples), ")"); + } + else + imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", coord_expr, ")"); + } + + if (!sparse) + imgexpr = remap_swizzle(get(result_type), 4, imgexpr); + pure = false; + } + + if (var && var->forwardable) + { + bool forward = forced_temporaries.find(id) == end(forced_temporaries); + auto &e = emit_op(result_type, id, imgexpr, forward); + + // We only need to track dependencies if we're reading from image load/store. + if (!pure) + { + e.loaded_from = var->self; + if (forward) + var->dependees.push_back(id); + } + } + else + emit_op(result_type, id, imgexpr, false); + + inherit_expression_dependencies(id, ops[2]); + if (type.image.ms) + inherit_expression_dependencies(id, ops[5]); + break; + } + + case OpImageTexelPointer: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto coord_expr = to_expression(ops[3]); + auto target_coord_type = expression_type(ops[3]); + target_coord_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(target_coord_type, expression_type(ops[3]).basetype, coord_expr); + + auto expr = join(to_expression(ops[2]), ", ", coord_expr); + if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ops[2], DecorationNonUniformEXT)) + convert_non_uniform_expression(expression_type(ops[2]), expr); + + auto &e = set(id, expr, result_type, true); + + // When using the pointer, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(ops[2]); + e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, ops[3]); + break; + } + + case OpImageWrite: + { + // We added Nonwritable speculatively to the OpImage variable due to glslangValidator + // not adding the proper qualifiers. + // If it turns out we need to write to the image after all, remove the qualifier and recompile. + auto *var = maybe_get_backing_variable(ops[0]); + if (var) + { + auto &flags = ir.meta[var->self].decoration.decoration_flags; + if (flags.get(DecorationNonWritable)) + { + flags.clear(DecorationNonWritable); + force_recompile(); + } + } + + auto &type = expression_type(ops[0]); + auto &value_type = expression_type(ops[2]); + auto store_type = value_type; + store_type.vecsize = 4; + + // imageStore only accepts int coords, not uint. + auto coord_expr = to_expression(ops[1]); + auto target_coord_type = expression_type(ops[1]); + target_coord_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(target_coord_type, expression_type(ops[1]).basetype, coord_expr); + + if (type.image.ms) + { + uint32_t operands = ops[3]; + if (operands != ImageOperandsSampleMask || length != 5) + SPIRV_CROSS_THROW("Multisampled image used in OpImageWrite, but unexpected operand mask was used."); + uint32_t samples = ops[4]; + statement("imageStore(", to_expression(ops[0]), ", ", coord_expr, ", ", to_expression(samples), ", ", + remap_swizzle(store_type, value_type.vecsize, to_expression(ops[2])), ");"); + } + else + statement("imageStore(", to_expression(ops[0]), ", ", coord_expr, ", ", + remap_swizzle(store_type, value_type.vecsize, to_expression(ops[2])), ");"); + + if (var && variable_storage_is_aliased(*var)) + flush_all_aliased_variables(); + break; + } + + case OpImageQuerySize: + { + auto &type = expression_type(ops[2]); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (type.basetype == SPIRType::Image) + { + string expr; + if (type.image.sampled == 2) + { + // The size of an image is always constant. + expr = join("imageSize(", to_expression(ops[2]), ")"); + } + else + { + // This path is hit for samplerBuffers and multisampled images which do not have LOD. + expr = join("textureSize(", convert_separate_image_to_expression(ops[2]), ")"); + } + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::Int, expr); + emit_op(result_type, id, expr, true); + } + else + SPIRV_CROSS_THROW("Invalid type for OpImageQuerySize."); + break; + } + + // Compute + case OpControlBarrier: + case OpMemoryBarrier: + { + uint32_t execution_scope = 0; + uint32_t memory; + uint32_t semantics; + + if (opcode == OpMemoryBarrier) + { + memory = evaluate_constant_u32(ops[0]); + semantics = evaluate_constant_u32(ops[1]); + } + else + { + execution_scope = evaluate_constant_u32(ops[0]); + memory = evaluate_constant_u32(ops[1]); + semantics = evaluate_constant_u32(ops[2]); + } + + if (execution_scope == ScopeSubgroup || memory == ScopeSubgroup) + { + // OpControlBarrier with ScopeSubgroup is subgroupBarrier() + if (opcode != OpControlBarrier) + { + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMemBarrier); + } + else + { + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBarrier); + } + } + + if (execution_scope != ScopeSubgroup && get_entry_point().model == ExecutionModelTessellationControl) + { + // Control shaders only have barriers, and it implies memory barriers. + if (opcode == OpControlBarrier) + statement("barrier();"); + break; + } + + // We only care about these flags, acquire/release and friends are not relevant to GLSL. + semantics = mask_relevant_memory_semantics(semantics); + + if (opcode == OpMemoryBarrier) + { + // If we are a memory barrier, and the next instruction is a control barrier, check if that memory barrier + // does what we need, so we avoid redundant barriers. + const Instruction *next = get_next_instruction_in_block(instruction); + if (next && next->op == OpControlBarrier) + { + auto *next_ops = stream(*next); + uint32_t next_memory = evaluate_constant_u32(next_ops[1]); + uint32_t next_semantics = evaluate_constant_u32(next_ops[2]); + next_semantics = mask_relevant_memory_semantics(next_semantics); + + bool memory_scope_covered = false; + if (next_memory == memory) + memory_scope_covered = true; + else if (next_semantics == MemorySemanticsWorkgroupMemoryMask) + { + // If we only care about workgroup memory, either Device or Workgroup scope is fine, + // scope does not have to match. + if ((next_memory == ScopeDevice || next_memory == ScopeWorkgroup) && + (memory == ScopeDevice || memory == ScopeWorkgroup)) + { + memory_scope_covered = true; + } + } + else if (memory == ScopeWorkgroup && next_memory == ScopeDevice) + { + // The control barrier has device scope, but the memory barrier just has workgroup scope. + memory_scope_covered = true; + } + + // If we have the same memory scope, and all memory types are covered, we're good. + if (memory_scope_covered && (semantics & next_semantics) == semantics) + break; + } + } + + // We are synchronizing some memory or syncing execution, + // so we cannot forward any loads beyond the memory barrier. + if (semantics || opcode == OpControlBarrier) + { + assert(current_emitting_block); + flush_control_dependent_expressions(current_emitting_block->self); + flush_all_active_variables(); + } + + if (memory == ScopeWorkgroup) // Only need to consider memory within a group + { + if (semantics == MemorySemanticsWorkgroupMemoryMask) + { + // OpControlBarrier implies a memory barrier for shared memory as well. + bool implies_shared_barrier = opcode == OpControlBarrier && execution_scope == ScopeWorkgroup; + if (!implies_shared_barrier) + statement("memoryBarrierShared();"); + } + else if (semantics != 0) + statement("groupMemoryBarrier();"); + } + else if (memory == ScopeSubgroup) + { + const uint32_t all_barriers = + MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask | MemorySemanticsImageMemoryMask; + + if (semantics & (MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask)) + { + // These are not relevant for GLSL, but assume it means memoryBarrier(). + // memoryBarrier() does everything, so no need to test anything else. + statement("subgroupMemoryBarrier();"); + } + else if ((semantics & all_barriers) == all_barriers) + { + // Short-hand instead of emitting 3 barriers. + statement("subgroupMemoryBarrier();"); + } + else + { + // Pick out individual barriers. + if (semantics & MemorySemanticsWorkgroupMemoryMask) + statement("subgroupMemoryBarrierShared();"); + if (semantics & MemorySemanticsUniformMemoryMask) + statement("subgroupMemoryBarrierBuffer();"); + if (semantics & MemorySemanticsImageMemoryMask) + statement("subgroupMemoryBarrierImage();"); + } + } + else + { + const uint32_t all_barriers = + MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask | MemorySemanticsImageMemoryMask; + + if (semantics & (MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask)) + { + // These are not relevant for GLSL, but assume it means memoryBarrier(). + // memoryBarrier() does everything, so no need to test anything else. + statement("memoryBarrier();"); + } + else if ((semantics & all_barriers) == all_barriers) + { + // Short-hand instead of emitting 4 barriers. + statement("memoryBarrier();"); + } + else + { + // Pick out individual barriers. + if (semantics & MemorySemanticsWorkgroupMemoryMask) + statement("memoryBarrierShared();"); + if (semantics & MemorySemanticsUniformMemoryMask) + statement("memoryBarrierBuffer();"); + if (semantics & MemorySemanticsImageMemoryMask) + statement("memoryBarrierImage();"); + } + } + + if (opcode == OpControlBarrier) + { + if (execution_scope == ScopeSubgroup) + statement("subgroupBarrier();"); + else + statement("barrier();"); + } + break; + } + + case OpExtInst: + { + uint32_t extension_set = ops[2]; + + if (get(extension_set).ext == SPIRExtension::GLSL) + { + emit_glsl_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (get(extension_set).ext == SPIRExtension::SPV_AMD_shader_ballot) + { + emit_spv_amd_shader_ballot_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (get(extension_set).ext == SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter) + { + emit_spv_amd_shader_explicit_vertex_parameter_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (get(extension_set).ext == SPIRExtension::SPV_AMD_shader_trinary_minmax) + { + emit_spv_amd_shader_trinary_minmax_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (get(extension_set).ext == SPIRExtension::SPV_AMD_gcn_shader) + { + emit_spv_amd_gcn_shader_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (get(extension_set).ext == SPIRExtension::SPV_debug_info) + { + break; // Ignore SPIR-V debug information extended instructions. + } + else + { + statement("// unimplemented ext op ", instruction.op); + break; + } + + break; + } + + // Legacy sub-group stuff ... + case OpSubgroupBallotKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + string expr; + expr = join("uvec4(unpackUint2x32(ballotARB(" + to_expression(ops[2]) + ")), 0u, 0u)"); + emit_op(result_type, id, expr, should_forward(ops[2])); + + require_extension_internal("GL_ARB_shader_ballot"); + inherit_expression_dependencies(id, ops[2]); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupFirstInvocationKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[2], "readFirstInvocationARB"); + + require_extension_internal("GL_ARB_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupReadInvocationKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_binary_func_op(result_type, id, ops[2], ops[3], "readInvocationARB"); + + require_extension_internal("GL_ARB_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupAllKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[2], "allInvocationsARB"); + + require_extension_internal("GL_ARB_shader_group_vote"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupAnyKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[2], "anyInvocationARB"); + + require_extension_internal("GL_ARB_shader_group_vote"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupAllEqualKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[2], "allInvocationsEqualARB"); + + require_extension_internal("GL_ARB_shader_group_vote"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpGroupIAddNonUniformAMD: + case OpGroupFAddNonUniformAMD: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[4], "addInvocationsNonUniformAMD"); + + require_extension_internal("GL_AMD_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpGroupFMinNonUniformAMD: + case OpGroupUMinNonUniformAMD: + case OpGroupSMinNonUniformAMD: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[4], "minInvocationsNonUniformAMD"); + + require_extension_internal("GL_AMD_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpGroupFMaxNonUniformAMD: + case OpGroupUMaxNonUniformAMD: + case OpGroupSMaxNonUniformAMD: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[4], "maxInvocationsNonUniformAMD"); + + require_extension_internal("GL_AMD_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpFragmentMaskFetchAMD: + { + auto &type = expression_type(ops[2]); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (type.image.dim == spv::DimSubpassData) + { + emit_unary_func_op(result_type, id, ops[2], "fragmentMaskFetchAMD"); + } + else + { + emit_binary_func_op(result_type, id, ops[2], ops[3], "fragmentMaskFetchAMD"); + } + + require_extension_internal("GL_AMD_shader_fragment_mask"); + break; + } + + case OpFragmentFetchAMD: + { + auto &type = expression_type(ops[2]); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (type.image.dim == spv::DimSubpassData) + { + emit_binary_func_op(result_type, id, ops[2], ops[4], "fragmentFetchAMD"); + } + else + { + emit_trinary_func_op(result_type, id, ops[2], ops[3], ops[4], "fragmentFetchAMD"); + } + + require_extension_internal("GL_AMD_shader_fragment_mask"); + break; + } + + // Vulkan 1.1 sub-group stuff ... + case OpGroupNonUniformElect: + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformBroadcastFirst: + case OpGroupNonUniformBallot: + case OpGroupNonUniformInverseBallot: + case OpGroupNonUniformBallotBitExtract: + case OpGroupNonUniformBallotBitCount: + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + case OpGroupNonUniformAll: + case OpGroupNonUniformAny: + case OpGroupNonUniformAllEqual: + case OpGroupNonUniformFAdd: + case OpGroupNonUniformIAdd: + case OpGroupNonUniformFMul: + case OpGroupNonUniformIMul: + case OpGroupNonUniformFMin: + case OpGroupNonUniformFMax: + case OpGroupNonUniformSMin: + case OpGroupNonUniformSMax: + case OpGroupNonUniformUMin: + case OpGroupNonUniformUMax: + case OpGroupNonUniformBitwiseAnd: + case OpGroupNonUniformBitwiseOr: + case OpGroupNonUniformBitwiseXor: + case OpGroupNonUniformQuadSwap: + case OpGroupNonUniformQuadBroadcast: + emit_subgroup_op(instruction); + break; + + case OpFUnordEqual: + case OpFUnordNotEqual: + case OpFUnordLessThan: + case OpFUnordGreaterThan: + case OpFUnordLessThanEqual: + case OpFUnordGreaterThanEqual: + { + // GLSL doesn't specify if floating point comparisons are ordered or unordered, + // but glslang always emits ordered floating point compares for GLSL. + // To get unordered compares, we can test the opposite thing and invert the result. + // This way, we force true when there is any NaN present. + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + + string expr; + if (expression_type(op0).vecsize > 1) + { + const char *comp_op = nullptr; + switch (opcode) + { + case OpFUnordEqual: + comp_op = "notEqual"; + break; + + case OpFUnordNotEqual: + comp_op = "equal"; + break; + + case OpFUnordLessThan: + comp_op = "greaterThanEqual"; + break; + + case OpFUnordLessThanEqual: + comp_op = "greaterThan"; + break; + + case OpFUnordGreaterThan: + comp_op = "lessThanEqual"; + break; + + case OpFUnordGreaterThanEqual: + comp_op = "lessThan"; + break; + + default: + assert(0); + break; + } + + expr = join("not(", comp_op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), "))"); + } + else + { + const char *comp_op = nullptr; + switch (opcode) + { + case OpFUnordEqual: + comp_op = " != "; + break; + + case OpFUnordNotEqual: + comp_op = " == "; + break; + + case OpFUnordLessThan: + comp_op = " >= "; + break; + + case OpFUnordLessThanEqual: + comp_op = " > "; + break; + + case OpFUnordGreaterThan: + comp_op = " <= "; + break; + + case OpFUnordGreaterThanEqual: + comp_op = " < "; + break; + + default: + assert(0); + break; + } + + expr = join("!(", to_enclosed_unpacked_expression(op0), comp_op, to_enclosed_unpacked_expression(op1), ")"); + } + + emit_op(ops[0], ops[1], expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(ops[1], op0); + inherit_expression_dependencies(ops[1], op1); + break; + } + + case OpReportIntersectionNV: + statement("reportIntersectionNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpIgnoreIntersectionNV: + statement("ignoreIntersectionNV();"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpTerminateRayNV: + statement("terminateRayNV();"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpTraceNV: + statement("traceNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ", ", + to_expression(ops[3]), ", ", to_expression(ops[4]), ", ", to_expression(ops[5]), ", ", + to_expression(ops[6]), ", ", to_expression(ops[7]), ", ", to_expression(ops[8]), ", ", + to_expression(ops[9]), ", ", to_expression(ops[10]), ");"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpExecuteCallableNV: + statement("executeCallableNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + + case OpConvertUToPtr: + { + auto &type = get(ops[0]); + if (type.storage != StorageClassPhysicalStorageBufferEXT) + SPIRV_CROSS_THROW("Only StorageClassPhysicalStorageBufferEXT is supported by OpConvertUToPtr."); + + auto op = type_to_glsl(type); + emit_unary_func_op(ops[0], ops[1], ops[2], op.c_str()); + break; + } + + case OpConvertPtrToU: + { + auto &type = get(ops[0]); + auto &ptr_type = expression_type(ops[2]); + if (ptr_type.storage != StorageClassPhysicalStorageBufferEXT) + SPIRV_CROSS_THROW("Only StorageClassPhysicalStorageBufferEXT is supported by OpConvertPtrToU."); + + auto op = type_to_glsl(type); + emit_unary_func_op(ops[0], ops[1], ops[2], op.c_str()); + break; + } + + case OpUndef: + // Undefined value has been declared. + break; + + case OpLine: + { + emit_line_directive(ops[0], ops[1]); + break; + } + + case OpNoLine: + break; + + case OpDemoteToHelperInvocationEXT: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("GL_EXT_demote_to_helper_invocation is only supported in Vulkan GLSL."); + require_extension_internal("GL_EXT_demote_to_helper_invocation"); + statement(backend.demote_literal, ";"); + break; + + case OpIsHelperInvocationEXT: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("GL_EXT_demote_to_helper_invocation is only supported in Vulkan GLSL."); + require_extension_internal("GL_EXT_demote_to_helper_invocation"); + emit_op(ops[0], ops[1], "helperInvocationEXT()", false); + break; + + case OpBeginInvocationInterlockEXT: + // If the interlock is complex, we emit this elsewhere. + if (!interlocked_is_complex) + { + if (options.es) + statement("beginInvocationInterlockNV();"); + else + statement("beginInvocationInterlockARB();"); + + flush_all_active_variables(); + // Make sure forwarding doesn't propagate outside interlock region. + } + break; + + case OpEndInvocationInterlockEXT: + // If the interlock is complex, we emit this elsewhere. + if (!interlocked_is_complex) + { + if (options.es) + statement("endInvocationInterlockNV();"); + else + statement("endInvocationInterlockARB();"); + + flush_all_active_variables(); + // Make sure forwarding doesn't propagate outside interlock region. + } + break; + + default: + statement("// unimplemented op ", instruction.op); + break; + } +} + +// Appends function arguments, mapped from global variables, beyond the specified arg index. +// This is used when a function call uses fewer arguments than the function defines. +// This situation may occur if the function signature has been dynamically modified to +// extract global variables referenced from within the function, and convert them to +// function arguments. This is necessary for shader languages that do not support global +// access to shader input content from within a function (eg. Metal). Each additional +// function args uses the name of the global variable. Function nesting will modify the +// functions and function calls all the way up the nesting chain. +void CompilerGLSL::append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector &arglist) +{ + auto &args = func.arguments; + uint32_t arg_cnt = uint32_t(args.size()); + for (uint32_t arg_idx = index; arg_idx < arg_cnt; arg_idx++) + { + auto &arg = args[arg_idx]; + assert(arg.alias_global_variable); + + // If the underlying variable needs to be declared + // (ie. a local variable with deferred declaration), do so now. + uint32_t var_id = get(arg.id).basevariable; + if (var_id) + flush_variable_declaration(var_id); + + arglist.push_back(to_func_call_arg(arg, arg.id)); + } +} + +string CompilerGLSL::to_member_name(const SPIRType &type, uint32_t index) +{ + if (type.type_alias != TypeID(0) && + !has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + { + return to_member_name(get(type.type_alias), index); + } + + auto &memb = ir.meta[type.self].members; + if (index < memb.size() && !memb[index].alias.empty()) + return memb[index].alias; + else + return join("_m", index); +} + +string CompilerGLSL::to_member_reference(uint32_t, const SPIRType &type, uint32_t index, bool) +{ + return join(".", to_member_name(type, index)); +} + +string CompilerGLSL::to_multi_member_reference(const SPIRType &type, const SmallVector &indices) +{ + string ret; + auto *member_type = &type; + for (auto &index : indices) + { + ret += join(".", to_member_name(*member_type, index)); + member_type = &get(member_type->member_types[index]); + } + return ret; +} + +void CompilerGLSL::add_member_name(SPIRType &type, uint32_t index) +{ + auto &memb = ir.meta[type.self].members; + if (index < memb.size() && !memb[index].alias.empty()) + { + auto &name = memb[index].alias; + if (name.empty()) + return; + + ParsedIR::sanitize_identifier(name, true, true); + update_name_cache(type.member_name_cache, name); + } +} + +// Checks whether the ID is a row_major matrix that requires conversion before use +bool CompilerGLSL::is_non_native_row_major_matrix(uint32_t id) +{ + // Natively supported row-major matrices do not need to be converted. + // Legacy targets do not support row major. + if (backend.native_row_major_matrix && !is_legacy()) + return false; + + auto *e = maybe_get(id); + if (e) + return e->need_transpose; + else + return has_decoration(id, DecorationRowMajor); +} + +// Checks whether the member is a row_major matrix that requires conversion before use +bool CompilerGLSL::member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) +{ + // Natively supported row-major matrices do not need to be converted. + if (backend.native_row_major_matrix && !is_legacy()) + return false; + + // Non-matrix or column-major matrix types do not need to be converted. + if (!has_member_decoration(type.self, index, DecorationRowMajor)) + return false; + + // Only square row-major matrices can be converted at this time. + // Converting non-square matrices will require defining custom GLSL function that + // swaps matrix elements while retaining the original dimensional form of the matrix. + const auto mbr_type = get(type.member_types[index]); + if (mbr_type.columns != mbr_type.vecsize) + SPIRV_CROSS_THROW("Row-major matrices must be square on this platform."); + + return true; +} + +// Checks if we need to remap physical type IDs when declaring the type in a buffer. +bool CompilerGLSL::member_is_remapped_physical_type(const SPIRType &type, uint32_t index) const +{ + return has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypeID); +} + +// Checks whether the member is in packed data type, that might need to be unpacked. +bool CompilerGLSL::member_is_packed_physical_type(const SPIRType &type, uint32_t index) const +{ + return has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypePacked); +} + +// Wraps the expression string in a function call that converts the +// row_major matrix result of the expression to a column_major matrix. +// Base implementation uses the standard library transpose() function. +// Subclasses may override to use a different function. +string CompilerGLSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, uint32_t /* physical_type_id */, + bool /*is_packed*/) +{ + strip_enclosed_expression(exp_str); + if (!is_matrix(exp_type)) + { + auto column_index = exp_str.find_last_of('['); + if (column_index == string::npos) + return exp_str; + + auto column_expr = exp_str.substr(column_index); + exp_str.resize(column_index); + + auto transposed_expr = type_to_glsl_constructor(exp_type) + "("; + + // Loading a column from a row-major matrix. Unroll the load. + for (uint32_t c = 0; c < exp_type.vecsize; c++) + { + transposed_expr += join(exp_str, '[', c, ']', column_expr); + if (c + 1 < exp_type.vecsize) + transposed_expr += ", "; + } + + transposed_expr += ")"; + return transposed_expr; + } + else if (options.version < 120) + { + // GLSL 110, ES 100 do not have transpose(), so emulate it. Note that + // these GLSL versions do not support non-square matrices. + if (exp_type.vecsize == 2 && exp_type.columns == 2) + { + if (!requires_transpose_2x2) + { + requires_transpose_2x2 = true; + force_recompile(); + } + } + else if (exp_type.vecsize == 3 && exp_type.columns == 3) + { + if (!requires_transpose_3x3) + { + requires_transpose_3x3 = true; + force_recompile(); + } + } + else if (exp_type.vecsize == 4 && exp_type.columns == 4) + { + if (!requires_transpose_4x4) + { + requires_transpose_4x4 = true; + force_recompile(); + } + } + else + SPIRV_CROSS_THROW("Non-square matrices are not supported in legacy GLSL, cannot transpose."); + return join("SPIRV_Cross_Transpose(", exp_str, ")"); + } + else + return join("transpose(", exp_str, ")"); +} + +string CompilerGLSL::variable_decl(const SPIRType &type, const string &name, uint32_t id) +{ + string type_name = type_to_glsl(type, id); + remap_variable_type_name(type, name, type_name); + return join(type_name, " ", name, type_to_array_glsl(type)); +} + +// Emit a structure member. Subclasses may override to modify output, +// or to dynamically add a padding member if needed. +void CompilerGLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const string &qualifier, uint32_t) +{ + auto &membertype = get(member_type_id); + + Bitset memberflags; + auto &memb = ir.meta[type.self].members; + if (index < memb.size()) + memberflags = memb[index].decoration_flags; + + string qualifiers; + bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + + if (is_block) + qualifiers = to_interpolation_qualifiers(memberflags); + + statement(layout_for_member(type, index), qualifiers, qualifier, flags_to_qualifiers_glsl(membertype, memberflags), + variable_decl(membertype, to_member_name(type, index)), ";"); +} + +void CompilerGLSL::emit_struct_padding_target(const SPIRType &) +{ +} + +const char *CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags) +{ + // GL_EXT_buffer_reference variables can be marked as restrict. + if (flags.get(DecorationRestrictPointerEXT)) + return "restrict "; + + // Structs do not have precision qualifiers, neither do doubles (desktop only anyways, so no mediump/highp). + if (type.basetype != SPIRType::Float && type.basetype != SPIRType::Int && type.basetype != SPIRType::UInt && + type.basetype != SPIRType::Image && type.basetype != SPIRType::SampledImage && + type.basetype != SPIRType::Sampler) + return ""; + + if (options.es) + { + auto &execution = get_entry_point(); + + if (flags.get(DecorationRelaxedPrecision)) + { + bool implied_fmediump = type.basetype == SPIRType::Float && + options.fragment.default_float_precision == Options::Mediump && + execution.model == ExecutionModelFragment; + + bool implied_imediump = (type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt) && + options.fragment.default_int_precision == Options::Mediump && + execution.model == ExecutionModelFragment; + + return implied_fmediump || implied_imediump ? "" : "mediump "; + } + else + { + bool implied_fhighp = + type.basetype == SPIRType::Float && ((options.fragment.default_float_precision == Options::Highp && + execution.model == ExecutionModelFragment) || + (execution.model != ExecutionModelFragment)); + + bool implied_ihighp = (type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt) && + ((options.fragment.default_int_precision == Options::Highp && + execution.model == ExecutionModelFragment) || + (execution.model != ExecutionModelFragment)); + + return implied_fhighp || implied_ihighp ? "" : "highp "; + } + } + else if (backend.allow_precision_qualifiers) + { + // Vulkan GLSL supports precision qualifiers, even in desktop profiles, which is convenient. + // The default is highp however, so only emit mediump in the rare case that a shader has these. + if (flags.get(DecorationRelaxedPrecision)) + return "mediump "; + else + return ""; + } + else + return ""; +} + +const char *CompilerGLSL::to_precision_qualifiers_glsl(uint32_t id) +{ + auto &type = expression_type(id); + bool use_precision_qualifiers = backend.allow_precision_qualifiers || options.es; + if (use_precision_qualifiers && (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage)) + { + // Force mediump for the sampler type. We cannot declare 16-bit or smaller image types. + auto &result_type = get(type.image.type); + if (result_type.width < 32) + return "mediump "; + } + return flags_to_qualifiers_glsl(type, ir.meta[id].decoration.decoration_flags); +} + +string CompilerGLSL::to_qualifiers_glsl(uint32_t id) +{ + auto &flags = ir.meta[id].decoration.decoration_flags; + string res; + + auto *var = maybe_get(id); + + if (var && var->storage == StorageClassWorkgroup && !backend.shared_is_implied) + res += "shared "; + + res += to_interpolation_qualifiers(flags); + if (var) + res += to_storage_qualifiers_glsl(*var); + + auto &type = expression_type(id); + if (type.image.dim != DimSubpassData && type.image.sampled == 2) + { + if (flags.get(DecorationCoherent)) + res += "coherent "; + if (flags.get(DecorationRestrict)) + res += "restrict "; + if (flags.get(DecorationNonWritable)) + res += "readonly "; + if (flags.get(DecorationNonReadable)) + res += "writeonly "; + } + + res += to_precision_qualifiers_glsl(id); + + return res; +} + +string CompilerGLSL::argument_decl(const SPIRFunction::Parameter &arg) +{ + // glslangValidator seems to make all arguments pointer no matter what which is rather bizarre ... + auto &type = expression_type(arg.id); + const char *direction = ""; + + if (type.pointer) + { + if (arg.write_count && arg.read_count) + direction = "inout "; + else if (arg.write_count) + direction = "out "; + } + + return join(direction, to_qualifiers_glsl(arg.id), variable_decl(type, to_name(arg.id), arg.id)); +} + +string CompilerGLSL::to_initializer_expression(const SPIRVariable &var) +{ + return to_expression(var.initializer); +} + +string CompilerGLSL::to_zero_initialized_expression(uint32_t type_id) +{ +#ifndef NDEBUG + auto &type = get(type_id); + assert(type.storage == StorageClassPrivate || type.storage == StorageClassFunction || + type.storage == StorageClassGeneric); +#endif + uint32_t id = ir.increase_bound_by(1); + ir.make_constant_null(id, type_id, false); + return constant_expression(get(id)); +} + +bool CompilerGLSL::type_can_zero_initialize(const SPIRType &type) const +{ + if (type.pointer) + return false; + + if (!type.array.empty() && options.flatten_multidimensional_arrays) + return false; + + for (auto &literal : type.array_size_literal) + if (!literal) + return false; + + for (auto &memb : type.member_types) + if (!type_can_zero_initialize(get(memb))) + return false; + + return true; +} + +string CompilerGLSL::variable_decl(const SPIRVariable &variable) +{ + // Ignore the pointer type since GLSL doesn't have pointers. + auto &type = get_variable_data_type(variable); + + if (type.pointer_depth > 1) + SPIRV_CROSS_THROW("Cannot declare pointer-to-pointer types."); + + auto res = join(to_qualifiers_glsl(variable.self), variable_decl(type, to_name(variable.self), variable.self)); + + if (variable.loop_variable && variable.static_expression) + { + uint32_t expr = variable.static_expression; + if (ir.ids[expr].get_type() != TypeUndef) + res += join(" = ", to_expression(variable.static_expression)); + else if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + res += join(" = ", to_zero_initialized_expression(get_variable_data_type_id(variable))); + } + else if (variable.initializer) + { + uint32_t expr = variable.initializer; + if (ir.ids[expr].get_type() != TypeUndef) + res += join(" = ", to_initializer_expression(variable)); + else if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + res += join(" = ", to_zero_initialized_expression(get_variable_data_type_id(variable))); + } + + return res; +} + +const char *CompilerGLSL::to_pls_qualifiers_glsl(const SPIRVariable &variable) +{ + auto &flags = ir.meta[variable.self].decoration.decoration_flags; + if (flags.get(DecorationRelaxedPrecision)) + return "mediump "; + else + return "highp "; +} + +string CompilerGLSL::pls_decl(const PlsRemap &var) +{ + auto &variable = get(var.id); + + SPIRType type; + type.vecsize = pls_format_to_components(var.format); + type.basetype = pls_format_to_basetype(var.format); + + return join(to_pls_layout(var.format), to_pls_qualifiers_glsl(variable), type_to_glsl(type), " ", + to_name(variable.self)); +} + +uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type) const +{ + return to_array_size_literal(type, uint32_t(type.array.size() - 1)); +} + +uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type, uint32_t index) const +{ + assert(type.array.size() == type.array_size_literal.size()); + + if (type.array_size_literal[index]) + { + return type.array[index]; + } + else + { + // Use the default spec constant value. + // This is the best we can do. + return evaluate_constant_u32(type.array[index]); + } +} + +string CompilerGLSL::to_array_size(const SPIRType &type, uint32_t index) +{ + assert(type.array.size() == type.array_size_literal.size()); + + auto &size = type.array[index]; + if (!type.array_size_literal[index]) + return to_expression(size); + else if (size) + return convert_to_string(size); + else if (!backend.unsized_array_supported) + { + // For runtime-sized arrays, we can work around + // lack of standard support for this by simply having + // a single element array. + // + // Runtime length arrays must always be the last element + // in an interface block. + return "1"; + } + else + return ""; +} + +string CompilerGLSL::type_to_array_glsl(const SPIRType &type) +{ + if (type.pointer && type.storage == StorageClassPhysicalStorageBufferEXT && type.basetype != SPIRType::Struct) + { + // We are using a wrapped pointer type, and we should not emit any array declarations here. + return ""; + } + + if (type.array.empty()) + return ""; + + if (options.flatten_multidimensional_arrays) + { + string res; + res += "["; + for (auto i = uint32_t(type.array.size()); i; i--) + { + res += enclose_expression(to_array_size(type, i - 1)); + if (i > 1) + res += " * "; + } + res += "]"; + return res; + } + else + { + if (type.array.size() > 1) + { + if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_arrays_of_arrays"); + else if (options.es && options.version < 310) + SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310. " + "Try using --flatten-multidimensional-arrays or set " + "options.flatten_multidimensional_arrays to true."); + } + + string res; + for (auto i = uint32_t(type.array.size()); i; i--) + { + res += "["; + res += to_array_size(type, i - 1); + res += "]"; + } + return res; + } +} + +string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t id) +{ + auto &imagetype = get(type.image.type); + string res; + + switch (imagetype.basetype) + { + case SPIRType::Int: + case SPIRType::Short: + case SPIRType::SByte: + res = "i"; + break; + case SPIRType::UInt: + case SPIRType::UShort: + case SPIRType::UByte: + res = "u"; + break; + default: + break; + } + + // For half image types, we will force mediump for the sampler, and cast to f16 after any sampling operation. + // We cannot express a true half texture type in GLSL. Neither for short integer formats for that matter. + + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && options.vulkan_semantics) + return res + "subpassInput" + (type.image.ms ? "MS" : ""); + else if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && + subpass_input_is_framebuffer_fetch(id)) + { + SPIRType sampled_type = get(type.image.type); + sampled_type.vecsize = 4; + return type_to_glsl(sampled_type); + } + + // If we're emulating subpassInput with samplers, force sampler2D + // so we don't have to specify format. + if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData) + { + // Sampler buffers are always declared as samplerBuffer even though they might be separate images in the SPIR-V. + if (type.image.dim == DimBuffer && type.image.sampled == 1) + res += "sampler"; + else + res += type.image.sampled == 2 ? "image" : "texture"; + } + else + res += "sampler"; + + switch (type.image.dim) + { + case Dim1D: + res += "1D"; + break; + case Dim2D: + res += "2D"; + break; + case Dim3D: + res += "3D"; + break; + case DimCube: + res += "Cube"; + break; + case DimRect: + if (options.es) + SPIRV_CROSS_THROW("Rectangle textures are not supported on OpenGL ES."); + + if (is_legacy_desktop()) + require_extension_internal("GL_ARB_texture_rectangle"); + + res += "2DRect"; + break; + + case DimBuffer: + if (options.es && options.version < 320) + require_extension_internal("GL_OES_texture_buffer"); + else if (!options.es && options.version < 300) + require_extension_internal("GL_EXT_texture_buffer_object"); + res += "Buffer"; + break; + + case DimSubpassData: + res += "2D"; + break; + default: + SPIRV_CROSS_THROW("Only 1D, 2D, 2DRect, 3D, Buffer, InputTarget and Cube textures supported."); + } + + if (type.image.ms) + res += "MS"; + if (type.image.arrayed) + { + if (is_legacy_desktop()) + require_extension_internal("GL_EXT_texture_array"); + res += "Array"; + } + + // "Shadow" state in GLSL only exists for samplers and combined image samplers. + if (((type.basetype == SPIRType::SampledImage) || (type.basetype == SPIRType::Sampler)) && + image_is_comparison(type, id)) + { + res += "Shadow"; + } + + return res; +} + +string CompilerGLSL::type_to_glsl_constructor(const SPIRType &type) +{ + if (backend.use_array_constructor && type.array.size() > 1) + { + if (options.flatten_multidimensional_arrays) + SPIRV_CROSS_THROW("Cannot flatten constructors of multidimensional array constructors, " + "e.g. float[][]()."); + else if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_arrays_of_arrays"); + else if (options.es && options.version < 310) + SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310."); + } + + auto e = type_to_glsl(type); + if (backend.use_array_constructor) + { + for (uint32_t i = 0; i < type.array.size(); i++) + e += "[]"; + } + return e; +} + +// The optional id parameter indicates the object whose type we are trying +// to find the description for. It is optional. Most type descriptions do not +// depend on a specific object's use of that type. +string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) +{ + if (type.pointer && type.storage == StorageClassPhysicalStorageBufferEXT && type.basetype != SPIRType::Struct) + { + // Need to create a magic type name which compacts the entire type information. + string name = type_to_glsl(get_pointee_type(type)); + for (size_t i = 0; i < type.array.size(); i++) + { + if (type.array_size_literal[i]) + name += join(type.array[i], "_"); + else + name += join("id", type.array[i], "_"); + } + name += "Pointer"; + return name; + } + + switch (type.basetype) + { + case SPIRType::Struct: + // Need OpName lookup here to get a "sensible" name for a struct. + if (backend.explicit_struct_type) + return join("struct ", to_name(type.self)); + else + return to_name(type.self); + + case SPIRType::Image: + case SPIRType::SampledImage: + return image_type_glsl(type, id); + + case SPIRType::Sampler: + // The depth field is set by calling code based on the variable ID of the sampler, effectively reintroducing + // this distinction into the type system. + return comparison_ids.count(id) ? "samplerShadow" : "sampler"; + + case SPIRType::AccelerationStructure: + return "accelerationStructureNV"; + + case SPIRType::Void: + return "void"; + + default: + break; + } + + if (type.basetype == SPIRType::UInt && is_legacy()) + SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy targets."); + + if (type.vecsize == 1 && type.columns == 1) // Scalar builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return "bool"; + case SPIRType::SByte: + return backend.basic_int8_type; + case SPIRType::UByte: + return backend.basic_uint8_type; + case SPIRType::Short: + return backend.basic_int16_type; + case SPIRType::UShort: + return backend.basic_uint16_type; + case SPIRType::Int: + return backend.basic_int_type; + case SPIRType::UInt: + return backend.basic_uint_type; + case SPIRType::AtomicCounter: + return "atomic_uint"; + case SPIRType::Half: + return "float16_t"; + case SPIRType::Float: + return "float"; + case SPIRType::Double: + return "double"; + case SPIRType::Int64: + return "int64_t"; + case SPIRType::UInt64: + return "uint64_t"; + default: + return "???"; + } + } + else if (type.vecsize > 1 && type.columns == 1) // Vector builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bvec", type.vecsize); + case SPIRType::SByte: + return join("i8vec", type.vecsize); + case SPIRType::UByte: + return join("u8vec", type.vecsize); + case SPIRType::Short: + return join("i16vec", type.vecsize); + case SPIRType::UShort: + return join("u16vec", type.vecsize); + case SPIRType::Int: + return join("ivec", type.vecsize); + case SPIRType::UInt: + return join("uvec", type.vecsize); + case SPIRType::Half: + return join("f16vec", type.vecsize); + case SPIRType::Float: + return join("vec", type.vecsize); + case SPIRType::Double: + return join("dvec", type.vecsize); + case SPIRType::Int64: + return join("i64vec", type.vecsize); + case SPIRType::UInt64: + return join("u64vec", type.vecsize); + default: + return "???"; + } + } + else if (type.vecsize == type.columns) // Simple Matrix builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bmat", type.vecsize); + case SPIRType::Int: + return join("imat", type.vecsize); + case SPIRType::UInt: + return join("umat", type.vecsize); + case SPIRType::Half: + return join("f16mat", type.vecsize); + case SPIRType::Float: + return join("mat", type.vecsize); + case SPIRType::Double: + return join("dmat", type.vecsize); + // Matrix types not supported for int64/uint64. + default: + return "???"; + } + } + else + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bmat", type.columns, "x", type.vecsize); + case SPIRType::Int: + return join("imat", type.columns, "x", type.vecsize); + case SPIRType::UInt: + return join("umat", type.columns, "x", type.vecsize); + case SPIRType::Half: + return join("f16mat", type.columns, "x", type.vecsize); + case SPIRType::Float: + return join("mat", type.columns, "x", type.vecsize); + case SPIRType::Double: + return join("dmat", type.columns, "x", type.vecsize); + // Matrix types not supported for int64/uint64. + default: + return "???"; + } + } +} + +void CompilerGLSL::add_variable(unordered_set &variables_primary, + const unordered_set &variables_secondary, string &name) +{ + if (name.empty()) + return; + + ParsedIR::sanitize_underscores(name); + if (ParsedIR::is_globally_reserved_identifier(name, true)) + { + name.clear(); + return; + } + + update_name_cache(variables_primary, variables_secondary, name); +} + +void CompilerGLSL::add_local_variable_name(uint32_t id) +{ + add_variable(local_variable_names, block_names, ir.meta[id].decoration.alias); +} + +void CompilerGLSL::add_resource_name(uint32_t id) +{ + add_variable(resource_names, block_names, ir.meta[id].decoration.alias); +} + +void CompilerGLSL::add_header_line(const std::string &line) +{ + header_lines.push_back(line); +} + +bool CompilerGLSL::has_extension(const std::string &ext) const +{ + auto itr = find(begin(forced_extensions), end(forced_extensions), ext); + return itr != end(forced_extensions); +} + +void CompilerGLSL::require_extension(const std::string &ext) +{ + if (!has_extension(ext)) + forced_extensions.push_back(ext); +} + +void CompilerGLSL::require_extension_internal(const string &ext) +{ + if (backend.supports_extensions && !has_extension(ext)) + { + forced_extensions.push_back(ext); + force_recompile(); + } +} + +void CompilerGLSL::flatten_buffer_block(VariableID id) +{ + auto &var = get(id); + auto &type = get(var.basetype); + auto name = to_name(type.self, false); + auto &flags = ir.meta[type.self].decoration.decoration_flags; + + if (!type.array.empty()) + SPIRV_CROSS_THROW(name + " is an array of UBOs."); + if (type.basetype != SPIRType::Struct) + SPIRV_CROSS_THROW(name + " is not a struct."); + if (!flags.get(DecorationBlock)) + SPIRV_CROSS_THROW(name + " is not a block."); + if (type.member_types.empty()) + SPIRV_CROSS_THROW(name + " is an empty struct."); + + flattened_buffer_blocks.insert(id); +} + +bool CompilerGLSL::builtin_translates_to_nonarray(spv::BuiltIn /*builtin*/) const +{ + return false; // GLSL itself does not need to translate array builtin types to non-array builtin types +} + +bool CompilerGLSL::check_atomic_image(uint32_t id) +{ + auto &type = expression_type(id); + if (type.storage == StorageClassImage) + { + if (options.es && options.version < 320) + require_extension_internal("GL_OES_shader_image_atomic"); + + auto *var = maybe_get_backing_variable(id); + if (var) + { + auto &flags = ir.meta[var->self].decoration.decoration_flags; + if (flags.get(DecorationNonWritable) || flags.get(DecorationNonReadable)) + { + flags.clear(DecorationNonWritable); + flags.clear(DecorationNonReadable); + force_recompile(); + } + } + return true; + } + else + return false; +} + +void CompilerGLSL::add_function_overload(const SPIRFunction &func) +{ + Hasher hasher; + for (auto &arg : func.arguments) + { + // Parameters can vary with pointer type or not, + // but that will not change the signature in GLSL/HLSL, + // so strip the pointer type before hashing. + uint32_t type_id = get_pointee_type_id(arg.type); + auto &type = get(type_id); + + if (!combined_image_samplers.empty()) + { + // If we have combined image samplers, we cannot really trust the image and sampler arguments + // we pass down to callees, because they may be shuffled around. + // Ignore these arguments, to make sure that functions need to differ in some other way + // to be considered different overloads. + if (type.basetype == SPIRType::SampledImage || + (type.basetype == SPIRType::Image && type.image.sampled == 1) || type.basetype == SPIRType::Sampler) + { + continue; + } + } + + hasher.u32(type_id); + } + uint64_t types_hash = hasher.get(); + + auto function_name = to_name(func.self); + auto itr = function_overloads.find(function_name); + if (itr != end(function_overloads)) + { + // There exists a function with this name already. + auto &overloads = itr->second; + if (overloads.count(types_hash) != 0) + { + // Overload conflict, assign a new name. + add_resource_name(func.self); + function_overloads[to_name(func.self)].insert(types_hash); + } + else + { + // Can reuse the name. + overloads.insert(types_hash); + } + } + else + { + // First time we see this function name. + add_resource_name(func.self); + function_overloads[to_name(func.self)].insert(types_hash); + } +} + +void CompilerGLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) +{ + if (func.self != ir.default_entry_point) + add_function_overload(func); + + // Avoid shadow declarations. + local_variable_names = resource_names; + + string decl; + + auto &type = get(func.return_type); + decl += flags_to_qualifiers_glsl(type, return_flags); + decl += type_to_glsl(type); + decl += type_to_array_glsl(type); + decl += " "; + + if (func.self == ir.default_entry_point) + { + // If we need complex fallback in GLSL, we just wrap main() in a function + // and interlock the entire shader ... + if (interlocked_is_complex) + decl += "spvMainInterlockedBody"; + else + decl += "main"; + + processing_entry_point = true; + } + else + decl += to_name(func.self); + + decl += "("; + SmallVector arglist; + for (auto &arg : func.arguments) + { + // Do not pass in separate images or samplers if we're remapping + // to combined image samplers. + if (skip_argument(arg.id)) + continue; + + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation + // to use same name for variables. + // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + for (auto &arg : func.shadow_arguments) + { + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation + // to use same name for variables. + // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + decl += merge(arglist); + decl += ")"; + statement(decl); +} + +void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags) +{ + // Avoid potential cycles. + if (func.active) + return; + func.active = true; + + // If we depend on a function, emit that function before we emit our own function. + for (auto block : func.blocks) + { + auto &b = get(block); + for (auto &i : b.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + if (op == OpFunctionCall) + { + // Recursively emit functions which are called. + uint32_t id = ops[2]; + emit_function(get(id), ir.meta[ops[1]].decoration.decoration_flags); + } + } + } + + if (func.entry_line.file_id != 0) + emit_line_directive(func.entry_line.file_id, func.entry_line.line_literal); + emit_function_prototype(func, return_flags); + begin_scope(); + + if (func.self == ir.default_entry_point) + emit_entry_point_declarations(); + + current_function = &func; + auto &entry_block = get(func.entry_block); + + sort(begin(func.constant_arrays_needed_on_stack), end(func.constant_arrays_needed_on_stack)); + for (auto &array : func.constant_arrays_needed_on_stack) + { + auto &c = get(array); + auto &type = get(c.constant_type); + statement(variable_decl(type, join("_", array, "_array_copy")), " = ", constant_expression(c), ";"); + } + + for (auto &v : func.local_variables) + { + auto &var = get(v); + var.deferred_declaration = false; + + if (var.storage == StorageClassWorkgroup) + { + // Special variable type which cannot have initializer, + // need to be declared as standalone variables. + // Comes from MSL which can push global variables as local variables in main function. + add_local_variable_name(var.self); + statement(variable_decl(var), ";"); + var.deferred_declaration = false; + } + else if (var.storage == StorageClassPrivate) + { + // These variables will not have had their CFG usage analyzed, so move it to the entry block. + // Comes from MSL which can push global variables as local variables in main function. + // We could just declare them right now, but we would miss out on an important initialization case which is + // LUT declaration in MSL. + // If we don't declare the variable when it is assigned we're forced to go through a helper function + // which copies elements one by one. + add_local_variable_name(var.self); + + if (var.initializer) + { + statement(variable_decl(var), ";"); + var.deferred_declaration = false; + } + else + { + auto &dominated = entry_block.dominated_variables; + if (find(begin(dominated), end(dominated), var.self) == end(dominated)) + entry_block.dominated_variables.push_back(var.self); + var.deferred_declaration = true; + } + } + else if (var.storage == StorageClassFunction && var.remapped_variable && var.static_expression) + { + // No need to declare this variable, it has a static expression. + var.deferred_declaration = false; + } + else if (expression_is_lvalue(v)) + { + add_local_variable_name(var.self); + + // Loop variables should never be declared early, they are explicitly emitted in a loop. + if (var.initializer && !var.loop_variable) + statement(variable_decl_function_local(var), ";"); + else + { + // Don't declare variable until first use to declutter the GLSL output quite a lot. + // If we don't touch the variable before first branch, + // declare it then since we need variable declaration to be in top scope. + var.deferred_declaration = true; + } + } + else + { + // HACK: SPIR-V in older glslang output likes to use samplers and images as local variables, but GLSL does not allow this. + // For these types (non-lvalue), we enforce forwarding through a shadowed variable. + // This means that when we OpStore to these variables, we just write in the expression ID directly. + // This breaks any kind of branching, since the variable must be statically assigned. + // Branching on samplers and images would be pretty much impossible to fake in GLSL. + var.statically_assigned = true; + } + + var.loop_variable_enable = false; + + // Loop variables are never declared outside their for-loop, so block any implicit declaration. + if (var.loop_variable) + var.deferred_declaration = false; + } + + // Enforce declaration order for regression testing purposes. + for (auto &block_id : func.blocks) + { + auto &block = get(block_id); + sort(begin(block.dominated_variables), end(block.dominated_variables)); + } + + for (auto &line : current_function->fixup_hooks_in) + line(); + + emit_block_chain(entry_block); + + end_scope(); + processing_entry_point = false; + statement(""); + + // Make sure deferred declaration state for local variables is cleared when we are done with function. + // We risk declaring Private/Workgroup variables in places we are not supposed to otherwise. + for (auto &v : func.local_variables) + { + auto &var = get(v); + var.deferred_declaration = false; + } +} + +void CompilerGLSL::emit_fixup() +{ + if (is_vertex_like_shader()) + { + if (options.vertex.fixup_clipspace) + { + const char *suffix = backend.float_literal_suffix ? "f" : ""; + statement("gl_Position.z = 2.0", suffix, " * gl_Position.z - gl_Position.w;"); + } + + if (options.vertex.flip_vert_y) + statement("gl_Position.y = -gl_Position.y;"); + } +} + +void CompilerGLSL::flush_phi(BlockID from, BlockID to) +{ + auto &child = get(to); + if (child.ignore_phi_from_block == from) + return; + + unordered_set temporary_phi_variables; + + for (auto itr = begin(child.phi_variables); itr != end(child.phi_variables); ++itr) + { + auto &phi = *itr; + + if (phi.parent == from) + { + auto &var = get(phi.function_variable); + + // A Phi variable might be a loop variable, so flush to static expression. + if (var.loop_variable && !var.loop_variable_enable) + var.static_expression = phi.local_variable; + else + { + flush_variable_declaration(phi.function_variable); + + // Check if we are going to write to a Phi variable that another statement will read from + // as part of another Phi node in our target block. + // For this case, we will need to copy phi.function_variable to a temporary, and use that for future reads. + // This is judged to be extremely rare, so deal with it here using a simple, but suboptimal algorithm. + bool need_saved_temporary = + find_if(itr + 1, end(child.phi_variables), [&](const SPIRBlock::Phi &future_phi) -> bool { + return future_phi.local_variable == ID(phi.function_variable) && future_phi.parent == from; + }) != end(child.phi_variables); + + if (need_saved_temporary) + { + // Need to make sure we declare the phi variable with a copy at the right scope. + // We cannot safely declare a temporary here since we might be inside a continue block. + if (!var.allocate_temporary_copy) + { + var.allocate_temporary_copy = true; + force_recompile(); + } + statement("_", phi.function_variable, "_copy", " = ", to_name(phi.function_variable), ";"); + temporary_phi_variables.insert(phi.function_variable); + } + + // This might be called in continue block, so make sure we + // use this to emit ESSL 1.0 compliant increments/decrements. + auto lhs = to_expression(phi.function_variable); + + string rhs; + if (temporary_phi_variables.count(phi.local_variable)) + rhs = join("_", phi.local_variable, "_copy"); + else + rhs = to_pointer_expression(phi.local_variable); + + if (!optimize_read_modify_write(get(var.basetype), lhs, rhs)) + statement(lhs, " = ", rhs, ";"); + } + + register_write(phi.function_variable); + } + } +} + +void CompilerGLSL::branch_to_continue(BlockID from, BlockID to) +{ + auto &to_block = get(to); + if (from == to) + return; + + assert(is_continue(to)); + if (to_block.complex_continue) + { + // Just emit the whole block chain as is. + auto usage_counts = expression_usage_counts; + + emit_block_chain(to_block); + + // Expression usage counts are moot after returning from the continue block. + expression_usage_counts = usage_counts; + } + else + { + auto &from_block = get(from); + bool outside_control_flow = false; + uint32_t loop_dominator = 0; + + // FIXME: Refactor this to not use the old loop_dominator tracking. + if (from_block.merge_block) + { + // If we are a loop header, we don't set the loop dominator, + // so just use "self" here. + loop_dominator = from; + } + else if (from_block.loop_dominator != BlockID(SPIRBlock::NoDominator)) + { + loop_dominator = from_block.loop_dominator; + } + + if (loop_dominator != 0) + { + auto &cfg = get_cfg_for_current_function(); + + // For non-complex continue blocks, we implicitly branch to the continue block + // by having the continue block be part of the loop header in for (; ; continue-block). + outside_control_flow = cfg.node_terminates_control_flow_in_sub_graph(loop_dominator, from); + } + + // Some simplification for for-loops. We always end up with a useless continue; + // statement since we branch to a loop block. + // Walk the CFG, if we unconditionally execute the block calling continue assuming we're in the loop block, + // we can avoid writing out an explicit continue statement. + // Similar optimization to return statements if we know we're outside flow control. + if (!outside_control_flow) + statement("continue;"); + } +} + +void CompilerGLSL::branch(BlockID from, BlockID to) +{ + flush_phi(from, to); + flush_control_dependent_expressions(from); + + bool to_is_continue = is_continue(to); + + // This is only a continue if we branch to our loop dominator. + if ((ir.block_meta[to] & ParsedIR::BLOCK_META_LOOP_HEADER_BIT) != 0 && get(from).loop_dominator == to) + { + // This can happen if we had a complex continue block which was emitted. + // Once the continue block tries to branch to the loop header, just emit continue; + // and end the chain here. + statement("continue;"); + } + else if (from != to && is_break(to)) + { + // We cannot break to ourselves, so check explicitly for from != to. + // This case can trigger if a loop header is all three of these things: + // - Continue block + // - Loop header + // - Break merge target all at once ... + + // Very dirty workaround. + // Switch constructs are able to break, but they cannot break out of a loop at the same time. + // Only sensible solution is to make a ladder variable, which we declare at the top of the switch block, + // write to the ladder here, and defer the break. + // The loop we're breaking out of must dominate the switch block, or there is no ladder breaking case. + if (current_emitting_switch && is_loop_break(to) && + current_emitting_switch->loop_dominator != BlockID(SPIRBlock::NoDominator) && + get(current_emitting_switch->loop_dominator).merge_block == to) + { + if (!current_emitting_switch->need_ladder_break) + { + force_recompile(); + current_emitting_switch->need_ladder_break = true; + } + + statement("_", current_emitting_switch->self, "_ladder_break = true;"); + } + statement("break;"); + } + else if (to_is_continue || from == to) + { + // For from == to case can happen for a do-while loop which branches into itself. + // We don't mark these cases as continue blocks, but the only possible way to branch into + // ourselves is through means of continue blocks. + + // If we are merging to a continue block, there is no need to emit the block chain for continue here. + // We can branch to the continue block after we merge execution. + + // Here we make use of structured control flow rules from spec: + // 2.11: - the merge block declared by a header block cannot be a merge block declared by any other header block + // - each header block must strictly dominate its merge block, unless the merge block is unreachable in the CFG + // If we are branching to a merge block, we must be inside a construct which dominates the merge block. + auto &block_meta = ir.block_meta[to]; + bool branching_to_merge = + (block_meta & (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT | + ParsedIR::BLOCK_META_LOOP_MERGE_BIT)) != 0; + if (!to_is_continue || !branching_to_merge) + branch_to_continue(from, to); + } + else if (!is_conditional(to)) + emit_block_chain(get(to)); + + // It is important that we check for break before continue. + // A block might serve two purposes, a break block for the inner scope, and + // a continue block in the outer scope. + // Inner scope always takes precedence. +} + +void CompilerGLSL::branch(BlockID from, uint32_t cond, BlockID true_block, BlockID false_block) +{ + auto &from_block = get(from); + BlockID merge_block = from_block.merge == SPIRBlock::MergeSelection ? from_block.next_block : BlockID(0); + + // If we branch directly to our selection merge target, we don't need a code path. + bool true_block_needs_code = true_block != merge_block || flush_phi_required(from, true_block); + bool false_block_needs_code = false_block != merge_block || flush_phi_required(from, false_block); + + if (!true_block_needs_code && !false_block_needs_code) + return; + + emit_block_hints(get(from)); + + if (true_block_needs_code) + { + statement("if (", to_expression(cond), ")"); + begin_scope(); + branch(from, true_block); + end_scope(); + + if (false_block_needs_code) + { + statement("else"); + begin_scope(); + branch(from, false_block); + end_scope(); + } + } + else if (false_block_needs_code) + { + // Only need false path, use negative conditional. + statement("if (!", to_enclosed_expression(cond), ")"); + begin_scope(); + branch(from, false_block); + end_scope(); + } +} + +// FIXME: This currently cannot handle complex continue blocks +// as in do-while. +// This should be seen as a "trivial" continue block. +string CompilerGLSL::emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block) +{ + auto *block = &get(continue_block); + + // While emitting the continue block, declare_temporary will check this + // if we have to emit temporaries. + current_continue_block = block; + + SmallVector statements; + + // Capture all statements into our list. + auto *old = redirect_statement; + redirect_statement = &statements; + + // Stamp out all blocks one after each other. + while ((ir.block_meta[block->self] & ParsedIR::BLOCK_META_LOOP_HEADER_BIT) == 0) + { + // Write out all instructions we have in this block. + emit_block_instructions(*block); + + // For plain branchless for/while continue blocks. + if (block->next_block) + { + flush_phi(continue_block, block->next_block); + block = &get(block->next_block); + } + // For do while blocks. The last block will be a select block. + else if (block->true_block && follow_true_block) + { + flush_phi(continue_block, block->true_block); + block = &get(block->true_block); + } + else if (block->false_block && follow_false_block) + { + flush_phi(continue_block, block->false_block); + block = &get(block->false_block); + } + else + { + SPIRV_CROSS_THROW("Invalid continue block detected!"); + } + } + + // Restore old pointer. + redirect_statement = old; + + // Somewhat ugly, strip off the last ';' since we use ',' instead. + // Ideally, we should select this behavior in statement(). + for (auto &s : statements) + { + if (!s.empty() && s.back() == ';') + s.erase(s.size() - 1, 1); + } + + current_continue_block = nullptr; + return merge(statements); +} + +void CompilerGLSL::emit_while_loop_initializers(const SPIRBlock &block) +{ + // While loops do not take initializers, so declare all of them outside. + for (auto &loop_var : block.loop_variables) + { + auto &var = get(loop_var); + statement(variable_decl(var), ";"); + } +} + +string CompilerGLSL::emit_for_loop_initializers(const SPIRBlock &block) +{ + if (block.loop_variables.empty()) + return ""; + + bool same_types = for_loop_initializers_are_same_type(block); + // We can only declare for loop initializers if all variables are of same type. + // If we cannot do this, declare individual variables before the loop header. + + // We might have a loop variable candidate which was not assigned to for some reason. + uint32_t missing_initializers = 0; + for (auto &variable : block.loop_variables) + { + uint32_t expr = get(variable).static_expression; + + // Sometimes loop variables are initialized with OpUndef, but we can just declare + // a plain variable without initializer in this case. + if (expr == 0 || ir.ids[expr].get_type() == TypeUndef) + missing_initializers++; + } + + if (block.loop_variables.size() == 1 && missing_initializers == 0) + { + return variable_decl(get(block.loop_variables.front())); + } + else if (!same_types || missing_initializers == uint32_t(block.loop_variables.size())) + { + for (auto &loop_var : block.loop_variables) + statement(variable_decl(get(loop_var)), ";"); + return ""; + } + else + { + // We have a mix of loop variables, either ones with a clear initializer, or ones without. + // Separate the two streams. + string expr; + + for (auto &loop_var : block.loop_variables) + { + uint32_t static_expr = get(loop_var).static_expression; + if (static_expr == 0 || ir.ids[static_expr].get_type() == TypeUndef) + { + statement(variable_decl(get(loop_var)), ";"); + } + else + { + auto &var = get(loop_var); + auto &type = get_variable_data_type(var); + if (expr.empty()) + { + // For loop initializers are of the form (block.true_block), get(block.merge_block))) + condition = join("!", enclose_expression(condition)); + + statement("while (", condition, ")"); + break; + } + + default: + block.disable_block_optimization = true; + force_recompile(); + begin_scope(); // We'll see an end_scope() later. + return false; + } + + begin_scope(); + return true; + } + else + { + block.disable_block_optimization = true; + force_recompile(); + begin_scope(); // We'll see an end_scope() later. + return false; + } + } + else if (method == SPIRBlock::MergeToDirectForLoop) + { + auto &child = get(block.next_block); + + // This block may be a dominating block, so make sure we flush undeclared variables before building the for loop header. + flush_undeclared_variables(child); + + uint32_t current_count = statement_count; + + // If we're trying to create a true for loop, + // we need to make sure that all opcodes before branch statement do not actually emit any code. + // We can then take the condition expression and create a for (; cond ; ) { body; } structure instead. + emit_block_instructions(child); + + bool condition_is_temporary = forced_temporaries.find(child.condition) == end(forced_temporaries); + + if (current_count == statement_count && condition_is_temporary) + { + uint32_t target_block = child.true_block; + + switch (continue_type) + { + case SPIRBlock::ForLoop: + { + // Important that we do this in this order because + // emitting the continue block can invalidate the condition expression. + auto initializer = emit_for_loop_initializers(block); + auto condition = to_expression(child.condition); + + // Condition might have to be inverted. + if (execution_is_noop(get(child.true_block), get(block.merge_block))) + { + condition = join("!", enclose_expression(condition)); + target_block = child.false_block; + } + + auto continue_block = emit_continue_block(block.continue_block, false, false); + emit_block_hints(block); + statement("for (", initializer, "; ", condition, "; ", continue_block, ")"); + break; + } + + case SPIRBlock::WhileLoop: + { + emit_while_loop_initializers(block); + emit_block_hints(block); + + auto condition = to_expression(child.condition); + // Condition might have to be inverted. + if (execution_is_noop(get(child.true_block), get(block.merge_block))) + { + condition = join("!", enclose_expression(condition)); + target_block = child.false_block; + } + + statement("while (", condition, ")"); + break; + } + + default: + block.disable_block_optimization = true; + force_recompile(); + begin_scope(); // We'll see an end_scope() later. + return false; + } + + begin_scope(); + branch(child.self, target_block); + return true; + } + else + { + block.disable_block_optimization = true; + force_recompile(); + begin_scope(); // We'll see an end_scope() later. + return false; + } + } + else + return false; +} + +void CompilerGLSL::flush_undeclared_variables(SPIRBlock &block) +{ + for (auto &v : block.dominated_variables) + flush_variable_declaration(v); +} + +void CompilerGLSL::emit_hoisted_temporaries(SmallVector> &temporaries) +{ + // If we need to force temporaries for certain IDs due to continue blocks, do it before starting loop header. + // Need to sort these to ensure that reference output is stable. + sort(begin(temporaries), end(temporaries), + [](const pair &a, const pair &b) { return a.second < b.second; }); + + for (auto &tmp : temporaries) + { + add_local_variable_name(tmp.second); + auto &flags = ir.meta[tmp.second].decoration.decoration_flags; + auto &type = get(tmp.first); + + // Not all targets support pointer literals, so don't bother with that case. + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(tmp.first)); + + statement(flags_to_qualifiers_glsl(type, flags), variable_decl(type, to_name(tmp.second)), initializer, ";"); + + hoisted_temporaries.insert(tmp.second); + forced_temporaries.insert(tmp.second); + + // The temporary might be read from before it's assigned, set up the expression now. + set(tmp.second, to_name(tmp.second), tmp.first, true); + } +} + +void CompilerGLSL::emit_block_chain(SPIRBlock &block) +{ + bool select_branch_to_true_block = false; + bool select_branch_to_false_block = false; + bool skip_direct_branch = false; + bool emitted_loop_header_variables = false; + bool force_complex_continue_block = false; + ValueSaver loop_level_saver(current_loop_level); + + if (block.merge == SPIRBlock::MergeLoop) + add_loop_level(); + + emit_hoisted_temporaries(block.declare_temporary); + + SPIRBlock::ContinueBlockType continue_type = SPIRBlock::ContinueNone; + if (block.continue_block) + { + continue_type = continue_block_type(get(block.continue_block)); + // If we know we cannot emit a loop, mark the block early as a complex loop so we don't force unnecessary recompiles. + if (continue_type == SPIRBlock::ComplexLoop) + block.complex_continue = true; + } + + // If we have loop variables, stop masking out access to the variable now. + for (auto var_id : block.loop_variables) + { + auto &var = get(var_id); + var.loop_variable_enable = true; + // We're not going to declare the variable directly, so emit a copy here. + emit_variable_temporary_copies(var); + } + + // Remember deferred declaration state. We will restore it before returning. + SmallVector rearm_dominated_variables(block.dominated_variables.size()); + for (size_t i = 0; i < block.dominated_variables.size(); i++) + { + uint32_t var_id = block.dominated_variables[i]; + auto &var = get(var_id); + rearm_dominated_variables[i] = var.deferred_declaration; + } + + // This is the method often used by spirv-opt to implement loops. + // The loop header goes straight into the continue block. + // However, don't attempt this on ESSL 1.0, because if a loop variable is used in a continue block, + // it *MUST* be used in the continue block. This loop method will not work. + if (!is_legacy_es() && block_is_loop_candidate(block, SPIRBlock::MergeToSelectContinueForLoop)) + { + flush_undeclared_variables(block); + if (attempt_emit_loop_header(block, SPIRBlock::MergeToSelectContinueForLoop)) + { + if (execution_is_noop(get(block.true_block), get(block.merge_block))) + select_branch_to_false_block = true; + else + select_branch_to_true_block = true; + + emitted_loop_header_variables = true; + force_complex_continue_block = true; + } + } + // This is the older loop behavior in glslang which branches to loop body directly from the loop header. + else if (block_is_loop_candidate(block, SPIRBlock::MergeToSelectForLoop)) + { + flush_undeclared_variables(block); + if (attempt_emit_loop_header(block, SPIRBlock::MergeToSelectForLoop)) + { + // The body of while, is actually just the true (or false) block, so always branch there unconditionally. + if (execution_is_noop(get(block.true_block), get(block.merge_block))) + select_branch_to_false_block = true; + else + select_branch_to_true_block = true; + + emitted_loop_header_variables = true; + } + } + // This is the newer loop behavior in glslang which branches from Loop header directly to + // a new block, which in turn has a OpBranchSelection without a selection merge. + else if (block_is_loop_candidate(block, SPIRBlock::MergeToDirectForLoop)) + { + flush_undeclared_variables(block); + if (attempt_emit_loop_header(block, SPIRBlock::MergeToDirectForLoop)) + { + skip_direct_branch = true; + emitted_loop_header_variables = true; + } + } + else if (continue_type == SPIRBlock::DoWhileLoop) + { + flush_undeclared_variables(block); + emit_while_loop_initializers(block); + emitted_loop_header_variables = true; + // We have some temporaries where the loop header is the dominator. + // We risk a case where we have code like: + // for (;;) { create-temporary; break; } consume-temporary; + // so force-declare temporaries here. + emit_hoisted_temporaries(block.potential_declare_temporary); + statement("do"); + begin_scope(); + + emit_block_instructions(block); + } + else if (block.merge == SPIRBlock::MergeLoop) + { + flush_undeclared_variables(block); + emit_while_loop_initializers(block); + emitted_loop_header_variables = true; + + // We have a generic loop without any distinguishable pattern like for, while or do while. + get(block.continue_block).complex_continue = true; + continue_type = SPIRBlock::ComplexLoop; + + // We have some temporaries where the loop header is the dominator. + // We risk a case where we have code like: + // for (;;) { create-temporary; break; } consume-temporary; + // so force-declare temporaries here. + emit_hoisted_temporaries(block.potential_declare_temporary); + statement("for (;;)"); + begin_scope(); + + emit_block_instructions(block); + } + else + { + emit_block_instructions(block); + } + + // If we didn't successfully emit a loop header and we had loop variable candidates, we have a problem + // as writes to said loop variables might have been masked out, we need a recompile. + if (!emitted_loop_header_variables && !block.loop_variables.empty()) + { + force_recompile(); + for (auto var : block.loop_variables) + get(var).loop_variable = false; + block.loop_variables.clear(); + } + + flush_undeclared_variables(block); + bool emit_next_block = true; + + // Handle end of block. + switch (block.terminator) + { + case SPIRBlock::Direct: + // True when emitting complex continue block. + if (block.loop_dominator == block.next_block) + { + branch(block.self, block.next_block); + emit_next_block = false; + } + // True if MergeToDirectForLoop succeeded. + else if (skip_direct_branch) + emit_next_block = false; + else if (is_continue(block.next_block) || is_break(block.next_block) || is_conditional(block.next_block)) + { + branch(block.self, block.next_block); + emit_next_block = false; + } + break; + + case SPIRBlock::Select: + // True if MergeToSelectForLoop or MergeToSelectContinueForLoop succeeded. + if (select_branch_to_true_block) + { + if (force_complex_continue_block) + { + assert(block.true_block == block.continue_block); + + // We're going to emit a continue block directly here, so make sure it's marked as complex. + auto &complex_continue = get(block.continue_block).complex_continue; + bool old_complex = complex_continue; + complex_continue = true; + branch(block.self, block.true_block); + complex_continue = old_complex; + } + else + branch(block.self, block.true_block); + } + else if (select_branch_to_false_block) + { + if (force_complex_continue_block) + { + assert(block.false_block == block.continue_block); + + // We're going to emit a continue block directly here, so make sure it's marked as complex. + auto &complex_continue = get(block.continue_block).complex_continue; + bool old_complex = complex_continue; + complex_continue = true; + branch(block.self, block.false_block); + complex_continue = old_complex; + } + else + branch(block.self, block.false_block); + } + else + branch(block.self, block.condition, block.true_block, block.false_block); + break; + + case SPIRBlock::MultiSelect: + { + auto &type = expression_type(block.condition); + bool unsigned_case = + type.basetype == SPIRType::UInt || type.basetype == SPIRType::UShort || type.basetype == SPIRType::UByte; + + if (block.merge == SPIRBlock::MergeNone) + SPIRV_CROSS_THROW("Switch statement is not structured"); + + if (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64) + { + // SPIR-V spec suggests this is allowed, but we cannot support it in higher level languages. + SPIRV_CROSS_THROW("Cannot use 64-bit switch selectors."); + } + + const char *label_suffix = ""; + if (type.basetype == SPIRType::UInt && backend.uint32_t_literal_suffix) + label_suffix = "u"; + else if (type.basetype == SPIRType::UShort) + label_suffix = backend.uint16_t_literal_suffix; + else if (type.basetype == SPIRType::Short) + label_suffix = backend.int16_t_literal_suffix; + + SPIRBlock *old_emitting_switch = current_emitting_switch; + current_emitting_switch = █ + + if (block.need_ladder_break) + statement("bool _", block.self, "_ladder_break = false;"); + + // Find all unique case constructs. + unordered_map> case_constructs; + SmallVector block_declaration_order; + SmallVector literals_to_merge; + + // If a switch case branches to the default block for some reason, we can just remove that literal from consideration + // and let the default: block handle it. + // 2.11 in SPIR-V spec states that for fall-through cases, there is a very strict declaration order which we can take advantage of here. + // We only need to consider possible fallthrough if order[i] branches to order[i + 1]. + for (auto &c : block.cases) + { + if (c.block != block.next_block && c.block != block.default_block) + { + if (!case_constructs.count(c.block)) + block_declaration_order.push_back(c.block); + case_constructs[c.block].push_back(c.value); + } + else if (c.block == block.next_block && block.default_block != block.next_block) + { + // We might have to flush phi inside specific case labels. + // If we can piggyback on default:, do so instead. + literals_to_merge.push_back(c.value); + } + } + + // Empty literal array -> default. + if (block.default_block != block.next_block) + { + auto &default_block = get(block.default_block); + + // We need to slide in the default block somewhere in this chain + // if there are fall-through scenarios since the default is declared separately in OpSwitch. + // Only consider trivial fall-through cases here. + size_t num_blocks = block_declaration_order.size(); + bool injected_block = false; + + for (size_t i = 0; i < num_blocks; i++) + { + auto &case_block = get(block_declaration_order[i]); + if (execution_is_direct_branch(case_block, default_block)) + { + // Fallthrough to default block, we must inject the default block here. + block_declaration_order.insert(begin(block_declaration_order) + i + 1, block.default_block); + injected_block = true; + break; + } + else if (execution_is_direct_branch(default_block, case_block)) + { + // Default case is falling through to another case label, we must inject the default block here. + block_declaration_order.insert(begin(block_declaration_order) + i, block.default_block); + injected_block = true; + break; + } + } + + // Order does not matter. + if (!injected_block) + block_declaration_order.push_back(block.default_block); + else if (is_legacy_es()) + SPIRV_CROSS_THROW("Default case label fallthrough to other case label is not supported in ESSL 1.0."); + + case_constructs[block.default_block] = {}; + } + + size_t num_blocks = block_declaration_order.size(); + + const auto to_case_label = [](uint32_t literal, bool is_unsigned_case) -> string { + return is_unsigned_case ? convert_to_string(literal) : convert_to_string(int32_t(literal)); + }; + + const auto to_legacy_case_label = [&](uint32_t condition, const SmallVector &labels, + const char *suffix) -> string { + string ret; + size_t count = labels.size(); + for (size_t i = 0; i < count; i++) + { + if (i) + ret += " || "; + ret += join(count > 1 ? "(" : "", to_enclosed_expression(condition), " == ", labels[i], suffix, + count > 1 ? ")" : ""); + } + return ret; + }; + + // We need to deal with a complex scenario for OpPhi. If we have case-fallthrough and Phi in the picture, + // we need to flush phi nodes outside the switch block in a branch, + // and skip any Phi handling inside the case label to make fall-through work as expected. + // This kind of code-gen is super awkward and it's a last resort. Normally we would want to handle this + // inside the case label if at all possible. + for (size_t i = 1; backend.support_case_fallthrough && i < num_blocks; i++) + { + if (flush_phi_required(block.self, block_declaration_order[i]) && + flush_phi_required(block_declaration_order[i - 1], block_declaration_order[i])) + { + uint32_t target_block = block_declaration_order[i]; + + // Make sure we flush Phi, it might have been marked to be ignored earlier. + get(target_block).ignore_phi_from_block = 0; + + auto &literals = case_constructs[target_block]; + + if (literals.empty()) + { + // Oh boy, gotta make a complete negative test instead! o.o + // Find all possible literals that would *not* make us enter the default block. + // If none of those literals match, we flush Phi ... + SmallVector conditions; + for (size_t j = 0; j < num_blocks; j++) + { + auto &negative_literals = case_constructs[block_declaration_order[j]]; + for (auto &case_label : negative_literals) + conditions.push_back(join(to_enclosed_expression(block.condition), + " != ", to_case_label(case_label, unsigned_case))); + } + + statement("if (", merge(conditions, " && "), ")"); + begin_scope(); + flush_phi(block.self, target_block); + end_scope(); + } + else + { + SmallVector conditions; + conditions.reserve(literals.size()); + for (auto &case_label : literals) + conditions.push_back(join(to_enclosed_expression(block.condition), + " == ", to_case_label(case_label, unsigned_case))); + statement("if (", merge(conditions, " || "), ")"); + begin_scope(); + flush_phi(block.self, target_block); + end_scope(); + } + + // Mark the block so that we don't flush Phi from header to case label. + get(target_block).ignore_phi_from_block = block.self; + } + } + + // If there is only one default block, and no cases, this is a case where SPIRV-opt decided to emulate + // non-structured exits with the help of a switch block. + // This is buggy on FXC, so just emit the logical equivalent of a do { } while(false), which is more idiomatic. + bool degenerate_switch = block.default_block != block.merge_block && block.cases.empty(); + + if (degenerate_switch || is_legacy_es()) + { + // ESSL 1.0 is not guaranteed to support do/while. + if (is_legacy_es()) + { + uint32_t counter = statement_count; + statement("for (int SPIRV_Cross_Dummy", counter, " = 0; SPIRV_Cross_Dummy", counter, + " < 1; SPIRV_Cross_Dummy", counter, "++)"); + } + else + statement("do"); + } + else + { + emit_block_hints(block); + statement("switch (", to_expression(block.condition), ")"); + } + begin_scope(); + + for (size_t i = 0; i < num_blocks; i++) + { + uint32_t target_block = block_declaration_order[i]; + auto &literals = case_constructs[target_block]; + + if (literals.empty()) + { + // Default case. + if (!degenerate_switch) + { + if (is_legacy_es()) + statement("else"); + else + statement("default:"); + } + } + else + { + if (is_legacy_es()) + { + statement((i ? "else " : ""), "if (", to_legacy_case_label(block.condition, literals, label_suffix), + ")"); + } + else + { + for (auto &case_literal : literals) + { + // The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here. + statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":"); + } + } + } + + auto &case_block = get(target_block); + if (backend.support_case_fallthrough && i + 1 < num_blocks && + execution_is_direct_branch(case_block, get(block_declaration_order[i + 1]))) + { + // We will fall through here, so just terminate the block chain early. + // We still need to deal with Phi potentially. + // No need for a stack-like thing here since we only do fall-through when there is a + // single trivial branch to fall-through target.. + current_emitting_switch_fallthrough = true; + } + else + current_emitting_switch_fallthrough = false; + + if (!degenerate_switch) + begin_scope(); + branch(block.self, target_block); + if (!degenerate_switch) + end_scope(); + + current_emitting_switch_fallthrough = false; + } + + // Might still have to flush phi variables if we branch from loop header directly to merge target. + if (flush_phi_required(block.self, block.next_block)) + { + if (block.default_block == block.next_block || !literals_to_merge.empty()) + { + for (auto &case_literal : literals_to_merge) + statement("case ", to_case_label(case_literal, unsigned_case), label_suffix, ":"); + + if (block.default_block == block.next_block) + { + if (is_legacy_es()) + statement("else"); + else + statement("default:"); + } + + begin_scope(); + flush_phi(block.self, block.next_block); + statement("break;"); + end_scope(); + } + } + + if (degenerate_switch && !is_legacy_es()) + end_scope_decl("while(false)"); + else + end_scope(); + + if (block.need_ladder_break) + { + statement("if (_", block.self, "_ladder_break)"); + begin_scope(); + statement("break;"); + end_scope(); + } + + current_emitting_switch = old_emitting_switch; + break; + } + + case SPIRBlock::Return: + { + for (auto &line : current_function->fixup_hooks_out) + line(); + + if (processing_entry_point) + emit_fixup(); + + auto &cfg = get_cfg_for_current_function(); + + if (block.return_value) + { + auto &type = expression_type(block.return_value); + if (!type.array.empty() && !backend.can_return_array) + { + // If we cannot return arrays, we will have a special out argument we can write to instead. + // The backend is responsible for setting this up, and redirection the return values as appropriate. + if (ir.ids[block.return_value].get_type() != TypeUndef) + { + emit_array_copy("SPIRV_Cross_return_value", block.return_value, StorageClassFunction, + get_expression_effective_storage_class(block.return_value)); + } + + if (!cfg.node_terminates_control_flow_in_sub_graph(current_function->entry_block, block.self) || + block.loop_dominator != BlockID(SPIRBlock::NoDominator)) + { + statement("return;"); + } + } + else + { + // OpReturnValue can return Undef, so don't emit anything for this case. + if (ir.ids[block.return_value].get_type() != TypeUndef) + statement("return ", to_expression(block.return_value), ";"); + } + } + else if (!cfg.node_terminates_control_flow_in_sub_graph(current_function->entry_block, block.self) || + block.loop_dominator != BlockID(SPIRBlock::NoDominator)) + { + // If this block is the very final block and not called from control flow, + // we do not need an explicit return which looks out of place. Just end the function here. + // In the very weird case of for(;;) { return; } executing return is unconditional, + // but we actually need a return here ... + statement("return;"); + } + break; + } + + case SPIRBlock::Kill: + statement(backend.discard_literal, ";"); + break; + + case SPIRBlock::Unreachable: + emit_next_block = false; + break; + + default: + SPIRV_CROSS_THROW("Unimplemented block terminator."); + } + + if (block.next_block && emit_next_block) + { + // If we hit this case, we're dealing with an unconditional branch, which means we will output + // that block after this. If we had selection merge, we already flushed phi variables. + if (block.merge != SPIRBlock::MergeSelection) + { + flush_phi(block.self, block.next_block); + // For a direct branch, need to remember to invalidate expressions in the next linear block instead. + get(block.next_block).invalidate_expressions = block.invalidate_expressions; + } + + // For switch fallthrough cases, we terminate the chain here, but we still need to handle Phi. + if (!current_emitting_switch_fallthrough) + { + // For merge selects we might have ignored the fact that a merge target + // could have been a break; or continue; + // We will need to deal with it here. + if (is_loop_break(block.next_block)) + { + // Cannot check for just break, because switch statements will also use break. + assert(block.merge == SPIRBlock::MergeSelection); + statement("break;"); + } + else if (is_continue(block.next_block)) + { + assert(block.merge == SPIRBlock::MergeSelection); + branch_to_continue(block.self, block.next_block); + } + else if (BlockID(block.self) != block.next_block) + emit_block_chain(get(block.next_block)); + } + } + + if (block.merge == SPIRBlock::MergeLoop) + { + if (continue_type == SPIRBlock::DoWhileLoop) + { + // Make sure that we run the continue block to get the expressions set, but this + // should become an empty string. + // We have no fallbacks if we cannot forward everything to temporaries ... + const auto &continue_block = get(block.continue_block); + bool positive_test = execution_is_noop(get(continue_block.true_block), + get(continue_block.loop_dominator)); + + uint32_t current_count = statement_count; + auto statements = emit_continue_block(block.continue_block, positive_test, !positive_test); + if (statement_count != current_count) + { + // The DoWhile block has side effects, force ComplexLoop pattern next pass. + get(block.continue_block).complex_continue = true; + force_recompile(); + } + + // Might have to invert the do-while test here. + auto condition = to_expression(continue_block.condition); + if (!positive_test) + condition = join("!", enclose_expression(condition)); + + end_scope_decl(join("while (", condition, ")")); + } + else + end_scope(); + + loop_level_saver.release(); + + // We cannot break out of two loops at once, so don't check for break; here. + // Using block.self as the "from" block isn't quite right, but it has the same scope + // and dominance structure, so it's fine. + if (is_continue(block.merge_block)) + branch_to_continue(block.self, block.merge_block); + else + emit_block_chain(get(block.merge_block)); + } + + // Forget about control dependent expressions now. + block.invalidate_expressions.clear(); + + // After we return, we must be out of scope, so if we somehow have to re-emit this function, + // re-declare variables if necessary. + assert(rearm_dominated_variables.size() == block.dominated_variables.size()); + for (size_t i = 0; i < block.dominated_variables.size(); i++) + { + uint32_t var = block.dominated_variables[i]; + get(var).deferred_declaration = rearm_dominated_variables[i]; + } + + // Just like for deferred declaration, we need to forget about loop variable enable + // if our block chain is reinstantiated later. + for (auto &var_id : block.loop_variables) + get(var_id).loop_variable_enable = false; +} + +void CompilerGLSL::begin_scope() +{ + statement("{"); + indent++; +} + +void CompilerGLSL::end_scope() +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("}"); +} + +void CompilerGLSL::end_scope(const string &trailer) +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("}", trailer); +} + +void CompilerGLSL::end_scope_decl() +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("};"); +} + +void CompilerGLSL::end_scope_decl(const string &decl) +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("} ", decl, ";"); +} + +void CompilerGLSL::check_function_call_constraints(const uint32_t *args, uint32_t length) +{ + // If our variable is remapped, and we rely on type-remapping information as + // well, then we cannot pass the variable as a function parameter. + // Fixing this is non-trivial without stamping out variants of the same function, + // so for now warn about this and suggest workarounds instead. + for (uint32_t i = 0; i < length; i++) + { + auto *var = maybe_get(args[i]); + if (!var || !var->remapped_variable) + continue; + + auto &type = get(var->basetype); + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData) + { + SPIRV_CROSS_THROW("Tried passing a remapped subpassInput variable to a function. " + "This will not work correctly because type-remapping information is lost. " + "To workaround, please consider not passing the subpass input as a function parameter, " + "or use in/out variables instead which do not need type remapping information."); + } + } +} + +const Instruction *CompilerGLSL::get_next_instruction_in_block(const Instruction &instr) +{ + // FIXME: This is kind of hacky. There should be a cleaner way. + auto offset = uint32_t(&instr - current_emitting_block->ops.data()); + if ((offset + 1) < current_emitting_block->ops.size()) + return ¤t_emitting_block->ops[offset + 1]; + else + return nullptr; +} + +uint32_t CompilerGLSL::mask_relevant_memory_semantics(uint32_t semantics) +{ + return semantics & (MemorySemanticsAtomicCounterMemoryMask | MemorySemanticsImageMemoryMask | + MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask | + MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask); +} + +void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageClass, StorageClass) +{ + statement(lhs, " = ", to_expression(rhs_id), ";"); +} + +void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr) +{ + if (!backend.force_gl_in_out_block) + return; + // This path is only relevant for GL backends. + + auto *var = maybe_get(source_id); + if (!var) + return; + + if (var->storage != StorageClassInput) + return; + + auto &type = get_variable_data_type(*var); + if (type.array.empty()) + return; + + auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn)); + bool is_builtin = is_builtin_variable(*var) && (builtin == BuiltInPointSize || builtin == BuiltInPosition); + bool is_tess = is_tessellation_shader(); + bool is_patch = has_decoration(var->self, DecorationPatch); + + // Tessellation input arrays are special in that they are unsized, so we cannot directly copy from it. + // We must unroll the array load. + // For builtins, we couldn't catch this case normally, + // because this is resolved in the OpAccessChain in most cases. + // If we load the entire array, we have no choice but to unroll here. + if (!is_patch && (is_builtin || is_tess)) + { + auto new_expr = join("_", target_id, "_unrolled"); + statement(variable_decl(type, new_expr, target_id), ";"); + string array_expr; + if (type.array_size_literal.back()) + { + array_expr = convert_to_string(type.array.back()); + if (type.array.back() == 0) + SPIRV_CROSS_THROW("Cannot unroll an array copy from unsized array."); + } + else + array_expr = to_expression(type.array.back()); + + // The array size might be a specialization constant, so use a for-loop instead. + statement("for (int i = 0; i < int(", array_expr, "); i++)"); + begin_scope(); + if (is_builtin) + statement(new_expr, "[i] = gl_in[i].", expr, ";"); + else + statement(new_expr, "[i] = ", expr, "[i];"); + end_scope(); + + expr = move(new_expr); + } +} + +void CompilerGLSL::cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) +{ + auto *var = maybe_get_backing_variable(source_id); + if (var) + source_id = var->self; + + // Only interested in standalone builtin variables. + if (!has_decoration(source_id, DecorationBuiltIn)) + return; + + auto builtin = static_cast(get_decoration(source_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + + // TODO: Fill in for more builtins. + switch (builtin) + { + case BuiltInLayer: + case BuiltInPrimitiveId: + case BuiltInViewportIndex: + case BuiltInInstanceId: + case BuiltInInstanceIndex: + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInSampleId: + case BuiltInBaseVertex: + case BuiltInBaseInstance: + case BuiltInDrawIndex: + case BuiltInFragStencilRefEXT: + case BuiltInInstanceCustomIndexNV: + expected_type = SPIRType::Int; + break; + + case BuiltInGlobalInvocationId: + case BuiltInLocalInvocationId: + case BuiltInWorkgroupId: + case BuiltInLocalInvocationIndex: + case BuiltInWorkgroupSize: + case BuiltInNumWorkgroups: + case BuiltInIncomingRayFlagsNV: + case BuiltInLaunchIdNV: + case BuiltInLaunchSizeNV: + expected_type = SPIRType::UInt; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + expr = bitcast_expression(expr_type, expected_type, expr); +} + +void CompilerGLSL::cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) +{ + // Only interested in standalone builtin variables. + if (!has_decoration(target_id, DecorationBuiltIn)) + return; + + auto builtin = static_cast(get_decoration(target_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + + // TODO: Fill in for more builtins. + switch (builtin) + { + case BuiltInLayer: + case BuiltInPrimitiveId: + case BuiltInViewportIndex: + case BuiltInFragStencilRefEXT: + expected_type = SPIRType::Int; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + { + auto type = expr_type; + type.basetype = expected_type; + expr = bitcast_expression(type, expr_type.basetype, expr); + } +} + +void CompilerGLSL::convert_non_uniform_expression(const SPIRType &type, std::string &expr) +{ + if (*backend.nonuniform_qualifier == '\0') + return; + + // Handle SPV_EXT_descriptor_indexing. + if (type.basetype == SPIRType::Sampler || type.basetype == SPIRType::SampledImage || + type.basetype == SPIRType::Image) + { + // The image/sampler ID must be declared as non-uniform. + // However, it is not legal GLSL to have + // nonuniformEXT(samplers[index]), so we must move the nonuniform qualifier + // to the array indexing, like + // samplers[nonuniformEXT(index)]. + // While the access chain will generally be nonuniformEXT, it's not necessarily so, + // so we might have to fixup the OpLoad-ed expression late. + + auto start_array_index = expr.find_first_of('['); + + if (start_array_index == string::npos) + return; + + // Check for the edge case that a non-arrayed resource was marked to be nonuniform, + // and the bracket we found is actually part of non-resource related data. + if (expr.find_first_of(',') < start_array_index) + return; + + // We've opened a bracket, track expressions until we can close the bracket. + // This must be our image index. + size_t end_array_index = string::npos; + unsigned bracket_count = 1; + for (size_t index = start_array_index + 1; index < expr.size(); index++) + { + if (expr[index] == ']') + { + if (--bracket_count == 0) + { + end_array_index = index; + break; + } + } + else if (expr[index] == '[') + bracket_count++; + } + + assert(bracket_count == 0); + + // Doesn't really make sense to declare a non-arrayed image with nonuniformEXT, but there's + // nothing we can do here to express that. + if (start_array_index == string::npos || end_array_index == string::npos || end_array_index < start_array_index) + return; + + start_array_index++; + + expr = join(expr.substr(0, start_array_index), backend.nonuniform_qualifier, "(", + expr.substr(start_array_index, end_array_index - start_array_index), ")", + expr.substr(end_array_index, string::npos)); + } +} + +void CompilerGLSL::emit_block_hints(const SPIRBlock &) +{ +} + +void CompilerGLSL::preserve_alias_on_reset(uint32_t id) +{ + preserved_aliases[id] = get_name(id); +} + +void CompilerGLSL::reset_name_caches() +{ + for (auto &preserved : preserved_aliases) + set_name(preserved.first, preserved.second); + + preserved_aliases.clear(); + resource_names.clear(); + block_input_names.clear(); + block_output_names.clear(); + block_ubo_names.clear(); + block_ssbo_names.clear(); + block_names.clear(); + function_overloads.clear(); +} + +void CompilerGLSL::fixup_type_alias() +{ + // Due to how some backends work, the "master" type of type_alias must be a block-like type if it exists. + ir.for_each_typed_id([&](uint32_t self, SPIRType &type) { + if (!type.type_alias) + return; + + if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock)) + { + // Top-level block types should never alias anything else. + type.type_alias = 0; + } + else if (type_is_block_like(type) && type.self == ID(self)) + { + // A block-like type is any type which contains Offset decoration, but not top-level blocks, + // i.e. blocks which are placed inside buffers. + // Become the master. + ir.for_each_typed_id([&](uint32_t other_id, SPIRType &other_type) { + if (other_id == self) + return; + + if (other_type.type_alias == type.type_alias) + other_type.type_alias = self; + }); + + this->get(type.type_alias).type_alias = self; + type.type_alias = 0; + } + }); +} + +void CompilerGLSL::reorder_type_alias() +{ + // Reorder declaration of types so that the master of the type alias is always emitted first. + // We need this in case a type B depends on type A (A must come before in the vector), but A is an alias of a type Abuffer, which + // means declaration of A doesn't happen (yet), and order would be B, ABuffer and not ABuffer, B. Fix this up here. + auto loop_lock = ir.create_loop_hard_lock(); + + auto &type_ids = ir.ids_for_type[TypeType]; + for (auto alias_itr = begin(type_ids); alias_itr != end(type_ids); ++alias_itr) + { + auto &type = get(*alias_itr); + if (type.type_alias != TypeID(0) && + !has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + { + // We will skip declaring this type, so make sure the type_alias type comes before. + auto master_itr = find(begin(type_ids), end(type_ids), ID(type.type_alias)); + assert(master_itr != end(type_ids)); + + if (alias_itr < master_itr) + { + // Must also swap the type order for the constant-type joined array. + auto &joined_types = ir.ids_for_constant_or_type; + auto alt_alias_itr = find(begin(joined_types), end(joined_types), *alias_itr); + auto alt_master_itr = find(begin(joined_types), end(joined_types), *master_itr); + assert(alt_alias_itr != end(joined_types)); + assert(alt_master_itr != end(joined_types)); + + swap(*alias_itr, *master_itr); + swap(*alt_alias_itr, *alt_master_itr); + } + } + } +} + +void CompilerGLSL::emit_line_directive(uint32_t file_id, uint32_t line_literal) +{ + // If we are redirecting statements, ignore the line directive. + // Common case here is continue blocks. + if (redirect_statement) + return; + + if (options.emit_line_directives) + { + require_extension_internal("GL_GOOGLE_cpp_style_line_directive"); + statement_no_indent("#line ", line_literal, " \"", get(file_id).str, "\""); + } +} + +void CompilerGLSL::propagate_nonuniform_qualifier(uint32_t id) +{ + // SPIR-V might only tag the very last ID with NonUniformEXT, but for codegen, + // we need to know NonUniformEXT a little earlier, when the resource is actually loaded. + // Back-propagate the qualifier based on the expression dependency chain. + + if (!has_decoration(id, DecorationNonUniformEXT)) + { + set_decoration(id, DecorationNonUniformEXT); + force_recompile(); + } + + auto *e = maybe_get(id); + auto *combined = maybe_get(id); + auto *chain = maybe_get(id); + if (e) + { + for (auto &expr : e->expression_dependencies) + propagate_nonuniform_qualifier(expr); + for (auto &expr : e->implied_read_expressions) + propagate_nonuniform_qualifier(expr); + } + else if (combined) + { + propagate_nonuniform_qualifier(combined->image); + propagate_nonuniform_qualifier(combined->sampler); + } + else if (chain) + { + for (auto &expr : chain->implied_read_expressions) + propagate_nonuniform_qualifier(expr); + } +} + +void CompilerGLSL::emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id, + SmallVector chain) +{ + // Fully unroll all member/array indices one by one. + + auto &lhs_type = get(lhs_type_id); + auto &rhs_type = get(rhs_type_id); + + if (!lhs_type.array.empty()) + { + // Could use a loop here to support specialization constants, but it gets rather complicated with nested array types, + // and this is a rather obscure opcode anyways, keep it simple unless we are forced to. + uint32_t array_size = to_array_size_literal(lhs_type); + chain.push_back(0); + + for (uint32_t i = 0; i < array_size; i++) + { + chain.back() = i; + emit_copy_logical_type(lhs_id, lhs_type.parent_type, rhs_id, rhs_type.parent_type, chain); + } + } + else if (lhs_type.basetype == SPIRType::Struct) + { + chain.push_back(0); + uint32_t member_count = uint32_t(lhs_type.member_types.size()); + for (uint32_t i = 0; i < member_count; i++) + { + chain.back() = i; + emit_copy_logical_type(lhs_id, lhs_type.member_types[i], rhs_id, rhs_type.member_types[i], chain); + } + } + else + { + // Need to handle unpack/packing fixups since this can differ wildly between the logical types, + // particularly in MSL. + // To deal with this, we emit access chains and go through emit_store_statement + // to deal with all the special cases we can encounter. + + AccessChainMeta lhs_meta, rhs_meta; + auto lhs = access_chain_internal(lhs_id, chain.data(), uint32_t(chain.size()), + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &lhs_meta); + auto rhs = access_chain_internal(rhs_id, chain.data(), uint32_t(chain.size()), + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &rhs_meta); + + uint32_t id = ir.increase_bound_by(2); + lhs_id = id; + rhs_id = id + 1; + + { + auto &lhs_expr = set(lhs_id, move(lhs), lhs_type_id, true); + lhs_expr.need_transpose = lhs_meta.need_transpose; + + if (lhs_meta.storage_is_packed) + set_extended_decoration(lhs_id, SPIRVCrossDecorationPhysicalTypePacked); + if (lhs_meta.storage_physical_type != 0) + set_extended_decoration(lhs_id, SPIRVCrossDecorationPhysicalTypeID, lhs_meta.storage_physical_type); + + forwarded_temporaries.insert(lhs_id); + suppressed_usage_tracking.insert(lhs_id); + } + + { + auto &rhs_expr = set(rhs_id, move(rhs), rhs_type_id, true); + rhs_expr.need_transpose = rhs_meta.need_transpose; + + if (rhs_meta.storage_is_packed) + set_extended_decoration(rhs_id, SPIRVCrossDecorationPhysicalTypePacked); + if (rhs_meta.storage_physical_type != 0) + set_extended_decoration(rhs_id, SPIRVCrossDecorationPhysicalTypeID, rhs_meta.storage_physical_type); + + forwarded_temporaries.insert(rhs_id); + suppressed_usage_tracking.insert(rhs_id); + } + + emit_store_statement(lhs_id, rhs_id); + } +} + +bool CompilerGLSL::subpass_input_is_framebuffer_fetch(uint32_t id) const +{ + if (!has_decoration(id, DecorationInputAttachmentIndex)) + return false; + + uint32_t input_attachment_index = get_decoration(id, DecorationInputAttachmentIndex); + for (auto &remap : subpass_to_framebuffer_fetch_attachment) + if (remap.first == input_attachment_index) + return true; + + return false; +} + +const SPIRVariable *CompilerGLSL::find_subpass_input_by_attachment_index(uint32_t index) const +{ + const SPIRVariable *ret = nullptr; + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (has_decoration(var.self, DecorationInputAttachmentIndex) && + get_decoration(var.self, DecorationInputAttachmentIndex) == index) + { + ret = &var; + } + }); + return ret; +} + +const SPIRVariable *CompilerGLSL::find_color_output_by_location(uint32_t location) const +{ + const SPIRVariable *ret = nullptr; + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (var.storage == StorageClassOutput && get_decoration(var.self, DecorationLocation) == location) + ret = &var; + }); + return ret; +} + +void CompilerGLSL::emit_inout_fragment_outputs_copy_to_subpass_inputs() +{ + for (auto &remap : subpass_to_framebuffer_fetch_attachment) + { + auto *subpass_var = find_subpass_input_by_attachment_index(remap.first); + auto *output_var = find_color_output_by_location(remap.second); + if (!subpass_var) + continue; + if (!output_var) + SPIRV_CROSS_THROW("Need to declare the corresponding fragment output variable to be able " + "to read from it."); + if (is_array(get(output_var->basetype))) + SPIRV_CROSS_THROW("Cannot use GL_EXT_shader_framebuffer_fetch with arrays of color outputs."); + + auto &func = get(get_entry_point().self); + func.fixup_hooks_in.push_back([=]() { + if (is_legacy()) + { + statement(to_expression(subpass_var->self), " = ", "gl_LastFragData[", + get_decoration(output_var->self, DecorationLocation), "];"); + } + else + { + uint32_t num_rt_components = this->get(output_var->basetype).vecsize; + statement(to_expression(subpass_var->self), vector_swizzle(num_rt_components, 0), " = ", + to_expression(output_var->self), ";"); + } + }); + } +} + +bool CompilerGLSL::variable_is_depth_or_compare(VariableID id) const +{ + return image_is_comparison(get(get(id).basetype), id); +} + +const char *CompilerGLSL::ShaderSubgroupSupportHelper::get_extension_name(Candidate c) +{ + static const char *const retval[CandidateCount] = { "GL_KHR_shader_subgroup_ballot", + "GL_KHR_shader_subgroup_basic", + "GL_KHR_shader_subgroup_vote", + "GL_NV_gpu_shader_5", + "GL_NV_shader_thread_group", + "GL_NV_shader_thread_shuffle", + "GL_ARB_shader_ballot", + "GL_ARB_shader_group_vote", + "GL_AMD_gcn_shader" }; + return retval[c]; +} + +SmallVector CompilerGLSL::ShaderSubgroupSupportHelper::get_extra_required_extension_names(Candidate c) +{ + switch (c) + { + case ARB_shader_ballot: + return { "GL_ARB_shader_int64" }; + case AMD_gcn_shader: + return { "GL_AMD_gpu_shader_int64", "GL_NV_gpu_shader5" }; + default: + return {}; + } +} + +const char *CompilerGLSL::ShaderSubgroupSupportHelper::get_extra_required_extension_predicate(Candidate c) +{ + switch (c) + { + case ARB_shader_ballot: + return "defined(GL_ARB_shader_int64)"; + case AMD_gcn_shader: + return "(defined(GL_AMD_gpu_shader_int64) || defined(GL_NV_gpu_shader5))"; + default: + return ""; + } +} + +CompilerGLSL::ShaderSubgroupSupportHelper::FeatureVector CompilerGLSL::ShaderSubgroupSupportHelper:: + get_feature_dependencies(Feature feature) +{ + switch (feature) + { + case SubgroupAllEqualT: + return { SubgroupBrodcast_First, SubgroupAll_Any_AllEqualBool }; + case SubgroupElect: + return { SubgroupBallotFindLSB_MSB, SubgroupBallot, SubgroupInvocationID }; + case SubgroupInverseBallot_InclBitCount_ExclBitCout: + return { SubgroupMask }; + case SubgroupBallotBitCount: + return { SubgroupBallot }; + default: + return {}; + } +} + +CompilerGLSL::ShaderSubgroupSupportHelper::FeatureMask CompilerGLSL::ShaderSubgroupSupportHelper:: + get_feature_dependency_mask(Feature feature) +{ + return build_mask(get_feature_dependencies(feature)); +} + +bool CompilerGLSL::ShaderSubgroupSupportHelper::can_feature_be_implemented_without_extensions(Feature feature) +{ + static const bool retval[FeatureCount] = { false, false, false, false, false, false, + true, // SubgroupBalloFindLSB_MSB + false, false, false, false, + true, // SubgroupMemBarrier - replaced with workgroup memory barriers + false, false, true, false }; + + return retval[feature]; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::Candidate CompilerGLSL::ShaderSubgroupSupportHelper:: + get_KHR_extension_for_feature(Feature feature) +{ + static const Candidate extensions[FeatureCount] = { + KHR_shader_subgroup_ballot, KHR_shader_subgroup_basic, KHR_shader_subgroup_basic, KHR_shader_subgroup_basic, + KHR_shader_subgroup_basic, KHR_shader_subgroup_ballot, KHR_shader_subgroup_ballot, KHR_shader_subgroup_vote, + KHR_shader_subgroup_vote, KHR_shader_subgroup_basic, KHR_shader_subgroup_ballot, KHR_shader_subgroup_basic, + KHR_shader_subgroup_basic, KHR_shader_subgroup_ballot, KHR_shader_subgroup_ballot, KHR_shader_subgroup_ballot + }; + + return extensions[feature]; +} + +void CompilerGLSL::ShaderSubgroupSupportHelper::request_feature(Feature feature) +{ + feature_mask |= (FeatureMask(1) << feature) | get_feature_dependency_mask(feature); +} + +bool CompilerGLSL::ShaderSubgroupSupportHelper::is_feature_requested(Feature feature) const +{ + return (feature_mask & (1u << feature)) != 0; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::Result CompilerGLSL::ShaderSubgroupSupportHelper::resolve() const +{ + Result res; + + for (uint32_t i = 0u; i < FeatureCount; ++i) + { + if (feature_mask & (1u << i)) + { + auto feature = static_cast(i); + std::unordered_set unique_candidates; + + auto candidates = get_candidates_for_feature(feature); + unique_candidates.insert(candidates.begin(), candidates.end()); + + auto deps = get_feature_dependencies(feature); + for (Feature d : deps) + { + candidates = get_candidates_for_feature(d); + if (!candidates.empty()) + unique_candidates.insert(candidates.begin(), candidates.end()); + } + + for (uint32_t c : unique_candidates) + ++res.weights[static_cast(c)]; + } + } + + return res; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::CandidateVector CompilerGLSL::ShaderSubgroupSupportHelper:: + get_candidates_for_feature(Feature ft, const Result &r) +{ + auto c = get_candidates_for_feature(ft); + auto cmp = [&r](Candidate a, Candidate b) { + if (r.weights[a] == r.weights[b]) + return a < b; // Prefer candidates with lower enum value + return r.weights[a] > r.weights[b]; + }; + std::sort(c.begin(), c.end(), cmp); + return c; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::CandidateVector CompilerGLSL::ShaderSubgroupSupportHelper:: + get_candidates_for_feature(Feature feature) +{ + switch (feature) + { + case SubgroupMask: + return { KHR_shader_subgroup_ballot, NV_shader_thread_group, ARB_shader_ballot }; + case SubgroupSize: + return { KHR_shader_subgroup_basic, NV_shader_thread_group, AMD_gcn_shader, ARB_shader_ballot }; + case SubgroupInvocationID: + return { KHR_shader_subgroup_basic, NV_shader_thread_group, ARB_shader_ballot }; + case SubgroupID: + return { KHR_shader_subgroup_basic, NV_shader_thread_group }; + case NumSubgroups: + return { KHR_shader_subgroup_basic, NV_shader_thread_group }; + case SubgroupBrodcast_First: + return { KHR_shader_subgroup_ballot, NV_shader_thread_shuffle, ARB_shader_ballot }; + case SubgroupBallotFindLSB_MSB: + return { KHR_shader_subgroup_ballot, NV_shader_thread_group }; + case SubgroupAll_Any_AllEqualBool: + return { KHR_shader_subgroup_vote, NV_gpu_shader_5, ARB_shader_group_vote, AMD_gcn_shader }; + case SubgroupAllEqualT: + return {}; // depends on other features only + case SubgroupElect: + return {}; // depends on other features only + case SubgroupBallot: + return { KHR_shader_subgroup_ballot, NV_shader_thread_group, ARB_shader_ballot }; + case SubgroupBarrier: + return { KHR_shader_subgroup_basic, NV_shader_thread_group, ARB_shader_ballot, AMD_gcn_shader }; + case SubgroupMemBarrier: + return { KHR_shader_subgroup_basic }; + case SubgroupInverseBallot_InclBitCount_ExclBitCout: + return {}; + case SubgroupBallotBitExtract: + return { NV_shader_thread_group }; + case SubgroupBallotBitCount: + return {}; + default: + return {}; + } +} + +CompilerGLSL::ShaderSubgroupSupportHelper::FeatureMask CompilerGLSL::ShaderSubgroupSupportHelper::build_mask( + const SmallVector &features) +{ + FeatureMask mask = 0; + for (Feature f : features) + mask |= FeatureMask(1) << f; + return mask; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::Result::Result() +{ + for (auto &weight : weights) + weight = 0; + + // Make sure KHR_shader_subgroup extensions are always prefered. + const uint32_t big_num = FeatureCount; + weights[KHR_shader_subgroup_ballot] = big_num; + weights[KHR_shader_subgroup_basic] = big_num; + weights[KHR_shader_subgroup_vote] = big_num; +} + +void CompilerGLSL::request_workaround_wrapper_overload(TypeID id) +{ + // Must be ordered to maintain deterministic output, so vector is appropriate. + if (find(begin(workaround_ubo_load_overload_types), end(workaround_ubo_load_overload_types), id) == + end(workaround_ubo_load_overload_types)) + { + force_recompile(); + workaround_ubo_load_overload_types.push_back(id); + } +} + +void CompilerGLSL::rewrite_load_for_wrapped_row_major(std::string &expr, TypeID loaded_type, ID ptr) +{ + // Loading row-major matrices from UBOs on older AMD Windows OpenGL drivers is problematic. + // To load these types correctly, we must first wrap them in a dummy function which only purpose is to + // ensure row_major decoration is actually respected. + auto *var = maybe_get_backing_variable(ptr); + if (!var) + return; + + auto &backing_type = get(var->basetype); + bool is_ubo = backing_type.basetype == SPIRType::Struct && backing_type.storage == StorageClassUniform && + has_decoration(backing_type.self, DecorationBlock); + if (!is_ubo) + return; + + auto *type = &get(loaded_type); + bool rewrite = false; + + if (is_matrix(*type)) + { + // To avoid adding a lot of unnecessary meta tracking to forward the row_major state, + // we will simply look at the base struct itself. It is exceptionally rare to mix and match row-major/col-major state. + // If there is any row-major action going on, we apply the workaround. + // It is harmless to apply the workaround to column-major matrices, so this is still a valid solution. + // If an access chain occurred, the workaround is not required, so loading vectors or scalars don't need workaround. + type = &backing_type; + } + + if (type->basetype == SPIRType::Struct) + { + // If we're loading a struct where any member is a row-major matrix, apply the workaround. + for (uint32_t i = 0; i < uint32_t(type->member_types.size()); i++) + { + if (combined_decoration_for_member(*type, i).get(DecorationRowMajor)) + { + rewrite = true; + break; + } + } + } + + if (rewrite) + { + request_workaround_wrapper_overload(loaded_type); + expr = join("SPIRV_Cross_workaround_load_row_major(", expr, ")"); + } +} diff --git a/third_party/spirv-cross/spirv_glsl.hpp b/third_party/spirv-cross/spirv_glsl.hpp new file mode 100644 index 0000000..fe0f76d --- /dev/null +++ b/third_party/spirv-cross/spirv_glsl.hpp @@ -0,0 +1,896 @@ +/* + * Copyright 2015-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_GLSL_HPP +#define SPIRV_CROSS_GLSL_HPP + +#include "GLSL.std.450.h" +#include "spirv_cross.hpp" +#include +#include +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +enum PlsFormat +{ + PlsNone = 0, + + PlsR11FG11FB10F, + PlsR32F, + PlsRG16F, + PlsRGB10A2, + PlsRGBA8, + PlsRG16, + + PlsRGBA8I, + PlsRG16I, + + PlsRGB10A2UI, + PlsRGBA8UI, + PlsRG16UI, + PlsR32UI +}; + +struct PlsRemap +{ + uint32_t id; + PlsFormat format; +}; + +enum AccessChainFlagBits +{ + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT = 1 << 0, + ACCESS_CHAIN_CHAIN_ONLY_BIT = 1 << 1, + ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2, + ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3, + ACCESS_CHAIN_LITERAL_MSB_FORCE_ID = 1 << 4, + ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT = 1 << 5 +}; +typedef uint32_t AccessChainFlags; + +class CompilerGLSL : public Compiler +{ +public: + struct Options + { + // The shading language version. Corresponds to #version $VALUE. + uint32_t version = 450; + + // Emit the OpenGL ES shading language instead of desktop OpenGL. + bool es = false; + + // Debug option to always emit temporary variables for all expressions. + bool force_temporary = false; + + // If true, Vulkan GLSL features are used instead of GL-compatible features. + // Mostly useful for debugging SPIR-V files. + bool vulkan_semantics = false; + + // If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders. + // The members of gl_PerVertex is determined by which built-ins are declared by the shader. + // This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension + // (EXT_shader_io_blocks) which makes things a bit more fuzzy. + bool separate_shader_objects = false; + + // Flattens multidimensional arrays, e.g. float foo[a][b][c] into single-dimensional arrays, + // e.g. float foo[a * b * c]. + // This function does not change the actual SPIRType of any object. + // Only the generated code, including declarations of interface variables are changed to be single array dimension. + bool flatten_multidimensional_arrays = false; + + // For older desktop GLSL targets than version 420, the + // GL_ARB_shading_language_420pack extensions is used to be able to support + // layout(binding) on UBOs and samplers. + // If disabled on older targets, binding decorations will be stripped. + bool enable_420pack_extension = true; + + // In non-Vulkan GLSL, emit push constant blocks as UBOs rather than plain uniforms. + bool emit_push_constant_as_uniform_buffer = false; + + // Always emit uniform blocks as plain uniforms, regardless of the GLSL version, even when UBOs are supported. + // Does not apply to shader storage or push constant blocks. + bool emit_uniform_buffer_as_plain_uniforms = false; + + // Emit OpLine directives if present in the module. + // May not correspond exactly to original source, but should be a good approximation. + bool emit_line_directives = false; + + // In cases where readonly/writeonly decoration are not used at all, + // we try to deduce which qualifier(s) we should actually used, since actually emitting + // read-write decoration is very rare, and older glslang/HLSL compilers tend to just emit readwrite as a matter of fact. + // The default (true) is to enable automatic deduction for these cases, but if you trust the decorations set + // by the SPIR-V, it's recommended to set this to false. + bool enable_storage_image_qualifier_deduction = true; + + // On some targets (WebGPU), uninitialized variables are banned. + // If this is enabled, all variables (temporaries, Private, Function) + // which would otherwise be uninitialized will now be initialized to 0 instead. + bool force_zero_initialized_variables = false; + + // In GLSL, force use of I/O block flattening, similar to + // what happens on legacy GLSL targets for blocks and structs. + bool force_flattened_io_blocks = false; + + enum Precision + { + DontCare, + Lowp, + Mediump, + Highp + }; + + struct VertexOptions + { + // "Vertex-like shader" here is any shader stage that can write BuiltInPosition. + + // GLSL: In vertex-like shaders, rewrite [0, w] depth (Vulkan/D3D style) to [-w, w] depth (GL style). + // MSL: In vertex-like shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. + // HLSL: In vertex-like shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. + bool fixup_clipspace = false; + + // In vertex-like shaders, inverts gl_Position.y or equivalent. + bool flip_vert_y = false; + + // GLSL only, for HLSL version of this option, see CompilerHLSL. + // If true, the backend will assume that InstanceIndex will need to apply + // a base instance offset. Set to false if you know you will never use base instance + // functionality as it might remove some internal uniforms. + bool support_nonzero_base_instance = true; + } vertex; + + struct FragmentOptions + { + // Add precision mediump float in ES targets when emitting GLES source. + // Add precision highp int in ES targets when emitting GLES source. + Precision default_float_precision = Mediump; + Precision default_int_precision = Highp; + } fragment; + }; + + void remap_pixel_local_storage(std::vector inputs, std::vector outputs) + { + pls_inputs = std::move(inputs); + pls_outputs = std::move(outputs); + remap_pls_variables(); + } + + // Redirect a subpassInput reading from input_attachment_index to instead load its value from + // the color attachment at location = color_location. Requires ESSL. + void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location); + + explicit CompilerGLSL(std::vector spirv_) + : Compiler(std::move(spirv_)) + { + init(); + } + + CompilerGLSL(const uint32_t *ir_, size_t word_count) + : Compiler(ir_, word_count) + { + init(); + } + + explicit CompilerGLSL(const ParsedIR &ir_) + : Compiler(ir_) + { + init(); + } + + explicit CompilerGLSL(ParsedIR &&ir_) + : Compiler(std::move(ir_)) + { + init(); + } + + const Options &get_common_options() const + { + return options; + } + + void set_common_options(const Options &opts) + { + options = opts; + } + + std::string compile() override; + + // Returns the current string held in the conversion buffer. Useful for + // capturing what has been converted so far when compile() throws an error. + std::string get_partial_source(); + + // Adds a line to be added right after #version in GLSL backend. + // This is useful for enabling custom extensions which are outside the scope of SPIRV-Cross. + // This can be combined with variable remapping. + // A new-line will be added. + // + // While add_header_line() is a more generic way of adding arbitrary text to the header + // of a GLSL file, require_extension() should be used when adding extensions since it will + // avoid creating collisions with SPIRV-Cross generated extensions. + // + // Code added via add_header_line() is typically backend-specific. + void add_header_line(const std::string &str); + + // Adds an extension which is required to run this shader, e.g. + // require_extension("GL_KHR_my_extension"); + void require_extension(const std::string &ext); + + // Legacy GLSL compatibility method. + // Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead. + // For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but + // mixing int and float is not. + // The name of the uniform array will be the same as the interface block name. + void flatten_buffer_block(VariableID id); + + // After compilation, query if a variable ID was used as a depth resource. + // This is meaningful for MSL since descriptor types depend on this knowledge. + // Cases which return true: + // - Images which are declared with depth = 1 image type. + // - Samplers which are statically used at least once with Dref opcodes. + // - Images which are statically used at least once with Dref opcodes. + bool variable_is_depth_or_compare(VariableID id) const; + +protected: + struct ShaderSubgroupSupportHelper + { + // lower enum value = greater priority + enum Candidate + { + KHR_shader_subgroup_ballot, + KHR_shader_subgroup_basic, + KHR_shader_subgroup_vote, + NV_gpu_shader_5, + NV_shader_thread_group, + NV_shader_thread_shuffle, + ARB_shader_ballot, + ARB_shader_group_vote, + AMD_gcn_shader, + + CandidateCount + }; + + static const char *get_extension_name(Candidate c); + static SmallVector get_extra_required_extension_names(Candidate c); + static const char *get_extra_required_extension_predicate(Candidate c); + + enum Feature + { + SubgroupMask, + SubgroupSize, + SubgroupInvocationID, + SubgroupID, + NumSubgroups, + SubgroupBrodcast_First, + SubgroupBallotFindLSB_MSB, + SubgroupAll_Any_AllEqualBool, + SubgroupAllEqualT, + SubgroupElect, + SubgroupBarrier, + SubgroupMemBarrier, + SubgroupBallot, + SubgroupInverseBallot_InclBitCount_ExclBitCout, + SubgroupBallotBitExtract, + SubgroupBallotBitCount, + + FeatureCount + }; + + using FeatureMask = uint32_t; + static_assert(sizeof(FeatureMask) * 8u >= FeatureCount, "Mask type needs more bits."); + + using CandidateVector = SmallVector; + using FeatureVector = SmallVector; + + static FeatureVector get_feature_dependencies(Feature feature); + static FeatureMask get_feature_dependency_mask(Feature feature); + static bool can_feature_be_implemented_without_extensions(Feature feature); + static Candidate get_KHR_extension_for_feature(Feature feature); + + struct Result + { + Result(); + uint32_t weights[CandidateCount]; + }; + + void request_feature(Feature feature); + bool is_feature_requested(Feature feature) const; + Result resolve() const; + + static CandidateVector get_candidates_for_feature(Feature ft, const Result &r); + + private: + static CandidateVector get_candidates_for_feature(Feature ft); + static FeatureMask build_mask(const SmallVector &features); + FeatureMask feature_mask = 0; + }; + + // TODO remove this function when all subgroup ops are supported (or make it always return true) + static bool is_supported_subgroup_op_in_opengl(spv::Op op); + + void reset(); + void emit_function(SPIRFunction &func, const Bitset &return_flags); + + bool has_extension(const std::string &ext) const; + void require_extension_internal(const std::string &ext); + + // Virtualize methods which need to be overridden by subclass targets like C++ and such. + virtual void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags); + + SPIRBlock *current_emitting_block = nullptr; + SPIRBlock *current_emitting_switch = nullptr; + bool current_emitting_switch_fallthrough = false; + + virtual void emit_instruction(const Instruction &instr); + void emit_block_instructions(SPIRBlock &block); + virtual void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count); + virtual void emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_shader_explicit_vertex_parameter_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count); + virtual void emit_header(); + void emit_line_directive(uint32_t file_id, uint32_t line_literal); + void build_workgroup_size(SmallVector &arguments, const SpecializationConstant &x, + const SpecializationConstant &y, const SpecializationConstant &z); + + void request_subgroup_feature(ShaderSubgroupSupportHelper::Feature feature); + + virtual void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id); + virtual void emit_texture_op(const Instruction &i, bool sparse); + virtual std::string to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions); + virtual void emit_subgroup_op(const Instruction &i); + virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0); + virtual std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage); + virtual void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const std::string &qualifier = "", uint32_t base_offset = 0); + virtual void emit_struct_padding_target(const SPIRType &type); + virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0); + std::string constant_expression(const SPIRConstant &c); + std::string constant_op_expression(const SPIRConstantOp &cop); + virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector); + virtual void emit_fixup(); + virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0); + virtual std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id); + + struct TextureFunctionBaseArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionBaseArguments() = default; + VariableID img = 0; + const SPIRType *imgtype = nullptr; + bool is_fetch = false, is_gather = false, is_proj = false; + }; + + struct TextureFunctionNameArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionNameArguments() = default; + TextureFunctionBaseArguments base; + bool has_array_offsets = false, has_offset = false, has_grad = false; + bool has_dref = false, is_sparse_feedback = false, has_min_lod = false; + uint32_t lod = 0; + }; + virtual std::string to_function_name(const TextureFunctionNameArguments &args); + + struct TextureFunctionArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionArguments() = default; + TextureFunctionBaseArguments base; + uint32_t coord = 0, coord_components = 0, dref = 0; + uint32_t grad_x = 0, grad_y = 0, lod = 0, coffset = 0, offset = 0; + uint32_t bias = 0, component = 0, sample = 0, sparse_texel = 0, min_lod = 0; + }; + virtual std::string to_function_args(const TextureFunctionArguments &args, bool *p_forward); + + void emit_sparse_feedback_temporaries(uint32_t result_type_id, uint32_t id, uint32_t &feedback_id, + uint32_t &texel_id); + uint32_t get_sparse_feedback_texel_id(uint32_t id) const; + virtual void emit_buffer_block(const SPIRVariable &type); + virtual void emit_push_constant_block(const SPIRVariable &var); + virtual void emit_uniform(const SPIRVariable &var); + virtual std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, + bool packed_type, bool row_major); + + virtual bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const; + + void emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id, + SmallVector chain); + + StringStream<> buffer; + + template + inline void statement_inner(T &&t) + { + buffer << std::forward(t); + statement_count++; + } + + template + inline void statement_inner(T &&t, Ts &&... ts) + { + buffer << std::forward(t); + statement_count++; + statement_inner(std::forward(ts)...); + } + + template + inline void statement(Ts &&... ts) + { + if (is_forcing_recompilation()) + { + // Do not bother emitting code while force_recompile is active. + // We will compile again. + statement_count++; + return; + } + + if (redirect_statement) + { + redirect_statement->push_back(join(std::forward(ts)...)); + statement_count++; + } + else + { + for (uint32_t i = 0; i < indent; i++) + buffer << " "; + statement_inner(std::forward(ts)...); + buffer << '\n'; + } + } + + template + inline void statement_no_indent(Ts &&... ts) + { + auto old_indent = indent; + indent = 0; + statement(std::forward(ts)...); + indent = old_indent; + } + + // Used for implementing continue blocks where + // we want to obtain a list of statements we can merge + // on a single line separated by comma. + SmallVector *redirect_statement = nullptr; + const SPIRBlock *current_continue_block = nullptr; + + void begin_scope(); + void end_scope(); + void end_scope(const std::string &trailer); + void end_scope_decl(); + void end_scope_decl(const std::string &decl); + + Options options; + + virtual std::string type_to_array_glsl( + const SPIRType &type); // Allow Metal to use the array template to make arrays a value type + std::string to_array_size(const SPIRType &type, uint32_t index); + uint32_t to_array_size_literal(const SPIRType &type, uint32_t index) const; + uint32_t to_array_size_literal(const SPIRType &type) const; + virtual std::string variable_decl(const SPIRVariable &variable); // Threadgroup arrays can't have a wrapper type + std::string variable_decl_function_local(SPIRVariable &variable); + + void add_local_variable_name(uint32_t id); + void add_resource_name(uint32_t id); + void add_member_name(SPIRType &type, uint32_t name); + void add_function_overload(const SPIRFunction &func); + + virtual bool is_non_native_row_major_matrix(uint32_t id); + virtual bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index); + bool member_is_remapped_physical_type(const SPIRType &type, uint32_t index) const; + bool member_is_packed_physical_type(const SPIRType &type, uint32_t index) const; + virtual std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, + uint32_t physical_type_id, bool is_packed); + + std::unordered_set local_variable_names; + std::unordered_set resource_names; + std::unordered_set block_input_names; + std::unordered_set block_output_names; + std::unordered_set block_ubo_names; + std::unordered_set block_ssbo_names; + std::unordered_set block_names; // A union of all block_*_names. + std::unordered_map> function_overloads; + std::unordered_map preserved_aliases; + void preserve_alias_on_reset(uint32_t id); + void reset_name_caches(); + + bool processing_entry_point = false; + + // Can be overriden by subclass backends for trivial things which + // shouldn't need polymorphism. + struct BackendVariations + { + std::string discard_literal = "discard"; + std::string demote_literal = "demote"; + std::string null_pointer_literal = ""; + bool float_literal_suffix = false; + bool double_literal_suffix = true; + bool uint32_t_literal_suffix = true; + bool long_long_literal_suffix = false; + const char *basic_int_type = "int"; + const char *basic_uint_type = "uint"; + const char *basic_int8_type = "int8_t"; + const char *basic_uint8_type = "uint8_t"; + const char *basic_int16_type = "int16_t"; + const char *basic_uint16_type = "uint16_t"; + const char *int16_t_literal_suffix = "s"; + const char *uint16_t_literal_suffix = "us"; + const char *nonuniform_qualifier = "nonuniformEXT"; + const char *boolean_mix_function = "mix"; + bool swizzle_is_function = false; + bool shared_is_implied = false; + bool unsized_array_supported = true; + bool explicit_struct_type = false; + bool use_initializer_list = false; + bool use_typed_initializer_list = false; + bool can_declare_struct_inline = true; + bool can_declare_arrays_inline = true; + bool native_row_major_matrix = true; + bool use_constructor_splatting = true; + bool allow_precision_qualifiers = false; + bool can_swizzle_scalar = false; + bool force_gl_in_out_block = false; + bool can_return_array = true; + bool allow_truncated_access_chain = false; + bool supports_extensions = false; + bool supports_empty_struct = false; + bool array_is_value_type = true; + bool buffer_offset_array_is_value_type = true; + bool comparison_image_samples_scalar = false; + bool native_pointers = false; + bool support_small_type_sampling_result = false; + bool support_case_fallthrough = true; + bool use_array_constructor = false; + bool needs_row_major_load_workaround = false; + } backend; + + void emit_struct(SPIRType &type); + void emit_resources(); + void emit_extension_workarounds(spv::ExecutionModel model); + void emit_buffer_block_native(const SPIRVariable &var); + void emit_buffer_reference_block(SPIRType &type, bool forward_declaration); + void emit_buffer_block_legacy(const SPIRVariable &var); + void emit_buffer_block_flattened(const SPIRVariable &type); + void fixup_implicit_builtin_block_names(); + void emit_declared_builtin_block(spv::StorageClass storage, spv::ExecutionModel model); + bool should_force_emit_builtin_block(spv::StorageClass storage); + void emit_push_constant_block_vulkan(const SPIRVariable &var); + void emit_push_constant_block_glsl(const SPIRVariable &var); + void emit_interface_block(const SPIRVariable &type); + void emit_flattened_io_block(const SPIRVariable &var, const char *qual); + void emit_flattened_io_block_struct(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices); + void emit_flattened_io_block_member(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices); + void emit_block_chain(SPIRBlock &block); + void emit_hoisted_temporaries(SmallVector> &temporaries); + std::string constant_value_macro_name(uint32_t id); + void emit_constant(const SPIRConstant &constant); + void emit_specialization_constant_op(const SPIRConstantOp &constant); + std::string emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block); + bool attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method); + + void branch(BlockID from, BlockID to); + void branch_to_continue(BlockID from, BlockID to); + void branch(BlockID from, uint32_t cond, BlockID true_block, BlockID false_block); + void flush_phi(BlockID from, BlockID to); + void flush_variable_declaration(uint32_t id); + void flush_undeclared_variables(SPIRBlock &block); + void emit_variable_temporary_copies(const SPIRVariable &var); + + bool should_dereference(uint32_t id); + bool should_forward(uint32_t id) const; + bool should_suppress_usage_tracking(uint32_t id) const; + void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp); + void emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op); + bool to_trivial_mix_op(const SPIRType &type, std::string &op, uint32_t left, uint32_t right, uint32_t lerp); + void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + uint32_t op3, const char *op); + void emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + const char *op); + void emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + + void emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op, + SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type); + void emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + SPIRType::BaseType input_type, bool skip_cast_if_equal_type); + void emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type); + void emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + const char *op, SPIRType::BaseType input_type); + void emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, SPIRType::BaseType expected_result_type, + SPIRType::BaseType input_type0, SPIRType::BaseType input_type1, + SPIRType::BaseType input_type2); + void emit_bitfield_insert_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + uint32_t op3, const char *op, SPIRType::BaseType offset_count_type); + + void emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); + void emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op); + void emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + bool negate, SPIRType::BaseType expected_type); + void emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + SPIRType::BaseType input_type, bool skip_cast_if_equal_type); + + SPIRType binary_op_bitcast_helper(std::string &cast_op0, std::string &cast_op1, SPIRType::BaseType &input_type, + uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type); + + virtual bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0); + + std::string to_ternary_expression(const SPIRType &result_type, uint32_t select, uint32_t true_value, + uint32_t false_value); + + void emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); + bool expression_is_forwarded(uint32_t id) const; + bool expression_suppresses_usage_tracking(uint32_t id) const; + bool expression_read_implies_multiple_reads(uint32_t id) const; + SPIRExpression &emit_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs, + bool suppress_usage_tracking = false); + + void access_chain_internal_append_index(std::string &expr, uint32_t base, const SPIRType *type, + AccessChainFlags flags, bool &access_chain_is_arrayed, uint32_t index); + + std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags, + AccessChainMeta *meta); + + virtual void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, + spv::StorageClass storage, bool &is_packed); + + std::string access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type, + AccessChainMeta *meta = nullptr, bool ptr_chain = false); + + std::string flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + uint32_t array_stride, bool need_transpose); + std::string flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset); + std::string flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + bool need_transpose); + std::string flattened_access_chain_vector(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + bool need_transpose); + std::pair flattened_access_chain_offset(const SPIRType &basetype, const uint32_t *indices, + uint32_t count, uint32_t offset, + uint32_t word_stride, bool *need_transpose = nullptr, + uint32_t *matrix_stride = nullptr, + uint32_t *array_stride = nullptr, + bool ptr_chain = false); + + const char *index_to_swizzle(uint32_t index); + std::string remap_swizzle(const SPIRType &result_type, uint32_t input_components, const std::string &expr); + std::string declare_temporary(uint32_t type, uint32_t id); + void emit_uninitialized_temporary(uint32_t type, uint32_t id); + SPIRExpression &emit_uninitialized_temporary_expression(uint32_t type, uint32_t id); + void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector &arglist); + std::string to_expression(uint32_t id, bool register_expression_read = true); + std::string to_composite_constructor_expression(uint32_t id, bool uses_buffer_offset); + std::string to_rerolled_array_expression(const std::string &expr, const SPIRType &type); + std::string to_enclosed_expression(uint32_t id, bool register_expression_read = true); + std::string to_unpacked_expression(uint32_t id, bool register_expression_read = true); + std::string to_unpacked_row_major_matrix_expression(uint32_t id); + std::string to_enclosed_unpacked_expression(uint32_t id, bool register_expression_read = true); + std::string to_dereferenced_expression(uint32_t id, bool register_expression_read = true); + std::string to_pointer_expression(uint32_t id, bool register_expression_read = true); + std::string to_enclosed_pointer_expression(uint32_t id, bool register_expression_read = true); + std::string to_extract_component_expression(uint32_t id, uint32_t index); + std::string enclose_expression(const std::string &expr); + std::string dereference_expression(const SPIRType &expression_type, const std::string &expr); + std::string address_of_expression(const std::string &expr); + void strip_enclosed_expression(std::string &expr); + std::string to_member_name(const SPIRType &type, uint32_t index); + virtual std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain); + std::string to_multi_member_reference(const SPIRType &type, const SmallVector &indices); + std::string type_to_glsl_constructor(const SPIRType &type); + std::string argument_decl(const SPIRFunction::Parameter &arg); + virtual std::string to_qualifiers_glsl(uint32_t id); + const char *to_precision_qualifiers_glsl(uint32_t id); + virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var); + const char *flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags); + const char *format_to_glsl(spv::ImageFormat format); + virtual std::string layout_for_member(const SPIRType &type, uint32_t index); + virtual std::string to_interpolation_qualifiers(const Bitset &flags); + std::string layout_for_variable(const SPIRVariable &variable); + std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id); + virtual bool skip_argument(uint32_t id) const; + virtual void emit_array_copy(const std::string &lhs, uint32_t rhs_id, spv::StorageClass lhs_storage, + spv::StorageClass rhs_storage); + virtual void emit_block_hints(const SPIRBlock &block); + virtual std::string to_initializer_expression(const SPIRVariable &var); + virtual std::string to_zero_initialized_expression(uint32_t type_id); + bool type_can_zero_initialize(const SPIRType &type) const; + + bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, + uint32_t *failed_index = nullptr, uint32_t start_offset = 0, + uint32_t end_offset = ~(0u)); + std::string buffer_to_packing_standard(const SPIRType &type, bool support_std430_without_scalar_layout); + + uint32_t type_to_packed_base_size(const SPIRType &type, BufferPackingStandard packing); + uint32_t type_to_packed_alignment(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + uint32_t type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + uint32_t type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + + std::string bitcast_glsl(const SPIRType &result_type, uint32_t arg); + virtual std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type); + + std::string bitcast_expression(SPIRType::BaseType target_type, uint32_t arg); + std::string bitcast_expression(const SPIRType &target_type, SPIRType::BaseType expr_type, const std::string &expr); + + std::string build_composite_combiner(uint32_t result_type, const uint32_t *elems, uint32_t length); + bool remove_duplicate_swizzle(std::string &op); + bool remove_unity_swizzle(uint32_t base, std::string &op); + + // Can modify flags to remote readonly/writeonly if image type + // and force recompile. + bool check_atomic_image(uint32_t id); + + virtual void replace_illegal_names(); + void replace_illegal_names(const std::unordered_set &keywords); + virtual void emit_entry_point_declarations(); + + void replace_fragment_output(SPIRVariable &var); + void replace_fragment_outputs(); + std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t id); + + uint32_t indent = 0; + + std::unordered_set emitted_functions; + + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + std::unordered_set flushed_phi_variables; + + std::unordered_set flattened_buffer_blocks; + std::unordered_map flattened_structs; + + ShaderSubgroupSupportHelper shader_subgroup_supporter; + + std::string load_flattened_struct(const std::string &basename, const SPIRType &type); + std::string to_flattened_struct_member(const std::string &basename, const SPIRType &type, uint32_t index); + void store_flattened_struct(uint32_t lhs_id, uint32_t value); + void store_flattened_struct(const std::string &basename, uint32_t rhs, const SPIRType &type, + const SmallVector &indices); + std::string to_flattened_access_chain_expression(uint32_t id); + + // Usage tracking. If a temporary is used more than once, use the temporary instead to + // avoid AST explosion when SPIRV is generated with pure SSA and doesn't write stuff to variables. + std::unordered_map expression_usage_counts; + void track_expression_read(uint32_t id); + + SmallVector forced_extensions; + SmallVector header_lines; + + // Used when expressions emit extra opcodes with their own unique IDs, + // and we need to reuse the IDs across recompilation loops. + // Currently used by NMin/Max/Clamp implementations. + std::unordered_map extra_sub_expressions; + + SmallVector workaround_ubo_load_overload_types; + void request_workaround_wrapper_overload(TypeID id); + void rewrite_load_for_wrapped_row_major(std::string &expr, TypeID loaded_type, ID ptr); + + uint32_t statement_count = 0; + + inline bool is_legacy() const + { + return (options.es && options.version < 300) || (!options.es && options.version < 130); + } + + inline bool is_legacy_es() const + { + return options.es && options.version < 300; + } + + inline bool is_legacy_desktop() const + { + return !options.es && options.version < 130; + } + + bool requires_transpose_2x2 = false; + bool requires_transpose_3x3 = false; + bool requires_transpose_4x4 = false; + + bool args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure); + void register_call_out_argument(uint32_t id); + void register_impure_function_call(); + void register_control_dependent_expression(uint32_t expr); + + // GL_EXT_shader_pixel_local_storage support. + std::vector pls_inputs; + std::vector pls_outputs; + std::string pls_decl(const PlsRemap &variable); + const char *to_pls_qualifiers_glsl(const SPIRVariable &variable); + void emit_pls(); + void remap_pls_variables(); + + // GL_EXT_shader_framebuffer_fetch support. + std::vector> subpass_to_framebuffer_fetch_attachment; + std::unordered_set inout_color_attachments; + bool subpass_input_is_framebuffer_fetch(uint32_t id) const; + void emit_inout_fragment_outputs_copy_to_subpass_inputs(); + const SPIRVariable *find_subpass_input_by_attachment_index(uint32_t index) const; + const SPIRVariable *find_color_output_by_location(uint32_t location) const; + + // A variant which takes two sets of name. The secondary is only used to verify there are no collisions, + // but the set is not updated when we have found a new name. + // Used primarily when adding block interface names. + void add_variable(std::unordered_set &variables_primary, + const std::unordered_set &variables_secondary, std::string &name); + + void check_function_call_constraints(const uint32_t *args, uint32_t length); + void handle_invalid_expression(uint32_t id); + void find_static_extensions(); + + std::string emit_for_loop_initializers(const SPIRBlock &block); + void emit_while_loop_initializers(const SPIRBlock &block); + bool for_loop_initializers_are_same_type(const SPIRBlock &block); + bool optimize_read_modify_write(const SPIRType &type, const std::string &lhs, const std::string &rhs); + void fixup_image_load_store_access(); + + bool type_is_empty(const SPIRType &type); + + virtual void declare_undefined_values(); + + bool can_use_io_location(spv::StorageClass storage, bool block); + const Instruction *get_next_instruction_in_block(const Instruction &instr); + static uint32_t mask_relevant_memory_semantics(uint32_t semantics); + + std::string convert_half_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + std::string convert_float_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + std::string convert_double_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + + std::string convert_separate_image_to_expression(uint32_t id); + + // Builtins in GLSL are always specific signedness, but the SPIR-V can declare them + // as either unsigned or signed. + // Sometimes we will need to automatically perform casts on load and store to make this work. + virtual void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type); + virtual void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type); + void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr); + void convert_non_uniform_expression(const SPIRType &type, std::string &expr); + + void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id); + void disallow_forwarding_in_expression_chain(const SPIRExpression &expr); + + bool expression_is_constant_null(uint32_t id) const; + bool expression_is_non_value_type_array(uint32_t ptr); + virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression); + + uint32_t get_integer_width_for_instruction(const Instruction &instr) const; + uint32_t get_integer_width_for_glsl_instruction(GLSLstd450 op, const uint32_t *arguments, uint32_t length) const; + + bool variable_is_lut(const SPIRVariable &var) const; + + char current_locale_radix_character = '.'; + + void fixup_type_alias(); + void reorder_type_alias(); + + void propagate_nonuniform_qualifier(uint32_t id); + + static const char *vector_swizzle(int vecsize, int index); + +private: + void init(); +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_hlsl.cpp b/third_party/spirv-cross/spirv_hlsl.cpp new file mode 100644 index 0000000..3c01762 --- /dev/null +++ b/third_party/spirv-cross/spirv_hlsl.cpp @@ -0,0 +1,5768 @@ +/* + * Copyright 2016-2020 Robert Konrad + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_hlsl.hpp" +#include "GLSL.std.450.h" +#include +#include + +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +enum class ImageFormatNormalizedState +{ + None = 0, + Unorm = 1, + Snorm = 2 +}; + +static ImageFormatNormalizedState image_format_to_normalized_state(ImageFormat fmt) +{ + switch (fmt) + { + case ImageFormatR8: + case ImageFormatR16: + case ImageFormatRg8: + case ImageFormatRg16: + case ImageFormatRgba8: + case ImageFormatRgba16: + case ImageFormatRgb10A2: + return ImageFormatNormalizedState::Unorm; + + case ImageFormatR8Snorm: + case ImageFormatR16Snorm: + case ImageFormatRg8Snorm: + case ImageFormatRg16Snorm: + case ImageFormatRgba8Snorm: + case ImageFormatRgba16Snorm: + return ImageFormatNormalizedState::Snorm; + + default: + break; + } + + return ImageFormatNormalizedState::None; +} + +static unsigned image_format_to_components(ImageFormat fmt) +{ + switch (fmt) + { + case ImageFormatR8: + case ImageFormatR16: + case ImageFormatR8Snorm: + case ImageFormatR16Snorm: + case ImageFormatR16f: + case ImageFormatR32f: + case ImageFormatR8i: + case ImageFormatR16i: + case ImageFormatR32i: + case ImageFormatR8ui: + case ImageFormatR16ui: + case ImageFormatR32ui: + return 1; + + case ImageFormatRg8: + case ImageFormatRg16: + case ImageFormatRg8Snorm: + case ImageFormatRg16Snorm: + case ImageFormatRg16f: + case ImageFormatRg32f: + case ImageFormatRg8i: + case ImageFormatRg16i: + case ImageFormatRg32i: + case ImageFormatRg8ui: + case ImageFormatRg16ui: + case ImageFormatRg32ui: + return 2; + + case ImageFormatR11fG11fB10f: + return 3; + + case ImageFormatRgba8: + case ImageFormatRgba16: + case ImageFormatRgb10A2: + case ImageFormatRgba8Snorm: + case ImageFormatRgba16Snorm: + case ImageFormatRgba16f: + case ImageFormatRgba32f: + case ImageFormatRgba8i: + case ImageFormatRgba16i: + case ImageFormatRgba32i: + case ImageFormatRgba8ui: + case ImageFormatRgba16ui: + case ImageFormatRgba32ui: + case ImageFormatRgb10a2ui: + return 4; + + case ImageFormatUnknown: + return 4; // Assume 4. + + default: + SPIRV_CROSS_THROW("Unrecognized typed image format."); + } +} + +static string image_format_to_type(ImageFormat fmt, SPIRType::BaseType basetype) +{ + switch (fmt) + { + case ImageFormatR8: + case ImageFormatR16: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "unorm float"; + case ImageFormatRg8: + case ImageFormatRg16: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "unorm float2"; + case ImageFormatRgba8: + case ImageFormatRgba16: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "unorm float4"; + case ImageFormatRgb10A2: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "unorm float4"; + + case ImageFormatR8Snorm: + case ImageFormatR16Snorm: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "snorm float"; + case ImageFormatRg8Snorm: + case ImageFormatRg16Snorm: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "snorm float2"; + case ImageFormatRgba8Snorm: + case ImageFormatRgba16Snorm: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "snorm float4"; + + case ImageFormatR16f: + case ImageFormatR32f: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "float"; + case ImageFormatRg16f: + case ImageFormatRg32f: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "float2"; + case ImageFormatRgba16f: + case ImageFormatRgba32f: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "float4"; + + case ImageFormatR11fG11fB10f: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "float3"; + + case ImageFormatR8i: + case ImageFormatR16i: + case ImageFormatR32i: + if (basetype != SPIRType::Int) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "int"; + case ImageFormatRg8i: + case ImageFormatRg16i: + case ImageFormatRg32i: + if (basetype != SPIRType::Int) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "int2"; + case ImageFormatRgba8i: + case ImageFormatRgba16i: + case ImageFormatRgba32i: + if (basetype != SPIRType::Int) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "int4"; + + case ImageFormatR8ui: + case ImageFormatR16ui: + case ImageFormatR32ui: + if (basetype != SPIRType::UInt) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "uint"; + case ImageFormatRg8ui: + case ImageFormatRg16ui: + case ImageFormatRg32ui: + if (basetype != SPIRType::UInt) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "uint2"; + case ImageFormatRgba8ui: + case ImageFormatRgba16ui: + case ImageFormatRgba32ui: + if (basetype != SPIRType::UInt) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "uint4"; + case ImageFormatRgb10a2ui: + if (basetype != SPIRType::UInt) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "uint4"; + + case ImageFormatUnknown: + switch (basetype) + { + case SPIRType::Float: + return "float4"; + case SPIRType::Int: + return "int4"; + case SPIRType::UInt: + return "uint4"; + default: + SPIRV_CROSS_THROW("Unsupported base type for image."); + } + + default: + SPIRV_CROSS_THROW("Unrecognized typed image format."); + } +} + +string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t id) +{ + auto &imagetype = get(type.image.type); + const char *dim = nullptr; + bool typed_load = false; + uint32_t components = 4; + + bool force_image_srv = hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(id, DecorationNonWritable); + + switch (type.image.dim) + { + case Dim1D: + typed_load = type.image.sampled == 2; + dim = "1D"; + break; + case Dim2D: + typed_load = type.image.sampled == 2; + dim = "2D"; + break; + case Dim3D: + typed_load = type.image.sampled == 2; + dim = "3D"; + break; + case DimCube: + if (type.image.sampled == 2) + SPIRV_CROSS_THROW("RWTextureCube does not exist in HLSL."); + dim = "Cube"; + break; + case DimRect: + SPIRV_CROSS_THROW("Rectangle texture support is not yet implemented for HLSL."); // TODO + case DimBuffer: + if (type.image.sampled == 1) + return join("Buffer<", type_to_glsl(imagetype), components, ">"); + else if (type.image.sampled == 2) + { + if (interlocked_resources.count(id)) + return join("RasterizerOrderedBuffer<", image_format_to_type(type.image.format, imagetype.basetype), + ">"); + + typed_load = !force_image_srv && type.image.sampled == 2; + + const char *rw = force_image_srv ? "" : "RW"; + return join(rw, "Buffer<", + typed_load ? image_format_to_type(type.image.format, imagetype.basetype) : + join(type_to_glsl(imagetype), components), + ">"); + } + else + SPIRV_CROSS_THROW("Sampler buffers must be either sampled or unsampled. Cannot deduce in runtime."); + case DimSubpassData: + dim = "2D"; + typed_load = false; + break; + default: + SPIRV_CROSS_THROW("Invalid dimension."); + } + const char *arrayed = type.image.arrayed ? "Array" : ""; + const char *ms = type.image.ms ? "MS" : ""; + const char *rw = typed_load && !force_image_srv ? "RW" : ""; + + if (force_image_srv) + typed_load = false; + + if (typed_load && interlocked_resources.count(id)) + rw = "RasterizerOrdered"; + + return join(rw, "Texture", dim, ms, arrayed, "<", + typed_load ? image_format_to_type(type.image.format, imagetype.basetype) : + join(type_to_glsl(imagetype), components), + ">"); +} + +string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t /*id*/) +{ + auto &imagetype = get(type.image.type); + string res; + + switch (imagetype.basetype) + { + case SPIRType::Int: + res = "i"; + break; + case SPIRType::UInt: + res = "u"; + break; + default: + break; + } + + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData) + return res + "subpassInput" + (type.image.ms ? "MS" : ""); + + // If we're emulating subpassInput with samplers, force sampler2D + // so we don't have to specify format. + if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData) + { + // Sampler buffers are always declared as samplerBuffer even though they might be separate images in the SPIR-V. + if (type.image.dim == DimBuffer && type.image.sampled == 1) + res += "sampler"; + else + res += type.image.sampled == 2 ? "image" : "texture"; + } + else + res += "sampler"; + + switch (type.image.dim) + { + case Dim1D: + res += "1D"; + break; + case Dim2D: + res += "2D"; + break; + case Dim3D: + res += "3D"; + break; + case DimCube: + res += "CUBE"; + break; + + case DimBuffer: + res += "Buffer"; + break; + + case DimSubpassData: + res += "2D"; + break; + default: + SPIRV_CROSS_THROW("Only 1D, 2D, 3D, Buffer, InputTarget and Cube textures supported."); + } + + if (type.image.ms) + res += "MS"; + if (type.image.arrayed) + res += "Array"; + + return res; +} + +string CompilerHLSL::image_type_hlsl(const SPIRType &type, uint32_t id) +{ + if (hlsl_options.shader_model <= 30) + return image_type_hlsl_legacy(type, id); + else + return image_type_hlsl_modern(type, id); +} + +// The optional id parameter indicates the object whose type we are trying +// to find the description for. It is optional. Most type descriptions do not +// depend on a specific object's use of that type. +string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id) +{ + // Ignore the pointer type since GLSL doesn't have pointers. + + switch (type.basetype) + { + case SPIRType::Struct: + // Need OpName lookup here to get a "sensible" name for a struct. + if (backend.explicit_struct_type) + return join("struct ", to_name(type.self)); + else + return to_name(type.self); + + case SPIRType::Image: + case SPIRType::SampledImage: + return image_type_hlsl(type, id); + + case SPIRType::Sampler: + return comparison_ids.count(id) ? "SamplerComparisonState" : "SamplerState"; + + case SPIRType::Void: + return "void"; + + default: + break; + } + + if (type.vecsize == 1 && type.columns == 1) // Scalar builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return "bool"; + case SPIRType::Int: + return backend.basic_int_type; + case SPIRType::UInt: + return backend.basic_uint_type; + case SPIRType::AtomicCounter: + return "atomic_uint"; + case SPIRType::Half: + if (hlsl_options.enable_16bit_types) + return "half"; + else + return "min16float"; + case SPIRType::Short: + if (hlsl_options.enable_16bit_types) + return "int16_t"; + else + return "min16int"; + case SPIRType::UShort: + if (hlsl_options.enable_16bit_types) + return "uint16_t"; + else + return "min16uint"; + case SPIRType::Float: + return "float"; + case SPIRType::Double: + return "double"; + case SPIRType::Int64: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("64-bit integers only supported in SM 6.0."); + return "int64_t"; + case SPIRType::UInt64: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("64-bit integers only supported in SM 6.0."); + return "uint64_t"; + default: + return "???"; + } + } + else if (type.vecsize > 1 && type.columns == 1) // Vector builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bool", type.vecsize); + case SPIRType::Int: + return join("int", type.vecsize); + case SPIRType::UInt: + return join("uint", type.vecsize); + case SPIRType::Half: + return join(hlsl_options.enable_16bit_types ? "half" : "min16float", type.vecsize); + case SPIRType::Short: + return join(hlsl_options.enable_16bit_types ? "int16_t" : "min16int", type.vecsize); + case SPIRType::UShort: + return join(hlsl_options.enable_16bit_types ? "uint16_t" : "min16uint", type.vecsize); + case SPIRType::Float: + return join("float", type.vecsize); + case SPIRType::Double: + return join("double", type.vecsize); + case SPIRType::Int64: + return join("i64vec", type.vecsize); + case SPIRType::UInt64: + return join("u64vec", type.vecsize); + default: + return "???"; + } + } + else + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bool", type.columns, "x", type.vecsize); + case SPIRType::Int: + return join("int", type.columns, "x", type.vecsize); + case SPIRType::UInt: + return join("uint", type.columns, "x", type.vecsize); + case SPIRType::Half: + return join(hlsl_options.enable_16bit_types ? "half" : "min16float", type.columns, "x", type.vecsize); + case SPIRType::Short: + return join(hlsl_options.enable_16bit_types ? "int16_t" : "min16int", type.columns, "x", type.vecsize); + case SPIRType::UShort: + return join(hlsl_options.enable_16bit_types ? "uint16_t" : "min16uint", type.columns, "x", type.vecsize); + case SPIRType::Float: + return join("float", type.columns, "x", type.vecsize); + case SPIRType::Double: + return join("double", type.columns, "x", type.vecsize); + // Matrix types not supported for int64/uint64. + default: + return "???"; + } + } +} + +void CompilerHLSL::emit_header() +{ + for (auto &header : header_lines) + statement(header); + + if (header_lines.size() > 0) + { + statement(""); + } +} + +void CompilerHLSL::emit_interface_block_globally(const SPIRVariable &var) +{ + add_resource_name(var.self); + + // The global copies of I/O variables should not contain interpolation qualifiers. + // These are emitted inside the interface structs. + auto &flags = ir.meta[var.self].decoration.decoration_flags; + auto old_flags = flags; + flags.reset(); + statement("static ", variable_decl(var), ";"); + flags = old_flags; +} + +const char *CompilerHLSL::to_storage_qualifiers_glsl(const SPIRVariable &var) +{ + // Input and output variables are handled specially in HLSL backend. + // The variables are declared as global, private variables, and do not need any qualifiers. + if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform || + var.storage == StorageClassPushConstant) + { + return "uniform "; + } + + return ""; +} + +void CompilerHLSL::emit_builtin_outputs_in_struct() +{ + auto &execution = get_entry_point(); + + bool legacy = hlsl_options.shader_model <= 30; + active_output_builtins.for_each_bit([&](uint32_t i) { + const char *type = nullptr; + const char *semantic = nullptr; + auto builtin = static_cast(i); + switch (builtin) + { + case BuiltInPosition: + type = "float4"; + semantic = legacy ? "POSITION" : "SV_Position"; + break; + + case BuiltInSampleMask: + if (hlsl_options.shader_model < 41 || execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Sample Mask output is only supported in PS 4.1 or higher."); + type = "uint"; + semantic = "SV_Coverage"; + break; + + case BuiltInFragDepth: + type = "float"; + if (legacy) + { + semantic = "DEPTH"; + } + else + { + if (hlsl_options.shader_model >= 50 && execution.flags.get(ExecutionModeDepthGreater)) + semantic = "SV_DepthGreaterEqual"; + else if (hlsl_options.shader_model >= 50 && execution.flags.get(ExecutionModeDepthLess)) + semantic = "SV_DepthLessEqual"; + else + semantic = "SV_Depth"; + } + break; + + case BuiltInClipDistance: + // HLSL is a bit weird here, use SV_ClipDistance0, SV_ClipDistance1 and so on with vectors. + for (uint32_t clip = 0; clip < clip_distance_count; clip += 4) + { + uint32_t to_declare = clip_distance_count - clip; + if (to_declare > 4) + to_declare = 4; + + uint32_t semantic_index = clip / 4; + + static const char *types[] = { "float", "float2", "float3", "float4" }; + statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassOutput), semantic_index, + " : SV_ClipDistance", semantic_index, ";"); + } + break; + + case BuiltInCullDistance: + // HLSL is a bit weird here, use SV_CullDistance0, SV_CullDistance1 and so on with vectors. + for (uint32_t cull = 0; cull < cull_distance_count; cull += 4) + { + uint32_t to_declare = cull_distance_count - cull; + if (to_declare > 4) + to_declare = 4; + + uint32_t semantic_index = cull / 4; + + static const char *types[] = { "float", "float2", "float3", "float4" }; + statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassOutput), semantic_index, + " : SV_CullDistance", semantic_index, ";"); + } + break; + + case BuiltInPointSize: + // If point_size_compat is enabled, just ignore PointSize. + // PointSize does not exist in HLSL, but some code bases might want to be able to use these shaders, + // even if it means working around the missing feature. + if (hlsl_options.point_size_compat) + break; + else + SPIRV_CROSS_THROW("Unsupported builtin in HLSL."); + + default: + SPIRV_CROSS_THROW("Unsupported builtin in HLSL."); + break; + } + + if (type && semantic) + statement(type, " ", builtin_to_glsl(builtin, StorageClassOutput), " : ", semantic, ";"); + }); +} + +void CompilerHLSL::emit_builtin_inputs_in_struct() +{ + bool legacy = hlsl_options.shader_model <= 30; + active_input_builtins.for_each_bit([&](uint32_t i) { + const char *type = nullptr; + const char *semantic = nullptr; + auto builtin = static_cast(i); + switch (builtin) + { + case BuiltInFragCoord: + type = "float4"; + semantic = legacy ? "VPOS" : "SV_Position"; + break; + + case BuiltInVertexId: + case BuiltInVertexIndex: + if (legacy) + SPIRV_CROSS_THROW("Vertex index not supported in SM 3.0 or lower."); + type = "uint"; + semantic = "SV_VertexID"; + break; + + case BuiltInInstanceId: + case BuiltInInstanceIndex: + if (legacy) + SPIRV_CROSS_THROW("Instance index not supported in SM 3.0 or lower."); + type = "uint"; + semantic = "SV_InstanceID"; + break; + + case BuiltInSampleId: + if (legacy) + SPIRV_CROSS_THROW("Sample ID not supported in SM 3.0 or lower."); + type = "uint"; + semantic = "SV_SampleIndex"; + break; + + case BuiltInSampleMask: + if (hlsl_options.shader_model < 50 || get_entry_point().model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Sample Mask input is only supported in PS 5.0 or higher."); + type = "uint"; + semantic = "SV_Coverage"; + break; + + case BuiltInGlobalInvocationId: + type = "uint3"; + semantic = "SV_DispatchThreadID"; + break; + + case BuiltInLocalInvocationId: + type = "uint3"; + semantic = "SV_GroupThreadID"; + break; + + case BuiltInLocalInvocationIndex: + type = "uint"; + semantic = "SV_GroupIndex"; + break; + + case BuiltInWorkgroupId: + type = "uint3"; + semantic = "SV_GroupID"; + break; + + case BuiltInFrontFacing: + type = "bool"; + semantic = "SV_IsFrontFace"; + break; + + case BuiltInNumWorkgroups: + case BuiltInSubgroupSize: + case BuiltInSubgroupLocalInvocationId: + case BuiltInSubgroupEqMask: + case BuiltInSubgroupLtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupGeMask: + // Handled specially. + break; + + case BuiltInClipDistance: + // HLSL is a bit weird here, use SV_ClipDistance0, SV_ClipDistance1 and so on with vectors. + for (uint32_t clip = 0; clip < clip_distance_count; clip += 4) + { + uint32_t to_declare = clip_distance_count - clip; + if (to_declare > 4) + to_declare = 4; + + uint32_t semantic_index = clip / 4; + + static const char *types[] = { "float", "float2", "float3", "float4" }; + statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassInput), semantic_index, + " : SV_ClipDistance", semantic_index, ";"); + } + break; + + case BuiltInCullDistance: + // HLSL is a bit weird here, use SV_CullDistance0, SV_CullDistance1 and so on with vectors. + for (uint32_t cull = 0; cull < cull_distance_count; cull += 4) + { + uint32_t to_declare = cull_distance_count - cull; + if (to_declare > 4) + to_declare = 4; + + uint32_t semantic_index = cull / 4; + + static const char *types[] = { "float", "float2", "float3", "float4" }; + statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassInput), semantic_index, + " : SV_CullDistance", semantic_index, ";"); + } + break; + + case BuiltInPointCoord: + // PointCoord is not supported, but provide a way to just ignore that, similar to PointSize. + if (hlsl_options.point_coord_compat) + break; + else + SPIRV_CROSS_THROW("Unsupported builtin in HLSL."); + + default: + SPIRV_CROSS_THROW("Unsupported builtin in HLSL."); + break; + } + + if (type && semantic) + statement(type, " ", builtin_to_glsl(builtin, StorageClassInput), " : ", semantic, ";"); + }); +} + +uint32_t CompilerHLSL::type_to_consumed_locations(const SPIRType &type) const +{ + // TODO: Need to verify correctness. + uint32_t elements = 0; + + if (type.basetype == SPIRType::Struct) + { + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + elements += type_to_consumed_locations(get(type.member_types[i])); + } + else + { + uint32_t array_multiplier = 1; + for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) + { + if (type.array_size_literal[i]) + array_multiplier *= type.array[i]; + else + array_multiplier *= evaluate_constant_u32(type.array[i]); + } + elements += array_multiplier * type.columns; + } + return elements; +} + +string CompilerHLSL::to_interpolation_qualifiers(const Bitset &flags) +{ + string res; + //if (flags & (1ull << DecorationSmooth)) + // res += "linear "; + if (flags.get(DecorationFlat)) + res += "nointerpolation "; + if (flags.get(DecorationNoPerspective)) + res += "noperspective "; + if (flags.get(DecorationCentroid)) + res += "centroid "; + if (flags.get(DecorationPatch)) + res += "patch "; // Seems to be different in actual HLSL. + if (flags.get(DecorationSample)) + res += "sample "; + if (flags.get(DecorationInvariant)) + res += "invariant "; // Not supported? + + return res; +} + +std::string CompilerHLSL::to_semantic(uint32_t location, ExecutionModel em, StorageClass sc) +{ + if (em == ExecutionModelVertex && sc == StorageClassInput) + { + // We have a vertex attribute - we should look at remapping it if the user provided + // vertex attribute hints. + for (auto &attribute : remap_vertex_attributes) + if (attribute.location == location) + return attribute.semantic; + } + + // Not a vertex attribute, or no remap_vertex_attributes entry. + return join("TEXCOORD", location); +} + +void CompilerHLSL::emit_io_block(const SPIRVariable &var) +{ + auto &execution = get_entry_point(); + + auto &type = get(var.basetype); + add_resource_name(type.self); + + statement("struct ", to_name(type.self)); + begin_scope(); + type.member_name_cache.clear(); + + uint32_t base_location = get_decoration(var.self, DecorationLocation); + + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + string semantic; + if (has_member_decoration(type.self, i, DecorationLocation)) + { + uint32_t location = get_member_decoration(type.self, i, DecorationLocation); + semantic = join(" : ", to_semantic(location, execution.model, var.storage)); + } + else + { + // If the block itself has a location, but not its members, use the implicit location. + // There could be a conflict if the block members partially specialize the locations. + // It is unclear how SPIR-V deals with this. Assume this does not happen for now. + uint32_t location = base_location + i; + semantic = join(" : ", to_semantic(location, execution.model, var.storage)); + } + + add_member_name(type, i); + + auto &membertype = get(type.member_types[i]); + statement(to_interpolation_qualifiers(get_member_decoration_bitset(type.self, i)), + variable_decl(membertype, to_member_name(type, i)), semantic, ";"); + } + + end_scope_decl(); + statement(""); + + statement("static ", variable_decl(var), ";"); + statement(""); +} + +void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set &active_locations) +{ + auto &execution = get_entry_point(); + auto type = get(var.basetype); + + string binding; + bool use_location_number = true; + bool legacy = hlsl_options.shader_model <= 30; + if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput) + { + // Dual-source blending is achieved in HLSL by emitting to SV_Target0 and 1. + uint32_t index = get_decoration(var.self, DecorationIndex); + uint32_t location = get_decoration(var.self, DecorationLocation); + + if (index != 0 && location != 0) + SPIRV_CROSS_THROW("Dual-source blending is only supported on MRT #0 in HLSL."); + + binding = join(legacy ? "COLOR" : "SV_Target", location + index); + use_location_number = false; + if (legacy) // COLOR must be a four-component vector on legacy shader model targets (HLSL ERR_COLOR_4COMP) + type.vecsize = 4; + } + + const auto get_vacant_location = [&]() -> uint32_t { + for (uint32_t i = 0; i < 64; i++) + if (!active_locations.count(i)) + return i; + SPIRV_CROSS_THROW("All locations from 0 to 63 are exhausted."); + }; + + bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex; + + auto &m = ir.meta[var.self].decoration; + auto name = to_name(var.self); + if (use_location_number) + { + uint32_t location_number; + + // If an explicit location exists, use it with TEXCOORD[N] semantic. + // Otherwise, pick a vacant location. + if (m.decoration_flags.get(DecorationLocation)) + location_number = m.location; + else + location_number = get_vacant_location(); + + // Allow semantic remap if specified. + auto semantic = to_semantic(location_number, execution.model, var.storage); + + if (need_matrix_unroll && type.columns > 1) + { + if (!type.array.empty()) + SPIRV_CROSS_THROW("Arrays of matrices used as input/output. This is not supported."); + + // Unroll matrices. + for (uint32_t i = 0; i < type.columns; i++) + { + SPIRType newtype = type; + newtype.columns = 1; + + string effective_semantic; + if (hlsl_options.flatten_matrix_vertex_input_semantics) + effective_semantic = to_semantic(location_number, execution.model, var.storage); + else + effective_semantic = join(semantic, "_", i); + + statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)), + variable_decl(newtype, join(name, "_", i)), " : ", effective_semantic, ";"); + active_locations.insert(location_number++); + } + } + else + { + statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)), variable_decl(type, name), " : ", + semantic, ";"); + + // Structs and arrays should consume more locations. + uint32_t consumed_locations = type_to_consumed_locations(type); + for (uint32_t i = 0; i < consumed_locations; i++) + active_locations.insert(location_number + i); + } + } + else + statement(variable_decl(type, name), " : ", binding, ";"); +} + +std::string CompilerHLSL::builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) +{ + switch (builtin) + { + case BuiltInVertexId: + return "gl_VertexID"; + case BuiltInInstanceId: + return "gl_InstanceID"; + case BuiltInNumWorkgroups: + { + if (!num_workgroups_builtin) + SPIRV_CROSS_THROW("NumWorkgroups builtin is used, but remap_num_workgroups_builtin() was not called. " + "Cannot emit code for this builtin."); + + auto &var = get(num_workgroups_builtin); + auto &type = get(var.basetype); + auto ret = join(to_name(num_workgroups_builtin), "_", get_member_name(type.self, 0)); + ParsedIR::sanitize_underscores(ret); + return ret; + } + case BuiltInPointCoord: + // Crude hack, but there is no real alternative. This path is only enabled if point_coord_compat is set. + return "float2(0.5f, 0.5f)"; + case BuiltInSubgroupLocalInvocationId: + return "WaveGetLaneIndex()"; + case BuiltInSubgroupSize: + return "WaveGetLaneCount()"; + + default: + return CompilerGLSL::builtin_to_glsl(builtin, storage); + } +} + +void CompilerHLSL::emit_builtin_variables() +{ + Bitset builtins = active_input_builtins; + builtins.merge_or(active_output_builtins); + + bool need_base_vertex_info = false; + + // Emit global variables for the interface variables which are statically used by the shader. + builtins.for_each_bit([&](uint32_t i) { + const char *type = nullptr; + auto builtin = static_cast(i); + uint32_t array_size = 0; + + switch (builtin) + { + case BuiltInFragCoord: + case BuiltInPosition: + type = "float4"; + break; + + case BuiltInFragDepth: + type = "float"; + break; + + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInInstanceIndex: + type = "int"; + if (hlsl_options.support_nonzero_base_vertex_base_instance) + need_base_vertex_info = true; + break; + + case BuiltInInstanceId: + case BuiltInSampleId: + type = "int"; + break; + + case BuiltInPointSize: + if (hlsl_options.point_size_compat) + { + // Just emit the global variable, it will be ignored. + type = "float"; + break; + } + else + SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin))); + + case BuiltInGlobalInvocationId: + case BuiltInLocalInvocationId: + case BuiltInWorkgroupId: + type = "uint3"; + break; + + case BuiltInLocalInvocationIndex: + type = "uint"; + break; + + case BuiltInFrontFacing: + type = "bool"; + break; + + case BuiltInNumWorkgroups: + case BuiltInPointCoord: + // Handled specially. + break; + + case BuiltInSubgroupLocalInvocationId: + case BuiltInSubgroupSize: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("Need SM 6.0 for Wave ops."); + break; + + case BuiltInSubgroupEqMask: + case BuiltInSubgroupLtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupGeMask: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("Need SM 6.0 for Wave ops."); + type = "uint4"; + break; + + case BuiltInClipDistance: + array_size = clip_distance_count; + type = "float"; + break; + + case BuiltInCullDistance: + array_size = cull_distance_count; + type = "float"; + break; + + case BuiltInSampleMask: + type = "int"; + break; + + default: + SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin))); + } + + StorageClass storage = active_input_builtins.get(i) ? StorageClassInput : StorageClassOutput; + + if (type) + { + if (array_size) + statement("static ", type, " ", builtin_to_glsl(builtin, storage), "[", array_size, "];"); + else + statement("static ", type, " ", builtin_to_glsl(builtin, storage), ";"); + } + + // SampleMask can be both in and out with sample builtin, in this case we have already + // declared the input variable and we need to add the output one now. + if (builtin == BuiltInSampleMask && storage == StorageClassInput && this->active_output_builtins.get(i)) + { + statement("static ", type, " ", this->builtin_to_glsl(builtin, StorageClassOutput), ";"); + } + }); + + if (need_base_vertex_info) + { + statement("cbuffer SPIRV_Cross_VertexInfo"); + begin_scope(); + statement("int SPIRV_Cross_BaseVertex;"); + statement("int SPIRV_Cross_BaseInstance;"); + end_scope_decl(); + statement(""); + } +} + +void CompilerHLSL::emit_composite_constants() +{ + // HLSL cannot declare structs or arrays inline, so we must move them out to + // global constants directly. + bool emitted = false; + + ir.for_each_typed_id([&](uint32_t, SPIRConstant &c) { + if (c.specialization) + return; + + auto &type = this->get(c.constant_type); + if (type.basetype == SPIRType::Struct || !type.array.empty()) + { + auto name = to_name(c.self); + statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";"); + emitted = true; + } + }); + + if (emitted) + statement(""); +} + +void CompilerHLSL::emit_specialization_constants_and_structs() +{ + bool emitted = false; + SpecializationConstant wg_x, wg_y, wg_z; + ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + auto loop_lock = ir.create_loop_hard_lock(); + for (auto &id_ : ir.ids_for_constant_or_type) + { + auto &id = ir.ids[id_]; + + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + + if (c.self == workgroup_size_id) + { + statement("static const uint3 gl_WorkGroupSize = ", + constant_expression(get(workgroup_size_id)), ";"); + emitted = true; + } + else if (c.specialization) + { + auto &type = get(c.constant_type); + auto name = to_name(c.self); + + // HLSL does not support specialization constants, so fallback to macros. + c.specialization_constant_macro_name = + constant_value_macro_name(get_decoration(c.self, DecorationSpecId)); + + statement("#ifndef ", c.specialization_constant_macro_name); + statement("#define ", c.specialization_constant_macro_name, " ", constant_expression(c)); + statement("#endif"); + statement("static const ", variable_decl(type, name), " = ", c.specialization_constant_macro_name, ";"); + emitted = true; + } + } + else if (id.get_type() == TypeConstantOp) + { + auto &c = id.get(); + auto &type = get(c.basetype); + auto name = to_name(c.self); + statement("static const ", variable_decl(type, name), " = ", constant_op_expression(c), ";"); + emitted = true; + } + else if (id.get_type() == TypeType) + { + auto &type = id.get(); + if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer && + (!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) && + !ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock))) + { + if (emitted) + statement(""); + emitted = false; + + emit_struct(type); + } + } + } + + if (emitted) + statement(""); +} + +void CompilerHLSL::replace_illegal_names() +{ + static const unordered_set keywords = { + // Additional HLSL specific keywords. + "line", "linear", "matrix", "point", "row_major", "sampler", + }; + + CompilerGLSL::replace_illegal_names(keywords); + CompilerGLSL::replace_illegal_names(); +} + +void CompilerHLSL::declare_undefined_values() +{ + bool emitted = false; + ir.for_each_typed_id([&](uint32_t, const SPIRUndef &undef) { + auto &type = this->get(undef.basetype); + // OpUndef can be void for some reason ... + if (type.basetype == SPIRType::Void) + return; + + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(undef.basetype)); + + statement("static ", variable_decl(type, to_name(undef.self), undef.self), initializer, ";"); + emitted = true; + }); + + if (emitted) + statement(""); +} + +void CompilerHLSL::emit_resources() +{ + auto &execution = get_entry_point(); + + replace_illegal_names(); + + emit_specialization_constants_and_structs(); + emit_composite_constants(); + + bool emitted = false; + + // Output UBOs and SSBOs + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + bool is_block_storage = type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform; + bool has_block_flags = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + + if (var.storage != StorageClassFunction && type.pointer && is_block_storage && !is_hidden_variable(var) && + has_block_flags) + { + emit_buffer_block(var); + emitted = true; + } + }); + + // Output push constant blocks + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassPushConstant && + !is_hidden_variable(var)) + { + emit_push_constant_block(var); + emitted = true; + } + }); + + if (execution.model == ExecutionModelVertex && hlsl_options.shader_model <= 30) + { + statement("uniform float4 gl_HalfPixel;"); + emitted = true; + } + + bool skip_separate_image_sampler = !combined_image_samplers.empty() || hlsl_options.shader_model <= 30; + + // Output Uniform Constants (values, samplers, images, etc). + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + // If we're remapping separate samplers and images, only emit the combined samplers. + if (skip_separate_image_sampler) + { + // Sampler buffers are always used without a sampler, and they will also work in regular D3D. + bool sampler_buffer = type.basetype == SPIRType::Image && type.image.dim == DimBuffer; + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + if (!sampler_buffer && (separate_image || separate_sampler)) + return; + } + + if (var.storage != StorageClassFunction && !is_builtin_variable(var) && !var.remapped_variable && + type.pointer && (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter)) + { + emit_uniform(var); + emitted = true; + } + }); + + if (emitted) + statement(""); + emitted = false; + + // Emit builtin input and output variables here. + emit_builtin_variables(); + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + + // Do not emit I/O blocks here. + // I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders + // and tessellation down the line. + if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer && + (var.storage == StorageClassInput || var.storage == StorageClassOutput) && !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self)) + { + // Only emit non-builtins which are not blocks here. Builtin variables are handled separately. + emit_interface_block_globally(var); + emitted = true; + } + }); + + if (emitted) + statement(""); + emitted = false; + + require_input = false; + require_output = false; + unordered_set active_inputs; + unordered_set active_outputs; + SmallVector input_variables; + SmallVector output_variables; + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + + if (var.storage != StorageClassInput && var.storage != StorageClassOutput) + return; + + // Do not emit I/O blocks here. + // I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders + // and tessellation down the line. + if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self)) + { + if (var.storage == StorageClassInput) + input_variables.push_back(&var); + else + output_variables.push_back(&var); + } + + // Reserve input and output locations for block variables as necessary. + if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self)) + { + auto &active = var.storage == StorageClassInput ? active_inputs : active_outputs; + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + if (has_member_decoration(type.self, i, DecorationLocation)) + { + uint32_t location = get_member_decoration(type.self, i, DecorationLocation); + active.insert(location); + } + } + + // Emit the block struct and a global variable here. + emit_io_block(var); + } + }); + + const auto variable_compare = [&](const SPIRVariable *a, const SPIRVariable *b) -> bool { + // Sort input and output variables based on, from more robust to less robust: + // - Location + // - Variable has a location + // - Name comparison + // - Variable has a name + // - Fallback: ID + bool has_location_a = has_decoration(a->self, DecorationLocation); + bool has_location_b = has_decoration(b->self, DecorationLocation); + + if (has_location_a && has_location_b) + { + return get_decoration(a->self, DecorationLocation) < get_decoration(b->self, DecorationLocation); + } + else if (has_location_a && !has_location_b) + return true; + else if (!has_location_a && has_location_b) + return false; + + const auto &name1 = to_name(a->self); + const auto &name2 = to_name(b->self); + + if (name1.empty() && name2.empty()) + return a->self < b->self; + else if (name1.empty()) + return true; + else if (name2.empty()) + return false; + + return name1.compare(name2) < 0; + }; + + auto input_builtins = active_input_builtins; + input_builtins.clear(BuiltInNumWorkgroups); + input_builtins.clear(BuiltInPointCoord); + input_builtins.clear(BuiltInSubgroupSize); + input_builtins.clear(BuiltInSubgroupLocalInvocationId); + input_builtins.clear(BuiltInSubgroupEqMask); + input_builtins.clear(BuiltInSubgroupLtMask); + input_builtins.clear(BuiltInSubgroupLeMask); + input_builtins.clear(BuiltInSubgroupGtMask); + input_builtins.clear(BuiltInSubgroupGeMask); + + if (!input_variables.empty() || !input_builtins.empty()) + { + require_input = true; + statement("struct SPIRV_Cross_Input"); + + begin_scope(); + sort(input_variables.begin(), input_variables.end(), variable_compare); + for (auto var : input_variables) + emit_interface_block_in_struct(*var, active_inputs); + emit_builtin_inputs_in_struct(); + end_scope_decl(); + statement(""); + } + + if (!output_variables.empty() || !active_output_builtins.empty()) + { + require_output = true; + statement("struct SPIRV_Cross_Output"); + + begin_scope(); + // FIXME: Use locations properly if they exist. + sort(output_variables.begin(), output_variables.end(), variable_compare); + for (auto var : output_variables) + emit_interface_block_in_struct(*var, active_outputs); + emit_builtin_outputs_in_struct(); + end_scope_decl(); + statement(""); + } + + // Global variables. + for (auto global : global_variables) + { + auto &var = get(global); + if (var.storage != StorageClassOutput) + { + if (!variable_is_lut(var)) + { + add_resource_name(var.self); + + const char *storage = nullptr; + switch (var.storage) + { + case StorageClassWorkgroup: + storage = "groupshared"; + break; + + default: + storage = "static"; + break; + } + + string initializer; + if (options.force_zero_initialized_variables && var.storage == StorageClassPrivate && + !var.initializer && !var.static_expression && type_can_zero_initialize(get_variable_data_type(var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(var))); + } + statement(storage, " ", variable_decl(var), initializer, ";"); + + emitted = true; + } + } + } + + if (emitted) + statement(""); + + declare_undefined_values(); + + if (requires_op_fmod) + { + static const char *types[] = { + "float", + "float2", + "float3", + "float4", + }; + + for (auto &type : types) + { + statement(type, " mod(", type, " x, ", type, " y)"); + begin_scope(); + statement("return x - y * floor(x / y);"); + end_scope(); + statement(""); + } + } + + emit_texture_size_variants(required_texture_size_variants.srv, "4", false, ""); + for (uint32_t norm = 0; norm < 3; norm++) + { + for (uint32_t comp = 0; comp < 4; comp++) + { + static const char *qualifiers[] = { "", "unorm ", "snorm " }; + static const char *vecsizes[] = { "", "2", "3", "4" }; + emit_texture_size_variants(required_texture_size_variants.uav[norm][comp], vecsizes[comp], true, + qualifiers[norm]); + } + } + + if (requires_fp16_packing) + { + // HLSL does not pack into a single word sadly :( + statement("uint SPIRV_Cross_packHalf2x16(float2 value)"); + begin_scope(); + statement("uint2 Packed = f32tof16(value);"); + statement("return Packed.x | (Packed.y << 16);"); + end_scope(); + statement(""); + + statement("float2 SPIRV_Cross_unpackHalf2x16(uint value)"); + begin_scope(); + statement("return f16tof32(uint2(value & 0xffff, value >> 16));"); + end_scope(); + statement(""); + } + + if (requires_uint2_packing) + { + statement("uint64_t SPIRV_Cross_packUint2x32(uint2 value)"); + begin_scope(); + statement("return (uint64_t(value.y) << 32) | uint64_t(value.x);"); + end_scope(); + statement(""); + + statement("uint2 SPIRV_Cross_unpackUint2x32(uint64_t value)"); + begin_scope(); + statement("uint2 Unpacked;"); + statement("Unpacked.x = uint(value & 0xffffffff);"); + statement("Unpacked.y = uint(value >> 32);"); + statement("return Unpacked;"); + end_scope(); + statement(""); + } + + if (requires_explicit_fp16_packing) + { + // HLSL does not pack into a single word sadly :( + statement("uint SPIRV_Cross_packFloat2x16(min16float2 value)"); + begin_scope(); + statement("uint2 Packed = f32tof16(value);"); + statement("return Packed.x | (Packed.y << 16);"); + end_scope(); + statement(""); + + statement("min16float2 SPIRV_Cross_unpackFloat2x16(uint value)"); + begin_scope(); + statement("return min16float2(f16tof32(uint2(value & 0xffff, value >> 16)));"); + end_scope(); + statement(""); + } + + // HLSL does not seem to have builtins for these operation, so roll them by hand ... + if (requires_unorm8_packing) + { + statement("uint SPIRV_Cross_packUnorm4x8(float4 value)"); + begin_scope(); + statement("uint4 Packed = uint4(round(saturate(value) * 255.0));"); + statement("return Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24);"); + end_scope(); + statement(""); + + statement("float4 SPIRV_Cross_unpackUnorm4x8(uint value)"); + begin_scope(); + statement("uint4 Packed = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);"); + statement("return float4(Packed) / 255.0;"); + end_scope(); + statement(""); + } + + if (requires_snorm8_packing) + { + statement("uint SPIRV_Cross_packSnorm4x8(float4 value)"); + begin_scope(); + statement("int4 Packed = int4(round(clamp(value, -1.0, 1.0) * 127.0)) & 0xff;"); + statement("return uint(Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24));"); + end_scope(); + statement(""); + + statement("float4 SPIRV_Cross_unpackSnorm4x8(uint value)"); + begin_scope(); + statement("int SignedValue = int(value);"); + statement("int4 Packed = int4(SignedValue << 24, SignedValue << 16, SignedValue << 8, SignedValue) >> 24;"); + statement("return clamp(float4(Packed) / 127.0, -1.0, 1.0);"); + end_scope(); + statement(""); + } + + if (requires_unorm16_packing) + { + statement("uint SPIRV_Cross_packUnorm2x16(float2 value)"); + begin_scope(); + statement("uint2 Packed = uint2(round(saturate(value) * 65535.0));"); + statement("return Packed.x | (Packed.y << 16);"); + end_scope(); + statement(""); + + statement("float2 SPIRV_Cross_unpackUnorm2x16(uint value)"); + begin_scope(); + statement("uint2 Packed = uint2(value & 0xffff, value >> 16);"); + statement("return float2(Packed) / 65535.0;"); + end_scope(); + statement(""); + } + + if (requires_snorm16_packing) + { + statement("uint SPIRV_Cross_packSnorm2x16(float2 value)"); + begin_scope(); + statement("int2 Packed = int2(round(clamp(value, -1.0, 1.0) * 32767.0)) & 0xffff;"); + statement("return uint(Packed.x | (Packed.y << 16));"); + end_scope(); + statement(""); + + statement("float2 SPIRV_Cross_unpackSnorm2x16(uint value)"); + begin_scope(); + statement("int SignedValue = int(value);"); + statement("int2 Packed = int2(SignedValue << 16, SignedValue) >> 16;"); + statement("return clamp(float2(Packed) / 32767.0, -1.0, 1.0);"); + end_scope(); + statement(""); + } + + if (requires_bitfield_insert) + { + static const char *types[] = { "uint", "uint2", "uint3", "uint4" }; + for (auto &type : types) + { + statement(type, " SPIRV_Cross_bitfieldInsert(", type, " Base, ", type, " Insert, uint Offset, uint Count)"); + begin_scope(); + statement("uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31));"); + statement("return (Base & ~Mask) | ((Insert << Offset) & Mask);"); + end_scope(); + statement(""); + } + } + + if (requires_bitfield_extract) + { + static const char *unsigned_types[] = { "uint", "uint2", "uint3", "uint4" }; + for (auto &type : unsigned_types) + { + statement(type, " SPIRV_Cross_bitfieldUExtract(", type, " Base, uint Offset, uint Count)"); + begin_scope(); + statement("uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1);"); + statement("return (Base >> Offset) & Mask;"); + end_scope(); + statement(""); + } + + // In this overload, we will have to do sign-extension, which we will emulate by shifting up and down. + static const char *signed_types[] = { "int", "int2", "int3", "int4" }; + for (auto &type : signed_types) + { + statement(type, " SPIRV_Cross_bitfieldSExtract(", type, " Base, int Offset, int Count)"); + begin_scope(); + statement("int Mask = Count == 32 ? -1 : ((1 << Count) - 1);"); + statement(type, " Masked = (Base >> Offset) & Mask;"); + statement("int ExtendShift = (32 - Count) & 31;"); + statement("return (Masked << ExtendShift) >> ExtendShift;"); + end_scope(); + statement(""); + } + } + + if (requires_inverse_2x2) + { + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement("float2x2 SPIRV_Cross_Inverse(float2x2 m)"); + begin_scope(); + statement("float2x2 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = m[1][1];"); + statement("adj[0][1] = -m[0][1];"); + statement_no_indent(""); + statement("adj[1][0] = -m[1][0];"); + statement("adj[1][1] = m[0][0];"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + } + + if (requires_inverse_3x3) + { + statement("// Returns the determinant of a 2x2 matrix."); + statement("float SPIRV_Cross_Det2x2(float a1, float a2, float b1, float b2)"); + begin_scope(); + statement("return a1 * b2 - b1 * a2;"); + end_scope(); + statement_no_indent(""); + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement("float3x3 SPIRV_Cross_Inverse(float3x3 m)"); + begin_scope(); + statement("float3x3 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = SPIRV_Cross_Det2x2(m[1][1], m[1][2], m[2][1], m[2][2]);"); + statement("adj[0][1] = -SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[2][1], m[2][2]);"); + statement("adj[0][2] = SPIRV_Cross_Det2x2(m[0][1], m[0][2], m[1][1], m[1][2]);"); + statement_no_indent(""); + statement("adj[1][0] = -SPIRV_Cross_Det2x2(m[1][0], m[1][2], m[2][0], m[2][2]);"); + statement("adj[1][1] = SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[2][0], m[2][2]);"); + statement("adj[1][2] = -SPIRV_Cross_Det2x2(m[0][0], m[0][2], m[1][0], m[1][2]);"); + statement_no_indent(""); + statement("adj[2][0] = SPIRV_Cross_Det2x2(m[1][0], m[1][1], m[2][0], m[2][1]);"); + statement("adj[2][1] = -SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[2][0], m[2][1]);"); + statement("adj[2][2] = SPIRV_Cross_Det2x2(m[0][0], m[0][1], m[1][0], m[1][1]);"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + } + + if (requires_inverse_4x4) + { + if (!requires_inverse_3x3) + { + statement("// Returns the determinant of a 2x2 matrix."); + statement("float SPIRV_Cross_Det2x2(float a1, float a2, float b1, float b2)"); + begin_scope(); + statement("return a1 * b2 - b1 * a2;"); + end_scope(); + statement(""); + } + + statement("// Returns the determinant of a 3x3 matrix."); + statement("float SPIRV_Cross_Det3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, " + "float c2, float c3)"); + begin_scope(); + statement("return a1 * SPIRV_Cross_Det2x2(b2, b3, c2, c3) - b1 * SPIRV_Cross_Det2x2(a2, a3, c2, c3) + c1 * " + "SPIRV_Cross_Det2x2(a2, a3, " + "b2, b3);"); + end_scope(); + statement_no_indent(""); + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement("float4x4 SPIRV_Cross_Inverse(float4x4 m)"); + begin_scope(); + statement("float4x4 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement( + "adj[0][0] = SPIRV_Cross_Det3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement( + "adj[0][1] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement( + "adj[0][2] = SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement( + "adj[0][3] = -SPIRV_Cross_Det3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], " + "m[2][3]);"); + statement_no_indent(""); + statement( + "adj[1][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement( + "adj[1][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement( + "adj[1][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement( + "adj[1][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], " + "m[2][3]);"); + statement_no_indent(""); + statement( + "adj[2][0] = SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement( + "adj[2][1] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement( + "adj[2][2] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement( + "adj[2][3] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], " + "m[2][3]);"); + statement_no_indent(""); + statement( + "adj[3][0] = -SPIRV_Cross_Det3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement( + "adj[3][1] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement( + "adj[3][2] = -SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement( + "adj[3][3] = SPIRV_Cross_Det3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], " + "m[2][2]);"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] " + "* m[3][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + } + + if (requires_scalar_reflect) + { + // FP16/FP64? No templates in HLSL. + statement("float SPIRV_Cross_Reflect(float i, float n)"); + begin_scope(); + statement("return i - 2.0 * dot(n, i) * n;"); + end_scope(); + statement(""); + } + + if (requires_scalar_refract) + { + // FP16/FP64? No templates in HLSL. + statement("float SPIRV_Cross_Refract(float i, float n, float eta)"); + begin_scope(); + statement("float NoI = n * i;"); + statement("float NoI2 = NoI * NoI;"); + statement("float k = 1.0 - eta * eta * (1.0 - NoI2);"); + statement("if (k < 0.0)"); + begin_scope(); + statement("return 0.0;"); + end_scope(); + statement("else"); + begin_scope(); + statement("return eta * i - (eta * NoI + sqrt(k)) * n;"); + end_scope(); + end_scope(); + statement(""); + } + + if (requires_scalar_faceforward) + { + // FP16/FP64? No templates in HLSL. + statement("float SPIRV_Cross_FaceForward(float n, float i, float nref)"); + begin_scope(); + statement("return i * nref < 0.0 ? n : -n;"); + end_scope(); + statement(""); + } +} + +void CompilerHLSL::emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav, + const char *type_qualifier) +{ + if (variant_mask == 0) + return; + + static const char *types[QueryTypeCount] = { "float", "int", "uint" }; + static const char *dims[QueryDimCount] = { "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray", + "Texture3D", "Buffer", "TextureCube", "TextureCubeArray", + "Texture2DMS", "Texture2DMSArray" }; + + static const bool has_lod[QueryDimCount] = { true, true, true, true, true, false, true, true, false, false }; + + static const char *ret_types[QueryDimCount] = { + "uint", "uint2", "uint2", "uint3", "uint3", "uint", "uint2", "uint3", "uint2", "uint3", + }; + + static const uint32_t return_arguments[QueryDimCount] = { + 1, 2, 2, 3, 3, 1, 2, 3, 2, 3, + }; + + for (uint32_t index = 0; index < QueryDimCount; index++) + { + for (uint32_t type_index = 0; type_index < QueryTypeCount; type_index++) + { + uint32_t bit = 16 * type_index + index; + uint64_t mask = 1ull << bit; + + if ((variant_mask & mask) == 0) + continue; + + statement(ret_types[index], " SPIRV_Cross_", (uav ? "image" : "texture"), "Size(", (uav ? "RW" : ""), + dims[index], "<", type_qualifier, types[type_index], vecsize_qualifier, "> Tex, ", + (uav ? "" : "uint Level, "), "out uint Param)"); + begin_scope(); + statement(ret_types[index], " ret;"); + switch (return_arguments[index]) + { + case 1: + if (has_lod[index] && !uav) + statement("Tex.GetDimensions(Level, ret.x, Param);"); + else + { + statement("Tex.GetDimensions(ret.x);"); + statement("Param = 0u;"); + } + break; + case 2: + if (has_lod[index] && !uav) + statement("Tex.GetDimensions(Level, ret.x, ret.y, Param);"); + else if (!uav) + statement("Tex.GetDimensions(ret.x, ret.y, Param);"); + else + { + statement("Tex.GetDimensions(ret.x, ret.y);"); + statement("Param = 0u;"); + } + break; + case 3: + if (has_lod[index] && !uav) + statement("Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param);"); + else if (!uav) + statement("Tex.GetDimensions(ret.x, ret.y, ret.z, Param);"); + else + { + statement("Tex.GetDimensions(ret.x, ret.y, ret.z);"); + statement("Param = 0u;"); + } + break; + } + + statement("return ret;"); + end_scope(); + statement(""); + } + } +} + +string CompilerHLSL::layout_for_member(const SPIRType &type, uint32_t index) +{ + auto &flags = get_member_decoration_bitset(type.self, index); + + // HLSL can emit row_major or column_major decoration in any struct. + // Do not try to merge combined decorations for children like in GLSL. + + // Flip the convention. HLSL is a bit odd in that the memory layout is column major ... but the language API is "row-major". + // The way to deal with this is to multiply everything in inverse order, and reverse the memory layout. + if (flags.get(DecorationColMajor)) + return "row_major "; + else if (flags.get(DecorationRowMajor)) + return "column_major "; + + return ""; +} + +void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const string &qualifier, uint32_t base_offset) +{ + auto &membertype = get(member_type_id); + + Bitset memberflags; + auto &memb = ir.meta[type.self].members; + if (index < memb.size()) + memberflags = memb[index].decoration_flags; + + string qualifiers; + bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + + if (is_block) + qualifiers = to_interpolation_qualifiers(memberflags); + + string packing_offset; + bool is_push_constant = type.storage == StorageClassPushConstant; + + if ((has_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset) || is_push_constant) && + has_member_decoration(type.self, index, DecorationOffset)) + { + uint32_t offset = memb[index].offset - base_offset; + if (offset & 3) + SPIRV_CROSS_THROW("Cannot pack on tighter bounds than 4 bytes in HLSL."); + + static const char *packing_swizzle[] = { "", ".y", ".z", ".w" }; + packing_offset = join(" : packoffset(c", offset / 16, packing_swizzle[(offset & 15) >> 2], ")"); + } + + statement(layout_for_member(type, index), qualifiers, qualifier, + variable_decl(membertype, to_member_name(type, index)), packing_offset, ";"); +} + +void CompilerHLSL::emit_buffer_block(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + + bool is_uav = var.storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock); + + if (is_uav) + { + Bitset flags = ir.get_buffer_block_flags(var); + bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self); + bool is_coherent = flags.get(DecorationCoherent) && !is_readonly; + bool is_interlocked = interlocked_resources.count(var.self) > 0; + const char *type_name = "ByteAddressBuffer "; + if (!is_readonly) + type_name = is_interlocked ? "RasterizerOrderedByteAddressBuffer " : "RWByteAddressBuffer "; + add_resource_name(var.self); + statement(is_coherent ? "globallycoherent " : "", type_name, to_name(var.self), type_to_array_glsl(type), + to_resource_binding(var), ";"); + } + else + { + if (type.array.empty()) + { + // Flatten the top-level struct so we can use packoffset, + // this restriction is similar to GLSL where layout(offset) is not possible on sub-structs. + flattened_structs[var.self] = false; + + // Prefer the block name if possible. + auto buffer_name = to_name(type.self, false); + if (ir.meta[type.self].decoration.alias.empty() || + resource_names.find(buffer_name) != end(resource_names) || + block_names.find(buffer_name) != end(block_names)) + { + buffer_name = get_block_fallback_name(var.self); + } + + add_variable(block_names, resource_names, buffer_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + if (buffer_name.empty()) + buffer_name = join("_", get(var.basetype).self, "_", var.self); + + uint32_t failed_index = 0; + if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, &failed_index)) + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + else + { + SPIRV_CROSS_THROW(join("cbuffer ID ", var.self, " (name: ", buffer_name, "), member index ", + failed_index, " (name: ", to_member_name(type, failed_index), + ") cannot be expressed with either HLSL packing layout or packoffset.")); + } + + block_names.insert(buffer_name); + + // Save for post-reflection later. + declared_block_names[var.self] = buffer_name; + + type.member_name_cache.clear(); + // var.self can be used as a backup name for the block name, + // so we need to make sure we don't disturb the name here on a recompile. + // It will need to be reset if we have to recompile. + preserve_alias_on_reset(var.self); + add_resource_name(var.self); + statement("cbuffer ", buffer_name, to_resource_binding(var)); + begin_scope(); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + auto backup_name = get_member_name(type.self, i); + auto member_name = to_member_name(type, i); + member_name = join(to_name(var.self), "_", member_name); + ParsedIR::sanitize_underscores(member_name); + set_member_name(type.self, i, member_name); + emit_struct_member(type, member, i, ""); + set_member_name(type.self, i, backup_name); + i++; + } + + end_scope_decl(); + statement(""); + } + else + { + if (hlsl_options.shader_model < 51) + SPIRV_CROSS_THROW( + "Need ConstantBuffer to use arrays of UBOs, but this is only supported in SM 5.1."); + + add_resource_name(type.self); + add_resource_name(var.self); + + // ConstantBuffer does not support packoffset, so it is unuseable unless everything aligns as we expect. + uint32_t failed_index = 0; + if (!buffer_is_packing_standard(type, BufferPackingHLSLCbuffer, &failed_index)) + { + SPIRV_CROSS_THROW(join("HLSL ConstantBuffer ID ", var.self, " (name: ", to_name(type.self), + "), member index ", failed_index, " (name: ", to_member_name(type, failed_index), + ") cannot be expressed with normal HLSL packing rules.")); + } + + emit_struct(get(type.self)); + statement("ConstantBuffer<", to_name(type.self), "> ", to_name(var.self), type_to_array_glsl(type), + to_resource_binding(var), ";"); + } + } +} + +void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var) +{ + if (root_constants_layout.empty()) + { + emit_buffer_block(var); + } + else + { + for (const auto &layout : root_constants_layout) + { + auto &type = get(var.basetype); + + uint32_t failed_index = 0; + if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, &failed_index, layout.start, + layout.end)) + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + else + { + SPIRV_CROSS_THROW(join("Root constant cbuffer ID ", var.self, " (name: ", to_name(type.self), ")", + ", member index ", failed_index, " (name: ", to_member_name(type, failed_index), + ") cannot be expressed with either HLSL packing layout or packoffset.")); + } + + flattened_structs[var.self] = false; + type.member_name_cache.clear(); + add_resource_name(var.self); + auto &memb = ir.meta[type.self].members; + + statement("cbuffer SPIRV_CROSS_RootConstant_", to_name(var.self), + to_resource_register(HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT, 'b', layout.binding, layout.space)); + begin_scope(); + + // Index of the next field in the generated root constant constant buffer + auto constant_index = 0u; + + // Iterate over all member of the push constant and check which of the fields + // fit into the given root constant layout. + for (auto i = 0u; i < memb.size(); i++) + { + const auto offset = memb[i].offset; + if (layout.start <= offset && offset < layout.end) + { + const auto &member = type.member_types[i]; + + add_member_name(type, constant_index); + auto backup_name = get_member_name(type.self, i); + auto member_name = to_member_name(type, i); + member_name = join(to_name(var.self), "_", member_name); + ParsedIR::sanitize_underscores(member_name); + set_member_name(type.self, constant_index, member_name); + emit_struct_member(type, member, i, "", layout.start); + set_member_name(type.self, constant_index, backup_name); + + constant_index++; + } + } + + end_scope_decl(); + } + } +} + +string CompilerHLSL::to_sampler_expression(uint32_t id) +{ + auto expr = join("_", to_expression(id)); + auto index = expr.find_first_of('['); + if (index == string::npos) + { + return expr + "_sampler"; + } + else + { + // We have an expression like _ident[array], so we cannot tack on _sampler, insert it inside the string instead. + return expr.insert(index, "_sampler"); + } +} + +void CompilerHLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) +{ + if (hlsl_options.shader_model >= 40 && combined_image_samplers.empty()) + { + set(result_id, result_type, image_id, samp_id); + } + else + { + // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types. + emit_op(result_type, result_id, to_combined_image_sampler(image_id, samp_id), true, true); + } +} + +string CompilerHLSL::to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) +{ + string arg_str = CompilerGLSL::to_func_call_arg(arg, id); + + if (hlsl_options.shader_model <= 30) + return arg_str; + + // Manufacture automatic sampler arg if the arg is a SampledImage texture and we're in modern HLSL. + auto &type = expression_type(id); + + // We don't have to consider combined image samplers here via OpSampledImage because + // those variables cannot be passed as arguments to functions. + // Only global SampledImage variables may be used as arguments. + if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer) + arg_str += ", " + to_sampler_expression(id); + + return arg_str; +} + +void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) +{ + if (func.self != ir.default_entry_point) + add_function_overload(func); + + auto &execution = get_entry_point(); + // Avoid shadow declarations. + local_variable_names = resource_names; + + string decl; + + auto &type = get(func.return_type); + if (type.array.empty()) + { + decl += flags_to_qualifiers_glsl(type, return_flags); + decl += type_to_glsl(type); + decl += " "; + } + else + { + // We cannot return arrays in HLSL, so "return" through an out variable. + decl = "void "; + } + + if (func.self == ir.default_entry_point) + { + if (execution.model == ExecutionModelVertex) + decl += "vert_main"; + else if (execution.model == ExecutionModelFragment) + decl += "frag_main"; + else if (execution.model == ExecutionModelGLCompute) + decl += "comp_main"; + else + SPIRV_CROSS_THROW("Unsupported execution model."); + processing_entry_point = true; + } + else + decl += to_name(func.self); + + decl += "("; + SmallVector arglist; + + if (!type.array.empty()) + { + // Fake array returns by writing to an out array instead. + string out_argument; + out_argument += "out "; + out_argument += type_to_glsl(type); + out_argument += " "; + out_argument += "SPIRV_Cross_return_value"; + out_argument += type_to_array_glsl(type); + arglist.push_back(move(out_argument)); + } + + for (auto &arg : func.arguments) + { + // Do not pass in separate images or samplers if we're remapping + // to combined image samplers. + if (skip_argument(arg.id)) + continue; + + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation + // to use same name for variables. + // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Flatten a combined sampler to two separate arguments in modern HLSL. + auto &arg_type = get(arg.type); + if (hlsl_options.shader_model > 30 && arg_type.basetype == SPIRType::SampledImage && + arg_type.image.dim != DimBuffer) + { + // Manufacture automatic sampler arg for SampledImage texture + arglist.push_back(join(image_is_comparison(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ", + to_sampler_expression(arg.id), type_to_array_glsl(arg_type))); + } + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + for (auto &arg : func.shadow_arguments) + { + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation + // to use same name for variables. + // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + decl += merge(arglist); + decl += ")"; + statement(decl); +} + +void CompilerHLSL::emit_hlsl_entry_point() +{ + SmallVector arguments; + + if (require_input) + arguments.push_back("SPIRV_Cross_Input stage_input"); + + // Add I/O blocks as separate arguments with appropriate storage qualifier. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + + if (var.storage != StorageClassInput && var.storage != StorageClassOutput) + return; + + if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self)) + { + if (var.storage == StorageClassInput) + { + arguments.push_back(join("in ", variable_decl(type, join("stage_input", to_name(var.self))))); + } + else if (var.storage == StorageClassOutput) + { + arguments.push_back(join("out ", variable_decl(type, join("stage_output", to_name(var.self))))); + } + } + }); + + auto &execution = get_entry_point(); + + switch (execution.model) + { + case ExecutionModelGLCompute: + { + SpecializationConstant wg_x, wg_y, wg_z; + get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + uint32_t x = execution.workgroup_size.x; + uint32_t y = execution.workgroup_size.y; + uint32_t z = execution.workgroup_size.z; + + auto x_expr = wg_x.id ? get(wg_x.id).specialization_constant_macro_name : to_string(x); + auto y_expr = wg_y.id ? get(wg_y.id).specialization_constant_macro_name : to_string(y); + auto z_expr = wg_z.id ? get(wg_z.id).specialization_constant_macro_name : to_string(z); + + statement("[numthreads(", x_expr, ", ", y_expr, ", ", z_expr, ")]"); + break; + } + case ExecutionModelFragment: + if (execution.flags.get(ExecutionModeEarlyFragmentTests)) + statement("[earlydepthstencil]"); + break; + default: + break; + } + + statement(require_output ? "SPIRV_Cross_Output " : "void ", "main(", merge(arguments), ")"); + begin_scope(); + bool legacy = hlsl_options.shader_model <= 30; + + // Copy builtins from entry point arguments to globals. + active_input_builtins.for_each_bit([&](uint32_t i) { + auto builtin = builtin_to_glsl(static_cast(i), StorageClassInput); + switch (static_cast(i)) + { + case BuiltInFragCoord: + // VPOS in D3D9 is sampled at integer locations, apply half-pixel offset to be consistent. + // TODO: Do we need an option here? Any reason why a D3D9 shader would be used + // on a D3D10+ system with a different rasterization config? + if (legacy) + statement(builtin, " = stage_input.", builtin, " + float4(0.5f, 0.5f, 0.0f, 0.0f);"); + else + { + statement(builtin, " = stage_input.", builtin, ";"); + // ZW are undefined in D3D9, only do this fixup here. + statement(builtin, ".w = 1.0 / ", builtin, ".w;"); + } + break; + + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInInstanceIndex: + // D3D semantics are uint, but shader wants int. + if (hlsl_options.support_nonzero_base_vertex_base_instance) + { + if (static_cast(i) == BuiltInInstanceIndex) + statement(builtin, " = int(stage_input.", builtin, ") + SPIRV_Cross_BaseInstance;"); + else + statement(builtin, " = int(stage_input.", builtin, ") + SPIRV_Cross_BaseVertex;"); + } + else + statement(builtin, " = int(stage_input.", builtin, ");"); + break; + + case BuiltInInstanceId: + // D3D semantics are uint, but shader wants int. + statement(builtin, " = int(stage_input.", builtin, ");"); + break; + + case BuiltInNumWorkgroups: + case BuiltInPointCoord: + case BuiltInSubgroupSize: + case BuiltInSubgroupLocalInvocationId: + break; + + case BuiltInSubgroupEqMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("gl_SubgroupEqMask = 1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96));"); + statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupEqMask.x = 0;"); + statement("if (WaveGetLaneIndex() >= 64 || WaveGetLaneIndex() < 32) gl_SubgroupEqMask.y = 0;"); + statement("if (WaveGetLaneIndex() >= 96 || WaveGetLaneIndex() < 64) gl_SubgroupEqMask.z = 0;"); + statement("if (WaveGetLaneIndex() < 96) gl_SubgroupEqMask.w = 0;"); + break; + + case BuiltInSubgroupGeMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("gl_SubgroupGeMask = ~((1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u);"); + statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupGeMask.x = 0u;"); + statement("if (WaveGetLaneIndex() >= 64) gl_SubgroupGeMask.y = 0u;"); + statement("if (WaveGetLaneIndex() >= 96) gl_SubgroupGeMask.z = 0u;"); + statement("if (WaveGetLaneIndex() < 32) gl_SubgroupGeMask.y = ~0u;"); + statement("if (WaveGetLaneIndex() < 64) gl_SubgroupGeMask.z = ~0u;"); + statement("if (WaveGetLaneIndex() < 96) gl_SubgroupGeMask.w = ~0u;"); + break; + + case BuiltInSubgroupGtMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("uint gt_lane_index = WaveGetLaneIndex() + 1;"); + statement("gl_SubgroupGtMask = ~((1u << (gt_lane_index - uint4(0, 32, 64, 96))) - 1u);"); + statement("if (gt_lane_index >= 32) gl_SubgroupGtMask.x = 0u;"); + statement("if (gt_lane_index >= 64) gl_SubgroupGtMask.y = 0u;"); + statement("if (gt_lane_index >= 96) gl_SubgroupGtMask.z = 0u;"); + statement("if (gt_lane_index >= 128) gl_SubgroupGtMask.w = 0u;"); + statement("if (gt_lane_index < 32) gl_SubgroupGtMask.y = ~0u;"); + statement("if (gt_lane_index < 64) gl_SubgroupGtMask.z = ~0u;"); + statement("if (gt_lane_index < 96) gl_SubgroupGtMask.w = ~0u;"); + break; + + case BuiltInSubgroupLeMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("uint le_lane_index = WaveGetLaneIndex() + 1;"); + statement("gl_SubgroupLeMask = (1u << (le_lane_index - uint4(0, 32, 64, 96))) - 1u;"); + statement("if (le_lane_index >= 32) gl_SubgroupLeMask.x = ~0u;"); + statement("if (le_lane_index >= 64) gl_SubgroupLeMask.y = ~0u;"); + statement("if (le_lane_index >= 96) gl_SubgroupLeMask.z = ~0u;"); + statement("if (le_lane_index >= 128) gl_SubgroupLeMask.w = ~0u;"); + statement("if (le_lane_index < 32) gl_SubgroupLeMask.y = 0u;"); + statement("if (le_lane_index < 64) gl_SubgroupLeMask.z = 0u;"); + statement("if (le_lane_index < 96) gl_SubgroupLeMask.w = 0u;"); + break; + + case BuiltInSubgroupLtMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("gl_SubgroupLtMask = (1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u;"); + statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupLtMask.x = ~0u;"); + statement("if (WaveGetLaneIndex() >= 64) gl_SubgroupLtMask.y = ~0u;"); + statement("if (WaveGetLaneIndex() >= 96) gl_SubgroupLtMask.z = ~0u;"); + statement("if (WaveGetLaneIndex() < 32) gl_SubgroupLtMask.y = 0u;"); + statement("if (WaveGetLaneIndex() < 64) gl_SubgroupLtMask.z = 0u;"); + statement("if (WaveGetLaneIndex() < 96) gl_SubgroupLtMask.w = 0u;"); + break; + + case BuiltInClipDistance: + for (uint32_t clip = 0; clip < clip_distance_count; clip++) + statement("gl_ClipDistance[", clip, "] = stage_input.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3], + ";"); + break; + + case BuiltInCullDistance: + for (uint32_t cull = 0; cull < cull_distance_count; cull++) + statement("gl_CullDistance[", cull, "] = stage_input.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3], + ";"); + break; + + default: + statement(builtin, " = stage_input.", builtin, ";"); + break; + } + }); + + // Copy from stage input struct to globals. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + + if (var.storage != StorageClassInput) + return; + + bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex; + + if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self)) + { + auto name = to_name(var.self); + auto &mtype = this->get(var.basetype); + if (need_matrix_unroll && mtype.columns > 1) + { + // Unroll matrices. + for (uint32_t col = 0; col < mtype.columns; col++) + statement(name, "[", col, "] = stage_input.", name, "_", col, ";"); + } + else + { + statement(name, " = stage_input.", name, ";"); + } + } + + // I/O blocks don't use the common stage input/output struct, but separate outputs. + if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self)) + { + auto name = to_name(var.self); + statement(name, " = stage_input", name, ";"); + } + }); + + // Run the shader. + if (execution.model == ExecutionModelVertex) + statement("vert_main();"); + else if (execution.model == ExecutionModelFragment) + statement("frag_main();"); + else if (execution.model == ExecutionModelGLCompute) + statement("comp_main();"); + else + SPIRV_CROSS_THROW("Unsupported shader stage."); + + // Copy block outputs. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + + if (var.storage != StorageClassOutput) + return; + + // I/O blocks don't use the common stage input/output struct, but separate outputs. + if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self)) + { + auto name = to_name(var.self); + statement("stage_output", name, " = ", name, ";"); + } + }); + + // Copy stage outputs. + if (require_output) + { + statement("SPIRV_Cross_Output stage_output;"); + + // Copy builtins from globals to return struct. + active_output_builtins.for_each_bit([&](uint32_t i) { + // PointSize doesn't exist in HLSL. + if (i == BuiltInPointSize) + return; + + switch (static_cast(i)) + { + case BuiltInClipDistance: + for (uint32_t clip = 0; clip < clip_distance_count; clip++) + statement("stage_output.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3], " = gl_ClipDistance[", + clip, "];"); + break; + + case BuiltInCullDistance: + for (uint32_t cull = 0; cull < cull_distance_count; cull++) + statement("stage_output.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3], " = gl_CullDistance[", + cull, "];"); + break; + + default: + { + auto builtin_expr = builtin_to_glsl(static_cast(i), StorageClassOutput); + statement("stage_output.", builtin_expr, " = ", builtin_expr, ";"); + break; + } + } + }); + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + + if (var.storage != StorageClassOutput) + return; + + if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer && + !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self)) + { + auto name = to_name(var.self); + + if (legacy && execution.model == ExecutionModelFragment) + { + string output_filler; + for (uint32_t size = type.vecsize; size < 4; ++size) + output_filler += ", 0.0"; + + statement("stage_output.", name, " = float4(", name, output_filler, ");"); + } + else + { + statement("stage_output.", name, " = ", name, ";"); + } + } + }); + + statement("return stage_output;"); + } + + end_scope(); +} + +void CompilerHLSL::emit_fixup() +{ + if (is_vertex_like_shader()) + { + // Do various mangling on the gl_Position. + if (hlsl_options.shader_model <= 30) + { + statement("gl_Position.x = gl_Position.x - gl_HalfPixel.x * " + "gl_Position.w;"); + statement("gl_Position.y = gl_Position.y + gl_HalfPixel.y * " + "gl_Position.w;"); + } + + if (options.vertex.flip_vert_y) + statement("gl_Position.y = -gl_Position.y;"); + if (options.vertex.fixup_clipspace) + statement("gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;"); + } +} + +void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse) +{ + if (sparse) + SPIRV_CROSS_THROW("Sparse feedback not yet supported in HLSL."); + + auto *ops = stream(i); + auto op = static_cast(i.op); + uint32_t length = i.length; + + SmallVector inherited_expressions; + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + VariableID img = ops[2]; + uint32_t coord = ops[3]; + uint32_t dref = 0; + uint32_t comp = 0; + bool gather = false; + bool proj = false; + const uint32_t *opt = nullptr; + auto *combined_image = maybe_get(img); + auto img_expr = to_expression(combined_image ? combined_image->image : img); + + inherited_expressions.push_back(coord); + + // Make sure non-uniform decoration is back-propagated to where it needs to be. + if (has_decoration(img, DecorationNonUniformEXT)) + propagate_nonuniform_qualifier(img); + + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + dref = ops[4]; + opt = &ops[5]; + length -= 5; + break; + + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + dref = ops[4]; + proj = true; + opt = &ops[5]; + length -= 5; + break; + + case OpImageDrefGather: + dref = ops[4]; + opt = &ops[5]; + gather = true; + length -= 5; + break; + + case OpImageGather: + comp = ops[4]; + opt = &ops[5]; + gather = true; + length -= 5; + break; + + case OpImageSampleProjImplicitLod: + case OpImageSampleProjExplicitLod: + opt = &ops[4]; + length -= 4; + proj = true; + break; + + case OpImageQueryLod: + opt = &ops[4]; + length -= 4; + break; + + default: + opt = &ops[4]; + length -= 4; + break; + } + + auto &imgtype = expression_type(img); + uint32_t coord_components = 0; + switch (imgtype.image.dim) + { + case spv::Dim1D: + coord_components = 1; + break; + case spv::Dim2D: + coord_components = 2; + break; + case spv::Dim3D: + coord_components = 3; + break; + case spv::DimCube: + coord_components = 3; + break; + case spv::DimBuffer: + coord_components = 1; + break; + default: + coord_components = 2; + break; + } + + if (dref) + inherited_expressions.push_back(dref); + + if (imgtype.image.arrayed) + coord_components++; + + uint32_t bias = 0; + uint32_t lod = 0; + uint32_t grad_x = 0; + uint32_t grad_y = 0; + uint32_t coffset = 0; + uint32_t offset = 0; + uint32_t coffsets = 0; + uint32_t sample = 0; + uint32_t minlod = 0; + uint32_t flags = 0; + + if (length) + { + flags = opt[0]; + opt++; + length--; + } + + auto test = [&](uint32_t &v, uint32_t flag) { + if (length && (flags & flag)) + { + v = *opt++; + inherited_expressions.push_back(v); + length--; + } + }; + + test(bias, ImageOperandsBiasMask); + test(lod, ImageOperandsLodMask); + test(grad_x, ImageOperandsGradMask); + test(grad_y, ImageOperandsGradMask); + test(coffset, ImageOperandsConstOffsetMask); + test(offset, ImageOperandsOffsetMask); + test(coffsets, ImageOperandsConstOffsetsMask); + test(sample, ImageOperandsSampleMask); + test(minlod, ImageOperandsMinLodMask); + + string expr; + string texop; + + if (minlod != 0) + SPIRV_CROSS_THROW("MinLod texture operand not supported in HLSL."); + + if (op == OpImageFetch) + { + if (hlsl_options.shader_model < 40) + { + SPIRV_CROSS_THROW("texelFetch is not supported in HLSL shader model 2/3."); + } + texop += img_expr; + texop += ".Load"; + } + else if (op == OpImageQueryLod) + { + texop += img_expr; + texop += ".CalculateLevelOfDetail"; + } + else + { + auto &imgformat = get(imgtype.image.type); + if (imgformat.basetype != SPIRType::Float) + { + SPIRV_CROSS_THROW("Sampling non-float textures is not supported in HLSL."); + } + + if (hlsl_options.shader_model >= 40) + { + texop += img_expr; + + if (image_is_comparison(imgtype, img)) + { + if (gather) + { + SPIRV_CROSS_THROW("GatherCmp does not exist in HLSL."); + } + else if (lod || grad_x || grad_y) + { + // Assume we want a fixed level, and the only thing we can get in HLSL is SampleCmpLevelZero. + texop += ".SampleCmpLevelZero"; + } + else + texop += ".SampleCmp"; + } + else if (gather) + { + uint32_t comp_num = evaluate_constant_u32(comp); + if (hlsl_options.shader_model >= 50) + { + switch (comp_num) + { + case 0: + texop += ".GatherRed"; + break; + case 1: + texop += ".GatherGreen"; + break; + case 2: + texop += ".GatherBlue"; + break; + case 3: + texop += ".GatherAlpha"; + break; + default: + SPIRV_CROSS_THROW("Invalid component."); + } + } + else + { + if (comp_num == 0) + texop += ".Gather"; + else + SPIRV_CROSS_THROW("HLSL shader model 4 can only gather from the red component."); + } + } + else if (bias) + texop += ".SampleBias"; + else if (grad_x || grad_y) + texop += ".SampleGrad"; + else if (lod) + texop += ".SampleLevel"; + else + texop += ".Sample"; + } + else + { + switch (imgtype.image.dim) + { + case Dim1D: + texop += "tex1D"; + break; + case Dim2D: + texop += "tex2D"; + break; + case Dim3D: + texop += "tex3D"; + break; + case DimCube: + texop += "texCUBE"; + break; + case DimRect: + case DimBuffer: + case DimSubpassData: + SPIRV_CROSS_THROW("Buffer texture support is not yet implemented for HLSL"); // TODO + default: + SPIRV_CROSS_THROW("Invalid dimension."); + } + + if (gather) + SPIRV_CROSS_THROW("textureGather is not supported in HLSL shader model 2/3."); + if (offset || coffset) + SPIRV_CROSS_THROW("textureOffset is not supported in HLSL shader model 2/3."); + + if (grad_x || grad_y) + texop += "grad"; + else if (lod) + texop += "lod"; + else if (bias) + texop += "bias"; + else if (proj || dref) + texop += "proj"; + } + } + + expr += texop; + expr += "("; + if (hlsl_options.shader_model < 40) + { + if (combined_image) + SPIRV_CROSS_THROW("Separate images/samplers are not supported in HLSL shader model 2/3."); + expr += to_expression(img); + } + else if (op != OpImageFetch) + { + string sampler_expr; + if (combined_image) + sampler_expr = to_expression(combined_image->sampler); + else + sampler_expr = to_sampler_expression(img); + expr += sampler_expr; + } + + auto swizzle = [](uint32_t comps, uint32_t in_comps) -> const char * { + if (comps == in_comps) + return ""; + + switch (comps) + { + case 1: + return ".x"; + case 2: + return ".xy"; + case 3: + return ".xyz"; + default: + return ""; + } + }; + + bool forward = should_forward(coord); + + // The IR can give us more components than we need, so chop them off as needed. + string coord_expr; + auto &coord_type = expression_type(coord); + if (coord_components != coord_type.vecsize) + coord_expr = to_enclosed_expression(coord) + swizzle(coord_components, expression_type(coord).vecsize); + else + coord_expr = to_expression(coord); + + if (proj && hlsl_options.shader_model >= 40) // Legacy HLSL has "proj" operations which do this for us. + coord_expr = coord_expr + " / " + to_extract_component_expression(coord, coord_components); + + if (hlsl_options.shader_model < 40) + { + if (dref) + { + if (imgtype.image.dim != spv::Dim1D && imgtype.image.dim != spv::Dim2D) + { + SPIRV_CROSS_THROW( + "Depth comparison is only supported for 1D and 2D textures in HLSL shader model 2/3."); + } + + if (grad_x || grad_y) + SPIRV_CROSS_THROW("Depth comparison is not supported for grad sampling in HLSL shader model 2/3."); + + for (uint32_t size = coord_components; size < 2; ++size) + coord_expr += ", 0.0"; + + forward = forward && should_forward(dref); + coord_expr += ", " + to_expression(dref); + } + else if (lod || bias || proj) + { + for (uint32_t size = coord_components; size < 3; ++size) + coord_expr += ", 0.0"; + } + + if (lod) + { + coord_expr = "float4(" + coord_expr + ", " + to_expression(lod) + ")"; + } + else if (bias) + { + coord_expr = "float4(" + coord_expr + ", " + to_expression(bias) + ")"; + } + else if (proj) + { + coord_expr = "float4(" + coord_expr + ", " + to_extract_component_expression(coord, coord_components) + ")"; + } + else if (dref) + { + // A "normal" sample gets fed into tex2Dproj as well, because the + // regular tex2D accepts only two coordinates. + coord_expr = "float4(" + coord_expr + ", 1.0)"; + } + + if (!!lod + !!bias + !!proj > 1) + SPIRV_CROSS_THROW("Legacy HLSL can only use one of lod/bias/proj modifiers."); + } + + if (op == OpImageFetch) + { + if (imgtype.image.dim != DimBuffer && !imgtype.image.ms) + coord_expr = + join("int", coord_components + 1, "(", coord_expr, ", ", lod ? to_expression(lod) : string("0"), ")"); + } + else + expr += ", "; + expr += coord_expr; + + if (dref && hlsl_options.shader_model >= 40) + { + forward = forward && should_forward(dref); + expr += ", "; + + if (proj) + expr += to_enclosed_expression(dref) + " / " + to_extract_component_expression(coord, coord_components); + else + expr += to_expression(dref); + } + + if (!dref && (grad_x || grad_y)) + { + forward = forward && should_forward(grad_x); + forward = forward && should_forward(grad_y); + expr += ", "; + expr += to_expression(grad_x); + expr += ", "; + expr += to_expression(grad_y); + } + + if (!dref && lod && hlsl_options.shader_model >= 40 && op != OpImageFetch) + { + forward = forward && should_forward(lod); + expr += ", "; + expr += to_expression(lod); + } + + if (!dref && bias && hlsl_options.shader_model >= 40) + { + forward = forward && should_forward(bias); + expr += ", "; + expr += to_expression(bias); + } + + if (coffset) + { + forward = forward && should_forward(coffset); + expr += ", "; + expr += to_expression(coffset); + } + else if (offset) + { + forward = forward && should_forward(offset); + expr += ", "; + expr += to_expression(offset); + } + + if (sample) + { + expr += ", "; + expr += to_expression(sample); + } + + expr += ")"; + + if (dref && hlsl_options.shader_model < 40) + expr += ".x"; + + if (op == OpImageQueryLod) + { + // This is rather awkward. + // textureQueryLod returns two values, the "accessed level", + // as well as the actual LOD lambda. + // As far as I can tell, there is no way to get the .x component + // according to GLSL spec, and it depends on the sampler itself. + // Just assume X == Y, so we will need to splat the result to a float2. + statement("float _", id, "_tmp = ", expr, ";"); + statement("float2 _", id, " = _", id, "_tmp.xx;"); + set(id, join("_", id), result_type, true); + } + else + { + emit_op(result_type, id, expr, forward, false); + } + + for (auto &inherit : inherited_expressions) + inherit_expression_dependencies(id, inherit); + + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjDrefImplicitLod: + register_control_dependent_expression(id); + break; + + default: + break; + } +} + +string CompilerHLSL::to_resource_binding(const SPIRVariable &var) +{ + const auto &type = get(var.basetype); + + // We can remap push constant blocks, even if they don't have any binding decoration. + if (type.storage != StorageClassPushConstant && !has_decoration(var.self, DecorationBinding)) + return ""; + + char space = '\0'; + + HLSLBindingFlagBits resource_flags = HLSL_BINDING_AUTO_NONE_BIT; + + switch (type.basetype) + { + case SPIRType::SampledImage: + space = 't'; // SRV + resource_flags = HLSL_BINDING_AUTO_SRV_BIT; + break; + + case SPIRType::Image: + if (type.image.sampled == 2 && type.image.dim != DimSubpassData) + { + if (has_decoration(var.self, DecorationNonWritable) && hlsl_options.nonwritable_uav_texture_as_srv) + { + space = 't'; // SRV + resource_flags = HLSL_BINDING_AUTO_SRV_BIT; + } + else + { + space = 'u'; // UAV + resource_flags = HLSL_BINDING_AUTO_UAV_BIT; + } + } + else + { + space = 't'; // SRV + resource_flags = HLSL_BINDING_AUTO_SRV_BIT; + } + break; + + case SPIRType::Sampler: + space = 's'; + resource_flags = HLSL_BINDING_AUTO_SAMPLER_BIT; + break; + + case SPIRType::Struct: + { + auto storage = type.storage; + if (storage == StorageClassUniform) + { + if (has_decoration(type.self, DecorationBufferBlock)) + { + Bitset flags = ir.get_buffer_block_flags(var); + bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self); + space = is_readonly ? 't' : 'u'; // UAV + resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT; + } + else if (has_decoration(type.self, DecorationBlock)) + { + space = 'b'; // Constant buffers + resource_flags = HLSL_BINDING_AUTO_CBV_BIT; + } + } + else if (storage == StorageClassPushConstant) + { + space = 'b'; // Constant buffers + resource_flags = HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT; + } + else if (storage == StorageClassStorageBuffer) + { + // UAV or SRV depending on readonly flag. + Bitset flags = ir.get_buffer_block_flags(var); + bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self); + space = is_readonly ? 't' : 'u'; + resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT; + } + + break; + } + default: + break; + } + + if (!space) + return ""; + + uint32_t desc_set = + resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantDescriptorSet : 0u; + uint32_t binding = resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantBinding : 0u; + + if (has_decoration(var.self, DecorationBinding)) + binding = get_decoration(var.self, DecorationBinding); + if (has_decoration(var.self, DecorationDescriptorSet)) + desc_set = get_decoration(var.self, DecorationDescriptorSet); + + return to_resource_register(resource_flags, space, binding, desc_set); +} + +string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) +{ + // For combined image samplers. + if (!has_decoration(var.self, DecorationBinding)) + return ""; + + return to_resource_register(HLSL_BINDING_AUTO_SAMPLER_BIT, 's', get_decoration(var.self, DecorationBinding), + get_decoration(var.self, DecorationDescriptorSet)); +} + +void CompilerHLSL::remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding) +{ + auto itr = resource_bindings.find({ get_execution_model(), desc_set, binding }); + if (itr != end(resource_bindings)) + { + auto &remap = itr->second; + remap.second = true; + + switch (type) + { + case HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT: + case HLSL_BINDING_AUTO_CBV_BIT: + desc_set = remap.first.cbv.register_space; + binding = remap.first.cbv.register_binding; + break; + + case HLSL_BINDING_AUTO_SRV_BIT: + desc_set = remap.first.srv.register_space; + binding = remap.first.srv.register_binding; + break; + + case HLSL_BINDING_AUTO_SAMPLER_BIT: + desc_set = remap.first.sampler.register_space; + binding = remap.first.sampler.register_binding; + break; + + case HLSL_BINDING_AUTO_UAV_BIT: + desc_set = remap.first.uav.register_space; + binding = remap.first.uav.register_binding; + break; + + default: + break; + } + } +} + +string CompilerHLSL::to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t space_set) +{ + if ((flag & resource_binding_flags) == 0) + { + remap_hlsl_resource_binding(flag, space_set, binding); + + // The push constant block did not have a binding, and there were no remap for it, + // so, declare without register binding. + if (flag == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT && space_set == ResourceBindingPushConstantDescriptorSet) + return ""; + + if (hlsl_options.shader_model >= 51) + return join(" : register(", space, binding, ", space", space_set, ")"); + else + return join(" : register(", space, binding, ")"); + } + else + return ""; +} + +void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + switch (type.basetype) + { + case SPIRType::SampledImage: + case SPIRType::Image: + { + bool is_coherent = false; + if (type.basetype == SPIRType::Image && type.image.sampled == 2) + is_coherent = has_decoration(var.self, DecorationCoherent); + + statement(is_coherent ? "globallycoherent " : "", image_type_hlsl_modern(type, var.self), " ", + to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";"); + + if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer) + { + // For combined image samplers, also emit a combined image sampler. + if (image_is_comparison(type, var.self)) + statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type), + to_resource_binding_sampler(var), ";"); + else + statement("SamplerState ", to_sampler_expression(var.self), type_to_array_glsl(type), + to_resource_binding_sampler(var), ";"); + } + break; + } + + case SPIRType::Sampler: + if (comparison_ids.count(var.self)) + statement("SamplerComparisonState ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), + ";"); + else + statement("SamplerState ", to_name(var.self), type_to_array_glsl(type), to_resource_binding(var), ";"); + break; + + default: + statement(variable_decl(var), to_resource_binding(var), ";"); + break; + } +} + +void CompilerHLSL::emit_legacy_uniform(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + switch (type.basetype) + { + case SPIRType::Sampler: + case SPIRType::Image: + SPIRV_CROSS_THROW("Separate image and samplers not supported in legacy HLSL."); + + default: + statement(variable_decl(var), ";"); + break; + } +} + +void CompilerHLSL::emit_uniform(const SPIRVariable &var) +{ + add_resource_name(var.self); + if (hlsl_options.shader_model >= 40) + emit_modern_uniform(var); + else + emit_legacy_uniform(var); +} + +bool CompilerHLSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) +{ + return false; +} + +string CompilerHLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) +{ + if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Int) + return type_to_glsl(out_type); + else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Int64) + return type_to_glsl(out_type); + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Float) + return "asuint"; + else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::UInt) + return type_to_glsl(out_type); + else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::UInt64) + return type_to_glsl(out_type); + else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Float) + return "asint"; + else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::UInt) + return "asfloat"; + else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::Int) + return "asfloat"; + else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Double) + SPIRV_CROSS_THROW("Double to Int64 is not supported in HLSL."); + else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Double) + SPIRV_CROSS_THROW("Double to UInt64 is not supported in HLSL."); + else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::Int64) + return "asdouble"; + else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::UInt64) + return "asdouble"; + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1) + { + if (!requires_explicit_fp16_packing) + { + requires_explicit_fp16_packing = true; + force_recompile(); + } + return "SPIRV_Cross_unpackFloat2x16"; + } + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Half && in_type.vecsize == 2) + { + if (!requires_explicit_fp16_packing) + { + requires_explicit_fp16_packing = true; + force_recompile(); + } + return "SPIRV_Cross_packFloat2x16"; + } + else + return ""; +} + +void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count) +{ + auto op = static_cast(eop); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, count); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + switch (op) + { + case GLSLstd450InverseSqrt: + emit_unary_func_op(result_type, id, args[0], "rsqrt"); + break; + + case GLSLstd450Fract: + emit_unary_func_op(result_type, id, args[0], "frac"); + break; + + case GLSLstd450RoundEven: + if (hlsl_options.shader_model < 40) + SPIRV_CROSS_THROW("roundEven is not supported in HLSL shader model 2/3."); + emit_unary_func_op(result_type, id, args[0], "round"); + break; + + case GLSLstd450Acosh: + case GLSLstd450Asinh: + case GLSLstd450Atanh: + SPIRV_CROSS_THROW("Inverse hyperbolics are not supported on HLSL."); + + case GLSLstd450FMix: + case GLSLstd450IMix: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "lerp"); + break; + + case GLSLstd450Atan2: + emit_binary_func_op(result_type, id, args[0], args[1], "atan2"); + break; + + case GLSLstd450Fma: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "mad"); + break; + + case GLSLstd450InterpolateAtCentroid: + emit_unary_func_op(result_type, id, args[0], "EvaluateAttributeAtCentroid"); + break; + case GLSLstd450InterpolateAtSample: + emit_binary_func_op(result_type, id, args[0], args[1], "EvaluateAttributeAtSample"); + break; + case GLSLstd450InterpolateAtOffset: + emit_binary_func_op(result_type, id, args[0], args[1], "EvaluateAttributeSnapped"); + break; + + case GLSLstd450PackHalf2x16: + if (!requires_fp16_packing) + { + requires_fp16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packHalf2x16"); + break; + + case GLSLstd450UnpackHalf2x16: + if (!requires_fp16_packing) + { + requires_fp16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackHalf2x16"); + break; + + case GLSLstd450PackSnorm4x8: + if (!requires_snorm8_packing) + { + requires_snorm8_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packSnorm4x8"); + break; + + case GLSLstd450UnpackSnorm4x8: + if (!requires_snorm8_packing) + { + requires_snorm8_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackSnorm4x8"); + break; + + case GLSLstd450PackUnorm4x8: + if (!requires_unorm8_packing) + { + requires_unorm8_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packUnorm4x8"); + break; + + case GLSLstd450UnpackUnorm4x8: + if (!requires_unorm8_packing) + { + requires_unorm8_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackUnorm4x8"); + break; + + case GLSLstd450PackSnorm2x16: + if (!requires_snorm16_packing) + { + requires_snorm16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packSnorm2x16"); + break; + + case GLSLstd450UnpackSnorm2x16: + if (!requires_snorm16_packing) + { + requires_snorm16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackSnorm2x16"); + break; + + case GLSLstd450PackUnorm2x16: + if (!requires_unorm16_packing) + { + requires_unorm16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_packUnorm2x16"); + break; + + case GLSLstd450UnpackUnorm2x16: + if (!requires_unorm16_packing) + { + requires_unorm16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_unpackUnorm2x16"); + break; + + case GLSLstd450PackDouble2x32: + case GLSLstd450UnpackDouble2x32: + SPIRV_CROSS_THROW("packDouble2x32/unpackDouble2x32 not supported in HLSL."); + + case GLSLstd450FindILsb: + { + auto basetype = expression_type(args[0]).basetype; + emit_unary_func_op_cast(result_type, id, args[0], "firstbitlow", basetype, basetype); + break; + } + + case GLSLstd450FindSMsb: + emit_unary_func_op_cast(result_type, id, args[0], "firstbithigh", int_type, int_type); + break; + + case GLSLstd450FindUMsb: + emit_unary_func_op_cast(result_type, id, args[0], "firstbithigh", uint_type, uint_type); + break; + + case GLSLstd450MatrixInverse: + { + auto &type = get(result_type); + if (type.vecsize == 2 && type.columns == 2) + { + if (!requires_inverse_2x2) + { + requires_inverse_2x2 = true; + force_recompile(); + } + } + else if (type.vecsize == 3 && type.columns == 3) + { + if (!requires_inverse_3x3) + { + requires_inverse_3x3 = true; + force_recompile(); + } + } + else if (type.vecsize == 4 && type.columns == 4) + { + if (!requires_inverse_4x4) + { + requires_inverse_4x4 = true; + force_recompile(); + } + } + emit_unary_func_op(result_type, id, args[0], "SPIRV_Cross_Inverse"); + break; + } + + case GLSLstd450Normalize: + // HLSL does not support scalar versions here. + if (expression_type(args[0]).vecsize == 1) + { + // Returns -1 or 1 for valid input, sign() does the job. + emit_unary_func_op(result_type, id, args[0], "sign"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Reflect: + if (get(result_type).vecsize == 1) + { + if (!requires_scalar_reflect) + { + requires_scalar_reflect = true; + force_recompile(); + } + emit_binary_func_op(result_type, id, args[0], args[1], "SPIRV_Cross_Reflect"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Refract: + if (get(result_type).vecsize == 1) + { + if (!requires_scalar_refract) + { + requires_scalar_refract = true; + force_recompile(); + } + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "SPIRV_Cross_Refract"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450FaceForward: + if (get(result_type).vecsize == 1) + { + if (!requires_scalar_faceforward) + { + requires_scalar_faceforward = true; + force_recompile(); + } + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "SPIRV_Cross_FaceForward"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + default: + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + } +} + +void CompilerHLSL::read_access_chain_array(const string &lhs, const SPIRAccessChain &chain) +{ + auto &type = get(chain.basetype); + + // Need to use a reserved identifier here since it might shadow an identifier in the access chain input or other loops. + auto ident = get_unique_identifier(); + + statement("[unroll]"); + statement("for (int ", ident, " = 0; ", ident, " < ", to_array_size(type, uint32_t(type.array.size() - 1)), "; ", + ident, "++)"); + begin_scope(); + auto subchain = chain; + subchain.dynamic_index = join(ident, " * ", chain.array_stride, " + ", chain.dynamic_index); + subchain.basetype = type.parent_type; + if (!get(subchain.basetype).array.empty()) + subchain.array_stride = get_decoration(subchain.basetype, DecorationArrayStride); + read_access_chain(nullptr, join(lhs, "[", ident, "]"), subchain); + end_scope(); +} + +void CompilerHLSL::read_access_chain_struct(const string &lhs, const SPIRAccessChain &chain) +{ + auto &type = get(chain.basetype); + auto subchain = chain; + uint32_t member_count = uint32_t(type.member_types.size()); + + for (uint32_t i = 0; i < member_count; i++) + { + uint32_t offset = type_struct_member_offset(type, i); + subchain.static_index = chain.static_index + offset; + subchain.basetype = type.member_types[i]; + + subchain.matrix_stride = 0; + subchain.array_stride = 0; + subchain.row_major_matrix = false; + + auto &member_type = get(subchain.basetype); + if (member_type.columns > 1) + { + subchain.matrix_stride = type_struct_member_matrix_stride(type, i); + subchain.row_major_matrix = has_member_decoration(type.self, i, DecorationRowMajor); + } + + if (!member_type.array.empty()) + subchain.array_stride = type_struct_member_array_stride(type, i); + + read_access_chain(nullptr, join(lhs, ".", to_member_name(type, i)), subchain); + } +} + +void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIRAccessChain &chain) +{ + auto &type = get(chain.basetype); + + SPIRType target_type; + target_type.basetype = SPIRType::UInt; + target_type.vecsize = type.vecsize; + target_type.columns = type.columns; + + if (!type.array.empty()) + { + read_access_chain_array(lhs, chain); + return; + } + else if (type.basetype == SPIRType::Struct) + { + read_access_chain_struct(lhs, chain); + return; + } + else if (type.width != 32 && !hlsl_options.enable_16bit_types) + SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported, unless SM 6.2 and " + "native 16-bit types are enabled."); + + bool templated_load = hlsl_options.shader_model >= 62; + string load_expr; + + string template_expr; + if (templated_load) + template_expr = join("<", type_to_glsl(type), ">"); + + // Load a vector or scalar. + if (type.columns == 1 && !chain.row_major_matrix) + { + const char *load_op = nullptr; + switch (type.vecsize) + { + case 1: + load_op = "Load"; + break; + case 2: + load_op = "Load2"; + break; + case 3: + load_op = "Load3"; + break; + case 4: + load_op = "Load4"; + break; + default: + SPIRV_CROSS_THROW("Unknown vector size."); + } + + if (templated_load) + load_op = "Load"; + + load_expr = join(chain.base, ".", load_op, template_expr, "(", chain.dynamic_index, chain.static_index, ")"); + } + else if (type.columns == 1) + { + // Strided load since we are loading a column from a row-major matrix. + if (templated_load) + { + auto scalar_type = type; + scalar_type.vecsize = 1; + scalar_type.columns = 1; + template_expr = join("<", type_to_glsl(scalar_type), ">"); + if (type.vecsize > 1) + load_expr += type_to_glsl(type) + "("; + } + else if (type.vecsize > 1) + { + load_expr = type_to_glsl(target_type); + load_expr += "("; + } + + for (uint32_t r = 0; r < type.vecsize; r++) + { + load_expr += join(chain.base, ".Load", template_expr, "(", chain.dynamic_index, + chain.static_index + r * chain.matrix_stride, ")"); + if (r + 1 < type.vecsize) + load_expr += ", "; + } + + if (type.vecsize > 1) + load_expr += ")"; + } + else if (!chain.row_major_matrix) + { + // Load a matrix, column-major, the easy case. + const char *load_op = nullptr; + switch (type.vecsize) + { + case 1: + load_op = "Load"; + break; + case 2: + load_op = "Load2"; + break; + case 3: + load_op = "Load3"; + break; + case 4: + load_op = "Load4"; + break; + default: + SPIRV_CROSS_THROW("Unknown vector size."); + } + + if (templated_load) + { + auto vector_type = type; + vector_type.columns = 1; + template_expr = join("<", type_to_glsl(vector_type), ">"); + load_expr = type_to_glsl(type); + load_op = "Load"; + } + else + { + // Note, this loading style in HLSL is *actually* row-major, but we always treat matrices as transposed in this backend, + // so row-major is technically column-major ... + load_expr = type_to_glsl(target_type); + } + load_expr += "("; + + for (uint32_t c = 0; c < type.columns; c++) + { + load_expr += join(chain.base, ".", load_op, template_expr, "(", chain.dynamic_index, + chain.static_index + c * chain.matrix_stride, ")"); + if (c + 1 < type.columns) + load_expr += ", "; + } + load_expr += ")"; + } + else + { + // Pick out elements one by one ... Hopefully compilers are smart enough to recognize this pattern + // considering HLSL is "row-major decl", but "column-major" memory layout (basically implicit transpose model, ugh) ... + + if (templated_load) + { + load_expr = type_to_glsl(type); + auto scalar_type = type; + scalar_type.vecsize = 1; + scalar_type.columns = 1; + template_expr = join("<", type_to_glsl(scalar_type), ">"); + } + else + load_expr = type_to_glsl(target_type); + + load_expr += "("; + + for (uint32_t c = 0; c < type.columns; c++) + { + for (uint32_t r = 0; r < type.vecsize; r++) + { + load_expr += join(chain.base, ".Load", template_expr, "(", chain.dynamic_index, + chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ")"); + + if ((r + 1 < type.vecsize) || (c + 1 < type.columns)) + load_expr += ", "; + } + } + load_expr += ")"; + } + + if (!templated_load) + { + auto bitcast_op = bitcast_glsl_op(type, target_type); + if (!bitcast_op.empty()) + load_expr = join(bitcast_op, "(", load_expr, ")"); + } + + if (lhs.empty()) + { + assert(expr); + *expr = move(load_expr); + } + else + statement(lhs, " = ", load_expr, ";"); +} + +void CompilerHLSL::emit_load(const Instruction &instruction) +{ + auto ops = stream(instruction); + + auto *chain = maybe_get(ops[2]); + if (chain) + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + + if (has_decoration(ptr, DecorationNonUniformEXT)) + propagate_nonuniform_qualifier(ptr); + + auto &type = get(result_type); + bool composite_load = !type.array.empty() || type.basetype == SPIRType::Struct; + + if (composite_load) + { + // We cannot make this work in one single expression as we might have nested structures and arrays, + // so unroll the load to an uninitialized temporary. + emit_uninitialized_temporary_expression(result_type, id); + read_access_chain(nullptr, to_expression(id), *chain); + track_expression_read(chain->self); + } + else + { + string load_expr; + read_access_chain(&load_expr, "", *chain); + + bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries); + + // If we are forwarding this load, + // don't register the read to access chain here, defer that to when we actually use the expression, + // using the add_implied_read_expression mechanism. + if (!forward) + track_expression_read(chain->self); + + // Do not forward complex load sequences like matrices, structs and arrays. + if (type.columns > 1) + forward = false; + + auto &e = emit_op(result_type, id, load_expr, forward, true); + e.need_transpose = false; + register_read(id, ptr, forward); + inherit_expression_dependencies(id, ptr); + if (forward) + add_implied_read_expression(e, chain->self); + } + } + else + CompilerGLSL::emit_instruction(instruction); +} + +void CompilerHLSL::write_access_chain_array(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain) +{ + auto &type = get(chain.basetype); + + // Need to use a reserved identifier here since it might shadow an identifier in the access chain input or other loops. + auto ident = get_unique_identifier(); + + uint32_t id = ir.increase_bound_by(2); + uint32_t int_type_id = id + 1; + SPIRType int_type; + int_type.basetype = SPIRType::Int; + int_type.width = 32; + set(int_type_id, int_type); + set(id, ident, int_type_id, true); + set_name(id, ident); + suppressed_usage_tracking.insert(id); + + statement("[unroll]"); + statement("for (int ", ident, " = 0; ", ident, " < ", to_array_size(type, uint32_t(type.array.size() - 1)), "; ", + ident, "++)"); + begin_scope(); + auto subchain = chain; + subchain.dynamic_index = join(ident, " * ", chain.array_stride, " + ", chain.dynamic_index); + subchain.basetype = type.parent_type; + + // Forcefully allow us to use an ID here by setting MSB. + auto subcomposite_chain = composite_chain; + subcomposite_chain.push_back(0x80000000u | id); + + if (!get(subchain.basetype).array.empty()) + subchain.array_stride = get_decoration(subchain.basetype, DecorationArrayStride); + + write_access_chain(subchain, value, subcomposite_chain); + end_scope(); +} + +void CompilerHLSL::write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain) +{ + auto &type = get(chain.basetype); + uint32_t member_count = uint32_t(type.member_types.size()); + auto subchain = chain; + + auto subcomposite_chain = composite_chain; + subcomposite_chain.push_back(0); + + for (uint32_t i = 0; i < member_count; i++) + { + uint32_t offset = type_struct_member_offset(type, i); + subchain.static_index = chain.static_index + offset; + subchain.basetype = type.member_types[i]; + + subchain.matrix_stride = 0; + subchain.array_stride = 0; + subchain.row_major_matrix = false; + + auto &member_type = get(subchain.basetype); + if (member_type.columns > 1) + { + subchain.matrix_stride = type_struct_member_matrix_stride(type, i); + subchain.row_major_matrix = has_member_decoration(type.self, i, DecorationRowMajor); + } + + if (!member_type.array.empty()) + subchain.array_stride = type_struct_member_array_stride(type, i); + + subcomposite_chain.back() = i; + write_access_chain(subchain, value, subcomposite_chain); + } +} + +string CompilerHLSL::write_access_chain_value(uint32_t value, const SmallVector &composite_chain, + bool enclose) +{ + string ret; + if (composite_chain.empty()) + ret = to_expression(value); + else + { + AccessChainMeta meta; + ret = access_chain_internal(value, composite_chain.data(), uint32_t(composite_chain.size()), + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_LITERAL_MSB_FORCE_ID, &meta); + } + + if (enclose) + ret = enclose_expression(ret); + return ret; +} + +void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain) +{ + auto &type = get(chain.basetype); + + // Make sure we trigger a read of the constituents in the access chain. + track_expression_read(chain.self); + + if (has_decoration(chain.self, DecorationNonUniformEXT)) + propagate_nonuniform_qualifier(chain.self); + + SPIRType target_type; + target_type.basetype = SPIRType::UInt; + target_type.vecsize = type.vecsize; + target_type.columns = type.columns; + + if (!type.array.empty()) + { + write_access_chain_array(chain, value, composite_chain); + register_write(chain.self); + return; + } + else if (type.basetype == SPIRType::Struct) + { + write_access_chain_struct(chain, value, composite_chain); + register_write(chain.self); + return; + } + else if (type.width != 32 && !hlsl_options.enable_16bit_types) + SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported, unless SM 6.2 and " + "native 16-bit types are enabled."); + + bool templated_store = hlsl_options.shader_model >= 62; + + string template_expr; + if (templated_store) + template_expr = join("<", type_to_glsl(type), ">"); + + if (type.columns == 1 && !chain.row_major_matrix) + { + const char *store_op = nullptr; + switch (type.vecsize) + { + case 1: + store_op = "Store"; + break; + case 2: + store_op = "Store2"; + break; + case 3: + store_op = "Store3"; + break; + case 4: + store_op = "Store4"; + break; + default: + SPIRV_CROSS_THROW("Unknown vector size."); + } + + auto store_expr = write_access_chain_value(value, composite_chain, false); + + if (!templated_store) + { + auto bitcast_op = bitcast_glsl_op(target_type, type); + if (!bitcast_op.empty()) + store_expr = join(bitcast_op, "(", store_expr, ")"); + } + else + store_op = "Store"; + statement(chain.base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index, ", ", + store_expr, ");"); + } + else if (type.columns == 1) + { + if (templated_store) + { + auto scalar_type = type; + scalar_type.vecsize = 1; + scalar_type.columns = 1; + template_expr = join("<", type_to_glsl(scalar_type), ">"); + } + + // Strided store. + for (uint32_t r = 0; r < type.vecsize; r++) + { + auto store_expr = write_access_chain_value(value, composite_chain, true); + if (type.vecsize > 1) + { + store_expr += "."; + store_expr += index_to_swizzle(r); + } + remove_duplicate_swizzle(store_expr); + + if (!templated_store) + { + auto bitcast_op = bitcast_glsl_op(target_type, type); + if (!bitcast_op.empty()) + store_expr = join(bitcast_op, "(", store_expr, ")"); + } + + statement(chain.base, ".Store", template_expr, "(", chain.dynamic_index, + chain.static_index + chain.matrix_stride * r, ", ", store_expr, ");"); + } + } + else if (!chain.row_major_matrix) + { + const char *store_op = nullptr; + switch (type.vecsize) + { + case 1: + store_op = "Store"; + break; + case 2: + store_op = "Store2"; + break; + case 3: + store_op = "Store3"; + break; + case 4: + store_op = "Store4"; + break; + default: + SPIRV_CROSS_THROW("Unknown vector size."); + } + + if (templated_store) + { + store_op = "Store"; + auto vector_type = type; + vector_type.columns = 1; + template_expr = join("<", type_to_glsl(vector_type), ">"); + } + + for (uint32_t c = 0; c < type.columns; c++) + { + auto store_expr = join(write_access_chain_value(value, composite_chain, true), "[", c, "]"); + + if (!templated_store) + { + auto bitcast_op = bitcast_glsl_op(target_type, type); + if (!bitcast_op.empty()) + store_expr = join(bitcast_op, "(", store_expr, ")"); + } + + statement(chain.base, ".", store_op, template_expr, "(", chain.dynamic_index, + chain.static_index + c * chain.matrix_stride, ", ", store_expr, ");"); + } + } + else + { + if (templated_store) + { + auto scalar_type = type; + scalar_type.vecsize = 1; + scalar_type.columns = 1; + template_expr = join("<", type_to_glsl(scalar_type), ">"); + } + + for (uint32_t r = 0; r < type.vecsize; r++) + { + for (uint32_t c = 0; c < type.columns; c++) + { + auto store_expr = + join(write_access_chain_value(value, composite_chain, true), "[", c, "].", index_to_swizzle(r)); + remove_duplicate_swizzle(store_expr); + auto bitcast_op = bitcast_glsl_op(target_type, type); + if (!bitcast_op.empty()) + store_expr = join(bitcast_op, "(", store_expr, ")"); + statement(chain.base, ".Store", template_expr, "(", chain.dynamic_index, + chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ", ", store_expr, ");"); + } + } + } + + register_write(chain.self); +} + +void CompilerHLSL::emit_store(const Instruction &instruction) +{ + auto ops = stream(instruction); + auto *chain = maybe_get(ops[0]); + if (chain) + write_access_chain(*chain, ops[1], {}); + else + CompilerGLSL::emit_instruction(instruction); +} + +void CompilerHLSL::emit_access_chain(const Instruction &instruction) +{ + auto ops = stream(instruction); + uint32_t length = instruction.length; + + bool need_byte_access_chain = false; + auto &type = expression_type(ops[2]); + const auto *chain = maybe_get(ops[2]); + + if (chain) + { + // Keep tacking on an existing access chain. + need_byte_access_chain = true; + } + else if (type.storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock)) + { + // If we are starting to poke into an SSBO, we are dealing with ByteAddressBuffers, and we need + // to emit SPIRAccessChain rather than a plain SPIRExpression. + uint32_t chain_arguments = length - 3; + if (chain_arguments > type.array.size()) + need_byte_access_chain = true; + } + + if (need_byte_access_chain) + { + // If we have a chain variable, we are already inside the SSBO, and any array type will refer to arrays within a block, + // and not array of SSBO. + uint32_t to_plain_buffer_length = chain ? 0u : static_cast(type.array.size()); + + auto *backing_variable = maybe_get_backing_variable(ops[2]); + + string base; + if (to_plain_buffer_length != 0) + base = access_chain(ops[2], &ops[3], to_plain_buffer_length, get(ops[0])); + else if (chain) + base = chain->base; + else + base = to_expression(ops[2]); + + // Start traversing type hierarchy at the proper non-pointer types. + auto *basetype = &get_pointee_type(type); + + // Traverse the type hierarchy down to the actual buffer types. + for (uint32_t i = 0; i < to_plain_buffer_length; i++) + { + assert(basetype->parent_type); + basetype = &get(basetype->parent_type); + } + + uint32_t matrix_stride = 0; + uint32_t array_stride = 0; + bool row_major_matrix = false; + + // Inherit matrix information. + if (chain) + { + matrix_stride = chain->matrix_stride; + row_major_matrix = chain->row_major_matrix; + array_stride = chain->array_stride; + } + + auto offsets = flattened_access_chain_offset(*basetype, &ops[3 + to_plain_buffer_length], + length - 3 - to_plain_buffer_length, 0, 1, &row_major_matrix, + &matrix_stride, &array_stride); + + auto &e = set(ops[1], ops[0], type.storage, base, offsets.first, offsets.second); + e.row_major_matrix = row_major_matrix; + e.matrix_stride = matrix_stride; + e.array_stride = array_stride; + e.immutable = should_forward(ops[2]); + e.loaded_from = backing_variable ? backing_variable->self : ID(0); + + if (chain) + { + e.dynamic_index += chain->dynamic_index; + e.static_index += chain->static_index; + } + + for (uint32_t i = 2; i < length; i++) + { + inherit_expression_dependencies(ops[1], ops[i]); + add_implied_read_expression(e, ops[i]); + } + + if (has_decoration(ops[1], DecorationNonUniformEXT)) + propagate_nonuniform_qualifier(ops[1]); + } + else + { + CompilerGLSL::emit_instruction(instruction); + } +} + +void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op) +{ + const char *atomic_op = nullptr; + + string value_expr; + if (op != OpAtomicIDecrement && op != OpAtomicIIncrement && op != OpAtomicLoad && op != OpAtomicStore) + value_expr = to_expression(ops[op == OpAtomicCompareExchange ? 6 : 5]); + + bool is_atomic_store = false; + + switch (op) + { + case OpAtomicIIncrement: + atomic_op = "InterlockedAdd"; + value_expr = "1"; + break; + + case OpAtomicIDecrement: + atomic_op = "InterlockedAdd"; + value_expr = "-1"; + break; + + case OpAtomicLoad: + atomic_op = "InterlockedAdd"; + value_expr = "0"; + break; + + case OpAtomicISub: + atomic_op = "InterlockedAdd"; + value_expr = join("-", enclose_expression(value_expr)); + break; + + case OpAtomicSMin: + case OpAtomicUMin: + atomic_op = "InterlockedMin"; + break; + + case OpAtomicSMax: + case OpAtomicUMax: + atomic_op = "InterlockedMax"; + break; + + case OpAtomicAnd: + atomic_op = "InterlockedAnd"; + break; + + case OpAtomicOr: + atomic_op = "InterlockedOr"; + break; + + case OpAtomicXor: + atomic_op = "InterlockedXor"; + break; + + case OpAtomicIAdd: + atomic_op = "InterlockedAdd"; + break; + + case OpAtomicExchange: + atomic_op = "InterlockedExchange"; + break; + + case OpAtomicStore: + atomic_op = "InterlockedExchange"; + is_atomic_store = true; + break; + + case OpAtomicCompareExchange: + if (length < 8) + SPIRV_CROSS_THROW("Not enough data for opcode."); + atomic_op = "InterlockedCompareExchange"; + value_expr = join(to_expression(ops[7]), ", ", value_expr); + break; + + default: + SPIRV_CROSS_THROW("Unknown atomic opcode."); + } + + if (is_atomic_store) + { + auto &data_type = expression_type(ops[0]); + auto *chain = maybe_get(ops[0]); + + auto &tmp_id = extra_sub_expressions[ops[0]]; + if (!tmp_id) + { + tmp_id = ir.increase_bound_by(1); + emit_uninitialized_temporary_expression(get_pointee_type(data_type).self, tmp_id); + } + + if (data_type.storage == StorageClassImage || !chain) + { + statement(atomic_op, "(", to_expression(ops[0]), ", ", to_expression(ops[3]), ", ", to_expression(tmp_id), + ");"); + } + else + { + // RWByteAddress buffer is always uint in its underlying type. + statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", + to_expression(ops[3]), ", ", to_expression(tmp_id), ");"); + } + } + else + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + forced_temporaries.insert(ops[1]); + + auto &type = get(result_type); + statement(variable_decl(type, to_name(id)), ";"); + + auto &data_type = expression_type(ops[2]); + auto *chain = maybe_get(ops[2]); + SPIRType::BaseType expr_type; + if (data_type.storage == StorageClassImage || !chain) + { + statement(atomic_op, "(", to_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");"); + expr_type = data_type.basetype; + } + else + { + // RWByteAddress buffer is always uint in its underlying type. + expr_type = SPIRType::UInt; + statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr, + ", ", to_name(id), ");"); + } + + auto expr = bitcast_expression(type, expr_type, to_name(id)); + set(id, expr, result_type, true); + } + flush_all_atomic_capable_variables(); +} + +void CompilerHLSL::emit_subgroup_op(const Instruction &i) +{ + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("Wave ops requires SM 6.0 or higher."); + + const uint32_t *ops = stream(i); + auto op = static_cast(i.op); + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto scope = static_cast(evaluate_constant_u32(ops[2])); + if (scope != ScopeSubgroup) + SPIRV_CROSS_THROW("Only subgroup scope is supported."); + + const auto make_inclusive_Sum = [&](const string &expr) -> string { + return join(expr, " + ", to_expression(ops[4])); + }; + + const auto make_inclusive_Product = [&](const string &expr) -> string { + return join(expr, " * ", to_expression(ops[4])); + }; + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + +#define make_inclusive_BitAnd(expr) "" +#define make_inclusive_BitOr(expr) "" +#define make_inclusive_BitXor(expr) "" +#define make_inclusive_Min(expr) "" +#define make_inclusive_Max(expr) "" + + switch (op) + { + case OpGroupNonUniformElect: + emit_op(result_type, id, "WaveIsFirstLane()", true); + break; + + case OpGroupNonUniformBroadcast: + emit_binary_func_op(result_type, id, ops[3], ops[4], "WaveReadLaneAt"); + break; + + case OpGroupNonUniformBroadcastFirst: + emit_unary_func_op(result_type, id, ops[3], "WaveReadLaneFirst"); + break; + + case OpGroupNonUniformBallot: + emit_unary_func_op(result_type, id, ops[3], "WaveActiveBallot"); + break; + + case OpGroupNonUniformInverseBallot: + SPIRV_CROSS_THROW("Cannot trivially implement InverseBallot in HLSL."); + break; + + case OpGroupNonUniformBallotBitExtract: + SPIRV_CROSS_THROW("Cannot trivially implement BallotBitExtract in HLSL."); + break; + + case OpGroupNonUniformBallotFindLSB: + SPIRV_CROSS_THROW("Cannot trivially implement BallotFindLSB in HLSL."); + break; + + case OpGroupNonUniformBallotFindMSB: + SPIRV_CROSS_THROW("Cannot trivially implement BallotFindMSB in HLSL."); + break; + + case OpGroupNonUniformBallotBitCount: + { + auto operation = static_cast(ops[3]); + if (operation == GroupOperationReduce) + { + bool forward = should_forward(ops[4]); + auto left = join("countbits(", to_enclosed_expression(ops[4]), ".x) + countbits(", + to_enclosed_expression(ops[4]), ".y)"); + auto right = join("countbits(", to_enclosed_expression(ops[4]), ".z) + countbits(", + to_enclosed_expression(ops[4]), ".w)"); + emit_op(result_type, id, join(left, " + ", right), forward); + inherit_expression_dependencies(id, ops[4]); + } + else if (operation == GroupOperationInclusiveScan) + SPIRV_CROSS_THROW("Cannot trivially implement BallotBitCount Inclusive Scan in HLSL."); + else if (operation == GroupOperationExclusiveScan) + SPIRV_CROSS_THROW("Cannot trivially implement BallotBitCount Exclusive Scan in HLSL."); + else + SPIRV_CROSS_THROW("Invalid BitCount operation."); + break; + } + + case OpGroupNonUniformShuffle: + SPIRV_CROSS_THROW("Cannot trivially implement Shuffle in HLSL."); + case OpGroupNonUniformShuffleXor: + SPIRV_CROSS_THROW("Cannot trivially implement ShuffleXor in HLSL."); + case OpGroupNonUniformShuffleUp: + SPIRV_CROSS_THROW("Cannot trivially implement ShuffleUp in HLSL."); + case OpGroupNonUniformShuffleDown: + SPIRV_CROSS_THROW("Cannot trivially implement ShuffleDown in HLSL."); + + case OpGroupNonUniformAll: + emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllTrue"); + break; + + case OpGroupNonUniformAny: + emit_unary_func_op(result_type, id, ops[3], "WaveActiveAnyTrue"); + break; + + case OpGroupNonUniformAllEqual: + { + auto &type = get(result_type); + emit_unary_func_op(result_type, id, ops[3], + type.basetype == SPIRType::Boolean ? "WaveActiveAllEqualBool" : "WaveActiveAllEqual"); + break; + } + + // clang-format off +#define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op(result_type, id, ops[4], "WaveActive" #hlsl_op); \ + else if (operation == GroupOperationInclusiveScan && supports_scan) \ + { \ + bool forward = should_forward(ops[4]); \ + emit_op(result_type, id, make_inclusive_##hlsl_op (join("WavePrefix" #hlsl_op, "(", to_expression(ops[4]), ")")), forward); \ + inherit_expression_dependencies(id, ops[4]); \ + } \ + else if (operation == GroupOperationExclusiveScan && supports_scan) \ + emit_unary_func_op(result_type, id, ops[4], "WavePrefix" #hlsl_op); \ + else if (operation == GroupOperationClusteredReduce) \ + SPIRV_CROSS_THROW("Cannot trivially implement ClusteredReduce in HLSL."); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + +#define HLSL_GROUP_OP_CAST(op, hlsl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[4], "WaveActive" #hlsl_op, type, type); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + + HLSL_GROUP_OP(FAdd, Sum, true) + HLSL_GROUP_OP(FMul, Product, true) + HLSL_GROUP_OP(FMin, Min, false) + HLSL_GROUP_OP(FMax, Max, false) + HLSL_GROUP_OP(IAdd, Sum, true) + HLSL_GROUP_OP(IMul, Product, true) + HLSL_GROUP_OP_CAST(SMin, Min, int_type) + HLSL_GROUP_OP_CAST(SMax, Max, int_type) + HLSL_GROUP_OP_CAST(UMin, Min, uint_type) + HLSL_GROUP_OP_CAST(UMax, Max, uint_type) + HLSL_GROUP_OP(BitwiseAnd, BitAnd, false) + HLSL_GROUP_OP(BitwiseOr, BitOr, false) + HLSL_GROUP_OP(BitwiseXor, BitXor, false) + +#undef HLSL_GROUP_OP +#undef HLSL_GROUP_OP_CAST + // clang-format on + + case OpGroupNonUniformQuadSwap: + { + uint32_t direction = evaluate_constant_u32(ops[4]); + if (direction == 0) + emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossX"); + else if (direction == 1) + emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossY"); + else if (direction == 2) + emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossDiagonal"); + else + SPIRV_CROSS_THROW("Invalid quad swap direction."); + break; + } + + case OpGroupNonUniformQuadBroadcast: + { + emit_binary_func_op(result_type, id, ops[3], ops[4], "QuadReadLaneAt"); + break; + } + + default: + SPIRV_CROSS_THROW("Invalid opcode for subgroup."); + } + + register_control_dependent_expression(id); +} + +void CompilerHLSL::emit_instruction(const Instruction &instruction) +{ + auto ops = stream(instruction); + auto opcode = static_cast(instruction.op); + +#define HLSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#define HLSL_BOP_CAST(op, type) \ + emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) +#define HLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) +#define HLSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define HLSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) +#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define HLSL_BFOP_CAST(op, type) \ + emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) +#define HLSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define HLSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(instruction); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + switch (opcode) + { + case OpAccessChain: + case OpInBoundsAccessChain: + { + emit_access_chain(instruction); + break; + } + case OpBitcast: + { + auto bitcast_type = get_bitcast_type(ops[0], ops[2]); + if (bitcast_type == CompilerHLSL::TypeNormal) + CompilerGLSL::emit_instruction(instruction); + else + { + if (!requires_uint2_packing) + { + requires_uint2_packing = true; + force_recompile(); + } + + if (bitcast_type == CompilerHLSL::TypePackUint2x32) + emit_unary_func_op(ops[0], ops[1], ops[2], "SPIRV_Cross_packUint2x32"); + else + emit_unary_func_op(ops[0], ops[1], ops[2], "SPIRV_Cross_unpackUint2x32"); + } + + break; + } + + case OpStore: + { + emit_store(instruction); + break; + } + + case OpLoad: + { + emit_load(instruction); + break; + } + + case OpMatrixTimesVector: + { + // Matrices are kept in a transposed state all the time, flip multiplication order always. + emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul"); + break; + } + + case OpVectorTimesMatrix: + { + // Matrices are kept in a transposed state all the time, flip multiplication order always. + emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul"); + break; + } + + case OpMatrixTimesMatrix: + { + // Matrices are kept in a transposed state all the time, flip multiplication order always. + emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul"); + break; + } + + case OpOuterProduct: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2]; + uint32_t b = ops[3]; + + auto &type = get(result_type); + string expr = type_to_glsl_constructor(type); + expr += "("; + for (uint32_t col = 0; col < type.columns; col++) + { + expr += to_enclosed_expression(a); + expr += " * "; + expr += to_extract_component_expression(b, col); + if (col + 1 < type.columns) + expr += ", "; + } + expr += ")"; + emit_op(result_type, id, expr, should_forward(a) && should_forward(b)); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + break; + } + + case OpFMod: + { + if (!requires_op_fmod) + { + requires_op_fmod = true; + force_recompile(); + } + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpFRem: + emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], "fmod"); + break; + + case OpImage: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto *combined = maybe_get(ops[2]); + + if (combined) + { + auto &e = emit_op(result_type, id, to_expression(combined->image), true, true); + auto *var = maybe_get_backing_variable(combined->image); + if (var) + e.loaded_from = var->self; + } + else + { + auto &e = emit_op(result_type, id, to_expression(ops[2]), true, true); + auto *var = maybe_get_backing_variable(ops[2]); + if (var) + e.loaded_from = var->self; + } + break; + } + + case OpDPdx: + HLSL_UFOP(ddx); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdy: + HLSL_UFOP(ddy); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdxFine: + HLSL_UFOP(ddx_fine); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdyFine: + HLSL_UFOP(ddy_fine); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdxCoarse: + HLSL_UFOP(ddx_coarse); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdyCoarse: + HLSL_UFOP(ddy_coarse); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidth: + case OpFwidthCoarse: + case OpFwidthFine: + HLSL_UFOP(fwidth); + register_control_dependent_expression(ops[1]); + break; + + case OpLogicalNot: + { + auto result_type = ops[0]; + auto id = ops[1]; + auto &type = get(result_type); + + if (type.vecsize > 1) + emit_unrolled_unary_op(result_type, id, ops[2], "!"); + else + HLSL_UOP(!); + break; + } + + case OpIEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "==", false, SPIRType::Unknown); + else + HLSL_BOP_CAST(==, int_type); + break; + } + + case OpLogicalEqual: + case OpFOrdEqual: + case OpFUnordEqual: + { + // HLSL != operator is unordered. + // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-float-rules. + // isnan() is apparently implemented as x != x as well. + // We cannot implement UnordEqual as !(OrdNotEqual), as HLSL cannot express OrdNotEqual. + // HACK: FUnordEqual will be implemented as FOrdEqual. + + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "==", false, SPIRType::Unknown); + else + HLSL_BOP(==); + break; + } + + case OpINotEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!=", false, SPIRType::Unknown); + else + HLSL_BOP_CAST(!=, int_type); + break; + } + + case OpLogicalNotEqual: + case OpFOrdNotEqual: + case OpFUnordNotEqual: + { + // HLSL != operator is unordered. + // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-float-rules. + // isnan() is apparently implemented as x != x as well. + + // FIXME: FOrdNotEqual cannot be implemented in a crisp and simple way here. + // We would need to do something like not(UnordEqual), but that cannot be expressed either. + // Adding a lot of NaN checks would be a breaking change from perspective of performance. + // SPIR-V will generally use isnan() checks when this even matters. + // HACK: FOrdNotEqual will be implemented as FUnordEqual. + + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!=", false, SPIRType::Unknown); + else + HLSL_BOP(!=); + break; + } + + case OpUGreaterThan: + case OpSGreaterThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + auto type = opcode == OpUGreaterThan ? uint_type : int_type; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", false, type); + else + HLSL_BOP_CAST(>, type); + break; + } + + case OpFOrdGreaterThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", false, SPIRType::Unknown); + else + HLSL_BOP(>); + break; + } + + case OpFUnordGreaterThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", true, SPIRType::Unknown); + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpUGreaterThanEqual: + case OpSGreaterThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + auto type = opcode == OpUGreaterThanEqual ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", false, type); + else + HLSL_BOP_CAST(>=, type); + break; + } + + case OpFOrdGreaterThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", false, SPIRType::Unknown); + else + HLSL_BOP(>=); + break; + } + + case OpFUnordGreaterThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", true, SPIRType::Unknown); + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpULessThan: + case OpSLessThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + auto type = opcode == OpULessThan ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", false, type); + else + HLSL_BOP_CAST(<, type); + break; + } + + case OpFOrdLessThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", false, SPIRType::Unknown); + else + HLSL_BOP(<); + break; + } + + case OpFUnordLessThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", true, SPIRType::Unknown); + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpULessThanEqual: + case OpSLessThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + auto type = opcode == OpULessThanEqual ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", false, type); + else + HLSL_BOP_CAST(<=, type); + break; + } + + case OpFOrdLessThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", false, SPIRType::Unknown); + else + HLSL_BOP(<=); + break; + } + + case OpFUnordLessThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", true, SPIRType::Unknown); + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpImageQueryLod: + emit_texture_op(instruction, false); + break; + + case OpImageQuerySizeLod: + { + auto result_type = ops[0]; + auto id = ops[1]; + + require_texture_query_variant(ops[2]); + auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter"); + statement("uint ", dummy_samples_levels, ";"); + + auto expr = join("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", ", + bitcast_expression(SPIRType::UInt, ops[3]), ", ", dummy_samples_levels, ")"); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::UInt, expr); + emit_op(result_type, id, expr, true); + break; + } + + case OpImageQuerySize: + { + auto result_type = ops[0]; + auto id = ops[1]; + + require_texture_query_variant(ops[2]); + bool uav = expression_type(ops[2]).image.sampled == 2; + + if (const auto *var = maybe_get_backing_variable(ops[2])) + if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var->self, DecorationNonWritable)) + uav = false; + + auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter"); + statement("uint ", dummy_samples_levels, ";"); + + string expr; + if (uav) + expr = join("SPIRV_Cross_imageSize(", to_expression(ops[2]), ", ", dummy_samples_levels, ")"); + else + expr = join("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")"); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::UInt, expr); + emit_op(result_type, id, expr, true); + break; + } + + case OpImageQuerySamples: + case OpImageQueryLevels: + { + auto result_type = ops[0]; + auto id = ops[1]; + + require_texture_query_variant(ops[2]); + bool uav = expression_type(ops[2]).image.sampled == 2; + if (opcode == OpImageQueryLevels && uav) + SPIRV_CROSS_THROW("Cannot query levels for UAV images."); + + if (const auto *var = maybe_get_backing_variable(ops[2])) + if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var->self, DecorationNonWritable)) + uav = false; + + // Keep it simple and do not emit special variants to make this look nicer ... + // This stuff is barely, if ever, used. + forced_temporaries.insert(id); + auto &type = get(result_type); + statement(variable_decl(type, to_name(id)), ";"); + + if (uav) + statement("SPIRV_Cross_imageSize(", to_expression(ops[2]), ", ", to_name(id), ");"); + else + statement("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", 0u, ", to_name(id), ");"); + + auto &restype = get(ops[0]); + auto expr = bitcast_expression(restype, SPIRType::UInt, to_name(id)); + set(id, expr, result_type, true); + break; + } + + case OpImageRead: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto *var = maybe_get_backing_variable(ops[2]); + auto &type = expression_type(ops[2]); + bool subpass_data = type.image.dim == DimSubpassData; + bool pure = false; + + string imgexpr; + + if (subpass_data) + { + if (hlsl_options.shader_model < 40) + SPIRV_CROSS_THROW("Subpass loads are not supported in HLSL shader model 2/3."); + + // Similar to GLSL, implement subpass loads using texelFetch. + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || instruction.length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used."); + uint32_t sample = ops[5]; + imgexpr = join(to_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")"); + } + else + imgexpr = join(to_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))"); + + pure = true; + } + else + { + imgexpr = join(to_expression(ops[2]), "[", to_expression(ops[3]), "]"); + // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4", + // except that the underlying type changes how the data is interpreted. + + bool force_srv = + hlsl_options.nonwritable_uav_texture_as_srv && var && has_decoration(var->self, DecorationNonWritable); + pure = force_srv; + + if (var && !subpass_data && !force_srv) + imgexpr = remap_swizzle(get(result_type), + image_format_to_components(get(var->basetype).image.format), imgexpr); + } + + if (var && var->forwardable) + { + bool forward = forced_temporaries.find(id) == end(forced_temporaries); + auto &e = emit_op(result_type, id, imgexpr, forward); + + if (!pure) + { + e.loaded_from = var->self; + if (forward) + var->dependees.push_back(id); + } + } + else + emit_op(result_type, id, imgexpr, false); + + inherit_expression_dependencies(id, ops[2]); + if (type.image.ms) + inherit_expression_dependencies(id, ops[5]); + break; + } + + case OpImageWrite: + { + auto *var = maybe_get_backing_variable(ops[0]); + + // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4", + // except that the underlying type changes how the data is interpreted. + auto value_expr = to_expression(ops[2]); + if (var) + { + auto &type = get(var->basetype); + auto narrowed_type = get(type.image.type); + narrowed_type.vecsize = image_format_to_components(type.image.format); + value_expr = remap_swizzle(narrowed_type, expression_type(ops[2]).vecsize, value_expr); + } + + statement(to_expression(ops[0]), "[", to_expression(ops[1]), "] = ", value_expr, ";"); + if (var && variable_storage_is_aliased(*var)) + flush_all_aliased_variables(); + break; + } + + case OpImageTexelPointer: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto expr = to_expression(ops[2]); + if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ops[2], DecorationNonUniformEXT)) + convert_non_uniform_expression(expression_type(ops[2]), expr); + expr += join("[", to_expression(ops[3]), "]"); + + auto &e = set(id, expr, result_type, true); + + // When using the pointer, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(ops[2]); + e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, ops[3]); + break; + } + + case OpAtomicCompareExchange: + case OpAtomicExchange: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + case OpAtomicIAdd: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicLoad: + case OpAtomicStore: + { + emit_atomic(ops, instruction.length, opcode); + break; + } + + case OpControlBarrier: + case OpMemoryBarrier: + { + uint32_t memory; + uint32_t semantics; + + if (opcode == OpMemoryBarrier) + { + memory = evaluate_constant_u32(ops[0]); + semantics = evaluate_constant_u32(ops[1]); + } + else + { + memory = evaluate_constant_u32(ops[1]); + semantics = evaluate_constant_u32(ops[2]); + } + + if (memory == ScopeSubgroup) + { + // No Wave-barriers in HLSL. + break; + } + + // We only care about these flags, acquire/release and friends are not relevant to GLSL. + semantics = mask_relevant_memory_semantics(semantics); + + if (opcode == OpMemoryBarrier) + { + // If we are a memory barrier, and the next instruction is a control barrier, check if that memory barrier + // does what we need, so we avoid redundant barriers. + const Instruction *next = get_next_instruction_in_block(instruction); + if (next && next->op == OpControlBarrier) + { + auto *next_ops = stream(*next); + uint32_t next_memory = evaluate_constant_u32(next_ops[1]); + uint32_t next_semantics = evaluate_constant_u32(next_ops[2]); + next_semantics = mask_relevant_memory_semantics(next_semantics); + + // There is no "just execution barrier" in HLSL. + // If there are no memory semantics for next instruction, we will imply group shared memory is synced. + if (next_semantics == 0) + next_semantics = MemorySemanticsWorkgroupMemoryMask; + + bool memory_scope_covered = false; + if (next_memory == memory) + memory_scope_covered = true; + else if (next_semantics == MemorySemanticsWorkgroupMemoryMask) + { + // If we only care about workgroup memory, either Device or Workgroup scope is fine, + // scope does not have to match. + if ((next_memory == ScopeDevice || next_memory == ScopeWorkgroup) && + (memory == ScopeDevice || memory == ScopeWorkgroup)) + { + memory_scope_covered = true; + } + } + else if (memory == ScopeWorkgroup && next_memory == ScopeDevice) + { + // The control barrier has device scope, but the memory barrier just has workgroup scope. + memory_scope_covered = true; + } + + // If we have the same memory scope, and all memory types are covered, we're good. + if (memory_scope_covered && (semantics & next_semantics) == semantics) + break; + } + } + + // We are synchronizing some memory or syncing execution, + // so we cannot forward any loads beyond the memory barrier. + if (semantics || opcode == OpControlBarrier) + { + assert(current_emitting_block); + flush_control_dependent_expressions(current_emitting_block->self); + flush_all_active_variables(); + } + + if (opcode == OpControlBarrier) + { + // We cannot emit just execution barrier, for no memory semantics pick the cheapest option. + if (semantics == MemorySemanticsWorkgroupMemoryMask || semantics == 0) + statement("GroupMemoryBarrierWithGroupSync();"); + else if (semantics != 0 && (semantics & MemorySemanticsWorkgroupMemoryMask) == 0) + statement("DeviceMemoryBarrierWithGroupSync();"); + else + statement("AllMemoryBarrierWithGroupSync();"); + } + else + { + if (semantics == MemorySemanticsWorkgroupMemoryMask) + statement("GroupMemoryBarrier();"); + else if (semantics != 0 && (semantics & MemorySemanticsWorkgroupMemoryMask) == 0) + statement("DeviceMemoryBarrier();"); + else + statement("AllMemoryBarrier();"); + } + break; + } + + case OpBitFieldInsert: + { + if (!requires_bitfield_insert) + { + requires_bitfield_insert = true; + force_recompile(); + } + + auto expr = join("SPIRV_Cross_bitfieldInsert(", to_expression(ops[2]), ", ", to_expression(ops[3]), ", ", + to_expression(ops[4]), ", ", to_expression(ops[5]), ")"); + + bool forward = + should_forward(ops[2]) && should_forward(ops[3]) && should_forward(ops[4]) && should_forward(ops[5]); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::UInt, expr); + emit_op(ops[0], ops[1], expr, forward); + break; + } + + case OpBitFieldSExtract: + case OpBitFieldUExtract: + { + if (!requires_bitfield_extract) + { + requires_bitfield_extract = true; + force_recompile(); + } + + if (opcode == OpBitFieldSExtract) + HLSL_TFOP(SPIRV_Cross_bitfieldSExtract); + else + HLSL_TFOP(SPIRV_Cross_bitfieldUExtract); + break; + } + + case OpBitCount: + { + auto basetype = expression_type(ops[2]).basetype; + emit_unary_func_op_cast(ops[0], ops[1], ops[2], "countbits", basetype, basetype); + break; + } + + case OpBitReverse: + HLSL_UFOP(reversebits); + break; + + case OpArrayLength: + { + auto *var = maybe_get(ops[2]); + if (!var) + SPIRV_CROSS_THROW("Array length must point directly to an SSBO block."); + + auto &type = get(var->basetype); + if (!has_decoration(type.self, DecorationBlock) && !has_decoration(type.self, DecorationBufferBlock)) + SPIRV_CROSS_THROW("Array length expression must point to a block type."); + + // This must be 32-bit uint, so we're good to go. + emit_uninitialized_temporary_expression(ops[0], ops[1]); + statement(to_expression(ops[2]), ".GetDimensions(", to_expression(ops[1]), ");"); + uint32_t offset = type_struct_member_offset(type, ops[3]); + uint32_t stride = type_struct_member_array_stride(type, ops[3]); + statement(to_expression(ops[1]), " = (", to_expression(ops[1]), " - ", offset, ") / ", stride, ";"); + break; + } + + case OpIsHelperInvocationEXT: + SPIRV_CROSS_THROW("helperInvocationEXT() is not supported in HLSL."); + + case OpBeginInvocationInterlockEXT: + case OpEndInvocationInterlockEXT: + if (hlsl_options.shader_model < 51) + SPIRV_CROSS_THROW("Rasterizer order views require Shader Model 5.1."); + break; // Nothing to do in the body + + default: + CompilerGLSL::emit_instruction(instruction); + break; + } +} + +void CompilerHLSL::require_texture_query_variant(uint32_t var_id) +{ + if (const auto *var = maybe_get_backing_variable(var_id)) + var_id = var->self; + + auto &type = expression_type(var_id); + bool uav = type.image.sampled == 2; + if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var_id, DecorationNonWritable)) + uav = false; + + uint32_t bit = 0; + switch (type.image.dim) + { + case Dim1D: + bit = type.image.arrayed ? Query1DArray : Query1D; + break; + + case Dim2D: + if (type.image.ms) + bit = type.image.arrayed ? Query2DMSArray : Query2DMS; + else + bit = type.image.arrayed ? Query2DArray : Query2D; + break; + + case Dim3D: + bit = Query3D; + break; + + case DimCube: + bit = type.image.arrayed ? QueryCubeArray : QueryCube; + break; + + case DimBuffer: + bit = QueryBuffer; + break; + + default: + SPIRV_CROSS_THROW("Unsupported query type."); + } + + switch (get(type.image.type).basetype) + { + case SPIRType::Float: + bit += QueryTypeFloat; + break; + + case SPIRType::Int: + bit += QueryTypeInt; + break; + + case SPIRType::UInt: + bit += QueryTypeUInt; + break; + + default: + SPIRV_CROSS_THROW("Unsupported query type."); + } + + auto norm_state = image_format_to_normalized_state(type.image.format); + auto &variant = uav ? required_texture_size_variants + .uav[uint32_t(norm_state)][image_format_to_components(type.image.format) - 1] : + required_texture_size_variants.srv; + + uint64_t mask = 1ull << bit; + if ((variant & mask) == 0) + { + force_recompile(); + variant |= mask; + } +} + +void CompilerHLSL::set_root_constant_layouts(std::vector layout) +{ + root_constants_layout = move(layout); +} + +void CompilerHLSL::add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes) +{ + remap_vertex_attributes.push_back(vertex_attributes); +} + +VariableID CompilerHLSL::remap_num_workgroups_builtin() +{ + update_active_builtins(); + + if (!active_input_builtins.get(BuiltInNumWorkgroups)) + return 0; + + // Create a new, fake UBO. + uint32_t offset = ir.increase_bound_by(4); + + uint32_t uint_type_id = offset; + uint32_t block_type_id = offset + 1; + uint32_t block_pointer_type_id = offset + 2; + uint32_t variable_id = offset + 3; + + SPIRType uint_type; + uint_type.basetype = SPIRType::UInt; + uint_type.width = 32; + uint_type.vecsize = 3; + uint_type.columns = 1; + set(uint_type_id, uint_type); + + SPIRType block_type; + block_type.basetype = SPIRType::Struct; + block_type.member_types.push_back(uint_type_id); + set(block_type_id, block_type); + set_decoration(block_type_id, DecorationBlock); + set_member_name(block_type_id, 0, "count"); + set_member_decoration(block_type_id, 0, DecorationOffset, 0); + + SPIRType block_pointer_type = block_type; + block_pointer_type.pointer = true; + block_pointer_type.storage = StorageClassUniform; + block_pointer_type.parent_type = block_type_id; + auto &ptr_type = set(block_pointer_type_id, block_pointer_type); + + // Preserve self. + ptr_type.self = block_type_id; + + set(variable_id, block_pointer_type_id, StorageClassUniform); + ir.meta[variable_id].decoration.alias = "SPIRV_Cross_NumWorkgroups"; + + num_workgroups_builtin = variable_id; + return variable_id; +} + +void CompilerHLSL::set_resource_binding_flags(HLSLBindingFlags flags) +{ + resource_binding_flags = flags; +} + +void CompilerHLSL::validate_shader_model() +{ + // Check for nonuniform qualifier. + // Instead of looping over all decorations to find this, just look at capabilities. + for (auto &cap : ir.declared_capabilities) + { + switch (cap) + { + case CapabilityShaderNonUniformEXT: + case CapabilityRuntimeDescriptorArrayEXT: + if (hlsl_options.shader_model < 51) + SPIRV_CROSS_THROW( + "Shader model 5.1 or higher is required to use bindless resources or NonUniformResourceIndex."); + default: + break; + } + } + + if (ir.addressing_model != AddressingModelLogical) + SPIRV_CROSS_THROW("Only Logical addressing model can be used with HLSL."); + + if (hlsl_options.enable_16bit_types && hlsl_options.shader_model < 62) + SPIRV_CROSS_THROW("Need at least shader model 6.2 when enabling native 16-bit type support."); +} + +string CompilerHLSL::compile() +{ + ir.fixup_reserved_names(); + + // Do not deal with ES-isms like precision, older extensions and such. + options.es = false; + options.version = 450; + options.vulkan_semantics = true; + backend.float_literal_suffix = true; + backend.double_literal_suffix = false; + backend.long_long_literal_suffix = true; + backend.uint32_t_literal_suffix = true; + backend.int16_t_literal_suffix = ""; + backend.uint16_t_literal_suffix = "u"; + backend.basic_int_type = "int"; + backend.basic_uint_type = "uint"; + backend.demote_literal = "discard"; + backend.boolean_mix_function = ""; + backend.swizzle_is_function = false; + backend.shared_is_implied = true; + backend.unsized_array_supported = true; + backend.explicit_struct_type = false; + backend.use_initializer_list = true; + backend.use_constructor_splatting = false; + backend.can_swizzle_scalar = true; + backend.can_declare_struct_inline = false; + backend.can_declare_arrays_inline = false; + backend.can_return_array = false; + backend.nonuniform_qualifier = "NonUniformResourceIndex"; + backend.support_case_fallthrough = false; + + fixup_type_alias(); + reorder_type_alias(); + build_function_control_flow_graphs_and_analyze(); + validate_shader_model(); + update_active_builtins(); + analyze_image_and_sampler_usage(); + analyze_interlocked_resource_usage(); + + // Subpass input needs SV_Position. + if (need_subpass_input) + active_input_builtins.set(BuiltInFragCoord); + + uint32_t pass_count = 0; + do + { + if (pass_count >= 3) + SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!"); + + reset(); + + // Move constructor for this type is broken on GCC 4.9 ... + buffer.reset(); + + emit_header(); + emit_resources(); + + emit_function(get(ir.default_entry_point), Bitset()); + emit_hlsl_entry_point(); + + pass_count++; + } while (is_forcing_recompilation()); + + // Entry point in HLSL is always main() for the time being. + get_entry_point().name = "main"; + + return buffer.str(); +} + +void CompilerHLSL::emit_block_hints(const SPIRBlock &block) +{ + switch (block.hint) + { + case SPIRBlock::HintFlatten: + statement("[flatten]"); + break; + case SPIRBlock::HintDontFlatten: + statement("[branch]"); + break; + case SPIRBlock::HintUnroll: + statement("[unroll]"); + break; + case SPIRBlock::HintDontUnroll: + statement("[loop]"); + break; + default: + break; + } +} + +string CompilerHLSL::get_unique_identifier() +{ + return join("_", unique_identifier_count++, "ident"); +} + +void CompilerHLSL::add_hlsl_resource_binding(const HLSLResourceBinding &binding) +{ + StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding }; + resource_bindings[tuple] = { binding, false }; +} + +bool CompilerHLSL::is_hlsl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const +{ + StageSetBinding tuple = { model, desc_set, binding }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) && itr->second.second; +} + +CompilerHLSL::BitcastType CompilerHLSL::get_bitcast_type(uint32_t result_type, uint32_t op0) +{ + auto &rslt_type = get(result_type); + auto &expr_type = expression_type(op0); + + if (rslt_type.basetype == SPIRType::BaseType::UInt64 && expr_type.basetype == SPIRType::BaseType::UInt && + expr_type.vecsize == 2) + return BitcastType::TypePackUint2x32; + else if (rslt_type.basetype == SPIRType::BaseType::UInt && rslt_type.vecsize == 2 && + expr_type.basetype == SPIRType::BaseType::UInt64) + return BitcastType::TypeUnpackUint64; + + return BitcastType::TypeNormal; +} + +bool CompilerHLSL::is_hlsl_force_storage_buffer_as_uav(ID id) const +{ + if (hlsl_options.force_storage_buffer_as_uav) + { + return true; + } + + const uint32_t desc_set = get_decoration(id, spv::DecorationDescriptorSet); + const uint32_t binding = get_decoration(id, spv::DecorationBinding); + + return (force_uav_buffer_bindings.find({ desc_set, binding }) != force_uav_buffer_bindings.end()); +} + +void CompilerHLSL::set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding) +{ + SetBindingPair pair = { desc_set, binding }; + force_uav_buffer_bindings.insert(pair); +} + +bool CompilerHLSL::builtin_translates_to_nonarray(spv::BuiltIn builtin) const +{ + return (builtin == BuiltInSampleMask); +} diff --git a/third_party/spirv-cross/spirv_hlsl.hpp b/third_party/spirv-cross/spirv_hlsl.hpp new file mode 100644 index 0000000..4abb203 --- /dev/null +++ b/third_party/spirv-cross/spirv_hlsl.hpp @@ -0,0 +1,367 @@ +/* + * Copyright 2016-2020 Robert Konrad + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_HLSL_HPP +#define SPIRV_HLSL_HPP + +#include "spirv_glsl.hpp" +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +// Interface which remaps vertex inputs to a fixed semantic name to make linking easier. +struct HLSLVertexAttributeRemap +{ + uint32_t location; + std::string semantic; +}; +// Specifying a root constant (d3d12) or push constant range (vulkan). +// +// `start` and `end` denotes the range of the root constant in bytes. +// Both values need to be multiple of 4. +struct RootConstants +{ + uint32_t start; + uint32_t end; + + uint32_t binding; + uint32_t space; +}; + +// For finer control, decorations may be removed from specific resources instead with unset_decoration(). +enum HLSLBindingFlagBits +{ + HLSL_BINDING_AUTO_NONE_BIT = 0, + + // Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration. + // A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land. + // Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it. + HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, + + // cbuffer resources will be declared as CBVs (b-space) without a register() declaration. + // A register will be automatically assigned, but must be reflected in D3D-land. + HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, + + // All SRVs (t-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, + + // All UAVs (u-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_UAV_BIT = 1 << 3, + + // All samplers (s-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4, + + // No resources will be declared with register(). + HLSL_BINDING_AUTO_ALL = 0x7fffffff +}; +using HLSLBindingFlags = uint32_t; + +// By matching stage, desc_set and binding for a SPIR-V resource, +// register bindings are set based on whether the HLSL resource is a +// CBV, UAV, SRV or Sampler. A single binding in SPIR-V might contain multiple +// resource types, e.g. COMBINED_IMAGE_SAMPLER, and SRV/Sampler bindings will be used respectively. +// On SM 5.0 and lower, register_space is ignored. +// +// To remap a push constant block which does not have any desc_set/binding associated with it, +// use ResourceBindingPushConstant{DescriptorSet,Binding} as values for desc_set/binding. +// For deeper control of push constants, set_root_constant_layouts() can be used instead. +struct HLSLResourceBinding +{ + spv::ExecutionModel stage = spv::ExecutionModelMax; + uint32_t desc_set = 0; + uint32_t binding = 0; + + struct Binding + { + uint32_t register_space = 0; + uint32_t register_binding = 0; + } cbv, uav, srv, sampler; +}; + +class CompilerHLSL : public CompilerGLSL +{ +public: + struct Options + { + uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow + + // Allows the PointSize builtin, and ignores it, as PointSize is not supported in HLSL. + bool point_size_compat = false; + + // Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL. + bool point_coord_compat = false; + + // If true, the backend will assume that VertexIndex and InstanceIndex will need to apply + // a base offset, and you will need to fill in a cbuffer with offsets. + // Set to false if you know you will never use base instance or base vertex + // functionality as it might remove an internal cbuffer. + bool support_nonzero_base_vertex_base_instance = false; + + // Forces a storage buffer to always be declared as UAV, even if the readonly decoration is used. + // By default, a readonly storage buffer will be declared as ByteAddressBuffer (SRV) instead. + // Alternatively, use set_hlsl_force_storage_buffer_as_uav to specify individually. + bool force_storage_buffer_as_uav = false; + + // Forces any storage image type marked as NonWritable to be considered an SRV instead. + // For this to work with function call parameters, NonWritable must be considered to be part of the type system + // so that NonWritable image arguments are also translated to Texture rather than RWTexture. + bool nonwritable_uav_texture_as_srv = false; + + // Enables native 16-bit types. Needs SM 6.2. + // Uses half/int16_t/uint16_t instead of min16* types. + // Also adds support for 16-bit load-store from (RW)ByteAddressBuffer. + bool enable_16bit_types = false; + + // If matrices are used as IO variables, flatten the attribute declaration to use + // TEXCOORD{N,N+1,N+2,...} rather than TEXCOORDN_{0,1,2,3}. + // If add_vertex_attribute_remap is used and this feature is used, + // the semantic name will be queried once per active location. + bool flatten_matrix_vertex_input_semantics = false; + }; + + explicit CompilerHLSL(std::vector spirv_) + : CompilerGLSL(std::move(spirv_)) + { + } + + CompilerHLSL(const uint32_t *ir_, size_t size) + : CompilerGLSL(ir_, size) + { + } + + explicit CompilerHLSL(const ParsedIR &ir_) + : CompilerGLSL(ir_) + { + } + + explicit CompilerHLSL(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) + { + } + + const Options &get_hlsl_options() const + { + return hlsl_options; + } + + void set_hlsl_options(const Options &opts) + { + hlsl_options = opts; + } + + // Optionally specify a custom root constant layout. + // + // Push constants ranges will be split up according to the + // layout specified. + void set_root_constant_layouts(std::vector layout); + + // Compiles and remaps vertex attributes at specific locations to a fixed semantic. + // The default is TEXCOORD# where # denotes location. + // Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row. + // $SEMANTIC is either TEXCOORD# or a semantic name specified here. + void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes); + std::string compile() override; + + // This is a special HLSL workaround for the NumWorkGroups builtin. + // This does not exist in HLSL, so the calling application must create a dummy cbuffer in + // which the application will store this builtin. + // The cbuffer layout will be: + // cbuffer SPIRV_Cross_NumWorkgroups : register(b#, space#) { uint3 SPIRV_Cross_NumWorkgroups_count; }; + // This must be called before compile(). + // The function returns 0 if NumWorkGroups builtin is not statically used in the shader from the current entry point. + // If non-zero, this returns the variable ID of a cbuffer which corresponds to + // the cbuffer declared above. By default, no binding or descriptor set decoration is set, + // so the calling application should declare explicit bindings on this ID before calling compile(). + VariableID remap_num_workgroups_builtin(); + + // Controls how resource bindings are declared in the output HLSL. + void set_resource_binding_flags(HLSLBindingFlags flags); + + // resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding + // to use for a particular SPIR-V description set + // and binding. If resource bindings are provided, + // is_hlsl_resource_binding_used() will return true after calling ::compile() if + // the set/binding combination was used by the HLSL code. + void add_hlsl_resource_binding(const HLSLResourceBinding &resource); + bool is_hlsl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; + + // Controls which storage buffer bindings will be forced to be declared as UAVs. + void set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding); + +private: + std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; + std::string image_type_hlsl(const SPIRType &type, uint32_t id); + std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id); + std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id); + void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; + void emit_hlsl_entry_point(); + void emit_header() override; + void emit_resources(); + void declare_undefined_values() override; + void emit_interface_block_globally(const SPIRVariable &type); + void emit_interface_block_in_struct(const SPIRVariable &type, std::unordered_set &active_locations); + void emit_builtin_inputs_in_struct(); + void emit_builtin_outputs_in_struct(); + void emit_texture_op(const Instruction &i, bool sparse) override; + void emit_instruction(const Instruction &instruction) override; + void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count) override; + void emit_buffer_block(const SPIRVariable &type) override; + void emit_push_constant_block(const SPIRVariable &var) override; + void emit_uniform(const SPIRVariable &var) override; + void emit_modern_uniform(const SPIRVariable &var); + void emit_legacy_uniform(const SPIRVariable &var); + void emit_specialization_constants_and_structs(); + void emit_composite_constants(); + void emit_fixup() override; + std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; + std::string layout_for_member(const SPIRType &type, uint32_t index) override; + std::string to_interpolation_qualifiers(const Bitset &flags) override; + std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; + bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) override; + std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; + std::string to_sampler_expression(uint32_t id); + std::string to_resource_binding(const SPIRVariable &var); + std::string to_resource_binding_sampler(const SPIRVariable &var); + std::string to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t set); + void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; + void emit_access_chain(const Instruction &instruction); + void emit_load(const Instruction &instruction); + void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain); + void read_access_chain_struct(const std::string &lhs, const SPIRAccessChain &chain); + void read_access_chain_array(const std::string &lhs, const SPIRAccessChain &chain); + void write_access_chain(const SPIRAccessChain &chain, uint32_t value, const SmallVector &composite_chain); + void write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain); + void write_access_chain_array(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain); + std::string write_access_chain_value(uint32_t value, const SmallVector &composite_chain, bool enclose); + void emit_store(const Instruction &instruction); + void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op); + void emit_subgroup_op(const Instruction &i) override; + void emit_block_hints(const SPIRBlock &block) override; + + void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier, + uint32_t base_offset = 0) override; + + const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override; + void replace_illegal_names() override; + + bool is_hlsl_force_storage_buffer_as_uav(ID id) const; + + Options hlsl_options; + + // TODO: Refactor this to be more similar to MSL, maybe have some common system in place? + bool requires_op_fmod = false; + bool requires_fp16_packing = false; + bool requires_uint2_packing = false; + bool requires_explicit_fp16_packing = false; + bool requires_unorm8_packing = false; + bool requires_snorm8_packing = false; + bool requires_unorm16_packing = false; + bool requires_snorm16_packing = false; + bool requires_bitfield_insert = false; + bool requires_bitfield_extract = false; + bool requires_inverse_2x2 = false; + bool requires_inverse_3x3 = false; + bool requires_inverse_4x4 = false; + bool requires_scalar_reflect = false; + bool requires_scalar_refract = false; + bool requires_scalar_faceforward = false; + + struct TextureSizeVariants + { + // MSVC 2013 workaround. + TextureSizeVariants() + { + srv = 0; + for (auto &unorm : uav) + for (auto &u : unorm) + u = 0; + } + uint64_t srv; + uint64_t uav[3][4]; + } required_texture_size_variants; + + void require_texture_query_variant(uint32_t var_id); + void emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav, + const char *type_qualifier); + + enum TextureQueryVariantDim + { + Query1D = 0, + Query1DArray, + Query2D, + Query2DArray, + Query3D, + QueryBuffer, + QueryCube, + QueryCubeArray, + Query2DMS, + Query2DMSArray, + QueryDimCount + }; + + enum TextureQueryVariantType + { + QueryTypeFloat = 0, + QueryTypeInt = 16, + QueryTypeUInt = 32, + QueryTypeCount = 3 + }; + + enum BitcastType + { + TypeNormal, + TypePackUint2x32, + TypeUnpackUint64 + }; + + BitcastType get_bitcast_type(uint32_t result_type, uint32_t op0); + + void emit_builtin_variables(); + bool require_output = false; + bool require_input = false; + SmallVector remap_vertex_attributes; + + uint32_t type_to_consumed_locations(const SPIRType &type) const; + + void emit_io_block(const SPIRVariable &var); + std::string to_semantic(uint32_t location, spv::ExecutionModel em, spv::StorageClass sc); + + uint32_t num_workgroups_builtin = 0; + HLSLBindingFlags resource_binding_flags = 0; + + // Custom root constant layout, which should be emitted + // when translating push constant ranges. + std::vector root_constants_layout; + + void validate_shader_model(); + + std::string get_unique_identifier(); + uint32_t unique_identifier_count = 0; + + std::unordered_map, InternalHasher> resource_bindings; + void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding); + + std::unordered_set force_uav_buffer_bindings; + + // Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but SV_Coverage is a scalar in HLSL. + bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_msl.cpp b/third_party/spirv-cross/spirv_msl.cpp new file mode 100644 index 0000000..360c045 --- /dev/null +++ b/third_party/spirv-cross/spirv_msl.cpp @@ -0,0 +1,14431 @@ +/* + * Copyright 2016-2020 The Brenwill Workshop Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_msl.hpp" +#include "GLSL.std.450.h" + +#include +#include +#include + +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +static const uint32_t k_unknown_location = ~0u; +static const uint32_t k_unknown_component = ~0u; +static const char *force_inline = "static inline __attribute__((always_inline))"; + +CompilerMSL::CompilerMSL(std::vector spirv_) + : CompilerGLSL(move(spirv_)) +{ +} + +CompilerMSL::CompilerMSL(const uint32_t *ir_, size_t word_count) + : CompilerGLSL(ir_, word_count) +{ +} + +CompilerMSL::CompilerMSL(const ParsedIR &ir_) + : CompilerGLSL(ir_) +{ +} + +CompilerMSL::CompilerMSL(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) +{ +} + +void CompilerMSL::add_msl_shader_input(const MSLShaderInput &si) +{ + inputs_by_location[si.location] = si; + if (si.builtin != BuiltInMax && !inputs_by_builtin.count(si.builtin)) + inputs_by_builtin[si.builtin] = si; +} + +void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding) +{ + StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding }; + resource_bindings[tuple] = { binding, false }; +} + +void CompilerMSL::add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index) +{ + SetBindingPair pair = { desc_set, binding }; + buffers_requiring_dynamic_offset[pair] = { index, 0 }; +} + +void CompilerMSL::add_inline_uniform_block(uint32_t desc_set, uint32_t binding) +{ + SetBindingPair pair = { desc_set, binding }; + inline_uniform_blocks.insert(pair); +} + +void CompilerMSL::add_discrete_descriptor_set(uint32_t desc_set) +{ + if (desc_set < kMaxArgumentBuffers) + argument_buffer_discrete_mask |= 1u << desc_set; +} + +void CompilerMSL::set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage) +{ + if (desc_set < kMaxArgumentBuffers) + { + if (device_storage) + argument_buffer_device_storage_mask |= 1u << desc_set; + else + argument_buffer_device_storage_mask &= ~(1u << desc_set); + } +} + +bool CompilerMSL::is_msl_shader_input_used(uint32_t location) +{ + return inputs_in_use.count(location) != 0; +} + +bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const +{ + StageSetBinding tuple = { model, desc_set, binding }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) && itr->second.second; +} + +// Returns the size of the array of resources used by the variable with the specified id. +// The returned value is retrieved from the resource binding added using add_msl_resource_binding(). +uint32_t CompilerMSL::get_resource_array_size(uint32_t id) const +{ + StageSetBinding tuple = { get_entry_point().model, get_decoration(id, DecorationDescriptorSet), + get_decoration(id, DecorationBinding) }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) ? itr->second.first.count : 0; +} + +uint32_t CompilerMSL::get_automatic_msl_resource_binding(uint32_t id) const +{ + return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexPrimary); +} + +uint32_t CompilerMSL::get_automatic_msl_resource_binding_secondary(uint32_t id) const +{ + return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexSecondary); +} + +uint32_t CompilerMSL::get_automatic_msl_resource_binding_tertiary(uint32_t id) const +{ + return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexTertiary); +} + +uint32_t CompilerMSL::get_automatic_msl_resource_binding_quaternary(uint32_t id) const +{ + return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexQuaternary); +} + +void CompilerMSL::set_fragment_output_components(uint32_t location, uint32_t components) +{ + fragment_output_components[location] = components; +} + +bool CompilerMSL::builtin_translates_to_nonarray(spv::BuiltIn builtin) const +{ + return (builtin == BuiltInSampleMask); +} + +void CompilerMSL::build_implicit_builtins() +{ + bool need_sample_pos = active_input_builtins.get(BuiltInSamplePosition); + bool need_vertex_params = capture_output_to_buffer && get_execution_model() == ExecutionModelVertex && + !msl_options.vertex_for_tessellation; + bool need_tesc_params = get_execution_model() == ExecutionModelTessellationControl; + bool need_subgroup_mask = + active_input_builtins.get(BuiltInSubgroupEqMask) || active_input_builtins.get(BuiltInSubgroupGeMask) || + active_input_builtins.get(BuiltInSubgroupGtMask) || active_input_builtins.get(BuiltInSubgroupLeMask) || + active_input_builtins.get(BuiltInSubgroupLtMask); + bool need_subgroup_ge_mask = !msl_options.is_ios() && (active_input_builtins.get(BuiltInSubgroupGeMask) || + active_input_builtins.get(BuiltInSubgroupGtMask)); + bool need_multiview = get_execution_model() == ExecutionModelVertex && !msl_options.view_index_from_device_index && + msl_options.multiview_layered_rendering && + (msl_options.multiview || active_input_builtins.get(BuiltInViewIndex)); + bool need_dispatch_base = + msl_options.dispatch_base && get_execution_model() == ExecutionModelGLCompute && + (active_input_builtins.get(BuiltInWorkgroupId) || active_input_builtins.get(BuiltInGlobalInvocationId)); + bool need_grid_params = get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation; + bool need_vertex_base_params = + need_grid_params && + (active_input_builtins.get(BuiltInVertexId) || active_input_builtins.get(BuiltInVertexIndex) || + active_input_builtins.get(BuiltInBaseVertex) || active_input_builtins.get(BuiltInInstanceId) || + active_input_builtins.get(BuiltInInstanceIndex) || active_input_builtins.get(BuiltInBaseInstance)); + bool need_sample_mask = msl_options.additional_fixed_sample_mask != 0xffffffff; + if (need_subpass_input || need_sample_pos || need_subgroup_mask || need_vertex_params || need_tesc_params || + need_multiview || need_dispatch_base || need_vertex_base_params || need_grid_params || needs_sample_id || + needs_subgroup_invocation_id || needs_subgroup_size || need_sample_mask) + { + bool has_frag_coord = false; + bool has_sample_id = false; + bool has_vertex_idx = false; + bool has_base_vertex = false; + bool has_instance_idx = false; + bool has_base_instance = false; + bool has_invocation_id = false; + bool has_primitive_id = false; + bool has_subgroup_invocation_id = false; + bool has_subgroup_size = false; + bool has_view_idx = false; + bool has_layer = false; + uint32_t workgroup_id_type = 0; + + // FIXME: Investigate the fact that there are no checks for the entry point interface variables. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (!ir.meta[var.self].decoration.builtin) + return; + + // Use Metal's native frame-buffer fetch API for subpass inputs. + BuiltIn builtin = ir.meta[var.self].decoration.builtin_type; + + if (var.storage == StorageClassOutput) + { + if (need_sample_mask && builtin == BuiltInSampleMask) + { + builtin_sample_mask_id = var.self; + mark_implicit_builtin(StorageClassOutput, BuiltInSampleMask, var.self); + does_shader_write_sample_mask = true; + } + } + + if (var.storage != StorageClassInput) + return; + + if (need_subpass_input && (!msl_options.use_framebuffer_fetch_subpasses)) + { + switch (builtin) + { + case BuiltInFragCoord: + mark_implicit_builtin(StorageClassInput, BuiltInFragCoord, var.self); + builtin_frag_coord_id = var.self; + has_frag_coord = true; + break; + case BuiltInLayer: + if (!msl_options.arrayed_subpass_input || msl_options.multiview) + break; + mark_implicit_builtin(StorageClassInput, BuiltInLayer, var.self); + builtin_layer_id = var.self; + has_layer = true; + break; + case BuiltInViewIndex: + if (!msl_options.multiview) + break; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var.self); + builtin_view_idx_id = var.self; + has_view_idx = true; + break; + default: + break; + } + } + + if ((need_sample_pos || needs_sample_id) && builtin == BuiltInSampleId) + { + builtin_sample_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSampleId, var.self); + has_sample_id = true; + } + + if (need_vertex_params) + { + switch (builtin) + { + case BuiltInVertexIndex: + builtin_vertex_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInVertexIndex, var.self); + has_vertex_idx = true; + break; + case BuiltInBaseVertex: + builtin_base_vertex_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInBaseVertex, var.self); + has_base_vertex = true; + break; + case BuiltInInstanceIndex: + builtin_instance_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var.self); + has_instance_idx = true; + break; + case BuiltInBaseInstance: + builtin_base_instance_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInBaseInstance, var.self); + has_base_instance = true; + break; + default: + break; + } + } + + if (need_tesc_params) + { + switch (builtin) + { + case BuiltInInvocationId: + builtin_invocation_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var.self); + has_invocation_id = true; + break; + case BuiltInPrimitiveId: + builtin_primitive_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var.self); + has_primitive_id = true; + break; + default: + break; + } + } + + if ((need_subgroup_mask || needs_subgroup_invocation_id) && builtin == BuiltInSubgroupLocalInvocationId) + { + builtin_subgroup_invocation_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupLocalInvocationId, var.self); + has_subgroup_invocation_id = true; + } + + if ((need_subgroup_ge_mask || needs_subgroup_size) && builtin == BuiltInSubgroupSize) + { + builtin_subgroup_size_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupSize, var.self); + has_subgroup_size = true; + } + + if (need_multiview) + { + switch (builtin) + { + case BuiltInInstanceIndex: + // The view index here is derived from the instance index. + builtin_instance_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var.self); + has_instance_idx = true; + break; + case BuiltInBaseInstance: + // If a non-zero base instance is used, we need to adjust for it when calculating the view index. + builtin_base_instance_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInBaseInstance, var.self); + has_base_instance = true; + break; + case BuiltInViewIndex: + builtin_view_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var.self); + has_view_idx = true; + break; + default: + break; + } + } + + // The base workgroup needs to have the same type and vector size + // as the workgroup or invocation ID, so keep track of the type that + // was used. + if (need_dispatch_base && workgroup_id_type == 0 && + (builtin == BuiltInWorkgroupId || builtin == BuiltInGlobalInvocationId)) + workgroup_id_type = var.basetype; + }); + + // Use Metal's native frame-buffer fetch API for subpass inputs. + if ((!has_frag_coord || (msl_options.multiview && !has_view_idx) || + (msl_options.arrayed_subpass_input && !msl_options.multiview && !has_layer)) && + (!msl_options.use_framebuffer_fetch_subpasses) && need_subpass_input) + { + if (!has_frag_coord) + { + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_id = offset; + uint32_t type_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + // Create gl_FragCoord. + SPIRType vec4_type; + vec4_type.basetype = SPIRType::Float; + vec4_type.width = 32; + vec4_type.vecsize = 4; + set(type_id, vec4_type); + + SPIRType vec4_type_ptr; + vec4_type_ptr = vec4_type; + vec4_type_ptr.pointer = true; + vec4_type_ptr.parent_type = type_id; + vec4_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, vec4_type_ptr); + ptr_type.self = type_id; + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInFragCoord); + builtin_frag_coord_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInFragCoord, var_id); + } + + if (!has_layer && msl_options.arrayed_subpass_input && !msl_options.multiview) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_Layer. + SPIRType uint_type_ptr; + uint_type_ptr = get_uint_type(); + uint_type_ptr.pointer = true; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInLayer); + builtin_layer_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInLayer, var_id); + } + + if (!has_view_idx && msl_options.multiview) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_ViewIndex. + SPIRType uint_type_ptr; + uint_type_ptr = get_uint_type(); + uint_type_ptr.pointer = true; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInViewIndex); + builtin_view_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var_id); + } + } + + if (!has_sample_id && (need_sample_pos || needs_sample_id)) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_SampleID. + SPIRType uint_type_ptr; + uint_type_ptr = get_uint_type(); + uint_type_ptr.pointer = true; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInSampleId); + builtin_sample_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInSampleId, var_id); + } + + if ((need_vertex_params && (!has_vertex_idx || !has_base_vertex || !has_instance_idx || !has_base_instance)) || + (need_multiview && (!has_instance_idx || !has_base_instance || !has_view_idx))) + { + uint32_t type_ptr_id = ir.increase_bound_by(1); + + SPIRType uint_type_ptr; + uint_type_ptr = get_uint_type(); + uint_type_ptr.pointer = true; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + if (need_vertex_params && !has_vertex_idx) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_VertexIndex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInVertexIndex); + builtin_vertex_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInVertexIndex, var_id); + } + + if (need_vertex_params && !has_base_vertex) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_BaseVertex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInBaseVertex); + builtin_base_vertex_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInBaseVertex, var_id); + } + + if (!has_instance_idx) // Needed by both multiview and tessellation + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_InstanceIndex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInInstanceIndex); + builtin_instance_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var_id); + } + + if (!has_base_instance) // Needed by both multiview and tessellation + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_BaseInstance. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInBaseInstance); + builtin_base_instance_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInBaseInstance, var_id); + } + + if (need_multiview) + { + // Multiview shaders are not allowed to write to gl_Layer, ostensibly because + // it is implicitly written from gl_ViewIndex, but we have to do that explicitly. + // Note that we can't just abuse gl_ViewIndex for this purpose: it's an input, but + // gl_Layer is an output in vertex-pipeline shaders. + uint32_t type_ptr_out_id = ir.increase_bound_by(2); + SPIRType uint_type_ptr_out; + uint_type_ptr_out = get_uint_type(); + uint_type_ptr_out.pointer = true; + uint_type_ptr_out.parent_type = get_uint_type_id(); + uint_type_ptr_out.storage = StorageClassOutput; + auto &ptr_out_type = set(type_ptr_out_id, uint_type_ptr_out); + ptr_out_type.self = get_uint_type_id(); + uint32_t var_id = type_ptr_out_id + 1; + set(var_id, type_ptr_out_id, StorageClassOutput); + set_decoration(var_id, DecorationBuiltIn, BuiltInLayer); + builtin_layer_id = var_id; + mark_implicit_builtin(StorageClassOutput, BuiltInLayer, var_id); + } + + if (need_multiview && !has_view_idx) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_ViewIndex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInViewIndex); + builtin_view_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var_id); + } + } + + if ((need_tesc_params && (msl_options.multi_patch_workgroup || !has_invocation_id || !has_primitive_id)) || + need_grid_params) + { + uint32_t type_ptr_id = ir.increase_bound_by(1); + + SPIRType uint_type_ptr; + uint_type_ptr = get_uint_type(); + uint_type_ptr.pointer = true; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + if (msl_options.multi_patch_workgroup || need_grid_params) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_GlobalInvocationID. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInGlobalInvocationId); + builtin_invocation_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInGlobalInvocationId, var_id); + } + else if (need_tesc_params && !has_invocation_id) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_InvocationID. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInInvocationId); + builtin_invocation_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var_id); + } + + if (need_tesc_params && !has_primitive_id) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_PrimitiveID. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInPrimitiveId); + builtin_primitive_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var_id); + } + + if (need_grid_params) + { + uint32_t var_id = ir.increase_bound_by(1); + + set(var_id, build_extended_vector_type(get_uint_type_id(), 3), StorageClassInput); + set_extended_decoration(var_id, SPIRVCrossDecorationBuiltInStageInputSize); + get_entry_point().interface_variables.push_back(var_id); + set_name(var_id, "spvStageInputSize"); + builtin_stage_input_size_id = var_id; + } + } + + if (!has_subgroup_invocation_id && (need_subgroup_mask || needs_subgroup_invocation_id)) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_SubgroupInvocationID. + SPIRType uint_type_ptr; + uint_type_ptr = get_uint_type(); + uint_type_ptr.pointer = true; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInSubgroupLocalInvocationId); + builtin_subgroup_invocation_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupLocalInvocationId, var_id); + } + + if (!has_subgroup_size && (need_subgroup_ge_mask || needs_subgroup_size)) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_SubgroupSize. + SPIRType uint_type_ptr; + uint_type_ptr = get_uint_type(); + uint_type_ptr.pointer = true; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInSubgroupSize); + builtin_subgroup_size_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupSize, var_id); + } + + if (need_dispatch_base || need_vertex_base_params) + { + if (workgroup_id_type == 0) + workgroup_id_type = build_extended_vector_type(get_uint_type_id(), 3); + uint32_t var_id; + if (msl_options.supports_msl_version(1, 2)) + { + // If we have MSL 1.2, we can (ab)use the [[grid_origin]] builtin + // to convey this information and save a buffer slot. + uint32_t offset = ir.increase_bound_by(1); + var_id = offset; + + set(var_id, workgroup_id_type, StorageClassInput); + set_extended_decoration(var_id, SPIRVCrossDecorationBuiltInDispatchBase); + get_entry_point().interface_variables.push_back(var_id); + } + else + { + // Otherwise, we need to fall back to a good ol' fashioned buffer. + uint32_t offset = ir.increase_bound_by(2); + var_id = offset; + uint32_t type_id = offset + 1; + + SPIRType var_type = get(workgroup_id_type); + var_type.storage = StorageClassUniform; + set(type_id, var_type); + + set(var_id, type_id, StorageClassUniform); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, ~(5u)); + set_decoration(var_id, DecorationBinding, msl_options.indirect_params_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, + msl_options.indirect_params_buffer_index); + } + set_name(var_id, "spvDispatchBase"); + builtin_dispatch_base_id = var_id; + } + + if (need_sample_mask && !does_shader_write_sample_mask) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t var_id = offset + 1; + + // Create gl_SampleMask. + SPIRType uint_type_ptr_out; + uint_type_ptr_out = get_uint_type(); + uint_type_ptr_out.pointer = true; + uint_type_ptr_out.parent_type = get_uint_type_id(); + uint_type_ptr_out.storage = StorageClassOutput; + + auto &ptr_out_type = set(offset, uint_type_ptr_out); + ptr_out_type.self = get_uint_type_id(); + set(var_id, offset, StorageClassOutput); + set_decoration(var_id, DecorationBuiltIn, BuiltInSampleMask); + builtin_sample_mask_id = var_id; + mark_implicit_builtin(StorageClassOutput, BuiltInSampleMask, var_id); + } + } + + if (needs_swizzle_buffer_def) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvSwizzleConstants"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, kSwizzleBufferBinding); + set_decoration(var_id, DecorationBinding, msl_options.swizzle_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.swizzle_buffer_index); + swizzle_buffer_id = var_id; + } + + if (!buffers_requiring_array_length.empty()) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvBufferSizeConstants"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, kBufferSizeBufferBinding); + set_decoration(var_id, DecorationBinding, msl_options.buffer_size_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.buffer_size_buffer_index); + buffer_size_buffer_id = var_id; + } + + if (needs_view_mask_buffer()) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvViewMask"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, ~(4u)); + set_decoration(var_id, DecorationBinding, msl_options.view_mask_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.view_mask_buffer_index); + view_mask_buffer_id = var_id; + } + + if (!buffers_requiring_dynamic_offset.empty()) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvDynamicOffsets"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, ~(5u)); + set_decoration(var_id, DecorationBinding, msl_options.dynamic_offsets_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, + msl_options.dynamic_offsets_buffer_index); + dynamic_offsets_buffer_id = var_id; + } +} + +// Checks if the specified builtin variable (e.g. gl_InstanceIndex) is marked as active. +// If not, it marks it as active and forces a recompilation. +// This might be used when the optimization of inactive builtins was too optimistic (e.g. when "spvOut" is emitted). +void CompilerMSL::ensure_builtin(spv::StorageClass storage, spv::BuiltIn builtin) +{ + Bitset *active_builtins = nullptr; + switch (storage) + { + case StorageClassInput: + active_builtins = &active_input_builtins; + break; + + case StorageClassOutput: + active_builtins = &active_output_builtins; + break; + + default: + break; + } + + // At this point, the specified builtin variable must have already been declared in the entry point. + // If not, mark as active and force recompile. + if (active_builtins != nullptr && !active_builtins->get(builtin)) + { + active_builtins->set(builtin); + force_recompile(); + } +} + +void CompilerMSL::mark_implicit_builtin(StorageClass storage, BuiltIn builtin, uint32_t id) +{ + Bitset *active_builtins = nullptr; + switch (storage) + { + case StorageClassInput: + active_builtins = &active_input_builtins; + break; + + case StorageClassOutput: + active_builtins = &active_output_builtins; + break; + + default: + break; + } + + assert(active_builtins != nullptr); + active_builtins->set(builtin); + + auto &var = get_entry_point().interface_variables; + if (find(begin(var), end(var), VariableID(id)) == end(var)) + var.push_back(id); +} + +uint32_t CompilerMSL::build_constant_uint_array_pointer() +{ + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_ptr_id = offset; + uint32_t type_ptr_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + // Create a buffer to hold extra data, including the swizzle constants. + SPIRType uint_type_pointer = get_uint_type(); + uint_type_pointer.pointer = true; + uint_type_pointer.pointer_depth = 1; + uint_type_pointer.parent_type = get_uint_type_id(); + uint_type_pointer.storage = StorageClassUniform; + set(type_ptr_id, uint_type_pointer); + set_decoration(type_ptr_id, DecorationArrayStride, 4); + + SPIRType uint_type_pointer2 = uint_type_pointer; + uint_type_pointer2.pointer_depth++; + uint_type_pointer2.parent_type = type_ptr_id; + set(type_ptr_ptr_id, uint_type_pointer2); + + set(var_id, type_ptr_ptr_id, StorageClassUniformConstant); + return var_id; +} + +static string create_sampler_address(const char *prefix, MSLSamplerAddress addr) +{ + switch (addr) + { + case MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE: + return join(prefix, "address::clamp_to_edge"); + case MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO: + return join(prefix, "address::clamp_to_zero"); + case MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER: + return join(prefix, "address::clamp_to_border"); + case MSL_SAMPLER_ADDRESS_REPEAT: + return join(prefix, "address::repeat"); + case MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT: + return join(prefix, "address::mirrored_repeat"); + default: + SPIRV_CROSS_THROW("Invalid sampler addressing mode."); + } +} + +SPIRType &CompilerMSL::get_stage_in_struct_type() +{ + auto &si_var = get(stage_in_var_id); + return get_variable_data_type(si_var); +} + +SPIRType &CompilerMSL::get_stage_out_struct_type() +{ + auto &so_var = get(stage_out_var_id); + return get_variable_data_type(so_var); +} + +SPIRType &CompilerMSL::get_patch_stage_in_struct_type() +{ + auto &si_var = get(patch_stage_in_var_id); + return get_variable_data_type(si_var); +} + +SPIRType &CompilerMSL::get_patch_stage_out_struct_type() +{ + auto &so_var = get(patch_stage_out_var_id); + return get_variable_data_type(so_var); +} + +std::string CompilerMSL::get_tess_factor_struct_name() +{ + if (get_entry_point().flags.get(ExecutionModeTriangles)) + return "MTLTriangleTessellationFactorsHalf"; + return "MTLQuadTessellationFactorsHalf"; +} + +SPIRType &CompilerMSL::get_uint_type() +{ + return get(get_uint_type_id()); +} + +uint32_t CompilerMSL::get_uint_type_id() +{ + if (uint_type_id != 0) + return uint_type_id; + + uint_type_id = ir.increase_bound_by(1); + + SPIRType type; + type.basetype = SPIRType::UInt; + type.width = 32; + set(uint_type_id, type); + return uint_type_id; +} + +void CompilerMSL::emit_entry_point_declarations() +{ + // FIXME: Get test coverage here ... + // Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries + declare_complex_constant_arrays(); + + // Emit constexpr samplers here. + for (auto &samp : constexpr_samplers_by_id) + { + auto &var = get(samp.first); + auto &type = get(var.basetype); + if (type.basetype == SPIRType::Sampler) + add_resource_name(samp.first); + + SmallVector args; + auto &s = samp.second; + + if (s.coord != MSL_SAMPLER_COORD_NORMALIZED) + args.push_back("coord::pixel"); + + if (s.min_filter == s.mag_filter) + { + if (s.min_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("filter::linear"); + } + else + { + if (s.min_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("min_filter::linear"); + if (s.mag_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("mag_filter::linear"); + } + + switch (s.mip_filter) + { + case MSL_SAMPLER_MIP_FILTER_NONE: + // Default + break; + case MSL_SAMPLER_MIP_FILTER_NEAREST: + args.push_back("mip_filter::nearest"); + break; + case MSL_SAMPLER_MIP_FILTER_LINEAR: + args.push_back("mip_filter::linear"); + break; + default: + SPIRV_CROSS_THROW("Invalid mip filter."); + } + + if (s.s_address == s.t_address && s.s_address == s.r_address) + { + if (s.s_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("", s.s_address)); + } + else + { + if (s.s_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("s_", s.s_address)); + if (s.t_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("t_", s.t_address)); + if (s.r_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("r_", s.r_address)); + } + + if (s.compare_enable) + { + switch (s.compare_func) + { + case MSL_SAMPLER_COMPARE_FUNC_ALWAYS: + args.push_back("compare_func::always"); + break; + case MSL_SAMPLER_COMPARE_FUNC_NEVER: + args.push_back("compare_func::never"); + break; + case MSL_SAMPLER_COMPARE_FUNC_EQUAL: + args.push_back("compare_func::equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL: + args.push_back("compare_func::not_equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_LESS: + args.push_back("compare_func::less"); + break; + case MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL: + args.push_back("compare_func::less_equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_GREATER: + args.push_back("compare_func::greater"); + break; + case MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL: + args.push_back("compare_func::greater_equal"); + break; + default: + SPIRV_CROSS_THROW("Invalid sampler compare function."); + } + } + + if (s.s_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER || s.t_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER || + s.r_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER) + { + switch (s.border_color) + { + case MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK: + args.push_back("border_color::opaque_black"); + break; + case MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE: + args.push_back("border_color::opaque_white"); + break; + case MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK: + args.push_back("border_color::transparent_black"); + break; + default: + SPIRV_CROSS_THROW("Invalid sampler border color."); + } + } + + if (s.anisotropy_enable) + args.push_back(join("max_anisotropy(", s.max_anisotropy, ")")); + if (s.lod_clamp_enable) + { + args.push_back(join("lod_clamp(", convert_to_string(s.lod_clamp_min, current_locale_radix_character), ", ", + convert_to_string(s.lod_clamp_max, current_locale_radix_character), ")")); + } + + // If we would emit no arguments, then omit the parentheses entirely. Otherwise, + // we'll wind up with a "most vexing parse" situation. + if (args.empty()) + statement("constexpr sampler ", + type.basetype == SPIRType::SampledImage ? to_sampler_expression(samp.first) : to_name(samp.first), + ";"); + else + statement("constexpr sampler ", + type.basetype == SPIRType::SampledImage ? to_sampler_expression(samp.first) : to_name(samp.first), + "(", merge(args), ");"); + } + + // Emit dynamic buffers here. + for (auto &dynamic_buffer : buffers_requiring_dynamic_offset) + { + if (!dynamic_buffer.second.second) + { + // Could happen if no buffer was used at requested binding point. + continue; + } + + const auto &var = get(dynamic_buffer.second.second); + uint32_t var_id = var.self; + const auto &type = get_variable_data_type(var); + string name = to_name(var.self); + uint32_t desc_set = get_decoration(var.self, DecorationDescriptorSet); + uint32_t arg_id = argument_buffer_ids[desc_set]; + uint32_t base_index = dynamic_buffer.second.first; + + if (!type.array.empty()) + { + // This is complicated, because we need to support arrays of arrays. + // And it's even worse if the outermost dimension is a runtime array, because now + // all this complicated goop has to go into the shader itself. (FIXME) + if (!type.array[type.array.size() - 1]) + SPIRV_CROSS_THROW("Runtime arrays with dynamic offsets are not supported yet."); + else + { + is_using_builtin_array = true; + statement(get_argument_address_space(var), " ", type_to_glsl(type), "* ", to_restrict(var_id), name, + type_to_array_glsl(type), " ="); + + uint32_t dim = uint32_t(type.array.size()); + uint32_t j = 0; + for (SmallVector indices(type.array.size()); + indices[type.array.size() - 1] < to_array_size_literal(type); j++) + { + while (dim > 0) + { + begin_scope(); + --dim; + } + + string arrays; + for (uint32_t i = uint32_t(type.array.size()); i; --i) + arrays += join("[", indices[i - 1], "]"); + statement("(", get_argument_address_space(var), " ", type_to_glsl(type), "* ", + to_restrict(var_id, false), ")((", get_argument_address_space(var), " char* ", + to_restrict(var_id, false), ")", to_name(arg_id), ".", ensure_valid_name(name, "m"), + arrays, " + ", to_name(dynamic_offsets_buffer_id), "[", base_index + j, "]),"); + + while (++indices[dim] >= to_array_size_literal(type, dim) && dim < type.array.size() - 1) + { + end_scope(","); + indices[dim++] = 0; + } + } + end_scope_decl(); + statement_no_indent(""); + is_using_builtin_array = false; + } + } + else + { + statement(get_argument_address_space(var), " auto& ", to_restrict(var_id), name, " = *(", + get_argument_address_space(var), " ", type_to_glsl(type), "* ", to_restrict(var_id, false), ")((", + get_argument_address_space(var), " char* ", to_restrict(var_id, false), ")", to_name(arg_id), ".", + ensure_valid_name(name, "m"), " + ", to_name(dynamic_offsets_buffer_id), "[", base_index, "]);"); + } + } + + // Emit buffer arrays here. + for (uint32_t array_id : buffer_arrays) + { + const auto &var = get(array_id); + const auto &type = get_variable_data_type(var); + const auto &buffer_type = get_variable_element_type(var); + string name = to_name(array_id); + statement(get_argument_address_space(var), " ", type_to_glsl(buffer_type), "* ", to_restrict(array_id), name, + "[] ="); + begin_scope(); + for (uint32_t i = 0; i < to_array_size_literal(type); ++i) + statement(name, "_", i, ","); + end_scope_decl(); + statement_no_indent(""); + } + // For some reason, without this, we end up emitting the arrays twice. + buffer_arrays.clear(); + + // Emit disabled fragment outputs. + std::sort(disabled_frag_outputs.begin(), disabled_frag_outputs.end()); + for (uint32_t var_id : disabled_frag_outputs) + { + auto &var = get(var_id); + add_local_variable_name(var_id); + statement(variable_decl(var), ";"); + var.deferred_declaration = false; + } +} + +string CompilerMSL::compile() +{ + ir.fixup_reserved_names(); + + // Do not deal with GLES-isms like precision, older extensions and such. + options.vulkan_semantics = true; + options.es = false; + options.version = 450; + backend.null_pointer_literal = "nullptr"; + backend.float_literal_suffix = false; + backend.uint32_t_literal_suffix = true; + backend.int16_t_literal_suffix = ""; + backend.uint16_t_literal_suffix = ""; + backend.basic_int_type = "int"; + backend.basic_uint_type = "uint"; + backend.basic_int8_type = "char"; + backend.basic_uint8_type = "uchar"; + backend.basic_int16_type = "short"; + backend.basic_uint16_type = "ushort"; + backend.discard_literal = "discard_fragment()"; + backend.demote_literal = "discard_fragment()"; + backend.boolean_mix_function = "select"; + backend.swizzle_is_function = false; + backend.shared_is_implied = false; + backend.use_initializer_list = true; + backend.use_typed_initializer_list = true; + backend.native_row_major_matrix = false; + backend.unsized_array_supported = false; + backend.can_declare_arrays_inline = false; + backend.allow_truncated_access_chain = true; + backend.comparison_image_samples_scalar = true; + backend.native_pointers = true; + backend.nonuniform_qualifier = ""; + backend.support_small_type_sampling_result = true; + backend.supports_empty_struct = true; + + // Allow Metal to use the array template unless we force it off. + backend.can_return_array = !msl_options.force_native_arrays; + backend.array_is_value_type = !msl_options.force_native_arrays; + // Arrays which are part of buffer objects are never considered to be native arrays. + backend.buffer_offset_array_is_value_type = false; + + capture_output_to_buffer = msl_options.capture_output_to_buffer; + is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer; + + // Initialize array here rather than constructor, MSVC 2013 workaround. + for (auto &id : next_metal_resource_ids) + id = 0; + + fixup_type_alias(); + replace_illegal_names(); + + build_function_control_flow_graphs_and_analyze(); + update_active_builtins(); + analyze_image_and_sampler_usage(); + analyze_sampled_image_usage(); + analyze_interlocked_resource_usage(); + preprocess_op_codes(); + build_implicit_builtins(); + + fixup_image_load_store_access(); + + set_enabled_interface_variables(get_active_interface_variables()); + if (msl_options.force_active_argument_buffer_resources) + activate_argument_buffer_resources(); + + if (swizzle_buffer_id) + active_interface_variables.insert(swizzle_buffer_id); + if (buffer_size_buffer_id) + active_interface_variables.insert(buffer_size_buffer_id); + if (view_mask_buffer_id) + active_interface_variables.insert(view_mask_buffer_id); + if (dynamic_offsets_buffer_id) + active_interface_variables.insert(dynamic_offsets_buffer_id); + if (builtin_layer_id) + active_interface_variables.insert(builtin_layer_id); + if (builtin_dispatch_base_id && !msl_options.supports_msl_version(1, 2)) + active_interface_variables.insert(builtin_dispatch_base_id); + if (builtin_sample_mask_id) + active_interface_variables.insert(builtin_sample_mask_id); + + // Create structs to hold input, output and uniform variables. + // Do output first to ensure out. is declared at top of entry function. + qual_pos_var_name = ""; + stage_out_var_id = add_interface_block(StorageClassOutput); + patch_stage_out_var_id = add_interface_block(StorageClassOutput, true); + stage_in_var_id = add_interface_block(StorageClassInput); + if (get_execution_model() == ExecutionModelTessellationEvaluation) + patch_stage_in_var_id = add_interface_block(StorageClassInput, true); + + if (get_execution_model() == ExecutionModelTessellationControl) + stage_out_ptr_var_id = add_interface_block_pointer(stage_out_var_id, StorageClassOutput); + if (is_tessellation_shader()) + stage_in_ptr_var_id = add_interface_block_pointer(stage_in_var_id, StorageClassInput); + + // Metal vertex functions that define no output must disable rasterization and return void. + if (!stage_out_var_id) + is_rasterization_disabled = true; + + // Convert the use of global variables to recursively-passed function parameters + localize_global_variables(); + extract_global_variables_from_functions(); + + // Mark any non-stage-in structs to be tightly packed. + mark_packable_structs(); + reorder_type_alias(); + + // Add fixup hooks required by shader inputs and outputs. This needs to happen before + // the loop, so the hooks aren't added multiple times. + fix_up_shader_inputs_outputs(); + + // If we are using argument buffers, we create argument buffer structures for them here. + // These buffers will be used in the entry point, not the individual resources. + if (msl_options.argument_buffers) + { + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("Argument buffers can only be used with MSL 2.0 and up."); + analyze_argument_buffers(); + } + + uint32_t pass_count = 0; + do + { + if (pass_count >= 3) + SPIRV_CROSS_THROW("Over 3 compilation loops detected. Must be a bug!"); + + reset(); + + // Start bindings at zero. + next_metal_resource_index_buffer = 0; + next_metal_resource_index_texture = 0; + next_metal_resource_index_sampler = 0; + for (auto &id : next_metal_resource_ids) + id = 0; + + // Move constructor for this type is broken on GCC 4.9 ... + buffer.reset(); + + emit_header(); + emit_custom_templates(); + emit_specialization_constants_and_structs(); + emit_resources(); + emit_custom_functions(); + emit_function(get(ir.default_entry_point), Bitset()); + + pass_count++; + } while (is_forcing_recompilation()); + + return buffer.str(); +} + +// Register the need to output any custom functions. +void CompilerMSL::preprocess_op_codes() +{ + OpCodePreprocessor preproc(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), preproc); + + suppress_missing_prototypes = preproc.suppress_missing_prototypes; + + if (preproc.uses_atomics) + { + add_header_line("#include "); + add_pragma_line("#pragma clang diagnostic ignored \"-Wunused-variable\""); + } + + // Before MSL 2.1 (2.2 for textures), Metal vertex functions that write to + // resources must disable rasterization and return void. + if (preproc.uses_resource_write) + is_rasterization_disabled = true; + + // Tessellation control shaders are run as compute functions in Metal, and so + // must capture their output to a buffer. + if (get_execution_model() == ExecutionModelTessellationControl || + (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation)) + { + is_rasterization_disabled = true; + capture_output_to_buffer = true; + } + + if (preproc.needs_subgroup_invocation_id) + needs_subgroup_invocation_id = true; + if (preproc.needs_subgroup_size) + needs_subgroup_size = true; + if (preproc.needs_sample_id) + needs_sample_id = true; +} + +// Move the Private and Workgroup global variables to the entry function. +// Non-constant variables cannot have global scope in Metal. +void CompilerMSL::localize_global_variables() +{ + auto &entry_func = get(ir.default_entry_point); + auto iter = global_variables.begin(); + while (iter != global_variables.end()) + { + uint32_t v_id = *iter; + auto &var = get(v_id); + if (var.storage == StorageClassPrivate || var.storage == StorageClassWorkgroup) + { + if (!variable_is_lut(var)) + entry_func.add_local_variable(v_id); + iter = global_variables.erase(iter); + } + else + iter++; + } +} + +// For any global variable accessed directly by a function, +// extract that variable and add it as an argument to that function. +void CompilerMSL::extract_global_variables_from_functions() +{ + // Uniforms + unordered_set global_var_ids; + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (var.storage == StorageClassInput || var.storage == StorageClassOutput || + var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || + var.storage == StorageClassPushConstant || var.storage == StorageClassStorageBuffer) + { + global_var_ids.insert(var.self); + } + }); + + // Local vars that are declared in the main function and accessed directly by a function + auto &entry_func = get(ir.default_entry_point); + for (auto &var : entry_func.local_variables) + if (get(var).storage != StorageClassFunction) + global_var_ids.insert(var); + + std::set added_arg_ids; + unordered_set processed_func_ids; + extract_global_variables_from_function(ir.default_entry_point, added_arg_ids, global_var_ids, processed_func_ids); +} + +// MSL does not support the use of global variables for shader input content. +// For any global variable accessed directly by the specified function, extract that variable, +// add it as an argument to that function, and the arg to the added_arg_ids collection. +void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::set &added_arg_ids, + unordered_set &global_var_ids, + unordered_set &processed_func_ids) +{ + // Avoid processing a function more than once + if (processed_func_ids.find(func_id) != processed_func_ids.end()) + { + // Return function global variables + added_arg_ids = function_global_vars[func_id]; + return; + } + + processed_func_ids.insert(func_id); + + auto &func = get(func_id); + + // Recursively establish global args added to functions on which we depend. + for (auto block : func.blocks) + { + auto &b = get(block); + for (auto &i : b.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + switch (op) + { + case OpLoad: + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + case OpArrayLength: + { + uint32_t base_id = ops[2]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + + // Use Metal's native frame-buffer fetch API for subpass inputs. + auto &type = get(ops[0]); + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && + (!msl_options.use_framebuffer_fetch_subpasses)) + { + // Implicitly reads gl_FragCoord. + assert(builtin_frag_coord_id != 0); + added_arg_ids.insert(builtin_frag_coord_id); + if (msl_options.multiview) + { + // Implicitly reads gl_ViewIndex. + assert(builtin_view_idx_id != 0); + added_arg_ids.insert(builtin_view_idx_id); + } + else if (msl_options.arrayed_subpass_input) + { + // Implicitly reads gl_Layer. + assert(builtin_layer_id != 0); + added_arg_ids.insert(builtin_layer_id); + } + } + + break; + } + + case OpFunctionCall: + { + // First see if any of the function call args are globals + for (uint32_t arg_idx = 3; arg_idx < i.length; arg_idx++) + { + uint32_t arg_id = ops[arg_idx]; + if (global_var_ids.find(arg_id) != global_var_ids.end()) + added_arg_ids.insert(arg_id); + } + + // Then recurse into the function itself to extract globals used internally in the function + uint32_t inner_func_id = ops[2]; + std::set inner_func_args; + extract_global_variables_from_function(inner_func_id, inner_func_args, global_var_ids, + processed_func_ids); + added_arg_ids.insert(inner_func_args.begin(), inner_func_args.end()); + break; + } + + case OpStore: + { + uint32_t base_id = ops[0]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + + uint32_t rvalue_id = ops[1]; + if (global_var_ids.find(rvalue_id) != global_var_ids.end()) + added_arg_ids.insert(rvalue_id); + + break; + } + + case OpSelect: + { + uint32_t base_id = ops[3]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + base_id = ops[4]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + break; + } + + // Emulate texture2D atomic operations + case OpImageTexelPointer: + { + // When using the pointer, we need to know which variable it is actually loaded from. + uint32_t base_id = ops[2]; + auto *var = maybe_get_backing_variable(base_id); + if (var && atomic_image_vars.count(var->self)) + { + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + } + break; + } + + case OpExtInst: + { + uint32_t extension_set = ops[2]; + if (get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(ops[3]); + switch (op_450) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + // For these, we really need the stage-in block. It is theoretically possible to pass the + // interpolant object, but a) doing so would require us to create an entirely new variable + // with Interpolant type, and b) if we have a struct or array, handling all the members and + // elements could get unwieldy fast. + added_arg_ids.insert(stage_in_var_id); + break; + } + default: + break; + } + } + } + + default: + break; + } + + // TODO: Add all other operations which can affect memory. + // We should consider a more unified system here to reduce boiler-plate. + // This kind of analysis is done in several places ... + } + } + + function_global_vars[func_id] = added_arg_ids; + + // Add the global variables as arguments to the function + if (func_id != ir.default_entry_point) + { + bool added_in = false; + bool added_out = false; + for (uint32_t arg_id : added_arg_ids) + { + auto &var = get(arg_id); + uint32_t type_id = var.basetype; + auto *p_type = &get(type_id); + BuiltIn bi_type = BuiltIn(get_decoration(arg_id, DecorationBuiltIn)); + + if (((is_tessellation_shader() && var.storage == StorageClassInput) || + (get_execution_model() == ExecutionModelTessellationControl && var.storage == StorageClassOutput)) && + !(has_decoration(arg_id, DecorationPatch) || is_patch_block(*p_type)) && + (!is_builtin_variable(var) || bi_type == BuiltInPosition || bi_type == BuiltInPointSize || + bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance || + p_type->basetype == SPIRType::Struct)) + { + // Tessellation control shaders see inputs and per-vertex outputs as arrays. + // Similarly, tessellation evaluation shaders see per-vertex inputs as arrays. + // We collected them into a structure; we must pass the array of this + // structure to the function. + std::string name; + if (var.storage == StorageClassInput) + { + if (added_in) + continue; + name = "gl_in"; + arg_id = stage_in_ptr_var_id; + added_in = true; + } + else if (var.storage == StorageClassOutput) + { + if (added_out) + continue; + name = "gl_out"; + arg_id = stage_out_ptr_var_id; + added_out = true; + } + type_id = get(arg_id).basetype; + uint32_t next_id = ir.increase_bound_by(1); + func.add_parameter(type_id, next_id, true); + set(next_id, type_id, StorageClassFunction, 0, arg_id); + + set_name(next_id, name); + } + else if (is_builtin_variable(var) && p_type->basetype == SPIRType::Struct) + { + // Get the pointee type + type_id = get_pointee_type_id(type_id); + p_type = &get(type_id); + + uint32_t mbr_idx = 0; + for (auto &mbr_type_id : p_type->member_types) + { + BuiltIn builtin = BuiltInMax; + bool is_builtin = is_member_builtin(*p_type, mbr_idx, &builtin); + if (is_builtin && has_active_builtin(builtin, var.storage)) + { + // Add a arg variable with the same type and decorations as the member + uint32_t next_ids = ir.increase_bound_by(2); + uint32_t ptr_type_id = next_ids + 0; + uint32_t var_id = next_ids + 1; + + // Make sure we have an actual pointer type, + // so that we will get the appropriate address space when declaring these builtins. + auto &ptr = set(ptr_type_id, get(mbr_type_id)); + ptr.self = mbr_type_id; + ptr.storage = var.storage; + ptr.pointer = true; + ptr.parent_type = mbr_type_id; + + func.add_parameter(mbr_type_id, var_id, true); + set(var_id, ptr_type_id, StorageClassFunction); + ir.meta[var_id].decoration = ir.meta[type_id].members[mbr_idx]; + } + mbr_idx++; + } + } + else + { + uint32_t next_id = ir.increase_bound_by(1); + func.add_parameter(type_id, next_id, true); + set(next_id, type_id, StorageClassFunction, 0, arg_id); + + // Ensure the existing variable has a valid name and the new variable has all the same meta info + set_name(arg_id, ensure_valid_name(to_name(arg_id), "v")); + ir.meta[next_id] = ir.meta[arg_id]; + } + } + } +} + +// For all variables that are some form of non-input-output interface block, mark that all the structs +// that are recursively contained within the type referenced by that variable should be packed tightly. +void CompilerMSL::mark_packable_structs() +{ + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (var.storage != StorageClassFunction && !is_hidden_variable(var)) + { + auto &type = this->get(var.basetype); + if (type.pointer && + (type.storage == StorageClassUniform || type.storage == StorageClassUniformConstant || + type.storage == StorageClassPushConstant || type.storage == StorageClassStorageBuffer) && + (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))) + mark_as_packable(type); + } + }); +} + +// If the specified type is a struct, it and any nested structs +// are marked as packable with the SPIRVCrossDecorationBufferBlockRepacked decoration, +void CompilerMSL::mark_as_packable(SPIRType &type) +{ + // If this is not the base type (eg. it's a pointer or array), tunnel down + if (type.parent_type) + { + mark_as_packable(get(type.parent_type)); + return; + } + + if (type.basetype == SPIRType::Struct) + { + set_extended_decoration(type.self, SPIRVCrossDecorationBufferBlockRepacked); + + // Recurse + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + uint32_t mbr_type_id = type.member_types[mbr_idx]; + auto &mbr_type = get(mbr_type_id); + mark_as_packable(mbr_type); + if (mbr_type.type_alias) + { + auto &mbr_type_alias = get(mbr_type.type_alias); + mark_as_packable(mbr_type_alias); + } + } + } +} + +// If a shader input exists at the location, it is marked as being used by this shader +void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, StorageClass storage) +{ + if (storage != StorageClassInput) + return; + if (is_array(type)) + { + uint32_t dim = 1; + for (uint32_t i = 0; i < type.array.size(); i++) + dim *= to_array_size_literal(type, i); + for (uint32_t i = 0; i < dim; i++) + { + if (is_matrix(type)) + { + for (uint32_t j = 0; j < type.columns; j++) + inputs_in_use.insert(location++); + } + else + inputs_in_use.insert(location++); + } + } + else if (is_matrix(type)) + { + for (uint32_t i = 0; i < type.columns; i++) + inputs_in_use.insert(location + i); + } + else + inputs_in_use.insert(location); +} + +uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const +{ + auto itr = fragment_output_components.find(location); + if (itr == end(fragment_output_components)) + return 4; + else + return itr->second; +} + +uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t components, SPIRType::BaseType basetype) +{ + uint32_t new_type_id = ir.increase_bound_by(1); + auto &old_type = get(type_id); + auto *type = &set(new_type_id, old_type); + type->vecsize = components; + if (basetype != SPIRType::Unknown) + type->basetype = basetype; + type->self = new_type_id; + type->parent_type = type_id; + type->array.clear(); + type->array_size_literal.clear(); + type->pointer = false; + + if (is_array(old_type)) + { + uint32_t array_type_id = ir.increase_bound_by(1); + type = &set(array_type_id, *type); + type->parent_type = new_type_id; + type->array = old_type.array; + type->array_size_literal = old_type.array_size_literal; + new_type_id = array_type_id; + } + + if (old_type.pointer) + { + uint32_t ptr_type_id = ir.increase_bound_by(1); + type = &set(ptr_type_id, *type); + type->self = new_type_id; + type->parent_type = new_type_id; + type->storage = old_type.storage; + type->pointer = true; + new_type_id = ptr_type_id; + } + + return new_type_id; +} + +uint32_t CompilerMSL::build_msl_interpolant_type(uint32_t type_id, bool is_noperspective) +{ + uint32_t new_type_id = ir.increase_bound_by(1); + SPIRType &type = set(new_type_id, get(type_id)); + type.basetype = SPIRType::Interpolant; + type.parent_type = type_id; + // In Metal, the pull-model interpolant type encodes perspective-vs-no-perspective in the type itself. + // Add this decoration so we know which argument to pass to the template. + if (is_noperspective) + set_decoration(new_type_id, DecorationNoPerspective); + return new_type_id; +} + +void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta) +{ + bool is_builtin = is_builtin_variable(var); + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + bool is_flat = has_decoration(var.self, DecorationFlat); + bool is_noperspective = has_decoration(var.self, DecorationNoPerspective); + bool is_centroid = has_decoration(var.self, DecorationCentroid); + bool is_sample = has_decoration(var.self, DecorationSample); + + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + uint32_t type_id = ensure_correct_builtin_type(var.basetype, builtin); + var.basetype = type_id; + + type_id = get_pointee_type_id(var.basetype); + if (meta.strip_array && is_array(get(type_id))) + type_id = get(type_id).parent_type; + auto &type = get(type_id); + uint32_t target_components = 0; + uint32_t type_components = type.vecsize; + + bool padded_output = false; + bool padded_input = false; + uint32_t start_component = 0; + + auto &entry_func = get(ir.default_entry_point); + + // Deal with Component decorations. + InterfaceBlockMeta::LocationMeta *location_meta = nullptr; + if (has_decoration(var.self, DecorationLocation)) + { + auto location_meta_itr = meta.location_meta.find(get_decoration(var.self, DecorationLocation)); + if (location_meta_itr != end(meta.location_meta)) + location_meta = &location_meta_itr->second; + } + + bool pad_fragment_output = has_decoration(var.self, DecorationLocation) && + msl_options.pad_fragment_output_components && + get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput; + + // Check if we need to pad fragment output to match a certain number of components. + if (location_meta) + { + start_component = get_decoration(var.self, DecorationComponent); + uint32_t num_components = location_meta->num_components; + if (pad_fragment_output) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + num_components = std::max(num_components, get_target_components_for_fragment_location(locn)); + } + + if (location_meta->ib_index != ~0u) + { + // We have already declared the variable. Just emit an early-declared variable and fixup as needed. + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + + if (var.storage == StorageClassInput) + { + uint32_t ib_index = location_meta->ib_index; + entry_func.fixup_hooks_in.push_back([=, &var]() { + statement(to_name(var.self), " = ", ib_var_ref, ".", to_member_name(ib_type, ib_index), + vector_swizzle(type_components, start_component), ";"); + }); + } + else + { + uint32_t ib_index = location_meta->ib_index; + entry_func.fixup_hooks_out.push_back([=, &var]() { + statement(ib_var_ref, ".", to_member_name(ib_type, ib_index), + vector_swizzle(type_components, start_component), " = ", to_name(var.self), ";"); + }); + } + return; + } + else + { + location_meta->ib_index = uint32_t(ib_type.member_types.size()); + type_id = build_extended_vector_type(type_id, num_components); + if (var.storage == StorageClassInput) + padded_input = true; + else + padded_output = true; + } + } + else if (pad_fragment_output) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + target_components = get_target_components_for_fragment_location(locn); + if (type_components < target_components) + { + // Make a new type here. + type_id = build_extended_vector_type(type_id, target_components); + padded_output = true; + } + } + + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(type_id, is_noperspective)); + else + ib_type.member_types.push_back(type_id); + + // Give the member a name + string mbr_name = ensure_valid_name(to_expression(var.self), "m"); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // Update the original variable reference to include the structure reference + string qual_var_name = ib_var_ref + "." + mbr_name; + // If using pull-model interpolation, need to add a call to the correct interpolation method. + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + { + if (is_centroid) + qual_var_name += ".interpolate_at_centroid()"; + else if (is_sample) + qual_var_name += join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + qual_var_name += ".interpolate_at_center()"; + } + + if (padded_output || padded_input) + { + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + + if (padded_output) + { + entry_func.fixup_hooks_out.push_back([=, &var]() { + statement(qual_var_name, vector_swizzle(type_components, start_component), " = ", to_name(var.self), + ";"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=, &var]() { + statement(to_name(var.self), " = ", qual_var_name, vector_swizzle(type_components, start_component), + ";"); + }); + } + } + else if (!meta.strip_array) + ir.meta[var.self].decoration.qualified_alias = qual_var_name; + + if (var.storage == StorageClassOutput && var.initializer != ID(0)) + { + if (padded_output || padded_input) + { + entry_func.fixup_hooks_in.push_back( + [=, &var]() { statement(to_name(var.self), " = ", to_expression(var.initializer), ";"); }); + } + else + { + entry_func.fixup_hooks_in.push_back( + [=, &var]() { statement(qual_var_name, " = ", to_expression(var.initializer), ";"); }); + } + } + + // Copy the variable location from the original variable to the member + if (get_decoration_bitset(var.self).get(DecorationLocation)) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + if (storage == StorageClassInput) + { + type_id = ensure_correct_input_type(var.basetype, locn, location_meta ? location_meta->num_components : 0); + if (!location_meta) + var.basetype = type_id; + + type_id = get_pointee_type_id(type_id); + if (meta.strip_array && is_array(get(type_id))) + type_id = get(type_id).parent_type; + if (pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = type_id; + } + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, get(type_id), storage); + } + else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin)) + { + uint32_t locn = inputs_by_builtin[builtin].location; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, type, storage); + } + + if (!location_meta) + { + if (get_decoration_bitset(var.self).get(DecorationComponent)) + { + uint32_t component = get_decoration(var.self, DecorationComponent); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, component); + } + } + + if (get_decoration_bitset(var.self).get(DecorationIndex)) + { + uint32_t index = get_decoration(var.self, DecorationIndex); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index); + } + + // Mark the member as builtin if needed + if (is_builtin) + { + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin); + if (builtin == BuiltInPosition && storage == StorageClassOutput) + qual_pos_var_name = qual_var_name; + } + + // Copy interpolation decorations if needed + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + // If we have location meta, there is no unique OrigID. We won't need it, since we flatten/unflatten + // the variable to stack anyways here. + if (!location_meta) + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); +} + +void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, + InterfaceBlockMeta &meta) +{ + auto &entry_func = get(ir.default_entry_point); + auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var); + uint32_t elem_cnt = 0; + + if (is_matrix(var_type)) + { + if (is_array(var_type)) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables."); + + elem_cnt = var_type.columns; + } + else if (is_array(var_type)) + { + if (var_type.array.size() != 1) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables."); + + elem_cnt = to_array_size_literal(var_type); + } + + bool is_builtin = is_builtin_variable(var); + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + bool is_flat = has_decoration(var.self, DecorationFlat); + bool is_noperspective = has_decoration(var.self, DecorationNoPerspective); + bool is_centroid = has_decoration(var.self, DecorationCentroid); + bool is_sample = has_decoration(var.self, DecorationSample); + + auto *usable_type = &var_type; + if (usable_type->pointer) + usable_type = &get(usable_type->parent_type); + while (is_array(*usable_type) || is_matrix(*usable_type)) + usable_type = &get(usable_type->parent_type); + + // If a builtin, force it to have the proper name. + if (is_builtin) + set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction)); + + bool flatten_from_ib_var = false; + string flatten_from_ib_mbr_name; + + if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance) + { + // Also declare [[clip_distance]] attribute here. + uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); + ib_type.member_types.push_back(get_variable_data_type_id(var)); + set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + + flatten_from_ib_mbr_name = builtin_to_glsl(BuiltInClipDistance, StorageClassOutput); + set_member_name(ib_type.self, clip_array_mbr_idx, flatten_from_ib_mbr_name); + + // When we flatten, we flatten directly from the "out" struct, + // not from a function variable. + flatten_from_ib_var = true; + + if (!msl_options.enable_clip_distance_user_varying) + return; + } + else if (!meta.strip_array) + { + // Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped. + entry_func.add_local_variable(var.self); + // We need to declare the variable early and at entry-point scope. + vars_needing_early_declaration.push_back(var.self); + } + + for (uint32_t i = 0; i < elem_cnt; i++) + { + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + + uint32_t target_components = 0; + bool padded_output = false; + uint32_t type_id = usable_type->self; + + // Check if we need to pad fragment output to match a certain number of components. + if (get_decoration_bitset(var.self).get(DecorationLocation) && msl_options.pad_fragment_output_components && + get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput) + { + uint32_t locn = get_decoration(var.self, DecorationLocation) + i; + target_components = get_target_components_for_fragment_location(locn); + if (usable_type->vecsize < target_components) + { + // Make a new type here. + type_id = build_extended_vector_type(usable_type->self, target_components); + padded_output = true; + } + } + + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(get_pointee_type_id(type_id), is_noperspective)); + else + ib_type.member_types.push_back(get_pointee_type_id(type_id)); + + // Give the member a name + string mbr_name = ensure_valid_name(join(to_expression(var.self), "_", i), "m"); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // There is no qualified alias since we need to flatten the internal array on return. + if (get_decoration_bitset(var.self).get(DecorationLocation)) + { + uint32_t locn = get_decoration(var.self, DecorationLocation) + i; + if (storage == StorageClassInput) + { + var.basetype = ensure_correct_input_type(var.basetype, locn); + uint32_t mbr_type_id = ensure_correct_input_type(usable_type->self, locn); + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; + } + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, *usable_type, storage); + } + else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin)) + { + uint32_t locn = inputs_by_builtin[builtin].location + i; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, *usable_type, storage); + } + else if (is_builtin && builtin == BuiltInClipDistance) + { + // Declare the ClipDistance as [[user(clipN)]]. + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, i); + } + + if (get_decoration_bitset(var.self).get(DecorationIndex)) + { + uint32_t index = get_decoration(var.self, DecorationIndex); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index); + } + + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); + + // Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped. + if (!meta.strip_array) + { + switch (storage) + { + case StorageClassInput: + entry_func.fixup_hooks_in.push_back([=, &var]() { + if (pull_model_inputs.count(var.self)) + { + string lerp_call; + if (is_centroid) + lerp_call = ".interpolate_at_centroid()"; + else if (is_sample) + lerp_call = join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + lerp_call = ".interpolate_at_center()"; + statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, lerp_call, ";"); + } + else + { + statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";"); + } + }); + break; + + case StorageClassOutput: + entry_func.fixup_hooks_out.push_back([=, &var]() { + if (padded_output) + { + auto &padded_type = this->get(type_id); + statement( + ib_var_ref, ".", mbr_name, " = ", + remap_swizzle(padded_type, usable_type->vecsize, join(to_name(var.self), "[", i, "]")), + ";"); + } + else if (flatten_from_ib_var) + statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i, + "];"); + else + statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];"); + }); + break; + + default: + break; + } + } + } +} + +uint32_t CompilerMSL::get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) +{ + auto &type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var); + uint32_t location = get_decoration(var.self, DecorationLocation); + + for (uint32_t i = 0; i < mbr_idx; i++) + { + auto &mbr_type = get(type.member_types[i]); + + // Start counting from any place we have a new location decoration. + if (has_member_decoration(type.self, mbr_idx, DecorationLocation)) + location = get_member_decoration(type.self, mbr_idx, DecorationLocation); + + uint32_t location_count = 1; + + if (mbr_type.columns > 1) + location_count = mbr_type.columns; + + if (!mbr_type.array.empty()) + for (uint32_t j = 0; j < uint32_t(mbr_type.array.size()); j++) + location_count *= to_array_size_literal(mbr_type, j); + + location += location_count; + } + + return location; +} + +void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, + uint32_t mbr_idx, InterfaceBlockMeta &meta) +{ + auto &entry_func = get(ir.default_entry_point); + auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var); + + BuiltIn builtin = BuiltInMax; + bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin); + bool is_flat = + has_member_decoration(var_type.self, mbr_idx, DecorationFlat) || has_decoration(var.self, DecorationFlat); + bool is_noperspective = has_member_decoration(var_type.self, mbr_idx, DecorationNoPerspective) || + has_decoration(var.self, DecorationNoPerspective); + bool is_centroid = has_member_decoration(var_type.self, mbr_idx, DecorationCentroid) || + has_decoration(var.self, DecorationCentroid); + bool is_sample = + has_member_decoration(var_type.self, mbr_idx, DecorationSample) || has_decoration(var.self, DecorationSample); + + uint32_t mbr_type_id = var_type.member_types[mbr_idx]; + auto &mbr_type = get(mbr_type_id); + uint32_t elem_cnt = 0; + + if (is_matrix(mbr_type)) + { + if (is_array(mbr_type)) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables."); + + elem_cnt = mbr_type.columns; + } + else if (is_array(mbr_type)) + { + if (mbr_type.array.size() != 1) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables."); + + elem_cnt = to_array_size_literal(mbr_type); + } + + auto *usable_type = &mbr_type; + if (usable_type->pointer) + usable_type = &get(usable_type->parent_type); + while (is_array(*usable_type) || is_matrix(*usable_type)) + usable_type = &get(usable_type->parent_type); + + bool flatten_from_ib_var = false; + string flatten_from_ib_mbr_name; + + if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance) + { + // Also declare [[clip_distance]] attribute here. + uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); + ib_type.member_types.push_back(mbr_type_id); + set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + + flatten_from_ib_mbr_name = builtin_to_glsl(BuiltInClipDistance, StorageClassOutput); + set_member_name(ib_type.self, clip_array_mbr_idx, flatten_from_ib_mbr_name); + + // When we flatten, we flatten directly from the "out" struct, + // not from a function variable. + flatten_from_ib_var = true; + + if (!msl_options.enable_clip_distance_user_varying) + return; + } + + for (uint32_t i = 0; i < elem_cnt; i++) + { + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(usable_type->self, is_noperspective)); + else + ib_type.member_types.push_back(usable_type->self); + + // Give the member a name + string mbr_name = ensure_valid_name(join(to_qualified_member_name(var_type, mbr_idx), "_", i), "m"); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation)) + { + uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation) + i; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, *usable_type, storage); + } + else if (has_decoration(var.self, DecorationLocation)) + { + uint32_t locn = get_accumulated_member_location(var, mbr_idx, meta.strip_array) + i; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, *usable_type, storage); + } + else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin)) + { + uint32_t locn = inputs_by_builtin[builtin].location + i; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, *usable_type, storage); + } + else if (is_builtin && builtin == BuiltInClipDistance) + { + // Declare the ClipDistance as [[user(clipN)]]. + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, i); + } + + if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent)) + SPIRV_CROSS_THROW("DecorationComponent on matrices and arrays make little sense."); + + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx); + + // Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate. + if (!meta.strip_array) + { + switch (storage) + { + case StorageClassInput: + entry_func.fixup_hooks_in.push_back([=, &var, &var_type]() { + if (pull_model_inputs.count(var.self)) + { + string lerp_call; + if (is_centroid) + lerp_call = ".interpolate_at_centroid()"; + else if (is_sample) + lerp_call = join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + lerp_call = ".interpolate_at_center()"; + statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), "[", i, "] = ", ib_var_ref, + ".", mbr_name, lerp_call, ";"); + } + else + { + statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), "[", i, "] = ", ib_var_ref, + ".", mbr_name, ";"); + } + }); + break; + + case StorageClassOutput: + entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() { + if (flatten_from_ib_var) + { + statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i, + "];"); + } + else + { + statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), ".", + to_member_name(var_type, mbr_idx), "[", i, "];"); + } + }); + break; + + default: + break; + } + } + } +} + +void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, uint32_t mbr_idx, + InterfaceBlockMeta &meta) +{ + auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var); + auto &entry_func = get(ir.default_entry_point); + + BuiltIn builtin = BuiltInMax; + bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin); + bool is_flat = + has_member_decoration(var_type.self, mbr_idx, DecorationFlat) || has_decoration(var.self, DecorationFlat); + bool is_noperspective = has_member_decoration(var_type.self, mbr_idx, DecorationNoPerspective) || + has_decoration(var.self, DecorationNoPerspective); + bool is_centroid = has_member_decoration(var_type.self, mbr_idx, DecorationCentroid) || + has_decoration(var.self, DecorationCentroid); + bool is_sample = + has_member_decoration(var_type.self, mbr_idx, DecorationSample) || has_decoration(var.self, DecorationSample); + + // Add a reference to the member to the interface struct. + uint32_t mbr_type_id = var_type.member_types[mbr_idx]; + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + mbr_type_id = ensure_correct_builtin_type(mbr_type_id, builtin); + var_type.member_types[mbr_idx] = mbr_type_id; + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(mbr_type_id, is_noperspective)); + else + ib_type.member_types.push_back(mbr_type_id); + + // Give the member a name + string mbr_name = ensure_valid_name(to_qualified_member_name(var_type, mbr_idx), "m"); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // Update the original variable reference to include the structure reference + string qual_var_name = ib_var_ref + "." + mbr_name; + // If using pull-model interpolation, need to add a call to the correct interpolation method. + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + { + if (is_centroid) + qual_var_name += ".interpolate_at_centroid()"; + else if (is_sample) + qual_var_name += join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + qual_var_name += ".interpolate_at_center()"; + } + + if (is_builtin && !meta.strip_array) + { + // For the builtin gl_PerVertex, we cannot treat it as a block anyways, + // so redirect to qualified name. + set_member_qualified_name(var_type.self, mbr_idx, qual_var_name); + } + else if (!meta.strip_array) + { + // Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate. + switch (storage) + { + case StorageClassInput: + entry_func.fixup_hooks_in.push_back([=, &var, &var_type]() { + statement(to_name(var.self), ".", to_member_name(var_type, mbr_idx), " = ", qual_var_name, ";"); + }); + break; + + case StorageClassOutput: + entry_func.fixup_hooks_out.push_back([=, &var, &var_type]() { + statement(qual_var_name, " = ", to_name(var.self), ".", to_member_name(var_type, mbr_idx), ";"); + }); + break; + + default: + break; + } + } + + // Copy the variable location from the original variable to the member + if (has_member_decoration(var_type.self, mbr_idx, DecorationLocation)) + { + uint32_t locn = get_member_decoration(var_type.self, mbr_idx, DecorationLocation); + if (storage == StorageClassInput) + { + mbr_type_id = ensure_correct_input_type(mbr_type_id, locn); + var_type.member_types[mbr_idx] = mbr_type_id; + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; + } + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, get(mbr_type_id), storage); + } + else if (has_decoration(var.self, DecorationLocation)) + { + // The block itself might have a location and in this case, all members of the block + // receive incrementing locations. + uint32_t locn = get_accumulated_member_location(var, mbr_idx, meta.strip_array); + if (storage == StorageClassInput) + { + mbr_type_id = ensure_correct_input_type(mbr_type_id, locn); + var_type.member_types[mbr_idx] = mbr_type_id; + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; + } + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, get(mbr_type_id), storage); + } + else if (is_builtin && is_tessellation_shader() && inputs_by_builtin.count(builtin)) + { + uint32_t locn = 0; + auto builtin_itr = inputs_by_builtin.find(builtin); + if (builtin_itr != end(inputs_by_builtin)) + locn = builtin_itr->second.location; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, get(mbr_type_id), storage); + } + + // Copy the component location, if present. + if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent)) + { + uint32_t comp = get_member_decoration(var_type.self, mbr_idx, DecorationComponent); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp); + } + + // Mark the member as builtin if needed + if (is_builtin) + { + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin); + if (builtin == BuiltInPosition && storage == StorageClassOutput) + qual_pos_var_name = qual_var_name; + } + + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, mbr_idx); +} + +// In Metal, the tessellation levels are stored as tightly packed half-precision floating point values. +// But, stage-in attribute offsets and strides must be multiples of four, so we can't pass the levels +// individually. Therefore, we must pass them as vectors. Triangles get a single float4, with the outer +// levels in 'xyz' and the inner level in 'w'. Quads get a float4 containing the outer levels and a +// float2 containing the inner levels. +void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var) +{ + auto &entry_func = get(ir.default_entry_point); + auto &var_type = get_variable_element_type(var); + + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + + // Force the variable to have the proper name. + set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction)); + + if (get_entry_point().flags.get(ExecutionModeTriangles)) + { + // Triangles are tricky, because we want only one member in the struct. + + // We need to declare the variable early and at entry-point scope. + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + + string mbr_name = "gl_TessLevel"; + + // If we already added the other one, we can skip this step. + if (!added_builtin_tess_level) + { + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + + uint32_t type_id = build_extended_vector_type(var_type.self, 4); + + ib_type.member_types.push_back(type_id); + + // Give the member a name + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // There is no qualified alias since we need to flatten the internal array on return. + if (get_decoration_bitset(var.self).get(DecorationLocation)) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, var_type, StorageClassInput); + } + else if (inputs_by_builtin.count(builtin)) + { + uint32_t locn = inputs_by_builtin[builtin].location; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, var_type, StorageClassInput); + } + + added_builtin_tess_level = true; + } + + switch (builtin) + { + case BuiltInTessLevelOuter: + entry_func.fixup_hooks_in.push_back([=, &var]() { + statement(to_name(var.self), "[0] = ", ib_var_ref, ".", mbr_name, ".x;"); + statement(to_name(var.self), "[1] = ", ib_var_ref, ".", mbr_name, ".y;"); + statement(to_name(var.self), "[2] = ", ib_var_ref, ".", mbr_name, ".z;"); + }); + break; + + case BuiltInTessLevelInner: + entry_func.fixup_hooks_in.push_back( + [=, &var]() { statement(to_name(var.self), "[0] = ", ib_var_ref, ".", mbr_name, ".w;"); }); + break; + + default: + assert(false); + break; + } + } + else + { + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + + uint32_t type_id = build_extended_vector_type(var_type.self, builtin == BuiltInTessLevelOuter ? 4 : 2); + // Change the type of the variable, too. + uint32_t ptr_type_id = ir.increase_bound_by(1); + auto &new_var_type = set(ptr_type_id, get(type_id)); + new_var_type.pointer = true; + new_var_type.storage = StorageClassInput; + new_var_type.parent_type = type_id; + var.basetype = ptr_type_id; + + ib_type.member_types.push_back(type_id); + + // Give the member a name + string mbr_name = to_expression(var.self); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // Since vectors can be indexed like arrays, there is no need to unpack this. We can + // just refer to the vector directly. So give it a qualified alias. + string qual_var_name = ib_var_ref + "." + mbr_name; + ir.meta[var.self].decoration.qualified_alias = qual_var_name; + + if (get_decoration_bitset(var.self).get(DecorationLocation)) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, new_var_type, StorageClassInput); + } + else if (inputs_by_builtin.count(builtin)) + { + uint32_t locn = inputs_by_builtin[builtin].location; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, new_var_type, StorageClassInput); + } + } +} + +void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, InterfaceBlockMeta &meta) +{ + auto &entry_func = get(ir.default_entry_point); + // Tessellation control I/O variables and tessellation evaluation per-point inputs are + // usually declared as arrays. In these cases, we want to add the element type to the + // interface block, since in Metal it's the interface block itself which is arrayed. + auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var); + bool is_builtin = is_builtin_variable(var); + auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + + if (var_type.basetype == SPIRType::Struct) + { + if (!is_builtin_type(var_type) && (!capture_output_to_buffer || storage == StorageClassInput) && + !meta.strip_array) + { + // For I/O blocks or structs, we will need to pass the block itself around + // to functions if they are used globally in leaf functions. + // Rather than passing down member by member, + // we unflatten I/O blocks while running the shader, + // and pass the actual struct type down to leaf functions. + // We then unflatten inputs, and flatten outputs in the "fixup" stages. + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + } + + if (capture_output_to_buffer && storage != StorageClassInput && !has_decoration(var_type.self, DecorationBlock)) + { + // In Metal tessellation shaders, the interface block itself is arrayed. This makes things + // very complicated, since stage-in structures in MSL don't support nested structures. + // Luckily, for stage-out when capturing output, we can avoid this and just add + // composite members directly, because the stage-out structure is stored to a buffer, + // not returned. + add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta); + } + else + { + // Flatten the struct members into the interface struct + for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(var_type.member_types.size()); mbr_idx++) + { + builtin = BuiltInMax; + is_builtin = is_member_builtin(var_type, mbr_idx, &builtin); + auto &mbr_type = get(var_type.member_types[mbr_idx]); + + if (!is_builtin || has_active_builtin(builtin, storage)) + { + bool is_composite_type = is_matrix(mbr_type) || is_array(mbr_type); + bool attribute_load_store = + storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; + bool storage_is_stage_io = + (storage == StorageClassInput && !(get_execution_model() == ExecutionModelTessellationControl && + msl_options.multi_patch_workgroup)) || + storage == StorageClassOutput; + + // ClipDistance always needs to be declared as user attributes. + if (builtin == BuiltInClipDistance) + is_builtin = false; + + if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type) + { + add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx, + meta); + } + else + { + add_plain_member_variable_to_interface_block(storage, ib_var_ref, ib_type, var, mbr_idx, meta); + } + } + } + } + } + else if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput && + !meta.strip_array && is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner)) + { + add_tess_level_input_to_interface_block(ib_var_ref, ib_type, var); + } + else if (var_type.basetype == SPIRType::Boolean || var_type.basetype == SPIRType::Char || + type_is_integral(var_type) || type_is_floating_point(var_type) || var_type.basetype == SPIRType::Boolean) + { + if (!is_builtin || has_active_builtin(builtin, storage)) + { + bool is_composite_type = is_matrix(var_type) || is_array(var_type); + bool storage_is_stage_io = + (storage == StorageClassInput && + !(get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup)) || + (storage == StorageClassOutput && !capture_output_to_buffer); + bool attribute_load_store = storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; + + // ClipDistance always needs to be declared as user attributes. + if (builtin == BuiltInClipDistance) + is_builtin = false; + + // MSL does not allow matrices or arrays in input or output variables, so need to handle it specially. + if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type) + { + add_composite_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta); + } + else + { + add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta); + } + } + } +} + +// Fix up the mapping of variables to interface member indices, which is used to compile access chains +// for per-vertex variables in a tessellation control shader. +void CompilerMSL::fix_up_interface_member_indices(StorageClass storage, uint32_t ib_type_id) +{ + // Only needed for tessellation shaders and pull-model interpolants. + // Need to redirect interface indices back to variables themselves. + // For structs, each member of the struct need a separate instance. + if (get_execution_model() != ExecutionModelTessellationControl && + !(get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput) && + !(get_execution_model() == ExecutionModelFragment && storage == StorageClassInput && + !pull_model_inputs.empty())) + return; + + auto mbr_cnt = uint32_t(ir.meta[ib_type_id].members.size()); + for (uint32_t i = 0; i < mbr_cnt; i++) + { + uint32_t var_id = get_extended_member_decoration(ib_type_id, i, SPIRVCrossDecorationInterfaceOrigID); + if (!var_id) + continue; + auto &var = get(var_id); + + auto &type = get_variable_element_type(var); + if (storage == StorageClassInput && type.basetype == SPIRType::Struct) + { + uint32_t mbr_idx = get_extended_member_decoration(ib_type_id, i, SPIRVCrossDecorationInterfaceMemberIndex); + + // Only set the lowest InterfaceMemberIndex for each variable member. + // IB struct members will be emitted in-order w.r.t. interface member index. + if (!has_extended_member_decoration(var_id, mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex)) + set_extended_member_decoration(var_id, mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, i); + } + else + { + // Only set the lowest InterfaceMemberIndex for each variable. + // IB struct members will be emitted in-order w.r.t. interface member index. + if (!has_extended_decoration(var_id, SPIRVCrossDecorationInterfaceMemberIndex)) + set_extended_decoration(var_id, SPIRVCrossDecorationInterfaceMemberIndex, i); + } + } +} + +// Add an interface structure for the type of storage, which is either StorageClassInput or StorageClassOutput. +// Returns the ID of the newly added variable, or zero if no variable was added. +uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) +{ + // Accumulate the variables that should appear in the interface struct. + SmallVector vars; + bool incl_builtins = storage == StorageClassOutput || is_tessellation_shader(); + bool has_seen_barycentric = false; + + InterfaceBlockMeta meta; + + // Varying interfaces between stages which use "user()" attribute can be dealt with + // without explicit packing and unpacking of components. For any variables which link against the runtime + // in some way (vertex attributes, fragment output, etc), we'll need to deal with it somehow. + bool pack_components = + (storage == StorageClassInput && get_execution_model() == ExecutionModelVertex) || + (storage == StorageClassOutput && get_execution_model() == ExecutionModelFragment) || + (storage == StorageClassOutput && get_execution_model() == ExecutionModelVertex && capture_output_to_buffer); + + ir.for_each_typed_id([&](uint32_t var_id, SPIRVariable &var) { + if (var.storage != storage) + return; + + auto &type = this->get(var.basetype); + + bool is_builtin = is_builtin_variable(var); + auto bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn)); + uint32_t location = get_decoration(var_id, DecorationLocation); + + // These builtins are part of the stage in/out structs. + bool is_interface_block_builtin = + (bi_type == BuiltInPosition || bi_type == BuiltInPointSize || bi_type == BuiltInClipDistance || + bi_type == BuiltInCullDistance || bi_type == BuiltInLayer || bi_type == BuiltInViewportIndex || + bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV || bi_type == BuiltInFragDepth || + bi_type == BuiltInFragStencilRefEXT || bi_type == BuiltInSampleMask) || + (get_execution_model() == ExecutionModelTessellationEvaluation && + (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner)); + + bool is_active = interface_variable_exists_in_entry_point(var.self); + if (is_builtin && is_active) + { + // Only emit the builtin if it's active in this entry point. Interface variable list might lie. + is_active = has_active_builtin(bi_type, storage); + } + + bool filter_patch_decoration = (has_decoration(var_id, DecorationPatch) || is_patch_block(type)) == patch; + + bool hidden = is_hidden_variable(var, incl_builtins); + + // ClipDistance is never hidden, we need to emulate it when used as an input. + if (bi_type == BuiltInClipDistance) + hidden = false; + + // It's not enough to simply avoid marking fragment outputs if the pipeline won't + // accept them. We can't put them in the struct at all, or otherwise the compiler + // complains that the outputs weren't explicitly marked. + if (get_execution_model() == ExecutionModelFragment && storage == StorageClassOutput && !patch && + ((is_builtin && ((bi_type == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) || + (bi_type == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin))) || + (!is_builtin && !(msl_options.enable_frag_output_mask & (1 << location))))) + { + hidden = true; + disabled_frag_outputs.push_back(var_id); + // If a builtin, force it to have the proper name. + if (is_builtin) + set_name(var_id, builtin_to_glsl(bi_type, StorageClassFunction)); + } + + // Barycentric inputs must be emitted in stage-in, because they can have interpolation arguments. + if (is_active && (bi_type == BuiltInBaryCoordNV || bi_type == BuiltInBaryCoordNoPerspNV)) + { + if (has_seen_barycentric) + SPIRV_CROSS_THROW("Cannot declare both BaryCoordNV and BaryCoordNoPerspNV in same shader in MSL."); + has_seen_barycentric = true; + hidden = false; + } + + if (is_active && !hidden && type.pointer && filter_patch_decoration && + (!is_builtin || is_interface_block_builtin)) + { + vars.push_back(&var); + + if (!is_builtin) + { + // Need to deal specially with DecorationComponent. + // Multiple variables can alias the same Location, and try to make sure each location is declared only once. + // We will swizzle data in and out to make this work. + // We only need to consider plain variables here, not composites. + // This is only relevant for vertex inputs and fragment outputs. + // Technically tessellation as well, but it is too complicated to support. + uint32_t component = get_decoration(var_id, DecorationComponent); + if (component != 0) + { + if (is_tessellation_shader()) + SPIRV_CROSS_THROW("Component decoration is not supported in tessellation shaders."); + else if (pack_components) + { + auto &location_meta = meta.location_meta[location]; + location_meta.num_components = std::max(location_meta.num_components, component + type.vecsize); + } + } + } + } + }); + + // If no variables qualify, leave. + // For patch input in a tessellation evaluation shader, the per-vertex stage inputs + // are included in a special patch control point array. + if (vars.empty() && !(storage == StorageClassInput && patch && stage_in_var_id)) + return 0; + + // Add a new typed variable for this interface structure. + // The initializer expression is allocated here, but populated when the function + // declaraion is emitted, because it is cleared after each compilation pass. + uint32_t next_id = ir.increase_bound_by(3); + uint32_t ib_type_id = next_id++; + auto &ib_type = set(ib_type_id); + ib_type.basetype = SPIRType::Struct; + ib_type.storage = storage; + set_decoration(ib_type_id, DecorationBlock); + + uint32_t ib_var_id = next_id++; + auto &var = set(ib_var_id, ib_type_id, storage, 0); + var.initializer = next_id++; + + string ib_var_ref; + auto &entry_func = get(ir.default_entry_point); + switch (storage) + { + case StorageClassInput: + ib_var_ref = patch ? patch_stage_in_var_name : stage_in_var_name; + if (get_execution_model() == ExecutionModelTessellationControl) + { + // Add a hook to populate the shared workgroup memory containing the gl_in array. + entry_func.fixup_hooks_in.push_back([=]() { + // Can't use PatchVertices, PrimitiveId, or InvocationId yet; the hooks for those may not have run yet. + if (msl_options.multi_patch_workgroup) + { + // n.b. builtin_invocation_id_id here is the dispatch global invocation ID, + // not the TC invocation ID. + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_in = &", + input_buffer_var_name, "[min(", to_expression(builtin_invocation_id_id), ".x / ", + get_entry_point().output_vertices, + ", spvIndirectParams[1] - 1) * spvIndirectParams[0]];"); + } + else + { + // It's safe to use InvocationId here because it's directly mapped to a + // Metal builtin, and therefore doesn't need a hook. + statement("if (", to_expression(builtin_invocation_id_id), " < spvIndirectParams[0])"); + statement(" ", input_wg_var_name, "[", to_expression(builtin_invocation_id_id), + "] = ", ib_var_ref, ";"); + statement("threadgroup_barrier(mem_flags::mem_threadgroup);"); + statement("if (", to_expression(builtin_invocation_id_id), + " >= ", get_entry_point().output_vertices, ")"); + statement(" return;"); + } + }); + } + break; + + case StorageClassOutput: + { + ib_var_ref = patch ? patch_stage_out_var_name : stage_out_var_name; + + // Add the output interface struct as a local variable to the entry function. + // If the entry point should return the output struct, set the entry function + // to return the output interface struct, otherwise to return nothing. + // Indicate the output var requires early initialization. + bool ep_should_return_output = !get_is_rasterization_disabled(); + uint32_t rtn_id = ep_should_return_output ? ib_var_id : 0; + if (!capture_output_to_buffer) + { + entry_func.add_local_variable(ib_var_id); + for (auto &blk_id : entry_func.blocks) + { + auto &blk = get(blk_id); + if (blk.terminator == SPIRBlock::Return) + blk.return_value = rtn_id; + } + vars_needing_early_declaration.push_back(ib_var_id); + } + else + { + switch (get_execution_model()) + { + case ExecutionModelVertex: + case ExecutionModelTessellationEvaluation: + // Instead of declaring a struct variable to hold the output and then + // copying that to the output buffer, we'll declare the output variable + // as a reference to the final output element in the buffer. Then we can + // avoid the extra copy. + entry_func.fixup_hooks_in.push_back([=]() { + if (stage_out_var_id) + { + // The first member of the indirect buffer is always the number of vertices + // to draw. + // We zero-base the InstanceID & VertexID variables for HLSL emulation elsewhere, so don't do it twice + if (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation) + { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", output_buffer_var_name, "[", to_expression(builtin_invocation_id_id), + ".y * ", to_expression(builtin_stage_input_size_id), ".x + ", + to_expression(builtin_invocation_id_id), ".x];"); + } + else if (msl_options.enable_base_index_zero) + { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", output_buffer_var_name, "[", to_expression(builtin_instance_idx_id), + " * spvIndirectParams[0] + ", to_expression(builtin_vertex_idx_id), "];"); + } + else + { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", output_buffer_var_name, "[(", to_expression(builtin_instance_idx_id), + " - ", to_expression(builtin_base_instance_id), ") * spvIndirectParams[0] + ", + to_expression(builtin_vertex_idx_id), " - ", + to_expression(builtin_base_vertex_id), "];"); + } + } + }); + break; + case ExecutionModelTessellationControl: + if (msl_options.multi_patch_workgroup) + { + // We cannot use PrimitiveId here, because the hook may not have run yet. + if (patch) + { + entry_func.fixup_hooks_in.push_back([=]() { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", patch_output_buffer_var_name, "[", to_expression(builtin_invocation_id_id), + ".x / ", get_entry_point().output_vertices, "];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=]() { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_out = &", + output_buffer_var_name, "[", to_expression(builtin_invocation_id_id), ".x - ", + to_expression(builtin_invocation_id_id), ".x % ", + get_entry_point().output_vertices, "];"); + }); + } + } + else + { + if (patch) + { + entry_func.fixup_hooks_in.push_back([=]() { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", patch_output_buffer_var_name, "[", to_expression(builtin_primitive_id_id), + "];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=]() { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_out = &", + output_buffer_var_name, "[", to_expression(builtin_primitive_id_id), " * ", + get_entry_point().output_vertices, "];"); + }); + } + } + break; + default: + break; + } + } + break; + } + + default: + break; + } + + set_name(ib_type_id, to_name(ir.default_entry_point) + "_" + ib_var_ref); + set_name(ib_var_id, ib_var_ref); + + for (auto *p_var : vars) + { + bool strip_array = + (get_execution_model() == ExecutionModelTessellationControl || + (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) && + !patch; + + meta.strip_array = strip_array; + add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var, meta); + } + + if (get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup && + storage == StorageClassInput) + { + // For tessellation control inputs, add all outputs from the vertex shader to ensure + // the struct containing them is the correct size and layout. + for (auto &input : inputs_by_location) + { + if (is_msl_shader_input_used(input.first)) + continue; + + // Create a fake variable to put at the location. + uint32_t offset = ir.increase_bound_by(4); + uint32_t type_id = offset; + uint32_t array_type_id = offset + 1; + uint32_t ptr_type_id = offset + 2; + uint32_t var_id = offset + 3; + + SPIRType type; + switch (input.second.format) + { + case MSL_SHADER_INPUT_FORMAT_UINT16: + case MSL_SHADER_INPUT_FORMAT_ANY16: + type.basetype = SPIRType::UShort; + type.width = 16; + break; + case MSL_SHADER_INPUT_FORMAT_ANY32: + default: + type.basetype = SPIRType::UInt; + type.width = 32; + break; + } + type.vecsize = input.second.vecsize; + set(type_id, type); + + type.array.push_back(0); + type.array_size_literal.push_back(true); + type.parent_type = type_id; + set(array_type_id, type); + + type.pointer = true; + type.parent_type = array_type_id; + type.storage = storage; + auto &ptr_type = set(ptr_type_id, type); + ptr_type.self = array_type_id; + + auto &fake_var = set(var_id, ptr_type_id, storage); + set_decoration(var_id, DecorationLocation, input.first); + meta.strip_array = true; + add_variable_to_interface_block(storage, ib_var_ref, ib_type, fake_var, meta); + } + } + + // Sort the members of the structure by their locations. + MemberSorter member_sorter(ib_type, ir.meta[ib_type_id], MemberSorter::Location); + member_sorter.sort(); + + // The member indices were saved to the original variables, but after the members + // were sorted, those indices are now likely incorrect. Fix those up now. + if (!patch) + fix_up_interface_member_indices(storage, ib_type_id); + + // For patch inputs, add one more member, holding the array of control point data. + if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput && patch && + stage_in_var_id) + { + uint32_t pcp_type_id = ir.increase_bound_by(1); + auto &pcp_type = set(pcp_type_id, ib_type); + pcp_type.basetype = SPIRType::ControlPointArray; + pcp_type.parent_type = pcp_type.type_alias = get_stage_in_struct_type().self; + pcp_type.storage = storage; + ir.meta[pcp_type_id] = ir.meta[ib_type.self]; + uint32_t mbr_idx = uint32_t(ib_type.member_types.size()); + ib_type.member_types.push_back(pcp_type_id); + set_member_name(ib_type.self, mbr_idx, "gl_in"); + } + + return ib_var_id; +} + +uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageClass storage) +{ + if (!ib_var_id) + return 0; + + uint32_t ib_ptr_var_id; + uint32_t next_id = ir.increase_bound_by(3); + auto &ib_type = expression_type(ib_var_id); + if (get_execution_model() == ExecutionModelTessellationControl) + { + // Tessellation control per-vertex I/O is presented as an array, so we must + // do the same with our struct here. + uint32_t ib_ptr_type_id = next_id++; + auto &ib_ptr_type = set(ib_ptr_type_id, ib_type); + ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self; + ib_ptr_type.pointer = true; + ib_ptr_type.storage = + storage == StorageClassInput ? + (msl_options.multi_patch_workgroup ? StorageClassStorageBuffer : StorageClassWorkgroup) : + StorageClassStorageBuffer; + ir.meta[ib_ptr_type_id] = ir.meta[ib_type.self]; + // To ensure that get_variable_data_type() doesn't strip off the pointer, + // which we need, use another pointer. + uint32_t ib_ptr_ptr_type_id = next_id++; + auto &ib_ptr_ptr_type = set(ib_ptr_ptr_type_id, ib_ptr_type); + ib_ptr_ptr_type.parent_type = ib_ptr_type_id; + ib_ptr_ptr_type.type_alias = ib_type.self; + ib_ptr_ptr_type.storage = StorageClassFunction; + ir.meta[ib_ptr_ptr_type_id] = ir.meta[ib_type.self]; + + ib_ptr_var_id = next_id; + set(ib_ptr_var_id, ib_ptr_ptr_type_id, StorageClassFunction, 0); + set_name(ib_ptr_var_id, storage == StorageClassInput ? "gl_in" : "gl_out"); + } + else + { + // Tessellation evaluation per-vertex inputs are also presented as arrays. + // But, in Metal, this array uses a very special type, 'patch_control_point', + // which is a container that can be used to access the control point data. + // To represent this, a special 'ControlPointArray' type has been added to the + // SPIRV-Cross type system. It should only be generated by and seen in the MSL + // backend (i.e. this one). + uint32_t pcp_type_id = next_id++; + auto &pcp_type = set(pcp_type_id, ib_type); + pcp_type.basetype = SPIRType::ControlPointArray; + pcp_type.parent_type = pcp_type.type_alias = ib_type.self; + pcp_type.storage = storage; + ir.meta[pcp_type_id] = ir.meta[ib_type.self]; + + ib_ptr_var_id = next_id; + set(ib_ptr_var_id, pcp_type_id, storage, 0); + set_name(ib_ptr_var_id, "gl_in"); + ir.meta[ib_ptr_var_id].decoration.qualified_alias = join(patch_stage_in_var_name, ".gl_in"); + } + return ib_ptr_var_id; +} + +// Ensure that the type is compatible with the builtin. +// If it is, simply return the given type ID. +// Otherwise, create a new type, and return it's ID. +uint32_t CompilerMSL::ensure_correct_builtin_type(uint32_t type_id, BuiltIn builtin) +{ + auto &type = get(type_id); + + if ((builtin == BuiltInSampleMask && is_array(type)) || + ((builtin == BuiltInLayer || builtin == BuiltInViewportIndex || builtin == BuiltInFragStencilRefEXT) && + type.basetype != SPIRType::UInt)) + { + uint32_t next_id = ir.increase_bound_by(type.pointer ? 2 : 1); + uint32_t base_type_id = next_id++; + auto &base_type = set(base_type_id); + base_type.basetype = SPIRType::UInt; + base_type.width = 32; + + if (!type.pointer) + return base_type_id; + + uint32_t ptr_type_id = next_id++; + auto &ptr_type = set(ptr_type_id); + ptr_type = base_type; + ptr_type.pointer = true; + ptr_type.storage = type.storage; + ptr_type.parent_type = base_type_id; + return ptr_type_id; + } + + return type_id; +} + +// Ensure that the type is compatible with the shader input. +// If it is, simply return the given type ID. +// Otherwise, create a new type, and return its ID. +uint32_t CompilerMSL::ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t num_components) +{ + auto &type = get(type_id); + + auto p_va = inputs_by_location.find(location); + if (p_va == end(inputs_by_location)) + { + if (num_components > type.vecsize) + return build_extended_vector_type(type_id, num_components); + else + return type_id; + } + + if (num_components == 0) + num_components = p_va->second.vecsize; + + switch (p_va->second.format) + { + case MSL_SHADER_INPUT_FORMAT_UINT8: + { + switch (type.basetype) + { + case SPIRType::UByte: + case SPIRType::UShort: + case SPIRType::UInt: + if (num_components > type.vecsize) + return build_extended_vector_type(type_id, num_components); + else + return type_id; + + case SPIRType::Short: + return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize, + SPIRType::UShort); + case SPIRType::Int: + return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize, + SPIRType::UInt); + + default: + SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader"); + } + } + + case MSL_SHADER_INPUT_FORMAT_UINT16: + { + switch (type.basetype) + { + case SPIRType::UShort: + case SPIRType::UInt: + if (num_components > type.vecsize) + return build_extended_vector_type(type_id, num_components); + else + return type_id; + + case SPIRType::Int: + return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize, + SPIRType::UInt); + + default: + SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader"); + } + } + + default: + if (num_components > type.vecsize) + type_id = build_extended_vector_type(type_id, num_components); + break; + } + + return type_id; +} + +void CompilerMSL::mark_struct_members_packed(const SPIRType &type) +{ + set_extended_decoration(type.self, SPIRVCrossDecorationPhysicalTypePacked); + + // Problem case! Struct needs to be placed at an awkward alignment. + // Mark every member of the child struct as packed. + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < mbr_cnt; i++) + { + auto &mbr_type = get(type.member_types[i]); + if (mbr_type.basetype == SPIRType::Struct) + { + // Recursively mark structs as packed. + auto *struct_type = &mbr_type; + while (!struct_type->array.empty()) + struct_type = &get(struct_type->parent_type); + mark_struct_members_packed(*struct_type); + } + else if (!is_scalar(mbr_type)) + set_extended_member_decoration(type.self, i, SPIRVCrossDecorationPhysicalTypePacked); + } +} + +void CompilerMSL::mark_scalar_layout_structs(const SPIRType &type) +{ + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < mbr_cnt; i++) + { + auto &mbr_type = get(type.member_types[i]); + if (mbr_type.basetype == SPIRType::Struct) + { + auto *struct_type = &mbr_type; + while (!struct_type->array.empty()) + struct_type = &get(struct_type->parent_type); + + if (has_extended_decoration(struct_type->self, SPIRVCrossDecorationPhysicalTypePacked)) + continue; + + uint32_t msl_alignment = get_declared_struct_member_alignment_msl(type, i); + uint32_t msl_size = get_declared_struct_member_size_msl(type, i); + uint32_t spirv_offset = type_struct_member_offset(type, i); + uint32_t spirv_offset_next; + if (i + 1 < mbr_cnt) + spirv_offset_next = type_struct_member_offset(type, i + 1); + else + spirv_offset_next = spirv_offset + msl_size; + + // Both are complicated cases. In scalar layout, a struct of float3 might just consume 12 bytes, + // and the next member will be placed at offset 12. + bool struct_is_misaligned = (spirv_offset % msl_alignment) != 0; + bool struct_is_too_large = spirv_offset + msl_size > spirv_offset_next; + uint32_t array_stride = 0; + bool struct_needs_explicit_padding = false; + + // Verify that if a struct is used as an array that ArrayStride matches the effective size of the struct. + if (!mbr_type.array.empty()) + { + array_stride = type_struct_member_array_stride(type, i); + uint32_t dimensions = uint32_t(mbr_type.array.size() - 1); + for (uint32_t dim = 0; dim < dimensions; dim++) + { + uint32_t array_size = to_array_size_literal(mbr_type, dim); + array_stride /= max(array_size, 1u); + } + + // Set expected struct size based on ArrayStride. + struct_needs_explicit_padding = true; + + // If struct size is larger than array stride, we might be able to fit, if we tightly pack. + if (get_declared_struct_size_msl(*struct_type) > array_stride) + struct_is_too_large = true; + } + + if (struct_is_misaligned || struct_is_too_large) + mark_struct_members_packed(*struct_type); + mark_scalar_layout_structs(*struct_type); + + if (struct_needs_explicit_padding) + { + msl_size = get_declared_struct_size_msl(*struct_type, true, true); + if (array_stride < msl_size) + { + SPIRV_CROSS_THROW("Cannot express an array stride smaller than size of struct type."); + } + else + { + if (has_extended_decoration(struct_type->self, SPIRVCrossDecorationPaddingTarget)) + { + if (array_stride != + get_extended_decoration(struct_type->self, SPIRVCrossDecorationPaddingTarget)) + SPIRV_CROSS_THROW( + "A struct is used with different array strides. Cannot express this in MSL."); + } + else + set_extended_decoration(struct_type->self, SPIRVCrossDecorationPaddingTarget, array_stride); + } + } + } + } +} + +// Sort the members of the struct type by offset, and pack and then pad members where needed +// to align MSL members with SPIR-V offsets. The struct members are iterated twice. Packing +// occurs first, followed by padding, because packing a member reduces both its size and its +// natural alignment, possibly requiring a padding member to be added ahead of it. +void CompilerMSL::align_struct(SPIRType &ib_type, unordered_set &aligned_structs) +{ + // We align structs recursively, so stop any redundant work. + ID &ib_type_id = ib_type.self; + if (aligned_structs.count(ib_type_id)) + return; + aligned_structs.insert(ib_type_id); + + // Sort the members of the interface structure by their offset. + // They should already be sorted per SPIR-V spec anyway. + MemberSorter member_sorter(ib_type, ir.meta[ib_type_id], MemberSorter::Offset); + member_sorter.sort(); + + auto mbr_cnt = uint32_t(ib_type.member_types.size()); + + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + // Pack any dependent struct types before we pack a parent struct. + auto &mbr_type = get(ib_type.member_types[mbr_idx]); + if (mbr_type.basetype == SPIRType::Struct) + align_struct(mbr_type, aligned_structs); + } + + // Test the alignment of each member, and if a member should be closer to the previous + // member than the default spacing expects, it is likely that the previous member is in + // a packed format. If so, and the previous member is packable, pack it. + // For example ... this applies to any 3-element vector that is followed by a scalar. + uint32_t msl_offset = 0; + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + // This checks the member in isolation, if the member needs some kind of type remapping to conform to SPIR-V + // offsets, array strides and matrix strides. + ensure_member_packing_rules_msl(ib_type, mbr_idx); + + // Align current offset to the current member's default alignment. If the member was packed, it will observe + // the updated alignment here. + uint32_t msl_align_mask = get_declared_struct_member_alignment_msl(ib_type, mbr_idx) - 1; + uint32_t aligned_msl_offset = (msl_offset + msl_align_mask) & ~msl_align_mask; + + // Fetch the member offset as declared in the SPIRV. + uint32_t spirv_mbr_offset = get_member_decoration(ib_type_id, mbr_idx, DecorationOffset); + if (spirv_mbr_offset > aligned_msl_offset) + { + // Since MSL and SPIR-V have slightly different struct member alignment and + // size rules, we'll pad to standard C-packing rules with a char[] array. If the member is farther + // away than C-packing, expects, add an inert padding member before the the member. + uint32_t padding_bytes = spirv_mbr_offset - aligned_msl_offset; + set_extended_member_decoration(ib_type_id, mbr_idx, SPIRVCrossDecorationPaddingTarget, padding_bytes); + + // Re-align as a sanity check that aligning post-padding matches up. + msl_offset += padding_bytes; + aligned_msl_offset = (msl_offset + msl_align_mask) & ~msl_align_mask; + } + else if (spirv_mbr_offset < aligned_msl_offset) + { + // This should not happen, but deal with unexpected scenarios. + // It *might* happen if a sub-struct has a larger alignment requirement in MSL than SPIR-V. + SPIRV_CROSS_THROW("Cannot represent buffer block correctly in MSL."); + } + + assert(aligned_msl_offset == spirv_mbr_offset); + + // Increment the current offset to be positioned immediately after the current member. + // Don't do this for the last member since it can be unsized, and it is not relevant for padding purposes here. + if (mbr_idx + 1 < mbr_cnt) + msl_offset = aligned_msl_offset + get_declared_struct_member_size_msl(ib_type, mbr_idx); + } +} + +bool CompilerMSL::validate_member_packing_rules_msl(const SPIRType &type, uint32_t index) const +{ + auto &mbr_type = get(type.member_types[index]); + uint32_t spirv_offset = get_member_decoration(type.self, index, DecorationOffset); + + if (index + 1 < type.member_types.size()) + { + // First, we will check offsets. If SPIR-V offset + MSL size > SPIR-V offset of next member, + // we *must* perform some kind of remapping, no way getting around it. + // We can always pad after this member if necessary, so that case is fine. + uint32_t spirv_offset_next = get_member_decoration(type.self, index + 1, DecorationOffset); + assert(spirv_offset_next >= spirv_offset); + uint32_t maximum_size = spirv_offset_next - spirv_offset; + uint32_t msl_mbr_size = get_declared_struct_member_size_msl(type, index); + if (msl_mbr_size > maximum_size) + return false; + } + + if (!mbr_type.array.empty()) + { + // If we have an array type, array stride must match exactly with SPIR-V. + + // An exception to this requirement is if we have one array element. + // This comes from DX scalar layout workaround. + // If app tries to be cheeky and access the member out of bounds, this will not work, but this is the best we can do. + // In OpAccessChain with logical memory models, access chains must be in-bounds in SPIR-V specification. + bool relax_array_stride = mbr_type.array.back() == 1 && mbr_type.array_size_literal.back(); + + if (!relax_array_stride) + { + uint32_t spirv_array_stride = type_struct_member_array_stride(type, index); + uint32_t msl_array_stride = get_declared_struct_member_array_stride_msl(type, index); + if (spirv_array_stride != msl_array_stride) + return false; + } + } + + if (is_matrix(mbr_type)) + { + // Need to check MatrixStride as well. + uint32_t spirv_matrix_stride = type_struct_member_matrix_stride(type, index); + uint32_t msl_matrix_stride = get_declared_struct_member_matrix_stride_msl(type, index); + if (spirv_matrix_stride != msl_matrix_stride) + return false; + } + + // Now, we check alignment. + uint32_t msl_alignment = get_declared_struct_member_alignment_msl(type, index); + if ((spirv_offset % msl_alignment) != 0) + return false; + + // We're in the clear. + return true; +} + +// Here we need to verify that the member type we declare conforms to Offset, ArrayStride or MatrixStride restrictions. +// If there is a mismatch, we need to emit remapped types, either normal types, or "packed_X" types. +// In odd cases we need to emit packed and remapped types, for e.g. weird matrices or arrays with weird array strides. +void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t index) +{ + if (validate_member_packing_rules_msl(ib_type, index)) + return; + + // We failed validation. + // This case will be nightmare-ish to deal with. This could possibly happen if struct alignment does not quite + // match up with what we want. Scalar block layout comes to mind here where we might have to work around the rule + // that struct alignment == max alignment of all members and struct size depends on this alignment. + auto &mbr_type = get(ib_type.member_types[index]); + if (mbr_type.basetype == SPIRType::Struct) + SPIRV_CROSS_THROW("Cannot perform any repacking for structs when it is used as a member of another struct."); + + // Perform remapping here. + // There is nothing to be gained by using packed scalars, so don't attempt it. + if (!is_scalar(ib_type)) + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + + // Try validating again, now with packed. + if (validate_member_packing_rules_msl(ib_type, index)) + return; + + // We're in deep trouble, and we need to create a new PhysicalType which matches up with what we expect. + // A lot of work goes here ... + // We will need remapping on Load and Store to translate the types between Logical and Physical. + + // First, we check if we have small vector std140 array. + // We detect this if we have an array of vectors, and array stride is greater than number of elements. + if (!mbr_type.array.empty() && !is_matrix(mbr_type)) + { + uint32_t array_stride = type_struct_member_array_stride(ib_type, index); + + // Hack off array-of-arrays until we find the array stride per element we must have to make it work. + uint32_t dimensions = uint32_t(mbr_type.array.size() - 1); + for (uint32_t dim = 0; dim < dimensions; dim++) + array_stride /= max(to_array_size_literal(mbr_type, dim), 1u); + + uint32_t elems_per_stride = array_stride / (mbr_type.width / 8); + + if (elems_per_stride == 3) + SPIRV_CROSS_THROW("Cannot use ArrayStride of 3 elements in remapping scenarios."); + else if (elems_per_stride > 4) + SPIRV_CROSS_THROW("Cannot represent vectors with more than 4 elements in MSL."); + + auto physical_type = mbr_type; + physical_type.vecsize = elems_per_stride; + physical_type.parent_type = 0; + uint32_t type_id = ir.increase_bound_by(1); + set(type_id, physical_type); + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID, type_id); + set_decoration(type_id, DecorationArrayStride, array_stride); + + // Remove packed_ for vectors of size 1, 2 and 4. + if (has_extended_decoration(ib_type.self, SPIRVCrossDecorationPhysicalTypePacked)) + SPIRV_CROSS_THROW("Unable to remove packed decoration as entire struct must be fully packed. Do not mix " + "scalar and std140 layout rules."); + else + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + } + else if (is_matrix(mbr_type)) + { + // MatrixStride might be std140-esque. + uint32_t matrix_stride = type_struct_member_matrix_stride(ib_type, index); + + uint32_t elems_per_stride = matrix_stride / (mbr_type.width / 8); + + if (elems_per_stride == 3) + SPIRV_CROSS_THROW("Cannot use ArrayStride of 3 elements in remapping scenarios."); + else if (elems_per_stride > 4) + SPIRV_CROSS_THROW("Cannot represent vectors with more than 4 elements in MSL."); + + bool row_major = has_member_decoration(ib_type.self, index, DecorationRowMajor); + + auto physical_type = mbr_type; + physical_type.parent_type = 0; + if (row_major) + physical_type.columns = elems_per_stride; + else + physical_type.vecsize = elems_per_stride; + uint32_t type_id = ir.increase_bound_by(1); + set(type_id, physical_type); + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID, type_id); + + // Remove packed_ for vectors of size 1, 2 and 4. + if (has_extended_decoration(ib_type.self, SPIRVCrossDecorationPhysicalTypePacked)) + SPIRV_CROSS_THROW("Unable to remove packed decoration as entire struct must be fully packed. Do not mix " + "scalar and std140 layout rules."); + else + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + } + else + SPIRV_CROSS_THROW("Found a buffer packing case which we cannot represent in MSL."); + + // Try validating again, now with physical type remapping. + if (validate_member_packing_rules_msl(ib_type, index)) + return; + + // We might have a particular odd scalar layout case where the last element of an array + // does not take up as much space as the ArrayStride or MatrixStride. This can happen with DX cbuffers. + // The "proper" workaround for this is extremely painful and essentially impossible in the edge case of float3[], + // so we hack around it by declaring the offending array or matrix with one less array size/col/row, + // and rely on padding to get the correct value. We will technically access arrays out of bounds into the padding region, + // but it should spill over gracefully without too much trouble. We rely on behavior like this for unsized arrays anyways. + + // E.g. we might observe a physical layout of: + // { float2 a[2]; float b; } in cbuffer layout where ArrayStride of a is 16, but offset of b is 24, packed right after a[1] ... + uint32_t type_id = get_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID); + auto &type = get(type_id); + + // Modify the physical type in-place. This is safe since each physical type workaround is a copy. + if (is_array(type)) + { + if (type.array.back() > 1) + { + if (!type.array_size_literal.back()) + SPIRV_CROSS_THROW("Cannot apply scalar layout workaround with spec constant array size."); + type.array.back() -= 1; + } + else + { + // We have an array of size 1, so we cannot decrement that. Our only option now is to + // force a packed layout instead, and drop the physical type remap since ArrayStride is meaningless now. + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID); + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + } + } + else if (is_matrix(type)) + { + bool row_major = has_member_decoration(ib_type.self, index, DecorationRowMajor); + if (!row_major) + { + // Slice off one column. If we only have 2 columns, this might turn the matrix into a vector with one array element instead. + if (type.columns > 2) + { + type.columns--; + } + else if (type.columns == 2) + { + type.columns = 1; + assert(type.array.empty()); + type.array.push_back(1); + type.array_size_literal.push_back(true); + } + } + else + { + // Slice off one row. If we only have 2 rows, this might turn the matrix into a vector with one array element instead. + if (type.vecsize > 2) + { + type.vecsize--; + } + else if (type.vecsize == 2) + { + type.vecsize = type.columns; + type.columns = 1; + assert(type.array.empty()); + type.array.push_back(1); + type.array_size_literal.push_back(true); + } + } + } + + // This better validate now, or we must fail gracefully. + if (!validate_member_packing_rules_msl(ib_type, index)) + SPIRV_CROSS_THROW("Found a buffer packing case which we cannot represent in MSL."); +} + +void CompilerMSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) +{ + auto &type = expression_type(rhs_expression); + + bool lhs_remapped_type = has_extended_decoration(lhs_expression, SPIRVCrossDecorationPhysicalTypeID); + bool lhs_packed_type = has_extended_decoration(lhs_expression, SPIRVCrossDecorationPhysicalTypePacked); + auto *lhs_e = maybe_get(lhs_expression); + auto *rhs_e = maybe_get(rhs_expression); + + bool transpose = lhs_e && lhs_e->need_transpose; + + // No physical type remapping, and no packed type, so can just emit a store directly. + if (!lhs_remapped_type && !lhs_packed_type) + { + // We might not be dealing with remapped physical types or packed types, + // but we might be doing a clean store to a row-major matrix. + // In this case, we just flip transpose states, and emit the store, a transpose must be in the RHS expression, if any. + if (is_matrix(type) && lhs_e && lhs_e->need_transpose) + { + if (!rhs_e) + SPIRV_CROSS_THROW("Need to transpose right-side expression of a store to row-major matrix, but it is " + "not a SPIRExpression."); + lhs_e->need_transpose = false; + + if (rhs_e && rhs_e->need_transpose) + { + // Direct copy, but might need to unpack RHS. + // Skip the transpose, as we will transpose when writing to LHS and transpose(transpose(T)) == T. + rhs_e->need_transpose = false; + statement(to_expression(lhs_expression), " = ", to_unpacked_row_major_matrix_expression(rhs_expression), + ";"); + rhs_e->need_transpose = true; + } + else + statement(to_expression(lhs_expression), " = transpose(", to_unpacked_expression(rhs_expression), ");"); + + lhs_e->need_transpose = true; + register_write(lhs_expression); + } + else if (lhs_e && lhs_e->need_transpose) + { + lhs_e->need_transpose = false; + + // Storing a column to a row-major matrix. Unroll the write. + for (uint32_t c = 0; c < type.vecsize; c++) + { + auto lhs_expr = to_dereferenced_expression(lhs_expression); + auto column_index = lhs_expr.find_last_of('['); + if (column_index != string::npos) + { + statement(lhs_expr.insert(column_index, join('[', c, ']')), " = ", + to_extract_component_expression(rhs_expression, c), ";"); + } + } + lhs_e->need_transpose = true; + register_write(lhs_expression); + } + else + CompilerGLSL::emit_store_statement(lhs_expression, rhs_expression); + } + else if (!lhs_remapped_type && !is_matrix(type) && !transpose) + { + // Even if the target type is packed, we can directly store to it. We cannot store to packed matrices directly, + // since they are declared as array of vectors instead, and we need the fallback path below. + CompilerGLSL::emit_store_statement(lhs_expression, rhs_expression); + } + else + { + // Special handling when storing to a remapped physical type. + // This is mostly to deal with std140 padded matrices or vectors. + + TypeID physical_type_id = lhs_remapped_type ? + ID(get_extended_decoration(lhs_expression, SPIRVCrossDecorationPhysicalTypeID)) : + type.self; + + auto &physical_type = get(physical_type_id); + + if (is_matrix(type)) + { + const char *packed_pfx = lhs_packed_type ? "packed_" : ""; + + // Packed matrices are stored as arrays of packed vectors, so we need + // to assign the vectors one at a time. + // For row-major matrices, we need to transpose the *right-hand* side, + // not the left-hand side. + + // Lots of cases to cover here ... + + bool rhs_transpose = rhs_e && rhs_e->need_transpose; + SPIRType write_type = type; + string cast_expr; + + // We're dealing with transpose manually. + if (rhs_transpose) + rhs_e->need_transpose = false; + + if (transpose) + { + // We're dealing with transpose manually. + lhs_e->need_transpose = false; + write_type.vecsize = type.columns; + write_type.columns = 1; + + if (physical_type.columns != type.columns) + cast_expr = join("(device ", packed_pfx, type_to_glsl(write_type), "&)"); + + if (rhs_transpose) + { + // If RHS is also transposed, we can just copy row by row. + for (uint32_t i = 0; i < type.vecsize; i++) + { + statement(cast_expr, to_enclosed_expression(lhs_expression), "[", i, "]", " = ", + to_unpacked_row_major_matrix_expression(rhs_expression), "[", i, "];"); + } + } + else + { + auto vector_type = expression_type(rhs_expression); + vector_type.vecsize = vector_type.columns; + vector_type.columns = 1; + + // Transpose on the fly. Emitting a lot of full transpose() ops and extracting lanes seems very bad, + // so pick out individual components instead. + for (uint32_t i = 0; i < type.vecsize; i++) + { + string rhs_row = type_to_glsl_constructor(vector_type) + "("; + for (uint32_t j = 0; j < vector_type.vecsize; j++) + { + rhs_row += join(to_enclosed_unpacked_expression(rhs_expression), "[", j, "][", i, "]"); + if (j + 1 < vector_type.vecsize) + rhs_row += ", "; + } + rhs_row += ")"; + + statement(cast_expr, to_enclosed_expression(lhs_expression), "[", i, "]", " = ", rhs_row, ";"); + } + } + + // We're dealing with transpose manually. + lhs_e->need_transpose = true; + } + else + { + write_type.columns = 1; + + if (physical_type.vecsize != type.vecsize) + cast_expr = join("(device ", packed_pfx, type_to_glsl(write_type), "&)"); + + if (rhs_transpose) + { + auto vector_type = expression_type(rhs_expression); + vector_type.columns = 1; + + // Transpose on the fly. Emitting a lot of full transpose() ops and extracting lanes seems very bad, + // so pick out individual components instead. + for (uint32_t i = 0; i < type.columns; i++) + { + string rhs_row = type_to_glsl_constructor(vector_type) + "("; + for (uint32_t j = 0; j < vector_type.vecsize; j++) + { + // Need to explicitly unpack expression since we've mucked with transpose state. + auto unpacked_expr = to_unpacked_row_major_matrix_expression(rhs_expression); + rhs_row += join(unpacked_expr, "[", j, "][", i, "]"); + if (j + 1 < vector_type.vecsize) + rhs_row += ", "; + } + rhs_row += ")"; + + statement(cast_expr, to_enclosed_expression(lhs_expression), "[", i, "]", " = ", rhs_row, ";"); + } + } + else + { + // Copy column-by-column. + for (uint32_t i = 0; i < type.columns; i++) + { + statement(cast_expr, to_enclosed_expression(lhs_expression), "[", i, "]", " = ", + to_enclosed_unpacked_expression(rhs_expression), "[", i, "];"); + } + } + } + + // We're dealing with transpose manually. + if (rhs_transpose) + rhs_e->need_transpose = true; + } + else if (transpose) + { + lhs_e->need_transpose = false; + + SPIRType write_type = type; + write_type.vecsize = 1; + write_type.columns = 1; + + // Storing a column to a row-major matrix. Unroll the write. + for (uint32_t c = 0; c < type.vecsize; c++) + { + auto lhs_expr = to_enclosed_expression(lhs_expression); + auto column_index = lhs_expr.find_last_of('['); + if (column_index != string::npos) + { + statement("((device ", type_to_glsl(write_type), "*)&", + lhs_expr.insert(column_index, join('[', c, ']', ")")), " = ", + to_extract_component_expression(rhs_expression, c), ";"); + } + } + + lhs_e->need_transpose = true; + } + else if ((is_matrix(physical_type) || is_array(physical_type)) && physical_type.vecsize > type.vecsize) + { + assert(type.vecsize >= 1 && type.vecsize <= 3); + + // If we have packed types, we cannot use swizzled stores. + // We could technically unroll the store for each element if needed. + // When remapping to a std140 physical type, we always get float4, + // and the packed decoration should always be removed. + assert(!lhs_packed_type); + + string lhs = to_dereferenced_expression(lhs_expression); + string rhs = to_pointer_expression(rhs_expression); + + // Unpack the expression so we can store to it with a float or float2. + // It's still an l-value, so it's fine. Most other unpacking of expressions turn them into r-values instead. + lhs = join("(device ", type_to_glsl(type), "&)", enclose_expression(lhs)); + if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs)) + statement(lhs, " = ", rhs, ";"); + } + else if (!is_matrix(type)) + { + string lhs = to_dereferenced_expression(lhs_expression); + string rhs = to_pointer_expression(rhs_expression); + if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs)) + statement(lhs, " = ", rhs, ";"); + } + + register_write(lhs_expression); + } +} + +static bool expression_ends_with(const string &expr_str, const std::string &ending) +{ + if (expr_str.length() >= ending.length()) + return (expr_str.compare(expr_str.length() - ending.length(), ending.length(), ending) == 0); + else + return false; +} + +// Converts the format of the current expression from packed to unpacked, +// by wrapping the expression in a constructor of the appropriate type. +// Also, handle special physical ID remapping scenarios, similar to emit_store_statement(). +string CompilerMSL::unpack_expression_type(string expr_str, const SPIRType &type, uint32_t physical_type_id, + bool packed, bool row_major) +{ + // Trivial case, nothing to do. + if (physical_type_id == 0 && !packed) + return expr_str; + + const SPIRType *physical_type = nullptr; + if (physical_type_id) + physical_type = &get(physical_type_id); + + static const char *swizzle_lut[] = { + ".x", + ".xy", + ".xyz", + }; + + if (physical_type && is_vector(*physical_type) && is_array(*physical_type) && + physical_type->vecsize > type.vecsize && !expression_ends_with(expr_str, swizzle_lut[type.vecsize - 1])) + { + // std140 array cases for vectors. + assert(type.vecsize >= 1 && type.vecsize <= 3); + return enclose_expression(expr_str) + swizzle_lut[type.vecsize - 1]; + } + else if (physical_type && is_matrix(*physical_type) && is_vector(type) && physical_type->vecsize > type.vecsize) + { + // Extract column from padded matrix. + assert(type.vecsize >= 1 && type.vecsize <= 3); + return enclose_expression(expr_str) + swizzle_lut[type.vecsize - 1]; + } + else if (is_matrix(type)) + { + // Packed matrices are stored as arrays of packed vectors. Unfortunately, + // we can't just pass the array straight to the matrix constructor. We have to + // pass each vector individually, so that they can be unpacked to normal vectors. + if (!physical_type) + physical_type = &type; + + uint32_t vecsize = type.vecsize; + uint32_t columns = type.columns; + if (row_major) + swap(vecsize, columns); + + uint32_t physical_vecsize = row_major ? physical_type->columns : physical_type->vecsize; + + const char *base_type = type.width == 16 ? "half" : "float"; + string unpack_expr = join(base_type, columns, "x", vecsize, "("); + + const char *load_swiz = ""; + + if (physical_vecsize != vecsize) + load_swiz = swizzle_lut[vecsize - 1]; + + for (uint32_t i = 0; i < columns; i++) + { + if (i > 0) + unpack_expr += ", "; + + if (packed) + unpack_expr += join(base_type, physical_vecsize, "(", expr_str, "[", i, "]", ")", load_swiz); + else + unpack_expr += join(expr_str, "[", i, "]", load_swiz); + } + + unpack_expr += ")"; + return unpack_expr; + } + else + { + return join(type_to_glsl(type), "(", expr_str, ")"); + } +} + +// Emits the file header info +void CompilerMSL::emit_header() +{ + // This particular line can be overridden during compilation, so make it a flag and not a pragma line. + if (suppress_missing_prototypes) + statement("#pragma clang diagnostic ignored \"-Wmissing-prototypes\""); + + // Disable warning about missing braces for array template to make arrays a value type + if (spv_function_implementations.count(SPVFuncImplUnsafeArray) != 0) + statement("#pragma clang diagnostic ignored \"-Wmissing-braces\""); + + for (auto &pragma : pragma_lines) + statement(pragma); + + if (!pragma_lines.empty() || suppress_missing_prototypes) + statement(""); + + statement("#include "); + statement("#include "); + + for (auto &header : header_lines) + statement(header); + + statement(""); + statement("using namespace metal;"); + statement(""); + + for (auto &td : typedef_lines) + statement(td); + + if (!typedef_lines.empty()) + statement(""); +} + +void CompilerMSL::add_pragma_line(const string &line) +{ + auto rslt = pragma_lines.insert(line); + if (rslt.second) + force_recompile(); +} + +void CompilerMSL::add_typedef_line(const string &line) +{ + auto rslt = typedef_lines.insert(line); + if (rslt.second) + force_recompile(); +} + +// Template struct like spvUnsafeArray<> need to be declared *before* any resources are declared +void CompilerMSL::emit_custom_templates() +{ + for (const auto &spv_func : spv_function_implementations) + { + switch (spv_func) + { + case SPVFuncImplUnsafeArray: + statement("template"); + statement("struct spvUnsafeArray"); + begin_scope(); + statement("T elements[Num ? Num : 1];"); + statement(""); + statement("thread T& operator [] (size_t pos) thread"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement("constexpr const thread T& operator [] (size_t pos) const thread"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement(""); + statement("device T& operator [] (size_t pos) device"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement("constexpr const device T& operator [] (size_t pos) const device"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement(""); + statement("constexpr const constant T& operator [] (size_t pos) const constant"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement(""); + statement("threadgroup T& operator [] (size_t pos) threadgroup"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement("constexpr const threadgroup T& operator [] (size_t pos) const threadgroup"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + end_scope_decl(); + statement(""); + break; + + default: + break; + } + } +} + +// Emits any needed custom function bodies. +// Metal helper functions must be static force-inline, i.e. static inline __attribute__((always_inline)) +// otherwise they will cause problems when linked together in a single Metallib. +void CompilerMSL::emit_custom_functions() +{ + for (uint32_t i = kArrayCopyMultidimMax; i >= 2; i--) + if (spv_function_implementations.count(static_cast(SPVFuncImplArrayCopyMultidimBase + i))) + spv_function_implementations.insert(static_cast(SPVFuncImplArrayCopyMultidimBase + i - 1)); + + if (spv_function_implementations.count(SPVFuncImplDynamicImageSampler)) + { + // Unfortunately, this one needs a lot of the other functions to compile OK. + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW( + "spvDynamicImageSampler requires default-constructible texture objects, which require MSL 2.0."); + spv_function_implementations.insert(SPVFuncImplForwardArgs); + spv_function_implementations.insert(SPVFuncImplTextureSwizzle); + if (msl_options.swizzle_texture_samples) + spv_function_implementations.insert(SPVFuncImplGatherSwizzle); + for (uint32_t i = SPVFuncImplChromaReconstructNearest2Plane; + i <= SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane; i++) + spv_function_implementations.insert(static_cast(i)); + spv_function_implementations.insert(SPVFuncImplExpandITUFullRange); + spv_function_implementations.insert(SPVFuncImplExpandITUNarrowRange); + spv_function_implementations.insert(SPVFuncImplConvertYCbCrBT709); + spv_function_implementations.insert(SPVFuncImplConvertYCbCrBT601); + spv_function_implementations.insert(SPVFuncImplConvertYCbCrBT2020); + } + + for (uint32_t i = SPVFuncImplChromaReconstructNearest2Plane; + i <= SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane; i++) + if (spv_function_implementations.count(static_cast(i))) + spv_function_implementations.insert(SPVFuncImplForwardArgs); + + if (spv_function_implementations.count(SPVFuncImplTextureSwizzle) || + spv_function_implementations.count(SPVFuncImplGatherSwizzle) || + spv_function_implementations.count(SPVFuncImplGatherCompareSwizzle)) + { + spv_function_implementations.insert(SPVFuncImplForwardArgs); + spv_function_implementations.insert(SPVFuncImplGetSwizzle); + } + + for (const auto &spv_func : spv_function_implementations) + { + switch (spv_func) + { + case SPVFuncImplMod: + statement("// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()"); + statement("template"); + statement("inline Tx mod(Tx x, Ty y)"); + begin_scope(); + statement("return x - y * floor(x / y);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplRadians: + statement("// Implementation of the GLSL radians() function"); + statement("template"); + statement("inline T radians(T d)"); + begin_scope(); + statement("return d * T(0.01745329251);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplDegrees: + statement("// Implementation of the GLSL degrees() function"); + statement("template"); + statement("inline T degrees(T r)"); + begin_scope(); + statement("return r * T(57.2957795131);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplFindILsb: + statement("// Implementation of the GLSL findLSB() function"); + statement("template"); + statement("inline T spvFindLSB(T x)"); + begin_scope(); + statement("return select(ctz(x), T(-1), x == T(0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplFindUMsb: + statement("// Implementation of the unsigned GLSL findMSB() function"); + statement("template"); + statement("inline T spvFindUMSB(T x)"); + begin_scope(); + statement("return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplFindSMsb: + statement("// Implementation of the signed GLSL findMSB() function"); + statement("template"); + statement("inline T spvFindSMSB(T x)"); + begin_scope(); + statement("T v = select(x, T(-1) - x, x < T(0));"); + statement("return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSSign: + statement("// Implementation of the GLSL sign() function for integer types"); + statement("template::value>::type>"); + statement("inline T sign(T x)"); + begin_scope(); + statement("return select(select(select(x, T(0), x == T(0)), T(1), x > T(0)), T(-1), x < T(0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplArrayCopy: + case SPVFuncImplArrayOfArrayCopy2Dim: + case SPVFuncImplArrayOfArrayCopy3Dim: + case SPVFuncImplArrayOfArrayCopy4Dim: + case SPVFuncImplArrayOfArrayCopy5Dim: + case SPVFuncImplArrayOfArrayCopy6Dim: + { + // Unfortunately we cannot template on the address space, so combinatorial explosion it is. + static const char *function_name_tags[] = { + "FromConstantToStack", "FromConstantToThreadGroup", "FromStackToStack", + "FromStackToThreadGroup", "FromThreadGroupToStack", "FromThreadGroupToThreadGroup", + "FromDeviceToDevice", "FromConstantToDevice", "FromStackToDevice", + "FromThreadGroupToDevice", "FromDeviceToStack", "FromDeviceToThreadGroup", + }; + + static const char *src_address_space[] = { + "constant", "constant", "thread const", "thread const", + "threadgroup const", "threadgroup const", "device const", "constant", + "thread const", "threadgroup const", "device const", "device const", + }; + + static const char *dst_address_space[] = { + "thread", "threadgroup", "thread", "threadgroup", "thread", "threadgroup", + "device", "device", "device", "device", "thread", "threadgroup", + }; + + for (uint32_t variant = 0; variant < 12; variant++) + { + uint32_t dimensions = spv_func - SPVFuncImplArrayCopyMultidimBase; + string tmp = "template 0) + { + string tex_width_str = convert_to_string(msl_options.texel_buffer_texture_width); + statement("// Returns 2D texture coords corresponding to 1D texel buffer coords"); + statement(force_inline); + statement("uint2 spvTexelBufferCoord(uint tc)"); + begin_scope(); + statement(join("return uint2(tc % ", tex_width_str, ", tc / ", tex_width_str, ");")); + end_scope(); + statement(""); + } + else + { + statement("// Returns 2D texture coords corresponding to 1D texel buffer coords"); + statement( + "#define spvTexelBufferCoord(tc, tex) uint2((tc) % (tex).get_width(), (tc) / (tex).get_width())"); + statement(""); + } + break; + } + + // Emulate texture2D atomic operations + case SPVFuncImplImage2DAtomicCoords: + { + if (msl_options.supports_msl_version(1, 2)) + { + statement("// The required alignment of a linear texture of R32Uint format."); + statement("constant uint spvLinearTextureAlignmentOverride [[function_constant(", + msl_options.r32ui_alignment_constant_id, ")]];"); + statement("constant uint spvLinearTextureAlignment = ", + "is_function_constant_defined(spvLinearTextureAlignmentOverride) ? ", + "spvLinearTextureAlignmentOverride : ", msl_options.r32ui_linear_texture_alignment, ";"); + } + else + { + statement("// The required alignment of a linear texture of R32Uint format."); + statement("constant uint spvLinearTextureAlignment = ", msl_options.r32ui_linear_texture_alignment, + ";"); + } + statement("// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics"); + statement("#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + ", + " spvLinearTextureAlignment / 4 - 1) & ~(", + " spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x)"); + statement(""); + break; + } + + // "fadd" intrinsic support + case SPVFuncImplFAdd: + statement("template"); + statement("T spvFAdd(T l, T r)"); + begin_scope(); + statement("return fma(T(1), l, r);"); + end_scope(); + statement(""); + break; + + // "fmul' intrinsic support + case SPVFuncImplFMul: + statement("template"); + statement("T spvFMul(T l, T r)"); + begin_scope(); + statement("return fma(l, r, T(0));"); + end_scope(); + statement(""); + + statement("template"); + statement("vec spvFMulVectorMatrix(vec v, matrix m)"); + begin_scope(); + statement("vec res = vec(0);"); + statement("for (uint i = Rows; i > 0; --i)"); + begin_scope(); + statement("vec tmp(0);"); + statement("for (uint j = 0; j < Cols; ++j)"); + begin_scope(); + statement("tmp[j] = m[j][i - 1];"); + end_scope(); + statement("res = fma(tmp, vec(v[i - 1]), res);"); + end_scope(); + statement("return res;"); + end_scope(); + statement(""); + + statement("template"); + statement("vec spvFMulMatrixVector(matrix m, vec v)"); + begin_scope(); + statement("vec res = vec(0);"); + statement("for (uint i = Cols; i > 0; --i)"); + begin_scope(); + statement("res = fma(m[i - 1], vec(v[i - 1]), res);"); + end_scope(); + statement("return res;"); + end_scope(); + statement(""); + + statement("template"); + statement( + "matrix spvFMulMatrixMatrix(matrix l, matrix r)"); + begin_scope(); + statement("matrix res;"); + statement("for (uint i = 0; i < RCols; i++)"); + begin_scope(); + statement("vec tmp(0);"); + statement("for (uint j = 0; j < LCols; j++)"); + begin_scope(); + statement("tmp = fma(vec(r[i][j]), l[j], tmp);"); + end_scope(); + statement("res[i] = tmp;"); + end_scope(); + statement("return res;"); + end_scope(); + statement(""); + break; + + // Emulate texturecube_array with texture2d_array for iOS where this type is not available + case SPVFuncImplCubemapTo2DArrayFace: + statement(force_inline); + statement("float3 spvCubemapTo2DArrayFace(float3 P)"); + begin_scope(); + statement("float3 Coords = abs(P.xyz);"); + statement("float CubeFace = 0;"); + statement("float ProjectionAxis = 0;"); + statement("float u = 0;"); + statement("float v = 0;"); + statement("if (Coords.x >= Coords.y && Coords.x >= Coords.z)"); + begin_scope(); + statement("CubeFace = P.x >= 0 ? 0 : 1;"); + statement("ProjectionAxis = Coords.x;"); + statement("u = P.x >= 0 ? -P.z : P.z;"); + statement("v = -P.y;"); + end_scope(); + statement("else if (Coords.y >= Coords.x && Coords.y >= Coords.z)"); + begin_scope(); + statement("CubeFace = P.y >= 0 ? 2 : 3;"); + statement("ProjectionAxis = Coords.y;"); + statement("u = P.x;"); + statement("v = P.y >= 0 ? P.z : -P.z;"); + end_scope(); + statement("else"); + begin_scope(); + statement("CubeFace = P.z >= 0 ? 4 : 5;"); + statement("ProjectionAxis = Coords.z;"); + statement("u = P.z >= 0 ? P.x : -P.x;"); + statement("v = -P.y;"); + end_scope(); + statement("u = 0.5 * (u/ProjectionAxis + 1);"); + statement("v = 0.5 * (v/ProjectionAxis + 1);"); + statement("return float3(u, v, CubeFace);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplInverse4x4: + statement("// Returns the determinant of a 2x2 matrix."); + statement(force_inline); + statement("float spvDet2x2(float a1, float a2, float b1, float b2)"); + begin_scope(); + statement("return a1 * b2 - b1 * a2;"); + end_scope(); + statement(""); + + statement("// Returns the determinant of a 3x3 matrix."); + statement(force_inline); + statement("float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, " + "float c2, float c3)"); + begin_scope(); + statement("return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * spvDet2x2(a2, a3, " + "b2, b3);"); + end_scope(); + statement(""); + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement(force_inline); + statement("float4x4 spvInverse4x4(float4x4 m)"); + begin_scope(); + statement("float4x4 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement("adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement("adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement("adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], " + "m[2][3]);"); + statement_no_indent(""); + statement("adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement("adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement("adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement("adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], " + "m[2][3]);"); + statement_no_indent(""); + statement("adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement("adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement("adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement("adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], " + "m[2][3]);"); + statement_no_indent(""); + statement("adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement("adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement("adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement("adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], " + "m[2][2]);"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] " + "* m[3][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplInverse3x3: + if (spv_function_implementations.count(SPVFuncImplInverse4x4) == 0) + { + statement("// Returns the determinant of a 2x2 matrix."); + statement(force_inline); + statement("float spvDet2x2(float a1, float a2, float b1, float b2)"); + begin_scope(); + statement("return a1 * b2 - b1 * a2;"); + end_scope(); + statement(""); + } + + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement(force_inline); + statement("float3x3 spvInverse3x3(float3x3 m)"); + begin_scope(); + statement("float3x3 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = spvDet2x2(m[1][1], m[1][2], m[2][1], m[2][2]);"); + statement("adj[0][1] = -spvDet2x2(m[0][1], m[0][2], m[2][1], m[2][2]);"); + statement("adj[0][2] = spvDet2x2(m[0][1], m[0][2], m[1][1], m[1][2]);"); + statement_no_indent(""); + statement("adj[1][0] = -spvDet2x2(m[1][0], m[1][2], m[2][0], m[2][2]);"); + statement("adj[1][1] = spvDet2x2(m[0][0], m[0][2], m[2][0], m[2][2]);"); + statement("adj[1][2] = -spvDet2x2(m[0][0], m[0][2], m[1][0], m[1][2]);"); + statement_no_indent(""); + statement("adj[2][0] = spvDet2x2(m[1][0], m[1][1], m[2][0], m[2][1]);"); + statement("adj[2][1] = -spvDet2x2(m[0][0], m[0][1], m[2][0], m[2][1]);"); + statement("adj[2][2] = spvDet2x2(m[0][0], m[0][1], m[1][0], m[1][1]);"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplInverse2x2: + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement(force_inline); + statement("float2x2 spvInverse2x2(float2x2 m)"); + begin_scope(); + statement("float2x2 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = m[1][1];"); + statement("adj[0][1] = -m[0][1];"); + statement_no_indent(""); + statement("adj[1][0] = -m[1][0];"); + statement("adj[1][1] = m[0][0];"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplForwardArgs: + statement("template struct spvRemoveReference { typedef T type; };"); + statement("template struct spvRemoveReference { typedef T type; };"); + statement("template struct spvRemoveReference { typedef T type; };"); + statement("template inline constexpr thread T&& spvForward(thread typename " + "spvRemoveReference::type& x)"); + begin_scope(); + statement("return static_cast(x);"); + end_scope(); + statement("template inline constexpr thread T&& spvForward(thread typename " + "spvRemoveReference::type&& x)"); + begin_scope(); + statement("return static_cast(x);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplGetSwizzle: + statement("enum class spvSwizzle : uint"); + begin_scope(); + statement("none = 0,"); + statement("zero,"); + statement("one,"); + statement("red,"); + statement("green,"); + statement("blue,"); + statement("alpha"); + end_scope_decl(); + statement(""); + statement("template"); + statement("inline T spvGetSwizzle(vec x, T c, spvSwizzle s)"); + begin_scope(); + statement("switch (s)"); + begin_scope(); + statement("case spvSwizzle::none:"); + statement(" return c;"); + statement("case spvSwizzle::zero:"); + statement(" return 0;"); + statement("case spvSwizzle::one:"); + statement(" return 1;"); + statement("case spvSwizzle::red:"); + statement(" return x.r;"); + statement("case spvSwizzle::green:"); + statement(" return x.g;"); + statement("case spvSwizzle::blue:"); + statement(" return x.b;"); + statement("case spvSwizzle::alpha:"); + statement(" return x.a;"); + end_scope(); + end_scope(); + statement(""); + break; + + case SPVFuncImplTextureSwizzle: + statement("// Wrapper function that swizzles texture samples and fetches."); + statement("template"); + statement("inline vec spvTextureSwizzle(vec x, uint s)"); + begin_scope(); + statement("if (!s)"); + statement(" return x;"); + statement("return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), " + "spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) " + "& 0xFF)), " + "spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF)));"); + end_scope(); + statement(""); + statement("template"); + statement("inline T spvTextureSwizzle(T x, uint s)"); + begin_scope(); + statement("return spvTextureSwizzle(vec(x, 0, 0, 1), s).x;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplGatherSwizzle: + statement("// Wrapper function that swizzles texture gathers."); + statement("template class Tex, " + "typename... Ts>"); + statement("inline vec spvGatherSwizzle(const thread Tex& t, sampler s, " + "uint sw, component c, Ts... params) METAL_CONST_ARG(c)"); + begin_scope(); + statement("if (sw)"); + begin_scope(); + statement("switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))"); + begin_scope(); + statement("case spvSwizzle::none:"); + statement(" break;"); + statement("case spvSwizzle::zero:"); + statement(" return vec(0, 0, 0, 0);"); + statement("case spvSwizzle::one:"); + statement(" return vec(1, 1, 1, 1);"); + statement("case spvSwizzle::red:"); + statement(" return t.gather(s, spvForward(params)..., component::x);"); + statement("case spvSwizzle::green:"); + statement(" return t.gather(s, spvForward(params)..., component::y);"); + statement("case spvSwizzle::blue:"); + statement(" return t.gather(s, spvForward(params)..., component::z);"); + statement("case spvSwizzle::alpha:"); + statement(" return t.gather(s, spvForward(params)..., component::w);"); + end_scope(); + end_scope(); + // texture::gather insists on its component parameter being a constant + // expression, so we need this silly workaround just to compile the shader. + statement("switch (c)"); + begin_scope(); + statement("case component::x:"); + statement(" return t.gather(s, spvForward(params)..., component::x);"); + statement("case component::y:"); + statement(" return t.gather(s, spvForward(params)..., component::y);"); + statement("case component::z:"); + statement(" return t.gather(s, spvForward(params)..., component::z);"); + statement("case component::w:"); + statement(" return t.gather(s, spvForward(params)..., component::w);"); + end_scope(); + end_scope(); + statement(""); + break; + + case SPVFuncImplGatherCompareSwizzle: + statement("// Wrapper function that swizzles depth texture gathers."); + statement("template class Tex, " + "typename... Ts>"); + statement("inline vec spvGatherCompareSwizzle(const thread Tex& t, sampler " + "s, uint sw, Ts... params) "); + begin_scope(); + statement("if (sw)"); + begin_scope(); + statement("switch (spvSwizzle(sw & 0xFF))"); + begin_scope(); + statement("case spvSwizzle::none:"); + statement("case spvSwizzle::red:"); + statement(" break;"); + statement("case spvSwizzle::zero:"); + statement("case spvSwizzle::green:"); + statement("case spvSwizzle::blue:"); + statement("case spvSwizzle::alpha:"); + statement(" return vec(0, 0, 0, 0);"); + statement("case spvSwizzle::one:"); + statement(" return vec(1, 1, 1, 1);"); + end_scope(); + end_scope(); + statement("return t.gather_compare(s, spvForward(params)...);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBroadcast: + // Metal doesn't allow broadcasting boolean values directly, but we can work around that by broadcasting + // them as integers. + statement("template"); + statement("inline T spvSubgroupBroadcast(T value, ushort lane)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return quad_broadcast(value, lane);"); + else + statement("return simd_broadcast(value, lane);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupBroadcast(bool value, ushort lane)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return !!quad_broadcast((ushort)value, lane);"); + else + statement("return !!simd_broadcast((ushort)value, lane);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupBroadcast(vec value, ushort lane)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return (vec)quad_broadcast((vec)value, lane);"); + else + statement("return (vec)simd_broadcast((vec)value, lane);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBroadcastFirst: + statement("template"); + statement("inline T spvSubgroupBroadcastFirst(T value)"); + begin_scope(); + statement("return simd_broadcast_first(value);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupBroadcastFirst(bool value)"); + begin_scope(); + statement("return !!simd_broadcast_first((ushort)value);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupBroadcastFirst(vec value)"); + begin_scope(); + statement("return (vec)simd_broadcast_first((vec)value);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallot: + statement("inline uint4 spvSubgroupBallot(bool value)"); + begin_scope(); + statement("simd_vote vote = simd_ballot(value);"); + statement("// simd_ballot() returns a 64-bit integer-like object, but"); + statement("// SPIR-V callers expect a uint4. We must convert."); + statement("// FIXME: This won't include higher bits if Apple ever supports"); + statement("// 128 lanes in an SIMD-group."); + statement("return uint4((uint)((simd_vote::vote_t)vote & 0xFFFFFFFF), (uint)(((simd_vote::vote_t)vote >> " + "32) & 0xFFFFFFFF), 0, 0);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallotBitExtract: + statement("inline bool spvSubgroupBallotBitExtract(uint4 ballot, uint bit)"); + begin_scope(); + statement("return !!extract_bits(ballot[bit / 32], bit % 32, 1);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallotFindLSB: + statement("inline uint spvSubgroupBallotFindLSB(uint4 ballot, uint gl_SubgroupSize)"); + begin_scope(); + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0));"); + statement("ballot &= mask;"); + statement("return select(ctz(ballot.x), select(32 + ctz(ballot.y), select(64 + ctz(ballot.z), select(96 + " + "ctz(ballot.w), uint(-1), ballot.w == 0), ballot.z == 0), ballot.y == 0), ballot.x == 0);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallotFindMSB: + statement("inline uint spvSubgroupBallotFindMSB(uint4 ballot, uint gl_SubgroupSize)"); + begin_scope(); + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0));"); + statement("ballot &= mask;"); + statement("return select(128 - (clz(ballot.w) + 1), select(96 - (clz(ballot.z) + 1), select(64 - " + "(clz(ballot.y) + 1), select(32 - (clz(ballot.x) + 1), uint(-1), ballot.x == 0), ballot.y == 0), " + "ballot.z == 0), ballot.w == 0);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallotBitCount: + statement("inline uint spvPopCount4(uint4 ballot)"); + begin_scope(); + statement("return popcount(ballot.x) + popcount(ballot.y) + popcount(ballot.z) + popcount(ballot.w);"); + end_scope(); + statement(""); + statement("inline uint spvSubgroupBallotBitCount(uint4 ballot, uint gl_SubgroupSize)"); + begin_scope(); + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0));"); + statement("return spvPopCount4(ballot & mask);"); + end_scope(); + statement(""); + statement("inline uint spvSubgroupBallotInclusiveBitCount(uint4 ballot, uint gl_SubgroupInvocationID)"); + begin_scope(); + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID + 1, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID + 1 - 32, 0)), " + "uint2(0));"); + statement("return spvPopCount4(ballot & mask);"); + end_scope(); + statement(""); + statement("inline uint spvSubgroupBallotExclusiveBitCount(uint4 ballot, uint gl_SubgroupInvocationID)"); + begin_scope(); + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID - 32, 0)), uint2(0));"); + statement("return spvPopCount4(ballot & mask);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupAllEqual: + // Metal doesn't provide a function to evaluate this directly. But, we can + // implement this by comparing every thread's value to one thread's value + // (in this case, the value of the first active thread). Then, by the transitive + // property of equality, if all comparisons return true, then they are all equal. + statement("template"); + statement("inline bool spvSubgroupAllEqual(T value)"); + begin_scope(); + statement("return simd_all(all(value == simd_broadcast_first(value)));"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupAllEqual(bool value)"); + begin_scope(); + statement("return simd_all(value) || !simd_any(value);"); + end_scope(); + statement(""); + statement("template"); + statement("inline bool spvSubgroupAllEqual(vec value)"); + begin_scope(); + statement("return simd_all(all(value == (vec)simd_broadcast_first((vec)value)));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupShuffle: + statement("template"); + statement("inline T spvSubgroupShuffle(T value, ushort lane)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return quad_shuffle(value, lane);"); + else + statement("return simd_shuffle(value, lane);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupShuffle(bool value, ushort lane)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return !!quad_shuffle((ushort)value, lane);"); + else + statement("return !!simd_shuffle((ushort)value, lane);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffle(vec value, ushort lane)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return (vec)quad_shuffle((vec)value, lane);"); + else + statement("return (vec)simd_shuffle((vec)value, lane);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupShuffleXor: + statement("template"); + statement("inline T spvSubgroupShuffleXor(T value, ushort mask)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return quad_shuffle_xor(value, mask);"); + else + statement("return simd_shuffle_xor(value, mask);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupShuffleXor(bool value, ushort mask)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return !!quad_shuffle_xor((ushort)value, mask);"); + else + statement("return !!simd_shuffle_xor((ushort)value, mask);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffleXor(vec value, ushort mask)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return (vec)quad_shuffle_xor((vec)value, mask);"); + else + statement("return (vec)simd_shuffle_xor((vec)value, mask);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupShuffleUp: + statement("template"); + statement("inline T spvSubgroupShuffleUp(T value, ushort delta)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return quad_shuffle_up(value, delta);"); + else + statement("return simd_shuffle_up(value, delta);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupShuffleUp(bool value, ushort delta)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return !!quad_shuffle_up((ushort)value, delta);"); + else + statement("return !!simd_shuffle_up((ushort)value, delta);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffleUp(vec value, ushort delta)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return (vec)quad_shuffle_up((vec)value, delta);"); + else + statement("return (vec)simd_shuffle_up((vec)value, delta);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupShuffleDown: + statement("template"); + statement("inline T spvSubgroupShuffleDown(T value, ushort delta)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return quad_shuffle_down(value, delta);"); + else + statement("return simd_shuffle_down(value, delta);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupShuffleDown(bool value, ushort delta)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return !!quad_shuffle_down((ushort)value, delta);"); + else + statement("return !!simd_shuffle_down((ushort)value, delta);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffleDown(vec value, ushort delta)"); + begin_scope(); + if (msl_options.is_ios()) + statement("return (vec)quad_shuffle_down((vec)value, delta);"); + else + statement("return (vec)simd_shuffle_down((vec)value, delta);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplQuadBroadcast: + statement("template"); + statement("inline T spvQuadBroadcast(T value, uint lane)"); + begin_scope(); + statement("return quad_broadcast(value, lane);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvQuadBroadcast(bool value, uint lane)"); + begin_scope(); + statement("return !!quad_broadcast((ushort)value, lane);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvQuadBroadcast(vec value, uint lane)"); + begin_scope(); + statement("return (vec)quad_broadcast((vec)value, lane);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplQuadSwap: + // We can implement this easily based on the following table giving + // the target lane ID from the direction and current lane ID: + // Direction + // | 0 | 1 | 2 | + // ---+---+---+---+ + // L 0 | 1 2 3 + // a 1 | 0 3 2 + // n 2 | 3 0 1 + // e 3 | 2 1 0 + // Notice that target = source ^ (direction + 1). + statement("template"); + statement("inline T spvQuadSwap(T value, uint dir)"); + begin_scope(); + statement("return quad_shuffle_xor(value, dir + 1);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvQuadSwap(bool value, uint dir)"); + begin_scope(); + statement("return !!quad_shuffle_xor((ushort)value, dir + 1);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvQuadSwap(vec value, uint dir)"); + begin_scope(); + statement("return (vec)quad_shuffle_xor((vec)value, dir + 1);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplReflectScalar: + // Metal does not support scalar versions of these functions. + statement("template"); + statement("inline T spvReflect(T i, T n)"); + begin_scope(); + statement("return i - T(2) * i * n * n;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplRefractScalar: + // Metal does not support scalar versions of these functions. + statement("template"); + statement("inline T spvRefract(T i, T n, T eta)"); + begin_scope(); + statement("T NoI = n * i;"); + statement("T NoI2 = NoI * NoI;"); + statement("T k = T(1) - eta * eta * (T(1) - NoI2);"); + statement("if (k < T(0))"); + begin_scope(); + statement("return T(0);"); + end_scope(); + statement("else"); + begin_scope(); + statement("return eta * i - (eta * NoI + sqrt(k)) * n;"); + end_scope(); + end_scope(); + statement(""); + break; + + case SPVFuncImplFaceForwardScalar: + // Metal does not support scalar versions of these functions. + statement("template"); + statement("inline T spvFaceForward(T n, T i, T nref)"); + begin_scope(); + statement("return i * nref < T(0) ? n : -n;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructNearest2Plane: + statement("template"); + statement("inline vec spvChromaReconstructNearest(texture2d plane0, texture2d plane1, sampler " + "samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("ycbcr.br = plane1.sample(samp, coord, spvForward(options)...).rg;"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructNearest3Plane: + statement("template"); + statement("inline vec spvChromaReconstructNearest(texture2d plane0, texture2d plane1, " + "texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("ycbcr.b = plane1.sample(samp, coord, spvForward(options)...).r;"); + statement("ycbcr.r = plane2.sample(samp, coord, spvForward(options)...).r;"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear422CositedEven2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear422CositedEven(texture2d plane0, texture2d " + "plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("if (fract(coord.x * plane1.get_width()) != 0.0)"); + begin_scope(); + statement("ycbcr.br = vec(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), 0.5).rg);"); + end_scope(); + statement("else"); + begin_scope(); + statement("ycbcr.br = plane1.sample(samp, coord, spvForward(options)...).rg;"); + end_scope(); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear422CositedEven3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear422CositedEven(texture2d plane0, texture2d " + "plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("if (fract(coord.x * plane1.get_width()) != 0.0)"); + begin_scope(); + statement("ycbcr.b = T(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), 0.5).r);"); + statement("ycbcr.r = T(mix(plane2.sample(samp, coord, spvForward(options)...), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 0)), 0.5).r);"); + end_scope(); + statement("else"); + begin_scope(); + statement("ycbcr.b = plane1.sample(samp, coord, spvForward(options)...).r;"); + statement("ycbcr.r = plane2.sample(samp, coord, spvForward(options)...).r;"); + end_scope(); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear422Midpoint2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear422Midpoint(texture2d plane0, texture2d " + "plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("int2 offs = int2(fract(coord.x * plane1.get_width()) != 0.0 ? 1 : -1, 0);"); + statement("ycbcr.br = vec(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., offs), 0.25).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear422Midpoint3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear422Midpoint(texture2d plane0, texture2d " + "plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("int2 offs = int2(fract(coord.x * plane1.get_width()) != 0.0 ? 1 : -1, 0);"); + statement("ycbcr.b = T(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., offs), 0.25).r);"); + statement("ycbcr.r = T(mix(plane2.sample(samp, coord, spvForward(options)...), " + "plane2.sample(samp, coord, spvForward(options)..., offs), 0.25).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XCositedEvenYCositedEven(texture2d plane0, " + "texture2d plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("float2 ab = fract(round(coord * float2(plane0.get_width(), plane0.get_height())) * 0.5);"); + statement("ycbcr.br = vec(mix(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XCositedEvenYCositedEven(texture2d plane0, " + "texture2d plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("float2 ab = fract(round(coord * float2(plane0.get_width(), plane0.get_height())) * 0.5);"); + statement("ycbcr.b = T(mix(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).r);"); + statement("ycbcr.r = T(mix(mix(plane2.sample(samp, coord, spvForward(options)...), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane2.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XMidpointYCositedEven(texture2d plane0, " + "texture2d plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0.5, " + "0)) * 0.5);"); + statement("ycbcr.br = vec(mix(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XMidpointYCositedEven(texture2d plane0, " + "texture2d plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0.5, " + "0)) * 0.5);"); + statement("ycbcr.b = T(mix(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).r);"); + statement("ycbcr.r = T(mix(mix(plane2.sample(samp, coord, spvForward(options)...), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane2.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XCositedEvenYMidpoint(texture2d plane0, " + "texture2d plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0, " + "0.5)) * 0.5);"); + statement("ycbcr.br = vec(mix(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XCositedEvenYMidpoint(texture2d plane0, " + "texture2d plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0, " + "0.5)) * 0.5);"); + statement("ycbcr.b = T(mix(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).r);"); + statement("ycbcr.r = T(mix(mix(plane2.sample(samp, coord, spvForward(options)...), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane2.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XMidpointYMidpoint(texture2d plane0, " + "texture2d plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0.5, " + "0.5)) * 0.5);"); + statement("ycbcr.br = vec(mix(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XMidpointYMidpoint(texture2d plane0, " + "texture2d plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, spvForward(options)...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0.5, " + "0.5)) * 0.5);"); + statement("ycbcr.b = T(mix(mix(plane1.sample(samp, coord, spvForward(options)...), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane1.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).r);"); + statement("ycbcr.r = T(mix(mix(plane2.sample(samp, coord, spvForward(options)...), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 0)), ab.x), " + "mix(plane2.sample(samp, coord, spvForward(options)..., int2(0, 1)), " + "plane2.sample(samp, coord, spvForward(options)..., int2(1, 1)), ab.x), ab.y).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplExpandITUFullRange: + statement("template"); + statement("inline vec spvExpandITUFullRange(vec ycbcr, int n)"); + begin_scope(); + statement("ycbcr.br -= exp2(T(n-1))/(exp2(T(n))-1);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplExpandITUNarrowRange: + statement("template"); + statement("inline vec spvExpandITUNarrowRange(vec ycbcr, int n)"); + begin_scope(); + statement("ycbcr.g = (ycbcr.g * (exp2(T(n)) - 1) - ldexp(T(16), n - 8))/ldexp(T(219), n - 8);"); + statement("ycbcr.br = (ycbcr.br * (exp2(T(n)) - 1) - ldexp(T(128), n - 8))/ldexp(T(224), n - 8);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplConvertYCbCrBT709: + statement("// cf. Khronos Data Format Specification, section 15.1.1"); + statement("constant float3x3 spvBT709Factors = {{1, 1, 1}, {0, -0.13397432/0.7152, 1.8556}, {1.5748, " + "-0.33480248/0.7152, 0}};"); + statement(""); + statement("template"); + statement("inline vec spvConvertYCbCrBT709(vec ycbcr)"); + begin_scope(); + statement("vec rgba;"); + statement("rgba.rgb = vec(spvBT709Factors * ycbcr.gbr);"); + statement("rgba.a = ycbcr.a;"); + statement("return rgba;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplConvertYCbCrBT601: + statement("// cf. Khronos Data Format Specification, section 15.1.2"); + statement("constant float3x3 spvBT601Factors = {{1, 1, 1}, {0, -0.202008/0.587, 1.772}, {1.402, " + "-0.419198/0.587, 0}};"); + statement(""); + statement("template"); + statement("inline vec spvConvertYCbCrBT601(vec ycbcr)"); + begin_scope(); + statement("vec rgba;"); + statement("rgba.rgb = vec(spvBT601Factors * ycbcr.gbr);"); + statement("rgba.a = ycbcr.a;"); + statement("return rgba;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplConvertYCbCrBT2020: + statement("// cf. Khronos Data Format Specification, section 15.1.3"); + statement("constant float3x3 spvBT2020Factors = {{1, 1, 1}, {0, -0.11156702/0.6780, 1.8814}, {1.4746, " + "-0.38737742/0.6780, 0}};"); + statement(""); + statement("template"); + statement("inline vec spvConvertYCbCrBT2020(vec ycbcr)"); + begin_scope(); + statement("vec rgba;"); + statement("rgba.rgb = vec(spvBT2020Factors * ycbcr.gbr);"); + statement("rgba.a = ycbcr.a;"); + statement("return rgba;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplDynamicImageSampler: + statement("enum class spvFormatResolution"); + begin_scope(); + statement("_444 = 0,"); + statement("_422,"); + statement("_420"); + end_scope_decl(); + statement(""); + statement("enum class spvChromaFilter"); + begin_scope(); + statement("nearest = 0,"); + statement("linear"); + end_scope_decl(); + statement(""); + statement("enum class spvXChromaLocation"); + begin_scope(); + statement("cosited_even = 0,"); + statement("midpoint"); + end_scope_decl(); + statement(""); + statement("enum class spvYChromaLocation"); + begin_scope(); + statement("cosited_even = 0,"); + statement("midpoint"); + end_scope_decl(); + statement(""); + statement("enum class spvYCbCrModelConversion"); + begin_scope(); + statement("rgb_identity = 0,"); + statement("ycbcr_identity,"); + statement("ycbcr_bt_709,"); + statement("ycbcr_bt_601,"); + statement("ycbcr_bt_2020"); + end_scope_decl(); + statement(""); + statement("enum class spvYCbCrRange"); + begin_scope(); + statement("itu_full = 0,"); + statement("itu_narrow"); + end_scope_decl(); + statement(""); + statement("struct spvComponentBits"); + begin_scope(); + statement("constexpr explicit spvComponentBits(int v) thread : value(v) {}"); + statement("uchar value : 6;"); + end_scope_decl(); + statement("// A class corresponding to metal::sampler which holds sampler"); + statement("// Y'CbCr conversion info."); + statement("struct spvYCbCrSampler"); + begin_scope(); + statement("constexpr spvYCbCrSampler() thread : val(build()) {}"); + statement("template"); + statement("constexpr spvYCbCrSampler(Ts... t) thread : val(build(t...)) {}"); + statement("constexpr spvYCbCrSampler(const thread spvYCbCrSampler& s) thread = default;"); + statement(""); + statement("spvFormatResolution get_resolution() const thread"); + begin_scope(); + statement("return spvFormatResolution((val & resolution_mask) >> resolution_base);"); + end_scope(); + statement("spvChromaFilter get_chroma_filter() const thread"); + begin_scope(); + statement("return spvChromaFilter((val & chroma_filter_mask) >> chroma_filter_base);"); + end_scope(); + statement("spvXChromaLocation get_x_chroma_offset() const thread"); + begin_scope(); + statement("return spvXChromaLocation((val & x_chroma_off_mask) >> x_chroma_off_base);"); + end_scope(); + statement("spvYChromaLocation get_y_chroma_offset() const thread"); + begin_scope(); + statement("return spvYChromaLocation((val & y_chroma_off_mask) >> y_chroma_off_base);"); + end_scope(); + statement("spvYCbCrModelConversion get_ycbcr_model() const thread"); + begin_scope(); + statement("return spvYCbCrModelConversion((val & ycbcr_model_mask) >> ycbcr_model_base);"); + end_scope(); + statement("spvYCbCrRange get_ycbcr_range() const thread"); + begin_scope(); + statement("return spvYCbCrRange((val & ycbcr_range_mask) >> ycbcr_range_base);"); + end_scope(); + statement("int get_bpc() const thread { return (val & bpc_mask) >> bpc_base; }"); + statement(""); + statement("private:"); + statement("ushort val;"); + statement(""); + statement("constexpr static constant ushort resolution_bits = 2;"); + statement("constexpr static constant ushort chroma_filter_bits = 2;"); + statement("constexpr static constant ushort x_chroma_off_bit = 1;"); + statement("constexpr static constant ushort y_chroma_off_bit = 1;"); + statement("constexpr static constant ushort ycbcr_model_bits = 3;"); + statement("constexpr static constant ushort ycbcr_range_bit = 1;"); + statement("constexpr static constant ushort bpc_bits = 6;"); + statement(""); + statement("constexpr static constant ushort resolution_base = 0;"); + statement("constexpr static constant ushort chroma_filter_base = 2;"); + statement("constexpr static constant ushort x_chroma_off_base = 4;"); + statement("constexpr static constant ushort y_chroma_off_base = 5;"); + statement("constexpr static constant ushort ycbcr_model_base = 6;"); + statement("constexpr static constant ushort ycbcr_range_base = 9;"); + statement("constexpr static constant ushort bpc_base = 10;"); + statement(""); + statement( + "constexpr static constant ushort resolution_mask = ((1 << resolution_bits) - 1) << resolution_base;"); + statement("constexpr static constant ushort chroma_filter_mask = ((1 << chroma_filter_bits) - 1) << " + "chroma_filter_base;"); + statement("constexpr static constant ushort x_chroma_off_mask = ((1 << x_chroma_off_bit) - 1) << " + "x_chroma_off_base;"); + statement("constexpr static constant ushort y_chroma_off_mask = ((1 << y_chroma_off_bit) - 1) << " + "y_chroma_off_base;"); + statement("constexpr static constant ushort ycbcr_model_mask = ((1 << ycbcr_model_bits) - 1) << " + "ycbcr_model_base;"); + statement("constexpr static constant ushort ycbcr_range_mask = ((1 << ycbcr_range_bit) - 1) << " + "ycbcr_range_base;"); + statement("constexpr static constant ushort bpc_mask = ((1 << bpc_bits) - 1) << bpc_base;"); + statement(""); + statement("static constexpr ushort build()"); + begin_scope(); + statement("return 0;"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvFormatResolution res, Ts... t)"); + begin_scope(); + statement("return (ushort(res) << resolution_base) | (build(t...) & ~resolution_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvChromaFilter filt, Ts... t)"); + begin_scope(); + statement("return (ushort(filt) << chroma_filter_base) | (build(t...) & ~chroma_filter_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvXChromaLocation loc, Ts... t)"); + begin_scope(); + statement("return (ushort(loc) << x_chroma_off_base) | (build(t...) & ~x_chroma_off_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvYChromaLocation loc, Ts... t)"); + begin_scope(); + statement("return (ushort(loc) << y_chroma_off_base) | (build(t...) & ~y_chroma_off_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvYCbCrModelConversion model, Ts... t)"); + begin_scope(); + statement("return (ushort(model) << ycbcr_model_base) | (build(t...) & ~ycbcr_model_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvYCbCrRange range, Ts... t)"); + begin_scope(); + statement("return (ushort(range) << ycbcr_range_base) | (build(t...) & ~ycbcr_range_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvComponentBits bpc, Ts... t)"); + begin_scope(); + statement("return (ushort(bpc.value) << bpc_base) | (build(t...) & ~bpc_mask);"); + end_scope(); + end_scope_decl(); + statement(""); + statement("// A class which can hold up to three textures and a sampler, including"); + statement("// Y'CbCr conversion info, used to pass combined image-samplers"); + statement("// dynamically to functions."); + statement("template"); + statement("struct spvDynamicImageSampler"); + begin_scope(); + statement("texture2d plane0;"); + statement("texture2d plane1;"); + statement("texture2d plane2;"); + statement("sampler samp;"); + statement("spvYCbCrSampler ycbcr_samp;"); + statement("uint swizzle = 0;"); + statement(""); + if (msl_options.swizzle_texture_samples) + { + statement("constexpr spvDynamicImageSampler(texture2d tex, sampler samp, uint sw) thread :"); + statement(" plane0(tex), samp(samp), swizzle(sw) {}"); + } + else + { + statement("constexpr spvDynamicImageSampler(texture2d tex, sampler samp) thread :"); + statement(" plane0(tex), samp(samp) {}"); + } + statement("constexpr spvDynamicImageSampler(texture2d tex, sampler samp, spvYCbCrSampler ycbcr_samp, " + "uint sw) thread :"); + statement(" plane0(tex), samp(samp), ycbcr_samp(ycbcr_samp), swizzle(sw) {}"); + statement("constexpr spvDynamicImageSampler(texture2d plane0, texture2d plane1,"); + statement(" sampler samp, spvYCbCrSampler ycbcr_samp, uint sw) thread :"); + statement(" plane0(plane0), plane1(plane1), samp(samp), ycbcr_samp(ycbcr_samp), swizzle(sw) {}"); + statement( + "constexpr spvDynamicImageSampler(texture2d plane0, texture2d plane1, texture2d plane2,"); + statement(" sampler samp, spvYCbCrSampler ycbcr_samp, uint sw) thread :"); + statement(" plane0(plane0), plane1(plane1), plane2(plane2), samp(samp), ycbcr_samp(ycbcr_samp), " + "swizzle(sw) {}"); + statement(""); + // XXX This is really hard to follow... I've left comments to make it a bit easier. + statement("template"); + statement("vec do_sample(float2 coord, LodOptions... options) const thread"); + begin_scope(); + statement("if (!is_null_texture(plane1))"); + begin_scope(); + statement("if (ycbcr_samp.get_resolution() == spvFormatResolution::_444 ||"); + statement(" ycbcr_samp.get_chroma_filter() == spvChromaFilter::nearest)"); + begin_scope(); + statement("if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructNearest(plane0, plane1, plane2, samp, coord,"); + statement(" spvForward(options)...);"); + statement( + "return spvChromaReconstructNearest(plane0, plane1, samp, coord, spvForward(options)...);"); + end_scope(); // if (resolution == 422 || chroma_filter == nearest) + statement("switch (ycbcr_samp.get_resolution())"); + begin_scope(); + statement("case spvFormatResolution::_444: break;"); + statement("case spvFormatResolution::_422:"); + begin_scope(); + statement("switch (ycbcr_samp.get_x_chroma_offset())"); + begin_scope(); + statement("case spvXChromaLocation::cosited_even:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear422CositedEven("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, spvForward(options)...);"); + statement(" return spvChromaReconstructLinear422CositedEven("); + statement(" plane0, plane1, samp, coord,"); + statement(" spvForward(options)...);"); + statement("case spvXChromaLocation::midpoint:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear422Midpoint("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, spvForward(options)...);"); + statement(" return spvChromaReconstructLinear422Midpoint("); + statement(" plane0, plane1, samp, coord,"); + statement(" spvForward(options)...);"); + end_scope(); // switch (x_chroma_offset) + end_scope(); // case 422: + statement("case spvFormatResolution::_420:"); + begin_scope(); + statement("switch (ycbcr_samp.get_x_chroma_offset())"); + begin_scope(); + statement("case spvXChromaLocation::cosited_even:"); + begin_scope(); + statement("switch (ycbcr_samp.get_y_chroma_offset())"); + begin_scope(); + statement("case spvYChromaLocation::cosited_even:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear420XCositedEvenYCositedEven("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, spvForward(options)...);"); + statement(" return spvChromaReconstructLinear420XCositedEvenYCositedEven("); + statement(" plane0, plane1, samp, coord,"); + statement(" spvForward(options)...);"); + statement("case spvYChromaLocation::midpoint:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear420XCositedEvenYMidpoint("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, spvForward(options)...);"); + statement(" return spvChromaReconstructLinear420XCositedEvenYMidpoint("); + statement(" plane0, plane1, samp, coord,"); + statement(" spvForward(options)...);"); + end_scope(); // switch (y_chroma_offset) + end_scope(); // case x::cosited_even: + statement("case spvXChromaLocation::midpoint:"); + begin_scope(); + statement("switch (ycbcr_samp.get_y_chroma_offset())"); + begin_scope(); + statement("case spvYChromaLocation::cosited_even:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear420XMidpointYCositedEven("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, spvForward(options)...);"); + statement(" return spvChromaReconstructLinear420XMidpointYCositedEven("); + statement(" plane0, plane1, samp, coord,"); + statement(" spvForward(options)...);"); + statement("case spvYChromaLocation::midpoint:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear420XMidpointYMidpoint("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, spvForward(options)...);"); + statement(" return spvChromaReconstructLinear420XMidpointYMidpoint("); + statement(" plane0, plane1, samp, coord,"); + statement(" spvForward(options)...);"); + end_scope(); // switch (y_chroma_offset) + end_scope(); // case x::midpoint + end_scope(); // switch (x_chroma_offset) + end_scope(); // case 420: + end_scope(); // switch (resolution) + end_scope(); // if (multiplanar) + statement("return plane0.sample(samp, coord, spvForward(options)...);"); + end_scope(); // do_sample() + statement("template "); + statement("vec sample(float2 coord, LodOptions... options) const thread"); + begin_scope(); + statement( + "vec s = spvTextureSwizzle(do_sample(coord, spvForward(options)...), swizzle);"); + statement("if (ycbcr_samp.get_ycbcr_model() == spvYCbCrModelConversion::rgb_identity)"); + statement(" return s;"); + statement(""); + statement("switch (ycbcr_samp.get_ycbcr_range())"); + begin_scope(); + statement("case spvYCbCrRange::itu_full:"); + statement(" s = spvExpandITUFullRange(s, ycbcr_samp.get_bpc());"); + statement(" break;"); + statement("case spvYCbCrRange::itu_narrow:"); + statement(" s = spvExpandITUNarrowRange(s, ycbcr_samp.get_bpc());"); + statement(" break;"); + end_scope(); + statement(""); + statement("switch (ycbcr_samp.get_ycbcr_model())"); + begin_scope(); + statement("case spvYCbCrModelConversion::rgb_identity:"); // Silence Clang warning + statement("case spvYCbCrModelConversion::ycbcr_identity:"); + statement(" return s;"); + statement("case spvYCbCrModelConversion::ycbcr_bt_709:"); + statement(" return spvConvertYCbCrBT709(s);"); + statement("case spvYCbCrModelConversion::ycbcr_bt_601:"); + statement(" return spvConvertYCbCrBT601(s);"); + statement("case spvYCbCrModelConversion::ycbcr_bt_2020:"); + statement(" return spvConvertYCbCrBT2020(s);"); + end_scope(); + end_scope(); + statement(""); + // Sampler Y'CbCr conversion forbids offsets. + statement("vec sample(float2 coord, int2 offset) const thread"); + begin_scope(); + if (msl_options.swizzle_texture_samples) + statement("return spvTextureSwizzle(plane0.sample(samp, coord, offset), swizzle);"); + else + statement("return plane0.sample(samp, coord, offset);"); + end_scope(); + statement("template"); + statement("vec sample(float2 coord, lod_options options, int2 offset) const thread"); + begin_scope(); + if (msl_options.swizzle_texture_samples) + statement("return spvTextureSwizzle(plane0.sample(samp, coord, options, offset), swizzle);"); + else + statement("return plane0.sample(samp, coord, options, offset);"); + end_scope(); + statement("#if __HAVE_MIN_LOD_CLAMP__"); + statement("vec sample(float2 coord, bias b, min_lod_clamp min_lod, int2 offset) const thread"); + begin_scope(); + statement("return plane0.sample(samp, coord, b, min_lod, offset);"); + end_scope(); + statement( + "vec sample(float2 coord, gradient2d grad, min_lod_clamp min_lod, int2 offset) const thread"); + begin_scope(); + statement("return plane0.sample(samp, coord, grad, min_lod, offset);"); + end_scope(); + statement("#endif"); + statement(""); + // Y'CbCr conversion forbids all operations but sampling. + statement("vec read(uint2 coord, uint lod = 0) const thread"); + begin_scope(); + statement("return plane0.read(coord, lod);"); + end_scope(); + statement(""); + statement("vec gather(float2 coord, int2 offset = int2(0), component c = component::x) const thread"); + begin_scope(); + if (msl_options.swizzle_texture_samples) + statement("return spvGatherSwizzle(plane0, samp, swizzle, c, coord, offset);"); + else + statement("return plane0.gather(samp, coord, offset, c);"); + end_scope(); + end_scope_decl(); + statement(""); + + default: + break; + } + } +} + +// Undefined global memory is not allowed in MSL. +// Declare constant and init to zeros. Use {}, as global constructors can break Metal. +void CompilerMSL::declare_undefined_values() +{ + bool emitted = false; + ir.for_each_typed_id([&](uint32_t, SPIRUndef &undef) { + auto &type = this->get(undef.basetype); + // OpUndef can be void for some reason ... + if (type.basetype == SPIRType::Void) + return; + + statement("constant ", variable_decl(type, to_name(undef.self), undef.self), " = {};"); + emitted = true; + }); + + if (emitted) + statement(""); +} + +void CompilerMSL::declare_constant_arrays() +{ + bool fully_inlined = ir.ids_for_type[TypeFunction].size() == 1; + + // MSL cannot declare arrays inline (except when declaring a variable), so we must move them out to + // global constants directly, so we are able to use constants as variable expressions. + bool emitted = false; + + ir.for_each_typed_id([&](uint32_t, SPIRConstant &c) { + if (c.specialization) + return; + + auto &type = this->get(c.constant_type); + // Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries. + // FIXME: However, hoisting constants to main() means we need to pass down constant arrays to leaf functions if they are used there. + // If there are multiple functions in the module, drop this case to avoid breaking use cases which do not need to + // link into Metal libraries. This is hacky. + if (!type.array.empty() && (!fully_inlined || is_scalar(type) || is_vector(type))) + { + auto name = to_name(c.self); + statement("constant ", variable_decl(type, name), " = ", constant_expression(c), ";"); + emitted = true; + } + }); + + if (emitted) + statement(""); +} + +// Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries +void CompilerMSL::declare_complex_constant_arrays() +{ + // If we do not have a fully inlined module, we did not opt in to + // declaring constant arrays of complex types. See CompilerMSL::declare_constant_arrays(). + bool fully_inlined = ir.ids_for_type[TypeFunction].size() == 1; + if (!fully_inlined) + return; + + // MSL cannot declare arrays inline (except when declaring a variable), so we must move them out to + // global constants directly, so we are able to use constants as variable expressions. + bool emitted = false; + + ir.for_each_typed_id([&](uint32_t, SPIRConstant &c) { + if (c.specialization) + return; + + auto &type = this->get(c.constant_type); + if (!type.array.empty() && !(is_scalar(type) || is_vector(type))) + { + auto name = to_name(c.self); + statement("", variable_decl(type, name), " = ", constant_expression(c), ";"); + emitted = true; + } + }); + + if (emitted) + statement(""); +} + +void CompilerMSL::emit_resources() +{ + declare_constant_arrays(); + declare_undefined_values(); + + // Emit the special [[stage_in]] and [[stage_out]] interface blocks which we created. + emit_interface_block(stage_out_var_id); + emit_interface_block(patch_stage_out_var_id); + emit_interface_block(stage_in_var_id); + emit_interface_block(patch_stage_in_var_id); +} + +// Emit declarations for the specialization Metal function constants +void CompilerMSL::emit_specialization_constants_and_structs() +{ + SpecializationConstant wg_x, wg_y, wg_z; + ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + bool emitted = false; + + unordered_set declared_structs; + unordered_set aligned_structs; + + // First, we need to deal with scalar block layout. + // It is possible that a struct may have to be placed at an alignment which does not match the innate alignment of the struct itself. + // In that case, if such a case exists for a struct, we must force that all elements of the struct become packed_ types. + // This makes the struct alignment as small as physically possible. + // When we actually align the struct later, we can insert padding as necessary to make the packed members behave like normally aligned types. + ir.for_each_typed_id([&](uint32_t type_id, const SPIRType &type) { + if (type.basetype == SPIRType::Struct && + has_extended_decoration(type_id, SPIRVCrossDecorationBufferBlockRepacked)) + mark_scalar_layout_structs(type); + }); + + // Very particular use of the soft loop lock. + // align_struct may need to create custom types on the fly, but we don't care about + // these types for purpose of iterating over them in ir.ids_for_type and friends. + auto loop_lock = ir.create_loop_soft_lock(); + + for (auto &id_ : ir.ids_for_constant_or_type) + { + auto &id = ir.ids[id_]; + + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + + if (c.self == workgroup_size_id) + { + // TODO: This can be expressed as a [[threads_per_threadgroup]] input semantic, but we need to know + // the work group size at compile time in SPIR-V, and [[threads_per_threadgroup]] would need to be passed around as a global. + // The work group size may be a specialization constant. + statement("constant uint3 ", builtin_to_glsl(BuiltInWorkgroupSize, StorageClassWorkgroup), + " [[maybe_unused]] = ", constant_expression(get(workgroup_size_id)), ";"); + emitted = true; + } + else if (c.specialization) + { + auto &type = get(c.constant_type); + string sc_type_name = type_to_glsl(type); + string sc_name = to_name(c.self); + string sc_tmp_name = sc_name + "_tmp"; + + // Function constants are only supported in MSL 1.2 and later. + // If we don't support it just declare the "default" directly. + // This "default" value can be overridden to the true specialization constant by the API user. + // Specialization constants which are used as array length expressions cannot be function constants in MSL, + // so just fall back to macros. + if (msl_options.supports_msl_version(1, 2) && has_decoration(c.self, DecorationSpecId) && + !c.is_used_as_array_length) + { + uint32_t constant_id = get_decoration(c.self, DecorationSpecId); + // Only scalar, non-composite values can be function constants. + statement("constant ", sc_type_name, " ", sc_tmp_name, " [[function_constant(", constant_id, + ")]];"); + statement("constant ", sc_type_name, " ", sc_name, " = is_function_constant_defined(", sc_tmp_name, + ") ? ", sc_tmp_name, " : ", constant_expression(c), ";"); + } + else if (has_decoration(c.self, DecorationSpecId)) + { + // Fallback to macro overrides. + c.specialization_constant_macro_name = + constant_value_macro_name(get_decoration(c.self, DecorationSpecId)); + + statement("#ifndef ", c.specialization_constant_macro_name); + statement("#define ", c.specialization_constant_macro_name, " ", constant_expression(c)); + statement("#endif"); + statement("constant ", sc_type_name, " ", sc_name, " = ", c.specialization_constant_macro_name, + ";"); + } + else + { + // Composite specialization constants must be built from other specialization constants. + statement("constant ", sc_type_name, " ", sc_name, " = ", constant_expression(c), ";"); + } + emitted = true; + } + } + else if (id.get_type() == TypeConstantOp) + { + auto &c = id.get(); + auto &type = get(c.basetype); + auto name = to_name(c.self); + statement("constant ", variable_decl(type, name), " = ", constant_op_expression(c), ";"); + emitted = true; + } + else if (id.get_type() == TypeType) + { + // Output non-builtin interface structs. These include local function structs + // and structs nested within uniform and read-write buffers. + auto &type = id.get(); + TypeID type_id = type.self; + + bool is_struct = (type.basetype == SPIRType::Struct) && type.array.empty() && !type.pointer; + bool is_block = + has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); + + bool is_builtin_block = is_block && is_builtin_type(type); + bool is_declarable_struct = is_struct && !is_builtin_block; + + // We'll declare this later. + if (stage_out_var_id && get_stage_out_struct_type().self == type_id) + is_declarable_struct = false; + if (patch_stage_out_var_id && get_patch_stage_out_struct_type().self == type_id) + is_declarable_struct = false; + if (stage_in_var_id && get_stage_in_struct_type().self == type_id) + is_declarable_struct = false; + if (patch_stage_in_var_id && get_patch_stage_in_struct_type().self == type_id) + is_declarable_struct = false; + + // Align and emit declarable structs...but avoid declaring each more than once. + if (is_declarable_struct && declared_structs.count(type_id) == 0) + { + if (emitted) + statement(""); + emitted = false; + + declared_structs.insert(type_id); + + if (has_extended_decoration(type_id, SPIRVCrossDecorationBufferBlockRepacked)) + align_struct(type, aligned_structs); + + // Make sure we declare the underlying struct type, and not the "decorated" type with pointers, etc. + emit_struct(get(type_id)); + } + } + } + + if (emitted) + statement(""); +} + +void CompilerMSL::emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1); + emit_op(result_type, result_id, + join("(isunordered(", to_enclosed_unpacked_expression(op0), ", ", to_enclosed_unpacked_expression(op1), + ") || ", to_enclosed_unpacked_expression(op0), " ", op, " ", to_enclosed_unpacked_expression(op1), + ")"), + forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id, uint32_t ptr) +{ + auto &ptr_type = expression_type(ptr); + auto &result_type = get(result_type_id); + if (ptr_type.storage != StorageClassInput && ptr_type.storage != StorageClassOutput) + return false; + if (ptr_type.storage == StorageClassOutput && get_execution_model() == ExecutionModelTessellationEvaluation) + return false; + + bool multi_patch_tess_ctl = get_execution_model() == ExecutionModelTessellationControl && + msl_options.multi_patch_workgroup && ptr_type.storage == StorageClassInput; + bool flat_matrix = is_matrix(result_type) && ptr_type.storage == StorageClassInput && !multi_patch_tess_ctl; + bool flat_struct = result_type.basetype == SPIRType::Struct && ptr_type.storage == StorageClassInput; + bool flat_data_type = flat_matrix || is_array(result_type) || flat_struct; + if (!flat_data_type) + return false; + + if (has_decoration(ptr, DecorationPatch)) + return false; + + // Now, we must unflatten a composite type and take care of interleaving array access with gl_in/gl_out. + // Lots of painful code duplication since we *really* should not unroll these kinds of loads in entry point fixup + // unless we're forced to do this when the code is emitting inoptimal OpLoads. + string expr; + + uint32_t interface_index = get_extended_decoration(ptr, SPIRVCrossDecorationInterfaceMemberIndex); + auto *var = maybe_get_backing_variable(ptr); + bool ptr_is_io_variable = ir.ids[ptr].get_type() == TypeVariable; + auto &expr_type = get_pointee_type(ptr_type.self); + + const auto &iface_type = expression_type(stage_in_ptr_var_id); + + if (result_type.array.size() > 2) + { + SPIRV_CROSS_THROW("Cannot load tessellation IO variables with more than 2 dimensions."); + } + else if (result_type.array.size() == 2) + { + if (!ptr_is_io_variable) + SPIRV_CROSS_THROW("Loading an array-of-array must be loaded directly from an IO variable."); + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + if (result_type.basetype == SPIRType::Struct || flat_matrix) + SPIRV_CROSS_THROW("Cannot load array-of-array of composite type in tessellation IO."); + + expr += type_to_glsl(result_type) + "({ "; + uint32_t num_control_points = to_array_size_literal(result_type, 1); + uint32_t base_interface_index = interface_index; + + auto &sub_type = get(result_type.parent_type); + + for (uint32_t i = 0; i < num_control_points; i++) + { + expr += type_to_glsl(sub_type) + "({ "; + interface_index = base_interface_index; + uint32_t array_size = to_array_size_literal(result_type, 0); + if (multi_patch_tess_ctl) + { + for (uint32_t j = 0; j < array_size; j++) + { + const uint32_t indices[3] = { i, interface_index, j }; + + AccessChainMeta meta; + expr += + access_chain_internal(stage_in_ptr_var_id, indices, 3, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta); + // If the expression has more vector components than the result type, insert + // a swizzle. This shouldn't happen normally on valid SPIR-V, but it might + // happen if we replace the type of an input variable. + if (!is_matrix(sub_type) && sub_type.basetype != SPIRType::Struct && + expr_type.vecsize > sub_type.vecsize) + expr += vector_swizzle(sub_type.vecsize, 0); + + if (j + 1 < array_size) + expr += ", "; + } + } + else + { + for (uint32_t j = 0; j < array_size; j++, interface_index++) + { + const uint32_t indices[2] = { i, interface_index }; + + AccessChainMeta meta; + expr += + access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta); + if (!is_matrix(sub_type) && sub_type.basetype != SPIRType::Struct && + expr_type.vecsize > sub_type.vecsize) + expr += vector_swizzle(sub_type.vecsize, 0); + + if (j + 1 < array_size) + expr += ", "; + } + } + expr += " })"; + if (i + 1 < num_control_points) + expr += ", "; + } + expr += " })"; + } + else if (flat_struct) + { + bool is_array_of_struct = is_array(result_type); + if (is_array_of_struct && !ptr_is_io_variable) + SPIRV_CROSS_THROW("Loading array of struct from IO variable must come directly from IO variable."); + + uint32_t num_control_points = 1; + if (is_array_of_struct) + { + num_control_points = to_array_size_literal(result_type, 0); + expr += type_to_glsl(result_type) + "({ "; + } + + auto &struct_type = is_array_of_struct ? get(result_type.parent_type) : result_type; + assert(struct_type.array.empty()); + + for (uint32_t i = 0; i < num_control_points; i++) + { + expr += type_to_glsl(struct_type) + "{ "; + for (uint32_t j = 0; j < uint32_t(struct_type.member_types.size()); j++) + { + // The base interface index is stored per variable for structs. + if (var) + { + interface_index = + get_extended_member_decoration(var->self, j, SPIRVCrossDecorationInterfaceMemberIndex); + } + + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + + const auto &mbr_type = get(struct_type.member_types[j]); + const auto &expr_mbr_type = get(expr_type.member_types[j]); + if (is_matrix(mbr_type) && ptr_type.storage == StorageClassInput && !multi_patch_tess_ctl) + { + expr += type_to_glsl(mbr_type) + "("; + for (uint32_t k = 0; k < mbr_type.columns; k++, interface_index++) + { + if (is_array_of_struct) + { + const uint32_t indices[2] = { i, interface_index }; + AccessChainMeta meta; + expr += access_chain_internal( + stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta); + } + else + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_mbr_type.vecsize > mbr_type.vecsize) + expr += vector_swizzle(mbr_type.vecsize, 0); + + if (k + 1 < mbr_type.columns) + expr += ", "; + } + expr += ")"; + } + else if (is_array(mbr_type)) + { + expr += type_to_glsl(mbr_type) + "({ "; + uint32_t array_size = to_array_size_literal(mbr_type, 0); + if (multi_patch_tess_ctl) + { + for (uint32_t k = 0; k < array_size; k++) + { + if (is_array_of_struct) + { + const uint32_t indices[3] = { i, interface_index, k }; + AccessChainMeta meta; + expr += access_chain_internal( + stage_in_ptr_var_id, indices, 3, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta); + } + else + expr += join(to_expression(ptr), ".", to_member_name(iface_type, interface_index), "[", + k, "]"); + if (expr_mbr_type.vecsize > mbr_type.vecsize) + expr += vector_swizzle(mbr_type.vecsize, 0); + + if (k + 1 < array_size) + expr += ", "; + } + } + else + { + for (uint32_t k = 0; k < array_size; k++, interface_index++) + { + if (is_array_of_struct) + { + const uint32_t indices[2] = { i, interface_index }; + AccessChainMeta meta; + expr += access_chain_internal( + stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta); + } + else + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_mbr_type.vecsize > mbr_type.vecsize) + expr += vector_swizzle(mbr_type.vecsize, 0); + + if (k + 1 < array_size) + expr += ", "; + } + } + expr += " })"; + } + else + { + if (is_array_of_struct) + { + const uint32_t indices[2] = { i, interface_index }; + AccessChainMeta meta; + expr += access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, + &meta); + } + else + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_mbr_type.vecsize > mbr_type.vecsize) + expr += vector_swizzle(mbr_type.vecsize, 0); + } + + if (j + 1 < struct_type.member_types.size()) + expr += ", "; + } + expr += " }"; + if (i + 1 < num_control_points) + expr += ", "; + } + if (is_array_of_struct) + expr += " })"; + } + else if (flat_matrix) + { + bool is_array_of_matrix = is_array(result_type); + if (is_array_of_matrix && !ptr_is_io_variable) + SPIRV_CROSS_THROW("Loading array of matrix from IO variable must come directly from IO variable."); + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + + if (is_array_of_matrix) + { + // Loading a matrix from each control point. + uint32_t base_interface_index = interface_index; + uint32_t num_control_points = to_array_size_literal(result_type, 0); + expr += type_to_glsl(result_type) + "({ "; + + auto &matrix_type = get_variable_element_type(get(ptr)); + + for (uint32_t i = 0; i < num_control_points; i++) + { + interface_index = base_interface_index; + expr += type_to_glsl(matrix_type) + "("; + for (uint32_t j = 0; j < result_type.columns; j++, interface_index++) + { + const uint32_t indices[2] = { i, interface_index }; + + AccessChainMeta meta; + expr += + access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta); + if (expr_type.vecsize > result_type.vecsize) + expr += vector_swizzle(result_type.vecsize, 0); + if (j + 1 < result_type.columns) + expr += ", "; + } + expr += ")"; + if (i + 1 < num_control_points) + expr += ", "; + } + + expr += " })"; + } + else + { + expr += type_to_glsl(result_type) + "("; + for (uint32_t i = 0; i < result_type.columns; i++, interface_index++) + { + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_type.vecsize > result_type.vecsize) + expr += vector_swizzle(result_type.vecsize, 0); + if (i + 1 < result_type.columns) + expr += ", "; + } + expr += ")"; + } + } + else if (ptr_is_io_variable) + { + assert(is_array(result_type)); + assert(result_type.array.size() == 1); + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + + // We're loading an array directly from a global variable. + // This means we're loading one member from each control point. + expr += type_to_glsl(result_type) + "({ "; + uint32_t num_control_points = to_array_size_literal(result_type, 0); + + for (uint32_t i = 0; i < num_control_points; i++) + { + const uint32_t indices[2] = { i, interface_index }; + + AccessChainMeta meta; + expr += access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta); + if (expr_type.vecsize > result_type.vecsize) + expr += vector_swizzle(result_type.vecsize, 0); + + if (i + 1 < num_control_points) + expr += ", "; + } + expr += " })"; + } + else + { + // We're loading an array from a concrete control point. + assert(is_array(result_type)); + assert(result_type.array.size() == 1); + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + + expr += type_to_glsl(result_type) + "({ "; + uint32_t array_size = to_array_size_literal(result_type, 0); + for (uint32_t i = 0; i < array_size; i++, interface_index++) + { + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_type.vecsize > result_type.vecsize) + expr += vector_swizzle(result_type.vecsize, 0); + if (i + 1 < array_size) + expr += ", "; + } + expr += " })"; + } + + emit_op(result_type_id, id, expr, false); + register_read(id, ptr, false); + return true; +} + +bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t length) +{ + // If this is a per-vertex output, remap it to the I/O array buffer. + + // Any object which did not go through IO flattening shenanigans will go there instead. + // We will unflatten on-demand instead as needed, but not all possible cases can be supported, especially with arrays. + + auto *var = maybe_get_backing_variable(ops[2]); + bool patch = false; + bool flat_data = false; + bool ptr_is_chain = false; + bool multi_patch = get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup; + + if (var) + { + patch = has_decoration(ops[2], DecorationPatch) || is_patch_block(get_variable_data_type(*var)); + + // Should match strip_array in add_interface_block. + flat_data = var->storage == StorageClassInput || + (var->storage == StorageClassOutput && get_execution_model() == ExecutionModelTessellationControl); + + // We might have a chained access chain, where + // we first take the access chain to the control point, and then we chain into a member or something similar. + // In this case, we need to skip gl_in/gl_out remapping. + ptr_is_chain = var->self != ID(ops[2]); + } + + BuiltIn bi_type = BuiltIn(get_decoration(ops[2], DecorationBuiltIn)); + if (var && flat_data && !patch && + (!is_builtin_variable(*var) || bi_type == BuiltInPosition || bi_type == BuiltInPointSize || + bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance || + get_variable_data_type(*var).basetype == SPIRType::Struct)) + { + AccessChainMeta meta; + SmallVector indices; + uint32_t next_id = ir.increase_bound_by(1); + + indices.reserve(length - 3 + 1); + + uint32_t first_non_array_index = ptr_is_chain ? 3 : 4; + VariableID stage_var_id = var->storage == StorageClassInput ? stage_in_ptr_var_id : stage_out_ptr_var_id; + VariableID ptr = ptr_is_chain ? VariableID(ops[2]) : stage_var_id; + if (!ptr_is_chain) + { + // Index into gl_in/gl_out with first array index. + indices.push_back(ops[3]); + } + + auto &result_ptr_type = get(ops[0]); + + uint32_t const_mbr_id = next_id++; + uint32_t index = get_extended_decoration(var->self, SPIRVCrossDecorationInterfaceMemberIndex); + if (var->storage == StorageClassInput || has_decoration(get_variable_element_type(*var).self, DecorationBlock)) + { + uint32_t i = first_non_array_index; + auto *type = &get_variable_element_type(*var); + if (index == uint32_t(-1) && length >= (first_non_array_index + 1)) + { + // Maybe this is a struct type in the input class, in which case + // we put it as a decoration on the corresponding member. + index = get_extended_member_decoration(var->self, get_constant(ops[first_non_array_index]).scalar(), + SPIRVCrossDecorationInterfaceMemberIndex); + assert(index != uint32_t(-1)); + i++; + type = &get(type->member_types[get_constant(ops[first_non_array_index]).scalar()]); + } + + // In this case, we're poking into flattened structures and arrays, so now we have to + // combine the following indices. If we encounter a non-constant index, + // we're hosed. + for (; i < length; ++i) + { + if ((multi_patch || (!is_array(*type) && !is_matrix(*type))) && type->basetype != SPIRType::Struct) + break; + + auto *c = maybe_get(ops[i]); + if (!c || c->specialization) + SPIRV_CROSS_THROW("Trying to dynamically index into an array interface variable in tessellation. " + "This is currently unsupported."); + + // We're in flattened space, so just increment the member index into IO block. + // We can only do this once in the current implementation, so either: + // Struct, Matrix or 1-dimensional array for a control point. + index += c->scalar(); + + if (type->parent_type) + type = &get(type->parent_type); + else if (type->basetype == SPIRType::Struct) + type = &get(type->member_types[c->scalar()]); + } + + if ((!multi_patch && (is_matrix(result_ptr_type) || is_array(result_ptr_type))) || + result_ptr_type.basetype == SPIRType::Struct) + { + // We're not going to emit the actual member name, we let any further OpLoad take care of that. + // Tag the access chain with the member index we're referencing. + set_extended_decoration(ops[1], SPIRVCrossDecorationInterfaceMemberIndex, index); + } + else + { + // Access the appropriate member of gl_in/gl_out. + set(const_mbr_id, get_uint_type_id(), index, false); + indices.push_back(const_mbr_id); + + // Append any straggling access chain indices. + if (i < length) + indices.insert(indices.end(), ops + i, ops + length); + } + } + else + { + assert(index != uint32_t(-1)); + set(const_mbr_id, get_uint_type_id(), index, false); + indices.push_back(const_mbr_id); + + indices.insert(indices.end(), ops + 4, ops + length); + } + + // We use the pointer to the base of the input/output array here, + // so this is always a pointer chain. + string e; + + if (!ptr_is_chain) + { + // This is the start of an access chain, use ptr_chain to index into control point array. + e = access_chain(ptr, indices.data(), uint32_t(indices.size()), result_ptr_type, &meta, true); + } + else + { + // If we're accessing a struct, we need to use member indices which are based on the IO block, + // not actual struct type, so we have to use a split access chain here where + // first path resolves the control point index, i.e. gl_in[index], and second half deals with + // looking up flattened member name. + + // However, it is possible that we partially accessed a struct, + // by taking pointer to member inside the control-point array. + // For this case, we fall back to a natural access chain since we have already dealt with remapping struct members. + // One way to check this here is if we have 2 implied read expressions. + // First one is the gl_in/gl_out struct itself, then an index into that array. + // If we have traversed further, we use a normal access chain formulation. + auto *ptr_expr = maybe_get(ptr); + if (ptr_expr && ptr_expr->implied_read_expressions.size() == 2) + { + e = join(to_expression(ptr), + access_chain_internal(stage_var_id, indices.data(), uint32_t(indices.size()), + ACCESS_CHAIN_CHAIN_ONLY_BIT, &meta)); + } + else + { + e = access_chain_internal(ptr, indices.data(), uint32_t(indices.size()), 0, &meta); + } + } + + // Get the actual type of the object that was accessed. If it's a vector type and we changed it, + // then we'll need to add a swizzle. + // For this, we can't necessarily rely on the type of the base expression, because it might be + // another access chain, and it will therefore already have the "correct" type. + auto *expr_type = &get_variable_data_type(*var); + if (has_extended_decoration(ops[2], SPIRVCrossDecorationTessIOOriginalInputTypeID)) + expr_type = &get(get_extended_decoration(ops[2], SPIRVCrossDecorationTessIOOriginalInputTypeID)); + for (uint32_t i = 3; i < length; i++) + { + if (!is_array(*expr_type) && expr_type->basetype == SPIRType::Struct) + expr_type = &get(expr_type->member_types[get(ops[i]).scalar()]); + else + expr_type = &get(expr_type->parent_type); + } + if (!is_array(*expr_type) && !is_matrix(*expr_type) && expr_type->basetype != SPIRType::Struct && + expr_type->vecsize > result_ptr_type.vecsize) + e += vector_swizzle(result_ptr_type.vecsize, 0); + + auto &expr = set(ops[1], move(e), ops[0], should_forward(ops[2])); + expr.loaded_from = var->self; + expr.need_transpose = meta.need_transpose; + expr.access_chain = true; + + // Mark the result as being packed if necessary. + if (meta.storage_is_packed) + set_extended_decoration(ops[1], SPIRVCrossDecorationPhysicalTypePacked); + if (meta.storage_physical_type != 0) + set_extended_decoration(ops[1], SPIRVCrossDecorationPhysicalTypeID, meta.storage_physical_type); + if (meta.storage_is_invariant) + set_decoration(ops[1], DecorationInvariant); + // Save the type we found in case the result is used in another access chain. + set_extended_decoration(ops[1], SPIRVCrossDecorationTessIOOriginalInputTypeID, expr_type->self); + + // If we have some expression dependencies in our access chain, this access chain is technically a forwarded + // temporary which could be subject to invalidation. + // Need to assume we're forwarded while calling inherit_expression_depdendencies. + forwarded_temporaries.insert(ops[1]); + // The access chain itself is never forced to a temporary, but its dependencies might. + suppressed_usage_tracking.insert(ops[1]); + + for (uint32_t i = 2; i < length; i++) + { + inherit_expression_dependencies(ops[1], ops[i]); + add_implied_read_expression(expr, ops[i]); + } + + // If we have no dependencies after all, i.e., all indices in the access chain are immutable temporaries, + // we're not forwarded after all. + if (expr.expression_dependencies.empty()) + forwarded_temporaries.erase(ops[1]); + + return true; + } + + // If this is the inner tessellation level, and we're tessellating triangles, + // drop the last index. It isn't an array in this case, so we can't have an + // array reference here. We need to make this ID a variable instead of an + // expression so we don't try to dereference it as a variable pointer. + // Don't do this if the index is a constant 1, though. We need to drop stores + // to that one. + auto *m = ir.find_meta(var ? var->self : ID(0)); + if (get_execution_model() == ExecutionModelTessellationControl && var && m && + m->decoration.builtin_type == BuiltInTessLevelInner && get_entry_point().flags.get(ExecutionModeTriangles)) + { + auto *c = maybe_get(ops[3]); + if (c && c->scalar() == 1) + return false; + auto &dest_var = set(ops[1], *var); + dest_var.basetype = ops[0]; + ir.meta[ops[1]] = ir.meta[ops[2]]; + inherit_expression_dependencies(ops[1], ops[2]); + return true; + } + + return false; +} + +bool CompilerMSL::is_out_of_bounds_tessellation_level(uint32_t id_lhs) +{ + if (!get_entry_point().flags.get(ExecutionModeTriangles)) + return false; + + // In SPIR-V, TessLevelInner always has two elements and TessLevelOuter always has + // four. This is true even if we are tessellating triangles. This allows clients + // to use a single tessellation control shader with multiple tessellation evaluation + // shaders. + // In Metal, however, only the first element of TessLevelInner and the first three + // of TessLevelOuter are accessible. This stems from how in Metal, the tessellation + // levels must be stored to a dedicated buffer in a particular format that depends + // on the patch type. Therefore, in Triangles mode, any access to the second + // inner level or the fourth outer level must be dropped. + const auto *e = maybe_get(id_lhs); + if (!e || !e->access_chain) + return false; + BuiltIn builtin = BuiltIn(get_decoration(e->loaded_from, DecorationBuiltIn)); + if (builtin != BuiltInTessLevelInner && builtin != BuiltInTessLevelOuter) + return false; + auto *c = maybe_get(e->implied_read_expressions[1]); + if (!c) + return false; + return (builtin == BuiltInTessLevelInner && c->scalar() == 1) || + (builtin == BuiltInTessLevelOuter && c->scalar() == 3); +} + +void CompilerMSL::prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, + spv::StorageClass storage, bool &is_packed) +{ + // If there is any risk of writes happening with the access chain in question, + // and there is a risk of concurrent write access to other components, + // we must cast the access chain to a plain pointer to ensure we only access the exact scalars we expect. + // The MSL compiler refuses to allow component-level access for any non-packed vector types. + if (!is_packed && (storage == StorageClassStorageBuffer || storage == StorageClassWorkgroup)) + { + const char *addr_space = storage == StorageClassWorkgroup ? "threadgroup" : "device"; + expr = join("((", addr_space, " ", type_to_glsl(type), "*)&", enclose_expression(expr), ")"); + + // Further indexing should happen with packed rules (array index, not swizzle). + is_packed = true; + } +} + +// Sets the interface member index for an access chain to a pull-model interpolant. +void CompilerMSL::fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length) +{ + auto *var = maybe_get_backing_variable(ops[2]); + if (!var || !pull_model_inputs.count(var->self)) + return; + // Get the base index. + uint32_t interface_index; + auto &var_type = get_variable_data_type(*var); + auto &result_type = get(ops[0]); + auto *type = &var_type; + if (has_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex)) + { + interface_index = get_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex); + } + else + { + // Assume an access chain into a struct variable. + assert(var_type.basetype == SPIRType::Struct); + auto &c = get(ops[3 + var_type.array.size()]); + interface_index = + get_extended_member_decoration(var->self, c.scalar(), SPIRVCrossDecorationInterfaceMemberIndex); + } + // Accumulate indices. We'll have to skip over the one for the struct, if present, because we already accounted + // for that getting the base index. + for (uint32_t i = 3; i < length; ++i) + { + if (is_vector(*type) && is_scalar(result_type)) + { + // We don't want to combine the next index. Actually, we need to save it + // so we know to apply a swizzle to the result of the interpolation. + set_extended_decoration(ops[1], SPIRVCrossDecorationInterpolantComponentExpr, ops[i]); + break; + } + + auto *c = maybe_get(ops[i]); + if (!c || c->specialization) + SPIRV_CROSS_THROW("Trying to dynamically index into an array interface variable using pull-model " + "interpolation. This is currently unsupported."); + + if (type->parent_type) + type = &get(type->parent_type); + else if (type->basetype == SPIRType::Struct) + type = &get(type->member_types[c->scalar()]); + + if (!has_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex) && + i - 3 == var_type.array.size()) + continue; + + interface_index += c->scalar(); + } + // Save this to the access chain itself so we can recover it later when calling an interpolation function. + set_extended_decoration(ops[1], SPIRVCrossDecorationInterfaceMemberIndex, interface_index); +} + +// Override for MSL-specific syntax instructions +void CompilerMSL::emit_instruction(const Instruction &instruction) +{ +#define MSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#define MSL_BOP_CAST(op, type) \ + emit_binary_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) +#define MSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) +#define MSL_QFOP(op) emit_quaternary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], #op) +#define MSL_TFOP(op) emit_trinary_func_op(ops[0], ops[1], ops[2], ops[3], ops[4], #op) +#define MSL_BFOP(op) emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], #op) +#define MSL_BFOP_CAST(op, type) \ + emit_binary_func_op_cast(ops[0], ops[1], ops[2], ops[3], #op, type, opcode_is_sign_invariant(opcode)) +#define MSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) +#define MSL_UNORD_BOP(op) emit_binary_unord_op(ops[0], ops[1], ops[2], ops[3], #op) + + auto ops = stream(instruction); + auto opcode = static_cast(instruction.op); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(instruction); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + switch (opcode) + { + case OpLoad: + { + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + if (is_tessellation_shader()) + { + if (!emit_tessellation_io_load(ops[0], id, ptr)) + CompilerGLSL::emit_instruction(instruction); + } + else + { + // Sample mask input for Metal is not an array + if (BuiltIn(get_decoration(ptr, DecorationBuiltIn)) == BuiltInSampleMask) + set_decoration(id, DecorationBuiltIn, BuiltInSampleMask); + CompilerGLSL::emit_instruction(instruction); + } + break; + } + + // Comparisons + case OpIEqual: + MSL_BOP_CAST(==, int_type); + break; + + case OpLogicalEqual: + case OpFOrdEqual: + MSL_BOP(==); + break; + + case OpINotEqual: + MSL_BOP_CAST(!=, int_type); + break; + + case OpLogicalNotEqual: + case OpFOrdNotEqual: + MSL_BOP(!=); + break; + + case OpUGreaterThan: + MSL_BOP_CAST(>, uint_type); + break; + + case OpSGreaterThan: + MSL_BOP_CAST(>, int_type); + break; + + case OpFOrdGreaterThan: + MSL_BOP(>); + break; + + case OpUGreaterThanEqual: + MSL_BOP_CAST(>=, uint_type); + break; + + case OpSGreaterThanEqual: + MSL_BOP_CAST(>=, int_type); + break; + + case OpFOrdGreaterThanEqual: + MSL_BOP(>=); + break; + + case OpULessThan: + MSL_BOP_CAST(<, uint_type); + break; + + case OpSLessThan: + MSL_BOP_CAST(<, int_type); + break; + + case OpFOrdLessThan: + MSL_BOP(<); + break; + + case OpULessThanEqual: + MSL_BOP_CAST(<=, uint_type); + break; + + case OpSLessThanEqual: + MSL_BOP_CAST(<=, int_type); + break; + + case OpFOrdLessThanEqual: + MSL_BOP(<=); + break; + + case OpFUnordEqual: + MSL_UNORD_BOP(==); + break; + + case OpFUnordNotEqual: + MSL_UNORD_BOP(!=); + break; + + case OpFUnordGreaterThan: + MSL_UNORD_BOP(>); + break; + + case OpFUnordGreaterThanEqual: + MSL_UNORD_BOP(>=); + break; + + case OpFUnordLessThan: + MSL_UNORD_BOP(<); + break; + + case OpFUnordLessThanEqual: + MSL_UNORD_BOP(<=); + break; + + // Derivatives + case OpDPdx: + case OpDPdxFine: + case OpDPdxCoarse: + MSL_UFOP(dfdx); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdy: + case OpDPdyFine: + case OpDPdyCoarse: + MSL_UFOP(dfdy); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidth: + case OpFwidthCoarse: + case OpFwidthFine: + MSL_UFOP(fwidth); + register_control_dependent_expression(ops[1]); + break; + + // Bitfield + case OpBitFieldInsert: + { + emit_bitfield_insert_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], "insert_bits", SPIRType::UInt); + break; + } + + case OpBitFieldSExtract: + { + emit_trinary_func_op_bitextract(ops[0], ops[1], ops[2], ops[3], ops[4], "extract_bits", int_type, int_type, + SPIRType::UInt, SPIRType::UInt); + break; + } + + case OpBitFieldUExtract: + { + emit_trinary_func_op_bitextract(ops[0], ops[1], ops[2], ops[3], ops[4], "extract_bits", uint_type, uint_type, + SPIRType::UInt, SPIRType::UInt); + break; + } + + case OpBitReverse: + // BitReverse does not have issues with sign since result type must match input type. + MSL_UFOP(reverse_bits); + break; + + case OpBitCount: + { + auto basetype = expression_type(ops[2]).basetype; + emit_unary_func_op_cast(ops[0], ops[1], ops[2], "popcount", basetype, basetype); + break; + } + + case OpFRem: + MSL_BFOP(fmod); + break; + + case OpFMul: + if (msl_options.invariant_float_math) + MSL_BFOP(spvFMul); + else + MSL_BOP(*); + break; + + case OpFAdd: + if (msl_options.invariant_float_math) + MSL_BFOP(spvFAdd); + else + MSL_BOP(+); + break; + + // Atomics + case OpAtomicExchange: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t mem_sem = ops[4]; + uint32_t val = ops[5]; + emit_atomic_func_op(result_type, id, "atomic_exchange_explicit", mem_sem, mem_sem, false, ptr, val); + break; + } + + case OpAtomicCompareExchange: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t mem_sem_pass = ops[4]; + uint32_t mem_sem_fail = ops[5]; + uint32_t val = ops[6]; + uint32_t comp = ops[7]; + emit_atomic_func_op(result_type, id, "atomic_compare_exchange_weak_explicit", mem_sem_pass, mem_sem_fail, true, + ptr, comp, true, false, val); + break; + } + + case OpAtomicCompareExchangeWeak: + SPIRV_CROSS_THROW("OpAtomicCompareExchangeWeak is only supported in kernel profile."); + + case OpAtomicLoad: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t mem_sem = ops[4]; + emit_atomic_func_op(result_type, id, "atomic_load_explicit", mem_sem, mem_sem, false, ptr, 0); + break; + } + + case OpAtomicStore: + { + uint32_t result_type = expression_type(ops[0]).self; + uint32_t id = ops[0]; + uint32_t ptr = ops[0]; + uint32_t mem_sem = ops[2]; + uint32_t val = ops[3]; + emit_atomic_func_op(result_type, id, "atomic_store_explicit", mem_sem, mem_sem, false, ptr, val); + break; + } + +#define MSL_AFMO_IMPL(op, valsrc, valconst) \ + do \ + { \ + uint32_t result_type = ops[0]; \ + uint32_t id = ops[1]; \ + uint32_t ptr = ops[2]; \ + uint32_t mem_sem = ops[4]; \ + uint32_t val = valsrc; \ + emit_atomic_func_op(result_type, id, "atomic_fetch_" #op "_explicit", mem_sem, mem_sem, false, ptr, val, \ + false, valconst); \ + } while (false) + +#define MSL_AFMO(op) MSL_AFMO_IMPL(op, ops[5], false) +#define MSL_AFMIO(op) MSL_AFMO_IMPL(op, 1, true) + + case OpAtomicIIncrement: + MSL_AFMIO(add); + break; + + case OpAtomicIDecrement: + MSL_AFMIO(sub); + break; + + case OpAtomicIAdd: + MSL_AFMO(add); + break; + + case OpAtomicISub: + MSL_AFMO(sub); + break; + + case OpAtomicSMin: + case OpAtomicUMin: + MSL_AFMO(min); + break; + + case OpAtomicSMax: + case OpAtomicUMax: + MSL_AFMO(max); + break; + + case OpAtomicAnd: + MSL_AFMO(and); + break; + + case OpAtomicOr: + MSL_AFMO(or); + break; + + case OpAtomicXor: + MSL_AFMO(xor); + break; + + // Images + + // Reads == Fetches in Metal + case OpImageRead: + { + // Mark that this shader reads from this image + uint32_t img_id = ops[2]; + auto &type = expression_type(img_id); + if (type.image.dim != DimSubpassData) + { + auto *p_var = maybe_get_backing_variable(img_id); + if (p_var && has_decoration(p_var->self, DecorationNonReadable)) + { + unset_decoration(p_var->self, DecorationNonReadable); + force_recompile(); + } + } + + emit_texture_op(instruction, false); + break; + } + + // Emulate texture2D atomic operations + case OpImageTexelPointer: + { + // When using the pointer, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(ops[2]); + if (var && atomic_image_vars.count(var->self)) + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + std::string coord = to_expression(ops[3]); + auto &type = expression_type(ops[2]); + if (type.image.dim == Dim2D) + { + coord = join("spvImage2DAtomicCoord(", coord, ", ", to_expression(ops[2]), ")"); + } + + auto &e = set(id, join(to_expression(ops[2]), "_atomic[", coord, "]"), result_type, true); + e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, ops[3]); + } + else + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto &e = + set(id, join(to_expression(ops[2]), ", ", to_expression(ops[3])), result_type, true); + + // When using the pointer, we need to know which variable it is actually loaded from. + e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, ops[3]); + } + break; + } + + case OpImageWrite: + { + uint32_t img_id = ops[0]; + uint32_t coord_id = ops[1]; + uint32_t texel_id = ops[2]; + const uint32_t *opt = &ops[3]; + uint32_t length = instruction.length - 3; + + // Bypass pointers because we need the real image struct + auto &type = expression_type(img_id); + auto &img_type = get(type.self); + + // Ensure this image has been marked as being written to and force a + // recommpile so that the image type output will include write access + auto *p_var = maybe_get_backing_variable(img_id); + if (p_var && has_decoration(p_var->self, DecorationNonWritable)) + { + unset_decoration(p_var->self, DecorationNonWritable); + force_recompile(); + } + + bool forward = false; + uint32_t bias = 0; + uint32_t lod = 0; + uint32_t flags = 0; + + if (length) + { + flags = *opt++; + length--; + } + + auto test = [&](uint32_t &v, uint32_t flag) { + if (length && (flags & flag)) + { + v = *opt++; + length--; + } + }; + + test(bias, ImageOperandsBiasMask); + test(lod, ImageOperandsLodMask); + + auto &texel_type = expression_type(texel_id); + auto store_type = texel_type; + store_type.vecsize = 4; + + TextureFunctionArguments args = {}; + args.base.img = img_id; + args.base.imgtype = &img_type; + args.base.is_fetch = true; + args.coord = coord_id; + args.lod = lod; + statement(join(to_expression(img_id), ".write(", + remap_swizzle(store_type, texel_type.vecsize, to_expression(texel_id)), ", ", + CompilerMSL::to_function_args(args, &forward), ");")); + + if (p_var && variable_storage_is_aliased(*p_var)) + flush_all_aliased_variables(); + + break; + } + + case OpImageQuerySize: + case OpImageQuerySizeLod: + { + uint32_t rslt_type_id = ops[0]; + auto &rslt_type = get(rslt_type_id); + + uint32_t id = ops[1]; + + uint32_t img_id = ops[2]; + string img_exp = to_expression(img_id); + auto &img_type = expression_type(img_id); + Dim img_dim = img_type.image.dim; + bool img_is_array = img_type.image.arrayed; + + if (img_type.basetype != SPIRType::Image) + SPIRV_CROSS_THROW("Invalid type for OpImageQuerySize."); + + string lod; + if (opcode == OpImageQuerySizeLod) + { + // LOD index defaults to zero, so don't bother outputing level zero index + string decl_lod = to_expression(ops[3]); + if (decl_lod != "0") + lod = decl_lod; + } + + string expr = type_to_glsl(rslt_type) + "("; + expr += img_exp + ".get_width(" + lod + ")"; + + if (img_dim == Dim2D || img_dim == DimCube || img_dim == Dim3D) + expr += ", " + img_exp + ".get_height(" + lod + ")"; + + if (img_dim == Dim3D) + expr += ", " + img_exp + ".get_depth(" + lod + ")"; + + if (img_is_array) + { + expr += ", " + img_exp + ".get_array_size()"; + if (img_dim == DimCube && msl_options.emulate_cube_array) + expr += " / 6"; + } + + expr += ")"; + + emit_op(rslt_type_id, id, expr, should_forward(img_id)); + + break; + } + + case OpImageQueryLod: + { + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("ImageQueryLod is only supported on MSL 2.2 and up."); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t image_id = ops[2]; + uint32_t coord_id = ops[3]; + emit_uninitialized_temporary_expression(result_type, id); + + auto sampler_expr = to_sampler_expression(image_id); + auto *combined = maybe_get(image_id); + auto image_expr = combined ? to_expression(combined->image) : to_expression(image_id); + + // TODO: It is unclear if calculcate_clamped_lod also conditionally rounds + // the reported LOD based on the sampler. NEAREST miplevel should + // round the LOD, but LINEAR miplevel should not round. + // Let's hope this does not become an issue ... + statement(to_expression(id), ".x = ", image_expr, ".calculate_clamped_lod(", sampler_expr, ", ", + to_expression(coord_id), ");"); + statement(to_expression(id), ".y = ", image_expr, ".calculate_unclamped_lod(", sampler_expr, ", ", + to_expression(coord_id), ");"); + register_control_dependent_expression(id); + break; + } + +#define MSL_ImgQry(qrytype) \ + do \ + { \ + uint32_t rslt_type_id = ops[0]; \ + auto &rslt_type = get(rslt_type_id); \ + uint32_t id = ops[1]; \ + uint32_t img_id = ops[2]; \ + string img_exp = to_expression(img_id); \ + string expr = type_to_glsl(rslt_type) + "(" + img_exp + ".get_num_" #qrytype "())"; \ + emit_op(rslt_type_id, id, expr, should_forward(img_id)); \ + } while (false) + + case OpImageQueryLevels: + MSL_ImgQry(mip_levels); + break; + + case OpImageQuerySamples: + MSL_ImgQry(samples); + break; + + case OpImage: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto *combined = maybe_get(ops[2]); + + if (combined) + { + auto &e = emit_op(result_type, id, to_expression(combined->image), true, true); + auto *var = maybe_get_backing_variable(combined->image); + if (var) + e.loaded_from = var->self; + } + else + { + auto *var = maybe_get_backing_variable(ops[2]); + SPIRExpression *e; + if (var && has_extended_decoration(var->self, SPIRVCrossDecorationDynamicImageSampler)) + e = &emit_op(result_type, id, join(to_expression(ops[2]), ".plane0"), true, true); + else + e = &emit_op(result_type, id, to_expression(ops[2]), true, true); + if (var) + e->loaded_from = var->self; + } + break; + } + + // Casting + case OpQuantizeToF16: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t arg = ops[2]; + + string exp; + auto &type = get(result_type); + + switch (type.vecsize) + { + case 1: + exp = join("float(half(", to_expression(arg), "))"); + break; + case 2: + exp = join("float2(half2(", to_expression(arg), "))"); + break; + case 3: + exp = join("float3(half3(", to_expression(arg), "))"); + break; + case 4: + exp = join("float4(half4(", to_expression(arg), "))"); + break; + default: + SPIRV_CROSS_THROW("Illegal argument to OpQuantizeToF16."); + } + + emit_op(result_type, id, exp, should_forward(arg)); + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + if (is_tessellation_shader()) + { + if (!emit_tessellation_access_chain(ops, instruction.length)) + CompilerGLSL::emit_instruction(instruction); + } + else + CompilerGLSL::emit_instruction(instruction); + fix_up_interpolant_access_chain(ops, instruction.length); + break; + + case OpStore: + if (is_out_of_bounds_tessellation_level(ops[0])) + break; + + if (maybe_emit_array_assignment(ops[0], ops[1])) + break; + + CompilerGLSL::emit_instruction(instruction); + break; + + // Compute barriers + case OpMemoryBarrier: + emit_barrier(0, ops[0], ops[1]); + break; + + case OpControlBarrier: + // In GLSL a memory barrier is often followed by a control barrier. + // But in MSL, memory barriers are also control barriers, so don't + // emit a simple control barrier if a memory barrier has just been emitted. + if (previous_instruction_opcode != OpMemoryBarrier) + emit_barrier(ops[0], ops[1], ops[2]); + break; + + case OpOuterProduct: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2]; + uint32_t b = ops[3]; + + auto &type = get(result_type); + string expr = type_to_glsl_constructor(type); + expr += "("; + for (uint32_t col = 0; col < type.columns; col++) + { + expr += to_enclosed_expression(a); + expr += " * "; + expr += to_extract_component_expression(b, col); + if (col + 1 < type.columns) + expr += ", "; + } + expr += ")"; + emit_op(result_type, id, expr, should_forward(a) && should_forward(b)); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + break; + } + + case OpVectorTimesMatrix: + case OpMatrixTimesVector: + { + if (!msl_options.invariant_float_math) + { + CompilerGLSL::emit_instruction(instruction); + break; + } + + // If the matrix needs transpose, just flip the multiply order. + auto *e = maybe_get(ops[opcode == OpMatrixTimesVector ? 2 : 3]); + if (e && e->need_transpose) + { + e->need_transpose = false; + string expr; + + if (opcode == OpMatrixTimesVector) + { + expr = join("spvFMulVectorMatrix(", to_enclosed_unpacked_expression(ops[3]), ", ", + to_unpacked_row_major_matrix_expression(ops[2]), ")"); + } + else + { + expr = join("spvFMulMatrixVector(", to_unpacked_row_major_matrix_expression(ops[3]), ", ", + to_enclosed_unpacked_expression(ops[2]), ")"); + } + + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + emit_op(ops[0], ops[1], expr, forward); + e->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + { + if (opcode == OpMatrixTimesVector) + MSL_BFOP(spvFMulMatrixVector); + else + MSL_BFOP(spvFMulVectorMatrix); + } + break; + } + + case OpMatrixTimesMatrix: + { + if (!msl_options.invariant_float_math) + { + CompilerGLSL::emit_instruction(instruction); + break; + } + + auto *a = maybe_get(ops[2]); + auto *b = maybe_get(ops[3]); + + // If both matrices need transpose, we can multiply in flipped order and tag the expression as transposed. + // a^T * b^T = (b * a)^T. + if (a && b && a->need_transpose && b->need_transpose) + { + a->need_transpose = false; + b->need_transpose = false; + + auto expr = + join("spvFMulMatrixMatrix(", enclose_expression(to_unpacked_row_major_matrix_expression(ops[3])), ", ", + enclose_expression(to_unpacked_row_major_matrix_expression(ops[2])), ")"); + + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + auto &e = emit_op(ops[0], ops[1], expr, forward); + e.need_transpose = true; + a->need_transpose = true; + b->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + MSL_BFOP(spvFMulMatrixMatrix); + + break; + } + + case OpIAddCarry: + case OpISubBorrow: + { + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, result_id); + + auto &res_type = get(type.member_types[1]); + if (opcode == OpIAddCarry) + { + statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", to_enclosed_expression(op0), " + ", + to_enclosed_expression(op1), ";"); + statement(to_expression(result_id), ".", to_member_name(type, 1), " = select(", type_to_glsl(res_type), + "(1), ", type_to_glsl(res_type), "(0), ", to_expression(result_id), ".", to_member_name(type, 0), + " >= max(", to_expression(op0), ", ", to_expression(op1), "));"); + } + else + { + statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", to_enclosed_expression(op0), " - ", + to_enclosed_expression(op1), ";"); + statement(to_expression(result_id), ".", to_member_name(type, 1), " = select(", type_to_glsl(res_type), + "(1), ", type_to_glsl(res_type), "(0), ", to_enclosed_expression(op0), + " >= ", to_enclosed_expression(op1), ");"); + } + break; + } + + case OpUMulExtended: + case OpSMulExtended: + { + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, result_id); + + statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", to_enclosed_expression(op0), " * ", + to_enclosed_expression(op1), ";"); + statement(to_expression(result_id), ".", to_member_name(type, 1), " = mulhi(", to_expression(op0), ", ", + to_expression(op1), ");"); + break; + } + + case OpArrayLength: + { + auto &type = expression_type(ops[2]); + uint32_t offset = type_struct_member_offset(type, ops[3]); + uint32_t stride = type_struct_member_array_stride(type, ops[3]); + + auto expr = join("(", to_buffer_size_expression(ops[2]), " - ", offset, ") / ", stride); + emit_op(ops[0], ops[1], expr, true); + break; + } + + // SPV_INTEL_shader_integer_functions2 + case OpUCountLeadingZerosINTEL: + MSL_UFOP(clz); + break; + + case OpUCountTrailingZerosINTEL: + MSL_UFOP(ctz); + break; + + case OpAbsISubINTEL: + case OpAbsUSubINTEL: + MSL_BFOP(absdiff); + break; + + case OpIAddSatINTEL: + case OpUAddSatINTEL: + MSL_BFOP(addsat); + break; + + case OpIAverageINTEL: + case OpUAverageINTEL: + MSL_BFOP(hadd); + break; + + case OpIAverageRoundedINTEL: + case OpUAverageRoundedINTEL: + MSL_BFOP(rhadd); + break; + + case OpISubSatINTEL: + case OpUSubSatINTEL: + MSL_BFOP(subsat); + break; + + case OpIMul32x16INTEL: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2], b = ops[3]; + bool forward = should_forward(a) && should_forward(b); + emit_op(result_type, id, join("int(short(", to_expression(a), ")) * int(short(", to_expression(b), "))"), + forward); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + break; + } + + case OpUMul32x16INTEL: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2], b = ops[3]; + bool forward = should_forward(a) && should_forward(b); + emit_op(result_type, id, join("uint(ushort(", to_expression(a), ")) * uint(ushort(", to_expression(b), "))"), + forward); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + break; + } + + // SPV_EXT_demote_to_helper_invocation + case OpDemoteToHelperInvocationEXT: + if (!msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("discard_fragment() does not formally have demote semantics until MSL 2.3."); + CompilerGLSL::emit_instruction(instruction); + break; + + case OpIsHelperInvocationEXT: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires MSL 2.3 on iOS."); + else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires MSL 2.1 on macOS."); + emit_op(ops[0], ops[1], "simd_is_helper_thread()", false); + break; + + case OpBeginInvocationInterlockEXT: + case OpEndInvocationInterlockEXT: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("Raster order groups require MSL 2.0."); + break; // Nothing to do in the body + + default: + CompilerGLSL::emit_instruction(instruction); + break; + } + + previous_instruction_opcode = opcode; +} + +void CompilerMSL::emit_texture_op(const Instruction &i, bool sparse) +{ + if (sparse) + SPIRV_CROSS_THROW("Sparse feedback not yet supported in MSL."); + + if (msl_options.use_framebuffer_fetch_subpasses) + { + auto *ops = stream(i); + + uint32_t result_type_id = ops[0]; + uint32_t id = ops[1]; + uint32_t img = ops[2]; + + auto &type = expression_type(img); + auto &imgtype = get(type.self); + + // Use Metal's native frame-buffer fetch API for subpass inputs. + if (imgtype.image.dim == DimSubpassData) + { + // Subpass inputs cannot be invalidated, + // so just forward the expression directly. + string expr = to_expression(img); + emit_op(result_type_id, id, expr, true); + return; + } + } + + // Fallback to default implementation + CompilerGLSL::emit_texture_op(i, sparse); +} + +void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem) +{ + if (get_execution_model() != ExecutionModelGLCompute && get_execution_model() != ExecutionModelTessellationControl) + return; + + uint32_t exe_scope = id_exe_scope ? evaluate_constant_u32(id_exe_scope) : uint32_t(ScopeInvocation); + uint32_t mem_scope = id_mem_scope ? evaluate_constant_u32(id_mem_scope) : uint32_t(ScopeInvocation); + // Use the wider of the two scopes (smaller value) + exe_scope = min(exe_scope, mem_scope); + + string bar_stmt; + if ((msl_options.is_ios() && msl_options.supports_msl_version(1, 2)) || msl_options.supports_msl_version(2)) + bar_stmt = exe_scope < ScopeSubgroup ? "threadgroup_barrier" : "simdgroup_barrier"; + else + bar_stmt = "threadgroup_barrier"; + bar_stmt += "("; + + uint32_t mem_sem = id_mem_sem ? evaluate_constant_u32(id_mem_sem) : uint32_t(MemorySemanticsMaskNone); + + // Use the | operator to combine flags if we can. + if (msl_options.supports_msl_version(1, 2)) + { + string mem_flags = ""; + // For tesc shaders, this also affects objects in the Output storage class. + // Since in Metal, these are placed in a device buffer, we have to sync device memory here. + if (get_execution_model() == ExecutionModelTessellationControl || + (mem_sem & (MemorySemanticsUniformMemoryMask | MemorySemanticsCrossWorkgroupMemoryMask))) + mem_flags += "mem_flags::mem_device"; + + // Fix tessellation patch function processing + if (get_execution_model() == ExecutionModelTessellationControl || + (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask))) + { + if (!mem_flags.empty()) + mem_flags += " | "; + mem_flags += "mem_flags::mem_threadgroup"; + } + if (mem_sem & MemorySemanticsImageMemoryMask) + { + if (!mem_flags.empty()) + mem_flags += " | "; + mem_flags += "mem_flags::mem_texture"; + } + + if (mem_flags.empty()) + mem_flags = "mem_flags::mem_none"; + + bar_stmt += mem_flags; + } + else + { + if ((mem_sem & (MemorySemanticsUniformMemoryMask | MemorySemanticsCrossWorkgroupMemoryMask)) && + (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask))) + bar_stmt += "mem_flags::mem_device_and_threadgroup"; + else if (mem_sem & (MemorySemanticsUniformMemoryMask | MemorySemanticsCrossWorkgroupMemoryMask)) + bar_stmt += "mem_flags::mem_device"; + else if (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask)) + bar_stmt += "mem_flags::mem_threadgroup"; + else if (mem_sem & MemorySemanticsImageMemoryMask) + bar_stmt += "mem_flags::mem_texture"; + else + bar_stmt += "mem_flags::mem_none"; + } + + bar_stmt += ");"; + + statement(bar_stmt); + + assert(current_emitting_block); + flush_control_dependent_expressions(current_emitting_block->self); + flush_all_active_variables(); +} + +void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id, StorageClass lhs_storage, + StorageClass rhs_storage) +{ + // Allow Metal to use the array template to make arrays a value type. + // This, however, cannot be used for threadgroup address specifiers, so consider the custom array copy as fallback. + bool lhs_thread = (lhs_storage == StorageClassOutput || lhs_storage == StorageClassFunction || + lhs_storage == StorageClassGeneric || lhs_storage == StorageClassPrivate); + bool rhs_thread = (rhs_storage == StorageClassInput || rhs_storage == StorageClassFunction || + rhs_storage == StorageClassGeneric || rhs_storage == StorageClassPrivate); + + // If threadgroup storage qualifiers are *not* used: + // Avoid spvCopy* wrapper functions; Otherwise, spvUnsafeArray<> template cannot be used with that storage qualifier. + if (lhs_thread && rhs_thread && !using_builtin_array()) + { + statement(lhs, " = ", to_expression(rhs_id), ";"); + } + else + { + // Assignment from an array initializer is fine. + auto &type = expression_type(rhs_id); + auto *var = maybe_get_backing_variable(rhs_id); + + // Unfortunately, we cannot template on address space in MSL, + // so explicit address space redirection it is ... + bool is_constant = false; + if (ir.ids[rhs_id].get_type() == TypeConstant) + { + is_constant = true; + } + else if (var && var->remapped_variable && var->statically_assigned && + ir.ids[var->static_expression].get_type() == TypeConstant) + { + is_constant = true; + } + else if (rhs_storage == StorageClassUniform) + { + is_constant = true; + } + + // For the case where we have OpLoad triggering an array copy, + // we cannot easily detect this case ahead of time since it's + // context dependent. We might have to force a recompile here + // if this is the only use of array copies in our shader. + if (type.array.size() > 1) + { + if (type.array.size() > kArrayCopyMultidimMax) + SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays."); + auto func = static_cast(SPVFuncImplArrayCopyMultidimBase + type.array.size()); + add_spv_func_and_recompile(func); + } + else + add_spv_func_and_recompile(SPVFuncImplArrayCopy); + + const char *tag = nullptr; + if (lhs_thread && is_constant) + tag = "FromConstantToStack"; + else if (lhs_storage == StorageClassWorkgroup && is_constant) + tag = "FromConstantToThreadGroup"; + else if (lhs_thread && rhs_thread) + tag = "FromStackToStack"; + else if (lhs_storage == StorageClassWorkgroup && rhs_thread) + tag = "FromStackToThreadGroup"; + else if (lhs_thread && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToStack"; + else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToThreadGroup"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && is_constant) + tag = "FromConstantToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_thread) + tag = "FromStackToDevice"; + else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToThreadGroup"; + else if (lhs_thread && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToStack"; + else + SPIRV_CROSS_THROW("Unknown storage class used for copying arrays."); + + // Pass internal array of spvUnsafeArray<> into wrapper functions + if (lhs_thread && !msl_options.force_native_arrays) + statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ".elements, ", to_expression(rhs_id), ");"); + else if (rhs_thread && !msl_options.force_native_arrays) + statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ".elements);"); + else + statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");"); + } +} + +// Since MSL does not allow arrays to be copied via simple variable assignment, +// if the LHS and RHS represent an assignment of an entire array, it must be +// implemented by calling an array copy function. +// Returns whether the struct assignment was emitted. +bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs) +{ + // We only care about assignments of an entire array + auto &type = expression_type(id_rhs); + if (type.array.size() == 0) + return false; + + auto *var = maybe_get(id_lhs); + + // Is this a remapped, static constant? Don't do anything. + if (var && var->remapped_variable && var->statically_assigned) + return true; + + if (ir.ids[id_rhs].get_type() == TypeConstant && var && var->deferred_declaration) + { + // Special case, if we end up declaring a variable when assigning the constant array, + // we can avoid the copy by directly assigning the constant expression. + // This is likely necessary to be able to use a variable as a true look-up table, as it is unlikely + // the compiler will be able to optimize the spvArrayCopy() into a constant LUT. + // After a variable has been declared, we can no longer assign constant arrays in MSL unfortunately. + statement(to_expression(id_lhs), " = ", constant_expression(get(id_rhs)), ";"); + return true; + } + + // Ensure the LHS variable has been declared + auto *p_v_lhs = maybe_get_backing_variable(id_lhs); + if (p_v_lhs) + flush_variable_declaration(p_v_lhs->self); + + emit_array_copy(to_expression(id_lhs), id_rhs, get_expression_effective_storage_class(id_lhs), + get_expression_effective_storage_class(id_rhs)); + register_write(id_lhs); + + return true; +} + +// Emits one of the atomic functions. In MSL, the atomic functions operate on pointers +void CompilerMSL::emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, uint32_t mem_order_1, + uint32_t mem_order_2, bool has_mem_order_2, uint32_t obj, uint32_t op1, + bool op1_is_pointer, bool op1_is_literal, uint32_t op2) +{ + string exp = string(op) + "("; + + auto &type = get_pointee_type(expression_type(obj)); + exp += "("; + auto *var = maybe_get_backing_variable(obj); + if (!var) + SPIRV_CROSS_THROW("No backing variable for atomic operation."); + + // Emulate texture2D atomic operations + const auto &res_type = get(var->basetype); + if (res_type.storage == StorageClassUniformConstant && res_type.basetype == SPIRType::Image) + { + exp += "device"; + } + else + { + exp += get_argument_address_space(*var); + } + + exp += " atomic_"; + exp += type_to_glsl(type); + exp += "*)"; + + exp += "&"; + exp += to_enclosed_expression(obj); + + bool is_atomic_compare_exchange_strong = op1_is_pointer && op1; + + if (is_atomic_compare_exchange_strong) + { + assert(strcmp(op, "atomic_compare_exchange_weak_explicit") == 0); + assert(op2); + assert(has_mem_order_2); + exp += ", &"; + exp += to_name(result_id); + exp += ", "; + exp += to_expression(op2); + exp += ", "; + exp += get_memory_order(mem_order_1); + exp += ", "; + exp += get_memory_order(mem_order_2); + exp += ")"; + + // MSL only supports the weak atomic compare exchange, so emit a CAS loop here. + // The MSL function returns false if the atomic write fails OR the comparison test fails, + // so we must validate that it wasn't the comparison test that failed before continuing + // the CAS loop, otherwise it will loop infinitely, with the comparison test always failing. + // The function updates the comparitor value from the memory value, so the additional + // comparison test evaluates the memory value against the expected value. + emit_uninitialized_temporary_expression(result_type, result_id); + statement("do"); + begin_scope(); + statement(to_name(result_id), " = ", to_expression(op1), ";"); + end_scope_decl(join("while (!", exp, " && ", to_name(result_id), " == ", to_enclosed_expression(op1), ")")); + } + else + { + assert(strcmp(op, "atomic_compare_exchange_weak_explicit") != 0); + if (op1) + { + if (op1_is_literal) + exp += join(", ", op1); + else + exp += ", " + to_expression(op1); + } + if (op2) + exp += ", " + to_expression(op2); + + exp += string(", ") + get_memory_order(mem_order_1); + if (has_mem_order_2) + exp += string(", ") + get_memory_order(mem_order_2); + + exp += ")"; + + if (strcmp(op, "atomic_store_explicit") != 0) + emit_op(result_type, result_id, exp, false); + else + statement(exp, ";"); + } + + flush_all_atomic_capable_variables(); +} + +// Metal only supports relaxed memory order for now +const char *CompilerMSL::get_memory_order(uint32_t) +{ + return "memory_order_relaxed"; +} + +// Override for MSL-specific extension syntax instructions +void CompilerMSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count) +{ + auto op = static_cast(eop); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, count); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + switch (op) + { + case GLSLstd450Atan2: + emit_binary_func_op(result_type, id, args[0], args[1], "atan2"); + break; + case GLSLstd450InverseSqrt: + emit_unary_func_op(result_type, id, args[0], "rsqrt"); + break; + case GLSLstd450RoundEven: + emit_unary_func_op(result_type, id, args[0], "rint"); + break; + + case GLSLstd450FindILsb: + { + // In this template version of findLSB, we return T. + auto basetype = expression_type(args[0]).basetype; + emit_unary_func_op_cast(result_type, id, args[0], "spvFindLSB", basetype, basetype); + break; + } + + case GLSLstd450FindSMsb: + emit_unary_func_op_cast(result_type, id, args[0], "spvFindSMSB", int_type, int_type); + break; + + case GLSLstd450FindUMsb: + emit_unary_func_op_cast(result_type, id, args[0], "spvFindUMSB", uint_type, uint_type); + break; + + case GLSLstd450PackSnorm4x8: + emit_unary_func_op(result_type, id, args[0], "pack_float_to_snorm4x8"); + break; + case GLSLstd450PackUnorm4x8: + emit_unary_func_op(result_type, id, args[0], "pack_float_to_unorm4x8"); + break; + case GLSLstd450PackSnorm2x16: + emit_unary_func_op(result_type, id, args[0], "pack_float_to_snorm2x16"); + break; + case GLSLstd450PackUnorm2x16: + emit_unary_func_op(result_type, id, args[0], "pack_float_to_unorm2x16"); + break; + + case GLSLstd450PackHalf2x16: + { + auto expr = join("as_type(half2(", to_expression(args[0]), "))"); + emit_op(result_type, id, expr, should_forward(args[0])); + inherit_expression_dependencies(id, args[0]); + break; + } + + case GLSLstd450UnpackSnorm4x8: + emit_unary_func_op(result_type, id, args[0], "unpack_snorm4x8_to_float"); + break; + case GLSLstd450UnpackUnorm4x8: + emit_unary_func_op(result_type, id, args[0], "unpack_unorm4x8_to_float"); + break; + case GLSLstd450UnpackSnorm2x16: + emit_unary_func_op(result_type, id, args[0], "unpack_snorm2x16_to_float"); + break; + case GLSLstd450UnpackUnorm2x16: + emit_unary_func_op(result_type, id, args[0], "unpack_unorm2x16_to_float"); + break; + + case GLSLstd450UnpackHalf2x16: + { + auto expr = join("float2(as_type(", to_expression(args[0]), "))"); + emit_op(result_type, id, expr, should_forward(args[0])); + inherit_expression_dependencies(id, args[0]); + break; + } + + case GLSLstd450PackDouble2x32: + emit_unary_func_op(result_type, id, args[0], "unsupported_GLSLstd450PackDouble2x32"); // Currently unsupported + break; + case GLSLstd450UnpackDouble2x32: + emit_unary_func_op(result_type, id, args[0], "unsupported_GLSLstd450UnpackDouble2x32"); // Currently unsupported + break; + + case GLSLstd450MatrixInverse: + { + auto &mat_type = get(result_type); + switch (mat_type.columns) + { + case 2: + emit_unary_func_op(result_type, id, args[0], "spvInverse2x2"); + break; + case 3: + emit_unary_func_op(result_type, id, args[0], "spvInverse3x3"); + break; + case 4: + emit_unary_func_op(result_type, id, args[0], "spvInverse4x4"); + break; + default: + break; + } + break; + } + + case GLSLstd450FMin: + // If the result type isn't float, don't bother calling the specific + // precise::/fast:: version. Metal doesn't have those for half and + // double types. + if (get(result_type).basetype != SPIRType::Float) + emit_binary_func_op(result_type, id, args[0], args[1], "min"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "fast::min"); + break; + + case GLSLstd450FMax: + if (get(result_type).basetype != SPIRType::Float) + emit_binary_func_op(result_type, id, args[0], args[1], "max"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "fast::max"); + break; + + case GLSLstd450FClamp: + // TODO: If args[1] is 0 and args[2] is 1, emit a saturate() call. + if (get(result_type).basetype != SPIRType::Float) + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp"); + else + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fast::clamp"); + break; + + case GLSLstd450NMin: + if (get(result_type).basetype != SPIRType::Float) + emit_binary_func_op(result_type, id, args[0], args[1], "min"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "precise::min"); + break; + + case GLSLstd450NMax: + if (get(result_type).basetype != SPIRType::Float) + emit_binary_func_op(result_type, id, args[0], args[1], "max"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "precise::max"); + break; + + case GLSLstd450NClamp: + // TODO: If args[1] is 0 and args[2] is 1, emit a saturate() call. + if (get(result_type).basetype != SPIRType::Float) + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp"); + else + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "precise::clamp"); + break; + + case GLSLstd450InterpolateAtCentroid: + { + // We can't just emit the expression normally, because the qualified name contains a call to the default + // interpolate method, or refers to a local variable. We saved the interface index we need; use it to construct + // the base for the method call. + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_centroid()", component), + should_forward(args[0])); + break; + } + + case GLSLstd450InterpolateAtSample: + { + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_sample(", to_expression(args[1]), ")", component), + should_forward(args[0]) && should_forward(args[1])); + break; + } + + case GLSLstd450InterpolateAtOffset: + { + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + // Like Direct3D, Metal puts the (0, 0) at the upper-left corner, not the center as SPIR-V and GLSL do. + // Offset the offset by (1/2 - 1/16), or 0.4375, to compensate for this. + // It has to be (1/2 - 1/16) and not 1/2, or several CTS tests subtly break on Intel. + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_offset(", to_expression(args[1]), " + 0.4375)", component), + should_forward(args[0]) && should_forward(args[1])); + break; + } + + case GLSLstd450Distance: + // MSL does not support scalar versions here. + if (expression_type(args[0]).vecsize == 1) + { + // Equivalent to length(a - b) -> abs(a - b). + emit_op(result_type, id, + join("abs(", to_enclosed_unpacked_expression(args[0]), " - ", + to_enclosed_unpacked_expression(args[1]), ")"), + should_forward(args[0]) && should_forward(args[1])); + inherit_expression_dependencies(id, args[0]); + inherit_expression_dependencies(id, args[1]); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Length: + // MSL does not support scalar versions here. + if (expression_type(args[0]).vecsize == 1) + { + // Equivalent to abs(). + emit_unary_func_op(result_type, id, args[0], "abs"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Normalize: + // MSL does not support scalar versions here. + if (expression_type(args[0]).vecsize == 1) + { + // Returns -1 or 1 for valid input, sign() does the job. + emit_unary_func_op(result_type, id, args[0], "sign"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Reflect: + if (get(result_type).vecsize == 1) + emit_binary_func_op(result_type, id, args[0], args[1], "spvReflect"); + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Refract: + if (get(result_type).vecsize == 1) + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "spvRefract"); + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450FaceForward: + if (get(result_type).vecsize == 1) + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "spvFaceForward"); + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Modf: + case GLSLstd450Frexp: + { + // Special case. If the variable is a scalar access chain, we cannot use it directly. We have to emit a temporary. + auto *ptr = maybe_get(args[1]); + if (ptr && ptr->access_chain && is_scalar(expression_type(args[1]))) + { + register_call_out_argument(args[1]); + forced_temporaries.insert(id); + + // Need to create temporaries and copy over to access chain after. + // We cannot directly take the reference of a vector swizzle in MSL, even if it's scalar ... + uint32_t &tmp_id = extra_sub_expressions[id]; + if (!tmp_id) + tmp_id = ir.increase_bound_by(1); + + uint32_t tmp_type_id = get_pointee_type_id(ptr->expression_type); + emit_uninitialized_temporary_expression(tmp_type_id, tmp_id); + emit_binary_func_op(result_type, id, args[0], tmp_id, eop == GLSLstd450Modf ? "modf" : "frexp"); + statement(to_expression(args[1]), " = ", to_expression(tmp_id), ";"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + } + + default: + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + } +} + +void CompilerMSL::emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t id, uint32_t eop, + const uint32_t *args, uint32_t count) +{ + enum AMDShaderTrinaryMinMax + { + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9 + }; + + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Trinary min/max functions require MSL 2.1."); + + auto op = static_cast(eop); + + switch (op) + { + case FMid3AMD: + case UMid3AMD: + case SMid3AMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "median3"); + break; + default: + CompilerGLSL::emit_spv_amd_shader_trinary_minmax_op(result_type, id, eop, args, count); + break; + } +} + +// Emit a structure declaration for the specified interface variable. +void CompilerMSL::emit_interface_block(uint32_t ib_var_id) +{ + if (ib_var_id) + { + auto &ib_var = get(ib_var_id); + auto &ib_type = get_variable_data_type(ib_var); + assert(ib_type.basetype == SPIRType::Struct && !ib_type.member_types.empty()); + emit_struct(ib_type); + } +} + +// Emits the declaration signature of the specified function. +// If this is the entry point function, Metal-specific return value and function arguments are added. +void CompilerMSL::emit_function_prototype(SPIRFunction &func, const Bitset &) +{ + if (func.self != ir.default_entry_point) + add_function_overload(func); + + local_variable_names = resource_names; + string decl; + + processing_entry_point = func.self == ir.default_entry_point; + + // Metal helper functions must be static force-inline otherwise they will cause problems when linked together in a single Metallib. + if (!processing_entry_point) + statement(force_inline); + + auto &type = get(func.return_type); + + if (!type.array.empty() && msl_options.force_native_arrays) + { + // We cannot return native arrays in MSL, so "return" through an out variable. + decl += "void"; + } + else + { + decl += func_type_decl(type); + } + + decl += " "; + decl += to_name(func.self); + decl += "("; + + if (!type.array.empty() && msl_options.force_native_arrays) + { + // Fake arrays returns by writing to an out array instead. + decl += "thread "; + decl += type_to_glsl(type); + decl += " (&SPIRV_Cross_return_value)"; + decl += type_to_array_glsl(type); + if (!func.arguments.empty()) + decl += ", "; + } + + if (processing_entry_point) + { + if (msl_options.argument_buffers) + decl += entry_point_args_argument_buffer(!func.arguments.empty()); + else + decl += entry_point_args_classic(!func.arguments.empty()); + + // If entry point function has variables that require early declaration, + // ensure they each have an empty initializer, creating one if needed. + // This is done at this late stage because the initialization expression + // is cleared after each compilation pass. + for (auto var_id : vars_needing_early_declaration) + { + auto &ed_var = get(var_id); + ID &initializer = ed_var.initializer; + if (!initializer) + initializer = ir.increase_bound_by(1); + + // Do not override proper initializers. + if (ir.ids[initializer].get_type() == TypeNone || ir.ids[initializer].get_type() == TypeExpression) + set(ed_var.initializer, "{}", ed_var.basetype, true); + } + } + + for (auto &arg : func.arguments) + { + uint32_t name_id = arg.id; + + auto *var = maybe_get(arg.id); + if (var) + { + // If we need to modify the name of the variable, make sure we modify the original variable. + // Our alias is just a shadow variable. + if (arg.alias_global_variable && var->basevariable) + name_id = var->basevariable; + + var->parameter = &arg; // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + } + + add_local_variable_name(name_id); + + decl += argument_decl(arg); + + bool is_dynamic_img_sampler = has_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler); + + auto &arg_type = get(arg.type); + if (arg_type.basetype == SPIRType::SampledImage && !is_dynamic_img_sampler) + { + // Manufacture automatic plane args for multiplanar texture + uint32_t planes = 1; + if (auto *constexpr_sampler = find_constexpr_sampler(name_id)) + if (constexpr_sampler->ycbcr_conversion_enable) + planes = constexpr_sampler->planes; + for (uint32_t i = 1; i < planes; i++) + decl += join(", ", argument_decl(arg), plane_name_suffix, i); + + // Manufacture automatic sampler arg for SampledImage texture + if (arg_type.image.dim != DimBuffer) + decl += join(", thread const ", sampler_type(arg_type, arg.id), " ", to_sampler_expression(arg.id)); + } + + // Manufacture automatic swizzle arg. + if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(arg_type) && + !is_dynamic_img_sampler) + { + bool arg_is_array = !arg_type.array.empty(); + decl += join(", constant uint", arg_is_array ? "* " : "& ", to_swizzle_expression(arg.id)); + } + + if (buffers_requiring_array_length.count(name_id)) + { + bool arg_is_array = !arg_type.array.empty(); + decl += join(", constant uint", arg_is_array ? "* " : "& ", to_buffer_size_expression(name_id)); + } + + if (&arg != &func.arguments.back()) + decl += ", "; + } + + decl += ")"; + statement(decl); +} + +static bool needs_chroma_reconstruction(const MSLConstexprSampler *constexpr_sampler) +{ + // For now, only multiplanar images need explicit reconstruction. GBGR and BGRG images + // use implicit reconstruction. + return constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable && constexpr_sampler->planes > 1; +} + +// Returns the texture sampling function string for the specified image and sampling characteristics. +string CompilerMSL::to_function_name(const TextureFunctionNameArguments &args) +{ + VariableID img = args.base.img; + auto &imgtype = *args.base.imgtype; + + const MSLConstexprSampler *constexpr_sampler = nullptr; + bool is_dynamic_img_sampler = false; + if (auto *var = maybe_get_backing_variable(img)) + { + constexpr_sampler = find_constexpr_sampler(var->basevariable ? var->basevariable : VariableID(var->self)); + is_dynamic_img_sampler = has_extended_decoration(var->self, SPIRVCrossDecorationDynamicImageSampler); + } + + // Special-case gather. We have to alter the component being looked up + // in the swizzle case. + if (msl_options.swizzle_texture_samples && args.base.is_gather && !is_dynamic_img_sampler && + (!constexpr_sampler || !constexpr_sampler->ycbcr_conversion_enable)) + { + add_spv_func_and_recompile(imgtype.image.depth ? SPVFuncImplGatherCompareSwizzle : SPVFuncImplGatherSwizzle); + return imgtype.image.depth ? "spvGatherCompareSwizzle" : "spvGatherSwizzle"; + } + + auto *combined = maybe_get(img); + + // Texture reference + string fname; + if (needs_chroma_reconstruction(constexpr_sampler) && !is_dynamic_img_sampler) + { + if (constexpr_sampler->planes != 2 && constexpr_sampler->planes != 3) + SPIRV_CROSS_THROW("Unhandled number of color image planes!"); + // 444 images aren't downsampled, so we don't need to do linear filtering. + if (constexpr_sampler->resolution == MSL_FORMAT_RESOLUTION_444 || + constexpr_sampler->chroma_filter == MSL_SAMPLER_FILTER_NEAREST) + { + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile(SPVFuncImplChromaReconstructNearest2Plane); + else + add_spv_func_and_recompile(SPVFuncImplChromaReconstructNearest3Plane); + fname = "spvChromaReconstructNearest"; + } + else // Linear with a downsampled format + { + fname = "spvChromaReconstructLinear"; + switch (constexpr_sampler->resolution) + { + case MSL_FORMAT_RESOLUTION_444: + assert(false); + break; // not reached + case MSL_FORMAT_RESOLUTION_422: + switch (constexpr_sampler->x_chroma_offset) + { + case MSL_CHROMA_LOCATION_COSITED_EVEN: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear422CositedEven2Plane); + else + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear422CositedEven3Plane); + fname += "422CositedEven"; + break; + case MSL_CHROMA_LOCATION_MIDPOINT: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear422Midpoint2Plane); + else + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear422Midpoint3Plane); + fname += "422Midpoint"; + break; + default: + SPIRV_CROSS_THROW("Invalid chroma location."); + } + break; + case MSL_FORMAT_RESOLUTION_420: + fname += "420"; + switch (constexpr_sampler->x_chroma_offset) + { + case MSL_CHROMA_LOCATION_COSITED_EVEN: + switch (constexpr_sampler->y_chroma_offset) + { + case MSL_CHROMA_LOCATION_COSITED_EVEN: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane); + else + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane); + fname += "XCositedEvenYCositedEven"; + break; + case MSL_CHROMA_LOCATION_MIDPOINT: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane); + else + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane); + fname += "XCositedEvenYMidpoint"; + break; + default: + SPIRV_CROSS_THROW("Invalid Y chroma location."); + } + break; + case MSL_CHROMA_LOCATION_MIDPOINT: + switch (constexpr_sampler->y_chroma_offset) + { + case MSL_CHROMA_LOCATION_COSITED_EVEN: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane); + else + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane); + fname += "XMidpointYCositedEven"; + break; + case MSL_CHROMA_LOCATION_MIDPOINT: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane); + else + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane); + fname += "XMidpointYMidpoint"; + break; + default: + SPIRV_CROSS_THROW("Invalid Y chroma location."); + } + break; + default: + SPIRV_CROSS_THROW("Invalid X chroma location."); + } + break; + default: + SPIRV_CROSS_THROW("Invalid format resolution."); + } + } + } + else + { + fname = to_expression(combined ? combined->image : img) + "."; + + // Texture function and sampler + if (args.base.is_fetch) + fname += "read"; + else if (args.base.is_gather) + fname += "gather"; + else + fname += "sample"; + + if (args.has_dref) + fname += "_compare"; + } + + return fname; +} + +string CompilerMSL::convert_to_f32(const string &expr, uint32_t components) +{ + SPIRType t; + t.basetype = SPIRType::Float; + t.vecsize = components; + t.columns = 1; + return join(type_to_glsl_constructor(t), "(", expr, ")"); +} + +static inline bool sampling_type_needs_f32_conversion(const SPIRType &type) +{ + // Double is not supported to begin with, but doesn't hurt to check for completion. + return type.basetype == SPIRType::Half || type.basetype == SPIRType::Double; +} + +// Returns the function args for a texture sampling function for the specified image and sampling characteristics. +string CompilerMSL::to_function_args(const TextureFunctionArguments &args, bool *p_forward) +{ + VariableID img = args.base.img; + auto &imgtype = *args.base.imgtype; + uint32_t lod = args.lod; + uint32_t grad_x = args.grad_x; + uint32_t grad_y = args.grad_y; + uint32_t bias = args.bias; + + const MSLConstexprSampler *constexpr_sampler = nullptr; + bool is_dynamic_img_sampler = false; + if (auto *var = maybe_get_backing_variable(img)) + { + constexpr_sampler = find_constexpr_sampler(var->basevariable ? var->basevariable : VariableID(var->self)); + is_dynamic_img_sampler = has_extended_decoration(var->self, SPIRVCrossDecorationDynamicImageSampler); + } + + string farg_str; + bool forward = true; + + if (!is_dynamic_img_sampler) + { + // Texture reference (for some cases) + if (needs_chroma_reconstruction(constexpr_sampler)) + { + // Multiplanar images need two or three textures. + farg_str += to_expression(img); + for (uint32_t i = 1; i < constexpr_sampler->planes; i++) + farg_str += join(", ", to_expression(img), plane_name_suffix, i); + } + else if ((!constexpr_sampler || !constexpr_sampler->ycbcr_conversion_enable) && + msl_options.swizzle_texture_samples && args.base.is_gather) + { + auto *combined = maybe_get(img); + farg_str += to_expression(combined ? combined->image : img); + } + + // Sampler reference + if (!args.base.is_fetch) + { + if (!farg_str.empty()) + farg_str += ", "; + farg_str += to_sampler_expression(img); + } + + if ((!constexpr_sampler || !constexpr_sampler->ycbcr_conversion_enable) && + msl_options.swizzle_texture_samples && args.base.is_gather) + { + // Add the swizzle constant from the swizzle buffer. + farg_str += ", " + to_swizzle_expression(img); + used_swizzle_buffer = true; + } + + // Swizzled gather puts the component before the other args, to allow template + // deduction to work. + if (args.component && msl_options.swizzle_texture_samples) + { + forward = should_forward(args.component); + farg_str += ", " + to_component_argument(args.component); + } + } + + // Texture coordinates + forward = forward && should_forward(args.coord); + auto coord_expr = to_enclosed_expression(args.coord); + auto &coord_type = expression_type(args.coord); + bool coord_is_fp = type_is_floating_point(coord_type); + bool is_cube_fetch = false; + + string tex_coords = coord_expr; + uint32_t alt_coord_component = 0; + + switch (imgtype.image.dim) + { + + case Dim1D: + if (coord_type.vecsize > 1) + tex_coords = enclose_expression(tex_coords) + ".x"; + + if (args.base.is_fetch) + tex_coords = "uint(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + else if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords = convert_to_f32(tex_coords, 1); + + if (msl_options.texture_1D_as_2D) + { + if (args.base.is_fetch) + tex_coords = "uint2(" + tex_coords + ", 0)"; + else + tex_coords = "float2(" + tex_coords + ", 0.5)"; + } + + alt_coord_component = 1; + break; + + case DimBuffer: + if (coord_type.vecsize > 1) + tex_coords = enclose_expression(tex_coords) + ".x"; + + if (msl_options.texture_buffer_native) + { + tex_coords = "uint(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + } + else + { + // Metal texel buffer textures are 2D, so convert 1D coord to 2D. + // Support for Metal 2.1's new texture_buffer type. + if (args.base.is_fetch) + { + if (msl_options.texel_buffer_texture_width > 0) + { + tex_coords = "spvTexelBufferCoord(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + } + else + { + tex_coords = "spvTexelBufferCoord(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ", " + + to_expression(img) + ")"; + } + } + } + + alt_coord_component = 1; + break; + + case DimSubpassData: + // If we're using Metal's native frame-buffer fetch API for subpass inputs, + // this path will not be hit. + tex_coords = "uint2(gl_FragCoord.xy)"; + alt_coord_component = 2; + break; + + case Dim2D: + if (coord_type.vecsize > 2) + tex_coords = enclose_expression(tex_coords) + ".xy"; + + if (args.base.is_fetch) + tex_coords = "uint2(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + else if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords = convert_to_f32(tex_coords, 2); + + alt_coord_component = 2; + break; + + case Dim3D: + if (coord_type.vecsize > 3) + tex_coords = enclose_expression(tex_coords) + ".xyz"; + + if (args.base.is_fetch) + tex_coords = "uint3(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + else if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords = convert_to_f32(tex_coords, 3); + + alt_coord_component = 3; + break; + + case DimCube: + if (args.base.is_fetch) + { + is_cube_fetch = true; + tex_coords += ".xy"; + tex_coords = "uint2(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + } + else + { + if (coord_type.vecsize > 3) + tex_coords = enclose_expression(tex_coords) + ".xyz"; + } + + if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords = convert_to_f32(tex_coords, 3); + + alt_coord_component = 3; + break; + + default: + break; + } + + if (args.base.is_fetch && (args.offset || args.coffset)) + { + uint32_t offset_expr = args.offset ? args.offset : args.coffset; + // Fetch offsets must be applied directly to the coordinate. + forward = forward && should_forward(offset_expr); + auto &type = expression_type(offset_expr); + if (imgtype.image.dim == Dim1D && msl_options.texture_1D_as_2D) + { + if (type.basetype != SPIRType::UInt) + tex_coords += join(" + uint2(", bitcast_expression(SPIRType::UInt, offset_expr), ", 0)"); + else + tex_coords += join(" + uint2(", to_enclosed_expression(offset_expr), ", 0)"); + } + else + { + if (type.basetype != SPIRType::UInt) + tex_coords += " + " + bitcast_expression(SPIRType::UInt, offset_expr); + else + tex_coords += " + " + to_enclosed_expression(offset_expr); + } + } + + // If projection, use alt coord as divisor + if (args.base.is_proj) + { + if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords += " / " + convert_to_f32(to_extract_component_expression(args.coord, alt_coord_component), 1); + else + tex_coords += " / " + to_extract_component_expression(args.coord, alt_coord_component); + } + + if (!farg_str.empty()) + farg_str += ", "; + + if (imgtype.image.dim == DimCube && imgtype.image.arrayed && msl_options.emulate_cube_array) + { + farg_str += "spvCubemapTo2DArrayFace(" + tex_coords + ").xy"; + + if (is_cube_fetch) + farg_str += ", uint(" + to_extract_component_expression(args.coord, 2) + ")"; + else + farg_str += + ", uint(spvCubemapTo2DArrayFace(" + tex_coords + ").z) + (uint(" + + round_fp_tex_coords(to_extract_component_expression(args.coord, alt_coord_component), coord_is_fp) + + ") * 6u)"; + + add_spv_func_and_recompile(SPVFuncImplCubemapTo2DArrayFace); + } + else + { + farg_str += tex_coords; + + // If fetch from cube, add face explicitly + if (is_cube_fetch) + { + // Special case for cube arrays, face and layer are packed in one dimension. + if (imgtype.image.arrayed) + farg_str += ", uint(" + to_extract_component_expression(args.coord, 2) + ") % 6u"; + else + farg_str += + ", uint(" + round_fp_tex_coords(to_extract_component_expression(args.coord, 2), coord_is_fp) + ")"; + } + + // If array, use alt coord + if (imgtype.image.arrayed) + { + // Special case for cube arrays, face and layer are packed in one dimension. + if (imgtype.image.dim == DimCube && args.base.is_fetch) + { + farg_str += ", uint(" + to_extract_component_expression(args.coord, 2) + ") / 6u"; + } + else + { + farg_str += + ", uint(" + + round_fp_tex_coords(to_extract_component_expression(args.coord, alt_coord_component), coord_is_fp) + + ")"; + if (imgtype.image.dim == DimSubpassData) + { + if (msl_options.multiview) + farg_str += " + gl_ViewIndex"; + else if (msl_options.arrayed_subpass_input) + farg_str += " + gl_Layer"; + } + } + } + else if (imgtype.image.dim == DimSubpassData) + { + if (msl_options.multiview) + farg_str += ", gl_ViewIndex"; + else if (msl_options.arrayed_subpass_input) + farg_str += ", gl_Layer"; + } + } + + // Depth compare reference value + if (args.dref) + { + forward = forward && should_forward(args.dref); + farg_str += ", "; + + auto &dref_type = expression_type(args.dref); + + string dref_expr; + if (args.base.is_proj) + dref_expr = join(to_enclosed_expression(args.dref), " / ", + to_extract_component_expression(args.coord, alt_coord_component)); + else + dref_expr = to_expression(args.dref); + + if (sampling_type_needs_f32_conversion(dref_type)) + dref_expr = convert_to_f32(dref_expr, 1); + + farg_str += dref_expr; + + if (msl_options.is_macos() && (grad_x || grad_y)) + { + // For sample compare, MSL does not support gradient2d for all targets (only iOS apparently according to docs). + // However, the most common case here is to have a constant gradient of 0, as that is the only way to express + // LOD == 0 in GLSL with sampler2DArrayShadow (cascaded shadow mapping). + // We will detect a compile-time constant 0 value for gradient and promote that to level(0) on MSL. + bool constant_zero_x = !grad_x || expression_is_constant_null(grad_x); + bool constant_zero_y = !grad_y || expression_is_constant_null(grad_y); + if (constant_zero_x && constant_zero_y) + { + lod = 0; + grad_x = 0; + grad_y = 0; + farg_str += ", level(0)"; + } + else if (!msl_options.supports_msl_version(2, 3)) + { + SPIRV_CROSS_THROW("Using non-constant 0.0 gradient() qualifier for sample_compare. This is not " + "supported on macOS prior to MSL 2.3."); + } + } + + if (msl_options.is_macos() && bias) + { + // Bias is not supported either on macOS with sample_compare. + // Verify it is compile-time zero, and drop the argument. + if (expression_is_constant_null(bias)) + { + bias = 0; + } + else if (!msl_options.supports_msl_version(2, 3)) + { + SPIRV_CROSS_THROW("Using non-constant 0.0 bias() qualifier for sample_compare. This is not supported " + "on macOS prior to MSL 2.3."); + } + } + } + + // LOD Options + // Metal does not support LOD for 1D textures. + if (bias && (imgtype.image.dim != Dim1D || msl_options.texture_1D_as_2D)) + { + forward = forward && should_forward(bias); + farg_str += ", bias(" + to_expression(bias) + ")"; + } + + // Metal does not support LOD for 1D textures. + if (lod && (imgtype.image.dim != Dim1D || msl_options.texture_1D_as_2D)) + { + forward = forward && should_forward(lod); + if (args.base.is_fetch) + { + farg_str += ", " + to_expression(lod); + } + else + { + farg_str += ", level(" + to_expression(lod) + ")"; + } + } + else if (args.base.is_fetch && !lod && (imgtype.image.dim != Dim1D || msl_options.texture_1D_as_2D) && + imgtype.image.dim != DimBuffer && !imgtype.image.ms && imgtype.image.sampled != 2) + { + // Lod argument is optional in OpImageFetch, but we require a LOD value, pick 0 as the default. + // Check for sampled type as well, because is_fetch is also used for OpImageRead in MSL. + farg_str += ", 0"; + } + + // Metal does not support LOD for 1D textures. + if ((grad_x || grad_y) && (imgtype.image.dim != Dim1D || msl_options.texture_1D_as_2D)) + { + forward = forward && should_forward(grad_x); + forward = forward && should_forward(grad_y); + string grad_opt; + switch (imgtype.image.dim) + { + case Dim1D: + case Dim2D: + grad_opt = "2d"; + break; + case Dim3D: + grad_opt = "3d"; + break; + case DimCube: + if (imgtype.image.arrayed && msl_options.emulate_cube_array) + grad_opt = "2d"; + else + grad_opt = "cube"; + break; + default: + grad_opt = "unsupported_gradient_dimension"; + break; + } + farg_str += ", gradient" + grad_opt + "(" + to_expression(grad_x) + ", " + to_expression(grad_y) + ")"; + } + + if (args.min_lod) + { + if (msl_options.is_macos()) + { + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("min_lod_clamp() is only supported in MSL 2.2+ and up on macOS."); + } + else if (msl_options.is_ios()) + SPIRV_CROSS_THROW("min_lod_clamp() is not supported on iOS."); + + forward = forward && should_forward(args.min_lod); + farg_str += ", min_lod_clamp(" + to_expression(args.min_lod) + ")"; + } + + // Add offsets + string offset_expr; + const SPIRType *offset_type = nullptr; + if (args.coffset && !args.base.is_fetch) + { + forward = forward && should_forward(args.coffset); + offset_expr = to_expression(args.coffset); + offset_type = &expression_type(args.coffset); + } + else if (args.offset && !args.base.is_fetch) + { + forward = forward && should_forward(args.offset); + offset_expr = to_expression(args.offset); + offset_type = &expression_type(args.offset); + } + + if (!offset_expr.empty()) + { + switch (imgtype.image.dim) + { + case Dim1D: + if (!msl_options.texture_1D_as_2D) + break; + if (offset_type->vecsize > 1) + offset_expr = enclose_expression(offset_expr) + ".x"; + + farg_str += join(", int2(", offset_expr, ", 0)"); + break; + + case Dim2D: + if (offset_type->vecsize > 2) + offset_expr = enclose_expression(offset_expr) + ".xy"; + + farg_str += ", " + offset_expr; + break; + + case Dim3D: + if (offset_type->vecsize > 3) + offset_expr = enclose_expression(offset_expr) + ".xyz"; + + farg_str += ", " + offset_expr; + break; + + default: + break; + } + } + + if (args.component) + { + // If 2D has gather component, ensure it also has an offset arg + if (imgtype.image.dim == Dim2D && offset_expr.empty()) + farg_str += ", int2(0)"; + + if (!msl_options.swizzle_texture_samples || is_dynamic_img_sampler) + { + forward = forward && should_forward(args.component); + + uint32_t image_var = 0; + if (const auto *combined = maybe_get(img)) + { + if (const auto *img_var = maybe_get_backing_variable(combined->image)) + image_var = img_var->self; + } + else if (const auto *var = maybe_get_backing_variable(img)) + { + image_var = var->self; + } + + if (image_var == 0 || !image_is_comparison(expression_type(image_var), image_var)) + farg_str += ", " + to_component_argument(args.component); + } + } + + if (args.sample) + { + forward = forward && should_forward(args.sample); + farg_str += ", "; + farg_str += to_expression(args.sample); + } + + *p_forward = forward; + + return farg_str; +} + +// If the texture coordinates are floating point, invokes MSL round() function to round them. +string CompilerMSL::round_fp_tex_coords(string tex_coords, bool coord_is_fp) +{ + return coord_is_fp ? ("round(" + tex_coords + ")") : tex_coords; +} + +// Returns a string to use in an image sampling function argument. +// The ID must be a scalar constant. +string CompilerMSL::to_component_argument(uint32_t id) +{ + uint32_t component_index = evaluate_constant_u32(id); + switch (component_index) + { + case 0: + return "component::x"; + case 1: + return "component::y"; + case 2: + return "component::z"; + case 3: + return "component::w"; + + default: + SPIRV_CROSS_THROW("The value (" + to_string(component_index) + ") of OpConstant ID " + to_string(id) + + " is not a valid Component index, which must be one of 0, 1, 2, or 3."); + return "component::x"; + } +} + +// Establish sampled image as expression object and assign the sampler to it. +void CompilerMSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) +{ + set(result_id, result_type, image_id, samp_id); +} + +string CompilerMSL::to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions) +{ + auto *ops = stream(i); + uint32_t result_type_id = ops[0]; + uint32_t img = ops[2]; + auto &result_type = get(result_type_id); + auto op = static_cast(i.op); + bool is_gather = (op == OpImageGather || op == OpImageDrefGather); + + // Bypass pointers because we need the real image struct + auto &type = expression_type(img); + auto &imgtype = get(type.self); + + const MSLConstexprSampler *constexpr_sampler = nullptr; + bool is_dynamic_img_sampler = false; + if (auto *var = maybe_get_backing_variable(img)) + { + constexpr_sampler = find_constexpr_sampler(var->basevariable ? var->basevariable : VariableID(var->self)); + is_dynamic_img_sampler = has_extended_decoration(var->self, SPIRVCrossDecorationDynamicImageSampler); + } + + string expr; + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable && !is_dynamic_img_sampler) + { + // If this needs sampler Y'CbCr conversion, we need to do some additional + // processing. + switch (constexpr_sampler->ycbcr_model) + { + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY: + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY: + // Default + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709: + add_spv_func_and_recompile(SPVFuncImplConvertYCbCrBT709); + expr += "spvConvertYCbCrBT709("; + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601: + add_spv_func_and_recompile(SPVFuncImplConvertYCbCrBT601); + expr += "spvConvertYCbCrBT601("; + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020: + add_spv_func_and_recompile(SPVFuncImplConvertYCbCrBT2020); + expr += "spvConvertYCbCrBT2020("; + break; + default: + SPIRV_CROSS_THROW("Invalid Y'CbCr model conversion."); + } + + if (constexpr_sampler->ycbcr_model != MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY) + { + switch (constexpr_sampler->ycbcr_range) + { + case MSL_SAMPLER_YCBCR_RANGE_ITU_FULL: + add_spv_func_and_recompile(SPVFuncImplExpandITUFullRange); + expr += "spvExpandITUFullRange("; + break; + case MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW: + add_spv_func_and_recompile(SPVFuncImplExpandITUNarrowRange); + expr += "spvExpandITUNarrowRange("; + break; + default: + SPIRV_CROSS_THROW("Invalid Y'CbCr range."); + } + } + } + else if (msl_options.swizzle_texture_samples && !is_gather && is_sampled_image_type(imgtype) && + !is_dynamic_img_sampler) + { + add_spv_func_and_recompile(SPVFuncImplTextureSwizzle); + expr += "spvTextureSwizzle("; + } + + string inner_expr = CompilerGLSL::to_texture_op(i, sparse, forward, inherited_expressions); + + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable && !is_dynamic_img_sampler) + { + if (!constexpr_sampler->swizzle_is_identity()) + { + static const char swizzle_names[] = "rgba"; + if (!constexpr_sampler->swizzle_has_one_or_zero()) + { + // If we can, do it inline. + expr += inner_expr + "."; + for (uint32_t c = 0; c < 4; c++) + { + switch (constexpr_sampler->swizzle[c]) + { + case MSL_COMPONENT_SWIZZLE_IDENTITY: + expr += swizzle_names[c]; + break; + case MSL_COMPONENT_SWIZZLE_R: + case MSL_COMPONENT_SWIZZLE_G: + case MSL_COMPONENT_SWIZZLE_B: + case MSL_COMPONENT_SWIZZLE_A: + expr += swizzle_names[constexpr_sampler->swizzle[c] - MSL_COMPONENT_SWIZZLE_R]; + break; + default: + SPIRV_CROSS_THROW("Invalid component swizzle."); + } + } + } + else + { + // Otherwise, we need to emit a temporary and swizzle that. + uint32_t temp_id = ir.increase_bound_by(1); + emit_op(result_type_id, temp_id, inner_expr, false); + for (auto &inherit : inherited_expressions) + inherit_expression_dependencies(temp_id, inherit); + inherited_expressions.clear(); + inherited_expressions.push_back(temp_id); + + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjDrefImplicitLod: + register_control_dependent_expression(temp_id); + break; + + default: + break; + } + expr += type_to_glsl(result_type) + "("; + for (uint32_t c = 0; c < 4; c++) + { + switch (constexpr_sampler->swizzle[c]) + { + case MSL_COMPONENT_SWIZZLE_IDENTITY: + expr += to_expression(temp_id) + "." + swizzle_names[c]; + break; + case MSL_COMPONENT_SWIZZLE_ZERO: + expr += "0"; + break; + case MSL_COMPONENT_SWIZZLE_ONE: + expr += "1"; + break; + case MSL_COMPONENT_SWIZZLE_R: + case MSL_COMPONENT_SWIZZLE_G: + case MSL_COMPONENT_SWIZZLE_B: + case MSL_COMPONENT_SWIZZLE_A: + expr += to_expression(temp_id) + "." + + swizzle_names[constexpr_sampler->swizzle[c] - MSL_COMPONENT_SWIZZLE_R]; + break; + default: + SPIRV_CROSS_THROW("Invalid component swizzle."); + } + if (c < 3) + expr += ", "; + } + expr += ")"; + } + } + else + expr += inner_expr; + if (constexpr_sampler->ycbcr_model != MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY) + { + expr += join(", ", constexpr_sampler->bpc, ")"); + if (constexpr_sampler->ycbcr_model != MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY) + expr += ")"; + } + } + else + { + expr += inner_expr; + if (msl_options.swizzle_texture_samples && !is_gather && is_sampled_image_type(imgtype) && + !is_dynamic_img_sampler) + { + // Add the swizzle constant from the swizzle buffer. + expr += ", " + to_swizzle_expression(img) + ")"; + used_swizzle_buffer = true; + } + } + + return expr; +} + +static string create_swizzle(MSLComponentSwizzle swizzle) +{ + switch (swizzle) + { + case MSL_COMPONENT_SWIZZLE_IDENTITY: + return "spvSwizzle::none"; + case MSL_COMPONENT_SWIZZLE_ZERO: + return "spvSwizzle::zero"; + case MSL_COMPONENT_SWIZZLE_ONE: + return "spvSwizzle::one"; + case MSL_COMPONENT_SWIZZLE_R: + return "spvSwizzle::red"; + case MSL_COMPONENT_SWIZZLE_G: + return "spvSwizzle::green"; + case MSL_COMPONENT_SWIZZLE_B: + return "spvSwizzle::blue"; + case MSL_COMPONENT_SWIZZLE_A: + return "spvSwizzle::alpha"; + default: + SPIRV_CROSS_THROW("Invalid component swizzle."); + return ""; + } +} + +// Returns a string representation of the ID, usable as a function arg. +// Manufacture automatic sampler arg for SampledImage texture. +string CompilerMSL::to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) +{ + string arg_str; + + auto &type = expression_type(id); + bool is_dynamic_img_sampler = has_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler); + // If the argument *itself* is a "dynamic" combined-image sampler, then we can just pass that around. + bool arg_is_dynamic_img_sampler = has_extended_decoration(id, SPIRVCrossDecorationDynamicImageSampler); + if (is_dynamic_img_sampler && !arg_is_dynamic_img_sampler) + arg_str = join("spvDynamicImageSampler<", type_to_glsl(get(type.image.type)), ">("); + + auto *c = maybe_get(id); + if (msl_options.force_native_arrays && c && !get(c->constant_type).array.empty()) + { + // If we are passing a constant array directly to a function for some reason, + // the callee will expect an argument in thread const address space + // (since we can only bind to arrays with references in MSL). + // To resolve this, we must emit a copy in this address space. + // This kind of code gen should be rare enough that performance is not a real concern. + // Inline the SPIR-V to avoid this kind of suboptimal codegen. + // + // We risk calling this inside a continue block (invalid code), + // so just create a thread local copy in the current function. + arg_str = join("_", id, "_array_copy"); + auto &constants = current_function->constant_arrays_needed_on_stack; + auto itr = find(begin(constants), end(constants), ID(id)); + if (itr == end(constants)) + { + force_recompile(); + constants.push_back(id); + } + } + else + arg_str += CompilerGLSL::to_func_call_arg(arg, id); + + // Need to check the base variable in case we need to apply a qualified alias. + uint32_t var_id = 0; + auto *var = maybe_get(id); + if (var) + var_id = var->basevariable; + + if (!arg_is_dynamic_img_sampler) + { + auto *constexpr_sampler = find_constexpr_sampler(var_id ? var_id : id); + if (type.basetype == SPIRType::SampledImage) + { + // Manufacture automatic plane args for multiplanar texture + uint32_t planes = 1; + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + { + planes = constexpr_sampler->planes; + // If this parameter isn't aliasing a global, then we need to use + // the special "dynamic image-sampler" class to pass it--and we need + // to use it for *every* non-alias parameter, in case a combined + // image-sampler with a Y'CbCr conversion is passed. Hopefully, this + // pathological case is so rare that it should never be hit in practice. + if (!arg.alias_global_variable) + add_spv_func_and_recompile(SPVFuncImplDynamicImageSampler); + } + for (uint32_t i = 1; i < planes; i++) + arg_str += join(", ", CompilerGLSL::to_func_call_arg(arg, id), plane_name_suffix, i); + // Manufacture automatic sampler arg if the arg is a SampledImage texture. + if (type.image.dim != DimBuffer) + arg_str += ", " + to_sampler_expression(var_id ? var_id : id); + + // Add sampler Y'CbCr conversion info if we have it + if (is_dynamic_img_sampler && constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + { + SmallVector samp_args; + + switch (constexpr_sampler->resolution) + { + case MSL_FORMAT_RESOLUTION_444: + // Default + break; + case MSL_FORMAT_RESOLUTION_422: + samp_args.push_back("spvFormatResolution::_422"); + break; + case MSL_FORMAT_RESOLUTION_420: + samp_args.push_back("spvFormatResolution::_420"); + break; + default: + SPIRV_CROSS_THROW("Invalid format resolution."); + } + + if (constexpr_sampler->chroma_filter != MSL_SAMPLER_FILTER_NEAREST) + samp_args.push_back("spvChromaFilter::linear"); + + if (constexpr_sampler->x_chroma_offset != MSL_CHROMA_LOCATION_COSITED_EVEN) + samp_args.push_back("spvXChromaLocation::midpoint"); + if (constexpr_sampler->y_chroma_offset != MSL_CHROMA_LOCATION_COSITED_EVEN) + samp_args.push_back("spvYChromaLocation::midpoint"); + switch (constexpr_sampler->ycbcr_model) + { + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY: + // Default + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY: + samp_args.push_back("spvYCbCrModelConversion::ycbcr_identity"); + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709: + samp_args.push_back("spvYCbCrModelConversion::ycbcr_bt_709"); + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601: + samp_args.push_back("spvYCbCrModelConversion::ycbcr_bt_601"); + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020: + samp_args.push_back("spvYCbCrModelConversion::ycbcr_bt_2020"); + break; + default: + SPIRV_CROSS_THROW("Invalid Y'CbCr model conversion."); + } + if (constexpr_sampler->ycbcr_range != MSL_SAMPLER_YCBCR_RANGE_ITU_FULL) + samp_args.push_back("spvYCbCrRange::itu_narrow"); + samp_args.push_back(join("spvComponentBits(", constexpr_sampler->bpc, ")")); + arg_str += join(", spvYCbCrSampler(", merge(samp_args), ")"); + } + } + + if (is_dynamic_img_sampler && constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + arg_str += join(", (uint(", create_swizzle(constexpr_sampler->swizzle[3]), ") << 24) | (uint(", + create_swizzle(constexpr_sampler->swizzle[2]), ") << 16) | (uint(", + create_swizzle(constexpr_sampler->swizzle[1]), ") << 8) | uint(", + create_swizzle(constexpr_sampler->swizzle[0]), ")"); + else if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(type)) + arg_str += ", " + to_swizzle_expression(var_id ? var_id : id); + + if (buffers_requiring_array_length.count(var_id)) + arg_str += ", " + to_buffer_size_expression(var_id ? var_id : id); + + if (is_dynamic_img_sampler) + arg_str += ")"; + } + + // Emulate texture2D atomic operations + auto *backing_var = maybe_get_backing_variable(var_id); + if (backing_var && atomic_image_vars.count(backing_var->self)) + { + arg_str += ", " + to_expression(var_id) + "_atomic"; + } + + return arg_str; +} + +// If the ID represents a sampled image that has been assigned a sampler already, +// generate an expression for the sampler, otherwise generate a fake sampler name +// by appending a suffix to the expression constructed from the ID. +string CompilerMSL::to_sampler_expression(uint32_t id) +{ + auto *combined = maybe_get(id); + auto expr = to_expression(combined ? combined->image : VariableID(id)); + auto index = expr.find_first_of('['); + + uint32_t samp_id = 0; + if (combined) + samp_id = combined->sampler; + + if (index == string::npos) + return samp_id ? to_expression(samp_id) : expr + sampler_name_suffix; + else + { + auto image_expr = expr.substr(0, index); + auto array_expr = expr.substr(index); + return samp_id ? to_expression(samp_id) : (image_expr + sampler_name_suffix + array_expr); + } +} + +string CompilerMSL::to_swizzle_expression(uint32_t id) +{ + auto *combined = maybe_get(id); + + auto expr = to_expression(combined ? combined->image : VariableID(id)); + auto index = expr.find_first_of('['); + + // If an image is part of an argument buffer translate this to a legal identifier. + string::size_type period = 0; + while ((period = expr.find_first_of('.', period)) != string::npos && period < index) + expr[period] = '_'; + + if (index == string::npos) + return expr + swizzle_name_suffix; + else + { + auto image_expr = expr.substr(0, index); + auto array_expr = expr.substr(index); + return image_expr + swizzle_name_suffix + array_expr; + } +} + +string CompilerMSL::to_buffer_size_expression(uint32_t id) +{ + auto expr = to_expression(id); + auto index = expr.find_first_of('['); + + // This is quite crude, but we need to translate the reference name (*spvDescriptorSetN.name) to + // the pointer expression spvDescriptorSetN.name to make a reasonable expression here. + // This only happens if we have argument buffers and we are using OpArrayLength on a lone SSBO in that set. + if (expr.size() >= 3 && expr[0] == '(' && expr[1] == '*') + expr = address_of_expression(expr); + + // If a buffer is part of an argument buffer translate this to a legal identifier. + for (auto &c : expr) + if (c == '.') + c = '_'; + + if (index == string::npos) + return expr + buffer_size_name_suffix; + else + { + auto buffer_expr = expr.substr(0, index); + auto array_expr = expr.substr(index); + return buffer_expr + buffer_size_name_suffix + array_expr; + } +} + +// Checks whether the type is a Block all of whose members have DecorationPatch. +bool CompilerMSL::is_patch_block(const SPIRType &type) +{ + if (!has_decoration(type.self, DecorationBlock)) + return false; + + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + if (!has_member_decoration(type.self, i, DecorationPatch)) + return false; + } + + return true; +} + +// Checks whether the ID is a row_major matrix that requires conversion before use +bool CompilerMSL::is_non_native_row_major_matrix(uint32_t id) +{ + auto *e = maybe_get(id); + if (e) + return e->need_transpose; + else + return has_decoration(id, DecorationRowMajor); +} + +// Checks whether the member is a row_major matrix that requires conversion before use +bool CompilerMSL::member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) +{ + return has_member_decoration(type.self, index, DecorationRowMajor); +} + +string CompilerMSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, uint32_t physical_type_id, + bool is_packed) +{ + if (!is_matrix(exp_type)) + { + return CompilerGLSL::convert_row_major_matrix(move(exp_str), exp_type, physical_type_id, is_packed); + } + else + { + strip_enclosed_expression(exp_str); + if (physical_type_id != 0 || is_packed) + exp_str = unpack_expression_type(exp_str, exp_type, physical_type_id, is_packed, true); + return join("transpose(", exp_str, ")"); + } +} + +// Called automatically at the end of the entry point function +void CompilerMSL::emit_fixup() +{ + if (is_vertex_like_shader() && stage_out_var_id && !qual_pos_var_name.empty() && !capture_output_to_buffer) + { + if (options.vertex.fixup_clipspace) + statement(qual_pos_var_name, ".z = (", qual_pos_var_name, ".z + ", qual_pos_var_name, + ".w) * 0.5; // Adjust clip-space for Metal"); + + if (options.vertex.flip_vert_y) + statement(qual_pos_var_name, ".y = -(", qual_pos_var_name, ".y);", " // Invert Y-axis for Metal"); + } +} + +// Return a string defining a structure member, with padding and packing. +string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const string &qualifier) +{ + if (member_is_remapped_physical_type(type, index)) + member_type_id = get_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypeID); + auto &physical_type = get(member_type_id); + + // If this member is packed, mark it as so. + string pack_pfx; + + // Allow Metal to use the array template to make arrays a value type + uint32_t orig_id = 0; + if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID)) + orig_id = get_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID); + + bool row_major = false; + if (is_matrix(physical_type)) + row_major = has_member_decoration(type.self, index, DecorationRowMajor); + + SPIRType row_major_physical_type; + const SPIRType *declared_type = &physical_type; + + // If a struct is being declared with physical layout, + // do not use array wrappers. + // This avoids a lot of complicated cases with packed vectors and matrices, + // and generally we cannot copy full arrays in and out of buffers into Function + // address space. + // Array of resources should also be declared as builtin arrays. + if (has_member_decoration(type.self, index, DecorationOffset)) + is_using_builtin_array = true; + else if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationResourceIndexPrimary)) + is_using_builtin_array = true; + + if (member_is_packed_physical_type(type, index)) + { + // If we're packing a matrix, output an appropriate typedef + if (physical_type.basetype == SPIRType::Struct) + { + SPIRV_CROSS_THROW("Cannot emit a packed struct currently."); + } + else if (is_matrix(physical_type)) + { + uint32_t rows = physical_type.vecsize; + uint32_t cols = physical_type.columns; + pack_pfx = "packed_"; + if (row_major) + { + // These are stored transposed. + rows = physical_type.columns; + cols = physical_type.vecsize; + pack_pfx = "packed_rm_"; + } + string base_type = physical_type.width == 16 ? "half" : "float"; + string td_line = "typedef "; + td_line += "packed_" + base_type + to_string(rows); + td_line += " " + pack_pfx; + // Use the actual matrix size here. + td_line += base_type + to_string(physical_type.columns) + "x" + to_string(physical_type.vecsize); + td_line += "[" + to_string(cols) + "]"; + td_line += ";"; + add_typedef_line(td_line); + } + else if (!is_scalar(physical_type)) // scalar type is already packed. + pack_pfx = "packed_"; + } + else if (row_major) + { + // Need to declare type with flipped vecsize/columns. + row_major_physical_type = physical_type; + swap(row_major_physical_type.vecsize, row_major_physical_type.columns); + declared_type = &row_major_physical_type; + } + + // Very specifically, image load-store in argument buffers are disallowed on MSL on iOS. + if (msl_options.is_ios() && physical_type.basetype == SPIRType::Image && physical_type.image.sampled == 2) + { + if (!has_decoration(orig_id, DecorationNonWritable)) + SPIRV_CROSS_THROW("Writable images are not allowed in argument buffers on iOS."); + } + + // Array information is baked into these types. + string array_type; + if (physical_type.basetype != SPIRType::Image && physical_type.basetype != SPIRType::Sampler && + physical_type.basetype != SPIRType::SampledImage) + { + BuiltIn builtin = BuiltInMax; + if (is_member_builtin(type, index, &builtin)) + is_using_builtin_array = true; + array_type = type_to_array_glsl(physical_type); + } + + auto result = join(pack_pfx, type_to_glsl(*declared_type, orig_id), " ", qualifier, to_member_name(type, index), + member_attribute_qualifier(type, index), array_type, ";"); + + is_using_builtin_array = false; + return result; +} + +// Emit a structure member, padding and packing to maintain the correct memeber alignments. +void CompilerMSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const string &qualifier, uint32_t) +{ + // If this member requires padding to maintain its declared offset, emit a dummy padding member before it. + if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPaddingTarget)) + { + uint32_t pad_len = get_extended_member_decoration(type.self, index, SPIRVCrossDecorationPaddingTarget); + statement("char _m", index, "_pad", "[", pad_len, "];"); + } + + // Handle HLSL-style 0-based vertex/instance index. + builtin_declaration = true; + statement(to_struct_member(type, member_type_id, index, qualifier)); + builtin_declaration = false; +} + +void CompilerMSL::emit_struct_padding_target(const SPIRType &type) +{ + uint32_t struct_size = get_declared_struct_size_msl(type, true, true); + uint32_t target_size = get_extended_decoration(type.self, SPIRVCrossDecorationPaddingTarget); + if (target_size < struct_size) + SPIRV_CROSS_THROW("Cannot pad with negative bytes."); + else if (target_size > struct_size) + statement("char _m0_final_padding[", target_size - struct_size, "];"); +} + +// Return a MSL qualifier for the specified function attribute member +string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t index) +{ + auto &execution = get_entry_point(); + + uint32_t mbr_type_id = type.member_types[index]; + auto &mbr_type = get(mbr_type_id); + + BuiltIn builtin = BuiltInMax; + bool is_builtin = is_member_builtin(type, index, &builtin); + + if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationResourceIndexPrimary)) + { + string quals = join( + " [[id(", get_extended_member_decoration(type.self, index, SPIRVCrossDecorationResourceIndexPrimary), ")"); + if (interlocked_resources.count( + get_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID))) + quals += ", raster_order_group(0)"; + quals += "]]"; + return quals; + } + + // Vertex function inputs + if (execution.model == ExecutionModelVertex && type.storage == StorageClassInput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInBaseVertex: + case BuiltInInstanceId: + case BuiltInInstanceIndex: + case BuiltInBaseInstance: + if (msl_options.vertex_for_tessellation) + return ""; + return string(" [[") + builtin_qualifier(builtin) + "]]"; + + case BuiltInDrawIndex: + SPIRV_CROSS_THROW("DrawIndex is not supported in MSL."); + + default: + return ""; + } + } + uint32_t locn = get_ordered_member_location(type.self, index); + if (locn != k_unknown_location) + return string(" [[attribute(") + convert_to_string(locn) + ")]]"; + } + + // Vertex and tessellation evaluation function outputs + if (((execution.model == ExecutionModelVertex && !msl_options.vertex_for_tessellation) || + execution.model == ExecutionModelTessellationEvaluation) && + type.storage == StorageClassOutput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInPointSize: + // Only mark the PointSize builtin if really rendering points. + // Some shaders may include a PointSize builtin even when used to render + // non-point topologies, and Metal will reject this builtin when compiling + // the shader into a render pipeline that uses a non-point topology. + return msl_options.enable_point_size_builtin ? (string(" [[") + builtin_qualifier(builtin) + "]]") : ""; + + case BuiltInViewportIndex: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); + /* fallthrough */ + case BuiltInPosition: + case BuiltInLayer: + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + + case BuiltInClipDistance: + if (has_member_decoration(type.self, index, DecorationLocation)) + return join(" [[user(clip", get_member_decoration(type.self, index, DecorationLocation), ")]]"); + else + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + + default: + return ""; + } + } + uint32_t comp; + uint32_t locn = get_ordered_member_location(type.self, index, &comp); + if (locn != k_unknown_location) + { + if (comp != k_unknown_component) + return string(" [[user(locn") + convert_to_string(locn) + "_" + convert_to_string(comp) + ")]]"; + else + return string(" [[user(locn") + convert_to_string(locn) + ")]]"; + } + } + + // Tessellation control function inputs + if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassInput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInInvocationId: + case BuiltInPrimitiveId: + if (msl_options.multi_patch_workgroup) + return ""; + /* fallthrough */ + case BuiltInSubgroupLocalInvocationId: // FIXME: Should work in any stage + case BuiltInSubgroupSize: // FIXME: Should work in any stage + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + case BuiltInPatchVertices: + return ""; + // Others come from stage input. + default: + break; + } + } + if (msl_options.multi_patch_workgroup) + return ""; + uint32_t locn = get_ordered_member_location(type.self, index); + if (locn != k_unknown_location) + return string(" [[attribute(") + convert_to_string(locn) + ")]]"; + } + + // Tessellation control function outputs + if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassOutput) + { + // For this type of shader, we always arrange for it to capture its + // output to a buffer. For this reason, qualifiers are irrelevant here. + return ""; + } + + // Tessellation evaluation function inputs + if (execution.model == ExecutionModelTessellationEvaluation && type.storage == StorageClassInput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInPrimitiveId: + case BuiltInTessCoord: + return string(" [[") + builtin_qualifier(builtin) + "]]"; + case BuiltInPatchVertices: + return ""; + // Others come from stage input. + default: + break; + } + } + // The special control point array must not be marked with an attribute. + if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray) + return ""; + uint32_t locn = get_ordered_member_location(type.self, index); + if (locn != k_unknown_location) + return string(" [[attribute(") + convert_to_string(locn) + ")]]"; + } + + // Tessellation evaluation function outputs were handled above. + + // Fragment function inputs + if (execution.model == ExecutionModelFragment && type.storage == StorageClassInput) + { + string quals; + if (is_builtin) + { + switch (builtin) + { + case BuiltInViewIndex: + if (!msl_options.multiview || !msl_options.multiview_layered_rendering) + break; + /* fallthrough */ + case BuiltInFrontFacing: + case BuiltInPointCoord: + case BuiltInFragCoord: + case BuiltInSampleId: + case BuiltInSampleMask: + case BuiltInLayer: + case BuiltInBaryCoordNV: + case BuiltInBaryCoordNoPerspNV: + quals = builtin_qualifier(builtin); + break; + + case BuiltInClipDistance: + return join(" [[user(clip", get_member_decoration(type.self, index, DecorationLocation), ")]]"); + + default: + break; + } + } + else + { + uint32_t comp; + uint32_t locn = get_ordered_member_location(type.self, index, &comp); + if (locn != k_unknown_location) + { + // For user-defined attributes, this is fine. From Vulkan spec: + // A user-defined output variable is considered to match an input variable in the subsequent stage if + // the two variables are declared with the same Location and Component decoration and match in type + // and decoration, except that interpolation decorations are not required to match. For the purposes + // of interface matching, variables declared without a Component decoration are considered to have a + // Component decoration of zero. + + if (comp != k_unknown_component && comp != 0) + quals = string("user(locn") + convert_to_string(locn) + "_" + convert_to_string(comp) + ")"; + else + quals = string("user(locn") + convert_to_string(locn) + ")"; + } + } + + if (builtin == BuiltInBaryCoordNV || builtin == BuiltInBaryCoordNoPerspNV) + { + if (has_member_decoration(type.self, index, DecorationFlat) || + has_member_decoration(type.self, index, DecorationCentroid) || + has_member_decoration(type.self, index, DecorationSample) || + has_member_decoration(type.self, index, DecorationNoPerspective)) + { + // NoPerspective is baked into the builtin type. + SPIRV_CROSS_THROW( + "Flat, Centroid, Sample, NoPerspective decorations are not supported for BaryCoord inputs."); + } + } + + // Don't bother decorating integers with the 'flat' attribute; it's + // the default (in fact, the only option). Also don't bother with the + // FragCoord builtin; it's always noperspective on Metal. + if (!type_is_integral(mbr_type) && (!is_builtin || builtin != BuiltInFragCoord)) + { + if (has_member_decoration(type.self, index, DecorationFlat)) + { + if (!quals.empty()) + quals += ", "; + quals += "flat"; + } + else if (has_member_decoration(type.self, index, DecorationCentroid)) + { + if (!quals.empty()) + quals += ", "; + if (has_member_decoration(type.self, index, DecorationNoPerspective)) + quals += "centroid_no_perspective"; + else + quals += "centroid_perspective"; + } + else if (has_member_decoration(type.self, index, DecorationSample)) + { + if (!quals.empty()) + quals += ", "; + if (has_member_decoration(type.self, index, DecorationNoPerspective)) + quals += "sample_no_perspective"; + else + quals += "sample_perspective"; + } + else if (has_member_decoration(type.self, index, DecorationNoPerspective)) + { + if (!quals.empty()) + quals += ", "; + quals += "center_no_perspective"; + } + } + + if (!quals.empty()) + return " [[" + quals + "]]"; + } + + // Fragment function outputs + if (execution.model == ExecutionModelFragment && type.storage == StorageClassOutput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInFragStencilRefEXT: + // Similar to PointSize, only mark FragStencilRef if there's a stencil buffer. + // Some shaders may include a FragStencilRef builtin even when used to render + // without a stencil attachment, and Metal will reject this builtin + // when compiling the shader into a render pipeline that does not set + // stencilAttachmentPixelFormat. + if (!msl_options.enable_frag_stencil_ref_builtin) + return ""; + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Stencil export only supported in MSL 2.1 and up."); + return string(" [[") + builtin_qualifier(builtin) + "]]"; + + case BuiltInFragDepth: + // Ditto FragDepth. + if (!msl_options.enable_frag_depth_builtin) + return ""; + /* fallthrough */ + case BuiltInSampleMask: + return string(" [[") + builtin_qualifier(builtin) + "]]"; + + default: + return ""; + } + } + uint32_t locn = get_ordered_member_location(type.self, index); + // Metal will likely complain about missing color attachments, too. + if (locn != k_unknown_location && !(msl_options.enable_frag_output_mask & (1 << locn))) + return ""; + if (locn != k_unknown_location && has_member_decoration(type.self, index, DecorationIndex)) + return join(" [[color(", locn, "), index(", get_member_decoration(type.self, index, DecorationIndex), + ")]]"); + else if (locn != k_unknown_location) + return join(" [[color(", locn, ")]]"); + else if (has_member_decoration(type.self, index, DecorationIndex)) + return join(" [[index(", get_member_decoration(type.self, index, DecorationIndex), ")]]"); + else + return ""; + } + + // Compute function inputs + if (execution.model == ExecutionModelGLCompute && type.storage == StorageClassInput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInGlobalInvocationId: + case BuiltInWorkgroupId: + case BuiltInNumWorkgroups: + case BuiltInLocalInvocationId: + case BuiltInLocalInvocationIndex: + case BuiltInNumSubgroups: + case BuiltInSubgroupId: + case BuiltInSubgroupLocalInvocationId: // FIXME: Should work in any stage + case BuiltInSubgroupSize: // FIXME: Should work in any stage + return string(" [[") + builtin_qualifier(builtin) + "]]"; + + default: + return ""; + } + } + } + + return ""; +} + +// Returns the location decoration of the member with the specified index in the specified type. +// If the location of the member has been explicitly set, that location is used. If not, this +// function assumes the members are ordered in their location order, and simply returns the +// index as the location. +uint32_t CompilerMSL::get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp) +{ + auto &m = ir.meta[type_id]; + if (index < m.members.size()) + { + auto &dec = m.members[index]; + if (comp) + { + if (dec.decoration_flags.get(DecorationComponent)) + *comp = dec.component; + else + *comp = k_unknown_component; + } + if (dec.decoration_flags.get(DecorationLocation)) + return dec.location; + } + + return index; +} + +// Returns the type declaration for a function, including the +// entry type if the current function is the entry point function +string CompilerMSL::func_type_decl(SPIRType &type) +{ + // The regular function return type. If not processing the entry point function, that's all we need + string return_type = type_to_glsl(type) + type_to_array_glsl(type); + if (!processing_entry_point) + return return_type; + + // If an outgoing interface block has been defined, and it should be returned, override the entry point return type + bool ep_should_return_output = !get_is_rasterization_disabled(); + if (stage_out_var_id && ep_should_return_output) + return_type = type_to_glsl(get_stage_out_struct_type()) + type_to_array_glsl(type); + + // Prepend a entry type, based on the execution model + string entry_type; + auto &execution = get_entry_point(); + switch (execution.model) + { + case ExecutionModelVertex: + if (msl_options.vertex_for_tessellation && !msl_options.supports_msl_version(1, 2)) + SPIRV_CROSS_THROW("Tessellation requires Metal 1.2."); + entry_type = msl_options.vertex_for_tessellation ? "kernel" : "vertex"; + break; + case ExecutionModelTessellationEvaluation: + if (!msl_options.supports_msl_version(1, 2)) + SPIRV_CROSS_THROW("Tessellation requires Metal 1.2."); + if (execution.flags.get(ExecutionModeIsolines)) + SPIRV_CROSS_THROW("Metal does not support isoline tessellation."); + if (msl_options.is_ios()) + entry_type = + join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ") ]] vertex"); + else + entry_type = join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ", ", + execution.output_vertices, ") ]] vertex"); + break; + case ExecutionModelFragment: + entry_type = execution.flags.get(ExecutionModeEarlyFragmentTests) || + execution.flags.get(ExecutionModePostDepthCoverage) ? + "[[ early_fragment_tests ]] fragment" : + "fragment"; + break; + case ExecutionModelTessellationControl: + if (!msl_options.supports_msl_version(1, 2)) + SPIRV_CROSS_THROW("Tessellation requires Metal 1.2."); + if (execution.flags.get(ExecutionModeIsolines)) + SPIRV_CROSS_THROW("Metal does not support isoline tessellation."); + /* fallthrough */ + case ExecutionModelGLCompute: + case ExecutionModelKernel: + entry_type = "kernel"; + break; + default: + entry_type = "unknown"; + break; + } + + return entry_type + " " + return_type; +} + +// In MSL, address space qualifiers are required for all pointer or reference variables +string CompilerMSL::get_argument_address_space(const SPIRVariable &argument) +{ + const auto &type = get(argument.basetype); + return get_type_address_space(type, argument.self, true); +} + +string CompilerMSL::get_type_address_space(const SPIRType &type, uint32_t id, bool argument) +{ + // This can be called for variable pointer contexts as well, so be very careful about which method we choose. + Bitset flags; + auto *var = maybe_get(id); + if (var && type.basetype == SPIRType::Struct && + (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))) + flags = get_buffer_block_flags(id); + else + flags = get_decoration_bitset(id); + + const char *addr_space = nullptr; + switch (type.storage) + { + case StorageClassWorkgroup: + addr_space = "threadgroup"; + break; + + case StorageClassStorageBuffer: + { + // For arguments from variable pointers, we use the write count deduction, so + // we should not assume any constness here. Only for global SSBOs. + bool readonly = false; + if (!var || has_decoration(type.self, DecorationBlock)) + readonly = flags.get(DecorationNonWritable); + + addr_space = readonly ? "const device" : "device"; + break; + } + + case StorageClassUniform: + case StorageClassUniformConstant: + case StorageClassPushConstant: + if (type.basetype == SPIRType::Struct) + { + bool ssbo = has_decoration(type.self, DecorationBufferBlock); + if (ssbo) + addr_space = flags.get(DecorationNonWritable) ? "const device" : "device"; + else + addr_space = "constant"; + } + else if (!argument) + { + addr_space = "constant"; + } + else if (type_is_msl_framebuffer_fetch(type)) + { + // Subpass inputs are passed around by value. + addr_space = ""; + } + break; + + case StorageClassFunction: + case StorageClassGeneric: + break; + + case StorageClassInput: + if (get_execution_model() == ExecutionModelTessellationControl && var && + var->basevariable == stage_in_ptr_var_id) + addr_space = msl_options.multi_patch_workgroup ? "constant" : "threadgroup"; + if (get_execution_model() == ExecutionModelFragment && var && var->basevariable == stage_in_var_id) + addr_space = "thread"; + break; + + case StorageClassOutput: + if (capture_output_to_buffer) + addr_space = "device"; + break; + + default: + break; + } + + if (!addr_space) + // No address space for plain values. + addr_space = type.pointer || (argument && type.basetype == SPIRType::ControlPointArray) ? "thread" : ""; + + return join(flags.get(DecorationVolatile) || flags.get(DecorationCoherent) ? "volatile " : "", addr_space); +} + +const char *CompilerMSL::to_restrict(uint32_t id, bool space) +{ + // This can be called for variable pointer contexts as well, so be very careful about which method we choose. + Bitset flags; + if (ir.ids[id].get_type() == TypeVariable) + { + uint32_t type_id = expression_type_id(id); + auto &type = expression_type(id); + if (type.basetype == SPIRType::Struct && + (has_decoration(type_id, DecorationBlock) || has_decoration(type_id, DecorationBufferBlock))) + flags = get_buffer_block_flags(id); + else + flags = get_decoration_bitset(id); + } + else + flags = get_decoration_bitset(id); + + return flags.get(DecorationRestrict) ? (space ? "restrict " : "restrict") : ""; +} + +string CompilerMSL::entry_point_arg_stage_in() +{ + string decl; + + if (get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup) + return decl; + + // Stage-in structure + uint32_t stage_in_id; + if (get_execution_model() == ExecutionModelTessellationEvaluation) + stage_in_id = patch_stage_in_var_id; + else + stage_in_id = stage_in_var_id; + + if (stage_in_id) + { + auto &var = get(stage_in_id); + auto &type = get_variable_data_type(var); + + add_resource_name(var.self); + decl = join(type_to_glsl(type), " ", to_name(var.self), " [[stage_in]]"); + } + + return decl; +} + +// Returns true if this input builtin should be a direct parameter on a shader function parameter list, +// and false for builtins that should be passed or calculated some other way. +bool CompilerMSL::is_direct_input_builtin(BuiltIn bi_type) +{ + switch (bi_type) + { + // Vertex function in + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInBaseVertex: + case BuiltInInstanceId: + case BuiltInInstanceIndex: + case BuiltInBaseInstance: + return get_execution_model() != ExecutionModelVertex || !msl_options.vertex_for_tessellation; + // Tess. control function in + case BuiltInPosition: + case BuiltInPointSize: + case BuiltInClipDistance: + case BuiltInCullDistance: + case BuiltInPatchVertices: + return false; + case BuiltInInvocationId: + case BuiltInPrimitiveId: + return get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup; + // Tess. evaluation function in + case BuiltInTessLevelInner: + case BuiltInTessLevelOuter: + return false; + // Fragment function in + case BuiltInSamplePosition: + case BuiltInHelperInvocation: + case BuiltInBaryCoordNV: + case BuiltInBaryCoordNoPerspNV: + return false; + case BuiltInViewIndex: + return get_execution_model() == ExecutionModelFragment && msl_options.multiview && + msl_options.multiview_layered_rendering; + // Any stage function in + case BuiltInDeviceIndex: + case BuiltInSubgroupEqMask: + case BuiltInSubgroupGeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupLtMask: + return false; + case BuiltInSubgroupLocalInvocationId: + case BuiltInSubgroupSize: + return get_execution_model() == ExecutionModelGLCompute || + (get_execution_model() == ExecutionModelFragment && msl_options.supports_msl_version(2, 2)); + default: + return true; + } +} + +void CompilerMSL::entry_point_args_builtin(string &ep_args) +{ + // Builtin variables + SmallVector, 8> active_builtins; + ir.for_each_typed_id([&](uint32_t var_id, SPIRVariable &var) { + auto bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn)); + + // Don't emit SamplePosition as a separate parameter. In the entry + // point, we get that by calling get_sample_position() on the sample ID. + if (var.storage == StorageClassInput && is_builtin_variable(var) && + get_variable_data_type(var).basetype != SPIRType::Struct && + get_variable_data_type(var).basetype != SPIRType::ControlPointArray) + { + // If the builtin is not part of the active input builtin set, don't emit it. + // Relevant for multiple entry-point modules which might declare unused builtins. + if (!active_input_builtins.get(bi_type) || !interface_variable_exists_in_entry_point(var_id)) + return; + + // Remember this variable. We may need to correct its type. + active_builtins.push_back(make_pair(&var, bi_type)); + + if (is_direct_input_builtin(bi_type)) + { + if (!ep_args.empty()) + ep_args += ", "; + + // Handle HLSL-style 0-based vertex/instance index. + builtin_declaration = true; + ep_args += builtin_type_decl(bi_type, var_id) + " " + to_expression(var_id); + ep_args += " [[" + builtin_qualifier(bi_type); + if (bi_type == BuiltInSampleMask && get_entry_point().flags.get(ExecutionModePostDepthCoverage)) + { + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Post-depth coverage requires MSL 2.0."); + if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Post-depth coverage on Mac requires MSL 2.3."); + ep_args += ", post_depth_coverage"; + } + ep_args += "]]"; + builtin_declaration = false; + } + } + + if (var.storage == StorageClassInput && + has_extended_decoration(var_id, SPIRVCrossDecorationBuiltInDispatchBase)) + { + // This is a special implicit builtin, not corresponding to any SPIR-V builtin, + // which holds the base that was passed to vkCmdDispatchBase() or vkCmdDrawIndexed(). If it's present, + // assume we emitted it for a good reason. + assert(msl_options.supports_msl_version(1, 2)); + if (!ep_args.empty()) + ep_args += ", "; + + ep_args += type_to_glsl(get_variable_data_type(var)) + " " + to_expression(var_id) + " [[grid_origin]]"; + } + + if (var.storage == StorageClassInput && + has_extended_decoration(var_id, SPIRVCrossDecorationBuiltInStageInputSize)) + { + // This is another special implicit builtin, not corresponding to any SPIR-V builtin, + // which holds the number of vertices and instances to draw. If it's present, + // assume we emitted it for a good reason. + assert(msl_options.supports_msl_version(1, 2)); + if (!ep_args.empty()) + ep_args += ", "; + + ep_args += type_to_glsl(get_variable_data_type(var)) + " " + to_expression(var_id) + " [[grid_size]]"; + } + }); + + // Correct the types of all encountered active builtins. We couldn't do this before + // because ensure_correct_builtin_type() may increase the bound, which isn't allowed + // while iterating over IDs. + for (auto &var : active_builtins) + var.first->basetype = ensure_correct_builtin_type(var.first->basetype, var.second); + + // Handle HLSL-style 0-based vertex/instance index. + if (needs_base_vertex_arg == TriState::Yes) + ep_args += built_in_func_arg(BuiltInBaseVertex, !ep_args.empty()); + + if (needs_base_instance_arg == TriState::Yes) + ep_args += built_in_func_arg(BuiltInBaseInstance, !ep_args.empty()); + + if (capture_output_to_buffer) + { + // Add parameters to hold the indirect draw parameters and the shader output. This has to be handled + // specially because it needs to be a pointer, not a reference. + if (stage_out_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("device ", type_to_glsl(get_stage_out_struct_type()), "* ", output_buffer_var_name, + " [[buffer(", msl_options.shader_output_buffer_index, ")]]"); + } + + if (get_execution_model() == ExecutionModelTessellationControl) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + join("constant uint* spvIndirectParams [[buffer(", msl_options.indirect_params_buffer_index, ")]]"); + } + else if (stage_out_var_id && + !(get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation)) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + join("device uint* spvIndirectParams [[buffer(", msl_options.indirect_params_buffer_index, ")]]"); + } + + if (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation && + (active_input_builtins.get(BuiltInVertexIndex) || active_input_builtins.get(BuiltInVertexId)) && + msl_options.vertex_index_type != Options::IndexType::None) + { + // Add the index buffer so we can set gl_VertexIndex correctly. + if (!ep_args.empty()) + ep_args += ", "; + switch (msl_options.vertex_index_type) + { + case Options::IndexType::None: + break; + case Options::IndexType::UInt16: + ep_args += join("const device ushort* ", index_buffer_var_name, " [[buffer(", + msl_options.shader_index_buffer_index, ")]]"); + break; + case Options::IndexType::UInt32: + ep_args += join("const device uint* ", index_buffer_var_name, " [[buffer(", + msl_options.shader_index_buffer_index, ")]]"); + break; + } + } + + // Tessellation control shaders get three additional parameters: + // a buffer to hold the per-patch data, a buffer to hold the per-patch + // tessellation levels, and a block of workgroup memory to hold the + // input control point data. + if (get_execution_model() == ExecutionModelTessellationControl) + { + if (patch_stage_out_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + join("device ", type_to_glsl(get_patch_stage_out_struct_type()), "* ", patch_output_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_patch_output_buffer_index), ")]]"); + } + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("device ", get_tess_factor_struct_name(), "* ", tess_factor_buffer_var_name, " [[buffer(", + convert_to_string(msl_options.shader_tess_factor_buffer_index), ")]]"); + if (stage_in_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + if (msl_options.multi_patch_workgroup) + { + ep_args += join("device ", type_to_glsl(get_stage_in_struct_type()), "* ", input_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_input_buffer_index), ")]]"); + } + else + { + ep_args += join("threadgroup ", type_to_glsl(get_stage_in_struct_type()), "* ", input_wg_var_name, + " [[threadgroup(", convert_to_string(msl_options.shader_input_wg_index), ")]]"); + } + } + } + } +} + +string CompilerMSL::entry_point_args_argument_buffer(bool append_comma) +{ + string ep_args = entry_point_arg_stage_in(); + Bitset claimed_bindings; + + for (uint32_t i = 0; i < kMaxArgumentBuffers; i++) + { + uint32_t id = argument_buffer_ids[i]; + if (id == 0) + continue; + + add_resource_name(id); + auto &var = get(id); + auto &type = get_variable_data_type(var); + + if (!ep_args.empty()) + ep_args += ", "; + + // Check if the argument buffer binding itself has been remapped. + uint32_t buffer_binding; + auto itr = resource_bindings.find({ get_entry_point().model, i, kArgumentBufferBinding }); + if (itr != end(resource_bindings)) + { + buffer_binding = itr->second.first.msl_buffer; + itr->second.second = true; + } + else + { + // As a fallback, directly map desc set <-> binding. + // If that was taken, take the next buffer binding. + if (claimed_bindings.get(i)) + buffer_binding = next_metal_resource_index_buffer; + else + buffer_binding = i; + } + + claimed_bindings.set(buffer_binding); + + ep_args += get_argument_address_space(var) + " " + type_to_glsl(type) + "& " + to_restrict(id) + to_name(id); + ep_args += " [[buffer(" + convert_to_string(buffer_binding) + ")]]"; + + next_metal_resource_index_buffer = max(next_metal_resource_index_buffer, buffer_binding + 1); + } + + entry_point_args_discrete_descriptors(ep_args); + entry_point_args_builtin(ep_args); + + if (!ep_args.empty() && append_comma) + ep_args += ", "; + + return ep_args; +} + +const MSLConstexprSampler *CompilerMSL::find_constexpr_sampler(uint32_t id) const +{ + // Try by ID. + { + auto itr = constexpr_samplers_by_id.find(id); + if (itr != end(constexpr_samplers_by_id)) + return &itr->second; + } + + // Try by binding. + { + uint32_t desc_set = get_decoration(id, DecorationDescriptorSet); + uint32_t binding = get_decoration(id, DecorationBinding); + + auto itr = constexpr_samplers_by_binding.find({ desc_set, binding }); + if (itr != end(constexpr_samplers_by_binding)) + return &itr->second; + } + + return nullptr; +} + +void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args) +{ + // Output resources, sorted by resource index & type + // We need to sort to work around a bug on macOS 10.13 with NVidia drivers where switching between shaders + // with different order of buffers can result in issues with buffer assignments inside the driver. + struct Resource + { + SPIRVariable *var; + string name; + SPIRType::BaseType basetype; + uint32_t index; + uint32_t plane; + uint32_t secondary_index; + }; + + SmallVector resources; + + ir.for_each_typed_id([&](uint32_t var_id, SPIRVariable &var) { + if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || + var.storage == StorageClassPushConstant || var.storage == StorageClassStorageBuffer) && + !is_hidden_variable(var)) + { + auto &type = get_variable_data_type(var); + + // Very specifically, image load-store in argument buffers are disallowed on MSL on iOS. + // But we won't know when the argument buffer is encoded whether this image will have + // a NonWritable decoration. So just use discrete arguments for all storage images + // on iOS. + if (!(msl_options.is_ios() && type.basetype == SPIRType::Image && type.image.sampled == 2) && + var.storage != StorageClassPushConstant) + { + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + if (descriptor_set_is_argument_buffer(desc_set)) + return; + } + + const MSLConstexprSampler *constexpr_sampler = nullptr; + if (type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::Sampler) + { + constexpr_sampler = find_constexpr_sampler(var_id); + if (constexpr_sampler) + { + // Mark this ID as a constexpr sampler for later in case it came from set/bindings. + constexpr_samplers_by_id[var_id] = *constexpr_sampler; + } + } + + // Emulate texture2D atomic operations + uint32_t secondary_index = 0; + if (atomic_image_vars.count(var.self)) + { + secondary_index = get_metal_resource_index(var, SPIRType::AtomicCounter, 0); + } + + if (type.basetype == SPIRType::SampledImage) + { + add_resource_name(var_id); + + uint32_t plane_count = 1; + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + plane_count = constexpr_sampler->planes; + + for (uint32_t i = 0; i < plane_count; i++) + resources.push_back({ &var, to_name(var_id), SPIRType::Image, + get_metal_resource_index(var, SPIRType::Image, i), i, secondary_index }); + + if (type.image.dim != DimBuffer && !constexpr_sampler) + { + resources.push_back({ &var, to_sampler_expression(var_id), SPIRType::Sampler, + get_metal_resource_index(var, SPIRType::Sampler), 0, 0 }); + } + } + else if (!constexpr_sampler) + { + // constexpr samplers are not declared as resources. + add_resource_name(var_id); + resources.push_back({ &var, to_name(var_id), type.basetype, + get_metal_resource_index(var, type.basetype), 0, secondary_index }); + } + } + }); + + sort(resources.begin(), resources.end(), [](const Resource &lhs, const Resource &rhs) { + return tie(lhs.basetype, lhs.index) < tie(rhs.basetype, rhs.index); + }); + + for (auto &r : resources) + { + auto &var = *r.var; + auto &type = get_variable_data_type(var); + + uint32_t var_id = var.self; + + switch (r.basetype) + { + case SPIRType::Struct: + { + auto &m = ir.meta[type.self]; + if (m.members.size() == 0) + break; + if (!type.array.empty()) + { + if (type.array.size() > 1) + SPIRV_CROSS_THROW("Arrays of arrays of buffers are not supported."); + + // Metal doesn't directly support this, so we must expand the + // array. We'll declare a local array to hold these elements + // later. + uint32_t array_size = to_array_size_literal(type); + + if (array_size == 0) + SPIRV_CROSS_THROW("Unsized arrays of buffers are not supported in MSL."); + + // Allow Metal to use the array template to make arrays a value type + is_using_builtin_array = true; + buffer_arrays.push_back(var_id); + for (uint32_t i = 0; i < array_size; ++i) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += get_argument_address_space(var) + " " + type_to_glsl(type) + "* " + to_restrict(var_id) + + r.name + "_" + convert_to_string(i); + ep_args += " [[buffer(" + convert_to_string(r.index + i) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + is_using_builtin_array = false; + } + else + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + get_argument_address_space(var) + " " + type_to_glsl(type) + "& " + to_restrict(var_id) + r.name; + ep_args += " [[buffer(" + convert_to_string(r.index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + break; + } + case SPIRType::Sampler: + if (!ep_args.empty()) + ep_args += ", "; + ep_args += sampler_type(type, var_id) + " " + r.name; + ep_args += " [[sampler(" + convert_to_string(r.index) + ")]]"; + break; + case SPIRType::Image: + { + if (!ep_args.empty()) + ep_args += ", "; + + // Use Metal's native frame-buffer fetch API for subpass inputs. + const auto &basetype = get(var.basetype); + if (!type_is_msl_framebuffer_fetch(basetype)) + { + ep_args += image_type_glsl(type, var_id) + " " + r.name; + if (r.plane > 0) + ep_args += join(plane_name_suffix, r.plane); + ep_args += " [[texture(" + convert_to_string(r.index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + else + { + if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Framebuffer fetch on Mac is not supported before MSL 2.3."); + ep_args += image_type_glsl(type, var_id) + " " + r.name; + ep_args += " [[color(" + convert_to_string(r.index) + ")]]"; + } + + // Emulate texture2D atomic operations + if (atomic_image_vars.count(var.self)) + { + ep_args += ", device atomic_" + type_to_glsl(get(basetype.image.type), 0); + ep_args += "* " + r.name + "_atomic"; + ep_args += " [[buffer(" + convert_to_string(r.secondary_index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + break; + } + default: + if (!ep_args.empty()) + ep_args += ", "; + if (!type.pointer) + ep_args += get_type_address_space(get(var.basetype), var_id) + " " + + type_to_glsl(type, var_id) + "& " + r.name; + else + ep_args += type_to_glsl(type, var_id) + " " + r.name; + ep_args += " [[buffer(" + convert_to_string(r.index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + break; + } + } +} + +// Returns a string containing a comma-delimited list of args for the entry point function +// This is the "classic" method of MSL 1 when we don't have argument buffer support. +string CompilerMSL::entry_point_args_classic(bool append_comma) +{ + string ep_args = entry_point_arg_stage_in(); + entry_point_args_discrete_descriptors(ep_args); + entry_point_args_builtin(ep_args); + + if (!ep_args.empty() && append_comma) + ep_args += ", "; + + return ep_args; +} + +void CompilerMSL::fix_up_shader_inputs_outputs() +{ + auto &entry_func = this->get(ir.default_entry_point); + + // Emit a guard to ensure we don't execute beyond the last vertex. + // Vertex shaders shouldn't have the problems with barriers in non-uniform control flow that + // tessellation control shaders do, so early returns should be OK. We may need to revisit this + // if it ever becomes possible to use barriers from a vertex shader. + if (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation) + { + entry_func.fixup_hooks_in.push_back([this]() { + statement("if (any(", to_expression(builtin_invocation_id_id), + " >= ", to_expression(builtin_stage_input_size_id), "))"); + statement(" return;"); + }); + } + + // Look for sampled images and buffer. Add hooks to set up the swizzle constants or array lengths. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = get_variable_data_type(var); + uint32_t var_id = var.self; + bool ssbo = has_decoration(type.self, DecorationBufferBlock); + + if (var.storage == StorageClassUniformConstant && !is_hidden_variable(var)) + { + if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(type)) + { + entry_func.fixup_hooks_in.push_back([this, &type, &var, var_id]() { + bool is_array_type = !type.array.empty(); + + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + if (descriptor_set_is_argument_buffer(desc_set)) + { + statement("constant uint", is_array_type ? "* " : "& ", to_swizzle_expression(var_id), + is_array_type ? " = &" : " = ", to_name(argument_buffer_ids[desc_set]), + ".spvSwizzleConstants", "[", + convert_to_string(get_metal_resource_index(var, SPIRType::Image)), "];"); + } + else + { + // If we have an array of images, we need to be able to index into it, so take a pointer instead. + statement("constant uint", is_array_type ? "* " : "& ", to_swizzle_expression(var_id), + is_array_type ? " = &" : " = ", to_name(swizzle_buffer_id), "[", + convert_to_string(get_metal_resource_index(var, SPIRType::Image)), "];"); + } + }); + } + } + else if ((var.storage == StorageClassStorageBuffer || (var.storage == StorageClassUniform && ssbo)) && + !is_hidden_variable(var)) + { + if (buffers_requiring_array_length.count(var.self)) + { + entry_func.fixup_hooks_in.push_back([this, &type, &var, var_id]() { + bool is_array_type = !type.array.empty(); + + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + if (descriptor_set_is_argument_buffer(desc_set)) + { + statement("constant uint", is_array_type ? "* " : "& ", to_buffer_size_expression(var_id), + is_array_type ? " = &" : " = ", to_name(argument_buffer_ids[desc_set]), + ".spvBufferSizeConstants", "[", + convert_to_string(get_metal_resource_index(var, SPIRType::Image)), "];"); + } + else + { + // If we have an array of images, we need to be able to index into it, so take a pointer instead. + statement("constant uint", is_array_type ? "* " : "& ", to_buffer_size_expression(var_id), + is_array_type ? " = &" : " = ", to_name(buffer_size_buffer_id), "[", + convert_to_string(get_metal_resource_index(var, type.basetype)), "];"); + } + }); + } + } + }); + + // Builtin variables + ir.for_each_typed_id([this, &entry_func](uint32_t, SPIRVariable &var) { + uint32_t var_id = var.self; + BuiltIn bi_type = ir.meta[var_id].decoration.builtin_type; + + if (var.storage == StorageClassInput && is_builtin_variable(var)) + { + switch (bi_type) + { + case BuiltInSamplePosition: + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = get_sample_position(", + to_expression(builtin_sample_id_id), ");"); + }); + break; + case BuiltInHelperInvocation: + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("simd_is_helper_thread() is only supported on macOS."); + else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires version 2.1 on macOS."); + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = simd_is_helper_thread();"); + }); + break; + case BuiltInInvocationId: + // This is direct-mapped without multi-patch workgroups. + if (get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_invocation_id_id), ".x % ", this->get_entry_point().output_vertices, + ";"); + }); + break; + case BuiltInPrimitiveId: + // This is natively supported by fragment and tessellation evaluation shaders. + // In tessellation control shaders, this is direct-mapped without multi-patch workgroups. + if (get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = min(", + to_expression(builtin_invocation_id_id), ".x / ", this->get_entry_point().output_vertices, + ", spvIndirectParams[1]);"); + }); + break; + case BuiltInPatchVertices: + if (get_execution_model() == ExecutionModelTessellationEvaluation) + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(patch_stage_in_var_id), ".gl_in.size();"); + }); + else + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = spvIndirectParams[0];"); + }); + break; + case BuiltInTessCoord: + // Emit a fixup to account for the shifted domain. Don't do this for triangles; + // MoltenVK will just reverse the winding order instead. + if (msl_options.tess_domain_origin_lower_left && !get_entry_point().flags.get(ExecutionModeTriangles)) + { + string tc = to_expression(var_id); + entry_func.fixup_hooks_in.push_back([=]() { statement(tc, ".y = 1.0 - ", tc, ".y;"); }); + } + break; + case BuiltInSubgroupLocalInvocationId: + // This is natively supported in compute shaders. + if (get_execution_model() == ExecutionModelGLCompute) + break; + + // This is natively supported in fragment shaders in MSL 2.2. + if (get_execution_model() == ExecutionModelFragment && msl_options.supports_msl_version(2, 2)) + break; + + if (msl_options.is_ios()) + SPIRV_CROSS_THROW( + "SubgroupLocalInvocationId cannot be used outside of compute shaders before MSL 2.2 on iOS."); + + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW( + "SubgroupLocalInvocationId cannot be used outside of compute shaders before MSL 2.1."); + + // Shaders other than compute shaders don't support the SIMD-group + // builtins directly, but we can emulate them using the SIMD-group + // functions. This might break if some of the subgroup terminated + // before reaching the entry point. + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = simd_prefix_exclusive_sum(1);"); + }); + break; + case BuiltInSubgroupSize: + // This is natively supported in compute shaders. + if (get_execution_model() == ExecutionModelGLCompute) + break; + + // This is natively supported in fragment shaders in MSL 2.2. + if (get_execution_model() == ExecutionModelFragment && msl_options.supports_msl_version(2, 2)) + break; + + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("SubgroupSize cannot be used outside of compute shaders on iOS."); + + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("SubgroupSize cannot be used outside of compute shaders before Metal 2.1."); + + entry_func.fixup_hooks_in.push_back( + [=]() { statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = simd_sum(1);"); }); + break; + case BuiltInSubgroupEqMask: + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Subgroup ballot functionality is unavailable on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_subgroup_invocation_id_id), " >= 32 ? uint4(0, (1 << (", + to_expression(builtin_subgroup_invocation_id_id), " - 32)), uint2(0)) : uint4(1 << ", + to_expression(builtin_subgroup_invocation_id_id), ", uint3(0));"); + }); + break; + case BuiltInSubgroupGeMask: + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Subgroup ballot functionality is unavailable on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + entry_func.fixup_hooks_in.push_back([=]() { + // Case where index < 32, size < 32: + // mask0 = bfi(0, 0xFFFFFFFF, index, size - index); + // mask1 = bfi(0, 0xFFFFFFFF, 0, 0); // Gives 0 + // Case where index < 32 but size >= 32: + // mask0 = bfi(0, 0xFFFFFFFF, index, 32 - index); + // mask1 = bfi(0, 0xFFFFFFFF, 0, size - 32); + // Case where index >= 32: + // mask0 = bfi(0, 0xFFFFFFFF, 32, 0); // Gives 0 + // mask1 = bfi(0, 0xFFFFFFFF, index - 32, size - index); + // This is expressed without branches to avoid divergent + // control flow--hence the complicated min/max expressions. + // This is further complicated by the fact that if you attempt + // to bfi/bfe out-of-bounds on Metal, undefined behavior is the + // result. + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, min(", + to_expression(builtin_subgroup_invocation_id_id), ", 32u), (uint)max(min((int)", + to_expression(builtin_subgroup_size_id), ", 32) - (int)", + to_expression(builtin_subgroup_invocation_id_id), + ", 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " - 32, 0), (uint)max((int)", + to_expression(builtin_subgroup_size_id), " - (int)max(", + to_expression(builtin_subgroup_invocation_id_id), ", 32u), 0)), uint2(0));"); + }); + break; + case BuiltInSubgroupGtMask: + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Subgroup ballot functionality is unavailable on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + entry_func.fixup_hooks_in.push_back([=]() { + // The same logic applies here, except now the index is one + // more than the subgroup invocation ID. + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, min(", + to_expression(builtin_subgroup_invocation_id_id), " + 1, 32u), (uint)max(min((int)", + to_expression(builtin_subgroup_size_id), ", 32) - (int)", + to_expression(builtin_subgroup_invocation_id_id), + " - 1, 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " + 1 - 32, 0), (uint)max((int)", + to_expression(builtin_subgroup_size_id), " - (int)max(", + to_expression(builtin_subgroup_invocation_id_id), " + 1, 32u), 0)), uint2(0));"); + }); + break; + case BuiltInSubgroupLeMask: + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Subgroup ballot functionality is unavailable on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(extract_bits(0xFFFFFFFF, 0, min(", + to_expression(builtin_subgroup_invocation_id_id), + " + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " + 1 - 32, 0)), uint2(0));"); + }); + break; + case BuiltInSubgroupLtMask: + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Subgroup ballot functionality is unavailable on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(extract_bits(0xFFFFFFFF, 0, min(", + to_expression(builtin_subgroup_invocation_id_id), + ", 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " - 32, 0)), uint2(0));"); + }); + break; + case BuiltInViewIndex: + if (!msl_options.multiview) + { + // According to the Vulkan spec, when not running under a multiview + // render pass, ViewIndex is 0. + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = 0;"); + }); + } + else if (msl_options.view_index_from_device_index) + { + // In this case, we take the view index from that of the device we're running on. + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + msl_options.device_index, ";"); + }); + // We actually don't want to set the render_target_array_index here. + // Since every physical device is rendering a different view, + // there's no need for layered rendering here. + } + else if (!msl_options.multiview_layered_rendering) + { + // In this case, the views are rendered one at a time. The view index, then, + // is just the first part of the "view mask". + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(view_mask_buffer_id), "[0];"); + }); + } + else if (get_execution_model() == ExecutionModelFragment) + { + // Because we adjusted the view index in the vertex shader, we have to + // adjust it back here. + entry_func.fixup_hooks_in.push_back([=]() { + statement(to_expression(var_id), " += ", to_expression(view_mask_buffer_id), "[0];"); + }); + } + else if (get_execution_model() == ExecutionModelVertex) + { + // Metal provides no special support for multiview, so we smuggle + // the view index in the instance index. + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(view_mask_buffer_id), "[0] + (", to_expression(builtin_instance_idx_id), + " - ", to_expression(builtin_base_instance_id), ") % ", + to_expression(view_mask_buffer_id), "[1];"); + statement(to_expression(builtin_instance_idx_id), " = (", + to_expression(builtin_instance_idx_id), " - ", + to_expression(builtin_base_instance_id), ") / ", to_expression(view_mask_buffer_id), + "[1] + ", to_expression(builtin_base_instance_id), ";"); + }); + // In addition to setting the variable itself, we also need to + // set the render_target_array_index with it on output. We have to + // offset this by the base view index, because Metal isn't in on + // our little game here. + entry_func.fixup_hooks_out.push_back([=]() { + statement(to_expression(builtin_layer_id), " = ", to_expression(var_id), " - ", + to_expression(view_mask_buffer_id), "[0];"); + }); + } + break; + case BuiltInDeviceIndex: + // Metal pipelines belong to the devices which create them, so we'll + // need to create a MTLPipelineState for every MTLDevice in a grouped + // VkDevice. We can assume, then, that the device index is constant. + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + msl_options.device_index, ";"); + }); + break; + case BuiltInWorkgroupId: + if (!msl_options.dispatch_base || !active_input_builtins.get(BuiltInWorkgroupId)) + break; + + // The vkCmdDispatchBase() command lets the client set the base value + // of WorkgroupId. Metal has no direct equivalent; we must make this + // adjustment ourselves. + entry_func.fixup_hooks_in.push_back([=]() { + statement(to_expression(var_id), " += ", to_dereferenced_expression(builtin_dispatch_base_id), ";"); + }); + break; + case BuiltInGlobalInvocationId: + if (!msl_options.dispatch_base || !active_input_builtins.get(BuiltInGlobalInvocationId)) + break; + + // GlobalInvocationId is defined as LocalInvocationId + WorkgroupId * WorkgroupSize. + // This needs to be adjusted too. + entry_func.fixup_hooks_in.push_back([=]() { + auto &execution = this->get_entry_point(); + uint32_t workgroup_size_id = execution.workgroup_size.constant; + if (workgroup_size_id) + statement(to_expression(var_id), " += ", to_dereferenced_expression(builtin_dispatch_base_id), + " * ", to_expression(workgroup_size_id), ";"); + else + statement(to_expression(var_id), " += ", to_dereferenced_expression(builtin_dispatch_base_id), + " * uint3(", execution.workgroup_size.x, ", ", execution.workgroup_size.y, ", ", + execution.workgroup_size.z, ");"); + }); + break; + case BuiltInVertexId: + case BuiltInVertexIndex: + // This is direct-mapped normally. + if (!msl_options.vertex_for_tessellation) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + builtin_declaration = true; + switch (msl_options.vertex_index_type) + { + case Options::IndexType::None: + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_invocation_id_id), ".x + ", + to_expression(builtin_dispatch_base_id), ".x;"); + break; + case Options::IndexType::UInt16: + case Options::IndexType::UInt32: + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", index_buffer_var_name, + "[", to_expression(builtin_invocation_id_id), ".x] + ", + to_expression(builtin_dispatch_base_id), ".x;"); + break; + } + builtin_declaration = false; + }); + break; + case BuiltInBaseVertex: + // This is direct-mapped normally. + if (!msl_options.vertex_for_tessellation) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_dispatch_base_id), ".x;"); + }); + break; + case BuiltInInstanceId: + case BuiltInInstanceIndex: + // This is direct-mapped normally. + if (!msl_options.vertex_for_tessellation) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + builtin_declaration = true; + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_invocation_id_id), ".y + ", to_expression(builtin_dispatch_base_id), + ".y;"); + builtin_declaration = false; + }); + break; + case BuiltInBaseInstance: + // This is direct-mapped normally. + if (!msl_options.vertex_for_tessellation) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_dispatch_base_id), ".y;"); + }); + break; + default: + break; + } + } + else if (var.storage == StorageClassOutput && is_builtin_variable(var)) + { + if (bi_type == BuiltInSampleMask && get_execution_model() == ExecutionModelFragment && + msl_options.additional_fixed_sample_mask != 0xffffffff) + { + // If the additional fixed sample mask was set, we need to adjust the sample_mask + // output to reflect that. If the shader outputs the sample_mask itself too, we need + // to AND the two masks to get the final one. + if (does_shader_write_sample_mask) + { + entry_func.fixup_hooks_out.push_back([=]() { + statement(to_expression(builtin_sample_mask_id), + " &= ", msl_options.additional_fixed_sample_mask, ";"); + }); + } + else + { + entry_func.fixup_hooks_out.push_back([=]() { + statement(to_expression(builtin_sample_mask_id), " = ", + msl_options.additional_fixed_sample_mask, ";"); + }); + } + } + } + }); +} + +// Returns the Metal index of the resource of the specified type as used by the specified variable. +uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane) +{ + auto &execution = get_entry_point(); + auto &var_dec = ir.meta[var.self].decoration; + auto &var_type = get(var.basetype); + uint32_t var_desc_set = (var.storage == StorageClassPushConstant) ? kPushConstDescSet : var_dec.set; + uint32_t var_binding = (var.storage == StorageClassPushConstant) ? kPushConstBinding : var_dec.binding; + + // If a matching binding has been specified, find and use it. + auto itr = resource_bindings.find({ execution.model, var_desc_set, var_binding }); + + // Atomic helper buffers for image atomics need to use secondary bindings as well. + bool use_secondary_binding = (var_type.basetype == SPIRType::SampledImage && basetype == SPIRType::Sampler) || + basetype == SPIRType::AtomicCounter; + + auto resource_decoration = + use_secondary_binding ? SPIRVCrossDecorationResourceIndexSecondary : SPIRVCrossDecorationResourceIndexPrimary; + + if (plane == 1) + resource_decoration = SPIRVCrossDecorationResourceIndexTertiary; + if (plane == 2) + resource_decoration = SPIRVCrossDecorationResourceIndexQuaternary; + + if (itr != end(resource_bindings)) + { + auto &remap = itr->second; + remap.second = true; + switch (basetype) + { + case SPIRType::Image: + set_extended_decoration(var.self, resource_decoration, remap.first.msl_texture + plane); + return remap.first.msl_texture + plane; + case SPIRType::Sampler: + set_extended_decoration(var.self, resource_decoration, remap.first.msl_sampler); + return remap.first.msl_sampler; + default: + set_extended_decoration(var.self, resource_decoration, remap.first.msl_buffer); + return remap.first.msl_buffer; + } + } + + // If we have already allocated an index, keep using it. + if (has_extended_decoration(var.self, resource_decoration)) + return get_extended_decoration(var.self, resource_decoration); + + // Allow user to enable decoration binding + if (msl_options.enable_decoration_binding) + { + // If there is no explicit mapping of bindings to MSL, use the declared binding. + if (has_decoration(var.self, DecorationBinding)) + { + var_binding = get_decoration(var.self, DecorationBinding); + // Avoid emitting sentinel bindings. + if (var_binding < 0x80000000u) + return var_binding; + } + } + + // If we did not explicitly remap, allocate bindings on demand. + // We cannot reliably use Binding decorations since SPIR-V and MSL's binding models are very different. + + bool allocate_argument_buffer_ids = false; + + if (var.storage != StorageClassPushConstant) + allocate_argument_buffer_ids = descriptor_set_is_argument_buffer(var_desc_set); + + uint32_t binding_stride = 1; + auto &type = get(var.basetype); + for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) + binding_stride *= to_array_size_literal(type, i); + + assert(binding_stride != 0); + + // If a binding has not been specified, revert to incrementing resource indices. + uint32_t resource_index; + + if (type_is_msl_framebuffer_fetch(type)) + { + // Frame-buffer fetch gets its fallback resource index from the input attachment index, + // which is then treated as color index. + resource_index = get_decoration(var.self, DecorationInputAttachmentIndex); + } + else if (allocate_argument_buffer_ids) + { + // Allocate from a flat ID binding space. + resource_index = next_metal_resource_ids[var_desc_set]; + next_metal_resource_ids[var_desc_set] += binding_stride; + } + else + { + // Allocate from plain bindings which are allocated per resource type. + switch (basetype) + { + case SPIRType::Image: + resource_index = next_metal_resource_index_texture; + next_metal_resource_index_texture += binding_stride; + break; + case SPIRType::Sampler: + resource_index = next_metal_resource_index_sampler; + next_metal_resource_index_sampler += binding_stride; + break; + default: + resource_index = next_metal_resource_index_buffer; + next_metal_resource_index_buffer += binding_stride; + break; + } + } + + set_extended_decoration(var.self, resource_decoration, resource_index); + return resource_index; +} + +bool CompilerMSL::type_is_msl_framebuffer_fetch(const SPIRType &type) const +{ + return type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && + msl_options.use_framebuffer_fetch_subpasses; +} + +string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) +{ + auto &var = get(arg.id); + auto &type = get_variable_data_type(var); + auto &var_type = get(arg.type); + StorageClass storage = var_type.storage; + bool is_pointer = var_type.pointer; + + // If we need to modify the name of the variable, make sure we use the original variable. + // Our alias is just a shadow variable. + uint32_t name_id = var.self; + if (arg.alias_global_variable && var.basevariable) + name_id = var.basevariable; + + bool constref = !arg.alias_global_variable && is_pointer && arg.write_count == 0; + // Framebuffer fetch is plain value, const looks out of place, but it is not wrong. + if (type_is_msl_framebuffer_fetch(type)) + constref = false; + + bool type_is_image = type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage || + type.basetype == SPIRType::Sampler; + + // Arrays of images/samplers in MSL are always const. + if (!type.array.empty() && type_is_image) + constref = true; + + string decl; + if (constref) + decl += "const "; + + // If this is a combined image-sampler for a 2D image with floating-point type, + // we emitted the 'spvDynamicImageSampler' type, and this is *not* an alias parameter + // for a global, then we need to emit a "dynamic" combined image-sampler. + // Unfortunately, this is necessary to properly support passing around + // combined image-samplers with Y'CbCr conversions on them. + bool is_dynamic_img_sampler = !arg.alias_global_variable && type.basetype == SPIRType::SampledImage && + type.image.dim == Dim2D && type_is_floating_point(get(type.image.type)) && + spv_function_implementations.count(SPVFuncImplDynamicImageSampler); + + // Allow Metal to use the array template to make arrays a value type + string address_space = get_argument_address_space(var); + bool builtin = is_builtin_variable(var); + is_using_builtin_array = builtin; + if (address_space == "threadgroup") + is_using_builtin_array = true; + + if (var.basevariable && (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id)) + decl += type_to_glsl(type, arg.id); + else if (builtin) + decl += builtin_type_decl(static_cast(get_decoration(arg.id, DecorationBuiltIn)), arg.id); + else if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && is_array(type)) + { + is_using_builtin_array = true; + decl += join(type_to_glsl(type, arg.id), "*"); + } + else if (is_dynamic_img_sampler) + { + decl += join("spvDynamicImageSampler<", type_to_glsl(get(type.image.type)), ">"); + // Mark the variable so that we can handle passing it to another function. + set_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler); + } + else + decl += type_to_glsl(type, arg.id); + + bool opaque_handle = storage == StorageClassUniformConstant; + + if (!builtin && !opaque_handle && !is_pointer && + (storage == StorageClassFunction || storage == StorageClassGeneric)) + { + // If the argument is a pure value and not an opaque type, we will pass by value. + if (msl_options.force_native_arrays && is_array(type)) + { + // We are receiving an array by value. This is problematic. + // We cannot be sure of the target address space since we are supposed to receive a copy, + // but this is not possible with MSL without some extra work. + // We will have to assume we're getting a reference in thread address space. + // If we happen to get a reference in constant address space, the caller must emit a copy and pass that. + // Thread const therefore becomes the only logical choice, since we cannot "create" a constant array from + // non-constant arrays, but we can create thread const from constant. + decl = string("thread const ") + decl; + decl += " (&"; + const char *restrict_kw = to_restrict(name_id); + if (*restrict_kw) + { + decl += " "; + decl += restrict_kw; + } + decl += to_expression(name_id); + decl += ")"; + decl += type_to_array_glsl(type); + } + else + { + if (!address_space.empty()) + decl = join(address_space, " ", decl); + decl += " "; + decl += to_expression(name_id); + } + } + else if (is_array(type) && !type_is_image) + { + // Arrays of images and samplers are special cased. + if (!address_space.empty()) + decl = join(address_space, " ", decl); + + if (msl_options.argument_buffers) + { + uint32_t desc_set = get_decoration(name_id, DecorationDescriptorSet); + if ((storage == StorageClassUniform || storage == StorageClassStorageBuffer) && + descriptor_set_is_argument_buffer(desc_set)) + { + // An awkward case where we need to emit *more* address space declarations (yay!). + // An example is where we pass down an array of buffer pointers to leaf functions. + // It's a constant array containing pointers to constants. + // The pointer array is always constant however. E.g. + // device SSBO * constant (&array)[N]. + // const device SSBO * constant (&array)[N]. + // constant SSBO * constant (&array)[N]. + // However, this only matters for argument buffers, since for MSL 1.0 style codegen, + // we emit the buffer array on stack instead, and that seems to work just fine apparently. + + // If the argument was marked as being in device address space, any pointer to member would + // be const device, not constant. + if (argument_buffer_device_storage_mask & (1u << desc_set)) + decl += " const device"; + else + decl += " constant"; + } + } + + decl += " (&"; + const char *restrict_kw = to_restrict(name_id); + if (*restrict_kw) + { + decl += " "; + decl += restrict_kw; + } + decl += to_expression(name_id); + decl += ")"; + decl += type_to_array_glsl(type); + } + else if (!opaque_handle && (!pull_model_inputs.count(var.basevariable) || type.basetype == SPIRType::Struct)) + { + // If this is going to be a reference to a variable pointer, the address space + // for the reference has to go before the '&', but after the '*'. + if (!address_space.empty()) + { + if (decl.back() == '*') + decl += join(" ", address_space, " "); + else + decl = join(address_space, " ", decl); + } + decl += "&"; + decl += " "; + decl += to_restrict(name_id); + decl += to_expression(name_id); + } + else + { + if (!address_space.empty()) + decl = join(address_space, " ", decl); + decl += " "; + decl += to_expression(name_id); + } + + // Emulate texture2D atomic operations + auto *backing_var = maybe_get_backing_variable(name_id); + if (backing_var && atomic_image_vars.count(backing_var->self)) + { + decl += ", device atomic_" + type_to_glsl(get(var_type.image.type), 0); + decl += "* " + to_expression(name_id) + "_atomic"; + } + + is_using_builtin_array = false; + + return decl; +} + +// If we're currently in the entry point function, and the object +// has a qualified name, use it, otherwise use the standard name. +string CompilerMSL::to_name(uint32_t id, bool allow_alias) const +{ + if (current_function && (current_function->self == ir.default_entry_point)) + { + auto *m = ir.find_meta(id); + if (m && !m->decoration.qualified_alias.empty()) + return m->decoration.qualified_alias; + } + return Compiler::to_name(id, allow_alias); +} + +// Returns a name that combines the name of the struct with the name of the member, except for Builtins +string CompilerMSL::to_qualified_member_name(const SPIRType &type, uint32_t index) +{ + // Don't qualify Builtin names because they are unique and are treated as such when building expressions + BuiltIn builtin = BuiltInMax; + if (is_member_builtin(type, index, &builtin)) + return builtin_to_glsl(builtin, type.storage); + + // Strip any underscore prefix from member name + string mbr_name = to_member_name(type, index); + size_t startPos = mbr_name.find_first_not_of("_"); + mbr_name = (startPos != string::npos) ? mbr_name.substr(startPos) : ""; + return join(to_name(type.self), "_", mbr_name); +} + +// Ensures that the specified name is permanently usable by prepending a prefix +// if the first chars are _ and a digit, which indicate a transient name. +string CompilerMSL::ensure_valid_name(string name, string pfx) +{ + return (name.size() >= 2 && name[0] == '_' && isdigit(name[1])) ? (pfx + name) : name; +} + +// Replace all names that match MSL keywords or Metal Standard Library functions. +void CompilerMSL::replace_illegal_names() +{ + // FIXME: MSL and GLSL are doing two different things here. + // Agree on convention and remove this override. + static const unordered_set keywords = { + "kernel", + "vertex", + "fragment", + "compute", + "bias", + "level", + "gradient2d", + "gradientcube", + "gradient3d", + "min_lod_clamp", + "assert", + "VARIABLE_TRACEPOINT", + "STATIC_DATA_TRACEPOINT", + "STATIC_DATA_TRACEPOINT_V", + "METAL_ALIGN", + "METAL_ASM", + "METAL_CONST", + "METAL_DEPRECATED", + "METAL_ENABLE_IF", + "METAL_FUNC", + "METAL_INTERNAL", + "METAL_NON_NULL_RETURN", + "METAL_NORETURN", + "METAL_NOTHROW", + "METAL_PURE", + "METAL_UNAVAILABLE", + "METAL_IMPLICIT", + "METAL_EXPLICIT", + "METAL_CONST_ARG", + "METAL_ARG_UNIFORM", + "METAL_ZERO_ARG", + "METAL_VALID_LOD_ARG", + "METAL_VALID_LEVEL_ARG", + "METAL_VALID_STORE_ORDER", + "METAL_VALID_LOAD_ORDER", + "METAL_VALID_COMPARE_EXCHANGE_FAILURE_ORDER", + "METAL_COMPATIBLE_COMPARE_EXCHANGE_ORDERS", + "METAL_VALID_RENDER_TARGET", + "is_function_constant_defined", + "CHAR_BIT", + "SCHAR_MAX", + "SCHAR_MIN", + "UCHAR_MAX", + "CHAR_MAX", + "CHAR_MIN", + "USHRT_MAX", + "SHRT_MAX", + "SHRT_MIN", + "UINT_MAX", + "INT_MAX", + "INT_MIN", + "FLT_DIG", + "FLT_MANT_DIG", + "FLT_MAX_10_EXP", + "FLT_MAX_EXP", + "FLT_MIN_10_EXP", + "FLT_MIN_EXP", + "FLT_RADIX", + "FLT_MAX", + "FLT_MIN", + "FLT_EPSILON", + "FP_ILOGB0", + "FP_ILOGBNAN", + "MAXFLOAT", + "HUGE_VALF", + "INFINITY", + "NAN", + "M_E_F", + "M_LOG2E_F", + "M_LOG10E_F", + "M_LN2_F", + "M_LN10_F", + "M_PI_F", + "M_PI_2_F", + "M_PI_4_F", + "M_1_PI_F", + "M_2_PI_F", + "M_2_SQRTPI_F", + "M_SQRT2_F", + "M_SQRT1_2_F", + "HALF_DIG", + "HALF_MANT_DIG", + "HALF_MAX_10_EXP", + "HALF_MAX_EXP", + "HALF_MIN_10_EXP", + "HALF_MIN_EXP", + "HALF_RADIX", + "HALF_MAX", + "HALF_MIN", + "HALF_EPSILON", + "MAXHALF", + "HUGE_VALH", + "M_E_H", + "M_LOG2E_H", + "M_LOG10E_H", + "M_LN2_H", + "M_LN10_H", + "M_PI_H", + "M_PI_2_H", + "M_PI_4_H", + "M_1_PI_H", + "M_2_PI_H", + "M_2_SQRTPI_H", + "M_SQRT2_H", + "M_SQRT1_2_H", + "DBL_DIG", + "DBL_MANT_DIG", + "DBL_MAX_10_EXP", + "DBL_MAX_EXP", + "DBL_MIN_10_EXP", + "DBL_MIN_EXP", + "DBL_RADIX", + "DBL_MAX", + "DBL_MIN", + "DBL_EPSILON", + "HUGE_VAL", + "M_E", + "M_LOG2E", + "M_LOG10E", + "M_LN2", + "M_LN10", + "M_PI", + "M_PI_2", + "M_PI_4", + "M_1_PI", + "M_2_PI", + "M_2_SQRTPI", + "M_SQRT2", + "M_SQRT1_2", + "quad_broadcast", + }; + + static const unordered_set illegal_func_names = { + "main", + "saturate", + "assert", + "VARIABLE_TRACEPOINT", + "STATIC_DATA_TRACEPOINT", + "STATIC_DATA_TRACEPOINT_V", + "METAL_ALIGN", + "METAL_ASM", + "METAL_CONST", + "METAL_DEPRECATED", + "METAL_ENABLE_IF", + "METAL_FUNC", + "METAL_INTERNAL", + "METAL_NON_NULL_RETURN", + "METAL_NORETURN", + "METAL_NOTHROW", + "METAL_PURE", + "METAL_UNAVAILABLE", + "METAL_IMPLICIT", + "METAL_EXPLICIT", + "METAL_CONST_ARG", + "METAL_ARG_UNIFORM", + "METAL_ZERO_ARG", + "METAL_VALID_LOD_ARG", + "METAL_VALID_LEVEL_ARG", + "METAL_VALID_STORE_ORDER", + "METAL_VALID_LOAD_ORDER", + "METAL_VALID_COMPARE_EXCHANGE_FAILURE_ORDER", + "METAL_COMPATIBLE_COMPARE_EXCHANGE_ORDERS", + "METAL_VALID_RENDER_TARGET", + "is_function_constant_defined", + "CHAR_BIT", + "SCHAR_MAX", + "SCHAR_MIN", + "UCHAR_MAX", + "CHAR_MAX", + "CHAR_MIN", + "USHRT_MAX", + "SHRT_MAX", + "SHRT_MIN", + "UINT_MAX", + "INT_MAX", + "INT_MIN", + "FLT_DIG", + "FLT_MANT_DIG", + "FLT_MAX_10_EXP", + "FLT_MAX_EXP", + "FLT_MIN_10_EXP", + "FLT_MIN_EXP", + "FLT_RADIX", + "FLT_MAX", + "FLT_MIN", + "FLT_EPSILON", + "FP_ILOGB0", + "FP_ILOGBNAN", + "MAXFLOAT", + "HUGE_VALF", + "INFINITY", + "NAN", + "M_E_F", + "M_LOG2E_F", + "M_LOG10E_F", + "M_LN2_F", + "M_LN10_F", + "M_PI_F", + "M_PI_2_F", + "M_PI_4_F", + "M_1_PI_F", + "M_2_PI_F", + "M_2_SQRTPI_F", + "M_SQRT2_F", + "M_SQRT1_2_F", + "HALF_DIG", + "HALF_MANT_DIG", + "HALF_MAX_10_EXP", + "HALF_MAX_EXP", + "HALF_MIN_10_EXP", + "HALF_MIN_EXP", + "HALF_RADIX", + "HALF_MAX", + "HALF_MIN", + "HALF_EPSILON", + "MAXHALF", + "HUGE_VALH", + "M_E_H", + "M_LOG2E_H", + "M_LOG10E_H", + "M_LN2_H", + "M_LN10_H", + "M_PI_H", + "M_PI_2_H", + "M_PI_4_H", + "M_1_PI_H", + "M_2_PI_H", + "M_2_SQRTPI_H", + "M_SQRT2_H", + "M_SQRT1_2_H", + "DBL_DIG", + "DBL_MANT_DIG", + "DBL_MAX_10_EXP", + "DBL_MAX_EXP", + "DBL_MIN_10_EXP", + "DBL_MIN_EXP", + "DBL_RADIX", + "DBL_MAX", + "DBL_MIN", + "DBL_EPSILON", + "HUGE_VAL", + "M_E", + "M_LOG2E", + "M_LOG10E", + "M_LN2", + "M_LN10", + "M_PI", + "M_PI_2", + "M_PI_4", + "M_1_PI", + "M_2_PI", + "M_2_SQRTPI", + "M_SQRT2", + "M_SQRT1_2", + }; + + ir.for_each_typed_id([&](uint32_t self, SPIRVariable &) { + auto *meta = ir.find_meta(self); + if (!meta) + return; + + auto &dec = meta->decoration; + if (keywords.find(dec.alias) != end(keywords)) + dec.alias += "0"; + }); + + ir.for_each_typed_id([&](uint32_t self, SPIRFunction &) { + auto *meta = ir.find_meta(self); + if (!meta) + return; + + auto &dec = meta->decoration; + if (illegal_func_names.find(dec.alias) != end(illegal_func_names)) + dec.alias += "0"; + }); + + ir.for_each_typed_id([&](uint32_t self, SPIRType &) { + auto *meta = ir.find_meta(self); + if (!meta) + return; + + for (auto &mbr_dec : meta->members) + if (keywords.find(mbr_dec.alias) != end(keywords)) + mbr_dec.alias += "0"; + }); + + for (auto &entry : ir.entry_points) + { + // Change both the entry point name and the alias, to keep them synced. + string &ep_name = entry.second.name; + if (illegal_func_names.find(ep_name) != end(illegal_func_names)) + ep_name += "0"; + + // Always write this because entry point might have been renamed earlier. + ir.meta[entry.first].decoration.alias = ep_name; + } + + CompilerGLSL::replace_illegal_names(); +} + +string CompilerMSL::to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain) +{ + if (index < uint32_t(type.member_type_index_redirection.size())) + index = type.member_type_index_redirection[index]; + + auto *var = maybe_get(base); + // If this is a buffer array, we have to dereference the buffer pointers. + // Otherwise, if this is a pointer expression, dereference it. + + bool declared_as_pointer = false; + + if (var) + { + // Only allow -> dereference for block types. This is so we get expressions like + // buffer[i]->first_member.second_member, rather than buffer[i]->first->second. + bool is_block = has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); + + bool is_buffer_variable = + is_block && (var->storage == StorageClassUniform || var->storage == StorageClassStorageBuffer); + declared_as_pointer = is_buffer_variable && is_array(get(var->basetype)); + } + + if (declared_as_pointer || (!ptr_chain && should_dereference(base))) + return join("->", to_member_name(type, index)); + else + return join(".", to_member_name(type, index)); +} + +string CompilerMSL::to_qualifiers_glsl(uint32_t id) +{ + string quals; + + auto &type = expression_type(id); + if (type.storage == StorageClassWorkgroup) + quals += "threadgroup "; + + return quals; +} + +// The optional id parameter indicates the object whose type we are trying +// to find the description for. It is optional. Most type descriptions do not +// depend on a specific object's use of that type. +string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id) +{ + string type_name; + + // Pointer? + if (type.pointer) + { + const char *restrict_kw; + type_name = join(get_type_address_space(type, id), " ", type_to_glsl(get(type.parent_type), id)); + + switch (type.basetype) + { + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::Sampler: + // These are handles. + break; + default: + // Anything else can be a raw pointer. + type_name += "*"; + restrict_kw = to_restrict(id); + if (*restrict_kw) + { + type_name += " "; + type_name += restrict_kw; + } + break; + } + return type_name; + } + + switch (type.basetype) + { + case SPIRType::Struct: + // Need OpName lookup here to get a "sensible" name for a struct. + // Allow Metal to use the array template to make arrays a value type + type_name = to_name(type.self); + break; + + case SPIRType::Image: + case SPIRType::SampledImage: + return image_type_glsl(type, id); + + case SPIRType::Sampler: + return sampler_type(type, id); + + case SPIRType::Void: + return "void"; + + case SPIRType::AtomicCounter: + return "atomic_uint"; + + case SPIRType::ControlPointArray: + return join("patch_control_point<", type_to_glsl(get(type.parent_type), id), ">"); + + case SPIRType::Interpolant: + return join("interpolant<", type_to_glsl(get(type.parent_type), id), ", interpolation::", + has_decoration(type.self, DecorationNoPerspective) ? "no_perspective" : "perspective", ">"); + + // Scalars + case SPIRType::Boolean: + type_name = "bool"; + break; + case SPIRType::Char: + case SPIRType::SByte: + type_name = "char"; + break; + case SPIRType::UByte: + type_name = "uchar"; + break; + case SPIRType::Short: + type_name = "short"; + break; + case SPIRType::UShort: + type_name = "ushort"; + break; + case SPIRType::Int: + type_name = "int"; + break; + case SPIRType::UInt: + type_name = "uint"; + break; + case SPIRType::Int64: + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("64-bit integers are only supported in MSL 2.2 and above."); + type_name = "long"; + break; + case SPIRType::UInt64: + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("64-bit integers are only supported in MSL 2.2 and above."); + type_name = "ulong"; + break; + case SPIRType::Half: + type_name = "half"; + break; + case SPIRType::Float: + type_name = "float"; + break; + case SPIRType::Double: + type_name = "double"; // Currently unsupported + break; + + default: + return "unknown_type"; + } + + // Matrix? + if (type.columns > 1) + type_name += to_string(type.columns) + "x"; + + // Vector or Matrix? + if (type.vecsize > 1) + type_name += to_string(type.vecsize); + + if (type.array.empty() || using_builtin_array()) + { + return type_name; + } + else + { + // Allow Metal to use the array template to make arrays a value type + add_spv_func_and_recompile(SPVFuncImplUnsafeArray); + string res; + string sizes; + + for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) + { + res += "spvUnsafeArray<"; + sizes += ", "; + sizes += to_array_size(type, i); + sizes += ">"; + } + + res += type_name + sizes; + return res; + } +} + +string CompilerMSL::type_to_array_glsl(const SPIRType &type) +{ + // Allow Metal to use the array template to make arrays a value type + switch (type.basetype) + { + case SPIRType::AtomicCounter: + case SPIRType::ControlPointArray: + { + return CompilerGLSL::type_to_array_glsl(type); + } + default: + { + if (using_builtin_array()) + return CompilerGLSL::type_to_array_glsl(type); + else + return ""; + } + } +} + +// Threadgroup arrays can't have a wrapper type +std::string CompilerMSL::variable_decl(const SPIRVariable &variable) +{ + if (variable.storage == StorageClassWorkgroup) + { + is_using_builtin_array = true; + } + std::string expr = CompilerGLSL::variable_decl(variable); + if (variable.storage == StorageClassWorkgroup) + { + is_using_builtin_array = false; + } + return expr; +} + +// GCC workaround of lambdas calling protected funcs +std::string CompilerMSL::variable_decl(const SPIRType &type, const std::string &name, uint32_t id) +{ + return CompilerGLSL::variable_decl(type, name, id); +} + +std::string CompilerMSL::sampler_type(const SPIRType &type, uint32_t id) +{ + auto *var = maybe_get(id); + if (var && var->basevariable) + { + // Check against the base variable, and not a fake ID which might have been generated for this variable. + id = var->basevariable; + } + + if (!type.array.empty()) + { + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of samplers."); + + if (type.array.size() > 1) + SPIRV_CROSS_THROW("Arrays of arrays of samplers are not supported in MSL."); + + // Arrays of samplers in MSL must be declared with a special array syntax ala C++11 std::array. + // If we have a runtime array, it could be a variable-count descriptor set binding. + uint32_t array_size = to_array_size_literal(type); + if (array_size == 0) + array_size = get_resource_array_size(id); + + if (array_size == 0) + SPIRV_CROSS_THROW("Unsized array of samplers is not supported in MSL."); + + auto &parent = get(get_pointee_type(type).parent_type); + return join("array<", sampler_type(parent, id), ", ", array_size, ">"); + } + else + return "sampler"; +} + +// Returns an MSL string describing the SPIR-V image type +string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id) +{ + auto *var = maybe_get(id); + if (var && var->basevariable) + { + // For comparison images, check against the base variable, + // and not the fake ID which might have been generated for this variable. + id = var->basevariable; + } + + if (!type.array.empty()) + { + uint32_t major = 2, minor = 0; + if (msl_options.is_ios()) + { + major = 1; + minor = 2; + } + if (!msl_options.supports_msl_version(major, minor)) + { + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("MSL 1.2 or greater is required for arrays of textures."); + else + SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of textures."); + } + + if (type.array.size() > 1) + SPIRV_CROSS_THROW("Arrays of arrays of textures are not supported in MSL."); + + // Arrays of images in MSL must be declared with a special array syntax ala C++11 std::array. + // If we have a runtime array, it could be a variable-count descriptor set binding. + uint32_t array_size = to_array_size_literal(type); + if (array_size == 0) + array_size = get_resource_array_size(id); + + if (array_size == 0) + SPIRV_CROSS_THROW("Unsized array of images is not supported in MSL."); + + auto &parent = get(get_pointee_type(type).parent_type); + return join("array<", image_type_glsl(parent, id), ", ", array_size, ">"); + } + + string img_type_name; + + // Bypass pointers because we need the real image struct + auto &img_type = get(type.self).image; + if (image_is_comparison(type, id)) + { + switch (img_type.dim) + { + case Dim1D: + case Dim2D: + if (img_type.dim == Dim1D && !msl_options.texture_1D_as_2D) + { + // Use a native Metal 1D texture + img_type_name += "depth1d_unsupported_by_metal"; + break; + } + + if (img_type.ms && img_type.arrayed) + { + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Multisampled array textures are supported from 2.1."); + img_type_name += "depth2d_ms_array"; + } + else if (img_type.ms) + img_type_name += "depth2d_ms"; + else if (img_type.arrayed) + img_type_name += "depth2d_array"; + else + img_type_name += "depth2d"; + break; + case Dim3D: + img_type_name += "depth3d_unsupported_by_metal"; + break; + case DimCube: + if (!msl_options.emulate_cube_array) + img_type_name += (img_type.arrayed ? "depthcube_array" : "depthcube"); + else + img_type_name += (img_type.arrayed ? "depth2d_array" : "depthcube"); + break; + default: + img_type_name += "unknown_depth_texture_type"; + break; + } + } + else + { + switch (img_type.dim) + { + case DimBuffer: + if (img_type.ms || img_type.arrayed) + SPIRV_CROSS_THROW("Cannot use texel buffers with multisampling or array layers."); + + if (msl_options.texture_buffer_native) + { + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Native texture_buffer type is only supported in MSL 2.1."); + img_type_name = "texture_buffer"; + } + else + img_type_name += "texture2d"; + break; + case Dim1D: + case Dim2D: + case DimSubpassData: + { + bool subpass_array = + img_type.dim == DimSubpassData && (msl_options.multiview || msl_options.arrayed_subpass_input); + if (img_type.dim == Dim1D && !msl_options.texture_1D_as_2D) + { + // Use a native Metal 1D texture + img_type_name += (img_type.arrayed ? "texture1d_array" : "texture1d"); + break; + } + + // Use Metal's native frame-buffer fetch API for subpass inputs. + if (type_is_msl_framebuffer_fetch(type)) + { + auto img_type_4 = get(img_type.type); + img_type_4.vecsize = 4; + return type_to_glsl(img_type_4); + } + if (img_type.ms && (img_type.arrayed || subpass_array)) + { + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Multisampled array textures are supported from 2.1."); + img_type_name += "texture2d_ms_array"; + } + else if (img_type.ms) + img_type_name += "texture2d_ms"; + else if (img_type.arrayed || subpass_array) + img_type_name += "texture2d_array"; + else + img_type_name += "texture2d"; + break; + } + case Dim3D: + img_type_name += "texture3d"; + break; + case DimCube: + if (!msl_options.emulate_cube_array) + img_type_name += (img_type.arrayed ? "texturecube_array" : "texturecube"); + else + img_type_name += (img_type.arrayed ? "texture2d_array" : "texturecube"); + break; + default: + img_type_name += "unknown_texture_type"; + break; + } + } + + // Append the pixel type + img_type_name += "<"; + img_type_name += type_to_glsl(get(img_type.type)); + + // For unsampled images, append the sample/read/write access qualifier. + // For kernel images, the access qualifier my be supplied directly by SPIR-V. + // Otherwise it may be set based on whether the image is read from or written to within the shader. + if (type.basetype == SPIRType::Image && type.image.sampled == 2 && type.image.dim != DimSubpassData) + { + switch (img_type.access) + { + case AccessQualifierReadOnly: + img_type_name += ", access::read"; + break; + + case AccessQualifierWriteOnly: + img_type_name += ", access::write"; + break; + + case AccessQualifierReadWrite: + img_type_name += ", access::read_write"; + break; + + default: + { + auto *p_var = maybe_get_backing_variable(id); + if (p_var && p_var->basevariable) + p_var = maybe_get(p_var->basevariable); + if (p_var && !has_decoration(p_var->self, DecorationNonWritable)) + { + img_type_name += ", access::"; + + if (!has_decoration(p_var->self, DecorationNonReadable)) + img_type_name += "read_"; + + img_type_name += "write"; + } + break; + } + } + } + + img_type_name += ">"; + + return img_type_name; +} + +void CompilerMSL::emit_subgroup_op(const Instruction &i) +{ + const uint32_t *ops = stream(i); + auto op = static_cast(i.op); + + // Metal 2.0 is required. iOS only supports quad ops. macOS only supports + // broadcast and shuffle on 10.13 (2.0), with full support in 10.14 (2.1). + // Note that iOS makes no distinction between a quad-group and a subgroup; + // all subgroups are quad-groups there. + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Subgroups are only supported in Metal 2.0 and up."); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + if (msl_options.is_ios()) + { + switch (op) + { + default: + SPIRV_CROSS_THROW("iOS only supports quad-group operations."); + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + case OpGroupNonUniformQuadSwap: + case OpGroupNonUniformQuadBroadcast: + break; + } + } + + if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1)) + { + switch (op) + { + default: + SPIRV_CROSS_THROW("Subgroup ops beyond broadcast and shuffle on macOS require Metal 2.1 and up."); + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + break; + } + } + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto scope = static_cast(evaluate_constant_u32(ops[2])); + if (scope != ScopeSubgroup) + SPIRV_CROSS_THROW("Only subgroup scope is supported."); + + switch (op) + { + case OpGroupNonUniformElect: + emit_op(result_type, id, "simd_is_first()", true); + break; + + case OpGroupNonUniformBroadcast: + emit_binary_func_op(result_type, id, ops[3], ops[4], "spvSubgroupBroadcast"); + break; + + case OpGroupNonUniformBroadcastFirst: + emit_unary_func_op(result_type, id, ops[3], "spvSubgroupBroadcastFirst"); + break; + + case OpGroupNonUniformBallot: + emit_unary_func_op(result_type, id, ops[3], "spvSubgroupBallot"); + break; + + case OpGroupNonUniformInverseBallot: + emit_binary_func_op(result_type, id, ops[3], builtin_subgroup_invocation_id_id, "spvSubgroupBallotBitExtract"); + break; + + case OpGroupNonUniformBallotBitExtract: + emit_binary_func_op(result_type, id, ops[3], ops[4], "spvSubgroupBallotBitExtract"); + break; + + case OpGroupNonUniformBallotFindLSB: + emit_binary_func_op(result_type, id, ops[3], builtin_subgroup_size_id, "spvSubgroupBallotFindLSB"); + break; + + case OpGroupNonUniformBallotFindMSB: + emit_binary_func_op(result_type, id, ops[3], builtin_subgroup_size_id, "spvSubgroupBallotFindMSB"); + break; + + case OpGroupNonUniformBallotBitCount: + { + auto operation = static_cast(ops[3]); + switch (operation) + { + case GroupOperationReduce: + emit_binary_func_op(result_type, id, ops[4], builtin_subgroup_size_id, "spvSubgroupBallotBitCount"); + break; + case GroupOperationInclusiveScan: + emit_binary_func_op(result_type, id, ops[4], builtin_subgroup_invocation_id_id, + "spvSubgroupBallotInclusiveBitCount"); + break; + case GroupOperationExclusiveScan: + emit_binary_func_op(result_type, id, ops[4], builtin_subgroup_invocation_id_id, + "spvSubgroupBallotExclusiveBitCount"); + break; + default: + SPIRV_CROSS_THROW("Invalid BitCount operation."); + break; + } + break; + } + + case OpGroupNonUniformShuffle: + emit_binary_func_op(result_type, id, ops[3], ops[4], "spvSubgroupShuffle"); + break; + + case OpGroupNonUniformShuffleXor: + emit_binary_func_op(result_type, id, ops[3], ops[4], "spvSubgroupShuffleXor"); + break; + + case OpGroupNonUniformShuffleUp: + emit_binary_func_op(result_type, id, ops[3], ops[4], "spvSubgroupShuffleUp"); + break; + + case OpGroupNonUniformShuffleDown: + emit_binary_func_op(result_type, id, ops[3], ops[4], "spvSubgroupShuffleDown"); + break; + + case OpGroupNonUniformAll: + emit_unary_func_op(result_type, id, ops[3], "simd_all"); + break; + + case OpGroupNonUniformAny: + emit_unary_func_op(result_type, id, ops[3], "simd_any"); + break; + + case OpGroupNonUniformAllEqual: + emit_unary_func_op(result_type, id, ops[3], "spvSubgroupAllEqual"); + break; + + // clang-format off +#define MSL_GROUP_OP(op, msl_op) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op(result_type, id, ops[4], "simd_" #msl_op); \ + else if (operation == GroupOperationInclusiveScan) \ + emit_unary_func_op(result_type, id, ops[4], "simd_prefix_inclusive_" #msl_op); \ + else if (operation == GroupOperationExclusiveScan) \ + emit_unary_func_op(result_type, id, ops[4], "simd_prefix_exclusive_" #msl_op); \ + else if (operation == GroupOperationClusteredReduce) \ + { \ + /* Only cluster sizes of 4 are supported. */ \ + uint32_t cluster_size = evaluate_constant_u32(ops[5]); \ + if (cluster_size != 4) \ + SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \ + emit_unary_func_op(result_type, id, ops[4], "quad_" #msl_op); \ + } \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + MSL_GROUP_OP(FAdd, sum) + MSL_GROUP_OP(FMul, product) + MSL_GROUP_OP(IAdd, sum) + MSL_GROUP_OP(IMul, product) +#undef MSL_GROUP_OP + // The others, unfortunately, don't support InclusiveScan or ExclusiveScan. + +#define MSL_GROUP_OP(op, msl_op) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op(result_type, id, ops[4], "simd_" #msl_op); \ + else if (operation == GroupOperationInclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support InclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationExclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support ExclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationClusteredReduce) \ + { \ + /* Only cluster sizes of 4 are supported. */ \ + uint32_t cluster_size = evaluate_constant_u32(ops[5]); \ + if (cluster_size != 4) \ + SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \ + emit_unary_func_op(result_type, id, ops[4], "quad_" #msl_op); \ + } \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + +#define MSL_GROUP_OP_CAST(op, msl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[4], "simd_" #msl_op, type, type); \ + else if (operation == GroupOperationInclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support InclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationExclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support ExclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationClusteredReduce) \ + { \ + /* Only cluster sizes of 4 are supported. */ \ + uint32_t cluster_size = evaluate_constant_u32(ops[5]); \ + if (cluster_size != 4) \ + SPIRV_CROSS_THROW("Metal only supports quad ClusteredReduce."); \ + emit_unary_func_op_cast(result_type, id, ops[4], "quad_" #msl_op, type, type); \ + } \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + + MSL_GROUP_OP(FMin, min) + MSL_GROUP_OP(FMax, max) + MSL_GROUP_OP_CAST(SMin, min, int_type) + MSL_GROUP_OP_CAST(SMax, max, int_type) + MSL_GROUP_OP_CAST(UMin, min, uint_type) + MSL_GROUP_OP_CAST(UMax, max, uint_type) + MSL_GROUP_OP(BitwiseAnd, and) + MSL_GROUP_OP(BitwiseOr, or) + MSL_GROUP_OP(BitwiseXor, xor) + MSL_GROUP_OP(LogicalAnd, and) + MSL_GROUP_OP(LogicalOr, or) + MSL_GROUP_OP(LogicalXor, xor) + // clang-format on +#undef MSL_GROUP_OP +#undef MSL_GROUP_OP_CAST + + case OpGroupNonUniformQuadSwap: + emit_binary_func_op(result_type, id, ops[3], ops[4], "spvQuadSwap"); + break; + + case OpGroupNonUniformQuadBroadcast: + emit_binary_func_op(result_type, id, ops[3], ops[4], "spvQuadBroadcast"); + break; + + default: + SPIRV_CROSS_THROW("Invalid opcode for subgroup."); + } + + register_control_dependent_expression(id); +} + +string CompilerMSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) +{ + if (out_type.basetype == in_type.basetype) + return ""; + + assert(out_type.basetype != SPIRType::Boolean); + assert(in_type.basetype != SPIRType::Boolean); + + bool integral_cast = type_is_integral(out_type) && type_is_integral(in_type); + bool same_size_cast = out_type.width == in_type.width; + + if (integral_cast && same_size_cast) + { + // Trivial bitcast case, casts between integers. + return type_to_glsl(out_type); + } + else + { + // Fall back to the catch-all bitcast in MSL. + return "as_type<" + type_to_glsl(out_type) + ">"; + } +} + +bool CompilerMSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) +{ + return false; +} + +// Returns an MSL string identifying the name of a SPIR-V builtin. +// Output builtins are qualified with the name of the stage out structure. +string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) +{ + switch (builtin) + { + + // Handle HLSL-style 0-based vertex/instance index. + // Override GLSL compiler strictness + case BuiltInVertexId: + ensure_builtin(StorageClassInput, BuiltInVertexId); + if (msl_options.enable_base_index_zero && msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + if (builtin_declaration) + { + if (needs_base_vertex_arg != TriState::No) + needs_base_vertex_arg = TriState::Yes; + return "gl_VertexID"; + } + else + { + ensure_builtin(StorageClassInput, BuiltInBaseVertex); + return "(gl_VertexID - gl_BaseVertex)"; + } + } + else + { + return "gl_VertexID"; + } + case BuiltInInstanceId: + ensure_builtin(StorageClassInput, BuiltInInstanceId); + if (msl_options.enable_base_index_zero && msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + if (builtin_declaration) + { + if (needs_base_instance_arg != TriState::No) + needs_base_instance_arg = TriState::Yes; + return "gl_InstanceID"; + } + else + { + ensure_builtin(StorageClassInput, BuiltInBaseInstance); + return "(gl_InstanceID - gl_BaseInstance)"; + } + } + else + { + return "gl_InstanceID"; + } + case BuiltInVertexIndex: + ensure_builtin(StorageClassInput, BuiltInVertexIndex); + if (msl_options.enable_base_index_zero && msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + if (builtin_declaration) + { + if (needs_base_vertex_arg != TriState::No) + needs_base_vertex_arg = TriState::Yes; + return "gl_VertexIndex"; + } + else + { + ensure_builtin(StorageClassInput, BuiltInBaseVertex); + return "(gl_VertexIndex - gl_BaseVertex)"; + } + } + else + { + return "gl_VertexIndex"; + } + case BuiltInInstanceIndex: + ensure_builtin(StorageClassInput, BuiltInInstanceIndex); + if (msl_options.enable_base_index_zero && msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + if (builtin_declaration) + { + if (needs_base_instance_arg != TriState::No) + needs_base_instance_arg = TriState::Yes; + return "gl_InstanceIndex"; + } + else + { + ensure_builtin(StorageClassInput, BuiltInBaseInstance); + return "(gl_InstanceIndex - gl_BaseInstance)"; + } + } + else + { + return "gl_InstanceIndex"; + } + case BuiltInBaseVertex: + if (msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + needs_base_vertex_arg = TriState::No; + return "gl_BaseVertex"; + } + else + { + SPIRV_CROSS_THROW("BaseVertex requires Metal 1.1 and Mac or Apple A9+ hardware."); + } + case BuiltInBaseInstance: + if (msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + needs_base_instance_arg = TriState::No; + return "gl_BaseInstance"; + } + else + { + SPIRV_CROSS_THROW("BaseInstance requires Metal 1.1 and Mac or Apple A9+ hardware."); + } + case BuiltInDrawIndex: + SPIRV_CROSS_THROW("DrawIndex is not supported in MSL."); + + // When used in the entry function, output builtins are qualified with output struct name. + // Test storage class as NOT Input, as output builtins might be part of generic type. + // Also don't do this for tessellation control shaders. + case BuiltInViewportIndex: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); + /* fallthrough */ + case BuiltInFragDepth: + case BuiltInFragStencilRefEXT: + if ((builtin == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) || + (builtin == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin)) + break; + /* fallthrough */ + case BuiltInPosition: + case BuiltInPointSize: + case BuiltInClipDistance: + case BuiltInCullDistance: + case BuiltInLayer: + case BuiltInSampleMask: + if (get_execution_model() == ExecutionModelTessellationControl) + break; + if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point)) + return stage_out_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin, storage); + + break; + + case BuiltInBaryCoordNV: + case BuiltInBaryCoordNoPerspNV: + if (storage == StorageClassInput && current_function && (current_function->self == ir.default_entry_point)) + return stage_in_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin, storage); + break; + + case BuiltInTessLevelOuter: + if (get_execution_model() == ExecutionModelTessellationEvaluation) + { + if (storage != StorageClassOutput && !get_entry_point().flags.get(ExecutionModeTriangles) && + current_function && (current_function->self == ir.default_entry_point)) + return join(patch_stage_in_var_name, ".", CompilerGLSL::builtin_to_glsl(builtin, storage)); + else + break; + } + if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point)) + return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), + "].edgeTessellationFactor"); + break; + + case BuiltInTessLevelInner: + if (get_execution_model() == ExecutionModelTessellationEvaluation) + { + if (storage != StorageClassOutput && !get_entry_point().flags.get(ExecutionModeTriangles) && + current_function && (current_function->self == ir.default_entry_point)) + return join(patch_stage_in_var_name, ".", CompilerGLSL::builtin_to_glsl(builtin, storage)); + else + break; + } + if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point)) + return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), + "].insideTessellationFactor"); + break; + + default: + break; + } + + return CompilerGLSL::builtin_to_glsl(builtin, storage); +} + +// Returns an MSL string attribute qualifer for a SPIR-V builtin +string CompilerMSL::builtin_qualifier(BuiltIn builtin) +{ + auto &execution = get_entry_point(); + + switch (builtin) + { + // Vertex function in + case BuiltInVertexId: + return "vertex_id"; + case BuiltInVertexIndex: + return "vertex_id"; + case BuiltInBaseVertex: + return "base_vertex"; + case BuiltInInstanceId: + return "instance_id"; + case BuiltInInstanceIndex: + return "instance_id"; + case BuiltInBaseInstance: + return "base_instance"; + case BuiltInDrawIndex: + SPIRV_CROSS_THROW("DrawIndex is not supported in MSL."); + + // Vertex function out + case BuiltInClipDistance: + return "clip_distance"; + case BuiltInPointSize: + return "point_size"; + case BuiltInPosition: + if (position_invariant) + { + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Invariant position is only supported on MSL 2.1 and up."); + return "position, invariant"; + } + else + return "position"; + case BuiltInLayer: + return "render_target_array_index"; + case BuiltInViewportIndex: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); + return "viewport_array_index"; + + // Tess. control function in + case BuiltInInvocationId: + if (msl_options.multi_patch_workgroup) + { + // Shouldn't be reached. + SPIRV_CROSS_THROW("InvocationId is computed manually with multi-patch workgroups in MSL."); + } + return "thread_index_in_threadgroup"; + case BuiltInPatchVertices: + // Shouldn't be reached. + SPIRV_CROSS_THROW("PatchVertices is derived from the auxiliary buffer in MSL."); + case BuiltInPrimitiveId: + switch (execution.model) + { + case ExecutionModelTessellationControl: + if (msl_options.multi_patch_workgroup) + { + // Shouldn't be reached. + SPIRV_CROSS_THROW("PrimitiveId is computed manually with multi-patch workgroups in MSL."); + } + return "threadgroup_position_in_grid"; + case ExecutionModelTessellationEvaluation: + return "patch_id"; + case ExecutionModelFragment: + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("PrimitiveId is not supported in fragment on iOS."); + else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("PrimitiveId on macOS requires MSL 2.2."); + return "primitive_id"; + default: + SPIRV_CROSS_THROW("PrimitiveId is not supported in this execution model."); + } + + // Tess. control function out + case BuiltInTessLevelOuter: + case BuiltInTessLevelInner: + // Shouldn't be reached. + SPIRV_CROSS_THROW("Tessellation levels are handled specially in MSL."); + + // Tess. evaluation function in + case BuiltInTessCoord: + return "position_in_patch"; + + // Fragment function in + case BuiltInFrontFacing: + return "front_facing"; + case BuiltInPointCoord: + return "point_coord"; + case BuiltInFragCoord: + return "position"; + case BuiltInSampleId: + return "sample_id"; + case BuiltInSampleMask: + return "sample_mask"; + case BuiltInSamplePosition: + // Shouldn't be reached. + SPIRV_CROSS_THROW("Sample position is retrieved by a function in MSL."); + case BuiltInViewIndex: + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("ViewIndex is handled specially outside fragment shaders."); + // The ViewIndex was implicitly used in the prior stages to set the render_target_array_index, + // so we can get it from there. + return "render_target_array_index"; + + // Fragment function out + case BuiltInFragDepth: + if (execution.flags.get(ExecutionModeDepthGreater)) + return "depth(greater)"; + else if (execution.flags.get(ExecutionModeDepthLess)) + return "depth(less)"; + else + return "depth(any)"; + + case BuiltInFragStencilRefEXT: + return "stencil"; + + // Compute function in + case BuiltInGlobalInvocationId: + return "thread_position_in_grid"; + + case BuiltInWorkgroupId: + return "threadgroup_position_in_grid"; + + case BuiltInNumWorkgroups: + return "threadgroups_per_grid"; + + case BuiltInLocalInvocationId: + return "thread_position_in_threadgroup"; + + case BuiltInLocalInvocationIndex: + return "thread_index_in_threadgroup"; + + case BuiltInSubgroupSize: + if (execution.model == ExecutionModelFragment) + { + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("threads_per_simdgroup requires Metal 2.2 in fragment shaders."); + return "threads_per_simdgroup"; + } + else + { + // thread_execution_width is an alias for threads_per_simdgroup, and it's only available since 1.0, + // but not in fragment. + return "thread_execution_width"; + } + + case BuiltInNumSubgroups: + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Subgroup builtins require Metal 2.0."); + return msl_options.is_ios() ? "quadgroups_per_threadgroup" : "simdgroups_per_threadgroup"; + + case BuiltInSubgroupId: + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Subgroup builtins require Metal 2.0."); + return msl_options.is_ios() ? "quadgroup_index_in_threadgroup" : "simdgroup_index_in_threadgroup"; + + case BuiltInSubgroupLocalInvocationId: + if (execution.model == ExecutionModelFragment) + { + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("thread_index_in_simdgroup requires Metal 2.2 in fragment shaders."); + return "thread_index_in_simdgroup"; + } + else + { + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Subgroup builtins require Metal 2.0."); + return msl_options.is_ios() ? "thread_index_in_quadgroup" : "thread_index_in_simdgroup"; + } + + case BuiltInSubgroupEqMask: + case BuiltInSubgroupGeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupLtMask: + // Shouldn't be reached. + SPIRV_CROSS_THROW("Subgroup ballot masks are handled specially in MSL."); + + case BuiltInBaryCoordNV: + // TODO: AMD barycentrics as well? Seem to have different swizzle and 2 components rather than 3. + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Barycentrics not supported on iOS."); + else if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Barycentrics are only supported in MSL 2.2 and above on macOS."); + return "barycentric_coord, center_perspective"; + + case BuiltInBaryCoordNoPerspNV: + // TODO: AMD barycentrics as well? Seem to have different swizzle and 2 components rather than 3. + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("Barycentrics not supported on iOS."); + else if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Barycentrics are only supported in MSL 2.2 and above on macOS."); + return "barycentric_coord, center_no_perspective"; + + default: + return "unsupported-built-in"; + } +} + +// Returns an MSL string type declaration for a SPIR-V builtin +string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id) +{ + const SPIREntryPoint &execution = get_entry_point(); + switch (builtin) + { + // Vertex function in + case BuiltInVertexId: + return "uint"; + case BuiltInVertexIndex: + return "uint"; + case BuiltInBaseVertex: + return "uint"; + case BuiltInInstanceId: + return "uint"; + case BuiltInInstanceIndex: + return "uint"; + case BuiltInBaseInstance: + return "uint"; + case BuiltInDrawIndex: + SPIRV_CROSS_THROW("DrawIndex is not supported in MSL."); + + // Vertex function out + case BuiltInClipDistance: + return "float"; + case BuiltInPointSize: + return "float"; + case BuiltInPosition: + return "float4"; + case BuiltInLayer: + return "uint"; + case BuiltInViewportIndex: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); + return "uint"; + + // Tess. control function in + case BuiltInInvocationId: + return "uint"; + case BuiltInPatchVertices: + return "uint"; + case BuiltInPrimitiveId: + return "uint"; + + // Tess. control function out + case BuiltInTessLevelInner: + if (execution.model == ExecutionModelTessellationEvaluation) + return !execution.flags.get(ExecutionModeTriangles) ? "float2" : "float"; + return "half"; + case BuiltInTessLevelOuter: + if (execution.model == ExecutionModelTessellationEvaluation) + return !execution.flags.get(ExecutionModeTriangles) ? "float4" : "float"; + return "half"; + + // Tess. evaluation function in + case BuiltInTessCoord: + return execution.flags.get(ExecutionModeTriangles) ? "float3" : "float2"; + + // Fragment function in + case BuiltInFrontFacing: + return "bool"; + case BuiltInPointCoord: + return "float2"; + case BuiltInFragCoord: + return "float4"; + case BuiltInSampleId: + return "uint"; + case BuiltInSampleMask: + return "uint"; + case BuiltInSamplePosition: + return "float2"; + case BuiltInViewIndex: + return "uint"; + + case BuiltInHelperInvocation: + return "bool"; + + case BuiltInBaryCoordNV: + case BuiltInBaryCoordNoPerspNV: + // Use the type as declared, can be 1, 2 or 3 components. + return type_to_glsl(get_variable_data_type(get(id))); + + // Fragment function out + case BuiltInFragDepth: + return "float"; + + case BuiltInFragStencilRefEXT: + return "uint"; + + // Compute function in + case BuiltInGlobalInvocationId: + case BuiltInLocalInvocationId: + case BuiltInNumWorkgroups: + case BuiltInWorkgroupId: + return "uint3"; + case BuiltInLocalInvocationIndex: + case BuiltInNumSubgroups: + case BuiltInSubgroupId: + case BuiltInSubgroupSize: + case BuiltInSubgroupLocalInvocationId: + return "uint"; + case BuiltInSubgroupEqMask: + case BuiltInSubgroupGeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupLtMask: + return "uint4"; + + case BuiltInDeviceIndex: + return "int"; + + default: + return "unsupported-built-in-type"; + } +} + +// Returns the declaration of a built-in argument to a function +string CompilerMSL::built_in_func_arg(BuiltIn builtin, bool prefix_comma) +{ + string bi_arg; + if (prefix_comma) + bi_arg += ", "; + + // Handle HLSL-style 0-based vertex/instance index. + builtin_declaration = true; + bi_arg += builtin_type_decl(builtin); + bi_arg += " " + builtin_to_glsl(builtin, StorageClassInput); + bi_arg += " [[" + builtin_qualifier(builtin) + "]]"; + builtin_declaration = false; + + return bi_arg; +} + +const SPIRType &CompilerMSL::get_physical_member_type(const SPIRType &type, uint32_t index) const +{ + if (member_is_remapped_physical_type(type, index)) + return get(get_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypeID)); + else + return get(type.member_types[index]); +} + +SPIRType CompilerMSL::get_presumed_input_type(const SPIRType &ib_type, uint32_t index) const +{ + SPIRType type = get_physical_member_type(ib_type, index); + uint32_t loc = get_member_decoration(ib_type.self, index, DecorationLocation); + if (inputs_by_location.count(loc)) + { + if (inputs_by_location.at(loc).vecsize > type.vecsize) + type.vecsize = inputs_by_location.at(loc).vecsize; + } + return type; +} + +uint32_t CompilerMSL::get_declared_type_array_stride_msl(const SPIRType &type, bool is_packed, bool row_major) const +{ + // Array stride in MSL is always size * array_size. sizeof(float3) == 16, + // unlike GLSL and HLSL where array stride would be 16 and size 12. + + // We could use parent type here and recurse, but that makes creating physical type remappings + // far more complicated. We'd rather just create the final type, and ignore having to create the entire type + // hierarchy in order to compute this value, so make a temporary type on the stack. + + auto basic_type = type; + basic_type.array.clear(); + basic_type.array_size_literal.clear(); + uint32_t value_size = get_declared_type_size_msl(basic_type, is_packed, row_major); + + uint32_t dimensions = uint32_t(type.array.size()); + assert(dimensions > 0); + dimensions--; + + // Multiply together every dimension, except the last one. + for (uint32_t dim = 0; dim < dimensions; dim++) + { + uint32_t array_size = to_array_size_literal(type, dim); + value_size *= max(array_size, 1u); + } + + return value_size; +} + +uint32_t CompilerMSL::get_declared_struct_member_array_stride_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_array_stride_msl(get_physical_member_type(type, index), + member_is_packed_physical_type(type, index), + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_input_array_stride_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_array_stride_msl(get_presumed_input_type(type, index), false, + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_type_matrix_stride_msl(const SPIRType &type, bool packed, bool row_major) const +{ + // For packed matrices, we just use the size of the vector type. + // Otherwise, MatrixStride == alignment, which is the size of the underlying vector type. + if (packed) + return (type.width / 8) * ((row_major && type.columns > 1) ? type.columns : type.vecsize); + else + return get_declared_type_alignment_msl(type, false, row_major); +} + +uint32_t CompilerMSL::get_declared_struct_member_matrix_stride_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_matrix_stride_msl(get_physical_member_type(type, index), + member_is_packed_physical_type(type, index), + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_input_matrix_stride_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_matrix_stride_msl(get_presumed_input_type(type, index), false, + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_struct_size_msl(const SPIRType &struct_type, bool ignore_alignment, + bool ignore_padding) const +{ + // If we have a target size, that is the declared size as well. + if (!ignore_padding && has_extended_decoration(struct_type.self, SPIRVCrossDecorationPaddingTarget)) + return get_extended_decoration(struct_type.self, SPIRVCrossDecorationPaddingTarget); + + if (struct_type.member_types.empty()) + return 0; + + uint32_t mbr_cnt = uint32_t(struct_type.member_types.size()); + + // In MSL, a struct's alignment is equal to the maximum alignment of any of its members. + uint32_t alignment = 1; + + if (!ignore_alignment) + { + for (uint32_t i = 0; i < mbr_cnt; i++) + { + uint32_t mbr_alignment = get_declared_struct_member_alignment_msl(struct_type, i); + alignment = max(alignment, mbr_alignment); + } + } + + // Last member will always be matched to the final Offset decoration, but size of struct in MSL now depends + // on physical size in MSL, and the size of the struct itself is then aligned to struct alignment. + uint32_t spirv_offset = type_struct_member_offset(struct_type, mbr_cnt - 1); + uint32_t msl_size = spirv_offset + get_declared_struct_member_size_msl(struct_type, mbr_cnt - 1); + msl_size = (msl_size + alignment - 1) & ~(alignment - 1); + return msl_size; +} + +// Returns the byte size of a struct member. +uint32_t CompilerMSL::get_declared_type_size_msl(const SPIRType &type, bool is_packed, bool row_major) const +{ + switch (type.basetype) + { + case SPIRType::Unknown: + case SPIRType::Void: + case SPIRType::AtomicCounter: + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::Sampler: + SPIRV_CROSS_THROW("Querying size of opaque object."); + + default: + { + if (!type.array.empty()) + { + uint32_t array_size = to_array_size_literal(type); + return get_declared_type_array_stride_msl(type, is_packed, row_major) * max(array_size, 1u); + } + + if (type.basetype == SPIRType::Struct) + return get_declared_struct_size_msl(type); + + if (is_packed) + { + return type.vecsize * type.columns * (type.width / 8); + } + else + { + // An unpacked 3-element vector or matrix column is the same memory size as a 4-element. + uint32_t vecsize = type.vecsize; + uint32_t columns = type.columns; + + if (row_major && columns > 1) + swap(vecsize, columns); + + if (vecsize == 3) + vecsize = 4; + + return vecsize * columns * (type.width / 8); + } + } + } +} + +uint32_t CompilerMSL::get_declared_struct_member_size_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_size_msl(get_physical_member_type(type, index), + member_is_packed_physical_type(type, index), + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_input_size_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_size_msl(get_presumed_input_type(type, index), false, + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +// Returns the byte alignment of a type. +uint32_t CompilerMSL::get_declared_type_alignment_msl(const SPIRType &type, bool is_packed, bool row_major) const +{ + switch (type.basetype) + { + case SPIRType::Unknown: + case SPIRType::Void: + case SPIRType::AtomicCounter: + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::Sampler: + SPIRV_CROSS_THROW("Querying alignment of opaque object."); + + case SPIRType::Int64: + SPIRV_CROSS_THROW("long types are not supported in buffers in MSL."); + case SPIRType::UInt64: + SPIRV_CROSS_THROW("ulong types are not supported in buffers in MSL."); + case SPIRType::Double: + SPIRV_CROSS_THROW("double types are not supported in buffers in MSL."); + + case SPIRType::Struct: + { + // In MSL, a struct's alignment is equal to the maximum alignment of any of its members. + uint32_t alignment = 1; + for (uint32_t i = 0; i < type.member_types.size(); i++) + alignment = max(alignment, uint32_t(get_declared_struct_member_alignment_msl(type, i))); + return alignment; + } + + default: + { + // Alignment of packed type is the same as the underlying component or column size. + // Alignment of unpacked type is the same as the vector size. + // Alignment of 3-elements vector is the same as 4-elements (including packed using column). + if (is_packed) + { + // If we have packed_T and friends, the alignment is always scalar. + return type.width / 8; + } + else + { + // This is the general rule for MSL. Size == alignment. + uint32_t vecsize = (row_major && type.columns > 1) ? type.columns : type.vecsize; + return (type.width / 8) * (vecsize == 3 ? 4 : vecsize); + } + } + } +} + +uint32_t CompilerMSL::get_declared_struct_member_alignment_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_alignment_msl(get_physical_member_type(type, index), + member_is_packed_physical_type(type, index), + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_input_alignment_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_alignment_msl(get_presumed_input_type(type, index), false, + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +bool CompilerMSL::skip_argument(uint32_t) const +{ + return false; +} + +void CompilerMSL::analyze_sampled_image_usage() +{ + if (msl_options.swizzle_texture_samples) + { + SampledImageScanner scanner(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), scanner); + } +} + +bool CompilerMSL::SampledImageScanner::handle(spv::Op opcode, const uint32_t *args, uint32_t length) +{ + switch (opcode) + { + case OpLoad: + case OpImage: + case OpSampledImage: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + auto &type = compiler.get(result_type); + if ((type.basetype != SPIRType::Image && type.basetype != SPIRType::SampledImage) || type.image.sampled != 1) + return true; + + uint32_t id = args[1]; + compiler.set(id, "", result_type, true); + break; + } + case OpImageSampleExplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageFetch: + case OpImageGather: + case OpImageDrefGather: + compiler.has_sampled_images = + compiler.has_sampled_images || compiler.is_sampled_image_type(compiler.expression_type(args[2])); + compiler.needs_swizzle_buffer_def = compiler.needs_swizzle_buffer_def || compiler.has_sampled_images; + break; + default: + break; + } + return true; +} + +// If a needed custom function wasn't added before, add it and force a recompile. +void CompilerMSL::add_spv_func_and_recompile(SPVFuncImpl spv_func) +{ + if (spv_function_implementations.count(spv_func) == 0) + { + spv_function_implementations.insert(spv_func); + suppress_missing_prototypes = true; + force_recompile(); + } +} + +bool CompilerMSL::OpCodePreprocessor::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + // Since MSL exists in a single execution scope, function prototype declarations are not + // needed, and clutter the output. If secondary functions are output (either as a SPIR-V + // function implementation or as indicated by the presence of OpFunctionCall), then set + // suppress_missing_prototypes to suppress compiler warnings of missing function prototypes. + + // Mark if the input requires the implementation of an SPIR-V function that does not exist in Metal. + SPVFuncImpl spv_func = get_spv_func_impl(opcode, args); + if (spv_func != SPVFuncImplNone) + { + compiler.spv_function_implementations.insert(spv_func); + suppress_missing_prototypes = true; + } + + switch (opcode) + { + + case OpFunctionCall: + suppress_missing_prototypes = true; + break; + + // Emulate texture2D atomic operations + case OpImageTexelPointer: + { + auto *var = compiler.maybe_get_backing_variable(args[2]); + image_pointers[args[1]] = var ? var->self : ID(0); + break; + } + + case OpImageWrite: + if (!compiler.msl_options.supports_msl_version(2, 2)) + uses_resource_write = true; + break; + + case OpStore: + check_resource_write(args[0]); + break; + + // Emulate texture2D atomic operations + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicCompareExchangeWeak: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + { + uses_atomics = true; + auto it = image_pointers.find(args[2]); + if (it != image_pointers.end()) + { + compiler.atomic_image_vars.insert(it->second); + } + check_resource_write(args[2]); + break; + } + + case OpAtomicStore: + { + uses_atomics = true; + auto it = image_pointers.find(args[0]); + if (it != image_pointers.end()) + { + compiler.atomic_image_vars.insert(it->second); + } + check_resource_write(args[0]); + break; + } + + case OpAtomicLoad: + { + uses_atomics = true; + auto it = image_pointers.find(args[2]); + if (it != image_pointers.end()) + { + compiler.atomic_image_vars.insert(it->second); + } + break; + } + + case OpGroupNonUniformInverseBallot: + needs_subgroup_invocation_id = true; + break; + + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + needs_subgroup_size = true; + break; + + case OpGroupNonUniformBallotBitCount: + if (args[3] == GroupOperationReduce) + needs_subgroup_size = true; + else + needs_subgroup_invocation_id = true; + break; + + case OpArrayLength: + { + auto *var = compiler.maybe_get_backing_variable(args[2]); + if (var) + compiler.buffers_requiring_array_length.insert(var->self); + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + // OpArrayLength might want to know if taking ArrayLength of an array of SSBOs. + uint32_t result_type = args[0]; + uint32_t id = args[1]; + uint32_t ptr = args[2]; + + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + compiler.ir.ids[id].set_allow_type_rewrite(); + break; + } + + case OpExtInst: + { + uint32_t extension_set = args[2]; + if (compiler.get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(args[3]); + switch (op_450) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + if (!compiler.msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Pull-model interpolation requires MSL 2.3."); + // Fragment varyings used with pull-model interpolation need special handling, + // due to the way pull-model interpolation works in Metal. + auto *var = compiler.maybe_get_backing_variable(args[4]); + if (var) + { + compiler.pull_model_inputs.insert(var->self); + auto &var_type = compiler.get_variable_element_type(*var); + // In addition, if this variable has a 'Sample' decoration, we need the sample ID + // in order to do default interpolation. + if (compiler.has_decoration(var->self, DecorationSample)) + { + needs_sample_id = true; + } + else if (var_type.basetype == SPIRType::Struct) + { + // Now we need to check each member and see if it has this decoration. + for (uint32_t i = 0; i < var_type.member_types.size(); ++i) + { + if (compiler.has_member_decoration(var_type.self, i, DecorationSample)) + { + needs_sample_id = true; + break; + } + } + } + } + break; + } + default: + break; + } + } + break; + } + + default: + break; + } + + // If it has one, keep track of the instruction's result type, mapped by ID + uint32_t result_type, result_id; + if (compiler.instruction_to_result_type(result_type, result_id, opcode, args, length)) + result_types[result_id] = result_type; + + return true; +} + +// If the variable is a Uniform or StorageBuffer, mark that a resource has been written to. +void CompilerMSL::OpCodePreprocessor::check_resource_write(uint32_t var_id) +{ + auto *p_var = compiler.maybe_get_backing_variable(var_id); + StorageClass sc = p_var ? p_var->storage : StorageClassMax; + if (!compiler.msl_options.supports_msl_version(2, 1) && + (sc == StorageClassUniform || sc == StorageClassStorageBuffer)) + uses_resource_write = true; +} + +// Returns an enumeration of a SPIR-V function that needs to be output for certain Op codes. +CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op opcode, const uint32_t *args) +{ + switch (opcode) + { + case OpFMod: + return SPVFuncImplMod; + + case OpFAdd: + if (compiler.msl_options.invariant_float_math) + { + return SPVFuncImplFAdd; + } + break; + + case OpFMul: + case OpOuterProduct: + case OpMatrixTimesVector: + case OpVectorTimesMatrix: + case OpMatrixTimesMatrix: + if (compiler.msl_options.invariant_float_math) + { + return SPVFuncImplFMul; + } + break; + + case OpTypeArray: + { + // Allow Metal to use the array template to make arrays a value type + return SPVFuncImplUnsafeArray; + } + + // Emulate texture2D atomic operations + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicCompareExchangeWeak: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + case OpAtomicLoad: + case OpAtomicStore: + { + auto it = image_pointers.find(args[opcode == OpAtomicStore ? 0 : 2]); + if (it != image_pointers.end()) + { + uint32_t tid = compiler.get(it->second).basetype; + if (tid && compiler.get(tid).image.dim == Dim2D) + return SPVFuncImplImage2DAtomicCoords; + } + break; + } + + case OpImageFetch: + case OpImageRead: + case OpImageWrite: + { + // Retrieve the image type, and if it's a Buffer, emit a texel coordinate function + uint32_t tid = result_types[args[opcode == OpImageWrite ? 0 : 2]]; + if (tid && compiler.get(tid).image.dim == DimBuffer && !compiler.msl_options.texture_buffer_native) + return SPVFuncImplTexelBufferCoords; + break; + } + + case OpExtInst: + { + uint32_t extension_set = args[2]; + if (compiler.get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(args[3]); + switch (op_450) + { + case GLSLstd450Radians: + return SPVFuncImplRadians; + case GLSLstd450Degrees: + return SPVFuncImplDegrees; + case GLSLstd450FindILsb: + return SPVFuncImplFindILsb; + case GLSLstd450FindSMsb: + return SPVFuncImplFindSMsb; + case GLSLstd450FindUMsb: + return SPVFuncImplFindUMsb; + case GLSLstd450SSign: + return SPVFuncImplSSign; + case GLSLstd450Reflect: + { + auto &type = compiler.get(args[0]); + if (type.vecsize == 1) + return SPVFuncImplReflectScalar; + break; + } + case GLSLstd450Refract: + { + auto &type = compiler.get(args[0]); + if (type.vecsize == 1) + return SPVFuncImplRefractScalar; + break; + } + case GLSLstd450FaceForward: + { + auto &type = compiler.get(args[0]); + if (type.vecsize == 1) + return SPVFuncImplFaceForwardScalar; + break; + } + case GLSLstd450MatrixInverse: + { + auto &mat_type = compiler.get(args[0]); + switch (mat_type.columns) + { + case 2: + return SPVFuncImplInverse2x2; + case 3: + return SPVFuncImplInverse3x3; + case 4: + return SPVFuncImplInverse4x4; + default: + break; + } + break; + } + default: + break; + } + } + break; + } + + case OpGroupNonUniformBroadcast: + return SPVFuncImplSubgroupBroadcast; + + case OpGroupNonUniformBroadcastFirst: + return SPVFuncImplSubgroupBroadcastFirst; + + case OpGroupNonUniformBallot: + return SPVFuncImplSubgroupBallot; + + case OpGroupNonUniformInverseBallot: + case OpGroupNonUniformBallotBitExtract: + return SPVFuncImplSubgroupBallotBitExtract; + + case OpGroupNonUniformBallotFindLSB: + return SPVFuncImplSubgroupBallotFindLSB; + + case OpGroupNonUniformBallotFindMSB: + return SPVFuncImplSubgroupBallotFindMSB; + + case OpGroupNonUniformBallotBitCount: + return SPVFuncImplSubgroupBallotBitCount; + + case OpGroupNonUniformAllEqual: + return SPVFuncImplSubgroupAllEqual; + + case OpGroupNonUniformShuffle: + return SPVFuncImplSubgroupShuffle; + + case OpGroupNonUniformShuffleXor: + return SPVFuncImplSubgroupShuffleXor; + + case OpGroupNonUniformShuffleUp: + return SPVFuncImplSubgroupShuffleUp; + + case OpGroupNonUniformShuffleDown: + return SPVFuncImplSubgroupShuffleDown; + + case OpGroupNonUniformQuadBroadcast: + return SPVFuncImplQuadBroadcast; + + case OpGroupNonUniformQuadSwap: + return SPVFuncImplQuadSwap; + + default: + break; + } + return SPVFuncImplNone; +} + +// Sort both type and meta member content based on builtin status (put builtins at end), +// then by the required sorting aspect. +void CompilerMSL::MemberSorter::sort() +{ + // Create a temporary array of consecutive member indices and sort it based on how + // the members should be reordered, based on builtin and sorting aspect meta info. + size_t mbr_cnt = type.member_types.size(); + SmallVector mbr_idxs(mbr_cnt); + std::iota(mbr_idxs.begin(), mbr_idxs.end(), 0); // Fill with consecutive indices + std::stable_sort(mbr_idxs.begin(), mbr_idxs.end(), *this); // Sort member indices based on sorting aspect + + bool sort_is_identity = true; + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + if (mbr_idx != mbr_idxs[mbr_idx]) + { + sort_is_identity = false; + break; + } + } + + if (sort_is_identity) + return; + + if (meta.members.size() < type.member_types.size()) + { + // This should never trigger in normal circumstances, but to be safe. + meta.members.resize(type.member_types.size()); + } + + // Move type and meta member info to the order defined by the sorted member indices. + // This is done by creating temporary copies of both member types and meta, and then + // copying back to the original content at the sorted indices. + auto mbr_types_cpy = type.member_types; + auto mbr_meta_cpy = meta.members; + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + type.member_types[mbr_idx] = mbr_types_cpy[mbr_idxs[mbr_idx]]; + meta.members[mbr_idx] = mbr_meta_cpy[mbr_idxs[mbr_idx]]; + } + + if (sort_aspect == SortAspect::Offset) + { + // If we're sorting by Offset, this might affect user code which accesses a buffer block. + // We will need to redirect member indices from one index to sorted index. + type.member_type_index_redirection = std::move(mbr_idxs); + } +} + +// Sort first by builtin status (put builtins at end), then by the sorting aspect. +bool CompilerMSL::MemberSorter::operator()(uint32_t mbr_idx1, uint32_t mbr_idx2) +{ + auto &mbr_meta1 = meta.members[mbr_idx1]; + auto &mbr_meta2 = meta.members[mbr_idx2]; + if (mbr_meta1.builtin != mbr_meta2.builtin) + return mbr_meta2.builtin; + else + switch (sort_aspect) + { + case Location: + return mbr_meta1.location < mbr_meta2.location; + case LocationReverse: + return mbr_meta1.location > mbr_meta2.location; + case Offset: + return mbr_meta1.offset < mbr_meta2.offset; + case OffsetThenLocationReverse: + return (mbr_meta1.offset < mbr_meta2.offset) || + ((mbr_meta1.offset == mbr_meta2.offset) && (mbr_meta1.location > mbr_meta2.location)); + case Alphabetical: + return mbr_meta1.alias < mbr_meta2.alias; + default: + return false; + } +} + +CompilerMSL::MemberSorter::MemberSorter(SPIRType &t, Meta &m, SortAspect sa) + : type(t) + , meta(m) + , sort_aspect(sa) +{ + // Ensure enough meta info is available + meta.members.resize(max(type.member_types.size(), meta.members.size())); +} + +void CompilerMSL::remap_constexpr_sampler(VariableID id, const MSLConstexprSampler &sampler) +{ + auto &type = get(get(id).basetype); + if (type.basetype != SPIRType::SampledImage && type.basetype != SPIRType::Sampler) + SPIRV_CROSS_THROW("Can only remap SampledImage and Sampler type."); + if (!type.array.empty()) + SPIRV_CROSS_THROW("Can not remap array of samplers."); + constexpr_samplers_by_id[id] = sampler; +} + +void CompilerMSL::remap_constexpr_sampler_by_binding(uint32_t desc_set, uint32_t binding, + const MSLConstexprSampler &sampler) +{ + constexpr_samplers_by_binding[{ desc_set, binding }] = sampler; +} + +void CompilerMSL::cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) +{ + auto *var = maybe_get_backing_variable(source_id); + if (var) + source_id = var->self; + + // Only interested in standalone builtin variables. + if (!has_decoration(source_id, DecorationBuiltIn)) + return; + + auto builtin = static_cast(get_decoration(source_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + auto expected_width = expr_type.width; + switch (builtin) + { + case BuiltInGlobalInvocationId: + case BuiltInLocalInvocationId: + case BuiltInWorkgroupId: + case BuiltInLocalInvocationIndex: + case BuiltInWorkgroupSize: + case BuiltInNumWorkgroups: + case BuiltInLayer: + case BuiltInViewportIndex: + case BuiltInFragStencilRefEXT: + case BuiltInPrimitiveId: + case BuiltInSubgroupSize: + case BuiltInSubgroupLocalInvocationId: + case BuiltInViewIndex: + case BuiltInVertexIndex: + case BuiltInInstanceIndex: + case BuiltInBaseInstance: + case BuiltInBaseVertex: + expected_type = SPIRType::UInt; + expected_width = 32; + break; + + case BuiltInTessLevelInner: + case BuiltInTessLevelOuter: + if (get_execution_model() == ExecutionModelTessellationControl) + { + expected_type = SPIRType::Half; + expected_width = 16; + } + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + { + if (expected_width != expr_type.width) + { + // These are of different widths, so we cannot do a straight bitcast. + expr = join(type_to_glsl(expr_type), "(", expr, ")"); + } + else + { + expr = bitcast_expression(expr_type, expected_type, expr); + } + } + + if (builtin == BuiltInTessCoord && get_entry_point().flags.get(ExecutionModeQuads) && expr_type.vecsize == 3) + { + // In SPIR-V, this is always a vec3, even for quads. In Metal, though, it's a float2 for quads. + // The code is expecting a float3, so we need to widen this. + expr = join("float3(", expr, ", 0)"); + } +} + +void CompilerMSL::cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) +{ + auto *var = maybe_get_backing_variable(target_id); + if (var) + target_id = var->self; + + // Only interested in standalone builtin variables. + if (!has_decoration(target_id, DecorationBuiltIn)) + return; + + auto builtin = static_cast(get_decoration(target_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + auto expected_width = expr_type.width; + switch (builtin) + { + case BuiltInLayer: + case BuiltInViewportIndex: + case BuiltInFragStencilRefEXT: + case BuiltInPrimitiveId: + case BuiltInViewIndex: + expected_type = SPIRType::UInt; + expected_width = 32; + break; + + case BuiltInTessLevelInner: + case BuiltInTessLevelOuter: + expected_type = SPIRType::Half; + expected_width = 16; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + { + if (expected_width != expr_type.width) + { + // These are of different widths, so we cannot do a straight bitcast. + auto type = expr_type; + type.basetype = expected_type; + type.width = expected_width; + expr = join(type_to_glsl(type), "(", expr, ")"); + } + else + { + auto type = expr_type; + type.basetype = expected_type; + expr = bitcast_expression(type, expr_type.basetype, expr); + } + } +} + +string CompilerMSL::to_initializer_expression(const SPIRVariable &var) +{ + // We risk getting an array initializer here with MSL. If we have an array. + // FIXME: We cannot handle non-constant arrays being initialized. + // We will need to inject spvArrayCopy here somehow ... + auto &type = get(var.basetype); + string expr; + if (ir.ids[var.initializer].get_type() == TypeConstant && + (!type.array.empty() || type.basetype == SPIRType::Struct)) + expr = constant_expression(get(var.initializer)); + else + expr = CompilerGLSL::to_initializer_expression(var); + // If the initializer has more vector components than the variable, add a swizzle. + // FIXME: This can't handle arrays or structs. + auto &init_type = expression_type(var.initializer); + if (type.array.empty() && type.basetype != SPIRType::Struct && init_type.vecsize > type.vecsize) + expr = enclose_expression(expr + vector_swizzle(type.vecsize, 0)); + return expr; +} + +string CompilerMSL::to_zero_initialized_expression(uint32_t) +{ + return "{}"; +} + +bool CompilerMSL::descriptor_set_is_argument_buffer(uint32_t desc_set) const +{ + if (!msl_options.argument_buffers) + return false; + if (desc_set >= kMaxArgumentBuffers) + return false; + + return (argument_buffer_discrete_mask & (1u << desc_set)) == 0; +} + +void CompilerMSL::analyze_argument_buffers() +{ + // Gather all used resources and sort them out into argument buffers. + // Each argument buffer corresponds to a descriptor set in SPIR-V. + // The [[id(N)]] values used correspond to the resource mapping we have for MSL. + // Otherwise, the binding number is used, but this is generally not safe some types like + // combined image samplers and arrays of resources. Metal needs different indices here, + // while SPIR-V can have one descriptor set binding. To use argument buffers in practice, + // you will need to use the remapping from the API. + for (auto &id : argument_buffer_ids) + id = 0; + + // Output resources, sorted by resource index & type. + struct Resource + { + SPIRVariable *var; + string name; + SPIRType::BaseType basetype; + uint32_t index; + uint32_t plane; + }; + SmallVector resources_in_set[kMaxArgumentBuffers]; + SmallVector inline_block_vars; + + bool set_needs_swizzle_buffer[kMaxArgumentBuffers] = {}; + bool set_needs_buffer_sizes[kMaxArgumentBuffers] = {}; + bool needs_buffer_sizes = false; + + ir.for_each_typed_id([&](uint32_t self, SPIRVariable &var) { + if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || + var.storage == StorageClassStorageBuffer) && + !is_hidden_variable(var)) + { + uint32_t desc_set = get_decoration(self, DecorationDescriptorSet); + // Ignore if it's part of a push descriptor set. + if (!descriptor_set_is_argument_buffer(desc_set)) + return; + + uint32_t var_id = var.self; + auto &type = get_variable_data_type(var); + + if (desc_set >= kMaxArgumentBuffers) + SPIRV_CROSS_THROW("Descriptor set index is out of range."); + + const MSLConstexprSampler *constexpr_sampler = nullptr; + if (type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::Sampler) + { + constexpr_sampler = find_constexpr_sampler(var_id); + if (constexpr_sampler) + { + // Mark this ID as a constexpr sampler for later in case it came from set/bindings. + constexpr_samplers_by_id[var_id] = *constexpr_sampler; + } + } + + uint32_t binding = get_decoration(var_id, DecorationBinding); + if (type.basetype == SPIRType::SampledImage) + { + add_resource_name(var_id); + + uint32_t plane_count = 1; + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + plane_count = constexpr_sampler->planes; + + for (uint32_t i = 0; i < plane_count; i++) + { + uint32_t image_resource_index = get_metal_resource_index(var, SPIRType::Image, i); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::Image, image_resource_index, i }); + } + + if (type.image.dim != DimBuffer && !constexpr_sampler) + { + uint32_t sampler_resource_index = get_metal_resource_index(var, SPIRType::Sampler); + resources_in_set[desc_set].push_back( + { &var, to_sampler_expression(var_id), SPIRType::Sampler, sampler_resource_index, 0 }); + } + } + else if (inline_uniform_blocks.count(SetBindingPair{ desc_set, binding })) + { + inline_block_vars.push_back(var_id); + } + else if (!constexpr_sampler) + { + // constexpr samplers are not declared as resources. + // Inline uniform blocks are always emitted at the end. + if (!msl_options.is_ios() || type.basetype != SPIRType::Image || type.image.sampled != 2) + { + add_resource_name(var_id); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), type.basetype, get_metal_resource_index(var, type.basetype), 0 }); + + // Emulate texture2D atomic operations + if (atomic_image_vars.count(var.self)) + { + uint32_t buffer_resource_index = get_metal_resource_index(var, SPIRType::AtomicCounter, 0); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id) + "_atomic", SPIRType::Struct, buffer_resource_index, 0 }); + } + } + } + + // Check if this descriptor set needs a swizzle buffer. + if (needs_swizzle_buffer_def && is_sampled_image_type(type)) + set_needs_swizzle_buffer[desc_set] = true; + else if (buffers_requiring_array_length.count(var_id) != 0) + { + set_needs_buffer_sizes[desc_set] = true; + needs_buffer_sizes = true; + } + } + }); + + if (needs_swizzle_buffer_def || needs_buffer_sizes) + { + uint32_t uint_ptr_type_id = 0; + + // We might have to add a swizzle buffer resource to the set. + for (uint32_t desc_set = 0; desc_set < kMaxArgumentBuffers; desc_set++) + { + if (!set_needs_swizzle_buffer[desc_set] && !set_needs_buffer_sizes[desc_set]) + continue; + + if (uint_ptr_type_id == 0) + { + uint_ptr_type_id = ir.increase_bound_by(1); + + // Create a buffer to hold extra data, including the swizzle constants. + SPIRType uint_type_pointer = get_uint_type(); + uint_type_pointer.pointer = true; + uint_type_pointer.pointer_depth = 1; + uint_type_pointer.parent_type = get_uint_type_id(); + uint_type_pointer.storage = StorageClassUniform; + set(uint_ptr_type_id, uint_type_pointer); + set_decoration(uint_ptr_type_id, DecorationArrayStride, 4); + } + + if (set_needs_swizzle_buffer[desc_set]) + { + uint32_t var_id = ir.increase_bound_by(1); + auto &var = set(var_id, uint_ptr_type_id, StorageClassUniformConstant); + set_name(var_id, "spvSwizzleConstants"); + set_decoration(var_id, DecorationDescriptorSet, desc_set); + set_decoration(var_id, DecorationBinding, kSwizzleBufferBinding); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::UInt, get_metal_resource_index(var, SPIRType::UInt), 0 }); + } + + if (set_needs_buffer_sizes[desc_set]) + { + uint32_t var_id = ir.increase_bound_by(1); + auto &var = set(var_id, uint_ptr_type_id, StorageClassUniformConstant); + set_name(var_id, "spvBufferSizeConstants"); + set_decoration(var_id, DecorationDescriptorSet, desc_set); + set_decoration(var_id, DecorationBinding, kBufferSizeBufferBinding); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::UInt, get_metal_resource_index(var, SPIRType::UInt), 0 }); + } + } + } + + // Now add inline uniform blocks. + for (uint32_t var_id : inline_block_vars) + { + auto &var = get(var_id); + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + add_resource_name(var_id); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::Struct, get_metal_resource_index(var, SPIRType::Struct), 0 }); + } + + for (uint32_t desc_set = 0; desc_set < kMaxArgumentBuffers; desc_set++) + { + auto &resources = resources_in_set[desc_set]; + if (resources.empty()) + continue; + + assert(descriptor_set_is_argument_buffer(desc_set)); + + uint32_t next_id = ir.increase_bound_by(3); + uint32_t type_id = next_id + 1; + uint32_t ptr_type_id = next_id + 2; + argument_buffer_ids[desc_set] = next_id; + + auto &buffer_type = set(type_id); + + buffer_type.basetype = SPIRType::Struct; + + if ((argument_buffer_device_storage_mask & (1u << desc_set)) != 0) + { + buffer_type.storage = StorageClassStorageBuffer; + // Make sure the argument buffer gets marked as const device. + set_decoration(next_id, DecorationNonWritable); + // Need to mark the type as a Block to enable this. + set_decoration(type_id, DecorationBlock); + } + else + buffer_type.storage = StorageClassUniform; + + set_name(type_id, join("spvDescriptorSetBuffer", desc_set)); + + auto &ptr_type = set(ptr_type_id); + ptr_type = buffer_type; + ptr_type.pointer = true; + ptr_type.pointer_depth = 1; + ptr_type.parent_type = type_id; + + uint32_t buffer_variable_id = next_id; + set(buffer_variable_id, ptr_type_id, StorageClassUniform); + set_name(buffer_variable_id, join("spvDescriptorSet", desc_set)); + + // Ids must be emitted in ID order. + sort(begin(resources), end(resources), [&](const Resource &lhs, const Resource &rhs) -> bool { + return tie(lhs.index, lhs.basetype) < tie(rhs.index, rhs.basetype); + }); + + uint32_t member_index = 0; + for (auto &resource : resources) + { + auto &var = *resource.var; + auto &type = get_variable_data_type(var); + string mbr_name = ensure_valid_name(resource.name, "m"); + if (resource.plane > 0) + mbr_name += join(plane_name_suffix, resource.plane); + set_member_name(buffer_type.self, member_index, mbr_name); + + if (resource.basetype == SPIRType::Sampler && type.basetype != SPIRType::Sampler) + { + // Have to synthesize a sampler type here. + + bool type_is_array = !type.array.empty(); + uint32_t sampler_type_id = ir.increase_bound_by(type_is_array ? 2 : 1); + auto &new_sampler_type = set(sampler_type_id); + new_sampler_type.basetype = SPIRType::Sampler; + new_sampler_type.storage = StorageClassUniformConstant; + + if (type_is_array) + { + uint32_t sampler_type_array_id = sampler_type_id + 1; + auto &sampler_type_array = set(sampler_type_array_id); + sampler_type_array = new_sampler_type; + sampler_type_array.array = type.array; + sampler_type_array.array_size_literal = type.array_size_literal; + sampler_type_array.parent_type = sampler_type_id; + buffer_type.member_types.push_back(sampler_type_array_id); + } + else + buffer_type.member_types.push_back(sampler_type_id); + } + else + { + uint32_t binding = get_decoration(var.self, DecorationBinding); + SetBindingPair pair = { desc_set, binding }; + + if (resource.basetype == SPIRType::Image || resource.basetype == SPIRType::Sampler || + resource.basetype == SPIRType::SampledImage) + { + // Drop pointer information when we emit the resources into a struct. + buffer_type.member_types.push_back(get_variable_data_type_id(var)); + if (resource.plane == 0) + set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name)); + } + else if (buffers_requiring_dynamic_offset.count(pair)) + { + // Don't set the qualified name here; we'll define a variable holding the corrected buffer address later. + buffer_type.member_types.push_back(var.basetype); + buffers_requiring_dynamic_offset[pair].second = var.self; + } + else if (inline_uniform_blocks.count(pair)) + { + // Put the buffer block itself into the argument buffer. + buffer_type.member_types.push_back(get_variable_data_type_id(var)); + set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name)); + } + else if (atomic_image_vars.count(var.self)) + { + // Emulate texture2D atomic operations. + // Don't set the qualified name: it's already set for this variable, + // and the code that references the buffer manually appends "_atomic" + // to the name. + uint32_t offset = ir.increase_bound_by(2); + uint32_t atomic_type_id = offset; + uint32_t type_ptr_id = offset + 1; + + SPIRType atomic_type; + atomic_type.basetype = SPIRType::AtomicCounter; + atomic_type.width = 32; + atomic_type.vecsize = 1; + set(atomic_type_id, atomic_type); + + atomic_type.pointer = true; + atomic_type.parent_type = atomic_type_id; + atomic_type.storage = StorageClassStorageBuffer; + auto &atomic_ptr_type = set(type_ptr_id, atomic_type); + atomic_ptr_type.self = atomic_type_id; + + buffer_type.member_types.push_back(type_ptr_id); + } + else + { + // Resources will be declared as pointers not references, so automatically dereference as appropriate. + buffer_type.member_types.push_back(var.basetype); + if (type.array.empty()) + set_qualified_name(var.self, join("(*", to_name(buffer_variable_id), ".", mbr_name, ")")); + else + set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name)); + } + } + + set_extended_member_decoration(buffer_type.self, member_index, SPIRVCrossDecorationResourceIndexPrimary, + resource.index); + set_extended_member_decoration(buffer_type.self, member_index, SPIRVCrossDecorationInterfaceOrigID, + var.self); + member_index++; + } + } +} + +void CompilerMSL::activate_argument_buffer_resources() +{ + // For ABI compatibility, force-enable all resources which are part of argument buffers. + ir.for_each_typed_id([&](uint32_t self, const SPIRVariable &) { + if (!has_decoration(self, DecorationDescriptorSet)) + return; + + uint32_t desc_set = get_decoration(self, DecorationDescriptorSet); + if (descriptor_set_is_argument_buffer(desc_set)) + active_interface_variables.insert(self); + }); +} + +bool CompilerMSL::using_builtin_array() const +{ + return msl_options.force_native_arrays || is_using_builtin_array; +} + +void CompilerMSL::set_combined_sampler_suffix(const char *suffix) +{ + sampler_name_suffix = suffix; +} + +const char *CompilerMSL::get_combined_sampler_suffix() const +{ + return sampler_name_suffix.c_str(); +} diff --git a/third_party/spirv-cross/spirv_msl.hpp b/third_party/spirv-cross/spirv_msl.hpp new file mode 100644 index 0000000..a26047e --- /dev/null +++ b/third_party/spirv-cross/spirv_msl.hpp @@ -0,0 +1,1048 @@ +/* + * Copyright 2016-2020 The Brenwill Workshop Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_MSL_HPP +#define SPIRV_CROSS_MSL_HPP + +#include "spirv_glsl.hpp" +#include +#include +#include +#include +#include + +namespace SPIRV_CROSS_NAMESPACE +{ + +// Indicates the format of a shader input. Currently limited to specifying +// if the input is an 8-bit unsigned integer, 16-bit unsigned integer, or +// some other format. +enum MSLShaderInputFormat +{ + MSL_SHADER_INPUT_FORMAT_OTHER = 0, + MSL_SHADER_INPUT_FORMAT_UINT8 = 1, + MSL_SHADER_INPUT_FORMAT_UINT16 = 2, + MSL_SHADER_INPUT_FORMAT_ANY16 = 3, + MSL_SHADER_INPUT_FORMAT_ANY32 = 4, + + // Deprecated aliases. + MSL_VERTEX_FORMAT_OTHER = MSL_SHADER_INPUT_FORMAT_OTHER, + MSL_VERTEX_FORMAT_UINT8 = MSL_SHADER_INPUT_FORMAT_UINT8, + MSL_VERTEX_FORMAT_UINT16 = MSL_SHADER_INPUT_FORMAT_UINT16, + + MSL_SHADER_INPUT_FORMAT_INT_MAX = 0x7fffffff +}; + +// Defines MSL characteristics of an input variable at a particular location. +// After compilation, it is possible to query whether or not this location was used. +// If vecsize is nonzero, it must be greater than or equal to the vecsize declared in the shader, +// or behavior is undefined. +struct MSLShaderInput +{ + uint32_t location = 0; + MSLShaderInputFormat format = MSL_SHADER_INPUT_FORMAT_OTHER; + spv::BuiltIn builtin = spv::BuiltInMax; + uint32_t vecsize = 0; +}; + +// Matches the binding index of a MSL resource for a binding within a descriptor set. +// Taken together, the stage, desc_set and binding combine to form a reference to a resource +// descriptor used in a particular shading stage. The count field indicates the number of +// resources consumed by this binding, if the binding represents an array of resources. +// If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value +// will be used to declare the array size in MSL, which does not support run-time-sized arrays. +// For resources that are not held in a run-time-sized array, the count field does not need to be populated. +// If using MSL 2.0 argument buffers, the descriptor set is not marked as a discrete descriptor set, +// and (for iOS only) the resource is not a storage image (sampled != 2), the binding reference we +// remap to will become an [[id(N)]] attribute within the "descriptor set" argument buffer structure. +// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will become a +// [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used. +struct MSLResourceBinding +{ + spv::ExecutionModel stage = spv::ExecutionModelMax; + uint32_t desc_set = 0; + uint32_t binding = 0; + uint32_t count = 0; + uint32_t msl_buffer = 0; + uint32_t msl_texture = 0; + uint32_t msl_sampler = 0; +}; + +enum MSLSamplerCoord +{ + MSL_SAMPLER_COORD_NORMALIZED = 0, + MSL_SAMPLER_COORD_PIXEL = 1, + MSL_SAMPLER_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerFilter +{ + MSL_SAMPLER_FILTER_NEAREST = 0, + MSL_SAMPLER_FILTER_LINEAR = 1, + MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerMipFilter +{ + MSL_SAMPLER_MIP_FILTER_NONE = 0, + MSL_SAMPLER_MIP_FILTER_NEAREST = 1, + MSL_SAMPLER_MIP_FILTER_LINEAR = 2, + MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerAddress +{ + MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0, + MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1, + MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2, + MSL_SAMPLER_ADDRESS_REPEAT = 3, + MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4, + MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerCompareFunc +{ + MSL_SAMPLER_COMPARE_FUNC_NEVER = 0, + MSL_SAMPLER_COMPARE_FUNC_LESS = 1, + MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2, + MSL_SAMPLER_COMPARE_FUNC_GREATER = 3, + MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4, + MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5, + MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6, + MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7, + MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerBorderColor +{ + MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0, + MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1, + MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2, + MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff +}; + +enum MSLFormatResolution +{ + MSL_FORMAT_RESOLUTION_444 = 0, + MSL_FORMAT_RESOLUTION_422, + MSL_FORMAT_RESOLUTION_420, + MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff +}; + +enum MSLChromaLocation +{ + MSL_CHROMA_LOCATION_COSITED_EVEN = 0, + MSL_CHROMA_LOCATION_MIDPOINT, + MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff +}; + +enum MSLComponentSwizzle +{ + MSL_COMPONENT_SWIZZLE_IDENTITY = 0, + MSL_COMPONENT_SWIZZLE_ZERO, + MSL_COMPONENT_SWIZZLE_ONE, + MSL_COMPONENT_SWIZZLE_R, + MSL_COMPONENT_SWIZZLE_G, + MSL_COMPONENT_SWIZZLE_B, + MSL_COMPONENT_SWIZZLE_A, + MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerYCbCrModelConversion +{ + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerYCbCrRange +{ + MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, + MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW, + MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff +}; + +struct MSLConstexprSampler +{ + MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED; + MSLSamplerFilter min_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLSamplerFilter mag_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLSamplerMipFilter mip_filter = MSL_SAMPLER_MIP_FILTER_NONE; + MSLSamplerAddress s_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerAddress t_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerAddress r_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerCompareFunc compare_func = MSL_SAMPLER_COMPARE_FUNC_NEVER; + MSLSamplerBorderColor border_color = MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK; + float lod_clamp_min = 0.0f; + float lod_clamp_max = 1000.0f; + int max_anisotropy = 1; + + // Sampler Y'CbCr conversion parameters + uint32_t planes = 0; + MSLFormatResolution resolution = MSL_FORMAT_RESOLUTION_444; + MSLSamplerFilter chroma_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLChromaLocation x_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN; + MSLChromaLocation y_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN; + MSLComponentSwizzle swizzle[4]; // IDENTITY, IDENTITY, IDENTITY, IDENTITY + MSLSamplerYCbCrModelConversion ycbcr_model = MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; + MSLSamplerYCbCrRange ycbcr_range = MSL_SAMPLER_YCBCR_RANGE_ITU_FULL; + uint32_t bpc = 8; + + bool compare_enable = false; + bool lod_clamp_enable = false; + bool anisotropy_enable = false; + bool ycbcr_conversion_enable = false; + + MSLConstexprSampler() + { + for (uint32_t i = 0; i < 4; i++) + swizzle[i] = MSL_COMPONENT_SWIZZLE_IDENTITY; + } + bool swizzle_is_identity() const + { + return (swizzle[0] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[1] == MSL_COMPONENT_SWIZZLE_IDENTITY && + swizzle[2] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[3] == MSL_COMPONENT_SWIZZLE_IDENTITY); + } + bool swizzle_has_one_or_zero() const + { + return (swizzle[0] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[0] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[1] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[1] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[2] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[2] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[3] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[3] == MSL_COMPONENT_SWIZZLE_ONE); + } +}; + +// Special constant used in a MSLResourceBinding desc_set +// element to indicate the bindings for the push constants. +// Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly. +static const uint32_t kPushConstDescSet = ResourceBindingPushConstantDescriptorSet; + +// Special constant used in a MSLResourceBinding binding +// element to indicate the bindings for the push constants. +// Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly. +static const uint32_t kPushConstBinding = ResourceBindingPushConstantBinding; + +// Special constant used in a MSLResourceBinding binding +// element to indicate the buffer binding for swizzle buffers. +static const uint32_t kSwizzleBufferBinding = ~(1u); + +// Special constant used in a MSLResourceBinding binding +// element to indicate the buffer binding for buffer size buffers to support OpArrayLength. +static const uint32_t kBufferSizeBufferBinding = ~(2u); + +// Special constant used in a MSLResourceBinding binding +// element to indicate the buffer binding used for the argument buffer itself. +// This buffer binding should be kept as small as possible as all automatic bindings for buffers +// will start at max(kArgumentBufferBinding) + 1. +static const uint32_t kArgumentBufferBinding = ~(3u); + +static const uint32_t kMaxArgumentBuffers = 8; + +// The arbitrary maximum for the nesting of array of array copies. +static const uint32_t kArrayCopyMultidimMax = 6; + +// Decompiles SPIR-V to Metal Shading Language +class CompilerMSL : public CompilerGLSL +{ +public: + // Options for compiling to Metal Shading Language + struct Options + { + typedef enum + { + iOS = 0, + macOS = 1 + } Platform; + + Platform platform = macOS; + uint32_t msl_version = make_msl_version(1, 2); + uint32_t texel_buffer_texture_width = 4096; // Width of 2D Metal textures used as 1D texel buffers + uint32_t r32ui_linear_texture_alignment = 4; + uint32_t r32ui_alignment_constant_id = 65535; + uint32_t swizzle_buffer_index = 30; + uint32_t indirect_params_buffer_index = 29; + uint32_t shader_output_buffer_index = 28; + uint32_t shader_patch_output_buffer_index = 27; + uint32_t shader_tess_factor_buffer_index = 26; + uint32_t buffer_size_buffer_index = 25; + uint32_t view_mask_buffer_index = 24; + uint32_t dynamic_offsets_buffer_index = 23; + uint32_t shader_input_buffer_index = 22; + uint32_t shader_index_buffer_index = 21; + uint32_t shader_input_wg_index = 0; + uint32_t device_index = 0; + uint32_t enable_frag_output_mask = 0xffffffff; + // Metal doesn't allow setting a fixed sample mask directly in the pipeline. + // We can evade this restriction by ANDing the internal sample_mask output + // of the shader with the additional fixed sample mask. + uint32_t additional_fixed_sample_mask = 0xffffffff; + bool enable_point_size_builtin = true; + bool enable_frag_depth_builtin = true; + bool enable_frag_stencil_ref_builtin = true; + bool disable_rasterization = false; + bool capture_output_to_buffer = false; + bool swizzle_texture_samples = false; + bool tess_domain_origin_lower_left = false; + bool multiview = false; + bool multiview_layered_rendering = true; + bool view_index_from_device_index = false; + bool dispatch_base = false; + bool texture_1D_as_2D = false; + + // Enable use of MSL 2.0 indirect argument buffers. + // MSL 2.0 must also be enabled. + bool argument_buffers = false; + + // Ensures vertex and instance indices start at zero. This reflects the behavior of HLSL with SV_VertexID and SV_InstanceID. + bool enable_base_index_zero = false; + + // Fragment output in MSL must have at least as many components as the render pass. + // Add support to explicit pad out components. + bool pad_fragment_output_components = false; + + // Specifies whether the iOS target version supports the [[base_vertex]] and [[base_instance]] attributes. + bool ios_support_base_vertex_instance = false; + + // Use Metal's native frame-buffer fetch API for subpass inputs. + bool use_framebuffer_fetch_subpasses = false; + + // Enables use of "fma" intrinsic for invariant float math + bool invariant_float_math = false; + + // Emulate texturecube_array with texture2d_array for iOS where this type is not available + bool emulate_cube_array = false; + + // Allow user to enable decoration binding + bool enable_decoration_binding = false; + + // Requires MSL 2.1, use the native support for texel buffers. + bool texture_buffer_native = false; + + // Forces all resources which are part of an argument buffer to be considered active. + // This ensures ABI compatibility between shaders where some resources might be unused, + // and would otherwise declare a different IAB. + bool force_active_argument_buffer_resources = false; + + // Forces the use of plain arrays, which works around certain driver bugs on certain versions + // of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210. + // May reduce performance in scenarios where arrays are copied around as value-types. + bool force_native_arrays = false; + + // If a shader writes clip distance, also emit user varyings which + // can be read in subsequent stages. + bool enable_clip_distance_user_varying = true; + + // In a tessellation control shader, assume that more than one patch can be processed in a + // single workgroup. This requires changes to the way the InvocationId and PrimitiveId + // builtins are processed, but should result in more efficient usage of the GPU. + bool multi_patch_workgroup = false; + + // If set, a vertex shader will be compiled as part of a tessellation pipeline. + // It will be translated as a compute kernel, so it can use the global invocation ID + // to index the output buffer. + bool vertex_for_tessellation = false; + + // Assume that SubpassData images have multiple layers. Layered input attachments + // are addressed relative to the Layer output from the vertex pipeline. This option + // has no effect with multiview, since all input attachments are assumed to be layered + // and will be addressed using the current ViewIndex. + bool arrayed_subpass_input = false; + + enum class IndexType + { + None = 0, + UInt16 = 1, + UInt32 = 2 + }; + + // The type of index in the index buffer, if present. For a compute shader, Metal + // requires specifying the indexing at pipeline creation, rather than at draw time + // as with graphics pipelines. This means we must create three different pipelines, + // for no indexing, 16-bit indices, and 32-bit indices. Each requires different + // handling for the gl_VertexIndex builtin. We may as well, then, create three + // different shaders for these three scenarios. + IndexType vertex_index_type = IndexType::None; + + bool is_ios() const + { + return platform == iOS; + } + + bool is_macos() const + { + return platform == macOS; + } + + void set_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) + { + msl_version = make_msl_version(major, minor, patch); + } + + bool supports_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) const + { + return msl_version >= make_msl_version(major, minor, patch); + } + + static uint32_t make_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) + { + return (major * 10000) + (minor * 100) + patch; + } + }; + + const Options &get_msl_options() const + { + return msl_options; + } + + void set_msl_options(const Options &opts) + { + msl_options = opts; + } + + // Provide feedback to calling API to allow runtime to disable pipeline + // rasterization if vertex shader requires rasterization to be disabled. + bool get_is_rasterization_disabled() const + { + return is_rasterization_disabled && (get_entry_point().model == spv::ExecutionModelVertex || + get_entry_point().model == spv::ExecutionModelTessellationControl || + get_entry_point().model == spv::ExecutionModelTessellationEvaluation); + } + + // Provide feedback to calling API to allow it to pass an auxiliary + // swizzle buffer if the shader needs it. + bool needs_swizzle_buffer() const + { + return used_swizzle_buffer; + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing STORAGE_BUFFER buffer sizes to support OpArrayLength. + bool needs_buffer_size_buffer() const + { + return !buffers_requiring_array_length.empty(); + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing the view mask for the current multiview subpass. + bool needs_view_mask_buffer() const + { + return msl_options.multiview && !msl_options.view_index_from_device_index; + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing the dispatch base workgroup ID. + bool needs_dispatch_base_buffer() const + { + return msl_options.dispatch_base && !msl_options.supports_msl_version(1, 2); + } + + // Provide feedback to calling API to allow it to pass an output + // buffer if the shader needs it. + bool needs_output_buffer() const + { + return capture_output_to_buffer && stage_out_var_id != ID(0); + } + + // Provide feedback to calling API to allow it to pass a patch output + // buffer if the shader needs it. + bool needs_patch_output_buffer() const + { + return capture_output_to_buffer && patch_stage_out_var_id != ID(0); + } + + // Provide feedback to calling API to allow it to pass an input threadgroup + // buffer if the shader needs it. + bool needs_input_threadgroup_mem() const + { + return capture_output_to_buffer && stage_in_var_id != ID(0); + } + + explicit CompilerMSL(std::vector spirv); + CompilerMSL(const uint32_t *ir, size_t word_count); + explicit CompilerMSL(const ParsedIR &ir); + explicit CompilerMSL(ParsedIR &&ir); + + // input is a shader input description used to fix up shader input variables. + // If shader inputs are provided, is_msl_shader_input_used() will return true after + // calling ::compile() if the location was used by the MSL code. + void add_msl_shader_input(const MSLShaderInput &input); + + // resource is a resource binding to indicate the MSL buffer, + // texture or sampler index to use for a particular SPIR-V description set + // and binding. If resource bindings are provided, + // is_msl_resource_binding_used() will return true after calling ::compile() if + // the set/binding combination was used by the MSL code. + void add_msl_resource_binding(const MSLResourceBinding &resource); + + // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource + // in this shader. index is the index within the dynamic offset buffer to use. This + // function marks that resource as using a dynamic offset (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC + // or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC). This function only has any effect if argument buffers + // are enabled. If so, the buffer will have its address adjusted at the beginning of the shader with + // an offset taken from the dynamic offset buffer. + void add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index); + + // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource + // in this shader. This function marks that resource as an inline uniform block + // (VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT). This function only has any effect if argument buffers + // are enabled. If so, the buffer block will be directly embedded into the argument + // buffer, instead of being referenced indirectly via pointer. + void add_inline_uniform_block(uint32_t desc_set, uint32_t binding); + + // When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets. + // This corresponds to VK_KHR_push_descriptor in Vulkan. + void add_discrete_descriptor_set(uint32_t desc_set); + + // If an argument buffer is large enough, it may need to be in the device storage space rather than + // constant. Opt-in to this behavior here on a per set basis. + void set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage); + + // Query after compilation is done. This allows you to check if an input location was used by the shader. + bool is_msl_shader_input_used(uint32_t location); + + // NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here. + // Constexpr samplers are always assumed to be emitted. + // No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped + // by remap_constexpr_sampler(_by_binding). + bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; + + // This must only be called after a successful call to CompilerMSL::compile(). + // For a variable resource ID obtained through reflection API, report the automatically assigned resource index. + // If the descriptor set was part of an argument buffer, report the [[id(N)]], + // or [[buffer/texture/sampler]] binding for other resources. + // If the resource was a combined image sampler, report the image binding here, + // use the _secondary version of this call to query the sampler half of the resource. + // If no binding exists, uint32_t(-1) is returned. + uint32_t get_automatic_msl_resource_binding(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers, in which case the + // sampler's binding is returned instead. For any other resource type, -1 is returned. + uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images, + // in which case the second plane's binding is returned instead. For any other resource type, -1 is returned. + uint32_t get_automatic_msl_resource_binding_tertiary(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for triplanar images, + // in which case the third plane's binding is returned instead. For any other resource type, -1 is returned. + uint32_t get_automatic_msl_resource_binding_quaternary(uint32_t id) const; + + // Compiles the SPIR-V code into Metal Shading Language. + std::string compile() override; + + // Remap a sampler with ID to a constexpr sampler. + // Older iOS targets must use constexpr samplers in certain cases (PCF), + // so a static sampler must be used. + // The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler. + // This can be used on both combined image/samplers (sampler2D) or standalone samplers. + // The remapped sampler must not be an array of samplers. + // Prefer remap_constexpr_sampler_by_binding unless you're also doing reflection anyways. + void remap_constexpr_sampler(VariableID id, const MSLConstexprSampler &sampler); + + // Same as remap_constexpr_sampler, except you provide set/binding, rather than variable ID. + // Remaps based on ID take priority over set/binding remaps. + void remap_constexpr_sampler_by_binding(uint32_t desc_set, uint32_t binding, const MSLConstexprSampler &sampler); + + // If using CompilerMSL::Options::pad_fragment_output_components, override the number of components we expect + // to use for a particular location. The default is 4 if number of components is not overridden. + void set_fragment_output_components(uint32_t location, uint32_t components); + + void set_combined_sampler_suffix(const char *suffix); + const char *get_combined_sampler_suffix() const; + +protected: + // An enum of SPIR-V functions that are implemented in additional + // source code that is added to the shader if necessary. + enum SPVFuncImpl + { + SPVFuncImplNone, + SPVFuncImplMod, + SPVFuncImplRadians, + SPVFuncImplDegrees, + SPVFuncImplFindILsb, + SPVFuncImplFindSMsb, + SPVFuncImplFindUMsb, + SPVFuncImplSSign, + SPVFuncImplArrayCopyMultidimBase, + // Unfortunately, we cannot use recursive templates in the MSL compiler properly, + // so stamp out variants up to some arbitrary maximum. + SPVFuncImplArrayCopy = SPVFuncImplArrayCopyMultidimBase + 1, + SPVFuncImplArrayOfArrayCopy2Dim = SPVFuncImplArrayCopyMultidimBase + 2, + SPVFuncImplArrayOfArrayCopy3Dim = SPVFuncImplArrayCopyMultidimBase + 3, + SPVFuncImplArrayOfArrayCopy4Dim = SPVFuncImplArrayCopyMultidimBase + 4, + SPVFuncImplArrayOfArrayCopy5Dim = SPVFuncImplArrayCopyMultidimBase + 5, + SPVFuncImplArrayOfArrayCopy6Dim = SPVFuncImplArrayCopyMultidimBase + 6, + SPVFuncImplTexelBufferCoords, + SPVFuncImplImage2DAtomicCoords, // Emulate texture2D atomic operations + SPVFuncImplFMul, + SPVFuncImplFAdd, + SPVFuncImplCubemapTo2DArrayFace, + SPVFuncImplUnsafeArray, // Allow Metal to use the array template to make arrays a value type + SPVFuncImplInverse4x4, + SPVFuncImplInverse3x3, + SPVFuncImplInverse2x2, + // It is very important that this come before *Swizzle and ChromaReconstruct*, to ensure it's + // emitted before them. + SPVFuncImplForwardArgs, + // Likewise, this must come before *Swizzle. + SPVFuncImplGetSwizzle, + SPVFuncImplTextureSwizzle, + SPVFuncImplGatherSwizzle, + SPVFuncImplGatherCompareSwizzle, + SPVFuncImplSubgroupBroadcast, + SPVFuncImplSubgroupBroadcastFirst, + SPVFuncImplSubgroupBallot, + SPVFuncImplSubgroupBallotBitExtract, + SPVFuncImplSubgroupBallotFindLSB, + SPVFuncImplSubgroupBallotFindMSB, + SPVFuncImplSubgroupBallotBitCount, + SPVFuncImplSubgroupAllEqual, + SPVFuncImplSubgroupShuffle, + SPVFuncImplSubgroupShuffleXor, + SPVFuncImplSubgroupShuffleUp, + SPVFuncImplSubgroupShuffleDown, + SPVFuncImplQuadBroadcast, + SPVFuncImplQuadSwap, + SPVFuncImplReflectScalar, + SPVFuncImplRefractScalar, + SPVFuncImplFaceForwardScalar, + SPVFuncImplChromaReconstructNearest2Plane, + SPVFuncImplChromaReconstructNearest3Plane, + SPVFuncImplChromaReconstructLinear422CositedEven2Plane, + SPVFuncImplChromaReconstructLinear422CositedEven3Plane, + SPVFuncImplChromaReconstructLinear422Midpoint2Plane, + SPVFuncImplChromaReconstructLinear422Midpoint3Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane, + SPVFuncImplExpandITUFullRange, + SPVFuncImplExpandITUNarrowRange, + SPVFuncImplConvertYCbCrBT709, + SPVFuncImplConvertYCbCrBT601, + SPVFuncImplConvertYCbCrBT2020, + SPVFuncImplDynamicImageSampler, + }; + + // If the underlying resource has been used for comparison then duplicate loads of that resource must be too + // Use Metal's native frame-buffer fetch API for subpass inputs. + void emit_texture_op(const Instruction &i, bool sparse) override; + void emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_instruction(const Instruction &instr) override; + void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count) override; + void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count) override; + void emit_header() override; + void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; + void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; + void emit_subgroup_op(const Instruction &i) override; + std::string to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions) override; + void emit_fixup() override; + std::string to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const std::string &qualifier = ""); + void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const std::string &qualifier = "", uint32_t base_offset = 0) override; + void emit_struct_padding_target(const SPIRType &type) override; + std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; + + // Allow Metal to use the array template to make arrays a value type + std::string type_to_array_glsl(const SPIRType &type) override; + + // Threadgroup arrays can't have a wrapper type + std::string variable_decl(const SPIRVariable &variable) override; + + // GCC workaround of lambdas calling protected functions (for older GCC versions) + std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override; + + std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override; + std::string sampler_type(const SPIRType &type, uint32_t id); + std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; + std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; + std::string to_name(uint32_t id, bool allow_alias = true) const override; + std::string to_function_name(const TextureFunctionNameArguments &args) override; + std::string to_function_args(const TextureFunctionArguments &args, bool *p_forward) override; + std::string to_initializer_expression(const SPIRVariable &var) override; + std::string to_zero_initialized_expression(uint32_t type_id) override; + + std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, + bool is_packed, bool row_major) override; + + // Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but [[sample_mask]] is a scalar in Metal. + bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override; + + std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; + bool emit_complex_bitcast(uint32_t result_id, uint32_t id, uint32_t op0) override; + bool skip_argument(uint32_t id) const override; + std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain) override; + std::string to_qualifiers_glsl(uint32_t id) override; + void replace_illegal_names() override; + void declare_undefined_values() override; + void declare_constant_arrays(); + + // Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries + void declare_complex_constant_arrays(); + + bool is_patch_block(const SPIRType &type); + bool is_non_native_row_major_matrix(uint32_t id) override; + bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override; + std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, uint32_t physical_type_id, + bool is_packed) override; + + void preprocess_op_codes(); + void localize_global_variables(); + void extract_global_variables_from_functions(); + void mark_packable_structs(); + void mark_as_packable(SPIRType &type); + + std::unordered_map> function_global_vars; + void extract_global_variables_from_function(uint32_t func_id, std::set &added_arg_ids, + std::unordered_set &global_var_ids, + std::unordered_set &processed_func_ids); + uint32_t add_interface_block(spv::StorageClass storage, bool patch = false); + uint32_t add_interface_block_pointer(uint32_t ib_var_id, spv::StorageClass storage); + + struct InterfaceBlockMeta + { + struct LocationMeta + { + uint32_t num_components = 0; + uint32_t ib_index = ~0u; + }; + std::unordered_map location_meta; + bool strip_array = false; + }; + + void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, InterfaceBlockMeta &meta); + void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta); + void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta); + void add_plain_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, uint32_t index, + InterfaceBlockMeta &meta); + void add_composite_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, uint32_t index, + InterfaceBlockMeta &meta); + uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array); + void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var); + + void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id); + + void mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, spv::StorageClass storage); + uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin); + uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t num_components = 0); + + void emit_custom_templates(); + void emit_custom_functions(); + void emit_resources(); + void emit_specialization_constants_and_structs(); + void emit_interface_block(uint32_t ib_var_id); + bool maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs); + uint32_t get_resource_array_size(uint32_t id) const; + + void fix_up_shader_inputs_outputs(); + + std::string func_type_decl(SPIRType &type); + std::string entry_point_args_classic(bool append_comma); + std::string entry_point_args_argument_buffer(bool append_comma); + std::string entry_point_arg_stage_in(); + void entry_point_args_builtin(std::string &args); + void entry_point_args_discrete_descriptors(std::string &args); + std::string to_qualified_member_name(const SPIRType &type, uint32_t index); + std::string ensure_valid_name(std::string name, std::string pfx); + std::string to_sampler_expression(uint32_t id); + std::string to_swizzle_expression(uint32_t id); + std::string to_buffer_size_expression(uint32_t id); + bool is_direct_input_builtin(spv::BuiltIn builtin); + std::string builtin_qualifier(spv::BuiltIn builtin); + std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0); + std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma); + std::string member_attribute_qualifier(const SPIRType &type, uint32_t index); + std::string argument_decl(const SPIRFunction::Parameter &arg); + std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp); + uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0); + uint32_t get_ordered_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr); + + // MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output. + // These values can change depending on various extended decorations which control packing rules. + // We need to make these rules match up with SPIR-V declared rules. + uint32_t get_declared_type_size_msl(const SPIRType &type, bool packed, bool row_major) const; + uint32_t get_declared_type_array_stride_msl(const SPIRType &type, bool packed, bool row_major) const; + uint32_t get_declared_type_matrix_stride_msl(const SPIRType &type, bool packed, bool row_major) const; + uint32_t get_declared_type_alignment_msl(const SPIRType &type, bool packed, bool row_major) const; + + uint32_t get_declared_struct_member_size_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_array_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_alignment_msl(const SPIRType &struct_type, uint32_t index) const; + + uint32_t get_declared_input_size_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_array_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_alignment_msl(const SPIRType &struct_type, uint32_t index) const; + + const SPIRType &get_physical_member_type(const SPIRType &struct_type, uint32_t index) const; + SPIRType get_presumed_input_type(const SPIRType &struct_type, uint32_t index) const; + + uint32_t get_declared_struct_size_msl(const SPIRType &struct_type, bool ignore_alignment = false, + bool ignore_padding = false) const; + + std::string to_component_argument(uint32_t id); + void align_struct(SPIRType &ib_type, std::unordered_set &aligned_structs); + void mark_scalar_layout_structs(const SPIRType &ib_type); + void mark_struct_members_packed(const SPIRType &type); + void ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t index); + bool validate_member_packing_rules_msl(const SPIRType &type, uint32_t index) const; + std::string get_argument_address_space(const SPIRVariable &argument); + std::string get_type_address_space(const SPIRType &type, uint32_t id, bool argument = false); + const char *to_restrict(uint32_t id, bool space = true); + SPIRType &get_stage_in_struct_type(); + SPIRType &get_stage_out_struct_type(); + SPIRType &get_patch_stage_in_struct_type(); + SPIRType &get_patch_stage_out_struct_type(); + std::string get_tess_factor_struct_name(); + SPIRType &get_uint_type(); + uint32_t get_uint_type_id(); + void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, uint32_t mem_order_1, + uint32_t mem_order_2, bool has_mem_order_2, uint32_t op0, uint32_t op1 = 0, + bool op1_is_pointer = false, bool op1_is_literal = false, uint32_t op2 = 0); + const char *get_memory_order(uint32_t spv_mem_sem); + void add_pragma_line(const std::string &line); + void add_typedef_line(const std::string &line); + void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem); + void emit_array_copy(const std::string &lhs, uint32_t rhs_id, spv::StorageClass lhs_storage, + spv::StorageClass rhs_storage) override; + void build_implicit_builtins(); + uint32_t build_constant_uint_array_pointer(); + void emit_entry_point_declarations() override; + uint32_t builtin_frag_coord_id = 0; + uint32_t builtin_sample_id_id = 0; + uint32_t builtin_sample_mask_id = 0; + uint32_t builtin_vertex_idx_id = 0; + uint32_t builtin_base_vertex_id = 0; + uint32_t builtin_instance_idx_id = 0; + uint32_t builtin_base_instance_id = 0; + uint32_t builtin_view_idx_id = 0; + uint32_t builtin_layer_id = 0; + uint32_t builtin_invocation_id_id = 0; + uint32_t builtin_primitive_id_id = 0; + uint32_t builtin_subgroup_invocation_id_id = 0; + uint32_t builtin_subgroup_size_id = 0; + uint32_t builtin_dispatch_base_id = 0; + uint32_t builtin_stage_input_size_id = 0; + uint32_t swizzle_buffer_id = 0; + uint32_t buffer_size_buffer_id = 0; + uint32_t view_mask_buffer_id = 0; + uint32_t dynamic_offsets_buffer_id = 0; + uint32_t uint_type_id = 0; + + bool does_shader_write_sample_mask = false; + + void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override; + void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override; + void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override; + + void analyze_sampled_image_usage(); + + void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, spv::StorageClass storage, + bool &is_packed) override; + void fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length); + bool emit_tessellation_access_chain(const uint32_t *ops, uint32_t length); + bool emit_tessellation_io_load(uint32_t result_type, uint32_t id, uint32_t ptr); + bool is_out_of_bounds_tessellation_level(uint32_t id_lhs); + + void ensure_builtin(spv::StorageClass storage, spv::BuiltIn builtin); + + void mark_implicit_builtin(spv::StorageClass storage, spv::BuiltIn builtin, uint32_t id); + + std::string convert_to_f32(const std::string &expr, uint32_t components); + + Options msl_options; + std::set spv_function_implementations; + // Must be ordered to ensure declarations are in a specific order. + std::map inputs_by_location; + std::unordered_map inputs_by_builtin; + std::unordered_set inputs_in_use; + std::unordered_map fragment_output_components; + std::set pragma_lines; + std::set typedef_lines; + SmallVector vars_needing_early_declaration; + + std::unordered_map, InternalHasher> resource_bindings; + + uint32_t next_metal_resource_index_buffer = 0; + uint32_t next_metal_resource_index_texture = 0; + uint32_t next_metal_resource_index_sampler = 0; + // Intentionally uninitialized, works around MSVC 2013 bug. + uint32_t next_metal_resource_ids[kMaxArgumentBuffers]; + + VariableID stage_in_var_id = 0; + VariableID stage_out_var_id = 0; + VariableID patch_stage_in_var_id = 0; + VariableID patch_stage_out_var_id = 0; + VariableID stage_in_ptr_var_id = 0; + VariableID stage_out_ptr_var_id = 0; + + // Handle HLSL-style 0-based vertex/instance index. + enum class TriState + { + Neutral, + No, + Yes + }; + TriState needs_base_vertex_arg = TriState::Neutral; + TriState needs_base_instance_arg = TriState::Neutral; + + bool has_sampled_images = false; + bool builtin_declaration = false; // Handle HLSL-style 0-based vertex/instance index. + + bool is_using_builtin_array = false; // Force the use of C style array declaration. + bool using_builtin_array() const; + + bool is_rasterization_disabled = false; + bool capture_output_to_buffer = false; + bool needs_swizzle_buffer_def = false; + bool used_swizzle_buffer = false; + bool added_builtin_tess_level = false; + bool needs_subgroup_invocation_id = false; + bool needs_subgroup_size = false; + bool needs_sample_id = false; + std::string qual_pos_var_name; + std::string stage_in_var_name = "in"; + std::string stage_out_var_name = "out"; + std::string patch_stage_in_var_name = "patchIn"; + std::string patch_stage_out_var_name = "patchOut"; + std::string sampler_name_suffix = "Smplr"; + std::string swizzle_name_suffix = "Swzl"; + std::string buffer_size_name_suffix = "BufferSize"; + std::string plane_name_suffix = "Plane"; + std::string input_wg_var_name = "gl_in"; + std::string input_buffer_var_name = "spvIn"; + std::string output_buffer_var_name = "spvOut"; + std::string patch_output_buffer_var_name = "spvPatchOut"; + std::string tess_factor_buffer_var_name = "spvTessLevel"; + std::string index_buffer_var_name = "spvIndices"; + spv::Op previous_instruction_opcode = spv::OpNop; + + // Must be ordered since declaration is in a specific order. + std::map constexpr_samplers_by_id; + std::unordered_map constexpr_samplers_by_binding; + const MSLConstexprSampler *find_constexpr_sampler(uint32_t id) const; + + std::unordered_set buffers_requiring_array_length; + SmallVector buffer_arrays; + std::unordered_set atomic_image_vars; // Emulate texture2D atomic operations + std::unordered_set pull_model_inputs; + + // Must be ordered since array is in a specific order. + std::map> buffers_requiring_dynamic_offset; + + SmallVector disabled_frag_outputs; + + std::unordered_set inline_uniform_blocks; + + uint32_t argument_buffer_ids[kMaxArgumentBuffers]; + uint32_t argument_buffer_discrete_mask = 0; + uint32_t argument_buffer_device_storage_mask = 0; + + void analyze_argument_buffers(); + bool descriptor_set_is_argument_buffer(uint32_t desc_set) const; + + uint32_t get_target_components_for_fragment_location(uint32_t location) const; + uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components, + SPIRType::BaseType basetype = SPIRType::Unknown); + uint32_t build_msl_interpolant_type(uint32_t type_id, bool is_noperspective); + + bool suppress_missing_prototypes = false; + + void add_spv_func_and_recompile(SPVFuncImpl spv_func); + + void activate_argument_buffer_resources(); + + bool type_is_msl_framebuffer_fetch(const SPIRType &type) const; + + // OpcodeHandler that handles several MSL preprocessing operations. + struct OpCodePreprocessor : OpcodeHandler + { + OpCodePreprocessor(CompilerMSL &compiler_) + : compiler(compiler_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + CompilerMSL::SPVFuncImpl get_spv_func_impl(spv::Op opcode, const uint32_t *args); + void check_resource_write(uint32_t var_id); + + CompilerMSL &compiler; + std::unordered_map result_types; + std::unordered_map image_pointers; // Emulate texture2D atomic operations + bool suppress_missing_prototypes = false; + bool uses_atomics = false; + bool uses_resource_write = false; + bool needs_subgroup_invocation_id = false; + bool needs_subgroup_size = false; + bool needs_sample_id = false; + }; + + // OpcodeHandler that scans for uses of sampled images + struct SampledImageScanner : OpcodeHandler + { + SampledImageScanner(CompilerMSL &compiler_) + : compiler(compiler_) + { + } + + bool handle(spv::Op opcode, const uint32_t *args, uint32_t) override; + + CompilerMSL &compiler; + }; + + // Sorts the members of a SPIRType and associated Meta info based on a settable sorting + // aspect, which defines which aspect of the struct members will be used to sort them. + // Regardless of the sorting aspect, built-in members always appear at the end of the struct. + struct MemberSorter + { + enum SortAspect + { + Location, + LocationReverse, + Offset, + OffsetThenLocationReverse, + Alphabetical + }; + + void sort(); + bool operator()(uint32_t mbr_idx1, uint32_t mbr_idx2); + MemberSorter(SPIRType &t, Meta &m, SortAspect sa); + + SPIRType &type; + Meta &meta; + SortAspect sort_aspect; + }; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_parser.cpp b/third_party/spirv-cross/spirv_parser.cpp new file mode 100644 index 0000000..92db528 --- /dev/null +++ b/third_party/spirv-cross/spirv_parser.cpp @@ -0,0 +1,1176 @@ +/* + * Copyright 2018-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_parser.hpp" +#include + +using namespace std; +using namespace spv; + +namespace SPIRV_CROSS_NAMESPACE +{ +Parser::Parser(vector spirv) +{ + ir.spirv = move(spirv); +} + +Parser::Parser(const uint32_t *spirv_data, size_t word_count) +{ + ir.spirv = vector(spirv_data, spirv_data + word_count); +} + +static bool decoration_is_string(Decoration decoration) +{ + switch (decoration) + { + case DecorationHlslSemanticGOOGLE: + return true; + + default: + return false; + } +} + +static inline uint32_t swap_endian(uint32_t v) +{ + return ((v >> 24) & 0x000000ffu) | ((v >> 8) & 0x0000ff00u) | ((v << 8) & 0x00ff0000u) | ((v << 24) & 0xff000000u); +} + +static bool is_valid_spirv_version(uint32_t version) +{ + switch (version) + { + // Allow v99 since it tends to just work. + case 99: + case 0x10000: // SPIR-V 1.0 + case 0x10100: // SPIR-V 1.1 + case 0x10200: // SPIR-V 1.2 + case 0x10300: // SPIR-V 1.3 + case 0x10400: // SPIR-V 1.4 + case 0x10500: // SPIR-V 1.5 + return true; + + default: + return false; + } +} + +void Parser::parse() +{ + auto &spirv = ir.spirv; + + auto len = spirv.size(); + if (len < 5) + SPIRV_CROSS_THROW("SPIRV file too small."); + + auto s = spirv.data(); + + // Endian-swap if we need to. + if (s[0] == swap_endian(MagicNumber)) + transform(begin(spirv), end(spirv), begin(spirv), [](uint32_t c) { return swap_endian(c); }); + + if (s[0] != MagicNumber || !is_valid_spirv_version(s[1])) + SPIRV_CROSS_THROW("Invalid SPIRV format."); + + uint32_t bound = s[3]; + + const uint32_t MaximumNumberOfIDs = 0x3fffff; + if (bound > MaximumNumberOfIDs) + SPIRV_CROSS_THROW("ID bound exceeds limit of 0x3fffff.\n"); + + ir.set_id_bounds(bound); + + uint32_t offset = 5; + + SmallVector instructions; + while (offset < len) + { + Instruction instr = {}; + instr.op = spirv[offset] & 0xffff; + instr.count = (spirv[offset] >> 16) & 0xffff; + + if (instr.count == 0) + SPIRV_CROSS_THROW("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file."); + + instr.offset = offset + 1; + instr.length = instr.count - 1; + + offset += instr.count; + + if (offset > spirv.size()) + SPIRV_CROSS_THROW("SPIR-V instruction goes out of bounds."); + + instructions.push_back(instr); + } + + for (auto &i : instructions) + parse(i); + + for (auto &fixup : forward_pointer_fixups) + { + auto &target = get(fixup.first); + auto &source = get(fixup.second); + target.member_types = source.member_types; + target.basetype = source.basetype; + target.self = source.self; + } + forward_pointer_fixups.clear(); + + if (current_function) + SPIRV_CROSS_THROW("Function was not terminated."); + if (current_block) + SPIRV_CROSS_THROW("Block was not terminated."); +} + +const uint32_t *Parser::stream(const Instruction &instr) const +{ + // If we're not going to use any arguments, just return nullptr. + // We want to avoid case where we return an out of range pointer + // that trips debug assertions on some platforms. + if (!instr.length) + return nullptr; + + if (instr.offset + instr.length > ir.spirv.size()) + SPIRV_CROSS_THROW("Compiler::stream() out of range."); + return &ir.spirv[instr.offset]; +} + +static string extract_string(const vector &spirv, uint32_t offset) +{ + string ret; + for (uint32_t i = offset; i < spirv.size(); i++) + { + uint32_t w = spirv[i]; + + for (uint32_t j = 0; j < 4; j++, w >>= 8) + { + char c = w & 0xff; + if (c == '\0') + return ret; + ret += c; + } + } + + SPIRV_CROSS_THROW("String was not terminated before EOF"); +} + +void Parser::parse(const Instruction &instruction) +{ + auto *ops = stream(instruction); + auto op = static_cast(instruction.op); + uint32_t length = instruction.length; + + switch (op) + { + case OpSourceContinued: + case OpSourceExtension: + case OpNop: + case OpModuleProcessed: + break; + + case OpString: + { + set(ops[0], extract_string(ir.spirv, instruction.offset + 1)); + break; + } + + case OpMemoryModel: + ir.addressing_model = static_cast(ops[0]); + ir.memory_model = static_cast(ops[1]); + break; + + case OpSource: + { + auto lang = static_cast(ops[0]); + switch (lang) + { + case SourceLanguageESSL: + ir.source.es = true; + ir.source.version = ops[1]; + ir.source.known = true; + ir.source.hlsl = false; + break; + + case SourceLanguageGLSL: + ir.source.es = false; + ir.source.version = ops[1]; + ir.source.known = true; + ir.source.hlsl = false; + break; + + case SourceLanguageHLSL: + // For purposes of cross-compiling, this is GLSL 450. + ir.source.es = false; + ir.source.version = 450; + ir.source.known = true; + ir.source.hlsl = true; + break; + + default: + ir.source.known = false; + break; + } + break; + } + + case OpUndef: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + set(id, result_type); + if (current_block) + current_block->ops.push_back(instruction); + break; + } + + case OpCapability: + { + uint32_t cap = ops[0]; + if (cap == CapabilityKernel) + SPIRV_CROSS_THROW("Kernel capability not supported."); + + ir.declared_capabilities.push_back(static_cast(ops[0])); + break; + } + + case OpExtension: + { + auto ext = extract_string(ir.spirv, instruction.offset); + ir.declared_extensions.push_back(move(ext)); + break; + } + + case OpExtInstImport: + { + uint32_t id = ops[0]; + auto ext = extract_string(ir.spirv, instruction.offset + 1); + if (ext == "GLSL.std.450") + set(id, SPIRExtension::GLSL); + else if (ext == "DebugInfo") + set(id, SPIRExtension::SPV_debug_info); + else if (ext == "SPV_AMD_shader_ballot") + set(id, SPIRExtension::SPV_AMD_shader_ballot); + else if (ext == "SPV_AMD_shader_explicit_vertex_parameter") + set(id, SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter); + else if (ext == "SPV_AMD_shader_trinary_minmax") + set(id, SPIRExtension::SPV_AMD_shader_trinary_minmax); + else if (ext == "SPV_AMD_gcn_shader") + set(id, SPIRExtension::SPV_AMD_gcn_shader); + else + set(id, SPIRExtension::Unsupported); + + // Other SPIR-V extensions which have ExtInstrs are currently not supported. + + break; + } + + case OpExtInst: + { + // The SPIR-V debug information extended instructions might come at global scope. + if (current_block) + current_block->ops.push_back(instruction); + break; + } + + case OpEntryPoint: + { + auto itr = + ir.entry_points.insert(make_pair(ops[1], SPIREntryPoint(ops[1], static_cast(ops[0]), + extract_string(ir.spirv, instruction.offset + 2)))); + auto &e = itr.first->second; + + // Strings need nul-terminator and consume the whole word. + uint32_t strlen_words = uint32_t((e.name.size() + 1 + 3) >> 2); + + for (uint32_t i = strlen_words + 2; i < instruction.length; i++) + e.interface_variables.push_back(ops[i]); + + // Set the name of the entry point in case OpName is not provided later. + ir.set_name(ops[1], e.name); + + // If we don't have an entry, make the first one our "default". + if (!ir.default_entry_point) + ir.default_entry_point = ops[1]; + break; + } + + case OpExecutionMode: + { + auto &execution = ir.entry_points[ops[0]]; + auto mode = static_cast(ops[1]); + execution.flags.set(mode); + + switch (mode) + { + case ExecutionModeInvocations: + execution.invocations = ops[2]; + break; + + case ExecutionModeLocalSize: + execution.workgroup_size.x = ops[2]; + execution.workgroup_size.y = ops[3]; + execution.workgroup_size.z = ops[4]; + break; + + case ExecutionModeOutputVertices: + execution.output_vertices = ops[2]; + break; + + default: + break; + } + break; + } + + case OpName: + { + uint32_t id = ops[0]; + ir.set_name(id, extract_string(ir.spirv, instruction.offset + 1)); + break; + } + + case OpMemberName: + { + uint32_t id = ops[0]; + uint32_t member = ops[1]; + ir.set_member_name(id, member, extract_string(ir.spirv, instruction.offset + 2)); + break; + } + + case OpDecorationGroup: + { + // Noop, this simply means an ID should be a collector of decorations. + // The meta array is already a flat array of decorations which will contain the relevant decorations. + break; + } + + case OpGroupDecorate: + { + uint32_t group_id = ops[0]; + auto &decorations = ir.meta[group_id].decoration; + auto &flags = decorations.decoration_flags; + + // Copies decorations from one ID to another. Only copy decorations which are set in the group, + // i.e., we cannot just copy the meta structure directly. + for (uint32_t i = 1; i < length; i++) + { + uint32_t target = ops[i]; + flags.for_each_bit([&](uint32_t bit) { + auto decoration = static_cast(bit); + + if (decoration_is_string(decoration)) + { + ir.set_decoration_string(target, decoration, ir.get_decoration_string(group_id, decoration)); + } + else + { + ir.meta[target].decoration_word_offset[decoration] = + ir.meta[group_id].decoration_word_offset[decoration]; + ir.set_decoration(target, decoration, ir.get_decoration(group_id, decoration)); + } + }); + } + break; + } + + case OpGroupMemberDecorate: + { + uint32_t group_id = ops[0]; + auto &flags = ir.meta[group_id].decoration.decoration_flags; + + // Copies decorations from one ID to another. Only copy decorations which are set in the group, + // i.e., we cannot just copy the meta structure directly. + for (uint32_t i = 1; i + 1 < length; i += 2) + { + uint32_t target = ops[i + 0]; + uint32_t index = ops[i + 1]; + flags.for_each_bit([&](uint32_t bit) { + auto decoration = static_cast(bit); + + if (decoration_is_string(decoration)) + ir.set_member_decoration_string(target, index, decoration, + ir.get_decoration_string(group_id, decoration)); + else + ir.set_member_decoration(target, index, decoration, ir.get_decoration(group_id, decoration)); + }); + } + break; + } + + case OpDecorate: + case OpDecorateId: + { + // OpDecorateId technically supports an array of arguments, but our only supported decorations are single uint, + // so merge decorate and decorate-id here. + uint32_t id = ops[0]; + + auto decoration = static_cast(ops[1]); + if (length >= 3) + { + ir.meta[id].decoration_word_offset[decoration] = uint32_t(&ops[2] - ir.spirv.data()); + ir.set_decoration(id, decoration, ops[2]); + } + else + ir.set_decoration(id, decoration); + + break; + } + + case OpDecorateStringGOOGLE: + { + uint32_t id = ops[0]; + auto decoration = static_cast(ops[1]); + ir.set_decoration_string(id, decoration, extract_string(ir.spirv, instruction.offset + 2)); + break; + } + + case OpMemberDecorate: + { + uint32_t id = ops[0]; + uint32_t member = ops[1]; + auto decoration = static_cast(ops[2]); + if (length >= 4) + ir.set_member_decoration(id, member, decoration, ops[3]); + else + ir.set_member_decoration(id, member, decoration); + break; + } + + case OpMemberDecorateStringGOOGLE: + { + uint32_t id = ops[0]; + uint32_t member = ops[1]; + auto decoration = static_cast(ops[2]); + ir.set_member_decoration_string(id, member, decoration, extract_string(ir.spirv, instruction.offset + 3)); + break; + } + + // Build up basic types. + case OpTypeVoid: + { + uint32_t id = ops[0]; + auto &type = set(id); + type.basetype = SPIRType::Void; + break; + } + + case OpTypeBool: + { + uint32_t id = ops[0]; + auto &type = set(id); + type.basetype = SPIRType::Boolean; + type.width = 1; + break; + } + + case OpTypeFloat: + { + uint32_t id = ops[0]; + uint32_t width = ops[1]; + auto &type = set(id); + if (width == 64) + type.basetype = SPIRType::Double; + else if (width == 32) + type.basetype = SPIRType::Float; + else if (width == 16) + type.basetype = SPIRType::Half; + else + SPIRV_CROSS_THROW("Unrecognized bit-width of floating point type."); + type.width = width; + break; + } + + case OpTypeInt: + { + uint32_t id = ops[0]; + uint32_t width = ops[1]; + bool signedness = ops[2] != 0; + auto &type = set(id); + type.basetype = signedness ? to_signed_basetype(width) : to_unsigned_basetype(width); + type.width = width; + break; + } + + // Build composite types by "inheriting". + // NOTE: The self member is also copied! For pointers and array modifiers this is a good thing + // since we can refer to decorations on pointee classes which is needed for UBO/SSBO, I/O blocks in geometry/tess etc. + case OpTypeVector: + { + uint32_t id = ops[0]; + uint32_t vecsize = ops[2]; + + auto &base = get(ops[1]); + auto &vecbase = set(id); + + vecbase = base; + vecbase.vecsize = vecsize; + vecbase.self = id; + vecbase.parent_type = ops[1]; + break; + } + + case OpTypeMatrix: + { + uint32_t id = ops[0]; + uint32_t colcount = ops[2]; + + auto &base = get(ops[1]); + auto &matrixbase = set(id); + + matrixbase = base; + matrixbase.columns = colcount; + matrixbase.self = id; + matrixbase.parent_type = ops[1]; + break; + } + + case OpTypeArray: + { + uint32_t id = ops[0]; + auto &arraybase = set(id); + + uint32_t tid = ops[1]; + auto &base = get(tid); + + arraybase = base; + arraybase.parent_type = tid; + + uint32_t cid = ops[2]; + ir.mark_used_as_array_length(cid); + auto *c = maybe_get(cid); + bool literal = c && !c->specialization; + + // We're copying type information into Array types, so we'll need a fixup for any physical pointer + // references. + if (base.forward_pointer) + forward_pointer_fixups.push_back({ id, tid }); + + arraybase.array_size_literal.push_back(literal); + arraybase.array.push_back(literal ? c->scalar() : cid); + // Do NOT set arraybase.self! + break; + } + + case OpTypeRuntimeArray: + { + uint32_t id = ops[0]; + + auto &base = get(ops[1]); + auto &arraybase = set(id); + + // We're copying type information into Array types, so we'll need a fixup for any physical pointer + // references. + if (base.forward_pointer) + forward_pointer_fixups.push_back({ id, ops[1] }); + + arraybase = base; + arraybase.array.push_back(0); + arraybase.array_size_literal.push_back(true); + arraybase.parent_type = ops[1]; + // Do NOT set arraybase.self! + break; + } + + case OpTypeImage: + { + uint32_t id = ops[0]; + auto &type = set(id); + type.basetype = SPIRType::Image; + type.image.type = ops[1]; + type.image.dim = static_cast(ops[2]); + type.image.depth = ops[3] == 1; + type.image.arrayed = ops[4] != 0; + type.image.ms = ops[5] != 0; + type.image.sampled = ops[6]; + type.image.format = static_cast(ops[7]); + type.image.access = (length >= 9) ? static_cast(ops[8]) : AccessQualifierMax; + break; + } + + case OpTypeSampledImage: + { + uint32_t id = ops[0]; + uint32_t imagetype = ops[1]; + auto &type = set(id); + type = get(imagetype); + type.basetype = SPIRType::SampledImage; + type.self = id; + break; + } + + case OpTypeSampler: + { + uint32_t id = ops[0]; + auto &type = set(id); + type.basetype = SPIRType::Sampler; + break; + } + + case OpTypePointer: + { + uint32_t id = ops[0]; + + // Very rarely, we might receive a FunctionPrototype here. + // We won't be able to compile it, but we shouldn't crash when parsing. + // We should be able to reflect. + auto *base = maybe_get(ops[2]); + auto &ptrbase = set(id); + + if (base) + ptrbase = *base; + + ptrbase.pointer = true; + ptrbase.pointer_depth++; + ptrbase.storage = static_cast(ops[1]); + + if (ptrbase.storage == StorageClassAtomicCounter) + ptrbase.basetype = SPIRType::AtomicCounter; + + if (base && base->forward_pointer) + forward_pointer_fixups.push_back({ id, ops[2] }); + + ptrbase.parent_type = ops[2]; + + // Do NOT set ptrbase.self! + break; + } + + case OpTypeForwardPointer: + { + uint32_t id = ops[0]; + auto &ptrbase = set(id); + ptrbase.pointer = true; + ptrbase.pointer_depth++; + ptrbase.storage = static_cast(ops[1]); + ptrbase.forward_pointer = true; + + if (ptrbase.storage == StorageClassAtomicCounter) + ptrbase.basetype = SPIRType::AtomicCounter; + + break; + } + + case OpTypeStruct: + { + uint32_t id = ops[0]; + auto &type = set(id); + type.basetype = SPIRType::Struct; + for (uint32_t i = 1; i < length; i++) + type.member_types.push_back(ops[i]); + + // Check if we have seen this struct type before, with just different + // decorations. + // + // Add workaround for issue #17 as well by looking at OpName for the struct + // types, which we shouldn't normally do. + // We should not normally have to consider type aliases like this to begin with + // however ... glslang issues #304, #307 cover this. + + // For stripped names, never consider struct type aliasing. + // We risk declaring the same struct multiple times, but type-punning is not allowed + // so this is safe. + bool consider_aliasing = !ir.get_name(type.self).empty(); + if (consider_aliasing) + { + for (auto &other : global_struct_cache) + { + if (ir.get_name(type.self) == ir.get_name(other) && + types_are_logically_equivalent(type, get(other))) + { + type.type_alias = other; + break; + } + } + + if (type.type_alias == TypeID(0)) + global_struct_cache.push_back(id); + } + break; + } + + case OpTypeFunction: + { + uint32_t id = ops[0]; + uint32_t ret = ops[1]; + + auto &func = set(id, ret); + for (uint32_t i = 2; i < length; i++) + func.parameter_types.push_back(ops[i]); + break; + } + + case OpTypeAccelerationStructureKHR: + { + uint32_t id = ops[0]; + auto &type = set(id); + type.basetype = SPIRType::AccelerationStructure; + break; + } + + case OpTypeRayQueryProvisionalKHR: + { + uint32_t id = ops[0]; + auto &type = set(id); + type.basetype = SPIRType::RayQuery; + break; + } + + // Variable declaration + // All variables are essentially pointers with a storage qualifier. + case OpVariable: + { + uint32_t type = ops[0]; + uint32_t id = ops[1]; + auto storage = static_cast(ops[2]); + uint32_t initializer = length == 4 ? ops[3] : 0; + + if (storage == StorageClassFunction) + { + if (!current_function) + SPIRV_CROSS_THROW("No function currently in scope"); + current_function->add_local_variable(id); + } + + set(id, type, storage, initializer); + break; + } + + // OpPhi + // OpPhi is a fairly magical opcode. + // It selects temporary variables based on which parent block we *came from*. + // In high-level languages we can "de-SSA" by creating a function local, and flush out temporaries to this function-local + // variable to emulate SSA Phi. + case OpPhi: + { + if (!current_function) + SPIRV_CROSS_THROW("No function currently in scope"); + if (!current_block) + SPIRV_CROSS_THROW("No block currently in scope"); + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + // Instead of a temporary, create a new function-wide temporary with this ID instead. + auto &var = set(id, result_type, spv::StorageClassFunction); + var.phi_variable = true; + + current_function->add_local_variable(id); + + for (uint32_t i = 2; i + 2 <= length; i += 2) + current_block->phi_variables.push_back({ ops[i], ops[i + 1], id }); + break; + } + + // Constants + case OpSpecConstant: + case OpConstant: + { + uint32_t id = ops[1]; + auto &type = get(ops[0]); + + if (type.width > 32) + set(id, ops[0], ops[2] | (uint64_t(ops[3]) << 32), op == OpSpecConstant); + else + set(id, ops[0], ops[2], op == OpSpecConstant); + break; + } + + case OpSpecConstantFalse: + case OpConstantFalse: + { + uint32_t id = ops[1]; + set(id, ops[0], uint32_t(0), op == OpSpecConstantFalse); + break; + } + + case OpSpecConstantTrue: + case OpConstantTrue: + { + uint32_t id = ops[1]; + set(id, ops[0], uint32_t(1), op == OpSpecConstantTrue); + break; + } + + case OpConstantNull: + { + uint32_t id = ops[1]; + uint32_t type = ops[0]; + ir.make_constant_null(id, type, true); + break; + } + + case OpSpecConstantComposite: + case OpConstantComposite: + { + uint32_t id = ops[1]; + uint32_t type = ops[0]; + + auto &ctype = get(type); + + // We can have constants which are structs and arrays. + // In this case, our SPIRConstant will be a list of other SPIRConstant ids which we + // can refer to. + if (ctype.basetype == SPIRType::Struct || !ctype.array.empty()) + { + set(id, type, ops + 2, length - 2, op == OpSpecConstantComposite); + } + else + { + uint32_t elements = length - 2; + if (elements > 4) + SPIRV_CROSS_THROW("OpConstantComposite only supports 1, 2, 3 and 4 elements."); + + SPIRConstant remapped_constant_ops[4]; + const SPIRConstant *c[4]; + for (uint32_t i = 0; i < elements; i++) + { + // Specialization constants operations can also be part of this. + // We do not know their value, so any attempt to query SPIRConstant later + // will fail. We can only propagate the ID of the expression and use to_expression on it. + auto *constant_op = maybe_get(ops[2 + i]); + auto *undef_op = maybe_get(ops[2 + i]); + if (constant_op) + { + if (op == OpConstantComposite) + SPIRV_CROSS_THROW("Specialization constant operation used in OpConstantComposite."); + + remapped_constant_ops[i].make_null(get(constant_op->basetype)); + remapped_constant_ops[i].self = constant_op->self; + remapped_constant_ops[i].constant_type = constant_op->basetype; + remapped_constant_ops[i].specialization = true; + c[i] = &remapped_constant_ops[i]; + } + else if (undef_op) + { + // Undefined, just pick 0. + remapped_constant_ops[i].make_null(get(undef_op->basetype)); + remapped_constant_ops[i].constant_type = undef_op->basetype; + c[i] = &remapped_constant_ops[i]; + } + else + c[i] = &get(ops[2 + i]); + } + set(id, type, c, elements, op == OpSpecConstantComposite); + } + break; + } + + // Functions + case OpFunction: + { + uint32_t res = ops[0]; + uint32_t id = ops[1]; + // Control + uint32_t type = ops[3]; + + if (current_function) + SPIRV_CROSS_THROW("Must end a function before starting a new one!"); + + current_function = &set(id, res, type); + break; + } + + case OpFunctionParameter: + { + uint32_t type = ops[0]; + uint32_t id = ops[1]; + + if (!current_function) + SPIRV_CROSS_THROW("Must be in a function!"); + + current_function->add_parameter(type, id); + set(id, type, StorageClassFunction); + break; + } + + case OpFunctionEnd: + { + if (current_block) + { + // Very specific error message, but seems to come up quite often. + SPIRV_CROSS_THROW( + "Cannot end a function before ending the current block.\n" + "Likely cause: If this SPIR-V was created from glslang HLSL, make sure the entry point is valid."); + } + current_function = nullptr; + break; + } + + // Blocks + case OpLabel: + { + // OpLabel always starts a block. + if (!current_function) + SPIRV_CROSS_THROW("Blocks cannot exist outside functions!"); + + uint32_t id = ops[0]; + + current_function->blocks.push_back(id); + if (!current_function->entry_block) + current_function->entry_block = id; + + if (current_block) + SPIRV_CROSS_THROW("Cannot start a block before ending the current block."); + + current_block = &set(id); + break; + } + + // Branch instructions end blocks. + case OpBranch: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + + uint32_t target = ops[0]; + current_block->terminator = SPIRBlock::Direct; + current_block->next_block = target; + current_block = nullptr; + break; + } + + case OpBranchConditional: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + + current_block->condition = ops[0]; + current_block->true_block = ops[1]; + current_block->false_block = ops[2]; + + current_block->terminator = SPIRBlock::Select; + current_block = nullptr; + break; + } + + case OpSwitch: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + + current_block->terminator = SPIRBlock::MultiSelect; + + current_block->condition = ops[0]; + current_block->default_block = ops[1]; + + for (uint32_t i = 2; i + 2 <= length; i += 2) + current_block->cases.push_back({ ops[i], ops[i + 1] }); + + // If we jump to next block, make it break instead since we're inside a switch case block at that point. + ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT; + + current_block = nullptr; + break; + } + + case OpKill: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::Kill; + current_block = nullptr; + break; + } + + case OpReturn: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::Return; + current_block = nullptr; + break; + } + + case OpReturnValue: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::Return; + current_block->return_value = ops[0]; + current_block = nullptr; + break; + } + + case OpUnreachable: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::Unreachable; + current_block = nullptr; + break; + } + + case OpSelectionMerge: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to modify a non-existing block."); + + current_block->next_block = ops[0]; + current_block->merge = SPIRBlock::MergeSelection; + ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_SELECTION_MERGE_BIT; + + if (length >= 2) + { + if (ops[1] & SelectionControlFlattenMask) + current_block->hint = SPIRBlock::HintFlatten; + else if (ops[1] & SelectionControlDontFlattenMask) + current_block->hint = SPIRBlock::HintDontFlatten; + } + break; + } + + case OpLoopMerge: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to modify a non-existing block."); + + current_block->merge_block = ops[0]; + current_block->continue_block = ops[1]; + current_block->merge = SPIRBlock::MergeLoop; + + ir.block_meta[current_block->self] |= ParsedIR::BLOCK_META_LOOP_HEADER_BIT; + ir.block_meta[current_block->merge_block] |= ParsedIR::BLOCK_META_LOOP_MERGE_BIT; + + ir.continue_block_to_loop_header[current_block->continue_block] = BlockID(current_block->self); + + // Don't add loop headers to continue blocks, + // which would make it impossible branch into the loop header since + // they are treated as continues. + if (current_block->continue_block != BlockID(current_block->self)) + ir.block_meta[current_block->continue_block] |= ParsedIR::BLOCK_META_CONTINUE_BIT; + + if (length >= 3) + { + if (ops[2] & LoopControlUnrollMask) + current_block->hint = SPIRBlock::HintUnroll; + else if (ops[2] & LoopControlDontUnrollMask) + current_block->hint = SPIRBlock::HintDontUnroll; + } + break; + } + + case OpSpecConstantOp: + { + if (length < 3) + SPIRV_CROSS_THROW("OpSpecConstantOp not enough arguments."); + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto spec_op = static_cast(ops[2]); + + set(id, result_type, spec_op, ops + 3, length - 3); + break; + } + + case OpLine: + { + // OpLine might come at global scope, but we don't care about those since they will not be declared in any + // meaningful correct order. + // Ignore all OpLine directives which live outside a function. + if (current_block) + current_block->ops.push_back(instruction); + + // Line directives may arrive before first OpLabel. + // Treat this as the line of the function declaration, + // so warnings for arguments can propagate properly. + if (current_function) + { + // Store the first one we find and emit it before creating the function prototype. + if (current_function->entry_line.file_id == 0) + { + current_function->entry_line.file_id = ops[0]; + current_function->entry_line.line_literal = ops[1]; + } + } + break; + } + + case OpNoLine: + { + // OpNoLine might come at global scope. + if (current_block) + current_block->ops.push_back(instruction); + break; + } + + // Actual opcodes. + default: + { + if (!current_block) + SPIRV_CROSS_THROW("Currently no block to insert opcode."); + + current_block->ops.push_back(instruction); + break; + } + } +} + +bool Parser::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const +{ + if (a.basetype != b.basetype) + return false; + if (a.width != b.width) + return false; + if (a.vecsize != b.vecsize) + return false; + if (a.columns != b.columns) + return false; + if (a.array.size() != b.array.size()) + return false; + + size_t array_count = a.array.size(); + if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0) + return false; + + if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage) + { + if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0) + return false; + } + + if (a.member_types.size() != b.member_types.size()) + return false; + + size_t member_types = a.member_types.size(); + for (size_t i = 0; i < member_types; i++) + { + if (!types_are_logically_equivalent(get(a.member_types[i]), get(b.member_types[i]))) + return false; + } + + return true; +} + +bool Parser::variable_storage_is_aliased(const SPIRVariable &v) const +{ + auto &type = get(v.basetype); + + auto *type_meta = ir.find_meta(type.self); + + bool ssbo = v.storage == StorageClassStorageBuffer || + (type_meta && type_meta->decoration.decoration_flags.get(DecorationBufferBlock)); + bool image = type.basetype == SPIRType::Image; + bool counter = type.basetype == SPIRType::AtomicCounter; + + bool is_restrict; + if (ssbo) + is_restrict = ir.get_buffer_block_flags(v).get(DecorationRestrict); + else + is_restrict = ir.has_decoration(v.self, DecorationRestrict); + + return !is_restrict && (ssbo || image || counter); +} +} // namespace SPIRV_CROSS_NAMESPACE diff --git a/third_party/spirv-cross/spirv_parser.hpp b/third_party/spirv-cross/spirv_parser.hpp new file mode 100644 index 0000000..7b3edde --- /dev/null +++ b/third_party/spirv-cross/spirv_parser.hpp @@ -0,0 +1,94 @@ +/* + * Copyright 2018-2020 Arm Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_PARSER_HPP +#define SPIRV_CROSS_PARSER_HPP + +#include "spirv_cross_parsed_ir.hpp" +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +class Parser +{ +public: + Parser(const uint32_t *spirv_data, size_t word_count); + Parser(std::vector spirv); + + void parse(); + + ParsedIR &get_parsed_ir() + { + return ir; + } + +private: + ParsedIR ir; + SPIRFunction *current_function = nullptr; + SPIRBlock *current_block = nullptr; + + void parse(const Instruction &instr); + const uint32_t *stream(const Instruction &instr) const; + + template + T &set(uint32_t id, P &&... args) + { + ir.add_typed_id(static_cast(T::type), id); + auto &var = variant_set(ir.ids[id], std::forward

. The section is to hold the names for IDs to +// keep them unchanged before and after assembling. All defined IDs to be added +// to this code builder will be assigned with a global name through OpName +// instruction. The name is extracted from the definition instruction. +// E.g. adding instruction: %var_a = OpConstant %int 2, will also add an +// instruction: OpName %var_a, "var_a". +// +// Note that the name must not be used on more than one defined IDs and +// friendly-name disassembling must be enabled so that OpName instructions will +// be respected. +class AssemblyBuilder { + // The base ID value for spec constants. + static const uint32_t SPEC_ID_BASE = 200; + + public: + // Initalize a minimal SPIR-V assembly code as the template. The minimal + // module contains an empty main function and some predefined names for the + // main function. + AssemblyBuilder() + : spec_id_counter_(SPEC_ID_BASE), + global_preamble_({ + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + // clang-format on + }), + names_(), + annotations_(), + types_consts_globals_(), + main_func_(), + main_func_postamble_({ + "OpReturn", + "OpFunctionEnd", + }) { + AppendTypesConstantsGlobals({ + "%void = OpTypeVoid", + "%main_func_type = OpTypeFunction %void", + }); + AppendInMain({ + "%main = OpFunction %void None %main_func_type", + "%main_func_entry_block = OpLabel", + }); + } + + // Appends OpName instructions to this builder. Instrcution strings that do + // not start with 'OpName ' will be skipped. Returns the references of this + // assembly builder. + AssemblyBuilder& AppendNames(const std::vector& vec_asm_code) { + for (auto& inst_str : vec_asm_code) { + if (inst_str.find("OpName ") == 0) { + names_.push_back(inst_str); + } + } + return *this; + } + + // Appends instructions to the types-constants-globals section and returns + // the reference of this assembly builder. IDs defined in the given code will + // be added to the Names section and then be registered with OpName + // instruction. Corresponding decoration instruction will be added for spec + // constants defined with opcode: 'OpSpecConstant'. + AssemblyBuilder& AppendTypesConstantsGlobals( + const std::vector& vec_asm_code) { + AddNamesForResultIDsIn(vec_asm_code); + // Check spec constants defined with OpSpecConstant. + for (auto& inst_str : vec_asm_code) { + if (inst_str.find("= OpSpecConstant ") != std::string::npos || + inst_str.find("= OpSpecConstantTrue ") != std::string::npos || + inst_str.find("= OpSpecConstantFalse ") != std::string::npos) { + AddSpecIDFor(GetResultIDName(inst_str)); + } + } + types_consts_globals_.insert(types_consts_globals_.end(), + vec_asm_code.begin(), vec_asm_code.end()); + return *this; + } + + // Appends instructions to the main function block, which is already labelled + // with "main_func_entry_block". Returns the reference of this assembly + // builder. IDs defined in the given code will be added to the Names section + // and then be registered with OpName instruction. + AssemblyBuilder& AppendInMain(const std::vector& vec_asm_code) { + AddNamesForResultIDsIn(vec_asm_code); + main_func_.insert(main_func_.end(), vec_asm_code.begin(), + vec_asm_code.end()); + return *this; + } + + // Appends annotation instructions to the annotation section, and returns the + // reference of this assembly builder. + AssemblyBuilder& AppendAnnotations( + const std::vector& vec_annotations) { + annotations_.insert(annotations_.end(), vec_annotations.begin(), + vec_annotations.end()); + return *this; + } + + // Pre-pends string to the preamble of the module. Useful for EFFCEE checks. + AssemblyBuilder& PrependPreamble(const std::vector& preamble) { + preamble_.insert(preamble_.end(), preamble.begin(), preamble.end()); + return *this; + } + + // Get the SPIR-V assembly code as string. + std::string GetCode() const { + std::ostringstream ss; + for (const auto& line : preamble_) { + ss << line << std::endl; + } + for (const auto& line : global_preamble_) { + ss << line << std::endl; + } + for (const auto& line : names_) { + ss << line << std::endl; + } + for (const auto& line : annotations_) { + ss << line << std::endl; + } + for (const auto& line : types_consts_globals_) { + ss << line << std::endl; + } + for (const auto& line : main_func_) { + ss << line << std::endl; + } + for (const auto& line : main_func_postamble_) { + ss << line << std::endl; + } + return ss.str(); + } + + private: + // Adds a given name to the Name section with OpName. If the given name has + // been added before, does nothing. + void AddOpNameIfNotExist(const std::string& id_name) { + if (!used_names_.count(id_name)) { + std::stringstream opname_inst; + opname_inst << "OpName " + << "%" << id_name << " \"" << id_name << "\""; + names_.emplace_back(opname_inst.str()); + used_names_.insert(id_name); + } + } + + // Adds the names in a vector of assembly code strings to the Names section. + // If a '=' sign is found in an instruction, this instruction will be treated + // as an ID defining instruction. The ID name used in the instruction will be + // extracted and added to the Names section. + void AddNamesForResultIDsIn(const std::vector& vec_asm_code) { + for (const auto& line : vec_asm_code) { + std::string name = GetResultIDName(line); + if (!name.empty()) { + AddOpNameIfNotExist(name); + } + } + } + + // Adds an OpDecorate SpecId instruction for the given ID name. + void AddSpecIDFor(const std::string& id_name) { + std::stringstream decorate_inst; + decorate_inst << "OpDecorate " + << "%" << id_name << " SpecId " << spec_id_counter_; + spec_id_counter_ += 1; + annotations_.emplace_back(decorate_inst.str()); + } + + // Extracts the ID name from a SPIR-V assembly instruction string. If the + // instruction is an ID-defining instruction (has result ID), returns the + // name of the result ID in string. If the instruction does not have result + // ID, returns an empty string. + std::string GetResultIDName(const std::string inst_str) { + std::string name; + if (inst_str.find('=') != std::string::npos) { + size_t assign_sign = inst_str.find('='); + name = inst_str.substr(0, assign_sign); + name.erase(remove_if(name.begin(), name.end(), + [](char c) { return c == ' ' || c == '%'; }), + name.end()); + } + return name; + } + + uint32_t spec_id_counter_; + // User-defined preamble. + std::vector preamble_; + // The vector that contains common preambles shared across all test SPIR-V + // code. + std::vector global_preamble_; + // The vector that contains OpName instructions. + std::vector names_; + // The vector that contains annotation instructions. + std::vector annotations_; + // The vector that contains the code to declare types, constants and global + // variables (aka. the Types-Constants-Globals section). + std::vector types_consts_globals_; + // The vector that contains the code in main function's entry block. + std::vector main_func_; + // The vector that contains the postamble of main function body. + std::vector main_func_postamble_; + // All of the defined variable names. + std::unordered_set used_names_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // TEST_OPT_ASSEMBLY_BUILDER_H_ diff --git a/third_party/spirv-tools/test/opt/assembly_builder_test.cpp b/third_party/spirv-tools/test/opt/assembly_builder_test.cpp new file mode 100644 index 0000000..55fbbe9 --- /dev/null +++ b/third_party/spirv-tools/test/opt/assembly_builder_test.cpp @@ -0,0 +1,283 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/opt/assembly_builder.h" + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using AssemblyBuilderTest = PassTest<::testing::Test>; + +TEST_F(AssemblyBuilderTest, MinimalShader) { + AssemblyBuilder builder; + std::vector expected = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %void \"void\"", + "OpName %main_func_type \"main_func_type\"", + "OpName %main \"main\"", + "OpName %main_func_entry_block \"main_func_entry_block\"", + "%void = OpTypeVoid", + "%main_func_type = OpTypeFunction %void", + "%main = OpFunction %void None %main_func_type", +"%main_func_entry_block = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + SinglePassRunAndCheck(builder.GetCode(), JoinAllInsts(expected), + /* skip_nop = */ false); +} + +TEST_F(AssemblyBuilderTest, ShaderWithConstants) { + AssemblyBuilder builder; + builder + .AppendTypesConstantsGlobals({ + // clang-format off + "%bool = OpTypeBool", + "%_PF_bool = OpTypePointer Function %bool", + "%bt = OpConstantTrue %bool", + "%bf = OpConstantFalse %bool", + "%int = OpTypeInt 32 1", + "%_PF_int = OpTypePointer Function %int", + "%si = OpConstant %int 1", + "%uint = OpTypeInt 32 0", + "%_PF_uint = OpTypePointer Function %uint", + "%ui = OpConstant %uint 2", + "%float = OpTypeFloat 32", + "%_PF_float = OpTypePointer Function %float", + "%f = OpConstant %float 3.1415", + "%double = OpTypeFloat 64", + "%_PF_double = OpTypePointer Function %double", + "%d = OpConstant %double 3.14159265358979", + // clang-format on + }) + .AppendInMain({ + // clang-format off + "%btv = OpVariable %_PF_bool Function", + "%bfv = OpVariable %_PF_bool Function", + "%iv = OpVariable %_PF_int Function", + "%uv = OpVariable %_PF_uint Function", + "%fv = OpVariable %_PF_float Function", + "%dv = OpVariable %_PF_double Function", + "OpStore %btv %bt", + "OpStore %bfv %bf", + "OpStore %iv %si", + "OpStore %uv %ui", + "OpStore %fv %f", + "OpStore %dv %d", + // clang-format on + }); + + std::vector expected = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %void \"void\"", + "OpName %main_func_type \"main_func_type\"", + "OpName %main \"main\"", + "OpName %main_func_entry_block \"main_func_entry_block\"", + "OpName %bool \"bool\"", + "OpName %_PF_bool \"_PF_bool\"", + "OpName %bt \"bt\"", + "OpName %bf \"bf\"", + "OpName %int \"int\"", + "OpName %_PF_int \"_PF_int\"", + "OpName %si \"si\"", + "OpName %uint \"uint\"", + "OpName %_PF_uint \"_PF_uint\"", + "OpName %ui \"ui\"", + "OpName %float \"float\"", + "OpName %_PF_float \"_PF_float\"", + "OpName %f \"f\"", + "OpName %double \"double\"", + "OpName %_PF_double \"_PF_double\"", + "OpName %d \"d\"", + "OpName %btv \"btv\"", + "OpName %bfv \"bfv\"", + "OpName %iv \"iv\"", + "OpName %uv \"uv\"", + "OpName %fv \"fv\"", + "OpName %dv \"dv\"", + "%void = OpTypeVoid", +"%main_func_type = OpTypeFunction %void", + "%bool = OpTypeBool", + "%_PF_bool = OpTypePointer Function %bool", + "%bt = OpConstantTrue %bool", + "%bf = OpConstantFalse %bool", + "%int = OpTypeInt 32 1", + "%_PF_int = OpTypePointer Function %int", + "%si = OpConstant %int 1", + "%uint = OpTypeInt 32 0", + "%_PF_uint = OpTypePointer Function %uint", + "%ui = OpConstant %uint 2", + "%float = OpTypeFloat 32", + "%_PF_float = OpTypePointer Function %float", + "%f = OpConstant %float 3.1415", + "%double = OpTypeFloat 64", + "%_PF_double = OpTypePointer Function %double", + "%d = OpConstant %double 3.14159265358979", + "%main = OpFunction %void None %main_func_type", +"%main_func_entry_block = OpLabel", + "%btv = OpVariable %_PF_bool Function", + "%bfv = OpVariable %_PF_bool Function", + "%iv = OpVariable %_PF_int Function", + "%uv = OpVariable %_PF_uint Function", + "%fv = OpVariable %_PF_float Function", + "%dv = OpVariable %_PF_double Function", + "OpStore %btv %bt", + "OpStore %bfv %bf", + "OpStore %iv %si", + "OpStore %uv %ui", + "OpStore %fv %f", + "OpStore %dv %d", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck(builder.GetCode(), JoinAllInsts(expected), + /* skip_nop = */ false); +} + +TEST_F(AssemblyBuilderTest, SpecConstants) { + AssemblyBuilder builder; + builder.AppendTypesConstantsGlobals({ + "%bool = OpTypeBool", + "%uint = OpTypeInt 32 0", + "%int = OpTypeInt 32 1", + "%float = OpTypeFloat 32", + "%double = OpTypeFloat 64", + "%v2int = OpTypeVector %int 2", + + "%spec_true = OpSpecConstantTrue %bool", + "%spec_false = OpSpecConstantFalse %bool", + "%spec_uint = OpSpecConstant %uint 1", + "%spec_int = OpSpecConstant %int 1", + "%spec_float = OpSpecConstant %float 1.25", + "%spec_double = OpSpecConstant %double 1.2345678", + + // Spec constants defined below should not have SpecID. + "%spec_add_op = OpSpecConstantOp %int IAdd %spec_int %spec_int", + "%spec_vec = OpSpecConstantComposite %v2int %spec_int %spec_int", + "%spec_vec_x = OpSpecConstantOp %int CompositeExtract %spec_vec 0", + }); + std::vector expected = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %void \"void\"", + "OpName %main_func_type \"main_func_type\"", + "OpName %main \"main\"", + "OpName %main_func_entry_block \"main_func_entry_block\"", + "OpName %bool \"bool\"", + "OpName %uint \"uint\"", + "OpName %int \"int\"", + "OpName %float \"float\"", + "OpName %double \"double\"", + "OpName %v2int \"v2int\"", + "OpName %spec_true \"spec_true\"", + "OpName %spec_false \"spec_false\"", + "OpName %spec_uint \"spec_uint\"", + "OpName %spec_int \"spec_int\"", + "OpName %spec_float \"spec_float\"", + "OpName %spec_double \"spec_double\"", + "OpName %spec_add_op \"spec_add_op\"", + "OpName %spec_vec \"spec_vec\"", + "OpName %spec_vec_x \"spec_vec_x\"", + "OpDecorate %spec_true SpecId 200", + "OpDecorate %spec_false SpecId 201", + "OpDecorate %spec_uint SpecId 202", + "OpDecorate %spec_int SpecId 203", + "OpDecorate %spec_float SpecId 204", + "OpDecorate %spec_double SpecId 205", + "%void = OpTypeVoid", + "%main_func_type = OpTypeFunction %void", + "%bool = OpTypeBool", + "%uint = OpTypeInt 32 0", + "%int = OpTypeInt 32 1", + "%float = OpTypeFloat 32", + "%double = OpTypeFloat 64", + "%v2int = OpTypeVector %int 2", + "%spec_true = OpSpecConstantTrue %bool", + "%spec_false = OpSpecConstantFalse %bool", + "%spec_uint = OpSpecConstant %uint 1", + "%spec_int = OpSpecConstant %int 1", + "%spec_float = OpSpecConstant %float 1.25", + "%spec_double = OpSpecConstant %double 1.2345678", + "%spec_add_op = OpSpecConstantOp %int IAdd %spec_int %spec_int", + "%spec_vec = OpSpecConstantComposite %v2int %spec_int %spec_int", + "%spec_vec_x = OpSpecConstantOp %int CompositeExtract %spec_vec 0", + "%main = OpFunction %void None %main_func_type", +"%main_func_entry_block = OpLabel", + "OpReturn", + "OpFunctionEnd", + + // clang-format on + }; + + SinglePassRunAndCheck(builder.GetCode(), JoinAllInsts(expected), + /* skip_nop = */ false); +} + +TEST_F(AssemblyBuilderTest, AppendNames) { + AssemblyBuilder builder; + builder.AppendNames({ + "OpName %void \"another_name_for_void\"", + "I am an invalid OpName instruction and should not be added", + "OpName %main \"another name for main\"", + }); + std::vector expected = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %void \"void\"", + "OpName %main_func_type \"main_func_type\"", + "OpName %main \"main\"", + "OpName %main_func_entry_block \"main_func_entry_block\"", + "OpName %void \"another_name_for_void\"", + "OpName %main \"another name for main\"", + "%void = OpTypeVoid", + "%main_func_type = OpTypeFunction %void", + "%main = OpFunction %void None %main_func_type", +"%main_func_entry_block = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + SinglePassRunAndCheck(builder.GetCode(), JoinAllInsts(expected), + /* skip_nop = */ false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/block_merge_test.cpp b/third_party/spirv-tools/test/opt/block_merge_test.cpp new file mode 100644 index 0000000..7381908 --- /dev/null +++ b/third_party/spirv-tools/test/opt/block_merge_test.cpp @@ -0,0 +1,1046 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using BlockMergeTest = PassTest<::testing::Test>; + +TEST_F(BlockMergeTest, Simple) { + // Note: SPIR-V hand edited to insert block boundary + // between two statements in main. + // + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v = BaseColor; + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpBranch %15 +%15 = OpLabel +%16 = OpLoad %v4float %v +OpStore %gl_FragColor %16 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +%16 = OpLoad %v4float %v +OpStore %gl_FragColor %16 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(BlockMergeTest, EmptyBlock) { + // Note: SPIR-V hand edited to insert empty block + // after two statements in main. + // + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v = BaseColor; + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpBranch %15 +%15 = OpLabel +%16 = OpLoad %v4float %v +OpStore %gl_FragColor %16 +OpBranch %17 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +%16 = OpLoad %v4float %v +OpStore %gl_FragColor %16 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(BlockMergeTest, NestedInControlFlow) { + // Note: SPIR-V hand edited to insert block boundary + // between OpFMul and OpStore in then-part. + // + // #version 140 + // in vec4 BaseColor; + // + // layout(std140) uniform U_t + // { + // bool g_B ; + // } ; + // + // void main() + // { + // vec4 v = BaseColor; + // if (g_B) + // vec4 v = v * 0.25; + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_B" +OpName %_ "" +OpName %v_0 "v" +OpName %gl_FragColor "gl_FragColor" +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t Block +OpDecorate %_ DescriptorSet 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%U_t = OpTypeStruct %uint +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_0_25 = OpConstant %float 0.25 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %10 +%24 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%v_0 = OpVariable %_ptr_Function_v4float Function +%25 = OpLoad %v4float %BaseColor +OpStore %v %25 +%26 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%27 = OpLoad %uint %26 +%28 = OpINotEqual %bool %27 %uint_0 +OpSelectionMerge %29 None +OpBranchConditional %28 %30 %29 +%30 = OpLabel +%31 = OpLoad %v4float %v +%32 = OpVectorTimesScalar %v4float %31 %float_0_25 +OpBranch %33 +%33 = OpLabel +OpStore %v_0 %32 +OpBranch %29 +%29 = OpLabel +%34 = OpLoad %v4float %v +OpStore %gl_FragColor %34 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%24 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%v_0 = OpVariable %_ptr_Function_v4float Function +%25 = OpLoad %v4float %BaseColor +OpStore %v %25 +%26 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%27 = OpLoad %uint %26 +%28 = OpINotEqual %bool %27 %uint_0 +OpSelectionMerge %29 None +OpBranchConditional %28 %30 %29 +%30 = OpLabel +%31 = OpLoad %v4float %v +%32 = OpVectorTimesScalar %v4float %31 %float_0_25 +OpStore %v_0 %32 +OpBranch %29 +%29 = OpLabel +%34 = OpLoad %v4float %v +OpStore %gl_FragColor %34 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(BlockMergeTest, PhiInSuccessorOfMergedBlock) { + const std::string text = R"( +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[then:%\w+]] [[else:%\w+]] +; CHECK: [[then]] = OpLabel +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[else]] = OpLabel +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpPhi {{%\w+}} %true [[then]] %false [[else]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %true %then %else +%then = OpLabel +OpBranch %then_next +%then_next = OpLabel +OpBranch %merge +%else = OpLabel +OpBranch %merge +%merge = OpLabel +%phi = OpPhi %bool %true %then_next %false %else +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, UpdateMergeInstruction) { + const std::string text = R"( +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[then:%\w+]] [[else:%\w+]] +; CHECK: [[then]] = OpLabel +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[else]] = OpLabel +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpSelectionMerge %real_merge None +OpBranchConditional %true %then %else +%then = OpLabel +OpBranch %merge +%else = OpLabel +OpBranch %merge +%merge = OpLabel +OpBranch %real_merge +%real_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, TwoMergeBlocksCannotBeMerged) { + const std::string text = R"( +; CHECK: OpSelectionMerge [[outer_merge:%\w+]] None +; CHECK: OpSelectionMerge [[inner_merge:%\w+]] None +; CHECK: [[inner_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[outer_merge]] +; CHECK: [[outer_merge]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpSelectionMerge %outer_merge None +OpBranchConditional %true %then %else +%then = OpLabel +OpBranch %inner_header +%else = OpLabel +OpBranch %inner_header +%inner_header = OpLabel +OpSelectionMerge %inner_merge None +OpBranchConditional %true %inner_then %inner_else +%inner_then = OpLabel +OpBranch %inner_merge +%inner_else = OpLabel +OpBranch %inner_merge +%inner_merge = OpLabel +OpBranch %outer_merge +%outer_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, MergeContinue) { + const std::string text = R"( +; CHECK: OpBranch [[header:%\w+]] +; CHECK: [[header]] = OpLabel +; CHECK-NEXT: OpLogicalAnd +; CHECK-NEXT: OpLoopMerge {{%\w+}} [[header]] None +; CHECK-NEXT: OpBranch [[header]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpBranch %header +%header = OpLabel +OpLoopMerge %merge %continue None +OpBranch %continue +%continue = OpLabel +%op = OpLogicalAnd %bool %true %false +OpBranch %header +%merge = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, MergeContinueWithOpLine) { + const std::string text = R"( +; CHECK: OpBranch [[header:%\w+]] +; CHECK: [[header]] = OpLabel +; CHECK-NEXT: OpLogicalAnd +; CHECK-NEXT: OpLine {{%\w+}} 1 1 +; CHECK-NEXT: OpLoopMerge {{%\w+}} [[header]] None +; CHECK-NEXT: OpBranch [[header]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%src = OpString "test.shader" +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpBranch %header +%header = OpLabel +OpLoopMerge %merge %continue None +OpBranch %continue +%continue = OpLabel +%op = OpLogicalAnd %bool %true %false +OpLine %src 1 1 +OpBranch %header +%merge = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, TwoHeadersCannotBeMerged) { + const std::string text = R"( +; CHECK: OpBranch [[loop_header:%\w+]] +; CHECK: [[loop_header]] = OpLabel +; CHECK-NEXT: OpLoopMerge +; CHECK-NEXT: OpBranch [[if_header:%\w+]] +; CHECK: [[if_header]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpBranch %header +%header = OpLabel +OpLoopMerge %merge %continue None +OpBranch %inner_header +%inner_header = OpLabel +OpSelectionMerge %if_merge None +OpBranchConditional %true %then %if_merge +%then = OpLabel +OpBranch %continue +%if_merge = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranchConditional %false %merge %header +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, CannotMergeContinue) { + const std::string text = R"( +; CHECK: OpBranch [[loop_header:%\w+]] +; CHECK: [[loop_header]] = OpLabel +; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_header:%\w+]] +; CHECK: [[if_header]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK: [[continue]] = OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpBranch %header +%header = OpLabel +OpLoopMerge %merge %continue None +OpBranchConditional %true %inner_header %merge +%inner_header = OpLabel +OpSelectionMerge %if_merge None +OpBranchConditional %true %then %if_merge +%then = OpLabel +OpBranch %continue +%if_merge = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranchConditional %false %merge %header +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, RemoveStructuredDeclaration) { + // Note: SPIR-V hand edited remove dead branch and add block + // before continue block + // + // #version 140 + // in vec4 BaseColor; + // + // void main() + // { + // while (true) { + // break; + // } + // gl_FragColor = BaseColor; + // } + + const std::string assembly = + R"( +; CHECK: OpLabel +; CHECK: [[header:%\w+]] = OpLabel +; CHECK-NOT: OpLoopMerge +; CHECK: OpReturn +; CHECK: [[continue:%\w+]] = OpLabel +; CHECK-NEXT: OpBranch [[block:%\w+]] +; CHECK: [[block]] = OpLabel +; CHECK-NEXT: OpBranch [[header]] +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %gl_FragColor "gl_FragColor" +OpName %BaseColor "BaseColor" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%main = OpFunction %void None %6 +%13 = OpLabel +OpBranch %14 +%14 = OpLabel +OpLoopMerge %15 %16 None +OpBranch %17 +%17 = OpLabel +OpBranch %15 +%18 = OpLabel +OpBranch %16 +%16 = OpLabel +OpBranch %14 +%15 = OpLabel +%19 = OpLoad %v4float %BaseColor +OpStore %gl_FragColor %19 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(assembly, true); +} + +TEST_F(BlockMergeTest, DontMergeKill) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None +; CHECK-NEXT: OpBranch [[ret:%\w+]] +; CHECK: [[ret:%\w+]] = OpLabel +; CHECK-NEXT: OpKill +; CHECK-DAG: [[cont]] = OpLabel +; CHECK-DAG: [[merge]] = OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranch %5 +%5 = OpLabel +OpKill +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, DontMergeTerminateInvocation) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None +; CHECK-NEXT: OpBranch [[ret:%\w+]] +; CHECK: [[ret:%\w+]] = OpLabel +; CHECK-NEXT: OpTerminateInvocation +; CHECK-DAG: [[cont]] = OpLabel +; CHECK-DAG: [[merge]] = OpLabel +OpCapability Shader +OpExtension "SPV_KHR_terminate_invocation" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranch %5 +%5 = OpLabel +OpTerminateInvocation +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, DontMergeUnreachable) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None +; CHECK-NEXT: OpBranch [[ret:%\w+]] +; CHECK: [[ret:%\w+]] = OpLabel +; CHECK-NEXT: OpUnreachable +; CHECK-DAG: [[cont]] = OpLabel +; CHECK-DAG: [[merge]] = OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranch %5 +%5 = OpLabel +OpUnreachable +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(BlockMergeTest, DontMergeReturn) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None +; CHECK-NEXT: OpBranch [[ret:%\w+]] +; CHECK: [[ret:%\w+]] = OpLabel +; CHECK-NEXT: OpReturn +; CHECK-DAG: [[cont]] = OpLabel +; CHECK-DAG: [[merge]] = OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranch %5 +%5 = OpLabel +OpReturn +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, DontMergeSwitch) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None +; CHECK-NEXT: OpBranch [[ret:%\w+]] +; CHECK: [[ret:%\w+]] = OpLabel +; CHECK-NEXT: OpSwitch +; CHECK-DAG: [[cont]] = OpLabel +; CHECK-DAG: [[merge]] = OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranch %5 +%5 = OpLabel +OpSwitch %int_0 %6 +%6 = OpLabel +OpReturn +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, DontMergeReturnValue) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] None +; CHECK-NEXT: OpBranch [[ret:%\w+]] +; CHECK: [[ret:%\w+]] = OpLabel +; CHECK-NEXT: OpReturn +; CHECK-DAG: [[cont]] = OpLabel +; CHECK-DAG: [[merge]] = OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%functy = OpTypeFunction %void +%otherfuncty = OpTypeFunction %bool +%true = OpConstantTrue %bool +%func = OpFunction %void None %functy +%1 = OpLabel +%2 = OpFunctionCall %bool %3 +OpReturn +OpFunctionEnd +%3 = OpFunction %bool None %otherfuncty +%4 = OpLabel +OpBranch %5 +%5 = OpLabel +OpLoopMerge %6 %7 None +OpBranch %8 +%8 = OpLabel +OpReturnValue %true +%7 = OpLabel +OpBranch %5 +%6 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, MergeHeaders) { + // Merge two headers when the second is the merge block of the first. + const std::string text = R"( +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpBranch [[header:%\w+]] +; CHECK-NEXT: [[header]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHEKC: OpReturn +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%functy = OpTypeFunction %void +%otherfuncty = OpTypeFunction %bool +%true = OpConstantTrue %bool +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %5 +%5 = OpLabel +OpLoopMerge %8 %7 None +OpBranch %8 +%7 = OpLabel +OpBranch %5 +%8 = OpLabel +OpSelectionMerge %m None +OpBranchConditional %true %a %m +%a = OpLabel +OpBranch %m +%m = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(BlockMergeTest, OpPhiInSuccessor) { + // Checks that when merging blocks A and B, the OpPhi at the start of B is + // removed and uses of its definition are replaced appropriately. + const std::string prefix = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +OpName %main "main" +OpName %x "x" +OpName %y "y" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_1 = OpConstant %int 1 +%main = OpFunction %void None %6 +%10 = OpLabel +%x = OpVariable %_ptr_Function_int Function +%y = OpVariable %_ptr_Function_int Function +OpStore %x %int_1 +%11 = OpLoad %int %x +)"; + + const std::string suffix_before = + R"(OpBranch %12 +%12 = OpLabel +%13 = OpPhi %int %11 %10 +OpStore %y %13 +OpReturn +OpFunctionEnd +)"; + + const std::string suffix_after = + R"(OpStore %y %11 +OpReturn +OpFunctionEnd +)"; + SinglePassRunAndCheck(prefix + suffix_before, + prefix + suffix_after, true, true); +} + +TEST_F(BlockMergeTest, MultipleOpPhisInSuccessor) { + // Checks that when merging blocks A and B, the OpPhis at the start of B are + // removed and uses of their definitions are replaced appropriately. + const std::string prefix = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +OpName %main "main" +OpName %S "S" +OpMemberName %S 0 "x" +OpMemberName %S 1 "f" +OpName %s "s" +OpName %g "g" +OpName %y "y" +OpName %t "t" +OpName %z "z" +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%float = OpTypeFloat 32 +%S = OpTypeStruct %int %float +%_ptr_Function_S = OpTypePointer Function %S +%int_1 = OpConstant %int 1 +%float_2 = OpConstant %float 2 +%16 = OpConstantComposite %S %int_1 %float_2 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Function_int = OpTypePointer Function %int +%int_3 = OpConstant %int 3 +%int_0 = OpConstant %int 0 +%main = OpFunction %void None %10 +%21 = OpLabel +%s = OpVariable %_ptr_Function_S Function +%g = OpVariable %_ptr_Function_float Function +%y = OpVariable %_ptr_Function_int Function +%t = OpVariable %_ptr_Function_S Function +%z = OpVariable %_ptr_Function_float Function +OpStore %s %16 +OpStore %g %float_2 +OpStore %y %int_3 +%22 = OpLoad %S %s +OpStore %t %22 +%23 = OpAccessChain %_ptr_Function_float %s %int_1 +%24 = OpLoad %float %23 +%25 = OpLoad %float %g +)"; + + const std::string suffix_before = + R"(OpBranch %26 +%26 = OpLabel +%27 = OpPhi %float %24 %21 +%28 = OpPhi %float %25 %21 +%29 = OpFAdd %float %27 %28 +%30 = OpAccessChain %_ptr_Function_int %s %int_0 +%31 = OpLoad %int %30 +OpBranch %32 +%32 = OpLabel +%33 = OpPhi %float %29 %26 +%34 = OpPhi %int %31 %26 +%35 = OpConvertSToF %float %34 +OpBranch %36 +%36 = OpLabel +%37 = OpPhi %float %35 %32 +%38 = OpFSub %float %33 %37 +%39 = OpLoad %int %y +OpBranch %40 +%40 = OpLabel +%41 = OpPhi %float %38 %36 +%42 = OpPhi %int %39 %36 +%43 = OpConvertSToF %float %42 +%44 = OpFAdd %float %41 %43 +OpStore %z %44 +OpReturn +OpFunctionEnd +)"; + + const std::string suffix_after = + R"(%29 = OpFAdd %float %24 %25 +%30 = OpAccessChain %_ptr_Function_int %s %int_0 +%31 = OpLoad %int %30 +%35 = OpConvertSToF %float %31 +%38 = OpFSub %float %29 %35 +%39 = OpLoad %int %y +%43 = OpConvertSToF %float %39 +%44 = OpFAdd %float %38 %43 +OpStore %z %44 +OpReturn +OpFunctionEnd +)"; + SinglePassRunAndCheck(prefix + suffix_before, + prefix + suffix_after, true, true); +} + +TEST_F(BlockMergeTest, UnreachableLoop) { + const std::string spirv = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%bool = OpTypeBool +%false = OpConstantFalse %bool +%main = OpFunction %void None %4 +%9 = OpLabel +OpBranch %10 +%11 = OpLabel +OpLoopMerge %12 %13 None +OpBranchConditional %false %13 %14 +%13 = OpLabel +OpSelectionMerge %15 None +OpBranchConditional %false %16 %17 +%16 = OpLabel +OpBranch %15 +%17 = OpLabel +OpBranch %15 +%15 = OpLabel +OpBranch %11 +%14 = OpLabel +OpReturn +%12 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(spirv, spirv, true, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// More complex control flow +// Others? + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/ccp_test.cpp b/third_party/spirv-tools/test/opt/ccp_test.cpp new file mode 100644 index 0000000..ef73435 --- /dev/null +++ b/third_party/spirv-tools/test/opt/ccp_test.cpp @@ -0,0 +1,1213 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/ccp_pass.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using CCPTest = PassTest<::testing::Test>; + +TEST_F(CCPTest, PropagateThroughPhis) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %x %outparm + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %x "x" + OpName %outparm "outparm" + OpDecorate %x Flat + OpDecorate %x Location 0 + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool +%_ptr_Function_int = OpTypePointer Function %int + %int_4 = OpConstant %int 4 + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 +%_ptr_Input_int = OpTypePointer Input %int + %x = OpVariable %_ptr_Input_int Input +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %3 + %4 = OpLabel + %5 = OpLoad %int %x + %9 = OpIAdd %int %int_1 %int_3 + %6 = OpSGreaterThan %bool %5 %int_3 + OpSelectionMerge %25 None + OpBranchConditional %6 %22 %23 + %22 = OpLabel + +; CHECK: OpCopyObject %int %int_4 + %7 = OpCopyObject %int %9 + + OpBranch %25 + %23 = OpLabel + %8 = OpCopyObject %int %int_4 + OpBranch %25 + %25 = OpLabel + +; %int_4 should have propagated to both OpPhi operands. +; CHECK: OpPhi %int %int_4 {{%\d+}} %int_4 {{%\d+}} + %35 = OpPhi %int %7 %22 %8 %23 + +; This function always returns 4. DCE should get rid of everything else. +; CHECK OpStore %outparm %int_4 + OpStore %outparm %35 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, SimplifyConditionals) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outparm + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %outparm "outparm" + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool +%_ptr_Function_int = OpTypePointer Function %int + %int_4 = OpConstant %int 4 + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %3 + %4 = OpLabel + %9 = OpIAdd %int %int_4 %int_3 + %6 = OpSGreaterThan %bool %9 %int_3 + OpSelectionMerge %25 None +; CHECK: OpBranchConditional %true [[bb_taken:%\d+]] [[bb_not_taken:%\d+]] + OpBranchConditional %6 %22 %23 +; CHECK: [[bb_taken]] = OpLabel + %22 = OpLabel +; CHECK: OpCopyObject %int %int_7 + %7 = OpCopyObject %int %9 + OpBranch %25 +; CHECK: [[bb_not_taken]] = OpLabel + %23 = OpLabel +; CHECK: [[id_not_evaluated:%\d+]] = OpCopyObject %int %int_4 + %8 = OpCopyObject %int %int_4 + OpBranch %25 + %25 = OpLabel + +; %int_7 should have propagated to the first OpPhi operand. But the else branch +; is not executable (conditional is always true), so no values should be +; propagated there and the value of the OpPhi should always be %int_7. +; CHECK: OpPhi %int %int_7 [[bb_taken]] [[id_not_evaluated]] [[bb_not_taken]] + %35 = OpPhi %int %7 %22 %8 %23 + +; Only the true path of the conditional is ever executed. The output of this +; function is always %int_7. +; CHECK: OpStore %outparm %int_7 + OpStore %outparm %35 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, SimplifySwitches) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outparm + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %outparm "outparm" + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_23 = OpConstant %int 23 + %int_42 = OpConstant %int 42 + %int_14 = OpConstant %int 14 + %int_15 = OpConstant %int 15 + %int_4 = OpConstant %int 4 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %6 + %15 = OpLabel + OpSelectionMerge %17 None + OpSwitch %int_23 %17 10 %18 13 %19 23 %20 + %18 = OpLabel + OpBranch %17 + %19 = OpLabel + OpBranch %17 + %20 = OpLabel + OpBranch %17 + %17 = OpLabel + %24 = OpPhi %int %int_23 %15 %int_42 %18 %int_14 %19 %int_15 %20 + +; The switch will always jump to label %20, which carries the value %int_15. +; CHECK: OpIAdd %int %int_15 %int_4 + %22 = OpIAdd %int %24 %int_4 + +; Consequently, the return value will always be %int_19. +; CHECK: OpStore %outparm %int_19 + OpStore %outparm %22 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, SimplifySwitchesDefaultBranch) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outparm + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %outparm "outparm" + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_42 = OpConstant %int 42 + %int_4 = OpConstant %int 4 + %int_1 = OpConstant %int 1 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %6 + %13 = OpLabel + %15 = OpIAdd %int %int_42 %int_4 + OpSelectionMerge %16 None + +; CHECK: OpSwitch %int_46 {{%\d+}} 10 {{%\d+}} + OpSwitch %15 %17 10 %18 + %18 = OpLabel + OpBranch %16 + %17 = OpLabel + OpBranch %16 + %16 = OpLabel + %22 = OpPhi %int %int_42 %18 %int_1 %17 + +; The switch will always jump to the default label %17. This carries the value +; %int_1. +; CHECK: OpIAdd %int %int_1 %int_4 + %20 = OpIAdd %int %22 %int_4 + +; Resulting in a return value of %int_5. +; CHECK: OpStore %outparm %int_5 + OpStore %outparm %20 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, SimplifyIntVector) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %v "v" + OpName %OutColor "OutColor" + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 +%_ptr_Function_v4int = OpTypePointer Function %v4int + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 + %int_4 = OpConstant %int 4 + %14 = OpConstantComposite %v4int %int_1 %int_2 %int_3 %int_4 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Output_v4int = OpTypePointer Output %v4int + %OutColor = OpVariable %_ptr_Output_v4int Output + %main = OpFunction %void None %3 + %5 = OpLabel + %v = OpVariable %_ptr_Function_v4int Function + OpStore %v %14 + %18 = OpAccessChain %_ptr_Function_int %v %uint_0 + %19 = OpLoad %int %18 + +; The constant folder does not see through access chains. To get this, the +; vector would have to be scalarized. +; CHECK: [[result_id:%\d+]] = OpIAdd %int {{%\d+}} %int_1 + %20 = OpIAdd %int %19 %int_1 + %21 = OpAccessChain %_ptr_Function_int %v %uint_0 + +; CHECK: OpStore {{%\d+}} [[result_id]] + OpStore %21 %20 + %24 = OpLoad %v4int %v + OpStore %OutColor %24 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, BadSimplifyFloatVector) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %v "v" + OpName %OutColor "OutColor" + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %float_4 = OpConstant %float 4 + %14 = OpConstantComposite %v4float %float_1 %float_2 %float_3 %float_4 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OutColor = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %v = OpVariable %_ptr_Function_v4float Function + OpStore %v %14 + %18 = OpAccessChain %_ptr_Function_float %v %uint_0 + %19 = OpLoad %float %18 + +; NOTE: This test should start failing once floating point folding is +; implemented (https://github.com/KhronosGroup/SPIRV-Tools/issues/943). +; This should be checking that we are adding %float_1 + %float_1. +; CHECK: [[result_id:%\d+]] = OpFAdd %float {{%\d+}} %float_1 + %20 = OpFAdd %float %19 %float_1 + %21 = OpAccessChain %_ptr_Function_float %v %uint_0 + +; This should be checkint that we are storing %float_2 instead of result_it. +; CHECK: OpStore {{%\d+}} [[result_id]] + OpStore %21 %20 + %24 = OpLoad %v4float %v + OpStore %OutColor %24 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, NoLoadStorePropagation) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outparm + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %x "x" + OpName %outparm "outparm" + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_23 = OpConstant %int 23 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %3 + %5 = OpLabel + %x = OpVariable %_ptr_Function_int Function + OpStore %x %int_23 + +; int_23 should not propagate into this load. +; CHECK: [[load_id:%\d+]] = OpLoad %int %x + %12 = OpLoad %int %x + +; Nor into this copy operation. +; CHECK: [[copy_id:%\d+]] = OpCopyObject %int [[load_id]] + %13 = OpCopyObject %int %12 + +; Likewise here. +; CHECK: OpStore %outparm [[copy_id]] + OpStore %outparm %13 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, HandleAbortInstructions) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool +; CHECK: %true = OpConstantTrue %bool + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %4 = OpLabel + %9 = OpIAdd %int %int_3 %int_1 + %6 = OpSGreaterThan %bool %9 %int_3 + OpSelectionMerge %23 None +; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}} + OpBranchConditional %6 %22 %23 + %22 = OpLabel + OpKill + %23 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, SSAWebCycles) { + // Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1159 + // When there is a cycle in the SSA def-use web, the propagator was getting + // into an infinite loop. SSA edges for Phi instructions should not be + // added to the edges to simulate. + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_4 = OpConstant %int 4 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 +%_ptr_Output_int = OpTypePointer Output %int + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %11 + %11 = OpLabel + %29 = OpPhi %int %int_0 %5 %22 %14 + %30 = OpPhi %int %int_0 %5 %25 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %bool %30 %int_4 +; CHECK: OpBranchConditional %true {{%\d+}} {{%\d+}} + OpBranchConditional %19 %12 %13 + %12 = OpLabel +; CHECK: OpIAdd %int %int_0 %int_0 + %22 = OpIAdd %int %29 %30 + OpBranch %14 + %14 = OpLabel +; CHECK: OpPhi %int %int_0 {{%\d+}} + %25 = OpPhi %int %30 %12 + OpBranch %11 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, LoopInductionVariables) { + // Test reduced from https://github.com/KhronosGroup/SPIRV-Tools/issues/1143 + // We are failing to properly consider the induction variable for this loop + // as Varying. + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 430 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %5 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + +; This Phi should not have all constant arguments: +; CHECK: [[phi_id:%\d+]] = OpPhi %int %int_0 {{%\d+}} {{%\d+}} {{%\d+}} + %22 = OpPhi %int %int_0 %12 %21 %15 + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + +; The Phi should never be considered to have the value %int_0. +; CHECK: [[branch_selector:%\d+]] = OpSLessThan %bool [[phi_id]] %int_10 + %18 = OpSLessThan %bool %22 %int_10 + +; This conditional was wrongly converted into an always-true jump due to the +; bad meet evaluation of %22. +; CHECK: OpBranchConditional [[branch_selector]] {{%\d+}} {{%\d+}} + OpBranchConditional %18 %19 %14 + %19 = OpLabel + OpBranch %15 + %15 = OpLabel +; CHECK: OpIAdd %int [[phi_id]] %int_1 + %21 = OpIAdd %int %22 %int_1 + OpBranch %13 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(CCPTest, HandleCompositeWithUndef) { + // Check to make sure that CCP does not crash when given a "constant" struct + // with an undef. If at a later time CCP is enhanced to optimize this case, + // it is not wrong. + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %_struct_7 = OpTypeStruct %int %int + %int_1 = OpConstant %int 1 + %9 = OpUndef %int + %10 = OpConstantComposite %_struct_7 %int_1 %9 + %main = OpFunction %void None %4 + %11 = OpLabel + %12 = OpCompositeExtract %int %10 0 + %13 = OpCopyObject %int %12 + OpReturn + OpFunctionEnd + )"; + + auto res = SinglePassRunToBinary(spv_asm, true); + EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange); +} + +TEST_F(CCPTest, SkipSpecConstantInstrucitons) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 500 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %10 = OpSpecConstantFalse %bool + %main = OpFunction %void None %4 + %11 = OpLabel + OpBranchConditional %10 %L1 %L2 + %L1 = OpLabel + OpReturn + %L2 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto res = SinglePassRunToBinary(spv_asm, true); + EXPECT_EQ(std::get<1>(res), Pass::Status::SuccessWithoutChange); +} + +TEST_F(CCPTest, UpdateSubsequentPhisToVarying) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" %in +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 1 +%false = OpConstantFalse %bool +%int0 = OpConstant %int 0 +%int1 = OpConstant %int 1 +%int6 = OpConstant %int 6 +%int_ptr_Input = OpTypePointer Input %int +%in = OpVariable %int_ptr_Input Input +%undef = OpUndef %int + +; Although no constants are propagated in this function, the propagator +; generates a new %true value while visiting conditional statements. +; CHECK: %true = OpConstantTrue %bool + +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +%outer_phi = OpPhi %int %int0 %1 %outer_add %15 +%cond1 = OpSLessThanEqual %bool %outer_phi %int6 +OpLoopMerge %3 %15 None +OpBranchConditional %cond1 %4 %3 +%4 = OpLabel +%ld = OpLoad %int %in +%cond2 = OpSGreaterThanEqual %bool %int1 %ld +OpSelectionMerge %10 None +OpBranchConditional %cond2 %8 %9 +%8 = OpLabel +OpBranch %10 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +%extra_phi = OpPhi %int %outer_phi %8 %outer_phi %9 +OpBranch %11 +%11 = OpLabel +%inner_phi = OpPhi %int %int0 %10 %inner_add %13 +%cond3 = OpSLessThanEqual %bool %inner_phi %int6 +OpLoopMerge %14 %13 None +OpBranchConditional %cond3 %12 %14 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +%inner_add = OpIAdd %int %inner_phi %int1 +OpBranch %11 +%14 = OpLabel +OpBranch %15 +%15 = OpLabel +%outer_add = OpIAdd %int %extra_phi %int1 +OpBranch %2 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + auto result = SinglePassRunAndMatch(text, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +TEST_F(CCPTest, UndefInPhi) { + const std::string text = R"( +; CHECK: [[uint1:%\w+]] = OpConstant {{%\w+}} 1 +; CHECK: [[phi:%\w+]] = OpPhi +; CHECK: OpIAdd {{%\w+}} [[phi]] [[uint1]] + OpCapability Kernel + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpDecorate %1 LinkageAttributes "func" Export + %void = OpTypeVoid + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %7 = OpUndef %uint + %8 = OpTypeFunction %void %bool + %1 = OpFunction %void None %8 + %9 = OpFunctionParameter %bool + %10 = OpLabel + OpBranchConditional %9 %11 %12 + %11 = OpLabel + OpBranch %13 + %12 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranchConditional %9 %13 %15 + %15 = OpLabel + OpBranch %13 + %13 = OpLabel + %16 = OpPhi %uint %uint_0 %11 %7 %14 %uint_1 %15 + %17 = OpIAdd %uint %16 %uint_1 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// Just test to make sure the constant fold rules are being used. Will rely on +// the folding test for specific testing of specific rules. +TEST_F(CCPTest, UseConstantFoldingRules) { + const std::string text = R"( +; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1 +; CHECK: OpReturnValue [[float1]] + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %1 LinkageAttributes "func" Export + %void = OpTypeVoid + %bool = OpTypeBool + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %8 = OpTypeFunction %float + %1 = OpFunction %float None %8 + %10 = OpLabel + %17 = OpFAdd %float %float_0 %float_1 + OpReturnValue %17 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// Test for #1300. Previously value for %5 would not settle during simulation. +TEST_F(CCPTest, SettlePhiLatticeValue) { + const std::string text = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranchConditional %true %2 %3 +%3 = OpLabel +OpBranch %2 +%2 = OpLabel +%5 = OpPhi %bool %true %1 %false %3 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunToBinary(text, true); +} + +TEST_F(CCPTest, NullBranchCondition) { + const std::string text = R"( +; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1 +; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 1 +%null = OpConstantNull %bool +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpSelectionMerge %2 None +OpBranchConditional %null %2 %3 +%3 = OpLabel +OpBranch %2 +%2 = OpLabel +%phi = OpPhi %int %int_1 %1 %int_2 %3 +%add = OpIAdd %int %int_1 %phi +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CCPTest, UndefBranchCondition) { + const std::string text = R"( +; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1 +; CHECK: [[phi:%\w+]] = OpPhi +; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 1 +%undef = OpUndef %bool +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpSelectionMerge %2 None +OpBranchConditional %undef %2 %3 +%3 = OpLabel +OpBranch %2 +%2 = OpLabel +%phi = OpPhi %int %int_1 %1 %int_2 %3 +%add = OpIAdd %int %int_1 %phi +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CCPTest, NullSwitchCondition) { + const std::string text = R"( +; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1 +; CHECK: [[int2:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpIAdd {{%\w+}} [[int1]] [[int2]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 1 +%null = OpConstantNull %int +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpSelectionMerge %2 None +OpSwitch %null %2 0 %3 +%3 = OpLabel +OpBranch %2 +%2 = OpLabel +%phi = OpPhi %int %int_1 %1 %int_2 %3 +%add = OpIAdd %int %int_1 %phi +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CCPTest, UndefSwitchCondition) { + const std::string text = R"( +; CHECK: [[int1:%\w+]] = OpConstant {{%\w+}} 1 +; CHECK: [[phi:%\w+]] = OpPhi +; CHECK: OpIAdd {{%\w+}} [[int1]] [[phi]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 1 +%undef = OpUndef %int +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpSelectionMerge %2 None +OpSwitch %undef %2 0 %3 +%3 = OpLabel +OpBranch %2 +%2 = OpLabel +%phi = OpPhi %int %int_1 %1 %int_2 %3 +%add = OpIAdd %int %int_1 %phi +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// Test for #1361. +TEST_F(CCPTest, CompositeConstructOfGlobalValue) { + const std::string text = R"( +; CHECK: [[phi:%\w+]] = OpPhi +; CHECK-NEXT: OpCompositeExtract {{%\w+}} [[phi]] 0 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" %in +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 1 +%bool = OpTypeBool +%functy = OpTypeFunction %void +%ptr_int_Input = OpTypePointer Input %int +%in = OpVariable %ptr_int_Input Input +%struct = OpTypeStruct %ptr_int_Input %ptr_int_Input +%struct_null = OpConstantNull %struct +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +%phi = OpPhi %struct %struct_null %1 %5 %4 +%extract = OpCompositeExtract %ptr_int_Input %phi 0 +OpLoopMerge %3 %4 None +OpBranch %4 +%4 = OpLabel +%5 = OpCompositeConstruct %struct %in %in +OpBranch %2 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CCPTest, FoldWithDecoration) { + const std::string text = R"( +; CHECK: OpCapability +; CHECK-NOT: OpDecorate +; CHECK: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %3 RelaxedPrecision + %void = OpTypeVoid + %5 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %float_0 = OpConstant %float 0 + %v4float = OpTypeVector %float 4 + %10 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %2 = OpFunction %void None %5 + %11 = OpLabel + %3 = OpVectorShuffle %v3float %10 %10 0 1 2 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CCPTest, DebugSimpleFoldConstant) { + const std::string text = R"( + OpCapability Shader + OpCapability Linkage + %ext = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + %file_name = OpString "test" + %float_name = OpString "float" + %main_name = OpString "main" + %f_name = OpString "f" + OpDecorate %1 LinkageAttributes "func" Export + %void = OpTypeVoid + %bool = OpTypeBool + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + +; CHECK: [[float1:%\w+]] = OpConstant {{%\w+}} 1 + %float_1 = OpConstant %float 1 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %8 = OpTypeFunction %float + %null_expr = OpExtInst %void %ext DebugExpression + %src = OpExtInst %void %ext DebugSource %file_name + %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL + %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float + %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf + %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %1 + %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal + %1 = OpFunction %float None %8 + %10 = OpLabel + +; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope +; CHECK: OpLine [[file:%\w+]] 1 0 +; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %float_1 + %s0 = OpExtInst %void %ext DebugScope %dbg_main + OpLine %file_name 1 0 + %17 = OpFAdd %float %float_0 %float_1 + %val = OpExtInst %void %ext DebugValue %dbg_f %17 %null_expr + +; CHECK: OpLine [[file]] 2 0 +; CHECK: OpReturnValue [[float1]] + OpLine %file_name 2 0 + OpReturnValue %17 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CCPTest, DebugFoldMultipleForSingleConstant) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + %ext = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outparm + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + %file_name = OpString "test" + %float_name = OpString "float" + %main_name = OpString "main" + %f_name = OpString "f" + OpName %main "main" + OpName %outparm "outparm" + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool +%_ptr_Function_int = OpTypePointer Function %int + %int_4 = OpConstant %int 4 + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %null_expr = OpExtInst %void %ext DebugExpression + %src = OpExtInst %void %ext DebugSource %file_name + %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL + %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float + %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf + %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main + %bb0 = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main + %bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main + %bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main + %bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main + %dbg_f0 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal + %dbg_f1 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 1 0 %dbg_main FlagIsLocal + %dbg_f2 = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 2 0 %dbg_main FlagIsLocal + %main = OpFunction %void None %3 + %4 = OpLabel + +; CHECK: OpExtInst %void [[ext:%\w+]] DebugScope +; CHECK: OpLine [[file:%\w+]] 1 0 +; CHECK: OpIAdd %int %int_4 %int_3 +; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %int_7 + %s0 = OpExtInst %void %ext DebugScope %bb0 + OpLine %file_name 1 0 + %9 = OpIAdd %int %int_4 %int_3 + %val0 = OpExtInst %void %ext DebugValue %dbg_f0 %9 %null_expr + +; CHECK: OpLine [[file]] 2 0 +; CHECK: OpSGreaterThan %bool %int_7 %int_3 +; CHECK: OpExtInst %void [[ext]] DebugValue {{%\w+}} %true + OpLine %file_name 2 0 + %6 = OpSGreaterThan %bool %9 %int_3 + %val1 = OpExtInst %void %ext DebugValue %dbg_f1 %6 %null_expr + + OpSelectionMerge %25 None + OpBranchConditional %6 %22 %23 + %22 = OpLabel + %s1 = OpExtInst %void %ext DebugScope %bb1 + %7 = OpCopyObject %int %9 + %val2 = OpExtInst %void %ext DebugValue %dbg_f2 %7 %null_expr + OpBranch %25 + %23 = OpLabel + %s2 = OpExtInst %void %ext DebugScope %bb2 + %8 = OpCopyObject %int %int_4 + OpBranch %25 + %25 = OpLabel + %s3 = OpExtInst %void %ext DebugScope %bb3 + %35 = OpPhi %int %7 %22 %8 %23 + OpStore %outparm %35 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3636 +TEST_F(CCPTest, CCPNoChangeFailure) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 2 + %13 = OpConstant %6 4 + %21 = OpConstant %6 1 + %10 = OpTypeBool + %17 = OpTypePointer Function %6 + +; CCP is generating two new constants during propagation that end up being +; dead because they cannot be replaced anywhere in the IR. CCP was wrongly +; considering the IR to be unmodified because of this. +; CHECK: %true = OpConstantTrue %bool +; CHECK: %int_3 = OpConstant %int 3 + + %4 = OpFunction %2 None %3 + %11 = OpLabel + OpBranch %5 + %5 = OpLabel + %23 = OpPhi %6 %7 %11 %20 %15 + %9 = OpSLessThan %10 %23 %13 + OpLoopMerge %8 %15 None + OpBranchConditional %9 %15 %8 + %15 = OpLabel + %20 = OpIAdd %6 %23 %21 + OpBranch %5 + %8 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndMatch(text, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3738 +// Similar to the previous one but more than one constant is generated in a +// single call to the instruction folder. +TEST_F(CCPTest, CCPNoChangeFailureSeveralConstantsDuringFolding) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool + %v3bool = OpTypeVector %bool 3 + %float_0 = OpConstant %float 0 + %12 = OpConstantComposite %v3float %float_0 %float_0 %float_0 +%float_0_300000012 = OpConstant %float 0.300000012 + %14 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_0_300000012 + +; CCP is generating several constants during a single instruction evaluation. +; When folding %19, it generates the constants %true and %24. They are dead +; because they cannot be replaced anywhere in the IR. CCP was wrongly +; considering the IR to be unmodified because of this. +; +; CHECK: %true = OpConstantTrue %bool +; CHECK: %24 = OpConstantComposite %v3bool %true %true %true +; CHECK: %float_1 = OpConstant %float 1 +; CHECK: %float_0_699999988 = OpConstant %float 0.699999988 + + %2 = OpFunction %void None %4 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %v3float %12 %15 %14 %18 + %19 = OpFOrdLessThan %v3bool %17 %14 + %20 = OpAll %bool %19 + OpLoopMerge %21 %18 None + OpBranchConditional %20 %18 %21 + %18 = OpLabel + OpBranch %16 + %21 = OpLabel + %22 = OpExtInst %v3float %1 FMix %12 %17 %14 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndMatch(text, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} + +// Test from https://github.com/KhronosGroup/SPIRV-Tools/issues/3991 +// Similar to the previous one but constants are created even when no +// instruction are ever folded during propagation. +TEST_F(CCPTest, CCPNoChangeFailureWithUnfoldableInstr) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v3float %float_0 %float_0 %float_0 +%float_0_300000012 = OpConstant %float 0.300000012 + %13 = OpConstantComposite %v3float %float_0_300000012 %float_0_300000012 %float_0_300000012 + +; CCP generates two constants when trying to fold an instruction, which it +; ultimately fails to fold. The instruction folder in CCP was only +; checking for newly added constants if the instruction folds successfully. +; +; CHECK: %float_1 = OpConstant %float 1 +; CHECK: %float_0_699999988 = OpConstant %float 0.69999998 + + %2 = OpFunction %void None %4 + %14 = OpLabel + %15 = OpBitcast %uint %float_0_300000012 + %16 = OpUGreaterThan %bool %15 %uint_0 + OpBranch %17 + %17 = OpLabel + %18 = OpPhi %v3float %11 %14 %13 %19 + OpLoopMerge %20 %19 None + OpBranchConditional %16 %19 %20 + %19 = OpLabel + OpBranch %17 + %20 = OpLabel + %21 = OpExtInst %v3float %1 FMix %11 %18 %13 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndMatch(text, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithChange); +} +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/cfg_cleanup_test.cpp b/third_party/spirv-tools/test/opt/cfg_cleanup_test.cpp new file mode 100644 index 0000000..3498f00 --- /dev/null +++ b/third_party/spirv-tools/test/opt/cfg_cleanup_test.cpp @@ -0,0 +1,456 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using CFGCleanupTest = PassTest<::testing::Test>; + +TEST_F(CFGCleanupTest, RemoveUnreachableBlocks) { + const std::string declarations = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %inf %outf4 +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %inf "inf" +OpName %outf4 "outf4" +OpDecorate %inf Location 0 +OpDecorate %outf4 Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float +%inf = OpVariable %_ptr_Input_float Input +%float_2 = OpConstant %float 2 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outf4 = OpVariable %_ptr_Output_v4float Output +%float_n0_5 = OpConstant %float -0.5 +)"; + + const std::string body_before = R"(%main = OpFunction %void None %6 +%14 = OpLabel +OpBranch %18 +%19 = OpLabel +%20 = OpLoad %float %inf +%21 = OpCompositeConstruct %v4float %20 %20 %20 %20 +OpStore %outf4 %21 +OpBranch %17 +%18 = OpLabel +%22 = OpLoad %float %inf +%23 = OpFAdd %float %22 %float_n0_5 +%24 = OpCompositeConstruct %v4float %23 %23 %23 %23 +OpStore %outf4 %24 +OpBranch %17 +%17 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string body_after = R"(%main = OpFunction %void None %6 +%14 = OpLabel +OpBranch %15 +%15 = OpLabel +%20 = OpLoad %float %inf +%21 = OpFAdd %float %20 %float_n0_5 +%22 = OpCompositeConstruct %v4float %21 %21 %21 %21 +OpStore %outf4 %22 +OpBranch %19 +%19 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(declarations + body_before, + declarations + body_after, true, true); +} + +TEST_F(CFGCleanupTest, RemoveDecorations) { + const std::string before = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %x "x" + OpName %dead "dead" + OpDecorate %x RelaxedPrecision + OpDecorate %dead RelaxedPrecision + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_2 = OpConstant %float 2 + %float_4 = OpConstant %float 4 + + %main = OpFunction %void None %6 + %14 = OpLabel + %x = OpVariable %_ptr_Function_float Function + OpBranch %18 + %19 = OpLabel + %dead = OpVariable %_ptr_Function_float Function + OpStore %dead %float_2 + OpBranch %17 + %18 = OpLabel + OpStore %x %float_4 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const std::string after = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpName %main "main" +OpName %x "x" +OpDecorate %x RelaxedPrecision +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_2 = OpConstant %float 2 +%float_4 = OpConstant %float 4 +%main = OpFunction %void None %6 +%11 = OpLabel +%x = OpVariable %_ptr_Function_float Function +OpBranch %12 +%12 = OpLabel +OpStore %x %float_4 +OpBranch %14 +%14 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(CFGCleanupTest, UpdatePhis) { + const std::string before = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %y %outparm + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %y "y" + OpName %outparm "outparm" + OpDecorate %y Flat + OpDecorate %y Location 0 + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_int = OpTypePointer Input %int + %y = OpVariable %_ptr_Input_int Input + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_42 = OpConstant %int 42 + %int_23 = OpConstant %int 23 + %int_5 = OpConstant %int 5 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %3 + %5 = OpLabel + %11 = OpLoad %int %y + OpBranch %21 + %16 = OpLabel + %20 = OpIAdd %int %11 %int_42 + OpBranch %17 + %21 = OpLabel + %24 = OpISub %int %11 %int_23 + OpBranch %17 + %17 = OpLabel + %31 = OpPhi %int %20 %16 %24 %21 + %27 = OpIAdd %int %31 %int_5 + OpStore %outparm %27 + OpReturn + OpFunctionEnd +)"; + + const std::string after = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %y %outparm +OpExecutionMode %main OriginUpperLeft +OpName %main "main" +OpName %y "y" +OpName %outparm "outparm" +OpDecorate %y Flat +OpDecorate %y Location 0 +OpDecorate %outparm Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_int = OpTypePointer Input %int +%y = OpVariable %_ptr_Input_int Input +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_42 = OpConstant %int 42 +%int_23 = OpConstant %int 23 +%int_5 = OpConstant %int 5 +%_ptr_Output_int = OpTypePointer Output %int +%outparm = OpVariable %_ptr_Output_int Output +%main = OpFunction %void None %6 +%16 = OpLabel +%17 = OpLoad %int %y +OpBranch %18 +%18 = OpLabel +%22 = OpISub %int %17 %int_23 +OpBranch %21 +%21 = OpLabel +%23 = OpPhi %int %22 %18 +%24 = OpIAdd %int %23 %int_5 +OpStore %outparm %24 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(CFGCleanupTest, RemoveNamedLabels) { + const std::string before = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 430 + OpName %main "main" + OpName %dead "dead" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %main = OpFunction %void None %5 + %6 = OpLabel + OpReturn + %dead = OpLabel + OpReturn + OpFunctionEnd)"; + + const std::string after = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpSource GLSL 430 +OpName %main "main" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%main = OpFunction %void None %5 +%6 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(CFGCleanupTest, RemovePhiArgsFromFarBlocks) { + const std::string before = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %y %outparm + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %y "y" + OpName %outparm "outparm" + OpDecorate %y Flat + OpDecorate %y Location 0 + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_int = OpTypePointer Input %int + %y = OpVariable %_ptr_Input_int Input + %int_42 = OpConstant %int 42 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %int_14 = OpConstant %int 14 + %int_15 = OpConstant %int 15 + %int_5 = OpConstant %int 5 + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %40 + %41 = OpLabel + %11 = OpLoad %int %y + OpBranch %40 + %40 = OpLabel + %12 = OpLoad %int %y + OpSelectionMerge %16 None + OpSwitch %12 %16 10 %13 13 %14 18 %15 + %13 = OpLabel + OpBranch %16 + %14 = OpLabel + OpStore %outparm %int_14 + OpBranch %16 + %15 = OpLabel + OpStore %outparm %int_15 + OpBranch %16 + %16 = OpLabel + %30 = OpPhi %int %11 %40 %int_42 %13 %11 %14 %11 %15 + %28 = OpIAdd %int %30 %int_5 + OpStore %outparm %28 + OpReturn + OpFunctionEnd)"; + + const std::string after = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %y %outparm +OpExecutionMode %main OriginUpperLeft +OpName %main "main" +OpName %y "y" +OpName %outparm "outparm" +OpDecorate %y Flat +OpDecorate %y Location 0 +OpDecorate %outparm Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_int = OpTypePointer Input %int +%y = OpVariable %_ptr_Input_int Input +%int_42 = OpConstant %int 42 +%_ptr_Output_int = OpTypePointer Output %int +%outparm = OpVariable %_ptr_Output_int Output +%int_14 = OpConstant %int 14 +%int_15 = OpConstant %int 15 +%int_5 = OpConstant %int 5 +%26 = OpUndef %int +%main = OpFunction %void None %6 +%15 = OpLabel +OpBranch %16 +%16 = OpLabel +%19 = OpLoad %int %y +OpSelectionMerge %20 None +OpSwitch %19 %20 10 %21 13 %22 18 %23 +%21 = OpLabel +OpBranch %20 +%22 = OpLabel +OpStore %outparm %int_14 +OpBranch %20 +%23 = OpLabel +OpStore %outparm %int_15 +OpBranch %20 +%20 = OpLabel +%24 = OpPhi %int %26 %16 %int_42 %21 %26 %22 %26 %23 +%25 = OpIAdd %int %24 %int_5 +OpStore %outparm %25 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(CFGCleanupTest, RemovePhiConstantArgs) { + const std::string before = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %y %outparm + OpExecutionMode %main OriginUpperLeft + OpName %main "main" + OpName %y "y" + OpName %outparm "outparm" + OpDecorate %y Flat + OpDecorate %y Location 0 + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %y = OpVariable %_ptr_Input_int Input + %int_10 = OpConstant %int 10 + %bool = OpTypeBool +%_ptr_Function_int = OpTypePointer Function %int + %int_23 = OpConstant %int 23 + %int_5 = OpConstant %int 5 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %24 = OpUndef %int + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %14 + %40 = OpLabel + %9 = OpLoad %int %y + %12 = OpSGreaterThan %bool %9 %int_10 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + %25 = OpPhi %int %24 %5 %int_23 %13 + %20 = OpIAdd %int %25 %int_5 + OpStore %outparm %20 + OpReturn + OpFunctionEnd)"; + + const std::string after = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %y %outparm +OpExecutionMode %main OriginUpperLeft +OpName %main "main" +OpName %y "y" +OpName %outparm "outparm" +OpDecorate %y Flat +OpDecorate %y Location 0 +OpDecorate %outparm Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%y = OpVariable %_ptr_Input_int Input +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%_ptr_Function_int = OpTypePointer Function %int +%int_23 = OpConstant %int 23 +%int_5 = OpConstant %int 5 +%_ptr_Output_int = OpTypePointer Output %int +%outparm = OpVariable %_ptr_Output_int Output +%15 = OpUndef %int +%main = OpFunction %void None %6 +%16 = OpLabel +OpBranch %17 +%17 = OpLabel +%22 = OpPhi %int %15 %16 +%23 = OpIAdd %int %22 %int_5 +OpStore %outparm %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/cfg_test.cpp b/third_party/spirv-tools/test/opt/cfg_test.cpp new file mode 100644 index 0000000..2cfc9f3 --- /dev/null +++ b/third_party/spirv-tools/test/opt/cfg_test.cpp @@ -0,0 +1,205 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/ir_context.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::ContainerEq; + +using CFGTest = PassTest<::testing::Test>; + +TEST_F(CFGTest, ForEachBlockInPostOrderIf) { + const std::string test = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpName %main "main" +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%4 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%5 = OpConstant %uint 5 +%main = OpFunction %void None %4 +%8 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %true %9 %10 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + CFG* cfg = context->cfg(); + Module* module = context->module(); + Function* function = &*module->begin(); + std::vector order; + cfg->ForEachBlockInPostOrder(&*function->begin(), [&order](BasicBlock* bb) { + order.push_back(bb->id()); + }); + + std::vector expected_result = {10, 9, 8}; + EXPECT_THAT(order, ContainerEq(expected_result)); +} + +TEST_F(CFGTest, ForEachBlockInPostOrderLoop) { + const std::string test = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpName %main "main" +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%4 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%5 = OpConstant %uint 5 +%main = OpFunction %void None %4 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %11 %10 None +OpBranchConditional %true %11 %10 +%10 = OpLabel +OpBranch %9 +%11 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + CFG* cfg = context->cfg(); + Module* module = context->module(); + Function* function = &*module->begin(); + std::vector order; + cfg->ForEachBlockInPostOrder(&*function->begin(), [&order](BasicBlock* bb) { + order.push_back(bb->id()); + }); + + std::vector expected_result1 = {10, 11, 9, 8}; + std::vector expected_result2 = {11, 10, 9, 8}; + EXPECT_THAT(order, AnyOf(ContainerEq(expected_result1), + ContainerEq(expected_result2))); +} + +TEST_F(CFGTest, ForEachBlockInReversePostOrderIf) { + const std::string test = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpName %main "main" +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%4 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%5 = OpConstant %uint 5 +%main = OpFunction %void None %4 +%8 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %true %9 %10 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + CFG* cfg = context->cfg(); + Module* module = context->module(); + Function* function = &*module->begin(); + std::vector order; + cfg->ForEachBlockInReversePostOrder( + &*function->begin(), + [&order](BasicBlock* bb) { order.push_back(bb->id()); }); + + std::vector expected_result = {8, 9, 10}; + EXPECT_THAT(order, ContainerEq(expected_result)); +} + +TEST_F(CFGTest, ForEachBlockInReversePostOrderLoop) { + const std::string test = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpName %main "main" +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%4 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%5 = OpConstant %uint 5 +%main = OpFunction %void None %4 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %11 %10 None +OpBranchConditional %true %11 %10 +%10 = OpLabel +OpBranch %9 +%11 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, test, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + CFG* cfg = context->cfg(); + Module* module = context->module(); + Function* function = &*module->begin(); + std::vector order; + cfg->ForEachBlockInReversePostOrder( + &*function->begin(), + [&order](BasicBlock* bb) { order.push_back(bb->id()); }); + + std::vector expected_result1 = {8, 9, 10, 11}; + std::vector expected_result2 = {8, 9, 11, 10}; + EXPECT_THAT(order, AnyOf(ContainerEq(expected_result1), + ContainerEq(expected_result2))); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/code_sink_test.cpp b/third_party/spirv-tools/test/opt/code_sink_test.cpp new file mode 100644 index 0000000..f1bd127 --- /dev/null +++ b/third_party/spirv-tools/test/opt/code_sink_test.cpp @@ -0,0 +1,557 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using CodeSinkTest = PassTest<::testing::Test>; + +TEST_F(CodeSinkTest, MoveToNextBlock) { + const std::string text = R"( +;CHECK: OpFunction +;CHECK: OpLabel +;CHECK: OpLabel +;CHECK: [[ac:%\w+]] = OpAccessChain +;CHECK: [[ld:%\w+]] = OpLoad %uint [[ac]] +;CHECK: OpCopyObject %uint [[ld]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %9 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %10 = OpTypeFunction %void + %1 = OpFunction %void None %10 + %11 = OpLabel + %12 = OpAccessChain %_ptr_Uniform_uint %9 %uint_0 + %13 = OpLoad %uint %12 + OpBranch %14 + %14 = OpLabel + %15 = OpCopyObject %uint %13 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CodeSinkTest, MovePastSelection) { + const std::string text = R"( +;CHECK: OpFunction +;CHECK: OpLabel +;CHECK: OpSelectionMerge [[merge_bb:%\w+]] +;CHECK: [[merge_bb]] = OpLabel +;CHECK: [[ac:%\w+]] = OpAccessChain +;CHECK: [[ld:%\w+]] = OpLoad %uint [[ac]] +;CHECK: OpCopyObject %uint [[ld]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpSelectionMerge %16 None + OpBranchConditional %true %17 %16 + %17 = OpLabel + OpBranch %16 + %16 = OpLabel + %18 = OpCopyObject %uint %15 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CodeSinkTest, MoveIntoSelection) { + const std::string text = R"( +;CHECK: OpFunction +;CHECK: OpLabel +;CHECK: OpSelectionMerge [[merge_bb:%\w+]] +;CHECK-NEXT: OpBranchConditional %true [[bb:%\w+]] [[merge_bb]] +;CHECK: [[bb]] = OpLabel +;CHECK-NEXT: [[ac:%\w+]] = OpAccessChain +;CHECK-NEXT: [[ld:%\w+]] = OpLoad %uint [[ac]] +;CHECK-NEXT: OpCopyObject %uint [[ld]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpSelectionMerge %16 None + OpBranchConditional %true %17 %16 + %17 = OpLabel + %18 = OpCopyObject %uint %15 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CodeSinkTest, LeaveBeforeSelection) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpSelectionMerge %16 None + OpBranchConditional %true %17 %20 + %20 = OpLabel + OpBranch %16 + %17 = OpLabel + %18 = OpCopyObject %uint %15 + OpBranch %16 + %16 = OpLabel + %19 = OpCopyObject %uint %15 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, LeaveAloneUseInSameBlock) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + %cond = OpIEqual %bool %15 %uint_0 + OpSelectionMerge %16 None + OpBranchConditional %cond %17 %16 + %17 = OpLabel + OpBranch %16 + %16 = OpLabel + %19 = OpCopyObject %uint %15 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, DontMoveIntoLoop) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpBranch %17 + %17 = OpLabel + OpLoopMerge %merge %cont None + OpBranch %cont + %cont = OpLabel + %cond = OpIEqual %bool %15 %uint_0 + OpBranchConditional %cond %merge %17 + %merge = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, DontMoveIntoLoop2) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpSelectionMerge %16 None + OpBranchConditional %true %17 %16 + %17 = OpLabel + OpLoopMerge %merge %cont None + OpBranch %cont + %cont = OpLabel + %cond = OpIEqual %bool %15 %uint_0 + OpBranchConditional %cond %merge %17 + %merge = OpLabel + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, DontMoveSelectionUsedInBothSides) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpSelectionMerge %16 None + OpBranchConditional %true %17 %20 + %20 = OpLabel + %19 = OpCopyObject %uint %15 + OpBranch %16 + %17 = OpLabel + %18 = OpCopyObject %uint %15 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, DontMoveBecauseOfStore) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpStore %14 %15 + OpSelectionMerge %16 None + OpBranchConditional %true %17 %20 + %20 = OpLabel + OpBranch %16 + %17 = OpLabel + %18 = OpCopyObject %uint %15 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, MoveReadOnlyLoadWithSync) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%_arr_uint_uint_4 = OpTypeArray %uint %uint_4 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpMemoryBarrier %uint_4 %mem_semantics + OpSelectionMerge %16 None + OpBranchConditional %true %17 %20 + %20 = OpLabel + OpBranch %16 + %17 = OpLabel + %18 = OpCopyObject %uint %15 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, DontMoveBecauseOfSync) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpDecorate %_arr_uint_uint_4 BufferBlock + OpMemberDecorate %_arr_uint_uint_4 0 Offset 0 + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%_arr_uint_uint_4 = OpTypeStruct %uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + OpMemoryBarrier %uint_4 %mem_semantics + OpSelectionMerge %16 None + OpBranchConditional %true %17 %20 + %20 = OpLabel + OpBranch %16 + %17 = OpLabel + %18 = OpCopyObject %uint %15 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, DontMoveBecauseOfAtomicWithSync) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpDecorate %_arr_uint_uint_4 BufferBlock + OpMemberDecorate %_arr_uint_uint_4 0 Offset 0 + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%mem_semantics = OpConstant %uint 0x42 ; Uniform memeory arquire +%_arr_uint_uint_4 = OpTypeStruct %uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + %al = OpAtomicLoad %uint %14 %uint_4 %mem_semantics + OpSelectionMerge %16 None + OpBranchConditional %true %17 %20 + %20 = OpLabel + OpBranch %16 + %17 = OpLabel + %18 = OpCopyObject %uint %15 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, MoveWithAtomicWithoutSync) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpDecorate %_arr_uint_uint_4 BufferBlock + OpMemberDecorate %_arr_uint_uint_4 0 Offset 0 + %void = OpTypeVoid + %bool = OpTypeBool + %true = OpConstantTrue %bool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_4 = OpConstant %uint 4 +%_arr_uint_uint_4 = OpTypeStruct %uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_Uniform__arr_uint_uint_4 = OpTypePointer Uniform %_arr_uint_uint_4 + %11 = OpVariable %_ptr_Uniform__arr_uint_uint_4 Uniform + %12 = OpTypeFunction %void + %1 = OpFunction %void None %12 + %13 = OpLabel + %14 = OpAccessChain %_ptr_Uniform_uint %11 %uint_0 + %15 = OpLoad %uint %14 + %al = OpAtomicLoad %uint %14 %uint_4 %uint_0 + OpSelectionMerge %16 None + OpBranchConditional %true %17 %20 + %20 = OpLabel + OpBranch %16 + %17 = OpLabel + %18 = OpCopyObject %uint %15 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + +TEST_F(CodeSinkTest, DecorationOnLoad) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" %2 + OpDecorate %3 RelaxedPrecision + %void = OpTypeVoid + %5 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %2 = OpVariable %_ptr_Input_float Input + %1 = OpFunction %void None %5 + %8 = OpLabel + %3 = OpLoad %float %2 + OpReturn + OpFunctionEnd +)"; + + // We just want to make sure the code does not crash. + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/combine_access_chains_test.cpp b/third_party/spirv-tools/test/opt/combine_access_chains_test.cpp new file mode 100644 index 0000000..aed14c9 --- /dev/null +++ b/third_party/spirv-tools/test/opt/combine_access_chains_test.cpp @@ -0,0 +1,773 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using CombineAccessChainsTest = PassTest<::testing::Test>; + +TEST_F(CombineAccessChainsTest, PtrAccessChainFromAccessChainConstant) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int3]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpAccessChain %ptr_Workgroup_uint %var %uint_0 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, PtrAccessChainFromInBoundsAccessChainConstant) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int3]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpInBoundsAccessChain %ptr_Workgroup_uint %var %uint_0 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, PtrAccessChainFromAccessChainCombineConstant) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[int2:%\w+]] = OpConstant [[int]] 2 +; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int2]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpAccessChain %ptr_Workgroup_uint %var %uint_1 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, PtrAccessChainFromAccessChainNonConstant) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[ld1:%\w+]] = OpLoad +; CHECK: [[ld2:%\w+]] = OpLoad +; CHECK: [[add:%\w+]] = OpIAdd [[int]] [[ld1]] [[ld2]] +; CHECK: OpAccessChain [[ptr_int]] [[var]] [[add]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Function_uint = OpTypePointer Function %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%local_var = OpVariable %ptr_Function_uint Function +%ld1 = OpLoad %uint %local_var +%gep = OpAccessChain %ptr_Workgroup_uint %var %ld1 +%ld2 = OpLoad %uint %local_var +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %ld2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, PtrAccessChainFromAccessChainExtraIndices) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int1:%\w+]] = OpConstant [[int]] 1 +; CHECK: [[int2:%\w+]] = OpConstant [[int]] 2 +; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int1]] [[int2]] [[int3]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%uint_array_4_array_4 = OpTypeArray %uint_array_4 %uint_4 +%uint_array_4_array_4_array_4 = OpTypeArray %uint_array_4_array_4 %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Function_uint = OpTypePointer Function %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%ptr_Workgroup_uint_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4 +%ptr_Workgroup_uint_array_4_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4_array_4_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpAccessChain %ptr_Workgroup_uint_array_4 %var %uint_1 %uint_0 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_2 %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, + PtrAccessChainFromPtrAccessChainCombineElementOperand) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6 +; CHECK: OpPtrAccessChain [[ptr_int]] [[var]] [[int6]] [[int3]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_3 %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, + PtrAccessChainFromPtrAccessChainOnlyElementOperand) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int4:%\w+]] = OpConstant [[int]] 4 +; CHECK: [[array:%\w+]] = OpTypeArray [[int]] [[int4]] +; CHECK: [[ptr_array:%\w+]] = OpTypePointer Workgroup [[array]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6 +; CHECK: OpPtrAccessChain [[ptr_array]] [[var]] [[int6]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %gep %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, + PtrAccessChainFromPtrAccessCombineNonElementIndex) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: OpPtrAccessChain [[ptr_int]] [[var]] [[int3]] [[int3]] [[int3]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%uint_array_4_array_4 = OpTypeArray %uint_array_4 %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Function_uint = OpTypePointer Function %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%ptr_Workgroup_uint_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3 %uint_0 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %uint_3 %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, + AccessChainFromPtrAccessChainOnlyElementOperand) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: OpPtrAccessChain [[ptr_int]] [[var]] [[int3]] [[int3]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3 +%gep = OpAccessChain %ptr_Workgroup_uint %ptr_gep %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, AccessChainFromPtrAccessChainAppend) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int1:%\w+]] = OpConstant [[int]] 1 +; CHECK: [[int2:%\w+]] = OpConstant [[int]] 2 +; CHECK: [[int3:%\w+]] = OpConstant [[int]] 3 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: OpPtrAccessChain [[ptr_int]] [[var]] [[int1]] [[int2]] [[int3]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%uint_array_4_array_4 = OpTypeArray %uint_array_4 %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%ptr_Workgroup_uint_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_1 %uint_2 +%gep = OpAccessChain %ptr_Workgroup_uint %ptr_gep %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, AccessChainFromAccessChainAppend) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int1:%\w+]] = OpConstant [[int]] 1 +; CHECK: [[int2:%\w+]] = OpConstant [[int]] 2 +; CHECK: [[ptr_int:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: OpAccessChain [[ptr_int]] [[var]] [[int1]] [[int2]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%uint_array_4_array_4 = OpTypeArray %uint_array_4 %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%ptr_Workgroup_uint_array_4_array_4 = OpTypePointer Workgroup %uint_array_4_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%ptr_gep = OpAccessChain %ptr_Workgroup_uint_array_4 %var %uint_1 +%gep = OpAccessChain %ptr_Workgroup_uint %ptr_gep %uint_2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, NonConstantStructSlide) { + const std::string text = R"( +; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0 +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[ld:%\w+]] = OpLoad +; CHECK: OpPtrAccessChain {{%\w+}} [[var]] [[ld]] [[int0]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%struct = OpTypeStruct %uint %uint +%ptr_Workgroup_struct = OpTypePointer Workgroup %struct +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Function_uint = OpTypePointer Function %uint +%wg_var = OpVariable %ptr_Workgroup_struct Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%1 = OpLabel +%func_var = OpVariable %ptr_Function_uint Function +%ld = OpLoad %uint %func_var +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_struct %wg_var %ld +%gep = OpAccessChain %ptr_Workgroup_uint %ptr_gep %uint_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, DontCombineNonConstantStructSlide) { + const std::string text = R"( +; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0 +; CHECK: [[ld:%\w+]] = OpLoad +; CHECK: [[gep:%\w+]] = OpAccessChain +; CHECK: OpPtrAccessChain {{%\w+}} [[gep]] [[ld]] [[int0]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_4 = OpConstant %uint 4 +%struct = OpTypeStruct %uint %uint +%struct_array_4 = OpTypeArray %struct %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Function_uint = OpTypePointer Function %uint +%ptr_Workgroup_struct = OpTypePointer Workgroup %struct +%ptr_Workgroup_struct_array_4 = OpTypePointer Workgroup %struct_array_4 +%wg_var = OpVariable %ptr_Workgroup_struct_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%1 = OpLabel +%func_var = OpVariable %ptr_Function_uint Function +%ld = OpLoad %uint %func_var +%gep = OpAccessChain %ptr_Workgroup_struct %wg_var %uint_0 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %ld %uint_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, CombineNonConstantStructSlideElement) { + const std::string text = R"( +; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0 +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[ld:%\w+]] = OpLoad +; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]] [[ld]] +; CHECK: OpPtrAccessChain {{%\w+}} [[var]] [[add]] [[int0]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_4 = OpConstant %uint 4 +%struct = OpTypeStruct %uint %uint +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Function_uint = OpTypePointer Function %uint +%ptr_Workgroup_struct = OpTypePointer Workgroup %struct +%wg_var = OpVariable %ptr_Workgroup_struct Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%1 = OpLabel +%func_var = OpVariable %ptr_Function_uint Function +%ld = OpLoad %uint %func_var +%gep = OpPtrAccessChain %ptr_Workgroup_struct %wg_var %ld +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint %gep %ld %uint_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, PtrAccessChainFromInBoundsPtrAccessChain) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int4:%\w+]] = OpConstant [[int]] 4 +; CHECK: [[array:%\w+]] = OpTypeArray [[int]] [[int4]] +; CHECK: [[ptr_array:%\w+]] = OpTypePointer Workgroup [[array]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6 +; CHECK: OpPtrAccessChain [[ptr_array]] [[var]] [[int6]] +OpCapability Shader +OpCapability VariablePointers +OpCapability Addresses +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpInBoundsPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3 +%ptr_gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %gep %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, InBoundsPtrAccessChainFromPtrAccessChain) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int4:%\w+]] = OpConstant [[int]] 4 +; CHECK: [[array:%\w+]] = OpTypeArray [[int]] [[int4]] +; CHECK: [[ptr_array:%\w+]] = OpTypePointer Workgroup [[array]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6 +; CHECK: OpPtrAccessChain [[ptr_array]] [[var]] [[int6]] +OpCapability Shader +OpCapability VariablePointers +OpCapability Addresses +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3 +%ptr_gep = OpInBoundsPtrAccessChain %ptr_Workgroup_uint_array_4 %gep %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, + InBoundsPtrAccessChainFromInBoundsPtrAccessChain) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[int4:%\w+]] = OpConstant [[int]] 4 +; CHECK: [[array:%\w+]] = OpTypeArray [[int]] [[int4]] +; CHECK: [[ptr_array:%\w+]] = OpTypePointer Workgroup [[array]] +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Workgroup +; CHECK: [[int6:%\w+]] = OpConstant [[int]] 6 +; CHECK: OpInBoundsPtrAccessChain [[ptr_array]] [[var]] [[int6]] +OpCapability Shader +OpCapability VariablePointers +OpCapability Addresses +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_array_4 = OpTypeArray %uint %uint_4 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%ptr_Workgroup_uint_array_4 = OpTypePointer Workgroup %uint_array_4 +%var = OpVariable %ptr_Workgroup_uint_array_4 Workgroup +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +%gep = OpInBoundsPtrAccessChain %ptr_Workgroup_uint_array_4 %var %uint_3 +%ptr_gep = OpInBoundsPtrAccessChain %ptr_Workgroup_uint_array_4 %gep %uint_3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, NoIndexAccessChains) { + const std::string text = R"( +; CHECK: [[var:%\w+]] = OpVariable +; CHECK-NOT: OpConstant +; CHECK: [[gep:%\w+]] = OpAccessChain {{%\w+}} [[var]] +; CHECK: OpAccessChain {{%\w+}} [[var]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%var = OpVariable %ptr_Workgroup_uint Workgroup +%void_func = OpTypeFunction %void +%func = OpFunction %void None %void_func +%1 = OpLabel +%gep1 = OpAccessChain %ptr_Workgroup_uint %var +%gep2 = OpAccessChain %ptr_Workgroup_uint %gep1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, NoIndexPtrAccessChains) { + const std::string text = R"( +; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0 +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[gep:%\w+]] = OpPtrAccessChain {{%\w+}} [[var]] [[int0]] +; CHECK: OpCopyObject {{%\w+}} [[gep]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%var = OpVariable %ptr_Workgroup_uint Workgroup +%void_func = OpTypeFunction %void +%func = OpFunction %void None %void_func +%1 = OpLabel +%gep1 = OpPtrAccessChain %ptr_Workgroup_uint %var %uint_0 +%gep2 = OpAccessChain %ptr_Workgroup_uint %gep1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, NoIndexPtrAccessChains2) { + const std::string text = R"( +; CHECK: [[int0:%\w+]] = OpConstant {{%\w+}} 0 +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: OpPtrAccessChain {{%\w+}} [[var]] [[int0]] +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%var = OpVariable %ptr_Workgroup_uint Workgroup +%void_func = OpTypeFunction %void +%func = OpFunction %void None %void_func +%1 = OpLabel +%gep1 = OpAccessChain %ptr_Workgroup_uint %var +%gep2 = OpPtrAccessChain %ptr_Workgroup_uint %gep1 %uint_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(CombineAccessChainsTest, CombineMixedSign) { + const std::string text = R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[uint2:%\w+]] = OpConstant [[uint]] 2 +; CHECK: OpInBoundsPtrAccessChain {{%\w+}} [[var]] [[uint2]] +OpCapability Shader +OpCapability VariablePointers +OpCapability Addresses +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%int = OpTypeInt 32 1 +%uint_1 = OpConstant %uint 1 +%int_1 = OpConstant %int 1 +%ptr_Workgroup_uint = OpTypePointer Workgroup %uint +%var = OpVariable %ptr_Workgroup_uint Workgroup +%void_func = OpTypeFunction %void +%func = OpFunction %void None %void_func +%1 = OpLabel +%gep1 = OpInBoundsPtrAccessChain %ptr_Workgroup_uint %var %uint_1 +%gep2 = OpInBoundsPtrAccessChain %ptr_Workgroup_uint %gep1 %int_1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/compact_ids_test.cpp b/third_party/spirv-tools/test/opt/compact_ids_test.cpp new file mode 100644 index 0000000..ba31d84 --- /dev/null +++ b/third_party/spirv-tools/test/opt/compact_ids_test.cpp @@ -0,0 +1,315 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using CompactIdsTest = PassTest<::testing::Test>; + +TEST_F(CompactIdsTest, PassOff) { + const std::string before = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +%99 = OpTypeInt 32 0 +%10 = OpTypeVector %99 2 +%20 = OpConstant %99 2 +%30 = OpTypeArray %99 %20 +)"; + + const std::string after = before; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, false); +} + +TEST_F(CompactIdsTest, PassOn) { + const std::string before = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %3 "simple_kernel" +%99 = OpTypeInt 32 0 +%10 = OpTypeVector %99 2 +%20 = OpConstant %99 2 +%30 = OpTypeArray %99 %20 +%40 = OpTypeVoid +%50 = OpTypeFunction %40 + %3 = OpFunction %40 None %50 +%70 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %1 "simple_kernel" +%2 = OpTypeInt 32 0 +%3 = OpTypeVector %2 2 +%4 = OpConstant %2 2 +%5 = OpTypeArray %2 %4 +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%1 = OpFunction %6 None %7 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, false); +} + +TEST_F(CompactIdsTest, DebugScope) { + const std::string text = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +%5 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %3 "simple_kernel" +%2 = OpString "test" +%99 = OpTypeInt 32 0 +%10 = OpTypeVector %99 2 +%20 = OpConstant %99 2 +%30 = OpTypeArray %99 %20 +%40 = OpTypeVoid +%50 = OpTypeFunction %40 +%11 = OpExtInst %40 %5 DebugSource %2 +%12 = OpExtInst %40 %5 DebugCompilationUnit 1 4 %11 HLSL +%13 = OpExtInst %40 %5 DebugTypeFunction FlagIsProtected|FlagIsPrivate %40 + +; CHECK: [[fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction +%14 = OpExtInst %40 %5 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %3 + %3 = OpFunction %40 None %50 +%70 = OpLabel + +; CHECK: DebugScope [[fn]] +%19 = OpExtInst %40 %5 DebugScope %14 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndMatch(text, true); +} + +TEST(CompactIds, InstructionResultIsUpdated) { + // For https://github.com/KhronosGroup/SPIRV-Tools/issues/827 + // In that bug, the compact Ids pass was directly updating the result Id + // word for an OpFunction instruction, but not updating the cached + // result_id_ in that Instruction object. + // + // This test is a bit cheesy. We don't expose internal interfaces enough + // to see the inconsistency. So reproduce the original scenario, with + // compact ids followed by a pass that trips up on the inconsistency. + + const std::string input(R"(OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %100 "main" +%200 = OpTypeVoid +%300 = OpTypeFunction %200 +%100 = OpFunction %200 None %300 +%400 = OpLabel +OpReturn +OpFunctionEnd +)"); + + std::vector binary; + const spv_target_env env = SPV_ENV_UNIVERSAL_1_0; + spvtools::SpirvTools tools(env); + auto assembled = tools.Assemble( + input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE(assembled); + + spvtools::Optimizer optimizer(env); + optimizer.RegisterPass(CreateCompactIdsPass()); + // The exhaustive inliner will use the result_id + optimizer.RegisterPass(CreateInlineExhaustivePass()); + + // This should not crash! + optimizer.Run(binary.data(), binary.size(), &binary); + + std::string disassembly; + tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + const std::string expected(R"(OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %1 "main" +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"); + + EXPECT_THAT(disassembly, ::testing::Eq(expected)); +} + +TEST(CompactIds, HeaderIsUpdated) { + const std::string input(R"(OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %100 "main" +%200 = OpTypeVoid +%300 = OpTypeFunction %200 +%100 = OpFunction %200 None %300 +%400 = OpLabel +OpReturn +OpFunctionEnd +)"); + + std::vector binary; + const spv_target_env env = SPV_ENV_UNIVERSAL_1_0; + spvtools::SpirvTools tools(env); + auto assembled = tools.Assemble( + input, &binary, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_TRUE(assembled); + + spvtools::Optimizer optimizer(env); + optimizer.RegisterPass(CreateCompactIdsPass()); + // The exhaustive inliner will use the result_id + optimizer.RegisterPass(CreateInlineExhaustivePass()); + + // This should not crash! + optimizer.Run(binary.data(), binary.size(), &binary); + + std::string disassembly; + tools.Disassemble(binary, &disassembly, SPV_BINARY_TO_TEXT_OPTION_NONE); + + const std::string expected(R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 5 +; Schema: 0 +OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %1 "main" +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"); + + EXPECT_THAT(disassembly, ::testing::Eq(expected)); +} + +// Test context consistency check after invalidating +// CFG and others by compact IDs Pass. +// Uses a GLSL shader with named labels for variety +TEST(CompactIds, ConsistentCheck) { + const std::string input(R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %main "main" +OpName %in_var_A "in.var.A" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpDecorate %in_var_A Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%in_var_A = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %3 +%5 = OpLabel +%12 = OpLoad %v4float %in_var_A +%23 = OpVectorShuffle %v4float %12 %12 0 0 0 1 +OpStore %out_var_SV_TARGET %23 +OpReturn +OpFunctionEnd +)"); + + spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + CompactIdsPass compact_id_pass; + context->BuildInvalidAnalyses(compact_id_pass.GetPreservedAnalyses()); + const auto status = compact_id_pass.Run(context.get()); + ASSERT_NE(status, Pass::Status::Failure); + EXPECT_TRUE(context->IsConsistent()); + + // Test output just in case + std::vector binary; + context->module()->ToBinary(&binary, false); + std::string disassembly; + tools.Disassemble(binary, &disassembly, + SpirvTools::kDefaultDisassembleOption); + + const std::string expected(R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_A %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %main "main" +OpName %in_var_A "in.var.A" +OpName %out_var_SV_TARGET "out.var.SV_TARGET" +OpDecorate %in_var_A Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%in_var_A = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %5 +%10 = OpLabel +%11 = OpLoad %v4float %in_var_A +%12 = OpVectorShuffle %v4float %11 %11 0 0 0 1 +OpStore %out_var_SV_TARGET %12 +OpReturn +OpFunctionEnd +)"); + + EXPECT_THAT(disassembly, ::testing::Eq(expected)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/constant_manager_test.cpp b/third_party/spirv-tools/test/opt/constant_manager_test.cpp new file mode 100644 index 0000000..14e14ec --- /dev/null +++ b/third_party/spirv-tools/test/opt/constant_manager_test.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/constants.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +using ConstantManagerTest = ::testing::Test; + +TEST_F(ConstantManagerTest, GetDefiningInstruction) { + const std::string text = R"( +%int = OpTypeInt 32 0 +%1 = OpTypeStruct %int +%2 = OpTypeStruct %int + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + Type* struct_type_1 = context->get_type_mgr()->GetType(1); + StructConstant struct_const_1(struct_type_1->AsStruct()); + Instruction* const_inst_1 = + context->get_constant_mgr()->GetDefiningInstruction(&struct_const_1, 1); + EXPECT_EQ(const_inst_1->type_id(), 1); + + Type* struct_type_2 = context->get_type_mgr()->GetType(2); + StructConstant struct_const_2(struct_type_2->AsStruct()); + Instruction* const_inst_2 = + context->get_constant_mgr()->GetDefiningInstruction(&struct_const_2, 2); + EXPECT_EQ(const_inst_2->type_id(), 2); +} + +TEST_F(ConstantManagerTest, GetDefiningInstruction2) { + const std::string text = R"( +%int = OpTypeInt 32 0 +%1 = OpTypeStruct %int +%2 = OpTypeStruct %int +%3 = OpConstantNull %1 +%4 = OpConstantNull %2 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + Type* struct_type_1 = context->get_type_mgr()->GetType(1); + NullConstant struct_const_1(struct_type_1->AsStruct()); + Instruction* const_inst_1 = + context->get_constant_mgr()->GetDefiningInstruction(&struct_const_1, 1); + EXPECT_EQ(const_inst_1->type_id(), 1); + EXPECT_EQ(const_inst_1->result_id(), 3); + + Type* struct_type_2 = context->get_type_mgr()->GetType(2); + NullConstant struct_const_2(struct_type_2->AsStruct()); + Instruction* const_inst_2 = + context->get_constant_mgr()->GetDefiningInstruction(&struct_const_2, 2); + EXPECT_EQ(const_inst_2->type_id(), 2); + EXPECT_EQ(const_inst_2->result_id(), 4); +} + +TEST_F(ConstantManagerTest, GetDefiningInstructionIdOverflow) { + const std::string text = R"( +%1 = OpTypeInt 32 0 +%3 = OpConstant %1 1 +%4 = OpConstant %1 2 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(context, nullptr); + + // Set the id bound to the max, so the new constant cannot be generated. + context->module()->SetIdBound(context->max_id_bound()); + + Type* int_type = context->get_type_mgr()->GetType(1); + IntConstant int_constant(int_type->AsInteger(), {3}); + Instruction* inst = + context->get_constant_mgr()->GetDefiningInstruction(&int_constant, 1); + EXPECT_EQ(inst, nullptr); +} + +} // namespace +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/constants_test.cpp b/third_party/spirv-tools/test/opt/constants_test.cpp new file mode 100644 index 0000000..55c92a5 --- /dev/null +++ b/third_party/spirv-tools/test/opt/constants_test.cpp @@ -0,0 +1,167 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/constants.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +using ConstantTest = ::testing::Test; +using ::testing::ValuesIn; + +template +struct GetExtendedValueCase { + bool is_signed; + int width; + std::vector words; + T expected_value; +}; + +using GetSignExtendedValueCase = GetExtendedValueCase; +using GetZeroExtendedValueCase = GetExtendedValueCase; + +using GetSignExtendedValueTest = + ::testing::TestWithParam; +using GetZeroExtendedValueTest = + ::testing::TestWithParam; + +TEST_P(GetSignExtendedValueTest, Case) { + Integer type(GetParam().width, GetParam().is_signed); + IntConstant value(&type, GetParam().words); + + EXPECT_EQ(GetParam().expected_value, value.GetSignExtendedValue()); +} + +TEST_P(GetZeroExtendedValueTest, Case) { + Integer type(GetParam().width, GetParam().is_signed); + IntConstant value(&type, GetParam().words); + + EXPECT_EQ(GetParam().expected_value, value.GetZeroExtendedValue()); +} + +const uint32_t k32ones = ~uint32_t(0); +const uint64_t k64ones = ~uint64_t(0); +const int64_t kSBillion = 1000 * 1000 * 1000; +const uint64_t kUBillion = 1000 * 1000 * 1000; + +INSTANTIATE_TEST_SUITE_P(AtMost32Bits, GetSignExtendedValueTest, + ValuesIn(std::vector{ + // 4 bits + {false, 4, {0}, 0}, + {false, 4, {7}, 7}, + {false, 4, {15}, 15}, + {true, 4, {0}, 0}, + {true, 4, {7}, 7}, + {true, 4, {0xfffffff8}, -8}, + {true, 4, {k32ones}, -1}, + // 16 bits + {false, 16, {0}, 0}, + {false, 16, {32767}, 32767}, + {false, 16, {32768}, 32768}, + {false, 16, {65000}, 65000}, + {true, 16, {0}, 0}, + {true, 16, {32767}, 32767}, + {true, 16, {0xfffffff8}, -8}, + {true, 16, {k32ones}, -1}, + // 32 bits + {false, 32, {0}, 0}, + {false, 32, {1000000}, 1000000}, + {true, 32, {0xfffffff8}, -8}, + {true, 32, {k32ones}, -1}, + })); + +INSTANTIATE_TEST_SUITE_P(AtMost64Bits, GetSignExtendedValueTest, + ValuesIn(std::vector{ + // 48 bits + {false, 48, {0, 0}, 0}, + {false, 48, {5, 0}, 5}, + {false, 48, {0xfffffff8, k32ones}, -8}, + {false, 48, {k32ones, k32ones}, -1}, + {false, 48, {0xdcd65000, 1}, 8 * kSBillion}, + {true, 48, {0xfffffff8, k32ones}, -8}, + {true, 48, {k32ones, k32ones}, -1}, + {true, 48, {0xdcd65000, 1}, 8 * kSBillion}, + + // 64 bits + {false, 64, {12, 0}, 12}, + {false, 64, {0xdcd65000, 1}, 8 * kSBillion}, + {false, 48, {0xfffffff8, k32ones}, -8}, + {false, 64, {k32ones, k32ones}, -1}, + {true, 64, {12, 0}, 12}, + {true, 64, {0xdcd65000, 1}, 8 * kSBillion}, + {true, 48, {0xfffffff8, k32ones}, -8}, + {true, 64, {k32ones, k32ones}, -1}, + })); + +INSTANTIATE_TEST_SUITE_P(AtMost32Bits, GetZeroExtendedValueTest, + ValuesIn(std::vector{ + // 4 bits + {false, 4, {0}, 0}, + {false, 4, {7}, 7}, + {false, 4, {15}, 15}, + {true, 4, {0}, 0}, + {true, 4, {7}, 7}, + {true, 4, {0xfffffff8}, 0xfffffff8}, + {true, 4, {k32ones}, k32ones}, + // 16 bits + {false, 16, {0}, 0}, + {false, 16, {32767}, 32767}, + {false, 16, {32768}, 32768}, + {false, 16, {65000}, 65000}, + {true, 16, {0}, 0}, + {true, 16, {32767}, 32767}, + {true, 16, {0xfffffff8}, 0xfffffff8}, + {true, 16, {k32ones}, k32ones}, + // 32 bits + {false, 32, {0}, 0}, + {false, 32, {1000000}, 1000000}, + {true, 32, {0xfffffff8}, 0xfffffff8}, + {true, 32, {k32ones}, k32ones}, + })); + +INSTANTIATE_TEST_SUITE_P(AtMost64Bits, GetZeroExtendedValueTest, + ValuesIn(std::vector{ + // 48 bits + {false, 48, {0, 0}, 0}, + {false, 48, {5, 0}, 5}, + {false, 48, {0xfffffff8, k32ones}, uint64_t(-8)}, + {false, 48, {k32ones, k32ones}, uint64_t(-1)}, + {false, 48, {0xdcd65000, 1}, 8 * kUBillion}, + {true, 48, {0xfffffff8, k32ones}, uint64_t(-8)}, + {true, 48, {k32ones, k32ones}, uint64_t(-1)}, + {true, 48, {0xdcd65000, 1}, 8 * kUBillion}, + + // 64 bits + {false, 64, {12, 0}, 12}, + {false, 64, {0xdcd65000, 1}, 8 * kUBillion}, + {false, 48, {0xfffffff8, k32ones}, uint64_t(-8)}, + {false, 64, {k32ones, k32ones}, k64ones}, + {true, 64, {12, 0}, 12}, + {true, 64, {0xdcd65000, 1}, 8 * kUBillion}, + {true, 48, {0xfffffff8, k32ones}, uint64_t(-8)}, + {true, 64, {k32ones, k32ones}, k64ones}, + })); + +} // namespace +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/convert_relaxed_to_half_test.cpp b/third_party/spirv-tools/test/opt/convert_relaxed_to_half_test.cpp new file mode 100644 index 0000000..c138154 --- /dev/null +++ b/third_party/spirv-tools/test/opt/convert_relaxed_to_half_test.cpp @@ -0,0 +1,1336 @@ +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Convert Relaxed to Half tests + +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ConvertToHalfTest = PassTest<::testing::Test>; + +TEST_F(ConvertToHalfTest, ConvertToHalfBasic) { + // The resulting SPIR-V was processed with --relax-float-ops. + // + // clang-format off + // + // SamplerState g_sSamp : register(s0); + // uniform Texture1D g_tTex1df4 : register(t0); + // + // struct PS_INPUT + // { + // float Tex0 : TEXCOORD0; + // }; + // + // struct PS_OUTPUT + // { + // float4 Color : SV_Target0; + // }; + // + // cbuffer cbuff{ + // float c; + // } + // + // PS_OUTPUT main(PS_INPUT i) + // { + // PS_OUTPUT psout; + // psout.Color = g_tTex1df4.Sample(g_sSamp, i.Tex0) * c; + // return psout; + // } + // + // clang-format on + + const std::string defs_before = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i_Tex0 %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "c" +OpName %_ "" +OpName %i_Tex0 "i.Tex0" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpMemberDecorate %cbuff 0 Offset 0 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %i_Tex0 Location 0 +OpDecorate %_entryPointOutput_Color Location 0 +OpDecorate %48 RelaxedPrecision +OpDecorate %63 RelaxedPrecision +OpDecorate %65 RelaxedPrecision +OpDecorate %66 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%19 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_19 UniformConstant +%23 = OpTypeSampler +%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 +%g_sSamp = OpVariable %_ptr_UniformConstant_23 UniformConstant +%27 = OpTypeSampledImage %19 +%cbuff = OpTypeStruct %float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability Sampled1D +OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i_Tex0 %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "c" +OpName %_ "" +OpName %i_Tex0 "i.Tex0" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpMemberDecorate %cbuff 0 Offset 0 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %i_Tex0 Location 0 +OpDecorate %_entryPointOutput_Color Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%19 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_19 UniformConstant +%23 = OpTypeSampler +%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 +%g_sSamp = OpVariable %_ptr_UniformConstant_23 UniformConstant +%27 = OpTypeSampledImage %19 +%cbuff = OpTypeStruct %float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%half = OpTypeFloat 16 +%v4half = OpTypeVector %half 4 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%48 = OpLoad %float %i_Tex0 +%58 = OpLoad %19 %g_tTex1df4 +%59 = OpLoad %23 %g_sSamp +%60 = OpSampledImage %27 %58 %59 +%63 = OpImageSampleImplicitLod %v4float %60 %48 +%64 = OpAccessChain %_ptr_Uniform_float %_ %int_0 +%65 = OpLoad %float %64 +%66 = OpVectorTimesScalar %v4float %63 %65 +OpStore %_entryPointOutput_Color %66 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%48 = OpLoad %float %i_Tex0 +%58 = OpLoad %19 %g_tTex1df4 +%59 = OpLoad %23 %g_sSamp +%60 = OpSampledImage %27 %58 %59 +%63 = OpImageSampleImplicitLod %v4float %60 %48 +%64 = OpAccessChain %_ptr_Uniform_float %_ %int_0 +%65 = OpLoad %float %64 +%69 = OpFConvert %v4half %63 +%70 = OpFConvert %half %65 +%66 = OpVectorTimesScalar %v4half %69 %70 +%71 = OpFConvert %v4float %66 +OpStore %_entryPointOutput_Color %71 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(ConvertToHalfTest, ConvertToHalfWithDrefSample) { + // The resulting SPIR-V was processed with --relax-float-ops. + // + // clang-format off + // + // SamplerComparisonState g_sSamp : register(s0); + // uniform Texture1D g_tTex1df4 : register(t0); + // + // cbuffer cbuff{ + // float c1; + // float c2; + // }; + // + // struct PS_INPUT + // { + // float Tex0 : TEXCOORD0; + // float Tex1 : TEXCOORD1; + // }; + // + // struct PS_OUTPUT + // { + // float Color : SV_Target0; + // }; + // + // PS_OUTPUT main(PS_INPUT i) + // { + // PS_OUTPUT psout; + // float txval10 = g_tTex1df4.SampleCmp(g_sSamp, i.Tex0 * 0.1, c1 + 0.1); + // float txval11 = g_tTex1df4.SampleCmp(g_sSamp, i.Tex1 * 0.2, c2 + 0.2); + // float t = txval10 + txval11; + // float t2 = t / 2.0; + // psout.Color = t2; + // return psout; + // } + // + // clang-format on + + const std::string defs_before = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i_Tex0 %i_Tex1 %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "c1" +OpMemberName %cbuff 1 "c2" +OpName %_ "" +OpName %i_Tex0 "i.Tex0" +OpName %i_Tex1 "i.Tex1" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpMemberDecorate %cbuff 0 Offset 0 +OpMemberDecorate %cbuff 1 Offset 4 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %i_Tex0 Location 0 +OpDecorate %i_Tex1 Location 1 +OpDecorate %_entryPointOutput_Color Location 0 +OpDecorate %100 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +OpDecorate %79 RelaxedPrecision +OpDecorate %98 RelaxedPrecision +OpDecorate %101 RelaxedPrecision +OpDecorate %110 RelaxedPrecision +OpDecorate %102 RelaxedPrecision +OpDecorate %112 RelaxedPrecision +OpDecorate %104 RelaxedPrecision +OpDecorate %113 RelaxedPrecision +OpDecorate %114 RelaxedPrecision +OpDecorate %116 RelaxedPrecision +OpDecorate %119 RelaxedPrecision +OpDecorate %121 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%16 = OpTypeImage %float 1D 1 0 0 1 Unknown +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_16 UniformConstant +%20 = OpTypeSampler +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 +%g_sSamp = OpVariable %_ptr_UniformConstant_20 UniformConstant +%24 = OpTypeSampledImage %16 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float_0_100000001 = OpConstant %float 0.100000001 +%cbuff = OpTypeStruct %float %float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%v2float = OpTypeVector %float 2 +%int_1 = OpConstant %int 1 +%float_0_200000003 = OpConstant %float 0.200000003 +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%i_Tex1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_float = OpTypePointer Output %float +%_entryPointOutput_Color = OpVariable %_ptr_Output_float Output +%float_0_5 = OpConstant %float 0.5 +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability Sampled1D +OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i_Tex0 %i_Tex1 %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "c1" +OpMemberName %cbuff 1 "c2" +OpName %_ "" +OpName %i_Tex0 "i.Tex0" +OpName %i_Tex1 "i.Tex1" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpMemberDecorate %cbuff 0 Offset 0 +OpMemberDecorate %cbuff 1 Offset 4 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %i_Tex0 Location 0 +OpDecorate %i_Tex1 Location 1 +OpDecorate %_entryPointOutput_Color Location 0 +%void = OpTypeVoid +%25 = OpTypeFunction %void +%float = OpTypeFloat 32 +%27 = OpTypeImage %float 1D 1 0 0 1 Unknown +%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_27 UniformConstant +%29 = OpTypeSampler +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 +%g_sSamp = OpVariable %_ptr_UniformConstant_29 UniformConstant +%31 = OpTypeSampledImage %27 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%float_0_100000001 = OpConstant %float 0.100000001 +%cbuff = OpTypeStruct %float %float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%v2float = OpTypeVector %float 2 +%int_1 = OpConstant %int 1 +%float_0_200000003 = OpConstant %float 0.200000003 +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%i_Tex1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_float = OpTypePointer Output %float +%_entryPointOutput_Color = OpVariable %_ptr_Output_float Output +%float_0_5 = OpConstant %float 0.5 +%half = OpTypeFloat 16 +%v2half = OpTypeVector %half 2 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%76 = OpLoad %float %i_Tex0 +%79 = OpLoad %float %i_Tex1 +%93 = OpLoad %16 %g_tTex1df4 +%94 = OpLoad %20 %g_sSamp +%95 = OpSampledImage %24 %93 %94 +%98 = OpFMul %float %76 %float_0_100000001 +%99 = OpAccessChain %_ptr_Uniform_float %_ %int_0 +%100 = OpLoad %float %99 +%101 = OpFAdd %float %100 %float_0_100000001 +%102 = OpCompositeConstruct %v2float %98 %101 +%104 = OpImageSampleDrefImplicitLod %float %95 %102 %101 +%105 = OpLoad %16 %g_tTex1df4 +%106 = OpLoad %20 %g_sSamp +%107 = OpSampledImage %24 %105 %106 +%110 = OpFMul %float %79 %float_0_200000003 +%111 = OpAccessChain %_ptr_Uniform_float %_ %int_1 +%112 = OpLoad %float %111 +%113 = OpFAdd %float %112 %float_0_200000003 +%114 = OpCompositeConstruct %v2float %110 %113 +%116 = OpImageSampleDrefImplicitLod %float %107 %114 %113 +%119 = OpFAdd %float %104 %116 +%121 = OpFMul %float %119 %float_0_5 +OpStore %_entryPointOutput_Color %121 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %25 +%43 = OpLabel +%11 = OpLoad %float %i_Tex0 +%12 = OpLoad %float %i_Tex1 +%44 = OpLoad %27 %g_tTex1df4 +%45 = OpLoad %29 %g_sSamp +%46 = OpSampledImage %31 %44 %45 +%53 = OpFConvert %half %11 +%54 = OpFConvert %half %float_0_100000001 +%13 = OpFMul %half %53 %54 +%47 = OpAccessChain %_ptr_Uniform_float %_ %int_0 +%10 = OpLoad %float %47 +%55 = OpFConvert %half %10 +%56 = OpFConvert %half %float_0_100000001 +%14 = OpFAdd %half %55 %56 +%16 = OpCompositeConstruct %v2half %13 %14 +%58 = OpFConvert %float %14 +%18 = OpImageSampleDrefImplicitLod %float %46 %16 %58 +%48 = OpLoad %27 %g_tTex1df4 +%49 = OpLoad %29 %g_sSamp +%50 = OpSampledImage %31 %48 %49 +%59 = OpFConvert %half %12 +%60 = OpFConvert %half %float_0_200000003 +%15 = OpFMul %half %59 %60 +%51 = OpAccessChain %_ptr_Uniform_float %_ %int_1 +%17 = OpLoad %float %51 +%61 = OpFConvert %half %17 +%62 = OpFConvert %half %float_0_200000003 +%19 = OpFAdd %half %61 %62 +%20 = OpCompositeConstruct %v2half %15 %19 +%63 = OpFConvert %float %19 +%21 = OpImageSampleDrefImplicitLod %float %50 %20 %63 +%64 = OpFConvert %half %18 +%65 = OpFConvert %half %21 +%22 = OpFAdd %half %64 %65 +%66 = OpFConvert %half %float_0_5 +%23 = OpFMul %half %22 %66 +%67 = OpFConvert %float %23 +OpStore %_entryPointOutput_Color %67 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(ConvertToHalfTest, ConvertToHalfWithVectorMatrixMult) { + // The resulting SPIR-V was processed with --relax-float-ops. + // + // clang-format off + // + // SamplerState g_sSamp : register(s0); + // uniform Texture1D g_tTex1df4 : register(t0); + // + // struct PS_OUTPUT + // { + // float4 Color : SV_Target0; + // }; + // + // cbuffer cbuff{ + // float4x4 M; + // } + // + // PS_OUTPUT main() + // { + // PS_OUTPUT psout; + // float4 txval10 = g_tTex1df4.Sample(g_sSamp, 0.1); + // float4 t = mul(txval10, M); + // psout.Color = t; + // return psout; + //} + // + // clang-format on + + const std::string defs_before = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "M" +OpName %_ "" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpMemberDecorate %cbuff 0 RowMajor +OpMemberDecorate %cbuff 0 Offset 0 +OpMemberDecorate %cbuff 0 MatrixStride 16 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %_entryPointOutput_Color Location 0 +OpDecorate %56 RelaxedPrecision +OpDecorate %58 RelaxedPrecision +OpDecorate %60 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%14 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_14 UniformConstant +%18 = OpTypeSampler +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%g_sSamp = OpVariable %_ptr_UniformConstant_18 UniformConstant +%22 = OpTypeSampledImage %14 +%float_0_100000001 = OpConstant %float 0.100000001 +%mat4v4float = OpTypeMatrix %v4float 4 +%cbuff = OpTypeStruct %mat4v4float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability Sampled1D +OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "M" +OpName %_ "" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpMemberDecorate %cbuff 0 RowMajor +OpMemberDecorate %cbuff 0 Offset 0 +OpMemberDecorate %cbuff 0 MatrixStride 16 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %_entryPointOutput_Color Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%14 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_14 UniformConstant +%18 = OpTypeSampler +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%g_sSamp = OpVariable %_ptr_UniformConstant_18 UniformConstant +%22 = OpTypeSampledImage %14 +%float_0_100000001 = OpConstant %float 0.100000001 +%mat4v4float = OpTypeMatrix %v4float 4 +%cbuff = OpTypeStruct %mat4v4float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_mat4v4float = OpTypePointer Uniform %mat4v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%half = OpTypeFloat 16 +%v4half = OpTypeVector %half 4 +%mat4v4half = OpTypeMatrix %v4half 4 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %14 %g_tTex1df4 +%54 = OpLoad %18 %g_sSamp +%55 = OpSampledImage %22 %53 %54 +%56 = OpImageSampleImplicitLod %v4float %55 %float_0_100000001 +%57 = OpAccessChain %_ptr_Uniform_mat4v4float %_ %int_0 +%58 = OpLoad %mat4v4float %57 +%60 = OpMatrixTimesVector %v4float %58 %56 +OpStore %_entryPointOutput_Color %60 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %14 %g_tTex1df4 +%54 = OpLoad %18 %g_sSamp +%55 = OpSampledImage %22 %53 %54 +%56 = OpImageSampleImplicitLod %v4float %55 %float_0_100000001 +%57 = OpAccessChain %_ptr_Uniform_mat4v4float %_ %int_0 +%58 = OpLoad %mat4v4float %57 +%67 = OpCompositeExtract %v4float %58 0 +%68 = OpFConvert %v4half %67 +%69 = OpCompositeExtract %v4float %58 1 +%70 = OpFConvert %v4half %69 +%71 = OpCompositeExtract %v4float %58 2 +%72 = OpFConvert %v4half %71 +%73 = OpCompositeExtract %v4float %58 3 +%74 = OpFConvert %v4half %73 +%75 = OpCompositeConstruct %mat4v4half %68 %70 %72 %74 +%64 = OpCopyObject %mat4v4float %58 +%65 = OpFConvert %v4half %56 +%60 = OpMatrixTimesVector %v4half %75 %65 +%66 = OpFConvert %v4float %60 +OpStore %_entryPointOutput_Color %66 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(ConvertToHalfTest, ConvertToHalfWithPhi) { + // The resulting SPIR-V was processed with --relax-float-ops. + // + // clang-format off + // + // SamplerState g_sSamp : register(s0); + // uniform Texture1D g_tTex1df4 : register(t0); + // + // struct PS_OUTPUT + // { + // float4 Color : SV_Target0; + // }; + // + // cbuffer cbuff{ + // bool b; + // float4x4 M; + // } + // + // PS_OUTPUT main() + // { + // PS_OUTPUT psout; + // float4 t; + // + // if (b) + // t = g_tTex1df4.Sample(g_sSamp, 0.1); + // else + // t = float4(0.0, 0.0, 0.0, 0.0); + // + // float4 t2 = t * 2.0; + // psout.Color = t2; + // return psout; + // } + // + // clang-format on + + const std::string defs_before = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "b" +OpMemberName %cbuff 1 "M" +OpName %_ "" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpMemberDecorate %cbuff 0 Offset 0 +OpMemberDecorate %cbuff 1 RowMajor +OpMemberDecorate %cbuff 1 Offset 16 +OpMemberDecorate %cbuff 1 MatrixStride 16 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpDecorate %_entryPointOutput_Color Location 0 +OpDecorate %72 RelaxedPrecision +OpDecorate %85 RelaxedPrecision +OpDecorate %74 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%mat4v4float = OpTypeMatrix %v4float 4 +%cbuff = OpTypeStruct %uint %mat4v4float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%29 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_29 UniformConstant +%33 = OpTypeSampler +%_ptr_UniformConstant_33 = OpTypePointer UniformConstant %33 +%g_sSamp = OpVariable %_ptr_UniformConstant_33 UniformConstant +%37 = OpTypeSampledImage %29 +%float_0_100000001 = OpConstant %float 0.100000001 +%float_0 = OpConstant %float 0 +%43 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_2 = OpConstant %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability Sampled1D +OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "b" +OpMemberName %cbuff 1 "M" +OpName %_ "" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpMemberDecorate %cbuff 0 Offset 0 +OpMemberDecorate %cbuff 1 RowMajor +OpMemberDecorate %cbuff 1 Offset 16 +OpMemberDecorate %cbuff 1 MatrixStride 16 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpDecorate %_entryPointOutput_Color Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%mat4v4float = OpTypeMatrix %v4float 4 +%cbuff = OpTypeStruct %uint %mat4v4float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%29 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_29 = OpTypePointer UniformConstant %29 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_29 UniformConstant +%33 = OpTypeSampler +%_ptr_UniformConstant_33 = OpTypePointer UniformConstant %33 +%g_sSamp = OpVariable %_ptr_UniformConstant_33 UniformConstant +%37 = OpTypeSampledImage %29 +%float_0_100000001 = OpConstant %float 0.100000001 +%float_0 = OpConstant %float 0 +%43 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_2 = OpConstant %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%half = OpTypeFloat 16 +%v4half = OpTypeVector %half 4 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%63 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpINotEqual %bool %64 %uint_0 +OpSelectionMerge %66 None +OpBranchConditional %65 %67 %68 +%67 = OpLabel +%69 = OpLoad %29 %g_tTex1df4 +%70 = OpLoad %33 %g_sSamp +%71 = OpSampledImage %37 %69 %70 +%72 = OpImageSampleImplicitLod %v4float %71 %float_0_100000001 +OpBranch %66 +%68 = OpLabel +OpBranch %66 +%66 = OpLabel +%85 = OpPhi %v4float %72 %67 %43 %68 +%74 = OpVectorTimesScalar %v4float %85 %float_2 +OpStore %_entryPointOutput_Color %74 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%63 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpINotEqual %bool %64 %uint_0 +OpSelectionMerge %66 None +OpBranchConditional %65 %67 %68 +%67 = OpLabel +%69 = OpLoad %29 %g_tTex1df4 +%70 = OpLoad %33 %g_sSamp +%71 = OpSampledImage %37 %69 %70 +%72 = OpImageSampleImplicitLod %v4float %71 %float_0_100000001 +%88 = OpFConvert %v4half %72 +OpBranch %66 +%68 = OpLabel +%89 = OpFConvert %v4half %43 +OpBranch %66 +%66 = OpLabel +%85 = OpPhi %v4half %88 %67 %89 %68 +%90 = OpFConvert %half %float_2 +%74 = OpVectorTimesScalar %v4half %85 %90 +%91 = OpFConvert %v4float %74 +OpStore %_entryPointOutput_Color %91 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(ConvertToHalfTest, ConvertToHalfWithLoopAndFConvert) { + // The resulting SPIR-V was processed with --relax-float-ops. + // + // The loop causes an FConvert to be generated at the bottom of the loop + // for the Phi. The FConvert is later processed and turned into a (dead) + // copy. + // + // clang-format off + // + // struct PS_OUTPUT + // { + // float4 Color : SV_Target0; + // }; + // + // cbuffer cbuff{ + // float4 a[10]; + // } + // + // PS_OUTPUT main() + // { + // PS_OUTPUT psout; + // float4 t = 0.0;; + // + // for (int i = 0; i<10; ++i) + // t = t + a[i]; + // + // float4 t2 = t / 10.0; + // psout.Color = t2; + // return psout; + // } + // + // clang-format on + + const std::string defs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "a" +OpName %_ "" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %_arr_v4float_uint_10 ArrayStride 16 +OpMemberDecorate %cbuff 0 Offset 0 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %_entryPointOutput_Color Location 0 +OpDecorate %96 RelaxedPrecision +OpDecorate %81 RelaxedPrecision +OpDecorate %75 RelaxedPrecision +OpDecorate %76 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%float_0 = OpConstant %float 0 +%15 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_v4float_uint_10 = OpTypeArray %v4float %uint_10 +%cbuff = OpTypeStruct %_arr_v4float_uint_10 +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%float_0_100000001 = OpConstant %float 0.100000001 +%94 = OpConstantComposite %v4float %float_0_100000001 %float_0_100000001 %float_0_100000001 %float_0_100000001 +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "a" +OpName %_ "" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %_arr_v4float_uint_10 ArrayStride 16 +OpMemberDecorate %cbuff 0 Offset 0 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %_entryPointOutput_Color Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%float_0 = OpConstant %float 0 +%15 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_v4float_uint_10 = OpTypeArray %v4float %uint_10 +%cbuff = OpTypeStruct %_arr_v4float_uint_10 +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%float_0_100000001 = OpConstant %float 0.100000001 +%94 = OpConstantComposite %v4float %float_0_100000001 %float_0_100000001 %float_0_100000001 %float_0_100000001 +%half = OpTypeFloat 16 +%v4half = OpTypeVector %half 4 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +OpBranch %65 +%65 = OpLabel +%96 = OpPhi %v4float %15 %5 %76 %71 +%95 = OpPhi %int %int_0 %5 %78 %71 +%70 = OpSLessThan %bool %95 %int_10 +OpLoopMerge %66 %71 None +OpBranchConditional %70 %71 %66 +%71 = OpLabel +%74 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %95 +%75 = OpLoad %v4float %74 +%76 = OpFAdd %v4float %96 %75 +%78 = OpIAdd %int %95 %int_1 +OpBranch %65 +%66 = OpLabel +%81 = OpFMul %v4float %96 %94 +OpStore %_entryPointOutput_Color %81 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%99 = OpFConvert %v4half %15 +OpBranch %65 +%65 = OpLabel +%96 = OpPhi %v4half %99 %5 %100 %71 +%95 = OpPhi %int %int_0 %5 %78 %71 +%70 = OpSLessThan %bool %95 %int_10 +OpLoopMerge %66 %71 None +OpBranchConditional %70 %71 %66 +%71 = OpLabel +%74 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 %95 +%75 = OpLoad %v4float %74 +%103 = OpFConvert %v4half %75 +%76 = OpFAdd %v4half %96 %103 +%78 = OpIAdd %int %95 %int_1 +%100 = OpCopyObject %v4half %76 +OpBranch %65 +%66 = OpLabel +%101 = OpFConvert %v4half %94 +%81 = OpFMul %v4half %96 %101 +%102 = OpFConvert %v4float %81 +OpStore %_entryPointOutput_Color %102 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(ConvertToHalfTest, ConvertToHalfWithExtracts) { + // The resulting SPIR-V was processed with --relax-float-ops. + // + // The extra converts in the func_after can be DCE'd. + // + // clang-format off + // + // SamplerState g_sSamp : register(s0); + // uniform Texture1D g_tTex1df4 : register(t0); + // + // struct PS_INPUT + // { + // float Tex0 : TEXCOORD0; + // }; + // + // struct PS_OUTPUT + // { + // float4 Color : SV_Target0; + // }; + // + // cbuffer cbuff{ + // float c; + // } + // + // PS_OUTPUT main(PS_INPUT i) + // { + // PS_OUTPUT psout; + // float4 tx = g_tTex1df4.Sample(g_sSamp, i.Tex0); + // float4 t = float4(tx.y, tx.z, tx.x, tx.w) * c; + // psout.Color = t; + // return psout; + // } + // + // clang-format on + + const std::string defs_before = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i_Tex0 %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "c" +OpName %_ "" +OpName %i_Tex0 "i.Tex0" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpMemberDecorate %cbuff 0 Offset 0 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %i_Tex0 Location 0 +OpDecorate %_entryPointOutput_Color Location 0 +OpDecorate %65 RelaxedPrecision +OpDecorate %82 RelaxedPrecision +OpDecorate %84 RelaxedPrecision +OpDecorate %86 RelaxedPrecision +OpDecorate %88 RelaxedPrecision +OpDecorate %90 RelaxedPrecision +OpDecorate %91 RelaxedPrecision +OpDecorate %93 RelaxedPrecision +OpDecorate %94 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%17 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant +%21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant +%25 = OpTypeSampledImage %17 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%cbuff = OpTypeStruct %float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability Sampled1D +OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i_Tex0 %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %cbuff "cbuff" +OpMemberName %cbuff 0 "c" +OpName %_ "" +OpName %i_Tex0 "i.Tex0" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpMemberDecorate %cbuff 0 Offset 0 +OpDecorate %cbuff Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 1 +OpDecorate %i_Tex0 Location 0 +OpDecorate %_entryPointOutput_Color Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%17 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant +%21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant +%25 = OpTypeSampledImage %17 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%cbuff = OpTypeStruct %float +%_ptr_Uniform_cbuff = OpTypePointer Uniform %cbuff +%_ = OpVariable %_ptr_Uniform_cbuff Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%half = OpTypeFloat 16 +%v4half = OpTypeVector %half 4 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%65 = OpLoad %float %i_Tex0 +%77 = OpLoad %17 %g_tTex1df4 +%78 = OpLoad %21 %g_sSamp +%79 = OpSampledImage %25 %77 %78 +%82 = OpImageSampleImplicitLod %v4float %79 %65 +%84 = OpCompositeExtract %float %82 1 +%86 = OpCompositeExtract %float %82 2 +%88 = OpCompositeExtract %float %82 0 +%90 = OpCompositeExtract %float %82 3 +%91 = OpCompositeConstruct %v4float %84 %86 %88 %90 +%92 = OpAccessChain %_ptr_Uniform_float %_ %int_0 +%93 = OpLoad %float %92 +%94 = OpVectorTimesScalar %v4float %91 %93 +OpStore %_entryPointOutput_Color %94 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%65 = OpLoad %float %i_Tex0 +%77 = OpLoad %17 %g_tTex1df4 +%78 = OpLoad %21 %g_sSamp +%79 = OpSampledImage %25 %77 %78 +%82 = OpImageSampleImplicitLod %v4float %79 %65 +%97 = OpFConvert %v4half %82 +%84 = OpCompositeExtract %half %97 1 +%98 = OpFConvert %v4half %82 +%86 = OpCompositeExtract %half %98 2 +%99 = OpFConvert %v4half %82 +%88 = OpCompositeExtract %half %99 0 +%100 = OpFConvert %v4half %82 +%90 = OpCompositeExtract %half %100 3 +%91 = OpCompositeConstruct %v4half %84 %86 %88 %90 +%92 = OpAccessChain %_ptr_Uniform_float %_ %int_0 +%93 = OpLoad %float %92 +%101 = OpFConvert %half %93 +%94 = OpVectorTimesScalar %v4half %91 %101 +%102 = OpFConvert %v4float %94 +OpStore %_entryPointOutput_Color %102 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(ConvertToHalfTest, ConvertToHalfWithClosure) { + // Include as many contiguous composite instructions as possible into + // half-precision computations + // + // Compiled with glslang -V -Os + // + // clang-format off + // + // #version 410 core + // + // precision mediump float; + // + // layout(location = 1) in vec3 foo; + // layout(location = 2) in mat2 bar; + // layout(location = 1) out vec3 res; + // + // vec3 func(vec3 tap, mat2 M) { + // return vec3(M * tap.xy, 1.0); + // } + // + // void main() { + // res = func(foo, bar); + // } + // + // clang-format on + + const std::string defs = + R"(OpCapability Shader +; CHECK: OpCapability Float16 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %res %foo %bar +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 410 +OpName %main "main" +OpName %res "res" +OpName %foo "foo" +OpName %bar "bar" +OpDecorate %res RelaxedPrecision +; CHECK-NOT: OpDecorate %res RelaxedPrecision +OpDecorate %res Location 1 +OpDecorate %foo RelaxedPrecision +; CHECK-NOT: OpDecorate %foo RelaxedPrecision +OpDecorate %foo Location 1 +OpDecorate %bar RelaxedPrecision +; CHECK-NOT: OpDecorate %bar RelaxedPrecision +OpDecorate %bar Location 2 +OpDecorate %34 RelaxedPrecision +OpDecorate %36 RelaxedPrecision +OpDecorate %41 RelaxedPrecision +OpDecorate %42 RelaxedPrecision +; CHECK-NOT: OpDecorate %34 RelaxedPrecision +; CHECK-NOT: OpDecorate %36 RelaxedPrecision +; CHECK-NOT: OpDecorate %41 RelaxedPrecision +; CHECK-NOT: OpDecorate %42 RelaxedPrecision +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%v2float = OpTypeVector %float 2 +%mat2v2float = OpTypeMatrix %v2float 2 +%float_1 = OpConstant %float 1 +%_ptr_Output_v3float = OpTypePointer Output %v3float +%res = OpVariable %_ptr_Output_v3float Output +%_ptr_Input_v3float = OpTypePointer Input %v3float +%foo = OpVariable %_ptr_Input_v3float Input +%_ptr_Input_mat2v2float = OpTypePointer Input %mat2v2float +%bar = OpVariable %_ptr_Input_mat2v2float Input +)"; + + const std::string func = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%34 = OpLoad %v3float %foo +%36 = OpLoad %mat2v2float %bar +; CHECK: %48 = OpFConvert %v3half %34 +; CHECK: %49 = OpFConvert %v3half %34 +%41 = OpVectorShuffle %v2float %34 %34 0 1 +; CHECK-NOT: %41 = OpVectorShuffle %v2float %34 %34 0 1 +; CHECK: %41 = OpVectorShuffle %v2half %48 %49 0 1 +%42 = OpMatrixTimesVector %v2float %36 %41 +; CHECK-NOT: %42 = OpMatrixTimesVector %v2float %36 %41 +; CHECK: %55 = OpCompositeExtract %v2float %36 0 +; CHECK: %56 = OpFConvert %v2half %55 +; CHECK: %57 = OpCompositeExtract %v2float %36 1 +; CHECK: %58 = OpFConvert %v2half %57 +; CHECK: %59 = OpCompositeConstruct %mat2v2half %56 %58 +; CHECK: %52 = OpCopyObject %mat2v2float %36 +; CHECK: %42 = OpMatrixTimesVector %v2half %59 %41 +%43 = OpCompositeExtract %float %42 0 +%44 = OpCompositeExtract %float %42 1 +; CHECK-NOT: %43 = OpCompositeExtract %float %42 0 +; CHECK-NOT: %44 = OpCompositeExtract %float %42 1 +; CHECK: %43 = OpCompositeExtract %half %42 0 +; CHECK: %44 = OpCompositeExtract %half %42 1 +%45 = OpCompositeConstruct %v3float %43 %44 %float_1 +; CHECK-NOT: %45 = OpCompositeConstruct %v3float %43 %44 %float_1 +; CHECK: %53 = OpFConvert %float %43 +; CHECK: %54 = OpFConvert %float %44 +; CHECK: %45 = OpCompositeConstruct %v3float %53 %54 %float_1 +OpStore %res %45 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(defs + func, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/copy_prop_array_test.cpp b/third_party/spirv-tools/test/opt/copy_prop_array_test.cpp new file mode 100644 index 0000000..72bc7f6 --- /dev/null +++ b/third_party/spirv-tools/test/opt/copy_prop_array_test.cpp @@ -0,0 +1,1819 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using CopyPropArrayPassTest = PassTest<::testing::Test>; + +TEST_F(CopyPropArrayPassTest, BasicPropagateArray) { + const std::string before = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK: OpVariable +; CHECK: OpAccessChain +; CHECK: [[new_address:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[new_address]] %24 +; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]] +; CHECK: OpStore %out_var_SV_Target [[load]] +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%28 = OpCompositeExtract %v4float %26 1 +%29 = OpCompositeExtract %v4float %26 2 +%30 = OpCompositeExtract %v4float %26 3 +%31 = OpCompositeExtract %v4float %26 4 +%32 = OpCompositeExtract %v4float %26 5 +%33 = OpCompositeExtract %v4float %26 6 +%34 = OpCompositeExtract %v4float %26 7 +%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34 +OpStore %23 %35 +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(before, false); +} + +TEST_F(CopyPropArrayPassTest, BasicPropagateArrayWithName) { + const std::string before = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %local "local" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK: OpVariable +; CHECK: OpAccessChain +; CHECK: [[new_address:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[new_address]] %24 +; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]] +; CHECK: OpStore %out_var_SV_Target [[load]] +%main = OpFunction %void None %13 +%22 = OpLabel +%local = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%28 = OpCompositeExtract %v4float %26 1 +%29 = OpCompositeExtract %v4float %26 2 +%30 = OpCompositeExtract %v4float %26 3 +%31 = OpCompositeExtract %v4float %26 4 +%32 = OpCompositeExtract %v4float %26 5 +%33 = OpCompositeExtract %v4float %26 6 +%34 = OpCompositeExtract %v4float %26 7 +%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34 +OpStore %local %35 +%36 = OpAccessChain %_ptr_Function_v4float %local %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(before, false); +} + +// Propagate 2d array. This test identifying a copy through multiple levels. +// Also has to traverse multiple OpAccessChains. +TEST_F(CopyPropArrayPassTest, Propagate2DArray) { + const std::string text = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_2 ArrayStride 16 +OpDecorate %_arr__arr_v4float_uint_2_uint_2 ArrayStride 32 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr__arr_v4float_uint_2_uint_2 = OpTypeArray %_arr_v4float_uint_2 %uint_2 +%type_MyCBuffer = OpTypeStruct %_arr__arr_v4float_uint_2_uint_2 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%14 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_2_0 = OpTypeArray %v4float %uint_2 +%_arr__arr_v4float_uint_2_0_uint_2 = OpTypeArray %_arr_v4float_uint_2_0 %uint_2 +%_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 = OpTypePointer Function %_arr__arr_v4float_uint_2_0_uint_2 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 = OpTypePointer Uniform %_arr__arr_v4float_uint_2_uint_2 +%_ptr_Function__arr_v4float_uint_2_0 = OpTypePointer Function %_arr_v4float_uint_2_0 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK: OpVariable +; CHECK: OpVariable +; CHECK: OpAccessChain +; CHECK: [[new_address:%\w+]] = OpAccessChain %_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 %MyCBuffer %int_0 +%main = OpFunction %void None %14 +%25 = OpLabel +%26 = OpVariable %_ptr_Function__arr_v4float_uint_2_0 Function +%27 = OpVariable %_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 Function +%28 = OpLoad %int %in_var_INDEX +%29 = OpAccessChain %_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 %MyCBuffer %int_0 +%30 = OpLoad %_arr__arr_v4float_uint_2_uint_2 %29 +%31 = OpCompositeExtract %_arr_v4float_uint_2 %30 0 +%32 = OpCompositeExtract %v4float %31 0 +%33 = OpCompositeExtract %v4float %31 1 +%34 = OpCompositeConstruct %_arr_v4float_uint_2_0 %32 %33 +%35 = OpCompositeExtract %_arr_v4float_uint_2 %30 1 +%36 = OpCompositeExtract %v4float %35 0 +%37 = OpCompositeExtract %v4float %35 1 +%38 = OpCompositeConstruct %_arr_v4float_uint_2_0 %36 %37 +%39 = OpCompositeConstruct %_arr__arr_v4float_uint_2_0_uint_2 %34 %38 +; CHECK: OpStore +OpStore %27 %39 +%40 = OpAccessChain %_ptr_Function__arr_v4float_uint_2_0 %27 %28 +%42 = OpAccessChain %_ptr_Function_v4float %40 %28 +%43 = OpLoad %v4float %42 +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_2 [[new_address]] %28 +; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[ac1]] %28 +; CHECK: [[load:%\w+]] = OpLoad %v4float [[ac2]] +; CHECK: OpStore %out_var_SV_Target [[load]] +OpStore %out_var_SV_Target %43 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + +// Propagate 2d array. This test identifying a copy through multiple levels. +// Also has to traverse multiple OpAccessChains. +TEST_F(CopyPropArrayPassTest, Propagate2DArrayWithMultiLevelExtract) { + const std::string text = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_2 ArrayStride 16 +OpDecorate %_arr__arr_v4float_uint_2_uint_2 ArrayStride 32 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr__arr_v4float_uint_2_uint_2 = OpTypeArray %_arr_v4float_uint_2 %uint_2 +%type_MyCBuffer = OpTypeStruct %_arr__arr_v4float_uint_2_uint_2 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%14 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_2_0 = OpTypeArray %v4float %uint_2 +%_arr__arr_v4float_uint_2_0_uint_2 = OpTypeArray %_arr_v4float_uint_2_0 %uint_2 +%_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 = OpTypePointer Function %_arr__arr_v4float_uint_2_0_uint_2 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 = OpTypePointer Uniform %_arr__arr_v4float_uint_2_uint_2 +%_ptr_Function__arr_v4float_uint_2_0 = OpTypePointer Function %_arr_v4float_uint_2_0 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK: OpVariable +; CHECK: OpVariable +; CHECK: OpAccessChain +; CHECK: [[new_address:%\w+]] = OpAccessChain %_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 %MyCBuffer %int_0 +%main = OpFunction %void None %14 +%25 = OpLabel +%26 = OpVariable %_ptr_Function__arr_v4float_uint_2_0 Function +%27 = OpVariable %_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 Function +%28 = OpLoad %int %in_var_INDEX +%29 = OpAccessChain %_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 %MyCBuffer %int_0 +%30 = OpLoad %_arr__arr_v4float_uint_2_uint_2 %29 +%32 = OpCompositeExtract %v4float %30 0 0 +%33 = OpCompositeExtract %v4float %30 0 1 +%34 = OpCompositeConstruct %_arr_v4float_uint_2_0 %32 %33 +%36 = OpCompositeExtract %v4float %30 1 0 +%37 = OpCompositeExtract %v4float %30 1 1 +%38 = OpCompositeConstruct %_arr_v4float_uint_2_0 %36 %37 +%39 = OpCompositeConstruct %_arr__arr_v4float_uint_2_0_uint_2 %34 %38 +; CHECK: OpStore +OpStore %27 %39 +%40 = OpAccessChain %_ptr_Function__arr_v4float_uint_2_0 %27 %28 +%42 = OpAccessChain %_ptr_Function_v4float %40 %28 +%43 = OpLoad %v4float %42 +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_2 [[new_address]] %28 +; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[ac1]] %28 +; CHECK: [[load:%\w+]] = OpLoad %v4float [[ac2]] +; CHECK: OpStore %out_var_SV_Target [[load]] +OpStore %out_var_SV_Target %43 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + +// Test decomposing an object when we need to "rewrite" a store. +TEST_F(CopyPropArrayPassTest, DecomposeObjectForArrayStore) { + const std::string text = + R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_MyCBuffer "type.MyCBuffer" + OpMemberName %type_MyCBuffer 0 "Data" + OpName %MyCBuffer "MyCBuffer" + OpName %main "main" + OpName %in_var_INDEX "in.var.INDEX" + OpName %out_var_SV_Target "out.var.SV_Target" + OpDecorate %_arr_v4float_uint_2 ArrayStride 16 + OpDecorate %_arr__arr_v4float_uint_2_uint_2 ArrayStride 32 + OpMemberDecorate %type_MyCBuffer 0 Offset 0 + OpDecorate %type_MyCBuffer Block + OpDecorate %in_var_INDEX Flat + OpDecorate %in_var_INDEX Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %MyCBuffer DescriptorSet 0 + OpDecorate %MyCBuffer Binding 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2 +%_arr__arr_v4float_uint_2_uint_2 = OpTypeArray %_arr_v4float_uint_2 %uint_2 +%type_MyCBuffer = OpTypeStruct %_arr__arr_v4float_uint_2_uint_2 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer + %void = OpTypeVoid + %14 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_2_0 = OpTypeArray %v4float %uint_2 +%_arr__arr_v4float_uint_2_0_uint_2 = OpTypeArray %_arr_v4float_uint_2_0 %uint_2 +%_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 = OpTypePointer Function %_arr__arr_v4float_uint_2_0_uint_2 + %int_0 = OpConstant %int 0 +%_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 = OpTypePointer Uniform %_arr__arr_v4float_uint_2_uint_2 +%_ptr_Function__arr_v4float_uint_2_0 = OpTypePointer Function %_arr_v4float_uint_2_0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %14 + %25 = OpLabel + %26 = OpVariable %_ptr_Function__arr_v4float_uint_2_0 Function + %27 = OpVariable %_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 Function + %28 = OpLoad %int %in_var_INDEX + %29 = OpAccessChain %_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 %MyCBuffer %int_0 + %30 = OpLoad %_arr__arr_v4float_uint_2_uint_2 %29 + %31 = OpCompositeExtract %_arr_v4float_uint_2 %30 0 + %32 = OpCompositeExtract %v4float %31 0 + %33 = OpCompositeExtract %v4float %31 1 + %34 = OpCompositeConstruct %_arr_v4float_uint_2_0 %32 %33 + %35 = OpCompositeExtract %_arr_v4float_uint_2 %30 1 + %36 = OpCompositeExtract %v4float %35 0 + %37 = OpCompositeExtract %v4float %35 1 + %38 = OpCompositeConstruct %_arr_v4float_uint_2_0 %36 %37 + %39 = OpCompositeConstruct %_arr__arr_v4float_uint_2_0_uint_2 %34 %38 + OpStore %27 %39 +; CHECK: [[access_chain:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_2 + %40 = OpAccessChain %_ptr_Function__arr_v4float_uint_2_0 %27 %28 +; CHECK: [[load:%\w+]] = OpLoad %_arr_v4float_uint_2 [[access_chain]] + %41 = OpLoad %_arr_v4float_uint_2_0 %40 +; CHECK: [[extract1:%\w+]] = OpCompositeExtract %v4float [[load]] 0 +; CHECK: [[extract2:%\w+]] = OpCompositeExtract %v4float [[load]] 1 +; CHECK: [[construct:%\w+]] = OpCompositeConstruct %_arr_v4float_uint_2_0 [[extract1]] [[extract2]] +; CHECK: OpStore %26 [[construct]] + OpStore %26 %41 + %42 = OpAccessChain %_ptr_Function_v4float %26 %28 + %43 = OpLoad %v4float %42 + OpStore %out_var_SV_Target %43 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + +// Test decomposing an object when we need to "rewrite" a store. +TEST_F(CopyPropArrayPassTest, DecomposeObjectForStructStore) { + const std::string text = + R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %type_MyCBuffer "type.MyCBuffer" + OpMemberName %type_MyCBuffer 0 "Data" + OpName %MyCBuffer "MyCBuffer" + OpName %main "main" + OpName %in_var_INDEX "in.var.INDEX" + OpName %out_var_SV_Target "out.var.SV_Target" + OpMemberDecorate %type_MyCBuffer 0 Offset 0 + OpDecorate %type_MyCBuffer Block + OpDecorate %in_var_INDEX Flat + OpDecorate %in_var_INDEX Location 0 + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %MyCBuffer DescriptorSet 0 + OpDecorate %MyCBuffer Binding 0 +; CHECK: OpDecorate [[decorated_type:%\w+]] GLSLPacked + OpDecorate %struct GLSLPacked + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +; CHECK: [[decorated_type]] = OpTypeStruct +%struct = OpTypeStruct %float %uint +%_arr_struct_uint_2 = OpTypeArray %struct %uint_2 +%type_MyCBuffer = OpTypeStruct %_arr_struct_uint_2 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer + %void = OpTypeVoid + %14 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +; CHECK: [[struct:%\w+]] = OpTypeStruct %float %uint +%struct_0 = OpTypeStruct %float %uint +%_arr_struct_0_uint_2 = OpTypeArray %struct_0 %uint_2 +%_ptr_Function__arr_struct_0_uint_2 = OpTypePointer Function %_arr_struct_0_uint_2 + %int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_struct_uint_2 = OpTypePointer Uniform %_arr_struct_uint_2 +; CHECK: [[decorated_ptr:%\w+]] = OpTypePointer Uniform [[decorated_type]] +%_ptr_Function_struct_0 = OpTypePointer Function %struct_0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %14 + %25 = OpLabel + %26 = OpVariable %_ptr_Function_struct_0 Function + %27 = OpVariable %_ptr_Function__arr_struct_0_uint_2 Function + %28 = OpLoad %int %in_var_INDEX + %29 = OpAccessChain %_ptr_Uniform__arr_struct_uint_2 %MyCBuffer %int_0 + %30 = OpLoad %_arr_struct_uint_2 %29 + %31 = OpCompositeExtract %struct %30 0 + %32 = OpCompositeExtract %v4float %31 0 + %33 = OpCompositeExtract %v4float %31 1 + %34 = OpCompositeConstruct %struct_0 %32 %33 + %35 = OpCompositeExtract %struct %30 1 + %36 = OpCompositeExtract %float %35 0 + %37 = OpCompositeExtract %uint %35 1 + %38 = OpCompositeConstruct %struct_0 %36 %37 + %39 = OpCompositeConstruct %_arr_struct_0_uint_2 %34 %38 + OpStore %27 %39 +; CHECK: [[access_chain:%\w+]] = OpAccessChain [[decorated_ptr]] + %40 = OpAccessChain %_ptr_Function_struct_0 %27 %28 +; CHECK: [[load:%\w+]] = OpLoad [[decorated_type]] [[access_chain]] + %41 = OpLoad %struct_0 %40 +; CHECK: [[extract1:%\w+]] = OpCompositeExtract %float [[load]] 0 +; CHECK: [[extract2:%\w+]] = OpCompositeExtract %uint [[load]] 1 +; CHECK: [[construct:%\w+]] = OpCompositeConstruct [[struct]] [[extract1]] [[extract2]] +; CHECK: OpStore %26 [[construct]] + OpStore %26 %41 + %42 = OpAccessChain %_ptr_Function_v4float %26 %28 + %43 = OpLoad %v4float %42 + OpStore %out_var_SV_Target %43 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, false); +} + +TEST_F(CopyPropArrayPassTest, CopyViaInserts) { + const std::string before = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK: OpVariable +; CHECK: OpAccessChain +; CHECK: [[new_address:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[new_address]] %24 +; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]] +; CHECK: OpStore %out_var_SV_Target [[load]] +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%undef = OpUndef %_arr_v4float_uint_8_0 +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%i0 = OpCompositeInsert %_arr_v4float_uint_8_0 %27 %undef 0 +%28 = OpCompositeExtract %v4float %26 1 +%i1 = OpCompositeInsert %_arr_v4float_uint_8_0 %28 %i0 1 +%29 = OpCompositeExtract %v4float %26 2 +%i2 = OpCompositeInsert %_arr_v4float_uint_8_0 %29 %i1 2 +%30 = OpCompositeExtract %v4float %26 3 +%i3 = OpCompositeInsert %_arr_v4float_uint_8_0 %30 %i2 3 +%31 = OpCompositeExtract %v4float %26 4 +%i4 = OpCompositeInsert %_arr_v4float_uint_8_0 %31 %i3 4 +%32 = OpCompositeExtract %v4float %26 5 +%i5 = OpCompositeInsert %_arr_v4float_uint_8_0 %32 %i4 5 +%33 = OpCompositeExtract %v4float %26 6 +%i6 = OpCompositeInsert %_arr_v4float_uint_8_0 %33 %i5 6 +%34 = OpCompositeExtract %v4float %26 7 +%i7 = OpCompositeInsert %_arr_v4float_uint_8_0 %34 %i6 7 +OpStore %23 %i7 +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(before, false); +} + +TEST_F(CopyPropArrayPassTest, IsomorphicTypes1) { + const std::string before = + R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[s1:%\w+]] = OpTypeStruct [[int]] +; CHECK: [[s2:%\w+]] = OpTypeStruct [[s1]] +; CHECK: [[a1:%\w+]] = OpTypeArray [[s2]] +; CHECK: [[s3:%\w+]] = OpTypeStruct [[a1]] +; CHECK: [[p_s3:%\w+]] = OpTypePointer Uniform [[s3]] +; CHECK: [[global_var:%\w+]] = OpVariable [[p_s3]] Uniform +; CHECK: [[p_a1:%\w+]] = OpTypePointer Uniform [[a1]] +; CHECK: [[p_s2:%\w+]] = OpTypePointer Uniform [[s2]] +; CHECK: [[ac1:%\w+]] = OpAccessChain [[p_a1]] [[global_var]] %uint_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain [[p_s2]] [[ac1]] %uint_0 +; CHECK: [[ld:%\w+]] = OpLoad [[s2]] [[ac2]] +; CHECK: [[ex:%\w+]] = OpCompositeExtract [[s1]] [[ld]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "PS_main" + OpExecutionMode %2 OriginUpperLeft + OpSource HLSL 600 + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 101 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %s1 = OpTypeStruct %uint + %s2 = OpTypeStruct %s1 +%a1 = OpTypeArray %s2 %uint_1 + %s3 = OpTypeStruct %a1 + %s1_1 = OpTypeStruct %uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %void = OpTypeVoid + %13 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 + %s1_0 = OpTypeStruct %uint + %s2_0 = OpTypeStruct %s1_0 +%a1_0 = OpTypeArray %s2_0 %uint_1 + %s3_0 = OpTypeStruct %a1_0 +%p_s3 = OpTypePointer Uniform %s3 +%p_s3_0 = OpTypePointer Function %s3_0 + %3 = OpVariable %p_s3 Uniform +%p_a1_0 = OpTypePointer Function %a1_0 +%p_s2_0 = OpTypePointer Function %s2_0 + %2 = OpFunction %void None %13 + %20 = OpLabel + %21 = OpVariable %p_a1_0 Function + %22 = OpLoad %s3 %3 + %23 = OpCompositeExtract %a1 %22 0 + %24 = OpCompositeExtract %s2 %23 0 + %25 = OpCompositeExtract %s1 %24 0 + %26 = OpCompositeExtract %uint %25 0 + %27 = OpCompositeConstruct %s1_0 %26 + %32 = OpCompositeConstruct %s2_0 %27 + %28 = OpCompositeConstruct %a1_0 %32 + OpStore %21 %28 + %29 = OpAccessChain %p_s2_0 %21 %uint_0 + %30 = OpLoad %s2 %29 + %31 = OpCompositeExtract %s1 %30 0 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(before, false); +} + +TEST_F(CopyPropArrayPassTest, IsomorphicTypes2) { + const std::string before = + R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[s1:%\w+]] = OpTypeStruct [[int]] +; CHECK: [[s2:%\w+]] = OpTypeStruct [[s1]] +; CHECK: [[a1:%\w+]] = OpTypeArray [[s2]] +; CHECK: [[s3:%\w+]] = OpTypeStruct [[a1]] +; CHECK: [[p_s3:%\w+]] = OpTypePointer Uniform [[s3]] +; CHECK: [[global_var:%\w+]] = OpVariable [[p_s3]] Uniform +; CHECK: [[p_s2:%\w+]] = OpTypePointer Uniform [[s2]] +; CHECK: [[p_s1:%\w+]] = OpTypePointer Uniform [[s1]] +; CHECK: [[ac1:%\w+]] = OpAccessChain [[p_s2]] [[global_var]] %uint_0 %uint_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain [[p_s1]] [[ac1]] %uint_0 +; CHECK: [[ld:%\w+]] = OpLoad [[s1]] [[ac2]] +; CHECK: [[ex:%\w+]] = OpCompositeExtract [[int]] [[ld]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "PS_main" + OpExecutionMode %2 OriginUpperLeft + OpSource HLSL 600 + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 101 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %_struct_6 = OpTypeStruct %uint + %_struct_7 = OpTypeStruct %_struct_6 +%_arr__struct_7_uint_1 = OpTypeArray %_struct_7 %uint_1 + %_struct_9 = OpTypeStruct %_arr__struct_7_uint_1 + %_struct_10 = OpTypeStruct %uint +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %void = OpTypeVoid + %13 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 + %_struct_15 = OpTypeStruct %uint +%_arr__struct_15_uint_1 = OpTypeArray %_struct_15 %uint_1 +%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 +%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15 + %3 = OpVariable %_ptr_Uniform__struct_9 Uniform +%_ptr_Function__arr__struct_15_uint_1 = OpTypePointer Function %_arr__struct_15_uint_1 + %2 = OpFunction %void None %13 + %20 = OpLabel + %21 = OpVariable %_ptr_Function__arr__struct_15_uint_1 Function + %22 = OpLoad %_struct_9 %3 + %23 = OpCompositeExtract %_arr__struct_7_uint_1 %22 0 + %24 = OpCompositeExtract %_struct_7 %23 0 + %25 = OpCompositeExtract %_struct_6 %24 0 + %26 = OpCompositeExtract %uint %25 0 + %27 = OpCompositeConstruct %_struct_15 %26 + %28 = OpCompositeConstruct %_arr__struct_15_uint_1 %27 + OpStore %21 %28 + %29 = OpAccessChain %_ptr_Function__struct_15 %21 %uint_0 + %30 = OpLoad %_struct_15 %29 + %31 = OpCompositeExtract %uint %30 0 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(before, false); +} + +TEST_F(CopyPropArrayPassTest, IsomorphicTypes3) { + const std::string before = + R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[s1:%\w+]] = OpTypeStruct [[int]] +; CHECK: [[s2:%\w+]] = OpTypeStruct [[s1]] +; CHECK: [[a1:%\w+]] = OpTypeArray [[s2]] +; CHECK: [[s3:%\w+]] = OpTypeStruct [[a1]] +; CHECK: [[s1_1:%\w+]] = OpTypeStruct [[int]] +; CHECK: [[p_s3:%\w+]] = OpTypePointer Uniform [[s3]] +; CHECK: [[p_s1_1:%\w+]] = OpTypePointer Function [[s1_1]] +; CHECK: [[global_var:%\w+]] = OpVariable [[p_s3]] Uniform +; CHECK: [[p_s2:%\w+]] = OpTypePointer Uniform [[s2]] +; CHECK: [[p_s1:%\w+]] = OpTypePointer Uniform [[s1]] +; CHECK: [[var:%\w+]] = OpVariable [[p_s1_1]] Function +; CHECK: [[ac1:%\w+]] = OpAccessChain [[p_s2]] [[global_var]] %uint_0 %uint_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain [[p_s1]] [[ac1]] %uint_0 +; CHECK: [[ld:%\w+]] = OpLoad [[s1]] [[ac2]] +; CHECK: [[ex:%\w+]] = OpCompositeExtract [[int]] [[ld]] +; CHECK: [[copy:%\w+]] = OpCompositeConstruct [[s1_1]] [[ex]] +; CHECK: OpStore [[var]] [[copy]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "PS_main" + OpExecutionMode %2 OriginUpperLeft + OpSource HLSL 600 + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 101 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %_struct_6 = OpTypeStruct %uint + %_struct_7 = OpTypeStruct %_struct_6 +%_arr__struct_7_uint_1 = OpTypeArray %_struct_7 %uint_1 + %_struct_9 = OpTypeStruct %_arr__struct_7_uint_1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %void = OpTypeVoid + %13 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 + %_struct_15 = OpTypeStruct %uint + %_struct_10 = OpTypeStruct %uint +%_arr__struct_15_uint_1 = OpTypeArray %_struct_15 %uint_1 +%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 +%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15 + %3 = OpVariable %_ptr_Uniform__struct_9 Uniform +%_ptr_Function__arr__struct_15_uint_1 = OpTypePointer Function %_arr__struct_15_uint_1 + %2 = OpFunction %void None %13 + %20 = OpLabel + %21 = OpVariable %_ptr_Function__arr__struct_15_uint_1 Function + %var = OpVariable %_ptr_Function__struct_15 Function + %22 = OpLoad %_struct_9 %3 + %23 = OpCompositeExtract %_arr__struct_7_uint_1 %22 0 + %24 = OpCompositeExtract %_struct_7 %23 0 + %25 = OpCompositeExtract %_struct_6 %24 0 + %26 = OpCompositeExtract %uint %25 0 + %27 = OpCompositeConstruct %_struct_15 %26 + %28 = OpCompositeConstruct %_arr__struct_15_uint_1 %27 + OpStore %21 %28 + %29 = OpAccessChain %_ptr_Function__struct_15 %21 %uint_0 + %30 = OpLoad %_struct_15 %29 + OpStore %var %30 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(before, false); +} + +TEST_F(CopyPropArrayPassTest, BadMergingTwoObjects) { + // The second element in the |OpCompositeConstruct| is from a different + // object. + const std::string text = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpName %type_ConstBuf "type.ConstBuf" +OpMemberName %type_ConstBuf 0 "TexSizeU" +OpMemberName %type_ConstBuf 1 "TexSizeV" +OpName %ConstBuf "ConstBuf" +OpName %main "main" +OpMemberDecorate %type_ConstBuf 0 Offset 0 +OpMemberDecorate %type_ConstBuf 1 Offset 8 +OpDecorate %type_ConstBuf Block +OpDecorate %ConstBuf DescriptorSet 0 +OpDecorate %ConstBuf Binding 2 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%type_ConstBuf = OpTypeStruct %v2float %v2float +%_ptr_Uniform_type_ConstBuf = OpTypePointer Uniform %type_ConstBuf +%void = OpTypeVoid +%9 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%int_0 = OpConstant %uint 0 +%uint_2 = OpConstant %uint 2 +%_arr_v2float_uint_2 = OpTypeArray %v2float %uint_2 +%_ptr_Function__arr_v2float_uint_2 = OpTypePointer Function %_arr_v2float_uint_2 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%ConstBuf = OpVariable %_ptr_Uniform_type_ConstBuf Uniform +%main = OpFunction %void None %9 +%24 = OpLabel +%25 = OpVariable %_ptr_Function__arr_v2float_uint_2 Function +%27 = OpAccessChain %_ptr_Uniform_v2float %ConstBuf %int_0 +%28 = OpLoad %v2float %27 +%29 = OpAccessChain %_ptr_Uniform_v2float %ConstBuf %int_0 +%30 = OpLoad %v2float %29 +%31 = OpFNegate %v2float %30 +%37 = OpCompositeConstruct %_arr_v2float_uint_2 %28 %31 +OpStore %25 %37 +OpReturn +OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CopyPropArrayPassTest, SecondElementNotContained) { + // The second element in the |OpCompositeConstruct| is not a memory object. + // Make sure no change happends. + const std::string text = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpName %type_ConstBuf "type.ConstBuf" +OpMemberName %type_ConstBuf 0 "TexSizeU" +OpMemberName %type_ConstBuf 1 "TexSizeV" +OpName %ConstBuf "ConstBuf" +OpName %main "main" +OpMemberDecorate %type_ConstBuf 0 Offset 0 +OpMemberDecorate %type_ConstBuf 1 Offset 8 +OpDecorate %type_ConstBuf Block +OpDecorate %ConstBuf DescriptorSet 0 +OpDecorate %ConstBuf Binding 2 +OpDecorate %ConstBuf2 DescriptorSet 1 +OpDecorate %ConstBuf2 Binding 2 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%type_ConstBuf = OpTypeStruct %v2float %v2float +%_ptr_Uniform_type_ConstBuf = OpTypePointer Uniform %type_ConstBuf +%void = OpTypeVoid +%9 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%int_0 = OpConstant %uint 0 +%int_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%_arr_v2float_uint_2 = OpTypeArray %v2float %uint_2 +%_ptr_Function__arr_v2float_uint_2 = OpTypePointer Function %_arr_v2float_uint_2 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%ConstBuf = OpVariable %_ptr_Uniform_type_ConstBuf Uniform +%ConstBuf2 = OpVariable %_ptr_Uniform_type_ConstBuf Uniform +%main = OpFunction %void None %9 +%24 = OpLabel +%25 = OpVariable %_ptr_Function__arr_v2float_uint_2 Function +%27 = OpAccessChain %_ptr_Uniform_v2float %ConstBuf %int_0 +%28 = OpLoad %v2float %27 +%29 = OpAccessChain %_ptr_Uniform_v2float %ConstBuf2 %int_1 +%30 = OpLoad %v2float %29 +%37 = OpCompositeConstruct %_arr_v2float_uint_2 %28 %30 +OpStore %25 %37 +OpReturn +OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} +// This test will place a load before the store. We cannot propagate in this +// case. +TEST_F(CopyPropArrayPassTest, LoadBeforeStore) { + const std::string text = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%38 = OpAccessChain %_ptr_Function_v4float %23 %24 +%39 = OpLoad %v4float %36 +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%28 = OpCompositeExtract %v4float %26 1 +%29 = OpCompositeExtract %v4float %26 2 +%30 = OpCompositeExtract %v4float %26 3 +%31 = OpCompositeExtract %v4float %26 4 +%32 = OpCompositeExtract %v4float %26 5 +%33 = OpCompositeExtract %v4float %26 6 +%34 = OpCompositeExtract %v4float %26 7 +%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34 +OpStore %23 %35 +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// This test will place a load where it is not dominated by the store. We +// cannot propagate in this case. +TEST_F(CopyPropArrayPassTest, LoadNotDominated) { + const std::string text = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +OpSelectionMerge %merge None +OpBranchConditional %true %if %else +%if = OpLabel +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%28 = OpCompositeExtract %v4float %26 1 +%29 = OpCompositeExtract %v4float %26 2 +%30 = OpCompositeExtract %v4float %26 3 +%31 = OpCompositeExtract %v4float %26 4 +%32 = OpCompositeExtract %v4float %26 5 +%33 = OpCompositeExtract %v4float %26 6 +%34 = OpCompositeExtract %v4float %26 7 +%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34 +OpStore %23 %35 +%38 = OpAccessChain %_ptr_Function_v4float %23 %24 +%39 = OpLoad %v4float %36 +OpBranch %merge +%else = OpLabel +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpBranch %merge +%merge = OpLabel +%phi = OpPhi %out_var_SV_Target %39 %if %37 %else +OpStore %out_var_SV_Target %phi +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// This test has a partial store to the variable. We cannot propagate in this +// case. +TEST_F(CopyPropArrayPassTest, PartialStore) { + const std::string text = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%f0 = OpConstant %float 0 +%v4const = OpConstantComposite %v4float %f0 %f0 %f0 %f0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%28 = OpCompositeExtract %v4float %26 1 +%29 = OpCompositeExtract %v4float %26 2 +%30 = OpCompositeExtract %v4float %26 3 +%31 = OpCompositeExtract %v4float %26 4 +%32 = OpCompositeExtract %v4float %26 5 +%33 = OpCompositeExtract %v4float %26 6 +%34 = OpCompositeExtract %v4float %26 7 +%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34 +OpStore %23 %35 +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 + OpStore %36 %v4const +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// This test does not have a proper copy of an object. We cannot propagate in +// this case. +TEST_F(CopyPropArrayPassTest, NotACopy) { + const std::string text = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%f0 = OpConstant %float 0 +%v4const = OpConstantComposite %v4float %f0 %f0 %f0 %f0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%28 = OpCompositeExtract %v4float %26 0 +%29 = OpCompositeExtract %v4float %26 2 +%30 = OpCompositeExtract %v4float %26 3 +%31 = OpCompositeExtract %v4float %26 4 +%32 = OpCompositeExtract %v4float %26 5 +%33 = OpCompositeExtract %v4float %26 6 +%34 = OpCompositeExtract %v4float %26 7 +%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34 +OpStore %23 %35 +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CopyPropArrayPassTest, BadCopyViaInserts1) { + const std::string text = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%undef = OpUndef %_arr_v4float_uint_8_0 +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%i0 = OpCompositeInsert %_arr_v4float_uint_8_0 %27 %undef 0 +%28 = OpCompositeExtract %v4float %26 1 +%i1 = OpCompositeInsert %_arr_v4float_uint_8_0 %28 %i0 1 +%29 = OpCompositeExtract %v4float %26 2 +%i2 = OpCompositeInsert %_arr_v4float_uint_8_0 %29 %i1 3 +%30 = OpCompositeExtract %v4float %26 3 +%i3 = OpCompositeInsert %_arr_v4float_uint_8_0 %30 %i2 3 +%31 = OpCompositeExtract %v4float %26 4 +%i4 = OpCompositeInsert %_arr_v4float_uint_8_0 %31 %i3 4 +%32 = OpCompositeExtract %v4float %26 5 +%i5 = OpCompositeInsert %_arr_v4float_uint_8_0 %32 %i4 5 +%33 = OpCompositeExtract %v4float %26 6 +%i6 = OpCompositeInsert %_arr_v4float_uint_8_0 %33 %i5 6 +%34 = OpCompositeExtract %v4float %26 7 +%i7 = OpCompositeInsert %_arr_v4float_uint_8_0 %34 %i6 7 +OpStore %23 %i7 +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CopyPropArrayPassTest, BadCopyViaInserts2) { + const std::string text = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%undef = OpUndef %_arr_v4float_uint_8_0 +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%i0 = OpCompositeInsert %_arr_v4float_uint_8_0 %27 %undef 0 +%28 = OpCompositeExtract %v4float %26 1 +%i1 = OpCompositeInsert %_arr_v4float_uint_8_0 %28 %i0 1 +%29 = OpCompositeExtract %v4float %26 3 +%i2 = OpCompositeInsert %_arr_v4float_uint_8_0 %29 %i1 2 +%30 = OpCompositeExtract %v4float %26 3 +%i3 = OpCompositeInsert %_arr_v4float_uint_8_0 %30 %i2 3 +%31 = OpCompositeExtract %v4float %26 4 +%i4 = OpCompositeInsert %_arr_v4float_uint_8_0 %31 %i3 4 +%32 = OpCompositeExtract %v4float %26 5 +%i5 = OpCompositeInsert %_arr_v4float_uint_8_0 %32 %i4 5 +%33 = OpCompositeExtract %v4float %26 6 +%i6 = OpCompositeInsert %_arr_v4float_uint_8_0 %33 %i5 6 +%34 = OpCompositeExtract %v4float %26 7 +%i7 = OpCompositeInsert %_arr_v4float_uint_8_0 %34 %i6 7 +OpStore %23 %i7 +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CopyPropArrayPassTest, BadCopyViaInserts3) { + const std::string text = + R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%undef = OpUndef %_arr_v4float_uint_8_0 +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%28 = OpCompositeExtract %v4float %26 1 +%i1 = OpCompositeInsert %_arr_v4float_uint_8_0 %28 %undef 1 +%29 = OpCompositeExtract %v4float %26 2 +%i2 = OpCompositeInsert %_arr_v4float_uint_8_0 %29 %i1 2 +%30 = OpCompositeExtract %v4float %26 3 +%i3 = OpCompositeInsert %_arr_v4float_uint_8_0 %30 %i2 3 +%31 = OpCompositeExtract %v4float %26 4 +%i4 = OpCompositeInsert %_arr_v4float_uint_8_0 %31 %i3 4 +%32 = OpCompositeExtract %v4float %26 5 +%i5 = OpCompositeInsert %_arr_v4float_uint_8_0 %32 %i4 5 +%33 = OpCompositeExtract %v4float %26 6 +%i6 = OpCompositeInsert %_arr_v4float_uint_8_0 %33 %i5 6 +%34 = OpCompositeExtract %v4float %26 7 +%i7 = OpCompositeInsert %_arr_v4float_uint_8_0 %34 %i6 7 +OpStore %23 %i7 +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(CopyPropArrayPassTest, AtomicAdd) { + const std::string before = R"(OpCapability SampledBuffer +OpCapability StorageImageExtendedFormats +OpCapability ImageBuffer +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID +OpExecutionMode %2 LocalSize 64 1 1 +OpSource HLSL 600 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +OpDecorate %4 DescriptorSet 4 +OpDecorate %4 Binding 70 +%uint = OpTypeInt 32 0 +%6 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6 +%_ptr_Function_6 = OpTypePointer Function %6 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Image_uint = OpTypePointer Image %uint +%4 = OpVariable %_ptr_UniformConstant_6 UniformConstant +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%2 = OpFunction %void None %10 +%17 = OpLabel +%16 = OpVariable %_ptr_Function_6 Function +%18 = OpLoad %6 %4 +OpStore %16 %18 +%19 = OpImageTexelPointer %_ptr_Image_uint %16 %uint_0 %uint_0 +%20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1 +OpReturn +OpFunctionEnd +)"; + + const std::string after = R"(OpCapability SampledBuffer +OpCapability StorageImageExtendedFormats +OpCapability ImageBuffer +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID +OpExecutionMode %2 LocalSize 64 1 1 +OpSource HLSL 600 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +OpDecorate %4 DescriptorSet 4 +OpDecorate %4 Binding 70 +%uint = OpTypeInt 32 0 +%6 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6 +%_ptr_Function_6 = OpTypePointer Function %6 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Image_uint = OpTypePointer Image %uint +%4 = OpVariable %_ptr_UniformConstant_6 UniformConstant +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%2 = OpFunction %void None %10 +%17 = OpLabel +%16 = OpVariable %_ptr_Function_6 Function +%18 = OpLoad %6 %4 +OpStore %16 %18 +%19 = OpImageTexelPointer %_ptr_Image_uint %4 %uint_0 %uint_0 +%20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(CopyPropArrayPassTest, IndexIsNullConstnat) { + const std::string text = R"( +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Uniform +; CHECK: [[null:%\w+]] = OpConstantNull %uint +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform__arr_uint_uint_1 [[var]] %uint_0 %uint_0 +; CHECK: OpAccessChain %_ptr_Uniform_uint [[ac1]] [[null]] +; CHECK-NEXT: OpReturn + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpDecorate %myCBuffer DescriptorSet 0 + OpDecorate %myCBuffer Binding 0 + OpDecorate %_arr_v4float_uint_1 ArrayStride 16 + OpMemberDecorate %MyConstantBuffer 0 Offset 0 + OpMemberDecorate %type_myCBuffer 0 Offset 0 + OpDecorate %type_myCBuffer Block + %uint = OpTypeInt 32 0 + %int_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 +%_arr_v4float_uint_1 = OpTypeArray %uint %uint_1 +%MyConstantBuffer = OpTypeStruct %_arr_v4float_uint_1 +%type_myCBuffer = OpTypeStruct %MyConstantBuffer +%_ptr_Uniform_type_myCBuffer = OpTypePointer Uniform %type_myCBuffer +%_arr_v4float_uint_1_0 = OpTypeArray %uint %uint_1 + %void = OpTypeVoid + %19 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %uint +%_ptr_Uniform_MyConstantBuffer = OpTypePointer Uniform %MyConstantBuffer + %myCBuffer = OpVariable %_ptr_Uniform_type_myCBuffer Uniform +%_ptr_Function__arr_v4float_uint_1_0 = OpTypePointer Function %_arr_v4float_uint_1_0 + %23 = OpConstantNull %uint + %main = OpFunction %void None %19 + %24 = OpLabel + %25 = OpVariable %_ptr_Function__arr_v4float_uint_1_0 Function + %26 = OpAccessChain %_ptr_Uniform_MyConstantBuffer %myCBuffer %int_0 + %27 = OpLoad %MyConstantBuffer %26 + %28 = OpCompositeExtract %_arr_v4float_uint_1 %27 0 + %29 = OpCompositeExtract %uint %28 0 + %30 = OpCompositeConstruct %_arr_v4float_uint_1_0 %29 + OpStore %25 %30 + %31 = OpAccessChain %_ptr_Function_v4float %25 %23 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(CopyPropArrayPassTest, DebugDeclare) { + const std::string before = + R"(OpCapability Shader +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%uint_32 = OpConstant %uint 32 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main + +; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal + +; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]] +; CHECK: OpAccessChain +; CHECK: [[newptr:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[newptr]] [[deref_expr]] +; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[newptr]] %24 +; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]] +; CHECK: OpStore %out_var_SV_Target [[load]] +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%28 = OpCompositeExtract %v4float %26 1 +%29 = OpCompositeExtract %v4float %26 2 +%30 = OpCompositeExtract %v4float %26 3 +%31 = OpCompositeExtract %v4float %26 4 +%32 = OpCompositeExtract %v4float %26 5 +%33 = OpCompositeExtract %v4float %26 6 +%34 = OpCompositeExtract %v4float %26 7 +%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34 +OpStore %23 %35 +%decl = OpExtInst %void %ext DebugDeclare %dbg_f %23 %null_expr +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(before, false); +} + +TEST_F(CopyPropArrayPassTest, DebugValue) { + const std::string before = + R"(OpCapability Shader +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +OpName %type_MyCBuffer "type.MyCBuffer" +OpMemberName %type_MyCBuffer 0 "Data" +OpName %MyCBuffer "MyCBuffer" +OpName %main "main" +OpName %in_var_INDEX "in.var.INDEX" +OpName %out_var_SV_Target "out.var.SV_Target" +OpDecorate %_arr_v4float_uint_8 ArrayStride 16 +OpMemberDecorate %type_MyCBuffer 0 Offset 0 +OpDecorate %type_MyCBuffer Block +OpDecorate %in_var_INDEX Flat +OpDecorate %in_var_INDEX Location 0 +OpDecorate %out_var_SV_Target Location 0 +OpDecorate %MyCBuffer DescriptorSet 0 +OpDecorate %MyCBuffer Binding 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%uint_32 = OpConstant %uint 32 +%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8 +%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8 +%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer +%void = OpTypeVoid +%13 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8 +%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0 +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform +%in_var_INDEX = OpVariable %_ptr_Input_int Input +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + +; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref +; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]] +%deref = OpExtInst %void %ext DebugOperation Deref +%expr = OpExtInst %void %ext DebugExpression %deref +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main + +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal +%main = OpFunction %void None %13 +%22 = OpLabel +%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function +%24 = OpLoad %int %in_var_INDEX +%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +%26 = OpLoad %_arr_v4float_uint_8 %25 +%27 = OpCompositeExtract %v4float %26 0 +%28 = OpCompositeExtract %v4float %26 1 +%29 = OpCompositeExtract %v4float %26 2 +%30 = OpCompositeExtract %v4float %26 3 +%31 = OpCompositeExtract %v4float %26 4 +%32 = OpCompositeExtract %v4float %26 5 +%33 = OpCompositeExtract %v4float %26 6 +%34 = OpCompositeExtract %v4float %26 7 +%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34 +OpStore %23 %35 + +; CHECK: OpAccessChain +; CHECK: [[newptr:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[newptr]] [[deref_expr]] +; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[newptr]] %24 +; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]] +; CHECK: OpStore %out_var_SV_Target [[load]] +%decl = OpExtInst %void %ext DebugValue %dbg_f %23 %expr +%36 = OpAccessChain %_ptr_Function_v4float %23 %24 +%37 = OpLoad %v4float %36 +OpStore %out_var_SV_Target %37 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(before, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dead_branch_elim_test.cpp b/third_party/spirv-tools/test/opt/dead_branch_elim_test.cpp new file mode 100644 index 0000000..41ce31d --- /dev/null +++ b/third_party/spirv-tools/test/opt/dead_branch_elim_test.cpp @@ -0,0 +1,3409 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using DeadBranchElimTest = PassTest<::testing::Test>; + +TEST_F(DeadBranchElimTest, IfThenElseTrue) { + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v; + // if (true) + // v = vec4(0.0,0.0,0.0,0.0); + // else + // v = vec4(1.0,1.0,1.0,1.0); + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %gl_FragColor "gl_FragColor" +OpName %BaseColor "BaseColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%16 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +OpSelectionMerge %20 None +OpBranchConditional %true %21 %22 +%21 = OpLabel +OpStore %v %14 +OpBranch %20 +%22 = OpLabel +OpStore %v %16 +OpBranch %20 +%20 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +OpBranch %21 +%21 = OpLabel +OpStore %v %14 +OpBranch %20 +%20 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, IfThenElseFalse) { + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v; + // if (false) + // v = vec4(0.0,0.0,0.0,0.0); + // else + // v = vec4(1.0,1.0,1.0,1.0); + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %gl_FragColor "gl_FragColor" +OpName %BaseColor "BaseColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%bool = OpTypeBool +%false = OpConstantFalse %bool +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%16 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +OpSelectionMerge %20 None +OpBranchConditional %false %21 %22 +%21 = OpLabel +OpStore %v %14 +OpBranch %20 +%22 = OpLabel +OpStore %v %16 +OpBranch %20 +%20 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +OpBranch %22 +%22 = OpLabel +OpStore %v %16 +OpBranch %20 +%20 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, IfThenElseNull) { + // For booleans OpConstantNull should be treated similar to OpConstantFalse. + // + // From the SPIR-V spec: + // OpConstantNull: Declares a new null constant value. + // The null value is type dependent, defined as follows: + // - Scalar Boolean: false + // ... + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %gl_FragColor "gl_FragColor" +OpName %BaseColor "BaseColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%bool = OpTypeBool +%9 = OpConstantNull %bool +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%16 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +OpSelectionMerge %20 None +OpBranchConditional %9 %21 %22 +%21 = OpLabel +OpStore %v %14 +OpBranch %20 +%22 = OpLabel +OpStore %v %16 +OpBranch %20 +%20 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +OpBranch %22 +%22 = OpLabel +OpStore %v %16 +OpBranch %20 +%20 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, IfThenTrue) { + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v = BaseColor; + // if (true) + // v = v * vec4(0.5,0.5,0.5,0.5); + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%bool = OpTypeBool +%true = OpConstantTrue %bool +%float_0_5 = OpConstant %float 0.5 +%15 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +OpSelectionMerge %19 None +OpBranchConditional %true %20 %19 +%20 = OpLabel +%21 = OpLoad %v4float %v +%22 = OpFMul %v4float %21 %15 +OpStore %v %22 +OpBranch %19 +%19 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +OpBranch %20 +%20 = OpLabel +%21 = OpLoad %v4float %v +%22 = OpFMul %v4float %21 %15 +OpStore %v %22 +OpBranch %19 +%19 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, IfThenFalse) { + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v = BaseColor; + // if (false) + // v = v * vec4(0.5,0.5,0.5,0.5); + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%bool = OpTypeBool +%false = OpConstantFalse %bool +%float_0_5 = OpConstant %float 0.5 +%15 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +OpSelectionMerge %19 None +OpBranchConditional %false %20 %19 +%20 = OpLabel +%21 = OpLoad %v4float %v +%22 = OpFMul %v4float %21 %15 +OpStore %v %22 +OpBranch %19 +%19 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +OpBranch %19 +%19 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, IfThenElsePhiTrue) { + // Test handling of phi in merge block after dead branch elimination. + // Note: The SPIR-V has had store/load elimination and phi insertion + // + // #version 140 + // + // void main() + // { + // vec4 v; + // if (true) + // v = vec4(0.0,0.0,0.0,0.0); + // else + // v = vec4(1.0,1.0,1.0,1.0); + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +)"; + + const std::string before = + R"(%main = OpFunction %void None %5 +%17 = OpLabel +OpSelectionMerge %18 None +OpBranchConditional %true %19 %20 +%19 = OpLabel +OpBranch %18 +%20 = OpLabel +OpBranch %18 +%18 = OpLabel +%21 = OpPhi %v4float %12 %19 %14 %20 +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %5 +%17 = OpLabel +OpBranch %19 +%19 = OpLabel +OpBranch %18 +%18 = OpLabel +OpStore %gl_FragColor %12 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, IfThenElsePhiFalse) { + // Test handling of phi in merge block after dead branch elimination. + // Note: The SPIR-V has had store/load elimination and phi insertion + // + // #version 140 + // + // void main() + // { + // vec4 v; + // if (true) + // v = vec4(0.0,0.0,0.0,0.0); + // else + // v = vec4(1.0,1.0,1.0,1.0); + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%bool = OpTypeBool +%false = OpConstantFalse %bool +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +)"; + + const std::string before = + R"(%main = OpFunction %void None %5 +%17 = OpLabel +OpSelectionMerge %18 None +OpBranchConditional %false %19 %20 +%19 = OpLabel +OpBranch %18 +%20 = OpLabel +OpBranch %18 +%18 = OpLabel +%21 = OpPhi %v4float %12 %19 %14 %20 +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %5 +%17 = OpLabel +OpBranch %20 +%20 = OpLabel +OpBranch %18 +%18 = OpLabel +OpStore %gl_FragColor %14 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, CompoundIfThenElseFalse) { + // #version 140 + // + // layout(std140) uniform U_t + // { + // bool g_B ; + // } ; + // + // void main() + // { + // vec4 v; + // if (false) { + // if (g_B) + // v = vec4(0.0,0.0,0.0,0.0); + // else + // v = vec4(1.0,1.0,1.0,1.0); + // } else { + // if (g_B) + // v = vec4(1.0,1.0,1.0,1.0); + // else + // v = vec4(0.0,0.0,0.0,0.0); + // } + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_B" +OpName %_ "" +OpName %v "v" +OpName %gl_FragColor "gl_FragColor" +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t Block +OpDecorate %_ DescriptorSet 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%bool = OpTypeBool +%false = OpConstantFalse %bool +%uint = OpTypeInt 32 0 +%U_t = OpTypeStruct %uint +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%uint_0 = OpConstant %uint 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%21 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%25 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +OpSelectionMerge %26 None +OpBranchConditional %false %27 %28 +%27 = OpLabel +%29 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%30 = OpLoad %uint %29 +%31 = OpINotEqual %bool %30 %uint_0 +OpSelectionMerge %32 None +OpBranchConditional %31 %33 %34 +%33 = OpLabel +OpStore %v %21 +OpBranch %32 +%34 = OpLabel +OpStore %v %23 +OpBranch %32 +%32 = OpLabel +OpBranch %26 +%28 = OpLabel +%35 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%36 = OpLoad %uint %35 +%37 = OpINotEqual %bool %36 %uint_0 +OpSelectionMerge %38 None +OpBranchConditional %37 %39 %40 +%39 = OpLabel +OpStore %v %23 +OpBranch %38 +%40 = OpLabel +OpStore %v %21 +OpBranch %38 +%38 = OpLabel +OpBranch %26 +%26 = OpLabel +%41 = OpLoad %v4float %v +OpStore %gl_FragColor %41 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%25 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +OpBranch %28 +%28 = OpLabel +%35 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%36 = OpLoad %uint %35 +%37 = OpINotEqual %bool %36 %uint_0 +OpSelectionMerge %38 None +OpBranchConditional %37 %39 %40 +%40 = OpLabel +OpStore %v %21 +OpBranch %38 +%39 = OpLabel +OpStore %v %23 +OpBranch %38 +%38 = OpLabel +OpBranch %26 +%26 = OpLabel +%41 = OpLoad %v4float %v +OpStore %gl_FragColor %41 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, PreventOrphanMerge) { + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%bool = OpTypeBool +%true = OpConstantTrue %bool +%float_0_5 = OpConstant %float 0.5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%16 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%17 = OpLoad %v4float %BaseColor +OpStore %v %17 +OpSelectionMerge %18 None +OpBranchConditional %true %19 %20 +%19 = OpLabel +OpKill +%20 = OpLabel +%21 = OpLoad %v4float %v +%22 = OpVectorTimesScalar %v4float %21 %float_0_5 +OpStore %v %22 +OpBranch %18 +%18 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%16 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%17 = OpLoad %v4float %BaseColor +OpStore %v %17 +OpBranch %19 +%19 = OpLabel +OpKill +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, HandleOrphanMerge) { + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_ "foo(" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %gl_FragColor Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%9 = OpTypeFunction %v4float +%bool = OpTypeBool +%true = OpConstantTrue %bool +%float_0 = OpConstant %float 0 +%13 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%15 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %6 +%17 = OpLabel +%18 = OpFunctionCall %v4float %foo_ +OpStore %gl_FragColor %18 +OpReturn +OpFunctionEnd +)"; + + const std::string before = + R"(%foo_ = OpFunction %v4float None %9 +%19 = OpLabel +OpSelectionMerge %20 None +OpBranchConditional %true %21 %22 +%21 = OpLabel +OpReturnValue %13 +%22 = OpLabel +OpReturnValue %15 +%20 = OpLabel +%23 = OpUndef %v4float +OpReturnValue %23 +OpFunctionEnd +)"; + + const std::string after = + R"(%foo_ = OpFunction %v4float None %9 +%19 = OpLabel +OpBranch %21 +%21 = OpLabel +OpReturnValue %13 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, KeepContinueTargetWhenKillAfterMerge) { + // #version 450 + // void main() { + // bool c; + // bool d; + // while(c) { + // if(d) { + // continue; + // } + // if(false) { + // continue; + // } + // discard; + // } + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %c "c" +OpName %d "d" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool +%false = OpConstantFalse %bool +)"; + + const std::string before = + R"(%main = OpFunction %void None %6 +%10 = OpLabel +%c = OpVariable %_ptr_Function_bool Function +%d = OpVariable %_ptr_Function_bool Function +OpBranch %11 +%11 = OpLabel +OpLoopMerge %12 %13 None +OpBranch %14 +%14 = OpLabel +%15 = OpLoad %bool %c +OpBranchConditional %15 %16 %12 +%16 = OpLabel +%17 = OpLoad %bool %d +OpSelectionMerge %18 None +OpBranchConditional %17 %19 %18 +%19 = OpLabel +OpBranch %13 +%18 = OpLabel +OpSelectionMerge %20 None +OpBranchConditional %false %21 %20 +%21 = OpLabel +OpBranch %13 +%20 = OpLabel +OpKill +%13 = OpLabel +OpBranch %11 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %6 +%10 = OpLabel +%c = OpVariable %_ptr_Function_bool Function +%d = OpVariable %_ptr_Function_bool Function +OpBranch %11 +%11 = OpLabel +OpLoopMerge %12 %13 None +OpBranch %14 +%14 = OpLabel +%15 = OpLoad %bool %c +OpBranchConditional %15 %16 %12 +%16 = OpLabel +%17 = OpLoad %bool %d +OpSelectionMerge %18 None +OpBranchConditional %17 %19 %18 +%19 = OpLabel +OpBranch %13 +%18 = OpLabel +OpBranch %20 +%20 = OpLabel +OpKill +%13 = OpLabel +OpBranch %11 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, DecorateDeleted) { + // Note: SPIR-V hand-edited to add decoration + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v = BaseColor; + // if (false) + // v = v * vec4(0.5,0.5,0.5,0.5); + // gl_FragColor = v; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %22 RelaxedPrecision +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%bool = OpTypeBool +%false = OpConstantFalse %bool +%float_0_5 = OpConstant %float 0.5 +%15 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%bool = OpTypeBool +%false = OpConstantFalse %bool +%float_0_5 = OpConstant %float 0.5 +%16 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +OpSelectionMerge %19 None +OpBranchConditional %false %20 %19 +%20 = OpLabel +%21 = OpLoad %v4float %v +%22 = OpFMul %v4float %21 %15 +OpStore %v %22 +OpBranch %19 +%19 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%18 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%19 = OpLoad %v4float %BaseColor +OpStore %v %19 +OpBranch %20 +%20 = OpLabel +%23 = OpLoad %v4float %v +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs_before + before, + predefs_after + after, true, true); +} + +TEST_F(DeadBranchElimTest, LoopInDeadBranch) { + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // vec4 v = BaseColor; + // if (false) + // for (int i=0; i<3; i++) + // v = v * 0.5; + // OutColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %i "i" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%bool = OpTypeBool +%false = OpConstantFalse %bool +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_3 = OpConstant %int 3 +%float_0_5 = OpConstant %float 0.5 +%int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%22 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%i = OpVariable %_ptr_Function_int Function +%23 = OpLoad %v4float %BaseColor +OpStore %v %23 +OpSelectionMerge %24 None +OpBranchConditional %false %25 %24 +%25 = OpLabel +OpStore %i %int_0 +OpBranch %26 +%26 = OpLabel +OpLoopMerge %27 %28 None +OpBranch %29 +%29 = OpLabel +%30 = OpLoad %int %i +%31 = OpSLessThan %bool %30 %int_3 +OpBranchConditional %31 %32 %27 +%32 = OpLabel +%33 = OpLoad %v4float %v +%34 = OpVectorTimesScalar %v4float %33 %float_0_5 +OpStore %v %34 +OpBranch %28 +%28 = OpLabel +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %26 +%27 = OpLabel +OpBranch %24 +%24 = OpLabel +%37 = OpLoad %v4float %v +OpStore %OutColor %37 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%22 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%i = OpVariable %_ptr_Function_int Function +%23 = OpLoad %v4float %BaseColor +OpStore %v %23 +OpBranch %24 +%24 = OpLabel +%37 = OpLoad %v4float %v +OpStore %OutColor %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, SwitchLiveCase) { + // #version 450 + // + // layout (location=0) in vec4 BaseColor; + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // switch (1) { + // case 0: + // OutColor = vec4(0.0,0.0,0.0,0.0); + // break; + // case 1: + // OutColor = vec4(0.125,0.125,0.125,0.125); + // break; + // case 2: + // OutColor = vec4(0.25,0.25,0.25,0.25); + // break; + // default: + // OutColor = vec4(1.0,1.0,1.0,1.0); + // } + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %OutColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %OutColor "OutColor" +OpName %BaseColor "BaseColor" +OpDecorate %OutColor Location 0 +OpDecorate %BaseColor Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +%13 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_0_125 = OpConstant %float 0.125 +%15 = OpConstantComposite %v4float %float_0_125 %float_0_125 %float_0_125 %float_0_125 +%float_0_25 = OpConstant %float 0.25 +%17 = OpConstantComposite %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25 +%float_1 = OpConstant %float 1 +%19 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %6 +%21 = OpLabel +OpSelectionMerge %22 None +OpSwitch %int_1 %23 0 %24 1 %25 2 %26 +%23 = OpLabel +OpStore %OutColor %19 +OpBranch %22 +%24 = OpLabel +OpStore %OutColor %13 +OpBranch %22 +%25 = OpLabel +OpStore %OutColor %15 +OpBranch %22 +%26 = OpLabel +OpStore %OutColor %17 +OpBranch %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %6 +%21 = OpLabel +OpBranch %25 +%25 = OpLabel +OpStore %OutColor %15 +OpBranch %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, SwitchLiveDefault) { + // #version 450 + // + // layout (location=0) in vec4 BaseColor; + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // switch (7) { + // case 0: + // OutColor = vec4(0.0,0.0,0.0,0.0); + // break; + // case 1: + // OutColor = vec4(0.125,0.125,0.125,0.125); + // break; + // case 2: + // OutColor = vec4(0.25,0.25,0.25,0.25); + // break; + // default: + // OutColor = vec4(1.0,1.0,1.0,1.0); + // } + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %OutColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %OutColor "OutColor" +OpName %BaseColor "BaseColor" +OpDecorate %OutColor Location 0 +OpDecorate %BaseColor Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_7 = OpConstant %int 7 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +%13 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_0_125 = OpConstant %float 0.125 +%15 = OpConstantComposite %v4float %float_0_125 %float_0_125 %float_0_125 %float_0_125 +%float_0_25 = OpConstant %float 0.25 +%17 = OpConstantComposite %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25 +%float_1 = OpConstant %float 1 +%19 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %6 +%21 = OpLabel +OpSelectionMerge %22 None +OpSwitch %int_7 %23 0 %24 1 %25 2 %26 +%23 = OpLabel +OpStore %OutColor %19 +OpBranch %22 +%24 = OpLabel +OpStore %OutColor %13 +OpBranch %22 +%25 = OpLabel +OpStore %OutColor %15 +OpBranch %22 +%26 = OpLabel +OpStore %OutColor %17 +OpBranch %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %6 +%21 = OpLabel +OpBranch %23 +%23 = OpLabel +OpStore %OutColor %19 +OpBranch %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, SwitchLiveCaseBreakFromLoop) { + // This sample does not directly translate to GLSL/HLSL as + // direct breaks from a loop cannot be made from a switch. + // This construct is currently formed by inlining a function + // containing early returns from the cases of a switch. The + // function is wrapped in a one-trip loop and returns are + // translated to branches to the loop's merge block. + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %OutColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %oc "oc" +OpName %OutColor "OutColor" +OpName %BaseColor "BaseColor" +OpDecorate %OutColor Location 0 +OpDecorate %BaseColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%17 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_0_125 = OpConstant %float 0.125 +%19 = OpConstantComposite %v4float %float_0_125 %float_0_125 %float_0_125 %float_0_125 +%float_0_25 = OpConstant %float 0.25 +%21 = OpConstantComposite %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25 +%float_1 = OpConstant %float 1 +%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%26 = OpLabel +%oc = OpVariable %_ptr_Function_v4float Function +OpBranch %27 +%27 = OpLabel +OpLoopMerge %28 %29 None +OpBranch %30 +%30 = OpLabel +OpSelectionMerge %31 None +OpSwitch %int_1 %31 0 %32 1 %33 2 %34 +%32 = OpLabel +OpStore %oc %17 +OpBranch %28 +%33 = OpLabel +OpStore %oc %19 +OpBranch %28 +%34 = OpLabel +OpStore %oc %21 +OpBranch %28 +%31 = OpLabel +OpStore %oc %23 +OpBranch %28 +%29 = OpLabel +OpBranchConditional %false %27 %28 +%28 = OpLabel +%35 = OpLoad %v4float %oc +OpStore %OutColor %35 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%26 = OpLabel +%oc = OpVariable %_ptr_Function_v4float Function +OpBranch %27 +%27 = OpLabel +OpLoopMerge %28 %29 None +OpBranch %30 +%30 = OpLabel +OpBranch %33 +%33 = OpLabel +OpStore %oc %19 +OpBranch %28 +%29 = OpLabel +OpBranch %27 +%28 = OpLabel +%35 = OpLoad %v4float %oc +OpStore %OutColor %35 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(DeadBranchElimTest, LeaveContinueBackedge) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None +; CHECK: [[continue]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} {{%\w+}} [[merge]] +; CHECK-NEXT: [[merge]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%bool = OpTypeBool +%false = OpConstantFalse %bool +%void = OpTypeVoid +%funcTy = OpTypeFunction %void +%func = OpFunction %void None %funcTy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranch %4 +%4 = OpLabel +; Be careful we don't remove the backedge to %2 despite never taking it. +OpBranchConditional %false %2 %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, LeaveContinueBackedgeExtraBlock) { + const std::string text = R"( +; CHECK: OpBranch [[header:%\w+]] +; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None +; CHECK-NEXT: OpBranch [[continue]] +; CHECK-NEXT: [[continue]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[extra:%\w+]] [[merge]] +; CHECK-NEXT: [[extra]] = OpLabel +; CHECK-NEXT: OpBranch [[header]] +; CHECK-NEXT: [[merge]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%bool = OpTypeBool +%false = OpConstantFalse %bool +%void = OpTypeVoid +%funcTy = OpTypeFunction %void +%func = OpFunction %void None %funcTy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranch %4 +%4 = OpLabel +; Be careful we don't remove the backedge to %2 despite never taking it. +OpBranchConditional %false %5 %3 +; This block remains live despite being unreachable. +%5 = OpLabel +OpBranch %2 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, RemovePhiWithUnreachableContinue) { + const std::string text = R"( +; CHECK: [[entry:%\w+]] = OpLabel +; CHECK-NEXT: OpBranch [[header:%\w+]] +; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None +; CHECK-NEXT: OpBranch [[ret:%\w+]] +; CHECK-NEXT: [[ret]] = OpLabel +; CHECK-NEXT: OpReturn +; CHECK: [[continue]] = OpLabel +; CHECK-NEXT: OpBranch [[header]] +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpUnreachable +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%bool = OpTypeBool +%false = OpConstantFalse %bool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%funcTy = OpTypeFunction %void +%func = OpFunction %void None %funcTy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +%phi = OpPhi %bool %false %1 %true %continue +OpLoopMerge %merge %continue None +OpBranch %3 +%3 = OpLabel +OpReturn +%continue = OpLabel +OpBranch %2 +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, UnreachableLoopMergeAndContinueTargets) { + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %bool +; CHECK: OpSelectionMerge [[header:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_lab:%\w+]] [[else_lab:%\w+]] +; CHECK: OpPhi %bool %false [[if_lab]] %false [[else_lab]] [[undef]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK-NEXT: OpBranch [[ret:%\w+]] +; CHECK-NEXT: [[ret]] = OpLabel +; CHECK-NEXT: OpReturn +; CHECK: [[continue]] = OpLabel +; CHECK-NEXT: OpBranch [[header]] +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpUnreachable +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%bool = OpTypeBool +%false = OpConstantFalse %bool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%funcTy = OpTypeFunction %void +%func = OpFunction %void None %funcTy +%1 = OpLabel +%c = OpUndef %bool +OpSelectionMerge %2 None +OpBranchConditional %c %if %else +%if = OpLabel +OpBranch %2 +%else = OpLabel +OpBranch %2 +%2 = OpLabel +%phi = OpPhi %bool %false %if %false %else %true %continue +OpLoopMerge %merge %continue None +OpBranch %3 +%3 = OpLabel +OpReturn +%continue = OpLabel +OpBranch %2 +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} +TEST_F(DeadBranchElimTest, EarlyReconvergence) { + const std::string text = R"( +; CHECK-NOT: OpBranchConditional +; CHECK: [[logical:%\w+]] = OpLogicalOr +; CHECK-NOT: OpPhi +; CHECK: OpLogicalAnd {{%\w+}} {{%\w+}} [[logical]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%false = OpConstantFalse %bool +%true = OpConstantTrue %bool +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpSelectionMerge %2 None +OpBranchConditional %false %3 %4 +%3 = OpLabel +%12 = OpLogicalNot %bool %true +OpBranch %2 +%4 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %false %5 %6 +%5 = OpLabel +%10 = OpLogicalAnd %bool %true %false +OpBranch %7 +%6 = OpLabel +%11 = OpLogicalOr %bool %true %false +OpBranch %7 +%7 = OpLabel +; This phi is in a block preceeding the merge %14! +%8 = OpPhi %bool %10 %5 %11 %6 +OpBranch %14 +%14 = OpLabel +OpBranch %2 +%2 = OpLabel +%9 = OpPhi %bool %12 %3 %8 %14 +%13 = OpLogicalAnd %bool %true %9 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, RemoveUnreachableBlocksFloating) { + const std::string text = R"( +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%1 = OpTypeFunction %void +%func = OpFunction %void None %1 +%2 = OpLabel +OpReturn +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, RemoveUnreachableBlocksFloatingJoin) { + const std::string text = R"( +; CHECK: OpFunction +; CHECK-NEXT: OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%false = OpConstantFalse %bool +%true = OpConstantTrue %bool +%1 = OpTypeFunction %void %bool +%func = OpFunction %void None %1 +%bool_param = OpFunctionParameter %bool +%2 = OpLabel +OpReturn +%3 = OpLabel +OpSelectionMerge %6 None +OpBranchConditional %bool_param %4 %5 +%4 = OpLabel +OpBranch %6 +%5 = OpLabel +OpBranch %6 +%6 = OpLabel +%7 = OpPhi %bool %true %4 %false %6 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, RemoveUnreachableBlocksDeadPhi) { + const std::string text = R"( +; CHECK: OpFunction +; CHECK-NEXT: OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpBranch [[label:%\w+]] +; CHECK-NEXT: [[label]] = OpLabel +; CHECK-NEXT: OpLogicalNot %bool %true +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpFunctionEnd +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%false = OpConstantFalse %bool +%true = OpConstantTrue %bool +%1 = OpTypeFunction %void %bool +%func = OpFunction %void None %1 +%bool_param = OpFunctionParameter %bool +%2 = OpLabel +OpBranch %3 +%4 = OpLabel +OpBranch %3 +%3 = OpLabel +%5 = OpPhi %bool %true %2 %false %4 +%6 = OpLogicalNot %bool %5 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, RemoveUnreachableBlocksPartiallyDeadPhi) { + const std::string text = R"( +; CHECK: OpFunction +; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpBranchConditional [[param]] [[merge:%\w+]] [[br:%\w+]] +; CHECK-NEXT: [[merge]] = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool %true %2 %false [[br]] +; CHECK-NEXT: OpLogicalNot %bool [[phi]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: [[br]] = OpLabel +; CHECK-NEXT: OpBranch [[merge]] +; CHECK-NEXT: OpFunctionEnd +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%false = OpConstantFalse %bool +%true = OpConstantTrue %bool +%1 = OpTypeFunction %void %bool +%func = OpFunction %void None %1 +%bool_param = OpFunctionParameter %bool +%2 = OpLabel +OpBranchConditional %bool_param %3 %7 +%7 = OpLabel +OpBranch %3 +%4 = OpLabel +OpBranch %3 +%3 = OpLabel +%5 = OpPhi %bool %true %2 %false %7 %false %4 +%6 = OpLogicalNot %bool %5 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, LiveHeaderDeadPhi) { + const std::string text = R"( +; CHECK: OpLabel +; CHECK-NOT: OpBranchConditional +; CHECK-NOT: OpPhi +; CHECK: OpLogicalNot %bool %false +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpSelectionMerge %3 None +OpBranchConditional %true %2 %3 +%2 = OpLabel +OpBranch %3 +%3 = OpLabel +%5 = OpPhi %bool %true %3 %false %2 +%6 = OpLogicalNot %bool %5 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, ExtraBackedgeBlocksLive) { + const std::string text = R"( +; CHECK: [[entry:%\w+]] = OpLabel +; CHECK-NOT: OpSelectionMerge +; CHECK: OpBranch [[header:%\w+]] +; CHECK-NEXT: [[header]] = OpLabel +; CHECK-NEXT: OpPhi %bool %true [[entry]] %false [[backedge:%\w+]] +; CHECK-NEXT: OpLoopMerge +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%func_ty = OpTypeFunction %void %bool +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %bool +%entry = OpLabel +OpSelectionMerge %if_merge None +; This dead branch is included to ensure the pass does work. +OpBranchConditional %false %if_merge %loop_header +%loop_header = OpLabel +; Both incoming edges are live, so the phi should be untouched. +%phi = OpPhi %bool %true %entry %false %backedge +OpLoopMerge %loop_merge %continue None +OpBranchConditional %param %loop_merge %continue +%continue = OpLabel +OpBranch %backedge +%backedge = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpBranch %if_merge +%if_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, ExtraBackedgeBlocksUnreachable) { + const std::string text = R"( +; CHECK: [[entry:%\w+]] = OpLabel +; CHECK-NEXT: OpBranch [[header:%\w+]] +; CHECK-NEXT: [[header]] = OpLabel +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None +; CHECK-NEXT: OpBranch [[merge]] +; CHECK-NEXT: [[merge]] = OpLabel +; CHECK-NEXT: OpReturn +; CHECK-NEXT: [[continue]] = OpLabel +; CHECK-NEXT: OpBranch [[header]] +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%func_ty = OpTypeFunction %void %bool +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %bool +%entry = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +; Since the continue is unreachable, %backedge will be removed. The phi will +; instead require an edge from %continue. +%phi = OpPhi %bool %true %entry %false %backedge +OpLoopMerge %merge %continue None +OpBranch %merge +%continue = OpLabel +OpBranch %backedge +%backedge = OpLabel +OpBranch %loop_header +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, NoUnnecessaryChanges) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%undef = OpUndef %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %4 %5 None +OpBranch %6 +%6 = OpLabel +OpReturn +%5 = OpLabel +OpBranch %2 +%4 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(std::get<1>(result), Pass::Status::SuccessWithoutChange); +} + +TEST_F(DeadBranchElimTest, ExtraBackedgePartiallyDead) { + const std::string text = R"( +; CHECK: OpLabel +; CHECK: [[header:%\w+]] = OpLabel +; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None +; CHECK: [[merge]] = OpLabel +; CHECK: [[continue]] = OpLabel +; CHECK: OpBranch [[extra:%\w+]] +; CHECK: [[extra]] = OpLabel +; CHECK-NOT: OpSelectionMerge +; CHECK-NEXT: OpBranch [[else:%\w+]] +; CHECK-NEXT: [[else]] = OpLabel +; CHECK-NEXT: OpLogicalOr +; CHECK-NEXT: OpBranch [[backedge:%\w+]] +; CHECK-NEXT: [[backedge:%\w+]] = OpLabel +; CHECK-NEXT: OpBranch [[header]] +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpName %func "func" +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%func_ty = OpTypeFunction %void %bool +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %bool +%entry = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %continue None +OpBranchConditional %param %loop_merge %continue +%continue = OpLabel +OpBranch %extra +%extra = OpLabel +OpSelectionMerge %backedge None +OpBranchConditional %false %then %else +%then = OpLabel +%and = OpLogicalAnd %bool %true %false +OpBranch %backedge +%else = OpLabel +%or = OpLogicalOr %bool %true %false +OpBranch %backedge +%backedge = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, UnreachableContinuePhiInMerge) { + const std::string text = R"( +; CHECK: [[entry:%\w+]] = OpLabel +; CHECK-NEXT: OpBranch [[header:%\w+]] +; CHECK-NEXT: [[header]] = OpLabel +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] None +; CHECK-NEXT: OpBranch [[label:%\w+]] +; CHECK-NEXT: [[label]] = OpLabel +; CHECK-NEXT: [[fadd:%\w+]] = OpFAdd +; CHECK-NEXT: OpBranch [[label:%\w+]] +; CHECK-NEXT: [[label]] = OpLabel +; CHECK-NEXT: OpBranch [[merge]] +; CHECK-NEXT: [[continue]] = OpLabel +; CHECK-NEXT: OpBranch [[header]] +; CHECK-NEXT: [[merge]] = OpLabel +; CHECK-NEXT: OpStore {{%\w+}} [[fadd]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 430 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %o "o" + OpName %S "S" + OpMemberName %S 0 "a" + OpName %U_t "U_t" + OpMemberName %U_t 0 "g_F" + OpMemberName %U_t 1 "g_F2" + OpDecorate %o Location 0 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %U_t 0 Volatile + OpMemberDecorate %U_t 0 Offset 0 + OpMemberDecorate %U_t 1 Offset 4 + OpDecorate %U_t BufferBlock + %void = OpTypeVoid + %7 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %true = OpConstantTrue %bool + %float_1 = OpConstant %float 1 + %float_5 = OpConstant %float 5 + %int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float + %o = OpVariable %_ptr_Output_float Output + %S = OpTypeStruct %float + %U_t = OpTypeStruct %S %S +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t + %main = OpFunction %void None %7 + %22 = OpLabel + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %float %float_0 %22 %25 %26 + %27 = OpPhi %int %int_0 %22 %28 %26 + OpLoopMerge %29 %26 None + OpBranch %40 + %40 = OpLabel + %25 = OpFAdd %float %24 %float_1 + OpSelectionMerge %30 None + OpBranchConditional %true %31 %30 + %31 = OpLabel + OpBranch %29 + %30 = OpLabel + OpBranch %26 + %26 = OpLabel + %28 = OpIAdd %int %27 %int_1 + %32 = OpSLessThan %bool %27 %int_10 +; continue block branches to the header or another none dead block. + OpBranchConditional %32 %23 %29 + %29 = OpLabel + %33 = OpPhi %float %24 %26 %25 %31 + OpStore %o %33 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, NonStructuredIf) { + const std::string text = R"( +; CHECK-NOT: OpBranchConditional +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpDecorate %func LinkageAttributes "func" Export +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpBranchConditional %true %then %else +%then = OpLabel +OpBranch %final +%else = OpLabel +OpBranch %final +%final = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, ReorderBlocks) { + const std::string text = R"( +; CHECK: OpLabel +; CHECK: OpBranch [[label:%\w+]] +; CHECK: [[label:%\w+]] = OpLabel +; CHECK-NEXT: OpLogicalNot +; CHECK-NEXT: OpBranch [[label:%\w+]] +; CHECK: [[label]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpSelectionMerge %3 None +OpBranchConditional %true %2 %3 +%3 = OpLabel +OpReturn +%2 = OpLabel +%not = OpLogicalNot %bool %true +OpBranch %3 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, ReorderBlocksMultiple) { + // Checks are not important. The validation post optimization is the + // important part. + const std::string text = R"( +; CHECK: OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpSelectionMerge %3 None +OpBranchConditional %true %2 %3 +%3 = OpLabel +OpReturn +%2 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %3 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, ReorderBlocksMultiple2) { + // Checks are not important. The validation post optimization is the + // important part. + const std::string text = R"( +; CHECK: OpLabel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpSelectionMerge %3 None +OpBranchConditional %true %2 %3 +%3 = OpLabel +OpBranch %5 +%5 = OpLabel +OpReturn +%2 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %3 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithEarlyExit1) { + // Checks that if a selection merge construct contains a conditional branch + // to the merge node, then the OpSelectionMerge instruction is positioned + // correctly. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%undef_bool = OpUndef %bool +)"; + + const std::string body = + R"( +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpBranch [[taken_branch:%\w+]] +; CHECK-NEXT: [[taken_branch]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[merge]] {{%\w+}} +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpSelectionMerge %outer_merge None +OpBranchConditional %true %bb1 %bb3 +%bb1 = OpLabel +OpBranchConditional %undef_bool %outer_merge %bb2 +%bb2 = OpLabel +OpBranch %outer_merge +%bb3 = OpLabel +OpBranch %outer_merge +%outer_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithEarlyExit2) { + // Checks that if a selection merge construct contains a conditional branch + // to the merge node, then the OpSelectionMerge instruction is positioned + // correctly. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%undef_bool = OpUndef %bool +)"; + + const std::string body = + R"( +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK-NEXT: [[bb1]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[inner_merge:%\w+]] +; CHECK: [[inner_merge]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[outer_merge:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[outer_merge]:%\w+]] {{%\w+}} +; CHECK: [[outer_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpSelectionMerge %outer_merge None +OpBranchConditional %true %bb1 %bb5 +%bb1 = OpLabel +OpSelectionMerge %inner_merge None +OpBranchConditional %undef_bool %bb2 %bb3 +%bb2 = OpLabel +OpBranch %inner_merge +%bb3 = OpLabel +OpBranch %inner_merge +%inner_merge = OpLabel +OpBranchConditional %undef_bool %outer_merge %bb4 +%bb4 = OpLabel +OpBranch %outer_merge +%bb5 = OpLabel +OpBranch %outer_merge +%outer_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithConditionalExit) { + // Checks that if a selection merge construct contains a conditional branch + // to the merge node, then we keep the OpSelectionMerge on that branch. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%undef_int = OpUndef %uint +)"; + + const std::string body = + R"( +; CHECK: OpLoopMerge [[loop_merge:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None +; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 1 [[bb3:%\w+]] +; CHECK: [[bb3]] = OpLabel +; CHECK-NEXT: OpBranch [[sel_merge]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpSwitch %undef_int %sel_merge 1 %bb3 +%bb3 = OpLabel +OpBranch %sel_merge +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop) { + // Checks that if a selection merge construct contains a conditional branch + // to a loop surrounding the selection merge, then we do not keep the + // OpSelectionMerge instruction. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%undef_bool = OpUndef %bool +)"; + + const std::string body = + R"( +; CHECK: OpLoopMerge [[loop_merge:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb3:%\w+]] [[loop_merge]] +; CHECK: [[bb3]] = OpLabel +; CHECK-NEXT: OpBranch [[sel_merge:%\w+]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpBranchConditional %undef_bool %bb3 %loop_merge +%bb3 = OpLabel +OpBranch %sel_merge +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue) { + // Checks that if a selection merge construct contains a conditional branch + // to continue of a loop surrounding the selection merge, then we do not keep + // the OpSelectionMerge instruction. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%undef_bool = OpUndef %bool +)"; + + const std::string body = + R"(; +; CHECK: OpLabel +; CHECK: [[loop_header:%\w+]] = OpLabel +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb3:%\w+]] [[loop_cont]] +; CHECK: [[bb3]] = OpLabel +; CHECK-NEXT: OpBranch [[sel_merge:%\w+]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_cont]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_header]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpBranchConditional %undef_bool %bb3 %cont +%bb3 = OpLabel +OpBranch %sel_merge +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop2) { + // Same as |SelectionMergeWithExitToLoop|, except the switch goes to the loop + // merge or the selection merge. In this case, we do not need an + // OpSelectionMerge either. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%undef_bool = OpUndef %bool +)"; + + const std::string body = + R"( +; CHECK: OpLoopMerge [[loop_merge:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge:%\w+]] [[loop_merge]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpBranchConditional %undef_bool %sel_merge %loop_merge +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue2) { + // Same as |SelectionMergeWithExitToLoopContinue|, except the branch goes to + // the loop continue or the selection merge. In this case, we do not need an + // OpSelectionMerge either. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%undef_bool = OpUndef %bool +)"; + + const std::string body = + R"( +; CHECK: OpLabel +; CHECK: [[loop_header:%\w+]] = OpLabel +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[sel_merge:%\w+]] [[loop_cont]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_cont]] = OpLabel +; CHECK: OpBranch [[loop_header]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpBranchConditional %undef_bool %sel_merge %cont +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop3) { + // Checks that if a selection merge construct contains a conditional branch + // to the merge of a surrounding loop, the selection merge, and another block + // inside the selection merge, then we must keep the OpSelectionMerge + // instruction on that branch. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%undef_int = OpUndef %uint +)"; + + const std::string body = + R"( +; CHECK: OpLoopMerge [[loop_merge:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None +; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_merge]] 1 [[bb3:%\w+]] +; CHECK: [[bb3]] = OpLabel +; CHECK-NEXT: OpBranch [[sel_merge]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpSwitch %undef_int %sel_merge 0 %loop_merge 1 %bb3 +%bb3 = OpLabel +OpBranch %sel_merge +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue3) { + // Checks that if a selection merge construct contains a conditional branch + // to the merge of a surrounding loop, the selection merge, and another block + // inside the selection merge, then we must keep the OpSelectionMerge + // instruction on that branch. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%undef_int = OpUndef %uint +)"; + + const std::string body = + R"( +; CHECK: OpLabel +; CHECK: [[loop_header:%\w+]] = OpLabel +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_continue:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[sel_merge:%\w+]] None +; CHECK-NEXT: OpSwitch {{%\w+}} [[sel_merge]] 0 [[loop_continue]] 1 [[bb3:%\w+]] +; CHECK: [[bb3]] = OpLabel +; CHECK-NEXT: OpBranch [[sel_merge]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_continue]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_header]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpSwitch %undef_int %sel_merge 0 %cont 1 %bb3 +%bb3 = OpLabel +OpBranch %sel_merge +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoop4) { + // Same as |SelectionMergeWithExitToLoop|, except the branch in the selection + // construct is an |OpSwitch| instead of an |OpConditionalBranch|. The + // OpSelectionMerge instruction is not needed in this case either. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%undef_int = OpUndef %uint +)"; + + const std::string body = + R"( +; CHECK: OpLoopMerge [[loop_merge:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_merge]] 1 [[bb3:%\w+]] +; CHECK: [[bb3]] = OpLabel +; CHECK-NEXT: OpBranch [[sel_merge:%\w+]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpSwitch %undef_int %bb3 0 %loop_merge 1 %bb3 +%bb3 = OpLabel +OpBranch %sel_merge +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithExitToLoopContinue4) { + // Same as |SelectionMergeWithExitToLoopContinue|, except the branch in the + // selection construct is an |OpSwitch| instead of an |OpConditionalBranch|. + // The OpSelectionMerge instruction is not needed in this case either. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%undef_int = OpUndef %uint +)"; + + const std::string body = + R"( +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSwitch {{%\w+}} [[bb3:%\w+]] 0 [[loop_cont]] 1 [[bb3:%\w+]] +; CHECK: [[bb3]] = OpLabel +; CHECK-NEXT: OpBranch [[sel_merge:%\w+]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_merge]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %sel_merge None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpSwitch %undef_int %bb3 0 %cont 1 %bb3 +%bb3 = OpLabel +OpBranch %sel_merge +%bb4 = OpLabel +OpBranch %sel_merge +%sel_merge = OpLabel +OpBranch %loop_merge +%cont = OpLabel +OpBranch %loop_header +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + body, true); +} + +TEST_F(DeadBranchElimTest, SelectionMergeSameAsLoopContinue) { + // Same as |SelectionMergeWithExitToLoopContinue|, except the branch in the + // selection construct is an |OpSwitch| instead of an |OpConditionalBranch|. + // The OpSelectionMerge instruction is not needed in this case either. + const std::string predefs = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%void = OpTypeVoid +%func_type = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%undef_bool = OpUndef %bool +)"; + + const std::string body = + R"( +; CHECK: OpLabel +; CHECK: [[loop_header:%\w+]] = OpLabel +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[loop_cont]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb3:%\w+]] [[loop_cont]] +; CHECK: [[bb3]] = OpLabel +; CHECK-NEXT: OpBranch [[loop_cont]] +; CHECK: [[loop_cont]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[loop_header]] [[loop_merge]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK-NEXT: OpReturn +%main = OpFunction %void None %func_type +%entry_bb = OpLabel +OpBranch %loop_header +%loop_header = OpLabel +OpLoopMerge %loop_merge %cont None +OpBranch %bb1 +%bb1 = OpLabel +OpSelectionMerge %cont None +OpBranchConditional %true %bb2 %bb4 +%bb2 = OpLabel +OpBranchConditional %undef_bool %bb3 %cont +%bb3 = OpLabel +OpBranch %cont +%bb4 = OpLabel +OpBranch %cont +%cont = OpLabel +OpBranchConditional %undef_bool %loop_header %loop_merge +%loop_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + // The selection merge in the loop naming the continue target as merge is + // invalid, but handled by this pass so validation is disabled. + SinglePassRunAndMatch(predefs + body, false); +} + +TEST_F(DeadBranchElimTest, SelectionMergeWithNestedLoop) { + const std::string body = + R"( +; CHECK: OpSelectionMerge [[merge1:%\w+]] +; CHECK: [[merge1]] = OpLabel +; CHECK-NEXT: OpBranch [[preheader:%\w+]] +; CHECK: [[preheader]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpBranch [[header:%\w+]] +; CHECK: [[header]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpLoopMerge [[merge2:%\w+]] +; CHECK: [[merge2]] = OpLabel +; CHECK-NEXT: OpUnreachable + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %h "h" + OpName %i "i" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool + %_ptr_Function_bool = OpTypePointer Function %bool + %true = OpConstantTrue %bool + %int = OpTypeInt 32 1 + %_ptr_Function_int = OpTypePointer Function %int + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %27 = OpUndef %bool + %main = OpFunction %void None %3 + %5 = OpLabel + %h = OpVariable %_ptr_Function_bool Function + %i = OpVariable %_ptr_Function_int Function + OpSelectionMerge %11 None + OpBranchConditional %27 %10 %11 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %true %13 %14 + %13 = OpLabel + OpStore %i %int_1 + OpBranch %19 + %19 = OpLabel + OpLoopMerge %21 %22 None + OpBranch %23 + %23 = OpLabel + %26 = OpSGreaterThan %bool %int_1 %int_0 + OpBranchConditional %true %20 %21 + %20 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %19 + %21 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(body, true); +} + +TEST_F(DeadBranchElimTest, DontFoldBackedge) { + const std::string body = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%false = OpConstantFalse %bool +%2 = OpFunction %void None %4 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +OpLoopMerge %9 %10 None +OpBranch %11 +%11 = OpLabel +%12 = OpUndef %bool +OpSelectionMerge %10 None +OpBranchConditional %12 %13 %10 +%13 = OpLabel +OpBranch %9 +%10 = OpLabel +OpBranch %14 +%14 = OpLabel +OpBranchConditional %false %8 %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(body, body, true); +} + +TEST_F(DeadBranchElimTest, FoldBackedgeToHeader) { + const std::string body = + R"( +; CHECK: OpLabel +; CHECK: [[header:%\w+]] = OpLabel +; CHECK-NEXT: OpLoopMerge {{%\w+}} [[cont:%\w+]] +; CHECK: [[cont]] = OpLabel +; This branch may not be in the continue block, but must come after it. +; CHECK: OpBranch [[header]] +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%2 = OpFunction %void None %4 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +OpLoopMerge %9 %10 None +OpBranch %11 +%11 = OpLabel +%12 = OpUndef %bool +OpSelectionMerge %10 None +OpBranchConditional %12 %13 %10 +%13 = OpLabel +OpBranch %9 +%10 = OpLabel +OpBranch %14 +%14 = OpLabel +OpBranchConditional %true %8 %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // The selection merge in the loop naming the continue target as merge is + // invalid, but handled by this pass so validation is disabled. + SinglePassRunAndMatch(body, false); +} + +TEST_F(DeadBranchElimTest, UnreachableMergeAndContinueSameBlock) { + const std::string spirv = R"( +; CHECK: OpLabel +; CHECK: [[outer:%\w+]] = OpLabel +; CHECK-NEXT: OpLoopMerge [[outer_merge:%\w+]] [[outer_cont:%\w+]] None +; CHECK-NEXT: OpBranch [[inner:%\w+]] +; CHECK: [[inner]] = OpLabel +; CHECK: OpLoopMerge [[outer_cont]] [[inner_cont:%\w+]] None +; CHECK: [[inner_cont]] = OpLabel +; CHECK-NEXT: OpBranch [[inner]] +; CHECK: [[outer_cont]] = OpLabel +; CHECK-NEXT: OpBranch [[outer]] +; CHECK: [[outer_merge]] = OpLabel +; CHECK-NEXT: OpUnreachable +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %outer_loop +%outer_loop = OpLabel +OpLoopMerge %outer_merge %outer_continue None +OpBranch %inner_loop +%inner_loop = OpLabel +OpLoopMerge %outer_continue %inner_continue None +OpBranch %inner_body +%inner_body = OpLabel +OpSelectionMerge %inner_continue None +OpBranchConditional %true %ret %inner_continue +%ret = OpLabel +OpReturn +%inner_continue = OpLabel +OpBranchConditional %true %outer_continue %inner_loop +%outer_continue = OpLabel +OpBranchConditional %true %outer_merge %outer_loop +%outer_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(spirv, true); +} + +// Fold a switch with a nested break. The only case should be the default. +TEST_F(DeadBranchElimTest, FoldSwitchWithNestedBreak) { + const std::string spirv = R"( +; CHECK: OpSwitch %int_3 [[case_bb:%\w+]]{{[[:space:]]}} +; CHECK: [[case_bb]] = OpLabel +; CHECK-NEXT: OpUndef +; CHECK-NEXT: OpSelectionMerge + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "main" + OpSource GLSL 450 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 + %bool = OpTypeBool + %2 = OpFunction %void None %4 + %10 = OpLabel + OpSelectionMerge %11 None + OpSwitch %int_3 %12 3 %13 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + %14 = OpUndef %bool + OpSelectionMerge %15 None + OpBranchConditional %14 %16 %15 + %16 = OpLabel + OpBranch %11 + %15 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(DeadBranchElimTest, FoldBranchWithBreakToSwitch) { + const std::string spirv = R"( +; CHECK: OpSelectionMerge [[sel_merge:%\w+]] +; CHECK-NEXT: OpSwitch {{%\w+}} {{%\w+}} 3 [[bb:%\w+]] +; CHECK: [[bb]] = OpLabel +; CHECK-NEXT: OpBranch [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NOT: OpSelectionMerge +; CHECK: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "main" + OpSource GLSL 450 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 + %bool = OpTypeBool + %true = OpConstantTrue %bool + %2 = OpFunction %void None %4 + %10 = OpLabel + %undef_int = OpUndef %int + OpSelectionMerge %11 None + OpSwitch %undef_int %12 3 %13 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %true %16 %15 + %16 = OpLabel + %14 = OpUndef %bool + OpBranchConditional %14 %11 %17 + %17 = OpLabel + OpBranch %15 + %15 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(DeadBranchElimTest, IfInSwitch) { + // #version 310 es + // + // void main() + // { + // switch(0) + // { + // case 0: + // if(false) + // { + // } + // else + // { + // } + // } + // } + + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +OpName %main "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%bool = OpTypeBool +%false = OpConstantFalse %bool +%main = OpFunction %void None %3 +%5 = OpLabel +OpSelectionMerge %9 None +OpSwitch %int_0 %9 0 %8 +%8 = OpLabel +OpSelectionMerge %13 None +OpBranchConditional %false %12 %13 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%bool = OpTypeBool +%false = OpConstantFalse %bool +%main = OpFunction %void None %4 +%9 = OpLabel +OpBranch %11 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(DeadBranchElimTest, BreakInNestedHeaderWithSingleCase) { + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%8 = OpUndef %bool +%main = OpFunction %void None %4 +%9 = OpLabel +OpSelectionMerge %10 None +OpSwitch %uint_0 %11 +%11 = OpLabel +OpSelectionMerge %12 None +OpBranchConditional %8 %10 %12 +%12 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(DeadBranchElimTest, BreakInNestedHeaderWithTwoCases) { + const std::string text = R"( +; CHECK: OpSelectionMerge [[merge:%\w+]] None +; CHECK-NEXT: OpSwitch %uint_0 [[bb:%\w+\n]] +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%8 = OpUndef %bool +%main = OpFunction %void None %4 +%9 = OpLabel +OpSelectionMerge %10 None +OpSwitch %uint_0 %11 1 %12 +%11 = OpLabel +OpSelectionMerge %13 None +OpBranchConditional %8 %10 %13 +%13 = OpLabel +OpBranch %10 +%12 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DeadBranchElimTest, DebugInformation) { + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +%name = OpString "test" +OpName %main "main" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 + +; CHECK: [[value:%\w+]] = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 + +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void +%dbg_main = OpExtInst %void %ext DebugFunction %name %ty %src 0 0 %cu %name FlagIsProtected|FlagIsPrivate 0 %main + +; CHECK: [[bb1:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugLexicalBlock [[src:%\w+]] 1 0 [[dbg_main:%\w+]] +; CHECK: [[bb2:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock [[src]] 2 0 [[dbg_main]] +; CHECK: [[bb3:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock [[src]] 3 0 [[dbg_main]] +%bb1 = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %dbg_main +%bb2 = OpExtInst %void %ext DebugLexicalBlock %src 2 0 %dbg_main +%bb3 = OpExtInst %void %ext DebugLexicalBlock %src 3 0 %dbg_main + +%dbg_f = OpExtInst %void %ext DebugTypeBasic %name %uint_32 Float +; CHECK: [[dbg_foo:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable {{%\w+}} [[ty:%\w+]] [[src]] 0 0 [[dbg_main]] +%dbg_foo = OpExtInst %void %ext DebugLocalVariable %name %dbg_f %src 0 0 %dbg_main FlagIsLocal +; CHECK: [[dbg_bar:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable {{%\w+}} [[ty]] [[src]] 1 0 [[bb3]] +%dbg_bar = OpExtInst %void %ext DebugLocalVariable %name %dbg_f %src 1 0 %bb3 FlagIsLocal + +%main = OpFunction %void None %5 +%17 = OpLabel +; CHECK-NOT: DebugScope [[dbg_main]] +; CHECK-NOT: OpLine {{%\w+}} 0 0 +%scope0 = OpExtInst %void %ext DebugScope %dbg_main +OpLine %name 0 0 +OpSelectionMerge %18 None +OpBranchConditional %true %19 %20 +%19 = OpLabel +; CHECK: DebugScope [[bb1]] +; CHECK: OpLine {{%\w+}} 1 0 +%scope1 = OpExtInst %void %ext DebugScope %bb1 +OpLine %name 1 0 +OpBranch %18 +%20 = OpLabel +; CHECK-NOT: DebugScope [[bb2]] +; CHECK-NOT: OpLine {{%\w+}} 2 0 +%scope2 = OpExtInst %void %ext DebugScope %bb2 +OpLine %name 2 0 +OpBranch %18 +%18 = OpLabel + +; CHECK: DebugScope [[bb3]] +; CHECK: OpLine {{%\w+}} 3 0 +; CHECK: DebugValue [[dbg_foo]] [[value]] +; CHECK: OpLine {{%\w+}} 4 0 +; CHECK: OpStore %gl_FragColor [[value]] +; CHECK: DebugDeclare [[dbg_bar]] %gl_FragColor +; CHECK: DebugValue [[dbg_bar]] [[value]] +%scope3 = OpExtInst %void %ext DebugScope %bb3 +OpLine %name 3 0 +%21 = OpPhi %v4float %12 %19 %14 %20 +%decl0 = OpExtInst %void %ext DebugValue %dbg_foo %21 %null_expr +OpLine %name 4 0 +OpStore %gl_FragColor %21 +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_bar %gl_FragColor %null_expr +%decl2 = OpExtInst %void %ext DebugValue %dbg_bar %21 %null_expr +OpLine %name 5 0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// More complex control flow +// Others? + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dead_insert_elim_test.cpp b/third_party/spirv-tools/test/opt/dead_insert_elim_test.cpp new file mode 100644 index 0000000..9ea948a --- /dev/null +++ b/third_party/spirv-tools/test/opt/dead_insert_elim_test.cpp @@ -0,0 +1,678 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using DeadInsertElimTest = PassTest<::testing::Test>; + +TEST_F(DeadInsertElimTest, InsertAfterInsertElim) { + // With two insertions to the same offset, the first is dead. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in float In0; + // layout (location=1) in float In1; + // layout (location=2) in vec2 In2; + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // vec2 v = In2; + // v.x = In0 + In1; // dead + // v.x = 0.0; + // OutColor = v.xyxy; + // } + + const std::string before_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In2 "In2" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %OutColor "OutColor" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_n" +OpName %_ "" +OpDecorate %In2 Location 2 +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %OutColor Location 0 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%In2 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_float = OpTypePointer Input %float +%In0 = OpVariable %_ptr_Input_float Input +%In1 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%int = OpTypeInt 32 1 +%_Globals_ = OpTypeStruct %uint %int +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +)"; + + const std::string after_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In2 "In2" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %OutColor "OutColor" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_n" +OpName %_ "" +OpDecorate %In2 Location 2 +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %OutColor Location 0 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%In2 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_float = OpTypePointer Input %float +%In0 = OpVariable %_ptr_Input_float Input +%In1 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%int = OpTypeInt 32 1 +%_Globals_ = OpTypeStruct %uint %int +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +)"; + + const std::string before = + R"(%main = OpFunction %void None %11 +%25 = OpLabel +%26 = OpLoad %v2float %In2 +%27 = OpLoad %float %In0 +%28 = OpLoad %float %In1 +%29 = OpFAdd %float %27 %28 +%35 = OpCompositeInsert %v2float %29 %26 0 +%37 = OpCompositeInsert %v2float %float_0 %35 0 +%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1 +OpStore %OutColor %33 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%23 = OpLabel +%24 = OpLoad %v2float %In2 +%29 = OpCompositeInsert %v2float %float_0 %24 0 +%30 = OpVectorShuffle %v4float %29 %29 0 1 0 1 +OpStore %OutColor %30 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_predefs + before, + after_predefs + after, true, true); +} + +TEST_F(DeadInsertElimTest, DeadInsertInChainWithPhi) { + // Dead insert eliminated with phi in insertion chain. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in vec4 In0; + // layout (location=1) in float In1; + // layout (location=2) in float In2; + // layout (location=0) out vec4 OutColor; + // + // layout(std140, binding = 0 ) uniform _Globals_ + // { + // bool g_b; + // }; + // + // void main() + // { + // vec4 v = In0; + // v.z = In1 + In2; + // if (g_b) v.w = 1.0; + // OutColor = vec4(v.x,v.y,0.0,v.w); + // } + + const std::string before_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpName %_ "" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%_Globals_ = OpTypeStruct %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +)"; + + const std::string after_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpName %_ "" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%_Globals_ = OpTypeStruct %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +)"; + + const std::string before = + R"(%main = OpFunction %void None %11 +%31 = OpLabel +%32 = OpLoad %v4float %In0 +%33 = OpLoad %float %In1 +%34 = OpLoad %float %In2 +%35 = OpFAdd %float %33 %34 +%51 = OpCompositeInsert %v4float %35 %32 2 +%37 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%38 = OpLoad %uint %37 +%39 = OpINotEqual %bool %38 %uint_0 +OpSelectionMerge %40 None +OpBranchConditional %39 %41 %40 +%41 = OpLabel +%53 = OpCompositeInsert %v4float %float_1 %51 3 +OpBranch %40 +%40 = OpLabel +%60 = OpPhi %v4float %51 %31 %53 %41 +%55 = OpCompositeExtract %float %60 0 +%57 = OpCompositeExtract %float %60 1 +%59 = OpCompositeExtract %float %60 3 +%49 = OpCompositeConstruct %v4float %55 %57 %float_0 %59 +OpStore %OutColor %49 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%27 = OpLabel +%28 = OpLoad %v4float %In0 +%33 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%34 = OpLoad %uint %33 +%35 = OpINotEqual %bool %34 %uint_0 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %36 +%37 = OpLabel +%38 = OpCompositeInsert %v4float %float_1 %28 3 +OpBranch %36 +%36 = OpLabel +%39 = OpPhi %v4float %28 %27 %38 %37 +%40 = OpCompositeExtract %float %39 0 +%41 = OpCompositeExtract %float %39 1 +%42 = OpCompositeExtract %float %39 3 +%43 = OpCompositeConstruct %v4float %40 %41 %float_0 %42 +OpStore %OutColor %43 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_predefs + before, + after_predefs + after, true, true); +} + +TEST_F(DeadInsertElimTest, DeadInsertTwoPasses) { + // Dead insert which requires two passes to eliminate + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in vec4 In0; + // layout (location=1) in float In1; + // layout (location=2) in float In2; + // layout (location=0) out vec4 OutColor; + // + // layout(std140, binding = 0 ) uniform _Globals_ + // { + // bool g_b; + // bool g_b2; + // }; + // + // void main() + // { + // vec4 v1, v2; + // v1 = In0; + // v1.y = In1 + In2; // dead, second pass + // if (g_b) v1.x = 1.0; + // v2.x = v1.x; + // v2.y = v1.y; // dead, first pass + // if (g_b2) v2.x = 0.0; + // OutColor = vec4(v2.x,v2.x,0.0,1.0); + // } + + const std::string before_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_b2" +OpName %_ "" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_Globals_ = OpTypeStruct %uint %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%27 = OpUndef %v4float +)"; + + const std::string after_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_b2" +OpName %_ "" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_Globals_ = OpTypeStruct %uint %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%27 = OpUndef %v4float +)"; + + const std::string before = + R"(%main = OpFunction %void None %10 +%28 = OpLabel +%29 = OpLoad %v4float %In0 +%30 = OpLoad %float %In1 +%31 = OpLoad %float %In2 +%32 = OpFAdd %float %30 %31 +%33 = OpCompositeInsert %v4float %32 %29 1 +%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%35 = OpLoad %uint %34 +%36 = OpINotEqual %bool %35 %uint_0 +OpSelectionMerge %37 None +OpBranchConditional %36 %38 %37 +%38 = OpLabel +%39 = OpCompositeInsert %v4float %float_1 %33 0 +OpBranch %37 +%37 = OpLabel +%40 = OpPhi %v4float %33 %28 %39 %38 +%41 = OpCompositeExtract %float %40 0 +%42 = OpCompositeInsert %v4float %41 %27 0 +%43 = OpCompositeExtract %float %40 1 +%44 = OpCompositeInsert %v4float %43 %42 1 +%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 +%46 = OpLoad %uint %45 +%47 = OpINotEqual %bool %46 %uint_0 +OpSelectionMerge %48 None +OpBranchConditional %47 %49 %48 +%49 = OpLabel +%50 = OpCompositeInsert %v4float %float_0 %44 0 +OpBranch %48 +%48 = OpLabel +%51 = OpPhi %v4float %44 %37 %50 %49 +%52 = OpCompositeExtract %float %51 0 +%53 = OpCompositeExtract %float %51 0 +%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1 +OpStore %OutColor %54 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%28 = OpLabel +%29 = OpLoad %v4float %In0 +%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%35 = OpLoad %uint %34 +%36 = OpINotEqual %bool %35 %uint_0 +OpSelectionMerge %37 None +OpBranchConditional %36 %38 %37 +%38 = OpLabel +%39 = OpCompositeInsert %v4float %float_1 %29 0 +OpBranch %37 +%37 = OpLabel +%40 = OpPhi %v4float %29 %28 %39 %38 +%41 = OpCompositeExtract %float %40 0 +%42 = OpCompositeInsert %v4float %41 %27 0 +%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 +%46 = OpLoad %uint %45 +%47 = OpINotEqual %bool %46 %uint_0 +OpSelectionMerge %48 None +OpBranchConditional %47 %49 %48 +%49 = OpLabel +%50 = OpCompositeInsert %v4float %float_0 %42 0 +OpBranch %48 +%48 = OpLabel +%51 = OpPhi %v4float %42 %37 %50 %49 +%52 = OpCompositeExtract %float %51 0 +%53 = OpCompositeExtract %float %51 0 +%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1 +OpStore %OutColor %54 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_predefs + before, + after_predefs + after, true, true); +} + +TEST_F(DeadInsertElimTest, DebugInsertAfterInsertElim) { + // With two insertions to the same offset, the first is dead. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in float In0; + // layout (location=1) in float In1; + // layout (location=2) in vec2 In2; + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // vec2 v = In2; + // v.x = In0 + In1; // dead + // v.x = 0.0; + // OutColor = v.xyxy; + // } + + const std::string text = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +OpName %main "main" +OpName %In2 "In2" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %OutColor "OutColor" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_n" +OpName %_ "" +OpDecorate %In2 Location 2 +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %OutColor Location 0 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%In2 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_float = OpTypePointer Input %float +%In0 = OpVariable %_ptr_Input_float Input +%In1 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%int = OpTypeInt 32 1 +%_Globals_ = OpTypeStruct %uint %int +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform + +%nullexpr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v2f = OpExtInst %void %ext DebugTypeVector %dbg_tf 2 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 0 %main +%dbg_foo = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v2f %src 0 0 %dbg_main FlagIsLocal + +%main = OpFunction %void None %11 +%25 = OpLabel +%26 = OpLoad %v2float %In2 +%27 = OpLoad %float %In0 +%28 = OpLoad %float %In1 +%29 = OpFAdd %float %27 %28 + +; CHECK: [[repl:%\w+]] = OpLoad %v2float %In2 +; CHECK-NOT: OpCompositeInsert +; CHECK: DebugValue {{%\w+}} [[repl:%\w+]] +; CHECK-NEXT: OpCompositeInsert %v2float %float_0 [[repl]] 0 +%35 = OpCompositeInsert %v2float %29 %26 0 +%value = OpExtInst %void %ext DebugValue %dbg_foo %35 %nullexpr +%37 = OpCompositeInsert %v2float %float_0 %35 0 + +%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1 +OpStore %OutColor %33 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dead_variable_elim_test.cpp b/third_party/spirv-tools/test/opt/dead_variable_elim_test.cpp new file mode 100644 index 0000000..a55ee62 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dead_variable_elim_test.cpp @@ -0,0 +1,298 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using DeadVariableElimTest = PassTest<::testing::Test>; + +// %dead is unused. Make sure we remove it along with its name. +TEST_F(DeadVariableElimTest, RemoveUnreferenced) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %dead "dead" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%dead = OpVariable %_ptr_Private_float Private +%main = OpFunction %void None %5 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%main = OpFunction %void None %5 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +// Since %dead is exported, make sure we keep it. It could be referenced +// somewhere else. +TEST_F(DeadVariableElimTest, KeepExported) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %dead "dead" +OpDecorate %dead LinkageAttributes "dead" Export +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%dead = OpVariable %_ptr_Private_float Private +%main = OpFunction %void None %5 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, before, true, true); +} + +// Delete %dead because it is unreferenced. Then %initializer becomes +// unreferenced, so remove it as well. +TEST_F(DeadVariableElimTest, RemoveUnreferencedWithInit1) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %dead "dead" +OpName %initializer "initializer" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%initializer = OpVariable %_ptr_Private_float Private +%dead = OpVariable %_ptr_Private_float Private %initializer +%main = OpFunction %void None %6 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%main = OpFunction %void None %6 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +// Delete %dead because it is unreferenced. In this case, the initialized has +// another reference, and should not be removed. +TEST_F(DeadVariableElimTest, RemoveUnreferencedWithInit2) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %dead "dead" +OpName %initializer "initializer" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%initializer = OpVariable %_ptr_Private_float Private +%dead = OpVariable %_ptr_Private_float Private %initializer +%main = OpFunction %void None %6 +%9 = OpLabel +%10 = OpLoad %float %initializer +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %initializer "initializer" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%initializer = OpVariable %_ptr_Private_float Private +%main = OpFunction %void None %6 +%9 = OpLabel +%10 = OpLoad %float %initializer +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +// Keep %live because it is used, and its initializer. +TEST_F(DeadVariableElimTest, KeepReferenced) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %live "live" +OpName %initializer "initializer" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%initializer = OpConstant %float 0 +%live = OpVariable %_ptr_Private_float Private %initializer +%main = OpFunction %void None %6 +%9 = OpLabel +%10 = OpLoad %float %live +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, before, true, true); +} + +// This test that the decoration associated with a variable are removed when the +// variable is removed. +TEST_F(DeadVariableElimTest, RemoveVariableAndDecorations) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpSource GLSL 450 +OpName %main "main" +OpName %B "B" +OpMemberName %B 0 "a" +OpName %Bdat "Bdat" +OpMemberDecorate %B 0 Offset 0 +OpDecorate %B BufferBlock +OpDecorate %Bdat DescriptorSet 0 +OpDecorate %Bdat Binding 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%B = OpTypeStruct %uint +%_ptr_Uniform_B = OpTypePointer Uniform %B +%Bdat = OpVariable %_ptr_Uniform_B Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%main = OpFunction %void None %6 +%13 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpSource GLSL 450 +OpName %main "main" +OpName %B "B" +OpMemberName %B 0 "a" +OpMemberDecorate %B 0 Offset 0 +OpDecorate %B BufferBlock +%void = OpTypeVoid +%6 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%B = OpTypeStruct %uint +%_ptr_Uniform_B = OpTypePointer Uniform %B +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%main = OpFunction %void None %6 +%13 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/debug_info_manager_test.cpp b/third_party/spirv-tools/test/opt/debug_info_manager_test.cpp new file mode 100644 index 0000000..911331a --- /dev/null +++ b/third_party/spirv-tools/test/opt/debug_info_manager_test.cpp @@ -0,0 +1,615 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/debug_info_manager.h" + +#include +#include +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/instruction.h" +#include "spirv-tools/libspirv.hpp" + +// Constants for OpenCL.DebugInfo.100 extension instructions. + +static const uint32_t kDebugFunctionOperandFunctionIndex = 13; +static const uint32_t kDebugInlinedAtOperandLineIndex = 4; +static const uint32_t kDebugInlinedAtOperandScopeIndex = 5; +static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6; + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +TEST(DebugInfoManager, GetDebugInlinedAt) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + %14 = OpString "#line 1 \"ps.hlsl\" +void main(float in_var_color : COLOR) { + float color = in_var_color; +} +" + %17 = OpString "float" + %21 = OpString "main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main + %100 = OpExtInst %void %1 DebugInlinedAt 7 %22 + %main = OpFunction %void None %27 + %28 = OpLabel + %31 = OpLoad %float %in_var_COLOR + OpStore %100 %31 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + DebugInfoManager manager(context.get()); + + EXPECT_EQ(manager.GetDebugInlinedAt(150), nullptr); + EXPECT_EQ(manager.GetDebugInlinedAt(31), nullptr); + EXPECT_EQ(manager.GetDebugInlinedAt(22), nullptr); + + auto* inst = manager.GetDebugInlinedAt(100); + EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7); + EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22); +} + +TEST(DebugInfoManager, CreateDebugInlinedAt) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + %14 = OpString "#line 1 \"ps.hlsl\" +void main(float in_var_color : COLOR) { + float color = in_var_color; +} +" + %17 = OpString "float" + %21 = OpString "main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main + %100 = OpExtInst %void %1 DebugInlinedAt 7 %22 + %main = OpFunction %void None %27 + %28 = OpLabel + %31 = OpLoad %float %in_var_COLOR + OpStore %100 %31 + OpReturn + OpFunctionEnd + )"; + + DebugScope scope(22U, 0U); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + DebugInfoManager manager(context.get()); + + uint32_t inlined_at_id = manager.CreateDebugInlinedAt(nullptr, scope); + auto* inlined_at = manager.GetDebugInlinedAt(inlined_at_id); + EXPECT_NE(inlined_at, nullptr); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), + 1); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), + 22); + EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1); + + const uint32_t line_number = 77U; + Instruction line(context.get(), SpvOpLine); + line.SetInOperands({ + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0U}}, + }); + + inlined_at_id = manager.CreateDebugInlinedAt(&line, scope); + inlined_at = manager.GetDebugInlinedAt(inlined_at_id); + EXPECT_NE(inlined_at, nullptr); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), + line_number); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), + 22); + EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1); + + scope.SetInlinedAt(100U); + inlined_at_id = manager.CreateDebugInlinedAt(&line, scope); + inlined_at = manager.GetDebugInlinedAt(inlined_at_id); + EXPECT_NE(inlined_at, nullptr); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), + line_number); + EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), + 22); + EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandInlinedIndex + 1); + EXPECT_EQ( + inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandInlinedIndex), + 100U); +} + +TEST(DebugInfoManager, GetDebugInfoNone) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + %14 = OpString "#line 1 \"ps.hlsl\" +void main(float in_var_color : COLOR) { + float color = in_var_color; +} +" + %17 = OpString "float" + %21 = OpString "main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main + %12 = OpExtInst %void %1 DebugInfoNone + %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0 + %main = OpFunction %void None %27 + %28 = OpLabel + %100 = OpVariable %_ptr_Function_float Function + %31 = OpLoad %float %in_var_COLOR + OpStore %100 %31 + %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + DebugInfoManager manager(context.get()); + + Instruction* debug_info_none_inst = manager.GetDebugInfoNone(); + EXPECT_NE(debug_info_none_inst, nullptr); + EXPECT_EQ(debug_info_none_inst->GetOpenCL100DebugOpcode(), + OpenCLDebugInfo100DebugInfoNone); + EXPECT_EQ(debug_info_none_inst->PreviousNode(), nullptr); +} + +TEST(DebugInfoManager, CreateDebugInfoNone) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + %14 = OpString "#line 1 \"ps.hlsl\" +void main(float in_var_color : COLOR) { + float color = in_var_color; +} +" + %17 = OpString "float" + %21 = OpString "main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main + %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0 + %main = OpFunction %void None %27 + %28 = OpLabel + %100 = OpVariable %_ptr_Function_float Function + %31 = OpLoad %float %in_var_COLOR + OpStore %100 %31 + %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + DebugInfoManager manager(context.get()); + + Instruction* debug_info_none_inst = manager.GetDebugInfoNone(); + EXPECT_NE(debug_info_none_inst, nullptr); + EXPECT_EQ(debug_info_none_inst->GetOpenCL100DebugOpcode(), + OpenCLDebugInfo100DebugInfoNone); + EXPECT_EQ(debug_info_none_inst->PreviousNode(), nullptr); +} + +TEST(DebugInfoManager, GetDebugFunction) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %200 "200" %in_var_COLOR + OpExecutionMode %200 OriginUpperLeft + %5 = OpString "ps.hlsl" + %14 = OpString "#line 1 \"ps.hlsl\" +void 200(float in_var_color : COLOR) { + float color = in_var_color; +} +" + %17 = OpString "float" + %21 = OpString "200" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %200 "200" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %200 + %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0 + %200 = OpFunction %void None %27 + %28 = OpLabel + %100 = OpVariable %_ptr_Function_float Function + %31 = OpLoad %float %in_var_COLOR + OpStore %100 %31 + %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + DebugInfoManager manager(context.get()); + + EXPECT_EQ(manager.GetDebugFunction(100), nullptr); + EXPECT_EQ(manager.GetDebugFunction(150), nullptr); + + Instruction* dbg_fn = manager.GetDebugFunction(200); + + EXPECT_EQ(dbg_fn->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugFunction); + EXPECT_EQ(dbg_fn->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex), + 200); +} + +TEST(DebugInfoManager, GetDebugFunction_InlinedAway) { + // struct PS_INPUT + // { + // float4 iColor : COLOR; + // }; + // + // struct PS_OUTPUT + // { + // float4 oColor : SV_Target0; + // }; + // + // float4 foo(float4 ic) + // { + // float4 c = ic / 2.0; + // return c; + // } + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // float4 ic = i.iColor; + // ps_output.oColor = foo(ic); + // return ps_output; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %in_var_COLOR %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %15 = OpString "foo2.frag" + %19 = OpString "PS_OUTPUT" + %23 = OpString "float" + %26 = OpString "oColor" + %28 = OpString "PS_INPUT" + %31 = OpString "iColor" + %33 = OpString "foo" + %37 = OpString "c" + %39 = OpString "ic" + %42 = OpString "src.MainPs" + %47 = OpString "ps_output" + %50 = OpString "i" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %52 = OpTypeFunction %void +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %float_0_5 = OpConstant %float 0.5 + %130 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 + %115 = OpExtInst %void %1 DebugInfoNone + %49 = OpExtInst %void %1 DebugExpression + %17 = OpExtInst %void %1 DebugSource %15 + %18 = OpExtInst %void %1 DebugCompilationUnit 1 4 %17 HLSL + %21 = OpExtInst %void %1 DebugTypeComposite %19 Structure %17 6 1 %18 %19 %uint_128 FlagIsProtected|FlagIsPrivate %22 + %24 = OpExtInst %void %1 DebugTypeBasic %23 %uint_32 Float + %25 = OpExtInst %void %1 DebugTypeVector %24 4 + %22 = OpExtInst %void %1 DebugTypeMember %26 %25 %17 8 5 %21 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %29 = OpExtInst %void %1 DebugTypeComposite %28 Structure %17 1 1 %18 %28 %uint_128 FlagIsProtected|FlagIsPrivate %30 + %30 = OpExtInst %void %1 DebugTypeMember %31 %25 %17 3 5 %29 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %32 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %25 %25 + %34 = OpExtInst %void %1 DebugFunction %33 %32 %17 11 1 %18 %33 FlagIsProtected|FlagIsPrivate 12 %115 + %36 = OpExtInst %void %1 DebugLexicalBlock %17 12 1 %34 + %38 = OpExtInst %void %1 DebugLocalVariable %37 %25 %17 13 12 %36 FlagIsLocal + %41 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %21 %29 + %43 = OpExtInst %void %1 DebugFunction %42 %41 %17 17 1 %18 %42 FlagIsProtected|FlagIsPrivate 18 %115 + %45 = OpExtInst %void %1 DebugLexicalBlock %17 18 1 %43 + %46 = OpExtInst %void %1 DebugLocalVariable %39 %25 %17 20 12 %45 FlagIsLocal + %48 = OpExtInst %void %1 DebugLocalVariable %47 %21 %17 19 15 %45 FlagIsLocal + %107 = OpExtInst %void %1 DebugInlinedAt 21 %45 + %MainPs = OpFunction %void None %52 + %53 = OpLabel + %57 = OpLoad %v4float %in_var_COLOR + %131 = OpExtInst %void %1 DebugScope %45 + OpLine %15 20 12 + %117 = OpExtInst %void %1 DebugValue %46 %57 %49 + %132 = OpExtInst %void %1 DebugScope %36 %107 + OpLine %15 13 19 + %112 = OpFMul %v4float %57 %130 + OpLine %15 13 12 + %116 = OpExtInst %void %1 DebugValue %38 %112 %49 + %133 = OpExtInst %void %1 DebugScope %45 + %128 = OpExtInst %void %1 DebugValue %48 %112 %49 %int_0 + %134 = OpExtInst %void %1 DebugNoScope + OpStore %out_var_SV_Target0 %112 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + DebugInfoManager manager(context.get()); + + EXPECT_EQ(manager.GetDebugFunction(115), nullptr); +} + +TEST(DebugInfoManager, CloneDebugInlinedAt) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + %14 = OpString "#line 1 \"ps.hlsl\" +void main(float in_var_color : COLOR) { + float color = in_var_color; +} +" + %17 = OpString "float" + %21 = OpString "main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main + %100 = OpExtInst %void %1 DebugInlinedAt 7 %22 + %main = OpFunction %void None %27 + %28 = OpLabel + %31 = OpLoad %float %in_var_COLOR + OpStore %100 %31 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + DebugInfoManager manager(context.get()); + + EXPECT_EQ(manager.CloneDebugInlinedAt(150), nullptr); + EXPECT_EQ(manager.CloneDebugInlinedAt(22), nullptr); + + auto* inst = manager.CloneDebugInlinedAt(100); + EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7); + EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22); + EXPECT_EQ(inst->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1); + + Instruction* before_100 = nullptr; + for (auto it = context->module()->ext_inst_debuginfo_begin(); + it != context->module()->ext_inst_debuginfo_end(); ++it) { + if (it->result_id() == 100) break; + before_100 = &*it; + } + EXPECT_NE(inst, before_100); + + inst = manager.CloneDebugInlinedAt(100, manager.GetDebugInlinedAt(100)); + EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7); + EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22); + EXPECT_EQ(inst->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1); + + before_100 = nullptr; + for (auto it = context->module()->ext_inst_debuginfo_begin(); + it != context->module()->ext_inst_debuginfo_end(); ++it) { + if (it->result_id() == 100) break; + before_100 = &*it; + } + EXPECT_EQ(inst, before_100); +} + +TEST(DebugInfoManager, KillDebugDeclares) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + %14 = OpString "#line 1 \"ps.hlsl\" +void main(float in_var_color : COLOR) { + float color = in_var_color; +} +" + %17 = OpString "float" + %21 = OpString "main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main + %12 = OpExtInst %void %1 DebugInfoNone + %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0 + %main = OpFunction %void None %27 + %28 = OpLabel + %100 = OpVariable %_ptr_Function_float Function + %31 = OpLoad %float %in_var_COLOR + OpStore %100 %31 + %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13 + %37 = OpExtInst %void %1 DebugDeclare %25 %100 %13 + %38 = OpExtInst %void %1 DebugDeclare %25 %100 %13 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto* dbg_info_mgr = context->get_debug_info_mgr(); + auto* def_use_mgr = context->get_def_use_mgr(); + + EXPECT_TRUE(dbg_info_mgr->IsVariableDebugDeclared(100)); + EXPECT_EQ(def_use_mgr->GetDef(36)->GetOpenCL100DebugOpcode(), + OpenCLDebugInfo100DebugDeclare); + EXPECT_EQ(def_use_mgr->GetDef(37)->GetOpenCL100DebugOpcode(), + OpenCLDebugInfo100DebugDeclare); + EXPECT_EQ(def_use_mgr->GetDef(38)->GetOpenCL100DebugOpcode(), + OpenCLDebugInfo100DebugDeclare); + + dbg_info_mgr->KillDebugDeclares(100); + EXPECT_EQ(def_use_mgr->GetDef(36), nullptr); + EXPECT_EQ(def_use_mgr->GetDef(37), nullptr); + EXPECT_EQ(def_use_mgr->GetDef(38), nullptr); + EXPECT_FALSE(dbg_info_mgr->IsVariableDebugDeclared(100)); +} + +} // namespace +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/decompose_initialized_variables_test.cpp b/third_party/spirv-tools/test/opt/decompose_initialized_variables_test.cpp new file mode 100644 index 0000000..06ba59a --- /dev/null +++ b/third_party/spirv-tools/test/opt/decompose_initialized_variables_test.cpp @@ -0,0 +1,252 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using DecomposeInitializedVariablesTest = PassTest<::testing::Test>; + +std::string single_entry_header = R"(OpCapability Shader +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical Vulkan +OpEntryPoint Vertex %1 "shader" +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%4 = OpConstantNull %uint +%void = OpTypeVoid +%6 = OpTypeFunction %void +)"; + +std::string GetFunctionTest(std::string body) { + auto result = single_entry_header; + result += "%_ptr_Function_uint = OpTypePointer Function %uint\n"; + result += "%1 = OpFunction %void None %6\n"; + result += "%8 = OpLabel\n"; + result += body + "\n"; + result += "OpReturn\n"; + result += "OpFunctionEnd\n"; + return result; +} + +TEST_F(DecomposeInitializedVariablesTest, FunctionChanged) { + std::string input = "%9 = OpVariable %_ptr_Function_uint Function %uint_1"; + std::string expected = R"(%9 = OpVariable %_ptr_Function_uint Function +OpStore %9 %uint_1)"; + + SinglePassRunAndCheck( + GetFunctionTest(input), GetFunctionTest(expected), + /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, FunctionUnchanged) { + std::string input = "%9 = OpVariable %_ptr_Function_uint Function"; + + SinglePassRunAndCheck( + GetFunctionTest(input), GetFunctionTest(input), /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, FunctionMultipleVariables) { + std::string input = R"(%9 = OpVariable %_ptr_Function_uint Function %uint_1 +%10 = OpVariable %_ptr_Function_uint Function %4)"; + std::string expected = R"(%9 = OpVariable %_ptr_Function_uint Function +%10 = OpVariable %_ptr_Function_uint Function +OpStore %9 %uint_1 +OpStore %10 %4)"; + + SinglePassRunAndCheck( + GetFunctionTest(input), GetFunctionTest(expected), + /* skip_nop = */ false); +} + +std::string GetGlobalTest(std::string storage_class, bool initialized, + bool decomposed) { + auto result = single_entry_header; + + result += "%_ptr_" + storage_class + "_uint = OpTypePointer " + + storage_class + " %uint\n"; + if (initialized) { + result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + + storage_class + " %4\n"; + } else { + result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + + storage_class + "\n"; + } + result += R"(%1 = OpFunction %void None %9 +%9 = OpLabel +)"; + if (decomposed) result += "OpStore %8 %4\n"; + result += R"(OpReturn +OpFunctionEnd +)"; + return result; +} + +TEST_F(DecomposeInitializedVariablesTest, PrivateChanged) { + std::string input = GetGlobalTest("Private", true, false); + std::string expected = GetGlobalTest("Private", false, true); + SinglePassRunAndCheck( + input, expected, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, PrivateUnchanged) { + std::string input = GetGlobalTest("Private", false, false); + SinglePassRunAndCheck( + input, input, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, OutputChanged) { + std::string input = GetGlobalTest("Output", true, false); + std::string expected = GetGlobalTest("Output", false, true); + SinglePassRunAndCheck( + input, expected, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, OutputUnchanged) { + std::string input = GetGlobalTest("Output", false, false); + SinglePassRunAndCheck( + input, input, /* skip_nop = */ false); +} + +std::string multiple_entry_header = R"(OpCapability Shader +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical Vulkan +OpEntryPoint Vertex %1 "vertex" +OpEntryPoint Fragment %2 "fragment" +%uint = OpTypeInt 32 0 +%4 = OpConstantNull %uint +%void = OpTypeVoid +%6 = OpTypeFunction %void +)"; + +std::string GetGlobalMultipleEntryTest(std::string storage_class, + bool initialized, bool decomposed) { + auto result = multiple_entry_header; + result += "%_ptr_" + storage_class + "_uint = OpTypePointer " + + storage_class + " %uint\n"; + if (initialized) { + result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + + storage_class + " %4\n"; + } else { + result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + + storage_class + "\n"; + } + result += R"(%1 = OpFunction %void None %9 +%9 = OpLabel +)"; + if (decomposed) result += "OpStore %8 %4\n"; + result += R"(OpReturn +OpFunctionEnd +%2 = OpFunction %void None %10 +%10 = OpLabel +)"; + if (decomposed) result += "OpStore %8 %4\n"; + result += R"(OpReturn +OpFunctionEnd +)"; + + return result; +} + +TEST_F(DecomposeInitializedVariablesTest, PrivateMultipleEntryChanged) { + std::string input = GetGlobalMultipleEntryTest("Private", true, false); + std::string expected = GetGlobalMultipleEntryTest("Private", false, true); + SinglePassRunAndCheck( + input, expected, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, PrivateMultipleEntryUnchanged) { + std::string input = GetGlobalMultipleEntryTest("Private", false, false); + SinglePassRunAndCheck( + input, input, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, OutputMultipleEntryChanged) { + std::string input = GetGlobalMultipleEntryTest("Output", true, false); + std::string expected = GetGlobalMultipleEntryTest("Output", false, true); + SinglePassRunAndCheck( + input, expected, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, OutputMultipleEntryUnchanged) { + std::string input = GetGlobalMultipleEntryTest("Output", false, false); + SinglePassRunAndCheck( + input, input, /* skip_nop = */ false); +} + +std::string GetGlobalWithNonEntryPointTest(std::string storage_class, + bool initialized, bool decomposed) { + auto result = single_entry_header; + result += "%_ptr_" + storage_class + "_uint = OpTypePointer " + + storage_class + " %uint\n"; + if (initialized) { + result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + + storage_class + " %4\n"; + } else { + result += "%8 = OpVariable %_ptr_" + storage_class + "_uint " + + storage_class + "\n"; + } + result += R"(%1 = OpFunction %void None %9 +%9 = OpLabel +)"; + if (decomposed) result += "OpStore %8 %4\n"; + result += R"(OpReturn +OpFunctionEnd +%10 = OpFunction %void None %11 +%11 = OpLabel +OpReturn +OpFunctionEnd +)"; + + return result; +} + +TEST_F(DecomposeInitializedVariablesTest, PrivateWithNonEntryPointChanged) { + std::string input = GetGlobalWithNonEntryPointTest("Private", true, false); + std::string expected = GetGlobalWithNonEntryPointTest("Private", false, true); + SinglePassRunAndCheck( + input, expected, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, PrivateWithNonEntryPointUnchanged) { + std::string input = GetGlobalWithNonEntryPointTest("Private", false, false); + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + input, input, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, OutputWithNonEntryPointChanged) { + std::string input = GetGlobalWithNonEntryPointTest("Output", true, false); + std::string expected = GetGlobalWithNonEntryPointTest("Output", false, true); + SinglePassRunAndCheck( + input, expected, /* skip_nop = */ false); +} + +TEST_F(DecomposeInitializedVariablesTest, OutputWithNonEntryPointUnchanged) { + std::string input = GetGlobalWithNonEntryPointTest("Output", false, false); + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + input, input, /* skip_nop = */ false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/decoration_manager_test.cpp b/third_party/spirv-tools/test/opt/decoration_manager_test.cpp new file mode 100644 index 0000000..fcfbff0 --- /dev/null +++ b/third_party/spirv-tools/test/opt/decoration_manager_test.cpp @@ -0,0 +1,1510 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/build_module.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/ir_context.h" +#include "source/spirv_constant.h" +#include "source/util/string_utils.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +using utils::MakeVector; + +class DecorationManagerTest : public ::testing::Test { + public: + DecorationManagerTest() + : tools_(SPV_ENV_UNIVERSAL_1_2), + context_(), + consumer_([this](spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + if (!error_message_.empty()) error_message_ += "\n"; + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + error_message_ += "ERROR"; + break; + case SPV_MSG_WARNING: + error_message_ += "WARNING"; + break; + case SPV_MSG_INFO: + error_message_ += "INFO"; + break; + case SPV_MSG_DEBUG: + error_message_ += "DEBUG"; + break; + } + error_message_ += + ": " + std::to_string(position.index) + ": " + message; + }), + disassemble_options_(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER), + error_message_() { + tools_.SetMessageConsumer(consumer_); + } + + void TearDown() override { error_message_.clear(); } + + DecorationManager* GetDecorationManager(const std::string& text) { + context_ = BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text); + if (context_.get()) + return context_->get_decoration_mgr(); + else + return nullptr; + } + + // Disassembles |binary| and outputs the result in |text|. If |text| is a + // null pointer, SPV_ERROR_INVALID_POINTER is returned. + spv_result_t Disassemble(const std::vector& binary, + std::string* text) { + if (!text) return SPV_ERROR_INVALID_POINTER; + return tools_.Disassemble(binary, text, disassemble_options_) + ? SPV_SUCCESS + : SPV_ERROR_INVALID_BINARY; + } + + // Returns the accumulated error messages for the test. + std::string GetErrorMessage() const { return error_message_; } + + std::string ToText(const std::vector& inst) { + std::vector binary = {SpvMagicNumber, 0x10200, 0u, 2u, 0u}; + for (const Instruction* i : inst) + i->ToBinaryWithoutAttachedDebugInsts(&binary); + std::string text; + Disassemble(binary, &text); + return text; + } + + std::string ModuleToText() { + std::vector binary; + context_->module()->ToBinary(&binary, false); + std::string text; + Disassemble(binary, &text); + return text; + } + + spvtools::MessageConsumer GetConsumer() { return consumer_; } + + private: + // An instance for calling SPIRV-Tools functionalities. + spvtools::SpirvTools tools_; + std::unique_ptr context_; + spvtools::MessageConsumer consumer_; + uint32_t disassemble_options_; + std::string error_message_; +}; + +TEST_F(DecorationManagerTest, + ComparingDecorationsWithDiffOpcodesDecorateDecorateId) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // This parameter can be interprated both as { SpvDecorationConstant } + // and also as a list of IDs: { 22 } + const std::vector param{SpvDecorationConstant}; + // OpDecorate %1 Constant + Instruction inst1( + &ir_context, SpvOpDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_DECORATION, param}}); + // OpDecorateId %1 %22 ; 'Constant' is decoration number 22 + Instruction inst2( + &ir_context, SpvOpDecorateId, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_ID, param}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, + ComparingDecorationsWithDiffOpcodesDecorateDecorateString) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // This parameter can be interprated both as { SpvDecorationConstant } + // and also as a null-terminated string with a single character with value 22. + const std::vector param{SpvDecorationConstant}; + // OpDecorate %1 Constant + Instruction inst1( + &ir_context, SpvOpDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_DECORATION, param}}); + // OpDecorateStringGOOGLE %1 !22 + Instruction inst2( + &ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_LITERAL_STRING, param}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffDecorateParam) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // OpDecorate %1 Constant + Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + // OpDecorate %1 Restrict + Instruction inst2(&ir_context, SpvOpDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationRestrict}}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffDecorateIdParam) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // OpDecorate %1 Constant + Instruction inst1( + &ir_context, SpvOpDecorateId, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_ID, {555}}}); + // OpDecorate %1 Restrict + Instruction inst2( + &ir_context, SpvOpDecorateId, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_ID, {666}}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, ComparingDecorationsWithDiffDecorateStringParam) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // OpDecorate %1 Constant + Instruction inst1(&ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, MakeVector("Hello!")}}); + // OpDecorate %1 Restrict + Instruction inst2(&ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, MakeVector("Hellx")}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, ComparingSameDecorationsOnDiffTargetAllowed) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // OpDecorate %1 Constant + Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + // OpDecorate %2 Constant + Instruction inst2(&ir_context, SpvOpDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {2u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, ComparingSameDecorationIdsOnDiffTargetAllowed) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + Instruction inst1( + &ir_context, SpvOpDecorateId, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, {SPV_OPERAND_TYPE_DECORATION, {44}}}); + Instruction inst2( + &ir_context, SpvOpDecorateId, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {2u}}, {SPV_OPERAND_TYPE_DECORATION, {44}}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, + ComparingSameDecorationStringsOnDiffTargetAllowed) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + Instruction inst1(&ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, MakeVector("hello")}}); + Instruction inst2(&ir_context, SpvOpDecorateStringGOOGLE, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {2u}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, MakeVector("hello")}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, ComparingSameDecorationsOnDiffTargetDisallowed) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // OpDecorate %1 Constant + Instruction inst1(&ir_context, SpvOpDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + // OpDecorate %2 Constant + Instruction inst2(&ir_context, SpvOpDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {2u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, false)); +} + +TEST_F(DecorationManagerTest, ComparingMemberDecorationsOnSameTypeDiffMember) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // OpMemberDecorate %1 0 Constant + Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + // OpMemberDecorate %1 1 Constant + Instruction inst2(&ir_context, SpvOpMemberDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, + ComparingSameMemberDecorationsOnDiffTargetAllowed) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // OpMemberDecorate %1 0 Constant + Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + // OpMemberDecorate %2 0 Constant + Instruction inst2(&ir_context, SpvOpMemberDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {2u}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->AreDecorationsTheSame(&inst1, &inst2, true)); +} + +TEST_F(DecorationManagerTest, + ComparingSameMemberDecorationsOnDiffTargetDisallowed) { + IRContext ir_context(SPV_ENV_UNIVERSAL_1_2, GetConsumer()); + // OpMemberDecorate %1 0 Constant + Instruction inst1(&ir_context, SpvOpMemberDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {1u}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + // OpMemberDecorate %2 0 Constant + Instruction inst2(&ir_context, SpvOpMemberDecorate, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {2u}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0u}}, + {SPV_OPERAND_TYPE_DECORATION, {SpvDecorationConstant}}}); + DecorationManager* decoManager = ir_context.get_decoration_mgr(); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->AreDecorationsTheSame(&inst1, &inst2, false)); +} + +TEST_F(DecorationManagerTest, RemoveDecorationFromVariable) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 %3 +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Uniform +%3 = OpVariable %4 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + decoManager->RemoveDecorationsFrom(1u); + auto decorations = decoManager->GetDecorationsFor(1u, false); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decorations.empty()); + decorations = decoManager->GetDecorationsFor(3u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + const std::string expected_decorations = R"(OpDecorate %2 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %3 +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Uniform +%3 = OpVariable %4 Uniform +)"; + EXPECT_THAT(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, RemoveDecorationStringFromVariable) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "hello world" +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 %3 +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Uniform +%3 = OpVariable %4 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + decoManager->RemoveDecorationsFrom(1u); + auto decorations = decoManager->GetDecorationsFor(1u, false); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decorations.empty()); + decorations = decoManager->GetDecorationsFor(3u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + const std::string expected_decorations = R"(OpDecorate %2 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %3 +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Uniform +%3 = OpVariable %4 Uniform +)"; + EXPECT_THAT(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, RemoveDecorationFromDecorationGroup) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 %3 +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Uniform +%3 = OpVariable %4 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + decoManager->RemoveDecorationsFrom(2u); + auto decorations = decoManager->GetDecorationsFor(2u, false); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decorations.empty()); + decorations = decoManager->GetDecorationsFor(1u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + const std::string expected_decorations = R"(OpDecorate %1 Constant +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + decorations = decoManager->GetDecorationsFor(3u, false); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_THAT(ToText(decorations), ""); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +%2 = OpDecorationGroup +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Uniform +%3 = OpVariable %4 Uniform +)"; + EXPECT_THAT(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, + RemoveDecorationFromDecorationGroupKeepDeadDecorations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 +%3 = OpTypeInt 32 0 +%1 = OpVariable %3 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + decoManager->RemoveDecorationsFrom(1u); + auto decorations = decoManager->GetDecorationsFor(1u, false); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decorations.empty()); + decorations = decoManager->GetDecorationsFor(2u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + const std::string expected_decorations = R"(OpDecorate %2 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %2 Restrict +%2 = OpDecorationGroup +%3 = OpTypeInt 32 0 +%1 = OpVariable %3 Uniform +)"; + EXPECT_THAT(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, RemoveAllDecorationsAppliedByGroup) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 +OpDecorate %3 BuiltIn VertexId +%3 = OpDecorationGroup +OpGroupDecorate %3 %1 +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Input +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + decoManager->RemoveDecorationsFrom(1u, [](const Instruction& inst) { + return inst.opcode() == SpvOpDecorate && + inst.GetSingleWordInOperand(0u) == 3u; + }); + auto decorations = decoManager->GetDecorationsFor(1u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + std::string expected_decorations = R"(OpDecorate %1 Constant +OpDecorate %2 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + decorations = decoManager->GetDecorationsFor(2u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + expected_decorations = R"(OpDecorate %2 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 +OpDecorate %3 BuiltIn VertexId +%3 = OpDecorationGroup +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Input +)"; + EXPECT_THAT(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, RemoveSomeDecorationsAppliedByGroup) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 +OpDecorate %3 BuiltIn VertexId +OpDecorate %3 Invariant +%3 = OpDecorationGroup +OpGroupDecorate %3 %1 +%uint = OpTypeInt 32 0 +%1 = OpVariable %uint Input +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + decoManager->RemoveDecorationsFrom(1u, [](const Instruction& inst) { + return inst.opcode() == SpvOpDecorate && + inst.GetSingleWordInOperand(0u) == 3u && + inst.GetSingleWordInOperand(1u) == SpvDecorationBuiltIn; + }); + auto decorations = decoManager->GetDecorationsFor(1u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + std::string expected_decorations = R"(OpDecorate %1 Constant +OpDecorate %1 Invariant +OpDecorate %2 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + decorations = decoManager->GetDecorationsFor(2u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + expected_decorations = R"(OpDecorate %2 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 +OpDecorate %3 BuiltIn VertexId +OpDecorate %3 Invariant +%3 = OpDecorationGroup +OpDecorate %1 Invariant +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Input +)"; + EXPECT_THAT(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, RemoveDecorationDecorate) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %1 Restrict +%2 = OpTypeInt 32 0 +%1 = OpVariable %2 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + auto decorations = decoManager->GetDecorationsFor(1u, false); + decoManager->RemoveDecoration(decorations.front()); + decorations = decoManager->GetDecorationsFor(1u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + const std::string expected_decorations = R"(OpDecorate %1 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); +} + +TEST_F(DecorationManagerTest, RemoveDecorationStringDecorate) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "foobar" +OpDecorate %1 Restrict +%2 = OpTypeInt 32 0 +%1 = OpVariable %2 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + auto decorations = decoManager->GetDecorationsFor(1u, false); + decoManager->RemoveDecoration(decorations.front()); + decorations = decoManager->GetDecorationsFor(1u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + const std::string expected_decorations = R"(OpDecorate %1 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); +} + +TEST_F(DecorationManagerTest, CloneDecorations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 +OpDecorate %3 BuiltIn VertexId +OpDecorate %3 Invariant +%3 = OpDecorationGroup +OpGroupDecorate %3 %1 +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Input +%5 = OpVariable %4 Input +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + + // Check cloning OpDecorate including group decorations. + auto decorations = decoManager->GetDecorationsFor(5u, false); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decorations.empty()); + + decoManager->CloneDecorations(1u, 5u); + decorations = decoManager->GetDecorationsFor(5u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + std::string expected_decorations = R"(OpDecorate %5 Constant +OpDecorate %2 Restrict +OpDecorate %3 BuiltIn VertexId +OpDecorate %3 Invariant +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + + // Check that bookkeeping for ID 2 remains the same. + decorations = decoManager->GetDecorationsFor(2u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + expected_decorations = R"(OpDecorate %2 Restrict +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %2 %1 %5 +OpDecorate %3 BuiltIn VertexId +OpDecorate %3 Invariant +%3 = OpDecorationGroup +OpGroupDecorate %3 %1 %5 +OpDecorate %5 Constant +%4 = OpTypeInt 32 0 +%1 = OpVariable %4 Input +%5 = OpVariable %4 Input +)"; + EXPECT_THAT(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, CloneDecorationsStringAndId) { + const std::string spirv = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "blah" +OpDecorateId %1 HlslCounterBufferGOOGLE %2 +OpDecorate %1 Aliased +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Uniform %3 +%1 = OpVariable %4 Uniform +%2 = OpVariable %4 Uniform +%5 = OpVariable %4 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + + // Check cloning OpDecorate including group decorations. + auto decorations = decoManager->GetDecorationsFor(5u, false); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decorations.empty()); + + decoManager->CloneDecorations(1u, 5u); + decorations = decoManager->GetDecorationsFor(5u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + std::string expected_decorations = + R"(OpDecorateString %5 UserSemantic "blah" +OpDecorateId %5 CounterBuffer %2 +OpDecorate %5 Aliased +)"; + EXPECT_THAT(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateString %1 UserSemantic "blah" +OpDecorateId %1 CounterBuffer %2 +OpDecorate %1 Aliased +OpDecorateString %5 UserSemantic "blah" +OpDecorateId %5 CounterBuffer %2 +OpDecorate %5 Aliased +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Uniform %3 +%1 = OpVariable %4 Uniform +%2 = OpVariable %4 Uniform +%5 = OpVariable %4 Uniform +)"; + EXPECT_THAT(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, CloneSomeDecorations) { + const std::string spirv = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorate %1 RelaxedPrecision +OpDecorate %1 Restrict +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Function %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +%1 = OpVariable %3 Function +%8 = OpUndef %2 +OpReturn +OpFunctionEnd +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_EQ(GetErrorMessage(), ""); + + // Check cloning OpDecorate including group decorations. + auto decorations = decoManager->GetDecorationsFor(8u, false); + EXPECT_EQ(GetErrorMessage(), ""); + EXPECT_TRUE(decorations.empty()); + + decoManager->CloneDecorations(1u, 8u, {SpvDecorationRelaxedPrecision}); + decorations = decoManager->GetDecorationsFor(8u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + std::string expected_decorations = + R"(OpDecorate %8 RelaxedPrecision +)"; + EXPECT_EQ(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorate %1 RelaxedPrecision +OpDecorate %1 Restrict +OpDecorate %8 RelaxedPrecision +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Function %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +%1 = OpVariable %3 Function +%8 = OpUndef %2 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(ModuleToText(), expected_binary); +} + +// Test cloning decoration for an id that is decorated via a group decoration. +TEST_F(DecorationManagerTest, CloneSomeGroupDecorations) { + const std::string spirv = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 RelaxedPrecision +OpDecorate %1 Restrict +%1 = OpDecorationGroup +OpGroupDecorate %1 %2 +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Function %3 +%5 = OpTypeVoid +%6 = OpTypeFunction %5 +%7 = OpFunction %5 None %6 +%8 = OpLabel +%2 = OpVariable %4 Function +%9 = OpUndef %3 +OpReturn +OpFunctionEnd +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_EQ(GetErrorMessage(), ""); + + // Check cloning OpDecorate including group decorations. + auto decorations = decoManager->GetDecorationsFor(9u, false); + EXPECT_EQ(GetErrorMessage(), ""); + EXPECT_TRUE(decorations.empty()); + + decoManager->CloneDecorations(2u, 9u, {SpvDecorationRelaxedPrecision}); + decorations = decoManager->GetDecorationsFor(9u, false); + EXPECT_THAT(GetErrorMessage(), ""); + + std::string expected_decorations = + R"(OpDecorate %9 RelaxedPrecision +)"; + EXPECT_EQ(ToText(decorations), expected_decorations); + + const std::string expected_binary = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 RelaxedPrecision +OpDecorate %1 Restrict +%1 = OpDecorationGroup +OpGroupDecorate %1 %2 +OpDecorate %9 RelaxedPrecision +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Function %3 +%5 = OpTypeVoid +%6 = OpTypeFunction %5 +%7 = OpFunction %5 None %6 +%8 = OpLabel +%2 = OpVariable %4 Function +%9 = OpUndef %3 +OpReturn +OpFunctionEnd +)"; + EXPECT_EQ(ModuleToText(), expected_binary); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsWithoutGroupsTrue) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %2 Restrict +OpDecorate %1 Constant +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsWithoutGroupsFalse) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %2 Restrict +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsIdWithoutGroupsTrue) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorateId %1 AlignmentId %nine +OpDecorateId %3 MaxByteOffsetId %nine +OpDecorateId %3 AlignmentId %nine +OpDecorateId %1 MaxByteOffsetId %nine +%u32 = OpTypeInt 32 0 +%nine = OpConstant %u32 9 +%1 = OpVariable %u32 Uniform +%3 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveTheSameDecorations(1u, 3u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsIdWithoutGroupsFalse) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorateId %1 AlignmentId %nine +OpDecorateId %2 MaxByteOffsetId %nine +OpDecorateId %2 AlignmentId %nine +%u32 = OpTypeInt 32 0 +%nine = OpConstant %u32 9 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsStringWithoutGroupsTrue) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "world" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "world" +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsStringWithoutGroupsFalse) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "world" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "hello" +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsWithGroupsTrue) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %1 Constant +OpDecorate %3 Restrict +%3 = OpDecorationGroup +OpGroupDecorate %3 %2 +OpDecorate %4 Invariant +%4 = OpDecorationGroup +OpGroupDecorate %4 %1 %2 +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsWithGroupsFalse) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %1 Constant +OpDecorate %4 Invariant +%4 = OpDecorationGroup +OpGroupDecorate %4 %1 %2 +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsDuplicateDecorations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Constant +OpDecorate %2 Constant +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsDifferentVariations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Location 0 +OpDecorate %2 Location 1 +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, + HaveTheSameDecorationsDuplicateMemberDecorations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %1 0 Location 0 +OpMemberDecorate %2 0 Location 0 +OpMemberDecorate %2 0 Location 0 +%u32 = OpTypeInt 32 0 +%1 = OpTypeStruct %u32 %u32 +%2 = OpTypeStruct %u32 %u32 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, + HaveTheSameDecorationsDifferentMemberSameDecoration) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %1 0 Location 0 +OpMemberDecorate %2 1 Location 0 +%u32 = OpTypeInt 32 0 +%1 = OpTypeStruct %u32 %u32 +%2 = OpTypeStruct %u32 %u32 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsDifferentMemberVariations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %1 0 Location 0 +OpMemberDecorate %2 0 Location 1 +%u32 = OpTypeInt 32 0 +%1 = OpTypeStruct %u32 %u32 +%2 = OpTypeStruct %u32 %u32 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsDuplicateIdDecorations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorateId %1 AlignmentId %2 +OpDecorateId %3 AlignmentId %2 +OpDecorateId %3 AlignmentId %2 +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%3 = OpVariable %u32 Uniform +%2 = OpSpecConstant %u32 0 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveTheSameDecorations(1u, 3u)); +} + +TEST_F(DecorationManagerTest, + HaveTheSameDecorationsDuplicateStringDecorations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "hello" +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsDifferentIdVariations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorateId %1 AlignmentId %2 +OpDecorateId %3 AlignmentId %4 +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%3 = OpVariable %u32 Uniform +%2 = OpSpecConstant %u32 0 +%4 = OpSpecConstant %u32 0 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsDifferentStringVariations) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "world" +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsLeftSymmetry) { + // Left being a subset of right is not enough. + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %1 Constant +OpDecorate %2 Constant +OpDecorate %2 Restrict +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationsRightSymmetry) { + // Right being a subset of left is not enough. + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %2 Constant +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationIdsLeftSymmetry) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorateId %1 AlignmentId %nine +OpDecorateId %1 AlignmentId %nine +OpDecorateId %2 AlignmentId %nine +OpDecorateId %2 MaxByteOffsetId %nine +%u32 = OpTypeInt 32 0 +%nine = OpConstant %u32 9 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationIdsRightSymmetry) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorateId %1 AlignmentId %nine +OpDecorateId %1 MaxByteOffsetId %nine +OpDecorateId %2 AlignmentId %nine +OpDecorateId %2 AlignmentId %nine +%u32 = OpTypeInt 32 0 +%nine = OpConstant %u32 9 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationStringsLeftSymmetry) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "world" +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, HaveTheSameDecorationStringsRightSymmetry) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE "world" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "hello" +OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "hello" +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveTheSameDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorate1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %2 Restrict +OpDecorate %1 Constant +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorate2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %2 Restrict +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorate3) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Constant +OpDecorate %2 Restrict +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorate4) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %2 Restrict +OpDecorate %1 Constant +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(2u, 1u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorate5) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Restrict +OpDecorate %2 Constant +OpDecorate %2 Restrict +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(2u, 1u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorate6) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Constant +OpDecorate %2 Restrict +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(2u, 1u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorate7) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %2 Constant +OpDecorate %2 Restrict +OpDecorate %1 Invariant +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%2 = OpVariable %u32 Uniform +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(2u, 1u)); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(1u, 2u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpMemberDecorate1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %1 0 Offset 4 +OpMemberDecorate %2 0 Offset 0 +OpMemberDecorate %2 0 Offset 4 +%u32 = OpTypeInt 32 0 +%1 = OpTypeStruct %u32 %u32 %u32 +%2 = OpTypeStruct %u32 %u32 %u32 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(1u, 2u)); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(2u, 1u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpMemberDecorate2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %2 0 Offset 0 +OpMemberDecorate %2 0 Offset 4 +%u32 = OpTypeInt 32 0 +%1 = OpTypeStruct %u32 %u32 %u32 +%2 = OpTypeStruct %u32 %u32 %u32 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(1u, 2u)); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(2u, 1u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorateId1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorateId %1 AlignmentId %2 +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%3 = OpVariable %u32 Uniform +%2 = OpSpecConstant %u32 0 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(1u, 3u)); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(3u, 1u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorateId2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorateId %1 AlignmentId %2 +OpDecorateId %3 AlignmentId %4 +%u32 = OpTypeInt 32 0 +%1 = OpVariable %u32 Uniform +%3 = OpVariable %u32 Uniform +%2 = OpSpecConstant %u32 0 +%4 = OpSpecConstant %u32 1 +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(1u, 3u)); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(3u, 1u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorateString1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateString %1 HlslSemanticGOOGLE "hello" +OpDecorateString %2 HlslSemanticGOOGLE "world" +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(1u, 2u)); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(2u, 1u)); +} + +TEST_F(DecorationManagerTest, SubSetTestOpDecorateString2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpExtension "SPV_GOOGLE_decorate_string" +OpMemoryModel Logical GLSL450 +OpDecorateString %1 HlslSemanticGOOGLE "hello" +)"; + DecorationManager* decoManager = GetDecorationManager(spirv); + EXPECT_THAT(GetErrorMessage(), ""); + EXPECT_FALSE(decoManager->HaveSubsetOfDecorations(1u, 2u)); + EXPECT_TRUE(decoManager->HaveSubsetOfDecorations(2u, 1u)); +} +} // namespace +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/def_use_test.cpp b/third_party/spirv-tools/test/opt/def_use_test.cpp new file mode 100644 index 0000000..cfdad74 --- /dev/null +++ b/third_party/spirv-tools/test/opt/def_use_test.cpp @@ -0,0 +1,1719 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +using ::testing::Contains; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; + +// Returns the number of uses of |id|. +uint32_t NumUses(const std::unique_ptr& context, uint32_t id) { + uint32_t count = 0; + context->get_def_use_mgr()->ForEachUse( + id, [&count](Instruction*, uint32_t) { ++count; }); + return count; +} + +// Returns the opcode of each use of |id|. +// +// If |id| is used multiple times in a single instruction, that instruction's +// opcode will appear a corresponding number of times. +std::vector GetUseOpcodes(const std::unique_ptr& context, + uint32_t id) { + std::vector opcodes; + context->get_def_use_mgr()->ForEachUse( + id, [&opcodes](Instruction* user, uint32_t) { + opcodes.push_back(user->opcode()); + }); + return opcodes; +} + +// Disassembles the given |inst| and returns the disassembly. +std::string DisassembleInst(Instruction* inst) { + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + + std::vector binary; + // We need this to generate the necessary header in the binary. + tools.Assemble("", &binary); + inst->ToBinaryWithoutAttachedDebugInsts(&binary); + + std::string text; + // We'll need to check the underlying id numbers. + // So turn off friendly names for ids. + tools.Disassemble(binary, &text, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + while (!text.empty() && text.back() == '\n') text.pop_back(); + return text; +} + +// A struct for holding expected id defs and uses. +struct InstDefUse { + using IdInstPair = std::pair; + using IdInstsPair = std::pair>; + + // Ids and their corresponding def instructions. + std::vector defs; + // Ids and their corresponding use instructions. + std::vector uses; +}; + +// Checks that the |actual_defs| and |actual_uses| are in accord with +// |expected_defs_uses|. +void CheckDef(const InstDefUse& expected_defs_uses, + const DefUseManager::IdToDefMap& actual_defs) { + // Check defs. + ASSERT_EQ(expected_defs_uses.defs.size(), actual_defs.size()); + for (uint32_t i = 0; i < expected_defs_uses.defs.size(); ++i) { + const auto id = expected_defs_uses.defs[i].first; + const auto expected_def = expected_defs_uses.defs[i].second; + ASSERT_EQ(1u, actual_defs.count(id)) << "expected to def id [" << id << "]"; + auto def = actual_defs.at(id); + if (def->opcode() != SpvOpConstant) { + // Constants don't disassemble properly without a full context. + EXPECT_EQ(expected_def, DisassembleInst(actual_defs.at(id))); + } + } +} + +using UserMap = std::unordered_map>; + +// Creates a mapping of all definitions to their users (except OpConstant). +// +// OpConstants are skipped because they cannot be disassembled in isolation. +UserMap BuildAllUsers(const DefUseManager* mgr, uint32_t idBound) { + UserMap userMap; + for (uint32_t id = 0; id != idBound; ++id) { + if (mgr->GetDef(id)) { + mgr->ForEachUser(id, [id, &userMap](Instruction* user) { + if (user->opcode() != SpvOpConstant) { + userMap[id].push_back(user); + } + }); + } + } + return userMap; +} + +// Constants don't disassemble properly without a full context, so skip them as +// checks. +void CheckUse(const InstDefUse& expected_defs_uses, const DefUseManager* mgr, + uint32_t idBound) { + UserMap actual_uses = BuildAllUsers(mgr, idBound); + // Check uses. + ASSERT_EQ(expected_defs_uses.uses.size(), actual_uses.size()); + for (uint32_t i = 0; i < expected_defs_uses.uses.size(); ++i) { + const auto id = expected_defs_uses.uses[i].first; + const auto& expected_uses = expected_defs_uses.uses[i].second; + + ASSERT_EQ(1u, actual_uses.count(id)) << "expected to use id [" << id << "]"; + const auto& uses = actual_uses.at(id); + + ASSERT_EQ(expected_uses.size(), uses.size()) + << "id [" << id << "] # uses: expected: " << expected_uses.size() + << " actual: " << uses.size(); + + std::vector actual_uses_disassembled; + for (const auto actual_use : uses) { + actual_uses_disassembled.emplace_back(DisassembleInst(actual_use)); + } + EXPECT_THAT(actual_uses_disassembled, + UnorderedElementsAreArray(expected_uses)); + } +} + +// The following test case mimics how LLVM handles induction variables. +// But, yeah, it's not very readable. However, we only care about the id +// defs and uses. So, no need to make sure this is valid OpPhi construct. +const char kOpPhiTestFunction[] = + " %1 = OpTypeVoid " + " %6 = OpTypeInt 32 0 " + "%10 = OpTypeFloat 32 " + "%16 = OpTypeBool " + " %3 = OpTypeFunction %1 " + " %8 = OpConstant %6 0 " + "%18 = OpConstant %6 1 " + "%12 = OpConstant %10 1.0 " + " %2 = OpFunction %1 None %3 " + " %4 = OpLabel " + " OpBranch %5 " + + " %5 = OpLabel " + " %7 = OpPhi %6 %8 %4 %9 %5 " + "%11 = OpPhi %10 %12 %4 %13 %5 " + " %9 = OpIAdd %6 %7 %8 " + "%13 = OpFAdd %10 %11 %12 " + "%17 = OpSLessThan %16 %7 %18 " + " OpLoopMerge %19 %5 None " + " OpBranchConditional %17 %5 %19 " + + "%19 = OpLabel " + " OpReturn " + " OpFunctionEnd"; + +struct ParseDefUseCase { + const char* text; + InstDefUse du; +}; + +using ParseDefUseTest = ::testing::TestWithParam; + +TEST_P(ParseDefUseTest, Case) { + const auto& tc = GetParam(); + + // Build module. + const std::vector text = {tc.text}; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text), + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Analyze def and use. + DefUseManager manager(context->module()); + + CheckDef(tc.du, manager.id_to_defs()); + CheckUse(tc.du, &manager, context->module()->IdBound()); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TestCase, ParseDefUseTest, + ::testing::ValuesIn(std::vector{ + {"", {{}, {}}}, // no instruction + {"OpMemoryModel Logical GLSL450", {{}, {}}}, // no def and use + { // single def, no use + "%1 = OpString \"wow\"", + { + {{1, "%1 = OpString \"wow\""}}, // defs + {} // uses + } + }, + { // multiple def, no use + "%1 = OpString \"hello\" " + "%2 = OpString \"world\" " + "%3 = OpTypeVoid", + { + { // defs + {1, "%1 = OpString \"hello\""}, + {2, "%2 = OpString \"world\""}, + {3, "%3 = OpTypeVoid"}, + }, + {} // uses + } + }, + { // multiple def, multiple use + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3 " + "%3 = OpTypeMatrix %2 3", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %1 3"}, + {3, "%3 = OpTypeMatrix %2 3"}, + }, + { // uses + {1, {"%2 = OpTypeVector %1 3"}}, + {2, {"%3 = OpTypeMatrix %2 3"}}, + } + } + }, + { // multiple use of the same id + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 2 " + "%3 = OpTypeVector %1 3 " + "%4 = OpTypeVector %1 4", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %1 2"}, + {3, "%3 = OpTypeVector %1 3"}, + {4, "%4 = OpTypeVector %1 4"}, + }, + { // uses + {1, + { + "%2 = OpTypeVector %1 2", + "%3 = OpTypeVector %1 3", + "%4 = OpTypeVector %1 4", + } + }, + } + } + }, + { // labels + "%1 = OpTypeVoid " + "%2 = OpTypeBool " + "%3 = OpTypeFunction %1 " + "%4 = OpConstantTrue %2 " + "%5 = OpFunction %1 None %3 " + + "%6 = OpLabel " + "OpBranchConditional %4 %7 %8 " + + "%7 = OpLabel " + "OpBranch %7 " + + "%8 = OpLabel " + "OpReturn " + + "OpFunctionEnd", + { + { // defs + {1, "%1 = OpTypeVoid"}, + {2, "%2 = OpTypeBool"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpConstantTrue %2"}, + {5, "%5 = OpFunction %1 None %3"}, + {6, "%6 = OpLabel"}, + {7, "%7 = OpLabel"}, + {8, "%8 = OpLabel"}, + }, + { // uses + {1, { + "%3 = OpTypeFunction %1", + "%5 = OpFunction %1 None %3", + } + }, + {2, {"%4 = OpConstantTrue %2"}}, + {3, {"%5 = OpFunction %1 None %3"}}, + {4, {"OpBranchConditional %4 %7 %8"}}, + {7, + { + "OpBranchConditional %4 %7 %8", + "OpBranch %7", + } + }, + {8, {"OpBranchConditional %4 %7 %8"}}, + } + } + }, + { // cross function + "%1 = OpTypeBool " + "%3 = OpTypeFunction %1 " + "%2 = OpFunction %1 None %3 " + + "%4 = OpLabel " + "%5 = OpVariable %1 Function " + "%6 = OpFunctionCall %1 %2 %5 " + "OpReturnValue %6 " + + "OpFunctionEnd", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpFunction %1 None %3"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpLabel"}, + {5, "%5 = OpVariable %1 Function"}, + {6, "%6 = OpFunctionCall %1 %2 %5"}, + }, + { // uses + {1, + { + "%2 = OpFunction %1 None %3", + "%3 = OpTypeFunction %1", + "%5 = OpVariable %1 Function", + "%6 = OpFunctionCall %1 %2 %5", + } + }, + {2, {"%6 = OpFunctionCall %1 %2 %5"}}, + {3, {"%2 = OpFunction %1 None %3"}}, + {5, {"%6 = OpFunctionCall %1 %2 %5"}}, + {6, {"OpReturnValue %6"}}, + } + } + }, + { // selection merge and loop merge + "%1 = OpTypeVoid " + "%3 = OpTypeFunction %1 " + "%10 = OpTypeBool " + "%8 = OpConstantTrue %10 " + "%2 = OpFunction %1 None %3 " + + "%4 = OpLabel " + "OpLoopMerge %5 %4 None " + "OpBranch %6 " + + "%5 = OpLabel " + "OpReturn " + + "%6 = OpLabel " + "OpSelectionMerge %7 None " + "OpBranchConditional %8 %9 %7 " + + "%7 = OpLabel " + "OpReturn " + + "%9 = OpLabel " + "OpReturn " + + "OpFunctionEnd", + { + { // defs + {1, "%1 = OpTypeVoid"}, + {2, "%2 = OpFunction %1 None %3"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpLabel"}, + {5, "%5 = OpLabel"}, + {6, "%6 = OpLabel"}, + {7, "%7 = OpLabel"}, + {8, "%8 = OpConstantTrue %10"}, + {9, "%9 = OpLabel"}, + {10, "%10 = OpTypeBool"}, + }, + { // uses + {1, + { + "%2 = OpFunction %1 None %3", + "%3 = OpTypeFunction %1", + } + }, + {3, {"%2 = OpFunction %1 None %3"}}, + {4, {"OpLoopMerge %5 %4 None"}}, + {5, {"OpLoopMerge %5 %4 None"}}, + {6, {"OpBranch %6"}}, + {7, + { + "OpSelectionMerge %7 None", + "OpBranchConditional %8 %9 %7", + } + }, + {8, {"OpBranchConditional %8 %9 %7"}}, + {9, {"OpBranchConditional %8 %9 %7"}}, + {10, {"%8 = OpConstantTrue %10"}}, + } + } + }, + { // Forward reference + "OpDecorate %1 Block " + "OpTypeForwardPointer %2 Input " + "%3 = OpTypeInt 32 0 " + "%1 = OpTypeStruct %3 " + "%2 = OpTypePointer Input %3", + { + { // defs + {1, "%1 = OpTypeStruct %3"}, + {2, "%2 = OpTypePointer Input %3"}, + {3, "%3 = OpTypeInt 32 0"}, + }, + { // uses + {1, {"OpDecorate %1 Block"}}, + {2, {"OpTypeForwardPointer %2 Input"}}, + {3, + { + "%1 = OpTypeStruct %3", + "%2 = OpTypePointer Input %3", + } + } + }, + }, + }, + { // OpPhi + kOpPhiTestFunction, + { + { // defs + {1, "%1 = OpTypeVoid"}, + {2, "%2 = OpFunction %1 None %3"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpLabel"}, + {5, "%5 = OpLabel"}, + {6, "%6 = OpTypeInt 32 0"}, + {7, "%7 = OpPhi %6 %8 %4 %9 %5"}, + {8, "%8 = OpConstant %6 0"}, + {9, "%9 = OpIAdd %6 %7 %8"}, + {10, "%10 = OpTypeFloat 32"}, + {11, "%11 = OpPhi %10 %12 %4 %13 %5"}, + {12, "%12 = OpConstant %10 1.0"}, + {13, "%13 = OpFAdd %10 %11 %12"}, + {16, "%16 = OpTypeBool"}, + {17, "%17 = OpSLessThan %16 %7 %18"}, + {18, "%18 = OpConstant %6 1"}, + {19, "%19 = OpLabel"}, + }, + { // uses + {1, + { + "%2 = OpFunction %1 None %3", + "%3 = OpTypeFunction %1", + } + }, + {3, {"%2 = OpFunction %1 None %3"}}, + {4, + { + "%7 = OpPhi %6 %8 %4 %9 %5", + "%11 = OpPhi %10 %12 %4 %13 %5", + } + }, + {5, + { + "OpBranch %5", + "%7 = OpPhi %6 %8 %4 %9 %5", + "%11 = OpPhi %10 %12 %4 %13 %5", + "OpLoopMerge %19 %5 None", + "OpBranchConditional %17 %5 %19", + } + }, + {6, + { + // Can't check constants properly + // "%8 = OpConstant %6 0", + // "%18 = OpConstant %6 1", + "%7 = OpPhi %6 %8 %4 %9 %5", + "%9 = OpIAdd %6 %7 %8", + } + }, + {7, + { + "%9 = OpIAdd %6 %7 %8", + "%17 = OpSLessThan %16 %7 %18", + } + }, + {8, + { + "%7 = OpPhi %6 %8 %4 %9 %5", + "%9 = OpIAdd %6 %7 %8", + } + }, + {9, {"%7 = OpPhi %6 %8 %4 %9 %5"}}, + {10, + { + // "%12 = OpConstant %10 1.0", + "%11 = OpPhi %10 %12 %4 %13 %5", + "%13 = OpFAdd %10 %11 %12", + } + }, + {11, {"%13 = OpFAdd %10 %11 %12"}}, + {12, + { + "%11 = OpPhi %10 %12 %4 %13 %5", + "%13 = OpFAdd %10 %11 %12", + } + }, + {13, {"%11 = OpPhi %10 %12 %4 %13 %5"}}, + {16, {"%17 = OpSLessThan %16 %7 %18"}}, + {17, {"OpBranchConditional %17 %5 %19"}}, + {18, {"%17 = OpSLessThan %16 %7 %18"}}, + {19, + { + "OpLoopMerge %19 %5 None", + "OpBranchConditional %17 %5 %19", + } + }, + }, + }, + }, + { // OpPhi defining and referencing the same id. + "%1 = OpTypeBool " + "%3 = OpTypeFunction %1 " + "%2 = OpConstantTrue %1 " + "%4 = OpFunction %1 None %3 " + "%6 = OpLabel " + " OpBranch %7 " + "%7 = OpLabel " + "%8 = OpPhi %1 %8 %7 %2 %6 " // both defines and uses %8 + " OpBranch %7 " + " OpFunctionEnd", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpConstantTrue %1"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpFunction %1 None %3"}, + {6, "%6 = OpLabel"}, + {7, "%7 = OpLabel"}, + {8, "%8 = OpPhi %1 %8 %7 %2 %6"}, + }, + { // uses + {1, + { + "%2 = OpConstantTrue %1", + "%3 = OpTypeFunction %1", + "%4 = OpFunction %1 None %3", + "%8 = OpPhi %1 %8 %7 %2 %6", + } + }, + {2, {"%8 = OpPhi %1 %8 %7 %2 %6"}}, + {3, {"%4 = OpFunction %1 None %3"}}, + {6, {"%8 = OpPhi %1 %8 %7 %2 %6"}}, + {7, + { + "OpBranch %7", + "%8 = OpPhi %1 %8 %7 %2 %6", + "OpBranch %7", + } + }, + {8, {"%8 = OpPhi %1 %8 %7 %2 %6"}}, + }, + }, + }, + }) +); +// clang-format on + +struct ReplaceUseCase { + const char* before; + std::vector> candidates; + const char* after; + InstDefUse du; +}; + +using ReplaceUseTest = ::testing::TestWithParam; + +// Disassembles the given |module| and returns the disassembly. +std::string DisassembleModule(Module* module) { + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + + std::vector binary; + module->ToBinary(&binary, /* skip_nop = */ false); + + std::string text; + // We'll need to check the underlying id numbers. + // So turn off friendly names for ids. + tools.Disassemble(binary, &text, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + while (!text.empty() && text.back() == '\n') text.pop_back(); + return text; +} + +TEST_P(ReplaceUseTest, Case) { + const auto& tc = GetParam(); + + // Build module. + const std::vector text = {tc.before}; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text), + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Force a re-build of def-use manager. + context->InvalidateAnalyses(IRContext::Analysis::kAnalysisDefUse); + (void)context->get_def_use_mgr(); + + // Do the substitution. + for (const auto& candidate : tc.candidates) { + context->ReplaceAllUsesWith(candidate.first, candidate.second); + } + + EXPECT_EQ(tc.after, DisassembleModule(context->module())); + CheckDef(tc.du, context->get_def_use_mgr()->id_to_defs()); + CheckUse(tc.du, context->get_def_use_mgr(), context->module()->IdBound()); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TestCase, ReplaceUseTest, + ::testing::ValuesIn(std::vector{ + { // no use, no replace request + "", {}, "", {}, + }, + { // replace one use + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3 " + "%3 = OpTypeInt 32 0 ", + {{1, 3}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %3 3\n" + "%3 = OpTypeInt 32 0", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %3 3"}, + {3, "%3 = OpTypeInt 32 0"}, + }, + { // uses + {3, {"%2 = OpTypeVector %3 3"}}, + }, + }, + }, + { // replace and then replace back + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3 " + "%3 = OpTypeInt 32 0", + {{1, 3}, {3, 1}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %1 3\n" + "%3 = OpTypeInt 32 0", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %1 3"}, + {3, "%3 = OpTypeInt 32 0"}, + }, + { // uses + {1, {"%2 = OpTypeVector %1 3"}}, + }, + }, + }, + { // replace with the same id + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3", + {{1, 1}, {2, 2}, {3, 3}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %1 3", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %1 3"}, + }, + { // uses + {1, {"%2 = OpTypeVector %1 3"}}, + }, + }, + }, + { // replace in sequence + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 3 " + "%3 = OpTypeInt 32 0 " + "%4 = OpTypeInt 32 1 ", + {{1, 3}, {3, 4}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %4 3\n" + "%3 = OpTypeInt 32 0\n" + "%4 = OpTypeInt 32 1", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %4 3"}, + {3, "%3 = OpTypeInt 32 0"}, + {4, "%4 = OpTypeInt 32 1"}, + }, + { // uses + {4, {"%2 = OpTypeVector %4 3"}}, + }, + }, + }, + { // replace multiple uses + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 2 " + "%3 = OpTypeVector %1 3 " + "%4 = OpTypeVector %1 4 " + "%5 = OpTypeMatrix %2 2 " + "%6 = OpTypeMatrix %3 3 " + "%7 = OpTypeMatrix %4 4 " + "%8 = OpTypeInt 32 0 " + "%9 = OpTypeInt 32 1 " + "%10 = OpTypeInt 64 0", + {{1, 8}, {2, 9}, {4, 10}}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %8 2\n" + "%3 = OpTypeVector %8 3\n" + "%4 = OpTypeVector %8 4\n" + "%5 = OpTypeMatrix %9 2\n" + "%6 = OpTypeMatrix %3 3\n" + "%7 = OpTypeMatrix %10 4\n" + "%8 = OpTypeInt 32 0\n" + "%9 = OpTypeInt 32 1\n" + "%10 = OpTypeInt 64 0", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %8 2"}, + {3, "%3 = OpTypeVector %8 3"}, + {4, "%4 = OpTypeVector %8 4"}, + {5, "%5 = OpTypeMatrix %9 2"}, + {6, "%6 = OpTypeMatrix %3 3"}, + {7, "%7 = OpTypeMatrix %10 4"}, + {8, "%8 = OpTypeInt 32 0"}, + {9, "%9 = OpTypeInt 32 1"}, + {10, "%10 = OpTypeInt 64 0"}, + }, + { // uses + {8, + { + "%2 = OpTypeVector %8 2", + "%3 = OpTypeVector %8 3", + "%4 = OpTypeVector %8 4", + } + }, + {9, {"%5 = OpTypeMatrix %9 2"}}, + {3, {"%6 = OpTypeMatrix %3 3"}}, + {10, {"%7 = OpTypeMatrix %10 4"}}, + }, + }, + }, + { // OpPhi. + kOpPhiTestFunction, + // replace one id used by OpPhi, replace one id generated by OpPhi + {{9, 13}, {11, 9}}, + "%1 = OpTypeVoid\n" + "%6 = OpTypeInt 32 0\n" + "%10 = OpTypeFloat 32\n" + "%16 = OpTypeBool\n" + "%3 = OpTypeFunction %1\n" + "%8 = OpConstant %6 0\n" + "%18 = OpConstant %6 1\n" + "%12 = OpConstant %10 1\n" + "%2 = OpFunction %1 None %3\n" + "%4 = OpLabel\n" + "OpBranch %5\n" + + "%5 = OpLabel\n" + "%7 = OpPhi %6 %8 %4 %13 %5\n" // %9 -> %13 + "%11 = OpPhi %10 %12 %4 %13 %5\n" + "%9 = OpIAdd %6 %7 %8\n" + "%13 = OpFAdd %10 %9 %12\n" // %11 -> %9 + "%17 = OpSLessThan %16 %7 %18\n" + "OpLoopMerge %19 %5 None\n" + "OpBranchConditional %17 %5 %19\n" + + "%19 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd", + { + { // defs. + {1, "%1 = OpTypeVoid"}, + {2, "%2 = OpFunction %1 None %3"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpLabel"}, + {5, "%5 = OpLabel"}, + {6, "%6 = OpTypeInt 32 0"}, + {7, "%7 = OpPhi %6 %8 %4 %13 %5"}, + {8, "%8 = OpConstant %6 0"}, + {9, "%9 = OpIAdd %6 %7 %8"}, + {10, "%10 = OpTypeFloat 32"}, + {11, "%11 = OpPhi %10 %12 %4 %13 %5"}, + {12, "%12 = OpConstant %10 1.0"}, + {13, "%13 = OpFAdd %10 %9 %12"}, + {16, "%16 = OpTypeBool"}, + {17, "%17 = OpSLessThan %16 %7 %18"}, + {18, "%18 = OpConstant %6 1"}, + {19, "%19 = OpLabel"}, + }, + { // uses + {1, + { + "%2 = OpFunction %1 None %3", + "%3 = OpTypeFunction %1", + } + }, + {3, {"%2 = OpFunction %1 None %3"}}, + {4, + { + "%7 = OpPhi %6 %8 %4 %13 %5", + "%11 = OpPhi %10 %12 %4 %13 %5", + } + }, + {5, + { + "OpBranch %5", + "%7 = OpPhi %6 %8 %4 %13 %5", + "%11 = OpPhi %10 %12 %4 %13 %5", + "OpLoopMerge %19 %5 None", + "OpBranchConditional %17 %5 %19", + } + }, + {6, + { + // Can't properly check constants + // "%8 = OpConstant %6 0", + // "%18 = OpConstant %6 1", + "%7 = OpPhi %6 %8 %4 %13 %5", + "%9 = OpIAdd %6 %7 %8" + } + }, + {7, + { + "%9 = OpIAdd %6 %7 %8", + "%17 = OpSLessThan %16 %7 %18", + } + }, + {8, + { + "%7 = OpPhi %6 %8 %4 %13 %5", + "%9 = OpIAdd %6 %7 %8", + } + }, + {9, {"%13 = OpFAdd %10 %9 %12"}}, // uses of %9 changed from %7 to %13 + {10, + { + "%11 = OpPhi %10 %12 %4 %13 %5", + // "%12 = OpConstant %10 1", + "%13 = OpFAdd %10 %9 %12" + } + }, + // no more uses of %11 + {12, + { + "%11 = OpPhi %10 %12 %4 %13 %5", + "%13 = OpFAdd %10 %9 %12" + } + }, + {13, { + "%7 = OpPhi %6 %8 %4 %13 %5", + "%11 = OpPhi %10 %12 %4 %13 %5", + } + }, + {16, {"%17 = OpSLessThan %16 %7 %18"}}, + {17, {"OpBranchConditional %17 %5 %19"}}, + {18, {"%17 = OpSLessThan %16 %7 %18"}}, + {19, + { + "OpLoopMerge %19 %5 None", + "OpBranchConditional %17 %5 %19", + } + }, + }, + }, + }, + { // OpPhi defining and referencing the same id. + "%1 = OpTypeBool " + "%3 = OpTypeFunction %1 " + "%2 = OpConstantTrue %1 " + + "%4 = OpFunction %3 None %1 " + "%6 = OpLabel " + " OpBranch %7 " + "%7 = OpLabel " + "%8 = OpPhi %1 %8 %7 %2 %6 " // both defines and uses %8 + " OpBranch %7 " + " OpFunctionEnd", + {{8, 2}}, + "%1 = OpTypeBool\n" + "%3 = OpTypeFunction %1\n" + "%2 = OpConstantTrue %1\n" + + "%4 = OpFunction %3 None %1\n" + "%6 = OpLabel\n" + "OpBranch %7\n" + "%7 = OpLabel\n" + "%8 = OpPhi %1 %2 %7 %2 %6\n" // use of %8 changed to %2 + "OpBranch %7\n" + "OpFunctionEnd", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpConstantTrue %1"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpFunction %3 None %1"}, + {6, "%6 = OpLabel"}, + {7, "%7 = OpLabel"}, + {8, "%8 = OpPhi %1 %2 %7 %2 %6"}, + }, + { // uses + {1, + { + "%2 = OpConstantTrue %1", + "%3 = OpTypeFunction %1", + "%4 = OpFunction %3 None %1", + "%8 = OpPhi %1 %2 %7 %2 %6", + } + }, + {2, + { + // Only checking users + "%8 = OpPhi %1 %2 %7 %2 %6", + } + }, + {3, {"%4 = OpFunction %3 None %1"}}, + {6, {"%8 = OpPhi %1 %2 %7 %2 %6"}}, + {7, + { + "OpBranch %7", + "%8 = OpPhi %1 %2 %7 %2 %6", + "OpBranch %7", + } + }, + // {8, {"%8 = OpPhi %1 %8 %7 %2 %6"}}, + }, + }, + }, + }) +); +// clang-format on + +struct KillDefCase { + const char* before; + std::vector ids_to_kill; + const char* after; + InstDefUse du; +}; + +using KillDefTest = ::testing::TestWithParam; + +TEST_P(KillDefTest, Case) { + const auto& tc = GetParam(); + + // Build module. + const std::vector text = {tc.before}; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text), + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Analyze def and use. + DefUseManager manager(context->module()); + + // Do the substitution. + for (const auto id : tc.ids_to_kill) context->KillDef(id); + + EXPECT_EQ(tc.after, DisassembleModule(context->module())); + CheckDef(tc.du, context->get_def_use_mgr()->id_to_defs()); + CheckUse(tc.du, context->get_def_use_mgr(), context->module()->IdBound()); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TestCase, KillDefTest, + ::testing::ValuesIn(std::vector{ + { // no def, no use, no kill + "", {}, "", {} + }, + { // kill nothing + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 2 " + "%3 = OpTypeVector %1 3 ", + {}, + "%1 = OpTypeBool\n" + "%2 = OpTypeVector %1 2\n" + "%3 = OpTypeVector %1 3", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpTypeVector %1 2"}, + {3, "%3 = OpTypeVector %1 3"}, + }, + { // uses + {1, + { + "%2 = OpTypeVector %1 2", + "%3 = OpTypeVector %1 3", + } + }, + }, + }, + }, + { // kill id used, kill id not used, kill id not defined + "%1 = OpTypeBool " + "%2 = OpTypeVector %1 2 " + "%3 = OpTypeVector %1 3 " + "%4 = OpTypeVector %1 4 " + "%5 = OpTypeMatrix %3 3 " + "%6 = OpTypeMatrix %2 3", + {1, 3, 5, 10}, // ids to kill + "%2 = OpTypeVector %1 2\n" + "%4 = OpTypeVector %1 4\n" + "%6 = OpTypeMatrix %2 3", + { + { // defs + {2, "%2 = OpTypeVector %1 2"}, + {4, "%4 = OpTypeVector %1 4"}, + {6, "%6 = OpTypeMatrix %2 3"}, + }, + { // uses. %1 and %3 are both killed, so no uses + // recorded for them anymore. + {2, {"%6 = OpTypeMatrix %2 3"}}, + } + }, + }, + { // OpPhi. + kOpPhiTestFunction, + {9, 11}, // kill one id used by OpPhi, kill one id generated by OpPhi + "%1 = OpTypeVoid\n" + "%6 = OpTypeInt 32 0\n" + "%10 = OpTypeFloat 32\n" + "%16 = OpTypeBool\n" + "%3 = OpTypeFunction %1\n" + "%8 = OpConstant %6 0\n" + "%18 = OpConstant %6 1\n" + "%12 = OpConstant %10 1\n" + "%2 = OpFunction %1 None %3\n" + "%4 = OpLabel\n" + "OpBranch %5\n" + + "%5 = OpLabel\n" + "%7 = OpPhi %6 %8 %4 %9 %5\n" + "%13 = OpFAdd %10 %11 %12\n" + "%17 = OpSLessThan %16 %7 %18\n" + "OpLoopMerge %19 %5 None\n" + "OpBranchConditional %17 %5 %19\n" + + "%19 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd", + { + { // defs. %9 & %11 are killed. + {1, "%1 = OpTypeVoid"}, + {2, "%2 = OpFunction %1 None %3"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpLabel"}, + {5, "%5 = OpLabel"}, + {6, "%6 = OpTypeInt 32 0"}, + {7, "%7 = OpPhi %6 %8 %4 %9 %5"}, + {8, "%8 = OpConstant %6 0"}, + {10, "%10 = OpTypeFloat 32"}, + {12, "%12 = OpConstant %10 1.0"}, + {13, "%13 = OpFAdd %10 %11 %12"}, + {16, "%16 = OpTypeBool"}, + {17, "%17 = OpSLessThan %16 %7 %18"}, + {18, "%18 = OpConstant %6 1"}, + {19, "%19 = OpLabel"}, + }, + { // uses + {1, + { + "%2 = OpFunction %1 None %3", + "%3 = OpTypeFunction %1", + } + }, + {3, {"%2 = OpFunction %1 None %3"}}, + {4, + { + "%7 = OpPhi %6 %8 %4 %9 %5", + // "%11 = OpPhi %10 %12 %4 %13 %5", + } + }, + {5, + { + "OpBranch %5", + "%7 = OpPhi %6 %8 %4 %9 %5", + // "%11 = OpPhi %10 %12 %4 %13 %5", + "OpLoopMerge %19 %5 None", + "OpBranchConditional %17 %5 %19", + } + }, + {6, + { + // Can't properly check constants + // "%8 = OpConstant %6 0", + // "%18 = OpConstant %6 1", + "%7 = OpPhi %6 %8 %4 %9 %5", + // "%9 = OpIAdd %6 %7 %8" + } + }, + {7, {"%17 = OpSLessThan %16 %7 %18"}}, + {8, + { + "%7 = OpPhi %6 %8 %4 %9 %5", + // "%9 = OpIAdd %6 %7 %8", + } + }, + // {9, {"%7 = OpPhi %6 %8 %4 %13 %5"}}, + {10, + { + // "%11 = OpPhi %10 %12 %4 %13 %5", + // "%12 = OpConstant %10 1", + "%13 = OpFAdd %10 %11 %12" + } + }, + // {11, {"%13 = OpFAdd %10 %11 %12"}}, + {12, + { + // "%11 = OpPhi %10 %12 %4 %13 %5", + "%13 = OpFAdd %10 %11 %12" + } + }, + // {13, {"%11 = OpPhi %10 %12 %4 %13 %5"}}, + {16, {"%17 = OpSLessThan %16 %7 %18"}}, + {17, {"OpBranchConditional %17 %5 %19"}}, + {18, {"%17 = OpSLessThan %16 %7 %18"}}, + {19, + { + "OpLoopMerge %19 %5 None", + "OpBranchConditional %17 %5 %19", + } + }, + }, + }, + }, + { // OpPhi defining and referencing the same id. + "%1 = OpTypeBool " + "%3 = OpTypeFunction %1 " + "%2 = OpConstantTrue %1 " + "%4 = OpFunction %3 None %1 " + "%6 = OpLabel " + " OpBranch %7 " + "%7 = OpLabel " + "%8 = OpPhi %1 %8 %7 %2 %6 " // both defines and uses %8 + " OpBranch %7 " + " OpFunctionEnd", + {8}, + "%1 = OpTypeBool\n" + "%3 = OpTypeFunction %1\n" + "%2 = OpConstantTrue %1\n" + + "%4 = OpFunction %3 None %1\n" + "%6 = OpLabel\n" + "OpBranch %7\n" + "%7 = OpLabel\n" + "OpBranch %7\n" + "OpFunctionEnd", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpConstantTrue %1"}, + {3, "%3 = OpTypeFunction %1"}, + {4, "%4 = OpFunction %3 None %1"}, + {6, "%6 = OpLabel"}, + {7, "%7 = OpLabel"}, + // {8, "%8 = OpPhi %1 %8 %7 %2 %6"}, + }, + { // uses + {1, + { + "%2 = OpConstantTrue %1", + "%3 = OpTypeFunction %1", + "%4 = OpFunction %3 None %1", + // "%8 = OpPhi %1 %8 %7 %2 %6", + } + }, + // {2, {"%8 = OpPhi %1 %8 %7 %2 %6"}}, + {3, {"%4 = OpFunction %3 None %1"}}, + // {6, {"%8 = OpPhi %1 %8 %7 %2 %6"}}, + {7, + { + "OpBranch %7", + // "%8 = OpPhi %1 %8 %7 %2 %6", + "OpBranch %7", + } + }, + // {8, {"%8 = OpPhi %1 %8 %7 %2 %6"}}, + }, + }, + }, + }) +); +// clang-format on + +TEST(DefUseTest, OpSwitch) { + // Because disassembler has basic type check for OpSwitch's selector, we + // cannot use the DisassembleInst() in the above. Thus, this special spotcheck + // test case. + + const char original_text[] = + // int64 f(int64 v) { + // switch (v) { + // case 1: break; + // case -4294967296: break; + // case 9223372036854775807: break; + // default: break; + // } + // return v; + // } + " %1 = OpTypeInt 64 1 " + " %3 = OpTypePointer Input %1 " + " %2 = OpFunction %1 None %3 " // %3 is int64(int64)* + " %4 = OpFunctionParameter %1 " + " %5 = OpLabel " + " %6 = OpLoad %1 %4 " // selector value + " OpSelectionMerge %7 None " + " OpSwitch %6 %8 " + " 1 %9 " // 1 + " -4294967296 %10 " // -2^32 + " 9223372036854775807 %11 " // 2^63-1 + " %8 = OpLabel " // default + " OpBranch %7 " + " %9 = OpLabel " + " OpBranch %7 " + "%10 = OpLabel " + " OpBranch %7 " + "%11 = OpLabel " + " OpBranch %7 " + " %7 = OpLabel " + " OpReturnValue %6 " + " OpFunctionEnd"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, original_text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Force a re-build of def-use manager. + context->InvalidateAnalyses(IRContext::Analysis::kAnalysisDefUse); + (void)context->get_def_use_mgr(); + + // Do a bunch replacements. + context->ReplaceAllUsesWith(11, 7); // to existing id + context->ReplaceAllUsesWith(10, 11); // to existing id + context->ReplaceAllUsesWith(9, 10); // to existing id + + // clang-format off + const char modified_text[] = + "%1 = OpTypeInt 64 1\n" + "%3 = OpTypePointer Input %1\n" + "%2 = OpFunction %1 None %3\n" // %3 is int64(int64)* + "%4 = OpFunctionParameter %1\n" + "%5 = OpLabel\n" + "%6 = OpLoad %1 %4\n" // selector value + "OpSelectionMerge %7 None\n" + "OpSwitch %6 %8 1 %10 -4294967296 %11 9223372036854775807 %7\n" // changed! + "%8 = OpLabel\n" // default + "OpBranch %7\n" + "%9 = OpLabel\n" + "OpBranch %7\n" + "%10 = OpLabel\n" + "OpBranch %7\n" + "%11 = OpLabel\n" + "OpBranch %7\n" + "%7 = OpLabel\n" + "OpReturnValue %6\n" + "OpFunctionEnd"; + // clang-format on + + EXPECT_EQ(modified_text, DisassembleModule(context->module())); + + InstDefUse def_uses = {}; + def_uses.defs = { + {1, "%1 = OpTypeInt 64 1"}, + {2, "%2 = OpFunction %1 None %3"}, + {3, "%3 = OpTypePointer Input %1"}, + {4, "%4 = OpFunctionParameter %1"}, + {5, "%5 = OpLabel"}, + {6, "%6 = OpLoad %1 %4"}, + {7, "%7 = OpLabel"}, + {8, "%8 = OpLabel"}, + {9, "%9 = OpLabel"}, + {10, "%10 = OpLabel"}, + {11, "%11 = OpLabel"}, + }; + CheckDef(def_uses, context->get_def_use_mgr()->id_to_defs()); + + { + EXPECT_EQ(2u, NumUses(context, 6)); + std::vector opcodes = GetUseOpcodes(context, 6u); + EXPECT_THAT(opcodes, UnorderedElementsAre(SpvOpSwitch, SpvOpReturnValue)); + } + { + EXPECT_EQ(6u, NumUses(context, 7)); + std::vector opcodes = GetUseOpcodes(context, 7u); + // OpSwitch is now a user of %7. + EXPECT_THAT(opcodes, UnorderedElementsAre(SpvOpSelectionMerge, SpvOpBranch, + SpvOpBranch, SpvOpBranch, + SpvOpBranch, SpvOpSwitch)); + } + // Check all ids only used by OpSwitch after replacement. + for (const auto id : {8u, 10u, 11u}) { + EXPECT_EQ(1u, NumUses(context, id)); + EXPECT_EQ(SpvOpSwitch, GetUseOpcodes(context, id).back()); + } +} + +// Test case for analyzing individual instructions. +struct AnalyzeInstDefUseTestCase { + const char* module_text; + InstDefUse expected_define_use; +}; + +using AnalyzeInstDefUseTest = + ::testing::TestWithParam; + +// Test the analyzing result for individual instructions. +TEST_P(AnalyzeInstDefUseTest, Case) { + auto tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.module_text); + ASSERT_NE(nullptr, context); + + // Analyze the instructions. + DefUseManager manager(context->module()); + + CheckDef(tc.expected_define_use, manager.id_to_defs()); + CheckUse(tc.expected_define_use, &manager, context->module()->IdBound()); + // CheckUse(tc.expected_define_use, manager.id_to_uses()); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TestCase, AnalyzeInstDefUseTest, + ::testing::ValuesIn(std::vector{ + { // A type declaring instruction. + "%1 = OpTypeInt 32 1", + { + // defs + {{1, "%1 = OpTypeInt 32 1"}}, + {}, // no uses + }, + }, + { // A type declaring instruction and a constant value. + "%1 = OpTypeBool " + "%2 = OpConstantTrue %1", + { + { // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpConstantTrue %1"}, + }, + { // uses + {1, {"%2 = OpConstantTrue %1"}}, + }, + }, + }, + })); +// clang-format on + +using AnalyzeInstDefUse = ::testing::Test; + +TEST(AnalyzeInstDefUse, UseWithNoResultId) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + + // Analyze the instructions. + DefUseManager manager(context.module()); + + Instruction label(&context, SpvOpLabel, 0, 2, {}); + manager.AnalyzeInstDefUse(&label); + + Instruction branch(&context, SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {2}}}); + manager.AnalyzeInstDefUse(&branch); + context.module()->SetIdBound(3); + + InstDefUse expected = { + // defs + { + {2, "%2 = OpLabel"}, + }, + // uses + {{2, {"OpBranch %2"}}}, + }; + + CheckDef(expected, manager.id_to_defs()); + CheckUse(expected, &manager, context.module()->IdBound()); +} + +TEST(AnalyzeInstDefUse, AddNewInstruction) { + const std::string input = "%1 = OpTypeBool"; + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input); + ASSERT_NE(nullptr, context); + + // Analyze the instructions. + DefUseManager manager(context->module()); + + Instruction newInst(context.get(), SpvOpConstantTrue, 1, 2, {}); + manager.AnalyzeInstDefUse(&newInst); + + InstDefUse expected = { + { + // defs + {1, "%1 = OpTypeBool"}, + {2, "%2 = OpConstantTrue %1"}, + }, + { + // uses + {1, {"%2 = OpConstantTrue %1"}}, + }, + }; + + CheckDef(expected, manager.id_to_defs()); + CheckUse(expected, &manager, context->module()->IdBound()); +} + +struct KillInstTestCase { + const char* before; + std::unordered_set indices_for_inst_to_kill; + const char* after; + InstDefUse expected_define_use; +}; + +using KillInstTest = ::testing::TestWithParam; + +TEST_P(KillInstTest, Case) { + auto tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.before, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Force a re-build of the def-use manager. + context->InvalidateAnalyses(IRContext::Analysis::kAnalysisDefUse); + (void)context->get_def_use_mgr(); + + // KillInst + context->module()->ForEachInst([&tc, &context](Instruction* inst) { + if (tc.indices_for_inst_to_kill.count(inst->result_id())) { + context->KillInst(inst); + } + }); + + EXPECT_EQ(tc.after, DisassembleModule(context->module())); + CheckDef(tc.expected_define_use, context->get_def_use_mgr()->id_to_defs()); + CheckUse(tc.expected_define_use, context->get_def_use_mgr(), + context->module()->IdBound()); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TestCase, KillInstTest, + ::testing::ValuesIn(std::vector{ + // Kill id defining instructions. + { + "%3 = OpTypeVoid " + "%1 = OpTypeFunction %3 " + "%2 = OpFunction %1 None %3 " + "%4 = OpLabel " + " OpBranch %5 " + "%5 = OpLabel " + " OpBranch %6 " + "%6 = OpLabel " + " OpBranch %4 " + "%7 = OpLabel " + " OpReturn " + " OpFunctionEnd", + {3, 5, 7}, + "%1 = OpTypeFunction %3\n" + "%2 = OpFunction %1 None %3\n" + "%4 = OpLabel\n" + "OpBranch %5\n" + "OpNop\n" + "OpBranch %6\n" + "%6 = OpLabel\n" + "OpBranch %4\n" + "OpNop\n" + "OpReturn\n" + "OpFunctionEnd", + { + // defs + { + {1, "%1 = OpTypeFunction %3"}, + {2, "%2 = OpFunction %1 None %3"}, + {4, "%4 = OpLabel"}, + {6, "%6 = OpLabel"}, + }, + // uses + { + {1, {"%2 = OpFunction %1 None %3"}}, + {4, {"OpBranch %4"}}, + {6, {"OpBranch %6"}}, + } + } + }, + // Kill instructions that do not have result ids. + { + "%3 = OpTypeVoid " + "%1 = OpTypeFunction %3 " + "%2 = OpFunction %1 None %3 " + "%4 = OpLabel " + " OpBranch %5 " + "%5 = OpLabel " + " OpBranch %6 " + "%6 = OpLabel " + " OpBranch %4 " + "%7 = OpLabel " + " OpReturn " + " OpFunctionEnd", + {2, 4}, + "%3 = OpTypeVoid\n" + "%1 = OpTypeFunction %3\n" + "OpNop\n" + "OpNop\n" + "OpBranch %5\n" + "%5 = OpLabel\n" + "OpBranch %6\n" + "%6 = OpLabel\n" + "OpBranch %4\n" + "%7 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd", + { + // defs + { + {1, "%1 = OpTypeFunction %3"}, + {3, "%3 = OpTypeVoid"}, + {5, "%5 = OpLabel"}, + {6, "%6 = OpLabel"}, + {7, "%7 = OpLabel"}, + }, + // uses + { + {3, {"%1 = OpTypeFunction %3"}}, + {5, {"OpBranch %5"}}, + {6, {"OpBranch %6"}}, + } + } + }, + })); +// clang-format on + +struct GetAnnotationsTestCase { + const char* code; + uint32_t id; + std::vector annotations; +}; + +using GetAnnotationsTest = ::testing::TestWithParam; + +TEST_P(GetAnnotationsTest, Case) { + const GetAnnotationsTestCase& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.code); + ASSERT_NE(nullptr, context); + + // Get annotations + DefUseManager manager(context->module()); + auto insts = manager.GetAnnotations(tc.id); + + // Check + ASSERT_EQ(tc.annotations.size(), insts.size()) + << "wrong number of annotation instructions"; + auto inst_iter = insts.begin(); + for (const std::string& expected_anno_inst : tc.annotations) { + EXPECT_EQ(expected_anno_inst, DisassembleInst(*inst_iter)) + << "annotation instruction mismatch"; + inst_iter++; + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TestCase, GetAnnotationsTest, + ::testing::ValuesIn(std::vector{ + // empty + {"", 0, {}}, + // basic + { + // code + "OpDecorate %1 Block " + "OpDecorate %1 RelaxedPrecision " + "%3 = OpTypeInt 32 0 " + "%1 = OpTypeStruct %3", + // id + 1, + // annotations + { + "OpDecorate %1 Block", + "OpDecorate %1 RelaxedPrecision", + }, + }, + // with debug instructions + { + // code + "OpName %1 \"struct_type\" " + "OpName %3 \"int_type\" " + "OpDecorate %1 Block " + "OpDecorate %1 RelaxedPrecision " + "%3 = OpTypeInt 32 0 " + "%1 = OpTypeStruct %3", + // id + 1, + // annotations + { + "OpDecorate %1 Block", + "OpDecorate %1 RelaxedPrecision", + }, + }, + // no annotations + { + // code + "OpName %1 \"struct_type\" " + "OpName %3 \"int_type\" " + "OpDecorate %1 Block " + "OpDecorate %1 RelaxedPrecision " + "%3 = OpTypeInt 32 0 " + "%1 = OpTypeStruct %3", + // id + 3, + // annotations + {}, + }, + // decoration group + { + // code + "OpDecorate %1 Block " + "OpDecorate %1 RelaxedPrecision " + "%1 = OpDecorationGroup " + "OpGroupDecorate %1 %2 %3 " + "%4 = OpTypeInt 32 0 " + "%2 = OpTypeStruct %4 " + "%3 = OpTypeStruct %4 %4", + // id + 3, + // annotations + { + "OpGroupDecorate %1 %2 %3", + }, + }, + // memeber decorate + { + // code + "OpMemberDecorate %1 0 RelaxedPrecision " + "%2 = OpTypeInt 32 0 " + "%1 = OpTypeStruct %2 %2", + // id + 1, + // annotations + { + "OpMemberDecorate %1 0 RelaxedPrecision", + }, + }, + })); + +using UpdateUsesTest = PassTest<::testing::Test>; + +TEST_F(UpdateUsesTest, KeepOldUses) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%uint_5 = OpConstant %uint 5", + "%25 = OpConstant %uint 25", + "%main = OpFunction %void None %4", + "%8 = OpLabel", + "%9 = OpIMul %uint %uint_5 %uint_5", + "%10 = OpIMul %uint %9 %uint_5", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, JoinAllInsts(text), + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* def = def_use_mgr->GetDef(9); + Instruction* use = def_use_mgr->GetDef(10); + def->SetOpcode(SpvOpCopyObject); + def->SetInOperands({{SPV_OPERAND_TYPE_ID, {25}}}); + context->UpdateDefUse(def); + + auto users = def_use_mgr->id_to_users(); + UserEntry entry = {def, use}; + EXPECT_THAT(users, Contains(entry)); +} +// clang-format on + +} // namespace +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/desc_sroa_test.cpp b/third_party/spirv-tools/test/opt/desc_sroa_test.cpp new file mode 100644 index 0000000..cdcc9a8 --- /dev/null +++ b/third_party/spirv-tools/test/opt/desc_sroa_test.cpp @@ -0,0 +1,734 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using DescriptorScalarReplacementTest = PassTest<::testing::Test>; + +std::string GetStructureArrayTestSpirv() { + // The SPIR-V for the following high-level shader: + // Flattening structures and arrays should result in the following binding + // numbers. Only the ones that are actually used in the shader should be in + // the final SPIR-V. + // + // globalS[0][0].t[0] 0 (used) + // globalS[0][0].t[1] 1 + // globalS[0][0].s[0] 2 (used) + // globalS[0][0].s[1] 3 + // globalS[0][1].t[0] 4 + // globalS[0][1].t[1] 5 + // globalS[0][1].s[0] 6 + // globalS[0][1].s[1] 7 + // globalS[1][0].t[0] 8 + // globalS[1][0].t[1] 9 + // globalS[1][0].s[0] 10 + // globalS[1][0].s[1] 11 + // globalS[1][1].t[0] 12 + // globalS[1][1].t[1] 13 (used) + // globalS[1][1].s[0] 14 + // globalS[1][1].s[1] 15 (used) + + /* + struct S { + Texture2D t[2]; + SamplerState s[2]; + }; + + S globalS[2][2]; + + float4 main() : SV_Target { + return globalS[0][0].t[0].Sample(globalS[0][0].s[0], float2(0,0)) + + globalS[1][1].t[1].Sample(globalS[1][1].s[1], float2(0,0)); + } + */ + + return R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpName %S "S" + OpMemberName %S 0 "t" + OpMemberName %S 1 "s" + OpName %type_2d_image "type.2d.image" + OpName %type_sampler "type.sampler" + OpName %globalS "globalS" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpName %src_main "src.main" + OpName %bb_entry "bb.entry" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %globalS DescriptorSet 0 + OpDecorate %globalS Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %v2float = OpTypeVector %float 2 + %10 = OpConstantComposite %v2float %float_0 %float_0 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2 +%type_sampler = OpTypeSampler +%_arr_type_sampler_uint_2 = OpTypeArray %type_sampler %uint_2 + %S = OpTypeStruct %_arr_type_2d_image_uint_2 %_arr_type_sampler_uint_2 +%_arr_S_uint_2 = OpTypeArray %S %uint_2 +%_arr__arr_S_uint_2_uint_2 = OpTypeArray %_arr_S_uint_2 %uint_2 +%_ptr_UniformConstant__arr__arr_S_uint_2_uint_2 = OpTypePointer UniformConstant %_arr__arr_S_uint_2_uint_2 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %24 = OpTypeFunction %void + %28 = OpTypeFunction %v4float +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%type_sampled_image = OpTypeSampledImage %type_2d_image + %globalS = OpVariable %_ptr_UniformConstant__arr__arr_S_uint_2_uint_2 UniformConstant +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %24 + %25 = OpLabel + %26 = OpFunctionCall %v4float %src_main + OpStore %out_var_SV_Target %26 + OpReturn + OpFunctionEnd + %src_main = OpFunction %v4float None %28 + %bb_entry = OpLabel + %31 = OpAccessChain %_ptr_UniformConstant_type_2d_image %globalS %int_0 %int_0 %int_0 %int_0 + %32 = OpLoad %type_2d_image %31 + %34 = OpAccessChain %_ptr_UniformConstant_type_sampler %globalS %int_0 %int_0 %int_1 %int_0 + %35 = OpLoad %type_sampler %34 + %37 = OpSampledImage %type_sampled_image %32 %35 + %38 = OpImageSampleImplicitLod %v4float %37 %10 None + %39 = OpAccessChain %_ptr_UniformConstant_type_2d_image %globalS %int_1 %int_1 %int_0 %int_1 + %40 = OpLoad %type_2d_image %39 + %41 = OpAccessChain %_ptr_UniformConstant_type_sampler %globalS %int_1 %int_1 %int_1 %int_1 + %42 = OpLoad %type_sampler %41 + %43 = OpSampledImage %type_sampled_image %40 %42 + %44 = OpImageSampleImplicitLod %v4float %43 %10 None + %45 = OpFAdd %v4float %38 %44 + OpReturnValue %45 + OpFunctionEnd + )"; +} + +TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfTextures) { + const std::string text = R"( +; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var1]] Binding 0 +; CHECK: OpDecorate [[var2:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var2]] Binding 1 +; CHECK: OpDecorate [[var3:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var3]] Binding 2 +; CHECK: OpDecorate [[var4:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var4]] Binding 3 +; CHECK: OpDecorate [[var5:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var5]] Binding 4 +; CHECK: [[image_type:%\w+]] = OpTypeImage +; CHECK: [[ptr_type:%\w+]] = OpTypePointer UniformConstant [[image_type]] +; CHECK: [[var1]] = OpVariable [[ptr_type]] UniformConstant +; CHECK: [[var2]] = OpVariable [[ptr_type]] UniformConstant +; CHECK: [[var3]] = OpVariable [[ptr_type]] UniformConstant +; CHECK: [[var4]] = OpVariable [[ptr_type]] UniformConstant +; CHECK: [[var5]] = OpVariable [[ptr_type]] UniformConstant +; CHECK: OpLoad [[image_type]] [[var1]] +; CHECK: OpLoad [[image_type]] [[var2]] +; CHECK: OpLoad [[image_type]] [[var3]] +; CHECK: OpLoad [[image_type]] [[var4]] +; CHECK: OpLoad [[image_type]] [[var5]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpDecorate %MyTextures DescriptorSet 0 + OpDecorate %MyTextures Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_3 = OpConstant %int 3 + %int_4 = OpConstant %int 4 + %uint = OpTypeInt 32 0 + %uint_5 = OpConstant %uint 5 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_5 = OpTypeArray %type_2d_image %uint_5 +%_ptr_UniformConstant__arr_type_2d_image_uint_5 = OpTypePointer UniformConstant %_arr_type_2d_image_uint_5 + %v2float = OpTypeVector %float 2 + %void = OpTypeVoid + %26 = OpTypeFunction %void +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %MyTextures = OpVariable %_ptr_UniformConstant__arr_type_2d_image_uint_5 UniformConstant + %main = OpFunction %void None %26 + %28 = OpLabel + %29 = OpUndef %v2float + %30 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_0 + %31 = OpLoad %type_2d_image %30 + %35 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_1 + %36 = OpLoad %type_2d_image %35 + %40 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_2 + %41 = OpLoad %type_2d_image %40 + %45 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_3 + %46 = OpLoad %type_2d_image %45 + %50 = OpAccessChain %_ptr_UniformConstant_type_2d_image %MyTextures %int_4 + %51 = OpLoad %type_2d_image %50 + OpReturn + OpFunctionEnd + + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSamplers) { + const std::string text = R"( +; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var1]] Binding 1 +; CHECK: OpDecorate [[var2:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var2]] Binding 2 +; CHECK: OpDecorate [[var3:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var3]] Binding 3 +; CHECK: [[sampler_type:%\w+]] = OpTypeSampler +; CHECK: [[ptr_type:%\w+]] = OpTypePointer UniformConstant [[sampler_type]] +; CHECK: [[var1]] = OpVariable [[ptr_type]] UniformConstant +; CHECK: [[var2]] = OpVariable [[ptr_type]] UniformConstant +; CHECK: [[var3]] = OpVariable [[ptr_type]] UniformConstant +; CHECK: OpLoad [[sampler_type]] [[var1]] +; CHECK: OpLoad [[sampler_type]] [[var2]] +; CHECK: OpLoad [[sampler_type]] [[var3]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpDecorate %MySampler DescriptorSet 0 + OpDecorate %MySampler Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 +%type_sampler = OpTypeSampler +%_arr_type_sampler_uint_3 = OpTypeArray %type_sampler %uint_3 +%_ptr_UniformConstant__arr_type_sampler_uint_3 = OpTypePointer UniformConstant %_arr_type_sampler_uint_3 + %void = OpTypeVoid + %26 = OpTypeFunction %void +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %MySampler = OpVariable %_ptr_UniformConstant__arr_type_sampler_uint_3 UniformConstant + %main = OpFunction %void None %26 + %28 = OpLabel + %31 = OpAccessChain %_ptr_UniformConstant_type_sampler %MySampler %int_0 + %32 = OpLoad %type_sampler %31 + %35 = OpAccessChain %_ptr_UniformConstant_type_sampler %MySampler %int_1 + %36 = OpLoad %type_sampler %35 + %40 = OpAccessChain %_ptr_UniformConstant_type_sampler %MySampler %int_2 + %41 = OpLoad %type_sampler %40 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, ExpandArrayOfSSBOs) { + // Tests the expansion of an SSBO. Also check that an access chain with more + // than 1 index is correctly handled. + const std::string text = R"( +; CHECK: OpDecorate [[var1:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var1]] Binding 0 +; CHECK: OpDecorate [[var2:%\w+]] DescriptorSet 0 +; CHECK: OpDecorate [[var2]] Binding 1 +; CHECK: OpTypeStruct +; CHECK: [[struct_type:%\w+]] = OpTypeStruct +; CHECK: [[ptr_type:%\w+]] = OpTypePointer Uniform [[struct_type]] +; CHECK: [[var1]] = OpVariable [[ptr_type]] Uniform +; CHECK: [[var2]] = OpVariable [[ptr_type]] Uniform +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var1]] %uint_0 %uint_0 %uint_0 +; CHECK: OpLoad %v4float [[ac1]] +; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var2]] %uint_0 %uint_0 %uint_0 +; CHECK: OpLoad %v4float [[ac2]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpDecorate %buffers DescriptorSet 0 + OpDecorate %buffers Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %_runtimearr_S ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable + OpDecorate %type_StructuredBuffer_S BufferBlock + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %S = OpTypeStruct %v4float +%_runtimearr_S = OpTypeRuntimeArray %S +%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_arr_type_StructuredBuffer_S_uint_2 = OpTypeArray %type_StructuredBuffer_S %uint_2 +%_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 = OpTypePointer Uniform %_arr_type_StructuredBuffer_S_uint_2 +%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S + %void = OpTypeVoid + %19 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %buffers = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 Uniform + %main = OpFunction %void None %19 + %21 = OpLabel + %22 = OpAccessChain %_ptr_Uniform_v4float %buffers %uint_0 %uint_0 %uint_0 %uint_0 + %23 = OpLoad %v4float %22 + %24 = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_S %buffers %uint_1 + %25 = OpAccessChain %_ptr_Uniform_v4float %24 %uint_0 %uint_0 %uint_0 + %26 = OpLoad %v4float %25 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, NameNewVariables) { + // Checks that if the original variable has a name, then the new variables + // will have a name derived from that name. + const std::string text = R"( +; CHECK: OpName [[var1:%\w+]] "SSBO[0]" +; CHECK: OpName [[var2:%\w+]] "SSBO[1]" +; CHECK: OpDecorate [[var1]] DescriptorSet 0 +; CHECK: OpDecorate [[var1]] Binding 0 +; CHECK: OpDecorate [[var2]] DescriptorSet 0 +; CHECK: OpDecorate [[var2]] Binding 1 +; CHECK: OpTypeStruct +; CHECK: [[struct_type:%\w+]] = OpTypeStruct +; CHECK: [[ptr_type:%\w+]] = OpTypePointer Uniform [[struct_type]] +; CHECK: [[var1]] = OpVariable [[ptr_type]] Uniform +; CHECK: [[var2]] = OpVariable [[ptr_type]] Uniform +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var1]] %uint_0 %uint_0 %uint_0 +; CHECK: OpLoad %v4float [[ac1]] +; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[var2]] %uint_0 %uint_0 %uint_0 +; CHECK: OpLoad %v4float [[ac2]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + OpName %buffers "SSBO" + OpDecorate %buffers DescriptorSet 0 + OpDecorate %buffers Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %_runtimearr_S ArrayStride 16 + OpMemberDecorate %type_StructuredBuffer_S 0 Offset 0 + OpMemberDecorate %type_StructuredBuffer_S 0 NonWritable + OpDecorate %type_StructuredBuffer_S BufferBlock + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %S = OpTypeStruct %v4float +%_runtimearr_S = OpTypeRuntimeArray %S +%type_StructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_arr_type_StructuredBuffer_S_uint_2 = OpTypeArray %type_StructuredBuffer_S %uint_2 +%_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 = OpTypePointer Uniform %_arr_type_StructuredBuffer_S_uint_2 +%_ptr_Uniform_type_StructuredBuffer_S = OpTypePointer Uniform %type_StructuredBuffer_S + %void = OpTypeVoid + %19 = OpTypeFunction %void +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %buffers = OpVariable %_ptr_Uniform__arr_type_StructuredBuffer_S_uint_2 Uniform + %main = OpFunction %void None %19 + %21 = OpLabel + %22 = OpAccessChain %_ptr_Uniform_v4float %buffers %uint_0 %uint_0 %uint_0 %uint_0 + %23 = OpLoad %v4float %22 + %24 = OpAccessChain %_ptr_Uniform_type_StructuredBuffer_S %buffers %uint_1 + %25 = OpAccessChain %_ptr_Uniform_v4float %24 %uint_0 %uint_0 %uint_0 + %26 = OpLoad %v4float %25 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, DontExpandCBuffers) { + // Checks that constant buffers are not expanded. + // Constant buffers are represented as global structures, but they should not + // be replaced with new variables for their elements. + /* + cbuffer MyCbuffer : register(b1) { + float2 a; + float2 b; + }; + float main() : A { + return a.x + b.y; + } + */ + const std::string text = R"( +; CHECK: OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0 %int_0 +; CHECK: OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_1 %int_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %out_var_A + OpSource HLSL 600 + OpName %type_MyCbuffer "type.MyCbuffer" + OpMemberName %type_MyCbuffer 0 "a" + OpMemberName %type_MyCbuffer 1 "b" + OpName %MyCbuffer "MyCbuffer" + OpName %out_var_A "out.var.A" + OpName %main "main" + OpDecorate %out_var_A Location 0 + OpDecorate %MyCbuffer DescriptorSet 0 + OpDecorate %MyCbuffer Binding 1 + OpMemberDecorate %type_MyCbuffer 0 Offset 0 + OpMemberDecorate %type_MyCbuffer 1 Offset 8 + OpDecorate %type_MyCbuffer Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%type_MyCbuffer = OpTypeStruct %v2float %v2float +%_ptr_Uniform_type_MyCbuffer = OpTypePointer Uniform %type_MyCbuffer +%_ptr_Output_float = OpTypePointer Output %float + %void = OpTypeVoid + %13 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %MyCbuffer = OpVariable %_ptr_Uniform_type_MyCbuffer Uniform + %out_var_A = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %13 + %15 = OpLabel + %16 = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_0 %int_0 + %17 = OpLoad %float %16 + %18 = OpAccessChain %_ptr_Uniform_float %MyCbuffer %int_1 %int_1 + %19 = OpLoad %float %18 + %20 = OpFAdd %float %17 %19 + OpStore %out_var_A %20 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, DontExpandStructuredBuffers) { + // Checks that structured buffers are not expanded. + // Structured buffers are represented as global structures, that have one + // member which is a runtime array. + /* + struct S { + float2 a; + float2 b; + }; + RWStructuredBuffer sb; + float main() : A { + return sb[0].a.x + sb[0].b.x; + } + */ + const std::string text = R"( +; CHECK: OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_0 %int_0 +; CHECK: OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_1 %int_0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %out_var_A + OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" + OpName %S "S" + OpMemberName %S 0 "a" + OpMemberName %S 1 "b" + OpName %sb "sb" + OpName %out_var_A "out.var.A" + OpName %main "main" + OpDecorate %out_var_A Location 0 + OpDecorate %sb DescriptorSet 0 + OpDecorate %sb Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 8 + OpDecorate %_runtimearr_S ArrayStride 16 + OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_S BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %S = OpTypeStruct %v2float %v2float +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S +%_ptr_Output_float = OpTypePointer Output %float + %void = OpTypeVoid + %17 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %sb = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform + %out_var_A = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %17 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_0 %int_0 + %21 = OpLoad %float %20 + %22 = OpAccessChain %_ptr_Uniform_float %sb %int_0 %uint_0 %int_1 %int_0 + %23 = OpLoad %float %22 + %24 = OpFAdd %float %21 %23 + OpStore %out_var_A %24 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, StructureArrayNames) { + // Checks that names are properly generated for multi-dimension arrays and + // structure members. + const std::string checks = R"( +; CHECK: OpName %globalS_0__0__t_0_ "globalS[0][0].t[0]" +; CHECK: OpName %globalS_0__0__s_0_ "globalS[0][0].s[0]" +; CHECK: OpName %globalS_1__1__t_1_ "globalS[1][1].t[1]" +; CHECK: OpName %globalS_1__1__s_1_ "globalS[1][1].s[1]" + )"; + + const std::string text = checks + GetStructureArrayTestSpirv(); + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, StructureArrayBindings) { + // Checks that flattening structures and arrays results in correct binding + // numbers. + const std::string checks = R"( +; CHECK: OpDecorate %globalS_0__0__t_0_ Binding 0 +; CHECK: OpDecorate %globalS_0__0__s_0_ Binding 2 +; CHECK: OpDecorate %globalS_1__1__t_1_ Binding 13 +; CHECK: OpDecorate %globalS_1__1__s_1_ Binding 15 + )"; + + const std::string text = checks + GetStructureArrayTestSpirv(); + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, StructureArrayReplacements) { + // Checks that all access chains indexing into structures and/or arrays are + // replaced with direct access to replacement variables. + const std::string checks = R"( +; CHECK-NOT: OpAccessChain +; CHECK: OpLoad %type_2d_image %globalS_0__0__t_0_ +; CHECK: OpLoad %type_sampler %globalS_0__0__s_0_ +; CHECK: OpLoad %type_2d_image %globalS_1__1__t_1_ +; CHECK: OpLoad %type_sampler %globalS_1__1__s_1_ + )"; + + const std::string text = checks + GetStructureArrayTestSpirv(); + SinglePassRunAndMatch(text, true); +} + +TEST_F(DescriptorScalarReplacementTest, ResourceStructAsFunctionParam) { + // Checks that a mix of OpAccessChain, OpLoad, and OpCompositeExtract patterns + // can be properly replaced with replacement variables. + // This pattern can be seen when a global structure of resources is passed to + // a function. + + /* High-level source: + // globalS[0].t[0] binding: 0 (used) + // globalS[0].t[1] binding: 1 (used) + // globalS[0].tt[0].s[0] binding: 2 + // globalS[0].tt[0].s[1] binding: 3 (used) + // globalS[0].tt[0].s[2] binding: 4 + // globalS[0].tt[1].s[0] binding: 5 + // globalS[0].tt[1].s[1] binding: 6 + // globalS[0].tt[1].s[2] binding: 7 (used) + // globalS[1].t[0] binding: 8 (used) + // globalS[1].t[1] binding: 9 (used) + // globalS[1].tt[0].s[0] binding: 10 + // globalS[1].tt[0].s[1] binding: 11 (used) + // globalS[1].tt[0].s[2] binding: 12 + // globalS[1].tt[1].s[0] binding: 13 + // globalS[1].tt[1].s[1] binding: 14 + // globalS[1].tt[1].s[2] binding: 15 (used) + + struct T { + SamplerState s[3]; + }; + + struct S { + Texture2D t[2]; + T tt[2]; + }; + + float4 tex2D(S x, float2 v) { + return x.t[0].Sample(x.tt[0].s[1], v) + x.t[1].Sample(x.tt[1].s[2], v); + } + + S globalS[2]; + + float4 main() : SV_Target { + return tex2D(globalS[0], float2(0,0)) + tex2D(globalS[1], float2(0,0)) ; + } + */ + const std::string shader = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_Target + OpExecutionMode %main OriginUpperLeft + OpName %S "S" + OpMemberName %S 0 "t" + OpMemberName %S 1 "tt" + OpName %type_2d_image "type.2d.image" + OpName %T "T" + OpMemberName %T 0 "s" + OpName %type_sampler "type.sampler" + OpName %globalS "globalS" + OpName %out_var_SV_Target "out.var.SV_Target" + OpName %main "main" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %out_var_SV_Target Location 0 + OpDecorate %globalS DescriptorSet 0 + OpDecorate %globalS Binding 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %v2float = OpTypeVector %float 2 + %14 = OpConstantComposite %v2float %float_0 %float_0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_arr_type_2d_image_uint_2 = OpTypeArray %type_2d_image %uint_2 + %uint_3 = OpConstant %uint 3 +%type_sampler = OpTypeSampler +%_arr_type_sampler_uint_3 = OpTypeArray %type_sampler %uint_3 + %T = OpTypeStruct %_arr_type_sampler_uint_3 +%_arr_T_uint_2 = OpTypeArray %T %uint_2 + %S = OpTypeStruct %_arr_type_2d_image_uint_2 %_arr_T_uint_2 +%_arr_S_uint_2 = OpTypeArray %S %uint_2 +%_ptr_UniformConstant__arr_S_uint_2 = OpTypePointer UniformConstant %_arr_S_uint_2 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_UniformConstant_S = OpTypePointer UniformConstant %S +%type_sampled_image = OpTypeSampledImage %type_2d_image + %globalS = OpVariable %_ptr_UniformConstant__arr_S_uint_2 UniformConstant +%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %27 + %29 = OpLabel + %30 = OpAccessChain %_ptr_UniformConstant_S %globalS %int_0 + %31 = OpLoad %S %30 + %32 = OpCompositeExtract %_arr_type_2d_image_uint_2 %31 0 + %33 = OpCompositeExtract %type_2d_image %32 0 + %34 = OpCompositeExtract %type_2d_image %32 1 + %35 = OpCompositeExtract %_arr_T_uint_2 %31 1 + %36 = OpCompositeExtract %T %35 0 + %37 = OpCompositeExtract %_arr_type_sampler_uint_3 %36 0 + %38 = OpCompositeExtract %type_sampler %37 1 + %39 = OpCompositeExtract %T %35 1 + %40 = OpCompositeExtract %_arr_type_sampler_uint_3 %39 0 + %41 = OpCompositeExtract %type_sampler %40 2 + %42 = OpSampledImage %type_sampled_image %33 %38 + %43 = OpImageSampleImplicitLod %v4float %42 %14 None + %44 = OpSampledImage %type_sampled_image %34 %41 + %45 = OpImageSampleImplicitLod %v4float %44 %14 None + %46 = OpFAdd %v4float %43 %45 + %47 = OpAccessChain %_ptr_UniformConstant_S %globalS %int_1 + %48 = OpLoad %S %47 + %49 = OpCompositeExtract %_arr_type_2d_image_uint_2 %48 0 + %50 = OpCompositeExtract %type_2d_image %49 0 + %51 = OpCompositeExtract %type_2d_image %49 1 + %52 = OpCompositeExtract %_arr_T_uint_2 %48 1 + %53 = OpCompositeExtract %T %52 0 + %54 = OpCompositeExtract %_arr_type_sampler_uint_3 %53 0 + %55 = OpCompositeExtract %type_sampler %54 1 + %56 = OpCompositeExtract %T %52 1 + %57 = OpCompositeExtract %_arr_type_sampler_uint_3 %56 0 + %58 = OpCompositeExtract %type_sampler %57 2 + %59 = OpSampledImage %type_sampled_image %50 %55 + %60 = OpImageSampleImplicitLod %v4float %59 %14 None + %61 = OpSampledImage %type_sampled_image %51 %58 + %62 = OpImageSampleImplicitLod %v4float %61 %14 None + %63 = OpFAdd %v4float %60 %62 + %64 = OpFAdd %v4float %46 %63 + OpStore %out_var_SV_Target %64 + OpReturn + OpFunctionEnd +)"; + + const std::string checks = R"( +; CHECK: OpName %globalS_0__t_0_ "globalS[0].t[0]" +; CHECK: OpName %globalS_0__t_1_ "globalS[0].t[1]" +; CHECK: OpName %globalS_1__t_0_ "globalS[1].t[0]" +; CHECK: OpName %globalS_1__t_1_ "globalS[1].t[1]" +; CHECK: OpName %globalS_0__tt_0__s_1_ "globalS[0].tt[0].s[1]" +; CHECK: OpName %globalS_0__tt_1__s_2_ "globalS[0].tt[1].s[2]" +; CHECK: OpName %globalS_1__tt_0__s_1_ "globalS[1].tt[0].s[1]" +; CHECK: OpName %globalS_1__tt_1__s_2_ "globalS[1].tt[1].s[2]" +; CHECK: OpDecorate %globalS_0__t_0_ Binding 0 +; CHECK: OpDecorate %globalS_0__t_1_ Binding 1 +; CHECK: OpDecorate %globalS_1__t_0_ Binding 8 +; CHECK: OpDecorate %globalS_1__t_1_ Binding 9 +; CHECK: OpDecorate %globalS_0__tt_0__s_1_ Binding 3 +; CHECK: OpDecorate %globalS_0__tt_1__s_2_ Binding 7 +; CHECK: OpDecorate %globalS_1__tt_0__s_1_ Binding 11 +; CHECK: OpDecorate %globalS_1__tt_1__s_2_ Binding 15 + +; CHECK: %globalS_0__t_0_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +; CHECK: %globalS_0__t_1_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +; CHECK: %globalS_1__t_0_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +; CHECK: %globalS_1__t_1_ = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +; CHECK: %globalS_0__tt_0__s_1_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +; CHECK: %globalS_0__tt_1__s_2_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +; CHECK: %globalS_1__tt_0__s_1_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +; CHECK: %globalS_1__tt_1__s_2_ = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + +; CHECK: [[img_1:%\w+]] = OpLoad %type_2d_image %globalS_0__t_0_ +; CHECK: [[img_2:%\w+]] = OpLoad %type_2d_image %globalS_0__t_1_ +; CHECK: [[sampler_1:%\w+]] = OpLoad %type_sampler %globalS_0__tt_0__s_1_ +; CHECK: [[sampler_2:%\w+]] = OpLoad %type_sampler %globalS_0__tt_1__s_2_ + +; CHECK: [[sampled_img_1:%\w+]] = OpSampledImage %type_sampled_image [[img_1]] [[sampler_1]] +; CHECK: [[sample_1:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_1]] +; CHECK: [[sampled_img_2:%\w+]] = OpSampledImage %type_sampled_image [[img_2]] [[sampler_2]] +; CHECK: [[sample_2:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_2]] +; CHECK: OpFAdd %v4float [[sample_1]] [[sample_2]] + +; CHECK: [[img_3:%\w+]] = OpLoad %type_2d_image %globalS_1__t_0_ +; CHECK: [[img_4:%\w+]] = OpLoad %type_2d_image %globalS_1__t_1_ +; CHECK: [[sampler_3:%\w+]] = OpLoad %type_sampler %globalS_1__tt_0__s_1_ +; CHECK: [[sampler_4:%\w+]] = OpLoad %type_sampler %globalS_1__tt_1__s_2_ + +; CHECK: [[sampled_img_3:%\w+]] = OpSampledImage %type_sampled_image [[img_3]] [[sampler_3]] +; CHECK: [[sample_3:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_3]] +; CHECK: [[sampled_img_4:%\w+]] = OpSampledImage %type_sampled_image [[img_4]] [[sampler_4]] +; CHECK: [[sample_4:%\w+]] = OpImageSampleImplicitLod %v4float [[sampled_img_4]] +; CHECK: OpFAdd %v4float [[sample_3]] [[sample_4]] +)"; + + SinglePassRunAndMatch(checks + shader, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/CMakeLists.txt b/third_party/spirv-tools/test/opt/dominator_tree/CMakeLists.txt new file mode 100644 index 0000000..813d628 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/CMakeLists.txt @@ -0,0 +1,31 @@ +# Copyright (c) 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +add_spvtools_unittest(TARGET dominator_analysis + SRCS ../function_utils.h + common_dominators.cpp + generated.cpp + nested_ifs.cpp + nested_ifs_post.cpp + nested_loops.cpp + nested_loops_with_unreachables.cpp + post.cpp + simple.cpp + switch_case_fallthrough.cpp + unreachable_for.cpp + unreachable_for_post.cpp + LIBS SPIRV-Tools-opt + PCH_FILE pch_test_opt_dom +) diff --git a/third_party/spirv-tools/test/opt/dominator_tree/common_dominators.cpp b/third_party/spirv-tools/test/opt/dominator_tree/common_dominators.cpp new file mode 100644 index 0000000..dfa03e9 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/common_dominators.cpp @@ -0,0 +1,151 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +using CommonDominatorsTest = ::testing::Test; + +const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %3 %4 None +OpBranch %5 +%5 = OpLabel +OpBranchConditional %true %3 %4 +%4 = OpLabel +OpBranch %2 +%3 = OpLabel +OpSelectionMerge %6 None +OpBranchConditional %true %7 %8 +%7 = OpLabel +OpBranch %6 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +OpBranch %6 +%6 = OpLabel +OpBranch %10 +%11 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + +BasicBlock* GetBlock(uint32_t id, std::unique_ptr& context) { + return context->get_instr_block(context->get_def_use_mgr()->GetDef(id)); +} + +TEST(CommonDominatorsTest, SameBlock) { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, context); + + DominatorAnalysis* analysis = + context->GetDominatorAnalysis(&*context->module()->begin()); + + for (auto& block : *context->module()->begin()) { + EXPECT_EQ(&block, analysis->CommonDominator(&block, &block)); + } +} + +TEST(CommonDominatorsTest, ParentAndChild) { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, context); + + DominatorAnalysis* analysis = + context->GetDominatorAnalysis(&*context->module()->begin()); + + EXPECT_EQ( + GetBlock(1u, context), + analysis->CommonDominator(GetBlock(1u, context), GetBlock(2u, context))); + EXPECT_EQ( + GetBlock(2u, context), + analysis->CommonDominator(GetBlock(2u, context), GetBlock(5u, context))); + EXPECT_EQ( + GetBlock(1u, context), + analysis->CommonDominator(GetBlock(1u, context), GetBlock(5u, context))); +} + +TEST(CommonDominatorsTest, BranchSplit) { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, context); + + DominatorAnalysis* analysis = + context->GetDominatorAnalysis(&*context->module()->begin()); + + EXPECT_EQ( + GetBlock(3u, context), + analysis->CommonDominator(GetBlock(7u, context), GetBlock(8u, context))); + EXPECT_EQ( + GetBlock(3u, context), + analysis->CommonDominator(GetBlock(7u, context), GetBlock(9u, context))); +} + +TEST(CommonDominatorsTest, LoopContinueAndMerge) { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, context); + + DominatorAnalysis* analysis = + context->GetDominatorAnalysis(&*context->module()->begin()); + + EXPECT_EQ( + GetBlock(5u, context), + analysis->CommonDominator(GetBlock(3u, context), GetBlock(4u, context))); +} + +TEST(CommonDominatorsTest, NoCommonDominator) { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, context); + + DominatorAnalysis* analysis = + context->GetDominatorAnalysis(&*context->module()->begin()); + + EXPECT_EQ(nullptr, analysis->CommonDominator(GetBlock(10u, context), + GetBlock(11u, context))); + EXPECT_EQ(nullptr, analysis->CommonDominator(GetBlock(11u, context), + GetBlock(6u, context))); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/generated.cpp b/third_party/spirv-tools/test/opt/dominator_tree/generated.cpp new file mode 100644 index 0000000..534f770 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/generated.cpp @@ -0,0 +1,1020 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/iterator.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +// Check that x dominates y, and +// if x != y then +// x strictly dominates y and +// y does not dominate x and +// y does not strictly dominate x +// if x == x then +// x does not strictly dominate itself +void check_dominance(const DominatorAnalysisBase& dom_tree, const Function* fn, + uint32_t x, uint32_t y) { + SCOPED_TRACE("Check dominance properties for Basic Block " + + std::to_string(x) + " and " + std::to_string(y)); + EXPECT_TRUE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, x), + spvtest::GetBasicBlock(fn, y))); + EXPECT_TRUE(dom_tree.Dominates(x, y)); + if (x == y) { + EXPECT_FALSE(dom_tree.StrictlyDominates(x, x)); + } else { + EXPECT_TRUE(dom_tree.StrictlyDominates(x, y)); + EXPECT_FALSE(dom_tree.Dominates(y, x)); + EXPECT_FALSE(dom_tree.StrictlyDominates(y, x)); + } +} + +// Check that x does not dominates y and vise versa +void check_no_dominance(const DominatorAnalysisBase& dom_tree, + const Function* fn, uint32_t x, uint32_t y) { + SCOPED_TRACE("Check no domination for Basic Block " + std::to_string(x) + + " and " + std::to_string(y)); + EXPECT_FALSE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, x), + spvtest::GetBasicBlock(fn, y))); + EXPECT_FALSE(dom_tree.Dominates(x, y)); + EXPECT_FALSE(dom_tree.StrictlyDominates(spvtest::GetBasicBlock(fn, x), + spvtest::GetBasicBlock(fn, y))); + EXPECT_FALSE(dom_tree.StrictlyDominates(x, y)); + + EXPECT_FALSE(dom_tree.Dominates(spvtest::GetBasicBlock(fn, y), + spvtest::GetBasicBlock(fn, x))); + EXPECT_FALSE(dom_tree.Dominates(y, x)); + EXPECT_FALSE(dom_tree.StrictlyDominates(spvtest::GetBasicBlock(fn, y), + spvtest::GetBasicBlock(fn, x))); + EXPECT_FALSE(dom_tree.StrictlyDominates(y, x)); +} + +TEST_F(PassClassTest, DominatorSimpleCFG) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstant %5 0 + %7 = OpConstantFalse %4 + %8 = OpConstantTrue %4 + %9 = OpConstant %5 1 + %1 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSwitch %6 %12 1 %13 + %12 = OpLabel + OpBranch %14 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranchConditional %8 %11 %15 + %15 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* fn = spvtest::GetFunction(module, 1); + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + // Test normal dominator tree + { + DominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block()); + EXPECT_TRUE( + dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id())); + + // (strict) dominance checks + for (uint32_t id : {10, 11, 12, 13, 14, 15}) + check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 10, 11); + check_dominance(dom_tree, fn, 10, 12); + check_dominance(dom_tree, fn, 10, 13); + check_dominance(dom_tree, fn, 10, 14); + check_dominance(dom_tree, fn, 10, 15); + + check_dominance(dom_tree, fn, 11, 12); + check_dominance(dom_tree, fn, 11, 13); + check_dominance(dom_tree, fn, 11, 14); + check_dominance(dom_tree, fn, 11, 15); + + check_dominance(dom_tree, fn, 14, 15); + + check_no_dominance(dom_tree, fn, 12, 13); + check_no_dominance(dom_tree, fn, 12, 14); + check_no_dominance(dom_tree, fn, 13, 14); + + // check with some invalid inputs + EXPECT_FALSE(dom_tree.Dominates(nullptr, entry)); + EXPECT_FALSE(dom_tree.Dominates(entry, nullptr)); + EXPECT_FALSE(dom_tree.Dominates(static_cast(nullptr), + static_cast(nullptr))); + EXPECT_FALSE(dom_tree.Dominates(10, 1)); + EXPECT_FALSE(dom_tree.Dominates(1, 10)); + EXPECT_FALSE(dom_tree.Dominates(1, 1)); + + EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, entry)); + EXPECT_FALSE(dom_tree.StrictlyDominates(entry, nullptr)); + EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, nullptr)); + EXPECT_FALSE(dom_tree.StrictlyDominates(10, 1)); + EXPECT_FALSE(dom_tree.StrictlyDominates(1, 10)); + EXPECT_FALSE(dom_tree.StrictlyDominates(1, 1)); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr); + EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block()); + EXPECT_EQ(dom_tree.ImmediateDominator(nullptr), nullptr); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 10)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)), + spvtest::GetBasicBlock(fn, 14)); + } + + // Test post dominator tree + { + PostDominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block()); + EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 15)); + + // (strict) dominance checks + for (uint32_t id : {10, 11, 12, 13, 14, 15}) + check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 14, 10); + check_dominance(dom_tree, fn, 14, 11); + check_dominance(dom_tree, fn, 14, 12); + check_dominance(dom_tree, fn, 14, 13); + + check_dominance(dom_tree, fn, 15, 10); + check_dominance(dom_tree, fn, 15, 11); + check_dominance(dom_tree, fn, 15, 12); + check_dominance(dom_tree, fn, 15, 13); + check_dominance(dom_tree, fn, 15, 14); + + check_no_dominance(dom_tree, fn, 13, 12); + check_no_dominance(dom_tree, fn, 12, 11); + check_no_dominance(dom_tree, fn, 13, 11); + + // check with some invalid inputs + EXPECT_FALSE(dom_tree.Dominates(nullptr, entry)); + EXPECT_FALSE(dom_tree.Dominates(entry, nullptr)); + EXPECT_FALSE(dom_tree.Dominates(static_cast(nullptr), + static_cast(nullptr))); + EXPECT_FALSE(dom_tree.Dominates(10, 1)); + EXPECT_FALSE(dom_tree.Dominates(1, 10)); + EXPECT_FALSE(dom_tree.Dominates(1, 1)); + + EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, entry)); + EXPECT_FALSE(dom_tree.StrictlyDominates(entry, nullptr)); + EXPECT_FALSE(dom_tree.StrictlyDominates(nullptr, nullptr)); + EXPECT_FALSE(dom_tree.StrictlyDominates(10, 1)); + EXPECT_FALSE(dom_tree.StrictlyDominates(1, 10)); + EXPECT_FALSE(dom_tree.StrictlyDominates(1, 1)); + + EXPECT_EQ(dom_tree.ImmediateDominator(nullptr), nullptr); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 14)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + spvtest::GetBasicBlock(fn, 14)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)), + spvtest::GetBasicBlock(fn, 14)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)), + spvtest::GetBasicBlock(fn, 15)); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)), + cfg.pseudo_exit_block()); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr); + } +} + +TEST_F(PassClassTest, DominatorIrreducibleCFG) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstantFalse %4 + %7 = OpConstantTrue %4 + %1 = OpFunction %2 None %3 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranchConditional %7 %10 %11 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %10 %12 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* fn = spvtest::GetFunction(module, 1); + + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 8); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + // Check normal dominator tree + { + DominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block()); + EXPECT_TRUE( + dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id())); + + // (strict) dominance checks + for (uint32_t id : {8, 9, 10, 11, 12}) + check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 8, 9); + check_dominance(dom_tree, fn, 8, 10); + check_dominance(dom_tree, fn, 8, 11); + check_dominance(dom_tree, fn, 8, 12); + + check_dominance(dom_tree, fn, 9, 10); + check_dominance(dom_tree, fn, 9, 11); + check_dominance(dom_tree, fn, 9, 12); + + check_dominance(dom_tree, fn, 11, 12); + + check_no_dominance(dom_tree, fn, 10, 11); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr); + EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block()); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)), + spvtest::GetBasicBlock(fn, 8)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)), + spvtest::GetBasicBlock(fn, 9)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 9)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + spvtest::GetBasicBlock(fn, 11)); + } + + // Check post dominator tree + { + PostDominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block()); + EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12)); + + // (strict) dominance checks + for (uint32_t id : {8, 9, 10, 11, 12}) + check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 12, 8); + check_dominance(dom_tree, fn, 12, 10); + check_dominance(dom_tree, fn, 12, 11); + check_dominance(dom_tree, fn, 12, 12); + + check_dominance(dom_tree, fn, 11, 8); + check_dominance(dom_tree, fn, 11, 9); + check_dominance(dom_tree, fn, 11, 10); + + check_dominance(dom_tree, fn, 9, 8); + + EXPECT_EQ(dom_tree.ImmediateDominator(entry), + spvtest::GetBasicBlock(fn, 9)); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 12)); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + cfg.pseudo_exit_block()); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr); + } +} + +TEST_F(PassClassTest, DominatorLoopToSelf) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstant %5 0 + %7 = OpConstantFalse %4 + %8 = OpConstantTrue %4 + %9 = OpConstant %5 1 + %1 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSwitch %6 %12 1 %11 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* fn = spvtest::GetFunction(module, 1); + + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + // Check normal dominator tree + { + DominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block()); + EXPECT_TRUE( + dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id())); + + // (strict) dominance checks + for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 10, 11); + check_dominance(dom_tree, fn, 10, 12); + check_dominance(dom_tree, fn, 11, 12); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr); + EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block()); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 10)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + spvtest::GetBasicBlock(fn, 11)); + + std::array node_order = {{10, 11, 12}}; + { + // Test dominator tree iteration order. + DominatorTree::iterator node_it = dom_tree.GetDomTree().begin(); + DominatorTree::iterator node_end = dom_tree.GetDomTree().end(); + for (uint32_t id : node_order) { + EXPECT_NE(node_it, node_end); + EXPECT_EQ(node_it->id(), id); + node_it++; + } + EXPECT_EQ(node_it, node_end); + } + { + // Same as above, but with const iterators. + DominatorTree::const_iterator node_it = dom_tree.GetDomTree().cbegin(); + DominatorTree::const_iterator node_end = dom_tree.GetDomTree().cend(); + for (uint32_t id : node_order) { + EXPECT_NE(node_it, node_end); + EXPECT_EQ(node_it->id(), id); + node_it++; + } + EXPECT_EQ(node_it, node_end); + } + { + // Test dominator tree iteration order. + DominatorTree::post_iterator node_it = dom_tree.GetDomTree().post_begin(); + DominatorTree::post_iterator node_end = dom_tree.GetDomTree().post_end(); + for (uint32_t id : make_range(node_order.rbegin(), node_order.rend())) { + EXPECT_NE(node_it, node_end); + EXPECT_EQ(node_it->id(), id); + node_it++; + } + EXPECT_EQ(node_it, node_end); + } + { + // Same as above, but with const iterators. + DominatorTree::const_post_iterator node_it = + dom_tree.GetDomTree().post_cbegin(); + DominatorTree::const_post_iterator node_end = + dom_tree.GetDomTree().post_cend(); + for (uint32_t id : make_range(node_order.rbegin(), node_order.rend())) { + EXPECT_NE(node_it, node_end); + EXPECT_EQ(node_it->id(), id); + node_it++; + } + EXPECT_EQ(node_it, node_end); + } + } + + // Check post dominator tree + { + PostDominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block()); + EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12)); + + // (strict) dominance checks + for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 12, 10); + check_dominance(dom_tree, fn, 12, 11); + check_dominance(dom_tree, fn, 12, 12); + + EXPECT_EQ(dom_tree.ImmediateDominator(entry), + spvtest::GetBasicBlock(fn, 11)); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 12)); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + cfg.pseudo_exit_block()); + + std::array node_order = {{12, 11, 10}}; + { + // Test dominator tree iteration order. + DominatorTree::iterator node_it = tree.begin(); + DominatorTree::iterator node_end = tree.end(); + for (uint32_t id : node_order) { + EXPECT_NE(node_it, node_end); + EXPECT_EQ(node_it->id(), id); + node_it++; + } + EXPECT_EQ(node_it, node_end); + } + { + // Same as above, but with const iterators. + DominatorTree::const_iterator node_it = tree.cbegin(); + DominatorTree::const_iterator node_end = tree.cend(); + for (uint32_t id : node_order) { + EXPECT_NE(node_it, node_end); + EXPECT_EQ(node_it->id(), id); + node_it++; + } + EXPECT_EQ(node_it, node_end); + } + { + // Test dominator tree iteration order. + DominatorTree::post_iterator node_it = dom_tree.GetDomTree().post_begin(); + DominatorTree::post_iterator node_end = dom_tree.GetDomTree().post_end(); + for (uint32_t id : make_range(node_order.rbegin(), node_order.rend())) { + EXPECT_NE(node_it, node_end); + EXPECT_EQ(node_it->id(), id); + node_it++; + } + EXPECT_EQ(node_it, node_end); + } + { + // Same as above, but with const iterators. + DominatorTree::const_post_iterator node_it = + dom_tree.GetDomTree().post_cbegin(); + DominatorTree::const_post_iterator node_end = + dom_tree.GetDomTree().post_cend(); + for (uint32_t id : make_range(node_order.rbegin(), node_order.rend())) { + EXPECT_NE(node_it, node_end); + EXPECT_EQ(node_it->id(), id); + node_it++; + } + EXPECT_EQ(node_it, node_end); + } + } +} + +TEST_F(PassClassTest, DominatorUnreachableInLoop) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstant %5 0 + %7 = OpConstantFalse %4 + %8 = OpConstantTrue %4 + %9 = OpConstant %5 1 + %1 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSwitch %6 %12 1 %13 + %12 = OpLabel + OpBranch %14 + %13 = OpLabel + OpUnreachable + %14 = OpLabel + OpBranchConditional %8 %11 %15 + %15 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* fn = spvtest::GetFunction(module, 1); + + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + // Check normal dominator tree + { + DominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block()); + EXPECT_TRUE( + dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id())); + + // (strict) dominance checks + for (uint32_t id : {10, 11, 12, 13, 14, 15}) + check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 10, 11); + check_dominance(dom_tree, fn, 10, 13); + check_dominance(dom_tree, fn, 10, 12); + check_dominance(dom_tree, fn, 10, 14); + check_dominance(dom_tree, fn, 10, 15); + + check_dominance(dom_tree, fn, 11, 12); + check_dominance(dom_tree, fn, 11, 13); + check_dominance(dom_tree, fn, 11, 14); + check_dominance(dom_tree, fn, 11, 15); + + check_dominance(dom_tree, fn, 12, 14); + check_dominance(dom_tree, fn, 12, 15); + + check_dominance(dom_tree, fn, 14, 15); + + check_no_dominance(dom_tree, fn, 13, 12); + check_no_dominance(dom_tree, fn, 13, 14); + check_no_dominance(dom_tree, fn, 13, 15); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr); + EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block()); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 10)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)), + spvtest::GetBasicBlock(fn, 12)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)), + spvtest::GetBasicBlock(fn, 14)); + } + + // Check post dominator tree. + { + PostDominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // (strict) dominance checks. + for (uint32_t id : {10, 11, 12, 13, 14, 15}) + check_dominance(dom_tree, fn, id, id); + + check_no_dominance(dom_tree, fn, 15, 10); + check_no_dominance(dom_tree, fn, 15, 11); + check_no_dominance(dom_tree, fn, 15, 12); + check_no_dominance(dom_tree, fn, 15, 13); + check_no_dominance(dom_tree, fn, 15, 14); + + check_dominance(dom_tree, fn, 14, 12); + + check_no_dominance(dom_tree, fn, 13, 10); + check_no_dominance(dom_tree, fn, 13, 11); + check_no_dominance(dom_tree, fn, 13, 12); + check_no_dominance(dom_tree, fn, 13, 14); + check_no_dominance(dom_tree, fn, 13, 15); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + spvtest::GetBasicBlock(fn, 14)); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 15)), + cfg.pseudo_exit_block()); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)), + cfg.pseudo_exit_block()); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 14)), + cfg.pseudo_exit_block()); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + cfg.pseudo_exit_block()); + } +} + +TEST_F(PassClassTest, DominatorInfinitLoop) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstant %5 0 + %7 = OpConstantFalse %4 + %8 = OpConstantTrue %4 + %9 = OpConstant %5 1 + %1 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSwitch %6 %12 1 %13 + %12 = OpLabel + OpReturn + %13 = OpLabel + OpBranch %13 + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* fn = spvtest::GetFunction(module, 1); + + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 10); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + // Check normal dominator tree + { + DominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block()); + EXPECT_TRUE( + dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id())); + + // (strict) dominance checks + for (uint32_t id : {10, 11, 12, 13}) check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 10, 11); + check_dominance(dom_tree, fn, 10, 12); + check_dominance(dom_tree, fn, 10, 13); + + check_dominance(dom_tree, fn, 11, 12); + check_dominance(dom_tree, fn, 11, 13); + + check_no_dominance(dom_tree, fn, 13, 12); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr); + EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block()); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 10)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + spvtest::GetBasicBlock(fn, 11)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 13)), + spvtest::GetBasicBlock(fn, 11)); + } + + // Check post dominator tree + { + PostDominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block()); + EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 12)); + + // (strict) dominance checks + for (uint32_t id : {10, 11, 12}) check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 12, 11); + check_dominance(dom_tree, fn, 12, 10); + + // 13 should be completely out of tree as it's unreachable from exit nodes + check_no_dominance(dom_tree, fn, 12, 13); + check_no_dominance(dom_tree, fn, 11, 13); + check_no_dominance(dom_tree, fn, 10, 13); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 12)), + cfg.pseudo_exit_block()); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)), + spvtest::GetBasicBlock(fn, 11)); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 11)), + spvtest::GetBasicBlock(fn, 12)); + } +} + +TEST_F(PassClassTest, DominatorUnreachableFromEntry) { + const std::string text = R"( + OpCapability Addresses + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %1 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeBool + %5 = OpTypeInt 32 0 + %6 = OpConstantFalse %4 + %7 = OpConstantTrue %4 + %1 = OpFunction %2 None %3 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + %10 = OpLabel + OpBranch %9 + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* fn = spvtest::GetFunction(module, 1); + + const BasicBlock* entry = spvtest::GetBasicBlock(fn, 8); + EXPECT_EQ(entry, fn->entry().get()) + << "The entry node is not the expected one"; + + // Check dominator tree + { + DominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block()); + EXPECT_TRUE( + dom_tree.Dominates(cfg.pseudo_entry_block()->id(), entry->id())); + + // (strict) dominance checks + for (uint32_t id : {8, 9}) check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 8, 9); + + check_no_dominance(dom_tree, fn, 10, 8); + check_no_dominance(dom_tree, fn, 10, 9); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_entry_block()), nullptr); + EXPECT_EQ(dom_tree.ImmediateDominator(entry), cfg.pseudo_entry_block()); + + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)), + spvtest::GetBasicBlock(fn, 8)); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)), + nullptr); + } + + // Check post dominator tree + { + PostDominatorAnalysis dom_tree; + const CFG& cfg = *context->cfg(); + dom_tree.InitializeTree(cfg, fn); + + // Inspect the actual tree + DominatorTree& tree = dom_tree.GetDomTree(); + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_exit_block()); + EXPECT_TRUE(dom_tree.Dominates(cfg.pseudo_exit_block()->id(), 9)); + + // (strict) dominance checks + for (uint32_t id : {8, 9, 10}) check_dominance(dom_tree, fn, id, id); + + check_dominance(dom_tree, fn, 9, 8); + check_dominance(dom_tree, fn, 9, 10); + + EXPECT_EQ(dom_tree.ImmediateDominator(entry), + spvtest::GetBasicBlock(fn, 9)); + + EXPECT_EQ(dom_tree.ImmediateDominator(cfg.pseudo_exit_block()), nullptr); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 9)), + cfg.pseudo_exit_block()); + EXPECT_EQ(dom_tree.ImmediateDominator(spvtest::GetBasicBlock(fn, 10)), + spvtest::GetBasicBlock(fn, 9)); + } +} + +TEST_F(PassClassTest, DominationForInstructions) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpConstant %6 37 + %10 = OpConstant %6 3 + %13 = OpConstant %6 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpIAdd %6 %9 %10 + %15 = OpISub %6 %12 %13 + OpSelectionMerge %18 None + OpBranchConditional %8 %16 %17 + %16 = OpLabel + %20 = OpISub %6 %12 %13 + OpBranch %18 + %17 = OpLabel + %21 = OpISub %6 %12 %13 + OpBranch %18 + %18 = OpLabel + %22 = OpISub %6 %12 %13 + OpReturn + OpFunctionEnd + )"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_0, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, context->module()) << "Assembling failed for shader:\n" + << text << std::endl; + + { + const DominatorAnalysis* dominator_analysis = context->GetDominatorAnalysis( + spvtest::GetFunction(context->module(), 4)); + + EXPECT_TRUE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(12), + context->get_def_use_mgr()->GetDef(15))); + EXPECT_FALSE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), + context->get_def_use_mgr()->GetDef(12))); + EXPECT_TRUE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(5), + context->get_def_use_mgr()->GetDef(12))); + EXPECT_FALSE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(12), + context->get_def_use_mgr()->GetDef(5))); + EXPECT_TRUE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), + context->get_def_use_mgr()->GetDef(16))); + EXPECT_TRUE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), + context->get_def_use_mgr()->GetDef(21))); + EXPECT_TRUE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), + context->get_def_use_mgr()->GetDef(18))); + EXPECT_TRUE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), + context->get_def_use_mgr()->GetDef(22))); + EXPECT_FALSE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(20), + context->get_def_use_mgr()->GetDef(22))); + EXPECT_FALSE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(21), + context->get_def_use_mgr()->GetDef(22))); + EXPECT_TRUE( + dominator_analysis->Dominates(context->get_def_use_mgr()->GetDef(15), + context->get_def_use_mgr()->GetDef(15))); + } + { + const PostDominatorAnalysis* post_dominator_analysis = + context->GetPostDominatorAnalysis( + spvtest::GetFunction(context->module(), 4)); + + EXPECT_TRUE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(15), + context->get_def_use_mgr()->GetDef(12))); + EXPECT_FALSE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(12), + context->get_def_use_mgr()->GetDef(15))); + EXPECT_TRUE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(12), + context->get_def_use_mgr()->GetDef(5))); + EXPECT_FALSE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(5), + context->get_def_use_mgr()->GetDef(12))); + EXPECT_FALSE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(16), + context->get_def_use_mgr()->GetDef(15))); + EXPECT_FALSE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(21), + context->get_def_use_mgr()->GetDef(15))); + EXPECT_TRUE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(18), + context->get_def_use_mgr()->GetDef(15))); + EXPECT_TRUE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(22), + context->get_def_use_mgr()->GetDef(15))); + EXPECT_TRUE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(22), + context->get_def_use_mgr()->GetDef(20))); + EXPECT_TRUE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(22), + context->get_def_use_mgr()->GetDef(21))); + EXPECT_TRUE(post_dominator_analysis->Dominates( + context->get_def_use_mgr()->GetDef(15), + context->get_def_use_mgr()->GetDef(15))); + } +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/nested_ifs.cpp b/third_party/spirv-tools/test/opt/dominator_tree/nested_ifs.cpp new file mode 100644 index 0000000..0552b75 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/nested_ifs.cpp @@ -0,0 +1,153 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 v; +void main(){ + if (true) { + if (true) { + v = vec4(1,1,1,1); + } else { + v = vec4(2,2,2,2); + } + } else { + if (true) { + v = vec4(3,3,3,3); + } else { + v = vec4(4,4,4,4); + } + } +} +*/ +TEST_F(PassClassTest, UnreachableNestedIfs) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %15 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 330 + OpName %4 "main" + OpName %15 "v" + OpDecorate %15 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %12 = OpTypeFloat 32 + %13 = OpTypeVector %12 4 + %14 = OpTypePointer Output %13 + %15 = OpVariable %14 Output + %16 = OpConstant %12 1 + %17 = OpConstantComposite %13 %16 %16 %16 %16 + %19 = OpConstant %12 2 + %20 = OpConstantComposite %13 %19 %19 %19 %19 + %24 = OpConstant %12 3 + %25 = OpConstantComposite %13 %24 %24 %24 %24 + %27 = OpConstant %12 4 + %28 = OpConstantComposite %13 %27 %27 %27 %27 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %21 + %8 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %7 %10 %18 + %10 = OpLabel + OpStore %15 %17 + OpBranch %11 + %18 = OpLabel + OpStore %15 %20 + OpBranch %11 + %11 = OpLabel + OpBranch %9 + %21 = OpLabel + OpSelectionMerge %23 None + OpBranchConditional %7 %22 %26 + %22 = OpLabel + OpStore %15 %25 + OpBranch %23 + %26 = OpLabel + OpStore %15 %28 + OpBranch %23 + %23 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + const Function* f = spvtest::GetFunction(module, 4); + + DominatorAnalysis* analysis = context->GetDominatorAnalysis(f); + + EXPECT_TRUE(analysis->Dominates(5, 8)); + EXPECT_TRUE(analysis->Dominates(5, 9)); + EXPECT_TRUE(analysis->Dominates(5, 21)); + EXPECT_TRUE(analysis->Dominates(5, 18)); + EXPECT_TRUE(analysis->Dominates(5, 10)); + EXPECT_TRUE(analysis->Dominates(5, 11)); + EXPECT_TRUE(analysis->Dominates(5, 23)); + EXPECT_TRUE(analysis->Dominates(5, 22)); + EXPECT_TRUE(analysis->Dominates(5, 26)); + EXPECT_TRUE(analysis->Dominates(8, 18)); + EXPECT_TRUE(analysis->Dominates(8, 10)); + EXPECT_TRUE(analysis->Dominates(8, 11)); + EXPECT_TRUE(analysis->Dominates(21, 23)); + EXPECT_TRUE(analysis->Dominates(21, 22)); + EXPECT_TRUE(analysis->Dominates(21, 26)); + + EXPECT_TRUE(analysis->StrictlyDominates(5, 8)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 9)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 21)); + EXPECT_TRUE(analysis->StrictlyDominates(8, 18)); + EXPECT_TRUE(analysis->StrictlyDominates(8, 10)); + EXPECT_TRUE(analysis->StrictlyDominates(8, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(21, 23)); + EXPECT_TRUE(analysis->StrictlyDominates(21, 22)); + EXPECT_TRUE(analysis->StrictlyDominates(21, 26)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/nested_ifs_post.cpp b/third_party/spirv-tools/test/opt/dominator_tree/nested_ifs_post.cpp new file mode 100644 index 0000000..ad759df --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/nested_ifs_post.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 v; +void main(){ + if (true) { + if (true) { + v = vec4(1,1,1,1); + } else { + v = vec4(2,2,2,2); + } + } else { + if (true) { + v = vec4(3,3,3,3); + } else { + v = vec4(4,4,4,4); + } + } +} +*/ +TEST_F(PassClassTest, UnreachableNestedIfs) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %15 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 330 + OpName %4 "main" + OpName %15 "v" + OpDecorate %15 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %12 = OpTypeFloat 32 + %13 = OpTypeVector %12 4 + %14 = OpTypePointer Output %13 + %15 = OpVariable %14 Output + %16 = OpConstant %12 1 + %17 = OpConstantComposite %13 %16 %16 %16 %16 + %19 = OpConstant %12 2 + %20 = OpConstantComposite %13 %19 %19 %19 %19 + %24 = OpConstant %12 3 + %25 = OpConstantComposite %13 %24 %24 %24 %24 + %27 = OpConstant %12 4 + %28 = OpConstantComposite %13 %27 %27 %27 %27 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %21 + %8 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %7 %10 %18 + %10 = OpLabel + OpStore %15 %17 + OpBranch %11 + %18 = OpLabel + OpStore %15 %20 + OpBranch %11 + %11 = OpLabel + OpBranch %9 + %21 = OpLabel + OpSelectionMerge %23 None + OpBranchConditional %7 %22 %26 + %22 = OpLabel + OpStore %15 %25 + OpBranch %23 + %26 = OpLabel + OpStore %15 %28 + OpBranch %23 + %23 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + const Function* f = spvtest::GetFunction(module, 4); + + PostDominatorAnalysis* analysis = context->GetPostDominatorAnalysis(f); + + EXPECT_TRUE(analysis->Dominates(5, 5)); + EXPECT_TRUE(analysis->Dominates(8, 8)); + EXPECT_TRUE(analysis->Dominates(9, 9)); + EXPECT_TRUE(analysis->Dominates(10, 10)); + EXPECT_TRUE(analysis->Dominates(11, 11)); + EXPECT_TRUE(analysis->Dominates(18, 18)); + EXPECT_TRUE(analysis->Dominates(21, 21)); + EXPECT_TRUE(analysis->Dominates(22, 22)); + EXPECT_TRUE(analysis->Dominates(23, 23)); + EXPECT_TRUE(analysis->Dominates(26, 26)); + EXPECT_TRUE(analysis->Dominates(9, 5)); + EXPECT_TRUE(analysis->Dominates(9, 11)); + EXPECT_TRUE(analysis->Dominates(9, 23)); + EXPECT_TRUE(analysis->Dominates(11, 10)); + EXPECT_TRUE(analysis->Dominates(11, 18)); + EXPECT_TRUE(analysis->Dominates(11, 8)); + EXPECT_TRUE(analysis->Dominates(23, 22)); + EXPECT_TRUE(analysis->Dominates(23, 26)); + EXPECT_TRUE(analysis->Dominates(23, 21)); + + EXPECT_TRUE(analysis->StrictlyDominates(9, 5)); + EXPECT_TRUE(analysis->StrictlyDominates(9, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(9, 23)); + EXPECT_TRUE(analysis->StrictlyDominates(11, 10)); + EXPECT_TRUE(analysis->StrictlyDominates(11, 18)); + EXPECT_TRUE(analysis->StrictlyDominates(11, 8)); + EXPECT_TRUE(analysis->StrictlyDominates(23, 22)); + EXPECT_TRUE(analysis->StrictlyDominates(23, 26)); + EXPECT_TRUE(analysis->StrictlyDominates(23, 21)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/nested_loops.cpp b/third_party/spirv-tools/test/opt/dominator_tree/nested_loops.cpp new file mode 100644 index 0000000..7d03937 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/nested_loops.cpp @@ -0,0 +1,433 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Generated from the following GLSL +#version 440 core +layout(location = 0) out vec4 v; +layout(location = 1) in vec4 in_val; +void main() { + for (int i = 0; i < in_val.x; ++i) { + for (int j = 0; j < in_val.y; j++) { + } + } + for (int i = 0; i < in_val.x; ++i) { + for (int j = 0; j < in_val.y; j++) { + } + if (in_val.z == in_val.w) { + break; + } + } + int i = 0; + while (i < in_val.x) { + ++i; + for (int j = 0; j < 1; j++) { + for (int k = 0; k < 1; k++) { + } + } + } + i = 0; + while (i < in_val.x) { + ++i; + if (in_val.z == in_val.w) { + continue; + } + for (int j = 0; j < 1; j++) { + for (int k = 0; k < 1; k++) { + } + if (in_val.z == in_val.w) { + break; + } + } + } + v = vec4(1,1,1,1); +} +*/ +TEST_F(PassClassTest, BasicVisitFromEntryPoint) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 %163 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %20 "in_val" + OpName %28 "j" + OpName %45 "i" + OpName %56 "j" + OpName %81 "i" + OpName %94 "j" + OpName %102 "k" + OpName %134 "j" + OpName %142 "k" + OpName %163 "v" + OpDecorate %20 Location 1 + OpDecorate %163 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpTypeFloat 32 + %18 = OpTypeVector %16 4 + %19 = OpTypePointer Input %18 + %20 = OpVariable %19 Input + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 0 + %23 = OpTypePointer Input %16 + %26 = OpTypeBool + %36 = OpConstant %21 1 + %41 = OpConstant %6 1 + %69 = OpConstant %21 2 + %72 = OpConstant %21 3 + %162 = OpTypePointer Output %18 + %163 = OpVariable %162 Output + %164 = OpConstant %16 1 + %165 = OpConstantComposite %18 %164 %164 %164 %164 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %28 = OpVariable %7 Function + %45 = OpVariable %7 Function + %56 = OpVariable %7 Function + %81 = OpVariable %7 Function + %94 = OpVariable %7 Function + %102 = OpVariable %7 Function + %134 = OpVariable %7 Function + %142 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %17 = OpConvertSToF %16 %15 + %24 = OpAccessChain %23 %20 %22 + %25 = OpLoad %16 %24 + %27 = OpFOrdLessThan %26 %17 %25 + OpBranchConditional %27 %11 %12 + %11 = OpLabel + OpStore %28 %9 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %6 %28 + %35 = OpConvertSToF %16 %34 + %37 = OpAccessChain %23 %20 %36 + %38 = OpLoad %16 %37 + %39 = OpFOrdLessThan %26 %35 %38 + OpBranchConditional %39 %30 %31 + %30 = OpLabel + OpBranch %32 + %32 = OpLabel + %40 = OpLoad %6 %28 + %42 = OpIAdd %6 %40 %41 + OpStore %28 %42 + OpBranch %29 + %31 = OpLabel + OpBranch %13 + %13 = OpLabel + %43 = OpLoad %6 %8 + %44 = OpIAdd %6 %43 %41 + OpStore %8 %44 + OpBranch %10 + %12 = OpLabel + OpStore %45 %9 + OpBranch %46 + %46 = OpLabel + OpLoopMerge %48 %49 None + OpBranch %50 + %50 = OpLabel + %51 = OpLoad %6 %45 + %52 = OpConvertSToF %16 %51 + %53 = OpAccessChain %23 %20 %22 + %54 = OpLoad %16 %53 + %55 = OpFOrdLessThan %26 %52 %54 + OpBranchConditional %55 %47 %48 + %47 = OpLabel + OpStore %56 %9 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %6 %56 + %63 = OpConvertSToF %16 %62 + %64 = OpAccessChain %23 %20 %36 + %65 = OpLoad %16 %64 + %66 = OpFOrdLessThan %26 %63 %65 + OpBranchConditional %66 %58 %59 + %58 = OpLabel + OpBranch %60 + %60 = OpLabel + %67 = OpLoad %6 %56 + %68 = OpIAdd %6 %67 %41 + OpStore %56 %68 + OpBranch %57 + %59 = OpLabel + %70 = OpAccessChain %23 %20 %69 + %71 = OpLoad %16 %70 + %73 = OpAccessChain %23 %20 %72 + %74 = OpLoad %16 %73 + %75 = OpFOrdEqual %26 %71 %74 + OpSelectionMerge %77 None + OpBranchConditional %75 %76 %77 + %76 = OpLabel + OpBranch %48 + %77 = OpLabel + OpBranch %49 + %49 = OpLabel + %79 = OpLoad %6 %45 + %80 = OpIAdd %6 %79 %41 + OpStore %45 %80 + OpBranch %46 + %48 = OpLabel + OpStore %81 %9 + OpBranch %82 + %82 = OpLabel + OpLoopMerge %84 %85 None + OpBranch %86 + %86 = OpLabel + %87 = OpLoad %6 %81 + %88 = OpConvertSToF %16 %87 + %89 = OpAccessChain %23 %20 %22 + %90 = OpLoad %16 %89 + %91 = OpFOrdLessThan %26 %88 %90 + OpBranchConditional %91 %83 %84 + %83 = OpLabel + %92 = OpLoad %6 %81 + %93 = OpIAdd %6 %92 %41 + OpStore %81 %93 + OpStore %94 %9 + OpBranch %95 + %95 = OpLabel + OpLoopMerge %97 %98 None + OpBranch %99 + %99 = OpLabel + %100 = OpLoad %6 %94 + %101 = OpSLessThan %26 %100 %41 + OpBranchConditional %101 %96 %97 + %96 = OpLabel + OpStore %102 %9 + OpBranch %103 + %103 = OpLabel + OpLoopMerge %105 %106 None + OpBranch %107 + %107 = OpLabel + %108 = OpLoad %6 %102 + %109 = OpSLessThan %26 %108 %41 + OpBranchConditional %109 %104 %105 + %104 = OpLabel + OpBranch %106 + %106 = OpLabel + %110 = OpLoad %6 %102 + %111 = OpIAdd %6 %110 %41 + OpStore %102 %111 + OpBranch %103 + %105 = OpLabel + OpBranch %98 + %98 = OpLabel + %112 = OpLoad %6 %94 + %113 = OpIAdd %6 %112 %41 + OpStore %94 %113 + OpBranch %95 + %97 = OpLabel + OpBranch %85 + %85 = OpLabel + OpBranch %82 + %84 = OpLabel + OpStore %81 %9 + OpBranch %114 + %114 = OpLabel + OpLoopMerge %116 %117 None + OpBranch %118 + %118 = OpLabel + %119 = OpLoad %6 %81 + %120 = OpConvertSToF %16 %119 + %121 = OpAccessChain %23 %20 %22 + %122 = OpLoad %16 %121 + %123 = OpFOrdLessThan %26 %120 %122 + OpBranchConditional %123 %115 %116 + %115 = OpLabel + %124 = OpLoad %6 %81 + %125 = OpIAdd %6 %124 %41 + OpStore %81 %125 + %126 = OpAccessChain %23 %20 %69 + %127 = OpLoad %16 %126 + %128 = OpAccessChain %23 %20 %72 + %129 = OpLoad %16 %128 + %130 = OpFOrdEqual %26 %127 %129 + OpSelectionMerge %132 None + OpBranchConditional %130 %131 %132 + %131 = OpLabel + OpBranch %117 + %132 = OpLabel + OpStore %134 %9 + OpBranch %135 + %135 = OpLabel + OpLoopMerge %137 %138 None + OpBranch %139 + %139 = OpLabel + %140 = OpLoad %6 %134 + %141 = OpSLessThan %26 %140 %41 + OpBranchConditional %141 %136 %137 + %136 = OpLabel + OpStore %142 %9 + OpBranch %143 + %143 = OpLabel + OpLoopMerge %145 %146 None + OpBranch %147 + %147 = OpLabel + %148 = OpLoad %6 %142 + %149 = OpSLessThan %26 %148 %41 + OpBranchConditional %149 %144 %145 + %144 = OpLabel + OpBranch %146 + %146 = OpLabel + %150 = OpLoad %6 %142 + %151 = OpIAdd %6 %150 %41 + OpStore %142 %151 + OpBranch %143 + %145 = OpLabel + %152 = OpAccessChain %23 %20 %69 + %153 = OpLoad %16 %152 + %154 = OpAccessChain %23 %20 %72 + %155 = OpLoad %16 %154 + %156 = OpFOrdEqual %26 %153 %155 + OpSelectionMerge %158 None + OpBranchConditional %156 %157 %158 + %157 = OpLabel + OpBranch %137 + %158 = OpLabel + OpBranch %138 + %138 = OpLabel + %160 = OpLoad %6 %134 + %161 = OpIAdd %6 %160 %41 + OpStore %134 %161 + OpBranch %135 + %137 = OpLabel + OpBranch %117 + %117 = OpLabel + OpBranch %114 + %116 = OpLabel + OpStore %163 %165 + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + const Function* f = spvtest::GetFunction(module, 4); + DominatorAnalysis* analysis = context->GetDominatorAnalysis(f); + + EXPECT_TRUE(analysis->Dominates(5, 10)); + EXPECT_TRUE(analysis->Dominates(5, 46)); + EXPECT_TRUE(analysis->Dominates(5, 82)); + EXPECT_TRUE(analysis->Dominates(5, 114)); + EXPECT_TRUE(analysis->Dominates(5, 116)); + + EXPECT_TRUE(analysis->Dominates(10, 14)); + EXPECT_TRUE(analysis->Dominates(10, 11)); + EXPECT_TRUE(analysis->Dominates(10, 29)); + EXPECT_TRUE(analysis->Dominates(10, 33)); + EXPECT_TRUE(analysis->Dominates(10, 30)); + EXPECT_TRUE(analysis->Dominates(10, 32)); + EXPECT_TRUE(analysis->Dominates(10, 31)); + EXPECT_TRUE(analysis->Dominates(10, 13)); + EXPECT_TRUE(analysis->Dominates(10, 12)); + + EXPECT_TRUE(analysis->Dominates(12, 46)); + + EXPECT_TRUE(analysis->Dominates(46, 50)); + EXPECT_TRUE(analysis->Dominates(46, 47)); + EXPECT_TRUE(analysis->Dominates(46, 57)); + EXPECT_TRUE(analysis->Dominates(46, 61)); + EXPECT_TRUE(analysis->Dominates(46, 58)); + EXPECT_TRUE(analysis->Dominates(46, 60)); + EXPECT_TRUE(analysis->Dominates(46, 59)); + EXPECT_TRUE(analysis->Dominates(46, 77)); + EXPECT_TRUE(analysis->Dominates(46, 49)); + EXPECT_TRUE(analysis->Dominates(46, 76)); + EXPECT_TRUE(analysis->Dominates(46, 48)); + + EXPECT_TRUE(analysis->Dominates(48, 82)); + + EXPECT_TRUE(analysis->Dominates(82, 86)); + EXPECT_TRUE(analysis->Dominates(82, 83)); + EXPECT_TRUE(analysis->Dominates(82, 95)); + EXPECT_TRUE(analysis->Dominates(82, 99)); + EXPECT_TRUE(analysis->Dominates(82, 96)); + EXPECT_TRUE(analysis->Dominates(82, 103)); + EXPECT_TRUE(analysis->Dominates(82, 107)); + EXPECT_TRUE(analysis->Dominates(82, 104)); + EXPECT_TRUE(analysis->Dominates(82, 106)); + EXPECT_TRUE(analysis->Dominates(82, 105)); + EXPECT_TRUE(analysis->Dominates(82, 98)); + EXPECT_TRUE(analysis->Dominates(82, 97)); + EXPECT_TRUE(analysis->Dominates(82, 85)); + EXPECT_TRUE(analysis->Dominates(82, 84)); + + EXPECT_TRUE(analysis->Dominates(84, 114)); + + EXPECT_TRUE(analysis->Dominates(114, 118)); + EXPECT_TRUE(analysis->Dominates(114, 116)); + EXPECT_TRUE(analysis->Dominates(114, 115)); + EXPECT_TRUE(analysis->Dominates(114, 132)); + EXPECT_TRUE(analysis->Dominates(114, 135)); + EXPECT_TRUE(analysis->Dominates(114, 139)); + EXPECT_TRUE(analysis->Dominates(114, 136)); + EXPECT_TRUE(analysis->Dominates(114, 143)); + EXPECT_TRUE(analysis->Dominates(114, 147)); + EXPECT_TRUE(analysis->Dominates(114, 144)); + EXPECT_TRUE(analysis->Dominates(114, 146)); + EXPECT_TRUE(analysis->Dominates(114, 145)); + EXPECT_TRUE(analysis->Dominates(114, 158)); + EXPECT_TRUE(analysis->Dominates(114, 138)); + EXPECT_TRUE(analysis->Dominates(114, 137)); + EXPECT_TRUE(analysis->Dominates(114, 131)); + EXPECT_TRUE(analysis->Dominates(114, 117)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/nested_loops_with_unreachables.cpp b/third_party/spirv-tools/test/opt/dominator_tree/nested_loops_with_unreachables.cpp new file mode 100644 index 0000000..e87e8dd --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/nested_loops_with_unreachables.cpp @@ -0,0 +1,848 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; + +using PassClassTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL +#version 440 core +layout(location = 0) out vec4 v; +layout(location = 1) in vec4 in_val; +void main() { + for (int i = 0; i < in_val.x; ++i) { + for (int j = 0; j < in_val.y; j++) { + } + } + for (int i = 0; i < in_val.x; ++i) { + for (int j = 0; j < in_val.y; j++) { + } + break; + } + int i = 0; + while (i < in_val.x) { + ++i; + for (int j = 0; j < 1; j++) { + for (int k = 0; k < 1; k++) { + } + break; + } + } + i = 0; + while (i < in_val.x) { + ++i; + continue; + for (int j = 0; j < 1; j++) { + for (int k = 0; k < 1; k++) { + } + break; + } + } + v = vec4(1,1,1,1); +} +*/ +TEST_F(PassClassTest, BasicVisitFromEntryPoint) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 %141 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %20 "in_val" + OpName %28 "j" + OpName %45 "i" + OpName %56 "j" + OpName %72 "i" + OpName %85 "j" + OpName %93 "k" + OpName %119 "j" + OpName %127 "k" + OpName %141 "v" + OpDecorate %20 Location 1 + OpDecorate %141 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpTypeFloat 32 + %18 = OpTypeVector %16 4 + %19 = OpTypePointer Input %18 + %20 = OpVariable %19 Input + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 0 + %23 = OpTypePointer Input %16 + %26 = OpTypeBool + %36 = OpConstant %21 1 + %41 = OpConstant %6 1 + %140 = OpTypePointer Output %18 + %141 = OpVariable %140 Output + %142 = OpConstant %16 1 + %143 = OpConstantComposite %18 %142 %142 %142 %142 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %28 = OpVariable %7 Function + %45 = OpVariable %7 Function + %56 = OpVariable %7 Function + %72 = OpVariable %7 Function + %85 = OpVariable %7 Function + %93 = OpVariable %7 Function + %119 = OpVariable %7 Function + %127 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %17 = OpConvertSToF %16 %15 + %24 = OpAccessChain %23 %20 %22 + %25 = OpLoad %16 %24 + %27 = OpFOrdLessThan %26 %17 %25 + OpBranchConditional %27 %11 %12 + %11 = OpLabel + OpStore %28 %9 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %6 %28 + %35 = OpConvertSToF %16 %34 + %37 = OpAccessChain %23 %20 %36 + %38 = OpLoad %16 %37 + %39 = OpFOrdLessThan %26 %35 %38 + OpBranchConditional %39 %30 %31 + %30 = OpLabel + OpBranch %32 + %32 = OpLabel + %40 = OpLoad %6 %28 + %42 = OpIAdd %6 %40 %41 + OpStore %28 %42 + OpBranch %29 + %31 = OpLabel + OpBranch %13 + %13 = OpLabel + %43 = OpLoad %6 %8 + %44 = OpIAdd %6 %43 %41 + OpStore %8 %44 + OpBranch %10 + %12 = OpLabel + OpStore %45 %9 + OpBranch %46 + %46 = OpLabel + OpLoopMerge %48 %49 None + OpBranch %50 + %50 = OpLabel + %51 = OpLoad %6 %45 + %52 = OpConvertSToF %16 %51 + %53 = OpAccessChain %23 %20 %22 + %54 = OpLoad %16 %53 + %55 = OpFOrdLessThan %26 %52 %54 + OpBranchConditional %55 %47 %48 + %47 = OpLabel + OpStore %56 %9 + OpBranch %57 + %57 = OpLabel + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %62 = OpLoad %6 %56 + %63 = OpConvertSToF %16 %62 + %64 = OpAccessChain %23 %20 %36 + %65 = OpLoad %16 %64 + %66 = OpFOrdLessThan %26 %63 %65 + OpBranchConditional %66 %58 %59 + %58 = OpLabel + OpBranch %60 + %60 = OpLabel + %67 = OpLoad %6 %56 + %68 = OpIAdd %6 %67 %41 + OpStore %56 %68 + OpBranch %57 + %59 = OpLabel + OpBranch %48 + %49 = OpLabel + %70 = OpLoad %6 %45 + %71 = OpIAdd %6 %70 %41 + OpStore %45 %71 + OpBranch %46 + %48 = OpLabel + OpStore %72 %9 + OpBranch %73 + %73 = OpLabel + OpLoopMerge %75 %76 None + OpBranch %77 + %77 = OpLabel + %78 = OpLoad %6 %72 + %79 = OpConvertSToF %16 %78 + %80 = OpAccessChain %23 %20 %22 + %81 = OpLoad %16 %80 + %82 = OpFOrdLessThan %26 %79 %81 + OpBranchConditional %82 %74 %75 + %74 = OpLabel + %83 = OpLoad %6 %72 + %84 = OpIAdd %6 %83 %41 + OpStore %72 %84 + OpStore %85 %9 + OpBranch %86 + %86 = OpLabel + OpLoopMerge %88 %89 None + OpBranch %90 + %90 = OpLabel + %91 = OpLoad %6 %85 + %92 = OpSLessThan %26 %91 %41 + OpBranchConditional %92 %87 %88 + %87 = OpLabel + OpStore %93 %9 + OpBranch %94 + %94 = OpLabel + OpLoopMerge %96 %97 None + OpBranch %98 + %98 = OpLabel + %99 = OpLoad %6 %93 + %100 = OpSLessThan %26 %99 %41 + OpBranchConditional %100 %95 %96 + %95 = OpLabel + OpBranch %97 + %97 = OpLabel + %101 = OpLoad %6 %93 + %102 = OpIAdd %6 %101 %41 + OpStore %93 %102 + OpBranch %94 + %96 = OpLabel + OpBranch %88 + %89 = OpLabel + %104 = OpLoad %6 %85 + %105 = OpIAdd %6 %104 %41 + OpStore %85 %105 + OpBranch %86 + %88 = OpLabel + OpBranch %76 + %76 = OpLabel + OpBranch %73 + %75 = OpLabel + OpStore %72 %9 + OpBranch %106 + %106 = OpLabel + OpLoopMerge %108 %109 None + OpBranch %110 + %110 = OpLabel + %111 = OpLoad %6 %72 + %112 = OpConvertSToF %16 %111 + %113 = OpAccessChain %23 %20 %22 + %114 = OpLoad %16 %113 + %115 = OpFOrdLessThan %26 %112 %114 + OpBranchConditional %115 %107 %108 + %107 = OpLabel + %116 = OpLoad %6 %72 + %117 = OpIAdd %6 %116 %41 + OpStore %72 %117 + OpBranch %109 + %109 = OpLabel + OpBranch %106 + %108 = OpLabel + OpStore %141 %143 + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + const Function* f = spvtest::GetFunction(module, 4); + DominatorAnalysis* analysis = context->GetDominatorAnalysis(f); + + EXPECT_TRUE(analysis->Dominates(5, 10)); + EXPECT_TRUE(analysis->Dominates(5, 14)); + EXPECT_TRUE(analysis->Dominates(5, 11)); + EXPECT_TRUE(analysis->Dominates(5, 29)); + EXPECT_TRUE(analysis->Dominates(5, 33)); + EXPECT_TRUE(analysis->Dominates(5, 30)); + EXPECT_TRUE(analysis->Dominates(5, 32)); + EXPECT_TRUE(analysis->Dominates(5, 31)); + EXPECT_TRUE(analysis->Dominates(5, 13)); + EXPECT_TRUE(analysis->Dominates(5, 12)); + EXPECT_TRUE(analysis->Dominates(5, 46)); + EXPECT_TRUE(analysis->Dominates(5, 50)); + EXPECT_TRUE(analysis->Dominates(5, 47)); + EXPECT_TRUE(analysis->Dominates(5, 57)); + EXPECT_TRUE(analysis->Dominates(5, 61)); + EXPECT_TRUE(analysis->Dominates(5, 59)); + EXPECT_TRUE(analysis->Dominates(5, 58)); + EXPECT_TRUE(analysis->Dominates(5, 60)); + EXPECT_TRUE(analysis->Dominates(5, 48)); + EXPECT_TRUE(analysis->Dominates(5, 73)); + EXPECT_TRUE(analysis->Dominates(5, 77)); + EXPECT_TRUE(analysis->Dominates(5, 75)); + EXPECT_TRUE(analysis->Dominates(5, 106)); + EXPECT_TRUE(analysis->Dominates(5, 110)); + EXPECT_TRUE(analysis->Dominates(5, 107)); + EXPECT_TRUE(analysis->Dominates(5, 108)); + EXPECT_TRUE(analysis->Dominates(5, 109)); + EXPECT_TRUE(analysis->Dominates(5, 74)); + EXPECT_TRUE(analysis->Dominates(5, 86)); + EXPECT_TRUE(analysis->Dominates(5, 90)); + EXPECT_TRUE(analysis->Dominates(5, 87)); + EXPECT_TRUE(analysis->Dominates(5, 94)); + EXPECT_TRUE(analysis->Dominates(5, 98)); + EXPECT_TRUE(analysis->Dominates(5, 95)); + EXPECT_TRUE(analysis->Dominates(5, 97)); + EXPECT_TRUE(analysis->Dominates(5, 96)); + EXPECT_TRUE(analysis->Dominates(5, 88)); + EXPECT_TRUE(analysis->Dominates(5, 76)); + + EXPECT_TRUE(analysis->Dominates(10, 14)); + EXPECT_TRUE(analysis->Dominates(10, 11)); + EXPECT_TRUE(analysis->Dominates(10, 29)); + EXPECT_TRUE(analysis->Dominates(10, 33)); + EXPECT_TRUE(analysis->Dominates(10, 30)); + EXPECT_TRUE(analysis->Dominates(10, 32)); + EXPECT_TRUE(analysis->Dominates(10, 31)); + EXPECT_TRUE(analysis->Dominates(10, 13)); + EXPECT_TRUE(analysis->Dominates(10, 12)); + EXPECT_TRUE(analysis->Dominates(10, 46)); + EXPECT_TRUE(analysis->Dominates(10, 50)); + EXPECT_TRUE(analysis->Dominates(10, 47)); + EXPECT_TRUE(analysis->Dominates(10, 57)); + EXPECT_TRUE(analysis->Dominates(10, 61)); + EXPECT_TRUE(analysis->Dominates(10, 59)); + EXPECT_TRUE(analysis->Dominates(10, 58)); + EXPECT_TRUE(analysis->Dominates(10, 60)); + EXPECT_TRUE(analysis->Dominates(10, 48)); + EXPECT_TRUE(analysis->Dominates(10, 73)); + EXPECT_TRUE(analysis->Dominates(10, 77)); + EXPECT_TRUE(analysis->Dominates(10, 75)); + EXPECT_TRUE(analysis->Dominates(10, 106)); + EXPECT_TRUE(analysis->Dominates(10, 110)); + EXPECT_TRUE(analysis->Dominates(10, 107)); + EXPECT_TRUE(analysis->Dominates(10, 108)); + EXPECT_TRUE(analysis->Dominates(10, 109)); + EXPECT_TRUE(analysis->Dominates(10, 74)); + EXPECT_TRUE(analysis->Dominates(10, 86)); + EXPECT_TRUE(analysis->Dominates(10, 90)); + EXPECT_TRUE(analysis->Dominates(10, 87)); + EXPECT_TRUE(analysis->Dominates(10, 94)); + EXPECT_TRUE(analysis->Dominates(10, 98)); + EXPECT_TRUE(analysis->Dominates(10, 95)); + EXPECT_TRUE(analysis->Dominates(10, 97)); + EXPECT_TRUE(analysis->Dominates(10, 96)); + EXPECT_TRUE(analysis->Dominates(10, 88)); + EXPECT_TRUE(analysis->Dominates(10, 76)); + + EXPECT_TRUE(analysis->Dominates(14, 11)); + EXPECT_TRUE(analysis->Dominates(14, 29)); + EXPECT_TRUE(analysis->Dominates(14, 33)); + EXPECT_TRUE(analysis->Dominates(14, 30)); + EXPECT_TRUE(analysis->Dominates(14, 32)); + EXPECT_TRUE(analysis->Dominates(14, 31)); + + EXPECT_TRUE(analysis->Dominates(11, 29)); + EXPECT_TRUE(analysis->Dominates(11, 33)); + EXPECT_TRUE(analysis->Dominates(11, 30)); + EXPECT_TRUE(analysis->Dominates(11, 32)); + EXPECT_TRUE(analysis->Dominates(11, 31)); + + EXPECT_TRUE(analysis->Dominates(29, 33)); + EXPECT_TRUE(analysis->Dominates(29, 30)); + EXPECT_TRUE(analysis->Dominates(29, 32)); + EXPECT_TRUE(analysis->Dominates(29, 31)); + + EXPECT_TRUE(analysis->Dominates(33, 30)); + + EXPECT_TRUE(analysis->Dominates(12, 46)); + EXPECT_TRUE(analysis->Dominates(12, 50)); + EXPECT_TRUE(analysis->Dominates(12, 47)); + EXPECT_TRUE(analysis->Dominates(12, 57)); + EXPECT_TRUE(analysis->Dominates(12, 61)); + EXPECT_TRUE(analysis->Dominates(12, 59)); + EXPECT_TRUE(analysis->Dominates(12, 58)); + EXPECT_TRUE(analysis->Dominates(12, 60)); + EXPECT_TRUE(analysis->Dominates(12, 48)); + EXPECT_TRUE(analysis->Dominates(12, 73)); + EXPECT_TRUE(analysis->Dominates(12, 77)); + EXPECT_TRUE(analysis->Dominates(12, 75)); + EXPECT_TRUE(analysis->Dominates(12, 106)); + EXPECT_TRUE(analysis->Dominates(12, 110)); + EXPECT_TRUE(analysis->Dominates(12, 107)); + EXPECT_TRUE(analysis->Dominates(12, 108)); + EXPECT_TRUE(analysis->Dominates(12, 109)); + EXPECT_TRUE(analysis->Dominates(12, 74)); + EXPECT_TRUE(analysis->Dominates(12, 86)); + EXPECT_TRUE(analysis->Dominates(12, 90)); + EXPECT_TRUE(analysis->Dominates(12, 87)); + EXPECT_TRUE(analysis->Dominates(12, 94)); + EXPECT_TRUE(analysis->Dominates(12, 98)); + EXPECT_TRUE(analysis->Dominates(12, 95)); + EXPECT_TRUE(analysis->Dominates(12, 97)); + EXPECT_TRUE(analysis->Dominates(12, 96)); + EXPECT_TRUE(analysis->Dominates(12, 88)); + EXPECT_TRUE(analysis->Dominates(12, 76)); + + EXPECT_TRUE(analysis->Dominates(46, 50)); + EXPECT_TRUE(analysis->Dominates(46, 47)); + EXPECT_TRUE(analysis->Dominates(46, 57)); + EXPECT_TRUE(analysis->Dominates(46, 61)); + EXPECT_TRUE(analysis->Dominates(46, 59)); + EXPECT_TRUE(analysis->Dominates(46, 58)); + EXPECT_TRUE(analysis->Dominates(46, 60)); + EXPECT_TRUE(analysis->Dominates(46, 48)); + EXPECT_TRUE(analysis->Dominates(46, 73)); + EXPECT_TRUE(analysis->Dominates(46, 77)); + EXPECT_TRUE(analysis->Dominates(46, 75)); + EXPECT_TRUE(analysis->Dominates(46, 106)); + EXPECT_TRUE(analysis->Dominates(46, 110)); + EXPECT_TRUE(analysis->Dominates(46, 107)); + EXPECT_TRUE(analysis->Dominates(46, 108)); + EXPECT_TRUE(analysis->Dominates(46, 109)); + EXPECT_TRUE(analysis->Dominates(46, 74)); + EXPECT_TRUE(analysis->Dominates(46, 86)); + EXPECT_TRUE(analysis->Dominates(46, 90)); + EXPECT_TRUE(analysis->Dominates(46, 87)); + EXPECT_TRUE(analysis->Dominates(46, 94)); + EXPECT_TRUE(analysis->Dominates(46, 98)); + EXPECT_TRUE(analysis->Dominates(46, 95)); + EXPECT_TRUE(analysis->Dominates(46, 97)); + EXPECT_TRUE(analysis->Dominates(46, 96)); + EXPECT_TRUE(analysis->Dominates(46, 88)); + EXPECT_TRUE(analysis->Dominates(46, 76)); + + EXPECT_TRUE(analysis->Dominates(50, 47)); + EXPECT_TRUE(analysis->Dominates(50, 57)); + EXPECT_TRUE(analysis->Dominates(50, 61)); + EXPECT_TRUE(analysis->Dominates(50, 59)); + EXPECT_TRUE(analysis->Dominates(50, 58)); + EXPECT_TRUE(analysis->Dominates(50, 60)); + + EXPECT_TRUE(analysis->Dominates(47, 57)); + EXPECT_TRUE(analysis->Dominates(47, 61)); + EXPECT_TRUE(analysis->Dominates(47, 59)); + EXPECT_TRUE(analysis->Dominates(47, 58)); + EXPECT_TRUE(analysis->Dominates(47, 60)); + + EXPECT_TRUE(analysis->Dominates(57, 61)); + EXPECT_TRUE(analysis->Dominates(57, 59)); + EXPECT_TRUE(analysis->Dominates(57, 58)); + EXPECT_TRUE(analysis->Dominates(57, 60)); + + EXPECT_TRUE(analysis->Dominates(61, 59)); + + EXPECT_TRUE(analysis->Dominates(48, 73)); + EXPECT_TRUE(analysis->Dominates(48, 77)); + EXPECT_TRUE(analysis->Dominates(48, 75)); + EXPECT_TRUE(analysis->Dominates(48, 106)); + EXPECT_TRUE(analysis->Dominates(48, 110)); + EXPECT_TRUE(analysis->Dominates(48, 107)); + EXPECT_TRUE(analysis->Dominates(48, 108)); + EXPECT_TRUE(analysis->Dominates(48, 109)); + EXPECT_TRUE(analysis->Dominates(48, 74)); + EXPECT_TRUE(analysis->Dominates(48, 86)); + EXPECT_TRUE(analysis->Dominates(48, 90)); + EXPECT_TRUE(analysis->Dominates(48, 87)); + EXPECT_TRUE(analysis->Dominates(48, 94)); + EXPECT_TRUE(analysis->Dominates(48, 98)); + EXPECT_TRUE(analysis->Dominates(48, 95)); + EXPECT_TRUE(analysis->Dominates(48, 97)); + EXPECT_TRUE(analysis->Dominates(48, 96)); + EXPECT_TRUE(analysis->Dominates(48, 88)); + EXPECT_TRUE(analysis->Dominates(48, 76)); + + EXPECT_TRUE(analysis->Dominates(73, 77)); + EXPECT_TRUE(analysis->Dominates(73, 75)); + EXPECT_TRUE(analysis->Dominates(73, 106)); + EXPECT_TRUE(analysis->Dominates(73, 110)); + EXPECT_TRUE(analysis->Dominates(73, 107)); + EXPECT_TRUE(analysis->Dominates(73, 108)); + EXPECT_TRUE(analysis->Dominates(73, 109)); + EXPECT_TRUE(analysis->Dominates(73, 74)); + EXPECT_TRUE(analysis->Dominates(73, 86)); + EXPECT_TRUE(analysis->Dominates(73, 90)); + EXPECT_TRUE(analysis->Dominates(73, 87)); + EXPECT_TRUE(analysis->Dominates(73, 94)); + EXPECT_TRUE(analysis->Dominates(73, 98)); + EXPECT_TRUE(analysis->Dominates(73, 95)); + EXPECT_TRUE(analysis->Dominates(73, 97)); + EXPECT_TRUE(analysis->Dominates(73, 96)); + EXPECT_TRUE(analysis->Dominates(73, 88)); + EXPECT_TRUE(analysis->Dominates(73, 76)); + + EXPECT_TRUE(analysis->Dominates(75, 106)); + EXPECT_TRUE(analysis->Dominates(75, 110)); + EXPECT_TRUE(analysis->Dominates(75, 107)); + EXPECT_TRUE(analysis->Dominates(75, 108)); + EXPECT_TRUE(analysis->Dominates(75, 109)); + + EXPECT_TRUE(analysis->Dominates(106, 110)); + EXPECT_TRUE(analysis->Dominates(106, 107)); + EXPECT_TRUE(analysis->Dominates(106, 108)); + EXPECT_TRUE(analysis->Dominates(106, 109)); + + EXPECT_TRUE(analysis->Dominates(110, 107)); + + EXPECT_TRUE(analysis->Dominates(77, 74)); + EXPECT_TRUE(analysis->Dominates(77, 86)); + EXPECT_TRUE(analysis->Dominates(77, 90)); + EXPECT_TRUE(analysis->Dominates(77, 87)); + EXPECT_TRUE(analysis->Dominates(77, 94)); + EXPECT_TRUE(analysis->Dominates(77, 98)); + EXPECT_TRUE(analysis->Dominates(77, 95)); + EXPECT_TRUE(analysis->Dominates(77, 97)); + EXPECT_TRUE(analysis->Dominates(77, 96)); + EXPECT_TRUE(analysis->Dominates(77, 88)); + + EXPECT_TRUE(analysis->Dominates(74, 86)); + EXPECT_TRUE(analysis->Dominates(74, 90)); + EXPECT_TRUE(analysis->Dominates(74, 87)); + EXPECT_TRUE(analysis->Dominates(74, 94)); + EXPECT_TRUE(analysis->Dominates(74, 98)); + EXPECT_TRUE(analysis->Dominates(74, 95)); + EXPECT_TRUE(analysis->Dominates(74, 97)); + EXPECT_TRUE(analysis->Dominates(74, 96)); + EXPECT_TRUE(analysis->Dominates(74, 88)); + + EXPECT_TRUE(analysis->Dominates(86, 90)); + EXPECT_TRUE(analysis->Dominates(86, 87)); + EXPECT_TRUE(analysis->Dominates(86, 94)); + EXPECT_TRUE(analysis->Dominates(86, 98)); + EXPECT_TRUE(analysis->Dominates(86, 95)); + EXPECT_TRUE(analysis->Dominates(86, 97)); + EXPECT_TRUE(analysis->Dominates(86, 96)); + EXPECT_TRUE(analysis->Dominates(86, 88)); + + EXPECT_TRUE(analysis->Dominates(90, 87)); + EXPECT_TRUE(analysis->Dominates(90, 94)); + EXPECT_TRUE(analysis->Dominates(90, 98)); + EXPECT_TRUE(analysis->Dominates(90, 95)); + EXPECT_TRUE(analysis->Dominates(90, 97)); + EXPECT_TRUE(analysis->Dominates(90, 96)); + + EXPECT_TRUE(analysis->Dominates(87, 94)); + EXPECT_TRUE(analysis->Dominates(87, 98)); + EXPECT_TRUE(analysis->Dominates(87, 95)); + EXPECT_TRUE(analysis->Dominates(87, 97)); + EXPECT_TRUE(analysis->Dominates(87, 96)); + + EXPECT_TRUE(analysis->Dominates(94, 98)); + EXPECT_TRUE(analysis->Dominates(94, 95)); + EXPECT_TRUE(analysis->Dominates(94, 97)); + EXPECT_TRUE(analysis->Dominates(94, 96)); + + EXPECT_TRUE(analysis->Dominates(98, 95)); + + EXPECT_TRUE(analysis->StrictlyDominates(5, 10)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 14)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 29)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 33)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 30)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 32)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 31)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 13)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 12)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 46)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 50)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 47)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 57)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 61)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 59)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 58)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 60)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 48)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 73)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 77)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 75)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 106)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 110)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 107)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 108)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 109)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 74)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 86)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 88)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 76)); + + EXPECT_TRUE(analysis->StrictlyDominates(10, 14)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 29)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 33)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 30)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 32)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 31)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 13)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 12)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 46)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 50)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 47)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 57)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 61)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 59)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 58)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 60)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 48)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 73)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 77)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 75)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 106)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 110)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 107)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 108)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 109)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 74)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 86)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 88)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 76)); + + EXPECT_TRUE(analysis->StrictlyDominates(14, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 29)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 33)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 30)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 32)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 31)); + + EXPECT_TRUE(analysis->StrictlyDominates(11, 29)); + EXPECT_TRUE(analysis->StrictlyDominates(11, 33)); + EXPECT_TRUE(analysis->StrictlyDominates(11, 30)); + EXPECT_TRUE(analysis->StrictlyDominates(11, 32)); + EXPECT_TRUE(analysis->StrictlyDominates(11, 31)); + + EXPECT_TRUE(analysis->StrictlyDominates(29, 33)); + EXPECT_TRUE(analysis->StrictlyDominates(29, 30)); + EXPECT_TRUE(analysis->StrictlyDominates(29, 32)); + EXPECT_TRUE(analysis->StrictlyDominates(29, 31)); + + EXPECT_TRUE(analysis->StrictlyDominates(33, 30)); + + EXPECT_TRUE(analysis->StrictlyDominates(12, 46)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 50)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 47)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 57)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 61)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 59)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 58)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 60)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 48)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 73)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 77)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 75)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 106)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 110)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 107)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 108)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 109)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 74)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 86)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 88)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 76)); + + EXPECT_TRUE(analysis->StrictlyDominates(46, 50)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 47)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 57)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 61)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 59)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 58)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 60)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 48)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 73)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 77)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 75)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 106)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 110)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 107)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 108)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 109)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 74)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 86)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 88)); + EXPECT_TRUE(analysis->StrictlyDominates(46, 76)); + + EXPECT_TRUE(analysis->StrictlyDominates(50, 47)); + EXPECT_TRUE(analysis->StrictlyDominates(50, 57)); + EXPECT_TRUE(analysis->StrictlyDominates(50, 61)); + EXPECT_TRUE(analysis->StrictlyDominates(50, 59)); + EXPECT_TRUE(analysis->StrictlyDominates(50, 58)); + EXPECT_TRUE(analysis->StrictlyDominates(50, 60)); + + EXPECT_TRUE(analysis->StrictlyDominates(47, 57)); + EXPECT_TRUE(analysis->StrictlyDominates(47, 61)); + EXPECT_TRUE(analysis->StrictlyDominates(47, 59)); + EXPECT_TRUE(analysis->StrictlyDominates(47, 58)); + EXPECT_TRUE(analysis->StrictlyDominates(47, 60)); + + EXPECT_TRUE(analysis->StrictlyDominates(57, 61)); + EXPECT_TRUE(analysis->StrictlyDominates(57, 59)); + EXPECT_TRUE(analysis->StrictlyDominates(57, 58)); + EXPECT_TRUE(analysis->StrictlyDominates(57, 60)); + + EXPECT_TRUE(analysis->StrictlyDominates(61, 59)); + + EXPECT_TRUE(analysis->StrictlyDominates(48, 73)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 77)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 75)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 106)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 110)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 107)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 108)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 109)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 74)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 86)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 88)); + EXPECT_TRUE(analysis->StrictlyDominates(48, 76)); + + EXPECT_TRUE(analysis->StrictlyDominates(73, 77)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 75)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 106)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 110)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 107)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 108)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 109)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 74)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 86)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 88)); + EXPECT_TRUE(analysis->StrictlyDominates(73, 76)); + + EXPECT_TRUE(analysis->StrictlyDominates(75, 106)); + EXPECT_TRUE(analysis->StrictlyDominates(75, 110)); + EXPECT_TRUE(analysis->StrictlyDominates(75, 107)); + EXPECT_TRUE(analysis->StrictlyDominates(75, 108)); + EXPECT_TRUE(analysis->StrictlyDominates(75, 109)); + + EXPECT_TRUE(analysis->StrictlyDominates(106, 110)); + EXPECT_TRUE(analysis->StrictlyDominates(106, 107)); + EXPECT_TRUE(analysis->StrictlyDominates(106, 108)); + EXPECT_TRUE(analysis->StrictlyDominates(106, 109)); + + EXPECT_TRUE(analysis->StrictlyDominates(110, 107)); + + EXPECT_TRUE(analysis->StrictlyDominates(77, 74)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 86)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(77, 88)); + + EXPECT_TRUE(analysis->StrictlyDominates(74, 86)); + EXPECT_TRUE(analysis->StrictlyDominates(74, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(74, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(74, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(74, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(74, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(74, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(74, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(74, 88)); + + EXPECT_TRUE(analysis->StrictlyDominates(86, 90)); + EXPECT_TRUE(analysis->StrictlyDominates(86, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(86, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(86, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(86, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(86, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(86, 96)); + EXPECT_TRUE(analysis->StrictlyDominates(86, 88)); + + EXPECT_TRUE(analysis->StrictlyDominates(90, 87)); + EXPECT_TRUE(analysis->StrictlyDominates(90, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(90, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(90, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(90, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(90, 96)); + + EXPECT_TRUE(analysis->StrictlyDominates(87, 94)); + EXPECT_TRUE(analysis->StrictlyDominates(87, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(87, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(87, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(87, 96)); + + EXPECT_TRUE(analysis->StrictlyDominates(94, 98)); + EXPECT_TRUE(analysis->StrictlyDominates(94, 95)); + EXPECT_TRUE(analysis->StrictlyDominates(94, 97)); + EXPECT_TRUE(analysis->StrictlyDominates(94, 96)); + + EXPECT_TRUE(analysis->StrictlyDominates(98, 95)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/pch_test_opt_dom.cpp b/third_party/spirv-tools/test/opt/dominator_tree/pch_test_opt_dom.cpp new file mode 100644 index 0000000..a28310e --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/pch_test_opt_dom.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_test_opt_dom.h" diff --git a/third_party/spirv-tools/test/opt/dominator_tree/pch_test_opt_dom.h b/third_party/spirv-tools/test/opt/dominator_tree/pch_test_opt_dom.h new file mode 100644 index 0000000..4e8106f --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/pch_test_opt_dom.h @@ -0,0 +1,25 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "source/opt/iterator.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/tree_iterator.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" diff --git a/third_party/spirv-tools/test/opt/dominator_tree/post.cpp b/third_party/spirv-tools/test/opt/dominator_tree/post.cpp new file mode 100644 index 0000000..bb10fde --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/post.cpp @@ -0,0 +1,207 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Generated from the following GLSL +#version 440 core +layout(location = 0) out vec4 c; +layout(location = 1)in vec4 in_val; +void main(){ + if ( in_val.x < 10) { + int z = 0; + int i = 0; + for (i = 0; i < in_val.y; ++i) { + z += i; + } + c = vec4(i,i,i,i); + } else { + c = vec4(1,1,1,1); + } +} +*/ +TEST_F(PassClassTest, BasicVisitFromEntryPoint) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %43 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %9 "in_val" + OpName %22 "z" + OpName %24 "i" + OpName %43 "c" + OpDecorate %9 Location 1 + OpDecorate %43 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Input %7 + %9 = OpVariable %8 Input + %10 = OpTypeInt 32 0 + %11 = OpConstant %10 0 + %12 = OpTypePointer Input %6 + %15 = OpConstant %6 10 + %16 = OpTypeBool + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %32 = OpConstant %10 1 + %40 = OpConstant %20 1 + %42 = OpTypePointer Output %7 + %43 = OpVariable %42 Output + %54 = OpConstant %6 1 + %55 = OpConstantComposite %7 %54 %54 %54 %54 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + %17 = OpFOrdLessThan %16 %14 %15 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %53 + %18 = OpLabel + OpStore %22 %23 + OpStore %24 %23 + OpStore %24 %23 + OpBranch %25 + %25 = OpLabel + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + %30 = OpLoad %20 %24 + %31 = OpConvertSToF %6 %30 + %33 = OpAccessChain %12 %9 %32 + %34 = OpLoad %6 %33 + %35 = OpFOrdLessThan %16 %31 %34 + OpBranchConditional %35 %26 %27 + %26 = OpLabel + %36 = OpLoad %20 %24 + %37 = OpLoad %20 %22 + %38 = OpIAdd %20 %37 %36 + OpStore %22 %38 + OpBranch %28 + %28 = OpLabel + %39 = OpLoad %20 %24 + %41 = OpIAdd %20 %39 %40 + OpStore %24 %41 + OpBranch %25 + %27 = OpLabel + %44 = OpLoad %20 %24 + %45 = OpConvertSToF %6 %44 + %46 = OpLoad %20 %24 + %47 = OpConvertSToF %6 %46 + %48 = OpLoad %20 %24 + %49 = OpConvertSToF %6 %48 + %50 = OpLoad %20 %24 + %51 = OpConvertSToF %6 %50 + %52 = OpCompositeConstruct %7 %45 %47 %49 %51 + OpStore %43 %52 + OpBranch %19 + %53 = OpLabel + OpStore %43 %55 + OpBranch %19 + %19 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + const Function* f = spvtest::GetFunction(module, 4); + CFG cfg(module); + PostDominatorAnalysis* analysis = context->GetPostDominatorAnalysis(f); + + EXPECT_TRUE(analysis->Dominates(19, 18)); + EXPECT_TRUE(analysis->Dominates(19, 5)); + EXPECT_TRUE(analysis->Dominates(19, 53)); + EXPECT_TRUE(analysis->Dominates(19, 19)); + EXPECT_TRUE(analysis->Dominates(19, 25)); + EXPECT_TRUE(analysis->Dominates(19, 29)); + EXPECT_TRUE(analysis->Dominates(19, 27)); + EXPECT_TRUE(analysis->Dominates(19, 26)); + EXPECT_TRUE(analysis->Dominates(19, 28)); + + EXPECT_TRUE(analysis->Dominates(27, 18)); + EXPECT_TRUE(analysis->Dominates(27, 25)); + EXPECT_TRUE(analysis->Dominates(27, 29)); + EXPECT_TRUE(analysis->Dominates(27, 27)); + EXPECT_TRUE(analysis->Dominates(27, 26)); + EXPECT_TRUE(analysis->Dominates(27, 28)); + + EXPECT_FALSE(analysis->Dominates(27, 19)); + EXPECT_FALSE(analysis->Dominates(27, 5)); + EXPECT_FALSE(analysis->Dominates(27, 53)); + + EXPECT_FALSE(analysis->StrictlyDominates(19, 19)); + + EXPECT_TRUE(analysis->StrictlyDominates(19, 18)); + EXPECT_TRUE(analysis->StrictlyDominates(19, 5)); + EXPECT_TRUE(analysis->StrictlyDominates(19, 53)); + EXPECT_TRUE(analysis->StrictlyDominates(19, 25)); + EXPECT_TRUE(analysis->StrictlyDominates(19, 29)); + EXPECT_TRUE(analysis->StrictlyDominates(19, 27)); + EXPECT_TRUE(analysis->StrictlyDominates(19, 26)); + EXPECT_TRUE(analysis->StrictlyDominates(19, 28)); + + // These would be expected true for a normal, non post, dominator tree + EXPECT_FALSE(analysis->Dominates(5, 18)); + EXPECT_FALSE(analysis->Dominates(5, 53)); + EXPECT_FALSE(analysis->Dominates(5, 19)); + EXPECT_FALSE(analysis->Dominates(5, 25)); + EXPECT_FALSE(analysis->Dominates(5, 29)); + EXPECT_FALSE(analysis->Dominates(5, 27)); + EXPECT_FALSE(analysis->Dominates(5, 26)); + EXPECT_FALSE(analysis->Dominates(5, 28)); + + EXPECT_FALSE(analysis->StrictlyDominates(5, 18)); + EXPECT_FALSE(analysis->StrictlyDominates(5, 53)); + EXPECT_FALSE(analysis->StrictlyDominates(5, 19)); + EXPECT_FALSE(analysis->StrictlyDominates(5, 25)); + EXPECT_FALSE(analysis->StrictlyDominates(5, 29)); + EXPECT_FALSE(analysis->StrictlyDominates(5, 27)); + EXPECT_FALSE(analysis->StrictlyDominates(5, 26)); + EXPECT_FALSE(analysis->StrictlyDominates(5, 28)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/simple.cpp b/third_party/spirv-tools/test/opt/dominator_tree/simple.cpp new file mode 100644 index 0000000..d11854d --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/simple.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL +#version 440 core +layout(location = 0) out vec4 c; +layout(location = 1)in vec4 in_val; +void main(){ + if ( in_val.x < 10) { + int z = 0; + int i = 0; + for (i = 0; i < in_val.y; ++i) { + z += i; + } + c = vec4(i,i,i,i); + } else { + c = vec4(1,1,1,1); + } +} +*/ +TEST_F(PassClassTest, BasicVisitFromEntryPoint) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %43 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %9 "in_val" + OpName %22 "z" + OpName %24 "i" + OpName %43 "c" + OpDecorate %9 Location 1 + OpDecorate %43 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Input %7 + %9 = OpVariable %8 Input + %10 = OpTypeInt 32 0 + %11 = OpConstant %10 0 + %12 = OpTypePointer Input %6 + %15 = OpConstant %6 10 + %16 = OpTypeBool + %20 = OpTypeInt 32 1 + %21 = OpTypePointer Function %20 + %23 = OpConstant %20 0 + %32 = OpConstant %10 1 + %40 = OpConstant %20 1 + %42 = OpTypePointer Output %7 + %43 = OpVariable %42 Output + %54 = OpConstant %6 1 + %55 = OpConstantComposite %7 %54 %54 %54 %54 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %22 = OpVariable %21 Function + %24 = OpVariable %21 Function + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + %17 = OpFOrdLessThan %16 %14 %15 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %53 + %18 = OpLabel + OpStore %22 %23 + OpStore %24 %23 + OpStore %24 %23 + OpBranch %25 + %25 = OpLabel + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + %30 = OpLoad %20 %24 + %31 = OpConvertSToF %6 %30 + %33 = OpAccessChain %12 %9 %32 + %34 = OpLoad %6 %33 + %35 = OpFOrdLessThan %16 %31 %34 + OpBranchConditional %35 %26 %27 + %26 = OpLabel + %36 = OpLoad %20 %24 + %37 = OpLoad %20 %22 + %38 = OpIAdd %20 %37 %36 + OpStore %22 %38 + OpBranch %28 + %28 = OpLabel + %39 = OpLoad %20 %24 + %41 = OpIAdd %20 %39 %40 + OpStore %24 %41 + OpBranch %25 + %27 = OpLabel + %44 = OpLoad %20 %24 + %45 = OpConvertSToF %6 %44 + %46 = OpLoad %20 %24 + %47 = OpConvertSToF %6 %46 + %48 = OpLoad %20 %24 + %49 = OpConvertSToF %6 %48 + %50 = OpLoad %20 %24 + %51 = OpConvertSToF %6 %50 + %52 = OpCompositeConstruct %7 %45 %47 %49 %51 + OpStore %43 %52 + OpBranch %19 + %53 = OpLabel + OpStore %43 %55 + OpBranch %19 + %19 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + + DominatorAnalysis* analysis = context->GetDominatorAnalysis(f); + const CFG& cfg = *context->cfg(); + + DominatorTree& tree = analysis->GetDomTree(); + + EXPECT_EQ(tree.GetRoot()->bb_, cfg.pseudo_entry_block()); + EXPECT_TRUE(analysis->Dominates(5, 18)); + EXPECT_TRUE(analysis->Dominates(5, 53)); + EXPECT_TRUE(analysis->Dominates(5, 19)); + EXPECT_TRUE(analysis->Dominates(5, 25)); + EXPECT_TRUE(analysis->Dominates(5, 29)); + EXPECT_TRUE(analysis->Dominates(5, 27)); + EXPECT_TRUE(analysis->Dominates(5, 26)); + EXPECT_TRUE(analysis->Dominates(5, 28)); + + EXPECT_TRUE(analysis->StrictlyDominates(5, 18)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 53)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 19)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 25)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 29)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 27)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 26)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 28)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/switch_case_fallthrough.cpp b/third_party/spirv-tools/test/opt/dominator_tree/switch_case_fallthrough.cpp new file mode 100644 index 0000000..d9dd7d1 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/switch_case_fallthrough.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Generated from the following GLSL +#version 440 core +layout(location = 0) out vec4 v; +layout(location = 1) in vec4 in_val; +void main() { + int i; + switch (int(in_val.x)) { + case 0: + i = 0; + case 1: + i = 1; + break; + case 2: + i = 2; + case 3: + i = 3; + case 4: + i = 4; + break; + default: + i = 0; + } + v = vec4(i, i, i, i); +} +*/ +TEST_F(PassClassTest, UnreachableNestedIfs) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %35 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %9 "in_val" + OpName %25 "i" + OpName %35 "v" + OpDecorate %9 Location 1 + OpDecorate %35 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Input %7 + %9 = OpVariable %8 Input + %10 = OpTypeInt 32 0 + %11 = OpConstant %10 0 + %12 = OpTypePointer Input %6 + %15 = OpTypeInt 32 1 + %24 = OpTypePointer Function %15 + %26 = OpConstant %15 0 + %27 = OpConstant %15 1 + %29 = OpConstant %15 2 + %30 = OpConstant %15 3 + %31 = OpConstant %15 4 + %34 = OpTypePointer Output %7 + %35 = OpVariable %34 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %24 Function + %13 = OpAccessChain %12 %9 %11 + %14 = OpLoad %6 %13 + %16 = OpConvertFToS %15 %14 + OpSelectionMerge %23 None + OpSwitch %16 %22 0 %17 1 %18 2 %19 3 %20 4 %21 + %22 = OpLabel + OpStore %25 %26 + OpBranch %23 + %17 = OpLabel + OpStore %25 %26 + OpBranch %18 + %18 = OpLabel + OpStore %25 %27 + OpBranch %23 + %19 = OpLabel + OpStore %25 %29 + OpBranch %20 + %20 = OpLabel + OpStore %25 %30 + OpBranch %21 + %21 = OpLabel + OpStore %25 %31 + OpBranch %23 + %23 = OpLabel + %36 = OpLoad %15 %25 + %37 = OpConvertSToF %6 %36 + %38 = OpLoad %15 %25 + %39 = OpConvertSToF %6 %38 + %40 = OpLoad %15 %25 + %41 = OpConvertSToF %6 %40 + %42 = OpLoad %15 %25 + %43 = OpConvertSToF %6 %42 + %44 = OpCompositeConstruct %7 %37 %39 %41 %43 + OpStore %35 %44 + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + const Function* f = spvtest::GetFunction(module, 4); + DominatorAnalysis* analysis = context->GetDominatorAnalysis(f); + + EXPECT_TRUE(analysis->Dominates(5, 5)); + EXPECT_TRUE(analysis->Dominates(5, 17)); + EXPECT_TRUE(analysis->Dominates(5, 18)); + EXPECT_TRUE(analysis->Dominates(5, 19)); + EXPECT_TRUE(analysis->Dominates(5, 20)); + EXPECT_TRUE(analysis->Dominates(5, 21)); + EXPECT_TRUE(analysis->Dominates(5, 22)); + EXPECT_TRUE(analysis->Dominates(5, 23)); + + EXPECT_TRUE(analysis->StrictlyDominates(5, 17)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 18)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 19)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 20)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 21)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 22)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 23)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/unreachable_for.cpp b/third_party/spirv-tools/test/opt/dominator_tree/unreachable_for.cpp new file mode 100644 index 0000000..469e5c1 --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/unreachable_for.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Generated from the following GLSL +#version 440 core +void main() { + for (int i = 0; i < 1; i++) { + break; + } +} +*/ +TEST_F(PassClassTest, UnreachableNestedIfs) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 1 + %17 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %12 + %13 = OpLabel + %20 = OpLoad %6 %8 + %21 = OpIAdd %6 %20 %16 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + const Function* f = spvtest::GetFunction(module, 4); + DominatorAnalysis* analysis = context->GetDominatorAnalysis(f); + EXPECT_TRUE(analysis->Dominates(5, 5)); + EXPECT_TRUE(analysis->Dominates(5, 10)); + EXPECT_TRUE(analysis->Dominates(5, 14)); + EXPECT_TRUE(analysis->Dominates(5, 11)); + EXPECT_TRUE(analysis->Dominates(5, 12)); + EXPECT_TRUE(analysis->Dominates(10, 10)); + EXPECT_TRUE(analysis->Dominates(10, 14)); + EXPECT_TRUE(analysis->Dominates(10, 11)); + EXPECT_TRUE(analysis->Dominates(10, 12)); + EXPECT_TRUE(analysis->Dominates(14, 14)); + EXPECT_TRUE(analysis->Dominates(14, 11)); + EXPECT_TRUE(analysis->Dominates(14, 12)); + EXPECT_TRUE(analysis->Dominates(11, 11)); + EXPECT_TRUE(analysis->Dominates(12, 12)); + + EXPECT_TRUE(analysis->StrictlyDominates(5, 10)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 14)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(5, 12)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 14)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 12)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 12)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/dominator_tree/unreachable_for_post.cpp b/third_party/spirv-tools/test/opt/dominator_tree/unreachable_for_post.cpp new file mode 100644 index 0000000..8d3e37b --- /dev/null +++ b/third_party/spirv-tools/test/opt/dominator_tree/unreachable_for_post.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Generated from the following GLSL +#version 440 core +void main() { + for (int i = 0; i < 1; i++) { + break; + } +} +*/ +TEST_F(PassClassTest, UnreachableNestedIfs) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 1 + %17 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %12 + %13 = OpLabel + %20 = OpLoad %6 %8 + %21 = OpIAdd %6 %20 %16 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + const Function* f = spvtest::GetFunction(module, 4); + + PostDominatorAnalysis* analysis = context->GetPostDominatorAnalysis(f); + + EXPECT_TRUE(analysis->Dominates(12, 12)); + EXPECT_TRUE(analysis->Dominates(12, 14)); + EXPECT_TRUE(analysis->Dominates(12, 11)); + EXPECT_TRUE(analysis->Dominates(12, 10)); + EXPECT_TRUE(analysis->Dominates(12, 5)); + EXPECT_TRUE(analysis->Dominates(14, 14)); + EXPECT_TRUE(analysis->Dominates(14, 10)); + EXPECT_TRUE(analysis->Dominates(14, 5)); + EXPECT_TRUE(analysis->Dominates(10, 10)); + EXPECT_TRUE(analysis->Dominates(10, 5)); + EXPECT_TRUE(analysis->Dominates(5, 5)); + + EXPECT_TRUE(analysis->StrictlyDominates(12, 14)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 11)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 10)); + EXPECT_TRUE(analysis->StrictlyDominates(12, 5)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 10)); + EXPECT_TRUE(analysis->StrictlyDominates(14, 5)); + EXPECT_TRUE(analysis->StrictlyDominates(10, 5)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/eliminate_dead_const_test.cpp b/third_party/spirv-tools/test/opt/eliminate_dead_const_test.cpp new file mode 100644 index 0000000..59f06f9 --- /dev/null +++ b/third_party/spirv-tools/test/opt/eliminate_dead_const_test.cpp @@ -0,0 +1,847 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using EliminateDeadConstantBasicTest = PassTest<::testing::Test>; + +TEST_F(EliminateDeadConstantBasicTest, BasicAllDeadConstants) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%bool = OpTypeBool", + "%true = OpConstantTrue %bool", + "%false = OpConstantFalse %bool", + "%int = OpTypeInt 32 1", + "%9 = OpConstant %int 1", + "%uint = OpTypeInt 32 0", + "%11 = OpConstant %uint 2", + "%float = OpTypeFloat 32", + "%13 = OpConstant %float 3.1415", + "%double = OpTypeFloat 64", + "%15 = OpConstant %double 3.14159265358979", + "%main = OpFunction %void None %4", + "%16 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + // None of the above constants is ever used, so all of them should be + // eliminated. + const char* const_decl_opcodes[] = { + " OpConstantTrue ", + " OpConstantFalse ", + " OpConstant ", + }; + // Skip lines that have any one of const_decl_opcodes. + const std::string expected_disassembly = + SelectiveJoin(text, [&const_decl_opcodes](const char* line) { + return std::any_of( + std::begin(const_decl_opcodes), std::end(const_decl_opcodes), + [&line](const char* const_decl_op) { + return std::string(line).find(const_decl_op) != std::string::npos; + }); + }); + + SinglePassRunAndCheck( + JoinAllInsts(text), expected_disassembly, /* skip_nop = */ true); +} + +TEST_F(EliminateDeadConstantBasicTest, BasicNoneDeadConstants) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %main \"main\"", + "OpName %btv \"btv\"", + "OpName %bfv \"bfv\"", + "OpName %iv \"iv\"", + "OpName %uv \"uv\"", + "OpName %fv \"fv\"", + "OpName %dv \"dv\"", + "%void = OpTypeVoid", + "%10 = OpTypeFunction %void", + "%bool = OpTypeBool", + "%_ptr_Function_bool = OpTypePointer Function %bool", + "%true = OpConstantTrue %bool", + "%false = OpConstantFalse %bool", + "%int = OpTypeInt 32 1", + "%_ptr_Function_int = OpTypePointer Function %int", + "%int_1 = OpConstant %int 1", + "%uint = OpTypeInt 32 0", + "%_ptr_Function_uint = OpTypePointer Function %uint", + "%uint_2 = OpConstant %uint 2", + "%float = OpTypeFloat 32", + "%_ptr_Function_float = OpTypePointer Function %float", + "%float_3_1415 = OpConstant %float 3.1415", + "%double = OpTypeFloat 64", + "%_ptr_Function_double = OpTypePointer Function %double", + "%double_3_14159265358979 = OpConstant %double 3.14159265358979", + "%main = OpFunction %void None %10", + "%27 = OpLabel", + "%btv = OpVariable %_ptr_Function_bool Function", + "%bfv = OpVariable %_ptr_Function_bool Function", + "%iv = OpVariable %_ptr_Function_int Function", + "%uv = OpVariable %_ptr_Function_uint Function", + "%fv = OpVariable %_ptr_Function_float Function", + "%dv = OpVariable %_ptr_Function_double Function", + "OpStore %btv %true", + "OpStore %bfv %false", + "OpStore %iv %int_1", + "OpStore %uv %uint_2", + "OpStore %fv %float_3_1415", + "OpStore %dv %double_3_14159265358979", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + // All constants are used, so none of them should be eliminated. + SinglePassRunAndCheck( + JoinAllInsts(text), JoinAllInsts(text), /* skip_nop = */ true); +} + +struct EliminateDeadConstantTestCase { + // Type declarations and constants that should be kept. + std::vector used_consts; + // Instructions that refer to constants, this is added to create uses for + // some constants so they won't be treated as dead constants. + std::vector main_insts; + // Dead constants that should be removed. + std::vector dead_consts; +}; + +// All types that are potentially required in EliminateDeadConstantTest. +const std::vector CommonTypes = { + // clang-format off + // scalar types + "%bool = OpTypeBool", + "%uint = OpTypeInt 32 0", + "%int = OpTypeInt 32 1", + "%float = OpTypeFloat 32", + "%double = OpTypeFloat 64", + // vector types + "%v2bool = OpTypeVector %bool 2", + "%v2uint = OpTypeVector %uint 2", + "%v2int = OpTypeVector %int 2", + "%v3int = OpTypeVector %int 3", + "%v4int = OpTypeVector %int 4", + "%v2float = OpTypeVector %float 2", + "%v3float = OpTypeVector %float 3", + "%v2double = OpTypeVector %double 2", + // variable pointer types + "%_pf_bool = OpTypePointer Function %bool", + "%_pf_uint = OpTypePointer Function %uint", + "%_pf_int = OpTypePointer Function %int", + "%_pf_float = OpTypePointer Function %float", + "%_pf_double = OpTypePointer Function %double", + "%_pf_v2int = OpTypePointer Function %v2int", + "%_pf_v3int = OpTypePointer Function %v3int", + "%_pf_v2float = OpTypePointer Function %v2float", + "%_pf_v3float = OpTypePointer Function %v3float", + "%_pf_v2double = OpTypePointer Function %v2double", + // struct types + "%inner_struct = OpTypeStruct %bool %int %float %double", + "%outer_struct = OpTypeStruct %inner_struct %int %double", + "%flat_struct = OpTypeStruct %bool %int %float %double", + // clang-format on +}; + +using EliminateDeadConstantTest = + PassTest<::testing::TestWithParam>; + +TEST_P(EliminateDeadConstantTest, Custom) { + auto& tc = GetParam(); + AssemblyBuilder builder; + builder.AppendTypesConstantsGlobals(CommonTypes) + .AppendTypesConstantsGlobals(tc.used_consts) + .AppendInMain(tc.main_insts); + const std::string expected = builder.GetCode(); + builder.AppendTypesConstantsGlobals(tc.dead_consts); + const std::string assembly_with_dead_const = builder.GetCode(); + SinglePassRunAndCheck( + assembly_with_dead_const, expected, /* skip_nop = */ true); +} + +INSTANTIATE_TEST_SUITE_P( + ScalarTypeConstants, EliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Scalar type constants, one dead constant and one used constant. + { + /* .used_consts = */ + { + "%used_const_int = OpConstant %int 1", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %used_const_int", + }, + /* .dead_consts = */ + { + "%dead_const_int = OpConstant %int 1", + }, + }, + { + /* .used_consts = */ + { + "%used_const_uint = OpConstant %uint 1", + }, + /* .main_insts = */ + { + "%uint_var = OpVariable %_pf_uint Function", + "OpStore %uint_var %used_const_uint", + }, + /* .dead_consts = */ + { + "%dead_const_uint = OpConstant %uint 1", + }, + }, + { + /* .used_consts = */ + { + "%used_const_float = OpConstant %float 3.1415", + }, + /* .main_insts = */ + { + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %used_const_float", + }, + /* .dead_consts = */ + { + "%dead_const_float = OpConstant %float 3.1415", + }, + }, + { + /* .used_consts = */ + { + "%used_const_double = OpConstant %double 3.141592653", + }, + /* .main_insts = */ + { + "%double_var = OpVariable %_pf_double Function", + "OpStore %double_var %used_const_double", + }, + /* .dead_consts = */ + { + "%dead_const_double = OpConstant %double 3.141592653", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + VectorTypeConstants, EliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Tests eliminating dead constant type ivec2. One dead constant vector + // and one used constant vector, each built from its own group of + // scalar constants. + { + /* .used_consts = */ + { + "%used_int_x = OpConstant %int 1", + "%used_int_y = OpConstant %int 2", + "%used_v2int = OpConstantComposite %v2int %used_int_x %used_int_y", + }, + /* .main_insts = */ + { + "%v2int_var = OpVariable %_pf_v2int Function", + "OpStore %v2int_var %used_v2int", + }, + /* .dead_consts = */ + { + "%dead_int_x = OpConstant %int 1", + "%dead_int_y = OpConstant %int 2", + "%dead_v2int = OpConstantComposite %v2int %dead_int_x %dead_int_y", + }, + }, + // Tests eliminating dead constant ivec2. One dead constant vector and + // one used constant vector. But both built from a same group of + // scalar constants. + { + /* .used_consts = */ + { + "%used_int_x = OpConstant %int 1", + "%used_int_y = OpConstant %int 2", + "%used_int_z = OpConstant %int 3", + "%used_v3int = OpConstantComposite %v3int %used_int_x %used_int_y %used_int_z", + }, + /* .main_insts = */ + { + "%v3int_var = OpVariable %_pf_v3int Function", + "OpStore %v3int_var %used_v3int", + }, + /* .dead_consts = */ + { + "%dead_v3int = OpConstantComposite %v3int %used_int_x %used_int_y %used_int_z", + }, + }, + // Tests eliminating dead cosntant vec2. One dead constant vector and + // one used constant vector. Each built from its own group of scalar + // constants. + { + /* .used_consts = */ + { + "%used_float_x = OpConstant %float 3.1415", + "%used_float_y = OpConstant %float 4.25", + "%used_v2float = OpConstantComposite %v2float %used_float_x %used_float_y", + }, + /* .main_insts = */ + { + "%v2float_var = OpVariable %_pf_v2float Function", + "OpStore %v2float_var %used_v2float", + }, + /* .dead_consts = */ + { + "%dead_float_x = OpConstant %float 3.1415", + "%dead_float_y = OpConstant %float 4.25", + "%dead_v2float = OpConstantComposite %v2float %dead_float_x %dead_float_y", + }, + }, + // Tests eliminating dead cosntant vec2. One dead constant vector and + // one used constant vector. Both built from a same group of scalar + // constants. + { + /* .used_consts = */ + { + "%used_float_x = OpConstant %float 3.1415", + "%used_float_y = OpConstant %float 4.25", + "%used_float_z = OpConstant %float 4.75", + "%used_v3float = OpConstantComposite %v3float %used_float_x %used_float_y %used_float_z", + }, + /* .main_insts = */ + { + "%v3float_var = OpVariable %_pf_v3float Function", + "OpStore %v3float_var %used_v3float", + }, + /* .dead_consts = */ + { + "%dead_v3float = OpConstantComposite %v3float %used_float_x %used_float_y %used_float_z", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + StructTypeConstants, EliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // A plain struct type dead constants. All of its components are dead + // constants too. + { + /* .used_consts = */ {}, + /* .main_insts = */ {}, + /* .dead_consts = */ + { + "%dead_bool = OpConstantTrue %bool", + "%dead_int = OpConstant %int 1", + "%dead_float = OpConstant %float 2.5", + "%dead_double = OpConstant %double 3.14159265358979", + "%dead_struct = OpConstantComposite %flat_struct %dead_bool %dead_int %dead_float %dead_double", + }, + }, + // A plain struct type dead constants. Some of its components are dead + // constants while others are not. + { + /* .used_consts = */ + { + "%used_int = OpConstant %int 1", + "%used_double = OpConstant %double 3.14159265358979", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %used_int", + "%double_var = OpVariable %_pf_double Function", + "OpStore %double_var %used_double", + }, + /* .dead_consts = */ + { + "%dead_bool = OpConstantTrue %bool", + "%dead_float = OpConstant %float 2.5", + "%dead_struct = OpConstantComposite %flat_struct %dead_bool %used_int %dead_float %used_double", + }, + }, + // A nesting struct type dead constants. All components of both outer + // and inner structs are dead and should be removed after dead constant + // elimination. + { + /* .used_consts = */ {}, + /* .main_insts = */ {}, + /* .dead_consts = */ + { + "%dead_bool = OpConstantTrue %bool", + "%dead_int = OpConstant %int 1", + "%dead_float = OpConstant %float 2.5", + "%dead_double = OpConstant %double 3.1415926535", + "%dead_inner_struct = OpConstantComposite %inner_struct %dead_bool %dead_int %dead_float %dead_double", + "%dead_int2 = OpConstant %int 2", + "%dead_double2 = OpConstant %double 1.428571428514", + "%dead_outer_struct = OpConstantComposite %outer_struct %dead_inner_struct %dead_int2 %dead_double2", + }, + }, + // A nesting struct type dead constants. Some of its components are + // dead constants while others are not. + { + /* .used_consts = */ + { + "%used_int = OpConstant %int 1", + "%used_double = OpConstant %double 3.14159265358979", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %used_int", + "%double_var = OpVariable %_pf_double Function", + "OpStore %double_var %used_double", + }, + /* .dead_consts = */ + { + "%dead_bool = OpConstantTrue %bool", + "%dead_float = OpConstant %float 2.5", + "%dead_inner_struct = OpConstantComposite %inner_struct %dead_bool %used_int %dead_float %used_double", + "%dead_int = OpConstant %int 2", + "%dead_outer_struct = OpConstantComposite %outer_struct %dead_inner_struct %dead_int %used_double", + }, + }, + // A nesting struct case. The inner struct is used while the outer struct is not + { + /* .used_const = */ + { + "%used_bool = OpConstantTrue %bool", + "%used_int = OpConstant %int 1", + "%used_float = OpConstant %float 1.25", + "%used_double = OpConstant %double 1.23456789012345", + "%used_inner_struct = OpConstantComposite %inner_struct %used_bool %used_int %used_float %used_double", + }, + /* .main_insts = */ + { + "%bool_var = OpVariable %_pf_bool Function", + "%bool_from_inner_struct = OpCompositeExtract %bool %used_inner_struct 0", + "OpStore %bool_var %bool_from_inner_struct", + }, + /* .dead_consts = */ + { + "%dead_int = OpConstant %int 2", + "%dead_outer_struct = OpConstantComposite %outer_struct %used_inner_struct %dead_int %used_double" + }, + }, + // A nesting struct case. The outer struct is used, so the inner struct should not + // be removed even though it is not used anywhere. + { + /* .used_const = */ + { + "%used_bool = OpConstantTrue %bool", + "%used_int = OpConstant %int 1", + "%used_float = OpConstant %float 1.25", + "%used_double = OpConstant %double 1.23456789012345", + "%used_inner_struct = OpConstantComposite %inner_struct %used_bool %used_int %used_float %used_double", + "%used_outer_struct = OpConstantComposite %outer_struct %used_inner_struct %used_int %used_double" + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Function", + "%int_from_outer_struct = OpCompositeExtract %int %used_outer_struct 1", + "OpStore %int_var %int_from_outer_struct", + }, + /* .dead_consts = */ {}, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + ScalarTypeSpecConstants, EliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // All scalar type spec constants. + { + /* .used_consts = */ + { + "%used_bool = OpSpecConstantTrue %bool", + "%used_uint = OpSpecConstant %uint 2", + "%used_int = OpSpecConstant %int 2", + "%used_float = OpSpecConstant %float 2.5", + "%used_double = OpSpecConstant %double 1.42857142851", + }, + /* .main_insts = */ + { + "%bool_var = OpVariable %_pf_bool Function", + "%uint_var = OpVariable %_pf_uint Function", + "%int_var = OpVariable %_pf_int Function", + "%float_var = OpVariable %_pf_float Function", + "%double_var = OpVariable %_pf_double Function", + "OpStore %bool_var %used_bool", "OpStore %uint_var %used_uint", + "OpStore %int_var %used_int", "OpStore %float_var %used_float", + "OpStore %double_var %used_double", + }, + /* .dead_consts = */ + { + "%dead_bool = OpSpecConstantTrue %bool", + "%dead_uint = OpSpecConstant %uint 2", + "%dead_int = OpSpecConstant %int 2", + "%dead_float = OpSpecConstant %float 2.5", + "%dead_double = OpSpecConstant %double 1.42857142851", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + VectorTypeSpecConstants, EliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Bool vector type spec constants. One vector has all component dead, + // another vector has one dead boolean and one used boolean. + { + /* .used_consts = */ + { + "%used_bool = OpSpecConstantTrue %bool", + }, + /* .main_insts = */ + { + "%bool_var = OpVariable %_pf_bool Function", + "OpStore %bool_var %used_bool", + }, + /* .dead_consts = */ + { + "%dead_bool = OpSpecConstantFalse %bool", + "%dead_bool_vec1 = OpSpecConstantComposite %v2bool %dead_bool %dead_bool", + "%dead_bool_vec2 = OpSpecConstantComposite %v2bool %dead_bool %used_bool", + }, + }, + + // Uint vector type spec constants. One vector has all component dead, + // another vector has one dead unsigend integer and one used unsigned + // integer. + { + /* .used_consts = */ + { + "%used_uint = OpSpecConstant %uint 3", + }, + /* .main_insts = */ + { + "%uint_var = OpVariable %_pf_uint Function", + "OpStore %uint_var %used_uint", + }, + /* .dead_consts = */ + { + "%dead_uint = OpSpecConstant %uint 1", + "%dead_uint_vec1 = OpSpecConstantComposite %v2uint %dead_uint %dead_uint", + "%dead_uint_vec2 = OpSpecConstantComposite %v2uint %dead_uint %used_uint", + }, + }, + + // Int vector type spec constants. One vector has all component dead, + // another vector has one dead integer and one used integer. + { + /* .used_consts = */ + { + "%used_int = OpSpecConstant %int 3", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %used_int", + }, + /* .dead_consts = */ + { + "%dead_int = OpSpecConstant %int 1", + "%dead_int_vec1 = OpSpecConstantComposite %v2int %dead_int %dead_int", + "%dead_int_vec2 = OpSpecConstantComposite %v2int %dead_int %used_int", + }, + }, + + // Int vector type spec constants built with both spec constants and + // front-end constants. + { + /* .used_consts = */ + { + "%used_spec_int = OpSpecConstant %int 3", + "%used_front_end_int = OpConstant %int 3", + }, + /* .main_insts = */ + { + "%int_var1 = OpVariable %_pf_int Function", + "OpStore %int_var1 %used_spec_int", + "%int_var2 = OpVariable %_pf_int Function", + "OpStore %int_var2 %used_front_end_int", + }, + /* .dead_consts = */ + { + "%dead_spec_int = OpSpecConstant %int 1", + "%dead_front_end_int = OpConstant %int 1", + // Dead front-end and dead spec constants + "%dead_int_vec1 = OpSpecConstantComposite %v2int %dead_spec_int %dead_front_end_int", + // Used front-end and dead spec constants + "%dead_int_vec2 = OpSpecConstantComposite %v2int %dead_spec_int %used_front_end_int", + // Dead front-end and used spec constants + "%dead_int_vec3 = OpSpecConstantComposite %v2int %dead_front_end_int %used_spec_int", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + SpecConstantOp, EliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Cast operations: uint <-> int <-> bool + { + /* .used_consts = */ {}, + /* .main_insts = */ {}, + /* .dead_consts = */ + { + // Assistant constants, only used in dead spec constant + // operations. + "%signed_zero = OpConstant %int 0", + "%signed_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero", + "%unsigned_zero = OpConstant %uint 0", + "%unsigned_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", + "%signed_one = OpConstant %int 1", + "%signed_one_vec = OpConstantComposite %v2int %signed_one %signed_one", + "%unsigned_one = OpConstant %uint 1", + "%unsigned_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + + // Spec constants that support casting to each other. + "%dead_bool = OpSpecConstantTrue %bool", + "%dead_uint = OpSpecConstant %uint 1", + "%dead_int = OpSpecConstant %int 2", + "%dead_bool_vec = OpSpecConstantComposite %v2bool %dead_bool %dead_bool", + "%dead_uint_vec = OpSpecConstantComposite %v2uint %dead_uint %dead_uint", + "%dead_int_vec = OpSpecConstantComposite %v2int %dead_int %dead_int", + + // Scalar cast to boolean spec constant. + "%int_to_bool = OpSpecConstantOp %bool INotEqual %dead_int %signed_zero", + "%uint_to_bool = OpSpecConstantOp %bool INotEqual %dead_uint %unsigned_zero", + + // Vector cast to boolean spec constant. + "%int_to_bool_vec = OpSpecConstantOp %v2bool INotEqual %dead_int_vec %signed_zero_vec", + "%uint_to_bool_vec = OpSpecConstantOp %v2bool INotEqual %dead_uint_vec %unsigned_zero_vec", + + // Scalar cast to int spec constant. + "%bool_to_int = OpSpecConstantOp %int Select %dead_bool %signed_one %signed_zero", + "%uint_to_int = OpSpecConstantOp %uint IAdd %dead_uint %unsigned_zero", + + // Vector cast to int spec constant. + "%bool_to_int_vec = OpSpecConstantOp %v2int Select %dead_bool_vec %signed_one_vec %signed_zero_vec", + "%uint_to_int_vec = OpSpecConstantOp %v2uint IAdd %dead_uint_vec %unsigned_zero_vec", + + // Scalar cast to uint spec constant. + "%bool_to_uint = OpSpecConstantOp %uint Select %dead_bool %unsigned_one %unsigned_zero", + "%int_to_uint_vec = OpSpecConstantOp %uint IAdd %dead_int %signed_zero", + + // Vector cast to uint spec constant. + "%bool_to_uint_vec = OpSpecConstantOp %v2uint Select %dead_bool_vec %unsigned_one_vec %unsigned_zero_vec", + "%int_to_uint = OpSpecConstantOp %v2uint IAdd %dead_int_vec %signed_zero_vec", + }, + }, + + // Add, sub, mul, div, rem. + { + /* .used_consts = */ {}, + /* .main_insts = */ {}, + /* .dead_consts = */ + { + "%dead_spec_int_a = OpSpecConstant %int 1", + "%dead_spec_int_a_vec = OpSpecConstantComposite %v2int %dead_spec_int_a %dead_spec_int_a", + + "%dead_spec_int_b = OpSpecConstant %int 2", + "%dead_spec_int_b_vec = OpSpecConstantComposite %v2int %dead_spec_int_b %dead_spec_int_b", + + "%dead_const_int_c = OpConstant %int 3", + "%dead_const_int_c_vec = OpConstantComposite %v2int %dead_const_int_c %dead_const_int_c", + + // Add + "%add_a_b = OpSpecConstantOp %int IAdd %dead_spec_int_a %dead_spec_int_b", + "%add_a_b_vec = OpSpecConstantOp %v2int IAdd %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Sub + "%sub_a_b = OpSpecConstantOp %int ISub %dead_spec_int_a %dead_spec_int_b", + "%sub_a_b_vec = OpSpecConstantOp %v2int ISub %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Mul + "%mul_a_b = OpSpecConstantOp %int IMul %dead_spec_int_a %dead_spec_int_b", + "%mul_a_b_vec = OpSpecConstantOp %v2int IMul %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Div + "%div_a_b = OpSpecConstantOp %int SDiv %dead_spec_int_a %dead_spec_int_b", + "%div_a_b_vec = OpSpecConstantOp %v2int SDiv %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Bitwise Xor + "%xor_a_b = OpSpecConstantOp %int BitwiseXor %dead_spec_int_a %dead_spec_int_b", + "%xor_a_b_vec = OpSpecConstantOp %v2int BitwiseXor %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Scalar Comparison + "%less_a_b = OpSpecConstantOp %bool SLessThan %dead_spec_int_a %dead_spec_int_b", + }, + }, + + // Vectors without used swizzles should be removed. + { + /* .used_consts = */ + { + "%used_int = OpConstant %int 3", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %used_int", + }, + /* .dead_consts = */ + { + "%dead_int = OpConstant %int 3", + + "%dead_spec_int_a = OpSpecConstant %int 1", + "%vec_a = OpSpecConstantComposite %v4int %dead_spec_int_a %dead_spec_int_a %dead_int %dead_int", + + "%dead_spec_int_b = OpSpecConstant %int 2", + "%vec_b = OpSpecConstantComposite %v4int %dead_spec_int_b %dead_spec_int_b %used_int %used_int", + + // Extract scalar + "%a_x = OpSpecConstantOp %int CompositeExtract %vec_a 0", + "%b_x = OpSpecConstantOp %int CompositeExtract %vec_b 0", + + // Extract vector + "%a_xy = OpSpecConstantOp %v2int VectorShuffle %vec_a %vec_a 0 1", + "%b_xy = OpSpecConstantOp %v2int VectorShuffle %vec_b %vec_b 0 1", + }, + }, + // Vectors with used swizzles should not be removed. + { + /* .used_consts = */ + { + "%used_int = OpConstant %int 3", + "%used_spec_int_a = OpSpecConstant %int 1", + "%used_spec_int_b = OpSpecConstant %int 2", + // Create vectors + "%vec_a = OpSpecConstantComposite %v4int %used_spec_int_a %used_spec_int_a %used_int %used_int", + "%vec_b = OpSpecConstantComposite %v4int %used_spec_int_b %used_spec_int_b %used_int %used_int", + // Extract vector + "%a_xy = OpSpecConstantOp %v2int VectorShuffle %vec_a %vec_a 0 1", + "%b_xy = OpSpecConstantOp %v2int VectorShuffle %vec_b %vec_b 0 1", + }, + /* .main_insts = */ + { + "%v2int_var_a = OpVariable %_pf_v2int Function", + "%v2int_var_b = OpVariable %_pf_v2int Function", + "OpStore %v2int_var_a %a_xy", + "OpStore %v2int_var_b %b_xy", + }, + /* .dead_consts = */ {}, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + LongDefUseChain, EliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Long Def-Use chain with binary operations. + { + /* .used_consts = */ + { + "%array_size = OpConstant %int 4", + "%type_arr_int_4 = OpTypeArray %int %array_size", + "%used_int_0 = OpConstant %int 100", + "%used_int_1 = OpConstant %int 1", + "%used_int_2 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_1", + "%used_int_3 = OpSpecConstantOp %int ISub %used_int_0 %used_int_2", + "%used_int_4 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_3", + "%used_int_5 = OpSpecConstantOp %int ISub %used_int_0 %used_int_4", + "%used_int_6 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_5", + "%used_int_7 = OpSpecConstantOp %int ISub %used_int_0 %used_int_6", + "%used_int_8 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_7", + "%used_int_9 = OpSpecConstantOp %int ISub %used_int_0 %used_int_8", + "%used_int_10 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_9", + "%used_int_11 = OpSpecConstantOp %int ISub %used_int_0 %used_int_10", + "%used_int_12 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_11", + "%used_int_13 = OpSpecConstantOp %int ISub %used_int_0 %used_int_12", + "%used_int_14 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_13", + "%used_int_15 = OpSpecConstantOp %int ISub %used_int_0 %used_int_14", + "%used_int_16 = OpSpecConstantOp %int ISub %used_int_0 %used_int_15", + "%used_int_17 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_16", + "%used_int_18 = OpSpecConstantOp %int ISub %used_int_0 %used_int_17", + "%used_int_19 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_18", + "%used_int_20 = OpSpecConstantOp %int ISub %used_int_0 %used_int_19", + "%used_vec_a = OpSpecConstantComposite %v2int %used_int_18 %used_int_19", + "%used_vec_b = OpSpecConstantOp %v2int IMul %used_vec_a %used_vec_a", + "%used_int_21 = OpSpecConstantOp %int CompositeExtract %used_vec_b 0", + "%used_array = OpConstantComposite %type_arr_int_4 %used_int_20 %used_int_20 %used_int_21 %used_int_21", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Function", + "%used_array_2 = OpCompositeExtract %int %used_array 2", + "OpStore %int_var %used_array_2", + }, + /* .dead_consts = */ + { + "%dead_int_1 = OpConstant %int 2", + "%dead_int_2 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_1", + "%dead_int_3 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_2", + "%dead_int_4 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_3", + "%dead_int_5 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_4", + "%dead_int_6 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_5", + "%dead_int_7 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_6", + "%dead_int_8 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_7", + "%dead_int_9 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_8", + "%dead_int_10 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_9", + "%dead_int_11 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_10", + "%dead_int_12 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_11", + "%dead_int_13 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_12", + "%dead_int_14 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_13", + "%dead_int_15 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_14", + "%dead_int_16 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_15", + "%dead_int_17 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_16", + "%dead_int_18 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_17", + "%dead_int_19 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_18", + "%dead_int_20 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_19", + "%dead_vec_a = OpSpecConstantComposite %v2int %dead_int_18 %dead_int_19", + "%dead_vec_b = OpSpecConstantOp %v2int IMul %dead_vec_a %dead_vec_a", + "%dead_int_21 = OpSpecConstantOp %int CompositeExtract %dead_vec_b 0", + "%dead_array = OpConstantComposite %type_arr_int_4 %dead_int_20 %used_int_20 %dead_int_19 %used_int_19", + }, + }, + // Long Def-Use chain with swizzle + // clang-format on + }))); + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/eliminate_dead_functions_test.cpp b/third_party/spirv-tools/test/opt/eliminate_dead_functions_test.cpp new file mode 100644 index 0000000..96ecdc6 --- /dev/null +++ b/third_party/spirv-tools/test/opt/eliminate_dead_functions_test.cpp @@ -0,0 +1,444 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::HasSubstr; +using EliminateDeadFunctionsBasicTest = PassTest<::testing::Test>; + +TEST_F(EliminateDeadFunctionsBasicTest, BasicDeleteDeadFunction) { + // The function Dead should be removed because it is never called. + const std::vector common_code = { + // clang-format off + "OpCapability Shader", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\"", + "OpName %main \"main\"", + "OpName %Live \"Live\"", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%main = OpFunction %void None %7", + "%15 = OpLabel", + "%16 = OpFunctionCall %void %Live", + "%17 = OpFunctionCall %void %Live", + "OpReturn", + "OpFunctionEnd", + "%Live = OpFunction %void None %7", + "%20 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + const std::vector dead_function = { + // clang-format off + "%Dead = OpFunction %void None %7", + "%19 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + JoinAllInsts(Concat(common_code, dead_function)), + JoinAllInsts(common_code), /* skip_nop = */ true); +} + +TEST_F(EliminateDeadFunctionsBasicTest, BasicKeepLiveFunction) { + // Everything is reachable from an entry point, so no functions should be + // deleted. + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\"", + "OpName %main \"main\"", + "OpName %Live1 \"Live1\"", + "OpName %Live2 \"Live2\"", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%main = OpFunction %void None %7", + "%15 = OpLabel", + "%16 = OpFunctionCall %void %Live2", + "%17 = OpFunctionCall %void %Live1", + "OpReturn", + "OpFunctionEnd", + "%Live1 = OpFunction %void None %7", + "%19 = OpLabel", + "OpReturn", + "OpFunctionEnd", + "%Live2 = OpFunction %void None %7", + "%20 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + std::string assembly = JoinAllInsts(text); + auto result = SinglePassRunAndDisassemble( + assembly, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(assembly, std::get<0>(result)); +} + +TEST_F(EliminateDeadFunctionsBasicTest, BasicKeepExportFunctions) { + // All functions are reachable. In particular, ExportedFunc and Constant are + // reachable because ExportedFunc is exported. Nothing should be removed. + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpCapability Linkage", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\"", + "OpName %main \"main\"", + "OpName %ExportedFunc \"ExportedFunc\"", + "OpName %Live \"Live\"", + "OpDecorate %ExportedFunc LinkageAttributes \"ExportedFunc\" Export", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%main = OpFunction %void None %7", + "%15 = OpLabel", + "OpReturn", + "OpFunctionEnd", +"%ExportedFunc = OpFunction %void None %7", + "%19 = OpLabel", + "%16 = OpFunctionCall %void %Live", + "OpReturn", + "OpFunctionEnd", + "%Live = OpFunction %void None %7", + "%20 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + std::string assembly = JoinAllInsts(text); + auto result = SinglePassRunAndDisassemble( + assembly, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(assembly, std::get<0>(result)); +} + +TEST_F(EliminateDeadFunctionsBasicTest, BasicRemoveDecorationsAndNames) { + // We want to remove the names and decorations associated with results that + // are removed. This test will check for that. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpName %main "main" + OpName %Dead "Dead" + OpName %x "x" + OpName %y "y" + OpName %z "z" + OpDecorate %x RelaxedPrecision + OpDecorate %y RelaxedPrecision + OpDecorate %z RelaxedPrecision + OpDecorate %6 RelaxedPrecision + OpDecorate %7 RelaxedPrecision + OpDecorate %8 RelaxedPrecision + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %10 + %14 = OpLabel + OpReturn + OpFunctionEnd + %Dead = OpFunction %void None %10 + %15 = OpLabel + %x = OpVariable %_ptr_Function_float Function + %y = OpVariable %_ptr_Function_float Function + %z = OpVariable %_ptr_Function_float Function + OpStore %x %float_1 + OpStore %y %float_1 + %6 = OpLoad %float %x + %7 = OpLoad %float %y + %8 = OpFAdd %float %6 %7 + OpStore %z %8 + OpReturn + OpFunctionEnd)"; + + const std::string expected_output = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpName %main "main" +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_1 = OpConstant %float 1 +%main = OpFunction %void None %10 +%14 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, expected_output, + /* skip_nop = */ true); +} + +TEST_F(EliminateDeadFunctionsBasicTest, DebugRemoveFunctionFromDebugFunction) { + // We want to remove id of OpFunction from DebugFunction. + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 %4 +OpExecutionMode %2 OriginUpperLeft +%5 = OpString "ps.hlsl" +OpSource HLSL 600 %5 "float4 foo() { + return 1; +} +float4 main(float4 color : COLOR) : SV_TARGET { + return foo() + color; +} +" +%6 = OpString "float" +%7 = OpString "main" +%8 = OpString "foo" +; CHECK: [[foo:%\d+]] = OpString "foo" +OpDecorate %3 Location 0 +OpDecorate %4 Location 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%v4float = OpTypeVector %float 4 +%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%18 = OpTypeFunction %void +%19 = OpTypeFunction %v4float +%3 = OpVariable %_ptr_Input_v4float Input +%4 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float +; CHECK: [[info_none:%\d+]] = OpExtInst %void %1 DebugInfoNone +%20 = OpExtInst %void %1 DebugSource %5 +%21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL +%22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float +%23 = OpExtInst %void %1 DebugTypeVector %22 4 +%24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23 +%25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 +%26 = OpExtInst %void %1 DebugFunction %7 %24 %20 4 1 %21 %7 FlagIsProtected|FlagIsPrivate 4 %2 +%27 = OpExtInst %void %1 DebugFunction %8 %25 %20 1 1 %21 %8 FlagIsProtected|FlagIsPrivate 1 %28 +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugFunction [[foo]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[info_none]] +%29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %27 +%40 = OpExtInst %void %1 DebugInlinedAt 4 %26 +%2 = OpFunction %void None %18 +%30 = OpLabel +%39 = OpVariable %_ptr_Function_v4float Function +%41 = OpExtInst %void %1 DebugScope %27 %40 +OpStore %39 %14 +%32 = OpLoad %v4float %39 +%42 = OpExtInst %void %1 DebugScope %26 +%33 = OpLoad %v4float %3 +%34 = OpFAdd %v4float %32 %33 +OpStore %4 %34 +%43 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +%28 = OpFunction %v4float None %19 +%36 = OpLabel +OpReturnValue %14 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(EliminateDeadFunctionsBasicTest, + DebugRemoveFunctionUsingExistingDebugInfoNone) { + // We want to remove id of OpFunction from DebugFunction. + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 %4 +OpExecutionMode %2 OriginUpperLeft +%5 = OpString "ps.hlsl" +OpSource HLSL 600 %5 "float4 foo() { + return 1; +} +float4 main(float4 color : COLOR) : SV_TARGET { + return foo() + color; +} +" +%6 = OpString "float" +%7 = OpString "main" +%8 = OpString "foo" +; CHECK: [[foo:%\d+]] = OpString "foo" +OpDecorate %3 Location 0 +OpDecorate %4 Location 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%v4float = OpTypeVector %float 4 +%14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%18 = OpTypeFunction %void +%19 = OpTypeFunction %v4float +%3 = OpVariable %_ptr_Input_v4float Input +%4 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float +; CHECK: [[info_none:%\d+]] = OpExtInst %void %1 DebugInfoNone +%20 = OpExtInst %void %1 DebugSource %5 +%21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL +%22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float +%23 = OpExtInst %void %1 DebugTypeVector %22 4 +%24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23 +%25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 +%26 = OpExtInst %void %1 DebugFunction %7 %24 %20 4 1 %21 %7 FlagIsProtected|FlagIsPrivate 4 %2 +%27 = OpExtInst %void %1 DebugFunction %8 %25 %20 1 1 %21 %8 FlagIsProtected|FlagIsPrivate 1 %28 +; CHECK: {{%\d+}} = OpExtInst %void %1 DebugFunction [[foo]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[info_none]] +%29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %27 +%35 = OpExtInst %void %1 DebugInfoNone +%40 = OpExtInst %void %1 DebugInlinedAt 4 %26 +%2 = OpFunction %void None %18 +%30 = OpLabel +%39 = OpVariable %_ptr_Function_v4float Function +%41 = OpExtInst %void %1 DebugScope %27 %40 +OpStore %39 %14 +%32 = OpLoad %v4float %39 +%42 = OpExtInst %void %1 DebugScope %26 +%33 = OpLoad %v4float %3 +%34 = OpFAdd %v4float %32 %33 +OpStore %4 %34 +%43 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +%28 = OpFunction %v4float None %19 +%36 = OpLabel +OpReturnValue %14 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoPersists) { + const std::string text = R"( +; CHECK: [[import:%\w+]] = OpExtInstImport +; CHECK: [[void:%\w+]] = OpTypeVoid +; CHECK-NOT: OpExtInst [[void]] [[import]] 1 +; CHECK: OpExtInst [[void]] [[import]] 2 +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%foo = OpFunction %void None %void_fn +%foo_entry = OpLabel +%non_semantic1 = OpExtInst %void %ext 1 +OpReturn +OpFunctionEnd +%non_semantic2 = OpExtInst %void %ext 2 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoRemoveDependent) { + const std::string text = R"( +; CHECK: [[import:%\w+]] = OpExtInstImport +; CHECK: [[void:%\w+]] = OpTypeVoid +; CHECK-NOT: OpExtInst [[void]] [[import]] 1 +; CHECK-NOT: OpExtInst [[void]] [[import]] 2 +; CHECK: OpExtInst [[void]] [[import]] 3 +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%foo = OpFunction %void None %void_fn +%foo_entry = OpLabel +%non_semantic1 = OpExtInst %void %ext 1 +OpReturn +OpFunctionEnd +%non_semantic2 = OpExtInst %void %ext 2 %foo +%non_semantic3 = OpExtInst %void %ext 3 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadFunctionsBasicTest, NonSemanticInfoRemoveDependentTree) { + const std::string text = R"( +; CHECK: [[import:%\w+]] = OpExtInstImport +; CHECK: [[void:%\w+]] = OpTypeVoid +; CHECK-NOT: OpExtInst [[void]] [[import]] 1 +; CHECK-NOT: OpExtInst [[void]] [[import]] 2 +; CHECK: OpExtInst [[void]] [[import]] 3 +; CHECK-NOT: OpExtInst [[void]] [[import]] 4 +; CHECK-NOT: OpExtInst [[void]] [[import]] 5 +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%foo = OpFunction %void None %void_fn +%foo_entry = OpLabel +%non_semantic1 = OpExtInst %void %ext 1 +OpReturn +OpFunctionEnd +%non_semantic2 = OpExtInst %void %ext 2 %foo +%non_semantic3 = OpExtInst %void %ext 3 +%non_semantic4 = OpExtInst %void %ext 4 %non_semantic2 +%non_semantic5 = OpExtInst %void %ext 5 %non_semantic4 +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/eliminate_dead_member_test.cpp b/third_party/spirv-tools/test/opt/eliminate_dead_member_test.cpp new file mode 100644 index 0000000..a9b0f28 --- /dev/null +++ b/third_party/spirv-tools/test/opt/eliminate_dead_member_test.cpp @@ -0,0 +1,1187 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "assembly_builder.h" +#include "gmock/gmock.h" +#include "pass_fixture.h" +#include "pass_utils.h" + +namespace { + +using namespace spvtools; + +using EliminateDeadMemberTest = opt::PassTest<::testing::Test>; + +TEST_F(EliminateDeadMemberTest, RemoveMember1) { + // Test that the member "y" is removed. + // Update OpMemberName for |y| and |z|. + // Update OpMemberDecorate for |y| and |z|. + // Update OpAccessChain for access to |z|. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "x" +; CHECK-NEXT: OpMemberName %type__Globals 1 "z" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 0 +; CHECK: OpMemberDecorate %type__Globals 1 Offset 8 +; CHECK: %type__Globals = OpTypeStruct %float %float +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %int_0 +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %in_var_Position "in.var.Position" + OpName %main "main" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %in_var_Position Location 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 8 + OpDecorate %type__Globals Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %int_2 = OpConstant %int 2 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %15 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%in_var_Position = OpVariable %_ptr_Input_v4float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %15 + %17 = OpLabel + %18 = OpLoad %v4float %in_var_Position + %19 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 + %20 = OpLoad %float %19 + %21 = OpCompositeExtract %float %18 0 + %22 = OpFAdd %float %21 %20 + %23 = OpCompositeInsert %v4float %22 %18 0 + %24 = OpCompositeExtract %float %18 1 + %25 = OpCompositeInsert %v4float %24 %23 1 + %26 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 + %27 = OpLoad %float %26 + %28 = OpCompositeExtract %float %18 2 + %29 = OpFAdd %float %28 %27 + %30 = OpCompositeInsert %v4float %29 %25 2 + OpStore %gl_Position %30 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMemberWithGroupDecorations) { + // Test that the member "y" is removed. + // Update OpGroupMemberDecorate for %type__Globals member 1 and 2. + // Update OpAccessChain for access to %type__Globals member 2. + const std::string text = R"( +; CHECK: OpDecorate [[gr1:%\w+]] Offset 0 +; CHECK: OpDecorate [[gr2:%\w+]] Offset 4 +; CHECK: OpDecorate [[gr3:%\w+]] Offset 8 +; CHECK: [[gr1]] = OpDecorationGroup +; CHECK: [[gr2]] = OpDecorationGroup +; CHECK: [[gr3]] = OpDecorationGroup +; CHECK: OpGroupMemberDecorate [[gr1]] %type__Globals 0 +; CHECK-NOT: OpGroupMemberDecorate [[gr2]] +; CHECK: OpGroupMemberDecorate [[gr3]] %type__Globals 1 +; CHECK: %type__Globals = OpTypeStruct %float %float +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %int_0 +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpName %_Globals "$Globals" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %in_var_Position Location 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpDecorate %gr1 Offset 0 + OpDecorate %gr2 Offset 4 + OpDecorate %gr3 Offset 8 + OpDecorate %type__Globals Block + %gr1 = OpDecorationGroup + %gr2 = OpDecorationGroup + %gr3 = OpDecorationGroup + OpGroupMemberDecorate %gr1 %type__Globals 0 + OpGroupMemberDecorate %gr2 %type__Globals 1 + OpGroupMemberDecorate %gr3 %type__Globals 2 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %int_2 = OpConstant %int 2 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %15 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%in_var_Position = OpVariable %_ptr_Input_v4float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %15 + %17 = OpLabel + %18 = OpLoad %v4float %in_var_Position + %19 = OpAccessChain %_ptr_Uniform_float %_Globals %int_0 + %20 = OpLoad %float %19 + %21 = OpCompositeExtract %float %18 0 + %22 = OpFAdd %float %21 %20 + %23 = OpCompositeInsert %v4float %22 %18 0 + %24 = OpCompositeExtract %float %18 1 + %25 = OpCompositeInsert %v4float %24 %23 1 + %26 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 + %27 = OpLoad %float %26 + %28 = OpCompositeExtract %float %18 2 + %29 = OpFAdd %float %28 %27 + %30 = OpCompositeInsert %v4float %29 %25 2 + OpStore %gl_Position %30 + OpReturn + OpFunctionEnd +)"; + + // Skipping validation because of a bug in the validator. See issue #2376. + SinglePassRunAndMatch(text, false); +} + +TEST_F(EliminateDeadMemberTest, RemoveMemberUpdateConstant) { + // Test that the member "x" is removed. + // Update the OpConstantComposite instruction. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NEXT: OpMemberName %type__Globals 1 "z" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 +; CHECK: OpMemberDecorate %type__Globals 1 Offset 8 +; CHECK: %type__Globals = OpTypeStruct %float %float +; CHECK: OpConstantComposite %type__Globals %float_1 %float_2 +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_0 +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %in_var_Position "in.var.Position" + OpName %main "main" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %in_var_Position Location 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 8 + OpDecorate %type__Globals Block + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %int_2 = OpConstant %int 2 +%type__Globals = OpTypeStruct %float %float %float + %13 = OpConstantComposite %type__Globals %float_0 %float_1 %float_2 +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %19 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%in_var_Position = OpVariable %_ptr_Input_v4float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %19 + %21 = OpLabel + %22 = OpLoad %v4float %in_var_Position + %23 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 + %24 = OpLoad %float %23 + %25 = OpCompositeExtract %float %22 0 + %26 = OpFAdd %float %25 %24 + %27 = OpCompositeInsert %v4float %26 %22 0 + %28 = OpCompositeExtract %float %22 1 + %29 = OpCompositeInsert %v4float %28 %27 1 + %30 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 + %31 = OpLoad %float %30 + %32 = OpCompositeExtract %float %22 2 + %33 = OpFAdd %float %32 %31 + %34 = OpCompositeInsert %v4float %33 %29 2 + OpStore %gl_Position %34 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMemberUpdateCompositeConstruct) { + // Test that the member "x" is removed. + // Update the OpConstantComposite instruction. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NEXT: OpMemberName %type__Globals 1 "z" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 +; CHECK: OpMemberDecorate %type__Globals 1 Offset 8 +; CHECK: %type__Globals = OpTypeStruct %float %float +; CHECK: OpCompositeConstruct %type__Globals %float_1 %float_2 +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_0 +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals %uint_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %in_var_Position "in.var.Position" + OpName %main "main" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %in_var_Position Location 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 8 + OpDecorate %type__Globals Block + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %int_2 = OpConstant %int 2 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %19 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%in_var_Position = OpVariable %_ptr_Input_v4float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %19 + %21 = OpLabel + %13 = OpCompositeConstruct %type__Globals %float_0 %float_1 %float_2 + %22 = OpLoad %v4float %in_var_Position + %23 = OpAccessChain %_ptr_Uniform_float %_Globals %int_1 + %24 = OpLoad %float %23 + %25 = OpCompositeExtract %float %22 0 + %26 = OpFAdd %float %25 %24 + %27 = OpCompositeInsert %v4float %26 %22 0 + %28 = OpCompositeExtract %float %22 1 + %29 = OpCompositeInsert %v4float %28 %27 1 + %30 = OpAccessChain %_ptr_Uniform_float %_Globals %int_2 + %31 = OpLoad %float %30 + %32 = OpCompositeExtract %float %22 2 + %33 = OpFAdd %float %32 %31 + %34 = OpCompositeInsert %v4float %33 %29 2 + OpStore %gl_Position %34 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateInserExtract1) { + // Test that the members "x" and "z" are removed. + // Update the OpCompositeExtract instruction. + // Remove the OpCompositeInsert instruction since the member being inserted is + // dead. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 +; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset +; CHECK: %type__Globals = OpTypeStruct %float +; CHECK: [[ld:%\w+]] = OpLoad %type__Globals %_Globals +; CHECK: OpCompositeExtract %float [[ld]] 0 +; CHECK-NOT: OpCompositeInsert +; CHECK: OpReturn + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 8 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %7 = OpTypeFunction %void + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %void None %7 + %8 = OpLabel + %9 = OpLoad %type__Globals %_Globals + %10 = OpCompositeExtract %float %9 1 + %11 = OpCompositeInsert %type__Globals %10 %9 2 + OpReturn + OpFunctionEnd + +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateInserExtract2) { + // Test that the members "x" and "z" are removed. + // Update the OpCompositeExtract instruction. + // Update the OpCompositeInsert instruction. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 +; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset +; CHECK: %type__Globals = OpTypeStruct %float +; CHECK: [[ld:%\w+]] = OpLoad %type__Globals %_Globals +; CHECK: [[ex:%\w+]] = OpCompositeExtract %float [[ld]] 0 +; CHECK: OpCompositeInsert %type__Globals [[ex]] [[ld]] 0 +; CHECK: OpReturn + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 8 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %7 = OpTypeFunction %void + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %void None %7 + %8 = OpLabel + %9 = OpLoad %type__Globals %_Globals + %10 = OpCompositeExtract %float %9 1 + %11 = OpCompositeInsert %type__Globals %10 %9 1 + OpReturn + OpFunctionEnd + +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateInserExtract3) { + // Test that the members "x" and "z" are removed, and one member from the + // substruct. Update the OpCompositeExtract instruction. Update the + // OpCompositeInsert instruction. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 16 +; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset +; CHECK: OpMemberDecorate [[struct:%\w+]] 0 Offset 4 +; CHECK: [[struct:%\w+]] = OpTypeStruct %float +; CHECK: %type__Globals = OpTypeStruct [[struct]] +; CHECK: [[ld:%\w+]] = OpLoad %type__Globals %_Globals +; CHECK: [[ex:%\w+]] = OpCompositeExtract %float [[ld]] 0 0 +; CHECK: OpCompositeInsert %type__Globals [[ex]] [[ld]] 0 0 +; CHECK: OpReturn + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 24 + OpMemberDecorate %_struct_6 0 Offset 0 + OpMemberDecorate %_struct_6 1 Offset 4 + OpDecorate %type__Globals Block + %float = OpTypeFloat 32 + %_struct_6 = OpTypeStruct %float %float +%type__Globals = OpTypeStruct %float %_struct_6 %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %7 = OpTypeFunction %void + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %void None %7 + %8 = OpLabel + %9 = OpLoad %type__Globals %_Globals + %10 = OpCompositeExtract %float %9 1 1 + %11 = OpCompositeInsert %type__Globals %10 %9 1 1 + OpReturn + OpFunctionEnd + +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateInserExtract4) { + // Test that the members "x" and "z" are removed, and one member from the + // substruct. Update the OpCompositeExtract instruction. Update the + // OpCompositeInsert instruction. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 16 +; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset +; CHECK: OpMemberDecorate [[struct:%\w+]] 0 Offset 4 +; CHECK: [[struct:%\w+]] = OpTypeStruct %float +; CHECK: [[array:%\w+]] = OpTypeArray [[struct]] +; CHECK: %type__Globals = OpTypeStruct [[array]] +; CHECK: [[ld:%\w+]] = OpLoad %type__Globals %_Globals +; CHECK: [[ex:%\w+]] = OpCompositeExtract %float [[ld]] 0 1 0 +; CHECK: OpCompositeInsert %type__Globals [[ex]] [[ld]] 0 1 0 +; CHECK: OpReturn + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 80 + OpMemberDecorate %_struct_6 0 Offset 0 + OpMemberDecorate %_struct_6 1 Offset 4 + OpDecorate %array ArrayStride 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 ; 32-bit int, sign-less + %uint_4 = OpConstant %uint 4 + %float = OpTypeFloat 32 + %_struct_6 = OpTypeStruct %float %float + %array = OpTypeArray %_struct_6 %uint_4 +%type__Globals = OpTypeStruct %float %array %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %7 = OpTypeFunction %void + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %void None %7 + %8 = OpLabel + %9 = OpLoad %type__Globals %_Globals + %10 = OpCompositeExtract %float %9 1 1 1 + %11 = OpCompositeInsert %type__Globals %10 %9 1 1 1 + OpReturn + OpFunctionEnd + +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMembersUpdateArrayLength) { + // Test that the members "x" and "y" are removed. + // Member "z" is live because of the OpArrayLength instruction. + // Update the OpArrayLength instruction. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "z" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 16 +; CHECK-NOT: OpMemberDecorate %type__Globals 1 Offset +; CHECK: %type__Globals = OpTypeStruct %_runtimearr_float +; CHECK: OpArrayLength %uint %_Globals 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpDecorate %_runtimearr_float ArrayStride 16 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 +%_runtimearr_float = OpTypeRuntimeArray %float +%type__Globals = OpTypeStruct %float %float %_runtimearr_float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %9 = OpTypeFunction %void + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %void None %9 + %10 = OpLabel + %11 = OpLoad %type__Globals %_Globals + %12 = OpArrayLength %uint %_Globals 2 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, KeepMembersOpStore) { + // Test that all members are kept because of an OpStore. + // No change expected. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %_Globals "$Globals2" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %9 = OpTypeFunction %void + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %_Globals2 = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %void None %9 + %10 = OpLabel + %11 = OpLoad %type__Globals %_Globals + OpStore %_Globals2 %11 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(EliminateDeadMemberTest, KeepMembersOpCopyMemory) { + // Test that all members are kept because of an OpCopyMemory. + // No change expected. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %_Globals "$Globals2" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %9 = OpTypeFunction %void + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %_Globals2 = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %void None %9 + %10 = OpLabel + OpCopyMemory %_Globals2 %_Globals + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(EliminateDeadMemberTest, KeepMembersOpCopyMemorySized) { + // Test that all members are kept because of an OpCopyMemorySized. + // No change expected. + const std::string text = R"( + OpCapability Shader + OpCapability Addresses + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %_Globals "$Globals2" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 + %uint_20 = OpConstant %uint 20 + %float = OpTypeFloat 32 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %9 = OpTypeFunction %void + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %_Globals2 = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %void None %9 + %10 = OpLabel + OpCopyMemorySized %_Globals2 %_Globals %uint_20 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(EliminateDeadMemberTest, KeepMembersOpReturnValue) { + // Test that all members are kept because of an OpCopyMemorySized. + // No change expected. + const std::string text = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %_Globals "$Globals2" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 + %uint_20 = OpConstant %uint 20 + %float = OpTypeFloat 32 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %void = OpTypeVoid + %9 = OpTypeFunction %type__Globals + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform + %_Globals2 = OpVariable %_ptr_Uniform_type__Globals Uniform + %main = OpFunction %type__Globals None %9 + %10 = OpLabel + %11 = OpLoad %type__Globals %_Globals + OpReturnValue %11 + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(EliminateDeadMemberTest, RemoveMemberAccessChainWithArrays) { + // Leave only 1 member in each of the structs. + // Update OpMemberName, OpMemberDecorate, and OpAccessChain. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 16 +; CHECK: OpMemberDecorate [[struct:%\w+]] 0 Offset 4 +; CHECK: [[struct]] = OpTypeStruct %float +; CHECK: [[array:%\w+]] = OpTypeArray [[struct]] +; CHECK: %type__Globals = OpTypeStruct [[array]] +; CHECK: [[undef:%\w+]] = OpUndef %uint +; CHECK: OpAccessChain %_ptr_Uniform_float %_Globals [[undef]] %uint_0 [[undef]] %uint_0 + OpCapability Shader + OpCapability VariablePointersStorageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 16 + OpMemberDecorate %type__Globals 2 Offset 48 + OpMemberDecorate %_struct_4 0 Offset 0 + OpMemberDecorate %_struct_4 1 Offset 4 + OpDecorate %_arr__struct_4_uint_2 ArrayStride 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 + %_struct_4 = OpTypeStruct %float %float +%_arr__struct_4_uint_2 = OpTypeArray %_struct_4 %uint_2 +%type__Globals = OpTypeStruct %float %_arr__struct_4_uint_2 %float +%_arr_type__Globals_uint_3 = OpTypeArray %type__Globals %uint_3 +%_ptr_Uniform__arr_type__Globals_uint_3 = OpTypePointer Uniform %_arr_type__Globals_uint_3 + %void = OpTypeVoid + %15 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform__arr_type__Globals_uint_3 Uniform + %main = OpFunction %void None %15 + %17 = OpLabel + %18 = OpUndef %uint + %19 = OpAccessChain %_ptr_Uniform_float %_Globals %18 %uint_1 %18 %uint_1 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMemberInboundsAccessChain) { + // Test that the member "y" is removed. + // Update OpMemberName for |y| and |z|. + // Update OpMemberDecorate for |y| and |z|. + // Update OpInboundsAccessChain for access to |z|. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "x" +; CHECK-NEXT: OpMemberName %type__Globals 1 "z" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 0 +; CHECK: OpMemberDecorate %type__Globals 1 Offset 8 +; CHECK: %type__Globals = OpTypeStruct %float %float +; CHECK: OpInBoundsAccessChain %_ptr_Uniform_float %_Globals %int_0 +; CHECK: OpInBoundsAccessChain %_ptr_Uniform_float %_Globals %uint_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_Position %gl_Position + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %in_var_Position "in.var.Position" + OpName %main "main" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %in_var_Position Location 0 + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 8 + OpDecorate %type__Globals Block + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %int_2 = OpConstant %int 2 +%type__Globals = OpTypeStruct %float %float %float +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %15 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform_type__Globals Uniform +%in_var_Position = OpVariable %_ptr_Input_v4float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %15 + %17 = OpLabel + %18 = OpLoad %v4float %in_var_Position + %19 = OpInBoundsAccessChain %_ptr_Uniform_float %_Globals %int_0 + %20 = OpLoad %float %19 + %21 = OpCompositeExtract %float %18 0 + %22 = OpFAdd %float %21 %20 + %23 = OpCompositeInsert %v4float %22 %18 0 + %24 = OpCompositeExtract %float %18 1 + %25 = OpCompositeInsert %v4float %24 %23 1 + %26 = OpInBoundsAccessChain %_ptr_Uniform_float %_Globals %int_2 + %27 = OpLoad %float %26 + %28 = OpCompositeExtract %float %18 2 + %29 = OpFAdd %float %28 %27 + %30 = OpCompositeInsert %v4float %29 %25 2 + OpStore %gl_Position %30 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMemberPtrAccessChain) { + // Test that the member "y" is removed. + // Update OpMemberName for |y| and |z|. + // Update OpMemberDecorate for |y| and |z|. + // Update OpInboundsAccessChain for access to |z|. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "x" +; CHECK-NEXT: OpMemberName %type__Globals 1 "z" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 0 +; CHECK: OpMemberDecorate %type__Globals 1 Offset 16 +; CHECK: %type__Globals = OpTypeStruct %float %float +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 +; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_1 %uint_0 +; CHECK: OpPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_0 %uint_1 + OpCapability Shader + OpCapability VariablePointersStorageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type__Globals = OpTypeStruct %float %float %float +%_arr_type__Globals_uint_3 = OpTypeArray %type__Globals %uint_3 +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%_ptr_Uniform__arr_type__Globals_uint_3 = OpTypePointer Uniform %_arr_type__Globals_uint_3 + %void = OpTypeVoid + %14 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform__arr_type__Globals_uint_3 Uniform + %main = OpFunction %void None %14 + %16 = OpLabel + %17 = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 + %18 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_1 %uint_0 + %19 = OpPtrAccessChain %_ptr_Uniform_float %17 %uint_0 %uint_2 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, RemoveMemberInBoundsPtrAccessChain) { + // Test that the member "y" is removed. + // Update OpMemberName for |y| and |z|. + // Update OpMemberDecorate for |y| and |z|. + // Update OpInboundsAccessChain for access to |z|. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "x" +; CHECK-NEXT: OpMemberName %type__Globals 1 "z" +; CHECK-NOT: OpMemberName +; CHECK: OpMemberDecorate %type__Globals 0 Offset 0 +; CHECK: OpMemberDecorate %type__Globals 1 Offset 16 +; CHECK: %type__Globals = OpTypeStruct %float %float +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 +; CHECK: OpInBoundsPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_1 %uint_0 +; CHECK: OpInBoundsPtrAccessChain %_ptr_Uniform_float [[ac]] %uint_0 %uint_1 + OpCapability Shader + OpCapability Addresses + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %_Globals "$Globals" + OpName %main "main" + OpDecorate %_Globals DescriptorSet 0 + OpDecorate %_Globals Binding 0 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + OpDecorate %type__Globals Block + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 +%type__Globals = OpTypeStruct %float %float %float +%_arr_type__Globals_uint_3 = OpTypeArray %type__Globals %uint_3 +%_ptr_Uniform_type__Globals = OpTypePointer Uniform %type__Globals +%_ptr_Uniform__arr_type__Globals_uint_3 = OpTypePointer Uniform %_arr_type__Globals_uint_3 + %void = OpTypeVoid + %14 = OpTypeFunction %void +%_ptr_Uniform_float = OpTypePointer Uniform %float + %_Globals = OpVariable %_ptr_Uniform__arr_type__Globals_uint_3 Uniform + %main = OpFunction %void None %14 + %16 = OpLabel + %17 = OpAccessChain %_ptr_Uniform_type__Globals %_Globals %uint_0 + %18 = OpInBoundsPtrAccessChain %_ptr_Uniform_float %17 %uint_1 %uint_0 + %19 = OpInBoundsPtrAccessChain %_ptr_Uniform_float %17 %uint_0 %uint_2 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, DontRemoveModfStructResultTypeMembers) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + %float = OpTypeFloat 32 + %void = OpTypeVoid + %21 = OpTypeFunction %void +%ModfStructType = OpTypeStruct %float %float +%main = OpFunction %void None %21 + %22 = OpLabel + %23 = OpUndef %float + %24 = OpExtInst %ModfStructType %1 ModfStruct %23 + %25 = OpCompositeExtract %float %24 1 + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(EliminateDeadMemberTest, DontChangeInputStructs) { + // The input for a shader has to match the type of the output from the + // previous shader in the pipeline. Because of that, we cannot change the + // types of input variables. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %input_var + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + %float = OpTypeFloat 32 + %void = OpTypeVoid + %21 = OpTypeFunction %void +%in_var_type = OpTypeStruct %float %float +%in_ptr_type = OpTypePointer Input %in_var_type +%input_var = OpVariable %in_ptr_type Input +%main = OpFunction %void None %21 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(EliminateDeadMemberTest, DontChangeOutputStructs) { + // The output for a shader has to match the type of the output from the + // previous shader in the pipeline. Because of that, we cannot change the + // types of output variables. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %output_var + OpExecutionMode %main OriginUpperLeft + OpSource HLSL 600 + %float = OpTypeFloat 32 + %void = OpTypeVoid + %21 = OpTypeFunction %void +%out_var_type = OpTypeStruct %float %float +%out_ptr_type = OpTypePointer Output %out_var_type +%output_var = OpVariable %out_ptr_type Output +%main = OpFunction %void None %21 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ true); + EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(EliminateDeadMemberTest, UpdateSpecConstOpExtract) { + // Test that an extract in an OpSpecConstantOp is correctly updated. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpDecorate [[spec_const:%\w+]] SpecId 1 +; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 +; CHECK: %type__Globals = OpTypeStruct %uint +; CHECK: [[struct:%\w+]] = OpSpecConstantComposite %type__Globals [[spec_const]] +; CHECK: OpSpecConstantOp %uint CompositeExtract [[struct]] 0 + OpCapability Shader + OpCapability Addresses + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %main "main" + OpDecorate %c_0 SpecId 0 + OpDecorate %c_1 SpecId 1 + OpDecorate %c_2 SpecId 2 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + %uint = OpTypeInt 32 0 + %c_0 = OpSpecConstant %uint 0 + %c_1 = OpSpecConstant %uint 1 + %c_2 = OpSpecConstant %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 +%type__Globals = OpTypeStruct %uint %uint %uint +%spec_const_global = OpSpecConstantComposite %type__Globals %c_0 %c_1 %c_2 +%extract = OpSpecConstantOp %uint CompositeExtract %spec_const_global 1 + %void = OpTypeVoid + %14 = OpTypeFunction %void + %main = OpFunction %void None %14 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(EliminateDeadMemberTest, UpdateSpecConstOpInsert) { + // Test that an insert in an OpSpecConstantOp is correctly updated. + const std::string text = R"( +; CHECK: OpName +; CHECK-NEXT: OpMemberName %type__Globals 0 "y" +; CHECK-NOT: OpMemberName +; CHECK: OpDecorate [[spec_const:%\w+]] SpecId 1 +; CHECK: OpMemberDecorate %type__Globals 0 Offset 4 +; CHECK: %type__Globals = OpTypeStruct %uint +; CHECK: [[struct:%\w+]] = OpSpecConstantComposite %type__Globals [[spec_const]] +; CHECK: OpSpecConstantOp %type__Globals CompositeInsert %uint_3 [[struct]] 0 + OpCapability Shader + OpCapability Addresses + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource HLSL 600 + OpName %type__Globals "type.$Globals" + OpMemberName %type__Globals 0 "x" + OpMemberName %type__Globals 1 "y" + OpMemberName %type__Globals 2 "z" + OpName %main "main" + OpDecorate %c_0 SpecId 0 + OpDecorate %c_1 SpecId 1 + OpDecorate %c_2 SpecId 2 + OpMemberDecorate %type__Globals 0 Offset 0 + OpMemberDecorate %type__Globals 1 Offset 4 + OpMemberDecorate %type__Globals 2 Offset 16 + %uint = OpTypeInt 32 0 + %c_0 = OpSpecConstant %uint 0 + %c_1 = OpSpecConstant %uint 1 + %c_2 = OpSpecConstant %uint 2 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_3 = OpConstant %uint 3 +%type__Globals = OpTypeStruct %uint %uint %uint +%spec_const_global = OpSpecConstantComposite %type__Globals %c_0 %c_1 %c_2 +%insert = OpSpecConstantOp %type__Globals CompositeInsert %uint_3 %spec_const_global 1 +%extract = OpSpecConstantOp %uint CompositeExtract %insert 1 + %void = OpTypeVoid + %14 = OpTypeFunction %void + %main = OpFunction %void None %14 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace diff --git a/third_party/spirv-tools/test/opt/feature_manager_test.cpp b/third_party/spirv-tools/test/opt/feature_manager_test.cpp new file mode 100644 index 0000000..767376c --- /dev/null +++ b/third_party/spirv-tools/test/opt/feature_manager_test.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +using FeatureManagerTest = ::testing::Test; + +TEST_F(FeatureManagerTest, MissingExtension) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + EXPECT_FALSE(context->get_feature_mgr()->HasExtension( + Extension::kSPV_KHR_variable_pointers)); +} + +TEST_F(FeatureManagerTest, OneExtension) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpExtension "SPV_KHR_variable_pointers" + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + EXPECT_TRUE(context->get_feature_mgr()->HasExtension( + Extension::kSPV_KHR_variable_pointers)); +} + +TEST_F(FeatureManagerTest, NotADifferentExtension) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpExtension "SPV_KHR_variable_pointers" + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + EXPECT_FALSE(context->get_feature_mgr()->HasExtension( + Extension::kSPV_KHR_storage_buffer_storage_class)); +} + +TEST_F(FeatureManagerTest, TwoExtensions) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_storage_buffer_storage_class" + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + EXPECT_TRUE(context->get_feature_mgr()->HasExtension( + Extension::kSPV_KHR_variable_pointers)); + EXPECT_TRUE(context->get_feature_mgr()->HasExtension( + Extension::kSPV_KHR_storage_buffer_storage_class)); +} + +// Test capability checks. +TEST_F(FeatureManagerTest, ExplicitlyPresent1) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader)); + EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel)); +} + +TEST_F(FeatureManagerTest, ExplicitlyPresent2) { + const std::string text = R"( +OpCapability Kernel +OpMemoryModel Logical GLSL450 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader)); + EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel)); +} + +TEST_F(FeatureManagerTest, ImplicitlyPresent) { + const std::string text = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ASSERT_NE(context, nullptr); + + // Check multiple levels of indirection. Tessellation implies Shader, which + // implies Matrix. + EXPECT_TRUE( + context->get_feature_mgr()->HasCapability(SpvCapabilityTessellation)); + EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityShader)); + EXPECT_TRUE(context->get_feature_mgr()->HasCapability(SpvCapabilityMatrix)); + EXPECT_FALSE(context->get_feature_mgr()->HasCapability(SpvCapabilityKernel)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/fix_storage_class_test.cpp b/third_party/spirv-tools/test/opt/fix_storage_class_test.cpp new file mode 100644 index 0000000..4c8504a --- /dev/null +++ b/third_party/spirv-tools/test/opt/fix_storage_class_test.cpp @@ -0,0 +1,840 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using FixStorageClassTest = PassTest<::testing::Test>; + +TEST_F(FixStorageClassTest, FixAccessChain) { + const std::string text = R"( +; CHECK: OpAccessChain %_ptr_Workgroup_float +; CHECK: OpAccessChain %_ptr_Uniform_float + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID + OpExecutionMode %1 LocalSize 8 8 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_7 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%ptr = OpTypePointer Function %_arr_float_uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 + %_struct_5 = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Workgroup__struct_5 = OpTypePointer Workgroup %_struct_5 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %30 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %6 = OpVariable %_ptr_Workgroup__struct_5 Workgroup + %8 = OpVariable %_ptr_Uniform__struct_7 Uniform +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %1 = OpFunction %void None %30 + %38 = OpLabel + %44 = OpLoad %v3uint %gl_LocalInvocationID + %50 = OpAccessChain %_ptr_Function_float %6 %int_0 %int_0 %int_0 + %51 = OpLoad %float %50 + %52 = OpFMul %float %float_2 %51 + OpStore %50 %52 + %55 = OpLoad %float %50 + %59 = OpCompositeExtract %uint %44 0 + %60 = OpAccessChain %_ptr_Uniform_float %8 %int_0 %59 + OpStore %60 %55 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixStorageClassTest, FixLinkedAccessChain) { + const std::string text = R"( +; CHECK: OpAccessChain %_ptr_Workgroup__arr_float_uint_10 +; CHECK: OpAccessChain %_ptr_Workgroup_float +; CHECK: OpAccessChain %_ptr_Uniform_float + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID + OpExecutionMode %1 LocalSize 8 8 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_7 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_ptr_Function__arr_float_uint_10 = OpTypePointer Function %_arr_float_uint_10 +%_ptr = OpTypePointer Function %_arr_float_uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 + %_struct_17 = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Workgroup__struct_17 = OpTypePointer Workgroup %_struct_17 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %23 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %27 = OpVariable %_ptr_Workgroup__struct_17 Workgroup + %5 = OpVariable %_ptr_Uniform__struct_7 Uniform +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %1 = OpFunction %void None %23 + %28 = OpLabel + %29 = OpLoad %v3uint %gl_LocalInvocationID + %30 = OpAccessChain %_ptr_Function__arr_float_uint_10 %27 %int_0 %int_0 + %31 = OpAccessChain %_ptr_Function_float %30 %int_0 + %32 = OpLoad %float %31 + %33 = OpFMul %float %float_2 %32 + OpStore %31 %33 + %34 = OpLoad %float %31 + %35 = OpCompositeExtract %uint %29 0 + %36 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %35 + OpStore %36 %34 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixStorageClassTest, FixCopyObject) { + const std::string text = R"( +; CHECK: OpCopyObject %_ptr_Workgroup__struct_17 +; CHECK: OpAccessChain %_ptr_Workgroup_float +; CHECK: OpAccessChain %_ptr_Uniform_float + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID + OpExecutionMode %1 LocalSize 8 8 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %8 DescriptorSet 0 + OpDecorate %8 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_7 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%ptr = OpTypePointer Function %_arr_float_uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 + %_struct_17 = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Workgroup__struct_17 = OpTypePointer Workgroup %_struct_17 +%_ptr_Function__struct_17 = OpTypePointer Function %_struct_17 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %30 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %6 = OpVariable %_ptr_Workgroup__struct_17 Workgroup + %8 = OpVariable %_ptr_Uniform__struct_7 Uniform +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %1 = OpFunction %void None %30 + %38 = OpLabel + %44 = OpLoad %v3uint %gl_LocalInvocationID + %cp = OpCopyObject %_ptr_Function__struct_17 %6 + %50 = OpAccessChain %_ptr_Function_float %cp %int_0 %int_0 %int_0 + %51 = OpLoad %float %50 + %52 = OpFMul %float %float_2 %51 + OpStore %50 %52 + %55 = OpLoad %float %50 + %59 = OpCompositeExtract %uint %44 0 + %60 = OpAccessChain %_ptr_Uniform_float %8 %int_0 %59 + OpStore %60 %55 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixStorageClassTest, FixPhiInSelMerge) { + const std::string text = R"( +; CHECK: OpPhi %_ptr_Workgroup__struct_19 +; CHECK: OpAccessChain %_ptr_Workgroup_float +; CHECK: OpAccessChain %_ptr_Uniform_float + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID + OpExecutionMode %1 LocalSize 8 8 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_7 BufferBlock + %bool = OpTypeBool + %true = OpConstantTrue %bool + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_ptr_Function__arr_float_uint_10 = OpTypePointer Function %_arr_float_uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 + %_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19 +%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %25 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %28 = OpVariable %_ptr_Workgroup__struct_19 Workgroup + %29 = OpVariable %_ptr_Workgroup__struct_19 Workgroup + %5 = OpVariable %_ptr_Uniform__struct_7 Uniform +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %1 = OpFunction %void None %25 + %30 = OpLabel + OpSelectionMerge %31 None + OpBranchConditional %true %32 %31 + %32 = OpLabel + OpBranch %31 + %31 = OpLabel + %33 = OpPhi %_ptr_Function__struct_19 %28 %30 %29 %32 + %34 = OpLoad %v3uint %gl_LocalInvocationID + %35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0 + %36 = OpLoad %float %35 + %37 = OpFMul %float %float_2 %36 + OpStore %35 %37 + %38 = OpLoad %float %35 + %39 = OpCompositeExtract %uint %34 0 + %40 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %39 + OpStore %40 %38 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixStorageClassTest, FixPhiInLoop) { + const std::string text = R"( +; CHECK: OpPhi %_ptr_Workgroup__struct_19 +; CHECK: OpAccessChain %_ptr_Workgroup_float +; CHECK: OpAccessChain %_ptr_Uniform_float + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID + OpExecutionMode %1 LocalSize 8 8 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_7 BufferBlock + %bool = OpTypeBool + %true = OpConstantTrue %bool + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_ptr_Function__arr_float_uint_10 = OpTypePointer Function %_arr_float_uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 + %_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19 +%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %25 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %28 = OpVariable %_ptr_Workgroup__struct_19 Workgroup + %29 = OpVariable %_ptr_Workgroup__struct_19 Workgroup + %5 = OpVariable %_ptr_Uniform__struct_7 Uniform +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %1 = OpFunction %void None %25 + %30 = OpLabel + OpSelectionMerge %31 None + OpBranchConditional %true %32 %31 + %32 = OpLabel + OpBranch %31 + %31 = OpLabel + %33 = OpPhi %_ptr_Function__struct_19 %28 %30 %29 %32 + %34 = OpLoad %v3uint %gl_LocalInvocationID + %35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0 + %36 = OpLoad %float %35 + %37 = OpFMul %float %float_2 %36 + OpStore %35 %37 + %38 = OpLoad %float %35 + %39 = OpCompositeExtract %uint %34 0 + %40 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %39 + OpStore %40 %38 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixStorageClassTest, DontChangeFunctionCalls) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "testMain" +OpExecutionMode %1 LocalSize 8 8 1 +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Workgroup_int = OpTypePointer Workgroup %int +%_ptr_Uniform_int = OpTypePointer Uniform %int +%void = OpTypeVoid +%8 = OpTypeFunction %void +%9 = OpTypeFunction %_ptr_Uniform_int %_ptr_Function_int +%10 = OpVariable %_ptr_Workgroup_int Workgroup +%2 = OpVariable %_ptr_Uniform_int Uniform +%1 = OpFunction %void None %8 +%11 = OpLabel +%12 = OpFunctionCall %_ptr_Uniform_int %13 %10 +OpReturn +OpFunctionEnd +%13 = OpFunction %_ptr_Uniform_int None %9 +%14 = OpFunctionParameter %_ptr_Function_int +%15 = OpLabel +OpReturnValue %2 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false, false); +} + +TEST_F(FixStorageClassTest, FixSelect) { + const std::string text = R"( +; CHECK: OpSelect %_ptr_Workgroup__struct_19 +; CHECK: OpAccessChain %_ptr_Workgroup_float +; CHECK: OpAccessChain %_ptr_Uniform_float + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID + OpExecutionMode %1 LocalSize 8 8 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_7 BufferBlock + %bool = OpTypeBool + %true = OpConstantTrue %bool + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_ptr_Function__arr_float_uint_10 = OpTypePointer Function %_arr_float_uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 + %_struct_19 = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Workgroup__struct_19 = OpTypePointer Workgroup %_struct_19 +%_ptr_Function__struct_19 = OpTypePointer Function %_struct_19 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %25 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %28 = OpVariable %_ptr_Workgroup__struct_19 Workgroup + %29 = OpVariable %_ptr_Workgroup__struct_19 Workgroup + %5 = OpVariable %_ptr_Uniform__struct_7 Uniform +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %1 = OpFunction %void None %25 + %30 = OpLabel + %33 = OpSelect %_ptr_Function__struct_19 %true %28 %29 + %34 = OpLoad %v3uint %gl_LocalInvocationID + %35 = OpAccessChain %_ptr_Function_float %33 %int_0 %int_0 %int_0 + %36 = OpLoad %float %35 + %37 = OpFMul %float %float_2 %36 + OpStore %35 %37 + %38 = OpLoad %float %35 + %39 = OpCompositeExtract %uint %34 0 + %40 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %39 + OpStore %40 %38 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixStorageClassTest, BitCast) { + const std::string text = R"(OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%_ptr_Output_void = OpTypePointer Output %void +%_ptr_Private__ptr_Output_void = OpTypePointer Private %_ptr_Output_void +%6 = OpVariable %_ptr_Private__ptr_Output_void Private +%1 = OpFunction %void Inline %3 +%7 = OpLabel +%8 = OpBitcast %_ptr_Output_void %6 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(FixStorageClassTest, FixLinkedAccessChain2) { + // This case is similar to FixLinkedAccessChain. The difference is that the + // first OpAccessChain instruction starts as workgroup storage class. Only + // the second one needs to change. + const std::string text = R"( +; CHECK: OpAccessChain %_ptr_Workgroup__arr_float_uint_10 +; CHECK: OpAccessChain %_ptr_Workgroup_float +; CHECK: OpAccessChain %_ptr_Uniform_float + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "testMain" %gl_GlobalInvocationID %gl_LocalInvocationID %gl_WorkGroupID + OpExecutionMode %1 LocalSize 8 8 1 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %gl_WorkGroupID BuiltIn WorkgroupId + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %_runtimearr_float ArrayStride 4 + OpMemberDecorate %_struct_7 0 Offset 0 + OpDecorate %_struct_7 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_ptr_Workgroup__arr_float_uint_10 = OpTypePointer Workgroup %_arr_float_uint_10 +%_ptr = OpTypePointer Function %_arr_float_uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 + %_struct_17 = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Workgroup__struct_17 = OpTypePointer Workgroup %_struct_17 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %23 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Uniform_float = OpTypePointer Uniform %float + %27 = OpVariable %_ptr_Workgroup__struct_17 Workgroup + %5 = OpVariable %_ptr_Uniform__struct_7 Uniform +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_WorkGroupID = OpVariable %_ptr_Input_v3uint Input + %1 = OpFunction %void None %23 + %28 = OpLabel + %29 = OpLoad %v3uint %gl_LocalInvocationID + %30 = OpAccessChain %_ptr_Workgroup__arr_float_uint_10 %27 %int_0 %int_0 + %31 = OpAccessChain %_ptr_Function_float %30 %int_0 + %32 = OpLoad %float %31 + %33 = OpFMul %float %float_2 %32 + OpStore %31 %33 + %34 = OpLoad %float %31 + %35 = OpCompositeExtract %uint %29 0 + %36 = OpAccessChain %_ptr_Uniform_float %5 %int_0 %35 + OpStore %36 %34 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +using FixTypeTest = PassTest<::testing::Test>; + +TEST_F(FixTypeTest, FixAccessChain) { + const std::string text = R"( +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_T [[ac1]] %int_0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 600 + OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" + OpName %S "S" + OpMemberName %S 0 "t" + OpName %T "T" + OpMemberName %T 0 "a" + OpName %A "A" + OpName %type_ACSBuffer_counter "type.ACSBuffer.counter" + OpMemberName %type_ACSBuffer_counter 0 "counter" + OpName %counter_var_A "counter.var.A" + OpName %main "main" + OpName %S_0 "S" + OpMemberName %S_0 0 "t" + OpName %T_0 "T" + OpMemberName %T_0 0 "a" + OpDecorate %A DescriptorSet 0 + OpDecorate %A Binding 0 + OpDecorate %counter_var_A DescriptorSet 0 + OpDecorate %counter_var_A Binding 1 + OpMemberDecorate %T 0 Offset 0 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %_runtimearr_S ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_S BufferBlock + OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0 + OpDecorate %type_ACSBuffer_counter BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %T = OpTypeStruct %int + %S = OpTypeStruct %T +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S +%type_ACSBuffer_counter = OpTypeStruct %int +%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter + %void = OpTypeVoid + %18 = OpTypeFunction %void + %T_0 = OpTypeStruct %int + %S_0 = OpTypeStruct %T_0 +%_ptr_Function_S_0 = OpTypePointer Function %S_0 +%_ptr_Uniform_S = OpTypePointer Uniform %S +%_ptr_Uniform_T = OpTypePointer Uniform %T + %22 = OpTypeFunction %T_0 %_ptr_Function_S_0 +%_ptr_Function_T_0 = OpTypePointer Function %T_0 + %A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform +%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform + %main = OpFunction %void None %18 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_T_0 Function + %26 = OpVariable %_ptr_Function_S_0 Function + %27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0 + %28 = OpAccessChain %_ptr_Function_T_0 %27 %int_0 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixTypeTest, FixLoad) { + const std::string text = R"( +; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain %_ptr_Uniform_T [[ac1]] %int_0 +; CHECK: [[ld:%\w+]] = OpLoad %T [[ac2]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 600 + OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" + OpName %S "S" + OpMemberName %S 0 "t" + OpName %T "T" + OpMemberName %T 0 "a" + OpName %A "A" + OpName %type_ACSBuffer_counter "type.ACSBuffer.counter" + OpMemberName %type_ACSBuffer_counter 0 "counter" + OpName %counter_var_A "counter.var.A" + OpName %main "main" + OpName %S_0 "S" + OpMemberName %S_0 0 "t" + OpName %T_0 "T" + OpMemberName %T_0 0 "a" + OpDecorate %A DescriptorSet 0 + OpDecorate %A Binding 0 + OpDecorate %counter_var_A DescriptorSet 0 + OpDecorate %counter_var_A Binding 1 + OpMemberDecorate %T 0 Offset 0 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %_runtimearr_S ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_S BufferBlock + OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0 + OpDecorate %type_ACSBuffer_counter BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %T = OpTypeStruct %int + %S = OpTypeStruct %T +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S +%type_ACSBuffer_counter = OpTypeStruct %int +%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter + %void = OpTypeVoid + %18 = OpTypeFunction %void + %T_0 = OpTypeStruct %int + %S_0 = OpTypeStruct %T_0 +%_ptr_Function_S_0 = OpTypePointer Function %S_0 +%_ptr_Uniform_S = OpTypePointer Uniform %S +%_ptr_Uniform_T = OpTypePointer Uniform %T + %22 = OpTypeFunction %T_0 %_ptr_Function_S_0 +%_ptr_Function_T_0 = OpTypePointer Function %T_0 + %A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform +%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform + %main = OpFunction %void None %18 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_T_0 Function + %26 = OpVariable %_ptr_Function_S_0 Function + %27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0 + %28 = OpAccessChain %_ptr_Uniform_T %27 %int_0 + %29 = OpLoad %T_0 %28 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixTypeTest, FixStore) { + const std::string text = R"( +; CHECK: [[ld:%\w+]] = OpLoad %T +; CHECK: OpStore + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 600 + OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" + OpName %S "S" + OpMemberName %S 0 "t" + OpName %T "T" + OpMemberName %T 0 "a" + OpName %A "A" + OpName %type_ACSBuffer_counter "type.ACSBuffer.counter" + OpMemberName %type_ACSBuffer_counter 0 "counter" + OpName %counter_var_A "counter.var.A" + OpName %main "main" + OpName %S_0 "S" + OpMemberName %S_0 0 "t" + OpName %T_0 "T" + OpMemberName %T_0 0 "a" + OpDecorate %A DescriptorSet 0 + OpDecorate %A Binding 0 + OpDecorate %counter_var_A DescriptorSet 0 + OpDecorate %counter_var_A Binding 1 + OpMemberDecorate %T 0 Offset 0 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %_runtimearr_S ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_S BufferBlock + OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0 + OpDecorate %type_ACSBuffer_counter BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %T = OpTypeStruct %int + %S = OpTypeStruct %T +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S +%type_ACSBuffer_counter = OpTypeStruct %int +%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter + %void = OpTypeVoid + %18 = OpTypeFunction %void + %T_0 = OpTypeStruct %int + %S_0 = OpTypeStruct %T_0 +%_ptr_Function_S_0 = OpTypePointer Function %S_0 +%_ptr_Uniform_S = OpTypePointer Uniform %S +%_ptr_Uniform_T = OpTypePointer Uniform %T + %22 = OpTypeFunction %T_0 %_ptr_Function_S_0 +%_ptr_Function_T_0 = OpTypePointer Function %T_0 + %A = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform +%counter_var_A = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform + %main = OpFunction %void None %18 + %24 = OpLabel + %25 = OpVariable %_ptr_Function_T_0 Function + %26 = OpVariable %_ptr_Function_S_0 Function + %27 = OpAccessChain %_ptr_Uniform_S %A %int_0 %uint_0 + %28 = OpAccessChain %_ptr_Uniform_T %27 %int_0 + %29 = OpLoad %T %28 + OpStore %25 %29 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixTypeTest, FixSelect) { + const std::string text = R"( +; CHECK: OpSelect %_ptr_Uniform__struct_3 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_runtimearr__struct_3 ArrayStride 4 + OpMemberDecorate %_struct_5 0 Offset 0 + OpDecorate %_struct_5 BufferBlock + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %_struct_3 = OpTypeStruct %uint +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 + %_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %void = OpTypeVoid + %11 = OpTypeFunction %void + %_struct_12 = OpTypeStruct %uint +%_ptr_Function__struct_12 = OpTypePointer Function %_struct_12 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %bool = OpTypeBool +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 + %2 = OpVariable %_ptr_Uniform__struct_5 Uniform + %1 = OpFunction %void None %11 + %17 = OpLabel + %18 = OpAccessChain %_ptr_Uniform_uint %2 %uint_0 %uint_0 %uint_0 + %19 = OpLoad %uint %18 + %20 = OpSGreaterThan %bool %19 %uint_0 + %21 = OpAccessChain %_ptr_Uniform__struct_3 %2 %uint_0 %uint_0 + %22 = OpAccessChain %_ptr_Uniform__struct_3 %2 %uint_0 %uint_1 + %23 = OpSelect %_ptr_Function__struct_12 %20 %21 %22 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(FixTypeTest, FixPhiInLoop) { + const std::string text = R"( +; CHECK: [[ac_init:%\w+]] = OpAccessChain %_ptr_Uniform__struct_3 +; CHECK: [[ac_phi:%\w+]] = OpPhi %_ptr_Uniform__struct_3 [[ac_init]] {{%\w+}} [[ac_update:%\w+]] {{%\w+}} +; CHECK: [[ac_update]] = OpPtrAccessChain %_ptr_Uniform__struct_3 [[ac_phi]] %int_1 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_runtimearr__struct_3 ArrayStride 4 + OpMemberDecorate %_struct_5 0 Offset 0 + OpDecorate %_struct_5 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %_struct_3 = OpTypeStruct %int + %_struct_9 = OpTypeStruct %int +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 + %_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %void = OpTypeVoid + %12 = OpTypeFunction %void + %bool = OpTypeBool +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 +%_ptr_Function__struct_9 = OpTypePointer Function %_struct_9 + %2 = OpVariable %_ptr_Uniform__struct_5 Uniform + %1 = OpFunction %void None %12 + %16 = OpLabel + %17 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %int_0 + OpBranch %18 + %18 = OpLabel + %20 = OpPhi %_ptr_Function__struct_9 %17 %16 %21 %22 + %23 = OpUndef %bool + OpLoopMerge %24 %22 None + OpBranchConditional %23 %22 %24 + %22 = OpLabel + %21 = OpPtrAccessChain %_ptr_Function__struct_9 %20 %int_1 + OpBranch %18 + %24 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/flatten_decoration_test.cpp b/third_party/spirv-tools/test/opt/flatten_decoration_test.cpp new file mode 100644 index 0000000..d8d8867 --- /dev/null +++ b/third_party/spirv-tools/test/opt/flatten_decoration_test.cpp @@ -0,0 +1,239 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +// Returns the initial part of the assembly text for a valid +// SPIR-V module, including instructions prior to decorations. +std::string PreambleAssembly() { + return + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %hue %saturation %value +OpExecutionMode %main OriginUpperLeft +OpName %main "main" +OpName %void_fn "void_fn" +OpName %hue "hue" +OpName %saturation "saturation" +OpName %value "value" +OpName %entry "entry" +OpName %Point "Point" +OpName %Camera "Camera" +)"; +} + +// Retuns types +std::string TypesAndFunctionsAssembly() { + return + R"(%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%Point = OpTypeStruct %float %float %float +%Camera = OpTypeStruct %float %float +%_ptr_Input_float = OpTypePointer Input %float +%hue = OpVariable %_ptr_Input_float Input +%saturation = OpVariable %_ptr_Input_float Input +%value = OpVariable %_ptr_Input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; +} + +struct FlattenDecorationCase { + // Names and decorations before the pass. + std::string input; + // Names and decorations after the pass. + std::string expected; +}; + +using FlattenDecorationTest = + PassTest<::testing::TestWithParam>; + +TEST_P(FlattenDecorationTest, TransformsDecorations) { + const auto before = + PreambleAssembly() + GetParam().input + TypesAndFunctionsAssembly(); + const auto after = + PreambleAssembly() + GetParam().expected + TypesAndFunctionsAssembly(); + + SinglePassRunAndCheck(before, after, false, true); +} + +INSTANTIATE_TEST_SUITE_P(NoUses, FlattenDecorationTest, + ::testing::ValuesIn(std::vector{ + // No OpDecorationGroup + {"", ""}, + + // OpDecorationGroup without any uses, and + // no OpName. + {"%group = OpDecorationGroup\n", ""}, + + // OpDecorationGroup without any uses, and + // with OpName targeting it. Proves you must + // remove the names as well. + {"OpName %group \"group\"\n" + "%group = OpDecorationGroup\n", + ""}, + + // OpDecorationGroup with decorations that + // target it, but no uses in OpGroupDecorate + // or OpGroupMemberDecorate instructions. + {"OpDecorate %group Flat\n" + "OpDecorate %group NoPerspective\n" + "%group = OpDecorationGroup\n", + ""}, + })); + +INSTANTIATE_TEST_SUITE_P(OpGroupDecorate, FlattenDecorationTest, + ::testing::ValuesIn(std::vector{ + // One OpGroupDecorate + {"OpName %group \"group\"\n" + "OpDecorate %group Flat\n" + "OpDecorate %group NoPerspective\n" + "%group = OpDecorationGroup\n" + "OpGroupDecorate %group %hue %saturation\n", + "OpDecorate %hue Flat\n" + "OpDecorate %saturation Flat\n" + "OpDecorate %hue NoPerspective\n" + "OpDecorate %saturation NoPerspective\n"}, + // Multiple OpGroupDecorate + {"OpName %group \"group\"\n" + "OpDecorate %group Flat\n" + "OpDecorate %group NoPerspective\n" + "%group = OpDecorationGroup\n" + "OpGroupDecorate %group %hue %value\n" + "OpGroupDecorate %group %saturation\n", + "OpDecorate %hue Flat\n" + "OpDecorate %value Flat\n" + "OpDecorate %saturation Flat\n" + "OpDecorate %hue NoPerspective\n" + "OpDecorate %value NoPerspective\n" + "OpDecorate %saturation NoPerspective\n"}, + // Two group decorations, interleaved + {"OpName %group0 \"group0\"\n" + "OpName %group1 \"group1\"\n" + "OpDecorate %group0 Flat\n" + "OpDecorate %group1 NoPerspective\n" + "%group0 = OpDecorationGroup\n" + "%group1 = OpDecorationGroup\n" + "OpGroupDecorate %group0 %hue %value\n" + "OpGroupDecorate %group1 %saturation\n", + "OpDecorate %hue Flat\n" + "OpDecorate %value Flat\n" + "OpDecorate %saturation NoPerspective\n"}, + // Decoration with operands + {"OpName %group \"group\"\n" + "OpDecorate %group Location 42\n" + "%group = OpDecorationGroup\n" + "OpGroupDecorate %group %hue %saturation\n", + "OpDecorate %hue Location 42\n" + "OpDecorate %saturation Location 42\n"}, + })); + +INSTANTIATE_TEST_SUITE_P(OpGroupMemberDecorate, FlattenDecorationTest, + ::testing::ValuesIn(std::vector{ + // One OpGroupMemberDecorate + {"OpName %group \"group\"\n" + "OpDecorate %group Flat\n" + "OpDecorate %group Offset 16\n" + "%group = OpDecorationGroup\n" + "OpGroupMemberDecorate %group %Point 1\n", + "OpMemberDecorate %Point 1 Flat\n" + "OpMemberDecorate %Point 1 Offset 16\n"}, + // Multiple OpGroupMemberDecorate using the same + // decoration group. + {"OpName %group \"group\"\n" + "OpDecorate %group Flat\n" + "OpDecorate %group NoPerspective\n" + "OpDecorate %group Offset 8\n" + "%group = OpDecorationGroup\n" + "OpGroupMemberDecorate %group %Point 2\n" + "OpGroupMemberDecorate %group %Camera 1\n", + "OpMemberDecorate %Point 2 Flat\n" + "OpMemberDecorate %Camera 1 Flat\n" + "OpMemberDecorate %Point 2 NoPerspective\n" + "OpMemberDecorate %Camera 1 NoPerspective\n" + "OpMemberDecorate %Point 2 Offset 8\n" + "OpMemberDecorate %Camera 1 Offset 8\n"}, + // Two groups of member decorations, interleaved. + // Decoration is with and without operands. + {"OpName %group0 \"group0\"\n" + "OpName %group1 \"group1\"\n" + "OpDecorate %group0 Flat\n" + "OpDecorate %group0 Offset 8\n" + "OpDecorate %group1 NoPerspective\n" + "OpDecorate %group1 Offset 16\n" + "%group0 = OpDecorationGroup\n" + "%group1 = OpDecorationGroup\n" + "OpGroupMemberDecorate %group0 %Point 0\n" + "OpGroupMemberDecorate %group1 %Point 2\n", + "OpMemberDecorate %Point 0 Flat\n" + "OpMemberDecorate %Point 0 Offset 8\n" + "OpMemberDecorate %Point 2 NoPerspective\n" + "OpMemberDecorate %Point 2 Offset 16\n"}, + })); + +INSTANTIATE_TEST_SUITE_P(UnrelatedDecorations, FlattenDecorationTest, + ::testing::ValuesIn(std::vector{ + // A non-group non-member decoration is untouched. + {"OpDecorate %hue Centroid\n" + "OpDecorate %saturation Flat\n", + "OpDecorate %hue Centroid\n" + "OpDecorate %saturation Flat\n"}, + // A non-group member decoration is untouched. + {"OpMemberDecorate %Point 0 Offset 0\n" + "OpMemberDecorate %Point 1 Offset 4\n" + "OpMemberDecorate %Point 1 Flat\n", + "OpMemberDecorate %Point 0 Offset 0\n" + "OpMemberDecorate %Point 1 Offset 4\n" + "OpMemberDecorate %Point 1 Flat\n"}, + // A non-group non-member decoration survives any + // replacement of group decorations. + {"OpName %group \"group\"\n" + "OpDecorate %group Flat\n" + "OpDecorate %hue Centroid\n" + "OpDecorate %group NoPerspective\n" + "%group = OpDecorationGroup\n" + "OpGroupDecorate %group %hue %saturation\n", + "OpDecorate %hue Flat\n" + "OpDecorate %saturation Flat\n" + "OpDecorate %hue Centroid\n" + "OpDecorate %hue NoPerspective\n" + "OpDecorate %saturation NoPerspective\n"}, + // A non-group member decoration survives any + // replacement of group decorations. + {"OpDecorate %group Offset 0\n" + "OpDecorate %group Flat\n" + "OpMemberDecorate %Point 1 Offset 4\n" + "%group = OpDecorationGroup\n" + "OpGroupMemberDecorate %group %Point 0\n", + "OpMemberDecorate %Point 0 Offset 0\n" + "OpMemberDecorate %Point 0 Flat\n" + "OpMemberDecorate %Point 1 Offset 4\n"}, + })); + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/fold_spec_const_op_composite_test.cpp b/third_party/spirv-tools/test/opt/fold_spec_const_op_composite_test.cpp new file mode 100644 index 0000000..7eddf7e --- /dev/null +++ b/third_party/spirv-tools/test/opt/fold_spec_const_op_composite_test.cpp @@ -0,0 +1,1513 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using FoldSpecConstantOpAndCompositePassBasicTest = PassTest<::testing::Test>; + +TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, Empty) { + SinglePassRunAndCheck( + "", "", /* skip_nop = */ true); +} + +// A test of the basic functionality of FoldSpecConstantOpAndCompositePass. +// A spec constant defined with an integer addition operation should be folded +// to a normal constant with fixed value. +TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, Basic) { + AssemblyBuilder builder; + builder.AppendTypesConstantsGlobals({ + // clang-format off + "%int = OpTypeInt 32 1", + "%frozen_spec_const_int = OpConstant %int 1", + "%const_int = OpConstant %int 2", + // Folding target: + "%spec_add = OpSpecConstantOp %int IAdd %frozen_spec_const_int %const_int", + // clang-format on + }); + + std::vector expected = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %void \"void\"", + "OpName %main_func_type \"main_func_type\"", + "OpName %main \"main\"", + "OpName %main_func_entry_block \"main_func_entry_block\"", + "OpName %int \"int\"", + "OpName %frozen_spec_const_int \"frozen_spec_const_int\"", + "OpName %const_int \"const_int\"", + "OpName %spec_add \"spec_add\"", + "%void = OpTypeVoid", + "%main_func_type = OpTypeFunction %void", + "%int = OpTypeInt 32 1", +"%frozen_spec_const_int = OpConstant %int 1", + "%const_int = OpConstant %int 2", + // The SpecConstantOp IAdd instruction should be replace by OpConstant + // instruction: + "%spec_add = OpConstant %int 3", + "%main = OpFunction %void None %main_func_type", +"%main_func_entry_block = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + builder.GetCode(), JoinAllInsts(expected), /* skip_nop = */ true); +} + +// A test of skipping folding an instruction when the instruction result type +// has decorations. +TEST_F(FoldSpecConstantOpAndCompositePassBasicTest, + SkipWhenTypeHasDecorations) { + AssemblyBuilder builder; + builder + .AppendAnnotations({ + // clang-format off + "OpDecorate %int RelaxedPrecision", + // clang-format on + }) + .AppendTypesConstantsGlobals({ + // clang-format off + "%int = OpTypeInt 32 1", + "%frozen_spec_const_int = OpConstant %int 1", + "%const_int = OpConstant %int 2", + // The following spec constant should not be folded as the result type + // has relaxed precision decoration. + "%spec_add = OpSpecConstantOp %int IAdd %frozen_spec_const_int %const_int", + // clang-format on + }); + + SinglePassRunAndCheck( + builder.GetCode(), builder.GetCode(), /* skip_nop = */ true); +} + +// All types and some common constants that are potentially required in +// FoldSpecConstantOpAndCompositeTest. +std::vector CommonTypesAndConstants() { + return std::vector{ + // clang-format off + // scalar types + "%bool = OpTypeBool", + "%ushort = OpTypeInt 16 0", + "%short = OpTypeInt 16 1", + "%uint = OpTypeInt 32 0", + "%int = OpTypeInt 32 1", + "%ulong = OpTypeInt 64 0", + "%long = OpTypeInt 64 1", + "%float = OpTypeFloat 32", + "%double = OpTypeFloat 64", + // vector types + "%v2bool = OpTypeVector %bool 2", + "%v2uint = OpTypeVector %uint 2", + "%v2int = OpTypeVector %int 2", + "%v3int = OpTypeVector %int 3", + "%v4int = OpTypeVector %int 4", + "%v2long = OpTypeVector %long 2", + "%v2ulong = OpTypeVector %ulong 2", + "%v2float = OpTypeVector %float 2", + "%v2double = OpTypeVector %double 2", + // variable pointer types + "%_pf_bool = OpTypePointer Function %bool", + "%_pf_uint = OpTypePointer Function %uint", + "%_pf_int = OpTypePointer Function %int", + "%_pf_float = OpTypePointer Function %float", + "%_pf_double = OpTypePointer Function %double", + "%_pf_v2int = OpTypePointer Function %v2int", + "%_pf_v2float = OpTypePointer Function %v2float", + "%_pf_v2double = OpTypePointer Function %v2double", + // struct types + "%inner_struct = OpTypeStruct %bool %int %float", + "%outer_struct = OpTypeStruct %inner_struct %int", + "%flat_struct = OpTypeStruct %bool %int %float", + + // common constants + // scalar constants: + "%bool_true = OpConstantTrue %bool", + "%bool_false = OpConstantFalse %bool", + "%bool_null = OpConstantNull %bool", + "%signed_zero = OpConstant %int 0", + "%unsigned_zero = OpConstant %uint 0", + "%long_zero = OpConstant %long 0", + "%ulong_zero = OpConstant %ulong 0", + "%signed_one = OpConstant %int 1", + "%unsigned_one = OpConstant %uint 1", + "%signed_two = OpConstant %int 2", + "%unsigned_two = OpConstant %uint 2", + "%signed_three = OpConstant %int 3", + "%unsigned_three = OpConstant %uint 3", + "%signed_null = OpConstantNull %int", + "%unsigned_null = OpConstantNull %uint", + "%signed_minus_one = OpConstant %int -1", + // vector constants: + "%bool_true_vec = OpConstantComposite %v2bool %bool_true %bool_true", + "%bool_false_vec = OpConstantComposite %v2bool %bool_false %bool_false", + "%bool_null_vec = OpConstantNull %v2bool", + "%signed_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero", + "%unsigned_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", + "%signed_one_vec = OpConstantComposite %v2int %signed_one %signed_one", + "%unsigned_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + "%signed_two_vec = OpConstantComposite %v2int %signed_two %signed_two", + "%unsigned_two_vec = OpConstantComposite %v2uint %unsigned_two %unsigned_two", + "%signed_three_vec = OpConstantComposite %v2int %signed_three %signed_three", + "%unsigned_three_vec = OpConstantComposite %v2uint %unsigned_three %unsigned_three", + "%signed_null_vec = OpConstantNull %v2int", + "%unsigned_null_vec = OpConstantNull %v2uint", + "%signed_minus_one_vec = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", + "%v4int_0_1_2_3 = OpConstantComposite %v4int %signed_zero %signed_one %signed_two %signed_three", + // clang-format on + }; +} + +// A helper function to strip OpName instructions from the given string of +// disassembly code. Returns the string with all OpName instruction stripped. +std::string StripOpNameInstructions(const std::string& str) { + std::stringstream ss(str); + std::ostringstream oss; + std::string inst_str; + while (std::getline(ss, inst_str, '\n')) { + if (inst_str.find("OpName %") == std::string::npos) { + oss << inst_str << '\n'; + } + } + return oss.str(); +} + +struct FoldSpecConstantOpAndCompositePassTestCase { + // Original constants with unfolded spec constants. + std::vector original; + // Expected cosntants after folding. + std::vector expected; +}; + +using FoldSpecConstantOpAndCompositePassTest = PassTest< + ::testing::TestWithParam>; + +TEST_P(FoldSpecConstantOpAndCompositePassTest, ParamTestCase) { + AssemblyBuilder test_code_builder, expected_code_builder; + const auto& tc = GetParam(); + test_code_builder.AppendTypesConstantsGlobals(CommonTypesAndConstants()); + test_code_builder.AppendTypesConstantsGlobals(tc.original); + expected_code_builder.AppendTypesConstantsGlobals(CommonTypesAndConstants()); + expected_code_builder.AppendTypesConstantsGlobals(tc.expected); + const std::string original = test_code_builder.GetCode(); + const std::string expected = expected_code_builder.GetCode(); + + // Run the optimization and get the result code in disassembly. + std::string optimized; + auto status = Pass::Status::SuccessWithoutChange; + std::tie(optimized, status) = + SinglePassRunAndDisassemble( + original, /* skip_nop = */ true, /* do_validation = */ false); + + // Check the optimized code, but ignore the OpName instructions. + EXPECT_NE(Pass::Status::Failure, status); + EXPECT_EQ( + StripOpNameInstructions(expected) == StripOpNameInstructions(original), + status == Pass::Status::SuccessWithoutChange); + EXPECT_EQ(StripOpNameInstructions(expected), + StripOpNameInstructions(optimized)); +} + +// Tests that OpSpecConstantComposite opcodes are replace with +// OpConstantComposite correctly. +INSTANTIATE_TEST_SUITE_P( + Composite, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // normal vector + { + // original + { + "%spec_v2bool = OpSpecConstantComposite %v2bool %bool_true %bool_false", + "%spec_v2uint = OpSpecConstantComposite %v2uint %unsigned_one %unsigned_one", + "%spec_v2int_a = OpSpecConstantComposite %v2int %signed_one %signed_two", + // Spec constants whose value can not be fully resolved should + // not be processed. + "%spec_int = OpSpecConstant %int 99", + "%spec_v2int_b = OpSpecConstantComposite %v2int %signed_one %spec_int", + }, + // expected + { + "%spec_v2bool = OpConstantComposite %v2bool %bool_true %bool_false", + "%spec_v2uint = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + "%spec_v2int_a = OpConstantComposite %v2int %signed_one %signed_two", + "%spec_int = OpSpecConstant %int 99", + "%spec_v2int_b = OpSpecConstantComposite %v2int %signed_one %spec_int", + }, + }, + // vector with null constants + { + // original + { + "%null_bool = OpConstantNull %bool", + "%null_int = OpConstantNull %int", + "%spec_v2bool = OpSpecConstantComposite %v2bool %null_bool %null_bool", + "%spec_v3int = OpSpecConstantComposite %v3int %null_int %null_int %null_int", + "%spec_v4int = OpSpecConstantComposite %v4int %null_int %null_int %null_int %null_int", + }, + // expected + { + "%null_bool = OpConstantNull %bool", + "%null_int = OpConstantNull %int", + "%spec_v2bool = OpConstantComposite %v2bool %null_bool %null_bool", + "%spec_v3int = OpConstantComposite %v3int %null_int %null_int %null_int", + "%spec_v4int = OpConstantComposite %v4int %null_int %null_int %null_int %null_int", + }, + }, + // flat struct + { + // original + { + "%float_1 = OpConstant %float 1", + "%flat_1 = OpSpecConstantComposite %flat_struct %bool_true %signed_null %float_1", + // following struct should not be folded as the value of + // %spec_float is not determined. + "%spec_float = OpSpecConstant %float 1", + "%flat_2 = OpSpecConstantComposite %flat_struct %bool_true %signed_one %spec_float", + }, + // expected + { + "%float_1 = OpConstant %float 1", + "%flat_1 = OpConstantComposite %flat_struct %bool_true %signed_null %float_1", + "%spec_float = OpSpecConstant %float 1", + "%flat_2 = OpSpecConstantComposite %flat_struct %bool_true %signed_one %spec_float", + } + }, + // nested struct + { + // original + { + "%float_1 = OpConstant %float 1", + "%inner_1 = OpSpecConstantComposite %inner_struct %bool_true %signed_null %float_1", + "%outer_1 = OpSpecConstantComposite %outer_struct %inner_1 %signed_one", + // following structs should not be folded as the value of + // %spec_float is not determined. + "%spec_float = OpSpecConstant %float 1", + "%inner_2 = OpSpecConstantComposite %inner_struct %bool_true %signed_null %spec_float", + "%outer_2 = OpSpecConstantComposite %outer_struct %inner_2 %signed_one", + }, + // expected + { + "%float_1 = OpConstant %float 1", + "%inner_1 = OpConstantComposite %inner_struct %bool_true %signed_null %float_1", + "%outer_1 = OpConstantComposite %outer_struct %inner_1 %signed_one", + "%spec_float = OpSpecConstant %float 1", + "%inner_2 = OpSpecConstantComposite %inner_struct %bool_true %signed_null %spec_float", + "%outer_2 = OpSpecConstantComposite %outer_struct %inner_2 %signed_one", + } + }, + // composite constants touched by OpUndef should be skipped + { + // original + { + "%undef = OpUndef %float", + "%inner = OpConstantComposite %inner_struct %bool_true %signed_one %undef", + "%outer = OpSpecConstantComposite %outer_struct %inner %signed_one", + }, + // expected + { + "%undef = OpUndef %float", + "%inner = OpConstantComposite %inner_struct %bool_true %signed_one %undef", + "%outer = OpSpecConstantComposite %outer_struct %inner %signed_one", + }, + }, + // Fold an QuantizetoF16 instruction + { + // original + { + "%float_1 = OpConstant %float 1", + "%quant_float = OpSpecConstantOp %float QuantizeToF16 %float_1", + }, + // expected + { + "%float_1 = OpConstant %float 1", + "%quant_float = OpConstant %float 1", + }, + } + // clang-format on + }))); + +// Tests for operations that resulting in different types. +INSTANTIATE_TEST_SUITE_P( + Cast, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // int -> bool scalar + { + // original + { + "%spec_bool_t = OpSpecConstantOp %bool INotEqual %signed_three %signed_zero", + "%spec_bool_f = OpSpecConstantOp %bool INotEqual %signed_zero %signed_zero", + "%spec_bool_from_null = OpSpecConstantOp %bool INotEqual %signed_null %signed_zero", + }, + // expected + { + "%spec_bool_t = OpConstantTrue %bool", + "%spec_bool_f = OpConstantFalse %bool", + "%spec_bool_from_null = OpConstantFalse %bool", + }, + }, + + // uint -> bool scalar + { + // original + { + "%spec_bool_t = OpSpecConstantOp %bool INotEqual %unsigned_three %unsigned_zero", + "%spec_bool_f = OpSpecConstantOp %bool INotEqual %unsigned_zero %unsigned_zero", + "%spec_bool_from_null = OpSpecConstantOp %bool INotEqual %unsigned_null %unsigned_zero", + }, + // expected + { + "%spec_bool_t = OpConstantTrue %bool", + "%spec_bool_f = OpConstantFalse %bool", + "%spec_bool_from_null = OpConstantFalse %bool", + }, + }, + + // bool -> int scalar + { + // original + { + "%spec_int_one = OpSpecConstantOp %int Select %bool_true %signed_one %signed_zero", + "%spec_int_zero = OpSpecConstantOp %int Select %bool_false %signed_one %signed_zero", + "%spec_int_from_null = OpSpecConstantOp %int Select %bool_null %signed_one %signed_zero", + }, + // expected + { + "%spec_int_one = OpConstant %int 1", + "%spec_int_zero = OpConstant %int 0", + "%spec_int_from_null = OpConstant %int 0", + }, + }, + + // uint -> int scalar + { + // original + { + "%spec_int_one = OpSpecConstantOp %int IAdd %unsigned_one %signed_zero", + "%spec_int_zero = OpSpecConstantOp %int IAdd %unsigned_zero %signed_zero", + "%spec_int_from_null = OpSpecConstantOp %int IAdd %unsigned_null %unsigned_zero", + }, + // expected + { + "%spec_int_one = OpConstant %int 1", + "%spec_int_zero = OpConstant %int 0", + "%spec_int_from_null = OpConstant %int 0", + }, + }, + + // bool -> uint scalar + { + // original + { + "%spec_uint_one = OpSpecConstantOp %uint Select %bool_true %unsigned_one %unsigned_zero", + "%spec_uint_zero = OpSpecConstantOp %uint Select %bool_false %unsigned_one %unsigned_zero", + "%spec_uint_from_null = OpSpecConstantOp %uint Select %bool_null %unsigned_one %unsigned_zero", + }, + // expected + { + "%spec_uint_one = OpConstant %uint 1", + "%spec_uint_zero = OpConstant %uint 0", + "%spec_uint_from_null = OpConstant %uint 0", + }, + }, + + // int -> uint scalar + { + // original + { + "%spec_uint_one = OpSpecConstantOp %uint IAdd %signed_one %unsigned_zero", + "%spec_uint_zero = OpSpecConstantOp %uint IAdd %signed_zero %unsigned_zero", + "%spec_uint_from_null = OpSpecConstantOp %uint IAdd %signed_null %unsigned_zero", + }, + // expected + { + "%spec_uint_one = OpConstant %uint 1", + "%spec_uint_zero = OpConstant %uint 0", + "%spec_uint_from_null = OpConstant %uint 0", + }, + }, + + // int -> bool vector + { + // original + { + "%spec_bool_t_vec = OpSpecConstantOp %v2bool INotEqual %signed_three_vec %signed_zero_vec", + "%spec_bool_f_vec = OpSpecConstantOp %v2bool INotEqual %signed_zero_vec %signed_zero_vec", + "%spec_bool_from_null = OpSpecConstantOp %v2bool INotEqual %signed_null_vec %signed_zero_vec", + }, + // expected + { + "%true = OpConstantTrue %bool", + "%true_0 = OpConstantTrue %bool", + "%spec_bool_t_vec = OpConstantComposite %v2bool %bool_true %bool_true", + "%false = OpConstantFalse %bool", + "%false_0 = OpConstantFalse %bool", + "%spec_bool_f_vec = OpConstantComposite %v2bool %bool_false %bool_false", + "%false_1 = OpConstantFalse %bool", + "%false_2 = OpConstantFalse %bool", + "%spec_bool_from_null = OpConstantComposite %v2bool %bool_false %bool_false", + }, + }, + + // uint -> bool vector + { + // original + { + "%spec_bool_t_vec = OpSpecConstantOp %v2bool INotEqual %unsigned_three_vec %unsigned_zero_vec", + "%spec_bool_f_vec = OpSpecConstantOp %v2bool INotEqual %unsigned_zero_vec %unsigned_zero_vec", + "%spec_bool_from_null = OpSpecConstantOp %v2bool INotEqual %unsigned_null_vec %unsigned_zero_vec", + }, + // expected + { + "%true = OpConstantTrue %bool", + "%true_0 = OpConstantTrue %bool", + "%spec_bool_t_vec = OpConstantComposite %v2bool %bool_true %bool_true", + "%false = OpConstantFalse %bool", + "%false_0 = OpConstantFalse %bool", + "%spec_bool_f_vec = OpConstantComposite %v2bool %bool_false %bool_false", + "%false_1 = OpConstantFalse %bool", + "%false_2 = OpConstantFalse %bool", + "%spec_bool_from_null = OpConstantComposite %v2bool %bool_false %bool_false", + }, + }, + + // bool -> int vector + { + // original + { + "%spec_int_one_vec = OpSpecConstantOp %v2int Select %bool_true_vec %signed_one_vec %signed_zero_vec", + "%spec_int_zero_vec = OpSpecConstantOp %v2int Select %bool_false_vec %signed_one_vec %signed_zero_vec", + "%spec_int_from_null = OpSpecConstantOp %v2int Select %bool_null_vec %signed_one_vec %signed_zero_vec", + }, + // expected + { + "%int_1 = OpConstant %int 1", + "%int_1_0 = OpConstant %int 1", + "%spec_int_one_vec = OpConstantComposite %v2int %signed_one %signed_one", + "%int_0 = OpConstant %int 0", + "%int_0_0 = OpConstant %int 0", + "%spec_int_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero", + "%int_0_1 = OpConstant %int 0", + "%int_0_2 = OpConstant %int 0", + "%spec_int_from_null = OpConstantComposite %v2int %signed_zero %signed_zero", + }, + }, + + // uint -> int vector + { + // original + { + "%spec_int_one_vec = OpSpecConstantOp %v2int IAdd %unsigned_one_vec %signed_zero_vec", + "%spec_int_zero_vec = OpSpecConstantOp %v2int IAdd %unsigned_zero_vec %signed_zero_vec", + "%spec_int_from_null = OpSpecConstantOp %v2int IAdd %unsigned_null_vec %signed_zero_vec", + }, + // expected + { + "%int_1 = OpConstant %int 1", + "%int_1_0 = OpConstant %int 1", + "%spec_int_one_vec = OpConstantComposite %v2int %signed_one %signed_one", + "%int_0 = OpConstant %int 0", + "%int_0_0 = OpConstant %int 0", + "%spec_int_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero", + "%int_0_1 = OpConstant %int 0", + "%int_0_2 = OpConstant %int 0", + "%spec_int_from_null = OpConstantComposite %v2int %signed_zero %signed_zero", + }, + }, + + // bool -> uint vector + { + // original + { + "%spec_uint_one_vec = OpSpecConstantOp %v2uint Select %bool_true_vec %unsigned_one_vec %unsigned_zero_vec", + "%spec_uint_zero_vec = OpSpecConstantOp %v2uint Select %bool_false_vec %unsigned_one_vec %unsigned_zero_vec", + "%spec_uint_from_null = OpSpecConstantOp %v2uint Select %bool_null_vec %unsigned_one_vec %unsigned_zero_vec", + }, + // expected + { + "%uint_1 = OpConstant %uint 1", + "%uint_1_0 = OpConstant %uint 1", + "%spec_uint_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + "%uint_0 = OpConstant %uint 0", + "%uint_0_0 = OpConstant %uint 0", + "%spec_uint_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", + "%uint_0_1 = OpConstant %uint 0", + "%uint_0_2 = OpConstant %uint 0", + "%spec_uint_from_null = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", + }, + }, + + // int -> uint vector + { + // original + { + "%spec_uint_one_vec = OpSpecConstantOp %v2uint IAdd %signed_one_vec %unsigned_zero_vec", + "%spec_uint_zero_vec = OpSpecConstantOp %v2uint IAdd %signed_zero_vec %unsigned_zero_vec", + "%spec_uint_from_null = OpSpecConstantOp %v2uint IAdd %signed_null_vec %unsigned_zero_vec", + }, + // expected + { + "%uint_1 = OpConstant %uint 1", + "%uint_1_0 = OpConstant %uint 1", + "%spec_uint_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + "%uint_0 = OpConstant %uint 0", + "%uint_0_0 = OpConstant %uint 0", + "%spec_uint_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", + "%uint_0_1 = OpConstant %uint 0", + "%uint_0_2 = OpConstant %uint 0", + "%spec_uint_from_null = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", + }, + }, + + // UConvert scalar + { + // original + { + "%spec_uint_zero = OpSpecConstantOp %uint UConvert %bool_false", + "%spec_uint_one = OpSpecConstantOp %uint UConvert %bool_true", + "%spec_ulong_zero = OpSpecConstantOp %ulong UConvert %unsigned_zero", + "%spec_ulong_one = OpSpecConstantOp %ulong UConvert %unsigned_one", + "%spec_short_zero = OpSpecConstantOp %ushort UConvert %unsigned_zero", + "%spec_short_one = OpSpecConstantOp %ushort UConvert %unsigned_one", + "%uint_max = OpConstant %uint 4294967295", + "%spec_ushort_max = OpSpecConstantOp %ushort UConvert %uint_max", + "%uint_0xDDDDDDDD = OpConstant %uint 3722304989", + "%spec_ushort_0xDDDD = OpSpecConstantOp %ushort UConvert %uint_0xDDDDDDDD", + }, + // expected + { + "%spec_uint_zero = OpConstant %uint 0", + "%spec_uint_one = OpConstant %uint 1", + "%spec_ulong_zero = OpConstant %ulong 0", + "%spec_ulong_one = OpConstant %ulong 1", + "%spec_short_zero = OpConstant %ushort 0", + "%spec_short_one = OpConstant %ushort 1", + "%uint_max = OpConstant %uint 4294967295", + "%spec_ushort_max = OpConstant %ushort 65535", + "%uint_0xDDDDDDDD = OpConstant %uint 3722304989", + "%spec_ushort_0xDDDD = OpConstant %ushort 56797", + }, + }, + + // SConvert scalar + { + // original + { + "%spec_long_zero = OpSpecConstantOp %long SConvert %signed_zero", + "%spec_long_one = OpSpecConstantOp %long SConvert %signed_one", + "%spec_long_minus_one = OpSpecConstantOp %long SConvert %signed_minus_one", + "%spec_short_minus_one_trunc = OpSpecConstantOp %short SConvert %signed_minus_one", + "%int_2_to_17_minus_one = OpConstant %int 131071", + "%spec_short_minus_one_trunc2 = OpSpecConstantOp %short SConvert %int_2_to_17_minus_one", + }, + // expected + { + "%spec_long_zero = OpConstant %long 0", + "%spec_long_one = OpConstant %long 1", + "%spec_long_minus_one = OpConstant %long -1", + "%spec_short_minus_one_trunc = OpConstant %short -1", + "%int_2_to_17_minus_one = OpConstant %int 131071", + "%spec_short_minus_one_trunc2 = OpConstant %short -1", + }, + }, + + // UConvert vector + { + // original + { + "%spec_v2uint_zero = OpSpecConstantOp %v2uint UConvert %bool_false_vec", + "%spec_v2uint_one = OpSpecConstantOp %v2uint UConvert %bool_true_vec", + "%spec_v2ulong_zero = OpSpecConstantOp %v2ulong UConvert %unsigned_zero_vec", + "%spec_v2ulong_one = OpSpecConstantOp %v2ulong UConvert %unsigned_one_vec", + }, + // expected + { + "%uint_0 = OpConstant %uint 0", + "%uint_0_0 = OpConstant %uint 0", + "%spec_v2uint_zero = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", + "%uint_1 = OpConstant %uint 1", + "%uint_1_0 = OpConstant %uint 1", + "%spec_v2uint_one = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + "%ulong_0 = OpConstant %ulong 0", + "%ulong_0_0 = OpConstant %ulong 0", + "%spec_v2ulong_zero = OpConstantComposite %v2ulong %ulong_zero %ulong_zero", + "%ulong_1 = OpConstant %ulong 1", + "%ulong_1_0 = OpConstant %ulong 1", + "%spec_v2ulong_one = OpConstantComposite %v2ulong %ulong_1 %ulong_1", + }, + }, + + // SConvert vector + { + // original + { + "%spec_v2long_zero = OpSpecConstantOp %v2long SConvert %signed_zero_vec", + "%spec_v2long_one = OpSpecConstantOp %v2long SConvert %signed_one_vec", + "%spec_v2long_minus_one = OpSpecConstantOp %v2long SConvert %signed_minus_one_vec", + }, + // expected + { + "%long_0 = OpConstant %long 0", + "%long_0_0 = OpConstant %long 0", + "%spec_v2long_zero = OpConstantComposite %v2long %long_zero %long_zero", + "%long_1 = OpConstant %long 1", + "%long_1_0 = OpConstant %long 1", + "%spec_v2long_one = OpConstantComposite %v2long %long_1 %long_1", + "%long_n1 = OpConstant %long -1", + "%long_n1_0 = OpConstant %long -1", + "%spec_v2long_minus_one = OpConstantComposite %v2long %long_n1 %long_n1", + }, + }, + // clang-format on + }))); + +// Tests about boolean scalar logical operations and comparison operations with +// scalar int/uint type. +INSTANTIATE_TEST_SUITE_P( + Logical, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // scalar integer comparison + { + // original + { + "%int_minus_1 = OpConstant %int -1", + + "%slt_0_1 = OpSpecConstantOp %bool SLessThan %signed_zero %signed_one", + "%sgt_0_1 = OpSpecConstantOp %bool SGreaterThan %signed_zero %signed_one", + "%sle_2_2 = OpSpecConstantOp %bool SLessThanEqual %signed_two %signed_two", + "%sge_2_1 = OpSpecConstantOp %bool SGreaterThanEqual %signed_two %signed_one", + "%sge_2_null = OpSpecConstantOp %bool SGreaterThanEqual %signed_two %signed_null", + "%sge_minus_1_null = OpSpecConstantOp %bool SGreaterThanEqual %int_minus_1 %signed_null", + + "%ult_0_1 = OpSpecConstantOp %bool ULessThan %unsigned_zero %unsigned_one", + "%ugt_0_1 = OpSpecConstantOp %bool UGreaterThan %unsigned_zero %unsigned_one", + "%ule_2_3 = OpSpecConstantOp %bool ULessThanEqual %unsigned_two %unsigned_three", + "%uge_1_1 = OpSpecConstantOp %bool UGreaterThanEqual %unsigned_one %unsigned_one", + "%uge_2_null = OpSpecConstantOp %bool UGreaterThanEqual %unsigned_two %unsigned_null", + "%uge_minus_1_null = OpSpecConstantOp %bool UGreaterThanEqual %int_minus_1 %unsigned_null", + }, + // expected + { + "%int_minus_1 = OpConstant %int -1", + + "%slt_0_1 = OpConstantTrue %bool", + "%sgt_0_1 = OpConstantFalse %bool", + "%sle_2_2 = OpConstantTrue %bool", + "%sge_2_1 = OpConstantTrue %bool", + "%sge_2_null = OpConstantTrue %bool", + "%sge_minus_1_null = OpConstantFalse %bool", + + "%ult_0_1 = OpConstantTrue %bool", + "%ugt_0_1 = OpConstantFalse %bool", + "%ule_2_3 = OpConstantTrue %bool", + "%uge_1_1 = OpConstantTrue %bool", + "%uge_2_null = OpConstantTrue %bool", + "%uge_minus_1_null = OpConstantTrue %bool", + }, + }, + // Logical and, or, xor. + { + // original + { + "%logical_or = OpSpecConstantOp %bool LogicalOr %bool_true %bool_false", + "%logical_and = OpSpecConstantOp %bool LogicalAnd %bool_true %bool_false", + "%logical_not = OpSpecConstantOp %bool LogicalNot %bool_true", + "%logical_eq = OpSpecConstantOp %bool LogicalEqual %bool_true %bool_true", + "%logical_neq = OpSpecConstantOp %bool LogicalNotEqual %bool_true %bool_true", + "%logical_and_null = OpSpecConstantOp %bool LogicalAnd %bool_true %bool_null", + }, + // expected + { + "%logical_or = OpConstantTrue %bool", + "%logical_and = OpConstantFalse %bool", + "%logical_not = OpConstantFalse %bool", + "%logical_eq = OpConstantTrue %bool", + "%logical_neq = OpConstantFalse %bool", + "%logical_and_null = OpConstantFalse %bool", + }, + }, + // clang-format on + }))); + +// Tests about arithmetic operations for scalar int and uint types. +INSTANTIATE_TEST_SUITE_P( + ScalarArithmetic, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // scalar integer negate + { + // original + { + "%int_minus_1 = OpSpecConstantOp %int SNegate %signed_one", + "%int_minus_2 = OpSpecConstantOp %int SNegate %signed_two", + "%int_neg_null = OpSpecConstantOp %int SNegate %signed_null", + "%int_max = OpConstant %int 2147483647", + "%int_neg_max = OpSpecConstantOp %int SNegate %int_max", + }, + // expected + { + "%int_minus_1 = OpConstant %int -1", + "%int_minus_2 = OpConstant %int -2", + "%int_neg_null = OpConstant %int 0", + "%int_max = OpConstant %int 2147483647", + "%int_neg_max = OpConstant %int -2147483647", + }, + }, + // scalar integer not + { + // original + { + "%uint_4294967294 = OpSpecConstantOp %uint Not %unsigned_one", + "%uint_4294967293 = OpSpecConstantOp %uint Not %unsigned_two", + "%uint_neg_null = OpSpecConstantOp %uint Not %unsigned_null", + }, + // expected + { + "%uint_4294967294 = OpConstant %uint 4294967294", + "%uint_4294967293 = OpConstant %uint 4294967293", + "%uint_neg_null = OpConstant %uint 4294967295", + }, + }, + // scalar integer add, sub, mul, div + { + // original + { + "%signed_max = OpConstant %int 2147483647", + "%signed_min = OpConstant %int -2147483648", + + "%spec_int_iadd = OpSpecConstantOp %int IAdd %signed_three %signed_two", + "%spec_int_isub = OpSpecConstantOp %int ISub %signed_one %spec_int_iadd", + "%spec_int_sdiv = OpSpecConstantOp %int SDiv %spec_int_isub %signed_two", + "%spec_int_imul = OpSpecConstantOp %int IMul %spec_int_sdiv %signed_three", + "%spec_int_iadd_null = OpSpecConstantOp %int IAdd %spec_int_imul %signed_null", + "%spec_int_imul_null = OpSpecConstantOp %int IMul %spec_int_iadd_null %signed_null", + "%spec_int_iadd_overflow = OpSpecConstantOp %int IAdd %signed_max %signed_three", + "%spec_int_isub_overflow = OpSpecConstantOp %int ISub %signed_min %signed_three", + + "%spec_uint_iadd = OpSpecConstantOp %uint IAdd %unsigned_three %unsigned_two", + "%spec_uint_isub = OpSpecConstantOp %uint ISub %unsigned_one %spec_uint_iadd", + "%spec_uint_udiv = OpSpecConstantOp %uint UDiv %spec_uint_isub %unsigned_three", + "%spec_uint_imul = OpSpecConstantOp %uint IMul %spec_uint_udiv %unsigned_two", + "%spec_uint_isub_null = OpSpecConstantOp %uint ISub %spec_uint_imul %signed_null", + }, + // expected + { + "%signed_max = OpConstant %int 2147483647", + "%signed_min = OpConstant %int -2147483648", + + "%spec_int_iadd = OpConstant %int 5", + "%spec_int_isub = OpConstant %int -4", + "%spec_int_sdiv = OpConstant %int -2", + "%spec_int_imul = OpConstant %int -6", + "%spec_int_iadd_null = OpConstant %int -6", + "%spec_int_imul_null = OpConstant %int 0", + "%spec_int_iadd_overflow = OpConstant %int -2147483646", + "%spec_int_isub_overflow = OpConstant %int 2147483645", + + "%spec_uint_iadd = OpConstant %uint 5", + "%spec_uint_isub = OpConstant %uint 4294967292", + "%spec_uint_udiv = OpConstant %uint 1431655764", + "%spec_uint_imul = OpConstant %uint 2863311528", + "%spec_uint_isub_null = OpConstant %uint 2863311528", + }, + }, + // scalar integer rem, mod + { + // original + { + // common constants + "%int_7 = OpConstant %int 7", + "%uint_7 = OpConstant %uint 7", + "%int_minus_7 = OpConstant %int -7", + "%int_minus_3 = OpConstant %int -3", + + // srem + "%7_srem_3 = OpSpecConstantOp %int SRem %int_7 %signed_three", + "%minus_7_srem_3 = OpSpecConstantOp %int SRem %int_minus_7 %signed_three", + "%7_srem_minus_3 = OpSpecConstantOp %int SRem %int_7 %int_minus_3", + "%minus_7_srem_minus_3 = OpSpecConstantOp %int SRem %int_minus_7 %int_minus_3", + // smod + "%7_smod_3 = OpSpecConstantOp %int SMod %int_7 %signed_three", + "%minus_7_smod_3 = OpSpecConstantOp %int SMod %int_minus_7 %signed_three", + "%7_smod_minus_3 = OpSpecConstantOp %int SMod %int_7 %int_minus_3", + "%minus_7_smod_minus_3 = OpSpecConstantOp %int SMod %int_minus_7 %int_minus_3", + // umod + "%7_umod_3 = OpSpecConstantOp %uint UMod %uint_7 %unsigned_three", + // null constant + "%null_srem_3 = OpSpecConstantOp %int SRem %signed_null %signed_three", + "%null_smod_3 = OpSpecConstantOp %int SMod %signed_null %signed_three", + "%null_umod_3 = OpSpecConstantOp %uint UMod %unsigned_null %unsigned_three", + }, + // expected + { + // common constants + "%int_7 = OpConstant %int 7", + "%uint_7 = OpConstant %uint 7", + "%int_minus_7 = OpConstant %int -7", + "%int_minus_3 = OpConstant %int -3", + + // srem + "%7_srem_3 = OpConstant %int 1", + "%minus_7_srem_3 = OpConstant %int -1", + "%7_srem_minus_3 = OpConstant %int 1", + "%minus_7_srem_minus_3 = OpConstant %int -1", + // smod + "%7_smod_3 = OpConstant %int 1", + "%minus_7_smod_3 = OpConstant %int 2", + "%7_smod_minus_3 = OpConstant %int -2", + "%minus_7_smod_minus_3 = OpConstant %int -1", + // umod + "%7_umod_3 = OpConstant %uint 1", + // null constant + "%null_srem_3 = OpConstant %int 0", + "%null_smod_3 = OpConstant %int 0", + "%null_umod_3 = OpConstant %uint 0", + }, + }, + // scalar integer bitwise and shift + { + // original + { + // bitwise + "%xor_1_3 = OpSpecConstantOp %int BitwiseXor %signed_one %signed_three", + "%and_1_2 = OpSpecConstantOp %int BitwiseAnd %signed_one %xor_1_3", + "%or_1_2 = OpSpecConstantOp %int BitwiseOr %signed_one %xor_1_3", + "%xor_3_null = OpSpecConstantOp %int BitwiseXor %or_1_2 %signed_null", + + // shift + "%unsigned_31 = OpConstant %uint 31", + "%unsigned_left_shift_max = OpSpecConstantOp %uint ShiftLeftLogical %unsigned_one %unsigned_31", + "%unsigned_right_shift_logical = OpSpecConstantOp %uint ShiftRightLogical %unsigned_left_shift_max %unsigned_31", + "%signed_right_shift_arithmetic = OpSpecConstantOp %int ShiftRightArithmetic %unsigned_left_shift_max %unsigned_31", + "%left_shift_null_31 = OpSpecConstantOp %uint ShiftLeftLogical %unsigned_null %unsigned_31", + "%right_shift_31_null = OpSpecConstantOp %uint ShiftRightLogical %unsigned_31 %unsigned_null", + }, + // expected + { + "%xor_1_3 = OpConstant %int 2", + "%and_1_2 = OpConstant %int 0", + "%or_1_2 = OpConstant %int 3", + "%xor_3_null = OpConstant %int 3", + + "%unsigned_31 = OpConstant %uint 31", + "%unsigned_left_shift_max = OpConstant %uint 2147483648", + "%unsigned_right_shift_logical = OpConstant %uint 1", + "%signed_right_shift_arithmetic = OpConstant %int -1", + "%left_shift_null_31 = OpConstant %uint 0", + "%right_shift_31_null = OpConstant %uint 31", + }, + }, + // Skip folding if any operands have undetermined value. + { + // original + { + "%spec_int = OpSpecConstant %int 1", + "%spec_iadd = OpSpecConstantOp %int IAdd %signed_three %spec_int", + }, + // expected + { + "%spec_int = OpSpecConstant %int 1", + "%spec_iadd = OpSpecConstantOp %int IAdd %signed_three %spec_int", + }, + }, + // clang-format on + }))); + +// Tests about arithmetic operations for vector int and uint types. +INSTANTIATE_TEST_SUITE_P( + VectorArithmetic, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // vector integer negate + { + // original + { + "%v2int_minus_1 = OpSpecConstantOp %v2int SNegate %signed_one_vec", + "%v2int_minus_2 = OpSpecConstantOp %v2int SNegate %signed_two_vec", + "%v2int_neg_null = OpSpecConstantOp %v2int SNegate %signed_null_vec", + }, + // expected + { + "%int_n1 = OpConstant %int -1", + "%int_n1_0 = OpConstant %int -1", + "%v2int_minus_1 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", + "%int_n2 = OpConstant %int -2", + "%int_n2_0 = OpConstant %int -2", + "%v2int_minus_2 = OpConstantComposite %v2int %int_n2 %int_n2", + "%int_0 = OpConstant %int 0", + "%int_0_0 = OpConstant %int 0", + "%v2int_neg_null = OpConstantComposite %v2int %signed_zero %signed_zero", + }, + }, + // vector integer (including null vetors) add, sub, div, mul + { + // original + { + "%spec_v2int_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %signed_two_vec", + "%spec_v2int_isub = OpSpecConstantOp %v2int ISub %signed_one_vec %spec_v2int_iadd", + "%spec_v2int_sdiv = OpSpecConstantOp %v2int SDiv %spec_v2int_isub %signed_two_vec", + "%spec_v2int_imul = OpSpecConstantOp %v2int IMul %spec_v2int_sdiv %signed_three_vec", + "%spec_v2int_iadd_null = OpSpecConstantOp %v2int IAdd %spec_v2int_imul %signed_null_vec", + + "%spec_v2uint_iadd = OpSpecConstantOp %v2uint IAdd %unsigned_three_vec %unsigned_two_vec", + "%spec_v2uint_isub = OpSpecConstantOp %v2uint ISub %unsigned_one_vec %spec_v2uint_iadd", + "%spec_v2uint_udiv = OpSpecConstantOp %v2uint UDiv %spec_v2uint_isub %unsigned_three_vec", + "%spec_v2uint_imul = OpSpecConstantOp %v2uint IMul %spec_v2uint_udiv %unsigned_two_vec", + "%spec_v2uint_isub_null = OpSpecConstantOp %v2uint ISub %spec_v2uint_imul %signed_null_vec", + }, + // expected + { + "%int_5 = OpConstant %int 5", + "%int_5_0 = OpConstant %int 5", + "%spec_v2int_iadd = OpConstantComposite %v2int %int_5 %int_5", + "%int_n4 = OpConstant %int -4", + "%int_n4_0 = OpConstant %int -4", + "%spec_v2int_isub = OpConstantComposite %v2int %int_n4 %int_n4", + "%int_n2 = OpConstant %int -2", + "%int_n2_0 = OpConstant %int -2", + "%spec_v2int_sdiv = OpConstantComposite %v2int %int_n2 %int_n2", + "%int_n6 = OpConstant %int -6", + "%int_n6_0 = OpConstant %int -6", + "%spec_v2int_imul = OpConstantComposite %v2int %int_n6 %int_n6", + "%int_n6_1 = OpConstant %int -6", + "%int_n6_2 = OpConstant %int -6", + "%spec_v2int_iadd_null = OpConstantComposite %v2int %int_n6 %int_n6", + + "%uint_5 = OpConstant %uint 5", + "%uint_5_0 = OpConstant %uint 5", + "%spec_v2uint_iadd = OpConstantComposite %v2uint %uint_5 %uint_5", + "%uint_4294967292 = OpConstant %uint 4294967292", + "%uint_4294967292_0 = OpConstant %uint 4294967292", + "%spec_v2uint_isub = OpConstantComposite %v2uint %uint_4294967292 %uint_4294967292", + "%uint_1431655764 = OpConstant %uint 1431655764", + "%uint_1431655764_0 = OpConstant %uint 1431655764", + "%spec_v2uint_udiv = OpConstantComposite %v2uint %uint_1431655764 %uint_1431655764", + "%uint_2863311528 = OpConstant %uint 2863311528", + "%uint_2863311528_0 = OpConstant %uint 2863311528", + "%spec_v2uint_imul = OpConstantComposite %v2uint %uint_2863311528 %uint_2863311528", + "%uint_2863311528_1 = OpConstant %uint 2863311528", + "%uint_2863311528_2 = OpConstant %uint 2863311528", + "%spec_v2uint_isub_null = OpConstantComposite %v2uint %uint_2863311528 %uint_2863311528", + }, + }, + // vector integer rem, mod + { + // original + { + // common constants + "%int_7 = OpConstant %int 7", + "%v2int_7 = OpConstantComposite %v2int %int_7 %int_7", + "%uint_7 = OpConstant %uint 7", + "%v2uint_7 = OpConstantComposite %v2uint %uint_7 %uint_7", + "%int_minus_7 = OpConstant %int -7", + "%v2int_minus_7 = OpConstantComposite %v2int %int_minus_7 %int_minus_7", + "%int_minus_3 = OpConstant %int -3", + "%v2int_minus_3 = OpConstantComposite %v2int %int_minus_3 %int_minus_3", + + // srem + "%7_srem_3 = OpSpecConstantOp %v2int SRem %v2int_7 %signed_three_vec", + "%minus_7_srem_3 = OpSpecConstantOp %v2int SRem %v2int_minus_7 %signed_three_vec", + "%7_srem_minus_3 = OpSpecConstantOp %v2int SRem %v2int_7 %v2int_minus_3", + "%minus_7_srem_minus_3 = OpSpecConstantOp %v2int SRem %v2int_minus_7 %v2int_minus_3", + // smod + "%7_smod_3 = OpSpecConstantOp %v2int SMod %v2int_7 %signed_three_vec", + "%minus_7_smod_3 = OpSpecConstantOp %v2int SMod %v2int_minus_7 %signed_three_vec", + "%7_smod_minus_3 = OpSpecConstantOp %v2int SMod %v2int_7 %v2int_minus_3", + "%minus_7_smod_minus_3 = OpSpecConstantOp %v2int SMod %v2int_minus_7 %v2int_minus_3", + // umod + "%7_umod_3 = OpSpecConstantOp %v2uint UMod %v2uint_7 %unsigned_three_vec", + }, + // expected + { + // common constants + "%int_7 = OpConstant %int 7", + "%v2int_7 = OpConstantComposite %v2int %int_7 %int_7", + "%uint_7 = OpConstant %uint 7", + "%v2uint_7 = OpConstantComposite %v2uint %uint_7 %uint_7", + "%int_minus_7 = OpConstant %int -7", + "%v2int_minus_7 = OpConstantComposite %v2int %int_minus_7 %int_minus_7", + "%int_minus_3 = OpConstant %int -3", + "%v2int_minus_3 = OpConstantComposite %v2int %int_minus_3 %int_minus_3", + + // srem + "%int_1 = OpConstant %int 1", + "%int_1_0 = OpConstant %int 1", + "%7_srem_3 = OpConstantComposite %v2int %signed_one %signed_one", + "%int_n1 = OpConstant %int -1", + "%int_n1_0 = OpConstant %int -1", + "%minus_7_srem_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", + "%int_1_1 = OpConstant %int 1", + "%int_1_2 = OpConstant %int 1", + "%7_srem_minus_3 = OpConstantComposite %v2int %signed_one %signed_one", + "%int_n1_1 = OpConstant %int -1", + "%int_n1_2 = OpConstant %int -1", + "%minus_7_srem_minus_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", + // smod + "%int_1_3 = OpConstant %int 1", + "%int_1_4 = OpConstant %int 1", + "%7_smod_3 = OpConstantComposite %v2int %signed_one %signed_one", + "%int_2 = OpConstant %int 2", + "%int_2_0 = OpConstant %int 2", + "%minus_7_smod_3 = OpConstantComposite %v2int %signed_two %signed_two", + "%int_n2 = OpConstant %int -2", + "%int_n2_0 = OpConstant %int -2", + "%7_smod_minus_3 = OpConstantComposite %v2int %int_n2 %int_n2", + "%int_n1_3 = OpConstant %int -1", + "%int_n1_4 = OpConstant %int -1", + "%minus_7_smod_minus_3 = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", + // umod + "%uint_1 = OpConstant %uint 1", + "%uint_1_0 = OpConstant %uint 1", + "%7_umod_3 = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + }, + }, + // vector integer bitwise, shift + { + // original + { + "%xor_1_3 = OpSpecConstantOp %v2int BitwiseXor %signed_one_vec %signed_three_vec", + "%and_1_2 = OpSpecConstantOp %v2int BitwiseAnd %signed_one_vec %xor_1_3", + "%or_1_2 = OpSpecConstantOp %v2int BitwiseOr %signed_one_vec %xor_1_3", + + "%unsigned_31 = OpConstant %uint 31", + "%v2unsigned_31 = OpConstantComposite %v2uint %unsigned_31 %unsigned_31", + "%unsigned_left_shift_max = OpSpecConstantOp %v2uint ShiftLeftLogical %unsigned_one_vec %v2unsigned_31", + "%unsigned_right_shift_logical = OpSpecConstantOp %v2uint ShiftRightLogical %unsigned_left_shift_max %v2unsigned_31", + "%signed_right_shift_arithmetic = OpSpecConstantOp %v2int ShiftRightArithmetic %unsigned_left_shift_max %v2unsigned_31", + }, + // expected + { + "%int_2 = OpConstant %int 2", + "%int_2_0 = OpConstant %int 2", + "%xor_1_3 = OpConstantComposite %v2int %signed_two %signed_two", + "%int_0 = OpConstant %int 0", + "%int_0_0 = OpConstant %int 0", + "%and_1_2 = OpConstantComposite %v2int %signed_zero %signed_zero", + "%int_3 = OpConstant %int 3", + "%int_3_0 = OpConstant %int 3", + "%or_1_2 = OpConstantComposite %v2int %signed_three %signed_three", + + "%unsigned_31 = OpConstant %uint 31", + "%v2unsigned_31 = OpConstantComposite %v2uint %unsigned_31 %unsigned_31", + "%uint_2147483648 = OpConstant %uint 2147483648", + "%uint_2147483648_0 = OpConstant %uint 2147483648", + "%unsigned_left_shift_max = OpConstantComposite %v2uint %uint_2147483648 %uint_2147483648", + "%uint_1 = OpConstant %uint 1", + "%uint_1_0 = OpConstant %uint 1", + "%unsigned_right_shift_logical = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + "%int_n1 = OpConstant %int -1", + "%int_n1_0 = OpConstant %int -1", + "%signed_right_shift_arithmetic = OpConstantComposite %v2int %signed_minus_one %signed_minus_one", + }, + }, + // Skip folding if any vector operands or components of the operands + // have undetermined value. + { + // original + { + "%spec_int = OpSpecConstant %int 1", + "%spec_vec = OpSpecConstantComposite %v2int %signed_zero %spec_int", + "%spec_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %spec_vec", + }, + // expected + { + "%spec_int = OpSpecConstant %int 1", + "%spec_vec = OpSpecConstantComposite %v2int %signed_zero %spec_int", + "%spec_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %spec_vec", + }, + }, + // Skip folding if any vector operands are defined by OpUndef + { + // original + { + "%undef = OpUndef %int", + "%vec = OpConstantComposite %v2int %undef %signed_one", + "%spec_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %vec", + }, + // expected + { + "%undef = OpUndef %int", + "%vec = OpConstantComposite %v2int %undef %signed_one", + "%spec_iadd = OpSpecConstantOp %v2int IAdd %signed_three_vec %vec", + }, + }, + // clang-format on + }))); + +// Tests for SpecConstantOp CompositeExtract instruction +INSTANTIATE_TEST_SUITE_P( + CompositeExtract, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // normal vector + { + // original + { + "%r = OpSpecConstantOp %int CompositeExtract %signed_three_vec 0", + "%x = OpSpecConstantOp %int CompositeExtract %v4int_0_1_2_3 0", + "%y = OpSpecConstantOp %int CompositeExtract %v4int_0_1_2_3 1", + "%z = OpSpecConstantOp %int CompositeExtract %v4int_0_1_2_3 2", + "%w = OpSpecConstantOp %int CompositeExtract %v4int_0_1_2_3 3", + }, + // expected + { + "%r = OpConstant %int 3", + "%x = OpConstant %int 0", + "%y = OpConstant %int 1", + "%z = OpConstant %int 2", + "%w = OpConstant %int 3", + }, + }, + // null vector + { + // original + { + "%x = OpSpecConstantOp %int CompositeExtract %signed_null_vec 0", + "%y = OpSpecConstantOp %int CompositeExtract %signed_null_vec 1", + "%null_v4int = OpConstantNull %v4int", + "%z = OpSpecConstantOp %int CompositeExtract %signed_null_vec 2", + }, + // expected + { + "%x = OpConstantNull %int", + "%y = OpConstantNull %int", + "%null_v4int = OpConstantNull %v4int", + "%z = OpConstantNull %int", + } + }, + // normal flat struct + { + // original + { + "%float_1 = OpConstant %float 1", + "%flat_1 = OpConstantComposite %flat_struct %bool_true %signed_null %float_1", + "%extract_bool = OpSpecConstantOp %bool CompositeExtract %flat_1 0", + "%extract_int = OpSpecConstantOp %int CompositeExtract %flat_1 1", + "%extract_float_1 = OpSpecConstantOp %float CompositeExtract %flat_1 2", + // foldable composite constants built with OpSpecConstantComposite + // should also be processed. + "%flat_2 = OpSpecConstantComposite %flat_struct %bool_true %signed_null %float_1", + "%extract_float_2 = OpSpecConstantOp %float CompositeExtract %flat_2 2", + }, + // expected + { + "%float_1 = OpConstant %float 1", + "%flat_1 = OpConstantComposite %flat_struct %bool_true %signed_null %float_1", + "%extract_bool = OpConstantTrue %bool", + "%extract_int = OpConstantNull %int", + "%extract_float_1 = OpConstant %float 1", + "%flat_2 = OpConstantComposite %flat_struct %bool_true %signed_null %float_1", + "%extract_float_2 = OpConstant %float 1", + }, + }, + // null flat struct + { + // original + { + "%flat = OpConstantNull %flat_struct", + "%extract_bool = OpSpecConstantOp %bool CompositeExtract %flat 0", + "%extract_int = OpSpecConstantOp %int CompositeExtract %flat 1", + "%extract_float = OpSpecConstantOp %float CompositeExtract %flat 2", + }, + // expected + { + "%flat = OpConstantNull %flat_struct", + "%extract_bool = OpConstantNull %bool", + "%extract_int = OpConstantNull %int", + "%extract_float = OpConstantNull %float", + }, + }, + // normal nested struct + { + // original + { + "%float_1 = OpConstant %float 1", + "%inner = OpConstantComposite %inner_struct %bool_true %signed_null %float_1", + "%outer = OpConstantComposite %outer_struct %inner %signed_one", + "%extract_inner = OpSpecConstantOp %inner_struct CompositeExtract %outer 0", + "%extract_int = OpSpecConstantOp %int CompositeExtract %outer 1", + "%extract_inner_float = OpSpecConstantOp %float CompositeExtract %outer 0 2", + }, + // expected + { + "%float_1 = OpConstant %float 1", + "%inner = OpConstantComposite %inner_struct %bool_true %signed_null %float_1", + "%outer = OpConstantComposite %outer_struct %inner %signed_one", + "%extract_inner = OpConstantComposite %inner_struct %bool_true %signed_null %float_1", + "%extract_int = OpConstant %int 1", + "%extract_inner_float = OpConstant %float 1", + }, + }, + // null nested struct + { + // original + { + "%outer = OpConstantNull %outer_struct", + "%extract_inner = OpSpecConstantOp %inner_struct CompositeExtract %outer 0", + "%extract_int = OpSpecConstantOp %int CompositeExtract %outer 1", + "%extract_inner_float = OpSpecConstantOp %float CompositeExtract %outer 0 2", + }, + // expected + { + "%outer = OpConstantNull %outer_struct", + "%extract_inner = OpConstantNull %inner_struct", + "%extract_int = OpConstantNull %int", + "%extract_inner_float = OpConstantNull %float", + }, + }, + // skip folding if the any composite constant's value are not fully + // determined, even though the extracting target might have + // determined value. + { + // original + { + "%float_1 = OpConstant %float 1", + "%spec_float = OpSpecConstant %float 1", + "%spec_inner = OpSpecConstantComposite %inner_struct %bool_true %signed_null %spec_float", + "%spec_outer = OpSpecConstantComposite %outer_struct %spec_inner %signed_one", + "%spec_vec = OpSpecConstantComposite %v2float %spec_float %float_1", + "%extract_inner = OpSpecConstantOp %int CompositeExtract %spec_inner 1", + "%extract_outer = OpSpecConstantOp %int CompositeExtract %spec_outer 1", + "%extract_vec = OpSpecConstantOp %float CompositeExtract %spec_vec 1", + }, + // expected + { + "%float_1 = OpConstant %float 1", + "%spec_float = OpSpecConstant %float 1", + "%spec_inner = OpSpecConstantComposite %inner_struct %bool_true %signed_null %spec_float", + "%spec_outer = OpSpecConstantComposite %outer_struct %spec_inner %signed_one", + "%spec_vec = OpSpecConstantComposite %v2float %spec_float %float_1", + "%extract_inner = OpSpecConstantOp %int CompositeExtract %spec_inner 1", + "%extract_outer = OpSpecConstantOp %int CompositeExtract %spec_outer 1", + "%extract_vec = OpSpecConstantOp %float CompositeExtract %spec_vec 1", + }, + }, + // skip if the composite constant depends on the result of OpUndef, + // even though the composite extract target element does not depends + // on the OpUndef. + { + // original + { + "%undef = OpUndef %float", + "%inner = OpConstantComposite %inner_struct %bool_true %signed_one %undef", + "%outer = OpConstantComposite %outer_struct %inner %signed_one", + "%extract_inner = OpSpecConstantOp %int CompositeExtract %inner 1", + "%extract_outer = OpSpecConstantOp %int CompositeExtract %outer 1", + }, + // expected + { + "%undef = OpUndef %float", + "%inner = OpConstantComposite %inner_struct %bool_true %signed_one %undef", + "%outer = OpConstantComposite %outer_struct %inner %signed_one", + "%extract_inner = OpSpecConstantOp %int CompositeExtract %inner 1", + "%extract_outer = OpSpecConstantOp %int CompositeExtract %outer 1", + }, + }, + // TODO(qining): Add tests for Array and other composite type constants. + // clang-format on + }))); + +// Tests the swizzle operations for spec const vectors. +INSTANTIATE_TEST_SUITE_P( + VectorShuffle, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // normal vector + { + // original + { + "%xy = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 0 1", + "%yz = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 1 2", + "%zw = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 2 3", + "%wx = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 3 0", + "%xx = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 0 0", + "%yyy = OpSpecConstantOp %v3int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 1 1 1", + "%wwww = OpSpecConstantOp %v4int VectorShuffle %v4int_0_1_2_3 %v4int_0_1_2_3 2 2 2 2", + }, + // expected + { + "%xy = OpConstantComposite %v2int %signed_zero %signed_one", + "%yz = OpConstantComposite %v2int %signed_one %signed_two", + "%zw = OpConstantComposite %v2int %signed_two %signed_three", + "%wx = OpConstantComposite %v2int %signed_three %signed_zero", + "%xx = OpConstantComposite %v2int %signed_zero %signed_zero", + "%yyy = OpConstantComposite %v3int %signed_one %signed_one %signed_one", + "%wwww = OpConstantComposite %v4int %signed_two %signed_two %signed_two %signed_two", + }, + }, + // null vector + { + // original + { + "%a = OpSpecConstantOp %v2int VectorShuffle %signed_null_vec %v4int_0_1_2_3 0 1", + "%b = OpSpecConstantOp %v2int VectorShuffle %signed_null_vec %v4int_0_1_2_3 2 3", + "%c = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %signed_null_vec 3 4", + "%d = OpSpecConstantOp %v2int VectorShuffle %signed_null_vec %signed_null_vec 1 2", + }, + // expected + { + "%a = OpConstantComposite %v2int %signed_null %signed_null", + "%b = OpConstantComposite %v2int %signed_zero %signed_one", + "%c = OpConstantComposite %v2int %signed_three %signed_null", + "%d = OpConstantComposite %v2int %signed_null %signed_null", + } + }, + // skip if any of the components of the vector operands do not have + // determined value, even though the result vector might not be + // built with those undermined values. + { + // original + { + "%spec_int = OpSpecConstant %int 1", + "%spec_ivec = OpSpecConstantComposite %v2int %signed_null %spec_int", + "%a = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %spec_ivec 0 1", + "%b = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %spec_ivec 3 4", + }, + // expected + { + "%spec_int = OpSpecConstant %int 1", + "%spec_ivec = OpSpecConstantComposite %v2int %signed_null %spec_int", + "%a = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %spec_ivec 0 1", + "%b = OpSpecConstantOp %v2int VectorShuffle %v4int_0_1_2_3 %spec_ivec 3 4", + }, + }, + // Skip if any components of the two vector operands depend on + // the result of OpUndef. Even though the selected components do + // not depend on the OpUndef result. + { + // original + { + "%undef = OpUndef %int", + "%vec_1 = OpConstantComposite %v2int %undef %signed_one", + "%dep = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 0 3", + "%not_dep_element = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 1 3", + "%no_dep_vector = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 2 3", + }, + // expected + { + "%undef = OpUndef %int", + "%vec_1 = OpConstantComposite %v2int %undef %signed_one", + "%dep = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 0 3", + "%not_dep_element = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 1 3", + "%no_dep_vector = OpSpecConstantOp %v2int VectorShuffle %vec_1 %signed_three_vec 2 3", + }, + }, + // clang-format on + }))); + +// Test with long use-def chain. +INSTANTIATE_TEST_SUITE_P( + LongDefUseChain, FoldSpecConstantOpAndCompositePassTest, + ::testing::ValuesIn(std::vector< + FoldSpecConstantOpAndCompositePassTestCase>({ + // clang-format off + // Long Def-Use chain with binary operations. + { + // original + { + "%array_size = OpConstant %int 4", + "%type_arr_int_4 = OpTypeArray %int %array_size", + "%spec_int_0 = OpConstant %int 100", + "%spec_int_1 = OpConstant %int 1", + "%spec_int_2 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_1", + "%spec_int_3 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_2", + "%spec_int_4 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_3", + "%spec_int_5 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_4", + "%spec_int_6 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_5", + "%spec_int_7 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_6", + "%spec_int_8 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_7", + "%spec_int_9 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_8", + "%spec_int_10 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_9", + "%spec_int_11 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_10", + "%spec_int_12 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_11", + "%spec_int_13 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_12", + "%spec_int_14 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_13", + "%spec_int_15 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_14", + "%spec_int_16 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_15", + "%spec_int_17 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_16", + "%spec_int_18 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_17", + "%spec_int_19 = OpSpecConstantOp %int IAdd %spec_int_0 %spec_int_18", + "%spec_int_20 = OpSpecConstantOp %int ISub %spec_int_0 %spec_int_19", + "%used_vec_a = OpSpecConstantComposite %v2int %spec_int_18 %spec_int_19", + "%used_vec_b = OpSpecConstantOp %v2int IMul %used_vec_a %used_vec_a", + "%spec_int_21 = OpSpecConstantOp %int CompositeExtract %used_vec_b 0", + "%array = OpConstantComposite %type_arr_int_4 %spec_int_20 %spec_int_20 %spec_int_21 %spec_int_21", + // Spec constants whose values can not be fully resolved should + // not be processed. + "%spec_int_22 = OpSpecConstant %int 123", + "%spec_int_23 = OpSpecConstantOp %int IAdd %spec_int_22 %signed_one", + }, + // expected + { + "%array_size = OpConstant %int 4", + "%type_arr_int_4 = OpTypeArray %int %array_size", + "%spec_int_0 = OpConstant %int 100", + "%spec_int_1 = OpConstant %int 1", + "%spec_int_2 = OpConstant %int 101", + "%spec_int_3 = OpConstant %int -1", + "%spec_int_4 = OpConstant %int 99", + "%spec_int_5 = OpConstant %int 1", + "%spec_int_6 = OpConstant %int 101", + "%spec_int_7 = OpConstant %int -1", + "%spec_int_8 = OpConstant %int 99", + "%spec_int_9 = OpConstant %int 1", + "%spec_int_10 = OpConstant %int 101", + "%spec_int_11 = OpConstant %int -1", + "%spec_int_12 = OpConstant %int 99", + "%spec_int_13 = OpConstant %int 1", + "%spec_int_14 = OpConstant %int 101", + "%spec_int_15 = OpConstant %int -1", + "%spec_int_16 = OpConstant %int 101", + "%spec_int_17 = OpConstant %int 201", + "%spec_int_18 = OpConstant %int -101", + "%spec_int_19 = OpConstant %int -1", + "%spec_int_20 = OpConstant %int 101", + "%used_vec_a = OpConstantComposite %v2int %spec_int_18 %spec_int_19", + "%int_10201 = OpConstant %int 10201", + "%int_1 = OpConstant %int 1", + "%used_vec_b = OpConstantComposite %v2int %int_10201 %signed_one", + "%spec_int_21 = OpConstant %int 10201", + "%array = OpConstantComposite %type_arr_int_4 %spec_int_20 %spec_int_20 %spec_int_21 %spec_int_21", + "%spec_int_22 = OpSpecConstant %int 123", + "%spec_int_23 = OpSpecConstantOp %int IAdd %spec_int_22 %signed_one", + }, + }, + // Long Def-Use chain with swizzle + }))); + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/fold_test.cpp b/third_party/spirv-tools/test/opt/fold_test.cpp new file mode 100644 index 0000000..bb6098c --- /dev/null +++ b/third_party/spirv-tools/test/opt/fold_test.cpp @@ -0,0 +1,7322 @@ +// Copyright (c) 2016 Google Inc. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/fold.h" + +#include +#include +#include +#include +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::Contains; + +std::string Disassemble(const std::string& original, IRContext* context, + uint32_t disassemble_options = 0) { + std::vector optimized_bin; + context->module()->ToBinary(&optimized_bin, true); + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + SpirvTools tools(target_env); + std::string optimized_asm; + EXPECT_TRUE( + tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options)) + << "Disassembling failed for shader:\n" + << original << std::endl; + return optimized_asm; +} + +void Match(const std::string& original, IRContext* context, + uint32_t disassemble_options = 0) { + std::string disassembly = Disassemble(original, context, disassemble_options); + auto match_result = effcee::Match(disassembly, original); + EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) + << match_result.message() << "\nChecking result:\n" + << disassembly; +} + +template +struct InstructionFoldingCase { + InstructionFoldingCase(const std::string& tb, uint32_t id, ResultType result) + : test_body(tb), id_to_fold(id), expected_result(result) {} + + std::string test_body; + uint32_t id_to_fold; + ResultType expected_result; +}; + +using IntegerInstructionFoldingTest = + ::testing::TestWithParam>; + +TEST_P(IntegerInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_TRUE(succeeded); + if (inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + EXPECT_EQ(inst->opcode(), SpvOpConstant); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::IntConstant* result = + const_mrg->GetConstantFromInst(inst)->AsIntConstant(); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + EXPECT_EQ(result->GetU32BitValue(), tc.expected_result); + } + } +} + +// Returns a common SPIR-V header for all of the test that follow. +#define INT_0_ID 100 +#define TRUE_ID 101 +#define VEC2_0_ID 102 +#define INT_7_ID 103 +#define FLOAT_0_ID 104 +#define DOUBLE_0_ID 105 +#define VEC4_0_ID 106 +#define DVEC4_0_ID 106 +#define HALF_0_ID 108 +const std::string& Header() { + static const std::string header = R"(OpCapability Shader +OpCapability Float16 +OpCapability Float64 +OpCapability Int16 +OpCapability Int64 +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +%void = OpTypeVoid +%void_func = OpTypeFunction %void +%bool = OpTypeBool +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%half = OpTypeFloat 16 +%101 = OpConstantTrue %bool ; Need a def with an numerical id to define id maps. +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%bool_null = OpConstantNull %bool +%short = OpTypeInt 16 1 +%int = OpTypeInt 32 1 +%long = OpTypeInt 64 1 +%uint = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%v4int = OpTypeVector %int 4 +%v4float = OpTypeVector %float 4 +%v4double = OpTypeVector %double 4 +%v2float = OpTypeVector %float 2 +%v2double = OpTypeVector %double 2 +%v2half = OpTypeVector %half 2 +%v2bool = OpTypeVector %bool 2 +%struct_v2int_int_int = OpTypeStruct %v2int %int %int +%_ptr_int = OpTypePointer Function %int +%_ptr_uint = OpTypePointer Function %uint +%_ptr_bool = OpTypePointer Function %bool +%_ptr_float = OpTypePointer Function %float +%_ptr_double = OpTypePointer Function %double +%_ptr_half = OpTypePointer Function %half +%_ptr_long = OpTypePointer Function %long +%_ptr_v2int = OpTypePointer Function %v2int +%_ptr_v4int = OpTypePointer Function %v4int +%_ptr_v4float = OpTypePointer Function %v4float +%_ptr_v4double = OpTypePointer Function %v4double +%_ptr_struct_v2int_int_int = OpTypePointer Function %struct_v2int_int_int +%_ptr_v2float = OpTypePointer Function %v2float +%_ptr_v2double = OpTypePointer Function %v2double +%short_0 = OpConstant %short 0 +%short_2 = OpConstant %short 2 +%short_3 = OpConstant %short 3 +%100 = OpConstant %int 0 ; Need a def with an numerical id to define id maps. +%103 = OpConstant %int 7 ; Need a def with an numerical id to define id maps. +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_3 = OpConstant %int 3 +%int_4 = OpConstant %int 4 +%int_n24 = OpConstant %int -24 +%int_min = OpConstant %int -2147483648 +%int_max = OpConstant %int 2147483647 +%long_0 = OpConstant %long 0 +%long_2 = OpConstant %long 2 +%long_3 = OpConstant %long 3 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%uint_4 = OpConstant %uint 4 +%uint_32 = OpConstant %uint 32 +%uint_42 = OpConstant %uint 42 +%uint_max = OpConstant %uint 4294967295 +%v2int_undef = OpUndef %v2int +%v2int_0_0 = OpConstantComposite %v2int %int_0 %int_0 +%v2int_1_0 = OpConstantComposite %v2int %int_1 %int_0 +%v2int_2_2 = OpConstantComposite %v2int %int_2 %int_2 +%v2int_2_3 = OpConstantComposite %v2int %int_2 %int_3 +%v2int_3_2 = OpConstantComposite %v2int %int_3 %int_2 +%v2int_4_4 = OpConstantComposite %v2int %int_4 %int_4 +%v2bool_null = OpConstantNull %v2bool +%v2bool_true_false = OpConstantComposite %v2bool %true %false +%v2bool_false_true = OpConstantComposite %v2bool %false %true +%struct_v2int_int_int_null = OpConstantNull %struct_v2int_int_int +%v2int_null = OpConstantNull %v2int +%102 = OpConstantComposite %v2int %103 %103 +%v4int_0_0_0_0 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 +%struct_undef_0_0 = OpConstantComposite %struct_v2int_int_int %v2int_undef %int_0 %int_0 +%float_n1 = OpConstant %float -1 +%104 = OpConstant %float 0 ; Need a def with an numerical id to define id maps. +%float_null = OpConstantNull %float +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%float_3 = OpConstant %float 3 +%float_4 = OpConstant %float 4 +%float_2049 = OpConstant %float 2049 +%float_n2049 = OpConstant %float -2049 +%float_0p5 = OpConstant %float 0.5 +%float_0p2 = OpConstant %float 0.2 +%float_pi = OpConstant %float 1.5555 +%float_1e16 = OpConstant %float 1e16 +%float_n1e16 = OpConstant %float -1e16 +%float_1en16 = OpConstant %float 1e-16 +%float_n1en16 = OpConstant %float -1e-16 +%v2float_0_0 = OpConstantComposite %v2float %float_0 %float_0 +%v2float_2_2 = OpConstantComposite %v2float %float_2 %float_2 +%v2float_2_3 = OpConstantComposite %v2float %float_2 %float_3 +%v2float_3_2 = OpConstantComposite %v2float %float_3 %float_2 +%v2float_4_4 = OpConstantComposite %v2float %float_4 %float_4 +%v2float_2_0p5 = OpConstantComposite %v2float %float_2 %float_0p5 +%v2float_0p2_0p5 = OpConstantComposite %v2float %float_0p2 %float_0p5 +%v2float_null = OpConstantNull %v2float +%double_n1 = OpConstant %double -1 +%105 = OpConstant %double 0 ; Need a def with an numerical id to define id maps. +%double_null = OpConstantNull %double +%double_0 = OpConstant %double 0 +%double_1 = OpConstant %double 1 +%double_2 = OpConstant %double 2 +%double_3 = OpConstant %double 3 +%double_4 = OpConstant %double 4 +%double_5 = OpConstant %double 5 +%double_0p5 = OpConstant %double 0.5 +%double_0p2 = OpConstant %double 0.2 +%v2double_0_0 = OpConstantComposite %v2double %double_0 %double_0 +%v2double_2_2 = OpConstantComposite %v2double %double_2 %double_2 +%v2double_2_3 = OpConstantComposite %v2double %double_2 %double_3 +%v2double_3_2 = OpConstantComposite %v2double %double_3 %double_2 +%v2double_4_4 = OpConstantComposite %v2double %double_4 %double_4 +%v2double_2_0p5 = OpConstantComposite %v2double %double_2 %double_0p5 +%v2double_null = OpConstantNull %v2double +%108 = OpConstant %half 0 +%half_1 = OpConstant %half 1 +%half_0_1 = OpConstantComposite %v2half %108 %half_1 +%106 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%v4float_0_0_0_0 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%v4float_0_0_0_1 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 +%v4float_0_1_0_0 = OpConstantComposite %v4float %float_0 %float_1 %float_null %float_0 +%v4float_1_1_1_1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%107 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0 +%v4double_0_0_0_0 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_0 +%v4double_0_0_0_1 = OpConstantComposite %v4double %double_0 %double_0 %double_0 %double_1 +%v4double_0_1_0_0 = OpConstantComposite %v4double %double_0 %double_1 %double_null %double_0 +%v4double_1_1_1_1 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_1 +%v4double_1_1_1_0p5 = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_0p5 +%v4double_null = OpConstantNull %v4double +%v4float_n1_2_1_3 = OpConstantComposite %v4float %float_n1 %float_2 %float_1 %float_3 +)"; + + return header; +} + +// Returns the header with definitions of float NaN and double NaN. Since FC +// "; CHECK: [[double_n0:%\\w+]] = OpConstant [[double]] -0\n" finds +// %double_nan = OpConstant %double -0x1.8p+1024 instead of +// %double_n0 = OpConstant %double -0, +// we separates those definitions from Header(). +const std::string& HeaderWithNaN() { + static const std::string headerWithNaN = + Header() + + R"(%float_nan = OpConstant %float -0x1.8p+128 +%double_nan = OpConstant %double -0x1.8p+1024 +)"; + + return headerWithNaN; +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, IntegerInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold 0*n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpIMul %int %int_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: fold n*0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpIMul %int %load %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 2: fold 0/n (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSDiv %int %int_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 3: fold n/0 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSDiv %int %load %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 4: fold 0/n (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpUDiv %uint %uint_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 5: fold n/0 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSDiv %int %load %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 6: fold 0 remainder n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSRem %int %int_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 7: fold n remainder 0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSRem %int %load %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 8: fold 0%n (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSMod %int %int_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 9: fold n%0 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSMod %int %load %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 10: fold 0%n (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpUMod %uint %uint_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 11: fold n%0 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpUMod %uint %load %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 12: fold n << 32 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpShiftLeftLogical %uint %load %uint_32\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 13: fold n >> 32 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpShiftRightLogical %uint %load %uint_32\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 14: fold n | 0xFFFFFFFF + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpBitwiseOr %uint %load %uint_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xFFFFFFFF), + // Test case 15: fold 0xFFFFFFFF | n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpBitwiseOr %uint %uint_max %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0xFFFFFFFF), + // Test case 16: fold n & 0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpBitwiseAnd %uint %load %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 17: fold 1/0 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSDiv %int %int_1 %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 18: fold 1/0 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpUDiv %uint %uint_1 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 19: fold OpSRem 1 0 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSRem %int %int_1 %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 20: fold 1%0 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSMod %int %int_1 %int_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 21: fold 1%0 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpUMod %uint %uint_1 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 22: fold unsigned n >> 42 (undefined, so set to zero). + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpShiftRightLogical %uint %load %uint_42\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 23: fold signed n >> 42 (undefined, so set to zero). + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpShiftRightLogical %int %load %uint_42\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 24: fold n << 42 (undefined, so set to zero). + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpShiftLeftLogical %int %load %uint_42\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 25: fold -24 >> 32 (defined as -1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpShiftRightArithmetic %int %int_n24 %uint_32\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -1), + // Test case 26: fold 2 >> 32 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpShiftRightArithmetic %int %int_2 %uint_32\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 27: fold 2 >> 32 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpShiftRightLogical %int %int_2 %uint_32\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 28: fold 2 << 32 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpShiftLeftLogical %int %int_2 %uint_32\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 29: fold -INT_MIN + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSNegate %int %int_min\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::min()), + // Test case 30: fold UMin 3 4 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %uint %1 UMin %uint_3 %uint_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 31: fold UMin 4 2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %uint %1 UMin %uint_4 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2), + // Test case 32: fold SMin 3 4 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 UMin %int_3 %int_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 33: fold SMin 4 2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 SMin %int_4 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2), + // Test case 34: fold UMax 3 4 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %uint %1 UMax %uint_3 %uint_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4), + // Test case 35: fold UMax 3 2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %uint %1 UMax %uint_3 %uint_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 36: fold SMax 3 4 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 UMax %int_3 %int_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4), + // Test case 37: fold SMax 3 2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 SMax %int_3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 38: fold UClamp 2 3 4 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %uint %1 UClamp %uint_2 %uint_3 %uint_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 39: fold UClamp 2 0 4 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %uint %1 UClamp %uint_2 %uint_0 %uint_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2), + // Test case 40: fold UClamp 2 0 1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %uint %1 UClamp %uint_2 %uint_0 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1), + // Test case 41: fold SClamp 2 3 4 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 SClamp %int_2 %int_3 %int_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 42: fold SClamp 2 0 4 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 SClamp %int_2 %int_0 %int_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2), + // Test case 43: fold SClamp 2 0 1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %int %1 SClamp %int_2 %int_0 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1), + // Test case 44: SClamp 1 2 x + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %int\n" + + "%2 = OpExtInst %int %1 SClamp %int_1 %int_2 %undef\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2), + // Test case 45: SClamp 2 x 1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %int\n" + + "%2 = OpExtInst %int %1 SClamp %int_2 %undef %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1), + // Test case 44: UClamp 1 2 x + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %uint\n" + + "%2 = OpExtInst %uint %1 UClamp %uint_1 %uint_2 %undef\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2), + // Test case 45: UClamp 2 x 1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %uint\n" + + "%2 = OpExtInst %uint %1 UClamp %uint_2 %undef %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1) +)); +// clang-format on + +using IntVectorInstructionFoldingTest = + ::testing::TestWithParam>>; + +TEST_P(IntVectorInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + SpvOp original_opcode = inst->opcode(); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_EQ(succeeded, inst == nullptr || inst->opcode() != original_opcode); + if (succeeded && inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + std::vector opcodes = {SpvOpConstantComposite}; + EXPECT_THAT(opcodes, Contains(inst->opcode())); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::Constant* result = const_mrg->GetConstantFromInst(inst); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + const std::vector& componenets = + result->AsVectorConstant()->GetComponents(); + EXPECT_EQ(componenets.size(), tc.expected_result.size()); + for (size_t i = 0; i < componenets.size(); i++) { + EXPECT_EQ(tc.expected_result[i], componenets[i]->GetU32()); + } + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, IntVectorInstructionFoldingTest, +::testing::Values( + // Test case 0: fold 0*n + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpVectorShuffle %v2int %v2int_2_2 %v2int_2_3 0 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {2,3}), + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 0 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0,3}), + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 4294967295 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0,0}), + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpVectorShuffle %v2int %v2int_null %v2int_2_3 0 4294967295 \n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {0,0}) +)); +// clang-format on + +using FloatVectorInstructionFoldingTest = + ::testing::TestWithParam>>; + +TEST_P(FloatVectorInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + SpvOp original_opcode = inst->opcode(); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_EQ(succeeded, inst == nullptr || inst->opcode() != original_opcode); + if (succeeded && inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + std::vector opcodes = {SpvOpConstantComposite}; + EXPECT_THAT(opcodes, Contains(inst->opcode())); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::Constant* result = const_mrg->GetConstantFromInst(inst); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + const std::vector& componenets = + result->AsVectorConstant()->GetComponents(); + EXPECT_EQ(componenets.size(), tc.expected_result.size()); + for (size_t i = 0; i < componenets.size(); i++) { + EXPECT_EQ(tc.expected_result[i], componenets[i]->GetFloat()); + } + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, FloatVectorInstructionFoldingTest, +::testing::Values( + // Test case 0: FMix {2.0, 2.0}, {2.0, 3.0} {0.2,0.5} + InstructionFoldingCase>( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %v2float %1 FMix %v2float_2_3 %v2float_0_0 %v2float_0p2_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, {1.6f,1.5f}) +)); +// clang-format on +using BooleanInstructionFoldingTest = + ::testing::TestWithParam>; + +TEST_P(BooleanInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_TRUE(succeeded); + if (inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + std::vector bool_opcodes = {SpvOpConstantTrue, SpvOpConstantFalse}; + EXPECT_THAT(bool_opcodes, Contains(inst->opcode())); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::BoolConstant* result = + const_mrg->GetConstantFromInst(inst)->AsBoolConstant(); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + EXPECT_EQ(result->value(), tc.expected_result); + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, BooleanInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold true || n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%load = OpLoad %bool %n\n" + + "%2 = OpLogicalOr %bool %true %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 1: fold n || true + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%load = OpLoad %bool %n\n" + + "%2 = OpLogicalOr %bool %load %true\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold false && n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%load = OpLoad %bool %n\n" + + "%2 = OpLogicalAnd %bool %false %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 3: fold n && false + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%load = OpLoad %bool %n\n" + + "%2 = OpLogicalAnd %bool %load %false\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 4: fold n < 0 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpULessThan %bool %load %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 5: fold UINT_MAX < n (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpULessThan %bool %uint_max %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 6: fold INT_MAX < n (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSLessThan %bool %int_max %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 7: fold n < INT_MIN (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSLessThan %bool %load %int_min\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 8: fold 0 > n (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpUGreaterThan %bool %uint_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 9: fold n > UINT_MAX (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpUGreaterThan %bool %load %uint_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 10: fold n > INT_MAX (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSGreaterThan %bool %load %int_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 11: fold INT_MIN > n (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpSGreaterThan %bool %int_min %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 12: fold 0 <= n (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpULessThanEqual %bool %uint_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 13: fold n <= UINT_MAX (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpULessThanEqual %bool %load %uint_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 14: fold INT_MIN <= n (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSLessThanEqual %bool %int_min %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 15: fold n <= INT_MAX (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSLessThanEqual %bool %load %int_max\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 16: fold n >= 0 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpUGreaterThanEqual %bool %load %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 17: fold UINT_MAX >= n (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load = OpLoad %uint %n\n" + + "%2 = OpUGreaterThanEqual %bool %uint_max %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 18: fold n >= INT_MIN (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSGreaterThanEqual %bool %load %int_min\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 19: fold INT_MAX >= n (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSGreaterThanEqual %bool %int_max %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(FClampAndCmpLHS, BooleanInstructionFoldingTest, +::testing::Values( + // Test case 0: fold 0.0 > clamp(n, 0.0, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFOrdGreaterThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 1: fold 0.0 > clamp(n, -1.0, -1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_n1\n" + + "%2 = OpFOrdGreaterThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold 0.0 >= clamp(n, 1, 2) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFOrdGreaterThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 3: fold 0.0 >= clamp(n, -1.0, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFOrdGreaterThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 4: fold 0.0 <= clamp(n, 0.0, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFOrdLessThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: fold 0.0 <= clamp(n, -1.0, -1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_n1\n" + + "%2 = OpFOrdLessThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 6: fold 0.0 < clamp(n, 1, 2) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFOrdLessThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: fold 0.0 < clamp(n, -1.0, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFOrdLessThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 8: fold 0.0 > clamp(n, 0.0, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFUnordGreaterThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 9: fold 0.0 > clamp(n, -1.0, -1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_n1\n" + + "%2 = OpFUnordGreaterThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 10: fold 0.0 >= clamp(n, 1, 2) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFUnordGreaterThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 11: fold 0.0 >= clamp(n, -1.0, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFUnordGreaterThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 12: fold 0.0 <= clamp(n, 0.0, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFUnordLessThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 13: fold 0.0 <= clamp(n, -1.0, -1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_n1\n" + + "%2 = OpFUnordLessThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 14: fold 0.0 < clamp(n, 1, 2) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFUnordLessThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 15: fold 0.0 < clamp(n, -1.0, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFUnordLessThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false) +)); + +INSTANTIATE_TEST_SUITE_P(FClampAndCmpRHS, BooleanInstructionFoldingTest, +::testing::Values( + // Test case 0: fold clamp(n, 0.0, 1.0) > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFOrdGreaterThan %bool %clamp %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 1: fold clamp(n, 1.0, 1.0) > 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_1\n" + + "%2 = OpFOrdGreaterThan %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold clamp(n, 1, 2) >= 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFOrdGreaterThanEqual %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: fold clamp(n, 1.0, 2.0) >= 3.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFOrdGreaterThanEqual %bool %clamp %float_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 4: fold clamp(n, 0.0, 1.0) <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFOrdLessThanEqual %bool %clamp %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: fold clamp(n, 1.0, 2.0) <= 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFOrdLessThanEqual %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 6: fold clamp(n, 1, 2) < 3 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFOrdLessThan %bool %clamp %float_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: fold clamp(n, -1.0, 0.0) < -1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFOrdLessThan %bool %clamp %float_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 8: fold clamp(n, 0.0, 1.0) > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFUnordGreaterThan %bool %clamp %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 9: fold clamp(n, 1.0, 2.0) > 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFUnordGreaterThan %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 10: fold clamp(n, 1, 2) >= 3.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFUnordGreaterThanEqual %bool %clamp %float_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 11: fold clamp(n, -1.0, 0.0) >= -1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFUnordGreaterThanEqual %bool %clamp %float_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 12: fold clamp(n, 0.0, 1.0) <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFUnordLessThanEqual %bool %clamp %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 13: fold clamp(n, 1.0, 1.0) <= 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_1\n" + + "%2 = OpFUnordLessThanEqual %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 14: fold clamp(n, 1, 2) < 3 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_1 %float_2\n" + + "%2 = OpFUnordLessThan %bool %clamp %float_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 15: fold clamp(n, -1.0, 0.0) < -1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFUnordLessThan %bool %clamp %float_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 16: fold clamp(n, -1.0, 0.0) < -1.0 (one test for double) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%ld = OpLoad %double %n\n" + + "%clamp = OpExtInst %double %1 FClamp %ld %double_n1 %double_0\n" + + "%2 = OpFUnordLessThan %bool %clamp %double_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false) +)); +// clang-format on + +using FloatInstructionFoldingTest = + ::testing::TestWithParam>; + +TEST_P(FloatInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_TRUE(succeeded); + if (inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + EXPECT_EQ(inst->opcode(), SpvOpConstant); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::FloatConstant* result = + const_mrg->GetConstantFromInst(inst)->AsFloatConstant(); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + if (!std::isnan(tc.expected_result)) { + EXPECT_EQ(result->GetFloatValue(), tc.expected_result); + } else { + EXPECT_TRUE(std::isnan(result->GetFloatValue())); + } + } + } +} + +// Not testing NaNs because there are no expectations concerning NaNs according +// to the "Precision and Operation of SPIR-V Instructions" section of the Vulkan +// specification. + +// clang-format off +INSTANTIATE_TEST_SUITE_P(FloatConstantFoldingTest, FloatInstructionFoldingTest, +::testing::Values( + // Test case 0: Fold 2.0 - 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFSub %float %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 1: Fold 2.0 + 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFAdd %float %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3.0), + // Test case 2: Fold 3.0 * 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFMul %float %float_3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 6.0), + // Test case 3: Fold 1.0 / 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.5), + // Test case 4: Fold 1.0 / 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_1 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::infinity()), + // Test case 5: Fold -1.0 / 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %float %float_n1 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits::infinity()), + // Test case 6: Fold (2.0, 3.0) dot (2.0, 0.5) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpDot %float %v2float_2_3 %v2float_2_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 5.5f), + // Test case 7: Fold (0.0, 0.0) dot v + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%v = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %v\n" + + "%3 = OpDot %float %v2float_0_0 %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0.0f), + // Test case 8: Fold v dot (0.0, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%v = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %v\n" + + "%3 = OpDot %float %2 %v2float_0_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0.0f), + // Test case 9: Fold Null dot v + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%v = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %v\n" + + "%3 = OpDot %float %v2float_null %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0.0f), + // Test case 10: Fold v dot Null + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%v = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %v\n" + + "%3 = OpDot %float %2 %v2float_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0.0f), + // Test case 11: Fold -2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFNegate %float %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -2), + // Test case 12: QuantizeToF16 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpQuantizeToF16 %float %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 13: QuantizeToF16 positive non exact + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpQuantizeToF16 %float %float_2049\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2048), + // Test case 14: QuantizeToF16 negative non exact + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpQuantizeToF16 %float %float_n2049\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -2048), + // Test case 15: QuantizeToF16 large positive + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpQuantizeToF16 %float %float_1e16\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::infinity()), + // Test case 16: QuantizeToF16 large negative + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpQuantizeToF16 %float %float_n1e16\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits::infinity()), + // Test case 17: QuantizeToF16 small positive + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpQuantizeToF16 %float %float_1en16\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 18: QuantizeToF16 small negative + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpQuantizeToF16 %float %float_n1en16\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 19: QuantizeToF16 nan + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpQuantizeToF16 %float %float_nan\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::quiet_NaN()), + // Test case 20: FMix 1.0 4.0 0.2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMix %float_1 %float_4 %float_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.6f), + // Test case 21: FMin 1.0 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMin %float_1 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0f), + // Test case 22: FMin 4.0 0.2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMin %float_4 %float_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.2f), + // Test case 23: FMax 1.0 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMax %float_1 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0f), + // Test case 24: FMax 1.0 0.2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FMax %float_1 %float_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0f), + // Test case 25: FClamp 1.0 0.2 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_1 %float_0p2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0f), + // Test case 26: FClamp 0.2 2.0 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_0p2 %float_2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0f), + // Test case 27: FClamp 2049.0 2.0 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 FClamp %float_2049 %float_2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0f), + // Test case 28: FClamp 1.0 2.0 x + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %float\n" + + "%2 = OpExtInst %float %1 FClamp %float_1 %float_2 %undef\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 29: FClamp 1.0 x 0.5 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %float\n" + + "%2 = OpExtInst %float %1 FClamp %float_1 %undef %float_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.5), + // Test case 30: Sin 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Sin %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 31: Cos 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Cos %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 32: Tan 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Tan %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 33: Asin 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Asin %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 34: Acos 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Acos %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 35: Atan 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Atan %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 36: Exp 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Exp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 37: Log 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Log %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 38: Exp2 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Exp2 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 39: Log2 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Log2 %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 40: Sqrt 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Sqrt %float_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 41: Atan2 0.0 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Atan2 %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.0), + // Test case 42: Pow 2.0 3.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %float %1 Pow %float_2 %float_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 8.0) +)); +// clang-format on + +using DoubleInstructionFoldingTest = + ::testing::TestWithParam>; + +TEST_P(DoubleInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_TRUE(succeeded); + if (inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + EXPECT_EQ(inst->opcode(), SpvOpConstant); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::FloatConstant* result = + const_mrg->GetConstantFromInst(inst)->AsFloatConstant(); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + EXPECT_EQ(result->GetDoubleValue(), tc.expected_result); + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(DoubleConstantFoldingTest, DoubleInstructionFoldingTest, +::testing::Values( + // Test case 0: Fold 2.0 - 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFSub %double %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 1: Fold 2.0 + 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFAdd %double %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3.0), + // Test case 2: Fold 3.0 * 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFMul %double %double_3 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 6.0), + // Test case 3: Fold 1.0 / 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.5), + // Test case 4: Fold 1.0 / 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_1 %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, std::numeric_limits::infinity()), + // Test case 5: Fold -1.0 / 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFDiv %double %double_n1 %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -std::numeric_limits::infinity()), + // Test case 6: Fold (2.0, 3.0) dot (2.0, 0.5) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpDot %double %v2double_2_3 %v2double_2_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 5.5f), + // Test case 7: Fold (0.0, 0.0) dot v + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%v = OpVariable %_ptr_v2double Function\n" + + "%2 = OpLoad %v2double %v\n" + + "%3 = OpDot %double %v2double_0_0 %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0.0f), + // Test case 8: Fold v dot (0.0, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%v = OpVariable %_ptr_v2double Function\n" + + "%2 = OpLoad %v2double %v\n" + + "%3 = OpDot %double %2 %v2double_0_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0.0f), + // Test case 9: Fold Null dot v + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%v = OpVariable %_ptr_v2double Function\n" + + "%2 = OpLoad %v2double %v\n" + + "%3 = OpDot %double %v2double_null %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0.0f), + // Test case 10: Fold v dot Null + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%v = OpVariable %_ptr_v2double Function\n" + + "%2 = OpLoad %v2double %v\n" + + "%3 = OpDot %double %2 %v2double_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0.0f), + // Test case 11: Fold -2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFNegate %double %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, -2), + // Test case 12: FMin 1.0 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 FMin %double_1 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 13: FMin 4.0 0.2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 FMin %double_4 %double_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.2), + // Test case 14: FMax 1.0 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 FMax %double_1 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 15: FMax 1.0 0.2 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 FMax %double_1 %double_0p2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 16: FClamp 1.0 0.2 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 FClamp %double_1 %double_0p2 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 1.0), + // Test case 17: FClamp 0.2 2.0 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 FClamp %double_0p2 %double_2 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 18: FClamp 5.0 2.0 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpExtInst %double %1 FClamp %double_5 %double_2 %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4.0), + // Test case 19: FClamp 1.0 2.0 x + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %double\n" + + "%2 = OpExtInst %double %1 FClamp %double_1 %double_2 %undef\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 20: FClamp 1.0 x 0.5 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %double\n" + + "%2 = OpExtInst %double %1 FClamp %double_1 %undef %double_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0.5), + // Test case 21: Sqrt 4.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %double\n" + + "%2 = OpExtInst %double %1 Sqrt %double_4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 2.0), + // Test case 22: Pow 2.0 3.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%undef = OpUndef %double\n" + + "%2 = OpExtInst %double %1 Pow %double_2 %double_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 8.0) +)); +// clang-format on + +// clang-format off +INSTANTIATE_TEST_SUITE_P(DoubleOrderedCompareConstantFoldingTest, BooleanInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold 1.0 == 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdEqual %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 1: fold 1.0 != 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdNotEqual %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold 1.0 < 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThan %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: fold 1.0 > 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThan %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 4: fold 1.0 <= 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThanEqual %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: fold 1.0 >= 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThanEqual %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 6: fold 1.0 == 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdEqual %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: fold 1.0 != 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdNotEqual %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 8: fold 1.0 < 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThan %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 9: fold 1.0 > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThan %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 10: fold 1.0 <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThanEqual %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 11: fold 1.0 >= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThanEqual %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 12: fold 2.0 < 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThan %bool %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 13: fold 2.0 > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThan %bool %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 14: fold 2.0 <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThanEqual %bool %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 15: fold 2.0 >= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThanEqual %bool %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(DoubleUnorderedCompareConstantFoldingTest, BooleanInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold 1.0 == 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordEqual %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 1: fold 1.0 != 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordNotEqual %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold 1.0 < 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThan %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: fold 1.0 > 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThan %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 4: fold 1.0 <= 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThanEqual %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: fold 1.0 >= 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThanEqual %bool %double_1 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 6: fold 1.0 == 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordEqual %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: fold 1.0 != 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordNotEqual %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 8: fold 1.0 < 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThan %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 9: fold 1.0 > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThan %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 10: fold 1.0 <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThanEqual %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 11: fold 1.0 >= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThanEqual %bool %double_1 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 12: fold 2.0 < 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThan %bool %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 13: fold 2.0 > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThan %bool %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 14: fold 2.0 <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThanEqual %bool %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 15: fold 2.0 >= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThanEqual %bool %double_2 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(FloatOrderedCompareConstantFoldingTest, BooleanInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold 1.0 == 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdEqual %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 1: fold 1.0 != 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdNotEqual %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold 1.0 < 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThan %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: fold 1.0 > 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThan %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 4: fold 1.0 <= 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThanEqual %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: fold 1.0 >= 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThanEqual %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 6: fold 1.0 == 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdEqual %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: fold 1.0 != 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdNotEqual %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 8: fold 1.0 < 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThan %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 9: fold 1.0 > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThan %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 10: fold 1.0 <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThanEqual %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 11: fold 1.0 >= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThanEqual %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 12: fold 2.0 < 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThan %bool %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 13: fold 2.0 > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThan %bool %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 14: fold 2.0 <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdLessThanEqual %bool %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 15: fold 2.0 >= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdGreaterThanEqual %bool %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(FloatUnorderedCompareConstantFoldingTest, BooleanInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold 1.0 == 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordEqual %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 1: fold 1.0 != 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordNotEqual %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold 1.0 < 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThan %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: fold 1.0 > 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThan %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 4: fold 1.0 <= 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThanEqual %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: fold 1.0 >= 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThanEqual %bool %float_1 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 6: fold 1.0 == 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordEqual %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 7: fold 1.0 != 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordNotEqual %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 8: fold 1.0 < 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThan %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 9: fold 1.0 > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThan %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 10: fold 1.0 <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThanEqual %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 11: fold 1.0 >= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThanEqual %bool %float_1 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 12: fold 2.0 < 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThan %bool %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 13: fold 2.0 > 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThan %bool %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 14: fold 2.0 <= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordLessThanEqual %bool %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 15: fold 2.0 >= 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordGreaterThanEqual %bool %float_2 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(DoubleNaNCompareConstantFoldingTest, BooleanInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold NaN == 0 (ord) + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdEqual %bool %double_nan %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 1: fold NaN == NaN (unord) + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordEqual %bool %double_nan %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold NaN != NaN (ord) + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdNotEqual %bool %double_nan %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 3: fold NaN != NaN (unord) + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordNotEqual %bool %double_nan %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(FloatNaNCompareConstantFoldingTest, BooleanInstructionFoldingTest, + ::testing::Values( + // Test case 0: fold NaN == 0 (ord) + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdEqual %bool %float_nan %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 1: fold NaN == NaN (unord) + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordEqual %bool %float_nan %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: fold NaN != NaN (ord) + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFOrdNotEqual %bool %float_nan %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, false), + // Test case 3: fold NaN != NaN (unord) + InstructionFoldingCase( + HeaderWithNaN() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFUnordNotEqual %bool %float_nan %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); +// clang-format on + +template +struct InstructionFoldingCaseWithMap { + InstructionFoldingCaseWithMap(const std::string& tb, uint32_t id, + ResultType result, + std::function map) + : test_body(tb), id_to_fold(id), expected_result(result), id_map(map) {} + + std::string test_body; + uint32_t id_to_fold; + ResultType expected_result; + std::function id_map; +}; + +using IntegerInstructionFoldingTestWithMap = + ::testing::TestWithParam>; + +TEST_P(IntegerInstructionFoldingTestWithMap, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + inst = context->get_instruction_folder().FoldInstructionToConstant(inst, + tc.id_map); + + // Make sure the instruction folded as expected. + EXPECT_NE(inst, nullptr); + if (inst != nullptr) { + EXPECT_EQ(inst->opcode(), SpvOpConstant); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::IntConstant* result = + const_mrg->GetConstantFromInst(inst)->AsIntConstant(); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + EXPECT_EQ(result->GetU32BitValue(), tc.expected_result); + } + } +} +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, IntegerInstructionFoldingTestWithMap, + ::testing::Values( + // Test case 0: fold %3 = 0; %3 * n + InstructionFoldingCaseWithMap( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%3 = OpCopyObject %int %int_0\n" + "%2 = OpIMul %int %3 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0, [](uint32_t id) {return (id == 3 ? INT_0_ID : id);}) + )); +// clang-format on + +using BooleanInstructionFoldingTestWithMap = + ::testing::TestWithParam>; + +TEST_P(BooleanInstructionFoldingTestWithMap, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + inst = context->get_instruction_folder().FoldInstructionToConstant(inst, + tc.id_map); + + // Make sure the instruction folded as expected. + EXPECT_NE(inst, nullptr); + if (inst != nullptr) { + std::vector bool_opcodes = {SpvOpConstantTrue, SpvOpConstantFalse}; + EXPECT_THAT(bool_opcodes, Contains(inst->opcode())); + analysis::ConstantManager* const_mrg = context->get_constant_mgr(); + const analysis::BoolConstant* result = + const_mrg->GetConstantFromInst(inst)->AsBoolConstant(); + EXPECT_NE(result, nullptr); + if (result != nullptr) { + EXPECT_EQ(result->value(), tc.expected_result); + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(TestCase, BooleanInstructionFoldingTestWithMap, + ::testing::Values( + // Test case 0: fold %3 = true; %3 || n + InstructionFoldingCaseWithMap( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%load = OpLoad %bool %n\n" + + "%3 = OpCopyObject %bool %true\n" + + "%2 = OpLogicalOr %bool %3 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true, [](uint32_t id) {return (id == 3 ? TRUE_ID : id);}) + )); +// clang-format on + +using GeneralInstructionFoldingTest = + ::testing::TestWithParam>; + +TEST_P(GeneralInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + std::unique_ptr original_inst(inst->Clone(context.get())); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_EQ(inst->result_id(), original_inst->result_id()); + EXPECT_EQ(inst->type_id(), original_inst->type_id()); + EXPECT_TRUE((!succeeded) == (tc.expected_result == 0)); + if (succeeded) { + EXPECT_EQ(inst->opcode(), SpvOpCopyObject); + EXPECT_EQ(inst->GetSingleWordInOperand(0), tc.expected_result); + } else { + EXPECT_EQ(inst->NumInOperands(), original_inst->NumInOperands()); + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + EXPECT_EQ(inst->GetOperand(i), original_inst->GetOperand(i)); + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(IntegerArithmeticTestCases, GeneralInstructionFoldingTest, + ::testing::Values( + // Test case 0: Don't fold n * m + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%load_m = OpLoad %int %m\n" + + "%2 = OpIMul %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Don't fold n / m (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpUDiv %uint %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 2: Don't fold n / m (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%load_m = OpLoad %int %m\n" + + "%2 = OpSDiv %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 3: Don't fold n remainder m + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%load_m = OpLoad %int %m\n" + + "%2 = OpSRem %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 4: Don't fold n % m (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%load_m = OpLoad %int %m\n" + + "%2 = OpSMod %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 5: Don't fold n % m (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpUMod %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 6: Don't fold n << m + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpShiftRightLogical %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 7: Don't fold n >> m + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpShiftLeftLogical %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 8: Don't fold n | m + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpBitwiseOr %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 9: Don't fold n & m + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpBitwiseAnd %int %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 10: Don't fold n < m (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpULessThan %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 11: Don't fold n > m (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpUGreaterThan %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 12: Don't fold n <= m (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpULessThanEqual %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 13: Don't fold n >= m (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%m = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%load_m = OpLoad %uint %m\n" + + "%2 = OpUGreaterThanEqual %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 14: Don't fold n < m (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%load_m = OpLoad %int %m\n" + + "%2 = OpULessThan %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 15: Don't fold n > m (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%load_m = OpLoad %int %m\n" + + "%2 = OpUGreaterThan %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 16: Don't fold n <= m (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%load_m = OpLoad %int %m\n" + + "%2 = OpULessThanEqual %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 17: Don't fold n >= m (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%m = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%load_m = OpLoad %int %m\n" + + "%2 = OpUGreaterThanEqual %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 18: Don't fold n || m + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%m = OpVariable %_ptr_bool Function\n" + + "%load_n = OpLoad %bool %n\n" + + "%load_m = OpLoad %bool %m\n" + + "%2 = OpLogicalOr %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 19: Don't fold n && m + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%m = OpVariable %_ptr_bool Function\n" + + "%load_n = OpLoad %bool %n\n" + + "%load_m = OpLoad %bool %m\n" + + "%2 = OpLogicalAnd %bool %load_n %load_m\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 20: Don't fold n * 3 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%2 = OpIMul %int %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 21: Don't fold n / 3 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpUDiv %uint %load_n %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 22: Don't fold n / 3 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%2 = OpSDiv %int %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 23: Don't fold n remainder 3 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%2 = OpSRem %int %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 24: Don't fold n % 3 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%2 = OpSMod %int %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 25: Don't fold n % 3 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpUMod %int %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 26: Don't fold n << 3 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpShiftRightLogical %int %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 27: Don't fold n >> 3 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpShiftLeftLogical %int %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 28: Don't fold n | 3 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpBitwiseOr %int %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 29: Don't fold n & 3 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpBitwiseAnd %uint %load_n %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 30: Don't fold n < 3 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpULessThan %bool %load_n %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 31: Don't fold n > 3 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpUGreaterThan %bool %load_n %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 32: Don't fold n <= 3 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpULessThanEqual %bool %load_n %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 33: Don't fold n >= 3 (unsigned) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%load_n = OpLoad %uint %n\n" + + "%2 = OpUGreaterThanEqual %bool %load_n %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 34: Don't fold n < 3 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%2 = OpULessThan %bool %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 35: Don't fold n > 3 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%2 = OpUGreaterThan %bool %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 36: Don't fold n <= 3 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%2 = OpULessThanEqual %bool %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 37: Don't fold n >= 3 (signed) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load_n = OpLoad %int %n\n" + + "%2 = OpUGreaterThanEqual %bool %load_n %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 38: Don't fold 2 + 3 (long), bad length + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpIAdd %long %long_2 %long_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 39: Don't fold 2 + 3 (short), bad length + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpIAdd %short %short_2 %short_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 40: fold 1*n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%2 = OpIMul %int %int_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 41: fold n*1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%2 = OpIMul %int %3 %int_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 42: Don't fold comparisons of 64-bit types + // (https://github.com/KhronosGroup/SPIRV-Tools/issues/3343). + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpSLessThan %bool %long_0 %long_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0) +)); + +INSTANTIATE_TEST_SUITE_P(CompositeExtractFoldingTest, GeneralInstructionFoldingTest, +::testing::Values( + // Test case 0: fold Insert feeding extract + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %n\n" + + "%3 = OpCompositeInsert %v4int %2 %v4int_0_0_0_0 0\n" + + "%4 = OpCompositeInsert %v4int %int_1 %3 1\n" + + "%5 = OpCompositeInsert %v4int %int_1 %4 2\n" + + "%6 = OpCompositeInsert %v4int %int_1 %5 3\n" + + "%7 = OpCompositeExtract %int %6 0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 7, 2), + // Test case 1: fold Composite construct feeding extract (position 0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %n\n" + + "%3 = OpCompositeConstruct %v4int %2 %int_0 %int_0 %int_0\n" + + "%4 = OpCompositeExtract %int %3 0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, 2), + // Test case 2: fold Composite construct feeding extract (position 3) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %n\n" + + "%3 = OpCompositeConstruct %v4int %2 %int_0 %int_0 %100\n" + + "%4 = OpCompositeExtract %int %3 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, INT_0_ID), + // Test case 3: fold Composite construct with vectors feeding extract (scalar element) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %n\n" + + "%3 = OpCompositeConstruct %v2int %2 %int_0\n" + + "%4 = OpCompositeConstruct %v4int %3 %int_0 %100\n" + + "%5 = OpCompositeExtract %int %4 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, INT_0_ID), + // Test case 4: fold Composite construct with vectors feeding extract (start of vector element) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %n\n" + + "%3 = OpCompositeConstruct %v2int %2 %int_0\n" + + "%4 = OpCompositeConstruct %v4int %3 %int_0 %100\n" + + "%5 = OpCompositeExtract %int %4 0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, 2), + // Test case 5: fold Composite construct with vectors feeding extract (middle of vector element) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %n\n" + + "%3 = OpCompositeConstruct %v2int %int_0 %2\n" + + "%4 = OpCompositeConstruct %v4int %3 %int_0 %100\n" + + "%5 = OpCompositeExtract %int %4 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, 2), + // Test case 6: fold Composite construct with multiple indices. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %n\n" + + "%3 = OpCompositeConstruct %v2int %int_0 %2\n" + + "%4 = OpCompositeConstruct %struct_v2int_int_int %3 %int_0 %100\n" + + "%5 = OpCompositeExtract %int %4 0 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, 2), + // Test case 7: fold constant extract. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeExtract %int %102 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, INT_7_ID), + // Test case 8: constant struct has OpUndef + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeExtract %int %struct_undef_0_0 0 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 9: Extracting a member of element inserted via Insert + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_struct_v2int_int_int Function\n" + + "%2 = OpLoad %struct_v2int_int_int %n\n" + + "%3 = OpCompositeInsert %struct_v2int_int_int %102 %2 0\n" + + "%4 = OpCompositeExtract %int %3 0 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, 103), + // Test case 10: Extracting a element that is partially changed by Insert. (Don't fold) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_struct_v2int_int_int Function\n" + + "%2 = OpLoad %struct_v2int_int_int %n\n" + + "%3 = OpCompositeInsert %struct_v2int_int_int %int_0 %2 0 1\n" + + "%4 = OpCompositeExtract %v2int %3 0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, 0), + // Test case 11: Extracting from result of vector shuffle (first input) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%2 = OpLoad %v2int %n\n" + + "%3 = OpVectorShuffle %v2int %102 %2 3 0\n" + + "%4 = OpCompositeExtract %int %3 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, INT_7_ID), + // Test case 12: Extracting from result of vector shuffle (second input) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%2 = OpLoad %v2int %n\n" + + "%3 = OpVectorShuffle %v2int %2 %102 2 0\n" + + "%4 = OpCompositeExtract %int %3 0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, INT_7_ID), + // Test case 13: https://github.com/KhronosGroup/SPIRV-Tools/issues/2608 + // Out of bounds access. Do not fold. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1\n" + + "%3 = OpCompositeExtract %float %2 4\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 0), + // Test case 14: https://github.com/KhronosGroup/SPIRV-Tools/issues/3631 + // Extract the component right after the vector constituent. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeConstruct %v2int %int_0 %int_0\n" + + "%3 = OpCompositeConstruct %v4int %2 %100 %int_0\n" + + "%4 = OpCompositeExtract %int %3 2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, INT_0_ID) +)); + +INSTANTIATE_TEST_SUITE_P(CompositeConstructFoldingTest, GeneralInstructionFoldingTest, +::testing::Values( + // Test case 0: fold Extracts feeding construct + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCopyObject %v4int %v4int_0_0_0_0\n" + + "%3 = OpCompositeExtract %int %2 0\n" + + "%4 = OpCompositeExtract %int %2 1\n" + + "%5 = OpCompositeExtract %int %2 2\n" + + "%6 = OpCompositeExtract %int %2 3\n" + + "%7 = OpCompositeConstruct %v4int %3 %4 %5 %6\n" + + "OpReturn\n" + + "OpFunctionEnd", + 7, 2), + // Test case 1: Don't fold Extracts feeding construct (Different source) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCopyObject %v4int %v4int_0_0_0_0\n" + + "%3 = OpCompositeExtract %int %2 0\n" + + "%4 = OpCompositeExtract %int %2 1\n" + + "%5 = OpCompositeExtract %int %2 2\n" + + "%6 = OpCompositeExtract %int %v4int_0_0_0_0 3\n" + + "%7 = OpCompositeConstruct %v4int %3 %4 %5 %6\n" + + "OpReturn\n" + + "OpFunctionEnd", + 7, 0), + // Test case 2: Don't fold Extracts feeding construct (bad indices) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCopyObject %v4int %v4int_0_0_0_0\n" + + "%3 = OpCompositeExtract %int %2 0\n" + + "%4 = OpCompositeExtract %int %2 0\n" + + "%5 = OpCompositeExtract %int %2 2\n" + + "%6 = OpCompositeExtract %int %2 3\n" + + "%7 = OpCompositeConstruct %v4int %3 %4 %5 %6\n" + + "OpReturn\n" + + "OpFunctionEnd", + 7, 0), + // Test case 3: Don't fold Extracts feeding construct (different type) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCopyObject %struct_v2int_int_int %struct_v2int_int_int_null\n" + + "%3 = OpCompositeExtract %v2int %2 0\n" + + "%4 = OpCompositeExtract %int %2 1\n" + + "%5 = OpCompositeExtract %int %2 2\n" + + "%7 = OpCompositeConstruct %v4int %3 %4 %5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 7, 0), + // Test case 4: Fold construct with constants to constant. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpCompositeConstruct %v2int %103 %103\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, VEC2_0_ID), + // Test case 5: Don't segfault when trying to fold an OpCompositeConstruct + // for an empty struct, and we reached the id limit. + InstructionFoldingCase( + Header() + "%empty_struct = OpTypeStruct\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%4194303 = OpCompositeConstruct %empty_struct\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4194303, 0) +)); + +INSTANTIATE_TEST_SUITE_P(PhiFoldingTest, GeneralInstructionFoldingTest, +::testing::Values( + // Test case 0: Fold phi with the same values for all edges. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + " OpBranchConditional %true %l1 %l2\n" + + "%l1 = OpLabel\n" + + " OpBranch %merge_lab\n" + + "%l2 = OpLabel\n" + + " OpBranch %merge_lab\n" + + "%merge_lab = OpLabel\n" + + "%2 = OpPhi %int %100 %l1 %100 %l2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, INT_0_ID), + // Test case 1: Fold phi in pass through loop. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + " OpBranch %l1\n" + + "%l1 = OpLabel\n" + + "%2 = OpPhi %int %100 %main_lab %2 %l1\n" + + " OpBranchConditional %true %l1 %merge_lab\n" + + "%merge_lab = OpLabel\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, INT_0_ID), + // Test case 2: Don't Fold phi because of different values. + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + " OpBranch %l1\n" + + "%l1 = OpLabel\n" + + "%2 = OpPhi %int %int_0 %main_lab %int_3 %l1\n" + + " OpBranchConditional %true %l1 %merge_lab\n" + + "%merge_lab = OpLabel\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0) +)); + +INSTANTIATE_TEST_SUITE_P(FloatRedundantFoldingTest, GeneralInstructionFoldingTest, + ::testing::Values( + // Test case 0: Don't fold n + 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFAdd %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Don't fold n - 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFSub %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 2: Don't fold n * 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFMul %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 3: Fold n + 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFAdd %float %3 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 4: Fold 0.0 + n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFAdd %float %float_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 5: Fold n - 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFSub %float %3 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 6: Fold n * 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFMul %float %3 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 7: Fold 1.0 * n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFMul %float %float_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 8: Fold n / 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFDiv %float %3 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 9: Fold n * 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFMul %float %3 %104\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, FLOAT_0_ID), + // Test case 10: Fold 0.0 * n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFMul %float %104 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, FLOAT_0_ID), + // Test case 11: Fold 0.0 / n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFDiv %float %104 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, FLOAT_0_ID), + // Test case 12: Don't fold mix(a, b, 2.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%b = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %a\n" + + "%4 = OpLoad %float %b\n" + + "%2 = OpExtInst %float %1 FMix %3 %4 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 13: Fold mix(a, b, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%b = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %a\n" + + "%4 = OpLoad %float %b\n" + + "%2 = OpExtInst %float %1 FMix %3 %4 %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 14: Fold mix(a, b, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_float Function\n" + + "%b = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %a\n" + + "%4 = OpLoad %float %b\n" + + "%2 = OpExtInst %float %1 FMix %3 %4 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4), + // Test case 15: Fold vector fadd with null + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %a\n" + + "%3 = OpFAdd %v2float %2 %v2float_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 2), + // Test case 16: Fold vector fadd with null + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %a\n" + + "%3 = OpFAdd %v2float %v2float_null %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 2), + // Test case 17: Fold vector fsub with null + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %a\n" + + "%3 = OpFSub %v2float %2 %v2float_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, 2), + // Test case 18: Fold 0.0(half) * n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_half Function\n" + + "%3 = OpLoad %half %n\n" + + "%2 = OpFMul %half %108 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, HALF_0_ID), + // Test case 19: Don't fold 1.0(half) * n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_half Function\n" + + "%3 = OpLoad %half %n\n" + + "%2 = OpFMul %half %half_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 20: Don't fold 1.0 * 1.0 (half) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFMul %half %half_1 %half_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 21: Don't fold (0.0, 1.0) * (0.0, 1.0) (half) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFMul %v2half %half_0_1 %half_0_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 22: Don't fold (0.0, 1.0) dotp (0.0, 1.0) (half) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpDot %half %half_0_1 %half_0_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0) +)); + +INSTANTIATE_TEST_SUITE_P(DoubleRedundantFoldingTest, GeneralInstructionFoldingTest, + ::testing::Values( + // Test case 0: Don't fold n + 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFAdd %double %3 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Don't fold n - 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFSub %double %3 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 2: Don't fold n * 2.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFMul %double %3 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 3: Fold n + 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFAdd %double %3 %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 4: Fold 0.0 + n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFAdd %double %double_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 5: Fold n - 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFSub %double %3 %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 6: Fold n * 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFMul %double %3 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 7: Fold 1.0 * n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFMul %double %double_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 8: Fold n / 1.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFDiv %double %3 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 9: Fold n * 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFMul %double %3 %105\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, DOUBLE_0_ID), + // Test case 10: Fold 0.0 * n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFMul %double %105 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, DOUBLE_0_ID), + // Test case 11: Fold 0.0 / n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFDiv %double %105 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, DOUBLE_0_ID), + // Test case 12: Don't fold mix(a, b, 2.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_double Function\n" + + "%b = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %a\n" + + "%4 = OpLoad %double %b\n" + + "%2 = OpExtInst %double %1 FMix %3 %4 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 13: Fold mix(a, b, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_double Function\n" + + "%b = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %a\n" + + "%4 = OpLoad %double %b\n" + + "%2 = OpExtInst %double %1 FMix %3 %4 %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 14: Fold mix(a, b, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%a = OpVariable %_ptr_double Function\n" + + "%b = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %a\n" + + "%4 = OpLoad %double %b\n" + + "%2 = OpExtInst %double %1 FMix %3 %4 %double_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 4) +)); + +INSTANTIATE_TEST_SUITE_P(FloatVectorRedundantFoldingTest, GeneralInstructionFoldingTest, + ::testing::Values( + // Test case 0: Don't fold a * vec4(0.0, 0.0, 0.0, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4float Function\n" + + "%3 = OpLoad %v4float %n\n" + + "%2 = OpFMul %v4float %3 %v4float_0_0_0_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Fold a * vec4(0.0, 0.0, 0.0, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4float Function\n" + + "%3 = OpLoad %v4float %n\n" + + "%2 = OpFMul %v4float %3 %106\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, VEC4_0_ID), + // Test case 2: Fold a * vec4(1.0, 1.0, 1.0, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4float Function\n" + + "%3 = OpLoad %v4float %n\n" + + "%2 = OpFMul %v4float %3 %v4float_1_1_1_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3) +)); + +INSTANTIATE_TEST_SUITE_P(DoubleVectorRedundantFoldingTest, GeneralInstructionFoldingTest, + ::testing::Values( + // Test case 0: Don't fold a * vec4(0.0, 0.0, 0.0, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%3 = OpLoad %v4double %n\n" + + "%2 = OpFMul %v4double %3 %v4double_0_0_0_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Fold a * vec4(0.0, 0.0, 0.0, 0.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%3 = OpLoad %v4double %n\n" + + "%2 = OpFMul %v4double %3 %106\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, DVEC4_0_ID), + // Test case 2: Fold a * vec4(1.0, 1.0, 1.0, 1.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%3 = OpLoad %v4double %n\n" + + "%2 = OpFMul %v4double %3 %v4double_1_1_1_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3) +)); + +INSTANTIATE_TEST_SUITE_P(IntegerRedundantFoldingTest, GeneralInstructionFoldingTest, + ::testing::Values( + // Test case 0: Don't fold n + 1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpIAdd %uint %3 %uint_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Don't fold 1 + n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpIAdd %uint %uint_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 2: Fold n + 0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpIAdd %uint %3 %uint_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 3: Fold 0 + n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_uint Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpIAdd %uint %uint_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 4: Don't fold n + (1,0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%3 = OpLoad %v2int %n\n" + + "%2 = OpIAdd %v2int %3 %v2int_1_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 5: Don't fold (1,0) + n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%3 = OpLoad %v2int %n\n" + + "%2 = OpIAdd %v2int %v2int_1_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 6: Fold n + (0,0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%3 = OpLoad %v2int %n\n" + + "%2 = OpIAdd %v2int %3 %v2int_0_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 7: Fold (0,0) + n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%3 = OpLoad %v2int %n\n" + + "%2 = OpIAdd %v2int %v2int_0_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3) +)); + +INSTANTIATE_TEST_SUITE_P(ClampAndCmpLHS, GeneralInstructionFoldingTest, +::testing::Values( + // Test case 0: Don't Fold 0.0 < clamp(-1, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFUnordLessThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Don't Fold 0.0 < clamp(-1, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFOrdLessThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 2: Don't Fold 0.0 <= clamp(-1, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFUnordLessThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 3: Don't Fold 0.0 <= clamp(-1, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFOrdLessThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 4: Don't Fold 0.0 > clamp(-1, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFUnordGreaterThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 5: Don't Fold 0.0 > clamp(-1, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFOrdGreaterThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 6: Don't Fold 0.0 >= clamp(-1, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFUnordGreaterThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 7: Don't Fold 0.0 >= clamp(-1, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFOrdGreaterThanEqual %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 8: Don't Fold 0.0 < clamp(0, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFUnordLessThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 9: Don't Fold 0.0 < clamp(0, 1) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFOrdLessThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 10: Don't Fold 0.0 > clamp(-1, 0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFUnordGreaterThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 11: Don't Fold 0.0 > clamp(-1, 0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFOrdGreaterThan %bool %float_0 %clamp\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0) +)); + +INSTANTIATE_TEST_SUITE_P(ClampAndCmpRHS, GeneralInstructionFoldingTest, +::testing::Values( + // Test case 0: Don't Fold clamp(-1, 1) < 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFUnordLessThan %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Don't Fold clamp(-1, 1) < 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFOrdLessThan %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 2: Don't Fold clamp(-1, 1) <= 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFUnordLessThanEqual %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 3: Don't Fold clamp(-1, 1) <= 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFOrdLessThanEqual %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 4: Don't Fold clamp(-1, 1) > 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFUnordGreaterThan %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 5: Don't Fold clamp(-1, 1) > 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFOrdGreaterThan %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 6: Don't Fold clamp(-1, 1) >= 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFUnordGreaterThanEqual %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 7: Don't Fold clamp(-1, 1) >= 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_1\n" + + "%2 = OpFOrdGreaterThanEqual %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 8: Don't Fold clamp(-1, 0) < 0.0 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFUnordLessThan %bool %clamp %float_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 9: Don't Fold clamp(0, 1) < 1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_0 %float_1\n" + + "%2 = OpFOrdLessThan %bool %clamp %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 10: Don't Fold clamp(-1, 0) > -1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFUnordGreaterThan %bool %clamp %float_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 11: Don't Fold clamp(-1, 0) > -1 + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%ld = OpLoad %float %n\n" + + "%clamp = OpExtInst %float %1 FClamp %ld %float_n1 %float_0\n" + + "%2 = OpFOrdGreaterThan %bool %clamp %float_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0) +)); + +INSTANTIATE_TEST_SUITE_P(FToIConstantFoldingTest, IntegerInstructionFoldingTest, + ::testing::Values( + // Test case 0: Fold int(3.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpConvertFToS %int %float_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 1: Fold uint(3.0) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpConvertFToU %int %float_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3) +)); + +INSTANTIATE_TEST_SUITE_P(IToFConstantFoldingTest, FloatInstructionFoldingTest, + ::testing::Values( + // Test case 0: Fold float(3) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpConvertSToF %float %int_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3.0), + // Test case 1: Fold float(3u) + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpConvertUToF %float %uint_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3.0) +)); +// clang-format on + +using ToNegateFoldingTest = + ::testing::TestWithParam>; + +TEST_P(ToNegateFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + std::unique_ptr original_inst(inst->Clone(context.get())); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + + // Make sure the instruction folded as expected. + EXPECT_EQ(inst->result_id(), original_inst->result_id()); + EXPECT_EQ(inst->type_id(), original_inst->type_id()); + EXPECT_TRUE((!succeeded) == (tc.expected_result == 0)); + if (succeeded) { + EXPECT_EQ(inst->opcode(), SpvOpFNegate); + EXPECT_EQ(inst->GetSingleWordInOperand(0), tc.expected_result); + } else { + EXPECT_EQ(inst->NumInOperands(), original_inst->NumInOperands()); + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + EXPECT_EQ(inst->GetOperand(i), original_inst->GetOperand(i)); + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(FloatRedundantSubFoldingTest, ToNegateFoldingTest, + ::testing::Values( + // Test case 0: Don't fold 1.0 - n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFSub %float %float_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Fold 0.0 - n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %n\n" + + "%2 = OpFSub %float %float_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 2: Don't fold (0,0,0,1) - n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4float Function\n" + + "%3 = OpLoad %v4float %n\n" + + "%2 = OpFSub %v4float %v4float_0_0_0_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 3: Fold (0,0,0,0) - n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4float Function\n" + + "%3 = OpLoad %v4float %n\n" + + "%2 = OpFSub %v4float %v4float_0_0_0_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3) +)); + +INSTANTIATE_TEST_SUITE_P(DoubleRedundantSubFoldingTest, ToNegateFoldingTest, + ::testing::Values( + // Test case 0: Don't fold 1.0 - n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFSub %double %double_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 1: Fold 0.0 - n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%3 = OpLoad %double %n\n" + + "%2 = OpFSub %double %double_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3), + // Test case 2: Don't fold (0,0,0,1) - n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%3 = OpLoad %v4double %n\n" + + "%2 = OpFSub %v4double %v4double_0_0_0_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 0), + // Test case 3: Fold (0,0,0,0) - n + InstructionFoldingCase( + Header() + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%3 = OpLoad %v4double %n\n" + + "%2 = OpFSub %v4double %v4double_0_0_0_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, 3) +)); + +using MatchingInstructionFoldingTest = + ::testing::TestWithParam>; + +TEST_P(MatchingInstructionFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + std::unique_ptr original_inst(inst->Clone(context.get())); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + EXPECT_EQ(succeeded, tc.expected_result); + if (succeeded) { + Match(tc.test_body, context.get()); + } +} + +INSTANTIATE_TEST_SUITE_P(RedundantIntegerMatching, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: Fold 0 + n (change sign) + InstructionFoldingCase( + Header() + + "; CHECK: [[uint:%\\w+]] = OpTypeInt 32 0\n" + + "; CHECK: %2 = OpBitcast [[uint]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %uint %n\n" + + "%2 = OpIAdd %uint %int_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 2, true), + // Test case 0: Fold 0 + n (change sign) + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: %2 = OpBitcast [[int]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%3 = OpLoad %int %n\n" + + "%2 = OpIAdd %int %uint_0 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(MergeNegateTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: fold consecutive fnegate + // -(-x) = x + InstructionFoldingCase( + Header() + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float:%\\w+]]\n" + + "; CHECK: %4 = OpCopyObject [[float]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFNegate %float %2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 1: fold fnegate(fmul with const). + // -(x * 2.0) = x * -2.0 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n2:%\\w+]] = OpConstant [[float]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[ld]] [[float_n2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFMul %float %2 %float_2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 2: fold fnegate(fmul with const). + // -(2.0 * x) = x * 2.0 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n2:%\\w+]] = OpConstant [[float]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[ld]] [[float_n2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFMul %float %float_2 %2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 3: fold fnegate(fdiv with const). + // -(x / 2.0) = x * -0.5 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n0p5:%\\w+]] = OpConstant [[float]] -0.5\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[ld]] [[float_n0p5]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %2 %float_2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 4: fold fnegate(fdiv with const). + // -(2.0 / x) = -2.0 / x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n2:%\\w+]] = OpConstant [[float]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %float_2 %2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 5: fold fnegate(fadd with const). + // -(2.0 + x) = -2.0 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n2:%\\w+]] = OpConstant [[float]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %float_2 %2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 6: fold fnegate(fadd with const). + // -(x + 2.0) = -2.0 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n2:%\\w+]] = OpConstant [[float]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %2 %float_2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 7: fold fnegate(fsub with const). + // -(2.0 - x) = x - 2.0 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[ld]] [[float_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %float_2 %2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 8: fold fnegate(fsub with const). + // -(x - 2.0) = 2.0 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %2 %float_2\n" + + "%4 = OpFNegate %float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 9: fold consecutive snegate + // -(-x) = x + InstructionFoldingCase( + Header() + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int:%\\w+]]\n" + + "; CHECK: %4 = OpCopyObject [[int]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSNegate %int %2\n" + + "%4 = OpSNegate %int %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 10: fold consecutive vector negate + // -(-x) = x + InstructionFoldingCase( + Header() + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v2float:%\\w+]]\n" + + "; CHECK: %4 = OpCopyObject [[v2float]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %var\n" + + "%3 = OpFNegate %v2float %2\n" + + "%4 = OpFNegate %v2float %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 11: fold snegate(iadd with const). + // -(2 + x) = -2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: OpConstant [[int]] -2147483648\n" + + "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpISub [[int]] [[int_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIAdd %int %int_2 %2\n" + + "%4 = OpSNegate %int %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 12: fold snegate(iadd with const). + // -(x + 2) = -2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: OpConstant [[int]] -2147483648\n" + + "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpISub [[int]] [[int_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIAdd %int %2 %int_2\n" + + "%4 = OpSNegate %int %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 13: fold snegate(isub with const). + // -(2 - x) = x - 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpISub [[int]] [[ld]] [[int_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpISub %int %int_2 %2\n" + + "%4 = OpSNegate %int %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 14: fold snegate(isub with const). + // -(x - 2) = 2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpISub [[int]] [[int_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpISub %int %2 %int_2\n" + + "%4 = OpSNegate %int %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 15: fold snegate(iadd with const). + // -(x + 2) = -2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[long_n2:%\\w+]] = OpConstant [[long]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[long_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIAdd %long %2 %long_2\n" + + "%4 = OpSNegate %long %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 16: fold snegate(isub with const). + // -(2 - x) = x - 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[ld]] [[long_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpISub %long %long_2 %2\n" + + "%4 = OpSNegate %long %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 17: fold snegate(isub with const). + // -(x - 2) = 2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[long_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpISub %long %2 %long_2\n" + + "%4 = OpSNegate %long %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 18: fold -vec4(-1.0, 2.0, 1.0, 3.0) + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[v4float:%\\w+]] = OpTypeVector [[float]] 4{{[[:space:]]}}\n" + + "; CHECK: [[float_n1:%\\w+]] = OpConstant [[float]] -1{{[[:space:]]}}\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1{{[[:space:]]}}\n" + + "; CHECK: [[float_n2:%\\w+]] = OpConstant [[float]] -2{{[[:space:]]}}\n" + + "; CHECK: [[float_n3:%\\w+]] = OpConstant [[float]] -3{{[[:space:]]}}\n" + + "; CHECK: [[v4float_1_n2_n1_n3:%\\w+]] = OpConstantComposite [[v4float]] [[float_1]] [[float_n2]] [[float_n1]] [[float_n3]]\n" + + "; CHECK: %2 = OpCopyObject [[v4float]] [[v4float_1_n2_n1_n3]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFNegate %v4float %v4float_n1_2_1_3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 19: fold vector fnegate with null + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v2double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: [[double_n0:%\\w+]] = OpConstant [[double]] -0\n" + + "; CHECK: [[v2double_0_0:%\\w+]] = OpConstantComposite [[v2double]] [[double_n0]] [[double_n0]]\n" + + "; CHECK: %2 = OpCopyObject [[v2double]] [[v2double_0_0]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpFNegate %v2double %v2double_null\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true) +)); + +INSTANTIATE_TEST_SUITE_P(ReciprocalFDivTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: scalar reicprocal + // x / 0.5 = x * 2.0 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %3 = OpFMul [[float]] [[ld]] [[float_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %2 %float_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 3, true), + // Test case 1: Unfoldable + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_0:%\\w+]] = OpConstant [[float]] 0\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %3 = OpFDiv [[float]] [[ld]] [[float_0]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %2 %104\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 3, false), + // Test case 2: Vector reciprocal + // x / {2.0, 0.5} = x * {0.5, 2.0} + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[v2float:%\\w+]] = OpTypeVector [[float]] 2\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[float_0p5:%\\w+]] = OpConstant [[float]] 0.5\n" + + "; CHECK: [[v2float_0p5_2:%\\w+]] = OpConstantComposite [[v2float]] [[float_0p5]] [[float_2]]\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v2float]]\n" + + "; CHECK: %3 = OpFMul [[v2float]] [[ld]] [[v2float_0p5_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %var\n" + + "%3 = OpFDiv %v2float %2 %v2float_2_0p5\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 3, true), + // Test case 3: double reciprocal + // x / 2.0 = x * 0.5 + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[double_0p5:%\\w+]] = OpConstant [[double]] 0.5\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[double]]\n" + + "; CHECK: %3 = OpFMul [[double]] [[ld]] [[double_0p5]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_double Function\n" + + "%2 = OpLoad %double %var\n" + + "%3 = OpFDiv %double %2 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 3, true), + // Test case 4: don't fold x / 0. + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %var\n" + + "%3 = OpFDiv %v2float %2 %v2float_null\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 3, false) +)); + +INSTANTIATE_TEST_SUITE_P(MergeMulTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: fold consecutive fmuls + // (x * 3.0) * 2.0 = x * 6.0 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_6:%\\w+]] = OpConstant [[float]] 6\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[ld]] [[float_6]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFMul %float %2 %float_3\n" + + "%4 = OpFMul %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 1: fold consecutive fmuls + // 2.0 * (x * 3.0) = x * 6.0 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_6:%\\w+]] = OpConstant [[float]] 6\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[ld]] [[float_6]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFMul %float %2 %float_3\n" + + "%4 = OpFMul %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 2: fold consecutive fmuls + // (3.0 * x) * 2.0 = x * 6.0 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_6:%\\w+]] = OpConstant [[float]] 6\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[ld]] [[float_6]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFMul %float %float_3 %2\n" + + "%4 = OpFMul %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 3: fold vector fmul + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[v2float:%\\w+]] = OpTypeVector [[float]] 2\n" + + "; CHECK: [[float_6:%\\w+]] = OpConstant [[float]] 6\n" + + "; CHECK: [[v2float_6_6:%\\w+]] = OpConstantComposite [[v2float]] [[float_6]] [[float_6]]\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v2float]]\n" + + "; CHECK: %4 = OpFMul [[v2float]] [[ld]] [[v2float_6_6]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %var\n" + + "%3 = OpFMul %v2float %2 %v2float_2_3\n" + + "%4 = OpFMul %v2float %3 %v2float_3_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 4: fold double fmuls + // (x * 3.0) * 2.0 = x * 6.0 + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[double_6:%\\w+]] = OpConstant [[double]] 6\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[double]]\n" + + "; CHECK: %4 = OpFMul [[double]] [[ld]] [[double_6]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_double Function\n" + + "%2 = OpLoad %double %var\n" + + "%3 = OpFMul %double %2 %double_3\n" + + "%4 = OpFMul %double %3 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 5: fold 32 bit imuls + // (x * 3) * 2 = x * 6 + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_6:%\\w+]] = OpConstant [[int]] 6\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIMul [[int]] [[ld]] [[int_6]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_3\n" + + "%4 = OpIMul %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 6: fold 64 bit imuls + // (x * 3) * 2 = x * 6 + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64\n" + + "; CHECK: [[long_6:%\\w+]] = OpConstant [[long]] 6\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIMul [[long]] [[ld]] [[long_6]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpIMul %long %2 %long_3\n" + + "%4 = OpIMul %long %3 %long_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 7: merge vector integer mults + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2\n" + + "; CHECK: [[int_6:%\\w+]] = OpConstant [[int]] 6\n" + + "; CHECK: [[v2int_6_6:%\\w+]] = OpConstantComposite [[v2int]] [[int_6]] [[int_6]]\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v2int]]\n" + + "; CHECK: %4 = OpIMul [[v2int]] [[ld]] [[v2int_6_6]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2int Function\n" + + "%2 = OpLoad %v2int %var\n" + + "%3 = OpIMul %v2int %2 %v2int_2_3\n" + + "%4 = OpIMul %v2int %3 %v2int_3_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 8: merge fmul of fdiv + // 2.0 * (2.0 / x) = 4.0 / x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_4]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %float_2 %2\n" + + "%4 = OpFMul %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 9: merge fmul of fdiv + // (2.0 / x) * 2.0 = 4.0 / x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_4]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %float_2 %2\n" + + "%4 = OpFMul %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 10: Do not merge imul of sdiv + // 4 * (x / 2) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSDiv %int %2 %int_2\n" + + "%4 = OpIMul %int %int_4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 11: Do not merge imul of sdiv + // (x / 2) * 4 + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSDiv %int %2 %int_2\n" + + "%4 = OpIMul %int %3 %int_4\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 12: Do not merge imul of udiv + // 4 * (x / 2) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpUDiv %uint %2 %uint_2\n" + + "%4 = OpIMul %uint %uint_4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 13: Do not merge imul of udiv + // (x / 2) * 4 + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpUDiv %uint %2 %uint_2\n" + + "%4 = OpIMul %uint %3 %uint_4\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 14: Don't fold + // (x / 3) * 4 + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_uint Function\n" + + "%2 = OpLoad %uint %var\n" + + "%3 = OpUDiv %uint %2 %uint_3\n" + + "%4 = OpIMul %uint %3 %uint_4\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 15: merge vector fmul of fdiv + // (x / {2,2}) * {4,4} = x * {2,2} + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[v2float:%\\w+]] = OpTypeVector [[float]] 2\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[v2float_2_2:%\\w+]] = OpConstantComposite [[v2float]] [[float_2]] [[float_2]]\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v2float]]\n" + + "; CHECK: %4 = OpFMul [[v2float]] [[ld]] [[v2float_2_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %v2float %var\n" + + "%3 = OpFDiv %v2float %2 %v2float_2_2\n" + + "%4 = OpFMul %v2float %3 %v2float_4_4\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 16: merge vector imul of snegate + // (-x) * {2,2} = x * {-2,-2} + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2{{[[:space:]]}}\n" + + "; CHECK: OpConstant [[int]] -2147483648{{[[:space:]]}}\n" + + "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + + "; CHECK: [[v2int_n2_n2:%\\w+]] = OpConstantComposite [[v2int]] [[int_n2]] [[int_n2]]\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v2int]]\n" + + "; CHECK: %4 = OpIMul [[v2int]] [[ld]] [[v2int_n2_n2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2int Function\n" + + "%2 = OpLoad %v2int %var\n" + + "%3 = OpSNegate %v2int %2\n" + + "%4 = OpIMul %v2int %3 %v2int_2_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 17: merge vector imul of snegate + // {2,2} * (-x) = x * {-2,-2} + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2{{[[:space:]]}}\n" + + "; CHECK: OpConstant [[int]] -2147483648{{[[:space:]]}}\n" + + "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + + "; CHECK: [[v2int_n2_n2:%\\w+]] = OpConstantComposite [[v2int]] [[int_n2]] [[int_n2]]\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v2int]]\n" + + "; CHECK: %4 = OpIMul [[v2int]] [[ld]] [[v2int_n2_n2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2int Function\n" + + "%2 = OpLoad %v2int %var\n" + + "%3 = OpSNegate %v2int %2\n" + + "%4 = OpIMul %v2int %v2int_2_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 18: Fold OpVectorTimesScalar + // {4,4} = OpVectorTimesScalar v2float {2,2} 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[v2float:%\\w+]] = OpTypeVector [[float]] 2\n" + + "; CHECK: [[float_4:%\\w+]] = OpConstant [[float]] 4\n" + + "; CHECK: [[v2float_4_4:%\\w+]] = OpConstantComposite [[v2float]] [[float_4]] [[float_4]]\n" + + "; CHECK: %2 = OpCopyObject [[v2float]] [[v2float_4_4]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesScalar %v2float %v2float_2_2 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 19: Fold OpVectorTimesScalar + // {0,0} = OpVectorTimesScalar v2float v2float_null -1 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[v2float:%\\w+]] = OpTypeVector [[float]] 2\n" + + "; CHECK: [[v2float_null:%\\w+]] = OpConstantNull [[v2float]]\n" + + "; CHECK: %2 = OpCopyObject [[v2float]] [[v2float_null]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesScalar %v2float %v2float_null %float_n1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 20: Fold OpVectorTimesScalar + // {4,4} = OpVectorTimesScalar v2double {2,2} 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v2double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: [[double_4:%\\w+]] = OpConstant [[double]] 4\n" + + "; CHECK: [[v2double_4_4:%\\w+]] = OpConstantComposite [[v2double]] [[double_4]] [[double_4]]\n" + + "; CHECK: %2 = OpCopyObject [[v2double]] [[v2double_4_4]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVectorTimesScalar %v2double %v2double_2_2 %double_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 21: Fold OpVectorTimesScalar + // {0,0} = OpVectorTimesScalar v2double {0,0} n + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v2double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: {{%\\w+}} = OpConstant [[double]] 0\n" + + "; CHECK: [[double_0:%\\w+]] = OpConstant [[double]] 0\n" + + "; CHECK: [[v2double_0_0:%\\w+]] = OpConstantComposite [[v2double]] [[double_0]] [[double_0]]\n" + + "; CHECK: %2 = OpCopyObject [[v2double]] [[v2double_0_0]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_double Function\n" + + "%load = OpLoad %double %n\n" + + "%2 = OpVectorTimesScalar %v2double %v2double_0_0 %load\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 22: Fold OpVectorTimesScalar + // {0,0} = OpVectorTimesScalar v2double n 0 + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v2double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: [[v2double_null:%\\w+]] = OpConstantNull [[v2double]]\n" + + "; CHECK: %2 = OpCopyObject [[v2double]] [[v2double_null]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2double Function\n" + + "%load = OpLoad %v2double %n\n" + + "%2 = OpVectorTimesScalar %v2double %load %double_0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 23: merge fmul of fdiv + // x * (y / x) = y + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[ldx:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[ldy:%\\w+]] = OpLoad [[float]] [[y:%\\w+]]\n" + + "; CHECK: %5 = OpCopyObject [[float]] [[ldy]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %x\n" + + "%3 = OpLoad %float %y\n" + + "%4 = OpFDiv %float %3 %2\n" + + "%5 = OpFMul %float %2 %4\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 5, true), + // Test case 24: merge fmul of fdiv + // (y / x) * x = y + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[ldx:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[ldy:%\\w+]] = OpLoad [[float]] [[y:%\\w+]]\n" + + "; CHECK: %5 = OpCopyObject [[float]] [[ldy]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %x\n" + + "%3 = OpLoad %float %y\n" + + "%4 = OpFDiv %float %3 %2\n" + + "%5 = OpFMul %float %4 %2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 5, true) +)); + +INSTANTIATE_TEST_SUITE_P(MergeDivTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: merge consecutive fdiv + // 4.0 / (2.0 / x) = 2.0 * x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFMul [[float]] [[float_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %float_2 %2\n" + + "%4 = OpFDiv %float %float_4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 1: merge consecutive fdiv + // 4.0 / (x / 2.0) = 8.0 / x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_8:%\\w+]] = OpConstant [[float]] 8\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_8]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %2 %float_2\n" + + "%4 = OpFDiv %float %float_4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 2: merge consecutive fdiv + // (4.0 / x) / 2.0 = 2.0 / x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFDiv [[float]] [[float_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %float_4 %2\n" + + "%4 = OpFDiv %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 3: Do not merge consecutive sdiv + // 4 / (2 / x) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSDiv %int %int_2 %2\n" + + "%4 = OpSDiv %int %int_4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 4: Do not merge consecutive sdiv + // 4 / (x / 2) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSDiv %int %2 %int_2\n" + + "%4 = OpSDiv %int %int_4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 5: Do not merge consecutive sdiv + // (4 / x) / 2 + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSDiv %int %int_4 %2\n" + + "%4 = OpSDiv %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 6: Do not merge consecutive sdiv + // (x / 4) / 2 + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSDiv %int %2 %int_4\n" + + "%4 = OpSDiv %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 7: Do not merge sdiv of imul + // 4 / (2 * x) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %int_2 %2\n" + + "%4 = OpSDiv %int %int_4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 8: Do not merge sdiv of imul + // 4 / (x * 2) + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_2\n" + + "%4 = OpSDiv %int %int_4 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 9: Do not merge sdiv of imul + // (4 * x) / 2 + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %int_4 %2\n" + + "%4 = OpSDiv %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 10: Do not merge sdiv of imul + // (x * 4) / 2 + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpIMul %int %2 %int_4\n" + + "%4 = OpSDiv %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 11: merge sdiv of snegate + // (-x) / 2 = x / -2 + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: OpConstant [[int]] -2147483648\n" + + "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpSDiv [[int]] [[ld]] [[int_n2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSNegate %int %2\n" + + "%4 = OpSDiv %int %3 %int_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 12: merge sdiv of snegate + // 2 / (-x) = -2 / x + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: OpConstant [[int]] -2147483648\n" + + "; CHECK: [[int_n2:%\\w+]] = OpConstant [[int]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpSDiv [[int]] [[int_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpSNegate %int %2\n" + + "%4 = OpSDiv %int %int_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 13: Don't merge + // (x / {null}) / {null} + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_v2float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFDiv %float %2 %v2float_null\n" + + "%4 = OpFDiv %float %3 %v2float_null\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, false), + // Test case 14: merge fmul of fdiv + // (y * x) / x = y + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[ldx:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[ldy:%\\w+]] = OpLoad [[float]] [[y:%\\w+]]\n" + + "; CHECK: %5 = OpCopyObject [[float]] [[ldy]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %x\n" + + "%3 = OpLoad %float %y\n" + + "%4 = OpFMul %float %3 %2\n" + + "%5 = OpFDiv %float %4 %2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 5, true), + // Test case 15: merge fmul of fdiv + // (x * y) / x = y + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[ldx:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: [[ldy:%\\w+]] = OpLoad [[float]] [[y:%\\w+]]\n" + + "; CHECK: %5 = OpCopyObject [[float]] [[ldy]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%x = OpVariable %_ptr_float Function\n" + + "%y = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %x\n" + + "%3 = OpLoad %float %y\n" + + "%4 = OpFMul %float %2 %3\n" + + "%5 = OpFDiv %float %4 %2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 5, true) +)); + +INSTANTIATE_TEST_SUITE_P(MergeAddTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: merge add of negate + // (-x) + 2 = 2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFNegate %float %2\n" + + "%4 = OpFAdd %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 1: merge add of negate + // 2 + (-x) = 2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpSNegate %float %2\n" + + "%4 = OpIAdd %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 2: merge add of negate + // (-x) + 2 = 2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[long_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpSNegate %long %2\n" + + "%4 = OpIAdd %long %3 %long_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 3: merge add of negate + // 2 + (-x) = 2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[long_2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpSNegate %long %2\n" + + "%4 = OpIAdd %long %long_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 4: merge add of subtract + // (x - 1) + 2 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %2 %float_1\n" + + "%4 = OpFAdd %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 5: merge add of subtract + // (1 - x) + 2 = 3 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_3]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %float_1 %2\n" + + "%4 = OpFAdd %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 6: merge add of subtract + // 2 + (x - 1) = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %2 %float_1\n" + + "%4 = OpFAdd %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 7: merge add of subtract + // 2 + (1 - x) = 3 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_3]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %float_1 %2\n" + + "%4 = OpFAdd %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 8: merge add of add + // (x + 1) + 2 = x + 3 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_3]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %2 %float_1\n" + + "%4 = OpFAdd %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 9: merge add of add + // (1 + x) + 2 = 3 + x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_3]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %float_1 %2\n" + + "%4 = OpFAdd %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 10: merge add of add + // 2 + (x + 1) = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_3]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %2 %float_1\n" + + "%4 = OpFAdd %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 11: merge add of add + // 2 + (1 + x) = 3 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_3]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %float_1 %2\n" + + "%4 = OpFAdd %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true) +)); + +INSTANTIATE_TEST_SUITE_P(MergeGenericAddSub, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: merge of add of sub + // (a - b) + b => a + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: %6 = OpCopyObject [[float]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %var0\n" + + "%4 = OpLoad %float %var1\n" + + "%5 = OpFSub %float %3 %4\n" + + "%6 = OpFAdd %float %5 %4\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 6, true), + // Test case 1: merge of add of sub + // b + (a - b) => a + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: %6 = OpCopyObject [[float]] %3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%3 = OpLoad %float %var0\n" + + "%4 = OpLoad %float %var1\n" + + "%5 = OpFSub %float %3 %4\n" + + "%6 = OpFAdd %float %4 %5\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 6, true) +)); + +INSTANTIATE_TEST_SUITE_P(FactorAddMul, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: factor of add of muls + // (a * b) + (a * c) => a * (b + c) + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" + + "; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%var2 = OpVariable %_ptr_float Function\n" + + "%4 = OpLoad %float %var0\n" + + "%5 = OpLoad %float %var1\n" + + "%6 = OpLoad %float %var2\n" + + "%7 = OpFMul %float %6 %4\n" + + "%8 = OpFMul %float %6 %5\n" + + "%9 = OpFAdd %float %7 %8\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true), + // Test case 1: factor of add of muls + // (b * a) + (a * c) => a * (b + c) + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" + + "; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%var2 = OpVariable %_ptr_float Function\n" + + "%4 = OpLoad %float %var0\n" + + "%5 = OpLoad %float %var1\n" + + "%6 = OpLoad %float %var2\n" + + "%7 = OpFMul %float %4 %6\n" + + "%8 = OpFMul %float %6 %5\n" + + "%9 = OpFAdd %float %7 %8\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true), + // Test case 2: factor of add of muls + // (a * b) + (c * a) => a * (b + c) + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" + + "; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%var2 = OpVariable %_ptr_float Function\n" + + "%4 = OpLoad %float %var0\n" + + "%5 = OpLoad %float %var1\n" + + "%6 = OpLoad %float %var2\n" + + "%7 = OpFMul %float %6 %4\n" + + "%8 = OpFMul %float %5 %6\n" + + "%9 = OpFAdd %float %7 %8\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true), + // Test case 3: factor of add of muls + // (b * a) + (c * a) => a * (b + c) + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[newadd:%\\w+]] = OpFAdd [[float]] %4 %5\n" + + "; CHECK: %9 = OpFMul [[float]] %6 [[newadd]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var0 = OpVariable %_ptr_float Function\n" + + "%var1 = OpVariable %_ptr_float Function\n" + + "%var2 = OpVariable %_ptr_float Function\n" + + "%4 = OpLoad %float %var0\n" + + "%5 = OpLoad %float %var1\n" + + "%6 = OpLoad %float %var2\n" + + "%7 = OpFMul %float %4 %6\n" + + "%8 = OpFMul %float %5 %6\n" + + "%9 = OpFAdd %float %7 %8\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true) +)); + +INSTANTIATE_TEST_SUITE_P(MergeSubTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: merge sub of negate + // (-x) - 2 = -2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n2:%\\w+]] = OpConstant [[float]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFNegate %float %2\n" + + "%4 = OpFSub %float %3 %float_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 1: merge sub of negate + // 2 - (-x) = x + 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_2:%\\w+]] = OpConstant [[float]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFNegate %float %2\n" + + "%4 = OpFSub %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 2: merge sub of negate + // (-x) - 2 = -2 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[long_n2:%\\w+]] = OpConstant [[long]] -2{{[[:space:]]}}\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpISub [[long]] [[long_n2]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpSNegate %long %2\n" + + "%4 = OpISub %long %3 %long_2\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 3: merge sub of negate + // 2 - (-x) = x + 2 + InstructionFoldingCase( + Header() + + "; CHECK: [[long:%\\w+]] = OpTypeInt 64 1\n" + + "; CHECK: [[long_2:%\\w+]] = OpConstant [[long]] 2\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[long]]\n" + + "; CHECK: %4 = OpIAdd [[long]] [[ld]] [[long_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_long Function\n" + + "%2 = OpLoad %long %var\n" + + "%3 = OpSNegate %long %2\n" + + "%4 = OpISub %long %long_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 4: merge add of subtract + // (x + 2) - 1 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %2 %float_2\n" + + "%4 = OpFSub %float %3 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 5: merge add of subtract + // (2 + x) - 1 = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %float_2 %2\n" + + "%4 = OpFSub %float %3 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 6: merge add of subtract + // 2 - (x + 1) = 1 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_1]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %2 %float_1\n" + + "%4 = OpFSub %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 7: merge add of subtract + // 2 - (1 + x) = 1 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_1]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFAdd %float %float_1 %2\n" + + "%4 = OpFSub %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 8: merge subtract of subtract + // (x - 2) - 1 = x - 3 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[ld]] [[float_3]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %2 %float_2\n" + + "%4 = OpFSub %float %3 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 9: merge subtract of subtract + // (2 - x) - 1 = 1 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_1]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %float_2 %2\n" + + "%4 = OpFSub %float %3 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 10: merge subtract of subtract + // 2 - (x - 1) = 3 - x + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_3:%\\w+]] = OpConstant [[float]] 3\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFSub [[float]] [[float_3]] [[ld]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %2 %float_1\n" + + "%4 = OpFSub %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 11: merge subtract of subtract + // 1 - (2 - x) = x + (-1) + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_n1:%\\w+]] = OpConstant [[float]] -1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_n1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %float_2 %2\n" + + "%4 = OpFSub %float %float_1 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 12: merge subtract of subtract + // 2 - (1 - x) = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: [[float_1:%\\w+]] = OpConstant [[float]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[float]]\n" + + "; CHECK: %4 = OpFAdd [[float]] [[ld]] [[float_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_float Function\n" + + "%2 = OpLoad %float %var\n" + + "%3 = OpFSub %float %float_1 %2\n" + + "%4 = OpFSub %float %float_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true), + // Test case 13: merge subtract of subtract with mixed types. + // 2 - (1 - x) = x + 1 + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int_1:%\\w+]] = OpConstant [[int]] 1\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[int]]\n" + + "; CHECK: %4 = OpIAdd [[int]] [[ld]] [[int_1]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%var = OpVariable %_ptr_int Function\n" + + "%2 = OpLoad %int %var\n" + + "%3 = OpISub %int %uint_1 %2\n" + + "%4 = OpISub %int %int_2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 4, true) +)); + +INSTANTIATE_TEST_SUITE_P(SelectFoldingTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: Fold select with the same values for both sides + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int0:%\\w+]] = OpConstant [[int]] 0\n" + + "; CHECK: %2 = OpCopyObject [[int]] [[int0]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_bool Function\n" + + "%load = OpLoad %bool %n\n" + + "%2 = OpSelect %int %load %100 %100\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 1: Fold select true to left side + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int0:%\\w+]] = OpConstant [[int]] 0\n" + + "; CHECK: %2 = OpCopyObject [[int]] [[int0]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %bool %n\n" + + "%2 = OpSelect %int %true %100 %n\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 2: Fold select false to right side + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int0:%\\w+]] = OpConstant [[int]] 0\n" + + "; CHECK: %2 = OpCopyObject [[int]] [[int0]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %bool %n\n" + + "%2 = OpSelect %int %false %n %100\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 3: Fold select null to right side + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[int0:%\\w+]] = OpConstant [[int]] 0\n" + + "; CHECK: %2 = OpCopyObject [[int]] [[int0]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_int Function\n" + + "%load = OpLoad %int %n\n" + + "%2 = OpSelect %int %bool_null %load %100\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 4: vector null + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2\n" + + "; CHECK: [[int2:%\\w+]] = OpConstant [[int]] 2\n" + + "; CHECK: [[v2int2_2:%\\w+]] = OpConstantComposite [[v2int]] [[int2]] [[int2]]\n" + + "; CHECK: %2 = OpCopyObject [[v2int]] [[v2int2_2]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%load = OpLoad %v2int %n\n" + + "%2 = OpSelect %v2int %v2bool_null %load %v2int_2_2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 2, true), + // Test case 5: vector select + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2\n" + + "; CHECK: %4 = OpVectorShuffle [[v2int]] %2 %3 0 3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%m = OpVariable %_ptr_v2int Function\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%2 = OpLoad %v2int %n\n" + + "%3 = OpLoad %v2int %n\n" + + "%4 = OpSelect %v2int %v2bool_true_false %2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 6: vector select + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: [[v2int:%\\w+]] = OpTypeVector [[int]] 2\n" + + "; CHECK: %4 = OpVectorShuffle [[v2int]] %2 %3 2 1\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%m = OpVariable %_ptr_v2int Function\n" + + "%n = OpVariable %_ptr_v2int Function\n" + + "%2 = OpLoad %v2int %n\n" + + "%3 = OpLoad %v2int %n\n" + + "%4 = OpSelect %v2int %v2bool_false_true %2 %3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true) +)); + +INSTANTIATE_TEST_SUITE_P(CompositeExtractMatchingTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: Extracting from result of consecutive shuffles of differing + // size. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: %5 = OpCompositeExtract [[int]] %2 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4int Function\n" + + "%2 = OpLoad %v4int %n\n" + + "%3 = OpVectorShuffle %v2int %2 %2 2 3\n" + + "%4 = OpVectorShuffle %v4int %2 %3 0 4 2 5\n" + + "%5 = OpCompositeExtract %int %4 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true), + // Test case 1: Extracting from result of vector shuffle of differing + // input and result sizes. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: %4 = OpCompositeExtract [[int]] %2 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4int Function\n" + + "%2 = OpLoad %v4int %n\n" + + "%3 = OpVectorShuffle %v2int %2 %2 2 3\n" + + "%4 = OpCompositeExtract %int %3 0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 2: Extracting from result of vector shuffle of differing + // input and result sizes. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: %4 = OpCompositeExtract [[int]] %2 3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4int Function\n" + + "%2 = OpLoad %v4int %n\n" + + "%3 = OpVectorShuffle %v2int %2 %2 2 3\n" + + "%4 = OpCompositeExtract %int %3 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true), + // Test case 3: Using fmix feeding extract with a 1 in the a position. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 4\n" + + "; CHECK: [[ptr_v4double:%\\w+]] = OpTypePointer Function [[v4double]]\n" + + "; CHECK: [[m:%\\w+]] = OpVariable [[ptr_v4double]] Function\n" + + "; CHECK: [[n:%\\w+]] = OpVariable [[ptr_v4double]] Function\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v4double]] [[n]]\n" + + "; CHECK: %5 = OpCompositeExtract [[double]] [[ld]] 1\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%m = OpVariable %_ptr_v4double Function\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%2 = OpLoad %v4double %m\n" + + "%3 = OpLoad %v4double %n\n" + + "%4 = OpExtInst %v4double %1 FMix %2 %3 %v4double_0_1_0_0\n" + + "%5 = OpCompositeExtract %double %4 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true), + // Test case 4: Using fmix feeding extract with a 0 in the a position. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 4\n" + + "; CHECK: [[ptr_v4double:%\\w+]] = OpTypePointer Function [[v4double]]\n" + + "; CHECK: [[m:%\\w+]] = OpVariable [[ptr_v4double]] Function\n" + + "; CHECK: [[n:%\\w+]] = OpVariable [[ptr_v4double]] Function\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v4double]] [[m]]\n" + + "; CHECK: %5 = OpCompositeExtract [[double]] [[ld]] 2\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%m = OpVariable %_ptr_v4double Function\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%2 = OpLoad %v4double %m\n" + + "%3 = OpLoad %v4double %n\n" + + "%4 = OpExtInst %v4double %1 FMix %2 %3 %v4double_0_1_0_0\n" + + "%5 = OpCompositeExtract %double %4 2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true), + // Test case 5: Using fmix feeding extract with a null for the alpha + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 4\n" + + "; CHECK: [[ptr_v4double:%\\w+]] = OpTypePointer Function [[v4double]]\n" + + "; CHECK: [[m:%\\w+]] = OpVariable [[ptr_v4double]] Function\n" + + "; CHECK: [[n:%\\w+]] = OpVariable [[ptr_v4double]] Function\n" + + "; CHECK: [[ld:%\\w+]] = OpLoad [[v4double]] [[m]]\n" + + "; CHECK: %5 = OpCompositeExtract [[double]] [[ld]] 0\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%m = OpVariable %_ptr_v4double Function\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%2 = OpLoad %v4double %m\n" + + "%3 = OpLoad %v4double %n\n" + + "%4 = OpExtInst %v4double %1 FMix %2 %3 %v4double_null\n" + + "%5 = OpCompositeExtract %double %4 0\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, true), + // Test case 6: Don't fold: Using fmix feeding extract with 0.5 in the a + // position. + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%m = OpVariable %_ptr_v4double Function\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%2 = OpLoad %v4double %m\n" + + "%3 = OpLoad %v4double %n\n" + + "%4 = OpExtInst %v4double %1 FMix %2 %3 %v4double_1_1_1_0p5\n" + + "%5 = OpCompositeExtract %double %4 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 5, false), + // Test case 7: Extracting the undefined literal value from a vector + // shuffle. + InstructionFoldingCase( + Header() + + "; CHECK: [[int:%\\w+]] = OpTypeInt 32 1\n" + + "; CHECK: %4 = OpUndef [[int]]\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4int Function\n" + + "%2 = OpLoad %v4int %n\n" + + "%3 = OpVectorShuffle %v2int %2 %2 2 4294967295\n" + + "%4 = OpCompositeExtract %int %3 1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 4, true) +)); + +INSTANTIATE_TEST_SUITE_P(DotProductMatchingTest, MatchingInstructionFoldingTest, +::testing::Values( + // Test case 0: Using OpDot to extract last element. + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: %3 = OpCompositeExtract [[float]] %2 3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4float Function\n" + + "%2 = OpLoad %v4float %n\n" + + "%3 = OpDot %float %2 %v4float_0_0_0_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 1: Using OpDot to extract last element. + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: %3 = OpCompositeExtract [[float]] %2 3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4float Function\n" + + "%2 = OpLoad %v4float %n\n" + + "%3 = OpDot %float %v4float_0_0_0_1 %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 2: Using OpDot to extract second element. + InstructionFoldingCase( + Header() + + "; CHECK: [[float:%\\w+]] = OpTypeFloat 32\n" + + "; CHECK: %3 = OpCompositeExtract [[float]] %2 1\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4float Function\n" + + "%2 = OpLoad %v4float %n\n" + + "%3 = OpDot %float %v4float_0_1_0_0 %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 3: Using OpDot to extract last element. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: %3 = OpCompositeExtract [[double]] %2 3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%2 = OpLoad %v4double %n\n" + + "%3 = OpDot %double %2 %v4double_0_0_0_1\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 4: Using OpDot to extract last element. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: %3 = OpCompositeExtract [[double]] %2 3\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%2 = OpLoad %v4double %n\n" + + "%3 = OpDot %double %v4double_0_0_0_1 %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true), + // Test case 5: Using OpDot to extract second element. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: %3 = OpCompositeExtract [[double]] %2 1\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%2 = OpLoad %v4double %n\n" + + "%3 = OpDot %double %v4double_0_1_0_0 %2\n" + + "OpReturn\n" + + "OpFunctionEnd", + 3, true) +)); + +using MatchingInstructionWithNoResultFoldingTest = +::testing::TestWithParam>; + +// Test folding instructions that do not have a result. The instruction +// that will be folded is the last instruction before the return. If there +// are multiple returns, there is not guarentee which one is used. +TEST_P(MatchingInstructionWithNoResultFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + Instruction* inst = nullptr; + Function* func = &*context->module()->begin(); + for (auto& bb : *func) { + Instruction* terminator = bb.terminator(); + if (terminator->IsReturnOrAbort()) { + inst = terminator->PreviousNode(); + break; + } + } + assert(inst && "Invalid test. Could not find instruction to fold."); + std::unique_ptr original_inst(inst->Clone(context.get())); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + EXPECT_EQ(succeeded, tc.expected_result); + if (succeeded) { + Match(tc.test_body, context.get()); + } +} + +INSTANTIATE_TEST_SUITE_P(StoreMatchingTest, MatchingInstructionWithNoResultFoldingTest, +::testing::Values( + // Test case 0: Remove store of undef. + InstructionFoldingCase( + Header() + + "; CHECK: OpLabel\n" + + "; CHECK-NOT: OpStore\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%undef = OpUndef %v4double\n" + + "OpStore %n %undef\n" + + "OpReturn\n" + + "OpFunctionEnd", + 0 /* OpStore */, true), + // Test case 1: Keep volatile store. + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%n = OpVariable %_ptr_v4double Function\n" + + "%undef = OpUndef %v4double\n" + + "OpStore %n %undef Volatile\n" + + "OpReturn\n" + + "OpFunctionEnd", + 0 /* OpStore */, false) +)); + +INSTANTIATE_TEST_SUITE_P(VectorShuffleMatchingTest, MatchingInstructionWithNoResultFoldingTest, +::testing::Values( + // Test case 0: Basic test 1 + InstructionFoldingCase( + Header() + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %7 %5 2 3 6 7\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %6 2 3 4 5\n" + + "%9 = OpVectorShuffle %v4double %7 %8 2 3 4 5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 1: Basic test 2 + InstructionFoldingCase( + Header() + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %6 %7 0 1 4 5\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %6 2 3 4 5\n" + + "%9 = OpVectorShuffle %v4double %8 %7 2 3 4 5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 2: Basic test 3 + InstructionFoldingCase( + Header() + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %5 %7 3 2 4 5\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %6 2 3 4 5\n" + + "%9 = OpVectorShuffle %v4double %8 %7 1 0 4 5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 3: Basic test 4 + InstructionFoldingCase( + Header() + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %7 %6 2 3 5 4\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %6 2 3 4 5\n" + + "%9 = OpVectorShuffle %v4double %7 %8 2 3 7 6\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 4: Don't fold, need both operands of the feeder. + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %6 2 3 4 5\n" + + "%9 = OpVectorShuffle %v4double %7 %8 2 3 7 5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, false), + // Test case 5: Don't fold, need both operands of the feeder. + InstructionFoldingCase( + Header() + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %6 2 3 4 5\n" + + "%9 = OpVectorShuffle %v4double %8 %7 2 0 7 5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, false), + // Test case 6: Fold, need both operands of the feeder, but they are the same. + InstructionFoldingCase( + Header() + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %5 %7 0 2 7 5\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %5 2 3 4 5\n" + + "%9 = OpVectorShuffle %v4double %8 %7 2 0 7 5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 7: Fold, need both operands of the feeder, but they are the same. + InstructionFoldingCase( + Header() + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %7 %5 2 0 5 7\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %5 2 3 4 5\n" + + "%9 = OpVectorShuffle %v4double %7 %8 2 0 7 5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 8: Replace first operand with a smaller vector. + InstructionFoldingCase( + Header() + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %5 %7 0 0 5 3\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v2double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v2double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v4double %5 %5 0 1 2 3\n" + + "%9 = OpVectorShuffle %v4double %8 %7 2 0 7 5\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 9: Replace first operand with a larger vector. + InstructionFoldingCase( + Header() + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %5 %7 3 0 7 5\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v2double %5 %5 0 3\n" + + "%9 = OpVectorShuffle %v4double %8 %7 1 0 5 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 10: Replace unused operand with null. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: [[null:%\\w+]] = OpConstantNull [[v4double]]\n" + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} [[null]] %7 4 2 5 3\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v2double %5 %5 0 3\n" + + "%9 = OpVectorShuffle %v4double %8 %7 4 2 5 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 11: Replace unused operand with null. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: [[null:%\\w+]] = OpConstantNull [[v4double]]\n" + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} [[null]] %5 2 2 5 5\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%8 = OpVectorShuffle %v2double %5 %5 0 3\n" + + "%9 = OpVectorShuffle %v4double %8 %8 2 2 3 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 12: Replace unused operand with null. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: [[null:%\\w+]] = OpConstantNull [[v4double]]\n" + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %7 [[null]] 2 0 1 3\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v2double %5 %5 0 3\n" + + "%9 = OpVectorShuffle %v4double %7 %8 2 0 1 3\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true), + // Test case 13: Shuffle with undef literal. + InstructionFoldingCase( + Header() + + "; CHECK: [[double:%\\w+]] = OpTypeFloat 64\n" + + "; CHECK: [[v4double:%\\w+]] = OpTypeVector [[double]] 2\n" + + "; CHECK: OpVectorShuffle\n" + + "; CHECK: OpVectorShuffle {{%\\w+}} %7 {{%\\w+}} 2 0 1 4294967295\n" + + "; CHECK: OpReturn\n" + + "%main = OpFunction %void None %void_func\n" + + "%main_lab = OpLabel\n" + + "%2 = OpVariable %_ptr_v4double Function\n" + + "%3 = OpVariable %_ptr_v4double Function\n" + + "%4 = OpVariable %_ptr_v4double Function\n" + + "%5 = OpLoad %v4double %2\n" + + "%6 = OpLoad %v4double %3\n" + + "%7 = OpLoad %v4double %4\n" + + "%8 = OpVectorShuffle %v2double %5 %5 0 1\n" + + "%9 = OpVectorShuffle %v4double %7 %8 2 0 1 4294967295\n" + + "OpReturn\n" + + "OpFunctionEnd", + 9, true) +)); + +using EntryPointFoldingTest = +::testing::TestWithParam>; + +TEST_P(EntryPointFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + Instruction* inst = nullptr; + inst = &*context->module()->entry_points().begin(); + assert(inst && "Invalid test. Could not find entry point instruction to fold."); + std::unique_ptr original_inst(inst->Clone(context.get())); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + EXPECT_EQ(succeeded, tc.expected_result); + if (succeeded) { + Match(tc.test_body, context.get()); + } +} + +INSTANTIATE_TEST_SUITE_P(OpEntryPointFoldingTest, EntryPointFoldingTest, +::testing::Values( + // Test case 0: Basic test 1 + InstructionFoldingCase(std::string() + + "; CHECK: OpEntryPoint Fragment %2 \"main\" %3\n" + + "OpCapability Shader\n" + + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + + "OpMemoryModel Logical GLSL450\n" + + "OpEntryPoint Fragment %2 \"main\" %3 %3 %3\n" + + "OpExecutionMode %2 OriginUpperLeft\n" + + "OpSource GLSL 430\n" + + "OpDecorate %3 Location 0\n" + + "%void = OpTypeVoid\n" + + "%5 = OpTypeFunction %void\n" + + "%float = OpTypeFloat 32\n" + + "%v4float = OpTypeVector %float 4\n" + + "%_ptr_Output_v4float = OpTypePointer Output %v4float\n" + + "%3 = OpVariable %_ptr_Output_v4float Output\n" + + "%int = OpTypeInt 32 1\n" + + "%int_0 = OpConstant %int 0\n" + +"%_ptr_PushConstant_v4float = OpTypePointer PushConstant %v4float\n" + + "%2 = OpFunction %void None %5\n" + + "%12 = OpLabel\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true), + InstructionFoldingCase(std::string() + + "; CHECK: OpEntryPoint Fragment %2 \"main\" %3 %4\n" + + "OpCapability Shader\n" + + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + + "OpMemoryModel Logical GLSL450\n" + + "OpEntryPoint Fragment %2 \"main\" %3 %4 %3\n" + + "OpExecutionMode %2 OriginUpperLeft\n" + + "OpSource GLSL 430\n" + + "OpDecorate %3 Location 0\n" + + "%void = OpTypeVoid\n" + + "%5 = OpTypeFunction %void\n" + + "%float = OpTypeFloat 32\n" + + "%v4float = OpTypeVector %float 4\n" + + "%_ptr_Output_v4float = OpTypePointer Output %v4float\n" + + "%3 = OpVariable %_ptr_Output_v4float Output\n" + + "%4 = OpVariable %_ptr_Output_v4float Output\n" + + "%int = OpTypeInt 32 1\n" + + "%int_0 = OpConstant %int 0\n" + +"%_ptr_PushConstant_v4float = OpTypePointer PushConstant %v4float\n" + + "%2 = OpFunction %void None %5\n" + + "%12 = OpLabel\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true), + InstructionFoldingCase(std::string() + + "; CHECK: OpEntryPoint Fragment %2 \"main\" %4 %3\n" + + "OpCapability Shader\n" + + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + + "OpMemoryModel Logical GLSL450\n" + + "OpEntryPoint Fragment %2 \"main\" %4 %4 %3\n" + + "OpExecutionMode %2 OriginUpperLeft\n" + + "OpSource GLSL 430\n" + + "OpDecorate %3 Location 0\n" + + "%void = OpTypeVoid\n" + + "%5 = OpTypeFunction %void\n" + + "%float = OpTypeFloat 32\n" + + "%v4float = OpTypeVector %float 4\n" + + "%_ptr_Output_v4float = OpTypePointer Output %v4float\n" + + "%3 = OpVariable %_ptr_Output_v4float Output\n" + + "%4 = OpVariable %_ptr_Output_v4float Output\n" + + "%int = OpTypeInt 32 1\n" + + "%int_0 = OpConstant %int 0\n" + +"%_ptr_PushConstant_v4float = OpTypePointer PushConstant %v4float\n" + + "%2 = OpFunction %void None %5\n" + + "%12 = OpLabel\n" + + "OpReturn\n" + + "OpFunctionEnd\n", + 9, true) +)); + +using SPV14FoldingTest = +::testing::TestWithParam>; + +TEST_P(SPV14FoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_4, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + std::unique_ptr original_inst(inst->Clone(context.get())); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + EXPECT_EQ(succeeded, tc.expected_result); + if (succeeded) { + Match(tc.test_body, context.get()); + } +} + +INSTANTIATE_TEST_SUITE_P(SPV14FoldingTest, SPV14FoldingTest, +::testing::Values( + // Test case 0: select vectors with scalar condition. + InstructionFoldingCase(std::string() + +"; CHECK-NOT: OpSelect\n" + +"; CHECK: %3 = OpCopyObject {{%\\w+}} %1\n" + +"OpCapability Shader\n" + +"OpCapability Linkage\n" + +"%void = OpTypeVoid\n" + +"%bool = OpTypeBool\n" + +"%true = OpConstantTrue %bool\n" + +"%int = OpTypeInt 32 0\n" + +"%int4 = OpTypeVector %int 4\n" + +"%int_0 = OpConstant %int 0\n" + +"%int_1 = OpConstant %int 1\n" + +"%1 = OpUndef %int4\n" + +"%2 = OpUndef %int4\n" + +"%void_fn = OpTypeFunction %void\n" + +"%func = OpFunction %void None %void_fn\n" + +"%entry = OpLabel\n" + +"%3 = OpSelect %int4 %true %1 %2\n" + +"OpReturn\n" + +"OpFunctionEnd\n" +, + 3, true), + // Test case 1: select struct with scalar condition. + InstructionFoldingCase(std::string() + +"; CHECK-NOT: OpSelect\n" + +"; CHECK: %3 = OpCopyObject {{%\\w+}} %2\n" + +"OpCapability Shader\n" + +"OpCapability Linkage\n" + +"%void = OpTypeVoid\n" + +"%bool = OpTypeBool\n" + +"%true = OpConstantFalse %bool\n" + +"%int = OpTypeInt 32 0\n" + +"%struct = OpTypeStruct %int %int %int %int\n" + +"%int_0 = OpConstant %int 0\n" + +"%int_1 = OpConstant %int 1\n" + +"%1 = OpUndef %struct\n" + +"%2 = OpUndef %struct\n" + +"%void_fn = OpTypeFunction %void\n" + +"%func = OpFunction %void None %void_fn\n" + +"%entry = OpLabel\n" + +"%3 = OpSelect %struct %true %1 %2\n" + +"OpReturn\n" + +"OpFunctionEnd\n" +, + 3, true), + // Test case 1: select array with scalar condition. + InstructionFoldingCase(std::string() + +"; CHECK-NOT: OpSelect\n" + +"; CHECK: %3 = OpCopyObject {{%\\w+}} %2\n" + +"OpCapability Shader\n" + +"OpCapability Linkage\n" + +"%void = OpTypeVoid\n" + +"%bool = OpTypeBool\n" + +"%true = OpConstantFalse %bool\n" + +"%int = OpTypeInt 32 0\n" + +"%int_0 = OpConstant %int 0\n" + +"%int_1 = OpConstant %int 1\n" + +"%int_4 = OpConstant %int 4\n" + +"%array = OpTypeStruct %int %int %int %int\n" + +"%1 = OpUndef %array\n" + +"%2 = OpUndef %array\n" + +"%void_fn = OpTypeFunction %void\n" + +"%func = OpFunction %void None %void_fn\n" + +"%entry = OpLabel\n" + +"%3 = OpSelect %array %true %1 %2\n" + +"OpReturn\n" + +"OpFunctionEnd\n" +, + 3, true) +)); + +std::string FloatControlsHeader(const std::string& capabilities) { + std::string header = R"( +OpCapability Shader +)" + capabilities + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +)"; + + return header; +} + +using FloatControlsFoldingTest = +::testing::TestWithParam>; + +TEST_P(FloatControlsFoldingTest, Case) { + const auto& tc = GetParam(); + + // Build module. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_4, nullptr, tc.test_body, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + // Fold the instruction to test. + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* inst = def_use_mgr->GetDef(tc.id_to_fold); + std::unique_ptr original_inst(inst->Clone(context.get())); + bool succeeded = context->get_instruction_folder().FoldInstruction(inst); + EXPECT_EQ(succeeded, tc.expected_result); + if (succeeded) { + Match(tc.test_body, context.get()); + } +} + +INSTANTIATE_TEST_SUITE_P(FloatControlsFoldingTest, FloatControlsFoldingTest, +::testing::Values( + // Test case 0: no folding with DenormPreserve + InstructionFoldingCase(FloatControlsHeader("OpCapability DenormPreserve") + + "%1 = OpFAdd %float %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n" +, + 1, false), + // Test case 1: no folding with DenormFlushToZero + InstructionFoldingCase(FloatControlsHeader("OpCapability DenormFlushToZero") + + "%1 = OpFAdd %float %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n" +, + 1, false), + // Test case 2: no folding with SignedZeroInfNanPreserve + InstructionFoldingCase(FloatControlsHeader("OpCapability SignedZeroInfNanPreserve") + + "%1 = OpFAdd %float %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n" +, + 1, false), + // Test case 3: no folding with RoundingModeRTE + InstructionFoldingCase(FloatControlsHeader("OpCapability RoundingModeRTE") + + "%1 = OpFAdd %float %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n" +, + 1, false), + // Test case 4: no folding with RoundingModeRTZ + InstructionFoldingCase(FloatControlsHeader("OpCapability RoundingModeRTZ") + + "%1 = OpFAdd %float %float_0 %float_1\n" + + "OpReturn\n" + + "OpFunctionEnd\n" +, + 1, false) +)); + +std::string ImageOperandsTestBody(const std::string& image_instruction) { + std::string body = R"( + OpCapability Shader + OpCapability ImageGatherExtended + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpDecorate %Texture DescriptorSet 0 + OpDecorate %Texture Binding 0 + %int = OpTypeInt 32 1 + %int_n1 = OpConstant %int -1 + %5 = OpConstant %int 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%type_sampled_image = OpTypeSampledImage %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image + %_ptr_int = OpTypePointer Function %int + %v2int = OpTypeVector %int 2 + %10 = OpTypeVector %float 4 + %void = OpTypeVoid + %22 = OpTypeFunction %void + %v2float = OpTypeVector %float 2 + %v3int = OpTypeVector %int 3 + %Texture = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %gSampler = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant + %101 = OpConstantComposite %v2int %int_n1 %int_n1 + %20 = OpConstantComposite %v2float %float_0 %float_0 + %main = OpFunction %void None %22 + %23 = OpLabel + %var = OpVariable %_ptr_int Function + %88 = OpLoad %type_2d_image %Texture + %val = OpLoad %int %var + %sampler = OpLoad %type_sampler %gSampler + %26 = OpSampledImage %type_sampled_image %88 %sampler +)" + image_instruction + R"( + OpReturn + OpFunctionEnd +)"; + + return body; +} + +INSTANTIATE_TEST_SUITE_P(ImageOperandsBitmaskFoldingTest, MatchingInstructionWithNoResultFoldingTest, +::testing::Values( + // Test case 0: OpImageFetch without Offset + InstructionFoldingCase(ImageOperandsTestBody( + "%89 = OpImageFetch %10 %88 %101 Lod %5 \n") + , 89, false), + // Test case 1: OpImageFetch with non-const offset + InstructionFoldingCase(ImageOperandsTestBody( + "%89 = OpImageFetch %10 %88 %101 Lod|Offset %5 %val \n") + , 89, false), + // Test case 2: OpImageFetch with Lod and Offset + InstructionFoldingCase(ImageOperandsTestBody( + " %89 = OpImageFetch %10 %88 %101 Lod|Offset %5 %101 \n" + "; CHECK: %89 = OpImageFetch %10 %88 %101 Lod|ConstOffset %5 %101 \n") + , 89, true), + // Test case 3: OpImageFetch with Bias and Offset + InstructionFoldingCase(ImageOperandsTestBody( + " %89 = OpImageFetch %10 %88 %101 Bias|Offset %5 %101 \n" + "; CHECK: %89 = OpImageFetch %10 %88 %101 Bias|ConstOffset %5 %101 \n") + , 89, true), + // Test case 4: OpImageFetch with Grad and Offset. + // Grad adds 2 operands to the instruction. + InstructionFoldingCase(ImageOperandsTestBody( + " %89 = OpImageFetch %10 %88 %101 Grad|Offset %5 %5 %101 \n" + "; CHECK: %89 = OpImageFetch %10 %88 %101 Grad|ConstOffset %5 %5 %101 \n") + , 89, true), + // Test case 5: OpImageFetch with Offset and MinLod. + // This is an example of a case where the bitmask bit-offset is larger than + // that of the Offset. + InstructionFoldingCase(ImageOperandsTestBody( + " %89 = OpImageFetch %10 %88 %101 Offset|MinLod %101 %5 \n" + "; CHECK: %89 = OpImageFetch %10 %88 %101 ConstOffset|MinLod %101 %5 \n") + , 89, true), + // Test case 6: OpImageGather with constant Offset + InstructionFoldingCase(ImageOperandsTestBody( + " %89 = OpImageGather %10 %26 %20 %5 Offset %101 \n" + "; CHECK: %89 = OpImageGather %10 %26 %20 %5 ConstOffset %101 \n") + , 89, true), + // Test case 7: OpImageWrite with constant Offset + InstructionFoldingCase(ImageOperandsTestBody( + " OpImageWrite %88 %5 %101 Offset %101 \n" + "; CHECK: OpImageWrite %88 %5 %101 ConstOffset %101 \n") + , 0 /* No result-id */, true) +)); + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/freeze_spec_const_test.cpp b/third_party/spirv-tools/test/opt/freeze_spec_const_test.cpp new file mode 100644 index 0000000..e5999ce --- /dev/null +++ b/third_party/spirv-tools/test/opt/freeze_spec_const_test.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +struct FreezeSpecConstantValueTypeTestCase { + const char* type_decl; + const char* spec_const; + const char* expected_frozen_const; +}; + +using FreezeSpecConstantValueTypeTest = + PassTest<::testing::TestWithParam>; + +TEST_P(FreezeSpecConstantValueTypeTest, PrimaryType) { + auto& test_case = GetParam(); + std::vector text = {"OpCapability Shader", + "OpMemoryModel Logical GLSL450", + test_case.type_decl, test_case.spec_const}; + std::vector expected = { + "OpCapability Shader", "OpMemoryModel Logical GLSL450", + test_case.type_decl, test_case.expected_frozen_const}; + SinglePassRunAndCheck( + JoinAllInsts(text), JoinAllInsts(expected), /* skip_nop = */ false); +} + +// Test each primary type. +INSTANTIATE_TEST_SUITE_P( + PrimaryTypeSpecConst, FreezeSpecConstantValueTypeTest, + ::testing::ValuesIn(std::vector({ + // Type declaration, original spec constant definition, expected frozen + // spec constants. + {"%int = OpTypeInt 32 1", "%2 = OpSpecConstant %int 1", + "%int_1 = OpConstant %int 1"}, + {"%uint = OpTypeInt 32 0", "%2 = OpSpecConstant %uint 1", + "%uint_1 = OpConstant %uint 1"}, + {"%float = OpTypeFloat 32", "%2 = OpSpecConstant %float 3.1415", + "%float_3_1415 = OpConstant %float 3.1415"}, + {"%double = OpTypeFloat 64", "%2 = OpSpecConstant %double 3.141592653", + "%double_3_141592653 = OpConstant %double 3.141592653"}, + {"%bool = OpTypeBool", "%2 = OpSpecConstantTrue %bool", + "%true = OpConstantTrue %bool"}, + {"%bool = OpTypeBool", "%2 = OpSpecConstantFalse %bool", + "%false = OpConstantFalse %bool"}, + }))); + +using FreezeSpecConstantValueRemoveDecorationTest = PassTest<::testing::Test>; + +TEST_F(FreezeSpecConstantValueRemoveDecorationTest, + RemoveDecorationInstWithSpecId) { + std::vector text = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpSource GLSL 450", + "OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"", + "OpSourceExtension \"GL_GOOGLE_include_directive\"", + "OpName %main \"main\"", + "OpDecorate %3 SpecId 200", + "OpDecorate %4 SpecId 201", + "OpDecorate %5 SpecId 202", + "OpDecorate %6 SpecId 203", + "%void = OpTypeVoid", + "%8 = OpTypeFunction %void", + "%int = OpTypeInt 32 1", + "%3 = OpSpecConstant %int 3", + "%float = OpTypeFloat 32", + "%4 = OpSpecConstant %float 3.1415", + "%double = OpTypeFloat 64", + "%5 = OpSpecConstant %double 3.14159265358979", + "%bool = OpTypeBool", + "%6 = OpSpecConstantTrue %bool", + "%13 = OpSpecConstantFalse %bool", + "%main = OpFunction %void None %8", + "%14 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + std::string expected_disassembly = SelectiveJoin(text, [](const char* line) { + return std::string(line).find("SpecId") != std::string::npos; + }); + std::vector> replacement_pairs = { + {"%3 = OpSpecConstant %int 3", "%int_3 = OpConstant %int 3"}, + {"%4 = OpSpecConstant %float 3.1415", + "%float_3_1415 = OpConstant %float 3.1415"}, + {"%5 = OpSpecConstant %double 3.14159265358979", + "%double_3_14159265358979 = OpConstant %double 3.14159265358979"}, + {"%6 = OpSpecConstantTrue ", "%true = OpConstantTrue "}, + {"%13 = OpSpecConstantFalse ", "%false = OpConstantFalse "}, + }; + for (auto& p : replacement_pairs) { + EXPECT_TRUE(FindAndReplace(&expected_disassembly, p.first, p.second)) + << "text:\n" + << expected_disassembly << "\n" + << "find_str:\n" + << p.first << "\n" + << "replace_str:\n" + << p.second << "\n"; + } + SinglePassRunAndCheck(JoinAllInsts(text), + expected_disassembly, + /* skip_nop = */ true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/function_test.cpp b/third_party/spirv-tools/test/opt/function_test.cpp new file mode 100644 index 0000000..af25bac --- /dev/null +++ b/third_party/spirv-tools/test/opt/function_test.cpp @@ -0,0 +1,301 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "function_utils.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::Eq; + +TEST(FunctionTest, HasEarlyReturn) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %6 "main" + +; Types + %2 = OpTypeBool + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstantTrue %2 + +; main function without early return + %6 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + +; function with early return + %11 = OpFunction %3 None %4 + %12 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %5 %13 %14 + %13 = OpLabel + OpReturn + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, shader, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + // Tests |function| without early return. + auto* function = spvtest::GetFunction(context->module(), 6); + ASSERT_FALSE(function->HasEarlyReturn()); + + // Tests |function| with early return. + function = spvtest::GetFunction(context->module(), 11); + ASSERT_TRUE(function->HasEarlyReturn()); +} + +TEST(FunctionTest, IsNotRecursive) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +OpDecorate %2 DescriptorSet 439418829 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%7 = OpTypeFunction %_struct_6 +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %12 +OpUnreachable +OpFunctionEnd +%12 = OpFunction %_struct_6 None %7 +%13 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + std::unique_ptr ctx = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto* func = spvtest::GetFunction(ctx->module(), 9); + EXPECT_FALSE(func->IsRecursive()); + + func = spvtest::GetFunction(ctx->module(), 12); + EXPECT_FALSE(func->IsRecursive()); +} + +TEST(FunctionTest, IsDirectlyRecursive) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +OpDecorate %2 DescriptorSet 439418829 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%7 = OpTypeFunction %_struct_6 +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %9 +OpUnreachable +OpFunctionEnd +)"; + + std::unique_ptr ctx = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto* func = spvtest::GetFunction(ctx->module(), 9); + EXPECT_TRUE(func->IsRecursive()); +} + +TEST(FunctionTest, IsIndirectlyRecursive) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +OpDecorate %2 DescriptorSet 439418829 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%7 = OpTypeFunction %_struct_6 +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %12 +OpUnreachable +OpFunctionEnd +%12 = OpFunction %_struct_6 None %7 +%13 = OpLabel +%14 = OpFunctionCall %_struct_6 %9 +OpUnreachable +OpFunctionEnd +)"; + + std::unique_ptr ctx = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto* func = spvtest::GetFunction(ctx->module(), 9); + EXPECT_TRUE(func->IsRecursive()); + + func = spvtest::GetFunction(ctx->module(), 12); + EXPECT_TRUE(func->IsRecursive()); +} + +TEST(FunctionTest, IsNotRecuriseCallingRecursive) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +OpDecorate %2 DescriptorSet 439418829 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%7 = OpTypeFunction %_struct_6 +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %9 +OpUnreachable +OpFunctionEnd +)"; + + std::unique_ptr ctx = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto* func = spvtest::GetFunction(ctx->module(), 1); + EXPECT_FALSE(func->IsRecursive()); +} + +TEST(FunctionTest, NonSemanticInfoSkipIteration) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%6 = OpExtInst %2 %1 1 +OpReturn +OpFunctionEnd +%7 = OpExtInst %2 %1 2 +%8 = OpExtInst %2 %1 3 +)"; + + std::unique_ptr ctx = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto* func = spvtest::GetFunction(ctx->module(), 4); + ASSERT_TRUE(func != nullptr); + std::unordered_set non_semantic_ids; + func->ForEachInst( + [&non_semantic_ids](const Instruction* inst) { + if (inst->opcode() == SpvOpExtInst) { + non_semantic_ids.insert(inst->result_id()); + } + }, + true, false); + + EXPECT_EQ(1, non_semantic_ids.count(6)); + EXPECT_EQ(0, non_semantic_ids.count(7)); + EXPECT_EQ(0, non_semantic_ids.count(8)); +} + +TEST(FunctionTest, NonSemanticInfoIncludeIteration) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%6 = OpExtInst %2 %1 1 +OpReturn +OpFunctionEnd +%7 = OpExtInst %2 %1 2 +%8 = OpExtInst %2 %1 3 +)"; + + std::unique_ptr ctx = + spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto* func = spvtest::GetFunction(ctx->module(), 4); + ASSERT_TRUE(func != nullptr); + std::unordered_set non_semantic_ids; + func->ForEachInst( + [&non_semantic_ids](const Instruction* inst) { + if (inst->opcode() == SpvOpExtInst) { + non_semantic_ids.insert(inst->result_id()); + } + }, + true, true); + + EXPECT_EQ(1, non_semantic_ids.count(6)); + EXPECT_EQ(1, non_semantic_ids.count(7)); + EXPECT_EQ(1, non_semantic_ids.count(8)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/function_utils.h b/third_party/spirv-tools/test/opt/function_utils.h new file mode 100644 index 0000000..803cacd --- /dev/null +++ b/third_party/spirv-tools/test/opt/function_utils.h @@ -0,0 +1,55 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_OPT_FUNCTION_UTILS_H_ +#define TEST_OPT_FUNCTION_UTILS_H_ + +#include "source/opt/function.h" +#include "source/opt/module.h" + +namespace spvtest { + +inline spvtools::opt::Function* GetFunction(spvtools::opt::Module* module, + uint32_t id) { + for (spvtools::opt::Function& f : *module) { + if (f.result_id() == id) { + return &f; + } + } + return nullptr; +} + +inline const spvtools::opt::Function* GetFunction( + const spvtools::opt::Module* module, uint32_t id) { + for (const spvtools::opt::Function& f : *module) { + if (f.result_id() == id) { + return &f; + } + } + return nullptr; +} + +inline const spvtools::opt::BasicBlock* GetBasicBlock( + const spvtools::opt::Function* fn, uint32_t id) { + for (const spvtools::opt::BasicBlock& bb : *fn) { + if (bb.id() == id) { + return &bb; + } + } + return nullptr; +} + +} // namespace spvtest + +#endif // TEST_OPT_FUNCTION_UTILS_H_ diff --git a/third_party/spirv-tools/test/opt/generate_webgpu_initializers_test.cpp b/third_party/spirv-tools/test/opt/generate_webgpu_initializers_test.cpp new file mode 100644 index 0000000..4aab2ce --- /dev/null +++ b/third_party/spirv-tools/test/opt/generate_webgpu_initializers_test.cpp @@ -0,0 +1,347 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +typedef std::tuple GenerateWebGPUInitializersParam; + +using GlobalVariableTest = + PassTest<::testing::TestWithParam>; +using LocalVariableTest = + PassTest<::testing::TestWithParam>; + +using GenerateWebGPUInitializersTest = PassTest<::testing::Test>; + +void operator+=(std::vector& lhs, const char* rhs) { + lhs.push_back(rhs); +} + +void operator+=(std::vector& lhs, + const std::vector& rhs) { + lhs.reserve(lhs.size() + rhs.size()); + for (auto* c : rhs) lhs.push_back(c); +} + +std::string GetGlobalVariableTestString(std::string ptr_str, + std::string var_str, + std::string const_str = "") { + std::vector result = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + ptr_str.c_str()}; + // clang-format on + + if (!const_str.empty()) result += const_str.c_str(); + + result += { + // clang-format off + var_str.c_str(), + "%uint_0 = OpConstant %uint 0", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%1 = OpFunction %void None %7", + "%8 = OpLabel", + "OpStore %4 %uint_0", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + return JoinAllInsts(result); +} + +std::string GetPointerString(std::string storage_type) { + std::string result = "%_ptr_"; + result += storage_type + "_uint = OpTypePointer "; + result += storage_type + " %uint"; + return result; +} + +std::string GetGlobalVariableString(std::string storage_type, + bool initialized) { + std::string result = "%4 = OpVariable %_ptr_"; + result += storage_type + "_uint "; + result += storage_type; + if (initialized) result += " %9"; + return result; +} + +std::string GetUninitializedGlobalVariableTestString(std::string storage_type) { + return GetGlobalVariableTestString( + GetPointerString(storage_type), + GetGlobalVariableString(storage_type, false)); +} + +std::string GetNullConstantString() { return "%9 = OpConstantNull %uint"; } + +std::string GetInitializedGlobalVariableTestString(std::string storage_type) { + return GetGlobalVariableTestString( + GetPointerString(storage_type), + GetGlobalVariableString(storage_type, true), GetNullConstantString()); +} + +TEST_P(GlobalVariableTest, Check) { + std::string storage_class = std::get<0>(GetParam()); + bool changed = std::get<1>(GetParam()); + std::string input = GetUninitializedGlobalVariableTestString(storage_class); + std::string expected = + changed ? GetInitializedGlobalVariableTestString(storage_class) : input; + + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + GenerateWebGPUInitializers, GlobalVariableTest, + ::testing::ValuesIn(std::vector({ + std::make_tuple("Private", true), + std::make_tuple("Output", true), + std::make_tuple("Function", true), + std::make_tuple("UniformConstant", false), + std::make_tuple("Input", false), + std::make_tuple("Uniform", false), + std::make_tuple("Workgroup", false) + }))); +// clang-format on + +std::string GetLocalVariableTestString(std::string ptr_str, std::string var_str, + std::string const_str = "") { + std::vector result = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + ptr_str.c_str(), + "%uint_0 = OpConstant %uint 0", + "%void = OpTypeVoid", + "%6 = OpTypeFunction %void"}; + // clang-format on + + if (!const_str.empty()) result += const_str.c_str(); + + result += { + // clang-format off + "%1 = OpFunction %void None %6", + "%7 = OpLabel", + var_str.c_str(), + "OpStore %8 %uint_0" + // clang-format on + }; + return JoinAllInsts(result); +} + +std::string GetLocalVariableString(std::string storage_type, bool initialized) { + std::string result = "%8 = OpVariable %_ptr_"; + result += storage_type + "_uint "; + result += storage_type; + if (initialized) result += " %9"; + return result; +} + +std::string GetUninitializedLocalVariableTestString(std::string storage_type) { + return GetLocalVariableTestString( + GetPointerString(storage_type), + GetLocalVariableString(storage_type, false)); +} + +std::string GetInitializedLocalVariableTestString(std::string storage_type) { + return GetLocalVariableTestString(GetPointerString(storage_type), + GetLocalVariableString(storage_type, true), + GetNullConstantString()); +} + +TEST_P(LocalVariableTest, Check) { + std::string storage_class = std::get<0>(GetParam()); + bool changed = std::get<1>(GetParam()); + + std::string input = GetUninitializedLocalVariableTestString(storage_class); + std::string expected = + changed ? GetInitializedLocalVariableTestString(storage_class) : input; + + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + GenerateWebGPUInitializers, LocalVariableTest, + ::testing::ValuesIn(std::vector({ + std::make_tuple("Private", true), + std::make_tuple("Output", true), + std::make_tuple("Function", true), + std::make_tuple("UniformConstant", false), + std::make_tuple("Input", false), + std::make_tuple("Uniform", false), + std::make_tuple("Workgroup", false) + }))); +// clang-format on + +TEST_F(GenerateWebGPUInitializersTest, AlreadyInitializedUnchanged) { + std::vector spirv = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + "%_ptr_Private_uint = OpTypePointer Private %uint", + "%uint_0 = OpConstant %uint 0", + "%5 = OpVariable %_ptr_Private_uint Private %uint_0", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%1 = OpFunction %void None %7", + "%8 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + std::string str = JoinAllInsts(spirv); + + SinglePassRunAndCheck(str, str, + /* skip_nop = */ false); +} + +TEST_F(GenerateWebGPUInitializersTest, AmbigiousArrays) { + std::vector input_spirv = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + "%uint_2 = OpConstant %uint 2", + "%_arr_uint_uint_2 = OpTypeArray %uint %uint_2", + "%_arr_uint_uint_2_0 = OpTypeArray %uint %uint_2", + "%_ptr_Private__arr_uint_uint_2 = OpTypePointer Private %_arr_uint_uint_2", +"%_ptr_Private__arr_uint_uint_2_0 = OpTypePointer Private %_arr_uint_uint_2_0", + "%8 = OpConstantNull %_arr_uint_uint_2_0", + "%9 = OpVariable %_ptr_Private__arr_uint_uint_2 Private", + "%10 = OpVariable %_ptr_Private__arr_uint_uint_2_0 Private %8", + "%void = OpTypeVoid", + "%12 = OpTypeFunction %void", + "%1 = OpFunction %void None %12", + "%13 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + std::string input_str = JoinAllInsts(input_spirv); + + std::vector expected_spirv = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + "%uint_2 = OpConstant %uint 2", + "%_arr_uint_uint_2 = OpTypeArray %uint %uint_2", + "%_arr_uint_uint_2_0 = OpTypeArray %uint %uint_2", + "%_ptr_Private__arr_uint_uint_2 = OpTypePointer Private %_arr_uint_uint_2", +"%_ptr_Private__arr_uint_uint_2_0 = OpTypePointer Private %_arr_uint_uint_2_0", + "%8 = OpConstantNull %_arr_uint_uint_2_0", + "%14 = OpConstantNull %_arr_uint_uint_2", + "%9 = OpVariable %_ptr_Private__arr_uint_uint_2 Private %14", + "%10 = OpVariable %_ptr_Private__arr_uint_uint_2_0 Private %8", + "%void = OpTypeVoid", + "%12 = OpTypeFunction %void", + "%1 = OpFunction %void None %12", + "%13 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + std::string expected_str = JoinAllInsts(expected_spirv); + + SinglePassRunAndCheck(input_str, expected_str, + /* skip_nop = */ false); +} + +TEST_F(GenerateWebGPUInitializersTest, AmbigiousStructs) { + std::vector input_spirv = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + "%_struct_3 = OpTypeStruct %uint", + "%_struct_4 = OpTypeStruct %uint", +"%_ptr_Private__struct_3 = OpTypePointer Private %_struct_3", +"%_ptr_Private__struct_4 = OpTypePointer Private %_struct_4", + "%7 = OpConstantNull %_struct_3", + "%8 = OpVariable %_ptr_Private__struct_3 Private %7", + "%9 = OpVariable %_ptr_Private__struct_4 Private", + "%void = OpTypeVoid", + "%11 = OpTypeFunction %void", + "%1 = OpFunction %void None %11", + "%12 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + std::string input_str = JoinAllInsts(input_spirv); + + std::vector expected_spirv = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + "%_struct_3 = OpTypeStruct %uint", + "%_struct_4 = OpTypeStruct %uint", +"%_ptr_Private__struct_3 = OpTypePointer Private %_struct_3", +"%_ptr_Private__struct_4 = OpTypePointer Private %_struct_4", + "%7 = OpConstantNull %_struct_3", + "%8 = OpVariable %_ptr_Private__struct_3 Private %7", + "%13 = OpConstantNull %_struct_4", + "%9 = OpVariable %_ptr_Private__struct_4 Private %13", + "%void = OpTypeVoid", + "%11 = OpTypeFunction %void", + "%1 = OpFunction %void None %11", + "%12 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + std::string expected_str = JoinAllInsts(expected_spirv); + + SinglePassRunAndCheck(input_str, expected_str, + /* skip_nop = */ false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/graphics_robust_access_test.cpp b/third_party/spirv-tools/test/opt/graphics_robust_access_test.cpp new file mode 100644 index 0000000..4b2cd44 --- /dev/null +++ b/third_party/spirv-tools/test/opt/graphics_robust_access_test.cpp @@ -0,0 +1,1562 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "pass_fixture.h" +#include "pass_utils.h" +#include "source/opt/graphics_robust_access_pass.h" + +namespace { + +using namespace spvtools; + +using opt::GraphicsRobustAccessPass; +using GraphicsRobustAccessTest = opt::PassTest<::testing::Test>; + +// Test incompatible module, determined at module-level. + +TEST_F(GraphicsRobustAccessTest, FailNotShader) { + const std::string text = R"( +; CHECK: Can only process Shader modules +OpCapability Kernel +)"; + + SinglePassRunAndFail(text); +} + +TEST_F(GraphicsRobustAccessTest, FailCantProcessVariablePointers) { + const std::string text = R"( +; CHECK: Can't process modules with VariablePointers capability +OpCapability VariablePointers +)"; + + SinglePassRunAndFail(text); +} + +TEST_F(GraphicsRobustAccessTest, FailCantProcessVariablePointersStorageBuffer) { + const std::string text = R"( +; CHECK: Can't process modules with VariablePointersStorageBuffer capability +OpCapability VariablePointersStorageBuffer +)"; + + SinglePassRunAndFail(text); +} + +TEST_F(GraphicsRobustAccessTest, FailCantProcessRuntimeDescriptorArrayEXT) { + const std::string text = R"( +; CHECK: Can't process modules with RuntimeDescriptorArrayEXT capability +OpCapability RuntimeDescriptorArrayEXT +)"; + + SinglePassRunAndFail(text); +} + +TEST_F(GraphicsRobustAccessTest, FailCantProcessPhysical32AddressingModel) { + const std::string text = R"( +; CHECK: Addressing model must be Logical. Found OpMemoryModel Physical32 OpenCL +OpCapability Shader +OpMemoryModel Physical32 OpenCL +)"; + + SinglePassRunAndFail(text); +} + +TEST_F(GraphicsRobustAccessTest, FailCantProcessPhysical64AddressingModel) { + const std::string text = R"( +; CHECK: Addressing model must be Logical. Found OpMemoryModel Physical64 OpenCL +OpCapability Shader +OpMemoryModel Physical64 OpenCL +)"; + + SinglePassRunAndFail(text); +} + +TEST_F(GraphicsRobustAccessTest, + FailCantProcessPhysicalStorageBuffer64EXTAddressingModel) { + const std::string text = R"( +; CHECK: Addressing model must be Logical. Found OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpCapability Shader +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +)"; + + SinglePassRunAndFail(text); +} + +// Test access chains + +// Returns the names of access chain instructions handled by the pass. +// For the purposes of this pass, regular and in-bounds access chains are the +// same.) +std::vector AccessChains() { + return {"OpAccessChain", "OpInBoundsAccessChain"}; +} + +std::string ShaderPreamble() { + return R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" +)"; +} + +std::string ShaderPreamble(const std::vector& names) { + std::ostringstream os; + os << ShaderPreamble(); + for (auto& name : names) { + os << " OpName %" << name << " \"" << name << "\"\n"; + } + return os.str(); +} + +std::string ShaderPreambleAC() { + return ShaderPreamble({"ac", "ptr_ty", "var"}); +} + +std::string ShaderPreambleAC(const std::vector& names) { + auto names2 = names; + names2.push_back("ac"); + names2.push_back("ptr_ty"); + names2.push_back("var"); + return ShaderPreamble(names2); +} + +std::string DecoSSBO() { + return R"( + OpDecorate %ssbo_s BufferBlock + OpMemberDecorate %ssbo_s 0 Offset 0 + OpMemberDecorate %ssbo_s 1 Offset 4 + OpMemberDecorate %ssbo_s 2 Offset 16 + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 +)"; +} + +std::string TypesVoid() { + return R"( + %void = OpTypeVoid + %void_fn = OpTypeFunction %void +)"; +} + +std::string TypesInt() { + return R"( + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 +)"; +} + +std::string TypesFloat() { + return R"( + %float = OpTypeFloat 32 +)"; +} + +std::string TypesShort() { + return R"( + %ushort = OpTypeInt 16 0 + %short = OpTypeInt 16 1 +)"; +} + +std::string TypesLong() { + return R"( + %ulong = OpTypeInt 64 0 + %long = OpTypeInt 64 1 +)"; +} + +std::string MainPrefix() { + return R"( + %main = OpFunction %void None %void_fn + %entry = OpLabel +)"; +} + +std::string MainSuffix() { + return R"( + OpReturn + OpFunctionEnd +)"; +} + +std::string ACCheck(const std::string& access_chain_inst, + const std::string& original, + const std::string& transformed) { + return "\n ; CHECK: %ac = " + access_chain_inst + " %ptr_ty %var" + + (transformed.empty() ? "" : " ") + transformed + + "\n ; CHECK-NOT: " + access_chain_inst + + "\n ; CHECK-NEXT: OpReturn" + "\n %ac = " + + access_chain_inst + " %ptr_ty %var " + (original.empty() ? "" : " ") + + original + "\n"; +} + +std::string ACCheckFail(const std::string& access_chain_inst, + const std::string& original, + const std::string& transformed) { + return "\n ; CHECK: %ac = " + access_chain_inst + " %ptr_ty %var" + + (transformed.empty() ? "" : " ") + transformed + + "\n ; CHECK-NOT: " + access_chain_inst + + "\n ; CHECK-NOT: OpReturn" + "\n %ac = " + + access_chain_inst + " %ptr_ty %var " + (original.empty() ? "" : " ") + + original + "\n"; +} + +// Access chain into: +// Vector +// Vector sizes 2, 3, 4 +// Matrix +// Matrix columns 2, 4 +// Component is vector 2, 4 +// Array +// Struct +// TODO(dneto): RuntimeArray + +TEST_F(GraphicsRobustAccessTest, ACVectorLeastInboundConstantUntouched) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( + %uvec2 = OpTypeVector %uint 2 + %var_ty = OpTypePointer Function %uvec2 + %ptr_ty = OpTypePointer Function %uint + %uint_0 = OpConstant %uint 0 + )" + << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_0", "%uint_0") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACVectorMostInboundConstantUntouched) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( + %v4uint = OpTypeVector %uint 4 + %var_ty = OpTypePointer Function %v4uint + %ptr_ty = OpTypePointer Function %uint + %uint_3 = OpConstant %uint 3 + )" + << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_3", "%uint_3") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACVectorExcessConstantClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( + %v4uint = OpTypeVector %uint 4 + %var_ty = OpTypePointer Function %v4uint + %ptr_ty = OpTypePointer Function %uint + %uint_4 = OpConstant %uint 4 + )" + << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%uint_4", "%int_3") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACVectorNegativeConstantClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( + %v4uint = OpTypeVector %uint 4 + %var_ty = OpTypePointer Function %v4uint + %ptr_ty = OpTypePointer Function %uint + %int_n1 = OpConstant %int -1 + )" + << MainPrefix() << R"( + ; CHECK: %int_0 = OpConstant %int 0 + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%int_n1", "%int_0") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +// Like the previous test, but ensures the pass knows how to modify an index +// which does not come first in the access chain. +TEST_F(GraphicsRobustAccessTest, ACVectorInArrayNegativeConstantClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() << R"( + %v4uint = OpTypeVector %uint 4 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %arr = OpTypeArray %v4uint %uint_2 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %uint + %int_n1 = OpConstant %int -1 + )" + << MainPrefix() << R"( + ; CHECK: %int_0 = OpConstant %int 0 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%uint_1 %int_n1", "%uint_1 %int_0") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACVectorGeneralClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() << R"( + %v4uint = OpTypeVector %uint 4 + %var_ty = OpTypePointer Function %v4uint + %ptr_ty = OpTypePointer Function %uint + %i = OpUndef %int)" + << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_3 = OpConstant %int 3 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3 + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACVectorGeneralShortClamped) { + // Show that signed 16 bit integers are clamped as well. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort() << + R"( + %v4short = OpTypeVector %short 4 + %var_ty = OpTypePointer Function %v4short + %ptr_ty = OpTypePointer Function %short + %i = OpUndef %short)" + << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-NOT: = OpTypeInt 32 + ; CHECK-DAG: %short_0 = OpConstant %short 0 + ; CHECK-DAG: %short_3 = OpConstant %short 3 + ; CHECK-NOT: = OpTypeInt 32 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %short %[[GLSLSTD450]] SClamp %i %short_0 %short_3 + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACVectorGeneralUShortClamped) { + // Show that unsigned 16 bit integers are clamped as well. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort() << + R"( + %v4ushort = OpTypeVector %ushort 4 + %var_ty = OpTypePointer Function %v4ushort + %ptr_ty = OpTypePointer Function %ushort + %i = OpUndef %ushort)" + << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-NOT: = OpTypeInt 32 + ; CHECK-DAG: %short_0 = OpConstant %short 0 + ; CHECK-DAG: %short_3 = OpConstant %short 3 + ; CHECK-NOT: = OpTypeInt 32 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %short_3 + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACVectorGeneralLongClamped) { + // Show that signed 64 bit integers are clamped as well. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int64\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesLong() << + R"( + %v4long = OpTypeVector %long 4 + %var_ty = OpTypePointer Function %v4long + %ptr_ty = OpTypePointer Function %long + %i = OpUndef %long)" + << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-NOT: = OpTypeInt 32 + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %long_3 = OpConstant %long 3 + ; CHECK-NOT: = OpTypeInt 32 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_3 + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACVectorGeneralULongClamped) { + // Show that unsigned 64 bit integers are clamped as well. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int64\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesLong() << + R"( + %v4ulong = OpTypeVector %ulong 4 + %var_ty = OpTypePointer Function %v4ulong + %ptr_ty = OpTypePointer Function %ulong + %i = OpUndef %ulong)" + << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-NOT: = OpTypeInt 32 + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %long_3 = OpConstant %long 3 + ; CHECK-NOT: = OpTypeInt 32 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_3 + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACMatrixLeastInboundConstantUntouched) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %v2float = OpTypeVector %float 2 + %mat4v2float = OpTypeMatrix %v2float 4 + %var_ty = OpTypePointer Function %mat4v2float + %ptr_ty = OpTypePointer Function %float + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%uint_0 %uint_1", "%uint_0 %uint_1") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACMatrixMostInboundConstantUntouched) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %v2float = OpTypeVector %float 2 + %mat4v2float = OpTypeMatrix %v2float 4 + %var_ty = OpTypePointer Function %mat4v2float + %ptr_ty = OpTypePointer Function %float + %uint_1 = OpConstant %uint 1 + %uint_3 = OpConstant %uint 3 + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%uint_3 %uint_1", "%uint_3 %uint_1") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACMatrixExcessConstantClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %v2float = OpTypeVector %float 2 + %mat4v2float = OpTypeMatrix %v2float 4 + %var_ty = OpTypePointer Function %mat4v2float + %ptr_ty = OpTypePointer Function %float + %uint_1 = OpConstant %uint 1 + %uint_4 = OpConstant %uint 4 + )" << MainPrefix() << R"( + ; CHECK: %int_3 = OpConstant %int 3 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%uint_4 %uint_1", "%int_3 %uint_1") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACMatrixNegativeConstantClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %v2float = OpTypeVector %float 2 + %mat4v2float = OpTypeMatrix %v2float 4 + %var_ty = OpTypePointer Function %mat4v2float + %ptr_ty = OpTypePointer Function %float + %uint_1 = OpConstant %uint 1 + %int_n1 = OpConstant %int -1 + )" << MainPrefix() << R"( + ; CHECK: %int_0 = OpConstant %int 0 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%int_n1 %uint_1", "%int_0 %uint_1") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACMatrixGeneralClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %v2float = OpTypeVector %float 2 + %mat4v2float = OpTypeMatrix %v2float 4 + %var_ty = OpTypePointer Function %mat4v2float + %ptr_ty = OpTypePointer Function %float + %uint_1 = OpConstant %uint 1 + %i = OpUndef %int + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_3 = OpConstant %int 3 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_3 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i %uint_1", "%[[clamp]] %uint_1") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayLeastInboundConstantUntouched) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %uint_200 = OpConstant %uint 200 + %arr = OpTypeArray %float %uint_200 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %int_0 = OpConstant %int 0 + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%int_0", "%int_0") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayMostInboundConstantUntouched) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %uint_200 = OpConstant %uint 200 + %arr = OpTypeArray %float %uint_200 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %int_199 = OpConstant %int 199 + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%int_199", "%int_199") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayGeneralClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %uint_200 = OpConstant %uint 200 + %arr = OpTypeArray %float %uint_200 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %int + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_199 = OpConstant %int 199 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayGeneralShortIndexUIntBoundsClamped) { + // Index is signed short, array bounds overflows the index type. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesShort() << TypesFloat() << R"( + %uint_70000 = OpConstant %uint 70000 ; overflows 16bits + %arr = OpTypeArray %float %uint_70000 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %short + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_69999 = OpConstant %int 69999 + ; CHECK: OpLabel + ; CHECK: %[[i_ext:\w+]] = OpSConvert %uint %i + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUShortIndexIntBoundsClamped) { + // Index is unsigned short, array bounds overflows the index type. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesShort() << TypesFloat() << R"( + %int_70000 = OpConstant %int 70000 ; overflows 16bits + %arr = OpTypeArray %float %int_70000 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %ushort + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_69999 = OpConstant %int 69999 + ; CHECK: OpLabel + ; CHECK: %[[i_ext:\w+]] = OpUConvert %uint %i + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %int_0 %int_69999 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayGeneralUIntIndexShortBoundsClamped) { + // Signed int index i is wider than the array bounds type. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesShort() << TypesFloat() << R"( + %short_200 = OpConstant %short 200 + %arr = OpTypeArray %float %short_200 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %uint + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_199 = OpConstant %int 199 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %int_199 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayGeneralIntIndexUShortBoundsClamped) { + // Unsigned int index i is wider than the array bounds type. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesShort() << TypesFloat() << R"( + %ushort_200 = OpConstant %ushort 200 + %arr = OpTypeArray %float %ushort_200 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %int + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_199 = OpConstant %int 199 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_199 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayGeneralLongIndexUIntBoundsClamped) { + // Signed long index i is wider than the array bounds type. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int64\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesLong() << TypesFloat() << R"( + %uint_200 = OpConstant %uint 200 + %arr = OpTypeArray %float %uint_200 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %long + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %long_199 = OpConstant %long 199 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %long_199 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayGeneralULongIndexIntBoundsClamped) { + // Unsigned long index i is wider than the array bounds type. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int64\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesLong() << TypesFloat() << R"( + %int_200 = OpConstant %int 200 + %arr = OpTypeArray %float %int_200 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %ulong + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %long_199 = OpConstant %long 199 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %long_199 + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, + ACArrayGeneralShortIndeArrayBiggerThanShortMaxClipsToShortIntMax) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesShort() + << TypesInt() << TypesFloat() << R"( + %uint_50000 = OpConstant %uint 50000 + %arr = OpTypeArray %float %uint_50000 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %ushort + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %short_0 = OpConstant %short 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %short 32767 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %ushort %[[GLSLSTD450]] SClamp %i %short_0 %[[intmax]] + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, + ACArrayGeneralIntIndexArrayBiggerThanIntMaxClipsToSignedIntMax) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %uint_3000000000 = OpConstant %uint 3000000000 + %arr = OpTypeArray %float %uint_3000000000 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %uint + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %int_0 %[[intmax]] + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%i", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, + ACArrayGeneralLongIndexArrayBiggerThanLongMaxClipsToSignedLongMax) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int64\n" + << ShaderPreambleAC({"i"}) << TypesVoid() << TypesInt() + << TypesLong() + << TypesFloat() + // 2^63 == 9,223,372,036,854,775,807 + << R"( + %ulong_9223372036854775999 = OpConstant %ulong 9223372036854775999 + %arr = OpTypeArray %float %ulong_9223372036854775999 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %ulong + )" + << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %long 9223372036854775807 + ; CHECK: OpLabel + ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %long_0 %[[intmax]] + %var = OpVariable %var_ty Function)" << ACCheck(ac, "%i", "%[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArraySpecIdSizedAlwaysClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"spec200"}) << R"( + OpDecorate %spec200 SpecId 0 )" << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %spec200 = OpSpecConstant %int 200 + %arr = OpTypeArray %float %spec200 + %var_ty = OpTypePointer Function %arr + %ptr_ty = OpTypePointer Function %float + %uint_5 = OpConstant %uint 5 + )" << MainPrefix() << R"( + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %uint_0 = OpConstant %uint 0 + ; CHECK-DAG: %uint_1 = OpConstant %uint 1 + ; CHECK-DAG: %[[uint_intmax:\w+]] = OpConstant %uint 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[max:\w+]] = OpISub %uint %spec200 %uint_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[uint_intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %uint_5 %uint_0 %[[smin]] + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%uint_5", "%[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACStructLeastUntouched) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %struct = OpTypeStruct %float %float %float + %var_ty = OpTypePointer Function %struct + %ptr_ty = OpTypePointer Function %float + %int_0 = OpConstant %int 0 + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%int_0", "%int_0") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACStructMostUntouched) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %struct = OpTypeStruct %float %float %float + %var_ty = OpTypePointer Function %struct + %ptr_ty = OpTypePointer Function %float + %int_2 = OpConstant %int 2 + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function)" + << ACCheck(ac, "%int_2", "%int_2") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACStructSpecConstantFail) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"struct", "spec200"}) + << "OpDecorate %spec200 SpecId 0\n" + << + + TypesVoid() << TypesInt() << TypesFloat() << R"( + %spec200 = OpSpecConstant %int 200 + %struct = OpTypeStruct %float %float %float + %var_ty = OpTypePointer Function %struct + %ptr_ty = OpTypePointer Function %float + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function + ; CHECK: Member index into struct is not a constant integer + ; CHECK-SAME: %spec200 = OpSpecConstant %int 200 + )" + << ACCheckFail(ac, "%spec200", "%spec200") << MainSuffix(); + SinglePassRunAndFail(shaders.str()); + } +} + +TEST_F(GraphicsRobustAccessTest, ACStructFloatConstantFail) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"struct"}) << + + TypesVoid() << TypesInt() << TypesFloat() << R"( + %float_2 = OpConstant %float 2 + %struct = OpTypeStruct %float %float %float + %var_ty = OpTypePointer Function %struct + %ptr_ty = OpTypePointer Function %float + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function + ; CHECK: Member index into struct is not a constant integer + ; CHECK-SAME: %float_2 = OpConstant %float 2 + )" + << ACCheckFail(ac, "%float_2", "%float_2") << MainSuffix(); + SinglePassRunAndFail(shaders.str()); + } +} + +TEST_F(GraphicsRobustAccessTest, ACStructNonConstantFail) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"struct", "i"}) << + + TypesVoid() << TypesInt() << TypesFloat() << R"( + %float_2 = OpConstant %float 2 + %struct = OpTypeStruct %float %float %float + %var_ty = OpTypePointer Function %struct + %ptr_ty = OpTypePointer Function %float + %i = OpUndef %int + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function + ; CHECK: Member index into struct is not a constant integer + ; CHECK-SAME: %i = OpUndef %int + )" + << ACCheckFail(ac, "%i", "%i") << MainSuffix(); + SinglePassRunAndFail(shaders.str()); + } +} + +TEST_F(GraphicsRobustAccessTest, ACStructExcessFail) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %struct = OpTypeStruct %float %float %float + %var_ty = OpTypePointer Function %struct + %ptr_ty = OpTypePointer Function %float + %i = OpConstant %int 4 + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function + ; CHECK: Member index 4 is out of bounds for struct type: + ; CHECK-SAME: %struct = OpTypeStruct %float %float %float + )" + << ACCheckFail(ac, "%i", "%i") << MainSuffix(); + SinglePassRunAndFail(shaders.str()); + } +} + +TEST_F(GraphicsRobustAccessTest, ACStructNegativeFail) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"struct", "i"}) << TypesVoid() << TypesInt() + << TypesFloat() << R"( + %struct = OpTypeStruct %float %float %float + %var_ty = OpTypePointer Function %struct + %ptr_ty = OpTypePointer Function %float + %i = OpConstant %int -1 + )" << MainPrefix() << R"( + %var = OpVariable %var_ty Function + ; CHECK: Member index -1 is out of bounds for struct type: + ; CHECK-SAME: %struct = OpTypeStruct %float %float %float + )" + << ACCheckFail(ac, "%i", "%i") << MainSuffix(); + SinglePassRunAndFail(shaders.str()); + } +} + +TEST_F(GraphicsRobustAccessTest, ACRTArrayLeastInboundClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC() << "OpDecorate %rtarr ArrayStride 4 " + << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"( + %rtarr = OpTypeRuntimeArray %float + %ssbo_s = OpTypeStruct %uint %uint %rtarr + %var_ty = OpTypePointer Uniform %ssbo_s + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_0 = OpConstant %int 0 + %int_2 = OpConstant %int 2 + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_1 = OpConstant %int 1 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 + ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %int_0 %int_0 %[[smin]] + )" + << MainPrefix() << ACCheck(ac, "%int_2 %int_0", "%int_2 %[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralShortIndexClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 " + << DecoSSBO() << TypesVoid() << TypesShort() << TypesFloat() << R"( + %rtarr = OpTypeRuntimeArray %float + %ssbo_s = OpTypeStruct %short %short %rtarr + %var_ty = OpTypePointer Uniform %ssbo_s + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %short_2 = OpConstant %short 2 + %i = OpUndef %short + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK: %uint = OpTypeInt 32 0 + ; CHECK-DAG: %uint_1 = OpConstant %uint 1 + ; CHECK-DAG: %uint_0 = OpConstant %uint 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 + ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1 + ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i + ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]] + )" + << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUShortIndexClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int16\n" + << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 " + << DecoSSBO() << TypesVoid() << TypesShort() << TypesFloat() << R"( + %rtarr = OpTypeRuntimeArray %float + %ssbo_s = OpTypeStruct %short %short %rtarr + %var_ty = OpTypePointer Uniform %ssbo_s + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %short_2 = OpConstant %short 2 + %i = OpUndef %ushort + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK: %uint = OpTypeInt 32 0 + ; CHECK-DAG: %uint_1 = OpConstant %uint 1 + ; CHECK-DAG: %uint_0 = OpConstant %uint 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 + ; CHECK-DAG: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1 + ; CHECK-DAG: %[[i_ext:\w+]] = OpSConvert %uint %i + ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %[[i_ext]] %uint_0 %[[smin]] + )" + << MainPrefix() << ACCheck(ac, "%short_2 %i", "%short_2 %[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralIntIndexClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 " + << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"( + %rtarr = OpTypeRuntimeArray %float + %ssbo_s = OpTypeStruct %int %int %rtarr + %var_ty = OpTypePointer Uniform %ssbo_s + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_2 = OpConstant %int 2 + %i = OpUndef %int + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_1 = OpConstant %int 1 + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 + ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]] + )" + << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralUIntIndexClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i"}) << "OpDecorate %rtarr ArrayStride 4 " + << DecoSSBO() << TypesVoid() << TypesInt() << TypesFloat() << R"( + %rtarr = OpTypeRuntimeArray %float + %ssbo_s = OpTypeStruct %int %int %rtarr + %var_ty = OpTypePointer Uniform %ssbo_s + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_2 = OpConstant %int 2 + %i = OpUndef %uint + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %uint_1 = OpConstant %uint 1 + ; CHECK-DAG: %uint_0 = OpConstant %uint 0 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %uint 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 + ; CHECK: %[[max:\w+]] = OpISub %uint %[[arrlen]] %uint_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %uint %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %uint %[[GLSLSTD450]] SClamp %i %uint_0 %[[smin]] + )" + << MainPrefix() << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralLongIndexClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int64" << ShaderPreambleAC({"i"}) + << "OpDecorate %rtarr ArrayStride 4 " << DecoSSBO() << TypesVoid() + << TypesInt() << TypesLong() << TypesFloat() << R"( + %rtarr = OpTypeRuntimeArray %float + %ssbo_s = OpTypeStruct %int %int %rtarr + %var_ty = OpTypePointer Uniform %ssbo_s + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_2 = OpConstant %int 2 + %i = OpUndef %long + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %long_0 = OpConstant %long 0 + ; CHECK-DAG: %long_1 = OpConstant %long 1 + ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %long 9223372036854775807 + ; CHECK: OpLabel + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 + ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]] + ; CHECK: %[[max:\w+]] = OpISub %long %[[arrlen_ext]] %long_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %long %[[GLSLSTD450]] UMin %[[max]] %[[longmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %long %[[GLSLSTD450]] SClamp %i %long_0 %[[smin]] + )" << MainPrefix() + << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACRTArrayGeneralULongIndexClamped) { + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << "OpCapability Int64" << ShaderPreambleAC({"i"}) + << "OpDecorate %rtarr ArrayStride 4 " << DecoSSBO() << TypesVoid() + << TypesInt() << TypesLong() << TypesFloat() << R"( + %rtarr = OpTypeRuntimeArray %float + %ssbo_s = OpTypeStruct %int %int %rtarr + %var_ty = OpTypePointer Uniform %ssbo_s + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_2 = OpConstant %int 2 + %i = OpUndef %ulong + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %ulong_0 = OpConstant %ulong 0 + ; CHECK-DAG: %ulong_1 = OpConstant %ulong 1 + ; CHECK-DAG: %[[longmax:\w+]] = OpConstant %ulong 9223372036854775807 + ; CHECK: OpLabel + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 + ; CHECK: %[[arrlen_ext:\w+]] = OpUConvert %ulong %[[arrlen]] + ; CHECK: %[[max:\w+]] = OpISub %ulong %[[arrlen_ext]] %ulong_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] UMin %[[max]] %[[longmax]] + ; CHECK: %[[clamp:\w+]] = OpExtInst %ulong %[[GLSLSTD450]] SClamp %i %ulong_0 %[[smin]] + )" << MainPrefix() + << ACCheck(ac, "%int_2 %i", "%int_2 %[[clamp]]") << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACRTArrayStructVectorElem) { + // The point of this test is that the access chain can have indices past the + // index into the runtime array. For good measure, the index into the final + // struct is out of bounds. We have to clamp that index too. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i", "j"}) + << "OpDecorate %rtarr ArrayStride 32\n" + << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n" + << "OpMemberDecorate %rtelem 1 Offset 16\n" + << TypesVoid() << TypesInt() << TypesFloat() << R"( + %v4float = OpTypeVector %float 4 + %rtelem = OpTypeStruct %v4float %v4float + %rtarr = OpTypeRuntimeArray %rtelem + %ssbo_s = OpTypeStruct %int %int %rtarr + %var_ty = OpTypePointer Uniform %ssbo_s + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %i = OpUndef %int + %j = OpUndef %int + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_3 = OpConstant %int 3 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %var 2 + ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]] + ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %int_3 + )" << MainPrefix() + << ACCheck(ac, "%int_2 %i %int_1 %j", + "%int_2 %[[clamp_i]] %int_1 %[[clamp_j]]") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACArrayRTArrayStructVectorElem) { + // Now add an additional level of arrays around the Block-decorated struct. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i", "ssbo_s"}) + << "OpDecorate %rtarr ArrayStride 32\n" + << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n" + << "OpMemberDecorate %rtelem 1 Offset 16\n" + << TypesVoid() << TypesInt() << TypesFloat() << R"( + %v4float = OpTypeVector %float 4 + %rtelem = OpTypeStruct %v4float %v4float + %rtarr = OpTypeRuntimeArray %rtelem + %ssbo_s = OpTypeStruct %int %int %rtarr + %arr_size = OpConstant %int 10 + %arr_ssbo = OpTypeArray %ssbo_s %arr_size + %var_ty = OpTypePointer Uniform %arr_ssbo + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %int_17 = OpConstant %int 17 + %i = OpUndef %int + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %[[ssbo_p:\w+]] = OpTypePointer Uniform %ssbo_s + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_9 = OpConstant %int 9 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 + ; CHECK: OpLabel + ; This access chain is manufatured only so we can compute the array length. + ; Note that the %int_9 is already clamped + ; CHECK: %[[ssbo_base:\w+]] = )" << ac + << R"( %[[ssbo_p]] %var %int_9 + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %[[ssbo_base]] 2 + ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %[[smin]] + )" << MainPrefix() + << ACCheck(ac, "%int_17 %int_2 %i %int_1 %int_2", + "%int_9 %int_2 %[[clamp_i]] %int_1 %int_2") + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, ACSplitACArrayRTArrayStructVectorElem) { + // Split the address calculation across two access chains. Force + // the transform to walk up the access chains to find the base variable. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i", "j", "k", "ssbo_s", "ssbo_pty", + "rtarr_pty", "ac_ssbo", "ac_rtarr"}) + << "OpDecorate %rtarr ArrayStride 32\n" + << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n" + << "OpMemberDecorate %rtelem 1 Offset 16\n" + << TypesVoid() << TypesInt() << TypesFloat() << R"( + %v4float = OpTypeVector %float 4 + %rtelem = OpTypeStruct %v4float %v4float + %rtarr = OpTypeRuntimeArray %rtelem + %ssbo_s = OpTypeStruct %int %int %rtarr + %arr_size = OpConstant %int 10 + %arr_ssbo = OpTypeArray %ssbo_s %arr_size + %var_ty = OpTypePointer Uniform %arr_ssbo + %ssbo_pty = OpTypePointer Uniform %ssbo_s + %rtarr_pty = OpTypePointer Uniform %rtarr + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %i = OpUndef %int + %j = OpUndef %int + %k = OpUndef %int + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_9 = OpConstant %int 9 + ; CHECK-DAG: %int_3 = OpConstant %int 3 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9 + ; CHECK: %ac_ssbo = )" << ac + << R"( %ssbo_pty %var %[[clamp_i]] + ; CHECK: %ac_rtarr = )" + << ac << R"( %rtarr_pty %ac_ssbo %int_2 + + ; This is the interesting bit. This array length is needed for an OpAccessChain + ; computing %ac, but the algorithm had to track back through %ac_rtarr's + ; definition to find the base pointer %ac_ssbo. + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2 + ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]] + ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3 + ; CHECK: %ac = )" << ac + << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]] + ; CHECK-NOT: AccessChain + )" << MainPrefix() + << "%ac_ssbo = " << ac << " %ssbo_pty %var %i\n" + << "%ac_rtarr = " << ac << " %rtarr_pty %ac_ssbo %int_2\n" + << "%ac = " << ac << " %ptr_ty %ac_rtarr %j %int_1 %k\n" + + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, + ACSplitACArrayRTArrayStructVectorElemAcrossBasicBlocks) { + // Split the address calculation across two access chains. Force + // the transform to walk up the access chains to find the base variable. + // This time, put the different access chains in different basic blocks. + // This is an integrity check to ensure that we keep the instruction-to-block + // mapping consistent. + for (auto* ac : AccessChains()) { + std::ostringstream shaders; + shaders << ShaderPreambleAC({"i", "j", "k", "bb1", "bb2", "ssbo_s", + "ssbo_pty", "rtarr_pty", "ac_ssbo", + "ac_rtarr"}) + << "OpDecorate %rtarr ArrayStride 32\n" + << DecoSSBO() << "OpMemberDecorate %rtelem 0 Offset 0\n" + << "OpMemberDecorate %rtelem 1 Offset 16\n" + << TypesVoid() << TypesInt() << TypesFloat() << R"( + %v4float = OpTypeVector %float 4 + %rtelem = OpTypeStruct %v4float %v4float + %rtarr = OpTypeRuntimeArray %rtelem + %ssbo_s = OpTypeStruct %int %int %rtarr + %arr_size = OpConstant %int 10 + %arr_ssbo = OpTypeArray %ssbo_s %arr_size + %var_ty = OpTypePointer Uniform %arr_ssbo + %ssbo_pty = OpTypePointer Uniform %ssbo_s + %rtarr_pty = OpTypePointer Uniform %rtarr + %ptr_ty = OpTypePointer Uniform %float + %var = OpVariable %var_ty Uniform + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %i = OpUndef %int + %j = OpUndef %int + %k = OpUndef %int + ; CHECK: %[[GLSLSTD450:\w+]] = OpExtInstImport "GLSL.std.450" + ; CHECK-DAG: %int_0 = OpConstant %int 0 + ; CHECK-DAG: %int_9 = OpConstant %int 9 + ; CHECK-DAG: %int_3 = OpConstant %int 3 + ; CHECK-DAG: %[[intmax:\w+]] = OpConstant %int 2147483647 + ; CHECK: OpLabel + ; CHECK: %[[clamp_i:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %i %int_0 %int_9 + ; CHECK: %ac_ssbo = )" << ac + << R"( %ssbo_pty %var %[[clamp_i]] + ; CHECK: %bb1 = OpLabel + ; CHECK: %ac_rtarr = )" + << ac << R"( %rtarr_pty %ac_ssbo %int_2 + ; CHECK: %bb2 = OpLabel + + ; This is the interesting bit. This array length is needed for an OpAccessChain + ; computing %ac, but the algorithm had to track back through %ac_rtarr's + ; definition to find the base pointer %ac_ssbo. + ; CHECK: %[[arrlen:\w+]] = OpArrayLength %uint %ac_ssbo 2 + ; CHECK: %[[max:\w+]] = OpISub %int %[[arrlen]] %int_1 + ; CHECK: %[[smin:\w+]] = OpExtInst %int %[[GLSLSTD450]] UMin %[[max]] %[[intmax]] + ; CHECK: %[[clamp_j:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %j %int_0 %[[smin]] + ; CHECK: %[[clamp_k:\w+]] = OpExtInst %int %[[GLSLSTD450]] SClamp %k %int_0 %int_3 + ; CHECK: %ac = )" << ac + << R"( %ptr_ty %ac_rtarr %[[clamp_j]] %int_1 %[[clamp_k]] + ; CHECK-NOT: AccessChain + )" << MainPrefix() + << "%ac_ssbo = " << ac << " %ssbo_pty %var %i\n" + << "OpBranch %bb1\n%bb1 = OpLabel\n" + << "%ac_rtarr = " << ac << " %rtarr_pty %ac_ssbo %int_2\n" + << "OpBranch %bb2\n%bb2 = OpLabel\n" + << "%ac = " << ac << " %ptr_ty %ac_rtarr %j %int_1 %k\n" + + << MainSuffix(); + SinglePassRunAndMatch(shaders.str(), true); + } +} + +TEST_F(GraphicsRobustAccessTest, bug3813) { + // This shader comes from Dawn's + // TextureViewSamplingTest.TextureCubeMapOnWholeTexture, converted from GLSL + // by glslang. + // The pass was inserting a signed 32-bit int type, but not correctly marking + // the shader as changed. + std::string shader = R"( +; SPIR-V +; Version: 1.0 +; Generator: Google Shaderc over Glslang; 10 +; Bound: 46 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 %29 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %4 "main" + OpName %8 "sc" + OpName %12 "texCoord" + OpName %21 "tc" + OpName %29 "fragColor" + OpName %32 "texture0" + OpName %36 "sampler0" + OpDecorate %12 Location 0 + OpDecorate %29 Location 0 + OpDecorate %32 DescriptorSet 0 + OpDecorate %32 Binding 1 + OpDecorate %36 DescriptorSet 0 + OpDecorate %36 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %10 = OpTypeVector %6 2 + %11 = OpTypePointer Input %10 + %12 = OpVariable %11 Input + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Input %6 + %19 = OpConstant %6 1 + %22 = OpConstant %13 1 + %27 = OpTypeVector %6 4 + %28 = OpTypePointer Output %27 + %29 = OpVariable %28 Output + %30 = OpTypeImage %6 Cube 0 0 0 1 Unknown + %31 = OpTypePointer UniformConstant %30 + %32 = OpVariable %31 UniformConstant + %34 = OpTypeSampler + %35 = OpTypePointer UniformConstant %34 + %36 = OpVariable %35 UniformConstant + %38 = OpTypeSampledImage %30 + %43 = OpTypeVector %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %21 = OpVariable %7 Function + %16 = OpAccessChain %15 %12 %14 + %17 = OpLoad %6 %16 + %18 = OpFMul %6 %9 %17 + %20 = OpFSub %6 %18 %19 + OpStore %8 %20 + %23 = OpAccessChain %15 %12 %22 + %24 = OpLoad %6 %23 + %25 = OpFMul %6 %9 %24 + %26 = OpFSub %6 %25 %19 + OpStore %21 %26 + %33 = OpLoad %30 %32 + %37 = OpLoad %34 %36 + %39 = OpSampledImage %38 %33 %37 + %40 = OpLoad %6 %21 + %41 = OpLoad %6 %8 + %42 = OpFNegate %6 %41 + %44 = OpCompositeConstruct %43 %19 %40 %42 + %45 = OpImageSampleImplicitLod %27 %39 %44 + OpStore %29 %45 + OpReturn + OpFunctionEnd +)"; + + std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %texCoord %fragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpName %main "main" +OpName %sc "sc" +OpName %texCoord "texCoord" +OpName %tc "tc" +OpName %fragColor "fragColor" +OpName %texture0 "texture0" +OpName %sampler0 "sampler0" +OpDecorate %texCoord Location 0 +OpDecorate %fragColor Location 0 +OpDecorate %texture0 DescriptorSet 0 +OpDecorate %texture0 Binding 1 +OpDecorate %sampler0 DescriptorSet 0 +OpDecorate %sampler0 Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_2 = OpConstant %float 2 +%v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoord = OpVariable %_ptr_Input_v2float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%float_1 = OpConstant %float 1 +%uint_1 = OpConstant %uint 1 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%fragColor = OpVariable %_ptr_Output_v4float Output +%23 = OpTypeImage %float Cube 0 0 0 1 Unknown +%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 +%texture0 = OpVariable %_ptr_UniformConstant_23 UniformConstant +%25 = OpTypeSampler +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 +%sampler0 = OpVariable %_ptr_UniformConstant_25 UniformConstant +%27 = OpTypeSampledImage %23 +%v3float = OpTypeVector %float 3 +%int = OpTypeInt 32 1 +%main = OpFunction %void None %10 +%29 = OpLabel +%sc = OpVariable %_ptr_Function_float Function +%tc = OpVariable %_ptr_Function_float Function +%30 = OpAccessChain %_ptr_Input_float %texCoord %uint_0 +%31 = OpLoad %float %30 +%32 = OpFMul %float %float_2 %31 +%33 = OpFSub %float %32 %float_1 +OpStore %sc %33 +%34 = OpAccessChain %_ptr_Input_float %texCoord %uint_1 +%35 = OpLoad %float %34 +%36 = OpFMul %float %float_2 %35 +%37 = OpFSub %float %36 %float_1 +OpStore %tc %37 +%38 = OpLoad %23 %texture0 +%39 = OpLoad %25 %sampler0 +%40 = OpSampledImage %27 %38 %39 +%41 = OpLoad %float %tc +%42 = OpLoad %float %sc +%43 = OpFNegate %float %42 +%44 = OpCompositeConstruct %v3float %float_1 %41 %43 +%45 = OpImageSampleImplicitLod %v4float %40 %44 +OpStore %fragColor %45 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(shader, expected, false, + true); +} + +// TODO(dneto): Test access chain index wider than 64 bits? +// TODO(dneto): Test struct access chain index wider than 64 bits? +// TODO(dneto): OpImageTexelPointer +// - all Dim types: 1D 2D Cube 3D Rect Buffer +// - all Dim types that can be arrayed: 1D 2D 3D +// - sample index: set to 0 if not multisampled +// - Dim (2D, Cube Rect} with multisampling +// -1 0 max excess +// TODO(dneto): Test OpImageTexelPointer with coordinate component index other +// than 32 bits. + +} // namespace diff --git a/third_party/spirv-tools/test/opt/if_conversion_test.cpp b/third_party/spirv-tools/test/opt/if_conversion_test.cpp new file mode 100644 index 0000000..dee15c3 --- /dev/null +++ b/third_party/spirv-tools/test/opt/if_conversion_test.cpp @@ -0,0 +1,562 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using IfConversionTest = PassTest<::testing::Test>; + +TEST_F(IfConversionTest, TestSimpleIfThenElse) { + const std::string text = R"( +; CHECK: OpSelectionMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHECK-NOT: OpPhi +; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 +; CHECK OpStore {{%\w+}} [[sel]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%11 = OpTypeFunction %void +%1 = OpFunction %void None %11 +%12 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %true %15 %16 +%15 = OpLabel +OpBranch %14 +%16 = OpLabel +OpBranch %14 +%14 = OpLabel +%18 = OpPhi %uint %uint_0 %15 %uint_1 %16 +OpStore %2 %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(IfConversionTest, TestSimpleHalfIfTrue) { + const std::string text = R"( +; CHECK: OpSelectionMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHECK-NOT: OpPhi +; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 +; CHECK OpStore {{%\w+}} [[sel]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%11 = OpTypeFunction %void +%1 = OpFunction %void None %11 +%12 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %true %15 %14 +%15 = OpLabel +OpBranch %14 +%14 = OpLabel +%18 = OpPhi %uint %uint_0 %15 %uint_1 %12 +OpStore %2 %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(IfConversionTest, TestSimpleHalfIfExtraBlock) { + const std::string text = R"( +; CHECK: OpSelectionMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHECK-NOT: OpPhi +; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 +; CHECK OpStore {{%\w+}} [[sel]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%11 = OpTypeFunction %void +%1 = OpFunction %void None %11 +%12 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %true %15 %14 +%15 = OpLabel +OpBranch %16 +%16 = OpLabel +OpBranch %14 +%14 = OpLabel +%18 = OpPhi %uint %uint_0 %15 %uint_1 %12 +OpStore %2 %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(IfConversionTest, TestSimpleHalfIfFalse) { + const std::string text = R"( +; CHECK: OpSelectionMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHECK-NOT: OpPhi +; CHECK: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 +; CHECK OpStore {{%\w+}} [[sel]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%11 = OpTypeFunction %void +%1 = OpFunction %void None %11 +%12 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %true %14 %15 +%15 = OpLabel +OpBranch %14 +%14 = OpLabel +%18 = OpPhi %uint %uint_0 %12 %uint_1 %15 +OpStore %2 %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(IfConversionTest, TestVectorSplat) { + const std::string text = R"( +; CHECK: [[bool_vec:%\w+]] = OpTypeVector %bool 2 +; CHECK: OpSelectionMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHECK-NOT: OpPhi +; CHECK: [[comp:%\w+]] = OpCompositeConstruct [[bool_vec]] %true %true +; CHECK: [[sel:%\w+]] = OpSelect {{%\w+}} [[comp]] +; CHECK OpStore {{%\w+}} [[sel]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_vec2 = OpTypeVector %uint 2 +%vec2_01 = OpConstantComposite %uint_vec2 %uint_0 %uint_1 +%vec2_10 = OpConstantComposite %uint_vec2 %uint_1 %uint_0 +%_ptr_Output_uint = OpTypePointer Output %uint_vec2 +%2 = OpVariable %_ptr_Output_uint Output +%11 = OpTypeFunction %void +%1 = OpFunction %void None %11 +%12 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %true %15 %16 +%15 = OpLabel +OpBranch %14 +%16 = OpLabel +OpBranch %14 +%14 = OpLabel +%18 = OpPhi %uint_vec2 %vec2_01 %15 %vec2_10 %16 +OpStore %2 %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(IfConversionTest, CodeMotionSameValue) { + const std::string text = R"( +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK-NOT: OpLabel +; CHECK: [[add:%\w+]] = OpIAdd %uint %uint_0 %uint_1 +; CHECK: OpSelectionMerge [[merge_lab:%\w+]] None +; CHECK-NEXT: OpBranchConditional +; CHECK: [[merge_lab]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpStore [[var]] [[add]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "func" %2 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint + %2 = OpVariable %_ptr_Output_uint Output + %8 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %1 = OpFunction %void None %8 + %11 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %true %13 %15 + %13 = OpLabel + %14 = OpIAdd %uint %uint_0 %uint_1 + OpBranch %12 + %15 = OpLabel + %16 = OpIAdd %uint %uint_0 %uint_1 + OpBranch %12 + %12 = OpLabel + %17 = OpPhi %uint %16 %15 %14 %13 + OpStore %2 %17 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(IfConversionTest, CodeMotionMultipleInstructions) { + const std::string text = R"( +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK-NOT: OpLabel +; CHECK: [[a1:%\w+]] = OpIAdd %uint %uint_0 %uint_1 +; CHECK: [[a2:%\w+]] = OpIAdd %uint [[a1]] %uint_1 +; CHECK: OpSelectionMerge [[merge_lab:%\w+]] None +; CHECK-NEXT: OpBranchConditional +; CHECK: [[merge_lab]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpStore [[var]] [[a2]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "func" %2 + %void = OpTypeVoid + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint + %2 = OpVariable %_ptr_Output_uint Output + %8 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %1 = OpFunction %void None %8 + %11 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %true %13 %15 + %13 = OpLabel + %a1 = OpIAdd %uint %uint_0 %uint_1 + %a2 = OpIAdd %uint %a1 %uint_1 + OpBranch %12 + %15 = OpLabel + %b1 = OpIAdd %uint %uint_0 %uint_1 + %b2 = OpIAdd %uint %b1 %uint_1 + OpBranch %12 + %12 = OpLabel + %17 = OpPhi %uint %b2 %15 %a2 %13 + OpStore %2 %17 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(IfConversionTest, NoCommonDominator) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%8 = OpTypeFunction %void +%1 = OpFunction %void None %8 +%9 = OpLabel +OpBranch %10 +%11 = OpLabel +OpBranch %10 +%10 = OpLabel +%12 = OpPhi %uint %uint_0 %9 %uint_1 %11 +OpStore %2 %12 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(IfConversionTest, LoopUntouched) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%8 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%1 = OpFunction %void None %8 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +%13 = OpPhi %uint %uint_0 %11 %uint_1 %12 +OpLoopMerge %14 %12 None +OpBranchConditional %true %14 %12 +%14 = OpLabel +OpStore %2 %13 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(IfConversionTest, TooManyPredecessors) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%8 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%1 = OpFunction %void None %8 +%11 = OpLabel +OpSelectionMerge %12 None +OpBranchConditional %true %13 %12 +%13 = OpLabel +OpBranchConditional %true %14 %12 +%14 = OpLabel +OpBranch %12 +%12 = OpLabel +%15 = OpPhi %uint %uint_0 %11 %uint_0 %13 %uint_1 %14 +OpStore %2 %15 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(IfConversionTest, NoCodeMotion) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%8 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%1 = OpFunction %void None %8 +%11 = OpLabel +OpSelectionMerge %12 None +OpBranchConditional %true %13 %12 +%13 = OpLabel +%14 = OpIAdd %uint %uint_0 %uint_1 +OpBranch %12 +%12 = OpLabel +%15 = OpPhi %uint %uint_0 %11 %14 %13 +OpStore %2 %15 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(IfConversionTest, NoCodeMotionImmovableInst) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%8 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%1 = OpFunction %void None %8 +%11 = OpLabel +OpSelectionMerge %12 None +OpBranchConditional %true %13 %14 +%13 = OpLabel +OpSelectionMerge %15 None +OpBranchConditional %true %16 %15 +%16 = OpLabel +%17 = OpIAdd %uint %uint_0 %uint_1 +OpBranch %15 +%15 = OpLabel +%18 = OpPhi %uint %uint_0 %13 %17 %16 +%19 = OpIAdd %uint %18 %uint_1 +OpBranch %12 +%14 = OpLabel +OpSelectionMerge %20 None +OpBranchConditional %true %21 %20 +%21 = OpLabel +%22 = OpIAdd %uint %uint_0 %uint_1 +OpBranch %20 +%20 = OpLabel +%23 = OpPhi %uint %uint_0 %14 %22 %21 +%24 = OpIAdd %uint %23 %uint_1 +OpBranch %12 +%12 = OpLabel +%25 = OpPhi %uint %24 %20 %19 %15 +OpStore %2 %25 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(IfConversionTest, InvalidCommonDominator) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%1 = OpTypeFunction %void +%2 = OpFunction %void None %1 +%3 = OpLabel +OpBranch %4 +%4 = OpLabel +OpLoopMerge %5 %6 None +OpBranch %7 +%7 = OpLabel +OpSelectionMerge %8 None +OpBranchConditional %true %8 %9 +%9 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %true %10 %5 +%10 = OpLabel +OpBranch %8 +%8 = OpLabel +OpBranch %6 +%6 = OpLabel +OpBranchConditional %true %4 %5 +%5 = OpLabel +%11 = OpPhi %float %float_0 %6 %float_1 %9 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(IfConversionTest, DebugInfoSimpleIfThenElse) { + // When it replaces an OpPhi with OpSelect, the new OpSelect must have + // the same scope and line information with the OpPhi. + const std::string text = R"( +; CHECK: OpSelectionMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHECK-NOT: OpPhi +; CHECK: DebugScope +; CHECK-NEXT: OpLine {{%\w+}} 3 7 +; CHECK-NEXT: [[sel:%\w+]] = OpSelect %uint %true %uint_0 %uint_1 +; CHECK-NEXT: DebugValue {{%\w+}} [[sel]] +; CHECK: OpStore {{%\w+}} [[sel]] +OpCapability Shader +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "func" %2 +%name = OpString "test" +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_32 = OpConstant %uint 32 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%11 = OpTypeFunction %void +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %name %uint_32 Float +%dbg_f = OpExtInst %void %ext DebugLocalVariable %name %dbg_tf %src 0 0 %cu FlagIsLocal +%1 = OpFunction %void None %11 +%12 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %true %15 %16 +%15 = OpLabel +OpBranch %14 +%16 = OpLabel +OpBranch %14 +%14 = OpLabel +%scope = OpExtInst %void %ext DebugScope %cu +OpLine %name 3 7 +%18 = OpPhi %uint %uint_0 %15 %uint_1 %16 +%value = OpExtInst %void %ext DebugValue %dbg_f %18 %null_expr +OpStore %2 %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/inline_opaque_test.cpp b/third_party/spirv-tools/test/opt/inline_opaque_test.cpp new file mode 100644 index 0000000..47e0533 --- /dev/null +++ b/third_party/spirv-tools/test/opt/inline_opaque_test.cpp @@ -0,0 +1,414 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InlineOpaqueTest = PassTest<::testing::Test>; + +TEST_F(InlineOpaqueTest, InlineCallWithStructArgContainingSampledImage) { + // Function with opaque argument is inlined. + // TODO(greg-lunarg): Add HLSL code + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %outColor %texCoords +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %foo_struct_S_t_vf2_vf21_ "foo(struct-S_t-vf2-vf21;" +OpName %s "s" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpName %param "param" +OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%17 = OpTypeImage %float 2D 0 0 0 1 Unknown +%18 = OpTypeSampledImage %17 +%S_t = OpTypeStruct %v2float %v2float %18 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%20 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%_ptr_Function_18 = OpTypePointer Function %18 +%sampler15 = OpVariable %_ptr_UniformConstant_18 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %12 +%28 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%29 = OpLoad %v2float %texCoords +%30 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %30 %29 +%31 = OpLoad %18 %sampler15 +%32 = OpAccessChain %_ptr_Function_18 %s0 %int_2 +OpStore %32 %31 +%33 = OpLoad %S_t %s0 +OpStore %param %33 +%34 = OpFunctionCall %void %foo_struct_S_t_vf2_vf21_ %param +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%34 = OpUndef %void +%main = OpFunction %void None %12 +%28 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%29 = OpLoad %v2float %texCoords +%30 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %30 %29 +%31 = OpLoad %18 %sampler15 +%32 = OpAccessChain %_ptr_Function_18 %s0 %int_2 +OpStore %32 %31 +%33 = OpLoad %S_t %s0 +OpStore %param %33 +%42 = OpAccessChain %_ptr_Function_18 %param %int_2 +%43 = OpLoad %18 %42 +%44 = OpAccessChain %_ptr_Function_v2float %param %int_0 +%45 = OpLoad %v2float %44 +%46 = OpImageSampleImplicitLod %v4float %43 %45 +OpStore %outColor %46 +OpReturn +OpFunctionEnd +)"; + + const std::string post_defs = + R"(%foo_struct_S_t_vf2_vf21_ = OpFunction %void None %20 +%s = OpFunctionParameter %_ptr_Function_S_t +%35 = OpLabel +%36 = OpAccessChain %_ptr_Function_18 %s %int_2 +%37 = OpLoad %18 %36 +%38 = OpAccessChain %_ptr_Function_v2float %s %int_0 +%39 = OpLoad %v2float %38 +%40 = OpImageSampleImplicitLod %v4float %37 %39 +OpStore %outColor %40 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs + before + post_defs, predefs + after + post_defs, true, true); +} + +TEST_F(InlineOpaqueTest, InlineOpaqueReturn) { + // Function with opaque return value is inlined. + // TODO(greg-lunarg): Add HLSL code + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %texCoords %outColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_ "foo(" +OpName %texCoords "texCoords" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %sampler16 "sampler16" +OpDecorate %sampler15 DescriptorSet 0 +OpDecorate %sampler16 DescriptorSet 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%bool = OpTypeBool +%false = OpConstantFalse %bool +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +%float_0 = OpConstant %float 0 +%16 = OpConstantComposite %v2float %float_0 %float_0 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%19 = OpTypeImage %float 2D 0 0 0 1 Unknown +%20 = OpTypeSampledImage %19 +%21 = OpTypeFunction %20 +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 +%_ptr_Function_20 = OpTypePointer Function %20 +%sampler15 = OpVariable %_ptr_UniformConstant_20 UniformConstant +%sampler16 = OpVariable %_ptr_UniformConstant_20 UniformConstant +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%24 = OpLabel +%25 = OpVariable %_ptr_Function_20 Function +%26 = OpFunctionCall %20 %foo_ +OpStore %25 %26 +%27 = OpLoad %20 %25 +%28 = OpLoad %v2float %texCoords +%29 = OpImageSampleImplicitLod %v4float %27 %28 +OpStore %outColor %29 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%24 = OpLabel +%34 = OpVariable %_ptr_Function_20 Function +%35 = OpVariable %_ptr_Function_20 Function +%25 = OpVariable %_ptr_Function_20 Function +%37 = OpLoad %20 %sampler16 +OpStore %34 %37 +%38 = OpLoad %20 %34 +OpStore %35 %38 +%26 = OpLoad %20 %35 +OpStore %25 %26 +%27 = OpLoad %20 %25 +%28 = OpLoad %v2float %texCoords +%29 = OpImageSampleImplicitLod %v4float %27 %28 +OpStore %outColor %29 +OpReturn +OpFunctionEnd +)"; + + const std::string post_defs = + R"(%foo_ = OpFunction %20 None %21 +%30 = OpLabel +%31 = OpVariable %_ptr_Function_20 Function +%32 = OpLoad %20 %sampler16 +OpStore %31 %32 +%33 = OpLoad %20 %31 +OpReturnValue %33 +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs + before + post_defs, predefs + after + post_defs, true, true); +} + +TEST_F(InlineOpaqueTest, InlineInNonEntryPointFunction) { + // This demonstrates opaque inlining in a function that is not + // an entry point function (main2) but is in the call tree of an + // entry point function (main). + // TODO(greg-lunarg): Add HLSL code + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %outColor %texCoords +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %main2 "main2" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %foo_struct_S_t_vf2_vf21_ "foo(struct-S_t-vf2-vf21;" +OpName %s "s" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpName %param "param" +OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%13 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%18 = OpTypeImage %float 2D 0 0 0 1 Unknown +%19 = OpTypeSampledImage %18 +%S_t = OpTypeStruct %v2float %v2float %19 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%21 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_19 = OpTypePointer UniformConstant %19 +%_ptr_Function_19 = OpTypePointer Function %19 +%sampler15 = OpVariable %_ptr_UniformConstant_19 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string before = + R"(%main2 = OpFunction %void None %13 +%29 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%30 = OpLoad %v2float %texCoords +%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %31 %30 +%32 = OpLoad %19 %sampler15 +%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2 +OpStore %33 %32 +%34 = OpLoad %S_t %s0 +OpStore %param %34 +%35 = OpFunctionCall %void %foo_struct_S_t_vf2_vf21_ %param +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%35 = OpUndef %void +%main2 = OpFunction %void None %13 +%29 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%30 = OpLoad %v2float %texCoords +%31 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %31 %30 +%32 = OpLoad %19 %sampler15 +%33 = OpAccessChain %_ptr_Function_19 %s0 %int_2 +OpStore %33 %32 +%34 = OpLoad %S_t %s0 +OpStore %param %34 +%45 = OpAccessChain %_ptr_Function_19 %param %int_2 +%46 = OpLoad %19 %45 +%47 = OpAccessChain %_ptr_Function_v2float %param %int_0 +%48 = OpLoad %v2float %47 +%49 = OpImageSampleImplicitLod %v4float %46 %48 +OpStore %outColor %49 +OpReturn +OpFunctionEnd +)"; + + const std::string post_defs = + R"(%main = OpFunction %void None %13 +%36 = OpLabel +%37 = OpFunctionCall %void %main2 +OpReturn +OpFunctionEnd +%foo_struct_S_t_vf2_vf21_ = OpFunction %void None %21 +%s = OpFunctionParameter %_ptr_Function_S_t +%38 = OpLabel +%39 = OpAccessChain %_ptr_Function_19 %s %int_2 +%40 = OpLoad %19 %39 +%41 = OpAccessChain %_ptr_Function_v2float %s %int_0 +%42 = OpLoad %v2float %41 +%43 = OpImageSampleImplicitLod %v4float %40 %42 +OpStore %outColor %43 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs + before + post_defs, predefs + after + post_defs, true, true); +} + +TEST_F(InlineOpaqueTest, NoInlineNoOpaque) { + // Function without opaque interface is not inlined. + // #version 140 + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // return bar.x + bar.y; + // } + // + // void main() + // { + // vec4 color = vec4(foo(BaseColor)); + // gl_FragColor = color; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_vf4_ "foo(vf4;" +OpName %bar "bar" +OpName %color "color" +OpName %BaseColor "BaseColor" +OpName %param "param" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %float %_ptr_Function_v4float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %10 +%21 = OpLabel +%color = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %BaseColor +OpStore %param %22 +%23 = OpFunctionCall %float %foo_vf4_ %param +%24 = OpCompositeConstruct %v4float %23 %23 %23 %23 +OpStore %color %24 +%25 = OpLoad %v4float %color +OpStore %gl_FragColor %25 +OpReturn +OpFunctionEnd +%foo_vf4_ = OpFunction %float None %14 +%bar = OpFunctionParameter %_ptr_Function_v4float +%26 = OpLabel +%27 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%28 = OpLoad %float %27 +%29 = OpAccessChain %_ptr_Function_float %bar %uint_1 +%30 = OpLoad %float %29 +%31 = OpFAdd %float %28 %30 +OpReturnValue %31 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/inline_test.cpp b/third_party/spirv-tools/test/opt/inline_test.cpp new file mode 100644 index 0000000..c0ca6da --- /dev/null +++ b/third_party/spirv-tools/test/opt/inline_test.cpp @@ -0,0 +1,3945 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InlineTest = PassTest<::testing::Test>; + +TEST_F(InlineTest, Simple) { + // #version 140 + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // return bar.x + bar.y; + // } + // + // void main() + // { + // vec4 color = vec4(foo(BaseColor)); + // gl_FragColor = color; + // } + const std::vector predefs = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpSource GLSL 140", + "OpName %main \"main\"", + "OpName %foo_vf4_ \"foo(vf4;\"", + "OpName %bar \"bar\"", + "OpName %color \"color\"", + "OpName %BaseColor \"BaseColor\"", + "OpName %param \"param\"", + "OpName %gl_FragColor \"gl_FragColor\"", + "%void = OpTypeVoid", + "%10 = OpTypeFunction %void", + "%float = OpTypeFloat 32", + "%v4float = OpTypeVector %float 4", +"%_ptr_Function_v4float = OpTypePointer Function %v4float", + "%14 = OpTypeFunction %float %_ptr_Function_v4float", + "%uint = OpTypeInt 32 0", + "%uint_0 = OpConstant %uint 0", +"%_ptr_Function_float = OpTypePointer Function %float", + "%uint_1 = OpConstant %uint 1", +"%_ptr_Input_v4float = OpTypePointer Input %v4float", + "%BaseColor = OpVariable %_ptr_Input_v4float Input", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", +"%gl_FragColor = OpVariable %_ptr_Output_v4float Output", + // clang-format on + }; + + const std::vector nonEntryFuncs = { + // clang-format off + "%foo_vf4_ = OpFunction %float None %14", + "%bar = OpFunctionParameter %_ptr_Function_v4float", + "%26 = OpLabel", + "%27 = OpAccessChain %_ptr_Function_float %bar %uint_0", + "%28 = OpLoad %float %27", + "%29 = OpAccessChain %_ptr_Function_float %bar %uint_1", + "%30 = OpLoad %float %29", + "%31 = OpFAdd %float %28 %30", + "OpReturnValue %31", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector before = { + // clang-format off + "%main = OpFunction %void None %10", + "%21 = OpLabel", + "%color = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%22 = OpLoad %v4float %BaseColor", + "OpStore %param %22", + "%23 = OpFunctionCall %float %foo_vf4_ %param", + "%24 = OpCompositeConstruct %v4float %23 %23 %23 %23", + "OpStore %color %24", + "%25 = OpLoad %v4float %color", + "OpStore %gl_FragColor %25", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector after = { + // clang-format off + "%main = OpFunction %void None %10", + "%21 = OpLabel", + "%32 = OpVariable %_ptr_Function_float Function", + "%color = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%22 = OpLoad %v4float %BaseColor", + "OpStore %param %22", + "%34 = OpAccessChain %_ptr_Function_float %param %uint_0", + "%35 = OpLoad %float %34", + "%36 = OpAccessChain %_ptr_Function_float %param %uint_1", + "%37 = OpLoad %float %36", + "%38 = OpFAdd %float %35 %37", + "OpStore %32 %38", + "%23 = OpLoad %float %32", + "%24 = OpCompositeConstruct %v4float %23 %23 %23 %23", + "OpStore %color %24", + "%25 = OpLoad %v4float %color", + "OpStore %gl_FragColor %25", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)), + JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)), + /* skip_nop = */ false, /* do_validate = */ true); +} + +TEST_F(InlineTest, Nested) { + // #version 140 + // + // in vec4 BaseColor; + // + // float foo2(float f, float f2) + // { + // return f * f2; + // } + // + // float foo(vec4 bar) + // { + // return foo2(bar.x + bar.y, bar.z); + // } + // + // void main() + // { + // vec4 color = vec4(foo(BaseColor)); + // gl_FragColor = color; + // } + const std::vector predefs = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpSource GLSL 140", + "OpName %main \"main\"", + "OpName %foo2_f1_f1_ \"foo2(f1;f1;\"", + "OpName %f \"f\"", + "OpName %f2 \"f2\"", + "OpName %foo_vf4_ \"foo(vf4;\"", + "OpName %bar \"bar\"", + "OpName %param \"param\"", + "OpName %param_0 \"param\"", + "OpName %color \"color\"", + "OpName %BaseColor \"BaseColor\"", + "OpName %param_1 \"param\"", + "OpName %gl_FragColor \"gl_FragColor\"", + "%void = OpTypeVoid", + "%15 = OpTypeFunction %void", + "%float = OpTypeFloat 32", +"%_ptr_Function_float = OpTypePointer Function %float", + "%18 = OpTypeFunction %float %_ptr_Function_float %_ptr_Function_float", + "%v4float = OpTypeVector %float 4", +"%_ptr_Function_v4float = OpTypePointer Function %v4float", + "%21 = OpTypeFunction %float %_ptr_Function_v4float", + "%uint = OpTypeInt 32 0", + "%uint_0 = OpConstant %uint 0", + "%uint_1 = OpConstant %uint 1", + "%uint_2 = OpConstant %uint 2", +"%_ptr_Input_v4float = OpTypePointer Input %v4float", + "%BaseColor = OpVariable %_ptr_Input_v4float Input", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", +"%gl_FragColor = OpVariable %_ptr_Output_v4float Output", + // clang-format on + }; + + const std::vector nonEntryFuncs = { + // clang-format off +"%foo2_f1_f1_ = OpFunction %float None %18", + "%f = OpFunctionParameter %_ptr_Function_float", + "%f2 = OpFunctionParameter %_ptr_Function_float", + "%33 = OpLabel", + "%34 = OpLoad %float %f", + "%35 = OpLoad %float %f2", + "%36 = OpFMul %float %34 %35", + "OpReturnValue %36", + "OpFunctionEnd", + "%foo_vf4_ = OpFunction %float None %21", + "%bar = OpFunctionParameter %_ptr_Function_v4float", + "%37 = OpLabel", + "%param = OpVariable %_ptr_Function_float Function", + "%param_0 = OpVariable %_ptr_Function_float Function", + "%38 = OpAccessChain %_ptr_Function_float %bar %uint_0", + "%39 = OpLoad %float %38", + "%40 = OpAccessChain %_ptr_Function_float %bar %uint_1", + "%41 = OpLoad %float %40", + "%42 = OpFAdd %float %39 %41", + "OpStore %param %42", + "%43 = OpAccessChain %_ptr_Function_float %bar %uint_2", + "%44 = OpLoad %float %43", + "OpStore %param_0 %44", + "%45 = OpFunctionCall %float %foo2_f1_f1_ %param %param_0", + "OpReturnValue %45", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector before = { + // clang-format off + "%main = OpFunction %void None %15", + "%28 = OpLabel", + "%color = OpVariable %_ptr_Function_v4float Function", + "%param_1 = OpVariable %_ptr_Function_v4float Function", + "%29 = OpLoad %v4float %BaseColor", + "OpStore %param_1 %29", + "%30 = OpFunctionCall %float %foo_vf4_ %param_1", + "%31 = OpCompositeConstruct %v4float %30 %30 %30 %30", + "OpStore %color %31", + "%32 = OpLoad %v4float %color", + "OpStore %gl_FragColor %32", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector after = { + // clang-format off + "%main = OpFunction %void None %15", + "%28 = OpLabel", + "%58 = OpVariable %_ptr_Function_float Function", + "%46 = OpVariable %_ptr_Function_float Function", + "%47 = OpVariable %_ptr_Function_float Function", + "%48 = OpVariable %_ptr_Function_float Function", + "%color = OpVariable %_ptr_Function_v4float Function", + "%param_1 = OpVariable %_ptr_Function_v4float Function", + "%29 = OpLoad %v4float %BaseColor", + "OpStore %param_1 %29", + "%50 = OpAccessChain %_ptr_Function_float %param_1 %uint_0", + "%51 = OpLoad %float %50", + "%52 = OpAccessChain %_ptr_Function_float %param_1 %uint_1", + "%53 = OpLoad %float %52", + "%54 = OpFAdd %float %51 %53", + "OpStore %46 %54", + "%55 = OpAccessChain %_ptr_Function_float %param_1 %uint_2", + "%56 = OpLoad %float %55", + "OpStore %47 %56", + "%60 = OpLoad %float %46", + "%61 = OpLoad %float %47", + "%62 = OpFMul %float %60 %61", + "OpStore %58 %62", + "%57 = OpLoad %float %58", + "OpStore %48 %57", + "%30 = OpLoad %float %48", + "%31 = OpCompositeConstruct %v4float %30 %30 %30 %30", + "OpStore %color %31", + "%32 = OpLoad %v4float %color", + "OpStore %gl_FragColor %32", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)), + JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)), + /* skip_nop = */ false, /* do_validate = */ true); +} + +TEST_F(InlineTest, InOutParameter) { + // #version 400 + // + // in vec4 Basecolor; + // + // void foo(inout vec4 bar) + // { + // bar.z = bar.x + bar.y; + // } + // + // void main() + // { + // vec4 b = Basecolor; + // foo(b); + // vec4 color = vec4(b.z); + // gl_FragColor = color; + // } + const std::vector predefs = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %Basecolor %gl_FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpSource GLSL 400", + "OpName %main \"main\"", + "OpName %foo_vf4_ \"foo(vf4;\"", + "OpName %bar \"bar\"", + "OpName %b \"b\"", + "OpName %Basecolor \"Basecolor\"", + "OpName %param \"param\"", + "OpName %color \"color\"", + "OpName %gl_FragColor \"gl_FragColor\"", + "%void = OpTypeVoid", + "%11 = OpTypeFunction %void", + "%float = OpTypeFloat 32", + "%v4float = OpTypeVector %float 4", +"%_ptr_Function_v4float = OpTypePointer Function %v4float", + "%15 = OpTypeFunction %void %_ptr_Function_v4float", + "%uint = OpTypeInt 32 0", + "%uint_0 = OpConstant %uint 0", +"%_ptr_Function_float = OpTypePointer Function %float", + "%uint_1 = OpConstant %uint 1", + "%uint_2 = OpConstant %uint 2", +"%_ptr_Input_v4float = OpTypePointer Input %v4float", + "%Basecolor = OpVariable %_ptr_Input_v4float Input", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", +"%gl_FragColor = OpVariable %_ptr_Output_v4float Output", + // clang-format on + }; + + const std::vector nonEntryFuncs = { + // clang-format off + "%foo_vf4_ = OpFunction %void None %15", + "%bar = OpFunctionParameter %_ptr_Function_v4float", + "%32 = OpLabel", + "%33 = OpAccessChain %_ptr_Function_float %bar %uint_0", + "%34 = OpLoad %float %33", + "%35 = OpAccessChain %_ptr_Function_float %bar %uint_1", + "%36 = OpLoad %float %35", + "%37 = OpFAdd %float %34 %36", + "%38 = OpAccessChain %_ptr_Function_float %bar %uint_2", + "OpStore %38 %37", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector before = { + // clang-format off + "%main = OpFunction %void None %11", + "%23 = OpLabel", + "%b = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%color = OpVariable %_ptr_Function_v4float Function", + "%24 = OpLoad %v4float %Basecolor", + "OpStore %b %24", + "%25 = OpLoad %v4float %b", + "OpStore %param %25", + "%26 = OpFunctionCall %void %foo_vf4_ %param", + "%27 = OpLoad %v4float %param", + "OpStore %b %27", + "%28 = OpAccessChain %_ptr_Function_float %b %uint_2", + "%29 = OpLoad %float %28", + "%30 = OpCompositeConstruct %v4float %29 %29 %29 %29", + "OpStore %color %30", + "%31 = OpLoad %v4float %color", + "OpStore %gl_FragColor %31", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector after = { + // clang-format off + "%26 = OpUndef %void", + "%main = OpFunction %void None %11", + "%23 = OpLabel", + "%b = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%color = OpVariable %_ptr_Function_v4float Function", + "%24 = OpLoad %v4float %Basecolor", + "OpStore %b %24", + "%25 = OpLoad %v4float %b", + "OpStore %param %25", + "%40 = OpAccessChain %_ptr_Function_float %param %uint_0", + "%41 = OpLoad %float %40", + "%42 = OpAccessChain %_ptr_Function_float %param %uint_1", + "%43 = OpLoad %float %42", + "%44 = OpFAdd %float %41 %43", + "%45 = OpAccessChain %_ptr_Function_float %param %uint_2", + "OpStore %45 %44", + "%27 = OpLoad %v4float %param", + "OpStore %b %27", + "%28 = OpAccessChain %_ptr_Function_float %b %uint_2", + "%29 = OpLoad %float %28", + "%30 = OpCompositeConstruct %v4float %29 %29 %29 %29", + "OpStore %color %30", + "%31 = OpLoad %v4float %color", + "OpStore %gl_FragColor %31", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)), + JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)), + /* skip_nop = */ false, /* do_validate = */ true); +} + +TEST_F(InlineTest, BranchInCallee) { + // #version 140 + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // float r = bar.x; + // if (r < 0.0) + // r = -r; + // return r; + // } + // + // void main() + // { + // vec4 color = vec4(foo(BaseColor)); + // + // gl_FragColor = color; + // } + const std::vector predefs = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpSource GLSL 140", + "OpName %main \"main\"", + "OpName %foo_vf4_ \"foo(vf4;\"", + "OpName %bar \"bar\"", + "OpName %r \"r\"", + "OpName %color \"color\"", + "OpName %BaseColor \"BaseColor\"", + "OpName %param \"param\"", + "OpName %gl_FragColor \"gl_FragColor\"", + "%void = OpTypeVoid", + "%11 = OpTypeFunction %void", + "%float = OpTypeFloat 32", + "%v4float = OpTypeVector %float 4", +"%_ptr_Function_v4float = OpTypePointer Function %v4float", + "%15 = OpTypeFunction %float %_ptr_Function_v4float", +"%_ptr_Function_float = OpTypePointer Function %float", + "%uint = OpTypeInt 32 0", + "%uint_0 = OpConstant %uint 0", + "%float_0 = OpConstant %float 0", + "%bool = OpTypeBool", +"%_ptr_Input_v4float = OpTypePointer Input %v4float", + "%BaseColor = OpVariable %_ptr_Input_v4float Input", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", +"%gl_FragColor = OpVariable %_ptr_Output_v4float Output", + // clang-format on + }; + + const std::vector nonEntryFuncs = { + // clang-format off + "%foo_vf4_ = OpFunction %float None %15", + "%bar = OpFunctionParameter %_ptr_Function_v4float", + "%28 = OpLabel", + "%r = OpVariable %_ptr_Function_float Function", + "%29 = OpAccessChain %_ptr_Function_float %bar %uint_0", + "%30 = OpLoad %float %29", + "OpStore %r %30", + "%31 = OpLoad %float %r", + "%32 = OpFOrdLessThan %bool %31 %float_0", + "OpSelectionMerge %33 None", + "OpBranchConditional %32 %34 %33", + "%34 = OpLabel", + "%35 = OpLoad %float %r", + "%36 = OpFNegate %float %35", + "OpStore %r %36", + "OpBranch %33", + "%33 = OpLabel", + "%37 = OpLoad %float %r", + "OpReturnValue %37", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector before = { + // clang-format off + "%main = OpFunction %void None %11", + "%23 = OpLabel", + "%color = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%24 = OpLoad %v4float %BaseColor", + "OpStore %param %24", + "%25 = OpFunctionCall %float %foo_vf4_ %param", + "%26 = OpCompositeConstruct %v4float %25 %25 %25 %25", + "OpStore %color %26", + "%27 = OpLoad %v4float %color", + "OpStore %gl_FragColor %27", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector after = { + // clang-format off + "%main = OpFunction %void None %11", + "%23 = OpLabel", + "%38 = OpVariable %_ptr_Function_float Function", + "%39 = OpVariable %_ptr_Function_float Function", + "%color = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%24 = OpLoad %v4float %BaseColor", + "OpStore %param %24", + "%41 = OpAccessChain %_ptr_Function_float %param %uint_0", + "%42 = OpLoad %float %41", + "OpStore %38 %42", + "%43 = OpLoad %float %38", + "%44 = OpFOrdLessThan %bool %43 %float_0", + "OpSelectionMerge %48 None", + "OpBranchConditional %44 %45 %48", + "%45 = OpLabel", + "%46 = OpLoad %float %38", + "%47 = OpFNegate %float %46", + "OpStore %38 %47", + "OpBranch %48", + "%48 = OpLabel", + "%49 = OpLoad %float %38", + "OpStore %39 %49", + "%25 = OpLoad %float %39", + "%26 = OpCompositeConstruct %v4float %25 %25 %25 %25", + "OpStore %color %26", + "%27 = OpLoad %v4float %color", + "OpStore %gl_FragColor %27", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)), + JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)), + /* skip_nop = */ false, /* do_validate = */ true); +} + +TEST_F(InlineTest, PhiAfterCall) { + // #version 140 + // + // in vec4 BaseColor; + // + // float foo(float bar) + // { + // float r = bar; + // if (r < 0.0) + // r = -r; + // return r; + // } + // + // void main() + // { + // vec4 color = BaseColor; + // if (foo(color.x) > 2.0 && foo(color.y) > 2.0) + // color = vec4(0.0); + // gl_FragColor = color; + // } + const std::vector predefs = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpSource GLSL 140", + "OpName %main \"main\"", + "OpName %foo_f1_ \"foo(f1;\"", + "OpName %bar \"bar\"", + "OpName %r \"r\"", + "OpName %color \"color\"", + "OpName %BaseColor \"BaseColor\"", + "OpName %param \"param\"", + "OpName %param_0 \"param\"", + "OpName %gl_FragColor \"gl_FragColor\"", + "%void = OpTypeVoid", + "%12 = OpTypeFunction %void", + "%float = OpTypeFloat 32", +"%_ptr_Function_float = OpTypePointer Function %float", + "%15 = OpTypeFunction %float %_ptr_Function_float", + "%float_0 = OpConstant %float 0", + "%bool = OpTypeBool", + "%v4float = OpTypeVector %float 4", +"%_ptr_Function_v4float = OpTypePointer Function %v4float", +"%_ptr_Input_v4float = OpTypePointer Input %v4float", + "%BaseColor = OpVariable %_ptr_Input_v4float Input", + "%uint = OpTypeInt 32 0", + "%uint_0 = OpConstant %uint 0", + "%float_2 = OpConstant %float 2", + "%uint_1 = OpConstant %uint 1", + "%25 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", +"%gl_FragColor = OpVariable %_ptr_Output_v4float Output", + // clang-format on + }; + + const std::vector nonEntryFuncs = { + // clang-format off + "%foo_f1_ = OpFunction %float None %15", + "%bar = OpFunctionParameter %_ptr_Function_float", + "%43 = OpLabel", + "%r = OpVariable %_ptr_Function_float Function", + "%44 = OpLoad %float %bar", + "OpStore %r %44", + "%45 = OpLoad %float %r", + "%46 = OpFOrdLessThan %bool %45 %float_0", + "OpSelectionMerge %47 None", + "OpBranchConditional %46 %48 %47", + "%48 = OpLabel", + "%49 = OpLoad %float %r", + "%50 = OpFNegate %float %49", + "OpStore %r %50", + "OpBranch %47", + "%47 = OpLabel", + "%51 = OpLoad %float %r", + "OpReturnValue %51", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector before = { + // clang-format off + "%main = OpFunction %void None %12", + "%27 = OpLabel", + "%color = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_float Function", + "%param_0 = OpVariable %_ptr_Function_float Function", + "%28 = OpLoad %v4float %BaseColor", + "OpStore %color %28", + "%29 = OpAccessChain %_ptr_Function_float %color %uint_0", + "%30 = OpLoad %float %29", + "OpStore %param %30", + "%31 = OpFunctionCall %float %foo_f1_ %param", + "%32 = OpFOrdGreaterThan %bool %31 %float_2", + "OpSelectionMerge %33 None", + "OpBranchConditional %32 %34 %33", + "%34 = OpLabel", + "%35 = OpAccessChain %_ptr_Function_float %color %uint_1", + "%36 = OpLoad %float %35", + "OpStore %param_0 %36", + "%37 = OpFunctionCall %float %foo_f1_ %param_0", + "%38 = OpFOrdGreaterThan %bool %37 %float_2", + "OpBranch %33", + "%33 = OpLabel", + "%39 = OpPhi %bool %32 %27 %38 %34", + "OpSelectionMerge %40 None", + "OpBranchConditional %39 %41 %40", + "%41 = OpLabel", + "OpStore %color %25", + "OpBranch %40", + "%40 = OpLabel", + "%42 = OpLoad %v4float %color", + "OpStore %gl_FragColor %42", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector after = { + // clang-format off + "%main = OpFunction %void None %12", + "%27 = OpLabel", + "%63 = OpVariable %_ptr_Function_float Function", + "%64 = OpVariable %_ptr_Function_float Function", + "%52 = OpVariable %_ptr_Function_float Function", + "%53 = OpVariable %_ptr_Function_float Function", + "%color = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_float Function", + "%param_0 = OpVariable %_ptr_Function_float Function", + "%28 = OpLoad %v4float %BaseColor", + "OpStore %color %28", + "%29 = OpAccessChain %_ptr_Function_float %color %uint_0", + "%30 = OpLoad %float %29", + "OpStore %param %30", + "%55 = OpLoad %float %param", + "OpStore %52 %55", + "%56 = OpLoad %float %52", + "%57 = OpFOrdLessThan %bool %56 %float_0", + "OpSelectionMerge %61 None", + "OpBranchConditional %57 %58 %61", + "%58 = OpLabel", + "%59 = OpLoad %float %52", + "%60 = OpFNegate %float %59", + "OpStore %52 %60", + "OpBranch %61", + "%61 = OpLabel", + "%62 = OpLoad %float %52", + "OpStore %53 %62", + "%31 = OpLoad %float %53", + "%32 = OpFOrdGreaterThan %bool %31 %float_2", + "OpSelectionMerge %33 None", + "OpBranchConditional %32 %34 %33", + "%34 = OpLabel", + "%35 = OpAccessChain %_ptr_Function_float %color %uint_1", + "%36 = OpLoad %float %35", + "OpStore %param_0 %36", + "%66 = OpLoad %float %param_0", + "OpStore %63 %66", + "%67 = OpLoad %float %63", + "%68 = OpFOrdLessThan %bool %67 %float_0", + "OpSelectionMerge %72 None", + "OpBranchConditional %68 %69 %72", + "%69 = OpLabel", + "%70 = OpLoad %float %63", + "%71 = OpFNegate %float %70", + "OpStore %63 %71", + "OpBranch %72", + "%72 = OpLabel", + "%73 = OpLoad %float %63", + "OpStore %64 %73", + "%37 = OpLoad %float %64", + "%38 = OpFOrdGreaterThan %bool %37 %float_2", + "OpBranch %33", + "%33 = OpLabel", + "%39 = OpPhi %bool %32 %61 %38 %72", + "OpSelectionMerge %40 None", + "OpBranchConditional %39 %41 %40", + "%41 = OpLabel", + "OpStore %color %25", + "OpBranch %40", + "%40 = OpLabel", + "%42 = OpLoad %v4float %color", + "OpStore %gl_FragColor %42", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)), + JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)), + /* skip_nop = */ false, /* do_validate = */ true); +} + +TEST_F(InlineTest, OpSampledImageOutOfBlock) { + // #version 450 + // + // uniform texture2D t2D; + // uniform sampler samp; + // out vec4 FragColor; + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // float r = bar.x; + // if (r < 0.0) + // r = -r; + // return r; + // } + // + // void main() + // { + // vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0)); + // vec4 color2 = vec4(foo(BaseColor)); + // vec4 color3 = texture(sampler2D(t2D, samp), vec2(0.5)); + // FragColor = (color1 + color2 + color3)/3; + // } + // + // Note: the before SPIR-V will need to be edited to create a use of + // the OpSampledImage across the function call. + const std::vector predefs = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpSource GLSL 450", + "OpName %main \"main\"", + "OpName %foo_vf4_ \"foo(vf4;\"", + "OpName %bar \"bar\"", + "OpName %r \"r\"", + "OpName %color1 \"color1\"", + "OpName %t2D \"t2D\"", + "OpName %samp \"samp\"", + "OpName %color2 \"color2\"", + "OpName %BaseColor \"BaseColor\"", + "OpName %param \"param\"", + "OpName %color3 \"color3\"", + "OpName %FragColor \"FragColor\"", + "OpDecorate %t2D DescriptorSet 0", + "OpDecorate %samp DescriptorSet 0", + "%void = OpTypeVoid", + "%15 = OpTypeFunction %void", + "%float = OpTypeFloat 32", + "%v4float = OpTypeVector %float 4", +"%_ptr_Function_v4float = OpTypePointer Function %v4float", + "%19 = OpTypeFunction %float %_ptr_Function_v4float", +"%_ptr_Function_float = OpTypePointer Function %float", + "%uint = OpTypeInt 32 0", + "%uint_0 = OpConstant %uint 0", + "%float_0 = OpConstant %float 0", + "%bool = OpTypeBool", + "%25 = OpTypeImage %float 2D 0 0 0 1 Unknown", +"%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25", + "%t2D = OpVariable %_ptr_UniformConstant_25 UniformConstant", + "%27 = OpTypeSampler", +"%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27", + "%samp = OpVariable %_ptr_UniformConstant_27 UniformConstant", + "%29 = OpTypeSampledImage %25", + "%v2float = OpTypeVector %float 2", + "%float_1 = OpConstant %float 1", + "%32 = OpConstantComposite %v2float %float_1 %float_1", +"%_ptr_Input_v4float = OpTypePointer Input %v4float", + "%BaseColor = OpVariable %_ptr_Input_v4float Input", + "%float_0_5 = OpConstant %float 0.5", + "%35 = OpConstantComposite %v2float %float_0_5 %float_0_5", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", + "%FragColor = OpVariable %_ptr_Output_v4float Output", + "%float_3 = OpConstant %float 3", + // clang-format on + }; + + const std::vector nonEntryFuncs = { + // clang-format off + "%foo_vf4_ = OpFunction %float None %19", + "%bar = OpFunctionParameter %_ptr_Function_v4float", + "%56 = OpLabel", + "%r = OpVariable %_ptr_Function_float Function", + "%57 = OpAccessChain %_ptr_Function_float %bar %uint_0", + "%58 = OpLoad %float %57", + "OpStore %r %58", + "%59 = OpLoad %float %r", + "%60 = OpFOrdLessThan %bool %59 %float_0", + "OpSelectionMerge %61 None", + "OpBranchConditional %60 %62 %61", + "%62 = OpLabel", + "%63 = OpLoad %float %r", + "%64 = OpFNegate %float %63", + "OpStore %r %64", + "OpBranch %61", + "%61 = OpLabel", + "%65 = OpLoad %float %r", + "OpReturnValue %65", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector before = { + // clang-format off + "%main = OpFunction %void None %15", + "%38 = OpLabel", + "%color1 = OpVariable %_ptr_Function_v4float Function", + "%color2 = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%color3 = OpVariable %_ptr_Function_v4float Function", + "%39 = OpLoad %25 %t2D", + "%40 = OpLoad %27 %samp", + "%41 = OpSampledImage %29 %39 %40", + "%42 = OpImageSampleImplicitLod %v4float %41 %32", + "OpStore %color1 %42", + "%43 = OpLoad %v4float %BaseColor", + "OpStore %param %43", + "%44 = OpFunctionCall %float %foo_vf4_ %param", + "%45 = OpCompositeConstruct %v4float %44 %44 %44 %44", + "OpStore %color2 %45", + "%46 = OpLoad %25 %t2D", + "%47 = OpLoad %27 %samp", + "%48 = OpImageSampleImplicitLod %v4float %41 %35", + "OpStore %color3 %48", + "%49 = OpLoad %v4float %color1", + "%50 = OpLoad %v4float %color2", + "%51 = OpFAdd %v4float %49 %50", + "%52 = OpLoad %v4float %color3", + "%53 = OpFAdd %v4float %51 %52", + "%54 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3", + "%55 = OpFDiv %v4float %53 %54", + "OpStore %FragColor %55", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector after = { + // clang-format off + "%main = OpFunction %void None %15", + "%38 = OpLabel", + "%66 = OpVariable %_ptr_Function_float Function", + "%67 = OpVariable %_ptr_Function_float Function", + "%color1 = OpVariable %_ptr_Function_v4float Function", + "%color2 = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%color3 = OpVariable %_ptr_Function_v4float Function", + "%39 = OpLoad %25 %t2D", + "%40 = OpLoad %27 %samp", + "%41 = OpSampledImage %29 %39 %40", + "%42 = OpImageSampleImplicitLod %v4float %41 %32", + "OpStore %color1 %42", + "%43 = OpLoad %v4float %BaseColor", + "OpStore %param %43", + "%69 = OpAccessChain %_ptr_Function_float %param %uint_0", + "%70 = OpLoad %float %69", + "OpStore %66 %70", + "%71 = OpLoad %float %66", + "%72 = OpFOrdLessThan %bool %71 %float_0", + "OpSelectionMerge %76 None", + "OpBranchConditional %72 %73 %76", + "%73 = OpLabel", + "%74 = OpLoad %float %66", + "%75 = OpFNegate %float %74", + "OpStore %66 %75", + "OpBranch %76", + "%76 = OpLabel", + "%77 = OpLoad %float %66", + "OpStore %67 %77", + "%44 = OpLoad %float %67", + "%45 = OpCompositeConstruct %v4float %44 %44 %44 %44", + "OpStore %color2 %45", + "%46 = OpLoad %25 %t2D", + "%47 = OpLoad %27 %samp", + "%78 = OpSampledImage %29 %39 %40", + "%48 = OpImageSampleImplicitLod %v4float %78 %35", + "OpStore %color3 %48", + "%49 = OpLoad %v4float %color1", + "%50 = OpLoad %v4float %color2", + "%51 = OpFAdd %v4float %49 %50", + "%52 = OpLoad %v4float %color3", + "%53 = OpFAdd %v4float %51 %52", + "%54 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3", + "%55 = OpFDiv %v4float %53 %54", + "OpStore %FragColor %55", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)), + JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)), + /* skip_nop = */ false, /* do_validate = */ true); +} + +TEST_F(InlineTest, OpImageOutOfBlock) { + // #version 450 + // + // uniform texture2D t2D; + // uniform sampler samp; + // uniform sampler samp2; + // + // out vec4 FragColor; + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // float r = bar.x; + // if (r < 0.0) + // r = -r; + // return r; + // } + // + // void main() + // { + // vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0)); + // vec4 color2 = vec4(foo(BaseColor)); + // vec4 color3 = texture(sampler2D(t2D, samp2), vec2(0.5)); + // FragColor = (color1 + color2 + color3)/3; + // } + // Note: the before SPIR-V will need to be edited to create an OpImage + // from the first OpSampledImage, place it before the call and use it + // in the second OpSampledImage following the call. + const std::vector predefs = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpSource GLSL 450", + "OpName %main \"main\"", + "OpName %foo_vf4_ \"foo(vf4;\"", + "OpName %bar \"bar\"", + "OpName %r \"r\"", + "OpName %color1 \"color1\"", + "OpName %t2D \"t2D\"", + "OpName %samp \"samp\"", + "OpName %color2 \"color2\"", + "OpName %BaseColor \"BaseColor\"", + "OpName %param \"param\"", + "OpName %color3 \"color3\"", + "OpName %samp2 \"samp2\"", + "OpName %FragColor \"FragColor\"", + "OpDecorate %t2D DescriptorSet 0", + "OpDecorate %samp DescriptorSet 0", + "OpDecorate %samp2 DescriptorSet 0", + "%void = OpTypeVoid", + "%16 = OpTypeFunction %void", + "%float = OpTypeFloat 32", + "%v4float = OpTypeVector %float 4", +"%_ptr_Function_v4float = OpTypePointer Function %v4float", + "%20 = OpTypeFunction %float %_ptr_Function_v4float", +"%_ptr_Function_float = OpTypePointer Function %float", + "%uint = OpTypeInt 32 0", + "%uint_0 = OpConstant %uint 0", + "%float_0 = OpConstant %float 0", + "%bool = OpTypeBool", + "%26 = OpTypeImage %float 2D 0 0 0 1 Unknown", +"%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26", + "%t2D = OpVariable %_ptr_UniformConstant_26 UniformConstant", + "%28 = OpTypeSampler", +"%_ptr_UniformConstant_28 = OpTypePointer UniformConstant %28", + "%samp = OpVariable %_ptr_UniformConstant_28 UniformConstant", + "%30 = OpTypeSampledImage %26", + "%v2float = OpTypeVector %float 2", + "%float_1 = OpConstant %float 1", + "%33 = OpConstantComposite %v2float %float_1 %float_1", +"%_ptr_Input_v4float = OpTypePointer Input %v4float", + "%BaseColor = OpVariable %_ptr_Input_v4float Input", + "%samp2 = OpVariable %_ptr_UniformConstant_28 UniformConstant", + "%float_0_5 = OpConstant %float 0.5", + "%36 = OpConstantComposite %v2float %float_0_5 %float_0_5", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", + "%FragColor = OpVariable %_ptr_Output_v4float Output", + "%float_3 = OpConstant %float 3", + // clang-format on + }; + + const std::vector nonEntryFuncs = { + // clang-format off + "%foo_vf4_ = OpFunction %float None %20", + "%bar = OpFunctionParameter %_ptr_Function_v4float", + "%58 = OpLabel", + "%r = OpVariable %_ptr_Function_float Function", + "%59 = OpAccessChain %_ptr_Function_float %bar %uint_0", + "%60 = OpLoad %float %59", + "OpStore %r %60", + "%61 = OpLoad %float %r", + "%62 = OpFOrdLessThan %bool %61 %float_0", + "OpSelectionMerge %63 None", + "OpBranchConditional %62 %64 %63", + "%64 = OpLabel", + "%65 = OpLoad %float %r", + "%66 = OpFNegate %float %65", + "OpStore %r %66", + "OpBranch %63", + "%63 = OpLabel", + "%67 = OpLoad %float %r", + "OpReturnValue %67", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector before = { + // clang-format off + "%main = OpFunction %void None %16", + "%39 = OpLabel", + "%color1 = OpVariable %_ptr_Function_v4float Function", + "%color2 = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%color3 = OpVariable %_ptr_Function_v4float Function", + "%40 = OpLoad %26 %t2D", + "%41 = OpLoad %28 %samp", + "%42 = OpSampledImage %30 %40 %41", + "%43 = OpImageSampleImplicitLod %v4float %42 %33", + "%44 = OpImage %26 %42", + "%45 = OpLoad %28 %samp2", + "OpStore %color1 %43", + "%46 = OpLoad %v4float %BaseColor", + "OpStore %param %46", + "%47 = OpFunctionCall %float %foo_vf4_ %param", + "%48 = OpCompositeConstruct %v4float %47 %47 %47 %47", + "OpStore %color2 %48", + "%49 = OpSampledImage %30 %44 %45", + "%50 = OpImageSampleImplicitLod %v4float %49 %36", + "OpStore %color3 %50", + "%51 = OpLoad %v4float %color1", + "%52 = OpLoad %v4float %color2", + "%53 = OpFAdd %v4float %51 %52", + "%54 = OpLoad %v4float %color3", + "%55 = OpFAdd %v4float %53 %54", + "%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3", + "%57 = OpFDiv %v4float %55 %56", + "OpStore %FragColor %57", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector after = { + // clang-format off + "%main = OpFunction %void None %16", + "%39 = OpLabel", + "%68 = OpVariable %_ptr_Function_float Function", + "%69 = OpVariable %_ptr_Function_float Function", + "%color1 = OpVariable %_ptr_Function_v4float Function", + "%color2 = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%color3 = OpVariable %_ptr_Function_v4float Function", + "%40 = OpLoad %26 %t2D", + "%41 = OpLoad %28 %samp", + "%42 = OpSampledImage %30 %40 %41", + "%43 = OpImageSampleImplicitLod %v4float %42 %33", + "%44 = OpImage %26 %42", + "%45 = OpLoad %28 %samp2", + "OpStore %color1 %43", + "%46 = OpLoad %v4float %BaseColor", + "OpStore %param %46", + "%71 = OpAccessChain %_ptr_Function_float %param %uint_0", + "%72 = OpLoad %float %71", + "OpStore %68 %72", + "%73 = OpLoad %float %68", + "%74 = OpFOrdLessThan %bool %73 %float_0", + "OpSelectionMerge %78 None", + "OpBranchConditional %74 %75 %78", + "%75 = OpLabel", + "%76 = OpLoad %float %68", + "%77 = OpFNegate %float %76", + "OpStore %68 %77", + "OpBranch %78", + "%78 = OpLabel", + "%79 = OpLoad %float %68", + "OpStore %69 %79", + "%47 = OpLoad %float %69", + "%48 = OpCompositeConstruct %v4float %47 %47 %47 %47", + "OpStore %color2 %48", + "%80 = OpSampledImage %30 %40 %41", + "%81 = OpImage %26 %80", + "%49 = OpSampledImage %30 %81 %45", + "%50 = OpImageSampleImplicitLod %v4float %49 %36", + "OpStore %color3 %50", + "%51 = OpLoad %v4float %color1", + "%52 = OpLoad %v4float %color2", + "%53 = OpFAdd %v4float %51 %52", + "%54 = OpLoad %v4float %color3", + "%55 = OpFAdd %v4float %53 %54", + "%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3", + "%57 = OpFDiv %v4float %55 %56", + "OpStore %FragColor %57", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)), + JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)), + /* skip_nop = */ false, /* do_validate = */ true); +} + +TEST_F(InlineTest, OpImageAndOpSampledImageOutOfBlock) { + // #version 450 + // + // uniform texture2D t2D; + // uniform sampler samp; + // uniform sampler samp2; + // + // out vec4 FragColor; + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // float r = bar.x; + // if (r < 0.0) + // r = -r; + // return r; + // } + // + // void main() + // { + // vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0)); + // vec4 color2 = vec4(foo(BaseColor)); + // vec4 color3 = texture(sampler2D(t2D, samp2), vec2(0.5)); + // FragColor = (color1 + color2 + color3)/3; + // } + // Note: the before SPIR-V will need to be edited to create an OpImage + // and subsequent OpSampledImage that is used across the function call. + const std::vector predefs = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpSource GLSL 450", + "OpName %main \"main\"", + "OpName %foo_vf4_ \"foo(vf4;\"", + "OpName %bar \"bar\"", + "OpName %r \"r\"", + "OpName %color1 \"color1\"", + "OpName %t2D \"t2D\"", + "OpName %samp \"samp\"", + "OpName %color2 \"color2\"", + "OpName %BaseColor \"BaseColor\"", + "OpName %param \"param\"", + "OpName %color3 \"color3\"", + "OpName %samp2 \"samp2\"", + "OpName %FragColor \"FragColor\"", + "OpDecorate %t2D DescriptorSet 0", + "OpDecorate %samp DescriptorSet 0", + "OpDecorate %samp2 DescriptorSet 0", + "%void = OpTypeVoid", + "%16 = OpTypeFunction %void", + "%float = OpTypeFloat 32", + "%v4float = OpTypeVector %float 4", +"%_ptr_Function_v4float = OpTypePointer Function %v4float", + "%20 = OpTypeFunction %float %_ptr_Function_v4float", +"%_ptr_Function_float = OpTypePointer Function %float", + "%uint = OpTypeInt 32 0", + "%uint_0 = OpConstant %uint 0", + "%float_0 = OpConstant %float 0", + "%bool = OpTypeBool", + "%26 = OpTypeImage %float 2D 0 0 0 1 Unknown", +"%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26", + "%t2D = OpVariable %_ptr_UniformConstant_26 UniformConstant", + "%28 = OpTypeSampler", +"%_ptr_UniformConstant_28 = OpTypePointer UniformConstant %28", + "%samp = OpVariable %_ptr_UniformConstant_28 UniformConstant", + "%30 = OpTypeSampledImage %26", + "%v2float = OpTypeVector %float 2", + "%float_1 = OpConstant %float 1", + "%33 = OpConstantComposite %v2float %float_1 %float_1", +"%_ptr_Input_v4float = OpTypePointer Input %v4float", + "%BaseColor = OpVariable %_ptr_Input_v4float Input", + "%samp2 = OpVariable %_ptr_UniformConstant_28 UniformConstant", + "%float_0_5 = OpConstant %float 0.5", + "%36 = OpConstantComposite %v2float %float_0_5 %float_0_5", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", + "%FragColor = OpVariable %_ptr_Output_v4float Output", + "%float_3 = OpConstant %float 3", + // clang-format on + }; + + const std::vector nonEntryFuncs = { + // clang-format off + "%foo_vf4_ = OpFunction %float None %20", + "%bar = OpFunctionParameter %_ptr_Function_v4float", + "%58 = OpLabel", + "%r = OpVariable %_ptr_Function_float Function", + "%59 = OpAccessChain %_ptr_Function_float %bar %uint_0", + "%60 = OpLoad %float %59", + "OpStore %r %60", + "%61 = OpLoad %float %r", + "%62 = OpFOrdLessThan %bool %61 %float_0", + "OpSelectionMerge %63 None", + "OpBranchConditional %62 %64 %63", + "%64 = OpLabel", + "%65 = OpLoad %float %r", + "%66 = OpFNegate %float %65", + "OpStore %r %66", + "OpBranch %63", + "%63 = OpLabel", + "%67 = OpLoad %float %r", + "OpReturnValue %67", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector before = { + // clang-format off + "%main = OpFunction %void None %16", + "%39 = OpLabel", + "%color1 = OpVariable %_ptr_Function_v4float Function", + "%color2 = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%color3 = OpVariable %_ptr_Function_v4float Function", + "%40 = OpLoad %26 %t2D", + "%41 = OpLoad %28 %samp", + "%42 = OpSampledImage %30 %40 %41", + "%43 = OpImageSampleImplicitLod %v4float %42 %33", + "%44 = OpImage %26 %42", + "%45 = OpLoad %28 %samp2", + "%46 = OpSampledImage %30 %44 %45", + "OpStore %color1 %43", + "%47 = OpLoad %v4float %BaseColor", + "OpStore %param %47", + "%48 = OpFunctionCall %float %foo_vf4_ %param", + "%49 = OpCompositeConstruct %v4float %48 %48 %48 %48", + "OpStore %color2 %49", + "%50 = OpImageSampleImplicitLod %v4float %46 %36", + "OpStore %color3 %50", + "%51 = OpLoad %v4float %color1", + "%52 = OpLoad %v4float %color2", + "%53 = OpFAdd %v4float %51 %52", + "%54 = OpLoad %v4float %color3", + "%55 = OpFAdd %v4float %53 %54", + "%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3", + "%57 = OpFDiv %v4float %55 %56", + "OpStore %FragColor %57", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + const std::vector after = { + // clang-format off + "%main = OpFunction %void None %16", + "%39 = OpLabel", + "%68 = OpVariable %_ptr_Function_float Function", + "%69 = OpVariable %_ptr_Function_float Function", + "%color1 = OpVariable %_ptr_Function_v4float Function", + "%color2 = OpVariable %_ptr_Function_v4float Function", + "%param = OpVariable %_ptr_Function_v4float Function", + "%color3 = OpVariable %_ptr_Function_v4float Function", + "%40 = OpLoad %26 %t2D", + "%41 = OpLoad %28 %samp", + "%42 = OpSampledImage %30 %40 %41", + "%43 = OpImageSampleImplicitLod %v4float %42 %33", + "%44 = OpImage %26 %42", + "%45 = OpLoad %28 %samp2", + "%46 = OpSampledImage %30 %44 %45", + "OpStore %color1 %43", + "%47 = OpLoad %v4float %BaseColor", + "OpStore %param %47", + "%71 = OpAccessChain %_ptr_Function_float %param %uint_0", + "%72 = OpLoad %float %71", + "OpStore %68 %72", + "%73 = OpLoad %float %68", + "%74 = OpFOrdLessThan %bool %73 %float_0", + "OpSelectionMerge %78 None", + "OpBranchConditional %74 %75 %78", + "%75 = OpLabel", + "%76 = OpLoad %float %68", + "%77 = OpFNegate %float %76", + "OpStore %68 %77", + "OpBranch %78", + "%78 = OpLabel", + "%79 = OpLoad %float %68", + "OpStore %69 %79", + "%48 = OpLoad %float %69", + "%49 = OpCompositeConstruct %v4float %48 %48 %48 %48", + "OpStore %color2 %49", + "%80 = OpSampledImage %30 %40 %41", + "%81 = OpImage %26 %80", + "%82 = OpSampledImage %30 %81 %45", + "%50 = OpImageSampleImplicitLod %v4float %82 %36", + "OpStore %color3 %50", + "%51 = OpLoad %v4float %color1", + "%52 = OpLoad %v4float %color2", + "%53 = OpFAdd %v4float %51 %52", + "%54 = OpLoad %v4float %color3", + "%55 = OpFAdd %v4float %53 %54", + "%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3", + "%57 = OpFDiv %v4float %55 %56", + "OpStore %FragColor %57", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck( + JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)), + JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)), + /* skip_nop = */ false, /* do_validate = */ true); +} + +TEST_F(InlineTest, EarlyReturnInLoopIsNotInlined) { + // #version 140 + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // while (true) { + // if (bar.x < 0.0) + // return 0.0; + // return bar.x; + // } + // } + // + // void main() + // { + // vec4 color = vec4(foo(BaseColor)); + // gl_FragColor = color; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_vf4_ "foo(vf4;" +OpName %bar "bar" +OpName %color "color" +OpName %BaseColor "BaseColor" +OpName %param "param" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %float %_ptr_Function_v4float +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %10 +%23 = OpLabel +%color = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%24 = OpLoad %v4float %BaseColor +OpStore %param %24 +%25 = OpFunctionCall %float %foo_vf4_ %param +%26 = OpCompositeConstruct %v4float %25 %25 %25 %25 +OpStore %color %26 +%27 = OpLoad %v4float %color +OpStore %gl_FragColor %27 +OpReturn +OpFunctionEnd +%foo_vf4_ = OpFunction %float None %14 +%bar = OpFunctionParameter %_ptr_Function_v4float +%28 = OpLabel +OpBranch %29 +%29 = OpLabel +OpLoopMerge %30 %31 None +OpBranch %32 +%32 = OpLabel +OpBranchConditional %true %33 %30 +%33 = OpLabel +%34 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%35 = OpLoad %float %34 +%36 = OpFOrdLessThan %bool %35 %float_0 +OpSelectionMerge %37 None +OpBranchConditional %36 %38 %37 +%38 = OpLabel +OpReturnValue %float_0 +%37 = OpLabel +%39 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%40 = OpLoad %float %39 +OpReturnValue %40 +%31 = OpLabel +OpBranch %29 +%30 = OpLabel +%41 = OpUndef %float +OpReturnValue %41 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, false, true); +} + +TEST_F(InlineTest, ExternalFunctionIsNotInlined) { + // In particular, don't crash. + // See report https://github.com/KhronosGroup/SPIRV-Tools/issues/605 + const std::string assembly = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %1 "entry_pt" +OpDecorate %2 LinkageAttributes "external" Import +%void = OpTypeVoid +%4 = OpTypeFunction %void +%2 = OpFunction %void None %4 +OpFunctionEnd +%1 = OpFunction %void None %4 +%5 = OpLabel +%6 = OpFunctionCall %void %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, false, true); +} + +TEST_F(InlineTest, SingleBlockLoopCallsMultiBlockCallee) { + // Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/787 + // + // CFG structure is: + // foo: + // fooentry -> fooexit + // + // main: + // entry -> loop + // loop -> loop, merge + // loop calls foo() + // merge + // + // Since the callee has multiple blocks, it will split the calling block + // into at least two, resulting in a new "back-half" block that contains + // the instructions after the inlined function call. If the calling block + // has an OpLoopMerge that points back to the calling block itself, then + // the OpLoopMerge can't remain in the back-half block, but must be + // moved to the end of the original calling block, and it continue target + // operand updated to point to the back-half block. + + const std::string predefs = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpSource OpenCL_C 120 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%void = OpTypeVoid +%5 = OpTypeFunction %void +)"; + + const std::string nonEntryFuncs = + R"(%6 = OpFunction %void None %5 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string before = + R"(%1 = OpFunction %void None %5 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +%11 = OpFunctionCall %void %6 +OpLoopMerge %12 %10 None +OpBranchConditional %true %10 %12 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%1 = OpFunction %void None %5 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +OpLoopMerge %12 %10 None +OpBranch %14 +%14 = OpLabel +OpBranchConditional %true %10 %12 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string undef = "%11 = OpUndef %void\n"; + + SinglePassRunAndCheck( + predefs + nonEntryFuncs + before, predefs + undef + nonEntryFuncs + after, + false, true); +} + +TEST_F(InlineTest, MultiBlockLoopHeaderCallsMultiBlockCallee) { + // Like SingleBlockLoopCallsMultiBlockCallee but the loop has several + // blocks, but the function call still occurs in the loop header. + // Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/800 + + const std::string predefs = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpSource OpenCL_C 120 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_3 = OpConstant %int 3 +%int_4 = OpConstant %int 4 +%int_5 = OpConstant %int 5 +%void = OpTypeVoid +%11 = OpTypeFunction %void +)"; + + const std::string nonEntryFuncs = + R"(%12 = OpFunction %void None %11 +%13 = OpLabel +%14 = OpCopyObject %int %int_1 +OpBranch %15 +%15 = OpLabel +%16 = OpCopyObject %int %int_2 +OpReturn +OpFunctionEnd +)"; + + const std::string before = + R"(%1 = OpFunction %void None %11 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpCopyObject %int %int_3 +%20 = OpFunctionCall %void %12 +%21 = OpCopyObject %int %int_4 +OpLoopMerge %22 %23 None +OpBranchConditional %true %23 %22 +%23 = OpLabel +%24 = OpCopyObject %int %int_5 +OpBranchConditional %true %18 %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%1 = OpFunction %void None %11 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpCopyObject %int %int_3 +%26 = OpCopyObject %int %int_1 +OpLoopMerge %22 %23 None +OpBranch %27 +%27 = OpLabel +%28 = OpCopyObject %int %int_2 +%21 = OpCopyObject %int %int_4 +OpBranchConditional %true %23 %22 +%23 = OpLabel +%24 = OpCopyObject %int %int_5 +OpBranchConditional %true %18 %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string undef = "%20 = OpUndef %void\n"; + SinglePassRunAndCheck( + predefs + nonEntryFuncs + before, predefs + undef + nonEntryFuncs + after, + false, true); +} + +TEST_F(InlineTest, SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge) { + // This is similar to SingleBlockLoopCallsMultiBlockCallee except + // that calleee block also has a merge instruction in its first block. + // That merge instruction must be an OpSelectionMerge (because the entry + // block of a function can't be the header of a loop since the entry + // block can't be the target of a branch). + // + // In this case the OpLoopMerge can't be placed in the same block as + // the OpSelectionMerge, so inlining must create a new block to contain + // the callee contents. + // + // Additionally, we have two extra OpCopyObject instructions to prove that + // the OpLoopMerge is moved to the right location. + // + // Also ensure that OpPhis within the cloned callee code are valid. + // We need to test that the predecessor blocks are remapped correctly so that + // dominance rules are satisfied + + const std::string predefs = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpSource OpenCL_C 120 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%void = OpTypeVoid +%6 = OpTypeFunction %void +)"; + + // This callee has multiple blocks, and an OpPhi in the last block + // that references a value from the first block. This tests that + // cloned block IDs are remapped appropriately. The OpPhi dominance + // requires that the remapped %9 must be in a block that dominates + // the remapped %8. + const std::string nonEntryFuncs = + R"(%7 = OpFunction %void None %6 +%8 = OpLabel +%9 = OpCopyObject %bool %true +OpSelectionMerge %10 None +OpBranchConditional %true %10 %10 +%10 = OpLabel +%11 = OpPhi %bool %9 %8 +OpReturn +OpFunctionEnd +)"; + + const std::string before = + R"(%1 = OpFunction %void None %6 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +%14 = OpCopyObject %bool %false +%15 = OpFunctionCall %void %7 +OpLoopMerge %16 %13 None +OpBranchConditional %true %13 %16 +%16 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // Note the remapped Phi uses %17 as the parent instead + // of %13, demonstrating that the parent block has been remapped + // correctly. + const std::string after = + R"(%1 = OpFunction %void None %6 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +%14 = OpCopyObject %bool %false +OpLoopMerge %16 %13 None +OpBranch %17 +%17 = OpLabel +%19 = OpCopyObject %bool %true +OpSelectionMerge %20 None +OpBranchConditional %true %20 %20 +%20 = OpLabel +%21 = OpPhi %bool %19 %17 +OpBranchConditional %true %13 %16 +%16 = OpLabel +OpReturn +OpFunctionEnd +)"; + const std::string undef = "%15 = OpUndef %void\n"; + SinglePassRunAndCheck( + predefs + nonEntryFuncs + before, predefs + undef + nonEntryFuncs + after, + false, true); +} + +TEST_F(InlineTest, + MultiBlockLoopHeaderCallsFromToMultiBlockCalleeHavingSelectionMerge) { + // This is similar to SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge + // but the call is in the header block of a multi block loop. + + const std::string predefs = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpSource OpenCL_C 120 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_3 = OpConstant %int 3 +%int_4 = OpConstant %int 4 +%int_5 = OpConstant %int 5 +%void = OpTypeVoid +%11 = OpTypeFunction %void +)"; + + const std::string nonEntryFuncs = + R"(%12 = OpFunction %void None %11 +%13 = OpLabel +%14 = OpCopyObject %int %int_1 +OpSelectionMerge %15 None +OpBranchConditional %true %15 %15 +%15 = OpLabel +%16 = OpCopyObject %int %int_2 +OpReturn +OpFunctionEnd +)"; + + const std::string before = + R"(%1 = OpFunction %void None %11 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpCopyObject %int %int_3 +%20 = OpFunctionCall %void %12 +%21 = OpCopyObject %int %int_4 +OpLoopMerge %22 %23 None +OpBranchConditional %true %23 %22 +%23 = OpLabel +%24 = OpCopyObject %int %int_5 +OpBranchConditional %true %18 %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%1 = OpFunction %void None %11 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpCopyObject %int %int_3 +OpLoopMerge %22 %23 None +OpBranch %25 +%25 = OpLabel +%27 = OpCopyObject %int %int_1 +OpSelectionMerge %28 None +OpBranchConditional %true %28 %28 +%28 = OpLabel +%29 = OpCopyObject %int %int_2 +%21 = OpCopyObject %int %int_4 +OpBranchConditional %true %23 %22 +%23 = OpLabel +%24 = OpCopyObject %int %int_5 +OpBranchConditional %true %18 %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string undef = "%20 = OpUndef %void\n"; + SinglePassRunAndCheck( + predefs + nonEntryFuncs + before, predefs + undef + nonEntryFuncs + after, + false, true); +} + +TEST_F(InlineTest, NonInlinableCalleeWithSingleReturn) { + // The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/2018 + // + // The callee has a single return, but cannot be inlined because the + // return is inside a loop. + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_GLF_color +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +OpName %main "main" +OpName %f_ "f(" +OpName %i "i" +OpName %_GLF_color "_GLF_color" +OpDecorate %_GLF_color Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%9 = OpTypeFunction %float +%float_1 = OpConstant %float 1 +%bool = OpTypeBool +%false = OpConstantFalse %bool +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_GLF_color = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%21 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1 +)"; + + const std::string caller = + R"(%main = OpFunction %void None %7 +%22 = OpLabel +%i = OpVariable %_ptr_Function_int Function +OpStore %i %int_0 +OpBranch %23 +%23 = OpLabel +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_1 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +OpStore %_GLF_color %20 +%30 = OpFunctionCall %float %f_ +OpBranch %25 +%25 = OpLabel +%31 = OpLoad %int %i +%32 = OpIAdd %int %31 %int_1 +OpStore %i %32 +OpBranch %23 +%24 = OpLabel +OpStore %_GLF_color %21 +OpReturn +OpFunctionEnd +)"; + + const std::string callee = + R"(%f_ = OpFunction %float None %9 +%33 = OpLabel +OpBranch %34 +%34 = OpLabel +OpLoopMerge %35 %36 None +OpBranch %37 +%37 = OpLabel +OpReturnValue %float_1 +%36 = OpLabel +OpBranch %34 +%35 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs + caller + callee, predefs + caller + callee, false, true); +} + +TEST_F(InlineTest, Decorated1) { + // Same test as Simple with the difference + // that OpFAdd in the outlined function is + // decorated with RelaxedPrecision + // Expected result is an equal decoration + // of the corresponding inlined instruction + // + // #version 140 + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // return bar.x + bar.y; + // } + // + // void main() + // { + // vec4 color = vec4(foo(BaseColor)); + // gl_FragColor = color; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_vf4_ "foo(vf4;" +OpName %bar "bar" +OpName %color "color" +OpName %BaseColor "BaseColor" +OpName %param "param" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %9 RelaxedPrecision +)"; + + const std::string before = + R"(%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%15 = OpTypeFunction %float %_ptr_Function_v4float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %11 +%22 = OpLabel +%color = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%23 = OpLoad %v4float %BaseColor +OpStore %param %23 +%24 = OpFunctionCall %float %foo_vf4_ %param +%25 = OpCompositeConstruct %v4float %24 %24 %24 %24 +OpStore %color %25 +%26 = OpLoad %v4float %color +OpStore %gl_FragColor %26 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpDecorate %38 RelaxedPrecision +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%15 = OpTypeFunction %float %_ptr_Function_v4float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %11 +%22 = OpLabel +%32 = OpVariable %_ptr_Function_float Function +%color = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%23 = OpLoad %v4float %BaseColor +OpStore %param %23 +%34 = OpAccessChain %_ptr_Function_float %param %uint_0 +%35 = OpLoad %float %34 +%36 = OpAccessChain %_ptr_Function_float %param %uint_1 +%37 = OpLoad %float %36 +%38 = OpFAdd %float %35 %37 +OpStore %32 %38 +%24 = OpLoad %float %32 +%25 = OpCompositeConstruct %v4float %24 %24 %24 %24 +OpStore %color %25 +%26 = OpLoad %v4float %color +OpStore %gl_FragColor %26 +OpReturn +OpFunctionEnd +)"; + + const std::string nonEntryFuncs = + R"(%foo_vf4_ = OpFunction %float None %15 +%bar = OpFunctionParameter %_ptr_Function_v4float +%27 = OpLabel +%28 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%29 = OpLoad %float %28 +%30 = OpAccessChain %_ptr_Function_float %bar %uint_1 +%31 = OpLoad %float %30 +%9 = OpFAdd %float %29 %31 +OpReturnValue %9 +OpFunctionEnd +)"; + SinglePassRunAndCheck(predefs + before + nonEntryFuncs, + predefs + after + nonEntryFuncs, + false, true); +} + +TEST_F(InlineTest, Decorated2) { + // Same test as Simple with the difference + // that the Result of the outlined OpFunction + // is decorated with RelaxedPrecision + // Expected result is an equal decoration + // of the created return variable + // + // #version 140 + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // return bar.x + bar.y; + // } + // + // void main() + // { + // vec4 color = vec4(foo(BaseColor)); + // gl_FragColor = color; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_vf4_ "foo(vf4;" +OpName %bar "bar" +OpName %color "color" +OpName %BaseColor "BaseColor" +OpName %param "param" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %foo_vf4_ RelaxedPrecision +)"; + + const std::string before = + R"(%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %float %_ptr_Function_v4float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %10 +%21 = OpLabel +%color = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %BaseColor +OpStore %param %22 +%23 = OpFunctionCall %float %foo_vf4_ %param +%24 = OpCompositeConstruct %v4float %23 %23 %23 %23 +OpStore %color %24 +%25 = OpLoad %v4float %color +OpStore %gl_FragColor %25 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpDecorate %32 RelaxedPrecision +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %float %_ptr_Function_v4float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %10 +%21 = OpLabel +%32 = OpVariable %_ptr_Function_float Function +%color = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %BaseColor +OpStore %param %22 +%34 = OpAccessChain %_ptr_Function_float %param %uint_0 +%35 = OpLoad %float %34 +%36 = OpAccessChain %_ptr_Function_float %param %uint_1 +%37 = OpLoad %float %36 +%38 = OpFAdd %float %35 %37 +OpStore %32 %38 +%23 = OpLoad %float %32 +%24 = OpCompositeConstruct %v4float %23 %23 %23 %23 +OpStore %color %24 +%25 = OpLoad %v4float %color +OpStore %gl_FragColor %25 +OpReturn +OpFunctionEnd +)"; + + const std::string nonEntryFuncs = + R"(%foo_vf4_ = OpFunction %float None %14 +%bar = OpFunctionParameter %_ptr_Function_v4float +%26 = OpLabel +%27 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%28 = OpLoad %float %27 +%29 = OpAccessChain %_ptr_Function_float %bar %uint_1 +%30 = OpLoad %float %29 +%31 = OpFAdd %float %28 %30 +OpReturnValue %31 +OpFunctionEnd +)"; + SinglePassRunAndCheck(predefs + before + nonEntryFuncs, + predefs + after + nonEntryFuncs, + false, true); +} + +TEST_F(InlineTest, DeleteName) { + // Test that the name of the result id of the call is deleted. + const std::string before = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpName %main "main" + OpName %main_entry "main_entry" + OpName %foo_result "foo_result" + OpName %void_fn "void_fn" + OpName %foo "foo" + OpName %foo_entry "foo_entry" + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %foo = OpFunction %void None %void_fn + %foo_entry = OpLabel + OpReturn + OpFunctionEnd + %main = OpFunction %void None %void_fn + %main_entry = OpLabel + %foo_result = OpFunctionCall %void %foo + OpReturn + OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpName %main "main" +OpName %main_entry "main_entry" +OpName %void_fn "void_fn" +OpName %foo "foo" +OpName %foo_entry "foo_entry" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%3 = OpUndef %void +%foo = OpFunction %void None %void_fn +%foo_entry = OpLabel +OpReturn +OpFunctionEnd +%main = OpFunction %void None %void_fn +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(InlineTest, SetParent) { + // Test that after inlining all basic blocks have the correct parent. + const std::string text = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpName %main "main" + OpName %main_entry "main_entry" + OpName %foo_result "foo_result" + OpName %void_fn "void_fn" + OpName %foo "foo" + OpName %foo_entry "foo_entry" + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %foo = OpFunction %void None %void_fn + %foo_entry = OpLabel + OpReturn + OpFunctionEnd + %main = OpFunction %void None %void_fn + %main_entry = OpLabel + %foo_result = OpFunctionCall %void %foo + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + InlineExhaustivePass pass; + pass.Run(context.get()); + + for (Function& func : *context->module()) { + for (BasicBlock& bb : func) { + EXPECT_TRUE(bb.GetParent() == &func); + } + } +} + +TEST_F(InlineTest, OpVariableWithInit) { + // Check that there is a store that corresponds to the initializer. This + // test makes sure that is a store to the variable in the loop and before any + // load. + const std::string text = R"( +; CHECK: OpFunction +; CHECK-NOT: OpFunctionEnd +; CHECK: [[var:%\w+]] = OpVariable %_ptr_Function_float Function %float_0 +; CHECK: OpLoopMerge [[outer_merge:%\w+]] +; CHECK-NOT: OpLoad %float [[var]] +; CHECK: OpStore [[var]] %float_0 +; CHECK: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpDecorate %o Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeFunction %float +%_ptr_Function_float = OpTypePointer Function %float + %float_0 = OpConstant %float 0 + %bool = OpTypeBool + %float_1 = OpConstant %float 1 +%_ptr_Output_float = OpTypePointer Output %float + %o = OpVariable %_ptr_Output_float Output + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_int = OpTypePointer Input %int + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %o %float_0 + OpBranch %34 + %34 = OpLabel + %39 = OpPhi %int %int_0 %5 %47 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %41 = OpSLessThan %bool %39 %int_2 + OpBranchConditional %41 %35 %36 + %35 = OpLabel + %42 = OpFunctionCall %float %foo_ + %43 = OpLoad %float %o + %44 = OpFAdd %float %43 %42 + OpStore %o %44 + OpBranch %37 + %37 = OpLabel + %47 = OpIAdd %int %39 %int_1 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + %foo_ = OpFunction %float None %7 + %9 = OpLabel + %n = OpVariable %_ptr_Function_float Function %float_0 + %13 = OpLoad %float %n + %15 = OpFOrdEqual %bool %13 %float_0 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + %19 = OpLoad %float %n + %20 = OpFAdd %float %19 %float_1 + OpStore %n %20 + OpBranch %17 + %17 = OpLabel + %21 = OpLoad %float %n + OpReturnValue %21 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, DontInlineDirectlyRecursiveFunc) { + // Test that the name of the result id of the call is deleted. + const std::string test = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +OpDecorate %2 DescriptorSet 439418829 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%15 = OpConstantNull %_struct_6 +%7 = OpTypeFunction %_struct_6 +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %9 +OpReturnValue %15 +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, test, false, true); +} + +TEST_F(InlineTest, DontInlineInDirectlyRecursiveFunc) { + // Test that the name of the result id of the call is deleted. + const std::string test = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +OpDecorate %2 DescriptorSet 439418829 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%15 = OpConstantNull %_struct_6 +%7 = OpTypeFunction %_struct_6 +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %12 +OpReturnValue %15 +OpFunctionEnd +%12 = OpFunction %_struct_6 None %7 +%13 = OpLabel +%14 = OpFunctionCall %_struct_6 %9 +OpReturnValue %15 +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, test, false, true); +} + +TEST_F(InlineTest, DontInlineFuncWithOpKillInContinue) { + const std::string test = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 330 +OpName %main "main" +OpName %kill_ "kill(" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %void None %3 +%5 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %11 %12 None +OpBranch %13 +%13 = OpLabel +OpBranchConditional %true %10 %11 +%10 = OpLabel +OpBranch %12 +%12 = OpLabel +%16 = OpFunctionCall %void %kill_ +OpBranch %9 +%11 = OpLabel +OpReturn +OpFunctionEnd +%kill_ = OpFunction %void None %3 +%7 = OpLabel +OpKill +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, test, false, true); +} + +TEST_F(InlineTest, DontInlineFuncWithDontInline) { + // Check that the function with DontInline flag is not inlined. + const std::string text = R"( +; CHECK: %foo = OpFunction %int DontInline +; CHECK: OpReturnValue %int_0 + +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 600 +OpName %main "main" +OpName %foo "foo" +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%7 = OpTypeFunction %int +%main = OpFunction %void None %6 +%8 = OpLabel +%9 = OpFunctionCall %int %foo +OpReturn +OpFunctionEnd +%foo = OpFunction %int DontInline %7 +%10 = OpLabel +OpReturnValue %int_0 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, InlineFuncWithOpKillNotInContinue) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 330 +OpName %main "main" +OpName %kill_ "kill(" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpFunctionCall %void %kill_ +OpReturn +OpFunctionEnd +%kill_ = OpFunction %void None %3 +%7 = OpLabel +OpKill +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 330 +OpName %main "main" +OpName %kill_ "kill(" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%16 = OpUndef %void +%main = OpFunction %void None %3 +%5 = OpLabel +OpKill +%18 = OpLabel +OpReturn +OpFunctionEnd +%kill_ = OpFunction %void None %3 +%7 = OpLabel +OpKill +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(InlineTest, DontInlineFuncWithOpTerminateInvocationInContinue) { + const std::string test = + R"(OpCapability Shader +OpExtension "SPV_KHR_terminate_invocation" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 330 +OpName %main "main" +OpName %kill_ "kill(" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %void None %3 +%5 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %11 %12 None +OpBranch %13 +%13 = OpLabel +OpBranchConditional %true %10 %11 +%10 = OpLabel +OpBranch %12 +%12 = OpLabel +%16 = OpFunctionCall %void %kill_ +OpBranch %9 +%11 = OpLabel +OpReturn +OpFunctionEnd +%kill_ = OpFunction %void None %3 +%7 = OpLabel +OpTerminateInvocation +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, test, false, true); +} + +TEST_F(InlineTest, InlineFuncWithOpTerminateInvocationNotInContinue) { + const std::string before = + R"(OpCapability Shader +OpExtension "SPV_KHR_terminate_invocation" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 330 +OpName %main "main" +OpName %kill_ "kill(" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpFunctionCall %void %kill_ +OpReturn +OpFunctionEnd +%kill_ = OpFunction %void None %3 +%7 = OpLabel +OpTerminateInvocation +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpExtension "SPV_KHR_terminate_invocation" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 330 +OpName %main "main" +OpName %kill_ "kill(" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%16 = OpUndef %void +%main = OpFunction %void None %3 +%5 = OpLabel +OpTerminateInvocation +%18 = OpLabel +OpReturn +OpFunctionEnd +%kill_ = OpFunction %void None %3 +%7 = OpLabel +OpTerminateInvocation +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(InlineTest, EarlyReturnFunctionInlined) { + // #version 140 + // + // in vec4 BaseColor; + // + // float foo(vec4 bar) + // { + // if (bar.x < 0.0) + // return 0.0; + // return bar.x; + // } + // + // void main() + // { + // vec4 color = vec4(foo(BaseColor)); + // gl_FragColor = color; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_vf4_ "foo(vf4;" +OpName %bar "bar" +OpName %color "color" +OpName %BaseColor "BaseColor" +OpName %param "param" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %float %_ptr_Function_v4float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string foo = + R"(%foo_vf4_ = OpFunction %float None %14 +%bar = OpFunctionParameter %_ptr_Function_v4float +%27 = OpLabel +%28 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%29 = OpLoad %float %28 +%30 = OpFOrdLessThan %bool %29 %float_0 +OpSelectionMerge %31 None +OpBranchConditional %30 %32 %31 +%32 = OpLabel +OpReturnValue %float_0 +%31 = OpLabel +%33 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%34 = OpLoad %float %33 +OpReturnValue %34 +OpFunctionEnd +)"; + + const std::string fooMergeReturn = + R"(%foo_vf4_ = OpFunction %float None %14 +%bar = OpFunctionParameter %_ptr_Function_v4float +%27 = OpLabel +%41 = OpVariable %_ptr_Function_bool Function %false +%36 = OpVariable %_ptr_Function_float Function +OpSelectionMerge %35 None +OpSwitch %uint_0 %38 +%38 = OpLabel +%28 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%29 = OpLoad %float %28 +%30 = OpFOrdLessThan %bool %29 %float_0 +OpSelectionMerge %31 None +OpBranchConditional %30 %32 %31 +%32 = OpLabel +OpStore %41 %true +OpStore %36 %float_0 +OpBranch %35 +%31 = OpLabel +%33 = OpAccessChain %_ptr_Function_float %bar %uint_0 +%34 = OpLoad %float %33 +OpStore %41 %true +OpStore %36 %34 +OpBranch %35 +%35 = OpLabel +%37 = OpLoad %float %36 +OpReturnValue %37 +OpFunctionEnd +)"; + + const std::string before = + R"(%main = OpFunction %void None %10 +%22 = OpLabel +%color = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%23 = OpLoad %v4float %BaseColor +OpStore %param %23 +%24 = OpFunctionCall %float %foo_vf4_ %param +%25 = OpCompositeConstruct %v4float %24 %24 %24 %24 +OpStore %color %25 +%26 = OpLoad %v4float %color +OpStore %gl_FragColor %26 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%false = OpConstantFalse %bool +%_ptr_Function_bool = OpTypePointer Function %bool +%true = OpConstantTrue %bool +%main = OpFunction %void None %10 +%22 = OpLabel +%43 = OpVariable %_ptr_Function_bool Function %false +%44 = OpVariable %_ptr_Function_float Function +%45 = OpVariable %_ptr_Function_float Function +%color = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%23 = OpLoad %v4float %BaseColor +OpStore %param %23 +OpStore %43 %false +OpSelectionMerge %55 None +OpSwitch %uint_0 %47 +%47 = OpLabel +%48 = OpAccessChain %_ptr_Function_float %param %uint_0 +%49 = OpLoad %float %48 +%50 = OpFOrdLessThan %bool %49 %float_0 +OpSelectionMerge %52 None +OpBranchConditional %50 %51 %52 +%51 = OpLabel +OpStore %43 %true +OpStore %44 %float_0 +OpBranch %55 +%52 = OpLabel +%53 = OpAccessChain %_ptr_Function_float %param %uint_0 +%54 = OpLoad %float %53 +OpStore %43 %true +OpStore %44 %54 +OpBranch %55 +%55 = OpLabel +%56 = OpLoad %float %44 +OpStore %45 %56 +%24 = OpLoad %float %45 +%25 = OpCompositeConstruct %v4float %24 %24 %24 %24 +OpStore %color %25 +%26 = OpLoad %v4float %color +OpStore %gl_FragColor %26 +OpReturn +OpFunctionEnd +)"; + + // The early return case must be handled by merge-return first. + AddPass(); + AddPass(); + RunAndCheck(predefs + before + foo, predefs + after + fooMergeReturn); +} + +TEST_F(InlineTest, EarlyReturnNotAppearingLastInFunctionInlined) { + // Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/755 + // + // Original example is derived from: + // + // #version 450 + // + // float foo() { + // if (true) { + // } + // } + // + // void main() { foo(); } + // + // But the order of basic blocks in foo is changed so that the return + // block is listed second-last. There is only one return in the callee + // but it does not appear last. + + const std::string predefs = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpSource GLSL 450 +OpName %main "main" +OpName %foo_ "foo(" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +)"; + + const std::string foo = + R"(%foo_ = OpFunction %void None %4 +%7 = OpLabel +OpSelectionMerge %8 None +OpBranchConditional %true %9 %8 +%8 = OpLabel +OpReturn +%9 = OpLabel +OpBranch %8 +OpFunctionEnd +)"; + + const std::string fooMergeReturn = + R"(%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%false = OpConstantFalse %bool +%_ptr_Function_bool = OpTypePointer Function %bool +%11 = OpUndef %void +%foo_ = OpFunction %void None %4 +%7 = OpLabel +%18 = OpVariable %_ptr_Function_bool Function %false +OpSelectionMerge %12 None +OpSwitch %uint_0 %13 +%13 = OpLabel +OpSelectionMerge %8 None +OpBranchConditional %true %9 %8 +%8 = OpLabel +OpStore %18 %true +OpBranch %12 +%9 = OpLabel +OpBranch %8 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string before = + R"(%main = OpFunction %void None %4 +%10 = OpLabel +%11 = OpFunctionCall %void %foo_ +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %4 +%10 = OpLabel +%19 = OpVariable %_ptr_Function_bool Function %false +OpStore %19 %false +OpSelectionMerge %24 None +OpSwitch %uint_0 %21 +%21 = OpLabel +OpSelectionMerge %22 None +OpBranchConditional %true %23 %22 +%22 = OpLabel +OpStore %19 %true +OpBranch %24 +%23 = OpLabel +OpBranch %22 +%24 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // The early return case must be handled by merge-return first. + AddPass(); + AddPass(); + RunAndCheck(predefs + foo + before, predefs + fooMergeReturn + after); +} + +TEST_F(InlineTest, CalleeWithSingleReturnNeedsSingleTripLoopWrapper) { + // The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/2018 + // + // The callee has a single return, but needs single-trip loop wrapper + // to be inlined because the return is in a selection structure. + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_GLF_color +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +OpName %main "main" +OpName %f_ "f(" +OpName %i "i" +OpName %_GLF_color "_GLF_color" +OpDecorate %_GLF_color Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%9 = OpTypeFunction %float +%float_1 = OpConstant %float 1 +%bool = OpTypeBool +%false = OpConstantFalse %bool +%true = OpConstantTrue %bool +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_GLF_color = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +%21 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%22 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1 +)"; + + const std::string new_predefs = + R"(%_ptr_Function_float = OpTypePointer Function %float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_bool = OpTypePointer Function %bool +)"; + + const std::string main_before = + R"(%main = OpFunction %void None %7 +%23 = OpLabel +%i = OpVariable %_ptr_Function_int Function +OpStore %i %int_0 +OpBranch %24 +%24 = OpLabel +OpLoopMerge %25 %26 None +OpBranch %27 +%27 = OpLabel +%28 = OpLoad %int %i +%29 = OpSLessThan %bool %28 %int_1 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +OpStore %_GLF_color %21 +%31 = OpFunctionCall %float %f_ +OpBranch %26 +%26 = OpLabel +%32 = OpLoad %int %i +%33 = OpIAdd %int %32 %int_1 +OpStore %i %33 +OpBranch %24 +%25 = OpLabel +OpStore %_GLF_color %22 +OpReturn +OpFunctionEnd +)"; + + const std::string main_after = + R"(%main = OpFunction %void None %7 +%23 = OpLabel +%46 = OpVariable %_ptr_Function_bool Function %false +%47 = OpVariable %_ptr_Function_float Function +%48 = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %i %int_0 +OpBranch %24 +%24 = OpLabel +OpLoopMerge %25 %26 None +OpBranch %27 +%27 = OpLabel +%28 = OpLoad %int %i +%29 = OpSLessThan %bool %28 %int_1 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +OpStore %_GLF_color %21 +OpStore %46 %false +OpSelectionMerge %53 None +OpSwitch %uint_0 %50 +%50 = OpLabel +OpSelectionMerge %52 None +OpBranchConditional %true %51 %52 +%51 = OpLabel +OpStore %46 %true +OpStore %47 %float_1 +OpBranch %53 +%52 = OpLabel +OpStore %46 %true +OpStore %47 %float_1 +OpBranch %53 +%53 = OpLabel +%54 = OpLoad %float %47 +OpStore %48 %54 +%31 = OpLoad %float %48 +OpBranch %26 +%26 = OpLabel +%32 = OpLoad %int %i +%33 = OpIAdd %int %32 %int_1 +OpStore %i %33 +OpBranch %24 +%25 = OpLabel +OpStore %_GLF_color %22 +OpReturn +OpFunctionEnd +)"; + + const std::string callee = + R"(%f_ = OpFunction %float None %9 +%34 = OpLabel +OpSelectionMerge %35 None +OpBranchConditional %true %36 %35 +%36 = OpLabel +OpReturnValue %float_1 +%35 = OpLabel +OpReturnValue %float_1 +OpFunctionEnd +)"; + + const std::string calleeMergeReturn = + R"(%f_ = OpFunction %float None %9 +%34 = OpLabel +%45 = OpVariable %_ptr_Function_bool Function %false +%39 = OpVariable %_ptr_Function_float Function +OpSelectionMerge %37 None +OpSwitch %uint_0 %41 +%41 = OpLabel +OpSelectionMerge %35 None +OpBranchConditional %true %36 %35 +%36 = OpLabel +OpStore %45 %true +OpStore %39 %float_1 +OpBranch %37 +%35 = OpLabel +OpStore %45 %true +OpStore %39 %float_1 +OpBranch %37 +%37 = OpLabel +%40 = OpLoad %float %39 +OpReturnValue %40 +OpFunctionEnd +)"; + + // The early return case must be handled by merge-return first. + AddPass(); + AddPass(); + RunAndCheck(predefs + main_before + callee, + predefs + new_predefs + main_after + calleeMergeReturn); +} + +TEST_F(InlineTest, ForwardReferencesInPhiInlined) { + // The basic structure of the test case is like this: + // + // int foo() { + // int result = 1; + // if (true) { + // result = 1; + // } + // return result; + // } + // + // void main() { + // int x = foo(); + // } + // + // but with modifications: Using Phi instead of load/store, and the + // return block in foo appears before the "then" block. + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpSource GLSL 450 +OpName %main "main" +OpName %foo_ "foo(" +OpName %x "x" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%8 = OpTypeFunction %int +%bool = OpTypeBool +%true = OpConstantTrue %bool +%int_0 = OpConstant %int 0 +%_ptr_Function_int = OpTypePointer Function %int +)"; + + const std::string callee = + R"(%foo_ = OpFunction %int None %8 +%13 = OpLabel +%14 = OpCopyObject %int %int_0 +OpSelectionMerge %15 None +OpBranchConditional %true %16 %15 +%15 = OpLabel +%17 = OpPhi %int %14 %13 %18 %16 +OpReturnValue %17 +%16 = OpLabel +%18 = OpCopyObject %int %int_0 +OpBranch %15 +OpFunctionEnd +)"; + + const std::string calleeMergeReturn = + R"(%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%false = OpConstantFalse %bool +%_ptr_Function_bool = OpTypePointer Function %bool +%foo_ = OpFunction %int None %8 +%13 = OpLabel +%29 = OpVariable %_ptr_Function_bool Function %false +%22 = OpVariable %_ptr_Function_int Function +OpSelectionMerge %21 None +OpSwitch %uint_0 %24 +%24 = OpLabel +%14 = OpCopyObject %int %int_0 +OpSelectionMerge %15 None +OpBranchConditional %true %16 %15 +%15 = OpLabel +%17 = OpPhi %int %14 %24 %18 %16 +OpStore %29 %true +OpStore %22 %17 +OpBranch %21 +%16 = OpLabel +%18 = OpCopyObject %int %int_0 +OpBranch %15 +%21 = OpLabel +%23 = OpLoad %int %22 +OpReturnValue %23 +OpFunctionEnd +)"; + + const std::string before = + R"(%main = OpFunction %void None %6 +%19 = OpLabel +%x = OpVariable %_ptr_Function_int Function +%20 = OpFunctionCall %int %foo_ +OpStore %x %20 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %6 +%19 = OpLabel +%30 = OpVariable %_ptr_Function_bool Function %false +%31 = OpVariable %_ptr_Function_int Function +%32 = OpVariable %_ptr_Function_int Function +%x = OpVariable %_ptr_Function_int Function +OpStore %30 %false +OpSelectionMerge %40 None +OpSwitch %uint_0 %34 +%34 = OpLabel +%35 = OpCopyObject %int %int_0 +OpSelectionMerge %36 None +OpBranchConditional %true %38 %36 +%36 = OpLabel +%37 = OpPhi %int %35 %34 %39 %38 +OpStore %30 %true +OpStore %31 %37 +OpBranch %40 +%38 = OpLabel +%39 = OpCopyObject %int %int_0 +OpBranch %36 +%40 = OpLabel +%41 = OpLoad %int %31 +OpStore %32 %41 +%20 = OpLoad %int %32 +OpStore %x %20 +OpReturn +OpFunctionEnd +)"; + + AddPass(); + AddPass(); + RunAndCheck(predefs + callee + before, predefs + calleeMergeReturn + after); +} + +TEST_F(InlineTest, DebugSimple) { + // Check that it correctly generates DebugInlinedAt and maps it to DebugScope + // for the inlined function foo(). + const std::string text = R"( +; CHECK: [[main_name:%\d+]] = OpString "main" +; CHECK: [[foo_name:%\d+]] = OpString "foo" +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[main_name]] {{%\d+}} {{%\d+}} 4 1 {{%\d+}} [[main_name]] FlagIsProtected|FlagIsPrivate 4 [[main:%\d+]] +; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[foo_name]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} [[foo_name]] FlagIsProtected|FlagIsPrivate 1 [[foo:%\d+]] +; CHECK: [[foo_bb:%\d+]] = OpExtInst %void {{%\d+}} DebugLexicalBlock {{%\d+}} 1 14 [[dbg_foo]] +; CHECK: [[inlined_at:%\d+]] = OpExtInst %void {{%\d+}} DebugInlinedAt 4 [[dbg_main]] +; CHECK: [[main]] = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void {{%\d+}} DebugScope [[foo_bb]] [[inlined_at]] +; CHECK: [[foo]] = OpFunction %v4float None + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %3 %4 + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %6 = OpString "float" + %main_name = OpString "main" + %foo_name = OpString "foo" + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %v4float = OpTypeVector %float 4 + %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %18 = OpTypeFunction %void + %19 = OpTypeFunction %v4float + %3 = OpVariable %_ptr_Input_v4float Input + %4 = OpVariable %_ptr_Output_v4float Output + %20 = OpExtInst %void %1 DebugSource %5 + %21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL + %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float + %23 = OpExtInst %void %1 DebugTypeVector %22 4 + %24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23 + %25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 + %dbg_main = OpExtInst %void %1 DebugFunction %main_name %24 %20 4 1 %21 %main_name FlagIsProtected|FlagIsPrivate 4 %main + %dbg_foo = OpExtInst %void %1 DebugFunction %foo_name %25 %20 1 1 %21 %foo_name FlagIsProtected|FlagIsPrivate 1 %foo + %29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %dbg_foo + %main = OpFunction %void None %18 + %30 = OpLabel + %31 = OpExtInst %void %1 DebugScope %dbg_main + %32 = OpFunctionCall %v4float %foo + %33 = OpLoad %v4float %3 + %34 = OpFAdd %v4float %32 %33 + OpStore %4 %34 + OpReturn + OpFunctionEnd + %foo = OpFunction %v4float None %19 + %35 = OpExtInst %void %1 DebugScope %dbg_foo + %36 = OpLabel + %37 = OpExtInst %void %1 DebugScope %29 + OpReturnValue %14 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, DebugNested) { + // When function main() calls function zoo() and function zoo() calls + // function bar() and function bar() calls function foo(), check that + // the inline pass correctly generates DebugInlinedAt instructions + // for the nested function calls. + const std::string text = R"( +; CHECK: [[v4f1:%\d+]] = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +; CHECK: [[v4f2:%\d+]] = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +; CHECK: [[v4f3:%\d+]] = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3 +; CHECK: [[color:%\d+]] = OpVariable %_ptr_Input_v4float Input +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 10 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 10 [[main:%\d+]] +; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[foo:%\d+]] +; CHECK: [[dbg_bar:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 4 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 4 [[bar:%\d+]] +; CHECK: [[dbg_zoo:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 7 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 7 [[zoo:%\d+]] +; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 600 [[dbg_main]] +; CHECK: [[inlined_to_zoo:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 700 [[dbg_zoo]] [[inlined_to_main]] +; CHECK: [[inlined_to_bar:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 300 [[dbg_bar]] [[inlined_to_zoo]] +; CHECK: [[main]] = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_foo]] [[inlined_to_bar]] +; CHECK-NEXT: OpLine {{%\d+}} 100 0 +; CHECK-NEXT: OpStore {{%\d+}} [[v4f1]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_bar]] [[inlined_to_zoo]] +; CHECK-NEXT: OpLine {{%\d+}} 300 0 +; CHECK-NEXT: [[foo_ret:%\d+]] = OpLoad %v4float +; CHECK-NEXT: OpLine {{%\d+}} 400 0 +; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[foo_ret]] [[v4f2]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_zoo]] [[inlined_to_main]] +; CHECK-NEXT: OpLine {{%\d+}} 700 0 +; CHECK-NEXT: [[bar_ret:%\d+]] = OpLoad %v4float +; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[bar_ret]] [[v4f3]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK-NEXT: OpLine {{%\d+}} 600 0 +; CHECK-NEXT: [[zoo_ret:%\d+]] = OpLoad %v4float +; CHECK-NEXT: [[color_val:%\d+]] = OpLoad %v4float [[color]] +; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[zoo_ret]] [[color_val]] +; CHECK: [[foo]] = OpFunction %v4float None +; CHECK: [[bar]] = OpFunction %v4float None +; CHECK: [[zoo]] = OpFunction %v4float None + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %3 %4 + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %6 = OpString "float" + %7 = OpString "main" + %8 = OpString "foo" + %9 = OpString "bar" + %10 = OpString "zoo" + OpDecorate %3 Location 0 + OpDecorate %4 Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %float_3 = OpConstant %float 3 + %v4float = OpTypeVector %float 4 + %18 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %19 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %20 = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %24 = OpTypeFunction %void + %25 = OpTypeFunction %v4float + %3 = OpVariable %_ptr_Input_v4float Input + %4 = OpVariable %_ptr_Output_v4float Output + %26 = OpExtInst %void %1 DebugSource %5 + %27 = OpExtInst %void %1 DebugCompilationUnit 1 4 %26 HLSL + %28 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float + %29 = OpExtInst %void %1 DebugTypeVector %28 4 + %30 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %29 %29 + %31 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %29 + %32 = OpExtInst %void %1 DebugFunction %7 %30 %26 10 1 %27 %7 FlagIsProtected|FlagIsPrivate 10 %main + %33 = OpExtInst %void %1 DebugFunction %8 %31 %26 1 1 %27 %8 FlagIsProtected|FlagIsPrivate 1 %foo + %35 = OpExtInst %void %1 DebugFunction %9 %31 %26 4 1 %27 %9 FlagIsProtected|FlagIsPrivate 4 %bar + %37 = OpExtInst %void %1 DebugFunction %10 %31 %26 7 1 %27 %10 FlagIsProtected|FlagIsPrivate 7 %zoo + %main = OpFunction %void None %24 + %39 = OpLabel + %40 = OpExtInst %void %1 DebugScope %32 + OpLine %5 600 0 + %41 = OpFunctionCall %v4float %zoo + %42 = OpLoad %v4float %3 + %43 = OpFAdd %v4float %41 %42 + OpStore %4 %43 + OpReturn + OpFunctionEnd + %foo = OpFunction %v4float None %25 + %44 = OpExtInst %void %1 DebugScope %33 + %45 = OpLabel + OpLine %5 100 0 + OpReturnValue %18 + OpFunctionEnd + OpLine %5 200 0 + %bar = OpFunction %v4float None %25 + %46 = OpExtInst %void %1 DebugScope %35 + %47 = OpLabel + OpLine %5 300 0 + %48 = OpFunctionCall %v4float %foo + OpLine %5 400 0 + %49 = OpFAdd %v4float %48 %19 + OpLine %5 500 0 + OpReturnValue %49 + OpFunctionEnd + %zoo = OpFunction %v4float None %25 + %50 = OpExtInst %void %1 DebugScope %37 + %51 = OpLabel + OpLine %5 700 0 + %52 = OpFunctionCall %v4float %bar + %53 = OpFAdd %v4float %52 %20 + OpReturnValue %53 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, DebugSimpleHLSLPixelShader) { + const std::string text = R"( +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 %src_main +; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} 1 47 [[dbg_main]] +; CHECK: %main = OpFunction %void None +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[lex_blk]] +; CHECK: OpLine {{%\d+}} 2 10 +; CHECK: {{%\d+}} = OpLoad %v4float %param_var_color +; CHECK: OpLine {{%\d+}} 2 3 +; CHECK: OpFunctionEnd +; CHECK: %src_main = OpFunction %v4float None + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET + OpExecutionMode %main OriginUpperLeft + %5 = OpString "ps.hlsl" + OpSource HLSL 600 %5 + %14 = OpString "#line 1 \"ps.hlsl\" +float4 main(float4 color : COLOR) : SV_TARGET { + return color; +} +" + %17 = OpString "float" + %21 = OpString "src.main" + %24 = OpString "color" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_SV_TARGET "out.var.SV_TARGET" + OpName %main "main" + OpName %param_var_color "param.var.color" + OpName %src_main "src.main" + OpName %color "color" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + OpDecorate %out_var_SV_TARGET Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %27 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %33 = OpTypeFunction %v4float %_ptr_Function_v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output + %13 = OpExtInst %void %1 DebugExpression + %15 = OpExtInst %void %1 DebugSource %5 %14 + %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float + %19 = OpExtInst %void %1 DebugTypeVector %18 4 + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %19 %19 + %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %src_main + %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 1 20 %22 FlagIsLocal 0 + %26 = OpExtInst %void %1 DebugLexicalBlock %15 1 47 %22 + %main = OpFunction %void None %27 + %28 = OpLabel +%param_var_color = OpVariable %_ptr_Function_v4float Function + %31 = OpLoad %v4float %in_var_COLOR + OpStore %param_var_color %31 + %32 = OpFunctionCall %v4float %src_main %param_var_color + OpStore %out_var_SV_TARGET %32 + OpReturn + OpFunctionEnd + OpLine %5 1 1 + %src_main = OpFunction %v4float None %33 + %34 = OpExtInst %void %1 DebugScope %22 + %color = OpFunctionParameter %_ptr_Function_v4float + %36 = OpExtInst %void %1 DebugDeclare %25 %color %13 + %bb_entry = OpLabel + %38 = OpExtInst %void %1 DebugScope %26 + OpLine %5 2 10 + %39 = OpLoad %v4float %color + OpLine %5 2 3 + OpReturnValue %39 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, DebugDeclareForCalleeFunctionParam) { + // Check that InlinePass correctly generates DebugDeclare instructions + // for callee function's parameters and maps them to corresponding + // local variables of caller function. + const std::string text = R"( +; CHECK: [[add:%\d+]] = OpString "add" +; CHECK: [[a:%\d+]] = OpString "a" +; CHECK: [[b:%\d+]] = OpString "b" +; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]] +; CHECK: [[dbg_a:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[a]] +; CHECK: [[dbg_b:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[b]] +; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5 +; CHECK: OpStore [[param_a:%\d+]] +; CHECK: OpStore [[param_b:%\d+]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_a]] [[param_a]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_b]] [[param_b]] + +OpCapability Shader +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "ps.hlsl" +OpSource HLSL 600 %file_name +%float_name = OpString "float" +%main_name = OpString "main" +%add_name = OpString "add" +%a_name = OpString "a" +%b_name = OpString "b" +OpDecorate %in_var_COLOR Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%v4float = OpTypeVector %float 4 +%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float +%void = OpTypeVoid +%void_fn_type = OpTypeFunction %void +%v4f_fn_type = OpTypeFunction %v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add +%dbg_a = OpExtInst %void %ext DebugLocalVariable %a_name %dbg_v4f %src 1 13 %dbg_add FlagIsLocal 0 +%dbg_b = OpExtInst %void %ext DebugLocalVariable %b_name %dbg_v4f %src 1 20 %dbg_add FlagIsLocal 1 +%add_lb = OpExtInst %void %ext DebugLexicalBlock %src 1 23 %dbg_add +%main = OpFunction %void None %void_fn_type +%main_bb = OpLabel +%param_a = OpVariable %_ptr_Function_v4float Function +%param_b = OpVariable %_ptr_Function_v4float Function +%scope0 = OpExtInst %void %ext DebugScope %dbg_main +OpStore %param_a %v4f1 +OpStore %param_b %v4f2 +%result = OpFunctionCall %v4float %add %param_a %param_b +OpStore %out_var_SV_TARGET %result +OpReturn +OpFunctionEnd +%add = OpFunction %v4float None %add_fn_type +%scope1 = OpExtInst %void %ext DebugScope %dbg_add +%a = OpFunctionParameter %_ptr_Function_v4float +%b = OpFunctionParameter %_ptr_Function_v4float +%decl0 = OpExtInst %void %ext DebugDeclare %dbg_a %a %null_expr +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_b %b %null_expr +%add_bb = OpLabel +%scope2 = OpExtInst %void %ext DebugScope %add_lb +%a_val = OpLoad %v4float %a +%b_val = OpLoad %v4float %b +%res = OpFAdd %v4float %a_val %b_val +OpReturnValue %res +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, DebugDeclareForCalleeLocalVar) { + // Check that InlinePass correctly generates DebugDeclare instructions + // for callee function's local variables and maps them to corresponding + // local variables of caller function. + const std::string text = R"( +; CHECK: [[add:%\d+]] = OpString "add" +; CHECK: [[foo:%\d+]] = OpString "foo" +; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]] +; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[foo]] {{%\d+}} {{%\d+}} 2 2 [[dbg_add]] +; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5 + +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]] +; CHECK: [[new_foo:%\d+]] = OpVariable %_ptr_Function_v4float Function + +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]] +; CHECK: [[a_val:%\d+]] = OpLoad %v4float +; CHECK: [[b_val:%\d+]] = OpLoad %v4float +; CHECK: [[res:%\d+]] = OpFAdd %v4float [[a_val]] [[b_val]] +; CHECK: OpStore [[new_foo]] [[res]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_foo]] [[new_foo]] + +OpCapability Shader +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "ps.hlsl" +OpSource HLSL 600 %file_name +%float_name = OpString "float" +%main_name = OpString "main" +%add_name = OpString "add" +%foo_name = OpString "foo" +OpDecorate %in_var_COLOR Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%v4float = OpTypeVector %float 4 +%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float +%void = OpTypeVoid +%void_fn_type = OpTypeFunction %void +%v4f_fn_type = OpTypeFunction %v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add +%dbg_foo = OpExtInst %void %ext DebugLocalVariable %foo_name %dbg_v4f %src 2 2 %dbg_add FlagIsLocal +%main = OpFunction %void None %void_fn_type +%main_bb = OpLabel +%param_a = OpVariable %_ptr_Function_v4float Function +%param_b = OpVariable %_ptr_Function_v4float Function +%scope0 = OpExtInst %void %ext DebugScope %dbg_main +OpStore %param_a %v4f1 +OpStore %param_b %v4f2 +%result = OpFunctionCall %v4float %add %param_a %param_b +OpStore %out_var_SV_TARGET %result +OpReturn +OpFunctionEnd +%add = OpFunction %v4float None %add_fn_type +%scope1 = OpExtInst %void %ext DebugScope %dbg_add +%a = OpFunctionParameter %_ptr_Function_v4float +%b = OpFunctionParameter %_ptr_Function_v4float +%add_bb = OpLabel +%foo = OpVariable %_ptr_Function_v4float Function +%a_val = OpLoad %v4float %a +%b_val = OpLoad %v4float %b +%res = OpFAdd %v4float %a_val %b_val +OpStore %foo %res +%decl = OpExtInst %void %ext DebugDeclare %dbg_foo %foo %null_expr +%foo_val = OpLoad %v4float %foo +OpReturnValue %foo_val +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, DebugDeclareMultiple) { + // Check that InlinePass correctly generates DebugDeclare instructions + // for callee function's parameters and maps them to corresponding + // local variables of caller function. + const std::string text = R"( +; CHECK: [[add:%\d+]] = OpString "add" +; CHECK: [[a:%\d+]] = OpString "a" +; CHECK: [[b:%\d+]] = OpString "b" +; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]] +; CHECK: [[dbg_a:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[a]] +; CHECK: [[dbg_b:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[b]] +; CHECK: OpFunction +; CHECK-NOT: OpFunctionEnd +; CHECK: OpStore [[param_a:%\d+]] +; CHECK: OpStore [[param_b:%\d+]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_a]] [[param_a]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_b]] [[param_b]] +; CHECK: [[a_val:%\d+]] = OpLoad %v4float [[param_a]] +; CHECK: OpStore [[foo:%\d+]] [[a_val]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugValue [[dbg_a]] [[foo]] + +OpCapability Shader +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "ps.hlsl" +OpSource HLSL 600 %file_name +%float_name = OpString "float" +%main_name = OpString "main" +%add_name = OpString "add" +%a_name = OpString "a" +%b_name = OpString "b" +OpDecorate %in_var_COLOR Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%v4float = OpTypeVector %float 4 +%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float +%void = OpTypeVoid +%void_fn_type = OpTypeFunction %void +%v4f_fn_type = OpTypeFunction %v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add +%dbg_a = OpExtInst %void %ext DebugLocalVariable %a_name %dbg_v4f %src 1 13 %dbg_add FlagIsLocal 0 +%dbg_b = OpExtInst %void %ext DebugLocalVariable %b_name %dbg_v4f %src 1 20 %dbg_add FlagIsLocal 1 +%main = OpFunction %void None %void_fn_type +%main_bb = OpLabel +%param_a = OpVariable %_ptr_Function_v4float Function +%param_b = OpVariable %_ptr_Function_v4float Function +%scope0 = OpExtInst %void %ext DebugScope %dbg_main +OpStore %param_a %v4f1 +OpStore %param_b %v4f2 +%result = OpFunctionCall %v4float %add %param_a %param_b +OpStore %out_var_SV_TARGET %result +OpReturn +OpFunctionEnd +%add = OpFunction %v4float None %add_fn_type +%scope1 = OpExtInst %void %ext DebugScope %dbg_add +%a = OpFunctionParameter %_ptr_Function_v4float +%b = OpFunctionParameter %_ptr_Function_v4float +%decl0 = OpExtInst %void %ext DebugDeclare %dbg_a %a %null_expr +%add_bb = OpLabel +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_b %b %null_expr +%foo = OpVariable %_ptr_Function_v4float Function +%a_val = OpLoad %v4float %a +OpStore %foo %a_val +%dbg_val = OpExtInst %void %ext DebugValue %dbg_a %foo %null_expr +%b_val = OpLoad %v4float %b +%res = OpFAdd %v4float %a_val %b_val +OpReturnValue %res +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, DebugValueForFunctionCallReturn) { + // Check that InlinePass correctly generates DebugValue instruction + // for function call's return value and maps it to a corresponding + // value in the caller function. + const std::string text = R"( +; CHECK: [[main:%\d+]] = OpString "main" +; CHECK: [[add:%\d+]] = OpString "add" +; CHECK: [[result:%\d+]] = OpString "result" +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[main]] +; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]] +; CHECK: [[dbg_result:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[result]] {{%\d+}} {{%\d+}} 6 2 [[dbg_main]] +; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5 +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]] +; CHECK: [[a_val:%\d+]] = OpLoad %v4float +; CHECK: [[b_val:%\d+]] = OpLoad %v4float +; CHECK: [[res:%\d+]] = OpFAdd %v4float [[a_val]] [[b_val]] +; CHECK: OpStore [[new_result:%\d+]] [[res]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK: [[result_val:%\d+]] = OpLoad %v4float [[new_result]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugValue [[dbg_result]] [[result_val]] + +OpCapability Shader +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "ps.hlsl" +OpSource HLSL 600 %file_name +%float_name = OpString "float" +%main_name = OpString "main" +%add_name = OpString "add" +%result_name = OpString "result" +OpDecorate %in_var_COLOR Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%v4float = OpTypeVector %float 4 +%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float +%void = OpTypeVoid +%void_fn_type = OpTypeFunction %void +%v4f_fn_type = OpTypeFunction %v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add +%dbg_result = OpExtInst %void %ext DebugLocalVariable %result_name %dbg_v4f %src 6 2 %dbg_main FlagIsLocal +%main = OpFunction %void None %void_fn_type +%main_bb = OpLabel +%param_a = OpVariable %_ptr_Function_v4float Function +%param_b = OpVariable %_ptr_Function_v4float Function +%scope0 = OpExtInst %void %ext DebugScope %dbg_main +OpStore %param_a %v4f1 +OpStore %param_b %v4f2 +%result = OpFunctionCall %v4float %add %param_a %param_b +%value = OpExtInst %void %ext DebugValue %dbg_result %result %null_expr +OpStore %out_var_SV_TARGET %result +OpReturn +OpFunctionEnd +%add = OpFunction %v4float None %add_fn_type +%scope1 = OpExtInst %void %ext DebugScope %dbg_add +%a = OpFunctionParameter %_ptr_Function_v4float +%b = OpFunctionParameter %_ptr_Function_v4float +%add_bb = OpLabel +%a_val = OpLoad %v4float %a +%b_val = OpLoad %v4float %b +%res = OpFAdd %v4float %a_val %b_val +OpReturnValue %res +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, NestedWithAnExistingDebugInlinedAt) { + // When a DebugScope instruction in a callee function already has a + // DebugInlinedAt information, we have to create a recursive + // DebugInlinedAt chain. See inlined_to_zoo and inlined_to_bar in + // the following code. + const std::string text = R"( +; CHECK: [[main:%\d+]] = OpString "main" +; CHECK: [[foo:%\d+]] = OpString "foo" +; CHECK: [[bar:%\d+]] = OpString "bar" +; CHECK: [[zoo:%\d+]] = OpString "zoo" +; CHECK: [[v4f1:%\d+]] = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +; CHECK: [[v4f2:%\d+]] = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +; CHECK: [[v4f3:%\d+]] = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3 +; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[main]] +; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[foo]] +; CHECK: [[dbg_bar:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[bar]] +; CHECK: [[dbg_zoo:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[zoo]] +; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 10 [[dbg_main]] +; CHECK: [[inlined_to_zoo:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 7 [[dbg_zoo]] [[inlined_to_main]] +; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 10 [[dbg_main]] +; CHECK: [[inlined_to_bar:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 4 [[dbg_bar]] [[inlined_to_zoo]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_foo]] [[inlined_to_bar]] +; CHECK: OpStore [[foo_ret:%\d+]] [[v4f1]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_bar]] [[inlined_to_zoo]] +; CHECK: [[foo_ret_val:%\d+]] = OpLoad %v4float [[foo_ret]] +; CHECK: [[bar_ret:%\d+]] = OpFAdd %v4float [[foo_ret_val]] [[v4f2]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_zoo]] [[inlined_to_main]] +; CHECK: [[zoo_result:%\d+]] = OpFAdd %v4float [[bar_ret]] [[v4f3]] +; CHECK: OpStore [[zoo_ret:%\d+]] [[zoo_result]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK: [[zoo_ret_val:%\d+]] = OpLoad %v4float [[zoo_ret]] +; CHECK: {{%\d+}} = OpFAdd %v4float [[zoo_ret_val]] {{%\d+}} + +OpCapability Shader +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "ps.hlsl" +OpSource HLSL 600 %file_name +%float_name = OpString "float" +%main_name = OpString "main" +%foo_name = OpString "foo" +%bar_name = OpString "bar" +%zoo_name = OpString "zoo" +OpDecorate %in_var_COLOR Location 0 +OpDecorate %out_var_SV_TARGET Location 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%float_3 = OpConstant %float 3 +%v4float = OpTypeVector %float 4 +%v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 +%v4f3 = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%void_fn_type = OpTypeFunction %void +%v4f_fn_type = OpTypeFunction %v4float +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%foo_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 10 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_foo = OpExtInst %void %ext DebugFunction %foo_name %foo_ty %src 1 1 %cu %foo_name FlagIsProtected|FlagIsPrivate 1 %foo +%dbg_bar = OpExtInst %void %ext DebugFunction %bar_name %foo_ty %src 4 1 %cu %bar_name FlagIsProtected|FlagIsPrivate 4 %bar +%dbg_zoo = OpExtInst %void %ext DebugFunction %zoo_name %foo_ty %src 7 1 %cu %zoo_name FlagIsProtected|FlagIsPrivate 7 %zoo +%inlined_to_zoo = OpExtInst %void %ext DebugInlinedAt 7 %dbg_zoo +%main = OpFunction %void None %void_fn_type +%main_bb = OpLabel +%scope0 = OpExtInst %void %ext DebugScope %dbg_main +%zoo_val = OpFunctionCall %v4float %zoo +%color = OpLoad %v4float %in_var_COLOR +%result = OpFAdd %v4float %zoo_val %color +OpStore %out_var_SV_TARGET %result +OpReturn +OpFunctionEnd +%foo = OpFunction %v4float None %v4f_fn_type +%scope1 = OpExtInst %void %ext DebugScope %dbg_foo +%foo_bb = OpLabel +OpReturnValue %v4f1 +OpFunctionEnd +%zoo = OpFunction %v4float None %v4f_fn_type +%scope3 = OpExtInst %void %ext DebugScope %dbg_zoo +%zoo_bb = OpLabel +%scope2 = OpExtInst %void %ext DebugScope %dbg_bar %inlined_to_zoo +%foo_val = OpFunctionCall %v4float %foo +%bar_val = OpFAdd %v4float %foo_val %v4f2 +%scope4 = OpExtInst %void %ext DebugScope %dbg_zoo +%zoo_ret = OpFAdd %v4float %bar_val %v4f3 +OpReturnValue %zoo_ret +OpFunctionEnd +%bar = OpFunction %v4float None %v4f_fn_type +%scope5 = OpExtInst %void %ext DebugScope %dbg_bar +%bar_bb = OpLabel +%foo_val0 = OpFunctionCall %v4float %foo +%bar_ret = OpFAdd %v4float %foo_val0 %v4f2 +OpReturnValue %bar_ret +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(InlineTest, UsingVoidFunctionResult) { + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %void +; CHECK: OpFunction +; CHECK: OpCopyObject %void [[undef]] +; CHECK: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpFunctionCall %2 %6 + %9 = OpCopyObject %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// Empty modules +// Modules without function definitions +// Modules in which all functions do not call other functions +// Caller and callee both accessing the same global variable +// Functions with OpLine & OpNoLine +// Others? + +// TODO(dneto): Test suggestions from code review +// https://github.com/KhronosGroup/SPIRV-Tools/pull/534 +// +// Callee function returns a value generated outside the callee, +// e.g. a constant value. This might exercise some logic not yet +// exercised by the current tests: the false branch in the "if" +// inside the SpvOpReturnValue case in InlinePass::GenInlineCode? +// SampledImage before function call, but callee is only single block. +// Then the SampledImage instruction is not cloned. Documents existing +// behaviour. +// SampledImage after function call. It is not cloned or changed. + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/insert_extract_elim_test.cpp b/third_party/spirv-tools/test/opt/insert_extract_elim_test.cpp new file mode 100644 index 0000000..c516975 --- /dev/null +++ b/third_party/spirv-tools/test/opt/insert_extract_elim_test.cpp @@ -0,0 +1,900 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "source/opt/simplification_pass.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InsertExtractElimTest = PassTest<::testing::Test>; + +TEST_F(InsertExtractElimTest, Simple) { + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // gl_FragColor = s0.v1; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%17 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +%19 = OpLoad %S_t %s0 +%20 = OpCompositeInsert %S_t %18 %19 1 +OpStore %s0 %20 +%21 = OpCompositeExtract %v4float %20 1 +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%17 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +%19 = OpLoad %S_t %s0 +%20 = OpCompositeInsert %S_t %18 %19 1 +OpStore %s0 %20 +OpStore %gl_FragColor %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(InsertExtractElimTest, OptimizeAcrossNonConflictingInsert) { + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // s0.v0[2] = 0.0; + // gl_FragColor = s0.v1; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%float_0 = OpConstant %float 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%18 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%19 = OpLoad %v4float %BaseColor +%20 = OpLoad %S_t %s0 +%21 = OpCompositeInsert %S_t %19 %20 1 +%22 = OpCompositeInsert %S_t %float_0 %21 0 2 +OpStore %s0 %22 +%23 = OpCompositeExtract %v4float %22 1 +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%18 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%19 = OpLoad %v4float %BaseColor +%20 = OpLoad %S_t %s0 +%21 = OpCompositeInsert %S_t %19 %20 1 +%22 = OpCompositeInsert %S_t %float_0 %21 0 2 +OpStore %s0 %22 +OpStore %gl_FragColor %19 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(InsertExtractElimTest, OptimizeOpaque) { + // SPIR-V not representable in GLSL; not generatable from HLSL + // for the moment. + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %outColor %texCoords +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%14 = OpTypeImage %float 2D 0 0 0 1 Unknown +%15 = OpTypeSampledImage %14 +%S_t = OpTypeStruct %v2float %v2float %15 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%17 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 +%_ptr_Function_15 = OpTypePointer Function %15 +%sampler15 = OpVariable %_ptr_UniformConstant_15 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%25 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%26 = OpLoad %v2float %texCoords +%27 = OpLoad %S_t %s0 +%28 = OpCompositeInsert %S_t %26 %27 0 +%29 = OpLoad %15 %sampler15 +%30 = OpCompositeInsert %S_t %29 %28 2 +OpStore %s0 %30 +%31 = OpCompositeExtract %15 %30 2 +%32 = OpCompositeExtract %v2float %30 0 +%33 = OpImageSampleImplicitLod %v4float %31 %32 +OpStore %outColor %33 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%25 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%26 = OpLoad %v2float %texCoords +%27 = OpLoad %S_t %s0 +%28 = OpCompositeInsert %S_t %26 %27 0 +%29 = OpLoad %15 %sampler15 +%30 = OpCompositeInsert %S_t %29 %28 2 +OpStore %s0 %30 +%33 = OpImageSampleImplicitLod %v4float %29 %26 +OpStore %outColor %33 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(InsertExtractElimTest, OptimizeNestedStruct) { + // The following HLSL has been pre-optimized to get the SPIR-V: + // struct S0 + // { + // int x; + // SamplerState ss; + // }; + // + // struct S1 + // { + // float b; + // S0 s0; + // }; + // + // struct S2 + // { + // int a1; + // S1 resources; + // }; + // + // SamplerState samp; + // Texture2D tex; + // + // float4 main(float4 vpos : VPOS) : COLOR0 + // { + // S1 s1; + // S2 s2; + // s1.s0.ss = samp; + // s2.resources = s1; + // return tex.Sample(s2.resources.s0.ss, float2(0.5)); + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_entryPointOutput +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %S0 "S0" +OpMemberName %S0 0 "x" +OpMemberName %S0 1 "ss" +OpName %S1 "S1" +OpMemberName %S1 0 "b" +OpMemberName %S1 1 "s0" +OpName %samp "samp" +OpName %S2 "S2" +OpMemberName %S2 0 "a1" +OpMemberName %S2 1 "resources" +OpName %tex "tex" +OpName %_entryPointOutput "@entryPointOutput" +OpDecorate %samp DescriptorSet 0 +OpDecorate %tex DescriptorSet 0 +OpDecorate %_entryPointOutput Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%14 = OpTypeFunction %v4float %_ptr_Function_v4float +%int = OpTypeInt 32 1 +%16 = OpTypeSampler +%S0 = OpTypeStruct %int %16 +%S1 = OpTypeStruct %float %S0 +%_ptr_Function_S1 = OpTypePointer Function %S1 +%int_1 = OpConstant %int 1 +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 +%samp = OpVariable %_ptr_UniformConstant_16 UniformConstant +%_ptr_Function_16 = OpTypePointer Function %16 +%S2 = OpTypeStruct %int %S1 +%_ptr_Function_S2 = OpTypePointer Function %S2 +%22 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 +%tex = OpVariable %_ptr_UniformConstant_22 UniformConstant +%24 = OpTypeSampledImage %22 +%v2float = OpTypeVector %float 2 +%float_0_5 = OpConstant %float 0.5 +%27 = OpConstantComposite %v2float %float_0_5 %float_0_5 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %10 +%30 = OpLabel +%31 = OpVariable %_ptr_Function_S1 Function +%32 = OpVariable %_ptr_Function_S2 Function +%33 = OpLoad %16 %samp +%34 = OpLoad %S1 %31 +%35 = OpCompositeInsert %S1 %33 %34 1 1 +OpStore %31 %35 +%36 = OpLoad %S2 %32 +%37 = OpCompositeInsert %S2 %35 %36 1 +OpStore %32 %37 +%38 = OpLoad %22 %tex +%39 = OpCompositeExtract %16 %37 1 1 1 +%40 = OpSampledImage %24 %38 %39 +%41 = OpImageSampleImplicitLod %v4float %40 %27 +OpStore %_entryPointOutput %41 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%30 = OpLabel +%31 = OpVariable %_ptr_Function_S1 Function +%32 = OpVariable %_ptr_Function_S2 Function +%33 = OpLoad %16 %samp +%34 = OpLoad %S1 %31 +%35 = OpCompositeInsert %S1 %33 %34 1 1 +OpStore %31 %35 +%36 = OpLoad %S2 %32 +%37 = OpCompositeInsert %S2 %35 %36 1 +OpStore %32 %37 +%38 = OpLoad %22 %tex +%40 = OpSampledImage %24 %38 %33 +%41 = OpImageSampleImplicitLod %v4float %40 %27 +OpStore %_entryPointOutput %41 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(InsertExtractElimTest, ConflictingInsertPreventsOptimization) { + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // s0.v1[2] = 0.0; + // gl_FragColor = s0.v1; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%float_0 = OpConstant %float 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %8 +%18 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%19 = OpLoad %v4float %BaseColor +%20 = OpLoad %S_t %s0 +%21 = OpCompositeInsert %S_t %19 %20 1 +%22 = OpCompositeInsert %S_t %float_0 %21 1 2 +OpStore %s0 %22 +%23 = OpCompositeExtract %v4float %22 1 +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(InsertExtractElimTest, ConflictingInsertPreventsOptimization2) { + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1[1] = 1.0; // dead + // s0.v1 = Baseline; + // gl_FragColor = vec4(s0.v1[1], 0.0, 0.0, 0.0); + // } + + const std::string before_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%float_1 = OpConstant %float 1 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +)"; + + const std::string after_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%float_1 = OpConstant %float 1 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%23 = OpLoad %S_t %s0 +%24 = OpCompositeInsert %S_t %float_1 %23 1 1 +%25 = OpLoad %v4float %BaseColor +%26 = OpCompositeInsert %S_t %25 %24 1 +%27 = OpCompositeExtract %float %26 1 1 +%28 = OpCompositeConstruct %v4float %27 %float_0 %float_0 %float_0 +OpStore %gl_FragColor %28 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%23 = OpLoad %S_t %s0 +%24 = OpCompositeInsert %S_t %float_1 %23 1 1 +%25 = OpLoad %v4float %BaseColor +%26 = OpCompositeInsert %S_t %25 %24 1 +%27 = OpCompositeExtract %float %25 1 +%28 = OpCompositeConstruct %v4float %27 %float_0 %float_0 %float_0 +OpStore %gl_FragColor %28 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_predefs + before, + after_predefs + after, true, true); +} + +TEST_F(InsertExtractElimTest, MixWithConstants) { + // Extract component of FMix with 0.0 or 1.0 as the a-value. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in float bc; + // layout (location=1) in float bc2; + // layout (location=2) in float m; + // layout (location=3) in float m2; + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // vec4 bcv = vec4(bc, bc2, 0.0, 1.0); + // vec4 bcv2 = vec4(bc2, bc, 1.0, 0.0); + // vec4 v = mix(bcv, bcv2, vec4(0.0,1.0,m,m2)); + // OutColor = vec4(v.y); + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %bc %bc2 %m %m2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %bc "bc" +OpName %bc2 "bc2" +OpName %m "m" +OpName %m2 "m2" +OpName %OutColor "OutColor" +OpDecorate %bc Location 0 +OpDecorate %bc2 Location 1 +OpDecorate %m Location 2 +OpDecorate %m2 Location 3 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_float = OpTypePointer Input %float +%bc = OpVariable %_ptr_Input_float Input +%bc2 = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%m = OpVariable %_ptr_Input_float Input +%m2 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%20 = OpLoad %float %bc +%21 = OpLoad %float %bc2 +%22 = OpCompositeConstruct %v4float %20 %21 %float_0 %float_1 +%23 = OpLoad %float %bc2 +%24 = OpLoad %float %bc +%25 = OpCompositeConstruct %v4float %23 %24 %float_1 %float_0 +%26 = OpLoad %float %m +%27 = OpLoad %float %m2 +%28 = OpCompositeConstruct %v4float %float_0 %float_1 %26 %27 +%29 = OpExtInst %v4float %1 FMix %22 %25 %28 +%30 = OpCompositeExtract %float %29 1 +%31 = OpCompositeConstruct %v4float %30 %30 %30 %30 +OpStore %OutColor %31 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%20 = OpLoad %float %bc +%21 = OpLoad %float %bc2 +%22 = OpCompositeConstruct %v4float %20 %21 %float_0 %float_1 +%23 = OpLoad %float %bc2 +%24 = OpLoad %float %bc +%25 = OpCompositeConstruct %v4float %23 %24 %float_1 %float_0 +%26 = OpLoad %float %m +%27 = OpLoad %float %m2 +%28 = OpCompositeConstruct %v4float %float_0 %float_1 %26 %27 +%29 = OpExtInst %v4float %1 FMix %22 %25 %28 +%31 = OpCompositeConstruct %v4float %24 %24 %24 %24 +OpStore %OutColor %31 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, + true, true); +} + +TEST_F(InsertExtractElimTest, VectorShuffle1) { + // Extract component from first vector in VectorShuffle + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in float bc; + // layout (location=1) in float bc2; + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // vec4 bcv = vec4(bc, bc2, 0.0, 1.0); + // vec4 v = bcv.zwxy; + // OutColor = vec4(v.y); + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %bc %bc2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %bc "bc" +OpName %bc2 "bc2" +OpName %OutColor "OutColor" +OpDecorate %bc Location 0 +OpDecorate %bc2 Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_float = OpTypePointer Input %float +%bc = OpVariable %_ptr_Input_float Input +%bc2 = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +)"; + + const std::string predefs_after = predefs_before + + "%24 = OpConstantComposite %v4float " + "%float_1 %float_1 %float_1 %float_1\n"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%18 = OpLoad %float %bc +%19 = OpLoad %float %bc2 +%20 = OpCompositeConstruct %v4float %18 %19 %float_0 %float_1 +%21 = OpVectorShuffle %v4float %20 %20 2 3 0 1 +%22 = OpCompositeExtract %float %21 1 +%23 = OpCompositeConstruct %v4float %22 %22 %22 %22 +OpStore %OutColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%18 = OpLoad %float %bc +%19 = OpLoad %float %bc2 +%20 = OpCompositeConstruct %v4float %18 %19 %float_0 %float_1 +%21 = OpVectorShuffle %v4float %20 %20 2 3 0 1 +OpStore %OutColor %24 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs_before + before, + predefs_after + after, true, true); +} + +TEST_F(InsertExtractElimTest, VectorShuffle2) { + // Extract component from second vector in VectorShuffle + // Identical to test VectorShuffle1 except for the vector + // shuffle index of 7. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in float bc; + // layout (location=1) in float bc2; + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // vec4 bcv = vec4(bc, bc2, 0.0, 1.0); + // vec4 v = bcv.zwxy; + // OutColor = vec4(v.y); + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %bc %bc2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %bc "bc" +OpName %bc2 "bc2" +OpName %OutColor "OutColor" +OpDecorate %bc Location 0 +OpDecorate %bc2 Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_float = OpTypePointer Input %float +%bc = OpVariable %_ptr_Input_float Input +%bc2 = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %bc %bc2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %bc "bc" +OpName %bc2 "bc2" +OpName %OutColor "OutColor" +OpDecorate %bc Location 0 +OpDecorate %bc2 Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_float = OpTypePointer Input %float +%bc = OpVariable %_ptr_Input_float Input +%bc2 = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%24 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%18 = OpLoad %float %bc +%19 = OpLoad %float %bc2 +%20 = OpCompositeConstruct %v4float %18 %19 %float_0 %float_1 +%21 = OpVectorShuffle %v4float %20 %20 2 7 0 1 +%22 = OpCompositeExtract %float %21 1 +%23 = OpCompositeConstruct %v4float %22 %22 %22 %22 +OpStore %OutColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%17 = OpLabel +%18 = OpLoad %float %bc +%19 = OpLoad %float %bc2 +%20 = OpCompositeConstruct %v4float %18 %19 %float_0 %float_1 +%21 = OpVectorShuffle %v4float %20 %20 2 7 0 1 +OpStore %OutColor %24 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs_before + before, + predefs_after + after, true, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/inst_bindless_check_test.cpp b/third_party/spirv-tools/test/opt/inst_bindless_check_test.cpp new file mode 100644 index 0000000..67a4968 --- /dev/null +++ b/third_party/spirv-tools/test/opt/inst_bindless_check_test.cpp @@ -0,0 +1,8488 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Bindless Check Instrumentation Tests. + +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InstBindlessTest = PassTest<::testing::Test>; + +TEST_F(InstBindlessTest, NoInstrumentConstIndexInbounds) { + // Texture2D g_tColor[128]; + // + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // + // ps_output.vColor = g_tColor[ 37 ].Sample(g_sAniso, i.vTextureCoords.xy); + // return ps_output; + // } + + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_37 = OpConstant %int 37 +%15 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_15_uint_128 = OpTypeArray %15 %uint_128 +%_ptr_UniformConstant__arr_15_uint_128 = OpTypePointer UniformConstant %_arr_15_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_15_uint_128 UniformConstant +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 +%21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%g_sAniso = OpVariable %_ptr_UniformConstant_21 UniformConstant +%23 = OpTypeSampledImage %15 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%MainPs = OpFunction %void None %8 +%26 = OpLabel +%27 = OpLoad %v2float %i_vTextureCoords +%28 = OpAccessChain %_ptr_UniformConstant_15 %g_tColor %int_37 +%29 = OpLoad %15 %28 +%30 = OpLoad %21 %g_sAniso +%31 = OpSampledImage %23 %29 %30 +%32 = OpImageSampleImplicitLod %v4float %31 %27 +OpStore %_entryPointOutput_vColor %32 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, before, true, true, 7u, + 23u, false, false); +} + +TEST_F(InstBindlessTest, NoInstrumentNonBindless) { + // This test verifies that the pass will correctly not instrument vanilla + // texture sample. + // + // Texture2D g_tColor; + // + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // ps_output.vColor = + // g_tColor.Sample(g_sAniso, i.vTextureCoords.xy); + // return ps_output; + // } + + const std::string whole_file = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 +%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant +%14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 +%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant +%16 = OpTypeSampledImage %12 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%MainPs = OpFunction %void None %8 +%19 = OpLabel +%20 = OpLoad %v2float %i_vTextureCoords +%21 = OpLoad %12 %g_tColor +%22 = OpLoad %14 %g_sAniso +%23 = OpSampledImage %16 %21 %22 +%24 = OpImageSampleImplicitLod %v4float %23 %20 +OpStore %_entryPointOutput_vColor %24 +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(whole_file, whole_file, true, + true, 7u, 23u, false, false); +} + +TEST_F(InstBindlessTest, Simple) { + // Texture2D g_tColor[128]; + // + // layout(push_constant) cbuffer PerViewConstantBuffer_t + // { + // uint g_nDataIdx; + // }; + // + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // ps_output.vColor = + // g_tColor[ g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy); + // return ps_output; + // } + + const std::string entry_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +)"; + + const std::string entry_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +)"; + + const std::string names_annots = + R"(OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +)"; + + const std::string new_annots = + R"(OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_55 Block +OpMemberDecorate %_struct_55 0 Offset 0 +OpMemberDecorate %_struct_55 1 Offset 4 +OpDecorate %57 DescriptorSet 7 +OpDecorate %57 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +)"; + + const std::string consts_types_vars = + R"(%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%16 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_16_uint_128 = OpTypeArray %16 %uint_128 +%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 +%24 = OpTypeSampler +%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24 +%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant +%26 = OpTypeSampledImage %16 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string new_consts_types_vars = + R"(%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%48 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_55 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 +%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_56 = OpConstant %uint 56 +%103 = OpConstantNull %v4float +)"; + + const std::string func_pt1 = + R"(%MainPs = OpFunction %void None %10 +%29 = OpLabel +%30 = OpLoad %v2float %i_vTextureCoords +%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%32 = OpLoad %uint %31 +%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32 +%34 = OpLoad %16 %33 +%35 = OpLoad %24 %g_sAniso +%36 = OpSampledImage %26 %34 %35 +)"; + + const std::string func_pt2_before = + R"(%37 = OpImageSampleImplicitLod %v4float %36 %30 +OpStore %_entryPointOutput_vColor %37 +OpReturn +OpFunctionEnd +)"; + + const std::string func_pt2_after = + R"(%40 = OpULessThan %bool %32 %uint_128 +OpSelectionMerge %41 None +OpBranchConditional %40 %42 %43 +%42 = OpLabel +%44 = OpLoad %16 %33 +%45 = OpSampledImage %26 %44 %35 +%46 = OpImageSampleImplicitLod %v4float %45 %30 +OpBranch %41 +%43 = OpLabel +%102 = OpFunctionCall %void %47 %uint_56 %uint_0 %32 %uint_128 +OpBranch %41 +%41 = OpLabel +%104 = OpPhi %v4float %46 %42 %103 %43 +OpStore %_entryPointOutput_vColor %104 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%47 = OpFunction %void None %48 +%49 = OpFunctionParameter %uint +%50 = OpFunctionParameter %uint +%51 = OpFunctionParameter %uint +%52 = OpFunctionParameter %uint +%53 = OpLabel +%59 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 +%62 = OpAtomicIAdd %uint %59 %uint_4 %uint_0 %uint_10 +%63 = OpIAdd %uint %62 %uint_10 +%64 = OpArrayLength %uint %57 1 +%65 = OpULessThanEqual %bool %63 %64 +OpSelectionMerge %66 None +OpBranchConditional %65 %67 %66 +%67 = OpLabel +%68 = OpIAdd %uint %62 %uint_0 +%70 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %68 +OpStore %70 %uint_10 +%72 = OpIAdd %uint %62 %uint_1 +%73 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %72 +OpStore %73 %uint_23 +%75 = OpIAdd %uint %62 %uint_2 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 +OpStore %76 %49 +%78 = OpIAdd %uint %62 %uint_3 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %78 +OpStore %79 %uint_4 +%82 = OpLoad %v4float %gl_FragCoord +%84 = OpBitcast %v4uint %82 +%85 = OpCompositeExtract %uint %84 0 +%86 = OpIAdd %uint %62 %uint_4 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %86 +OpStore %87 %85 +%88 = OpCompositeExtract %uint %84 1 +%90 = OpIAdd %uint %62 %uint_5 +%91 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %90 +OpStore %91 %88 +%93 = OpIAdd %uint %62 %uint_7 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %93 +OpStore %94 %50 +%96 = OpIAdd %uint %62 %uint_8 +%97 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %96 +OpStore %97 %51 +%99 = OpIAdd %uint %62 %uint_9 +%100 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %99 +OpStore %100 %52 +OpBranch %66 +%66 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + entry_before + names_annots + consts_types_vars + func_pt1 + + func_pt2_before, + entry_after + names_annots + new_annots + consts_types_vars + + new_consts_types_vars + func_pt1 + func_pt2_after + output_func, + true, true, 7u, 23u, false, false); +} + +TEST_F(InstBindlessTest, InstrumentMultipleInstructions) { + // Texture2D g_tColor[128]; + // + // layout(push_constant) cbuffer PerViewConstantBuffer_t + // { + // uint g_nDataIdx; + // uint g_nDataIdx2; + // }; + // + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // + // float t = g_tColor[g_nDataIdx ].Sample(g_sAniso, i.vTextureCoords.xy); + // float t2 = g_tColor[g_nDataIdx2].Sample(g_sAniso, i.vTextureCoords.xy); + // ps_output.vColor = t + t2; + // return ps_output; + // } + + const std::string defs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%17 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_17_uint_128 = OpTypeArray %17 %uint_128 +%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%25 = OpTypeSampler +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 +%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant +%27 = OpTypeSampledImage %17 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 4 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%17 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_17_uint_128 = OpTypeArray %17 %uint_128 +%_ptr_UniformConstant__arr_17_uint_128 = OpTypePointer UniformConstant %_arr_17_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_17_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%25 = OpTypeSampler +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 +%g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant +%27 = OpTypeSampledImage %17 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%56 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_58 = OpConstant %uint 58 +%111 = OpConstantNull %v4float +%uint_64 = OpConstant %uint 64 +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %10 +%30 = OpLabel +%31 = OpLoad %v2float %i_vTextureCoords +%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%33 = OpLoad %uint %32 +%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33 +%35 = OpLoad %17 %34 +%36 = OpLoad %25 %g_sAniso +%37 = OpSampledImage %27 %35 %36 +%38 = OpImageSampleImplicitLod %v4float %37 %31 +%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 +%40 = OpLoad %uint %39 +%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 +%42 = OpLoad %17 %41 +%43 = OpSampledImage %27 %42 %36 +%44 = OpImageSampleImplicitLod %v4float %43 %31 +%45 = OpFAdd %v4float %38 %44 +OpStore %_entryPointOutput_vColor %45 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %10 +%30 = OpLabel +%31 = OpLoad %v2float %i_vTextureCoords +%32 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%33 = OpLoad %uint %32 +%34 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %33 +%35 = OpLoad %17 %34 +%36 = OpLoad %25 %g_sAniso +%37 = OpSampledImage %27 %35 %36 +%48 = OpULessThan %bool %33 %uint_128 +OpSelectionMerge %49 None +OpBranchConditional %48 %50 %51 +%50 = OpLabel +%52 = OpLoad %17 %34 +%53 = OpSampledImage %27 %52 %36 +%54 = OpImageSampleImplicitLod %v4float %53 %31 +OpBranch %49 +%51 = OpLabel +%110 = OpFunctionCall %void %55 %uint_58 %uint_0 %33 %uint_128 +OpBranch %49 +%49 = OpLabel +%112 = OpPhi %v4float %54 %50 %111 %51 +%39 = OpAccessChain %_ptr_PushConstant_uint %_ %int_1 +%40 = OpLoad %uint %39 +%41 = OpAccessChain %_ptr_UniformConstant_17 %g_tColor %40 +%42 = OpLoad %17 %41 +%43 = OpSampledImage %27 %42 %36 +%113 = OpULessThan %bool %40 %uint_128 +OpSelectionMerge %114 None +OpBranchConditional %113 %115 %116 +%115 = OpLabel +%117 = OpLoad %17 %41 +%118 = OpSampledImage %27 %117 %36 +%119 = OpImageSampleImplicitLod %v4float %118 %31 +OpBranch %114 +%116 = OpLabel +%121 = OpFunctionCall %void %55 %uint_64 %uint_0 %40 %uint_128 +OpBranch %114 +%114 = OpLabel +%122 = OpPhi %v4float %119 %115 %111 %116 +%45 = OpFAdd %v4float %112 %122 +OpStore %_entryPointOutput_vColor %45 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%55 = OpFunction %void None %56 +%57 = OpFunctionParameter %uint +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpLabel +%67 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%70 = OpAtomicIAdd %uint %67 %uint_4 %uint_0 %uint_10 +%71 = OpIAdd %uint %70 %uint_10 +%72 = OpArrayLength %uint %65 1 +%73 = OpULessThanEqual %bool %71 %72 +OpSelectionMerge %74 None +OpBranchConditional %73 %75 %74 +%75 = OpLabel +%76 = OpIAdd %uint %70 %uint_0 +%78 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %76 +OpStore %78 %uint_10 +%80 = OpIAdd %uint %70 %uint_1 +%81 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %80 +OpStore %81 %uint_23 +%83 = OpIAdd %uint %70 %uint_2 +%84 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %83 +OpStore %84 %57 +%86 = OpIAdd %uint %70 %uint_3 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %86 +OpStore %87 %uint_4 +%90 = OpLoad %v4float %gl_FragCoord +%92 = OpBitcast %v4uint %90 +%93 = OpCompositeExtract %uint %92 0 +%94 = OpIAdd %uint %70 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %93 +%96 = OpCompositeExtract %uint %92 1 +%98 = OpIAdd %uint %70 %uint_5 +%99 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %98 +OpStore %99 %96 +%101 = OpIAdd %uint %70 %uint_7 +%102 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %101 +OpStore %102 %58 +%104 = OpIAdd %uint %70 %uint_8 +%105 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %104 +OpStore %105 %59 +%107 = OpIAdd %uint %70 %uint_9 +%108 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %107 +OpStore %108 %60 +OpBranch %74 +%74 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + output_func, true, + true, 7u, 23u, false, false); +} + +TEST_F(InstBindlessTest, InstrumentOpImage) { + // This test verifies that the pass will correctly instrument shader + // using OpImage. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpCapability StorageImageReadWithoutFormat +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 0 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%39 = OpTypeSampledImage %20 +%_arr_39_uint_128 = OpTypeArray %39 %uint_128 +%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39 +%_ptr_Input_v2int = OpTypePointer Input %v2int +%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability StorageImageReadWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_51 Block +OpMemberDecorate %_struct_51 0 Offset 0 +OpMemberDecorate %_struct_51 1 Offset 4 +OpDecorate %53 DescriptorSet 7 +OpDecorate %53 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%15 = OpTypeImage %float 2D 0 0 0 0 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%18 = OpTypeSampledImage %15 +%_arr_18_uint_128 = OpTypeArray %18 %uint_128 +%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%_ptr_Input_v2int = OpTypePointer Input %v2int +%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%44 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_51 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_51 = OpTypePointer StorageBuffer %_struct_51 +%53 = OpVariable %_ptr_StorageBuffer__struct_51 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_51 = OpConstant %uint 51 +%99 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2int %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64 +%66 = OpLoad %39 %65 +%75 = OpImage %20 %66 +%71 = OpImageRead %v4float %75 %53 +OpStore %_entryPointOutput_vColor %71 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %9 +%26 = OpLabel +%27 = OpLoad %v2int %i_vTextureCoords +%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%29 = OpLoad %uint %28 +%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29 +%31 = OpLoad %18 %30 +%32 = OpImage %15 %31 +%36 = OpULessThan %bool %29 %uint_128 +OpSelectionMerge %37 None +OpBranchConditional %36 %38 %39 +%38 = OpLabel +%40 = OpLoad %18 %30 +%41 = OpImage %15 %40 +%42 = OpImageRead %v4float %41 %27 +OpBranch %37 +%39 = OpLabel +%98 = OpFunctionCall %void %43 %uint_51 %uint_0 %29 %uint_128 +OpBranch %37 +%37 = OpLabel +%100 = OpPhi %v4float %42 %38 %99 %39 +OpStore %_entryPointOutput_vColor %100 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%43 = OpFunction %void None %44 +%45 = OpFunctionParameter %uint +%46 = OpFunctionParameter %uint +%47 = OpFunctionParameter %uint +%48 = OpFunctionParameter %uint +%49 = OpLabel +%55 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_0 +%58 = OpAtomicIAdd %uint %55 %uint_4 %uint_0 %uint_10 +%59 = OpIAdd %uint %58 %uint_10 +%60 = OpArrayLength %uint %53 1 +%61 = OpULessThanEqual %bool %59 %60 +OpSelectionMerge %62 None +OpBranchConditional %61 %63 %62 +%63 = OpLabel +%64 = OpIAdd %uint %58 %uint_0 +%66 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %64 +OpStore %66 %uint_10 +%68 = OpIAdd %uint %58 %uint_1 +%69 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %68 +OpStore %69 %uint_23 +%71 = OpIAdd %uint %58 %uint_2 +%72 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %71 +OpStore %72 %45 +%74 = OpIAdd %uint %58 %uint_3 +%75 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %74 +OpStore %75 %uint_4 +%78 = OpLoad %v4float %gl_FragCoord +%80 = OpBitcast %v4uint %78 +%81 = OpCompositeExtract %uint %80 0 +%82 = OpIAdd %uint %58 %uint_4 +%83 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %82 +OpStore %83 %81 +%84 = OpCompositeExtract %uint %80 1 +%86 = OpIAdd %uint %58 %uint_5 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %86 +OpStore %87 %84 +%89 = OpIAdd %uint %58 %uint_7 +%90 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %89 +OpStore %90 %46 +%92 = OpIAdd %uint %58 %uint_8 +%93 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %92 +OpStore %93 %47 +%95 = OpIAdd %uint %58 %uint_9 +%96 = OpAccessChain %_ptr_StorageBuffer_uint %53 %uint_1 %95 +OpStore %96 %48 +OpBranch %62 +%62 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + output_func, true, + true, 7u, 23u, false, false); +} + +TEST_F(InstBindlessTest, InstrumentSampledImage) { + // This test verifies that the pass will correctly instrument shader + // using sampled image. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%39 = OpTypeSampledImage %20 +%_arr_39_uint_128 = OpTypeArray %39 %uint_128 +%_ptr_UniformConstant__arr_39_uint_128 = OpTypePointer UniformConstant %_arr_39_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_39_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_39 = OpTypePointer UniformConstant %39 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_49 Block +OpMemberDecorate %_struct_49 0 Offset 0 +OpMemberDecorate %_struct_49 1 Offset 4 +OpDecorate %51 DescriptorSet 7 +OpDecorate %51 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%15 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%18 = OpTypeSampledImage %15 +%_arr_18_uint_128 = OpTypeArray %18 %uint_128 +%_ptr_UniformConstant__arr_18_uint_128 = OpTypePointer UniformConstant %_arr_18_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_18_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%42 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_49 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_49 = OpTypePointer StorageBuffer %_struct_49 +%51 = OpVariable %_ptr_StorageBuffer__struct_49 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_49 = OpConstant %uint 49 +%97 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2float %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_39 %g_tColor %64 +%66 = OpLoad %39 %65 +%71 = OpImageSampleImplicitLod %v4float %66 %53 +OpStore %_entryPointOutput_vColor %71 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %9 +%26 = OpLabel +%27 = OpLoad %v2float %i_vTextureCoords +%28 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%29 = OpLoad %uint %28 +%30 = OpAccessChain %_ptr_UniformConstant_18 %g_tColor %29 +%31 = OpLoad %18 %30 +%35 = OpULessThan %bool %29 %uint_128 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %38 +%37 = OpLabel +%39 = OpLoad %18 %30 +%40 = OpImageSampleImplicitLod %v4float %39 %27 +OpBranch %36 +%38 = OpLabel +%96 = OpFunctionCall %void %41 %uint_49 %uint_0 %29 %uint_128 +OpBranch %36 +%36 = OpLabel +%98 = OpPhi %v4float %40 %37 %97 %38 +OpStore %_entryPointOutput_vColor %98 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%41 = OpFunction %void None %42 +%43 = OpFunctionParameter %uint +%44 = OpFunctionParameter %uint +%45 = OpFunctionParameter %uint +%46 = OpFunctionParameter %uint +%47 = OpLabel +%53 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_0 +%56 = OpAtomicIAdd %uint %53 %uint_4 %uint_0 %uint_10 +%57 = OpIAdd %uint %56 %uint_10 +%58 = OpArrayLength %uint %51 1 +%59 = OpULessThanEqual %bool %57 %58 +OpSelectionMerge %60 None +OpBranchConditional %59 %61 %60 +%61 = OpLabel +%62 = OpIAdd %uint %56 %uint_0 +%64 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %62 +OpStore %64 %uint_10 +%66 = OpIAdd %uint %56 %uint_1 +%67 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %66 +OpStore %67 %uint_23 +%69 = OpIAdd %uint %56 %uint_2 +%70 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %69 +OpStore %70 %43 +%72 = OpIAdd %uint %56 %uint_3 +%73 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %72 +OpStore %73 %uint_4 +%76 = OpLoad %v4float %gl_FragCoord +%78 = OpBitcast %v4uint %76 +%79 = OpCompositeExtract %uint %78 0 +%80 = OpIAdd %uint %56 %uint_4 +%81 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %80 +OpStore %81 %79 +%82 = OpCompositeExtract %uint %78 1 +%84 = OpIAdd %uint %56 %uint_5 +%85 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %84 +OpStore %85 %82 +%87 = OpIAdd %uint %56 %uint_7 +%88 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %87 +OpStore %88 %44 +%90 = OpIAdd %uint %56 %uint_8 +%91 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %90 +OpStore %91 %45 +%93 = OpIAdd %uint %56 %uint_9 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %51 %uint_1 %93 +OpStore %94 %46 +OpBranch %60 +%60 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + output_func, true, + true, 7u, 23u, false, false); +} + +TEST_F(InstBindlessTest, InstrumentImageWrite) { + // This test verifies that the pass will correctly instrument shader + // doing bindless image write. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpCapability StorageImageWriteWithoutFormat +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 0 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%80 = OpConstantNull %v4float +%_arr_20_uint_128 = OpTypeArray %20 %uint_128 +%_ptr_UniformConstant__arr_20_uint_128 = OpTypePointer UniformConstant %_arr_20_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_20_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 +%_ptr_Input_v2int = OpTypePointer Input %v2int +%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability StorageImageWriteWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 3 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_48 Block +OpMemberDecorate %_struct_48 0 Offset 0 +OpMemberDecorate %_struct_48 1 Offset 4 +OpDecorate %50 DescriptorSet 7 +OpDecorate %50 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%v2int = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%16 = OpTypeImage %float 2D 0 0 0 0 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%19 = OpConstantNull %v4float +%_arr_16_uint_128 = OpTypeArray %16 %uint_128 +%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 +%_ptr_Input_v2int = OpTypePointer Input %v2int +%i_vTextureCoords = OpVariable %_ptr_Input_v2int Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%41 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_48 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_48 = OpTypePointer StorageBuffer %_struct_48 +%50 = OpVariable %_ptr_StorageBuffer__struct_48 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_51 = OpConstant %uint 51 +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2int %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64 +%66 = OpLoad %20 %65 +OpImageWrite %66 %53 %80 +OpStore %_entryPointOutput_vColor %80 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %9 +%27 = OpLabel +%28 = OpLoad %v2int %i_vTextureCoords +%29 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%30 = OpLoad %uint %29 +%31 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %30 +%32 = OpLoad %16 %31 +%35 = OpULessThan %bool %30 %uint_128 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %38 +%37 = OpLabel +%39 = OpLoad %16 %31 +OpImageWrite %39 %28 %19 +OpBranch %36 +%38 = OpLabel +%95 = OpFunctionCall %void %40 %uint_51 %uint_0 %30 %uint_128 +OpBranch %36 +%36 = OpLabel +OpStore %_entryPointOutput_vColor %19 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%40 = OpFunction %void None %41 +%42 = OpFunctionParameter %uint +%43 = OpFunctionParameter %uint +%44 = OpFunctionParameter %uint +%45 = OpFunctionParameter %uint +%46 = OpLabel +%52 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_0 +%55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_10 +%56 = OpIAdd %uint %55 %uint_10 +%57 = OpArrayLength %uint %50 1 +%58 = OpULessThanEqual %bool %56 %57 +OpSelectionMerge %59 None +OpBranchConditional %58 %60 %59 +%60 = OpLabel +%61 = OpIAdd %uint %55 %uint_0 +%63 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %61 +OpStore %63 %uint_10 +%65 = OpIAdd %uint %55 %uint_1 +%66 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %65 +OpStore %66 %uint_23 +%68 = OpIAdd %uint %55 %uint_2 +%69 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %68 +OpStore %69 %42 +%71 = OpIAdd %uint %55 %uint_3 +%72 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %71 +OpStore %72 %uint_4 +%75 = OpLoad %v4float %gl_FragCoord +%77 = OpBitcast %v4uint %75 +%78 = OpCompositeExtract %uint %77 0 +%79 = OpIAdd %uint %55 %uint_4 +%80 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %79 +OpStore %80 %78 +%81 = OpCompositeExtract %uint %77 1 +%83 = OpIAdd %uint %55 %uint_5 +%84 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %83 +OpStore %84 %81 +%86 = OpIAdd %uint %55 %uint_7 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %86 +OpStore %87 %43 +%89 = OpIAdd %uint %55 %uint_8 +%90 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %89 +OpStore %90 %44 +%92 = OpIAdd %uint %55 %uint_9 +%93 = OpAccessChain %_ptr_StorageBuffer_uint %50 %uint_1 %92 +OpStore %93 %45 +OpBranch %59 +%59 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + output_func, true, + true, 7u, 23u, false, false); +} + +TEST_F(InstBindlessTest, InstrumentVertexSimple) { + // This test verifies that the pass will correctly instrument shader + // doing bindless image write. This test was created by editing the SPIR-V + // from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %_ %coords2D +OpSource GLSL 450 +OpName %main "main" +OpName %lod "lod" +OpName %coords1D "coords1D" +OpName %gl_PerVertex "gl_PerVertex" +OpMemberName %gl_PerVertex 0 "gl_Position" +OpMemberName %gl_PerVertex 1 "gl_PointSize" +OpMemberName %gl_PerVertex 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex 3 "gl_CullDistance" +OpName %_ "" +OpName %texSampler1D "texSampler1D" +OpName %foo "foo" +OpMemberName %foo 0 "g_idx" +OpName %__0 "" +OpName %coords2D "coords2D" +OpMemberDecorate %gl_PerVertex 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex Block +OpDecorate %texSampler1D DescriptorSet 0 +OpDecorate %texSampler1D Binding 3 +OpMemberDecorate %foo 0 Offset 0 +OpDecorate %foo Block +OpDecorate %__0 DescriptorSet 0 +OpDecorate %__0 Binding 5 +OpDecorate %coords2D Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_3 = OpConstant %float 3 +%float_1_78900003 = OpConstant %float 1.78900003 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex +%_ = OpVariable %_ptr_Output_gl_PerVertex Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%21 = OpTypeImage %float 1D 0 0 0 1 Unknown +%22 = OpTypeSampledImage %21 +%uint_128 = OpConstant %uint 128 +%_arr_22_uint_128 = OpTypeArray %22 %uint_128 +%_ptr_UniformConstant__arr_22_uint_128 = OpTypePointer UniformConstant %_arr_22_uint_128 +%texSampler1D = OpVariable %_ptr_UniformConstant__arr_22_uint_128 UniformConstant +%foo = OpTypeStruct %int +%_ptr_Uniform_foo = OpTypePointer Uniform %foo +%__0 = OpVariable %_ptr_Uniform_foo Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%coords2D = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability Sampled1D +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %_ %coords2D %gl_VertexIndex %gl_InstanceIndex +OpSource GLSL 450 +OpName %main "main" +OpName %lod "lod" +OpName %coords1D "coords1D" +OpName %gl_PerVertex "gl_PerVertex" +OpMemberName %gl_PerVertex 0 "gl_Position" +OpMemberName %gl_PerVertex 1 "gl_PointSize" +OpMemberName %gl_PerVertex 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex 3 "gl_CullDistance" +OpName %_ "" +OpName %texSampler1D "texSampler1D" +OpName %foo "foo" +OpMemberName %foo 0 "g_idx" +OpName %__0 "" +OpName %coords2D "coords2D" +OpMemberDecorate %gl_PerVertex 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex Block +OpDecorate %texSampler1D DescriptorSet 0 +OpDecorate %texSampler1D Binding 3 +OpMemberDecorate %foo 0 Offset 0 +OpDecorate %foo Block +OpDecorate %__0 DescriptorSet 0 +OpDecorate %__0 Binding 5 +OpDecorate %coords2D Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_61 Block +OpMemberDecorate %_struct_61 0 Offset 0 +OpMemberDecorate %_struct_61 1 Offset 4 +OpDecorate %63 DescriptorSet 7 +OpDecorate %63 Binding 0 +OpDecorate %gl_VertexIndex BuiltIn VertexIndex +OpDecorate %gl_InstanceIndex BuiltIn InstanceIndex +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_3 = OpConstant %float 3 +%float_1_78900003 = OpConstant %float 1.78900003 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex +%_ = OpVariable %_ptr_Output_gl_PerVertex Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%24 = OpTypeImage %float 1D 0 0 0 1 Unknown +%25 = OpTypeSampledImage %24 +%uint_128 = OpConstant %uint 128 +%_arr_25_uint_128 = OpTypeArray %25 %uint_128 +%_ptr_UniformConstant__arr_25_uint_128 = OpTypePointer UniformConstant %_arr_25_uint_128 +%texSampler1D = OpVariable %_ptr_UniformConstant__arr_25_uint_128 UniformConstant +%foo = OpTypeStruct %int +%_ptr_Uniform_foo = OpTypePointer Uniform %foo +%__0 = OpVariable %_ptr_Uniform_foo Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%coords2D = OpVariable %_ptr_Input_v2float Input +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%54 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_61 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_61 = OpTypePointer StorageBuffer %_struct_61 +%63 = OpVariable %_ptr_StorageBuffer__struct_61 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_uint = OpTypePointer Input %uint +%gl_VertexIndex = OpVariable %_ptr_Input_uint Input +%gl_InstanceIndex = OpVariable %_ptr_Input_uint Input +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_74 = OpConstant %uint 74 +%106 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%lod = OpVariable %_ptr_Function_float Function +%coords1D = OpVariable %_ptr_Function_float Function +OpStore %lod %float_3 +OpStore %coords1D %float_1_78900003 +%31 = OpAccessChain %_ptr_Uniform_int %__0 %int_0 +%32 = OpLoad %int %31 +%34 = OpAccessChain %_ptr_UniformConstant_22 %texSampler1D %32 +%35 = OpLoad %22 %34 +%36 = OpLoad %float %coords1D +%37 = OpLoad %float %lod +%38 = OpImageSampleExplicitLod %v4float %35 %36 Lod %37 +%40 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +OpStore %40 %38 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %12 +%35 = OpLabel +%lod = OpVariable %_ptr_Function_float Function +%coords1D = OpVariable %_ptr_Function_float Function +OpStore %lod %float_3 +OpStore %coords1D %float_1_78900003 +%36 = OpAccessChain %_ptr_Uniform_int %__0 %int_0 +%37 = OpLoad %int %36 +%38 = OpAccessChain %_ptr_UniformConstant_25 %texSampler1D %37 +%39 = OpLoad %25 %38 +%40 = OpLoad %float %coords1D +%41 = OpLoad %float %lod +%46 = OpULessThan %bool %37 %uint_128 +OpSelectionMerge %47 None +OpBranchConditional %46 %48 %49 +%48 = OpLabel +%50 = OpLoad %25 %38 +%51 = OpImageSampleExplicitLod %v4float %50 %40 Lod %41 +OpBranch %47 +%49 = OpLabel +%52 = OpBitcast %uint %37 +%105 = OpFunctionCall %void %53 %uint_74 %uint_0 %52 %uint_128 +OpBranch %47 +%47 = OpLabel +%107 = OpPhi %v4float %51 %48 %106 %49 +%43 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +OpStore %43 %107 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%53 = OpFunction %void None %54 +%55 = OpFunctionParameter %uint +%56 = OpFunctionParameter %uint +%57 = OpFunctionParameter %uint +%58 = OpFunctionParameter %uint +%59 = OpLabel +%65 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_0 +%68 = OpAtomicIAdd %uint %65 %uint_4 %uint_0 %uint_10 +%69 = OpIAdd %uint %68 %uint_10 +%70 = OpArrayLength %uint %63 1 +%71 = OpULessThanEqual %bool %69 %70 +OpSelectionMerge %72 None +OpBranchConditional %71 %73 %72 +%73 = OpLabel +%74 = OpIAdd %uint %68 %uint_0 +%75 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %74 +OpStore %75 %uint_10 +%77 = OpIAdd %uint %68 %uint_1 +%78 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %77 +OpStore %78 %uint_23 +%80 = OpIAdd %uint %68 %uint_2 +%81 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %80 +OpStore %81 %55 +%83 = OpIAdd %uint %68 %uint_3 +%84 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %83 +OpStore %84 %uint_0 +%87 = OpLoad %uint %gl_VertexIndex +%88 = OpIAdd %uint %68 %uint_4 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %88 +OpStore %89 %87 +%91 = OpLoad %uint %gl_InstanceIndex +%93 = OpIAdd %uint %68 %uint_5 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %93 +OpStore %94 %91 +%96 = OpIAdd %uint %68 %uint_7 +%97 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %96 +OpStore %97 %56 +%99 = OpIAdd %uint %68 %uint_8 +%100 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %99 +OpStore %100 %57 +%102 = OpIAdd %uint %68 %uint_9 +%103 = OpAccessChain %_ptr_StorageBuffer_uint %63 %uint_1 %102 +OpStore %103 %58 +OpBranch %72 +%72 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + output_func, true, + true, 7u, 23u, false, false); +} + +TEST_F(InstBindlessTest, InstrumentTeseSimple) { + // This test verifies that the pass will correctly instrument tessellation + // evaluation shader doing bindless buffer load. + // + // clang-format off + // + // #version 450 + // #extension GL_EXT_nonuniform_qualifier : enable + // + // layout(std140, set = 0, binding = 0) uniform ufoo { uint index; } uniform_index_buffer; + // + // layout(set = 0, binding = 1) buffer bfoo { vec4 val; } adds[11]; + // + // layout(triangles, equal_spacing, cw) in; + // + // void main() { + // gl_Position = adds[uniform_index_buffer.index].val; + // } + // + // clang-format on + + const std::string defs_before = + R"(OpCapability Tessellation +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationEvaluation %main "main" %_ +OpExecutionMode %main Triangles +OpExecutionMode %main SpacingEqual +OpExecutionMode %main VertexOrderCw +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %gl_PerVertex "gl_PerVertex" +OpMemberName %gl_PerVertex 0 "gl_Position" +OpMemberName %gl_PerVertex 1 "gl_PointSize" +OpMemberName %gl_PerVertex 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex 3 "gl_CullDistance" +OpName %_ "" +OpName %bfoo "bfoo" +OpMemberName %bfoo 0 "val" +OpName %adds "adds" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "index" +OpName %uniform_index_buffer "uniform_index_buffer" +OpMemberDecorate %gl_PerVertex 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex Block +OpMemberDecorate %bfoo 0 Offset 0 +OpDecorate %bfoo Block +OpDecorate %adds DescriptorSet 0 +OpDecorate %adds Binding 1 +OpMemberDecorate %ufoo 0 Offset 0 +OpDecorate %ufoo Block +OpDecorate %uniform_index_buffer DescriptorSet 0 +OpDecorate %uniform_index_buffer Binding 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex +%_ = OpVariable %_ptr_Output_gl_PerVertex Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%bfoo = OpTypeStruct %v4float +%uint_11 = OpConstant %uint 11 +%_arr_bfoo_uint_11 = OpTypeArray %bfoo %uint_11 +%_ptr_StorageBuffer__arr_bfoo_uint_11 = OpTypePointer StorageBuffer %_arr_bfoo_uint_11 +%adds = OpVariable %_ptr_StorageBuffer__arr_bfoo_uint_11 StorageBuffer +%ufoo = OpTypeStruct %uint +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%uniform_index_buffer = OpVariable %_ptr_Uniform_ufoo Uniform +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +)"; + + const std::string defs_after = + R"(OpCapability Tessellation +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationEvaluation %main "main" %_ %gl_PrimitiveID %gl_TessCoord +OpExecutionMode %main Triangles +OpExecutionMode %main SpacingEqual +OpExecutionMode %main VertexOrderCw +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %gl_PerVertex "gl_PerVertex" +OpMemberName %gl_PerVertex 0 "gl_Position" +OpMemberName %gl_PerVertex 1 "gl_PointSize" +OpMemberName %gl_PerVertex 2 "gl_ClipDistance" +OpMemberName %gl_PerVertex 3 "gl_CullDistance" +OpName %_ "" +OpName %bfoo "bfoo" +OpMemberName %bfoo 0 "val" +OpName %adds "adds" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "index" +OpName %uniform_index_buffer "uniform_index_buffer" +OpMemberDecorate %gl_PerVertex 0 BuiltIn Position +OpMemberDecorate %gl_PerVertex 1 BuiltIn PointSize +OpMemberDecorate %gl_PerVertex 2 BuiltIn ClipDistance +OpMemberDecorate %gl_PerVertex 3 BuiltIn CullDistance +OpDecorate %gl_PerVertex Block +OpMemberDecorate %bfoo 0 Offset 0 +OpDecorate %bfoo Block +OpDecorate %adds DescriptorSet 0 +OpDecorate %adds Binding 1 +OpMemberDecorate %ufoo 0 Offset 0 +OpDecorate %ufoo Block +OpDecorate %uniform_index_buffer DescriptorSet 0 +OpDecorate %uniform_index_buffer Binding 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_47 Block +OpMemberDecorate %_struct_47 0 Offset 0 +OpMemberDecorate %_struct_47 1 Offset 4 +OpDecorate %49 DescriptorSet 7 +OpDecorate %49 Binding 0 +OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId +OpDecorate %gl_TessCoord BuiltIn TessCoord +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%gl_PerVertex = OpTypeStruct %v4float %float %_arr_float_uint_1 %_arr_float_uint_1 +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex +%_ = OpVariable %_ptr_Output_gl_PerVertex Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%bfoo = OpTypeStruct %v4float +%uint_11 = OpConstant %uint 11 +%_arr_bfoo_uint_11 = OpTypeArray %bfoo %uint_11 +%_ptr_StorageBuffer__arr_bfoo_uint_11 = OpTypePointer StorageBuffer %_arr_bfoo_uint_11 +%adds = OpVariable %_ptr_StorageBuffer__arr_bfoo_uint_11 StorageBuffer +%ufoo = OpTypeStruct %uint +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%uniform_index_buffer = OpVariable %_ptr_Uniform_ufoo Uniform +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%40 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_47 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47 +%49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_uint = OpTypePointer Input %uint +%gl_PrimitiveID = OpVariable %_ptr_Input_uint Input +%v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float +%gl_TessCoord = OpVariable %_ptr_Input_v3float Input +%v3uint = OpTypeVector %uint 3 +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_63 = OpConstant %uint 63 +%101 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0 +%26 = OpLoad %uint %25 +%28 = OpAccessChain %_ptr_StorageBuffer_v4float %adds %26 %int_0 +%29 = OpLoad %v4float %28 +%31 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %10 +%26 = OpLabel +%27 = OpAccessChain %_ptr_Uniform_uint %uniform_index_buffer %int_0 +%28 = OpLoad %uint %27 +%29 = OpAccessChain %_ptr_StorageBuffer_v4float %adds %28 %int_0 +%34 = OpULessThan %bool %28 %uint_11 +OpSelectionMerge %35 None +OpBranchConditional %34 %36 %37 +%36 = OpLabel +%38 = OpLoad %v4float %29 +OpBranch %35 +%37 = OpLabel +%100 = OpFunctionCall %void %39 %uint_63 %uint_0 %28 %uint_11 +OpBranch %35 +%35 = OpLabel +%102 = OpPhi %v4float %38 %36 %101 %37 +%31 = OpAccessChain %_ptr_Output_v4float %_ %int_0 +OpStore %31 %102 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(%39 = OpFunction %void None %40 +%41 = OpFunctionParameter %uint +%42 = OpFunctionParameter %uint +%43 = OpFunctionParameter %uint +%44 = OpFunctionParameter %uint +%45 = OpLabel +%51 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0 +%54 = OpAtomicIAdd %uint %51 %uint_4 %uint_0 %uint_10 +%55 = OpIAdd %uint %54 %uint_10 +%56 = OpArrayLength %uint %49 1 +%57 = OpULessThanEqual %bool %55 %56 +OpSelectionMerge %58 None +OpBranchConditional %57 %59 %58 +%59 = OpLabel +%60 = OpIAdd %uint %54 %uint_0 +%61 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %60 +OpStore %61 %uint_10 +%63 = OpIAdd %uint %54 %uint_1 +%64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %63 +OpStore %64 %uint_23 +%66 = OpIAdd %uint %54 %uint_2 +%67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66 +OpStore %67 %41 +%69 = OpIAdd %uint %54 %uint_3 +%70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69 +OpStore %70 %uint_2 +%73 = OpLoad %uint %gl_PrimitiveID +%74 = OpIAdd %uint %54 %uint_4 +%75 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %74 +OpStore %75 %73 +%79 = OpLoad %v3float %gl_TessCoord +%81 = OpBitcast %v3uint %79 +%82 = OpCompositeExtract %uint %81 0 +%83 = OpCompositeExtract %uint %81 1 +%85 = OpIAdd %uint %54 %uint_5 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %85 +OpStore %86 %82 +%88 = OpIAdd %uint %54 %uint_6 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %88 +OpStore %89 %83 +%91 = OpIAdd %uint %54 %uint_7 +%92 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %91 +OpStore %92 %42 +%94 = OpIAdd %uint %54 %uint_8 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %94 +OpStore %95 %43 +%97 = OpIAdd %uint %54 %uint_9 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %97 +OpStore %98 %44 +OpBranch %58 +%58 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + output_func, true, + true, 7u, 23u, false, false); +} + +TEST_F(InstBindlessTest, MultipleDebugFunctions) { + // Same source as Simple, but compiled -g and not optimized, especially not + // inlined. The OpSource has had the source extracted for the sake of brevity. + + const std::string defs_before = + R"(OpCapability Shader +%2 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +%1 = OpString "foo5.frag" +OpSource HLSL 500 %1 +OpName %MainPs "MainPs" +OpName %PS_INPUT "PS_INPUT" +OpMemberName %PS_INPUT 0 "vTextureCoords" +OpName %PS_OUTPUT "PS_OUTPUT" +OpMemberName %PS_OUTPUT 0 "vColor" +OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;" +OpName %i "i" +OpName %ps_output "ps_output" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_0 "i" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpName %param "param" +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 1 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT +%v4float = OpTypeVector %float 4 +%PS_OUTPUT = OpTypeStruct %v4float +%13 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%21 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_21_uint_128 = OpTypeArray %21 %uint_128 +%_ptr_UniformConstant__arr_21_uint_128 = OpTypePointer UniformConstant %_arr_21_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_21_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%36 = OpTypeSampler +%_ptr_UniformConstant_36 = OpTypePointer UniformConstant %36 +%g_sAniso = OpVariable %_ptr_UniformConstant_36 UniformConstant +%40 = OpTypeSampledImage %21 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +%5 = OpString "foo5.frag" +OpSource HLSL 500 %5 +OpName %MainPs "MainPs" +OpName %PS_INPUT "PS_INPUT" +OpMemberName %PS_INPUT 0 "vTextureCoords" +OpName %PS_OUTPUT "PS_OUTPUT" +OpMemberName %PS_OUTPUT 0 "vColor" +OpName %_MainPs_struct_PS_INPUT_vf21_ "@MainPs(struct-PS_INPUT-vf21;" +OpName %i "i" +OpName %ps_output "ps_output" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_0 "i" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpName %param "param" +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 1 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_77 Block +OpMemberDecorate %_struct_77 0 Offset 0 +OpMemberDecorate %_struct_77 1 Offset 4 +OpDecorate %79 DescriptorSet 7 +OpDecorate %79 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%18 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT +%v4float = OpTypeVector %float 4 +%PS_OUTPUT = OpTypeStruct %v4float +%23 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%27 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_27_uint_128 = OpTypeArray %27 %uint_128 +%_ptr_UniformConstant__arr_27_uint_128 = OpTypePointer UniformConstant %_arr_27_uint_128 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_27_uint_128 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27 +%35 = OpTypeSampler +%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35 +%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant +%37 = OpTypeSampledImage %27 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%70 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_77 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77 +%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_109 = OpConstant %uint 109 +%125 = OpConstantNull %v4float +)"; + + const std::string func1_before = + R"(%MainPs = OpFunction %void None %4 +%6 = OpLabel +%i_0 = OpVariable %_ptr_Function_PS_INPUT Function +%param = OpVariable %_ptr_Function_PS_INPUT Function +OpLine %1 21 0 +%54 = OpLoad %v2float %i_vTextureCoords +%55 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0 +OpStore %55 %54 +%59 = OpLoad %PS_INPUT %i_0 +OpStore %param %59 +%60 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param +%61 = OpCompositeExtract %v4float %60 0 +OpStore %_entryPointOutput_vColor %61 +OpReturn +OpFunctionEnd +)"; + + const std::string func1_after = + R"(%MainPs = OpFunction %void None %18 +%42 = OpLabel +%i_0 = OpVariable %_ptr_Function_PS_INPUT Function +%param = OpVariable %_ptr_Function_PS_INPUT Function +OpLine %5 21 0 +%43 = OpLoad %v2float %i_vTextureCoords +%44 = OpAccessChain %_ptr_Function_v2float %i_0 %int_0 +OpStore %44 %43 +%45 = OpLoad %PS_INPUT %i_0 +OpStore %param %45 +%46 = OpFunctionCall %PS_OUTPUT %_MainPs_struct_PS_INPUT_vf21_ %param +%47 = OpCompositeExtract %v4float %46 0 +OpStore %_entryPointOutput_vColor %47 +OpReturn +OpFunctionEnd +)"; + + const std::string func2_before = + R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %13 +%i = OpFunctionParameter %_ptr_Function_PS_INPUT +%16 = OpLabel +%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function +OpLine %1 24 0 +%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%32 = OpLoad %uint %31 +%34 = OpAccessChain %_ptr_UniformConstant_21 %g_tColor %32 +%35 = OpLoad %21 %34 +%39 = OpLoad %36 %g_sAniso +%41 = OpSampledImage %40 %35 %39 +%43 = OpAccessChain %_ptr_Function_v2float %i %int_0 +%44 = OpLoad %v2float %43 +%45 = OpImageSampleImplicitLod %v4float %41 %44 +%47 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 +OpStore %47 %45 +OpLine %1 25 0 +%48 = OpLoad %PS_OUTPUT %ps_output +OpReturnValue %48 +OpFunctionEnd +)"; + + const std::string func2_after = + R"(%_MainPs_struct_PS_INPUT_vf21_ = OpFunction %PS_OUTPUT None %23 +%i = OpFunctionParameter %_ptr_Function_PS_INPUT +%48 = OpLabel +%ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function +OpLine %5 24 0 +%49 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%50 = OpLoad %uint %49 +%51 = OpAccessChain %_ptr_UniformConstant_27 %g_tColor %50 +%52 = OpLoad %27 %51 +%53 = OpLoad %35 %g_sAniso +%54 = OpSampledImage %37 %52 %53 +%55 = OpAccessChain %_ptr_Function_v2float %i %int_0 +%56 = OpLoad %v2float %55 +OpNoLine +%62 = OpULessThan %bool %50 %uint_128 +OpSelectionMerge %63 None +OpBranchConditional %62 %64 %65 +%64 = OpLabel +%66 = OpLoad %27 %51 +%67 = OpSampledImage %37 %66 %53 +OpLine %5 24 0 +%68 = OpImageSampleImplicitLod %v4float %67 %56 +OpNoLine +OpBranch %63 +%65 = OpLabel +%124 = OpFunctionCall %void %69 %uint_109 %uint_0 %50 %uint_128 +OpBranch %63 +%63 = OpLabel +%126 = OpPhi %v4float %68 %64 %125 %65 +OpLine %5 24 0 +%58 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 +OpStore %58 %126 +OpLine %5 25 0 +%59 = OpLoad %PS_OUTPUT %ps_output +OpReturnValue %59 +OpFunctionEnd +)"; + + const std::string output_func = + R"(%69 = OpFunction %void None %70 +%71 = OpFunctionParameter %uint +%72 = OpFunctionParameter %uint +%73 = OpFunctionParameter %uint +%74 = OpFunctionParameter %uint +%75 = OpLabel +%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0 +%84 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10 +%85 = OpIAdd %uint %84 %uint_10 +%86 = OpArrayLength %uint %79 1 +%87 = OpULessThanEqual %bool %85 %86 +OpSelectionMerge %88 None +OpBranchConditional %87 %89 %88 +%89 = OpLabel +%90 = OpIAdd %uint %84 %uint_0 +%92 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %90 +OpStore %92 %uint_10 +%94 = OpIAdd %uint %84 %uint_1 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94 +OpStore %95 %uint_23 +%97 = OpIAdd %uint %84 %uint_2 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %97 +OpStore %98 %71 +%100 = OpIAdd %uint %84 %uint_3 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %100 +OpStore %101 %uint_4 +%104 = OpLoad %v4float %gl_FragCoord +%106 = OpBitcast %v4uint %104 +%107 = OpCompositeExtract %uint %106 0 +%108 = OpIAdd %uint %84 %uint_4 +%109 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %108 +OpStore %109 %107 +%110 = OpCompositeExtract %uint %106 1 +%112 = OpIAdd %uint %84 %uint_5 +%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112 +OpStore %113 %110 +%115 = OpIAdd %uint %84 %uint_7 +%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115 +OpStore %116 %72 +%118 = OpIAdd %uint %84 %uint_8 +%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118 +OpStore %119 %73 +%121 = OpIAdd %uint %84 %uint_9 +%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121 +OpStore %122 %74 +OpBranch %88 +%88 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func1_before + func2_before, + defs_after + func1_after + func2_after + output_func, true, true, 7u, 23u, + false, false); +} + +TEST_F(InstBindlessTest, RuntimeArray) { + // This test verifies that the pass will correctly instrument shader + // with runtime descriptor array. This test was created by editing the + // SPIR-V from the Simple test. + + const std::string defs_before = + R"(OpCapability Shader +OpCapability RuntimeDescriptorArray +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 1 +OpDecorate %g_tColor Binding 2 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 1 +OpDecorate %g_sAniso Binding 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%20 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_rarr_20 = OpTypeRuntimeArray %20 +%_ptr_UniformConstant__arr_20 = OpTypePointer UniformConstant %_rarr_20 +%g_tColor = OpVariable %_ptr_UniformConstant__arr_20 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_20 = OpTypePointer UniformConstant %20 +%35 = OpTypeSampler +%_ptr_UniformConstant_35 = OpTypePointer UniformConstant %35 +%g_sAniso = OpVariable %_ptr_UniformConstant_35 UniformConstant +%39 = OpTypeSampledImage %20 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability RuntimeDescriptorArray +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" +OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" +OpName %_ "" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 1 +OpDecorate %g_tColor Binding 2 +OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 +OpDecorate %PerViewConstantBuffer_t Block +OpDecorate %g_sAniso DescriptorSet 1 +OpDecorate %g_sAniso Binding 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_46 Block +OpMemberDecorate %_struct_46 0 Offset 0 +OpDecorate %48 DescriptorSet 7 +OpDecorate %48 Binding 1 +OpDecorate %_struct_71 Block +OpMemberDecorate %_struct_71 0 Offset 0 +OpMemberDecorate %_struct_71 1 Offset 4 +OpDecorate %73 DescriptorSet 7 +OpDecorate %73 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%16 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_runtimearr_16 = OpTypeRuntimeArray %16 +%_ptr_UniformConstant__runtimearr_16 = OpTypePointer UniformConstant %_runtimearr_16 +%g_tColor = OpVariable %_ptr_UniformConstant__runtimearr_16 UniformConstant +%PerViewConstantBuffer_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t +%_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 +%24 = OpTypeSampler +%_ptr_UniformConstant_24 = OpTypePointer UniformConstant %24 +%g_sAniso = OpVariable %_ptr_UniformConstant_24 UniformConstant +%26 = OpTypeSampledImage %16 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint_0 = OpConstant %uint 0 +%uint_2 = OpConstant %uint 2 +%41 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_46 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_46 = OpTypePointer StorageBuffer %_struct_46 +%48 = OpVariable %_ptr_StorageBuffer__struct_46 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%65 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_71 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_71 = OpTypePointer StorageBuffer %_struct_71 +%73 = OpVariable %_ptr_StorageBuffer__struct_71 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_59 = OpConstant %uint 59 +%116 = OpConstantNull %v4float +%119 = OpTypeFunction %uint %uint %uint %uint %uint +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %3 +%5 = OpLabel +%53 = OpLoad %v2float %i_vTextureCoords +%63 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%64 = OpLoad %uint %63 +%65 = OpAccessChain %_ptr_UniformConstant_20 %g_tColor %64 +%66 = OpLoad %20 %65 +%67 = OpLoad %35 %g_sAniso +%68 = OpSampledImage %39 %66 %67 +%71 = OpImageSampleImplicitLod %v4float %68 %53 +OpStore %_entryPointOutput_vColor %71 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %10 +%29 = OpLabel +%30 = OpLoad %v2float %i_vTextureCoords +%31 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 +%32 = OpLoad %uint %31 +%33 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %32 +%34 = OpLoad %16 %33 +%35 = OpLoad %24 %g_sAniso +%36 = OpSampledImage %26 %34 %35 +%55 = OpFunctionCall %uint %40 %uint_2 %uint_2 +%57 = OpULessThan %bool %32 %55 +OpSelectionMerge %58 None +OpBranchConditional %57 %59 %60 +%59 = OpLabel +%61 = OpLoad %16 %33 +%62 = OpSampledImage %26 %61 %35 +%136 = OpFunctionCall %uint %118 %uint_0 %uint_1 %uint_2 %32 +%137 = OpULessThan %bool %uint_0 %136 +OpSelectionMerge %138 None +OpBranchConditional %137 %139 %140 +%139 = OpLabel +%141 = OpLoad %16 %33 +%142 = OpSampledImage %26 %141 %35 +%143 = OpImageSampleImplicitLod %v4float %142 %30 +OpBranch %138 +%140 = OpLabel +%144 = OpFunctionCall %void %64 %uint_59 %uint_1 %32 %uint_0 +OpBranch %138 +%138 = OpLabel +%145 = OpPhi %v4float %143 %139 %116 %140 +OpBranch %58 +%60 = OpLabel +%115 = OpFunctionCall %void %64 %uint_59 %uint_0 %32 %55 +OpBranch %58 +%58 = OpLabel +%117 = OpPhi %v4float %145 %138 %116 %60 +OpStore %_entryPointOutput_vColor %117 +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%40 = OpFunction %uint None %41 +%42 = OpFunctionParameter %uint +%43 = OpFunctionParameter %uint +%44 = OpLabel +%50 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %42 +%51 = OpLoad %uint %50 +%52 = OpIAdd %uint %51 %43 +%53 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %52 +%54 = OpLoad %uint %53 +OpReturnValue %54 +OpFunctionEnd +%64 = OpFunction %void None %65 +%66 = OpFunctionParameter %uint +%67 = OpFunctionParameter %uint +%68 = OpFunctionParameter %uint +%69 = OpFunctionParameter %uint +%70 = OpLabel +%74 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_0 +%77 = OpAtomicIAdd %uint %74 %uint_4 %uint_0 %uint_10 +%78 = OpIAdd %uint %77 %uint_10 +%79 = OpArrayLength %uint %73 1 +%80 = OpULessThanEqual %bool %78 %79 +OpSelectionMerge %81 None +OpBranchConditional %80 %82 %81 +%82 = OpLabel +%83 = OpIAdd %uint %77 %uint_0 +%84 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %83 +OpStore %84 %uint_10 +%86 = OpIAdd %uint %77 %uint_1 +%87 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %86 +OpStore %87 %uint_23 +%88 = OpIAdd %uint %77 %uint_2 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %88 +OpStore %89 %66 +%91 = OpIAdd %uint %77 %uint_3 +%92 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %91 +OpStore %92 %uint_4 +%95 = OpLoad %v4float %gl_FragCoord +%97 = OpBitcast %v4uint %95 +%98 = OpCompositeExtract %uint %97 0 +%99 = OpIAdd %uint %77 %uint_4 +%100 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %99 +OpStore %100 %98 +%101 = OpCompositeExtract %uint %97 1 +%103 = OpIAdd %uint %77 %uint_5 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %103 +OpStore %104 %101 +%106 = OpIAdd %uint %77 %uint_7 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %106 +OpStore %107 %67 +%109 = OpIAdd %uint %77 %uint_8 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %109 +OpStore %110 %68 +%112 = OpIAdd %uint %77 %uint_9 +%113 = OpAccessChain %_ptr_StorageBuffer_uint %73 %uint_1 %112 +OpStore %113 %69 +OpBranch %81 +%81 = OpLabel +OpReturn +OpFunctionEnd +%118 = OpFunction %uint None %119 +%120 = OpFunctionParameter %uint +%121 = OpFunctionParameter %uint +%122 = OpFunctionParameter %uint +%123 = OpFunctionParameter %uint +%124 = OpLabel +%125 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %120 +%126 = OpLoad %uint %125 +%127 = OpIAdd %uint %126 %121 +%128 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %127 +%129 = OpLoad %uint %128 +%130 = OpIAdd %uint %129 %122 +%131 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %130 +%132 = OpLoad %uint %131 +%133 = OpIAdd %uint %132 %123 +%134 = OpAccessChain %_ptr_StorageBuffer_uint %48 %uint_0 %133 +%135 = OpLoad %uint %134 +OpReturnValue %135 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, InstrumentInitCheckOnScalarDescriptor) { + // This test verifies that the pass will correctly instrument vanilla + // texture sample on a scalar descriptor with an initialization check if the + // input_init_enable argument is set to true. This can happen when the + // descriptor indexing extension is enabled in the API but the SPIR-V + // does not have the extension enabled because it does not contain a + // runtime array. This is the same shader as NoInstrumentNonBindless. + + const std::string defs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 +%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant +%14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 +%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant +%16 = OpTypeSampledImage %12 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %MainPs "MainPs" %i_vTextureCoords %_entryPointOutput_vColor %gl_FragCoord +OpExecutionMode %MainPs OriginUpperLeft +OpSource HLSL 500 +OpName %MainPs "MainPs" +OpName %g_tColor "g_tColor" +OpName %g_sAniso "g_sAniso" +OpName %i_vTextureCoords "i.vTextureCoords" +OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" +OpDecorate %g_tColor DescriptorSet 0 +OpDecorate %g_tColor Binding 0 +OpDecorate %g_sAniso DescriptorSet 0 +OpDecorate %g_sAniso Binding 0 +OpDecorate %i_vTextureCoords Location 0 +OpDecorate %_entryPointOutput_vColor Location 0 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_35 Block +OpMemberDecorate %_struct_35 0 Offset 0 +OpDecorate %37 DescriptorSet 7 +OpDecorate %37 Binding 1 +OpDecorate %_struct_67 Block +OpMemberDecorate %_struct_67 0 Offset 0 +OpMemberDecorate %_struct_67 1 Offset 4 +OpDecorate %69 DescriptorSet 7 +OpDecorate %69 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%12 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 +%g_tColor = OpVariable %_ptr_UniformConstant_12 UniformConstant +%14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 +%g_sAniso = OpVariable %_ptr_UniformConstant_14 UniformConstant +%16 = OpTypeSampledImage %12 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%28 = OpTypeFunction %uint %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_35 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_35 = OpTypePointer StorageBuffer %_struct_35 +%37 = OpVariable %_ptr_StorageBuffer__struct_35 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%uint_1 = OpConstant %uint 1 +%61 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_67 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_67 = OpTypePointer StorageBuffer %_struct_67 +%69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_39 = OpConstant %uint 39 +%113 = OpConstantNull %v4float +)"; + + const std::string func_before = + R"(%MainPs = OpFunction %void None %8 +%19 = OpLabel +%20 = OpLoad %v2float %i_vTextureCoords +%21 = OpLoad %12 %g_tColor +%22 = OpLoad %14 %g_sAniso +%23 = OpSampledImage %16 %21 %22 +%24 = OpImageSampleImplicitLod %v4float %23 %20 +OpStore %_entryPointOutput_vColor %24 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%MainPs = OpFunction %void None %8 +%19 = OpLabel +%20 = OpLoad %v2float %i_vTextureCoords +%21 = OpLoad %12 %g_tColor +%22 = OpLoad %14 %g_sAniso +%23 = OpSampledImage %16 %21 %22 +%50 = OpFunctionCall %uint %27 %uint_0 %uint_0 %uint_0 %uint_0 +%52 = OpULessThan %bool %uint_0 %50 +OpSelectionMerge %54 None +OpBranchConditional %52 %55 %56 +%55 = OpLabel +%57 = OpLoad %12 %g_tColor +%58 = OpSampledImage %16 %57 %22 +%59 = OpImageSampleImplicitLod %v4float %58 %20 +OpBranch %54 +%56 = OpLabel +%112 = OpFunctionCall %void %60 %uint_39 %uint_1 %uint_0 %uint_0 +OpBranch %54 +%54 = OpLabel +%114 = OpPhi %v4float %59 %55 %113 %56 +OpStore %_entryPointOutput_vColor %114 +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%27 = OpFunction %uint None %28 +%29 = OpFunctionParameter %uint +%30 = OpFunctionParameter %uint +%31 = OpFunctionParameter %uint +%32 = OpFunctionParameter %uint +%33 = OpLabel +%39 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %29 +%40 = OpLoad %uint %39 +%41 = OpIAdd %uint %40 %30 +%42 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %41 +%43 = OpLoad %uint %42 +%44 = OpIAdd %uint %43 %31 +%45 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %44 +%46 = OpLoad %uint %45 +%47 = OpIAdd %uint %46 %32 +%48 = OpAccessChain %_ptr_StorageBuffer_uint %37 %uint_0 %47 +%49 = OpLoad %uint %48 +OpReturnValue %49 +OpFunctionEnd +%60 = OpFunction %void None %61 +%62 = OpFunctionParameter %uint +%63 = OpFunctionParameter %uint +%64 = OpFunctionParameter %uint +%65 = OpFunctionParameter %uint +%66 = OpLabel +%70 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 +%73 = OpAtomicIAdd %uint %70 %uint_4 %uint_0 %uint_10 +%74 = OpIAdd %uint %73 %uint_10 +%75 = OpArrayLength %uint %69 1 +%76 = OpULessThanEqual %bool %74 %75 +OpSelectionMerge %77 None +OpBranchConditional %76 %78 %77 +%78 = OpLabel +%79 = OpIAdd %uint %73 %uint_0 +%80 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %79 +OpStore %80 %uint_10 +%82 = OpIAdd %uint %73 %uint_1 +%83 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %82 +OpStore %83 %uint_23 +%85 = OpIAdd %uint %73 %uint_2 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %85 +OpStore %86 %62 +%88 = OpIAdd %uint %73 %uint_3 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %88 +OpStore %89 %uint_4 +%92 = OpLoad %v4float %gl_FragCoord +%94 = OpBitcast %v4uint %92 +%95 = OpCompositeExtract %uint %94 0 +%96 = OpIAdd %uint %73 %uint_4 +%97 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %96 +OpStore %97 %95 +%98 = OpCompositeExtract %uint %94 1 +%100 = OpIAdd %uint %73 %uint_5 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %100 +OpStore %101 %98 +%103 = OpIAdd %uint %73 %uint_7 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %103 +OpStore %104 %63 +%106 = OpIAdd %uint %73 %uint_8 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %106 +OpStore %107 %64 +%109 = OpIAdd %uint %73 %uint_9 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_1 %109 +OpStore %110 %65 +OpBranch %77 +%77 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, SPV14AddToEntryPoint) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] +; CHECK: OpDecorate [[v1]] DescriptorSet 7 +; CHECK: OpDecorate [[v2]] DescriptorSet 7 +; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer +; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var +OpExecutionMode %foo OriginUpperLeft +OpDecorate %image_var DescriptorSet 0 +OpDecorate %image_var Binding 0 +OpDecorate %sampler_var DescriptorSet 0 +OpDecorate %sampler_var Binding 1 +OpDecorate %gid DescriptorSet 0 +OpDecorate %gid Binding 2 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%v3int = OpTypeVector %int 3 +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%v4float = OpTypeVector %float 4 +%struct = OpTypeStruct %v3int +%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct +%ptr_ssbo_v3int = OpTypePointer StorageBuffer %v3int +%gid = OpVariable %ptr_ssbo_struct StorageBuffer +%image = OpTypeImage %float 3D 0 0 0 1 Unknown +%ptr_uc_image = OpTypePointer UniformConstant %image +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%image_var = OpVariable %ptr_uc_image UniformConstant +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%sampled = OpTypeSampledImage %image +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +%ld_sampler = OpLoad %sampler %sampler_var +%gep = OpAccessChain %ptr_ssbo_v3int %gid %int_0 +%ld_gid = OpLoad %v3int %gep +%convert = OpConvertUToF %v3float %ld_gid +%sampled_image = OpSampledImage %sampled %ld_image %ld_sampler +%sample = OpImageSampleImplicitLod %v4float %sampled_image %convert +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4); + SinglePassRunAndMatch(text, true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, SPV14AddToEntryPoints) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment {{%\w+}} "foo" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] +; CHECK: OpEntryPoint Fragment {{%\w+}} "bar" {{%\w+}} {{%\w+}} {{%\w+}} [[v1:%\w+]] [[v2:%\w+]] +; CHECK: OpDecorate [[v1]] DescriptorSet 7 +; CHECK: OpDecorate [[v2]] DescriptorSet 7 +; CHECK: [[v1]] = OpVariable {{%\w+}} StorageBuffer +; CHECK: [[v2]] = OpVariable {{%\w+}} StorageBuffer +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %foo "foo" %gid %image_var %sampler_var +OpEntryPoint Fragment %foo "bar" %gid %image_var %sampler_var +OpExecutionMode %foo OriginUpperLeft +OpDecorate %image_var DescriptorSet 0 +OpDecorate %image_var Binding 0 +OpDecorate %sampler_var DescriptorSet 0 +OpDecorate %sampler_var Binding 1 +OpDecorate %gid DescriptorSet 0 +OpDecorate %gid Binding 2 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%v3int = OpTypeVector %int 3 +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%v4float = OpTypeVector %float 4 +%struct = OpTypeStruct %v3int +%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct +%ptr_ssbo_v3int = OpTypePointer StorageBuffer %v3int +%gid = OpVariable %ptr_ssbo_struct StorageBuffer +%image = OpTypeImage %float 3D 0 0 0 1 Unknown +%ptr_uc_image = OpTypePointer UniformConstant %image +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%image_var = OpVariable %ptr_uc_image UniformConstant +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%sampled = OpTypeSampledImage %image +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +%ld_sampler = OpLoad %sampler %sampler_var +%gep = OpAccessChain %ptr_ssbo_v3int %gid %int_0 +%ld_gid = OpLoad %v3int %gep +%convert = OpConvertUToF %v3float %ld_gid +%sampled_image = OpSampledImage %sampled %ld_image %ld_sampler +%sample = OpImageSampleImplicitLod %v4float %sampled_image %convert +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_1_SPIRV_1_4); + SinglePassRunAndMatch(text, true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedUBOArray) { + // #version 450 + // #extension GL_EXT_nonuniform_qualifier : enable + // + // layout(location=0) in nonuniformEXT flat int nu_ii; + // layout(location=0) out float b; + // + // layout(binding=3) uniform uname { float a; } uniformBuffer[]; + // + // void main() + // { + // b = uniformBuffer[nu_ii].a; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability RuntimeDescriptorArray +OpCapability UniformBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %nu_ii +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %uname "uname" +OpMemberName %uname 0 "a" +OpName %uniformBuffer "uniformBuffer" +OpName %nu_ii "nu_ii" +OpDecorate %b Location 0 +OpMemberDecorate %uname 0 Offset 0 +OpDecorate %uname Block +OpDecorate %uniformBuffer DescriptorSet 0 +OpDecorate %uniformBuffer Binding 3 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %16 NonUniform +OpDecorate %20 NonUniform +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%uname = OpTypeStruct %float +%_runtimearr_uname = OpTypeRuntimeArray %uname +%_ptr_Uniform__runtimearr_uname = OpTypePointer Uniform %_runtimearr_uname +%uniformBuffer = OpVariable %_ptr_Uniform__runtimearr_uname Uniform +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability RuntimeDescriptorArray +OpCapability UniformBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %uname "uname" +OpMemberName %uname 0 "a" +OpName %uniformBuffer "uniformBuffer" +OpName %nu_ii "nu_ii" +OpDecorate %b Location 0 +OpMemberDecorate %uname 0 Offset 0 +OpDecorate %uname Block +OpDecorate %uniformBuffer DescriptorSet 0 +OpDecorate %uniformBuffer Binding 3 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %7 NonUniform +OpDecorate %102 NonUniform +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_31 Block +OpMemberDecorate %_struct_31 0 Offset 0 +OpDecorate %33 DescriptorSet 7 +OpDecorate %33 Binding 1 +OpDecorate %130 NonUniform +OpDecorate %_struct_55 Block +OpMemberDecorate %_struct_55 0 Offset 0 +OpMemberDecorate %_struct_55 1 Offset 4 +OpDecorate %57 DescriptorSet 7 +OpDecorate %57 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %127 NonUniform +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%uname = OpTypeStruct %float +%_runtimearr_uname = OpTypeRuntimeArray %uname +%_ptr_Uniform__runtimearr_uname = OpTypePointer Uniform %_runtimearr_uname +%uniformBuffer = OpVariable %_ptr_Uniform__runtimearr_uname Uniform +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%26 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_31 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 +%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%49 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_55 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 +%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_45 = OpConstant %uint 45 +%101 = OpConstantNull %float +%105 = OpTypeFunction %uint %uint %uint %uint %uint +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpLoad %int %nu_ii +%19 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %16 %int_0 +%20 = OpLoad %float %19 +OpStore %b %20 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %10 +%19 = OpLabel +%7 = OpLoad %int %nu_ii +%20 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0 +%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 +%42 = OpULessThan %bool %7 %40 +OpSelectionMerge %43 None +OpBranchConditional %42 %44 %45 +%44 = OpLabel +%103 = OpBitcast %uint %7 +%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 +%123 = OpULessThan %bool %uint_0 %122 +OpSelectionMerge %124 None +OpBranchConditional %123 %125 %126 +%125 = OpLabel +%127 = OpLoad %float %20 +OpBranch %124 +%126 = OpLabel +%128 = OpBitcast %uint %7 +%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 +OpBranch %124 +%124 = OpLabel +%130 = OpPhi %float %127 %125 %101 %126 +OpBranch %43 +%45 = OpLabel +%47 = OpBitcast %uint %7 +%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 +OpBranch %43 +%43 = OpLabel +%102 = OpPhi %float %130 %124 %101 %45 +OpStore %b %102 +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%25 = OpFunction %uint None %26 +%27 = OpFunctionParameter %uint +%28 = OpFunctionParameter %uint +%29 = OpLabel +%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 +%36 = OpLoad %uint %35 +%37 = OpIAdd %uint %36 %28 +%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 +%39 = OpLoad %uint %38 +OpReturnValue %39 +OpFunctionEnd +%48 = OpFunction %void None %49 +%50 = OpFunctionParameter %uint +%51 = OpFunctionParameter %uint +%52 = OpFunctionParameter %uint +%53 = OpFunctionParameter %uint +%54 = OpLabel +%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 +%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10 +%62 = OpIAdd %uint %61 %uint_10 +%63 = OpArrayLength %uint %57 1 +%64 = OpULessThanEqual %bool %62 %63 +OpSelectionMerge %65 None +OpBranchConditional %64 %66 %65 +%66 = OpLabel +%67 = OpIAdd %uint %61 %uint_0 +%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 +OpStore %68 %uint_10 +%70 = OpIAdd %uint %61 %uint_1 +%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 +OpStore %71 %uint_23 +%73 = OpIAdd %uint %61 %uint_2 +%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 +OpStore %74 %50 +%75 = OpIAdd %uint %61 %uint_3 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 +OpStore %76 %uint_4 +%80 = OpLoad %v4float %gl_FragCoord +%82 = OpBitcast %v4uint %80 +%83 = OpCompositeExtract %uint %82 0 +%84 = OpIAdd %uint %61 %uint_4 +%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 +OpStore %85 %83 +%86 = OpCompositeExtract %uint %82 1 +%88 = OpIAdd %uint %61 %uint_5 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 +OpStore %89 %86 +%91 = OpIAdd %uint %61 %uint_7 +%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 +OpStore %92 %51 +%94 = OpIAdd %uint %61 %uint_8 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 +OpStore %95 %52 +%97 = OpIAdd %uint %61 %uint_9 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 +OpStore %98 %53 +OpBranch %65 +%65 = OpLabel +OpReturn +OpFunctionEnd +%104 = OpFunction %uint None %105 +%106 = OpFunctionParameter %uint +%107 = OpFunctionParameter %uint +%108 = OpFunctionParameter %uint +%109 = OpFunctionParameter %uint +%110 = OpLabel +%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 +%112 = OpLoad %uint %111 +%113 = OpIAdd %uint %112 %107 +%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 +%115 = OpLoad %uint %114 +%116 = OpIAdd %uint %115 %108 +%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 +%118 = OpLoad %uint %117 +%119 = OpIAdd %uint %118 %109 +%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 +%121 = OpLoad %uint %120 +OpReturnValue %121 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArrayDeprecated) { + // #version 450 + // #extension GL_EXT_nonuniform_qualifier : enable + // + // layout(location=0) in nonuniformEXT flat int nu_ii; + // layout(location=0) out float b; + // + // layout(binding=3) buffer bname { float b; } storageBuffer[]; + // + // void main() + // { + // b = storageBuffer[nu_ii].b; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability RuntimeDescriptorArray +OpCapability StorageBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %nu_ii +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %bname "bname" +OpMemberName %bname 0 "a" +OpName %storageBuffer "storageBuffer" +OpName %nu_ii "nu_ii" +OpDecorate %b Location 0 +OpMemberDecorate %bname 0 Offset 0 +OpDecorate %bname Block +OpDecorate %storageBuffer DescriptorSet 0 +OpDecorate %storageBuffer Binding 3 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %16 NonUniform +OpDecorate %20 NonUniform +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%bname = OpTypeStruct %float +%_runtimearr_bname = OpTypeRuntimeArray %bname +%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname +%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability RuntimeDescriptorArray +OpCapability StorageBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %bname "bname" +OpMemberName %bname 0 "a" +OpName %storageBuffer "storageBuffer" +OpName %nu_ii "nu_ii" +OpDecorate %b Location 0 +OpMemberDecorate %bname 0 Offset 0 +OpDecorate %bname Block +OpDecorate %storageBuffer DescriptorSet 0 +OpDecorate %storageBuffer Binding 3 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %7 NonUniform +OpDecorate %102 NonUniform +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_31 Block +OpMemberDecorate %_struct_31 0 Offset 0 +OpDecorate %33 DescriptorSet 7 +OpDecorate %33 Binding 1 +OpDecorate %130 NonUniform +OpDecorate %_struct_55 Block +OpMemberDecorate %_struct_55 0 Offset 0 +OpMemberDecorate %_struct_55 1 Offset 4 +OpDecorate %57 DescriptorSet 7 +OpDecorate %57 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %127 NonUniform +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%bname = OpTypeStruct %float +%_runtimearr_bname = OpTypeRuntimeArray %bname +%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname +%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%26 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_31 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 +%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%49 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_55 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 +%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_45 = OpConstant %uint 45 +%101 = OpConstantNull %float +%105 = OpTypeFunction %uint %uint %uint %uint %uint +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpLoad %int %nu_ii +%19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0 +%20 = OpLoad %float %19 +OpStore %b %20 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %10 +%19 = OpLabel +%7 = OpLoad %int %nu_ii +%20 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %7 %int_0 +%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 +%42 = OpULessThan %bool %7 %40 +OpSelectionMerge %43 None +OpBranchConditional %42 %44 %45 +%44 = OpLabel +%103 = OpBitcast %uint %7 +%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 +%123 = OpULessThan %bool %uint_0 %122 +OpSelectionMerge %124 None +OpBranchConditional %123 %125 %126 +%125 = OpLabel +%127 = OpLoad %float %20 +OpBranch %124 +%126 = OpLabel +%128 = OpBitcast %uint %7 +%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 +OpBranch %124 +%124 = OpLabel +%130 = OpPhi %float %127 %125 %101 %126 +OpBranch %43 +%45 = OpLabel +%47 = OpBitcast %uint %7 +%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 +OpBranch %43 +%43 = OpLabel +%102 = OpPhi %float %130 %124 %101 %45 +OpStore %b %102 +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%25 = OpFunction %uint None %26 +%27 = OpFunctionParameter %uint +%28 = OpFunctionParameter %uint +%29 = OpLabel +%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 +%36 = OpLoad %uint %35 +%37 = OpIAdd %uint %36 %28 +%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 +%39 = OpLoad %uint %38 +OpReturnValue %39 +OpFunctionEnd +%48 = OpFunction %void None %49 +%50 = OpFunctionParameter %uint +%51 = OpFunctionParameter %uint +%52 = OpFunctionParameter %uint +%53 = OpFunctionParameter %uint +%54 = OpLabel +%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 +%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10 +%62 = OpIAdd %uint %61 %uint_10 +%63 = OpArrayLength %uint %57 1 +%64 = OpULessThanEqual %bool %62 %63 +OpSelectionMerge %65 None +OpBranchConditional %64 %66 %65 +%66 = OpLabel +%67 = OpIAdd %uint %61 %uint_0 +%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 +OpStore %68 %uint_10 +%70 = OpIAdd %uint %61 %uint_1 +%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 +OpStore %71 %uint_23 +%73 = OpIAdd %uint %61 %uint_2 +%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 +OpStore %74 %50 +%75 = OpIAdd %uint %61 %uint_3 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 +OpStore %76 %uint_4 +%80 = OpLoad %v4float %gl_FragCoord +%82 = OpBitcast %v4uint %80 +%83 = OpCompositeExtract %uint %82 0 +%84 = OpIAdd %uint %61 %uint_4 +%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 +OpStore %85 %83 +%86 = OpCompositeExtract %uint %82 1 +%88 = OpIAdd %uint %61 %uint_5 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 +OpStore %89 %86 +%91 = OpIAdd %uint %61 %uint_7 +%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 +OpStore %92 %51 +%94 = OpIAdd %uint %61 %uint_8 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 +OpStore %95 %52 +%97 = OpIAdd %uint %61 %uint_9 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 +OpStore %98 %53 +OpBranch %65 +%65 = OpLabel +OpReturn +OpFunctionEnd +%104 = OpFunction %uint None %105 +%106 = OpFunctionParameter %uint +%107 = OpFunctionParameter %uint +%108 = OpFunctionParameter %uint +%109 = OpFunctionParameter %uint +%110 = OpLabel +%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 +%112 = OpLoad %uint %111 +%113 = OpIAdd %uint %112 %107 +%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 +%115 = OpLoad %uint %114 +%116 = OpIAdd %uint %115 %108 +%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 +%118 = OpLoad %uint %117 +%119 = OpIAdd %uint %118 %109 +%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 +%121 = OpLoad %uint %120 +OpReturnValue %121 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, InstBoundsAndInitLoadUnsizedSSBOArray) { + // Same as Deprecated but declaring as StorageBuffer Block + + const std::string defs_before = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability RuntimeDescriptorArray +OpCapability StorageBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %nu_ii +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %bname "bname" +OpMemberName %bname 0 "a" +OpName %storageBuffer "storageBuffer" +OpName %nu_ii "nu_ii" +OpDecorate %b Location 0 +OpMemberDecorate %bname 0 Offset 0 +OpDecorate %bname Block +OpDecorate %storageBuffer DescriptorSet 0 +OpDecorate %storageBuffer Binding 3 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %16 NonUniform +OpDecorate %20 NonUniform +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%bname = OpTypeStruct %float +%_runtimearr_bname = OpTypeRuntimeArray %bname +%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname +%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability RuntimeDescriptorArray +OpCapability StorageBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %bname "bname" +OpMemberName %bname 0 "a" +OpName %storageBuffer "storageBuffer" +OpName %nu_ii "nu_ii" +OpDecorate %b Location 0 +OpMemberDecorate %bname 0 Offset 0 +OpDecorate %bname Block +OpDecorate %storageBuffer DescriptorSet 0 +OpDecorate %storageBuffer Binding 3 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %7 NonUniform +OpDecorate %102 NonUniform +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_31 Block +OpMemberDecorate %_struct_31 0 Offset 0 +OpDecorate %33 DescriptorSet 7 +OpDecorate %33 Binding 1 +OpDecorate %130 NonUniform +OpDecorate %_struct_55 Block +OpMemberDecorate %_struct_55 0 Offset 0 +OpMemberDecorate %_struct_55 1 Offset 4 +OpDecorate %57 DescriptorSet 7 +OpDecorate %57 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %127 NonUniform +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%bname = OpTypeStruct %float +%_runtimearr_bname = OpTypeRuntimeArray %bname +%_ptr_StorageBuffer__runtimearr_bname = OpTypePointer StorageBuffer %_runtimearr_bname +%storageBuffer = OpVariable %_ptr_StorageBuffer__runtimearr_bname StorageBuffer +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_3 = OpConstant %uint 3 +%26 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_31 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 +%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%49 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_55 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_55 = OpTypePointer StorageBuffer %_struct_55 +%57 = OpVariable %_ptr_StorageBuffer__struct_55 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_45 = OpConstant %uint 45 +%101 = OpConstantNull %float +%105 = OpTypeFunction %uint %uint %uint %uint %uint +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpLoad %int %nu_ii +%19 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %16 %int_0 +%20 = OpLoad %float %19 +OpStore %b %20 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %10 +%19 = OpLabel +%7 = OpLoad %int %nu_ii +%20 = OpAccessChain %_ptr_StorageBuffer_float %storageBuffer %7 %int_0 +%40 = OpFunctionCall %uint %25 %uint_1 %uint_3 +%42 = OpULessThan %bool %7 %40 +OpSelectionMerge %43 None +OpBranchConditional %42 %44 %45 +%44 = OpLabel +%103 = OpBitcast %uint %7 +%122 = OpFunctionCall %uint %104 %uint_0 %uint_0 %uint_3 %103 +%123 = OpULessThan %bool %uint_0 %122 +OpSelectionMerge %124 None +OpBranchConditional %123 %125 %126 +%125 = OpLabel +%127 = OpLoad %float %20 +OpBranch %124 +%126 = OpLabel +%128 = OpBitcast %uint %7 +%129 = OpFunctionCall %void %48 %uint_45 %uint_1 %128 %uint_0 +OpBranch %124 +%124 = OpLabel +%130 = OpPhi %float %127 %125 %101 %126 +OpBranch %43 +%45 = OpLabel +%47 = OpBitcast %uint %7 +%100 = OpFunctionCall %void %48 %uint_45 %uint_0 %47 %40 +OpBranch %43 +%43 = OpLabel +%102 = OpPhi %float %130 %124 %101 %45 +OpStore %b %102 +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%25 = OpFunction %uint None %26 +%27 = OpFunctionParameter %uint +%28 = OpFunctionParameter %uint +%29 = OpLabel +%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 +%36 = OpLoad %uint %35 +%37 = OpIAdd %uint %36 %28 +%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 +%39 = OpLoad %uint %38 +OpReturnValue %39 +OpFunctionEnd +%48 = OpFunction %void None %49 +%50 = OpFunctionParameter %uint +%51 = OpFunctionParameter %uint +%52 = OpFunctionParameter %uint +%53 = OpFunctionParameter %uint +%54 = OpLabel +%58 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_0 +%61 = OpAtomicIAdd %uint %58 %uint_4 %uint_0 %uint_10 +%62 = OpIAdd %uint %61 %uint_10 +%63 = OpArrayLength %uint %57 1 +%64 = OpULessThanEqual %bool %62 %63 +OpSelectionMerge %65 None +OpBranchConditional %64 %66 %65 +%66 = OpLabel +%67 = OpIAdd %uint %61 %uint_0 +%68 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %67 +OpStore %68 %uint_10 +%70 = OpIAdd %uint %61 %uint_1 +%71 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %70 +OpStore %71 %uint_23 +%73 = OpIAdd %uint %61 %uint_2 +%74 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %73 +OpStore %74 %50 +%75 = OpIAdd %uint %61 %uint_3 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %75 +OpStore %76 %uint_4 +%80 = OpLoad %v4float %gl_FragCoord +%82 = OpBitcast %v4uint %80 +%83 = OpCompositeExtract %uint %82 0 +%84 = OpIAdd %uint %61 %uint_4 +%85 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %84 +OpStore %85 %83 +%86 = OpCompositeExtract %uint %82 1 +%88 = OpIAdd %uint %61 %uint_5 +%89 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %88 +OpStore %89 %86 +%91 = OpIAdd %uint %61 %uint_7 +%92 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %91 +OpStore %92 %51 +%94 = OpIAdd %uint %61 %uint_8 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %94 +OpStore %95 %52 +%97 = OpIAdd %uint %61 %uint_9 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %57 %uint_1 %97 +OpStore %98 %53 +OpBranch %65 +%65 = OpLabel +OpReturn +OpFunctionEnd +%104 = OpFunction %uint None %105 +%106 = OpFunctionParameter %uint +%107 = OpFunctionParameter %uint +%108 = OpFunctionParameter %uint +%109 = OpFunctionParameter %uint +%110 = OpLabel +%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %106 +%112 = OpLoad %uint %111 +%113 = OpIAdd %uint %112 %107 +%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 +%115 = OpLoad %uint %114 +%116 = OpIAdd %uint %115 %108 +%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 +%118 = OpLoad %uint %117 +%119 = OpIAdd %uint %118 %109 +%120 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %119 +%121 = OpLoad %uint %120 +OpReturnValue %121 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, InstInitLoadUBOScalar) { + // #version 450 + // #extension GL_EXT_nonuniform_qualifier : enable + // + // layout(location=0) out float b; + // layout(binding=3) uniform uname { float a; } uniformBuffer; + // + // void main() + // { + // b = uniformBuffer.a; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %uname "uname" +OpMemberName %uname 0 "a" +OpName %uniformBuffer "uniformBuffer" +OpDecorate %b Location 0 +OpMemberDecorate %uname 0 Offset 0 +OpDecorate %uname Block +OpDecorate %uniformBuffer DescriptorSet 0 +OpDecorate %uniformBuffer Binding 3 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%uname = OpTypeStruct %float +%_ptr_Uniform_uname = OpTypePointer Uniform %uname +%uniformBuffer = OpVariable %_ptr_Uniform_uname Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %uname "uname" +OpMemberName %uname 0 "a" +OpName %uniformBuffer "uniformBuffer" +OpDecorate %b Location 0 +OpMemberDecorate %uname 0 Offset 0 +OpDecorate %uname Block +OpDecorate %uniformBuffer DescriptorSet 0 +OpDecorate %uniformBuffer Binding 3 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_28 Block +OpMemberDecorate %_struct_28 0 Offset 0 +OpDecorate %30 DescriptorSet 7 +OpDecorate %30 Binding 1 +OpDecorate %_struct_58 Block +OpMemberDecorate %_struct_58 0 Offset 0 +OpMemberDecorate %_struct_58 1 Offset 4 +OpDecorate %60 DescriptorSet 7 +OpDecorate %60 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%uname = OpTypeStruct %float +%_ptr_Uniform_uname = OpTypePointer Uniform %uname +%uniformBuffer = OpVariable %_ptr_Uniform_uname Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_3 = OpConstant %uint 3 +%21 = OpTypeFunction %uint %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_28 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_28 = OpTypePointer StorageBuffer %_struct_28 +%30 = OpVariable %_ptr_StorageBuffer__struct_28 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%uint_1 = OpConstant %uint 1 +%52 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_58 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_58 = OpTypePointer StorageBuffer %_struct_58 +%60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_32 = OpConstant %uint 32 +%104 = OpConstantNull %float +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0 +%16 = OpLoad %float %15 +OpStore %b %16 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %7 +%14 = OpLabel +%15 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %int_0 +%43 = OpFunctionCall %uint %20 %uint_0 %uint_0 %uint_3 %uint_0 +%45 = OpULessThan %bool %uint_0 %43 +OpSelectionMerge %47 None +OpBranchConditional %45 %48 %49 +%48 = OpLabel +%50 = OpLoad %float %15 +OpBranch %47 +%49 = OpLabel +%103 = OpFunctionCall %void %51 %uint_32 %uint_1 %uint_0 %uint_0 +OpBranch %47 +%47 = OpLabel +%105 = OpPhi %float %50 %48 %104 %49 +OpStore %b %105 +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%20 = OpFunction %uint None %21 +%22 = OpFunctionParameter %uint +%23 = OpFunctionParameter %uint +%24 = OpFunctionParameter %uint +%25 = OpFunctionParameter %uint +%26 = OpLabel +%32 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %22 +%33 = OpLoad %uint %32 +%34 = OpIAdd %uint %33 %23 +%35 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %34 +%36 = OpLoad %uint %35 +%37 = OpIAdd %uint %36 %24 +%38 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %37 +%39 = OpLoad %uint %38 +%40 = OpIAdd %uint %39 %25 +%41 = OpAccessChain %_ptr_StorageBuffer_uint %30 %uint_0 %40 +%42 = OpLoad %uint %41 +OpReturnValue %42 +OpFunctionEnd +%51 = OpFunction %void None %52 +%53 = OpFunctionParameter %uint +%54 = OpFunctionParameter %uint +%55 = OpFunctionParameter %uint +%56 = OpFunctionParameter %uint +%57 = OpLabel +%61 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_0 +%64 = OpAtomicIAdd %uint %61 %uint_4 %uint_0 %uint_10 +%65 = OpIAdd %uint %64 %uint_10 +%66 = OpArrayLength %uint %60 1 +%67 = OpULessThanEqual %bool %65 %66 +OpSelectionMerge %68 None +OpBranchConditional %67 %69 %68 +%69 = OpLabel +%70 = OpIAdd %uint %64 %uint_0 +%71 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %70 +OpStore %71 %uint_10 +%73 = OpIAdd %uint %64 %uint_1 +%74 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %73 +OpStore %74 %uint_23 +%76 = OpIAdd %uint %64 %uint_2 +%77 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %76 +OpStore %77 %53 +%78 = OpIAdd %uint %64 %uint_3 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %78 +OpStore %79 %uint_4 +%83 = OpLoad %v4float %gl_FragCoord +%85 = OpBitcast %v4uint %83 +%86 = OpCompositeExtract %uint %85 0 +%87 = OpIAdd %uint %64 %uint_4 +%88 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %87 +OpStore %88 %86 +%89 = OpCompositeExtract %uint %85 1 +%91 = OpIAdd %uint %64 %uint_5 +%92 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %91 +OpStore %92 %89 +%94 = OpIAdd %uint %64 %uint_7 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %94 +OpStore %95 %54 +%97 = OpIAdd %uint %64 %uint_8 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %97 +OpStore %98 %55 +%100 = OpIAdd %uint %64 %uint_9 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %100 +OpStore %101 %56 +OpBranch %68 +%68 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, InstBoundsInitStoreUnsizedSSBOArray) { + // #version 450 + // #extension GL_EXT_nonuniform_qualifier : enable + // + // layout(location=0) in nonuniformEXT flat int nu_ii; + // layout(location=1) in float b; + // + // layout(binding=4) buffer bname { float b; } storageBuffer[]; + // + // void main() + // { + // storageBuffer[nu_ii].b = b; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability RuntimeDescriptorArray +OpCapability StorageBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %nu_ii %b +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %bname "bname" +OpMemberName %bname 0 "b" +OpName %storageBuffer "storageBuffer" +OpName %nu_ii "nu_ii" +OpName %b "b" +OpMemberDecorate %bname 0 Offset 0 +OpDecorate %bname BufferBlock +OpDecorate %storageBuffer DescriptorSet 0 +OpDecorate %storageBuffer Binding 4 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %14 NonUniform +OpDecorate %b Location 1 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%bname = OpTypeStruct %float +%_runtimearr_bname = OpTypeRuntimeArray %bname +%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname +%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_Input_float = OpTypePointer Input %float +%b = OpVariable %_ptr_Input_float Input +%_ptr_Uniform_float = OpTypePointer Uniform %float +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability RuntimeDescriptorArray +OpCapability StorageBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %nu_ii %b %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %bname "bname" +OpMemberName %bname 0 "b" +OpName %storageBuffer "storageBuffer" +OpName %nu_ii "nu_ii" +OpName %b "b" +OpMemberDecorate %bname 0 Offset 0 +OpDecorate %bname BufferBlock +OpDecorate %storageBuffer DescriptorSet 0 +OpDecorate %storageBuffer Binding 4 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %7 NonUniform +OpDecorate %b Location 1 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_31 Block +OpMemberDecorate %_struct_31 0 Offset 0 +OpDecorate %33 DescriptorSet 7 +OpDecorate %33 Binding 1 +OpDecorate %_struct_54 Block +OpMemberDecorate %_struct_54 0 Offset 0 +OpMemberDecorate %_struct_54 1 Offset 4 +OpDecorate %56 DescriptorSet 7 +OpDecorate %56 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%bname = OpTypeStruct %float +%_runtimearr_bname = OpTypeRuntimeArray %bname +%_ptr_Uniform__runtimearr_bname = OpTypePointer Uniform %_runtimearr_bname +%storageBuffer = OpVariable %_ptr_Uniform__runtimearr_bname Uniform +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_Input_float = OpTypePointer Input %float +%b = OpVariable %_ptr_Input_float Input +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_4 = OpConstant %uint 4 +%26 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_31 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_31 = OpTypePointer StorageBuffer %_struct_31 +%33 = OpVariable %_ptr_StorageBuffer__struct_31 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%48 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_54 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_54 = OpTypePointer StorageBuffer %_struct_54 +%56 = OpVariable %_ptr_StorageBuffer__struct_54 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_45 = OpConstant %uint 45 +%102 = OpTypeFunction %uint %uint %uint %uint %uint +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%14 = OpLoad %int %nu_ii +%18 = OpLoad %float %b +%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %14 %int_0 +OpStore %20 %18 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %9 +%18 = OpLabel +%7 = OpLoad %int %nu_ii +%19 = OpLoad %float %b +%20 = OpAccessChain %_ptr_Uniform_float %storageBuffer %7 %int_0 +%40 = OpFunctionCall %uint %25 %uint_1 %uint_4 +%42 = OpULessThan %bool %7 %40 +OpSelectionMerge %43 None +OpBranchConditional %42 %44 %45 +%44 = OpLabel +%100 = OpBitcast %uint %7 +%119 = OpFunctionCall %uint %101 %uint_0 %uint_0 %uint_4 %100 +%120 = OpULessThan %bool %uint_0 %119 +OpSelectionMerge %121 None +OpBranchConditional %120 %122 %123 +%122 = OpLabel +OpStore %20 %19 +OpBranch %121 +%123 = OpLabel +%124 = OpBitcast %uint %7 +%125 = OpFunctionCall %void %47 %uint_45 %uint_1 %124 %uint_0 +OpBranch %121 +%121 = OpLabel +OpBranch %43 +%45 = OpLabel +%46 = OpBitcast %uint %7 +%99 = OpFunctionCall %void %47 %uint_45 %uint_0 %46 %40 +OpBranch %43 +%43 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%25 = OpFunction %uint None %26 +%27 = OpFunctionParameter %uint +%28 = OpFunctionParameter %uint +%29 = OpLabel +%35 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %27 +%36 = OpLoad %uint %35 +%37 = OpIAdd %uint %36 %28 +%38 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %37 +%39 = OpLoad %uint %38 +OpReturnValue %39 +OpFunctionEnd +%47 = OpFunction %void None %48 +%49 = OpFunctionParameter %uint +%50 = OpFunctionParameter %uint +%51 = OpFunctionParameter %uint +%52 = OpFunctionParameter %uint +%53 = OpLabel +%57 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_0 +%59 = OpAtomicIAdd %uint %57 %uint_4 %uint_0 %uint_10 +%60 = OpIAdd %uint %59 %uint_10 +%61 = OpArrayLength %uint %56 1 +%62 = OpULessThanEqual %bool %60 %61 +OpSelectionMerge %63 None +OpBranchConditional %62 %64 %63 +%64 = OpLabel +%65 = OpIAdd %uint %59 %uint_0 +%66 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %65 +OpStore %66 %uint_10 +%68 = OpIAdd %uint %59 %uint_1 +%69 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %68 +OpStore %69 %uint_23 +%71 = OpIAdd %uint %59 %uint_2 +%72 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %71 +OpStore %72 %49 +%74 = OpIAdd %uint %59 %uint_3 +%75 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %74 +OpStore %75 %uint_4 +%79 = OpLoad %v4float %gl_FragCoord +%81 = OpBitcast %v4uint %79 +%82 = OpCompositeExtract %uint %81 0 +%83 = OpIAdd %uint %59 %uint_4 +%84 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %83 +OpStore %84 %82 +%85 = OpCompositeExtract %uint %81 1 +%87 = OpIAdd %uint %59 %uint_5 +%88 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %87 +OpStore %88 %85 +%90 = OpIAdd %uint %59 %uint_7 +%91 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %90 +OpStore %91 %50 +%93 = OpIAdd %uint %59 %uint_8 +%94 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %93 +OpStore %94 %51 +%96 = OpIAdd %uint %59 %uint_9 +%97 = OpAccessChain %_ptr_StorageBuffer_uint %56 %uint_1 %96 +OpStore %97 %52 +OpBranch %63 +%63 = OpLabel +OpReturn +OpFunctionEnd +%101 = OpFunction %uint None %102 +%103 = OpFunctionParameter %uint +%104 = OpFunctionParameter %uint +%105 = OpFunctionParameter %uint +%106 = OpFunctionParameter %uint +%107 = OpLabel +%108 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %103 +%109 = OpLoad %uint %108 +%110 = OpIAdd %uint %109 %104 +%111 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %110 +%112 = OpLoad %uint %111 +%113 = OpIAdd %uint %112 %105 +%114 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %113 +%115 = OpLoad %uint %114 +%116 = OpIAdd %uint %115 %106 +%117 = OpAccessChain %_ptr_StorageBuffer_uint %33 %uint_0 %116 +%118 = OpLoad %uint %117 +OpReturnValue %118 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, InstBoundsInitLoadSizedUBOArray) { + // #version 450 + // #extension GL_EXT_nonuniform_qualifier : enable + // + // layout(location=0) in nonuniformEXT flat int nu_ii; + // layout(location=0) out float b; + // + // layout(binding=3) uniform uname { float a; } uniformBuffer[128]; + // + // void main() + // { + // b = uniformBuffer[nu_ii].a; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability UniformBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %nu_ii +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %uname "uname" +OpMemberName %uname 0 "a" +OpName %uniformBuffer "uniformBuffer" +OpName %nu_ii "nu_ii" +OpDecorate %b Location 0 +OpMemberDecorate %uname 0 Offset 0 +OpDecorate %uname Block +OpDecorate %uniformBuffer DescriptorSet 0 +OpDecorate %uniformBuffer Binding 3 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %18 NonUniform +OpDecorate %22 NonUniform +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%uname = OpTypeStruct %float +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_uname_uint_128 = OpTypeArray %uname %uint_128 +%_ptr_Uniform__arr_uname_uint_128 = OpTypePointer Uniform %_arr_uname_uint_128 +%uniformBuffer = OpVariable %_ptr_Uniform__arr_uname_uint_128 Uniform +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability UniformBufferArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %b %nu_ii %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %b "b" +OpName %uname "uname" +OpMemberName %uname 0 "a" +OpName %uniformBuffer "uniformBuffer" +OpName %nu_ii "nu_ii" +OpDecorate %b Location 0 +OpMemberDecorate %uname 0 Offset 0 +OpDecorate %uname Block +OpDecorate %uniformBuffer DescriptorSet 0 +OpDecorate %uniformBuffer Binding 3 +OpDecorate %nu_ii Flat +OpDecorate %nu_ii Location 0 +OpDecorate %nu_ii NonUniform +OpDecorate %7 NonUniform +OpDecorate %89 NonUniform +OpDecorate %120 NonUniform +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpMemberDecorate %_struct_39 1 Offset 4 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %_struct_98 Block +OpMemberDecorate %_struct_98 0 Offset 0 +OpDecorate %100 DescriptorSet 7 +OpDecorate %100 Binding 1 +OpDecorate %117 NonUniform +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float +%b = OpVariable %_ptr_Output_float Output +%uname = OpTypeStruct %float +%uint = OpTypeInt 32 0 +%uint_128 = OpConstant %uint 128 +%_arr_uname_uint_128 = OpTypeArray %uname %uint_128 +%_ptr_Uniform__arr_uname_uint_128 = OpTypePointer Uniform %_arr_uname_uint_128 +%uniformBuffer = OpVariable %_ptr_Uniform__arr_uname_uint_128 Uniform +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%nu_ii = OpVariable %_ptr_Input_int Input +%int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%32 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_39 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_46 = OpConstant %uint 46 +%88 = OpConstantNull %float +%92 = OpTypeFunction %uint %uint %uint %uint %uint +%_struct_98 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_98 = OpTypePointer StorageBuffer %_struct_98 +%100 = OpVariable %_ptr_StorageBuffer__struct_98 StorageBuffer +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%18 = OpLoad %int %nu_ii +%21 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %18 %int_0 +%22 = OpLoad %float %21 +OpStore %b %22 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %10 +%21 = OpLabel +%7 = OpLoad %int %nu_ii +%22 = OpAccessChain %_ptr_Uniform_float %uniformBuffer %7 %int_0 +%25 = OpULessThan %bool %7 %uint_128 +OpSelectionMerge %26 None +OpBranchConditional %25 %27 %28 +%27 = OpLabel +%90 = OpBitcast %uint %7 +%112 = OpFunctionCall %uint %91 %uint_0 %uint_0 %uint_3 %90 +%113 = OpULessThan %bool %uint_0 %112 +OpSelectionMerge %114 None +OpBranchConditional %113 %115 %116 +%115 = OpLabel +%117 = OpLoad %float %22 +OpBranch %114 +%116 = OpLabel +%118 = OpBitcast %uint %7 +%119 = OpFunctionCall %void %31 %uint_46 %uint_1 %118 %uint_0 +OpBranch %114 +%114 = OpLabel +%120 = OpPhi %float %117 %115 %88 %116 +OpBranch %26 +%28 = OpLabel +%30 = OpBitcast %uint %7 +%87 = OpFunctionCall %void %31 %uint_46 %uint_0 %30 %uint_128 +OpBranch %26 +%26 = OpLabel +%89 = OpPhi %float %120 %114 %88 %28 +OpStore %b %89 +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%31 = OpFunction %void None %32 +%33 = OpFunctionParameter %uint +%34 = OpFunctionParameter %uint +%35 = OpFunctionParameter %uint +%36 = OpFunctionParameter %uint +%37 = OpLabel +%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 +%46 = OpAtomicIAdd %uint %43 %uint_4 %uint_0 %uint_10 +%47 = OpIAdd %uint %46 %uint_10 +%48 = OpArrayLength %uint %41 1 +%49 = OpULessThanEqual %bool %47 %48 +OpSelectionMerge %50 None +OpBranchConditional %49 %51 %50 +%51 = OpLabel +%52 = OpIAdd %uint %46 %uint_0 +%54 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %52 +OpStore %54 %uint_10 +%56 = OpIAdd %uint %46 %uint_1 +%57 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %56 +OpStore %57 %uint_23 +%59 = OpIAdd %uint %46 %uint_2 +%60 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %59 +OpStore %60 %33 +%62 = OpIAdd %uint %46 %uint_3 +%63 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %62 +OpStore %63 %uint_4 +%67 = OpLoad %v4float %gl_FragCoord +%69 = OpBitcast %v4uint %67 +%70 = OpCompositeExtract %uint %69 0 +%71 = OpIAdd %uint %46 %uint_4 +%72 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %71 +OpStore %72 %70 +%73 = OpCompositeExtract %uint %69 1 +%75 = OpIAdd %uint %46 %uint_5 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %75 +OpStore %76 %73 +%78 = OpIAdd %uint %46 %uint_7 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %78 +OpStore %79 %34 +%81 = OpIAdd %uint %46 %uint_8 +%82 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %81 +OpStore %82 %35 +%84 = OpIAdd %uint %46 %uint_9 +%85 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_1 %84 +OpStore %85 %36 +OpBranch %50 +%50 = OpLabel +OpReturn +OpFunctionEnd +%91 = OpFunction %uint None %92 +%93 = OpFunctionParameter %uint +%94 = OpFunctionParameter %uint +%95 = OpFunctionParameter %uint +%96 = OpFunctionParameter %uint +%97 = OpLabel +%101 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %93 +%102 = OpLoad %uint %101 +%103 = OpIAdd %uint %102 %94 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %103 +%105 = OpLoad %uint %104 +%106 = OpIAdd %uint %105 %95 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %106 +%108 = OpLoad %uint %107 +%109 = OpIAdd %uint %108 %96 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %100 %uint_0 %109 +%111 = OpLoad %uint %110 +OpReturnValue %111 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, + InstBoundsComputeShaderInitLoadVariableSizedSampledImagesArray) { + // #version 450 + // #extension GL_EXT_nonuniform_qualifier : enable + // + // layout (local_size_x = 1, local_size_y = 1) in; + // + // layout(set = 0, binding = 0, std140) buffer Input { + // uint index; + // float red; + // } sbo; + // + // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[]; + // + // void main() + // { + // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability RuntimeDescriptorArray +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %Input "Input" +OpMemberName %Input 0 "index" +OpMemberName %Input 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %Input 0 Offset 0 +OpMemberDecorate %Input 1 Offset 4 +OpDecorate %Input BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +%void = OpTypeVoid +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%Input = OpTypeStruct %uint %float +%_ptr_Uniform_Input = OpTypePointer Uniform %Input +%sbo = OpVariable %_ptr_Uniform_Input Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability RuntimeDescriptorArray +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %Input "Input" +OpMemberName %Input 0 "index" +OpMemberName %Input 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %Input 0 Offset 0 +OpMemberDecorate %Input 1 Offset 4 +OpDecorate %Input BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 1 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%void = OpTypeVoid +%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%Input = OpTypeStruct %uint %float +%_ptr_Uniform_Input = OpTypePointer Uniform %Input +%sbo = OpVariable %_ptr_Uniform_Input Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%20 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint_1 = OpConstant %uint 1 +%34 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_39 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%57 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_5 = OpConstant %uint 5 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_50 = OpConstant %uint 50 +%112 = OpConstantNull %v4float +%115 = OpTypeFunction %uint %uint %uint %uint %uint +%uint_47 = OpConstant %uint 47 +%140 = OpConstantNull %uint +%uint_53 = OpConstant %uint 53 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %7 +%24 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%132 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_0 %uint_0 +%133 = OpULessThan %bool %uint_0 %132 +OpSelectionMerge %134 None +OpBranchConditional %133 %135 %136 +%135 = OpLabel +%137 = OpLoad %uint %25 +OpBranch %134 +%136 = OpLabel +%139 = OpFunctionCall %void %56 %uint_47 %uint_1 %uint_0 %uint_0 +OpBranch %134 +%134 = OpLabel +%141 = OpPhi %uint %137 %135 %140 %136 +%27 = OpAccessChain %_ptr_UniformConstant_13 %images %141 +%28 = OpLoad %13 %27 +%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 +%50 = OpULessThan %bool %141 %48 +OpSelectionMerge %51 None +OpBranchConditional %50 %52 %53 +%52 = OpLabel +%54 = OpLoad %13 %27 +%142 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_1 %141 +%143 = OpULessThan %bool %uint_0 %142 +OpSelectionMerge %144 None +OpBranchConditional %143 %145 %146 +%145 = OpLabel +%147 = OpLoad %13 %27 +%148 = OpImageRead %v4float %147 %20 +OpBranch %144 +%146 = OpLabel +%149 = OpFunctionCall %void %56 %uint_50 %uint_1 %141 %uint_0 +OpBranch %144 +%144 = OpLabel +%150 = OpPhi %v4float %148 %145 %112 %146 +OpBranch %51 +%53 = OpLabel +%111 = OpFunctionCall %void %56 %uint_50 %uint_0 %141 %48 +OpBranch %51 +%51 = OpLabel +%113 = OpPhi %v4float %150 %144 %112 %53 +%30 = OpCompositeExtract %float %113 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +%151 = OpFunctionCall %uint %114 %uint_0 %uint_0 %uint_0 %uint_0 +%152 = OpULessThan %bool %uint_0 %151 +OpSelectionMerge %153 None +OpBranchConditional %152 %154 %155 +%154 = OpLabel +OpStore %31 %30 +OpBranch %153 +%155 = OpLabel +%157 = OpFunctionCall %void %56 %uint_53 %uint_1 %uint_0 %uint_0 +OpBranch %153 +%153 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%33 = OpFunction %uint None %34 +%35 = OpFunctionParameter %uint +%36 = OpFunctionParameter %uint +%37 = OpLabel +%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 +%44 = OpLoad %uint %43 +%45 = OpIAdd %uint %44 %36 +%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 +%47 = OpLoad %uint %46 +OpReturnValue %47 +OpFunctionEnd +%56 = OpFunction %void None %57 +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpFunctionParameter %uint +%62 = OpLabel +%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 +%70 = OpIAdd %uint %69 %uint_10 +%71 = OpArrayLength %uint %65 1 +%72 = OpULessThanEqual %bool %70 %71 +OpSelectionMerge %73 None +OpBranchConditional %72 %74 %73 +%74 = OpLabel +%75 = OpIAdd %uint %69 %uint_0 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 +OpStore %76 %uint_10 +%78 = OpIAdd %uint %69 %uint_1 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 +OpStore %79 %uint_23 +%81 = OpIAdd %uint %69 %uint_2 +%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 +OpStore %82 %58 +%85 = OpIAdd %uint %69 %uint_3 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 +OpStore %86 %uint_5 +%90 = OpLoad %v3uint %gl_GlobalInvocationID +%91 = OpCompositeExtract %uint %90 0 +%92 = OpCompositeExtract %uint %90 1 +%93 = OpCompositeExtract %uint %90 2 +%94 = OpIAdd %uint %69 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %91 +%96 = OpIAdd %uint %69 %uint_5 +%97 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %96 +OpStore %97 %92 +%99 = OpIAdd %uint %69 %uint_6 +%100 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %99 +OpStore %100 %93 +%102 = OpIAdd %uint %69 %uint_7 +%103 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %102 +OpStore %103 %59 +%105 = OpIAdd %uint %69 %uint_8 +%106 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %105 +OpStore %106 %60 +%108 = OpIAdd %uint %69 %uint_9 +%109 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %108 +OpStore %109 %61 +OpBranch %73 +%73 = OpLabel +OpReturn +OpFunctionEnd +%114 = OpFunction %uint None %115 +%116 = OpFunctionParameter %uint +%117 = OpFunctionParameter %uint +%118 = OpFunctionParameter %uint +%119 = OpFunctionParameter %uint +%120 = OpLabel +%121 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %116 +%122 = OpLoad %uint %121 +%123 = OpIAdd %uint %122 %117 +%124 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %123 +%125 = OpLoad %uint %124 +%126 = OpIAdd %uint %125 %118 +%127 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %126 +%128 = OpLoad %uint %127 +%129 = OpIAdd %uint %128 %119 +%130 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %129 +%131 = OpLoad %uint %130 +OpReturnValue %131 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, + InstBoundsRayGenerationInitLoadVariableSizedSampledImagesArray) { + // #version 460 + // #extension GL_EXT_nonuniform_qualifier : require + // #extension GL_NV_ray_tracing : require + // + // layout(set = 0, binding = 0, std140) buffer StorageBuffer { + // uint index; + // float red; + // } sbo; + // + // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[]; + // + // void main() + // { + // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; + // } + + const std::string defs_before = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationNV %main "main" +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +%void = OpTypeVoid +)"; + + const std::string defs_after = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint RayGenerationNV %main "main" %89 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 1 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %89 BuiltIn LaunchIdNV +%void = OpTypeVoid +)"; + + const std::string func_before = + R"(%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%20 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint_1 = OpConstant %uint 1 +%34 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_39 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%57 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_5313 = OpConstant %uint 5313 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%89 = OpVariable %_ptr_Input_v3uint Input +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_51 = OpConstant %uint 51 +%113 = OpConstantNull %v4float +%116 = OpTypeFunction %uint %uint %uint %uint %uint +%uint_48 = OpConstant %uint 48 +%141 = OpConstantNull %uint +%uint_54 = OpConstant %uint 54 +%main = OpFunction %void None %7 +%24 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%134 = OpULessThan %bool %uint_0 %133 +OpSelectionMerge %135 None +OpBranchConditional %134 %136 %137 +%136 = OpLabel +%138 = OpLoad %uint %25 +OpBranch %135 +%137 = OpLabel +%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 +OpBranch %135 +%135 = OpLabel +%142 = OpPhi %uint %138 %136 %141 %137 +%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +%28 = OpLoad %13 %27 +%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 +%50 = OpULessThan %bool %142 %48 +OpSelectionMerge %51 None +OpBranchConditional %50 %52 %53 +%52 = OpLabel +%54 = OpLoad %13 %27 +%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 +%144 = OpULessThan %bool %uint_0 %143 +OpSelectionMerge %145 None +OpBranchConditional %144 %146 %147 +%146 = OpLabel +%148 = OpLoad %13 %27 +%149 = OpImageRead %v4float %148 %20 +OpBranch %145 +%147 = OpLabel +%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 +OpBranch %145 +%145 = OpLabel +%151 = OpPhi %v4float %149 %146 %113 %147 +OpBranch %51 +%53 = OpLabel +%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 +OpBranch %51 +%51 = OpLabel +%114 = OpPhi %v4float %151 %145 %113 %53 +%30 = OpCompositeExtract %float %114 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%153 = OpULessThan %bool %uint_0 %152 +OpSelectionMerge %154 None +OpBranchConditional %153 %155 %156 +%155 = OpLabel +OpStore %31 %30 +OpBranch %154 +%156 = OpLabel +%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 +OpBranch %154 +%154 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%33 = OpFunction %uint None %34 +%35 = OpFunctionParameter %uint +%36 = OpFunctionParameter %uint +%37 = OpLabel +%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 +%44 = OpLoad %uint %43 +%45 = OpIAdd %uint %44 %36 +%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 +%47 = OpLoad %uint %46 +OpReturnValue %47 +OpFunctionEnd +%56 = OpFunction %void None %57 +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpFunctionParameter %uint +%62 = OpLabel +%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 +%70 = OpIAdd %uint %69 %uint_10 +%71 = OpArrayLength %uint %65 1 +%72 = OpULessThanEqual %bool %70 %71 +OpSelectionMerge %73 None +OpBranchConditional %72 %74 %73 +%74 = OpLabel +%75 = OpIAdd %uint %69 %uint_0 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 +OpStore %76 %uint_10 +%78 = OpIAdd %uint %69 %uint_1 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 +OpStore %79 %uint_23 +%81 = OpIAdd %uint %69 %uint_2 +%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 +OpStore %82 %58 +%85 = OpIAdd %uint %69 %uint_3 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 +OpStore %86 %uint_5313 +%90 = OpLoad %v3uint %89 +%91 = OpCompositeExtract %uint %90 0 +%92 = OpCompositeExtract %uint %90 1 +%93 = OpCompositeExtract %uint %90 2 +%94 = OpIAdd %uint %69 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %91 +%97 = OpIAdd %uint %69 %uint_5 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 +OpStore %98 %92 +%100 = OpIAdd %uint %69 %uint_6 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 +OpStore %101 %93 +%103 = OpIAdd %uint %69 %uint_7 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 +OpStore %104 %59 +%106 = OpIAdd %uint %69 %uint_8 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 +OpStore %107 %60 +%109 = OpIAdd %uint %69 %uint_9 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 +OpStore %110 %61 +OpBranch %73 +%73 = OpLabel +OpReturn +OpFunctionEnd +%115 = OpFunction %uint None %116 +%117 = OpFunctionParameter %uint +%118 = OpFunctionParameter %uint +%119 = OpFunctionParameter %uint +%120 = OpFunctionParameter %uint +%121 = OpLabel +%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 +%123 = OpLoad %uint %122 +%124 = OpIAdd %uint %123 %118 +%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 +%126 = OpLoad %uint %125 +%127 = OpIAdd %uint %126 %119 +%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 +%129 = OpLoad %uint %128 +%130 = OpIAdd %uint %129 %120 +%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 +%132 = OpLoad %uint %131 +OpReturnValue %132 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, + InstBoundsIntersectionInitLoadVariableSizedSampledImagesArray) { + // #version 460 + // #extension GL_EXT_nonuniform_qualifier : require + // #extension GL_NV_ray_tracing : require + // + // layout(set = 0, binding = 0, std140) buffer StorageBuffer { + // uint index; + // float red; + // } sbo; + // + // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[]; + // + // void main() + // { + // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; + // } + + const std::string defs_before = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint IntersectionNV %main "main" +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +%void = OpTypeVoid +)"; + + const std::string defs_after = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint IntersectionNV %main "main" %89 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 1 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %89 BuiltIn LaunchIdNV +%void = OpTypeVoid +)"; + + const std::string func_before = + R"(%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%20 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint_1 = OpConstant %uint 1 +%34 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_39 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%57 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_5314 = OpConstant %uint 5314 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%89 = OpVariable %_ptr_Input_v3uint Input +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_51 = OpConstant %uint 51 +%113 = OpConstantNull %v4float +%116 = OpTypeFunction %uint %uint %uint %uint %uint +%uint_48 = OpConstant %uint 48 +%141 = OpConstantNull %uint +%uint_54 = OpConstant %uint 54 +%main = OpFunction %void None %7 +%24 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%134 = OpULessThan %bool %uint_0 %133 +OpSelectionMerge %135 None +OpBranchConditional %134 %136 %137 +%136 = OpLabel +%138 = OpLoad %uint %25 +OpBranch %135 +%137 = OpLabel +%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 +OpBranch %135 +%135 = OpLabel +%142 = OpPhi %uint %138 %136 %141 %137 +%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +%28 = OpLoad %13 %27 +%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 +%50 = OpULessThan %bool %142 %48 +OpSelectionMerge %51 None +OpBranchConditional %50 %52 %53 +%52 = OpLabel +%54 = OpLoad %13 %27 +%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 +%144 = OpULessThan %bool %uint_0 %143 +OpSelectionMerge %145 None +OpBranchConditional %144 %146 %147 +%146 = OpLabel +%148 = OpLoad %13 %27 +%149 = OpImageRead %v4float %148 %20 +OpBranch %145 +%147 = OpLabel +%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 +OpBranch %145 +%145 = OpLabel +%151 = OpPhi %v4float %149 %146 %113 %147 +OpBranch %51 +%53 = OpLabel +%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 +OpBranch %51 +%51 = OpLabel +%114 = OpPhi %v4float %151 %145 %113 %53 +%30 = OpCompositeExtract %float %114 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%153 = OpULessThan %bool %uint_0 %152 +OpSelectionMerge %154 None +OpBranchConditional %153 %155 %156 +%155 = OpLabel +OpStore %31 %30 +OpBranch %154 +%156 = OpLabel +%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 +OpBranch %154 +%154 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%33 = OpFunction %uint None %34 +%35 = OpFunctionParameter %uint +%36 = OpFunctionParameter %uint +%37 = OpLabel +%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 +%44 = OpLoad %uint %43 +%45 = OpIAdd %uint %44 %36 +%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 +%47 = OpLoad %uint %46 +OpReturnValue %47 +OpFunctionEnd +%56 = OpFunction %void None %57 +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpFunctionParameter %uint +%62 = OpLabel +%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 +%70 = OpIAdd %uint %69 %uint_10 +%71 = OpArrayLength %uint %65 1 +%72 = OpULessThanEqual %bool %70 %71 +OpSelectionMerge %73 None +OpBranchConditional %72 %74 %73 +%74 = OpLabel +%75 = OpIAdd %uint %69 %uint_0 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 +OpStore %76 %uint_10 +%78 = OpIAdd %uint %69 %uint_1 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 +OpStore %79 %uint_23 +%81 = OpIAdd %uint %69 %uint_2 +%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 +OpStore %82 %58 +%85 = OpIAdd %uint %69 %uint_3 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 +OpStore %86 %uint_5314 +%90 = OpLoad %v3uint %89 +%91 = OpCompositeExtract %uint %90 0 +%92 = OpCompositeExtract %uint %90 1 +%93 = OpCompositeExtract %uint %90 2 +%94 = OpIAdd %uint %69 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %91 +%97 = OpIAdd %uint %69 %uint_5 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 +OpStore %98 %92 +%100 = OpIAdd %uint %69 %uint_6 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 +OpStore %101 %93 +%103 = OpIAdd %uint %69 %uint_7 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 +OpStore %104 %59 +%106 = OpIAdd %uint %69 %uint_8 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 +OpStore %107 %60 +%109 = OpIAdd %uint %69 %uint_9 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 +OpStore %110 %61 +OpBranch %73 +%73 = OpLabel +OpReturn +OpFunctionEnd +%115 = OpFunction %uint None %116 +%117 = OpFunctionParameter %uint +%118 = OpFunctionParameter %uint +%119 = OpFunctionParameter %uint +%120 = OpFunctionParameter %uint +%121 = OpLabel +%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 +%123 = OpLoad %uint %122 +%124 = OpIAdd %uint %123 %118 +%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 +%126 = OpLoad %uint %125 +%127 = OpIAdd %uint %126 %119 +%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 +%129 = OpLoad %uint %128 +%130 = OpIAdd %uint %129 %120 +%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 +%132 = OpLoad %uint %131 +OpReturnValue %132 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, + InstBoundsAnyHitInitLoadVariableSizedSampledImagesArray) { + // #version 460 + // #extension GL_EXT_nonuniform_qualifier : require + // #extension GL_NV_ray_tracing : require + // + // layout(set = 0, binding = 0, std140) buffer StorageBuffer { + // uint index; + // float red; + // } sbo; + // + // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[]; + // + // void main() + // { + // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; + // } + + const std::string defs_before = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint AnyHitNV %main "main" +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +%void = OpTypeVoid +)"; + + const std::string defs_after = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint AnyHitNV %main "main" %89 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 1 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %89 BuiltIn LaunchIdNV +%void = OpTypeVoid +)"; + + const std::string func_before = + R"(%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%20 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint_1 = OpConstant %uint 1 +%34 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_39 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%57 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_5315 = OpConstant %uint 5315 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%89 = OpVariable %_ptr_Input_v3uint Input +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_51 = OpConstant %uint 51 +%113 = OpConstantNull %v4float +%116 = OpTypeFunction %uint %uint %uint %uint %uint +%uint_48 = OpConstant %uint 48 +%141 = OpConstantNull %uint +%uint_54 = OpConstant %uint 54 +%main = OpFunction %void None %7 +%24 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%134 = OpULessThan %bool %uint_0 %133 +OpSelectionMerge %135 None +OpBranchConditional %134 %136 %137 +%136 = OpLabel +%138 = OpLoad %uint %25 +OpBranch %135 +%137 = OpLabel +%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 +OpBranch %135 +%135 = OpLabel +%142 = OpPhi %uint %138 %136 %141 %137 +%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +%28 = OpLoad %13 %27 +%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 +%50 = OpULessThan %bool %142 %48 +OpSelectionMerge %51 None +OpBranchConditional %50 %52 %53 +%52 = OpLabel +%54 = OpLoad %13 %27 +%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 +%144 = OpULessThan %bool %uint_0 %143 +OpSelectionMerge %145 None +OpBranchConditional %144 %146 %147 +%146 = OpLabel +%148 = OpLoad %13 %27 +%149 = OpImageRead %v4float %148 %20 +OpBranch %145 +%147 = OpLabel +%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 +OpBranch %145 +%145 = OpLabel +%151 = OpPhi %v4float %149 %146 %113 %147 +OpBranch %51 +%53 = OpLabel +%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 +OpBranch %51 +%51 = OpLabel +%114 = OpPhi %v4float %151 %145 %113 %53 +%30 = OpCompositeExtract %float %114 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%153 = OpULessThan %bool %uint_0 %152 +OpSelectionMerge %154 None +OpBranchConditional %153 %155 %156 +%155 = OpLabel +OpStore %31 %30 +OpBranch %154 +%156 = OpLabel +%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 +OpBranch %154 +%154 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%33 = OpFunction %uint None %34 +%35 = OpFunctionParameter %uint +%36 = OpFunctionParameter %uint +%37 = OpLabel +%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 +%44 = OpLoad %uint %43 +%45 = OpIAdd %uint %44 %36 +%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 +%47 = OpLoad %uint %46 +OpReturnValue %47 +OpFunctionEnd +%56 = OpFunction %void None %57 +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpFunctionParameter %uint +%62 = OpLabel +%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 +%70 = OpIAdd %uint %69 %uint_10 +%71 = OpArrayLength %uint %65 1 +%72 = OpULessThanEqual %bool %70 %71 +OpSelectionMerge %73 None +OpBranchConditional %72 %74 %73 +%74 = OpLabel +%75 = OpIAdd %uint %69 %uint_0 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 +OpStore %76 %uint_10 +%78 = OpIAdd %uint %69 %uint_1 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 +OpStore %79 %uint_23 +%81 = OpIAdd %uint %69 %uint_2 +%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 +OpStore %82 %58 +%85 = OpIAdd %uint %69 %uint_3 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 +OpStore %86 %uint_5315 +%90 = OpLoad %v3uint %89 +%91 = OpCompositeExtract %uint %90 0 +%92 = OpCompositeExtract %uint %90 1 +%93 = OpCompositeExtract %uint %90 2 +%94 = OpIAdd %uint %69 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %91 +%97 = OpIAdd %uint %69 %uint_5 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 +OpStore %98 %92 +%100 = OpIAdd %uint %69 %uint_6 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 +OpStore %101 %93 +%103 = OpIAdd %uint %69 %uint_7 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 +OpStore %104 %59 +%106 = OpIAdd %uint %69 %uint_8 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 +OpStore %107 %60 +%109 = OpIAdd %uint %69 %uint_9 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 +OpStore %110 %61 +OpBranch %73 +%73 = OpLabel +OpReturn +OpFunctionEnd +%115 = OpFunction %uint None %116 +%117 = OpFunctionParameter %uint +%118 = OpFunctionParameter %uint +%119 = OpFunctionParameter %uint +%120 = OpFunctionParameter %uint +%121 = OpLabel +%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 +%123 = OpLoad %uint %122 +%124 = OpIAdd %uint %123 %118 +%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 +%126 = OpLoad %uint %125 +%127 = OpIAdd %uint %126 %119 +%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 +%129 = OpLoad %uint %128 +%130 = OpIAdd %uint %129 %120 +%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 +%132 = OpLoad %uint %131 +OpReturnValue %132 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, + InstBoundsClosestHitInitLoadVariableSizedSampledImagesArray) { + // #version 460 + // #extension GL_EXT_nonuniform_qualifier : require + // #extension GL_NV_ray_tracing : require + // + // layout(set = 0, binding = 0, std140) buffer StorageBuffer { + // uint index; + // float red; + // } sbo; + // + // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[]; + // + // void main() + // { + // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; + // } + + const std::string defs_before = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint ClosestHitNV %main "main" +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +%void = OpTypeVoid +)"; + + const std::string defs_after = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint ClosestHitNV %main "main" %89 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 1 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %89 BuiltIn LaunchIdNV +%void = OpTypeVoid +)"; + + const std::string func_before = + R"(%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%20 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint_1 = OpConstant %uint 1 +%34 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_39 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%57 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_5316 = OpConstant %uint 5316 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%89 = OpVariable %_ptr_Input_v3uint Input +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_51 = OpConstant %uint 51 +%113 = OpConstantNull %v4float +%116 = OpTypeFunction %uint %uint %uint %uint %uint +%uint_48 = OpConstant %uint 48 +%141 = OpConstantNull %uint +%uint_54 = OpConstant %uint 54 +%main = OpFunction %void None %7 +%24 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%134 = OpULessThan %bool %uint_0 %133 +OpSelectionMerge %135 None +OpBranchConditional %134 %136 %137 +%136 = OpLabel +%138 = OpLoad %uint %25 +OpBranch %135 +%137 = OpLabel +%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 +OpBranch %135 +%135 = OpLabel +%142 = OpPhi %uint %138 %136 %141 %137 +%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +%28 = OpLoad %13 %27 +%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 +%50 = OpULessThan %bool %142 %48 +OpSelectionMerge %51 None +OpBranchConditional %50 %52 %53 +%52 = OpLabel +%54 = OpLoad %13 %27 +%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 +%144 = OpULessThan %bool %uint_0 %143 +OpSelectionMerge %145 None +OpBranchConditional %144 %146 %147 +%146 = OpLabel +%148 = OpLoad %13 %27 +%149 = OpImageRead %v4float %148 %20 +OpBranch %145 +%147 = OpLabel +%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 +OpBranch %145 +%145 = OpLabel +%151 = OpPhi %v4float %149 %146 %113 %147 +OpBranch %51 +%53 = OpLabel +%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 +OpBranch %51 +%51 = OpLabel +%114 = OpPhi %v4float %151 %145 %113 %53 +%30 = OpCompositeExtract %float %114 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%153 = OpULessThan %bool %uint_0 %152 +OpSelectionMerge %154 None +OpBranchConditional %153 %155 %156 +%155 = OpLabel +OpStore %31 %30 +OpBranch %154 +%156 = OpLabel +%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 +OpBranch %154 +%154 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%33 = OpFunction %uint None %34 +%35 = OpFunctionParameter %uint +%36 = OpFunctionParameter %uint +%37 = OpLabel +%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 +%44 = OpLoad %uint %43 +%45 = OpIAdd %uint %44 %36 +%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 +%47 = OpLoad %uint %46 +OpReturnValue %47 +OpFunctionEnd +%56 = OpFunction %void None %57 +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpFunctionParameter %uint +%62 = OpLabel +%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 +%70 = OpIAdd %uint %69 %uint_10 +%71 = OpArrayLength %uint %65 1 +%72 = OpULessThanEqual %bool %70 %71 +OpSelectionMerge %73 None +OpBranchConditional %72 %74 %73 +%74 = OpLabel +%75 = OpIAdd %uint %69 %uint_0 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 +OpStore %76 %uint_10 +%78 = OpIAdd %uint %69 %uint_1 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 +OpStore %79 %uint_23 +%81 = OpIAdd %uint %69 %uint_2 +%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 +OpStore %82 %58 +%85 = OpIAdd %uint %69 %uint_3 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 +OpStore %86 %uint_5316 +%90 = OpLoad %v3uint %89 +%91 = OpCompositeExtract %uint %90 0 +%92 = OpCompositeExtract %uint %90 1 +%93 = OpCompositeExtract %uint %90 2 +%94 = OpIAdd %uint %69 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %91 +%97 = OpIAdd %uint %69 %uint_5 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 +OpStore %98 %92 +%100 = OpIAdd %uint %69 %uint_6 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 +OpStore %101 %93 +%103 = OpIAdd %uint %69 %uint_7 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 +OpStore %104 %59 +%106 = OpIAdd %uint %69 %uint_8 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 +OpStore %107 %60 +%109 = OpIAdd %uint %69 %uint_9 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 +OpStore %110 %61 +OpBranch %73 +%73 = OpLabel +OpReturn +OpFunctionEnd +%115 = OpFunction %uint None %116 +%117 = OpFunctionParameter %uint +%118 = OpFunctionParameter %uint +%119 = OpFunctionParameter %uint +%120 = OpFunctionParameter %uint +%121 = OpLabel +%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 +%123 = OpLoad %uint %122 +%124 = OpIAdd %uint %123 %118 +%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 +%126 = OpLoad %uint %125 +%127 = OpIAdd %uint %126 %119 +%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 +%129 = OpLoad %uint %128 +%130 = OpIAdd %uint %129 %120 +%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 +%132 = OpLoad %uint %131 +OpReturnValue %132 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, + InstBoundsMissInitLoadVariableSizedSampledImagesArray) { + // #version 460 + // #extension GL_EXT_nonuniform_qualifier : require + // #extension GL_NV_ray_tracing : require + // + // layout(set = 0, binding = 0, std140) buffer StorageBuffer { + // uint index; + // float red; + // } sbo; + // + // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[]; + // + // void main() + // { + // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; + // } + + const std::string defs_before = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint MissNV %main "main" +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +%void = OpTypeVoid +)"; + + const std::string defs_after = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint MissNV %main "main" %89 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 1 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %89 BuiltIn LaunchIdNV +%void = OpTypeVoid +)"; + + const std::string func_before = + R"(%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%20 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint_1 = OpConstant %uint 1 +%34 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_39 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%57 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_5317 = OpConstant %uint 5317 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%89 = OpVariable %_ptr_Input_v3uint Input +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_51 = OpConstant %uint 51 +%113 = OpConstantNull %v4float +%116 = OpTypeFunction %uint %uint %uint %uint %uint +%uint_48 = OpConstant %uint 48 +%141 = OpConstantNull %uint +%uint_54 = OpConstant %uint 54 +%main = OpFunction %void None %7 +%24 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%134 = OpULessThan %bool %uint_0 %133 +OpSelectionMerge %135 None +OpBranchConditional %134 %136 %137 +%136 = OpLabel +%138 = OpLoad %uint %25 +OpBranch %135 +%137 = OpLabel +%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 +OpBranch %135 +%135 = OpLabel +%142 = OpPhi %uint %138 %136 %141 %137 +%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +%28 = OpLoad %13 %27 +%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 +%50 = OpULessThan %bool %142 %48 +OpSelectionMerge %51 None +OpBranchConditional %50 %52 %53 +%52 = OpLabel +%54 = OpLoad %13 %27 +%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 +%144 = OpULessThan %bool %uint_0 %143 +OpSelectionMerge %145 None +OpBranchConditional %144 %146 %147 +%146 = OpLabel +%148 = OpLoad %13 %27 +%149 = OpImageRead %v4float %148 %20 +OpBranch %145 +%147 = OpLabel +%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 +OpBranch %145 +%145 = OpLabel +%151 = OpPhi %v4float %149 %146 %113 %147 +OpBranch %51 +%53 = OpLabel +%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 +OpBranch %51 +%51 = OpLabel +%114 = OpPhi %v4float %151 %145 %113 %53 +%30 = OpCompositeExtract %float %114 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%153 = OpULessThan %bool %uint_0 %152 +OpSelectionMerge %154 None +OpBranchConditional %153 %155 %156 +%155 = OpLabel +OpStore %31 %30 +OpBranch %154 +%156 = OpLabel +%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 +OpBranch %154 +%154 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%33 = OpFunction %uint None %34 +%35 = OpFunctionParameter %uint +%36 = OpFunctionParameter %uint +%37 = OpLabel +%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 +%44 = OpLoad %uint %43 +%45 = OpIAdd %uint %44 %36 +%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 +%47 = OpLoad %uint %46 +OpReturnValue %47 +OpFunctionEnd +%56 = OpFunction %void None %57 +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpFunctionParameter %uint +%62 = OpLabel +%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 +%70 = OpIAdd %uint %69 %uint_10 +%71 = OpArrayLength %uint %65 1 +%72 = OpULessThanEqual %bool %70 %71 +OpSelectionMerge %73 None +OpBranchConditional %72 %74 %73 +%74 = OpLabel +%75 = OpIAdd %uint %69 %uint_0 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 +OpStore %76 %uint_10 +%78 = OpIAdd %uint %69 %uint_1 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 +OpStore %79 %uint_23 +%81 = OpIAdd %uint %69 %uint_2 +%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 +OpStore %82 %58 +%85 = OpIAdd %uint %69 %uint_3 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 +OpStore %86 %uint_5317 +%90 = OpLoad %v3uint %89 +%91 = OpCompositeExtract %uint %90 0 +%92 = OpCompositeExtract %uint %90 1 +%93 = OpCompositeExtract %uint %90 2 +%94 = OpIAdd %uint %69 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %91 +%97 = OpIAdd %uint %69 %uint_5 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 +OpStore %98 %92 +%100 = OpIAdd %uint %69 %uint_6 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 +OpStore %101 %93 +%103 = OpIAdd %uint %69 %uint_7 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 +OpStore %104 %59 +%106 = OpIAdd %uint %69 %uint_8 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 +OpStore %107 %60 +%109 = OpIAdd %uint %69 %uint_9 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 +OpStore %110 %61 +OpBranch %73 +%73 = OpLabel +OpReturn +OpFunctionEnd +%115 = OpFunction %uint None %116 +%117 = OpFunctionParameter %uint +%118 = OpFunctionParameter %uint +%119 = OpFunctionParameter %uint +%120 = OpFunctionParameter %uint +%121 = OpLabel +%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 +%123 = OpLoad %uint %122 +%124 = OpIAdd %uint %123 %118 +%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 +%126 = OpLoad %uint %125 +%127 = OpIAdd %uint %126 %119 +%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 +%129 = OpLoad %uint %128 +%130 = OpIAdd %uint %129 %120 +%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 +%132 = OpLoad %uint %131 +OpReturnValue %132 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, + InstBoundsCallableInitLoadVariableSizedSampledImagesArray) { + // #version 460 + // #extension GL_EXT_nonuniform_qualifier : require + // #extension GL_NV_ray_tracing : require + // + // layout(set = 0, binding = 0, std140) buffer StorageBuffer { + // uint index; + // float red; + // } sbo; + // + // layout(set = 0, binding = 1, rgba32f) readonly uniform image2D images[]; + // + // void main() + // { + // sbo.red = imageLoad(images[sbo.index], ivec2(0, 0)).r; + // } + + const std::string defs_before = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint CallableNV %main "main" +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +%void = OpTypeVoid +)"; + + const std::string defs_after = + R"(OpCapability RuntimeDescriptorArray +OpCapability RayTracingNV +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_NV_ray_tracing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint CallableNV %main "main" %89 +OpSource GLSL 460 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpSourceExtension "GL_NV_ray_tracing" +OpName %main "main" +OpName %StorageBuffer "StorageBuffer" +OpMemberName %StorageBuffer 0 "index" +OpMemberName %StorageBuffer 1 "red" +OpName %sbo "sbo" +OpName %images "images" +OpMemberDecorate %StorageBuffer 0 Offset 0 +OpMemberDecorate %StorageBuffer 1 Offset 4 +OpDecorate %StorageBuffer BufferBlock +OpDecorate %sbo DescriptorSet 0 +OpDecorate %sbo Binding 0 +OpDecorate %images DescriptorSet 0 +OpDecorate %images Binding 1 +OpDecorate %images NonWritable +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 1 +OpDecorate %_struct_63 Block +OpMemberDecorate %_struct_63 0 Offset 0 +OpMemberDecorate %_struct_63 1 Offset 4 +OpDecorate %65 DescriptorSet 7 +OpDecorate %65 Binding 0 +OpDecorate %89 BuiltIn LaunchIdNV +%void = OpTypeVoid +)"; + + const std::string func_before = + R"(%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%25 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%main = OpFunction %void None %3 +%5 = OpLabel +%19 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%20 = OpLoad %uint %19 +%22 = OpAccessChain %_ptr_UniformConstant_13 %images %20 +%23 = OpLoad %13 %22 +%27 = OpImageRead %v4float %23 %25 +%29 = OpCompositeExtract %float %27 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +OpStore %31 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%7 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%StorageBuffer = OpTypeStruct %uint %float +%_ptr_Uniform_StorageBuffer = OpTypePointer Uniform %StorageBuffer +%sbo = OpVariable %_ptr_Uniform_StorageBuffer Uniform +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%13 = OpTypeImage %float 2D 0 0 0 2 Rgba32f +%_runtimearr_13 = OpTypeRuntimeArray %13 +%_ptr_UniformConstant__runtimearr_13 = OpTypePointer UniformConstant %_runtimearr_13 +%images = OpVariable %_ptr_UniformConstant__runtimearr_13 UniformConstant +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%v2int = OpTypeVector %int 2 +%20 = OpConstantComposite %v2int %int_0 %int_0 +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%uint_1 = OpConstant %uint 1 +%34 = OpTypeFunction %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_39 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%bool = OpTypeBool +%57 = OpTypeFunction %void %uint %uint %uint %uint +%_struct_63 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_63 = OpTypePointer StorageBuffer %_struct_63 +%65 = OpVariable %_ptr_StorageBuffer__struct_63 StorageBuffer +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_5318 = OpConstant %uint 5318 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%89 = OpVariable %_ptr_Input_v3uint Input +%uint_5 = OpConstant %uint 5 +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_51 = OpConstant %uint 51 +%113 = OpConstantNull %v4float +%116 = OpTypeFunction %uint %uint %uint %uint %uint +%uint_48 = OpConstant %uint 48 +%141 = OpConstantNull %uint +%uint_54 = OpConstant %uint 54 +%main = OpFunction %void None %7 +%24 = OpLabel +%25 = OpAccessChain %_ptr_Uniform_uint %sbo %int_0 +%133 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%134 = OpULessThan %bool %uint_0 %133 +OpSelectionMerge %135 None +OpBranchConditional %134 %136 %137 +%136 = OpLabel +%138 = OpLoad %uint %25 +OpBranch %135 +%137 = OpLabel +%140 = OpFunctionCall %void %56 %uint_48 %uint_1 %uint_0 %uint_0 +OpBranch %135 +%135 = OpLabel +%142 = OpPhi %uint %138 %136 %141 %137 +%27 = OpAccessChain %_ptr_UniformConstant_13 %images %142 +%28 = OpLoad %13 %27 +%48 = OpFunctionCall %uint %33 %uint_1 %uint_1 +%50 = OpULessThan %bool %142 %48 +OpSelectionMerge %51 None +OpBranchConditional %50 %52 %53 +%52 = OpLabel +%54 = OpLoad %13 %27 +%143 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_1 %142 +%144 = OpULessThan %bool %uint_0 %143 +OpSelectionMerge %145 None +OpBranchConditional %144 %146 %147 +%146 = OpLabel +%148 = OpLoad %13 %27 +%149 = OpImageRead %v4float %148 %20 +OpBranch %145 +%147 = OpLabel +%150 = OpFunctionCall %void %56 %uint_51 %uint_1 %142 %uint_0 +OpBranch %145 +%145 = OpLabel +%151 = OpPhi %v4float %149 %146 %113 %147 +OpBranch %51 +%53 = OpLabel +%112 = OpFunctionCall %void %56 %uint_51 %uint_0 %142 %48 +OpBranch %51 +%51 = OpLabel +%114 = OpPhi %v4float %151 %145 %113 %53 +%30 = OpCompositeExtract %float %114 0 +%31 = OpAccessChain %_ptr_Uniform_float %sbo %int_1 +%152 = OpFunctionCall %uint %115 %uint_0 %uint_0 %uint_0 %uint_0 +%153 = OpULessThan %bool %uint_0 %152 +OpSelectionMerge %154 None +OpBranchConditional %153 %155 %156 +%155 = OpLabel +OpStore %31 %30 +OpBranch %154 +%156 = OpLabel +%158 = OpFunctionCall %void %56 %uint_54 %uint_1 %uint_0 %uint_0 +OpBranch %154 +%154 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%33 = OpFunction %uint None %34 +%35 = OpFunctionParameter %uint +%36 = OpFunctionParameter %uint +%37 = OpLabel +%43 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %35 +%44 = OpLoad %uint %43 +%45 = OpIAdd %uint %44 %36 +%46 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %45 +%47 = OpLoad %uint %46 +OpReturnValue %47 +OpFunctionEnd +%56 = OpFunction %void None %57 +%58 = OpFunctionParameter %uint +%59 = OpFunctionParameter %uint +%60 = OpFunctionParameter %uint +%61 = OpFunctionParameter %uint +%62 = OpLabel +%66 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_0 +%69 = OpAtomicIAdd %uint %66 %uint_4 %uint_0 %uint_10 +%70 = OpIAdd %uint %69 %uint_10 +%71 = OpArrayLength %uint %65 1 +%72 = OpULessThanEqual %bool %70 %71 +OpSelectionMerge %73 None +OpBranchConditional %72 %74 %73 +%74 = OpLabel +%75 = OpIAdd %uint %69 %uint_0 +%76 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %75 +OpStore %76 %uint_10 +%78 = OpIAdd %uint %69 %uint_1 +%79 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %78 +OpStore %79 %uint_23 +%81 = OpIAdd %uint %69 %uint_2 +%82 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %81 +OpStore %82 %58 +%85 = OpIAdd %uint %69 %uint_3 +%86 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %85 +OpStore %86 %uint_5318 +%90 = OpLoad %v3uint %89 +%91 = OpCompositeExtract %uint %90 0 +%92 = OpCompositeExtract %uint %90 1 +%93 = OpCompositeExtract %uint %90 2 +%94 = OpIAdd %uint %69 %uint_4 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %94 +OpStore %95 %91 +%97 = OpIAdd %uint %69 %uint_5 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %97 +OpStore %98 %92 +%100 = OpIAdd %uint %69 %uint_6 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %100 +OpStore %101 %93 +%103 = OpIAdd %uint %69 %uint_7 +%104 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %103 +OpStore %104 %59 +%106 = OpIAdd %uint %69 %uint_8 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %106 +OpStore %107 %60 +%109 = OpIAdd %uint %69 %uint_9 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %65 %uint_1 %109 +OpStore %110 %61 +OpBranch %73 +%73 = OpLabel +OpReturn +OpFunctionEnd +%115 = OpFunction %uint None %116 +%117 = OpFunctionParameter %uint +%118 = OpFunctionParameter %uint +%119 = OpFunctionParameter %uint +%120 = OpFunctionParameter %uint +%121 = OpLabel +%122 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %117 +%123 = OpLoad %uint %122 +%124 = OpIAdd %uint %123 %118 +%125 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %124 +%126 = OpLoad %uint %125 +%127 = OpIAdd %uint %126 %119 +%128 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %127 +%129 = OpLoad %uint %128 +%130 = OpIAdd %uint %129 %120 +%131 = OpAccessChain %_ptr_StorageBuffer_uint %41 %uint_0 %130 +%132 = OpLoad %uint %131 +OpReturnValue %132 +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, InstBoundsInitSameBlockOpReplication) { + // Test that same block ops like OpSampledImage are replicated properly + // where needed. + // + // clang-format off + // + // #version 450 core + // #extension GL_EXT_nonuniform_qualifier : enable + // + // layout(location = 0) in vec2 inTexcoord; + // layout(location = 0) out vec4 outColor; + // + // layout(set = 0, binding = 0) uniform Uniforms { + // vec2 var0; + // } uniforms; + // + // layout(set = 0, binding = 1) uniform sampler uniformSampler; + // layout(set = 0, binding = 2) uniform texture2D uniformTex; + // layout(set = 0, binding = 3) uniform texture2D uniformTexArr[8]; + // + // void main() { + // int index = 0; + // float x = texture(sampler2D(uniformTexArr[nonuniformEXT(index)], uniformSampler), inTexcoord.xy).x; + // float y = texture(sampler2D(uniformTex, uniformSampler), inTexcoord.xy * uniforms.var0.xy).x; + // outColor = vec4(x, y, 0.0, 0.0); + // } + // + // clang-format on + + const std::string defs_before = + R"(OpCapability Shader +OpCapability ShaderNonUniformEXT +OpCapability SampledImageArrayNonUniformIndexingEXT +OpExtension "SPV_EXT_descriptor_indexing" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %inTexcoord %outColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %index "index" +OpName %x "x" +OpName %uniformTexArr "uniformTexArr" +OpName %uniformSampler "uniformSampler" +OpName %inTexcoord "inTexcoord" +OpName %y "y" +OpName %uniformTex "uniformTex" +OpName %Uniforms "Uniforms" +OpMemberName %Uniforms 0 "var0" +OpName %uniforms "uniforms" +OpName %outColor "outColor" +OpDecorate %uniformTexArr DescriptorSet 0 +OpDecorate %uniformTexArr Binding 3 +OpDecorate %19 NonUniformEXT +OpDecorate %22 NonUniformEXT +OpDecorate %uniformSampler DescriptorSet 0 +OpDecorate %uniformSampler Binding 1 +OpDecorate %inTexcoord Location 0 +OpDecorate %uniformTex DescriptorSet 0 +OpDecorate %uniformTex Binding 2 +OpMemberDecorate %Uniforms 0 Offset 0 +OpDecorate %Uniforms Block +OpDecorate %uniforms DescriptorSet 0 +OpDecorate %uniforms Binding 0 +OpDecorate %outColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%13 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_13_uint_8 = OpTypeArray %13 %uint_8 +%_ptr_UniformConstant__arr_13_uint_8 = OpTypePointer UniformConstant %_arr_13_uint_8 +%uniformTexArr = OpVariable %_ptr_UniformConstant__arr_13_uint_8 UniformConstant +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%23 = OpTypeSampler +%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 +%uniformSampler = OpVariable %_ptr_UniformConstant_23 UniformConstant +%27 = OpTypeSampledImage %13 +%v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%inTexcoord = OpVariable %_ptr_Input_v2float Input +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uniformTex = OpVariable %_ptr_UniformConstant_13 UniformConstant +%Uniforms = OpTypeStruct %v2float +%_ptr_Uniform_Uniforms = OpTypePointer Uniform %Uniforms +%uniforms = OpVariable %_ptr_Uniform_Uniforms Uniform +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability ShaderNonUniform +OpCapability SampledImageArrayNonUniformIndexing +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %inTexcoord %outColor %gl_FragCoord +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_EXT_nonuniform_qualifier" +OpName %main "main" +OpName %index "index" +OpName %x "x" +OpName %uniformTexArr "uniformTexArr" +OpName %uniformSampler "uniformSampler" +OpName %inTexcoord "inTexcoord" +OpName %y "y" +OpName %uniformTex "uniformTex" +OpName %Uniforms "Uniforms" +OpMemberName %Uniforms 0 "var0" +OpName %uniforms "uniforms" +OpName %outColor "outColor" +OpDecorate %uniformTexArr DescriptorSet 0 +OpDecorate %uniformTexArr Binding 3 +OpDecorate %19 NonUniform +OpDecorate %22 NonUniform +OpDecorate %uniformSampler DescriptorSet 0 +OpDecorate %uniformSampler Binding 1 +OpDecorate %inTexcoord Location 0 +OpDecorate %uniformTex DescriptorSet 0 +OpDecorate %uniformTex Binding 2 +OpMemberDecorate %Uniforms 0 Offset 0 +OpDecorate %Uniforms Block +OpDecorate %uniforms DescriptorSet 0 +OpDecorate %uniforms Binding 0 +OpDecorate %outColor Location 0 +OpDecorate %63 NonUniform +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_75 Block +OpMemberDecorate %_struct_75 0 Offset 0 +OpMemberDecorate %_struct_75 1 Offset 4 +OpDecorate %77 DescriptorSet 7 +OpDecorate %77 Binding 0 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %_struct_132 Block +OpMemberDecorate %_struct_132 0 Offset 0 +OpDecorate %134 DescriptorSet 7 +OpDecorate %134 Binding 1 +OpDecorate %151 NonUniform +%void = OpTypeVoid +%3 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%13 = OpTypeImage %float 2D 0 0 0 1 Unknown +%uint = OpTypeInt 32 0 +%uint_8 = OpConstant %uint 8 +%_arr_13_uint_8 = OpTypeArray %13 %uint_8 +%_ptr_UniformConstant__arr_13_uint_8 = OpTypePointer UniformConstant %_arr_13_uint_8 +%uniformTexArr = OpVariable %_ptr_UniformConstant__arr_13_uint_8 UniformConstant +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%23 = OpTypeSampler +%_ptr_UniformConstant_23 = OpTypePointer UniformConstant %23 +%uniformSampler = OpVariable %_ptr_UniformConstant_23 UniformConstant +%27 = OpTypeSampledImage %13 +%v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%inTexcoord = OpVariable %_ptr_Input_v2float Input +%v4float = OpTypeVector %float 4 +%uint_0 = OpConstant %uint 0 +%uniformTex = OpVariable %_ptr_UniformConstant_13 UniformConstant +%Uniforms = OpTypeStruct %v2float +%_ptr_Uniform_Uniforms = OpTypePointer Uniform %Uniforms +%uniforms = OpVariable %_ptr_Uniform_Uniforms Uniform +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%68 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_75 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_75 = OpTypePointer StorageBuffer %_struct_75 +%77 = OpVariable %_ptr_StorageBuffer__struct_75 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_1 = OpConstant %uint 1 +%uint_23 = OpConstant %uint 23 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%v4uint = OpTypeVector %uint 4 +%uint_5 = OpConstant %uint 5 +%uint_7 = OpConstant %uint 7 +%uint_9 = OpConstant %uint 9 +%uint_79 = OpConstant %uint 79 +%122 = OpConstantNull %v4float +%126 = OpTypeFunction %uint %uint %uint %uint %uint +%_struct_132 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_132 = OpTypePointer StorageBuffer %_struct_132 +%134 = OpVariable %_ptr_StorageBuffer__struct_132 StorageBuffer +%uint_87 = OpConstant %uint 87 +%165 = OpConstantNull %v2float +%uint_89 = OpConstant %uint 89 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%index = OpVariable %_ptr_Function_int Function +%x = OpVariable %_ptr_Function_float Function +%y = OpVariable %_ptr_Function_float Function +OpStore %index %int_0 +%19 = OpLoad %int %index +%21 = OpAccessChain %_ptr_UniformConstant_13 %uniformTexArr %19 +%22 = OpLoad %13 %21 +%26 = OpLoad %23 %uniformSampler +%28 = OpSampledImage %27 %22 %26 +%32 = OpLoad %v2float %inTexcoord +%34 = OpImageSampleImplicitLod %v4float %28 %32 +%36 = OpCompositeExtract %float %34 0 +OpStore %x %36 +%39 = OpLoad %13 %uniformTex +%40 = OpLoad %23 %uniformSampler +%41 = OpSampledImage %27 %39 %40 +%42 = OpLoad %v2float %inTexcoord +%47 = OpAccessChain %_ptr_Uniform_v2float %uniforms %int_0 +%48 = OpLoad %v2float %47 +%49 = OpFMul %v2float %42 %48 +%50 = OpImageSampleImplicitLod %v4float %41 %49 +%51 = OpCompositeExtract %float %50 0 +OpStore %y %51 +%54 = OpLoad %float %x +%55 = OpLoad %float %y +%57 = OpCompositeConstruct %v4float %54 %55 %float_0 %float_0 +OpStore %outColor %57 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%index = OpVariable %_ptr_Function_int Function +%x = OpVariable %_ptr_Function_float Function +%y = OpVariable %_ptr_Function_float Function +OpStore %index %int_0 +%19 = OpLoad %int %index +%21 = OpAccessChain %_ptr_UniformConstant_13 %uniformTexArr %19 +%22 = OpLoad %13 %21 +%26 = OpLoad %23 %uniformSampler +%28 = OpSampledImage %27 %22 %26 +%32 = OpLoad %v2float %inTexcoord +%59 = OpULessThan %bool %19 %uint_8 +OpSelectionMerge %60 None +OpBranchConditional %59 %61 %62 +%61 = OpLabel +%63 = OpLoad %13 %21 +%64 = OpSampledImage %27 %63 %26 +%124 = OpBitcast %uint %19 +%146 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_3 %124 +%147 = OpULessThan %bool %uint_0 %146 +OpSelectionMerge %148 None +OpBranchConditional %147 %149 %150 +%149 = OpLabel +%151 = OpLoad %13 %21 +%152 = OpSampledImage %27 %151 %26 +%153 = OpImageSampleImplicitLod %v4float %152 %32 +OpBranch %148 +%150 = OpLabel +%154 = OpBitcast %uint %19 +%155 = OpFunctionCall %void %67 %uint_79 %uint_1 %154 %uint_0 +OpBranch %148 +%148 = OpLabel +%156 = OpPhi %v4float %153 %149 %122 %150 +OpBranch %60 +%62 = OpLabel +%66 = OpBitcast %uint %19 +%121 = OpFunctionCall %void %67 %uint_79 %uint_0 %66 %uint_8 +OpBranch %60 +%60 = OpLabel +%123 = OpPhi %v4float %156 %148 %122 %62 +%36 = OpCompositeExtract %float %123 0 +OpStore %x %36 +%39 = OpLoad %13 %uniformTex +%40 = OpLoad %23 %uniformSampler +%41 = OpSampledImage %27 %39 %40 +%42 = OpLoad %v2float %inTexcoord +%47 = OpAccessChain %_ptr_Uniform_v2float %uniforms %int_0 +%157 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_0 %uint_0 +%158 = OpULessThan %bool %uint_0 %157 +OpSelectionMerge %159 None +OpBranchConditional %158 %160 %161 +%160 = OpLabel +%162 = OpLoad %v2float %47 +OpBranch %159 +%161 = OpLabel +%164 = OpFunctionCall %void %67 %uint_87 %uint_1 %uint_0 %uint_0 +OpBranch %159 +%159 = OpLabel +%166 = OpPhi %v2float %162 %160 %165 %161 +%49 = OpFMul %v2float %42 %166 +%167 = OpSampledImage %27 %39 %40 +%168 = OpFunctionCall %uint %125 %uint_0 %uint_0 %uint_2 %uint_0 +%169 = OpULessThan %bool %uint_0 %168 +OpSelectionMerge %170 None +OpBranchConditional %169 %171 %172 +%171 = OpLabel +%173 = OpLoad %13 %uniformTex +%174 = OpSampledImage %27 %173 %40 +%175 = OpImageSampleImplicitLod %v4float %174 %49 +OpBranch %170 +%172 = OpLabel +%177 = OpFunctionCall %void %67 %uint_89 %uint_1 %uint_0 %uint_0 +OpBranch %170 +%170 = OpLabel +%178 = OpPhi %v4float %175 %171 %122 %172 +%51 = OpCompositeExtract %float %178 0 +OpStore %y %51 +%54 = OpLoad %float %x +%55 = OpLoad %float %y +%57 = OpCompositeConstruct %v4float %54 %55 %float_0 %float_0 +OpStore %outColor %57 +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%67 = OpFunction %void None %68 +%69 = OpFunctionParameter %uint +%70 = OpFunctionParameter %uint +%71 = OpFunctionParameter %uint +%72 = OpFunctionParameter %uint +%73 = OpLabel +%79 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_0 +%82 = OpAtomicIAdd %uint %79 %uint_4 %uint_0 %uint_10 +%83 = OpIAdd %uint %82 %uint_10 +%84 = OpArrayLength %uint %77 1 +%85 = OpULessThanEqual %bool %83 %84 +OpSelectionMerge %86 None +OpBranchConditional %85 %87 %86 +%87 = OpLabel +%88 = OpIAdd %uint %82 %uint_0 +%90 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %88 +OpStore %90 %uint_10 +%92 = OpIAdd %uint %82 %uint_1 +%93 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %92 +OpStore %93 %uint_23 +%95 = OpIAdd %uint %82 %uint_2 +%96 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %95 +OpStore %96 %69 +%98 = OpIAdd %uint %82 %uint_3 +%99 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %98 +OpStore %99 %uint_4 +%102 = OpLoad %v4float %gl_FragCoord +%104 = OpBitcast %v4uint %102 +%105 = OpCompositeExtract %uint %104 0 +%106 = OpIAdd %uint %82 %uint_4 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %106 +OpStore %107 %105 +%108 = OpCompositeExtract %uint %104 1 +%110 = OpIAdd %uint %82 %uint_5 +%111 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %110 +OpStore %111 %108 +%113 = OpIAdd %uint %82 %uint_7 +%114 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %113 +OpStore %114 %70 +%115 = OpIAdd %uint %82 %uint_8 +%116 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %115 +OpStore %116 %71 +%118 = OpIAdd %uint %82 %uint_9 +%119 = OpAccessChain %_ptr_StorageBuffer_uint %77 %uint_1 %118 +OpStore %119 %72 +OpBranch %86 +%86 = OpLabel +OpReturn +OpFunctionEnd +%125 = OpFunction %uint None %126 +%127 = OpFunctionParameter %uint +%128 = OpFunctionParameter %uint +%129 = OpFunctionParameter %uint +%130 = OpFunctionParameter %uint +%131 = OpLabel +%135 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %127 +%136 = OpLoad %uint %135 +%137 = OpIAdd %uint %136 %128 +%138 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %137 +%139 = OpLoad %uint %138 +%140 = OpIAdd %uint %139 %129 +%141 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %140 +%142 = OpLoad %uint %141 +%143 = OpIAdd %uint %142 %130 +%144 = OpAccessChain %_ptr_StorageBuffer_uint %134 %uint_0 %143 +%145 = OpLoad %uint %144 +OpReturnValue %145 +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u, true, true); +} + +TEST_F(InstBindlessTest, MultipleUniformNonAggregateRefsNoDescInit) { + // Check that uniform refs do not go out-of-bounds. All checks use same input + // buffer read function call result at top of function for uniform buffer + // length. Because descriptor indexing is not being checked, we can avoid one + // buffer load. + // + // Texture2D g_tColor; + // SamplerState g_sAniso; + // + // layout(push_constant) cbuffer PerViewPushConst_t { bool g_B; }; + // + // cbuffer PerViewConstantBuffer_t { + // float2 g_TexOff0; + // float2 g_TexOff1; + // }; + // + // struct PS_INPUT { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) { + // PS_OUTPUT ps_output; + // float2 off; + // float2 vtc; + // if (g_B) + // off = g_TexOff0; + // else + // off = g_TexOff1; + // vtc = i.vTextureCoords.xy + off; + // ps_output.vColor = g_tColor.Sample(g_sAniso, vtc); + // return ps_output; + // } + + const std::string text = R"( + OpCapability Shader +;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %130 %157 %gl_FragCoord + OpExecutionMode %MainPs OriginUpperLeft + OpSource HLSL 500 + OpName %MainPs "MainPs" + OpName %PerViewPushConst_t "PerViewPushConst_t" + OpMemberName %PerViewPushConst_t 0 "g_B" + OpName %_ "" + OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" + OpMemberName %PerViewConstantBuffer_t 0 "g_TexOff0" + OpMemberName %PerViewConstantBuffer_t 1 "g_TexOff1" + OpName %__0 "" + OpName %g_tColor "g_tColor" + OpName %g_sAniso "g_sAniso" + OpName %i_vTextureCoords "i.vTextureCoords" + OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" + OpMemberDecorate %PerViewPushConst_t 0 Offset 0 + OpDecorate %PerViewPushConst_t Block + OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 + OpMemberDecorate %PerViewConstantBuffer_t 1 Offset 8 + OpDecorate %PerViewConstantBuffer_t Block + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 1 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 2 + OpDecorate %i_vTextureCoords Location 0 + OpDecorate %_entryPointOutput_vColor Location 0 + ;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 + ;CHECK: OpDecorate %_struct_128 Block + ;CHECK: OpMemberDecorate %_struct_128 0 Offset 0 + ;CHECK: OpDecorate %130 DescriptorSet 7 + ;CHECK: OpDecorate %130 Binding 1 + ;CHECK: OpDecorate %_struct_155 Block + ;CHECK: OpMemberDecorate %_struct_155 0 Offset 0 + ;CHECK: OpMemberDecorate %_struct_155 1 Offset 4 + ;CHECK: OpDecorate %157 DescriptorSet 7 + ;CHECK: OpDecorate %157 Binding 0 + ;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 +%PerViewPushConst_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t + %_ = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint + %bool = OpTypeBool + %uint_0 = OpConstant %uint 0 +%PerViewConstantBuffer_t = OpTypeStruct %v2float %v2float +%_ptr_Uniform_PerViewConstantBuffer_t = OpTypePointer Uniform %PerViewConstantBuffer_t + %__0 = OpVariable %_ptr_Uniform_PerViewConstantBuffer_t Uniform +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %int_1 = OpConstant %int 1 + %49 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_49 = OpTypePointer UniformConstant %49 + %g_tColor = OpVariable %_ptr_UniformConstant_49 UniformConstant + %53 = OpTypeSampler +%_ptr_UniformConstant_53 = OpTypePointer UniformConstant %53 + %g_sAniso = OpVariable %_ptr_UniformConstant_53 UniformConstant + %57 = OpTypeSampledImage %49 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output + ;CHECK: %uint_7 = OpConstant %uint 7 + ;CHECK: %uint_1 = OpConstant %uint 1 + ;CHECK: %122 = OpTypeFunction %uint %uint %uint %uint + ;CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint + ;CHECK: %_struct_128 = OpTypeStruct %_runtimearr_uint + ;CHECK: %_ptr_StorageBuffer__struct_128 = OpTypePointer StorageBuffer %_struct_128 + ;CHECK: %130 = OpVariable %_ptr_StorageBuffer__struct_128 StorageBuffer + ;CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + ;CHECK: %uint_3 = OpConstant %uint 3 + ;CHECK: %148 = OpTypeFunction %void %uint %uint %uint %uint %uint + ;CHECK: %_struct_155 = OpTypeStruct %uint %_runtimearr_uint + ;CHECK: %_ptr_StorageBuffer__struct_155 = OpTypePointer StorageBuffer %_struct_155 + ;CHECK: %157 = OpVariable %_ptr_StorageBuffer__struct_155 StorageBuffer + ;CHECK: %uint_11 = OpConstant %uint 11 + ;CHECK: %uint_4 = OpConstant %uint 4 + ;CHECK: %uint_23 = OpConstant %uint 23 + ;CHECK: %uint_2 = OpConstant %uint 2 + ;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float + ;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + ;CHECK: %v4uint = OpTypeVector %uint 4 + ;CHECK: %uint_5 = OpConstant %uint 5 + ;CHECK: %uint_8 = OpConstant %uint 8 + ;CHECK: %uint_9 = OpConstant %uint 9 + ;CHECK: %uint_10 = OpConstant %uint 10 + ;CHECK: %uint_71 = OpConstant %uint 71 + ;CHECK: %202 = OpConstantNull %v2float + ;CHECK: %uint_75 = OpConstant %uint 75 + %MainPs = OpFunction %void None %3 + %5 = OpLabel + ;CHECK: %140 = OpFunctionCall %uint %121 %uint_1 %uint_1 %uint_0 + ;CHECK: OpBranch %117 + ;CHECK: %117 = OpLabel + ;CHECK: OpBranch %116 + ;CHECK: %116 = OpLabel + %69 = OpLoad %v2float %i_vTextureCoords + %82 = OpAccessChain %_ptr_PushConstant_uint %_ %int_0 + %83 = OpLoad %uint %82 + %84 = OpINotEqual %bool %83 %uint_0 + OpSelectionMerge %91 None + OpBranchConditional %84 %85 %88 + %85 = OpLabel + %86 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_0 + %87 = OpLoad %v2float %86 + ;CHECK-NOT: %87 = OpLoad %v2float %86 + ;CHECK: %119 = OpIAdd %uint %uint_0 %uint_7 + ;CHECK: %141 = OpULessThan %bool %119 %140 + ;CHECK: OpSelectionMerge %143 None + ;CHECK: OpBranchConditional %141 %144 %145 + ;CHECK: %144 = OpLabel + ;CHECK: %146 = OpLoad %v2float %86 + ;CHECK: OpBranch %143 + ;CHECK: %145 = OpLabel + ;CHECK: %201 = OpFunctionCall %void %147 %uint_71 %uint_3 %uint_0 %119 %140 + ;CHECK: OpBranch %143 + ;CHECK: %143 = OpLabel + ;CHECK: %203 = OpPhi %v2float %146 %144 %202 %145 + OpBranch %91 + %88 = OpLabel + %89 = OpAccessChain %_ptr_Uniform_v2float %__0 %int_1 + %90 = OpLoad %v2float %89 + ;CHECK-NOT: %90 = OpLoad %v2float %89 + ;CHECK: %204 = OpIAdd %uint %uint_8 %uint_7 + ;CHECK: %205 = OpULessThan %bool %204 %140 + ;CHECK: OpSelectionMerge %206 None + ;CHECK: OpBranchConditional %205 %207 %208 + ;CHECK: %207 = OpLabel + ;CHECK: %209 = OpLoad %v2float %89 + ;CHECK: OpBranch %206 + ;CHECK: %208 = OpLabel + ;CHECK: %211 = OpFunctionCall %void %147 %uint_75 %uint_3 %uint_0 %204 %140 + ;CHECK: OpBranch %206 + ;CHECK: %206 = OpLabel + ;CHECK: %212 = OpPhi %v2float %209 %207 %202 %208 + OpBranch %91 + %91 = OpLabel + %115 = OpPhi %v2float %87 %85 %90 %88 + ;CHECK-NOT: %115 = OpPhi %v2float %87 %85 %90 %88 + ;CHECK: %115 = OpPhi %v2float %203 %143 %212 %206 + %95 = OpFAdd %v2float %69 %115 + %96 = OpLoad %49 %g_tColor + %97 = OpLoad %53 %g_sAniso + %98 = OpSampledImage %57 %96 %97 + %100 = OpImageSampleImplicitLod %v4float %98 %95 + OpStore %_entryPointOutput_vColor %100 + OpReturn + OpFunctionEnd + ;CHECK: %121 = OpFunction %uint None %122 + ;CHECK: %123 = OpFunctionParameter %uint + ;CHECK: %124 = OpFunctionParameter %uint + ;CHECK: %125 = OpFunctionParameter %uint + ;CHECK: %126 = OpLabel + ;CHECK: %132 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %123 + ;CHECK: %133 = OpLoad %uint %132 + ;CHECK: %134 = OpIAdd %uint %133 %124 + ;CHECK: %135 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %134 + ;CHECK: %136 = OpLoad %uint %135 + ;CHECK: %137 = OpIAdd %uint %136 %125 + ;CHECK: %138 = OpAccessChain %_ptr_StorageBuffer_uint %130 %uint_0 %137 + ;CHECK: %139 = OpLoad %uint %138 + ;CHECK: OpReturnValue %139 + ;CHECK: OpFunctionEnd + ;CHECK: %147 = OpFunction %void None %148 + ;CHECK: %149 = OpFunctionParameter %uint + ;CHECK: %150 = OpFunctionParameter %uint + ;CHECK: %151 = OpFunctionParameter %uint + ;CHECK: %152 = OpFunctionParameter %uint + ;CHECK: %153 = OpFunctionParameter %uint + ;CHECK: %154 = OpLabel + ;CHECK: %158 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_0 + ;CHECK: %161 = OpAtomicIAdd %uint %158 %uint_4 %uint_0 %uint_11 + ;CHECK: %162 = OpIAdd %uint %161 %uint_11 + ;CHECK: %163 = OpArrayLength %uint %157 1 + ;CHECK: %164 = OpULessThanEqual %bool %162 %163 + ;CHECK: OpSelectionMerge %165 None + ;CHECK: OpBranchConditional %164 %166 %165 + ;CHECK: %166 = OpLabel + ;CHECK: %167 = OpIAdd %uint %161 %uint_0 + ;CHECK: %168 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %167 + ;CHECK: OpStore %168 %uint_11 + ;CHECK: %170 = OpIAdd %uint %161 %uint_1 + ;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %170 + ;CHECK: OpStore %171 %uint_23 + ;CHECK: %173 = OpIAdd %uint %161 %uint_2 + ;CHECK: %174 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %173 + ;CHECK: OpStore %174 %149 + ;CHECK: %175 = OpIAdd %uint %161 %uint_3 + ;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %175 + ;CHECK: OpStore %176 %uint_4 + ;CHECK: %179 = OpLoad %v4float %gl_FragCoord + ;CHECK: %181 = OpBitcast %v4uint %179 + ;CHECK: %182 = OpCompositeExtract %uint %181 0 + ;CHECK: %183 = OpIAdd %uint %161 %uint_4 + ;CHECK: %184 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %183 + ;CHECK: OpStore %184 %182 + ;CHECK: %185 = OpCompositeExtract %uint %181 1 + ;CHECK: %187 = OpIAdd %uint %161 %uint_5 + ;CHECK: %188 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %187 + ;CHECK: OpStore %188 %185 + ;CHECK: %189 = OpIAdd %uint %161 %uint_7 + ;CHECK: %190 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %189 + ;CHECK: OpStore %190 %150 + ;CHECK: %192 = OpIAdd %uint %161 %uint_8 + ;CHECK: %193 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %192 + ;CHECK: OpStore %193 %151 + ;CHECK: %195 = OpIAdd %uint %161 %uint_9 + ;CHECK: %196 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %195 + ;CHECK: OpStore %196 %152 + ;CHECK: %198 = OpIAdd %uint %161 %uint_10 + ;CHECK: %199 = OpAccessChain %_ptr_StorageBuffer_uint %157 %uint_1 %198 + ;CHECK: OpStore %199 %153 + ;CHECK: OpBranch %165 + ;CHECK: %165 = OpLabel + ;CHECK: OpReturn + ;CHECK: OpFunctionEnd + )"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true, 7u, 23u, false, + false, true); +} + +TEST_F(InstBindlessTest, UniformArrayRefNoDescInit) { + // Check that uniform array ref does not go out-of-bounds. + // + // Texture2D g_tColor; + // SamplerState g_sAniso; + // + // layout(push_constant) cbuffer PerViewPushConst_t { uint g_c; }; + // + // struct PerBatchEnvMapConstantBuffer_t { + // float4x3 g_matEnvMapWorldToLocal; + // float4 g_vEnvironmentMapBoxMins; + // float2 g_TexOff; + // }; + // + // cbuffer _BindlessFastEnvMapCB_PS_t { + // PerBatchEnvMapConstantBuffer_t g_envMapConstants[128]; + // }; + // + // struct PS_INPUT { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) { + // PS_OUTPUT ps_output; + // float2 off; + // float2 vtc; + // off = g_envMapConstants[g_c].g_TexOff; + // vtc = i.vTextureCoords.xy + off; + // ps_output.vColor = g_tColor.Sample(g_sAniso, vtc); + // return ps_output; + // } + + const std::string text = R"( + OpCapability Shader +;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor + OpExecutionMode %MainPs OriginUpperLeft + OpSource HLSL 500 + OpName %MainPs "MainPs" + OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" + OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" + OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" + OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" + OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" + OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" + OpName %_ "" + OpName %PerViewPushConst_t "PerViewPushConst_t" + OpMemberName %PerViewPushConst_t 0 "g_c" + OpName %__0 "" + OpName %g_tColor "g_tColor" + OpName %g_sAniso "g_sAniso" + OpName %i_vTextureCoords "i.vTextureCoords" + OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 + OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 + OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 + OpDecorate %_BindlessFastEnvMapCB_PS_t Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 2 + OpMemberDecorate %PerViewPushConst_t 0 Offset 0 + OpDecorate %PerViewPushConst_t Block + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + OpDecorate %i_vTextureCoords Location 0 + OpDecorate %_entryPointOutput_vColor Location 0 +;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +;CHECK: OpDecorate %_struct_111 Block +;CHECK: OpMemberDecorate %_struct_111 0 Offset 0 +;CHECK: OpDecorate %113 DescriptorSet 7 +;CHECK: OpDecorate %113 Binding 1 +;CHECK: OpDecorate %_struct_139 Block +;CHECK: OpMemberDecorate %_struct_139 0 Offset 0 +;CHECK: OpMemberDecorate %_struct_139 1 Offset 4 +;CHECK: OpDecorate %141 DescriptorSet 7 +;CHECK: OpDecorate %141 Binding 0 +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 +%mat4v3float = OpTypeMatrix %v3float 4 +%PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float + %uint = OpTypeInt 32 0 + %uint_128 = OpConstant %uint 128 +%_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128 +%_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 +%_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t + %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%PerViewPushConst_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t + %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint + %int_2 = OpConstant %int 2 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %46 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampler +%_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50 + %g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant + %54 = OpTypeSampledImage %46 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +;CHECK: %uint_0 = OpConstant %uint 0 +;CHECK: %uint_80 = OpConstant %uint 80 +;CHECK: %uint_64 = OpConstant %uint 64 +;CHECK: %uint_7 = OpConstant %uint 7 +;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_1 = OpConstant %uint 1 +;CHECK: %105 = OpTypeFunction %uint %uint %uint %uint +;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint +;CHECK:%_struct_111 = OpTypeStruct %_runtimearr_uint +;CHECK:%_ptr_StorageBuffer__struct_111 = OpTypePointer StorageBuffer %_struct_111 +;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer +;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +;CHECK: %bool = OpTypeBool +;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %132 = OpTypeFunction %void %uint %uint %uint %uint %uint +;CHECK:%_struct_139 = OpTypeStruct %uint %_runtimearr_uint +;CHECK:%_ptr_StorageBuffer__struct_139 = OpTypePointer StorageBuffer %_struct_139 +;CHECK: %141 = OpVariable %_ptr_StorageBuffer__struct_139 StorageBuffer +;CHECK: %uint_11 = OpConstant %uint 11 +;CHECK: %uint_4 = OpConstant %uint 4 +;CHECK: %uint_23 = OpConstant %uint 23 +;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %uint_5 = OpConstant %uint 5 +;CHECK: %uint_8 = OpConstant %uint 8 +;CHECK: %uint_9 = OpConstant %uint 9 +;CHECK: %uint_10 = OpConstant %uint 10 +;CHECK: %uint_78 = OpConstant %uint 78 +;CHECK: %185 = OpConstantNull %v2float + %MainPs = OpFunction %void None %3 + %5 = OpLabel +;CHECK: %123 = OpFunctionCall %uint %104 %uint_1 %uint_2 %uint_0 +;CHECK: OpBranch %93 +;CHECK: %93 = OpLabel +;CHECK: OpBranch %92 +;CHECK: %92 = OpLabel + %66 = OpLoad %v2float %i_vTextureCoords + %79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0 + %80 = OpLoad %uint %79 + %81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2 + %82 = OpLoad %v2float %81 +;CHECK-NOT: %82 = OpLoad %v2float %81 +;CHECK: %96 = OpIMul %uint %uint_80 %80 +;CHECK: %97 = OpIAdd %uint %uint_0 %96 +;CHECK: %99 = OpIAdd %uint %97 %uint_64 +;CHECK: %101 = OpIAdd %uint %99 %uint_7 +;CHECK: %125 = OpULessThan %bool %101 %123 +;CHECK: OpSelectionMerge %127 None +;CHECK: OpBranchConditional %125 %128 %129 +;CHECK: %128 = OpLabel +;CHECK: %130 = OpLoad %v2float %81 +;CHECK: OpBranch %127 +;CHECK: %129 = OpLabel +;CHECK: %184 = OpFunctionCall %void %131 %uint_78 %uint_3 %uint_0 %101 %123 +;CHECK: OpBranch %127 +;CHECK: %127 = OpLabel +;CHECK: %186 = OpPhi %v2float %130 %128 %185 %129 + %86 = OpFAdd %v2float %66 %82 +;CHECK-NOT: %86 = OpFAdd %v2float %66 %82 +;CHECK: %86 = OpFAdd %v2float %66 %186 + %87 = OpLoad %46 %g_tColor + %88 = OpLoad %50 %g_sAniso + %89 = OpSampledImage %54 %87 %88 + %91 = OpImageSampleImplicitLod %v4float %89 %86 + OpStore %_entryPointOutput_vColor %91 + OpReturn + OpFunctionEnd +;CHECK: %104 = OpFunction %uint None %105 +;CHECK: %106 = OpFunctionParameter %uint +;CHECK: %107 = OpFunctionParameter %uint +;CHECK: %108 = OpFunctionParameter %uint +;CHECK: %109 = OpLabel +;CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %106 +;CHECK: %116 = OpLoad %uint %115 +;CHECK: %117 = OpIAdd %uint %116 %107 +;CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %117 +;CHECK: %119 = OpLoad %uint %118 +;CHECK: %120 = OpIAdd %uint %119 %108 +;CHECK: %121 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %120 +;CHECK: %122 = OpLoad %uint %121 +;CHECK: OpReturnValue %122 +;CHECK: OpFunctionEnd +;CHECK: %131 = OpFunction %void None %132 +;CHECK: %133 = OpFunctionParameter %uint +;CHECK: %134 = OpFunctionParameter %uint +;CHECK: %135 = OpFunctionParameter %uint +;CHECK: %136 = OpFunctionParameter %uint +;CHECK: %137 = OpFunctionParameter %uint +;CHECK: %138 = OpLabel +;CHECK: %142 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_0 +;CHECK: %145 = OpAtomicIAdd %uint %142 %uint_4 %uint_0 %uint_11 +;CHECK: %146 = OpIAdd %uint %145 %uint_11 +;CHECK: %147 = OpArrayLength %uint %141 1 +;CHECK: %148 = OpULessThanEqual %bool %146 %147 +;CHECK: OpSelectionMerge %149 None +;CHECK: OpBranchConditional %148 %150 %149 +;CHECK: %150 = OpLabel +;CHECK: %151 = OpIAdd %uint %145 %uint_0 +;CHECK: %152 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %151 +;CHECK: OpStore %152 %uint_11 +;CHECK: %154 = OpIAdd %uint %145 %uint_1 +;CHECK: %155 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %154 +;CHECK: OpStore %155 %uint_23 +;CHECK: %156 = OpIAdd %uint %145 %uint_2 +;CHECK: %157 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %156 +;CHECK: OpStore %157 %133 +;CHECK: %158 = OpIAdd %uint %145 %uint_3 +;CHECK: %159 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %158 +;CHECK: OpStore %159 %uint_4 +;CHECK: %162 = OpLoad %v4float %gl_FragCoord +;CHECK: %164 = OpBitcast %v4uint %162 +;CHECK: %165 = OpCompositeExtract %uint %164 0 +;CHECK: %166 = OpIAdd %uint %145 %uint_4 +;CHECK: %167 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %166 +;CHECK: OpStore %167 %165 +;CHECK: %168 = OpCompositeExtract %uint %164 1 +;CHECK: %170 = OpIAdd %uint %145 %uint_5 +;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %170 +;CHECK: OpStore %171 %168 +;CHECK: %172 = OpIAdd %uint %145 %uint_7 +;CHECK: %173 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %172 +;CHECK: OpStore %173 %134 +;CHECK: %175 = OpIAdd %uint %145 %uint_8 +;CHECK: %176 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %175 +;CHECK: OpStore %176 %135 +;CHECK: %178 = OpIAdd %uint %145 %uint_9 +;CHECK: %179 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %178 +;CHECK: OpStore %179 %136 +;CHECK: %181 = OpIAdd %uint %145 %uint_10 +;CHECK: %182 = OpAccessChain %_ptr_StorageBuffer_uint %141 %uint_1 %181 +;CHECK: OpStore %182 %137 +;CHECK: OpBranch %149 +;CHECK: %149 = OpLabel +;CHECK: OpReturn +;CHECK: OpFunctionEnd + )"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true, 7u, 23u, false, + false, true); +} + +TEST_F(InstBindlessTest, UniformArrayRefWithDescInit) { + // The buffer-oob and desc-init checks should use the same debug + // output buffer write function. + // + // Same source as UniformArrayRefNoDescInit + + const std::string text = R"( + OpCapability Shader +;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %113 %144 %gl_FragCoord + OpExecutionMode %MainPs OriginUpperLeft + OpSource HLSL 500 + OpName %MainPs "MainPs" + OpName %PerBatchEnvMapConstantBuffer_t +"PerBatchEnvMapConstantBuffer_t" OpMemberName %PerBatchEnvMapConstantBuffer_t 0 +"g_matEnvMapWorldToLocal" OpMemberName %PerBatchEnvMapConstantBuffer_t 1 +"g_vEnvironmentMapBoxMins" OpMemberName %PerBatchEnvMapConstantBuffer_t 2 +"g_TexOff" OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" + OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" + OpName %_ "" + OpName %PerViewPushConst_t "PerViewPushConst_t" + OpMemberName %PerViewPushConst_t 0 "g_c" + OpName %__0 "" + OpName %g_tColor "g_tColor" + OpName %g_sAniso "g_sAniso" + OpName %i_vTextureCoords "i.vTextureCoords" + OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 + OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 + OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 + OpDecorate %_BindlessFastEnvMapCB_PS_t Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 2 + OpMemberDecorate %PerViewPushConst_t 0 Offset 0 + OpDecorate %PerViewPushConst_t Block + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + OpDecorate %i_vTextureCoords Location 0 + OpDecorate %_entryPointOutput_vColor Location 0 +;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +;CHECK: OpDecorate %_struct_111 Block +;CHECK: OpMemberDecorate %_struct_111 0 Offset 0 +;CHECK: OpDecorate %113 DescriptorSet 7 +;CHECK: OpDecorate %113 Binding 1 +;CHECK: OpDecorate %_struct_142 Block +;CHECK: OpMemberDecorate %_struct_142 0 Offset 0 +;CHECK: OpMemberDecorate %_struct_142 1 Offset 4 +;CHECK: OpDecorate %144 DescriptorSet 7 +;CHECK: OpDecorate %144 Binding 0 +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 +%mat4v3float = OpTypeMatrix %v3float 4 +%PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float + %uint = OpTypeInt 32 0 + %uint_128 = OpConstant %uint 128 +%_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128 +%_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 +%_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t + %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%PerViewPushConst_t = OpTypeStruct %uint +%_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t + %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant +%_ptr_PushConstant_uint = OpTypePointer PushConstant %uint + %int_2 = OpConstant %int 2 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %46 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_46 = OpTypePointer UniformConstant %46 + %g_tColor = OpVariable %_ptr_UniformConstant_46 UniformConstant + %50 = OpTypeSampler +%_ptr_UniformConstant_50 = OpTypePointer UniformConstant %50 + %g_sAniso = OpVariable %_ptr_UniformConstant_50 UniformConstant + %54 = OpTypeSampledImage %46 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +;CHECK: %uint_0 = OpConstant %uint 0 +;CHECK: %uint_80 = OpConstant %uint 80 +;CHECK: %uint_64 = OpConstant %uint 64 +;CHECK: %uint_7 = OpConstant %uint 7 +;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %104 = OpTypeFunction %uint %uint %uint %uint %uint +;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint +;CHECK:%_struct_111 = OpTypeStruct %_runtimearr_uint +;CHECK:%_ptr_StorageBuffer__struct_111 = OpTypePointer StorageBuffer %_struct_111 +;CHECK: %113 = OpVariable %_ptr_StorageBuffer__struct_111 StorageBuffer +;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +;CHECK: %bool = OpTypeBool +;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %135 = OpTypeFunction %void %uint %uint %uint %uint %uint +;CHECK:%_struct_142 = OpTypeStruct %uint %_runtimearr_uint +;CHECK:%_ptr_StorageBuffer__struct_142 = OpTypePointer StorageBuffer %_struct_142 +;CHECK: %144 = OpVariable %_ptr_StorageBuffer__struct_142 StorageBuffer +;CHECK: %uint_11 = OpConstant %uint 11 +;CHECK: %uint_4 = OpConstant %uint 4 +;CHECK: %uint_1 = OpConstant %uint 1 +;CHECK: %uint_23 = OpConstant %uint 23 +;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %uint_5 = OpConstant %uint 5 +;CHECK: %uint_8 = OpConstant %uint 8 +;CHECK: %uint_9 = OpConstant %uint 9 +;CHECK: %uint_10 = OpConstant %uint 10 +;CHECK: %uint_78 = OpConstant %uint 78 +;CHECK: %189 = OpConstantNull %v2float +;CHECK: %uint_83 = OpConstant %uint 83 +;CHECK: %201 = OpConstantNull %v4float + %MainPs = OpFunction %void None %3 + %5 = OpLabel +;CHECK: %126 = OpFunctionCall %uint %103 %uint_0 %uint_0 %uint_2 %uint_0 +;CHECK: %191 = OpFunctionCall %uint %103 %uint_0 %uint_0 %uint_0 %uint_0 +;CHECK: OpBranch %93 +;CHECK: %93 = OpLabel +;CHECK: OpBranch %92 +;CHECK: %92 = OpLabel + %66 = OpLoad %v2float %i_vTextureCoords + %79 = OpAccessChain %_ptr_PushConstant_uint %__0 %int_0 + %80 = OpLoad %uint %79 + %81 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %80 %int_2 + %82 = OpLoad %v2float %81 + %86 = OpFAdd %v2float %66 %82 +;CHECK-NOT: %82 = OpLoad %v2float %81 +;CHECK-NOT: %86 = OpFAdd %v2float %66 %82 +;CHECK: %96 = OpIMul %uint %uint_80 %80 +;CHECK: %97 = OpIAdd %uint %uint_0 %96 +;CHECK: %99 = OpIAdd %uint %97 %uint_64 +;CHECK: %101 = OpIAdd %uint %99 %uint_7 +;CHECK: %128 = OpULessThan %bool %101 %126 +;CHECK: OpSelectionMerge %130 None +;CHECK: OpBranchConditional %128 %131 %132 +;CHECK: %131 = OpLabel +;CHECK: %133 = OpLoad %v2float %81 +;CHECK: OpBranch %130 +;CHECK: %132 = OpLabel +;CHECK: %188 = OpFunctionCall %void %134 %uint_78 %uint_3 %uint_0 %101 %126 +;CHECK: OpBranch %130 +;CHECK: %130 = OpLabel +;CHECK: %190 = OpPhi %v2float %133 %131 %189 %132 +;CHECK: %86 = OpFAdd %v2float %66 %190 + %87 = OpLoad %46 %g_tColor %88 = OpLoad %50 %g_sAniso %89 = + OpSampledImage %54 %87 %88 %91 = OpImageSampleImplicitLod %v4float %89 %86 + OpStore %_entryPointOutput_vColor %91 +;CHECK-NOT: %91 = OpImageSampleImplicitLod %v4float %89 %86 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %91 +;CHECK: %192 = OpULessThan %bool %uint_0 %191 +;CHECK: OpSelectionMerge %193 None +;CHECK: OpBranchConditional %192 %194 %195 +;CHECK: %194 = OpLabel +;CHECK: %196 = OpLoad %46 %g_tColor +;CHECK: %197 = OpSampledImage %54 %196 %88 +;CHECK: %198 = OpImageSampleImplicitLod %v4float %197 %86 +;CHECK: OpBranch %193 +;CHECK: %195 = OpLabel +;CHECK: %200 = OpFunctionCall %void %134 %uint_83 %uint_1 %uint_0 %uint_0 %uint_0 +;CHECK: OpBranch %193 +;CHECK: %193 = OpLabel +;CHECK: %202 = OpPhi %v4float %198 %194 %201 %195 +;CHECK: OpStore %_entryPointOutput_vColor %202 + OpReturn + OpFunctionEnd +;CHECK: %103 = OpFunction %uint None %104 +;CHECK: %105 = OpFunctionParameter %uint +;CHECK: %106 = OpFunctionParameter %uint +;CHECK: %107 = OpFunctionParameter %uint +;CHECK: %108 = OpFunctionParameter %uint +;CHECK: %109 = OpLabel +;CHECK: %115 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %105 +;CHECK: %116 = OpLoad %uint %115 +;CHECK: %117 = OpIAdd %uint %116 %106 +;CHECK: %118 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %117 +;CHECK: %119 = OpLoad %uint %118 +;CHECK: %120 = OpIAdd %uint %119 %107 +;CHECK: %121 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %120 +;CHECK: %122 = OpLoad %uint %121 +;CHECK: %123 = OpIAdd %uint %122 %108 +;CHECK: %124 = OpAccessChain %_ptr_StorageBuffer_uint %113 %uint_0 %123 +;CHECK: %125 = OpLoad %uint %124 +;CHECK: OpReturnValue %125 +;CHECK: OpFunctionEnd +;CHECK: %134 = OpFunction %void None %135 +;CHECK: %136 = OpFunctionParameter %uint +;CHECK: %137 = OpFunctionParameter %uint +;CHECK: %138 = OpFunctionParameter %uint +;CHECK: %139 = OpFunctionParameter %uint +;CHECK: %140 = OpFunctionParameter %uint +;CHECK: %141 = OpLabel +;CHECK: %145 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_0 +;CHECK: %148 = OpAtomicIAdd %uint %145 %uint_4 %uint_0 %uint_11 +;CHECK: %149 = OpIAdd %uint %148 %uint_11 +;CHECK: %150 = OpArrayLength %uint %144 1 +;CHECK: %151 = OpULessThanEqual %bool %149 %150 +;CHECK: OpSelectionMerge %152 None +;CHECK: OpBranchConditional %151 %153 %152 +;CHECK: %153 = OpLabel +;CHECK: %154 = OpIAdd %uint %148 %uint_0 +;CHECK: %156 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %154 +;CHECK: OpStore %156 %uint_11 +;CHECK: %158 = OpIAdd %uint %148 %uint_1 +;CHECK: %159 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %158 +;CHECK: OpStore %159 %uint_23 +;CHECK: %160 = OpIAdd %uint %148 %uint_2 +;CHECK: %161 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %160 +;CHECK: OpStore %161 %136 +;CHECK: %162 = OpIAdd %uint %148 %uint_3 +;CHECK: %163 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %162 +;CHECK: OpStore %163 %uint_4 +;CHECK: %166 = OpLoad %v4float %gl_FragCoord +;CHECK: %168 = OpBitcast %v4uint %166 +;CHECK: %169 = OpCompositeExtract %uint %168 0 +;CHECK: %170 = OpIAdd %uint %148 %uint_4 +;CHECK: %171 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %170 +;CHECK: OpStore %171 %169 +;CHECK: %172 = OpCompositeExtract %uint %168 1 +;CHECK: %174 = OpIAdd %uint %148 %uint_5 +;CHECK: %175 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %174 +;CHECK: OpStore %175 %172 +;CHECK: %176 = OpIAdd %uint %148 %uint_7 +;CHECK: %177 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %176 +;CHECK: OpStore %177 %137 +;CHECK: %179 = OpIAdd %uint %148 %uint_8 +;CHECK: %180 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %179 +;CHECK: OpStore %180 %138 +;CHECK: %182 = OpIAdd %uint %148 %uint_9 +;CHECK: %183 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %182 +;CHECK: OpStore %183 %139 +;CHECK: %185 = OpIAdd %uint %148 %uint_10 +;CHECK: %186 = OpAccessChain %_ptr_StorageBuffer_uint %144 %uint_1 %185 +;CHECK: OpStore %186 %140 +;CHECK: OpBranch %152 +;CHECK: %152 = OpLabel +;CHECK: OpReturn +;CHECK: OpFunctionEnd + )"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true, 7u, 23u, true, true, + true); +} + +TEST_F(InstBindlessTest, Descriptor16BitIdxRef) { + // Check that descriptor indexed with 16bit index is inbounds and + // initialized + // + // Use Simple source with min16uint g_nDataIdx + + const std::string text = R"( + OpCapability Shader + OpCapability Int16 + OpCapability StoragePushConstant16 +;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %_ %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %60 %gl_FragCoord %119 + OpExecutionMode %MainPs OriginUpperLeft + OpSource HLSL 500 + OpName %MainPs "MainPs" + OpName %g_tColor "g_tColor" + OpName %PerViewConstantBuffer_t "PerViewConstantBuffer_t" + OpMemberName %PerViewConstantBuffer_t 0 "g_nDataIdx" + OpName %_ "" + OpName %g_sAniso "g_sAniso" + OpName %i_vTextureCoords "i.vTextureCoords" + OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpMemberDecorate %PerViewConstantBuffer_t 0 Offset 0 + OpDecorate %PerViewConstantBuffer_t Block + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 0 + OpDecorate %i_vTextureCoords Location 0 + OpDecorate %_entryPointOutput_vColor Location 0 +;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +;CHECK: OpDecorate %_struct_58 Block +;CHECK: OpMemberDecorate %_struct_58 0 Offset 0 +;CHECK: OpMemberDecorate %_struct_58 1 Offset 4 +;CHECK: OpDecorate %60 DescriptorSet 7 +;CHECK: OpDecorate %60 Binding 0 +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +;CHECK: OpDecorate %_struct_117 Block +;CHECK: OpMemberDecorate %_struct_117 0 Offset 0 +;CHECK: OpDecorate %119 DescriptorSet 7 +;CHECK: OpDecorate %119 Binding 1 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %16 = OpTypeImage %float 2D 0 0 0 1 Unknown + %uint = OpTypeInt 32 0 + %uint_128 = OpConstant %uint 128 +%_arr_16_uint_128 = OpTypeArray %16 %uint_128 +%_ptr_UniformConstant__arr_16_uint_128 = OpTypePointer UniformConstant %_arr_16_uint_128 + %g_tColor = OpVariable %_ptr_UniformConstant__arr_16_uint_128 UniformConstant + %ushort = OpTypeInt 16 0 +%PerViewConstantBuffer_t = OpTypeStruct %ushort +%_ptr_PushConstant_PerViewConstantBuffer_t = OpTypePointer PushConstant %PerViewConstantBuffer_t + %_ = OpVariable %_ptr_PushConstant_PerViewConstantBuffer_t PushConstant +%_ptr_PushConstant_ushort = OpTypePointer PushConstant %ushort +%_ptr_UniformConstant_16 = OpTypePointer UniformConstant %16 + %25 = OpTypeSampler +%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25 + %g_sAniso = OpVariable %_ptr_UniformConstant_25 UniformConstant + %27 = OpTypeSampledImage %16 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +;CHECK: %uint_0 = OpConstant %uint 0 +;CHECK: %bool = OpTypeBool +;CHECK: %51 = OpTypeFunction %void %uint %uint %uint %uint +;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint +;CHECK: %_struct_58 = OpTypeStruct %uint %_runtimearr_uint +;CHECK:%_ptr_StorageBuffer__struct_58 = OpTypePointer StorageBuffer %_struct_58 +;CHECK: %60 = OpVariable %_ptr_StorageBuffer__struct_58 StorageBuffer +;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +;CHECK: %uint_10 = OpConstant %uint 10 +;CHECK: %uint_4 = OpConstant %uint 4 +;CHECK: %uint_1 = OpConstant %uint 1 +;CHECK: %uint_23 = OpConstant %uint 23 +;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %uint_5 = OpConstant %uint 5 +;CHECK: %uint_7 = OpConstant %uint 7 +;CHECK: %uint_8 = OpConstant %uint 8 +;CHECK: %uint_9 = OpConstant %uint 9 +;CHECK: %uint_60 = OpConstant %uint 60 +;CHECK: %106 = OpConstantNull %v4float +;CHECK: %111 = OpTypeFunction %uint %uint %uint %uint %uint +;CHECK:%_struct_117 = OpTypeStruct %_runtimearr_uint +;CHECK:%_ptr_StorageBuffer__struct_117 = OpTypePointer StorageBuffer %_struct_117 +;CHECK: %119 = OpVariable %_ptr_StorageBuffer__struct_117 StorageBuffer + %MainPs = OpFunction %void None %10 + %30 = OpLabel +;CHECK: OpBranch %108 +;CHECK: %108 = OpLabel +;CHECK: OpBranch %39 +;CHECK: %39 = OpLabel + %31 = OpLoad %v2float %i_vTextureCoords + %32 = OpAccessChain %_ptr_PushConstant_ushort %_ %int_0 + %33 = OpLoad %ushort %32 + %34 = OpAccessChain %_ptr_UniformConstant_16 %g_tColor %33 + %35 = OpLoad %16 %34 + %36 = OpLoad %25 %g_sAniso + %37 = OpSampledImage %27 %35 %36 + %38 = OpImageSampleImplicitLod %v4float %37 %31 + OpStore %_entryPointOutput_vColor %38 +;CHECK-NOT: %38 = OpImageSampleImplicitLod %v4float %37 %31 +;CHECK-NOT: OpStore %_entryPointOutput_vColor %38 +;CHECK: %41 = OpUConvert %uint %33 +;CHECK: %43 = OpULessThan %bool %41 %uint_128 +;CHECK: OpSelectionMerge %44 None +;CHECK: OpBranchConditional %43 %45 %46 +;CHECK: %45 = OpLabel +;CHECK: %47 = OpLoad %16 %34 +;CHECK: %48 = OpSampledImage %27 %47 %36 +;CHECK: %109 = OpUConvert %uint %33 +;CHECK: %131 = OpFunctionCall %uint %110 %uint_0 %uint_0 %uint_0 %109 +;CHECK: %132 = OpULessThan %bool %uint_0 %131 +;CHECK: OpSelectionMerge %133 None +;CHECK: OpBranchConditional %132 %134 %135 +;CHECK: %134 = OpLabel +;CHECK: %136 = OpLoad %16 %34 +;CHECK: %137 = OpSampledImage %27 %136 %36 +;CHECK: %138 = OpImageSampleImplicitLod %v4float %137 %31 +;CHECK: OpBranch %133 +;CHECK: %135 = OpLabel +;CHECK: %139 = OpUConvert %uint %33 +;CHECK: %140 = OpFunctionCall %void %50 %uint_60 %uint_1 %139 %uint_0 +;CHECK: OpBranch %133 +;CHECK: %133 = OpLabel +;CHECK: %141 = OpPhi %v4float %138 %134 %106 %135 +;CHECK: OpBranch %44 +;CHECK: %46 = OpLabel +;CHECK: %105 = OpFunctionCall %void %50 %uint_60 %uint_0 %41 %uint_128 +;CHECK: OpBranch %44 +;CHECK: %44 = OpLabel +;CHECK: %107 = OpPhi %v4float %141 %133 %106 %46 +;CHECK: OpStore %_entryPointOutput_vColor %107 + OpReturn + OpFunctionEnd +;CHECK: %50 = OpFunction %void None %51 +;CHECK: %52 = OpFunctionParameter %uint +;CHECK: %53 = OpFunctionParameter %uint +;CHECK: %54 = OpFunctionParameter %uint +;CHECK: %55 = OpFunctionParameter %uint +;CHECK: %56 = OpLabel +;CHECK: %62 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_0 +;CHECK: %65 = OpAtomicIAdd %uint %62 %uint_4 %uint_0 %uint_10 +;CHECK: %66 = OpIAdd %uint %65 %uint_10 +;CHECK: %67 = OpArrayLength %uint %60 1 +;CHECK: %68 = OpULessThanEqual %bool %66 %67 +;CHECK: OpSelectionMerge %69 None +;CHECK: OpBranchConditional %68 %70 %69 +;CHECK: %70 = OpLabel +;CHECK: %71 = OpIAdd %uint %65 %uint_0 +;CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %71 +;CHECK: OpStore %73 %uint_10 +;CHECK: %75 = OpIAdd %uint %65 %uint_1 +;CHECK: %76 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %75 +;CHECK: OpStore %76 %uint_23 +;CHECK: %78 = OpIAdd %uint %65 %uint_2 +;CHECK: %79 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %78 +;CHECK: OpStore %79 %52 +;CHECK: %81 = OpIAdd %uint %65 %uint_3 +;CHECK: %82 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %81 +;CHECK: OpStore %82 %uint_4 +;CHECK: %85 = OpLoad %v4float %gl_FragCoord +;CHECK: %87 = OpBitcast %v4uint %85 +;CHECK: %88 = OpCompositeExtract %uint %87 0 +;CHECK: %89 = OpIAdd %uint %65 %uint_4 +;CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %89 +;CHECK: OpStore %90 %88 +;CHECK: %91 = OpCompositeExtract %uint %87 1 +;CHECK: %93 = OpIAdd %uint %65 %uint_5 +;CHECK: %94 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %93 +;CHECK: OpStore %94 %91 +;CHECK: %96 = OpIAdd %uint %65 %uint_7 +;CHECK: %97 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %96 +;CHECK: OpStore %97 %53 +;CHECK: %99 = OpIAdd %uint %65 %uint_8 +;CHECK: %100 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %99 +;CHECK: OpStore %100 %54 +;CHECK: %102 = OpIAdd %uint %65 %uint_9 +;CHECK: %103 = OpAccessChain %_ptr_StorageBuffer_uint %60 %uint_1 %102 +;CHECK: OpStore %103 %55 +;CHECK: OpBranch %69 +;CHECK: %69 = OpLabel +;CHECK: OpReturn +;CHECK: OpFunctionEnd +;CHECK: %110 = OpFunction %uint None %111 +;CHECK: %112 = OpFunctionParameter %uint +;CHECK: %113 = OpFunctionParameter %uint +;CHECK: %114 = OpFunctionParameter %uint +;CHECK: %115 = OpFunctionParameter %uint +;CHECK: %116 = OpLabel +;CHECK: %120 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %112 +;CHECK: %121 = OpLoad %uint %120 +;CHECK: %122 = OpIAdd %uint %121 %113 +;CHECK: %123 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %122 +;CHECK: %124 = OpLoad %uint %123 +;CHECK: %125 = OpIAdd %uint %124 %114 +;CHECK: %126 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %125 +;CHECK: %127 = OpLoad %uint %126 +;CHECK: %128 = OpIAdd %uint %127 %115 +;CHECK: %129 = OpAccessChain %_ptr_StorageBuffer_uint %119 %uint_0 %128 +;CHECK: %130 = OpLoad %uint %129 +;CHECK: OpReturnValue %130 +;CHECK: OpFunctionEnd + )"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true, 7u, 23u, true, true, + false); +} + +TEST_F(InstBindlessTest, UniformArray16bitIdxRef) { + // Check that uniform array ref with 16bit index does not go out-of-bounds. + // + // Texture2D g_tColor; + // SamplerState g_sAniso; + // + // layout(push_constant) cbuffer PerViewPushConst_t { min16uint g_c; }; + // + // struct PerBatchEnvMapConstantBuffer_t { + // float4x3 g_matEnvMapWorldToLocal; + // float4 g_vEnvironmentMapBoxMins; + // float2 g_TexOff; + // }; + // + // cbuffer _BindlessFastEnvMapCB_PS_t { + // PerBatchEnvMapConstantBuffer_t g_envMapConstants[128]; + // }; + // + // struct PS_INPUT { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) { + // PS_OUTPUT ps_output; + // float2 off; + // float2 vtc; + // off = g_envMapConstants[g_c].g_TexOff; + // vtc = i.vTextureCoords.xy + off; + // ps_output.vColor = g_tColor.Sample(g_sAniso, vtc); + // return ps_output; + // } + + const std::string text = R"( + OpCapability Shader + OpCapability Int16 + OpCapability StoragePushConstant16 +;CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor +;CHECK: OpEntryPoint Fragment %MainPs "MainPs" %_ %__0 %g_tColor %g_sAniso %i_vTextureCoords %_entryPointOutput_vColor %69 %97 %gl_FragCoord + OpExecutionMode %MainPs OriginUpperLeft + OpSource HLSL 500 + OpName %MainPs "MainPs" + OpName %PerBatchEnvMapConstantBuffer_t "PerBatchEnvMapConstantBuffer_t" + OpMemberName %PerBatchEnvMapConstantBuffer_t 0 "g_matEnvMapWorldToLocal" + OpMemberName %PerBatchEnvMapConstantBuffer_t 1 "g_vEnvironmentMapBoxMins" + OpMemberName %PerBatchEnvMapConstantBuffer_t 2 "g_TexOff" + OpName %_BindlessFastEnvMapCB_PS_t "_BindlessFastEnvMapCB_PS_t" + OpMemberName %_BindlessFastEnvMapCB_PS_t 0 "g_envMapConstants" + OpName %_ "" + OpName %PerViewPushConst_t "PerViewPushConst_t" + OpMemberName %PerViewPushConst_t 0 "g_c" + OpName %__0 "" + OpName %g_tColor "g_tColor" + OpName %g_sAniso "g_sAniso" + OpName %i_vTextureCoords "i.vTextureCoords" + OpName %_entryPointOutput_vColor "@entryPointOutput.vColor" + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 RowMajor + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 Offset 0 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 0 MatrixStride 16 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 1 Offset 48 + OpMemberDecorate %PerBatchEnvMapConstantBuffer_t 2 Offset 64 + OpDecorate %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 ArrayStride 80 + OpMemberDecorate %_BindlessFastEnvMapCB_PS_t 0 Offset 0 + OpDecorate %_BindlessFastEnvMapCB_PS_t Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpMemberDecorate %PerViewPushConst_t 0 Offset 0 + OpDecorate %PerViewPushConst_t Block + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 0 + OpDecorate %i_vTextureCoords Location 0 + OpDecorate %_entryPointOutput_vColor Location 0 +;CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +;CHECK: OpDecorate %_struct_67 Block +;CHECK: OpMemberDecorate %_struct_67 0 Offset 0 +;CHECK: OpDecorate %69 DescriptorSet 7 +;CHECK: OpDecorate %69 Binding 1 +;CHECK: OpDecorate %_struct_95 Block +;CHECK: OpMemberDecorate %_struct_95 0 Offset 0 +;CHECK: OpMemberDecorate %_struct_95 1 Offset 4 +;CHECK: OpDecorate %97 DescriptorSet 7 +;CHECK: OpDecorate %97 Binding 0 +;CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord + %void = OpTypeVoid + %14 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 +%mat4v3float = OpTypeMatrix %v3float 4 +%PerBatchEnvMapConstantBuffer_t = OpTypeStruct %mat4v3float %v4float %v2float + %uint = OpTypeInt 32 0 + %uint_128 = OpConstant %uint 128 +%_arr_PerBatchEnvMapConstantBuffer_t_uint_128 = OpTypeArray %PerBatchEnvMapConstantBuffer_t %uint_128 +%_BindlessFastEnvMapCB_PS_t = OpTypeStruct %_arr_PerBatchEnvMapConstantBuffer_t_uint_128 +%_ptr_Uniform__BindlessFastEnvMapCB_PS_t = OpTypePointer Uniform %_BindlessFastEnvMapCB_PS_t + %_ = OpVariable %_ptr_Uniform__BindlessFastEnvMapCB_PS_t Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %ushort = OpTypeInt 16 0 +%PerViewPushConst_t = OpTypeStruct %ushort +%_ptr_PushConstant_PerViewPushConst_t = OpTypePointer PushConstant %PerViewPushConst_t + %__0 = OpVariable %_ptr_PushConstant_PerViewPushConst_t PushConstant +%_ptr_PushConstant_ushort = OpTypePointer PushConstant %ushort + %int_2 = OpConstant %int 2 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %30 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_30 = OpTypePointer UniformConstant %30 + %g_tColor = OpVariable %_ptr_UniformConstant_30 UniformConstant + %32 = OpTypeSampler +%_ptr_UniformConstant_32 = OpTypePointer UniformConstant %32 + %g_sAniso = OpVariable %_ptr_UniformConstant_32 UniformConstant + %34 = OpTypeSampledImage %30 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%i_vTextureCoords = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_vColor = OpVariable %_ptr_Output_v4float Output +;CHECK: %uint_0 = OpConstant %uint 0 +;CHECK: %uint_80 = OpConstant %uint 80 +;CHECK: %uint_64 = OpConstant %uint 64 +;CHECK: %uint_7 = OpConstant %uint 7 +;CHECK: %uint_1 = OpConstant %uint 1 +;CHECK: %61 = OpTypeFunction %uint %uint %uint %uint +;CHECK:%_runtimearr_uint = OpTypeRuntimeArray %uint +;CHECK: %_struct_67 = OpTypeStruct %_runtimearr_uint +;CHECK:%_ptr_StorageBuffer__struct_67 = OpTypePointer StorageBuffer %_struct_67 +;CHECK: %69 = OpVariable %_ptr_StorageBuffer__struct_67 StorageBuffer +;CHECK:%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +;CHECK: %bool = OpTypeBool +;CHECK: %uint_3 = OpConstant %uint 3 +;CHECK: %88 = OpTypeFunction %void %uint %uint %uint %uint %uint +;CHECK: %_struct_95 = OpTypeStruct %uint %_runtimearr_uint +;CHECK:%_ptr_StorageBuffer__struct_95 = OpTypePointer StorageBuffer %_struct_95 +;CHECK: %97 = OpVariable %_ptr_StorageBuffer__struct_95 StorageBuffer +;CHECK: %uint_11 = OpConstant %uint 11 +;CHECK: %uint_4 = OpConstant %uint 4 +;CHECK: %uint_23 = OpConstant %uint 23 +;CHECK: %uint_2 = OpConstant %uint 2 +;CHECK:%_ptr_Input_v4float = OpTypePointer Input %v4float +;CHECK:%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +;CHECK: %v4uint = OpTypeVector %uint 4 +;CHECK: %uint_5 = OpConstant %uint 5 +;CHECK: %uint_8 = OpConstant %uint 8 +;CHECK: %uint_9 = OpConstant %uint 9 +;CHECK: %uint_10 = OpConstant %uint 10 +;CHECK: %uint_81 = OpConstant %uint 81 +;CHECK: %142 = OpConstantNull %v2float + %MainPs = OpFunction %void None %14 + %37 = OpLabel +;CHECK: %79 = OpFunctionCall %uint %60 %uint_1 %uint_0 %uint_0 +;CHECK: OpBranch %49 +;CHECK: %49 = OpLabel +;CHECK: OpBranch %48 +;CHECK: %48 = OpLabel + %38 = OpLoad %v2float %i_vTextureCoords + %39 = OpAccessChain %_ptr_PushConstant_ushort %__0 %int_0 + %40 = OpLoad %ushort %39 + %41 = OpAccessChain %_ptr_Uniform_v2float %_ %int_0 %40 %int_2 + %42 = OpLoad %v2float %41 + %43 = OpFAdd %v2float %38 %42 +;CHECK-NOT: %42 = OpLoad %v2float %41 +;CHECK-NOT: %43 = OpFAdd %v2float %38 %42 +;CHECK: %52 = OpUConvert %uint %40 +;CHECK: %53 = OpIMul %uint %uint_80 %52 +;CHECK: %54 = OpIAdd %uint %uint_0 %53 +;CHECK: %56 = OpIAdd %uint %54 %uint_64 +;CHECK: %58 = OpIAdd %uint %56 %uint_7 +;CHECK: %81 = OpULessThan %bool %58 %79 +;CHECK: OpSelectionMerge %83 None +;CHECK: OpBranchConditional %81 %84 %85 +;CHECK: %84 = OpLabel +;CHECK: %86 = OpLoad %v2float %41 +;CHECK: OpBranch %83 +;CHECK: %85 = OpLabel +;CHECK: %141 = OpFunctionCall %void %87 %uint_81 %uint_3 %uint_0 %58 %79 +;CHECK: OpBranch %83 +;CHECK: %83 = OpLabel +;CHECK: %143 = OpPhi %v2float %86 %84 %142 %85 +;CHECK: %43 = OpFAdd %v2float %38 %143 + %44 = OpLoad %30 %g_tColor + %45 = OpLoad %32 %g_sAniso + %46 = OpSampledImage %34 %44 %45 + %47 = OpImageSampleImplicitLod %v4float %46 %43 + OpStore %_entryPointOutput_vColor %47 + OpReturn + OpFunctionEnd +;CHECK: %60 = OpFunction %uint None %61 +;CHECK: %62 = OpFunctionParameter %uint +;CHECK: %63 = OpFunctionParameter %uint +;CHECK: %64 = OpFunctionParameter %uint +;CHECK: %65 = OpLabel +;CHECK: %71 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %62 +;CHECK: %72 = OpLoad %uint %71 +;CHECK: %73 = OpIAdd %uint %72 %63 +;CHECK: %74 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %73 +;CHECK: %75 = OpLoad %uint %74 +;CHECK: %76 = OpIAdd %uint %75 %64 +;CHECK: %77 = OpAccessChain %_ptr_StorageBuffer_uint %69 %uint_0 %76 +;CHECK: %78 = OpLoad %uint %77 +;CHECK: OpReturnValue %78 +;CHECK: OpFunctionEnd +;CHECK: %87 = OpFunction %void None %88 +;CHECK: %89 = OpFunctionParameter %uint +;CHECK: %90 = OpFunctionParameter %uint +;CHECK: %91 = OpFunctionParameter %uint +;CHECK: %92 = OpFunctionParameter %uint +;CHECK: %93 = OpFunctionParameter %uint +;CHECK: %94 = OpLabel +;CHECK: %98 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_0 +;CHECK: %101 = OpAtomicIAdd %uint %98 %uint_4 %uint_0 %uint_11 +;CHECK: %102 = OpIAdd %uint %101 %uint_11 +;CHECK: %103 = OpArrayLength %uint %97 1 +;CHECK: %104 = OpULessThanEqual %bool %102 %103 +;CHECK: OpSelectionMerge %105 None +;CHECK: OpBranchConditional %104 %106 %105 +;CHECK: %106 = OpLabel +;CHECK: %107 = OpIAdd %uint %101 %uint_0 +;CHECK: %108 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %107 +;CHECK: OpStore %108 %uint_11 +;CHECK: %110 = OpIAdd %uint %101 %uint_1 +;CHECK: %111 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %110 +;CHECK: OpStore %111 %uint_23 +;CHECK: %113 = OpIAdd %uint %101 %uint_2 +;CHECK: %114 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %113 +;CHECK: OpStore %114 %89 +;CHECK: %115 = OpIAdd %uint %101 %uint_3 +;CHECK: %116 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %115 +;CHECK: OpStore %116 %uint_4 +;CHECK: %119 = OpLoad %v4float %gl_FragCoord +;CHECK: %121 = OpBitcast %v4uint %119 +;CHECK: %122 = OpCompositeExtract %uint %121 0 +;CHECK: %123 = OpIAdd %uint %101 %uint_4 +;CHECK: %124 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %123 +;CHECK: OpStore %124 %122 +;CHECK: %125 = OpCompositeExtract %uint %121 1 +;CHECK: %127 = OpIAdd %uint %101 %uint_5 +;CHECK: %128 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %127 +;CHECK: OpStore %128 %125 +;CHECK: %129 = OpIAdd %uint %101 %uint_7 +;CHECK: %130 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %129 +;CHECK: OpStore %130 %90 +;CHECK: %132 = OpIAdd %uint %101 %uint_8 +;CHECK: %133 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %132 +;CHECK: OpStore %133 %91 +;CHECK: %135 = OpIAdd %uint %101 %uint_9 +;CHECK: %136 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %135 +;CHECK: OpStore %136 %92 +;CHECK: %138 = OpIAdd %uint %101 %uint_10 +;CHECK: %139 = OpAccessChain %_ptr_StorageBuffer_uint %97 %uint_1 %138 +;CHECK: OpStore %139 %93 +;CHECK: OpBranch %105 +;CHECK: %105 = OpLabel +;CHECK: OpReturn +;CHECK: OpFunctionEnd + )"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true, 7u, 23u, false, + false, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// Compute shader +// Geometry shader +// Tesselation control shader +// Tesselation eval shader +// OpImage +// SampledImage variable + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/inst_buff_addr_check_test.cpp b/third_party/spirv-tools/test/opt/inst_buff_addr_check_test.cpp new file mode 100644 index 0000000..41ead67 --- /dev/null +++ b/third_party/spirv-tools/test/opt/inst_buff_addr_check_test.cpp @@ -0,0 +1,620 @@ +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Bindless Check Instrumentation Tests. +// Tests ending with V2 use version 2 record format. + +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InstBuffAddrTest = PassTest<::testing::Test>; + +TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferStore) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // + // layout(buffer_reference, buffer_reference_align = 16) buffer bufStruct; + // + // layout(set = 0, binding = 0) uniform ufoo { + // bufStruct data; + // uint offset; + // } u_info; + // + // layout(buffer_reference, std140) buffer bufStruct { + // layout(offset = 0) int a[2]; + // layout(offset = 32) int b; + // }; + // + // void main() { + // u_info.data.b = 0xca7; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_EXT_physical_storage_buffer" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "data" +OpMemberName %ufoo 1 "offset" +OpName %bufStruct "bufStruct" +OpMemberName %bufStruct 0 "a" +OpMemberName %bufStruct 1 "b" +OpName %u_info "u_info" +OpMemberDecorate %ufoo 0 Offset 0 +OpMemberDecorate %ufoo 1 Offset 8 +OpDecorate %ufoo Block +OpDecorate %_arr_int_uint_2 ArrayStride 16 +OpMemberDecorate %bufStruct 0 Offset 0 +OpMemberDecorate %bufStruct 1 Offset 32 +OpDecorate %bufStruct Block +OpDecorate %u_info DescriptorSet 0 +OpDecorate %u_info Binding 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer +%uint = OpTypeInt 32 0 +%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint +%int = OpTypeInt 32 1 +%uint_2 = OpConstant %uint 2 +%_arr_int_uint_2 = OpTypeArray %int %uint_2 +%bufStruct = OpTypeStruct %_arr_int_uint_2 %int +%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%u_info = OpVariable %_ptr_Uniform_ufoo Uniform +%int_0 = OpConstant %int 0 +%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct +%int_1 = OpConstant %int 1 +%int_3239 = OpConstant %int 3239 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpExtension "SPV_EXT_physical_storage_buffer" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %ufoo "ufoo" +OpMemberName %ufoo 0 "data" +OpMemberName %ufoo 1 "offset" +OpName %bufStruct "bufStruct" +OpMemberName %bufStruct 0 "a" +OpMemberName %bufStruct 1 "b" +OpName %u_info "u_info" +OpMemberDecorate %ufoo 0 Offset 0 +OpMemberDecorate %ufoo 1 Offset 8 +OpDecorate %ufoo Block +OpDecorate %_arr_int_uint_2 ArrayStride 16 +OpMemberDecorate %bufStruct 0 Offset 0 +OpMemberDecorate %bufStruct 1 Offset 32 +OpDecorate %bufStruct Block +OpDecorate %u_info DescriptorSet 0 +OpDecorate %u_info Binding 0 +OpDecorate %_runtimearr_ulong ArrayStride 8 +OpDecorate %_struct_39 Block +OpMemberDecorate %_struct_39 0 Offset 0 +OpDecorate %41 DescriptorSet 7 +OpDecorate %41 Binding 2 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_77 Block +OpMemberDecorate %_struct_77 0 Offset 0 +OpMemberDecorate %_struct_77 1 Offset 4 +OpDecorate %79 DescriptorSet 7 +OpDecorate %79 Binding 0 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%void = OpTypeVoid +%8 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_bufStruct PhysicalStorageBuffer +%uint = OpTypeInt 32 0 +%ufoo = OpTypeStruct %_ptr_PhysicalStorageBuffer_bufStruct %uint +%int = OpTypeInt 32 1 +%uint_2 = OpConstant %uint 2 +%_arr_int_uint_2 = OpTypeArray %int %uint_2 +%bufStruct = OpTypeStruct %_arr_int_uint_2 %int +%_ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer PhysicalStorageBuffer %bufStruct +%_ptr_Uniform_ufoo = OpTypePointer Uniform %ufoo +%u_info = OpVariable %_ptr_Uniform_ufoo Uniform +%int_0 = OpConstant %int 0 +%_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct = OpTypePointer Uniform %_ptr_PhysicalStorageBuffer_bufStruct +%int_1 = OpConstant %int 1 +%int_3239 = OpConstant %int 3239 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +%ulong = OpTypeInt 64 0 +%uint_4 = OpConstant %uint 4 +%bool = OpTypeBool +%28 = OpTypeFunction %bool %ulong %uint +%uint_1 = OpConstant %uint 1 +%_runtimearr_ulong = OpTypeRuntimeArray %ulong +%_struct_39 = OpTypeStruct %_runtimearr_ulong +%_ptr_StorageBuffer__struct_39 = OpTypePointer StorageBuffer %_struct_39 +%41 = OpVariable %_ptr_StorageBuffer__struct_39 StorageBuffer +%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong +%uint_0 = OpConstant %uint 0 +%uint_32 = OpConstant %uint 32 +%70 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_77 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_77 = OpTypePointer StorageBuffer %_struct_77 +%79 = OpVariable %_ptr_StorageBuffer__struct_77 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_23 = OpConstant %uint 23 +%uint_5 = OpConstant %uint 5 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_8 = OpConstant %uint 8 +%uint_9 = OpConstant %uint 9 +%uint_48 = OpConstant %uint 48 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%17 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 +%18 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %17 +%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %18 %int_1 +OpStore %22 %int_3239 Aligned 16 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %8 +%19 = OpLabel +%20 = OpAccessChain %_ptr_Uniform__ptr_PhysicalStorageBuffer_bufStruct %u_info %int_0 +%21 = OpLoad %_ptr_PhysicalStorageBuffer_bufStruct %20 +%22 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %21 %int_1 +%24 = OpConvertPtrToU %ulong %22 +%61 = OpFunctionCall %bool %26 %24 %uint_4 +OpSelectionMerge %62 None +OpBranchConditional %61 %63 %64 +%63 = OpLabel +OpStore %22 %int_3239 Aligned 16 +OpBranch %62 +%64 = OpLabel +%65 = OpUConvert %uint %24 +%67 = OpShiftRightLogical %ulong %24 %uint_32 +%68 = OpUConvert %uint %67 +%124 = OpFunctionCall %void %69 %uint_48 %uint_2 %65 %68 +OpBranch %62 +%62 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%26 = OpFunction %bool None %28 +%29 = OpFunctionParameter %ulong +%30 = OpFunctionParameter %uint +%31 = OpLabel +OpBranch %32 +%32 = OpLabel +%34 = OpPhi %uint %uint_1 %31 %35 %33 +OpLoopMerge %37 %33 None +OpBranch %33 +%33 = OpLabel +%35 = OpIAdd %uint %34 %uint_1 +%44 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %35 +%45 = OpLoad %ulong %44 +%46 = OpUGreaterThan %bool %45 %29 +OpBranchConditional %46 %37 %32 +%37 = OpLabel +%47 = OpISub %uint %35 %uint_1 +%48 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %47 +%49 = OpLoad %ulong %48 +%50 = OpISub %ulong %29 %49 +%51 = OpUConvert %ulong %30 +%52 = OpIAdd %ulong %50 %51 +%53 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %uint_0 +%54 = OpLoad %ulong %53 +%55 = OpUConvert %uint %54 +%56 = OpISub %uint %47 %uint_1 +%57 = OpIAdd %uint %56 %55 +%58 = OpAccessChain %_ptr_StorageBuffer_ulong %41 %uint_0 %57 +%59 = OpLoad %ulong %58 +%60 = OpULessThanEqual %bool %52 %59 +OpReturnValue %60 +OpFunctionEnd +%69 = OpFunction %void None %70 +%71 = OpFunctionParameter %uint +%72 = OpFunctionParameter %uint +%73 = OpFunctionParameter %uint +%74 = OpFunctionParameter %uint +%75 = OpLabel +%81 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_0 +%83 = OpAtomicIAdd %uint %81 %uint_4 %uint_0 %uint_10 +%84 = OpIAdd %uint %83 %uint_10 +%85 = OpArrayLength %uint %79 1 +%86 = OpULessThanEqual %bool %84 %85 +OpSelectionMerge %87 None +OpBranchConditional %86 %88 %87 +%88 = OpLabel +%89 = OpIAdd %uint %83 %uint_0 +%90 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %89 +OpStore %90 %uint_10 +%92 = OpIAdd %uint %83 %uint_1 +%93 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %92 +OpStore %93 %uint_23 +%94 = OpIAdd %uint %83 %uint_2 +%95 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %94 +OpStore %95 %71 +%98 = OpIAdd %uint %83 %uint_3 +%99 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %98 +OpStore %99 %uint_5 +%103 = OpLoad %v3uint %gl_GlobalInvocationID +%104 = OpCompositeExtract %uint %103 0 +%105 = OpCompositeExtract %uint %103 1 +%106 = OpCompositeExtract %uint %103 2 +%107 = OpIAdd %uint %83 %uint_4 +%108 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %107 +OpStore %108 %104 +%109 = OpIAdd %uint %83 %uint_5 +%110 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %109 +OpStore %110 %105 +%112 = OpIAdd %uint %83 %uint_6 +%113 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %112 +OpStore %113 %106 +%115 = OpIAdd %uint %83 %uint_7 +%116 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %115 +OpStore %116 %72 +%118 = OpIAdd %uint %83 %uint_8 +%119 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %118 +OpStore %119 %73 +%121 = OpIAdd %uint %83 %uint_9 +%122 = OpAccessChain %_ptr_StorageBuffer_uint %79 %uint_1 %121 +OpStore %122 %74 +OpBranch %87 +%87 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u); +} + +TEST_F(InstBuffAddrTest, InstPhysicalStorageBufferLoadAndStore) { + // #version 450 + // #extension GL_EXT_buffer_reference : enable + + // // forward reference + // layout(buffer_reference) buffer blockType; + + // layout(buffer_reference, std430, buffer_reference_align = 16) buffer + // blockType { + // int x; + // blockType next; + // }; + + // layout(std430) buffer rootBlock { + // blockType root; + // } r; + + // void main() + // { + // blockType b = r.root; + // b = b.next; + // b.x = 531; + // } + + const std::string defs_before = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_EXT_physical_storage_buffer" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %blockType "blockType" +OpMemberName %blockType 0 "x" +OpMemberName %blockType 1 "next" +OpName %rootBlock "rootBlock" +OpMemberName %rootBlock 0 "root" +OpName %r "r" +OpMemberDecorate %blockType 0 Offset 0 +OpMemberDecorate %blockType 1 Offset 8 +OpDecorate %blockType Block +OpMemberDecorate %rootBlock 0 Offset 0 +OpDecorate %rootBlock Block +OpDecorate %r DescriptorSet 0 +OpDecorate %r Binding 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer +%int = OpTypeInt 32 1 +%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType +%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType +%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType +%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock +%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType +%int_1 = OpConstant %int 1 +%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType +%int_531 = OpConstant %int 531 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +)"; + + const std::string defs_after = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpCapability Int64 +OpExtension "SPV_EXT_physical_storage_buffer" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +OpName %main "main" +OpName %blockType "blockType" +OpMemberName %blockType 0 "x" +OpMemberName %blockType 1 "next" +OpName %rootBlock "rootBlock" +OpMemberName %rootBlock 0 "root" +OpName %r "r" +OpMemberDecorate %blockType 0 Offset 0 +OpMemberDecorate %blockType 1 Offset 8 +OpDecorate %blockType Block +OpMemberDecorate %rootBlock 0 Offset 0 +OpDecorate %rootBlock Block +OpDecorate %r DescriptorSet 0 +OpDecorate %r Binding 0 +OpDecorate %_runtimearr_ulong ArrayStride 8 +OpDecorate %_struct_45 Block +OpMemberDecorate %_struct_45 0 Offset 0 +OpDecorate %47 DescriptorSet 7 +OpDecorate %47 Binding 2 +OpDecorate %_runtimearr_uint ArrayStride 4 +OpDecorate %_struct_84 Block +OpMemberDecorate %_struct_84 0 Offset 0 +OpMemberDecorate %_struct_84 1 Offset 4 +OpDecorate %86 DescriptorSet 7 +OpDecorate %86 Binding 0 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer +%int = OpTypeInt 32 1 +%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType +%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType +%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType +%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock +%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType +%int_1 = OpConstant %int 1 +%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType +%int_531 = OpConstant %int 531 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +%uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%ulong = OpTypeInt 64 0 +%uint_8 = OpConstant %uint 8 +%bool = OpTypeBool +%34 = OpTypeFunction %bool %ulong %uint +%uint_1 = OpConstant %uint 1 +%_runtimearr_ulong = OpTypeRuntimeArray %ulong +%_struct_45 = OpTypeStruct %_runtimearr_ulong +%_ptr_StorageBuffer__struct_45 = OpTypePointer StorageBuffer %_struct_45 +%47 = OpVariable %_ptr_StorageBuffer__struct_45 StorageBuffer +%_ptr_StorageBuffer_ulong = OpTypePointer StorageBuffer %ulong +%uint_0 = OpConstant %uint 0 +%uint_32 = OpConstant %uint 32 +%77 = OpTypeFunction %void %uint %uint %uint %uint +%_runtimearr_uint = OpTypeRuntimeArray %uint +%_struct_84 = OpTypeStruct %uint %_runtimearr_uint +%_ptr_StorageBuffer__struct_84 = OpTypePointer StorageBuffer %_struct_84 +%86 = OpVariable %_ptr_StorageBuffer__struct_84 StorageBuffer +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%uint_10 = OpConstant %uint 10 +%uint_4 = OpConstant %uint 4 +%uint_23 = OpConstant %uint 23 +%uint_5 = OpConstant %uint 5 +%uint_3 = OpConstant %uint 3 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%uint_6 = OpConstant %uint 6 +%uint_7 = OpConstant %uint 7 +%uint_9 = OpConstant %uint 9 +%uint_44 = OpConstant %uint 44 +%132 = OpConstantNull %ulong +%uint_46 = OpConstant %uint 46 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0 +%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16 +%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1 +%22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 +%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 +OpStore %26 %int_531 Aligned 16 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0 +%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16 +%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1 +%30 = OpConvertPtrToU %ulong %21 +%67 = OpFunctionCall %bool %32 %30 %uint_8 +OpSelectionMerge %68 None +OpBranchConditional %67 %69 %70 +%69 = OpLabel +%71 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 +OpBranch %68 +%70 = OpLabel +%72 = OpUConvert %uint %30 +%74 = OpShiftRightLogical %ulong %30 %uint_32 +%75 = OpUConvert %uint %74 +%131 = OpFunctionCall %void %76 %uint_44 %uint_2 %72 %75 +%133 = OpConvertUToPtr %_ptr_PhysicalStorageBuffer_blockType %132 +OpBranch %68 +%68 = OpLabel +%134 = OpPhi %_ptr_PhysicalStorageBuffer_blockType %71 %69 %133 %70 +%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %134 %int_0 +%135 = OpConvertPtrToU %ulong %26 +%136 = OpFunctionCall %bool %32 %135 %uint_4 +OpSelectionMerge %137 None +OpBranchConditional %136 %138 %139 +%138 = OpLabel +OpStore %26 %int_531 Aligned 16 +OpBranch %137 +%139 = OpLabel +%140 = OpUConvert %uint %135 +%141 = OpShiftRightLogical %ulong %135 %uint_32 +%142 = OpUConvert %uint %141 +%144 = OpFunctionCall %void %76 %uint_46 %uint_2 %140 %142 +OpBranch %137 +%137 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string new_funcs = + R"(%32 = OpFunction %bool None %34 +%35 = OpFunctionParameter %ulong +%36 = OpFunctionParameter %uint +%37 = OpLabel +OpBranch %38 +%38 = OpLabel +%40 = OpPhi %uint %uint_1 %37 %41 %39 +OpLoopMerge %43 %39 None +OpBranch %39 +%39 = OpLabel +%41 = OpIAdd %uint %40 %uint_1 +%50 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %41 +%51 = OpLoad %ulong %50 +%52 = OpUGreaterThan %bool %51 %35 +OpBranchConditional %52 %43 %38 +%43 = OpLabel +%53 = OpISub %uint %41 %uint_1 +%54 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %53 +%55 = OpLoad %ulong %54 +%56 = OpISub %ulong %35 %55 +%57 = OpUConvert %ulong %36 +%58 = OpIAdd %ulong %56 %57 +%59 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %uint_0 +%60 = OpLoad %ulong %59 +%61 = OpUConvert %uint %60 +%62 = OpISub %uint %53 %uint_1 +%63 = OpIAdd %uint %62 %61 +%64 = OpAccessChain %_ptr_StorageBuffer_ulong %47 %uint_0 %63 +%65 = OpLoad %ulong %64 +%66 = OpULessThanEqual %bool %58 %65 +OpReturnValue %66 +OpFunctionEnd +%76 = OpFunction %void None %77 +%78 = OpFunctionParameter %uint +%79 = OpFunctionParameter %uint +%80 = OpFunctionParameter %uint +%81 = OpFunctionParameter %uint +%82 = OpLabel +%88 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_0 +%91 = OpAtomicIAdd %uint %88 %uint_4 %uint_0 %uint_10 +%92 = OpIAdd %uint %91 %uint_10 +%93 = OpArrayLength %uint %86 1 +%94 = OpULessThanEqual %bool %92 %93 +OpSelectionMerge %95 None +OpBranchConditional %94 %96 %95 +%96 = OpLabel +%97 = OpIAdd %uint %91 %uint_0 +%98 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %97 +OpStore %98 %uint_10 +%100 = OpIAdd %uint %91 %uint_1 +%101 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %100 +OpStore %101 %uint_23 +%102 = OpIAdd %uint %91 %uint_2 +%103 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %102 +OpStore %103 %78 +%106 = OpIAdd %uint %91 %uint_3 +%107 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %106 +OpStore %107 %uint_5 +%111 = OpLoad %v3uint %gl_GlobalInvocationID +%112 = OpCompositeExtract %uint %111 0 +%113 = OpCompositeExtract %uint %111 1 +%114 = OpCompositeExtract %uint %111 2 +%115 = OpIAdd %uint %91 %uint_4 +%116 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %115 +OpStore %116 %112 +%117 = OpIAdd %uint %91 %uint_5 +%118 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %117 +OpStore %118 %113 +%120 = OpIAdd %uint %91 %uint_6 +%121 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %120 +OpStore %121 %114 +%123 = OpIAdd %uint %91 %uint_7 +%124 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %123 +OpStore %124 %79 +%125 = OpIAdd %uint %91 %uint_8 +%126 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %125 +OpStore %126 %80 +%128 = OpIAdd %uint %91 %uint_9 +%129 = OpAccessChain %_ptr_StorageBuffer_uint %86 %uint_1 %128 +OpStore %129 %81 +OpBranch %95 +%95 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs_before + func_before, defs_after + func_after + new_funcs, true, + true, 7u, 23u); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/inst_debug_printf_test.cpp b/third_party/spirv-tools/test/opt/inst_debug_printf_test.cpp new file mode 100644 index 0000000..8123ffb --- /dev/null +++ b/third_party/spirv-tools/test/opt/inst_debug_printf_test.cpp @@ -0,0 +1,215 @@ +// Copyright (c) 2020 Valve Corporation +// Copyright (c) 2020 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Debug Printf Instrumentation Tests. + +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using InstDebugPrintfTest = PassTest<::testing::Test>; + +TEST_F(InstDebugPrintfTest, V4Float32) { + // SamplerState g_sDefault; + // Texture2D g_tColor; + // + // struct PS_INPUT + // { + // float2 vBaseTexCoord : TEXCOORD0; + // }; + // + // struct PS_OUTPUT + // { + // float4 vDiffuse : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT o; + // + // o.vDiffuse.rgba = g_tColor.Sample(g_sDefault, (i.vBaseTexCoord.xy).xy); + // debugPrintfEXT("diffuse: %v4f", o.vDiffuse.rgba); + // return o; + // } + + const std::string defs = + R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.DebugPrintf" +; CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info" +; CHECK-NOT: %1 = OpExtInstImport "NonSemantic.DebugPrintf" +; CHECK: OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "MainPs" %3 %4 +; CHECK: OpEntryPoint Fragment %2 "MainPs" %3 %4 %gl_FragCoord +OpExecutionMode %2 OriginUpperLeft +%5 = OpString "Color is %vn" +)"; + + const std::string decorates = + R"(OpDecorate %6 DescriptorSet 0 +OpDecorate %6 Binding 1 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +OpDecorate %3 Location 0 +OpDecorate %4 Location 0 +; CHECK: OpDecorate %_runtimearr_uint ArrayStride 4 +; CHECK: OpDecorate %_struct_47 Block +; CHECK: OpMemberDecorate %_struct_47 0 Offset 0 +; CHECK: OpMemberDecorate %_struct_47 1 Offset 4 +; CHECK: OpDecorate %49 DescriptorSet 7 +; CHECK: OpDecorate %49 Binding 3 +; CHECK: OpDecorate %gl_FragCoord BuiltIn FragCoord +)"; + + const std::string globals = + R"(%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%13 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 +%6 = OpVariable %_ptr_UniformConstant_13 UniformConstant +%15 = OpTypeSampler +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 +%7 = OpVariable %_ptr_UniformConstant_15 UniformConstant +%17 = OpTypeSampledImage %13 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%3 = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%4 = OpVariable %_ptr_Output_v4float Output +; CHECK: %uint = OpTypeInt 32 0 +; CHECK: %38 = OpTypeFunction %void %uint %uint %uint %uint %uint %uint +; CHECK: %_runtimearr_uint = OpTypeRuntimeArray %uint +; CHECK: %_struct_47 = OpTypeStruct %uint %_runtimearr_uint +; CHECK: %_ptr_StorageBuffer__struct_47 = OpTypePointer StorageBuffer %_struct_47 +; CHECK: %49 = OpVariable %_ptr_StorageBuffer__struct_47 StorageBuffer +; CHECK: %_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +; CHECK: %bool = OpTypeBool +; CHECK: %_ptr_Input_v4float = OpTypePointer Input %v4float +; CHECK: %gl_FragCoord = OpVariable %_ptr_Input_v4float Input +; CHECK: %v4uint = OpTypeVector %uint 4 +)"; + + const std::string main = + R"(%2 = OpFunction %void None %9 +%20 = OpLabel +%21 = OpLoad %v2float %3 +%22 = OpLoad %13 %6 +%23 = OpLoad %15 %7 +%24 = OpSampledImage %17 %22 %23 +%25 = OpImageSampleImplicitLod %v4float %24 %21 +%26 = OpExtInst %void %1 1 %5 %25 +; CHECK-NOT: %26 = OpExtInst %void %1 1 %5 %25 +; CHECK: %29 = OpCompositeExtract %float %25 0 +; CHECK: %30 = OpBitcast %uint %29 +; CHECK: %31 = OpCompositeExtract %float %25 1 +; CHECK: %32 = OpBitcast %uint %31 +; CHECK: %33 = OpCompositeExtract %float %25 2 +; CHECK: %34 = OpBitcast %uint %33 +; CHECK: %35 = OpCompositeExtract %float %25 3 +; CHECK: %36 = OpBitcast %uint %35 +; CHECK: %101 = OpFunctionCall %void %37 %uint_36 %uint_5 %30 %32 %34 %36 +; CHECK: OpBranch %102 +; CHECK: %102 = OpLabel +OpStore %4 %25 +OpReturn +OpFunctionEnd +)"; + + const std::string output_func = + R"(; CHECK: %37 = OpFunction %void None %38 +; CHECK: %39 = OpFunctionParameter %uint +; CHECK: %40 = OpFunctionParameter %uint +; CHECK: %41 = OpFunctionParameter %uint +; CHECK: %42 = OpFunctionParameter %uint +; CHECK: %43 = OpFunctionParameter %uint +; CHECK: %44 = OpFunctionParameter %uint +; CHECK: %45 = OpLabel +; CHECK: %52 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_0 +; CHECK: %55 = OpAtomicIAdd %uint %52 %uint_4 %uint_0 %uint_12 +; CHECK: %56 = OpIAdd %uint %55 %uint_12 +; CHECK: %57 = OpArrayLength %uint %49 1 +; CHECK: %59 = OpULessThanEqual %bool %56 %57 +; CHECK: OpSelectionMerge %60 None +; CHECK: OpBranchConditional %59 %61 %60 +; CHECK: %61 = OpLabel +; CHECK: %62 = OpIAdd %uint %55 %uint_0 +; CHECK: %64 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %62 +; CHECK: OpStore %64 %uint_12 +; CHECK: %66 = OpIAdd %uint %55 %uint_1 +; CHECK: %67 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %66 +; CHECK: OpStore %67 %uint_23 +; CHECK: %69 = OpIAdd %uint %55 %uint_2 +; CHECK: %70 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %69 +; CHECK: OpStore %70 %39 +; CHECK: %72 = OpIAdd %uint %55 %uint_3 +; CHECK: %73 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %72 +; CHECK: OpStore %73 %uint_4 +; CHECK: %76 = OpLoad %v4float %gl_FragCoord +; CHECK: %78 = OpBitcast %v4uint %76 +; CHECK: %79 = OpCompositeExtract %uint %78 0 +; CHECK: %80 = OpIAdd %uint %55 %uint_4 +; CHECK: %81 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %80 +; CHECK: OpStore %81 %79 +; CHECK: %82 = OpCompositeExtract %uint %78 1 +; CHECK: %83 = OpIAdd %uint %55 %uint_5 +; CHECK: %84 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %83 +; CHECK: OpStore %84 %82 +; CHECK: %86 = OpIAdd %uint %55 %uint_7 +; CHECK: %87 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %86 +; CHECK: OpStore %87 %40 +; CHECK: %89 = OpIAdd %uint %55 %uint_8 +; CHECK: %90 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %89 +; CHECK: OpStore %90 %41 +; CHECK: %92 = OpIAdd %uint %55 %uint_9 +; CHECK: %93 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %92 +; CHECK: OpStore %93 %42 +; CHECK: %95 = OpIAdd %uint %55 %uint_10 +; CHECK: %96 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %95 +; CHECK: OpStore %96 %43 +; CHECK: %98 = OpIAdd %uint %55 %uint_11 +; CHECK: %99 = OpAccessChain %_ptr_StorageBuffer_uint %49 %uint_1 %98 +; CHECK: OpStore %99 %44 +; CHECK: OpBranch %60 +; CHECK: %60 = OpLabel +; CHECK: OpReturn +; CHECK: OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch( + defs + decorates + globals + main + output_func, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// Compute shader +// Geometry shader +// Tesselation control shader +// Tesselation eval shader +// Vertex shader + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/instruction_list_test.cpp b/third_party/spirv-tools/test/opt/instruction_list_test.cpp new file mode 100644 index 0000000..e745790 --- /dev/null +++ b/third_party/spirv-tools/test/opt/instruction_list_test.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/instruction.h" +#include "source/opt/instruction_list.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::ContainerEq; +using ::testing::ElementsAre; +using InstructionListTest = ::testing::Test; + +// A class that overrides the destructor, so we can trace it. +class TestInstruction : public Instruction { + public: + TestInstruction() : Instruction() { created_instructions_.push_back(this); } + + ~TestInstruction() { deleted_instructions_.push_back(this); } + + static std::vector created_instructions_; + static std::vector deleted_instructions_; +}; + +std::vector TestInstruction::created_instructions_; +std::vector TestInstruction::deleted_instructions_; + +// Test that the destructor for InstructionList is calling the destructor +// for every element that is in the list. +TEST(InstructionListTest, Destructor) { + InstructionList* list = new InstructionList(); + list->push_back(std::unique_ptr(new Instruction())); + list->push_back(std::unique_ptr(new Instruction())); + delete list; + + // Sorting because we do not care if the order of create and destruction is + // the same. Using generic sort just incase things are changed above. + std::sort(TestInstruction::created_instructions_.begin(), + TestInstruction::created_instructions_.end()); + std::sort(TestInstruction::deleted_instructions_.begin(), + TestInstruction::deleted_instructions_.end()); + EXPECT_THAT(TestInstruction::created_instructions_, + ContainerEq(TestInstruction::deleted_instructions_)); +} + +// Test the |InsertBefore| with a single instruction in the iterator class. +// Need to make sure the elements are inserted in the correct order, and the +// return value points to the correct location. +// +// Comparing addresses to make sure they remain stable, so other data structures +// can have pointers to instructions in InstructionList. +TEST(InstructionListTest, InsertBefore1) { + InstructionList list; + std::vector inserted_instructions; + for (int i = 0; i < 4; i++) { + std::unique_ptr inst(new Instruction()); + inserted_instructions.push_back(inst.get()); + auto new_element = list.end().InsertBefore(std::move(inst)); + EXPECT_EQ(&*new_element, inserted_instructions.back()); + } + + std::vector output; + for (auto& i : list) { + output.push_back(&i); + } + EXPECT_THAT(output, ContainerEq(inserted_instructions)); +} + +// Test inserting an entire vector of instructions using InsertBefore. Checking +// the order of insertion and the return value. +// +// Comparing addresses to make sure they remain stable, so other data structures +// can have pointers to instructions in InstructionList. +TEST(InstructionListTest, InsertBefore2) { + InstructionList list; + std::vector> new_instructions; + std::vector created_instructions; + for (int i = 0; i < 4; i++) { + std::unique_ptr inst(new Instruction()); + created_instructions.push_back(inst.get()); + new_instructions.push_back(std::move(inst)); + } + auto new_element = list.begin().InsertBefore(std::move(new_instructions)); + EXPECT_TRUE(new_instructions.empty()); + EXPECT_EQ(&*new_element, created_instructions.front()); + + std::vector output; + for (auto& i : list) { + output.push_back(&i); + } + EXPECT_THAT(output, ContainerEq(created_instructions)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/instruction_test.cpp b/third_party/spirv-tools/test/opt/instruction_test.cpp new file mode 100644 index 0000000..c5b92ef --- /dev/null +++ b/third_party/spirv-tools/test/opt/instruction_test.cpp @@ -0,0 +1,1536 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::Eq; +using spvtest::MakeInstruction; +using DescriptorTypeTest = PassTest<::testing::Test>; +using OpaqueTypeTest = PassTest<::testing::Test>; +using GetBaseTest = PassTest<::testing::Test>; +using ValidBasePointerTest = PassTest<::testing::Test>; +using VulkanBufferTest = PassTest<::testing::Test>; + +TEST(InstructionTest, CreateTrivial) { + Instruction empty; + EXPECT_EQ(SpvOpNop, empty.opcode()); + EXPECT_EQ(0u, empty.type_id()); + EXPECT_EQ(0u, empty.result_id()); + EXPECT_EQ(0u, empty.NumOperands()); + EXPECT_EQ(0u, empty.NumOperandWords()); + EXPECT_EQ(0u, empty.NumInOperandWords()); + EXPECT_EQ(empty.cend(), empty.cbegin()); + EXPECT_EQ(empty.end(), empty.begin()); +} + +TEST(InstructionTest, CreateWithOpcodeAndNoOperands) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context, SpvOpReturn); + EXPECT_EQ(SpvOpReturn, inst.opcode()); + EXPECT_EQ(0u, inst.type_id()); + EXPECT_EQ(0u, inst.result_id()); + EXPECT_EQ(0u, inst.NumOperands()); + EXPECT_EQ(0u, inst.NumOperandWords()); + EXPECT_EQ(0u, inst.NumInOperandWords()); + EXPECT_EQ(inst.cend(), inst.cbegin()); + EXPECT_EQ(inst.end(), inst.begin()); +} + +TEST(InstructionTest, OperandAsCString) { + Operand::OperandData abcde{0x64636261, 0x65}; + Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde)); + EXPECT_STREQ("abcde", operand.AsCString()); +} + +TEST(InstructionTest, OperandAsString) { + Operand::OperandData abcde{0x64636261, 0x65}; + Operand operand(SPV_OPERAND_TYPE_LITERAL_STRING, std::move(abcde)); + EXPECT_EQ("abcde", operand.AsString()); +} + +TEST(InstructionTest, OperandAsLiteralUint64_32bits) { + Operand::OperandData words{0x1234}; + Operand operand(SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, std::move(words)); + EXPECT_EQ(uint64_t(0x1234), operand.AsLiteralUint64()); +} + +TEST(InstructionTest, OperandAsLiteralUint64_64bits) { + Operand::OperandData words{0x1234, 0x89ab}; + Operand operand(SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, std::move(words)); + EXPECT_EQ((uint64_t(0x89ab) << 32 | 0x1234), operand.AsLiteralUint64()); +} + +// The words for an OpTypeInt for 32-bit signed integer resulting in Id 44. +uint32_t kSampleInstructionWords[] = {(4 << 16) | uint32_t(SpvOpTypeInt), 44, + 32, 1}; +// The operands that would be parsed from kSampleInstructionWords +spv_parsed_operand_t kSampleParsedOperands[] = { + {1, 1, SPV_OPERAND_TYPE_RESULT_ID, SPV_NUMBER_NONE, 0}, + {2, 1, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_NUMBER_UNSIGNED_INT, 32}, + {3, 1, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_NUMBER_UNSIGNED_INT, 1}, +}; + +// A valid parse of kSampleParsedOperands. +spv_parsed_instruction_t kSampleParsedInstruction = {kSampleInstructionWords, + uint16_t(4), + uint16_t(SpvOpTypeInt), + SPV_EXT_INST_TYPE_NONE, + 0, // type id + 44, // result id + kSampleParsedOperands, + 3}; + +// The words for an OpAccessChain instruction. +uint32_t kSampleAccessChainInstructionWords[] = { + (7 << 16) | uint32_t(SpvOpAccessChain), 100, 101, 102, 103, 104, 105}; + +// The operands that would be parsed from kSampleAccessChainInstructionWords. +spv_parsed_operand_t kSampleAccessChainOperands[] = { + {1, 1, SPV_OPERAND_TYPE_RESULT_ID, SPV_NUMBER_NONE, 0}, + {2, 1, SPV_OPERAND_TYPE_TYPE_ID, SPV_NUMBER_NONE, 0}, + {3, 1, SPV_OPERAND_TYPE_ID, SPV_NUMBER_NONE, 0}, + {4, 1, SPV_OPERAND_TYPE_ID, SPV_NUMBER_NONE, 0}, + {5, 1, SPV_OPERAND_TYPE_ID, SPV_NUMBER_NONE, 0}, + {6, 1, SPV_OPERAND_TYPE_ID, SPV_NUMBER_NONE, 0}, +}; + +// A valid parse of kSampleAccessChainInstructionWords +spv_parsed_instruction_t kSampleAccessChainInstruction = { + kSampleAccessChainInstructionWords, + uint16_t(7), + uint16_t(SpvOpAccessChain), + SPV_EXT_INST_TYPE_NONE, + 100, // type id + 101, // result id + kSampleAccessChainOperands, + 6}; + +// The words for an OpControlBarrier instruction. +uint32_t kSampleControlBarrierInstructionWords[] = { + (4 << 16) | uint32_t(SpvOpControlBarrier), 100, 101, 102}; + +// The operands that would be parsed from kSampleControlBarrierInstructionWords. +spv_parsed_operand_t kSampleControlBarrierOperands[] = { + {1, 1, SPV_OPERAND_TYPE_SCOPE_ID, SPV_NUMBER_NONE, 0}, // Execution + {2, 1, SPV_OPERAND_TYPE_SCOPE_ID, SPV_NUMBER_NONE, 0}, // Memory + {3, 1, SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, SPV_NUMBER_NONE, + 0}, // Semantics +}; + +// A valid parse of kSampleControlBarrierInstructionWords +spv_parsed_instruction_t kSampleControlBarrierInstruction = { + kSampleControlBarrierInstructionWords, + uint16_t(4), + uint16_t(SpvOpControlBarrier), + SPV_EXT_INST_TYPE_NONE, + 0, // type id + 0, // result id + kSampleControlBarrierOperands, + 3}; + +TEST(InstructionTest, CreateWithOpcodeAndOperands) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context, kSampleParsedInstruction); + EXPECT_EQ(SpvOpTypeInt, inst.opcode()); + EXPECT_EQ(0u, inst.type_id()); + EXPECT_EQ(44u, inst.result_id()); + EXPECT_EQ(3u, inst.NumOperands()); + EXPECT_EQ(3u, inst.NumOperandWords()); + EXPECT_EQ(2u, inst.NumInOperandWords()); +} + +TEST(InstructionTest, GetOperand) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context, kSampleParsedInstruction); + EXPECT_THAT(inst.GetOperand(0).words, Eq(std::vector{44})); + EXPECT_THAT(inst.GetOperand(1).words, Eq(std::vector{32})); + EXPECT_THAT(inst.GetOperand(2).words, Eq(std::vector{1})); +} + +TEST(InstructionTest, GetInOperand) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context, kSampleParsedInstruction); + EXPECT_THAT(inst.GetInOperand(0).words, Eq(std::vector{32})); + EXPECT_THAT(inst.GetInOperand(1).words, Eq(std::vector{1})); +} + +TEST(InstructionTest, OperandConstIterators) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context, kSampleParsedInstruction); + // Spot check iteration across operands. + auto cbegin = inst.cbegin(); + auto cend = inst.cend(); + EXPECT_NE(cend, inst.cbegin()); + + auto citer = inst.cbegin(); + for (int i = 0; i < 3; ++i, ++citer) { + const auto& operand = *citer; + EXPECT_THAT(operand.type, Eq(kSampleParsedOperands[i].type)); + EXPECT_THAT(operand.words, + Eq(std::vector{kSampleInstructionWords[i + 1]})); + EXPECT_NE(cend, citer); + } + EXPECT_EQ(cend, citer); + + // Check that cbegin and cend have not changed. + EXPECT_EQ(cbegin, inst.cbegin()); + EXPECT_EQ(cend, inst.cend()); + + // Check arithmetic. + const Operand& operand2 = *(inst.cbegin() + 2); + EXPECT_EQ(SPV_OPERAND_TYPE_LITERAL_INTEGER, operand2.type); +} + +TEST(InstructionTest, OperandIterators) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context, kSampleParsedInstruction); + // Spot check iteration across operands, with mutable iterators. + auto begin = inst.begin(); + auto end = inst.end(); + EXPECT_NE(end, inst.begin()); + + auto iter = inst.begin(); + for (int i = 0; i < 3; ++i, ++iter) { + const auto& operand = *iter; + EXPECT_THAT(operand.type, Eq(kSampleParsedOperands[i].type)); + EXPECT_THAT(operand.words, + Eq(std::vector{kSampleInstructionWords[i + 1]})); + EXPECT_NE(end, iter); + } + EXPECT_EQ(end, iter); + + // Check that begin and end have not changed. + EXPECT_EQ(begin, inst.begin()); + EXPECT_EQ(end, inst.end()); + + // Check arithmetic. + Operand& operand2 = *(inst.begin() + 2); + EXPECT_EQ(SPV_OPERAND_TYPE_LITERAL_INTEGER, operand2.type); + + // Check mutation through an iterator. + operand2.type = SPV_OPERAND_TYPE_TYPE_ID; + EXPECT_EQ(SPV_OPERAND_TYPE_TYPE_ID, (*(inst.cbegin() + 2)).type); +} + +TEST(InstructionTest, ForInIdStandardIdTypes) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context, kSampleAccessChainInstruction); + + std::vector ids; + inst.ForEachInId([&ids](const uint32_t* idptr) { ids.push_back(*idptr); }); + EXPECT_THAT(ids, Eq(std::vector{102, 103, 104, 105})); + + ids.clear(); + inst.ForEachInId([&ids](uint32_t* idptr) { ids.push_back(*idptr); }); + EXPECT_THAT(ids, Eq(std::vector{102, 103, 104, 105})); +} + +TEST(InstructionTest, ForInIdNonstandardIdTypes) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context, kSampleControlBarrierInstruction); + + std::vector ids; + inst.ForEachInId([&ids](const uint32_t* idptr) { ids.push_back(*idptr); }); + EXPECT_THAT(ids, Eq(std::vector{100, 101, 102})); + + ids.clear(); + inst.ForEachInId([&ids](uint32_t* idptr) { ids.push_back(*idptr); }); + EXPECT_THAT(ids, Eq(std::vector{100, 101, 102})); +} + +TEST(InstructionTest, UniqueIds) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst1(&context); + Instruction inst2(&context); + EXPECT_NE(inst1.unique_id(), inst2.unique_id()); +} + +TEST(InstructionTest, CloneUniqueIdDifferent) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&context); + std::unique_ptr clone(inst.Clone(&context)); + EXPECT_EQ(inst.context(), clone->context()); + EXPECT_NE(inst.unique_id(), clone->unique_id()); +} + +TEST(InstructionTest, CloneDifferentContext) { + IRContext c1(SPV_ENV_UNIVERSAL_1_2, nullptr); + IRContext c2(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&c1); + std::unique_ptr clone(inst.Clone(&c2)); + EXPECT_EQ(&c1, inst.context()); + EXPECT_EQ(&c2, clone->context()); + EXPECT_NE(&c1, &c2); +} + +TEST(InstructionTest, CloneDifferentContextDifferentUniqueId) { + IRContext c1(SPV_ENV_UNIVERSAL_1_2, nullptr); + IRContext c2(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction inst(&c1); + Instruction other(&c2); + std::unique_ptr clone(inst.Clone(&c2)); + EXPECT_EQ(&c2, clone->context()); + EXPECT_NE(other.unique_id(), clone->unique_id()); +} + +TEST(InstructionTest, EqualsEqualsOperator) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction i1(&context); + Instruction i2(&context); + std::unique_ptr clone(i1.Clone(&context)); + EXPECT_TRUE(i1 == i1); + EXPECT_FALSE(i1 == i2); + EXPECT_FALSE(i1 == *clone); + EXPECT_FALSE(i2 == *clone); +} + +TEST(InstructionTest, LessThanOperator) { + IRContext context(SPV_ENV_UNIVERSAL_1_2, nullptr); + Instruction i1(&context); + Instruction i2(&context); + std::unique_ptr clone(i1.Clone(&context)); + EXPECT_TRUE(i1 < i2); + EXPECT_TRUE(i1 < *clone); + EXPECT_TRUE(i2 < *clone); +} + +TEST_F(DescriptorTypeTest, StorageImage) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "myStorageImage" + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 2 R32f + %8 = OpTypePointer UniformConstant %7 + %3 = OpVariable %8 UniformConstant + %2 = OpFunction %4 None %5 + %9 = OpLabel + %10 = OpCopyObject %8 %3 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* type = context->get_def_use_mgr()->GetDef(8); + EXPECT_TRUE(type->IsVulkanStorageImage()); + EXPECT_FALSE(type->IsVulkanSampledImage()); + EXPECT_FALSE(type->IsVulkanStorageTexelBuffer()); + EXPECT_FALSE(type->IsVulkanStorageBuffer()); + EXPECT_FALSE(type->IsVulkanUniformBuffer()); + + Instruction* variable = context->get_def_use_mgr()->GetDef(3); + EXPECT_FALSE(variable->IsReadOnlyPointer()); + + Instruction* object_copy = context->get_def_use_mgr()->GetDef(10); + EXPECT_FALSE(object_copy->IsReadOnlyPointer()); +} + +TEST_F(DescriptorTypeTest, SampledImage) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "myStorageImage" + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypePointer UniformConstant %7 + %3 = OpVariable %8 UniformConstant + %2 = OpFunction %4 None %5 + %9 = OpLabel + %10 = OpCopyObject %8 %3 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* type = context->get_def_use_mgr()->GetDef(8); + EXPECT_FALSE(type->IsVulkanStorageImage()); + EXPECT_TRUE(type->IsVulkanSampledImage()); + EXPECT_FALSE(type->IsVulkanStorageTexelBuffer()); + EXPECT_FALSE(type->IsVulkanStorageBuffer()); + EXPECT_FALSE(type->IsVulkanUniformBuffer()); + + Instruction* variable = context->get_def_use_mgr()->GetDef(3); + EXPECT_TRUE(variable->IsReadOnlyPointer()); + + Instruction* object_copy = context->get_def_use_mgr()->GetDef(10); + EXPECT_TRUE(object_copy->IsReadOnlyPointer()); +} + +TEST_F(DescriptorTypeTest, StorageTexelBuffer) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "myStorageImage" + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 Buffer 0 0 0 2 R32f + %8 = OpTypePointer UniformConstant %7 + %3 = OpVariable %8 UniformConstant + %2 = OpFunction %4 None %5 + %9 = OpLabel + %10 = OpCopyObject %8 %3 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* type = context->get_def_use_mgr()->GetDef(8); + EXPECT_FALSE(type->IsVulkanStorageImage()); + EXPECT_FALSE(type->IsVulkanSampledImage()); + EXPECT_TRUE(type->IsVulkanStorageTexelBuffer()); + EXPECT_FALSE(type->IsVulkanStorageBuffer()); + EXPECT_FALSE(type->IsVulkanUniformBuffer()); + + Instruction* variable = context->get_def_use_mgr()->GetDef(3); + EXPECT_FALSE(variable->IsReadOnlyPointer()); + + Instruction* object_copy = context->get_def_use_mgr()->GetDef(10); + EXPECT_FALSE(object_copy->IsReadOnlyPointer()); +} + +TEST_F(DescriptorTypeTest, StorageBuffer) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "myStorageImage" + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + OpDecorate %9 BufferBlock + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeRuntimeArray %7 + %9 = OpTypeStruct %8 + %10 = OpTypePointer Uniform %9 + %3 = OpVariable %10 Uniform + %2 = OpFunction %4 None %5 + %11 = OpLabel + %12 = OpCopyObject %8 %3 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* type = context->get_def_use_mgr()->GetDef(10); + EXPECT_FALSE(type->IsVulkanStorageImage()); + EXPECT_FALSE(type->IsVulkanSampledImage()); + EXPECT_FALSE(type->IsVulkanStorageTexelBuffer()); + EXPECT_TRUE(type->IsVulkanStorageBuffer()); + EXPECT_FALSE(type->IsVulkanUniformBuffer()); + + Instruction* variable = context->get_def_use_mgr()->GetDef(3); + EXPECT_FALSE(variable->IsReadOnlyPointer()); + + Instruction* object_copy = context->get_def_use_mgr()->GetDef(12); + EXPECT_FALSE(object_copy->IsReadOnlyPointer()); +} + +TEST_F(DescriptorTypeTest, UniformBuffer) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "myStorageImage" + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + OpDecorate %9 Block + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeRuntimeArray %7 + %9 = OpTypeStruct %8 + %10 = OpTypePointer Uniform %9 + %3 = OpVariable %10 Uniform + %2 = OpFunction %4 None %5 + %11 = OpLabel + %12 = OpCopyObject %10 %3 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* type = context->get_def_use_mgr()->GetDef(10); + EXPECT_FALSE(type->IsVulkanStorageImage()); + EXPECT_FALSE(type->IsVulkanSampledImage()); + EXPECT_FALSE(type->IsVulkanStorageTexelBuffer()); + EXPECT_FALSE(type->IsVulkanStorageBuffer()); + EXPECT_TRUE(type->IsVulkanUniformBuffer()); + + Instruction* variable = context->get_def_use_mgr()->GetDef(3); + EXPECT_TRUE(variable->IsReadOnlyPointer()); + + Instruction* object_copy = context->get_def_use_mgr()->GetDef(12); + EXPECT_TRUE(object_copy->IsReadOnlyPointer()); +} + +TEST_F(DescriptorTypeTest, NonWritableIsReadOnly) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "myStorageImage" + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + OpDecorate %9 BufferBlock + OpDecorate %3 NonWritable + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeRuntimeArray %7 + %9 = OpTypeStruct %8 + %10 = OpTypePointer Uniform %9 + %3 = OpVariable %10 Uniform + %2 = OpFunction %4 None %5 + %11 = OpLabel + %12 = OpCopyObject %8 %3 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* variable = context->get_def_use_mgr()->GetDef(3); + EXPECT_TRUE(variable->IsReadOnlyPointer()); + + // This demonstrates that the check for whether a pointer is read-only is not + // precise: copying a NonWritable-decorated variable can yield a pointer that + // the check does not regard as read-only. + Instruction* object_copy = context->get_def_use_mgr()->GetDef(12); + EXPECT_FALSE(object_copy->IsReadOnlyPointer()); +} + +TEST_F(DescriptorTypeTest, AccessChainIntoReadOnlyStructIsReadOnly) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 320 + OpMemberDecorate %3 0 Offset 0 + OpMemberDecorate %3 1 Offset 4 + OpDecorate %3 Block + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %3 = OpTypeStruct %6 %8 + %9 = OpTypePointer PushConstant %3 + %10 = OpVariable %9 PushConstant + %11 = OpConstant %6 0 + %12 = OpTypePointer PushConstant %6 + %13 = OpConstant %6 1 + %14 = OpTypePointer PushConstant %8 + %2 = OpFunction %4 None %5 + %15 = OpLabel + %16 = OpVariable %7 Function + %17 = OpAccessChain %12 %10 %11 + %18 = OpAccessChain %14 %10 %13 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + Instruction* push_constant_struct_variable = + context->get_def_use_mgr()->GetDef(10); + EXPECT_TRUE(push_constant_struct_variable->IsReadOnlyPointer()); + + Instruction* push_constant_struct_field_0 = + context->get_def_use_mgr()->GetDef(17); + EXPECT_TRUE(push_constant_struct_field_0->IsReadOnlyPointer()); + + Instruction* push_constant_struct_field_1 = + context->get_def_use_mgr()->GetDef(18); + EXPECT_TRUE(push_constant_struct_field_1->IsReadOnlyPointer()); +} + +TEST_F(DescriptorTypeTest, ReadOnlyPointerParameter) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 320 + OpMemberDecorate %3 0 Offset 0 + OpMemberDecorate %3 1 Offset 4 + OpDecorate %3 Block + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %3 = OpTypeStruct %6 %8 + %9 = OpTypePointer PushConstant %3 + %10 = OpVariable %9 PushConstant + %11 = OpConstant %6 0 + %12 = OpTypePointer PushConstant %6 + %13 = OpConstant %6 1 + %14 = OpTypePointer PushConstant %8 + %15 = OpTypeFunction %4 %9 + %2 = OpFunction %4 None %5 + %16 = OpLabel + %17 = OpVariable %7 Function + %18 = OpAccessChain %12 %10 %11 + %19 = OpAccessChain %14 %10 %13 + OpReturn + OpFunctionEnd + %20 = OpFunction %4 None %15 + %21 = OpFunctionParameter %9 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + Instruction* push_constant_struct_parameter = + context->get_def_use_mgr()->GetDef(21); + EXPECT_TRUE(push_constant_struct_parameter->IsReadOnlyPointer()); +} + +TEST_F(OpaqueTypeTest, BaseOpaqueTypesShader) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypeImage %5 2D 1 0 0 1 Unknown + %7 = OpTypeSampler + %8 = OpTypeSampledImage %6 + %9 = OpTypeRuntimeArray %5 + %2 = OpFunction %3 None %4 + %10 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* image_type = context->get_def_use_mgr()->GetDef(6); + EXPECT_TRUE(image_type->IsOpaqueType()); + Instruction* sampler_type = context->get_def_use_mgr()->GetDef(7); + EXPECT_TRUE(sampler_type->IsOpaqueType()); + Instruction* sampled_image_type = context->get_def_use_mgr()->GetDef(8); + EXPECT_TRUE(sampled_image_type->IsOpaqueType()); + Instruction* runtime_array_type = context->get_def_use_mgr()->GetDef(9); + EXPECT_TRUE(runtime_array_type->IsOpaqueType()); + Instruction* float_type = context->get_def_use_mgr()->GetDef(5); + EXPECT_FALSE(float_type->IsOpaqueType()); + Instruction* void_type = context->get_def_use_mgr()->GetDef(3); + EXPECT_FALSE(void_type->IsOpaqueType()); +} + +TEST_F(OpaqueTypeTest, OpaqueStructTypes) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypeRuntimeArray %5 + %7 = OpTypeStruct %6 %6 + %8 = OpTypeStruct %5 %6 + %9 = OpTypeStruct %6 %5 + %10 = OpTypeStruct %7 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + for (int i = 7; i <= 10; i++) { + Instruction* type = context->get_def_use_mgr()->GetDef(i); + EXPECT_TRUE(type->IsOpaqueType()); + } +} + +TEST_F(GetBaseTest, SampleImage) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "myStorageImage" + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeVector %6 4 + %9 = OpConstant %6 0 + %10 = OpConstantComposite %7 %9 %9 + %11 = OpTypeImage %6 2D 0 0 0 1 R32f + %12 = OpTypePointer UniformConstant %11 + %3 = OpVariable %12 UniformConstant + %13 = OpTypeSampledImage %11 + %14 = OpTypeSampler + %15 = OpTypePointer UniformConstant %14 + %16 = OpVariable %15 UniformConstant + %2 = OpFunction %4 None %5 + %17 = OpLabel + %18 = OpLoad %11 %3 + %19 = OpLoad %14 %16 + %20 = OpSampledImage %13 %18 %19 + %21 = OpImageSampleImplicitLod %8 %20 %10 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* load = context->get_def_use_mgr()->GetDef(21); + Instruction* base = context->get_def_use_mgr()->GetDef(20); + EXPECT_TRUE(load->GetBaseAddress() == base); +} + +TEST_F(GetBaseTest, PtrAccessChain) { + const std::string text = R"( + OpCapability VariablePointers + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "PSMain" %2 + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 8388353 + %int_0 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %2 = OpVariable %_ptr_Function_v4float Input + %1 = OpFunction %void None %4 + %10 = OpLabel + %11 = OpPtrAccessChain %_ptr_Function_v4float %2 %int_0 + %12 = OpLoad %v4float %11 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* load = context->get_def_use_mgr()->GetDef(12); + Instruction* base = context->get_def_use_mgr()->GetDef(2); + EXPECT_TRUE(load->GetBaseAddress() == base); +} + +TEST_F(GetBaseTest, ImageRead) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "myStorageImage" + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 0 + %7 = OpTypeVector %6 2 + %8 = OpConstant %6 0 + %9 = OpConstantComposite %7 %8 %8 + %10 = OpTypeImage %6 2D 0 0 0 2 R32f + %11 = OpTypePointer UniformConstant %10 + %3 = OpVariable %11 UniformConstant + %2 = OpFunction %4 None %5 + %12 = OpLabel + %13 = OpLoad %10 %3 + %14 = OpImageRead %6 %13 %9 + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + Instruction* load = context->get_def_use_mgr()->GetDef(14); + Instruction* base = context->get_def_use_mgr()->GetDef(13); + EXPECT_TRUE(load->GetBaseAddress() == base); +} + +TEST_F(ValidBasePointerTest, OpSelectBadNoVariablePointersStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer StorageBuffer %3 +%5 = OpVariable %4 StorageBuffer +%6 = OpTypeFunction %2 +%7 = OpTypeBool +%8 = OpConstantTrue %7 +%1 = OpFunction %2 None %6 +%9 = OpLabel +%10 = OpSelect %4 %8 %5 %5 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* select = context->get_def_use_mgr()->GetDef(10); + EXPECT_NE(select, nullptr); + EXPECT_FALSE(select->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpSelectBadNoVariablePointers) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Workgroup %3 +%5 = OpVariable %4 Workgroup +%6 = OpTypeFunction %2 +%7 = OpTypeBool +%8 = OpConstantTrue %7 +%1 = OpFunction %2 None %6 +%9 = OpLabel +%10 = OpSelect %4 %8 %5 %5 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* select = context->get_def_use_mgr()->GetDef(10); + EXPECT_NE(select, nullptr); + EXPECT_FALSE(select->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpSelectGoodVariablePointersStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer StorageBuffer %3 +%5 = OpVariable %4 StorageBuffer +%6 = OpTypeFunction %2 +%7 = OpTypeBool +%8 = OpConstantTrue %7 +%1 = OpFunction %2 None %6 +%9 = OpLabel +%10 = OpSelect %4 %8 %5 %5 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* select = context->get_def_use_mgr()->GetDef(10); + EXPECT_NE(select, nullptr); + EXPECT_TRUE(select->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpSelectGoodVariablePointers) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Workgroup %3 +%5 = OpVariable %4 Workgroup +%6 = OpTypeFunction %2 +%7 = OpTypeBool +%8 = OpConstantTrue %7 +%1 = OpFunction %2 None %6 +%9 = OpLabel +%10 = OpSelect %4 %8 %5 %5 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* select = context->get_def_use_mgr()->GetDef(10); + EXPECT_NE(select, nullptr); + EXPECT_TRUE(select->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpConstantNullBadNoVariablePointersStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer StorageBuffer %3 +%5 = OpConstantNull %4 +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* null_inst = context->get_def_use_mgr()->GetDef(5); + EXPECT_NE(null_inst, nullptr); + EXPECT_FALSE(null_inst->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpConstantNullBadNoVariablePointers) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Workgroup %3 +%5 = OpConstantNull %4 +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* null_inst = context->get_def_use_mgr()->GetDef(5); + EXPECT_NE(null_inst, nullptr); + EXPECT_FALSE(null_inst->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpConstantNullGoodVariablePointersStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer StorageBuffer %3 +%5 = OpConstantNull %4 +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* null_inst = context->get_def_use_mgr()->GetDef(5); + EXPECT_NE(null_inst, nullptr); + EXPECT_TRUE(null_inst->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpConstantNullGoodVariablePointers) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Workgroup %3 +%5 = OpConstantNull %4 +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* null_inst = context->get_def_use_mgr()->GetDef(5); + EXPECT_NE(null_inst, nullptr); + EXPECT_TRUE(null_inst->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpPhiBadNoVariablePointersStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer StorageBuffer %3 +%5 = OpVariable %4 StorageBuffer +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +%9 = OpPhi %4 %5 %7 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* phi = context->get_def_use_mgr()->GetDef(9); + EXPECT_NE(phi, nullptr); + EXPECT_FALSE(phi->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpPhiBadNoVariablePointers) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Workgroup %3 +%5 = OpVariable %4 Workgroup +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +%9 = OpPhi %4 %5 %7 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* phi = context->get_def_use_mgr()->GetDef(9); + EXPECT_NE(phi, nullptr); + EXPECT_FALSE(phi->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpPhiGoodVariablePointersStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer StorageBuffer %3 +%5 = OpVariable %4 StorageBuffer +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +%9 = OpPhi %4 %5 %7 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* phi = context->get_def_use_mgr()->GetDef(9); + EXPECT_NE(phi, nullptr); + EXPECT_TRUE(phi->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpPhiGoodVariablePointers) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Workgroup %3 +%5 = OpVariable %4 Workgroup +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +%9 = OpPhi %4 %5 %7 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* phi = context->get_def_use_mgr()->GetDef(9); + EXPECT_NE(phi, nullptr); + EXPECT_TRUE(phi->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpFunctionCallBadNoVariablePointersStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer StorageBuffer %3 +%5 = OpConstantNull %4 +%6 = OpTypeFunction %2 +%7 = OpTypeFunction %4 +%1 = OpFunction %2 None %6 +%8 = OpLabel +%9 = OpFunctionCall %4 %10 +OpReturn +OpFunctionEnd +%10 = OpFunction %4 None %7 +%11 = OpLabel +OpReturnValue %5 +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* null_inst = context->get_def_use_mgr()->GetDef(9); + EXPECT_NE(null_inst, nullptr); + EXPECT_FALSE(null_inst->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpFunctionCallBadNoVariablePointers) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Workgroup %3 +%5 = OpConstantNull %4 +%6 = OpTypeFunction %2 +%7 = OpTypeFunction %4 +%1 = OpFunction %2 None %6 +%8 = OpLabel +%9 = OpFunctionCall %4 %10 +OpReturn +OpFunctionEnd +%10 = OpFunction %4 None %7 +%11 = OpLabel +OpReturnValue %5 +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* null_inst = context->get_def_use_mgr()->GetDef(9); + EXPECT_NE(null_inst, nullptr); + EXPECT_FALSE(null_inst->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpFunctionCallGoodVariablePointersStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer StorageBuffer %3 +%5 = OpConstantNull %4 +%6 = OpTypeFunction %2 +%7 = OpTypeFunction %4 +%1 = OpFunction %2 None %6 +%8 = OpLabel +%9 = OpFunctionCall %4 %10 +OpReturn +OpFunctionEnd +%10 = OpFunction %4 None %7 +%11 = OpLabel +OpReturnValue %5 +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* null_inst = context->get_def_use_mgr()->GetDef(9); + EXPECT_NE(null_inst, nullptr); + EXPECT_TRUE(null_inst->IsValidBasePointer()); +} + +TEST_F(ValidBasePointerTest, OpFunctionCallGoodVariablePointers) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Workgroup %3 +%5 = OpConstantNull %4 +%6 = OpTypeFunction %2 +%7 = OpTypeFunction %4 +%1 = OpFunction %2 None %6 +%8 = OpLabel +%9 = OpFunctionCall %4 %10 +OpReturn +OpFunctionEnd +%10 = OpFunction %4 None %7 +%11 = OpLabel +OpReturnValue %5 +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + Instruction* null_inst = context->get_def_use_mgr()->GetDef(9); + EXPECT_NE(null_inst, nullptr); + EXPECT_TRUE(null_inst->IsValidBasePointer()); +} + +TEST_F(VulkanBufferTest, VulkanStorageBuffer) { + const std::string text = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArray +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpExecutionMode %1 LocalSize 1 1 1 +OpDecorate %2 Block +OpMemberDecorate %2 0 Offset 0 +OpDecorate %3 BufferBlock +OpMemberDecorate %3 0 Offset 0 +%4 = OpTypeVoid +%5 = OpTypeInt 32 0 +%2 = OpTypeStruct %5 +%3 = OpTypeStruct %5 + +%6 = OpTypePointer StorageBuffer %2 +%7 = OpTypePointer Uniform %2 +%8 = OpTypePointer Uniform %3 + +%9 = OpConstant %5 1 +%10 = OpTypeArray %2 %9 +%11 = OpTypeArray %3 %9 +%12 = OpTypePointer StorageBuffer %10 +%13 = OpTypePointer Uniform %10 +%14 = OpTypePointer Uniform %11 + +%15 = OpTypeRuntimeArray %2 +%16 = OpTypeRuntimeArray %3 +%17 = OpTypePointer StorageBuffer %15 +%18 = OpTypePointer Uniform %15 +%19 = OpTypePointer Uniform %16 + +%50 = OpTypeFunction %4 +%1 = OpFunction %4 None %50 +%51 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + + // Standard SSBO and UBO + Instruction* inst = context->get_def_use_mgr()->GetDef(6); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(7); + EXPECT_EQ(false, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(8); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + + // Arrayed SSBO and UBO + inst = context->get_def_use_mgr()->GetDef(12); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(13); + EXPECT_EQ(false, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(14); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + + // Runtime arrayed SSBO and UBO + inst = context->get_def_use_mgr()->GetDef(17); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(false, inst->IsVulkanStorageBuffer()); + inst = context->get_def_use_mgr()->GetDef(19); + EXPECT_EQ(true, inst->IsVulkanStorageBuffer()); +} + +TEST_F(VulkanBufferTest, VulkanUniformBuffer) { + const std::string text = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArray +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpExecutionMode %1 LocalSize 1 1 1 +OpDecorate %2 Block +OpMemberDecorate %2 0 Offset 0 +OpDecorate %3 BufferBlock +OpMemberDecorate %3 0 Offset 0 +%4 = OpTypeVoid +%5 = OpTypeInt 32 0 +%2 = OpTypeStruct %5 +%3 = OpTypeStruct %5 + +%6 = OpTypePointer StorageBuffer %2 +%7 = OpTypePointer Uniform %2 +%8 = OpTypePointer Uniform %3 + +%9 = OpConstant %5 1 +%10 = OpTypeArray %2 %9 +%11 = OpTypeArray %3 %9 +%12 = OpTypePointer StorageBuffer %10 +%13 = OpTypePointer Uniform %10 +%14 = OpTypePointer Uniform %11 + +%15 = OpTypeRuntimeArray %2 +%16 = OpTypeRuntimeArray %3 +%17 = OpTypePointer StorageBuffer %15 +%18 = OpTypePointer Uniform %15 +%19 = OpTypePointer Uniform %16 + +%50 = OpTypeFunction %4 +%1 = OpFunction %4 None %50 +%51 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + + // Standard SSBO and UBO + Instruction* inst = context->get_def_use_mgr()->GetDef(6); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(7); + EXPECT_EQ(true, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(8); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + + // Arrayed SSBO and UBO + inst = context->get_def_use_mgr()->GetDef(12); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(13); + EXPECT_EQ(true, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(14); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + + // Runtime arrayed SSBO and UBO + inst = context->get_def_use_mgr()->GetDef(17); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(true, inst->IsVulkanUniformBuffer()); + inst = context->get_def_use_mgr()->GetDef(19); + EXPECT_EQ(false, inst->IsVulkanUniformBuffer()); +} + +TEST_F(VulkanBufferTest, ImageQueries) { + const std::string text = R"( +OpCapability Shader +OpCapability ImageBuffer +OpCapability RuntimeDescriptorArray +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpExecutionMode %1 LocalSize 1 1 1 +%2 = OpTypeVoid +%3 = OpTypeFloat 32 + +%4 = OpTypeImage %3 Buffer 0 0 0 1 Rgba32f +%5 = OpTypeImage %3 Buffer 0 0 0 2 Rgba32f +%6 = OpTypeImage %3 2D 0 0 0 1 Rgba32f +%7 = OpTypeImage %3 2D 0 0 0 2 Rgba32f + +%8 = OpTypePointer UniformConstant %4 +%9 = OpTypePointer UniformConstant %5 +%10 = OpTypePointer UniformConstant %6 +%11 = OpTypePointer UniformConstant %7 + +%12 = OpTypeInt 32 0 +%13 = OpConstant %12 1 +%14 = OpTypeArray %4 %13 +%15 = OpTypeArray %5 %13 +%16 = OpTypeArray %6 %13 +%17 = OpTypeArray %7 %13 +%18 = OpTypePointer UniformConstant %14 +%19 = OpTypePointer UniformConstant %15 +%20 = OpTypePointer UniformConstant %16 +%21 = OpTypePointer UniformConstant %17 + +%22 = OpTypeRuntimeArray %4 +%23 = OpTypeRuntimeArray %5 +%24 = OpTypeRuntimeArray %6 +%25 = OpTypeRuntimeArray %7 +%26 = OpTypePointer UniformConstant %22 +%27 = OpTypePointer UniformConstant %23 +%28 = OpTypePointer UniformConstant %24 +%29 = OpTypePointer UniformConstant %25 + +%50 = OpTypeFunction %4 +%1 = OpFunction %4 None %50 +%51 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text); + EXPECT_NE(context, nullptr); + + // Bare pointers + Instruction* inst = context->get_def_use_mgr()->GetDef(8); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(9); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(true, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(11); + EXPECT_EQ(true, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + // Array pointers + inst = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(19); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(20); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(true, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(21); + EXPECT_EQ(true, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + // Runtime array pointers + inst = context->get_def_use_mgr()->GetDef(26); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(27); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(true, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(28); + EXPECT_EQ(false, inst->IsVulkanStorageImage()); + EXPECT_EQ(true, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); + + inst = context->get_def_use_mgr()->GetDef(29); + EXPECT_EQ(true, inst->IsVulkanStorageImage()); + EXPECT_EQ(false, inst->IsVulkanSampledImage()); + EXPECT_EQ(false, inst->IsVulkanStorageTexelBuffer()); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/ir_builder.cpp b/third_party/spirv-tools/test/opt/ir_builder.cpp new file mode 100644 index 0000000..cb234e0 --- /dev/null +++ b/third_party/spirv-tools/test/opt/ir_builder.cpp @@ -0,0 +1,439 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/basic_block.h" +#include "source/opt/build_module.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/type_manager.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { +namespace { + +using Analysis = IRContext::Analysis; +using IRBuilderTest = ::testing::Test; + +bool Validate(const std::vector& bin) { + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + spv_context spvContext = spvContextCreate(target_env); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {bin.data(), bin.size()}; + spv_result_t error = spvValidate(spvContext, &binary, &diagnostic); + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + return error == 0; +} + +void Match(const std::string& original, IRContext* context, + bool do_validation = true) { + std::vector bin; + context->module()->ToBinary(&bin, true); + if (do_validation) { + EXPECT_TRUE(Validate(bin)); + } + std::string assembly; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_2); + EXPECT_TRUE( + tools.Disassemble(bin, &assembly, SpirvTools::kDefaultDisassembleOption)) + << "Disassembling failed for shader:\n" + << assembly << std::endl; + auto match_result = effcee::Match(assembly, original); + EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) + << match_result.message() << "\nChecking result:\n" + << assembly; +} + +TEST_F(IRBuilderTest, TestInsnAddition) { + const std::string text = R"( +; CHECK: %18 = OpLabel +; CHECK: OpPhi %int %int_0 %14 +; CHECK: OpPhi %bool %16 %14 +; CHECK: OpBranch %17 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %4 "i" + OpName %3 "c" + OpDecorate %3 Location 0 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 0 + %10 = OpTypeBool + %11 = OpTypeFloat 32 + %12 = OpTypeVector %11 4 + %13 = OpTypePointer Output %12 + %3 = OpVariable %13 Output + %2 = OpFunction %5 None %6 + %14 = OpLabel + %4 = OpVariable %8 Function + OpStore %4 %9 + %15 = OpLoad %7 %4 + %16 = OpINotEqual %10 %15 %9 + OpSelectionMerge %17 None + OpBranchConditional %16 %18 %17 + %18 = OpLabel + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd +)"; + + { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + BasicBlock* bb = context->cfg()->block(18); + + // Build managers. + context->get_def_use_mgr(); + context->get_instr_block(nullptr); + + InstructionBuilder builder(context.get(), &*bb->begin()); + Instruction* phi1 = builder.AddPhi(7, {9, 14}); + Instruction* phi2 = builder.AddPhi(10, {16, 14}); + + // Make sure the InstructionBuilder did not update the def/use manager. + EXPECT_EQ(context->get_def_use_mgr()->GetDef(phi1->result_id()), nullptr); + EXPECT_EQ(context->get_def_use_mgr()->GetDef(phi2->result_id()), nullptr); + EXPECT_EQ(context->get_instr_block(phi1), nullptr); + EXPECT_EQ(context->get_instr_block(phi2), nullptr); + + Match(text, context.get()); + } + + { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Build managers. + context->get_def_use_mgr(); + context->get_instr_block(nullptr); + + BasicBlock* bb = context->cfg()->block(18); + InstructionBuilder builder( + context.get(), &*bb->begin(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* phi1 = builder.AddPhi(7, {9, 14}); + Instruction* phi2 = builder.AddPhi(10, {16, 14}); + + // Make sure InstructionBuilder updated the def/use manager + EXPECT_NE(context->get_def_use_mgr()->GetDef(phi1->result_id()), nullptr); + EXPECT_NE(context->get_def_use_mgr()->GetDef(phi2->result_id()), nullptr); + EXPECT_NE(context->get_instr_block(phi1), nullptr); + EXPECT_NE(context->get_instr_block(phi2), nullptr); + + Match(text, context.get()); + } +} + +TEST_F(IRBuilderTest, TestCondBranchAddition) { + const std::string text = R"( +; CHECK: %main = OpFunction %void None %6 +; CHECK-NEXT: %15 = OpLabel +; CHECK-NEXT: OpSelectionMerge %13 None +; CHECK-NEXT: OpBranchConditional %true %14 %13 +; CHECK-NEXT: %14 = OpLabel +; CHECK-NEXT: OpBranch %13 +; CHECK-NEXT: %13 = OpLabel +; CHECK-NEXT: OpReturn + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %4 "i" + OpName %3 "c" + OpDecorate %3 Location 0 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeBool + %8 = OpTypePointer Private %7 + %9 = OpConstantTrue %7 + %10 = OpTypeFloat 32 + %11 = OpTypeVector %10 4 + %12 = OpTypePointer Output %11 + %3 = OpVariable %12 Output + %4 = OpVariable %8 Private + %2 = OpFunction %5 None %6 + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + + { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + Function& fn = *context->module()->begin(); + + BasicBlock& bb_merge = *fn.begin(); + + // TODO(1841): Handle id overflow. + fn.begin().InsertBefore(std::unique_ptr( + new BasicBlock(std::unique_ptr(new Instruction( + context.get(), SpvOpLabel, 0, context->TakeNextId(), {}))))); + BasicBlock& bb_true = *fn.begin(); + { + InstructionBuilder builder(context.get(), &*bb_true.begin()); + builder.AddBranch(bb_merge.id()); + } + + // TODO(1841): Handle id overflow. + fn.begin().InsertBefore(std::unique_ptr( + new BasicBlock(std::unique_ptr(new Instruction( + context.get(), SpvOpLabel, 0, context->TakeNextId(), {}))))); + BasicBlock& bb_cond = *fn.begin(); + + InstructionBuilder builder(context.get(), &bb_cond); + // This also test consecutive instruction insertion: merge selection + + // branch. + builder.AddConditionalBranch(9, bb_true.id(), bb_merge.id(), bb_merge.id()); + + Match(text, context.get()); + } +} + +TEST_F(IRBuilderTest, AddSelect) { + const std::string text = R"( +; CHECK: [[bool:%\w+]] = OpTypeBool +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[true:%\w+]] = OpConstantTrue [[bool]] +; CHECK: [[u0:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[u1:%\w+]] = OpConstant [[uint]] 1 +; CHECK: OpSelect [[uint]] [[true]] [[u0]] [[u1]] +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%1 = OpTypeVoid +%2 = OpTypeBool +%3 = OpTypeInt 32 0 +%4 = OpConstantTrue %2 +%5 = OpConstant %3 0 +%6 = OpConstant %3 1 +%7 = OpTypeFunction %1 +%8 = OpFunction %1 None %7 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(nullptr, context); + + InstructionBuilder builder(context.get(), + &*context->module()->begin()->begin()->begin()); + EXPECT_NE(nullptr, builder.AddSelect(3u, 4u, 5u, 6u)); + + Match(text, context.get()); +} + +TEST_F(IRBuilderTest, AddCompositeConstruct) { + const std::string text = R"( +; CHECK: [[uint:%\w+]] = OpTypeInt +; CHECK: [[u0:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[u1:%\w+]] = OpConstant [[uint]] 1 +; CHECK: [[struct:%\w+]] = OpTypeStruct [[uint]] [[uint]] [[uint]] [[uint]] +; CHECK: OpCompositeConstruct [[struct]] [[u0]] [[u1]] [[u1]] [[u0]] +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpConstant %2 1 +%5 = OpTypeStruct %2 %2 %2 %2 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(nullptr, context); + + InstructionBuilder builder(context.get(), + &*context->module()->begin()->begin()->begin()); + std::vector ids = {3u, 4u, 4u, 3u}; + EXPECT_NE(nullptr, builder.AddCompositeConstruct(5u, ids)); + + Match(text, context.get()); +} + +TEST_F(IRBuilderTest, ConstantAdder) { + const std::string text = R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: OpConstant [[uint]] 13 +; CHECK: [[sint:%\w+]] = OpTypeInt 32 1 +; CHECK: OpConstant [[sint]] -1 +; CHECK: OpConstant [[uint]] 1 +; CHECK: OpConstant [[sint]] 34 +; CHECK: OpConstant [[uint]] 0 +; CHECK: OpConstant [[sint]] 0 +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(nullptr, context); + + InstructionBuilder builder(context.get(), + &*context->module()->begin()->begin()->begin()); + EXPECT_NE(nullptr, builder.GetUintConstant(13)); + EXPECT_NE(nullptr, builder.GetSintConstant(-1)); + + // Try adding the same constants again to make sure they aren't added. + EXPECT_NE(nullptr, builder.GetUintConstant(13)); + EXPECT_NE(nullptr, builder.GetSintConstant(-1)); + + // Try adding different constants to make sure the type is reused. + EXPECT_NE(nullptr, builder.GetUintConstant(1)); + EXPECT_NE(nullptr, builder.GetSintConstant(34)); + + // Try adding 0 as both signed and unsigned. + EXPECT_NE(nullptr, builder.GetUintConstant(0)); + EXPECT_NE(nullptr, builder.GetSintConstant(0)); + + Match(text, context.get()); +} + +TEST_F(IRBuilderTest, ConstantAdderTypeAlreadyExists) { + const std::string text = R"( +; CHECK: OpConstant %uint 13 +; CHECK: OpConstant %int -1 +; CHECK: OpConstant %uint 1 +; CHECK: OpConstant %int 34 +; CHECK: OpConstant %uint 0 +; CHECK: OpConstant %int 0 +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%uint = OpTypeInt 32 0 +%int = OpTypeInt 32 1 +%4 = OpTypeFunction %1 +%5 = OpFunction %1 None %4 +%6 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(nullptr, context); + + InstructionBuilder builder(context.get(), + &*context->module()->begin()->begin()->begin()); + Instruction* const_1 = builder.GetUintConstant(13); + Instruction* const_2 = builder.GetSintConstant(-1); + + EXPECT_NE(nullptr, const_1); + EXPECT_NE(nullptr, const_2); + + // Try adding the same constants again to make sure they aren't added. + EXPECT_EQ(const_1, builder.GetUintConstant(13)); + EXPECT_EQ(const_2, builder.GetSintConstant(-1)); + + Instruction* const_3 = builder.GetUintConstant(1); + Instruction* const_4 = builder.GetSintConstant(34); + + // Try adding different constants to make sure the type is reused. + EXPECT_NE(nullptr, const_3); + EXPECT_NE(nullptr, const_4); + + Instruction* const_5 = builder.GetUintConstant(0); + Instruction* const_6 = builder.GetSintConstant(0); + + // Try adding 0 as both signed and unsigned. + EXPECT_NE(nullptr, const_5); + EXPECT_NE(nullptr, const_6); + + // They have the same value but different types so should be unique. + EXPECT_NE(const_5, const_6); + + // Check the types are correct. + uint32_t type_id_unsigned = const_1->GetSingleWordOperand(0); + uint32_t type_id_signed = const_2->GetSingleWordOperand(0); + + EXPECT_NE(type_id_unsigned, type_id_signed); + + EXPECT_EQ(const_3->GetSingleWordOperand(0), type_id_unsigned); + EXPECT_EQ(const_5->GetSingleWordOperand(0), type_id_unsigned); + + EXPECT_EQ(const_4->GetSingleWordOperand(0), type_id_signed); + EXPECT_EQ(const_6->GetSingleWordOperand(0), type_id_signed); + + Match(text, context.get()); +} + +TEST_F(IRBuilderTest, AccelerationStructureNV) { + const std::string text = R"( +; CHECK: OpTypeAccelerationStructureKHR +OpCapability Shader +OpCapability RayTracingNV +OpExtension "SPV_NV_ray_tracing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %8 "main" +OpExecutionMode %8 OriginUpperLeft +%1 = OpTypeVoid +%2 = OpTypeBool +%3 = OpTypeAccelerationStructureNV +%7 = OpTypeFunction %1 +%8 = OpFunction %1 None %7 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(nullptr, context); + + InstructionBuilder builder(context.get(), + &*context->module()->begin()->begin()->begin()); + Match(text, context.get()); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/ir_context_test.cpp b/third_party/spirv-tools/test/opt/ir_context_test.cpp new file mode 100644 index 0000000..b6866d0 --- /dev/null +++ b/third_party/spirv-tools/test/opt/ir_context_test.cpp @@ -0,0 +1,1173 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/ir_context.h" + +#include +#include +#include +#include + +#include "OpenCLDebugInfo100.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/pass.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +static const uint32_t kDebugDeclareOperandVariableIndex = 5; +static const uint32_t kDebugValueOperandValueIndex = 5; + +namespace spvtools { +namespace opt { +namespace { + +using Analysis = IRContext::Analysis; +using ::testing::Each; +using ::testing::UnorderedElementsAre; + +class NoopPassPreservesNothing : public Pass { + public: + NoopPassPreservesNothing(Status s) : Pass(), status_to_return_(s) {} + + const char* name() const override { return "noop-pass"; } + Status Process() override { return status_to_return_; } + + private: + Status status_to_return_; +}; + +class NoopPassPreservesAll : public Pass { + public: + NoopPassPreservesAll(Status s) : Pass(), status_to_return_(s) {} + + const char* name() const override { return "noop-pass"; } + Status Process() override { return status_to_return_; } + + Analysis GetPreservedAnalyses() override { + return Analysis(IRContext::kAnalysisEnd - 1); + } + + private: + Status status_to_return_; +}; + +class NoopPassPreservesFirst : public Pass { + public: + NoopPassPreservesFirst(Status s) : Pass(), status_to_return_(s) {} + + const char* name() const override { return "noop-pass"; } + Status Process() override { return status_to_return_; } + + Analysis GetPreservedAnalyses() override { return IRContext::kAnalysisBegin; } + + private: + Status status_to_return_; +}; + +using IRContextTest = PassTest<::testing::Test>; + +TEST_F(IRContextTest, IndividualValidAfterBuild) { + std::unique_ptr module(new Module()); + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), + spvtools::MessageConsumer()); + + for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd; + i <<= 1) { + localContext.BuildInvalidAnalyses(i); + EXPECT_TRUE(localContext.AreAnalysesValid(i)); + } +} + +TEST_F(IRContextTest, AllValidAfterBuild) { + std::unique_ptr module = MakeUnique(); + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), + spvtools::MessageConsumer()); + + Analysis built_analyses = IRContext::kAnalysisNone; + for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd; + i <<= 1) { + localContext.BuildInvalidAnalyses(i); + built_analyses |= i; + } + EXPECT_TRUE(localContext.AreAnalysesValid(built_analyses)); +} + +TEST_F(IRContextTest, AllValidAfterPassNoChange) { + std::unique_ptr module = MakeUnique(); + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), + spvtools::MessageConsumer()); + + Analysis built_analyses = IRContext::kAnalysisNone; + for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd; + i <<= 1) { + localContext.BuildInvalidAnalyses(i); + built_analyses |= i; + } + + NoopPassPreservesNothing pass(Pass::Status::SuccessWithoutChange); + Pass::Status s = pass.Run(&localContext); + EXPECT_EQ(s, Pass::Status::SuccessWithoutChange); + EXPECT_TRUE(localContext.AreAnalysesValid(built_analyses)); +} + +TEST_F(IRContextTest, NoneValidAfterPassWithChange) { + std::unique_ptr module = MakeUnique(); + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), + spvtools::MessageConsumer()); + + for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd; + i <<= 1) { + localContext.BuildInvalidAnalyses(i); + } + + NoopPassPreservesNothing pass(Pass::Status::SuccessWithChange); + Pass::Status s = pass.Run(&localContext); + EXPECT_EQ(s, Pass::Status::SuccessWithChange); + for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd; + i <<= 1) { + EXPECT_FALSE(localContext.AreAnalysesValid(i)); + } +} + +TEST_F(IRContextTest, AllPreservedAfterPassWithChange) { + std::unique_ptr module = MakeUnique(); + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), + spvtools::MessageConsumer()); + + for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd; + i <<= 1) { + localContext.BuildInvalidAnalyses(i); + } + + NoopPassPreservesAll pass(Pass::Status::SuccessWithChange); + Pass::Status s = pass.Run(&localContext); + EXPECT_EQ(s, Pass::Status::SuccessWithChange); + for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd; + i <<= 1) { + EXPECT_TRUE(localContext.AreAnalysesValid(i)); + } +} + +TEST_F(IRContextTest, PreserveFirstOnlyAfterPassWithChange) { + std::unique_ptr module = MakeUnique(); + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, std::move(module), + spvtools::MessageConsumer()); + + for (Analysis i = IRContext::kAnalysisBegin; i < IRContext::kAnalysisEnd; + i <<= 1) { + localContext.BuildInvalidAnalyses(i); + } + + NoopPassPreservesFirst pass(Pass::Status::SuccessWithChange); + Pass::Status s = pass.Run(&localContext); + EXPECT_EQ(s, Pass::Status::SuccessWithChange); + EXPECT_TRUE(localContext.AreAnalysesValid(IRContext::kAnalysisBegin)); + for (Analysis i = IRContext::kAnalysisBegin << 1; i < IRContext::kAnalysisEnd; + i <<= 1) { + EXPECT_FALSE(localContext.AreAnalysesValid(i)); + } +} + +TEST_F(IRContextTest, KillMemberName) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %3 "stuff" + OpMemberName %3 0 "refZ" + OpMemberDecorate %3 0 Offset 0 + OpDecorate %3 Block + %4 = OpTypeFloat 32 + %3 = OpTypeStruct %4 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %2 = OpFunction %5 None %6 + %7 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Build the decoration manager. + context->get_decoration_mgr(); + + // Delete the OpTypeStruct. Should delete the OpName, OpMemberName, and + // OpMemberDecorate associated with it. + context->KillDef(3); + + // Make sure all of the name are removed. + for (auto& inst : context->debugs2()) { + EXPECT_EQ(inst.opcode(), SpvOpNop); + } + + // Make sure all of the decorations are removed. + for (auto& inst : context->annotations()) { + EXPECT_EQ(inst.opcode(), SpvOpNop); + } +} + +TEST_F(IRContextTest, KillGroupDecoration) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpDecorate %3 Restrict + %3 = OpDecorationGroup + OpGroupDecorate %3 %4 %5 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeStruct %6 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %2 = OpFunction %9 None %10 + %11 = OpLabel + %4 = OpVariable %7 Function + %5 = OpVariable %7 Function + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Build the decoration manager. + context->get_decoration_mgr(); + + // Delete the second variable. + context->KillDef(5); + + // The three decorations instructions should still be there. The first two + // should be the same, but the third should have %5 removed. + + // Check the OpDecorate instruction + auto inst = context->annotation_begin(); + EXPECT_EQ(inst->opcode(), SpvOpDecorate); + EXPECT_EQ(inst->GetSingleWordInOperand(0), 3); + + // Check the OpDecorationGroup Instruction + ++inst; + EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup); + EXPECT_EQ(inst->result_id(), 3); + + // Check that %5 is no longer part of the group. + ++inst; + EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate); + EXPECT_EQ(inst->NumInOperands(), 2); + EXPECT_EQ(inst->GetSingleWordInOperand(0), 3); + EXPECT_EQ(inst->GetSingleWordInOperand(1), 4); + + // Check that we are at the end. + ++inst; + EXPECT_EQ(inst, context->annotation_end()); +} + +TEST_F(IRContextTest, TakeNextUniqueIdIncrementing) { + const uint32_t NUM_TESTS = 1000; + IRContext localContext(SPV_ENV_UNIVERSAL_1_2, nullptr); + for (uint32_t i = 1; i < NUM_TESTS; ++i) + EXPECT_EQ(i, localContext.TakeNextUniqueId()); +} + +TEST_F(IRContextTest, KillGroupDecorationWitNoDecorations) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpDecorationGroup + OpGroupDecorate %3 %4 %5 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeStruct %6 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %2 = OpFunction %9 None %10 + %11 = OpLabel + %4 = OpVariable %7 Function + %5 = OpVariable %7 Function + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Build the decoration manager. + context->get_decoration_mgr(); + + // Delete the second variable. + context->KillDef(5); + + // The two decoration instructions should still be there. The first one + // should be the same, but the second should have %5 removed. + + // Check the OpDecorationGroup Instruction + auto inst = context->annotation_begin(); + EXPECT_EQ(inst->opcode(), SpvOpDecorationGroup); + EXPECT_EQ(inst->result_id(), 3); + + // Check that %5 is no longer part of the group. + ++inst; + EXPECT_EQ(inst->opcode(), SpvOpGroupDecorate); + EXPECT_EQ(inst->NumInOperands(), 2); + EXPECT_EQ(inst->GetSingleWordInOperand(0), 3); + EXPECT_EQ(inst->GetSingleWordInOperand(1), 4); + + // Check that we are at the end. + ++inst; + EXPECT_EQ(inst, context->annotation_end()); +} + +TEST_F(IRContextTest, KillDecorationGroup) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpDecorationGroup + OpGroupDecorate %3 %4 %5 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeStruct %6 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %2 = OpFunction %9 None %10 + %11 = OpLabel + %4 = OpVariable %7 Function + %5 = OpVariable %7 Function + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Build the decoration manager. + context->get_decoration_mgr(); + + // Delete the second variable. + context->KillDef(3); + + // Check the OpDecorationGroup Instruction is still there. + EXPECT_TRUE(context->annotations().empty()); +} + +TEST_F(IRContextTest, KillFunctionFromDebugFunction) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpString "ps.hlsl" + %4 = OpString "foo" + OpSource HLSL 600 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %7 = OpExtInst %void %1 DebugSource %3 + %8 = OpExtInst %void %1 DebugCompilationUnit 1 4 %7 HLSL + %9 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void + %10 = OpExtInst %void %1 DebugFunction %4 %9 %7 1 1 %8 %4 FlagIsProtected|FlagIsPrivate 1 %11 + %2 = OpFunction %void None %6 + %12 = OpLabel + OpReturn + OpFunctionEnd + %11 = OpFunction %void None %6 + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Delete the second variable. + context->KillDef(11); + + // Get DebugInfoNone id. + uint32_t debug_info_none_id = 0; + for (auto it = context->ext_inst_debuginfo_begin(); + it != context->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { + debug_info_none_id = it->result_id(); + } + } + EXPECT_NE(0, debug_info_none_id); + + // Check the Function operand of DebugFunction is DebugInfoNone. + const uint32_t kDebugFunctionOperandFunctionIndex = 13; + bool checked = false; + for (auto it = context->ext_inst_debuginfo_begin(); + it != context->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + EXPECT_FALSE(checked); + EXPECT_EQ(it->GetOperand(kDebugFunctionOperandFunctionIndex).words[0], + debug_info_none_id); + checked = true; + } + } + EXPECT_TRUE(checked); +} + +TEST_F(IRContextTest, KillVariableFromDebugGlobalVariable) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %3 = OpString "ps.hlsl" + %4 = OpString "foo" + %5 = OpString "int" + OpSource HLSL 600 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Private_uint = OpTypePointer Private %uint + %void = OpTypeVoid + %10 = OpTypeFunction %void + %11 = OpVariable %_ptr_Private_uint Private + %12 = OpExtInst %void %1 DebugSource %3 + %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL + %14 = OpExtInst %void %1 DebugTypeBasic %5 %uint_32 Signed + %15 = OpExtInst %void %1 DebugGlobalVariable %4 %14 %12 1 12 %13 %4 %11 FlagIsDefinition + %2 = OpFunction %void None %10 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + + // Delete the second variable. + context->KillDef(11); + + // Get DebugInfoNone id. + uint32_t debug_info_none_id = 0; + for (auto it = context->ext_inst_debuginfo_begin(); + it != context->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { + debug_info_none_id = it->result_id(); + } + } + EXPECT_NE(0, debug_info_none_id); + + // Check the Function operand of DebugFunction is DebugInfoNone. + const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; + bool checked = false; + for (auto it = context->ext_inst_debuginfo_begin(); + it != context->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugGlobalVariable) { + EXPECT_FALSE(checked); + EXPECT_EQ( + it->GetOperand(kDebugGlobalVariableOperandVariableIndex).words[0], + debug_info_none_id); + checked = true; + } + } + EXPECT_TRUE(checked); +} + +TEST_F(IRContextTest, BasicVisitFromEntryPoint) { + // Make sure we visit the entry point, and the function it calls. + // Do not visit Dead or Exported. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %10 "main" + OpName %10 "main" + OpName %Dead "Dead" + OpName %11 "Constant" + OpName %ExportedFunc "ExportedFunc" + OpDecorate %ExportedFunc LinkageAttributes "ExportedFunc" Export + %void = OpTypeVoid + %6 = OpTypeFunction %void + %10 = OpFunction %void None %6 + %14 = OpLabel + %15 = OpFunctionCall %void %11 + %16 = OpFunctionCall %void %11 + OpReturn + OpFunctionEnd + %11 = OpFunction %void None %6 + %18 = OpLabel + OpReturn + OpFunctionEnd + %Dead = OpFunction %void None %6 + %19 = OpLabel + OpReturn + OpFunctionEnd +%ExportedFunc = OpFunction %void None %7 + %20 = OpLabel + %21 = OpFunctionCall %void %11 + OpReturn + OpFunctionEnd +)"; + // clang-format on + + std::unique_ptr localContext = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n" + << text << std::endl; + std::vector processed; + Pass::ProcessFunction mark_visited = [&processed](Function* fp) { + processed.push_back(fp->result_id()); + return false; + }; + localContext->ProcessEntryPointCallTree(mark_visited); + EXPECT_THAT(processed, UnorderedElementsAre(10, 11)); +} + +TEST_F(IRContextTest, BasicVisitReachable) { + // Make sure we visit the entry point, exported function, and the function + // they call. Do not visit Dead. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %10 "main" + OpName %10 "main" + OpName %Dead "Dead" + OpName %11 "Constant" + OpName %12 "ExportedFunc" + OpName %13 "Constant2" + OpDecorate %12 LinkageAttributes "ExportedFunc" Export + %void = OpTypeVoid + %6 = OpTypeFunction %void + %10 = OpFunction %void None %6 + %14 = OpLabel + %15 = OpFunctionCall %void %11 + %16 = OpFunctionCall %void %11 + OpReturn + OpFunctionEnd + %11 = OpFunction %void None %6 + %18 = OpLabel + OpReturn + OpFunctionEnd + %Dead = OpFunction %void None %6 + %19 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %void None %6 + %20 = OpLabel + %21 = OpFunctionCall %void %13 + OpReturn + OpFunctionEnd + %13 = OpFunction %void None %6 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + + std::unique_ptr localContext = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n" + << text << std::endl; + + std::vector processed; + Pass::ProcessFunction mark_visited = [&processed](Function* fp) { + processed.push_back(fp->result_id()); + return false; + }; + localContext->ProcessReachableCallTree(mark_visited); + EXPECT_THAT(processed, UnorderedElementsAre(10, 11, 12, 13)); +} + +TEST_F(IRContextTest, BasicVisitOnlyOnce) { + // Make sure we visit %12 only once, even if it is called from two different + // functions. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %10 "main" + OpName %10 "main" + OpName %Dead "Dead" + OpName %11 "Constant" + OpName %12 "ExportedFunc" + OpDecorate %12 LinkageAttributes "ExportedFunc" Export + %void = OpTypeVoid + %6 = OpTypeFunction %void + %10 = OpFunction %void None %6 + %14 = OpLabel + %15 = OpFunctionCall %void %11 + %16 = OpFunctionCall %void %12 + OpReturn + OpFunctionEnd + %11 = OpFunction %void None %6 + %18 = OpLabel + %19 = OpFunctionCall %void %12 + OpReturn + OpFunctionEnd + %Dead = OpFunction %void None %6 + %20 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %void None %6 + %21 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + + std::unique_ptr localContext = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n" + << text << std::endl; + + std::vector processed; + Pass::ProcessFunction mark_visited = [&processed](Function* fp) { + processed.push_back(fp->result_id()); + return false; + }; + localContext->ProcessReachableCallTree(mark_visited); + EXPECT_THAT(processed, UnorderedElementsAre(10, 11, 12)); +} + +TEST_F(IRContextTest, BasicDontVisitExportedVariable) { + // Make sure we only visit functions and not exported variables. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %10 "main" + OpExecutionMode %10 OriginUpperLeft + OpSource GLSL 150 + OpName %10 "main" + OpName %12 "export_var" + OpDecorate %12 LinkageAttributes "export_var" Export + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_1 = OpConstant %float 1 + %12 = OpVariable %float Output + %10 = OpFunction %void None %6 + %14 = OpLabel + OpStore %12 %float_1 + OpReturn + OpFunctionEnd +)"; + // clang-format on + + std::unique_ptr localContext = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, localContext) << "Assembling failed for shader:\n" + << text << std::endl; + + std::vector processed; + Pass::ProcessFunction mark_visited = [&processed](Function* fp) { + processed.push_back(fp->result_id()); + return false; + }; + localContext->ProcessReachableCallTree(mark_visited); + EXPECT_THAT(processed, UnorderedElementsAre(10)); +} + +TEST_F(IRContextTest, IdBoundTestAtLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound); + uint32_t next_id_bound = context->TakeNextId(); + EXPECT_EQ(next_id_bound, 0); + EXPECT_EQ(current_bound, context->module()->id_bound()); + next_id_bound = context->TakeNextId(); + EXPECT_EQ(next_id_bound, 0); +} + +TEST_F(IRContextTest, IdBoundTestBelowLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound + 100); + uint32_t next_id_bound = context->TakeNextId(); + EXPECT_EQ(next_id_bound, current_bound); + EXPECT_EQ(current_bound + 1, context->module()->id_bound()); + next_id_bound = context->TakeNextId(); + EXPECT_EQ(next_id_bound, current_bound + 1); +} + +TEST_F(IRContextTest, IdBoundTestNearLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound + 1); + uint32_t next_id_bound = context->TakeNextId(); + EXPECT_EQ(next_id_bound, current_bound); + EXPECT_EQ(current_bound + 1, context->module()->id_bound()); + next_id_bound = context->TakeNextId(); + EXPECT_EQ(next_id_bound, 0); +} + +TEST_F(IRContextTest, IdBoundTestUIntMax) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4294967294 = OpLabel ; ID is UINT_MAX-1 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + uint32_t current_bound = context->module()->id_bound(); + + // Expecting |BuildModule| to preserve the numeric ids. + EXPECT_EQ(current_bound, std::numeric_limits::max()); + + context->set_max_id_bound(current_bound); + uint32_t next_id_bound = context->TakeNextId(); + EXPECT_EQ(next_id_bound, 0); + EXPECT_EQ(current_bound, context->module()->id_bound()); +} + +TEST_F(IRContextTest, CfgAndDomAnalysis) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + // Building the dominator analysis should build the CFG. + ASSERT_TRUE(ctx->module()->begin() != ctx->module()->end()); + ctx->GetDominatorAnalysis(&*ctx->module()->begin()); + + EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG)); + EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis)); + + // Invalidating the CFG analysis should invalidate the dominator analysis. + ctx->InvalidateAnalyses(IRContext::kAnalysisCFG); + EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisCFG)); + EXPECT_FALSE(ctx->AreAnalysesValid(IRContext::kAnalysisDominatorAnalysis)); +} + +TEST_F(IRContextTest, AsanErrorTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %6 %11 %5 + OpStore %10 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule( + env, consumer, shader, SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + opt::Function* fun = + context->cfg()->block(5)->GetParent(); // Computes the CFG analysis + opt::DominatorAnalysis* dom = nullptr; + dom = context->GetDominatorAnalysis(fun); // Computes the dominator analysis, + // which depends on the CFG + // analysis + context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisDominatorAnalysis); // Invalidates the + // CFG analysis + dom = context->GetDominatorAnalysis( + fun); // Recompute the CFG analysis because the Dominator tree uses it. + auto bb = dom->ImmediateDominator(5); + std::cout + << bb->id(); // Make sure asan does not complain about use after free. +} + +TEST_F(IRContextTest, DebugInstructionReplaceSingleUse) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +%2 = OpString "test" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeFloat 32 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 32 +%10 = OpExtInst %3 %1 DebugExpression +%11 = OpExtInst %3 %1 DebugSource %2 +%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL +%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3 +%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17 +%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float +%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal +%17 = OpFunction %3 None %4 +%18 = OpLabel +%19 = OpExtInst %3 %1 DebugScope %14 +%20 = OpVariable %6 Function +%26 = OpVariable %6 Function +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %5 %7 %18 +OpBranch %23 +%23 = OpLabel +OpLine %2 0 0 +OpStore %20 %7 +%24 = OpExtInst %3 %1 DebugValue %16 %22 %10 +%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo); + NoopPassPreservesAll pass(Pass::Status::SuccessWithChange); + pass.Run(ctx.get()); + EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo)); + + auto* dbg_value = ctx->get_def_use_mgr()->GetDef(24); + EXPECT_TRUE(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex) == + 22); + EXPECT_TRUE(ctx->ReplaceAllUsesWith(22, 7)); + dbg_value = ctx->get_def_use_mgr()->GetDef(24); + EXPECT_TRUE(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex) == + 7); + + auto* dbg_decl = ctx->get_def_use_mgr()->GetDef(25); + EXPECT_TRUE( + dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26); + EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20)); + dbg_decl = ctx->get_def_use_mgr()->GetDef(25); + EXPECT_TRUE( + dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20); +} + +TEST_F(IRContextTest, DebugInstructionReplaceAllUses) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +%2 = OpString "test" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeFloat 32 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 32 +%10 = OpExtInst %3 %1 DebugExpression +%11 = OpExtInst %3 %1 DebugSource %2 +%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL +%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3 +%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17 +%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float +%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal +%27 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 1 0 %14 FlagIsLocal +%17 = OpFunction %3 None %4 +%18 = OpLabel +%19 = OpExtInst %3 %1 DebugScope %14 +%20 = OpVariable %6 Function +%26 = OpVariable %6 Function +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %5 %7 %18 +OpBranch %23 +%23 = OpLabel +OpLine %2 0 0 +OpStore %20 %7 +%24 = OpExtInst %3 %1 DebugValue %16 %22 %10 +%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10 +%28 = OpExtInst %3 %1 DebugValue %27 %22 %10 +%29 = OpExtInst %3 %1 DebugDeclare %27 %26 %10 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo); + NoopPassPreservesAll pass(Pass::Status::SuccessWithChange); + pass.Run(ctx.get()); + EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo)); + + auto* dbg_value0 = ctx->get_def_use_mgr()->GetDef(24); + auto* dbg_value1 = ctx->get_def_use_mgr()->GetDef(28); + EXPECT_TRUE(dbg_value0->GetSingleWordOperand(kDebugValueOperandValueIndex) == + 22); + EXPECT_TRUE(dbg_value1->GetSingleWordOperand(kDebugValueOperandValueIndex) == + 22); + EXPECT_TRUE(ctx->ReplaceAllUsesWith(22, 7)); + dbg_value0 = ctx->get_def_use_mgr()->GetDef(24); + dbg_value1 = ctx->get_def_use_mgr()->GetDef(28); + EXPECT_TRUE(dbg_value0->GetSingleWordOperand(kDebugValueOperandValueIndex) == + 7); + EXPECT_TRUE(dbg_value1->GetSingleWordOperand(kDebugValueOperandValueIndex) == + 7); + + auto* dbg_decl0 = ctx->get_def_use_mgr()->GetDef(25); + auto* dbg_decl1 = ctx->get_def_use_mgr()->GetDef(29); + EXPECT_TRUE( + dbg_decl0->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26); + EXPECT_TRUE( + dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 26); + EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20)); + dbg_decl0 = ctx->get_def_use_mgr()->GetDef(25); + dbg_decl1 = ctx->get_def_use_mgr()->GetDef(29); + EXPECT_TRUE( + dbg_decl0->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20); + EXPECT_TRUE( + dbg_decl1->GetSingleWordOperand(kDebugDeclareOperandVariableIndex) == 20); +} + +TEST_F(IRContextTest, DebugInstructionReplaceDebugScopeAndDebugInlinedAt) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +%2 = OpString "test" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeFloat 32 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 32 +%10 = OpExtInst %3 %1 DebugExpression +%11 = OpExtInst %3 %1 DebugSource %2 +%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL +%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3 +%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17 +%15 = OpExtInst %3 %1 DebugInfoNone +%16 = OpExtInst %3 %1 DebugFunction %2 %13 %11 10 10 %12 %2 FlagIsProtected|FlagIsPrivate 0 %15 +%25 = OpExtInst %3 %1 DebugInlinedAt 0 %14 +%26 = OpExtInst %3 %1 DebugInlinedAt 2 %14 +%17 = OpFunction %3 None %4 +%18 = OpLabel +%19 = OpExtInst %3 %1 DebugScope %14 +%20 = OpVariable %6 Function +OpBranch %21 +%21 = OpLabel +%24 = OpExtInst %3 %1 DebugScope %16 +%22 = OpPhi %5 %7 %18 +OpBranch %23 +%23 = OpLabel +%27 = OpExtInst %3 %1 DebugScope %16 %25 +OpLine %2 0 0 +%28 = OpFAdd %5 %7 %7 +OpStore %20 %28 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo); + NoopPassPreservesAll pass(Pass::Status::SuccessWithChange); + pass.Run(ctx.get()); + EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo)); + + auto* inst0 = ctx->get_def_use_mgr()->GetDef(20); + auto* inst1 = ctx->get_def_use_mgr()->GetDef(22); + auto* inst2 = ctx->get_def_use_mgr()->GetDef(28); + EXPECT_EQ(inst0->GetDebugScope().GetLexicalScope(), 14); + EXPECT_EQ(inst1->GetDebugScope().GetLexicalScope(), 16); + EXPECT_EQ(inst2->GetDebugScope().GetLexicalScope(), 16); + EXPECT_EQ(inst2->GetDebugInlinedAt(), 25); + + EXPECT_TRUE(ctx->ReplaceAllUsesWith(14, 12)); + EXPECT_TRUE(ctx->ReplaceAllUsesWith(16, 14)); + EXPECT_TRUE(ctx->ReplaceAllUsesWith(25, 26)); + EXPECT_EQ(inst0->GetDebugScope().GetLexicalScope(), 12); + EXPECT_EQ(inst1->GetDebugScope().GetLexicalScope(), 14); + EXPECT_EQ(inst2->GetDebugScope().GetLexicalScope(), 14); + EXPECT_EQ(inst2->GetDebugInlinedAt(), 26); +} + +TEST_F(IRContextTest, AddDebugValueAfterReplaceUse) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +%2 = OpString "test" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeFloat 32 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpTypeInt 32 0 +%9 = OpConstant %8 32 +%10 = OpExtInst %3 %1 DebugExpression +%11 = OpExtInst %3 %1 DebugSource %2 +%12 = OpExtInst %3 %1 DebugCompilationUnit 1 4 %11 HLSL +%13 = OpExtInst %3 %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %3 +%14 = OpExtInst %3 %1 DebugFunction %2 %13 %11 0 0 %12 %2 FlagIsProtected|FlagIsPrivate 0 %17 +%15 = OpExtInst %3 %1 DebugTypeBasic %2 %9 Float +%16 = OpExtInst %3 %1 DebugLocalVariable %2 %15 %11 0 0 %14 FlagIsLocal +%17 = OpFunction %3 None %4 +%18 = OpLabel +%19 = OpExtInst %3 %1 DebugScope %14 +%20 = OpVariable %6 Function +%26 = OpVariable %6 Function +OpBranch %21 +%21 = OpLabel +%27 = OpExtInst %3 %1 DebugScope %14 +%22 = OpPhi %5 %7 %18 +OpBranch %23 +%23 = OpLabel +%28 = OpExtInst %3 %1 DebugScope %14 +OpLine %2 0 0 +OpStore %20 %7 +%24 = OpExtInst %3 %1 DebugValue %16 %22 %10 +%25 = OpExtInst %3 %1 DebugDeclare %16 %26 %10 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr ctx = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ctx->BuildInvalidAnalyses(IRContext::kAnalysisDebugInfo); + NoopPassPreservesAll pass(Pass::Status::SuccessWithChange); + pass.Run(ctx.get()); + EXPECT_TRUE(ctx->AreAnalysesValid(IRContext::kAnalysisDebugInfo)); + + // Replace all uses of result it '26' with '20' + auto* dbg_decl = ctx->get_def_use_mgr()->GetDef(25); + EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex), + 26); + EXPECT_TRUE(ctx->ReplaceAllUsesWith(26, 20)); + dbg_decl = ctx->get_def_use_mgr()->GetDef(25); + EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex), + 20); + + // No DebugValue should be added because result id '26' is not used for + // DebugDeclare. + ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 26, 22, + dbg_decl, nullptr); + EXPECT_EQ(dbg_decl->NextNode()->opcode(), SpvOpReturn); + + // DebugValue should be added because result id '20' is used for DebugDeclare. + ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 20, 22, + dbg_decl, nullptr); + EXPECT_EQ(dbg_decl->NextNode()->GetOpenCL100DebugOpcode(), + OpenCLDebugInfo100DebugValue); + + // Replace all uses of result it '20' with '26' + EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex), + 20); + EXPECT_TRUE(ctx->ReplaceAllUsesWith(20, 26)); + EXPECT_EQ(dbg_decl->GetSingleWordOperand(kDebugDeclareOperandVariableIndex), + 26); + + // No DebugValue should be added because result id '20' is not used for + // DebugDeclare. + ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 20, 7, + dbg_decl, nullptr); + Instruction* dbg_value = dbg_decl->NextNode(); + EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue); + EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 22); + + // DebugValue should be added because result id '26' is used for DebugDeclare. + ctx->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible(dbg_decl, 26, 7, + dbg_decl, nullptr); + dbg_value = dbg_decl->NextNode(); + EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue); + EXPECT_EQ(dbg_value->GetSingleWordOperand(kDebugValueOperandValueIndex), 7); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/ir_loader_test.cpp b/third_party/spirv-tools/test/opt/ir_loader_test.cpp new file mode 100644 index 0000000..8b77aa3 --- /dev/null +++ b/third_party/spirv-tools/test/opt/ir_loader_test.cpp @@ -0,0 +1,1413 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { +namespace { + +constexpr uint32_t kOpLineOperandLineIndex = 1; + +void DoRoundTripCheck(const std::string& text) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + ASSERT_NE(nullptr, context) << "Failed to assemble\n" << text; + + std::vector binary; + context->module()->ToBinary(&binary, /* skip_nop = */ false); + + std::string disassembled_text; + EXPECT_TRUE(t.Disassemble(binary, &disassembled_text)); + EXPECT_EQ(text, disassembled_text); +} + +TEST(IrBuilder, RoundTrip) { + // #version 310 es + // int add(int a, int b) { return a + b; } + // void main() { add(1, 2); } + DoRoundTripCheck( + // clang-format off + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %main \"main\"\n" + "OpSource ESSL 310\n" + "OpSourceExtension \"GL_GOOGLE_cpp_style_line_directive\"\n" + "OpSourceExtension \"GL_GOOGLE_include_directive\"\n" + "OpName %main \"main\"\n" + "OpName %add_i1_i1_ \"add(i1;i1;\"\n" + "OpName %a \"a\"\n" + "OpName %b \"b\"\n" + "OpName %param \"param\"\n" + "OpName %param_0 \"param\"\n" + "%void = OpTypeVoid\n" + "%9 = OpTypeFunction %void\n" + "%int = OpTypeInt 32 1\n" + "%_ptr_Function_int = OpTypePointer Function %int\n" + "%12 = OpTypeFunction %int %_ptr_Function_int %_ptr_Function_int\n" + "%int_1 = OpConstant %int 1\n" + "%int_2 = OpConstant %int 2\n" + "%main = OpFunction %void None %9\n" + "%15 = OpLabel\n" + "%param = OpVariable %_ptr_Function_int Function\n" + "%param_0 = OpVariable %_ptr_Function_int Function\n" + "OpStore %param %int_1\n" + "OpStore %param_0 %int_2\n" + "%16 = OpFunctionCall %int %add_i1_i1_ %param %param_0\n" + "OpReturn\n" + "OpFunctionEnd\n" + "%add_i1_i1_ = OpFunction %int None %12\n" + "%a = OpFunctionParameter %_ptr_Function_int\n" + "%b = OpFunctionParameter %_ptr_Function_int\n" + "%17 = OpLabel\n" + "%18 = OpLoad %int %a\n" + "%19 = OpLoad %int %b\n" + "%20 = OpIAdd %int %18 %19\n" + "OpReturnValue %20\n" + "OpFunctionEnd\n"); + // clang-format on +} + +TEST(IrBuilder, RoundTripIncompleteBasicBlock) { + DoRoundTripCheck( + "%2 = OpFunction %1 None %3\n" + "%4 = OpLabel\n" + "OpNop\n"); +} + +TEST(IrBuilder, RoundTripIncompleteFunction) { + DoRoundTripCheck("%2 = OpFunction %1 None %3\n"); +} + +TEST(IrBuilder, KeepLineDebugInfo) { + // #version 310 es + // void main() {} + DoRoundTripCheck( + // clang-format off + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %main \"main\"\n" + "%3 = OpString \"minimal.vert\"\n" + "OpSource ESSL 310\n" + "OpName %main \"main\"\n" + "OpLine %3 10 10\n" + "%void = OpTypeVoid\n" + "OpLine %3 100 100\n" + "%5 = OpTypeFunction %void\n" + "%main = OpFunction %void None %5\n" + "OpLine %3 1 1\n" + "OpNoLine\n" + "OpLine %3 2 2\n" + "OpLine %3 3 3\n" + "%6 = OpLabel\n" + "OpLine %3 4 4\n" + "OpNoLine\n" + "OpReturn\n" + "OpFunctionEnd\n"); + // clang-format on +} + +TEST(IrBuilder, DistributeLineDebugInfo) { + const std::string text = + // clang-format off + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %main \"main\"\n" + "OpSource ESSL 310\n" + "%file = OpString \"test\"\n" + "OpName %main \"main\"\n" + "OpName %f_ \"f(\"\n" + "OpName %gv1 \"gv1\"\n" + "OpName %gv2 \"gv2\"\n" + "OpName %lv1 \"lv1\"\n" + "OpName %lv2 \"lv2\"\n" + "OpName %lv1_0 \"lv1\"\n" + "%void = OpTypeVoid\n" + "%10 = OpTypeFunction %void\n" + "OpLine %file 10 0\n" + "%float = OpTypeFloat 32\n" + "%12 = OpTypeFunction %float\n" + "%_ptr_Private_float = OpTypePointer Private %float\n" + "%gv1 = OpVariable %_ptr_Private_float Private\n" + "%float_10 = OpConstant %float 10\n" + "%gv2 = OpVariable %_ptr_Private_float Private\n" + "%float_100 = OpConstant %float 100\n" + "%_ptr_Function_float = OpTypePointer Function %float\n" + "%main = OpFunction %void None %10\n" + "%17 = OpLabel\n" + "%lv1_0 = OpVariable %_ptr_Function_float Function\n" + "OpStore %gv1 %float_10\n" + "OpStore %gv2 %float_100\n" + "OpLine %file 1 0\n" + "OpNoLine\n" + "OpLine %file 2 0\n" + "%18 = OpLoad %float %gv1\n" + "%19 = OpLoad %float %gv2\n" + "%20 = OpFSub %float %18 %19\n" + "OpStore %lv1_0 %20\n" + "OpReturn\n" + "OpFunctionEnd\n" + "%f_ = OpFunction %float None %12\n" + "%21 = OpLabel\n" + "%lv1 = OpVariable %_ptr_Function_float Function\n" + "%lv2 = OpVariable %_ptr_Function_float Function\n" + "OpLine %file 3 0\n" + "OpLine %file 4 0\n" + "%22 = OpLoad %float %gv1\n" + "%23 = OpLoad %float %gv2\n" + "%24 = OpFAdd %float %22 %23\n" + "OpStore %lv1 %24\n" + "OpLine %file 5 0\n" + "OpLine %file 6 0\n" + "OpNoLine\n" + "%25 = OpLoad %float %gv1\n" + "%26 = OpLoad %float %gv2\n" + "%27 = OpFMul %float %25 %26\n" + "OpBranch %28\n" + "%28 = OpLabel\n" + "OpStore %lv2 %27\n" + "%29 = OpLoad %float %lv1\n" + "OpLine %file 7 0\n" + "%30 = OpLoad %float %lv2\n" + "%31 = OpFDiv %float %28 %29\n" + "OpReturnValue %30\n" + "OpFunctionEnd\n"; + // clang-format on + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + + struct LineInstrCheck { + uint32_t id; + std::vector line_numbers; + }; + const uint32_t kNoLine = 0; + const LineInstrCheck line_checks[] = { + {12, {10}}, {18, {1, kNoLine, 2}}, + {19, {2}}, {20, {2}}, + {22, {3, 4}}, {23, {4}}, + {24, {4}}, {25, {5, 6, kNoLine}}, + {26, {}}, {27, {}}, + {28, {}}, {29, {}}, + {30, {7}}, {31, {7}}, + }; + + spvtools::opt::analysis::DefUseManager* def_use_mgr = + context->get_def_use_mgr(); + for (const LineInstrCheck& check : line_checks) { + auto& lines = def_use_mgr->GetDef(check.id)->dbg_line_insts(); + for (uint32_t i = 0; i < check.line_numbers.size(); ++i) { + if (check.line_numbers[i] == kNoLine) { + EXPECT_EQ(lines[i].opcode(), SpvOpNoLine); + continue; + } + EXPECT_EQ(lines[i].opcode(), SpvOpLine); + EXPECT_EQ(lines[i].GetSingleWordOperand(kOpLineOperandLineIndex), + check.line_numbers[i]); + } + } +} + +TEST(IrBuilder, ConsumeDebugInfoInst) { + // /* HLSL */ + // + // struct VS_OUTPUT { + // float4 pos : SV_POSITION; + // float4 color : COLOR; + // }; + // + // VS_OUTPUT main(float4 pos : POSITION, + // float4 color : COLOR) { + // VS_OUTPUT vout; + // vout.pos = pos; + // vout.color = color; + // return vout; + // } + DoRoundTripCheck(R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %pos %color %gl_Position %out_var_COLOR +%7 = OpString "simple_vs.hlsl" +%8 = OpString "#line 1 \"simple_vs.hlsl\" +struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; + +VS_OUTPUT main(float4 pos : POSITION, + float4 color : COLOR) { + VS_OUTPUT vout; + vout.pos = pos; + vout.color = color; + return vout; +} +" +OpSource HLSL 600 %7 "#line 1 \"simple_vs.hlsl\" +struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; + +VS_OUTPUT main(float4 pos : POSITION, + float4 color : COLOR) { + VS_OUTPUT vout; + vout.pos = pos; + vout.color = color; + return vout; +} +" +%9 = OpString "struct VS_OUTPUT" +%10 = OpString "float" +%11 = OpString "pos : SV_POSITION" +%12 = OpString "color : COLOR" +%13 = OpString "VS_OUTPUT" +%14 = OpString "main" +%15 = OpString "VS_OUTPUT_main_v4f_v4f" +%16 = OpString "pos : POSITION" +%17 = OpString "color : COLOR" +%18 = OpString "vout" +OpName %out_var_COLOR "out.var.COLOR" +OpName %main "main" +OpName %VS_OUTPUT "VS_OUTPUT" +OpMemberName %VS_OUTPUT 0 "pos" +OpMemberName %VS_OUTPUT 1 "color" +OpName %pos "pos" +OpName %color "color" +OpName %vout "vout" +OpDecorate %gl_Position BuiltIn Position +OpDecorate %pos Location 0 +OpDecorate %color Location 1 +OpDecorate %out_var_COLOR Location 0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_32 = OpConstant %int 32 +%int_128 = OpConstant %int 128 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%31 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float +%VS_OUTPUT = OpTypeStruct %v4float %v4float +%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT +OpLine %7 6 23 +%pos = OpVariable %_ptr_Input_v4float Input +OpLine %7 7 23 +%color = OpVariable %_ptr_Input_v4float Input +OpLine %7 2 16 +%gl_Position = OpVariable %_ptr_Output_v4float Output +OpLine %7 3 18 +%out_var_COLOR = OpVariable %_ptr_Output_v4float Output +%34 = OpExtInst %void %1 DebugSource %7 %8 +%35 = OpExtInst %void %1 DebugCompilationUnit 2 4 %34 HLSL +%36 = OpExtInst %void %1 DebugTypeComposite %9 Structure %34 1 1 %35 %13 %int_128 FlagIsProtected|FlagIsPrivate %37 %38 +%39 = OpExtInst %void %1 DebugTypeBasic %10 %int_32 Float +%40 = OpExtInst %void %1 DebugTypeVector %39 4 +%37 = OpExtInst %void %1 DebugTypeMember %11 %40 %34 2 3 %36 %int_0 %int_128 FlagIsProtected|FlagIsPrivate +%38 = OpExtInst %void %1 DebugTypeMember %12 %40 %34 3 3 %36 %int_128 %int_128 FlagIsProtected|FlagIsPrivate +%41 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %40 %40 +%42 = OpExtInst %void %1 DebugExpression +%43 = OpExtInst %void %1 DebugFunction %14 %41 %34 6 1 %35 %15 FlagIsProtected|FlagIsPrivate 7 %main +%44 = OpExtInst %void %1 DebugLocalVariable %16 %40 %34 6 16 %43 FlagIsLocal 0 +%45 = OpExtInst %void %1 DebugLocalVariable %17 %40 %34 7 16 %43 FlagIsLocal 1 +%46 = OpExtInst %void %1 DebugLocalVariable %18 %36 %34 8 3 %43 FlagIsLocal +OpLine %7 6 1 +%main = OpFunction %void None %31 +%47 = OpLabel +%60 = OpExtInst %void %1 DebugScope %43 +OpLine %7 8 13 +%vout = OpVariable %_ptr_Function_VS_OUTPUT Function +%49 = OpExtInst %void %1 DebugDeclare %46 %vout %42 +OpLine %7 9 14 +%50 = OpLoad %v4float %pos +OpLine %7 9 3 +%51 = OpAccessChain %_ptr_Function_v4float %vout %int_0 +%52 = OpExtInst %void %1 DebugValue %46 %51 %42 %int_0 +OpStore %51 %50 +OpLine %7 10 16 +%53 = OpLoad %v4float %color +OpLine %7 10 3 +%54 = OpAccessChain %_ptr_Function_v4float %vout %int_1 +%55 = OpExtInst %void %1 DebugValue %46 %54 %42 %int_1 +OpStore %54 %53 +OpLine %7 11 10 +%56 = OpLoad %VS_OUTPUT %vout +OpLine %7 11 3 +%57 = OpCompositeExtract %v4float %56 0 +OpStore %gl_Position %57 +%58 = OpCompositeExtract %v4float %56 1 +OpStore %out_var_COLOR %58 +%61 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +)"); +} + +TEST(IrBuilder, ConsumeDebugInfoLexicalScopeInst) { + // /* HLSL */ + // + // float4 func2(float arg2) { // func2_block + // return float4(arg2, 0, 0, 0); + // } + // + // float4 func1(float arg1) { // func1_block + // if (arg1 > 1) { // if_true_block + // return float4(0, 0, 0, 0); + // } + // return func2(arg1); // if_merge_block + // } + // + // float4 main(float pos : POSITION) : SV_POSITION { // main + // return func1(pos); + // } + DoRoundTripCheck(R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %pos %gl_Position +%5 = OpString "block/block.hlsl" +%6 = OpString "#line 1 \"block/block.hlsl\" +float4 func2(float arg2) { + return float4(arg2, 0, 0, 0); +} + +float4 func1(float arg1) { + if (arg1 > 1) { + return float4(0, 0, 0, 0); + } + return func2(arg1); +} + +float4 main(float pos : POSITION) : SV_POSITION { + return func1(pos); +} +" +OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\" +float4 func2(float arg2) { + return float4(arg2, 0, 0, 0); +} + +float4 func1(float arg1) { + if (arg1 > 1) { + return float4(0, 0, 0, 0); + } + return func2(arg1); +} + +float4 main(float pos : POSITION) : SV_POSITION { + return func1(pos); +} +" +%7 = OpString "float" +%8 = OpString "main" +%9 = OpString "v4f_main_f" +%10 = OpString "v4f_func1_f" +%11 = OpString "v4f_func2_f" +%12 = OpString "pos : POSITION" +%13 = OpString "func1" +%14 = OpString "func2" +OpName %main "main" +OpName %pos "pos" +OpName %bb_entry "bb.entry" +OpName %param_var_arg1 "param.var.arg1" +OpName %func1 "func1" +OpName %arg1 "arg1" +OpName %bb_entry_0 "bb.entry" +OpName %param_var_arg2 "param.var.arg2" +OpName %if_true "if.true" +OpName %if_merge "if.merge" +OpName %func2 "func2" +OpName %arg2 "arg2" +OpName %bb_entry_1 "bb.entry" +OpDecorate %gl_Position BuiltIn Position +OpDecorate %pos Location 0 +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%float_1 = OpConstant %float 1 +%float_0 = OpConstant %float 0 +%int_32 = OpConstant %int 32 +%v4float = OpTypeVector %float 4 +%32 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%36 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%38 = OpTypeFunction %v4float %_ptr_Function_float +%bool = OpTypeBool +OpLine %5 12 25 +%pos = OpVariable %_ptr_Input_float Input +OpLine %5 12 37 +%gl_Position = OpVariable %_ptr_Output_v4float Output +%40 = OpExtInst %void %1 DebugSource %5 %6 +%41 = OpExtInst %void %1 DebugCompilationUnit 2 4 %40 HLSL +%42 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float +%43 = OpExtInst %void %1 DebugTypeVector %42 4 +%44 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42 +%45 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42 +%46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42 +%47 = OpExtInst %void %1 DebugFunction %8 %44 %40 12 1 %41 %9 FlagIsProtected|FlagIsPrivate 13 %main +%48 = OpExtInst %void %1 DebugFunction %13 %45 %40 5 1 %41 %10 FlagIsProtected|FlagIsPrivate 13 %func1 +%49 = OpExtInst %void %1 DebugFunction %14 %46 %40 1 1 %41 %11 FlagIsProtected|FlagIsPrivate 13 %func2 +%50 = OpExtInst %void %1 DebugLexicalBlock %40 6 17 %48 +%51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48 +OpLine %5 12 1 +%main = OpFunction %void None %36 +%bb_entry = OpLabel +%70 = OpExtInst %void %1 DebugScope %47 +OpLine %5 13 16 +%param_var_arg1 = OpVariable %_ptr_Function_float Function +%53 = OpLoad %float %pos +OpStore %param_var_arg1 %53 +OpLine %5 13 10 +%54 = OpFunctionCall %v4float %func1 %param_var_arg1 +OpLine %5 13 3 +OpStore %gl_Position %54 +%71 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +OpLine %5 5 1 +%func1 = OpFunction %v4float None %38 +OpLine %5 5 20 +%arg1 = OpFunctionParameter %_ptr_Function_float +%bb_entry_0 = OpLabel +%72 = OpExtInst %void %1 DebugScope %48 +OpLine %5 9 16 +%param_var_arg2 = OpVariable %_ptr_Function_float Function +OpLine %5 6 7 +%57 = OpLoad %float %arg1 +OpLine %5 6 12 +%58 = OpFOrdGreaterThan %bool %57 %float_1 +OpLine %5 6 17 +%73 = OpExtInst %void %1 DebugNoScope +OpSelectionMerge %if_merge None +OpBranchConditional %58 %if_true %if_merge +%if_true = OpLabel +%74 = OpExtInst %void %1 DebugScope %50 +OpLine %5 7 5 +%75 = OpExtInst %void %1 DebugNoScope +OpReturnValue %32 +%if_merge = OpLabel +%76 = OpExtInst %void %1 DebugScope %51 +OpLine %5 9 16 +%63 = OpLoad %float %arg1 +OpStore %param_var_arg2 %63 +OpLine %5 9 10 +%64 = OpFunctionCall %v4float %func2 %param_var_arg2 +OpLine %5 9 3 +%77 = OpExtInst %void %1 DebugNoScope +OpReturnValue %64 +OpFunctionEnd +OpLine %5 1 1 +%func2 = OpFunction %v4float None %38 +OpLine %5 1 20 +%arg2 = OpFunctionParameter %_ptr_Function_float +%bb_entry_1 = OpLabel +%78 = OpExtInst %void %1 DebugScope %49 +OpLine %5 2 17 +%67 = OpLoad %float %arg2 +%68 = OpCompositeConstruct %v4float %67 %float_0 %float_0 %float_0 +OpLine %5 2 3 +%79 = OpExtInst %void %1 DebugNoScope +OpReturnValue %68 +OpFunctionEnd +)"); +} + +TEST(IrBuilder, ConsumeDebugInlinedAt) { + // /* HLSL */ + // + // float4 func2(float arg2) { // func2_block + // return float4(arg2, 0, 0, 0); + // } + // + // float4 func1(float arg1) { // func1_block + // if (arg1 > 1) { // if_true_block + // return float4(0, 0, 0, 0); + // } + // return func2(arg1); // if_merge_block + // } + // + // float4 main(float pos : POSITION) : SV_POSITION { // main + // return func1(pos); + // } + // + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): In the following + // SPIRV code, we use DebugInfoNone to reference opted-out function from + // DebugFunction similar to opted-out global variable for DebugGlobalVariable, + // but this is not a part of the spec yet. We are still in discussion and we + // must correct it if our decision is different. + DoRoundTripCheck(R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %pos %gl_Position +%5 = OpString "block/block.hlsl" +%6 = OpString "#line 1 \"block/block.hlsl\" +float4 func2(float arg2) { + return float4(arg2, 0, 0, 0); +} + +float4 func1(float arg1) { + if (arg1 > 1) { + return float4(0, 0, 0, 0); + } + return func2(arg1); +} + +float4 main(float pos : POSITION) : SV_POSITION { + return func1(pos); +} +" +OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\" +float4 func2(float arg2) { + return float4(arg2, 0, 0, 0); +} + +float4 func1(float arg1) { + if (arg1 > 1) { + return float4(0, 0, 0, 0); + } + return func2(arg1); +} + +float4 main(float pos : POSITION) : SV_POSITION { + return func1(pos); +} +" +%7 = OpString "float" +%8 = OpString "main" +%9 = OpString "v4f_main_f" +%10 = OpString "v4f_func1_f" +%11 = OpString "v4f_func2_f" +%12 = OpString "pos : POSITION" +%13 = OpString "func1" +%14 = OpString "func2" +OpName %main "main" +OpName %pos "pos" +OpName %bb_entry "bb.entry" +OpName %if_true "if.true" +OpName %if_merge "if.merge" +OpDecorate %gl_Position BuiltIn Position +OpDecorate %pos Location 0 +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%float_1 = OpConstant %float 1 +%float_0 = OpConstant %float 0 +%int_32 = OpConstant %int 32 +%v4float = OpTypeVector %float 4 +%24 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%28 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%30 = OpTypeFunction %v4float %_ptr_Function_float +%bool = OpTypeBool +OpLine %5 12 25 +%pos = OpVariable %_ptr_Input_float Input +OpLine %5 12 37 +%gl_Position = OpVariable %_ptr_Output_v4float Output +%32 = OpExtInst %void %1 DebugInfoNone +%33 = OpExtInst %void %1 DebugSource %5 %6 +%34 = OpExtInst %void %1 DebugCompilationUnit 2 4 %33 HLSL +%35 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float +%36 = OpExtInst %void %1 DebugTypeVector %35 4 +%37 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35 +%38 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35 +%39 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %36 %35 +%40 = OpExtInst %void %1 DebugFunction %8 %37 %33 12 1 %34 %9 FlagIsProtected|FlagIsPrivate 13 %main +%41 = OpExtInst %void %1 DebugFunction %13 %38 %33 5 1 %34 %10 FlagIsProtected|FlagIsPrivate 13 %32 +%42 = OpExtInst %void %1 DebugFunction %14 %39 %33 1 1 %34 %11 FlagIsProtected|FlagIsPrivate 13 %32 +%43 = OpExtInst %void %1 DebugLexicalBlock %33 12 49 %40 +%44 = OpExtInst %void %1 DebugLexicalBlock %33 5 26 %41 +%45 = OpExtInst %void %1 DebugLexicalBlock %33 1 26 %42 +%46 = OpExtInst %void %1 DebugLexicalBlock %33 6 17 %44 +%47 = OpExtInst %void %1 DebugLexicalBlock %33 9 3 %44 +%48 = OpExtInst %void %1 DebugInlinedAt 9 %47 +%49 = OpExtInst %void %1 DebugInlinedAt 13 %43 +%50 = OpExtInst %void %1 DebugInlinedAt 13 %43 %48 +OpLine %5 12 1 +%main = OpFunction %void None %28 +%bb_entry = OpLabel +%62 = OpExtInst %void %1 DebugScope %44 %49 +OpLine %5 6 7 +%52 = OpLoad %float %pos +OpLine %5 6 12 +%53 = OpFOrdGreaterThan %bool %52 %float_1 +OpLine %5 6 17 +%63 = OpExtInst %void %1 DebugNoScope +OpSelectionMerge %if_merge None +OpBranchConditional %53 %if_true %if_merge +%if_true = OpLabel +%64 = OpExtInst %void %1 DebugScope %46 %49 +OpLine %5 7 5 +OpStore %gl_Position %24 +%65 = OpExtInst %void %1 DebugNoScope +OpReturn +%if_merge = OpLabel +%66 = OpExtInst %void %1 DebugScope %45 %50 +OpLine %5 2 17 +%58 = OpLoad %float %pos +OpLine %5 2 10 +%59 = OpCompositeConstruct %v4float %58 %float_0 %float_0 %float_0 +%67 = OpExtInst %void %1 DebugScope %43 +OpLine %5 13 3 +OpStore %gl_Position %59 +%68 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +)"); +} + +TEST(IrBuilder, DebugInfoInstInFunctionOutOfBlock) { + // /* HLSL */ + // + // float4 func2(float arg2) { // func2_block + // return float4(arg2, 0, 0, 0); + // } + // + // float4 func1(float arg1) { // func1_block + // if (arg1 > 1) { // if_true_block + // return float4(0, 0, 0, 0); + // } + // return func2(arg1); // if_merge_block + // } + // + // float4 main(float pos : POSITION) : SV_POSITION { // main + // return func1(pos); + // } + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %pos %gl_Position +%5 = OpString "block/block.hlsl" +%6 = OpString "#line 1 \"block/block.hlsl\" +float4 func2(float arg2) { + return float4(arg2, 0, 0, 0); +} + +float4 func1(float arg1) { + if (arg1 > 1) { + return float4(0, 0, 0, 0); + } + return func2(arg1); +} + +float4 main(float pos : POSITION) : SV_POSITION { + return func1(pos); +} +" +OpSource HLSL 600 %5 "#line 1 \"block/block.hlsl\" +float4 func2(float arg2) { + return float4(arg2, 0, 0, 0); +} + +float4 func1(float arg1) { + if (arg1 > 1) { + return float4(0, 0, 0, 0); + } + return func2(arg1); +} + +float4 main(float pos : POSITION) : SV_POSITION { + return func1(pos); +} +" +%7 = OpString "float" +%8 = OpString "main" +%9 = OpString "v4f_main_f" +%10 = OpString "v4f_func1_f" +%11 = OpString "v4f_func2_f" +%12 = OpString "pos : POSITION" +%13 = OpString "func1" +%14 = OpString "func2" +OpName %main "main" +OpName %pos "pos" +OpName %bb_entry "bb.entry" +OpName %param_var_arg1 "param.var.arg1" +OpName %func1 "func1" +OpName %arg1 "arg1" +OpName %bb_entry_0 "bb.entry" +OpName %param_var_arg2 "param.var.arg2" +OpName %if_true "if.true" +OpName %if_merge "if.merge" +OpName %func2 "func2" +OpName %arg2 "arg2" +OpName %bb_entry_1 "bb.entry" +OpDecorate %gl_Position BuiltIn Position +OpDecorate %pos Location 0 +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%float_1 = OpConstant %float 1 +%float_0 = OpConstant %float 0 +%int_32 = OpConstant %int 32 +%v4float = OpTypeVector %float 4 +%32 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%36 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float +%38 = OpTypeFunction %v4float %_ptr_Function_float +%bool = OpTypeBool +OpLine %5 12 25 +%pos = OpVariable %_ptr_Input_float Input +OpLine %5 12 37 +%gl_Position = OpVariable %_ptr_Output_v4float Output +%40 = OpExtInst %void %1 DebugSource %5 %6 +%41 = OpExtInst %void %1 DebugCompilationUnit 2 4 %40 HLSL +%42 = OpExtInst %void %1 DebugTypeBasic %7 %int_32 Float +%43 = OpExtInst %void %1 DebugTypeVector %42 4 +%44 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42 +%45 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42 +%46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %42 +%47 = OpExtInst %void %1 DebugFunction %8 %44 %40 12 1 %41 %9 FlagIsProtected|FlagIsPrivate 13 %main +%48 = OpExtInst %void %1 DebugFunction %13 %45 %40 5 1 %41 %10 FlagIsProtected|FlagIsPrivate 13 %func1 +%49 = OpExtInst %void %1 DebugFunction %14 %46 %40 1 1 %41 %11 FlagIsProtected|FlagIsPrivate 13 %func2 +%50 = OpExtInst %void %1 DebugLexicalBlock %40 6 17 %48 +%51 = OpExtInst %void %1 DebugLexicalBlock %40 9 3 %48 +OpLine %5 12 1 +%main = OpFunction %void None %36 +%bb_entry = OpLabel +%70 = OpExtInst %void %1 DebugScope %47 +OpLine %5 13 16 +%param_var_arg1 = OpVariable %_ptr_Function_float Function +%53 = OpLoad %float %pos +OpStore %param_var_arg1 %53 +OpLine %5 13 10 +%54 = OpFunctionCall %v4float %func1 %param_var_arg1 +OpLine %5 13 3 +OpStore %gl_Position %54 +%71 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +OpLine %5 5 1 +%func1 = OpFunction %v4float None %38 +OpLine %5 5 20 +%arg1 = OpFunctionParameter %_ptr_Function_float +%bb_entry_0 = OpLabel +%72 = OpExtInst %void %1 DebugScope %48 +OpLine %5 9 16 +%param_var_arg2 = OpVariable %_ptr_Function_float Function +OpLine %5 6 7 +%57 = OpLoad %float %arg1 +OpLine %5 6 12 +%58 = OpFOrdGreaterThan %bool %57 %float_1 +OpLine %5 6 17 +%73 = OpExtInst %void %1 DebugNoScope +OpSelectionMerge %if_merge None +OpBranchConditional %58 %if_true %if_merge +%if_true = OpLabel +%74 = OpExtInst %void %1 DebugScope %50 +OpLine %5 7 5 +%75 = OpExtInst %void %1 DebugNoScope +OpReturnValue %32 +%if_merge = OpLabel +%76 = OpExtInst %void %1 DebugScope %51 +OpLine %5 9 16 +%63 = OpLoad %float %arg1 +OpStore %param_var_arg2 %63 +OpLine %5 9 10 +%64 = OpFunctionCall %v4float %func2 %param_var_arg2 +OpLine %5 9 3 +%77 = OpExtInst %void %1 DebugNoScope +OpReturnValue %64 +OpFunctionEnd +OpLine %5 1 1 +%func2 = OpFunction %v4float None %38 +OpLine %5 1 20 +%arg2 = OpFunctionParameter %_ptr_Function_float +%bb_entry_1 = OpLabel +%78 = OpExtInst %void %1 DebugScope %49 +OpLine %5 2 17 +%67 = OpLoad %float %arg2 +%68 = OpCompositeConstruct %v4float %67 %float_0 %float_0 %float_0 +OpLine %5 2 3 +%79 = OpExtInst %void %1 DebugNoScope +OpReturnValue %68 +OpFunctionEnd +)"; + + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + ASSERT_NE(nullptr, context); + + std::vector binary; + context->module()->ToBinary(&binary, /* skip_nop = */ false); + + std::string disassembled_text; + EXPECT_TRUE(t.Disassemble(binary, &disassembled_text)); + EXPECT_EQ(text, disassembled_text); +} + +TEST(IrBuilder, DebugInfoInstInFunctionOutOfBlock2) { + // /* HLSL */ + // + // struct VS_OUTPUT { + // float4 pos : SV_POSITION; + // float4 color : COLOR; + // }; + // + // VS_OUTPUT main(float4 pos : POSITION, + // float4 color : COLOR) { + // VS_OUTPUT vout; + // vout.pos = pos; + // vout.color = color; + // return vout; + // } + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR +%7 = OpString "vs.hlsl" +OpSource HLSL 600 %7 "#line 1 \"vs.hlsl\" +struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; + +VS_OUTPUT main(float4 pos : POSITION, + float4 color : COLOR) { + VS_OUTPUT vout; + vout.pos = pos; + vout.color = color; + return vout; +} +" +%8 = OpString "#line 1 \"vs.hlsl\" +struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; + +VS_OUTPUT main(float4 pos : POSITION, + float4 color : COLOR) { + VS_OUTPUT vout; + vout.pos = pos; + vout.color = color; + return vout; +} +" +%9 = OpString "VS_OUTPUT" +%10 = OpString "float" +%11 = OpString "src.main" +%12 = OpString "pos" +%13 = OpString "color" +%14 = OpString "vout" +OpName %in_var_POSITION "in.var.POSITION" +OpName %in_var_COLOR "in.var.COLOR" +OpName %out_var_COLOR "out.var.COLOR" +OpName %main "main" +OpName %param_var_pos "param.var.pos" +OpName %param_var_color "param.var.color" +OpName %VS_OUTPUT "VS_OUTPUT" +OpMemberName %VS_OUTPUT 0 "pos" +OpMemberName %VS_OUTPUT 1 "color" +OpName %src_main "src.main" +OpName %pos "pos" +OpName %color "color" +OpName %bb_entry "bb.entry" +OpName %vout "vout" +OpDecorate %gl_Position BuiltIn Position +OpDecorate %in_var_POSITION Location 0 +OpDecorate %in_var_COLOR Location 1 +OpDecorate %out_var_COLOR Location 0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%void = OpTypeVoid +%uint_256 = OpConstant %uint 256 +%uint_0 = OpConstant %uint 0 +%uint_128 = OpConstant %uint 128 +%36 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float +%VS_OUTPUT = OpTypeStruct %v4float %v4float +%38 = OpTypeFunction %VS_OUTPUT %_ptr_Function_v4float %_ptr_Function_v4float +%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT +OpLine %7 6 29 +%in_var_POSITION = OpVariable %_ptr_Input_v4float Input +OpLine %7 7 31 +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +OpLine %7 2 16 +%gl_Position = OpVariable %_ptr_Output_v4float Output +OpLine %7 3 18 +%out_var_COLOR = OpVariable %_ptr_Output_v4float Output +%40 = OpExtInst %void %1 DebugExpression +%41 = OpExtInst %void %1 DebugSource %7 %8 +%42 = OpExtInst %void %1 DebugCompilationUnit 1 4 %41 HLSL +%43 = OpExtInst %void %1 DebugTypeComposite %9 Structure %41 1 1 %42 %9 %uint_256 FlagIsProtected|FlagIsPrivate %44 %45 +%46 = OpExtInst %void %1 DebugTypeBasic %10 %uint_32 Float +%47 = OpExtInst %void %1 DebugTypeVector %46 4 +%48 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %43 %47 %47 +%49 = OpExtInst %void %1 DebugFunction %11 %48 %41 6 1 %42 %11 FlagIsProtected|FlagIsPrivate 7 %src_main +%50 = OpExtInst %void %1 DebugLocalVariable %12 %47 %41 6 23 %49 FlagIsLocal 0 +%51 = OpExtInst %void %1 DebugLocalVariable %13 %47 %41 7 23 %49 FlagIsLocal 1 +%52 = OpExtInst %void %1 DebugLexicalBlock %41 7 38 %49 +%53 = OpExtInst %void %1 DebugLocalVariable %14 %43 %41 8 13 %52 FlagIsLocal +%44 = OpExtInst %void %1 DebugTypeMember %12 %47 %41 2 3 %43 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate +%45 = OpExtInst %void %1 DebugTypeMember %13 %47 %41 3 3 %43 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate +OpLine %7 6 1 +%main = OpFunction %void None %36 +%54 = OpLabel +%74 = OpExtInst %void %1 DebugScope %42 +OpLine %7 6 23 +%param_var_pos = OpVariable %_ptr_Function_v4float Function +OpLine %7 7 23 +%param_var_color = OpVariable %_ptr_Function_v4float Function +OpLine %7 6 23 +%56 = OpLoad %v4float %in_var_POSITION +OpStore %param_var_pos %56 +OpLine %7 7 23 +%57 = OpLoad %v4float %in_var_COLOR +OpStore %param_var_color %57 +OpLine %7 6 1 +%58 = OpFunctionCall %VS_OUTPUT %src_main %param_var_pos %param_var_color +OpLine %7 6 11 +%59 = OpCompositeExtract %v4float %58 0 +OpLine %7 2 16 +OpStore %gl_Position %59 +OpLine %7 6 11 +%60 = OpCompositeExtract %v4float %58 1 +OpLine %7 3 18 +OpStore %out_var_COLOR %60 +%75 = OpExtInst %void %1 DebugNoScope +OpReturn +OpFunctionEnd +OpLine %7 6 1 +%src_main = OpFunction %VS_OUTPUT None %38 +%76 = OpExtInst %void %1 DebugScope %49 +OpLine %7 6 23 +%pos = OpFunctionParameter %_ptr_Function_v4float +OpLine %7 7 23 +%color = OpFunctionParameter %_ptr_Function_v4float +%63 = OpExtInst %void %1 DebugDeclare %50 %pos %40 +%64 = OpExtInst %void %1 DebugDeclare %51 %color %40 +%77 = OpExtInst %void %1 DebugNoScope +%bb_entry = OpLabel +%78 = OpExtInst %void %1 DebugScope %52 +OpLine %7 8 13 +%vout = OpVariable %_ptr_Function_VS_OUTPUT Function +%67 = OpExtInst %void %1 DebugDeclare %53 %vout %40 +OpLine %7 9 14 +%68 = OpLoad %v4float %pos +OpLine %7 9 3 +%69 = OpAccessChain %_ptr_Function_v4float %vout %int_0 +OpStore %69 %68 +OpLine %7 10 16 +%70 = OpLoad %v4float %color +OpLine %7 10 3 +%71 = OpAccessChain %_ptr_Function_v4float %vout %int_1 +OpStore %71 %70 +OpLine %7 11 10 +%72 = OpLoad %VS_OUTPUT %vout +OpLine %7 11 3 +%79 = OpExtInst %void %1 DebugNoScope +OpReturnValue %72 +OpFunctionEnd +)"; + + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + ASSERT_NE(nullptr, context); + + std::vector binary; + context->module()->ToBinary(&binary, /* skip_nop = */ false); + + std::string disassembled_text; + EXPECT_TRUE(t.Disassemble(binary, &disassembled_text)); + EXPECT_EQ(text, disassembled_text); +} + +TEST(IrBuilder, DebugInfoForTerminationInsts) { + // Check that DebugScope instructions for termination instructions are + // preserved. + DoRoundTripCheck(R"(OpCapability Shader +%1 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%3 = OpString "simple_vs.hlsl" +OpSource HLSL 600 %3 +OpName %main "main" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%6 = OpExtInst %void %1 DebugSource %3 +%7 = OpExtInst %void %1 DebugCompilationUnit 2 4 %6 HLSL +%main = OpFunction %void None %5 +%8 = OpLabel +%20 = OpExtInst %void %1 DebugScope %7 +OpBranch %10 +%21 = OpExtInst %void %1 DebugNoScope +%10 = OpLabel +%22 = OpExtInst %void %1 DebugScope %7 +OpKill +%23 = OpExtInst %void %1 DebugNoScope +%14 = OpLabel +%24 = OpExtInst %void %1 DebugScope %7 +OpUnreachable +%25 = OpExtInst %void %1 DebugNoScope +%17 = OpLabel +%26 = OpExtInst %void %1 DebugScope %7 +OpReturn +%27 = OpExtInst %void %1 DebugNoScope +OpFunctionEnd +)"); +} + +TEST(IrBuilder, LocalGlobalVariables) { + // #version 310 es + // + // float gv1 = 10.; + // float gv2 = 100.; + // + // float f() { + // float lv1 = gv1 + gv2; + // float lv2 = gv1 * gv2; + // return lv1 / lv2; + // } + // + // void main() { + // float lv1 = gv1 - gv2; + // } + DoRoundTripCheck( + // clang-format off + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %main \"main\"\n" + "OpSource ESSL 310\n" + "OpName %main \"main\"\n" + "OpName %f_ \"f(\"\n" + "OpName %gv1 \"gv1\"\n" + "OpName %gv2 \"gv2\"\n" + "OpName %lv1 \"lv1\"\n" + "OpName %lv2 \"lv2\"\n" + "OpName %lv1_0 \"lv1\"\n" + "%void = OpTypeVoid\n" + "%10 = OpTypeFunction %void\n" + "%float = OpTypeFloat 32\n" + "%12 = OpTypeFunction %float\n" + "%_ptr_Private_float = OpTypePointer Private %float\n" + "%gv1 = OpVariable %_ptr_Private_float Private\n" + "%float_10 = OpConstant %float 10\n" + "%gv2 = OpVariable %_ptr_Private_float Private\n" + "%float_100 = OpConstant %float 100\n" + "%_ptr_Function_float = OpTypePointer Function %float\n" + "%main = OpFunction %void None %10\n" + "%17 = OpLabel\n" + "%lv1_0 = OpVariable %_ptr_Function_float Function\n" + "OpStore %gv1 %float_10\n" + "OpStore %gv2 %float_100\n" + "%18 = OpLoad %float %gv1\n" + "%19 = OpLoad %float %gv2\n" + "%20 = OpFSub %float %18 %19\n" + "OpStore %lv1_0 %20\n" + "OpReturn\n" + "OpFunctionEnd\n" + "%f_ = OpFunction %float None %12\n" + "%21 = OpLabel\n" + "%lv1 = OpVariable %_ptr_Function_float Function\n" + "%lv2 = OpVariable %_ptr_Function_float Function\n" + "%22 = OpLoad %float %gv1\n" + "%23 = OpLoad %float %gv2\n" + "%24 = OpFAdd %float %22 %23\n" + "OpStore %lv1 %24\n" + "%25 = OpLoad %float %gv1\n" + "%26 = OpLoad %float %gv2\n" + "%27 = OpFMul %float %25 %26\n" + "OpStore %lv2 %27\n" + "%28 = OpLoad %float %lv1\n" + "%29 = OpLoad %float %lv2\n" + "%30 = OpFDiv %float %28 %29\n" + "OpReturnValue %30\n" + "OpFunctionEnd\n"); + // clang-format on +} + +TEST(IrBuilder, OpUndefOutsideFunction) { + // #version 310 es + // void main() {} + const std::string text = + // clang-format off + "OpMemoryModel Logical GLSL450\n" + "%int = OpTypeInt 32 1\n" + "%uint = OpTypeInt 32 0\n" + "%float = OpTypeFloat 32\n" + "%4 = OpUndef %int\n" + "%int_10 = OpConstant %int 10\n" + "%6 = OpUndef %uint\n" + "%bool = OpTypeBool\n" + "%8 = OpUndef %float\n" + "%double = OpTypeFloat 64\n"; + // clang-format on + + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + ASSERT_NE(nullptr, context); + + const auto opundef_count = std::count_if( + context->module()->types_values_begin(), + context->module()->types_values_end(), + [](const Instruction& inst) { return inst.opcode() == SpvOpUndef; }); + EXPECT_EQ(3, opundef_count); + + std::vector binary; + context->module()->ToBinary(&binary, /* skip_nop = */ false); + + std::string disassembled_text; + EXPECT_TRUE(t.Disassemble(binary, &disassembled_text)); + EXPECT_EQ(text, disassembled_text); +} + +TEST(IrBuilder, OpUndefInBasicBlock) { + DoRoundTripCheck( + // clang-format off + "OpMemoryModel Logical GLSL450\n" + "OpName %main \"main\"\n" + "%void = OpTypeVoid\n" + "%uint = OpTypeInt 32 0\n" + "%double = OpTypeFloat 64\n" + "%5 = OpTypeFunction %void\n" + "%main = OpFunction %void None %5\n" + "%6 = OpLabel\n" + "%7 = OpUndef %uint\n" + "%8 = OpUndef %double\n" + "OpReturn\n" + "OpFunctionEnd\n"); + // clang-format on +} + +TEST(IrBuilder, KeepLineDebugInfoBeforeType) { + DoRoundTripCheck( + // clang-format off + "OpCapability Shader\n" + "OpMemoryModel Logical GLSL450\n" + "%1 = OpString \"minimal.vert\"\n" + "OpLine %1 1 1\n" + "OpNoLine\n" + "%void = OpTypeVoid\n" + "OpLine %1 2 2\n" + "%3 = OpTypeFunction %void\n"); + // clang-format on +} + +TEST(IrBuilder, KeepLineDebugInfoBeforeLabel) { + DoRoundTripCheck( + // clang-format off + "OpCapability Shader\n" + "OpMemoryModel Logical GLSL450\n" + "%1 = OpString \"minimal.vert\"\n" + "%void = OpTypeVoid\n" + "%3 = OpTypeFunction %void\n" + "%4 = OpFunction %void None %3\n" + "%5 = OpLabel\n" + "OpBranch %6\n" + "OpLine %1 1 1\n" + "OpLine %1 2 2\n" + "%6 = OpLabel\n" + "OpBranch %7\n" + "OpLine %1 100 100\n" + "%7 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n"); + // clang-format on +} + +TEST(IrBuilder, KeepLineDebugInfoBeforeFunctionEnd) { + DoRoundTripCheck( + // clang-format off + "OpCapability Shader\n" + "OpMemoryModel Logical GLSL450\n" + "%1 = OpString \"minimal.vert\"\n" + "%void = OpTypeVoid\n" + "%3 = OpTypeFunction %void\n" + "%4 = OpFunction %void None %3\n" + "OpLine %1 1 1\n" + "OpLine %1 2 2\n" + "OpFunctionEnd\n"); + // clang-format on +} + +TEST(IrBuilder, KeepModuleProcessedInRightPlace) { + DoRoundTripCheck( + // clang-format off + "OpCapability Shader\n" + "OpMemoryModel Logical GLSL450\n" + "%1 = OpString \"minimal.vert\"\n" + "OpName %void \"void\"\n" + "OpModuleProcessed \"Made it faster\"\n" + "OpModuleProcessed \".. and smaller\"\n" + "%void = OpTypeVoid\n"); + // clang-format on +} + +// Checks the given |error_message| is reported when trying to build a module +// from the given |assembly|. +void DoErrorMessageCheck(const std::string& assembly, + const std::string& error_message, uint32_t line_num) { + auto consumer = [error_message, line_num](spv_message_level_t, const char*, + const spv_position_t& position, + const char* m) { + EXPECT_EQ(error_message, m); + EXPECT_EQ(line_num, position.line); + }; + + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, std::move(consumer), assembly); + EXPECT_EQ(nullptr, context); +} + +TEST(IrBuilder, FunctionInsideFunction) { + DoErrorMessageCheck("%2 = OpFunction %1 None %3\n%5 = OpFunction %4 None %6", + "function inside function", 2); +} + +TEST(IrBuilder, MismatchOpFunctionEnd) { + DoErrorMessageCheck("OpFunctionEnd", + "OpFunctionEnd without corresponding OpFunction", 1); +} + +TEST(IrBuilder, OpFunctionEndInsideBasicBlock) { + DoErrorMessageCheck( + "%2 = OpFunction %1 None %3\n" + "%4 = OpLabel\n" + "OpFunctionEnd", + "OpFunctionEnd inside basic block", 3); +} + +TEST(IrBuilder, BasicBlockOutsideFunction) { + DoErrorMessageCheck("OpCapability Shader\n%1 = OpLabel", + "OpLabel outside function", 2); +} + +TEST(IrBuilder, OpLabelInsideBasicBlock) { + DoErrorMessageCheck( + "%2 = OpFunction %1 None %3\n" + "%4 = OpLabel\n" + "%5 = OpLabel", + "OpLabel inside basic block", 3); +} + +TEST(IrBuilder, TerminatorOutsideFunction) { + DoErrorMessageCheck("OpReturn", "terminator instruction outside function", 1); +} + +TEST(IrBuilder, TerminatorOutsideBasicBlock) { + DoErrorMessageCheck("%2 = OpFunction %1 None %3\nOpReturn", + "terminator instruction outside basic block", 2); +} + +TEST(IrBuilder, NotAllowedInstAppearingInFunction) { + DoErrorMessageCheck("%2 = OpFunction %1 None %3\n%5 = OpVariable %4 Function", + "Non-OpFunctionParameter (opcode: 59) found inside " + "function but outside basic block", + 2); +} + +TEST(IrBuilder, UniqueIds) { + const std::string text = + // clang-format off + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %main \"main\"\n" + "OpSource ESSL 310\n" + "OpName %main \"main\"\n" + "OpName %f_ \"f(\"\n" + "OpName %gv1 \"gv1\"\n" + "OpName %gv2 \"gv2\"\n" + "OpName %lv1 \"lv1\"\n" + "OpName %lv2 \"lv2\"\n" + "OpName %lv1_0 \"lv1\"\n" + "%void = OpTypeVoid\n" + "%10 = OpTypeFunction %void\n" + "%float = OpTypeFloat 32\n" + "%12 = OpTypeFunction %float\n" + "%_ptr_Private_float = OpTypePointer Private %float\n" + "%gv1 = OpVariable %_ptr_Private_float Private\n" + "%float_10 = OpConstant %float 10\n" + "%gv2 = OpVariable %_ptr_Private_float Private\n" + "%float_100 = OpConstant %float 100\n" + "%_ptr_Function_float = OpTypePointer Function %float\n" + "%main = OpFunction %void None %10\n" + "%17 = OpLabel\n" + "%lv1_0 = OpVariable %_ptr_Function_float Function\n" + "OpStore %gv1 %float_10\n" + "OpStore %gv2 %float_100\n" + "%18 = OpLoad %float %gv1\n" + "%19 = OpLoad %float %gv2\n" + "%20 = OpFSub %float %18 %19\n" + "OpStore %lv1_0 %20\n" + "OpReturn\n" + "OpFunctionEnd\n" + "%f_ = OpFunction %float None %12\n" + "%21 = OpLabel\n" + "%lv1 = OpVariable %_ptr_Function_float Function\n" + "%lv2 = OpVariable %_ptr_Function_float Function\n" + "%22 = OpLoad %float %gv1\n" + "%23 = OpLoad %float %gv2\n" + "%24 = OpFAdd %float %22 %23\n" + "OpStore %lv1 %24\n" + "%25 = OpLoad %float %gv1\n" + "%26 = OpLoad %float %gv2\n" + "%27 = OpFMul %float %25 %26\n" + "OpStore %lv2 %27\n" + "%28 = OpLoad %float %lv1\n" + "%29 = OpLoad %float %lv2\n" + "%30 = OpFDiv %float %28 %29\n" + "OpReturnValue %30\n" + "OpFunctionEnd\n"; + // clang-format on + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + ASSERT_NE(nullptr, context); + + std::unordered_set ids; + context->module()->ForEachInst([&ids](const Instruction* inst) { + EXPECT_TRUE(ids.insert(inst->unique_id()).second); + }); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/iterator_test.cpp b/third_party/spirv-tools/test/opt/iterator_test.cpp new file mode 100644 index 0000000..d61bc1a --- /dev/null +++ b/third_party/spirv-tools/test/opt/iterator_test.cpp @@ -0,0 +1,267 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" + +#include "source/opt/iterator.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::ContainerEq; + +TEST(Iterator, IncrementDeref) { + const int count = 100; + std::vector> data; + for (int i = 0; i < count; ++i) { + data.emplace_back(new int(i)); + } + + UptrVectorIterator it(&data, data.begin()); + UptrVectorIterator end(&data, data.end()); + + EXPECT_EQ(*data[0], *it); + for (int i = 1; i < count; ++i) { + EXPECT_NE(end, it); + EXPECT_EQ(*data[i], *(++it)); + } + EXPECT_EQ(end, ++it); +} + +TEST(Iterator, DecrementDeref) { + const int count = 100; + std::vector> data; + for (int i = 0; i < count; ++i) { + data.emplace_back(new int(i)); + } + + UptrVectorIterator begin(&data, data.begin()); + UptrVectorIterator it(&data, data.end()); + + for (int i = count - 1; i >= 0; --i) { + EXPECT_NE(begin, it); + EXPECT_EQ(*data[i], *(--it)); + } + EXPECT_EQ(begin, it); +} + +TEST(Iterator, PostIncrementDeref) { + const int count = 100; + std::vector> data; + for (int i = 0; i < count; ++i) { + data.emplace_back(new int(i)); + } + + UptrVectorIterator it(&data, data.begin()); + UptrVectorIterator end(&data, data.end()); + + for (int i = 0; i < count; ++i) { + EXPECT_NE(end, it); + EXPECT_EQ(*data[i], *(it++)); + } + EXPECT_EQ(end, it); +} + +TEST(Iterator, PostDecrementDeref) { + const int count = 100; + std::vector> data; + for (int i = 0; i < count; ++i) { + data.emplace_back(new int(i)); + } + + UptrVectorIterator begin(&data, data.begin()); + UptrVectorIterator end(&data, data.end()); + UptrVectorIterator it(&data, data.end()); + + EXPECT_EQ(end, it--); + for (int i = count - 1; i >= 1; --i) { + EXPECT_EQ(*data[i], *(it--)); + } + // Decrementing .begin() is undefined behavior. + EXPECT_EQ(*data[0], *it); +} + +TEST(Iterator, Access) { + const int count = 100; + std::vector> data; + for (int i = 0; i < count; ++i) { + data.emplace_back(new int(i)); + } + + UptrVectorIterator it(&data, data.begin()); + + for (int i = 0; i < count; ++i) EXPECT_EQ(*data[i], it[i]); +} + +TEST(Iterator, Comparison) { + const int count = 100; + std::vector> data; + for (int i = 0; i < count; ++i) { + data.emplace_back(new int(i)); + } + + UptrVectorIterator it(&data, data.begin()); + UptrVectorIterator end(&data, data.end()); + + for (int i = 0; i < count; ++i, ++it) EXPECT_TRUE(it < end); + EXPECT_EQ(end, it); +} + +TEST(Iterator, InsertBeginEnd) { + const int count = 100; + + std::vector> data; + std::vector expected; + std::vector actual; + + for (int i = 0; i < count; ++i) { + data.emplace_back(new int(i)); + expected.push_back(i); + } + + // Insert at the beginning + expected.insert(expected.begin(), -100); + UptrVectorIterator begin(&data, data.begin()); + auto insert_point = begin.InsertBefore(MakeUnique(-100)); + for (int i = 0; i < count + 1; ++i) { + actual.push_back(*(insert_point++)); + } + EXPECT_THAT(actual, ContainerEq(expected)); + + // Insert at the end + expected.push_back(-42); + expected.push_back(-36); + expected.push_back(-77); + UptrVectorIterator end(&data, data.end()); + end = end.InsertBefore(MakeUnique(-77)); + end = end.InsertBefore(MakeUnique(-36)); + end = end.InsertBefore(MakeUnique(-42)); + + actual.clear(); + begin = UptrVectorIterator(&data, data.begin()); + for (int i = 0; i < count + 4; ++i) { + actual.push_back(*(begin++)); + } + EXPECT_THAT(actual, ContainerEq(expected)); +} + +TEST(Iterator, InsertMiddle) { + const int count = 100; + + std::vector> data; + std::vector expected; + std::vector actual; + + for (int i = 0; i < count; ++i) { + data.emplace_back(new int(i)); + expected.push_back(i); + } + + const int insert_pos = 42; + expected.insert(expected.begin() + insert_pos, -100); + expected.insert(expected.begin() + insert_pos, -42); + + UptrVectorIterator it(&data, data.begin()); + for (int i = 0; i < insert_pos; ++i) ++it; + it = it.InsertBefore(MakeUnique(-100)); + it = it.InsertBefore(MakeUnique(-42)); + auto begin = UptrVectorIterator(&data, data.begin()); + for (int i = 0; i < count + 2; ++i) { + actual.push_back(*(begin++)); + } + EXPECT_THAT(actual, ContainerEq(expected)); +} + +TEST(IteratorRange, Interface) { + const uint32_t count = 100; + + std::vector> data; + + for (uint32_t i = 0; i < count; ++i) { + data.emplace_back(new uint32_t(i)); + } + + auto b = UptrVectorIterator(&data, data.begin()); + auto e = UptrVectorIterator(&data, data.end()); + auto range = IteratorRange(b, e); + + EXPECT_EQ(b, range.begin()); + EXPECT_EQ(e, range.end()); + EXPECT_FALSE(range.empty()); + EXPECT_EQ(count, range.size()); + EXPECT_EQ(0u, *range.begin()); + EXPECT_EQ(99u, *(--range.end())); + + // IteratorRange itself is immutable. + ++b, --e; + EXPECT_EQ(count, range.size()); + ++range.begin(), --range.end(); + EXPECT_EQ(count, range.size()); +} + +TEST(Iterator, FilterIterator) { + struct Placeholder { + int val; + }; + std::vector data = {{1}, {2}, {3}, {4}, {5}, + {6}, {7}, {8}, {9}, {10}}; + + // Predicate to only consider odd values. + struct Predicate { + bool operator()(const Placeholder& data) { return data.val % 2; } + }; + Predicate pred; + + auto filter_range = MakeFilterIteratorRange(data.begin(), data.end(), pred); + + EXPECT_EQ(filter_range.begin().Get(), data.begin()); + EXPECT_EQ(filter_range.end(), filter_range.begin().GetEnd()); + + for (Placeholder& data : filter_range) { + EXPECT_EQ(data.val % 2, 1); + } + + for (auto it = filter_range.begin(); it != filter_range.end(); it++) { + EXPECT_EQ(it->val % 2, 1); + EXPECT_EQ((*it).val % 2, 1); + } + + for (auto it = filter_range.begin(); it != filter_range.end(); ++it) { + EXPECT_EQ(it->val % 2, 1); + EXPECT_EQ((*it).val % 2, 1); + } + + EXPECT_EQ(MakeFilterIterator(data.begin(), data.end(), pred).Get(), + data.begin()); + EXPECT_EQ(MakeFilterIterator(data.end(), data.end(), pred).Get(), data.end()); + EXPECT_EQ(MakeFilterIterator(data.begin(), data.end(), pred).GetEnd(), + MakeFilterIterator(data.end(), data.end(), pred)); + EXPECT_NE(MakeFilterIterator(data.begin(), data.end(), pred), + MakeFilterIterator(data.end(), data.end(), pred)); + + // Empty range: no values satisfies the predicate. + auto empty_range = MakeFilterIteratorRange( + data.begin(), data.end(), + [](const Placeholder& data) { return data.val > 10; }); + EXPECT_EQ(empty_range.begin(), empty_range.end()); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/legalize_vector_shuffle_test.cpp b/third_party/spirv-tools/test/opt/legalize_vector_shuffle_test.cpp new file mode 100644 index 0000000..07d96eb --- /dev/null +++ b/third_party/spirv-tools/test/opt/legalize_vector_shuffle_test.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using LegalizeVectorShuffleTest = PassTest<::testing::Test>; + +void operator+=(std::vector& lhs, const char* rhs) { + lhs.push_back(rhs); +} + +void operator+=(std::vector& lhs, + const std::vector rhs) { + for (auto elem : rhs) lhs.push_back(elem); +} + +std::vector header = { + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", + "%v3uint = OpTypeVector %uint 3"}; + +std::string GetTestString(const char* shuffle) { + std::vector result = header; + result += {"%_ptr_Function_v3uint = OpTypePointer Function %v3uint", + "%void = OpTypeVoid", + "%6 = OpTypeFunction %void", + "%1 = OpFunction %void None %6", + "%7 = OpLabel", + "%8 = OpVariable %_ptr_Function_v3uint Function", + "%9 = OpLoad %v3uint %8", + "%10 = OpLoad %v3uint %8"}; + result += shuffle; + result += {"OpReturn", "OpFunctionEnd"}; + return JoinAllInsts(result); +} + +TEST_F(LegalizeVectorShuffleTest, Changed) { + std::string input = + GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0xFFFFFFFF"); + std::string expected = + GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0"); + + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +TEST_F(LegalizeVectorShuffleTest, FunctionUnchanged) { + std::string input = + GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0"); + std::string expected = + GetTestString("%11 = OpVectorShuffle %v3uint %9 %10 2 1 0"); + + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/line_debug_info_test.cpp b/third_party/spirv-tools/test/opt/line_debug_info_test.cpp new file mode 100644 index 0000000..6a20a01 --- /dev/null +++ b/third_party/spirv-tools/test/opt/line_debug_info_test.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +// A pass turning all none debug line instructions into Nop. +class NopifyPass : public Pass { + public: + const char* name() const override { return "NopifyPass"; } + Status Process() override { + bool modified = false; + context()->module()->ForEachInst( + [&modified](Instruction* inst) { + inst->ToNop(); + modified = true; + }, + /* run_on_debug_line_insts = */ false); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; + } +}; + +using PassTestForLineDebugInfo = PassTest<::testing::Test>; + +// This test's purpose to show our implementation choice: line debug info is +// preserved even if the following instruction is killed. It serves as a guard +// of potential behavior changes. +TEST_F(PassTestForLineDebugInfo, KeepLineDebugInfo) { + // clang-format off + const char* text = + "OpCapability Shader " + "%1 = OpExtInstImport \"GLSL.std.450\" " + "OpMemoryModel Logical GLSL450 " + "OpEntryPoint Vertex %2 \"main\" " + "%3 = OpString \"minimal.vert\" " + "OpNoLine " + "OpLine %3 10 10 " + "%void = OpTypeVoid " + "OpLine %3 100 100 " + "%5 = OpTypeFunction %void " + "%2 = OpFunction %void None %5 " + "OpLine %3 1 1 " + "OpNoLine " + "OpLine %3 2 2 " + "OpLine %3 3 3 " + "%6 = OpLabel " + "OpLine %3 4 4 " + "OpNoLine " + "OpReturn " + "OpLine %3 4 4 " + "OpNoLine " + "OpFunctionEnd "; + // clang-format on + + const char* result_keep_nop = + "OpNop\n" + "OpNop\n" + "OpNop\n" + "OpNop\n" + "OpNop\n" + "OpNoLine\n" + "OpLine %3 10 10\n" + "OpNop\n" + "OpLine %3 100 100\n" + "OpNop\n" + "OpNop\n" + "OpLine %3 1 1\n" + "OpNoLine\n" + "OpLine %3 2 2\n" + "OpLine %3 3 3\n" + "OpNop\n" + "OpLine %3 4 4\n" + "OpNoLine\n" + "OpNop\n" + "OpLine %3 4 4\n" + "OpNoLine\n" + "OpNop\n"; + SinglePassRunAndCheck(text, result_keep_nop, + /* skip_nop = */ false); + const char* result_skip_nop = + "OpNoLine\n" + "OpLine %3 10 10\n" + "OpLine %3 100 100\n" + "OpLine %3 1 1\n" + "OpNoLine\n" + "OpLine %3 2 2\n" + "OpLine %3 3 3\n" + "OpLine %3 4 4\n" + "OpNoLine\n" + "OpLine %3 4 4\n" + "OpNoLine\n"; + SinglePassRunAndCheck(text, result_skip_nop, + /* skip_nop = */ true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/local_access_chain_convert_test.cpp b/third_party/spirv-tools/test/opt/local_access_chain_convert_test.cpp new file mode 100644 index 0000000..6fcf23f --- /dev/null +++ b/third_party/spirv-tools/test/opt/local_access_chain_convert_test.cpp @@ -0,0 +1,1171 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using LocalAccessChainConvertTest = PassTest<::testing::Test>; + +TEST_F(LocalAccessChainConvertTest, StructOfVecsOfFloatConverted) { + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // gl_FragColor = s0.v1; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"( +; CHECK: [[st_id:%\w+]] = OpLoad %v4float %BaseColor +; CHECK: [[ld1:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex1:%\w+]] = OpCompositeInsert %S_t [[st_id]] [[ld1]] 1 +; CHECK: OpStore %s0 [[ex1]] +; CHECK: [[ld2:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex2:%\w+]] = OpCompositeExtract %v4float [[ld2]] 1 +; CHECK: OpStore %gl_FragColor [[ex2]] +%main = OpFunction %void None %8 +%17 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +%19 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +OpStore %19 %18 +%20 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +%21 = OpLoad %v4float %20 +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs_before + before, + true); +} + +TEST_F(LocalAccessChainConvertTest, DebugScopeAndLineInfoForNewInstructions) { + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // gl_FragColor = s0.v1; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%5 = OpString "ps.hlsl" +%6 = OpString "float" +%var_name = OpString "s0" +%main_name = OpString "main" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_32 = OpConstant %int 32 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%20 = OpExtInst %void %ext DebugSource %5 +%21 = OpExtInst %void %ext DebugCompilationUnit 1 4 %20 HLSL +%22 = OpExtInst %void %ext DebugTypeBasic %6 %int_32 Float +%23 = OpExtInst %void %ext DebugTypeVector %22 4 +%24 = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %24 %20 4 1 %21 %main_name FlagIsProtected|FlagIsPrivate 4 %main +%25 = OpExtInst %void %ext DebugLocalVariable %var_name %23 %20 0 0 %dbg_main FlagIsLocal +)"; + + const std::string before = + R"( +; CHECK: [[st_id:%\w+]] = OpLoad %v4float %BaseColor +; CHECK: OpLine {{%\w+}} 1 0 +; CHECK: [[ld1:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex1:%\w+]] = OpCompositeInsert %S_t [[st_id]] [[ld1]] 1 +; CHECK: OpStore %s0 [[ex1]] +; CHECK: OpLine {{%\w+}} 3 0 +; CHECK: [[ld2:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex2:%\w+]] = OpCompositeExtract %v4float [[ld2]] 1 +; CHECK: OpLine {{%\w+}} 4 0 +; CHECK: OpStore %gl_FragColor [[ex2]] +%main = OpFunction %void None %8 +%17 = OpLabel +%26 = OpExtInst %void %ext DebugScope %dbg_main +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +OpLine %5 0 0 +%19 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +OpLine %5 1 0 +OpStore %19 %18 +OpLine %5 2 0 +%27 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +OpLine %5 3 0 +%28 = OpLoad %v4float %27 +OpLine %5 4 0 +OpStore %gl_FragColor %28 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs_before + before, + true); +} + +TEST_F(LocalAccessChainConvertTest, TestTargetsReferencedByDebugValue) { + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // gl_FragColor = s0.v1; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%5 = OpString "ps.hlsl" +%6 = OpString "float" +%var_name = OpString "s0" +%main_name = OpString "main" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_32 = OpConstant %int 32 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%deref = OpExtInst %void %ext DebugOperation Deref +%deref_expr = OpExtInst %void %ext DebugExpression %deref +%null_expr = OpExtInst %void %ext DebugExpression +%20 = OpExtInst %void %ext DebugSource %5 +%21 = OpExtInst %void %ext DebugCompilationUnit 1 4 %20 HLSL +%22 = OpExtInst %void %ext DebugTypeBasic %6 %int_32 Float +%23 = OpExtInst %void %ext DebugTypeVector %22 4 +%24 = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %24 %20 4 1 %21 %main_name FlagIsProtected|FlagIsPrivate 4 %main +%25 = OpExtInst %void %ext DebugLocalVariable %var_name %23 %20 0 0 %dbg_main FlagIsLocal +)"; + + const std::string before = + R"( +; CHECK: [[st_id:%\w+]] = OpLoad %v4float %BaseColor +; CHECK: OpLine {{%\w+}} 0 0 +; CHECK: [[s0_1_ptr:%\w+]] = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +; CHECK: DebugValue [[dbg_s0:%\w+]] [[s0_1_ptr]] +; CHECK: OpLine {{%\w+}} 1 0 +; CHECK: [[s0:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[comp:%\w+]] = OpCompositeInsert %S_t [[st_id]] [[s0]] 1 +; CHECK: OpStore %s0 [[comp]] +; CHECK: OpLine {{%\w+}} 2 0 +; CHECK: [[s0_2_ptr:%\w+]] = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +; CHECK: OpLine {{%\w+}} 3 0 +; CHECK: [[s0:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[s0_2_val:%\w+]] = OpCompositeExtract %v4float [[s0]] 1 +; CHECK: DebugValue [[dbg_s0]] [[s0_2_val]] +; CHECK: OpLine {{%\w+}} 4 0 +; CHECK: OpStore %gl_FragColor [[s0_2_val]] +%main = OpFunction %void None %8 +%17 = OpLabel +%26 = OpExtInst %void %ext DebugScope %dbg_main +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +OpLine %5 0 0 +%19 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +%29 = OpExtInst %void %ext DebugValue %25 %19 %deref_expr %int_1 +OpLine %5 1 0 +OpStore %19 %18 +OpLine %5 2 0 +%27 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +OpLine %5 3 0 +%28 = OpLoad %v4float %27 +%30 = OpExtInst %void %ext DebugValue %25 %28 %null_expr %int_1 +OpLine %5 4 0 +OpStore %gl_FragColor %28 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs_before + before, + true); +} + +TEST_F(LocalAccessChainConvertTest, InBoundsAccessChainsConverted) { + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // gl_FragColor = s0.v1; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"( +; CHECK: [[st_id:%\w+]] = OpLoad %v4float %BaseColor +; CHECK: [[ld1:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex1:%\w+]] = OpCompositeInsert %S_t [[st_id]] [[ld1]] 1 +; CHECK: OpStore %s0 [[ex1]] +; CHECK: [[ld2:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex2:%\w+]] = OpCompositeExtract %v4float [[ld2]] 1 +; CHECK: OpStore %gl_FragColor [[ex2]] +%main = OpFunction %void None %8 +%17 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +%19 = OpInBoundsAccessChain %_ptr_Function_v4float %s0 %int_1 +OpStore %19 %18 +%20 = OpInBoundsAccessChain %_ptr_Function_v4float %s0 %int_1 +%21 = OpLoad %v4float %20 +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs_before + before, + true); +} + +TEST_F(LocalAccessChainConvertTest, TwoUsesofSingleChainConverted) { + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // gl_FragColor = s0.v1; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"( +; CHECK: [[st_id:%\w+]] = OpLoad %v4float %BaseColor +; CHECK: [[ld1:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex1:%\w+]] = OpCompositeInsert %S_t [[st_id]] [[ld1]] 1 +; CHECK: OpStore %s0 [[ex1]] +; CHECK: [[ld2:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex2:%\w+]] = OpCompositeExtract %v4float [[ld2]] 1 +; CHECK: OpStore %gl_FragColor [[ex2]] +%main = OpFunction %void None %8 +%17 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +%19 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +OpStore %19 %18 +%20 = OpLoad %v4float %19 +OpStore %gl_FragColor %20 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs_before + before, + true); +} + +TEST_F(LocalAccessChainConvertTest, OpaqueConverted) { + // SPIR-V not representable in GLSL; not generatable from HLSL + // at the moment + + const std::string predefs = + R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %outColor %texCoords +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %foo_struct_S_t_vf2_vf21_ "foo(struct-S_t-vf2-vf21;" +OpName %s "s" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpName %param "param" +OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%17 = OpTypeImage %float 2D 0 0 0 1 Unknown +%18 = OpTypeSampledImage %17 +%S_t = OpTypeStruct %v2float %v2float %18 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%20 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%_ptr_Function_18 = OpTypePointer Function %18 +%sampler15 = OpVariable %_ptr_UniformConstant_18 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string before = + R"( +; CHECK: [[l1:%\w+]] = OpLoad %S_t %param +; CHECK: [[e1:%\w+]] = OpCompositeExtract {{%\w+}} [[l1]] 2 +; CHECK: [[l2:%\w+]] = OpLoad %S_t %param +; CHECK: [[e2:%\w+]] = OpCompositeExtract {{%\w+}} [[l2]] 0 +; CHECK: OpImageSampleImplicitLod {{%\w+}} [[e1]] [[e2]] +%main = OpFunction %void None %12 +%28 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%29 = OpLoad %v2float %texCoords +%30 = OpAccessChain %_ptr_Function_v2float %s0 %int_0 +OpStore %30 %29 +%31 = OpLoad %18 %sampler15 +%32 = OpAccessChain %_ptr_Function_18 %s0 %int_2 +OpStore %32 %31 +%33 = OpLoad %S_t %s0 +OpStore %param %33 +%34 = OpAccessChain %_ptr_Function_18 %param %int_2 +%35 = OpLoad %18 %34 +%36 = OpAccessChain %_ptr_Function_v2float %param %int_0 +%37 = OpLoad %v2float %36 +%38 = OpImageSampleImplicitLod %v4float %35 %37 +OpStore %outColor %38 +OpReturn +OpFunctionEnd +)"; + + const std::string remain = + R"(%foo_struct_S_t_vf2_vf21_ = OpFunction %void None %20 +%s = OpFunctionParameter %_ptr_Function_S_t +%39 = OpLabel +%40 = OpAccessChain %_ptr_Function_18 %s %int_2 +%41 = OpLoad %18 %40 +%42 = OpAccessChain %_ptr_Function_v2float %s %int_0 +%43 = OpLoad %v2float %42 +%44 = OpImageSampleImplicitLod %v4float %41 %43 +OpStore %outColor %44 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + before + remain, + true); +} + +TEST_F(LocalAccessChainConvertTest, NestedStructsConverted) { + // #version 140 + // + // in vec4 BaseColor; + // + // struct S1_t { + // vec4 v1; + // }; + // + // struct S2_t { + // vec4 v2; + // S1_t s1; + // }; + // + // void main() + // { + // S2_t s2; + // s2.s1.v1 = BaseColor; + // gl_FragColor = s2.s1.v1; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S1_t "S1_t" +OpMemberName %S1_t 0 "v1" +OpName %S2_t "S2_t" +OpMemberName %S2_t 0 "v2" +OpMemberName %S2_t 1 "s1" +OpName %s2 "s2" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S1_t = OpTypeStruct %v4float +%S2_t = OpTypeStruct %v4float %S1_t +%_ptr_Function_S2_t = OpTypePointer Function %S2_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%int_0 = OpConstant %int 0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"( +; CHECK: [[st_id:%\w+]] = OpLoad %v4float %BaseColor +; CHECK: [[ld1:%\w+]] = OpLoad %S2_t %s2 +; CHECK: [[ex1:%\w+]] = OpCompositeInsert %S2_t [[st_id]] [[ld1]] 1 0 +; CHECK: OpStore %s2 [[ex1]] +; CHECK: [[ld2:%\w+]] = OpLoad %S2_t %s2 +; CHECK: [[ex2:%\w+]] = OpCompositeExtract %v4float [[ld2]] 1 0 +; CHECK: OpStore %gl_FragColor [[ex2]] +%main = OpFunction %void None %9 +%19 = OpLabel +%s2 = OpVariable %_ptr_Function_S2_t Function +%20 = OpLoad %v4float %BaseColor +%21 = OpAccessChain %_ptr_Function_v4float %s2 %int_1 %int_0 +OpStore %21 %20 +%22 = OpAccessChain %_ptr_Function_v4float %s2 %int_1 %int_0 +%23 = OpLoad %v4float %22 +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs_before + before, + true); +} + +TEST_F(LocalAccessChainConvertTest, SomeAccessChainsHaveNoUse) { + // Based on HLSL source code: + // struct S { + // float f; + // }; + + // float main(float input : A) : B { + // S local = { input }; + // return local.f; + // } + + const std::string predefs = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" %in_var_A %out_var_B +OpName %main "main" +OpName %in_var_A "in.var.A" +OpName %out_var_B "out.var.B" +OpName %S "S" +OpName %local "local" +%int = OpTypeInt 32 1 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_float = OpTypePointer Output %float +%S = OpTypeStruct %float +%_ptr_Function_S = OpTypePointer Function %S +%int_0 = OpConstant %int 0 +%in_var_A = OpVariable %_ptr_Input_float Input +%out_var_B = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %8 +%15 = OpLabel +%local = OpVariable %_ptr_Function_S Function +%16 = OpLoad %float %in_var_A +%17 = OpCompositeConstruct %S %16 +OpStore %local %17 +)"; + + const std::string before = + R"( +; CHECK: [[ld:%\w+]] = OpLoad %S %local +; CHECK: [[ex:%\w+]] = OpCompositeExtract %float [[ld]] 0 +; CHECK: OpStore %out_var_B [[ex]] +%18 = OpAccessChain %_ptr_Function_float %local %int_0 +%19 = OpAccessChain %_ptr_Function_float %local %int_0 +%20 = OpLoad %float %18 +OpStore %out_var_B %20 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + before, true); +} + +TEST_F(LocalAccessChainConvertTest, + StructOfVecsOfFloatConvertedWithDecorationOnLoad) { + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // gl_FragColor = s0.v1; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %21 RelaxedPrecision +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"( +; CHECK: OpDecorate +; CHECK: OpDecorate [[ld2:%\w+]] RelaxedPrecision +; CHECK-NOT: OpDecorate +; CHECK: [[st_id:%\w+]] = OpLoad %v4float %BaseColor +; CHECK: [[ld1:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ins:%\w+]] = OpCompositeInsert %S_t [[st_id]] [[ld1]] 1 +; CHECK: OpStore %s0 [[ins]] +; CHECK: [[ld2]] = OpLoad %S_t %s0 +; CHECK: [[ex2:%\w+]] = OpCompositeExtract %v4float [[ld2]] 1 +; CHECK: OpStore %gl_FragColor [[ex2]] +%main = OpFunction %void None %8 +%17 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +%19 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +OpStore %19 %18 +%20 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +%21 = OpLoad %v4float %20 +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs_before + before, + true); +} + +TEST_F(LocalAccessChainConvertTest, + StructOfVecsOfFloatConvertedWithDecorationOnStore) { + // #version 140 + // + // in vec4 BaseColor; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // gl_FragColor = s0.v1; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %s0 RelaxedPrecision +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"( +; CHECK: OpDecorate +; CHECK: OpDecorate [[ld1:%\w+]] RelaxedPrecision +; CHECK: OpDecorate [[ins:%\w+]] RelaxedPrecision +; CHECK-NOT: OpDecorate +; CHECK: [[st_id:%\w+]] = OpLoad %v4float %BaseColor +; CHECK: [[ld1]] = OpLoad %S_t %s0 +; CHECK: [[ins]] = OpCompositeInsert %S_t [[st_id]] [[ld1]] 1 +; CHECK: OpStore %s0 [[ins]] +; CHECK: [[ld2:%\w+]] = OpLoad %S_t %s0 +; CHECK: [[ex2:%\w+]] = OpCompositeExtract %v4float [[ld2]] 1 +; CHECK: OpStore %gl_FragColor [[ex2]] +%main = OpFunction %void None %8 +%17 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%18 = OpLoad %v4float %BaseColor +%19 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +OpStore %19 %18 +%20 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +%21 = OpLoad %v4float %20 +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs_before + before, + true); +} + +TEST_F(LocalAccessChainConvertTest, DynamicallyIndexedVarNotConverted) { + // #version 140 + // + // in vec4 BaseColor; + // flat in int Idx; + // in float Bi; + // + // struct S_t { + // vec4 v0; + // vec4 v1; + // }; + // + // void main() + // { + // S_t s0; + // s0.v1 = BaseColor; + // s0.v1[Idx] = Bi; + // gl_FragColor = s0.v1; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Idx %Bi %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpName %s0 "s0" +OpName %BaseColor "BaseColor" +OpName %Idx "Idx" +OpName %Bi "Bi" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %Idx Flat +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%S_t = OpTypeStruct %v4float %v4float +%_ptr_Function_S_t = OpTypePointer Function %S_t +%int = OpTypeInt 32 1 +%int_1 = OpConstant %int 1 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_int = OpTypePointer Input %int +%Idx = OpVariable %_ptr_Input_int Input +%_ptr_Input_float = OpTypePointer Input %float +%Bi = OpVariable %_ptr_Input_float Input +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %10 +%22 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%23 = OpLoad %v4float %BaseColor +%24 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +OpStore %24 %23 +%25 = OpLoad %int %Idx +%26 = OpLoad %float %Bi +%27 = OpAccessChain %_ptr_Function_float %s0 %int_1 %25 +OpStore %27 %26 +%28 = OpAccessChain %_ptr_Function_v4float %s0 %int_1 +%29 = OpLoad %v4float %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, false, + true); +} + +TEST_F(LocalAccessChainConvertTest, VariablePointersStorageBuffer) { + // A case with a storage buffer variable pointer. We should still convert + // the access chain on the function scope symbol. + const std::string test = + R"( +; CHECK: OpFunction +; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Function +; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[var]] +; CHECK: OpCompositeExtract {{%\w+}} [[ld]] 0 0 + OpCapability Shader + OpCapability VariablePointersStorageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_struct_3 Block + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 0 + OpDecorate %_ptr_StorageBuffer_int ArrayStride 4 + OpDecorate %_arr_int_int_128 ArrayStride 4 + %void = OpTypeVoid + %8 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_128 = OpConstant %int 128 +%_arr_int_int_128 = OpTypeArray %int %int_128 + %_struct_3 = OpTypeStruct %_arr_int_int_128 +%_ptr_StorageBuffer__struct_3 = OpTypePointer StorageBuffer %_struct_3 +%_ptr_Function__struct_3 = OpTypePointer Function %_struct_3 + %4 = OpVariable %_ptr_StorageBuffer__struct_3 StorageBuffer + %bool = OpTypeBool + %true = OpConstantTrue %bool + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int +%_ptr_Function_int = OpTypePointer Function %int + %2 = OpFunction %void None %8 + %18 = OpLabel + %19 = OpVariable %_ptr_Function__struct_3 Function + %20 = OpAccessChain %_ptr_StorageBuffer_int %4 %int_0 %int_0 + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %_ptr_StorageBuffer_int %20 %18 %23 %24 + OpLoopMerge %25 %24 None + OpBranchConditional %true %26 %25 + %26 = OpLabel + OpStore %22 %int_0 + OpBranch %24 + %24 = OpLabel + %23 = OpPtrAccessChain %_ptr_StorageBuffer_int %22 %int_1 + OpBranch %21 + %25 = OpLabel + %27 = OpAccessChain %_ptr_Function_int %19 %int_0 %int_0 + %28 = OpLoad %int %27 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(test, true); +} + +TEST_F(LocalAccessChainConvertTest, VariablePointers) { + // A case with variable pointer capability. We should not convert + // the access chain on the function scope symbol because the variable pointer + // could the analysis to miss references to function scope symbols. + const std::string test = + R"(OpCapability Shader +OpCapability VariablePointers +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "main" +OpExecutionMode %2 LocalSize 1 1 1 +OpSource GLSL 450 +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_struct_3 Block +OpDecorate %4 DescriptorSet 0 +OpDecorate %4 Binding 0 +OpDecorate %_ptr_StorageBuffer_int ArrayStride 4 +OpDecorate %_arr_int_int_128 ArrayStride 4 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_128 = OpConstant %int 128 +%_arr_int_int_128 = OpTypeArray %int %int_128 +%_struct_3 = OpTypeStruct %_arr_int_int_128 +%_ptr_StorageBuffer__struct_3 = OpTypePointer StorageBuffer %_struct_3 +%_ptr_Function__struct_3 = OpTypePointer Function %_struct_3 +%4 = OpVariable %_ptr_StorageBuffer__struct_3 StorageBuffer +%bool = OpTypeBool +%true = OpConstantTrue %bool +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int +%_ptr_Function_int = OpTypePointer Function %int +%2 = OpFunction %void None %8 +%18 = OpLabel +%19 = OpVariable %_ptr_Function__struct_3 Function +%20 = OpAccessChain %_ptr_StorageBuffer_int %4 %int_0 %int_0 +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %_ptr_StorageBuffer_int %20 %18 %23 %24 +OpLoopMerge %25 %24 None +OpBranchConditional %true %26 %25 +%26 = OpLabel +OpStore %22 %int_0 +OpBranch %24 +%24 = OpLabel +%23 = OpPtrAccessChain %_ptr_StorageBuffer_int %22 %int_1 +OpBranch %21 +%25 = OpLabel +%27 = OpAccessChain %_ptr_Function_int %19 %int_0 %int_0 +%28 = OpLoad %int %27 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(test, test, false, true); +} + +TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingLoad) { + const std::string text = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "PSMain" + OpExecutionMode %4 OriginUpperLeft + OpDecorate %10 Location 47360 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %_struct_8 = OpTypeStruct %v4float +%_ptr_Function__struct_8 = OpTypePointer Function %_struct_8 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %4 = OpFunction %void None %3 + %5 = OpLabel + %10 = OpVariable %_ptr_Function__struct_8 Function + %4194301 = OpAccessChain %_ptr_Function_v4float %10 %int_0 + %4194302 = OpLoad %v4float %4194301 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingStore1) { + const std::string text = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "PSMain" + OpExecutionMode %4 OriginUpperLeft + OpDecorate %10 Location 47360 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %_struct_7 = OpTypeStruct %v4float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %13 = OpConstantNull %v4float + %4 = OpFunction %void None %3 + %5 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %4194302 = OpAccessChain %_ptr_Function_v4float %10 %int_0 + OpStore %4194302 %13 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(LocalAccessChainConvertTest, IdOverflowReplacingStore2) { + const std::string text = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "PSMain" + OpExecutionMode %4 OriginUpperLeft + OpDecorate %10 Location 47360 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %_struct_7 = OpTypeStruct %v4float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %13 = OpConstantNull %v4float + %4 = OpFunction %void None %3 + %5 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %4194301 = OpAccessChain %_ptr_Function_v4float %10 %int_0 + OpStore %4194301 %13 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(LocalAccessChainConvertTest, AccessChainWithNoIndex) { + const std::string before = + R"( +; CHECK: OpFunction +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: OpStore [[var]] %true +; CHECK: OpLoad %bool [[var]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool +%_ptr_Function_bool = OpTypePointer Function %bool + %2 = OpFunction %void None %4 + %8 = OpLabel + %9 = OpVariable %_ptr_Function_bool Function + %10 = OpAccessChain %_ptr_Function_bool %9 + OpStore %10 %true + %11 = OpLoad %bool %10 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// Assorted vector and matrix types +// Assorted struct array types +// Assorted scalar types +// Assorted non-target types +// OpInBoundsAccessChain +// Others? + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/local_redundancy_elimination_test.cpp b/third_party/spirv-tools/test/opt/local_redundancy_elimination_test.cpp new file mode 100644 index 0000000..291e1bc --- /dev/null +++ b/third_party/spirv-tools/test/opt/local_redundancy_elimination_test.cpp @@ -0,0 +1,203 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/build_module.h" +#include "source/opt/value_number_table.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; +using LocalRedundancyEliminationTest = PassTest<::testing::Test>; + +// Remove an instruction when it was already computed. +TEST_F(LocalRedundancyEliminationTest, RemoveRedundantAdd) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 +; CHECK: OpFAdd +; CHECK-NOT: OpFAdd + %11 = OpFAdd %5 %9 %9 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +// Make sure we keep instruction that are different, but look similar. +TEST_F(LocalRedundancyEliminationTest, KeepDifferentAdd) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 +; CHECK: OpFAdd + OpStore %8 %10 + %11 = OpLoad %5 %8 +; CHECK: %11 = OpLoad + %12 = OpFAdd %5 %11 %11 +; CHECK: OpFAdd [[:%\w+]] %11 %11 + OpReturn + OpFunctionEnd + )"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} + +// This test is check that the values are being propagated properly, and that +// we are able to identify sequences of instruction that are not needed. +TEST_F(LocalRedundancyEliminationTest, RemoveMultipleInstructions) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Uniform %5 + %8 = OpVariable %6 Uniform + %2 = OpFunction %3 None %4 + %7 = OpLabel +; CHECK: [[r1:%\w+]] = OpLoad + %9 = OpLoad %5 %8 +; CHECK-NEXT: [[r2:%\w+]] = OpFAdd [[:%\w+]] [[r1]] [[r1]] + %10 = OpFAdd %5 %9 %9 +; CHECK-NEXT: [[r3:%\w+]] = OpFMul [[:%\w+]] [[r2]] [[r1]] + %11 = OpFMul %5 %10 %9 +; CHECK-NOT: OpLoad + %12 = OpLoad %5 %8 +; CHECK-NOT: OpFAdd [[:\w+]] %12 %12 + %13 = OpFAdd %5 %12 %12 +; CHECK-NOT: OpFMul + %14 = OpFMul %5 %13 %12 +; CHECK-NEXT: [[:%\w+]] = OpFAdd [[:%\w+]] [[r3]] [[r3]] + %15 = OpFAdd %5 %14 %11 + OpReturn + OpFunctionEnd + )"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} + +// Redundant instructions in different blocks should be kept. +TEST_F(LocalRedundancyEliminationTest, KeepInstructionsInDifferentBlocks) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %bb1 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 +; CHECK: OpFAdd + OpBranch %bb2 + %bb2 = OpLabel +; CHECK: OpFAdd + %11 = OpFAdd %5 %9 %9 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalRedundancyEliminationTest, StorageBufferIdentification) { + const std::string text = R"( +; CHECK: [[gep:%\w+]] = OpAccessChain +; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]] +; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]] +; CHECK: OpStore [[gep]] [[add]] +; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[gep]] +; CHECK: [[add:%\w+]] = OpIAdd {{%\w+}} [[ld]] +; CHECK: OpStore [[gep]] [[add]] + +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %block BufferBlock +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%block = OpTypeStruct %int +%array = OpTypeArray %block %int_1 +%ptr_ssbo_array = OpTypePointer Uniform %array +%ptr_ssbo_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_ssbo_array Uniform +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 +%ld1 = OpLoad %int %gep1 +%add1 = OpIAdd %int %ld1 %int_1 +%gep2 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 +OpStore %gep2 %add1 +%gep3 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 +%ld3 = OpLoad %int %gep3 +%add3 = OpIAdd %int %ld3 %int_1 +%gep4 = OpAccessChain %ptr_ssbo_int %var %int_0 %int_0 +OpStore %gep4 %add3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/local_single_block_elim.cpp b/third_party/spirv-tools/test/opt/local_single_block_elim.cpp new file mode 100644 index 0000000..8e1cee6 --- /dev/null +++ b/third_party/spirv-tools/test/opt/local_single_block_elim.cpp @@ -0,0 +1,1464 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using LocalSingleBlockLoadStoreElimTest = PassTest<::testing::Test>; + +TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleStoreLoadElim) { + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v = BaseColor; + // gl_FragColor = v; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +%15 = OpLoad %v4float %v +OpStore %gl_FragColor %15 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpStore %gl_FragColor %14 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + before, predefs_before + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, SimpleLoadLoadElim) { + // #version 140 + // + // in vec4 BaseColor; + // in float fi; + // + // void main() + // { + // vec4 v = BaseColor; + // if (fi < 0) + // v = vec4(0.0); + // gl_FragData[0] = v; + // gl_FragData[1] = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragData +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %fi "fi" +OpName %gl_FragData "gl_FragData" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%16 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_arr_v4float_uint_32 = OpTypeArray %v4float %uint_32 +%_ptr_Output__arr_v4float_uint_32 = OpTypePointer Output %_arr_v4float_uint_32 +%gl_FragData = OpVariable %_ptr_Output__arr_v4float_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%int_1 = OpConstant %int 1 +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%25 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%26 = OpLoad %v4float %BaseColor +OpStore %v %26 +%27 = OpLoad %float %fi +%28 = OpFOrdLessThan %bool %27 %float_0 +OpSelectionMerge %29 None +OpBranchConditional %28 %30 %29 +%30 = OpLabel +OpStore %v %16 +OpBranch %29 +%29 = OpLabel +%31 = OpLoad %v4float %v +%32 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_0 +OpStore %32 %31 +%33 = OpLoad %v4float %v +%34 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_1 +OpStore %34 %33 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%25 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%26 = OpLoad %v4float %BaseColor +OpStore %v %26 +%27 = OpLoad %float %fi +%28 = OpFOrdLessThan %bool %27 %float_0 +OpSelectionMerge %29 None +OpBranchConditional %28 %30 %29 +%30 = OpLabel +OpStore %v %16 +OpBranch %29 +%29 = OpLabel +%31 = OpLoad %v4float %v +%32 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_0 +OpStore %32 %31 +%34 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_1 +OpStore %34 %31 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs + before, predefs + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, StoreStoreElim) { + // + // Note first store to v is eliminated + // + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // vec4 v = BaseColor; + // v = v * 0.5; + // OutColor = v; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%float_0_5 = OpConstant %float 0.5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%14 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%15 = OpLoad %v4float %BaseColor +OpStore %v %15 +%16 = OpLoad %v4float %v +%17 = OpVectorTimesScalar %v4float %16 %float_0_5 +OpStore %v %17 +%18 = OpLoad %v4float %v +OpStore %OutColor %18 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%14 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%15 = OpLoad %v4float %BaseColor +%17 = OpVectorTimesScalar %v4float %15 %float_0_5 +OpStore %v %17 +OpStore %OutColor %17 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + before, predefs_before + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, + NoStoreElimIfInterveningAccessChainLoad) { + // + // Note the first Store to %v is not eliminated due to the following access + // chain reference. + // + // #version 450 + // + // layout(location = 0) in vec4 BaseColor0; + // layout(location = 1) in vec4 BaseColor1; + // layout(location = 2) flat in int Idx; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // vec4 v = BaseColor0; + // float f = v[Idx]; + // v = BaseColor1 + vec4(0.1); + // OutColor = v/f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor0 %Idx %BaseColor1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %BaseColor0 "BaseColor0" +OpName %f "f" +OpName %Idx "Idx" +OpName %BaseColor1 "BaseColor1" +OpName %OutColor "OutColor" +OpDecorate %BaseColor0 Location 0 +OpDecorate %Idx Flat +OpDecorate %Idx Location 2 +OpDecorate %BaseColor1 Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%Idx = OpVariable %_ptr_Input_int Input +%BaseColor1 = OpVariable %_ptr_Input_v4float Input +%float_0_100000001 = OpConstant %float 0.100000001 +%19 = OpConstantComposite %v4float %float_0_100000001 %float_0_100000001 %float_0_100000001 %float_0_100000001 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %10 +%21 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%22 = OpLoad %v4float %BaseColor0 +OpStore %v %22 +%23 = OpLoad %int %Idx +%24 = OpAccessChain %_ptr_Function_float %v %23 +%25 = OpLoad %float %24 +OpStore %f %25 +%26 = OpLoad %v4float %BaseColor1 +%27 = OpFAdd %v4float %26 %19 +OpStore %v %27 +%28 = OpLoad %v4float %v +%29 = OpLoad %float %f +%30 = OpCompositeConstruct %v4float %29 %29 %29 %29 +%31 = OpFDiv %v4float %28 %30 +OpStore %OutColor %31 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%21 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%22 = OpLoad %v4float %BaseColor0 +OpStore %v %22 +%23 = OpLoad %int %Idx +%24 = OpAccessChain %_ptr_Function_float %v %23 +%25 = OpLoad %float %24 +OpStore %f %25 +%26 = OpLoad %v4float %BaseColor1 +%27 = OpFAdd %v4float %26 %19 +OpStore %v %27 +%30 = OpCompositeConstruct %v4float %25 %25 %25 %25 +%31 = OpFDiv %v4float %27 %30 +OpStore %OutColor %31 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs + before, predefs + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, NoElimIfInterveningAccessChainStore) { + // #version 140 + // + // in vec4 BaseColor; + // flat in int Idx; + // + // void main() + // { + // vec4 v = BaseColor; + // v[Idx] = 0; + // gl_FragColor = v; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Idx %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %Idx "Idx" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %Idx Flat +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%Idx = OpVariable %_ptr_Input_int Input +%float_0 = OpConstant %float 0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %8 +%18 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%19 = OpLoad %v4float %BaseColor +OpStore %v %19 +%20 = OpLoad %int %Idx +%21 = OpAccessChain %_ptr_Function_float %v %20 +OpStore %21 %float_0 +%22 = OpLoad %v4float %v +OpStore %gl_FragColor %22 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, + false, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, NoElimIfInterveningFunctionCall) { + // #version 140 + // + // in vec4 BaseColor; + // + // void foo() { + // } + // + // void main() + // { + // vec4 v = BaseColor; + // foo(); + // gl_FragColor = v; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_ "foo(" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %8 +%14 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%15 = OpLoad %v4float %BaseColor +OpStore %v %15 +%16 = OpFunctionCall %void %foo_ +%17 = OpLoad %v4float %v +OpStore %gl_FragColor %17 +OpReturn +OpFunctionEnd +%foo_ = OpFunction %void None %8 +%18 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, + false, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, ElimIfCopyObjectInFunction) { + // Note: SPIR-V hand edited to insert CopyObject + // + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v1 = BaseColor; + // gl_FragData[0] = v1; + // vec4 v2 = BaseColor * 0.5; + // gl_FragData[1] = v2; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragData +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v1 "v1" +OpName %BaseColor "BaseColor" +OpName %gl_FragData "gl_FragData" +OpName %v2 "v2" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_arr_v4float_uint_32 = OpTypeArray %v4float %uint_32 +%_ptr_Output__arr_v4float_uint_32 = OpTypePointer Output %_arr_v4float_uint_32 +%gl_FragData = OpVariable %_ptr_Output__arr_v4float_uint_32 Output +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%float_0_5 = OpConstant %float 0.5 +%int_1 = OpConstant %int 1 +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%22 = OpLabel +%v1 = OpVariable %_ptr_Function_v4float Function +%v2 = OpVariable %_ptr_Function_v4float Function +%23 = OpLoad %v4float %BaseColor +OpStore %v1 %23 +%24 = OpLoad %v4float %v1 +%25 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_0 +OpStore %25 %24 +%26 = OpLoad %v4float %BaseColor +%27 = OpVectorTimesScalar %v4float %26 %float_0_5 +%28 = OpCopyObject %_ptr_Function_v4float %v2 +OpStore %28 %27 +%29 = OpLoad %v4float %28 +%30 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_1 +OpStore %30 %29 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%22 = OpLabel +%v1 = OpVariable %_ptr_Function_v4float Function +%v2 = OpVariable %_ptr_Function_v4float Function +%23 = OpLoad %v4float %BaseColor +OpStore %v1 %23 +%25 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_0 +OpStore %25 %23 +%26 = OpLoad %v4float %BaseColor +%27 = OpVectorTimesScalar %v4float %26 %float_0_5 +%28 = OpCopyObject %_ptr_Function_v4float %v2 +OpStore %28 %27 +%30 = OpAccessChain %_ptr_Output_v4float %gl_FragData %int_1 +OpStore %30 %27 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs + before, predefs + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, ElimOpaque) { + // SPIR-V not representable in GLSL; not generatable from HLSL + // at the moment + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %outColor %texCoords +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpName %param "param" +OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%17 = OpTypeImage %float 2D 0 0 0 1 Unknown +%18 = OpTypeSampledImage %17 +%S_t = OpTypeStruct %v2float %v2float %18 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%20 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_18 = OpTypePointer UniformConstant %18 +%_ptr_Function_18 = OpTypePointer Function %18 +%sampler15 = OpVariable %_ptr_UniformConstant_18 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string before = + R"(%main = OpFunction %void None %12 +%28 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%29 = OpLoad %v2float %texCoords +%30 = OpLoad %S_t %s0 +%31 = OpCompositeInsert %S_t %29 %30 0 +OpStore %s0 %31 +%32 = OpLoad %18 %sampler15 +%33 = OpLoad %S_t %s0 +%34 = OpCompositeInsert %S_t %32 %33 2 +OpStore %s0 %34 +%35 = OpLoad %S_t %s0 +OpStore %param %35 +%36 = OpLoad %S_t %param +%37 = OpCompositeExtract %18 %36 2 +%38 = OpLoad %S_t %param +%39 = OpCompositeExtract %v2float %38 0 +%40 = OpImageSampleImplicitLod %v4float %37 %39 +OpStore %outColor %40 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %12 +%28 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%param = OpVariable %_ptr_Function_S_t Function +%29 = OpLoad %v2float %texCoords +%30 = OpLoad %S_t %s0 +%31 = OpCompositeInsert %S_t %29 %30 0 +%32 = OpLoad %18 %sampler15 +%34 = OpCompositeInsert %S_t %32 %31 2 +OpStore %s0 %34 +OpStore %param %34 +%37 = OpCompositeExtract %18 %34 2 +%39 = OpCompositeExtract %v2float %34 0 +%40 = OpImageSampleImplicitLod %v4float %37 %39 +OpStore %outColor %40 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + predefs + before, predefs + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, PositiveAndNegativeCallTree) { + // Note that the call tree function bar is optimized, but foo is not + // + // #version 140 + // + // in vec4 BaseColor; + // + // vec4 foo(vec4 v1) + // { + // vec4 t = v1; + // return t; + // } + // + // vec4 bar(vec4 v1) + // { + // vec4 t = v1; + // return t; + // } + // + // void main() + // { + // gl_FragColor = bar(BaseColor); + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_vf4_ "foo(vf4;" +OpName %v1 "v1" +OpName %bar_vf4_ "bar(vf4;" +OpName %v1_0 "v1" +OpName %t "t" +OpName %t_0 "t" +OpName %gl_FragColor "gl_FragColor" +OpName %BaseColor "BaseColor" +OpName %param "param" +%void = OpTypeVoid +%13 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%17 = OpTypeFunction %v4float %_ptr_Function_v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%main = OpFunction %void None %13 +%20 = OpLabel +%param = OpVariable %_ptr_Function_v4float Function +%21 = OpLoad %v4float %BaseColor +OpStore %param %21 +%22 = OpFunctionCall %v4float %bar_vf4_ %param +OpStore %gl_FragColor %22 +OpReturn +OpFunctionEnd +)"; + + const std::string before = + R"(%foo_vf4_ = OpFunction %v4float None %17 +%v1 = OpFunctionParameter %_ptr_Function_v4float +%23 = OpLabel +%t = OpVariable %_ptr_Function_v4float Function +%24 = OpLoad %v4float %v1 +OpStore %t %24 +%25 = OpLoad %v4float %t +OpReturnValue %25 +OpFunctionEnd +%bar_vf4_ = OpFunction %v4float None %17 +%v1_0 = OpFunctionParameter %_ptr_Function_v4float +%26 = OpLabel +%t_0 = OpVariable %_ptr_Function_v4float Function +%27 = OpLoad %v4float %v1_0 +OpStore %t_0 %27 +%28 = OpLoad %v4float %t_0 +OpReturnValue %28 +OpFunctionEnd +)"; + + const std::string after = + R"(%foo_vf4_ = OpFunction %v4float None %17 +%v1 = OpFunctionParameter %_ptr_Function_v4float +%23 = OpLabel +%t = OpVariable %_ptr_Function_v4float Function +%24 = OpLoad %v4float %v1 +OpStore %t %24 +%25 = OpLoad %v4float %t +OpReturnValue %25 +OpFunctionEnd +%bar_vf4_ = OpFunction %v4float None %17 +%v1_0 = OpFunctionParameter %_ptr_Function_v4float +%26 = OpLabel +%t_0 = OpVariable %_ptr_Function_v4float Function +%27 = OpLoad %v4float %v1_0 +OpStore %t_0 %27 +OpReturnValue %27 +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs + before, predefs + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, PointerVariable) { + // Test that checks if a pointer variable is removed. + + const std::string before = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_runtimearr__struct_3 ArrayStride 16 +OpMemberDecorate %_struct_5 0 Offset 0 +OpDecorate %_struct_5 BufferBlock +OpMemberDecorate %_struct_6 0 Offset 0 +OpDecorate %_struct_6 BufferBlock +OpDecorate %2 Location 0 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_struct_3 = OpTypeStruct %v4float +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 +%_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 +%_struct_6 = OpTypeStruct %int +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 +%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5 +%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%2 = OpVariable %_ptr_Output_v4float Output +%7 = OpVariable %_ptr_Uniform__struct_5 Uniform +%1 = OpFunction %void None %10 +%23 = OpLabel +%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function +OpStore %24 %7 +%26 = OpLoad %_ptr_Uniform__struct_5 %24 +%27 = OpAccessChain %_ptr_Uniform_v4float %26 %int_0 %uint_0 %int_0 +%28 = OpLoad %v4float %27 +%29 = OpCopyObject %v4float %28 +OpStore %2 %28 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_runtimearr__struct_3 ArrayStride 16 +OpMemberDecorate %_struct_5 0 Offset 0 +OpDecorate %_struct_5 BufferBlock +OpMemberDecorate %_struct_6 0 Offset 0 +OpDecorate %_struct_6 BufferBlock +OpDecorate %2 Location 0 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_struct_3 = OpTypeStruct %v4float +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 +%_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 +%_struct_6 = OpTypeStruct %int +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 +%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5 +%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%2 = OpVariable %_ptr_Output_v4float Output +%7 = OpVariable %_ptr_Uniform__struct_5 Uniform +%1 = OpFunction %void None %10 +%23 = OpLabel +%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function +OpStore %24 %7 +%27 = OpAccessChain %_ptr_Uniform_v4float %7 %int_0 %uint_0 %int_0 +%28 = OpLoad %v4float %27 +%29 = OpCopyObject %v4float %28 +OpStore %2 %28 +OpReturn +OpFunctionEnd +)"; + + // Relax logical pointers to allow pointer allocations. + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ValidatorOptions()->relax_logical_pointer = true; + SinglePassRunAndCheck(before, after, true, + true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, RedundantStore) { + // Test that checks if a pointer variable is removed. + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpBranch %16 +%16 = OpLabel +%15 = OpLoad %v4float %v +OpStore %v %15 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpBranch %16 +%16 = OpLabel +%15 = OpLoad %v4float %v +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + predefs_before + before, predefs_before + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, RedundantStore2) { + // Test that checks if a pointer variable is removed. + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpBranch %16 +%16 = OpLabel +%15 = OpLoad %v4float %v +OpStore %v %15 +%17 = OpLoad %v4float %v +OpStore %v %17 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %7 +%13 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%14 = OpLoad %v4float %BaseColor +OpStore %v %14 +OpBranch %16 +%16 = OpLabel +%15 = OpLoad %v4float %v +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + predefs_before + before, predefs_before + after, true, true); +} + +// Test that that an unused OpAccessChain between two store does does not +// hinders the removal of the first store. We need to check this because +// local-access-chain-convert does always remove the OpAccessChain instructions +// that become dead. + +TEST_F(LocalSingleBlockLoadStoreElimTest, + StoreElimIfInterveningUnusedAccessChain) { + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor0 %Idx %BaseColor1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %BaseColor0 "BaseColor0" +OpName %Idx "Idx" +OpName %BaseColor1 "BaseColor1" +OpName %OutColor "OutColor" +OpDecorate %BaseColor0 Location 0 +OpDecorate %Idx Flat +OpDecorate %Idx Location 2 +OpDecorate %BaseColor1 Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%Idx = OpVariable %_ptr_Input_int Input +%BaseColor1 = OpVariable %_ptr_Input_v4float Input +%float_0_100000001 = OpConstant %float 0.100000001 +%19 = OpConstantComposite %v4float %float_0_100000001 %float_0_100000001 %float_0_100000001 %float_0_100000001 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %10 +%21 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %BaseColor0 +OpStore %v %22 +%23 = OpLoad %int %Idx +%24 = OpAccessChain %_ptr_Function_float %v %23 +%26 = OpLoad %v4float %BaseColor1 +%27 = OpFAdd %v4float %26 %19 +OpStore %v %27 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%21 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %BaseColor0 +%23 = OpLoad %int %Idx +%24 = OpAccessChain %_ptr_Function_float %v %23 +%26 = OpLoad %v4float %BaseColor1 +%27 = OpFAdd %v4float %26 %19 +OpStore %v %27 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + predefs + before, predefs + after, true, true); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, VariablePointerTest) { + // Check that the load of the first variable is still used and that the load + // of the third variable is propagated. The first load has to remain because + // of the store to the variable pointer. + const std::string text = R"( +; CHECK: [[v1:%\w+]] = OpVariable +; CHECK: [[v2:%\w+]] = OpVariable +; CHECK: [[v3:%\w+]] = OpVariable +; CHECK: [[phi:%\w+]] = OpPhi +; CHECK: [[ld1:%\w+]] = OpLoad %int [[v1]] +; CHECK: OpIAdd %int [[ld1]] %int_0 + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %_struct_3 0 Offset 0 + OpMemberDecorate %_struct_3 1 Offset 4 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %_struct_3 = OpTypeStruct %int %int +%_ptr_Function__struct_3 = OpTypePointer Function %_struct_3 +%_ptr_Function_int = OpTypePointer Function %int + %true = OpConstantTrue %bool + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %13 = OpConstantNull %_struct_3 + %2 = OpFunction %void None %5 + %14 = OpLabel + %15 = OpVariable %_ptr_Function_int Function + %16 = OpVariable %_ptr_Function_int Function + %17 = OpVariable %_ptr_Function_int Function + OpSelectionMerge %18 None + OpBranchConditional %true %19 %20 + %19 = OpLabel + OpBranch %18 + %20 = OpLabel + OpBranch %18 + %18 = OpLabel + %21 = OpPhi %_ptr_Function_int %15 %19 %16 %20 + OpStore %15 %int_1 + OpStore %21 %int_0 + %22 = OpLoad %int %15 + OpStore %17 %int_0 + %23 = OpLoad %int %17 + %24 = OpIAdd %int %22 %23 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalSingleBlockLoadStoreElimTest, DebugDeclareTest) { + // If OpenCL.DebugInfo.100 enabled, check that store/load is still + // optimized, but stores are not deleted for store/store. + // + // struct PS_INPUT { + // float4 c0 : COLOR0; + // float4 c1 : COLOR1; + // }; + // + // struct PS_OUTPUT { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) { + // PS_OUTPUT ps_output; + // float4 c; + // c = i.c0; + // c += i.c1; + // c /= 2.0; + // ps_output.vColor = c; + // return ps_output; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %in_var_COLOR0 %in_var_COLOR1 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %6 = OpString "foo3.frag" + %7 = OpString "PS_OUTPUT" + %8 = OpString "float" + %9 = OpString "vColor" + %10 = OpString "PS_INPUT" + %11 = OpString "c1" + %12 = OpString "c0" + %13 = OpString "src.MainPs" + %14 = OpString "c" + %15 = OpString "ps_output" + %16 = OpString "i" + OpName %in_var_COLOR0 "in.var.COLOR0" + OpName %in_var_COLOR1 "in.var.COLOR1" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "c0" + OpMemberName %PS_INPUT 1 "c1" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %src_MainPs "src.MainPs" + OpName %i "i" + OpName %bb_entry "bb.entry" + OpName %ps_output "ps_output" + OpName %c "c" + OpDecorate %in_var_COLOR0 Location 0 + OpDecorate %in_var_COLOR1 Location 1 + OpDecorate %out_var_SV_Target0 Location 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %v4float = OpTypeVector %float 4 + %31 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_256 = OpConstant %uint 256 + %40 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v4float %v4float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %42 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v4float = OpTypePointer Function %v4float +%in_var_COLOR0 = OpVariable %_ptr_Input_v4float Input +%in_var_COLOR1 = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %45 = OpExtInst %void %1 DebugSource %6 + %46 = OpExtInst %void %1 DebugCompilationUnit 1 4 %45 HLSL + %47 = OpExtInst %void %1 DebugTypeComposite %7 Structure %45 8 1 %46 %7 %uint_128 FlagIsProtected|FlagIsPrivate %48 + %49 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float + %50 = OpExtInst %void %1 DebugTypeVector %49 4 + %48 = OpExtInst %void %1 DebugTypeMember %9 %50 %45 10 5 %47 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %51 = OpExtInst %void %1 DebugTypeComposite %10 Structure %45 2 1 %46 %10 %uint_256 FlagIsProtected|FlagIsPrivate %52 %53 + %53 = OpExtInst %void %1 DebugTypeMember %11 %50 %45 5 5 %51 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate + %52 = OpExtInst %void %1 DebugTypeMember %12 %50 %45 4 5 %51 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %54 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %47 %51 + %55 = OpExtInst %void %1 DebugFunction %13 %54 %45 13 1 %46 %13 FlagIsProtected|FlagIsPrivate 14 %src_MainPs + %56 = OpExtInst %void %1 DebugLexicalBlock %45 14 1 %55 + %57 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %56 FlagIsLocal + %58 = OpExtInst %void %1 DebugLocalVariable %15 %47 %45 15 15 %56 FlagIsLocal + %59 = OpExtInst %void %1 DebugExpression + %60 = OpExtInst %void %1 DebugLocalVariable %16 %51 %45 13 29 %55 FlagIsLocal 1 + %61 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %55 FlagIsLocal 1 + %MainPs = OpFunction %void None %40 + %62 = OpLabel +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %63 = OpLoad %v4float %in_var_COLOR0 + %64 = OpLoad %v4float %in_var_COLOR1 + %65 = OpCompositeConstruct %PS_INPUT %63 %64 + OpStore %param_var_i %65 + %66 = OpFunctionCall %PS_OUTPUT %src_MainPs %param_var_i + %67 = OpCompositeExtract %v4float %66 0 + OpStore %out_var_SV_Target0 %67 + OpReturn + OpFunctionEnd + OpLine %6 13 1 + %src_MainPs = OpFunction %PS_OUTPUT None %42 + %83 = OpExtInst %void %1 DebugScope %55 + OpLine %6 13 29 + %i = OpFunctionParameter %_ptr_Function_PS_INPUT + %69 = OpExtInst %void %1 DebugDeclare %60 %i %59 + %84 = OpExtInst %void %1 DebugNoScope + %bb_entry = OpLabel + %85 = OpExtInst %void %1 DebugScope %56 + %ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function + %c = OpVariable %_ptr_Function_v4float Function + %71 = OpExtInst %void %1 DebugDeclare %61 %c %59 + OpLine %6 18 9 + %72 = OpAccessChain %_ptr_Function_v4float %i %int_0 + OpLine %6 18 13 + %73 = OpLoad %v4float %72 + OpLine %6 18 5 + OpStore %c %73 +;CHECK: OpStore %c %73 + OpLine %6 19 10 + %74 = OpAccessChain %_ptr_Function_v4float %i %int_1 + OpLine %6 19 14 + %75 = OpLoad %v4float %74 + OpLine %6 19 5 + %76 = OpLoad %v4float %c +;CHECK-NOT: OpLine %6 19 5 +;CHECK-NOT: %76 = OpLoad %v4float %c + OpLine %6 19 7 + %77 = OpFAdd %v4float %76 %75 +;CHECK-NOT: %77 = OpFAdd %v4float %76 %75 +;CHECK: %77 = OpFAdd %v4float %73 %75 + OpLine %6 19 5 + OpStore %c %77 +;CHECK: OpStore %c %77 + OpLine %6 20 5 + %78 = OpLoad %v4float %c +;CHECK-NOT: OpLine %6 20 5 +;CHECK-NOT: %78 = OpLoad %v4float %c + OpLine %6 20 7 + %79 = OpFDiv %v4float %78 %31 +;CHECK-NOT %79 = OpFDiv %v4float %78 %31 +;CHECK: %79 = OpFDiv %v4float %77 %31 + OpLine %6 20 5 + OpStore %c %79 +;CHECK: OpStore %c %79 + OpLine %6 22 26 + %80 = OpLoad %v4float %c +;CHECK-NOT: OpLine %6 22 26 +;CHECK-NOT: %80 = OpLoad %v4float %c + OpLine %6 22 5 + %81 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 + OpStore %81 %80 +;CHECK-NOT: OpStore %81 %80 +;CHECK: OpStore %81 %79 + OpLine %6 23 12 + %82 = OpLoad %PS_OUTPUT %ps_output + OpLine %6 23 5 + OpReturnValue %82 + %86 = OpExtInst %void %1 DebugNoScope + OpFunctionEnd + )"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} +TEST_F(LocalSingleBlockLoadStoreElimTest, DebugValueTest) { + // If OpenCL.DebugInfo.100 enabled, check that store/load is still + // optimized, but stores are not deleted for store/store. + // Same source as DebugDeclareTest; DebugDeclare replaced with + // equivalent DebugValue + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %in_var_COLOR0 %in_var_COLOR1 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %6 = OpString "foo3.frag" + %7 = OpString "PS_OUTPUT" + %8 = OpString "float" + %9 = OpString "vColor" + %10 = OpString "PS_INPUT" + %11 = OpString "c1" + %12 = OpString "c0" + %13 = OpString "src.MainPs" + %14 = OpString "c" + %15 = OpString "ps_output" + %16 = OpString "i" + OpName %in_var_COLOR0 "in.var.COLOR0" + OpName %in_var_COLOR1 "in.var.COLOR1" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "c0" + OpMemberName %PS_INPUT 1 "c1" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %src_MainPs "src.MainPs" + OpName %i "i" + OpName %bb_entry "bb.entry" + OpName %ps_output "ps_output" + OpName %c "c" + OpDecorate %in_var_COLOR0 Location 0 + OpDecorate %in_var_COLOR1 Location 1 + OpDecorate %out_var_SV_Target0 Location 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %float_2 = OpConstant %float 2 + %v4float = OpTypeVector %float 4 + %31 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_256 = OpConstant %uint 256 + %40 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v4float %v4float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %42 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v4float = OpTypePointer Function %v4float +%in_var_COLOR0 = OpVariable %_ptr_Input_v4float Input +%in_var_COLOR1 = OpVariable %_ptr_Input_v4float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %45 = OpExtInst %void %1 DebugSource %6 + %46 = OpExtInst %void %1 DebugCompilationUnit 1 4 %45 HLSL + %47 = OpExtInst %void %1 DebugTypeComposite %7 Structure %45 8 1 %46 %7 %uint_128 FlagIsProtected|FlagIsPrivate %48 + %49 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float + %50 = OpExtInst %void %1 DebugTypeVector %49 4 + %48 = OpExtInst %void %1 DebugTypeMember %9 %50 %45 10 5 %47 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %51 = OpExtInst %void %1 DebugTypeComposite %10 Structure %45 2 1 %46 %10 %uint_256 FlagIsProtected|FlagIsPrivate %52 %53 + %53 = OpExtInst %void %1 DebugTypeMember %11 %50 %45 5 5 %51 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate + %52 = OpExtInst %void %1 DebugTypeMember %12 %50 %45 4 5 %51 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %54 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %47 %51 + %55 = OpExtInst %void %1 DebugFunction %13 %54 %45 13 1 %46 %13 FlagIsProtected|FlagIsPrivate 14 %src_MainPs + %56 = OpExtInst %void %1 DebugLexicalBlock %45 14 1 %55 + %57 = OpExtInst %void %1 DebugLocalVariable %14 %50 %45 16 12 %56 FlagIsLocal + %58 = OpExtInst %void %1 DebugLocalVariable %15 %47 %45 15 15 %56 FlagIsLocal + %59 = OpExtInst %void %1 DebugExpression %60 = OpExtInst %void %1 DebugOperation Deref %61 = OpExtInst +%void %1 DebugExpression %60 %62 = OpExtInst %void %1 DebugLocalVariable %16 %51 +%45 13 29 %55 FlagIsLocal 1 %63 = OpExtInst %void %1 DebugLocalVariable %14 %50 +%45 16 12 %55 FlagIsLocal 1 %MainPs = OpFunction %void None %40 %64 = OpLabel +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %65 = OpLoad %v4float %in_var_COLOR0 + %66 = OpLoad %v4float %in_var_COLOR1 + %67 = OpCompositeConstruct %PS_INPUT %65 %66 + OpStore %param_var_i %67 + %68 = OpFunctionCall %PS_OUTPUT %src_MainPs %param_var_i + %69 = OpCompositeExtract %v4float %68 0 + OpStore %out_var_SV_Target0 %69 + OpReturn + OpFunctionEnd + OpLine %6 13 1 + %src_MainPs = OpFunction %PS_OUTPUT None %42 + %70 = OpExtInst %void %1 DebugScope %55 + OpLine %6 13 29 + %i = OpFunctionParameter %_ptr_Function_PS_INPUT + %71 = OpExtInst %void %1 DebugDeclare %62 %i %59 + %72 = OpExtInst %void %1 DebugNoScope + %bb_entry = OpLabel + %73 = OpExtInst %void %1 DebugScope %56 + %ps_output = OpVariable %_ptr_Function_PS_OUTPUT Function + %c = OpVariable %_ptr_Function_v4float Function + %74 = OpExtInst %void %1 DebugValue %63 %c %61 + OpLine %6 18 9 + %75 = OpAccessChain %_ptr_Function_v4float %i %int_0 + OpLine %6 18 13 + %76 = OpLoad %v4float %75 + OpLine %6 18 5 + OpStore %c %76 +;CHECK: OpStore %c %76 + OpLine %6 19 10 + %77 = OpAccessChain %_ptr_Function_v4float %i %int_1 + OpLine %6 19 14 + %78 = OpLoad %v4float %77 + OpLine %6 19 5 + %79 = OpLoad %v4float %c +;CHECK-NOT: OpLine %6 19 5 +;CHECK-NOT: %79 = OpLoad %v4float %c + OpLine %6 19 7 + %80 = OpFAdd %v4float %79 %78 +;CHECK-NOT: %80 = OpFAdd %v4float %79 %78 +;CHECK: %80 = OpFAdd %v4float %76 %78 + OpLine %6 19 5 + OpStore %c %80 +;CHECK: OpStore %c %80 + OpLine %6 20 5 + %81 = OpLoad %v4float %c +;CHECK-NOT: OpLine %6 20 5 +;CHECK-NOT: %81 = OpLoad %v4float %c + OpLine %6 20 7 + %82 = OpFDiv %v4float %81 %31 +;CHECK-NOT: %82 = OpFDiv %v4float %81 %31 +;CHECK: %82 = OpFDiv %v4float %80 %31 + OpLine %6 20 5 + OpStore %c %82 +;CHECK: OpStore %c %82 + OpLine %6 22 26 + %83 = OpLoad %v4float %c +;CHECK-NOT: OpLine %6 22 26 +;CHECK-NOT: %83 = OpLoad %v4float %c + OpLine %6 22 5 + %84 = OpAccessChain %_ptr_Function_v4float %ps_output %int_0 + OpStore %84 %83 +;CHECK-NOT: OpStore %84 %83 +;CHECK: OpStore %84 %82 + OpLine %6 23 12 + %85 = OpLoad %PS_OUTPUT %ps_output + OpLine %6 23 5 + OpReturnValue %85 + %86 = OpExtInst %void %1 DebugNoScope + OpFunctionEnd + )"; + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// Other target variable types +// InBounds Access Chains +// Check for correctness in the presence of function calls +// Others? + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/local_single_store_elim_test.cpp b/third_party/spirv-tools/test/opt/local_single_store_elim_test.cpp new file mode 100644 index 0000000..d015dfb --- /dev/null +++ b/third_party/spirv-tools/test/opt/local_single_store_elim_test.cpp @@ -0,0 +1,1452 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using LocalSingleStoreElimTest = PassTest<::testing::Test>; + +TEST_F(LocalSingleStoreElimTest, PositiveAndNegative) { + // Single store to v is optimized. Multiple store to + // f is not optimized. + // + // #version 140 + // + // in vec4 BaseColor; + // in float fi; + // + // void main() + // { + // vec4 v = BaseColor; + // float f = fi; + // if (f < 0) + // f = 0.0; + // gl_FragColor = v + f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %f "f" +OpName %fi "fi" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%26 = OpLoad %v4float %v +%27 = OpLoad %float %f +%28 = OpCompositeConstruct %v4float %27 %27 %27 %27 +%29 = OpFAdd %v4float %26 %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%27 = OpLoad %float %f +%28 = OpCompositeConstruct %v4float %27 %27 %27 %27 +%29 = OpFAdd %v4float %20 %28 +OpStore %gl_FragColor %29 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + +TEST_F(LocalSingleStoreElimTest, ThreeStores) { + // Three stores to multiple loads of v is not optimized. + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %fi "fi" +OpName %r "r" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%r = OpVariable %_ptr_Function_v4float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +%22 = OpFOrdLessThan %bool %21 %float_0 +OpSelectionMerge %23 None +OpBranchConditional %22 %24 %25 +%24 = OpLabel +%26 = OpLoad %v4float %v +OpStore %v %26 +OpStore %r %26 +OpBranch %23 +%25 = OpLabel +%27 = OpLoad %v4float %v +%28 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 +OpStore %v %28 +%29 = OpFSub %v4float %28 %27 +OpStore %r %29 +OpBranch %23 +%23 = OpLabel +%30 = OpLoad %v4float %r +OpStore %gl_FragColor %30 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + before, true, true); +} + +TEST_F(LocalSingleStoreElimTest, MultipleLoads) { + // Single store to multiple loads of v is optimized. + // + // #version 140 + // + // in vec4 BaseColor; + // in float fi; + // + // void main() + // { + // vec4 v = BaseColor; + // float f = fi; + // if (f < 0) + // f = 0.0; + // gl_FragColor = v + f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %fi "fi" +OpName %r "r" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%r = OpVariable %_ptr_Function_v4float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +%22 = OpFOrdLessThan %bool %21 %float_0 +OpSelectionMerge %23 None +OpBranchConditional %22 %24 %25 +%24 = OpLabel +%26 = OpLoad %v4float %v +OpStore %r %26 +OpBranch %23 +%25 = OpLabel +%27 = OpLoad %v4float %v +%28 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 +%29 = OpFSub %v4float %28 %27 +OpStore %r %29 +OpBranch %23 +%23 = OpLabel +%30 = OpLoad %v4float %r +OpStore %gl_FragColor %30 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%r = OpVariable %_ptr_Function_v4float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +%22 = OpFOrdLessThan %bool %21 %float_0 +OpSelectionMerge %23 None +OpBranchConditional %22 %24 %25 +%24 = OpLabel +OpStore %r %20 +OpBranch %23 +%25 = OpLabel +%28 = OpCompositeConstruct %v4float %float_1 %float_1 %float_1 %float_1 +%29 = OpFSub %v4float %28 %20 +OpStore %r %29 +OpBranch %23 +%23 = OpLabel +%30 = OpLoad %v4float %r +OpStore %gl_FragColor %30 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + +TEST_F(LocalSingleStoreElimTest, NoStoreElimWithInterveningAccessChainLoad) { + // Last load of v is eliminated, but access chain load and store of v isn't + // + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v = BaseColor; + // float f = v[3]; + // gl_FragColor = v * f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %f "f" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +%19 = OpAccessChain %_ptr_Function_float %v %uint_3 +%20 = OpLoad %float %19 +OpStore %f %20 +%21 = OpLoad %v4float %v +%22 = OpLoad %float %f +%23 = OpVectorTimesScalar %v4float %21 %22 +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +%19 = OpAccessChain %_ptr_Function_float %v %uint_3 +%20 = OpLoad %float %19 +OpStore %f %20 +%23 = OpVectorTimesScalar %v4float %18 %20 +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + +TEST_F(LocalSingleStoreElimTest, NoReplaceOfDominatingPartialStore) { + // Note: SPIR-V hand edited to initialize v to vec4(0.0) + // + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v; + // float v[1] = 1.0; + // gl_FragColor = v; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %gl_FragColor "gl_FragColor" +OpName %BaseColor "BaseColor" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%12 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%float_1 = OpConstant %float 1 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%main = OpFunction %void None %7 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function %12 +%20 = OpAccessChain %_ptr_Function_float %v %uint_1 +OpStore %20 %float_1 +%21 = OpLoad %v4float %v +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, + true); +} + +TEST_F(LocalSingleStoreElimTest, ElimIfCopyObjectInFunction) { + // Note: hand edited to insert OpCopyObject + // + // #version 140 + // + // in vec4 BaseColor; + // in float fi; + // + // void main() + // { + // vec4 v = BaseColor; + // float f = fi; + // if (f < 0) + // f = 0.0; + // gl_FragColor = v + f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %f "f" +OpName %fi "fi" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%26 = OpCopyObject %_ptr_Function_v4float %v +%27 = OpLoad %v4float %26 +%28 = OpLoad %float %f +%29 = OpCompositeConstruct %v4float %28 %28 %28 %28 +%30 = OpFAdd %v4float %27 %29 +OpStore %gl_FragColor %30 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%19 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%f = OpVariable %_ptr_Function_float Function +%20 = OpLoad %v4float %BaseColor +OpStore %v %20 +%21 = OpLoad %float %fi +OpStore %f %21 +%22 = OpLoad %float %f +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +OpStore %f %float_0 +OpBranch %24 +%24 = OpLabel +%26 = OpCopyObject %_ptr_Function_v4float %v +%28 = OpLoad %float %f +%29 = OpCompositeConstruct %v4float %28 %28 %28 %28 +%30 = OpFAdd %v4float %20 %29 +OpStore %gl_FragColor %30 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + +TEST_F(LocalSingleStoreElimTest, NoOptIfStoreNotDominating) { + // Single store to f not optimized because it does not dominate + // the load. + // + // #version 140 + // + // in vec4 BaseColor; + // in float fi; + // + // void main() + // { + // float f; + // if (fi < 0) + // f = 0.5; + // if (fi < 0) + // gl_FragColor = BaseColor * f; + // else + // gl_FragColor = BaseColor; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %fi %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %fi "fi" +OpName %f "f" +OpName %gl_FragColor "gl_FragColor" +OpName %BaseColor "BaseColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Function_float = OpTypePointer Function %float +%float_0_5 = OpConstant %float 0.5 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%main = OpFunction %void None %8 +%18 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%19 = OpLoad %float %fi +%20 = OpFOrdLessThan %bool %19 %float_0 +OpSelectionMerge %21 None +OpBranchConditional %20 %22 %21 +%22 = OpLabel +OpStore %f %float_0_5 +OpBranch %21 +%21 = OpLabel +%23 = OpLoad %float %fi +%24 = OpFOrdLessThan %bool %23 %float_0 +OpSelectionMerge %25 None +OpBranchConditional %24 %26 %27 +%26 = OpLabel +%28 = OpLoad %v4float %BaseColor +%29 = OpLoad %float %f +%30 = OpVectorTimesScalar %v4float %28 %29 +OpStore %gl_FragColor %30 +OpBranch %25 +%27 = OpLabel +%31 = OpLoad %v4float %BaseColor +OpStore %gl_FragColor %31 +OpBranch %25 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, + true); +} + +TEST_F(LocalSingleStoreElimTest, OptInitializedVariableLikeStore) { + // Initialized variable f is optimized like it was a store. + // Note: The SPIR-V was edited to turn the store to f to an + // an initialization. + // + // #version 140 + // + // void main() + // { + // float f = 0.0; + // gl_FragColor = vec4(f); + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %f "f" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %gl_FragColor Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %6 +%12 = OpLabel +%f = OpVariable %_ptr_Function_float Function %float_0 +%13 = OpLoad %float %f +%14 = OpCompositeConstruct %v4float %13 %13 %13 %13 +OpStore %gl_FragColor %14 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %6 +%12 = OpLabel +%f = OpVariable %_ptr_Function_float Function %float_0 +%14 = OpCompositeConstruct %v4float %float_0 %float_0 %float_0 %float_0 +OpStore %gl_FragColor %14 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + +TEST_F(LocalSingleStoreElimTest, PointerVariable) { + // Test that checks if a pointer variable is removed. + + const std::string before = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_runtimearr__struct_3 ArrayStride 16 +OpMemberDecorate %_struct_5 0 Offset 0 +OpDecorate %_struct_5 BufferBlock +OpMemberDecorate %_struct_6 0 Offset 0 +OpDecorate %_struct_6 BufferBlock +OpDecorate %2 Location 0 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_struct_3 = OpTypeStruct %v4float +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 +%_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 +%_struct_6 = OpTypeStruct %int +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 +%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5 +%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%2 = OpVariable %_ptr_Output_v4float Output +%7 = OpVariable %_ptr_Uniform__struct_5 Uniform +%1 = OpFunction %void None %10 +%23 = OpLabel +%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function +OpStore %24 %7 +%26 = OpLoad %_ptr_Uniform__struct_5 %24 +%27 = OpAccessChain %_ptr_Uniform_v4float %26 %int_0 %uint_0 %int_0 +%28 = OpLoad %v4float %27 +%29 = OpCopyObject %v4float %28 +OpStore %2 %28 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_runtimearr__struct_3 ArrayStride 16 +OpMemberDecorate %_struct_5 0 Offset 0 +OpDecorate %_struct_5 BufferBlock +OpMemberDecorate %_struct_6 0 Offset 0 +OpDecorate %_struct_6 BufferBlock +OpDecorate %2 Location 0 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_struct_3 = OpTypeStruct %v4float +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 +%_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 +%_struct_6 = OpTypeStruct %int +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 +%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5 +%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%2 = OpVariable %_ptr_Output_v4float Output +%7 = OpVariable %_ptr_Uniform__struct_5 Uniform +%1 = OpFunction %void None %10 +%23 = OpLabel +%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function +OpStore %24 %7 +%27 = OpAccessChain %_ptr_Uniform_v4float %7 %int_0 %uint_0 %int_0 +%28 = OpLoad %v4float %27 +%29 = OpCopyObject %v4float %28 +OpStore %2 %28 +OpReturn +OpFunctionEnd +)"; + + // Relax logical pointers to allow pointer allocations. + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ValidatorOptions()->relax_logical_pointer = true; + SinglePassRunAndCheck(before, after, true, true); +} + +// Test that that an unused OpAccessChain between a store and a use does does +// not hinders the replacement of the use. We need to check this because +// local-access-chain-convert does always remove the OpAccessChain instructions +// that become dead. + +TEST_F(LocalSingleStoreElimTest, + StoreElimWithUnusedInterveningAccessChainLoad) { + // Last load of v is eliminated, but access chain load and store of v isn't + // + // #version 140 + // + // in vec4 BaseColor; + // + // void main() + // { + // vec4 v = BaseColor; + // float f = v[3]; + // gl_FragColor = v * f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +%19 = OpAccessChain %_ptr_Function_float %v %uint_3 +%21 = OpLoad %v4float %v +OpStore %gl_FragColor %21 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +%19 = OpAccessChain %_ptr_Function_float %v %uint_3 +OpStore %gl_FragColor %18 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(predefs + before, + predefs + after, true, true); +} + +TEST_F(LocalSingleStoreElimTest, VariablePointerTest) { + // Check that the load of the first variable is still used and that the load + // of the third variable is propagated. The first load has to remain because + // of the store to the variable pointer. + const std::string text = R"( +; CHECK: [[v1:%\w+]] = OpVariable +; CHECK: [[v2:%\w+]] = OpVariable +; CHECK: [[v3:%\w+]] = OpVariable +; CHECK: [[ld1:%\w+]] = OpLoad %int [[v1]] +; CHECK: OpIAdd %int [[ld1]] %int_0 + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %_struct_3 0 Offset 0 + OpMemberDecorate %_struct_3 1 Offset 4 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %_struct_3 = OpTypeStruct %int %int +%_ptr_Function__struct_3 = OpTypePointer Function %_struct_3 +%_ptr_Function_int = OpTypePointer Function %int + %true = OpConstantTrue %bool + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %13 = OpConstantNull %_struct_3 + %2 = OpFunction %void None %5 + %14 = OpLabel + %15 = OpVariable %_ptr_Function_int Function + %16 = OpVariable %_ptr_Function_int Function + %17 = OpVariable %_ptr_Function_int Function + OpStore %15 %int_1 + OpStore %17 %int_0 + OpSelectionMerge %18 None + OpBranchConditional %true %19 %20 + %19 = OpLabel + OpBranch %18 + %20 = OpLabel + OpBranch %18 + %18 = OpLabel + %21 = OpPhi %_ptr_Function_int %15 %19 %16 %20 + OpStore %21 %int_0 + %22 = OpLoad %int %15 + %23 = OpLoad %int %17 + %24 = OpIAdd %int %22 %23 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalSingleStoreElimTest, DebugDeclareTest) { + // If OpenCL.DebugInfo.100 enabled, check that store/load is still + // optimized, DebugValue placed after the store and the associated + // DebugDeclare is removed. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %20 = OpString "foo.frag" + %24 = OpString "PS_OUTPUT" + %28 = OpString "float" + %31 = OpString "vColor" + %33 = OpString "PS_INPUT" + %38 = OpString "vTextureCoords" + %40 = OpString "@type.2d.image" + %41 = OpString "type.2d.image" + %43 = OpString "Texture2D.TemplateParam" + %47 = OpString "src.MainPs" + %51 = OpString "tc" + %53 = OpString "ps_output" + %56 = OpString "i" + %58 = OpString "@type.sampler" + %59 = OpString "type.sampler" + %61 = OpString "g_sAniso" + %63 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_64 = OpConstant %uint 64 + %65 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %75 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%_ptr_Function_v4float = OpTypePointer Function %v4float + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %39 = OpExtInst %void %1 DebugInfoNone + %55 = OpExtInst %void %1 DebugExpression + %22 = OpExtInst %void %1 DebugSource %20 + %23 = OpExtInst %void %1 DebugCompilationUnit 1 4 %22 HLSL + %26 = OpExtInst %void %1 DebugTypeComposite %24 Structure %22 10 1 %23 %24 %uint_128 FlagIsProtected|FlagIsPrivate %27 + %29 = OpExtInst %void %1 DebugTypeBasic %28 %uint_32 Float + %30 = OpExtInst %void %1 DebugTypeVector %29 4 + %27 = OpExtInst %void %1 DebugTypeMember %31 %30 %22 12 5 %26 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %35 = OpExtInst %void %1 DebugTypeComposite %33 Structure %22 5 1 %23 %33 %uint_64 FlagIsProtected|FlagIsPrivate %36 + %37 = OpExtInst %void %1 DebugTypeVector %29 2 + %36 = OpExtInst %void %1 DebugTypeMember %38 %37 %22 7 5 %35 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate + %42 = OpExtInst %void %1 DebugTypeComposite %40 Class %22 0 0 %23 %41 %39 FlagIsProtected|FlagIsPrivate + %44 = OpExtInst %void %1 DebugTypeTemplateParameter %43 %29 %39 %22 0 0 + %45 = OpExtInst %void %1 DebugTypeTemplate %42 %44 + %46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %26 %35 + %48 = OpExtInst %void %1 DebugFunction %47 %46 %22 15 1 %23 %47 FlagIsProtected|FlagIsPrivate 16 %39 + %50 = OpExtInst %void %1 DebugLexicalBlock %22 16 1 %48 + %52 = OpExtInst %void %1 DebugLocalVariable %51 %37 %22 19 12 %50 FlagIsLocal + %54 = OpExtInst %void %1 DebugLocalVariable %53 %26 %22 17 15 %50 FlagIsLocal + %57 = OpExtInst %void %1 DebugLocalVariable %56 %35 %22 15 29 %48 FlagIsLocal 1 + %60 = OpExtInst %void %1 DebugTypeComposite %58 Structure %22 0 0 %23 %59 %39 FlagIsProtected|FlagIsPrivate + %62 = OpExtInst %void %1 DebugGlobalVariable %61 %60 %22 3 14 %23 %61 %g_sAniso FlagIsDefinition + %64 = OpExtInst %void %1 DebugGlobalVariable %63 %42 %22 1 11 %23 %63 %g_tColor FlagIsDefinition + %MainPs = OpFunction %void None %65 + %66 = OpLabel + %114 = OpExtInst %void %1 DebugScope %50 + %98 = OpVariable %_ptr_Function_PS_OUTPUT Function + %99 = OpVariable %_ptr_Function_v2float Function + %115 = OpExtInst %void %1 DebugNoScope + %100 = OpVariable %_ptr_Function_PS_OUTPUT Function +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %70 = OpLoad %v2float %in_var_TEXCOORD2 + %71 = OpCompositeConstruct %PS_INPUT %70 + OpStore %param_var_i %71 + %116 = OpExtInst %void %1 DebugScope %48 + %102 = OpExtInst %void %1 DebugDeclare %57 %param_var_i %55 + %117 = OpExtInst %void %1 DebugScope %50 + %103 = OpExtInst %void %1 DebugDeclare %54 %98 %55 + OpLine %20 19 17 + %104 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %105 = OpLoad %v2float %104 + OpLine %20 19 12 + OpStore %99 %105 + %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55 +;CHECK-NOT: %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55 +;CHECK: %119 = OpExtInst %void %1 DebugValue %52 %105 %55 + OpLine %20 20 26 + %107 = OpLoad %type_2d_image %g_tColor + OpLine %20 20 46 + %108 = OpLoad %type_sampler %g_sAniso + OpLine %20 20 57 + %109 = OpLoad %v2float %99 +;CHECK-NOT: %109 = OpLoad %v2float %99 + OpLine %20 20 26 + %110 = OpSampledImage %type_sampled_image %107 %108 + %111 = OpImageSampleImplicitLod %v4float %110 %109 None +;CHECK-NOT: %111 = OpImageSampleImplicitLod %v4float %110 %109 None +;CHECK: %111 = OpImageSampleImplicitLod %v4float %110 %105 None + OpLine %20 20 5 + %112 = OpAccessChain %_ptr_Function_v4float %98 %int_0 + OpStore %112 %111 + OpLine %20 21 12 + %113 = OpLoad %PS_OUTPUT %98 + OpLine %20 21 5 + OpStore %100 %113 + %118 = OpExtInst %void %1 DebugNoScope + %73 = OpLoad %PS_OUTPUT %100 + %74 = OpCompositeExtract %v4float %73 0 + OpStore %out_var_SV_Target0 %74 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalSingleStoreElimTest, DebugValueTest) { + // If OpenCL.DebugInfo.100 enabled, check that store/load is still + // optimized, DebugValue placed after the store and the associated + // DebugValue Deref is removed. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %7 = OpString "foo.frag" + %8 = OpString "PS_OUTPUT" + %9 = OpString "float" + %10 = OpString "vColor" + %11 = OpString "PS_INPUT" + %12 = OpString "vTextureCoords" + %13 = OpString "@type.2d.image" + %14 = OpString "type.2d.image" + %15 = OpString "Texture2D.TemplateParam" + %16 = OpString "src.MainPs" + %17 = OpString "tc" + %18 = OpString "ps_output" + %19 = OpString "i" + %20 = OpString "@type.sampler" + %21 = OpString "type.sampler" + %22 = OpString "g_sAniso" + %23 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_64 = OpConstant %uint 64 + %45 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%_ptr_Function_v4float = OpTypePointer Function %v4float + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %51 = OpExtInst %void %1 DebugInfoNone + %52 = OpExtInst %void %1 DebugExpression + %53 = OpExtInst %void %1 DebugOperation Deref + %54 = OpExtInst %void %1 DebugExpression %53 + %55 = OpExtInst %void %1 DebugSource %7 + %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL + %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58 + %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float + %60 = OpExtInst %void %1 DebugTypeVector %59 4 + %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62 + %63 = OpExtInst %void %1 DebugTypeVector %59 2 + %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate + %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate + %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0 + %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65 + %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61 + %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51 + %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68 + %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal + %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal + %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1 + %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate + %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition + %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition + %MainPs = OpFunction %void None %45 + %76 = OpLabel + %101 = OpExtInst %void %1 DebugScope %69 + %78 = OpVariable %_ptr_Function_PS_OUTPUT Function + %79 = OpVariable %_ptr_Function_v2float Function + %102 = OpExtInst %void %1 DebugNoScope + %81 = OpVariable %_ptr_Function_PS_OUTPUT Function +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %82 = OpLoad %v2float %in_var_TEXCOORD2 + %83 = OpCompositeConstruct %PS_INPUT %82 + OpStore %param_var_i %83 + %103 = OpExtInst %void %1 DebugScope %68 + %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 + %104 = OpExtInst %void %1 DebugScope %69 + %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52 + OpLine %7 19 17 + %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %89 = OpLoad %v2float %88 + OpLine %7 19 12 + OpStore %79 %89 + %90 = OpExtInst %void %1 DebugValue %70 %79 %54 +;CHECK-NOT: %90 = OpExtInst %void %1 DebugValue %70 %79 %54 +;CHECK: %106 = OpExtInst %void %1 DebugValue %70 %89 %52 + OpLine %7 20 26 + %91 = OpLoad %type_2d_image %g_tColor + OpLine %7 20 46 + %92 = OpLoad %type_sampler %g_sAniso + OpLine %7 20 57 + %93 = OpLoad %v2float %79 +;CHECK-NOT: %93 = OpLoad %v2float %79 + OpLine %7 20 26 + %94 = OpSampledImage %type_sampled_image %91 %92 + %95 = OpImageSampleImplicitLod %v4float %94 %93 None +;CHECK-NOT: %95 = OpImageSampleImplicitLod %v4float %94 %93 None +;CHECK: %95 = OpImageSampleImplicitLod %v4float %94 %89 None + OpLine %7 20 5 + %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0 + OpStore %96 %95 + OpLine %7 21 12 + %97 = OpLoad %PS_OUTPUT %78 + OpLine %7 21 5 + OpStore %81 %97 + %105 = OpExtInst %void %1 DebugNoScope + %99 = OpLoad %PS_OUTPUT %81 + %100 = OpCompositeExtract %v4float %99 0 + OpStore %out_var_SV_Target0 %100 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalSingleStoreElimTest, UseStoreLineInfoForDebugValueLine) { + // When the store is in the scope of OpenCL.DebugInfo.100 DebugDeclare, + // the OpLine of the added OpenCL.DebugInfo.100 DebugValue must be the + // same with the OpLine of the store. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR + %7 = OpString "simple.hlsl" + %8 = OpString "float" + %9 = OpString "VS_OUTPUT" + %10 = OpString "color" + %11 = OpString "pos" + %12 = OpString "main" + %13 = OpString "" + %14 = OpString "vout" + OpName %in_var_POSITION "in.var.POSITION" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_COLOR "out.var.COLOR" + OpName %main "main" + OpName %VS_OUTPUT "VS_OUTPUT" + OpMemberName %VS_OUTPUT 0 "pos" + OpMemberName %VS_OUTPUT 1 "color" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %in_var_POSITION Location 0 + OpDecorate %in_var_COLOR Location 1 + OpDecorate %out_var_COLOR Location 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_256 = OpConstant %uint 256 + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %36 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %VS_OUTPUT = OpTypeStruct %v4float %v4float +%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT +%in_var_POSITION = OpVariable %_ptr_Input_v4float Input +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output +%out_var_COLOR = OpVariable %_ptr_Output_v4float Output + %85 = OpExtInst %void %1 DebugOperation Deref + %81 = OpExtInst %void %1 DebugInfoNone + %52 = OpExtInst %void %1 DebugExpression + %40 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float + %41 = OpExtInst %void %1 DebugTypeVector %40 4 + %42 = OpExtInst %void %1 DebugSource %7 + %43 = OpExtInst %void %1 DebugCompilationUnit 1 4 %42 HLSL + %44 = OpExtInst %void %1 DebugTypeComposite %9 Structure %42 1 8 %43 %9 %uint_256 FlagIsProtected|FlagIsPrivate %45 %46 + %46 = OpExtInst %void %1 DebugTypeMember %10 %41 %42 3 10 %44 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate + %45 = OpExtInst %void %1 DebugTypeMember %11 %41 %42 2 10 %44 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %44 %41 %41 + %48 = OpExtInst %void %1 DebugFunction %12 %47 %42 6 1 %43 %13 FlagIsProtected|FlagIsPrivate 7 %81 + %49 = OpExtInst %void %1 DebugLexicalBlock %42 7 38 %48 + %50 = OpExtInst %void %1 DebugLocalVariable %14 %44 %42 8 13 %49 FlagIsLocal + %84 = OpExtInst %void %1 DebugExpression %85 + %main = OpFunction %void None %36 + %54 = OpLabel + %91 = OpExtInst %void %1 DebugScope %49 + OpLine %7 7 23 + %83 = OpVariable %_ptr_Function_v4float Function + OpLine %7 8 13 + %87 = OpExtInst %void %1 DebugValue %50 %83 %84 %int_1 + OpLine %7 7 23 + %82 = OpVariable %_ptr_Function_v4float Function + OpLine %7 8 13 + %86 = OpExtInst %void %1 DebugValue %50 %82 %84 %int_0 + OpNoLine + %92 = OpExtInst %void %1 DebugNoScope + %55 = OpLoad %v4float %in_var_POSITION + %56 = OpLoad %v4float %in_var_COLOR +;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION +;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR + + %94 = OpExtInst %void %1 DebugScope %49 + OpLine %7 9 3 + OpStore %82 %55 +;CHECK: OpLine [[file:%\w+]] 9 3 +;CHECK: OpStore {{%\w+}} [[pos]] +;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[vout:%\w+]] [[pos]] [[empty_expr:%\w+]] %int_0 +;CHECK: OpLine [[file]] 10 3 +;CHECK: OpStore {{%\w+}} [[color]] +;CHECK: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[vout]] [[color]] [[empty_expr]] %int_1 + + OpLine %7 10 3 + OpStore %83 %56 + OpLine %7 11 10 + %90 = OpCompositeConstruct %VS_OUTPUT %55 %56 + OpNoLine + %95 = OpExtInst %void %1 DebugNoScope + %58 = OpCompositeExtract %v4float %90 0 + OpStore %gl_Position %58 + %59 = OpCompositeExtract %v4float %90 1 + OpStore %out_var_COLOR %59 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalSingleStoreElimTest, AddDebugValueforStoreOutOfDebugDeclareScope) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR + %7 = OpString "simple.hlsl" + %8 = OpString "float" + %9 = OpString "VS_OUTPUT" + %10 = OpString "color" + %11 = OpString "pos" + %12 = OpString "main" + %13 = OpString "" + %14 = OpString "vout" + OpName %in_var_POSITION "in.var.POSITION" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_COLOR "out.var.COLOR" + OpName %main "main" + OpName %VS_OUTPUT "VS_OUTPUT" + OpMemberName %VS_OUTPUT 0 "pos" + OpMemberName %VS_OUTPUT 1 "color" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %in_var_POSITION Location 0 + OpDecorate %in_var_COLOR Location 1 + OpDecorate %out_var_COLOR Location 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_256 = OpConstant %uint 256 + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %36 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %VS_OUTPUT = OpTypeStruct %v4float %v4float +%_ptr_Function_VS_OUTPUT = OpTypePointer Function %VS_OUTPUT +%in_var_POSITION = OpVariable %_ptr_Input_v4float Input +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output +%out_var_COLOR = OpVariable %_ptr_Output_v4float Output + %85 = OpExtInst %void %1 DebugOperation Deref + %81 = OpExtInst %void %1 DebugInfoNone + %52 = OpExtInst %void %1 DebugExpression + %40 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float + %41 = OpExtInst %void %1 DebugTypeVector %40 4 + %42 = OpExtInst %void %1 DebugSource %7 + %43 = OpExtInst %void %1 DebugCompilationUnit 1 4 %42 HLSL + %44 = OpExtInst %void %1 DebugTypeComposite %9 Structure %42 1 8 %43 %9 %uint_256 FlagIsProtected|FlagIsPrivate %45 %46 + %46 = OpExtInst %void %1 DebugTypeMember %10 %41 %42 3 10 %44 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate + %45 = OpExtInst %void %1 DebugTypeMember %11 %41 %42 2 10 %44 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %44 %41 %41 + %48 = OpExtInst %void %1 DebugFunction %12 %47 %42 6 1 %43 %13 FlagIsProtected|FlagIsPrivate 7 %81 + %49 = OpExtInst %void %1 DebugLexicalBlock %42 7 38 %48 + %50 = OpExtInst %void %1 DebugLocalVariable %14 %44 %42 8 13 %49 FlagIsLocal + %51 = OpExtInst %void %1 DebugLocalVariable %10 %41 %42 7 23 %48 FlagIsLocal 2 + %53 = OpExtInst %void %1 DebugLocalVariable %11 %41 %42 6 23 %48 FlagIsLocal 1 +;CHECK: [[dbg_color:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable {{%\w+}} {{%\w+}} {{%\w+}} 7 23 {{%\w+}} FlagIsLocal 2 +;CHECK: [[dbg_pos:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable {{%\w+}} {{%\w+}} {{%\w+}} 6 23 {{%\w+}} FlagIsLocal 1 + + %84 = OpExtInst %void %1 DebugExpression %85 + %main = OpFunction %void None %36 + %54 = OpLabel + %91 = OpExtInst %void %1 DebugScope %49 + OpLine %7 7 23 + %83 = OpVariable %_ptr_Function_v4float Function + OpLine %7 8 13 + %87 = OpExtInst %void %1 DebugValue %50 %83 %84 %int_1 + OpLine %7 7 23 + %82 = OpVariable %_ptr_Function_v4float Function + OpLine %7 8 13 + %86 = OpExtInst %void %1 DebugValue %50 %82 %84 %int_0 + OpNoLine + %92 = OpExtInst %void %1 DebugNoScope +%param_var_pos = OpVariable %_ptr_Function_v4float Function +%param_var_color = OpVariable %_ptr_Function_v4float Function + %55 = OpLoad %v4float %in_var_POSITION + OpStore %param_var_pos %55 + %56 = OpLoad %v4float %in_var_COLOR +;CHECK: DebugNoScope +;CHECK-NOT: OpLine +;CHECK: [[pos:%\w+]] = OpLoad %v4float %in_var_POSITION +;CHECK: [[color:%\w+]] = OpLoad %v4float %in_var_COLOR + + OpStore %param_var_color %56 + %93 = OpExtInst %void %1 DebugScope %48 + OpLine %7 6 23 + %73 = OpExtInst %void %1 DebugDeclare %53 %param_var_pos %52 + OpLine %7 7 23 + %74 = OpExtInst %void %1 DebugDeclare %51 %param_var_color %52 +;CHECK: OpLine [[file:%\w+]] 6 23 +;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_pos]] [[pos]] [[empty_expr:%\w+]] +;CHECK: OpLine [[file]] 7 23 +;CHECK-NEXT: {{%\w+}} = OpExtInst %void {{%\w+}} DebugValue [[dbg_color]] [[color]] [[empty_expr]] + + %94 = OpExtInst %void %1 DebugScope %49 + OpLine %7 9 3 + OpStore %82 %55 + OpLine %7 10 3 + OpStore %83 %56 + OpLine %7 11 10 + %90 = OpCompositeConstruct %VS_OUTPUT %55 %56 + OpNoLine + %95 = OpExtInst %void %1 DebugNoScope + %58 = OpCompositeExtract %v4float %90 0 + OpStore %gl_Position %58 + %59 = OpCompositeExtract %v4float %90 1 + OpStore %out_var_COLOR %59 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, false); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// Other types +// Others? + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/local_ssa_elim_test.cpp b/third_party/spirv-tools/test/opt/local_ssa_elim_test.cpp new file mode 100644 index 0000000..ff42193 --- /dev/null +++ b/third_party/spirv-tools/test/opt/local_ssa_elim_test.cpp @@ -0,0 +1,3943 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using LocalSSAElimTest = PassTest<::testing::Test>; + +TEST_F(LocalSSAElimTest, ForLoop) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // for (int i=0; i<4; i++) { + // f = f + BC[i]; + // } + // fo = f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%22 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %23 +%23 = OpLabel +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%22 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %23 +%23 = OpLabel +%39 = OpPhi %float %float_0 %22 %34 %25 +%38 = OpPhi %int %int_0 %22 %36 %25 +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%28 = OpSLessThan %bool %38 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%32 = OpAccessChain %_ptr_Input_float %BC %38 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %39 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%36 = OpIAdd %int %38 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +OpStore %fo %39 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(LocalSSAElimTest, NestedForLoop) { + // #version 450 + // + // layout (location=0) in mat4 BC; + // layout (location=0) out float fo; + // + // void main() + // { + // float f = 0.0; + // for (int i=0; i<4; i++) + // for (int j=0; j<4; j++) + // f = f + BC[i][j]; + // fo = f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %j "j" +OpName %BC "BC" +OpName %fo "fo" +OpDecorate %BC Location 0 +OpDecorate %fo Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 +%_ptr_Input_mat4v4float = OpTypePointer Input %mat4v4float +%BC = OpVariable %_ptr_Input_mat4v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +)"; + + const std::string before = + R"( +; CHECK: = OpFunction +; CHECK-NEXT: [[entry:%\w+]] = OpLabel +; CHECK: [[outer_header:%\w+]] = OpLabel +; CHECK-NEXT: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]] +; CHECK-NEXT: [[i:%\w+]] = OpPhi %int %int_0 [[entry]] [[i_next:%\w+]] [[outer_be]] +; CHECK-NEXT: OpSLessThan {{%\w+}} [[i]] +; CHECK: [[inner_pre_header:%\w+]] = OpLabel +; CHECK: [[inner_header:%\w+]] = OpLabel +; CHECK-NEXT: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]] +; CHECK-NEXT: [[j:%\w+]] = OpPhi %int %int_0 [[inner_pre_header]] [[j_next:%\w+]] [[inner_be]] +; CHECK: [[inner_be]] = OpLabel +; CHECK: [[f_next]] = OpFAdd %float [[inner_f]] +; CHECK: [[j_next]] = OpIAdd %int [[j]] %int_1 +; CHECK: [[outer_be]] = OpLabel +; CHECK: [[i_next]] = OpIAdd +; CHECK: OpStore %fo [[outer_f]] +%main = OpFunction %void None %9 +%24 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%j = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %25 +%25 = OpLabel +%26 = OpLoad %int %i +%27 = OpSLessThan %bool %26 %int_4 +OpLoopMerge %28 %29 None +OpBranchConditional %27 %30 %28 +%30 = OpLabel +OpStore %j %int_0 +OpBranch %31 +%31 = OpLabel +%32 = OpLoad %int %j +%33 = OpSLessThan %bool %32 %int_4 +OpLoopMerge %50 %34 None +OpBranchConditional %33 %34 %50 +%34 = OpLabel +%35 = OpLoad %float %f +%36 = OpLoad %int %i +%37 = OpLoad %int %j +%38 = OpAccessChain %_ptr_Input_float %BC %36 %37 +%39 = OpLoad %float %38 +%40 = OpFAdd %float %35 %39 +OpStore %f %40 +%41 = OpLoad %int %j +%42 = OpIAdd %int %41 %int_1 +OpStore %j %42 +OpBranch %31 +%50 = OpLabel +OpBranch %29 +%29 = OpLabel +%43 = OpLoad %int %i +%44 = OpIAdd %int %43 %int_1 +OpStore %i %44 +OpBranch %25 +%28 = OpLabel +%45 = OpLoad %float %f +OpStore %fo %45 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(predefs + before, true); +} + +TEST_F(LocalSSAElimTest, ForLoopWithContinue) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // for (int i=0; i<4; i++) { + // float t = BC[i]; + // if (t < 0.0) + // continue; + // f = f + t; + // } + // fo = f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +)"; + + const std::string names = + R"(OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %t "t" +OpName %BC "BC" +OpName %fo "fo" +)"; + + const std::string predefs2 = + R"(%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%23 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%t = OpVariable %_ptr_Function_float Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %24 +%24 = OpLabel +OpLoopMerge %25 %26 None +OpBranch %27 +%27 = OpLabel +%28 = OpLoad %int %i +%29 = OpSLessThan %bool %28 %int_4 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +OpStore %t %33 +%34 = OpLoad %float %t +%35 = OpFOrdLessThan %bool %34 %float_0 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %36 +%37 = OpLabel +OpBranch %26 +%36 = OpLabel +%38 = OpLoad %float %f +%39 = OpLoad %float %t +%40 = OpFAdd %float %38 %39 +OpStore %f %40 +OpBranch %26 +%26 = OpLabel +%41 = OpLoad %int %i +%42 = OpIAdd %int %41 %int_1 +OpStore %i %42 +OpBranch %24 +%25 = OpLabel +%43 = OpLoad %float %f +OpStore %fo %43 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%23 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%t = OpVariable %_ptr_Function_float Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %24 +%24 = OpLabel +%45 = OpPhi %float %float_0 %23 %47 %26 +%44 = OpPhi %int %int_0 %23 %42 %26 +OpLoopMerge %25 %26 None +OpBranch %27 +%27 = OpLabel +%29 = OpSLessThan %bool %44 %int_4 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +%32 = OpAccessChain %_ptr_Input_float %BC %44 +%33 = OpLoad %float %32 +OpStore %t %33 +%35 = OpFOrdLessThan %bool %33 %float_0 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %36 +%37 = OpLabel +OpBranch %26 +%36 = OpLabel +%40 = OpFAdd %float %45 %33 +OpStore %f %40 +OpBranch %26 +%26 = OpLabel +%47 = OpPhi %float %45 %37 %40 %36 +%42 = OpIAdd %int %44 %int_1 +OpStore %i %42 +OpBranch %24 +%25 = OpLabel +OpStore %fo %45 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + names + predefs2 + before, + predefs + names + predefs2 + after, + true, true); +} + +TEST_F(LocalSSAElimTest, ForLoopWithBreak) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // for (int i=0; i<4; i++) { + // float t = f + BC[i]; + // if (t > 1.0) + // break; + // f = t; + // } + // fo = f; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %t "t" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%24 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%t = OpVariable %_ptr_Function_float Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %25 +%25 = OpLabel +OpLoopMerge %26 %27 None +OpBranch %28 +%28 = OpLabel +%29 = OpLoad %int %i +%30 = OpSLessThan %bool %29 %int_4 +OpBranchConditional %30 %31 %26 +%31 = OpLabel +%32 = OpLoad %float %f +%33 = OpLoad %int %i +%34 = OpAccessChain %_ptr_Input_float %BC %33 +%35 = OpLoad %float %34 +%36 = OpFAdd %float %32 %35 +OpStore %t %36 +%37 = OpLoad %float %t +%38 = OpFOrdGreaterThan %bool %37 %float_1 +OpSelectionMerge %39 None +OpBranchConditional %38 %40 %39 +%40 = OpLabel +OpBranch %26 +%39 = OpLabel +%41 = OpLoad %float %t +OpStore %f %41 +OpBranch %27 +%27 = OpLabel +%42 = OpLoad %int %i +%43 = OpIAdd %int %42 %int_1 +OpStore %i %43 +OpBranch %25 +%26 = OpLabel +%44 = OpLoad %float %f +OpStore %fo %44 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%24 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%t = OpVariable %_ptr_Function_float Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %25 +%25 = OpLabel +%46 = OpPhi %float %float_0 %24 %36 %27 +%45 = OpPhi %int %int_0 %24 %43 %27 +OpLoopMerge %26 %27 None +OpBranch %28 +%28 = OpLabel +%30 = OpSLessThan %bool %45 %int_4 +OpBranchConditional %30 %31 %26 +%31 = OpLabel +%34 = OpAccessChain %_ptr_Input_float %BC %45 +%35 = OpLoad %float %34 +%36 = OpFAdd %float %46 %35 +OpStore %t %36 +%38 = OpFOrdGreaterThan %bool %36 %float_1 +OpSelectionMerge %39 None +OpBranchConditional %38 %40 %39 +%40 = OpLabel +OpBranch %26 +%39 = OpLabel +OpStore %f %36 +OpBranch %27 +%27 = OpLabel +%43 = OpIAdd %int %45 %int_1 +OpStore %i %43 +OpBranch %25 +%26 = OpLabel +OpStore %fo %46 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(LocalSSAElimTest, SwapProblem) { + // #version 140 + // + // in float fe; + // out float fo; + // + // void main() + // { + // float f1 = 0.0; + // float f2 = 1.0; + // int ie = int(fe); + // for (int i=0; i(predefs + before, predefs + after, true, + true); +} + +TEST_F(LocalSSAElimTest, LostCopyProblem) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // float t; + // for (int i=0; i<4; i++) { + // t = f; + // f = f + BC[i]; + // if (f > 1.0) + // break; + // } + // fo = t; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %t "t" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%24 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%t = OpVariable %_ptr_Function_float Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %25 +%25 = OpLabel +OpLoopMerge %26 %27 None +OpBranch %28 +%28 = OpLabel +%29 = OpLoad %int %i +%30 = OpSLessThan %bool %29 %int_4 +OpBranchConditional %30 %31 %26 +%31 = OpLabel +%32 = OpLoad %float %f +OpStore %t %32 +%33 = OpLoad %float %f +%34 = OpLoad %int %i +%35 = OpAccessChain %_ptr_Input_float %BC %34 +%36 = OpLoad %float %35 +%37 = OpFAdd %float %33 %36 +OpStore %f %37 +%38 = OpLoad %float %f +%39 = OpFOrdGreaterThan %bool %38 %float_1 +OpSelectionMerge %40 None +OpBranchConditional %39 %41 %40 +%41 = OpLabel +OpBranch %26 +%40 = OpLabel +OpBranch %27 +%27 = OpLabel +%42 = OpLoad %int %i +%43 = OpIAdd %int %42 %int_1 +OpStore %i %43 +OpBranch %25 +%26 = OpLabel +%44 = OpLoad %float %t +OpStore %fo %44 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%49 = OpUndef %float +%main = OpFunction %void None %9 +%24 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%t = OpVariable %_ptr_Function_float Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %25 +%25 = OpLabel +%46 = OpPhi %float %float_0 %24 %37 %27 +%45 = OpPhi %int %int_0 %24 %43 %27 +%48 = OpPhi %float %49 %24 %46 %27 +OpLoopMerge %26 %27 None +OpBranch %28 +%28 = OpLabel +%30 = OpSLessThan %bool %45 %int_4 +OpBranchConditional %30 %31 %26 +%31 = OpLabel +OpStore %t %46 +%35 = OpAccessChain %_ptr_Input_float %BC %45 +%36 = OpLoad %float %35 +%37 = OpFAdd %float %46 %36 +OpStore %f %37 +%39 = OpFOrdGreaterThan %bool %37 %float_1 +OpSelectionMerge %40 None +OpBranchConditional %39 %41 %40 +%41 = OpLabel +OpBranch %26 +%40 = OpLabel +OpBranch %27 +%27 = OpLabel +%43 = OpIAdd %int %45 %int_1 +OpStore %i %43 +OpBranch %25 +%26 = OpLabel +%47 = OpPhi %float %48 %28 %46 %41 +OpStore %fo %47 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(LocalSSAElimTest, IfThenElse) { + // #version 140 + // + // in vec4 BaseColor; + // in float f; + // + // void main() + // { + // vec4 v; + // if (f >= 0) + // v = BaseColor * 0.5; + // else + // v = BaseColor + vec4(1.0,1.0,1.0,1.0); + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %f %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %f "f" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float +%f = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%float_0_5 = OpConstant %float 0.5 +%float_1 = OpConstant %float 1 +%18 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%20 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%21 = OpLoad %float %f +%22 = OpFOrdGreaterThanEqual %bool %21 %float_0 +OpSelectionMerge %23 None +OpBranchConditional %22 %24 %25 +%24 = OpLabel +%26 = OpLoad %v4float %BaseColor +%27 = OpVectorTimesScalar %v4float %26 %float_0_5 +OpStore %v %27 +OpBranch %23 +%25 = OpLabel +%28 = OpLoad %v4float %BaseColor +%29 = OpFAdd %v4float %28 %18 +OpStore %v %29 +OpBranch %23 +%23 = OpLabel +%30 = OpLoad %v4float %v +OpStore %gl_FragColor %30 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%20 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%21 = OpLoad %float %f +%22 = OpFOrdGreaterThanEqual %bool %21 %float_0 +OpSelectionMerge %23 None +OpBranchConditional %22 %24 %25 +%24 = OpLabel +%26 = OpLoad %v4float %BaseColor +%27 = OpVectorTimesScalar %v4float %26 %float_0_5 +OpStore %v %27 +OpBranch %23 +%25 = OpLabel +%28 = OpLoad %v4float %BaseColor +%29 = OpFAdd %v4float %28 %18 +OpStore %v %29 +OpBranch %23 +%23 = OpLabel +%31 = OpPhi %v4float %27 %24 %29 %25 +OpStore %gl_FragColor %31 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(LocalSSAElimTest, IfThen) { + // #version 140 + // + // in vec4 BaseColor; + // in float f; + // + // void main() + // { + // vec4 v = BaseColor; + // if (f <= 0) + // v = v * 0.5; + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %f %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %f "f" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%f = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%float_0_5 = OpConstant %float 0.5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %8 +%18 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%19 = OpLoad %v4float %BaseColor +OpStore %v %19 +%20 = OpLoad %float %f +%21 = OpFOrdLessThanEqual %bool %20 %float_0 +OpSelectionMerge %22 None +OpBranchConditional %21 %23 %22 +%23 = OpLabel +%24 = OpLoad %v4float %v +%25 = OpVectorTimesScalar %v4float %24 %float_0_5 +OpStore %v %25 +OpBranch %22 +%22 = OpLabel +%26 = OpLoad %v4float %v +OpStore %gl_FragColor %26 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %8 +%18 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%19 = OpLoad %v4float %BaseColor +OpStore %v %19 +%20 = OpLoad %float %f +%21 = OpFOrdLessThanEqual %bool %20 %float_0 +OpSelectionMerge %22 None +OpBranchConditional %21 %23 %22 +%23 = OpLabel +%25 = OpVectorTimesScalar %v4float %19 %float_0_5 +OpStore %v %25 +OpBranch %22 +%22 = OpLabel +%27 = OpPhi %v4float %19 %18 %25 %23 +OpStore %gl_FragColor %27 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(LocalSSAElimTest, Switch) { + // #version 140 + // + // in vec4 BaseColor; + // in float f; + // + // void main() + // { + // vec4 v = BaseColor; + // int i = int(f); + // switch (i) { + // case 0: + // v = v * 0.25; + // break; + // case 1: + // v = v * 0.625; + // break; + // case 2: + // v = v * 0.75; + // break; + // default: + // break; + // } + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %f %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %i "i" +OpName %f "f" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_float = OpTypePointer Input %float +%f = OpVariable %_ptr_Input_float Input +%float_0_25 = OpConstant %float 0.25 +%float_0_625 = OpConstant %float 0.625 +%float_0_75 = OpConstant %float 0.75 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%21 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%i = OpVariable %_ptr_Function_int Function +%22 = OpLoad %v4float %BaseColor +OpStore %v %22 +%23 = OpLoad %float %f +%24 = OpConvertFToS %int %23 +OpStore %i %24 +%25 = OpLoad %int %i +OpSelectionMerge %26 None +OpSwitch %25 %27 0 %28 1 %29 2 %30 +%27 = OpLabel +OpBranch %26 +%28 = OpLabel +%31 = OpLoad %v4float %v +%32 = OpVectorTimesScalar %v4float %31 %float_0_25 +OpStore %v %32 +OpBranch %26 +%29 = OpLabel +%33 = OpLoad %v4float %v +%34 = OpVectorTimesScalar %v4float %33 %float_0_625 +OpStore %v %34 +OpBranch %26 +%30 = OpLabel +%35 = OpLoad %v4float %v +%36 = OpVectorTimesScalar %v4float %35 %float_0_75 +OpStore %v %36 +OpBranch %26 +%26 = OpLabel +%37 = OpLoad %v4float %v +OpStore %gl_FragColor %37 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%21 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%i = OpVariable %_ptr_Function_int Function +%22 = OpLoad %v4float %BaseColor +OpStore %v %22 +%23 = OpLoad %float %f +%24 = OpConvertFToS %int %23 +OpStore %i %24 +OpSelectionMerge %26 None +OpSwitch %24 %27 0 %28 1 %29 2 %30 +%27 = OpLabel +OpBranch %26 +%28 = OpLabel +%32 = OpVectorTimesScalar %v4float %22 %float_0_25 +OpStore %v %32 +OpBranch %26 +%29 = OpLabel +%34 = OpVectorTimesScalar %v4float %22 %float_0_625 +OpStore %v %34 +OpBranch %26 +%30 = OpLabel +%36 = OpVectorTimesScalar %v4float %22 %float_0_75 +OpStore %v %36 +OpBranch %26 +%26 = OpLabel +%38 = OpPhi %v4float %22 %27 %32 %28 %34 %29 %36 %30 +OpStore %gl_FragColor %38 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(LocalSSAElimTest, SwitchWithFallThrough) { + // #version 140 + // + // in vec4 BaseColor; + // in float f; + // + // void main() + // { + // vec4 v = BaseColor; + // int i = int(f); + // switch (i) { + // case 0: + // v = v * 0.25; + // break; + // case 1: + // v = v + 0.25; + // case 2: + // v = v * 0.75; + // break; + // default: + // break; + // } + // gl_FragColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %f %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %i "i" +OpName %f "f" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_float = OpTypePointer Input %float +%f = OpVariable %_ptr_Input_float Input +%float_0_25 = OpConstant %float 0.25 +%float_0_75 = OpConstant %float 0.75 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string before = + R"(%main = OpFunction %void None %9 +%20 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%i = OpVariable %_ptr_Function_int Function +%21 = OpLoad %v4float %BaseColor +OpStore %v %21 +%22 = OpLoad %float %f +%23 = OpConvertFToS %int %22 +OpStore %i %23 +%24 = OpLoad %int %i +OpSelectionMerge %25 None +OpSwitch %24 %26 0 %27 1 %28 2 %29 +%26 = OpLabel +OpBranch %25 +%27 = OpLabel +%30 = OpLoad %v4float %v +%31 = OpVectorTimesScalar %v4float %30 %float_0_25 +OpStore %v %31 +OpBranch %25 +%28 = OpLabel +%32 = OpLoad %v4float %v +%33 = OpCompositeConstruct %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25 +%34 = OpFAdd %v4float %32 %33 +OpStore %v %34 +OpBranch %29 +%29 = OpLabel +%35 = OpLoad %v4float %v +%36 = OpVectorTimesScalar %v4float %35 %float_0_75 +OpStore %v %36 +OpBranch %25 +%25 = OpLabel +%37 = OpLoad %v4float %v +OpStore %gl_FragColor %37 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %9 +%20 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%i = OpVariable %_ptr_Function_int Function +%21 = OpLoad %v4float %BaseColor +OpStore %v %21 +%22 = OpLoad %float %f +%23 = OpConvertFToS %int %22 +OpStore %i %23 +OpSelectionMerge %25 None +OpSwitch %23 %26 0 %27 1 %28 2 %29 +%26 = OpLabel +OpBranch %25 +%27 = OpLabel +%31 = OpVectorTimesScalar %v4float %21 %float_0_25 +OpStore %v %31 +OpBranch %25 +%28 = OpLabel +%33 = OpCompositeConstruct %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25 +%34 = OpFAdd %v4float %21 %33 +OpStore %v %34 +OpBranch %29 +%29 = OpLabel +%38 = OpPhi %v4float %21 %20 %34 %28 +%36 = OpVectorTimesScalar %v4float %38 %float_0_75 +OpStore %v %36 +OpBranch %25 +%25 = OpLabel +%39 = OpPhi %v4float %21 %26 %31 %27 %36 %29 +OpStore %gl_FragColor %39 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + before, predefs + after, true, + true); +} + +TEST_F(LocalSSAElimTest, DontPatchPhiInLoopHeaderThatIsNotAVar) { + // From https://github.com/KhronosGroup/SPIRV-Tools/issues/826 + // Don't try patching the (%16 %7) value/predecessor pair in the OpPhi. + // That OpPhi is unrelated to this optimization: we did not set that up + // in the SSA initialization for the loop header block. + // The pass should be a no-op on this module. + + const std::string before = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%1 = OpFunction %void None %3 +%6 = OpLabel +OpBranch %7 +%7 = OpLabel +%8 = OpPhi %float %float_1 %6 %9 %7 +%9 = OpFAdd %float %8 %float_1 +OpLoopMerge %10 %7 None +OpBranch %7 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, before, true, true); +} + +TEST_F(LocalSSAElimTest, OptInitializedVariableLikeStore) { + // Note: SPIR-V edited to change store to v into variable initialization + // + // #version 450 + // + // layout (location=0) in vec4 iColor; + // layout (location=1) in float fi; + // layout (location=0) out vec4 oColor; + // + // void main() + // { + // vec4 v = vec4(0.0); + // if (fi < 0.0) + // v.x = iColor.x; + // oColor = v; + // } + + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %fi %iColor %oColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %fi "fi" +OpName %iColor "iColor" +OpName %oColor "oColor" +OpDecorate %fi Location 1 +OpDecorate %iColor Location 0 +OpDecorate %oColor Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%float_0 = OpConstant %float 0 +%13 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_float = OpTypePointer Input %float +%fi = OpVariable %_ptr_Input_float Input +%bool = OpTypeBool +%_ptr_Input_v4float = OpTypePointer Input %v4float +%iColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%oColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %8 +%21 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function %13 +%22 = OpLoad %float %fi +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +%26 = OpAccessChain %_ptr_Input_float %iColor %uint_0 +%27 = OpLoad %float %26 +%28 = OpLoad %v4float %v +%29 = OpCompositeInsert %v4float %27 %28 0 +OpStore %v %29 +OpBranch %24 +%24 = OpLabel +%30 = OpLoad %v4float %v +OpStore %oColor %30 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %8 +%21 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function %13 +%22 = OpLoad %float %fi +%23 = OpFOrdLessThan %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %24 +%25 = OpLabel +%26 = OpAccessChain %_ptr_Input_float %iColor %uint_0 +%27 = OpLoad %float %26 +%29 = OpCompositeInsert %v4float %27 %13 0 +OpStore %v %29 +OpBranch %24 +%24 = OpLabel +%31 = OpPhi %v4float %13 %21 %29 %25 +OpStore %oColor %31 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(predefs + func_before, + predefs + func_after, true, true); +} + +TEST_F(LocalSSAElimTest, PointerVariable) { + // Test that checks if a pointer variable is removed. + + const std::string before = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_runtimearr__struct_3 ArrayStride 16 +OpMemberDecorate %_struct_5 0 Offset 0 +OpDecorate %_struct_5 BufferBlock +OpMemberDecorate %_struct_6 0 Offset 0 +OpDecorate %_struct_6 BufferBlock +OpDecorate %2 Location 0 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_struct_3 = OpTypeStruct %v4float +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 +%_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 +%_struct_6 = OpTypeStruct %int +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 +%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5 +%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%2 = OpVariable %_ptr_Output_v4float Output +%7 = OpVariable %_ptr_Uniform__struct_5 Uniform +%1 = OpFunction %void None %10 +%23 = OpLabel +%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function +OpStore %24 %7 +%26 = OpLoad %_ptr_Uniform__struct_5 %24 +%27 = OpAccessChain %_ptr_Uniform_v4float %26 %int_0 %uint_0 %int_0 +%28 = OpLoad %v4float %27 +%29 = OpCopyObject %v4float %28 +OpStore %2 %28 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_runtimearr__struct_3 ArrayStride 16 +OpMemberDecorate %_struct_5 0 Offset 0 +OpDecorate %_struct_5 BufferBlock +OpMemberDecorate %_struct_6 0 Offset 0 +OpDecorate %_struct_6 BufferBlock +OpDecorate %2 Location 0 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_struct_3 = OpTypeStruct %v4float +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 +%_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 +%_struct_6 = OpTypeStruct %int +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 +%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5 +%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%2 = OpVariable %_ptr_Output_v4float Output +%7 = OpVariable %_ptr_Uniform__struct_5 Uniform +%1 = OpFunction %void None %10 +%23 = OpLabel +%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function +OpStore %24 %7 +%27 = OpAccessChain %_ptr_Uniform_v4float %7 %int_0 %uint_0 %int_0 +%28 = OpLoad %v4float %27 +%29 = OpCopyObject %v4float %28 +OpStore %2 %28 +OpReturn +OpFunctionEnd +)"; + + // Relax logical pointers to allow pointer allocations. + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ValidatorOptions()->relax_logical_pointer = true; + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(LocalSSAElimTest, VerifyInstToBlockMap) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // for (int i=0; i<4; i++) { + // f = f + BC[i]; + // } + // fo = f; + // } + + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %8 +%22 = OpLabel +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +OpBranch %23 +%23 = OpLabel +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(nullptr, context); + + // Force the instruction to block mapping to get built. + context->get_instr_block(27u); + + auto pass = MakeUnique(); + pass->SetMessageConsumer(nullptr); + const auto status = pass->Run(context.get()); + EXPECT_TRUE(status == Pass::Status::SuccessWithChange); +} + +TEST_F(LocalSSAElimTest, CompositeExtractProblem) { + const std::string spv_asm = R"( + OpCapability Tessellation + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint TessellationControl %2 "main" %16 %17 %18 %20 %22 %26 %27 %30 %31 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %v3float = OpTypeVector %float 3 + %v2float = OpTypeVector %float 2 + %_struct_11 = OpTypeStruct %v4float %v4float %v4float %v3float %v3float %v2float %v2float +%_arr__struct_11_uint_3 = OpTypeArray %_struct_11 %uint_3 +%_ptr_Function__arr__struct_11_uint_3 = OpTypePointer Function %_arr__struct_11_uint_3 +%_arr_v4float_uint_3 = OpTypeArray %v4float %uint_3 +%_ptr_Input__arr_v4float_uint_3 = OpTypePointer Input %_arr_v4float_uint_3 + %16 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input + %17 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input + %18 = OpVariable %_ptr_Input__arr_v4float_uint_3 Input +%_ptr_Input_uint = OpTypePointer Input %uint + %20 = OpVariable %_ptr_Input_uint Input +%_ptr_Output__arr_v4float_uint_3 = OpTypePointer Output %_arr_v4float_uint_3 + %22 = OpVariable %_ptr_Output__arr_v4float_uint_3 Output +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_arr_v3float_uint_3 = OpTypeArray %v3float %uint_3 +%_ptr_Input__arr_v3float_uint_3 = OpTypePointer Input %_arr_v3float_uint_3 + %26 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input + %27 = OpVariable %_ptr_Input__arr_v3float_uint_3 Input +%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3 +%_ptr_Input__arr_v2float_uint_3 = OpTypePointer Input %_arr_v2float_uint_3 + %30 = OpVariable %_ptr_Input__arr_v2float_uint_3 Input + %31 = OpVariable %_ptr_Input__arr_v2float_uint_3 Input +%_ptr_Function__struct_11 = OpTypePointer Function %_struct_11 + %2 = OpFunction %void None %4 + %33 = OpLabel + %66 = OpVariable %_ptr_Function__arr__struct_11_uint_3 Function + %34 = OpLoad %_arr_v4float_uint_3 %16 + %35 = OpLoad %_arr_v4float_uint_3 %17 + %36 = OpLoad %_arr_v4float_uint_3 %18 + %37 = OpLoad %_arr_v3float_uint_3 %26 + %38 = OpLoad %_arr_v3float_uint_3 %27 + %39 = OpLoad %_arr_v2float_uint_3 %30 + %40 = OpLoad %_arr_v2float_uint_3 %31 + %41 = OpCompositeExtract %v4float %34 0 + %42 = OpCompositeExtract %v4float %35 0 + %43 = OpCompositeExtract %v4float %36 0 + %44 = OpCompositeExtract %v3float %37 0 + %45 = OpCompositeExtract %v3float %38 0 + %46 = OpCompositeExtract %v2float %39 0 + %47 = OpCompositeExtract %v2float %40 0 + %48 = OpCompositeConstruct %_struct_11 %41 %42 %43 %44 %45 %46 %47 + %49 = OpCompositeExtract %v4float %34 1 + %50 = OpCompositeExtract %v4float %35 1 + %51 = OpCompositeExtract %v4float %36 1 + %52 = OpCompositeExtract %v3float %37 1 + %53 = OpCompositeExtract %v3float %38 1 + %54 = OpCompositeExtract %v2float %39 1 + %55 = OpCompositeExtract %v2float %40 1 + %56 = OpCompositeConstruct %_struct_11 %49 %50 %51 %52 %53 %54 %55 + %57 = OpCompositeExtract %v4float %34 2 + %58 = OpCompositeExtract %v4float %35 2 + %59 = OpCompositeExtract %v4float %36 2 + %60 = OpCompositeExtract %v3float %37 2 + %61 = OpCompositeExtract %v3float %38 2 + %62 = OpCompositeExtract %v2float %39 2 + %63 = OpCompositeExtract %v2float %40 2 + %64 = OpCompositeConstruct %_struct_11 %57 %58 %59 %60 %61 %62 %63 + %65 = OpCompositeConstruct %_arr__struct_11_uint_3 %48 %56 %64 + %67 = OpLoad %uint %20 + +; CHECK OpStore {{%\d+}} [[store_source:%\d+]] + OpStore %66 %65 + %68 = OpAccessChain %_ptr_Function__struct_11 %66 %67 + +; This load was being removed, because %_ptr_Function__struct_11 was being +; wrongfully considered an SSA target. +; CHECK OpLoad %_struct_11 %68 + %69 = OpLoad %_struct_11 %68 + +; Similarly, %69 cannot be replaced with %65. +; CHECK-NOT: OpCompositeExtract %v4float [[store_source]] 0 + %70 = OpCompositeExtract %v4float %69 0 + + %71 = OpAccessChain %_ptr_Output_v4float %22 %67 + OpStore %71 %70 + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(spv_asm, true); +} + +// Test that the RelaxedPrecision decoration on the variable to added to the +// result of the OpPhi instruction. +TEST_F(LocalSSAElimTest, DecoratedVariable) { + const std::string spv_asm = R"( +; CHECK: OpDecorate [[var:%\w+]] RelaxedPrecision +; CHECK: OpDecorate [[phi_id:%\w+]] RelaxedPrecision +; CHECK: [[phi_id]] = OpPhi + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpDecorate %v RelaxedPrecision + %void = OpTypeVoid + %func_t = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %int = OpTypeInt 32 0 + %int_p = OpTypePointer Function %int + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %2 = OpFunction %void None %func_t + %33 = OpLabel + %v = OpVariable %int_p Function + OpSelectionMerge %merge None + OpBranchConditional %true %l1 %l2 + %l1 = OpLabel + OpStore %v %int_1 + OpBranch %merge + %l2 = OpLabel + OpStore %v %int_0 + OpBranch %merge + %merge = OpLabel + %ld = OpLoad %int %v + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(spv_asm, true); +} + +// Test that the RelaxedPrecision decoration on the variable to added to the +// result of the OpPhi instruction. +TEST_F(LocalSSAElimTest, MultipleEdges) { + const std::string spv_asm = R"( + ; CHECK: OpSelectionMerge + ; CHECK: [[header_bb:%\w+]] = OpLabel + ; CHECK-NOT: OpLabel + ; CHECK: OpSwitch {{%\w+}} {{%\w+}} 76 [[bb1:%\w+]] 17 [[bb2:%\w+]] + ; CHECK-SAME: 4 [[bb2]] + ; CHECK: [[bb2]] = OpLabel + ; CHECK-NEXT: OpPhi [[type:%\w+]] [[val:%\w+]] [[header_bb]] %int_0 [[bb1]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %true = OpConstantTrue %bool + %false = OpConstantFalse %bool + %int_1 = OpConstant %int 1 + %4 = OpFunction %void None %3 + %5 = OpLabel + %8 = OpVariable %_ptr_Function_int Function + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + OpBranchConditional %true %11 %12 + %11 = OpLabel + OpSelectionMerge %19 None + OpBranchConditional %false %18 %19 + %18 = OpLabel + OpSelectionMerge %22 None + OpSwitch %int_0 %22 76 %20 17 %21 4 %21 + %20 = OpLabel + %23 = OpLoad %int %8 + OpStore %8 %int_0 + OpBranch %21 + %21 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %19 + %19 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(spv_asm, true); +} + +TEST_F(LocalSSAElimTest, VariablePointerTest1) { + // Check that the load of the first variable is still used and that the load + // of the third variable is propagated. The first load has to remain because + // of the store to the variable pointer. + const std::string text = R"( +; CHECK: [[v1:%\w+]] = OpVariable +; CHECK: [[v2:%\w+]] = OpVariable +; CHECK: [[v3:%\w+]] = OpVariable +; CHECK: [[ld1:%\w+]] = OpLoad %int [[v1]] +; CHECK: OpIAdd %int [[ld1]] %int_0 + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %_struct_3 0 Offset 0 + OpMemberDecorate %_struct_3 1 Offset 4 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %_struct_3 = OpTypeStruct %int %int +%_ptr_Function__struct_3 = OpTypePointer Function %_struct_3 +%_ptr_Function_int = OpTypePointer Function %int + %true = OpConstantTrue %bool + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %13 = OpConstantNull %_struct_3 + %2 = OpFunction %void None %5 + %14 = OpLabel + %15 = OpVariable %_ptr_Function_int Function + %16 = OpVariable %_ptr_Function_int Function + %17 = OpVariable %_ptr_Function_int Function + OpStore %15 %int_1 + OpStore %17 %int_0 + OpSelectionMerge %18 None + OpBranchConditional %true %19 %20 + %19 = OpLabel + OpBranch %18 + %20 = OpLabel + OpBranch %18 + %18 = OpLabel + %21 = OpPhi %_ptr_Function_int %15 %19 %16 %20 + OpStore %21 %int_0 + %22 = OpLoad %int %15 + %23 = OpLoad %int %17 + %24 = OpIAdd %int %22 %23 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalSSAElimTest, VariablePointerTest2) { + // Check that the load of the first variable is still used and that the load + // of the third variable is propagated. The first load has to remain because + // of the store to the variable pointer. + const std::string text = R"( +; CHECK: [[v1:%\w+]] = OpVariable +; CHECK: [[v2:%\w+]] = OpVariable +; CHECK: [[v3:%\w+]] = OpVariable +; CHECK: [[ld1:%\w+]] = OpLoad %int [[v1]] +; CHECK: OpIAdd %int [[ld1]] %int_0 + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + OpSource GLSL 450 + OpMemberDecorate %_struct_3 0 Offset 0 + OpMemberDecorate %_struct_3 1 Offset 4 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool + %_struct_3 = OpTypeStruct %int %int +%_ptr_Function__struct_3 = OpTypePointer Function %_struct_3 +%_ptr_Function_int = OpTypePointer Function %int + %true = OpConstantTrue %bool + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %13 = OpConstantNull %_struct_3 + %2 = OpFunction %void None %5 + %14 = OpLabel + %15 = OpVariable %_ptr_Function_int Function + %16 = OpVariable %_ptr_Function_int Function + %17 = OpVariable %_ptr_Function_int Function + OpStore %15 %int_1 + OpStore %17 %int_0 + OpSelectionMerge %18 None + OpBranchConditional %true %19 %20 + %19 = OpLabel + OpBranch %18 + %20 = OpLabel + OpBranch %18 + %18 = OpLabel + %21 = OpPhi %_ptr_Function_int %15 %19 %16 %20 + OpStore %21 %int_0 + %22 = OpLoad %int %15 + %23 = OpLoad %int %17 + %24 = OpIAdd %int %22 %23 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalSSAElimTest, ChainedTrivialPhis) { + // Check that the copy object get the undef value implicitly assigned in the + // entry block. + const std::string text = R"( +; CHECK: [[undef:%\w+]] = OpUndef %v4float +; CHECK: OpCopyObject %v4float [[undef]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 18 6 + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %2 = OpFunction %void None %4 + %9 = OpLabel + %10 = OpVariable %_ptr_Function_v4float Function + OpBranch %11 + %11 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpUndef %bool + OpBranchConditional %15 %16 %12 + %16 = OpLabel + %17 = OpUndef %bool + OpSelectionMerge %18 None + OpBranchConditional %17 %19 %18 + %19 = OpLabel + %20 = OpUndef %bool + OpLoopMerge %21 %22 None + OpBranchConditional %20 %23 %21 + %23 = OpLabel + %24 = OpLoad %v4float %10 + %25 = OpCopyObject %v4float %24 + %26 = OpUndef %bool + OpBranch %22 + %22 = OpLabel + OpBranch %19 + %21 = OpLabel + OpBranch %12 + %18 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %11 + %12 = OpLabel + %27 = OpLoad %v4float %10 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(LocalSSAElimTest, Overflowtest1) { + // Check that the copy object get the undef value implicitly assigned in the + // entry block. + const std::string text = R"( +OpCapability Geometry +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "P2Mai" %12 %17 +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%11 = OpTypePointer Input %7 +%16 = OpTypePointer Output %7 +%23 = OpTypePointer Function %7 +%12 = OpVariable %11 Input +%17 = OpVariable %16 Output +%4 = OpFunction %2 None %3 +%2177 = OpLabel +%4194302 = OpVariable %23 Function +%4194301 = OpLoad %7 %4194302 +OpStore %17 %4194301 +OpReturn +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(LocalSSAElimTest, OpConstantNull) { + const std::string text = R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Int64 +OpMemoryModel Physical64 OpenCL +OpEntryPoint Kernel %4 "A" +OpSource OpenCL_C 200000 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%11 = OpTypePointer CrossWorkgroup %6 +%16 = OpConstantNull %11 +%20 = OpConstant %6 269484031 +%4 = OpFunction %2 None %3 +%17 = OpLabel +%18 = OpLoad %6 %16 Aligned 536870912 +%19 = OpBitwiseXor %6 %18 %20 +OpStore %16 %19 Aligned 536870912 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunToBinary(text, false); +} + +TEST_F(LocalSSAElimTest, DebugForLoop) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // for (int i=0; i<4; i++) { + // f = f + BC[i]; + // } + // fo = f; + // } + + const std::string text = R"( +; CHECK: [[f_name:%\w+]] = OpString "f" +; CHECK: [[i_name:%\w+]] = OpString "i" +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] + +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0 +; CHECK-NEXT: OpStore %i %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0 + +; CHECK-NOT: DebugDeclare + +; CHECK: [[loop_head:%\w+]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0 +; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]] +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None +; CHECK-NEXT: OpBranch [[loop_body:%\w+]] + +; CHECK-NEXT: [[loop_body]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]] + +; CHECK: [[bb]] = OpLabel +; CHECK: OpStore %f [[f_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]] +; CHECK-NEXT: OpBranch [[loop_cont]] + +; CHECK: [[loop_cont]] = OpLabel +; CHECK: OpStore %i [[i_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]] +; CHECK-NEXT: OpBranch [[loop_head]] + +; CHECK: [[loop_merge]] = OpLabel + +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +OpSource GLSL 140 +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal +%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal +%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr +OpBranch %23 +%23 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, AddDebugValueForFunctionParameterWithPhi) { + // Test the distribution of DebugValue for a parameter of an inlined function + // and the visibility of Phi instruction. The ssa-rewrite pass must add + // DebugValue for the value assignment of function argument even when it is an + // inlined function. It has to check the visibility Phi through all its value + // operands. See the DebugValue for "int i" of "foo()" in the following code. + // + // struct VS_OUTPUT { + // float4 pos : SV_POSITION; + // float4 color : COLOR; + // }; + // + // float4 foo(int i, float4 pos) { + // while (i < pos.x) { + // pos = pos.x + i; + // ++i; + // } + // return pos; + // } + // + // VS_OUTPUT main(float4 pos : POSITION, + // float4 color : COLOR) { + // VS_OUTPUT vout; + // vout.pos = foo(4, pos); + // vout.color = color; + // return vout; + // } + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_POSITION %in_var_COLOR %gl_Position %out_var_COLOR + %7 = OpString "vertex.hlsl" + %8 = OpString "float" + %9 = OpString "VS_OUTPUT" + %10 = OpString "color" + %11 = OpString "pos" + %12 = OpString "int" + %13 = OpString "foo" + %14 = OpString "" + %15 = OpString "i" + %16 = OpString "main" + %17 = OpString "vout" + OpName %in_var_POSITION "in.var.POSITION" + OpName %in_var_COLOR "in.var.COLOR" + OpName %out_var_COLOR "out.var.COLOR" + OpName %main "main" + OpName %param_var_pos "param.var.pos" + OpName %param_var_color "param.var.color" + OpName %VS_OUTPUT "VS_OUTPUT" + OpMemberName %VS_OUTPUT 0 "pos" + OpMemberName %VS_OUTPUT 1 "color" + OpDecorate %gl_Position BuiltIn Position + OpDecorate %in_var_POSITION Location 0 + OpDecorate %in_var_COLOR Location 1 + OpDecorate %out_var_COLOR Location 0 + %int = OpTypeInt 32 1 + %int_4 = OpConstant %int 4 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_256 = OpConstant %uint 256 + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %50 = OpTypeFunction %void +%_ptr_Function_v4float = OpTypePointer Function %v4float + %VS_OUTPUT = OpTypeStruct %v4float %v4float +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Function_float = OpTypePointer Function %float + %bool = OpTypeBool +%in_var_POSITION = OpVariable %_ptr_Input_v4float Input +%in_var_COLOR = OpVariable %_ptr_Input_v4float Input +%gl_Position = OpVariable %_ptr_Output_v4float Output +%out_var_COLOR = OpVariable %_ptr_Output_v4float Output + %156 = OpExtInst %void %1 DebugInfoNone + %77 = OpExtInst %void %1 DebugExpression + %58 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 Float + %59 = OpExtInst %void %1 DebugTypeVector %58 4 + %60 = OpExtInst %void %1 DebugSource %7 + %61 = OpExtInst %void %1 DebugCompilationUnit 1 4 %60 HLSL + %62 = OpExtInst %void %1 DebugTypeComposite %9 Structure %60 1 8 %61 %9 %uint_256 FlagIsProtected|FlagIsPrivate %63 %64 + %64 = OpExtInst %void %1 DebugTypeMember %10 %59 %60 3 10 %62 %uint_128 %uint_128 FlagIsProtected|FlagIsPrivate + %63 = OpExtInst %void %1 DebugTypeMember %11 %59 %60 2 10 %62 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %65 = OpExtInst %void %1 DebugTypeBasic %12 %uint_32 Signed + %66 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %59 %65 %59 + %67 = OpExtInst %void %1 DebugFunction %13 %66 %60 6 1 %61 %14 FlagIsProtected|FlagIsPrivate 6 %156 + %68 = OpExtInst %void %1 DebugLexicalBlock %60 6 31 %67 + %69 = OpExtInst %void %1 DebugLexicalBlock %60 7 21 %68 + %70 = OpExtInst %void %1 DebugLocalVariable %11 %59 %60 6 26 %67 FlagIsLocal 2 + +; CHECK: [[i_name:%\w+]] = OpString "i" +; CHECK: [[null_expr:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugExpression +; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] {{%\w+}} {{%\w+}} 6 16 {{%\w+}} FlagIsLocal 1 + %71 = OpExtInst %void %1 DebugLocalVariable %15 %65 %60 6 16 %67 FlagIsLocal 1 + %72 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %62 %59 %59 + %73 = OpExtInst %void %1 DebugFunction %16 %72 %60 14 1 %61 %14 FlagIsProtected|FlagIsPrivate 15 %156 + %74 = OpExtInst %void %1 DebugLexicalBlock %60 15 38 %73 + %75 = OpExtInst %void %1 DebugLocalVariable %17 %62 %60 16 13 %74 FlagIsLocal + %76 = OpExtInst %void %1 DebugLocalVariable %10 %59 %60 15 23 %73 FlagIsLocal 2 + %78 = OpExtInst %void %1 DebugLocalVariable %11 %59 %60 14 23 %73 FlagIsLocal 1 + %155 = OpExtInst %void %1 DebugInlinedAt 17 %74 + %main = OpFunction %void None %50 + %79 = OpLabel + %168 = OpExtInst %void %1 DebugScope %74 + +; CHECK: [[i:%\w+]] = OpVariable %_ptr_Function_int Function + %120 = OpVariable %_ptr_Function_int Function + %121 = OpVariable %_ptr_Function_v4float Function + %169 = OpExtInst %void %1 DebugNoScope +%param_var_pos = OpVariable %_ptr_Function_v4float Function +%param_var_color = OpVariable %_ptr_Function_v4float Function + %80 = OpLoad %v4float %in_var_POSITION + OpStore %param_var_pos %80 + %81 = OpLoad %v4float %in_var_COLOR + OpStore %param_var_color %81 + %170 = OpExtInst %void %1 DebugScope %73 + %124 = OpExtInst %void %1 DebugDeclare %78 %param_var_pos %77 + %125 = OpExtInst %void %1 DebugDeclare %76 %param_var_color %77 + %171 = OpExtInst %void %1 DebugScope %74 + OpLine %7 17 18 + +; CHECK: OpStore {{%\w+}} %int_4 +; CHECK: DebugValue [[dbg_i]] %int_4 [[null_expr]] + OpStore %120 %int_4 + OpStore %121 %80 + %172 = OpExtInst %void %1 DebugScope %67 %155 + %135 = OpExtInst %void %1 DebugDeclare %71 %120 %77 + %136 = OpExtInst %void %1 DebugDeclare %70 %121 %77 + %173 = OpExtInst %void %1 DebugScope %68 %155 + OpLine %7 7 3 + OpBranch %137 + %174 = OpExtInst %void %1 DebugNoScope + %137 = OpLabel + +; CHECK: [[phi:%\w+]] = OpPhi %int %int_4 +; CHECK: DebugValue [[dbg_i]] [[phi]] [[null_expr]] + %175 = OpExtInst %void %1 DebugScope %68 %155 + OpLine %7 7 10 + %138 = OpLoad %int %120 + %139 = OpConvertSToF %float %138 + OpLine %7 7 14 + %140 = OpAccessChain %_ptr_Function_float %121 %int_0 + %141 = OpLoad %float %140 + OpLine %7 7 12 + %142 = OpFOrdLessThan %bool %139 %141 + OpLine %7 7 3 + %176 = OpExtInst %void %1 DebugNoScope + OpLoopMerge %153 %152 None + OpBranchConditional %142 %143 %153 + %177 = OpExtInst %void %1 DebugNoScope + %143 = OpLabel + %178 = OpExtInst %void %1 DebugScope %69 %155 + OpLine %7 8 11 + %144 = OpAccessChain %_ptr_Function_float %121 %int_0 + %145 = OpLoad %float %144 + OpLine %7 8 19 + %146 = OpLoad %int %120 + %147 = OpConvertSToF %float %146 + OpLine %7 8 17 + %148 = OpFAdd %float %145 %147 + OpLine %7 8 11 + %149 = OpCompositeConstruct %v4float %148 %148 %148 %148 + OpLine %7 8 5 + OpStore %121 %149 + OpLine %7 9 5 + %151 = OpIAdd %int %146 %int_1 + OpLine %7 9 7 + +; CHECK: OpStore [[i]] [[value:%\w+]] +; CHECK: DebugValue [[dbg_i]] [[value]] [[null_expr]] + OpStore %120 %151 + %179 = OpExtInst %void %1 DebugScope %68 %155 + OpLine %7 10 3 + OpBranch %152 + %180 = OpExtInst %void %1 DebugNoScope + %152 = OpLabel + %181 = OpExtInst %void %1 DebugScope %68 %155 + OpBranch %137 + %182 = OpExtInst %void %1 DebugNoScope + %153 = OpLabel + %183 = OpExtInst %void %1 DebugScope %68 %155 + OpLine %7 11 10 + %154 = OpLoad %v4float %121 + %184 = OpExtInst %void %1 DebugScope %74 + %167 = OpExtInst %void %1 DebugValue %75 %154 %77 %int_0 + %166 = OpExtInst %void %1 DebugValue %75 %81 %77 %int_1 + OpLine %7 19 10 + %165 = OpCompositeConstruct %VS_OUTPUT %154 %81 + %185 = OpExtInst %void %1 DebugNoScope + %83 = OpCompositeExtract %v4float %165 0 + OpStore %gl_Position %83 + %84 = OpCompositeExtract %v4float %165 1 + OpStore %out_var_COLOR %84 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, PartiallyKillDebugDeclare) { + // For a reference variable e.g., int i in the following example, + // we do not propagate DebugValue for a store or phi instruction + // out of the variable's scope. In that case, we should not remove + // DebugDeclare for the variable that we did not add its DebugValue. + // + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // int j; + // void main() + // { + // float f = 0.0; + // for (j=0; j<4; j++) { + // int& i = j; + // f = f + BC[i]; + // } + // fo = f; + // } + + const std::string text = R"( +; CHECK: [[f_name:%\w+]] = OpString "f" +; CHECK: [[i_name:%\w+]] = OpString "i" +; CHECK: [[fn:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugFunction +; CHECK: [[bb:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[f_name]] {{%\w+}} {{%\w+}} 0 0 [[fn]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] {{%\w+}} {{%\w+}} 0 0 [[bb]] + +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0 +; CHECK-NOT: DebugDeclare [[dbg_f]] +; CHECK: OpExtInst %void [[ext]] DebugDeclare [[dbg_i]] %j + +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +OpSource GLSL 140 +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +%j_name = OpString "j" +OpName %main "main" +OpName %f "f" +OpName %j "j" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Private_int = OpTypePointer Private %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%j = OpVariable %_ptr_Private_int Private +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%bb = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal +%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 0 0 %bb FlagIsLocal +%dbg_j = OpExtInst %void %ext DebugGlobalVariable %j_name %dbg_v4f %src 0 0 %dbg_main %j_name %j FlagIsPrivate +%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +OpStore %f %float_0 +OpStore %j %int_0 +%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr +OpBranch %23 +%23 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%27 = OpLoad %int %j +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %bb +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %j %null_expr +%30 = OpLoad %float %f +%31 = OpLoad %int %j +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %int %j +%36 = OpIAdd %int %35 %int_1 +OpStore %j %36 +OpBranch %23 +%24 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugValueForReferenceVariable) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // float& x = f; + // for (int i=0; i<4; i++) { + // x = x + BC[i]; + // } + // fo = f; + // } + + const std::string text = R"( +; CHECK: [[f_name:%\w+]] = OpString "f" +; CHECK: [[i_name:%\w+]] = OpString "i" +; CHECK: [[x_name:%\w+]] = OpString "x" +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] +; CHECK: [[dbg_x:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[x_name]] + +; CHECK: OpStore %f %float_0 +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0 +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] %float_0 +; CHECK: OpStore %i %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0 + +; CHECK-NOT: DebugDeclare + +; CHECK: [[loop_head:%\w+]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0 +; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0 +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[phi0]] +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]] +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None +; CHECK-NEXT: OpBranch [[loop_body:%\w+]] + +; CHECK: [[loop_body]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]] + +; CHECK: [[bb]] = OpLabel +; CHECK: OpStore %f [[f_val:%\w+]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[f_val]] +; CHECK: OpBranch [[loop_cont]] + +; CHECK: [[loop_cont]] = OpLabel +; CHECK: OpStore %i [[i_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]] +; CHECK-NEXT: OpBranch [[loop_head]] + +; CHECK: [[loop_merge]] = OpLabel + +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +OpSource GLSL 140 +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +%x_name = OpString "x" +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal +%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal +%dbg_x = OpExtInst %void %ext DebugLocalVariable %x_name %dbg_v4f %src 2 0 %dbg_main FlagIsLocal +%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr +%decl2 = OpExtInst %void %ext DebugDeclare %dbg_x %f %null_expr +OpBranch %23 +%23 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugValueForReferenceVariableInBB) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // for (int i=0; i<4; i++) { + // float& x = f; + // x = x + BC[i]; + // { + // x = x + BC[i]; + // } + // } + // fo = f; + // } + + const std::string text = R"( +; CHECK: [[f_name:%\w+]] = OpString "f" +; CHECK: [[i_name:%\w+]] = OpString "i" +; CHECK: [[x_name:%\w+]] = OpString "x" +; CHECK: [[dbg_main:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugFunction +; CHECK: [[dbg_bb:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock +; CHECK: [[dbg_bb_child:%\w+]] = OpExtInst %void [[ext]] DebugLexicalBlock +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[f_name]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] +; CHECK: [[dbg_x:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[x_name]] + +; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %float_0 +; CHECK-NEXT: OpStore %i %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %int_0 + +; CHECK-NOT: DebugDeclare + +; CHECK: [[loop_head:%\w+]] = OpLabel +; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_main]] +; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0 +; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0 +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[phi0]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[phi0]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[phi1]] +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None +; CHECK-NEXT: OpBranch [[loop_body:%\w+]] + +; CHECK-NEXT: [[loop_body]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]] + +; CHECK: [[bb]] = OpLabel +; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_bb]] +; CHECK: OpStore %f [[f_val:%\w+]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_val]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[f_val]] +; CHECK: OpBranch [[bb_child:%\w+]] + +; CHECK: [[bb_child]] = OpLabel +; CHECK: OpExtInst %void [[ext]] DebugScope [[dbg_bb_child]] +; CHECK: OpStore %f [[new_f_val:%\w+]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[new_f_val]] +; CHECK-DAG: OpExtInst %void [[ext]] DebugValue [[dbg_x]] [[new_f_val]] +; CHECK: OpBranch [[loop_cont]] + +; CHECK: [[loop_cont]] = OpLabel +; CHECK: OpStore %i [[i_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] [[i_val]] +; CHECK-NEXT: OpBranch [[loop_head]] + +; CHECK: [[loop_merge]] = OpLabel + +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +OpSource GLSL 140 +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +%x_name = OpString "x" +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%bb = OpExtInst %void %ext DebugLexicalBlock %src 0 0 %dbg_main +%bb_child = OpExtInst %void %ext DebugLexicalBlock %src 1 0 %bb +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal +%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal +%dbg_x = OpExtInst %void %ext DebugLocalVariable %x_name %dbg_v4f %src 2 0 %bb FlagIsLocal +%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +%decl0 = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr +%decl1 = OpExtInst %void %ext DebugDeclare %dbg_i %i %null_expr +OpBranch %23 +%23 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%scope = OpExtInst %void %ext DebugScope %bb +%decl2 = OpExtInst %void %ext DebugDeclare %dbg_x %f %null_expr +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %38 +%38 = OpLabel +%child_scope = OpExtInst %void %ext DebugScope %bb_child +%39 = OpLoad %float %f +%40 = OpFAdd %float %39 %33 +OpStore %f %40 +OpBranch %25 +%25 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugForLoopUseDebugValueInsteadOfDebugDeclare) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // struct S { + // float f; + // int i; + // }; + // + // void main() + // { + // S foo = {0.0, 0}; + // for (; foo.i<4; foo.i++) { + // foo.f = foo.f + BC[foo.i]; + // } + // fo = foo.f; + // } + + const std::string text = R"( +; CHECK: [[f_name:%\w+]] = OpString "f" +; CHECK: [[empty_expr:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugExpression +; CHECK: [[deref_op:%\w+]] = OpExtInst %void [[ext]] DebugOperation Deref +; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref_op]] +; CHECK: [[dbg_foo:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[f_name]] + +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] %float_0 [[empty_expr]] %uint_0 +; CHECK-NEXT: OpStore %i %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] %int_0 [[empty_expr]] %uint_1 + +; CHECK: [[loop_head:%\w+]] = OpLabel +; CHECK: [[phi0:%\w+]] = OpPhi %float %float_0 +; CHECK: [[phi1:%\w+]] = OpPhi %int %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[phi0]] [[empty_expr]] %uint_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[phi1]] [[empty_expr]] %uint_1 +; CHECK: OpLoopMerge [[loop_merge:%\w+]] [[loop_cont:%\w+]] None +; CHECK-NEXT: OpBranch [[loop_body:%\w+]] + +; CHECK: [[loop_body]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[bb:%\w+]] [[loop_merge]] + +; CHECK: [[bb]] = OpLabel +; CHECK: OpStore %f [[f_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[f_val]] [[empty_expr]] %uint_0 +; CHECK-NEXT: OpBranch [[loop_cont]] + +; CHECK: [[loop_cont]] = OpLabel +; CHECK: OpStore %i [[i_val:%\w+]] +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_foo]] [[i_val]] [[empty_expr]] %uint_1 +; CHECK-NEXT: OpBranch [[loop_head]] + +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +OpSource GLSL 140 +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%deref_op = OpExtInst %void %ext DebugOperation Deref +%deref = OpExtInst %void %ext DebugExpression %deref_op +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_foo = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal +%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +%decl0 = OpExtInst %void %ext DebugValue %dbg_foo %f %deref %uint_0 +%decl1 = OpExtInst %void %ext DebugValue %dbg_foo %i %deref %uint_1 +OpBranch %23 +%23 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugValueNotUsedForDebugDeclare) { + // #version 140 + // + // in vec4 BC; + // out float fo; + // + // void main() + // { + // float f = 0.0; + // for (int i=0; i<4; i++) { + // f = f + BC[i]; + // } + // fo = f; + // } + + const std::string text = R"( +; CHECK: [[f_name:%\w+]] = OpString "f" +; CHECK: [[i_name:%\w+]] = OpString "i" +; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext:%\d+]] DebugLocalVariable [[f_name]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable [[i_name]] + +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: OpStore %i %int_0 +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_f]] %f +; CHECK-NEXT: OpExtInst %void [[ext]] DebugValue [[dbg_i]] %i + +; CHECK-NOT: DebugValue +; CHECK-NOT: DebugDeclare + +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +OpSource GLSL 140 +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_tf 4 +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal +%dbg_i = OpExtInst %void %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %dbg_main FlagIsLocal +%main = OpFunction %void None %8 +%22 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %f %float_0 +OpStore %i %int_0 +%decl0 = OpExtInst %void %ext DebugValue %dbg_f %f %null_expr +%decl1 = OpExtInst %void %ext DebugValue %dbg_i %i %null_expr +OpBranch %23 +%23 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %24 %25 None +OpBranch %26 +%26 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%27 = OpLoad %int %i +%28 = OpSLessThan %bool %27 %int_4 +OpBranchConditional %28 %29 %24 +%29 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%30 = OpLoad %float %f +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +%34 = OpFAdd %float %30 %33 +OpStore %f %34 +OpBranch %25 +%25 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %int %i +%36 = OpIAdd %int %35 %int_1 +OpStore %i %36 +OpBranch %23 +%24 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %float %f +OpStore %fo %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugNestedForLoop) { + const std::string text = R"( +; CHECK: = OpFunction +; CHECK-NEXT: [[entry:%\w+]] = OpLabel +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_f:%\w+]] %float_0 + +; CHECK: [[outer_header:%\w+]] = OpLabel +; CHECK: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[outer_be:%\w+]] +; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[outer_f]] + +; CHECK: [[inner_pre_header:%\w+]] = OpLabel +; CHECK: [[inner_header:%\w+]] = OpLabel +; CHECK: [[inner_f]] = OpPhi %float [[outer_f]] [[inner_pre_header]] [[f_next:%\w+]] [[inner_be:%\w+]] +; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[inner_f]] + +; CHECK: [[inner_be]] = OpLabel +; CHECK: [[f_next]] = OpFAdd %float [[inner_f]] +; CHECK-NEXT: OpStore %f [[f_next]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_next]] + +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +OpSource GLSL 450 +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %j "j" +OpName %BC "BC" +OpName %fo "fo" +OpDecorate %BC Location 0 +OpDecorate %fo Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%mat4v4float = OpTypeMatrix %v4float 4 +%_ptr_Input_mat4v4float = OpTypePointer Input %mat4v4float +%BC = OpVariable %_ptr_Input_mat4v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output + +; Debug information +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal + +%main = OpFunction %void None %9 +%24 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%j = OpVariable %_ptr_Function_int Function + +; DebugDeclare +OpStore %f %float_0 +%decl = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr + +OpStore %i %int_0 +OpBranch %25 +%25 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%26 = OpLoad %int %i +%27 = OpSLessThan %bool %26 %int_4 +OpLoopMerge %28 %29 None +OpBranchConditional %27 %30 %28 +%30 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +OpStore %j %int_0 +OpBranch %31 +%31 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%32 = OpLoad %int %j +%33 = OpSLessThan %bool %32 %int_4 +OpLoopMerge %50 %34 None +OpBranchConditional %33 %34 %50 +%34 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %float %f +%36 = OpLoad %int %i +%37 = OpLoad %int %j +%38 = OpAccessChain %_ptr_Input_float %BC %36 %37 +%39 = OpLoad %float %38 +%40 = OpFAdd %float %35 %39 +OpStore %f %40 +%41 = OpLoad %int %j +%42 = OpIAdd %int %41 %int_1 +OpStore %j %42 +OpBranch %31 +%50 = OpLabel +%s6 = OpExtInst %void %ext DebugScope %dbg_main +OpBranch %29 +%29 = OpLabel +%s7 = OpExtInst %void %ext DebugScope %dbg_main +%43 = OpLoad %int %i +%44 = OpIAdd %int %43 %int_1 +OpStore %i %44 +OpBranch %25 +%28 = OpLabel +%s8 = OpExtInst %void %ext DebugScope %dbg_main +%45 = OpLoad %float %f +OpStore %fo %45 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugForLoopWithContinue) { + const std::string text = R"( +; CHECK: = OpFunction +; CHECK-NEXT: [[entry:%\w+]] = OpLabel +; CHECK: OpStore %f %float_0 +; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_f:%\w+]] %float_0 + +; CHECK: [[outer_header:%\w+]] = OpLabel +; CHECK: [[outer_f:%\w+]] = OpPhi %float %float_0 [[entry]] [[inner_f:%\w+]] [[cont:%\w+]] +; CHECK: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[outer_f]] + +; CHECK: [[f_next:%\w+]] = OpFAdd %float [[outer_f]] +; CHECK-NEXT: OpStore %f [[f_next]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[f_next]] + +; CHECK: [[cont]] = OpLabel +; CHECK: [[inner_f]] = OpPhi %float [[outer_f]] {{%\d+}} [[f_next]] {{%\d+}} +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[inner_f]] + +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BC %fo +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +OpSource GLSL 140 +OpName %main "main" +OpName %f "f" +OpName %i "i" +OpName %t "t" +OpName %BC "BC" +OpName %fo "fo" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BC = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output + +; Debug information +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal + +%main = OpFunction %void None %9 +%23 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%f = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%t = OpVariable %_ptr_Function_float Function + +; DebugDeclare +OpStore %f %float_0 +%decl = OpExtInst %void %ext DebugDeclare %dbg_f %f %null_expr + +OpStore %i %int_0 +OpBranch %24 +%24 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpLoopMerge %25 %26 None +OpBranch %27 +%27 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%28 = OpLoad %int %i +%29 = OpSLessThan %bool %28 %int_4 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Input_float %BC %31 +%33 = OpLoad %float %32 +OpStore %t %33 +%34 = OpLoad %float %t +%35 = OpFOrdLessThan %bool %34 %float_0 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %36 +%37 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +OpBranch %26 +%36 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%38 = OpLoad %float %f +%39 = OpLoad %float %t +%40 = OpFAdd %float %38 %39 +OpStore %f %40 +OpBranch %26 +%26 = OpLabel +%s6 = OpExtInst %void %ext DebugScope %dbg_main +%41 = OpLoad %int %i +%42 = OpIAdd %int %41 %int_1 +OpStore %i %42 +OpBranch %24 +%25 = OpLabel +%s7 = OpExtInst %void %ext DebugScope %dbg_main +%43 = OpLoad %float %f +OpStore %fo %43 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugIfElse) { + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %f %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%v_name = OpString "v" +OpSource GLSL 140 +OpName %main "main" +OpName %f "f" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float +%f = OpVariable %_ptr_Input_float Input +%float_0 = OpConstant %float 0 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%float_0_5 = OpConstant %float 0.5 +%float_1 = OpConstant %float 1 +%18 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output + +; Debug information +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_v = OpExtInst %void %ext DebugLocalVariable %v_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal + +%main = OpFunction %void None %8 +%20 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main + +; DebugDeclare +%v = OpVariable %_ptr_Function_v4float Function +%decl = OpExtInst %void %ext DebugDeclare %dbg_v %v %null_expr + +%21 = OpLoad %float %f +%22 = OpFOrdGreaterThanEqual %bool %21 %float_0 +OpSelectionMerge %23 None +OpBranchConditional %22 %24 %25 + +; CHECK: OpBranchConditional +; CHECK-NEXT: [[br0:%\w+]] = OpLabel +; CHECK: OpStore %v [[v0:%\w+]] +; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_v:%\w+]] [[v0]] +%24 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +%26 = OpLoad %v4float %BaseColor +%27 = OpVectorTimesScalar %v4float %26 %float_0_5 +OpStore %v %27 +OpBranch %23 + +; CHECK: [[br1:%\w+]] = OpLabel +; CHECK: OpStore %v [[v1:%\w+]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v1]] +%25 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%28 = OpLoad %v4float %BaseColor +%29 = OpFAdd %v4float %28 %18 +OpStore %v %29 +OpBranch %23 + +; CHECK: [[phi:%\w+]] = OpPhi %v4float [[v0]] [[br0]] [[v1]] [[br1]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi]] +%23 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%30 = OpLoad %v4float %v +OpStore %gl_FragColor %30 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugSwitch) { + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %f %gl_FragColor +OpExecutionMode %main OriginUpperLeft +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%v_name = OpString "v" +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %i "i" +OpName %f "f" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_float = OpTypePointer Input %float +%f = OpVariable %_ptr_Input_float Input +%float_0_25 = OpConstant %float 0.25 +%float_0_75 = OpConstant %float 0.75 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output + +; Debug information +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %void +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_v = OpExtInst %void %ext DebugLocalVariable %v_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal + +%main = OpFunction %void None %9 +%20 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%v = OpVariable %_ptr_Function_v4float Function +%i = OpVariable %_ptr_Function_int Function +%21 = OpLoad %v4float %BaseColor + +; DebugDeclare +OpStore %v %21 +%decl = OpExtInst %void %ext DebugDeclare %dbg_v %v %null_expr + +; CHECK: %main = OpFunction %void None +; CHECK-NEXT: [[entry:%\w+]] = OpLabel +; CHECK: OpStore %v [[v0:%\w+]] +; CHECK-NEXT: = OpExtInst %void [[ext:%\w+]] DebugValue [[dbg_v:%\w+]] [[v0]] +; CHECK: OpSwitch {{%\w+}} [[case0:%\w+]] 0 [[case1:%\w+]] 1 [[case2:%\w+]] 2 [[case3:%\w+]] +; CHECK: OpStore %v [[v1:%\w+]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v1]] +; CHECK: OpStore %v [[v2:%\w+]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v2]] +; CHECK: [[phi0:%\w+]] = OpPhi %v4float [[v0]] [[entry]] [[v2]] [[case2]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi0]] +; CHECK: OpStore %v [[v3:%\w+]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[v3]] +; CHECK: [[phi1:%\w+]] = OpPhi %v4float [[v0]] [[case0]] [[v1]] [[case1]] [[v3]] [[case3]] +; CHECK-NEXT: = OpExtInst %void [[ext]] DebugValue [[dbg_v]] [[phi1]] + +%22 = OpLoad %float %f +%23 = OpConvertFToS %int %22 +OpStore %i %23 +%24 = OpLoad %int %i +OpSelectionMerge %25 None +OpSwitch %24 %26 0 %27 1 %28 2 %29 +%26 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +OpBranch %25 +%27 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%30 = OpLoad %v4float %v +%31 = OpVectorTimesScalar %v4float %30 %float_0_25 +OpStore %v %31 +OpBranch %25 +%28 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%32 = OpLoad %v4float %v +%33 = OpCompositeConstruct %v4float %float_0_25 %float_0_25 %float_0_25 %float_0_25 +%34 = OpFAdd %v4float %32 %33 +OpStore %v %34 +OpBranch %29 +%29 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%35 = OpLoad %v4float %v +%36 = OpVectorTimesScalar %v4float %35 %float_0_75 +OpStore %v %36 +OpBranch %25 +%25 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpLoad %v4float %v +OpStore %gl_FragColor %37 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(LocalSSAElimTest, DebugSwapProblem) { + // #version 140 + // + // in float fe; + // out float fo; + // + // void main() + // { + // float f1 = 0.0; + // float f2 = 1.0; + // int ie = int(fe); + // for (int i=0; i(text, true); +} + +TEST_F(LocalSSAElimTest, RemoveDebugDeclareWithoutLoads) { + // Check that the DebugDeclare for c is removed even though its loads + // had been removed previously by single block store/load optimization. + // In the presence of DebugDeclare, single-block can and does remove loads, + // but cannot change the stores into DebugValues and remove the DebugDeclare + // because it is only a per block optimization, not a function optimization. + // So SSA-rewrite must perform this role. + // + // Texture2D g_tColor; + // SamplerState g_sAniso; + // + // struct PS_INPUT + // { + // float2 vTextureCoords2 : TEXCOORD2; + // float2 vTextureCoords3 : TEXCOORD3; + // }; + // + // struct PS_OUTPUT + // { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) + // { + // PS_OUTPUT ps_output; + // float4 c; + // c = g_tColor.Sample(g_sAniso, i.vTextureCoords2.xy); + // c += g_tColor.Sample(g_sAniso, i.vTextureCoords3.xy); + // ps_output.vColor = c; + // return ps_output; + // } + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %in_var_TEXCOORD3 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %22 = OpString "foo.frag" + %26 = OpString "PS_OUTPUT" + %30 = OpString "float" + %33 = OpString "vColor" + %35 = OpString "PS_INPUT" + %40 = OpString "vTextureCoords3" + %42 = OpString "vTextureCoords2" + %44 = OpString "@type.2d.image" + %45 = OpString "type.2d.image" + %47 = OpString "Texture2D.TemplateParam" + %51 = OpString "src.MainPs" + %55 = OpString "c" + %57 = OpString "ps_output" + %60 = OpString "i" + %62 = OpString "@type.sampler" + %63 = OpString "type.sampler" + %65 = OpString "g_sAniso" + %67 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %in_var_TEXCOORD3 "in.var.TEXCOORD3" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords2" + OpMemberName %PS_INPUT 1 "vTextureCoords3" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %in_var_TEXCOORD3 Location 1 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_64 = OpConstant %uint 64 + %69 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%in_var_TEXCOORD3 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %43 = OpExtInst %void %1 DebugInfoNone + %59 = OpExtInst %void %1 DebugExpression + %24 = OpExtInst %void %1 DebugSource %22 + %25 = OpExtInst %void %1 DebugCompilationUnit 1 4 %24 HLSL + %28 = OpExtInst %void %1 DebugTypeComposite %26 Structure %24 11 1 %25 %26 %uint_128 FlagIsProtected|FlagIsPrivate %29 + %31 = OpExtInst %void %1 DebugTypeBasic %30 %uint_32 Float + %32 = OpExtInst %void %1 DebugTypeVector %31 4 + %29 = OpExtInst %void %1 DebugTypeMember %33 %32 %24 13 5 %28 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %36 = OpExtInst %void %1 DebugTypeComposite %35 Structure %24 5 1 %25 %35 %uint_128 FlagIsProtected|FlagIsPrivate %37 %38 + %39 = OpExtInst %void %1 DebugTypeVector %31 2 + %38 = OpExtInst %void %1 DebugTypeMember %40 %39 %24 8 5 %36 %uint_64 %uint_64 FlagIsProtected|FlagIsPrivate + %37 = OpExtInst %void %1 DebugTypeMember %42 %39 %24 7 5 %36 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate + %46 = OpExtInst %void %1 DebugTypeComposite %44 Class %24 0 0 %25 %45 %43 FlagIsProtected|FlagIsPrivate + %50 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %28 %36 + %52 = OpExtInst %void %1 DebugFunction %51 %50 %24 16 1 %25 %51 FlagIsProtected|FlagIsPrivate 17 %43 + %54 = OpExtInst %void %1 DebugLexicalBlock %24 17 1 %52 + %56 = OpExtInst %void %1 DebugLocalVariable %55 %32 %24 20 12 %54 FlagIsLocal + %58 = OpExtInst %void %1 DebugLocalVariable %57 %28 %24 18 15 %54 FlagIsLocal + %61 = OpExtInst %void %1 DebugLocalVariable %60 %36 %24 16 29 %52 FlagIsLocal 1 + %64 = OpExtInst %void %1 DebugTypeComposite %62 Structure %24 0 0 %25 %63 %43 FlagIsProtected|FlagIsPrivate + %66 = OpExtInst %void %1 DebugGlobalVariable %65 %64 %24 3 14 %25 %65 %g_sAniso FlagIsDefinition + %68 = OpExtInst %void %1 DebugGlobalVariable %67 %46 %24 1 11 %25 %67 %g_tColor FlagIsDefinition + %MainPs = OpFunction %void None %69 + %70 = OpLabel + %135 = OpExtInst %void %1 DebugScope %54 + %111 = OpVariable %_ptr_Function_PS_OUTPUT Function + %112 = OpVariable %_ptr_Function_v4float Function + %136 = OpExtInst %void %1 DebugNoScope +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %74 = OpLoad %v2float %in_var_TEXCOORD2 + %75 = OpLoad %v2float %in_var_TEXCOORD3 + %76 = OpCompositeConstruct %PS_INPUT %74 %75 + OpStore %param_var_i %76 + %137 = OpExtInst %void %1 DebugScope %52 + %115 = OpExtInst %void %1 DebugDeclare %61 %param_var_i %59 + %138 = OpExtInst %void %1 DebugScope %54 + %116 = OpExtInst %void %1 DebugDeclare %58 %111 %59 + %117 = OpExtInst %void %1 DebugDeclare %56 %112 %59 +;CHECK-NOT: %117 = OpExtInst %void %1 DebugDeclare %56 %112 %59 + OpLine %22 21 9 + %118 = OpLoad %type_2d_image %g_tColor + OpLine %22 21 29 + %119 = OpLoad %type_sampler %g_sAniso + OpLine %22 21 40 + %120 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %121 = OpLoad %v2float %120 + OpLine %22 21 9 + %122 = OpSampledImage %type_sampled_image %118 %119 + %123 = OpImageSampleImplicitLod %v4float %122 %121 None + OpLine %22 21 5 + OpStore %112 %123 +;CHECK: %140 = OpExtInst %void %1 DebugValue %56 %123 %59 + OpLine %22 22 10 + %124 = OpLoad %type_2d_image %g_tColor + OpLine %22 22 30 + %125 = OpLoad %type_sampler %g_sAniso + OpLine %22 22 41 + %126 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_1 + %127 = OpLoad %v2float %126 + OpLine %22 22 10 + %128 = OpSampledImage %type_sampled_image %124 %125 + %129 = OpImageSampleImplicitLod %v4float %128 %127 None + OpLine %22 22 7 + %131 = OpFAdd %v4float %123 %129 + OpLine %22 22 5 + OpStore %112 %131 +;CHECK: %141 = OpExtInst %void %1 DebugValue %56 %131 %59 + OpLine %22 23 5 + %133 = OpAccessChain %_ptr_Function_v4float %111 %int_0 + OpStore %133 %131 + OpLine %22 24 12 + %134 = OpLoad %PS_OUTPUT %111 + %139 = OpExtInst %void %1 DebugNoScope + %79 = OpCompositeExtract %v4float %134 0 + OpStore %out_var_SV_Target0 %79 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +// Check support for pointer variables. When pointer variables are used, the +// computation of reaching definitions may need to follow pointer chains. +// See https://github.com/KhronosGroup/SPIRV-Tools/issues/3873 for details. +TEST_F(LocalSSAElimTest, PointerVariables) { + const std::string text = R"( + OpCapability Shader + OpCapability VariablePointers + OpExtension "SPV_KHR_variable_pointers" + OpMemoryModel Logical Simple + OpEntryPoint Fragment %1 "main" %2 %3 + OpExecutionMode %1 OriginUpperLeft + %float = OpTypeFloat 32 + %void = OpTypeVoid + %6 = OpTypeFunction %void +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Function__ptr_Input_float = OpTypePointer Function %_ptr_Input_float + %2 = OpVariable %_ptr_Input_float Input + %3 = OpVariable %_ptr_Output_float Output + %1 = OpFunction %void None %6 + %10 = OpLabel + %11 = OpVariable %_ptr_Function__ptr_Input_float Function + OpStore %11 %2 + +; CHECK-NOT: %12 = OpLoad %_ptr_Input_float %11 + %12 = OpLoad %_ptr_Input_float %11 + +; CHECK: %13 = OpLoad %float %2 + %13 = OpLoad %float %12 + + OpStore %3 %13 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// No optimization in the presence of +// access chains +// function calls +// OpCopyMemory? +// unsupported extensions +// Others? + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/CMakeLists.txt b/third_party/spirv-tools/test/opt/loop_optimizations/CMakeLists.txt new file mode 100644 index 0000000..e362078 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/CMakeLists.txt @@ -0,0 +1,41 @@ +# Copyright (c) 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +add_spvtools_unittest(TARGET opt_loops + SRCS ../function_utils.h + dependence_analysis.cpp + dependence_analysis_helpers.cpp + fusion_compatibility.cpp + fusion_illegal.cpp + fusion_legal.cpp + fusion_pass.cpp + hoist_all_loop_types.cpp + hoist_double_nested_loops.cpp + hoist_from_independent_loops.cpp + hoist_simple_case.cpp + hoist_single_nested_loops.cpp + hoist_without_preheader.cpp + lcssa.cpp + loop_descriptions.cpp + loop_fission.cpp + nested_loops.cpp + peeling.cpp + peeling_pass.cpp + unroll_assumptions.cpp + unroll_simple.cpp + unswitch.cpp + LIBS SPIRV-Tools-opt + PCH_FILE pch_test_opt_loop +) diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/dependence_analysis.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/dependence_analysis.cpp new file mode 100644 index 0000000..8aeb20a --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/dependence_analysis.cpp @@ -0,0 +1,4205 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/iterator.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" +#include "source/opt/tree_iterator.h" +#include "test/opt//assembly_builder.h" +#include "test/opt//function_utils.h" +#include "test/opt//pass_fixture.h" +#include "test/opt//pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using DependencyAnalysis = ::testing::Test; + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void main(){ + int[10] arr; + int[10] arr2; + int a = 2; + for (int i = 0; i < 10; i++) { + arr[a] = arr[3]; + arr[a*2] = arr[a+3]; + arr[6] = arr2[6]; + arr[a+5] = arr2[7]; + } +} +*/ +TEST(DependencyAnalysis, ZIV) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %25 "arr" + OpName %39 "arr2" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 10 + %23 = OpTypeArray %6 %22 + %24 = OpTypePointer Function %23 + %27 = OpConstant %6 3 + %38 = OpConstant %6 6 + %44 = OpConstant %6 5 + %46 = OpConstant %6 7 + %51 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %24 Function + %39 = OpVariable %24 Function + OpBranch %12 + %12 = OpLabel + %53 = OpPhi %6 %11 %5 %52 %15 + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %20 = OpSLessThan %19 %53 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %28 = OpAccessChain %7 %25 %27 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %7 %25 %9 + OpStore %30 %29 + %32 = OpIMul %6 %9 %9 + %34 = OpIAdd %6 %9 %27 + %35 = OpAccessChain %7 %25 %34 + %36 = OpLoad %6 %35 + %37 = OpAccessChain %7 %25 %32 + OpStore %37 %36 + %40 = OpAccessChain %7 %39 %38 + %41 = OpLoad %6 %40 + %42 = OpAccessChain %7 %25 %38 + OpStore %42 %41 + %45 = OpIAdd %6 %9 %44 + %47 = OpAccessChain %7 %39 %46 + %48 = OpLoad %6 %47 + %49 = OpAccessChain %7 %25 %45 + OpStore %49 %48 + OpBranch %15 + %15 = OpLabel + %52 = OpIAdd %6 %53 %51 + OpBranch %12 + %14 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 13)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // 29 -> 30 tests looking through constants. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(29), + store[0], &distance_vector)); + } + + // 36 -> 37 tests looking through additions. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(36), + store[1], &distance_vector)); + } + + // 41 -> 42 tests looking at same index across two different arrays. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(41), + store[2], &distance_vector)); + } + + // 48 -> 49 tests looking through additions for same index in two different + // arrays. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(48), + store[3], &distance_vector)); + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +layout(location = 0) in vec4 c; +void main(){ + int[10] arr; + int[10] arr2; + int[10] arr3; + int[10] arr4; + int[10] arr5; + int N = int(c.x); + for (int i = 0; i < N; i++) { + arr[2*N] = arr[N]; + arr2[2*N+1] = arr2[N]; + arr3[2*N] = arr3[N-1]; + arr4[N] = arr5[N]; + } +} +*/ +TEST(DependencyAnalysis, SymbolicZIV) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %12 "c" + OpName %33 "arr" + OpName %41 "arr2" + OpName %50 "arr3" + OpName %58 "arr4" + OpName %60 "arr5" + OpDecorate %12 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeFloat 32 + %10 = OpTypeVector %9 4 + %11 = OpTypePointer Input %10 + %12 = OpVariable %11 Input + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Input %9 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %30 = OpConstant %13 10 + %31 = OpTypeArray %6 %30 + %32 = OpTypePointer Function %31 + %34 = OpConstant %6 2 + %44 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %32 Function + %41 = OpVariable %32 Function + %50 = OpVariable %32 Function + %58 = OpVariable %32 Function + %60 = OpVariable %32 Function + %16 = OpAccessChain %15 %12 %14 + %17 = OpLoad %9 %16 + %18 = OpConvertFToS %6 %17 + OpBranch %21 + %21 = OpLabel + %67 = OpPhi %6 %20 %5 %66 %24 + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %29 = OpSLessThan %28 %67 %18 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + %36 = OpIMul %6 %34 %18 + %38 = OpAccessChain %7 %33 %18 + %39 = OpLoad %6 %38 + %40 = OpAccessChain %7 %33 %36 + OpStore %40 %39 + %43 = OpIMul %6 %34 %18 + %45 = OpIAdd %6 %43 %44 + %47 = OpAccessChain %7 %41 %18 + %48 = OpLoad %6 %47 + %49 = OpAccessChain %7 %41 %45 + OpStore %49 %48 + %52 = OpIMul %6 %34 %18 + %54 = OpISub %6 %18 %44 + %55 = OpAccessChain %7 %50 %54 + %56 = OpLoad %6 %55 + %57 = OpAccessChain %7 %50 %52 + OpStore %57 %56 + %62 = OpAccessChain %7 %60 %18 + %63 = OpLoad %6 %62 + %64 = OpAccessChain %7 %58 %18 + OpStore %64 %63 + OpBranch %24 + %24 = OpLabel + %66 = OpIAdd %6 %67 %44 + OpBranch %21 + %23 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 22)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // independent due to loop bounds (won't enter if N <= 0). + // 39 -> 40 tests looking through symbols and multiplicaiton. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(39), + store[0], &distance_vector)); + } + + // 48 -> 49 tests looking through symbols and multiplication + addition. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(48), + store[1], &distance_vector)); + } + + // 56 -> 57 tests looking through symbols and arithmetic on load and store. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(56), + store[2], &distance_vector)); + } + + // independent as different arrays + // 63 -> 64 tests looking through symbols and load/store from/to different + // arrays. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(63), + store[3], &distance_vector)); + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void a(){ + int[10] arr; + int[11] arr2; + int[20] arr3; + int[20] arr4; + int a = 2; + for (int i = 0; i < 10; i++) { + arr[i] = arr[i]; + arr2[i] = arr2[i+1]; + arr3[i] = arr3[i-1]; + arr4[2*i] = arr4[i]; + } +} +void b(){ + int[10] arr; + int[11] arr2; + int[20] arr3; + int[20] arr4; + int a = 2; + for (int i = 10; i > 0; i--) { + arr[i] = arr[i]; + arr2[i] = arr2[i+1]; + arr3[i] = arr3[i-1]; + arr4[2*i] = arr4[i]; + } +} + +void main() { + a(); + b(); +} +*/ +TEST(DependencyAnalysis, SIV) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %12 "a" + OpName %14 "i" + OpName %29 "arr" + OpName %38 "arr2" + OpName %49 "arr3" + OpName %56 "arr4" + OpName %65 "a" + OpName %66 "i" + OpName %74 "arr" + OpName %80 "arr2" + OpName %87 "arr3" + OpName %94 "arr4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 2 + %15 = OpConstant %10 0 + %22 = OpConstant %10 10 + %23 = OpTypeBool + %25 = OpTypeInt 32 0 + %26 = OpConstant %25 10 + %27 = OpTypeArray %10 %26 + %28 = OpTypePointer Function %27 + %35 = OpConstant %25 11 + %36 = OpTypeArray %10 %35 + %37 = OpTypePointer Function %36 + %41 = OpConstant %10 1 + %46 = OpConstant %25 20 + %47 = OpTypeArray %10 %46 + %48 = OpTypePointer Function %47 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %103 = OpFunctionCall %2 %6 + %104 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %14 = OpVariable %11 Function + %29 = OpVariable %28 Function + %38 = OpVariable %37 Function + %49 = OpVariable %48 Function + %56 = OpVariable %48 Function + OpStore %12 %13 + OpStore %14 %15 + OpBranch %16 + %16 = OpLabel + %105 = OpPhi %10 %15 %7 %64 %19 + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %24 = OpSLessThan %23 %105 %22 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + %32 = OpAccessChain %11 %29 %105 + %33 = OpLoad %10 %32 + %34 = OpAccessChain %11 %29 %105 + OpStore %34 %33 + %42 = OpIAdd %10 %105 %41 + %43 = OpAccessChain %11 %38 %42 + %44 = OpLoad %10 %43 + %45 = OpAccessChain %11 %38 %105 + OpStore %45 %44 + %52 = OpISub %10 %105 %41 + %53 = OpAccessChain %11 %49 %52 + %54 = OpLoad %10 %53 + %55 = OpAccessChain %11 %49 %105 + OpStore %55 %54 + %58 = OpIMul %10 %13 %105 + %60 = OpAccessChain %11 %56 %105 + %61 = OpLoad %10 %60 + %62 = OpAccessChain %11 %56 %58 + OpStore %62 %61 + OpBranch %19 + %19 = OpLabel + %64 = OpIAdd %10 %105 %41 + OpStore %14 %64 + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %65 = OpVariable %11 Function + %66 = OpVariable %11 Function + %74 = OpVariable %28 Function + %80 = OpVariable %37 Function + %87 = OpVariable %48 Function + %94 = OpVariable %48 Function + OpStore %65 %13 + OpStore %66 %22 + OpBranch %67 + %67 = OpLabel + %106 = OpPhi %10 %22 %9 %102 %70 + OpLoopMerge %69 %70 None + OpBranch %71 + %71 = OpLabel + %73 = OpSGreaterThan %23 %106 %15 + OpBranchConditional %73 %68 %69 + %68 = OpLabel + %77 = OpAccessChain %11 %74 %106 + %78 = OpLoad %10 %77 + %79 = OpAccessChain %11 %74 %106 + OpStore %79 %78 + %83 = OpIAdd %10 %106 %41 + %84 = OpAccessChain %11 %80 %83 + %85 = OpLoad %10 %84 + %86 = OpAccessChain %11 %80 %106 + OpStore %86 %85 + %90 = OpISub %10 %106 %41 + %91 = OpAccessChain %11 %87 %90 + %92 = OpLoad %10 %91 + %93 = OpAccessChain %11 %87 %106 + OpStore %93 %92 + %96 = OpIMul %10 %13 %106 + %98 = OpAccessChain %11 %94 %106 + %99 = OpLoad %10 %98 + %100 = OpAccessChain %11 %94 %96 + OpStore %100 %99 + OpBranch %70 + %70 = OpLabel + %102 = OpISub %10 %106 %41 + OpStore %66 %102 + OpBranch %67 + %69 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + // For the loop in function a. + { + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 17)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // = dependence + // 33 -> 34 tests looking at SIV in same array. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(33), store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::EQ); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); + } + + // > -1 dependence + // 44 -> 45 tests looking at SIV in same array with addition. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(44), store[1], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::GT); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, -1); + } + + // < 1 dependence + // 54 -> 55 tests looking at SIV in same array with subtraction. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(54), store[2], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::LT); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, 1); + } + + // <=> dependence + // 61 -> 62 tests looking at SIV in same array with multiplication. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(61), store[3], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::UNKNOWN); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::ALL); + } + } + // For the loop in function b. + { + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 68)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // = dependence + // 78 -> 79 tests looking at SIV in same array. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(78), store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::EQ); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); + } + + // < 1 dependence + // 85 -> 86 tests looking at SIV in same array with addition. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(85), store[1], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::LT); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, 1); + } + + // > -1 dependence + // 92 -> 93 tests looking at SIV in same array with subtraction. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(92), store[2], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::GT); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, -1); + } + + // <=> dependence + // 99 -> 100 tests looking at SIV in same array with multiplication. + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(99), store[3], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::UNKNOWN); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::ALL); + } + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +layout(location = 0) in vec4 c; +void a() { + int[13] arr; + int[15] arr2; + int[18] arr3; + int[18] arr4; + int N = int(c.x); + int C = 2; + int a = 2; + for (int i = 0; i < N; i++) { // Bounds are N - 1 + arr[i+2*N] = arr[i+N]; // |distance| = N + arr2[i+N] = arr2[i+2*N] + C; // |distance| = N + arr3[2*i+2*N+1] = arr3[2*i+N+1]; // |distance| = N + arr4[a*i+N+1] = arr4[a*i+2*N+1]; // |distance| = N + } +} +void b() { + int[13] arr; + int[15] arr2; + int[18] arr3; + int[18] arr4; + int N = int(c.x); + int C = 2; + int a = 2; + for (int i = N; i > 0; i--) { // Bounds are N - 1 + arr[i+2*N] = arr[i+N]; // |distance| = N + arr2[i+N] = arr2[i+2*N] + C; // |distance| = N + arr3[2*i+2*N+1] = arr3[2*i+N+1]; // |distance| = N + arr4[a*i+N+1] = arr4[a*i+2*N+1]; // |distance| = N + } +} +void main(){ + a(); + b(); +}*/ +TEST(DependencyAnalysis, SymbolicSIV) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %16 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %12 "N" + OpName %16 "c" + OpName %23 "C" + OpName %25 "a" + OpName %26 "i" + OpName %40 "arr" + OpName %54 "arr2" + OpName %70 "arr3" + OpName %86 "arr4" + OpName %105 "N" + OpName %109 "C" + OpName %110 "a" + OpName %111 "i" + OpName %120 "arr" + OpName %131 "arr2" + OpName %144 "arr3" + OpName %159 "arr4" + OpDecorate %16 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpTypeFloat 32 + %14 = OpTypeVector %13 4 + %15 = OpTypePointer Input %14 + %16 = OpVariable %15 Input + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %19 = OpTypePointer Input %13 + %24 = OpConstant %10 2 + %27 = OpConstant %10 0 + %35 = OpTypeBool + %37 = OpConstant %17 13 + %38 = OpTypeArray %10 %37 + %39 = OpTypePointer Function %38 + %51 = OpConstant %17 15 + %52 = OpTypeArray %10 %51 + %53 = OpTypePointer Function %52 + %67 = OpConstant %17 18 + %68 = OpTypeArray %10 %67 + %69 = OpTypePointer Function %68 + %76 = OpConstant %10 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %178 = OpFunctionCall %2 %6 + %179 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %23 = OpVariable %11 Function + %25 = OpVariable %11 Function + %26 = OpVariable %11 Function + %40 = OpVariable %39 Function + %54 = OpVariable %53 Function + %70 = OpVariable %69 Function + %86 = OpVariable %69 Function + %20 = OpAccessChain %19 %16 %18 + %21 = OpLoad %13 %20 + %22 = OpConvertFToS %10 %21 + OpStore %12 %22 + OpStore %23 %24 + OpStore %25 %24 + OpStore %26 %27 + OpBranch %28 + %28 = OpLabel + %180 = OpPhi %10 %27 %7 %104 %31 + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %36 = OpSLessThan %35 %180 %22 + OpBranchConditional %36 %29 %30 + %29 = OpLabel + %43 = OpIMul %10 %24 %22 + %44 = OpIAdd %10 %180 %43 + %47 = OpIAdd %10 %180 %22 + %48 = OpAccessChain %11 %40 %47 + %49 = OpLoad %10 %48 + %50 = OpAccessChain %11 %40 %44 + OpStore %50 %49 + %57 = OpIAdd %10 %180 %22 + %60 = OpIMul %10 %24 %22 + %61 = OpIAdd %10 %180 %60 + %62 = OpAccessChain %11 %54 %61 + %63 = OpLoad %10 %62 + %65 = OpIAdd %10 %63 %24 + %66 = OpAccessChain %11 %54 %57 + OpStore %66 %65 + %72 = OpIMul %10 %24 %180 + %74 = OpIMul %10 %24 %22 + %75 = OpIAdd %10 %72 %74 + %77 = OpIAdd %10 %75 %76 + %79 = OpIMul %10 %24 %180 + %81 = OpIAdd %10 %79 %22 + %82 = OpIAdd %10 %81 %76 + %83 = OpAccessChain %11 %70 %82 + %84 = OpLoad %10 %83 + %85 = OpAccessChain %11 %70 %77 + OpStore %85 %84 + %89 = OpIMul %10 %24 %180 + %91 = OpIAdd %10 %89 %22 + %92 = OpIAdd %10 %91 %76 + %95 = OpIMul %10 %24 %180 + %97 = OpIMul %10 %24 %22 + %98 = OpIAdd %10 %95 %97 + %99 = OpIAdd %10 %98 %76 + %100 = OpAccessChain %11 %86 %99 + %101 = OpLoad %10 %100 + %102 = OpAccessChain %11 %86 %92 + OpStore %102 %101 + OpBranch %31 + %31 = OpLabel + %104 = OpIAdd %10 %180 %76 + OpStore %26 %104 + OpBranch %28 + %30 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %105 = OpVariable %11 Function + %109 = OpVariable %11 Function + %110 = OpVariable %11 Function + %111 = OpVariable %11 Function + %120 = OpVariable %39 Function + %131 = OpVariable %53 Function + %144 = OpVariable %69 Function + %159 = OpVariable %69 Function + %106 = OpAccessChain %19 %16 %18 + %107 = OpLoad %13 %106 + %108 = OpConvertFToS %10 %107 + OpStore %105 %108 + OpStore %109 %24 + OpStore %110 %24 + OpStore %111 %108 + OpBranch %113 + %113 = OpLabel + %181 = OpPhi %10 %108 %9 %177 %116 + OpLoopMerge %115 %116 None + OpBranch %117 + %117 = OpLabel + %119 = OpSGreaterThan %35 %181 %27 + OpBranchConditional %119 %114 %115 + %114 = OpLabel + %123 = OpIMul %10 %24 %108 + %124 = OpIAdd %10 %181 %123 + %127 = OpIAdd %10 %181 %108 + %128 = OpAccessChain %11 %120 %127 + %129 = OpLoad %10 %128 + %130 = OpAccessChain %11 %120 %124 + OpStore %130 %129 + %134 = OpIAdd %10 %181 %108 + %137 = OpIMul %10 %24 %108 + %138 = OpIAdd %10 %181 %137 + %139 = OpAccessChain %11 %131 %138 + %140 = OpLoad %10 %139 + %142 = OpIAdd %10 %140 %24 + %143 = OpAccessChain %11 %131 %134 + OpStore %143 %142 + %146 = OpIMul %10 %24 %181 + %148 = OpIMul %10 %24 %108 + %149 = OpIAdd %10 %146 %148 + %150 = OpIAdd %10 %149 %76 + %152 = OpIMul %10 %24 %181 + %154 = OpIAdd %10 %152 %108 + %155 = OpIAdd %10 %154 %76 + %156 = OpAccessChain %11 %144 %155 + %157 = OpLoad %10 %156 + %158 = OpAccessChain %11 %144 %150 + OpStore %158 %157 + %162 = OpIMul %10 %24 %181 + %164 = OpIAdd %10 %162 %108 + %165 = OpIAdd %10 %164 %76 + %168 = OpIMul %10 %24 %181 + %170 = OpIMul %10 %24 %108 + %171 = OpIAdd %10 %168 %170 + %172 = OpIAdd %10 %171 %76 + %173 = OpAccessChain %11 %159 %172 + %174 = OpLoad %10 %173 + %175 = OpAccessChain %11 %159 %165 + OpStore %175 %174 + OpBranch %116 + %116 = OpLabel + %177 = OpISub %10 %181 %76 + OpStore %111 %177 + OpBranch %113 + %115 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + // For the loop in function a. + { + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // independent due to loop bounds (won't enter when N <= 0) + // 49 -> 50 tests looking through SIV and symbols with multiplication + { + DistanceVector distance_vector{loops.size()}; + // Independent but not yet supported. + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(49), store[0], &distance_vector)); + } + + // 63 -> 66 tests looking through SIV and symbols with multiplication and + + // C + { + DistanceVector distance_vector{loops.size()}; + // Independent. + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(63), + store[1], &distance_vector)); + } + + // 84 -> 85 tests looking through arithmetic on SIV and symbols + { + DistanceVector distance_vector{loops.size()}; + // Independent but not yet supported. + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(84), store[2], &distance_vector)); + } + + // 101 -> 102 tests looking through symbol arithmetic on SIV and symbols + { + DistanceVector distance_vector{loops.size()}; + // Independent. + EXPECT_TRUE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(101), store[3], &distance_vector)); + } + } + // For the loop in function b. + { + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 114)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // independent due to loop bounds (won't enter when N <= 0). + // 129 -> 130 tests looking through SIV and symbols with multiplication. + { + DistanceVector distance_vector{loops.size()}; + // Independent but not yet supported. + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(129), store[0], &distance_vector)); + } + + // 140 -> 143 tests looking through SIV and symbols with multiplication and + // + C. + { + DistanceVector distance_vector{loops.size()}; + // Independent. + EXPECT_TRUE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(140), store[1], &distance_vector)); + } + + // 157 -> 158 tests looking through arithmetic on SIV and symbols. + { + DistanceVector distance_vector{loops.size()}; + // Independent but not yet supported. + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(157), store[2], &distance_vector)); + } + + // 174 -> 175 tests looking through symbol arithmetic on SIV and symbols. + { + DistanceVector distance_vector{loops.size()}; + // Independent. + EXPECT_TRUE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(174), store[3], &distance_vector)); + } + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void a() { + int[6] arr; + int N = 5; + for (int i = 1; i < N; i++) { + arr[i] = arr[N-i]; + } +} +void b() { + int[6] arr; + int N = 5; + for (int i = 1; i < N; i++) { + arr[N-i] = arr[i]; + } +} +void c() { + int[11] arr; + int N = 10; + for (int i = 1; i < N; i++) { + arr[i] = arr[N-i+1]; + } +} +void d() { + int[11] arr; + int N = 10; + for (int i = 1; i < N; i++) { + arr[N-i+1] = arr[i]; + } +} +void e() { + int[6] arr; + int N = 5; + for (int i = N; i > 0; i--) { + arr[i] = arr[N-i]; + } +} +void f() { + int[6] arr; + int N = 5; + for (int i = N; i > 0; i--) { + arr[N-i] = arr[i]; + } +} +void g() { + int[11] arr; + int N = 10; + for (int i = N; i > 0; i--) { + arr[i] = arr[N-i+1]; + } +} +void h() { + int[11] arr; + int N = 10; + for (int i = N; i > 0; i--) { + arr[N-i+1] = arr[i]; + } +} +void main(){ + a(); + b(); + c(); + d(); + e(); + f(); + g(); + h(); +} +*/ +TEST(DependencyAnalysis, Crossing) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %10 "c(" + OpName %12 "d(" + OpName %14 "e(" + OpName %16 "f(" + OpName %18 "g(" + OpName %20 "h(" + OpName %24 "N" + OpName %26 "i" + OpName %41 "arr" + OpName %51 "N" + OpName %52 "i" + OpName %61 "arr" + OpName %71 "N" + OpName %73 "i" + OpName %85 "arr" + OpName %96 "N" + OpName %97 "i" + OpName %106 "arr" + OpName %117 "N" + OpName %118 "i" + OpName %128 "arr" + OpName %138 "N" + OpName %139 "i" + OpName %148 "arr" + OpName %158 "N" + OpName %159 "i" + OpName %168 "arr" + OpName %179 "N" + OpName %180 "i" + OpName %189 "arr" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %22 = OpTypeInt 32 1 + %23 = OpTypePointer Function %22 + %25 = OpConstant %22 5 + %27 = OpConstant %22 1 + %35 = OpTypeBool + %37 = OpTypeInt 32 0 + %38 = OpConstant %37 6 + %39 = OpTypeArray %22 %38 + %40 = OpTypePointer Function %39 + %72 = OpConstant %22 10 + %82 = OpConstant %37 11 + %83 = OpTypeArray %22 %82 + %84 = OpTypePointer Function %83 + %126 = OpConstant %22 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %200 = OpFunctionCall %2 %6 + %201 = OpFunctionCall %2 %8 + %202 = OpFunctionCall %2 %10 + %203 = OpFunctionCall %2 %12 + %204 = OpFunctionCall %2 %14 + %205 = OpFunctionCall %2 %16 + %206 = OpFunctionCall %2 %18 + %207 = OpFunctionCall %2 %20 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %24 = OpVariable %23 Function + %26 = OpVariable %23 Function + %41 = OpVariable %40 Function + OpStore %24 %25 + OpStore %26 %27 + OpBranch %28 + %28 = OpLabel + %208 = OpPhi %22 %27 %7 %50 %31 + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %36 = OpSLessThan %35 %208 %25 + OpBranchConditional %36 %29 %30 + %29 = OpLabel + %45 = OpISub %22 %25 %208 + %46 = OpAccessChain %23 %41 %45 + %47 = OpLoad %22 %46 + %48 = OpAccessChain %23 %41 %208 + OpStore %48 %47 + OpBranch %31 + %31 = OpLabel + %50 = OpIAdd %22 %208 %27 + OpStore %26 %50 + OpBranch %28 + %30 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %51 = OpVariable %23 Function + %52 = OpVariable %23 Function + %61 = OpVariable %40 Function + OpStore %51 %25 + OpStore %52 %27 + OpBranch %53 + %53 = OpLabel + %209 = OpPhi %22 %27 %9 %70 %56 + OpLoopMerge %55 %56 None + OpBranch %57 + %57 = OpLabel + %60 = OpSLessThan %35 %209 %25 + OpBranchConditional %60 %54 %55 + %54 = OpLabel + %64 = OpISub %22 %25 %209 + %66 = OpAccessChain %23 %61 %209 + %67 = OpLoad %22 %66 + %68 = OpAccessChain %23 %61 %64 + OpStore %68 %67 + OpBranch %56 + %56 = OpLabel + %70 = OpIAdd %22 %209 %27 + OpStore %52 %70 + OpBranch %53 + %55 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %71 = OpVariable %23 Function + %73 = OpVariable %23 Function + %85 = OpVariable %84 Function + OpStore %71 %72 + OpStore %73 %27 + OpBranch %74 + %74 = OpLabel + %210 = OpPhi %22 %27 %11 %95 %77 + OpLoopMerge %76 %77 None + OpBranch %78 + %78 = OpLabel + %81 = OpSLessThan %35 %210 %72 + OpBranchConditional %81 %75 %76 + %75 = OpLabel + %89 = OpISub %22 %72 %210 + %90 = OpIAdd %22 %89 %27 + %91 = OpAccessChain %23 %85 %90 + %92 = OpLoad %22 %91 + %93 = OpAccessChain %23 %85 %210 + OpStore %93 %92 + OpBranch %77 + %77 = OpLabel + %95 = OpIAdd %22 %210 %27 + OpStore %73 %95 + OpBranch %74 + %76 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %96 = OpVariable %23 Function + %97 = OpVariable %23 Function + %106 = OpVariable %84 Function + OpStore %96 %72 + OpStore %97 %27 + OpBranch %98 + %98 = OpLabel + %211 = OpPhi %22 %27 %13 %116 %101 + OpLoopMerge %100 %101 None + OpBranch %102 + %102 = OpLabel + %105 = OpSLessThan %35 %211 %72 + OpBranchConditional %105 %99 %100 + %99 = OpLabel + %109 = OpISub %22 %72 %211 + %110 = OpIAdd %22 %109 %27 + %112 = OpAccessChain %23 %106 %211 + %113 = OpLoad %22 %112 + %114 = OpAccessChain %23 %106 %110 + OpStore %114 %113 + OpBranch %101 + %101 = OpLabel + %116 = OpIAdd %22 %211 %27 + OpStore %97 %116 + OpBranch %98 + %100 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %117 = OpVariable %23 Function + %118 = OpVariable %23 Function + %128 = OpVariable %40 Function + OpStore %117 %25 + OpStore %118 %25 + OpBranch %120 + %120 = OpLabel + %212 = OpPhi %22 %25 %15 %137 %123 + OpLoopMerge %122 %123 None + OpBranch %124 + %124 = OpLabel + %127 = OpSGreaterThan %35 %212 %126 + OpBranchConditional %127 %121 %122 + %121 = OpLabel + %132 = OpISub %22 %25 %212 + %133 = OpAccessChain %23 %128 %132 + %134 = OpLoad %22 %133 + %135 = OpAccessChain %23 %128 %212 + OpStore %135 %134 + OpBranch %123 + %123 = OpLabel + %137 = OpISub %22 %212 %27 + OpStore %118 %137 + OpBranch %120 + %122 = OpLabel + OpReturn + OpFunctionEnd + %16 = OpFunction %2 None %3 + %17 = OpLabel + %138 = OpVariable %23 Function + %139 = OpVariable %23 Function + %148 = OpVariable %40 Function + OpStore %138 %25 + OpStore %139 %25 + OpBranch %141 + %141 = OpLabel + %213 = OpPhi %22 %25 %17 %157 %144 + OpLoopMerge %143 %144 None + OpBranch %145 + %145 = OpLabel + %147 = OpSGreaterThan %35 %213 %126 + OpBranchConditional %147 %142 %143 + %142 = OpLabel + %151 = OpISub %22 %25 %213 + %153 = OpAccessChain %23 %148 %213 + %154 = OpLoad %22 %153 + %155 = OpAccessChain %23 %148 %151 + OpStore %155 %154 + OpBranch %144 + %144 = OpLabel + %157 = OpISub %22 %213 %27 + OpStore %139 %157 + OpBranch %141 + %143 = OpLabel + OpReturn + OpFunctionEnd + %18 = OpFunction %2 None %3 + %19 = OpLabel + %158 = OpVariable %23 Function + %159 = OpVariable %23 Function + %168 = OpVariable %84 Function + OpStore %158 %72 + OpStore %159 %72 + OpBranch %161 + %161 = OpLabel + %214 = OpPhi %22 %72 %19 %178 %164 + OpLoopMerge %163 %164 None + OpBranch %165 + %165 = OpLabel + %167 = OpSGreaterThan %35 %214 %126 + OpBranchConditional %167 %162 %163 + %162 = OpLabel + %172 = OpISub %22 %72 %214 + %173 = OpIAdd %22 %172 %27 + %174 = OpAccessChain %23 %168 %173 + %175 = OpLoad %22 %174 + %176 = OpAccessChain %23 %168 %214 + OpStore %176 %175 + OpBranch %164 + %164 = OpLabel + %178 = OpISub %22 %214 %27 + OpStore %159 %178 + OpBranch %161 + %163 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %3 + %21 = OpLabel + %179 = OpVariable %23 Function + %180 = OpVariable %23 Function + %189 = OpVariable %84 Function + OpStore %179 %72 + OpStore %180 %72 + OpBranch %182 + %182 = OpLabel + %215 = OpPhi %22 %72 %21 %199 %185 + OpLoopMerge %184 %185 None + OpBranch %186 + %186 = OpLabel + %188 = OpSGreaterThan %35 %215 %126 + OpBranchConditional %188 %183 %184 + %183 = OpLabel + %192 = OpISub %22 %72 %215 + %193 = OpIAdd %22 %192 %27 + %195 = OpAccessChain %23 %189 %215 + %196 = OpLoad %22 %195 + %197 = OpAccessChain %23 %189 %193 + OpStore %197 %196 + OpBranch %185 + %185 = OpLabel + %199 = OpISub %22 %215 %27 + OpStore %180 %199 + OpBranch %182 + %184 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + // First two tests can be split into two loops. + // Tests even crossing subscripts from low to high indexes. + // 47 -> 48 + { + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + } + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(47), + store, &distance_vector)); + } + + // Tests even crossing subscripts from high to low indexes. + // 67 -> 68 + { + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 54)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + } + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(67), + store, &distance_vector)); + } + + // Next two tests can have an end peeled, then be split. + // Tests uneven crossing subscripts from low to high indexes. + // 92 -> 93 + { + const Function* f = spvtest::GetFunction(module, 10); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 75)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + } + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(92), + store, &distance_vector)); + } + + // Tests uneven crossing subscripts from high to low indexes. + // 113 -> 114 + { + const Function* f = spvtest::GetFunction(module, 12); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 99)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + } + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(113), + store, &distance_vector)); + } + + // First two tests can be split into two loops. + // Tests even crossing subscripts from low to high indexes. + // 134 -> 135 + { + const Function* f = spvtest::GetFunction(module, 14); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 121)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + } + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(134), + store, &distance_vector)); + } + + // Tests even crossing subscripts from high to low indexes. + // 154 -> 155 + { + const Function* f = spvtest::GetFunction(module, 16); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 142)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + } + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(154), + store, &distance_vector)); + } + + // Next two tests can have an end peeled, then be split. + // Tests uneven crossing subscripts from low to high indexes. + // 175 -> 176 + { + const Function* f = spvtest::GetFunction(module, 18); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 162)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + } + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(175), + store, &distance_vector)); + } + + // Tests uneven crossing subscripts from high to low indexes. + // 196 -> 197 + { + const Function* f = spvtest::GetFunction(module, 20); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 183)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + } + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(196), + store, &distance_vector)); + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void a() { + int[10] arr; + for (int i = 0; i < 10; i++) { + arr[0] = arr[i]; // peel first + arr[i] = arr[0]; // peel first + arr[9] = arr[i]; // peel last + arr[i] = arr[9]; // peel last + } +} +void b() { + int[11] arr; + for (int i = 0; i <= 10; i++) { + arr[0] = arr[i]; // peel first + arr[i] = arr[0]; // peel first + arr[10] = arr[i]; // peel last + arr[i] = arr[10]; // peel last + + } +} +void c() { + int[11] arr; + for (int i = 10; i > 0; i--) { + arr[10] = arr[i]; // peel first + arr[i] = arr[10]; // peel first + arr[1] = arr[i]; // peel last + arr[i] = arr[1]; // peel last + + } +} +void d() { + int[11] arr; + for (int i = 10; i >= 0; i--) { + arr[10] = arr[i]; // peel first + arr[i] = arr[10]; // peel first + arr[0] = arr[i]; // peel last + arr[i] = arr[0]; // peel last + + } +} +void main(){ + a(); + b(); + c(); + d(); +} +*/ +TEST(DependencyAnalysis, WeakZeroSIV) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %10 "c(" + OpName %12 "d(" + OpName %16 "i" + OpName %31 "arr" + OpName %52 "i" + OpName %63 "arr" + OpName %82 "i" + OpName %90 "arr" + OpName %109 "i" + OpName %117 "arr" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 0 + %24 = OpConstant %14 10 + %25 = OpTypeBool + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %14 %28 + %30 = OpTypePointer Function %29 + %40 = OpConstant %14 9 + %50 = OpConstant %14 1 + %60 = OpConstant %27 11 + %61 = OpTypeArray %14 %60 + %62 = OpTypePointer Function %61 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %136 = OpFunctionCall %2 %6 + %137 = OpFunctionCall %2 %8 + %138 = OpFunctionCall %2 %10 + %139 = OpFunctionCall %2 %12 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %16 = OpVariable %15 Function + %31 = OpVariable %30 Function + OpStore %16 %17 + OpBranch %18 + %18 = OpLabel + %140 = OpPhi %14 %17 %7 %51 %21 + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + %26 = OpSLessThan %25 %140 %24 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + %33 = OpAccessChain %15 %31 %140 + %34 = OpLoad %14 %33 + %35 = OpAccessChain %15 %31 %17 + OpStore %35 %34 + %37 = OpAccessChain %15 %31 %17 + %38 = OpLoad %14 %37 + %39 = OpAccessChain %15 %31 %140 + OpStore %39 %38 + %42 = OpAccessChain %15 %31 %140 + %43 = OpLoad %14 %42 + %44 = OpAccessChain %15 %31 %40 + OpStore %44 %43 + %46 = OpAccessChain %15 %31 %40 + %47 = OpLoad %14 %46 + %48 = OpAccessChain %15 %31 %140 + OpStore %48 %47 + OpBranch %21 + %21 = OpLabel + %51 = OpIAdd %14 %140 %50 + OpStore %16 %51 + OpBranch %18 + %20 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %52 = OpVariable %15 Function + %63 = OpVariable %62 Function + OpStore %52 %17 + OpBranch %53 + %53 = OpLabel + %141 = OpPhi %14 %17 %9 %81 %56 + OpLoopMerge %55 %56 None + OpBranch %57 + %57 = OpLabel + %59 = OpSLessThanEqual %25 %141 %24 + OpBranchConditional %59 %54 %55 + %54 = OpLabel + %65 = OpAccessChain %15 %63 %141 + %66 = OpLoad %14 %65 + %67 = OpAccessChain %15 %63 %17 + OpStore %67 %66 + %69 = OpAccessChain %15 %63 %17 + %70 = OpLoad %14 %69 + %71 = OpAccessChain %15 %63 %141 + OpStore %71 %70 + %73 = OpAccessChain %15 %63 %141 + %74 = OpLoad %14 %73 + %75 = OpAccessChain %15 %63 %24 + OpStore %75 %74 + %77 = OpAccessChain %15 %63 %24 + %78 = OpLoad %14 %77 + %79 = OpAccessChain %15 %63 %141 + OpStore %79 %78 + OpBranch %56 + %56 = OpLabel + %81 = OpIAdd %14 %141 %50 + OpStore %52 %81 + OpBranch %53 + %55 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %82 = OpVariable %15 Function + %90 = OpVariable %62 Function + OpStore %82 %24 + OpBranch %83 + %83 = OpLabel + %142 = OpPhi %14 %24 %11 %108 %86 + OpLoopMerge %85 %86 None + OpBranch %87 + %87 = OpLabel + %89 = OpSGreaterThan %25 %142 %17 + OpBranchConditional %89 %84 %85 + %84 = OpLabel + %92 = OpAccessChain %15 %90 %142 + %93 = OpLoad %14 %92 + %94 = OpAccessChain %15 %90 %24 + OpStore %94 %93 + %96 = OpAccessChain %15 %90 %24 + %97 = OpLoad %14 %96 + %98 = OpAccessChain %15 %90 %142 + OpStore %98 %97 + %100 = OpAccessChain %15 %90 %142 + %101 = OpLoad %14 %100 + %102 = OpAccessChain %15 %90 %50 + OpStore %102 %101 + %104 = OpAccessChain %15 %90 %50 + %105 = OpLoad %14 %104 + %106 = OpAccessChain %15 %90 %142 + OpStore %106 %105 + OpBranch %86 + %86 = OpLabel + %108 = OpISub %14 %142 %50 + OpStore %82 %108 + OpBranch %83 + %85 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %109 = OpVariable %15 Function + %117 = OpVariable %62 Function + OpStore %109 %24 + OpBranch %110 + %110 = OpLabel + %143 = OpPhi %14 %24 %13 %135 %113 + OpLoopMerge %112 %113 None + OpBranch %114 + %114 = OpLabel + %116 = OpSGreaterThanEqual %25 %143 %17 + OpBranchConditional %116 %111 %112 + %111 = OpLabel + %119 = OpAccessChain %15 %117 %143 + %120 = OpLoad %14 %119 + %121 = OpAccessChain %15 %117 %24 + OpStore %121 %120 + %123 = OpAccessChain %15 %117 %24 + %124 = OpLoad %14 %123 + %125 = OpAccessChain %15 %117 %143 + OpStore %125 %124 + %127 = OpAccessChain %15 %117 %143 + %128 = OpLoad %14 %127 + %129 = OpAccessChain %15 %117 %17 + OpStore %129 %128 + %131 = OpAccessChain %15 %117 %17 + %132 = OpLoad %14 %131 + %133 = OpAccessChain %15 %117 %143 + OpStore %133 %132 + OpBranch %113 + %113 = OpLabel + %135 = OpISub %14 %143 %50 + OpStore %109 %135 + OpBranch %110 + %112 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + // For the loop in function a + { + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 19)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // Tests identifying peel first with weak zero with destination as zero + // index. + // 34 -> 35 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(34), store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); + } + + // Tests identifying peel first with weak zero with source as zero index. + // 38 -> 39 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(38), store[1], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); + } + + // Tests identifying peel first with weak zero with destination as zero + // index. + // 43 -> 44 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(43), store[2], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); + } + + // Tests identifying peel first with weak zero with source as zero index. + // 47 -> 48 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(47), store[3], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); + } + } + // For the loop in function b + { + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 54)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // Tests identifying peel first with weak zero with destination as zero + // index. + // 66 -> 67 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(66), store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); + } + + // Tests identifying peel first with weak zero with source as zero index. + // 70 -> 71 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(70), store[1], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); + } + + // Tests identifying peel first with weak zero with destination as zero + // index. + // 74 -> 75 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(74), store[2], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); + } + + // Tests identifying peel first with weak zero with source as zero index. + // 78 -> 79 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(78), store[3], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); + } + } + // For the loop in function c + { + const Function* f = spvtest::GetFunction(module, 10); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 84)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // Tests identifying peel first with weak zero with destination as zero + // index. + // 93 -> 94 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(93), store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); + } + + // Tests identifying peel first with weak zero with source as zero index. + // 97 -> 98 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(97), store[1], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); + } + + // Tests identifying peel first with weak zero with destination as zero + // index. + // 101 -> 102 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(101), store[2], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); + } + + // Tests identifying peel first with weak zero with source as zero index. + // 105 -> 106 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(105), store[3], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); + } + } + // For the loop in function d + { + const Function* f = spvtest::GetFunction(module, 12); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[4]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 111)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(store[i]); + } + + // Tests identifying peel first with weak zero with destination as zero + // index. + // 120 -> 121 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(120), store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); + } + + // Tests identifying peel first with weak zero with source as zero index. + // 124 -> 125 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(124), store[1], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_first); + } + + // Tests identifying peel first with weak zero with destination as zero + // index. + // 128 -> 129 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(128), store[2], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); + } + + // Tests identifying peel first with weak zero with source as zero index. + // 132 -> 133 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(132), store[3], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::PEEL); + EXPECT_TRUE(distance_vector.GetEntries()[0].peel_last); + } + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void main(){ + int[10][10] arr; + for (int i = 0; i < 10; i++) { + arr[i][i] = arr[i][i]; + arr[0][i] = arr[1][i]; + arr[1][i] = arr[0][i]; + arr[i][0] = arr[i][1]; + arr[i][1] = arr[i][0]; + arr[0][1] = arr[1][0]; + } +} +*/ +TEST(DependencyAnalysis, MultipleSubscriptZIVSIV) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %24 "arr" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypeArray %21 %20 + %23 = OpTypePointer Function %22 + %33 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %24 = OpVariable %23 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %58 = OpPhi %6 %9 %5 %57 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %58 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %29 = OpAccessChain %7 %24 %58 %58 + %30 = OpLoad %6 %29 + %31 = OpAccessChain %7 %24 %58 %58 + OpStore %31 %30 + %35 = OpAccessChain %7 %24 %33 %58 + %36 = OpLoad %6 %35 + %37 = OpAccessChain %7 %24 %9 %58 + OpStore %37 %36 + %40 = OpAccessChain %7 %24 %9 %58 + %41 = OpLoad %6 %40 + %42 = OpAccessChain %7 %24 %33 %58 + OpStore %42 %41 + %45 = OpAccessChain %7 %24 %58 %33 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %7 %24 %58 %9 + OpStore %47 %46 + %50 = OpAccessChain %7 %24 %58 %9 + %51 = OpLoad %6 %50 + %52 = OpAccessChain %7 %24 %58 %33 + OpStore %52 %51 + %53 = OpAccessChain %7 %24 %33 %9 + %54 = OpLoad %6 %53 + %55 = OpAccessChain %7 %24 %9 %33 + OpStore %55 %54 + OpBranch %13 + %13 = OpLabel + %57 = OpIAdd %6 %58 %33 + OpStore %8 %57 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[6]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 11)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 6; ++i) { + EXPECT_TRUE(store[i]); + } + + // 30 -> 31 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(30), + store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::EQ); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); + } + + // 36 -> 37 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(36), + store[1], &distance_vector)); + } + + // 41 -> 42 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(41), + store[2], &distance_vector)); + } + + // 46 -> 47 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(46), + store[3], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::EQ); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); + } + + // 51 -> 52 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(51), + store[4], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::EQ); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); + } + + // 54 -> 55 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_TRUE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(54), + store[5], &distance_vector)); + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void a(){ + int[10] arr; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + arr[j] = arr[j]; + } + } +} +void b(){ + int[10] arr; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + arr[i] = arr[i]; + } + } +} +void main() { + a(); + b(); +} +*/ +TEST(DependencyAnalysis, IrrelevantSubscripts) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %12 "i" + OpName %23 "j" + OpName %35 "arr" + OpName %46 "i" + OpName %54 "j" + OpName %62 "arr" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 0 + %20 = OpConstant %10 10 + %21 = OpTypeBool + %31 = OpTypeInt 32 0 + %32 = OpConstant %31 10 + %33 = OpTypeArray %10 %32 + %34 = OpTypePointer Function %33 + %42 = OpConstant %10 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %72 = OpFunctionCall %2 %6 + %73 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %23 = OpVariable %11 Function + %35 = OpVariable %34 Function + OpStore %12 %13 + OpBranch %14 + %14 = OpLabel + %74 = OpPhi %10 %13 %7 %45 %17 + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + %22 = OpSLessThan %21 %74 %20 + OpBranchConditional %22 %15 %16 + %15 = OpLabel + OpStore %23 %13 + OpBranch %24 + %24 = OpLabel + %75 = OpPhi %10 %13 %15 %43 %27 + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %30 = OpSLessThan %21 %75 %20 + OpBranchConditional %30 %25 %26 + %25 = OpLabel + %38 = OpAccessChain %11 %35 %75 + %39 = OpLoad %10 %38 + %40 = OpAccessChain %11 %35 %75 + OpStore %40 %39 + OpBranch %27 + %27 = OpLabel + %43 = OpIAdd %10 %75 %42 + OpStore %23 %43 + OpBranch %24 + %26 = OpLabel + OpBranch %17 + %17 = OpLabel + %45 = OpIAdd %10 %74 %42 + OpStore %12 %45 + OpBranch %14 + %16 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %46 = OpVariable %11 Function + %54 = OpVariable %11 Function + %62 = OpVariable %34 Function + OpStore %46 %13 + OpBranch %47 + %47 = OpLabel + %77 = OpPhi %10 %13 %9 %71 %50 + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %53 = OpSLessThan %21 %77 %20 + OpBranchConditional %53 %48 %49 + %48 = OpLabel + OpStore %54 %13 + OpBranch %55 + %55 = OpLabel + %78 = OpPhi %10 %13 %48 %69 %58 + OpLoopMerge %57 %58 None + OpBranch %59 + %59 = OpLabel + %61 = OpSLessThan %21 %78 %20 + OpBranchConditional %61 %56 %57 + %56 = OpLabel + %65 = OpAccessChain %11 %62 %77 + %66 = OpLoad %10 %65 + %67 = OpAccessChain %11 %62 %77 + OpStore %67 %66 + OpBranch %58 + %58 = OpLabel + %69 = OpIAdd %10 %78 %42 + OpStore %54 %69 + OpBranch %55 + %57 = OpLabel + OpBranch %50 + %50 = OpLabel + %71 = OpIAdd %10 %77 %42 + OpStore %46 %71 + OpBranch %47 + %49 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + // For the loop in function a + { + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + std::vector loops{&ld.GetLoopByIndex(1), + &ld.GetLoopByIndex(0)}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[1]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 25)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 1; ++i) { + EXPECT_TRUE(store[i]); + } + + // 39 -> 40 + { + DistanceVector distance_vector{loops.size()}; + analysis.SetDebugStream(std::cout); + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(39), store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::IRRELEVANT); + EXPECT_EQ(distance_vector.GetEntries()[1].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[1].distance, 0); + } + } + + // For the loop in function b + { + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + std::vector loops{&ld.GetLoopByIndex(1), + &ld.GetLoopByIndex(0)}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[1]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 56)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 1; ++i) { + EXPECT_TRUE(store[i]); + } + + // 66 -> 67 + { + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.GetDependence( + context->get_def_use_mgr()->GetDef(66), store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::DISTANCE); + EXPECT_EQ(distance_vector.GetEntries()[0].distance, 0); + EXPECT_EQ(distance_vector.GetEntries()[1].dependence_information, + DistanceEntry::DependenceInformation::IRRELEVANT); + } + } +} + +void CheckDependenceAndDirection(const Instruction* source, + const Instruction* destination, + bool expected_dependence, + DistanceVector expected_distance, + LoopDependenceAnalysis* analysis) { + DistanceVector dv_entry(2); + EXPECT_EQ(expected_dependence, + analysis->GetDependence(source, destination, &dv_entry)); + EXPECT_EQ(expected_distance, dv_entry); +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +layout(location = 0) in vec4 c; +void main(){ + int[10] arr; + int a = 2; + int b = 3; + int N = int(c.x); + for (int i = 0; i < 10; i++) { + for (int j = 2; j < 10; j++) { + arr[i] = arr[j]; // 0 + arr[j] = arr[i]; // 1 + arr[j-2] = arr[i+3]; // 2 + arr[j-a] = arr[i+b]; // 3 + arr[2*i] = arr[4*j+3]; // 4, independent + arr[2*i] = arr[4*j]; // 5 + arr[i+j] = arr[i+j]; // 6 + arr[10*i+j] = arr[10*i+j]; // 7 + arr[10*i+10*j] = arr[10*i+10*j+3]; // 8, independent + arr[10*i+10*j] = arr[10*i+N*j+3]; // 9, bail out because of N coefficient + arr[10*i+10*j] = arr[10*i+10*j+N]; // 10, bail out because of N constant + // term + arr[10*i+N*j] = arr[10*i+10*j+3]; // 11, bail out because of N coefficient + arr[10*i+10*j+N] = arr[10*i+10*j]; // 12, bail out because of N constant + // term + arr[10*i] = arr[5*j]; // 13, independent + arr[5*i] = arr[10*j]; // 14, independent + arr[9*i] = arr[3*j]; // 15, independent + arr[3*i] = arr[9*j]; // 16, independent + } + } +} +*/ +TEST(DependencyAnalysis, MIV) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %16 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %12 "N" + OpName %16 "c" + OpName %23 "i" + OpName %34 "j" + OpName %45 "arr" + OpDecorate %16 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %13 = OpTypeFloat 32 + %14 = OpTypeVector %13 4 + %15 = OpTypePointer Input %14 + %16 = OpVariable %15 Input + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %19 = OpTypePointer Input %13 + %24 = OpConstant %6 0 + %31 = OpConstant %6 10 + %32 = OpTypeBool + %42 = OpConstant %17 10 + %43 = OpTypeArray %6 %42 + %44 = OpTypePointer Function %43 + %74 = OpConstant %6 4 + %184 = OpConstant %6 5 + %197 = OpConstant %6 9 + %213 = OpConstant %6 1 + %218 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %23 = OpVariable %7 Function + %34 = OpVariable %7 Function + %45 = OpVariable %44 Function + OpStore %8 %9 + OpStore %10 %11 + %20 = OpAccessChain %19 %16 %18 + %21 = OpLoad %13 %20 + %22 = OpConvertFToS %6 %21 + OpStore %12 %22 + OpStore %23 %24 + OpBranch %25 + %25 = OpLabel + %217 = OpPhi %6 %24 %5 %216 %28 + %219 = OpPhi %6 %218 %5 %220 %28 + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + %33 = OpSLessThan %32 %217 %31 + OpBranchConditional %33 %26 %27 + %26 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %220 = OpPhi %6 %9 %26 %214 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %32 %220 %31 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %48 = OpAccessChain %7 %45 %220 + %49 = OpLoad %6 %48 + %50 = OpAccessChain %7 %45 %217 + OpStore %50 %49 + %53 = OpAccessChain %7 %45 %217 + %54 = OpLoad %6 %53 + %55 = OpAccessChain %7 %45 %220 + OpStore %55 %54 + %57 = OpISub %6 %220 %9 + %59 = OpIAdd %6 %217 %11 + %60 = OpAccessChain %7 %45 %59 + %61 = OpLoad %6 %60 + %62 = OpAccessChain %7 %45 %57 + OpStore %62 %61 + %65 = OpISub %6 %220 %9 + %68 = OpIAdd %6 %217 %11 + %69 = OpAccessChain %7 %45 %68 + %70 = OpLoad %6 %69 + %71 = OpAccessChain %7 %45 %65 + OpStore %71 %70 + %73 = OpIMul %6 %9 %217 + %76 = OpIMul %6 %74 %220 + %77 = OpIAdd %6 %76 %11 + %78 = OpAccessChain %7 %45 %77 + %79 = OpLoad %6 %78 + %80 = OpAccessChain %7 %45 %73 + OpStore %80 %79 + %82 = OpIMul %6 %9 %217 + %84 = OpIMul %6 %74 %220 + %85 = OpAccessChain %7 %45 %84 + %86 = OpLoad %6 %85 + %87 = OpAccessChain %7 %45 %82 + OpStore %87 %86 + %90 = OpIAdd %6 %217 %220 + %93 = OpIAdd %6 %217 %220 + %94 = OpAccessChain %7 %45 %93 + %95 = OpLoad %6 %94 + %96 = OpAccessChain %7 %45 %90 + OpStore %96 %95 + %98 = OpIMul %6 %31 %217 + %100 = OpIAdd %6 %98 %220 + %102 = OpIMul %6 %31 %217 + %104 = OpIAdd %6 %102 %220 + %105 = OpAccessChain %7 %45 %104 + %106 = OpLoad %6 %105 + %107 = OpAccessChain %7 %45 %100 + OpStore %107 %106 + %109 = OpIMul %6 %31 %217 + %111 = OpIMul %6 %31 %220 + %112 = OpIAdd %6 %109 %111 + %114 = OpIMul %6 %31 %217 + %116 = OpIMul %6 %31 %220 + %117 = OpIAdd %6 %114 %116 + %118 = OpIAdd %6 %117 %11 + %119 = OpAccessChain %7 %45 %118 + %120 = OpLoad %6 %119 + %121 = OpAccessChain %7 %45 %112 + OpStore %121 %120 + %123 = OpIMul %6 %31 %217 + %125 = OpIMul %6 %31 %220 + %126 = OpIAdd %6 %123 %125 + %128 = OpIMul %6 %31 %217 + %131 = OpIMul %6 %22 %220 + %132 = OpIAdd %6 %128 %131 + %133 = OpIAdd %6 %132 %11 + %134 = OpAccessChain %7 %45 %133 + %135 = OpLoad %6 %134 + %136 = OpAccessChain %7 %45 %126 + OpStore %136 %135 + %138 = OpIMul %6 %31 %217 + %140 = OpIMul %6 %31 %220 + %141 = OpIAdd %6 %138 %140 + %143 = OpIMul %6 %31 %217 + %145 = OpIMul %6 %31 %220 + %146 = OpIAdd %6 %143 %145 + %148 = OpIAdd %6 %146 %22 + %149 = OpAccessChain %7 %45 %148 + %150 = OpLoad %6 %149 + %151 = OpAccessChain %7 %45 %141 + OpStore %151 %150 + %153 = OpIMul %6 %31 %217 + %156 = OpIMul %6 %22 %220 + %157 = OpIAdd %6 %153 %156 + %159 = OpIMul %6 %31 %217 + %161 = OpIMul %6 %31 %220 + %162 = OpIAdd %6 %159 %161 + %163 = OpIAdd %6 %162 %11 + %164 = OpAccessChain %7 %45 %163 + %165 = OpLoad %6 %164 + %166 = OpAccessChain %7 %45 %157 + OpStore %166 %165 + %168 = OpIMul %6 %31 %217 + %170 = OpIMul %6 %31 %220 + %171 = OpIAdd %6 %168 %170 + %173 = OpIAdd %6 %171 %22 + %175 = OpIMul %6 %31 %217 + %177 = OpIMul %6 %31 %220 + %178 = OpIAdd %6 %175 %177 + %179 = OpAccessChain %7 %45 %178 + %180 = OpLoad %6 %179 + %181 = OpAccessChain %7 %45 %173 + OpStore %181 %180 + %183 = OpIMul %6 %31 %217 + %186 = OpIMul %6 %184 %220 + %187 = OpAccessChain %7 %45 %186 + %188 = OpLoad %6 %187 + %189 = OpAccessChain %7 %45 %183 + OpStore %189 %188 + %191 = OpIMul %6 %184 %217 + %193 = OpIMul %6 %31 %220 + %194 = OpAccessChain %7 %45 %193 + %195 = OpLoad %6 %194 + %196 = OpAccessChain %7 %45 %191 + OpStore %196 %195 + %199 = OpIMul %6 %197 %217 + %201 = OpIMul %6 %11 %220 + %202 = OpAccessChain %7 %45 %201 + %203 = OpLoad %6 %202 + %204 = OpAccessChain %7 %45 %199 + OpStore %204 %203 + %206 = OpIMul %6 %11 %217 + %208 = OpIMul %6 %197 %220 + %209 = OpAccessChain %7 %45 %208 + %210 = OpLoad %6 %209 + %211 = OpAccessChain %7 %45 %206 + OpStore %211 %210 + OpBranch %38 + %38 = OpLabel + %214 = OpIAdd %6 %220 %213 + OpStore %34 %214 + OpBranch %35 + %37 = OpLabel + OpBranch %28 + %28 = OpLabel + %216 = OpIAdd %6 %217 %213 + OpStore %23 %216 + OpBranch %25 + %27 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + std::vector loops{&ld.GetLoopByIndex(0), &ld.GetLoopByIndex(1)}; + + LoopDependenceAnalysis analysis{context.get(), loops}; + + const int instructions_expected = 17; + const Instruction* store[instructions_expected]; + const Instruction* load[instructions_expected]; + int stores_found = 0; + int loads_found = 0; + + int block_id = 36; + ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + + if (inst.opcode() == SpvOp::SpvOpLoad) { + load[loads_found] = &inst; + ++loads_found; + } + } + + EXPECT_EQ(instructions_expected, stores_found); + EXPECT_EQ(instructions_expected, loads_found); + + auto directions_all = DistanceEntry(DistanceEntry::Directions::ALL); + auto directions_none = DistanceEntry(DistanceEntry::Directions::NONE); + + auto dependent = DistanceVector({directions_all, directions_all}); + auto independent = DistanceVector({directions_none, directions_none}); + + CheckDependenceAndDirection(load[0], store[0], false, dependent, &analysis); + CheckDependenceAndDirection(load[1], store[1], false, dependent, &analysis); + CheckDependenceAndDirection(load[2], store[2], false, dependent, &analysis); + CheckDependenceAndDirection(load[3], store[3], false, dependent, &analysis); + CheckDependenceAndDirection(load[4], store[4], true, independent, &analysis); + CheckDependenceAndDirection(load[5], store[5], false, dependent, &analysis); + CheckDependenceAndDirection(load[6], store[6], false, dependent, &analysis); + CheckDependenceAndDirection(load[7], store[7], false, dependent, &analysis); + CheckDependenceAndDirection(load[8], store[8], true, independent, &analysis); + CheckDependenceAndDirection(load[9], store[9], false, dependent, &analysis); + CheckDependenceAndDirection(load[10], store[10], false, dependent, &analysis); + CheckDependenceAndDirection(load[11], store[11], false, dependent, &analysis); + CheckDependenceAndDirection(load[12], store[12], false, dependent, &analysis); + CheckDependenceAndDirection(load[13], store[13], true, independent, + &analysis); + CheckDependenceAndDirection(load[14], store[14], true, independent, + &analysis); + CheckDependenceAndDirection(load[15], store[15], true, independent, + &analysis); + CheckDependenceAndDirection(load[16], store[16], true, independent, + &analysis); +} + +void PartitionSubscripts(const Instruction* instruction_0, + const Instruction* instruction_1, + LoopDependenceAnalysis* analysis, + std::vector> expected_ids) { + auto subscripts_0 = analysis->GetSubscripts(instruction_0); + auto subscripts_1 = analysis->GetSubscripts(instruction_1); + + std::vector>> + expected_partition{}; + + for (const auto& partition : expected_ids) { + expected_partition.push_back( + std::set>{}); + for (auto id : partition) { + expected_partition.back().insert({subscripts_0[id], subscripts_1[id]}); + } + } + + EXPECT_EQ(expected_partition, + analysis->PartitionSubscripts(subscripts_0, subscripts_1)); +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void main(){ + int[10][10][10][10] arr; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + for (int k = 0; k < 10; k++) { + for (int l = 0; l < 10; l++) { + arr[i][j][k][l] = arr[i][j][k][l]; // 0, all independent + arr[i][j][k][l] = arr[i][j][l][0]; // 1, last 2 coupled + arr[i][j][k][l] = arr[j][i][k][l]; // 2, first 2 coupled + arr[i][j][k][l] = arr[l][j][k][i]; // 3, first & last coupled + arr[i][j][k][l] = arr[i][k][j][l]; // 4, middle 2 coupled + arr[i+j][j][k][l] = arr[i][j][k][l]; // 5, first 2 coupled + arr[i+j+k][j][k][l] = arr[i][j][k][l]; // 6, first 3 coupled + arr[i+j+k+l][j][k][l] = arr[i][j][k][l]; // 7, all 4 coupled + arr[i][j][k][l] = arr[i][l][j][k]; // 8, last 3 coupled + arr[i][j-k][k][l] = arr[i][j][l][k]; // 9, last 3 coupled + arr[i][j][k][l] = arr[l][i][j][k]; // 10, all 4 coupled + arr[i][j][k][l] = arr[j][i][l][k]; // 11, 2 coupled partitions (i,j) & +(l&k) + arr[i][j][k][l] = arr[k][l][i][j]; // 12, 2 coupled partitions (i,k) & +(j&l) + } + } + } + } +} +*/ +TEST(DependencyAnalysis, SubscriptPartitioning) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %27 "k" + OpName %35 "l" + OpName %50 "arr" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %43 = OpTypeInt 32 0 + %44 = OpConstant %43 10 + %45 = OpTypeArray %6 %44 + %46 = OpTypeArray %45 %44 + %47 = OpTypeArray %46 %44 + %48 = OpTypeArray %47 %44 + %49 = OpTypePointer Function %48 + %208 = OpConstant %6 1 + %217 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %27 = OpVariable %7 Function + %35 = OpVariable %7 Function + %50 = OpVariable %49 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %216 = OpPhi %6 %9 %5 %215 %13 + %218 = OpPhi %6 %217 %5 %221 %13 + %219 = OpPhi %6 %217 %5 %222 %13 + %220 = OpPhi %6 %217 %5 %223 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %216 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %221 = OpPhi %6 %9 %11 %213 %23 + %222 = OpPhi %6 %219 %11 %224 %23 + %223 = OpPhi %6 %220 %11 %225 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %221 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + OpStore %27 %9 + OpBranch %28 + %28 = OpLabel + %224 = OpPhi %6 %9 %21 %211 %31 + %225 = OpPhi %6 %223 %21 %226 %31 + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %34 = OpSLessThan %17 %224 %16 + OpBranchConditional %34 %29 %30 + %29 = OpLabel + OpStore %35 %9 + OpBranch %36 + %36 = OpLabel + %226 = OpPhi %6 %9 %29 %209 %39 + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %42 = OpSLessThan %17 %226 %16 + OpBranchConditional %42 %37 %38 + %37 = OpLabel + %59 = OpAccessChain %7 %50 %216 %221 %224 %226 + %60 = OpLoad %6 %59 + %61 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %61 %60 + %69 = OpAccessChain %7 %50 %216 %221 %226 %9 + %70 = OpLoad %6 %69 + %71 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %71 %70 + %80 = OpAccessChain %7 %50 %221 %216 %224 %226 + %81 = OpLoad %6 %80 + %82 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %82 %81 + %91 = OpAccessChain %7 %50 %226 %221 %224 %216 + %92 = OpLoad %6 %91 + %93 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %93 %92 + %102 = OpAccessChain %7 %50 %216 %224 %221 %226 + %103 = OpLoad %6 %102 + %104 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %104 %103 + %107 = OpIAdd %6 %216 %221 + %115 = OpAccessChain %7 %50 %216 %221 %224 %226 + %116 = OpLoad %6 %115 + %117 = OpAccessChain %7 %50 %107 %221 %224 %226 + OpStore %117 %116 + %120 = OpIAdd %6 %216 %221 + %122 = OpIAdd %6 %120 %224 + %130 = OpAccessChain %7 %50 %216 %221 %224 %226 + %131 = OpLoad %6 %130 + %132 = OpAccessChain %7 %50 %122 %221 %224 %226 + OpStore %132 %131 + %135 = OpIAdd %6 %216 %221 + %137 = OpIAdd %6 %135 %224 + %139 = OpIAdd %6 %137 %226 + %147 = OpAccessChain %7 %50 %216 %221 %224 %226 + %148 = OpLoad %6 %147 + %149 = OpAccessChain %7 %50 %139 %221 %224 %226 + OpStore %149 %148 + %158 = OpAccessChain %7 %50 %216 %226 %221 %224 + %159 = OpLoad %6 %158 + %160 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %160 %159 + %164 = OpISub %6 %221 %224 + %171 = OpAccessChain %7 %50 %216 %221 %226 %224 + %172 = OpLoad %6 %171 + %173 = OpAccessChain %7 %50 %216 %164 %224 %226 + OpStore %173 %172 + %182 = OpAccessChain %7 %50 %226 %216 %221 %224 + %183 = OpLoad %6 %182 + %184 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %184 %183 + %193 = OpAccessChain %7 %50 %221 %216 %226 %224 + %194 = OpLoad %6 %193 + %195 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %195 %194 + %204 = OpAccessChain %7 %50 %224 %226 %216 %221 + %205 = OpLoad %6 %204 + %206 = OpAccessChain %7 %50 %216 %221 %224 %226 + OpStore %206 %205 + OpBranch %39 + %39 = OpLabel + %209 = OpIAdd %6 %226 %208 + OpStore %35 %209 + OpBranch %36 + %38 = OpLabel + OpBranch %31 + %31 = OpLabel + %211 = OpIAdd %6 %224 %208 + OpStore %27 %211 + OpBranch %28 + %30 = OpLabel + OpBranch %23 + %23 = OpLabel + %213 = OpIAdd %6 %221 %208 + OpStore %19 %213 + OpBranch %20 + %22 = OpLabel + OpBranch %13 + %13 = OpLabel + %215 = OpIAdd %6 %216 %208 + OpStore %8 %215 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + std::vector loop_nest{ + &ld.GetLoopByIndex(0), &ld.GetLoopByIndex(1), &ld.GetLoopByIndex(2), + &ld.GetLoopByIndex(3)}; + LoopDependenceAnalysis analysis{context.get(), loop_nest}; + + const int instructions_expected = 13; + const Instruction* store[instructions_expected]; + const Instruction* load[instructions_expected]; + int stores_found = 0; + int loads_found = 0; + + int block_id = 37; + ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + + if (inst.opcode() == SpvOp::SpvOpLoad) { + load[loads_found] = &inst; + ++loads_found; + } + } + + EXPECT_EQ(instructions_expected, stores_found); + EXPECT_EQ(instructions_expected, loads_found); + + PartitionSubscripts(load[0], store[0], &analysis, {{0}, {1}, {2}, {3}}); + PartitionSubscripts(load[1], store[1], &analysis, {{0}, {1}, {2, 3}}); + PartitionSubscripts(load[2], store[2], &analysis, {{0, 1}, {2}, {3}}); + PartitionSubscripts(load[3], store[3], &analysis, {{0, 3}, {1}, {2}}); + PartitionSubscripts(load[4], store[4], &analysis, {{0}, {1, 2}, {3}}); + PartitionSubscripts(load[5], store[5], &analysis, {{0, 1}, {2}, {3}}); + PartitionSubscripts(load[6], store[6], &analysis, {{0, 1, 2}, {3}}); + PartitionSubscripts(load[7], store[7], &analysis, {{0, 1, 2, 3}}); + PartitionSubscripts(load[8], store[8], &analysis, {{0}, {1, 2, 3}}); + PartitionSubscripts(load[9], store[9], &analysis, {{0}, {1, 2, 3}}); + PartitionSubscripts(load[10], store[10], &analysis, {{0, 1, 2, 3}}); + PartitionSubscripts(load[11], store[11], &analysis, {{0, 1}, {2, 3}}); + PartitionSubscripts(load[12], store[12], &analysis, {{0, 2}, {1, 3}}); +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store + +#version 440 core +void a() { + int[10][10] arr; + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + // Dependent, distance vector (1, -1) + arr[i+1][i+j] = arr[i][i+j]; + } + } +} + +void b() { + int[10][10] arr; + for (int i = 0; i < 10; ++i) { + // Independent + arr[i+1][i+2] = arr[i][i] + 2; + } +} + +void c() { + int[10][10] arr; + for (int i = 0; i < 10; ++i) { + // Dependence point (1,2) + arr[i][i] = arr[1][i-1] + 2; + } +} + +void d() { + int[10][10][10] arr; + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + for (int k = 0; k < 10; ++k) { + // Dependent, distance vector (1,1,-1) + arr[j-i][i+1][j+k] = arr[j-i][i][j+k]; + } + } + } +} + +void e() { + int[10][10] arr; + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 10; ++j) { + // Independent with GCD after propagation + arr[i][2*j+i] = arr[i][2*j-i+5]; + } + } +} + +void main(){ + a(); + b(); + c(); + d(); + e(); +} +*/ +TEST(DependencyAnalysis, Delta) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %10 "c(" + OpName %12 "d(" + OpName %14 "e(" + OpName %18 "i" + OpName %29 "j" + OpName %42 "arr" + OpName %60 "i" + OpName %68 "arr" + OpName %82 "i" + OpName %90 "arr" + OpName %101 "i" + OpName %109 "j" + OpName %117 "k" + OpName %127 "arr" + OpName %152 "i" + OpName %160 "j" + OpName %168 "arr" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %16 = OpTypeInt 32 1 + %17 = OpTypePointer Function %16 + %19 = OpConstant %16 0 + %26 = OpConstant %16 10 + %27 = OpTypeBool + %37 = OpTypeInt 32 0 + %38 = OpConstant %37 10 + %39 = OpTypeArray %16 %38 + %40 = OpTypeArray %39 %38 + %41 = OpTypePointer Function %40 + %44 = OpConstant %16 1 + %72 = OpConstant %16 2 + %125 = OpTypeArray %40 %38 + %126 = OpTypePointer Function %125 + %179 = OpConstant %16 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %188 = OpFunctionCall %2 %6 + %189 = OpFunctionCall %2 %8 + %190 = OpFunctionCall %2 %10 + %191 = OpFunctionCall %2 %12 + %192 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %18 = OpVariable %17 Function + %29 = OpVariable %17 Function + %42 = OpVariable %41 Function + OpStore %18 %19 + OpBranch %20 + %20 = OpLabel + %193 = OpPhi %16 %19 %7 %59 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %28 = OpSLessThan %27 %193 %26 + OpBranchConditional %28 %21 %22 + %21 = OpLabel + OpStore %29 %19 + OpBranch %30 + %30 = OpLabel + %194 = OpPhi %16 %19 %21 %57 %33 + OpLoopMerge %32 %33 None + OpBranch %34 + %34 = OpLabel + %36 = OpSLessThan %27 %194 %26 + OpBranchConditional %36 %31 %32 + %31 = OpLabel + %45 = OpIAdd %16 %193 %44 + %48 = OpIAdd %16 %193 %194 + %52 = OpIAdd %16 %193 %194 + %53 = OpAccessChain %17 %42 %193 %52 + %54 = OpLoad %16 %53 + %55 = OpAccessChain %17 %42 %45 %48 + OpStore %55 %54 + OpBranch %33 + %33 = OpLabel + %57 = OpIAdd %16 %194 %44 + OpStore %29 %57 + OpBranch %30 + %32 = OpLabel + OpBranch %23 + %23 = OpLabel + %59 = OpIAdd %16 %193 %44 + OpStore %18 %59 + OpBranch %20 + %22 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %60 = OpVariable %17 Function + %68 = OpVariable %41 Function + OpStore %60 %19 + OpBranch %61 + %61 = OpLabel + %196 = OpPhi %16 %19 %9 %81 %64 + OpLoopMerge %63 %64 None + OpBranch %65 + %65 = OpLabel + %67 = OpSLessThan %27 %196 %26 + OpBranchConditional %67 %62 %63 + %62 = OpLabel + %70 = OpIAdd %16 %196 %44 + %73 = OpIAdd %16 %196 %72 + %76 = OpAccessChain %17 %68 %196 %196 + %77 = OpLoad %16 %76 + %78 = OpIAdd %16 %77 %72 + %79 = OpAccessChain %17 %68 %70 %73 + OpStore %79 %78 + OpBranch %64 + %64 = OpLabel + %81 = OpIAdd %16 %196 %44 + OpStore %60 %81 + OpBranch %61 + %63 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %82 = OpVariable %17 Function + %90 = OpVariable %41 Function + OpStore %82 %19 + OpBranch %83 + %83 = OpLabel + %197 = OpPhi %16 %19 %11 %100 %86 + OpLoopMerge %85 %86 None + OpBranch %87 + %87 = OpLabel + %89 = OpSLessThan %27 %197 %26 + OpBranchConditional %89 %84 %85 + %84 = OpLabel + %94 = OpISub %16 %197 %44 + %95 = OpAccessChain %17 %90 %44 %94 + %96 = OpLoad %16 %95 + %97 = OpIAdd %16 %96 %72 + %98 = OpAccessChain %17 %90 %197 %197 + OpStore %98 %97 + OpBranch %86 + %86 = OpLabel + %100 = OpIAdd %16 %197 %44 + OpStore %82 %100 + OpBranch %83 + %85 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %101 = OpVariable %17 Function + %109 = OpVariable %17 Function + %117 = OpVariable %17 Function + %127 = OpVariable %126 Function + OpStore %101 %19 + OpBranch %102 + %102 = OpLabel + %198 = OpPhi %16 %19 %13 %151 %105 + OpLoopMerge %104 %105 None + OpBranch %106 + %106 = OpLabel + %108 = OpSLessThan %27 %198 %26 + OpBranchConditional %108 %103 %104 + %103 = OpLabel + OpStore %109 %19 + OpBranch %110 + %110 = OpLabel + %199 = OpPhi %16 %19 %103 %149 %113 + OpLoopMerge %112 %113 None + OpBranch %114 + %114 = OpLabel + %116 = OpSLessThan %27 %199 %26 + OpBranchConditional %116 %111 %112 + %111 = OpLabel + OpStore %117 %19 + OpBranch %118 + %118 = OpLabel + %201 = OpPhi %16 %19 %111 %147 %121 + OpLoopMerge %120 %121 None + OpBranch %122 + %122 = OpLabel + %124 = OpSLessThan %27 %201 %26 + OpBranchConditional %124 %119 %120 + %119 = OpLabel + %130 = OpISub %16 %199 %198 + %132 = OpIAdd %16 %198 %44 + %135 = OpIAdd %16 %199 %201 + %138 = OpISub %16 %199 %198 + %142 = OpIAdd %16 %199 %201 + %143 = OpAccessChain %17 %127 %138 %198 %142 + %144 = OpLoad %16 %143 + %145 = OpAccessChain %17 %127 %130 %132 %135 + OpStore %145 %144 + OpBranch %121 + %121 = OpLabel + %147 = OpIAdd %16 %201 %44 + OpStore %117 %147 + OpBranch %118 + %120 = OpLabel + OpBranch %113 + %113 = OpLabel + %149 = OpIAdd %16 %199 %44 + OpStore %109 %149 + OpBranch %110 + %112 = OpLabel + OpBranch %105 + %105 = OpLabel + %151 = OpIAdd %16 %198 %44 + OpStore %101 %151 + OpBranch %102 + %104 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %152 = OpVariable %17 Function + %160 = OpVariable %17 Function + %168 = OpVariable %41 Function + OpStore %152 %19 + OpBranch %153 + %153 = OpLabel + %204 = OpPhi %16 %19 %15 %187 %156 + OpLoopMerge %155 %156 None + OpBranch %157 + %157 = OpLabel + %159 = OpSLessThan %27 %204 %26 + OpBranchConditional %159 %154 %155 + %154 = OpLabel + OpStore %160 %19 + OpBranch %161 + %161 = OpLabel + %205 = OpPhi %16 %19 %154 %185 %164 + OpLoopMerge %163 %164 None + OpBranch %165 + %165 = OpLabel + %167 = OpSLessThan %27 %205 %26 + OpBranchConditional %167 %162 %163 + %162 = OpLabel + %171 = OpIMul %16 %72 %205 + %173 = OpIAdd %16 %171 %204 + %176 = OpIMul %16 %72 %205 + %178 = OpISub %16 %176 %204 + %180 = OpIAdd %16 %178 %179 + %181 = OpAccessChain %17 %168 %204 %180 + %182 = OpLoad %16 %181 + %183 = OpAccessChain %17 %168 %204 %173 + OpStore %183 %182 + OpBranch %164 + %164 = OpLabel + %185 = OpIAdd %16 %205 %44 + OpStore %160 %185 + OpBranch %161 + %163 = OpLabel + OpBranch %156 + %156 = OpLabel + %187 = OpIAdd %16 %204 %44 + OpStore %152 %187 + OpBranch %153 + %155 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + ASSERT_NE(nullptr, context); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + { + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + const Instruction* store = nullptr; + const Instruction* load = nullptr; + + int block_id = 31; + ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + + if (inst.opcode() == SpvOp::SpvOpLoad) { + load = &inst; + } + } + + EXPECT_NE(nullptr, store); + EXPECT_NE(nullptr, load); + + std::vector loop_nest{&ld.GetLoopByIndex(0), + &ld.GetLoopByIndex(1)}; + LoopDependenceAnalysis analysis{context.get(), loop_nest}; + + DistanceVector dv_entry(loop_nest.size()); + + std::vector expected_entries{ + DistanceEntry(DistanceEntry::Directions::LT, 1), + DistanceEntry(DistanceEntry::Directions::LT, 1)}; + + DistanceVector expected_distance_vector(expected_entries); + + auto is_independent = analysis.GetDependence(load, store, &dv_entry); + + EXPECT_FALSE(is_independent); + EXPECT_EQ(expected_distance_vector, dv_entry); + } + + { + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + const Instruction* store = nullptr; + const Instruction* load = nullptr; + + int block_id = 62; + ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + + if (inst.opcode() == SpvOp::SpvOpLoad) { + load = &inst; + } + } + + EXPECT_NE(nullptr, store); + EXPECT_NE(nullptr, load); + + std::vector loop_nest{&ld.GetLoopByIndex(0)}; + LoopDependenceAnalysis analysis{context.get(), loop_nest}; + + DistanceVector dv_entry(loop_nest.size()); + auto is_independent = analysis.GetDependence(load, store, &dv_entry); + + EXPECT_TRUE(is_independent); + } + + { + const Function* f = spvtest::GetFunction(module, 10); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + const Instruction* store = nullptr; + const Instruction* load = nullptr; + + int block_id = 84; + ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + + if (inst.opcode() == SpvOp::SpvOpLoad) { + load = &inst; + } + } + + EXPECT_NE(nullptr, store); + EXPECT_NE(nullptr, load); + + std::vector loop_nest{&ld.GetLoopByIndex(0)}; + LoopDependenceAnalysis analysis{context.get(), loop_nest}; + + DistanceVector dv_entry(loop_nest.size()); + auto is_independent = analysis.GetDependence(load, store, &dv_entry); + + DistanceVector expected_distance_vector({DistanceEntry(1, 2)}); + + EXPECT_FALSE(is_independent); + EXPECT_EQ(expected_distance_vector, dv_entry); + } + + { + const Function* f = spvtest::GetFunction(module, 12); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + const Instruction* store = nullptr; + const Instruction* load = nullptr; + + int block_id = 119; + ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + + if (inst.opcode() == SpvOp::SpvOpLoad) { + load = &inst; + } + } + + EXPECT_NE(nullptr, store); + EXPECT_NE(nullptr, load); + + std::vector loop_nest{ + &ld.GetLoopByIndex(0), &ld.GetLoopByIndex(1), &ld.GetLoopByIndex(2)}; + LoopDependenceAnalysis analysis{context.get(), loop_nest}; + + DistanceVector dv_entry(loop_nest.size()); + + std::vector expected_entries{ + DistanceEntry(DistanceEntry::Directions::LT, 1), + DistanceEntry(DistanceEntry::Directions::LT, 1), + DistanceEntry(DistanceEntry::Directions::GT, -1)}; + + DistanceVector expected_distance_vector(expected_entries); + + auto is_independent = analysis.GetDependence(store, load, &dv_entry); + + EXPECT_FALSE(is_independent); + EXPECT_EQ(expected_distance_vector, dv_entry); + } + + { + const Function* f = spvtest::GetFunction(module, 14); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + const Instruction* store = nullptr; + const Instruction* load = nullptr; + + int block_id = 162; + ASSERT_TRUE(spvtest::GetBasicBlock(f, block_id)); + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, block_id)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + + if (inst.opcode() == SpvOp::SpvOpLoad) { + load = &inst; + } + } + + EXPECT_NE(nullptr, store); + EXPECT_NE(nullptr, load); + + std::vector loop_nest{&ld.GetLoopByIndex(0), + &ld.GetLoopByIndex(1)}; + LoopDependenceAnalysis analysis{context.get(), loop_nest}; + + DistanceVector dv_entry(loop_nest.size()); + auto is_independent = analysis.GetDependence(load, store, &dv_entry); + + EXPECT_TRUE(is_independent); + } +} + +TEST(DependencyAnalysis, ConstraintIntersection) { + LoopDependenceAnalysis analysis{nullptr, std::vector{}}; + auto scalar_evolution = analysis.GetScalarEvolution(); + { + // One is none. Other should be returned + auto none = analysis.make_constraint(); + auto x = scalar_evolution->CreateConstant(1); + auto y = scalar_evolution->CreateConstant(10); + auto point = analysis.make_constraint(x, y, nullptr); + + auto ret_0 = analysis.IntersectConstraints(none, point, nullptr, nullptr); + + auto ret_point_0 = ret_0->AsDependencePoint(); + ASSERT_NE(nullptr, ret_point_0); + EXPECT_EQ(*x, *ret_point_0->GetSource()); + EXPECT_EQ(*y, *ret_point_0->GetDestination()); + + auto ret_1 = analysis.IntersectConstraints(point, none, nullptr, nullptr); + + auto ret_point_1 = ret_1->AsDependencePoint(); + ASSERT_NE(nullptr, ret_point_1); + EXPECT_EQ(*x, *ret_point_1->GetSource()); + EXPECT_EQ(*y, *ret_point_1->GetDestination()); + } + + { + // Both distances + auto x = scalar_evolution->CreateConstant(1); + auto y = scalar_evolution->CreateConstant(10); + + auto distance_0 = analysis.make_constraint(x, nullptr); + auto distance_1 = analysis.make_constraint(y, nullptr); + + // Equal distances + auto ret_0 = + analysis.IntersectConstraints(distance_1, distance_1, nullptr, nullptr); + + auto ret_distance = ret_0->AsDependenceDistance(); + ASSERT_NE(nullptr, ret_distance); + EXPECT_EQ(*y, *ret_distance->GetDistance()); + + // Non-equal distances + auto ret_1 = + analysis.IntersectConstraints(distance_0, distance_1, nullptr, nullptr); + EXPECT_NE(nullptr, ret_1->AsDependenceEmpty()); + } + + { + // Both points + auto x = scalar_evolution->CreateConstant(1); + auto y = scalar_evolution->CreateConstant(10); + + auto point_0 = analysis.make_constraint(x, y, nullptr); + auto point_1 = analysis.make_constraint(x, y, nullptr); + auto point_2 = analysis.make_constraint(y, y, nullptr); + + // Equal points + auto ret_0 = + analysis.IntersectConstraints(point_0, point_1, nullptr, nullptr); + auto ret_point_0 = ret_0->AsDependencePoint(); + ASSERT_NE(nullptr, ret_point_0); + EXPECT_EQ(*x, *ret_point_0->GetSource()); + EXPECT_EQ(*y, *ret_point_0->GetDestination()); + + // Non-equal points + auto ret_1 = + analysis.IntersectConstraints(point_0, point_2, nullptr, nullptr); + EXPECT_NE(nullptr, ret_1->AsDependenceEmpty()); + } + + { + // Both lines, parallel + auto a0 = scalar_evolution->CreateConstant(3); + auto b0 = scalar_evolution->CreateConstant(6); + auto c0 = scalar_evolution->CreateConstant(9); + + auto a1 = scalar_evolution->CreateConstant(6); + auto b1 = scalar_evolution->CreateConstant(12); + auto c1 = scalar_evolution->CreateConstant(18); + + auto line_0 = analysis.make_constraint(a0, b0, c0, nullptr); + auto line_1 = analysis.make_constraint(a1, b1, c1, nullptr); + + // Same line, both ways + auto ret_0 = + analysis.IntersectConstraints(line_0, line_1, nullptr, nullptr); + auto ret_1 = + analysis.IntersectConstraints(line_1, line_0, nullptr, nullptr); + + auto ret_line_0 = ret_0->AsDependenceLine(); + auto ret_line_1 = ret_1->AsDependenceLine(); + + EXPECT_NE(nullptr, ret_line_0); + EXPECT_NE(nullptr, ret_line_1); + + // Non-intersecting parallel lines + auto c2 = scalar_evolution->CreateConstant(12); + auto line_2 = analysis.make_constraint(a1, b1, c2, nullptr); + + auto ret_2 = + analysis.IntersectConstraints(line_0, line_2, nullptr, nullptr); + auto ret_3 = + analysis.IntersectConstraints(line_2, line_0, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_2->AsDependenceEmpty()); + EXPECT_NE(nullptr, ret_3->AsDependenceEmpty()); + + auto c3 = scalar_evolution->CreateConstant(20); + auto line_3 = analysis.make_constraint(a1, b1, c3, nullptr); + + auto ret_4 = + analysis.IntersectConstraints(line_0, line_3, nullptr, nullptr); + auto ret_5 = + analysis.IntersectConstraints(line_3, line_0, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_4->AsDependenceEmpty()); + EXPECT_NE(nullptr, ret_5->AsDependenceEmpty()); + } + + { + // Non-constant line + auto unknown = scalar_evolution->CreateCantComputeNode(); + auto constant = scalar_evolution->CreateConstant(10); + + auto line_0 = analysis.make_constraint(constant, constant, + constant, nullptr); + auto line_1 = analysis.make_constraint(unknown, unknown, + unknown, nullptr); + + auto ret_0 = + analysis.IntersectConstraints(line_0, line_1, nullptr, nullptr); + auto ret_1 = + analysis.IntersectConstraints(line_1, line_0, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_0->AsDependenceNone()); + EXPECT_NE(nullptr, ret_1->AsDependenceNone()); + } + + { + auto bound_0 = scalar_evolution->CreateConstant(0); + auto bound_1 = scalar_evolution->CreateConstant(20); + + auto a0 = scalar_evolution->CreateConstant(1); + auto b0 = scalar_evolution->CreateConstant(2); + auto c0 = scalar_evolution->CreateConstant(6); + + auto a1 = scalar_evolution->CreateConstant(-1); + auto b1 = scalar_evolution->CreateConstant(2); + auto c1 = scalar_evolution->CreateConstant(2); + + auto line_0 = analysis.make_constraint(a0, b0, c0, nullptr); + auto line_1 = analysis.make_constraint(a1, b1, c1, nullptr); + + // Intersecting lines, has integer solution, in bounds + auto ret_0 = + analysis.IntersectConstraints(line_0, line_1, bound_0, bound_1); + auto ret_1 = + analysis.IntersectConstraints(line_1, line_0, bound_0, bound_1); + + auto ret_point_0 = ret_0->AsDependencePoint(); + auto ret_point_1 = ret_1->AsDependencePoint(); + + EXPECT_NE(nullptr, ret_point_0); + EXPECT_NE(nullptr, ret_point_1); + + auto const_2 = scalar_evolution->CreateConstant(2); + + EXPECT_EQ(*const_2, *ret_point_0->GetSource()); + EXPECT_EQ(*const_2, *ret_point_0->GetDestination()); + + EXPECT_EQ(*const_2, *ret_point_1->GetSource()); + EXPECT_EQ(*const_2, *ret_point_1->GetDestination()); + + // Intersecting lines, has integer solution, out of bounds + auto ret_2 = + analysis.IntersectConstraints(line_0, line_1, bound_0, bound_0); + auto ret_3 = + analysis.IntersectConstraints(line_1, line_0, bound_0, bound_0); + + EXPECT_NE(nullptr, ret_2->AsDependenceEmpty()); + EXPECT_NE(nullptr, ret_3->AsDependenceEmpty()); + + auto a2 = scalar_evolution->CreateConstant(-4); + auto b2 = scalar_evolution->CreateConstant(1); + auto c2 = scalar_evolution->CreateConstant(0); + + auto a3 = scalar_evolution->CreateConstant(4); + auto b3 = scalar_evolution->CreateConstant(1); + auto c3 = scalar_evolution->CreateConstant(4); + + auto line_2 = analysis.make_constraint(a2, b2, c2, nullptr); + auto line_3 = analysis.make_constraint(a3, b3, c3, nullptr); + + // Intersecting, no integer solution + auto ret_4 = + analysis.IntersectConstraints(line_2, line_3, bound_0, bound_1); + auto ret_5 = + analysis.IntersectConstraints(line_3, line_2, bound_0, bound_1); + + EXPECT_NE(nullptr, ret_4->AsDependenceEmpty()); + EXPECT_NE(nullptr, ret_5->AsDependenceEmpty()); + + auto unknown = scalar_evolution->CreateCantComputeNode(); + + // Non-constant bound + auto ret_6 = + analysis.IntersectConstraints(line_0, line_1, unknown, bound_1); + auto ret_7 = + analysis.IntersectConstraints(line_1, line_0, bound_0, unknown); + + EXPECT_NE(nullptr, ret_6->AsDependenceNone()); + EXPECT_NE(nullptr, ret_7->AsDependenceNone()); + } + + { + auto constant_0 = scalar_evolution->CreateConstant(0); + auto constant_1 = scalar_evolution->CreateConstant(1); + auto constant_neg_1 = scalar_evolution->CreateConstant(-1); + auto constant_2 = scalar_evolution->CreateConstant(2); + auto constant_neg_2 = scalar_evolution->CreateConstant(-2); + + auto point_0_0 = analysis.make_constraint( + constant_0, constant_0, nullptr); + auto point_0_1 = analysis.make_constraint( + constant_0, constant_1, nullptr); + auto point_1_0 = analysis.make_constraint( + constant_1, constant_0, nullptr); + auto point_1_1 = analysis.make_constraint( + constant_1, constant_1, nullptr); + auto point_1_2 = analysis.make_constraint( + constant_1, constant_2, nullptr); + auto point_1_neg_1 = analysis.make_constraint( + constant_1, constant_neg_1, nullptr); + auto point_neg_1_1 = analysis.make_constraint( + constant_neg_1, constant_1, nullptr); + + auto line_y_0 = analysis.make_constraint( + constant_0, constant_1, constant_0, nullptr); + auto line_y_1 = analysis.make_constraint( + constant_0, constant_1, constant_1, nullptr); + auto line_y_2 = analysis.make_constraint( + constant_0, constant_1, constant_2, nullptr); + + // Parallel horizontal lines, y = 0 & y = 1, should return no intersection + auto ret = + analysis.IntersectConstraints(line_y_0, line_y_1, nullptr, nullptr); + + EXPECT_NE(nullptr, ret->AsDependenceEmpty()); + + // Parallel horizontal lines, y = 1 & y = 2, should return no intersection + auto ret_y_12 = + analysis.IntersectConstraints(line_y_1, line_y_2, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_y_12->AsDependenceEmpty()); + + // Same horizontal lines, y = 0 & y = 0, should return the line + auto ret_y_same_0 = + analysis.IntersectConstraints(line_y_0, line_y_0, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_y_same_0->AsDependenceLine()); + + // Same horizontal lines, y = 1 & y = 1, should return the line + auto ret_y_same_1 = + analysis.IntersectConstraints(line_y_1, line_y_1, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_y_same_1->AsDependenceLine()); + + auto line_x_0 = analysis.make_constraint( + constant_1, constant_0, constant_0, nullptr); + auto line_x_1 = analysis.make_constraint( + constant_1, constant_0, constant_1, nullptr); + auto line_x_2 = analysis.make_constraint( + constant_1, constant_0, constant_2, nullptr); + auto line_2x_1 = analysis.make_constraint( + constant_2, constant_0, constant_1, nullptr); + auto line_2x_2 = analysis.make_constraint( + constant_2, constant_0, constant_2, nullptr); + + // Parallel vertical lines, x = 0 & x = 1, should return no intersection + auto ret_x = + analysis.IntersectConstraints(line_x_0, line_x_1, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_x->AsDependenceEmpty()); + + // Parallel vertical lines, x = 1 & x = 2, should return no intersection + auto ret_x_12 = + analysis.IntersectConstraints(line_x_1, line_x_2, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_x_12->AsDependenceEmpty()); + + // Parallel vertical lines, 2x = 1 & 2x = 2, should return no intersection + auto ret_2x_2_2x_1 = + analysis.IntersectConstraints(line_2x_2, line_2x_1, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_2x_2_2x_1->AsDependenceEmpty()); + + // same line, 2x=2 & x = 1 + auto ret_2x_2_x_1 = + analysis.IntersectConstraints(line_2x_2, line_x_1, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_2x_2_x_1->AsDependenceLine()); + + // Same vertical lines, x = 0 & x = 0, should return the line + auto ret_x_same_0 = + analysis.IntersectConstraints(line_x_0, line_x_0, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_x_same_0->AsDependenceLine()); + // EXPECT_EQ(*line_x_0, *ret_x_same_0->AsDependenceLine()); + + // Same vertical lines, x = 1 & x = 1, should return the line + auto ret_x_same_1 = + analysis.IntersectConstraints(line_x_1, line_x_1, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_x_same_1->AsDependenceLine()); + EXPECT_EQ(*line_x_1, *ret_x_same_1->AsDependenceLine()); + + // x=1 & y = 0, intersect at (1, 0) + auto ret_1_0 = analysis.IntersectConstraints(line_x_1, line_y_0, + constant_neg_1, constant_2); + + auto ret_point_1_0 = ret_1_0->AsDependencePoint(); + EXPECT_NE(nullptr, ret_point_1_0); + EXPECT_EQ(*point_1_0, *ret_point_1_0); + + // x=1 & y = 1, intersect at (1, 1) + auto ret_1_1 = analysis.IntersectConstraints(line_x_1, line_y_1, + constant_neg_1, constant_2); + + auto ret_point_1_1 = ret_1_1->AsDependencePoint(); + EXPECT_NE(nullptr, ret_point_1_1); + EXPECT_EQ(*point_1_1, *ret_point_1_1); + + // x=0 & y = 0, intersect at (0, 0) + auto ret_0_0 = analysis.IntersectConstraints(line_x_0, line_y_0, + constant_neg_1, constant_2); + + auto ret_point_0_0 = ret_0_0->AsDependencePoint(); + EXPECT_NE(nullptr, ret_point_0_0); + EXPECT_EQ(*point_0_0, *ret_point_0_0); + + // x=0 & y = 1, intersect at (0, 1) + auto ret_0_1 = analysis.IntersectConstraints(line_x_0, line_y_1, + constant_neg_1, constant_2); + auto ret_point_0_1 = ret_0_1->AsDependencePoint(); + EXPECT_NE(nullptr, ret_point_0_1); + EXPECT_EQ(*point_0_1, *ret_point_0_1); + + // x = 1 & y = 2 + auto ret_1_2 = analysis.IntersectConstraints(line_x_1, line_y_2, + constant_neg_1, constant_2); + auto ret_point_1_2 = ret_1_2->AsDependencePoint(); + EXPECT_NE(nullptr, ret_point_1_2); + EXPECT_EQ(*point_1_2, *ret_point_1_2); + + auto line_x_y_0 = analysis.make_constraint( + constant_1, constant_1, constant_0, nullptr); + auto line_x_y_1 = analysis.make_constraint( + constant_1, constant_1, constant_1, nullptr); + + // x+y=0 & x=0, intersect (0, 0) + auto ret_xy_0_x_0 = analysis.IntersectConstraints( + line_x_y_0, line_x_0, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_xy_0_x_0->AsDependencePoint()); + EXPECT_EQ(*point_0_0, *ret_xy_0_x_0); + + // x+y=0 & y=0, intersect (0, 0) + auto ret_xy_0_y_0 = analysis.IntersectConstraints( + line_x_y_0, line_y_0, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_xy_0_y_0->AsDependencePoint()); + EXPECT_EQ(*point_0_0, *ret_xy_0_y_0); + + // x+y=0 & x=1, intersect (1, -1) + auto ret_xy_0_x_1 = analysis.IntersectConstraints( + line_x_y_0, line_x_1, constant_neg_2, constant_2); + + EXPECT_NE(nullptr, ret_xy_0_x_1->AsDependencePoint()); + EXPECT_EQ(*point_1_neg_1, *ret_xy_0_x_1); + + // x+y=0 & y=1, intersect (-1, 1) + auto ret_xy_0_y_1 = analysis.IntersectConstraints( + line_x_y_0, line_y_1, constant_neg_2, constant_2); + + EXPECT_NE(nullptr, ret_xy_0_y_1->AsDependencePoint()); + EXPECT_EQ(*point_neg_1_1, *ret_xy_0_y_1); + + // x=0 & x+y=0, intersect (0, 0) + auto ret_x_0_xy_0 = analysis.IntersectConstraints( + line_x_0, line_x_y_0, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_x_0_xy_0->AsDependencePoint()); + EXPECT_EQ(*point_0_0, *ret_x_0_xy_0); + + // y=0 & x+y=0, intersect (0, 0) + auto ret_y_0_xy_0 = analysis.IntersectConstraints( + line_y_0, line_x_y_0, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_y_0_xy_0->AsDependencePoint()); + EXPECT_EQ(*point_0_0, *ret_y_0_xy_0); + + // x=1 & x+y=0, intersect (1, -1) + auto ret_x_1_xy_0 = analysis.IntersectConstraints( + line_x_1, line_x_y_0, constant_neg_2, constant_2); + + EXPECT_NE(nullptr, ret_x_1_xy_0->AsDependencePoint()); + EXPECT_EQ(*point_1_neg_1, *ret_x_1_xy_0); + + // y=1 & x+y=0, intersect (-1, 1) + auto ret_y_1_xy_0 = analysis.IntersectConstraints( + line_y_1, line_x_y_0, constant_neg_2, constant_2); + + EXPECT_NE(nullptr, ret_y_1_xy_0->AsDependencePoint()); + EXPECT_EQ(*point_neg_1_1, *ret_y_1_xy_0); + + // x+y=1 & x=0, intersect (0, 1) + auto ret_xy_1_x_0 = analysis.IntersectConstraints( + line_x_y_1, line_x_0, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_xy_1_x_0->AsDependencePoint()); + EXPECT_EQ(*point_0_1, *ret_xy_1_x_0); + + // x+y=1 & y=0, intersect (1, 0) + auto ret_xy_1_y_0 = analysis.IntersectConstraints( + line_x_y_1, line_y_0, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_xy_1_y_0->AsDependencePoint()); + EXPECT_EQ(*point_1_0, *ret_xy_1_y_0); + + // x+y=1 & x=1, intersect (1, 0) + auto ret_xy_1_x_1 = analysis.IntersectConstraints( + line_x_y_1, line_x_1, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_xy_1_x_1->AsDependencePoint()); + EXPECT_EQ(*point_1_0, *ret_xy_1_x_1); + + // x+y=1 & y=1, intersect (0, 1) + auto ret_xy_1_y_1 = analysis.IntersectConstraints( + line_x_y_1, line_y_1, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_xy_1_y_1->AsDependencePoint()); + EXPECT_EQ(*point_0_1, *ret_xy_1_y_1); + + // x=0 & x+y=1, intersect (0, 1) + auto ret_x_0_xy_1 = analysis.IntersectConstraints( + line_x_0, line_x_y_1, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_x_0_xy_1->AsDependencePoint()); + EXPECT_EQ(*point_0_1, *ret_x_0_xy_1); + + // y=0 & x+y=1, intersect (1, 0) + auto ret_y_0_xy_1 = analysis.IntersectConstraints( + line_y_0, line_x_y_1, constant_neg_1, constant_2); + + EXPECT_NE(nullptr, ret_y_0_xy_1->AsDependencePoint()); + EXPECT_EQ(*point_1_0, *ret_y_0_xy_1); + + // x=1 & x+y=1, intersect (1, 0) + auto ret_x_1_xy_1 = analysis.IntersectConstraints( + line_x_1, line_x_y_1, constant_neg_2, constant_2); + + EXPECT_NE(nullptr, ret_x_1_xy_1->AsDependencePoint()); + EXPECT_EQ(*point_1_0, *ret_x_1_xy_1); + + // y=1 & x+y=1, intersect (0, 1) + auto ret_y_1_xy_1 = analysis.IntersectConstraints( + line_y_1, line_x_y_1, constant_neg_2, constant_2); + + EXPECT_NE(nullptr, ret_y_1_xy_1->AsDependencePoint()); + EXPECT_EQ(*point_0_1, *ret_y_1_xy_1); + } + + { + // Line and point + auto a = scalar_evolution->CreateConstant(3); + auto b = scalar_evolution->CreateConstant(10); + auto c = scalar_evolution->CreateConstant(16); + + auto line = analysis.make_constraint(a, b, c, nullptr); + + // Point on line + auto x = scalar_evolution->CreateConstant(2); + auto y = scalar_evolution->CreateConstant(1); + auto point_0 = analysis.make_constraint(x, y, nullptr); + + auto ret_0 = analysis.IntersectConstraints(line, point_0, nullptr, nullptr); + auto ret_1 = analysis.IntersectConstraints(point_0, line, nullptr, nullptr); + + auto ret_point_0 = ret_0->AsDependencePoint(); + auto ret_point_1 = ret_1->AsDependencePoint(); + ASSERT_NE(nullptr, ret_point_0); + ASSERT_NE(nullptr, ret_point_1); + + EXPECT_EQ(*x, *ret_point_0->GetSource()); + EXPECT_EQ(*y, *ret_point_0->GetDestination()); + + EXPECT_EQ(*x, *ret_point_1->GetSource()); + EXPECT_EQ(*y, *ret_point_1->GetDestination()); + + // Point not on line + auto point_1 = analysis.make_constraint(a, a, nullptr); + + auto ret_2 = analysis.IntersectConstraints(line, point_1, nullptr, nullptr); + auto ret_3 = analysis.IntersectConstraints(point_1, line, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_2->AsDependenceEmpty()); + EXPECT_NE(nullptr, ret_3->AsDependenceEmpty()); + + // Non-constant + auto unknown = scalar_evolution->CreateCantComputeNode(); + + auto point_2 = + analysis.make_constraint(unknown, x, nullptr); + + auto ret_4 = analysis.IntersectConstraints(line, point_2, nullptr, nullptr); + auto ret_5 = analysis.IntersectConstraints(point_2, line, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_4->AsDependenceNone()); + EXPECT_NE(nullptr, ret_5->AsDependenceNone()); + } + + { + // Distance and point + auto d = scalar_evolution->CreateConstant(5); + auto distance = analysis.make_constraint(d, nullptr); + + // Point on line + auto x = scalar_evolution->CreateConstant(10); + auto point_0 = analysis.make_constraint(d, x, nullptr); + + auto ret_0 = + analysis.IntersectConstraints(distance, point_0, nullptr, nullptr); + auto ret_1 = + analysis.IntersectConstraints(point_0, distance, nullptr, nullptr); + + auto ret_point_0 = ret_0->AsDependencePoint(); + auto ret_point_1 = ret_1->AsDependencePoint(); + ASSERT_NE(nullptr, ret_point_0); + ASSERT_NE(nullptr, ret_point_1); + + // Point not on line + auto point_1 = analysis.make_constraint(x, x, nullptr); + + auto ret_2 = + analysis.IntersectConstraints(distance, point_1, nullptr, nullptr); + auto ret_3 = + analysis.IntersectConstraints(point_1, distance, nullptr, nullptr); + + EXPECT_NE(nullptr, ret_2->AsDependenceEmpty()); + EXPECT_NE(nullptr, ret_3->AsDependenceEmpty()); + + // Non-constant + auto unknown = scalar_evolution->CreateCantComputeNode(); + auto unknown_distance = + analysis.make_constraint(unknown, nullptr); + + auto ret_4 = analysis.IntersectConstraints(unknown_distance, point_1, + nullptr, nullptr); + auto ret_5 = analysis.IntersectConstraints(point_1, unknown_distance, + nullptr, nullptr); + + EXPECT_NE(nullptr, ret_4->AsDependenceNone()); + EXPECT_NE(nullptr, ret_5->AsDependenceNone()); + } +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/dependence_analysis_helpers.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/dependence_analysis_helpers.cpp new file mode 100644 index 0000000..715cf54 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/dependence_analysis_helpers.cpp @@ -0,0 +1,3017 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/iterator.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/tree_iterator.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using DependencyAnalysisHelpers = ::testing::Test; + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void a() { + int[10][10] arr; + int i = 0; + int j = 0; + for (; i < 10 && j < 10; i++, j++) { + arr[i][j] = arr[i][j]; + } +} +void b() { + int[10] arr; + for (int i = 0; i < 10; i+=2) { + arr[i] = arr[i]; + } +} +void main(){ + a(); + b(); +} +*/ +TEST(DependencyAnalysisHelpers, UnsupportedLoops) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %12 "i" + OpName %14 "j" + OpName %32 "arr" + OpName %45 "i" + OpName %54 "arr" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 0 + %21 = OpConstant %10 10 + %22 = OpTypeBool + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %10 %28 + %30 = OpTypeArray %29 %28 + %31 = OpTypePointer Function %30 + %41 = OpConstant %10 1 + %53 = OpTypePointer Function %29 + %60 = OpConstant %10 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %63 = OpFunctionCall %2 %6 + %64 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %12 = OpVariable %11 Function + %14 = OpVariable %11 Function + %32 = OpVariable %31 Function + OpStore %12 %13 + OpStore %14 %13 + OpBranch %15 + %15 = OpLabel + %65 = OpPhi %10 %13 %7 %42 %18 + %66 = OpPhi %10 %13 %7 %44 %18 + OpLoopMerge %17 %18 None + OpBranch %19 + %19 = OpLabel + %23 = OpSLessThan %22 %65 %21 + %25 = OpSLessThan %22 %66 %21 + %26 = OpLogicalAnd %22 %23 %25 + OpBranchConditional %26 %16 %17 + %16 = OpLabel + %37 = OpAccessChain %11 %32 %65 %66 + %38 = OpLoad %10 %37 + %39 = OpAccessChain %11 %32 %65 %66 + OpStore %39 %38 + OpBranch %18 + %18 = OpLabel + %42 = OpIAdd %10 %65 %41 + OpStore %12 %42 + %44 = OpIAdd %10 %66 %41 + OpStore %14 %44 + OpBranch %15 + %17 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %45 = OpVariable %11 Function + %54 = OpVariable %53 Function + OpStore %45 %13 + OpBranch %46 + %46 = OpLabel + %67 = OpPhi %10 %13 %9 %62 %49 + OpLoopMerge %48 %49 None + OpBranch %50 + %50 = OpLabel + %52 = OpSLessThan %22 %67 %21 + OpBranchConditional %52 %47 %48 + %47 = OpLabel + %57 = OpAccessChain %11 %54 %67 + %58 = OpLoad %10 %57 + %59 = OpAccessChain %11 %54 %67 + OpStore %59 %58 + OpBranch %49 + %49 = OpLabel + %62 = OpIAdd %10 %67 %60 + OpStore %45 %62 + OpBranch %46 + %48 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + { + // Function a + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[1] = {nullptr}; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 16)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + // 38 -> 39 + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.IsSupportedLoop(loops[0])); + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(38), + store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::UNKNOWN); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::ALL); + } + { + // Function b + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* store[1] = {nullptr}; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 47)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store[stores_found] = &inst; + ++stores_found; + } + } + // 58 -> 59 + DistanceVector distance_vector{loops.size()}; + EXPECT_FALSE(analysis.IsSupportedLoop(loops[0])); + EXPECT_FALSE(analysis.GetDependence(context->get_def_use_mgr()->GetDef(58), + store[0], &distance_vector)); + EXPECT_EQ(distance_vector.GetEntries()[0].dependence_information, + DistanceEntry::DependenceInformation::UNKNOWN); + EXPECT_EQ(distance_vector.GetEntries()[0].direction, + DistanceEntry::Directions::ALL); + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void a() { + for (int i = -10; i < 0; i++) { + + } +} +void b() { + for (int i = -5; i < 5; i++) { + + } +} +void c() { + for (int i = 0; i < 10; i++) { + + } +} +void d() { + for (int i = 5; i < 15; i++) { + + } +} +void e() { + for (int i = -10; i <= 0; i++) { + + } +} +void f() { + for (int i = -5; i <= 5; i++) { + + } +} +void g() { + for (int i = 0; i <= 10; i++) { + + } +} +void h() { + for (int i = 5; i <= 15; i++) { + + } +} +void i() { + for (int i = 0; i > -10; i--) { + + } +} +void j() { + for (int i = 5; i > -5; i--) { + + } +} +void k() { + for (int i = 10; i > 0; i--) { + + } +} +void l() { + for (int i = 15; i > 5; i--) { + + } +} +void m() { + for (int i = 0; i >= -10; i--) { + + } +} +void n() { + for (int i = 5; i >= -5; i--) { + + } +} +void o() { + for (int i = 10; i >= 0; i--) { + + } +} +void p() { + for (int i = 15; i >= 5; i--) { + + } +} +void main(){ + a(); + b(); + c(); + d(); + e(); + f(); + g(); + h(); + i(); + j(); + k(); + l(); + m(); + n(); + o(); + p(); +} +*/ +TEST(DependencyAnalysisHelpers, loop_information) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %10 "c(" + OpName %12 "d(" + OpName %14 "e(" + OpName %16 "f(" + OpName %18 "g(" + OpName %20 "h(" + OpName %22 "i(" + OpName %24 "j(" + OpName %26 "k(" + OpName %28 "l(" + OpName %30 "m(" + OpName %32 "n(" + OpName %34 "o(" + OpName %36 "p(" + OpName %40 "i" + OpName %54 "i" + OpName %66 "i" + OpName %77 "i" + OpName %88 "i" + OpName %98 "i" + OpName %108 "i" + OpName %118 "i" + OpName %128 "i" + OpName %138 "i" + OpName %148 "i" + OpName %158 "i" + OpName %168 "i" + OpName %178 "i" + OpName %188 "i" + OpName %198 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %38 = OpTypeInt 32 1 + %39 = OpTypePointer Function %38 + %41 = OpConstant %38 -10 + %48 = OpConstant %38 0 + %49 = OpTypeBool + %52 = OpConstant %38 1 + %55 = OpConstant %38 -5 + %62 = OpConstant %38 5 + %73 = OpConstant %38 10 + %84 = OpConstant %38 15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %208 = OpFunctionCall %2 %6 + %209 = OpFunctionCall %2 %8 + %210 = OpFunctionCall %2 %10 + %211 = OpFunctionCall %2 %12 + %212 = OpFunctionCall %2 %14 + %213 = OpFunctionCall %2 %16 + %214 = OpFunctionCall %2 %18 + %215 = OpFunctionCall %2 %20 + %216 = OpFunctionCall %2 %22 + %217 = OpFunctionCall %2 %24 + %218 = OpFunctionCall %2 %26 + %219 = OpFunctionCall %2 %28 + %220 = OpFunctionCall %2 %30 + %221 = OpFunctionCall %2 %32 + %222 = OpFunctionCall %2 %34 + %223 = OpFunctionCall %2 %36 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %40 = OpVariable %39 Function + OpStore %40 %41 + OpBranch %42 + %42 = OpLabel + %224 = OpPhi %38 %41 %7 %53 %45 + OpLoopMerge %44 %45 None + OpBranch %46 + %46 = OpLabel + %50 = OpSLessThan %49 %224 %48 + OpBranchConditional %50 %43 %44 + %43 = OpLabel + OpBranch %45 + %45 = OpLabel + %53 = OpIAdd %38 %224 %52 + OpStore %40 %53 + OpBranch %42 + %44 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %54 = OpVariable %39 Function + OpStore %54 %55 + OpBranch %56 + %56 = OpLabel + %225 = OpPhi %38 %55 %9 %65 %59 + OpLoopMerge %58 %59 None + OpBranch %60 + %60 = OpLabel + %63 = OpSLessThan %49 %225 %62 + OpBranchConditional %63 %57 %58 + %57 = OpLabel + OpBranch %59 + %59 = OpLabel + %65 = OpIAdd %38 %225 %52 + OpStore %54 %65 + OpBranch %56 + %58 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %66 = OpVariable %39 Function + OpStore %66 %48 + OpBranch %67 + %67 = OpLabel + %226 = OpPhi %38 %48 %11 %76 %70 + OpLoopMerge %69 %70 None + OpBranch %71 + %71 = OpLabel + %74 = OpSLessThan %49 %226 %73 + OpBranchConditional %74 %68 %69 + %68 = OpLabel + OpBranch %70 + %70 = OpLabel + %76 = OpIAdd %38 %226 %52 + OpStore %66 %76 + OpBranch %67 + %69 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %77 = OpVariable %39 Function + OpStore %77 %62 + OpBranch %78 + %78 = OpLabel + %227 = OpPhi %38 %62 %13 %87 %81 + OpLoopMerge %80 %81 None + OpBranch %82 + %82 = OpLabel + %85 = OpSLessThan %49 %227 %84 + OpBranchConditional %85 %79 %80 + %79 = OpLabel + OpBranch %81 + %81 = OpLabel + %87 = OpIAdd %38 %227 %52 + OpStore %77 %87 + OpBranch %78 + %80 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %88 = OpVariable %39 Function + OpStore %88 %41 + OpBranch %89 + %89 = OpLabel + %228 = OpPhi %38 %41 %15 %97 %92 + OpLoopMerge %91 %92 None + OpBranch %93 + %93 = OpLabel + %95 = OpSLessThanEqual %49 %228 %48 + OpBranchConditional %95 %90 %91 + %90 = OpLabel + OpBranch %92 + %92 = OpLabel + %97 = OpIAdd %38 %228 %52 + OpStore %88 %97 + OpBranch %89 + %91 = OpLabel + OpReturn + OpFunctionEnd + %16 = OpFunction %2 None %3 + %17 = OpLabel + %98 = OpVariable %39 Function + OpStore %98 %55 + OpBranch %99 + %99 = OpLabel + %229 = OpPhi %38 %55 %17 %107 %102 + OpLoopMerge %101 %102 None + OpBranch %103 + %103 = OpLabel + %105 = OpSLessThanEqual %49 %229 %62 + OpBranchConditional %105 %100 %101 + %100 = OpLabel + OpBranch %102 + %102 = OpLabel + %107 = OpIAdd %38 %229 %52 + OpStore %98 %107 + OpBranch %99 + %101 = OpLabel + OpReturn + OpFunctionEnd + %18 = OpFunction %2 None %3 + %19 = OpLabel + %108 = OpVariable %39 Function + OpStore %108 %48 + OpBranch %109 + %109 = OpLabel + %230 = OpPhi %38 %48 %19 %117 %112 + OpLoopMerge %111 %112 None + OpBranch %113 + %113 = OpLabel + %115 = OpSLessThanEqual %49 %230 %73 + OpBranchConditional %115 %110 %111 + %110 = OpLabel + OpBranch %112 + %112 = OpLabel + %117 = OpIAdd %38 %230 %52 + OpStore %108 %117 + OpBranch %109 + %111 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %3 + %21 = OpLabel + %118 = OpVariable %39 Function + OpStore %118 %62 + OpBranch %119 + %119 = OpLabel + %231 = OpPhi %38 %62 %21 %127 %122 + OpLoopMerge %121 %122 None + OpBranch %123 + %123 = OpLabel + %125 = OpSLessThanEqual %49 %231 %84 + OpBranchConditional %125 %120 %121 + %120 = OpLabel + OpBranch %122 + %122 = OpLabel + %127 = OpIAdd %38 %231 %52 + OpStore %118 %127 + OpBranch %119 + %121 = OpLabel + OpReturn + OpFunctionEnd + %22 = OpFunction %2 None %3 + %23 = OpLabel + %128 = OpVariable %39 Function + OpStore %128 %48 + OpBranch %129 + %129 = OpLabel + %232 = OpPhi %38 %48 %23 %137 %132 + OpLoopMerge %131 %132 None + OpBranch %133 + %133 = OpLabel + %135 = OpSGreaterThan %49 %232 %41 + OpBranchConditional %135 %130 %131 + %130 = OpLabel + OpBranch %132 + %132 = OpLabel + %137 = OpISub %38 %232 %52 + OpStore %128 %137 + OpBranch %129 + %131 = OpLabel + OpReturn + OpFunctionEnd + %24 = OpFunction %2 None %3 + %25 = OpLabel + %138 = OpVariable %39 Function + OpStore %138 %62 + OpBranch %139 + %139 = OpLabel + %233 = OpPhi %38 %62 %25 %147 %142 + OpLoopMerge %141 %142 None + OpBranch %143 + %143 = OpLabel + %145 = OpSGreaterThan %49 %233 %55 + OpBranchConditional %145 %140 %141 + %140 = OpLabel + OpBranch %142 + %142 = OpLabel + %147 = OpISub %38 %233 %52 + OpStore %138 %147 + OpBranch %139 + %141 = OpLabel + OpReturn + OpFunctionEnd + %26 = OpFunction %2 None %3 + %27 = OpLabel + %148 = OpVariable %39 Function + OpStore %148 %73 + OpBranch %149 + %149 = OpLabel + %234 = OpPhi %38 %73 %27 %157 %152 + OpLoopMerge %151 %152 None + OpBranch %153 + %153 = OpLabel + %155 = OpSGreaterThan %49 %234 %48 + OpBranchConditional %155 %150 %151 + %150 = OpLabel + OpBranch %152 + %152 = OpLabel + %157 = OpISub %38 %234 %52 + OpStore %148 %157 + OpBranch %149 + %151 = OpLabel + OpReturn + OpFunctionEnd + %28 = OpFunction %2 None %3 + %29 = OpLabel + %158 = OpVariable %39 Function + OpStore %158 %84 + OpBranch %159 + %159 = OpLabel + %235 = OpPhi %38 %84 %29 %167 %162 + OpLoopMerge %161 %162 None + OpBranch %163 + %163 = OpLabel + %165 = OpSGreaterThan %49 %235 %62 + OpBranchConditional %165 %160 %161 + %160 = OpLabel + OpBranch %162 + %162 = OpLabel + %167 = OpISub %38 %235 %52 + OpStore %158 %167 + OpBranch %159 + %161 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %3 + %31 = OpLabel + %168 = OpVariable %39 Function + OpStore %168 %48 + OpBranch %169 + %169 = OpLabel + %236 = OpPhi %38 %48 %31 %177 %172 + OpLoopMerge %171 %172 None + OpBranch %173 + %173 = OpLabel + %175 = OpSGreaterThanEqual %49 %236 %41 + OpBranchConditional %175 %170 %171 + %170 = OpLabel + OpBranch %172 + %172 = OpLabel + %177 = OpISub %38 %236 %52 + OpStore %168 %177 + OpBranch %169 + %171 = OpLabel + OpReturn + OpFunctionEnd + %32 = OpFunction %2 None %3 + %33 = OpLabel + %178 = OpVariable %39 Function + OpStore %178 %62 + OpBranch %179 + %179 = OpLabel + %237 = OpPhi %38 %62 %33 %187 %182 + OpLoopMerge %181 %182 None + OpBranch %183 + %183 = OpLabel + %185 = OpSGreaterThanEqual %49 %237 %55 + OpBranchConditional %185 %180 %181 + %180 = OpLabel + OpBranch %182 + %182 = OpLabel + %187 = OpISub %38 %237 %52 + OpStore %178 %187 + OpBranch %179 + %181 = OpLabel + OpReturn + OpFunctionEnd + %34 = OpFunction %2 None %3 + %35 = OpLabel + %188 = OpVariable %39 Function + OpStore %188 %73 + OpBranch %189 + %189 = OpLabel + %238 = OpPhi %38 %73 %35 %197 %192 + OpLoopMerge %191 %192 None + OpBranch %193 + %193 = OpLabel + %195 = OpSGreaterThanEqual %49 %238 %48 + OpBranchConditional %195 %190 %191 + %190 = OpLabel + OpBranch %192 + %192 = OpLabel + %197 = OpISub %38 %238 %52 + OpStore %188 %197 + OpBranch %189 + %191 = OpLabel + OpReturn + OpFunctionEnd + %36 = OpFunction %2 None %3 + %37 = OpLabel + %198 = OpVariable %39 Function + OpStore %198 %84 + OpBranch %199 + %199 = OpLabel + %239 = OpPhi %38 %84 %37 %207 %202 + OpLoopMerge %201 %202 None + OpBranch %203 + %203 = OpLabel + %205 = OpSGreaterThanEqual %49 %239 %62 + OpBranchConditional %205 %200 %201 + %200 = OpLabel + OpBranch %202 + %202 = OpLabel + %207 = OpISub %38 %239 %52 + OpStore %198 %207 + OpBranch %199 + %201 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + { + // Function a + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -10); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -1); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(-10)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(1)), + analysis.GetScalarEvolution()->CreateConstant(-1)); + } + { + // Function b + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -5); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 4); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(-5)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(1)), + analysis.GetScalarEvolution()->CreateConstant(4)); + } + { + // Function c + const Function* f = spvtest::GetFunction(module, 10); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 0); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 9); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(0)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(1)), + analysis.GetScalarEvolution()->CreateConstant(9)); + } + { + // Function d + const Function* f = spvtest::GetFunction(module, 12); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 5); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 14); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(5)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(1)), + analysis.GetScalarEvolution()->CreateConstant(14)); + } + { + // Function e + const Function* f = spvtest::GetFunction(module, 14); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -10); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 0); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 11); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(-10)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(1)), + analysis.GetScalarEvolution()->CreateConstant(0)); + } + { + // Function f + const Function* f = spvtest::GetFunction(module, 16); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -5); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 5); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 11); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(-5)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(1)), + analysis.GetScalarEvolution()->CreateConstant(5)); + } + { + // Function g + const Function* f = spvtest::GetFunction(module, 18); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 0); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 11); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(0)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(1)), + analysis.GetScalarEvolution()->CreateConstant(10)); + } + { + // Function h + const Function* f = spvtest::GetFunction(module, 20); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 5); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 15); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 11); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(5)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(1)), + analysis.GetScalarEvolution()->CreateConstant(15)); + } + { + // Function i + const Function* f = spvtest::GetFunction(module, 22); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 0); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -9); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(0)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(-1)), + analysis.GetScalarEvolution()->CreateConstant(-9)); + } + { + // Function j + const Function* f = spvtest::GetFunction(module, 24); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 5); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -4); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(5)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(-1)), + analysis.GetScalarEvolution()->CreateConstant(-4)); + } + { + // Function k + const Function* f = spvtest::GetFunction(module, 26); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 1); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(10)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(-1)), + analysis.GetScalarEvolution()->CreateConstant(1)); + } + { + // Function l + const Function* f = spvtest::GetFunction(module, 28); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 15); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 6); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(15)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(-1)), + analysis.GetScalarEvolution()->CreateConstant(6)); + } + { + // Function m + const Function* f = spvtest::GetFunction(module, 30); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 0); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -10); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 11); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(0)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(-1)), + analysis.GetScalarEvolution()->CreateConstant(-10)); + } + { + // Function n + const Function* f = spvtest::GetFunction(module, 32); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 5); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + -5); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 11); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(5)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(-1)), + analysis.GetScalarEvolution()->CreateConstant(-5)); + } + { + // Function o + const Function* f = spvtest::GetFunction(module, 34); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 10); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 0); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 11); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(10)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(-1)), + analysis.GetScalarEvolution()->CreateConstant(0)); + } + { + // Function p + const Function* f = spvtest::GetFunction(module, 36); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_EQ( + analysis.GetLowerBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 15); + EXPECT_EQ( + analysis.GetUpperBound(loop)->AsSEConstantNode()->FoldToSingleValue(), + 5); + + EXPECT_EQ( + analysis.GetTripCount(loop)->AsSEConstantNode()->FoldToSingleValue(), + 11); + + EXPECT_EQ(analysis.GetFirstTripInductionNode(loop), + analysis.GetScalarEvolution()->CreateConstant(15)); + + EXPECT_EQ(analysis.GetFinalTripInductionNode( + loop, analysis.GetScalarEvolution()->CreateConstant(-1)), + analysis.GetScalarEvolution()->CreateConstant(5)); + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +void main(){ + for (int i = 0; i < 10; i++) { + + } +} +*/ +TEST(DependencyAnalysisHelpers, bounds_checks) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %22 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %22 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %22 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + // We need a shader that includes a loop for this test so we can build a + // LoopDependenceAnalaysis + const Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + EXPECT_TRUE(analysis.IsWithinBounds(0, 0, 0)); + EXPECT_TRUE(analysis.IsWithinBounds(0, -1, 0)); + EXPECT_TRUE(analysis.IsWithinBounds(0, 0, 1)); + EXPECT_TRUE(analysis.IsWithinBounds(0, -1, 1)); + EXPECT_TRUE(analysis.IsWithinBounds(-2, -2, -2)); + EXPECT_TRUE(analysis.IsWithinBounds(-2, -3, 0)); + EXPECT_TRUE(analysis.IsWithinBounds(-2, 0, -3)); + EXPECT_TRUE(analysis.IsWithinBounds(2, 2, 2)); + EXPECT_TRUE(analysis.IsWithinBounds(2, 3, 0)); + + EXPECT_FALSE(analysis.IsWithinBounds(2, 3, 3)); + EXPECT_FALSE(analysis.IsWithinBounds(0, 1, 5)); + EXPECT_FALSE(analysis.IsWithinBounds(0, -1, -4)); + EXPECT_FALSE(analysis.IsWithinBounds(-2, -4, -3)); +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +layout(location = 0) in vec4 in_vec; +// Loop iterates from constant to symbolic +void a() { + int N = int(in_vec.x); + int arr[10]; + for (int i = 0; i < N; i++) { // Bounds are N - 0 - 1 + arr[i] = arr[i+N]; // |distance| = N + arr[i+N] = arr[i]; // |distance| = N + } +} +void b() { + int N = int(in_vec.x); + int arr[10]; + for (int i = 0; i <= N; i++) { // Bounds are N - 0 + arr[i] = arr[i+N]; // |distance| = N + arr[i+N] = arr[i]; // |distance| = N + } +} +void c() { + int N = int(in_vec.x); + int arr[10]; + for (int i = 9; i > N; i--) { // Bounds are 9 - N - 1 + arr[i] = arr[i+N]; // |distance| = N + arr[i+N] = arr[i]; // |distance| = N + } +} +void d() { + int N = int(in_vec.x); + int arr[10]; + for (int i = 9; i >= N; i--) { // Bounds are 9 - N + arr[i] = arr[i+N]; // |distance| = N + arr[i+N] = arr[i]; // |distance| = N + } +} +void main(){ + a(); + b(); + c(); + d(); +} +*/ +TEST(DependencyAnalysisHelpers, const_to_symbolic) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %10 "c(" + OpName %12 "d(" + OpName %16 "N" + OpName %20 "in_vec" + OpName %27 "i" + OpName %41 "arr" + OpName %59 "N" + OpName %63 "i" + OpName %72 "arr" + OpName %89 "N" + OpName %93 "i" + OpName %103 "arr" + OpName %120 "N" + OpName %124 "i" + OpName %133 "arr" + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %17 = OpTypeFloat 32 + %18 = OpTypeVector %17 4 + %19 = OpTypePointer Input %18 + %20 = OpVariable %19 Input + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 0 + %23 = OpTypePointer Input %17 + %28 = OpConstant %14 0 + %36 = OpTypeBool + %38 = OpConstant %21 10 + %39 = OpTypeArray %14 %38 + %40 = OpTypePointer Function %39 + %57 = OpConstant %14 1 + %94 = OpConstant %14 9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %150 = OpFunctionCall %2 %6 + %151 = OpFunctionCall %2 %8 + %152 = OpFunctionCall %2 %10 + %153 = OpFunctionCall %2 %12 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %16 = OpVariable %15 Function + %27 = OpVariable %15 Function + %41 = OpVariable %40 Function + %24 = OpAccessChain %23 %20 %22 + %25 = OpLoad %17 %24 + %26 = OpConvertFToS %14 %25 + OpStore %16 %26 + OpStore %27 %28 + OpBranch %29 + %29 = OpLabel + %154 = OpPhi %14 %28 %7 %58 %32 + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %37 = OpSLessThan %36 %154 %26 + OpBranchConditional %37 %30 %31 + %30 = OpLabel + %45 = OpIAdd %14 %154 %26 + %46 = OpAccessChain %15 %41 %45 + %47 = OpLoad %14 %46 + %48 = OpAccessChain %15 %41 %154 + OpStore %48 %47 + %51 = OpIAdd %14 %154 %26 + %53 = OpAccessChain %15 %41 %154 + %54 = OpLoad %14 %53 + %55 = OpAccessChain %15 %41 %51 + OpStore %55 %54 + OpBranch %32 + %32 = OpLabel + %58 = OpIAdd %14 %154 %57 + OpStore %27 %58 + OpBranch %29 + %31 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %59 = OpVariable %15 Function + %63 = OpVariable %15 Function + %72 = OpVariable %40 Function + %60 = OpAccessChain %23 %20 %22 + %61 = OpLoad %17 %60 + %62 = OpConvertFToS %14 %61 + OpStore %59 %62 + OpStore %63 %28 + OpBranch %64 + %64 = OpLabel + %155 = OpPhi %14 %28 %9 %88 %67 + OpLoopMerge %66 %67 None + OpBranch %68 + %68 = OpLabel + %71 = OpSLessThanEqual %36 %155 %62 + OpBranchConditional %71 %65 %66 + %65 = OpLabel + %76 = OpIAdd %14 %155 %62 + %77 = OpAccessChain %15 %72 %76 + %78 = OpLoad %14 %77 + %79 = OpAccessChain %15 %72 %155 + OpStore %79 %78 + %82 = OpIAdd %14 %155 %62 + %84 = OpAccessChain %15 %72 %155 + %85 = OpLoad %14 %84 + %86 = OpAccessChain %15 %72 %82 + OpStore %86 %85 + OpBranch %67 + %67 = OpLabel + %88 = OpIAdd %14 %155 %57 + OpStore %63 %88 + OpBranch %64 + %66 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %89 = OpVariable %15 Function + %93 = OpVariable %15 Function + %103 = OpVariable %40 Function + %90 = OpAccessChain %23 %20 %22 + %91 = OpLoad %17 %90 + %92 = OpConvertFToS %14 %91 + OpStore %89 %92 + OpStore %93 %94 + OpBranch %95 + %95 = OpLabel + %156 = OpPhi %14 %94 %11 %119 %98 + OpLoopMerge %97 %98 None + OpBranch %99 + %99 = OpLabel + %102 = OpSGreaterThan %36 %156 %92 + OpBranchConditional %102 %96 %97 + %96 = OpLabel + %107 = OpIAdd %14 %156 %92 + %108 = OpAccessChain %15 %103 %107 + %109 = OpLoad %14 %108 + %110 = OpAccessChain %15 %103 %156 + OpStore %110 %109 + %113 = OpIAdd %14 %156 %92 + %115 = OpAccessChain %15 %103 %156 + %116 = OpLoad %14 %115 + %117 = OpAccessChain %15 %103 %113 + OpStore %117 %116 + OpBranch %98 + %98 = OpLabel + %119 = OpISub %14 %156 %57 + OpStore %93 %119 + OpBranch %95 + %97 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %120 = OpVariable %15 Function + %124 = OpVariable %15 Function + %133 = OpVariable %40 Function + %121 = OpAccessChain %23 %20 %22 + %122 = OpLoad %17 %121 + %123 = OpConvertFToS %14 %122 + OpStore %120 %123 + OpStore %124 %94 + OpBranch %125 + %125 = OpLabel + %157 = OpPhi %14 %94 %13 %149 %128 + OpLoopMerge %127 %128 None + OpBranch %129 + %129 = OpLabel + %132 = OpSGreaterThanEqual %36 %157 %123 + OpBranchConditional %132 %126 %127 + %126 = OpLabel + %137 = OpIAdd %14 %157 %123 + %138 = OpAccessChain %15 %133 %137 + %139 = OpLoad %14 %138 + %140 = OpAccessChain %15 %133 %157 + OpStore %140 %139 + %143 = OpIAdd %14 %157 %123 + %145 = OpAccessChain %15 %133 %157 + %146 = OpLoad %14 %145 + %147 = OpAccessChain %15 %133 %143 + OpStore %147 %146 + OpBranch %128 + %128 = OpLabel + %149 = OpISub %14 %157 %57 + OpStore %124 %149 + OpBranch %125 + %127 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + { + // Function a + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 47 -> 48 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(47) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Independent and supported. + EXPECT_TRUE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 54 -> 55 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(54) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Independent but not supported. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function b + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 65)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 78 -> 79 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(78) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Dependent. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 85 -> 86 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(85) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Dependent. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function c + const Function* f = spvtest::GetFunction(module, 10); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 96)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 109 -> 110 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(109) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Independent but not supported. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 116 -> 117 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(116) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Independent but not supported. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function d + const Function* f = spvtest::GetFunction(module, 12); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 126)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 139 -> 140 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(139) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Dependent. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 146 -> 147 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(146) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Dependent. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +layout(location = 0) in vec4 in_vec; +// Loop iterates from symbolic to constant +void a() { + int N = int(in_vec.x); + int arr[10]; + for (int i = N; i < 9; i++) { // Bounds are 9 - N - 1 + arr[i] = arr[i+N]; // |distance| = N + arr[i+N] = arr[i]; // |distance| = N + } +} +void b() { + int N = int(in_vec.x); + int arr[10]; + for (int i = N; i <= 9; i++) { // Bounds are 9 - N + arr[i] = arr[i+N]; // |distance| = N + arr[i+N] = arr[i]; // |distance| = N + } +} +void c() { + int N = int(in_vec.x); + int arr[10]; + for (int i = N; i > 0; i--) { // Bounds are N - 0 - 1 + arr[i] = arr[i+N]; // |distance| = N + arr[i+N] = arr[i]; // |distance| = N + } +} +void d() { + int N = int(in_vec.x); + int arr[10]; + for (int i = N; i >= 0; i--) { // Bounds are N - 0 + arr[i] = arr[i+N]; // |distance| = N + arr[i+N] = arr[i]; // |distance| = N + } +} +void main(){ + a(); + b(); + c(); + d(); +} +*/ +TEST(DependencyAnalysisHelpers, symbolic_to_const) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %10 "c(" + OpName %12 "d(" + OpName %16 "N" + OpName %20 "in_vec" + OpName %27 "i" + OpName %41 "arr" + OpName %59 "N" + OpName %63 "i" + OpName %72 "arr" + OpName %89 "N" + OpName %93 "i" + OpName %103 "arr" + OpName %120 "N" + OpName %124 "i" + OpName %133 "arr" + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %17 = OpTypeFloat 32 + %18 = OpTypeVector %17 4 + %19 = OpTypePointer Input %18 + %20 = OpVariable %19 Input + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 0 + %23 = OpTypePointer Input %17 + %35 = OpConstant %14 9 + %36 = OpTypeBool + %38 = OpConstant %21 10 + %39 = OpTypeArray %14 %38 + %40 = OpTypePointer Function %39 + %57 = OpConstant %14 1 + %101 = OpConstant %14 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %150 = OpFunctionCall %2 %6 + %151 = OpFunctionCall %2 %8 + %152 = OpFunctionCall %2 %10 + %153 = OpFunctionCall %2 %12 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %16 = OpVariable %15 Function + %27 = OpVariable %15 Function + %41 = OpVariable %40 Function + %24 = OpAccessChain %23 %20 %22 + %25 = OpLoad %17 %24 + %26 = OpConvertFToS %14 %25 + OpStore %16 %26 + OpStore %27 %26 + OpBranch %29 + %29 = OpLabel + %154 = OpPhi %14 %26 %7 %58 %32 + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %37 = OpSLessThan %36 %154 %35 + OpBranchConditional %37 %30 %31 + %30 = OpLabel + %45 = OpIAdd %14 %154 %26 + %46 = OpAccessChain %15 %41 %45 + %47 = OpLoad %14 %46 + %48 = OpAccessChain %15 %41 %154 + OpStore %48 %47 + %51 = OpIAdd %14 %154 %26 + %53 = OpAccessChain %15 %41 %154 + %54 = OpLoad %14 %53 + %55 = OpAccessChain %15 %41 %51 + OpStore %55 %54 + OpBranch %32 + %32 = OpLabel + %58 = OpIAdd %14 %154 %57 + OpStore %27 %58 + OpBranch %29 + %31 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %59 = OpVariable %15 Function + %63 = OpVariable %15 Function + %72 = OpVariable %40 Function + %60 = OpAccessChain %23 %20 %22 + %61 = OpLoad %17 %60 + %62 = OpConvertFToS %14 %61 + OpStore %59 %62 + OpStore %63 %62 + OpBranch %65 + %65 = OpLabel + %155 = OpPhi %14 %62 %9 %88 %68 + OpLoopMerge %67 %68 None + OpBranch %69 + %69 = OpLabel + %71 = OpSLessThanEqual %36 %155 %35 + OpBranchConditional %71 %66 %67 + %66 = OpLabel + %76 = OpIAdd %14 %155 %62 + %77 = OpAccessChain %15 %72 %76 + %78 = OpLoad %14 %77 + %79 = OpAccessChain %15 %72 %155 + OpStore %79 %78 + %82 = OpIAdd %14 %155 %62 + %84 = OpAccessChain %15 %72 %155 + %85 = OpLoad %14 %84 + %86 = OpAccessChain %15 %72 %82 + OpStore %86 %85 + OpBranch %68 + %68 = OpLabel + %88 = OpIAdd %14 %155 %57 + OpStore %63 %88 + OpBranch %65 + %67 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %89 = OpVariable %15 Function + %93 = OpVariable %15 Function + %103 = OpVariable %40 Function + %90 = OpAccessChain %23 %20 %22 + %91 = OpLoad %17 %90 + %92 = OpConvertFToS %14 %91 + OpStore %89 %92 + OpStore %93 %92 + OpBranch %95 + %95 = OpLabel + %156 = OpPhi %14 %92 %11 %119 %98 + OpLoopMerge %97 %98 None + OpBranch %99 + %99 = OpLabel + %102 = OpSGreaterThan %36 %156 %101 + OpBranchConditional %102 %96 %97 + %96 = OpLabel + %107 = OpIAdd %14 %156 %92 + %108 = OpAccessChain %15 %103 %107 + %109 = OpLoad %14 %108 + %110 = OpAccessChain %15 %103 %156 + OpStore %110 %109 + %113 = OpIAdd %14 %156 %92 + %115 = OpAccessChain %15 %103 %156 + %116 = OpLoad %14 %115 + %117 = OpAccessChain %15 %103 %113 + OpStore %117 %116 + OpBranch %98 + %98 = OpLabel + %119 = OpISub %14 %156 %57 + OpStore %93 %119 + OpBranch %95 + %97 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %120 = OpVariable %15 Function + %124 = OpVariable %15 Function + %133 = OpVariable %40 Function + %121 = OpAccessChain %23 %20 %22 + %122 = OpLoad %17 %121 + %123 = OpConvertFToS %14 %122 + OpStore %120 %123 + OpStore %124 %123 + OpBranch %126 + %126 = OpLabel + %157 = OpPhi %14 %123 %13 %149 %129 + OpLoopMerge %128 %129 None + OpBranch %130 + %130 = OpLabel + %132 = OpSGreaterThanEqual %36 %157 %101 + OpBranchConditional %132 %127 %128 + %127 = OpLabel + %137 = OpIAdd %14 %157 %123 + %138 = OpAccessChain %15 %133 %137 + %139 = OpLoad %14 %138 + %140 = OpAccessChain %15 %133 %157 + OpStore %140 %139 + %143 = OpIAdd %14 %157 %123 + %145 = OpAccessChain %15 %133 %157 + %146 = OpLoad %14 %145 + %147 = OpAccessChain %15 %133 %143 + OpStore %147 %146 + OpBranch %129 + %129 = OpLabel + %149 = OpISub %14 %157 %57 + OpStore %124 %149 + OpBranch %126 + %128 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + { + // Function a + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 47 -> 48 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(47) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Independent but not supported. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 54 -> 55 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(54) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Independent but not supported. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function b + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 66)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 78 -> 79 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(78) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Dependent. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 85 -> 86 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(85) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Dependent. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function c + const Function* f = spvtest::GetFunction(module, 10); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 96)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 109 -> 110 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(109) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Independent and supported. + EXPECT_TRUE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 116 -> 117 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(116) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Independent but not supported. + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function d + const Function* f = spvtest::GetFunction(module, 12); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 127)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 139 -> 140 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(139) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Dependent + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 146 -> 147 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(146) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + // Dependent + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } +} + +/* + Generated from the following GLSL fragment shader + with --eliminate-local-multi-store +#version 440 core +layout(location = 0) in vec4 in_vec; +// Loop iterates from symbolic to symbolic +void a() { + int M = int(in_vec.x); + int N = int(in_vec.y); + int arr[10]; + for (int i = M; i < N; i++) { // Bounds are N - M - 1 + arr[i+M+N] = arr[i+M+2*N]; // |distance| = N + arr[i+M+2*N] = arr[i+M+N]; // |distance| = N + } +} +void b() { + int M = int(in_vec.x); + int N = int(in_vec.y); + int arr[10]; + for (int i = M; i <= N; i++) { // Bounds are N - M + arr[i+M+N] = arr[i+M+2*N]; // |distance| = N + arr[i+M+2*N] = arr[i+M+N]; // |distance| = N + } +} +void c() { + int M = int(in_vec.x); + int N = int(in_vec.y); + int arr[10]; + for (int i = M; i > N; i--) { // Bounds are M - N - 1 + arr[i+M+N] = arr[i+M+2*N]; // |distance| = N + arr[i+M+2*N] = arr[i+M+N]; // |distance| = N + } +} +void d() { + int M = int(in_vec.x); + int N = int(in_vec.y); + int arr[10]; + for (int i = M; i >= N; i--) { // Bounds are M - N + arr[i+M+N] = arr[i+M+2*N]; // |distance| = N + arr[i+M+2*N] = arr[i+M+N]; // |distance| = N + } +} +void main(){ + a(); + b(); + c(); + d(); +} +*/ +TEST(DependencyAnalysisHelpers, symbolic_to_symbolic) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %20 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %6 "a(" + OpName %8 "b(" + OpName %10 "c(" + OpName %12 "d(" + OpName %16 "M" + OpName %20 "in_vec" + OpName %27 "N" + OpName %32 "i" + OpName %46 "arr" + OpName %79 "M" + OpName %83 "N" + OpName %87 "i" + OpName %97 "arr" + OpName %128 "M" + OpName %132 "N" + OpName %136 "i" + OpName %146 "arr" + OpName %177 "M" + OpName %181 "N" + OpName %185 "i" + OpName %195 "arr" + OpDecorate %20 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %17 = OpTypeFloat 32 + %18 = OpTypeVector %17 4 + %19 = OpTypePointer Input %18 + %20 = OpVariable %19 Input + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 0 + %23 = OpTypePointer Input %17 + %28 = OpConstant %21 1 + %41 = OpTypeBool + %43 = OpConstant %21 10 + %44 = OpTypeArray %14 %43 + %45 = OpTypePointer Function %44 + %55 = OpConstant %14 2 + %77 = OpConstant %14 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %226 = OpFunctionCall %2 %6 + %227 = OpFunctionCall %2 %8 + %228 = OpFunctionCall %2 %10 + %229 = OpFunctionCall %2 %12 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %16 = OpVariable %15 Function + %27 = OpVariable %15 Function + %32 = OpVariable %15 Function + %46 = OpVariable %45 Function + %24 = OpAccessChain %23 %20 %22 + %25 = OpLoad %17 %24 + %26 = OpConvertFToS %14 %25 + OpStore %16 %26 + %29 = OpAccessChain %23 %20 %28 + %30 = OpLoad %17 %29 + %31 = OpConvertFToS %14 %30 + OpStore %27 %31 + OpStore %32 %26 + OpBranch %34 + %34 = OpLabel + %230 = OpPhi %14 %26 %7 %78 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %42 = OpSLessThan %41 %230 %31 + OpBranchConditional %42 %35 %36 + %35 = OpLabel + %49 = OpIAdd %14 %230 %26 + %51 = OpIAdd %14 %49 %31 + %54 = OpIAdd %14 %230 %26 + %57 = OpIMul %14 %55 %31 + %58 = OpIAdd %14 %54 %57 + %59 = OpAccessChain %15 %46 %58 + %60 = OpLoad %14 %59 + %61 = OpAccessChain %15 %46 %51 + OpStore %61 %60 + %64 = OpIAdd %14 %230 %26 + %66 = OpIMul %14 %55 %31 + %67 = OpIAdd %14 %64 %66 + %70 = OpIAdd %14 %230 %26 + %72 = OpIAdd %14 %70 %31 + %73 = OpAccessChain %15 %46 %72 + %74 = OpLoad %14 %73 + %75 = OpAccessChain %15 %46 %67 + OpStore %75 %74 + OpBranch %37 + %37 = OpLabel + %78 = OpIAdd %14 %230 %77 + OpStore %32 %78 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %79 = OpVariable %15 Function + %83 = OpVariable %15 Function + %87 = OpVariable %15 Function + %97 = OpVariable %45 Function + %80 = OpAccessChain %23 %20 %22 + %81 = OpLoad %17 %80 + %82 = OpConvertFToS %14 %81 + OpStore %79 %82 + %84 = OpAccessChain %23 %20 %28 + %85 = OpLoad %17 %84 + %86 = OpConvertFToS %14 %85 + OpStore %83 %86 + OpStore %87 %82 + OpBranch %89 + %89 = OpLabel + %231 = OpPhi %14 %82 %9 %127 %92 + OpLoopMerge %91 %92 None + OpBranch %93 + %93 = OpLabel + %96 = OpSLessThanEqual %41 %231 %86 + OpBranchConditional %96 %90 %91 + %90 = OpLabel + %100 = OpIAdd %14 %231 %82 + %102 = OpIAdd %14 %100 %86 + %105 = OpIAdd %14 %231 %82 + %107 = OpIMul %14 %55 %86 + %108 = OpIAdd %14 %105 %107 + %109 = OpAccessChain %15 %97 %108 + %110 = OpLoad %14 %109 + %111 = OpAccessChain %15 %97 %102 + OpStore %111 %110 + %114 = OpIAdd %14 %231 %82 + %116 = OpIMul %14 %55 %86 + %117 = OpIAdd %14 %114 %116 + %120 = OpIAdd %14 %231 %82 + %122 = OpIAdd %14 %120 %86 + %123 = OpAccessChain %15 %97 %122 + %124 = OpLoad %14 %123 + %125 = OpAccessChain %15 %97 %117 + OpStore %125 %124 + OpBranch %92 + %92 = OpLabel + %127 = OpIAdd %14 %231 %77 + OpStore %87 %127 + OpBranch %89 + %91 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + %128 = OpVariable %15 Function + %132 = OpVariable %15 Function + %136 = OpVariable %15 Function + %146 = OpVariable %45 Function + %129 = OpAccessChain %23 %20 %22 + %130 = OpLoad %17 %129 + %131 = OpConvertFToS %14 %130 + OpStore %128 %131 + %133 = OpAccessChain %23 %20 %28 + %134 = OpLoad %17 %133 + %135 = OpConvertFToS %14 %134 + OpStore %132 %135 + OpStore %136 %131 + OpBranch %138 + %138 = OpLabel + %232 = OpPhi %14 %131 %11 %176 %141 + OpLoopMerge %140 %141 None + OpBranch %142 + %142 = OpLabel + %145 = OpSGreaterThan %41 %232 %135 + OpBranchConditional %145 %139 %140 + %139 = OpLabel + %149 = OpIAdd %14 %232 %131 + %151 = OpIAdd %14 %149 %135 + %154 = OpIAdd %14 %232 %131 + %156 = OpIMul %14 %55 %135 + %157 = OpIAdd %14 %154 %156 + %158 = OpAccessChain %15 %146 %157 + %159 = OpLoad %14 %158 + %160 = OpAccessChain %15 %146 %151 + OpStore %160 %159 + %163 = OpIAdd %14 %232 %131 + %165 = OpIMul %14 %55 %135 + %166 = OpIAdd %14 %163 %165 + %169 = OpIAdd %14 %232 %131 + %171 = OpIAdd %14 %169 %135 + %172 = OpAccessChain %15 %146 %171 + %173 = OpLoad %14 %172 + %174 = OpAccessChain %15 %146 %166 + OpStore %174 %173 + OpBranch %141 + %141 = OpLabel + %176 = OpISub %14 %232 %77 + OpStore %136 %176 + OpBranch %138 + %140 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + %177 = OpVariable %15 Function + %181 = OpVariable %15 Function + %185 = OpVariable %15 Function + %195 = OpVariable %45 Function + %178 = OpAccessChain %23 %20 %22 + %179 = OpLoad %17 %178 + %180 = OpConvertFToS %14 %179 + OpStore %177 %180 + %182 = OpAccessChain %23 %20 %28 + %183 = OpLoad %17 %182 + %184 = OpConvertFToS %14 %183 + OpStore %181 %184 + OpStore %185 %180 + OpBranch %187 + %187 = OpLabel + %233 = OpPhi %14 %180 %13 %225 %190 + OpLoopMerge %189 %190 None + OpBranch %191 + %191 = OpLabel + %194 = OpSGreaterThanEqual %41 %233 %184 + OpBranchConditional %194 %188 %189 + %188 = OpLabel + %198 = OpIAdd %14 %233 %180 + %200 = OpIAdd %14 %198 %184 + %203 = OpIAdd %14 %233 %180 + %205 = OpIMul %14 %55 %184 + %206 = OpIAdd %14 %203 %205 + %207 = OpAccessChain %15 %195 %206 + %208 = OpLoad %14 %207 + %209 = OpAccessChain %15 %195 %200 + OpStore %209 %208 + %212 = OpIAdd %14 %233 %180 + %214 = OpIMul %14 %55 %184 + %215 = OpIAdd %14 %212 %214 + %218 = OpIAdd %14 %233 %180 + %220 = OpIAdd %14 %218 %184 + %221 = OpAccessChain %15 %195 %220 + %222 = OpLoad %14 %221 + %223 = OpAccessChain %15 %195 %215 + OpStore %223 %222 + OpBranch %190 + %190 = OpLabel + %225 = OpISub %14 %233 %77 + OpStore %185 %225 + OpBranch %187 + %189 = OpLabel + OpReturn + OpFunctionEnd +)"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + { + // Function a + const Function* f = spvtest::GetFunction(module, 6); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 35)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 60 -> 61 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(60) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 74 -> 75 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(74) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function b + const Function* f = spvtest::GetFunction(module, 8); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 90)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 110 -> 111 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(110) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 124 -> 125 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(124) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function c + const Function* f = spvtest::GetFunction(module, 10); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 139)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 159 -> 160 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(159) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 173 -> 174 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(173) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } + { + // Function d + const Function* f = spvtest::GetFunction(module, 12); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + Loop* loop = &ld.GetLoopByIndex(0); + std::vector loops{loop}; + LoopDependenceAnalysis analysis{context.get(), loops}; + + const Instruction* stores[2]; + int stores_found = 0; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 188)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[stores_found] = &inst; + ++stores_found; + } + } + + for (int i = 0; i < 2; ++i) { + EXPECT_TRUE(stores[i]); + } + + // 208 -> 209 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(208) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[0]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + + // 222 -> 223 + { + // Analyse and simplify the instruction behind the access chain of this + // load. + Instruction* load_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(222) + ->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* load = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(load_var)); + + // Analyse and simplify the instruction behind the access chain of this + // store. + Instruction* store_var = context->get_def_use_mgr()->GetDef( + context->get_def_use_mgr() + ->GetDef(stores[1]->GetSingleWordInOperand(0)) + ->GetSingleWordInOperand(1)); + SENode* store = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->AnalyzeInstruction(store_var)); + + SENode* delta = analysis.GetScalarEvolution()->SimplifyExpression( + analysis.GetScalarEvolution()->CreateSubtraction(load, store)); + + EXPECT_FALSE(analysis.IsProvablyOutsideOfLoopBounds( + loop, delta, store->AsSERecurrentNode()->GetCoefficient())); + } + } +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/fusion_compatibility.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/fusion_compatibility.cpp new file mode 100644 index 0000000..cda8576 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/fusion_compatibility.cpp @@ -0,0 +1,1785 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_fusion.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using FusionCompatibilityTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int i = 0; // Can't fuse, i=0 in first & i=10 in second + for (; i < 10; i++) {} + for (; i < 10; i++) {} +} +*/ +TEST_F(FusionCompatibilityTest, SameInductionVariableDifferentBounds) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %31 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %31 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %31 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpBranch %22 + %22 = OpLabel + %32 = OpPhi %6 %31 %12 %30 %25 + OpLoopMerge %24 %25 None + OpBranch %26 + %26 = OpLabel + %28 = OpSLessThan %17 %32 %16 + OpBranchConditional %28 %23 %24 + %23 = OpLabel + OpBranch %25 + %25 = OpLabel + %30 = OpIAdd %6 %32 %20 + OpStore %8 %30 + OpBranch %22 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_FALSE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 1 +#version 440 core +void main() { + for (int i = 0; i < 10; i++) {} + for (int i = 0; i < 10; i++) {} +} +*/ +TEST_F(FusionCompatibilityTest, Compatible) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %22 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %32 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %32 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %32 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %33 = OpPhi %6 %9 %12 %31 %26 + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %33 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %31 = OpIAdd %6 %33 %20 + OpStore %22 %31 + OpBranch %23 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 2 +#version 440 core +void main() { + for (int i = 0; i < 10; i++) {} + for (int j = 0; j < 10; j++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, DifferentName) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %22 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %32 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %32 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %32 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %33 = OpPhi %6 %9 %12 %31 %26 + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %33 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %31 = OpIAdd %6 %33 %20 + OpStore %22 %31 + OpBranch %23 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + // Can't fuse, different step + for (int i = 0; i < 10; i++) {} + for (int j = 0; j < 10; j=j+2) {} +} + +*/ +TEST_F(FusionCompatibilityTest, SameBoundsDifferentStep) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %22 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %31 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %33 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %33 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %33 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %34 = OpPhi %6 %9 %12 %32 %26 + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %34 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %32 = OpIAdd %6 %34 %31 + OpStore %22 %32 + OpBranch %23 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_FALSE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 4 +#version 440 core +void main() { + // Can't fuse, different upper bound + for (int i = 0; i < 10; i++) {} + for (int j = 0; j < 20; j++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, DifferentUpperBound) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %22 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %29 = OpConstant %6 20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %33 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %33 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %33 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %34 = OpPhi %6 %9 %12 %32 %26 + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %30 = OpSLessThan %17 %34 %29 + OpBranchConditional %30 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %32 = OpIAdd %6 %34 %20 + OpStore %22 %32 + OpBranch %23 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_FALSE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 5 +#version 440 core +void main() { + // Can't fuse, different lower bound + for (int i = 5; i < 10; i++) {} + for (int j = 0; j < 10; j++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, DifferentLowerBound) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %22 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 5 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %23 = OpConstant %6 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %33 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %33 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %33 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %23 + OpBranch %24 + %24 = OpLabel + %34 = OpPhi %6 %23 %12 %32 %27 + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %30 = OpSLessThan %17 %34 %16 + OpBranchConditional %30 %25 %26 + %25 = OpLabel + OpBranch %27 + %27 = OpLabel + %32 = OpIAdd %6 %34 %20 + OpStore %22 %32 + OpBranch %24 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_FALSE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 6 +#version 440 core +void main() { + // Can't fuse, break in first loop + for (int i = 0; i < 10; i++) { + if (i == 5) { + break; + } + } + for (int j = 0; j < 10; j++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, Break) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %28 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 5 + %26 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %28 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %38 = OpPhi %6 %9 %5 %27 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %38 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %21 = OpIEqual %17 %38 %20 + OpSelectionMerge %23 None + OpBranchConditional %21 %22 %23 + %22 = OpLabel + OpBranch %12 + %23 = OpLabel + OpBranch %13 + %13 = OpLabel + %27 = OpIAdd %6 %38 %26 + OpStore %8 %27 + OpBranch %10 + %12 = OpLabel + OpStore %28 %9 + OpBranch %29 + %29 = OpLabel + %39 = OpPhi %6 %9 %12 %37 %32 + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %35 = OpSLessThan %17 %39 %16 + OpBranchConditional %35 %30 %31 + %30 = OpLabel + OpBranch %32 + %32 = OpLabel + %37 = OpIAdd %6 %39 %26 + OpStore %28 %37 + OpBranch %29 + %31 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_FALSE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +layout(location = 0) in vec4 c; +void main() { + int N = int(c.x); + for (int i = 0; i < N; i++) {} + for (int j = 0; j < N; j++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, UnknownButSameUpperBound) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "N" + OpName %12 "c" + OpName %19 "i" + OpName %33 "j" + OpDecorate %12 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeFloat 32 + %10 = OpTypeVector %9 4 + %11 = OpTypePointer Input %10 + %12 = OpVariable %11 Input + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Input %9 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %31 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %33 = OpVariable %7 Function + %16 = OpAccessChain %15 %12 %14 + %17 = OpLoad %9 %16 + %18 = OpConvertFToS %6 %17 + OpStore %8 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + %44 = OpPhi %6 %20 %5 %32 %24 + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %29 = OpSLessThan %28 %44 %18 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + OpBranch %24 + %24 = OpLabel + %32 = OpIAdd %6 %44 %31 + OpStore %19 %32 + OpBranch %21 + %23 = OpLabel + OpStore %33 %20 + OpBranch %34 + %34 = OpLabel + %46 = OpPhi %6 %20 %23 %43 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %41 = OpSLessThan %28 %46 %18 + OpBranchConditional %41 %35 %36 + %35 = OpLabel + OpBranch %37 + %37 = OpLabel + %43 = OpIAdd %6 %46 %31 + OpStore %33 %43 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +layout(location = 0) in vec4 c; +void main() { + int N = int(c.x); + for (int i = 0; N > j; i++) {} + for (int j = 0; N > j; j++) {} +} +*/ +TEST_F(FusionCompatibilityTest, UnknownButSameUpperBoundReverseCondition) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "N" + OpName %12 "c" + OpName %19 "i" + OpName %33 "j" + OpDecorate %12 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeFloat 32 + %10 = OpTypeVector %9 4 + %11 = OpTypePointer Input %10 + %12 = OpVariable %11 Input + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Input %9 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %31 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %33 = OpVariable %7 Function + %16 = OpAccessChain %15 %12 %14 + %17 = OpLoad %9 %16 + %18 = OpConvertFToS %6 %17 + OpStore %8 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + %45 = OpPhi %6 %20 %5 %32 %24 + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %29 = OpSGreaterThan %28 %18 %45 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + OpBranch %24 + %24 = OpLabel + %32 = OpIAdd %6 %45 %31 + OpStore %19 %32 + OpBranch %21 + %23 = OpLabel + OpStore %33 %20 + OpBranch %34 + %34 = OpLabel + %47 = OpPhi %6 %20 %23 %43 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %41 = OpSGreaterThan %28 %18 %47 + OpBranchConditional %41 %35 %36 + %35 = OpLabel + OpBranch %37 + %37 = OpLabel + %43 = OpIAdd %6 %47 %31 + OpStore %33 %43 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +layout(location = 0) in vec4 c; +void main() { + // Can't fuse different bound + int N = int(c.x); + for (int i = 0; i < N; i++) {} + for (int j = 0; j < N+1; j++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, UnknownUpperBoundAddition) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "N" + OpName %12 "c" + OpName %19 "i" + OpName %33 "j" + OpDecorate %12 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeFloat 32 + %10 = OpTypeVector %9 4 + %11 = OpTypePointer Input %10 + %12 = OpVariable %11 Input + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Input %9 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %31 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %33 = OpVariable %7 Function + %16 = OpAccessChain %15 %12 %14 + %17 = OpLoad %9 %16 + %18 = OpConvertFToS %6 %17 + OpStore %8 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + %45 = OpPhi %6 %20 %5 %32 %24 + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %29 = OpSLessThan %28 %45 %18 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + OpBranch %24 + %24 = OpLabel + %32 = OpIAdd %6 %45 %31 + OpStore %19 %32 + OpBranch %21 + %23 = OpLabel + OpStore %33 %20 + OpBranch %34 + %34 = OpLabel + %47 = OpPhi %6 %20 %23 %44 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %41 = OpIAdd %6 %18 %31 + %42 = OpSLessThan %28 %47 %41 + OpBranchConditional %42 %35 %36 + %35 = OpLabel + OpBranch %37 + %37 = OpLabel + %44 = OpIAdd %6 %47 %31 + OpStore %33 %44 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_FALSE(fusion.AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 10 +#version 440 core +void main() { + for (int i = 0; i < 10; i++) {} + for (int j = 0; j < 10; j++) {} + for (int k = 0; k < 10; k++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, SeveralAdjacentLoops) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %22 "j" + OpName %32 "k" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + %32 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %42 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %42 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %42 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %43 = OpPhi %6 %9 %12 %31 %26 + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %43 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %31 = OpIAdd %6 %43 %20 + OpStore %22 %31 + OpBranch %23 + %25 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + %44 = OpPhi %6 %9 %25 %41 %36 + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %39 = OpSLessThan %17 %44 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpBranch %36 + %36 = OpLabel + %41 = OpIAdd %6 %44 %20 + OpStore %32 %41 + OpBranch %33 + %35 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + + EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_0).AreCompatible()); + EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_2).AreCompatible()); + EXPECT_FALSE(LoopFusion(context.get(), loop_1, loop_0).AreCompatible()); + EXPECT_TRUE(LoopFusion(context.get(), loop_0, loop_1).AreCompatible()); + EXPECT_TRUE(LoopFusion(context.get(), loop_1, loop_2).AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + // Can't fuse, not adjacent + int x = 0; + for (int i = 0; i < 10; i++) { + if (i > 10) { + x++; + } + } + x++; + for (int j = 0; j < 10; j++) {} + for (int k = 0; k < 10; k++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, NonAdjacentLoops) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "x" + OpName %10 "i" + OpName %31 "j" + OpName %41 "k" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %25 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %31 = OpVariable %7 Function + %41 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + %52 = OpPhi %6 %9 %5 %56 %14 + %51 = OpPhi %6 %9 %5 %28 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %18 %51 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %21 = OpSGreaterThan %18 %52 %17 + OpSelectionMerge %23 None + OpBranchConditional %21 %22 %23 + %22 = OpLabel + %26 = OpIAdd %6 %52 %25 + OpStore %8 %26 + OpBranch %23 + %23 = OpLabel + %56 = OpPhi %6 %52 %12 %26 %22 + OpBranch %14 + %14 = OpLabel + %28 = OpIAdd %6 %51 %25 + OpStore %10 %28 + OpBranch %11 + %13 = OpLabel + %30 = OpIAdd %6 %52 %25 + OpStore %8 %30 + OpStore %31 %9 + OpBranch %32 + %32 = OpLabel + %53 = OpPhi %6 %9 %13 %40 %35 + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel + %38 = OpSLessThan %18 %53 %17 + OpBranchConditional %38 %33 %34 + %33 = OpLabel + OpBranch %35 + %35 = OpLabel + %40 = OpIAdd %6 %53 %25 + OpStore %31 %40 + OpBranch %32 + %34 = OpLabel + OpStore %41 %9 + OpBranch %42 + %42 = OpLabel + %54 = OpPhi %6 %9 %34 %50 %45 + OpLoopMerge %44 %45 None + OpBranch %46 + %46 = OpLabel + %48 = OpSLessThan %18 %54 %17 + OpBranchConditional %48 %43 %44 + %43 = OpLabel + OpBranch %45 + %45 = OpLabel + %50 = OpIAdd %6 %54 %25 + OpStore %41 %50 + OpBranch %42 + %44 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + + EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_0).AreCompatible()); + EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_2).AreCompatible()); + EXPECT_FALSE(LoopFusion(context.get(), loop_0, loop_1).AreCompatible()); + EXPECT_TRUE(LoopFusion(context.get(), loop_1, loop_2).AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 12 +#version 440 core +void main() { + int j = 0; + int i = 0; + for (; i < 10; i++) {} + for (; j < 10; j++) {} +} + +*/ +TEST_F(FusionCompatibilityTest, CompatibleInitDeclaredBeforeLoops) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "j" + OpName %10 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %21 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + %32 = OpPhi %6 %9 %5 %22 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %18 %32 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + OpBranch %14 + %14 = OpLabel + %22 = OpIAdd %6 %32 %21 + OpStore %10 %22 + OpBranch %11 + %13 = OpLabel + OpBranch %23 + %23 = OpLabel + %33 = OpPhi %6 %9 %13 %31 %26 + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %18 %33 %17 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %31 = OpIAdd %6 %33 %21 + OpStore %8 %31 + OpBranch %23 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + EXPECT_TRUE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 13 regenerate! +#version 440 core +void main() { + int[10] a; + int[10] b; + // Can't fuse, several induction variables + for (int j = 0; j < 10; j++) { + b[i] = a[i]; + } + for (int i = 0, j = 0; i < 10; i++, j = j+2) { + } +} + +*/ +TEST_F(FusionCompatibilityTest, SeveralInductionVariables) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "j" + OpName %23 "b" + OpName %25 "a" + OpName %33 "i" + OpName %34 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %31 = OpConstant %6 1 + %48 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %33 = OpVariable %7 Function + %34 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %50 = OpPhi %6 %9 %5 %32 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %50 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %50 + %28 = OpLoad %6 %27 + %29 = OpAccessChain %7 %23 %50 + OpStore %29 %28 + OpBranch %13 + %13 = OpLabel + %32 = OpIAdd %6 %50 %31 + OpStore %8 %32 + OpBranch %10 + %12 = OpLabel + OpStore %33 %9 + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %52 = OpPhi %6 %9 %12 %49 %38 + %51 = OpPhi %6 %9 %12 %46 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %51 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %44 = OpAccessChain %7 %25 %52 + OpStore %44 %51 + OpBranch %38 + %38 = OpLabel + %46 = OpIAdd %6 %51 %31 + OpStore %33 %46 + %49 = OpIAdd %6 %52 %48 + OpStore %34 %49 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + EXPECT_FALSE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 14 +#version 440 core +void main() { + // Fine + for (int i = 0; i < 10; i = i + 2) {} + for (int j = 0; j < 10; j = j + 2) {} +} + +*/ +TEST_F(FusionCompatibilityTest, CompatibleNonIncrementStep) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "j" + OpName %10 "i" + OpName %11 "i" + OpName %24 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %22 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %11 = OpVariable %7 Function + %24 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpStore %11 %9 + OpBranch %12 + %12 = OpLabel + %34 = OpPhi %6 %9 %5 %23 %15 + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %20 = OpSLessThan %19 %34 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + OpBranch %15 + %15 = OpLabel + %23 = OpIAdd %6 %34 %22 + OpStore %11 %23 + OpBranch %12 + %14 = OpLabel + OpStore %24 %9 + OpBranch %25 + %25 = OpLabel + %35 = OpPhi %6 %9 %14 %33 %28 + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + %31 = OpSLessThan %19 %35 %18 + OpBranchConditional %31 %26 %27 + %26 = OpLabel + OpBranch %28 + %28 = OpLabel + %33 = OpIAdd %6 %35 %22 + OpStore %24 %33 + OpBranch %25 + %27 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + EXPECT_TRUE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 15 +#version 440 core + +int j = 0; + +void main() { + // Not compatible, unknown init for second. + for (int i = 0; i < 10; i = i + 2) {} + for (; j < 10; j = j + 2) {} +} + +*/ +TEST_F(FusionCompatibilityTest, UnknonInitForSecondLoop) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "j" + OpName %11 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Private %6 + %8 = OpVariable %7 Private + %9 = OpConstant %6 0 + %10 = OpTypePointer Function %6 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %22 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + OpStore %8 %9 + OpStore %11 %9 + OpBranch %12 + %12 = OpLabel + %33 = OpPhi %6 %9 %5 %23 %15 + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %20 = OpSLessThan %19 %33 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + OpBranch %15 + %15 = OpLabel + %23 = OpIAdd %6 %33 %22 + OpStore %11 %23 + OpBranch %12 + %14 = OpLabel + OpBranch %24 + %24 = OpLabel + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %29 = OpLoad %6 %8 + %30 = OpSLessThan %19 %29 %18 + OpBranchConditional %30 %25 %26 + %25 = OpLabel + OpBranch %27 + %27 = OpLabel + %31 = OpLoad %6 %8 + %32 = OpIAdd %6 %31 %22 + OpStore %8 %32 + OpBranch %24 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + EXPECT_FALSE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 16 +#version 440 core +void main() { + // Not compatible, continue in loop 0 + for (int i = 0; i < 10; ++i) { + if (i % 2 == 1) { + continue; + } + } + for (int j = 0; j < 10; ++j) {} +} + +*/ +TEST_F(FusionCompatibilityTest, Continue) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %29 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 2 + %22 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %29 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %39 = OpPhi %6 %9 %5 %28 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %39 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %21 = OpSMod %6 %39 %20 + %23 = OpIEqual %17 %21 %22 + OpSelectionMerge %25 None + OpBranchConditional %23 %24 %25 + %24 = OpLabel + OpBranch %13 + %25 = OpLabel + OpBranch %13 + %13 = OpLabel + %28 = OpIAdd %6 %39 %22 + OpStore %8 %28 + OpBranch %10 + %12 = OpLabel + OpStore %29 %9 + OpBranch %30 + %30 = OpLabel + %40 = OpPhi %6 %9 %12 %38 %33 + OpLoopMerge %32 %33 None + OpBranch %34 + %34 = OpLabel + %36 = OpSLessThan %17 %40 %16 + OpBranchConditional %36 %31 %32 + %31 = OpLabel + OpBranch %33 + %33 = OpLabel + %38 = OpIAdd %6 %40 %22 + OpStore %29 %38 + OpBranch %30 + %32 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + EXPECT_FALSE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + // Compatible + for (int i = 0; i < 10; ++i) { + if (i % 2 == 1) { + } else { + a[i] = i; + } + } + for (int j = 0; j < 10; ++j) {} +} + +*/ +TEST_F(FusionCompatibilityTest, IfElseInLoop) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %31 "a" + OpName %37 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 2 + %22 = OpConstant %6 1 + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %6 %28 + %30 = OpTypePointer Function %29 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %31 = OpVariable %30 Function + %37 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %47 = OpPhi %6 %9 %5 %36 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %47 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %21 = OpSMod %6 %47 %20 + %23 = OpIEqual %17 %21 %22 + OpSelectionMerge %25 None + OpBranchConditional %23 %24 %26 + %24 = OpLabel + OpBranch %25 + %26 = OpLabel + %34 = OpAccessChain %7 %31 %47 + OpStore %34 %47 + OpBranch %25 + %25 = OpLabel + OpBranch %13 + %13 = OpLabel + %36 = OpIAdd %6 %47 %22 + OpStore %8 %36 + OpBranch %10 + %12 = OpLabel + OpStore %37 %9 + OpBranch %38 + %38 = OpLabel + %48 = OpPhi %6 %9 %12 %46 %41 + OpLoopMerge %40 %41 None + OpBranch %42 + %42 = OpLabel + %44 = OpSLessThan %17 %48 %16 + OpBranchConditional %44 %39 %40 + %39 = OpLabel + OpBranch %41 + %41 = OpLabel + %46 = OpIAdd %6 %48 %22 + OpStore %37 %46 + OpBranch %38 + %40 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + EXPECT_TRUE(LoopFusion(context.get(), loops[0], loops[1]).AreCompatible()); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/fusion_illegal.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/fusion_illegal.cpp new file mode 100644 index 0000000..26d5445 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/fusion_illegal.cpp @@ -0,0 +1,1592 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_fusion.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using FusionIllegalTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Illegal, loop-independent dependence will become a + // backward loop-carried antidependence + for (int i = 0; i < 10; i++) { + a[i] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i+1] + 2; + } +} + +*/ +TEST_F(FusionIllegalTest, PositiveDistanceCreatedRAW) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "b" + OpName %34 "i" + OpName %42 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 1 + %48 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %53 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %53 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %53 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %28 %29 + %31 = OpAccessChain %7 %23 %53 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %53 %29 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %54 = OpPhi %6 %9 %12 %52 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %54 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpIAdd %6 %54 %29 + %46 = OpAccessChain %7 %23 %45 + %47 = OpLoad %6 %46 + %49 = OpIAdd %6 %47 %48 + %50 = OpAccessChain %7 %42 %54 + OpStore %50 %49 + OpBranch %38 + %38 = OpLabel + %52 = OpIAdd %6 %54 %29 + OpStore %34 %52 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core + +int func() { + return 10; +} + +void main() { + int[10] a; + int[10] b; + // Illegal, function call + for (int i = 0; i < 10; i++) { + a[i] = func(); + } + for (int i = 0; i < 10; i++) { + b[i] = a[i]; + } +} +*/ +TEST_F(FusionIllegalTest, FunctionCall) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "func(" + OpName %14 "i" + OpName %28 "a" + OpName %35 "i" + OpName %43 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpConstant %6 10 + %13 = OpTypePointer Function %6 + %15 = OpConstant %6 0 + %22 = OpTypeBool + %24 = OpTypeInt 32 0 + %25 = OpConstant %24 10 + %26 = OpTypeArray %6 %25 + %27 = OpTypePointer Function %26 + %33 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpVariable %13 Function + %28 = OpVariable %27 Function + %35 = OpVariable %13 Function + %43 = OpVariable %27 Function + OpStore %14 %15 + OpBranch %16 + %16 = OpLabel + %51 = OpPhi %6 %15 %5 %34 %19 + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %23 = OpSLessThan %22 %51 %10 + OpBranchConditional %23 %17 %18 + %17 = OpLabel + %30 = OpFunctionCall %6 %8 + %31 = OpAccessChain %13 %28 %51 + OpStore %31 %30 + OpBranch %19 + %19 = OpLabel + %34 = OpIAdd %6 %51 %33 + OpStore %14 %34 + OpBranch %16 + %18 = OpLabel + OpStore %35 %15 + OpBranch %36 + %36 = OpLabel + %52 = OpPhi %6 %15 %18 %50 %39 + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %42 = OpSLessThan %22 %52 %10 + OpBranchConditional %42 %37 %38 + %37 = OpLabel + %46 = OpAccessChain %13 %28 %52 + %47 = OpLoad %6 %46 + %48 = OpAccessChain %13 %43 %52 + OpStore %48 %47 + OpBranch %39 + %39 = OpLabel + %50 = OpIAdd %6 %52 %33 + OpStore %35 %50 + OpBranch %36 + %38 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + OpReturnValue %10 + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 16 +#version 440 core +void main() { + int[10][10] a; + int[10][10] b; + int[10][10] c; + // Illegal outer. + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + c[i][j] = a[i][j] + 2; + } + } + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + b[i][j] = c[i+1][j] + 10; + } + } +} + +*/ +TEST_F(FusionIllegalTest, PositiveDistanceCreatedRAWOuterLoop) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %32 "c" + OpName %35 "a" + OpName %48 "i" + OpName %56 "j" + OpName %64 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %6 %28 + %30 = OpTypeArray %29 %28 + %31 = OpTypePointer Function %30 + %40 = OpConstant %6 2 + %44 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %31 Function + %35 = OpVariable %31 Function + %48 = OpVariable %7 Function + %56 = OpVariable %7 Function + %64 = OpVariable %31 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %78 = OpPhi %6 %9 %5 %47 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %78 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %82 = OpPhi %6 %9 %11 %45 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %82 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + %38 = OpAccessChain %7 %35 %78 %82 + %39 = OpLoad %6 %38 + %41 = OpIAdd %6 %39 %40 + %42 = OpAccessChain %7 %32 %78 %82 + OpStore %42 %41 + OpBranch %23 + %23 = OpLabel + %45 = OpIAdd %6 %82 %44 + OpStore %19 %45 + OpBranch %20 + %22 = OpLabel + OpBranch %13 + %13 = OpLabel + %47 = OpIAdd %6 %78 %44 + OpStore %8 %47 + OpBranch %10 + %12 = OpLabel + OpStore %48 %9 + OpBranch %49 + %49 = OpLabel + %79 = OpPhi %6 %9 %12 %77 %52 + OpLoopMerge %51 %52 None + OpBranch %53 + %53 = OpLabel + %55 = OpSLessThan %17 %79 %16 + OpBranchConditional %55 %50 %51 + %50 = OpLabel + OpStore %56 %9 + OpBranch %57 + %57 = OpLabel + %80 = OpPhi %6 %9 %50 %75 %60 + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %63 = OpSLessThan %17 %80 %16 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %68 = OpIAdd %6 %79 %44 + %70 = OpAccessChain %7 %32 %68 %80 + %71 = OpLoad %6 %70 + %72 = OpIAdd %6 %71 %16 + %73 = OpAccessChain %7 %64 %79 %80 + OpStore %73 %72 + OpBranch %60 + %60 = OpLabel + %75 = OpIAdd %6 %80 %44 + OpStore %56 %75 + OpBranch %57 + %59 = OpLabel + OpBranch %52 + %52 = OpLabel + %77 = OpIAdd %6 %79 %44 + OpStore %48 %77 + OpBranch %49 + %51 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 4u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + auto loop_3 = loops[3]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_2, loop_3); + EXPECT_FALSE(fusion.AreCompatible()); + } + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 19 +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Illegal, would create a backward loop-carried anti-dependence. + for (int i = 0; i < 10; i++) { + c[i] = a[i] + 1; + } + for (int i = 0; i < 10; i++) { + a[i+1] = c[i] + 2; + } +} + +*/ +TEST_F(FusionIllegalTest, PositiveDistanceCreatedWAR) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "c" + OpName %25 "a" + OpName %34 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 1 + %47 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %34 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %52 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %52 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %52 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %28 %29 + %31 = OpAccessChain %7 %23 %52 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %52 %29 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %53 = OpPhi %6 %9 %12 %51 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %53 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %43 = OpIAdd %6 %53 %29 + %45 = OpAccessChain %7 %23 %53 + %46 = OpLoad %6 %45 + %48 = OpIAdd %6 %46 %47 + %49 = OpAccessChain %7 %25 %43 + OpStore %49 %48 + OpBranch %38 + %38 = OpLabel + %51 = OpIAdd %6 %53 %29 + OpStore %34 %51 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 21 +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Illegal, would create a backward loop-carried anti-dependence. + for (int i = 0; i < 10; i++) { + a[i] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + a[i+1] = c[i+1] + 2; + } +} + +*/ +TEST_F(FusionIllegalTest, PositiveDistanceCreatedWAW) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "b" + OpName %34 "i" + OpName %44 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 1 + %49 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %34 = OpVariable %7 Function + %44 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %54 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %54 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %54 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %28 %29 + %31 = OpAccessChain %7 %23 %54 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %54 %29 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %55 = OpPhi %6 %9 %12 %53 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %55 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %43 = OpIAdd %6 %55 %29 + %46 = OpIAdd %6 %55 %29 + %47 = OpAccessChain %7 %44 %46 + %48 = OpLoad %6 %47 + %50 = OpIAdd %6 %48 %49 + %51 = OpAccessChain %7 %23 %43 + OpStore %51 %50 + OpBranch %38 + %38 = OpLabel + %53 = OpIAdd %6 %55 %29 + OpStore %34 %53 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 28 +#version 440 core +void main() { + int[10] a; + int[10] b; + + int sum_0 = 0; + + // Illegal + for (int i = 0; i < 10; i++) { + sum_0 += a[i]; + } + for (int j = 0; j < 10; j++) { + sum_0 += b[j]; + } +} + +*/ +TEST_F(FusionIllegalTest, SameReductionVariable) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "sum_0" + OpName %10 "i" + OpName %24 "a" + OpName %33 "j" + OpName %41 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 10 + %22 = OpTypeArray %6 %21 + %23 = OpTypePointer Function %22 + %31 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %24 = OpVariable %23 Function + %33 = OpVariable %7 Function + %41 = OpVariable %23 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + %52 = OpPhi %6 %9 %5 %29 %14 + %49 = OpPhi %6 %9 %5 %32 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %18 %49 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %26 = OpAccessChain %7 %24 %49 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %52 %27 + OpStore %8 %29 + OpBranch %14 + %14 = OpLabel + %32 = OpIAdd %6 %49 %31 + OpStore %10 %32 + OpBranch %11 + %13 = OpLabel + OpStore %33 %9 + OpBranch %34 + %34 = OpLabel + %51 = OpPhi %6 %52 %13 %46 %37 + %50 = OpPhi %6 %9 %13 %48 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %40 = OpSLessThan %18 %50 %17 + OpBranchConditional %40 %35 %36 + %35 = OpLabel + %43 = OpAccessChain %7 %41 %50 + %44 = OpLoad %6 %43 + %46 = OpIAdd %6 %51 %44 + OpStore %8 %46 + OpBranch %37 + %37 = OpLabel + %48 = OpIAdd %6 %50 %31 + OpStore %33 %48 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 28 +#version 440 core +void main() { + int[10] a; + int[10] b; + + int sum_0 = 0; + + // Illegal + for (int i = 0; i < 10; i++) { + sum_0 += a[i]; + } + for (int j = 0; j < 10; j++) { + sum_0 += b[j]; + } +} + +*/ +TEST_F(FusionIllegalTest, SameReductionVariableLCSSA) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "sum_0" + OpName %10 "i" + OpName %24 "a" + OpName %33 "j" + OpName %41 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 10 + %22 = OpTypeArray %6 %21 + %23 = OpTypePointer Function %22 + %31 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %24 = OpVariable %23 Function + %33 = OpVariable %7 Function + %41 = OpVariable %23 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + %52 = OpPhi %6 %9 %5 %29 %14 + %49 = OpPhi %6 %9 %5 %32 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %18 %49 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %26 = OpAccessChain %7 %24 %49 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %52 %27 + OpStore %8 %29 + OpBranch %14 + %14 = OpLabel + %32 = OpIAdd %6 %49 %31 + OpStore %10 %32 + OpBranch %11 + %13 = OpLabel + OpStore %33 %9 + OpBranch %34 + %34 = OpLabel + %51 = OpPhi %6 %52 %13 %46 %37 + %50 = OpPhi %6 %9 %13 %48 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %40 = OpSLessThan %18 %50 %17 + OpBranchConditional %40 %35 %36 + %35 = OpLabel + %43 = OpAccessChain %7 %41 %50 + %44 = OpLoad %6 %43 + %46 = OpIAdd %6 %51 %44 + OpStore %8 %46 + OpBranch %37 + %37 = OpLabel + %48 = OpIAdd %6 %50 %31 + OpStore %33 %48 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopUtils utils_0(context.get(), loops[0]); + utils_0.MakeLoopClosedSSA(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 30 +#version 440 core +int x; +void main() { + int[10] a; + int[10] b; + + // Illegal, x is unknown. + for (int i = 0; i < 10; i++) { + a[x] = a[i]; + } + for (int j = 0; j < 10; j++) { + a[j] = b[j]; + } +} + +*/ +TEST_F(FusionIllegalTest, UnknownIndexVariable) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "x" + OpName %34 "j" + OpName %43 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %24 = OpTypePointer Private %6 + %25 = OpVariable %24 Private + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %34 = OpVariable %7 Function + %43 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %50 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %50 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpLoad %6 %25 + %28 = OpAccessChain %7 %23 %50 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %7 %23 %26 + OpStore %30 %29 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %50 %32 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %51 = OpPhi %6 %9 %12 %49 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %51 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %43 %51 + %46 = OpLoad %6 %45 + %47 = OpAccessChain %7 %23 %51 + OpStore %47 %46 + OpBranch %38 + %38 = OpLabel + %49 = OpIAdd %6 %51 %32 + OpStore %34 %49 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + + int sum = 0; + + // Illegal, accumulator used for indexing. + for (int i = 0; i < 10; i++) { + sum += a[i]; + b[sum] = a[i]; + } + for (int j = 0; j < 10; j++) { + b[j] = b[j]+1; + } +} + +*/ +TEST_F(FusionIllegalTest, AccumulatorIndexing) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "sum" + OpName %10 "i" + OpName %24 "a" + OpName %30 "b" + OpName %39 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 10 + %22 = OpTypeArray %6 %21 + %23 = OpTypePointer Function %22 + %37 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %24 = OpVariable %23 Function + %30 = OpVariable %23 Function + %39 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + %57 = OpPhi %6 %9 %5 %29 %14 + %55 = OpPhi %6 %9 %5 %38 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %18 %55 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %26 = OpAccessChain %7 %24 %55 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %57 %27 + OpStore %8 %29 + %33 = OpAccessChain %7 %24 %55 + %34 = OpLoad %6 %33 + %35 = OpAccessChain %7 %30 %29 + OpStore %35 %34 + OpBranch %14 + %14 = OpLabel + %38 = OpIAdd %6 %55 %37 + OpStore %10 %38 + OpBranch %11 + %13 = OpLabel + OpStore %39 %9 + OpBranch %40 + %40 = OpLabel + %56 = OpPhi %6 %9 %13 %54 %43 + OpLoopMerge %42 %43 None + OpBranch %44 + %44 = OpLabel + %46 = OpSLessThan %18 %56 %17 + OpBranchConditional %46 %41 %42 + %41 = OpLabel + %49 = OpAccessChain %7 %30 %56 + %50 = OpLoad %6 %49 + %51 = OpIAdd %6 %50 %37 + %52 = OpAccessChain %7 %30 %56 + OpStore %52 %51 + OpBranch %43 + %43 = OpLabel + %54 = OpIAdd %6 %56 %37 + OpStore %39 %54 + OpBranch %40 + %42 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 33 +#version 440 core +void main() { + int[10] a; + int[10] b; + + // Illegal, barrier. + for (int i = 0; i < 10; i++) { + a[i] = a[i] * 2; + memoryBarrier(); + } + for (int j = 0; j < 10; j++) { + b[j] = b[j] + 1; + } +} + +*/ +TEST_F(FusionIllegalTest, Barrier) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %36 "j" + OpName %44 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %28 = OpConstant %6 2 + %31 = OpConstant %19 1 + %32 = OpConstant %19 3400 + %34 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %36 = OpVariable %7 Function + %44 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %53 = OpPhi %6 %9 %5 %35 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %53 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpAccessChain %7 %23 %53 + %27 = OpLoad %6 %26 + %29 = OpIMul %6 %27 %28 + %30 = OpAccessChain %7 %23 %53 + OpStore %30 %29 + OpMemoryBarrier %31 %32 + OpBranch %13 + %13 = OpLabel + %35 = OpIAdd %6 %53 %34 + OpStore %8 %35 + OpBranch %10 + %12 = OpLabel + OpStore %36 %9 + OpBranch %37 + %37 = OpLabel + %54 = OpPhi %6 %9 %12 %52 %40 + OpLoopMerge %39 %40 None + OpBranch %41 + %41 = OpLabel + %43 = OpSLessThan %17 %54 %16 + OpBranchConditional %43 %38 %39 + %38 = OpLabel + %47 = OpAccessChain %7 %44 %54 + %48 = OpLoad %6 %47 + %49 = OpIAdd %6 %48 %34 + %50 = OpAccessChain %7 %44 %54 + OpStore %50 %49 + OpBranch %40 + %40 = OpLabel + %52 = OpIAdd %6 %54 %34 + OpStore %36 %52 + OpBranch %37 + %39 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +struct TestStruct { + int[10] a; + int b; +}; + +void main() { + TestStruct test_0; + TestStruct test_1; + + for (int i = 0; i < 10; i++) { + test_0.a[i] = i; + } + for (int j = 0; j < 10; j++) { + test_0 = test_1; + } +} + +*/ +TEST_F(FusionIllegalTest, ArrayInStruct) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %22 "TestStruct" + OpMemberName %22 0 "a" + OpMemberName %22 1 "b" + OpName %24 "test_0" + OpName %31 "j" + OpName %39 "test_1" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypeStruct %21 %6 + %23 = OpTypePointer Function %22 + %29 = OpConstant %6 1 + %47 = OpUndef %22 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %24 = OpVariable %23 Function + %31 = OpVariable %7 Function + %39 = OpVariable %23 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %43 = OpPhi %6 %9 %5 %30 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %43 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %24 %9 %43 + OpStore %27 %43 + OpBranch %13 + %13 = OpLabel + %30 = OpIAdd %6 %43 %29 + OpStore %8 %30 + OpBranch %10 + %12 = OpLabel + OpStore %31 %9 + OpBranch %32 + %32 = OpLabel + %44 = OpPhi %6 %9 %12 %42 %35 + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel + %38 = OpSLessThan %17 %44 %16 + OpBranchConditional %38 %33 %34 + %33 = OpLabel + OpStore %24 %47 + OpBranch %35 + %35 = OpLabel + %42 = OpIAdd %6 %44 %29 + OpStore %31 %42 + OpBranch %32 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 450 + +struct P {float x,y,z;}; +uniform G { int a; P b[2]; int c; } g; +layout(location = 0) out float o; + +void main() +{ + P p[2]; + for (int i = 0; i < 2; ++i) { + p = g.b; + } + for (int j = 0; j < 2; ++j) { + o = p[g.a].x; + } +} + +*/ +TEST_F(FusionIllegalTest, NestedAccessChain) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %64 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "i" + OpName %20 "P" + OpMemberName %20 0 "x" + OpMemberName %20 1 "y" + OpMemberName %20 2 "z" + OpName %25 "p" + OpName %26 "P" + OpMemberName %26 0 "x" + OpMemberName %26 1 "y" + OpMemberName %26 2 "z" + OpName %28 "G" + OpMemberName %28 0 "a" + OpMemberName %28 1 "b" + OpMemberName %28 2 "c" + OpName %30 "g" + OpName %55 "j" + OpName %64 "o" + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 4 + OpMemberDecorate %26 2 Offset 8 + OpDecorate %27 ArrayStride 16 + OpMemberDecorate %28 0 Offset 0 + OpMemberDecorate %28 1 Offset 16 + OpMemberDecorate %28 2 Offset 48 + OpDecorate %28 Block + OpDecorate %30 DescriptorSet 0 + OpDecorate %64 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 2 + %17 = OpTypeBool + %19 = OpTypeFloat 32 + %20 = OpTypeStruct %19 %19 %19 + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 2 + %23 = OpTypeArray %20 %22 + %24 = OpTypePointer Function %23 + %26 = OpTypeStruct %19 %19 %19 + %27 = OpTypeArray %26 %22 + %28 = OpTypeStruct %6 %27 %6 + %29 = OpTypePointer Uniform %28 + %30 = OpVariable %29 Uniform + %31 = OpConstant %6 1 + %32 = OpTypePointer Uniform %27 + %36 = OpTypePointer Function %20 + %39 = OpTypePointer Function %19 + %63 = OpTypePointer Output %19 + %64 = OpVariable %63 Output + %65 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %25 = OpVariable %24 Function + %55 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %72 = OpPhi %6 %9 %5 %54 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %72 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %33 = OpAccessChain %32 %30 %31 + %34 = OpLoad %27 %33 + %35 = OpCompositeExtract %26 %34 0 + %37 = OpAccessChain %36 %25 %9 + %38 = OpCompositeExtract %19 %35 0 + %40 = OpAccessChain %39 %37 %9 + OpStore %40 %38 + %41 = OpCompositeExtract %19 %35 1 + %42 = OpAccessChain %39 %37 %31 + OpStore %42 %41 + %43 = OpCompositeExtract %19 %35 2 + %44 = OpAccessChain %39 %37 %16 + OpStore %44 %43 + %45 = OpCompositeExtract %26 %34 1 + %46 = OpAccessChain %36 %25 %31 + %47 = OpCompositeExtract %19 %45 0 + %48 = OpAccessChain %39 %46 %9 + OpStore %48 %47 + %49 = OpCompositeExtract %19 %45 1 + %50 = OpAccessChain %39 %46 %31 + OpStore %50 %49 + %51 = OpCompositeExtract %19 %45 2 + %52 = OpAccessChain %39 %46 %16 + OpStore %52 %51 + OpBranch %13 + %13 = OpLabel + %54 = OpIAdd %6 %72 %31 + OpStore %8 %54 + OpBranch %10 + %12 = OpLabel + OpStore %55 %9 + OpBranch %56 + %56 = OpLabel + %73 = OpPhi %6 %9 %12 %71 %59 + OpLoopMerge %58 %59 None + OpBranch %60 + %60 = OpLabel + %62 = OpSLessThan %17 %73 %16 + OpBranchConditional %62 %57 %58 + %57 = OpLabel + %66 = OpAccessChain %65 %30 %9 + %67 = OpLoad %6 %66 + %68 = OpAccessChain %39 %25 %67 %9 + %69 = OpLoad %19 %68 + OpStore %64 %69 + OpBranch %59 + %59 = OpLabel + %71 = OpIAdd %6 %73 %31 + OpStore %55 %71 + OpBranch %56 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_FALSE(fusion.IsLegal()); + } +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp new file mode 100644 index 0000000..56b0b76 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/fusion_legal.cpp @@ -0,0 +1,4580 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_fusion.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using FusionLegalTest = PassTest<::testing::Test>; + +bool Validate(const std::vector& bin) { + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + spv_context spvContext = spvContextCreate(target_env); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {bin.data(), bin.size()}; + spv_result_t error = spvValidate(spvContext, &binary, &diagnostic); + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + return error == 0; +} + +void Match(const std::string& checks, IRContext* context) { + // Silence unused warnings with !defined(SPIRV_EFFCE) + (void)checks; + + std::vector bin; + context->module()->ToBinary(&bin, true); + EXPECT_TRUE(Validate(bin)); + std::string assembly; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_2); + EXPECT_TRUE( + tools.Disassemble(bin, &assembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) + << "Disassembling failed for shader:\n" + << assembly << std::endl; + auto match_result = effcee::Match(assembly, checks); + EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) + << match_result.message() << "\nChecking result:\n" + << assembly; +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + // No dependence, legal + for (int i = 0; i < 10; i++) { + a[i] = a[i]*2; + } + for (int i = 0; i < 10; i++) { + b[i] = b[i]+2; + } +} + +*/ +TEST_F(FusionLegalTest, DifferentArraysInLoops) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %34 "i" + OpName %42 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %28 = OpConstant %6 2 + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %51 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %51 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpAccessChain %7 %23 %51 + %27 = OpLoad %6 %26 + %29 = OpIMul %6 %27 %28 + %30 = OpAccessChain %7 %23 %51 + OpStore %30 %29 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %51 %32 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %52 = OpPhi %6 %9 %12 %50 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %52 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %42 %52 + %46 = OpLoad %6 %45 + %47 = OpIAdd %6 %46 %28 + %48 = OpAccessChain %7 %42 %52 + OpStore %48 %47 + OpBranch %38 + %38 = OpLabel + %50 = OpIAdd %6 %52 %32 + OpStore %34 %50 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +)"; + + Match(checks, context.get()); + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Only loads to the same array, legal + for (int i = 0; i < 10; i++) { + b[i] = a[i]*2; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i]+2; + } +} + +*/ +TEST_F(FusionLegalTest, OnlyLoadsToSameArray) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "b" + OpName %25 "a" + OpName %35 "i" + OpName %43 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 2 + %33 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %35 = OpVariable %7 Function + %43 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %52 = OpPhi %6 %9 %5 %34 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %52 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %52 + %28 = OpLoad %6 %27 + %30 = OpIMul %6 %28 %29 + %31 = OpAccessChain %7 %23 %52 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %34 = OpIAdd %6 %52 %33 + OpStore %8 %34 + OpBranch %10 + %12 = OpLabel + OpStore %35 %9 + OpBranch %36 + %36 = OpLabel + %53 = OpPhi %6 %9 %12 %51 %39 + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %42 = OpSLessThan %17 %53 %16 + OpBranchConditional %42 %37 %38 + %37 = OpLabel + %46 = OpAccessChain %7 %25 %53 + %47 = OpLoad %6 %46 + %48 = OpIAdd %6 %47 %29 + %49 = OpAccessChain %7 %43 %53 + OpStore %49 %48 + OpBranch %39 + %39 = OpLabel + %51 = OpIAdd %6 %53 %33 + OpStore %35 %51 + OpBranch %36 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +)"; + + Match(checks, context.get()); + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + // No loop-carried dependences, legal + for (int i = 0; i < 10; i++) { + a[i] = a[i]*2; + } + for (int i = 0; i < 10; i++) { + b[i] = a[i]+2; + } +} + +*/ +TEST_F(FusionLegalTest, NoLoopCarriedDependences) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %34 "i" + OpName %42 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %28 = OpConstant %6 2 + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %51 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %51 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpAccessChain %7 %23 %51 + %27 = OpLoad %6 %26 + %29 = OpIMul %6 %27 %28 + %30 = OpAccessChain %7 %23 %51 + OpStore %30 %29 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %51 %32 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %52 = OpPhi %6 %9 %12 %50 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %52 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %23 %52 + %46 = OpLoad %6 %45 + %47 = OpIAdd %6 %46 %28 + %48 = OpAccessChain %7 %42 %52 + OpStore %48 %47 + OpBranch %38 + %38 = OpLabel + %50 = OpIAdd %6 %52 %32 + OpStore %34 %50 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +)"; + + Match(checks, context.get()); + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Parallelism inhibiting, but legal. + for (int i = 0; i < 10; i++) { + a[i] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i] + c[i-1]; + } +} + +*/ +TEST_F(FusionLegalTest, ExistingLoopCarriedDependence) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "b" + OpName %34 "i" + OpName %42 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %55 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %55 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %55 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %28 %29 + %31 = OpAccessChain %7 %23 %55 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %55 %29 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %56 = OpPhi %6 %9 %12 %54 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %56 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %23 %56 + %46 = OpLoad %6 %45 + %48 = OpISub %6 %56 %29 + %49 = OpAccessChain %7 %42 %48 + %50 = OpLoad %6 %49 + %51 = OpIAdd %6 %46 %50 + %52 = OpAccessChain %7 %42 %56 + OpStore %52 %51 + OpBranch %38 + %38 = OpLabel + %54 = OpIAdd %6 %56 %29 + OpStore %34 %54 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[I_1:%\w+]] = OpISub {{%\w+}} [[PHI]] {{%\w+}} +CHECK-NEXT: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +)"; + + Match(checks, context.get()); + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Creates a loop-carried dependence, but negative, so legal + for (int i = 0; i < 10; i++) { + a[i+1] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i] + 2; + } +} + +*/ +TEST_F(FusionLegalTest, NegativeDistanceCreatedRAW) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %27 "b" + OpName %35 "i" + OpName %43 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %25 = OpConstant %6 1 + %48 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %27 = OpVariable %22 Function + %35 = OpVariable %7 Function + %43 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %53 = OpPhi %6 %9 %5 %34 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %53 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpIAdd %6 %53 %25 + %29 = OpAccessChain %7 %27 %53 + %30 = OpLoad %6 %29 + %31 = OpIAdd %6 %30 %25 + %32 = OpAccessChain %7 %23 %26 + OpStore %32 %31 + OpBranch %13 + %13 = OpLabel + %34 = OpIAdd %6 %53 %25 + OpStore %8 %34 + OpBranch %10 + %12 = OpLabel + OpStore %35 %9 + OpBranch %36 + %36 = OpLabel + %54 = OpPhi %6 %9 %12 %52 %39 + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %42 = OpSLessThan %17 %54 %16 + OpBranchConditional %42 %37 %38 + %37 = OpLabel + %46 = OpAccessChain %7 %23 %54 + %47 = OpLoad %6 %46 + %49 = OpIAdd %6 %47 %48 + %50 = OpAccessChain %7 %43 %54 + OpStore %50 %49 + OpBranch %39 + %39 = OpLabel + %52 = OpIAdd %6 %54 %25 + OpStore %35 %52 + OpBranch %36 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } + + { + auto& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Legal + for (int i = 0; i < 10; i++) { + a[i+1] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i+1] + 2; + } +} + +*/ +TEST_F(FusionLegalTest, NoLoopCarriedDependencesAdjustedIndex) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %27 "b" + OpName %35 "i" + OpName %43 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %25 = OpConstant %6 1 + %49 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %27 = OpVariable %22 Function + %35 = OpVariable %7 Function + %43 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %54 = OpPhi %6 %9 %5 %34 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %54 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpIAdd %6 %54 %25 + %29 = OpAccessChain %7 %27 %54 + %30 = OpLoad %6 %29 + %31 = OpIAdd %6 %30 %25 + %32 = OpAccessChain %7 %23 %26 + OpStore %32 %31 + OpBranch %13 + %13 = OpLabel + %34 = OpIAdd %6 %54 %25 + OpStore %8 %34 + OpBranch %10 + %12 = OpLabel + OpStore %35 %9 + OpBranch %36 + %36 = OpLabel + %55 = OpPhi %6 %9 %12 %53 %39 + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %42 = OpSLessThan %17 %55 %16 + OpBranchConditional %42 %37 %38 + %37 = OpLabel + %46 = OpIAdd %6 %55 %25 + %47 = OpAccessChain %7 %23 %46 + %48 = OpLoad %6 %47 + %50 = OpIAdd %6 %48 %49 + %51 = OpAccessChain %7 %43 %55 + OpStore %51 %50 + OpBranch %39 + %39 = OpLabel + %53 = OpIAdd %6 %55 %25 + OpStore %35 %53 + OpBranch %36 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} +CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +)"; + + Match(checks, context.get()); + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Legal, independent locations in |a|, SIV + for (int i = 0; i < 10; i++) { + a[2*i+1] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[2*i] + 2; + } +} + +*/ +TEST_F(FusionLegalTest, IndependentSIV) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %29 "b" + OpName %37 "i" + OpName %45 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %24 = OpConstant %6 2 + %27 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %29 = OpVariable %22 Function + %37 = OpVariable %7 Function + %45 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %55 = OpPhi %6 %9 %5 %36 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %55 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpIMul %6 %24 %55 + %28 = OpIAdd %6 %26 %27 + %31 = OpAccessChain %7 %29 %55 + %32 = OpLoad %6 %31 + %33 = OpIAdd %6 %32 %27 + %34 = OpAccessChain %7 %23 %28 + OpStore %34 %33 + OpBranch %13 + %13 = OpLabel + %36 = OpIAdd %6 %55 %27 + OpStore %8 %36 + OpBranch %10 + %12 = OpLabel + OpStore %37 %9 + OpBranch %38 + %38 = OpLabel + %56 = OpPhi %6 %9 %12 %54 %41 + OpLoopMerge %40 %41 None + OpBranch %42 + %42 = OpLabel + %44 = OpSLessThan %17 %56 %16 + OpBranchConditional %44 %39 %40 + %39 = OpLabel + %48 = OpIMul %6 %24 %56 + %49 = OpAccessChain %7 %23 %48 + %50 = OpLoad %6 %49 + %51 = OpIAdd %6 %50 %24 + %52 = OpAccessChain %7 %45 %56 + OpStore %52 %51 + OpBranch %41 + %41 = OpLabel + %54 = OpIAdd %6 %56 %27 + OpStore %37 %54 + OpBranch %38 + %40 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[I_2:%\w+]] = OpIMul {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[I_2_1:%\w+]] = OpIAdd {{%\w+}} [[I_2]] {{%\w+}} +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_2_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[I_2:%\w+]] = OpIMul {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_2]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +)"; + + Match(checks, context.get()); + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Legal, independent locations in |a|, ZIV + for (int i = 0; i < 10; i++) { + a[1] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[9] + 2; + } +} + +*/ +TEST_F(FusionLegalTest, IndependentZIV) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "b" + OpName %33 "i" + OpName %41 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %24 = OpConstant %6 1 + %43 = OpConstant %6 9 + %46 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %33 = OpVariable %7 Function + %41 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %51 = OpPhi %6 %9 %5 %32 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %51 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %51 + %28 = OpLoad %6 %27 + %29 = OpIAdd %6 %28 %24 + %30 = OpAccessChain %7 %23 %24 + OpStore %30 %29 + OpBranch %13 + %13 = OpLabel + %32 = OpIAdd %6 %51 %24 + OpStore %8 %32 + OpBranch %10 + %12 = OpLabel + OpStore %33 %9 + OpBranch %34 + %34 = OpLabel + %52 = OpPhi %6 %9 %12 %50 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %40 = OpSLessThan %17 %52 %16 + OpBranchConditional %40 %35 %36 + %35 = OpLabel + %44 = OpAccessChain %7 %23 %43 + %45 = OpLoad %6 %44 + %47 = OpIAdd %6 %45 %46 + %48 = OpAccessChain %7 %41 %52 + OpStore %48 %47 + OpBranch %37 + %37 = OpLabel + %50 = OpIAdd %6 %52 %24 + OpStore %33 %50 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK-NOT: OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK: OpStore +CHECK-NOT: OpPhi +CHECK-NOT: OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK: OpLoad +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +)"; + + Match(checks, context.get()); + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[20] a; + int[10] b; + int[10] c; + // Legal, non-overlapping sections in |a| + for (int i = 0; i < 10; i++) { + a[i] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i+10] + 2; + } +} + +*/ +TEST_F(FusionLegalTest, NonOverlappingAccesses) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %28 "b" + OpName %37 "i" + OpName %45 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 20 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %25 = OpConstant %19 10 + %26 = OpTypeArray %6 %25 + %27 = OpTypePointer Function %26 + %32 = OpConstant %6 1 + %51 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %28 = OpVariable %27 Function + %37 = OpVariable %7 Function + %45 = OpVariable %27 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %56 = OpPhi %6 %9 %5 %36 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %56 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %30 = OpAccessChain %7 %28 %56 + %31 = OpLoad %6 %30 + %33 = OpIAdd %6 %31 %32 + %34 = OpAccessChain %7 %23 %56 + OpStore %34 %33 + OpBranch %13 + %13 = OpLabel + %36 = OpIAdd %6 %56 %32 + OpStore %8 %36 + OpBranch %10 + %12 = OpLabel + OpStore %37 %9 + OpBranch %38 + %38 = OpLabel + %57 = OpPhi %6 %9 %12 %55 %41 + OpLoopMerge %40 %41 None + OpBranch %42 + %42 = OpLabel + %44 = OpSLessThan %17 %57 %16 + OpBranchConditional %44 %39 %40 + %39 = OpLabel + %48 = OpIAdd %6 %57 %16 + %49 = OpAccessChain %7 %23 %48 + %50 = OpLoad %6 %49 + %52 = OpIAdd %6 %50 %51 + %53 = OpAccessChain %7 %45 %57 + OpStore %53 %52 + OpBranch %41 + %41 = OpLabel + %55 = OpIAdd %6 %57 %32 + OpStore %37 %55 + OpBranch %38 + %40 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NOT: OpPhi +CHECK: [[I_10:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} +CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_10]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +)"; + + Match(checks, context.get()); + + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Legal, 3 adjacent loops + for (int i = 0; i < 10; i++) { + a[i] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i] + 2; + } + for (int i = 0; i < 10; i++) { + b[i] = c[i] + 10; + } +} + +*/ +TEST_F(FusionLegalTest, AdjacentLoops) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "b" + OpName %34 "i" + OpName %42 "c" + OpName %52 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 1 + %47 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + %52 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %68 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %68 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %68 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %28 %29 + %31 = OpAccessChain %7 %23 %68 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %68 %29 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %69 = OpPhi %6 %9 %12 %51 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %69 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %23 %69 + %46 = OpLoad %6 %45 + %48 = OpIAdd %6 %46 %47 + %49 = OpAccessChain %7 %42 %69 + OpStore %49 %48 + OpBranch %38 + %38 = OpLabel + %51 = OpIAdd %6 %69 %29 + OpStore %34 %51 + OpBranch %35 + %37 = OpLabel + OpStore %52 %9 + OpBranch %53 + %53 = OpLabel + %70 = OpPhi %6 %9 %37 %67 %56 + OpLoopMerge %55 %56 None + OpBranch %57 + %57 = OpLabel + %59 = OpSLessThan %17 %70 %16 + OpBranchConditional %59 %54 %55 + %54 = OpLabel + %62 = OpAccessChain %7 %42 %70 + %63 = OpLoad %6 %62 + %64 = OpIAdd %6 %63 %16 + %65 = OpAccessChain %7 %25 %70 + OpStore %65 %64 + OpBranch %56 + %56 = OpLabel + %67 = OpIAdd %6 %70 %29 + OpStore %52 %67 + OpBranch %53 + %55 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[1], loops[2]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_1]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] +CHECK: [[STORE_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_2]] + )"; + + Match(checks, context.get()); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + std::string checks_ = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] +CHECK: [[STORE_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_2]] + )"; + + Match(checks_, context.get()); + + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10][10] a; + int[10][10] b; + int[10][10] c; + // Legal inner loop fusion + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + c[i][j] = a[i][j] + 2; + } + for (int j = 0; j < 10; j++) { + b[i][j] = c[i][j] + 10; + } + } +} + +*/ +TEST_F(FusionLegalTest, InnerLoopFusion) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %32 "c" + OpName %35 "a" + OpName %46 "j" + OpName %54 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %6 %28 + %30 = OpTypeArray %29 %28 + %31 = OpTypePointer Function %30 + %40 = OpConstant %6 2 + %44 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %31 Function + %35 = OpVariable %31 Function + %46 = OpVariable %7 Function + %54 = OpVariable %31 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %67 = OpPhi %6 %9 %5 %66 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %67 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %68 = OpPhi %6 %9 %11 %45 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %68 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + %38 = OpAccessChain %7 %35 %67 %68 + %39 = OpLoad %6 %38 + %41 = OpIAdd %6 %39 %40 + %42 = OpAccessChain %7 %32 %67 %68 + OpStore %42 %41 + OpBranch %23 + %23 = OpLabel + %45 = OpIAdd %6 %68 %44 + OpStore %19 %45 + OpBranch %20 + %22 = OpLabel + OpStore %46 %9 + OpBranch %47 + %47 = OpLabel + %69 = OpPhi %6 %9 %22 %64 %50 + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %53 = OpSLessThan %17 %69 %16 + OpBranchConditional %53 %48 %49 + %48 = OpLabel + %59 = OpAccessChain %7 %32 %67 %69 + %60 = OpLoad %6 %59 + %61 = OpIAdd %6 %60 %16 + %62 = OpAccessChain %7 %54 %67 %69 + OpStore %62 %61 + OpBranch %50 + %50 = OpLabel + %64 = OpIAdd %6 %69 %44 + OpStore %46 %64 + OpBranch %47 + %49 = OpLabel + OpBranch %13 + %13 = OpLabel + %66 = OpIAdd %6 %67 %44 + OpStore %8 %66 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + + auto& ld_final = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld_final.NumLoops(), 2u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// 12 +#version 440 core +void main() { + int[10][10] a; + int[10][10] b; + int[10][10] c; + // Legal both + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + c[i][j] = a[i][j] + 2; + } + } + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + b[i][j] = c[i][j] + 10; + } + } +} + +*/ +TEST_F(FusionLegalTest, OuterAndInnerLoop) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %32 "c" + OpName %35 "a" + OpName %48 "i" + OpName %56 "j" + OpName %64 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %6 %28 + %30 = OpTypeArray %29 %28 + %31 = OpTypePointer Function %30 + %40 = OpConstant %6 2 + %44 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %31 Function + %35 = OpVariable %31 Function + %48 = OpVariable %7 Function + %56 = OpVariable %7 Function + %64 = OpVariable %31 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %77 = OpPhi %6 %9 %5 %47 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %77 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %81 = OpPhi %6 %9 %11 %45 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %81 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + %38 = OpAccessChain %7 %35 %77 %81 + %39 = OpLoad %6 %38 + %41 = OpIAdd %6 %39 %40 + %42 = OpAccessChain %7 %32 %77 %81 + OpStore %42 %41 + OpBranch %23 + %23 = OpLabel + %45 = OpIAdd %6 %81 %44 + OpStore %19 %45 + OpBranch %20 + %22 = OpLabel + OpBranch %13 + %13 = OpLabel + %47 = OpIAdd %6 %77 %44 + OpStore %8 %47 + OpBranch %10 + %12 = OpLabel + OpStore %48 %9 + OpBranch %49 + %49 = OpLabel + %78 = OpPhi %6 %9 %12 %76 %52 + OpLoopMerge %51 %52 None + OpBranch %53 + %53 = OpLabel + %55 = OpSLessThan %17 %78 %16 + OpBranchConditional %55 %50 %51 + %50 = OpLabel + OpStore %56 %9 + OpBranch %57 + %57 = OpLabel + %79 = OpPhi %6 %9 %50 %74 %60 + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %63 = OpSLessThan %17 %79 %16 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %69 = OpAccessChain %7 %32 %78 %79 + %70 = OpLoad %6 %69 + %71 = OpIAdd %6 %70 %16 + %72 = OpAccessChain %7 %64 %78 %79 + OpStore %72 %71 + OpBranch %60 + %60 = OpLabel + %74 = OpIAdd %6 %79 %44 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %52 + %52 = OpLabel + %76 = OpIAdd %6 %78 %44 + OpStore %48 %76 + OpBranch %49 + %51 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 4u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + auto loop_3 = loops[3]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_2, loop_3); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_3); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + fusion.Fuse(); + } + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK: [[PHI_2:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } + + { + auto& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + fusion.Fuse(); + } + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } + + { + auto& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10][10] a; + int[10][10] b; + int[10][10] c; + // Legal both, more complex + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + if (i % 2 == 0 && j % 2 == 0) { + c[i][j] = a[i][j] + 2; + } + } + } + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + b[i][j] = c[i][j] + 10; + } + } +} + +*/ +TEST_F(FusionLegalTest, OuterAndInnerLoopMoreComplex) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %44 "c" + OpName %47 "a" + OpName %59 "i" + OpName %67 "j" + OpName %75 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %28 = OpConstant %6 2 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 10 + %41 = OpTypeArray %6 %40 + %42 = OpTypeArray %41 %40 + %43 = OpTypePointer Function %42 + %55 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %44 = OpVariable %43 Function + %47 = OpVariable %43 Function + %59 = OpVariable %7 Function + %67 = OpVariable %7 Function + %75 = OpVariable %43 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %88 = OpPhi %6 %9 %5 %58 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %88 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %92 = OpPhi %6 %9 %11 %56 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %92 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + %29 = OpSMod %6 %88 %28 + %30 = OpIEqual %17 %29 %9 + OpSelectionMerge %32 None + OpBranchConditional %30 %31 %32 + %31 = OpLabel + %34 = OpSMod %6 %92 %28 + %35 = OpIEqual %17 %34 %9 + OpBranch %32 + %32 = OpLabel + %36 = OpPhi %17 %30 %21 %35 %31 + OpSelectionMerge %38 None + OpBranchConditional %36 %37 %38 + %37 = OpLabel + %50 = OpAccessChain %7 %47 %88 %92 + %51 = OpLoad %6 %50 + %52 = OpIAdd %6 %51 %28 + %53 = OpAccessChain %7 %44 %88 %92 + OpStore %53 %52 + OpBranch %38 + %38 = OpLabel + OpBranch %23 + %23 = OpLabel + %56 = OpIAdd %6 %92 %55 + OpStore %19 %56 + OpBranch %20 + %22 = OpLabel + OpBranch %13 + %13 = OpLabel + %58 = OpIAdd %6 %88 %55 + OpStore %8 %58 + OpBranch %10 + %12 = OpLabel + OpStore %59 %9 + OpBranch %60 + %60 = OpLabel + %89 = OpPhi %6 %9 %12 %87 %63 + OpLoopMerge %62 %63 None + OpBranch %64 + %64 = OpLabel + %66 = OpSLessThan %17 %89 %16 + OpBranchConditional %66 %61 %62 + %61 = OpLabel + OpStore %67 %9 + OpBranch %68 + %68 = OpLabel + %90 = OpPhi %6 %9 %61 %85 %71 + OpLoopMerge %70 %71 None + OpBranch %72 + %72 = OpLabel + %74 = OpSLessThan %17 %90 %16 + OpBranchConditional %74 %69 %70 + %69 = OpLabel + %80 = OpAccessChain %7 %44 %89 %90 + %81 = OpLoad %6 %80 + %82 = OpIAdd %6 %81 %16 + %83 = OpAccessChain %7 %75 %89 %90 + OpStore %83 %82 + OpBranch %71 + %71 = OpLabel + %85 = OpIAdd %6 %90 %55 + OpStore %67 %85 + OpBranch %68 + %70 = OpLabel + OpBranch %63 + %63 = OpLabel + %87 = OpIAdd %6 %89 %55 + OpStore %59 %87 + OpBranch %60 + %62 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 4u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + auto loop_3 = loops[3]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_2, loop_3); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_3); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + fusion.Fuse(); + } + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: OpPhi +CHECK-NEXT: OpSelectionMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK: [[PHI_2:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + fusion.Fuse(); + } + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: OpPhi +CHECK-NEXT: OpSelectionMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10][10] a; + int[10][10] b; + int[10][10] c; + // Outer would have been illegal to fuse, but since written + // like this, inner loop fusion is legal. + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + c[i][j] = a[i][j] + 2; + } + for (int j = 0; j < 10; j++) { + b[i][j] = c[i+1][j] + 10; + } + } +} + +*/ +TEST_F(FusionLegalTest, InnerWithExistingDependenceOnOuter) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %32 "c" + OpName %35 "a" + OpName %46 "j" + OpName %54 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %6 %28 + %30 = OpTypeArray %29 %28 + %31 = OpTypePointer Function %30 + %40 = OpConstant %6 2 + %44 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %31 Function + %35 = OpVariable %31 Function + %46 = OpVariable %7 Function + %54 = OpVariable %31 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %68 = OpPhi %6 %9 %5 %67 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %68 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %69 = OpPhi %6 %9 %11 %45 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %69 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + %38 = OpAccessChain %7 %35 %68 %69 + %39 = OpLoad %6 %38 + %41 = OpIAdd %6 %39 %40 + %42 = OpAccessChain %7 %32 %68 %69 + OpStore %42 %41 + OpBranch %23 + %23 = OpLabel + %45 = OpIAdd %6 %69 %44 + OpStore %19 %45 + OpBranch %20 + %22 = OpLabel + OpStore %46 %9 + OpBranch %47 + %47 = OpLabel + %70 = OpPhi %6 %9 %22 %65 %50 + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %53 = OpSLessThan %17 %70 %16 + OpBranchConditional %53 %48 %49 + %48 = OpLabel + %58 = OpIAdd %6 %68 %44 + %60 = OpAccessChain %7 %32 %58 %70 + %61 = OpLoad %6 %60 + %62 = OpIAdd %6 %61 %16 + %63 = OpAccessChain %7 %54 %68 %70 + OpStore %63 %62 + OpBranch %50 + %50 = OpLabel + %65 = OpIAdd %6 %70 %44 + OpStore %46 %65 + OpBranch %47 + %49 = OpLabel + OpBranch %13 + %13 = OpLabel + %67 = OpIAdd %6 %68 %44 + OpStore %8 %67 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI_0]] {{%\w+}} +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // One dimensional arrays. Legal, outer dist 0, inner independent. + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + c[i] = a[j] + 2; + } + } + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + b[j] = c[i] + 10; + } + } +} + +*/ +TEST_F(FusionLegalTest, OuterAndInnerLoopOneDimArrays) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %31 "c" + OpName %33 "a" + OpName %45 "i" + OpName %53 "j" + OpName %61 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %6 %28 + %30 = OpTypePointer Function %29 + %37 = OpConstant %6 2 + %41 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %31 = OpVariable %30 Function + %33 = OpVariable %30 Function + %45 = OpVariable %7 Function + %53 = OpVariable %7 Function + %61 = OpVariable %30 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %72 = OpPhi %6 %9 %5 %44 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %72 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %76 = OpPhi %6 %9 %11 %42 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %76 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + %35 = OpAccessChain %7 %33 %76 + %36 = OpLoad %6 %35 + %38 = OpIAdd %6 %36 %37 + %39 = OpAccessChain %7 %31 %72 + OpStore %39 %38 + OpBranch %23 + %23 = OpLabel + %42 = OpIAdd %6 %76 %41 + OpStore %19 %42 + OpBranch %20 + %22 = OpLabel + OpBranch %13 + %13 = OpLabel + %44 = OpIAdd %6 %72 %41 + OpStore %8 %44 + OpBranch %10 + %12 = OpLabel + OpStore %45 %9 + OpBranch %46 + %46 = OpLabel + %73 = OpPhi %6 %9 %12 %71 %49 + OpLoopMerge %48 %49 None + OpBranch %50 + %50 = OpLabel + %52 = OpSLessThan %17 %73 %16 + OpBranchConditional %52 %47 %48 + %47 = OpLabel + OpStore %53 %9 + OpBranch %54 + %54 = OpLabel + %74 = OpPhi %6 %9 %47 %69 %57 + OpLoopMerge %56 %57 None + OpBranch %58 + %58 = OpLabel + %60 = OpSLessThan %17 %74 %16 + OpBranchConditional %60 %55 %56 + %55 = OpLabel + %64 = OpAccessChain %7 %31 %73 + %65 = OpLoad %6 %64 + %66 = OpIAdd %6 %65 %16 + %67 = OpAccessChain %7 %61 %74 + OpStore %67 %66 + OpBranch %57 + %57 = OpLabel + %69 = OpIAdd %6 %74 %41 + OpStore %53 %69 + OpBranch %54 + %56 = OpLabel + OpBranch %49 + %49 = OpLabel + %71 = OpIAdd %6 %73 %41 + OpStore %45 %71 + OpBranch %46 + %48 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 4u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + auto loop_3 = loops[3]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_2, loop_3); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + fusion.Fuse(); + } + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK: [[PHI_2:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_2]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + auto loop_0 = loops[0]; + auto loop_1 = loops[1]; + auto loop_2 = loops[2]; + + { + LoopFusion fusion(context.get(), loop_0, loop_1); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_0, loop_2); + EXPECT_FALSE(fusion.AreCompatible()); + } + + { + LoopFusion fusion(context.get(), loop_1, loop_2); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Legal, creates a loop-carried dependence, but has negative distance + for (int i = 0; i < 10; i++) { + c[i] = a[i+1] + 1; + } + for (int i = 0; i < 10; i++) { + a[i] = c[i] + 2; + } +} + +*/ +TEST_F(FusionLegalTest, NegativeDistanceCreatedWAR) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "c" + OpName %25 "a" + OpName %35 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %27 = OpConstant %6 1 + %47 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %35 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %52 = OpPhi %6 %9 %5 %34 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %52 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %28 = OpIAdd %6 %52 %27 + %29 = OpAccessChain %7 %25 %28 + %30 = OpLoad %6 %29 + %31 = OpIAdd %6 %30 %27 + %32 = OpAccessChain %7 %23 %52 + OpStore %32 %31 + OpBranch %13 + %13 = OpLabel + %34 = OpIAdd %6 %52 %27 + OpStore %8 %34 + OpBranch %10 + %12 = OpLabel + OpStore %35 %9 + OpBranch %36 + %36 = OpLabel + %53 = OpPhi %6 %9 %12 %51 %39 + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %42 = OpSLessThan %17 %53 %16 + OpBranchConditional %42 %37 %38 + %37 = OpLabel + %45 = OpAccessChain %7 %23 %53 + %46 = OpLoad %6 %45 + %48 = OpIAdd %6 %46 %47 + %49 = OpAccessChain %7 %25 %53 + OpStore %49 %48 + OpBranch %39 + %39 = OpLabel + %51 = OpIAdd %6 %53 %27 + OpStore %35 %51 + OpBranch %36 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} +CHECK-NEXT: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } + + { + auto& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Legal, creates a loop-carried dependence, but has negative distance + for (int i = 0; i < 10; i++) { + a[i+1] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + a[i] = c[i+1] + 2; + } +} + +*/ +TEST_F(FusionLegalTest, NegativeDistanceCreatedWAW) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %27 "b" + OpName %35 "i" + OpName %44 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %25 = OpConstant %6 1 + %49 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %27 = OpVariable %22 Function + %35 = OpVariable %7 Function + %44 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %54 = OpPhi %6 %9 %5 %34 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %54 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpIAdd %6 %54 %25 + %29 = OpAccessChain %7 %27 %54 + %30 = OpLoad %6 %29 + %31 = OpIAdd %6 %30 %25 + %32 = OpAccessChain %7 %23 %26 + OpStore %32 %31 + OpBranch %13 + %13 = OpLabel + %34 = OpIAdd %6 %54 %25 + OpStore %8 %34 + OpBranch %10 + %12 = OpLabel + OpStore %35 %9 + OpBranch %36 + %36 = OpLabel + %55 = OpPhi %6 %9 %12 %53 %39 + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %42 = OpSLessThan %17 %55 %16 + OpBranchConditional %42 %37 %38 + %37 = OpLabel + %46 = OpIAdd %6 %55 %25 + %47 = OpAccessChain %7 %44 %46 + %48 = OpLoad %6 %47 + %50 = OpIAdd %6 %48 %49 + %51 = OpAccessChain %7 %23 %55 + OpStore %51 %50 + OpBranch %39 + %39 = OpLabel + %53 = OpIAdd %6 %55 %25 + OpStore %35 %53 + OpBranch %36 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] +CHECK-NEXT: OpStore +CHECK-NOT: OpPhi +CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} +CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Legal, no loop-carried dependence + for (int i = 0; i < 10; i++) { + a[i] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + a[i] = c[i+1] + 2; + } +} + +*/ +TEST_F(FusionLegalTest, NoLoopCarriedDependencesWAW) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "b" + OpName %34 "i" + OpName %43 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 1 + %48 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %34 = OpVariable %7 Function + %43 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %53 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %53 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %53 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %28 %29 + %31 = OpAccessChain %7 %23 %53 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %53 %29 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %54 = OpPhi %6 %9 %12 %52 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %54 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpIAdd %6 %54 %29 + %46 = OpAccessChain %7 %43 %45 + %47 = OpLoad %6 %46 + %49 = OpIAdd %6 %47 %48 + %50 = OpAccessChain %7 %23 %54 + OpStore %50 %49 + OpBranch %38 + %38 = OpLabel + %52 = OpIAdd %6 %54 %29 + OpStore %34 %52 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[I_1:%\w+]] = OpIAdd {{%\w+}} [[PHI]] {{%\w+}} +CHECK-NEXT: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[I_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10][10] a; + int[10][10] b; + int[10][10] c; + // Legal outer. Continue and break are fine if nested in inner loops + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + if (j % 2 == 0) { + c[i][j] = a[i][j] + 2; + } else { + continue; + } + } + } + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + if (j % 2 == 0) { + b[i][j] = c[i][j] + 10; + } else { + break; + } + } + } +} + +*/ +TEST_F(FusionLegalTest, OuterloopWithBreakContinueInInner) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %38 "c" + OpName %41 "a" + OpName %55 "i" + OpName %63 "j" + OpName %76 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %28 = OpConstant %6 2 + %33 = OpTypeInt 32 0 + %34 = OpConstant %33 10 + %35 = OpTypeArray %6 %34 + %36 = OpTypeArray %35 %34 + %37 = OpTypePointer Function %36 + %51 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %38 = OpVariable %37 Function + %41 = OpVariable %37 Function + %55 = OpVariable %7 Function + %63 = OpVariable %7 Function + %76 = OpVariable %37 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %91 = OpPhi %6 %9 %5 %54 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %91 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %96 = OpPhi %6 %9 %11 %52 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %96 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + %29 = OpSMod %6 %96 %28 + %30 = OpIEqual %17 %29 %9 + OpSelectionMerge %sel_merge None + OpBranchConditional %30 %31 %48 + %31 = OpLabel + %44 = OpAccessChain %7 %41 %91 %96 + %45 = OpLoad %6 %44 + %46 = OpIAdd %6 %45 %28 + %47 = OpAccessChain %7 %38 %91 %96 + OpStore %47 %46 + OpBranch %32 + %48 = OpLabel + OpBranch %sel_merge + %32 = OpLabel + OpBranch %sel_merge + %sel_merge = OpLabel + OpBranch %23 + %23 = OpLabel + %52 = OpIAdd %6 %96 %51 + OpStore %19 %52 + OpBranch %20 + %22 = OpLabel + OpBranch %13 + %13 = OpLabel + %54 = OpIAdd %6 %91 %51 + OpStore %8 %54 + OpBranch %10 + %12 = OpLabel + OpStore %55 %9 + OpBranch %56 + %56 = OpLabel + %92 = OpPhi %6 %9 %12 %90 %59 + OpLoopMerge %58 %59 None + OpBranch %60 + %60 = OpLabel + %62 = OpSLessThan %17 %92 %16 + OpBranchConditional %62 %57 %58 + %57 = OpLabel + OpStore %63 %9 + OpBranch %64 + %64 = OpLabel + %93 = OpPhi %6 %9 %57 %88 %67 + OpLoopMerge %66 %67 None + OpBranch %68 + %68 = OpLabel + %70 = OpSLessThan %17 %93 %16 + OpBranchConditional %70 %65 %66 + %65 = OpLabel + %72 = OpSMod %6 %93 %28 + %73 = OpIEqual %17 %72 %9 + OpSelectionMerge %75 None + OpBranchConditional %73 %74 %66 + %74 = OpLabel + %81 = OpAccessChain %7 %38 %92 %93 + %82 = OpLoad %6 %81 + %83 = OpIAdd %6 %82 %16 + %84 = OpAccessChain %7 %76 %92 %93 + OpStore %84 %83 + OpBranch %75 + %75 = OpLabel + OpBranch %67 + %67 = OpLabel + %88 = OpIAdd %6 %93 %51 + OpStore %63 %88 + OpBranch %64 + %66 = OpLabel + OpBranch %59 + %59 = OpLabel + %90 = OpIAdd %6 %92 %51 + OpStore %55 %90 + OpBranch %56 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 4u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[2]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[1], loops[2]); + EXPECT_FALSE(fusion.AreCompatible()); + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK: [[PHI_2:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] [[PHI_2]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// j loop preheader removed manually +#version 440 core +void main() { + int[10] a; + int[10] b; + int i = 0; + int j = 0; + // No loop-carried dependences, legal + for (; i < 10; i++) { + a[i] = a[i]*2; + } + for (; j < 10; j++) { + b[j] = a[j]+2; + } +} + +*/ +TEST_F(FusionLegalTest, DifferentArraysInLoopsNoPreheader) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %10 "j" + OpName %24 "a" + OpName %42 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 10 + %22 = OpTypeArray %6 %21 + %23 = OpTypePointer Function %22 + %29 = OpConstant %6 2 + %33 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %24 = OpVariable %23 Function + %42 = OpVariable %23 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + %51 = OpPhi %6 %9 %5 %34 %14 + OpLoopMerge %35 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %18 %51 %17 + OpBranchConditional %19 %12 %35 + %12 = OpLabel + %27 = OpAccessChain %7 %24 %51 + %28 = OpLoad %6 %27 + %30 = OpIMul %6 %28 %29 + %31 = OpAccessChain %7 %24 %51 + OpStore %31 %30 + OpBranch %14 + %14 = OpLabel + %34 = OpIAdd %6 %51 %33 + OpStore %8 %34 + OpBranch %11 + %35 = OpLabel + %52 = OpPhi %6 %9 %15 %50 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %18 %52 %17 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %24 %52 + %46 = OpLoad %6 %45 + %47 = OpIAdd %6 %46 %29 + %48 = OpAccessChain %7 %42 %52 + OpStore %48 %47 + OpBranch %38 + %38 = OpLabel + %50 = OpIAdd %6 %52 %33 + OpStore %10 %50 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + { + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_FALSE(fusion.AreCompatible()); + } + + ld.CreatePreHeaderBlocksIfMissing(); + + { + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +// j & k loop preheaders removed manually +#version 440 core +void main() { + int[10] a; + int[10] b; + int i = 0; + int j = 0; + int k = 0; + // No loop-carried dependences, legal + for (; i < 10; i++) { + a[i] = a[i]*2; + } + for (; j < 10; j++) { + b[j] = a[j]+2; + } + for (; k < 10; k++) { + a[k] = a[k]*2; + } +} + +*/ +TEST_F(FusionLegalTest, AdjacentLoopsNoPreheaders) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %10 "j" + OpName %11 "k" + OpName %25 "a" + OpName %43 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 10 + %23 = OpTypeArray %6 %22 + %24 = OpTypePointer Function %23 + %30 = OpConstant %6 2 + %34 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %11 = OpVariable %7 Function + %25 = OpVariable %24 Function + %43 = OpVariable %24 Function + OpStore %8 %9 + OpStore %10 %9 + OpStore %11 %9 + OpBranch %12 + %12 = OpLabel + %67 = OpPhi %6 %9 %5 %35 %15 + OpLoopMerge %36 %15 None + OpBranch %16 + %16 = OpLabel + %20 = OpSLessThan %19 %67 %18 + OpBranchConditional %20 %13 %36 + %13 = OpLabel + %28 = OpAccessChain %7 %25 %67 + %29 = OpLoad %6 %28 + %31 = OpIMul %6 %29 %30 + %32 = OpAccessChain %7 %25 %67 + OpStore %32 %31 + OpBranch %15 + %15 = OpLabel + %35 = OpIAdd %6 %67 %34 + OpStore %8 %35 + OpBranch %12 + %36 = OpLabel + %68 = OpPhi %6 %9 %16 %51 %39 + OpLoopMerge %52 %39 None + OpBranch %40 + %40 = OpLabel + %42 = OpSLessThan %19 %68 %18 + OpBranchConditional %42 %37 %52 + %37 = OpLabel + %46 = OpAccessChain %7 %25 %68 + %47 = OpLoad %6 %46 + %48 = OpIAdd %6 %47 %30 + %49 = OpAccessChain %7 %43 %68 + OpStore %49 %48 + OpBranch %39 + %39 = OpLabel + %51 = OpIAdd %6 %68 %34 + OpStore %10 %51 + OpBranch %36 + %52 = OpLabel + %70 = OpPhi %6 %9 %40 %66 %55 + OpLoopMerge %54 %55 None + OpBranch %56 + %56 = OpLabel + %58 = OpSLessThan %19 %70 %18 + OpBranchConditional %58 %53 %54 + %53 = OpLabel + %61 = OpAccessChain %7 %25 %70 + %62 = OpLoad %6 %61 + %63 = OpIMul %6 %62 %30 + %64 = OpAccessChain %7 %25 %70 + OpStore %64 %63 + OpBranch %55 + %55 = OpLabel + %66 = OpIAdd %6 %70 %34 + OpStore %11 %66 + OpBranch %52 + %54 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 3u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + { + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_FALSE(fusion.AreCompatible()); + } + + ld.CreatePreHeaderBlocksIfMissing(); + + { + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + std::string checks = R"( +CHECK: [[PHI_0:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_0]] +CHECK-NEXT: OpStore [[STORE_1]] +CHECK: [[PHI_1:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] +CHECK: [[STORE_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI_1]] +CHECK-NEXT: OpStore [[STORE_2]] + )"; + + Match(checks, context.get()); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + std::string checks = R"( +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_0]] +CHECK: [[STORE_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpLoad {{%\w+}} [[LOAD_2]] +CHECK: [[STORE_2:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_2]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + + int sum_0 = 0; + int sum_1 = 0; + + // No loop-carried dependences, legal + for (int i = 0; i < 10; i++) { + sum_0 += a[i]; + } + for (int j = 0; j < 10; j++) { + sum_1 += b[j]; + } + + int total = sum_0 + sum_1; +} + +*/ +TEST_F(FusionLegalTest, IndependentReductions) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "sum_0" + OpName %10 "sum_1" + OpName %11 "i" + OpName %25 "a" + OpName %34 "j" + OpName %42 "b" + OpName %50 "total" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 10 + %23 = OpTypeArray %6 %22 + %24 = OpTypePointer Function %23 + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %11 = OpVariable %7 Function + %25 = OpVariable %24 Function + %34 = OpVariable %7 Function + %42 = OpVariable %24 Function + %50 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpStore %11 %9 + OpBranch %12 + %12 = OpLabel + %57 = OpPhi %6 %9 %5 %30 %15 + %54 = OpPhi %6 %9 %5 %33 %15 + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %20 = OpSLessThan %19 %54 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %27 = OpAccessChain %7 %25 %54 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %57 %28 + OpStore %8 %30 + OpBranch %15 + %15 = OpLabel + %33 = OpIAdd %6 %54 %32 + OpStore %11 %33 + OpBranch %12 + %14 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %58 = OpPhi %6 %9 %14 %47 %38 + %55 = OpPhi %6 %9 %14 %49 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %19 %55 %18 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %44 = OpAccessChain %7 %42 %55 + %45 = OpLoad %6 %44 + %47 = OpIAdd %6 %58 %45 + OpStore %10 %47 + OpBranch %38 + %38 = OpLabel + %49 = OpIAdd %6 %55 %32 + OpStore %34 %49 + OpBranch %35 + %37 = OpLabel + %53 = OpIAdd %6 %57 %58 + OpStore %50 %53 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + std::string checks = R"( +CHECK: [[SUM_0:%\w+]] = OpPhi +CHECK-NEXT: [[SUM_1:%\w+]] = OpPhi +CHECK-NEXT: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] +CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[SUM_0]] [[LOAD_RES_0]] +CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[LOAD_RES_1:%\w+]] = OpLoad {{%\w+}} [[LOAD_1]] +CHECK-NEXT: [[ADD_RES_1:%\w+]] = OpIAdd {{%\w+}} [[SUM_1]] [[LOAD_RES_1]] +CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + + int sum_0 = 0; + int sum_1 = 0; + + // No loop-carried dependences, legal + for (int i = 0; i < 10; i++) { + sum_0 += a[i]; + } + for (int j = 0; j < 10; j++) { + sum_1 += b[j]; + } + + int total = sum_0 + sum_1; +} + +*/ +TEST_F(FusionLegalTest, IndependentReductionsOneLCSSA) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "sum_0" + OpName %10 "sum_1" + OpName %11 "i" + OpName %25 "a" + OpName %34 "j" + OpName %42 "b" + OpName %50 "total" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 10 + %23 = OpTypeArray %6 %22 + %24 = OpTypePointer Function %23 + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %11 = OpVariable %7 Function + %25 = OpVariable %24 Function + %34 = OpVariable %7 Function + %42 = OpVariable %24 Function + %50 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpStore %11 %9 + OpBranch %12 + %12 = OpLabel + %57 = OpPhi %6 %9 %5 %30 %15 + %54 = OpPhi %6 %9 %5 %33 %15 + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %20 = OpSLessThan %19 %54 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %27 = OpAccessChain %7 %25 %54 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %57 %28 + OpStore %8 %30 + OpBranch %15 + %15 = OpLabel + %33 = OpIAdd %6 %54 %32 + OpStore %11 %33 + OpBranch %12 + %14 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %58 = OpPhi %6 %9 %14 %47 %38 + %55 = OpPhi %6 %9 %14 %49 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %19 %55 %18 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %44 = OpAccessChain %7 %42 %55 + %45 = OpLoad %6 %44 + %47 = OpIAdd %6 %58 %45 + OpStore %10 %47 + OpBranch %38 + %38 = OpLabel + %49 = OpIAdd %6 %55 %32 + OpStore %34 %49 + OpBranch %35 + %37 = OpLabel + %53 = OpIAdd %6 %57 %58 + OpStore %50 %53 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopUtils utils_0(context.get(), loops[0]); + utils_0.MakeLoopClosedSSA(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + std::string checks = R"( +CHECK: [[SUM_0:%\w+]] = OpPhi +CHECK-NEXT: [[SUM_1:%\w+]] = OpPhi +CHECK-NEXT: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] +CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[SUM_0]] [[LOAD_RES_0]] +CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[LOAD_RES_1:%\w+]] = OpLoad {{%\w+}} [[LOAD_1]] +CHECK-NEXT: [[ADD_RES_1:%\w+]] = OpIAdd {{%\w+}} [[SUM_1]] [[LOAD_RES_1]] +CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + + int sum_0 = 0; + int sum_1 = 0; + + // No loop-carried dependences, legal + for (int i = 0; i < 10; i++) { + sum_0 += a[i]; + } + for (int j = 0; j < 10; j++) { + sum_1 += b[j]; + } + + int total = sum_0 + sum_1; +} + +*/ +TEST_F(FusionLegalTest, IndependentReductionsBothLCSSA) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "sum_0" + OpName %10 "sum_1" + OpName %11 "i" + OpName %25 "a" + OpName %34 "j" + OpName %42 "b" + OpName %50 "total" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 10 + %23 = OpTypeArray %6 %22 + %24 = OpTypePointer Function %23 + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %11 = OpVariable %7 Function + %25 = OpVariable %24 Function + %34 = OpVariable %7 Function + %42 = OpVariable %24 Function + %50 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpStore %11 %9 + OpBranch %12 + %12 = OpLabel + %57 = OpPhi %6 %9 %5 %30 %15 + %54 = OpPhi %6 %9 %5 %33 %15 + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %20 = OpSLessThan %19 %54 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %27 = OpAccessChain %7 %25 %54 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %57 %28 + OpStore %8 %30 + OpBranch %15 + %15 = OpLabel + %33 = OpIAdd %6 %54 %32 + OpStore %11 %33 + OpBranch %12 + %14 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %58 = OpPhi %6 %9 %14 %47 %38 + %55 = OpPhi %6 %9 %14 %49 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %19 %55 %18 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %44 = OpAccessChain %7 %42 %55 + %45 = OpLoad %6 %44 + %47 = OpIAdd %6 %58 %45 + OpStore %10 %47 + OpBranch %38 + %38 = OpLabel + %49 = OpIAdd %6 %55 %32 + OpStore %34 %49 + OpBranch %35 + %37 = OpLabel + %53 = OpIAdd %6 %57 %58 + OpStore %50 %53 + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopUtils utils_0(context.get(), loops[0]); + utils_0.MakeLoopClosedSSA(); + LoopUtils utils_1(context.get(), loops[1]); + utils_1.MakeLoopClosedSSA(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + std::string checks = R"( +CHECK: [[SUM_0:%\w+]] = OpPhi +CHECK-NEXT: [[SUM_1:%\w+]] = OpPhi +CHECK-NEXT: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] +CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[SUM_0]] [[LOAD_RES_0]] +CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[LOAD_RES_1:%\w+]] = OpLoad {{%\w+}} [[LOAD_1]] +CHECK-NEXT: [[ADD_RES_1:%\w+]] = OpIAdd {{%\w+}} [[SUM_1]] [[LOAD_RES_1]] +CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + + int sum_0 = 0; + + // No loop-carried dependences, legal + for (int i = 0; i < 10; i++) { + sum_0 += a[i]; + } + for (int j = 0; j < 10; j++) { + a[j] = b[j]; + } +} + +*/ +TEST_F(FusionLegalTest, LoadStoreReductionAndNonLoopCarriedDependence) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "sum_0" + OpName %10 "i" + OpName %24 "a" + OpName %33 "j" + OpName %42 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 10 + %22 = OpTypeArray %6 %21 + %23 = OpTypePointer Function %22 + %31 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %24 = OpVariable %23 Function + %33 = OpVariable %7 Function + %42 = OpVariable %23 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + %51 = OpPhi %6 %9 %5 %29 %14 + %49 = OpPhi %6 %9 %5 %32 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %18 %49 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %26 = OpAccessChain %7 %24 %49 + %27 = OpLoad %6 %26 + %29 = OpIAdd %6 %51 %27 + OpStore %8 %29 + OpBranch %14 + %14 = OpLabel + %32 = OpIAdd %6 %49 %31 + OpStore %10 %32 + OpBranch %11 + %13 = OpLabel + OpStore %33 %9 + OpBranch %34 + %34 = OpLabel + %50 = OpPhi %6 %9 %13 %48 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %40 = OpSLessThan %18 %50 %17 + OpBranchConditional %40 %35 %36 + %35 = OpLabel + %44 = OpAccessChain %7 %42 %50 + %45 = OpLoad %6 %44 + %46 = OpAccessChain %7 %24 %50 + OpStore %46 %45 + OpBranch %37 + %37 = OpLabel + %48 = OpIAdd %6 %50 %31 + OpStore %33 %48 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + // TODO: Loop descriptor doesn't return induction variables but all OpPhi + // in the header and LoopDependenceAnalysis falls over. + // EXPECT_TRUE(fusion.IsLegal()); + + // fusion.Fuse(); + } + + { + // LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + // EXPECT_EQ(ld.NumLoops(), 1u); + + // std::string checks = R"( + // CHECK: [[SUM_0:%\w+]] = OpPhi + // CHECK-NEXT: [[PHI:%\w+]] = OpPhi + // CHECK-NEXT: OpLoopMerge + // CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] + // CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] + // CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[SUM_0]] [[LOAD_RES_0]] + // CHECK-NEXT: OpStore {{%\w+}} [[ADD_RES_0]] + // CHECK-NOT: OpPhi + // CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] + // CHECK-NEXT: [[LOAD_RES_1:%\w+]] = OpLoad {{%\w+}} [[LOAD_1]] + // CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] + // CHECK-NEXT: OpStore [[STORE_1]] [[LOAD_RES_1]] + // )"; + + // Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +int x; +void main() { + int[10] a; + int[10] b; + + // Legal. + for (int i = 0; i < 10; i++) { + x += a[i]; + } + for (int j = 0; j < 10; j++) { + b[j] = b[j]+1; + } +} + +*/ +TEST_F(FusionLegalTest, ReductionAndNonLoopCarriedDependence) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %20 "x" + OpName %25 "a" + OpName %34 "j" + OpName %42 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypePointer Private %6 + %20 = OpVariable %19 Private + %21 = OpTypeInt 32 0 + %22 = OpConstant %21 10 + %23 = OpTypeArray %6 %22 + %24 = OpTypePointer Function %23 + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %25 = OpVariable %24 Function + %34 = OpVariable %7 Function + %42 = OpVariable %24 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %51 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %51 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %51 + %28 = OpLoad %6 %27 + %29 = OpLoad %6 %20 + %30 = OpIAdd %6 %29 %28 + OpStore %20 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %51 %32 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %52 = OpPhi %6 %9 %12 %50 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %52 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %42 %52 + %46 = OpLoad %6 %45 + %47 = OpIAdd %6 %46 %32 + %48 = OpAccessChain %7 %42 %52 + OpStore %48 %47 + OpBranch %38 + %38 = OpLabel + %50 = OpIAdd %6 %52 %32 + OpStore %34 %50 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + std::string checks = R"( +CHECK: OpName [[X:%\w+]] "x" +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[LOAD_0:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: [[LOAD_RES_0:%\w+]] = OpLoad {{%\w+}} [[LOAD_0]] +CHECK-NEXT: [[X_LOAD:%\w+]] = OpLoad {{%\w+}} [[X]] +CHECK-NEXT: [[ADD_RES_0:%\w+]] = OpIAdd {{%\w+}} [[X_LOAD]] [[LOAD_RES_0]] +CHECK-NEXT: OpStore [[X]] [[ADD_RES_0]] +CHECK-NOT: OpPhi +CHECK: [[LOAD_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: {{%\w+}} = OpLoad {{%\w+}} [[LOAD_1]] +CHECK: [[STORE_1:%\w+]] = OpAccessChain {{%\w+}} {{%\w+}} [[PHI]] +CHECK-NEXT: OpStore [[STORE_1]] + )"; + + Match(checks, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +struct TestStruct { + int[10] a; + int b; +}; + +void main() { + TestStruct test_0; + TestStruct test_1; + TestStruct test_2; + + test_1.b = 2; + + for (int i = 0; i < 10; i++) { + test_0.a[i] = i; + } + for (int j = 0; j < 10; j++) { + test_2 = test_1; + } +} + +*/ +TEST_F(FusionLegalTest, ArrayInStruct) { + std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %10 "TestStruct" + OpMemberName %10 0 "a" + OpMemberName %10 1 "b" + OpName %12 "test_1" + OpName %17 "i" + OpName %28 "test_0" + OpName %34 "j" + OpName %42 "test_2" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 10 + %9 = OpTypeArray %6 %8 + %10 = OpTypeStruct %9 %6 + %11 = OpTypePointer Function %10 + %13 = OpConstant %6 1 + %14 = OpConstant %6 2 + %15 = OpTypePointer Function %6 + %18 = OpConstant %6 0 + %25 = OpConstant %6 10 + %26 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %11 Function + %17 = OpVariable %15 Function + %28 = OpVariable %11 Function + %34 = OpVariable %15 Function + %42 = OpVariable %11 Function + %16 = OpAccessChain %15 %12 %13 + OpStore %16 %14 + OpStore %17 %18 + OpBranch %19 + %19 = OpLabel + %46 = OpPhi %6 %18 %5 %33 %22 + OpLoopMerge %21 %22 None + OpBranch %23 + %23 = OpLabel + %27 = OpSLessThan %26 %46 %25 + OpBranchConditional %27 %20 %21 + %20 = OpLabel + %31 = OpAccessChain %15 %28 %18 %46 + OpStore %31 %46 + OpBranch %22 + %22 = OpLabel + %33 = OpIAdd %6 %46 %13 + OpStore %17 %33 + OpBranch %19 + %21 = OpLabel + OpStore %34 %18 + OpBranch %35 + %35 = OpLabel + %47 = OpPhi %6 %18 %21 %45 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %26 %47 %25 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %43 = OpLoad %10 %12 + OpStore %42 %43 + OpBranch %38 + %38 = OpLabel + %45 = OpIAdd %6 %47 %13 + OpStore %34 %45 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 2u); + + auto loops = ld.GetLoopsInBinaryLayoutOrder(); + + LoopFusion fusion(context.get(), loops[0], loops[1]); + EXPECT_TRUE(fusion.AreCompatible()); + EXPECT_TRUE(fusion.IsLegal()); + + fusion.Fuse(); + } + + { + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), 1u); + + // clang-format off + std::string checks = R"( +CHECK: OpName [[TEST_1:%\w+]] "test_1" +CHECK: OpName [[TEST_0:%\w+]] "test_0" +CHECK: OpName [[TEST_2:%\w+]] "test_2" +CHECK: [[PHI:%\w+]] = OpPhi +CHECK-NEXT: OpLoopMerge +CHECK: [[TEST_0_STORE:%\w+]] = OpAccessChain {{%\w+}} [[TEST_0]] {{%\w+}} {{%\w+}} +CHECK-NEXT: OpStore [[TEST_0_STORE]] [[PHI]] +CHECK-NOT: OpPhi +CHECK: [[TEST_1_LOAD:%\w+]] = OpLoad {{%\w+}} [[TEST_1]] +CHECK: OpStore [[TEST_2]] [[TEST_1_LOAD]] + )"; + // clang-format on + + Match(checks, context.get()); + } +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/fusion_pass.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/fusion_pass.cpp new file mode 100644 index 0000000..9493923 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/fusion_pass.cpp @@ -0,0 +1,717 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using FusionPassTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + for (int i = 0; i < 10; i++) { + a[i] = a[i]*2; + } + for (int i = 0; i < 10; i++) { + b[i] = a[i]+2; + } +} + +*/ +TEST_F(FusionPassTest, SimpleFusion) { + const std::string text = R"( +; CHECK: OpPhi +; CHECK: OpLoad +; CHECK: OpStore +; CHECK-NOT: OpPhi +; CHECK: OpLoad +; CHECK: OpStore + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %34 "i" + OpName %42 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %28 = OpConstant %6 2 + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %51 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %51 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpAccessChain %7 %23 %51 + %27 = OpLoad %6 %26 + %29 = OpIMul %6 %27 %28 + %30 = OpAccessChain %7 %23 %51 + OpStore %30 %29 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %51 %32 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %52 = OpPhi %6 %9 %12 %50 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %52 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %23 %52 + %46 = OpLoad %6 %45 + %47 = OpIAdd %6 %46 %28 + %48 = OpAccessChain %7 %42 %52 + OpStore %48 %47 + OpBranch %38 + %38 = OpLabel + %50 = OpIAdd %6 %52 %32 + OpStore %34 %50 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true, 20); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + for (int i = 0; i < 10; i++) { + a[i] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i] + 2; + } + for (int i = 0; i < 10; i++) { + b[i] = c[i] + 10; + } +} + +*/ +TEST_F(FusionPassTest, ThreeLoopsFused) { + const std::string text = R"( +; CHECK: OpPhi +; CHECK: OpLoad +; CHECK: OpStore +; CHECK-NOT: OpPhi +; CHECK: OpLoad +; CHECK: OpStore +; CHECK-NOT: OpPhi +; CHECK: OpLoad +; CHECK: OpStore + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "b" + OpName %34 "i" + OpName %42 "c" + OpName %52 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 1 + %47 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + %52 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %68 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %68 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %68 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %28 %29 + %31 = OpAccessChain %7 %23 %68 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %68 %29 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %69 = OpPhi %6 %9 %12 %51 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %69 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %23 %69 + %46 = OpLoad %6 %45 + %48 = OpIAdd %6 %46 %47 + %49 = OpAccessChain %7 %42 %69 + OpStore %49 %48 + OpBranch %38 + %38 = OpLabel + %51 = OpIAdd %6 %69 %29 + OpStore %34 %51 + OpBranch %35 + %37 = OpLabel + OpStore %52 %9 + OpBranch %53 + %53 = OpLabel + %70 = OpPhi %6 %9 %37 %67 %56 + OpLoopMerge %55 %56 None + OpBranch %57 + %57 = OpLabel + %59 = OpSLessThan %17 %70 %16 + OpBranchConditional %59 %54 %55 + %54 = OpLabel + %62 = OpAccessChain %7 %42 %70 + %63 = OpLoad %6 %62 + %64 = OpIAdd %6 %63 %16 + %65 = OpAccessChain %7 %25 %70 + OpStore %65 %64 + OpBranch %56 + %56 = OpLabel + %67 = OpIAdd %6 %70 %29 + OpStore %52 %67 + OpBranch %53 + %55 = OpLabel + OpReturn + OpFunctionEnd + + )"; + + SinglePassRunAndMatch(text, true, 20); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10][10] a; + int[10][10] b; + int[10][10] c; + // Legal both + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + c[i][j] = a[i][j] + 2; + } + } + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + b[i][j] = c[i][j] + 10; + } + } +} + +*/ +TEST_F(FusionPassTest, NestedLoopsFused) { + const std::string text = R"( +; CHECK: OpPhi +; CHECK: OpPhi +; CHECK: OpLoad +; CHECK: OpStore +; CHECK-NOT: OpPhi +; CHECK: OpLoad +; CHECK: OpStore + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %19 "j" + OpName %32 "c" + OpName %35 "a" + OpName %48 "i" + OpName %56 "j" + OpName %64 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 10 + %29 = OpTypeArray %6 %28 + %30 = OpTypeArray %29 %28 + %31 = OpTypePointer Function %30 + %40 = OpConstant %6 2 + %44 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %31 Function + %35 = OpVariable %31 Function + %48 = OpVariable %7 Function + %56 = OpVariable %7 Function + %64 = OpVariable %31 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %77 = OpPhi %6 %9 %5 %47 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %77 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + %81 = OpPhi %6 %9 %11 %45 %23 + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %26 = OpSLessThan %17 %81 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + %38 = OpAccessChain %7 %35 %77 %81 + %39 = OpLoad %6 %38 + %41 = OpIAdd %6 %39 %40 + %42 = OpAccessChain %7 %32 %77 %81 + OpStore %42 %41 + OpBranch %23 + %23 = OpLabel + %45 = OpIAdd %6 %81 %44 + OpStore %19 %45 + OpBranch %20 + %22 = OpLabel + OpBranch %13 + %13 = OpLabel + %47 = OpIAdd %6 %77 %44 + OpStore %8 %47 + OpBranch %10 + %12 = OpLabel + OpStore %48 %9 + OpBranch %49 + %49 = OpLabel + %78 = OpPhi %6 %9 %12 %76 %52 + OpLoopMerge %51 %52 None + OpBranch %53 + %53 = OpLabel + %55 = OpSLessThan %17 %78 %16 + OpBranchConditional %55 %50 %51 + %50 = OpLabel + OpStore %56 %9 + OpBranch %57 + %57 = OpLabel + %79 = OpPhi %6 %9 %50 %74 %60 + OpLoopMerge %59 %60 None + OpBranch %61 + %61 = OpLabel + %63 = OpSLessThan %17 %79 %16 + OpBranchConditional %63 %58 %59 + %58 = OpLabel + %69 = OpAccessChain %7 %32 %78 %79 + %70 = OpLoad %6 %69 + %71 = OpIAdd %6 %70 %16 + %72 = OpAccessChain %7 %64 %78 %79 + OpStore %72 %71 + OpBranch %60 + %60 = OpLabel + %74 = OpIAdd %6 %79 %44 + OpStore %56 %74 + OpBranch %57 + %59 = OpLabel + OpBranch %52 + %52 = OpLabel + %76 = OpIAdd %6 %78 %44 + OpStore %48 %76 + OpBranch %49 + %51 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true, 20); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + // Can't fuse, different step + for (int i = 0; i < 10; i++) {} + for (int j = 0; j < 10; j=j+2) {} +} + +*/ +TEST_F(FusionPassTest, Incompatible) { + const std::string text = R"( +; CHECK: OpPhi +; CHECK-NEXT: OpLoopMerge +; CHECK: OpPhi +; CHECK-NEXT: OpLoopMerge + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %22 "j" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %31 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %33 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %33 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %33 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %34 = OpPhi %6 %9 %12 %32 %26 + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %34 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %32 = OpIAdd %6 %34 %31 + OpStore %22 %32 + OpBranch %23 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true, 20); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + int[10] c; + // Illegal, loop-independent dependence will become a + // backward loop-carried antidependence + for (int i = 0; i < 10; i++) { + a[i] = b[i] + 1; + } + for (int i = 0; i < 10; i++) { + c[i] = a[i+1] + 2; + } +} + +*/ +TEST_F(FusionPassTest, Illegal) { + std::string text = R"( +; CHECK: OpPhi +; CHECK-NEXT: OpLoopMerge +; CHECK: OpLoad +; CHECK: OpStore +; CHECK: OpPhi +; CHECK-NEXT: OpLoopMerge +; CHECK: OpLoad +; CHECK: OpStore + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %25 "b" + OpName %34 "i" + OpName %42 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %29 = OpConstant %6 1 + %48 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %25 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %53 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %53 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpAccessChain %7 %25 %53 + %28 = OpLoad %6 %27 + %30 = OpIAdd %6 %28 %29 + %31 = OpAccessChain %7 %23 %53 + OpStore %31 %30 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %53 %29 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %54 = OpPhi %6 %9 %12 %52 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %54 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpIAdd %6 %54 %29 + %46 = OpAccessChain %7 %23 %45 + %47 = OpLoad %6 %46 + %49 = OpIAdd %6 %47 %48 + %50 = OpAccessChain %7 %42 %54 + OpStore %50 %49 + OpBranch %38 + %38 = OpLabel + %52 = OpIAdd %6 %54 %29 + OpStore %34 %52 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true, 20); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +void main() { + int[10] a; + int[10] b; + for (int i = 0; i < 10; i++) { + a[i] = a[i]*2; + } + for (int i = 0; i < 10; i++) { + b[i] = a[i]+2; + } +} + +*/ +TEST_F(FusionPassTest, TooManyRegisters) { + const std::string text = R"( +; CHECK: OpPhi +; CHECK-NEXT: OpLoopMerge +; CHECK: OpLoad +; CHECK: OpStore +; CHECK: OpPhi +; CHECK-NEXT: OpLoopMerge +; CHECK: OpLoad +; CHECK: OpStore + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + OpName %8 "i" + OpName %23 "a" + OpName %34 "i" + OpName %42 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 10 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %28 = OpConstant %6 2 + %32 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %23 = OpVariable %22 Function + %34 = OpVariable %7 Function + %42 = OpVariable %22 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %51 = OpPhi %6 %9 %5 %33 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %51 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpAccessChain %7 %23 %51 + %27 = OpLoad %6 %26 + %29 = OpIMul %6 %27 %28 + %30 = OpAccessChain %7 %23 %51 + OpStore %30 %29 + OpBranch %13 + %13 = OpLabel + %33 = OpIAdd %6 %51 %32 + OpStore %8 %33 + OpBranch %10 + %12 = OpLabel + OpStore %34 %9 + OpBranch %35 + %35 = OpLabel + %52 = OpPhi %6 %9 %12 %50 %38 + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %41 = OpSLessThan %17 %52 %16 + OpBranchConditional %41 %36 %37 + %36 = OpLabel + %45 = OpAccessChain %7 %23 %52 + %46 = OpLoad %6 %45 + %47 = OpIAdd %6 %46 %28 + %48 = OpAccessChain %7 %42 %52 + OpStore %48 %47 + OpBranch %38 + %38 = OpLabel + %50 = OpIAdd %6 %52 %32 + OpStore %34 %50 + OpBranch %35 + %37 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true, 5); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/hoist_all_loop_types.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_all_loop_types.cpp new file mode 100644 index 0000000..27e0a0d --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_all_loop_types.cpp @@ -0,0 +1,285 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/licm_pass.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Tests that all loop types are handled appropriately by the LICM pass. + + Generated from the following GLSL fragment shader +--eliminate-local-multi-store has also been run on the spv binary +#version 440 core +void main(){ + int i_1 = 0; + for (i_1 = 0; i_1 < 10; i_1++) { + } + int i_2 = 0; + while (i_2 < 10) { + i_2++; + } + int i_3 = 0; + do { + i_3++; + } while (i_3 < 10); + int hoist = 0; + int i_4 = 0; + int i_5 = 0; + int i_6 = 0; + for (i_4 = 0; i_4 < 10; i_4++) { + while (i_5 < 10) { + do { + hoist = i_1 + i_2 + i_3; + i_6++; + } while (i_6 < 10); + i_5++; + } + } +} +*/ +TEST_F(PassClassTest, AllLoopTypes) { + const std::string before_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_1 = OpConstant %int 1 +%main = OpFunction %void None %4 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +%13 = OpPhi %int %int_0 %11 %14 %15 +OpLoopMerge %16 %15 None +OpBranch %17 +%17 = OpLabel +%18 = OpSLessThan %bool %13 %int_10 +OpBranchConditional %18 %19 %16 +%19 = OpLabel +OpBranch %15 +%15 = OpLabel +%14 = OpIAdd %int %13 %int_1 +OpBranch %12 +%16 = OpLabel +OpBranch %20 +%20 = OpLabel +%21 = OpPhi %int %int_0 %16 %22 %23 +OpLoopMerge %24 %23 None +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThan %bool %21 %int_10 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%22 = OpIAdd %int %21 %int_1 +OpBranch %23 +%23 = OpLabel +OpBranch %20 +%24 = OpLabel +OpBranch %28 +%28 = OpLabel +%29 = OpPhi %int %int_0 %24 %30 %31 +OpLoopMerge %32 %31 None +OpBranch %33 +%33 = OpLabel +%30 = OpIAdd %int %29 %int_1 +OpBranch %31 +%31 = OpLabel +%34 = OpSLessThan %bool %30 %int_10 +OpBranchConditional %34 %28 %32 +%32 = OpLabel +OpBranch %35 +%35 = OpLabel +%36 = OpPhi %int %int_0 %32 %37 %38 +%39 = OpPhi %int %int_0 %32 %40 %38 +%41 = OpPhi %int %int_0 %32 %42 %38 +%43 = OpPhi %int %int_0 %32 %44 %38 +OpLoopMerge %45 %38 None +OpBranch %46 +%46 = OpLabel +%47 = OpSLessThan %bool %39 %int_10 +OpBranchConditional %47 %48 %45 +%48 = OpLabel +OpBranch %49 +%49 = OpLabel +%37 = OpPhi %int %36 %48 %50 %51 +%42 = OpPhi %int %41 %48 %52 %51 +%44 = OpPhi %int %43 %48 %53 %51 +OpLoopMerge %54 %51 None +OpBranch %55 +%55 = OpLabel +%56 = OpSLessThan %bool %42 %int_10 +OpBranchConditional %56 %57 %54 +%57 = OpLabel +OpBranch %58 +%58 = OpLabel +%59 = OpPhi %int %37 %57 %50 %60 +%61 = OpPhi %int %44 %57 %53 %60 +OpLoopMerge %62 %60 None +OpBranch %63 +%63 = OpLabel +%64 = OpIAdd %int %13 %21 +%50 = OpIAdd %int %64 %30 +%53 = OpIAdd %int %61 %int_1 +OpBranch %60 +%60 = OpLabel +%65 = OpSLessThan %bool %53 %int_10 +OpBranchConditional %65 %58 %62 +%62 = OpLabel +%52 = OpIAdd %int %42 %int_1 +OpBranch %51 +%51 = OpLabel +OpBranch %49 +%54 = OpLabel +OpBranch %38 +%38 = OpLabel +%40 = OpIAdd %int %39 %int_1 +OpBranch %35 +%45 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_1 = OpConstant %int 1 +%main = OpFunction %void None %4 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +%13 = OpPhi %int %int_0 %11 %14 %15 +OpLoopMerge %16 %15 None +OpBranch %17 +%17 = OpLabel +%18 = OpSLessThan %bool %13 %int_10 +OpBranchConditional %18 %19 %16 +%19 = OpLabel +OpBranch %15 +%15 = OpLabel +%14 = OpIAdd %int %13 %int_1 +OpBranch %12 +%16 = OpLabel +OpBranch %20 +%20 = OpLabel +%21 = OpPhi %int %int_0 %16 %22 %23 +OpLoopMerge %24 %23 None +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThan %bool %21 %int_10 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%22 = OpIAdd %int %21 %int_1 +OpBranch %23 +%23 = OpLabel +OpBranch %20 +%24 = OpLabel +OpBranch %28 +%28 = OpLabel +%29 = OpPhi %int %int_0 %24 %30 %31 +OpLoopMerge %32 %31 None +OpBranch %33 +%33 = OpLabel +%30 = OpIAdd %int %29 %int_1 +OpBranch %31 +%31 = OpLabel +%34 = OpSLessThan %bool %30 %int_10 +OpBranchConditional %34 %28 %32 +%32 = OpLabel +%64 = OpIAdd %int %13 %21 +%50 = OpIAdd %int %64 %30 +OpBranch %35 +%35 = OpLabel +%36 = OpPhi %int %int_0 %32 %37 %38 +%39 = OpPhi %int %int_0 %32 %40 %38 +%41 = OpPhi %int %int_0 %32 %42 %38 +%43 = OpPhi %int %int_0 %32 %44 %38 +OpLoopMerge %45 %38 None +OpBranch %46 +%46 = OpLabel +%47 = OpSLessThan %bool %39 %int_10 +OpBranchConditional %47 %48 %45 +%48 = OpLabel +OpBranch %49 +%49 = OpLabel +%37 = OpPhi %int %36 %48 %50 %51 +%42 = OpPhi %int %41 %48 %52 %51 +%44 = OpPhi %int %43 %48 %53 %51 +OpLoopMerge %54 %51 None +OpBranch %55 +%55 = OpLabel +%56 = OpSLessThan %bool %42 %int_10 +OpBranchConditional %56 %57 %54 +%57 = OpLabel +OpBranch %58 +%58 = OpLabel +%59 = OpPhi %int %37 %57 %50 %60 +%61 = OpPhi %int %44 %57 %53 %60 +OpLoopMerge %62 %60 None +OpBranch %63 +%63 = OpLabel +%53 = OpIAdd %int %61 %int_1 +OpBranch %60 +%60 = OpLabel +%65 = OpSLessThan %bool %53 %int_10 +OpBranchConditional %65 %58 %62 +%62 = OpLabel +%52 = OpIAdd %int %42 %int_1 +OpBranch %51 +%51 = OpLabel +OpBranch %49 +%54 = OpLabel +OpBranch %38 +%38 = OpLabel +%40 = OpIAdd %int %39 %int_1 +OpBranch %35 +%45 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_hoist, after_hoist, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/hoist_double_nested_loops.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_double_nested_loops.cpp new file mode 100644 index 0000000..ea19496 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_double_nested_loops.cpp @@ -0,0 +1,162 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/licm_pass.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Tests that the LICM pass will move invariants through multiple loops + + Generated from the following GLSL fragment shader +--eliminate-local-multi-store has also been run on the spv binary +#version 440 core +void main(){ + int a = 2; + int b = 1; + int hoist = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // hoist 'hoist = a - b' out of both loops + hoist = a - b; + } + } +} +*/ +TEST_F(PassClassTest, NestedDoubleHoist) { + const std::string before_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_2 = OpConstant %int 2 +%int_1 = OpConstant %int 1 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%12 = OpUndef %int +%main = OpFunction %void None %4 +%13 = OpLabel +OpBranch %14 +%14 = OpLabel +%15 = OpPhi %int %int_0 %13 %16 %17 +%18 = OpPhi %int %int_0 %13 %19 %17 +%20 = OpPhi %int %12 %13 %21 %17 +OpLoopMerge %22 %17 None +OpBranch %23 +%23 = OpLabel +%24 = OpSLessThan %bool %18 %int_10 +OpBranchConditional %24 %25 %22 +%25 = OpLabel +OpBranch %26 +%26 = OpLabel +%16 = OpPhi %int %15 %25 %27 %28 +%21 = OpPhi %int %int_0 %25 %29 %28 +OpLoopMerge %30 %28 None +OpBranch %31 +%31 = OpLabel +%32 = OpSLessThan %bool %21 %int_10 +OpBranchConditional %32 %33 %30 +%33 = OpLabel +%27 = OpISub %int %int_2 %int_1 +OpBranch %28 +%28 = OpLabel +%29 = OpIAdd %int %21 %int_1 +OpBranch %26 +%30 = OpLabel +OpBranch %17 +%17 = OpLabel +%19 = OpIAdd %int %18 %int_1 +OpBranch %14 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_2 = OpConstant %int 2 +%int_1 = OpConstant %int 1 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%12 = OpUndef %int +%main = OpFunction %void None %4 +%13 = OpLabel +%27 = OpISub %int %int_2 %int_1 +OpBranch %14 +%14 = OpLabel +%15 = OpPhi %int %int_0 %13 %16 %17 +%18 = OpPhi %int %int_0 %13 %19 %17 +%20 = OpPhi %int %12 %13 %21 %17 +OpLoopMerge %22 %17 None +OpBranch %23 +%23 = OpLabel +%24 = OpSLessThan %bool %18 %int_10 +OpBranchConditional %24 %25 %22 +%25 = OpLabel +OpBranch %26 +%26 = OpLabel +%16 = OpPhi %int %15 %25 %27 %28 +%21 = OpPhi %int %int_0 %25 %29 %28 +OpLoopMerge %30 %28 None +OpBranch %31 +%31 = OpLabel +%32 = OpSLessThan %bool %21 %int_10 +OpBranchConditional %32 %33 %30 +%33 = OpLabel +OpBranch %28 +%28 = OpLabel +%29 = OpIAdd %int %21 %int_1 +OpBranch %26 +%30 = OpLabel +OpBranch %17 +%17 = OpLabel +%19 = OpIAdd %int %18 %int_1 +OpBranch %14 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_hoist, after_hoist, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/hoist_from_independent_loops.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_from_independent_loops.cpp new file mode 100644 index 0000000..abc79e3 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_from_independent_loops.cpp @@ -0,0 +1,201 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/licm_pass.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Tests that the LICM pass will analyse multiple independent loops in a function + + Generated from the following GLSL fragment shader +--eliminate-local-multi-store has also been run on the spv binary +#version 440 core +void main(){ + int a = 1; + int b = 2; + int hoist = 0; + for (int i = 0; i < 10; i++) { + // invariant + hoist = a + b; + } + for (int i = 0; i < 10; i++) { + // invariant + hoist = a + b; + } + int c = 1; + int d = 2; + int hoist2 = 0; + for (int i = 0; i < 10; i++) { + // invariant + hoist2 = c + d; + } +} +*/ +TEST_F(PassClassTest, HoistFromIndependentLoops) { + const std::string before_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%main = OpFunction %void None %4 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +%14 = OpPhi %int %int_0 %12 %15 %16 +%17 = OpPhi %int %int_0 %12 %18 %16 +OpLoopMerge %19 %16 None +OpBranch %20 +%20 = OpLabel +%21 = OpSLessThan %bool %17 %int_10 +OpBranchConditional %21 %22 %19 +%22 = OpLabel +%15 = OpIAdd %int %int_1 %int_2 +OpBranch %16 +%16 = OpLabel +%18 = OpIAdd %int %17 %int_1 +OpBranch %13 +%19 = OpLabel +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %int %14 %19 %25 %26 +%27 = OpPhi %int %int_0 %19 %28 %26 +OpLoopMerge %29 %26 None +OpBranch %30 +%30 = OpLabel +%31 = OpSLessThan %bool %27 %int_10 +OpBranchConditional %31 %32 %29 +%32 = OpLabel +%25 = OpIAdd %int %int_1 %int_2 +OpBranch %26 +%26 = OpLabel +%28 = OpIAdd %int %27 %int_1 +OpBranch %23 +%29 = OpLabel +OpBranch %33 +%33 = OpLabel +%34 = OpPhi %int %int_0 %29 %35 %36 +%37 = OpPhi %int %int_0 %29 %38 %36 +OpLoopMerge %39 %36 None +OpBranch %40 +%40 = OpLabel +%41 = OpSLessThan %bool %37 %int_10 +OpBranchConditional %41 %42 %39 +%42 = OpLabel +%35 = OpIAdd %int %int_1 %int_2 +OpBranch %36 +%36 = OpLabel +%38 = OpIAdd %int %37 %int_1 +OpBranch %33 +%39 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%main = OpFunction %void None %4 +%12 = OpLabel +%15 = OpIAdd %int %int_1 %int_2 +OpBranch %13 +%13 = OpLabel +%14 = OpPhi %int %int_0 %12 %15 %16 +%17 = OpPhi %int %int_0 %12 %18 %16 +OpLoopMerge %19 %16 None +OpBranch %20 +%20 = OpLabel +%21 = OpSLessThan %bool %17 %int_10 +OpBranchConditional %21 %22 %19 +%22 = OpLabel +OpBranch %16 +%16 = OpLabel +%18 = OpIAdd %int %17 %int_1 +OpBranch %13 +%19 = OpLabel +%25 = OpIAdd %int %int_1 %int_2 +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %int %14 %19 %25 %26 +%27 = OpPhi %int %int_0 %19 %28 %26 +OpLoopMerge %29 %26 None +OpBranch %30 +%30 = OpLabel +%31 = OpSLessThan %bool %27 %int_10 +OpBranchConditional %31 %32 %29 +%32 = OpLabel +OpBranch %26 +%26 = OpLabel +%28 = OpIAdd %int %27 %int_1 +OpBranch %23 +%29 = OpLabel +%35 = OpIAdd %int %int_1 %int_2 +OpBranch %33 +%33 = OpLabel +%34 = OpPhi %int %int_0 %29 %35 %36 +%37 = OpPhi %int %int_0 %29 %38 %36 +OpLoopMerge %39 %36 None +OpBranch %40 +%40 = OpLabel +%41 = OpSLessThan %bool %37 %int_10 +OpBranchConditional %41 %42 %39 +%42 = OpLabel +OpBranch %36 +%36 = OpLabel +%38 = OpIAdd %int %37 %int_1 +OpBranch %33 +%39 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_hoist, after_hoist, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/hoist_simple_case.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_simple_case.cpp new file mode 100644 index 0000000..e973d9d --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_simple_case.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/licm_pass.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + A simple test for the LICM pass + + Generated from the following GLSL fragment shader +--eliminate-local-multi-store has also been run on the spv binary +#version 440 core +void main(){ + int a = 1; + int b = 2; + int hoist = 0; + for (int i = 0; i < 10; i++) { + // invariant + hoist = a + b; + } +} +*/ +TEST_F(PassClassTest, SimpleHoist) { + const std::string before_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%main = OpFunction %void None %4 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +%14 = OpPhi %int %int_0 %12 %15 %16 +%17 = OpPhi %int %int_0 %12 %18 %16 +OpLoopMerge %19 %16 None +OpBranch %20 +%20 = OpLabel +%21 = OpSLessThan %bool %17 %int_10 +OpBranchConditional %21 %22 %19 +%22 = OpLabel +%15 = OpIAdd %int %int_1 %int_2 +OpBranch %16 +%16 = OpLabel +%18 = OpIAdd %int %17 %int_1 +OpBranch %13 +%19 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%main = OpFunction %void None %4 +%12 = OpLabel +%15 = OpIAdd %int %int_1 %int_2 +OpBranch %13 +%13 = OpLabel +%14 = OpPhi %int %int_0 %12 %15 %16 +%17 = OpPhi %int %int_0 %12 %18 %16 +OpLoopMerge %19 %16 None +OpBranch %20 +%20 = OpLabel +%21 = OpSLessThan %bool %17 %int_10 +OpBranchConditional %21 %22 %19 +%22 = OpLabel +OpBranch %16 +%16 = OpLabel +%18 = OpIAdd %int %17 %int_1 +OpBranch %13 +%19 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_hoist, after_hoist, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/hoist_single_nested_loops.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_single_nested_loops.cpp new file mode 100644 index 0000000..056f3f0 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_single_nested_loops.cpp @@ -0,0 +1,209 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/licm_pass.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; + +using PassClassTest = PassTest<::testing::Test>; + +/* + Tests that the LICM pass will detect an move an invariant from a nested loop, + but not it's parent loop + + Generated from the following GLSL fragment shader +--eliminate-local-multi-store has also been run on the spv binary +#version 440 core +void main(){ + int a = 2; + int hoist = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + // hoist 'hoist = a - i' out of j loop, but not i loop + hoist = a - i; + } + } +} +*/ +TEST_F(PassClassTest, NestedSingleHoist) { + const std::string before_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_2 = OpConstant %int 2 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_1 = OpConstant %int 1 +%12 = OpUndef %int +%main = OpFunction %void None %4 +%13 = OpLabel +OpBranch %14 +%14 = OpLabel +%15 = OpPhi %int %int_0 %13 %16 %17 +%18 = OpPhi %int %int_0 %13 %19 %17 +%20 = OpPhi %int %12 %13 %21 %17 +OpLoopMerge %22 %17 None +OpBranch %23 +%23 = OpLabel +%24 = OpSLessThan %bool %18 %int_10 +OpBranchConditional %24 %25 %22 +%25 = OpLabel +OpBranch %26 +%26 = OpLabel +%16 = OpPhi %int %15 %25 %27 %28 +%21 = OpPhi %int %int_0 %25 %29 %28 +OpLoopMerge %30 %28 None +OpBranch %31 +%31 = OpLabel +%32 = OpSLessThan %bool %21 %int_10 +OpBranchConditional %32 %33 %30 +%33 = OpLabel +%27 = OpISub %int %int_2 %18 +OpBranch %28 +%28 = OpLabel +%29 = OpIAdd %int %21 %int_1 +OpBranch %26 +%30 = OpLabel +OpBranch %17 +%17 = OpLabel +%19 = OpIAdd %int %18 %int_1 +OpBranch %14 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after_hoist = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_2 = OpConstant %int 2 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_1 = OpConstant %int 1 +%12 = OpUndef %int +%main = OpFunction %void None %4 +%13 = OpLabel +OpBranch %14 +%14 = OpLabel +%15 = OpPhi %int %int_0 %13 %16 %17 +%18 = OpPhi %int %int_0 %13 %19 %17 +%20 = OpPhi %int %12 %13 %21 %17 +OpLoopMerge %22 %17 None +OpBranch %23 +%23 = OpLabel +%24 = OpSLessThan %bool %18 %int_10 +OpBranchConditional %24 %25 %22 +%25 = OpLabel +%27 = OpISub %int %int_2 %18 +OpBranch %26 +%26 = OpLabel +%16 = OpPhi %int %15 %25 %27 %28 +%21 = OpPhi %int %int_0 %25 %29 %28 +OpLoopMerge %30 %28 None +OpBranch %31 +%31 = OpLabel +%32 = OpSLessThan %bool %21 %int_10 +OpBranchConditional %32 %33 %30 +%33 = OpLabel +OpBranch %28 +%28 = OpLabel +%29 = OpIAdd %int %21 %int_1 +OpBranch %26 +%30 = OpLabel +OpBranch %17 +%17 = OpLabel +%19 = OpIAdd %int %18 %int_1 +OpBranch %14 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_hoist, after_hoist, true); +} + +TEST_F(PassClassTest, PreHeaderIsAlsoHeader) { + // Move OpSLessThan out of the inner loop. The preheader for the inner loop + // is the header of the outer loop. The loop merge should not be separated + // from the branch in that block. + const std::string text = R"( + ; CHECK: OpFunction + ; CHECK-NEXT: OpLabel + ; CHECK-NEXT: OpBranch [[header:%\w+]] + ; CHECK: [[header]] = OpLabel + ; CHECK-NEXT: OpSLessThan %bool %int_1 %int_1 + ; CHECK-NEXT: OpLoopMerge + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %bool = OpTypeBool + %2 = OpFunction %void None %4 + %18 = OpLabel + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %25 = OpSLessThan %bool %int_1 %int_1 + OpLoopMerge %26 %27 None + OpBranchConditional %25 %27 %26 + %27 = OpLabel + OpBranch %24 + %26 = OpLabel + OpBranch %22 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/hoist_without_preheader.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_without_preheader.cpp new file mode 100644 index 0000000..2e34b01 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/hoist_without_preheader.cpp @@ -0,0 +1,197 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/licm_pass.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* + Tests that the LICM pass will generate a preheader when one is not present + + Generated from the following GLSL fragment shader +--eliminate-local-multi-store has also been run on the spv binary +#version 440 core +void main(){ + int a = 1; + int b = 2; + int hoist = 0; + for (int i = 0; i < 10; i++) { + if (i == 5) { + break; + } + } + for (int i = 0; i < 10; i++) { + hoist = a + b; + } +} +*/ +TEST_F(PassClassTest, HoistWithoutPreheader) { + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_5 = OpConstant %int 5 +%main = OpFunction %void None %4 +%13 = OpLabel +OpBranch %14 +%14 = OpLabel +%15 = OpPhi %int %int_0 %13 %16 %17 +; CHECK: OpLoopMerge [[preheader:%\w+]] +OpLoopMerge %25 %17 None +OpBranch %19 +%19 = OpLabel +%20 = OpSLessThan %bool %15 %int_10 +OpBranchConditional %20 %21 %25 +%21 = OpLabel +%22 = OpIEqual %bool %15 %int_5 +OpSelectionMerge %23 None +OpBranchConditional %22 %24 %23 +%24 = OpLabel +OpBranch %25 +%23 = OpLabel +OpBranch %17 +%17 = OpLabel +%16 = OpIAdd %int %15 %int_1 +OpBranch %14 +; Check that we hoisted the code to the preheader +; CHECK: [[preheader]] = OpLabel +; CHECK-NEXT: OpPhi +; CHECK-NEXT: OpPhi +; CHECK-NEXT: OpIAdd +; CHECK-NEXT: OpBranch [[header:%\w+]] +; CHECK: [[header]] = OpLabel +; CHECK-NEXT: OpPhi +; CHECK-NEXT: OpPhi +; CHECK: OpLoopMerge +%25 = OpLabel +%26 = OpPhi %int %int_0 %24 %int_0 %19 %27 %28 +%29 = OpPhi %int %int_0 %24 %int_0 %19 %30 %28 +OpLoopMerge %31 %28 None +OpBranch %32 +%32 = OpLabel +%33 = OpSLessThan %bool %29 %int_10 +OpBranchConditional %33 %34 %31 +%34 = OpLabel +%27 = OpIAdd %int %int_1 %int_2 +OpBranch %28 +%28 = OpLabel +%30 = OpIAdd %int %29 %int_1 +OpBranch %25 +%31 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(PassClassTest, HoistWithoutPreheaderAtIdBound) { + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 440 +OpName %main "main" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_5 = OpConstant %int 5 +%main = OpFunction %void None %4 +%13 = OpLabel +OpBranch %14 +%14 = OpLabel +%15 = OpPhi %int %int_0 %13 %16 %17 +OpLoopMerge %25 %17 None +OpBranch %19 +%19 = OpLabel +%20 = OpSLessThan %bool %15 %int_10 +OpBranchConditional %20 %21 %25 +%21 = OpLabel +%22 = OpIEqual %bool %15 %int_5 +OpSelectionMerge %23 None +OpBranchConditional %22 %24 %23 +%24 = OpLabel +OpBranch %25 +%23 = OpLabel +OpBranch %17 +%17 = OpLabel +%16 = OpIAdd %int %15 %int_1 +OpBranch %14 +%25 = OpLabel +%26 = OpPhi %int %int_0 %24 %int_0 %19 %27 %28 +%29 = OpPhi %int %int_0 %24 %int_0 %19 %30 %28 +OpLoopMerge %31 %28 None +OpBranch %32 +%32 = OpLabel +%33 = OpSLessThan %bool %29 %int_10 +OpBranchConditional %33 %34 %31 +%34 = OpLabel +%27 = OpIAdd %int %int_1 %int_2 +OpBranch %28 +%28 = OpLabel +%30 = OpIAdd %int %29 %int_1 +OpBranch %25 +%31 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound); + + auto pass = MakeUnique(); + auto result = pass->Run(context.get()); + EXPECT_EQ(result, Pass::Status::Failure); + + std::vector binary; + context->module()->ToBinary(&binary, false); + std::string optimized_asm; + SpirvTools tools_(SPV_ENV_UNIVERSAL_1_1); + tools_.Disassemble(binary, &optimized_asm); + std::cout << optimized_asm << std::endl; +} +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/lcssa.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/lcssa.cpp new file mode 100644 index 0000000..ace6ce1 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/lcssa.cpp @@ -0,0 +1,607 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "source/opt/build_module.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" +#include "source/opt/pass.h" +#include "test/opt//assembly_builder.h" +#include "test/opt/function_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +bool Validate(const std::vector& bin) { + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + spv_context spvContext = spvContextCreate(target_env); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {bin.data(), bin.size()}; + spv_result_t error = spvValidate(spvContext, &binary, &diagnostic); + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + return error == 0; +} + +void Match(const std::string& original, IRContext* context, + bool do_validation = true) { + std::vector bin; + context->module()->ToBinary(&bin, true); + if (do_validation) { + EXPECT_TRUE(Validate(bin)); + } + std::string assembly; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_2); + EXPECT_TRUE( + tools.Disassemble(bin, &assembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) + << "Disassembling failed for shader:\n" + << assembly << std::endl; + auto match_result = effcee::Match(assembly, original); + EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) + << match_result.message() << "\nChecking result:\n" + << assembly; +} + +using LCSSATest = ::testing::Test; + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +layout(location = 0) out vec4 c; +void main() { + int i = 0; + for (; i < 10; i++) { + } + if (i != 0) { + i = 1; + } +} +*/ +TEST_F(LCSSATest, SimpleLCSSA) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] %19 None +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} %30 %20 +; CHECK-NEXT: %27 = OpINotEqual {{%\w+}} [[phi]] %9 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %3 "c" + OpDecorate %3 Location 0 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 0 + %10 = OpConstant %7 10 + %11 = OpTypeBool + %12 = OpConstant %7 1 + %13 = OpTypeFloat 32 + %14 = OpTypeVector %13 4 + %15 = OpTypePointer Output %14 + %3 = OpVariable %15 Output + %2 = OpFunction %5 None %6 + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + %30 = OpPhi %7 %9 %16 %25 %19 + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %22 = OpSLessThan %11 %30 %10 + OpBranchConditional %22 %23 %18 + %23 = OpLabel + OpBranch %19 + %19 = OpLabel + %25 = OpIAdd %7 %30 %12 + OpBranch %17 + %18 = OpLabel + %27 = OpINotEqual %11 %30 %9 + OpSelectionMerge %28 None + OpBranchConditional %27 %29 %28 + %29 = OpLabel + OpBranch %28 + %28 = OpLabel + %31 = OpPhi %7 %30 %18 %12 %29 + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor ld{context.get(), f}; + + Loop* loop = ld[17]; + EXPECT_FALSE(loop->IsLCSSA()); + LoopUtils Util(context.get(), loop); + Util.MakeLoopClosedSSA(); + EXPECT_TRUE(loop->IsLCSSA()); + Match(text, context.get()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +layout(location = 0) out vec4 c; +void main() { + int i = 0; + for (; i < 10; i++) { + } + if (i != 0) { + i = 1; + } +} +*/ +// Same test as above, but should reuse an existing phi. +TEST_F(LCSSATest, PhiReuseLCSSA) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] %19 None +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} %30 %20 +; CHECK-NEXT: %27 = OpINotEqual {{%\w+}} [[phi]] %9 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %3 "c" + OpDecorate %3 Location 0 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 0 + %10 = OpConstant %7 10 + %11 = OpTypeBool + %12 = OpConstant %7 1 + %13 = OpTypeFloat 32 + %14 = OpTypeVector %13 4 + %15 = OpTypePointer Output %14 + %3 = OpVariable %15 Output + %2 = OpFunction %5 None %6 + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + %30 = OpPhi %7 %9 %16 %25 %19 + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %22 = OpSLessThan %11 %30 %10 + OpBranchConditional %22 %23 %18 + %23 = OpLabel + OpBranch %19 + %19 = OpLabel + %25 = OpIAdd %7 %30 %12 + OpBranch %17 + %18 = OpLabel + %32 = OpPhi %7 %30 %20 + %27 = OpINotEqual %11 %30 %9 + OpSelectionMerge %28 None + OpBranchConditional %27 %29 %28 + %29 = OpLabel + OpBranch %28 + %28 = OpLabel + %31 = OpPhi %7 %30 %18 %12 %29 + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor ld{context.get(), f}; + + Loop* loop = ld[17]; + EXPECT_FALSE(loop->IsLCSSA()); + LoopUtils Util(context.get(), loop); + Util.MakeLoopClosedSSA(); + EXPECT_TRUE(loop->IsLCSSA()); + Match(text, context.get()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +layout(location = 0) out vec4 c; +void main() { + int i = 0; + int j = 0; + for (; i < 10; i++) {} + for (; j < 10; j++) {} + if (j != 0) { + i = 1; + } +} +*/ +TEST_F(LCSSATest, DualLoopLCSSA) { + const std::string text = R"( +; CHECK: %20 = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi %6 %17 %21 +; CHECK: %33 = OpLabel +; CHECK-NEXT: {{%\w+}} = OpPhi {{%\w+}} [[phi]] %28 %11 %34 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %3 "c" + OpDecorate %3 Location 0 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 0 + %9 = OpConstant %6 10 + %10 = OpTypeBool + %11 = OpConstant %6 1 + %12 = OpTypeFloat 32 + %13 = OpTypeVector %12 4 + %14 = OpTypePointer Output %13 + %3 = OpVariable %14 Output + %2 = OpFunction %4 None %5 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %6 %8 %15 %18 %19 + OpLoopMerge %20 %19 None + OpBranch %21 + %21 = OpLabel + %22 = OpSLessThan %10 %17 %9 + OpBranchConditional %22 %23 %20 + %23 = OpLabel + OpBranch %19 + %19 = OpLabel + %18 = OpIAdd %6 %17 %11 + OpBranch %16 + %20 = OpLabel + OpBranch %24 + %24 = OpLabel + %25 = OpPhi %6 %8 %20 %26 %27 + OpLoopMerge %28 %27 None + OpBranch %29 + %29 = OpLabel + %30 = OpSLessThan %10 %25 %9 + OpBranchConditional %30 %31 %28 + %31 = OpLabel + OpBranch %27 + %27 = OpLabel + %26 = OpIAdd %6 %25 %11 + OpBranch %24 + %28 = OpLabel + %32 = OpINotEqual %10 %25 %8 + OpSelectionMerge %33 None + OpBranchConditional %32 %34 %33 + %34 = OpLabel + OpBranch %33 + %33 = OpLabel + %35 = OpPhi %6 %17 %28 %11 %34 + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor ld{context.get(), f}; + + Loop* loop = ld[16]; + EXPECT_FALSE(loop->IsLCSSA()); + LoopUtils Util(context.get(), loop); + Util.MakeLoopClosedSSA(); + EXPECT_TRUE(loop->IsLCSSA()); + Match(text, context.get()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +layout(location = 0) out vec4 c; +void main() { + int i = 0; + if (i != 0) { + for (; i < 10; i++) {} + } + if (i != 0) { + i = 1; + } +} +*/ +TEST_F(LCSSATest, PhiUserLCSSA) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] %22 None +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} %20 %24 +; CHECK: %17 = OpLabel +; CHECK-NEXT: {{%\w+}} = OpPhi {{%\w+}} %8 %15 [[phi]] %23 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %3 "c" + OpDecorate %3 Location 0 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 0 + %9 = OpTypeBool + %10 = OpConstant %6 10 + %11 = OpConstant %6 1 + %12 = OpTypeFloat 32 + %13 = OpTypeVector %12 4 + %14 = OpTypePointer Output %13 + %3 = OpVariable %14 Output + %2 = OpFunction %4 None %5 + %15 = OpLabel + %16 = OpINotEqual %9 %8 %8 + OpSelectionMerge %17 None + OpBranchConditional %16 %18 %17 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + %20 = OpPhi %6 %8 %18 %21 %22 + OpLoopMerge %23 %22 None + OpBranch %24 + %24 = OpLabel + %25 = OpSLessThan %9 %20 %10 + OpBranchConditional %25 %26 %23 + %26 = OpLabel + OpBranch %22 + %22 = OpLabel + %21 = OpIAdd %6 %20 %11 + OpBranch %19 + %23 = OpLabel + OpBranch %17 + %17 = OpLabel + %27 = OpPhi %6 %8 %15 %20 %23 + %28 = OpINotEqual %9 %27 %8 + OpSelectionMerge %29 None + OpBranchConditional %28 %30 %29 + %30 = OpLabel + OpBranch %29 + %29 = OpLabel + %31 = OpPhi %6 %27 %17 %11 %30 + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor ld{context.get(), f}; + + Loop* loop = ld[19]; + EXPECT_FALSE(loop->IsLCSSA()); + LoopUtils Util(context.get(), loop); + Util.MakeLoopClosedSSA(); + EXPECT_TRUE(loop->IsLCSSA()); + Match(text, context.get()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +void main() { + int i = 0; + if (i != 0) { + for (; i < 10; i++) { + if (i > 5) break; + } + } + if (i != 0) { + i = 1; + } +} +*/ +TEST_F(LCSSATest, LCSSAWithBreak) { + const std::string text = R"( +; CHECK: OpLoopMerge [[merge:%\w+]] %19 None +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} %17 %21 %17 %26 +; CHECK: %14 = OpLabel +; CHECK-NEXT: {{%\w+}} = OpPhi {{%\w+}} %7 %12 [[phi]] [[merge]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpConstant %5 0 + %8 = OpTypeBool + %9 = OpConstant %5 10 + %10 = OpConstant %5 5 + %11 = OpConstant %5 1 + %2 = OpFunction %3 None %4 + %12 = OpLabel + %13 = OpINotEqual %8 %7 %7 + OpSelectionMerge %14 None + OpBranchConditional %13 %15 %14 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %5 %7 %15 %18 %19 + OpLoopMerge %20 %19 None + OpBranch %21 + %21 = OpLabel + %22 = OpSLessThan %8 %17 %9 + OpBranchConditional %22 %23 %20 + %23 = OpLabel + %24 = OpSGreaterThan %8 %17 %10 + OpSelectionMerge %25 None + OpBranchConditional %24 %26 %25 + %26 = OpLabel + OpBranch %20 + %25 = OpLabel + OpBranch %19 + %19 = OpLabel + %18 = OpIAdd %5 %17 %11 + OpBranch %16 + %20 = OpLabel + OpBranch %14 + %14 = OpLabel + %27 = OpPhi %5 %7 %12 %17 %20 + %28 = OpINotEqual %8 %27 %7 + OpSelectionMerge %29 None + OpBranchConditional %28 %30 %29 + %30 = OpLabel + OpBranch %29 + %29 = OpLabel + %31 = OpPhi %5 %27 %14 %11 %30 + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor ld{context.get(), f}; + + Loop* loop = ld[19]; + EXPECT_FALSE(loop->IsLCSSA()); + LoopUtils Util(context.get(), loop); + Util.MakeLoopClosedSSA(); + EXPECT_TRUE(loop->IsLCSSA()); + Match(text, context.get()); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +void main() { + int i = 0; + for (; i < 10; i++) {} + for (int j = i; j < 10;) { j = i + j; } +} +*/ +TEST_F(LCSSATest, LCSSAUseInNonEligiblePhi) { + const std::string text = R"( +; CHECK: %12 = OpLabel +; CHECK-NEXT: [[def_to_close:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[closing_phi:%\w+]] = OpPhi {{%\w+}} [[def_to_close]] %17 +; CHECK: %16 = OpLabel +; CHECK-NEXT: [[use_in_phi:%\w+]] = OpPhi {{%\w+}} %21 %22 [[closing_phi]] [[merge]] +; CHECK: OpIAdd {{%\w+}} [[closing_phi]] [[use_in_phi]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpConstant %5 0 + %8 = OpConstant %5 10 + %9 = OpTypeBool + %10 = OpConstant %5 1 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + %13 = OpPhi %5 %7 %11 %14 %15 + OpLoopMerge %16 %15 None + OpBranch %17 + %17 = OpLabel + %18 = OpSLessThan %9 %13 %8 + OpBranchConditional %18 %19 %16 + %19 = OpLabel + OpBranch %15 + %15 = OpLabel + %14 = OpIAdd %5 %13 %10 + OpBranch %12 + %16 = OpLabel + %20 = OpPhi %5 %13 %17 %21 %22 + OpLoopMerge %23 %22 None + OpBranch %24 + %24 = OpLabel + %25 = OpSLessThan %9 %20 %8 + OpBranchConditional %25 %26 %23 + %26 = OpLabel + %21 = OpIAdd %5 %13 %20 + OpBranch %22 + %22 = OpLabel + OpBranch %16 + %23 = OpLabel + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor ld{context.get(), f}; + + Loop* loop = ld[12]; + EXPECT_FALSE(loop->IsLCSSA()); + LoopUtils Util(context.get(), loop); + Util.MakeLoopClosedSSA(); + EXPECT_TRUE(loop->IsLCSSA()); + Match(text, context.get()); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/loop_descriptions.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/loop_descriptions.cpp new file mode 100644 index 0000000..4d2f989 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/loop_descriptions.cpp @@ -0,0 +1,421 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 c; +void main() { + int i = 0; + for(; i < 10; ++i) { + } +} +*/ +TEST_F(PassClassTest, BasicVisitFromEntryPoint) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %5 "i" + OpName %3 "c" + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpConstant %8 1 + %14 = OpTypeFloat 32 + %15 = OpTypeVector %14 4 + %16 = OpTypePointer Output %15 + %3 = OpVariable %16 Output + %2 = OpFunction %6 None %7 + %17 = OpLabel + %5 = OpVariable %9 Function + OpStore %5 %10 + OpBranch %18 + %18 = OpLabel + OpLoopMerge %19 %20 None + OpBranch %21 + %21 = OpLabel + %22 = OpLoad %8 %5 + %23 = OpSLessThan %12 %22 %11 + OpBranchConditional %23 %24 %19 + %24 = OpLabel + OpBranch %20 + %20 = OpLabel + %25 = OpLoad %8 %5 + %26 = OpIAdd %8 %25 %13 + OpStore %5 %26 + OpBranch %18 + %19 = OpLabel + OpReturn + OpFunctionEnd + )"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + Loop& loop = ld.GetLoopByIndex(0); + EXPECT_EQ(loop.GetHeaderBlock(), spvtest::GetBasicBlock(f, 18)); + EXPECT_EQ(loop.GetLatchBlock(), spvtest::GetBasicBlock(f, 20)); + EXPECT_EQ(loop.GetMergeBlock(), spvtest::GetBasicBlock(f, 19)); + + EXPECT_FALSE(loop.HasNestedLoops()); + EXPECT_FALSE(loop.IsNested()); + EXPECT_EQ(loop.GetDepth(), 1u); +} + +/* +Generated from the following GLSL: +#version 330 core +layout(location = 0) out vec4 c; +void main() { + for(int i = 0; i < 10; ++i) {} + for(int i = 0; i < 10; ++i) {} +} + +But it was "hacked" to make the first loop merge block the second loop header. +*/ +TEST_F(PassClassTest, LoopWithNoPreHeader) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %4 "i" + OpName %5 "i" + OpName %3 "c" + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpConstant %8 1 + %14 = OpTypeFloat 32 + %15 = OpTypeVector %14 4 + %16 = OpTypePointer Output %15 + %3 = OpVariable %16 Output + %2 = OpFunction %6 None %7 + %17 = OpLabel + %4 = OpVariable %9 Function + %5 = OpVariable %9 Function + OpStore %4 %10 + OpStore %5 %10 + OpBranch %18 + %18 = OpLabel + OpLoopMerge %27 %20 None + OpBranch %21 + %21 = OpLabel + %22 = OpLoad %8 %4 + %23 = OpSLessThan %12 %22 %11 + OpBranchConditional %23 %24 %27 + %24 = OpLabel + OpBranch %20 + %20 = OpLabel + %25 = OpLoad %8 %4 + %26 = OpIAdd %8 %25 %13 + OpStore %4 %26 + OpBranch %18 + %27 = OpLabel + OpLoopMerge %28 %29 None + OpBranch %30 + %30 = OpLabel + %31 = OpLoad %8 %5 + %32 = OpSLessThan %12 %31 %11 + OpBranchConditional %32 %33 %28 + %33 = OpLabel + OpBranch %29 + %29 = OpLabel + %34 = OpLoad %8 %5 + %35 = OpIAdd %8 %34 %13 + OpStore %5 %35 + OpBranch %27 + %28 = OpLabel + OpReturn + OpFunctionEnd + )"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + EXPECT_EQ(ld.NumLoops(), 2u); + + Loop* loop = ld[27]; + EXPECT_EQ(loop->GetPreHeaderBlock(), nullptr); + EXPECT_NE(loop->GetOrCreatePreHeaderBlock(), nullptr); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +in vec4 c; +void main() { + int i = 0; + bool cond = c[0] == 0; + for (; i < 10; i++) { + if (cond) { + return; + } + else { + return; + } + } + bool cond2 = i == 9; +} +*/ +TEST_F(PassClassTest, NoLoop) { + const std::string text = R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 3 +; Bound: 47 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %16 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 330 + OpName %4 "main" + OpName %16 "c" + OpDecorate %16 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeBool + %11 = OpTypePointer Function %10 + %13 = OpTypeFloat 32 + %14 = OpTypeVector %13 4 + %15 = OpTypePointer Input %14 + %16 = OpVariable %15 Input + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %19 = OpTypePointer Input %13 + %22 = OpConstant %13 0 + %30 = OpConstant %6 10 + %39 = OpConstant %6 1 + %46 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpAccessChain %19 %16 %18 + %21 = OpLoad %13 %20 + %23 = OpFOrdEqual %10 %21 %22 + OpBranch %24 + %24 = OpLabel + %45 = OpPhi %6 %9 %5 %40 %27 + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %31 = OpSLessThan %10 %45 %30 + OpBranchConditional %31 %25 %26 + %25 = OpLabel + OpSelectionMerge %34 None + OpBranchConditional %23 %33 %36 + %33 = OpLabel + OpReturn + %36 = OpLabel + OpReturn + %34 = OpLabel + OpBranch %27 + %27 = OpLabel + %40 = OpIAdd %6 %46 %39 + OpBranch %24 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor ld{context.get(), f}; + + EXPECT_EQ(ld.NumLoops(), 0u); +} + +/* +Generated from following GLSL with latch block artificially inserted to be +seperate from continue. +#version 430 +void main(void) { + float x[10]; + for (int i = 0; i < 10; ++i) { + x[i] = i; + } +} +*/ +TEST_F(PassClassTest, LoopLatchNotContinue) { + const std::string text = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "i" + OpName %4 "x" + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 0 + %10 = OpConstant %7 10 + %11 = OpTypeBool + %12 = OpTypeFloat 32 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 10 + %15 = OpTypeArray %12 %14 + %16 = OpTypePointer Function %15 + %17 = OpTypePointer Function %12 + %18 = OpConstant %7 1 + %2 = OpFunction %5 None %6 + %19 = OpLabel + %3 = OpVariable %8 Function + %4 = OpVariable %16 Function + OpStore %3 %9 + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %7 %9 %19 %22 %30 + OpLoopMerge %24 %23 None + OpBranch %25 + %25 = OpLabel + %26 = OpSLessThan %11 %21 %10 + OpBranchConditional %26 %27 %24 + %27 = OpLabel + %28 = OpConvertSToF %12 %21 + %29 = OpAccessChain %17 %4 %21 + OpStore %29 %28 + OpBranch %23 + %23 = OpLabel + %22 = OpIAdd %7 %21 %18 + OpStore %3 %22 + OpBranch %30 + %30 = OpLabel + OpBranch %20 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor ld{context.get(), f}; + + EXPECT_EQ(ld.NumLoops(), 1u); + + Loop& loop = ld.GetLoopByIndex(0u); + + EXPECT_NE(loop.GetLatchBlock(), loop.GetContinueBlock()); + + EXPECT_EQ(loop.GetContinueBlock()->id(), 23u); + EXPECT_EQ(loop.GetLatchBlock()->id(), 30u); +} + +TEST_F(PassClassTest, UnreachableMerge) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %1 = OpFunction %void None %3 + %4 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %6 %7 None + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %7 + %7 = OpLabel + OpBranch %5 + %6 = OpLabel + OpUnreachable + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_3, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 1); + LoopDescriptor ld{context.get(), f}; + + EXPECT_EQ(ld.NumLoops(), 1u); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/loop_fission.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/loop_fission.cpp new file mode 100644 index 0000000..55b9c26 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/loop_fission.cpp @@ -0,0 +1,3491 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/loop_fission.h" +#include "source/opt/loop_unroller.h" +#include "source/opt/loop_utils.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using FissionClassTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL + +#version 430 + +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + A[i] = B[i]; + B[i] = A[i]; + } +} + +Result should be equivalent to: + +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + A[i] = B[i]; + } + + for (int i = 0; i < 10; i++) { + B[i] = A[i]; + } +} +*/ +TEST_F(FissionClassTest, SimpleFission) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "A" +OpName %5 "B" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 1 +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %17 Function +%5 = OpVariable %17 Function +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %20 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %22 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +%29 = OpAccessChain %18 %5 %22 +%30 = OpLoad %13 %29 +%31 = OpAccessChain %18 %4 %22 +OpStore %31 %30 +%32 = OpAccessChain %18 %4 %22 +%33 = OpLoad %13 %32 +%34 = OpAccessChain %18 %5 %22 +OpStore %34 %33 +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %8 %22 %19 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + +const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "A" +OpName %5 "B" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 1 +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %17 Function +%5 = OpVariable %17 Function +OpBranch %35 +%35 = OpLabel +%36 = OpPhi %8 %10 %20 %47 %46 +OpLoopMerge %48 %46 None +OpBranch %37 +%37 = OpLabel +%38 = OpSLessThan %12 %36 %11 +OpBranchConditional %38 %39 %48 +%39 = OpLabel +%40 = OpAccessChain %18 %5 %36 +%41 = OpLoad %13 %40 +%42 = OpAccessChain %18 %4 %36 +OpStore %42 %41 +OpBranch %46 +%46 = OpLabel +%47 = OpIAdd %8 %36 %19 +OpBranch %35 +%48 = OpLabel +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %48 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %22 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +%32 = OpAccessChain %18 %4 %22 +%33 = OpLoad %13 %32 +%34 = OpAccessChain %18 %5 %22 +OpStore %34 %33 +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %8 %22 %19 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); + + // Check that the loop will NOT be split when provided with a pass-through + // register pressure functor which just returns false. + SinglePassRunAndCheck( + source, source, true, + [](const RegisterLiveness::RegionRegisterLiveness&) { return false; }); +} + +/* +Generated from the following GLSL + +#version 430 + +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + A[i] = B[i]; + B[i] = A[i+1]; + } +} + +This loop should not be split, as the i+1 dependence would be broken by +splitting the loop. +*/ + +TEST_F(FissionClassTest, FissionInterdependency) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "A" +OpName %5 "B" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 1 +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %17 Function +%5 = OpVariable %17 Function +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %20 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %22 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +%29 = OpAccessChain %18 %5 %22 +%30 = OpLoad %13 %29 +%31 = OpAccessChain %18 %4 %22 +OpStore %31 %30 +%32 = OpIAdd %8 %22 %19 +%33 = OpAccessChain %18 %4 %32 +%34 = OpLoad %13 %33 +%35 = OpAccessChain %18 %5 %22 +OpStore %35 %34 +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %8 %22 %19 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, source, true); +} + +/* +Generated from the following GLSL + +#version 430 + +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + A[i] = B[i]; + B[i+1] = A[i]; + } +} + + +This should not be split as the load B[i] is dependent on the store B[i+1] +*/ +TEST_F(FissionClassTest, FissionInterdependency2) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "A" +OpName %5 "B" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 1 +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %17 Function +%5 = OpVariable %17 Function +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %20 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %22 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +%29 = OpAccessChain %18 %5 %22 +%30 = OpLoad %13 %29 +%31 = OpAccessChain %18 %4 %22 +OpStore %31 %30 +%32 = OpIAdd %8 %22 %19 +%33 = OpAccessChain %18 %4 %22 +%34 = OpLoad %13 %33 +%35 = OpAccessChain %18 %5 %32 +OpStore %35 %34 +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %8 %22 %19 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, source, true); +} + +/* +#version 430 +void main(void) { + float A[10]; + float B[10]; + float C[10] + float D[10] + for (int i = 0; i < 10; i++) { + A[i] = B[i]; + B[i] = A[i]; + C[i] = D[i]; + D[i] = C[i]; + } +} + +This should be split into the equivalent of: + + for (int i = 0; i < 10; i++) { + A[i] = B[i]; + B[i] = A[i]; + } + for (int i = 0; i < 10; i++) { + C[i] = D[i]; + D[i] = C[i]; + } + +We then check that the loop is broken into four for loops like so, if the pass +is run twice: + for (int i = 0; i < 10; i++) + A[i] = B[i]; + for (int i = 0; i < 10; i++) + B[i] = A[i]; + for (int i = 0; i < 10; i++) + C[i] = D[i]; + for (int i = 0; i < 10; i++) + D[i] = C[i]; + +*/ + +TEST_F(FissionClassTest, FissionMultipleLoadStores) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string source = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "i" + OpName %4 "A" + OpName %5 "B" + OpName %6 "C" + OpName %7 "D" + %8 = OpTypeVoid + %9 = OpTypeFunction %8 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %12 = OpConstant %10 0 + %13 = OpConstant %10 10 + %14 = OpTypeBool + %15 = OpTypeFloat 32 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 10 + %18 = OpTypeArray %15 %17 + %19 = OpTypePointer Function %18 + %20 = OpTypePointer Function %15 + %21 = OpConstant %10 1 + %2 = OpFunction %8 None %9 + %22 = OpLabel + %3 = OpVariable %11 Function + %4 = OpVariable %19 Function + %5 = OpVariable %19 Function + %6 = OpVariable %19 Function + %7 = OpVariable %19 Function + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %10 %12 %22 %25 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %29 = OpSLessThan %14 %24 %13 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + %31 = OpAccessChain %20 %5 %24 + %32 = OpLoad %15 %31 + %33 = OpAccessChain %20 %4 %24 + OpStore %33 %32 + %34 = OpAccessChain %20 %4 %24 + %35 = OpLoad %15 %34 + %36 = OpAccessChain %20 %5 %24 + OpStore %36 %35 + %37 = OpAccessChain %20 %7 %24 + %38 = OpLoad %15 %37 + %39 = OpAccessChain %20 %6 %24 + OpStore %39 %38 + %40 = OpAccessChain %20 %6 %24 + %41 = OpLoad %15 %40 + %42 = OpAccessChain %20 %7 %24 + OpStore %42 %41 + OpBranch %26 + %26 = OpLabel + %25 = OpIAdd %10 %24 %21 + OpBranch %23 + %27 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "A" +OpName %5 "B" +OpName %6 "C" +OpName %7 "D" +%8 = OpTypeVoid +%9 = OpTypeFunction %8 +%10 = OpTypeInt 32 1 +%11 = OpTypePointer Function %10 +%12 = OpConstant %10 0 +%13 = OpConstant %10 10 +%14 = OpTypeBool +%15 = OpTypeFloat 32 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 10 +%18 = OpTypeArray %15 %17 +%19 = OpTypePointer Function %18 +%20 = OpTypePointer Function %15 +%21 = OpConstant %10 1 +%2 = OpFunction %8 None %9 +%22 = OpLabel +%3 = OpVariable %11 Function +%4 = OpVariable %19 Function +%5 = OpVariable %19 Function +%6 = OpVariable %19 Function +%7 = OpVariable %19 Function +OpBranch %43 +%43 = OpLabel +%44 = OpPhi %10 %12 %22 %61 %60 +OpLoopMerge %62 %60 None +OpBranch %45 +%45 = OpLabel +%46 = OpSLessThan %14 %44 %13 +OpBranchConditional %46 %47 %62 +%47 = OpLabel +%48 = OpAccessChain %20 %5 %44 +%49 = OpLoad %15 %48 +%50 = OpAccessChain %20 %4 %44 +OpStore %50 %49 +%51 = OpAccessChain %20 %4 %44 +%52 = OpLoad %15 %51 +%53 = OpAccessChain %20 %5 %44 +OpStore %53 %52 +OpBranch %60 +%60 = OpLabel +%61 = OpIAdd %10 %44 %21 +OpBranch %43 +%62 = OpLabel +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %10 %12 %62 %25 %26 +OpLoopMerge %27 %26 None +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %14 %24 %13 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +%37 = OpAccessChain %20 %7 %24 +%38 = OpLoad %15 %37 +%39 = OpAccessChain %20 %6 %24 +OpStore %39 %38 +%40 = OpAccessChain %20 %6 %24 +%41 = OpLoad %15 %40 +%42 = OpAccessChain %20 %7 %24 +OpStore %42 %41 +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %10 %24 %21 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + +const std::string expected_multiple_passes = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "A" +OpName %5 "B" +OpName %6 "C" +OpName %7 "D" +%8 = OpTypeVoid +%9 = OpTypeFunction %8 +%10 = OpTypeInt 32 1 +%11 = OpTypePointer Function %10 +%12 = OpConstant %10 0 +%13 = OpConstant %10 10 +%14 = OpTypeBool +%15 = OpTypeFloat 32 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 10 +%18 = OpTypeArray %15 %17 +%19 = OpTypePointer Function %18 +%20 = OpTypePointer Function %15 +%21 = OpConstant %10 1 +%2 = OpFunction %8 None %9 +%22 = OpLabel +%3 = OpVariable %11 Function +%4 = OpVariable %19 Function +%5 = OpVariable %19 Function +%6 = OpVariable %19 Function +%7 = OpVariable %19 Function +OpBranch %63 +%63 = OpLabel +%64 = OpPhi %10 %12 %22 %75 %74 +OpLoopMerge %76 %74 None +OpBranch %65 +%65 = OpLabel +%66 = OpSLessThan %14 %64 %13 +OpBranchConditional %66 %67 %76 +%67 = OpLabel +%68 = OpAccessChain %20 %5 %64 +%69 = OpLoad %15 %68 +%70 = OpAccessChain %20 %4 %64 +OpStore %70 %69 +OpBranch %74 +%74 = OpLabel +%75 = OpIAdd %10 %64 %21 +OpBranch %63 +%76 = OpLabel +OpBranch %43 +%43 = OpLabel +%44 = OpPhi %10 %12 %76 %61 %60 +OpLoopMerge %62 %60 None +OpBranch %45 +%45 = OpLabel +%46 = OpSLessThan %14 %44 %13 +OpBranchConditional %46 %47 %62 +%47 = OpLabel +%51 = OpAccessChain %20 %4 %44 +%52 = OpLoad %15 %51 +%53 = OpAccessChain %20 %5 %44 +OpStore %53 %52 +OpBranch %60 +%60 = OpLabel +%61 = OpIAdd %10 %44 %21 +OpBranch %43 +%62 = OpLabel +OpBranch %77 +%77 = OpLabel +%78 = OpPhi %10 %12 %62 %89 %88 +OpLoopMerge %90 %88 None +OpBranch %79 +%79 = OpLabel +%80 = OpSLessThan %14 %78 %13 +OpBranchConditional %80 %81 %90 +%81 = OpLabel +%82 = OpAccessChain %20 %7 %78 +%83 = OpLoad %15 %82 +%84 = OpAccessChain %20 %6 %78 +OpStore %84 %83 +OpBranch %88 +%88 = OpLabel +%89 = OpIAdd %10 %78 %21 +OpBranch %77 +%90 = OpLabel +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %10 %12 %90 %25 %26 +OpLoopMerge %27 %26 None +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %14 %24 %13 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +%40 = OpAccessChain %20 %6 %24 +%41 = OpLoad %15 %40 +%42 = OpAccessChain %20 %7 %24 +OpStore %42 %41 +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %10 %24 %21 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on +std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); +Module* module = context->module(); +EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + +SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); +SinglePassRunAndCheck(source, expected, true); + +// By passing 1 as argument we are using the constructor which makes the +// critera to split the loop be if the registers in the loop exceede 1. By +// using this constructor we are also enabling multiple passes (disabled by +// default). +SinglePassRunAndCheck(source, expected_multiple_passes, true, + 1); +} + +/* +#version 430 +void main(void) { + int accumulator = 0; + float X[10]; + float Y[10]; + + for (int i = 0; i < 10; i++) { + X[i] = Y[i]; + Y[i] = X[i]; + accumulator += i; + } +} + +This should be split into the equivalent of: + +#version 430 +void main(void) { + int accumulator = 0; + float X[10]; + float Y[10]; + + for (int i = 0; i < 10; i++) { + X[i] = Y[i]; + } + for (int i = 0; i < 10; i++) { + Y[i] = X[i]; + accumulator += i; + } +} +*/ +TEST_F(FissionClassTest, FissionWithAccumulator) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string source = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "accumulator" + OpName %4 "i" + OpName %5 "X" + OpName %6 "Y" + %7 = OpTypeVoid + %8 = OpTypeFunction %7 + %9 = OpTypeInt 32 1 + %10 = OpTypePointer Function %9 + %11 = OpConstant %9 0 + %12 = OpConstant %9 10 + %13 = OpTypeBool + %14 = OpTypeFloat 32 + %15 = OpTypeInt 32 0 + %16 = OpConstant %15 10 + %17 = OpTypeArray %14 %16 + %18 = OpTypePointer Function %17 + %19 = OpTypePointer Function %14 + %20 = OpConstant %9 1 + %2 = OpFunction %7 None %8 + %21 = OpLabel + %3 = OpVariable %10 Function + %4 = OpVariable %10 Function + %5 = OpVariable %18 Function + %6 = OpVariable %18 Function + OpBranch %22 + %22 = OpLabel + %23 = OpPhi %9 %11 %21 %24 %25 + %26 = OpPhi %9 %11 %21 %27 %25 + OpLoopMerge %28 %25 None + OpBranch %29 + %29 = OpLabel + %30 = OpSLessThan %13 %26 %12 + OpBranchConditional %30 %31 %28 + %31 = OpLabel + %32 = OpAccessChain %19 %6 %26 + %33 = OpLoad %14 %32 + %34 = OpAccessChain %19 %5 %26 + OpStore %34 %33 + %35 = OpAccessChain %19 %5 %26 + %36 = OpLoad %14 %35 + %37 = OpAccessChain %19 %6 %26 + OpStore %37 %36 + %24 = OpIAdd %9 %23 %26 + OpBranch %25 + %25 = OpLabel + %27 = OpIAdd %9 %26 %20 + OpBranch %22 + %28 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "accumulator" +OpName %4 "i" +OpName %5 "X" +OpName %6 "Y" +%7 = OpTypeVoid +%8 = OpTypeFunction %7 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 0 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpTypeFloat 32 +%15 = OpTypeInt 32 0 +%16 = OpConstant %15 10 +%17 = OpTypeArray %14 %16 +%18 = OpTypePointer Function %17 +%19 = OpTypePointer Function %14 +%20 = OpConstant %9 1 +%2 = OpFunction %7 None %8 +%21 = OpLabel +%3 = OpVariable %10 Function +%4 = OpVariable %10 Function +%5 = OpVariable %18 Function +%6 = OpVariable %18 Function +OpBranch %38 +%38 = OpLabel +%40 = OpPhi %9 %11 %21 %52 %51 +OpLoopMerge %53 %51 None +OpBranch %41 +%41 = OpLabel +%42 = OpSLessThan %13 %40 %12 +OpBranchConditional %42 %43 %53 +%43 = OpLabel +%44 = OpAccessChain %19 %6 %40 +%45 = OpLoad %14 %44 +%46 = OpAccessChain %19 %5 %40 +OpStore %46 %45 +OpBranch %51 +%51 = OpLabel +%52 = OpIAdd %9 %40 %20 +OpBranch %38 +%53 = OpLabel +OpBranch %22 +%22 = OpLabel +%23 = OpPhi %9 %11 %53 %24 %25 +%26 = OpPhi %9 %11 %53 %27 %25 +OpLoopMerge %28 %25 None +OpBranch %29 +%29 = OpLabel +%30 = OpSLessThan %13 %26 %12 +OpBranchConditional %30 %31 %28 +%31 = OpLabel +%35 = OpAccessChain %19 %5 %26 +%36 = OpLoad %14 %35 +%37 = OpAccessChain %19 %6 %26 +OpStore %37 %36 +%24 = OpIAdd %9 %23 %26 +OpBranch %25 +%25 = OpLabel +%27 = OpIAdd %9 %26 %20 +OpBranch %22 +%28 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); +} + +/* +Generated from the following glsl: + +#version 430 +layout(location=0) out float x; +layout(location=1) out float y; + +void main(void) { + float accumulator_1 = 0; + float accumulator_2 = 0; + for (int i = 0; i < 10; i++) { + accumulator_1 += i; + accumulator_2 += i; + } + + x = accumulator_1; + y = accumulator_2; +} + +Should be split into equivalent of: + +void main(void) { + float accumulator_1 = 0; + float accumulator_2 = 0; + for (int i = 0; i < 10; i++) { + accumulator_1 += i; + } + + for (int i = 0; i < 10; i++) { + accumulator_2 += i; + } + x = accumulator_1; + y = accumulator_2; +} + +*/ +TEST_F(FissionClassTest, FissionWithPhisUsedOutwithLoop) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string source = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %5 "accumulator_1" + OpName %6 "accumulator_2" + OpName %7 "i" + OpName %3 "x" + OpName %4 "y" + OpDecorate %3 Location 0 + OpDecorate %4 Location 1 + %8 = OpTypeVoid + %9 = OpTypeFunction %8 + %10 = OpTypeFloat 32 + %11 = OpTypePointer Function %10 + %12 = OpConstant %10 0 + %13 = OpTypeInt 32 1 + %14 = OpTypePointer Function %13 + %15 = OpConstant %13 0 + %16 = OpConstant %13 10 + %17 = OpTypeBool + %18 = OpConstant %13 1 + %19 = OpTypePointer Output %10 + %3 = OpVariable %19 Output + %4 = OpVariable %19 Output + %2 = OpFunction %8 None %9 + %20 = OpLabel + %5 = OpVariable %11 Function + %6 = OpVariable %11 Function + %7 = OpVariable %14 Function + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %10 %12 %20 %23 %24 + %25 = OpPhi %10 %12 %20 %26 %24 + %27 = OpPhi %13 %15 %20 %28 %24 + OpLoopMerge %29 %24 None + OpBranch %30 + %30 = OpLabel + %31 = OpSLessThan %17 %27 %16 + OpBranchConditional %31 %32 %29 + %32 = OpLabel + %33 = OpConvertSToF %10 %27 + %26 = OpFAdd %10 %25 %33 + %34 = OpConvertSToF %10 %27 + %23 = OpFAdd %10 %22 %34 + OpBranch %24 + %24 = OpLabel + %28 = OpIAdd %13 %27 %18 + OpStore %7 %28 + OpBranch %21 + %29 = OpLabel + OpStore %3 %25 + OpStore %4 %22 + OpReturn + OpFunctionEnd + )"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 %4 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %5 "accumulator_1" +OpName %6 "accumulator_2" +OpName %7 "i" +OpName %3 "x" +OpName %4 "y" +OpDecorate %3 Location 0 +OpDecorate %4 Location 1 +%8 = OpTypeVoid +%9 = OpTypeFunction %8 +%10 = OpTypeFloat 32 +%11 = OpTypePointer Function %10 +%12 = OpConstant %10 0 +%13 = OpTypeInt 32 1 +%14 = OpTypePointer Function %13 +%15 = OpConstant %13 0 +%16 = OpConstant %13 10 +%17 = OpTypeBool +%18 = OpConstant %13 1 +%19 = OpTypePointer Output %10 +%3 = OpVariable %19 Output +%4 = OpVariable %19 Output +%2 = OpFunction %8 None %9 +%20 = OpLabel +%5 = OpVariable %11 Function +%6 = OpVariable %11 Function +%7 = OpVariable %14 Function +OpBranch %35 +%35 = OpLabel +%37 = OpPhi %10 %12 %20 %43 %46 +%38 = OpPhi %13 %15 %20 %47 %46 +OpLoopMerge %48 %46 None +OpBranch %39 +%39 = OpLabel +%40 = OpSLessThan %17 %38 %16 +OpBranchConditional %40 %41 %48 +%41 = OpLabel +%42 = OpConvertSToF %10 %38 +%43 = OpFAdd %10 %37 %42 +OpBranch %46 +%46 = OpLabel +%47 = OpIAdd %13 %38 %18 +OpStore %7 %47 +OpBranch %35 +%48 = OpLabel +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %10 %12 %48 %23 %24 +%27 = OpPhi %13 %15 %48 %28 %24 +OpLoopMerge %29 %24 None +OpBranch %30 +%30 = OpLabel +%31 = OpSLessThan %17 %27 %16 +OpBranchConditional %31 %32 %29 +%32 = OpLabel +%34 = OpConvertSToF %10 %27 +%23 = OpFAdd %10 %22 %34 +OpBranch %24 +%24 = OpLabel +%28 = OpIAdd %13 %27 %18 +OpStore %7 %28 +OpBranch %21 +%29 = OpLabel +OpStore %3 %37 +OpStore %4 %22 +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); +} + +/* +#version 430 +void main(void) { + float A[10][10]; + float B[10][10]; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + A[i][j] = B[i][j]; + B[i][j] = A[i][j]; + } + } +} + +Should be split into equivalent of: + +#version 430 +void main(void) { + float A[10][10]; + float B[10][10]; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + A[i][j] = B[i][j]; + } + for (int j = 0; j < 10; j++) { + B[i][j] = A[i][j]; + } + } +} + + +*/ +TEST_F(FissionClassTest, FissionNested) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string source = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "i" + OpName %4 "j" + OpName %5 "A" + OpName %6 "B" + %7 = OpTypeVoid + %8 = OpTypeFunction %7 + %9 = OpTypeInt 32 1 + %10 = OpTypePointer Function %9 + %11 = OpConstant %9 0 + %12 = OpConstant %9 10 + %13 = OpTypeBool + %14 = OpTypeFloat 32 + %15 = OpTypeInt 32 0 + %16 = OpConstant %15 10 + %17 = OpTypeArray %14 %16 + %18 = OpTypeArray %17 %16 + %19 = OpTypePointer Function %18 + %20 = OpTypePointer Function %14 + %21 = OpConstant %9 1 + %2 = OpFunction %7 None %8 + %22 = OpLabel + %3 = OpVariable %10 Function + %4 = OpVariable %10 Function + %5 = OpVariable %19 Function + %6 = OpVariable %19 Function + OpStore %3 %11 + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %9 %11 %22 %25 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %29 = OpSLessThan %13 %24 %12 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + OpStore %4 %11 + OpBranch %31 + %31 = OpLabel + %32 = OpPhi %9 %11 %30 %33 %34 + OpLoopMerge %35 %34 None + OpBranch %36 + %36 = OpLabel + %37 = OpSLessThan %13 %32 %12 + OpBranchConditional %37 %38 %35 + %38 = OpLabel + %39 = OpAccessChain %20 %6 %24 %32 + %40 = OpLoad %14 %39 + %41 = OpAccessChain %20 %5 %24 %32 + OpStore %41 %40 + %42 = OpAccessChain %20 %5 %24 %32 + %43 = OpLoad %14 %42 + %44 = OpAccessChain %20 %6 %24 %32 + OpStore %44 %43 + OpBranch %34 + %34 = OpLabel + %33 = OpIAdd %9 %32 %21 + OpStore %4 %33 + OpBranch %31 + %35 = OpLabel + OpBranch %26 + %26 = OpLabel + %25 = OpIAdd %9 %24 %21 + OpStore %3 %25 + OpBranch %23 + %27 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "j" +OpName %5 "A" +OpName %6 "B" +%7 = OpTypeVoid +%8 = OpTypeFunction %7 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 0 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpTypeFloat 32 +%15 = OpTypeInt 32 0 +%16 = OpConstant %15 10 +%17 = OpTypeArray %14 %16 +%18 = OpTypeArray %17 %16 +%19 = OpTypePointer Function %18 +%20 = OpTypePointer Function %14 +%21 = OpConstant %9 1 +%2 = OpFunction %7 None %8 +%22 = OpLabel +%3 = OpVariable %10 Function +%4 = OpVariable %10 Function +%5 = OpVariable %19 Function +%6 = OpVariable %19 Function +OpStore %3 %11 +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %9 %11 %22 %25 %26 +OpLoopMerge %27 %26 None +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %13 %24 %12 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +OpStore %4 %11 +OpBranch %45 +%45 = OpLabel +%46 = OpPhi %9 %11 %30 %57 %56 +OpLoopMerge %58 %56 None +OpBranch %47 +%47 = OpLabel +%48 = OpSLessThan %13 %46 %12 +OpBranchConditional %48 %49 %58 +%49 = OpLabel +%50 = OpAccessChain %20 %6 %24 %46 +%51 = OpLoad %14 %50 +%52 = OpAccessChain %20 %5 %24 %46 +OpStore %52 %51 +OpBranch %56 +%56 = OpLabel +%57 = OpIAdd %9 %46 %21 +OpStore %4 %57 +OpBranch %45 +%58 = OpLabel +OpBranch %31 +%31 = OpLabel +%32 = OpPhi %9 %11 %58 %33 %34 +OpLoopMerge %35 %34 None +OpBranch %36 +%36 = OpLabel +%37 = OpSLessThan %13 %32 %12 +OpBranchConditional %37 %38 %35 +%38 = OpLabel +%42 = OpAccessChain %20 %5 %24 %32 +%43 = OpLoad %14 %42 +%44 = OpAccessChain %20 %6 %24 %32 +OpStore %44 %43 +OpBranch %34 +%34 = OpLabel +%33 = OpIAdd %9 %32 %21 +OpStore %4 %33 +OpBranch %31 +%35 = OpLabel +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %9 %24 %21 +OpStore %3 %25 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); +} + +/* +#version 430 +void main(void) { + int accumulator = 0; + float A[10]; + float B[10]; + float C[10]; + + for (int i = 0; i < 10; i++) { + int c = C[i]; + A[i] = B[i]; + B[i] = A[i] + c; + } +} + +This loop should not be split as we would have to break the order of the loads +to do so. It would be grouped into two sets: + +1 + int c = C[i]; + B[i] = A[i] + c; + +2 + A[i] = B[i]; + +To keep the load C[i] in the same order we would need to put B[i] ahead of that +*/ +TEST_F(FissionClassTest, FissionLoad) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "c" +OpName %5 "C" +OpName %6 "A" +OpName %7 "B" +%8 = OpTypeVoid +%9 = OpTypeFunction %8 +%10 = OpTypeInt 32 1 +%11 = OpTypePointer Function %10 +%12 = OpConstant %10 0 +%13 = OpConstant %10 10 +%14 = OpTypeBool +%15 = OpTypeFloat 32 +%16 = OpTypePointer Function %15 +%17 = OpTypeInt 32 0 +%18 = OpConstant %17 10 +%19 = OpTypeArray %15 %18 +%20 = OpTypePointer Function %19 +%21 = OpConstant %10 1 +%2 = OpFunction %8 None %9 +%22 = OpLabel +%3 = OpVariable %11 Function +%4 = OpVariable %16 Function +%5 = OpVariable %20 Function +%6 = OpVariable %20 Function +%7 = OpVariable %20 Function +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %10 %12 %22 %25 %26 +OpLoopMerge %27 %26 None +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %14 %24 %13 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +%31 = OpAccessChain %16 %5 %24 +%32 = OpLoad %15 %31 +OpStore %4 %32 +%33 = OpAccessChain %16 %7 %24 +%34 = OpLoad %15 %33 +%35 = OpAccessChain %16 %6 %24 +OpStore %35 %34 +%36 = OpAccessChain %16 %6 %24 +%37 = OpLoad %15 %36 +%38 = OpFAdd %15 %37 %32 +%39 = OpAccessChain %16 %7 %24 +OpStore %39 %38 +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %10 %24 %21 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, source, true); +} + +/* +#version 430 +layout(location=0) flat in int condition; +void main(void) { + float A[10]; + float B[10]; + + for (int i = 0; i < 10; i++) { + if (condition == 1) + A[i] = B[i]; + else + B[i] = A[i]; + } +} + + +When this is split we leave the condition check and control flow inplace and +leave its removal for dead code elimination. + +#version 430 +layout(location=0) flat in int condition; +void main(void) { + float A[10]; + float B[10]; + + for (int i = 0; i < 10; i++) { + if (condition == 1) + A[i] = B[i]; + else + ; + } + for (int i = 0; i < 10; i++) { + if (condition == 1) + ; + else + B[i] = A[i]; + } +} + + +*/ +TEST_F(FissionClassTest, FissionControlFlow) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string source = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %4 "i" + OpName %3 "condition" + OpName %5 "A" + OpName %6 "B" + OpDecorate %3 Flat + OpDecorate %3 Location 0 + %7 = OpTypeVoid + %8 = OpTypeFunction %7 + %9 = OpTypeInt 32 1 + %10 = OpTypePointer Function %9 + %11 = OpConstant %9 0 + %12 = OpConstant %9 10 + %13 = OpTypeBool + %14 = OpTypePointer Input %9 + %3 = OpVariable %14 Input + %15 = OpConstant %9 1 + %16 = OpTypeFloat 32 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 10 + %19 = OpTypeArray %16 %18 + %20 = OpTypePointer Function %19 + %21 = OpTypePointer Function %16 + %2 = OpFunction %7 None %8 + %22 = OpLabel + %4 = OpVariable %10 Function + %5 = OpVariable %20 Function + %6 = OpVariable %20 Function + %31 = OpLoad %9 %3 + OpStore %4 %11 + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %9 %11 %22 %25 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %29 = OpSLessThan %13 %24 %12 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + %32 = OpIEqual %13 %31 %15 + OpSelectionMerge %33 None + OpBranchConditional %32 %34 %35 + %34 = OpLabel + %36 = OpAccessChain %21 %6 %24 + %37 = OpLoad %16 %36 + %38 = OpAccessChain %21 %5 %24 + OpStore %38 %37 + OpBranch %33 + %35 = OpLabel + %39 = OpAccessChain %21 %5 %24 + %40 = OpLoad %16 %39 + %41 = OpAccessChain %21 %6 %24 + OpStore %41 %40 + OpBranch %33 + %33 = OpLabel + OpBranch %26 + %26 = OpLabel + %25 = OpIAdd %9 %24 %15 + OpStore %4 %25 + OpBranch %23 + %27 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %4 "i" +OpName %3 "condition" +OpName %5 "A" +OpName %6 "B" +OpDecorate %3 Flat +OpDecorate %3 Location 0 +%7 = OpTypeVoid +%8 = OpTypeFunction %7 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 0 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpTypePointer Input %9 +%3 = OpVariable %14 Input +%15 = OpConstant %9 1 +%16 = OpTypeFloat 32 +%17 = OpTypeInt 32 0 +%18 = OpConstant %17 10 +%19 = OpTypeArray %16 %18 +%20 = OpTypePointer Function %19 +%21 = OpTypePointer Function %16 +%2 = OpFunction %7 None %8 +%22 = OpLabel +%4 = OpVariable %10 Function +%5 = OpVariable %20 Function +%6 = OpVariable %20 Function +%23 = OpLoad %9 %3 +OpStore %4 %11 +OpBranch %42 +%42 = OpLabel +%43 = OpPhi %9 %11 %22 %58 %57 +OpLoopMerge %59 %57 None +OpBranch %44 +%44 = OpLabel +%45 = OpSLessThan %13 %43 %12 +OpBranchConditional %45 %46 %59 +%46 = OpLabel +%47 = OpIEqual %13 %23 %15 +OpSelectionMerge %56 None +OpBranchConditional %47 %52 %48 +%48 = OpLabel +OpBranch %56 +%52 = OpLabel +%53 = OpAccessChain %21 %6 %43 +%54 = OpLoad %16 %53 +%55 = OpAccessChain %21 %5 %43 +OpStore %55 %54 +OpBranch %56 +%56 = OpLabel +OpBranch %57 +%57 = OpLabel +%58 = OpIAdd %9 %43 %15 +OpStore %4 %58 +OpBranch %42 +%59 = OpLabel +OpBranch %24 +%24 = OpLabel +%25 = OpPhi %9 %11 %59 %26 %27 +OpLoopMerge %28 %27 None +OpBranch %29 +%29 = OpLabel +%30 = OpSLessThan %13 %25 %12 +OpBranchConditional %30 %31 %28 +%31 = OpLabel +%32 = OpIEqual %13 %23 %15 +OpSelectionMerge %33 None +OpBranchConditional %32 %34 %35 +%34 = OpLabel +OpBranch %33 +%35 = OpLabel +%39 = OpAccessChain %21 %5 %25 +%40 = OpLoad %16 %39 +%41 = OpAccessChain %21 %6 %25 +OpStore %41 %40 +OpBranch %33 +%33 = OpLabel +OpBranch %27 +%27 = OpLabel +%26 = OpIAdd %9 %25 %15 +OpStore %4 %26 +OpBranch %24 +%28 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); +} + +/* +#version 430 +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + if (i == 1) + B[i] = A[i]; + else if (i == 2) + A[i] = B[i]; + else + A[i] = 0; + } +} + +After running the pass with multiple splits enabled (via register threshold of +1) we expect the equivalent of: + +#version 430 +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + if (i == 1) + B[i] = A[i]; + else if (i == 2) + else + } + for (int i = 0; i < 10; i++) { + if (i == 1) + else if (i == 2) + A[i] = B[i]; + else + } + for (int i = 0; i < 10; i++) { + if (i == 1) + else if (i == 2) + else + A[i] = 0; + } + +} + +*/ +TEST_F(FissionClassTest, FissionControlFlow2) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string source = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "i" + OpName %4 "B" + OpName %5 "A" + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpConstant %8 1 + %14 = OpTypeFloat 32 + %15 = OpTypeInt 32 0 + %16 = OpConstant %15 10 + %17 = OpTypeArray %14 %16 + %18 = OpTypePointer Function %17 + %19 = OpTypePointer Function %14 + %20 = OpConstant %8 2 + %21 = OpConstant %14 0 + %2 = OpFunction %6 None %7 + %22 = OpLabel + %3 = OpVariable %9 Function + %4 = OpVariable %18 Function + %5 = OpVariable %18 Function + OpStore %3 %10 + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %8 %10 %22 %25 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %29 = OpSLessThan %12 %24 %11 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + %31 = OpIEqual %12 %24 %13 + OpSelectionMerge %32 None + OpBranchConditional %31 %33 %34 + %33 = OpLabel + %35 = OpAccessChain %19 %5 %24 + %36 = OpLoad %14 %35 + %37 = OpAccessChain %19 %4 %24 + OpStore %37 %36 + OpBranch %32 + %34 = OpLabel + %38 = OpIEqual %12 %24 %20 + OpSelectionMerge %39 None + OpBranchConditional %38 %40 %41 + %40 = OpLabel + %42 = OpAccessChain %19 %4 %24 + %43 = OpLoad %14 %42 + %44 = OpAccessChain %19 %5 %24 + OpStore %44 %43 + OpBranch %39 + %41 = OpLabel + %45 = OpAccessChain %19 %5 %24 + OpStore %45 %21 + OpBranch %39 + %39 = OpLabel + OpBranch %32 + %32 = OpLabel + OpBranch %26 + %26 = OpLabel + %25 = OpIAdd %8 %24 %13 + OpStore %3 %25 + OpBranch %23 + %27 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "B" +OpName %5 "A" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpConstant %8 1 +%14 = OpTypeFloat 32 +%15 = OpTypeInt 32 0 +%16 = OpConstant %15 10 +%17 = OpTypeArray %14 %16 +%18 = OpTypePointer Function %17 +%19 = OpTypePointer Function %14 +%20 = OpConstant %8 2 +%21 = OpConstant %14 0 +%2 = OpFunction %6 None %7 +%22 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %18 Function +%5 = OpVariable %18 Function +OpStore %3 %10 +OpBranch %46 +%46 = OpLabel +%47 = OpPhi %8 %10 %22 %67 %66 +OpLoopMerge %68 %66 None +OpBranch %48 +%48 = OpLabel +%49 = OpSLessThan %12 %47 %11 +OpBranchConditional %49 %50 %68 +%50 = OpLabel +%51 = OpIEqual %12 %47 %13 +OpSelectionMerge %65 None +OpBranchConditional %51 %61 %52 +%52 = OpLabel +%53 = OpIEqual %12 %47 %20 +OpSelectionMerge %60 None +OpBranchConditional %53 %56 %54 +%54 = OpLabel +OpBranch %60 +%56 = OpLabel +OpBranch %60 +%60 = OpLabel +OpBranch %65 +%61 = OpLabel +%62 = OpAccessChain %19 %5 %47 +%63 = OpLoad %14 %62 +%64 = OpAccessChain %19 %4 %47 +OpStore %64 %63 +OpBranch %65 +%65 = OpLabel +OpBranch %66 +%66 = OpLabel +%67 = OpIAdd %8 %47 %13 +OpStore %3 %67 +OpBranch %46 +%68 = OpLabel +OpBranch %69 +%69 = OpLabel +%70 = OpPhi %8 %10 %68 %87 %86 +OpLoopMerge %88 %86 None +OpBranch %71 +%71 = OpLabel +%72 = OpSLessThan %12 %70 %11 +OpBranchConditional %72 %73 %88 +%73 = OpLabel +%74 = OpIEqual %12 %70 %13 +OpSelectionMerge %85 None +OpBranchConditional %74 %84 %75 +%75 = OpLabel +%76 = OpIEqual %12 %70 %20 +OpSelectionMerge %83 None +OpBranchConditional %76 %79 %77 +%77 = OpLabel +OpBranch %83 +%79 = OpLabel +%80 = OpAccessChain %19 %4 %70 +%81 = OpLoad %14 %80 +%82 = OpAccessChain %19 %5 %70 +OpStore %82 %81 +OpBranch %83 +%83 = OpLabel +OpBranch %85 +%84 = OpLabel +OpBranch %85 +%85 = OpLabel +OpBranch %86 +%86 = OpLabel +%87 = OpIAdd %8 %70 %13 +OpStore %3 %87 +OpBranch %69 +%88 = OpLabel +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %8 %10 %88 %25 %26 +OpLoopMerge %27 %26 None +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %12 %24 %11 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +%31 = OpIEqual %12 %24 %13 +OpSelectionMerge %32 None +OpBranchConditional %31 %33 %34 +%33 = OpLabel +OpBranch %32 +%34 = OpLabel +%38 = OpIEqual %12 %24 %20 +OpSelectionMerge %39 None +OpBranchConditional %38 %40 %41 +%40 = OpLabel +OpBranch %39 +%41 = OpLabel +%45 = OpAccessChain %19 %5 %24 +OpStore %45 %21 +OpBranch %39 +%39 = OpLabel +OpBranch %32 +%32 = OpLabel +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %8 %24 %13 +OpStore %3 %25 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true, 1); +} + +/* +#version 430 +layout(location=0) flat in int condition; +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + B[i] = A[i]; + memoryBarrier(); + A[i] = B[i]; + } +} + +This should not be split due to the memory barrier. +*/ +TEST_F(FissionClassTest, FissionBarrier) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %4 "i" +OpName %5 "B" +OpName %6 "A" +OpName %3 "condition" +OpDecorate %3 Flat +OpDecorate %3 Location 0 +%7 = OpTypeVoid +%8 = OpTypeFunction %7 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 0 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpTypeFloat 32 +%15 = OpTypeInt 32 0 +%16 = OpConstant %15 10 +%17 = OpTypeArray %14 %16 +%18 = OpTypePointer Function %17 +%19 = OpTypePointer Function %14 +%20 = OpConstant %15 1 +%21 = OpConstant %15 4048 +%22 = OpConstant %9 1 +%23 = OpTypePointer Input %9 +%3 = OpVariable %23 Input +%2 = OpFunction %7 None %8 +%24 = OpLabel +%4 = OpVariable %10 Function +%5 = OpVariable %18 Function +%6 = OpVariable %18 Function +OpStore %4 %11 +OpBranch %25 +%25 = OpLabel +%26 = OpPhi %9 %11 %24 %27 %28 +OpLoopMerge %29 %28 None +OpBranch %30 +%30 = OpLabel +%31 = OpSLessThan %13 %26 %12 +OpBranchConditional %31 %32 %29 +%32 = OpLabel +%33 = OpAccessChain %19 %6 %26 +%34 = OpLoad %14 %33 +%35 = OpAccessChain %19 %5 %26 +OpStore %35 %34 +OpMemoryBarrier %20 %21 +%36 = OpAccessChain %19 %5 %26 +%37 = OpLoad %14 %36 +%38 = OpAccessChain %19 %6 %26 +OpStore %38 %37 +OpBranch %28 +%28 = OpLabel +%27 = OpIAdd %9 %26 %22 +OpStore %4 %27 +OpBranch %25 +%29 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, source, true); +} + +/* +#version 430 +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + B[i] = A[i]; + if ( i== 1) + break; + A[i] = B[i]; + } +} + +This should not be split due to the break. +*/ +TEST_F(FissionClassTest, FissionBreak) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "B" +OpName %5 "A" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 1 +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %17 Function +%5 = OpVariable %17 Function +OpStore %3 %10 +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %20 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %22 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +%29 = OpAccessChain %18 %5 %22 +%30 = OpLoad %13 %29 +%31 = OpAccessChain %18 %4 %22 +OpStore %31 %30 +%32 = OpIEqual %12 %22 %19 +OpSelectionMerge %33 None +OpBranchConditional %32 %34 %33 +%34 = OpLabel +OpBranch %25 +%33 = OpLabel +%35 = OpAccessChain %18 %4 %22 +%36 = OpLoad %13 %35 +%37 = OpAccessChain %18 %5 %22 +OpStore %37 %36 +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %8 %22 %19 +OpStore %3 %23 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, source, true); +} + +/* +#version 430 +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; i++) { + B[i] = A[i]; + if ( i== 1) + continue; + A[i] = B[i]; + } +} + +This loop should be split into: + + for (int i = 0; i < 10; i++) { + B[i] = A[i]; + if ( i== 1) + continue; + } + for (int i = 0; i < 10; i++) { + if ( i== 1) + continue; + A[i] = B[i]; + } +The continue block in the first loop is left to DCE. +} + + +*/ +TEST_F(FissionClassTest, FissionContinue) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "B" +OpName %5 "A" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 1 +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %17 Function +%5 = OpVariable %17 Function +OpStore %3 %10 +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %20 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %22 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +%29 = OpAccessChain %18 %5 %22 +%30 = OpLoad %13 %29 +%31 = OpAccessChain %18 %4 %22 +OpStore %31 %30 +%32 = OpIEqual %12 %22 %19 +OpSelectionMerge %33 None +OpBranchConditional %32 %34 %33 +%34 = OpLabel +OpBranch %24 +%33 = OpLabel +%35 = OpAccessChain %18 %4 %22 +%36 = OpLoad %13 %35 +%37 = OpAccessChain %18 %5 %22 +OpStore %37 %36 +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %8 %22 %19 +OpStore %3 %23 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + +const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "B" +OpName %5 "A" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 1 +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %17 Function +%5 = OpVariable %17 Function +OpStore %3 %10 +OpBranch %38 +%38 = OpLabel +%39 = OpPhi %8 %10 %20 %53 %52 +OpLoopMerge %54 %52 None +OpBranch %40 +%40 = OpLabel +%41 = OpSLessThan %12 %39 %11 +OpBranchConditional %41 %42 %54 +%42 = OpLabel +%43 = OpAccessChain %18 %5 %39 +%44 = OpLoad %13 %43 +%45 = OpAccessChain %18 %4 %39 +OpStore %45 %44 +%46 = OpIEqual %12 %39 %19 +OpSelectionMerge %48 None +OpBranchConditional %46 %47 %48 +%47 = OpLabel +OpBranch %52 +%48 = OpLabel +OpBranch %52 +%52 = OpLabel +%53 = OpIAdd %8 %39 %19 +OpStore %3 %53 +OpBranch %38 +%54 = OpLabel +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %54 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %22 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +%32 = OpIEqual %12 %22 %19 +OpSelectionMerge %33 None +OpBranchConditional %32 %34 %33 +%34 = OpLabel +OpBranch %24 +%33 = OpLabel +%35 = OpAccessChain %18 %4 %22 +%36 = OpLoad %13 %35 +%37 = OpAccessChain %18 %5 %22 +OpStore %37 %36 +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %8 %22 %19 +OpStore %3 %23 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; +// clang-format on +std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); +Module* module = context->module(); +EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + +SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); +SinglePassRunAndCheck(source, expected, true); +} + +/* +#version 430 +void main(void) { + float A[10]; + float B[10]; + int i = 0; + do { + B[i] = A[i]; + A[i] = B[i]; + ++i; + } while (i < 10); +} + + +Check that this is split into: + int i = 0; + do { + B[i] = A[i]; + ++i; + } while (i < 10); + + i = 0; + do { + A[i] = B[i]; + ++i; + } while (i < 10); + + +*/ +TEST_F(FissionClassTest, FissionDoWhile) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "B" +OpName %5 "A" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpTypeFloat 32 +%12 = OpTypeInt 32 0 +%13 = OpConstant %12 10 +%14 = OpTypeArray %11 %13 +%15 = OpTypePointer Function %14 +%16 = OpTypePointer Function %11 +%17 = OpConstant %8 1 +%18 = OpConstant %8 10 +%19 = OpTypeBool +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %15 Function +%5 = OpVariable %15 Function +OpStore %3 %10 +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %20 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpAccessChain %16 %5 %22 +%28 = OpLoad %11 %27 +%29 = OpAccessChain %16 %4 %22 +OpStore %29 %28 +%30 = OpAccessChain %16 %4 %22 +%31 = OpLoad %11 %30 +%32 = OpAccessChain %16 %5 %22 +OpStore %32 %31 +%23 = OpIAdd %8 %22 %17 +OpStore %3 %23 +OpBranch %24 +%24 = OpLabel +%33 = OpSLessThan %19 %23 %18 +OpBranchConditional %33 %21 %25 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + +const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "B" +OpName %5 "A" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpTypeFloat 32 +%12 = OpTypeInt 32 0 +%13 = OpConstant %12 10 +%14 = OpTypeArray %11 %13 +%15 = OpTypePointer Function %14 +%16 = OpTypePointer Function %11 +%17 = OpConstant %8 1 +%18 = OpConstant %8 10 +%19 = OpTypeBool +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %15 Function +%5 = OpVariable %15 Function +OpStore %3 %10 +OpBranch %34 +%34 = OpLabel +%35 = OpPhi %8 %10 %20 %43 %44 +OpLoopMerge %46 %44 None +OpBranch %36 +%36 = OpLabel +%37 = OpAccessChain %16 %5 %35 +%38 = OpLoad %11 %37 +%39 = OpAccessChain %16 %4 %35 +OpStore %39 %38 +%43 = OpIAdd %8 %35 %17 +OpStore %3 %43 +OpBranch %44 +%44 = OpLabel +%45 = OpSLessThan %19 %43 %18 +OpBranchConditional %45 %34 %46 +%46 = OpLabel +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %46 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%30 = OpAccessChain %16 %4 %22 +%31 = OpLoad %11 %30 +%32 = OpAccessChain %16 %5 %22 +OpStore %32 %31 +%23 = OpIAdd %8 %22 %17 +OpStore %3 %23 +OpBranch %24 +%24 = OpLabel +%33 = OpSLessThan %19 %23 %18 +OpBranchConditional %33 %21 %25 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); +} + +/* + +#version 430 +void main(void) { + float A[10][10]; + float B[10][10]; + for (int j = 0; j < 10; ++j) { + for (int i = 0; i < 10; ++i) { + B[i][j] = A[i][i]; + A[i][i] = B[i][j + 1]; + } + } +} + + +This loop can't be split because the load B[i][j + 1] is dependent on the store +B[i][j]. + +*/ +TEST_F(FissionClassTest, FissionNestedDependency) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "j" +OpName %4 "i" +OpName %5 "B" +OpName %6 "A" +%7 = OpTypeVoid +%8 = OpTypeFunction %7 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 0 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpTypeFloat 32 +%15 = OpTypeInt 32 0 +%16 = OpConstant %15 10 +%17 = OpTypeArray %14 %16 +%18 = OpTypeArray %17 %16 +%19 = OpTypePointer Function %18 +%20 = OpTypePointer Function %14 +%21 = OpConstant %9 1 +%2 = OpFunction %7 None %8 +%22 = OpLabel +%3 = OpVariable %10 Function +%4 = OpVariable %10 Function +%5 = OpVariable %19 Function +%6 = OpVariable %19 Function +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %9 %11 %22 %25 %26 +OpLoopMerge %27 %26 None +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %13 %24 %12 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +OpBranch %31 +%31 = OpLabel +%32 = OpPhi %9 %11 %30 %33 %34 +OpLoopMerge %35 %34 None +OpBranch %36 +%36 = OpLabel +%37 = OpSLessThan %13 %32 %12 +OpBranchConditional %37 %38 %35 +%38 = OpLabel +%39 = OpAccessChain %20 %6 %32 %32 +%40 = OpLoad %14 %39 +%41 = OpAccessChain %20 %5 %32 %24 +OpStore %41 %40 +%42 = OpIAdd %9 %24 %21 +%43 = OpAccessChain %20 %5 %32 %42 +%44 = OpLoad %14 %43 +%45 = OpAccessChain %20 %6 %32 %32 +OpStore %45 %44 +OpBranch %34 +%34 = OpLabel +%33 = OpIAdd %9 %32 %21 +OpBranch %31 +%35 = OpLabel +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %9 %24 %21 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, source, true); +} + +/* +#version 430 +void main(void) { + float A[10][10]; + float B[10][10]; + for (int j = 0; j < 10; ++j) { + for (int i = 0; i < 10; ++i) { + B[i][i] = A[i][j]; + A[i][j+1] = B[i][i]; + } + } +} + +This loop should not be split as the load A[i][j+1] would be reading a value +written in the store A[i][j] which would be hit before A[i][j+1] if the loops +where split but would not get hit before the read currently. + +*/ +TEST_F(FissionClassTest, FissionNestedDependency2) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "j" +OpName %4 "i" +OpName %5 "B" +OpName %6 "A" +%7 = OpTypeVoid +%8 = OpTypeFunction %7 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 0 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpTypeFloat 32 +%15 = OpTypeInt 32 0 +%16 = OpConstant %15 10 +%17 = OpTypeArray %14 %16 +%18 = OpTypeArray %17 %16 +%19 = OpTypePointer Function %18 +%20 = OpTypePointer Function %14 +%21 = OpConstant %9 1 +%2 = OpFunction %7 None %8 +%22 = OpLabel +%3 = OpVariable %10 Function +%4 = OpVariable %10 Function +%5 = OpVariable %19 Function +%6 = OpVariable %19 Function +OpStore %3 %11 +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %9 %11 %22 %25 %26 +OpLoopMerge %27 %26 None +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %13 %24 %12 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +OpStore %4 %11 +OpBranch %31 +%31 = OpLabel +%32 = OpPhi %9 %11 %30 %33 %34 +OpLoopMerge %35 %34 None +OpBranch %36 +%36 = OpLabel +%37 = OpSLessThan %13 %32 %12 +OpBranchConditional %37 %38 %35 +%38 = OpLabel +%39 = OpAccessChain %20 %6 %32 %24 +%40 = OpLoad %14 %39 +%41 = OpAccessChain %20 %5 %32 %32 +OpStore %41 %40 +%42 = OpIAdd %9 %24 %21 +%43 = OpAccessChain %20 %5 %32 %32 +%44 = OpLoad %14 %43 +%45 = OpAccessChain %20 %6 %32 %42 +OpStore %45 %44 +OpBranch %34 +%34 = OpLabel +%33 = OpIAdd %9 %32 %21 +OpStore %4 %33 +OpBranch %31 +%35 = OpLabel +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %9 %24 %21 +OpStore %3 %25 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, source, true); +} + +/* +#version 430 +void main(void) { + float A[10][10]; + float B[10][10]; + for (int j = 0; j < 10; ++j) { + for (int i = 0; i < 10; ++i) { + B[i][j] = A[i][j]; + A[i][j] = B[i][j]; + } + for (int i = 0; i < 10; ++i) { + B[i][j] = A[i][j]; + A[i][j] = B[i][j]; + } + } +} + + + +Should be split into: + +for (int j = 0; j < 10; ++j) { + for (int i = 0; i < 10; ++i) + B[i][j] = A[i][j]; + for (int i = 0; i < 10; ++i) + A[i][j] = B[i][j]; + for (int i = 0; i < 10; ++i) + B[i][j] = A[i][j]; + for (int i = 0; i < 10; ++i) + A[i][j] = B[i][j]; +*/ +TEST_F(FissionClassTest, FissionMultipleLoopsNested) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "j" + OpName %4 "i" + OpName %5 "B" + OpName %6 "A" + OpName %7 "i" + %8 = OpTypeVoid + %9 = OpTypeFunction %8 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %12 = OpConstant %10 0 + %13 = OpConstant %10 10 + %14 = OpTypeBool + %15 = OpTypeFloat 32 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 10 + %18 = OpTypeArray %15 %17 + %19 = OpTypeArray %18 %17 + %20 = OpTypePointer Function %19 + %21 = OpTypePointer Function %15 + %22 = OpConstant %10 1 + %2 = OpFunction %8 None %9 + %23 = OpLabel + %3 = OpVariable %11 Function + %4 = OpVariable %11 Function + %5 = OpVariable %20 Function + %6 = OpVariable %20 Function + %7 = OpVariable %11 Function + OpStore %3 %12 + OpBranch %24 + %24 = OpLabel + %25 = OpPhi %10 %12 %23 %26 %27 + OpLoopMerge %28 %27 None + OpBranch %29 + %29 = OpLabel + %30 = OpSLessThan %14 %25 %13 + OpBranchConditional %30 %31 %28 + %31 = OpLabel + OpStore %4 %12 + OpBranch %32 + %32 = OpLabel + %33 = OpPhi %10 %12 %31 %34 %35 + OpLoopMerge %36 %35 None + OpBranch %37 + %37 = OpLabel + %38 = OpSLessThan %14 %33 %13 + OpBranchConditional %38 %39 %36 + %39 = OpLabel + %40 = OpAccessChain %21 %6 %33 %25 + %41 = OpLoad %15 %40 + %42 = OpAccessChain %21 %5 %33 %25 + OpStore %42 %41 + %43 = OpAccessChain %21 %5 %33 %25 + %44 = OpLoad %15 %43 + %45 = OpAccessChain %21 %6 %33 %25 + OpStore %45 %44 + OpBranch %35 + %35 = OpLabel + %34 = OpIAdd %10 %33 %22 + OpStore %4 %34 + OpBranch %32 + %36 = OpLabel + OpStore %7 %12 + OpBranch %46 + %46 = OpLabel + %47 = OpPhi %10 %12 %36 %48 %49 + OpLoopMerge %50 %49 None + OpBranch %51 + %51 = OpLabel + %52 = OpSLessThan %14 %47 %13 + OpBranchConditional %52 %53 %50 + %53 = OpLabel + %54 = OpAccessChain %21 %6 %47 %25 + %55 = OpLoad %15 %54 + %56 = OpAccessChain %21 %5 %47 %25 + OpStore %56 %55 + %57 = OpAccessChain %21 %5 %47 %25 + %58 = OpLoad %15 %57 + %59 = OpAccessChain %21 %6 %47 %25 + OpStore %59 %58 + OpBranch %49 + %49 = OpLabel + %48 = OpIAdd %10 %47 %22 + OpStore %7 %48 + OpBranch %46 + %50 = OpLabel + OpBranch %27 + %27 = OpLabel + %26 = OpIAdd %10 %25 %22 + OpStore %3 %26 + OpBranch %24 + %28 = OpLabel + OpReturn + OpFunctionEnd +)"; + +const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "j" +OpName %4 "i" +OpName %5 "B" +OpName %6 "A" +OpName %7 "i" +%8 = OpTypeVoid +%9 = OpTypeFunction %8 +%10 = OpTypeInt 32 1 +%11 = OpTypePointer Function %10 +%12 = OpConstant %10 0 +%13 = OpConstant %10 10 +%14 = OpTypeBool +%15 = OpTypeFloat 32 +%16 = OpTypeInt 32 0 +%17 = OpConstant %16 10 +%18 = OpTypeArray %15 %17 +%19 = OpTypeArray %18 %17 +%20 = OpTypePointer Function %19 +%21 = OpTypePointer Function %15 +%22 = OpConstant %10 1 +%2 = OpFunction %8 None %9 +%23 = OpLabel +%3 = OpVariable %11 Function +%4 = OpVariable %11 Function +%5 = OpVariable %20 Function +%6 = OpVariable %20 Function +%7 = OpVariable %11 Function +OpStore %3 %12 +OpBranch %24 +%24 = OpLabel +%25 = OpPhi %10 %12 %23 %26 %27 +OpLoopMerge %28 %27 None +OpBranch %29 +%29 = OpLabel +%30 = OpSLessThan %14 %25 %13 +OpBranchConditional %30 %31 %28 +%31 = OpLabel +OpStore %4 %12 +OpBranch %60 +%60 = OpLabel +%61 = OpPhi %10 %12 %31 %72 %71 +OpLoopMerge %73 %71 None +OpBranch %62 +%62 = OpLabel +%63 = OpSLessThan %14 %61 %13 +OpBranchConditional %63 %64 %73 +%64 = OpLabel +%65 = OpAccessChain %21 %6 %61 %25 +%66 = OpLoad %15 %65 +%67 = OpAccessChain %21 %5 %61 %25 +OpStore %67 %66 +OpBranch %71 +%71 = OpLabel +%72 = OpIAdd %10 %61 %22 +OpStore %4 %72 +OpBranch %60 +%73 = OpLabel +OpBranch %32 +%32 = OpLabel +%33 = OpPhi %10 %12 %73 %34 %35 +OpLoopMerge %36 %35 None +OpBranch %37 +%37 = OpLabel +%38 = OpSLessThan %14 %33 %13 +OpBranchConditional %38 %39 %36 +%39 = OpLabel +%43 = OpAccessChain %21 %5 %33 %25 +%44 = OpLoad %15 %43 +%45 = OpAccessChain %21 %6 %33 %25 +OpStore %45 %44 +OpBranch %35 +%35 = OpLabel +%34 = OpIAdd %10 %33 %22 +OpStore %4 %34 +OpBranch %32 +%36 = OpLabel +OpStore %7 %12 +OpBranch %74 +%74 = OpLabel +%75 = OpPhi %10 %12 %36 %86 %85 +OpLoopMerge %87 %85 None +OpBranch %76 +%76 = OpLabel +%77 = OpSLessThan %14 %75 %13 +OpBranchConditional %77 %78 %87 +%78 = OpLabel +%79 = OpAccessChain %21 %6 %75 %25 +%80 = OpLoad %15 %79 +%81 = OpAccessChain %21 %5 %75 %25 +OpStore %81 %80 +OpBranch %85 +%85 = OpLabel +%86 = OpIAdd %10 %75 %22 +OpStore %7 %86 +OpBranch %74 +%87 = OpLabel +OpBranch %46 +%46 = OpLabel +%47 = OpPhi %10 %12 %87 %48 %49 +OpLoopMerge %50 %49 None +OpBranch %51 +%51 = OpLabel +%52 = OpSLessThan %14 %47 %13 +OpBranchConditional %52 %53 %50 +%53 = OpLabel +%57 = OpAccessChain %21 %5 %47 %25 +%58 = OpLoad %15 %57 +%59 = OpAccessChain %21 %6 %47 %25 +OpStore %59 %58 +OpBranch %49 +%49 = OpLabel +%48 = OpIAdd %10 %47 %22 +OpStore %7 %48 +OpBranch %46 +%50 = OpLabel +OpBranch %27 +%27 = OpLabel +%26 = OpIAdd %10 %25 %22 +OpStore %3 %26 +OpBranch %24 +%28 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + const Function* function = spvtest::GetFunction(module, 2); + LoopDescriptor& pre_pass_descriptor = *context->GetLoopDescriptor(function); + EXPECT_EQ(pre_pass_descriptor.NumLoops(), 3u); + EXPECT_EQ(pre_pass_descriptor.pre_begin()->NumImmediateChildren(), 2u); + + // Test that the pass transforms the ir into the expected output. + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); + + // Test that the loop descriptor is correctly maintained and updated by the + // pass. + LoopFissionPass loop_fission; + loop_fission.SetContextForTesting(context.get()); + loop_fission.Process(); + + function = spvtest::GetFunction(module, 2); + LoopDescriptor& post_pass_descriptor = *context->GetLoopDescriptor(function); + EXPECT_EQ(post_pass_descriptor.NumLoops(), 5u); + EXPECT_EQ(post_pass_descriptor.pre_begin()->NumImmediateChildren(), 4u); +} + +/* +#version 430 +void main(void) { + float A[10][10]; + float B[10][10]; + for (int i = 0; i < 10; ++i) { + B[i][i] = A[i][i]; + A[i][i] = B[i][i]; + } + for (int i = 0; i < 10; ++i) { + B[i][i] = A[i][i]; + A[i][i] = B[i][i] + } +} + + + +Should be split into: + + for (int i = 0; i < 10; ++i) + B[i][i] = A[i][i]; + for (int i = 0; i < 10; ++i) + A[i][i] = B[i][i]; + for (int i = 0; i < 10; ++i) + B[i][i] = A[i][i]; + for (int i = 0; i < 10; ++i) + A[i][i] = B[i][i]; +*/ +TEST_F(FissionClassTest, FissionMultipleLoops) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "i" + OpName %4 "B" + OpName %5 "A" + OpName %6 "i" + %7 = OpTypeVoid + %8 = OpTypeFunction %7 + %9 = OpTypeInt 32 1 + %10 = OpTypePointer Function %9 + %11 = OpConstant %9 0 + %12 = OpConstant %9 10 + %13 = OpTypeBool + %14 = OpTypeFloat 32 + %15 = OpTypeInt 32 0 + %16 = OpConstant %15 10 + %17 = OpTypeArray %14 %16 + %18 = OpTypePointer Function %17 + %19 = OpTypePointer Function %14 + %20 = OpConstant %9 1 + %2 = OpFunction %7 None %8 + %21 = OpLabel + %3 = OpVariable %10 Function + %4 = OpVariable %18 Function + %5 = OpVariable %18 Function + %6 = OpVariable %10 Function + OpStore %3 %11 + OpBranch %22 + %22 = OpLabel + %23 = OpPhi %9 %11 %21 %24 %25 + OpLoopMerge %26 %25 None + OpBranch %27 + %27 = OpLabel + %28 = OpSLessThan %13 %23 %12 + OpBranchConditional %28 %29 %26 + %29 = OpLabel + %30 = OpAccessChain %19 %5 %23 + %31 = OpLoad %14 %30 + %32 = OpAccessChain %19 %4 %23 + OpStore %32 %31 + %33 = OpAccessChain %19 %4 %23 + %34 = OpLoad %14 %33 + %35 = OpAccessChain %19 %5 %23 + OpStore %35 %34 + OpBranch %25 + %25 = OpLabel + %24 = OpIAdd %9 %23 %20 + OpStore %3 %24 + OpBranch %22 + %26 = OpLabel + OpStore %6 %11 + OpBranch %36 + %36 = OpLabel + %37 = OpPhi %9 %11 %26 %38 %39 + OpLoopMerge %40 %39 None + OpBranch %41 + %41 = OpLabel + %42 = OpSLessThan %13 %37 %12 + OpBranchConditional %42 %43 %40 + %43 = OpLabel + %44 = OpAccessChain %19 %5 %37 + %45 = OpLoad %14 %44 + %46 = OpAccessChain %19 %4 %37 + OpStore %46 %45 + %47 = OpAccessChain %19 %4 %37 + %48 = OpLoad %14 %47 + %49 = OpAccessChain %19 %5 %37 + OpStore %49 %48 + OpBranch %39 + %39 = OpLabel + %38 = OpIAdd %9 %37 %20 + OpStore %6 %38 + OpBranch %36 + %40 = OpLabel + OpReturn + OpFunctionEnd +)"; + +const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "B" +OpName %5 "A" +OpName %6 "i" +%7 = OpTypeVoid +%8 = OpTypeFunction %7 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 0 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpTypeFloat 32 +%15 = OpTypeInt 32 0 +%16 = OpConstant %15 10 +%17 = OpTypeArray %14 %16 +%18 = OpTypePointer Function %17 +%19 = OpTypePointer Function %14 +%20 = OpConstant %9 1 +%2 = OpFunction %7 None %8 +%21 = OpLabel +%3 = OpVariable %10 Function +%4 = OpVariable %18 Function +%5 = OpVariable %18 Function +%6 = OpVariable %10 Function +OpStore %3 %11 +OpBranch %64 +%64 = OpLabel +%65 = OpPhi %9 %11 %21 %76 %75 +OpLoopMerge %77 %75 None +OpBranch %66 +%66 = OpLabel +%67 = OpSLessThan %13 %65 %12 +OpBranchConditional %67 %68 %77 +%68 = OpLabel +%69 = OpAccessChain %19 %5 %65 +%70 = OpLoad %14 %69 +%71 = OpAccessChain %19 %4 %65 +OpStore %71 %70 +OpBranch %75 +%75 = OpLabel +%76 = OpIAdd %9 %65 %20 +OpStore %3 %76 +OpBranch %64 +%77 = OpLabel +OpBranch %22 +%22 = OpLabel +%23 = OpPhi %9 %11 %77 %24 %25 +OpLoopMerge %26 %25 None +OpBranch %27 +%27 = OpLabel +%28 = OpSLessThan %13 %23 %12 +OpBranchConditional %28 %29 %26 +%29 = OpLabel +%33 = OpAccessChain %19 %4 %23 +%34 = OpLoad %14 %33 +%35 = OpAccessChain %19 %5 %23 +OpStore %35 %34 +OpBranch %25 +%25 = OpLabel +%24 = OpIAdd %9 %23 %20 +OpStore %3 %24 +OpBranch %22 +%26 = OpLabel +OpStore %6 %11 +OpBranch %50 +%50 = OpLabel +%51 = OpPhi %9 %11 %26 %62 %61 +OpLoopMerge %63 %61 None +OpBranch %52 +%52 = OpLabel +%53 = OpSLessThan %13 %51 %12 +OpBranchConditional %53 %54 %63 +%54 = OpLabel +%55 = OpAccessChain %19 %5 %51 +%56 = OpLoad %14 %55 +%57 = OpAccessChain %19 %4 %51 +OpStore %57 %56 +OpBranch %61 +%61 = OpLabel +%62 = OpIAdd %9 %51 %20 +OpStore %6 %62 +OpBranch %50 +%63 = OpLabel +OpBranch %36 +%36 = OpLabel +%37 = OpPhi %9 %11 %63 %38 %39 +OpLoopMerge %40 %39 None +OpBranch %41 +%41 = OpLabel +%42 = OpSLessThan %13 %37 %12 +OpBranchConditional %42 %43 %40 +%43 = OpLabel +%47 = OpAccessChain %19 %4 %37 +%48 = OpLoad %14 %47 +%49 = OpAccessChain %19 %5 %37 +OpStore %49 %48 +OpBranch %39 +%39 = OpLabel +%38 = OpIAdd %9 %37 %20 +OpStore %6 %38 +OpBranch %36 +%40 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); + + const Function* function = spvtest::GetFunction(module, 2); + LoopDescriptor& pre_pass_descriptor = *context->GetLoopDescriptor(function); + EXPECT_EQ(pre_pass_descriptor.NumLoops(), 2u); + EXPECT_EQ(pre_pass_descriptor.pre_begin()->NumImmediateChildren(), 0u); + + // Test that the pass transforms the ir into the expected output. + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); + + // Test that the loop descriptor is correctly maintained and updated by the + // pass. + LoopFissionPass loop_fission; + loop_fission.SetContextForTesting(context.get()); + loop_fission.Process(); + + function = spvtest::GetFunction(module, 2); + LoopDescriptor& post_pass_descriptor = *context->GetLoopDescriptor(function); + EXPECT_EQ(post_pass_descriptor.NumLoops(), 4u); + EXPECT_EQ(post_pass_descriptor.pre_begin()->NumImmediateChildren(), 0u); +} + +/* +#version 430 +int foo() { return 1; } +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; ++i) { + B[i] = A[i]; + foo(); + A[i] = B[i]; + } +} + +This should not be split as it has a function call in it so we can't determine +if it has side effects. +*/ +TEST_F(FissionClassTest, FissionFunctionCall) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "foo(" +OpName %4 "i" +OpName %5 "B" +OpName %6 "A" +%7 = OpTypeVoid +%8 = OpTypeFunction %7 +%9 = OpTypeInt 32 1 +%10 = OpTypeFunction %9 +%11 = OpConstant %9 1 +%12 = OpTypePointer Function %9 +%13 = OpConstant %9 0 +%14 = OpConstant %9 10 +%15 = OpTypeBool +%16 = OpTypeFloat 32 +%17 = OpTypeInt 32 0 +%18 = OpConstant %17 10 +%19 = OpTypeArray %16 %18 +%20 = OpTypePointer Function %19 +%21 = OpTypePointer Function %16 +%2 = OpFunction %7 None %8 +%22 = OpLabel +%4 = OpVariable %12 Function +%5 = OpVariable %20 Function +%6 = OpVariable %20 Function +OpStore %4 %13 +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %9 %13 %22 %25 %26 +OpLoopMerge %27 %26 None +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %15 %24 %14 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +%31 = OpAccessChain %21 %6 %24 +%32 = OpLoad %16 %31 +%33 = OpAccessChain %21 %5 %24 +OpStore %33 %32 +%34 = OpFunctionCall %9 %3 +%35 = OpAccessChain %21 %5 %24 +%36 = OpLoad %16 %35 +%37 = OpAccessChain %21 %6 %24 +OpStore %37 %36 +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %9 %24 %11 +OpStore %4 %25 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +%3 = OpFunction %9 None %10 +%38 = OpLabel +OpReturnValue %11 +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, source, true); +} + +/* +#version 430 +void main(void) { + float A[10]; + float B[10]; + for (int i = 0; i < 10; ++i) { + switch (i) { + case 1: + B[i] = A[i]; + break; + default: + A[i] = B[i]; + } + } +} + +This should be split into: + for (int i = 0; i < 10; ++i) { + switch (i) { + case 1: + break; + default: + A[i] = B[i]; + } + } + + for (int i = 0; i < 10; ++i) { + switch (i) { + case 1: + B[i] = A[i]; + break; + default: + break; + } + } + +*/ +TEST_F(FissionClassTest, FissionSwitchStatement) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string source = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "i" + OpName %4 "B" + OpName %5 "A" + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 10 + %16 = OpTypeArray %13 %15 + %17 = OpTypePointer Function %16 + %18 = OpTypePointer Function %13 + %19 = OpConstant %8 1 + %2 = OpFunction %6 None %7 + %20 = OpLabel + %3 = OpVariable %9 Function + %4 = OpVariable %17 Function + %5 = OpVariable %17 Function + OpStore %3 %10 + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %8 %10 %20 %23 %24 + OpLoopMerge %25 %24 None + OpBranch %26 + %26 = OpLabel + %27 = OpSLessThan %12 %22 %11 + OpBranchConditional %27 %28 %25 + %28 = OpLabel + OpSelectionMerge %29 None + OpSwitch %22 %30 1 %31 + %30 = OpLabel + %32 = OpAccessChain %18 %4 %22 + %33 = OpLoad %13 %32 + %34 = OpAccessChain %18 %5 %22 + OpStore %34 %33 + OpBranch %29 + %31 = OpLabel + %35 = OpAccessChain %18 %5 %22 + %36 = OpLoad %13 %35 + %37 = OpAccessChain %18 %4 %22 + OpStore %37 %36 + OpBranch %29 + %29 = OpLabel + OpBranch %24 + %24 = OpLabel + %23 = OpIAdd %8 %22 %19 + OpStore %3 %23 + OpBranch %21 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + +const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "B" +OpName %5 "A" +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 10 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 1 +%2 = OpFunction %6 None %7 +%20 = OpLabel +%3 = OpVariable %9 Function +%4 = OpVariable %17 Function +%5 = OpVariable %17 Function +OpStore %3 %10 +OpBranch %38 +%38 = OpLabel +%39 = OpPhi %8 %10 %20 %53 %52 +OpLoopMerge %54 %52 None +OpBranch %40 +%40 = OpLabel +%41 = OpSLessThan %12 %39 %11 +OpBranchConditional %41 %42 %54 +%42 = OpLabel +OpSelectionMerge %51 None +OpSwitch %39 %47 1 %43 +%43 = OpLabel +OpBranch %51 +%47 = OpLabel +%48 = OpAccessChain %18 %4 %39 +%49 = OpLoad %13 %48 +%50 = OpAccessChain %18 %5 %39 +OpStore %50 %49 +OpBranch %51 +%51 = OpLabel +OpBranch %52 +%52 = OpLabel +%53 = OpIAdd %8 %39 %19 +OpStore %3 %53 +OpBranch %38 +%54 = OpLabel +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %8 %10 %54 %23 %24 +OpLoopMerge %25 %24 None +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %22 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +OpSelectionMerge %29 None +OpSwitch %22 %30 1 %31 +%30 = OpLabel +OpBranch %29 +%31 = OpLabel +%35 = OpAccessChain %18 %5 %22 +%36 = OpLoad %13 %35 +%37 = OpAccessChain %18 %4 %22 +OpStore %37 %36 +OpBranch %29 +%29 = OpLabel +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %8 %22 %19 +OpStore %3 %23 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(source, expected, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/nested_loops.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/nested_loops.cpp new file mode 100644 index 0000000..651cdef --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/nested_loops.cpp @@ -0,0 +1,795 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/iterator.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" +#include "source/opt/tree_iterator.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; + +bool Validate(const std::vector& bin) { + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + spv_context spvContext = spvContextCreate(target_env); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {bin.data(), bin.size()}; + spv_result_t error = spvValidate(spvContext, &binary, &diagnostic); + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + return error == 0; +} + +using PassClassTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 c; +void main() { + int i = 0; + for (; i < 10; ++i) { + int j = 0; + int k = 0; + for (; j < 11; ++j) {} + for (; k < 12; ++k) {} + } +} +*/ +TEST_F(PassClassTest, BasicVisitFromEntryPoint) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %4 "i" + OpName %5 "j" + OpName %6 "k" + OpName %3 "c" + OpDecorate %3 Location 0 + %7 = OpTypeVoid + %8 = OpTypeFunction %7 + %9 = OpTypeInt 32 1 + %10 = OpTypePointer Function %9 + %11 = OpConstant %9 0 + %12 = OpConstant %9 10 + %13 = OpTypeBool + %14 = OpConstant %9 11 + %15 = OpConstant %9 1 + %16 = OpConstant %9 12 + %17 = OpTypeFloat 32 + %18 = OpTypeVector %17 4 + %19 = OpTypePointer Output %18 + %3 = OpVariable %19 Output + %2 = OpFunction %7 None %8 + %20 = OpLabel + %4 = OpVariable %10 Function + %5 = OpVariable %10 Function + %6 = OpVariable %10 Function + OpStore %4 %11 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %25 = OpLoad %9 %4 + %26 = OpSLessThan %13 %25 %12 + OpBranchConditional %26 %27 %22 + %27 = OpLabel + OpStore %5 %11 + OpStore %6 %11 + OpBranch %28 + %28 = OpLabel + OpLoopMerge %29 %30 None + OpBranch %31 + %31 = OpLabel + %32 = OpLoad %9 %5 + %33 = OpSLessThan %13 %32 %14 + OpBranchConditional %33 %34 %29 + %34 = OpLabel + OpBranch %30 + %30 = OpLabel + %35 = OpLoad %9 %5 + %36 = OpIAdd %9 %35 %15 + OpStore %5 %36 + OpBranch %28 + %29 = OpLabel + OpBranch %37 + %37 = OpLabel + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %41 = OpLoad %9 %6 + %42 = OpSLessThan %13 %41 %16 + OpBranchConditional %42 %43 %38 + %43 = OpLabel + OpBranch %39 + %39 = OpLabel + %44 = OpLoad %9 %6 + %45 = OpIAdd %9 %44 %15 + OpStore %6 %45 + OpBranch %37 + %38 = OpLabel + OpBranch %23 + %23 = OpLabel + %46 = OpLoad %9 %4 + %47 = OpIAdd %9 %46 %15 + OpStore %4 %47 + OpBranch %21 + %22 = OpLabel + OpReturn + OpFunctionEnd + )"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + EXPECT_EQ(ld.NumLoops(), 3u); + + // Invalid basic block id. + EXPECT_EQ(ld[0u], nullptr); + // Not a loop header. + EXPECT_EQ(ld[20], nullptr); + + Loop& parent_loop = *ld[21]; + EXPECT_TRUE(parent_loop.HasNestedLoops()); + EXPECT_FALSE(parent_loop.IsNested()); + EXPECT_EQ(parent_loop.GetDepth(), 1u); + EXPECT_EQ(std::distance(parent_loop.begin(), parent_loop.end()), 2u); + EXPECT_EQ(parent_loop.GetHeaderBlock(), spvtest::GetBasicBlock(f, 21)); + EXPECT_EQ(parent_loop.GetLatchBlock(), spvtest::GetBasicBlock(f, 23)); + EXPECT_EQ(parent_loop.GetMergeBlock(), spvtest::GetBasicBlock(f, 22)); + + Loop& child_loop_1 = *ld[28]; + EXPECT_FALSE(child_loop_1.HasNestedLoops()); + EXPECT_TRUE(child_loop_1.IsNested()); + EXPECT_EQ(child_loop_1.GetDepth(), 2u); + EXPECT_EQ(std::distance(child_loop_1.begin(), child_loop_1.end()), 0u); + EXPECT_EQ(child_loop_1.GetHeaderBlock(), spvtest::GetBasicBlock(f, 28)); + EXPECT_EQ(child_loop_1.GetLatchBlock(), spvtest::GetBasicBlock(f, 30)); + EXPECT_EQ(child_loop_1.GetMergeBlock(), spvtest::GetBasicBlock(f, 29)); + + Loop& child_loop_2 = *ld[37]; + EXPECT_FALSE(child_loop_2.HasNestedLoops()); + EXPECT_TRUE(child_loop_2.IsNested()); + EXPECT_EQ(child_loop_2.GetDepth(), 2u); + EXPECT_EQ(std::distance(child_loop_2.begin(), child_loop_2.end()), 0u); + EXPECT_EQ(child_loop_2.GetHeaderBlock(), spvtest::GetBasicBlock(f, 37)); + EXPECT_EQ(child_loop_2.GetLatchBlock(), spvtest::GetBasicBlock(f, 39)); + EXPECT_EQ(child_loop_2.GetMergeBlock(), spvtest::GetBasicBlock(f, 38)); +} + +static void CheckLoopBlocks(Loop* loop, + std::unordered_set* expected_ids) { + SCOPED_TRACE("Check loop " + std::to_string(loop->GetHeaderBlock()->id())); + for (uint32_t bb_id : loop->GetBlocks()) { + EXPECT_EQ(expected_ids->count(bb_id), 1u); + expected_ids->erase(bb_id); + } + EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock())); + EXPECT_EQ(expected_ids->size(), 0u); +} + +/* +Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 c; +void main() { + int i = 0; + for (; i < 10; ++i) { + for (int j = 0; j < 11; ++j) { + if (j < 5) { + for (int k = 0; k < 12; ++k) {} + } + else {} + for (int k = 0; k < 12; ++k) {} + } + } +}*/ +TEST_F(PassClassTest, TripleNestedLoop) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %4 "i" + OpName %5 "j" + OpName %6 "k" + OpName %7 "k" + OpName %3 "c" + OpDecorate %3 Location 0 + %8 = OpTypeVoid + %9 = OpTypeFunction %8 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %12 = OpConstant %10 0 + %13 = OpConstant %10 10 + %14 = OpTypeBool + %15 = OpConstant %10 11 + %16 = OpConstant %10 5 + %17 = OpConstant %10 12 + %18 = OpConstant %10 1 + %19 = OpTypeFloat 32 + %20 = OpTypeVector %19 4 + %21 = OpTypePointer Output %20 + %3 = OpVariable %21 Output + %2 = OpFunction %8 None %9 + %22 = OpLabel + %4 = OpVariable %11 Function + %5 = OpVariable %11 Function + %6 = OpVariable %11 Function + %7 = OpVariable %11 Function + OpStore %4 %12 + OpBranch %23 + %23 = OpLabel + OpLoopMerge %24 %25 None + OpBranch %26 + %26 = OpLabel + %27 = OpLoad %10 %4 + %28 = OpSLessThan %14 %27 %13 + OpBranchConditional %28 %29 %24 + %29 = OpLabel + OpStore %5 %12 + OpBranch %30 + %30 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %34 = OpLoad %10 %5 + %35 = OpSLessThan %14 %34 %15 + OpBranchConditional %35 %36 %31 + %36 = OpLabel + %37 = OpLoad %10 %5 + %38 = OpSLessThan %14 %37 %16 + OpSelectionMerge %39 None + OpBranchConditional %38 %40 %39 + %40 = OpLabel + OpStore %6 %12 + OpBranch %41 + %41 = OpLabel + OpLoopMerge %42 %43 None + OpBranch %44 + %44 = OpLabel + %45 = OpLoad %10 %6 + %46 = OpSLessThan %14 %45 %17 + OpBranchConditional %46 %47 %42 + %47 = OpLabel + OpBranch %43 + %43 = OpLabel + %48 = OpLoad %10 %6 + %49 = OpIAdd %10 %48 %18 + OpStore %6 %49 + OpBranch %41 + %42 = OpLabel + OpBranch %39 + %39 = OpLabel + OpStore %7 %12 + OpBranch %50 + %50 = OpLabel + OpLoopMerge %51 %52 None + OpBranch %53 + %53 = OpLabel + %54 = OpLoad %10 %7 + %55 = OpSLessThan %14 %54 %17 + OpBranchConditional %55 %56 %51 + %56 = OpLabel + OpBranch %52 + %52 = OpLabel + %57 = OpLoad %10 %7 + %58 = OpIAdd %10 %57 %18 + OpStore %7 %58 + OpBranch %50 + %51 = OpLabel + OpBranch %32 + %32 = OpLabel + %59 = OpLoad %10 %5 + %60 = OpIAdd %10 %59 %18 + OpStore %5 %60 + OpBranch %30 + %31 = OpLabel + OpBranch %25 + %25 = OpLabel + %61 = OpLoad %10 %4 + %62 = OpIAdd %10 %61 %18 + OpStore %4 %62 + OpBranch %23 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + EXPECT_EQ(ld.NumLoops(), 4u); + + // Invalid basic block id. + EXPECT_EQ(ld[0u], nullptr); + // Not in a loop. + EXPECT_EQ(ld[22], nullptr); + + // Check that we can map basic block to the correct loop. + // The following block ids do not belong to a loop. + for (uint32_t bb_id : {22, 24}) EXPECT_EQ(ld[bb_id], nullptr); + + { + std::unordered_set basic_block_in_loop = { + {23, 26, 29, 30, 33, 36, 40, 41, 44, 47, 43, + 42, 39, 50, 53, 56, 52, 51, 32, 31, 25}}; + Loop* loop = ld[23]; + CheckLoopBlocks(loop, &basic_block_in_loop); + + EXPECT_TRUE(loop->HasNestedLoops()); + EXPECT_FALSE(loop->IsNested()); + EXPECT_EQ(loop->GetDepth(), 1u); + EXPECT_EQ(std::distance(loop->begin(), loop->end()), 1u); + EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 22)); + EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 23)); + EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 25)); + EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 24)); + EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock())); + EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock())); + } + + { + std::unordered_set basic_block_in_loop = { + {30, 33, 36, 40, 41, 44, 47, 43, 42, 39, 50, 53, 56, 52, 51, 32}}; + Loop* loop = ld[30]; + CheckLoopBlocks(loop, &basic_block_in_loop); + + EXPECT_TRUE(loop->HasNestedLoops()); + EXPECT_TRUE(loop->IsNested()); + EXPECT_EQ(loop->GetDepth(), 2u); + EXPECT_EQ(std::distance(loop->begin(), loop->end()), 2u); + EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 29)); + EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 30)); + EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 32)); + EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 31)); + EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock())); + EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock())); + } + + { + std::unordered_set basic_block_in_loop = {{41, 44, 47, 43}}; + Loop* loop = ld[41]; + CheckLoopBlocks(loop, &basic_block_in_loop); + + EXPECT_FALSE(loop->HasNestedLoops()); + EXPECT_TRUE(loop->IsNested()); + EXPECT_EQ(loop->GetDepth(), 3u); + EXPECT_EQ(std::distance(loop->begin(), loop->end()), 0u); + EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 40)); + EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 41)); + EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 43)); + EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 42)); + EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock())); + EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock())); + } + + { + std::unordered_set basic_block_in_loop = {{50, 53, 56, 52}}; + Loop* loop = ld[50]; + CheckLoopBlocks(loop, &basic_block_in_loop); + + EXPECT_FALSE(loop->HasNestedLoops()); + EXPECT_TRUE(loop->IsNested()); + EXPECT_EQ(loop->GetDepth(), 3u); + EXPECT_EQ(std::distance(loop->begin(), loop->end()), 0u); + EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 39)); + EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 50)); + EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 52)); + EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 51)); + EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock())); + EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock())); + } + + // Make sure LoopDescriptor gives us the inner most loop when we query for + // loops. + for (const BasicBlock& bb : *f) { + if (Loop* loop = ld[&bb]) { + for (Loop& sub_loop : + make_range(++TreeDFIterator(loop), TreeDFIterator())) { + EXPECT_FALSE(sub_loop.IsInsideLoop(bb.id())); + } + } + } +} + +/* +Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 c; +void main() { + for (int i = 0; i < 10; ++i) { + for (int j = 0; j < 11; ++j) { + for (int k = 0; k < 11; ++k) {} + } + for (int k = 0; k < 12; ++k) {} + } +} +*/ +TEST_F(PassClassTest, LoopParentTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %4 "i" + OpName %5 "j" + OpName %6 "k" + OpName %7 "k" + OpName %3 "c" + OpDecorate %3 Location 0 + %8 = OpTypeVoid + %9 = OpTypeFunction %8 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %12 = OpConstant %10 0 + %13 = OpConstant %10 10 + %14 = OpTypeBool + %15 = OpConstant %10 11 + %16 = OpConstant %10 1 + %17 = OpConstant %10 12 + %18 = OpTypeFloat 32 + %19 = OpTypeVector %18 4 + %20 = OpTypePointer Output %19 + %3 = OpVariable %20 Output + %2 = OpFunction %8 None %9 + %21 = OpLabel + %4 = OpVariable %11 Function + %5 = OpVariable %11 Function + %6 = OpVariable %11 Function + %7 = OpVariable %11 Function + OpStore %4 %12 + OpBranch %22 + %22 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %26 = OpLoad %10 %4 + %27 = OpSLessThan %14 %26 %13 + OpBranchConditional %27 %28 %23 + %28 = OpLabel + OpStore %5 %12 + OpBranch %29 + %29 = OpLabel + OpLoopMerge %30 %31 None + OpBranch %32 + %32 = OpLabel + %33 = OpLoad %10 %5 + %34 = OpSLessThan %14 %33 %15 + OpBranchConditional %34 %35 %30 + %35 = OpLabel + OpStore %6 %12 + OpBranch %36 + %36 = OpLabel + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %40 = OpLoad %10 %6 + %41 = OpSLessThan %14 %40 %15 + OpBranchConditional %41 %42 %37 + %42 = OpLabel + OpBranch %38 + %38 = OpLabel + %43 = OpLoad %10 %6 + %44 = OpIAdd %10 %43 %16 + OpStore %6 %44 + OpBranch %36 + %37 = OpLabel + OpBranch %31 + %31 = OpLabel + %45 = OpLoad %10 %5 + %46 = OpIAdd %10 %45 %16 + OpStore %5 %46 + OpBranch %29 + %30 = OpLabel + OpStore %7 %12 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %48 %49 None + OpBranch %50 + %50 = OpLabel + %51 = OpLoad %10 %7 + %52 = OpSLessThan %14 %51 %17 + OpBranchConditional %52 %53 %48 + %53 = OpLabel + OpBranch %49 + %49 = OpLabel + %54 = OpLoad %10 %7 + %55 = OpIAdd %10 %54 %16 + OpStore %7 %55 + OpBranch %47 + %48 = OpLabel + OpBranch %24 + %24 = OpLabel + %56 = OpLoad %10 %4 + %57 = OpIAdd %10 %56 %16 + OpStore %4 %57 + OpBranch %22 + %23 = OpLabel + OpReturn + OpFunctionEnd + )"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + EXPECT_EQ(ld.NumLoops(), 4u); + + { + Loop& loop = *ld[22]; + EXPECT_TRUE(loop.HasNestedLoops()); + EXPECT_FALSE(loop.IsNested()); + EXPECT_EQ(loop.GetDepth(), 1u); + EXPECT_EQ(loop.GetParent(), nullptr); + } + + { + Loop& loop = *ld[29]; + EXPECT_TRUE(loop.HasNestedLoops()); + EXPECT_TRUE(loop.IsNested()); + EXPECT_EQ(loop.GetDepth(), 2u); + EXPECT_EQ(loop.GetParent(), ld[22]); + } + + { + Loop& loop = *ld[36]; + EXPECT_FALSE(loop.HasNestedLoops()); + EXPECT_TRUE(loop.IsNested()); + EXPECT_EQ(loop.GetDepth(), 3u); + EXPECT_EQ(loop.GetParent(), ld[29]); + } + + { + Loop& loop = *ld[47]; + EXPECT_FALSE(loop.HasNestedLoops()); + EXPECT_TRUE(loop.IsNested()); + EXPECT_EQ(loop.GetDepth(), 2u); + EXPECT_EQ(loop.GetParent(), ld[22]); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store +The preheader of loop %33 and %41 were removed as well. + +#version 330 core +void main() { + int a = 0; + for (int i = 0; i < 10; ++i) { + if (i == 0) { + a = 1; + } else { + a = 2; + } + for (int j = 0; j < 11; ++j) { + a++; + } + } + for (int k = 0; k < 12; ++k) {} +} +*/ +TEST_F(PassClassTest, CreatePreheaderTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpConstant %5 0 + %8 = OpConstant %5 10 + %9 = OpTypeBool + %10 = OpConstant %5 1 + %11 = OpConstant %5 2 + %12 = OpConstant %5 11 + %13 = OpConstant %5 12 + %14 = OpUndef %5 + %2 = OpFunction %3 None %4 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %5 %7 %15 %18 %19 + %20 = OpPhi %5 %7 %15 %21 %19 + %22 = OpPhi %5 %14 %15 %23 %19 + OpLoopMerge %41 %19 None + OpBranch %25 + %25 = OpLabel + %26 = OpSLessThan %9 %20 %8 + OpBranchConditional %26 %27 %41 + %27 = OpLabel + %28 = OpIEqual %9 %20 %7 + OpSelectionMerge %33 None + OpBranchConditional %28 %30 %31 + %30 = OpLabel + OpBranch %33 + %31 = OpLabel + OpBranch %33 + %33 = OpLabel + %18 = OpPhi %5 %10 %30 %11 %31 %34 %35 + %23 = OpPhi %5 %7 %30 %7 %31 %36 %35 + OpLoopMerge %37 %35 None + OpBranch %38 + %38 = OpLabel + %39 = OpSLessThan %9 %23 %12 + OpBranchConditional %39 %40 %37 + %40 = OpLabel + %34 = OpIAdd %5 %18 %10 + OpBranch %35 + %35 = OpLabel + %36 = OpIAdd %5 %23 %10 + OpBranch %33 + %37 = OpLabel + OpBranch %19 + %19 = OpLabel + %21 = OpIAdd %5 %20 %10 + OpBranch %16 + %41 = OpLabel + %42 = OpPhi %5 %7 %25 %43 %44 + OpLoopMerge %45 %44 None + OpBranch %46 + %46 = OpLabel + %47 = OpSLessThan %9 %42 %13 + OpBranchConditional %47 %48 %45 + %48 = OpLabel + OpBranch %44 + %44 = OpLabel + %43 = OpIAdd %5 %42 %10 + OpBranch %41 + %45 = OpLabel + OpReturn + OpFunctionEnd + )"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + // No invalidation of the cfg should occur during this test. + CFG* cfg = context->cfg(); + + EXPECT_EQ(ld.NumLoops(), 3u); + + { + Loop& loop = *ld[16]; + EXPECT_TRUE(loop.HasNestedLoops()); + EXPECT_FALSE(loop.IsNested()); + EXPECT_EQ(loop.GetDepth(), 1u); + EXPECT_EQ(loop.GetParent(), nullptr); + } + + { + Loop& loop = *ld[33]; + EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr); + EXPECT_NE(loop.GetOrCreatePreHeaderBlock(), nullptr); + // Make sure the loop descriptor was properly updated. + EXPECT_EQ(ld[loop.GetPreHeaderBlock()], ld[16]); + { + const std::vector& preds = + cfg->preds(loop.GetPreHeaderBlock()->id()); + std::unordered_set pred_set(preds.begin(), preds.end()); + EXPECT_EQ(pred_set.size(), 2u); + EXPECT_TRUE(pred_set.count(30)); + EXPECT_TRUE(pred_set.count(31)); + // Check the phi instructions. + loop.GetPreHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) { + for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) { + EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i))); + } + }); + } + { + const std::vector& preds = + cfg->preds(loop.GetHeaderBlock()->id()); + std::unordered_set pred_set(preds.begin(), preds.end()); + EXPECT_EQ(pred_set.size(), 2u); + EXPECT_TRUE(pred_set.count(loop.GetPreHeaderBlock()->id())); + EXPECT_TRUE(pred_set.count(35)); + // Check the phi instructions. + loop.GetHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) { + for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) { + EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i))); + } + }); + } + } + + { + Loop& loop = *ld[41]; + EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr); + EXPECT_NE(loop.GetOrCreatePreHeaderBlock(), nullptr); + EXPECT_EQ(ld[loop.GetPreHeaderBlock()], nullptr); + EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id()).size(), 1u); + EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id())[0], 25u); + // Check the phi instructions. + loop.GetPreHeaderBlock()->ForEachPhiInst([](Instruction* phi) { + EXPECT_EQ(phi->NumInOperands(), 2u); + EXPECT_EQ(phi->GetSingleWordInOperand(1), 25u); + }); + { + const std::vector& preds = + cfg->preds(loop.GetHeaderBlock()->id()); + std::unordered_set pred_set(preds.begin(), preds.end()); + EXPECT_EQ(pred_set.size(), 2u); + EXPECT_TRUE(pred_set.count(loop.GetPreHeaderBlock()->id())); + EXPECT_TRUE(pred_set.count(44)); + // Check the phi instructions. + loop.GetHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) { + for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) { + EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i))); + } + }); + } + } + + // Make sure pre-header insertion leaves the module valid. + std::vector bin; + context->module()->ToBinary(&bin, true); + EXPECT_TRUE(Validate(bin)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/pch_test_opt_loop.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/pch_test_opt_loop.cpp new file mode 100644 index 0000000..f4ac7b2 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/pch_test_opt_loop.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_test_opt_loop.h" diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/pch_test_opt_loop.h b/third_party/spirv-tools/test/opt/loop_optimizations/pch_test_opt_loop.h new file mode 100644 index 0000000..4e8106f --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/pch_test_opt_loop.h @@ -0,0 +1,25 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "source/opt/iterator.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/tree_iterator.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/peeling.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/peeling.cpp new file mode 100644 index 0000000..10d8add --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/peeling.cpp @@ -0,0 +1,1186 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "source/opt/ir_builder.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_peeling.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using PeelingTest = PassTest<::testing::Test>; + +bool Validate(const std::vector& bin) { + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + spv_context spvContext = spvContextCreate(target_env); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {bin.data(), bin.size()}; + spv_result_t error = spvValidate(spvContext, &binary, &diagnostic); + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + return error == 0; +} + +void Match(const std::string& checks, IRContext* context) { + // Silence unused warnings with !defined(SPIRV_EFFCE) + (void)checks; + + std::vector bin; + context->module()->ToBinary(&bin, true); + EXPECT_TRUE(Validate(bin)); + std::string assembly; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_2); + EXPECT_TRUE( + tools.Disassemble(bin, &assembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)) + << "Disassembling failed for shader:\n" + << assembly << std::endl; + auto match_result = effcee::Match(assembly, checks); + EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) + << match_result.message() << "\nChecking result:\n" + << assembly; +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +First test: +#version 330 core +void main() { + for(int i = 0; i < 10; ++i) { + if (i < 4) + break; + } +} + +Second test (with a common sub-expression elimination): +#version 330 core +void main() { + for(int i = 0; i + 1 < 10; ++i) { + } +} + +Third test: +#version 330 core +void main() { + int a[10]; + for (int i = 0; a[i] != 0; i++) {} +} + +Forth test: +#version 330 core +void main() { + for (long i = 0; i < 10; i++) {} +} + +Fifth test: +#version 330 core +void main() { + for (float i = 0; i < 10; i++) {} +} + +Sixth test: +#version 450 +layout(location = 0)out float o; +void main() { + o = 0.0; + for( int i = 0; true; i++ ) { + o += 1.0; + if (i > 10) break; + } +} +*/ +TEST_F(PeelingTest, CannotPeel) { + // Build the given SPIR-V program in |text|, take the first loop in the first + // function and test that it is not peelable. |loop_count_id| is the id + // representing the loop count, if equals to 0, then the function build a 10 + // constant as loop count. + auto test_cannot_peel = [](const std::string& text, uint32_t loop_count_id) { + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + Instruction* loop_count = nullptr; + if (loop_count_id) { + loop_count = context->get_def_use_mgr()->GetDef(loop_count_id); + } else { + InstructionBuilder builder(context.get(), &*f.begin()); + // Exit condition. + loop_count = builder.GetSintConstant(10); + } + + LoopPeeling peel(&*ld.begin(), loop_count); + EXPECT_FALSE(peel.CanPeelLoop()); + }; + { + SCOPED_TRACE("loop with break"); + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_4 = OpConstant %int 4 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %28 = OpPhi %int %int_0 %5 %27 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %bool %28 %int_10 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %21 = OpSLessThan %bool %28 %int_4 + OpSelectionMerge %23 None + OpBranchConditional %21 %22 %23 + %22 = OpLabel + OpBranch %12 + %23 = OpLabel + OpBranch %13 + %13 = OpLabel + %27 = OpIAdd %int %28 %int_1 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + test_cannot_peel(text, 0); + } + + { + SCOPED_TRACE("Ambiguous iterator update"); + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %23 = OpPhi %int %int_0 %5 %17 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %17 = OpIAdd %int %23 %int_1 + %20 = OpSLessThan %bool %17 %int_10 + OpBranchConditional %20 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + test_cannot_peel(text, 0); + } + + { + SCOPED_TRACE("No loop static bounds"); + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + OpName %i "i" + OpName %a "a" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_int_uint_10 = OpTypeArray %int %uint_10 +%_ptr_Function__arr_int_uint_10 = OpTypePointer Function %_arr_int_uint_10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %a = OpVariable %_ptr_Function__arr_int_uint_10 Function + OpStore %i %int_0 + OpBranch %10 + %10 = OpLabel + %28 = OpPhi %int %int_0 %5 %27 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %21 = OpAccessChain %_ptr_Function_int %a %28 + %22 = OpLoad %int %21 + %24 = OpINotEqual %bool %22 %int_0 + OpBranchConditional %24 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %27 = OpIAdd %int %28 %int_1 + OpStore %i %27 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + test_cannot_peel(text, 22); + } + { + SCOPED_TRACE("Int 64 type for conditions"); + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginLowerLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %4 "i" + %6 = OpTypeVoid + %3 = OpTypeFunction %6 + %7 = OpTypeInt 64 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 0 + %15 = OpConstant %7 10 + %16 = OpTypeBool + %17 = OpConstant %7 1 + %2 = OpFunction %6 None %3 + %5 = OpLabel + %4 = OpVariable %8 Function + OpStore %4 %9 + OpBranch %10 + %10 = OpLabel + %22 = OpPhi %7 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %16 %22 %15 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %7 %22 %17 + OpStore %4 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + // %15 is a constant for a 64 int. Currently rejected. + test_cannot_peel(text, 15); + } + { + SCOPED_TRACE("Float type for conditions"); + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginLowerLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %4 "i" + %6 = OpTypeVoid + %3 = OpTypeFunction %6 + %7 = OpTypeFloat 32 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 0 + %15 = OpConstant %7 10 + %16 = OpTypeBool + %17 = OpConstant %7 1 + %2 = OpFunction %6 None %3 + %5 = OpLabel + %4 = OpVariable %8 Function + OpStore %4 %9 + OpBranch %10 + %10 = OpLabel + %22 = OpPhi %7 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpFOrdLessThan %16 %22 %15 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpFAdd %7 %22 %17 + OpStore %4 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + // %15 is a constant for a float. Currently rejected. + test_cannot_peel(text, 15); + } + { + SCOPED_TRACE("Side effect before exit"); + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %o + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %o "o" + OpName %i "i" + OpDecorate %o Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %o = OpVariable %_ptr_Output_float Output + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %true = OpConstantTrue %bool + %float_1 = OpConstant %float 1 + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + OpStore %o %float_0 + OpStore %i %int_0 + OpBranch %14 + %14 = OpLabel + %33 = OpPhi %int %int_0 %5 %32 %17 + OpLoopMerge %16 %17 None + OpBranch %15 + %15 = OpLabel + %22 = OpLoad %float %o + %23 = OpFAdd %float %22 %float_1 + OpStore %o %23 + %26 = OpSGreaterThan %bool %33 %int_10 + OpSelectionMerge %28 None + OpBranchConditional %26 %27 %28 + %27 = OpLabel + OpBranch %16 + %28 = OpLabel + OpBranch %17 + %17 = OpLabel + %32 = OpIAdd %int %33 %int_1 + OpStore %i %32 + OpBranch %14 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + test_cannot_peel(text, 0); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +void main() { + int i = 0; + for (; i < 10; i++) {} +} +*/ +TEST_F(PeelingTest, SimplePeeling) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %22 = OpPhi %int %int_0 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %bool %22 %int_10 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %int %22 %int_1 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + // Peel before. + { + SCOPED_TRACE("Peel before"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + InstructionBuilder builder(context.get(), &*f.begin()); + // Exit condition. + Instruction* ten_cst = builder.GetSintConstant(10); + + LoopPeeling peel(&*ld.begin(), ten_cst); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelBefore(2); + + const std::string check = R"( +CHECK: [[CST_TEN:%\w+]] = OpConstant {{%\w+}} 10 +CHECK: [[CST_TWO:%\w+]] = OpConstant {{%\w+}} 2 +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} [[CST_TWO]] [[CST_TEN]] +CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] [[CST_TWO]] [[CST_TEN]] +CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel +CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]] +CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None +CHECK: [[COND_BLOCK:%\w+]] = OpLabel +CHECK-NEXT: OpSLessThan +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]] +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]] +CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranch [[BEFORE_LOOP]] + +CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]] +CHECK-NEXT: OpLoopMerge +)"; + + Match(check, context.get()); + } + + // Peel after. + { + SCOPED_TRACE("Peel after"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + InstructionBuilder builder(context.get(), &*f.begin()); + // Exit condition. + Instruction* ten_cst = builder.GetSintConstant(10); + + LoopPeeling peel(&*ld.begin(), ten_cst); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelAfter(2); + + const std::string check = R"( +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]] +CHECK: [[BEFORE_LOOP]] = OpLabel +CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]] +CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None +CHECK: [[COND_BLOCK:%\w+]] = OpLabel +CHECK-NEXT: OpSLessThan +CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}} +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]] +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]] +CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranch [[BEFORE_LOOP]] + +CHECK: [[IF_MERGE]] = OpLabel +CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]] +CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]] +CHECK-NEXT: OpLoopMerge + +)"; + + Match(check, context.get()); + } + + // Same as above, but reuse the induction variable. + // Peel before. + { + SCOPED_TRACE("Peel before with IV reuse"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + InstructionBuilder builder(context.get(), &*f.begin()); + // Exit condition. + Instruction* ten_cst = builder.GetSintConstant(10); + + LoopPeeling peel(&*ld.begin(), ten_cst, + context->get_def_use_mgr()->GetDef(22)); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelBefore(2); + + const std::string check = R"( +CHECK: [[CST_TEN:%\w+]] = OpConstant {{%\w+}} 10 +CHECK: [[CST_TWO:%\w+]] = OpConstant {{%\w+}} 2 +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} [[CST_TWO]] [[CST_TEN]] +CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] [[CST_TWO]] [[CST_TEN]] +CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel +CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None +CHECK: [[COND_BLOCK:%\w+]] = OpLabel +CHECK-NEXT: OpSLessThan +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[i]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]] +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]] +CHECK-NEXT: OpBranch [[BEFORE_LOOP]] + +CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]] +CHECK-NEXT: OpLoopMerge +)"; + + Match(check, context.get()); + } + + // Peel after. + { + SCOPED_TRACE("Peel after IV reuse"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + InstructionBuilder builder(context.get(), &*f.begin()); + // Exit condition. + Instruction* ten_cst = builder.GetSintConstant(10); + + LoopPeeling peel(&*ld.begin(), ten_cst, + context->get_def_use_mgr()->GetDef(22)); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelAfter(2); + + const std::string check = R"( +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]] +CHECK: [[BEFORE_LOOP]] = OpLabel +CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None +CHECK: [[COND_BLOCK:%\w+]] = OpLabel +CHECK-NEXT: OpSLessThan +CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[I]] {{%\w+}} +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]] +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]] +CHECK-NEXT: OpBranch [[BEFORE_LOOP]] + +CHECK: [[IF_MERGE]] = OpLabel +CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]] +CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]] +CHECK-NEXT: OpLoopMerge + +)"; + + Match(check, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +void main() { + int a[10]; + int n = a[0]; + for(int i = 0; i < n; ++i) {} +} +*/ +TEST_F(PeelingTest, PeelingUncountable) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + OpName %a "a" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %uint = OpTypeInt 32 0 + %uint_10 = OpConstant %uint 10 +%_arr_int_uint_10 = OpTypeArray %int %uint_10 +%_ptr_Function__arr_int_uint_10 = OpTypePointer Function %_arr_int_uint_10 + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %a = OpVariable %_ptr_Function__arr_int_uint_10 Function + %15 = OpAccessChain %_ptr_Function_int %a %int_0 + %16 = OpLoad %int %15 + OpBranch %18 + %18 = OpLabel + %30 = OpPhi %int %int_0 %5 %29 %21 + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + %26 = OpSLessThan %bool %30 %16 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + OpBranch %21 + %21 = OpLabel + %29 = OpIAdd %int %30 %int_1 + OpBranch %18 + %20 = OpLabel + OpReturn + OpFunctionEnd + )"; + + // Peel before. + { + SCOPED_TRACE("Peel before"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + Instruction* loop_count = context->get_def_use_mgr()->GetDef(16); + EXPECT_EQ(loop_count->opcode(), SpvOpLoad); + + LoopPeeling peel(&*ld.begin(), loop_count); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelBefore(1); + + const std::string check = R"( +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[LOOP_COUNT:%\w+]] = OpLoad +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} {{%\w+}} [[LOOP_COUNT]] +CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] {{%\w+}} [[LOOP_COUNT]] +CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel +CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]] +CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None +CHECK: [[COND_BLOCK:%\w+]] = OpLabel +CHECK-NEXT: OpSLessThan +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]] +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]] +CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranch [[BEFORE_LOOP]] + +CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]] +CHECK-NEXT: OpLoopMerge +)"; + + Match(check, context.get()); + } + + // Peel after. + { + SCOPED_TRACE("Peel after"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + Instruction* loop_count = context->get_def_use_mgr()->GetDef(16); + EXPECT_EQ(loop_count->opcode(), SpvOpLoad); + + LoopPeeling peel(&*ld.begin(), loop_count); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelAfter(1); + + const std::string check = R"( +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]] +CHECK: [[BEFORE_LOOP]] = OpLabel +CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]] +CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None +CHECK: [[COND_BLOCK:%\w+]] = OpLabel +CHECK-NEXT: OpSLessThan +CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}} +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]] +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]] +CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranch [[BEFORE_LOOP]] + +CHECK: [[IF_MERGE]] = OpLabel +CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]] +CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]] +CHECK-NEXT: OpLoopMerge + +)"; + + Match(check, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +void main() { + int i = 0; + do { + i++; + } while (i < 10); +} +*/ +TEST_F(PeelingTest, DoWhilePeeling) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %21 = OpPhi %int %int_0 %5 %16 %13 + OpLoopMerge %12 %13 None + OpBranch %11 + %11 = OpLabel + %16 = OpIAdd %int %21 %int_1 + OpBranch %13 + %13 = OpLabel + %20 = OpSLessThan %bool %16 %int_10 + OpBranchConditional %20 %10 %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + // Peel before. + { + SCOPED_TRACE("Peel before"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + InstructionBuilder builder(context.get(), &*f.begin()); + // Exit condition. + Instruction* ten_cst = builder.GetUintConstant(10); + + LoopPeeling peel(&*ld.begin(), ten_cst); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelBefore(2); + + const std::string check = R"( +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpULessThan {{%\w+}} +CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] +CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel +CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]] +CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]] +CHECK: [[BE]] = OpLabel +CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpULessThan {{%\w+}} [[DUMMY_IT_1]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] [[BEFORE_LOOP]] [[AFTER_LOOP_PREHEADER]] + +CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[I_1]] [[AFTER_LOOP_PREHEADER]] +CHECK-NEXT: OpLoopMerge +)"; + + Match(check, context.get()); + } + + // Peel after. + { + SCOPED_TRACE("Peel after"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + InstructionBuilder builder(context.get(), &*f.begin()); + // Exit condition. + Instruction* ten_cst = builder.GetUintConstant(10); + + LoopPeeling peel(&*ld.begin(), ten_cst); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelAfter(2); + + const std::string check = R"( +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpULessThan {{%\w+}} +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]] +CHECK: [[BEFORE_LOOP]] = OpLabel +CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]] +CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]] +CHECK: [[BE]] = OpLabel +CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: [[EXIT_VAL:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT_1]] +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpULessThan {{%\w+}} [[EXIT_VAL]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] [[BEFORE_LOOP]] [[BEFORE_LOOP_MERGE]] + +CHECK: [[IF_MERGE]] = OpLabel +CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I_1]] [[BEFORE_LOOP_MERGE]] +CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]] +CHECK-NEXT: OpLoopMerge +)"; + + Match(check, context.get()); + } +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +void main() { + int a[10]; + int n = a[0]; + for(int i = 0; i < n; ++i) {} +} +*/ +TEST_F(PeelingTest, PeelingLoopWithStore) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %o %n + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %o "o" + OpName %end "end" + OpName %n "n" + OpName %i "i" + OpDecorate %o Location 0 + OpDecorate %n Flat + OpDecorate %n Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %o = OpVariable %_ptr_Output_float Output + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%_ptr_Input_int = OpTypePointer Input %int + %n = OpVariable %_ptr_Input_int Input + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %float_1 = OpConstant %float 1 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %end = OpVariable %_ptr_Function_int Function + %i = OpVariable %_ptr_Function_int Function + OpStore %o %float_0 + %15 = OpLoad %int %n + OpStore %end %15 + OpStore %i %int_0 + OpBranch %18 + %18 = OpLabel + %33 = OpPhi %int %int_0 %5 %32 %21 + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + %26 = OpSLessThan %bool %33 %15 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + %28 = OpLoad %float %o + %29 = OpFAdd %float %28 %float_1 + OpStore %o %29 + OpBranch %21 + %21 = OpLabel + %32 = OpIAdd %int %33 %int_1 + OpStore %i %32 + OpBranch %18 + %20 = OpLabel + OpReturn + OpFunctionEnd + )"; + + // Peel before. + { + SCOPED_TRACE("Peel before"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + Instruction* loop_count = context->get_def_use_mgr()->GetDef(15); + EXPECT_EQ(loop_count->opcode(), SpvOpLoad); + + LoopPeeling peel(&*ld.begin(), loop_count); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelBefore(1); + + const std::string check = R"( +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[LOOP_COUNT:%\w+]] = OpLoad +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} {{%\w+}} [[LOOP_COUNT]] +CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] {{%\w+}} [[LOOP_COUNT]] +CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel +CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]] +CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None +CHECK: [[COND_BLOCK:%\w+]] = OpLabel +CHECK-NEXT: OpSLessThan +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]] +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]] +CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranch [[BEFORE_LOOP]] + +CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]] +CHECK-NEXT: OpLoopMerge +)"; + + Match(check, context.get()); + } + + // Peel after. + { + SCOPED_TRACE("Peel after"); + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function& f = *module->begin(); + LoopDescriptor& ld = *context->GetLoopDescriptor(&f); + + EXPECT_EQ(ld.NumLoops(), 1u); + + Instruction* loop_count = context->get_def_use_mgr()->GetDef(15); + EXPECT_EQ(loop_count->opcode(), SpvOpLoad); + + LoopPeeling peel(&*ld.begin(), loop_count); + EXPECT_TRUE(peel.CanPeelLoop()); + peel.PeelAfter(1); + + const std::string check = R"( +CHECK: OpFunction +CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel +CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} +CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]] +CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]] +CHECK: [[BEFORE_LOOP]] = OpLabel +CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]] +CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]] +CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None +CHECK: [[COND_BLOCK:%\w+]] = OpLabel +CHECK-NEXT: OpSLessThan +CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}} +CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]] +CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]] +CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]] +CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]] +CHECK-NEXT: OpBranch [[BEFORE_LOOP]] + +CHECK: [[IF_MERGE]] = OpLabel +CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]] +CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]] + +CHECK: [[AFTER_LOOP]] = OpLabel +CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]] +CHECK-NEXT: OpLoopMerge + +)"; + + Match(check, context.get()); + } +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/peeling_pass.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/peeling_pass.cpp new file mode 100644 index 0000000..284ad83 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/peeling_pass.cpp @@ -0,0 +1,1099 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/ir_builder.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_peeling.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +class PeelingPassTest : public PassTest<::testing::Test> { + public: + // Generic routine to run the loop peeling pass and check + LoopPeelingPass::LoopPeelingStats AssembleAndRunPeelingTest( + const std::string& text_head, const std::string& text_tail, SpvOp opcode, + const std::string& res_id, const std::string& op1, + const std::string& op2) { + std::string opcode_str; + switch (opcode) { + case SpvOpSLessThan: + opcode_str = "OpSLessThan"; + break; + case SpvOpSGreaterThan: + opcode_str = "OpSGreaterThan"; + break; + case SpvOpSLessThanEqual: + opcode_str = "OpSLessThanEqual"; + break; + case SpvOpSGreaterThanEqual: + opcode_str = "OpSGreaterThanEqual"; + break; + case SpvOpIEqual: + opcode_str = "OpIEqual"; + break; + case SpvOpINotEqual: + opcode_str = "OpINotEqual"; + break; + default: + assert(false && "Unhandled"); + break; + } + std::string test_cond = + res_id + " = " + opcode_str + " %bool " + op1 + " " + op2 + "\n"; + + LoopPeelingPass::LoopPeelingStats stats; + SinglePassRunAndDisassemble( + text_head + test_cond + text_tail, true, true, &stats); + + return stats; + } + + // Generic routine to run the loop peeling pass and check + LoopPeelingPass::LoopPeelingStats RunPeelingTest( + const std::string& text_head, const std::string& text_tail, SpvOp opcode, + const std::string& res_id, const std::string& op1, const std::string& op2, + size_t nb_of_loops) { + LoopPeelingPass::LoopPeelingStats stats = AssembleAndRunPeelingTest( + text_head, text_tail, opcode, res_id, op1, op2); + + Function& f = *context()->module()->begin(); + LoopDescriptor& ld = *context()->GetLoopDescriptor(&f); + EXPECT_EQ(ld.NumLoops(), nb_of_loops); + + return stats; + } + + using PeelTraceType = + std::vector>; + + void BuildAndCheckTrace(const std::string& text_head, + const std::string& text_tail, SpvOp opcode, + const std::string& res_id, const std::string& op1, + const std::string& op2, + const PeelTraceType& expected_peel_trace, + size_t expected_nb_of_loops) { + auto stats = RunPeelingTest(text_head, text_tail, opcode, res_id, op1, op2, + expected_nb_of_loops); + + EXPECT_EQ(stats.peeled_loops_.size(), expected_peel_trace.size()); + if (stats.peeled_loops_.size() != expected_peel_trace.size()) { + return; + } + + PeelTraceType::const_iterator expected_trace_it = + expected_peel_trace.begin(); + decltype(stats.peeled_loops_)::const_iterator stats_it = + stats.peeled_loops_.begin(); + + while (expected_trace_it != expected_peel_trace.end()) { + EXPECT_EQ(expected_trace_it->first, std::get<1>(*stats_it)); + EXPECT_EQ(expected_trace_it->second, std::get<2>(*stats_it)); + ++expected_trace_it; + ++stats_it; + } + } +}; + +/* +Test are derivation of the following generated test from the following GLSL + +--eliminate-local-multi-store + +#version 330 core +void main() { + int a = 0; + for(int i = 1; i < 10; i += 2) { + if (i < 3) { + a += 2; + } + } +} + +The condition is interchanged to test < > <= >= == and peel before/after +opportunities. +*/ +TEST_F(PeelingPassTest, PeelingPassBasic) { + const std::string text_head = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + OpName %a "a" + OpName %i "i" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %bool = OpTypeBool + %int_20 = OpConstant %int 20 + %int_19 = OpConstant %int 19 + %int_18 = OpConstant %int 18 + %int_17 = OpConstant %int 17 + %int_16 = OpConstant %int 16 + %int_15 = OpConstant %int 15 + %int_14 = OpConstant %int 14 + %int_13 = OpConstant %int 13 + %int_12 = OpConstant %int 12 + %int_11 = OpConstant %int 11 + %int_10 = OpConstant %int 10 + %int_9 = OpConstant %int 9 + %int_8 = OpConstant %int 8 + %int_7 = OpConstant %int 7 + %int_6 = OpConstant %int 6 + %int_5 = OpConstant %int 5 + %int_4 = OpConstant %int 4 + %int_3 = OpConstant %int 3 + %int_2 = OpConstant %int 2 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %a = OpVariable %_ptr_Function_int Function + %i = OpVariable %_ptr_Function_int Function + OpStore %a %int_0 + OpStore %i %int_0 + OpBranch %11 + %11 = OpLabel + %31 = OpPhi %int %int_0 %5 %33 %14 + %32 = OpPhi %int %int_1 %5 %30 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %bool %32 %int_20 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + )"; + const std::string text_tail = R"( + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + %27 = OpIAdd %int %31 %int_2 + OpStore %a %27 + OpBranch %24 + %24 = OpLabel + %33 = OpPhi %int %31 %12 %27 %23 + OpBranch %14 + %14 = OpLabel + %30 = OpIAdd %int %32 %int_2 + OpStore %i %30 + OpBranch %11 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto run_test = [&text_head, &text_tail, this](SpvOp opcode, + const std::string& op1, + const std::string& op2) { + auto stats = + RunPeelingTest(text_head, text_tail, opcode, "%22", op1, op2, 2); + + EXPECT_EQ(stats.peeled_loops_.size(), 1u); + if (stats.peeled_loops_.size() != 1u) + return std::pair{ + LoopPeelingPass::PeelDirection::kNone, 0}; + + return std::pair{ + std::get<1>(*stats.peeled_loops_.begin()), + std::get<2>(*stats.peeled_loops_.begin())}; + }; + + // Test LT + // Peel before by a factor of 2. + { + SCOPED_TRACE("Peel before iv < 4"); + + std::pair peel_info = + run_test(SpvOpSLessThan, "%32", "%int_4"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before 4 > iv"); + + std::pair peel_info = + run_test(SpvOpSGreaterThan, "%int_4", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before iv < 5"); + + std::pair peel_info = + run_test(SpvOpSLessThan, "%32", "%int_5"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before 5 > iv"); + + std::pair peel_info = + run_test(SpvOpSGreaterThan, "%int_5", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + + // Peel after by a factor of 2. + { + SCOPED_TRACE("Peel after iv < 16"); + + std::pair peel_info = + run_test(SpvOpSLessThan, "%32", "%int_16"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after 16 > iv"); + + std::pair peel_info = + run_test(SpvOpSGreaterThan, "%int_16", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after iv < 17"); + + std::pair peel_info = + run_test(SpvOpSLessThan, "%32", "%int_17"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after 17 > iv"); + + std::pair peel_info = + run_test(SpvOpSGreaterThan, "%int_17", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + + // Test GT + // Peel before by a factor of 2. + { + SCOPED_TRACE("Peel before iv > 5"); + + std::pair peel_info = + run_test(SpvOpSGreaterThan, "%32", "%int_5"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before 5 < iv"); + + std::pair peel_info = + run_test(SpvOpSLessThan, "%int_5", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before iv > 4"); + + std::pair peel_info = + run_test(SpvOpSGreaterThan, "%32", "%int_4"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before 4 < iv"); + + std::pair peel_info = + run_test(SpvOpSLessThan, "%int_4", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + + // Peel after by a factor of 2. + { + SCOPED_TRACE("Peel after iv > 16"); + + std::pair peel_info = + run_test(SpvOpSGreaterThan, "%32", "%int_16"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after 16 < iv"); + + std::pair peel_info = + run_test(SpvOpSLessThan, "%int_16", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after iv > 17"); + + std::pair peel_info = + run_test(SpvOpSGreaterThan, "%32", "%int_17"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after 17 < iv"); + + std::pair peel_info = + run_test(SpvOpSLessThan, "%int_17", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + + // Test LE + // Peel before by a factor of 2. + { + SCOPED_TRACE("Peel before iv <= 4"); + + std::pair peel_info = + run_test(SpvOpSLessThanEqual, "%32", "%int_4"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before 4 => iv"); + + std::pair peel_info = + run_test(SpvOpSGreaterThanEqual, "%int_4", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before iv <= 3"); + + std::pair peel_info = + run_test(SpvOpSLessThanEqual, "%32", "%int_3"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before 3 => iv"); + + std::pair peel_info = + run_test(SpvOpSGreaterThanEqual, "%int_3", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + + // Peel after by a factor of 2. + { + SCOPED_TRACE("Peel after iv <= 16"); + + std::pair peel_info = + run_test(SpvOpSLessThanEqual, "%32", "%int_16"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after 16 => iv"); + + std::pair peel_info = + run_test(SpvOpSGreaterThanEqual, "%int_16", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after iv <= 15"); + + std::pair peel_info = + run_test(SpvOpSLessThanEqual, "%32", "%int_15"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after 15 => iv"); + + std::pair peel_info = + run_test(SpvOpSGreaterThanEqual, "%int_15", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + + // Test GE + // Peel before by a factor of 2. + { + SCOPED_TRACE("Peel before iv >= 5"); + + std::pair peel_info = + run_test(SpvOpSGreaterThanEqual, "%32", "%int_5"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before 35 >= iv"); + + std::pair peel_info = + run_test(SpvOpSLessThanEqual, "%int_5", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before iv >= 4"); + + std::pair peel_info = + run_test(SpvOpSGreaterThanEqual, "%32", "%int_4"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel before 4 <= iv"); + + std::pair peel_info = + run_test(SpvOpSLessThanEqual, "%int_4", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 2u); + } + + // Peel after by a factor of 2. + { + SCOPED_TRACE("Peel after iv >= 17"); + + std::pair peel_info = + run_test(SpvOpSGreaterThanEqual, "%32", "%int_17"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after 17 <= iv"); + + std::pair peel_info = + run_test(SpvOpSLessThanEqual, "%int_17", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after iv >= 16"); + + std::pair peel_info = + run_test(SpvOpSGreaterThanEqual, "%32", "%int_16"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + { + SCOPED_TRACE("Peel after 16 <= iv"); + + std::pair peel_info = + run_test(SpvOpSLessThanEqual, "%int_16", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 2u); + } + + // Test EQ + // Peel before by a factor of 1. + { + SCOPED_TRACE("Peel before iv == 1"); + + std::pair peel_info = + run_test(SpvOpIEqual, "%32", "%int_1"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 1u); + } + { + SCOPED_TRACE("Peel before 1 == iv"); + + std::pair peel_info = + run_test(SpvOpIEqual, "%int_1", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 1u); + } + + // Peel after by a factor of 1. + { + SCOPED_TRACE("Peel after iv == 19"); + + std::pair peel_info = + run_test(SpvOpIEqual, "%32", "%int_19"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 1u); + } + { + SCOPED_TRACE("Peel after 19 == iv"); + + std::pair peel_info = + run_test(SpvOpIEqual, "%int_19", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 1u); + } + + // Test NE + // Peel before by a factor of 1. + { + SCOPED_TRACE("Peel before iv != 1"); + + std::pair peel_info = + run_test(SpvOpINotEqual, "%32", "%int_1"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 1u); + } + { + SCOPED_TRACE("Peel before 1 != iv"); + + std::pair peel_info = + run_test(SpvOpINotEqual, "%int_1", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore); + EXPECT_EQ(peel_info.second, 1u); + } + + // Peel after by a factor of 1. + { + SCOPED_TRACE("Peel after iv != 19"); + + std::pair peel_info = + run_test(SpvOpINotEqual, "%32", "%int_19"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 1u); + } + { + SCOPED_TRACE("Peel after 19 != iv"); + + std::pair peel_info = + run_test(SpvOpINotEqual, "%int_19", "%32"); + EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter); + EXPECT_EQ(peel_info.second, 1u); + } + + // No peel. + { + SCOPED_TRACE("No Peel: 20 => iv"); + + auto stats = RunPeelingTest(text_head, text_tail, SpvOpSLessThanEqual, + "%22", "%int_20", "%32", 1); + + EXPECT_EQ(stats.peeled_loops_.size(), 0u); + } +} + +/* +Test are derivation of the following generated test from the following GLSL + +--eliminate-local-multi-store + +#version 330 core +void main() { + int a = 0; + for(int i = 0; i < 10; ++i) { + if (i < 3) { + a += 2; + } + if (i < 1) { + a += 2; + } + } +} + +The condition is interchanged to test < > <= >= == and peel before/after +opportunities. +*/ +TEST_F(PeelingPassTest, MultiplePeelingPass) { + const std::string text_head = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + OpName %a "a" + OpName %i "i" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %bool = OpTypeBool + %int_10 = OpConstant %int 10 + %int_9 = OpConstant %int 9 + %int_8 = OpConstant %int 8 + %int_7 = OpConstant %int 7 + %int_6 = OpConstant %int 6 + %int_5 = OpConstant %int 5 + %int_4 = OpConstant %int 4 + %int_3 = OpConstant %int 3 + %int_2 = OpConstant %int 2 + %int_1 = OpConstant %int 1 + %int_0 = OpConstant %int 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %a = OpVariable %_ptr_Function_int Function + %i = OpVariable %_ptr_Function_int Function + OpStore %a %int_0 + OpStore %i %int_0 + OpBranch %11 + %11 = OpLabel + %37 = OpPhi %int %int_0 %5 %40 %14 + %38 = OpPhi %int %int_0 %5 %36 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %bool %38 %int_10 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + )"; + const std::string text_tail = R"( + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %24 + %23 = OpLabel + %27 = OpIAdd %int %37 %int_2 + OpStore %a %27 + OpBranch %24 + %24 = OpLabel + %39 = OpPhi %int %37 %12 %27 %23 + %30 = OpSLessThan %bool %38 %int_1 + OpSelectionMerge %32 None + OpBranchConditional %30 %31 %32 + %31 = OpLabel + %34 = OpIAdd %int %39 %int_2 + OpStore %a %34 + OpBranch %32 + %32 = OpLabel + %40 = OpPhi %int %39 %24 %34 %31 + OpBranch %14 + %14 = OpLabel + %36 = OpIAdd %int %38 %int_1 + OpStore %i %36 + OpBranch %11 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto run_test = [&text_head, &text_tail, this]( + SpvOp opcode, const std::string& op1, + const std::string& op2, + const PeelTraceType& expected_peel_trace) { + BuildAndCheckTrace(text_head, text_tail, opcode, "%22", op1, op2, + expected_peel_trace, expected_peel_trace.size() + 1); + }; + + // Test LT + // Peel before by a factor of 3. + { + SCOPED_TRACE("Peel before iv < 3"); + + run_test(SpvOpSLessThan, "%38", "%int_3", + {{LoopPeelingPass::PeelDirection::kBefore, 3u}}); + } + { + SCOPED_TRACE("Peel before 3 > iv"); + + run_test(SpvOpSGreaterThan, "%int_3", "%38", + {{LoopPeelingPass::PeelDirection::kBefore, 3u}}); + } + + // Peel after by a factor of 2. + { + SCOPED_TRACE("Peel after iv < 8"); + + run_test(SpvOpSLessThan, "%38", "%int_8", + {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); + } + { + SCOPED_TRACE("Peel after 8 > iv"); + + run_test(SpvOpSGreaterThan, "%int_8", "%38", + {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); + } + + // Test GT + // Peel before by a factor of 2. + { + SCOPED_TRACE("Peel before iv > 2"); + + run_test(SpvOpSGreaterThan, "%38", "%int_2", + {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); + } + { + SCOPED_TRACE("Peel before 2 < iv"); + + run_test(SpvOpSLessThan, "%int_2", "%38", + {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); + } + + // Peel after by a factor of 3. + { + SCOPED_TRACE("Peel after iv > 7"); + + run_test(SpvOpSGreaterThan, "%38", "%int_7", + {{LoopPeelingPass::PeelDirection::kAfter, 3u}}); + } + { + SCOPED_TRACE("Peel after 7 < iv"); + + run_test(SpvOpSLessThan, "%int_7", "%38", + {{LoopPeelingPass::PeelDirection::kAfter, 3u}}); + } + + // Test LE + // Peel before by a factor of 2. + { + SCOPED_TRACE("Peel before iv <= 1"); + + run_test(SpvOpSLessThanEqual, "%38", "%int_1", + {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); + } + { + SCOPED_TRACE("Peel before 1 => iv"); + + run_test(SpvOpSGreaterThanEqual, "%int_1", "%38", + {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); + } + + // Peel after by a factor of 2. + { + SCOPED_TRACE("Peel after iv <= 7"); + + run_test(SpvOpSLessThanEqual, "%38", "%int_7", + {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); + } + { + SCOPED_TRACE("Peel after 7 => iv"); + + run_test(SpvOpSGreaterThanEqual, "%int_7", "%38", + {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); + } + + // Test GE + // Peel before by a factor of 2. + { + SCOPED_TRACE("Peel before iv >= 2"); + + run_test(SpvOpSGreaterThanEqual, "%38", "%int_2", + {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); + } + { + SCOPED_TRACE("Peel before 2 <= iv"); + + run_test(SpvOpSLessThanEqual, "%int_2", "%38", + {{LoopPeelingPass::PeelDirection::kBefore, 2u}}); + } + + // Peel after by a factor of 2. + { + SCOPED_TRACE("Peel after iv >= 8"); + + run_test(SpvOpSGreaterThanEqual, "%38", "%int_8", + {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); + } + { + SCOPED_TRACE("Peel after 8 <= iv"); + + run_test(SpvOpSLessThanEqual, "%int_8", "%38", + {{LoopPeelingPass::PeelDirection::kAfter, 2u}}); + } + // Test EQ + // Peel before by a factor of 1. + { + SCOPED_TRACE("Peel before iv == 0"); + + run_test(SpvOpIEqual, "%38", "%int_0", + {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); + } + { + SCOPED_TRACE("Peel before 0 == iv"); + + run_test(SpvOpIEqual, "%int_0", "%38", + {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); + } + + // Peel after by a factor of 1. + { + SCOPED_TRACE("Peel after iv == 9"); + + run_test(SpvOpIEqual, "%38", "%int_9", + {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); + } + { + SCOPED_TRACE("Peel after 9 == iv"); + + run_test(SpvOpIEqual, "%int_9", "%38", + {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); + } + + // Test NE + // Peel before by a factor of 1. + { + SCOPED_TRACE("Peel before iv != 0"); + + run_test(SpvOpINotEqual, "%38", "%int_0", + {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); + } + { + SCOPED_TRACE("Peel before 0 != iv"); + + run_test(SpvOpINotEqual, "%int_0", "%38", + {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); + } + + // Peel after by a factor of 1. + { + SCOPED_TRACE("Peel after iv != 9"); + + run_test(SpvOpINotEqual, "%38", "%int_9", + {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); + } + { + SCOPED_TRACE("Peel after 9 != iv"); + + run_test(SpvOpINotEqual, "%int_9", "%38", + {{LoopPeelingPass::PeelDirection::kBefore, 1u}}); + } +} + +/* +Test are derivation of the following generated test from the following GLSL + +--eliminate-local-multi-store + +#version 330 core +void main() { + int a = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + if (i < 3) { + a += 2; + } + } + } +} +*/ +TEST_F(PeelingPassTest, PeelingNestedPass) { + const std::string text_head = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + OpName %a "a" + OpName %i "i" + OpName %j "j" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_7 = OpConstant %int 7 + %int_3 = OpConstant %int 3 + %int_2 = OpConstant %int 2 + %int_1 = OpConstant %int 1 + %43 = OpUndef %int + %main = OpFunction %void None %3 + %5 = OpLabel + %a = OpVariable %_ptr_Function_int Function + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + OpStore %a %int_0 + OpStore %i %int_0 + OpBranch %11 + %11 = OpLabel + %41 = OpPhi %int %int_0 %5 %45 %14 + %42 = OpPhi %int %int_0 %5 %40 %14 + %44 = OpPhi %int %43 %5 %46 %14 + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThan %bool %42 %int_10 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + OpStore %j %int_0 + OpBranch %21 + %21 = OpLabel + %45 = OpPhi %int %41 %12 %47 %24 + %46 = OpPhi %int %int_0 %12 %38 %24 + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %27 = OpSLessThan %bool %46 %int_10 + OpBranchConditional %27 %22 %23 + %22 = OpLabel + )"; + + const std::string text_tail = R"( + OpSelectionMerge %32 None + OpBranchConditional %30 %31 %32 + %31 = OpLabel + %35 = OpIAdd %int %45 %int_2 + OpStore %a %35 + OpBranch %32 + %32 = OpLabel + %47 = OpPhi %int %45 %22 %35 %31 + OpBranch %24 + %24 = OpLabel + %38 = OpIAdd %int %46 %int_1 + OpStore %j %38 + OpBranch %21 + %23 = OpLabel + OpBranch %14 + %14 = OpLabel + %40 = OpIAdd %int %42 %int_1 + OpStore %i %40 + OpBranch %11 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto run_test = + [&text_head, &text_tail, this]( + SpvOp opcode, const std::string& op1, const std::string& op2, + const PeelTraceType& expected_peel_trace, size_t nb_of_loops) { + BuildAndCheckTrace(text_head, text_tail, opcode, "%30", op1, op2, + expected_peel_trace, nb_of_loops); + }; + + // Peeling outer before by a factor of 3. + { + SCOPED_TRACE("Peel before iv_i < 3"); + + // Expect peel before by a factor of 3 and 4 loops at the end. + run_test(SpvOpSLessThan, "%42", "%int_3", + {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 4); + } + // Peeling outer loop after by a factor of 3. + { + SCOPED_TRACE("Peel after iv_i < 7"); + + // Expect peel after by a factor of 3 and 4 loops at the end. + run_test(SpvOpSLessThan, "%42", "%int_7", + {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 4); + } + + // Peeling inner loop before by a factor of 3. + { + SCOPED_TRACE("Peel before iv_j < 3"); + + // Expect peel before by a factor of 3 and 3 loops at the end. + run_test(SpvOpSLessThan, "%46", "%int_3", + {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 3); + } + // Peeling inner loop after by a factor of 3. + { + SCOPED_TRACE("Peel after iv_j < 7"); + + // Expect peel after by a factor of 3 and 3 loops at the end. + run_test(SpvOpSLessThan, "%46", "%int_7", + {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 3); + } + + // Not unworkable condition. + { + SCOPED_TRACE("No peel"); + + // Expect no peeling and 2 loops at the end. + run_test(SpvOpSLessThan, "%46", "%42", {}, 2); + } + + // Could do a peeling of 3, but the goes over the threshold. + { + SCOPED_TRACE("Over threshold"); + + size_t current_threshold = LoopPeelingPass::GetLoopPeelingThreshold(); + LoopPeelingPass::SetLoopPeelingThreshold(1u); + // Expect no peeling and 2 loops at the end. + run_test(SpvOpSLessThan, "%46", "%int_7", {}, 2); + LoopPeelingPass::SetLoopPeelingThreshold(current_threshold); + } +} +/* +Test are derivation of the following generated test from the following GLSL + +--eliminate-local-multi-store + +#version 330 core +void main() { + int a = 0; + for (int i = 0, j = 0; i < 10; j++, i++) { + if (i < j) { + a += 2; + } + } +} +*/ +TEST_F(PeelingPassTest, PeelingNoChanges) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + OpName %a "a" + OpName %i "i" + OpName %j "j" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_2 = OpConstant %int 2 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %a = OpVariable %_ptr_Function_int Function + %i = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + OpStore %a %int_0 + OpStore %i %int_0 + OpStore %j %int_0 + OpBranch %12 + %12 = OpLabel + %34 = OpPhi %int %int_0 %5 %37 %15 + %35 = OpPhi %int %int_0 %5 %33 %15 + %36 = OpPhi %int %int_0 %5 %31 %15 + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %20 = OpSLessThan %bool %35 %int_10 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %23 = OpSLessThan %bool %35 %36 + OpSelectionMerge %25 None + OpBranchConditional %23 %24 %25 + %24 = OpLabel + %28 = OpIAdd %int %34 %int_2 + OpStore %a %28 + OpBranch %25 + %25 = OpLabel + %37 = OpPhi %int %34 %13 %28 %24 + OpBranch %15 + %15 = OpLabel + %31 = OpIAdd %int %36 %int_1 + OpStore %j %31 + %33 = OpIAdd %int %35 %int_1 + OpStore %i %33 + OpBranch %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + { + auto result = + SinglePassRunAndDisassemble(text, true, false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + } +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/unroll_assumptions.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/unroll_assumptions.cpp new file mode 100644 index 0000000..0f93302 --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/unroll_assumptions.cpp @@ -0,0 +1,1515 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/loop_unroller.h" +#include "source/opt/loop_utils.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +template +class PartialUnrollerTestPass : public Pass { + public: + PartialUnrollerTestPass() : Pass() {} + + const char* name() const override { return "Loop unroller"; } + + Status Process() override { + bool changed = false; + for (Function& f : *context()->module()) { + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); + for (auto& loop : loop_descriptor) { + LoopUtils loop_utils{context(), &loop}; + if (loop_utils.PartiallyUnroll(factor)) { + changed = true; + } + } + } + + if (changed) return Pass::Status::SuccessWithChange; + return Pass::Status::SuccessWithoutChange; + } +}; + +/* +Generated from the following GLSL +#version 410 core +layout(location = 0) flat in int in_upper_bound; +void main() { + for (int i = 0; i < in_upper_bound; ++i) { + x[i] = 1.0f; + } +} +*/ +TEST_F(PassClassTest, CheckUpperBound) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "in_upper_bound" +OpName %4 "x" +OpDecorate %3 Flat +OpDecorate %3 Location 0 +%5 = OpTypeVoid +%6 = OpTypeFunction %5 +%7 = OpTypeInt 32 1 +%8 = OpTypePointer Function %7 +%9 = OpConstant %7 0 +%10 = OpTypePointer Input %7 +%3 = OpVariable %10 Input +%11 = OpTypeBool +%12 = OpTypeFloat 32 +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 10 +%15 = OpTypeArray %12 %14 +%16 = OpTypePointer Function %15 +%17 = OpConstant %12 1 +%18 = OpTypePointer Function %12 +%19 = OpConstant %7 1 +%2 = OpFunction %5 None %6 +%20 = OpLabel +%4 = OpVariable %16 Function +OpBranch %21 +%21 = OpLabel +%22 = OpPhi %7 %9 %20 %23 %24 +OpLoopMerge %25 %24 Unroll +OpBranch %26 +%26 = OpLabel +%27 = OpLoad %7 %3 +%28 = OpSLessThan %11 %22 %27 +OpBranchConditional %28 %29 %25 +%29 = OpLabel +%30 = OpAccessChain %18 %4 %22 +OpStore %30 %17 +OpBranch %24 +%24 = OpLabel +%23 = OpIAdd %7 %22 %19 +OpBranch %21 +%25 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[10]; + for (uint i = 0; i < 2; i++) { + for (float x = 0; x < 5; ++x) { + out_array[x + i*5] = i; + } + } +} +*/ +TEST_F(PassClassTest, UnrollNestedLoopsInvalid) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "out_array" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 0 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpConstant %6 2 +%10 = OpTypeBool +%11 = OpTypeInt 32 1 +%12 = OpTypePointer Function %11 +%13 = OpConstant %11 0 +%14 = OpConstant %11 5 +%15 = OpTypeFloat 32 +%16 = OpConstant %6 10 +%17 = OpTypeArray %15 %16 +%18 = OpTypePointer Function %17 +%19 = OpConstant %6 5 +%20 = OpTypePointer Function %15 +%21 = OpConstant %11 1 +%22 = OpUndef %11 +%2 = OpFunction %4 None %5 +%23 = OpLabel +%3 = OpVariable %18 Function +OpBranch %24 +%24 = OpLabel +%25 = OpPhi %6 %8 %23 %26 %27 +%28 = OpPhi %11 %22 %23 %29 %27 +OpLoopMerge %30 %27 Unroll +OpBranch %31 +%31 = OpLabel +%32 = OpULessThan %10 %25 %9 +OpBranchConditional %32 %33 %30 +%33 = OpLabel +OpBranch %34 +%34 = OpLabel +%29 = OpPhi %11 %13 %33 %35 %36 +OpLoopMerge %37 %36 None +OpBranch %38 +%38 = OpLabel +%39 = OpSLessThan %10 %29 %14 +OpBranchConditional %39 %40 %37 +%40 = OpLabel +%41 = OpBitcast %6 %29 +%42 = OpIMul %6 %25 %19 +%43 = OpIAdd %6 %41 %42 +%44 = OpConvertUToF %15 %25 +%45 = OpAccessChain %20 %3 %43 +OpStore %45 %44 +OpBranch %36 +%36 = OpLabel +%35 = OpIAdd %11 %29 %21 +OpBranch %34 +%37 = OpLabel +OpBranch %27 +%27 = OpLabel +%26 = OpIAdd %6 %25 %21 +OpBranch %24 +%30 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, text, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main(){ + float x[10]; + for (int i = 0; i < 10; i++) { + if (i == 5) { + break; + } + x[i] = i; + } +} +*/ +TEST_F(PassClassTest, BreakInBody) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +OpName %3 "x" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpConstant %6 10 +%10 = OpTypeBool +%11 = OpConstant %6 5 +%12 = OpTypeFloat 32 +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 10 +%15 = OpTypeArray %12 %14 +%16 = OpTypePointer Function %15 +%17 = OpTypePointer Function %12 +%18 = OpConstant %6 1 +%2 = OpFunction %4 None %5 +%19 = OpLabel +%3 = OpVariable %16 Function +OpBranch %20 +%20 = OpLabel +%21 = OpPhi %6 %8 %19 %22 %23 +OpLoopMerge %24 %23 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThan %10 %21 %9 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%28 = OpIEqual %10 %21 %11 +OpSelectionMerge %29 None +OpBranchConditional %28 %30 %29 +%30 = OpLabel +OpBranch %24 +%29 = OpLabel +%31 = OpConvertSToF %12 %21 +%32 = OpAccessChain %17 %3 %21 +OpStore %32 %31 +OpBranch %23 +%23 = OpLabel +%22 = OpIAdd %6 %21 %18 +OpBranch %20 +%24 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, text, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main(){ + float x[10]; + for (int i = 0; i < 10; i++) { + if (i == 5) { + continue; + } + x[i] = i; + } +} +*/ +TEST_F(PassClassTest, ContinueInBody) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +OpName %3 "x" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpConstant %6 10 +%10 = OpTypeBool +%11 = OpConstant %6 5 +%12 = OpTypeFloat 32 +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 10 +%15 = OpTypeArray %12 %14 +%16 = OpTypePointer Function %15 +%17 = OpTypePointer Function %12 +%18 = OpConstant %6 1 +%2 = OpFunction %4 None %5 +%19 = OpLabel +%3 = OpVariable %16 Function +OpBranch %20 +%20 = OpLabel +%21 = OpPhi %6 %8 %19 %22 %23 +OpLoopMerge %24 %23 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThan %10 %21 %9 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%28 = OpIEqual %10 %21 %11 +OpSelectionMerge %29 None +OpBranchConditional %28 %30 %29 +%30 = OpLabel +OpBranch %23 +%29 = OpLabel +%31 = OpConvertSToF %12 %21 +%32 = OpAccessChain %17 %3 %21 +OpStore %32 %31 +OpBranch %23 +%23 = OpLabel +%22 = OpIAdd %6 %21 %18 +OpBranch %20 +%24 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, text, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main(){ + float x[10]; + for (int i = 0; i < 10; i++) { + if (i == 5) { + return; + } + x[i] = i; + } +} +*/ +TEST_F(PassClassTest, ReturnInBody) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +OpName %3 "x" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpConstant %6 10 +%10 = OpTypeBool +%11 = OpConstant %6 5 +%12 = OpTypeFloat 32 +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 10 +%15 = OpTypeArray %12 %14 +%16 = OpTypePointer Function %15 +%17 = OpTypePointer Function %12 +%18 = OpConstant %6 1 +%2 = OpFunction %4 None %5 +%19 = OpLabel +%3 = OpVariable %16 Function +OpBranch %20 +%20 = OpLabel +%21 = OpPhi %6 %8 %19 %22 %23 +OpLoopMerge %24 %23 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThan %10 %21 %9 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%28 = OpIEqual %10 %21 %11 +OpSelectionMerge %29 None +OpBranchConditional %28 %30 %29 +%30 = OpLabel +OpReturn +%29 = OpLabel +%31 = OpConvertSToF %12 %21 +%32 = OpAccessChain %17 %3 %21 +OpStore %32 %31 +OpBranch %23 +%23 = OpLabel +%22 = OpIAdd %6 %21 %18 +OpBranch %20 +%24 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(PassClassTest, KillInBody) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpTypeBool +%5 = OpTypeInt 32 0 +%6 = OpConstant %5 0 +%7 = OpConstant %5 1 +%8 = OpConstant %5 5 +%1 = OpFunction %2 None %3 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +%11 = OpPhi %5 %6 %9 %12 %13 +%14 = OpULessThan %4 %11 %8 +OpLoopMerge %15 %13 Unroll +OpBranchConditional %14 %16 %15 +%16 = OpLabel +OpKill +%13 = OpLabel +%12 = OpIAdd %5 %11 %7 +OpBranch %10 +%15 = OpLabel +OpReturn +OpFunctionEnd +)"; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(PassClassTest, TerminateInvocationInBody) { + const std::string text = R"(OpCapability Shader +OpExtension "SPV_KHR_terminate_invocation" +OpMemoryModel Logical Simple +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpTypeBool +%5 = OpTypeInt 32 0 +%6 = OpConstant %5 0 +%7 = OpConstant %5 1 +%8 = OpConstant %5 5 +%1 = OpFunction %2 None %3 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +%11 = OpPhi %5 %6 %9 %12 %13 +%14 = OpULessThan %4 %11 %8 +OpLoopMerge %15 %13 Unroll +OpBranchConditional %14 %16 %15 +%16 = OpLabel +OpTerminateInvocation +%13 = OpLabel +%12 = OpIAdd %5 %11 %7 +OpBranch %10 +%15 = OpLabel +OpReturn +OpFunctionEnd +)"; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, text, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main() { + int j = 0; + for (int i = 0; i < 10 && i > 0; i++) { + j++; + } +} +*/ +TEST_F(PassClassTest, MultipleConditionsSingleVariable) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpConstant %5 10 +%9 = OpTypeBool +%10 = OpConstant %5 1 +%2 = OpFunction %3 None %4 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +%13 = OpPhi %5 %7 %11 %14 %15 +%16 = OpPhi %5 %7 %11 %17 %15 +OpLoopMerge %18 %15 Unroll +OpBranch %19 +%19 = OpLabel +%20 = OpSLessThan %9 %16 %8 +%21 = OpSGreaterThan %9 %16 %7 +%22 = OpLogicalAnd %9 %20 %21 +OpBranchConditional %22 %23 %18 +%23 = OpLabel +%14 = OpIAdd %5 %13 %10 +OpBranch %15 +%15 = OpLabel +%17 = OpIAdd %5 %16 %10 +OpBranch %12 +%18 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main() { + int i = 0; + int j = 0; + int k = 0; + for (; i < 10 && j > 0; i++, j++) { + k++; + } +} +*/ +TEST_F(PassClassTest, MultipleConditionsMultipleVariables) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpConstant %5 10 +%9 = OpTypeBool +%10 = OpConstant %5 1 +%2 = OpFunction %3 None %4 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +%13 = OpPhi %5 %7 %11 %14 %15 +%16 = OpPhi %5 %7 %11 %17 %15 +%18 = OpPhi %5 %7 %11 %19 %15 +OpLoopMerge %20 %15 Unroll +OpBranch %21 +%21 = OpLabel +%22 = OpSLessThan %9 %13 %8 +%23 = OpSGreaterThan %9 %16 %7 +%24 = OpLogicalAnd %9 %22 %23 +OpBranchConditional %24 %25 %20 +%25 = OpLabel +%19 = OpIAdd %5 %18 %10 +OpBranch %15 +%15 = OpLabel +%14 = OpIAdd %5 %13 %10 +%17 = OpIAdd %5 %16 %10 +OpBranch %12 +%20 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main() { + float i = 0.0; + int j = 0; + for (; i < 10; i++) { + j++; + } +} +*/ +TEST_F(PassClassTest, FloatingPointLoop) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeFloat 32 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %5 10 +%12 = OpTypeBool +%13 = OpConstant %8 1 +%14 = OpConstant %5 1 +%2 = OpFunction %3 None %4 +%15 = OpLabel +OpBranch %16 +%16 = OpLabel +%17 = OpPhi %5 %7 %15 %18 %19 +%20 = OpPhi %8 %10 %15 %21 %19 +OpLoopMerge %22 %19 Unroll +OpBranch %23 +%23 = OpLabel +%24 = OpFOrdLessThan %12 %17 %11 +OpBranchConditional %24 %25 %22 +%25 = OpLabel +%21 = OpIAdd %8 %20 %13 +OpBranch %19 +%19 = OpLabel +%18 = OpFAdd %5 %17 %14 +OpBranch %16 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main() { + int i = 2; + int j = 0; + if (j == 0) { i = 5; } + for (; i < 3; ++i) { + j++; + } +} +*/ +TEST_F(PassClassTest, InductionPhiOutsideLoop) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 2 +%8 = OpConstant %5 0 +%9 = OpTypeBool +%10 = OpConstant %5 5 +%11 = OpConstant %5 3 +%12 = OpConstant %5 1 +%2 = OpFunction %3 None %4 +%13 = OpLabel +%14 = OpIEqual %9 %8 %8 +OpSelectionMerge %15 None +OpBranchConditional %14 %16 %15 +%16 = OpLabel +OpBranch %15 +%15 = OpLabel +%17 = OpPhi %5 %7 %13 %10 %16 +OpBranch %18 +%18 = OpLabel +%19 = OpPhi %5 %17 %15 %20 %21 +%22 = OpPhi %5 %8 %15 %23 %21 +OpLoopMerge %24 %21 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThan %9 %19 %11 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%23 = OpIAdd %5 %22 %12 +OpBranch %21 +%21 = OpLabel +%20 = OpIAdd %5 %19 %12 +OpBranch %18 +%24 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main() { + int j = 0; + for (int i = 0; i == 0; ++i) { + ++j; + } + for (int i = 0; i != 3; ++i) { + ++j; + } + for (int i = 0; i < 3; i *= 2) { + ++j; + } + for (int i = 10; i > 3; i /= 2) { + ++j; + } + for (int i = 10; i > 3; i |= 2) { + ++j; + } + for (int i = 10; i > 3; i &= 2) { + ++j; + } + for (int i = 10; i > 3; i ^= 2) { + ++j; + } + for (int i = 0; i < 3; i << 2) { + ++j; + } + for (int i = 10; i > 3; i >> 2) { + ++j; + } +} +*/ +TEST_F(PassClassTest, UnsupportedLoopTypes) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpTypeBool +%9 = OpConstant %5 1 +%10 = OpConstant %5 3 +%11 = OpConstant %5 2 +%12 = OpConstant %5 10 +%2 = OpFunction %3 None %4 +%13 = OpLabel +OpBranch %14 +%14 = OpLabel +%15 = OpPhi %5 %7 %13 %16 %17 +%18 = OpPhi %5 %7 %13 %19 %17 +OpLoopMerge %20 %17 Unroll +OpBranch %21 +%21 = OpLabel +%22 = OpIEqual %8 %18 %7 +OpBranchConditional %22 %23 %20 +%23 = OpLabel +%16 = OpIAdd %5 %15 %9 +OpBranch %17 +%17 = OpLabel +%19 = OpIAdd %5 %18 %9 +OpBranch %14 +%20 = OpLabel +OpBranch %24 +%24 = OpLabel +%25 = OpPhi %5 %15 %20 %26 %27 +%28 = OpPhi %5 %7 %20 %29 %27 +OpLoopMerge %30 %27 Unroll +OpBranch %31 +%31 = OpLabel +%32 = OpINotEqual %8 %28 %10 +OpBranchConditional %32 %33 %30 +%33 = OpLabel +%26 = OpIAdd %5 %25 %9 +OpBranch %27 +%27 = OpLabel +%29 = OpIAdd %5 %28 %9 +OpBranch %24 +%30 = OpLabel +OpBranch %34 +%34 = OpLabel +%35 = OpPhi %5 %25 %30 %36 %37 +%38 = OpPhi %5 %7 %30 %39 %37 +OpLoopMerge %40 %37 Unroll +OpBranch %41 +%41 = OpLabel +%42 = OpSLessThan %8 %38 %10 +OpBranchConditional %42 %43 %40 +%43 = OpLabel +%36 = OpIAdd %5 %35 %9 +OpBranch %37 +%37 = OpLabel +%39 = OpIMul %5 %38 %11 +OpBranch %34 +%40 = OpLabel +OpBranch %44 +%44 = OpLabel +%45 = OpPhi %5 %35 %40 %46 %47 +%48 = OpPhi %5 %12 %40 %49 %47 +OpLoopMerge %50 %47 Unroll +OpBranch %51 +%51 = OpLabel +%52 = OpSGreaterThan %8 %48 %10 +OpBranchConditional %52 %53 %50 +%53 = OpLabel +%46 = OpIAdd %5 %45 %9 +OpBranch %47 +%47 = OpLabel +%49 = OpSDiv %5 %48 %11 +OpBranch %44 +%50 = OpLabel +OpBranch %54 +%54 = OpLabel +%55 = OpPhi %5 %45 %50 %56 %57 +%58 = OpPhi %5 %12 %50 %59 %57 +OpLoopMerge %60 %57 Unroll +OpBranch %61 +%61 = OpLabel +%62 = OpSGreaterThan %8 %58 %10 +OpBranchConditional %62 %63 %60 +%63 = OpLabel +%56 = OpIAdd %5 %55 %9 +OpBranch %57 +%57 = OpLabel +%59 = OpBitwiseOr %5 %58 %11 +OpBranch %54 +%60 = OpLabel +OpBranch %64 +%64 = OpLabel +%65 = OpPhi %5 %55 %60 %66 %67 +%68 = OpPhi %5 %12 %60 %69 %67 +OpLoopMerge %70 %67 Unroll +OpBranch %71 +%71 = OpLabel +%72 = OpSGreaterThan %8 %68 %10 +OpBranchConditional %72 %73 %70 +%73 = OpLabel +%66 = OpIAdd %5 %65 %9 +OpBranch %67 +%67 = OpLabel +%69 = OpBitwiseAnd %5 %68 %11 +OpBranch %64 +%70 = OpLabel +OpBranch %74 +%74 = OpLabel +%75 = OpPhi %5 %65 %70 %76 %77 +%78 = OpPhi %5 %12 %70 %79 %77 +OpLoopMerge %80 %77 Unroll +OpBranch %81 +%81 = OpLabel +%82 = OpSGreaterThan %8 %78 %10 +OpBranchConditional %82 %83 %80 +%83 = OpLabel +%76 = OpIAdd %5 %75 %9 +OpBranch %77 +%77 = OpLabel +%79 = OpBitwiseXor %5 %78 %11 +OpBranch %74 +%80 = OpLabel +OpBranch %84 +%84 = OpLabel +%85 = OpPhi %5 %75 %80 %86 %87 +OpLoopMerge %88 %87 Unroll +OpBranch %89 +%89 = OpLabel +%90 = OpSLessThan %8 %7 %10 +OpBranchConditional %90 %91 %88 +%91 = OpLabel +%86 = OpIAdd %5 %85 %9 +OpBranch %87 +%87 = OpLabel +%92 = OpShiftLeftLogical %5 %7 %11 +OpBranch %84 +%88 = OpLabel +OpBranch %93 +%93 = OpLabel +%94 = OpPhi %5 %85 %88 %95 %96 +OpLoopMerge %97 %96 Unroll +OpBranch %98 +%98 = OpLabel +%99 = OpSGreaterThan %8 %12 %10 +OpBranchConditional %99 %100 %97 +%100 = OpLabel +%95 = OpIAdd %5 %94 %9 +OpBranch %96 +%96 = OpLabel +%101 = OpShiftRightArithmetic %5 %12 %11 +OpBranch %93 +%97 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +#version 430 + +layout(location = 0) out float o; + +void main(void) { + for (int j = 2; j < 0; j += 1) { + o += 1.0; + } +} +*/ +TEST_F(PassClassTest, NegativeNumberOfIterations) { + // clang-format off + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "o" +OpDecorate %3 Location 0 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 2 +%9 = OpConstant %6 0 +%10 = OpTypeBool +%11 = OpTypeFloat 32 +%12 = OpTypePointer Output %11 +%3 = OpVariable %12 Output +%13 = OpConstant %11 1 +%14 = OpConstant %6 1 +%2 = OpFunction %4 None %5 +%15 = OpLabel +OpBranch %16 +%16 = OpLabel +%17 = OpPhi %6 %8 %15 %18 %19 +OpLoopMerge %20 %19 None +OpBranch %21 +%21 = OpLabel +%22 = OpSLessThan %10 %17 %9 +OpBranchConditional %22 %23 %20 +%23 = OpLabel +%24 = OpLoad %11 %3 +%25 = OpFAdd %11 %24 %13 +OpStore %3 %25 +OpBranch %19 +%19 = OpLabel +%18 = OpIAdd %6 %17 %14 +OpBranch %16 +%20 = OpLabel +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +#version 430 + +layout(location = 0) out float o; + +void main(void) { + float s = 0.0; + for (int j = 0; j < 3; j += 1) { + s += 1.0; + j += 1; + } + o = s; +} +*/ +TEST_F(PassClassTest, MultipleStepOperations) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "o" +OpDecorate %3 Location 0 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeFloat 32 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 0 +%12 = OpConstant %9 3 +%13 = OpTypeBool +%14 = OpConstant %6 1 +%15 = OpConstant %9 1 +%16 = OpTypePointer Output %6 +%3 = OpVariable %16 Output +%2 = OpFunction %4 None %5 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpPhi %6 %8 %17 %20 %21 +%22 = OpPhi %9 %11 %17 %23 %21 +OpLoopMerge %24 %21 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThan %13 %22 %12 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%20 = OpFAdd %6 %19 %14 +%28 = OpIAdd %9 %22 %15 +OpBranch %21 +%21 = OpLabel +%23 = OpIAdd %9 %28 %15 +OpBranch %18 +%24 = OpLabel +OpStore %3 %19 +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +#version 430 + +layout(location = 0) out float o; + +void main(void) { + float s = 0.0; + for (int j = 10; j > 20; j -= 1) { + s += 1.0; + } + o = s; +} +*/ + +TEST_F(PassClassTest, ConditionFalseFromStartGreaterThan) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "o" +OpDecorate %3 Location 0 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeFloat 32 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 10 +%12 = OpConstant %9 20 +%13 = OpTypeBool +%14 = OpConstant %6 1 +%15 = OpConstant %9 1 +%16 = OpTypePointer Output %6 +%3 = OpVariable %16 Output +%2 = OpFunction %4 None %5 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpPhi %6 %8 %17 %20 %21 +%22 = OpPhi %9 %11 %17 %23 %21 +OpLoopMerge %24 %21 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSGreaterThan %13 %22 %12 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%20 = OpFAdd %6 %19 %14 +OpBranch %21 +%21 = OpLabel +%23 = OpISub %9 %22 %15 +OpBranch %18 +%24 = OpLabel +OpStore %3 %19 +OpReturn +OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +#version 430 + +layout(location = 0) out float o; + +void main(void) { + float s = 0.0; + for (int j = 10; j >= 20; j -= 1) { + s += 1.0; + } + o = s; +} +*/ +TEST_F(PassClassTest, ConditionFalseFromStartGreaterThanOrEqual) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "o" +OpDecorate %3 Location 0 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeFloat 32 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 10 +%12 = OpConstant %9 20 +%13 = OpTypeBool +%14 = OpConstant %6 1 +%15 = OpConstant %9 1 +%16 = OpTypePointer Output %6 +%3 = OpVariable %16 Output +%2 = OpFunction %4 None %5 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpPhi %6 %8 %17 %20 %21 +%22 = OpPhi %9 %11 %17 %23 %21 +OpLoopMerge %24 %21 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSGreaterThanEqual %13 %22 %12 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%20 = OpFAdd %6 %19 %14 +OpBranch %21 +%21 = OpLabel +%23 = OpISub %9 %22 %15 +OpBranch %18 +%24 = OpLabel +OpStore %3 %19 +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +#version 430 + +layout(location = 0) out float o; + +void main(void) { + float s = 0.0; + for (int j = 20; j < 10; j -= 1) { + s += 1.0; + } + o = s; +} +*/ +TEST_F(PassClassTest, ConditionFalseFromStartLessThan) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "o" +OpDecorate %3 Location 0 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeFloat 32 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 20 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpConstant %6 1 +%15 = OpConstant %9 1 +%16 = OpTypePointer Output %6 +%3 = OpVariable %16 Output +%2 = OpFunction %4 None %5 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpPhi %6 %8 %17 %20 %21 +%22 = OpPhi %9 %11 %17 %23 %21 +OpLoopMerge %24 %21 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThan %13 %22 %12 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%20 = OpFAdd %6 %19 %14 +OpBranch %21 +%21 = OpLabel +%23 = OpISub %9 %22 %15 +OpBranch %18 +%24 = OpLabel +OpStore %3 %19 +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +/* +#version 430 + +layout(location = 0) out float o; + +void main(void) { + float s = 0.0; + for (int j = 20; j <= 10; j -= 1) { + s += 1.0; + } + o = s; +} +*/ +TEST_F(PassClassTest, ConditionFalseFromStartLessThanEqual) { + // clang-format off + // With LocalMultiStoreElimPass +const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "o" +OpDecorate %3 Location 0 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeFloat 32 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpTypeInt 32 1 +%10 = OpTypePointer Function %9 +%11 = OpConstant %9 20 +%12 = OpConstant %9 10 +%13 = OpTypeBool +%14 = OpConstant %6 1 +%15 = OpConstant %9 1 +%16 = OpTypePointer Output %6 +%3 = OpVariable %16 Output +%2 = OpFunction %4 None %5 +%17 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpPhi %6 %8 %17 %20 %21 +%22 = OpPhi %9 %11 %17 %23 %21 +OpLoopMerge %24 %21 Unroll +OpBranch %25 +%25 = OpLabel +%26 = OpSLessThanEqual %13 %22 %12 +OpBranchConditional %26 %27 %24 +%27 = OpLabel +%20 = OpFAdd %6 %19 %14 +OpBranch %21 +%21 = OpLabel +%23 = OpISub %9 %22 %15 +OpBranch %18 +%24 = OpLabel +OpStore %3 %19 +OpReturn +OpFunctionEnd +)"; + + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + // Make sure the pass doesn't run + SinglePassRunAndCheck(text, text, false); + SinglePassRunAndCheck>(text, text, false); + SinglePassRunAndCheck>(text, text, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/unroll_simple.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/unroll_simple.cpp new file mode 100644 index 0000000..016316a --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/unroll_simple.cpp @@ -0,0 +1,3406 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/loop_unroller.h" +#include "source/opt/loop_utils.h" +#include "source/opt/pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 c; +void main() { + float x[4]; + for (int i = 0; i < 4; ++i) { + x[i] = 1.0f; + } +} +*/ +TEST_F(PassClassTest, SimpleFullyUnrollTest) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %5 "x" + OpName %3 "c" + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 4 + %12 = OpTypeBool + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 4 + %16 = OpTypeArray %13 %15 + %17 = OpTypePointer Function %16 + %18 = OpConstant %13 1 + %19 = OpTypePointer Function %13 + %20 = OpConstant %8 1 + %21 = OpTypeVector %13 4 + %22 = OpTypePointer Output %21 + %3 = OpVariable %22 Output + %2 = OpFunction %6 None %7 + %23 = OpLabel + %5 = OpVariable %17 Function + OpBranch %24 + %24 = OpLabel + %35 = OpPhi %8 %10 %23 %34 %26 + OpLoopMerge %25 %26 Unroll + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %12 %35 %11 + OpBranchConditional %29 %30 %25 + %30 = OpLabel + %32 = OpAccessChain %19 %5 %35 + OpStore %32 %18 + OpBranch %26 + %26 = OpLabel + %34 = OpIAdd %8 %35 %20 + OpBranch %24 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 330 +OpName %2 "main" +OpName %4 "x" +OpName %3 "c" +OpDecorate %3 Location 0 +%5 = OpTypeVoid +%6 = OpTypeFunction %5 +%7 = OpTypeInt 32 1 +%8 = OpTypePointer Function %7 +%9 = OpConstant %7 0 +%10 = OpConstant %7 4 +%11 = OpTypeBool +%12 = OpTypeFloat 32 +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 4 +%15 = OpTypeArray %12 %14 +%16 = OpTypePointer Function %15 +%17 = OpConstant %12 1 +%18 = OpTypePointer Function %12 +%19 = OpConstant %7 1 +%20 = OpTypeVector %12 4 +%21 = OpTypePointer Output %20 +%3 = OpVariable %21 Output +%2 = OpFunction %5 None %6 +%22 = OpLabel +%4 = OpVariable %16 Function +OpBranch %23 +%23 = OpLabel +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %11 %9 %10 +OpBranch %30 +%30 = OpLabel +%31 = OpAccessChain %18 %4 %9 +OpStore %31 %17 +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %7 %9 %19 +OpBranch %32 +%32 = OpLabel +OpBranch %34 +%34 = OpLabel +%35 = OpSLessThan %11 %25 %10 +OpBranch %36 +%36 = OpLabel +%37 = OpAccessChain %18 %4 %25 +OpStore %37 %17 +OpBranch %38 +%38 = OpLabel +%39 = OpIAdd %7 %25 %19 +OpBranch %40 +%40 = OpLabel +OpBranch %42 +%42 = OpLabel +%43 = OpSLessThan %11 %39 %10 +OpBranch %44 +%44 = OpLabel +%45 = OpAccessChain %18 %4 %39 +OpStore %45 %17 +OpBranch %46 +%46 = OpLabel +%47 = OpIAdd %7 %39 %19 +OpBranch %48 +%48 = OpLabel +OpBranch %50 +%50 = OpLabel +%51 = OpSLessThan %11 %47 %10 +OpBranch %52 +%52 = OpLabel +%53 = OpAccessChain %18 %4 %47 +OpStore %53 %17 +OpBranch %54 +%54 = OpLabel +%55 = OpIAdd %7 %47 %19 +OpBranch %27 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, output, false); +} + +/* +Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 c; +void main() { + float x[4]; + for (int i = 0; i < 4; ++i) { + x[i] = 1.0f; + } +} +*/ +TEST_F(PassClassTest, SimpleFullyUnrollWithDebugInstructions) { + // We must preserve the debug information including OpenCL.DebugInfo.100 + // instructions and OpLine instructions. Only the first block has + // DebugDeclare and DebugValue used for the declaration (i.e., DebugValue + // with Deref). Other blocks unrolled from the loop must not contain them. + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 330 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +%i_name = OpString "i" +OpName %2 "main" +OpName %5 "x" +OpName %3 "c" +OpDecorate %3 Location 0 +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 0 +%11 = OpConstant %8 4 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%uint_32 = OpConstant %14 32 +%15 = OpConstant %14 4 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpConstant %13 1 +%19 = OpTypePointer Function %13 +%20 = OpConstant %8 1 +%21 = OpTypeVector %13 4 +%22 = OpTypePointer Output %21 +%3 = OpVariable %22 Output +%null_expr = OpExtInst %6 %ext DebugExpression +%deref = OpExtInst %6 %ext DebugOperation Deref +%deref_expr = OpExtInst %6 %ext DebugExpression %deref +%src = OpExtInst %6 %ext DebugSource %file_name +%cu = OpExtInst %6 %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %6 %ext DebugTypeBasic %float_name %uint_32 Float +%dbg_v4f = OpExtInst %6 %ext DebugTypeVector %dbg_tf 4 +%main_ty = OpExtInst %6 %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f +%dbg_main = OpExtInst %6 %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %2 +%bb = OpExtInst %6 %ext DebugLexicalBlock %src 0 0 %dbg_main +%dbg_f = OpExtInst %6 %ext DebugLocalVariable %f_name %dbg_v4f %src 0 0 %dbg_main FlagIsLocal +%dbg_i = OpExtInst %6 %ext DebugLocalVariable %i_name %dbg_v4f %src 1 0 %bb FlagIsLocal + +; CHECK: [[f:%\w+]] = OpString "f" +; CHECK: [[i:%\w+]] = OpString "i" +; CHECK: [[int_0:%\w+]] = OpConstant {{%\w+}} 0 + +; CHECK: [[null_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression +; CHECK: [[deref:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugOperation Deref +; CHECK: [[deref_expr:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugExpression [[deref]] +; CHECK: [[dbg_fn:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugFunction +; CHECK: [[dbg_bb:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLexicalBlock +; CHECK: [[dbg_f:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[f]] {{%\w+}} {{%\w+}} 0 0 [[dbg_fn]] +; CHECK: [[dbg_i:%\w+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable [[i]] {{%\w+}} {{%\w+}} 1 0 [[dbg_bb]] + +%2 = OpFunction %6 None %7 +%23 = OpLabel + +; The first block has DebugDeclare and DebugValue with Deref +; +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_fn]] +; CHECK: [[x:%\w+]] = OpVariable {{%\w+}} Function +; CHECK: OpLine {{%\w+}} 0 0 +; CHECK: OpBranch +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_fn]] +; CHECK: DebugValue [[dbg_f]] [[int_0]] [[null_expr]] +; CHECK: OpBranch +; CHECK: DebugScope [[dbg_fn]] +; CHECK: OpLine {{%\w+}} 1 1 +; CHECK: OpSLessThan +; CHECK: OpLine {{%\w+}} 2 0 +; CHECK: OpBranch +; CHECK: OpLabel +; CHECK: DebugScope [[dbg_bb]] +; CHECK: DebugDeclare [[dbg_f]] [[x]] [[null_expr]] +; CHECK: DebugValue [[dbg_i]] [[x]] [[deref_expr]] +; CHECK: OpLine {{%\w+}} 3 0 +; +; CHECK: OpLine {{%\w+}} 6 0 +; CHECK: [[add:%\w+]] = OpIAdd +; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]] +; CHECK: OpLine {{%\w+}} 7 0 + +; Other blocks do not have DebugDeclare and DebugValue with Deref +; +; CHECK: DebugScope [[dbg_fn]] +; CHECK: OpLine {{%\w+}} 1 1 +; CHECK: OpSLessThan +; CHECK: OpLine {{%\w+}} 2 0 +; CHECK: OpBranch +; CHECK: OpLabel +; +; CHECK: DebugScope [[dbg_bb]] +; CHECK-NOT: DebugDeclare [[dbg_f]] [[x]] [[null_expr]] +; CHECK-NOT: DebugValue [[dbg_i]] [[x]] [[deref_expr]] +; CHECK: OpLine {{%\w+}} 3 0 +; +; CHECK: OpLine {{%\w+}} 6 0 +; CHECK: [[add:%\w+]] = OpIAdd +; CHECK: DebugValue [[dbg_f]] [[add]] [[null_expr]] +; CHECK: OpLine {{%\w+}} 7 0 +; +; CHECK-NOT: DebugDeclare [[dbg_f]] [[x]] [[null_expr]] +; CHECK-NOT: DebugValue [[dbg_i]] [[x]] [[deref_expr]] +; CHECK: DebugScope [[dbg_fn]] +; CHECK: OpLine {{%\w+}} 8 0 +; CHECK: OpReturn + +%s0 = OpExtInst %6 %ext DebugScope %dbg_main +%5 = OpVariable %17 Function +OpLine %file_name 0 0 +OpBranch %24 +%24 = OpLabel +%s1 = OpExtInst %6 %ext DebugScope %dbg_main +%35 = OpPhi %8 %10 %23 %34 %26 +%value0 = OpExtInst %6 %ext DebugValue %dbg_f %35 %null_expr +OpLine %file_name 1 0 +OpLoopMerge %25 %26 Unroll +OpBranch %27 +%27 = OpLabel +%s2 = OpExtInst %6 %ext DebugScope %dbg_main +OpLine %file_name 1 1 +%29 = OpSLessThan %12 %35 %11 +OpLine %file_name 2 0 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +%s3 = OpExtInst %6 %ext DebugScope %bb +%decl0 = OpExtInst %6 %ext DebugDeclare %dbg_f %5 %null_expr +%decl1 = OpExtInst %6 %ext DebugValue %dbg_i %5 %deref_expr +OpLine %file_name 3 0 +%32 = OpAccessChain %19 %5 %35 +OpLine %file_name 4 0 +OpStore %32 %18 +OpLine %file_name 5 0 +OpBranch %26 +%26 = OpLabel +%s4 = OpExtInst %6 %ext DebugScope %dbg_main +OpLine %file_name 6 0 +%34 = OpIAdd %8 %35 %20 +%value1 = OpExtInst %6 %ext DebugValue %dbg_f %34 %null_expr +OpLine %file_name 7 0 +OpBranch %24 +%25 = OpLabel +%s5 = OpExtInst %6 %ext DebugScope %dbg_main +OpLine %file_name 8 0 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndMatch(text, true); +} + +template +class PartialUnrollerTestPass : public Pass { + public: + PartialUnrollerTestPass() : Pass() {} + + const char* name() const override { return "Loop unroller"; } + + Status Process() override { + for (Function& f : *context()->module()) { + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); + for (auto& loop : loop_descriptor) { + LoopUtils loop_utils{context(), &loop}; + loop_utils.PartiallyUnroll(factor); + } + } + + return Pass::Status::SuccessWithChange; + } +}; + +/* +Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 c; +void main() { + float x[10]; + for (int i = 0; i < 10; ++i) { + x[i] = 1.0f; + } +} +*/ +TEST_F(PassClassTest, SimplePartialUnroll) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %5 "x" + OpName %3 "c" + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 10 + %16 = OpTypeArray %13 %15 + %17 = OpTypePointer Function %16 + %18 = OpConstant %13 1 + %19 = OpTypePointer Function %13 + %20 = OpConstant %8 1 + %21 = OpTypeVector %13 4 + %22 = OpTypePointer Output %21 + %3 = OpVariable %22 Output + %2 = OpFunction %6 None %7 + %23 = OpLabel + %5 = OpVariable %17 Function + OpBranch %24 + %24 = OpLabel + %35 = OpPhi %8 %10 %23 %34 %26 + OpLoopMerge %25 %26 Unroll + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %12 %35 %11 + OpBranchConditional %29 %30 %25 + %30 = OpLabel + %32 = OpAccessChain %19 %5 %35 + OpStore %32 %18 + OpBranch %26 + %26 = OpLabel + %34 = OpIAdd %8 %35 %20 + OpBranch %24 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 330 +OpName %2 "main" +OpName %4 "x" +OpName %3 "c" +OpDecorate %3 Location 0 +%5 = OpTypeVoid +%6 = OpTypeFunction %5 +%7 = OpTypeInt 32 1 +%8 = OpTypePointer Function %7 +%9 = OpConstant %7 0 +%10 = OpConstant %7 10 +%11 = OpTypeBool +%12 = OpTypeFloat 32 +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 10 +%15 = OpTypeArray %12 %14 +%16 = OpTypePointer Function %15 +%17 = OpConstant %12 1 +%18 = OpTypePointer Function %12 +%19 = OpConstant %7 1 +%20 = OpTypeVector %12 4 +%21 = OpTypePointer Output %20 +%3 = OpVariable %21 Output +%2 = OpFunction %5 None %6 +%22 = OpLabel +%4 = OpVariable %16 Function +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %7 %9 %22 %39 %38 +OpLoopMerge %27 %38 DontUnroll +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %11 %24 %10 +OpBranchConditional %29 %30 %27 +%30 = OpLabel +%31 = OpAccessChain %18 %4 %24 +OpStore %31 %17 +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %7 %24 %19 +OpBranch %32 +%32 = OpLabel +OpBranch %34 +%34 = OpLabel +%35 = OpSLessThan %11 %25 %10 +OpBranch %36 +%36 = OpLabel +%37 = OpAccessChain %18 %4 %25 +OpStore %37 %17 +OpBranch %38 +%38 = OpLabel +%39 = OpIAdd %7 %25 %19 +OpBranch %23 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck>(text, output, false); +} + +/* +Generated from the following GLSL +#version 330 core +layout(location = 0) out vec4 c; +void main() { + float x[10]; + for (int i = 0; i < 10; ++i) { + x[i] = 1.0f; + } +} +*/ +TEST_F(PassClassTest, SimpleUnevenPartialUnroll) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 330 + OpName %2 "main" + OpName %5 "x" + OpName %3 "c" + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 10 + %16 = OpTypeArray %13 %15 + %17 = OpTypePointer Function %16 + %18 = OpConstant %13 1 + %19 = OpTypePointer Function %13 + %20 = OpConstant %8 1 + %21 = OpTypeVector %13 4 + %22 = OpTypePointer Output %21 + %3 = OpVariable %22 Output + %2 = OpFunction %6 None %7 + %23 = OpLabel + %5 = OpVariable %17 Function + OpBranch %24 + %24 = OpLabel + %35 = OpPhi %8 %10 %23 %34 %26 + OpLoopMerge %25 %26 Unroll + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %12 %35 %11 + OpBranchConditional %29 %30 %25 + %30 = OpLabel + %32 = OpAccessChain %19 %5 %35 + OpStore %32 %18 + OpBranch %26 + %26 = OpLabel + %34 = OpIAdd %8 %35 %20 + OpBranch %24 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 330 +OpName %2 "main" +OpName %4 "x" +OpName %3 "c" +OpDecorate %3 Location 0 +%5 = OpTypeVoid +%6 = OpTypeFunction %5 +%7 = OpTypeInt 32 1 +%8 = OpTypePointer Function %7 +%9 = OpConstant %7 0 +%10 = OpConstant %7 10 +%11 = OpTypeBool +%12 = OpTypeFloat 32 +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 10 +%15 = OpTypeArray %12 %14 +%16 = OpTypePointer Function %15 +%17 = OpConstant %12 1 +%18 = OpTypePointer Function %12 +%19 = OpConstant %7 1 +%20 = OpTypeVector %12 4 +%21 = OpTypePointer Output %20 +%3 = OpVariable %21 Output +%58 = OpConstant %13 1 +%2 = OpFunction %5 None %6 +%22 = OpLabel +%4 = OpVariable %16 Function +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %7 %9 %22 %25 %26 +OpLoopMerge %32 %26 Unroll +OpBranch %28 +%28 = OpLabel +%29 = OpSLessThan %11 %24 %58 +OpBranchConditional %29 %30 %32 +%30 = OpLabel +%31 = OpAccessChain %18 %4 %24 +OpStore %31 %17 +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %7 %24 %19 +OpBranch %23 +%32 = OpLabel +OpBranch %33 +%33 = OpLabel +%34 = OpPhi %7 %24 %32 %57 %56 +OpLoopMerge %41 %56 DontUnroll +OpBranch %35 +%35 = OpLabel +%36 = OpSLessThan %11 %34 %10 +OpBranchConditional %36 %37 %41 +%37 = OpLabel +%38 = OpAccessChain %18 %4 %34 +OpStore %38 %17 +OpBranch %39 +%39 = OpLabel +%40 = OpIAdd %7 %34 %19 +OpBranch %42 +%42 = OpLabel +OpBranch %44 +%44 = OpLabel +%45 = OpSLessThan %11 %40 %10 +OpBranch %46 +%46 = OpLabel +%47 = OpAccessChain %18 %4 %40 +OpStore %47 %17 +OpBranch %48 +%48 = OpLabel +%49 = OpIAdd %7 %40 %19 +OpBranch %50 +%50 = OpLabel +OpBranch %52 +%52 = OpLabel +%53 = OpSLessThan %11 %49 %10 +OpBranch %54 +%54 = OpLabel +%55 = OpAccessChain %18 %4 %49 +OpStore %55 %17 +OpBranch %56 +%56 = OpLabel +%57 = OpIAdd %7 %49 %19 +OpBranch %33 +%41 = OpLabel +OpReturn +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + // By unrolling by a factor that doesn't divide evenly into the number of loop + // iterations we perfom an additional transform when partially unrolling to + // account for the remainder. + SinglePassRunAndCheck>(text, output, false); +} + +/* Generated from +#version 410 core +layout(location=0) flat in int upper_bound; +void main() { + float x[10]; + for (int i = 2; i < 8; i+=2) { + x[i] = i; + } +} +*/ +TEST_F(PassClassTest, SimpleLoopIterationsCheck) { + // With LocalMultiStoreElimPass + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %5 "x" +OpName %3 "upper_bound" +OpDecorate %3 Flat +OpDecorate %3 Location 0 +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 2 +%11 = OpConstant %8 8 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpTypePointer Input %8 +%3 = OpVariable %19 Input +%2 = OpFunction %6 None %7 +%20 = OpLabel +%5 = OpVariable %17 Function +OpBranch %21 +%21 = OpLabel +%34 = OpPhi %8 %10 %20 %33 %23 +OpLoopMerge %22 %23 Unroll +OpBranch %24 +%24 = OpLabel +%26 = OpSLessThan %12 %34 %11 +OpBranchConditional %26 %27 %22 +%27 = OpLabel +%30 = OpConvertSToF %13 %34 +%31 = OpAccessChain %18 %5 %34 +OpStore %31 %30 +OpBranch %23 +%23 = OpLabel +%33 = OpIAdd %8 %34 %10 +OpBranch %21 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + Function* f = spvtest::GetFunction(module, 2); + + LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); + EXPECT_EQ(loop_descriptor.NumLoops(), 1u); + + Loop& loop = loop_descriptor.GetLoopByIndex(0); + + EXPECT_TRUE(loop.HasUnrollLoopControl()); + + BasicBlock* condition = loop.FindConditionBlock(); + EXPECT_EQ(condition->id(), 24u); + + Instruction* induction = loop.FindConditionVariable(condition); + EXPECT_EQ(induction->result_id(), 34u); + + LoopUtils loop_utils{context.get(), &loop}; + EXPECT_TRUE(loop_utils.CanPerformUnroll()); + + size_t iterations = 0; + EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(), + &iterations)); + EXPECT_EQ(iterations, 3u); +} + +/* Generated from +#version 410 core +void main() { + float x[10]; + for (int i = -1; i < 6; i+=3) { + x[i] = i; + } +} +*/ +TEST_F(PassClassTest, SimpleLoopIterationsCheckSignedInit) { + // With LocalMultiStoreElimPass + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" %3 +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %5 "x" +OpName %3 "upper_bound" +OpDecorate %3 Flat +OpDecorate %3 Location 0 +%6 = OpTypeVoid +%7 = OpTypeFunction %6 +%8 = OpTypeInt 32 1 +%9 = OpTypePointer Function %8 +%10 = OpConstant %8 -1 +%11 = OpConstant %8 6 +%12 = OpTypeBool +%13 = OpTypeFloat 32 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 10 +%16 = OpTypeArray %13 %15 +%17 = OpTypePointer Function %16 +%18 = OpTypePointer Function %13 +%19 = OpConstant %8 3 +%20 = OpTypePointer Input %8 +%3 = OpVariable %20 Input +%2 = OpFunction %6 None %7 +%21 = OpLabel +%5 = OpVariable %17 Function +OpBranch %22 +%22 = OpLabel +%35 = OpPhi %8 %10 %21 %34 %24 +OpLoopMerge %23 %24 None +OpBranch %25 +%25 = OpLabel +%27 = OpSLessThan %12 %35 %11 +OpBranchConditional %27 %28 %23 +%28 = OpLabel +%31 = OpConvertSToF %13 %35 +%32 = OpAccessChain %18 %5 %35 +OpStore %32 %31 +OpBranch %24 +%24 = OpLabel +%34 = OpIAdd %8 %35 %19 +OpBranch %22 +%23 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + Function* f = spvtest::GetFunction(module, 2); + + LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); + + EXPECT_EQ(loop_descriptor.NumLoops(), 1u); + + Loop& loop = loop_descriptor.GetLoopByIndex(0); + + EXPECT_FALSE(loop.HasUnrollLoopControl()); + + BasicBlock* condition = loop.FindConditionBlock(); + EXPECT_EQ(condition->id(), 25u); + + Instruction* induction = loop.FindConditionVariable(condition); + EXPECT_EQ(induction->result_id(), 35u); + + LoopUtils loop_utils{context.get(), &loop}; + EXPECT_TRUE(loop_utils.CanPerformUnroll()); + + size_t iterations = 0; + EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(), + &iterations)); + EXPECT_EQ(iterations, 3u); +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[6]; + for (uint i = 0; i < 2; i++) { + for (int x = 0; x < 3; ++x) { + out_array[x + i*3] = i; + } + } +} +*/ +TEST_F(PassClassTest, UnrollNestedLoops) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %35 "out_array" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 2 + %17 = OpTypeBool + %19 = OpTypeInt 32 1 + %20 = OpTypePointer Function %19 + %22 = OpConstant %19 0 + %29 = OpConstant %19 3 + %31 = OpTypeFloat 32 + %32 = OpConstant %6 6 + %33 = OpTypeArray %31 %32 + %34 = OpTypePointer Function %33 + %39 = OpConstant %6 3 + %44 = OpTypePointer Function %31 + %47 = OpConstant %19 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %35 = OpVariable %34 Function + OpBranch %10 + %10 = OpLabel + %51 = OpPhi %6 %9 %5 %50 %13 + OpLoopMerge %12 %13 Unroll + OpBranch %14 + %14 = OpLabel + %18 = OpULessThan %17 %51 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %23 + %23 = OpLabel + %54 = OpPhi %19 %22 %11 %48 %26 + OpLoopMerge %25 %26 Unroll + OpBranch %27 + %27 = OpLabel + %30 = OpSLessThan %17 %54 %29 + OpBranchConditional %30 %24 %25 + %24 = OpLabel + %37 = OpBitcast %6 %54 + %40 = OpIMul %6 %51 %39 + %41 = OpIAdd %6 %37 %40 + %43 = OpConvertUToF %31 %51 + %45 = OpAccessChain %44 %35 %41 + OpStore %45 %43 + OpBranch %26 + %26 = OpLabel + %48 = OpIAdd %19 %54 %47 + OpBranch %23 + %25 = OpLabel + OpBranch %13 + %13 = OpLabel + %50 = OpIAdd %6 %51 %47 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "out_array" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 0 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpConstant %6 2 +%10 = OpTypeBool +%11 = OpTypeInt 32 1 +%12 = OpTypePointer Function %11 +%13 = OpConstant %11 0 +%14 = OpConstant %11 3 +%15 = OpTypeFloat 32 +%16 = OpConstant %6 6 +%17 = OpTypeArray %15 %16 +%18 = OpTypePointer Function %17 +%19 = OpConstant %6 3 +%20 = OpTypePointer Function %15 +%21 = OpConstant %11 1 +%2 = OpFunction %4 None %5 +%22 = OpLabel +%3 = OpVariable %18 Function +OpBranch %23 +%23 = OpLabel +OpBranch %28 +%28 = OpLabel +%29 = OpULessThan %10 %8 %9 +OpBranch %30 +%30 = OpLabel +OpBranch %31 +%31 = OpLabel +OpBranch %36 +%36 = OpLabel +%37 = OpSLessThan %10 %13 %14 +OpBranch %38 +%38 = OpLabel +%39 = OpBitcast %6 %13 +%40 = OpIMul %6 %8 %19 +%41 = OpIAdd %6 %39 %40 +%42 = OpConvertUToF %15 %8 +%43 = OpAccessChain %20 %3 %41 +OpStore %43 %42 +OpBranch %34 +%34 = OpLabel +%33 = OpIAdd %11 %13 %21 +OpBranch %44 +%44 = OpLabel +OpBranch %46 +%46 = OpLabel +%47 = OpSLessThan %10 %33 %14 +OpBranch %48 +%48 = OpLabel +%49 = OpBitcast %6 %33 +%50 = OpIMul %6 %8 %19 +%51 = OpIAdd %6 %49 %50 +%52 = OpConvertUToF %15 %8 +%53 = OpAccessChain %20 %3 %51 +OpStore %53 %52 +OpBranch %54 +%54 = OpLabel +%55 = OpIAdd %11 %33 %21 +OpBranch %56 +%56 = OpLabel +OpBranch %58 +%58 = OpLabel +%59 = OpSLessThan %10 %55 %14 +OpBranch %60 +%60 = OpLabel +%61 = OpBitcast %6 %55 +%62 = OpIMul %6 %8 %19 +%63 = OpIAdd %6 %61 %62 +%64 = OpConvertUToF %15 %8 +%65 = OpAccessChain %20 %3 %63 +OpStore %65 %64 +OpBranch %66 +%66 = OpLabel +%67 = OpIAdd %11 %55 %21 +OpBranch %35 +%35 = OpLabel +OpBranch %26 +%26 = OpLabel +%25 = OpIAdd %6 %8 %21 +OpBranch %68 +%68 = OpLabel +OpBranch %70 +%70 = OpLabel +%71 = OpULessThan %10 %25 %9 +OpBranch %72 +%72 = OpLabel +OpBranch %73 +%73 = OpLabel +OpBranch %74 +%74 = OpLabel +%75 = OpSLessThan %10 %13 %14 +OpBranch %76 +%76 = OpLabel +%77 = OpBitcast %6 %13 +%78 = OpIMul %6 %25 %19 +%79 = OpIAdd %6 %77 %78 +%80 = OpConvertUToF %15 %25 +%81 = OpAccessChain %20 %3 %79 +OpStore %81 %80 +OpBranch %82 +%82 = OpLabel +%83 = OpIAdd %11 %13 %21 +OpBranch %84 +%84 = OpLabel +OpBranch %85 +%85 = OpLabel +%86 = OpSLessThan %10 %83 %14 +OpBranch %87 +%87 = OpLabel +%88 = OpBitcast %6 %83 +%89 = OpIMul %6 %25 %19 +%90 = OpIAdd %6 %88 %89 +%91 = OpConvertUToF %15 %25 +%92 = OpAccessChain %20 %3 %90 +OpStore %92 %91 +OpBranch %93 +%93 = OpLabel +%94 = OpIAdd %11 %83 %21 +OpBranch %95 +%95 = OpLabel +OpBranch %96 +%96 = OpLabel +%97 = OpSLessThan %10 %94 %14 +OpBranch %98 +%98 = OpLabel +%99 = OpBitcast %6 %94 +%100 = OpIMul %6 %25 %19 +%101 = OpIAdd %6 %99 %100 +%102 = OpConvertUToF %15 %25 +%103 = OpAccessChain %20 %3 %101 +OpStore %103 %102 +OpBranch %104 +%104 = OpLabel +%105 = OpIAdd %11 %94 %21 +OpBranch %106 +%106 = OpLabel +OpBranch %107 +%107 = OpLabel +%108 = OpIAdd %6 %25 %21 +OpBranch %27 +%27 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, output, false); +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[2]; + for (int i = -3; i < -1; i++) { + out_array[3 + i] = i; + } +} +*/ +TEST_F(PassClassTest, NegativeConditionAndInit) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %23 "out_array" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 -3 + %16 = OpConstant %6 -1 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 2 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %25 = OpConstant %6 3 + %30 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %22 Function + OpBranch %10 + %10 = OpLabel + %32 = OpPhi %6 %9 %5 %31 %13 + OpLoopMerge %12 %13 Unroll + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %32 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpIAdd %6 %32 %25 + %28 = OpAccessChain %7 %23 %26 + OpStore %28 %32 + OpBranch %13 + %13 = OpLabel + %31 = OpIAdd %6 %32 %30 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "out_array" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 -3 +%9 = OpConstant %6 -1 +%10 = OpTypeBool +%11 = OpTypeInt 32 0 +%12 = OpConstant %11 2 +%13 = OpTypeArray %6 %12 +%14 = OpTypePointer Function %13 +%15 = OpConstant %6 3 +%16 = OpConstant %6 1 +%2 = OpFunction %4 None %5 +%17 = OpLabel +%3 = OpVariable %14 Function +OpBranch %18 +%18 = OpLabel +OpBranch %23 +%23 = OpLabel +%24 = OpSLessThan %10 %8 %9 +OpBranch %25 +%25 = OpLabel +%26 = OpIAdd %6 %8 %15 +%27 = OpAccessChain %7 %3 %26 +OpStore %27 %8 +OpBranch %21 +%21 = OpLabel +%20 = OpIAdd %6 %8 %16 +OpBranch %28 +%28 = OpLabel +OpBranch %30 +%30 = OpLabel +%31 = OpSLessThan %10 %20 %9 +OpBranch %32 +%32 = OpLabel +%33 = OpIAdd %6 %20 %15 +%34 = OpAccessChain %7 %3 %33 +OpStore %34 %20 +OpBranch %35 +%35 = OpLabel +%36 = OpIAdd %6 %20 %16 +OpBranch %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + // SinglePassRunAndCheck(text, expected, false); + + Function* f = spvtest::GetFunction(module, 4); + + LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); + EXPECT_EQ(loop_descriptor.NumLoops(), 1u); + + Loop& loop = loop_descriptor.GetLoopByIndex(0); + + EXPECT_TRUE(loop.HasUnrollLoopControl()); + + BasicBlock* condition = loop.FindConditionBlock(); + EXPECT_EQ(condition->id(), 14u); + + Instruction* induction = loop.FindConditionVariable(condition); + EXPECT_EQ(induction->result_id(), 32u); + + LoopUtils loop_utils{context.get(), &loop}; + EXPECT_TRUE(loop_utils.CanPerformUnroll()); + + size_t iterations = 0; + EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(), + &iterations)); + EXPECT_EQ(iterations, 2u); + SinglePassRunAndCheck(text, expected, false); +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[9]; + for (int i = -10; i < -1; i++) { + out_array[i] = i; + } +} +*/ +TEST_F(PassClassTest, NegativeConditionAndInitResidualUnroll) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %23 "out_array" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 -10 + %16 = OpConstant %6 -1 + %17 = OpTypeBool + %19 = OpTypeInt 32 0 + %20 = OpConstant %19 9 + %21 = OpTypeArray %6 %20 + %22 = OpTypePointer Function %21 + %25 = OpConstant %6 10 + %30 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %22 Function + OpBranch %10 + %10 = OpLabel + %32 = OpPhi %6 %9 %5 %31 %13 + OpLoopMerge %12 %13 Unroll + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %32 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %26 = OpIAdd %6 %32 %25 + %28 = OpAccessChain %7 %23 %26 + OpStore %28 %32 + OpBranch %13 + %13 = OpLabel + %31 = OpIAdd %6 %32 %30 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "out_array" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 -10 +%9 = OpConstant %6 -1 +%10 = OpTypeBool +%11 = OpTypeInt 32 0 +%12 = OpConstant %11 9 +%13 = OpTypeArray %6 %12 +%14 = OpTypePointer Function %13 +%15 = OpConstant %6 10 +%16 = OpConstant %6 1 +%48 = OpConstant %6 -9 +%2 = OpFunction %4 None %5 +%17 = OpLabel +%3 = OpVariable %14 Function +OpBranch %18 +%18 = OpLabel +%19 = OpPhi %6 %8 %17 %20 %21 +OpLoopMerge %28 %21 Unroll +OpBranch %23 +%23 = OpLabel +%24 = OpSLessThan %10 %19 %48 +OpBranchConditional %24 %25 %28 +%25 = OpLabel +%26 = OpIAdd %6 %19 %15 +%27 = OpAccessChain %7 %3 %26 +OpStore %27 %19 +OpBranch %21 +%21 = OpLabel +%20 = OpIAdd %6 %19 %16 +OpBranch %18 +%28 = OpLabel +OpBranch %29 +%29 = OpLabel +%30 = OpPhi %6 %19 %28 %47 %46 +OpLoopMerge %38 %46 DontUnroll +OpBranch %31 +%31 = OpLabel +%32 = OpSLessThan %10 %30 %9 +OpBranchConditional %32 %33 %38 +%33 = OpLabel +%34 = OpIAdd %6 %30 %15 +%35 = OpAccessChain %7 %3 %34 +OpStore %35 %30 +OpBranch %36 +%36 = OpLabel +%37 = OpIAdd %6 %30 %16 +OpBranch %39 +%39 = OpLabel +OpBranch %41 +%41 = OpLabel +%42 = OpSLessThan %10 %37 %9 +OpBranch %43 +%43 = OpLabel +%44 = OpIAdd %6 %37 %15 +%45 = OpAccessChain %7 %3 %44 +OpStore %45 %37 +OpBranch %46 +%46 = OpLabel +%47 = OpIAdd %6 %37 %16 +OpBranch %29 +%38 = OpLabel +OpReturn +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + Function* f = spvtest::GetFunction(module, 4); + + LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); + EXPECT_EQ(loop_descriptor.NumLoops(), 1u); + + Loop& loop = loop_descriptor.GetLoopByIndex(0); + + EXPECT_TRUE(loop.HasUnrollLoopControl()); + + BasicBlock* condition = loop.FindConditionBlock(); + EXPECT_EQ(condition->id(), 14u); + + Instruction* induction = loop.FindConditionVariable(condition); + EXPECT_EQ(induction->result_id(), 32u); + + LoopUtils loop_utils{context.get(), &loop}; + EXPECT_TRUE(loop_utils.CanPerformUnroll()); + + size_t iterations = 0; + EXPECT_TRUE(loop.FindNumberOfIterations(induction, &*condition->ctail(), + &iterations)); + EXPECT_EQ(iterations, 9u); + SinglePassRunAndCheck>(text, expected, false); +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[10]; + for (uint i = 0; i < 2; i++) { + for (int x = 0; x < 5; ++x) { + out_array[x + i*5] = i; + } + } +} +*/ +TEST_F(PassClassTest, UnrollNestedLoopsValidateDescriptor) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %35 "out_array" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 2 + %17 = OpTypeBool + %19 = OpTypeInt 32 1 + %20 = OpTypePointer Function %19 + %22 = OpConstant %19 0 + %29 = OpConstant %19 5 + %31 = OpTypeFloat 32 + %32 = OpConstant %6 10 + %33 = OpTypeArray %31 %32 + %34 = OpTypePointer Function %33 + %39 = OpConstant %6 5 + %44 = OpTypePointer Function %31 + %47 = OpConstant %19 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %35 = OpVariable %34 Function + OpBranch %10 + %10 = OpLabel + %51 = OpPhi %6 %9 %5 %50 %13 + OpLoopMerge %12 %13 Unroll + OpBranch %14 + %14 = OpLabel + %18 = OpULessThan %17 %51 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %23 + %23 = OpLabel + %54 = OpPhi %19 %22 %11 %48 %26 + OpLoopMerge %25 %26 Unroll + OpBranch %27 + %27 = OpLabel + %30 = OpSLessThan %17 %54 %29 + OpBranchConditional %30 %24 %25 + %24 = OpLabel + %37 = OpBitcast %6 %54 + %40 = OpIMul %6 %51 %39 + %41 = OpIAdd %6 %37 %40 + %43 = OpConvertUToF %31 %51 + %45 = OpAccessChain %44 %35 %41 + OpStore %45 %43 + OpBranch %26 + %26 = OpLabel + %48 = OpIAdd %19 %54 %47 + OpBranch %23 + %25 = OpLabel + OpBranch %13 + %13 = OpLabel + %50 = OpIAdd %6 %51 %47 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + { // Test fully unroll + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); + EXPECT_EQ(loop_descriptor.NumLoops(), 2u); + + Loop& outer_loop = loop_descriptor.GetLoopByIndex(1); + + EXPECT_TRUE(outer_loop.HasUnrollLoopControl()); + + Loop& inner_loop = loop_descriptor.GetLoopByIndex(0); + + EXPECT_TRUE(inner_loop.HasUnrollLoopControl()); + + EXPECT_EQ(outer_loop.GetBlocks().size(), 9u); + + EXPECT_EQ(inner_loop.GetBlocks().size(), 4u); + EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u); + EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u); + + { + LoopUtils loop_utils{context.get(), &inner_loop}; + loop_utils.FullyUnroll(); + loop_utils.Finalize(); + } + + EXPECT_EQ(loop_descriptor.NumLoops(), 1u); + EXPECT_EQ(outer_loop.GetBlocks().size(), 25u); + EXPECT_EQ(outer_loop.NumImmediateChildren(), 0u); + { + LoopUtils loop_utils{context.get(), &outer_loop}; + loop_utils.FullyUnroll(); + loop_utils.Finalize(); + } + EXPECT_EQ(loop_descriptor.NumLoops(), 0u); + } + + { // Test partially unroll + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + + Function* f = spvtest::GetFunction(module, 4); + LoopDescriptor& loop_descriptor = *context->GetLoopDescriptor(f); + EXPECT_EQ(loop_descriptor.NumLoops(), 2u); + + Loop& outer_loop = loop_descriptor.GetLoopByIndex(1); + + EXPECT_TRUE(outer_loop.HasUnrollLoopControl()); + + Loop& inner_loop = loop_descriptor.GetLoopByIndex(0); + + EXPECT_TRUE(inner_loop.HasUnrollLoopControl()); + + EXPECT_EQ(outer_loop.GetBlocks().size(), 9u); + + EXPECT_EQ(inner_loop.GetBlocks().size(), 4u); + + EXPECT_EQ(outer_loop.NumImmediateChildren(), 1u); + EXPECT_EQ(inner_loop.NumImmediateChildren(), 0u); + + LoopUtils loop_utils{context.get(), &inner_loop}; + loop_utils.PartiallyUnroll(2); + loop_utils.Finalize(); + + // The number of loops should actually grow. + EXPECT_EQ(loop_descriptor.NumLoops(), 3u); + EXPECT_EQ(outer_loop.GetBlocks().size(), 18u); + EXPECT_EQ(outer_loop.NumImmediateChildren(), 2u); + } +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[3]; + for (int i = 3; i > 0; --i) { + out_array[i] = i; + } +} +*/ +TEST_F(PassClassTest, FullyUnrollNegativeStepLoopTest) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %24 "out_array" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3 + %16 = OpConstant %6 0 + %17 = OpTypeBool + %19 = OpTypeFloat 32 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 3 + %22 = OpTypeArray %19 %21 + %23 = OpTypePointer Function %22 + %28 = OpTypePointer Function %19 + %31 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %24 = OpVariable %23 Function + OpBranch %10 + %10 = OpLabel + %33 = OpPhi %6 %9 %5 %32 %13 + OpLoopMerge %12 %13 Unroll + OpBranch %14 + %14 = OpLabel + %18 = OpSGreaterThan %17 %33 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpConvertSToF %19 %33 + %29 = OpAccessChain %28 %24 %33 + OpStore %29 %27 + OpBranch %13 + %13 = OpLabel + %32 = OpISub %6 %33 %31 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "out_array" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 3 +%9 = OpConstant %6 0 +%10 = OpTypeBool +%11 = OpTypeFloat 32 +%12 = OpTypeInt 32 0 +%13 = OpConstant %12 3 +%14 = OpTypeArray %11 %13 +%15 = OpTypePointer Function %14 +%16 = OpTypePointer Function %11 +%17 = OpConstant %6 1 +%2 = OpFunction %4 None %5 +%18 = OpLabel +%3 = OpVariable %15 Function +OpBranch %19 +%19 = OpLabel +OpBranch %24 +%24 = OpLabel +%25 = OpSGreaterThan %10 %8 %9 +OpBranch %26 +%26 = OpLabel +%27 = OpConvertSToF %11 %8 +%28 = OpAccessChain %16 %3 %8 +OpStore %28 %27 +OpBranch %22 +%22 = OpLabel +%21 = OpISub %6 %8 %17 +OpBranch %29 +%29 = OpLabel +OpBranch %31 +%31 = OpLabel +%32 = OpSGreaterThan %10 %21 %9 +OpBranch %33 +%33 = OpLabel +%34 = OpConvertSToF %11 %21 +%35 = OpAccessChain %16 %3 %21 +OpStore %35 %34 +OpBranch %36 +%36 = OpLabel +%37 = OpISub %6 %21 %17 +OpBranch %38 +%38 = OpLabel +OpBranch %40 +%40 = OpLabel +%41 = OpSGreaterThan %10 %37 %9 +OpBranch %42 +%42 = OpLabel +%43 = OpConvertSToF %11 %37 +%44 = OpAccessChain %16 %3 %37 +OpStore %44 %43 +OpBranch %45 +%45 = OpLabel +%46 = OpISub %6 %37 %17 +OpBranch %23 +%23 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, output, false); +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[3]; + for (int i = 9; i > 0; i-=3) { + out_array[i] = i; + } +} +*/ +TEST_F(PassClassTest, FullyUnrollNegativeNonOneStepLoop) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %24 "out_array" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 9 + %16 = OpConstant %6 0 + %17 = OpTypeBool + %19 = OpTypeFloat 32 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 3 + %22 = OpTypeArray %19 %21 + %23 = OpTypePointer Function %22 + %28 = OpTypePointer Function %19 + %30 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %24 = OpVariable %23 Function + OpBranch %10 + %10 = OpLabel + %33 = OpPhi %6 %9 %5 %32 %13 + OpLoopMerge %12 %13 Unroll + OpBranch %14 + %14 = OpLabel + %18 = OpSGreaterThan %17 %33 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %27 = OpConvertSToF %19 %33 + %29 = OpAccessChain %28 %24 %33 + OpStore %29 %27 + OpBranch %13 + %13 = OpLabel + %32 = OpISub %6 %33 %30 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "out_array" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 9 +%9 = OpConstant %6 0 +%10 = OpTypeBool +%11 = OpTypeFloat 32 +%12 = OpTypeInt 32 0 +%13 = OpConstant %12 3 +%14 = OpTypeArray %11 %13 +%15 = OpTypePointer Function %14 +%16 = OpTypePointer Function %11 +%17 = OpConstant %6 3 +%2 = OpFunction %4 None %5 +%18 = OpLabel +%3 = OpVariable %15 Function +OpBranch %19 +%19 = OpLabel +OpBranch %24 +%24 = OpLabel +%25 = OpSGreaterThan %10 %8 %9 +OpBranch %26 +%26 = OpLabel +%27 = OpConvertSToF %11 %8 +%28 = OpAccessChain %16 %3 %8 +OpStore %28 %27 +OpBranch %22 +%22 = OpLabel +%21 = OpISub %6 %8 %17 +OpBranch %29 +%29 = OpLabel +OpBranch %31 +%31 = OpLabel +%32 = OpSGreaterThan %10 %21 %9 +OpBranch %33 +%33 = OpLabel +%34 = OpConvertSToF %11 %21 +%35 = OpAccessChain %16 %3 %21 +OpStore %35 %34 +OpBranch %36 +%36 = OpLabel +%37 = OpISub %6 %21 %17 +OpBranch %38 +%38 = OpLabel +OpBranch %40 +%40 = OpLabel +%41 = OpSGreaterThan %10 %37 %9 +OpBranch %42 +%42 = OpLabel +%43 = OpConvertSToF %11 %37 +%44 = OpAccessChain %16 %3 %37 +OpStore %44 %43 +OpBranch %45 +%45 = OpLabel +%46 = OpISub %6 %37 %17 +OpBranch %23 +%23 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, output, false); +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[3]; + for (int i = 0; i < 7; i+=3) { + out_array[i] = i; + } +} +*/ +TEST_F(PassClassTest, FullyUnrollNonDivisibleStepLoop) { + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +OpSource GLSL 410 +OpName %4 "main" +OpName %24 "out_array" +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%9 = OpConstant %6 0 +%16 = OpConstant %6 7 +%17 = OpTypeBool +%19 = OpTypeFloat 32 +%20 = OpTypeInt 32 0 +%21 = OpConstant %20 3 +%22 = OpTypeArray %19 %21 +%23 = OpTypePointer Function %22 +%28 = OpTypePointer Function %19 +%30 = OpConstant %6 3 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%24 = OpVariable %23 Function +OpBranch %10 +%10 = OpLabel +%33 = OpPhi %6 %9 %5 %32 %13 +OpLoopMerge %12 %13 Unroll +OpBranch %14 +%14 = OpLabel +%18 = OpSLessThan %17 %33 %16 +OpBranchConditional %18 %11 %12 +%11 = OpLabel +%27 = OpConvertSToF %19 %33 +%29 = OpAccessChain %28 %24 %33 +OpStore %29 %27 +OpBranch %13 +%13 = OpLabel +%32 = OpIAdd %6 %33 %30 +OpBranch %10 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "out_array" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 0 +%9 = OpConstant %6 7 +%10 = OpTypeBool +%11 = OpTypeFloat 32 +%12 = OpTypeInt 32 0 +%13 = OpConstant %12 3 +%14 = OpTypeArray %11 %13 +%15 = OpTypePointer Function %14 +%16 = OpTypePointer Function %11 +%17 = OpConstant %6 3 +%2 = OpFunction %4 None %5 +%18 = OpLabel +%3 = OpVariable %15 Function +OpBranch %19 +%19 = OpLabel +OpBranch %24 +%24 = OpLabel +%25 = OpSLessThan %10 %8 %9 +OpBranch %26 +%26 = OpLabel +%27 = OpConvertSToF %11 %8 +%28 = OpAccessChain %16 %3 %8 +OpStore %28 %27 +OpBranch %22 +%22 = OpLabel +%21 = OpIAdd %6 %8 %17 +OpBranch %29 +%29 = OpLabel +OpBranch %31 +%31 = OpLabel +%32 = OpSLessThan %10 %21 %9 +OpBranch %33 +%33 = OpLabel +%34 = OpConvertSToF %11 %21 +%35 = OpAccessChain %16 %3 %21 +OpStore %35 %34 +OpBranch %36 +%36 = OpLabel +%37 = OpIAdd %6 %21 %17 +OpBranch %38 +%38 = OpLabel +OpBranch %40 +%40 = OpLabel +%41 = OpSLessThan %10 %37 %9 +OpBranch %42 +%42 = OpLabel +%43 = OpConvertSToF %11 %37 +%44 = OpAccessChain %16 %3 %37 +OpStore %44 %43 +OpBranch %45 +%45 = OpLabel +%46 = OpIAdd %6 %37 %17 +OpBranch %23 +%23 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, output, false); +} + +/* +Generated from the following GLSL +#version 410 core +void main() { + float out_array[4]; + for (int i = 11; i > 0; i-=3) { + out_array[i] = i; + } +} +*/ +TEST_F(PassClassTest, FullyUnrollNegativeNonDivisibleStepLoop) { + // With LocalMultiStoreElimPass + const std::string text = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +OpSource GLSL 410 +OpName %4 "main" +OpName %24 "out_array" +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%9 = OpConstant %6 11 +%16 = OpConstant %6 0 +%17 = OpTypeBool +%19 = OpTypeFloat 32 +%20 = OpTypeInt 32 0 +%21 = OpConstant %20 4 +%22 = OpTypeArray %19 %21 +%23 = OpTypePointer Function %22 +%28 = OpTypePointer Function %19 +%30 = OpConstant %6 3 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%24 = OpVariable %23 Function +OpBranch %10 +%10 = OpLabel +%33 = OpPhi %6 %9 %5 %32 %13 +OpLoopMerge %12 %13 Unroll +OpBranch %14 +%14 = OpLabel +%18 = OpSGreaterThan %17 %33 %16 +OpBranchConditional %18 %11 %12 +%11 = OpLabel +%27 = OpConvertSToF %19 %33 +%29 = OpAccessChain %28 %24 %33 +OpStore %29 %27 +OpBranch %13 +%13 = OpLabel +%32 = OpISub %6 %33 %30 +OpBranch %10 +%12 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "out_array" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypePointer Function %6 +%8 = OpConstant %6 11 +%9 = OpConstant %6 0 +%10 = OpTypeBool +%11 = OpTypeFloat 32 +%12 = OpTypeInt 32 0 +%13 = OpConstant %12 4 +%14 = OpTypeArray %11 %13 +%15 = OpTypePointer Function %14 +%16 = OpTypePointer Function %11 +%17 = OpConstant %6 3 +%2 = OpFunction %4 None %5 +%18 = OpLabel +%3 = OpVariable %15 Function +OpBranch %19 +%19 = OpLabel +OpBranch %24 +%24 = OpLabel +%25 = OpSGreaterThan %10 %8 %9 +OpBranch %26 +%26 = OpLabel +%27 = OpConvertSToF %11 %8 +%28 = OpAccessChain %16 %3 %8 +OpStore %28 %27 +OpBranch %22 +%22 = OpLabel +%21 = OpISub %6 %8 %17 +OpBranch %29 +%29 = OpLabel +OpBranch %31 +%31 = OpLabel +%32 = OpSGreaterThan %10 %21 %9 +OpBranch %33 +%33 = OpLabel +%34 = OpConvertSToF %11 %21 +%35 = OpAccessChain %16 %3 %21 +OpStore %35 %34 +OpBranch %36 +%36 = OpLabel +%37 = OpISub %6 %21 %17 +OpBranch %38 +%38 = OpLabel +OpBranch %40 +%40 = OpLabel +%41 = OpSGreaterThan %10 %37 %9 +OpBranch %42 +%42 = OpLabel +%43 = OpConvertSToF %11 %37 +%44 = OpAccessChain %16 %3 %37 +OpStore %44 %43 +OpBranch %45 +%45 = OpLabel +%46 = OpISub %6 %37 %17 +OpBranch %47 +%47 = OpLabel +OpBranch %49 +%49 = OpLabel +%50 = OpSGreaterThan %10 %46 %9 +OpBranch %51 +%51 = OpLabel +%52 = OpConvertSToF %11 %46 +%53 = OpAccessChain %16 %3 %46 +OpStore %53 %52 +OpBranch %54 +%54 = OpLabel +%55 = OpISub %6 %46 %17 +OpBranch %23 +%23 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, output, false); +} + +// With LocalMultiStoreElimPass +static const std::string multiple_phi_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %8 "foo(" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %14 = OpConstant %6 3 + %22 = OpConstant %6 6 + %23 = OpTypeBool + %31 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %40 = OpFunctionCall %6 %8 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + OpBranch %16 + %16 = OpLabel + %41 = OpPhi %6 %12 %9 %34 %19 + %42 = OpPhi %6 %14 %9 %29 %19 + %43 = OpPhi %6 %12 %9 %32 %19 + OpLoopMerge %18 %19 Unroll + OpBranch %20 + %20 = OpLabel + %24 = OpSLessThan %23 %43 %22 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + %27 = OpIMul %6 %43 %41 + %29 = OpIAdd %6 %42 %27 + OpBranch %19 + %19 = OpLabel + %32 = OpIAdd %6 %43 %31 + %34 = OpISub %6 %41 %31 + OpBranch %16 + %18 = OpLabel + %37 = OpIAdd %6 %42 %41 + OpReturnValue %37 + OpFunctionEnd + )"; + +TEST_F(PassClassTest, PartiallyUnrollResidualMultipleInductionVariables) { + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "foo(" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypeFunction %6 +%8 = OpTypePointer Function %6 +%9 = OpConstant %6 0 +%10 = OpConstant %6 3 +%11 = OpConstant %6 6 +%12 = OpTypeBool +%13 = OpConstant %6 1 +%82 = OpTypeInt 32 0 +%83 = OpConstant %82 2 +%2 = OpFunction %4 None %5 +%14 = OpLabel +%15 = OpFunctionCall %6 %3 +OpReturn +OpFunctionEnd +%3 = OpFunction %6 None %7 +%16 = OpLabel +OpBranch %17 +%17 = OpLabel +%18 = OpPhi %6 %9 %16 %19 %20 +%21 = OpPhi %6 %10 %16 %22 %20 +%23 = OpPhi %6 %9 %16 %24 %20 +OpLoopMerge %31 %20 Unroll +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %23 %83 +OpBranchConditional %27 %28 %31 +%28 = OpLabel +%29 = OpIMul %6 %23 %18 +%22 = OpIAdd %6 %21 %29 +OpBranch %20 +%20 = OpLabel +%24 = OpIAdd %6 %23 %13 +%19 = OpISub %6 %18 %13 +OpBranch %17 +%31 = OpLabel +OpBranch %32 +%32 = OpLabel +%33 = OpPhi %6 %18 %31 %81 %79 +%34 = OpPhi %6 %21 %31 %78 %79 +%35 = OpPhi %6 %23 %31 %80 %79 +OpLoopMerge %44 %79 DontUnroll +OpBranch %36 +%36 = OpLabel +%37 = OpSLessThan %12 %35 %11 +OpBranchConditional %37 %38 %44 +%38 = OpLabel +%39 = OpIMul %6 %35 %33 +%40 = OpIAdd %6 %34 %39 +OpBranch %41 +%41 = OpLabel +%42 = OpIAdd %6 %35 %13 +%43 = OpISub %6 %33 %13 +OpBranch %46 +%46 = OpLabel +OpBranch %50 +%50 = OpLabel +%51 = OpSLessThan %12 %42 %11 +OpBranch %52 +%52 = OpLabel +%53 = OpIMul %6 %42 %43 +%54 = OpIAdd %6 %40 %53 +OpBranch %55 +%55 = OpLabel +%56 = OpIAdd %6 %42 %13 +%57 = OpISub %6 %43 %13 +OpBranch %58 +%58 = OpLabel +OpBranch %62 +%62 = OpLabel +%63 = OpSLessThan %12 %56 %11 +OpBranch %64 +%64 = OpLabel +%65 = OpIMul %6 %56 %57 +%66 = OpIAdd %6 %54 %65 +OpBranch %67 +%67 = OpLabel +%68 = OpIAdd %6 %56 %13 +%69 = OpISub %6 %57 %13 +OpBranch %70 +%70 = OpLabel +OpBranch %74 +%74 = OpLabel +%75 = OpSLessThan %12 %68 %11 +OpBranch %76 +%76 = OpLabel +%77 = OpIMul %6 %68 %69 +%78 = OpIAdd %6 %66 %77 +OpBranch %79 +%79 = OpLabel +%80 = OpIAdd %6 %68 %13 +%81 = OpISub %6 %69 %13 +OpBranch %32 +%44 = OpLabel +%45 = OpIAdd %6 %34 %33 +OpReturnValue %45 +%25 = OpLabel +%30 = OpIAdd %6 %34 %33 +OpReturnValue %30 +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << multiple_phi_shader << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck>(multiple_phi_shader, output, + false); +} + +TEST_F(PassClassTest, PartiallyUnrollMultipleInductionVariables) { + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "foo(" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypeFunction %6 +%8 = OpTypePointer Function %6 +%9 = OpConstant %6 0 +%10 = OpConstant %6 3 +%11 = OpConstant %6 6 +%12 = OpTypeBool +%13 = OpConstant %6 1 +%2 = OpFunction %4 None %5 +%14 = OpLabel +%15 = OpFunctionCall %6 %3 +OpReturn +OpFunctionEnd +%3 = OpFunction %6 None %7 +%16 = OpLabel +OpBranch %17 +%17 = OpLabel +%18 = OpPhi %6 %9 %16 %42 %40 +%21 = OpPhi %6 %10 %16 %39 %40 +%23 = OpPhi %6 %9 %16 %41 %40 +OpLoopMerge %25 %40 DontUnroll +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %23 %11 +OpBranchConditional %27 %28 %25 +%28 = OpLabel +%29 = OpIMul %6 %23 %18 +%22 = OpIAdd %6 %21 %29 +OpBranch %20 +%20 = OpLabel +%24 = OpIAdd %6 %23 %13 +%19 = OpISub %6 %18 %13 +OpBranch %31 +%31 = OpLabel +OpBranch %35 +%35 = OpLabel +%36 = OpSLessThan %12 %24 %11 +OpBranch %37 +%37 = OpLabel +%38 = OpIMul %6 %24 %19 +%39 = OpIAdd %6 %22 %38 +OpBranch %40 +%40 = OpLabel +%41 = OpIAdd %6 %24 %13 +%42 = OpISub %6 %19 %13 +OpBranch %17 +%25 = OpLabel +%30 = OpIAdd %6 %21 %18 +OpReturnValue %30 +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << multiple_phi_shader << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck>(multiple_phi_shader, output, + false); +} + +TEST_F(PassClassTest, FullyUnrollMultipleInductionVariables) { + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 410 +OpName %2 "main" +OpName %3 "foo(" +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpTypeInt 32 1 +%7 = OpTypeFunction %6 +%8 = OpTypePointer Function %6 +%9 = OpConstant %6 0 +%10 = OpConstant %6 3 +%11 = OpConstant %6 6 +%12 = OpTypeBool +%13 = OpConstant %6 1 +%2 = OpFunction %4 None %5 +%14 = OpLabel +%15 = OpFunctionCall %6 %3 +OpReturn +OpFunctionEnd +%3 = OpFunction %6 None %7 +%16 = OpLabel +OpBranch %17 +%17 = OpLabel +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %12 %9 %11 +OpBranch %28 +%28 = OpLabel +%29 = OpIMul %6 %9 %9 +%22 = OpIAdd %6 %10 %29 +OpBranch %20 +%20 = OpLabel +%24 = OpIAdd %6 %9 %13 +%19 = OpISub %6 %9 %13 +OpBranch %31 +%31 = OpLabel +OpBranch %35 +%35 = OpLabel +%36 = OpSLessThan %12 %24 %11 +OpBranch %37 +%37 = OpLabel +%38 = OpIMul %6 %24 %19 +%39 = OpIAdd %6 %22 %38 +OpBranch %40 +%40 = OpLabel +%41 = OpIAdd %6 %24 %13 +%42 = OpISub %6 %19 %13 +OpBranch %43 +%43 = OpLabel +OpBranch %47 +%47 = OpLabel +%48 = OpSLessThan %12 %41 %11 +OpBranch %49 +%49 = OpLabel +%50 = OpIMul %6 %41 %42 +%51 = OpIAdd %6 %39 %50 +OpBranch %52 +%52 = OpLabel +%53 = OpIAdd %6 %41 %13 +%54 = OpISub %6 %42 %13 +OpBranch %55 +%55 = OpLabel +OpBranch %59 +%59 = OpLabel +%60 = OpSLessThan %12 %53 %11 +OpBranch %61 +%61 = OpLabel +%62 = OpIMul %6 %53 %54 +%63 = OpIAdd %6 %51 %62 +OpBranch %64 +%64 = OpLabel +%65 = OpIAdd %6 %53 %13 +%66 = OpISub %6 %54 %13 +OpBranch %67 +%67 = OpLabel +OpBranch %71 +%71 = OpLabel +%72 = OpSLessThan %12 %65 %11 +OpBranch %73 +%73 = OpLabel +%74 = OpIMul %6 %65 %66 +%75 = OpIAdd %6 %63 %74 +OpBranch %76 +%76 = OpLabel +%77 = OpIAdd %6 %65 %13 +%78 = OpISub %6 %66 %13 +OpBranch %79 +%79 = OpLabel +OpBranch %83 +%83 = OpLabel +%84 = OpSLessThan %12 %77 %11 +OpBranch %85 +%85 = OpLabel +%86 = OpIMul %6 %77 %78 +%87 = OpIAdd %6 %75 %86 +OpBranch %88 +%88 = OpLabel +%89 = OpIAdd %6 %77 %13 +%90 = OpISub %6 %78 %13 +OpBranch %25 +%25 = OpLabel +%30 = OpIAdd %6 %87 %90 +OpReturnValue %30 +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, multiple_phi_shader, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << multiple_phi_shader << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(multiple_phi_shader, output, false); +} + +/* +Generated from the following GLSL +#version 440 core +void main() +{ + int j = 0; + for (int i = 0; i <= 2; ++i) + ++j; + + for (int i = 1; i >= 0; --i) + ++j; +} +*/ +TEST_F(PassClassTest, FullyUnrollEqualToOperations) { + // With LocalMultiStoreElimPass + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 440 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 2 + %18 = OpTypeBool + %21 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %11 + %11 = OpLabel + %37 = OpPhi %6 %9 %5 %22 %14 + %38 = OpPhi %6 %9 %5 %24 %14 + OpLoopMerge %13 %14 Unroll + OpBranch %15 + %15 = OpLabel + %19 = OpSLessThanEqual %18 %38 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %22 = OpIAdd %6 %37 %21 + OpBranch %14 + %14 = OpLabel + %24 = OpIAdd %6 %38 %21 + OpBranch %11 + %13 = OpLabel + OpBranch %26 + %26 = OpLabel + %39 = OpPhi %6 %37 %13 %34 %29 + %40 = OpPhi %6 %21 %13 %36 %29 + OpLoopMerge %28 %29 Unroll + OpBranch %30 + %30 = OpLabel + %32 = OpSGreaterThanEqual %18 %40 %9 + OpBranchConditional %32 %27 %28 + %27 = OpLabel + %34 = OpIAdd %6 %39 %21 + OpBranch %29 + %29 = OpLabel + %36 = OpISub %6 %40 %21 + OpBranch %26 + %28 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 440 +OpName %2 "main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpTypePointer Function %5 +%7 = OpConstant %5 0 +%8 = OpConstant %5 2 +%9 = OpTypeBool +%10 = OpConstant %5 1 +%2 = OpFunction %3 None %4 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +OpBranch %19 +%19 = OpLabel +%20 = OpSLessThanEqual %9 %7 %8 +OpBranch %21 +%21 = OpLabel +%14 = OpIAdd %5 %7 %10 +OpBranch %15 +%15 = OpLabel +%17 = OpIAdd %5 %7 %10 +OpBranch %41 +%41 = OpLabel +OpBranch %44 +%44 = OpLabel +%45 = OpSLessThanEqual %9 %17 %8 +OpBranch %46 +%46 = OpLabel +%47 = OpIAdd %5 %14 %10 +OpBranch %48 +%48 = OpLabel +%49 = OpIAdd %5 %17 %10 +OpBranch %50 +%50 = OpLabel +OpBranch %53 +%53 = OpLabel +%54 = OpSLessThanEqual %9 %49 %8 +OpBranch %55 +%55 = OpLabel +%56 = OpIAdd %5 %47 %10 +OpBranch %57 +%57 = OpLabel +%58 = OpIAdd %5 %49 %10 +OpBranch %18 +%18 = OpLabel +OpBranch %22 +%22 = OpLabel +OpBranch %29 +%29 = OpLabel +%30 = OpSGreaterThanEqual %9 %10 %7 +OpBranch %31 +%31 = OpLabel +%24 = OpIAdd %5 %56 %10 +OpBranch %25 +%25 = OpLabel +%27 = OpISub %5 %10 %10 +OpBranch %32 +%32 = OpLabel +OpBranch %35 +%35 = OpLabel +%36 = OpSGreaterThanEqual %9 %27 %7 +OpBranch %37 +%37 = OpLabel +%38 = OpIAdd %5 %24 %10 +OpBranch %39 +%39 = OpLabel +%40 = OpISub %5 %27 %10 +OpBranch %28 +%28 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << text << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(text, output, false); +} + +// With LocalMultiStoreElimPass +const std::string condition_in_header = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 430 + OpDecorate %o Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_n2 = OpConstant %int -2 + %int_2 = OpConstant %int 2 + %bool = OpTypeBool + %float = OpTypeFloat 32 +%_ptr_Output_float = OpTypePointer Output %float + %o = OpVariable %_ptr_Output_float Output + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %6 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %27 = OpPhi %int %int_n2 %15 %26 %18 + %21 = OpSLessThanEqual %bool %27 %int_2 + OpLoopMerge %17 %18 Unroll + OpBranchConditional %21 %22 %17 + %22 = OpLabel + %23 = OpLoad %float %o + %24 = OpFAdd %float %23 %float_1 + OpStore %o %24 + OpBranch %18 + %18 = OpLabel + %26 = OpIAdd %int %27 %int_2 + OpBranch %16 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + +TEST_F(PassClassTest, FullyUnrollConditionIsInHeaderBlock) { + const std::string output = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpSource GLSL 430 +OpDecorate %2 Location 0 +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpConstant %5 -2 +%7 = OpConstant %5 2 +%8 = OpTypeBool +%9 = OpTypeFloat 32 +%10 = OpTypePointer Output %9 +%2 = OpVariable %10 Output +%11 = OpConstant %9 1 +%1 = OpFunction %3 None %4 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +%17 = OpSLessThanEqual %8 %6 %7 +OpBranch %19 +%19 = OpLabel +%20 = OpLoad %9 %2 +%21 = OpFAdd %9 %20 %11 +OpStore %2 %21 +OpBranch %16 +%16 = OpLabel +%15 = OpIAdd %5 %6 %7 +OpBranch %22 +%22 = OpLabel +%24 = OpSLessThanEqual %8 %15 %7 +OpBranch %25 +%25 = OpLabel +%26 = OpLoad %9 %2 +%27 = OpFAdd %9 %26 %11 +OpStore %2 %27 +OpBranch %28 +%28 = OpLabel +%29 = OpIAdd %5 %15 %7 +OpBranch %30 +%30 = OpLabel +%32 = OpSLessThanEqual %8 %29 %7 +OpBranch %33 +%33 = OpLabel +%34 = OpLoad %9 %2 +%35 = OpFAdd %9 %34 %11 +OpStore %2 %35 +OpBranch %36 +%36 = OpLabel +%37 = OpIAdd %5 %29 %7 +OpBranch %18 +%18 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << condition_in_header << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(condition_in_header, output, false); +} + +TEST_F(PassClassTest, PartiallyUnrollResidualConditionIsInHeaderBlock) { + const std::string output = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpSource GLSL 430 +OpDecorate %2 Location 0 +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpConstant %5 -2 +%7 = OpConstant %5 2 +%8 = OpTypeBool +%9 = OpTypeFloat 32 +%10 = OpTypePointer Output %9 +%2 = OpVariable %10 Output +%11 = OpConstant %9 1 +%40 = OpTypeInt 32 0 +%41 = OpConstant %40 1 +%1 = OpFunction %3 None %4 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +%14 = OpPhi %5 %6 %12 %15 %16 +%17 = OpSLessThanEqual %8 %14 %41 +OpLoopMerge %22 %16 Unroll +OpBranchConditional %17 %19 %22 +%19 = OpLabel +%20 = OpLoad %9 %2 +%21 = OpFAdd %9 %20 %11 +OpStore %2 %21 +OpBranch %16 +%16 = OpLabel +%15 = OpIAdd %5 %14 %7 +OpBranch %13 +%22 = OpLabel +OpBranch %23 +%23 = OpLabel +%24 = OpPhi %5 %14 %22 %39 %38 +%25 = OpSLessThanEqual %8 %24 %7 +OpLoopMerge %31 %38 DontUnroll +OpBranchConditional %25 %26 %31 +%26 = OpLabel +%27 = OpLoad %9 %2 +%28 = OpFAdd %9 %27 %11 +OpStore %2 %28 +OpBranch %29 +%29 = OpLabel +%30 = OpIAdd %5 %24 %7 +OpBranch %32 +%32 = OpLabel +%34 = OpSLessThanEqual %8 %30 %7 +OpBranch %35 +%35 = OpLabel +%36 = OpLoad %9 %2 +%37 = OpFAdd %9 %36 %11 +OpStore %2 %37 +OpBranch %38 +%38 = OpLabel +%39 = OpIAdd %5 %30 %7 +OpBranch %23 +%31 = OpLabel +OpReturn +%18 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, condition_in_header, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for ushader:\n" + << condition_in_header << std::endl; + + LoopUnroller loop_unroller; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck>(condition_in_header, output, + false); +} + +/* +Generated from following GLSL with latch block artificially inserted to be +seperate from continue. +#version 430 +void main(void) { + float x[10]; + for (int i = 0; i < 10; ++i) { + x[i] = i; + } +} +*/ +TEST_F(PassClassTest, PartiallyUnrollLatchNotContinue) { + const std::string text = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "i" + OpName %4 "x" + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 0 + %10 = OpConstant %7 10 + %11 = OpTypeBool + %12 = OpTypeFloat 32 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 10 + %15 = OpTypeArray %12 %14 + %16 = OpTypePointer Function %15 + %17 = OpTypePointer Function %12 + %18 = OpConstant %7 1 + %2 = OpFunction %5 None %6 + %19 = OpLabel + %3 = OpVariable %8 Function + %4 = OpVariable %16 Function + OpStore %3 %9 + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %7 %9 %19 %22 %30 + OpLoopMerge %24 %23 Unroll + OpBranch %25 + %25 = OpLabel + %26 = OpSLessThan %11 %21 %10 + OpBranchConditional %26 %27 %24 + %27 = OpLabel + %28 = OpConvertSToF %12 %21 + %29 = OpAccessChain %17 %4 %21 + OpStore %29 %28 + OpBranch %23 + %23 = OpLabel + %22 = OpIAdd %7 %21 %18 + OpStore %3 %22 + OpBranch %30 + %30 = OpLabel + OpBranch %20 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string expected = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpName %2 "main" +OpName %3 "i" +OpName %4 "x" +%5 = OpTypeVoid +%6 = OpTypeFunction %5 +%7 = OpTypeInt 32 1 +%8 = OpTypePointer Function %7 +%9 = OpConstant %7 0 +%10 = OpConstant %7 10 +%11 = OpTypeBool +%12 = OpTypeFloat 32 +%13 = OpTypeInt 32 0 +%14 = OpConstant %13 10 +%15 = OpTypeArray %12 %14 +%16 = OpTypePointer Function %15 +%17 = OpTypePointer Function %12 +%18 = OpConstant %7 1 +%63 = OpConstant %13 1 +%2 = OpFunction %5 None %6 +%19 = OpLabel +%3 = OpVariable %8 Function +%4 = OpVariable %16 Function +OpStore %3 %9 +OpBranch %20 +%20 = OpLabel +%21 = OpPhi %7 %9 %19 %22 %23 +OpLoopMerge %31 %25 Unroll +OpBranch %26 +%26 = OpLabel +%27 = OpSLessThan %11 %21 %63 +OpBranchConditional %27 %28 %31 +%28 = OpLabel +%29 = OpConvertSToF %12 %21 +%30 = OpAccessChain %17 %4 %21 +OpStore %30 %29 +OpBranch %25 +%25 = OpLabel +%22 = OpIAdd %7 %21 %18 +OpStore %3 %22 +OpBranch %23 +%23 = OpLabel +OpBranch %20 +%31 = OpLabel +OpBranch %32 +%32 = OpLabel +%33 = OpPhi %7 %21 %31 %61 %62 +OpLoopMerge %42 %60 DontUnroll +OpBranch %34 +%34 = OpLabel +%35 = OpSLessThan %11 %33 %10 +OpBranchConditional %35 %36 %42 +%36 = OpLabel +%37 = OpConvertSToF %12 %33 +%38 = OpAccessChain %17 %4 %33 +OpStore %38 %37 +OpBranch %39 +%39 = OpLabel +%40 = OpIAdd %7 %33 %18 +OpStore %3 %40 +OpBranch %41 +%41 = OpLabel +OpBranch %43 +%43 = OpLabel +OpBranch %45 +%45 = OpLabel +%46 = OpSLessThan %11 %40 %10 +OpBranch %47 +%47 = OpLabel +%48 = OpConvertSToF %12 %40 +%49 = OpAccessChain %17 %4 %40 +OpStore %49 %48 +OpBranch %50 +%50 = OpLabel +%51 = OpIAdd %7 %40 %18 +OpStore %3 %51 +OpBranch %52 +%52 = OpLabel +OpBranch %53 +%53 = OpLabel +OpBranch %55 +%55 = OpLabel +%56 = OpSLessThan %11 %51 %10 +OpBranch %57 +%57 = OpLabel +%58 = OpConvertSToF %12 %51 +%59 = OpAccessChain %17 %4 %51 +OpStore %59 %58 +OpBranch %60 +%60 = OpLabel +%61 = OpIAdd %7 %51 %18 +OpStore %3 %61 +OpBranch %62 +%62 = OpLabel +OpBranch %32 +%42 = OpLabel +OpReturn +%24 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck>(text, expected, true); + + // Make sure the latch block information is preserved and propagated correctly + // by the pass. + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + PartialUnrollerTestPass<3> unroller; + unroller.SetContextForTesting(context.get()); + unroller.Process(); + + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + LoopDescriptor ld{context.get(), f}; + + EXPECT_EQ(ld.NumLoops(), 2u); + + Loop& loop_1 = ld.GetLoopByIndex(0u); + EXPECT_NE(loop_1.GetLatchBlock(), loop_1.GetContinueBlock()); + + Loop& loop_2 = ld.GetLoopByIndex(1u); + EXPECT_NE(loop_2.GetLatchBlock(), loop_2.GetContinueBlock()); +} + +// Test that a loop with a self-referencing OpPhi instruction is handled +// correctly. +TEST_F(PassClassTest, OpPhiSelfReference) { + const std::string text = R"( + ; Find the two adds from the unrolled loop + ; CHECK: OpIAdd + ; CHECK: OpIAdd + ; CHECK: OpIAdd %uint %uint_0 %uint_1 + ; CHECK-NEXT: OpReturn + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 8 8 1 + OpSource HLSL 600 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 + %bool = OpTypeBool + %true = OpConstantTrue %bool + %2 = OpFunction %void None %5 + %10 = OpLabel + OpBranch %19 + %19 = OpLabel + %20 = OpPhi %uint %uint_0 %10 %20 %21 + %22 = OpPhi %uint %uint_0 %10 %23 %21 + %24 = OpULessThanEqual %bool %22 %uint_1 + OpLoopMerge %25 %21 Unroll + OpBranchConditional %24 %21 %25 + %21 = OpLabel + %23 = OpIAdd %uint %22 %uint_1 + OpBranch %19 + %25 = OpLabel + %14 = OpIAdd %uint %20 %uint_1 + OpReturn + OpFunctionEnd + )"; + + const bool kFullyUnroll = true; + const uint32_t kUnrollFactor = 0; + SinglePassRunAndMatch(text, true, kFullyUnroll, + kUnrollFactor); +} + +// Test that a loop containing an unreachable merge block can still be unrolled +// correctly. +TEST_F(PassClassTest, UnreachableMerge) { + const std::string text = R"( +; Identify the first iteration of the unrolled loop, and make sure it contains +; the unreachable merge block. +; The first SelectionMerge corresponds to the original loop merge. +; The second is the branch in the loop. +; CHECK: OpSelectionMerge {{%\w+}} None +; CHECK: OpSelectionMerge [[unrch1:%\w+]] None +; CHECK: [[unrch1]] = OpLabel +; CHECK-NEXT: OpUnreachable +; Identify the second iteration of the unrolled loop, and make sure it contains +; the unreachable merge block. +; The first SelectionMerge corresponds to the original loop merge +; The second is the branch in the loop. +; CHECK: OpSelectionMerge {{%\w+}} None +; CHECK: OpSelectionMerge [[unrch2:%\w+]] None +; CHECK: [[unrch2]] = OpLabel +; CHECK-NEXT: OpUnreachable + + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 64 1 1 + OpSource HLSL 600 + OpName %main "main" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_2 = OpConstant %uint 2 + %uint_1 = OpConstant %uint 1 + %bool = OpTypeBool + %void = OpTypeVoid + %18 = OpTypeFunction %void + %main = OpFunction %void None %18 + %23 = OpLabel + OpBranch %24 + %24 = OpLabel + %28 = OpPhi %uint %uint_0 %23 %29 %27 + %30 = OpULessThan %bool %28 %uint_2 + OpLoopMerge %31 %27 Unroll + OpBranchConditional %30 %32 %31 + %32 = OpLabel + OpSelectionMerge %33 None + OpSwitch %uint_0 %34 + %34 = OpLabel + %35 = OpUndef %bool + OpSelectionMerge %36 None + OpBranchConditional %35 %37 %38 + %38 = OpLabel + OpBranch %33 + %37 = OpLabel + OpBranch %33 + %36 = OpLabel + OpUnreachable + %33 = OpLabel + OpBranch %27 + %27 = OpLabel + %29 = OpIAdd %uint %28 %uint_1 + OpBranch %24 + %31 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const bool kFullyUnroll = true; + const uint32_t kUnrollFactor = 0; + SinglePassRunAndMatch(text, true, kFullyUnroll, + kUnrollFactor); +} + +TEST_F(PassClassTest, InitValueIsConstantNull) { + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstantNull %6 + %13 = OpConstant %6 1 + %21 = OpConstant %6 1 + %10 = OpTypeBool + %17 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %11 = OpLabel + OpBranch %5 + %5 = OpLabel + %23 = OpPhi %6 %7 %11 %20 %15 + OpLoopMerge %8 %15 Unroll + OpBranch %14 + %14 = OpLabel + %9 = OpSLessThan %10 %23 %13 + OpBranchConditional %9 %15 %8 + %15 = OpLabel + %20 = OpIAdd %6 %23 %21 + OpBranch %5 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource ESSL 320 +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpConstantNull %5 +%7 = OpConstant %5 1 +%8 = OpConstant %5 1 +%9 = OpTypeBool +%10 = OpTypePointer Function %5 +%2 = OpFunction %3 None %4 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +OpBranch %17 +%17 = OpLabel +%18 = OpSLessThan %9 %6 %7 +OpBranch %15 +%15 = OpLabel +%14 = OpIAdd %5 %6 %8 +OpBranch %16 +%16 = OpLabel +OpReturn +OpFunctionEnd +)"; + + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, shader, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << shader << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(shader, output, false); +} + +TEST_F(PassClassTest, ConditionValueIsConstantNull) { + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstantNull %6 + %13 = OpConstant %6 1 + %21 = OpConstant %6 1 + %10 = OpTypeBool + %17 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %11 = OpLabel + OpBranch %5 + %5 = OpLabel + %23 = OpPhi %6 %13 %11 %20 %15 + OpLoopMerge %8 %15 Unroll + OpBranch %14 + %14 = OpLabel + %9 = OpSGreaterThan %10 %23 %7 + OpBranchConditional %9 %15 %8 + %15 = OpLabel + %20 = OpISub %6 %23 %21 + OpBranch %5 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const std::string output = R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource ESSL 320 +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeInt 32 1 +%6 = OpConstantNull %5 +%7 = OpConstant %5 1 +%8 = OpConstant %5 1 +%9 = OpTypeBool +%10 = OpTypePointer Function %5 +%2 = OpFunction %3 None %4 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +OpBranch %17 +%17 = OpLabel +%18 = OpSGreaterThan %9 %7 %6 +OpBranch %15 +%15 = OpLabel +%14 = OpISub %5 %7 %8 +OpBranch %16 +%16 = OpLabel +OpReturn +OpFunctionEnd +)"; + + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, shader, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << shader << std::endl; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(shader, output, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/loop_optimizations/unswitch.cpp b/third_party/spirv-tools/test/opt/loop_optimizations/unswitch.cpp new file mode 100644 index 0000000..dc7073f --- /dev/null +++ b/third_party/spirv-tools/test/opt/loop_optimizations/unswitch.cpp @@ -0,0 +1,967 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using UnswitchTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 450 core +uniform vec4 c; +void main() { + int i = 0; + int j = 0; + bool cond = c[0] == 0; + for (; i < 10; i++, j++) { + if (cond) { + i++; + } + else { + j++; + } + } +} +*/ +TEST_F(UnswitchTest, SimpleUnswitch) { + const std::string text = R"( +; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual +; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None +; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] + +; Loop specialized for false. +; CHECK: [[loop_f]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_j:%\w+]] [[continue]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] +; [[loop_body]] = OpLabel +; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None +; CHECK: OpBranchConditional %false [[bb1:%\w+]] [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1 +; CHECK-NEXT: OpBranch [[sel_merge]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1 +; CHECK-NEXT: OpBranch [[sel_merge]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK: OpBranch [[if_merge]] + +; Loop specialized for true. +; CHECK: [[loop_t]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: [[phi_j:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_j:%\w+]] [[continue]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] +; [[loop_body]] = OpLabel +; CHECK: OpSelectionMerge [[sel_merge:%\w+]] None +; CHECK: OpBranchConditional %true [[bb1:%\w+]] [[bb2:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: [[inc_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1 +; CHECK-NEXT: OpBranch [[sel_merge]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: [[inc_j:%\w+]] = OpIAdd %int [[phi_j]] %int_1 +; CHECK-NEXT: OpBranch [[sel_merge]] +; CHECK: [[sel_merge]] = OpLabel +; CHECK: OpBranch [[if_merge]] + +; CHECK: [[if_merge]] = OpLabel +; CHECK-NEXT: OpReturn + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %c "c" + OpDecorate %c Location 0 + OpDecorate %c DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_UniformConstant_v4float = OpTypePointer UniformConstant %v4float + %c = OpVariable %_ptr_UniformConstant_v4float UniformConstant + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float + %float_0 = OpConstant %float 0 + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %21 = OpAccessChain %_ptr_UniformConstant_float %c %uint_0 + %22 = OpLoad %float %21 + %24 = OpFOrdEqual %bool %22 %float_0 + OpBranch %25 + %25 = OpLabel + %46 = OpPhi %int %int_0 %5 %43 %28 + %47 = OpPhi %int %int_0 %5 %45 %28 + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + %32 = OpSLessThan %bool %46 %int_10 + OpBranchConditional %32 %26 %27 + %26 = OpLabel + OpSelectionMerge %35 None + OpBranchConditional %24 %34 %39 + %34 = OpLabel + %38 = OpIAdd %int %46 %int_1 + OpBranch %35 + %39 = OpLabel + %41 = OpIAdd %int %47 %int_1 + OpBranch %35 + %35 = OpLabel + %48 = OpPhi %int %38 %34 %46 %39 + %49 = OpPhi %int %47 %34 %41 %39 + OpBranch %28 + %28 = OpLabel + %43 = OpIAdd %int %48 %int_1 + %45 = OpIAdd %int %49 %int_1 + OpBranch %25 + %27 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +in vec4 c; +void main() { + int i = 0; + bool cond = c[0] == 0; + for (; i < 10; i++) { + if (cond) { + i++; + } + else { + return; + } + } +} +*/ +TEST_F(UnswitchTest, UnswitchExit) { + const std::string text = R"( +; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual +; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None +; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] + +; Loop specialized for false. +; CHECK: [[loop_f]] = OpLabel +; CHECK: OpReturn + +; Loop specialized for true. +; CHECK: [[loop_t]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]] +; Check that we have i+=2. +; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1 +; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1 +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpBranch [[if_merge]] + +; CHECK: [[if_merge]] = OpLabel +; CHECK-NEXT: OpReturn + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %c + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + OpName %c "c" + OpDecorate %c Location 0 + OpDecorate %23 Uniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %c = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_float %c %uint_0 + %21 = OpLoad %float %20 + %23 = OpFOrdEqual %bool %21 %float_0 + OpBranch %24 + %24 = OpLabel + %42 = OpPhi %int %int_0 %5 %41 %27 + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %31 = OpSLessThan %bool %42 %int_10 + OpBranchConditional %31 %25 %26 + %25 = OpLabel + OpSelectionMerge %34 None + OpBranchConditional %23 %33 %38 + %33 = OpLabel + %37 = OpIAdd %int %42 %int_1 + OpBranch %34 + %38 = OpLabel + OpReturn + %34 = OpLabel + OpBranch %27 + %27 = OpLabel + %41 = OpIAdd %int %37 %int_1 + OpBranch %24 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +in vec4 c; +void main() { + int i = 0; + bool cond = c[0] == 0; + for (; i < 10; i++) { + if (cond) { + continue; + } + else { + i++; + } + } +} +*/ +TEST_F(UnswitchTest, UnswitchContinue) { + const std::string text = R"( +; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual +; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None +; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] + +; Loop specialized for false. +; CHECK: [[loop_f]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_f]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] +; CHECK: [[loop_body:%\w+]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional %false +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpBranch [[if_merge]] + +; Loop specialized for true. +; CHECK: [[loop_t]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] +; CHECK: [[loop_body:%\w+]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional %true +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpBranch [[if_merge]] + +; CHECK: [[if_merge]] = OpLabel +; CHECK-NEXT: OpReturn + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %c + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + OpName %c "c" + OpDecorate %c Location 0 + OpDecorate %23 Uniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %c = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_float %c %uint_0 + %21 = OpLoad %float %20 + %23 = OpFOrdEqual %bool %21 %float_0 + OpBranch %24 + %24 = OpLabel + %42 = OpPhi %int %int_0 %5 %41 %27 + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %31 = OpSLessThan %bool %42 %int_10 + OpBranchConditional %31 %25 %26 + %25 = OpLabel + OpSelectionMerge %34 None + OpBranchConditional %23 %33 %36 + %33 = OpLabel + OpBranch %27 + %36 = OpLabel + %39 = OpIAdd %int %42 %int_1 + OpBranch %34 + %34 = OpLabel + OpBranch %27 + %27 = OpLabel + %43 = OpPhi %int %42 %33 %39 %34 + %41 = OpIAdd %int %43 %int_1 + OpBranch %24 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +in vec4 c; +void main() { + int i = 0; + bool cond = c[0] == 0; + for (; i < 10; i++) { + if (cond) { + i++; + } + else { + break; + } + } +} +*/ +TEST_F(UnswitchTest, UnswitchKillLoop) { + const std::string text = R"( +; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual +; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None +; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] + +; Loop specialized for false. +; CHECK: [[loop_f]] = OpLabel +; CHECK: OpBranch [[if_merge]] + +; Loop specialized for true. +; CHECK: [[loop_t]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_t]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] {{%\w+}} [[merge]] +; Check that we have i+=2. +; CHECK: [[phi_i:%\w+]] = OpIAdd %int [[phi_i]] %int_1 +; CHECK: [[iv_i]] = OpIAdd %int [[phi_i]] %int_1 +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpBranch [[if_merge]] + +; CHECK: [[if_merge]] = OpLabel +; CHECK-NEXT: OpReturn + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %c + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + OpName %c "c" + OpDecorate %c Location 0 + OpDecorate %23 Uniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %c = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %20 = OpAccessChain %_ptr_Input_float %c %uint_0 + %21 = OpLoad %float %20 + %23 = OpFOrdEqual %bool %21 %float_0 + OpBranch %24 + %24 = OpLabel + %42 = OpPhi %int %int_0 %5 %41 %27 + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %31 = OpSLessThan %bool %42 %int_10 + OpBranchConditional %31 %25 %26 + %25 = OpLabel + OpSelectionMerge %34 None + OpBranchConditional %23 %33 %38 + %33 = OpLabel + %37 = OpIAdd %int %42 %int_1 + OpBranch %34 + %38 = OpLabel + OpBranch %26 + %34 = OpLabel + OpBranch %27 + %27 = OpLabel + %41 = OpIAdd %int %37 %int_1 + OpBranch %24 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +in vec4 c; +void main() { + int i = 0; + int cond = int(c[0]); + for (; i < 10; i++) { + switch (cond) { + case 0: + return; + case 1: + discard; + case 2: + break; + default: + break; + } + } + bool cond2 = i == 9; +} +*/ +TEST_F(UnswitchTest, UnswitchSwitch) { + const std::string text = R"( +; CHECK: [[cst_cond:%\w+]] = OpConvertFToS +; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None +; CHECK-NEXT: OpSwitch [[cst_cond]] [[default:%\w+]] 0 [[loop_0:%\w+]] 1 [[loop_1:%\w+]] 2 [[loop_2:%\w+]] + +; Loop specialized for 2. +; CHECK: [[loop_2]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_2]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] +; CHECK: [[loop_body]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpSwitch %int_2 +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpBranch [[if_merge]] + +; Loop specialized for 1. +; CHECK: [[loop_1]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_1]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] +; CHECK: [[loop_body]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpSwitch %int_1 +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpBranch [[if_merge]] + +; Loop specialized for 0. +; CHECK: [[loop_0]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[loop_0]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] +; CHECK: [[loop_body]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpSwitch %int_0 +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpBranch [[if_merge]] + +; Loop specialized for the default case. +; CHECK: [[default]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: [[phi_i:%\w+]] = OpPhi %int %int_0 [[default]] [[iv_i:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK: [[loop_exit:%\w+]] = OpSLessThan {{%\w+}} [[phi_i]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional [[loop_exit]] [[loop_body:%\w+]] [[merge]] +; CHECK: [[loop_body]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpSwitch %uint_3 +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: OpBranch [[if_merge]] + +; CHECK: [[if_merge]] = OpLabel +; CHECK-NEXT: OpReturn + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %c + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + OpName %c "c" + OpDecorate %c Location 0 + OpDecorate %20 Uniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %c = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 +%_ptr_Function_bool = OpTypePointer Function %bool + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpAccessChain %_ptr_Input_float %c %uint_0 + %19 = OpLoad %float %18 + %20 = OpConvertFToS %int %19 + OpBranch %21 + %21 = OpLabel + %49 = OpPhi %int %int_0 %5 %43 %24 + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %29 = OpSLessThan %bool %49 %int_10 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + OpSelectionMerge %35 None + OpSwitch %20 %34 0 %31 1 %32 2 %33 + %34 = OpLabel + OpBranch %35 + %31 = OpLabel + OpReturn + %32 = OpLabel + OpKill + %33 = OpLabel + OpBranch %35 + %35 = OpLabel + OpBranch %24 + %24 = OpLabel + %43 = OpIAdd %int %49 %int_1 + OpBranch %21 + %23 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 440 core +layout(location = 0)in vec4 c; +void main() { + int i = 0; + int j = 0; + int k = 0; + bool cond = c[0] == 0; + for (; i < 10; i++) { + for (; j < 10; j++) { + if (cond) { + i++; + } else { + j++; + } + } + } +} +*/ +TEST_F(UnswitchTest, UnSwitchNested) { + // Test that an branch can be unswitched out of two nested loops. + const std::string text = R"( +; CHECK: [[cst_cond:%\w+]] = OpFOrdEqual +; CHECK-NEXT: OpSelectionMerge [[if_merge:%\w+]] None +; CHECK-NEXT: OpBranchConditional [[cst_cond]] [[loop_t:%\w+]] [[loop_f:%\w+]] + +; Loop specialized for false +; CHECK: [[loop_f]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue:%\w+]] +; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_f]] {{%\w+}} [[continue]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK-NOT: [[merge]] = OpLabel +; CHECK: OpLoopMerge +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpSLessThan +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional %false +; CHECK: [[merge]] = OpLabel + +; Loop specialized for true. Same as first loop except the branch condition is true. +; CHECK: [[loop_t]] = OpLabel +; CHECK-NEXT: OpBranch [[loop:%\w+]] +; CHECK: [[loop]] = OpLabel +; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue:%\w+]] +; CHECK-NEXT: {{%\w+}} = OpPhi %int %int_0 [[loop_t]] {{%\w+}} [[continue]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] None +; CHECK-NOT: [[merge]] = OpLabel +; CHECK: OpLoopMerge +; CHECK-NEXT: OpBranch [[bb1:%\w+]] +; CHECK: [[bb1]] = OpLabel +; CHECK-NEXT: OpSLessThan +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional %true +; CHECK: [[merge]] = OpLabel + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %c + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 440 + OpName %main "main" + OpName %c "c" + OpDecorate %c Location 0 + OpDecorate %25 Uniform + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %c = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %22 = OpAccessChain %_ptr_Input_float %c %uint_0 + %23 = OpLoad %float %22 + %25 = OpFOrdEqual %bool %23 %float_0 + OpBranch %26 + %26 = OpLabel + %67 = OpPhi %int %int_0 %5 %52 %29 + %68 = OpPhi %int %int_0 %5 %70 %29 + OpLoopMerge %28 %29 None + OpBranch %30 + %30 = OpLabel + %33 = OpSLessThan %bool %67 %int_10 + OpBranchConditional %33 %27 %28 + %27 = OpLabel + OpBranch %34 + %34 = OpLabel + %69 = OpPhi %int %67 %27 %46 %37 + %70 = OpPhi %int %68 %27 %50 %37 + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %40 = OpSLessThan %bool %70 %int_10 + OpBranchConditional %40 %35 %36 + %35 = OpLabel + OpSelectionMerge %43 None + OpBranchConditional %25 %42 %47 + %42 = OpLabel + %46 = OpIAdd %int %69 %int_1 + OpBranch %43 + %47 = OpLabel + OpReturn + %43 = OpLabel + OpBranch %37 + %37 = OpLabel + %50 = OpIAdd %int %70 %int_1 + OpBranch %34 + %36 = OpLabel + OpBranch %29 + %29 = OpLabel + %52 = OpIAdd %int %69 %int_1 + OpBranch %26 + %28 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 330 core +in vec4 c; +void main() { + bool cond = false; + if (c[0] == 0) { + cond = c[1] == 0; + } else { + cond = c[2] == 0; + } + for (int i = 0; i < 10; i++) { + if (cond) { + i++; + } + } +} +*/ +TEST_F(UnswitchTest, UnswitchNotUniform) { + // Check that the unswitch is not triggered (condition loop invariant but not + // uniform) + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %c + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + OpName %c "c" + OpDecorate %c Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %c = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %float_0 = OpConstant %float 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_0 = OpConstant %int 0 + %int_10 = OpConstant %int 10 + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %17 = OpAccessChain %_ptr_Input_float %c %uint_0 + %18 = OpLoad %float %17 + %20 = OpFOrdEqual %bool %18 %float_0 + OpSelectionMerge %22 None + OpBranchConditional %20 %21 %27 + %21 = OpLabel + %24 = OpAccessChain %_ptr_Input_float %c %uint_1 + %25 = OpLoad %float %24 + %26 = OpFOrdEqual %bool %25 %float_0 + OpBranch %22 + %27 = OpLabel + %29 = OpAccessChain %_ptr_Input_float %c %uint_2 + %30 = OpLoad %float %29 + %31 = OpFOrdEqual %bool %30 %float_0 + OpBranch %22 + %22 = OpLabel + %52 = OpPhi %bool %26 %21 %31 %27 + OpBranch %36 + %36 = OpLabel + %53 = OpPhi %int %int_0 %22 %51 %39 + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + %43 = OpSLessThan %bool %53 %int_10 + OpBranchConditional %43 %37 %38 + %37 = OpLabel + OpSelectionMerge %46 None + OpBranchConditional %52 %45 %46 + %45 = OpLabel + %49 = OpIAdd %int %53 %int_1 + OpBranch %46 + %46 = OpLabel + %54 = OpPhi %int %53 %37 %49 %45 + OpBranch %39 + %39 = OpLabel + %51 = OpIAdd %int %54 %int_1 + OpBranch %36 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto result = + SinglePassRunAndDisassemble(text, true, false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(UnswitchTest, DontUnswitchLatch) { + // Check that the unswitch is not triggered for the latch branch. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool +%false = OpConstantFalse %bool + %4 = OpFunction %void None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranch %7 + %7 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranchConditional %false %6 %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto result = + SinglePassRunAndDisassemble(text, true, false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(UnswitchTest, DontUnswitchConstantCondition) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %true = OpConstantTrue %bool + %int_1 = OpConstant %int 1 + %main = OpFunction %void None %4 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + %12 = OpPhi %int %int_0 %10 %13 %14 + OpLoopMerge %15 %14 None + OpBranch %16 + %16 = OpLabel + %17 = OpSLessThan %bool %12 %int_1 + OpBranchConditional %17 %18 %15 + %18 = OpLabel + OpSelectionMerge %19 None + OpBranchConditional %true %20 %19 + %20 = OpLabel + %21 = OpIAdd %int %12 %int_1 + OpBranch %19 + %19 = OpLabel + %22 = OpPhi %int %21 %20 %12 %18 + OpBranch %14 + %14 = OpLabel + %13 = OpIAdd %int %22 %int_1 + OpBranch %11 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto result = + SinglePassRunAndDisassemble(text, true, false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/module_test.cpp b/third_party/spirv-tools/test/opt/module_test.cpp new file mode 100644 index 0000000..a3c2eed --- /dev/null +++ b/third_party/spirv-tools/test/opt/module_test.cpp @@ -0,0 +1,341 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "spirv-tools/libspirv.hpp" +#include "test/opt/module_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::Eq; +using spvtest::GetIdBound; + +TEST(ModuleTest, SetIdBound) { + Module m; + // It's initialized to 0. + EXPECT_EQ(0u, GetIdBound(m)); + + m.SetIdBound(19); + EXPECT_EQ(19u, GetIdBound(m)); + + m.SetIdBound(102); + EXPECT_EQ(102u, GetIdBound(m)); +} + +// Returns an IRContext owning the module formed by assembling the given text, +// then loading the result. +inline std::unique_ptr BuildModule(std::string text) { + return spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); +} + +TEST(ModuleTest, ComputeIdBound) { + // Emtpy module case. + EXPECT_EQ(1u, BuildModule("")->module()->ComputeIdBound()); + // Sensitive to result id + EXPECT_EQ(2u, BuildModule("%void = OpTypeVoid")->module()->ComputeIdBound()); + // Sensitive to type id + EXPECT_EQ(1000u, + BuildModule("%a = OpTypeArray !999 3")->module()->ComputeIdBound()); + // Sensitive to a regular Id parameter + EXPECT_EQ(2000u, + BuildModule("OpDecorate !1999 0")->module()->ComputeIdBound()); + // Sensitive to a scope Id parameter. + EXPECT_EQ(3000u, + BuildModule("%f = OpFunction %void None %fntype %a = OpLabel " + "OpMemoryBarrier !2999 %b\n") + ->module() + ->ComputeIdBound()); + // Sensitive to a semantics Id parameter + EXPECT_EQ(4000u, + BuildModule("%f = OpFunction %void None %fntype %a = OpLabel " + "OpMemoryBarrier %b !3999\n") + ->module() + ->ComputeIdBound()); +} + +TEST(ModuleTest, OstreamOperator) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %7 "restrict" +OpDecorate %8 Restrict +%9 = OpTypeVoid +%10 = OpTypeInt 32 0 +%11 = OpTypeStruct %10 %10 +%12 = OpTypePointer Function %10 +%13 = OpTypePointer Function %11 +%14 = OpConstant %10 0 +%15 = OpConstant %10 1 +%7 = OpTypeFunction %9 +%1 = OpFunction %9 None %7 +%2 = OpLabel +%8 = OpVariable %13 Function +%3 = OpAccessChain %12 %8 %14 +%4 = OpLoad %10 %3 +%5 = OpAccessChain %12 %8 %15 +%6 = OpLoad %10 %5 +OpReturn +OpFunctionEnd)"; + + std::string s; + std::ostringstream str(s); + str << *BuildModule(text)->module(); + EXPECT_EQ(text, str.str()); +} + +TEST(ModuleTest, OstreamOperatorInt64) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpCapability Int64 +OpMemoryModel Logical GLSL450 +OpName %7 "restrict" +OpDecorate %5 Restrict +%9 = OpTypeVoid +%10 = OpTypeInt 64 0 +%11 = OpTypeStruct %10 %10 +%12 = OpTypePointer Function %10 +%13 = OpTypePointer Function %11 +%14 = OpConstant %10 0 +%15 = OpConstant %10 1 +%16 = OpConstant %10 4294967297 +%7 = OpTypeFunction %9 +%1 = OpFunction %9 None %7 +%2 = OpLabel +%5 = OpVariable %12 Function +%6 = OpLoad %10 %5 +OpSelectionMerge %3 None +OpSwitch %6 %3 4294967297 %4 +%4 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::string s; + std::ostringstream str(s); + str << *BuildModule(text)->module(); + EXPECT_EQ(text, str.str()); +} + +TEST(ModuleTest, IdBoundTestAtLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = BuildModule(text); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound); + uint32_t next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, 0); + EXPECT_EQ(current_bound, context->module()->id_bound()); + next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, 0); +} + +TEST(ModuleTest, IdBoundTestBelowLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = BuildModule(text); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound + 100); + uint32_t next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, current_bound); + EXPECT_EQ(current_bound + 1, context->module()->id_bound()); + next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, current_bound + 1); +} + +TEST(ModuleTest, IdBoundTestNearLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = BuildModule(text); + uint32_t current_bound = context->module()->id_bound(); + context->set_max_id_bound(current_bound + 1); + uint32_t next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, current_bound); + EXPECT_EQ(current_bound + 1, context->module()->id_bound()); + next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, 0); +} + +TEST(ModuleTest, IdBoundTestUIntMax) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4294967294 = OpLabel ; ID is UINT_MAX-1 +OpReturn +OpFunctionEnd)"; + + std::unique_ptr context = BuildModule(text); + uint32_t current_bound = context->module()->id_bound(); + + // Expecting |BuildModule| to preserve the numeric ids. + EXPECT_EQ(current_bound, std::numeric_limits::max()); + + context->set_max_id_bound(current_bound); + uint32_t next_id_bound = context->module()->TakeNextIdBound(); + EXPECT_EQ(next_id_bound, 0); + EXPECT_EQ(current_bound, context->module()->id_bound()); +} + +// Tests that "text" does not change when it is assembled, converted into a +// module, converted back to a binary, and then disassembled. +void AssembleAndDisassemble(const std::string& text) { + std::unique_ptr context = BuildModule(text); + std::vector binary; + + context->module()->ToBinary(&binary, false); + + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::string s; + tools.Disassemble(binary, &s); + EXPECT_EQ(s, text); +} + +TEST(ModuleTest, TrailingOpLine) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%5 = OpString "file.ext" +%void = OpTypeVoid +%2 = OpTypeFunction %void +%3 = OpFunction %void None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +OpLine %5 1 0 +)"; + + AssembleAndDisassemble(text); +} + +TEST(ModuleTest, TrailingOpNoLine) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%3 = OpFunction %void None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +OpNoLine +)"; + + AssembleAndDisassemble(text); +} + +TEST(ModuleTest, MulitpleTrailingOpLine) { + const std::string text = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%5 = OpString "file.ext" +%void = OpTypeVoid +%2 = OpTypeFunction %void +%3 = OpFunction %void None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +OpLine %5 1 0 +OpNoLine +OpLine %5 1 1 +)"; + + AssembleAndDisassemble(text); +} + +TEST(ModuleTest, NonSemanticInfoIteration) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpExtInst %2 %1 1 +%5 = OpFunction %2 None %3 +%6 = OpLabel +%7 = OpExtInst %2 %1 1 +OpReturn +OpFunctionEnd +%8 = OpExtInst %2 %1 1 +%9 = OpFunction %2 None %3 +%10 = OpLabel +%11 = OpExtInst %2 %1 1 +OpReturn +OpFunctionEnd +%12 = OpExtInst %2 %1 1 +)"; + + std::unique_ptr context = BuildModule(text); + std::unordered_set non_semantic_ids; + context->module()->ForEachInst( + [&non_semantic_ids](const Instruction* inst) { + if (inst->opcode() == SpvOpExtInst) { + non_semantic_ids.insert(inst->result_id()); + } + }, + false); + + EXPECT_EQ(1, non_semantic_ids.count(4)); + EXPECT_EQ(1, non_semantic_ids.count(7)); + EXPECT_EQ(1, non_semantic_ids.count(8)); + EXPECT_EQ(1, non_semantic_ids.count(11)); + EXPECT_EQ(1, non_semantic_ids.count(12)); +} +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/module_utils.h b/third_party/spirv-tools/test/opt/module_utils.h new file mode 100644 index 0000000..007f132 --- /dev/null +++ b/third_party/spirv-tools/test/opt/module_utils.h @@ -0,0 +1,34 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_OPT_MODULE_UTILS_H_ +#define TEST_OPT_MODULE_UTILS_H_ + +#include +#include "source/opt/module.h" + +namespace spvtest { + +inline uint32_t GetIdBound(const spvtools::opt::Module& m) { + std::vector binary; + m.ToBinary(&binary, false); + // The 5-word header must always exist. + EXPECT_LE(5u, binary.size()); + // The bound is the fourth word. + return binary[3]; +} + +} // namespace spvtest + +#endif // TEST_OPT_MODULE_UTILS_H_ diff --git a/third_party/spirv-tools/test/opt/optimizer_test.cpp b/third_party/spirv-tools/test/opt/optimizer_test.cpp new file mode 100644 index 0000000..945aa78 --- /dev/null +++ b/third_party/spirv-tools/test/opt/optimizer_test.cpp @@ -0,0 +1,767 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::Eq; + +// Return a string that contains the minimum instructions needed to form +// a valid module. Other instructions can be appended to this string. +std::string Header() { + return R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +)"; +} + +TEST(Optimizer, CanRunNullPassWithDistinctInputOutputVectors) { + SpirvTools tools(SPV_ENV_UNIVERSAL_1_0); + std::vector binary_in; + tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", + &binary_in); + + Optimizer opt(SPV_ENV_UNIVERSAL_1_0); + opt.RegisterPass(CreateNullPass()); + std::vector binary_out; + opt.Run(binary_in.data(), binary_in.size(), &binary_out); + + std::string disassembly; + tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly); + EXPECT_THAT(disassembly, + Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n")); +} + +TEST(Optimizer, CanRunTransformingPassWithDistinctInputOutputVectors) { + SpirvTools tools(SPV_ENV_UNIVERSAL_1_0); + std::vector binary_in; + tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", + &binary_in); + + Optimizer opt(SPV_ENV_UNIVERSAL_1_0); + opt.RegisterPass(CreateStripDebugInfoPass()); + std::vector binary_out; + opt.Run(binary_in.data(), binary_in.size(), &binary_out); + + std::string disassembly; + tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly); + EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n")); +} + +TEST(Optimizer, CanRunNullPassWithAliasedVectors) { + SpirvTools tools(SPV_ENV_UNIVERSAL_1_0); + std::vector binary; + tools.Assemble("OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary); + + Optimizer opt(SPV_ENV_UNIVERSAL_1_0); + opt.RegisterPass(CreateNullPass()); + opt.Run(binary.data(), binary.size(), &binary); // This is the key. + + std::string disassembly; + tools.Disassemble(binary.data(), binary.size(), &disassembly); + EXPECT_THAT(disassembly, Eq("OpName %foo \"foo\"\n%foo = OpTypeVoid\n")); +} + +TEST(Optimizer, CanRunNullPassWithAliasedVectorDataButDifferentSize) { + SpirvTools tools(SPV_ENV_UNIVERSAL_1_0); + std::vector binary; + tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary); + + Optimizer opt(SPV_ENV_UNIVERSAL_1_0); + opt.RegisterPass(CreateNullPass()); + auto orig_size = binary.size(); + // Now change the size. Add a word that will be ignored + // by the optimizer. + binary.push_back(42); + EXPECT_THAT(orig_size + 1, Eq(binary.size())); + opt.Run(binary.data(), orig_size, &binary); // This is the key. + // The binary vector should have been rewritten. + EXPECT_THAT(binary.size(), Eq(orig_size)); + + std::string disassembly; + tools.Disassemble(binary.data(), binary.size(), &disassembly); + EXPECT_THAT(disassembly, + Eq(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid\n")); +} + +TEST(Optimizer, CanRunTransformingPassWithAliasedVectors) { + SpirvTools tools(SPV_ENV_UNIVERSAL_1_0); + std::vector binary; + tools.Assemble(Header() + "OpName %foo \"foo\"\n%foo = OpTypeVoid", &binary); + + Optimizer opt(SPV_ENV_UNIVERSAL_1_0); + opt.RegisterPass(CreateStripDebugInfoPass()); + opt.Run(binary.data(), binary.size(), &binary); // This is the key + + std::string disassembly; + tools.Disassemble(binary.data(), binary.size(), &disassembly); + EXPECT_THAT(disassembly, Eq(Header() + "%void = OpTypeVoid\n")); +} + +TEST(Optimizer, CanValidateFlags) { + Optimizer opt(SPV_ENV_UNIVERSAL_1_0); + EXPECT_FALSE(opt.FlagHasValidForm("bad-flag")); + EXPECT_TRUE(opt.FlagHasValidForm("-O")); + EXPECT_TRUE(opt.FlagHasValidForm("-Os")); + EXPECT_FALSE(opt.FlagHasValidForm("-O2")); + EXPECT_TRUE(opt.FlagHasValidForm("--this_flag")); +} + +TEST(Optimizer, CanRegisterPassesFromFlags) { + SpirvTools tools(SPV_ENV_UNIVERSAL_1_0); + Optimizer opt(SPV_ENV_UNIVERSAL_1_0); + + spv_message_level_t msg_level; + const char* msg_fname; + spv_position_t msg_position; + const char* msg; + auto examine_message = [&msg_level, &msg_fname, &msg_position, &msg]( + spv_message_level_t ml, const char* f, + const spv_position_t& p, const char* m) { + msg_level = ml; + msg_fname = f; + msg_position = p; + msg = m; + }; + opt.SetMessageConsumer(examine_message); + + std::vector pass_flags = { + "--strip-debug", + "--strip-reflect", + "--set-spec-const-default-value=23:42 21:12", + "--if-conversion", + "--freeze-spec-const", + "--inline-entry-points-exhaustive", + "--inline-entry-points-opaque", + "--convert-local-access-chains", + "--eliminate-dead-code-aggressive", + "--eliminate-insert-extract", + "--eliminate-local-single-block", + "--eliminate-local-single-store", + "--merge-blocks", + "--merge-return", + "--eliminate-dead-branches", + "--eliminate-dead-functions", + "--eliminate-local-multi-store", + "--eliminate-dead-const", + "--eliminate-dead-inserts", + "--eliminate-dead-variables", + "--fold-spec-const-op-composite", + "--loop-unswitch", + "--scalar-replacement=300", + "--scalar-replacement", + "--strength-reduction", + "--unify-const", + "--flatten-decorations", + "--compact-ids", + "--cfg-cleanup", + "--local-redundancy-elimination", + "--loop-invariant-code-motion", + "--reduce-load-size", + "--redundancy-elimination", + "--private-to-local", + "--remove-duplicates", + "--workaround-1209", + "--replace-invalid-opcode", + "--simplify-instructions", + "--ssa-rewrite", + "--copy-propagate-arrays", + "--loop-fission=20", + "--loop-fusion=2", + "--loop-unroll", + "--vector-dce", + "--loop-unroll-partial=3", + "--loop-peeling", + "--ccp", + "-O", + "-Os", + "--legalize-hlsl"}; + EXPECT_TRUE(opt.RegisterPassesFromFlags(pass_flags)); + + // Test some invalid flags. + EXPECT_FALSE(opt.RegisterPassFromFlag("-O2")); + EXPECT_EQ(msg_level, SPV_MSG_ERROR); + + EXPECT_FALSE(opt.RegisterPassFromFlag("-loop-unroll")); + EXPECT_EQ(msg_level, SPV_MSG_ERROR); + + EXPECT_FALSE(opt.RegisterPassFromFlag("--set-spec-const-default-value")); + EXPECT_EQ(msg_level, SPV_MSG_ERROR); + + EXPECT_FALSE(opt.RegisterPassFromFlag("--scalar-replacement=s")); + EXPECT_EQ(msg_level, SPV_MSG_ERROR); + + EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fission=-4")); + EXPECT_EQ(msg_level, SPV_MSG_ERROR); + + EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-fusion=xx")); + EXPECT_EQ(msg_level, SPV_MSG_ERROR); + + EXPECT_FALSE(opt.RegisterPassFromFlag("--loop-unroll-partial")); + EXPECT_EQ(msg_level, SPV_MSG_ERROR); +} + +TEST(Optimizer, VulkanToWebGPUSetsCorrectPasses) { + Optimizer opt(SPV_ENV_VULKAN_1_1); + opt.RegisterVulkanToWebGPUPasses(); + std::vector pass_names = opt.GetPassNames(); + + std::vector registered_passes; + for (auto name = pass_names.begin(); name != pass_names.end(); ++name) + registered_passes.push_back(*name); + + std::vector expected_passes = {"eliminate-dead-branches", + "eliminate-dead-code-aggressive", + "eliminate-dead-const", + "flatten-decorations", + "strip-atomic-counter-memory", + "generate-webgpu-initializers", + "legalize-vector-shuffle", + "split-invalid-unreachable", + "compact-ids"}; + std::sort(registered_passes.begin(), registered_passes.end()); + std::sort(expected_passes.begin(), expected_passes.end()); + + ASSERT_EQ(registered_passes.size(), expected_passes.size()); + for (size_t i = 0; i < registered_passes.size(); i++) + EXPECT_EQ(registered_passes[i], expected_passes[i]); +} + +struct VulkanToWebGPUPassCase { + // Input SPIR-V + std::string input; + // Expected result SPIR-V + std::string expected; + // Specific pass under test, used for logging messages. + std::string pass; +}; + +using VulkanToWebGPUPassTest = + PassTest<::testing::TestWithParam>; + +TEST_P(VulkanToWebGPUPassTest, Ran) { + std::vector binary; + { + SpirvTools tools(SPV_ENV_VULKAN_1_1); + tools.Assemble(GetParam().input, &binary); + } + + Optimizer opt(SPV_ENV_VULKAN_1_1); + opt.RegisterVulkanToWebGPUPasses(); + + std::vector optimized; + class ValidatorOptions validator_options; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, + validator_options, true)) + << GetParam().input << "\n"; + std::string disassembly; + { + SpirvTools tools(SPV_ENV_WEBGPU_0); + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); + } + + EXPECT_EQ(GetParam().expected, disassembly) + << "Was expecting pass '" << GetParam().pass << "' to have been run.\n"; +} + +INSTANTIATE_TEST_SUITE_P( + Optimizer, VulkanToWebGPUPassTest, + ::testing::ValuesIn(std::vector{ + // FlattenDecorations + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Fragment %main \"main\" %hue %saturation %value\n" + "OpExecutionMode %main OriginUpperLeft\n" + "OpDecorate %group Flat\n" + "OpDecorate %group NoPerspective\n" + "%group = OpDecorationGroup\n" + "%void = OpTypeVoid\n" + "%void_fn = OpTypeFunction %void\n" + "%float = OpTypeFloat 32\n" + "%_ptr_Input_float = OpTypePointer Input %float\n" + "%hue = OpVariable %_ptr_Input_float Input\n" + "%saturation = OpVariable %_ptr_Input_float Input\n" + "%value = OpVariable %_ptr_Input_float Input\n" + "%main = OpFunction %void None %void_fn\n" + "%entry = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Fragment %1 \"main\" %2 %3 %4\n" + "OpExecutionMode %1 OriginUpperLeft\n" + "%void = OpTypeVoid\n" + "%6 = OpTypeFunction %void\n" + "%float = OpTypeFloat 32\n" + "%_ptr_Input_float = OpTypePointer Input %float\n" + "%2 = OpVariable %_ptr_Input_float Input\n" + "%3 = OpVariable %_ptr_Input_float Input\n" + "%4 = OpVariable %_ptr_Input_float Input\n" + "%1 = OpFunction %void None %6\n" + "%9 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n", + // pass + "flatten-decorations"}, + // Eliminate Dead Constants + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %func \"shader\"\n" + "%u32 = OpTypeInt 32 0\n" + "%u32_ptr = OpTypePointer Workgroup %u32\n" + "%u32_var = OpVariable %u32_ptr Workgroup\n" + "%u32_1 = OpConstant %u32 1\n" + "%cross_device = OpConstant %u32 0\n" + "%relaxed = OpConstant %u32 0\n" + "%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288\n" + "%void = OpTypeVoid\n" + "%void_f = OpTypeFunction %void\n" + "%func = OpFunction %void None %void_f\n" + "%label = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n" + "%4 = OpVariable %_ptr_Workgroup_uint Workgroup\n" + "%void = OpTypeVoid\n" + "%6 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %6\n" + "%7 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n", + "eliminate-dead-const"}, + // Strip Atomic Counter Memory + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %func \"shader\"\n" + "%u32 = OpTypeInt 32 0\n" + "%u32_ptr = OpTypePointer Workgroup %u32\n" + "%u32_var = OpVariable %u32_ptr Workgroup\n" + "%u32_0 = OpConstant %u32 0\n" + "%u32_1 = OpConstant %u32 1\n" + "%cross_device = OpConstant %u32 0\n" + "%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288\n" + "%void = OpTypeVoid\n" + "%void_f = OpTypeFunction %void\n" + "%func = OpFunction %void None %void_f\n" + "%label = OpLabel\n" + " OpAtomicStore %u32_var %cross_device " + "%acquire_release_atomic_counter_workgroup %u32_1\n" + "%val1 = OpAtomicIIncrement %u32 %u32_var %cross_device " + "%acquire_release_atomic_counter_workgroup\n" + "%val2 = OpAtomicCompareExchange %u32 %u32_var %cross_device " + "%acquire_release_atomic_counter_workgroup " + "%acquire_release_atomic_counter_workgroup %u32_0 %u32_0\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint\n" + "%4 = OpVariable %_ptr_Workgroup_uint Workgroup\n" + "%uint_0 = OpConstant %uint 0\n" + "%uint_1 = OpConstant %uint 1\n" + "%uint_0_0 = OpConstant %uint 0\n" + "%void = OpTypeVoid\n" + "%9 = OpTypeFunction %void\n" + "%uint_264 = OpConstant %uint 264\n" + "%1 = OpFunction %void None %9\n" + "%11 = OpLabel\n" + "OpAtomicStore %4 %uint_0_0 %uint_264 %uint_1\n" + "%12 = OpAtomicIIncrement %uint %4 %uint_0_0 %uint_264\n" + "%13 = OpAtomicCompareExchange %uint %4 %uint_0_0 %uint_264 %uint_264 " + "%uint_0 %uint_0\n" + "OpReturn\n" + "OpFunctionEnd\n", + // pass + "strip-atomic-counter-memory"}, + // Generate WebGPU Initializers + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %func \"shader\"\n" + "%u32 = OpTypeInt 32 0\n" + "%u32_ptr = OpTypePointer Private %u32\n" + "%u32_var = OpVariable %u32_ptr Private\n" + "%u32_0 = OpConstant %u32 0\n" + "%void = OpTypeVoid\n" + "%void_f = OpTypeFunction %void\n" + "%func = OpFunction %void None %void_f\n" + "%label = OpLabel\n" + "OpStore %u32_var %u32_0\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%_ptr_Private_uint = OpTypePointer Private %uint\n" + "%4 = OpConstantNull %uint\n" + "%5 = OpVariable %_ptr_Private_uint Private %4\n" + "%uint_0 = OpConstant %uint 0\n" + "%void = OpTypeVoid\n" + "%8 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %8\n" + "%9 = OpLabel\n" + "OpStore %5 %uint_0\n" + "OpReturn\n" + "OpFunctionEnd\n", + // pass + "generate-webgpu-initializers"}, + // Legalize Vector Shuffle + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%v3uint = OpTypeVector %uint 3\n" + "%_ptr_Function_v3uint = OpTypePointer Function %v3uint\n" + "%void = OpTypeVoid\n" + "%6 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %6\n" + "%7 = OpLabel\n" + "%8 = OpVariable %_ptr_Function_v3uint Function\n" + "%9 = OpLoad %v3uint %8\n" + "%10 = OpLoad %v3uint %8\n" + "%11 = OpVectorShuffle %v3uint %9 %10 2 1 0xFFFFFFFF\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%v3uint = OpTypeVector %uint 3\n" + "%_ptr_Function_v3uint = OpTypePointer Function %v3uint\n" + "%void = OpTypeVoid\n" + "%6 = OpTypeFunction %void\n" + "%7 = OpConstantNull %v3uint\n" + "%1 = OpFunction %void None %6\n" + "%8 = OpLabel\n" + "%9 = OpVariable %_ptr_Function_v3uint Function %7\n" + "%10 = OpLoad %v3uint %9\n" + "%11 = OpLoad %v3uint %9\n" + "%12 = OpVectorShuffle %v3uint %10 %11 2 1 0\n" + "OpReturn\n" + "OpFunctionEnd\n", + // pass + "legalize-vector-shuffle"}, + // Split Invalid Unreachable + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%uint_1 = OpConstant %uint 1\n" + "%uint_2 = OpConstant %uint 2\n" + "%void = OpTypeVoid\n" + "%bool = OpTypeBool\n" + "%7 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %7\n" + "%8 = OpLabel\n" + "OpBranch %9\n" + "%9 = OpLabel\n" + "OpLoopMerge %10 %11 None\n" + "OpBranch %12\n" + "%12 = OpLabel\n" + "%13 = OpSLessThan %bool %uint_1 %uint_2\n" + "OpSelectionMerge %11 None\n" + "OpBranchConditional %13 %14 %15\n" + "%14 = OpLabel\n" + "OpReturn\n" + "%15 = OpLabel\n" + "OpReturn\n" + "%10 = OpLabel\n" + "OpUnreachable\n" + "%11 = OpLabel\n" + "OpBranch %9\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%uint_1 = OpConstant %uint 1\n" + "%uint_2 = OpConstant %uint 2\n" + "%void = OpTypeVoid\n" + "%bool = OpTypeBool\n" + "%7 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %7\n" + "%8 = OpLabel\n" + "OpBranch %9\n" + "%9 = OpLabel\n" + "OpLoopMerge %10 %11 None\n" + "OpBranch %12\n" + "%12 = OpLabel\n" + "%13 = OpSLessThan %bool %uint_1 %uint_2\n" + "OpSelectionMerge %14 None\n" + "OpBranchConditional %13 %15 %16\n" + "%15 = OpLabel\n" + "OpReturn\n" + "%16 = OpLabel\n" + "OpReturn\n" + "%10 = OpLabel\n" + "OpUnreachable\n" + "%14 = OpLabel\n" + "OpUnreachable\n" + "%11 = OpLabel\n" + "OpBranch %9\n" + "OpFunctionEnd\n", + // pass + "split-invalid-unreachable"}, + // Compact IDs + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1000 \"shader\"\n" + "%10 = OpTypeVoid\n" + "%100 = OpTypeFunction %10\n" + "%1000 = OpFunction %10 None %100\n" + "%10000 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%void = OpTypeVoid\n" + "%3 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %3\n" + "%4 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n", + // pass + "compact-ids"}})); + +TEST(Optimizer, WebGPUToVulkanSetsCorrectPasses) { + Optimizer opt(SPV_ENV_WEBGPU_0); + opt.RegisterWebGPUToVulkanPasses(); + std::vector pass_names = opt.GetPassNames(); + + std::vector registered_passes; + for (auto name = pass_names.begin(); name != pass_names.end(); ++name) + registered_passes.push_back(*name); + + std::vector expected_passes = {"decompose-initialized-variables", + "compact-ids"}; + std::sort(registered_passes.begin(), registered_passes.end()); + std::sort(expected_passes.begin(), expected_passes.end()); + + ASSERT_EQ(registered_passes.size(), expected_passes.size()); + for (size_t i = 0; i < registered_passes.size(); i++) + EXPECT_EQ(registered_passes[i], expected_passes[i]); +} + +struct WebGPUToVulkanPassCase { + // Input SPIR-V + std::string input; + // Expected result SPIR-V + std::string expected; + // Specific pass under test, used for logging messages. + std::string pass; +}; + +using WebGPUToVulkanPassTest = + PassTest<::testing::TestWithParam>; + +TEST_P(WebGPUToVulkanPassTest, Ran) { + std::vector binary; + { + SpirvTools tools(SPV_ENV_WEBGPU_0); + tools.Assemble(GetParam().input, &binary); + } + + Optimizer opt(SPV_ENV_WEBGPU_0); + opt.RegisterWebGPUToVulkanPasses(); + + std::vector optimized; + class ValidatorOptions validator_options; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, + validator_options, true)); + std::string disassembly; + { + SpirvTools tools(SPV_ENV_VULKAN_1_1); + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); + } + + EXPECT_EQ(GetParam().expected, disassembly) + << "Was expecting pass '" << GetParam().pass << "' to have been run.\n"; +} + +INSTANTIATE_TEST_SUITE_P( + Optimizer, WebGPUToVulkanPassTest, + ::testing::ValuesIn(std::vector{ + // Decompose Initialized Variables + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%_ptr_Function_uint = OpTypePointer Function %uint\n" + "%4 = OpConstantNull %uint\n" + "%void = OpTypeVoid\n" + "%6 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %6\n" + "%7 = OpLabel\n" + "%8 = OpVariable %_ptr_Function_uint Function %4\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%uint = OpTypeInt 32 0\n" + "%_ptr_Function_uint = OpTypePointer Function %uint\n" + "%4 = OpConstantNull %uint\n" + "%void = OpTypeVoid\n" + "%6 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %6\n" + "%7 = OpLabel\n" + "%8 = OpVariable %_ptr_Function_uint Function\n" + "OpStore %8 %4\n" + "OpReturn\n" + "OpFunctionEnd\n", + // pass + "decompose-initialized-variables"}, + // Compact IDs + {// input + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1000 \"shader\"\n" + "%10 = OpTypeVoid\n" + "%100 = OpTypeFunction %10\n" + "%1000 = OpFunction %10 None %100\n" + "%10000 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n", + // expected + "OpCapability Shader\n" + "OpCapability VulkanMemoryModel\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical Vulkan\n" + "OpEntryPoint Vertex %1 \"shader\"\n" + "%void = OpTypeVoid\n" + "%3 = OpTypeFunction %void\n" + "%1 = OpFunction %void None %3\n" + "%4 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n", + // pass + "compact-ids"}})); + +TEST(Optimizer, RemoveNop) { + // Test that OpNops are removed even if no optimizations are run. + const std::string before = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%3 = OpFunction %void None %2 +%4 = OpLabel +OpNop +OpReturn +OpFunctionEnd +)"; + + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%3 = OpFunction %void None %2 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::vector binary; + { + SpirvTools tools(SPV_ENV_VULKAN_1_1); + tools.Assemble(before, &binary); + } + + Optimizer opt(SPV_ENV_VULKAN_1_1); + + std::vector optimized; + class ValidatorOptions validator_options; + ASSERT_TRUE(opt.Run(binary.data(), binary.size(), &optimized, + validator_options, true)) + << before << "\n"; + std::string disassembly; + { + SpirvTools tools(SPV_ENV_WEBGPU_0); + tools.Disassemble(optimized.data(), optimized.size(), &disassembly); + } + + EXPECT_EQ(after, disassembly) + << "Was expecting the OpNop to have been removed."; +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/pass_fixture.h b/third_party/spirv-tools/test/opt/pass_fixture.h new file mode 100644 index 0000000..e520821 --- /dev/null +++ b/third_party/spirv-tools/test/opt/pass_fixture.h @@ -0,0 +1,295 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_OPT_PASS_FIXTURE_H_ +#define TEST_OPT_PASS_FIXTURE_H_ + +#include +#include +#include +#include +#include +#include + +#include "effcee/effcee.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/pass_manager.h" +#include "source/opt/passes.h" +#include "source/spirv_optimizer_options.h" +#include "source/spirv_validator_options.h" +#include "source/util/make_unique.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { + +// Template class for testing passes. It contains some handy utility methods for +// running passes and checking results. +// +// To write value-Parameterized tests: +// using ValueParamTest = PassTest<::testing::TestWithParam>; +// To use as normal fixture: +// using FixtureTest = PassTest<::testing::Test>; +template +class PassTest : public TestT { + public: + PassTest() + : consumer_( + [](spv_message_level_t, const char*, const spv_position_t&, + const char* message) { std::cerr << message << std::endl; }), + context_(nullptr), + manager_(new PassManager()), + assemble_options_(SpirvTools::kDefaultAssembleOption), + disassemble_options_(SpirvTools::kDefaultDisassembleOption), + env_(SPV_ENV_UNIVERSAL_1_3) {} + + // Runs the given |pass| on the binary assembled from the |original|. + // Returns a tuple of the optimized binary and the boolean value returned + // from pass Process() function. + std::tuple, Pass::Status> OptimizeToBinary( + Pass* pass, const std::string& original, bool skip_nop) { + context_ = BuildModule(env_, consumer_, original, assemble_options_); + EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n" + << original << std::endl; + if (!context()) { + return std::make_tuple(std::vector(), Pass::Status::Failure); + } + + context()->set_preserve_bindings(OptimizerOptions()->preserve_bindings_); + context()->set_preserve_spec_constants( + OptimizerOptions()->preserve_spec_constants_); + + const auto status = pass->Run(context()); + + std::vector binary; + if (status != Pass::Status::Failure) { + context()->module()->ToBinary(&binary, skip_nop); + } + return std::make_tuple(binary, status); + } + + // Runs a single pass of class |PassT| on the binary assembled from the + // |assembly|. Returns a tuple of the optimized binary and the boolean value + // from the pass Process() function. + template + std::tuple, Pass::Status> SinglePassRunToBinary( + const std::string& assembly, bool skip_nop, Args&&... args) { + auto pass = MakeUnique(std::forward(args)...); + pass->SetMessageConsumer(consumer_); + return OptimizeToBinary(pass.get(), assembly, skip_nop); + } + + // Runs a single pass of class |PassT| on the binary assembled from the + // |assembly|, disassembles the optimized binary. Returns a tuple of + // disassembly string and the boolean value from the pass Process() function. + template + std::tuple SinglePassRunAndDisassemble( + const std::string& assembly, bool skip_nop, bool do_validation, + Args&&... args) { + std::vector optimized_bin; + auto status = Pass::Status::SuccessWithoutChange; + std::tie(optimized_bin, status) = SinglePassRunToBinary( + assembly, skip_nop, std::forward(args)...); + if (do_validation) { + spv_context spvContext = spvContextCreate(env_); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()}; + spv_result_t error = spvValidateWithOptions( + spvContext, ValidatorOptions(), &binary, &diagnostic); + EXPECT_EQ(error, 0); + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + } + std::string optimized_asm; + SpirvTools tools(env_); + EXPECT_TRUE( + tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options_)) + << "Disassembling failed for shader:\n" + << assembly << std::endl; + return std::make_tuple(optimized_asm, status); + } + + // Runs a single pass of class |PassT| on the binary assembled from the + // |original| assembly, and checks whether the optimized binary can be + // disassembled to the |expected| assembly. Optionally will also validate + // the optimized binary. This does *not* involve pass manager. Callers + // are suggested to use SCOPED_TRACE() for better messages. + template + void SinglePassRunAndCheck(const std::string& original, + const std::string& expected, bool skip_nop, + bool do_validation, Args&&... args) { + std::vector optimized_bin; + auto status = Pass::Status::SuccessWithoutChange; + std::tie(optimized_bin, status) = SinglePassRunToBinary( + original, skip_nop, std::forward(args)...); + // Check whether the pass returns the correct modification indication. + EXPECT_NE(Pass::Status::Failure, status); + EXPECT_EQ(original == expected, + status == Pass::Status::SuccessWithoutChange); + if (do_validation) { + spv_context spvContext = spvContextCreate(env_); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {optimized_bin.data(), optimized_bin.size()}; + spv_result_t error = spvValidateWithOptions( + spvContext, ValidatorOptions(), &binary, &diagnostic); + EXPECT_EQ(error, 0); + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + } + std::string optimized_asm; + SpirvTools tools(env_); + EXPECT_TRUE( + tools.Disassemble(optimized_bin, &optimized_asm, disassemble_options_)) + << "Disassembling failed for shader:\n" + << original << std::endl; + EXPECT_EQ(expected, optimized_asm); + } + + // Runs a single pass of class |PassT| on the binary assembled from the + // |original| assembly, and checks whether the optimized binary can be + // disassembled to the |expected| assembly. This does *not* involve pass + // manager. Callers are suggested to use SCOPED_TRACE() for better messages. + template + void SinglePassRunAndCheck(const std::string& original, + const std::string& expected, bool skip_nop, + Args&&... args) { + SinglePassRunAndCheck(original, expected, skip_nop, false, + std::forward(args)...); + } + + // Runs a single pass of class |PassT| on the binary assembled from the + // |original| assembly, then runs an Effcee matcher over the disassembled + // result, using checks parsed from |original|. Always skips OpNop. + // This does *not* involve pass manager. Callers are suggested to use + // SCOPED_TRACE() for better messages. + // Returns a tuple of disassembly string and the boolean value from the pass + // Process() function. + template + std::tuple SinglePassRunAndMatch( + const std::string& original, bool do_validation, Args&&... args) { + const bool skip_nop = true; + auto pass_result = SinglePassRunAndDisassemble( + original, skip_nop, do_validation, std::forward(args)...); + auto disassembly = std::get<0>(pass_result); + auto match_result = effcee::Match(disassembly, original); + EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) + << match_result.message() << "\nChecking result:\n" + << disassembly; + return pass_result; + } + + // Runs a single pass of class |PassT| on the binary assembled from the + // |original| assembly. Check for failure and expect an Effcee matcher + // to pass when run on the diagnostic messages. This does *not* involve + // pass manager. Callers are suggested to use SCOPED_TRACE() for better + // messages. + template + void SinglePassRunAndFail(const std::string& original, Args&&... args) { + context_ = BuildModule(env_, consumer_, original, assemble_options_); + EXPECT_NE(nullptr, context()) << "Assembling failed for shader:\n" + << original << std::endl; + std::ostringstream errs; + auto error_consumer = [&errs](spv_message_level_t, const char*, + const spv_position_t&, const char* message) { + errs << message << std::endl; + }; + auto pass = MakeUnique(std::forward(args)...); + pass->SetMessageConsumer(error_consumer); + const auto status = pass->Run(context()); + EXPECT_EQ(Pass::Status::Failure, status); + auto match_result = effcee::Match(errs.str(), original); + EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) + << match_result.message() << "\nChecking messages:\n" + << errs.str(); + } + + // Adds a pass to be run. + template + void AddPass(Args&&... args) { + manager_->AddPass(std::forward(args)...); + } + + // Renews the pass manager, including clearing all previously added passes. + void RenewPassManger() { + manager_ = MakeUnique(); + manager_->SetMessageConsumer(consumer_); + } + + // Runs the passes added thus far using a pass manager on the binary assembled + // from the |original| assembly, and checks whether the optimized binary can + // be disassembled to the |expected| assembly. Callers are suggested to use + // SCOPED_TRACE() for better messages. + void RunAndCheck(const std::string& original, const std::string& expected) { + assert(manager_->NumPasses()); + + context_ = BuildModule(env_, nullptr, original, assemble_options_); + ASSERT_NE(nullptr, context()); + + context()->set_preserve_bindings(OptimizerOptions()->preserve_bindings_); + context()->set_preserve_spec_constants( + OptimizerOptions()->preserve_spec_constants_); + + auto status = manager_->Run(context()); + EXPECT_NE(status, Pass::Status::Failure); + + if (status != Pass::Status::Failure) { + std::vector binary; + context()->module()->ToBinary(&binary, /* skip_nop = */ false); + + std::string optimized; + SpirvTools tools(env_); + EXPECT_TRUE(tools.Disassemble(binary, &optimized, disassemble_options_)); + EXPECT_EQ(expected, optimized); + } + } + + void SetAssembleOptions(uint32_t assemble_options) { + assemble_options_ = assemble_options; + } + + void SetDisassembleOptions(uint32_t disassemble_options) { + disassemble_options_ = disassemble_options; + } + + MessageConsumer consumer() { return consumer_; } + IRContext* context() { return context_.get(); } + + void SetMessageConsumer(MessageConsumer msg_consumer) { + consumer_ = msg_consumer; + } + + spv_optimizer_options OptimizerOptions() { return &optimizer_options_; } + + spv_validator_options ValidatorOptions() { return &validator_options_; } + + void SetTargetEnv(spv_target_env env) { env_ = env; } + + private: + MessageConsumer consumer_; // Message consumer. + std::unique_ptr context_; // IR context + std::unique_ptr manager_; // The pass manager. + uint32_t assemble_options_; + uint32_t disassemble_options_; + spv_optimizer_options_t optimizer_options_; + spv_validator_options_t validator_options_; + spv_target_env env_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // TEST_OPT_PASS_FIXTURE_H_ diff --git a/third_party/spirv-tools/test/opt/pass_manager_test.cpp b/third_party/spirv-tools/test/opt/pass_manager_test.cpp new file mode 100644 index 0000000..22d5e22 --- /dev/null +++ b/third_party/spirv-tools/test/opt/pass_manager_test.cpp @@ -0,0 +1,191 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/util/make_unique.h" +#include "test/opt/module_utils.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using spvtest::GetIdBound; +using ::testing::Eq; + +// A null pass whose construtors accept arguments +class NullPassWithArgs : public NullPass { + public: + NullPassWithArgs(uint32_t) {} + NullPassWithArgs(std::string) {} + NullPassWithArgs(const std::vector&) {} + NullPassWithArgs(const std::vector&, uint32_t) {} + + const char* name() const override { return "null-with-args"; } +}; + +TEST(PassManager, Interface) { + PassManager manager; + EXPECT_EQ(0u, manager.NumPasses()); + + manager.AddPass(); + EXPECT_EQ(1u, manager.NumPasses()); + EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); + + manager.AddPass(MakeUnique()); + EXPECT_EQ(2u, manager.NumPasses()); + EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); + EXPECT_STREQ("null", manager.GetPass(1)->name()); + + manager.AddPass(); + EXPECT_EQ(3u, manager.NumPasses()); + EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); + EXPECT_STREQ("null", manager.GetPass(1)->name()); + EXPECT_STREQ("strip-debug", manager.GetPass(2)->name()); + + manager.AddPass(1u); + manager.AddPass("null pass args"); + manager.AddPass(std::initializer_list{1, 2}); + manager.AddPass(std::initializer_list{1, 2}, 3); + EXPECT_EQ(7u, manager.NumPasses()); + EXPECT_STREQ("strip-debug", manager.GetPass(0)->name()); + EXPECT_STREQ("null", manager.GetPass(1)->name()); + EXPECT_STREQ("strip-debug", manager.GetPass(2)->name()); + EXPECT_STREQ("null-with-args", manager.GetPass(3)->name()); + EXPECT_STREQ("null-with-args", manager.GetPass(4)->name()); + EXPECT_STREQ("null-with-args", manager.GetPass(5)->name()); + EXPECT_STREQ("null-with-args", manager.GetPass(6)->name()); +} + +// A pass that appends an OpNop instruction to the debug1 section. +class AppendOpNopPass : public Pass { + public: + const char* name() const override { return "AppendOpNop"; } + Status Process() override { + context()->AddDebug1Inst(MakeUnique(context())); + return Status::SuccessWithChange; + } +}; + +// A pass that appends specified number of OpNop instructions to the debug1 +// section. +class AppendMultipleOpNopPass : public Pass { + public: + explicit AppendMultipleOpNopPass(uint32_t num_nop) : num_nop_(num_nop) {} + + const char* name() const override { return "AppendOpNop"; } + Status Process() override { + for (uint32_t i = 0; i < num_nop_; i++) { + context()->AddDebug1Inst(MakeUnique(context())); + } + return Status::SuccessWithChange; + } + + private: + uint32_t num_nop_; +}; + +// A pass that duplicates the last instruction in the debug1 section. +class DuplicateInstPass : public Pass { + public: + const char* name() const override { return "DuplicateInst"; } + Status Process() override { + auto inst = MakeUnique(*(--context()->debug1_end())); + context()->AddDebug1Inst(std::move(inst)); + return Status::SuccessWithChange; + } +}; + +using PassManagerTest = PassTest<::testing::Test>; + +TEST_F(PassManagerTest, Run) { + const std::string text = "OpMemoryModel Logical GLSL450\nOpSource ESSL 310\n"; + + AddPass(); + AddPass(); + RunAndCheck(text, text + "OpNop\nOpNop\n"); + + RenewPassManger(); + AddPass(); + AddPass(); + RunAndCheck(text, text + "OpNop\nOpNop\n"); + + RenewPassManger(); + AddPass(); + AddPass(); + RunAndCheck(text, text + "OpSource ESSL 310\nOpNop\n"); + + RenewPassManger(); + AddPass(3); + RunAndCheck(text, text + "OpNop\nOpNop\nOpNop\n"); +} + +// A pass that appends an OpTypeVoid instruction that uses a given id. +class AppendTypeVoidInstPass : public Pass { + public: + explicit AppendTypeVoidInstPass(uint32_t result_id) : result_id_(result_id) {} + + const char* name() const override { return "AppendTypeVoidInstPass"; } + Status Process() override { + auto inst = MakeUnique(context(), SpvOpTypeVoid, 0, result_id_, + std::vector{}); + context()->AddType(std::move(inst)); + return Status::SuccessWithChange; + } + + private: + uint32_t result_id_; +}; + +TEST(PassManager, RecomputeIdBoundAutomatically) { + PassManager manager; + std::unique_ptr module(new Module()); + IRContext context(SPV_ENV_UNIVERSAL_1_2, std::move(module), + manager.consumer()); + EXPECT_THAT(GetIdBound(*context.module()), Eq(0u)); + + manager.Run(&context); + manager.AddPass(); + // With no ID changes, the ID bound does not change. + EXPECT_THAT(GetIdBound(*context.module()), Eq(0u)); + + // Now we force an Id of 100 to be used. + manager.AddPass(MakeUnique(100)); + EXPECT_THAT(GetIdBound(*context.module()), Eq(0u)); + manager.Run(&context); + // The Id has been updated automatically, even though the pass + // did not update it. + EXPECT_THAT(GetIdBound(*context.module()), Eq(101u)); + + // Try one more time! + manager.AddPass(MakeUnique(200)); + manager.Run(&context); + EXPECT_THAT(GetIdBound(*context.module()), Eq(201u)); + + // Add another pass, but which uses a lower Id. + manager.AddPass(MakeUnique(10)); + manager.Run(&context); + // The Id stays high. + EXPECT_THAT(GetIdBound(*context.module()), Eq(201u)); +} + +} // anonymous namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/pass_merge_return_test.cpp b/third_party/spirv-tools/test/opt/pass_merge_return_test.cpp new file mode 100644 index 0000000..fd97efa --- /dev/null +++ b/third_party/spirv-tools/test/opt/pass_merge_return_test.cpp @@ -0,0 +1,2572 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv-tools/optimizer.hpp" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using MergeReturnPassTest = PassTest<::testing::Test>; + +TEST_F(MergeReturnPassTest, OneReturn) { + const std::string before = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %1 "simple_kernel" +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = before; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(MergeReturnPassTest, TwoReturnsNoValue) { + const std::string before = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %6 "simple_kernel" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpBranchConditional %4 %8 %9 +%8 = OpLabel +OpReturn +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %6 "simple_kernel" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpBranchConditional %4 %8 %9 +%8 = OpLabel +OpBranch %10 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(MergeReturnPassTest, DebugTwoReturnsNoValue) { + const std::string before = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +%10 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %6 "simple_kernel" +%11 = OpString "test" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%12 = OpExtInst %2 %10 DebugSource %11 +%13 = OpExtInst %2 %10 DebugCompilationUnit 1 4 %12 HLSL +%14 = OpExtInst %2 %10 DebugTypeFunction FlagIsProtected|FlagIsPrivate %2 +%15 = OpExtInst %2 %10 DebugFunction %11 %14 %12 0 0 %13 %11 FlagIsProtected|FlagIsPrivate 0 %6 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpBranchConditional %4 %8 %9 +%8 = OpLabel +%16 = OpExtInst %2 %10 DebugScope %15 +OpLine %11 100 0 +OpReturn +%9 = OpLabel +%17 = OpExtInst %2 %10 DebugScope %13 +OpLine %11 200 0 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +%10 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %6 "simple_kernel" +%11 = OpString "test" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%12 = OpExtInst %2 %10 DebugSource %11 +%13 = OpExtInst %2 %10 DebugCompilationUnit 1 4 %12 HLSL +%14 = OpExtInst %2 %10 DebugTypeFunction FlagIsProtected|FlagIsPrivate %2 +%15 = OpExtInst %2 %10 DebugFunction %11 %14 %12 0 0 %13 %11 FlagIsProtected|FlagIsPrivate 0 %6 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpBranchConditional %4 %8 %9 +%8 = OpLabel +%19 = OpExtInst %2 %10 DebugScope %15 +OpLine %11 100 0 +OpBranch %18 +%20 = OpExtInst %2 %10 DebugNoScope +%9 = OpLabel +%21 = OpExtInst %2 %10 DebugScope %13 +OpLine %11 200 0 +OpBranch %18 +%22 = OpExtInst %2 %10 DebugNoScope +%18 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(MergeReturnPassTest, TwoReturnsWithValues) { + const std::string before = + R"(OpCapability Linkage +OpCapability Kernel +OpMemoryModel Logical OpenCL +OpDecorate %7 LinkageAttributes "simple_kernel" Export +%1 = OpTypeInt 32 0 +%2 = OpTypeBool +%3 = OpConstantFalse %2 +%4 = OpConstant %1 0 +%5 = OpConstant %1 1 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +OpBranchConditional %3 %9 %10 +%9 = OpLabel +OpReturnValue %4 +%10 = OpLabel +OpReturnValue %5 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Linkage +OpCapability Kernel +OpMemoryModel Logical OpenCL +OpDecorate %7 LinkageAttributes "simple_kernel" Export +%1 = OpTypeInt 32 0 +%2 = OpTypeBool +%3 = OpConstantFalse %2 +%4 = OpConstant %1 0 +%5 = OpConstant %1 1 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +OpBranchConditional %3 %9 %10 +%9 = OpLabel +OpBranch %11 +%10 = OpLabel +OpBranch %11 +%11 = OpLabel +%12 = OpPhi %1 %4 %9 %5 %10 +OpReturnValue %12 +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(MergeReturnPassTest, UnreachableReturnsNoValue) { + const std::string before = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %6 "simple_kernel" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpReturn +%8 = OpLabel +OpBranchConditional %4 %9 %10 +%9 = OpLabel +OpReturn +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %6 "simple_kernel" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpBranch %11 +%8 = OpLabel +OpBranchConditional %4 %9 %10 +%9 = OpLabel +OpBranch %11 +%10 = OpLabel +OpBranch %11 +%11 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(MergeReturnPassTest, UnreachableReturnsWithValues) { + const std::string before = + R"(OpCapability Linkage +OpCapability Kernel +OpMemoryModel Logical OpenCL +OpDecorate %7 LinkageAttributes "simple_kernel" Export +%1 = OpTypeInt 32 0 +%2 = OpTypeBool +%3 = OpConstantFalse %2 +%4 = OpConstant %1 0 +%5 = OpConstant %1 1 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +%9 = OpIAdd %1 %4 %5 +OpReturnValue %9 +%10 = OpLabel +OpBranchConditional %3 %11 %12 +%11 = OpLabel +OpReturnValue %4 +%12 = OpLabel +OpReturnValue %5 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Linkage +OpCapability Kernel +OpMemoryModel Logical OpenCL +OpDecorate %7 LinkageAttributes "simple_kernel" Export +%1 = OpTypeInt 32 0 +%2 = OpTypeBool +%3 = OpConstantFalse %2 +%4 = OpConstant %1 0 +%5 = OpConstant %1 1 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +%9 = OpIAdd %1 %4 %5 +OpBranch %13 +%10 = OpLabel +OpBranchConditional %3 %11 %12 +%11 = OpLabel +OpBranch %13 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +%14 = OpPhi %1 %9 %8 %4 %11 %5 %12 +OpReturnValue %14 +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(MergeReturnPassTest, DebugUnreachableReturnsWithValues) { + const std::string before = + R"(OpCapability Linkage +OpCapability Kernel +%13 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical OpenCL +%14 = OpString "test" +OpDecorate %7 LinkageAttributes "simple_kernel" Export +%1 = OpTypeInt 32 0 +%20 = OpTypeVoid +%2 = OpTypeBool +%3 = OpConstantFalse %2 +%4 = OpConstant %1 0 +%5 = OpConstant %1 1 +%6 = OpTypeFunction %1 +%15 = OpExtInst %20 %13 DebugSource %14 +%16 = OpExtInst %20 %13 DebugCompilationUnit 1 4 %15 HLSL +%17 = OpExtInst %20 %13 DebugTypeFunction FlagIsProtected|FlagIsPrivate %20 +%18 = OpExtInst %20 %13 DebugFunction %14 %17 %15 0 0 %16 %14 FlagIsProtected|FlagIsPrivate 0 %7 +%7 = OpFunction %1 None %6 +%8 = OpLabel +%9 = OpIAdd %1 %4 %5 +%19 = OpExtInst %20 %13 DebugScope %18 +OpLine %14 100 0 +OpReturnValue %9 +%10 = OpLabel +OpBranchConditional %3 %11 %12 +%11 = OpLabel +%21 = OpExtInst %20 %13 DebugScope %16 +OpLine %14 200 0 +OpReturnValue %4 +%12 = OpLabel +%22 = OpExtInst %20 %13 DebugScope %18 +OpLine %14 300 0 +OpReturnValue %5 +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Linkage +OpCapability Kernel +%13 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical OpenCL +%14 = OpString "test" +OpDecorate %7 LinkageAttributes "simple_kernel" Export +%1 = OpTypeInt 32 0 +%20 = OpTypeVoid +%2 = OpTypeBool +%3 = OpConstantFalse %2 +%4 = OpConstant %1 0 +%5 = OpConstant %1 1 +%6 = OpTypeFunction %1 +%15 = OpExtInst %20 %13 DebugSource %14 +%16 = OpExtInst %20 %13 DebugCompilationUnit 1 4 %15 HLSL +%17 = OpExtInst %20 %13 DebugTypeFunction FlagIsProtected|FlagIsPrivate %20 +%18 = OpExtInst %20 %13 DebugFunction %14 %17 %15 0 0 %16 %14 FlagIsProtected|FlagIsPrivate 0 %7 +%7 = OpFunction %1 None %6 +%8 = OpLabel +%9 = OpIAdd %1 %4 %5 +%25 = OpExtInst %20 %13 DebugScope %18 +OpLine %14 100 0 +OpBranch %23 +%26 = OpExtInst %20 %13 DebugNoScope +%10 = OpLabel +OpBranchConditional %3 %11 %12 +%11 = OpLabel +%27 = OpExtInst %20 %13 DebugScope %16 +OpLine %14 200 0 +OpBranch %23 +%28 = OpExtInst %20 %13 DebugNoScope +%12 = OpLabel +%29 = OpExtInst %20 %13 DebugScope %18 +OpLine %14 300 0 +OpBranch %23 +%30 = OpExtInst %20 %13 DebugNoScope +%23 = OpLabel +%24 = OpPhi %1 %9 %8 %4 %11 %5 %12 +OpReturnValue %24 +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(MergeReturnPassTest, StructuredControlFlowWithUnreachableMerge) { + const std::string before = + R"( +; CHECK: [[false:%\w+]] = OpConstantFalse +; CHECK: [[true:%\w+]] = OpConstantTrue +; CHECK: OpFunction +; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]] +; CHECK: OpSelectionMerge [[return_block:%\w+]] +; CHECK: OpSelectionMerge [[merge_lab:%\w+]] +; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]] +; CHECK: [[if_lab]] = OpLabel +; CHECK-NEXT: OpStore [[var]] [[true]] +; CHECK-NEXT: OpBranch [[return_block]] +; CHECK: [[then_lab]] = OpLabel +; CHECK-NEXT: OpStore [[var]] [[true]] +; CHECK-NEXT: OpBranch [[return_block]] +; CHECK: [[merge_lab]] = OpLabel +; CHECK-NEXT: OpBranch [[return_block]] +; CHECK: [[return_block]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %6 "simple_shader" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %4 %8 %9 +%8 = OpLabel +OpReturn +%9 = OpLabel +OpReturn +%10 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, DebugStructuredControlFlowWithUnreachableMerge) { + const std::string before = + R"( +; CHECK: [[false:%\w+]] = OpConstantFalse +; CHECK: [[true:%\w+]] = OpConstantTrue +; CHECK: OpFunction +; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]] +; CHECK: OpSelectionMerge [[return_block:%\w+]] +; CHECK: OpSelectionMerge [[merge_lab:%\w+]] +; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]] +; CHECK: [[if_lab]] = OpLabel +; CHECK-NEXT: OpStore [[var]] [[true]] +; CHECK-NEXT: DebugScope +; CHECK-NEXT: OpLine {{%\d+}} 100 0 +; CHECK-NEXT: OpBranch [[return_block]] +; CHECK: [[then_lab]] = OpLabel +; CHECK-NEXT: OpStore [[var]] [[true]] +; CHECK-NEXT: DebugScope +; CHECK-NEXT: OpLine {{%\d+}} 200 0 +; CHECK-NEXT: OpBranch [[return_block]] +; CHECK: [[merge_lab]] = OpLabel +; CHECK-NEXT: DebugScope +; CHECK-NEXT: OpLine {{%\d+}} 300 0 +; CHECK-NEXT: OpBranch [[return_block]] +; CHECK: [[return_block]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Addresses +OpCapability Shader +OpCapability Linkage +%12 = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %6 "simple_shader" +%11 = OpString "test" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%13 = OpExtInst %2 %12 DebugSource %11 +%14 = OpExtInst %2 %12 DebugCompilationUnit 1 4 %13 HLSL +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %4 %8 %9 +%8 = OpLabel +%15 = OpExtInst %2 %12 DebugScope %14 +OpLine %11 100 0 +OpReturn +%9 = OpLabel +%16 = OpExtInst %2 %12 DebugScope %14 +OpLine %11 200 0 +OpReturn +%10 = OpLabel +%17 = OpExtInst %2 %12 DebugScope %14 +OpLine %11 300 0 +OpUnreachable +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, StructuredControlFlowAddPhi) { + const std::string before = + R"( +; CHECK: [[false:%\w+]] = OpConstantFalse +; CHECK: [[true:%\w+]] = OpConstantTrue +; CHECK: OpFunction +; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]] +; CHECK: OpSelectionMerge [[single_case_switch_merge:%\w+]] +; CHECK: OpSelectionMerge [[merge_lab:%\w+]] +; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]] +; CHECK: [[if_lab]] = OpLabel +; CHECK-NEXT: [[add:%\w+]] = OpIAdd [[type:%\w+]] +; CHECK-NEXT: OpBranch +; CHECK: [[then_lab]] = OpLabel +; CHECK-NEXT: OpStore [[var]] [[true]] +; CHECK-NEXT: OpBranch [[single_case_switch_merge]] +; CHECK: [[merge_lab]] = OpLabel +; CHECK: [[single_case_switch_merge]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %6 "simple_shader" +%2 = OpTypeVoid +%3 = OpTypeBool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %4 %8 %9 +%8 = OpLabel +%11 = OpIAdd %int %int_0 %int_0 +OpBranch %10 +%9 = OpLabel +OpReturn +%10 = OpLabel +%12 = OpIAdd %int %11 %11 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, StructuredControlDecoration) { + const std::string before = + R"( +; CHECK: OpDecorate [[dec_id:%\w+]] RelaxedPrecision +; CHECK: [[false:%\w+]] = OpConstantFalse +; CHECK: [[true:%\w+]] = OpConstantTrue +; CHECK: OpFunction +; CHECK: [[var:%\w+]] = OpVariable [[:%\w+]] Function [[false]] +; CHECK: OpSelectionMerge [[return_block:%\w+]] +; CHECK: OpSelectionMerge [[merge_lab:%\w+]] +; CHECK: OpBranchConditional [[cond:%\w+]] [[if_lab:%\w+]] [[then_lab:%\w+]] +; CHECK: [[if_lab]] = OpLabel +; CHECK-NEXT: [[dec_id]] = OpIAdd [[type:%\w+]] +; CHECK-NEXT: OpBranch +; CHECK: [[then_lab]] = OpLabel +; CHECK-NEXT: OpStore [[var]] [[true]] +; CHECK-NEXT: OpBranch [[return_block]] +; CHECK: [[merge_lab]] = OpLabel +; CHECK-NEXT: OpStore [[var]] [[true]] +; CHECK-NEXT: OpBranch [[return_block]] +; CHECK: [[return_block]] = OpLabel +; CHECK-NEXT: OpReturn +OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %6 "simple_shader" +OpDecorate %11 RelaxedPrecision +%2 = OpTypeVoid +%3 = OpTypeBool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %4 %8 %9 +%8 = OpLabel +%11 = OpIAdd %int %int_0 %int_0 +OpBranch %10 +%9 = OpLabel +OpReturn +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, SplitBlockUsedInPhi) { + const std::string before = + R"( +; CHECK: OpFunction +; CHECK: OpSelectionMerge [[single_case_switch_merge:%\w+]] +; CHECK: OpLoopMerge [[loop_merge:%\w+]] +; CHECK: [[loop_merge]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[single_case_switch_merge]] [[old_code_path:%\w+]] +; CHECK: [[old_code_path:%\w+]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[side_node:%\w+]] [[phi_block:%\w+]] +; CHECK: [[phi_block]] = OpLabel +; CHECK-NEXT: OpPhi %bool %false [[side_node]] %true [[old_code_path]] + OpCapability Addresses + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "simple_shader" + %void = OpTypeVoid + %bool = OpTypeBool + %false = OpConstantFalse %bool + %true = OpConstantTrue %bool + %6 = OpTypeFunction %void + %1 = OpFunction %void None %6 + %7 = OpLabel + OpLoopMerge %merge %cont None + OpBranchConditional %false %9 %merge + %9 = OpLabel + OpReturn + %cont = OpLabel + OpBranch %7 + %merge = OpLabel + OpSelectionMerge %merge2 None + OpBranchConditional %false %if %merge2 + %if = OpLabel + OpBranch %merge2 + %merge2 = OpLabel + %12 = OpPhi %bool %false %if %true %merge + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, DebugSplitBlockUsedInPhi) { + const std::string before = + R"( +; CHECK: DebugScope +; CHECK-NEXT: OpLine {{%\d+}} 100 0 +; CHECK: OpLoopMerge + +; CHECK: OpStore [[return_in_loop:%\w+]] %true +; CHECK-NEXT: DebugScope +; CHECK-NEXT: OpLine {{%\d+}} 200 0 +; CHECK-NEXT: OpBranch [[check_early_return:%\w+]] + +; CHECK: [[check_early_return]] = OpLabel +; CHECK-NEXT: [[early_return:%\w+]] = OpLoad %bool [[return_in_loop]] +; CHECK-NEXT: OpSelectionMerge [[not_early_return:%\w+]] None +; CHECK-NEXT: OpBranchConditional [[early_return]] {{%\d+}} [[not_early_return]] + +; CHECK: [[not_early_return]] = OpLabel +; CHECK-NEXT: DebugScope +; CHECK-NEXT: OpLine {{%\d+}} 400 0 +; CHECK: OpSelectionMerge [[merge2:%\w+]] None + +; CHECK: [[merge2]] = OpLabel +; CHECK-NEXT: DebugScope +; CHECK-NEXT: OpLine {{%\d+}} 600 0 +; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool %false {{%\d+}} %true [[not_early_return]] +; CHECK-NEXT: DebugValue {{%\d+}} [[phi]] + + OpCapability Addresses + OpCapability Shader + OpCapability Linkage + %ext = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "simple_shader" + %tn = OpString "test" + %void = OpTypeVoid + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_8 = OpConstant %uint 8 + %false = OpConstantFalse %bool + %true = OpConstantTrue %bool + %6 = OpTypeFunction %void + %src = OpExtInst %void %ext DebugSource %tn + %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL + %ty = OpExtInst %void %ext DebugTypeBasic %tn %uint_8 Boolean + %v = OpExtInst %void %ext DebugLocalVariable %tn %ty %src 0 0 %cu FlagIsLocal + %expr = OpExtInst %void %ext DebugExpression + %1 = OpFunction %void None %6 + %7 = OpLabel + %s0 = OpExtInst %void %ext DebugScope %cu + OpLine %tn 100 0 + OpLoopMerge %merge %cont None + OpBranchConditional %false %9 %merge + %9 = OpLabel + %s1 = OpExtInst %void %ext DebugScope %cu + OpLine %tn 200 0 + OpReturn + %cont = OpLabel + %s2 = OpExtInst %void %ext DebugScope %cu + OpLine %tn 300 0 + OpBranch %7 + %merge = OpLabel + %s3 = OpExtInst %void %ext DebugScope %cu + OpLine %tn 400 0 + OpSelectionMerge %merge2 None + OpBranchConditional %false %if %merge2 + %if = OpLabel + %s4 = OpExtInst %void %ext DebugScope %cu + OpLine %tn 500 0 + OpBranch %merge2 + %merge2 = OpLabel + %s5 = OpExtInst %void %ext DebugScope %cu + OpLine %tn 600 0 + %12 = OpPhi %bool %false %if %true %merge + %dv = OpExtInst %void %ext DebugValue %v %12 %expr + OpLine %tn 900 0 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, false); +} + +// TODO(#1861): Reenable these test when the breaks from selection constructs +// are reenabled. +/* +TEST_F(MergeReturnPassTest, UpdateOrderWhenPredicating) { + const std::string before = + R"( +; CHECK: OpFunction +; CHECK: OpFunction +; CHECK: OpSelectionMerge [[m1:%\w+]] None +; CHECK-NOT: OpReturn +; CHECK: [[m1]] = OpLabel +; CHECK: OpSelectionMerge [[m2:%\w+]] None +; CHECK: OpSelectionMerge [[m3:%\w+]] None +; CHECK: OpSelectionMerge [[m4:%\w+]] None +; CHECK: OpLabel +; CHECK-NEXT: OpStore +; CHECK-NEXT: OpBranch [[m4]] +; CHECK: [[m4]] = OpLabel +; CHECK-NEXT: [[ld4:%\w+]] = OpLoad %bool +; CHECK-NEXT: OpBranchConditional [[ld4]] [[m3]] +; CHECK: [[m3]] = OpLabel +; CHECK-NEXT: [[ld3:%\w+]] = OpLoad %bool +; CHECK-NEXT: OpBranchConditional [[ld3]] [[m2]] +; CHECK: [[m2]] = OpLabel + OpCapability SampledBuffer + OpCapability StorageImageExtendedFormats + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "PS_DebugTiles" + OpExecutionMode %1 OriginUpperLeft + OpSource HLSL 600 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool + %1 = OpFunction %void None %3 + %5 = OpLabel + %6 = OpFunctionCall %void %7 + OpReturn + OpFunctionEnd + %7 = OpFunction %void None %3 + %8 = OpLabel + %9 = OpUndef %bool + OpSelectionMerge %10 None + OpBranchConditional %9 %11 %10 + %11 = OpLabel + OpReturn + %10 = OpLabel + %12 = OpUndef %bool + OpSelectionMerge %13 None + OpBranchConditional %12 %14 %15 + %15 = OpLabel + %16 = OpUndef %bool + OpSelectionMerge %17 None + OpBranchConditional %16 %18 %17 + %18 = OpLabel + OpReturn + %17 = OpLabel + OpBranch %13 + %14 = OpLabel + OpReturn + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, false); +} +*/ + +TEST_F(MergeReturnPassTest, StructuredControlFlowBothMergeAndHeader) { + const std::string test = + R"( +; CHECK: OpFunction +; CHECK: [[ret_flag:%\w+]] = OpVariable %_ptr_Function_bool Function %false +; CHECK: OpSelectionMerge [[single_case_switch_merge:%\w+]] +; CHECK: OpLoopMerge [[loop1_merge:%\w+]] {{%\w+}} +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[if_lab:%\w+]] {{%\w+}} +; CHECK: [[if_lab]] = OpLabel +; CHECK: OpStore [[ret_flag]] %true +; CHECK-NEXT: OpBranch [[loop1_merge]] +; CHECK: [[loop1_merge]] = OpLabel +; CHECK-NEXT: [[ld:%\w+]] = OpLoad %bool [[ret_flag]] +; CHECK-NOT: OpLabel +; CHECK: OpBranchConditional [[ld]] [[single_case_switch_merge]] [[empty_block:%\w+]] +; CHECK: [[empty_block]] = OpLabel +; CHECK-NEXT: OpBranch [[loop2:%\w+]] +; CHECK: [[loop2]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpLoopMerge + OpCapability Addresses + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "simple_shader" + %void = OpTypeVoid + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %false = OpConstantFalse %bool + %7 = OpTypeFunction %void + %1 = OpFunction %void None %7 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranchConditional %false %12 %13 + %12 = OpLabel + OpReturn + %13 = OpLabel + OpBranch %10 + %11 = OpLabel + OpBranch %9 + %10 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %16 = OpIAdd %uint %uint_0 %uint_0 + OpBranchConditional %false %10 %14 + %14 = OpLabel + %17 = OpIAdd %uint %16 %16 + OpReturn + OpFunctionEnd + +)"; + + const std::string after = + R"(OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "simple_shader" +%void = OpTypeVoid +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%false = OpConstantFalse %bool +%7 = OpTypeFunction %void +%_ptr_Function_bool = OpTypePointer Function %bool +%true = OpConstantTrue %bool +%1 = OpFunction %void None %7 +%8 = OpLabel +%18 = OpVariable %_ptr_Function_bool Function %false +OpSelectionMerge %9 None +OpBranchConditional %false %10 %11 +%10 = OpLabel +OpStore %18 %true +OpBranch %9 +%11 = OpLabel +OpBranch %9 +%9 = OpLabel +%23 = OpLoad %bool %18 +OpSelectionMerge %22 None +OpBranchConditional %23 %22 %21 +%21 = OpLabel +OpBranch %20 +%20 = OpLabel +OpLoopMerge %12 %13 None +OpBranch %13 +%13 = OpLabel +%14 = OpIAdd %uint %uint_0 %uint_0 +OpBranchConditional %false %20 %12 +%12 = OpLabel +%15 = OpIAdd %uint %14 %14 +OpStore %18 %true +OpBranch %22 +%22 = OpLabel +OpBranch %16 +%16 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(test, false); +} + +// TODO(#1861): Reenable these test when the breaks from selection constructs +// are reenabled. +/* +TEST_F(MergeReturnPassTest, NestedSelectionMerge) { + const std::string before = + R"( + OpCapability Addresses + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "simple_shader" + %void = OpTypeVoid + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %false = OpConstantFalse %bool + %7 = OpTypeFunction %void + %1 = OpFunction %void None %7 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %false %10 %11 + %10 = OpLabel + OpReturn + %11 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %false %13 %14 + %13 = OpLabel + %15 = OpIAdd %uint %uint_0 %uint_0 + OpBranch %12 + %14 = OpLabel + OpReturn + %12 = OpLabel + OpBranch %9 + %9 = OpLabel + %16 = OpIAdd %uint %15 %15 + OpReturn + OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "simple_shader" +%void = OpTypeVoid +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%false = OpConstantFalse %bool +%7 = OpTypeFunction %void +%_ptr_Function_bool = OpTypePointer Function %bool +%true = OpConstantTrue %bool +%26 = OpUndef %uint +%1 = OpFunction %void None %7 +%8 = OpLabel +%19 = OpVariable %_ptr_Function_bool Function %false +OpSelectionMerge %9 None +OpBranchConditional %false %10 %11 +%10 = OpLabel +OpStore %19 %true +OpBranch %9 +%11 = OpLabel +OpSelectionMerge %12 None +OpBranchConditional %false %13 %14 +%13 = OpLabel +%15 = OpIAdd %uint %uint_0 %uint_0 +OpBranch %12 +%14 = OpLabel +OpStore %19 %true +OpBranch %12 +%12 = OpLabel +%27 = OpPhi %uint %15 %13 %26 %14 +%22 = OpLoad %bool %19 +OpBranchConditional %22 %9 %21 +%21 = OpLabel +OpBranch %9 +%9 = OpLabel +%28 = OpPhi %uint %27 %21 %26 %10 %26 %12 +%25 = OpLoad %bool %19 +OpSelectionMerge %24 None +OpBranchConditional %25 %24 %23 +%23 = OpLabel +%16 = OpIAdd %uint %28 %28 +OpStore %19 %true +OpBranch %24 +%24 = OpLabel +OpBranch %17 +%17 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, false, true); +} + +// This is essentially the same as NestedSelectionMerge, except +// the order of the first branch is changed. This is to make sure things +// work even if the order of the traversals change. +TEST_F(MergeReturnPassTest, NestedSelectionMerge2) { + const std::string before = + R"( OpCapability Addresses + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "simple_shader" + %void = OpTypeVoid + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %false = OpConstantFalse %bool + %7 = OpTypeFunction %void + %1 = OpFunction %void None %7 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %false %10 %11 + %11 = OpLabel + OpReturn + %10 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %false %13 %14 + %13 = OpLabel + %15 = OpIAdd %uint %uint_0 %uint_0 + OpBranch %12 + %14 = OpLabel + OpReturn + %12 = OpLabel + OpBranch %9 + %9 = OpLabel + %16 = OpIAdd %uint %15 %15 + OpReturn + OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "simple_shader" +%void = OpTypeVoid +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%false = OpConstantFalse %bool +%7 = OpTypeFunction %void +%_ptr_Function_bool = OpTypePointer Function %bool +%true = OpConstantTrue %bool +%26 = OpUndef %uint +%1 = OpFunction %void None %7 +%8 = OpLabel +%19 = OpVariable %_ptr_Function_bool Function %false +OpSelectionMerge %9 None +OpBranchConditional %false %10 %11 +%11 = OpLabel +OpStore %19 %true +OpBranch %9 +%10 = OpLabel +OpSelectionMerge %12 None +OpBranchConditional %false %13 %14 +%13 = OpLabel +%15 = OpIAdd %uint %uint_0 %uint_0 +OpBranch %12 +%14 = OpLabel +OpStore %19 %true +OpBranch %12 +%12 = OpLabel +%27 = OpPhi %uint %15 %13 %26 %14 +%25 = OpLoad %bool %19 +OpBranchConditional %25 %9 %24 +%24 = OpLabel +OpBranch %9 +%9 = OpLabel +%28 = OpPhi %uint %27 %24 %26 %11 %26 %12 +%23 = OpLoad %bool %19 +OpSelectionMerge %22 None +OpBranchConditional %23 %22 %21 +%21 = OpLabel +%16 = OpIAdd %uint %28 %28 +OpStore %19 %true +OpBranch %22 +%22 = OpLabel +OpBranch %17 +%17 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, false, true); +} + +TEST_F(MergeReturnPassTest, NestedSelectionMerge3) { + const std::string before = + R"( OpCapability Addresses + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "simple_shader" + %void = OpTypeVoid + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %false = OpConstantFalse %bool + %7 = OpTypeFunction %void + %1 = OpFunction %void None %7 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %false %10 %11 + %11 = OpLabel + OpReturn + %10 = OpLabel + %12 = OpIAdd %uint %uint_0 %uint_0 + OpSelectionMerge %13 None + OpBranchConditional %false %14 %15 + %14 = OpLabel + OpBranch %13 + %15 = OpLabel + OpReturn + %13 = OpLabel + OpBranch %9 + %9 = OpLabel + %16 = OpIAdd %uint %12 %12 + OpReturn + OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "simple_shader" +%void = OpTypeVoid +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%false = OpConstantFalse %bool +%7 = OpTypeFunction %void +%_ptr_Function_bool = OpTypePointer Function %bool +%true = OpConstantTrue %bool +%26 = OpUndef %uint +%1 = OpFunction %void None %7 +%8 = OpLabel +%19 = OpVariable %_ptr_Function_bool Function %false +OpSelectionMerge %9 None +OpBranchConditional %false %10 %11 +%11 = OpLabel +OpStore %19 %true +OpBranch %9 +%10 = OpLabel +%12 = OpIAdd %uint %uint_0 %uint_0 +OpSelectionMerge %13 None +OpBranchConditional %false %14 %15 +%14 = OpLabel +OpBranch %13 +%15 = OpLabel +OpStore %19 %true +OpBranch %13 +%13 = OpLabel +%25 = OpLoad %bool %19 +OpBranchConditional %25 %9 %24 +%24 = OpLabel +OpBranch %9 +%9 = OpLabel +%27 = OpPhi %uint %12 %24 %26 %11 %26 %13 +%23 = OpLoad %bool %19 +OpSelectionMerge %22 None +OpBranchConditional %23 %22 %21 +%21 = OpLabel +%16 = OpIAdd %uint %27 %27 +OpStore %19 %true +OpBranch %22 +%22 = OpLabel +OpBranch %17 +%17 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, false, true); +} +*/ + +TEST_F(MergeReturnPassTest, NestedLoopMerge) { + const std::string test = + R"( +; CHECK: OpFunction +; CHECK: OpSelectionMerge [[single_case_switch_merge:%\w+]] +; CHECK: OpLoopMerge [[outer_loop_merge:%\w+]] +; CHECK: OpLoopMerge [[inner_loop_merge:%\w+]] +; CHECK: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional %true [[early_exit_block:%\w+]] +; CHECK: [[early_exit_block]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpBranch [[inner_loop_merge]] +; CHECK: [[inner_loop_merge]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[outer_loop_merge]] +; CHECK: [[outer_loop_merge]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[single_case_switch_merge]] +; CHECK: [[single_case_switch_merge]] = OpLabel +; CHECK-NOT: OpLabel +; CHECK: OpReturn + OpCapability SampledBuffer + OpCapability StorageImageExtendedFormats + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "CS" + OpExecutionMode %2 LocalSize 8 8 1 + OpSource HLSL 600 + %uint = OpTypeInt 32 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 + %bool = OpTypeBool + %true = OpConstantTrue %bool +%_ptr_Function_uint = OpTypePointer Function %uint + %2 = OpFunction %void None %6 + %14 = OpLabel + OpBranch %19 + %19 = OpLabel + %20 = OpPhi %uint %uint_0 %2 %34 %23 + %21 = OpULessThan %bool %20 %uint_1 + OpLoopMerge %22 %23 DontUnroll + OpBranchConditional %21 %24 %22 + %24 = OpLabel + OpBranch %25 + %25 = OpLabel + %27 = OpINotEqual %bool %uint_1 %uint_0 + OpLoopMerge %28 %29 DontUnroll + OpBranchConditional %27 %30 %28 + %30 = OpLabel + OpSelectionMerge %31 None + OpBranchConditional %true %32 %31 + %32 = OpLabel + OpReturn + %31 = OpLabel + OpBranch %29 + %29 = OpLabel + OpBranch %25 + %28 = OpLabel + OpBranch %23 + %23 = OpLabel + %34 = OpIAdd %uint %20 %uint_1 + OpBranch %19 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(test, false); +} + +TEST_F(MergeReturnPassTest, ReturnValueDecoration) { + const std::string test = + R"( +; CHECK: OpDecorate [[func:%\w+]] RelaxedPrecision +; CHECK: OpDecorate [[ret_val:%\w+]] RelaxedPrecision +; CHECK: [[func]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NOT: OpLabel +; CHECK: [[ret_val]] = OpVariable +OpCapability Linkage +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %11 "simple_shader" +OpDecorate %7 RelaxedPrecision +%12 = OpTypeVoid +%1 = OpTypeInt 32 0 +%2 = OpTypeBool +%3 = OpConstantFalse %2 +%4 = OpConstant %1 0 +%5 = OpConstant %1 1 +%6 = OpTypeFunction %1 +%13 = OpTypeFunction %12 +%11 = OpFunction %12 None %13 +%l1 = OpLabel +%fc = OpFunctionCall %1 %7 +OpReturn +OpFunctionEnd +%7 = OpFunction %1 None %6 +%8 = OpLabel +OpBranchConditional %3 %9 %10 +%9 = OpLabel +OpReturnValue %4 +%10 = OpLabel +OpReturnValue %5 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(test, false); +} + +TEST_F(MergeReturnPassTest, + StructuredControlFlowWithNonTrivialUnreachableMerge) { + const std::string before = + R"( +OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %6 "simple_shader" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %4 %8 %9 +%8 = OpLabel +OpReturn +%9 = OpLabel +OpReturn +%10 = OpLabel +%11 = OpUndef %3 +OpUnreachable +OpFunctionEnd +)"; + + std::vector messages = { + {SPV_MSG_ERROR, nullptr, 0, 0, + "Module contains unreachable blocks during merge return. Run dead " + "branch elimination before merge return."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunToBinary(before, false); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); + EXPECT_TRUE(messages.empty()); +} + +TEST_F(MergeReturnPassTest, + StructuredControlFlowWithNonTrivialUnreachableContinue) { + const std::string before = + R"( +OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %6 "simple_shader" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpBranch %header +%header = OpLabel +OpLoopMerge %merge %continue None +OpBranchConditional %4 %8 %merge +%8 = OpLabel +OpReturn +%continue = OpLabel +%11 = OpUndef %3 +OpBranch %header +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::vector messages = { + {SPV_MSG_ERROR, nullptr, 0, 0, + "Module contains unreachable blocks during merge return. Run dead " + "branch elimination before merge return."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunToBinary(before, false); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); + EXPECT_TRUE(messages.empty()); +} + +TEST_F(MergeReturnPassTest, StructuredControlFlowWithUnreachableBlock) { + const std::string before = + R"( +OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %6 "simple_shader" +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantFalse %3 +%1 = OpTypeFunction %2 +%6 = OpFunction %2 None %1 +%7 = OpLabel +OpBranch %header +%header = OpLabel +OpLoopMerge %merge %continue None +OpBranchConditional %4 %8 %merge +%8 = OpLabel +OpReturn +%continue = OpLabel +OpBranch %header +%merge = OpLabel +OpReturn +%unreachable = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + std::vector messages = { + {SPV_MSG_ERROR, nullptr, 0, 0, + "Module contains unreachable blocks during merge return. Run dead " + "branch elimination before merge return."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunToBinary(before, false); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); + EXPECT_TRUE(messages.empty()); +} + +TEST_F(MergeReturnPassTest, StructuredControlFlowDontChangeEntryPhi) { + const std::string before = + R"( +; CHECK: OpFunction %void +; CHECK: OpLabel +; CHECK: [[pre_header:%\w+]] = OpLabel +; CHECK: [[header:%\w+]] = OpLabel +; CHECK-NEXT: OpPhi %bool {{%\w+}} [[pre_header]] [[iv:%\w+]] [[continue:%\w+]] +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] [[continue]] +; CHECK: [[continue]] = OpLabel +; CHECK-NEXT: [[iv]] = Op +; CHECK: [[merge]] = OpLabel + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %4 = OpTypeFunction %void + %1 = OpFunction %void None %4 + %5 = OpLabel + %6 = OpUndef %bool + OpBranch %7 + %7 = OpLabel + %8 = OpPhi %bool %6 %5 %9 %10 + OpLoopMerge %11 %10 None + OpBranch %12 + %12 = OpLabel + %13 = OpUndef %bool + OpSelectionMerge %10 DontFlatten + OpBranchConditional %13 %10 %14 + %14 = OpLabel + OpReturn + %10 = OpLabel + %9 = OpUndef %bool + OpBranchConditional %13 %7 %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, StructuredControlFlowPartialReplacePhi) { + const std::string before = + R"( +; CHECK: OpFunction %void +; CHECK: OpLabel +; CHECK: [[pre_header:%\w+]] = OpLabel +; CHECK: [[header:%\w+]] = OpLabel +; CHECK-NEXT: OpPhi +; CHECK-NEXT: OpLoopMerge [[merge:%\w+]] +; CHECK: OpLabel +; CHECK: [[old_ret_block:%\w+]] = OpLabel +; CHECK: [[bb:%\w+]] = OpLabel +; CHECK-NEXT: [[val:%\w+]] = OpUndef %bool +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[phi1:%\w+]] = OpPhi %bool {{%\w+}} [[old_ret_block]] [[val]] [[bb]] +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} [[bb2:%\w+]] +; CHECK: [[bb2]] = OpLabel +; CHECK: OpBranch [[header2:%\w+]] +; CHECK: [[header2]] = OpLabel +; CHECK-NEXT: [[phi2:%\w+]] = OpPhi %bool [[phi1]] [[continue2:%\w+]] [[phi1]] [[bb2]] +; CHECK-NEXT: OpLoopMerge {{%\w+}} [[continue2]] +; CHECK: [[continue2]] = OpLabel +; CHECK-NEXT: OpBranch [[header2]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %4 = OpTypeFunction %void + %1 = OpFunction %void None %4 + %5 = OpLabel + %6 = OpUndef %bool + OpBranch %7 + %7 = OpLabel + %8 = OpPhi %bool %6 %5 %9 %10 + OpLoopMerge %11 %10 None + OpBranch %12 + %12 = OpLabel + %13 = OpUndef %bool + OpSelectionMerge %10 DontFlatten + OpBranchConditional %13 %10 %14 + %14 = OpLabel + OpReturn + %10 = OpLabel + %9 = OpUndef %bool + OpBranchConditional %13 %7 %11 + %11 = OpLabel + %phi = OpPhi %bool %9 %10 %9 %cont + OpLoopMerge %ret %cont None + OpBranch %bb + %bb = OpLabel + OpBranchConditional %13 %ret %cont + %cont = OpLabel + OpBranch %11 + %ret = OpLabel + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, GeneratePhiInOuterLoop) { + const std::string before = + R"( + ; CHECK: OpSelectionMerge + ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] + ; CHECK-NEXT: [[def_bb1]] = OpLabel + ; CHECK: OpLoopMerge [[merge:%\w+]] [[continue:%\w+]] + ; CHECK: [[continue]] = OpLabel + ; CHECK-NEXT: [[undef:%\w+]] = OpUndef + ; CHECK: [[merge]] = OpLabel + ; CHECK-NEXT: [[phi:%\w+]] = OpPhi %bool {{%\w+}} {{%\w+}} [[undef]] [[continue]] + ; CHECK: OpCopyObject %bool [[phi]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool + %8 = OpTypeFunction %bool + %false = OpConstantFalse %bool + %4 = OpFunction %void None %3 + %5 = OpLabel + %63 = OpFunctionCall %bool %9 + OpReturn + OpFunctionEnd + %9 = OpFunction %bool None %8 + %10 = OpLabel + OpBranch %31 + %31 = OpLabel + OpLoopMerge %33 %34 None + OpBranch %32 + %32 = OpLabel + OpSelectionMerge %34 None + OpBranchConditional %false %46 %34 + %46 = OpLabel + OpLoopMerge %51 %52 None + OpBranch %53 + %53 = OpLabel + OpBranchConditional %false %50 %51 + %50 = OpLabel + OpReturnValue %false + %52 = OpLabel + OpBranch %46 + %51 = OpLabel + OpBranch %34 + %34 = OpLabel + %64 = OpUndef %bool + OpBranchConditional %false %31 %33 + %33 = OpLabel + OpBranch %28 + %28 = OpLabel + %60 = OpCopyObject %bool %64 + OpBranch %17 + %17 = OpLabel + OpReturnValue %false + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, SerialLoopsUpdateBlockMapping) { + // #2455: This test case triggers phi insertions that use previously inserted + // phis. Without the fix, it fails to validate. + const std::string spirv = R"( +; CHECK: OpSelectionMerge +; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] +; CHECK-NEXT: [[def_bb1]] = OpLabel +; CHECK: OpLoopMerge +; CHECK: OpLoopMerge +; CHECK: OpLoopMerge [[merge:%\w+]] +; CHECK: [[def:%\w+]] = OpFOrdLessThan +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[def]] +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] +; CHECK: [[cont]] = OpLabel +; CHECK-NEXT: OpBranchConditional [[phi]] +; CHECK-NOT: [[def]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %20 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %53 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypeFunction %7 + %11 = OpTypeBool + %12 = OpConstantFalse %11 + %15 = OpConstant %6 1 + %16 = OpConstantComposite %7 %15 %15 %15 + %18 = OpTypeInt 32 1 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 1 + %28 = OpConstant %18 0 + %31 = OpTypePointer Function %11 + %33 = OpConstantTrue %11 + %51 = OpTypeVector %6 4 + %52 = OpTypePointer Input %51 + %53 = OpVariable %52 Input + %54 = OpTypeInt 32 0 + %55 = OpConstant %54 0 + %56 = OpTypePointer Input %6 + %59 = OpConstant %6 0 + %76 = OpUndef %18 + %77 = OpUndef %11 + %78 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %75 = OpFunctionCall %7 %9 + OpReturn + OpFunctionEnd + %9 = OpFunction %7 None %8 + %10 = OpLabel + %20 = OpVariable %19 Function + OpBranch %14 + %14 = OpLabel + OpBranch %22 + %22 = OpLabel + %27 = OpLoad %18 %20 + OpLoopMerge %24 %25 None + OpBranch %24 + %25 = OpLabel + OpBranch %22 + %24 = OpLabel + OpBranch %34 + %34 = OpLabel + OpLoopMerge %36 %40 None + OpBranch %35 + %35 = OpLabel + OpBranchConditional %77 %39 %40 + %39 = OpLabel + OpReturnValue %16 + %40 = OpLabel + OpBranchConditional %12 %34 %36 + %36 = OpLabel + OpBranch %43 + %43 = OpLabel + OpLoopMerge %45 %49 None + OpBranch %44 + %44 = OpLabel + OpBranchConditional %77 %48 %49 + %48 = OpLabel + OpReturnValue %16 + %49 = OpLabel + %60 = OpFOrdLessThan %11 %15 %59 + OpBranchConditional %12 %43 %45 + %45 = OpLabel + OpBranch %62 + %62 = OpLabel + OpLoopMerge %64 %68 None + OpBranch %63 + %63 = OpLabel + OpBranchConditional %77 %67 %68 + %67 = OpLabel + OpReturnValue %16 + %68 = OpLabel + OpBranchConditional %60 %62 %64 + %64 = OpLabel + OpReturnValue %16 + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(MergeReturnPassTest, InnerLoopMergeIsOuterLoopContinue) { + const std::string before = + R"( + ; CHECK: OpSelectionMerge + ; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] + ; CHECK-NEXT: [[def_bb1]] = OpLabel + ; CHECK-NEXT: OpBranch [[outer_loop_header:%\w+]] + ; CHECK: [[outer_loop_header]] = OpLabel + ; CHECK-NEXT: OpLoopMerge [[outer_loop_merge:%\w+]] [[outer_loop_continue:%\w+]] None + ; CHECK: [[outer_loop_continue]] = OpLabel + ; CHECK-NEXT: OpBranch [[outer_loop_header]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %6 = OpTypeFunction %bool + %true = OpConstantTrue %bool + %2 = OpFunction %void None %4 + %8 = OpLabel + %9 = OpFunctionCall %bool %10 + OpReturn + OpFunctionEnd + %10 = OpFunction %bool None %6 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpLoopMerge %13 %14 None + OpBranchConditional %true %15 %13 + %15 = OpLabel + OpLoopMerge %14 %16 None + OpBranchConditional %true %17 %14 + %17 = OpLabel + OpReturnValue %true + %16 = OpLabel + OpBranch %15 + %14 = OpLabel + OpBranch %12 + %13 = OpLabel + OpReturnValue %true + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, BreakFromLoopUseNoLongerDominated) { + const std::string spirv = R"( +; CHECK: [[undef:%\w+]] = OpUndef +; CHECK: OpSelectionMerge +; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] +; CHECK-NEXT: [[def_bb1]] = OpLabel +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] +; CHECK-NEXT: OpBranch [[body:%\w+]] +; CHECK: [[body]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[non_ret:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[ret:%\w+]] [[non_ret]] +; CHECK: [[ret]] = OpLabel +; CHECK-NEXT: OpStore +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[non_ret]] = OpLabel +; CHECK-NEXT: [[def:%\w+]] = OpLogicalNot +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[break:%\w+]] [[cont]] +; CHECK: [[break]] = OpLabel +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[cont]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} {{%\w+}} [[merge]] +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} [[undef]] [[ret]] [[def]] [[break]] [[def]] [[cont]] +; CHECK: OpLogicalNot {{%\w+}} [[phi]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" +OpExecutionMode %func LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%func = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %8 %7 None +OpBranch %3 +%3 = OpLabel +OpSelectionMerge %5 None +OpBranchConditional %true %4 %5 +%4 = OpLabel +OpReturn +%5 = OpLabel +%def = OpLogicalNot %bool %true +OpBranchConditional %true %6 %7 +%6 = OpLabel +OpBranch %8 +%7 = OpLabel +OpBranchConditional %true %2 %8 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +%use = OpLogicalNot %bool %def +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(MergeReturnPassTest, TwoBreaksFromLoopUsesNoLongerDominated) { + const std::string spirv = R"( +; CHECK: [[undef:%\w+]] = OpUndef +; CHECK: OpSelectionMerge +; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] +; CHECK-NEXT: [[def_bb1]] = OpLabel +; CHECK: OpLoopMerge [[merge:%\w+]] [[cont:%\w+]] +; CHECK-NEXT: OpBranch [[body:%\w+]] +; CHECK: [[body]] = OpLabel +; CHECK-NEXT: OpSelectionMerge [[body2:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[ret1:%\w+]] [[body2]] +; CHECK: [[ret1]] = OpLabel +; CHECK-NEXT: OpStore +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[body2]] = OpLabel +; CHECK-NEXT: [[def1:%\w+]] = OpLogicalNot +; CHECK-NEXT: OpSelectionMerge [[body3:%\w+]] +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[ret2:%\w+]] [[body3:%\w+]] +; CHECK: [[ret2]] = OpLabel +; CHECK-NEXT: OpStore +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[body3]] = OpLabel +; CHECK-NEXT: [[def2:%\w+]] = OpLogicalAnd +; CHECK-NEXT: OpBranchConditional {{%\w+}} [[break:%\w+]] [[cont]] +; CHECK: [[break]] = OpLabel +; CHECK-NEXT: OpBranch [[merge]] +; CHECK: [[cont]] = OpLabel +; CHECK-NEXT: OpBranchConditional {{%\w+}} {{%\w+}} [[merge]] +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: [[phi1:%\w+]] = OpPhi {{%\w+}} [[undef]] [[ret1]] [[undef]] [[ret2]] [[def1]] [[break]] [[def1]] [[cont]] +; CHECK-NEXT: [[phi2:%\w+]] = OpPhi {{%\w+}} [[undef]] [[ret1]] [[undef]] [[ret2]] [[def2]] [[break]] [[def2]] [[cont]] +; CHECK: OpLogicalNot {{%\w+}} [[phi1]] +; CHECK: OpLogicalAnd {{%\w+}} [[phi2]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" +OpExecutionMode %func LocalSize 1 1 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%true = OpConstantTrue %bool +%func = OpFunction %void None %void_fn +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %10 %9 None +OpBranch %3 +%3 = OpLabel +OpSelectionMerge %5 None +OpBranchConditional %true %4 %5 +%4 = OpLabel +OpReturn +%5 = OpLabel +%def1 = OpLogicalNot %bool %true +OpSelectionMerge %7 None +OpBranchConditional %true %6 %7 +%6 = OpLabel +OpReturn +%7 = OpLabel +%def2 = OpLogicalAnd %bool %true %true +OpBranchConditional %true %8 %9 +%8 = OpLabel +OpBranch %10 +%9 = OpLabel +OpBranchConditional %true %2 %10 +%10 = OpLabel +OpBranch %11 +%11 = OpLabel +%use1 = OpLogicalNot %bool %def1 +%use2 = OpLogicalAnd %bool %def2 %true +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(MergeReturnPassTest, PredicateBreakBlock) { + const std::string spirv = R"( +; IDs are being preserved so we can rely on basic block labels. +; CHECK: [[undef:%\w+]] = OpUndef +; CHECK: [[undef:%\w+]] = OpUndef +; CHECK: %13 = OpLabel +; CHECK-NEXT: [[def:%\w+]] = OpLogicalNot +; CHECK: %8 = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi {{%\w+}} [[undef]] {{%\w+}} [[undef]] {{%\w+}} [[def]] %13 [[undef]] {{%\w+}} +; CHECK: OpLogicalAnd {{%\w+}} [[phi]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "func" +OpExecutionMode %1 LocalSize 1 1 1 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%bool = OpTypeBool +%true = OpUndef %bool +%1 = OpFunction %void None %3 +%6 = OpLabel +OpBranch %7 +%7 = OpLabel +OpLoopMerge %8 %9 None +OpBranch %10 +%10 = OpLabel +OpSelectionMerge %11 None +OpBranchConditional %true %12 %13 +%12 = OpLabel +OpLoopMerge %14 %15 None +OpBranch %16 +%16 = OpLabel +OpReturn +%15 = OpLabel +OpBranch %12 +%14 = OpLabel +OpUnreachable +%13 = OpLabel +%17 = OpLogicalNot %bool %true +OpBranch %8 +%11 = OpLabel +OpUnreachable +%9 = OpLabel +OpBranch %7 +%8 = OpLabel +OpBranch %18 +%18 = OpLabel +%19 = OpLogicalAnd %bool %17 %true +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(MergeReturnPassTest, SingleReturnInLoop) { + const std::string predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%9 = OpTypeFunction %float +%float_1 = OpConstant %float 1 +)"; + + const std::string caller = + R"( +; CHECK: OpFunction +; CHECK: OpFunctionEnd +%main = OpFunction %void None %7 +%22 = OpLabel +%30 = OpFunctionCall %float %f_ +OpReturn +OpFunctionEnd +)"; + + const std::string callee = + R"( +; CHECK: OpFunction +; CHECK: OpLoopMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHECK: OpReturnValue +; CHECK-NEXT: OpFunctionEnd +%f_ = OpFunction %float None %9 +%33 = OpLabel +OpBranch %34 +%34 = OpLabel +OpLoopMerge %35 %36 None +OpBranch %37 +%37 = OpLabel +OpReturnValue %float_1 +%36 = OpLabel +OpBranch %34 +%35 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(predefs + caller + callee, true); +} + +TEST_F(MergeReturnPassTest, MergeToMergeBranch) { + const std::string text = + R"( +; CHECK: [[new_undef:%\w+]] = OpUndef %uint +; CHECK: OpSelectionMerge +; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] +; CHECK-NEXT: [[def_bb1]] = OpLabel +; CHECK: OpLoopMerge [[merge1:%\w+]] +; CHECK: OpLoopMerge [[merge2:%\w+]] +; CHECK: [[merge1]] = OpLabel +; CHECK-NEXT: OpPhi %uint [[new_undef]] [[merge2]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 100 1 1 + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %bool = OpTypeBool + %false = OpConstantFalse %bool + %uint_0 = OpConstant %uint 0 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %13 = OpUndef %bool + %2 = OpFunction %void None %4 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + OpLoopMerge %19 %20 None + OpBranchConditional %13 %21 %19 + %21 = OpLabel + OpReturn + %20 = OpLabel + OpBranch %18 + %19 = OpLabel + %22 = OpUndef %uint + OpBranch %23 + %23 = OpLabel + OpBranch %16 + %17 = OpLabel + OpBranch %15 + %16 = OpLabel + %24 = OpCopyObject %uint %22 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(MergeReturnPassTest, PhiInSecondMerge) { + // Add and use a phi in the second merge block from the return. + const std::string text = + R"( +; CHECK: OpSelectionMerge +; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] +; CHECK-NEXT: [[def_bb1]] = OpLabel +; CHECK: OpLoopMerge [[merge_bb:%\w+]] [[continue_bb:%\w+]] +; CHECK: [[continue_bb]] = OpLabel +; CHECK-NEXT: [[val:%\w+]] = OpUndef %float +; CHECK: [[merge_bb]] = OpLabel +; CHECK-NEXT: [[phi:%\w+]] = OpPhi %float {{%\w+}} {{%\w+}} [[val]] [[continue_bb]] +; CHECK-NOT: OpLabel +; CHECK: OpBranchConditional {{%\w+}} {{%\w+}} [[old_merge:%\w+]] +; CHECK: [[old_merge]] = OpLabel +; CHECK-NEXT: OpConvertFToS %int [[phi]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %bool = OpTypeBool + %8 = OpUndef %bool + %2 = OpFunction %void None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %13 + %13 = OpLabel + OpLoopMerge %18 %14 None + OpBranchConditional %8 %15 %18 + %15 = OpLabel + OpReturn + %14 = OpLabel + OpBranch %13 + %18 = OpLabel + OpBranch %12 + %12 = OpLabel + %16 = OpUndef %float + OpBranchConditional %8 %10 %11 + %11 = OpLabel + %17 = OpConvertFToS %int %16 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(MergeReturnPassTest, ReturnsInSwitch) { + // Cannot branch directly to single case switch merge block from original + // switch. Must branch to merge block of original switch and then do + // predicated branch to merge block of single case switch. + const std::string text = + R"( +; CHECK: OpSelectionMerge [[single_case_switch_merge_bb:%\w+]] +; CHECK-NEXT: OpSwitch {{%\w+}} [[def_bb1:%\w+]] +; CHECK-NEXT: [[def_bb1]] = OpLabel +; CHECK: OpSelectionMerge +; CHECK-NEXT: OpSwitch {{%\w+}} [[inner_merge_bb:%\w+]] 0 {{%\w+}} 1 {{%\w+}} +; CHECK: OpBranch [[inner_merge_bb]] +; CHECK: OpBranch [[inner_merge_bb]] +; CHECK-NEXT: [[inner_merge_bb]] = OpLabel +; CHECK: OpBranchConditional {{%\w+}} [[single_case_switch_merge_bb]] {{%\w+}} + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %PSMain "PSMain" %_entryPointOutput_color + OpExecutionMode %PSMain OriginUpperLeft + OpSource HLSL 500 + OpMemberDecorate %cb 0 Offset 0 + OpMemberDecorate %cb 1 Offset 16 + OpMemberDecorate %cb 2 Offset 32 + OpMemberDecorate %cb 3 Offset 48 + OpDecorate %cb Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 0 + OpDecorate %_entryPointOutput_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %8 = OpTypeFunction %v4float + %int = OpTypeInt 32 1 + %cb = OpTypeStruct %v4float %v4float %v4float %int +%_ptr_Uniform_cb = OpTypePointer Uniform %cb + %_ = OpVariable %_ptr_Uniform_cb Uniform + %int_3 = OpConstant %int 3 +%_ptr_Uniform_int = OpTypePointer Uniform %int + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float + %int_1 = OpConstant %int 1 + %int_2 = OpConstant %int 2 + %float_0 = OpConstant %float 0 + %float_1 = OpConstant %float 1 + %45 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_1 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_color = OpVariable %_ptr_Output_v4float Output + %PSMain = OpFunction %void None %3 + %5 = OpLabel + %50 = OpFunctionCall %v4float %BlendValue_ + OpStore %_entryPointOutput_color %50 + OpReturn + OpFunctionEnd +%BlendValue_ = OpFunction %v4float None %8 + %10 = OpLabel + %21 = OpAccessChain %_ptr_Uniform_int %_ %int_3 + %22 = OpLoad %int %21 + OpSelectionMerge %25 None + OpSwitch %22 %25 0 %23 1 %24 + %23 = OpLabel + %28 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 + %29 = OpLoad %v4float %28 + OpReturnValue %29 + %24 = OpLabel + %31 = OpAccessChain %_ptr_Uniform_v4float %_ %int_0 + %32 = OpLoad %v4float %31 + %34 = OpAccessChain %_ptr_Uniform_v4float %_ %int_1 + %35 = OpLoad %v4float %34 + %37 = OpAccessChain %_ptr_Uniform_v4float %_ %int_2 + %38 = OpLoad %v4float %37 + %39 = OpFMul %v4float %35 %38 + %40 = OpFAdd %v4float %32 %39 + OpReturnValue %40 + %25 = OpLabel + OpReturnValue %45 + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(MergeReturnPassTest, UnreachableMergeAndContinue) { + // Make sure that the pass can handle a single block that is both a merge and + // a continue. + const std::string text = + R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %2 = OpFunction %void None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %true %12 %13 + %12 = OpLabel + OpReturn + %13 = OpLabel + OpReturn + %10 = OpLabel + OpBranch %8 + %9 = OpLabel + OpUnreachable + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + auto result = SinglePassRunAndDisassemble(text, true, true); + + // Not looking for any particular output. Other tests do that. + // Just want to make sure the check for unreachable blocks does not emit an + // error. + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + +TEST_F(MergeReturnPassTest, SingleReturnInMiddle) { + const std::string before = + R"( +; CHECK: OpFunction +; CHECK: OpReturn +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpName %main "main" + OpName %foo_ "foo(" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %foo_ = OpFunction %void None %4 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %true %9 %8 + %8 = OpLabel + OpReturn + %9 = OpLabel + OpBranch %8 + OpFunctionEnd + %main = OpFunction %void None %4 + %10 = OpLabel + %11 = OpFunctionCall %void %foo_ + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, false); +} + +TEST_F(MergeReturnPassTest, PhiWithTooManyEntries) { + // Check that the OpPhi node has the correct number of entries. This is + // checked by doing validation with the match. + const std::string before = + R"( +; CHECK: OpLoopMerge [[merge:%\w+]] +; CHECK: [[merge]] = OpLabel +; CHECK-NEXT: {{%\w+}} = OpPhi %int {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} {{%\w+}} + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %6 = OpTypeFunction %int + %bool = OpTypeBool + %int_1 = OpConstant %int 1 + %false = OpConstantFalse %bool + %2 = OpFunction %void None %4 + %10 = OpLabel + %11 = OpFunctionCall %int %12 + OpReturn + OpFunctionEnd + %12 = OpFunction %int None %6 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + %15 = OpPhi %int %int_1 %13 %16 %17 + OpLoopMerge %18 %17 None + OpBranch %19 + %19 = OpLabel + %20 = OpUndef %bool + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + OpSelectionMerge %25 None + OpBranchConditional %20 %22 %25 + %25 = OpLabel + OpReturnValue %int_1 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %20 %27 %26 + %27 = OpLabel + OpBranch %28 + %28 = OpLabel + OpLoopMerge %29 %30 None + OpBranch %31 + %31 = OpLabel + OpReturnValue %int_1 + %30 = OpLabel + OpBranch %28 + %29 = OpLabel + OpUnreachable + %26 = OpLabel + OpBranch %17 + %17 = OpLabel + %16 = OpPhi %int %15 %26 + OpBranchConditional %false %14 %18 + %18 = OpLabel + OpReturnValue %16 + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, true); +} + +TEST_F(MergeReturnPassTest, PointerUsedAfterLoop) { + // Make sure that a Phi instruction is not generated for an id whose type is a + // pointer. It needs to be regenerated. + const std::string before = + R"( +; CHECK: OpFunction %void +; CHECK: OpFunction %void +; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_v2uint +; CHECK: OpLoopMerge [[merge_bb:%\w+]] +; CHECK: [[merge_bb]] = OpLabel +; CHECK-NEXT: [[ac:%\w+]] = OpAccessChain %_ptr_Function_uint [[param]] %uint_1 +; CHECK: OpStore [[ac]] %uint_1 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 +%_ptr_Function_v2uint = OpTypePointer Function %v2uint + %8 = OpTypeFunction %void %_ptr_Function_v2uint + %uint_1 = OpConstant %uint 1 + %bool = OpTypeBool +%_ptr_Function_uint = OpTypePointer Function %uint + %false = OpConstantFalse %bool + %2 = OpFunction %void None %4 + %13 = OpLabel + %14 = OpVariable %_ptr_Function_v2uint Function + %15 = OpFunctionCall %void %16 %14 + OpReturn + OpFunctionEnd + %16 = OpFunction %void None %8 + %17 = OpFunctionParameter %_ptr_Function_v2uint + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + OpSelectionMerge %23 None + OpBranchConditional %false %24 %23 + %24 = OpLabel + OpReturn + %23 = OpLabel + OpBranch %21 + %21 = OpLabel + %25 = OpAccessChain %_ptr_Function_uint %17 %uint_1 + OpBranchConditional %false %19 %20 + %20 = OpLabel + OpStore %25 %uint_1 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, true); +} + +TEST_F(MergeReturnPassTest, VariablePointerFunctionScope) { + // Make sure that a Phi instruction is not generated for an id whose type is a + // function scope pointer, even if the VariablePointers capability is + // available. It needs to be regenerated. + const std::string before = + R"( +; CHECK: OpFunction %void +; CHECK: OpFunction %void +; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_v2uint +; CHECK: OpLoopMerge [[merge_bb:%\w+]] +; CHECK: [[merge_bb]] = OpLabel +; CHECK-NEXT: [[ac:%\w+]] = OpAccessChain %_ptr_Function_uint [[param]] %uint_1 +; CHECK: OpStore [[ac]] %uint_1 + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 +%_ptr_Function_v2uint = OpTypePointer Function %v2uint + %8 = OpTypeFunction %void %_ptr_Function_v2uint + %uint_1 = OpConstant %uint 1 + %bool = OpTypeBool +%_ptr_Function_uint = OpTypePointer Function %uint + %false = OpConstantFalse %bool + %2 = OpFunction %void None %4 + %13 = OpLabel + %14 = OpVariable %_ptr_Function_v2uint Function + %15 = OpFunctionCall %void %16 %14 + OpReturn + OpFunctionEnd + %16 = OpFunction %void None %8 + %17 = OpFunctionParameter %_ptr_Function_v2uint + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + OpSelectionMerge %23 None + OpBranchConditional %false %24 %23 + %24 = OpLabel + OpReturn + %23 = OpLabel + OpBranch %21 + %21 = OpLabel + %25 = OpAccessChain %_ptr_Function_uint %17 %uint_1 + OpBranchConditional %false %19 %20 + %20 = OpLabel + OpStore %25 %uint_1 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, true); +} + +TEST_F(MergeReturnPassTest, ChainedPointerUsedAfterLoop) { + // Make sure that a Phi instruction is not generated for an id whose type is a + // pointer. It needs to be regenerated. + const std::string before = + R"( +; CHECK: OpFunction %void +; CHECK: OpFunction %void +; CHECK-NEXT: [[param:%\w+]] = OpFunctionParameter %_ptr_Function_ +; CHECK: OpLoopMerge [[merge_bb:%\w+]] +; CHECK: [[merge_bb]] = OpLabel +; CHECK-NEXT: [[ac1:%\w+]] = OpAccessChain %_ptr_Function_v2uint [[param]] %uint_1 +; CHECK-NEXT: [[ac2:%\w+]] = OpAccessChain %_ptr_Function_uint [[ac1]] %uint_1 +; CHECK: OpStore [[ac2]] %uint_1 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %v2uint = OpTypeVector %uint 2 +%_arr_v2uint_uint_2 = OpTypeArray %v2uint %uint_2 +%_ptr_Function_v2uint = OpTypePointer Function %v2uint +%_ptr_Function__arr_v2uint_uint_2 = OpTypePointer Function %_arr_v2uint_uint_2 +%_ptr_Function_uint = OpTypePointer Function %uint + %13 = OpTypeFunction %void %_ptr_Function__arr_v2uint_uint_2 + %bool = OpTypeBool + %false = OpConstantFalse %bool + %2 = OpFunction %void None %4 + %16 = OpLabel + %17 = OpVariable %_ptr_Function__arr_v2uint_uint_2 Function + %18 = OpFunctionCall %void %19 %17 + OpReturn + OpFunctionEnd + %19 = OpFunction %void None %13 + %20 = OpFunctionParameter %_ptr_Function__arr_v2uint_uint_2 + %21 = OpLabel + OpBranch %22 + %22 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %false %27 %26 + %27 = OpLabel + OpReturn + %26 = OpLabel + OpBranch %24 + %24 = OpLabel + %28 = OpAccessChain %_ptr_Function_v2uint %20 %uint_1 + %29 = OpAccessChain %_ptr_Function_uint %28 %uint_1 + OpBranchConditional %false %22 %23 + %23 = OpLabel + OpStore %29 %uint_1 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(before, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/pass_remove_duplicates_test.cpp b/third_party/spirv-tools/test/opt/pass_remove_duplicates_test.cpp new file mode 100644 index 0000000..887fdfd --- /dev/null +++ b/third_party/spirv-tools/test/opt/pass_remove_duplicates_test.cpp @@ -0,0 +1,646 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/opt/pass_manager.h" +#include "source/opt/remove_duplicates_pass.h" +#include "source/spirv_constant.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace opt { +namespace { + +class RemoveDuplicatesTest : public ::testing::Test { + public: + RemoveDuplicatesTest() + : tools_(SPV_ENV_UNIVERSAL_1_2), + context_(), + consumer_([this](spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + if (!error_message_.empty()) error_message_ += "\n"; + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + error_message_ += "ERROR"; + break; + case SPV_MSG_WARNING: + error_message_ += "WARNING"; + break; + case SPV_MSG_INFO: + error_message_ += "INFO"; + break; + case SPV_MSG_DEBUG: + error_message_ += "DEBUG"; + break; + } + error_message_ += + ": " + std::to_string(position.index) + ": " + message; + }), + disassemble_options_(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER), + error_message_() { + tools_.SetMessageConsumer(consumer_); + } + + void TearDown() override { error_message_.clear(); } + + std::string RunPass(const std::string& text) { + context_ = spvtools::BuildModule(SPV_ENV_UNIVERSAL_1_2, consumer_, text); + if (!context_.get()) return std::string(); + + PassManager manager; + manager.SetMessageConsumer(consumer_); + manager.AddPass(); + + Pass::Status pass_res = manager.Run(context_.get()); + if (pass_res == Pass::Status::Failure) return std::string(); + + return ModuleToText(); + } + + // Disassembles |binary| and outputs the result in |text|. If |text| is a + // null pointer, SPV_ERROR_INVALID_POINTER is returned. + spv_result_t Disassemble(const std::vector& binary, + std::string* text) { + if (!text) return SPV_ERROR_INVALID_POINTER; + return tools_.Disassemble(binary, text, disassemble_options_) + ? SPV_SUCCESS + : SPV_ERROR_INVALID_BINARY; + } + + // Returns the accumulated error messages for the test. + std::string GetErrorMessage() const { return error_message_; } + + std::string ToText(const std::vector& inst) { + std::vector binary = {SpvMagicNumber, 0x10200, 0u, 2u, 0u}; + for (const Instruction* i : inst) + i->ToBinaryWithoutAttachedDebugInsts(&binary); + std::string text; + Disassemble(binary, &text); + return text; + } + + std::string ModuleToText() { + std::vector binary; + context_->module()->ToBinary(&binary, false); + std::string text; + Disassemble(binary, &text); + return text; + } + + private: + spvtools::SpirvTools + tools_; // An instance for calling SPIRV-Tools functionalities. + std::unique_ptr context_; + spvtools::MessageConsumer consumer_; + uint32_t disassemble_options_; + std::string error_message_; +}; + +TEST_F(RemoveDuplicatesTest, DuplicateCapabilities) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Shader +OpMemoryModel Logical GLSL450 +)"; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, DuplicateExtInstImports) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "OpenCL.std" +%2 = OpExtInstImport "OpenCL.std" +%3 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +)"; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "OpenCL.std" +%3 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, DuplicateTypes) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 32 0 +%3 = OpTypeStruct %1 %2 +)"; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%3 = OpTypeStruct %1 %1 +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, SameTypeDifferentMemberDecoration) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 GLSLPacked +%2 = OpTypeInt 32 0 +%1 = OpTypeStruct %2 %2 +%3 = OpTypeStruct %2 %2 +)"; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 GLSLPacked +%2 = OpTypeInt 32 0 +%1 = OpTypeStruct %2 %2 +%3 = OpTypeStruct %2 %2 +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, SameTypeAndMemberDecoration) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 GLSLPacked +OpDecorate %2 GLSLPacked +%3 = OpTypeInt 32 0 +%1 = OpTypeStruct %3 %3 +%2 = OpTypeStruct %3 %3 +)"; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 GLSLPacked +%3 = OpTypeInt 32 0 +%1 = OpTypeStruct %3 %3 +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, SameTypeAndDifferentName) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %1 "Type1" +OpName %2 "Type2" +%3 = OpTypeInt 32 0 +%1 = OpTypeStruct %3 %3 +%2 = OpTypeStruct %3 %3 +)"; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %1 "Type1" +%3 = OpTypeInt 32 0 +%1 = OpTypeStruct %3 %3 +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +// Check that #1033 has been fixed. +TEST_F(RemoveDuplicatesTest, DoNotRemoveDifferentOpDecorationGroup) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +%1 = OpDecorationGroup +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %3 %1 %2 +%4 = OpTypeInt 32 0 +%3 = OpVariable %4 Uniform +)"; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +%1 = OpDecorationGroup +OpDecorate %2 Restrict +%2 = OpDecorationGroup +OpGroupDecorate %3 %1 %2 +%4 = OpTypeInt 32 0 +%3 = OpVariable %4 Uniform +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +TEST_F(RemoveDuplicatesTest, DifferentDecorationGroup) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %1 Restrict +%1 = OpDecorationGroup +OpDecorate %2 Constant +%2 = OpDecorationGroup +OpGroupDecorate %1 %3 +OpGroupDecorate %2 %4 +%5 = OpTypeInt 32 0 +%3 = OpVariable %5 Uniform +%4 = OpVariable %5 Uniform +)"; + const std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 Constant +OpDecorate %1 Restrict +%1 = OpDecorationGroup +OpDecorate %2 Constant +%2 = OpDecorationGroup +OpGroupDecorate %1 %3 +OpGroupDecorate %2 %4 +%5 = OpTypeInt 32 0 +%3 = OpVariable %5 Uniform +%4 = OpVariable %5 Uniform +)"; + + EXPECT_EQ(RunPass(spirv), after); + EXPECT_EQ(GetErrorMessage(), ""); +} + +// Test what happens when a type is a resource type. For now we are merging +// them, but, if we want to merge types and make reflection work (issue #1372), +// we will not be able to merge %2 and %3 below. +TEST_F(RemoveDuplicatesTest, DontMergeNestedResourceTypes) { + const std::string spirv = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %2 "NormalAdjust" +OpMemberName %2 0 "XDir" +OpMemberName %3 0 "AdjustXYZ" +OpMemberName %3 1 "AdjustDir" +OpName %4 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %2 0 Offset 0 +OpMemberDecorate %3 0 Offset 0 +OpMemberDecorate %3 1 Offset 16 +OpDecorate %3 Block +OpDecorate %4 DescriptorSet 0 +OpDecorate %4 Binding 0 +%5 = OpTypeFloat 32 +%6 = OpTypeVector %5 3 +%1 = OpTypeStruct %6 +%2 = OpTypeStruct %6 +%3 = OpTypeStruct %1 %2 +%7 = OpTypePointer Uniform %3 +%4 = OpVariable %7 Uniform +)"; + + const std::string result = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpMemberName %3 0 "AdjustXYZ" +OpMemberName %3 1 "AdjustDir" +OpName %4 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %3 0 Offset 0 +OpMemberDecorate %3 1 Offset 16 +OpDecorate %3 Block +OpDecorate %4 DescriptorSet 0 +OpDecorate %4 Binding 0 +%5 = OpTypeFloat 32 +%6 = OpTypeVector %5 3 +%1 = OpTypeStruct %6 +%3 = OpTypeStruct %1 %1 +%7 = OpTypePointer Uniform %3 +%4 = OpVariable %7 Uniform +)"; + + EXPECT_EQ(RunPass(spirv), result); + EXPECT_EQ(GetErrorMessage(), ""); +} + +// See comment for DontMergeNestedResourceTypes. +TEST_F(RemoveDuplicatesTest, DontMergeResourceTypes) { + const std::string spirv = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %2 "NormalAdjust" +OpMemberName %2 0 "XDir" +OpName %3 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %2 0 Offset 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +OpDecorate %4 DescriptorSet 1 +OpDecorate %4 Binding 0 +%5 = OpTypeFloat 32 +%6 = OpTypeVector %5 3 +%1 = OpTypeStruct %6 +%2 = OpTypeStruct %6 +%7 = OpTypePointer Uniform %1 +%8 = OpTypePointer Uniform %2 +%3 = OpVariable %7 Uniform +%4 = OpVariable %8 Uniform +)"; + + const std::string result = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %3 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +OpDecorate %4 DescriptorSet 1 +OpDecorate %4 Binding 0 +%5 = OpTypeFloat 32 +%6 = OpTypeVector %5 3 +%1 = OpTypeStruct %6 +%7 = OpTypePointer Uniform %1 +%3 = OpVariable %7 Uniform +%4 = OpVariable %7 Uniform +)"; + + EXPECT_EQ(RunPass(spirv), result); + EXPECT_EQ(GetErrorMessage(), ""); +} + +// See comment for DontMergeNestedResourceTypes. +TEST_F(RemoveDuplicatesTest, DontMergeResourceTypesContainingArray) { + const std::string spirv = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %2 "NormalAdjust" +OpMemberName %2 0 "XDir" +OpName %3 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %2 0 Offset 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +OpDecorate %4 DescriptorSet 1 +OpDecorate %4 Binding 0 +%5 = OpTypeFloat 32 +%6 = OpTypeVector %5 3 +%1 = OpTypeStruct %6 +%2 = OpTypeStruct %6 +%7 = OpTypeInt 32 0 +%8 = OpConstant %7 4 +%9 = OpTypeArray %1 %8 +%10 = OpTypeArray %2 %8 +%11 = OpTypePointer Uniform %9 +%12 = OpTypePointer Uniform %10 +%3 = OpVariable %11 Uniform +%4 = OpVariable %12 Uniform +)"; + + const std::string result = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %3 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +OpDecorate %4 DescriptorSet 1 +OpDecorate %4 Binding 0 +%5 = OpTypeFloat 32 +%6 = OpTypeVector %5 3 +%1 = OpTypeStruct %6 +%7 = OpTypeInt 32 0 +%8 = OpConstant %7 4 +%9 = OpTypeArray %1 %8 +%11 = OpTypePointer Uniform %9 +%3 = OpVariable %11 Uniform +%4 = OpVariable %11 Uniform +)"; + + EXPECT_EQ(RunPass(spirv), result); + EXPECT_EQ(GetErrorMessage(), ""); +} + +// Test that we merge the type of a resource with a type that is not the type +// a resource. The resource type appears first in this case. We must keep +// the resource type. +TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType1) { + const std::string spirv = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %2 "NormalAdjust" +OpMemberName %2 0 "XDir" +OpName %3 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %2 0 Offset 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +%4 = OpTypeFloat 32 +%5 = OpTypeVector %4 3 +%1 = OpTypeStruct %5 +%2 = OpTypeStruct %5 +%6 = OpTypePointer Uniform %1 +%7 = OpTypePointer Uniform %2 +%3 = OpVariable %6 Uniform +%8 = OpVariable %7 Uniform +)"; + + const std::string result = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %3 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +%4 = OpTypeFloat 32 +%5 = OpTypeVector %4 3 +%1 = OpTypeStruct %5 +%6 = OpTypePointer Uniform %1 +%3 = OpVariable %6 Uniform +%8 = OpVariable %6 Uniform +)"; + + EXPECT_EQ(RunPass(spirv), result); + EXPECT_EQ(GetErrorMessage(), ""); +} + +// Test that we merge the type of a resource with a type that is not the type +// a resource. The resource type appears second in this case. We must keep +// the resource type. +// +// See comment for DontMergeNestedResourceTypes. +TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType2) { + const std::string spirv = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %2 "NormalAdjust" +OpMemberName %2 0 "XDir" +OpName %3 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpMemberDecorate %2 0 Offset 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +%4 = OpTypeFloat 32 +%5 = OpTypeVector %4 3 +%1 = OpTypeStruct %5 +%2 = OpTypeStruct %5 +%6 = OpTypePointer Uniform %1 +%7 = OpTypePointer Uniform %2 +%8 = OpVariable %6 Uniform +%3 = OpVariable %7 Uniform +)"; + + const std::string result = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpSource HLSL 600 +OpName %1 "PositionAdjust" +OpMemberName %1 0 "XAdjust" +OpName %3 "Constants" +OpMemberDecorate %1 0 Offset 0 +OpDecorate %3 DescriptorSet 0 +OpDecorate %3 Binding 0 +%4 = OpTypeFloat 32 +%5 = OpTypeVector %4 3 +%1 = OpTypeStruct %5 +%6 = OpTypePointer Uniform %1 +%8 = OpVariable %6 Uniform +%3 = OpVariable %6 Uniform +)"; + + EXPECT_EQ(RunPass(spirv), result); + EXPECT_EQ(GetErrorMessage(), ""); +} + +// In this test, %8 and %9 are the same and only %9 is used in a resource. +// However, we cannot merge them unless we also merge %2 and %3, which cannot +// happen because both are used in resources. +// +// If we try to avoid replaces resource types, then remove duplicates should +// have not change in this case. That is not currently implemented. +TEST_F(RemoveDuplicatesTest, MergeResourceTypeWithNonresourceType3) { + const std::string spirv = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpSource HLSL 600 +OpName %2 "PositionAdjust" +OpMemberName %2 0 "XAdjust" +OpName %3 "NormalAdjust" +OpMemberName %3 0 "XDir" +OpName %4 "Constants" +OpMemberDecorate %2 0 Offset 0 +OpMemberDecorate %3 0 Offset 0 +OpDecorate %4 DescriptorSet 0 +OpDecorate %4 Binding 0 +OpDecorate %5 DescriptorSet 1 +OpDecorate %5 Binding 0 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 3 +%2 = OpTypeStruct %7 +%3 = OpTypeStruct %7 +%8 = OpTypePointer Uniform %3 +%9 = OpTypePointer Uniform %2 +%10 = OpTypeStruct %3 +%11 = OpTypePointer Uniform %10 +%5 = OpVariable %9 Uniform +%4 = OpVariable %11 Uniform +%12 = OpTypeVoid +%13 = OpTypeFunction %12 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 0 +%1 = OpFunction %12 None %13 +%16 = OpLabel +%17 = OpAccessChain %8 %4 %15 +OpReturn +OpFunctionEnd +)"; + + const std::string result = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpSource HLSL 600 +OpName %2 "PositionAdjust" +OpMemberName %2 0 "XAdjust" +OpName %4 "Constants" +OpMemberDecorate %2 0 Offset 0 +OpDecorate %4 DescriptorSet 0 +OpDecorate %4 Binding 0 +OpDecorate %5 DescriptorSet 1 +OpDecorate %5 Binding 0 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 3 +%2 = OpTypeStruct %7 +%8 = OpTypePointer Uniform %2 +%10 = OpTypeStruct %2 +%11 = OpTypePointer Uniform %10 +%5 = OpVariable %8 Uniform +%4 = OpVariable %11 Uniform +%12 = OpTypeVoid +%13 = OpTypeFunction %12 +%14 = OpTypeInt 32 0 +%15 = OpConstant %14 0 +%1 = OpFunction %12 None %13 +%16 = OpLabel +%17 = OpAccessChain %8 %4 %15 +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(RunPass(spirv), result); + EXPECT_EQ(GetErrorMessage(), ""); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/pass_utils.cpp b/third_party/spirv-tools/test/opt/pass_utils.cpp new file mode 100644 index 0000000..4709d0f --- /dev/null +++ b/third_party/spirv-tools/test/opt/pass_utils.cpp @@ -0,0 +1,102 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/opt/pass_utils.h" + +#include +#include + +namespace spvtools { +namespace opt { +namespace { + +// Well, this is another place requiring the knowledge of the grammar and can be +// stale when SPIR-V is updated. It would be nice to automatically generate +// this, but the cost is just too high. + +const char* kDebugOpcodes[] = { + // clang-format off + "OpSourceContinued", "OpSource", "OpSourceExtension", + "OpName", "OpMemberName", "OpString", + "OpLine", "OpNoLine", "OpModuleProcessed" + // clang-format on +}; + +} // anonymous namespace + +MessageConsumer GetTestMessageConsumer( + std::vector& expected_messages) { + return [&expected_messages](spv_message_level_t level, const char* source, + const spv_position_t& position, + const char* message) { + EXPECT_TRUE(!expected_messages.empty()); + if (expected_messages.empty()) { + return; + } + + EXPECT_EQ(expected_messages[0].level, level); + EXPECT_EQ(expected_messages[0].line_number, position.line); + EXPECT_EQ(expected_messages[0].column_number, position.column); + EXPECT_STREQ(expected_messages[0].source_file, source); + EXPECT_STREQ(expected_messages[0].message, message); + + expected_messages.erase(expected_messages.begin()); + }; +} + +bool FindAndReplace(std::string* process_str, const std::string find_str, + const std::string replace_str) { + if (process_str->empty() || find_str.empty()) { + return false; + } + bool replaced = false; + // Note this algorithm has quadratic time complexity. It is OK for test cases + // with short strings, but might not fit in other contexts. + for (size_t pos = process_str->find(find_str, 0); pos != std::string::npos; + pos = process_str->find(find_str, pos)) { + process_str->replace(pos, find_str.length(), replace_str); + pos += replace_str.length(); + replaced = true; + } + return replaced; +} + +bool ContainsDebugOpcode(const char* inst) { + return std::any_of(std::begin(kDebugOpcodes), std::end(kDebugOpcodes), + [inst](const char* op) { + return std::string(inst).find(op) != std::string::npos; + }); +} + +std::string SelectiveJoin(const std::vector& strings, + const std::function& skip_dictator, + char delimiter) { + std::ostringstream oss; + for (const auto* str : strings) { + if (!skip_dictator(str)) oss << str << delimiter; + } + return oss.str(); +} + +std::string JoinAllInsts(const std::vector& insts) { + return SelectiveJoin(insts, [](const char*) { return false; }); +} + +std::string JoinNonDebugInsts(const std::vector& insts) { + return SelectiveJoin( + insts, [](const char* inst) { return ContainsDebugOpcode(inst); }); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/pass_utils.h b/third_party/spirv-tools/test/opt/pass_utils.h new file mode 100644 index 0000000..8968f8a --- /dev/null +++ b/third_party/spirv-tools/test/opt/pass_utils.h @@ -0,0 +1,84 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_OPT_PASS_UTILS_H_ +#define TEST_OPT_PASS_UTILS_H_ + +#include +#include +#include +#include +#include + +#include "gtest/gtest.h" +#include "include/spirv-tools/libspirv.h" +#include "include/spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { + +struct Message { + spv_message_level_t level; + const char* source_file; + uint32_t line_number; + uint32_t column_number; + const char* message; +}; + +// Return a message consumer that can be used to check that the message produced +// are the messages in |expexted_messages|, and in the same order. +MessageConsumer GetTestMessageConsumer(std::vector& expected_messages); + +// In-place substring replacement. Finds the |find_str| in the |process_str| +// and replaces the found substring with |replace_str|. Returns true if at +// least one replacement is done successfully, returns false otherwise. The +// replaced substring won't be processed again, which means: If the +// |replace_str| has |find_str| as its substring, that newly replaced part of +// |process_str| won't be processed again. +bool FindAndReplace(std::string* process_str, const std::string find_str, + const std::string replace_str); + +// Returns true if the given string contains any debug opcode substring. +bool ContainsDebugOpcode(const char* inst); + +// Returns the concatenated string from a vector of |strings|, with postfixing +// each string with the given |delimiter|. if the |skip_dictator| returns true +// for an original string, that string will be omitted. +std::string SelectiveJoin(const std::vector& strings, + const std::function& skip_dictator, + char delimiter = '\n'); + +// Concatenates a vector of strings into one string. Each string is postfixed +// with '\n'. +std::string JoinAllInsts(const std::vector& insts); + +// Concatenates a vector of strings into one string. Each string is postfixed +// with '\n'. If a string contains opcode for debug instruction, that string +// will be ignored. +std::string JoinNonDebugInsts(const std::vector& insts); + +// Returns a vector that contains the contents of |a| followed by the contents +// of |b|. +template +std::vector Concat(const std::vector& a, const std::vector& b) { + std::vector ret; + std::copy(a.begin(), a.end(), back_inserter(ret)); + std::copy(b.begin(), b.end(), back_inserter(ret)); + return ret; +} + +} // namespace opt +} // namespace spvtools + +#endif // TEST_OPT_PASS_UTILS_H_ diff --git a/third_party/spirv-tools/test/opt/pch_test_opt.cpp b/third_party/spirv-tools/test/opt/pch_test_opt.cpp new file mode 100644 index 0000000..f158129 --- /dev/null +++ b/third_party/spirv-tools/test/opt/pch_test_opt.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_test_opt.h" diff --git a/third_party/spirv-tools/test/opt/pch_test_opt.h b/third_party/spirv-tools/test/opt/pch_test_opt.h new file mode 100644 index 0000000..4e8106f --- /dev/null +++ b/third_party/spirv-tools/test/opt/pch_test_opt.h @@ -0,0 +1,25 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "source/opt/iterator.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/tree_iterator.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" diff --git a/third_party/spirv-tools/test/opt/private_to_local_test.cpp b/third_party/spirv-tools/test/opt/private_to_local_test.cpp new file mode 100644 index 0000000..8b5ec59 --- /dev/null +++ b/third_party/spirv-tools/test/opt/private_to_local_test.cpp @@ -0,0 +1,501 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/build_module.h" +#include "source/opt/value_number_table.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; +using PrivateToLocalTest = PassTest<::testing::Test>; + +TEST_F(PrivateToLocalTest, ChangeToLocal) { + // Change the private variable to a local, and change the types accordingly. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 +; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 + %5 = OpTypeFloat 32 +; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] + %6 = OpTypePointer Private %5 +; CHECK-NOT: OpVariable [[.+]] Private + %8 = OpVariable %6 Private +; CHECK: OpFunction + %2 = OpFunction %3 None %4 +; CHECK: OpLabel + %7 = OpLabel +; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function +; CHECK: OpLoad [[float]] [[newvar]] + %9 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(PrivateToLocalTest, ReuseExistingType) { + // Change the private variable to a local, and change the types accordingly. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 +; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 + %5 = OpTypeFloat 32 + %func_ptr = OpTypePointer Function %5 +; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] +; CHECK-NOT: [[%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] + %6 = OpTypePointer Private %5 +; CHECK-NOT: OpVariable [[.+]] Private + %8 = OpVariable %6 Private +; CHECK: OpFunction + %2 = OpFunction %3 None %4 +; CHECK: OpLabel + %7 = OpLabel +; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function +; CHECK: OpLoad [[float]] [[newvar]] + %9 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(PrivateToLocalTest, UpdateAccessChain) { + // Change the private variable to a local, and change the AccessChain. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void +; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat + %float = OpTypeFloat 32 +; CHECK: [[struct:%[a-zA-Z_\d]+]] = OpTypeStruct + %_struct_8 = OpTypeStruct %float +%_ptr_Private_float = OpTypePointer Private %float +; CHECK: [[new_struct_type:%[a-zA-Z_\d]+]] = OpTypePointer Function [[struct]] +; CHECK: [[new_float_type:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] +%_ptr_Private__struct_8 = OpTypePointer Private %_struct_8 +; CHECK-NOT: OpVariable [[.+]] Private + %11 = OpVariable %_ptr_Private__struct_8 Private +; CHECK: OpFunction + %2 = OpFunction %void None %6 +; CHECK: OpLabel + %12 = OpLabel +; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[new_struct_type]] Function +; CHECK: [[member:%[a-zA-Z_\d]+]] = OpAccessChain [[new_float_type]] [[newvar]] + %13 = OpAccessChain %_ptr_Private_float %11 %uint_0 +; CHECK: OpLoad [[float]] [[member]] + %14 = OpLoad %float %13 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(PrivateToLocalTest, UseTexelPointer) { + // Change the private variable to a local, and change the OpImageTexelPointer. + const std::string text = R"( +OpCapability SampledBuffer + OpCapability StorageImageExtendedFormats + OpCapability ImageBuffer + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID + OpExecutionMode %2 LocalSize 64 1 1 + OpSource HLSL 600 + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %4 DescriptorSet 4 + OpDecorate %4 Binding 70 + %uint = OpTypeInt 32 0 + %6 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6 +%_ptr_Private_6 = OpTypePointer Private %6 + %void = OpTypeVoid + %10 = OpTypeFunction %void + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Image_uint = OpTypePointer Image %uint + %4 = OpVariable %_ptr_UniformConstant_6 UniformConstant + %16 = OpVariable %_ptr_Private_6 Private +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + %2 = OpFunction %void None %10 + %17 = OpLabel +; Make sure the variable was moved. +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpVariable %_ptr_Function_6 Function + %18 = OpLoad %6 %4 + OpStore %16 %18 + %19 = OpImageTexelPointer %_ptr_Image_uint %16 %uint_0 %uint_0 + %20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(PrivateToLocalTest, UsedInTwoFunctions) { + // Should not change because it is used in multiple functions. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Private %5 + %8 = OpVariable %6 Private + %2 = OpFunction %3 None %4 + %7 = OpLabel + %9 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + %10 = OpFunction %3 None %4 + %11 = OpLabel + %12 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(PrivateToLocalTest, UsedInFunctionCall) { + // Should not change because it is used in a function call. Changing the + // signature of the function would require cloning the function, which is not + // worth it. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float + %7 = OpTypeFunction %void %_ptr_Private_float + %8 = OpVariable %_ptr_Private_float Private + %2 = OpFunction %void None %4 + %9 = OpLabel + %10 = OpFunctionCall %void %11 %8 + OpReturn + OpFunctionEnd + %11 = OpFunction %void None %7 + %12 = OpFunctionParameter %_ptr_Private_float + %13 = OpLabel + %14 = OpLoad %float %12 + OpReturn + OpFunctionEnd + )"; + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct1) { + // Test that the correct pointer type is picked up. + const std::string text = R"( +; CHECK: [[struct1:%[a-zA-Z_\d]+]] = OpTypeStruct +; CHECK: [[struct2:%[a-zA-Z_\d]+]] = OpTypeStruct +; CHECK: [[priv_ptr:%[\w]+]] = OpTypePointer Private [[struct1]] +; CHECK: [[fuct_ptr2:%[\w]+]] = OpTypePointer Function [[struct2]] +; CHECK: [[fuct_ptr1:%[\w]+]] = OpTypePointer Function [[struct1]] +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[fuct_ptr1]] Function +; CHECK: OpLoad [[struct1]] [[newvar]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %struct1 = OpTypeStruct %5 + %struct2 = OpTypeStruct %5 + %6 = OpTypePointer Private %struct1 + %func_ptr2 = OpTypePointer Function %struct2 + %8 = OpVariable %6 Private + %2 = OpFunction %3 None %4 + %7 = OpLabel + %9 = OpLoad %struct1 %8 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(PrivateToLocalTest, CreatePointerToAmbiguousStruct2) { + // Test that the correct pointer type is picked up. + const std::string text = R"( +; CHECK: [[struct1:%[a-zA-Z_\d]+]] = OpTypeStruct +; CHECK: [[struct2:%[a-zA-Z_\d]+]] = OpTypeStruct +; CHECK: [[priv_ptr:%[\w]+]] = OpTypePointer Private [[struct2]] +; CHECK: [[fuct_ptr1:%[\w]+]] = OpTypePointer Function [[struct1]] +; CHECK: [[fuct_ptr2:%[\w]+]] = OpTypePointer Function [[struct2]] +; CHECK: OpFunction +; CHECK: OpLabel +; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[fuct_ptr2]] Function +; CHECK: OpLoad [[struct2]] [[newvar]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %struct1 = OpTypeStruct %5 + %struct2 = OpTypeStruct %5 + %6 = OpTypePointer Private %struct2 + %func_ptr2 = OpTypePointer Function %struct1 + %8 = OpVariable %6 Private + %2 = OpFunction %3 None %4 + %7 = OpLabel + %9 = OpLoad %struct2 %8 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +TEST_F(PrivateToLocalTest, SPV14RemoveFromInterface) { + const std::string text = R"( +; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv +; CHECK: OpEntryPoint GLCompute %foo "foo" %in +; CHECK: %priv = OpVariable {{%\w+}} Function +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" %in %priv +OpExecutionMode %foo LocalSize 1 1 1 +OpName %foo "foo" +OpName %in "in" +OpName %priv "priv" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ptr_private_int = OpTypePointer Private %int +%in = OpVariable %ptr_ssbo_int StorageBuffer +%priv = OpVariable %ptr_private_int Private +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %int %in +OpStore %priv %ld +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleEntryPoints) { + const std::string text = R"( +; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv +; CHECK-NOT: OpEntryPoint GLCompute %foo "bar" %in %priv +; CHECK: OpEntryPoint GLCompute %foo "foo" %in +; CHECK: OpEntryPoint GLCompute %foo "bar" %in +; CHECK: %priv = OpVariable {{%\w+}} Function +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" %in %priv +OpEntryPoint GLCompute %foo "bar" %in %priv +OpExecutionMode %foo LocalSize 1 1 1 +OpName %foo "foo" +OpName %in "in" +OpName %priv "priv" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ptr_private_int = OpTypePointer Private %int +%in = OpVariable %ptr_ssbo_int StorageBuffer +%priv = OpVariable %ptr_private_int Private +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpLoad %int %in +OpStore %priv %ld +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(PrivateToLocalTest, SPV14RemoveFromInterfaceMultipleVariables) { + const std::string text = R"( +; CHECK-NOT: OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2 +; CHECK: OpEntryPoint GLCompute %foo "foo" %in +; CHECK: %priv1 = OpVariable {{%\w+}} Function +; CHECK: %priv2 = OpVariable {{%\w+}} Function +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" %in %priv1 %priv2 +OpExecutionMode %foo LocalSize 1 1 1 +OpName %foo "foo" +OpName %in "in" +OpName %priv1 "priv1" +OpName %priv2 "priv2" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ptr_private_int = OpTypePointer Private %int +%in = OpVariable %ptr_ssbo_int StorageBuffer +%priv1 = OpVariable %ptr_private_int Private +%priv2 = OpVariable %ptr_private_int Private +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%1 = OpFunctionCall %void %bar1 +%2 = OpFunctionCall %void %bar2 +OpReturn +OpFunctionEnd +%bar1 = OpFunction %void None %void_fn +%3 = OpLabel +%ld1 = OpLoad %int %in +OpStore %priv1 %ld1 +OpReturn +OpFunctionEnd +%bar2 = OpFunction %void None %void_fn +%4 = OpLabel +%ld2 = OpLoad %int %in +OpStore %priv2 %ld2 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(PrivateToLocalTest, IdBoundOverflow1) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginLowerLeft + OpSource HLSL 84 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeStruct %7 + %4194302 = OpTypeStruct %8 %8 + %9 = OpTypeStruct %8 %8 + %11 = OpTypePointer Private %7 + %18 = OpTypeStruct %6 %9 + %12 = OpVariable %11 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpLoad %7 %12 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(PrivateToLocalTest, DebugPrivateToLocal) { + // Debug instructions must not have any impact on changing the private + // variable to a local. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + %10 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %11 = OpString "test" + OpSource GLSL 430 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 32 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 +; CHECK: [[float:%[a-zA-Z_\d]+]] = OpTypeFloat 32 + %5 = OpTypeFloat 32 +; CHECK: [[newtype:%[a-zA-Z_\d]+]] = OpTypePointer Function [[float]] + %6 = OpTypePointer Private %5 +; CHECK-NOT: OpVariable [[.+]] Private + %8 = OpVariable %6 Private + + %12 = OpExtInst %3 %10 DebugTypeBasic %11 %14 Float + %15 = OpExtInst %3 %10 DebugSource %11 + %16 = OpExtInst %3 %10 DebugCompilationUnit 1 4 %15 GLSL +; CHECK-NOT: DebugGlobalVariable +; CHECK: [[dbg_newvar:%[a-zA-Z_\d]+]] = OpExtInst {{%\w+}} {{%\w+}} DebugLocalVariable + %17 = OpExtInst %3 %10 DebugGlobalVariable %11 %12 %15 0 0 %16 %11 %8 FlagIsDefinition + +; CHECK: OpFunction + %2 = OpFunction %3 None %4 +; CHECK: OpLabel + %7 = OpLabel +; CHECK-NEXT: [[newvar:%[a-zA-Z_\d]+]] = OpVariable [[newtype]] Function +; CHECK-NEXT: DebugDeclare [[dbg_newvar]] [[newvar]] +; CHECK: OpLoad [[float]] [[newvar]] + %9 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/propagator_test.cpp b/third_party/spirv-tools/test/opt/propagator_test.cpp new file mode 100644 index 0000000..fb8e487 --- /dev/null +++ b/third_party/spirv-tools/test/opt/propagator_test.cpp @@ -0,0 +1,219 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/cfg.h" +#include "source/opt/ir_context.h" +#include "source/opt/pass.h" +#include "source/opt/propagator.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; + +class PropagatorTest : public testing::Test { + protected: + virtual void TearDown() { + ctx_.reset(nullptr); + values_.clear(); + values_vec_.clear(); + } + + void Assemble(const std::string& input) { + ctx_ = BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, input); + ASSERT_NE(nullptr, ctx_) << "Assembling failed for shader:\n" + << input << "\n"; + } + + bool Propagate(const SSAPropagator::VisitFunction& visit_fn) { + SSAPropagator propagator(ctx_.get(), visit_fn); + bool retval = false; + for (auto& fn : *ctx_->module()) { + retval |= propagator.Run(&fn); + } + return retval; + } + + const std::vector& GetValues() { + values_vec_.clear(); + for (const auto& it : values_) { + values_vec_.push_back(it.second); + } + return values_vec_; + } + + std::unique_ptr ctx_; + std::map values_; + std::vector values_vec_; +}; + +TEST_F(PropagatorTest, LocalPropagate) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %outparm + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %x "x" + OpName %y "y" + OpName %z "z" + OpName %outparm "outparm" + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_4 = OpConstant %int 4 + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %3 + %5 = OpLabel + %x = OpVariable %_ptr_Function_int Function + %y = OpVariable %_ptr_Function_int Function + %z = OpVariable %_ptr_Function_int Function + OpStore %x %int_4 + OpStore %y %int_3 + OpStore %z %int_1 + %20 = OpLoad %int %z + OpStore %outparm %20 + OpReturn + OpFunctionEnd + )"; + Assemble(spv_asm); + + const auto visit_fn = [this](Instruction* instr, BasicBlock** dest_bb) { + *dest_bb = nullptr; + if (instr->opcode() == SpvOpStore) { + uint32_t lhs_id = instr->GetSingleWordOperand(0); + uint32_t rhs_id = instr->GetSingleWordOperand(1); + Instruction* rhs_def = ctx_->get_def_use_mgr()->GetDef(rhs_id); + if (rhs_def->opcode() == SpvOpConstant) { + uint32_t val = rhs_def->GetSingleWordOperand(2); + values_[lhs_id] = val; + return SSAPropagator::kInteresting; + } + } + return SSAPropagator::kVarying; + }; + + EXPECT_TRUE(Propagate(visit_fn)); + EXPECT_THAT(GetValues(), UnorderedElementsAre(4, 3, 1)); +} + +TEST_F(PropagatorTest, PropagateThroughPhis) { + const std::string spv_asm = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %x %outparm + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %x "x" + OpName %outparm "outparm" + OpDecorate %x Flat + OpDecorate %x Location 0 + OpDecorate %outparm Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %bool = OpTypeBool +%_ptr_Function_int = OpTypePointer Function %int + %int_4 = OpConstant %int 4 + %int_3 = OpConstant %int 3 + %int_1 = OpConstant %int 1 +%_ptr_Input_int = OpTypePointer Input %int + %x = OpVariable %_ptr_Input_int Input +%_ptr_Output_int = OpTypePointer Output %int + %outparm = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %3 + %4 = OpLabel + %5 = OpLoad %int %x + %6 = OpSGreaterThan %bool %5 %int_3 + OpSelectionMerge %25 None + OpBranchConditional %6 %22 %23 + %22 = OpLabel + %7 = OpLoad %int %int_4 + OpBranch %25 + %23 = OpLabel + %8 = OpLoad %int %int_4 + OpBranch %25 + %25 = OpLabel + %35 = OpPhi %int %7 %22 %8 %23 + OpStore %outparm %35 + OpReturn + OpFunctionEnd + )"; + + Assemble(spv_asm); + + Instruction* phi_instr = nullptr; + const auto visit_fn = [this, &phi_instr](Instruction* instr, + BasicBlock** dest_bb) { + *dest_bb = nullptr; + if (instr->opcode() == SpvOpLoad) { + uint32_t rhs_id = instr->GetSingleWordOperand(2); + Instruction* rhs_def = ctx_->get_def_use_mgr()->GetDef(rhs_id); + if (rhs_def->opcode() == SpvOpConstant) { + uint32_t val = rhs_def->GetSingleWordOperand(2); + values_[instr->result_id()] = val; + return SSAPropagator::kInteresting; + } + } else if (instr->opcode() == SpvOpPhi) { + phi_instr = instr; + SSAPropagator::PropStatus retval; + for (uint32_t i = 2; i < instr->NumOperands(); i += 2) { + uint32_t phi_arg_id = instr->GetSingleWordOperand(i); + auto it = values_.find(phi_arg_id); + if (it != values_.end()) { + EXPECT_EQ(it->second, 4u); + retval = SSAPropagator::kInteresting; + values_[instr->result_id()] = it->second; + } else { + retval = SSAPropagator::kNotInteresting; + break; + } + } + return retval; + } + + return SSAPropagator::kVarying; + }; + + EXPECT_TRUE(Propagate(visit_fn)); + + // The propagator should've concluded that the Phi instruction has a constant + // value of 4. + EXPECT_NE(phi_instr, nullptr); + EXPECT_EQ(values_[phi_instr->result_id()], 4u); + + EXPECT_THAT(GetValues(), UnorderedElementsAre(4u, 4u, 4u)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/reduce_load_size_test.cpp b/third_party/spirv-tools/test/opt/reduce_load_size_test.cpp new file mode 100644 index 0000000..7672e8f --- /dev/null +++ b/third_party/spirv-tools/test/opt/reduce_load_size_test.cpp @@ -0,0 +1,424 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ReduceLoadSizeTest = PassTest<::testing::Test>; + +TEST_F(ReduceLoadSizeTest, cbuffer_load_extract) { + // Originally from the following HLSL: + // struct S { + // uint f; + // }; + // + // + // cbuffer gBuffer { uint a[32]; }; + // + // RWStructuredBuffer gRWSBuffer; + // + // uint foo(uint p[32]) { + // return p[1]; + // } + // + // [numthreads(1,1,1)] + // void main() { + // gRWSBuffer[0].f = foo(a); + // } + const std::string test = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 600 + OpName %type_gBuffer "type.gBuffer" + OpMemberName %type_gBuffer 0 "a" + OpName %gBuffer "gBuffer" + OpName %S "S" + OpMemberName %S 0 "f" + OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" + OpName %gRWSBuffer "gRWSBuffer" + OpName %main "main" + OpDecorate %_arr_uint_uint_32 ArrayStride 16 + OpMemberDecorate %type_gBuffer 0 Offset 0 + OpDecorate %type_gBuffer Block + OpMemberDecorate %S 0 Offset 0 + OpDecorate %_runtimearr_S ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_S BufferBlock + OpDecorate %gBuffer DescriptorSet 0 + OpDecorate %gBuffer Binding 0 + OpDecorate %gRWSBuffer DescriptorSet 0 + OpDecorate %gRWSBuffer Binding 1 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 +%type_gBuffer = OpTypeStruct %_arr_uint_uint_32 +%_ptr_Uniform_type_gBuffer = OpTypePointer Uniform %type_gBuffer + %S = OpTypeStruct %uint +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %15 = OpTypeFunction %void + %int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_uint_uint_32 = OpTypePointer Uniform %_arr_uint_uint_32 + %uint_0 = OpConstant %uint 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %gBuffer = OpVariable %_ptr_Uniform_type_gBuffer Uniform + %gRWSBuffer = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform + %main = OpFunction %void None %15 + %20 = OpLabel +; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %gBuffer %int_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1 +; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[ac2]] +; CHECK: OpStore {{%\w+}} [[ld]] + %21 = OpAccessChain %_ptr_Uniform__arr_uint_uint_32 %gBuffer %int_0 + %22 = OpLoad %_arr_uint_uint_32 %21 ; Load of 32-element array. + %23 = OpCompositeExtract %uint %22 1 + %24 = OpAccessChain %_ptr_Uniform_uint %gRWSBuffer %int_0 %uint_0 %int_0 + OpStore %24 %23 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(test, false); +} + +TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_not_affected_by_debug_instr) { + // Originally from the following HLSL: + // struct S { + // uint f; + // }; + // + // + // cbuffer gBuffer { uint a[32]; }; + // + // RWStructuredBuffer gRWSBuffer; + // + // uint foo(uint p[32]) { + // return p[1]; + // } + // + // [numthreads(1,1,1)] + // void main() { + // gRWSBuffer[0].f = foo(a); + // } + const std::string test = + R"( + OpCapability Shader + %ext = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource HLSL 600 + %file_name = OpString "test" + %float_name = OpString "float" + %main_name = OpString "main" + %f_name = OpString "f" + OpName %type_gBuffer "type.gBuffer" + OpMemberName %type_gBuffer 0 "a" + OpName %gBuffer "gBuffer" + OpName %S "S" + OpMemberName %S 0 "f" + OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" + OpName %gRWSBuffer "gRWSBuffer" + OpName %main "main" + OpDecorate %_arr_uint_uint_32 ArrayStride 16 + OpMemberDecorate %type_gBuffer 0 Offset 0 + OpDecorate %type_gBuffer Block + OpMemberDecorate %S 0 Offset 0 + OpDecorate %_runtimearr_S ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_S BufferBlock + OpDecorate %gBuffer DescriptorSet 0 + OpDecorate %gBuffer Binding 0 + OpDecorate %gRWSBuffer DescriptorSet 0 + OpDecorate %gRWSBuffer Binding 1 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_arr_uint_uint_32 = OpTypeArray %uint %uint_32 +%type_gBuffer = OpTypeStruct %_arr_uint_uint_32 +%_ptr_Uniform_type_gBuffer = OpTypePointer Uniform %type_gBuffer + %S = OpTypeStruct %uint +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %15 = OpTypeFunction %void + %int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_uint_uint_32 = OpTypePointer Uniform %_arr_uint_uint_32 + %uint_0 = OpConstant %uint 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %gBuffer = OpVariable %_ptr_Uniform_type_gBuffer Uniform + %gRWSBuffer = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform + %null_expr = OpExtInst %void %ext DebugExpression + %src = OpExtInst %void %ext DebugSource %file_name + %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL + %dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float + %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf + %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main + %dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal + %main = OpFunction %void None %15 + %20 = OpLabel + %s = OpExtInst %void %ext DebugScope %dbg_main +; CHECK: [[ac1:%\w+]] = OpAccessChain {{%\w+}} %gBuffer %int_0 +; CHECK: [[ac2:%\w+]] = OpAccessChain {{%\w+}} [[ac1]] %uint_1 +; CHECK: [[ld:%\w+]] = OpLoad {{%\w+}} [[ac2]] +; CHECK: OpStore {{%\w+}} [[ld]] + %21 = OpAccessChain %_ptr_Uniform__arr_uint_uint_32 %gBuffer %int_0 + %22 = OpLoad %_arr_uint_uint_32 %21 ; Load of 32-element array. + %value = OpExtInst %void %ext DebugValue %dbg_f %22 %null_expr + %23 = OpCompositeExtract %uint %22 1 + %24 = OpAccessChain %_ptr_Uniform_uint %gRWSBuffer %int_0 %uint_0 %int_0 + OpStore %24 %23 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(test, false); +} + +TEST_F(ReduceLoadSizeTest, cbuffer_load_extract_vector) { + // Originally from the following HLSL: + // struct S { + // uint f; + // }; + // + // + // cbuffer gBuffer { uint4 a; }; + // + // RWStructuredBuffer gRWSBuffer; + // + // uint foo(uint p[32]) { + // return p[1]; + // } + // + // [numthreads(1,1,1)] + // void main() { + // gRWSBuffer[0].f = foo(a); + // } + const std::string test = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource HLSL 600 +OpName %type_gBuffer "type.gBuffer" +OpMemberName %type_gBuffer 0 "a" +OpName %gBuffer "gBuffer" +OpName %S "S" +OpMemberName %S 0 "f" +OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" +OpName %gRWSBuffer "gRWSBuffer" +OpName %main "main" +OpMemberDecorate %type_gBuffer 0 Offset 0 +OpDecorate %type_gBuffer Block +OpMemberDecorate %S 0 Offset 0 +OpDecorate %_runtimearr_S ArrayStride 4 +OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 +OpDecorate %type_RWStructuredBuffer_S BufferBlock +OpDecorate %gBuffer DescriptorSet 0 +OpDecorate %gBuffer Binding 0 +OpDecorate %gRWSBuffer DescriptorSet 0 +OpDecorate %gRWSBuffer Binding 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%v4uint = OpTypeVector %uint 4 +%type_gBuffer = OpTypeStruct %v4uint +%_ptr_Uniform_type_gBuffer = OpTypePointer Uniform %type_gBuffer +%S = OpTypeStruct %uint +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S +%int = OpTypeInt 32 1 +%void = OpTypeVoid +%15 = OpTypeFunction %void +%int_0 = OpConstant %int 0 +%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%gBuffer = OpVariable %_ptr_Uniform_type_gBuffer Uniform +%gRWSBuffer = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform +%main = OpFunction %void None %15 +%20 = OpLabel +%21 = OpAccessChain %_ptr_Uniform_v4uint %gBuffer %int_0 +%22 = OpLoad %v4uint %21 +%23 = OpCompositeExtract %uint %22 1 +%24 = OpAccessChain %_ptr_Uniform_uint %gRWSBuffer %int_0 %uint_0 %int_0 +OpStore %24 %23 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndCheck(test, test, true, false); +} + +TEST_F(ReduceLoadSizeTest, cbuffer_load_5_extract) { + // All of the elements of the value loaded are used, so we should not + // change the load. + const std::string test = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource HLSL 600 +OpName %type_gBuffer "type.gBuffer" +OpMemberName %type_gBuffer 0 "a" +OpName %gBuffer "gBuffer" +OpName %S "S" +OpMemberName %S 0 "f" +OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" +OpName %gRWSBuffer "gRWSBuffer" +OpName %main "main" +OpDecorate %_arr_uint_uint_5 ArrayStride 16 +OpMemberDecorate %type_gBuffer 0 Offset 0 +OpDecorate %type_gBuffer Block +OpMemberDecorate %S 0 Offset 0 +OpDecorate %_runtimearr_S ArrayStride 4 +OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 +OpDecorate %type_RWStructuredBuffer_S BufferBlock +OpDecorate %gBuffer DescriptorSet 0 +OpDecorate %gBuffer Binding 0 +OpDecorate %gRWSBuffer DescriptorSet 0 +OpDecorate %gRWSBuffer Binding 1 +%uint = OpTypeInt 32 0 +%uint_5 = OpConstant %uint 5 +%_arr_uint_uint_5 = OpTypeArray %uint %uint_5 +%type_gBuffer = OpTypeStruct %_arr_uint_uint_5 +%_ptr_Uniform_type_gBuffer = OpTypePointer Uniform %type_gBuffer +%S = OpTypeStruct %uint +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S +%int = OpTypeInt 32 1 +%void = OpTypeVoid +%15 = OpTypeFunction %void +%int_0 = OpConstant %int 0 +%_ptr_Uniform__arr_uint_uint_5 = OpTypePointer Uniform %_arr_uint_uint_5 +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%gBuffer = OpVariable %_ptr_Uniform_type_gBuffer Uniform +%gRWSBuffer = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform +%main = OpFunction %void None %15 +%20 = OpLabel +%21 = OpAccessChain %_ptr_Uniform__arr_uint_uint_5 %gBuffer %int_0 +%22 = OpLoad %_arr_uint_uint_5 %21 +%23 = OpCompositeExtract %uint %22 0 +%24 = OpCompositeExtract %uint %22 1 +%25 = OpCompositeExtract %uint %22 2 +%26 = OpCompositeExtract %uint %22 3 +%27 = OpCompositeExtract %uint %22 4 +%28 = OpIAdd %uint %23 %24 +%29 = OpIAdd %uint %28 %25 +%30 = OpIAdd %uint %29 %26 +%31 = OpIAdd %uint %20 %27 +%32 = OpAccessChain %_ptr_Uniform_uint %gRWSBuffer %int_0 %uint_0 %int_0 +OpStore %32 %31 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndCheck(test, test, true, false); +} + +TEST_F(ReduceLoadSizeTest, cbuffer_load_fully_used) { + // The result of the load (%22) is used in an instruction that uses the whole + // load and has only 1 in operand. This trigger issue #1559. + const std::string test = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource HLSL 600 +OpName %type_gBuffer "type.gBuffer" +OpMemberName %type_gBuffer 0 "a" +OpName %gBuffer "gBuffer" +OpName %S "S" +OpMemberName %S 0 "f" +OpName %type_RWStructuredBuffer_S "type.RWStructuredBuffer.S" +OpName %gRWSBuffer "gRWSBuffer" +OpName %main "main" +OpMemberDecorate %type_gBuffer 0 Offset 0 +OpDecorate %type_gBuffer Block +OpMemberDecorate %S 0 Offset 0 +OpDecorate %_runtimearr_S ArrayStride 4 +OpMemberDecorate %type_RWStructuredBuffer_S 0 Offset 0 +OpDecorate %type_RWStructuredBuffer_S BufferBlock +OpDecorate %gBuffer DescriptorSet 0 +OpDecorate %gBuffer Binding 0 +OpDecorate %gRWSBuffer DescriptorSet 0 +OpDecorate %gRWSBuffer Binding 1 +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%v4uint = OpTypeVector %uint 4 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%type_gBuffer = OpTypeStruct %v4uint +%_ptr_Uniform_type_gBuffer = OpTypePointer Uniform %type_gBuffer +%S = OpTypeStruct %uint +%_runtimearr_S = OpTypeRuntimeArray %S +%type_RWStructuredBuffer_S = OpTypeStruct %_runtimearr_S +%_ptr_Uniform_type_RWStructuredBuffer_S = OpTypePointer Uniform %type_RWStructuredBuffer_S +%int = OpTypeInt 32 1 +%void = OpTypeVoid +%15 = OpTypeFunction %void +%int_0 = OpConstant %int 0 +%_ptr_Uniform_v4uint = OpTypePointer Uniform %v4uint +%uint_0 = OpConstant %uint 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%gBuffer = OpVariable %_ptr_Uniform_type_gBuffer Uniform +%gRWSBuffer = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_S Uniform +%main = OpFunction %void None %15 +%20 = OpLabel +%21 = OpAccessChain %_ptr_Uniform_v4uint %gBuffer %int_0 +%22 = OpLoad %v4uint %21 +%23 = OpCompositeExtract %uint %22 1 +%24 = OpConvertUToF %v4float %22 +%25 = OpAccessChain %_ptr_Uniform_uint %gRWSBuffer %int_0 %uint_0 %int_0 +OpStore %25 %23 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndCheck(test, test, true, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/redundancy_elimination_test.cpp b/third_party/spirv-tools/test/opt/redundancy_elimination_test.cpp new file mode 100644 index 0000000..474f466 --- /dev/null +++ b/third_party/spirv-tools/test/opt/redundancy_elimination_test.cpp @@ -0,0 +1,340 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/build_module.h" +#include "source/opt/value_number_table.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; +using RedundancyEliminationTest = PassTest<::testing::Test>; + +// Test that it can get a simple case of local redundancy elimination. +// The rest of the test check for extra functionality. +TEST_F(RedundancyEliminationTest, RemoveRedundantLocalAdd) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 +; CHECK: OpFAdd +; CHECK-NOT: OpFAdd + %11 = OpFAdd %5 %9 %9 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +// Remove a redundant add across basic blocks. +TEST_F(RedundancyEliminationTest, RemoveRedundantAdd) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 + OpBranch %11 + %11 = OpLabel +; CHECK: OpFAdd +; CHECK-NOT: OpFAdd + %12 = OpFAdd %5 %9 %9 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +// Remove a redundant add going through a multiple basic blocks. +TEST_F(RedundancyEliminationTest, RemoveRedundantAddDiamond) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + %10 = OpVariable %6 Function + %11 = OpLoad %5 %10 + %12 = OpFAdd %5 %11 %11 +; CHECK: OpFAdd +; CHECK-NOT: OpFAdd + OpBranchConditional %8 %13 %14 + %13 = OpLabel + OpBranch %15 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + %16 = OpFAdd %5 %11 %11 + OpReturn + OpFunctionEnd + + )"; + SinglePassRunAndMatch(text, false); +} + +// Remove a redundant add in a side node. +TEST_F(RedundancyEliminationTest, RemoveRedundantAddInSideNode) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + %10 = OpVariable %6 Function + %11 = OpLoad %5 %10 + %12 = OpFAdd %5 %11 %11 +; CHECK: OpFAdd +; CHECK-NOT: OpFAdd + OpBranchConditional %8 %13 %14 + %13 = OpLabel + OpBranch %15 + %14 = OpLabel + %16 = OpFAdd %5 %11 %11 + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + + )"; + SinglePassRunAndMatch(text, false); +} + +// Remove a redundant add whose value is in the result of a phi node. +TEST_F(RedundancyEliminationTest, RemoveRedundantAddWithPhi) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + %10 = OpVariable %6 Function + %11 = OpLoad %5 %10 + OpBranchConditional %8 %13 %14 + %13 = OpLabel + %add1 = OpFAdd %5 %11 %11 +; CHECK: OpFAdd + OpBranch %15 + %14 = OpLabel + %add2 = OpFAdd %5 %11 %11 +; CHECK: OpFAdd + OpBranch %15 + %15 = OpLabel +; CHECK: OpPhi + %phi = OpPhi %5 %add1 %13 %add2 %14 +; CHECK-NOT: OpFAdd + %16 = OpFAdd %5 %11 %11 + OpReturn + OpFunctionEnd + + )"; + SinglePassRunAndMatch(text, false); +} + +// Keep the add because it is redundant on some paths, but not all paths. +TEST_F(RedundancyEliminationTest, KeepPartiallyRedundantAdd) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + %10 = OpVariable %6 Function + %11 = OpLoad %5 %10 + OpBranchConditional %8 %13 %14 + %13 = OpLabel + %add = OpFAdd %5 %11 %11 + OpBranch %15 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + %16 = OpFAdd %5 %11 %11 + OpReturn + OpFunctionEnd + + )"; + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// Keep the add. Even if it is redundant on all paths, there is no single id +// whose definition dominates the add and contains the same value. +TEST_F(RedundancyEliminationTest, KeepRedundantAddWithoutPhi) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + %10 = OpVariable %6 Function + %11 = OpLoad %5 %10 + OpBranchConditional %8 %13 %14 + %13 = OpLabel + %add1 = OpFAdd %5 %11 %11 + OpBranch %15 + %14 = OpLabel + %add2 = OpFAdd %5 %11 %11 + OpBranch %15 + %15 = OpLabel + %16 = OpFAdd %5 %11 %11 + OpReturn + OpFunctionEnd + + )"; + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// Test that it can get a simple case of local redundancy elimination +// when it has OpenCL.DebugInfo.100 instructions. +TEST_F(RedundancyEliminationTest, OpenCLDebugInfo100) { + // When three redundant DebugValues exist, only one DebugValue must remain. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + %2 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %3 "main" + OpExecutionMode %3 OriginUpperLeft + OpSource GLSL 430 + %4 = OpString "ps.hlsl" + %5 = OpString "float" + %6 = OpString "s0" + %7 = OpString "main" + %void = OpTypeVoid + %9 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Function_float = OpTypePointer Function %float + %15 = OpExtInst %void %1 DebugExpression + %16 = OpExtInst %void %1 DebugSource %4 + %17 = OpExtInst %void %1 DebugCompilationUnit 1 4 %16 HLSL + %18 = OpExtInst %void %1 DebugTypeBasic %5 %uint_32 Float + %19 = OpExtInst %void %1 DebugTypeVector %18 4 + %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %19 + %21 = OpExtInst %void %1 DebugFunction %7 %20 %16 4 1 %17 %7 FlagIsProtected|FlagIsPrivate 4 %3 +; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void {{%\w+}} DebugLocalVariable + %22 = OpExtInst %void %1 DebugLocalVariable %6 %19 %16 0 0 %21 FlagIsLocal + %14 = OpExtInst %void %1 DebugLocalVariable %6 %19 %16 0 0 %21 FlagIsLocal + %3 = OpFunction %void None %9 + %23 = OpLabel + %24 = OpExtInst %void %1 DebugScope %21 + %25 = OpVariable %_ptr_Function_float Function + %26 = OpLoad %float %25 + OpLine %4 0 0 +; Two `OpFAdd %float %26 %26` are the same. One must be removed. +; After removing one `OpFAdd %float %26 %26`, two DebugValues are the same. +; One must be removed. +; +; CHECK: OpLine {{%\w+}} 0 0 +; CHECK-NEXT: [[add:%\w+]] = OpFAdd %float [[value:%\w+]] +; CHECK-NEXT: DebugValue [[dbg_local_var]] [[add]] +; CHECK-NEXT: OpLine {{%\w+}} 1 0 +; CHECK-NEXT: OpFAdd %float [[add]] [[value]] +; CHECK-NEXT: OpReturn + %27 = OpFAdd %float %26 %26 + %28 = OpExtInst %void %1 DebugValue %22 %27 %15 %uint_0 + OpLine %4 1 0 + %29 = OpFAdd %float %26 %26 + %30 = OpExtInst %void %1 DebugValue %14 %29 %15 %uint_0 + %31 = OpExtInst %void %1 DebugValue %22 %29 %15 %uint_0 + %32 = OpFAdd %float %29 %26 + %33 = OpFAdd %float %27 %26 + OpReturn + OpFunctionEnd + )"; + SinglePassRunAndMatch(text, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/register_liveness.cpp b/third_party/spirv-tools/test/opt/register_liveness.cpp new file mode 100644 index 0000000..7cb210f --- /dev/null +++ b/third_party/spirv-tools/test/opt/register_liveness.cpp @@ -0,0 +1,1322 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/register_pressure.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using PassClassTest = PassTest<::testing::Test>; + +void CompareSets(const std::unordered_set& computed, + const std::unordered_set& expected) { + for (Instruction* insn : computed) { + EXPECT_TRUE(expected.count(insn->result_id())) + << "Unexpected instruction in live set: " << *insn; + } + EXPECT_EQ(computed.size(), expected.size()); +} + +/* +Generated from the following GLSL + +#version 330 +in vec4 BaseColor; +flat in int Count; +void main() +{ + vec4 color = BaseColor; + vec4 acc; + if (Count == 0) { + acc = color; + } + else { + acc = color + vec4(0,1,2,0); + } + gl_FragColor = acc + color; +} +*/ +TEST_F(PassClassTest, LivenessWithIf) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %11 %15 %32 + OpExecutionMode %4 OriginLowerLeft + OpSource GLSL 330 + OpName %4 "main" + OpName %11 "BaseColor" + OpName %15 "Count" + OpName %32 "gl_FragColor" + OpDecorate %11 Location 0 + OpDecorate %15 Flat + OpDecorate %15 Location 0 + OpDecorate %32 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %10 = OpTypePointer Input %7 + %11 = OpVariable %10 Input + %13 = OpTypeInt 32 1 + %14 = OpTypePointer Input %13 + %15 = OpVariable %14 Input + %17 = OpConstant %13 0 + %18 = OpTypeBool + %26 = OpConstant %6 0 + %27 = OpConstant %6 1 + %28 = OpConstant %6 2 + %29 = OpConstantComposite %7 %26 %27 %28 %26 + %31 = OpTypePointer Output %7 + %32 = OpVariable %31 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %7 %11 + %16 = OpLoad %13 %15 + %19 = OpIEqual %18 %16 %17 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %24 + %20 = OpLabel + OpBranch %21 + %24 = OpLabel + %30 = OpFAdd %7 %12 %29 + OpBranch %21 + %21 = OpLabel + %36 = OpPhi %7 %12 %20 %30 %24 + %35 = OpFAdd %7 %36 %12 + OpStore %32 %35 + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function* f = &*module->begin(); + LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); + const RegisterLiveness* register_liveness = liveness_analysis->Get(f); + { + SCOPED_TRACE("Block 5"); + auto live_sets = register_liveness->Get(5); + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 15, // %15 = OpVariable %14 Input + 32, // %32 = OpVariable %31 Output + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 12, // %12 = OpLoad %7 %11 + 32, // %32 = OpVariable %31 Output + }; + CompareSets(live_sets->live_out_, live_out); + } + { + SCOPED_TRACE("Block 20"); + auto live_sets = register_liveness->Get(20); + std::unordered_set live_inout{ + 12, // %12 = OpLoad %7 %11 + 32, // %32 = OpVariable %31 Output + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + } + { + SCOPED_TRACE("Block 24"); + auto live_sets = register_liveness->Get(24); + std::unordered_set live_in{ + 12, // %12 = OpLoad %7 %11 + 32, // %32 = OpVariable %31 Output + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 12, // %12 = OpLoad %7 %11 + 30, // %30 = OpFAdd %7 %12 %29 + 32, // %32 = OpVariable %31 Output + }; + CompareSets(live_sets->live_out_, live_out); + } + { + SCOPED_TRACE("Block 21"); + auto live_sets = register_liveness->Get(21); + std::unordered_set live_in{ + 12, // %12 = OpLoad %7 %11 + 32, // %32 = OpVariable %31 Output + 36, // %36 = OpPhi %7 %12 %20 %30 %24 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{}; + CompareSets(live_sets->live_out_, live_out); + } +} + +/* +Generated from the following GLSL +#version 330 +in vec4 bigColor; +in vec4 BaseColor; +in float f; +flat in int Count; +flat in uvec4 v4; +void main() +{ + vec4 color = BaseColor; + for (int i = 0; i < Count; ++i) + color += bigColor; + float sum = 0.0; + for (int i = 0; i < 4; ++i) { + float acc = 0.0; + if (sum == 0.0) { + acc = v4[i]; + } + else { + acc = BaseColor[i]; + } + sum += acc + v4[i]; + } + vec4 tv4; + for (int i = 0; i < 4; ++i) + tv4[i] = v4[i] * 4u; + color += vec4(sum) + tv4; + vec4 r; + r.xyz = BaseColor.xyz; + for (int i = 0; i < Count; ++i) + r.w = f; + color.xyz += r.xyz; + for (int i = 0; i < 16; i += 4) + for (int j = 0; j < 4; j++) + color *= f; + gl_FragColor = color + tv4; +} +*/ +TEST_F(PassClassTest, RegisterLiveness) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %11 %24 %28 %55 %124 %176 + OpExecutionMode %4 OriginLowerLeft + OpSource GLSL 330 + OpName %4 "main" + OpName %11 "BaseColor" + OpName %24 "Count" + OpName %28 "bigColor" + OpName %55 "v4" + OpName %84 "tv4" + OpName %124 "f" + OpName %176 "gl_FragColor" + OpDecorate %11 Location 0 + OpDecorate %24 Flat + OpDecorate %24 Location 0 + OpDecorate %28 Location 0 + OpDecorate %55 Flat + OpDecorate %55 Location 0 + OpDecorate %124 Location 0 + OpDecorate %176 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Function %7 + %10 = OpTypePointer Input %7 + %11 = OpVariable %10 Input + %13 = OpTypeInt 32 1 + %16 = OpConstant %13 0 + %23 = OpTypePointer Input %13 + %24 = OpVariable %23 Input + %26 = OpTypeBool + %28 = OpVariable %10 Input + %33 = OpConstant %13 1 + %35 = OpTypePointer Function %6 + %37 = OpConstant %6 0 + %45 = OpConstant %13 4 + %52 = OpTypeInt 32 0 + %53 = OpTypeVector %52 4 + %54 = OpTypePointer Input %53 + %55 = OpVariable %54 Input + %57 = OpTypePointer Input %52 + %63 = OpTypePointer Input %6 + %89 = OpConstant %52 4 + %102 = OpTypeVector %6 3 + %124 = OpVariable %63 Input + %158 = OpConstant %13 16 + %175 = OpTypePointer Output %7 + %176 = OpVariable %175 Output + %195 = OpUndef %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %84 = OpVariable %8 Function + %12 = OpLoad %7 %11 + OpBranch %17 + %17 = OpLabel + %191 = OpPhi %7 %12 %5 %31 %18 + %184 = OpPhi %13 %16 %5 %34 %18 + %25 = OpLoad %13 %24 + %27 = OpSLessThan %26 %184 %25 + OpLoopMerge %19 %18 None + OpBranchConditional %27 %18 %19 + %18 = OpLabel + %29 = OpLoad %7 %28 + %31 = OpFAdd %7 %191 %29 + %34 = OpIAdd %13 %184 %33 + OpBranch %17 + %19 = OpLabel + OpBranch %39 + %39 = OpLabel + %188 = OpPhi %6 %37 %19 %73 %51 + %185 = OpPhi %13 %16 %19 %75 %51 + %46 = OpSLessThan %26 %185 %45 + OpLoopMerge %41 %51 None + OpBranchConditional %46 %40 %41 + %40 = OpLabel + %49 = OpFOrdEqual %26 %188 %37 + OpSelectionMerge %51 None + OpBranchConditional %49 %50 %61 + %50 = OpLabel + %58 = OpAccessChain %57 %55 %185 + %59 = OpLoad %52 %58 + %60 = OpConvertUToF %6 %59 + OpBranch %51 + %61 = OpLabel + %64 = OpAccessChain %63 %11 %185 + %65 = OpLoad %6 %64 + OpBranch %51 + %51 = OpLabel + %210 = OpPhi %6 %60 %50 %65 %61 + %68 = OpAccessChain %57 %55 %185 + %69 = OpLoad %52 %68 + %70 = OpConvertUToF %6 %69 + %71 = OpFAdd %6 %210 %70 + %73 = OpFAdd %6 %188 %71 + %75 = OpIAdd %13 %185 %33 + OpBranch %39 + %41 = OpLabel + OpBranch %77 + %77 = OpLabel + %186 = OpPhi %13 %16 %41 %94 %78 + %83 = OpSLessThan %26 %186 %45 + OpLoopMerge %79 %78 None + OpBranchConditional %83 %78 %79 + %78 = OpLabel + %87 = OpAccessChain %57 %55 %186 + %88 = OpLoad %52 %87 + %90 = OpIMul %52 %88 %89 + %91 = OpConvertUToF %6 %90 + %92 = OpAccessChain %35 %84 %186 + OpStore %92 %91 + %94 = OpIAdd %13 %186 %33 + OpBranch %77 + %79 = OpLabel + %96 = OpCompositeConstruct %7 %188 %188 %188 %188 + %97 = OpLoad %7 %84 + %98 = OpFAdd %7 %96 %97 + %100 = OpFAdd %7 %191 %98 + %104 = OpVectorShuffle %102 %12 %12 0 1 2 + %106 = OpVectorShuffle %7 %195 %104 4 5 6 3 + OpBranch %108 + %108 = OpLabel + %197 = OpPhi %7 %106 %79 %208 %133 + %196 = OpPhi %13 %16 %79 %143 %133 + %115 = OpSLessThan %26 %196 %25 + OpLoopMerge %110 %133 None + OpBranchConditional %115 %109 %110 + %109 = OpLabel + OpBranch %117 + %117 = OpLabel + %209 = OpPhi %7 %197 %109 %181 %118 + %204 = OpPhi %13 %16 %109 %129 %118 + %123 = OpSLessThan %26 %204 %45 + OpLoopMerge %119 %118 None + OpBranchConditional %123 %118 %119 + %118 = OpLabel + %125 = OpLoad %6 %124 + %181 = OpCompositeInsert %7 %125 %209 3 + %129 = OpIAdd %13 %204 %33 + OpBranch %117 + %119 = OpLabel + OpBranch %131 + %131 = OpLabel + %208 = OpPhi %7 %209 %119 %183 %132 + %205 = OpPhi %13 %16 %119 %141 %132 + %137 = OpSLessThan %26 %205 %45 + OpLoopMerge %133 %132 None + OpBranchConditional %137 %132 %133 + %132 = OpLabel + %138 = OpLoad %6 %124 + %183 = OpCompositeInsert %7 %138 %208 3 + %141 = OpIAdd %13 %205 %33 + OpBranch %131 + %133 = OpLabel + %143 = OpIAdd %13 %196 %33 + OpBranch %108 + %110 = OpLabel + %145 = OpVectorShuffle %102 %197 %197 0 1 2 + %147 = OpVectorShuffle %102 %100 %100 0 1 2 + %148 = OpFAdd %102 %147 %145 + %150 = OpVectorShuffle %7 %100 %148 4 5 6 3 + OpBranch %152 + %152 = OpLabel + %200 = OpPhi %7 %150 %110 %203 %163 + %199 = OpPhi %13 %16 %110 %174 %163 + %159 = OpSLessThan %26 %199 %158 + OpLoopMerge %154 %163 None + OpBranchConditional %159 %153 %154 + %153 = OpLabel + OpBranch %161 + %161 = OpLabel + %203 = OpPhi %7 %200 %153 %170 %162 + %201 = OpPhi %13 %16 %153 %172 %162 + %167 = OpSLessThan %26 %201 %45 + OpLoopMerge %163 %162 None + OpBranchConditional %167 %162 %163 + %162 = OpLabel + %168 = OpLoad %6 %124 + %170 = OpVectorTimesScalar %7 %203 %168 + %172 = OpIAdd %13 %201 %33 + OpBranch %161 + %163 = OpLabel + %174 = OpIAdd %13 %199 %45 + OpBranch %152 + %154 = OpLabel + %178 = OpLoad %7 %84 + %179 = OpFAdd %7 %200 %178 + OpStore %176 %179 + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function* f = &*module->begin(); + LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); + const RegisterLiveness* register_liveness = liveness_analysis->Get(f); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + + { + SCOPED_TRACE("Block 5"); + auto live_sets = register_liveness->Get(5); + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 24, // %24 = OpVariable %23 Input + 28, // %28 = OpVariable %10 Input + 55, // %55 = OpVariable %54 Input + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 24, // %24 = OpVariable %23 Input + 28, // %28 = OpVariable %10 Input + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 8u); + } + { + SCOPED_TRACE("Block 17"); + auto live_sets = register_liveness->Get(17); + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 24, // %24 = OpVariable %23 Input + 28, // %28 = OpVariable %10 Input + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 184, // %184 = OpPhi %13 %16 %5 %34 %18 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 28, // %28 = OpVariable %10 Input + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 184, // %184 = OpPhi %13 %16 %5 %34 %18 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 11u); + } + { + SCOPED_TRACE("Block 18"); + auto live_sets = register_liveness->Get(18); + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 24, // %24 = OpVariable %23 Input + 28, // %28 = OpVariable %10 Input + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 184, // %184 = OpPhi %13 %16 %5 %34 %18 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 24, // %24 = OpVariable %23 Input + 28, // %28 = OpVariable %10 Input + 31, // %31 = OpFAdd %7 %191 %29 + 34, // %34 = OpIAdd %13 %184 %33 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 12u); + } + { + SCOPED_TRACE("Block 19"); + auto live_sets = register_liveness->Get(19); + std::unordered_set live_inout{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 8u); + } + { + SCOPED_TRACE("Block 39"); + auto live_sets = register_liveness->Get(39); + std::unordered_set live_inout{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 11u); + } + { + SCOPED_TRACE("Block 40"); + auto live_sets = register_liveness->Get(40); + std::unordered_set live_inout{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 11u); + } + { + SCOPED_TRACE("Block 50"); + auto live_sets = register_liveness->Get(50); + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 60, // %60 = OpConvertUToF %6 %59 + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 12u); + } + { + SCOPED_TRACE("Block 61"); + auto live_sets = register_liveness->Get(61); + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 65, // %65 = OpLoad %6 %64 + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 12u); + } + { + SCOPED_TRACE("Block 51"); + auto live_sets = register_liveness->Get(51); + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + 210, // %210 = OpPhi %6 %60 %50 %65 %61 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 73, // %73 = OpFAdd %6 %188 %71 + 75, // %75 = OpIAdd %13 %185 %33 + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 13u); + } + { + SCOPED_TRACE("Block 41"); + auto live_sets = register_liveness->Get(41); + std::unordered_set live_inout{ + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 8u); + } + { + SCOPED_TRACE("Block 77"); + auto live_sets = register_liveness->Get(77); + std::unordered_set live_inout{ + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 186, // %186 = OpPhi %13 %16 %41 %94 %78 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 10u); + } + { + SCOPED_TRACE("Block 78"); + auto live_sets = register_liveness->Get(78); + std::unordered_set live_in{ + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 186, // %186 = OpPhi %13 %16 %41 %94 %78 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 94, // %94 = OpIAdd %13 %186 %33 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 11u); + } + { + SCOPED_TRACE("Block 79"); + auto live_sets = register_liveness->Get(79); + std::unordered_set live_in{ + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 106, // %106 = OpVectorShuffle %7 %195 %104 4 5 6 3 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 9u); + } + { + SCOPED_TRACE("Block 108"); + auto live_sets = register_liveness->Get(108); + std::unordered_set live_in{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 197, // %197 = OpPhi %7 %106 %79 %208 %133 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 197, // %197 = OpPhi %7 %106 %79 %208 %133 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 8u); + } + { + SCOPED_TRACE("Block 109"); + auto live_sets = register_liveness->Get(109); + std::unordered_set live_inout{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 197, // %197 = OpPhi %7 %106 %79 %208 %133 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 7u); + } + { + SCOPED_TRACE("Block 117"); + auto live_sets = register_liveness->Get(117); + std::unordered_set live_inout{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 204, // %204 = OpPhi %13 %16 %109 %129 %118 + 209, // %209 = OpPhi %7 %197 %109 %181 %118 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 9u); + } + { + SCOPED_TRACE("Block 118"); + auto live_sets = register_liveness->Get(118); + std::unordered_set live_in{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 204, // %204 = OpPhi %13 %16 %109 %129 %118 + 209, // %209 = OpPhi %7 %197 %109 %181 %118 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 129, // %129 = OpIAdd %13 %204 %33 + 176, // %176 = OpVariable %175 Output + 181, // %181 = OpCompositeInsert %7 %125 %209 3 + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 10u); + } + { + SCOPED_TRACE("Block 119"); + auto live_sets = register_liveness->Get(119); + std::unordered_set live_inout{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 209, // %209 = OpPhi %7 %197 %109 %181 %118 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 7u); + } + { + SCOPED_TRACE("Block 131"); + auto live_sets = register_liveness->Get(131); + std::unordered_set live_inout{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 205, // %205 = OpPhi %13 %16 %119 %141 %132 + 208, // %208 = OpPhi %7 %209 %119 %183 %132 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 9u); + } + { + SCOPED_TRACE("Block 132"); + auto live_sets = register_liveness->Get(132); + std::unordered_set live_in{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 205, // %205 = OpPhi %13 %16 %119 %141 %132 + 208, // %208 = OpPhi %7 %209 %119 %183 %132 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 141, // %141 = OpIAdd %13 %205 %33 + 176, // %176 = OpVariable %175 Output + 183, // %183 = OpCompositeInsert %7 %138 %208 3 + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 10u); + } + { + SCOPED_TRACE("Block 133"); + auto live_sets = register_liveness->Get(133); + std::unordered_set live_in{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 196, // %196 = OpPhi %13 %16 %79 %143 %133 + 208, // %208 = OpPhi %7 %209 %119 %183 %132 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 25, // %25 = OpLoad %13 %24 + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 143, // %143 = OpIAdd %13 %196 %33 + 176, // %176 = OpVariable %175 Output + 208, // %208 = OpPhi %7 %209 %119 %183 %132 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 8u); + } + { + SCOPED_TRACE("Block 110"); + auto live_sets = register_liveness->Get(110); + std::unordered_set live_in{ + 84, // %84 = OpVariable %8 Function + 100, // %100 = OpFAdd %7 %191 %98 + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 197, // %197 = OpPhi %7 %106 %79 %208 %133 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 150, // %150 = OpVectorShuffle %7 %100 %148 4 5 6 3 + 176, // %176 = OpVariable %175 Output + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 7u); + } + { + SCOPED_TRACE("Block 152"); + auto live_sets = register_liveness->Get(152); + std::unordered_set live_inout{ + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 199, // %199 = OpPhi %13 %16 %110 %174 %163 + 200, // %200 = OpPhi %7 %150 %110 %203 %163 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 6u); + } + { + SCOPED_TRACE("Block 153"); + auto live_sets = register_liveness->Get(153); + std::unordered_set live_inout{ + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 199, // %199 = OpPhi %13 %16 %110 %174 %163 + 200, // %200 = OpPhi %7 %150 %110 %203 %163 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 5u); + } + { + SCOPED_TRACE("Block 161"); + auto live_sets = register_liveness->Get(161); + std::unordered_set live_inout{ + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 199, // %199 = OpPhi %13 %16 %110 %174 %163 + 201, // %201 = OpPhi %13 %16 %153 %172 %162 + 203, // %203 = OpPhi %7 %200 %153 %170 %162 + }; + CompareSets(live_sets->live_in_, live_inout); + CompareSets(live_sets->live_out_, live_inout); + + EXPECT_EQ(live_sets->used_registers_, 7u); + } + { + SCOPED_TRACE("Block 162"); + auto live_sets = register_liveness->Get(162); + std::unordered_set live_in{ + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 199, // %199 = OpPhi %13 %16 %110 %174 %163 + 201, // %201 = OpPhi %13 %16 %153 %172 %162 + 203, // %203 = OpPhi %7 %200 %153 %170 %162 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 170, // %170 = OpVectorTimesScalar %7 %203 %168 + 172, // %172 = OpIAdd %13 %201 %33 + 176, // %176 = OpVariable %175 Output + 199, // %199 = OpPhi %13 %16 %110 %174 %163 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 8u); + } + { + SCOPED_TRACE("Block 163"); + auto live_sets = register_liveness->Get(163); + std::unordered_set live_in{ + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 199, // %199 = OpPhi %13 %16 %110 %174 %163 + 203, // %203 = OpPhi %7 %200 %153 %170 %162 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{ + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 174, // %174 = OpIAdd %13 %199 %45 + 176, // %176 = OpVariable %175 Output + 203, // %203 = OpPhi %7 %200 %153 %170 %162 + }; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 6u); + } + { + SCOPED_TRACE("Block 154"); + auto live_sets = register_liveness->Get(154); + std::unordered_set live_in{ + 84, // %84 = OpVariable %8 Function + 176, // %176 = OpVariable %175 Output + 200, // %200 = OpPhi %7 %150 %110 %203 %163 + }; + CompareSets(live_sets->live_in_, live_in); + + std::unordered_set live_out{}; + CompareSets(live_sets->live_out_, live_out); + + EXPECT_EQ(live_sets->used_registers_, 4u); + } + + { + SCOPED_TRACE("Compute loop pressure"); + RegisterLiveness::RegionRegisterLiveness loop_reg_pressure; + register_liveness->ComputeLoopRegisterPressure(*ld[39], &loop_reg_pressure); + // Generate(*context->cfg()->block(39), &loop_reg_pressure); + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(loop_reg_pressure.live_in_, live_in); + + std::unordered_set live_out{ + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(loop_reg_pressure.live_out_, live_out); + + EXPECT_EQ(loop_reg_pressure.used_registers_, 13u); + } + + { + SCOPED_TRACE("Loop Fusion simulation"); + RegisterLiveness::RegionRegisterLiveness simulation_resut; + register_liveness->SimulateFusion(*ld[17], *ld[39], &simulation_resut); + + std::unordered_set live_in{ + 11, // %11 = OpVariable %10 Input + 12, // %12 = OpLoad %7 %11 + 24, // %24 = OpVariable %23 Input + 25, // %25 = OpLoad %13 %24 + 28, // %28 = OpVariable %10 Input + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 184, // %184 = OpPhi %13 %16 %5 %34 %18 + 185, // %185 = OpPhi %13 %16 %19 %75 %51 + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(simulation_resut.live_in_, live_in); + + std::unordered_set live_out{ + 12, // %12 = OpLoad %7 %11 + 25, // %25 = OpLoad %13 %24 + 55, // %55 = OpVariable %54 Input + 84, // %84 = OpVariable %8 Function + 124, // %124 = OpVariable %63 Input + 176, // %176 = OpVariable %175 Output + 188, // %188 = OpPhi %6 %37 %19 %73 %51 + 191, // %191 = OpPhi %7 %12 %5 %31 %18 + }; + CompareSets(simulation_resut.live_out_, live_out); + + EXPECT_EQ(simulation_resut.used_registers_, 17u); + } +} + +TEST_F(PassClassTest, FissionSimulation) { + const std::string source = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "i" + OpName %4 "A" + OpName %5 "B" + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 10 + %16 = OpTypeArray %13 %15 + %17 = OpTypePointer Function %16 + %18 = OpTypePointer Function %13 + %19 = OpConstant %8 1 + %2 = OpFunction %6 None %7 + %20 = OpLabel + %3 = OpVariable %9 Function + %4 = OpVariable %17 Function + %5 = OpVariable %17 Function + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %8 %10 %20 %23 %24 + OpLoopMerge %25 %24 None + OpBranch %26 + %26 = OpLabel + %27 = OpSLessThan %12 %22 %11 + OpBranchConditional %27 %28 %25 + %28 = OpLabel + %29 = OpAccessChain %18 %5 %22 + %30 = OpLoad %13 %29 + %31 = OpAccessChain %18 %4 %22 + OpStore %31 %30 + %32 = OpAccessChain %18 %4 %22 + %33 = OpLoad %13 %32 + %34 = OpAccessChain %18 %5 %22 + OpStore %34 %33 + OpBranch %24 + %24 = OpLabel + %23 = OpIAdd %8 %22 %19 + OpBranch %21 + %25 = OpLabel + OpStore %3 %22 + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << source << std::endl; + Function* f = &*module->begin(); + LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); + const RegisterLiveness* register_liveness = liveness_analysis->Get(f); + LoopDescriptor& ld = *context->GetLoopDescriptor(f); + analysis::DefUseManager& def_use_mgr = *context->get_def_use_mgr(); + + { + RegisterLiveness::RegionRegisterLiveness l1_sim_resut; + RegisterLiveness::RegionRegisterLiveness l2_sim_resut; + std::unordered_set moved_instructions{ + def_use_mgr.GetDef(29), def_use_mgr.GetDef(30), def_use_mgr.GetDef(31), + def_use_mgr.GetDef(31)->NextNode()}; + std::unordered_set copied_instructions{ + def_use_mgr.GetDef(22), def_use_mgr.GetDef(27), + def_use_mgr.GetDef(27)->NextNode(), def_use_mgr.GetDef(23)}; + + register_liveness->SimulateFission(*ld[21], moved_instructions, + copied_instructions, &l1_sim_resut, + &l2_sim_resut); + { + SCOPED_TRACE("L1 simulation"); + std::unordered_set live_in{ + 3, // %3 = OpVariable %9 Function + 4, // %4 = OpVariable %17 Function + 5, // %5 = OpVariable %17 Function + 22, // %22 = OpPhi %8 %10 %20 %23 %24 + }; + CompareSets(l1_sim_resut.live_in_, live_in); + + std::unordered_set live_out{ + 3, // %3 = OpVariable %9 Function + 4, // %4 = OpVariable %17 Function + 5, // %5 = OpVariable %17 Function + 22, // %22 = OpPhi %8 %10 %20 %23 %24 + }; + CompareSets(l1_sim_resut.live_out_, live_out); + + EXPECT_EQ(l1_sim_resut.used_registers_, 6u); + } + { + SCOPED_TRACE("L2 simulation"); + std::unordered_set live_in{ + 3, // %3 = OpVariable %9 Function + 4, // %4 = OpVariable %17 Function + 5, // %5 = OpVariable %17 Function + 22, // %22 = OpPhi %8 %10 %20 %23 %24 + }; + CompareSets(l2_sim_resut.live_in_, live_in); + + std::unordered_set live_out{ + 3, // %3 = OpVariable %9 Function + 22, // %22 = OpPhi %8 %10 %20 %23 %24 + }; + CompareSets(l2_sim_resut.live_out_, live_out); + + EXPECT_EQ(l2_sim_resut.used_registers_, 6u); + } + } +} + +// Test that register liveness does not fail when there is an unreachable block. +// We are not testing if the liveness is computed correctly because the specific +// results do not matter for unreachable blocks. +TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginLowerLeft + OpSource GLSL 330 + OpSourceExtension "GL_ARB_shading_language_420pack" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %2 = OpFunction %void None %4 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %7 %8 None + OpBranch %9 + %9 = OpLabel + OpBranch %7 + %8 = OpLabel + OpBranch %6 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + Function* f = &*module->begin(); + LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis(); + liveness_analysis->Get(f); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/relax_float_ops_test.cpp b/third_party/spirv-tools/test/opt/relax_float_ops_test.cpp new file mode 100644 index 0000000..14cde0b --- /dev/null +++ b/third_party/spirv-tools/test/opt/relax_float_ops_test.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Relax float ops tests + +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using RelaxFloatOpsTest = PassTest<::testing::Test>; + +TEST_F(RelaxFloatOpsTest, RelaxFloatOpsBasic) { + // All float result instructions in functions should be relaxed + // clang-format off + // + // SamplerState g_sSamp : register(s0); + // uniform Texture1D g_tTex1df4 : register(t0); + // + // struct PS_INPUT + // { + // float Tex0 : TEXCOORD0; + // float Tex1 : TEXCOORD1; + // }; + // + // struct PS_OUTPUT + // { + // float4 Color : SV_Target0; + // }; + // + // PS_OUTPUT main(PS_INPUT i) + // { + // PS_OUTPUT psout; + // float4 txval10 = g_tTex1df4.Sample(g_sSamp, i.Tex0); + // float4 txval11 = g_tTex1df4.Sample(g_sSamp, i.Tex1); + // float4 t = txval10 + txval11; + // float4 t2 = t / 2.0; + // psout.Color = t2; + // return psout; + // } + // clang-format on + + const std::string defs0 = + R"(OpCapability Shader +OpCapability Sampled1D +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i_Tex0 %i_Tex1 %_entryPointOutput_Color +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpName %main "main" +OpName %g_tTex1df4 "g_tTex1df4" +OpName %g_sSamp "g_sSamp" +OpName %i_Tex0 "i.Tex0" +OpName %i_Tex1 "i.Tex1" +OpName %_entryPointOutput_Color "@entryPointOutput.Color" +OpDecorate %g_tTex1df4 DescriptorSet 0 +OpDecorate %g_tTex1df4 Binding 0 +OpDecorate %g_sSamp DescriptorSet 0 +OpDecorate %g_sSamp Binding 0 +OpDecorate %i_Tex0 Location 0 +OpDecorate %i_Tex1 Location 1 +OpDecorate %_entryPointOutput_Color Location 0 +)"; + + const std::string defs1 = + R"(%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%17 = OpTypeImage %float 1D 0 0 0 1 Unknown +%_ptr_UniformConstant_17 = OpTypePointer UniformConstant %17 +%g_tTex1df4 = OpVariable %_ptr_UniformConstant_17 UniformConstant +%21 = OpTypeSampler +%_ptr_UniformConstant_21 = OpTypePointer UniformConstant %21 +%g_sSamp = OpVariable %_ptr_UniformConstant_21 UniformConstant +%25 = OpTypeSampledImage %17 +%_ptr_Input_float = OpTypePointer Input %float +%i_Tex0 = OpVariable %_ptr_Input_float Input +%i_Tex1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput_Color = OpVariable %_ptr_Output_v4float Output +%float_0_5 = OpConstant %float 0.5 +%116 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5 +)"; + + const std::string relax_decos = + R"(OpDecorate %60 RelaxedPrecision +OpDecorate %63 RelaxedPrecision +OpDecorate %82 RelaxedPrecision +OpDecorate %88 RelaxedPrecision +OpDecorate %91 RelaxedPrecision +OpDecorate %94 RelaxedPrecision +)"; + + const std::string func_orig = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%60 = OpLoad %float %i_Tex0 +%63 = OpLoad %float %i_Tex1 +%77 = OpLoad %17 %g_tTex1df4 +%78 = OpLoad %21 %g_sSamp +%79 = OpSampledImage %25 %77 %78 +%82 = OpImageSampleImplicitLod %v4float %79 %60 +%83 = OpLoad %17 %g_tTex1df4 +%84 = OpLoad %21 %g_sSamp +%85 = OpSampledImage %25 %83 %84 +%88 = OpImageSampleImplicitLod %v4float %85 %63 +%91 = OpFAdd %v4float %82 %88 +%94 = OpFMul %v4float %91 %116 +OpStore %_entryPointOutput_Color %94 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + defs0 + defs1 + func_orig, defs0 + relax_decos + defs1 + func_orig, true, + true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/replace_invalid_opc_test.cpp b/third_party/spirv-tools/test/opt/replace_invalid_opc_test.cpp new file mode 100644 index 0000000..1be904b --- /dev/null +++ b/third_party/spirv-tools/test/opt/replace_invalid_opc_test.cpp @@ -0,0 +1,566 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "pass_utils.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ReplaceInvalidOpcodeTest = PassTest<::testing::Test>; + +TEST_F(ReplaceInvalidOpcodeTest, ReplaceInstruction) { + const std::string text = R"( +; CHECK: [[special_const:%\w+]] = OpConstant %float -6.2598534e+18 +; CHECK: [[constant:%\w+]] = OpConstantComposite %v4float [[special_const]] [[special_const]] [[special_const]] [[special_const]] +; CHECK-NOT: OpImageSampleImplicitLod +; CHECK: OpStore [[:%\w+]] [[constant]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5 + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpDecorate %3 Location 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %_struct_6 0 BuiltIn Position + OpDecorate %_struct_6 Block + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %12 = OpTypeSampler +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %14 = OpTypeSampledImage %10 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %_struct_6 = OpTypeStruct %v4float +%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6 + %5 = OpVariable %_ptr_Output__struct_6 Output + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %23 = OpConstantComposite %v2float %float_0 %float_0 + %24 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %25 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %main = OpFunction %void None %8 + %26 = OpLabel + %27 = OpLoad %12 %25 + %28 = OpLoad %10 %24 + %29 = OpSampledImage %14 %28 %27 + %30 = OpImageSampleImplicitLod %v4float %29 %23 + %31 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %31 %30 + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(ReplaceInvalidOpcodeTest, ReplaceInstructionInNonEntryPoint) { + const std::string text = R"( +; CHECK: [[special_const:%\w+]] = OpConstant %float -6.2598534e+18 +; CHECK: [[constant:%\w+]] = OpConstantComposite %v4float [[special_const]] [[special_const]] [[special_const]] [[special_const]] +; CHECK-NOT: OpImageSampleImplicitLod +; CHECK: OpStore [[:%\w+]] [[constant]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5 + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpDecorate %3 Location 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %_struct_6 0 BuiltIn Position + OpDecorate %_struct_6 Block + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %12 = OpTypeSampler +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %14 = OpTypeSampledImage %10 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %_struct_6 = OpTypeStruct %v4float +%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6 + %5 = OpVariable %_ptr_Output__struct_6 Output + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %23 = OpConstantComposite %v2float %float_0 %float_0 + %24 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %25 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %main = OpFunction %void None %8 + %26 = OpLabel + %27 = OpFunctionCall %void %28 + OpReturn + OpFunctionEnd + %28 = OpFunction %void None %8 + %29 = OpLabel + %30 = OpLoad %12 %25 + %31 = OpLoad %10 %24 + %32 = OpSampledImage %14 %31 %30 + %33 = OpImageSampleImplicitLod %v4float %32 %23 + %34 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %34 %33 + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(ReplaceInvalidOpcodeTest, ReplaceInstructionMultipleEntryPoints) { + const std::string text = R"( +; CHECK: [[special_const:%\w+]] = OpConstant %float -6.2598534e+18 +; CHECK: [[constant:%\w+]] = OpConstantComposite %v4float [[special_const]] [[special_const]] [[special_const]] [[special_const]] +; CHECK-NOT: OpImageSampleImplicitLod +; CHECK: OpStore [[:%\w+]] [[constant]] +; CHECK-NOT: OpImageSampleImplicitLod +; CHECK: OpStore [[:%\w+]] [[constant]] + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5 + OpEntryPoint Vertex %main2 "main2" %3 %gl_VertexIndex %5 + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpName %main2 "main2" + OpDecorate %3 Location 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %_struct_6 0 BuiltIn Position + OpDecorate %_struct_6 Block + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %12 = OpTypeSampler +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %14 = OpTypeSampledImage %10 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %_struct_6 = OpTypeStruct %v4float +%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6 + %5 = OpVariable %_ptr_Output__struct_6 Output + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %23 = OpConstantComposite %v2float %float_0 %float_0 + %24 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %25 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %main = OpFunction %void None %8 + %26 = OpLabel + %27 = OpLoad %12 %25 + %28 = OpLoad %10 %24 + %29 = OpSampledImage %14 %28 %27 + %30 = OpImageSampleImplicitLod %v4float %29 %23 + %31 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %8 + %46 = OpLabel + %47 = OpLoad %12 %25 + %48 = OpLoad %10 %24 + %49 = OpSampledImage %14 %48 %47 + %50 = OpImageSampleImplicitLod %v4float %49 %23 + %51 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %51 %50 + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} +TEST_F(ReplaceInvalidOpcodeTest, DontReplaceInstruction) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %3 %gl_VertexIndex %5 + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpDecorate %3 Location 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %_struct_6 0 BuiltIn Position + OpDecorate %_struct_6 Block + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %12 = OpTypeSampler +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %14 = OpTypeSampledImage %10 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %_struct_6 = OpTypeStruct %v4float +%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6 + %5 = OpVariable %_ptr_Output__struct_6 Output + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %23 = OpConstantComposite %v2float %float_0 %float_0 + %24 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %25 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %main = OpFunction %void None %8 + %26 = OpLabel + %27 = OpLoad %12 %25 + %28 = OpLoad %10 %24 + %29 = OpSampledImage %14 %28 %27 + %30 = OpImageSampleImplicitLod %v4float %29 %23 + %31 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %31 %30 + OpReturn + OpFunctionEnd)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(ReplaceInvalidOpcodeTest, MultipleEntryPointsDifferentStage) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5 + OpEntryPoint Fragment %main2 "main2" %3 %gl_VertexIndex %5 + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpName %main2 "main2" + OpDecorate %3 Location 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %_struct_6 0 BuiltIn Position + OpDecorate %_struct_6 Block + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %12 = OpTypeSampler +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %14 = OpTypeSampledImage %10 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %_struct_6 = OpTypeStruct %v4float +%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6 + %5 = OpVariable %_ptr_Output__struct_6 Output + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %23 = OpConstantComposite %v2float %float_0 %float_0 + %24 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %25 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %main = OpFunction %void None %8 + %26 = OpLabel + %27 = OpLoad %12 %25 + %28 = OpLoad %10 %24 + %29 = OpSampledImage %14 %28 %27 + %30 = OpImageSampleImplicitLod %v4float %29 %23 + %31 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %31 %30 + OpReturn + OpFunctionEnd + %main2 = OpFunction %void None %8 + %46 = OpLabel + %47 = OpLoad %12 %25 + %48 = OpLoad %10 %24 + %49 = OpSampledImage %14 %48 %47 + %50 = OpImageSampleImplicitLod %v4float %49 %23 + %51 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %51 %50 + OpReturn + OpFunctionEnd)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(ReplaceInvalidOpcodeTest, DontReplaceLinkage) { + const std::string text = R"( + OpCapability Shader + OpCapability Linkage + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5 + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpDecorate %3 Location 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %_struct_6 0 BuiltIn Position + OpDecorate %_struct_6 Block + %void = OpTypeVoid + %8 = OpTypeFunction %void + %float = OpTypeFloat 32 + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %12 = OpTypeSampler +%_ptr_UniformConstant_12 = OpTypePointer UniformConstant %12 + %14 = OpTypeSampledImage %10 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %_struct_6 = OpTypeStruct %v4float +%_ptr_Output__struct_6 = OpTypePointer Output %_struct_6 + %5 = OpVariable %_ptr_Output__struct_6 Output + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %23 = OpConstantComposite %v2float %float_0 %float_0 + %24 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %25 = OpVariable %_ptr_UniformConstant_12 UniformConstant + %main = OpFunction %void None %8 + %26 = OpLabel + %27 = OpLoad %12 %25 + %28 = OpLoad %10 %24 + %29 = OpSampledImage %14 %28 %27 + %30 = OpImageSampleImplicitLod %v4float %29 %23 + %31 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %31 %30 + OpReturn + OpFunctionEnd)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(ReplaceInvalidOpcodeTest, BarrierDontReplace) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%uint_264 = OpConstant %uint 264 + %main = OpFunction %void None %3 + %5 = OpLabel + OpControlBarrier %uint_2 %uint_2 %uint_264 + OpReturn + OpFunctionEnd)"; + + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(ReplaceInvalidOpcodeTest, BarrierReplace) { + const std::string text = R"( +; CHECK-NOT: OpControlBarrier + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%uint_264 = OpConstant %uint 264 + %main = OpFunction %void None %3 + %5 = OpLabel + OpControlBarrier %uint_2 %uint_2 %uint_264 + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(ReplaceInvalidOpcodeTest, MessageTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5 + OpSource GLSL 400 + %6 = OpString "test.hlsl" + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpDecorate %3 Location 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %_struct_7 0 BuiltIn Position + OpDecorate %_struct_7 Block + %void = OpTypeVoid + %9 = OpTypeFunction %void + %float = OpTypeFloat 32 + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %13 = OpTypeSampler +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %15 = OpTypeSampledImage %11 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %_struct_7 = OpTypeStruct %v4float +%_ptr_Output__struct_7 = OpTypePointer Output %_struct_7 + %5 = OpVariable %_ptr_Output__struct_7 Output + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %24 = OpConstantComposite %v2float %float_0 %float_0 + %25 = OpVariable %_ptr_UniformConstant_11 UniformConstant + %26 = OpVariable %_ptr_UniformConstant_13 UniformConstant + %main = OpFunction %void None %9 + %27 = OpLabel + OpLine %6 2 4 + %28 = OpLoad %13 %26 + %29 = OpLoad %11 %25 + %30 = OpSampledImage %15 %29 %28 + %31 = OpImageSampleImplicitLod %v4float %30 %24 + %32 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %32 %31 + OpReturn + OpFunctionEnd)"; + + std::vector messages = { + {SPV_MSG_WARNING, "test.hlsl", 2, 4, + "Removing ImageSampleImplicitLod instruction because of incompatible " + "execution model."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + +TEST_F(ReplaceInvalidOpcodeTest, MultipleMessageTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %3 %gl_VertexIndex %5 + OpSource GLSL 400 + %6 = OpString "test.hlsl" + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpDecorate %3 Location 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %_struct_7 0 BuiltIn Position + OpDecorate %_struct_7 Block + %void = OpTypeVoid + %9 = OpTypeFunction %void + %float = OpTypeFloat 32 + %11 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_11 = OpTypePointer UniformConstant %11 + %13 = OpTypeSampler +%_ptr_UniformConstant_13 = OpTypePointer UniformConstant %13 + %15 = OpTypeSampledImage %11 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %3 = OpVariable %_ptr_Output_v4float Output + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input + %_struct_7 = OpTypeStruct %v4float +%_ptr_Output__struct_7 = OpTypePointer Output %_struct_7 + %5 = OpVariable %_ptr_Output__struct_7 Output + %int_0 = OpConstant %int 0 + %float_0 = OpConstant %float 0 + %24 = OpConstantComposite %v2float %float_0 %float_0 + %25 = OpVariable %_ptr_UniformConstant_11 UniformConstant + %26 = OpVariable %_ptr_UniformConstant_13 UniformConstant + %main = OpFunction %void None %9 + %27 = OpLabel + OpLine %6 2 4 + %28 = OpLoad %13 %26 + %29 = OpLoad %11 %25 + %30 = OpSampledImage %15 %29 %28 + %31 = OpImageSampleImplicitLod %v4float %30 %24 + OpLine %6 12 4 + %41 = OpImageSampleProjImplicitLod %v4float %30 %24 + %32 = OpAccessChain %_ptr_Output_v4float %5 %int_0 + OpStore %32 %31 + OpReturn + OpFunctionEnd)"; + + std::vector messages = { + {SPV_MSG_WARNING, "test.hlsl", 2, 4, + "Removing ImageSampleImplicitLod instruction because of incompatible " + "execution model."}, + {SPV_MSG_WARNING, "test.hlsl", 12, 4, + "Removing ImageSampleProjImplicitLod instruction because of " + "incompatible " + "execution model."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/scalar_analysis.cpp b/third_party/spirv-tools/test/opt/scalar_analysis.cpp new file mode 100644 index 0000000..598d8c7 --- /dev/null +++ b/third_party/spirv-tools/test/opt/scalar_analysis.cpp @@ -0,0 +1,1221 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opt/iterator.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/tree_iterator.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/function_utils.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::UnorderedElementsAre; +using ScalarAnalysisTest = PassTest<::testing::Test>; + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 410 core +layout (location = 1) out float array[10]; +void main() { + for (int i = 0; i < 10; ++i) { + array[i] = array[i+1]; + } +} +*/ +TEST_F(ScalarAnalysisTest, BasicEvolutionTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %24 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %24 "array" + OpDecorate %24 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %19 = OpTypeFloat 32 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 10 + %22 = OpTypeArray %19 %21 + %23 = OpTypePointer Output %22 + %24 = OpVariable %23 Output + %27 = OpConstant %6 1 + %29 = OpTypePointer Output %19 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %35 = OpPhi %6 %9 %5 %34 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %35 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %28 = OpIAdd %6 %35 %27 + %30 = OpAccessChain %29 %24 %28 + %31 = OpLoad %19 %30 + %32 = OpAccessChain %29 %24 %35 + OpStore %32 %31 + OpBranch %13 + %13 = OpLabel + %34 = OpIAdd %6 %35 %27 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + ScalarEvolutionAnalysis analysis{context.get()}; + + const Instruction* store = nullptr; + const Instruction* load = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 11)) { + if (inst.opcode() == SpvOp::SpvOpStore) { + store = &inst; + } + if (inst.opcode() == SpvOp::SpvOpLoad) { + load = &inst; + } + } + + EXPECT_NE(load, nullptr); + EXPECT_NE(store, nullptr); + + Instruction* access_chain = + context->get_def_use_mgr()->GetDef(load->GetSingleWordInOperand(0)); + + Instruction* child = context->get_def_use_mgr()->GetDef( + access_chain->GetSingleWordInOperand(1)); + const SENode* node = analysis.AnalyzeInstruction(child); + + EXPECT_NE(node, nullptr); + + // Unsimplified node should have the form of ADD(REC(0,1), 1) + EXPECT_EQ(node->GetType(), SENode::Add); + + const SENode* child_1 = node->GetChild(0); + EXPECT_TRUE(child_1->GetType() == SENode::Constant || + child_1->GetType() == SENode::RecurrentAddExpr); + + const SENode* child_2 = node->GetChild(1); + EXPECT_TRUE(child_2->GetType() == SENode::Constant || + child_2->GetType() == SENode::RecurrentAddExpr); + + SENode* simplified = analysis.SimplifyExpression(const_cast(node)); + // Simplified should be in the form of REC(1,1) + EXPECT_EQ(simplified->GetType(), SENode::RecurrentAddExpr); + + EXPECT_EQ(simplified->GetChild(0)->GetType(), SENode::Constant); + EXPECT_EQ(simplified->GetChild(0)->AsSEConstantNode()->FoldToSingleValue(), + 1); + + EXPECT_EQ(simplified->GetChild(1)->GetType(), SENode::Constant); + EXPECT_EQ(simplified->GetChild(1)->AsSEConstantNode()->FoldToSingleValue(), + 1); + + EXPECT_EQ(simplified->GetChild(0), simplified->GetChild(1)); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 410 core +layout (location = 1) out float array[10]; +layout (location = 2) flat in int loop_invariant; +void main() { + for (int i = 0; i < 10; ++i) { + array[i] = array[i+loop_invariant]; + } +} + +*/ +TEST_F(ScalarAnalysisTest, LoadTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "array" + OpName %4 "loop_invariant" + OpDecorate %3 Location 1 + OpDecorate %4 Flat + OpDecorate %4 Location 2 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 0 + %10 = OpConstant %7 10 + %11 = OpTypeBool + %12 = OpTypeFloat 32 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 10 + %15 = OpTypeArray %12 %14 + %16 = OpTypePointer Output %15 + %3 = OpVariable %16 Output + %17 = OpTypePointer Input %7 + %4 = OpVariable %17 Input + %18 = OpTypePointer Output %12 + %19 = OpConstant %7 1 + %2 = OpFunction %5 None %6 + %20 = OpLabel + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %7 %9 %20 %23 %24 + OpLoopMerge %25 %24 None + OpBranch %26 + %26 = OpLabel + %27 = OpSLessThan %11 %22 %10 + OpBranchConditional %27 %28 %25 + %28 = OpLabel + %29 = OpLoad %7 %4 + %30 = OpIAdd %7 %22 %29 + %31 = OpAccessChain %18 %3 %30 + %32 = OpLoad %12 %31 + %33 = OpAccessChain %18 %3 %22 + OpStore %33 %32 + OpBranch %24 + %24 = OpLabel + %23 = OpIAdd %7 %22 %19 + OpBranch %21 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + ScalarEvolutionAnalysis analysis{context.get()}; + + const Instruction* load = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 28)) { + if (inst.opcode() == SpvOp::SpvOpLoad) { + load = &inst; + } + } + + EXPECT_NE(load, nullptr); + + Instruction* access_chain = + context->get_def_use_mgr()->GetDef(load->GetSingleWordInOperand(0)); + + Instruction* child = context->get_def_use_mgr()->GetDef( + access_chain->GetSingleWordInOperand(1)); + // const SENode* node = + // analysis.GetNodeFromInstruction(child->unique_id()); + + const SENode* node = analysis.AnalyzeInstruction(child); + + EXPECT_NE(node, nullptr); + + // Unsimplified node should have the form of ADD(REC(0,1), X) + EXPECT_EQ(node->GetType(), SENode::Add); + + const SENode* child_1 = node->GetChild(0); + EXPECT_TRUE(child_1->GetType() == SENode::ValueUnknown || + child_1->GetType() == SENode::RecurrentAddExpr); + + const SENode* child_2 = node->GetChild(1); + EXPECT_TRUE(child_2->GetType() == SENode::ValueUnknown || + child_2->GetType() == SENode::RecurrentAddExpr); + + SENode* simplified = analysis.SimplifyExpression(const_cast(node)); + EXPECT_EQ(simplified->GetType(), SENode::RecurrentAddExpr); + + const SERecurrentNode* rec = simplified->AsSERecurrentNode(); + + EXPECT_NE(rec->GetChild(0), rec->GetChild(1)); + + EXPECT_EQ(rec->GetOffset()->GetType(), SENode::ValueUnknown); + + EXPECT_EQ(rec->GetCoefficient()->GetType(), SENode::Constant); + EXPECT_EQ(rec->GetCoefficient()->AsSEConstantNode()->FoldToSingleValue(), 1u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 410 core +layout (location = 1) out float array[10]; +layout (location = 2) flat in int loop_invariant; +void main() { + array[0] = array[loop_invariant * 2 + 4 + 5 - 24 - loop_invariant - +loop_invariant+ 16 * 3]; +} + +*/ +TEST_F(ScalarAnalysisTest, SimplifySimple) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %3 "array" + OpName %4 "loop_invariant" + OpDecorate %3 Location 1 + OpDecorate %4 Flat + OpDecorate %4 Location 2 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeFloat 32 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 10 + %10 = OpTypeArray %7 %9 + %11 = OpTypePointer Output %10 + %3 = OpVariable %11 Output + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpTypePointer Input %12 + %4 = OpVariable %14 Input + %15 = OpConstant %12 2 + %16 = OpConstant %12 4 + %17 = OpConstant %12 5 + %18 = OpConstant %12 24 + %19 = OpConstant %12 48 + %20 = OpTypePointer Output %7 + %2 = OpFunction %5 None %6 + %21 = OpLabel + %22 = OpLoad %12 %4 + %23 = OpIMul %12 %22 %15 + %24 = OpIAdd %12 %23 %16 + %25 = OpIAdd %12 %24 %17 + %26 = OpISub %12 %25 %18 + %28 = OpISub %12 %26 %22 + %30 = OpISub %12 %28 %22 + %31 = OpIAdd %12 %30 %19 + %32 = OpAccessChain %20 %3 %31 + %33 = OpLoad %7 %32 + %34 = OpAccessChain %20 %3 %13 + OpStore %34 %33 + OpReturn + OpFunctionEnd + )"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + ScalarEvolutionAnalysis analysis{context.get()}; + + const Instruction* load = nullptr; + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 21)) { + if (inst.opcode() == SpvOp::SpvOpLoad && inst.result_id() == 33) { + load = &inst; + } + } + + EXPECT_NE(load, nullptr); + + Instruction* access_chain = + context->get_def_use_mgr()->GetDef(load->GetSingleWordInOperand(0)); + + Instruction* child = context->get_def_use_mgr()->GetDef( + access_chain->GetSingleWordInOperand(1)); + + const SENode* node = analysis.AnalyzeInstruction(child); + + // Unsimplified is a very large graph with an add at the top. + EXPECT_NE(node, nullptr); + EXPECT_EQ(node->GetType(), SENode::Add); + + // Simplified node should resolve down to a constant expression as the loads + // will eliminate themselves. + SENode* simplified = analysis.SimplifyExpression(const_cast(node)); + + EXPECT_EQ(simplified->GetType(), SENode::Constant); + EXPECT_EQ(simplified->AsSEConstantNode()->FoldToSingleValue(), 33u); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 410 core +layout(location = 0) in vec4 c; +layout (location = 1) out float array[10]; +void main() { + int N = int(c.x); + for (int i = 0; i < 10; ++i) { + array[i] = array[i]; + array[i] = array[i-1]; + array[i] = array[i+1]; + array[i+1] = array[i+1]; + array[i+N] = array[i+N]; + array[i] = array[i+N]; + } +} + +*/ +TEST_F(ScalarAnalysisTest, Simplify) { + const std::string text = R"( OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 %33 + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 410 + OpName %4 "main" + OpName %8 "N" + OpName %12 "c" + OpName %19 "i" + OpName %33 "array" + OpDecorate %12 Location 0 + OpDecorate %33 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeFloat 32 + %10 = OpTypeVector %9 4 + %11 = OpTypePointer Input %10 + %12 = OpVariable %11 Input + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Input %9 + %20 = OpConstant %6 0 + %27 = OpConstant %6 10 + %28 = OpTypeBool + %30 = OpConstant %13 10 + %31 = OpTypeArray %9 %30 + %32 = OpTypePointer Output %31 + %33 = OpVariable %32 Output + %36 = OpTypePointer Output %9 + %42 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %16 = OpAccessChain %15 %12 %14 + %17 = OpLoad %9 %16 + %18 = OpConvertFToS %6 %17 + OpStore %8 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + %78 = OpPhi %6 %20 %5 %77 %24 + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %29 = OpSLessThan %28 %78 %27 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + %37 = OpAccessChain %36 %33 %78 + %38 = OpLoad %9 %37 + %39 = OpAccessChain %36 %33 %78 + OpStore %39 %38 + %43 = OpISub %6 %78 %42 + %44 = OpAccessChain %36 %33 %43 + %45 = OpLoad %9 %44 + %46 = OpAccessChain %36 %33 %78 + OpStore %46 %45 + %49 = OpIAdd %6 %78 %42 + %50 = OpAccessChain %36 %33 %49 + %51 = OpLoad %9 %50 + %52 = OpAccessChain %36 %33 %78 + OpStore %52 %51 + %54 = OpIAdd %6 %78 %42 + %56 = OpIAdd %6 %78 %42 + %57 = OpAccessChain %36 %33 %56 + %58 = OpLoad %9 %57 + %59 = OpAccessChain %36 %33 %54 + OpStore %59 %58 + %62 = OpIAdd %6 %78 %18 + %65 = OpIAdd %6 %78 %18 + %66 = OpAccessChain %36 %33 %65 + %67 = OpLoad %9 %66 + %68 = OpAccessChain %36 %33 %62 + OpStore %68 %67 + %72 = OpIAdd %6 %78 %18 + %73 = OpAccessChain %36 %33 %72 + %74 = OpLoad %9 %73 + %75 = OpAccessChain %36 %33 %78 + OpStore %75 %74 + OpBranch %24 + %24 = OpLabel + %77 = OpIAdd %6 %78 %42 + OpStore %19 %77 + OpBranch %21 + %23 = OpLabel + OpReturn + OpFunctionEnd +)"; + // clang-format on + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 4); + ScalarEvolutionAnalysis analysis{context.get()}; + + const Instruction* loads[6]; + const Instruction* stores[6]; + int load_count = 0; + int store_count = 0; + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 22)) { + if (inst.opcode() == SpvOp::SpvOpLoad) { + loads[load_count] = &inst; + ++load_count; + } + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[store_count] = &inst; + ++store_count; + } + } + + EXPECT_EQ(load_count, 6); + EXPECT_EQ(store_count, 6); + + Instruction* load_access_chain; + Instruction* store_access_chain; + Instruction* load_child; + Instruction* store_child; + SENode* load_node; + SENode* store_node; + SENode* subtract_node; + SENode* simplified_node; + + // Testing [i] - [i] == 0 + load_access_chain = + context->get_def_use_mgr()->GetDef(loads[0]->GetSingleWordInOperand(0)); + store_access_chain = + context->get_def_use_mgr()->GetDef(stores[0]->GetSingleWordInOperand(0)); + + load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + load_node = analysis.AnalyzeInstruction(load_child); + store_node = analysis.AnalyzeInstruction(store_child); + + subtract_node = analysis.CreateSubtraction(store_node, load_node); + simplified_node = analysis.SimplifyExpression(subtract_node); + EXPECT_EQ(simplified_node->GetType(), SENode::Constant); + EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), 0u); + + // Testing [i] - [i-1] == 1 + load_access_chain = + context->get_def_use_mgr()->GetDef(loads[1]->GetSingleWordInOperand(0)); + store_access_chain = + context->get_def_use_mgr()->GetDef(stores[1]->GetSingleWordInOperand(0)); + + load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + load_node = analysis.AnalyzeInstruction(load_child); + store_node = analysis.AnalyzeInstruction(store_child); + + subtract_node = analysis.CreateSubtraction(store_node, load_node); + simplified_node = analysis.SimplifyExpression(subtract_node); + + EXPECT_EQ(simplified_node->GetType(), SENode::Constant); + EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), 1u); + + // Testing [i] - [i+1] == -1 + load_access_chain = + context->get_def_use_mgr()->GetDef(loads[2]->GetSingleWordInOperand(0)); + store_access_chain = + context->get_def_use_mgr()->GetDef(stores[2]->GetSingleWordInOperand(0)); + + load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + load_node = analysis.AnalyzeInstruction(load_child); + store_node = analysis.AnalyzeInstruction(store_child); + + subtract_node = analysis.CreateSubtraction(store_node, load_node); + simplified_node = analysis.SimplifyExpression(subtract_node); + EXPECT_EQ(simplified_node->GetType(), SENode::Constant); + EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), -1); + + // Testing [i+1] - [i+1] == 0 + load_access_chain = + context->get_def_use_mgr()->GetDef(loads[3]->GetSingleWordInOperand(0)); + store_access_chain = + context->get_def_use_mgr()->GetDef(stores[3]->GetSingleWordInOperand(0)); + + load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + load_node = analysis.AnalyzeInstruction(load_child); + store_node = analysis.AnalyzeInstruction(store_child); + + subtract_node = analysis.CreateSubtraction(store_node, load_node); + simplified_node = analysis.SimplifyExpression(subtract_node); + EXPECT_EQ(simplified_node->GetType(), SENode::Constant); + EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), 0u); + + // Testing [i+N] - [i+N] == 0 + load_access_chain = + context->get_def_use_mgr()->GetDef(loads[4]->GetSingleWordInOperand(0)); + store_access_chain = + context->get_def_use_mgr()->GetDef(stores[4]->GetSingleWordInOperand(0)); + + load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + load_node = analysis.AnalyzeInstruction(load_child); + store_node = analysis.AnalyzeInstruction(store_child); + + subtract_node = analysis.CreateSubtraction(store_node, load_node); + + simplified_node = analysis.SimplifyExpression(subtract_node); + EXPECT_EQ(simplified_node->GetType(), SENode::Constant); + EXPECT_EQ(simplified_node->AsSEConstantNode()->FoldToSingleValue(), 0u); + + // Testing [i] - [i+N] == -N + load_access_chain = + context->get_def_use_mgr()->GetDef(loads[5]->GetSingleWordInOperand(0)); + store_access_chain = + context->get_def_use_mgr()->GetDef(stores[5]->GetSingleWordInOperand(0)); + + load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + load_node = analysis.AnalyzeInstruction(load_child); + store_node = analysis.AnalyzeInstruction(store_child); + + subtract_node = analysis.CreateSubtraction(store_node, load_node); + simplified_node = analysis.SimplifyExpression(subtract_node); + EXPECT_EQ(simplified_node->GetType(), SENode::Negative); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 430 +layout(location = 1) out float array[10]; +layout(location = 2) flat in int loop_invariant; +void main(void) { + for (int i = 0; i < 10; ++i) { + array[i * 2 + i * 5] = array[i * i * 2]; + array[i * 2] = array[i * 5]; + } +} + +*/ + +TEST_F(ScalarAnalysisTest, SimplifyMultiplyInductions) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %5 "i" + OpName %3 "array" + OpName %4 "loop_invariant" + OpDecorate %3 Location 1 + OpDecorate %4 Flat + OpDecorate %4 Location 2 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 10 + %16 = OpTypeArray %13 %15 + %17 = OpTypePointer Output %16 + %3 = OpVariable %17 Output + %18 = OpConstant %8 2 + %19 = OpConstant %8 5 + %20 = OpTypePointer Output %13 + %21 = OpConstant %8 1 + %22 = OpTypePointer Input %8 + %4 = OpVariable %22 Input + %2 = OpFunction %6 None %7 + %23 = OpLabel + %5 = OpVariable %9 Function + OpStore %5 %10 + OpBranch %24 + %24 = OpLabel + %25 = OpPhi %8 %10 %23 %26 %27 + OpLoopMerge %28 %27 None + OpBranch %29 + %29 = OpLabel + %30 = OpSLessThan %12 %25 %11 + OpBranchConditional %30 %31 %28 + %31 = OpLabel + %32 = OpIMul %8 %25 %18 + %33 = OpIMul %8 %25 %19 + %34 = OpIAdd %8 %32 %33 + %35 = OpIMul %8 %25 %25 + %36 = OpIMul %8 %35 %18 + %37 = OpAccessChain %20 %3 %36 + %38 = OpLoad %13 %37 + %39 = OpAccessChain %20 %3 %34 + OpStore %39 %38 + %40 = OpIMul %8 %25 %18 + %41 = OpIMul %8 %25 %19 + %42 = OpAccessChain %20 %3 %41 + %43 = OpLoad %13 %42 + %44 = OpAccessChain %20 %3 %40 + OpStore %44 %43 + OpBranch %27 + %27 = OpLabel + %26 = OpIAdd %8 %25 %21 + OpStore %5 %26 + OpBranch %24 + %28 = OpLabel + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + ScalarEvolutionAnalysis analysis{context.get()}; + + const Instruction* loads[2] = {nullptr, nullptr}; + const Instruction* stores[2] = {nullptr, nullptr}; + int load_count = 0; + int store_count = 0; + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 31)) { + if (inst.opcode() == SpvOp::SpvOpLoad) { + loads[load_count] = &inst; + ++load_count; + } + if (inst.opcode() == SpvOp::SpvOpStore) { + stores[store_count] = &inst; + ++store_count; + } + } + + EXPECT_EQ(load_count, 2); + EXPECT_EQ(store_count, 2); + + Instruction* load_access_chain = + context->get_def_use_mgr()->GetDef(loads[0]->GetSingleWordInOperand(0)); + Instruction* store_access_chain = + context->get_def_use_mgr()->GetDef(stores[0]->GetSingleWordInOperand(0)); + + Instruction* load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + Instruction* store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + SENode* store_node = analysis.AnalyzeInstruction(store_child); + + SENode* store_simplified = analysis.SimplifyExpression(store_node); + + load_access_chain = + context->get_def_use_mgr()->GetDef(loads[1]->GetSingleWordInOperand(0)); + store_access_chain = + context->get_def_use_mgr()->GetDef(stores[1]->GetSingleWordInOperand(0)); + load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + SENode* second_store = + analysis.SimplifyExpression(analysis.AnalyzeInstruction(store_child)); + SENode* second_load = + analysis.SimplifyExpression(analysis.AnalyzeInstruction(load_child)); + SENode* combined_add = analysis.SimplifyExpression( + analysis.CreateAddNode(second_load, second_store)); + + // We're checking that the two recurrent expression have been correctly + // folded. In store_simplified they will have been folded as the entire + // expression was simplified as one. In combined_add the two expressions have + // been simplified one after the other which means the recurrent expressions + // aren't exactly the same but should still be folded as they are with respect + // to the same loop. + EXPECT_EQ(combined_add, store_simplified); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 430 +void main(void) { + for (int i = 0; i < 10; --i) { + array[i] = array[i]; + } +} + +*/ + +TEST_F(ScalarAnalysisTest, SimplifyNegativeSteps) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %5 "i" + OpName %3 "array" + OpName %4 "loop_invariant" + OpDecorate %3 Location 1 + OpDecorate %4 Flat + OpDecorate %4 Location 2 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 10 + %16 = OpTypeArray %13 %15 + %17 = OpTypePointer Output %16 + %3 = OpVariable %17 Output + %18 = OpTypePointer Output %13 + %19 = OpConstant %8 1 + %20 = OpTypePointer Input %8 + %4 = OpVariable %20 Input + %2 = OpFunction %6 None %7 + %21 = OpLabel + %5 = OpVariable %9 Function + OpStore %5 %10 + OpBranch %22 + %22 = OpLabel + %23 = OpPhi %8 %10 %21 %24 %25 + OpLoopMerge %26 %25 None + OpBranch %27 + %27 = OpLabel + %28 = OpSLessThan %12 %23 %11 + OpBranchConditional %28 %29 %26 + %29 = OpLabel + %30 = OpAccessChain %18 %3 %23 + %31 = OpLoad %13 %30 + %32 = OpAccessChain %18 %3 %23 + OpStore %32 %31 + OpBranch %25 + %25 = OpLabel + %24 = OpISub %8 %23 %19 + OpStore %5 %24 + OpBranch %22 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + ScalarEvolutionAnalysis analysis{context.get()}; + + const Instruction* loads[1] = {nullptr}; + int load_count = 0; + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 29)) { + if (inst.opcode() == SpvOp::SpvOpLoad) { + loads[load_count] = &inst; + ++load_count; + } + } + + EXPECT_EQ(load_count, 1); + + Instruction* load_access_chain = + context->get_def_use_mgr()->GetDef(loads[0]->GetSingleWordInOperand(0)); + Instruction* load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + + SENode* load_node = analysis.AnalyzeInstruction(load_child); + + EXPECT_TRUE(load_node); + EXPECT_EQ(load_node->GetType(), SENode::RecurrentAddExpr); + EXPECT_TRUE(load_node->AsSERecurrentNode()); + + SENode* child_1 = load_node->AsSERecurrentNode()->GetCoefficient(); + SENode* child_2 = load_node->AsSERecurrentNode()->GetOffset(); + + EXPECT_EQ(child_1->GetType(), SENode::Constant); + EXPECT_EQ(child_2->GetType(), SENode::Constant); + + EXPECT_EQ(child_1->AsSEConstantNode()->FoldToSingleValue(), -1); + EXPECT_EQ(child_2->AsSEConstantNode()->FoldToSingleValue(), 0u); + + SERecurrentNode* load_simplified = + analysis.SimplifyExpression(load_node)->AsSERecurrentNode(); + + EXPECT_TRUE(load_simplified); + EXPECT_EQ(load_node, load_simplified); + + EXPECT_EQ(load_simplified->GetType(), SENode::RecurrentAddExpr); + EXPECT_TRUE(load_simplified->AsSERecurrentNode()); + + SENode* simplified_child_1 = + load_simplified->AsSERecurrentNode()->GetCoefficient(); + SENode* simplified_child_2 = + load_simplified->AsSERecurrentNode()->GetOffset(); + + EXPECT_EQ(child_1, simplified_child_1); + EXPECT_EQ(child_2, simplified_child_2); +} + +/* +Generated from the following GLSL + --eliminate-local-multi-store + +#version 430 +void main(void) { + for (int i = 0; i < 10; --i) { + array[i] = array[i]; + } +} + +*/ + +TEST_F(ScalarAnalysisTest, SimplifyInductionsAndLoads) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %5 "i" + OpName %3 "array" + OpName %4 "N" + OpDecorate %3 Location 1 + OpDecorate %4 Flat + OpDecorate %4 Location 2 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 0 + %11 = OpConstant %8 10 + %12 = OpTypeBool + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 10 + %16 = OpTypeArray %13 %15 + %17 = OpTypePointer Output %16 + %3 = OpVariable %17 Output + %18 = OpConstant %8 2 + %19 = OpTypePointer Input %8 + %4 = OpVariable %19 Input + %20 = OpTypePointer Output %13 + %21 = OpConstant %8 1 + %2 = OpFunction %6 None %7 + %22 = OpLabel + %5 = OpVariable %9 Function + OpStore %5 %10 + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %8 %10 %22 %25 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %29 = OpSLessThan %12 %24 %11 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + %31 = OpLoad %8 %4 + %32 = OpIMul %8 %18 %31 + %33 = OpIAdd %8 %24 %32 + %35 = OpIAdd %8 %24 %31 + %36 = OpAccessChain %20 %3 %35 + %37 = OpLoad %13 %36 + %38 = OpAccessChain %20 %3 %33 + OpStore %38 %37 + %39 = OpIMul %8 %18 %24 + %41 = OpIMul %8 %18 %31 + %42 = OpIAdd %8 %39 %41 + %43 = OpIAdd %8 %42 %21 + %44 = OpIMul %8 %18 %24 + %46 = OpIAdd %8 %44 %31 + %47 = OpIAdd %8 %46 %21 + %48 = OpAccessChain %20 %3 %47 + %49 = OpLoad %13 %48 + %50 = OpAccessChain %20 %3 %43 + OpStore %50 %49 + OpBranch %26 + %26 = OpLabel + %25 = OpISub %8 %24 %21 + OpStore %5 %25 + OpBranch %23 + %27 = OpLabel + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + ScalarEvolutionAnalysis analysis{context.get()}; + + std::vector loads{}; + std::vector stores{}; + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 30)) { + if (inst.opcode() == SpvOp::SpvOpLoad) { + loads.push_back(&inst); + } + if (inst.opcode() == SpvOp::SpvOpStore) { + stores.push_back(&inst); + } + } + + EXPECT_EQ(loads.size(), 3u); + EXPECT_EQ(stores.size(), 2u); + { + Instruction* store_access_chain = context->get_def_use_mgr()->GetDef( + stores[0]->GetSingleWordInOperand(0)); + + Instruction* store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + + SENode* store_node = analysis.AnalyzeInstruction(store_child); + + SENode* store_simplified = analysis.SimplifyExpression(store_node); + + Instruction* load_access_chain = + context->get_def_use_mgr()->GetDef(loads[1]->GetSingleWordInOperand(0)); + + Instruction* load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + + SENode* load_node = analysis.AnalyzeInstruction(load_child); + + SENode* load_simplified = analysis.SimplifyExpression(load_node); + + SENode* difference = + analysis.CreateSubtraction(store_simplified, load_simplified); + + SENode* difference_simplified = analysis.SimplifyExpression(difference); + + // Check that i+2*N - i*N, turns into just N when both sides have already + // been simplified into a single recurrent expression. + EXPECT_EQ(difference_simplified->GetType(), SENode::ValueUnknown); + + // Check that the inverse, i*N - i+2*N turns into -N. + SENode* difference_inverse = analysis.SimplifyExpression( + analysis.CreateSubtraction(load_simplified, store_simplified)); + + EXPECT_EQ(difference_inverse->GetType(), SENode::Negative); + EXPECT_EQ(difference_inverse->GetChild(0)->GetType(), SENode::ValueUnknown); + EXPECT_EQ(difference_inverse->GetChild(0), difference_simplified); + } + + { + Instruction* store_access_chain = context->get_def_use_mgr()->GetDef( + stores[1]->GetSingleWordInOperand(0)); + + Instruction* store_child = context->get_def_use_mgr()->GetDef( + store_access_chain->GetSingleWordInOperand(1)); + SENode* store_node = analysis.AnalyzeInstruction(store_child); + SENode* store_simplified = analysis.SimplifyExpression(store_node); + + Instruction* load_access_chain = + context->get_def_use_mgr()->GetDef(loads[2]->GetSingleWordInOperand(0)); + + Instruction* load_child = context->get_def_use_mgr()->GetDef( + load_access_chain->GetSingleWordInOperand(1)); + + SENode* load_node = analysis.AnalyzeInstruction(load_child); + + SENode* load_simplified = analysis.SimplifyExpression(load_node); + + SENode* difference = + analysis.CreateSubtraction(store_simplified, load_simplified); + SENode* difference_simplified = analysis.SimplifyExpression(difference); + + // Check that 2*i + 2*N + 1 - 2*i + N + 1, turns into just N when both + // sides have already been simplified into a single recurrent expression. + EXPECT_EQ(difference_simplified->GetType(), SENode::ValueUnknown); + + // Check that the inverse, (2*i + N + 1) - (2*i + 2*N + 1) turns into -N. + SENode* difference_inverse = analysis.SimplifyExpression( + analysis.CreateSubtraction(load_simplified, store_simplified)); + + EXPECT_EQ(difference_inverse->GetType(), SENode::Negative); + EXPECT_EQ(difference_inverse->GetChild(0)->GetType(), SENode::ValueUnknown); + EXPECT_EQ(difference_inverse->GetChild(0), difference_simplified); + } +} + +/* Generated from the following GLSL + --eliminate-local-multi-store + + #version 430 + layout(location = 1) out float array[10]; + layout(location = 2) flat in int N; + void main(void) { + int step = 0; + for (int i = 0; i < N; i += step) { + step++; + } + } +*/ +TEST_F(ScalarAnalysisTest, InductionWithVariantStep) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 %4 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpName %2 "main" + OpName %5 "step" + OpName %6 "i" + OpName %3 "N" + OpName %4 "array" + OpDecorate %3 Flat + OpDecorate %3 Location 2 + OpDecorate %4 Location 1 + %7 = OpTypeVoid + %8 = OpTypeFunction %7 + %9 = OpTypeInt 32 1 + %10 = OpTypePointer Function %9 + %11 = OpConstant %9 0 + %12 = OpTypePointer Input %9 + %3 = OpVariable %12 Input + %13 = OpTypeBool + %14 = OpConstant %9 1 + %15 = OpTypeFloat 32 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 10 + %18 = OpTypeArray %15 %17 + %19 = OpTypePointer Output %18 + %4 = OpVariable %19 Output + %2 = OpFunction %7 None %8 + %20 = OpLabel + %5 = OpVariable %10 Function + %6 = OpVariable %10 Function + OpStore %5 %11 + OpStore %6 %11 + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %9 %11 %20 %23 %24 + %25 = OpPhi %9 %11 %20 %26 %24 + OpLoopMerge %27 %24 None + OpBranch %28 + %28 = OpLabel + %29 = OpLoad %9 %3 + %30 = OpSLessThan %13 %25 %29 + OpBranchConditional %30 %31 %27 + %31 = OpLabel + %23 = OpIAdd %9 %22 %14 + OpStore %5 %23 + OpBranch %24 + %24 = OpLabel + %26 = OpIAdd %9 %25 %23 + OpStore %6 %26 + OpBranch %21 + %27 = OpLabel + OpReturn + OpFunctionEnd + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + Module* module = context->module(); + EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n" + << text << std::endl; + const Function* f = spvtest::GetFunction(module, 2); + ScalarEvolutionAnalysis analysis{context.get()}; + + std::vector phis{}; + + for (const Instruction& inst : *spvtest::GetBasicBlock(f, 21)) { + if (inst.opcode() == SpvOp::SpvOpPhi) { + phis.push_back(&inst); + } + } + + EXPECT_EQ(phis.size(), 2u); + SENode* phi_node_1 = analysis.AnalyzeInstruction(phis[0]); + SENode* phi_node_2 = analysis.AnalyzeInstruction(phis[1]); + phi_node_1->DumpDot(std::cout, true); + EXPECT_NE(phi_node_1, nullptr); + EXPECT_NE(phi_node_2, nullptr); + + EXPECT_EQ(phi_node_1->GetType(), SENode::RecurrentAddExpr); + EXPECT_EQ(phi_node_2->GetType(), SENode::CanNotCompute); + + SENode* simplified_1 = analysis.SimplifyExpression(phi_node_1); + SENode* simplified_2 = analysis.SimplifyExpression(phi_node_2); + + EXPECT_EQ(simplified_1->GetType(), SENode::RecurrentAddExpr); + EXPECT_EQ(simplified_2->GetType(), SENode::CanNotCompute); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/scalar_replacement_test.cpp b/third_party/spirv-tools/test/opt/scalar_replacement_test.cpp new file mode 100644 index 0000000..2130f69 --- /dev/null +++ b/third_party/spirv-tools/test/opt/scalar_replacement_test.cpp @@ -0,0 +1,2084 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ScalarReplacementTest = PassTest<::testing::Test>; + +TEST_F(ScalarReplacementTest, SimpleStruct) { + const std::string text = R"( +; +; CHECK: [[struct:%\w+]] = OpTypeStruct [[elem:%\w+]] +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: [[elem_ptr:%\w+]] = OpTypePointer Function [[elem]] +; CHECK: OpConstantNull [[struct]] +; CHECK: [[null:%\w+]] = OpConstantNull [[elem]] +; CHECK-NOT: OpVariable [[struct_ptr]] +; CHECK: [[one:%\w+]] = OpVariable [[elem_ptr]] Function [[null]] +; CHECK-NEXT: [[two:%\w+]] = OpVariable [[elem_ptr]] Function [[null]] +; CHECK-NOT: OpVariable [[elem_ptr]] Function [[null]] +; CHECK-NOT: OpVariable [[struct_ptr]] +; CHECK-NOT: OpInBoundsAccessChain +; CHECK: [[l1:%\w+]] = OpLoad [[elem]] [[two]] +; CHECK-NOT: OpAccessChain +; CHECK: [[l2:%\w+]] = OpLoad [[elem]] [[one]] +; CHECK: OpIAdd [[elem]] [[l1]] [[l2]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %6 "simple_struct" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeStruct %2 %2 %2 %2 +%4 = OpTypePointer Function %3 +%5 = OpTypePointer Function %2 +%6 = OpTypeFunction %2 +%7 = OpConstantNull %3 +%8 = OpConstant %2 0 +%9 = OpConstant %2 1 +%10 = OpConstant %2 2 +%11 = OpConstant %2 3 +%12 = OpFunction %2 None %6 +%13 = OpLabel +%14 = OpVariable %4 Function %7 +%15 = OpInBoundsAccessChain %5 %14 %8 +%16 = OpLoad %2 %15 +%17 = OpAccessChain %5 %14 %10 +%18 = OpLoad %2 %17 +%19 = OpIAdd %2 %16 %18 +OpReturnValue %19 +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, StructInitialization) { + const std::string text = R"( +; +; CHECK: [[elem:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct:%\w+]] = OpTypeStruct [[elem]] [[elem]] [[elem]] [[elem]] +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: [[elem_ptr:%\w+]] = OpTypePointer Function [[elem]] +; CHECK: [[zero:%\w+]] = OpConstant [[elem]] 0 +; CHECK: [[undef:%\w+]] = OpUndef [[elem]] +; CHECK: [[two:%\w+]] = OpConstant [[elem]] 2 +; CHECK: [[null:%\w+]] = OpConstantNull [[elem]] +; CHECK-NOT: OpVariable [[struct_ptr]] +; CHECK: OpVariable [[elem_ptr]] Function [[null]] +; CHECK-NEXT: OpVariable [[elem_ptr]] Function [[two]] +; CHECK-NOT: OpVariable [[elem_ptr]] Function [[undef]] +; CHECK-NEXT: OpVariable [[elem_ptr]] Function +; CHECK-NEXT: OpVariable [[elem_ptr]] Function [[zero]] +; CHECK-NOT: OpVariable [[elem_ptr]] Function [[undef]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %6 "struct_init" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeStruct %2 %2 %2 %2 +%4 = OpTypePointer Function %3 +%20 = OpTypePointer Function %2 +%6 = OpTypeFunction %1 +%7 = OpConstant %2 0 +%8 = OpUndef %2 +%9 = OpConstant %2 2 +%30 = OpConstant %2 1 +%31 = OpConstant %2 3 +%10 = OpConstantNull %2 +%11 = OpConstantComposite %3 %7 %8 %9 %10 +%12 = OpFunction %1 None %6 +%13 = OpLabel +%14 = OpVariable %4 Function %11 +%15 = OpAccessChain %20 %14 %7 +OpStore %15 %10 +%16 = OpAccessChain %20 %14 %9 +OpStore %16 %10 +%17 = OpAccessChain %20 %14 %30 +OpStore %17 %10 +%18 = OpAccessChain %20 %14 %31 +OpStore %18 %10 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, SpecConstantInitialization) { + const std::string text = R"( +; +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct:%\w+]] = OpTypeStruct [[int]] [[int]] +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: [[int_ptr:%\w+]] = OpTypePointer Function [[int]] +; CHECK: [[spec_comp:%\w+]] = OpSpecConstantComposite [[struct]] +; CHECK: [[ex0:%\w+]] = OpSpecConstantOp [[int]] CompositeExtract [[spec_comp]] 0 +; CHECK: [[ex1:%\w+]] = OpSpecConstantOp [[int]] CompositeExtract [[spec_comp]] 1 +; CHECK-NOT: OpVariable [[struct]] +; CHECK: OpVariable [[int_ptr]] Function [[ex1]] +; CHECK-NEXT: OpVariable [[int_ptr]] Function [[ex0]] +; CHECK-NOT: OpVariable [[struct]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %6 "spec_const" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeStruct %2 %2 +%4 = OpTypePointer Function %3 +%20 = OpTypePointer Function %2 +%5 = OpTypeFunction %1 +%6 = OpConstant %2 0 +%30 = OpConstant %2 1 +%7 = OpSpecConstant %2 0 +%8 = OpSpecConstantOp %2 IAdd %7 %7 +%9 = OpSpecConstantComposite %3 %7 %8 +%10 = OpFunction %1 None %5 +%11 = OpLabel +%12 = OpVariable %4 Function %9 +%13 = OpAccessChain %20 %12 %6 +%14 = OpLoad %2 %13 +%15 = OpAccessChain %20 %12 %30 +%16 = OpLoad %2 %15 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +// TODO(alanbaker): Re-enable when vector and matrix scalarization is supported. +// TEST_F(ScalarReplacementTest, VectorInitialization) { +// const std::string text = R"( +// ; +// ; CHECK: [[elem:%\w+]] = OpTypeInt 32 0 +// ; CHECK: [[vector:%\w+]] = OpTypeVector [[elem]] 4 +// ; CHECK: [[vector_ptr:%\w+]] = OpTypePointer Function [[vector]] +// ; CHECK: [[elem_ptr:%\w+]] = OpTypePointer Function [[elem]] +// ; CHECK: [[zero:%\w+]] = OpConstant [[elem]] 0 +// ; CHECK: [[undef:%\w+]] = OpUndef [[elem]] +// ; CHECK: [[two:%\w+]] = OpConstant [[elem]] 2 +// ; CHECK: [[null:%\w+]] = OpConstantNull [[elem]] +// ; CHECK-NOT: OpVariable [[vector_ptr]] +// ; CHECK: OpVariable [[elem_ptr]] Function [[zero]] +// ; CHECK-NOT: OpVariable [[elem_ptr]] Function [[undef]] +// ; CHECK-NEXT: OpVariable [[elem_ptr]] Function +// ; CHECK-NEXT: OpVariable [[elem_ptr]] Function [[two]] +// ; CHECK-NEXT: OpVariable [[elem_ptr]] Function [[null]] +// ; CHECK-NOT: OpVariable [[elem_ptr]] Function [[undef]] +// ; +// OpCapability Shader +// OpCapability Linkage +// OpMemoryModel Logical GLSL450 +// OpName %6 "vector_init" +// %1 = OpTypeVoid +// %2 = OpTypeInt 32 0 +// %3 = OpTypeVector %2 4 +// %4 = OpTypePointer Function %3 +// %20 = OpTypePointer Function %2 +// %6 = OpTypeFunction %1 +// %7 = OpConstant %2 0 +// %8 = OpUndef %2 +// %9 = OpConstant %2 2 +// %30 = OpConstant %2 1 +// %31 = OpConstant %2 3 +// %10 = OpConstantNull %2 +// %11 = OpConstantComposite %3 %10 %9 %8 %7 +// %12 = OpFunction %1 None %6 +// %13 = OpLabel +// %14 = OpVariable %4 Function %11 +// %15 = OpAccessChain %20 %14 %7 +// OpStore %15 %10 +// %16 = OpAccessChain %20 %14 %9 +// OpStore %16 %10 +// %17 = OpAccessChain %20 %14 %30 +// OpStore %17 %10 +// %18 = OpAccessChain %20 %14 %31 +// OpStore %18 %10 +// OpReturn +// OpFunctionEnd +// )"; +// +// SinglePassRunAndMatch(text, true); +// } +// +// TEST_F(ScalarReplacementTest, MatrixInitialization) { +// const std::string text = R"( +// ; +// ; CHECK: [[float:%\w+]] = OpTypeFloat 32 +// ; CHECK: [[vector:%\w+]] = OpTypeVector [[float]] 2 +// ; CHECK: [[matrix:%\w+]] = OpTypeMatrix [[vector]] 2 +// ; CHECK: [[matrix_ptr:%\w+]] = OpTypePointer Function [[matrix]] +// ; CHECK: [[float_ptr:%\w+]] = OpTypePointer Function [[float]] +// ; CHECK: [[vec_ptr:%\w+]] = OpTypePointer Function [[vector]] +// ; CHECK: [[zerof:%\w+]] = OpConstant [[float]] 0 +// ; CHECK: [[onef:%\w+]] = OpConstant [[float]] 1 +// ; CHECK: [[one_zero:%\w+]] = OpConstantComposite [[vector]] [[onef]] +// [[zerof]] ; CHECK: [[zero_one:%\w+]] = OpConstantComposite [[vector]] +// [[zerof]] [[onef]] ; CHECK: [[const_mat:%\w+]] = OpConstantComposite +// [[matrix]] [[one_zero]] +// [[zero_one]] ; CHECK-NOT: OpVariable [[matrix]] ; CHECK-NOT: OpVariable +// [[vector]] Function [[one_zero]] ; CHECK: [[f1:%\w+]] = OpVariable +// [[float_ptr]] Function [[zerof]] ; CHECK-NEXT: [[f2:%\w+]] = OpVariable +// [[float_ptr]] Function [[onef]] ; CHECK-NEXT: [[vec_var:%\w+]] = OpVariable +// [[vec_ptr]] Function [[zero_one]] ; CHECK-NOT: OpVariable [[matrix]] ; +// CHECK-NOT: OpVariable [[vector]] Function [[one_zero]] +// ; +// OpCapability Shader +// OpCapability Linkage +// OpMemoryModel Logical GLSL450 +// OpName %7 "matrix_init" +// %1 = OpTypeVoid +// %2 = OpTypeFloat 32 +// %3 = OpTypeVector %2 2 +// %4 = OpTypeMatrix %3 2 +// %5 = OpTypePointer Function %4 +// %6 = OpTypePointer Function %2 +// %30 = OpTypePointer Function %3 +// %10 = OpTypeInt 32 0 +// %7 = OpTypeFunction %1 %10 +// %8 = OpConstant %2 0.0 +// %9 = OpConstant %2 1.0 +// %11 = OpConstant %10 0 +// %12 = OpConstant %10 1 +// %13 = OpConstantComposite %3 %9 %8 +// %14 = OpConstantComposite %3 %8 %9 +// %15 = OpConstantComposite %4 %13 %14 +// %16 = OpFunction %1 None %7 +// %31 = OpFunctionParameter %10 +// %17 = OpLabel +// %18 = OpVariable %5 Function %15 +// %19 = OpAccessChain %6 %18 %11 %12 +// OpStore %19 %8 +// %20 = OpAccessChain %6 %18 %11 %11 +// OpStore %20 %8 +// %21 = OpAccessChain %30 %18 %12 +// OpStore %21 %14 +// OpReturn +// OpFunctionEnd +// )"; +// +// SinglePassRunAndMatch(text, true); +// } + +TEST_F(ScalarReplacementTest, ElideAccessChain) { + const std::string text = R"( +; +; CHECK: [[var:%\w+]] = OpVariable +; CHECK-NOT: OpAccessChain +; CHECK: OpStore [[var]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %6 "elide_access_chain" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeStruct %2 %2 %2 %2 +%4 = OpTypePointer Function %3 +%20 = OpTypePointer Function %2 +%6 = OpTypeFunction %1 +%7 = OpConstant %2 0 +%8 = OpUndef %2 +%9 = OpConstant %2 2 +%10 = OpConstantNull %2 +%11 = OpConstantComposite %3 %7 %8 %9 %10 +%12 = OpFunction %1 None %6 +%13 = OpLabel +%14 = OpVariable %4 Function %11 +%15 = OpAccessChain %20 %14 %7 +OpStore %15 %10 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ElideMultipleAccessChains) { + const std::string text = R"( +; +; CHECK: [[var:%\w+]] = OpVariable +; CHECK-NOT: OpInBoundsAccessChain +; CHECK OpStore [[var]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %6 "elide_two_access_chains" +%1 = OpTypeVoid +%2 = OpTypeFloat 32 +%3 = OpTypeStruct %2 %2 +%4 = OpTypeStruct %3 %3 +%5 = OpTypePointer Function %4 +%6 = OpTypePointer Function %2 +%7 = OpTypeFunction %1 +%8 = OpConstant %2 0.0 +%9 = OpConstant %2 1.0 +%10 = OpTypeInt 32 0 +%11 = OpConstant %10 0 +%12 = OpConstant %10 1 +%13 = OpConstantComposite %3 %9 %8 +%14 = OpConstantComposite %3 %8 %9 +%15 = OpConstantComposite %4 %13 %14 +%16 = OpFunction %1 None %7 +%17 = OpLabel +%18 = OpVariable %5 Function %15 +%19 = OpInBoundsAccessChain %6 %18 %11 %12 +OpStore %19 %8 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ReplaceAccessChain) { + const std::string text = R"( +; +; CHECK: [[param:%\w+]] = OpFunctionParameter +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[access:%\w+]] = OpAccessChain {{%\w+}} [[var]] [[param]] +; CHECK: OpStore [[access]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %7 "replace_access_chain" +%1 = OpTypeVoid +%2 = OpTypeFloat 32 +%10 = OpTypeInt 32 0 +%uint_2 = OpConstant %10 2 +%3 = OpTypeArray %2 %uint_2 +%4 = OpTypeStruct %3 %3 +%5 = OpTypePointer Function %4 +%20 = OpTypePointer Function %3 +%6 = OpTypePointer Function %2 +%7 = OpTypeFunction %1 %10 +%8 = OpConstant %2 0.0 +%9 = OpConstant %2 1.0 +%11 = OpConstant %10 0 +%12 = OpConstant %10 1 +%13 = OpConstantComposite %3 %9 %8 +%14 = OpConstantComposite %3 %8 %9 +%15 = OpConstantComposite %4 %13 %14 +%16 = OpFunction %1 None %7 +%32 = OpFunctionParameter %10 +%17 = OpLabel +%18 = OpVariable %5 Function %15 +%19 = OpAccessChain %6 %18 %11 %32 +OpStore %19 %8 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ArrayInitialization) { + const std::string text = R"( +; +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[array:%\w+]] = OpTypeArray +; CHECK: [[array_ptr:%\w+]] = OpTypePointer Function [[array]] +; CHECK: [[float_ptr:%\w+]] = OpTypePointer Function [[float]] +; CHECK: [[float0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[float1:%\w+]] = OpConstant [[float]] 1 +; CHECK: [[float2:%\w+]] = OpConstant [[float]] 2 +; CHECK-NOT: OpVariable [[array_ptr]] +; CHECK: [[var0:%\w+]] = OpVariable [[float_ptr]] Function [[float0]] +; CHECK-NEXT: [[var1:%\w+]] = OpVariable [[float_ptr]] Function [[float1]] +; CHECK-NEXT: [[var2:%\w+]] = OpVariable [[float_ptr]] Function [[float2]] +; CHECK-NOT: OpVariable [[array_ptr]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "array_init" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%uint_3 = OpConstant %uint 3 +%float_array = OpTypeArray %float %uint_3 +%array_ptr = OpTypePointer Function %float_array +%float_ptr = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%const_array = OpConstantComposite %float_array %float_2 %float_1 %float_0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%3 = OpVariable %array_ptr Function %const_array +%4 = OpInBoundsAccessChain %float_ptr %3 %uint_0 +OpStore %4 %float_0 +%5 = OpInBoundsAccessChain %float_ptr %3 %uint_1 +OpStore %5 %float_0 +%6 = OpInBoundsAccessChain %float_ptr %3 %uint_2 +OpStore %6 %float_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, NonUniformCompositeInitialization) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[long:%\w+]] = OpTypeInt 64 1 +; CHECK: [[dvector:%\w+]] = OpTypeVector +; CHECK: [[vector:%\w+]] = OpTypeVector +; CHECK: [[array:%\w+]] = OpTypeArray +; CHECK: [[matrix:%\w+]] = OpTypeMatrix +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[vector]] +; CHECK: [[struct2:%\w+]] = OpTypeStruct [[struct1]] [[matrix]] [[array]] [[uint]] +; CHECK: [[struct1_ptr:%\w+]] = OpTypePointer Function [[struct1]] +; CHECK: [[matrix_ptr:%\w+]] = OpTypePointer Function [[matrix]] +; CHECK: [[array_ptr:%\w+]] = OpTypePointer Function [[array]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[struct2_ptr:%\w+]] = OpTypePointer Function [[struct2]] +; CHECK: [[const_array:%\w+]] = OpConstantComposite [[array]] +; CHECK: [[const_matrix:%\w+]] = OpConstantNull [[matrix]] +; CHECK: [[const_struct1:%\w+]] = OpConstantComposite [[struct1]] +; CHECK: OpConstantNull [[uint]] +; CHECK: OpConstantNull [[vector]] +; CHECK: OpConstantNull [[long]] +; CHECK: OpFunction +; CHECK-NOT: OpVariable [[struct2_ptr]] Function +; CHECK: OpVariable [[uint_ptr]] Function +; CHECK-NEXT: OpVariable [[matrix_ptr]] Function [[const_matrix]] +; CHECK-NOT: OpVariable [[struct1_ptr]] Function [[const_struct1]] +; CHECK-NOT: OpVariable [[struct2_ptr]] Function +; +OpCapability Shader +OpCapability Linkage +OpCapability Int64 +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpName %func "non_uniform_composite_init" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%int64 = OpTypeInt 64 1 +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%double2 = OpTypeVector %double 2 +%float4 = OpTypeVector %float 4 +%int64_0 = OpConstant %int64 0 +%int64_1 = OpConstant %int64 1 +%int64_2 = OpConstant %int64 2 +%int64_3 = OpConstant %int64 3 +%int64_array3 = OpTypeArray %int64 %int64_3 +%matrix_double2 = OpTypeMatrix %double2 2 +%struct1 = OpTypeStruct %uint %float4 +%struct2 = OpTypeStruct %struct1 %matrix_double2 %int64_array3 %uint +%struct1_ptr = OpTypePointer Function %struct1 +%matrix_double2_ptr = OpTypePointer Function %matrix_double2 +%int64_array_ptr = OpTypePointer Function %int64_array3 +%uint_ptr = OpTypePointer Function %uint +%struct2_ptr = OpTypePointer Function %struct2 +%const_uint = OpConstant %uint 0 +%const_int64_array = OpConstantComposite %int64_array3 %int64_0 %int64_1 %int64_2 +%const_double2 = OpConstantNull %double2 +%const_matrix_double2 = OpConstantNull %matrix_double2 +%undef_float4 = OpUndef %float4 +%const_struct1 = OpConstantComposite %struct1 %const_uint %undef_float4 +%const_struct2 = OpConstantComposite %struct2 %const_struct1 %const_matrix_double2 %const_int64_array %const_uint +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct2_ptr Function %const_struct2 +%3 = OpAccessChain %struct1_ptr %var %int64_0 +OpStore %3 %const_struct1 +%4 = OpAccessChain %matrix_double2_ptr %var %int64_1 +OpStore %4 %const_matrix_double2 +%5 = OpAccessChain %int64_array_ptr %var %int64_2 +OpStore %5 %const_int64_array +%6 = OpAccessChain %uint_ptr %var %int64_3 +OpStore %6 %const_uint +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ElideUncombinedAccessChains) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[var:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK-NOT: OpAccessChain +; CHECK: OpStore [[var]] [[const]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "elide_uncombined_access_chains" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint +%struct2 = OpTypeStruct %struct1 +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%struct2_ptr = OpTypePointer Function %struct2 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct2_ptr Function +%3 = OpAccessChain %struct1_ptr %var %uint_0 +%4 = OpAccessChain %uint_ptr %3 %uint_0 +OpStore %4 %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ElideSingleUncombinedAccessChains) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[array:%\w+]] = OpTypeArray [[uint]] +; CHECK: [[array_ptr:%\w+]] = OpTypePointer Function [[array]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[param:%\w+]] = OpFunctionParameter [[uint]] +; CHECK: [[var:%\w+]] = OpVariable [[array_ptr]] Function +; CHECK: [[access:%\w+]] = OpAccessChain {{.*}} [[var]] [[param]] +; CHECK: OpStore [[access]] [[const]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "elide_single_uncombined_access_chains" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%array = OpTypeArray %uint %uint_1 +%struct2 = OpTypeStruct %array +%uint_ptr = OpTypePointer Function %uint +%array_ptr = OpTypePointer Function %array +%struct2_ptr = OpTypePointer Function %struct2 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void %uint +%1 = OpFunction %void None %func +%param = OpFunctionParameter %uint +%2 = OpLabel +%var = OpVariable %struct2_ptr Function +%3 = OpAccessChain %array_ptr %var %uint_0 +%4 = OpAccessChain %uint_ptr %3 %param +OpStore %4 %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ReplaceWholeLoad) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] +; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]] +; CHECK: OpCompositeConstruct [[struct1]] [[l0]] [[l1]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "replace_whole_load" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%load = OpLoad %struct1 %var +%3 = OpAccessChain %uint_ptr %var %uint_0 +OpStore %3 %uint_0 +%4 = OpAccessChain %uint_ptr %var %uint_1 +OpStore %4 %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ReplaceWholeLoadCopyMemoryAccess) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]] Nontemporal +; CHECK: OpCompositeConstruct [[struct1]] [[l0]] [[null]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "replace_whole_load_copy_memory_access" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%load = OpLoad %struct1 %var Nontemporal +%3 = OpAccessChain %uint_ptr %var %uint_0 +OpStore %3 %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ReplaceWholeStore) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[const_struct:%\w+]] = OpConstantComposite [[struct1]] [[const]] [[const]] +; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[uint]] [[const_struct]] 0 +; CHECK: OpStore [[var0]] [[ex0]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "replace_whole_store" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%const_struct = OpConstantComposite %struct1 %uint_0 %uint_0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +OpStore %var %const_struct +%3 = OpAccessChain %uint_ptr %var %uint_0 +%4 = OpLoad %uint %3 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ReplaceWholeStoreCopyMemoryAccess) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[const_struct:%\w+]] = OpConstantComposite [[struct1]] [[const]] [[const]] +; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK-NOT: OpVariable +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[uint]] [[const_struct]] 0 +; CHECK: OpStore [[var0]] [[ex0]] Aligned 4 +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "replace_whole_store_copy_memory_access" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%const_struct = OpConstantComposite %struct1 %uint_0 %uint_0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +OpStore %var %const_struct Aligned 4 +%3 = OpAccessChain %uint_ptr %var %uint_0 +%4 = OpLoad %uint %3 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DontTouchVolatileLoad) { + const std::string text = R"( +; +; CHECK: [[struct:%\w+]] = OpTypeStruct +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[struct_ptr]] +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "dont_touch_volatile_load" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%3 = OpAccessChain %uint_ptr %var %uint_0 +%4 = OpLoad %uint %3 Volatile +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DontTouchVolatileStore) { + const std::string text = R"( +; +; CHECK: [[struct:%\w+]] = OpTypeStruct +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[struct_ptr]] +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "dont_touch_volatile_store" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%3 = OpAccessChain %uint_ptr %var %uint_0 +OpStore %3 %uint_0 Volatile +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DontTouchSpecNonFunctionVariable) { + const std::string text = R"( +; +; CHECK: [[struct:%\w+]] = OpTypeStruct +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Uniform [[struct]] +; CHECK: OpConstant +; CHECK-NEXT: OpVariable [[struct_ptr]] +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "dont_touch_spec_constant_access_chain" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint +%uint_ptr = OpTypePointer Uniform %uint +%struct1_ptr = OpTypePointer Uniform %struct1 +%uint_0 = OpConstant %uint 0 +%var = OpVariable %struct1_ptr Uniform +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%3 = OpAccessChain %uint_ptr %var %uint_0 +OpStore %3 %uint_0 Volatile +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DontTouchSpecConstantAccessChain) { + const std::string text = R"( +; +; CHECK: [[array:%\w+]] = OpTypeArray +; CHECK: [[array_ptr:%\w+]] = OpTypePointer Function [[array]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[array_ptr]] +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "dont_touch_spec_constant_access_chain" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%array = OpTypeArray %uint %uint_1 +%uint_ptr = OpTypePointer Function %uint +%array_ptr = OpTypePointer Function %array +%uint_0 = OpConstant %uint 0 +%spec_const = OpSpecConstant %uint 0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %array_ptr Function +%3 = OpAccessChain %uint_ptr %var %spec_const +OpStore %3 %uint_0 Volatile +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, NoPartialAccesses) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: OpLabel +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "no_partial_accesses" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%const = OpConstantNull %struct1 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +OpStore %var %const +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DontTouchPtrAccessChain) { + const std::string text = R"( +; +; CHECK: [[struct:%\w+]] = OpTypeStruct +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[struct_ptr]] +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "dont_touch_ptr_access_chain" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%3 = OpPtrAccessChain %uint_ptr %var %uint_0 %uint_0 +OpStore %3 %uint_0 +%4 = OpAccessChain %uint_ptr %var %uint_0 +OpStore %4 %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(ScalarReplacementTest, DontTouchInBoundsPtrAccessChain) { + const std::string text = R"( +; +; CHECK: [[struct:%\w+]] = OpTypeStruct +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[struct_ptr]] +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "dont_touch_in_bounds_ptr_access_chain" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%3 = OpInBoundsPtrAccessChain %uint_ptr %var %uint_0 %uint_0 +OpStore %3 %uint_0 +%4 = OpInBoundsAccessChain %uint_ptr %var %uint_0 +OpStore %4 %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(ScalarReplacementTest, DonTouchAliasedDecoration) { + const std::string text = R"( +; +; CHECK: [[struct:%\w+]] = OpTypeStruct +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[struct_ptr]] +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "aliased" +OpDecorate %var Aliased +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%3 = OpAccessChain %uint_ptr %var %uint_0 +%4 = OpLoad %uint %3 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, CopyRestrictDecoration) { + const std::string text = R"( +; +; CHECK: OpName +; CHECK-NEXT: OpDecorate [[var0:%\w+]] Restrict +; CHECK-NEXT: OpDecorate [[var1:%\w+]] Restrict +; CHECK: [[int:%\w+]] = OpTypeInt +; CHECK: [[struct:%\w+]] = OpTypeStruct +; CHECK: [[int_ptr:%\w+]] = OpTypePointer Function [[int]] +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: OpLabel +; CHECK-NEXT: [[var1]] = OpVariable [[int_ptr]] +; CHECK-NEXT: [[var0]] = OpVariable [[int_ptr]] +; CHECK-NOT: OpVariable [[struct_ptr]] +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "restrict" +OpDecorate %var Restrict +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%3 = OpAccessChain %uint_ptr %var %uint_0 +%4 = OpLoad %uint %3 +%5 = OpAccessChain %uint_ptr %var %uint_1 +%6 = OpLoad %uint %5 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DontClobberDecoratesOnSubtypes) { + const std::string text = R"( +; +; CHECK: OpDecorate [[array:%\w+]] ArrayStride 1 +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[array]] = OpTypeArray [[uint]] +; CHECK: [[array_ptr:%\w+]] = OpTypePointer Function [[array]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[array_ptr]] Function +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "array_stride" +OpDecorate %array ArrayStride 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%array = OpTypeArray %uint %uint_1 +%struct1 = OpTypeStruct %array +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void %uint +%1 = OpFunction %void None %func +%param = OpFunctionParameter %uint +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%3 = OpAccessChain %uint_ptr %var %uint_0 %param +%4 = OpLoad %uint %3 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DontCopyMemberDecorate) { + const std::string text = R"( +; +; CHECK-NOT: OpDecorate +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct:%\w+]] = OpTypeStruct [[uint]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[struct_ptr:%\w+]] = OpTypePointer Function [[struct]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[uint_ptr]] Function +; CHECK-NOT: OpVariable +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "member_decorate" +OpMemberDecorate %struct1 0 Offset 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%struct1 = OpTypeStruct %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%func = OpTypeFunction %void %uint +%1 = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct1_ptr Function +%3 = OpAccessChain %uint_ptr %var %uint_0 +%4 = OpLoad %uint %3 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, NoPartialAccesses2) { + const std::string text = R"( +; +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_ptr:%\w+]] = OpTypePointer Function [[float]] +; CHECK: OpVariable [[float_ptr]] Function +; CHECK: OpVariable [[float_ptr]] Function +; CHECK: OpVariable [[float_ptr]] Function +; CHECK: OpVariable [[float_ptr]] Function +; CHECK: OpVariable [[float_ptr]] Function +; CHECK: OpVariable [[float_ptr]] Function +; CHECK: OpVariable [[float_ptr]] Function +; CHECK-NOT: OpVariable +; +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %fo +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %S "S" +OpMemberName %S 0 "x" +OpMemberName %S 1 "y" +OpName %ts1 "ts1" +OpName %S_0 "S" +OpMemberName %S_0 0 "x" +OpMemberName %S_0 1 "y" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_s1" +OpMemberName %U_t 1 "g_s2" +OpMemberName %U_t 2 "g_s3" +OpName %_ "" +OpName %ts2 "ts2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpName %__0 "" +OpName %ts3 "ts3" +OpName %ts4 "ts4" +OpName %fo "fo" +OpMemberDecorate %S_0 0 Offset 0 +OpMemberDecorate %S_0 1 Offset 4 +OpMemberDecorate %U_t 0 Offset 0 +OpMemberDecorate %U_t 1 Offset 8 +OpMemberDecorate %U_t 2 Offset 16 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpDecorate %_Globals_ Block +OpDecorate %__0 DescriptorSet 0 +OpDecorate %__0 Binding 0 +OpDecorate %fo Location 0 +%void = OpTypeVoid +%15 = OpTypeFunction %void +%float = OpTypeFloat 32 +%S = OpTypeStruct %float %float +%_ptr_Function_S = OpTypePointer Function %S +%S_0 = OpTypeStruct %float %float +%U_t = OpTypeStruct %S_0 %S_0 %S_0 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_S_0 = OpTypePointer Uniform %S_0 +%_ptr_Function_float = OpTypePointer Function %float +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%_Globals_ = OpTypeStruct %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%__0 = OpVariable %_ptr_Uniform__Globals_ Uniform +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float +%fo = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %15 +%30 = OpLabel +%ts1 = OpVariable %_ptr_Function_S Function +%ts2 = OpVariable %_ptr_Function_S Function +%ts3 = OpVariable %_ptr_Function_S Function +%ts4 = OpVariable %_ptr_Function_S Function +%31 = OpAccessChain %_ptr_Uniform_S_0 %_ %int_0 +%32 = OpLoad %S_0 %31 +%33 = OpCompositeExtract %float %32 0 +%34 = OpAccessChain %_ptr_Function_float %ts1 %int_0 +OpStore %34 %33 +%35 = OpCompositeExtract %float %32 1 +%36 = OpAccessChain %_ptr_Function_float %ts1 %int_1 +OpStore %36 %35 +%37 = OpAccessChain %_ptr_Uniform_S_0 %_ %int_1 +%38 = OpLoad %S_0 %37 +%39 = OpCompositeExtract %float %38 0 +%40 = OpAccessChain %_ptr_Function_float %ts2 %int_0 +OpStore %40 %39 +%41 = OpCompositeExtract %float %38 1 +%42 = OpAccessChain %_ptr_Function_float %ts2 %int_1 +OpStore %42 %41 +%43 = OpAccessChain %_ptr_Uniform_uint %__0 %int_0 +%44 = OpLoad %uint %43 +%45 = OpINotEqual %bool %44 %uint_0 +OpSelectionMerge %46 None +OpBranchConditional %45 %47 %48 +%47 = OpLabel +%49 = OpLoad %S %ts1 +OpStore %ts3 %49 +OpBranch %46 +%48 = OpLabel +%50 = OpLoad %S %ts2 +OpStore %ts3 %50 +OpBranch %46 +%46 = OpLabel +%51 = OpLoad %S %ts3 +OpStore %ts4 %51 +%52 = OpAccessChain %_ptr_Function_float %ts4 %int_1 +%53 = OpLoad %float %52 +OpStore %fo %53 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[var0:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK-NOT: OpVariable +; CHECK: [[l0:%\w+]] = OpLoad [[uint]] [[var0]] +; CHECK: [[c0:%\w+]] = OpCompositeConstruct [[struct1]] [[l0]] [[null]] +; CHECK: [[e0:%\w+]] = OpCompositeExtract [[uint]] [[c0]] 0 +; CHECK: OpStore [[var1]] [[e0]] +; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "replace_whole_load" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var2 = OpVariable %struct1_ptr Function +%var1 = OpVariable %struct1_ptr Function +%load1 = OpLoad %struct1 %var1 +OpStore %var2 %load1 +%load2 = OpLoad %struct1 %var2 +%3 = OpCompositeExtract %uint %load2 0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, ReplaceWholeLoadAndStore2) { + // TODO: We can improve this case by ensuring that |var2| is processed first. + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[uint]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[null:%\w+]] = OpConstantNull [[uint]] +; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK-NOT: OpVariable +; CHECK: [[l0a:%\w+]] = OpLoad [[uint]] [[var0a]] +; CHECK: [[l0b:%\w+]] = OpLoad [[uint]] [[var0b]] +; CHECK: [[c0:%\w+]] = OpCompositeConstruct [[struct1]] [[l0b]] [[l0a]] +; CHECK: [[e0:%\w+]] = OpCompositeExtract [[uint]] [[c0]] 0 +; CHECK: OpStore [[var1]] [[e0]] +; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "replace_whole_load" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct1 = OpTypeStruct %uint %uint +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var1 = OpVariable %struct1_ptr Function +%var2 = OpVariable %struct1_ptr Function +%load1 = OpLoad %struct1 %var1 +OpStore %var2 %load1 +%load2 = OpLoad %struct1 %var2 +%3 = OpCompositeExtract %uint %load2 0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, CreateAmbiguousNullConstant1) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[struct_member:%\w+]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[null:%\w+]] = OpConstantNull [[struct_member]] +; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK-NOT: OpVariable +; CHECK: OpStore [[var1]] +; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "replace_whole_load" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct2 = OpTypeStruct %uint +%struct3 = OpTypeStruct %uint +%struct1 = OpTypeStruct %uint %struct2 +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var1 = OpVariable %struct1_ptr Function +%var2 = OpVariable %struct1_ptr Function +%load1 = OpLoad %struct1 %var1 +OpStore %var2 %load1 +%load2 = OpLoad %struct1 %var2 +%3 = OpCompositeExtract %uint %load2 0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, SpecConstantArray) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt +; CHECK: [[spec_const:%\w+]] = OpSpecConstant [[int]] 4 +; CHECK: [[spec_op:%\w+]] = OpSpecConstantOp [[int]] IAdd [[spec_const]] [[spec_const]] +; CHECK: [[array1:%\w+]] = OpTypeArray [[int]] [[spec_const]] +; CHECK: [[array2:%\w+]] = OpTypeArray [[int]] [[spec_op]] +; CHECK: [[ptr_array1:%\w+]] = OpTypePointer Function [[array1]] +; CHECK: [[ptr_array2:%\w+]] = OpTypePointer Function [[array2]] +; CHECK: OpLabel +; CHECK-NEXT: OpVariable [[ptr_array1]] Function +; CHECK-NEXT: OpVariable [[ptr_array2]] Function +; CHECK-NOT: OpVariable +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%spec_const = OpSpecConstant %int 4 +%spec_op = OpSpecConstantOp %int IAdd %spec_const %spec_const +%array_1 = OpTypeArray %int %spec_const +%array_2 = OpTypeArray %int %spec_op +%ptr_array_1_Function = OpTypePointer Function %array_1 +%ptr_array_2_Function = OpTypePointer Function %array_2 +%func = OpFunction %void None %void_fn +%1 = OpLabel +%var_1 = OpVariable %ptr_array_1_Function Function +%var_2 = OpVariable %ptr_array_2_Function Function +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, CreateAmbiguousNullConstant2) { + const std::string text = R"( +; +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct1:%\w+]] = OpTypeStruct [[uint]] [[struct_member:%\w+]] +; CHECK: [[uint_ptr:%\w+]] = OpTypePointer Function [[uint]] +; CHECK: [[const:%\w+]] = OpConstant [[uint]] 0 +; CHECK: [[null:%\w+]] = OpConstantNull [[struct_member]] +; CHECK: [[var0a:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[var1:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: [[var0b:%\w+]] = OpVariable [[uint_ptr]] Function +; CHECK: OpStore [[var1]] +; CHECK: [[l1:%\w+]] = OpLoad [[uint]] [[var1]] +; CHECK: [[c1:%\w+]] = OpCompositeConstruct [[struct1]] [[l1]] [[null]] +; CHECK: [[e1:%\w+]] = OpCompositeExtract [[uint]] [[c1]] 0 +; +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %func "replace_whole_load" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct3 = OpTypeStruct %uint +%struct2 = OpTypeStruct %uint +%struct1 = OpTypeStruct %uint %struct2 +%uint_ptr = OpTypePointer Function %uint +%struct1_ptr = OpTypePointer Function %struct1 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%func = OpTypeFunction %void +%1 = OpFunction %void None %func +%2 = OpLabel +%var1 = OpVariable %struct1_ptr Function +%var2 = OpVariable %struct1_ptr Function +%load1 = OpLoad %struct1 %var1 +OpStore %var2 %load1 +%load2 = OpLoad %struct1 %var2 +%3 = OpCompositeExtract %uint %load2 0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +// Test that a struct of size 4 is not replaced when there is a limit of 2. +TEST_F(ScalarReplacementTest, TestLimit) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %6 "simple_struct" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeStruct %2 %2 %2 %2 +%4 = OpTypePointer Function %3 +%5 = OpTypePointer Function %2 +%6 = OpTypeFunction %2 +%7 = OpConstantNull %3 +%8 = OpConstant %2 0 +%9 = OpConstant %2 1 +%10 = OpConstant %2 2 +%11 = OpConstant %2 3 +%12 = OpFunction %2 None %6 +%13 = OpLabel +%14 = OpVariable %4 Function %7 +%15 = OpInBoundsAccessChain %5 %14 %8 +%16 = OpLoad %2 %15 +%17 = OpAccessChain %5 %14 %10 +%18 = OpLoad %2 %17 +%19 = OpIAdd %2 %16 %18 +OpReturnValue %19 +OpFunctionEnd + )"; + + auto result = + SinglePassRunAndDisassemble(text, true, false, 2); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// Test that a struct of size 4 is replaced when there is a limit of 0 (no +// limit). This is the same spir-v as a test above, so we do not check that it +// is correctly transformed. We leave that to the test above. +TEST_F(ScalarReplacementTest, TestUnimited) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %6 "simple_struct" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeStruct %2 %2 %2 %2 +%4 = OpTypePointer Function %3 +%5 = OpTypePointer Function %2 +%6 = OpTypeFunction %2 +%7 = OpConstantNull %3 +%8 = OpConstant %2 0 +%9 = OpConstant %2 1 +%10 = OpConstant %2 2 +%11 = OpConstant %2 3 +%12 = OpFunction %2 None %6 +%13 = OpLabel +%14 = OpVariable %4 Function %7 +%15 = OpInBoundsAccessChain %5 %14 %8 +%16 = OpLoad %2 %15 +%17 = OpAccessChain %5 %14 %10 +%18 = OpLoad %2 %17 +%19 = OpIAdd %2 %16 %18 +OpReturnValue %19 +OpFunctionEnd + )"; + + auto result = + SinglePassRunAndDisassemble(text, true, false, 0); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + +TEST_F(ScalarReplacementTest, AmbigousPointer) { + const std::string text = R"( +; CHECK: [[s1:%\w+]] = OpTypeStruct %uint +; CHECK: [[s2:%\w+]] = OpTypeStruct %uint +; CHECK: [[s3:%\w+]] = OpTypeStruct [[s2]] +; CHECK: [[s3_const:%\w+]] = OpConstantComposite [[s3]] +; CHECK: [[s2_ptr:%\w+]] = OpTypePointer Function [[s2]] +; CHECK: OpCompositeExtract [[s2]] [[s3_const]] + + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %_struct_7 = OpTypeStruct %uint + %_struct_8 = OpTypeStruct %uint + %_struct_9 = OpTypeStruct %_struct_8 + %uint_1 = OpConstant %uint 1 + %11 = OpConstantComposite %_struct_8 %uint_1 + %12 = OpConstantComposite %_struct_9 %11 +%_ptr_Function__struct_9 = OpTypePointer Function %_struct_9 +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %2 = OpFunction %void None %5 + %15 = OpLabel + %var = OpVariable %_ptr_Function__struct_9 Function + OpStore %var %12 + %ld = OpLoad %_struct_9 %var + %ex = OpCompositeExtract %_struct_8 %ld 0 + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +// Test that scalar replacement does not crash when there is an OpAccessChain +// with no index. If we choose to handle this case in the future, then the +// result can change. +TEST_F(ScalarReplacementTest, TestAccessChainWithNoIndexes) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginLowerLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %_struct_5 = OpTypeStruct %float +%_ptr_Function__struct_5 = OpTypePointer Function %_struct_5 + %1 = OpFunction %void None %3 + %7 = OpLabel + %8 = OpVariable %_ptr_Function__struct_5 Function + %9 = OpAccessChain %_ptr_Function__struct_5 %8 + OpReturn + OpFunctionEnd + )"; + + auto result = + SinglePassRunAndDisassemble(text, true, false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// Test that id overflow is handled gracefully. +TEST_F(ScalarReplacementTest, IdBoundOverflow1) { + const std::string text = R"( +OpCapability ImageQuery +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +OpDecorate %4194302 DescriptorSet 1073495039 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeStruct %6 %6 +%557056 = OpTypeStruct %7 +%9 = OpTypePointer Function %7 +%18 = OpTypeFunction %7 %9 +%4 = OpFunction %2 Pure|Const %3 +%1836763 = OpLabel +%4194302 = OpVariable %9 Function +%10 = OpVariable %9 Function +OpKill +%4194301 = OpLabel +%524296 = OpLoad %7 %4194302 +OpKill +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true, false); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +// Test that id overflow is handled gracefully. +TEST_F(ScalarReplacementTest, IdBoundOverflow2) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %17 +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeVector %6 4 +%8 = OpTypeStruct %7 +%9 = OpTypePointer Function %8 +%16 = OpTypePointer Output %7 +%21 = OpTypeInt 32 1 +%22 = OpConstant %21 0 +%23 = OpTypePointer Function %7 +%17 = OpVariable %16 Output +%4 = OpFunction %2 None %3 +%5 = OpLabel +%4194300 = OpVariable %23 Function +%10 = OpVariable %9 Function +%4194301 = OpAccessChain %23 %10 %22 +%4194302 = OpLoad %7 %4194301 +OpStore %4194300 %4194302 +%15 = OpLoad %7 %4194300 +OpStore %17 %15 +OpReturn +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true, false); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +// Test that id overflow is handled gracefully. +TEST_F(ScalarReplacementTest, IdBoundOverflow3) { + const std::string text = R"( +OpCapability InterpolationFunction +OpExtension "z" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeFloat 32 +%7 = OpTypeStruct %6 %6 +%9 = OpTypePointer Function %7 +%18 = OpTypeFunction %7 %9 +%21 = OpTypeInt 32 0 +%22 = OpConstant %21 4293000676 +%4194302 = OpConstantNull %6 +%4 = OpFunction %2 Inline|Pure %3 +%786464 = OpLabel +%4194298 = OpVariable %9 Function +%10 = OpVariable %9 Function +%4194299 = OpUDiv %21 %22 %22 +%4194300 = OpLoad %7 %10 +%50959 = OpLoad %7 %4194298 +OpKill +OpFunctionEnd +%1 = OpFunction %7 None %18 +%19 = OpFunctionParameter %9 +%147667 = OpLabel +%2044391 = OpUDiv %21 %22 %22 +%25 = OpLoad %7 %19 +OpReturnValue %25 +OpFunctionEnd +%4194295 = OpFunction %2 None %3 +%4194296 = OpLabel +OpKill +OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}, + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true, false); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +// Test that replacements for OpAccessChain do not go out of bounds. +// https://github.com/KhronosGroup/SPIRV-Tools/issues/2609. +TEST_F(ScalarReplacementTest, OutOfBoundOpAccessChain) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %_GLF_color + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 310 + OpName %main "main" + OpName %a "a" + OpName %_GLF_color "_GLF_color" + OpDecorate %_GLF_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %int_1 = OpConstant %int 1 + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%_ptr_Function__arr_float_uint_1 = OpTypePointer Function %_arr_float_uint_1 +%_ptr_Function_float = OpTypePointer Function %float +%_ptr_Output_float = OpTypePointer Output %float + %_GLF_color = OpVariable %_ptr_Output_float Output + %main = OpFunction %void None %3 + %5 = OpLabel + %a = OpVariable %_ptr_Function__arr_float_uint_1 Function + %21 = OpAccessChain %_ptr_Function_float %a %int_1 + %22 = OpLoad %float %21 + OpStore %_GLF_color %22 + OpReturn + OpFunctionEnd + )"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + auto result = + SinglePassRunAndDisassemble(text, true, false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(ScalarReplacementTest, CharIndex) { + const std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer Function [[int]] +; CHECK: OpVariable [[ptr]] Function +OpCapability Shader +OpCapability Int8 +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1024 = OpConstant %int 1024 +%char = OpTypeInt 8 0 +%char_1 = OpConstant %char 1 +%array = OpTypeArray %int %int_1024 +%ptr_func_array = OpTypePointer Function %array +%ptr_func_int = OpTypePointer Function %int +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_func_array Function +%gep = OpAccessChain %ptr_func_int %var %char_1 +OpStore %gep %int_1024 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true, 0); +} + +TEST_F(ScalarReplacementTest, OutOfBoundsOpAccessChainNegative) { + const std::string text = R"( +OpCapability Shader +OpCapability Int8 +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1024 = OpConstant %int 1024 +%char = OpTypeInt 8 1 +%char_n1 = OpConstant %char -1 +%array = OpTypeArray %int %int_1024 +%ptr_func_array = OpTypePointer Function %array +%ptr_func_int = OpTypePointer Function %int +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%var = OpVariable %ptr_func_array Function +%gep = OpAccessChain %ptr_func_int %var %char_n1 +OpStore %gep %int_1024 +OpReturn +OpFunctionEnd +)"; + + auto result = + SinglePassRunAndDisassemble(text, true, true, 0); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(ScalarReplacementTest, RelaxedPrecisionMemberDecoration) { + const std::string text = R"( +; CHECK: OpDecorate {{%\w+}} RelaxedPrecision +; CHECK: OpDecorate [[new_var:%\w+]] RelaxedPrecision +; CHECK: [[new_var]] = OpVariable %_ptr_Function_v3float Function +; CHECK: OpLoad %v3float [[new_var]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "Draw2DTexCol_VS" %2 %3 + OpSource HLSL 600 + OpDecorate %2 Location 0 + OpDecorate %3 Location 1 + OpDecorate %3 RelaxedPrecision + OpMemberDecorate %_struct_4 1 RelaxedPrecision + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %void = OpTypeVoid + %11 = OpTypeFunction %void + %_struct_4 = OpTypeStruct %v3float %v3float +%_ptr_Function__struct_4 = OpTypePointer Function %_struct_4 +%_ptr_Function_v3float = OpTypePointer Function %v3float + %2 = OpVariable %_ptr_Input_v3float Input + %3 = OpVariable %_ptr_Input_v3float Input + %1 = OpFunction %void None %11 + %14 = OpLabel + %15 = OpVariable %_ptr_Function__struct_4 Function + %16 = OpLoad %v3float %2 + %17 = OpLoad %v3float %3 + %18 = OpCompositeConstruct %_struct_4 %16 %17 + OpStore %15 %18 + %19 = OpAccessChain %_ptr_Function_v3float %15 %int_1 + %20 = OpLoad %v3float %19 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DebugDeclare) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +%test = OpString "test" +OpName %6 "simple_struct" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%uint_32 = OpConstant %2 32 +%3 = OpTypeStruct %2 %2 %2 %2 +%4 = OpTypePointer Function %3 +%5 = OpTypePointer Function %2 +%6 = OpTypeFunction %2 +%7 = OpConstantNull %3 +%8 = OpConstant %2 0 +%9 = OpConstant %2 1 +%10 = OpConstant %2 2 +%11 = OpConstant %2 3 +%null_expr = OpExtInst %1 %ext DebugExpression +%src = OpExtInst %1 %ext DebugSource %test +%cu = OpExtInst %1 %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %1 %ext DebugTypeBasic %test %uint_32 Float +%main_ty = OpExtInst %1 %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %1 +%dbg_main = OpExtInst %1 %ext DebugFunction %test %main_ty %src 0 0 %cu %test FlagIsProtected|FlagIsPrivate 0 %12 +%dbg_foo = OpExtInst %1 %ext DebugLocalVariable %test %dbg_tf %src 0 0 %dbg_main FlagIsLocal +%12 = OpFunction %2 None %6 +%13 = OpLabel +%scope = OpExtInst %1 %ext DebugScope %dbg_main +%14 = OpVariable %4 Function %7 + +; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref +; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable +; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]] +; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3 +; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 +; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 +; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 +; CHECK-NOT: DebugDeclare +%decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr + +%15 = OpInBoundsAccessChain %5 %14 %8 +%16 = OpLoad %2 %15 +%17 = OpAccessChain %5 %14 %10 +%18 = OpLoad %2 %17 +%19 = OpIAdd %2 %16 %18 +OpReturnValue %19 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DebugValue) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +%test = OpString "test" +OpName %6 "simple_struct" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%uint_32 = OpConstant %2 32 +%3 = OpTypeStruct %2 %2 %2 %2 +%4 = OpTypePointer Function %3 +%5 = OpTypePointer Function %2 +%6 = OpTypeFunction %2 +%7 = OpConstantNull %3 +%8 = OpConstant %2 0 +%9 = OpConstant %2 1 +%10 = OpConstant %2 2 +%11 = OpConstant %2 3 +%deref = OpExtInst %1 %ext DebugOperation Deref +%deref_expr = OpExtInst %1 %ext DebugExpression %deref +%null_expr = OpExtInst %1 %ext DebugExpression +%src = OpExtInst %1 %ext DebugSource %test +%cu = OpExtInst %1 %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %1 %ext DebugTypeBasic %test %uint_32 Float +%main_ty = OpExtInst %1 %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %1 +%dbg_main = OpExtInst %1 %ext DebugFunction %test %main_ty %src 0 0 %cu %test FlagIsProtected|FlagIsPrivate 0 %12 +%dbg_foo = OpExtInst %1 %ext DebugLocalVariable %test %dbg_tf %src 0 0 %dbg_main FlagIsLocal +%12 = OpFunction %2 None %6 +%13 = OpLabel +%scope = OpExtInst %1 %ext DebugScope %dbg_main +%14 = OpVariable %4 Function %7 + +; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref +; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]] +; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable +; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_2 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_3 +%value = OpExtInst %1 %ext DebugValue %dbg_foo %14 %deref_expr + +%15 = OpInBoundsAccessChain %5 %14 %8 +%16 = OpLoad %2 %15 +%17 = OpAccessChain %5 %14 %10 +%18 = OpLoad %2 %17 +%19 = OpIAdd %2 %16 %18 +OpReturnValue %19 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(ScalarReplacementTest, DebugDeclareRecursive) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +%test = OpString "test" +OpName %6 "simple_struct" +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%uint_32 = OpConstant %2 32 +%float = OpTypeFloat 32 +%float_1 = OpConstant %float 1 +%member = OpTypeStruct %2 %float +%3 = OpTypeStruct %2 %member %float +%4 = OpTypePointer Function %3 +%5 = OpTypePointer Function %2 +%ptr_float_Function = OpTypePointer Function %float +%6 = OpTypeFunction %2 +%cmember = OpConstantComposite %member %uint_32 %float_1 +%7 = OpConstantComposite %3 %uint_32 %cmember %float_1 +%8 = OpConstant %2 0 +%9 = OpConstant %2 1 +%10 = OpConstant %2 2 +%null_expr = OpExtInst %1 %ext DebugExpression +%src = OpExtInst %1 %ext DebugSource %test +%cu = OpExtInst %1 %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %1 %ext DebugTypeBasic %test %uint_32 Float +%main_ty = OpExtInst %1 %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %1 +%dbg_main = OpExtInst %1 %ext DebugFunction %test %main_ty %src 0 0 %cu %test FlagIsProtected|FlagIsPrivate 0 %12 +%dbg_foo = OpExtInst %1 %ext DebugLocalVariable %test %dbg_tf %src 0 0 %dbg_main FlagIsLocal +%12 = OpFunction %2 None %6 +%13 = OpLabel +%scope = OpExtInst %1 %ext DebugScope %dbg_main +%14 = OpVariable %4 Function %7 + +; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref +; CHECK: [[dbg_local_var:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable +; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]] +; CHECK: [[repl2:%\w+]] = OpVariable %_ptr_Function_float Function %float_1 +; CHECK: [[repl1:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 +; CHECK: [[repl3:%\w+]] = OpVariable %_ptr_Function_float Function %float_1 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl3]] [[deref_expr]] %int_2 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl1]] [[deref_expr]] %int_1 %int_0 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl2]] [[deref_expr]] %int_1 %int_1 +; CHECK: [[repl0:%\w+]] = OpVariable %_ptr_Function_uint Function %uint_32 +; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_local_var]] [[repl0]] [[deref_expr]] %int_0 +; CHECK-NOT: DebugDeclare +%decl = OpExtInst %1 %ext DebugDeclare %dbg_foo %14 %null_expr + +%15 = OpInBoundsAccessChain %5 %14 %8 +%16 = OpLoad %2 %15 +%17 = OpAccessChain %ptr_float_Function %14 %10 +%18 = OpLoad %float %17 +%value = OpConvertFToU %2 %18 +%19 = OpIAdd %2 %16 %value +OpReturnValue %19 +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/set_spec_const_default_value_test.cpp b/third_party/spirv-tools/test/opt/set_spec_const_default_value_test.cpp new file mode 100644 index 0000000..5e63862 --- /dev/null +++ b/third_party/spirv-tools/test/opt/set_spec_const_default_value_test.cpp @@ -0,0 +1,1077 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using testing::Eq; +using SpecIdToValueStrMap = + SetSpecConstantDefaultValuePass::SpecIdToValueStrMap; +using SpecIdToValueBitPatternMap = + SetSpecConstantDefaultValuePass::SpecIdToValueBitPatternMap; + +struct DefaultValuesStringParsingTestCase { + const char* default_values_str; + bool expect_success; + SpecIdToValueStrMap expected_map; +}; + +using DefaultValuesStringParsingTest = + ::testing::TestWithParam; + +TEST_P(DefaultValuesStringParsingTest, TestCase) { + const auto& tc = GetParam(); + auto actual_map = SetSpecConstantDefaultValuePass::ParseDefaultValuesString( + tc.default_values_str); + if (tc.expect_success) { + EXPECT_NE(nullptr, actual_map); + if (actual_map) { + EXPECT_THAT(*actual_map, Eq(tc.expected_map)); + } + } else { + EXPECT_EQ(nullptr, actual_map); + } +} + +INSTANTIATE_TEST_SUITE_P( + ValidString, DefaultValuesStringParsingTest, + ::testing::ValuesIn(std::vector{ + // 0. empty map + {"", true, SpecIdToValueStrMap{}}, + // 1. one pair + {"100:1024", true, SpecIdToValueStrMap{{100, "1024"}}}, + // 2. two pairs + {"100:1024 200:2048", true, + SpecIdToValueStrMap{{100, "1024"}, {200, "2048"}}}, + // 3. spaces between entries + {"100:1024 \n \r \t \v \f 200:2048", true, + SpecIdToValueStrMap{{100, "1024"}, {200, "2048"}}}, + // 4. \t, \n, \r and spaces before spec id + {" \n \r\t \t \v \f 100:1024", true, + SpecIdToValueStrMap{{100, "1024"}}}, + // 5. \t, \n, \r and spaces after value string + {"100:1024 \n \r\t \t \v \f ", true, + SpecIdToValueStrMap{{100, "1024"}}}, + // 6. maximum spec id + {"4294967295:0", true, SpecIdToValueStrMap{{4294967295, "0"}}}, + // 7. minimum spec id + {"0:100", true, SpecIdToValueStrMap{{0, "100"}}}, + // 8. random content without spaces are allowed + {"200:random_stuff", true, SpecIdToValueStrMap{{200, "random_stuff"}}}, + // 9. support hex format spec id (just because we use the + // ParseNumber() utility) + {"0x100:1024", true, SpecIdToValueStrMap{{256, "1024"}}}, + // 10. multiple entries + {"101:1 102:2 103:3 104:4 200:201 9999:1000 0x100:333", true, + SpecIdToValueStrMap{{101, "1"}, + {102, "2"}, + {103, "3"}, + {104, "4"}, + {200, "201"}, + {9999, "1000"}, + {256, "333"}}}, + // 11. default value in hex float format + {"100:0x0.3p10", true, SpecIdToValueStrMap{{100, "0x0.3p10"}}}, + // 12. default value in decimal float format + {"100:1.5e-13", true, SpecIdToValueStrMap{{100, "1.5e-13"}}}, + })); + +INSTANTIATE_TEST_SUITE_P( + InvalidString, DefaultValuesStringParsingTest, + ::testing::ValuesIn(std::vector{ + // 0. missing default value + {"100:", false, SpecIdToValueStrMap{}}, + // 1. spec id is not an integer + {"100.0:200", false, SpecIdToValueStrMap{}}, + // 2. spec id is not a number + {"something_not_a_number:1", false, SpecIdToValueStrMap{}}, + // 3. only spec id number + {"100", false, SpecIdToValueStrMap{}}, + // 4. same spec id defined multiple times + {"100:20 100:21", false, SpecIdToValueStrMap{}}, + // 5. Multiple definition of an identical spec id in different forms + // is not allowed + {"0x100:100 256:200", false, SpecIdToValueStrMap{}}, + // 6. empty spec id + {":3", false, SpecIdToValueStrMap{}}, + // 7. only colon + {":", false, SpecIdToValueStrMap{}}, + // 8. spec id overflow + {"4294967296:200", false, SpecIdToValueStrMap{}}, + // 9. spec id less than 0 + {"-1:200", false, SpecIdToValueStrMap{}}, + // 10. nullptr + {nullptr, false, SpecIdToValueStrMap{}}, + // 11. only a number is invalid + {"1234", false, SpecIdToValueStrMap{}}, + // 12. invalid entry separator + {"12:34;23:14", false, SpecIdToValueStrMap{}}, + // 13. invalid spec id and default value separator + {"12@34", false, SpecIdToValueStrMap{}}, + // 14. spaces before colon + {"100 :1024", false, SpecIdToValueStrMap{}}, + // 15. spaces after colon + {"100: 1024", false, SpecIdToValueStrMap{}}, + // 16. spec id represented in hex float format is invalid + {"0x3p10:200", false, SpecIdToValueStrMap{}}, + })); + +struct SetSpecConstantDefaultValueInStringFormTestCase { + const char* code; + SpecIdToValueStrMap default_values; + const char* expected; +}; + +using SetSpecConstantDefaultValueInStringFormParamTest = PassTest< + ::testing::TestWithParam>; + +TEST_P(SetSpecConstantDefaultValueInStringFormParamTest, TestCase) { + const auto& tc = GetParam(); + SinglePassRunAndCheck( + tc.code, tc.expected, /* skip_nop = */ false, tc.default_values); +} + +INSTANTIATE_TEST_SUITE_P( + ValidCases, SetSpecConstantDefaultValueInStringFormParamTest, + ::testing::ValuesIn(std::vector< + SetSpecConstantDefaultValueInStringFormTestCase>{ + // 0. Empty. + {"", SpecIdToValueStrMap{}, ""}, + // 1. Empty with non-empty values to set. + {"", SpecIdToValueStrMap{{1, "100"}, {2, "200"}}, ""}, + // 2. Bool type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n", + // default values + SpecIdToValueStrMap{{100, "false"}, {101, "true"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantFalse %bool\n" + "%2 = OpSpecConstantTrue %bool\n", + }, + // 3. 32-bit int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 10\n" + "%2 = OpSpecConstant %int 11\n" + "%3 = OpSpecConstant %int 11\n", + // default values + SpecIdToValueStrMap{ + {100, "2147483647"}, {101, "0xffffffff"}, {102, "-42"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 2147483647\n" + "%2 = OpSpecConstant %int -1\n" + "%3 = OpSpecConstant %int -42\n", + }, + // 4. 64-bit uint type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%ulong = OpTypeInt 64 0\n" + "%1 = OpSpecConstant %ulong 10\n" + "%2 = OpSpecConstant %ulong 11\n", + // default values + SpecIdToValueStrMap{{100, "18446744073709551614"}, {101, "0x100"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%ulong = OpTypeInt 64 0\n" + "%1 = OpSpecConstant %ulong 18446744073709551614\n" + "%2 = OpSpecConstant %ulong 256\n", + }, + // 5. 32-bit float type. + { + // code + "OpDecorate %1 SpecId 101\n" + "OpDecorate %2 SpecId 102\n" + "%float = OpTypeFloat 32\n" + "%1 = OpSpecConstant %float 200\n" + "%2 = OpSpecConstant %float 201\n", + // default values + SpecIdToValueStrMap{{101, "-0x1.fffffep+128"}, {102, "2.5"}}, + // expected + "OpDecorate %1 SpecId 101\n" + "OpDecorate %2 SpecId 102\n" + "%float = OpTypeFloat 32\n" + "%1 = OpSpecConstant %float -0x1.fffffep+128\n" + "%2 = OpSpecConstant %float 2.5\n", + }, + // 6. 64-bit float type. + { + // code + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 3.14159265358979\n" + "%2 = OpSpecConstant %double 0.14285\n", + // default values + SpecIdToValueStrMap{{201, "0x1.fffffffffffffp+1024"}, + {202, "-32.5"}}, + // expected + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 0x1.fffffffffffffp+1024\n" + "%2 = OpSpecConstant %double -32.5\n", + }, + // 7. SpecId not found, expect no modification. + { + // code + "OpDecorate %1 SpecId 201\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 3.14159265358979\n", + // default values + SpecIdToValueStrMap{{8888, "0.0"}}, + // expected + "OpDecorate %1 SpecId 201\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 3.14159265358979\n", + }, + // 8. Multiple types of spec constants. + { + // code + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "OpDecorate %3 SpecId 203\n" + "%bool = OpTypeBool\n" + "%int = OpTypeInt 32 1\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 3.14159265358979\n" + "%2 = OpSpecConstant %int 1024\n" + "%3 = OpSpecConstantTrue %bool\n", + // default values + SpecIdToValueStrMap{ + {201, "0x1.fffffffffffffp+1024"}, + {202, "2048"}, + {203, "false"}, + }, + // expected + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "OpDecorate %3 SpecId 203\n" + "%bool = OpTypeBool\n" + "%int = OpTypeInt 32 1\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 0x1.fffffffffffffp+1024\n" + "%2 = OpSpecConstant %int 2048\n" + "%3 = OpSpecConstantFalse %bool\n", + }, + // 9. Ignore other decorations. + { + // code + "OpDecorate %1 ArrayStride 4\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueStrMap{{4, "0x7fffffff"}}, + // expected + "OpDecorate %1 ArrayStride 4\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 100\n", + }, + // 10. Distinguish from other decorations. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %1 ArrayStride 4\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueStrMap{{4, "0x7fffffff"}, {100, "0xffffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %1 ArrayStride 4\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int -1\n", + }, + // 11. Decorate through decoration group. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueStrMap{{100, "0x7fffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 2147483647\n", + }, + // 12. Ignore other decorations in decoration group. + { + // code + "OpDecorate %1 ArrayStride 4\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueStrMap{{4, "0x7fffffff"}}, + // expected + "OpDecorate %1 ArrayStride 4\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + }, + // 13. Distinguish from other decorations in decoration group. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %1 ArrayStride 4\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueStrMap{{100, "0x7fffffff"}, {4, "0x00000001"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %1 ArrayStride 4\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 2147483647\n", + }, + // 14. Unchanged bool default value + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n", + // default values + SpecIdToValueStrMap{{100, "true"}, {101, "false"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n", + }, + // 15. Unchanged int default values + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%int = OpTypeInt 32 1\n" + "%ulong = OpTypeInt 64 0\n" + "%1 = OpSpecConstant %int 10\n" + "%2 = OpSpecConstant %ulong 11\n", + // default values + SpecIdToValueStrMap{{100, "10"}, {101, "11"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%int = OpTypeInt 32 1\n" + "%ulong = OpTypeInt 64 0\n" + "%1 = OpSpecConstant %int 10\n" + "%2 = OpSpecConstant %ulong 11\n", + }, + // 16. Unchanged float default values + { + // code + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "%float = OpTypeFloat 32\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %float 3.1415\n" + "%2 = OpSpecConstant %double 0.14285\n", + // default values + SpecIdToValueStrMap{{201, "3.1415"}, {202, "0.14285"}}, + // expected + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "%float = OpTypeFloat 32\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %float 3.1415\n" + "%2 = OpSpecConstant %double 0.14285\n", + }, + // 17. OpGroupDecorate may have multiple target ids defined by the same + // eligible spec constant + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2 %2 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueStrMap{{100, "0xffffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2 %2 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int -1\n", + }, + })); + +INSTANTIATE_TEST_SUITE_P( + InvalidCases, SetSpecConstantDefaultValueInStringFormParamTest, + ::testing::ValuesIn(std::vector< + SetSpecConstantDefaultValueInStringFormTestCase>{ + // 0. Do not crash when decoration group is not used. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n" + "%3 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueStrMap{{100, "0x7fffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n" + "%3 = OpSpecConstant %int 100\n", + }, + // 1. Do not crash when target does not exist. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n", + // default values + SpecIdToValueStrMap{{100, "0x7fffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n", + }, + // 2. Do nothing when SpecId decoration is not attached to a + // non-spec-constant instruction. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n" + "%int_101 = OpConstant %int 101\n", + // default values + SpecIdToValueStrMap{{100, "0x7fffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n" + "%int_101 = OpConstant %int 101\n", + }, + // 3. Do nothing when SpecId decoration is not attached to a + // OpSpecConstant{|True|False} instruction. + { + // code + "OpDecorate %1 SpecId 100\n" + "%int = OpTypeInt 32 1\n" + "%3 = OpSpecConstant %int 101\n" + "%1 = OpSpecConstantOp %int IAdd %3 %3\n", + // default values + SpecIdToValueStrMap{{100, "0x7fffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%int = OpTypeInt 32 1\n" + "%3 = OpSpecConstant %int 101\n" + "%1 = OpSpecConstantOp %int IAdd %3 %3\n", + }, + // 4. Do not crash and do nothing when SpecId decoration is applied to + // multiple spec constants. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2 %3 %4\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n" + "%3 = OpSpecConstant %int 200\n" + "%4 = OpSpecConstant %int 300\n", + // default values + SpecIdToValueStrMap{{100, "0xffffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2 %3 %4\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n" + "%3 = OpSpecConstant %int 200\n" + "%4 = OpSpecConstant %int 300\n", + }, + // 5. Do not crash and do nothing when SpecId decoration is attached to + // non-spec-constants (invalid case). + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%2 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%int_100 = OpConstant %int 100\n", + // default values + SpecIdToValueStrMap{{100, "0xffffffff"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%2 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%int_100 = OpConstant %int 100\n", + }, + // 6. Boolean type spec constant cannot be set with numeric values in + // string form. i.e. only 'true' and 'false' are acceptable for setting + // boolean type spec constants. Nothing should be done if numeric values + // in string form are provided. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "OpDecorate %4 SpecId 103\n" + "OpDecorate %5 SpecId 104\n" + "OpDecorate %6 SpecId 105\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n" + "%3 = OpSpecConstantTrue %bool\n" + "%4 = OpSpecConstantTrue %bool\n" + "%5 = OpSpecConstantTrue %bool\n" + "%6 = OpSpecConstantFalse %bool\n", + // default values + SpecIdToValueStrMap{{100, "0"}, + {101, "1"}, + {102, "0x0"}, + {103, "0.0"}, + {104, "-0.0"}, + {105, "0x12345678"}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "OpDecorate %4 SpecId 103\n" + "OpDecorate %5 SpecId 104\n" + "OpDecorate %6 SpecId 105\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n" + "%3 = OpSpecConstantTrue %bool\n" + "%4 = OpSpecConstantTrue %bool\n" + "%5 = OpSpecConstantTrue %bool\n" + "%6 = OpSpecConstantFalse %bool\n", + }, + })); + +struct SetSpecConstantDefaultValueInBitPatternFormTestCase { + const char* code; + SpecIdToValueBitPatternMap default_values; + const char* expected; +}; + +using SetSpecConstantDefaultValueInBitPatternFormParamTest = + PassTest<::testing::TestWithParam< + SetSpecConstantDefaultValueInBitPatternFormTestCase>>; + +TEST_P(SetSpecConstantDefaultValueInBitPatternFormParamTest, TestCase) { + const auto& tc = GetParam(); + SinglePassRunAndCheck( + tc.code, tc.expected, /* skip_nop = */ false, tc.default_values); +} + +INSTANTIATE_TEST_SUITE_P( + ValidCases, SetSpecConstantDefaultValueInBitPatternFormParamTest, + ::testing::ValuesIn(std::vector< + SetSpecConstantDefaultValueInBitPatternFormTestCase>{ + // 0. Empty. + {"", SpecIdToValueBitPatternMap{}, ""}, + // 1. Empty with non-empty values to set. + {"", SpecIdToValueBitPatternMap{{1, {100}}, {2, {200}}}, ""}, + // 2. Baisc bool type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x0}}, {101, {0x1}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantFalse %bool\n" + "%2 = OpSpecConstantTrue %bool\n", + }, + // 3. 32-bit int type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 10\n" + "%2 = OpSpecConstant %int 11\n" + "%3 = OpSpecConstant %int 11\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {2147483647}}, {101, {0xffffffff}}, {102, {0xffffffd6}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 2147483647\n" + "%2 = OpSpecConstant %int -1\n" + "%3 = OpSpecConstant %int -42\n", + }, + // 4. 64-bit uint type. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%ulong = OpTypeInt 64 0\n" + "%1 = OpSpecConstant %ulong 10\n" + "%2 = OpSpecConstant %ulong 11\n", + // default values + SpecIdToValueBitPatternMap{{100, {0xFFFFFFFE, 0xFFFFFFFF}}, + {101, {0x100, 0x0}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%ulong = OpTypeInt 64 0\n" + "%1 = OpSpecConstant %ulong 18446744073709551614\n" + "%2 = OpSpecConstant %ulong 256\n", + }, + // 5. 32-bit float type. + { + // code + "OpDecorate %1 SpecId 101\n" + "OpDecorate %2 SpecId 102\n" + "%float = OpTypeFloat 32\n" + "%1 = OpSpecConstant %float 200\n" + "%2 = OpSpecConstant %float 201\n", + // default values + SpecIdToValueBitPatternMap{{101, {0xffffffff}}, + {102, {0x40200000}}}, + // expected + "OpDecorate %1 SpecId 101\n" + "OpDecorate %2 SpecId 102\n" + "%float = OpTypeFloat 32\n" + "%1 = OpSpecConstant %float -0x1.fffffep+128\n" + "%2 = OpSpecConstant %float 2.5\n", + }, + // 6. 64-bit float type. + { + // code + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 3.14159265358979\n" + "%2 = OpSpecConstant %double 0.14285\n", + // default values + SpecIdToValueBitPatternMap{{201, {0xffffffff, 0x7fffffff}}, + {202, {0x00000000, 0xc0404000}}}, + // expected + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 0x1.fffffffffffffp+1024\n" + "%2 = OpSpecConstant %double -32.5\n", + }, + // 7. SpecId not found, expect no modification. + { + // code + "OpDecorate %1 SpecId 201\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 3.14159265358979\n", + // default values + SpecIdToValueBitPatternMap{{8888, {0x0}}}, + // expected + "OpDecorate %1 SpecId 201\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 3.14159265358979\n", + }, + // 8. Multiple types of spec constants. + { + // code + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "OpDecorate %3 SpecId 203\n" + "%bool = OpTypeBool\n" + "%int = OpTypeInt 32 1\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 3.14159265358979\n" + "%2 = OpSpecConstant %int 1024\n" + "%3 = OpSpecConstantTrue %bool\n", + // default values + SpecIdToValueBitPatternMap{ + {201, {0xffffffff, 0x7fffffff}}, + {202, {0x00000800}}, + {203, {0x0}}, + }, + // expected + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "OpDecorate %3 SpecId 203\n" + "%bool = OpTypeBool\n" + "%int = OpTypeInt 32 1\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %double 0x1.fffffffffffffp+1024\n" + "%2 = OpSpecConstant %int 2048\n" + "%3 = OpSpecConstantFalse %bool\n", + }, + // 9. Ignore other decorations. + { + // code + "OpDecorate %1 ArrayStride 4\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueBitPatternMap{{4, {0x7fffffff}}}, + // expected + "OpDecorate %1 ArrayStride 4\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 100\n", + }, + // 10. Distinguish from other decorations. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %1 ArrayStride 4\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueBitPatternMap{{4, {0x7fffffff}}, {100, {0xffffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %1 ArrayStride 4\n" + "%int = OpTypeInt 32 1\n" + "%1 = OpSpecConstant %int -1\n", + }, + // 11. Decorate through decoration group. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x7fffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 2147483647\n", + }, + // 12. Ignore other decorations in decoration group. + { + // code + "OpDecorate %1 ArrayStride 4\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueBitPatternMap{{4, {0x7fffffff}}}, + // expected + "OpDecorate %1 ArrayStride 4\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + }, + // 13. Distinguish from other decorations in decoration group. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %1 ArrayStride 4\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x7fffffff}}, {4, {0x00000001}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %1 ArrayStride 4\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 2147483647\n", + }, + // 14. Unchanged bool default value + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x1}}, {101, {0x0}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n", + }, + // 15. Unchanged int default values + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%int = OpTypeInt 32 1\n" + "%ulong = OpTypeInt 64 0\n" + "%1 = OpSpecConstant %int 10\n" + "%2 = OpSpecConstant %ulong 11\n", + // default values + SpecIdToValueBitPatternMap{{100, {10}}, {101, {11, 0}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "%int = OpTypeInt 32 1\n" + "%ulong = OpTypeInt 64 0\n" + "%1 = OpSpecConstant %int 10\n" + "%2 = OpSpecConstant %ulong 11\n", + }, + // 16. Unchanged float default values + { + // code + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "%float = OpTypeFloat 32\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %float 3.25\n" + "%2 = OpSpecConstant %double 1.25\n", + // default values + SpecIdToValueBitPatternMap{{201, {0x40500000}}, + {202, {0x00000000, 0x3ff40000}}}, + // expected + "OpDecorate %1 SpecId 201\n" + "OpDecorate %2 SpecId 202\n" + "%float = OpTypeFloat 32\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %float 3.25\n" + "%2 = OpSpecConstant %double 1.25\n", + }, + // 17. OpGroupDecorate may have multiple target ids defined by the same + // eligible spec constant + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2 %2 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueBitPatternMap{{100, {0xffffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2 %2 %2\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int -1\n", + }, + // 18. For Boolean type spec constants,if any word in the bit pattern + // is not zero, it can be considered as a 'true', otherwise, it can be + // considered as a 'false'. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantTrue %bool\n" + "%2 = OpSpecConstantFalse %bool\n" + "%3 = OpSpecConstantFalse %bool\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {0x0, 0x0, 0x0, 0x0}}, + {101, {0x10101010}}, + {102, {0x0, 0x0, 0x0, 0x2}}, + }, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%bool = OpTypeBool\n" + "%1 = OpSpecConstantFalse %bool\n" + "%2 = OpSpecConstantTrue %bool\n" + "%3 = OpSpecConstantTrue %bool\n", + }, + })); + +INSTANTIATE_TEST_SUITE_P( + InvalidCases, SetSpecConstantDefaultValueInBitPatternFormParamTest, + ::testing::ValuesIn(std::vector< + SetSpecConstantDefaultValueInBitPatternFormTestCase>{ + // 0. Do not crash when decoration group is not used. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n" + "%3 = OpSpecConstant %int 100\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x7fffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n" + "%3 = OpSpecConstant %int 100\n", + }, + // 1. Do not crash when target does not exist. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x7fffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n", + }, + // 2. Do nothing when SpecId decoration is not attached to a + // non-spec-constant instruction. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n" + "%int_101 = OpConstant %int 101\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x7fffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%int = OpTypeInt 32 1\n" + "%int_101 = OpConstant %int 101\n", + }, + // 3. Do nothing when SpecId decoration is not attached to a + // OpSpecConstant{|True|False} instruction. + { + // code + "OpDecorate %1 SpecId 100\n" + "%int = OpTypeInt 32 1\n" + "%3 = OpSpecConstant %int 101\n" + "%1 = OpSpecConstantOp %int IAdd %3 %3\n", + // default values + SpecIdToValueBitPatternMap{{100, {0x7fffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%int = OpTypeInt 32 1\n" + "%3 = OpSpecConstant %int 101\n" + "%1 = OpSpecConstantOp %int IAdd %3 %3\n", + }, + // 4. Do not crash and do nothing when SpecId decoration is applied to + // multiple spec constants. + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2 %3 %4\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n" + "%3 = OpSpecConstant %int 200\n" + "%4 = OpSpecConstant %int 300\n", + // default values + SpecIdToValueBitPatternMap{{100, {0xffffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2 %3 %4\n" + "%int = OpTypeInt 32 1\n" + "%2 = OpSpecConstant %int 100\n" + "%3 = OpSpecConstant %int 200\n" + "%4 = OpSpecConstant %int 300\n", + }, + // 5. Do not crash and do nothing when SpecId decoration is attached to + // non-spec-constants (invalid case). + { + // code + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%2 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%int_100 = OpConstant %int 100\n", + // default values + SpecIdToValueBitPatternMap{{100, {0xffffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "%1 = OpDecorationGroup\n" + "%2 = OpDecorationGroup\n" + "OpGroupDecorate %1 %2\n" + "%int = OpTypeInt 32 1\n" + "%int_100 = OpConstant %int 100\n", + }, + // 6. Incompatible input bit pattern with the type. Nothing should be + // done in such a case. + { + // code + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%int = OpTypeInt 32 1\n" + "%ulong = OpTypeInt 64 0\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %int 100\n" + "%2 = OpSpecConstant %ulong 200\n" + "%3 = OpSpecConstant %double 3.141592653\n", + // default values + SpecIdToValueBitPatternMap{ + {100, {10, 0}}, {101, {11}}, {102, {0xffffffff}}}, + // expected + "OpDecorate %1 SpecId 100\n" + "OpDecorate %2 SpecId 101\n" + "OpDecorate %3 SpecId 102\n" + "%int = OpTypeInt 32 1\n" + "%ulong = OpTypeInt 64 0\n" + "%double = OpTypeFloat 64\n" + "%1 = OpSpecConstant %int 100\n" + "%2 = OpSpecConstant %ulong 200\n" + "%3 = OpSpecConstant %double 3.141592653\n", + }, + })); + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/simplification_test.cpp b/third_party/spirv-tools/test/opt/simplification_test.cpp new file mode 100644 index 0000000..7a9696e --- /dev/null +++ b/third_party/spirv-tools/test/opt/simplification_test.cpp @@ -0,0 +1,365 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/simplification_pass.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using SimplificationTest = PassTest<::testing::Test>; + +TEST_F(SimplificationTest, StraightLineTest) { + // Testing that folding rules are combined in simple straight line code. + const std::string text = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %i %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 430 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %i "i" + OpName %o "o" + OpDecorate %i Flat + OpDecorate %i Location 0 + OpDecorate %o Location 0 + %void = OpTypeVoid + %8 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %int_0 = OpConstant %int 0 + %13 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 + %int_1 = OpConstant %int 1 +%_ptr_Input_v4int = OpTypePointer Input %v4int + %i = OpVariable %_ptr_Input_v4int Input +%_ptr_Output_int = OpTypePointer Output %int + %o = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %8 + %21 = OpLabel + %31 = OpCompositeInsert %v4int %int_1 %13 0 +; CHECK: [[load:%[a-zA-Z_\d]+]] = OpLoad + %23 = OpLoad %v4int %i + %33 = OpCompositeInsert %v4int %int_0 %23 0 + %35 = OpCompositeExtract %int %31 0 +; CHECK: [[extract:%[a-zA-Z_\d]+]] = OpCompositeExtract %int [[load]] 1 + %37 = OpCompositeExtract %int %33 1 +; CHECK: [[add:%[a-zA-Z_\d]+]] = OpIAdd %int %int_1 [[extract]] + %29 = OpIAdd %int %35 %37 + OpStore %o %29 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(SimplificationTest, NewInstructionTest) { + // Testing that new instructions are simplified. Specifically, + // that the new add instruction generated by FactorAddMul is + // further simplified by MergeGenericAddSub. + const std::string text = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 430 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %_ptr_int = OpTypePointer Function %int +; CHECK: [[mul:%[a-zA-Z_\d]+]] = OpIMul %int %13 %11 + %main = OpFunction %void None %4 + %7 = OpLabel + %8 = OpVariable %_ptr_int Function + %9 = OpVariable %_ptr_int Function + %10 = OpVariable %_ptr_int Function + %11 = OpLoad %int %8 + %12 = OpLoad %int %9 + %13 = OpLoad %int %10 + %14 = OpISub %int %11 %12 + %15 = OpIMul %int %13 %11 + %16 = OpIMul %int %13 %12 + %17 = OpIAdd %int %14 %15 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(SimplificationTest, AcrossBasicBlocks) { + // Testing that folding rules are combined across basic blocks. + const std::string text = R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %i %o + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 430 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %i "i" + OpName %o "o" + OpDecorate %i Flat + OpDecorate %i Location 0 + OpDecorate %o Location 0 + %void = OpTypeVoid + %8 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %int_0 = OpConstant %int 0 +%_ptr_Input_v4int = OpTypePointer Input %v4int + %i = OpVariable %_ptr_Input_v4int Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_int = OpTypePointer Input %int + %int_10 = OpConstant %int 10 + %bool = OpTypeBool + %int_1 = OpConstant %int 1 +%_ptr_Output_int = OpTypePointer Output %int + %o = OpVariable %_ptr_Output_int Output + %main = OpFunction %void None %8 + %24 = OpLabel +; CHECK: [[load:%[a-zA-Z_\d]+]] = OpLoad %v4int %i + %25 = OpLoad %v4int %i + %41 = OpCompositeInsert %v4int %int_0 %25 0 + %27 = OpAccessChain %_ptr_Input_int %i %uint_0 + %28 = OpLoad %int %27 + %29 = OpSGreaterThan %bool %28 %int_10 + OpSelectionMerge %30 None + OpBranchConditional %29 %31 %32 + %31 = OpLabel + %43 = OpCopyObject %v4int %25 + OpBranch %30 + %32 = OpLabel + %45 = OpCopyObject %v4int %25 + OpBranch %30 + %30 = OpLabel + %50 = OpPhi %v4int %43 %31 %45 %32 +; CHECK: [[extract1:%[a-zA-Z_\d]+]] = OpCompositeExtract %int [[load]] 0 + %47 = OpCompositeExtract %int %50 0 +; CHECK: [[extract2:%[a-zA-Z_\d]+]] = OpCompositeExtract %int [[load]] 1 + %49 = OpCompositeExtract %int %41 1 +; CHECK: [[add:%[a-zA-Z_\d]+]] = OpIAdd %int [[extract1]] [[extract2]] + %39 = OpIAdd %int %47 %49 + OpStore %o %39 + OpReturn + OpFunctionEnd + +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(SimplificationTest, ThroughLoops) { + // Testing that folding rules are applied multiple times to instructions + // to be able to propagate across loop iterations. + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %o %i + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 430 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %o "o" + OpName %i "i" + OpDecorate %o Location 0 + OpDecorate %i Flat + OpDecorate %i Location 0 + %void = OpTypeVoid + %8 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %v4int = OpTypeVector %int 4 + %int_0 = OpConstant %int 0 +; CHECK: [[constant:%[a-zA-Z_\d]+]] = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 + %13 = OpConstantComposite %v4int %int_0 %int_0 %int_0 %int_0 + %bool = OpTypeBool +%_ptr_Output_int = OpTypePointer Output %int + %o = OpVariable %_ptr_Output_int Output +%_ptr_Input_v4int = OpTypePointer Input %v4int + %i = OpVariable %_ptr_Input_v4int Input + %68 = OpUndef %v4int + %main = OpFunction %void None %8 + %23 = OpLabel +; CHECK: [[load:%[a-zA-Z_\d]+]] = OpLoad %v4int %i + %load = OpLoad %v4int %i + OpBranch %24 + %24 = OpLabel + %67 = OpPhi %v4int %load %23 %64 %26 +; CHECK: OpLoopMerge [[merge_lab:%[a-zA-Z_\d]+]] + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %48 = OpCompositeExtract %int %67 0 + %30 = OpIEqual %bool %48 %int_0 + OpBranchConditional %30 %31 %25 + %31 = OpLabel + %50 = OpCompositeExtract %int %67 0 + %54 = OpCompositeExtract %int %67 1 + %58 = OpCompositeExtract %int %67 2 + %62 = OpCompositeExtract %int %67 3 + %64 = OpCompositeConstruct %v4int %50 %54 %58 %62 + OpBranch %26 + %26 = OpLabel + OpBranch %24 + %25 = OpLabel +; CHECK: [[merge_lab]] = OpLabel +; CHECK: [[extract:%[a-zA-Z_\d]+]] = OpCompositeExtract %int [[load]] 0 + %66 = OpCompositeExtract %int %67 0 +; CHECK-NEXT: OpStore %o [[extract]] + OpStore %o %66 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(SimplificationTest, CopyObjectWithDecorations1) { + // Don't simplify OpCopyObject if the result id has a decoration that the + // operand does not. + const std::string text = R"(OpCapability Shader +OpCapability ShaderNonUniform +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpDecorate %3 NonUniform +%void = OpTypeVoid +%5 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%2 = OpFunction %void None %5 +%7 = OpLabel +%8 = OpUndef %int +%3 = OpCopyObject %int %8 +%9 = OpIAdd %int %3 %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, false); +} + +TEST_F(SimplificationTest, CopyObjectWithDecorations2) { + // Simplify OpCopyObject if the result id is a subset of the decorations of + // the operand. + const std::string before = R"(OpCapability Shader +OpCapability ShaderNonUniform +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpDecorate %3 NonUniform +%void = OpTypeVoid +%5 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%2 = OpFunction %void None %5 +%7 = OpLabel +%3 = OpUndef %int +%8 = OpCopyObject %int %3 +%9 = OpIAdd %int %8 %8 +OpReturn +OpFunctionEnd +)"; + + const std::string after = R"(OpCapability Shader +OpCapability ShaderNonUniform +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +OpSource GLSL 430 +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpDecorate %3 NonUniform +%void = OpTypeVoid +%5 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%2 = OpFunction %void None %5 +%7 = OpLabel +%3 = OpUndef %int +%9 = OpIAdd %int %3 %3 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(SimplificationTest, DontMoveDecorations) { + const std::string spirv = R"( +; CHECK-NOT: RelaxedPrecision +; CHECK: [[sub:%\w+]] = OpFSub +; CHECK: OpStore {{.*}} [[sub]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %add RelaxedPrecision +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 1 Offset 4 +OpDecorate %in DescriptorSet 0 +OpDecorate %in Binding 0 +OpDecorate %out DescriptorSet 0 +OpDecorate %out Binding 1 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%void_fn = OpTypeFunction %void +%block = OpTypeStruct %float %float +%ptr_ssbo_block = OpTypePointer StorageBuffer %block +%in = OpVariable %ptr_ssbo_block StorageBuffer +%out = OpVariable %ptr_ssbo_block StorageBuffer +%ptr_ssbo_float = OpTypePointer StorageBuffer %float +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%float_0 = OpConstant %float 0 +%main = OpFunction %void None %void_fn +%entry = OpLabel +%in_gep_0 = OpAccessChain %ptr_ssbo_float %in %int_0 +%in_gep_1 = OpAccessChain %ptr_ssbo_float %in %int_1 +%load_0 = OpLoad %float %in_gep_0 +%load_1 = OpLoad %float %in_gep_1 +%sub = OpFSub %float %load_0 %load_1 +%add = OpFAdd %float %float_0 %sub +%out_gep_0 = OpAccessChain %ptr_ssbo_float %out %int_0 +OpStore %out_gep_0 %add +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(spirv, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/split_invalid_unreachable_test.cpp b/third_party/spirv-tools/test/opt/split_invalid_unreachable_test.cpp new file mode 100644 index 0000000..520af01 --- /dev/null +++ b/third_party/spirv-tools/test/opt/split_invalid_unreachable_test.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using SplitInvalidUnreachableTest = PassTest<::testing::Test>; + +std::string spirv_header = R"(OpCapability Shader +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical Vulkan +OpEntryPoint Vertex %1 "shader" +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%void = OpTypeVoid +%bool = OpTypeBool +%7 = OpTypeFunction %void +)"; + +std::string function_head = R"(%1 = OpFunction %void None %7 +%8 = OpLabel +OpBranch %9 +)"; + +std::string function_tail = "OpFunctionEnd\n"; + +std::string GetLoopMergeBlock(std::string block_id, std::string merge_id, + std::string continue_id, std::string body_id) { + std::string result; + result += block_id + " = OpLabel\n"; + result += "OpLoopMerge " + merge_id + " " + continue_id + " None\n"; + result += "OpBranch " + body_id + "\n"; + return result; +} + +std::string GetSelectionMergeBlock(std::string block_id, + std::string condition_id, + std::string merge_id, std::string true_id, + std::string false_id) { + std::string result; + result += block_id + " = OpLabel\n"; + result += condition_id + " = OpSLessThan %bool %uint_1 %uint_2\n"; + result += "OpSelectionMerge " + merge_id + " None\n"; + result += "OpBranchConditional " + condition_id + " " + true_id + " " + + false_id + "\n"; + + return result; +} + +std::string GetReturnBlock(std::string block_id) { + std::string result; + result += block_id + " = OpLabel\n"; + result += "OpReturn\n"; + return result; +} + +std::string GetUnreachableBlock(std::string block_id) { + std::string result; + result += block_id + " = OpLabel\n"; + result += "OpUnreachable\n"; + return result; +} + +std::string GetBranchBlock(std::string block_id, std::string target_id) { + std::string result; + result += block_id + " = OpLabel\n"; + result += "OpBranch " + target_id + "\n"; + return result; +} + +TEST_F(SplitInvalidUnreachableTest, NoInvalidBlocks) { + std::string input = spirv_header + function_head; + input += GetLoopMergeBlock("%9", "%10", "%11", "%12"); + input += GetSelectionMergeBlock("%12", "%13", "%14", "%15", "%16"); + input += GetReturnBlock("%15"); + input += GetReturnBlock("%16"); + input += GetUnreachableBlock("%10"); + input += GetBranchBlock("%11", "%9"); + input += GetUnreachableBlock("%14"); + input += function_tail; + + SinglePassRunAndCheck(input, input, + /* skip_nop = */ false); +} + +TEST_F(SplitInvalidUnreachableTest, SelectionInLoop) { + std::string input = spirv_header + function_head; + input += GetLoopMergeBlock("%9", "%10", "%11", "%12"); + input += GetSelectionMergeBlock("%12", "%13", "%11", "%15", "%16"); + input += GetReturnBlock("%15"); + input += GetReturnBlock("%16"); + input += GetUnreachableBlock("%10"); + input += GetBranchBlock("%11", "%9"); + input += function_tail; + + std::string expected = spirv_header + function_head; + expected += GetLoopMergeBlock("%9", "%10", "%11", "%12"); + expected += GetSelectionMergeBlock("%12", "%13", "%16", "%14", "%15"); + expected += GetReturnBlock("%14"); + expected += GetReturnBlock("%15"); + expected += GetUnreachableBlock("%10"); + expected += GetUnreachableBlock("%16"); + expected += GetBranchBlock("%11", "%9"); + expected += function_tail; + + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +TEST_F(SplitInvalidUnreachableTest, LoopInSelection) { + std::string input = spirv_header + function_head; + input += GetSelectionMergeBlock("%9", "%10", "%11", "%12", "%13"); + input += GetLoopMergeBlock("%12", "%14", "%11", "%15"); + input += GetReturnBlock("%13"); + input += GetUnreachableBlock("%14"); + input += GetBranchBlock("%11", "%12"); + input += GetReturnBlock("%15"); + input += function_tail; + + std::string expected = spirv_header + function_head; + expected += GetSelectionMergeBlock("%9", "%10", "%16", "%12", "%13"); + expected += GetLoopMergeBlock("%12", "%14", "%11", "%15"); + expected += GetReturnBlock("%13"); + expected += GetUnreachableBlock("%14"); + expected += GetUnreachableBlock("%16"); + expected += GetBranchBlock("%11", "%12"); + expected += GetReturnBlock("%15"); + expected += function_tail; + + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/strength_reduction_test.cpp b/third_party/spirv-tools/test/opt/strength_reduction_test.cpp new file mode 100644 index 0000000..31d0503 --- /dev/null +++ b/third_party/spirv-tools/test/opt/strength_reduction_test.cpp @@ -0,0 +1,438 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; +using StrengthReductionBasicTest = PassTest<::testing::Test>; + +// Test to make sure we replace 5*8. +TEST_F(StrengthReductionBasicTest, BasicReplaceMulBy8) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%uint_5 = OpConstant %uint 5", + "%uint_8 = OpConstant %uint 8", + "%main = OpFunction %void None %4", + "%8 = OpLabel", + "%9 = OpIMul %uint %uint_5 %uint_8", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + auto result = SinglePassRunAndDisassemble( + JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); + const std::string& output = std::get<0>(result); + EXPECT_THAT(output, Not(HasSubstr("OpIMul"))); + EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_5 %uint_3")); +} + +// TODO(dneto): Add Effcee as required dependency, and make this unconditional. +// Test to make sure we replace 16*5 +// Also demonstrate use of Effcee matching. +TEST_F(StrengthReductionBasicTest, BasicReplaceMulBy16) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void +; We know disassembly will produce %uint here, but +; CHECK: %uint = OpTypeInt 32 0 +; CHECK-DAG: [[five:%[a-zA-Z_\d]+]] = OpConstant %uint 5 + +; We have RE2 regular expressions, so \w matches [_a-zA-Z0-9]. +; This shows the preferred pattern for matching SPIR-V identifiers. +; (We could have cheated in this case since we know the disassembler will +; generate the 'nice' name of "%uint_4". +; CHECK-DAG: [[four:%\w+]] = OpConstant %uint 4 + %uint = OpTypeInt 32 0 + %uint_5 = OpConstant %uint 5 + %uint_16 = OpConstant %uint 16 + %main = OpFunction %void None %4 +; CHECK: OpLabel + %8 = OpLabel +; CHECK-NEXT: OpShiftLeftLogical %uint [[five]] [[four]] +; The multiplication disappears. +; CHECK-NOT: OpIMul + %9 = OpIMul %uint %uint_16 %uint_5 + OpReturn +; CHECK: OpFunctionEnd + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} + +// Test to make sure we replace a multiple of 32 and 4. +TEST_F(StrengthReductionBasicTest, BasicTwoPowersOf2) { + // In this case, we have two powers of 2. Need to make sure we replace only + // one of them for the bit shift. + // clang-format off + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%int_32 = OpConstant %int 32 + %int_4 = OpConstant %int 4 + %main = OpFunction %void None %4 + %8 = OpLabel + %9 = OpIMul %int %int_32 %int_4 + OpReturn + OpFunctionEnd +)"; + // clang-format on + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); + const std::string& output = std::get<0>(result); + EXPECT_THAT(output, Not(HasSubstr("OpIMul"))); + EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %int %int_4 %uint_5")); +} + +// Test to make sure we don't replace 0*5. +TEST_F(StrengthReductionBasicTest, BasicDontReplace0) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%int = OpTypeInt 32 1", + "%int_0 = OpConstant %int 0", + "%int_5 = OpConstant %int 5", + "%main = OpFunction %void None %4", + "%8 = OpLabel", + "%9 = OpIMul %int %int_0 %int_5", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + auto result = SinglePassRunAndDisassemble( + JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// Test to make sure we do not replace a multiple of 5 and 7. +TEST_F(StrengthReductionBasicTest, BasicNoChange) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + "OpName %2 \"main\"", + "%3 = OpTypeVoid", + "%4 = OpTypeFunction %3", + "%5 = OpTypeInt 32 1", + "%6 = OpTypeInt 32 0", + "%7 = OpConstant %5 5", + "%8 = OpConstant %5 7", + "%2 = OpFunction %3 None %4", + "%9 = OpLabel", + "%10 = OpIMul %5 %7 %8", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + auto result = SinglePassRunAndDisassemble( + JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +// Test to make sure constants and types are reused and not duplicated. +TEST_F(StrengthReductionBasicTest, NoDuplicateConstantsAndTypes) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%uint_8 = OpConstant %uint 8", + "%uint_3 = OpConstant %uint 3", + "%main = OpFunction %void None %4", + "%8 = OpLabel", + "%9 = OpIMul %uint %uint_8 %uint_3", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + auto result = SinglePassRunAndDisassemble( + JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); + const std::string& output = std::get<0>(result); + EXPECT_THAT(output, + Not(MatchesRegex(".*OpConstant %uint 3.*OpConstant %uint 3.*"))); + EXPECT_THAT(output, Not(MatchesRegex(".*OpTypeInt 32 0.*OpTypeInt 32 0.*"))); +} + +// Test to make sure we generate the constants only once +TEST_F(StrengthReductionBasicTest, BasicCreateOneConst) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\"", + "OpName %main \"main\"", + "%void = OpTypeVoid", + "%4 = OpTypeFunction %void", + "%uint = OpTypeInt 32 0", + "%uint_5 = OpConstant %uint 5", + "%uint_9 = OpConstant %uint 9", + "%uint_128 = OpConstant %uint 128", + "%main = OpFunction %void None %4", + "%8 = OpLabel", + "%9 = OpIMul %uint %uint_5 %uint_128", + "%10 = OpIMul %uint %uint_9 %uint_128", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + auto result = SinglePassRunAndDisassemble( + JoinAllInsts(text), /* skip_nop = */ true, /* do_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); + const std::string& output = std::get<0>(result); + EXPECT_THAT(output, Not(HasSubstr("OpIMul"))); + EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_5 %uint_7")); + EXPECT_THAT(output, HasSubstr("OpShiftLeftLogical %uint %uint_9 %uint_7")); +} + +// Test to make sure we generate the instructions in the correct position and +// that the uses get replaced as well. Here we check that the use in the return +// is replaced, we also check that we can replace two OpIMuls when one feeds the +// other. +TEST_F(StrengthReductionBasicTest, BasicCheckPositionAndReplacement) { + // This is just the preamble to set up the test. + const std::vector common_text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %gl_FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpName %main \"main\"", + "OpName %foo_i1_ \"foo(i1;\"", + "OpName %n \"n\"", + "OpName %gl_FragColor \"gl_FragColor\"", + "OpName %param \"param\"", + "OpDecorate %gl_FragColor Location 0", + "%void = OpTypeVoid", + "%3 = OpTypeFunction %void", + "%int = OpTypeInt 32 1", +"%_ptr_Function_int = OpTypePointer Function %int", + "%8 = OpTypeFunction %int %_ptr_Function_int", + "%int_256 = OpConstant %int 256", + "%int_2 = OpConstant %int 2", + "%float = OpTypeFloat 32", + "%v4float = OpTypeVector %float 4", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", +"%gl_FragColor = OpVariable %_ptr_Output_v4float Output", + "%float_1 = OpConstant %float 1", + "%int_10 = OpConstant %int 10", + "%float_0_375 = OpConstant %float 0.375", + "%float_0_75 = OpConstant %float 0.75", + "%uint = OpTypeInt 32 0", + "%uint_8 = OpConstant %uint 8", + "%uint_1 = OpConstant %uint 1", + "%main = OpFunction %void None %3", + "%5 = OpLabel", + "%param = OpVariable %_ptr_Function_int Function", + "OpStore %param %int_10", + "%26 = OpFunctionCall %int %foo_i1_ %param", + "%27 = OpConvertSToF %float %26", + "%28 = OpFDiv %float %float_1 %27", + "%31 = OpCompositeConstruct %v4float %28 %float_0_375 %float_0_75 %float_1", + "OpStore %gl_FragColor %31", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + // This is the real test. The two OpIMul should be replaced. The expected + // output is in |foo_after|. + const std::vector foo_before = { + // clang-format off + "%foo_i1_ = OpFunction %int None %8", + "%n = OpFunctionParameter %_ptr_Function_int", + "%11 = OpLabel", + "%12 = OpLoad %int %n", + "%14 = OpIMul %int %12 %int_256", + "%16 = OpIMul %int %14 %int_2", + "OpReturnValue %16", + "OpFunctionEnd", + + // clang-format on + }; + + const std::vector foo_after = { + // clang-format off + "%foo_i1_ = OpFunction %int None %8", + "%n = OpFunctionParameter %_ptr_Function_int", + "%11 = OpLabel", + "%12 = OpLoad %int %n", + "%33 = OpShiftLeftLogical %int %12 %uint_8", + "%34 = OpShiftLeftLogical %int %33 %uint_1", + "OpReturnValue %34", + "OpFunctionEnd", + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + JoinAllInsts(Concat(common_text, foo_before)), + JoinAllInsts(Concat(common_text, foo_after)), + /* skip_nop = */ true, /* do_validate = */ true); +} + +// Test that, when the result of an OpIMul instruction has more than 1 use, and +// the instruction is replaced, all of the uses of the results are replace with +// the new result. +TEST_F(StrengthReductionBasicTest, BasicTestMultipleReplacements) { + // This is just the preamble to set up the test. + const std::vector common_text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\" %gl_FragColor", + "OpExecutionMode %main OriginUpperLeft", + "OpName %main \"main\"", + "OpName %foo_i1_ \"foo(i1;\"", + "OpName %n \"n\"", + "OpName %gl_FragColor \"gl_FragColor\"", + "OpName %param \"param\"", + "OpDecorate %gl_FragColor Location 0", + "%void = OpTypeVoid", + "%3 = OpTypeFunction %void", + "%int = OpTypeInt 32 1", +"%_ptr_Function_int = OpTypePointer Function %int", + "%8 = OpTypeFunction %int %_ptr_Function_int", + "%int_256 = OpConstant %int 256", + "%int_2 = OpConstant %int 2", + "%float = OpTypeFloat 32", + "%v4float = OpTypeVector %float 4", +"%_ptr_Output_v4float = OpTypePointer Output %v4float", +"%gl_FragColor = OpVariable %_ptr_Output_v4float Output", + "%float_1 = OpConstant %float 1", + "%int_10 = OpConstant %int 10", + "%float_0_375 = OpConstant %float 0.375", + "%float_0_75 = OpConstant %float 0.75", + "%uint = OpTypeInt 32 0", + "%uint_8 = OpConstant %uint 8", + "%uint_1 = OpConstant %uint 1", + "%main = OpFunction %void None %3", + "%5 = OpLabel", + "%param = OpVariable %_ptr_Function_int Function", + "OpStore %param %int_10", + "%26 = OpFunctionCall %int %foo_i1_ %param", + "%27 = OpConvertSToF %float %26", + "%28 = OpFDiv %float %float_1 %27", + "%31 = OpCompositeConstruct %v4float %28 %float_0_375 %float_0_75 %float_1", + "OpStore %gl_FragColor %31", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + // This is the real test. The two OpIMul instructions should be replaced. In + // particular, we want to be sure that both uses of %16 are changed to use the + // new result. + const std::vector foo_before = { + // clang-format off + "%foo_i1_ = OpFunction %int None %8", + "%n = OpFunctionParameter %_ptr_Function_int", + "%11 = OpLabel", + "%12 = OpLoad %int %n", + "%14 = OpIMul %int %12 %int_256", + "%16 = OpIMul %int %14 %int_2", + "%17 = OpIAdd %int %14 %16", + "OpReturnValue %17", + "OpFunctionEnd", + + // clang-format on + }; + + const std::vector foo_after = { + // clang-format off + "%foo_i1_ = OpFunction %int None %8", + "%n = OpFunctionParameter %_ptr_Function_int", + "%11 = OpLabel", + "%12 = OpLoad %int %n", + "%34 = OpShiftLeftLogical %int %12 %uint_8", + "%35 = OpShiftLeftLogical %int %34 %uint_1", + "%17 = OpIAdd %int %34 %35", + "OpReturnValue %17", + "OpFunctionEnd", + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + JoinAllInsts(Concat(common_text, foo_before)), + JoinAllInsts(Concat(common_text, foo_after)), + /* skip_nop = */ true, /* do_validate = */ true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/strip_atomic_counter_memory_test.cpp b/third_party/spirv-tools/test/opt/strip_atomic_counter_memory_test.cpp new file mode 100644 index 0000000..90daa59 --- /dev/null +++ b/third_party/spirv-tools/test/opt/strip_atomic_counter_memory_test.cpp @@ -0,0 +1,406 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +typedef std::tuple StripAtomicCounterMemoryParam; + +using MemorySemanticsModified = + PassTest<::testing::TestWithParam>; +using NonMemorySemanticsUnmodifiedTest = PassTest<::testing::Test>; + +void operator+=(std::vector& lhs, const char* rhs) { + lhs.push_back(rhs); +} + +std::string GetConstDecl(std::string val) { + std::string decl; + decl += "%uint_" + val + " = OpConstant %uint " + val; + return decl; +} + +std::string GetUnchangedString(std::string(generate_inst)(std::string), + std::string val) { + std::string decl = GetConstDecl(val); + std::string inst = generate_inst(val); + + std::vector result = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", +"%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint", + "%4 = OpVariable %_ptr_Workgroup_uint Workgroup", + "%uint_0 = OpConstant %uint 0", + "%uint_1 = OpConstant %uint 1", + "%void = OpTypeVoid", + "%8 = OpTypeFunction %void", + decl.c_str(), + "%1 = OpFunction %void None %8", + "%10 = OpLabel", + inst.c_str(), + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + return JoinAllInsts(result); +} + +std::string GetChangedString(std::string(generate_inst)(std::string), + std::string orig, std::string changed) { + std::string orig_decl = GetConstDecl(orig); + std::string changed_decl = GetConstDecl(changed); + std::string inst = generate_inst(changed); + + std::vector result = { + // clang-format off + "OpCapability Shader", + "OpCapability VulkanMemoryModel", + "OpExtension \"SPV_KHR_vulkan_memory_model\"", + "OpMemoryModel Logical Vulkan", + "OpEntryPoint Vertex %1 \"shader\"", + "%uint = OpTypeInt 32 0", +"%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint", + "%4 = OpVariable %_ptr_Workgroup_uint Workgroup", + "%uint_0 = OpConstant %uint 0", + "%uint_1 = OpConstant %uint 1", + "%void = OpTypeVoid", + "%8 = OpTypeFunction %void", + orig_decl.c_str() }; + // clang-format on + if (changed != "0") result += changed_decl.c_str(); + result += "%1 = OpFunction %void None %8"; + result += "%10 = OpLabel"; + result += inst.c_str(); + result += "OpReturn"; + result += "OpFunctionEnd"; + return JoinAllInsts(result); +} + +std::tuple GetInputAndExpected( + std::string(generate_inst)(std::string), + StripAtomicCounterMemoryParam param) { + std::string orig = std::get<0>(param); + std::string changed = std::get<1>(param); + std::string input = GetUnchangedString(generate_inst, orig); + std::string expected = orig == changed + ? GetUnchangedString(generate_inst, changed) + : GetChangedString(generate_inst, orig, changed); + return std::make_tuple(input, expected); +} + +std::string GetOpControlBarrierInst(std::string val) { + return "OpControlBarrier %uint_1 %uint_1 %uint_" + val; +} + +TEST_P(MemorySemanticsModified, OpControlBarrier) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpControlBarrierInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpMemoryBarrierInst(std::string val) { + return "OpMemoryBarrier %uint_1 %uint_" + val; +} + +TEST_P(MemorySemanticsModified, OpMemoryBarrier) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpMemoryBarrierInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicLoadInst(std::string val) { + return "%11 = OpAtomicLoad %uint %4 %uint_1 %uint_" + val; +} + +TEST_P(MemorySemanticsModified, OpAtomicLoad) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicLoadInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicStoreInst(std::string val) { + return "OpAtomicStore %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicStore) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicStoreInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicExchangeInst(std::string val) { + return "%11 = OpAtomicExchange %uint %4 %uint_1 %uint_" + val + " %uint_0"; +} + +TEST_P(MemorySemanticsModified, OpAtomicExchange) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicExchangeInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicCompareExchangeInst(std::string val) { + return "%11 = OpAtomicCompareExchange %uint %4 %uint_1 %uint_" + val + + " %uint_" + val + " %uint_0 %uint_0"; +} + +TEST_P(MemorySemanticsModified, OpAtomicCompareExchange) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicCompareExchangeInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicCompareExchangeWeakInst(std::string val) { + return "%11 = OpAtomicCompareExchangeWeak %uint %4 %uint_1 %uint_" + val + + " %uint_" + val + " %uint_0 %uint_0"; +} + +TEST_P(MemorySemanticsModified, OpAtomicCompareExchangeWeak) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicCompareExchangeWeakInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicIIncrementInst(std::string val) { + return "%11 = OpAtomicIIncrement %uint %4 %uint_1 %uint_" + val; +} + +TEST_P(MemorySemanticsModified, OpAtomicIIncrement) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicIIncrementInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicIDecrementInst(std::string val) { + return "%11 = OpAtomicIDecrement %uint %4 %uint_1 %uint_" + val; +} + +TEST_P(MemorySemanticsModified, OpAtomicIDecrement) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicIDecrementInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicIAddInst(std::string val) { + return "%11 = OpAtomicIAdd %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicIAdd) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicIAddInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicISubInst(std::string val) { + return "%11 = OpAtomicISub %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicISub) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicISubInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicSMinInst(std::string val) { + return "%11 = OpAtomicSMin %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicSMin) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicSMinInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicUMinInst(std::string val) { + return "%11 = OpAtomicUMin %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicUMin) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicUMinInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicSMaxInst(std::string val) { + return "%11 = OpAtomicSMax %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicSMax) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicSMaxInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicUMaxInst(std::string val) { + return "%11 = OpAtomicUMax %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicUMax) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicUMaxInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicAndInst(std::string val) { + return "%11 = OpAtomicAnd %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicAnd) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicAndInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicOrInst(std::string val) { + return "%11 = OpAtomicOr %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicOr) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicOrInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicXorInst(std::string val) { + return "%11 = OpAtomicXor %uint %4 %uint_1 %uint_" + val + " %uint_1"; +} + +TEST_P(MemorySemanticsModified, OpAtomicXor) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicXorInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicFlagTestAndSetInst(std::string val) { + return "%11 = OpAtomicFlagTestAndSet %uint %4 %uint_1 %uint_" + val; +} + +TEST_P(MemorySemanticsModified, OpAtomicFlagTestAndSet) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicFlagTestAndSetInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpAtomicFlagClearInst(std::string val) { + return "OpAtomicFlagClear %4 %uint_1 %uint_" + val; +} + +TEST_P(MemorySemanticsModified, OpAtomicFlagClear) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpAtomicFlagClearInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetOpMemoryNamedBarrierInst(std::string val) { + return "OpMemoryNamedBarrier %4 %uint_1 %uint_" + val; +} + +TEST_P(MemorySemanticsModified, OpMemoryNamedBarrier) { + std::string input, expected; + std::tie(input, expected) = + GetInputAndExpected(GetOpMemoryNamedBarrierInst, GetParam()); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + StripAtomicCounterMemoryTest, MemorySemanticsModified, + ::testing::ValuesIn(std::vector({ + std::make_tuple("1024", "0"), + std::make_tuple("5", "5"), + std::make_tuple("1288", "264"), + std::make_tuple("264", "264") + }))); +// clang-format on + +std::string GetNoMemorySemanticsPresentInst(std::string val) { + return "%11 = OpVariable %_ptr_Workgroup_uint Workgroup %uint_" + val; +} + +TEST_F(NonMemorySemanticsUnmodifiedTest, NoMemorySemanticsPresent) { + std::string input, expected; + StripAtomicCounterMemoryParam param = std::make_tuple("1288", "1288"); + std::tie(input, expected) = + GetInputAndExpected(GetNoMemorySemanticsPresentInst, param); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +std::string GetMemorySemanticsPresentInst(std::string val) { + return "%11 = OpAtomicIAdd %uint %4 %uint_1 %uint_" + val + " %uint_1288"; +} + +TEST_F(NonMemorySemanticsUnmodifiedTest, MemorySemanticsPresent) { + std::string input, expected; + StripAtomicCounterMemoryParam param = std::make_tuple("1288", "264"); + std::tie(input, expected) = + GetInputAndExpected(GetMemorySemanticsPresentInst, param); + SinglePassRunAndCheck(input, expected, + /* skip_nop = */ false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/strip_debug_info_test.cpp b/third_party/spirv-tools/test/opt/strip_debug_info_test.cpp new file mode 100644 index 0000000..088bba9 --- /dev/null +++ b/third_party/spirv-tools/test/opt/strip_debug_info_test.cpp @@ -0,0 +1,232 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using StripLineDebugInfoTest = PassTest<::testing::Test>; + +TEST_F(StripLineDebugInfoTest, LineNoLine) { + std::vector text = { + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + "%3 = OpString \"minimal.vert\"", + "OpModuleProcessed \"42\"", + "OpModuleProcessed \"43\"", + "OpModuleProcessed \"44\"", + "OpNoLine", + "OpLine %3 10 10", + "%void = OpTypeVoid", + "OpLine %3 100 100", + "%5 = OpTypeFunction %void", + "%2 = OpFunction %void None %5", + "OpLine %3 1 1", + "OpNoLine", + "OpLine %3 2 2", + "OpLine %3 3 3", + "%6 = OpLabel", + "OpLine %3 4 4", + "OpNoLine", + "OpReturn", + "OpLine %3 4 4", + "OpNoLine", + "OpFunctionEnd", + "OpNoLine", + "OpLine %3 4 5" + // clang-format on + }; + SinglePassRunAndCheck(JoinAllInsts(text), + JoinNonDebugInsts(text), + /* skip_nop = */ false); + + // Let's add more debug instruction before the "OpString" instruction. + const std::vector more_text = { + "OpSourceContinued \"I'm a happy shader! Yay! ;)\"", + "OpSourceContinued \"wahahaha\"", + "OpSource ESSL 310", + "OpSource ESSL 310", + "OpSourceContinued \"wahahaha\"", + "OpSourceContinued \"wahahaha\"", + "OpSourceExtension \"save-the-world-extension\"", + "OpName %2 \"main\"", + }; + text.insert(text.begin() + 4, more_text.cbegin(), more_text.cend()); + SinglePassRunAndCheck(JoinAllInsts(text), + JoinNonDebugInsts(text), + /* skip_nop = */ false); +} + +using StripDebugStringTest = PassTest<::testing::Test>; + +TEST_F(StripDebugStringTest, OpDecorateRemoved) { + std::vector input{ + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + "%3 = OpString \"minimal.vert\"", + "OpDecorate %3 Location 1337", + "%void = OpTypeVoid", + "%5 = OpTypeFunction %void", + "%2 = OpFunction %void None %5", + "%6 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + std::vector output{ + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + "%void = OpTypeVoid", + "%5 = OpTypeFunction %void", + "%2 = OpFunction %void None %5", + "%6 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck(JoinAllInsts(input), + JoinAllInsts(output), + /* skip_nop = */ false, + /* do_validation */ true); +} + +TEST_F(StripDebugStringTest, OpNameRemoved) { + std::vector input{ + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + "%3 = OpString \"minimal.vert\"", + "OpName %3 \"bob\"", + "%void = OpTypeVoid", + "%5 = OpTypeFunction %void", + "%2 = OpFunction %void None %5", + "%6 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + std::vector output{ + // clang-format off + "OpCapability Shader", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + "%void = OpTypeVoid", + "%5 = OpTypeFunction %void", + "%2 = OpFunction %void None %5", + "%6 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck(JoinAllInsts(input), + JoinAllInsts(output), + /* skip_nop = */ false, + /* do_validation */ true); +} + +TEST_F(StripDebugStringTest, OpStringRemovedWithNonSemantic) { + std::vector input{ + // clang-format off + "OpCapability Shader", + "OpExtension \"SPV_KHR_non_semantic_info\"", + "%1 = OpExtInstImport \"NonSemantic.Testing.Set\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + // this string is not referenced, should be removed fully + "%3 = OpString \"minimal.vert\"", + "OpName %3 \"bob\"", + // this string is referenced and cannot be removed, + // but the name should be + "%4 = OpString \"secondary.inc\"", + "OpName %4 \"sue\"", + "%void = OpTypeVoid", + "%6 = OpTypeFunction %void", + "%2 = OpFunction %void None %6", + "%7 = OpLabel", + "%8 = OpExtInst %void %1 5 %4", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + std::vector output{ + // clang-format off + "OpCapability Shader", + "OpExtension \"SPV_KHR_non_semantic_info\"", + "%1 = OpExtInstImport \"NonSemantic.Testing.Set\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %2 \"main\"", + "%4 = OpString \"secondary.inc\"", + "%void = OpTypeVoid", + "%6 = OpTypeFunction %void", + "%2 = OpFunction %void None %6", + "%7 = OpLabel", + "%8 = OpExtInst %void %1 5 %4", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + SinglePassRunAndCheck(JoinAllInsts(input), + JoinAllInsts(output), + /* skip_nop = */ false, + /* do_validation */ true); +} + +using StripDebugInfoTest = PassTest<::testing::TestWithParam>; + +TEST_P(StripDebugInfoTest, Kind) { + std::vector text = { + "OpCapability Shader", + "OpMemoryModel Logical GLSL450", + GetParam(), + }; + SinglePassRunAndCheck(JoinAllInsts(text), + JoinNonDebugInsts(text), + /* skip_nop = */ false); +} + +// Test each possible non-line debug instruction. +// clang-format off +INSTANTIATE_TEST_SUITE_P( + SingleKindDebugInst, StripDebugInfoTest, + ::testing::ValuesIn(std::vector({ + "OpSourceContinued \"I'm a happy shader! Yay! ;)\"", + "OpSource ESSL 310", + "OpSourceExtension \"save-the-world-extension\"", + "OpName %main \"main\"", + "OpMemberName %struct 0 \"field\"", + "%1 = OpString \"name.vert\"", + "OpModuleProcessed \"42\"", + }))); +// clang-format on + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/strip_reflect_info_test.cpp b/third_party/spirv-tools/test/opt/strip_reflect_info_test.cpp new file mode 100644 index 0000000..f3fc115 --- /dev/null +++ b/third_party/spirv-tools/test/opt/strip_reflect_info_test.cpp @@ -0,0 +1,231 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "gmock/gmock.h" + +#include "spirv-tools/optimizer.hpp" + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using StripLineReflectInfoTest = PassTest<::testing::Test>; +using StripNonSemanticInfoTest = PassTest<::testing::Test>; + +// This test acts as an end-to-end code example on how to strip +// reflection info from a SPIR-V module. Use this code pattern +// when you have compiled HLSL code with Glslang or DXC using +// option -fhlsl_functionality1 to insert reflection information, +// but then want to filter out the extra instructions before sending +// it to a driver that does not implement VK_GOOGLE_hlsl_functionality1. +TEST_F(StripLineReflectInfoTest, StripReflectEnd2EndExample) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" +OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_1); + std::vector binary_in; + tools.Assemble(before, &binary_in); + + // Instantiate the optimizer, and run the strip-reflection-info + // pass over the |binary_in| module, and place the modified module + // into |binary_out|. + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_1); + optimizer.RegisterPass(spvtools::CreateStripReflectInfoPass()); + std::vector binary_out; + optimizer.Run(binary_in.data(), binary_in.size(), &binary_out); + + // Check results + std::string disassembly; + tools.Disassemble(binary_out.data(), binary_out.size(), &disassembly); + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + EXPECT_THAT(disassembly, testing::Eq(after)); +} + +// This test is functionally the same as the end-to-end test above, +// but uses the test SinglePassRunAndCheck test fixture instead. +TEST_F(StripLineReflectInfoTest, StripHlslSemantic) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateStringGOOGLE %float HlslSemanticGOOGLE "foobar" +OpDecorateStringGOOGLE %void HlslSemanticGOOGLE "my goodness" +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripLineReflectInfoTest, StripHlslCounterBuffer) { + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpDecorateId %void HlslCounterBufferGOOGLE %float +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%void = OpTypeVoid +%float = OpTypeFloat 32 +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripLineReflectInfoTest, StripHlslSemanticOnMember) { + // This is a non-sensical example, but exercises the instructions. + std::string before = R"(OpCapability Shader +OpCapability Linkage +OpExtension "SPV_GOOGLE_decorate_string" +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical Simple +OpMemberDecorateStringGOOGLE %struct 0 HlslSemanticGOOGLE "foobar" +%float = OpTypeFloat 32 +%_struct_3 = OpTypeStruct %float +)"; + std::string after = R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical Simple +%float = OpTypeFloat 32 +%_struct_3 = OpTypeStruct %float +)"; + + SinglePassRunAndCheck(before, after, false); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticImport) { + std::string text = R"( +; CHECK-NOT: OpExtension "SPV_KHR_non_semantic_info" +; CHECK-NOT: OpExtInstImport +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticGlobal) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%1 = OpExtInst %void %ext 1 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticInFunction) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +%1 = OpExtInst %void %ext 1 %foo +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticAfterFunction) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%1 = OpExtInst %void %ext 1 %foo +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(StripNonSemanticInfoTest, StripNonSemanticBetweenFunctions) { + std::string text = R"( +; CHECK-NOT: OpExtInst +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.Test" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%1 = OpExtInst %void %ext 1 %foo +%bar = OpFunction %void None %void_fn +%bar_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/struct_cfg_analysis_test.cpp b/third_party/spirv-tools/test/opt/struct_cfg_analysis_test.cpp new file mode 100644 index 0000000..e7031cb --- /dev/null +++ b/third_party/spirv-tools/test/opt/struct_cfg_analysis_test.cpp @@ -0,0 +1,1531 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/struct_cfg_analysis.h" + +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using StructCFGAnalysisTest = PassTest<::testing::Test>; +using ::testing::UnorderedElementsAre; + +TEST_F(StructCFGAnalysisTest, BBInSelection) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%1 = OpLabel +OpSelectionMerge %3 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The header is not in the construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // BB2 is in the construct. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 0); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 0); + EXPECT_EQ(analysis.LoopNestingDepth(2), 0); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The merge node is not in the construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); +} + +TEST_F(StructCFGAnalysisTest, BBInLoop) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %3 %4 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpBranch %3 +%4 = OpLabel +OpBranch %1 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The header is not in the construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // BB2 is in the construct. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 1); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 3); + EXPECT_EQ(analysis.LoopNestingDepth(2), 1); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The merge node is not in the construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The continue block is in the construct. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 1); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 3); + EXPECT_EQ(analysis.LoopNestingDepth(4), 1); + EXPECT_EQ(analysis.ContainingSwitch(4), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 0); + EXPECT_TRUE(analysis.IsContinueBlock(4)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_TRUE(analysis.IsInContinueConstruct(4)); + EXPECT_FALSE(analysis.IsMergeBlock(4)); +} + +TEST_F(StructCFGAnalysisTest, SelectionInLoop) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %3 %4 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpSelectionMerge %6 None +OpBranchConditional %undef_bool %5 %6 +%5 = OpLabel +OpBranch %6 +%6 = OpLabel +OpBranch %3 +%4 = OpLabel +OpBranch %1 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The loop header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // Selection header is in the loop only. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 1); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 3); + EXPECT_EQ(analysis.LoopNestingDepth(2), 1); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The loop merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The continue block is in the loop only. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 1); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 3); + EXPECT_EQ(analysis.LoopNestingDepth(4), 1); + EXPECT_EQ(analysis.ContainingSwitch(4), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 0); + EXPECT_TRUE(analysis.IsContinueBlock(4)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_TRUE(analysis.IsInContinueConstruct(4)); + EXPECT_FALSE(analysis.IsMergeBlock(4)); + + // BB5 is in the selection and the loop. + EXPECT_EQ(analysis.ContainingConstruct(5), 2); + EXPECT_EQ(analysis.ContainingLoop(5), 1); + EXPECT_EQ(analysis.MergeBlock(5), 6); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 3); + EXPECT_EQ(analysis.LoopNestingDepth(5), 1); + EXPECT_EQ(analysis.ContainingSwitch(5), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 0); + EXPECT_FALSE(analysis.IsContinueBlock(5)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_FALSE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); + + // The selection merge is in the loop only. + EXPECT_EQ(analysis.ContainingConstruct(6), 1); + EXPECT_EQ(analysis.ContainingLoop(6), 1); + EXPECT_EQ(analysis.MergeBlock(6), 3); + EXPECT_EQ(analysis.NestingDepth(6), 1); + EXPECT_EQ(analysis.LoopMergeBlock(6), 3); + EXPECT_EQ(analysis.LoopNestingDepth(6), 1); + EXPECT_EQ(analysis.ContainingSwitch(6), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(6), 0); + EXPECT_FALSE(analysis.IsContinueBlock(6)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6)); + EXPECT_FALSE(analysis.IsInContinueConstruct(6)); + EXPECT_TRUE(analysis.IsMergeBlock(6)); +} + +TEST_F(StructCFGAnalysisTest, LoopInSelection) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpSelectionMerge %3 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpLoopMerge %4 %5 None +OpBranchConditional %undef_bool %4 %6 +%5 = OpLabel +OpBranch %2 +%6 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The selection header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // Loop header is in the selection only. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 0); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 0); + EXPECT_EQ(analysis.LoopNestingDepth(2), 0); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The selection merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The loop merge is in the selection only. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 0); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 0); + EXPECT_EQ(analysis.LoopNestingDepth(4), 0); + EXPECT_EQ(analysis.ContainingSwitch(4), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 0); + EXPECT_FALSE(analysis.IsContinueBlock(4)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_FALSE(analysis.IsInContinueConstruct(4)); + EXPECT_TRUE(analysis.IsMergeBlock(4)); + + // The loop continue target is in the loop. + EXPECT_EQ(analysis.ContainingConstruct(5), 2); + EXPECT_EQ(analysis.ContainingLoop(5), 2); + EXPECT_EQ(analysis.MergeBlock(5), 4); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 4); + EXPECT_EQ(analysis.LoopNestingDepth(5), 1); + EXPECT_EQ(analysis.ContainingSwitch(5), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 0); + EXPECT_TRUE(analysis.IsContinueBlock(5)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_TRUE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); + + // BB6 is in the loop. + EXPECT_EQ(analysis.ContainingConstruct(6), 2); + EXPECT_EQ(analysis.ContainingLoop(6), 2); + EXPECT_EQ(analysis.MergeBlock(6), 4); + EXPECT_EQ(analysis.NestingDepth(6), 2); + EXPECT_EQ(analysis.LoopMergeBlock(6), 4); + EXPECT_EQ(analysis.LoopNestingDepth(6), 1); + EXPECT_EQ(analysis.ContainingSwitch(6), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(6), 0); + EXPECT_FALSE(analysis.IsContinueBlock(6)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6)); + EXPECT_FALSE(analysis.IsInContinueConstruct(6)); + EXPECT_FALSE(analysis.IsMergeBlock(6)); +} + +TEST_F(StructCFGAnalysisTest, SelectionInSelection) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpSelectionMerge %3 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpSelectionMerge %4 None +OpBranchConditional %undef_bool %4 %5 +%5 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The outer selection header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // The inner header is in the outer selection. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 0); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 0); + EXPECT_EQ(analysis.LoopNestingDepth(2), 0); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The outer merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The inner merge is in the outer selection. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 0); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 0); + EXPECT_EQ(analysis.LoopNestingDepth(4), 0); + EXPECT_EQ(analysis.ContainingSwitch(4), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 0); + EXPECT_FALSE(analysis.IsContinueBlock(4)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_FALSE(analysis.IsInContinueConstruct(4)); + EXPECT_TRUE(analysis.IsMergeBlock(4)); + + // BB5 is in the inner selection. + EXPECT_EQ(analysis.ContainingConstruct(5), 2); + EXPECT_EQ(analysis.ContainingLoop(5), 0); + EXPECT_EQ(analysis.MergeBlock(5), 4); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 0); + EXPECT_EQ(analysis.LoopNestingDepth(5), 0); + EXPECT_EQ(analysis.ContainingSwitch(5), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 0); + EXPECT_FALSE(analysis.IsContinueBlock(5)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_FALSE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); +} + +TEST_F(StructCFGAnalysisTest, LoopInLoop) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %3 %7 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpLoopMerge %4 %5 None +OpBranchConditional %undef_bool %4 %6 +%5 = OpLabel +OpBranch %2 +%6 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %3 +%7 = OpLabel +OpBranch %1 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The outer loop header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // The inner loop header is in the outer loop. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 1); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 3); + EXPECT_EQ(analysis.LoopNestingDepth(2), 1); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The outer merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The inner merge is in the outer loop. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 1); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 3); + EXPECT_EQ(analysis.LoopNestingDepth(4), 1); + EXPECT_EQ(analysis.ContainingSwitch(4), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 0); + EXPECT_FALSE(analysis.IsContinueBlock(4)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_FALSE(analysis.IsInContinueConstruct(4)); + EXPECT_TRUE(analysis.IsMergeBlock(4)); + + // The inner continue target is in the inner loop. + EXPECT_EQ(analysis.ContainingConstruct(5), 2); + EXPECT_EQ(analysis.ContainingLoop(5), 2); + EXPECT_EQ(analysis.MergeBlock(5), 4); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 4); + EXPECT_EQ(analysis.LoopNestingDepth(5), 2); + EXPECT_EQ(analysis.ContainingSwitch(5), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 0); + EXPECT_TRUE(analysis.IsContinueBlock(5)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_TRUE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); + + // BB6 is in the loop. + EXPECT_EQ(analysis.ContainingConstruct(6), 2); + EXPECT_EQ(analysis.ContainingLoop(6), 2); + EXPECT_EQ(analysis.MergeBlock(6), 4); + EXPECT_EQ(analysis.NestingDepth(6), 2); + EXPECT_EQ(analysis.LoopMergeBlock(6), 4); + EXPECT_EQ(analysis.LoopNestingDepth(6), 2); + EXPECT_EQ(analysis.ContainingSwitch(6), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(6), 0); + EXPECT_FALSE(analysis.IsContinueBlock(6)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6)); + EXPECT_FALSE(analysis.IsInContinueConstruct(6)); + EXPECT_FALSE(analysis.IsMergeBlock(6)); + + // The outer continue target is in the outer loop. + EXPECT_EQ(analysis.ContainingConstruct(7), 1); + EXPECT_EQ(analysis.ContainingLoop(7), 1); + EXPECT_EQ(analysis.MergeBlock(7), 3); + EXPECT_EQ(analysis.NestingDepth(7), 1); + EXPECT_EQ(analysis.LoopMergeBlock(7), 3); + EXPECT_EQ(analysis.LoopNestingDepth(7), 1); + EXPECT_EQ(analysis.ContainingSwitch(7), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(7), 0); + EXPECT_TRUE(analysis.IsContinueBlock(7)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7)); + EXPECT_TRUE(analysis.IsInContinueConstruct(7)); + EXPECT_FALSE(analysis.IsMergeBlock(7)); +} + +TEST_F(StructCFGAnalysisTest, KernelTest) { + const std::string text = R"( +OpCapability Kernel +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%1 = OpLabel +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // No structured control flow, so none of the basic block are in any + // construct. + for (uint32_t i = 1; i <= 3; i++) { + EXPECT_EQ(analysis.ContainingConstruct(i), 0); + EXPECT_EQ(analysis.ContainingLoop(i), 0); + EXPECT_EQ(analysis.MergeBlock(i), 0); + EXPECT_EQ(analysis.NestingDepth(i), 0); + EXPECT_EQ(analysis.LoopMergeBlock(i), 0); + EXPECT_EQ(analysis.LoopNestingDepth(i), 0); + EXPECT_EQ(analysis.ContainingSwitch(i), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(i), 0); + EXPECT_FALSE(analysis.IsContinueBlock(i)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(i)); + EXPECT_FALSE(analysis.IsInContinueConstruct(i)); + EXPECT_FALSE(analysis.IsMergeBlock(i)); + } +} + +TEST_F(StructCFGAnalysisTest, EmptyFunctionTest) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %func LinkageAttributes "x" Import +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + // #2451: This segfaulted on empty functions. + StructuredCFGAnalysis analysis(context.get()); +} + +TEST_F(StructCFGAnalysisTest, BBInSwitch) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%1 = OpLabel +OpSelectionMerge %3 None +OpSwitch %uint_undef %2 0 %3 +%2 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The header is not in the construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // BB2 is in the construct. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 0); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 0); + EXPECT_EQ(analysis.LoopNestingDepth(2), 0); + EXPECT_EQ(analysis.ContainingSwitch(2), 1); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 3); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The merge node is not in the construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); +} + +TEST_F(StructCFGAnalysisTest, LoopInSwitch) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpSelectionMerge %3 None +OpSwitch %uint_undef %2 1 %3 +%2 = OpLabel +OpLoopMerge %4 %5 None +OpBranchConditional %undef_bool %4 %6 +%5 = OpLabel +OpBranch %2 +%6 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The selection header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // Loop header is in the selection only. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 0); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 0); + EXPECT_EQ(analysis.LoopNestingDepth(2), 0); + EXPECT_EQ(analysis.ContainingSwitch(2), 1); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 3); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The selection merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The loop merge is in the selection only. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 0); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 0); + EXPECT_EQ(analysis.LoopNestingDepth(4), 0); + EXPECT_EQ(analysis.ContainingSwitch(4), 1); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 3); + EXPECT_FALSE(analysis.IsContinueBlock(4)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_FALSE(analysis.IsInContinueConstruct(4)); + EXPECT_TRUE(analysis.IsMergeBlock(4)); + + // The loop continue target is in the loop. + EXPECT_EQ(analysis.ContainingConstruct(5), 2); + EXPECT_EQ(analysis.ContainingLoop(5), 2); + EXPECT_EQ(analysis.MergeBlock(5), 4); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 4); + EXPECT_EQ(analysis.LoopNestingDepth(5), 1); + EXPECT_EQ(analysis.ContainingSwitch(5), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 0); + EXPECT_TRUE(analysis.IsContinueBlock(5)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_TRUE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); + + // BB6 is in the loop. + EXPECT_EQ(analysis.ContainingConstruct(6), 2); + EXPECT_EQ(analysis.ContainingLoop(6), 2); + EXPECT_EQ(analysis.MergeBlock(6), 4); + EXPECT_EQ(analysis.NestingDepth(6), 2); + EXPECT_EQ(analysis.LoopMergeBlock(6), 4); + EXPECT_EQ(analysis.LoopNestingDepth(6), 1); + EXPECT_EQ(analysis.ContainingSwitch(6), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(6), 0); + EXPECT_FALSE(analysis.IsContinueBlock(6)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6)); + EXPECT_FALSE(analysis.IsInContinueConstruct(6)); + EXPECT_FALSE(analysis.IsMergeBlock(6)); +} + +TEST_F(StructCFGAnalysisTest, SelectionInSwitch) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpSelectionMerge %3 None +OpSwitch %uint_undef %2 10 %3 +%2 = OpLabel +OpSelectionMerge %4 None +OpBranchConditional %undef_bool %4 %5 +%5 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The outer selection header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // The inner header is in the outer selection. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 0); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 0); + EXPECT_EQ(analysis.LoopNestingDepth(2), 0); + EXPECT_EQ(analysis.ContainingSwitch(2), 1); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 3); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The outer merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The inner merge is in the outer selection. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 0); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 0); + EXPECT_EQ(analysis.LoopNestingDepth(4), 0); + EXPECT_EQ(analysis.ContainingSwitch(4), 1); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 3); + EXPECT_FALSE(analysis.IsContinueBlock(4)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_FALSE(analysis.IsInContinueConstruct(4)); + EXPECT_TRUE(analysis.IsMergeBlock(4)); + + // BB5 is in the inner selection. + EXPECT_EQ(analysis.ContainingConstruct(5), 2); + EXPECT_EQ(analysis.ContainingLoop(5), 0); + EXPECT_EQ(analysis.MergeBlock(5), 4); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 0); + EXPECT_EQ(analysis.LoopNestingDepth(5), 0); + EXPECT_EQ(analysis.ContainingSwitch(5), 1); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 3); + EXPECT_FALSE(analysis.IsContinueBlock(5)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_FALSE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); +} + +TEST_F(StructCFGAnalysisTest, SwitchInSelection) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpSelectionMerge %3 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpSelectionMerge %4 None +OpSwitch %uint_undef %4 7 %5 +%5 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %3 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The outer selection header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // The inner header is in the outer selection. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 0); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 0); + EXPECT_EQ(analysis.LoopNestingDepth(2), 0); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The outer merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The inner merge is in the outer selection. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 0); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 0); + EXPECT_EQ(analysis.LoopNestingDepth(4), 0); + EXPECT_EQ(analysis.ContainingSwitch(4), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 0); + EXPECT_FALSE(analysis.IsContinueBlock(4)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_FALSE(analysis.IsInContinueConstruct(4)); + EXPECT_TRUE(analysis.IsMergeBlock(4)); + + // BB5 is in the inner selection. + EXPECT_EQ(analysis.ContainingConstruct(5), 2); + EXPECT_EQ(analysis.ContainingLoop(5), 0); + EXPECT_EQ(analysis.MergeBlock(5), 4); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 0); + EXPECT_EQ(analysis.LoopNestingDepth(5), 0); + EXPECT_EQ(analysis.ContainingSwitch(5), 2); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 4); + EXPECT_FALSE(analysis.IsContinueBlock(5)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_FALSE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); +} + +TEST_F(StructCFGAnalysisTest, SelectionInContinue) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %3 %4 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpBranch %3 +%4 = OpLabel +OpSelectionMerge %6 None +OpBranchConditional %undef_bool %5 %6 +%5 = OpLabel +OpBranch %6 +%6 = OpLabel +OpBranch %1 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The loop header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // Selection header is in the loop only. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 1); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 3); + EXPECT_EQ(analysis.LoopNestingDepth(2), 1); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The loop merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The continue block is in the loop only. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 1); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 3); + EXPECT_EQ(analysis.LoopNestingDepth(4), 1); + EXPECT_EQ(analysis.ContainingSwitch(4), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 0); + EXPECT_TRUE(analysis.IsContinueBlock(4)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_TRUE(analysis.IsInContinueConstruct(4)); + EXPECT_FALSE(analysis.IsMergeBlock(4)); + + // BB5 is in the selection and the continue for the loop. + EXPECT_EQ(analysis.ContainingConstruct(5), 4); + EXPECT_EQ(analysis.ContainingLoop(5), 1); + EXPECT_EQ(analysis.MergeBlock(5), 6); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 3); + EXPECT_EQ(analysis.LoopNestingDepth(5), 1); + EXPECT_EQ(analysis.ContainingSwitch(5), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 0); + EXPECT_FALSE(analysis.IsContinueBlock(5)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_TRUE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); + + // BB5 is in the continue for the loop. + EXPECT_EQ(analysis.ContainingConstruct(6), 1); + EXPECT_EQ(analysis.ContainingLoop(6), 1); + EXPECT_EQ(analysis.MergeBlock(6), 3); + EXPECT_EQ(analysis.NestingDepth(6), 1); + EXPECT_EQ(analysis.LoopMergeBlock(6), 3); + EXPECT_EQ(analysis.LoopNestingDepth(6), 1); + EXPECT_EQ(analysis.ContainingSwitch(6), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(6), 0); + EXPECT_FALSE(analysis.IsContinueBlock(6)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(6)); + EXPECT_TRUE(analysis.IsInContinueConstruct(6)); + EXPECT_TRUE(analysis.IsMergeBlock(6)); +} + +TEST_F(StructCFGAnalysisTest, LoopInContinue) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +%void = OpTypeVoid +%bool = OpTypeBool +%bool_undef = OpUndef %bool +%uint = OpTypeInt 32 0 +%uint_undef = OpUndef %uint +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%entry_lab = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %3 %7 None +OpBranchConditional %undef_bool %2 %3 +%2 = OpLabel +OpBranchConditional %undef_bool %3 %7 +%7 = OpLabel +OpLoopMerge %4 %5 None +OpBranchConditional %undef_bool %4 %6 +%5 = OpLabel +OpBranch %7 +%6 = OpLabel +OpBranch %4 +%4 = OpLabel +OpBranch %1 +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + // The outer loop header is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(1), 0); + EXPECT_EQ(analysis.ContainingLoop(1), 0); + EXPECT_EQ(analysis.MergeBlock(1), 0); + EXPECT_EQ(analysis.NestingDepth(1), 0); + EXPECT_EQ(analysis.LoopMergeBlock(1), 0); + EXPECT_EQ(analysis.LoopNestingDepth(1), 0); + EXPECT_EQ(analysis.ContainingSwitch(1), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(1), 0); + EXPECT_FALSE(analysis.IsContinueBlock(1)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(1)); + EXPECT_FALSE(analysis.IsInContinueConstruct(1)); + EXPECT_FALSE(analysis.IsMergeBlock(1)); + + // BB2 is a regular block in the inner loop. + EXPECT_EQ(analysis.ContainingConstruct(2), 1); + EXPECT_EQ(analysis.ContainingLoop(2), 1); + EXPECT_EQ(analysis.MergeBlock(2), 3); + EXPECT_EQ(analysis.NestingDepth(2), 1); + EXPECT_EQ(analysis.LoopMergeBlock(2), 3); + EXPECT_EQ(analysis.LoopNestingDepth(2), 1); + EXPECT_EQ(analysis.ContainingSwitch(2), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(2), 0); + EXPECT_FALSE(analysis.IsContinueBlock(2)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(2)); + EXPECT_FALSE(analysis.IsInContinueConstruct(2)); + EXPECT_FALSE(analysis.IsMergeBlock(2)); + + // The outer merge node is not in either construct. + EXPECT_EQ(analysis.ContainingConstruct(3), 0); + EXPECT_EQ(analysis.ContainingLoop(3), 0); + EXPECT_EQ(analysis.MergeBlock(3), 0); + EXPECT_EQ(analysis.NestingDepth(3), 0); + EXPECT_EQ(analysis.LoopMergeBlock(3), 0); + EXPECT_EQ(analysis.LoopNestingDepth(3), 0); + EXPECT_EQ(analysis.ContainingSwitch(3), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(3), 0); + EXPECT_FALSE(analysis.IsContinueBlock(3)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(3)); + EXPECT_FALSE(analysis.IsInContinueConstruct(3)); + EXPECT_TRUE(analysis.IsMergeBlock(3)); + + // The inner merge is in the continue of the outer loop. + EXPECT_EQ(analysis.ContainingConstruct(4), 1); + EXPECT_EQ(analysis.ContainingLoop(4), 1); + EXPECT_EQ(analysis.MergeBlock(4), 3); + EXPECT_EQ(analysis.NestingDepth(4), 1); + EXPECT_EQ(analysis.LoopMergeBlock(4), 3); + EXPECT_EQ(analysis.LoopNestingDepth(4), 1); + EXPECT_EQ(analysis.ContainingSwitch(4), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(4), 0); + EXPECT_FALSE(analysis.IsContinueBlock(4)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(4)); + EXPECT_TRUE(analysis.IsInContinueConstruct(4)); + EXPECT_TRUE(analysis.IsMergeBlock(4)); + + // The inner continue target is in the inner loop. + EXPECT_EQ(analysis.ContainingConstruct(5), 7); + EXPECT_EQ(analysis.ContainingLoop(5), 7); + EXPECT_EQ(analysis.MergeBlock(5), 4); + EXPECT_EQ(analysis.NestingDepth(5), 2); + EXPECT_EQ(analysis.LoopMergeBlock(5), 4); + EXPECT_EQ(analysis.LoopNestingDepth(5), 2); + EXPECT_EQ(analysis.ContainingSwitch(5), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(5), 0); + EXPECT_TRUE(analysis.IsContinueBlock(5)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(5)); + EXPECT_TRUE(analysis.IsInContinueConstruct(5)); + EXPECT_FALSE(analysis.IsMergeBlock(5)); + + // BB6 is a regular block in the inner loop. + EXPECT_EQ(analysis.ContainingConstruct(6), 7); + EXPECT_EQ(analysis.ContainingLoop(6), 7); + EXPECT_EQ(analysis.MergeBlock(6), 4); + EXPECT_EQ(analysis.NestingDepth(6), 2); + EXPECT_EQ(analysis.LoopMergeBlock(6), 4); + EXPECT_EQ(analysis.LoopNestingDepth(6), 2); + EXPECT_EQ(analysis.ContainingSwitch(6), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(6), 0); + EXPECT_FALSE(analysis.IsContinueBlock(6)); + EXPECT_FALSE(analysis.IsInContainingLoopsContinueConstruct(6)); + EXPECT_TRUE(analysis.IsInContinueConstruct(6)); + EXPECT_FALSE(analysis.IsMergeBlock(6)); + + // The outer continue target is in the outer loop. + EXPECT_EQ(analysis.ContainingConstruct(7), 1); + EXPECT_EQ(analysis.ContainingLoop(7), 1); + EXPECT_EQ(analysis.MergeBlock(7), 3); + EXPECT_EQ(analysis.NestingDepth(7), 1); + EXPECT_EQ(analysis.LoopMergeBlock(7), 3); + EXPECT_EQ(analysis.LoopNestingDepth(7), 1); + EXPECT_EQ(analysis.ContainingSwitch(7), 0); + EXPECT_EQ(analysis.SwitchMergeBlock(7), 0); + EXPECT_TRUE(analysis.IsContinueBlock(7)); + EXPECT_TRUE(analysis.IsInContainingLoopsContinueConstruct(7)); + EXPECT_TRUE(analysis.IsInContinueConstruct(7)); + EXPECT_FALSE(analysis.IsMergeBlock(7)); +} + +TEST_F(StructCFGAnalysisTest, FuncCallInContinueDirect) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %4 = OpUndef %bool + %uint = OpTypeInt 32 0 + %6 = OpUndef %uint + %7 = OpTypeFunction %void + %1 = OpFunction %void None %7 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranchConditional %12 %10 %11 + %11 = OpLabel + %13 = OpFunctionCall %void %14 + OpBranch %9 + %10 = OpLabel + %15 = OpFunctionCall %void %16 + OpReturn + OpFunctionEnd + %14 = OpFunction %void None %7 + %17 = OpLabel + OpReturn + OpFunctionEnd + %16 = OpFunction %void None %7 + %18 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + auto c = analysis.FindFuncsCalledFromContinue(); + EXPECT_THAT(c, UnorderedElementsAre(14u)); +} + +TEST_F(StructCFGAnalysisTest, FuncCallInContinueIndirect) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + %void = OpTypeVoid + %bool = OpTypeBool + %4 = OpUndef %bool + %uint = OpTypeInt 32 0 + %6 = OpUndef %uint + %7 = OpTypeFunction %void + %1 = OpFunction %void None %7 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranchConditional %12 %10 %11 + %11 = OpLabel + %13 = OpFunctionCall %void %14 + OpBranch %9 + %10 = OpLabel + %15 = OpFunctionCall %void %16 + OpReturn + OpFunctionEnd + %14 = OpFunction %void None %7 + %17 = OpLabel + %19 = OpFunctionCall %void %16 + OpReturn + OpFunctionEnd + %16 = OpFunction %void None %7 + %18 = OpLabel + %20 = OpFunctionCall %void %21 + OpReturn + OpFunctionEnd + %21 = OpFunction %void None %7 + %22 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + auto c = analysis.FindFuncsCalledFromContinue(); + EXPECT_THAT(c, UnorderedElementsAre(14u, 16u, 21u)); +} + +TEST_F(StructCFGAnalysisTest, SingleBlockLoop) { + const std::string text = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %bool = OpTypeBool + %undef = OpUndef %bool + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn + %2 = OpLabel + OpBranch %3 + %3 = OpLabel + OpLoopMerge %4 %3 None + OpBranchConditional %undef %3 %4 + %4 = OpLabel + OpReturn + OpFunctionEnd +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + StructuredCFGAnalysis analysis(context.get()); + + EXPECT_TRUE(analysis.IsInContinueConstruct(3)); +} +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/type_manager_test.cpp b/third_party/spirv-tools/test/opt/type_manager_test.cpp new file mode 100644 index 0000000..fdae2ef --- /dev/null +++ b/third_party/spirv-tools/test/opt/type_manager_test.cpp @@ -0,0 +1,1174 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "effcee/effcee.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "source/opt/build_module.h" +#include "source/opt/instruction.h" +#include "source/opt/type_manager.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +bool Validate(const std::vector& bin) { + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2; + spv_context spvContext = spvContextCreate(target_env); + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t binary = {bin.data(), bin.size()}; + spv_result_t error = spvValidate(spvContext, &binary, &diagnostic); + if (error != 0) spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(spvContext); + return error == 0; +} + +void Match(const std::string& original, IRContext* context, + bool do_validation = true) { + std::vector bin; + context->module()->ToBinary(&bin, true); + if (do_validation) { + EXPECT_TRUE(Validate(bin)); + } + std::string assembly; + SpirvTools tools(SPV_ENV_UNIVERSAL_1_2); + EXPECT_TRUE( + tools.Disassemble(bin, &assembly, SpirvTools::kDefaultDisassembleOption)) + << "Disassembling failed for shader:\n" + << assembly << std::endl; + auto match_result = effcee::Match(assembly, original); + EXPECT_EQ(effcee::Result::Status::Ok, match_result.status()) + << match_result.message() << "\nChecking result:\n" + << assembly; +} + +std::vector> GenerateAllTypes() { + // Types in this test case are only equal to themselves, nothing else. + std::vector> types; + + // Void, Bool + types.emplace_back(new Void()); + auto* voidt = types.back().get(); + types.emplace_back(new Bool()); + auto* boolt = types.back().get(); + + // Integer + types.emplace_back(new Integer(32, true)); + auto* s32 = types.back().get(); + types.emplace_back(new Integer(32, false)); + types.emplace_back(new Integer(64, true)); + types.emplace_back(new Integer(64, false)); + auto* u64 = types.back().get(); + + // Float + types.emplace_back(new Float(32)); + auto* f32 = types.back().get(); + types.emplace_back(new Float(64)); + + // Vector + types.emplace_back(new Vector(s32, 2)); + types.emplace_back(new Vector(s32, 3)); + auto* v3s32 = types.back().get(); + types.emplace_back(new Vector(u64, 4)); + types.emplace_back(new Vector(f32, 3)); + auto* v3f32 = types.back().get(); + + // Matrix + types.emplace_back(new Matrix(v3s32, 3)); + types.emplace_back(new Matrix(v3s32, 4)); + types.emplace_back(new Matrix(v3f32, 4)); + + // Images + types.emplace_back(new Image(s32, SpvDim2D, 0, 0, 0, 0, SpvImageFormatRg8, + SpvAccessQualifierReadOnly)); + auto* image1 = types.back().get(); + types.emplace_back(new Image(s32, SpvDim2D, 0, 1, 0, 0, SpvImageFormatRg8, + SpvAccessQualifierReadOnly)); + types.emplace_back(new Image(s32, SpvDim3D, 0, 1, 0, 0, SpvImageFormatRg8, + SpvAccessQualifierReadOnly)); + types.emplace_back(new Image(voidt, SpvDim3D, 0, 1, 0, 1, SpvImageFormatRg8, + SpvAccessQualifierReadWrite)); + auto* image2 = types.back().get(); + + // Sampler + types.emplace_back(new Sampler()); + + // Sampled Image + types.emplace_back(new SampledImage(image1)); + types.emplace_back(new SampledImage(image2)); + + // Array + types.emplace_back(new Array(f32, Array::LengthInfo{100, {0, 100u}})); + types.emplace_back(new Array(f32, Array::LengthInfo{42, {0, 42u}})); + auto* a42f32 = types.back().get(); + types.emplace_back(new Array(u64, Array::LengthInfo{24, {0, 24u}})); + + // RuntimeArray + types.emplace_back(new RuntimeArray(v3f32)); + types.emplace_back(new RuntimeArray(v3s32)); + auto* rav3s32 = types.back().get(); + + // Struct + types.emplace_back(new Struct(std::vector{s32})); + types.emplace_back(new Struct(std::vector{s32, f32})); + auto* sts32f32 = types.back().get(); + types.emplace_back( + new Struct(std::vector{u64, a42f32, rav3s32})); + + // Opaque + types.emplace_back(new Opaque("")); + types.emplace_back(new Opaque("hello")); + types.emplace_back(new Opaque("world")); + + // Pointer + types.emplace_back(new Pointer(f32, SpvStorageClassInput)); + types.emplace_back(new Pointer(sts32f32, SpvStorageClassFunction)); + types.emplace_back(new Pointer(a42f32, SpvStorageClassFunction)); + + // Function + types.emplace_back(new Function(voidt, {})); + types.emplace_back(new Function(voidt, {boolt})); + types.emplace_back(new Function(voidt, {boolt, s32})); + types.emplace_back(new Function(s32, {boolt, s32})); + + // Event, Device Event, Reserve Id, Queue, + types.emplace_back(new Event()); + types.emplace_back(new DeviceEvent()); + types.emplace_back(new ReserveId()); + types.emplace_back(new Queue()); + + // Pipe, Forward Pointer, PipeStorage, NamedBarrier, AccelerationStructureNV, + // CooperativeMatrixNV + types.emplace_back(new Pipe(SpvAccessQualifierReadWrite)); + types.emplace_back(new Pipe(SpvAccessQualifierReadOnly)); + types.emplace_back(new ForwardPointer(1, SpvStorageClassInput)); + types.emplace_back(new ForwardPointer(2, SpvStorageClassInput)); + types.emplace_back(new ForwardPointer(2, SpvStorageClassUniform)); + types.emplace_back(new PipeStorage()); + types.emplace_back(new NamedBarrier()); + types.emplace_back(new AccelerationStructureNV()); + types.emplace_back(new CooperativeMatrixNV(f32, 24, 24, 24)); + + return types; +} + +TEST(TypeManager, TypeStrings) { + const std::string text = R"( + OpDecorate %spec_const_with_id SpecId 99 + OpTypeForwardPointer %p Uniform + %void = OpTypeVoid + %bool = OpTypeBool + %u32 = OpTypeInt 32 0 + %id4 = OpConstant %u32 4 + %s32 = OpTypeInt 32 1 + %f64 = OpTypeFloat 64 + %v3u32 = OpTypeVector %u32 3 + %m3x3 = OpTypeMatrix %v3u32 3 + %img1 = OpTypeImage %s32 Cube 0 1 1 0 R32f ReadWrite + %img2 = OpTypeImage %s32 Cube 0 1 1 0 R32f + %sampler = OpTypeSampler + %si1 = OpTypeSampledImage %img1 + %si2 = OpTypeSampledImage %img2 + %a5u32 = OpTypeArray %u32 %id4 + %af64 = OpTypeRuntimeArray %f64 + %st1 = OpTypeStruct %u32 + %st2 = OpTypeStruct %f64 %s32 %v3u32 + %opaque1 = OpTypeOpaque "" + %opaque2 = OpTypeOpaque "opaque" + %p = OpTypePointer Uniform %st1 + %f = OpTypeFunction %void %u32 %u32 + %event = OpTypeEvent + %de = OpTypeDeviceEvent + %ri = OpTypeReserveId + %queue = OpTypeQueue + %pipe = OpTypePipe ReadOnly + %ps = OpTypePipeStorage + %nb = OpTypeNamedBarrier + %rtacc = OpTypeAccelerationStructureNV + ; Set up other kinds of OpTypeArray + %s64 = OpTypeInt 64 1 + ; ID 32 + %spec_const_without_id = OpSpecConstant %s32 44 + %spec_const_with_id = OpSpecConstant %s32 42 ;; This is ID 1 + %long_constant = OpConstant %s64 5000000000 + %spec_const_op = OpSpecConstantOp %s32 IAdd %id4 %id4 + ; ID 35 + %arr_spec_const_without_id = OpTypeArray %s32 %spec_const_without_id + %arr_spec_const_with_id = OpTypeArray %s32 %spec_const_with_id + %arr_long_constant = OpTypeArray %s32 %long_constant + %arr_spec_const_op = OpTypeArray %s32 %spec_const_op + %cm = OpTypeCooperativeMatrixNV %f64 %id4 %id4 %id4 + )"; + + std::vector> type_id_strs = { + {3, "void"}, + {4, "bool"}, + {5, "uint32"}, + // Id 6 is used by the constant. + {7, "sint32"}, + {8, "float64"}, + {9, ""}, + {10, "<, 3>"}, + {11, "image(sint32, 3, 0, 1, 1, 0, 3, 2)"}, + {12, "image(sint32, 3, 0, 1, 1, 0, 3, 0)"}, + {13, "sampler"}, + {14, "sampled_image(image(sint32, 3, 0, 1, 1, 0, 3, 2))"}, + {15, "sampled_image(image(sint32, 3, 0, 1, 1, 0, 3, 0))"}, + {16, "[uint32, id(6), words(0,4)]"}, + {17, "[float64]"}, + {18, "{uint32}"}, + {19, "{float64, sint32, }"}, + {20, "opaque('')"}, + {21, "opaque('opaque')"}, + {2, "{uint32} 2*"}, // Include storage class number + {22, "(uint32, uint32) -> void"}, + {23, "event"}, + {24, "device_event"}, + {25, "reserve_id"}, + {26, "queue"}, + {27, "pipe(0)"}, + {28, "pipe_storage"}, + {29, "named_barrier"}, + {30, "accelerationStructureNV"}, + {31, "sint64"}, + {35, "[sint32, id(32), words(0,44)]"}, + {36, "[sint32, id(1), words(1,99,42)]"}, + {37, "[sint32, id(33), words(0,705032704,1)]"}, + {38, "[sint32, id(34), words(2,34)]"}, + {39, ""}, + }; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + ASSERT_NE(nullptr, context.get()); // It assembled + TypeManager manager(nullptr, context.get()); + + EXPECT_EQ(type_id_strs.size(), manager.NumTypes()); + + for (const auto& p : type_id_strs) { + ASSERT_NE(nullptr, manager.GetType(p.first)); + EXPECT_EQ(p.second, manager.GetType(p.first)->str()) + << " id is " << p.first; + EXPECT_EQ(p.first, manager.GetId(manager.GetType(p.first))); + } +} + +TEST(TypeManager, StructWithFwdPtr) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %7 "test" + OpSource OpenCL_C 102000 + OpDecorate %11 FuncParamAttr NoCapture + %11 = OpDecorationGroup + OpGroupDecorate %11 %8 %9 + OpTypeForwardPointer %100 CrossWorkgroup + %void = OpTypeVoid + %150 = OpTypeStruct %100 +%100 = OpTypePointer CrossWorkgroup %150 + %6 = OpTypeFunction %void %100 %100 + %7 = OpFunction %void Pure %6 + %8 = OpFunctionParameter %100 + %9 = OpFunctionParameter %100 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + TypeManager manager(nullptr, context.get()); + + Type* p100 = manager.GetType(100); + Type* s150 = manager.GetType(150); + + EXPECT_TRUE(p100->AsPointer()); + EXPECT_EQ(p100->AsPointer()->pointee_type(), s150); + + EXPECT_TRUE(s150->AsStruct()); + EXPECT_EQ(s150->AsStruct()->element_types()[0], p100); +} + +TEST(TypeManager, CircularFwdPtr) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %7 "test" + OpSource OpenCL_C 102000 + OpDecorate %11 FuncParamAttr NoCapture + %11 = OpDecorationGroup + OpGroupDecorate %11 %8 %9 + OpTypeForwardPointer %100 CrossWorkgroup + OpTypeForwardPointer %200 CrossWorkgroup + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %150 = OpTypeStruct %200 %int + %250 = OpTypeStruct %100 %float +%100 = OpTypePointer CrossWorkgroup %150 +%200 = OpTypePointer CrossWorkgroup %250 + %6 = OpTypeFunction %void %100 %200 + %7 = OpFunction %void Pure %6 + %8 = OpFunctionParameter %100 + %9 = OpFunctionParameter %200 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + TypeManager manager(nullptr, context.get()); + + Type* p100 = manager.GetType(100); + Type* s150 = manager.GetType(150); + Type* p200 = manager.GetType(200); + Type* s250 = manager.GetType(250); + + EXPECT_TRUE(p100->AsPointer()); + EXPECT_EQ(p100->AsPointer()->pointee_type(), s150); + + EXPECT_TRUE(p200->AsPointer()); + EXPECT_EQ(p200->AsPointer()->pointee_type(), s250); + + EXPECT_TRUE(s150->AsStruct()); + EXPECT_EQ(s150->AsStruct()->element_types()[0], p200); + + EXPECT_TRUE(s250->AsStruct()); + EXPECT_EQ(s250->AsStruct()->element_types()[0], p100); +} + +TEST(TypeManager, IsomorphicStructWithFwdPtr) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %7 "test" + OpSource OpenCL_C 102000 + OpDecorate %11 FuncParamAttr NoCapture + %11 = OpDecorationGroup + OpGroupDecorate %11 %8 %9 + OpTypeForwardPointer %100 CrossWorkgroup + OpTypeForwardPointer %200 CrossWorkgroup + %void = OpTypeVoid + %_struct_1 = OpTypeStruct %100 + %_struct_2 = OpTypeStruct %200 +%100 = OpTypePointer CrossWorkgroup %_struct_1 +%200 = OpTypePointer CrossWorkgroup %_struct_2 + %6 = OpTypeFunction %void %100 %200 + %7 = OpFunction %void Pure %6 + %8 = OpFunctionParameter %100 + %9 = OpFunctionParameter %200 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + TypeManager manager(nullptr, context.get()); + + EXPECT_EQ(manager.GetType(100), manager.GetType(200)); +} + +TEST(TypeManager, IsomorphicCircularFwdPtr) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %7 "test" + OpSource OpenCL_C 102000 + OpDecorate %11 FuncParamAttr NoCapture + %11 = OpDecorationGroup + OpGroupDecorate %11 %8 %9 + OpTypeForwardPointer %100 CrossWorkgroup + OpTypeForwardPointer %200 CrossWorkgroup + OpTypeForwardPointer %300 CrossWorkgroup + OpTypeForwardPointer %400 CrossWorkgroup + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %150 = OpTypeStruct %200 %int + %250 = OpTypeStruct %100 %float + %350 = OpTypeStruct %400 %int + %450 = OpTypeStruct %300 %float +%100 = OpTypePointer CrossWorkgroup %150 +%200 = OpTypePointer CrossWorkgroup %250 +%300 = OpTypePointer CrossWorkgroup %350 +%400 = OpTypePointer CrossWorkgroup %450 + %6 = OpTypeFunction %void %100 %200 + %7 = OpFunction %void Pure %6 + %8 = OpFunctionParameter %100 + %9 = OpFunctionParameter %200 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + TypeManager manager(nullptr, context.get()); + + Type* p100 = manager.GetType(100); + Type* p300 = manager.GetType(300); + EXPECT_EQ(p100, p300); + Type* p200 = manager.GetType(200); + Type* p400 = manager.GetType(400); + EXPECT_EQ(p200, p400); + + Type* p150 = manager.GetType(150); + Type* p350 = manager.GetType(350); + EXPECT_EQ(p150, p350); + Type* p250 = manager.GetType(250); + Type* p450 = manager.GetType(450); + EXPECT_EQ(p250, p450); +} + +TEST(TypeManager, PartialIsomorphicFwdPtr) { + const std::string text = R"( + OpCapability Addresses + OpCapability Kernel + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %7 "test" + OpSource OpenCL_C 102000 + OpDecorate %11 FuncParamAttr NoCapture + %11 = OpDecorationGroup + OpGroupDecorate %11 %8 %9 + OpTypeForwardPointer %100 CrossWorkgroup + OpTypeForwardPointer %200 CrossWorkgroup + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %150 = OpTypeStruct %200 %int + %250 = OpTypeStruct %200 %int +%100 = OpTypePointer CrossWorkgroup %150 +%200 = OpTypePointer CrossWorkgroup %250 + %6 = OpTypeFunction %void %100 %200 + %7 = OpFunction %void Pure %6 + %8 = OpFunctionParameter %100 + %9 = OpFunctionParameter %200 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + TypeManager manager(nullptr, context.get()); + + Type* p100 = manager.GetType(100); + Type* p200 = manager.GetType(200); + EXPECT_EQ(p100->AsPointer()->pointee_type(), + p200->AsPointer()->pointee_type()); +} + +TEST(TypeManager, DecorationOnStruct) { + const std::string text = R"( + OpDecorate %struct1 Block + OpDecorate %struct2 Block + OpDecorate %struct3 Block + OpDecorate %struct4 Block + + %u32 = OpTypeInt 32 0 ; id: 5 + %f32 = OpTypeFloat 32 ; id: 6 + %struct1 = OpTypeStruct %u32 %f32 ; base + %struct2 = OpTypeStruct %f32 %u32 ; different member order + %struct3 = OpTypeStruct %f32 ; different member list + %struct4 = OpTypeStruct %u32 %f32 ; the same + %struct7 = OpTypeStruct %f32 ; no decoration + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + TypeManager manager(nullptr, context.get()); + + ASSERT_EQ(7u, manager.NumTypes()); + // Make sure we get ids correct. + ASSERT_EQ("uint32", manager.GetType(5)->str()); + ASSERT_EQ("float32", manager.GetType(6)->str()); + + // Try all combinations of pairs. Expect to be the same type only when the + // same id or (1, 4). + for (const auto id1 : {1, 2, 3, 4, 7}) { + for (const auto id2 : {1, 2, 3, 4, 7}) { + if (id1 == id2 || (id1 == 1 && id2 == 4) || (id1 == 4 && id2 == 1)) { + EXPECT_TRUE(manager.GetType(id1)->IsSame(manager.GetType(id2))) + << "%struct" << id1 << " is expected to be the same as %struct" + << id2; + } else { + EXPECT_FALSE(manager.GetType(id1)->IsSame(manager.GetType(id2))) + << "%struct" << id1 << " is expected to be different with %struct" + << id2; + } + } + } +} + +TEST(TypeManager, DecorationOnMember) { + const std::string text = R"( + OpMemberDecorate %struct1 0 Offset 0 + OpMemberDecorate %struct2 0 Offset 0 + OpMemberDecorate %struct3 0 Offset 0 + OpMemberDecorate %struct4 0 Offset 0 + OpMemberDecorate %struct5 1 Offset 0 + OpMemberDecorate %struct6 0 Offset 4 + + OpDecorate %struct7 Block + OpMemberDecorate %struct7 0 Offset 0 + + %u32 = OpTypeInt 32 0 ; id: 8 + %f32 = OpTypeFloat 32 ; id: 9 + %struct1 = OpTypeStruct %u32 %f32 ; base + %struct2 = OpTypeStruct %f32 %u32 ; different member order + %struct3 = OpTypeStruct %f32 ; different member list + %struct4 = OpTypeStruct %u32 %f32 ; the same + %struct5 = OpTypeStruct %u32 %f32 ; member decorate different field + %struct6 = OpTypeStruct %u32 %f32 ; different member decoration parameter + %struct7 = OpTypeStruct %u32 %f32 ; extra decoration on the struct + %struct10 = OpTypeStruct %u32 %f32 ; no member decoration + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + TypeManager manager(nullptr, context.get()); + + ASSERT_EQ(10u, manager.NumTypes()); + // Make sure we get ids correct. + ASSERT_EQ("uint32", manager.GetType(8)->str()); + ASSERT_EQ("float32", manager.GetType(9)->str()); + + // Try all combinations of pairs. Expect to be the same type only when the + // same id or (1, 4). + for (const auto id1 : {1, 2, 3, 4, 5, 6, 7, 10}) { + for (const auto id2 : {1, 2, 3, 4, 5, 6, 7, 10}) { + if (id1 == id2 || (id1 == 1 && id2 == 4) || (id1 == 4 && id2 == 1)) { + EXPECT_TRUE(manager.GetType(id1)->IsSame(manager.GetType(id2))) + << "%struct" << id1 << " is expected to be the same as %struct" + << id2; + } else { + EXPECT_FALSE(manager.GetType(id1)->IsSame(manager.GetType(id2))) + << "%struct" << id1 << " is expected to be different with %struct" + << id2; + } + } + } +} + +TEST(TypeManager, DecorationEmpty) { + const std::string text = R"( + OpDecorate %struct1 Block + OpMemberDecorate %struct2 0 Offset 0 + + %u32 = OpTypeInt 32 0 ; id: 3 + %f32 = OpTypeFloat 32 ; id: 4 + %struct1 = OpTypeStruct %u32 %f32 + %struct2 = OpTypeStruct %f32 %u32 + %struct5 = OpTypeStruct %f32 + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + TypeManager manager(nullptr, context.get()); + + ASSERT_EQ(5u, manager.NumTypes()); + // Make sure we get ids correct. + ASSERT_EQ("uint32", manager.GetType(3)->str()); + ASSERT_EQ("float32", manager.GetType(4)->str()); + + // %struct1 with decoration on itself + EXPECT_FALSE(manager.GetType(1)->decoration_empty()); + // %struct2 with decoration on its member + EXPECT_FALSE(manager.GetType(2)->decoration_empty()); + EXPECT_TRUE(manager.GetType(3)->decoration_empty()); + EXPECT_TRUE(manager.GetType(4)->decoration_empty()); + // %struct5 has no decorations + EXPECT_TRUE(manager.GetType(5)->decoration_empty()); +} + +TEST(TypeManager, BeginEndForEmptyModule) { + const std::string text = ""; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + TypeManager manager(nullptr, context.get()); + ASSERT_EQ(0u, manager.NumTypes()); + + EXPECT_EQ(manager.begin(), manager.end()); +} + +TEST(TypeManager, BeginEnd) { + const std::string text = R"( + %void1 = OpTypeVoid + %void2 = OpTypeVoid + %bool = OpTypeBool + %u32 = OpTypeInt 32 0 + %f64 = OpTypeFloat 64 + )"; + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text); + TypeManager manager(nullptr, context.get()); + ASSERT_EQ(5u, manager.NumTypes()); + + EXPECT_NE(manager.begin(), manager.end()); + for (const auto& t : manager) { + switch (t.first) { + case 1: + case 2: + EXPECT_EQ("void", t.second->str()); + break; + case 3: + EXPECT_EQ("bool", t.second->str()); + break; + case 4: + EXPECT_EQ("uint32", t.second->str()); + break; + case 5: + EXPECT_EQ("float64", t.second->str()); + break; + default: + EXPECT_TRUE(false && "unreachable"); + break; + } + } +} + +TEST(TypeManager, LookupType) { + const std::string text = R"( +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%int = OpTypeInt 32 1 +%vec2 = OpTypeVector %int 2 +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(context, nullptr); + TypeManager manager(nullptr, context.get()); + + Void voidTy; + EXPECT_EQ(manager.GetId(&voidTy), 1u); + + Integer uintTy(32, false); + EXPECT_EQ(manager.GetId(&uintTy), 2u); + + Integer intTy(32, true); + EXPECT_EQ(manager.GetId(&intTy), 3u); + + Integer intTy2(32, true); + Vector vecTy(&intTy2, 2u); + EXPECT_EQ(manager.GetId(&vecTy), 4u); +} + +TEST(TypeManager, RemoveId) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 32 1 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + context->get_type_mgr()->RemoveId(1u); + ASSERT_EQ(context->get_type_mgr()->GetType(1u), nullptr); + ASSERT_NE(context->get_type_mgr()->GetType(2u), nullptr); + + context->get_type_mgr()->RemoveId(2u); + ASSERT_EQ(context->get_type_mgr()->GetType(1u), nullptr); + ASSERT_EQ(context->get_type_mgr()->GetType(2u), nullptr); +} + +TEST(TypeManager, RemoveIdNonDuplicateAmbiguousType) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + Integer u32(32, false); + Struct st({&u32}); + ASSERT_EQ(context->get_type_mgr()->GetId(&st), 2u); + context->get_type_mgr()->RemoveId(2u); + ASSERT_EQ(context->get_type_mgr()->GetType(2u), nullptr); + ASSERT_EQ(context->get_type_mgr()->GetId(&st), 0u); +} + +TEST(TypeManager, RemoveIdDuplicateAmbiguousType) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 +%3 = OpTypeStruct %1 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + Integer u32(32, false); + Struct st({&u32}); + uint32_t id = context->get_type_mgr()->GetId(&st); + ASSERT_NE(id, 0u); + uint32_t toRemove = id == 2u ? 2u : 3u; + uint32_t toStay = id == 2u ? 3u : 2u; + context->get_type_mgr()->RemoveId(toRemove); + ASSERT_EQ(context->get_type_mgr()->GetType(toRemove), nullptr); + ASSERT_EQ(context->get_type_mgr()->GetId(&st), toStay); +} + +TEST(TypeManager, RemoveIdDoesntUnmapOtherTypes) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 +%3 = OpTypeStruct %1 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + Integer u32(32, false); + Struct st({&u32}); + + EXPECT_EQ(1u, context->get_type_mgr()->GetId(&u32)); + uint32_t id = context->get_type_mgr()->GetId(&st); + ASSERT_NE(id, 0u); + uint32_t toRemove = id == 2u ? 3u : 2u; + uint32_t toStay = id == 2u ? 2u : 3u; + context->get_type_mgr()->RemoveId(toRemove); + ASSERT_EQ(context->get_type_mgr()->GetType(toRemove), nullptr); + ASSERT_EQ(context->get_type_mgr()->GetId(&st), toStay); +} + +TEST(TypeManager, GetTypeAndPointerType) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + Integer u32(32, false); + Pointer u32Ptr(&u32, SpvStorageClassFunction); + Struct st({&u32}); + Pointer stPtr(&st, SpvStorageClassInput); + + auto pair = context->get_type_mgr()->GetTypeAndPointerType( + 3u, SpvStorageClassFunction); + ASSERT_EQ(nullptr, pair.first); + ASSERT_EQ(nullptr, pair.second); + + pair = context->get_type_mgr()->GetTypeAndPointerType( + 1u, SpvStorageClassFunction); + ASSERT_TRUE(pair.first->IsSame(&u32)); + ASSERT_TRUE(pair.second->IsSame(&u32Ptr)); + + pair = + context->get_type_mgr()->GetTypeAndPointerType(2u, SpvStorageClassInput); + ASSERT_TRUE(pair.first->IsSame(&st)); + ASSERT_TRUE(pair.second->IsSame(&stPtr)); +} + +TEST(TypeManager, DuplicateType) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 32 0 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + const Type* type1 = context->get_type_mgr()->GetType(1u); + const Type* type2 = context->get_type_mgr()->GetType(2u); + EXPECT_NE(type1, nullptr); + EXPECT_NE(type2, nullptr); + EXPECT_EQ(*type1, *type2); +} + +TEST(TypeManager, MultipleStructs) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpDecorate %3 Constant +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 +%3 = OpTypeStruct %1 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + const Type* type1 = context->get_type_mgr()->GetType(2u); + const Type* type2 = context->get_type_mgr()->GetType(3u); + EXPECT_NE(type1, nullptr); + EXPECT_NE(type2, nullptr); + EXPECT_FALSE(type1->IsSame(type2)); +} + +TEST(TypeManager, RemovingIdAvoidsUseAfterFree) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + Integer u32(32, false); + Struct st({&u32}); + const Type* type = context->get_type_mgr()->GetType(2u); + EXPECT_NE(type, nullptr); + context->get_type_mgr()->RemoveId(1u); + EXPECT_TRUE(type->IsSame(&st)); +} + +TEST(TypeManager, RegisterAndRemoveId) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + uint32_t id = 2u; + { + // Ensure that u32 goes out of scope. + Integer u32(32, false); + Struct st({&u32}); + context->get_type_mgr()->RegisterType(id, st); + } + + context->get_type_mgr()->RemoveId(id); + EXPECT_EQ(nullptr, context->get_type_mgr()->GetType(id)); +} + +TEST(TypeManager, RegisterAndRemoveIdAllTypes) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + std::vector> types = GenerateAllTypes(); + uint32_t id = 1u; + for (auto& t : types) { + context->get_type_mgr()->RegisterType(id, *t); + EXPECT_EQ(*t, *context->get_type_mgr()->GetType(id)); + } + types.clear(); + + for (; id > 0; --id) { + context->get_type_mgr()->RemoveId(id); + EXPECT_EQ(nullptr, context->get_type_mgr()->GetType(id)); + } +} + +TEST(TypeManager, RegisterAndRemoveIdWithDecorations) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +)"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + uint32_t id = 2u; + { + Integer u32(32, false); + Struct st({&u32, &u32}); + st.AddDecoration({10}); + st.AddDecoration({11}); + st.AddMemberDecoration(0, {{35, 4}}); + st.AddMemberDecoration(1, {{35, 4}}); + st.AddMemberDecoration(1, {{36, 5}}); + context->get_type_mgr()->RegisterType(id, st); + EXPECT_EQ(st, *context->get_type_mgr()->GetType(id)); + } + + context->get_type_mgr()->RemoveId(id); + EXPECT_EQ(nullptr, context->get_type_mgr()->GetType(id)); +} + +TEST(TypeManager, GetTypeInstructionInt) { + const std::string text = R"( +; CHECK: OpTypeInt 32 0 +; CHECK: OpTypeInt 16 1 +OpCapability Shader +OpCapability Int16 +OpCapability Linkage +OpMemoryModel Logical GLSL450 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(context, nullptr); + + Integer uint_32(32, false); + context->get_type_mgr()->GetTypeInstruction(&uint_32); + + Integer int_16(16, true); + context->get_type_mgr()->GetTypeInstruction(&int_16); + + Match(text, context.get()); +} + +TEST(TypeManager, GetTypeInstructionDuplicateInts) { + const std::string text = R"( +; CHECK: OpTypeInt 32 0 +; CHECK-NOT: OpType +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + EXPECT_NE(context, nullptr); + + Integer uint_32(32, false); + uint32_t id = context->get_type_mgr()->GetTypeInstruction(&uint_32); + + Integer other(32, false); + EXPECT_EQ(context->get_type_mgr()->GetTypeInstruction(&other), id); + + Match(text, context.get()); +} + +TEST(TypeManager, GetTypeInstructionAllTypes) { + const std::string text = R"( +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[input_ptr:%\w+]] = OpTypePointer Input [[uint]] +; CHECK: [[uniform_ptr:%\w+]] = OpTypePointer Uniform [[uint]] +; CHECK: [[uint24:%\w+]] = OpConstant [[uint]] 24 +; CHECK: [[uint42:%\w+]] = OpConstant [[uint]] 42 +; CHECK: [[uint100:%\w+]] = OpConstant [[uint]] 100 +; CHECK: [[void:%\w+]] = OpTypeVoid +; CHECK: [[bool:%\w+]] = OpTypeBool +; CHECK: [[s32:%\w+]] = OpTypeInt 32 1 +; CHECK: OpTypeInt 64 1 +; CHECK: [[u64:%\w+]] = OpTypeInt 64 0 +; CHECK: [[f32:%\w+]] = OpTypeFloat 32 +; CHECK: OpTypeFloat 64 +; CHECK: OpTypeVector [[s32]] 2 +; CHECK: [[v3s32:%\w+]] = OpTypeVector [[s32]] 3 +; CHECK: OpTypeVector [[u64]] 4 +; CHECK: [[v3f32:%\w+]] = OpTypeVector [[f32]] 3 +; CHECK: OpTypeMatrix [[v3s32]] 3 +; CHECK: OpTypeMatrix [[v3s32]] 4 +; CHECK: OpTypeMatrix [[v3f32]] 4 +; CHECK: [[image1:%\w+]] = OpTypeImage [[s32]] 2D 0 0 0 0 Rg8 ReadOnly +; CHECK: OpTypeImage [[s32]] 2D 0 1 0 0 Rg8 ReadOnly +; CHECK: OpTypeImage [[s32]] 3D 0 1 0 0 Rg8 ReadOnly +; CHECK: [[image2:%\w+]] = OpTypeImage [[void]] 3D 0 1 0 1 Rg8 ReadWrite +; CHECK: OpTypeSampler +; CHECK: OpTypeSampledImage [[image1]] +; CHECK: OpTypeSampledImage [[image2]] +; CHECK: OpTypeArray [[f32]] [[uint100]] +; CHECK: [[a42f32:%\w+]] = OpTypeArray [[f32]] [[uint42]] +; CHECK: OpTypeArray [[u64]] [[uint24]] +; CHECK: OpTypeRuntimeArray [[v3f32]] +; CHECK: [[rav3s32:%\w+]] = OpTypeRuntimeArray [[v3s32]] +; CHECK: OpTypeStruct [[s32]] +; CHECK: [[sts32f32:%\w+]] = OpTypeStruct [[s32]] [[f32]] +; CHECK: OpTypeStruct [[u64]] [[a42f32]] [[rav3s32]] +; CHECK: OpTypeOpaque "" +; CHECK: OpTypeOpaque "hello" +; CHECK: OpTypeOpaque "world" +; CHECK: OpTypePointer Input [[f32]] +; CHECK: OpTypePointer Function [[sts32f32]] +; CHECK: OpTypePointer Function [[a42f32]] +; CHECK: OpTypeFunction [[void]] +; CHECK: OpTypeFunction [[void]] [[bool]] +; CHECK: OpTypeFunction [[void]] [[bool]] [[s32]] +; CHECK: OpTypeFunction [[s32]] [[bool]] [[s32]] +; CHECK: OpTypeEvent +; CHECK: OpTypeDeviceEvent +; CHECK: OpTypeReserveId +; CHECK: OpTypeQueue +; CHECK: OpTypePipe ReadWrite +; CHECK: OpTypePipe ReadOnly +; CHECK: OpTypeForwardPointer [[input_ptr]] Input +; CHECK: OpTypeForwardPointer [[uniform_ptr]] Input +; CHECK: OpTypeForwardPointer [[uniform_ptr]] Uniform +; CHECK: OpTypePipeStorage +; CHECK: OpTypeNamedBarrier +; CHECK: OpTypeAccelerationStructureKHR +; CHECK: OpTypeCooperativeMatrixNV [[f32]] [[uint24]] [[uint24]] [[uint24]] +OpCapability Shader +OpCapability Int64 +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%uint = OpTypeInt 32 0 +%1 = OpTypePointer Input %uint +%2 = OpTypePointer Uniform %uint +%24 = OpConstant %uint 24 +%42 = OpConstant %uint 42 +%100 = OpConstant %uint 100 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + std::vector> types = GenerateAllTypes(); + for (auto& t : types) { + context->get_type_mgr()->GetTypeInstruction(t.get()); + } + + Match(text, context.get(), false); +} + +TEST(TypeManager, GetTypeInstructionWithDecorations) { + const std::string text = R"( +; CHECK: OpDecorate [[struct:%\w+]] CPacked +; CHECK: OpMemberDecorate [[struct]] 1 Offset 4 +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[struct]] = OpTypeStruct [[uint]] [[uint]] +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%uint = OpTypeInt 32 0 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + Integer u32(32, false); + Struct st({&u32, &u32}); + st.AddDecoration({10}); + st.AddMemberDecoration(1, {{35, 4}}); + (void)context->get_def_use_mgr(); + context->get_type_mgr()->GetTypeInstruction(&st); + + Match(text, context.get()); +} + +TEST(TypeManager, GetPointerToAmbiguousType1) { + const std::string text = R"( +; CHECK: [[struct1:%\w+]] = OpTypeStruct +; CHECK: [[struct2:%\w+]] = OpTypeStruct +; CHECK: OpTypePointer Function [[struct2]] +; CHECK: OpTypePointer Function [[struct1]] +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%uint = OpTypeInt 32 0 +%1 = OpTypeStruct %uint +%2 = OpTypeStruct %uint +%3 = OpTypePointer Function %2 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + context->get_type_mgr()->FindPointerToType(1, SpvStorageClassFunction); + Match(text, context.get()); +} + +TEST(TypeManager, GetPointerToAmbiguousType2) { + const std::string text = R"( +; CHECK: [[struct1:%\w+]] = OpTypeStruct +; CHECK: [[struct2:%\w+]] = OpTypeStruct +; CHECK: OpTypePointer Function [[struct1]] +; CHECK: OpTypePointer Function [[struct2]] +OpCapability Shader +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%uint = OpTypeInt 32 0 +%1 = OpTypeStruct %uint +%2 = OpTypeStruct %uint +%3 = OpTypePointer Function %1 + )"; + + std::unique_ptr context = + BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + EXPECT_NE(context, nullptr); + + context->get_type_mgr()->FindPointerToType(2, SpvStorageClassFunction); + Match(text, context.get()); +} + +} // namespace +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/types_test.cpp b/third_party/spirv-tools/test/opt/types_test.cpp new file mode 100644 index 0000000..82e4040 --- /dev/null +++ b/third_party/spirv-tools/test/opt/types_test.cpp @@ -0,0 +1,389 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/types.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +// Fixture class providing some element types. +class SameTypeTest : public ::testing::Test { + protected: + void SetUp() override { + void_t_ = MakeUnique(); + u32_t_ = MakeUnique(32, false); + f64_t_ = MakeUnique(64); + v3u32_t_ = MakeUnique(u32_t_.get(), 3); + image_t_ = + MakeUnique(f64_t_.get(), SpvDim2D, 1, 1, 0, 0, SpvImageFormatR16, + SpvAccessQualifierReadWrite); + } + + // Element types to be used for constructing other types for testing. + std::unique_ptr void_t_; + std::unique_ptr u32_t_; + std::unique_ptr f64_t_; + std::unique_ptr v3u32_t_; + std::unique_ptr image_t_; +}; + +#define TestMultipleInstancesOfTheSameTypeQualified(ty, name, ...) \ + TEST_F(SameTypeTest, MultiSame##ty##name) { \ + std::vector> types; \ + for (int i = 0; i < 10; ++i) types.emplace_back(new ty(__VA_ARGS__)); \ + for (size_t i = 0; i < types.size(); ++i) { \ + for (size_t j = 0; j < types.size(); ++j) { \ + EXPECT_TRUE(types[i]->IsSame(types[j].get())) \ + << "expected '" << types[i]->str() << "' is the same as '" \ + << types[j]->str() << "'"; \ + EXPECT_TRUE(*types[i] == *types[j]) \ + << "expected '" << types[i]->str() << "' is the same as '" \ + << types[j]->str() << "'"; \ + } \ + } \ + } +#define TestMultipleInstancesOfTheSameType(ty, ...) \ + TestMultipleInstancesOfTheSameTypeQualified(ty, Simple, __VA_ARGS__) + +// clang-format off +TestMultipleInstancesOfTheSameType(Void) +TestMultipleInstancesOfTheSameType(Bool) +TestMultipleInstancesOfTheSameType(Integer, 32, true) +TestMultipleInstancesOfTheSameType(Float, 64) +TestMultipleInstancesOfTheSameType(Vector, u32_t_.get(), 3) +TestMultipleInstancesOfTheSameType(Matrix, v3u32_t_.get(), 4) +TestMultipleInstancesOfTheSameType(Image, f64_t_.get(), SpvDimCube, 0, 0, 1, 1, + SpvImageFormatRgb10A2, + SpvAccessQualifierWriteOnly) +TestMultipleInstancesOfTheSameType(Sampler) +TestMultipleInstancesOfTheSameType(SampledImage, image_t_.get()) +// There are three classes of arrays, based on the kinds of length information +// they have. +// 1. Array length is a constant or spec constant without spec ID, with literals +// for the constant value. +TestMultipleInstancesOfTheSameTypeQualified(Array, LenConstant, u32_t_.get(), + Array::LengthInfo{42, + { + 0, + 9999, + }}) +// 2. Array length is a spec constant with a given spec id. +TestMultipleInstancesOfTheSameTypeQualified(Array, LenSpecId, u32_t_.get(), + Array::LengthInfo{42, {1, 99}}) +// 3. Array length is an OpSpecConstantOp expression +TestMultipleInstancesOfTheSameTypeQualified(Array, LenDefiningId, u32_t_.get(), + Array::LengthInfo{42, {2, 42}}) + +TestMultipleInstancesOfTheSameType(RuntimeArray, u32_t_.get()) +TestMultipleInstancesOfTheSameType(Struct, std::vector{ + u32_t_.get(), f64_t_.get()}) +TestMultipleInstancesOfTheSameType(Opaque, "testing rocks") +TestMultipleInstancesOfTheSameType(Pointer, u32_t_.get(), SpvStorageClassInput) +TestMultipleInstancesOfTheSameType(Function, u32_t_.get(), + {f64_t_.get(), f64_t_.get()}) +TestMultipleInstancesOfTheSameType(Event) +TestMultipleInstancesOfTheSameType(DeviceEvent) +TestMultipleInstancesOfTheSameType(ReserveId) +TestMultipleInstancesOfTheSameType(Queue) +TestMultipleInstancesOfTheSameType(Pipe, SpvAccessQualifierReadWrite) +TestMultipleInstancesOfTheSameType(ForwardPointer, 10, SpvStorageClassUniform) +TestMultipleInstancesOfTheSameType(PipeStorage) +TestMultipleInstancesOfTheSameType(NamedBarrier) +TestMultipleInstancesOfTheSameType(AccelerationStructureNV) +#undef TestMultipleInstanceOfTheSameType +#undef TestMultipleInstanceOfTheSameTypeQual + +std::vector> GenerateAllTypes() { + // clang-format on + // Types in this test case are only equal to themselves, nothing else. + std::vector> types; + + // Forward Pointer + types.emplace_back(new ForwardPointer(10000, SpvStorageClassInput)); + types.emplace_back(new ForwardPointer(20000, SpvStorageClassInput)); + + // Void, Bool + types.emplace_back(new Void()); + auto* voidt = types.back().get(); + types.emplace_back(new Bool()); + auto* boolt = types.back().get(); + + // Integer + types.emplace_back(new Integer(32, true)); + auto* s32 = types.back().get(); + types.emplace_back(new Integer(32, false)); + types.emplace_back(new Integer(64, true)); + types.emplace_back(new Integer(64, false)); + auto* u64 = types.back().get(); + + // Float + types.emplace_back(new Float(32)); + auto* f32 = types.back().get(); + types.emplace_back(new Float(64)); + + // Vector + types.emplace_back(new Vector(s32, 2)); + types.emplace_back(new Vector(s32, 3)); + auto* v3s32 = types.back().get(); + types.emplace_back(new Vector(u64, 4)); + types.emplace_back(new Vector(f32, 3)); + auto* v3f32 = types.back().get(); + + // Matrix + types.emplace_back(new Matrix(v3s32, 3)); + types.emplace_back(new Matrix(v3s32, 4)); + types.emplace_back(new Matrix(v3f32, 4)); + + // Images + types.emplace_back(new Image(s32, SpvDim2D, 0, 0, 0, 0, SpvImageFormatRg8, + SpvAccessQualifierReadOnly)); + auto* image1 = types.back().get(); + types.emplace_back(new Image(s32, SpvDim2D, 0, 1, 0, 0, SpvImageFormatRg8, + SpvAccessQualifierReadOnly)); + types.emplace_back(new Image(s32, SpvDim3D, 0, 1, 0, 0, SpvImageFormatRg8, + SpvAccessQualifierReadOnly)); + types.emplace_back(new Image(voidt, SpvDim3D, 0, 1, 0, 1, SpvImageFormatRg8, + SpvAccessQualifierReadWrite)); + auto* image2 = types.back().get(); + + // Sampler + types.emplace_back(new Sampler()); + + // Sampled Image + types.emplace_back(new SampledImage(image1)); + types.emplace_back(new SampledImage(image2)); + + // Array + // Length is constant with integer bit representation of 42. + types.emplace_back(new Array(f32, Array::LengthInfo{99u, {0, 42u}})); + auto* a42f32 = types.back().get(); + // Differs from previous in length value only. + types.emplace_back(new Array(f32, Array::LengthInfo{99u, {0, 44u}})); + // Length is 64-bit constant integer value 42. + types.emplace_back(new Array(u64, Array::LengthInfo{100u, {0, 42u, 0u}})); + // Differs from previous in length value only. + types.emplace_back(new Array(u64, Array::LengthInfo{100u, {0, 44u, 0u}})); + + // Length is spec constant with spec id 18 and default value 44. + types.emplace_back(new Array(f32, Array::LengthInfo{99u, + { + 1, + 18u, + 44u, + }})); + // Differs from previous in spec id only. + types.emplace_back(new Array(f32, Array::LengthInfo{99u, {1, 19u, 44u}})); + // Differs from previous in literal value only. + types.emplace_back(new Array(f32, Array::LengthInfo{99u, {1, 19u, 48u}})); + // Length is spec constant op with id 42. + types.emplace_back(new Array(f32, Array::LengthInfo{42u, {2, 42}})); + // Differs from previous in result id only. + types.emplace_back(new Array(f32, Array::LengthInfo{43u, {2, 43}})); + + // RuntimeArray + types.emplace_back(new RuntimeArray(v3f32)); + types.emplace_back(new RuntimeArray(v3s32)); + auto* rav3s32 = types.back().get(); + + // Struct + types.emplace_back(new Struct(std::vector{s32})); + types.emplace_back(new Struct(std::vector{s32, f32})); + auto* sts32f32 = types.back().get(); + types.emplace_back( + new Struct(std::vector{u64, a42f32, rav3s32})); + + // Opaque + types.emplace_back(new Opaque("")); + types.emplace_back(new Opaque("hello")); + types.emplace_back(new Opaque("world")); + + // Pointer + types.emplace_back(new Pointer(f32, SpvStorageClassInput)); + types.emplace_back(new Pointer(sts32f32, SpvStorageClassFunction)); + types.emplace_back(new Pointer(a42f32, SpvStorageClassFunction)); + types.emplace_back(new Pointer(voidt, SpvStorageClassFunction)); + + // Function + types.emplace_back(new Function(voidt, {})); + types.emplace_back(new Function(voidt, {boolt})); + types.emplace_back(new Function(voidt, {boolt, s32})); + types.emplace_back(new Function(s32, {boolt, s32})); + + // Event, Device Event, Reserve Id, Queue, + types.emplace_back(new Event()); + types.emplace_back(new DeviceEvent()); + types.emplace_back(new ReserveId()); + types.emplace_back(new Queue()); + + // Pipe, Forward Pointer, PipeStorage, NamedBarrier + types.emplace_back(new Pipe(SpvAccessQualifierReadWrite)); + types.emplace_back(new Pipe(SpvAccessQualifierReadOnly)); + types.emplace_back(new ForwardPointer(1, SpvStorageClassInput)); + types.emplace_back(new ForwardPointer(2, SpvStorageClassInput)); + types.emplace_back(new ForwardPointer(2, SpvStorageClassUniform)); + types.emplace_back(new PipeStorage()); + types.emplace_back(new NamedBarrier()); + + return types; +} + +TEST(Types, AllTypes) { + // Types in this test case are only equal to themselves, nothing else. + std::vector> types = GenerateAllTypes(); + + for (size_t i = 0; i < types.size(); ++i) { + for (size_t j = 0; j < types.size(); ++j) { + if (i == j) { + EXPECT_TRUE(types[i]->IsSame(types[j].get())) + << "expected '" << types[i]->str() << "' is the same as '" + << types[j]->str() << "'"; + } else { + EXPECT_FALSE(types[i]->IsSame(types[j].get())) + << "entry (" << i << "," << j << ") expected '" << types[i]->str() + << "' is different to '" << types[j]->str() << "'"; + } + } + } +} + +TEST(Types, IntSignedness) { + std::vector signednesses = {true, false, false, true}; + std::vector> types; + for (bool s : signednesses) { + types.emplace_back(new Integer(32, s)); + } + for (size_t i = 0; i < signednesses.size(); i++) { + EXPECT_EQ(signednesses[i], types[i]->IsSigned()); + } +} + +TEST(Types, IntWidth) { + std::vector widths = {1, 2, 4, 8, 16, 32, 48, 64, 128}; + std::vector> types; + for (uint32_t w : widths) { + types.emplace_back(new Integer(w, true)); + } + for (size_t i = 0; i < widths.size(); i++) { + EXPECT_EQ(widths[i], types[i]->width()); + } +} + +TEST(Types, FloatWidth) { + std::vector widths = {1, 2, 4, 8, 16, 32, 48, 64, 128}; + std::vector> types; + for (uint32_t w : widths) { + types.emplace_back(new Float(w)); + } + for (size_t i = 0; i < widths.size(); i++) { + EXPECT_EQ(widths[i], types[i]->width()); + } +} + +TEST(Types, VectorElementCount) { + auto s32 = MakeUnique(32, true); + for (uint32_t c : {2, 3, 4}) { + auto s32v = MakeUnique(s32.get(), c); + EXPECT_EQ(c, s32v->element_count()); + } +} + +TEST(Types, MatrixElementCount) { + auto s32 = MakeUnique(32, true); + auto s32v4 = MakeUnique(s32.get(), 4); + for (uint32_t c : {1, 2, 3, 4, 10, 100}) { + auto s32m = MakeUnique(s32v4.get(), c); + EXPECT_EQ(c, s32m->element_count()); + } +} + +TEST(Types, IsUniqueType) { + std::vector> types = GenerateAllTypes(); + + for (auto& t : types) { + bool expectation = true; + // Disallowing variable pointers. + switch (t->kind()) { + case Type::kArray: + case Type::kRuntimeArray: + case Type::kStruct: + expectation = false; + break; + default: + break; + } + EXPECT_EQ(t->IsUniqueType(false), expectation) + << "expected '" << t->str() << "' to be a " + << (expectation ? "" : "non-") << "unique type"; + + // Allowing variables pointers. + if (t->AsPointer()) expectation = false; + EXPECT_EQ(t->IsUniqueType(true), expectation) + << "expected '" << t->str() << "' to be a " + << (expectation ? "" : "non-") << "unique type"; + } +} + +std::vector> GenerateAllTypesWithDecorations() { + std::vector> types = GenerateAllTypes(); + uint32_t elems = 1; + uint32_t decs = 1; + for (auto& t : types) { + for (uint32_t i = 0; i < (decs % 10); ++i) { + std::vector decoration; + for (uint32_t j = 0; j < (elems % 4) + 1; ++j) { + decoration.push_back(j); + } + t->AddDecoration(std::move(decoration)); + ++elems; + ++decs; + } + } + + return types; +} + +TEST(Types, Clone) { + std::vector> types = GenerateAllTypesWithDecorations(); + for (auto& t : types) { + auto clone = t->Clone(); + EXPECT_TRUE(*t == *clone); + EXPECT_TRUE(t->HasSameDecorations(clone.get())); + EXPECT_NE(clone.get(), t.get()); + } +} + +TEST(Types, RemoveDecorations) { + std::vector> types = GenerateAllTypesWithDecorations(); + for (auto& t : types) { + auto decorationless = t->RemoveDecorations(); + EXPECT_EQ(*t == *decorationless, t->decoration_empty()); + EXPECT_EQ(t->HasSameDecorations(decorationless.get()), + t->decoration_empty()); + EXPECT_NE(t.get(), decorationless.get()); + } +} + +} // namespace +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/unify_const_test.cpp b/third_party/spirv-tools/test/opt/unify_const_test.cpp new file mode 100644 index 0000000..6ed2173 --- /dev/null +++ b/third_party/spirv-tools/test/opt/unify_const_test.cpp @@ -0,0 +1,996 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +// Returns the types defining instructions commonly used in many tests. +std::vector CommonTypes() { + return std::vector{ + // clang-format off + // scalar types + "%bool = OpTypeBool", + "%uint = OpTypeInt 32 0", + "%int = OpTypeInt 32 1", + "%uint64 = OpTypeInt 64 0", + "%int64 = OpTypeInt 64 1", + "%float = OpTypeFloat 32", + "%double = OpTypeFloat 64", + // vector types + "%v2bool = OpTypeVector %bool 2", + "%v2uint = OpTypeVector %uint 2", + "%v2int = OpTypeVector %int 2", + "%v3int = OpTypeVector %int 3", + "%v4int = OpTypeVector %int 4", + "%v2float = OpTypeVector %float 2", + "%v3float = OpTypeVector %float 3", + "%v2double = OpTypeVector %double 2", + // struct types + "%inner_struct = OpTypeStruct %bool %float", + "%outer_struct = OpTypeStruct %inner_struct %int %double", + "%flat_struct = OpTypeStruct %bool %int %float %double", + // variable pointer types + "%_pf_bool = OpTypePointer Function %bool", + "%_pf_uint = OpTypePointer Function %uint", + "%_pf_int = OpTypePointer Function %int", + "%_pf_uint64 = OpTypePointer Function %uint64", + "%_pf_int64 = OpTypePointer Function %int64", + "%_pf_float = OpTypePointer Function %float", + "%_pf_double = OpTypePointer Function %double", + "%_pf_v2int = OpTypePointer Function %v2int", + "%_pf_v3int = OpTypePointer Function %v3int", + "%_pf_v4int = OpTypePointer Function %v4int", + "%_pf_v2float = OpTypePointer Function %v2float", + "%_pf_v3float = OpTypePointer Function %v3float", + "%_pf_v2double = OpTypePointer Function %v2double", + "%_pf_inner_struct = OpTypePointer Function %inner_struct", + "%_pf_outer_struct = OpTypePointer Function %outer_struct", + "%_pf_flat_struct = OpTypePointer Function %flat_struct", + // clang-format on + }; +} + +// A helper function to strip OpName instructions from the given string of +// disassembly code and put those debug instructions to a set. Returns the +// string with all OpName instruction stripped and a set of OpName +// instructions. +std::tuple> +StripOpNameInstructionsToSet(const std::string& str) { + std::stringstream ss(str); + std::ostringstream oss; + std::string inst_str; + std::unordered_set opname_instructions; + while (std::getline(ss, inst_str, '\n')) { + if (inst_str.find("OpName %") == std::string::npos) { + oss << inst_str << '\n'; + } else { + opname_instructions.insert(inst_str); + } + } + return std::make_tuple(oss.str(), std::move(opname_instructions)); +} + +// The test fixture for all tests of UnifyConstantPass. This fixture defines +// the rule of checking: all the optimized code should be exactly the same as +// the expected code, except the OpName instructions, which can be different in +// order. +template +class UnifyConstantTest : public PassTest { + protected: + // Runs UnifyConstantPass on the code built from the given |test_builder|, + // and checks whether the optimization result matches with the code built + // from |expected_builder|. + void Check(const AssemblyBuilder& expected_builder, + const AssemblyBuilder& test_builder) { + // unoptimized code + const std::string original_before_strip = test_builder.GetCode(); + std::string original_without_opnames; + std::unordered_set original_opnames; + std::tie(original_without_opnames, original_opnames) = + StripOpNameInstructionsToSet(original_before_strip); + + // expected code + std::string expected_without_opnames; + std::unordered_set expected_opnames; + std::tie(expected_without_opnames, expected_opnames) = + StripOpNameInstructionsToSet(expected_builder.GetCode()); + + // optimized code + std::string optimized_before_strip; + auto status = Pass::Status::SuccessWithoutChange; + std::tie(optimized_before_strip, status) = + this->template SinglePassRunAndDisassemble( + test_builder.GetCode(), + /* skip_nop = */ true, /* do_validation = */ false); + std::string optimized_without_opnames; + std::unordered_set optimized_opnames; + std::tie(optimized_without_opnames, optimized_opnames) = + StripOpNameInstructionsToSet(optimized_before_strip); + + // Flag "status" should be returned correctly. + EXPECT_NE(Pass::Status::Failure, status); + EXPECT_EQ(expected_without_opnames == original_without_opnames, + status == Pass::Status::SuccessWithoutChange); + // Code except OpName instructions should be exactly the same. + EXPECT_EQ(expected_without_opnames, optimized_without_opnames); + // OpName instructions can be in different order, but the content must be + // the same. + EXPECT_EQ(expected_opnames, optimized_opnames); + } +}; + +using UnifyFrontEndConstantSingleTest = + UnifyConstantTest>; + +TEST_F(UnifyFrontEndConstantSingleTest, Basic) { + AssemblyBuilder test_builder; + AssemblyBuilder expected_builder; + + test_builder + .AppendTypesConstantsGlobals({ + "%uint = OpTypeInt 32 0", "%_pf_uint = OpTypePointer Function %uint", + "%unsigned_1 = OpConstant %uint 1", + "%unsigned_1_duplicate = OpConstant %uint 1", // duplicated constant + }) + .AppendInMain({ + "%uint_var = OpVariable %_pf_uint Function", + "OpStore %uint_var %unsigned_1_duplicate", + }); + + expected_builder + .AppendTypesConstantsGlobals({ + "%uint = OpTypeInt 32 0", + "%_pf_uint = OpTypePointer Function %uint", + "%unsigned_1 = OpConstant %uint 1", + }) + .AppendInMain({ + "%uint_var = OpVariable %_pf_uint Function", + "OpStore %uint_var %unsigned_1", + }) + .AppendNames({ + "OpName %unsigned_1 \"unsigned_1_duplicate\"", // the OpName + // instruction of the + // removed duplicated + // constant won't be + // erased. + }); + Check(expected_builder, test_builder); +} + +TEST_F(UnifyFrontEndConstantSingleTest, SkipWhenResultIdHasDecorations) { + AssemblyBuilder test_builder; + AssemblyBuilder expected_builder; + + test_builder + .AppendAnnotations({ + // So far we don't have valid decorations for constants. This is + // preparing for the future updates of SPIR-V. + // TODO(qining): change to a valid decoration once they are available. + "OpDecorate %f_1 RelaxedPrecision", + "OpDecorate %f_2_dup RelaxedPrecision", + }) + .AppendTypesConstantsGlobals({ + // clang-format off + "%float = OpTypeFloat 32", + "%_pf_float = OpTypePointer Function %float", + "%f_1 = OpConstant %float 1", + // %f_1 has decoration, so %f_1 will not be used to replace %f_1_dup. + "%f_1_dup = OpConstant %float 1", + "%f_2 = OpConstant %float 2", + // %_2_dup has decoration, so %f_2 will not replace %f_2_dup. + "%f_2_dup = OpConstant %float 2", + // no decoration for %f_3 or %f_3_dup, %f_3_dup should be replaced. + "%f_3 = OpConstant %float 3", + "%f_3_dup = OpConstant %float 3", + // clang-format on + }) + .AppendInMain({ + // clang-format off + "%f_var = OpVariable %_pf_float Function", + "OpStore %f_var %f_1_dup", + "OpStore %f_var %f_2_dup", + "OpStore %f_var %f_3_dup", + // clang-format on + }); + + expected_builder + .AppendAnnotations({ + "OpDecorate %f_1 RelaxedPrecision", + "OpDecorate %f_2_dup RelaxedPrecision", + }) + .AppendTypesConstantsGlobals({ + // clang-format off + "%float = OpTypeFloat 32", + "%_pf_float = OpTypePointer Function %float", + "%f_1 = OpConstant %float 1", + "%f_1_dup = OpConstant %float 1", + "%f_2 = OpConstant %float 2", + "%f_2_dup = OpConstant %float 2", + "%f_3 = OpConstant %float 3", + // clang-format on + }) + .AppendInMain({ + // clang-format off + "%f_var = OpVariable %_pf_float Function", + "OpStore %f_var %f_1_dup", + "OpStore %f_var %f_2_dup", + "OpStore %f_var %f_3", + // clang-format on + }) + .AppendNames({ + "OpName %f_3 \"f_3_dup\"", + }); + + Check(expected_builder, test_builder); +} + +TEST_F(UnifyFrontEndConstantSingleTest, UnifyWithDecorationOnTypes) { + AssemblyBuilder test_builder; + AssemblyBuilder expected_builder; + + test_builder + .AppendAnnotations({ + "OpMemberDecorate %flat_d 1 RelaxedPrecision", + }) + .AppendTypesConstantsGlobals({ + // clang-format off + "%int = OpTypeInt 32 1", + "%float = OpTypeFloat 32", + "%flat = OpTypeStruct %int %float", + "%_pf_flat = OpTypePointer Function %flat", + // decorated flat struct + "%flat_d = OpTypeStruct %int %float", + "%_pf_flat_d = OpTypePointer Function %flat_d", + // perserved contants. %flat_1 and %flat_d has same members, but + // their type are different in decorations, so they should not be + // used to replace each other. + "%int_1 = OpConstant %int 1", + "%float_1 = OpConstant %float 1", + "%flat_1 = OpConstantComposite %flat %int_1 %float_1", + "%flat_d_1 = OpConstantComposite %flat_d %int_1 %float_1", + // duplicated constants. + "%flat_1_dup = OpConstantComposite %flat %int_1 %float_1", + "%flat_d_1_dup = OpConstantComposite %flat_d %int_1 %float_1", + // clang-format on + }) + .AppendInMain({ + "%flat_var = OpVariable %_pf_flat Function", + "OpStore %flat_var %flat_1_dup", + "%flat_d_var = OpVariable %_pf_flat_d Function", + "OpStore %flat_d_var %flat_d_1_dup", + }); + + expected_builder + .AppendAnnotations({ + "OpMemberDecorate %flat_d 1 RelaxedPrecision", + }) + .AppendTypesConstantsGlobals({ + // clang-format off + "%int = OpTypeInt 32 1", + "%float = OpTypeFloat 32", + "%flat = OpTypeStruct %int %float", + "%_pf_flat = OpTypePointer Function %flat", + // decorated flat struct + "%flat_d = OpTypeStruct %int %float", + "%_pf_flat_d = OpTypePointer Function %flat_d", + "%int_1 = OpConstant %int 1", + "%float_1 = OpConstant %float 1", + "%flat_1 = OpConstantComposite %flat %int_1 %float_1", + "%flat_d_1 = OpConstantComposite %flat_d %int_1 %float_1", + // clang-format on + }) + .AppendInMain({ + "%flat_var = OpVariable %_pf_flat Function", + "OpStore %flat_var %flat_1", + "%flat_d_var = OpVariable %_pf_flat_d Function", + "OpStore %flat_d_var %flat_d_1", + }) + .AppendNames({ + "OpName %flat_1 \"flat_1_dup\"", + "OpName %flat_d_1 \"flat_d_1_dup\"", + }); + + Check(expected_builder, test_builder); +} + +struct UnifyConstantTestCase { + // preserved constants. + std::vector preserved_consts; + // expected uses of the preserved constants. + std::vector use_preserved_consts; + // duplicated constants of the preserved constants. + std::vector duplicate_consts; + // uses of the duplicated constants, expected to be updated to use the + // preserved constants. + std::vector use_duplicate_consts; + // The updated OpName instructions that originally refer to duplicated + // constants. + std::vector remapped_names; +}; + +using UnifyFrontEndConstantParamTest = UnifyConstantTest< + PassTest<::testing::TestWithParam>>; + +TEST_P(UnifyFrontEndConstantParamTest, TestCase) { + auto& tc = GetParam(); + AssemblyBuilder test_builder; + AssemblyBuilder expected_builder; + test_builder.AppendTypesConstantsGlobals(CommonTypes()); + expected_builder.AppendTypesConstantsGlobals(CommonTypes()); + + test_builder.AppendTypesConstantsGlobals(tc.preserved_consts) + .AppendTypesConstantsGlobals(tc.duplicate_consts) + .AppendInMain(tc.use_duplicate_consts); + + // Duplicated constants are killed in the expected output, and the debug + // instructions attached to those duplicated instructions will be migrated to + // the corresponding preserved constants. + expected_builder.AppendTypesConstantsGlobals(tc.preserved_consts) + .AppendInMain(tc.use_preserved_consts) + .AppendNames(tc.remapped_names); + + Check(expected_builder, test_builder); +} + +INSTANTIATE_TEST_SUITE_P( + Case, UnifyFrontEndConstantParamTest, + :: + testing:: + ValuesIn( + std:: + vector( + { + // clang-format off + // basic tests for scalar constants + { + // preserved constants + { + "%bool_true = OpConstantTrue %bool", + "%signed_1 = OpConstant %int 1", + "%signed_minus_1 = OpConstant %int64 -1", + "%unsigned_max = OpConstant %uint64 18446744073709551615", + "%float_1 = OpConstant %float 1", + "%double_1 = OpConstant %double 1", + }, + // use preserved constants in main + { + "%bool_var = OpVariable %_pf_bool Function", + "OpStore %bool_var %bool_true", + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %signed_1", + "%int64_var = OpVariable %_pf_int64 Function", + "OpStore %int64_var %signed_minus_1", + "%uint64_var = OpVariable %_pf_uint64 Function", + "OpStore %uint64_var %unsigned_max", + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %float_1", + "%double_var = OpVariable %_pf_double Function", + "OpStore %double_var %double_1", + }, + // duplicated constants + { + "%bool_true_duplicate = OpConstantTrue %bool", + "%signed_1_duplicate = OpConstant %int 1", + "%signed_minus_1_duplicate = OpConstant %int64 -1", + "%unsigned_max_duplicate = OpConstant %uint64 18446744073709551615", + "%float_1_duplicate = OpConstant %float 1", + "%double_1_duplicate = OpConstant %double 1", + }, + // use duplicated constants in main + { + "%bool_var = OpVariable %_pf_bool Function", + "OpStore %bool_var %bool_true_duplicate", + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %signed_1_duplicate", + "%int64_var = OpVariable %_pf_int64 Function", + "OpStore %int64_var %signed_minus_1_duplicate", + "%uint64_var = OpVariable %_pf_uint64 Function", + "OpStore %uint64_var %unsigned_max_duplicate", + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %float_1_duplicate", + "%double_var = OpVariable %_pf_double Function", + "OpStore %double_var %double_1_duplicate", + }, + // remapped names + { + "OpName %bool_true \"bool_true_duplicate\"", + "OpName %signed_1 \"signed_1_duplicate\"", + "OpName %signed_minus_1 \"signed_minus_1_duplicate\"", + "OpName %unsigned_max \"unsigned_max_duplicate\"", + "OpName %float_1 \"float_1_duplicate\"", + "OpName %double_1 \"double_1_duplicate\"", + }, + }, + // NaN in different bit patterns should not be unified, but the ones + // using same bit pattern should be unified. + { + // preserved constants + { + "%float_nan_1 = OpConstant %float 0x1.8p+128", // !2143289344, 7FC00000 + "%float_nan_2 = OpConstant %float 0x1.800002p+128",// !2143289345 7FC00001 + }, + // use preserved constants in main + { + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %float_nan_1", + "OpStore %float_var %float_nan_2", + }, + // duplicated constants + { + "%float_nan_1_duplicate = OpConstant %float 0x1.8p+128", // !2143289344, 7FC00000 + "%float_nan_2_duplicate = OpConstant %float 0x1.800002p+128",// !2143289345, 7FC00001 + }, + // use duplicated constants in main + { + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %float_nan_1_duplicate", + "OpStore %float_var %float_nan_2_duplicate", + }, + // remapped names + { + "OpName %float_nan_1 \"float_nan_1_duplicate\"", + "OpName %float_nan_2 \"float_nan_2_duplicate\"", + }, + }, + // null values + { + // preserved constants + { + "%bool_null = OpConstantNull %bool", + "%signed_null = OpConstantNull %int", + "%signed_64_null = OpConstantNull %int64", + "%float_null = OpConstantNull %float", + "%double_null = OpConstantNull %double", + // zero-valued constants will not be unified with the equivalent + // null constants. + "%signed_zero = OpConstant %int 0", + }, + // use preserved constants in main + { + "%bool_var = OpVariable %_pf_bool Function", + "OpStore %bool_var %bool_null", + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %signed_null", + "%int64_var = OpVariable %_pf_int64 Function", + "OpStore %int64_var %signed_64_null", + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %float_null", + "%double_var = OpVariable %_pf_double Function", + "OpStore %double_var %double_null", + }, + // duplicated constants + { + "%bool_null_duplicate = OpConstantNull %bool", + "%signed_null_duplicate = OpConstantNull %int", + "%signed_64_null_duplicate = OpConstantNull %int64", + "%float_null_duplicate = OpConstantNull %float", + "%double_null_duplicate = OpConstantNull %double", + }, + // use duplicated constants in main + { + "%bool_var = OpVariable %_pf_bool Function", + "OpStore %bool_var %bool_null_duplicate", + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %signed_null_duplicate", + "%int64_var = OpVariable %_pf_int64 Function", + "OpStore %int64_var %signed_64_null_duplicate", + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %float_null_duplicate", + "%double_var = OpVariable %_pf_double Function", + "OpStore %double_var %double_null_duplicate", + }, + // remapped names + { + "OpName %bool_null \"bool_null_duplicate\"", + "OpName %signed_null \"signed_null_duplicate\"", + "OpName %signed_64_null \"signed_64_null_duplicate\"", + "OpName %float_null \"float_null_duplicate\"", + "OpName %double_null \"double_null_duplicate\"", + }, + }, + // constant sampler + { + // preserved constants + { + "%sampler = OpTypeSampler", + "%_pf_sampler = OpTypePointer Function %sampler", + "%sampler_1 = OpConstantSampler %sampler Repeat 0 Linear", + }, + // use preserved constants in main + { + "%sampler_var = OpVariable %_pf_sampler Function", + "OpStore %sampler_var %sampler_1", + }, + // duplicated constants + { + "%sampler_1_duplicate = OpConstantSampler %sampler Repeat 0 Linear", + }, + // use duplicated constants in main + { + "%sampler_var = OpVariable %_pf_sampler Function", + "OpStore %sampler_var %sampler_1_duplicate", + }, + // remapped names + { + "OpName %sampler_1 \"sampler_1_duplicate\"", + }, + }, + // duplicate vector built from same ids. + { + // preserved constants + { + "%signed_1 = OpConstant %int 1", + "%signed_2 = OpConstant %int 2", + "%signed_3 = OpConstant %int 3", + "%signed_4 = OpConstant %int 4", + "%vec = OpConstantComposite %v4int %signed_1 %signed_2 %signed_3 %signed_4", + }, + // use preserved constants in main + { + "%vec_var = OpVariable %_pf_v4int Function", + "OpStore %vec_var %vec", + }, + // duplicated constants + { + "%vec_duplicate = OpConstantComposite %v4int %signed_1 %signed_2 %signed_3 %signed_4", + }, + // use duplicated constants in main + { + "%vec_var = OpVariable %_pf_v4int Function", + "OpStore %vec_var %vec_duplicate", + }, + // remapped names + { + "OpName %vec \"vec_duplicate\"", + } + }, + // duplicate vector built from duplicated ids. + { + // preserved constants + { + "%signed_1 = OpConstant %int 1", + "%signed_2 = OpConstant %int 2", + "%signed_3 = OpConstant %int 3", + "%signed_4 = OpConstant %int 4", + "%vec = OpConstantComposite %v4int %signed_1 %signed_2 %signed_3 %signed_4", + }, + // use preserved constants in main + { + "%vec_var = OpVariable %_pf_v4int Function", + "OpStore %vec_var %vec", + }, + // duplicated constants + { + "%signed_3_duplicate = OpConstant %int 3", + "%signed_4_duplicate = OpConstant %int 4", + "%vec_duplicate = OpConstantComposite %v4int %signed_1 %signed_2 %signed_3_duplicate %signed_4_duplicate", + }, + // use duplicated constants in main + { + "%vec_var = OpVariable %_pf_v4int Function", + "OpStore %vec_var %vec_duplicate", + }, + // remapped names + { + "OpName %signed_3 \"signed_3_duplicate\"", + "OpName %signed_4 \"signed_4_duplicate\"", + "OpName %vec \"vec_duplicate\"", + }, + }, + // flat struct + { + // preserved constants + { + "%bool_true = OpConstantTrue %bool", + "%signed_1 = OpConstant %int 1", + "%float_1 = OpConstant %float 1", + "%double_1 = OpConstant %double 1", + "%s = OpConstantComposite %flat_struct %bool_true %signed_1 %float_1 %double_1", + }, + // use preserved constants in main + { + "%s_var = OpVariable %_pf_flat_struct Function", + "OpStore %s_var %s", + }, + // duplicated constants + { + "%float_1_duplicate = OpConstant %float 1", + "%double_1_duplicate = OpConstant %double 1", + "%s_duplicate = OpConstantComposite %flat_struct %bool_true %signed_1 %float_1_duplicate %double_1_duplicate", + }, + // use duplicated constants in main + { + "%s_var = OpVariable %_pf_flat_struct Function", + "OpStore %s_var %s_duplicate", + }, + // remapped names + { + "OpName %float_1 \"float_1_duplicate\"", + "OpName %double_1 \"double_1_duplicate\"", + "OpName %s \"s_duplicate\"", + }, + }, + // nested struct + { + // preserved constants + { + "%bool_true = OpConstantTrue %bool", + "%signed_1 = OpConstant %int 1", + "%float_1 = OpConstant %float 1", + "%double_1 = OpConstant %double 1", + "%inner = OpConstantComposite %inner_struct %bool_true %float_1", + "%outer = OpConstantComposite %outer_struct %inner %signed_1 %double_1", + }, + // use preserved constants in main + { + "%outer_var = OpVariable %_pf_outer_struct Function", + "OpStore %outer_var %outer", + }, + // duplicated constants + { + "%float_1_duplicate = OpConstant %float 1", + "%double_1_duplicate = OpConstant %double 1", + "%inner_duplicate = OpConstantComposite %inner_struct %bool_true %float_1_duplicate", + "%outer_duplicate = OpConstantComposite %outer_struct %inner_duplicate %signed_1 %double_1_duplicate", + }, + // use duplicated constants in main + { + "%outer_var = OpVariable %_pf_outer_struct Function", + "OpStore %outer_var %outer_duplicate", + }, + // remapped names + { + "OpName %float_1 \"float_1_duplicate\"", + "OpName %double_1 \"double_1_duplicate\"", + "OpName %inner \"inner_duplicate\"", + "OpName %outer \"outer_duplicate\"", + }, + }, + // composite type null constants. Null constants and zero-valued + // constants should not be used to replace each other. + { + // preserved constants + { + "%bool_zero = OpConstantFalse %bool", + "%float_zero = OpConstant %float 0", + "%int_null = OpConstantNull %int", + "%double_null = OpConstantNull %double", + // inner_struct type null constant. + "%null_inner = OpConstantNull %inner_struct", + // zero-valued composite constant built from zero-valued constant + // component. inner_zero should not be replace by null_inner. + "%inner_zero = OpConstantComposite %inner_struct %bool_zero %float_zero", + // zero-valued composite contant built from zero-valued constants + // and null constants. + "%outer_zero = OpConstantComposite %outer_struct %inner_zero %int_null %double_null", + // outer_struct type null constant, it should not be replaced by + // outer_zero. + "%null_outer = OpConstantNull %outer_struct", + }, + // use preserved constants in main + { + "%inner_var = OpVariable %_pf_inner_struct Function", + "OpStore %inner_var %inner_zero", + "OpStore %inner_var %null_inner", + "%outer_var = OpVariable %_pf_outer_struct Function", + "OpStore %outer_var %outer_zero", + "OpStore %outer_var %null_outer", + }, + // duplicated constants + { + "%null_inner_dup = OpConstantNull %inner_struct", + "%null_outer_dup = OpConstantNull %outer_struct", + "%inner_zero_dup = OpConstantComposite %inner_struct %bool_zero %float_zero", + "%outer_zero_dup = OpConstantComposite %outer_struct %inner_zero_dup %int_null %double_null", + }, + // use duplicated constants in main + { + "%inner_var = OpVariable %_pf_inner_struct Function", + "OpStore %inner_var %inner_zero_dup", + "OpStore %inner_var %null_inner_dup", + "%outer_var = OpVariable %_pf_outer_struct Function", + "OpStore %outer_var %outer_zero_dup", + "OpStore %outer_var %null_outer_dup", + }, + // remapped names + { + "OpName %null_inner \"null_inner_dup\"", + "OpName %null_outer \"null_outer_dup\"", + "OpName %inner_zero \"inner_zero_dup\"", + "OpName %outer_zero \"outer_zero_dup\"", + }, + }, + // Spec Constants with SpecId decoration should be skipped. + { + // preserved constants + { + // Assembly builder will add OpDecorate SpecId instruction for the + // following spec constant instructions automatically. + "%spec_bool_1 = OpSpecConstantTrue %bool", + "%spec_bool_2 = OpSpecConstantTrue %bool", + "%spec_int_1 = OpSpecConstant %int 1", + "%spec_int_2 = OpSpecConstant %int 1", + }, + // use preserved constants in main + { + "%bool_var = OpVariable %_pf_bool Function", + "OpStore %bool_var %spec_bool_1", + "OpStore %bool_var %spec_bool_2", + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %spec_int_1", + "OpStore %int_var %spec_int_2", + }, + // duplicated constants. No duplicated instruction to remove in this + // case. + {}, + // use duplicated constants in main. Same as the above 'use preserved + // constants in main' defined above, as no instruction should be + // removed in this case. + { + "%bool_var = OpVariable %_pf_bool Function", + "OpStore %bool_var %spec_bool_1", + "OpStore %bool_var %spec_bool_2", + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %spec_int_1", + "OpStore %int_var %spec_int_2", + }, + // remapped names. No duplicated instruction removed, so this is + // empty. + {} + }, + // spec constant composite + { + // preserved constants + { + "%spec_bool_true = OpSpecConstantTrue %bool", + "%spec_signed_1 = OpSpecConstant %int 1", + "%float_1 = OpConstant %float 1", + "%double_1 = OpConstant %double 1", + "%spec_inner = OpSpecConstantComposite %inner_struct %spec_bool_true %float_1", + "%spec_outer = OpSpecConstantComposite %outer_struct %spec_inner %spec_signed_1 %double_1", + "%spec_vec2 = OpSpecConstantComposite %v2float %float_1 %float_1", + }, + // use preserved constants in main + { + "%outer_var = OpVariable %_pf_outer_struct Function", + "OpStore %outer_var %spec_outer", + "%v2float_var = OpVariable %_pf_v2float Function", + "OpStore %v2float_var %spec_vec2", + }, + // duplicated constants + { + "%float_1_duplicate = OpConstant %float 1", + "%double_1_duplicate = OpConstant %double 1", + "%spec_inner_duplicate = OpSpecConstantComposite %inner_struct %spec_bool_true %float_1_duplicate", + "%spec_outer_duplicate = OpSpecConstantComposite %outer_struct %spec_inner_duplicate %spec_signed_1 %double_1_duplicate", + "%spec_vec2_duplicate = OpSpecConstantComposite %v2float %float_1 %float_1_duplicate", + }, + // use duplicated constants in main + { + "%outer_var = OpVariable %_pf_outer_struct Function", + "OpStore %outer_var %spec_outer_duplicate", + "%v2float_var = OpVariable %_pf_v2float Function", + "OpStore %v2float_var %spec_vec2_duplicate", + }, + // remapped names + { + "OpName %float_1 \"float_1_duplicate\"", + "OpName %double_1 \"double_1_duplicate\"", + "OpName %spec_inner \"spec_inner_duplicate\"", + "OpName %spec_outer \"spec_outer_duplicate\"", + "OpName %spec_vec2 \"spec_vec2_duplicate\"", + }, + }, + // spec constant op with int scalar + { + // preserved constants + { + "%spec_signed_1 = OpSpecConstant %int 1", + "%spec_signed_2 = OpSpecConstant %int 2", + "%spec_signed_add = OpSpecConstantOp %int IAdd %spec_signed_1 %spec_signed_2", + }, + // use preserved constants in main + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %spec_signed_add", + }, + // duplicated constants + { + "%spec_signed_add_duplicate = OpSpecConstantOp %int IAdd %spec_signed_1 %spec_signed_2", + }, + // use duplicated contants in main + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %spec_signed_add_duplicate", + }, + // remapped names + { + "OpName %spec_signed_add \"spec_signed_add_duplicate\"", + }, + }, + // spec constant op composite extract + { + // preserved constants + { + "%float_1 = OpConstant %float 1", + "%spec_vec2 = OpSpecConstantComposite %v2float %float_1 %float_1", + "%spec_extract = OpSpecConstantOp %float CompositeExtract %spec_vec2 1", + }, + // use preserved constants in main + { + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %spec_extract", + }, + // duplicated constants + { + "%spec_extract_duplicate = OpSpecConstantOp %float CompositeExtract %spec_vec2 1", + }, + // use duplicated constants in main + { + "%float_var = OpVariable %_pf_float Function", + "OpStore %float_var %spec_extract_duplicate", + }, + // remapped names + { + "OpName %spec_extract \"spec_extract_duplicate\"", + }, + }, + // spec constant op vector shuffle + { + // preserved constants + { + "%float_1 = OpConstant %float 1", + "%float_2 = OpConstant %float 2", + "%spec_vec2_1 = OpSpecConstantComposite %v2float %float_1 %float_1", + "%spec_vec2_2 = OpSpecConstantComposite %v2float %float_2 %float_2", + "%spec_vector_shuffle = OpSpecConstantOp %v2float VectorShuffle %spec_vec2_1 %spec_vec2_2 1 2", + }, + // use preserved constants in main + { + "%v2float_var = OpVariable %_pf_v2float Function", + "OpStore %v2float_var %spec_vector_shuffle", + }, + // duplicated constants + { + "%spec_vector_shuffle_duplicate = OpSpecConstantOp %v2float VectorShuffle %spec_vec2_1 %spec_vec2_2 1 2", + }, + // use duplicated constants in main + { + "%v2float_var = OpVariable %_pf_v2float Function", + "OpStore %v2float_var %spec_vector_shuffle_duplicate", + }, + // remapped names + { + "OpName %spec_vector_shuffle \"spec_vector_shuffle_duplicate\"", + }, + }, + // long dependency chain + { + // preserved constants + { + "%array_size = OpConstant %int 4", + "%type_arr_int_4 = OpTypeArray %int %array_size", + "%signed_0 = OpConstant %int 100", + "%signed_1 = OpConstant %int 1", + "%signed_2 = OpSpecConstantOp %int IAdd %signed_0 %signed_1", + "%signed_3 = OpSpecConstantOp %int ISub %signed_0 %signed_2", + "%signed_4 = OpSpecConstantOp %int IAdd %signed_0 %signed_3", + "%signed_5 = OpSpecConstantOp %int ISub %signed_0 %signed_4", + "%signed_6 = OpSpecConstantOp %int IAdd %signed_0 %signed_5", + "%signed_7 = OpSpecConstantOp %int ISub %signed_0 %signed_6", + "%signed_8 = OpSpecConstantOp %int IAdd %signed_0 %signed_7", + "%signed_9 = OpSpecConstantOp %int ISub %signed_0 %signed_8", + "%signed_10 = OpSpecConstantOp %int IAdd %signed_0 %signed_9", + "%signed_11 = OpSpecConstantOp %int ISub %signed_0 %signed_10", + "%signed_12 = OpSpecConstantOp %int IAdd %signed_0 %signed_11", + "%signed_13 = OpSpecConstantOp %int ISub %signed_0 %signed_12", + "%signed_14 = OpSpecConstantOp %int IAdd %signed_0 %signed_13", + "%signed_15 = OpSpecConstantOp %int ISub %signed_0 %signed_14", + "%signed_16 = OpSpecConstantOp %int ISub %signed_0 %signed_15", + "%signed_17 = OpSpecConstantOp %int IAdd %signed_0 %signed_16", + "%signed_18 = OpSpecConstantOp %int ISub %signed_0 %signed_17", + "%signed_19 = OpSpecConstantOp %int IAdd %signed_0 %signed_18", + "%signed_20 = OpSpecConstantOp %int ISub %signed_0 %signed_19", + "%signed_vec_a = OpSpecConstantComposite %v2int %signed_18 %signed_19", + "%signed_vec_b = OpSpecConstantOp %v2int IMul %signed_vec_a %signed_vec_a", + "%signed_21 = OpSpecConstantOp %int CompositeExtract %signed_vec_b 0", + "%signed_array = OpConstantComposite %type_arr_int_4 %signed_20 %signed_20 %signed_21 %signed_21", + "%signed_22 = OpSpecConstantOp %int CompositeExtract %signed_array 0", + }, + // use preserved constants in main + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %signed_22", + }, + // duplicated constants + { + "%signed_0_dup = OpConstant %int 100", + "%signed_1_dup = OpConstant %int 1", + "%signed_2_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_1_dup", + "%signed_3_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_2_dup", + "%signed_4_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_3_dup", + "%signed_5_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_4_dup", + "%signed_6_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_5_dup", + "%signed_7_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_6_dup", + "%signed_8_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_7_dup", + "%signed_9_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_8_dup", + "%signed_10_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_9_dup", + "%signed_11_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_10_dup", + "%signed_12_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_11_dup", + "%signed_13_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_12_dup", + "%signed_14_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_13_dup", + "%signed_15_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_14_dup", + "%signed_16_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_15_dup", + "%signed_17_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_16_dup", + "%signed_18_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_17_dup", + "%signed_19_dup = OpSpecConstantOp %int IAdd %signed_0_dup %signed_18_dup", + "%signed_20_dup = OpSpecConstantOp %int ISub %signed_0_dup %signed_19_dup", + "%signed_vec_a_dup = OpSpecConstantComposite %v2int %signed_18_dup %signed_19_dup", + "%signed_vec_b_dup = OpSpecConstantOp %v2int IMul %signed_vec_a_dup %signed_vec_a_dup", + "%signed_21_dup = OpSpecConstantOp %int CompositeExtract %signed_vec_b_dup 0", + "%signed_array_dup = OpConstantComposite %type_arr_int_4 %signed_20_dup %signed_20_dup %signed_21_dup %signed_21_dup", + "%signed_22_dup = OpSpecConstantOp %int CompositeExtract %signed_array_dup 0", + }, + // use duplicated constants in main + { + "%int_var = OpVariable %_pf_int Function", + "OpStore %int_var %signed_22_dup", + }, + // remapped names + { + "OpName %signed_0 \"signed_0_dup\"", + "OpName %signed_1 \"signed_1_dup\"", + "OpName %signed_2 \"signed_2_dup\"", + "OpName %signed_3 \"signed_3_dup\"", + "OpName %signed_4 \"signed_4_dup\"", + "OpName %signed_5 \"signed_5_dup\"", + "OpName %signed_6 \"signed_6_dup\"", + "OpName %signed_7 \"signed_7_dup\"", + "OpName %signed_8 \"signed_8_dup\"", + "OpName %signed_9 \"signed_9_dup\"", + "OpName %signed_10 \"signed_10_dup\"", + "OpName %signed_11 \"signed_11_dup\"", + "OpName %signed_12 \"signed_12_dup\"", + "OpName %signed_13 \"signed_13_dup\"", + "OpName %signed_14 \"signed_14_dup\"", + "OpName %signed_15 \"signed_15_dup\"", + "OpName %signed_16 \"signed_16_dup\"", + "OpName %signed_17 \"signed_17_dup\"", + "OpName %signed_18 \"signed_18_dup\"", + "OpName %signed_19 \"signed_19_dup\"", + "OpName %signed_20 \"signed_20_dup\"", + "OpName %signed_vec_a \"signed_vec_a_dup\"", + "OpName %signed_vec_b \"signed_vec_b_dup\"", + "OpName %signed_21 \"signed_21_dup\"", + "OpName %signed_array \"signed_array_dup\"", + "OpName %signed_22 \"signed_22_dup\"", + }, + }, + // clang-format on + }))); + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/upgrade_memory_model_test.cpp b/third_party/spirv-tools/test/opt/upgrade_memory_model_test.cpp new file mode 100644 index 0000000..7f64ffd --- /dev/null +++ b/third_party/spirv-tools/test/opt/upgrade_memory_model_test.cpp @@ -0,0 +1,2272 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "assembly_builder.h" +#include "gmock/gmock.h" +#include "pass_fixture.h" +#include "pass_utils.h" + +namespace { + +using namespace spvtools; + +using UpgradeMemoryModelTest = opt::PassTest<::testing::Test>; + +TEST_F(UpgradeMemoryModelTest, InvalidMemoryModelOpenCL) { + const std::string text = R"( +; CHECK: OpMemoryModel Logical OpenCL +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, InvalidMemoryModelVulkan) { + const std::string text = R"( +; CHECK: OpMemoryModel Logical Vulkan +OpCapability Shader +OpCapability Linkage +OpCapability VulkanMemoryModel +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical Vulkan +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, JustMemoryModel) { + const std::string text = R"( +; CHECK: OpCapability VulkanMemoryModel +; CHECK: OpExtension "SPV_KHR_vulkan_memory_model" +; CHECK: OpMemoryModel Logical Vulkan +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, RemoveDecorations) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var Volatile +OpDecorate %var Coherent +%int = OpTypeInt 32 0 +%ptr_int_Uniform = OpTypePointer Uniform %int +%var = OpVariable %ptr_int_Uniform Uniform +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, WorkgroupVariable) { + const std::string text = R"( +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_Workgroup Workgroup +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %int %var +OpStore %var %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, WorkgroupFunctionParameter) { + const std::string text = R"( +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Workgroup = OpTypePointer Workgroup %int +%func_ty = OpTypeFunction %void %ptr_int_Workgroup +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_int_Workgroup +%1 = OpLabel +%ld = OpLoad %int %param +OpStore %param %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SimpleUniformVariable) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var Coherent +OpDecorate %var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Uniform = OpTypePointer Uniform %int +%var = OpVariable %ptr_int_Uniform Uniform +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %int %var +OpStore %var %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SimpleUniformFunctionParameter) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %param Coherent +OpDecorate %param Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Uniform = OpTypePointer Uniform %int +%func_ty = OpTypeFunction %void %ptr_int_Uniform +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_int_Uniform +%1 = OpLabel +%ld = OpLoad %int %param +OpStore %param %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SimpleUniformVariableOnlyVolatile) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK-NOT: OpConstant +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Uniform = OpTypePointer Uniform %int +%var = OpVariable %ptr_int_Uniform Uniform +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %int %var +OpStore %var %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SimpleUniformVariableCopied) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var Coherent +OpDecorate %var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Uniform = OpTypePointer Uniform %int +%var = OpVariable %ptr_int_Uniform Uniform +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%copy = OpCopyObject %ptr_int_Uniform %var +%ld = OpLoad %int %copy +OpStore %copy %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SimpleUniformFunctionParameterCopied) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %param Coherent +OpDecorate %param Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Uniform = OpTypePointer Uniform %int +%func_ty = OpTypeFunction %void %ptr_int_Uniform +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_int_Uniform +%1 = OpLabel +%copy = OpCopyObject %ptr_int_Uniform %param +%ld = OpLoad %int %copy +%copy2 = OpCopyObject %ptr_int_Uniform %param +OpStore %copy2 %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SimpleUniformVariableAccessChain) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %var Coherent +OpDecorate %var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int3 = OpConstant %int 3 +%int_array_3 = OpTypeArray %int %int3 +%ptr_intarray_Uniform = OpTypePointer Uniform %int_array_3 +%ptr_int_Uniform = OpTypePointer Uniform %int +%var = OpVariable %ptr_intarray_Uniform Uniform +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%gep = OpAccessChain %ptr_int_Uniform %var %int0 +%ld = OpLoad %int %gep +OpStore %gep %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SimpleUniformFunctionParameterAccessChain) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %param Coherent +OpDecorate %param Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int3 = OpConstant %int 3 +%int_array_3 = OpTypeArray %int %int3 +%ptr_intarray_Uniform = OpTypePointer Uniform %int_array_3 +%ptr_int_Uniform = OpTypePointer Uniform %int +%func_ty = OpTypeFunction %void %ptr_intarray_Uniform +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_intarray_Uniform +%1 = OpLabel +%ld_gep = OpAccessChain %ptr_int_Uniform %param %int0 +%ld = OpLoad %int %ld_gep +%st_gep = OpAccessChain %ptr_int_Uniform %param %int0 +OpStore %st_gep %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerSelect) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %var Coherent +OpDecorate %var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%null = OpConstantNull %ptr_int_StorageBuffer +%var = OpVariable %ptr_int_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%select = OpSelect %ptr_int_StorageBuffer %true %var %null +%ld = OpLoad %int %select +OpStore %var %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerSelectConservative) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %var1 Coherent +OpDecorate %var2 Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%var1 = OpVariable %ptr_int_StorageBuffer StorageBuffer +%var2 = OpVariable %ptr_int_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%select = OpSelect %ptr_int_StorageBuffer %true %var1 %var2 +%ld = OpLoad %int %select +OpStore %select %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VariablePointerIncrement) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{%\w+}} Coherent +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +OpDecorate %param Coherent +OpDecorate %param ArrayStride 4 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int1 = OpConstant %int 1 +%int10 = OpConstant %int 10 +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_int_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_int_StorageBuffer +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +%phi = OpPhi %ptr_int_StorageBuffer %param %1 %ptr_next %2 +%iv = OpPhi %int %int0 %1 %inc %2 +%inc = OpIAdd %int %iv %int1 +%ptr_next = OpPtrAccessChain %ptr_int_StorageBuffer %phi %int1 +%cmp = OpIEqual %bool %iv %int10 +OpLoopMerge %3 %2 None +OpBranchConditional %cmp %3 %2 +%3 = OpLabel +%ld = OpLoad %int %phi +OpStore %phi %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentStructElement) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%struct = OpTypeStruct %int +%ptr_struct_StorageBuffer = OpTypePointer StorageBuffer %struct +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_struct_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_struct_StorageBuffer +%1 = OpLabel +%gep = OpAccessChain %ptr_int_StorageBuffer %param %int0 +%ld = OpLoad %int %gep +OpStore %gep %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentElementFullStructAccess) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%ptr_struct_StorageBuffer = OpTypePointer StorageBuffer %struct +%func_ty = OpTypeFunction %void %ptr_struct_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_struct_StorageBuffer +%1 = OpLabel +%ld = OpLoad %struct %param +OpStore %param %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentElementNotAccessed) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK-NOT: MakePointerAvailable +; CHECK-NOT: NonPrivatePointer +; CHECK-NOT: MakePointerVisible +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 1 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%struct = OpTypeStruct %int %int +%ptr_struct_StorageBuffer = OpTypePointer StorageBuffer %struct +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_struct_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_struct_StorageBuffer +%1 = OpLabel +%gep = OpAccessChain %ptr_int_StorageBuffer %param %int0 +%ld = OpLoad %int %gep +OpStore %gep %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, MultiIndexAccessCoherent) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %inner 1 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int1 = OpConstant %int 1 +%inner = OpTypeStruct %int %int +%middle = OpTypeStruct %inner +%outer = OpTypeStruct %middle %middle +%ptr_outer_StorageBuffer = OpTypePointer StorageBuffer %outer +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_outer_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_outer_StorageBuffer +%1 = OpLabel +%ld_gep = OpInBoundsAccessChain %ptr_int_StorageBuffer %param %int0 %int0 %int1 +%ld = OpLoad %int %ld_gep +%st_gep = OpInBoundsAccessChain %ptr_int_StorageBuffer %param %int1 %int0 %int1 +OpStore %st_gep %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, MultiIndexAccessNonCoherent) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK-NOT: MakePointerAvailable +; CHECK-NOT: NonPrivatePointer +; CHECK-NOT: MakePointerVisible +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %inner 1 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int1 = OpConstant %int 1 +%inner = OpTypeStruct %int %int +%middle = OpTypeStruct %inner +%outer = OpTypeStruct %middle %middle +%ptr_outer_StorageBuffer = OpTypePointer StorageBuffer %outer +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_outer_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_outer_StorageBuffer +%1 = OpLabel +%ld_gep = OpInBoundsAccessChain %ptr_int_StorageBuffer %param %int0 %int0 %int0 +%ld = OpLoad %int %ld_gep +%st_gep = OpInBoundsAccessChain %ptr_int_StorageBuffer %param %int1 %int0 %int0 +OpStore %st_gep %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, ConsecutiveAccessChainCoherent) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %inner 1 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int1 = OpConstant %int 1 +%inner = OpTypeStruct %int %int +%middle = OpTypeStruct %inner +%outer = OpTypeStruct %middle %middle +%ptr_outer_StorageBuffer = OpTypePointer StorageBuffer %outer +%ptr_middle_StorageBuffer = OpTypePointer StorageBuffer %middle +%ptr_inner_StorageBuffer = OpTypePointer StorageBuffer %inner +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_outer_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_outer_StorageBuffer +%1 = OpLabel +%ld_gep1 = OpInBoundsAccessChain %ptr_middle_StorageBuffer %param %int0 +%ld_gep2 = OpInBoundsAccessChain %ptr_inner_StorageBuffer %ld_gep1 %int0 +%ld_gep3 = OpInBoundsAccessChain %ptr_int_StorageBuffer %ld_gep2 %int1 +%ld = OpLoad %int %ld_gep3 +%st_gep1 = OpInBoundsAccessChain %ptr_middle_StorageBuffer %param %int1 +%st_gep2 = OpInBoundsAccessChain %ptr_inner_StorageBuffer %st_gep1 %int0 +%st_gep3 = OpInBoundsAccessChain %ptr_int_StorageBuffer %st_gep2 %int1 +OpStore %st_gep3 %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, ConsecutiveAccessChainNonCoherent) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK-NOT: MakePointerAvailable +; CHECK-NOT: NonPrivatePointer +; CHECK-NOT: MakePointerVisible +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %inner 1 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int1 = OpConstant %int 1 +%inner = OpTypeStruct %int %int +%middle = OpTypeStruct %inner +%outer = OpTypeStruct %middle %middle +%ptr_outer_StorageBuffer = OpTypePointer StorageBuffer %outer +%ptr_middle_StorageBuffer = OpTypePointer StorageBuffer %middle +%ptr_inner_StorageBuffer = OpTypePointer StorageBuffer %inner +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_outer_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_outer_StorageBuffer +%1 = OpLabel +%ld_gep1 = OpInBoundsAccessChain %ptr_middle_StorageBuffer %param %int0 +%ld_gep2 = OpInBoundsAccessChain %ptr_inner_StorageBuffer %ld_gep1 %int0 +%ld_gep3 = OpInBoundsAccessChain %ptr_int_StorageBuffer %ld_gep2 %int0 +%ld = OpLoad %int %ld_gep3 +%st_gep1 = OpInBoundsAccessChain %ptr_middle_StorageBuffer %param %int1 +%st_gep2 = OpInBoundsAccessChain %ptr_inner_StorageBuffer %st_gep1 %int0 +%st_gep3 = OpInBoundsAccessChain %ptr_int_StorageBuffer %st_gep2 %int0 +OpStore %st_gep3 %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentStructElementAccess) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %middle 0 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int1 = OpConstant %int 1 +%inner = OpTypeStruct %int %int +%middle = OpTypeStruct %inner +%outer = OpTypeStruct %middle %middle +%ptr_outer_StorageBuffer = OpTypePointer StorageBuffer %outer +%ptr_middle_StorageBuffer = OpTypePointer StorageBuffer %middle +%ptr_inner_StorageBuffer = OpTypePointer StorageBuffer %inner +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_outer_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_outer_StorageBuffer +%1 = OpLabel +%ld_gep1 = OpInBoundsAccessChain %ptr_middle_StorageBuffer %param %int0 +%ld_gep2 = OpInBoundsAccessChain %ptr_inner_StorageBuffer %ld_gep1 %int0 +%ld_gep3 = OpInBoundsAccessChain %ptr_int_StorageBuffer %ld_gep2 %int1 +%ld = OpLoad %int %ld_gep3 +%st_gep1 = OpInBoundsAccessChain %ptr_middle_StorageBuffer %param %int1 +%st_gep2 = OpInBoundsAccessChain %ptr_inner_StorageBuffer %st_gep1 %int0 +%st_gep3 = OpInBoundsAccessChain %ptr_int_StorageBuffer %st_gep2 %int1 +OpStore %st_gep3 %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, NonCoherentLoadCoherentStore) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK-NOT: MakePointerVisible +; CHECK: OpStore {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpMemberDecorate %outer 1 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%int1 = OpConstant %int 1 +%inner = OpTypeStruct %int %int +%middle = OpTypeStruct %inner +%outer = OpTypeStruct %middle %middle +%ptr_outer_StorageBuffer = OpTypePointer StorageBuffer %outer +%ptr_middle_StorageBuffer = OpTypePointer StorageBuffer %middle +%ptr_inner_StorageBuffer = OpTypePointer StorageBuffer %inner +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_outer_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_outer_StorageBuffer +%1 = OpLabel +%ld_gep1 = OpInBoundsAccessChain %ptr_middle_StorageBuffer %param %int0 +%ld_gep2 = OpInBoundsAccessChain %ptr_inner_StorageBuffer %ld_gep1 %int0 +%ld_gep3 = OpInBoundsAccessChain %ptr_int_StorageBuffer %ld_gep2 %int1 +%ld = OpLoad %int %ld_gep3 +%st_gep1 = OpInBoundsAccessChain %ptr_middle_StorageBuffer %param %int1 +%st_gep2 = OpInBoundsAccessChain %ptr_inner_StorageBuffer %st_gep1 %int0 +%st_gep3 = OpInBoundsAccessChain %ptr_int_StorageBuffer %st_gep2 %int1 +OpStore %st_gep3 %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CopyMemory) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile|MakePointerVisible|NonPrivatePointer [[queuefamily]] +; CHECK-NOT: [[queuefamily]] +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %in_var Coherent +OpDecorate %out_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%in_var = OpVariable %ptr_int_StorageBuffer StorageBuffer +%out_var = OpVariable %ptr_int_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpCopyMemory %out_var %in_var +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CopyMemorySized) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpCopyMemorySized {{%\w+}} {{%\w+}} {{%\w+}} Volatile|MakePointerAvailable|NonPrivatePointer [[queuefamily]] +; CHECK-NOT: [[queuefamily]] +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %out_param Coherent +OpDecorate %in_param Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int4 = OpConstant %int 4 +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%func_ty = OpTypeFunction %void %ptr_int_StorageBuffer %ptr_int_StorageBuffer +%func = OpFunction %void None %func_ty +%in_param = OpFunctionParameter %ptr_int_StorageBuffer +%out_param = OpFunctionParameter %ptr_int_StorageBuffer +%1 = OpLabel +OpCopyMemorySized %out_param %in_param %int4 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CopyMemoryTwoScopes) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK-DAG: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK-DAG: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailable|MakePointerVisible|NonPrivatePointer [[workgroup]] [[queuefamily]] +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %in_var Coherent +OpDecorate %out_var Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Workgroup = OpTypePointer Workgroup %int +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%in_var = OpVariable %ptr_int_StorageBuffer StorageBuffer +%out_var = OpVariable %ptr_int_Workgroup Workgroup +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpCopyMemory %out_var %in_var +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileImageRead) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile +; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexel +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageReadWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%int0 = OpConstant %int 0 +%v2int_0 = OpConstantComposite %v2int %int0 %int0 +%image = OpTypeImage %float 2D 0 0 0 2 Unknown +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%var = OpVariable %ptr_image_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %image %var +%rd = OpImageRead %float %ld %v2int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentImageRead) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisible|NonPrivateTexel [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageReadWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %var Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%int0 = OpConstant %int 0 +%v2int_0 = OpConstantComposite %v2int %int0 %int0 +%image = OpTypeImage %float 2D 0 0 0 2 Unknown +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%var = OpVariable %ptr_image_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %image %var +%rd = OpImageRead %float %ld %v2int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentImageReadExtractedFromSampledImage) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[image:%\w+]] = OpTypeImage +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad [[image]] {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK-NOT: NonPrivatePointer +; CHECK: OpImageRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisible|NonPrivateTexel [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageReadWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %var Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%int0 = OpConstant %int 0 +%v2int_0 = OpConstantComposite %v2int %int0 %int0 +%image = OpTypeImage %float 2D 0 0 0 0 Unknown +%sampled_image = OpTypeSampledImage %image +%sampler = OpTypeSampler +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%ptr_sampler_StorageBuffer = OpTypePointer StorageBuffer %sampler +%var = OpVariable %ptr_image_StorageBuffer StorageBuffer +%sampler_var = OpVariable %ptr_sampler_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %image %var +%ld_sampler = OpLoad %sampler %sampler_var +%sample = OpSampledImage %sampled_image %ld %ld_sampler +%extract = OpImage %image %sample +%rd = OpImageRead %float %extract %v2int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileImageWrite) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile +; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexel +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageWriteWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %param Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%float0 = OpConstant %float 0 +%v2int_null = OpConstantNull %v2int +%image = OpTypeImage %float 2D 0 0 0 0 Unknown +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%func_ty = OpTypeFunction %void %ptr_image_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_image_StorageBuffer +%1 = OpLabel +%ld = OpLoad %image %param +OpImageWrite %ld %v2int_null %float0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentImageWrite) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer +; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailable|NonPrivateTexel [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageWriteWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %param Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%float0 = OpConstant %float 0 +%v2int_null = OpConstantNull %v2int +%image = OpTypeImage %float 2D 0 0 0 0 Unknown +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%func_ty = OpTypeFunction %void %ptr_image_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_image_StorageBuffer +%1 = OpLabel +%ld = OpLoad %image %param +OpImageWrite %ld %v2int_null %float0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentImageWriteExtractFromSampledImage) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer +; CHECK-NOT: NonPrivatePointer +; CHECK: OpImageWrite {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelAvailable|NonPrivateTexel [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageWriteWithoutFormat +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %param Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%float0 = OpConstant %float 0 +%v2int_null = OpConstantNull %v2int +%image = OpTypeImage %float 2D 0 0 0 0 Unknown +%sampled_image = OpTypeSampledImage %image +%sampler = OpTypeSampler +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%ptr_sampler_StorageBuffer = OpTypePointer StorageBuffer %sampler +%func_ty = OpTypeFunction %void %ptr_image_StorageBuffer %ptr_sampler_StorageBuffer +%func = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr_image_StorageBuffer +%sampler_param = OpFunctionParameter %ptr_sampler_StorageBuffer +%1 = OpLabel +%ld = OpLoad %image %param +%ld_sampler = OpLoad %sampler %sampler_param +%sample = OpSampledImage %sampled_image %ld %ld_sampler +%extract = OpImage %image %sample +OpImageWrite %extract %v2int_null %float0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileImageSparseRead) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: OpLoad {{%\w+}} {{%\w+}} Volatile +; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} VolatileTexel +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageReadWithoutFormat +OpCapability SparseResidency +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%int0 = OpConstant %int 0 +%v2int_0 = OpConstantComposite %v2int %int0 %int0 +%image = OpTypeImage %float 2D 0 0 0 2 Unknown +%struct = OpTypeStruct %int %float +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%var = OpVariable %ptr_image_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %image %var +%rd = OpImageSparseRead %struct %ld %v2int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentImageSparseRead) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad {{%\w+}} {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisible|NonPrivateTexel [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageReadWithoutFormat +OpCapability SparseResidency +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %var Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%int0 = OpConstant %int 0 +%v2int_0 = OpConstantComposite %v2int %int0 %int0 +%image = OpTypeImage %float 2D 0 0 0 2 Unknown +%struct = OpTypeStruct %int %float +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%var = OpVariable %ptr_image_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %image %var +%rd = OpImageSparseRead %struct %ld %v2int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, + CoherentImageSparseReadExtractedFromSampledImage) { + const std::string text = R"( +; CHECK-NOT: OpDecorate +; CHECK: [[image:%\w+]] = OpTypeImage +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpLoad [[image]] {{%\w+}} MakePointerVisible|NonPrivatePointer [[scope]] +; CHECK-NOT: NonPrivatePointer +; CHECK: OpImageSparseRead {{%\w+}} {{%\w+}} {{%\w+}} MakeTexelVisible|NonPrivateTexel [[scope]] +OpCapability Shader +OpCapability Linkage +OpCapability StorageImageReadWithoutFormat +OpCapability SparseResidency +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpDecorate %var Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%v2int = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%int0 = OpConstant %int 0 +%v2int_0 = OpConstantComposite %v2int %int0 %int0 +%image = OpTypeImage %float 2D 0 0 0 0 Unknown +%struct = OpTypeStruct %int %float +%sampled_image = OpTypeSampledImage %image +%sampler = OpTypeSampler +%ptr_image_StorageBuffer = OpTypePointer StorageBuffer %image +%ptr_sampler_StorageBuffer = OpTypePointer StorageBuffer %sampler +%var = OpVariable %ptr_image_StorageBuffer StorageBuffer +%sampler_var = OpVariable %ptr_sampler_StorageBuffer StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %image %var +%ld_sampler = OpLoad %sampler %sampler_var +%sample = OpSampledImage %sampled_image %ld %ld_sampler +%extract = OpImage %image %sample +%rd = OpImageSparseRead %struct %extract %v2int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierNoChange) { + const std::string text = R"( +; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0 +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[none]] +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %func "func" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%workgroup = OpConstant %int 2 +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpControlBarrier %workgroup %workgroup %none +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutput) { + const std::string text = R"( +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096 +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]] +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %func "func" %var +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%workgroup = OpConstant %int 2 +%ptr_int_Output = OpTypePointer Output %int +%var = OpVariable %ptr_int_Output Output +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %int %var +OpControlBarrier %workgroup %workgroup %none +OpStore %var %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, TessellationMemoryBarrierNoChange) { + const std::string text = R"( +; CHECK: [[none:%\w+]] = OpConstant {{%\w+}} 0 +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpMemoryBarrier [[workgroup]] [[none]] +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %func "func" %var +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%workgroup = OpConstant %int 2 +%ptr_int_Output = OpTypePointer Output %int +%var = OpVariable %ptr_int_Output Output +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %int %var +OpMemoryBarrier %workgroup %none +OpStore %var %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, TessellationControlBarrierAddOutputSubFunction) { + const std::string text = R"( +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096 +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]] +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %func "func" %var +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%workgroup = OpConstant %int 2 +%ptr_int_Output = OpTypePointer Output %int +%var = OpVariable %ptr_int_Output Output +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%call = OpFunctionCall %void %sub_func +OpReturn +OpFunctionEnd +%sub_func = OpFunction %void None %func_ty +%2 = OpLabel +%ld = OpLoad %int %var +OpControlBarrier %workgroup %workgroup %none +OpStore %var %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, + TessellationControlBarrierAddOutputDifferentFunctions) { + const std::string text = R"( +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: [[output:%\w+]] = OpConstant {{%\w+}} 4096 +; CHECK: OpControlBarrier [[workgroup]] [[workgroup]] [[output]] +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %func "func" %var +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%workgroup = OpConstant %int 2 +%ptr_int_Output = OpTypePointer Output %int +%var = OpVariable %ptr_int_Output Output +%func_ty = OpTypeFunction %void +%ld_func_ty = OpTypeFunction %int +%st_func_ty = OpTypeFunction %void %int +%func = OpFunction %void None %func_ty +%1 = OpLabel +%call_ld = OpFunctionCall %int %ld_func +%call_barrier = OpFunctionCall %void %barrier_func +%call_st = OpFunctionCall %void %st_func %call_ld +OpReturn +OpFunctionEnd +%ld_func = OpFunction %int None %ld_func_ty +%2 = OpLabel +%ld = OpLoad %int %var +OpReturnValue %ld +OpFunctionEnd +%barrier_func = OpFunction %void None %func_ty +%3 = OpLabel +OpControlBarrier %workgroup %workgroup %none +OpReturn +OpFunctionEnd +%st_func = OpFunction %void None %st_func_ty +%param = OpFunctionParameter %int +%4 = OpLabel +OpStore %var %param +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, ChangeControlBarrierMemoryScope) { + std::string text = R"( +; CHECK: [[workgroup:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpControlBarrier [[workgroup]] [[queuefamily]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpControlBarrier %workgroup %device %none +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, ChangeMemoryBarrierMemoryScope) { + std::string text = R"( +; CHECK: [[queuefamily:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpMemoryBarrier [[queuefamily]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%device = OpConstant %int 1 +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpMemoryBarrier %device %none +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, ChangeAtomicMemoryScope) { + std::string text = R"( +; CHECK: [[int:%\w+]] = OpTypeInt +; CHECK: [[var:%\w+]] = OpVariable +; CHECK: [[qf:%\w+]] = OpConstant [[int]] 5 +; CHECK: OpAtomicLoad [[int]] [[var]] [[qf]] +; CHECK: OpAtomicStore [[var]] [[qf]] +; CHECK: OpAtomicExchange [[int]] [[var]] [[qf]] +; CHECK: OpAtomicCompareExchange [[int]] [[var]] [[qf]] +; CHECK: OpAtomicIIncrement [[int]] [[var]] [[qf]] +; CHECK: OpAtomicIDecrement [[int]] [[var]] [[qf]] +; CHECK: OpAtomicIAdd [[int]] [[var]] [[qf]] +; CHECK: OpAtomicISub [[int]] [[var]] [[qf]] +; CHECK: OpAtomicSMin [[int]] [[var]] [[qf]] +; CHECK: OpAtomicSMax [[int]] [[var]] [[qf]] +; CHECK: OpAtomicUMin [[int]] [[var]] [[qf]] +; CHECK: OpAtomicUMax [[int]] [[var]] [[qf]] +; CHECK: OpAtomicAnd [[int]] [[var]] [[qf]] +; CHECK: OpAtomicOr [[int]] [[var]] [[qf]] +; CHECK: OpAtomicXor [[int]] [[var]] [[qf]] +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%none = OpConstant %int 0 +%device = OpConstant %int 1 +%func_ty = OpTypeFunction %void +%ptr_int_StorageBuffer = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_int_StorageBuffer StorageBuffer +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpAtomicLoad %int %var %device %none +OpAtomicStore %var %device %none %ld +%ex = OpAtomicExchange %int %var %device %none %ld +%cmp_ex = OpAtomicCompareExchange %int %var %device %none %none %ld %ld +%inc = OpAtomicIIncrement %int %var %device %none +%dec = OpAtomicIDecrement %int %var %device %none +%add = OpAtomicIAdd %int %var %device %none %ld +%sub = OpAtomicISub %int %var %device %none %ld +%smin = OpAtomicSMin %int %var %device %none %ld +%smax = OpAtomicSMax %int %var %device %none %ld +%umin = OpAtomicUMin %int %var %device %none %ld +%umax = OpAtomicUMax %int %var %device %none %ld +%and = OpAtomicAnd %int %var %device %none %ld +%or = OpAtomicOr %int %var %device %none %ld +%xor = OpAtomicXor %int %var %device %none %ld +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, UpgradeModfNoFlags) { + const std::string text = R"( +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[float]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer +; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[float]] +; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]] +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0 +; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1 +; CHECK: OpStore [[var]] [[ex1]] +; CHECK-NOT: NonPrivatePointer +; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +%import = OpExtInstImport "GLSL.std.450" +OpEntryPoint GLCompute %func "func" +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%ptr_ssbo_float = OpTypePointer StorageBuffer %float +%ssbo_var = OpVariable %ptr_ssbo_float StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%2 = OpExtInst %float %import Modf %float_0 %ssbo_var +%3 = OpFAdd %float %float_0 %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, UpgradeModfWorkgroupCoherent) { + const std::string text = R"( +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer Workgroup [[float]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] Workgroup +; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[float]] +; CHECK: [[wg_scope:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]] +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0 +; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1 +; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailable|NonPrivatePointer [[wg_scope]] +; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +%import = OpExtInstImport "GLSL.std.450" +OpEntryPoint GLCompute %func "func" +OpDecorate %wg_var Coherent +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%ptr_wg_float = OpTypePointer Workgroup %float +%wg_var = OpVariable %ptr_wg_float Workgroup +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%2 = OpExtInst %float %import Modf %float_0 %wg_var +%3 = OpFAdd %float %float_0 %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, UpgradeModfSSBOCoherent) { + const std::string text = R"( +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[float]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer +; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[float]] +; CHECK: [[qf_scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]] +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0 +; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1 +; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailable|NonPrivatePointer [[qf_scope]] +; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +%import = OpExtInstImport "GLSL.std.450" +OpEntryPoint GLCompute %func "func" +OpDecorate %ssbo_var Coherent +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%ptr_ssbo_float = OpTypePointer StorageBuffer %float +%ssbo_var = OpVariable %ptr_ssbo_float StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%2 = OpExtInst %float %import Modf %float_0 %ssbo_var +%3 = OpFAdd %float %float_0 %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, UpgradeModfSSBOVolatile) { + const std::string text = R"( +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[float]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer +; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[float]] +; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} ModfStruct [[float_0]] +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0 +; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 1 +; CHECK: OpStore [[var]] [[ex1]] Volatile +; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +%import = OpExtInstImport "GLSL.std.450" +OpEntryPoint GLCompute %func "func" +OpDecorate %wg_var Volatile +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%ptr_ssbo_float = OpTypePointer StorageBuffer %float +%wg_var = OpVariable %ptr_ssbo_float StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%2 = OpExtInst %float %import Modf %float_0 %wg_var +%3 = OpFAdd %float %float_0 %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, UpgradeFrexpNoFlags) { + const std::string text = R"( +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[int]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer +; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[int]] +; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]] +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0 +; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1 +; CHECK: OpStore [[var]] [[ex1]] +; CHECK-NOT: NonPrivatePointer +; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +%import = OpExtInstImport "GLSL.std.450" +OpEntryPoint GLCompute %func "func" +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%2 = OpExtInst %float %import Frexp %float_0 %ssbo_var +%3 = OpFAdd %float %float_0 %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, UpgradeFrexpWorkgroupCoherent) { + const std::string text = R"( +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer Workgroup [[int]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] Workgroup +; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[int]] +; CHECK: [[wg_scope:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]] +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0 +; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1 +; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailable|NonPrivatePointer [[wg_scope]] +; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +%import = OpExtInstImport "GLSL.std.450" +OpEntryPoint GLCompute %func "func" +OpDecorate %wg_var Coherent +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 0 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%2 = OpExtInst %float %import Frexp %float_0 %wg_var +%3 = OpFAdd %float %float_0 %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, UpgradeFrexpSSBOCoherent) { + const std::string text = R"( +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[int]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer +; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[int]] +; CHECK: [[qf_scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]] +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0 +; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1 +; CHECK: OpStore [[var]] [[ex1]] MakePointerAvailable|NonPrivatePointer [[qf_scope]] +; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +%import = OpExtInstImport "GLSL.std.450" +OpEntryPoint GLCompute %func "func" +OpDecorate %ssbo_var Coherent +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%2 = OpExtInst %float %import Frexp %float_0 %ssbo_var +%3 = OpFAdd %float %float_0 %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, UpgradeFrexpSSBOVolatile) { + const std::string text = R"( +; CHECK: [[float:%\w+]] = OpTypeFloat 32 +; CHECK: [[float_0:%\w+]] = OpConstant [[float]] 0 +; CHECK: [[int:%\w+]] = OpTypeInt 32 0 +; CHECK: [[ptr:%\w+]] = OpTypePointer StorageBuffer [[int]] +; CHECK: [[var:%\w+]] = OpVariable [[ptr]] StorageBuffer +; CHECK: [[struct:%\w+]] = OpTypeStruct [[float]] [[int]] +; CHECK: [[modfstruct:%\w+]] = OpExtInst [[struct]] {{%\w+}} FrexpStruct [[float_0]] +; CHECK: [[ex0:%\w+]] = OpCompositeExtract [[float]] [[modfstruct]] 0 +; CHECK: [[ex1:%\w+]] = OpCompositeExtract [[int]] [[modfstruct]] 1 +; CHECK: OpStore [[var]] [[ex1]] Volatile +; CHECK: OpFAdd [[float]] [[float_0]] [[ex0]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +%import = OpExtInstImport "GLSL.std.450" +OpEntryPoint GLCompute %func "func" +OpDecorate %wg_var Volatile +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%wg_var = OpVariable %ptr_ssbo_int StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%2 = OpExtInst %float %import Frexp %float_0 %wg_var +%3 = OpFAdd %float %float_0 %2 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryAddOperands) { + const std::string text = R"( +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} None None +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryDuplicateOperand) { + const std::string text = R"( +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Nontemporal Nontemporal +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src Nontemporal +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14NormalizeCopyMemoryDuplicateOperands) { + const std::string text = R"( +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned 4 Aligned 4 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src Aligned 4 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherent) { + const std::string text = R"( +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] None +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %dst Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherentPreviousArgs) { + const std::string text = R"( +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailable|NonPrivatePointer 4 [[scope]] Aligned 4 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %dst Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src Aligned 4 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemorySrcCoherent) { + const std::string text = R"( +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} None MakePointerVisible|NonPrivatePointer [[scope]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %src Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemorySrcCoherentPreviousArgs) { + const std::string text = R"( +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned 4 Aligned|MakePointerVisible|NonPrivatePointer 4 [[scope]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %src Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src Aligned 4 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothCoherent) { + const std::string text = R"( +; CHECK-DAG: [[queue:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK-DAG: [[wg:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[wg]] MakePointerVisible|NonPrivatePointer [[queue]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %src Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ptr_wg_int = OpTypePointer Workgroup %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothCoherentPreviousArgs) { + const std::string text = R"( +; CHECK-DAG: [[queue:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK-DAG: [[wg:%\w+]] = OpConstant {{%\w+}} 2 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailable|NonPrivatePointer 4 [[queue]] Aligned|MakePointerVisible|NonPrivatePointer 4 [[wg]] +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %dst Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ptr_wg_int = OpTypePointer Workgroup %int +%src = OpVariable %ptr_wg_int Workgroup +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src Aligned 4 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothVolatile) { + const std::string text = R"( +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile Volatile +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %src Volatile +OpDecorate %dst Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryBothVolatilePreviousArgs) { + const std::string text = R"( +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Volatile|Aligned 4 Volatile|Aligned 4 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %src Volatile +OpDecorate %dst Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src Aligned 4 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, SPV14CopyMemoryDstCoherentTwoOperands) { + const std::string text = R"( +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} MakePointerAvailable|NonPrivatePointer [[scope]] None +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %dst Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src None None +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, + SPV14CopyMemoryDstCoherentPreviousArgsTwoOperands) { + const std::string text = R"( +; CHECK: [[scope:%\w+]] = OpConstant {{%\w+}} 5 +; CHECK: OpCopyMemory {{%\w+}} {{%\w+}} Aligned|MakePointerAvailable|NonPrivatePointer 4 [[scope]] Aligned 8 +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" %src %dst +OpDecorate %dst Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%src = OpVariable %ptr_ssbo_int StorageBuffer +%dst = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %dst %src Aligned 4 Aligned 8 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoad) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768 +; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%relaxed = OpConstant %int 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicLoad %int %ssbo_var %device %relaxed +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadPreviousFlags) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834 +; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%acquire_ssbo = OpConstant %int 66 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicLoad %int %ssbo_var %device %acquire_ssbo +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicStore) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32768 +; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%device = OpConstant %int 1 +%relaxed = OpConstant %int 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpAtomicStore %ssbo_var %device %relaxed %int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicStorePreviousFlags) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 32836 +; CHECK: OpAtomicStore {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%device = OpConstant %int 1 +%release_ssbo = OpConstant %int 68 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpAtomicStore %ssbo_var %device %release_ssbo %int_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchange) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768 +; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile]] [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%device = OpConstant %int 1 +%relaxed = OpConstant %int 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicCompareExchange %int %ssbo_var %device %relaxed %relaxed %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicCompareExchangePreviousFlags) { + const std::string text = R"( +; CHECK-NOT: OpDecorate {{.*}} Volatile +; CHECK: [[volatile_acq_rel:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32840 +; CHECK: [[volatile_acq:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32834 +; CHECK: OpAtomicCompareExchange [[int]] {{.*}} {{.*}} [[volatile_acq_rel]] [[volatile_acq]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %ssbo_var Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%device = OpConstant %int 1 +%acq_ssbo = OpConstant %int 66 +%acq_rel_ssbo = OpConstant %int 72 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicCompareExchange %int %ssbo_var %device %acq_rel_ssbo %acq_ssbo %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, VolatileAtomicLoadMemberDecoration) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate {{.*}} {{.*}} Volatile +; CHECK: [[relaxed:%[a-zA-Z0-9_]+]] = OpConstant {{.*}} 0 +; CHECK: [[volatile:%[a-zA-Z0-9_]+]] = OpConstant [[int:%[a-zA-Z0-9_]+]] 32768 +; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[relaxed]] +; CHECK: OpAtomicLoad [[int]] {{.*}} {{.*}} [[volatile]] +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 1 Volatile +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%relaxed = OpConstant %int 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%struct = OpTypeStruct %int %int +%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct +%ssbo_var = OpVariable %ptr_ssbo_struct StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%gep0 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_0 +%ld0 = OpAtomicLoad %int %gep0 %device %relaxed +%gep1 = OpAccessChain %ptr_ssbo_int %ssbo_var %int_1 +%ld1 = OpAtomicLoad %int %gep1 %device %relaxed +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(UpgradeMemoryModelTest, CoherentStructMemberInArray) { + const std::string text = R"( +; CHECK-NOT: OpMemberDecorate +; CHECK: [[int:%[a-zA-Z0-9_]+]] = OpTypeInt 32 0 +; CHECK: [[device:%[a-zA-Z0-9_]+]] = OpConstant [[int]] 1 +; CHECK: OpLoad [[int]] {{.*}} MakePointerVisible|NonPrivatePointer +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %inner 1 Coherent +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_4 = OpConstant %int 4 +%inner = OpTypeStruct %int %int +%array = OpTypeArray %inner %int_4 +%struct = OpTypeStruct %array +%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ssbo_var = OpVariable %ptr_ssbo_struct StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_ssbo_int %ssbo_var %int_0 %int_0 %int_1 +%ld = OpLoad %int %gep +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace diff --git a/third_party/spirv-tools/test/opt/utils_test.cpp b/third_party/spirv-tools/test/opt/utils_test.cpp new file mode 100644 index 0000000..5ce146b --- /dev/null +++ b/third_party/spirv-tools/test/opt/utils_test.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gtest/gtest.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +TEST(JoinAllInsts, Cases) { + EXPECT_EQ("", JoinAllInsts({})); + EXPECT_EQ("a\n", JoinAllInsts({"a"})); + EXPECT_EQ("a\nb\n", JoinAllInsts({"a", "b"})); + EXPECT_EQ("a\nb\nc\n", JoinAllInsts({"a", "b", "c"})); + EXPECT_EQ("hello,\nworld!\n\n\n", JoinAllInsts({"hello,", "world!", "\n"})); +} + +TEST(JoinNonDebugInsts, Cases) { + EXPECT_EQ("", JoinNonDebugInsts({})); + EXPECT_EQ("a\n", JoinNonDebugInsts({"a"})); + EXPECT_EQ("", JoinNonDebugInsts({"OpName"})); + EXPECT_EQ("a\nb\n", JoinNonDebugInsts({"a", "b"})); + EXPECT_EQ("", JoinNonDebugInsts({"OpName", "%1 = OpString \"42\""})); + EXPECT_EQ("Opstring\n", JoinNonDebugInsts({"OpName", "Opstring"})); + EXPECT_EQ("the only remaining string\n", + JoinNonDebugInsts( + {"OpSourceContinued", "OpSource", "OpSourceExtension", + "lgtm OpName", "hello OpMemberName", "this is a OpString", + "lonely OpLine", "happy OpNoLine", "OpModuleProcessed", + "the only remaining string"})); +} + +struct SubstringReplacementTestCase { + const char* orig_str; + const char* find_substr; + const char* replace_substr; + const char* expected_str; + bool replace_should_succeed; +}; + +using FindAndReplaceTest = + ::testing::TestWithParam; + +TEST_P(FindAndReplaceTest, SubstringReplacement) { + auto process = std::string(GetParam().orig_str); + EXPECT_EQ(GetParam().replace_should_succeed, + FindAndReplace(&process, GetParam().find_substr, + GetParam().replace_substr)) + << "Original string: " << GetParam().orig_str + << " replace: " << GetParam().find_substr + << " to: " << GetParam().replace_substr + << " should returns: " << GetParam().replace_should_succeed; + EXPECT_STREQ(GetParam().expected_str, process.c_str()) + << "Original string: " << GetParam().orig_str + << " replace: " << GetParam().find_substr + << " to: " << GetParam().replace_substr + << " expected string: " << GetParam().expected_str; +} + +INSTANTIATE_TEST_SUITE_P( + SubstringReplacement, FindAndReplaceTest, + ::testing::ValuesIn(std::vector({ + // orig string, find substring, replace substring, expected string, + // replacement happened + {"", "", "", "", false}, + {"", "b", "", "", false}, + {"", "", "c", "", false}, + {"", "a", "b", "", false}, + + {"a", "", "c", "a", false}, + {"a", "b", "c", "a", false}, + {"a", "b", "", "a", false}, + {"a", "a", "", "", true}, + {"a", "a", "b", "b", true}, + + {"ab", "a", "b", "bb", true}, + {"ab", "a", "", "b", true}, + {"ab", "b", "", "a", true}, + {"ab", "ab", "", "", true}, + {"ab", "ab", "cd", "cd", true}, + {"bc", "abc", "efg", "bc", false}, + + {"abc", "ab", "bc", "bcc", true}, + {"abc", "ab", "", "c", true}, + {"abc", "bc", "", "a", true}, + {"abc", "bc", "d", "ad", true}, + {"abc", "a", "123", "123bc", true}, + {"abc", "ab", "a", "ac", true}, + {"abc", "a", "aab", "aabbc", true}, + {"abc", "abcd", "efg", "abc", false}, + }))); + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/value_table_test.cpp b/third_party/spirv-tools/test/opt/value_table_test.cpp new file mode 100644 index 0000000..c760f98 --- /dev/null +++ b/third_party/spirv-tools/test/opt/value_table_test.cpp @@ -0,0 +1,832 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/opt/build_module.h" +#include "source/opt/value_number_table.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" + +namespace spvtools { +namespace opt { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; +using ValueTableTest = PassTest<::testing::Test>; + +TEST_F(ValueTableTest, SameInstructionSameValue) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(vtable.GetValueNumber(inst), vtable.GetValueNumber(inst)); +} + +TEST_F(ValueTableTest, DifferentInstructionSameValue) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 + %11 = OpFAdd %5 %9 %9 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(10); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(11); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, SameValueDifferentBlock) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 + OpBranch %11 + %11 = OpLabel + %12 = OpFAdd %5 %9 %9 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(10); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(12); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, DifferentValue) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 + %11 = OpFAdd %5 %9 %10 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(10); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(11); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, DifferentValueDifferentBlock) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpFAdd %5 %9 %9 + OpBranch %11 + %11 = OpLabel + %12 = OpFAdd %5 %9 %10 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(10); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(12); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, SameLoad) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst = context->get_def_use_mgr()->GetDef(9); + EXPECT_EQ(vtable.GetValueNumber(inst), vtable.GetValueNumber(inst)); +} + +// Two different loads, even from the same memory, must given different value +// numbers if the memory is not read-only. +TEST_F(ValueTableTest, DifferentFunctionLoad) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(9); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(10); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, DifferentUniformLoad) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Uniform %5 + %8 = OpVariable %6 Uniform + %2 = OpFunction %3 None %4 + %7 = OpLabel + %9 = OpLoad %5 %8 + %10 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(9); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, DifferentInputLoad) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Input %5 + %8 = OpVariable %6 Input + %2 = OpFunction %3 None %4 + %7 = OpLabel + %9 = OpLoad %5 %8 + %10 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(9); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, DifferentUniformConstantLoad) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer UniformConstant %5 + %8 = OpVariable %6 UniformConstant + %2 = OpFunction %3 None %4 + %7 = OpLabel + %9 = OpLoad %5 %8 + %10 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(9); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, DifferentPushConstantLoad) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer PushConstant %5 + %8 = OpVariable %6 PushConstant + %2 = OpFunction %3 None %4 + %7 = OpLabel + %9 = OpLoad %5 %8 + %10 = OpLoad %5 %8 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(9); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, SameCall) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypeFunction %5 + %7 = OpTypePointer Function %5 + %8 = OpVariable %7 Private + %2 = OpFunction %3 None %4 + %9 = OpLabel + %10 = OpFunctionCall %5 %11 + OpReturn + OpFunctionEnd + %11 = OpFunction %5 None %6 + %12 = OpLabel + %13 = OpLoad %5 %8 + OpReturnValue %13 + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(vtable.GetValueNumber(inst), vtable.GetValueNumber(inst)); +} + +// Function calls should be given a new value number, even if they are the same. +TEST_F(ValueTableTest, DifferentCall) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypeFunction %5 + %7 = OpTypePointer Function %5 + %8 = OpVariable %7 Private + %2 = OpFunction %3 None %4 + %9 = OpLabel + %10 = OpFunctionCall %5 %11 + %12 = OpFunctionCall %5 %11 + OpReturn + OpFunctionEnd + %11 = OpFunction %5 None %6 + %13 = OpLabel + %14 = OpLoad %5 %8 + OpReturnValue %14 + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(10); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(12); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +// It is possible to have two instruction that compute the same numerical value, +// but with different types. They should have different value numbers. +TEST_F(ValueTableTest, DifferentTypes) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 0 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + %9 = OpVariable %7 Function + %10 = OpLoad %5 %9 + %11 = OpIAdd %5 %10 %10 + %12 = OpIAdd %6 %10 %10 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(11); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(12); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, CopyObject) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Function %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + %8 = OpVariable %6 Function + %9 = OpLoad %5 %8 + %10 = OpCopyObject %5 %9 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(9); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(10); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, CopyObjectWitDecoration) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpDecorate %3 NonUniformEXT + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %2 = OpFunction %4 None %5 + %8 = OpLabel + %9 = OpVariable %7 Function + %10 = OpLoad %6 %9 + %3 = OpCopyObject %6 %10 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(10); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(3); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +// Test that a phi where the operands have the same value assigned that value +// to the result of the phi. +TEST_F(ValueTableTest, PhiTest1) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Uniform %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpVariable %6 Uniform + %2 = OpFunction %3 None %4 + %10 = OpLabel + OpBranchConditional %8 %11 %12 + %11 = OpLabel + %13 = OpLoad %5 %9 + OpBranch %14 + %12 = OpLabel + %15 = OpLoad %5 %9 + OpBranch %14 + %14 = OpLabel + %16 = OpPhi %5 %13 %11 %15 %12 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(13); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(15); + Instruction* phi = context->get_def_use_mgr()->GetDef(16); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(phi)); +} + +TEST_F(ValueTableTest, PhiTest1WithDecoration) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + OpDecorate %3 NonUniformEXT + %4 = OpTypeVoid + %5 = OpTypeFunction %5 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Uniform %6 + %8 = OpTypeBool + %9 = OpConstantTrue %8 + %10 = OpVariable %7 Uniform + %2 = OpFunction %4 None %5 + %11 = OpLabel + OpBranchConditional %9 %12 %13 + %12 = OpLabel + %14 = OpLoad %6 %10 + OpBranch %15 + %13 = OpLabel + %16 = OpLoad %6 %10 + OpBranch %15 + %15 = OpLabel + %3 = OpPhi %6 %14 %12 %16 %13 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(14); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(16); + Instruction* phi = context->get_def_use_mgr()->GetDef(3); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(phi)); +} + +// When the values for the inputs to a phi do not match, then the phi should +// have its own value number. +TEST_F(ValueTableTest, PhiTest2) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Uniform %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpVariable %6 Uniform + %10 = OpVariable %6 Uniform + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpBranchConditional %8 %12 %13 + %12 = OpLabel + %14 = OpLoad %5 %9 + OpBranch %15 + %13 = OpLabel + %16 = OpLoad %5 %10 + OpBranch %15 + %15 = OpLabel + %17 = OpPhi %14 %12 %16 %13 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(14); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(16); + Instruction* phi = context->get_def_use_mgr()->GetDef(17); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(phi)); + EXPECT_NE(vtable.GetValueNumber(inst2), vtable.GetValueNumber(phi)); +} + +// Test that a phi node in a loop header gets a new value because one of its +// inputs comes from later in the loop. +TEST_F(ValueTableTest, PhiLoopTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeFloat 32 + %6 = OpTypePointer Uniform %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpVariable %6 Uniform + %10 = OpVariable %6 Uniform + %2 = OpFunction %3 None %4 + %11 = OpLabel + %12 = OpLoad %5 %9 + OpSelectionMerge %13 None + OpBranchConditional %8 %14 %13 + %14 = OpLabel + %15 = OpPhi %5 %12 %11 %16 %14 + %16 = OpLoad %5 %9 + OpLoopMerge %17 %14 None + OpBranchConditional %8 %14 %17 + %17 = OpLabel + OpBranch %13 + %13 = OpLabel + %18 = OpPhi %5 %12 %11 %16 %17 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(12); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(16); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); + + Instruction* phi1 = context->get_def_use_mgr()->GetDef(15); + EXPECT_NE(vtable.GetValueNumber(inst1), vtable.GetValueNumber(phi1)); + + Instruction* phi2 = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(phi2)); + EXPECT_NE(vtable.GetValueNumber(phi1), vtable.GetValueNumber(phi2)); +} + +// Test to make sure that OpPhi instructions with no in operands are handled +// correctly. +TEST_F(ValueTableTest, EmptyPhiTest) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 430 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %2 = OpFunction %void None %4 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %true %9 %8 + %9 = OpLabel + OpKill + %8 = OpLabel + %10 = OpPhi %bool + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst = context->get_def_use_mgr()->GetDef(10); + vtable.GetValueNumber(inst); +} + +TEST_F(ValueTableTest, RedundantSampledImageLoad) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 330 + OpName %main "main" + OpName %tex0 "tex0" + OpName %gl_FragColor "gl_FragColor" + OpDecorate %tex0 Location 0 + OpDecorate %tex0 DescriptorSet 0 + OpDecorate %tex0 Binding 0 + OpDecorate %gl_FragColor Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %9 = OpTypeImage %float 2D 0 0 0 1 Unknown + %10 = OpTypeSampledImage %9 +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %tex0 = OpVariable %_ptr_UniformConstant_10 UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %13 = OpConstantNull %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output + %14 = OpUndef %v4float + %main = OpFunction %void None %6 + %15 = OpLabel + %16 = OpLoad %10 %tex0 + %17 = OpImageSampleProjImplicitLod %v4float %16 %13 + %18 = OpImageSampleProjImplicitLod %v4float %16 %13 + %19 = OpFAdd %v4float %18 %17 + OpStore %gl_FragColor %19 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* load1 = context->get_def_use_mgr()->GetDef(17); + Instruction* load2 = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(vtable.GetValueNumber(load1), vtable.GetValueNumber(load2)); +} + +TEST_F(ValueTableTest, DifferentDebugLocalVariableSameValue) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + %2 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %3 "main" + OpExecutionMode %3 OriginUpperLeft + OpSource GLSL 430 + %4 = OpString "test" + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 32 + %9 = OpExtInst %5 %2 DebugSource %4 + %10 = OpExtInst %5 %2 DebugCompilationUnit 1 4 %9 HLSL + %11 = OpExtInst %5 %2 DebugTypeBasic %4 %8 Float + %12 = OpExtInst %5 %2 DebugLocalVariable %4 %11 %9 0 0 %10 FlagIsLocal + %13 = OpExtInst %5 %2 DebugLocalVariable %4 %11 %9 0 0 %10 FlagIsLocal + %3 = OpFunction %5 None %6 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(12); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(13); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, DifferentDebugValueSameValue) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + %2 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %3 "main" + OpExecutionMode %3 OriginUpperLeft + OpSource GLSL 430 + %4 = OpString "test" + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 32 + %9 = OpExtInst %5 %2 DebugSource %4 + %10 = OpExtInst %5 %2 DebugCompilationUnit 1 4 %9 HLSL + %11 = OpExtInst %5 %2 DebugTypeBasic %4 %8 Float + %12 = OpExtInst %5 %2 DebugLocalVariable %4 %11 %9 0 0 %10 FlagIsLocal + %13 = OpExtInst %5 %2 DebugExpression + %3 = OpFunction %5 None %6 + %14 = OpLabel + %15 = OpExtInst %5 %2 DebugValue %12 %8 %13 + %16 = OpExtInst %5 %2 DebugValue %12 %8 %13 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(15); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(16); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +TEST_F(ValueTableTest, DifferentDebugDeclareSameValue) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + %2 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %3 "main" + OpExecutionMode %3 OriginUpperLeft + OpSource GLSL 430 + %4 = OpString "test" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint + %uint_32 = OpConstant %uint 32 + %10 = OpExtInst %void %2 DebugSource %4 + %11 = OpExtInst %void %2 DebugCompilationUnit 1 4 %10 HLSL + %12 = OpExtInst %void %2 DebugTypeBasic %4 %uint_32 Float + %13 = OpExtInst %void %2 DebugLocalVariable %4 %12 %10 0 0 %11 FlagIsLocal + %14 = OpExtInst %void %2 DebugExpression + %3 = OpFunction %void None %6 + %15 = OpLabel + %16 = OpVariable %_ptr_Function_uint Function + %17 = OpExtInst %void %2 DebugDeclare %13 %16 %14 + %18 = OpExtInst %void %2 DebugDeclare %13 %16 %14 + OpReturn + OpFunctionEnd + )"; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text); + ValueNumberTable vtable(context.get()); + Instruction* inst1 = context->get_def_use_mgr()->GetDef(17); + Instruction* inst2 = context->get_def_use_mgr()->GetDef(18); + EXPECT_EQ(vtable.GetValueNumber(inst1), vtable.GetValueNumber(inst2)); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/vector_dce_test.cpp b/third_party/spirv-tools/test/opt/vector_dce_test.cpp new file mode 100644 index 0000000..9bdad37 --- /dev/null +++ b/third_party/spirv-tools/test/opt/vector_dce_test.cpp @@ -0,0 +1,1356 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using VectorDCETest = PassTest<::testing::Test>; + +TEST_F(VectorDCETest, InsertAfterInsertElim) { + // With two insertions to the same offset, the first is dead. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in float In0; + // layout (location=1) in float In1; + // layout (location=2) in vec2 In2; + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // vec2 v = In2; + // v.x = In0 + In1; // dead + // v.x = 0.0; + // OutColor = v.xyxy; + // } + + const std::string before_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In2 "In2" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %OutColor "OutColor" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_n" +OpName %_ "" +OpDecorate %In2 Location 2 +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %OutColor Location 0 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%In2 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_float = OpTypePointer Input %float +%In0 = OpVariable %_ptr_Input_float Input +%In1 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%int = OpTypeInt 32 1 +%_Globals_ = OpTypeStruct %uint %int +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +)"; + + const std::string after_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In2 "In2" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %OutColor "OutColor" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_n" +OpName %_ "" +OpDecorate %In2 Location 2 +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %OutColor Location 0 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%In2 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_float = OpTypePointer Input %float +%In0 = OpVariable %_ptr_Input_float Input +%In1 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%int = OpTypeInt 32 1 +%_Globals_ = OpTypeStruct %uint %int +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +)"; + + const std::string before = + R"(%main = OpFunction %void None %11 +%25 = OpLabel +%26 = OpLoad %v2float %In2 +%27 = OpLoad %float %In0 +%28 = OpLoad %float %In1 +%29 = OpFAdd %float %27 %28 +%35 = OpCompositeInsert %v2float %29 %26 0 +%37 = OpCompositeInsert %v2float %float_0 %35 0 +%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1 +OpStore %OutColor %33 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%23 = OpLabel +%24 = OpLoad %v2float %In2 +%25 = OpLoad %float %In0 +%26 = OpLoad %float %In1 +%27 = OpFAdd %float %25 %26 +%28 = OpCompositeInsert %v2float %27 %24 0 +%29 = OpCompositeInsert %v2float %float_0 %24 0 +%30 = OpVectorShuffle %v4float %29 %29 0 1 0 1 +OpStore %OutColor %30 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_predefs + before, + after_predefs + after, true, true); +} + +TEST_F(VectorDCETest, DeadInsertInChainWithPhi) { + // Dead insert eliminated with phi in insertion chain. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in vec4 In0; + // layout (location=1) in float In1; + // layout (location=2) in float In2; + // layout (location=0) out vec4 OutColor; + // + // layout(std140, binding = 0 ) uniform _Globals_ + // { + // bool g_b; + // }; + // + // void main() + // { + // vec4 v = In0; + // v.z = In1 + In2; + // if (g_b) v.w = 1.0; + // OutColor = vec4(v.x,v.y,0.0,v.w); + // } + + const std::string before_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpName %_ "" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%_Globals_ = OpTypeStruct %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +)"; + + const std::string after_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpName %_ "" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_ptr_Function_float = OpTypePointer Function %float +%_Globals_ = OpTypeStruct %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +)"; + + const std::string before = + R"(%main = OpFunction %void None %11 +%31 = OpLabel +%32 = OpLoad %v4float %In0 +%33 = OpLoad %float %In1 +%34 = OpLoad %float %In2 +%35 = OpFAdd %float %33 %34 +%51 = OpCompositeInsert %v4float %35 %32 2 +%37 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%38 = OpLoad %uint %37 +%39 = OpINotEqual %bool %38 %uint_0 +OpSelectionMerge %40 None +OpBranchConditional %39 %41 %40 +%41 = OpLabel +%53 = OpCompositeInsert %v4float %float_1 %51 3 +OpBranch %40 +%40 = OpLabel +%60 = OpPhi %v4float %51 %31 %53 %41 +%55 = OpCompositeExtract %float %60 0 +%57 = OpCompositeExtract %float %60 1 +%59 = OpCompositeExtract %float %60 3 +%49 = OpCompositeConstruct %v4float %55 %57 %float_0 %59 +OpStore %OutColor %49 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%27 = OpLabel +%28 = OpLoad %v4float %In0 +%29 = OpLoad %float %In1 +%30 = OpLoad %float %In2 +%31 = OpFAdd %float %29 %30 +%32 = OpCompositeInsert %v4float %31 %28 2 +%33 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%34 = OpLoad %uint %33 +%35 = OpINotEqual %bool %34 %uint_0 +OpSelectionMerge %36 None +OpBranchConditional %35 %37 %36 +%37 = OpLabel +%38 = OpCompositeInsert %v4float %float_1 %28 3 +OpBranch %36 +%36 = OpLabel +%39 = OpPhi %v4float %28 %27 %38 %37 +%40 = OpCompositeExtract %float %39 0 +%41 = OpCompositeExtract %float %39 1 +%42 = OpCompositeExtract %float %39 3 +%43 = OpCompositeConstruct %v4float %40 %41 %float_0 %42 +OpStore %OutColor %43 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_predefs + before, + after_predefs + after, true, true); +} + +TEST_F(VectorDCETest, DeadInsertWithScalars) { + // Dead insert which requires two passes to eliminate + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in vec4 In0; + // layout (location=1) in float In1; + // layout (location=2) in float In2; + // layout (location=0) out vec4 OutColor; + // + // layout(std140, binding = 0 ) uniform _Globals_ + // { + // bool g_b; + // bool g_b2; + // }; + // + // void main() + // { + // vec4 v1, v2; + // v1 = In0; + // v1.y = In1 + In2; // dead, second pass + // if (g_b) v1.x = 1.0; + // v2.x = v1.x; + // v2.y = v1.y; // dead, first pass + // if (g_b2) v2.x = 0.0; + // OutColor = vec4(v2.x,v2.x,0.0,1.0); + // } + + const std::string before_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_b2" +OpName %_ "" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_Globals_ = OpTypeStruct %uint %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%27 = OpUndef %v4float +)"; + + const std::string after_predefs = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %In2 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_b2" +OpName %_ "" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%_Globals_ = OpTypeStruct %uint %uint +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%bool = OpTypeBool +%uint_0 = OpConstant %uint 0 +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%float_0 = OpConstant %float 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%27 = OpUndef %v4float +%55 = OpUndef %v4float +)"; + + const std::string before = + R"(%main = OpFunction %void None %10 +%28 = OpLabel +%29 = OpLoad %v4float %In0 +%30 = OpLoad %float %In1 +%31 = OpLoad %float %In2 +%32 = OpFAdd %float %30 %31 +%33 = OpCompositeInsert %v4float %32 %29 1 +%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%35 = OpLoad %uint %34 +%36 = OpINotEqual %bool %35 %uint_0 +OpSelectionMerge %37 None +OpBranchConditional %36 %38 %37 +%38 = OpLabel +%39 = OpCompositeInsert %v4float %float_1 %33 0 +OpBranch %37 +%37 = OpLabel +%40 = OpPhi %v4float %33 %28 %39 %38 +%41 = OpCompositeExtract %float %40 0 +%42 = OpCompositeInsert %v4float %41 %27 0 +%43 = OpCompositeExtract %float %40 1 +%44 = OpCompositeInsert %v4float %43 %42 1 +%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 +%46 = OpLoad %uint %45 +%47 = OpINotEqual %bool %46 %uint_0 +OpSelectionMerge %48 None +OpBranchConditional %47 %49 %48 +%49 = OpLabel +%50 = OpCompositeInsert %v4float %float_0 %44 0 +OpBranch %48 +%48 = OpLabel +%51 = OpPhi %v4float %44 %37 %50 %49 +%52 = OpCompositeExtract %float %51 0 +%53 = OpCompositeExtract %float %51 0 +%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1 +OpStore %OutColor %54 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(%main = OpFunction %void None %10 +%28 = OpLabel +%29 = OpLoad %v4float %In0 +%30 = OpLoad %float %In1 +%31 = OpLoad %float %In2 +%32 = OpFAdd %float %30 %31 +%33 = OpCompositeInsert %v4float %32 %29 1 +%34 = OpAccessChain %_ptr_Uniform_uint %_ %int_0 +%35 = OpLoad %uint %34 +%36 = OpINotEqual %bool %35 %uint_0 +OpSelectionMerge %37 None +OpBranchConditional %36 %38 %37 +%38 = OpLabel +%39 = OpCompositeInsert %v4float %float_1 %55 0 +OpBranch %37 +%37 = OpLabel +%40 = OpPhi %v4float %29 %28 %39 %38 +%41 = OpCompositeExtract %float %40 0 +%42 = OpCompositeInsert %v4float %41 %55 0 +%43 = OpCompositeExtract %float %40 1 +%44 = OpCompositeInsert %v4float %43 %42 1 +%45 = OpAccessChain %_ptr_Uniform_uint %_ %int_1 +%46 = OpLoad %uint %45 +%47 = OpINotEqual %bool %46 %uint_0 +OpSelectionMerge %48 None +OpBranchConditional %47 %49 %48 +%49 = OpLabel +%50 = OpCompositeInsert %v4float %float_0 %55 0 +OpBranch %48 +%48 = OpLabel +%51 = OpPhi %v4float %42 %37 %50 %49 +%52 = OpCompositeExtract %float %51 0 +%53 = OpCompositeExtract %float %51 0 +%54 = OpCompositeConstruct %v4float %52 %53 %float_0 %float_1 +OpStore %OutColor %54 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before_predefs + before, + after_predefs + after, true, true); +} + +TEST_F(VectorDCETest, InsertObjectLive) { + // Make sure that the object being inserted in an OpCompositeInsert + // is not removed when it is uses later on. + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %In1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %10 +%28 = OpLabel +%29 = OpLoad %v4float %In0 +%30 = OpLoad %float %In1 +%33 = OpCompositeInsert %v4float %30 %29 1 +OpStore %OutColor %33 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, before, true, true); +} + +TEST_F(VectorDCETest, DeadInsertInCycle) { + // Dead insert in chain with cycle. Demonstrates analysis can handle + // cycles in chains going through scalars intermediate values. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) in vec4 In0; + // layout (location=1) in float In1; + // layout (location=2) in float In2; + // layout (location=0) out vec4 OutColor; + // + // layout(std140, binding = 0 ) uniform _Globals_ + // { + // int g_n ; + // }; + // + // void main() + // { + // vec2 v = vec2(0.0, 1.0); + // for (int i = 0; i < g_n; i++) { + // v.x = v.x + 1; + // v.y = v.y * 0.9; // dead + // } + // OutColor = vec4(v.x); + // } + + const std::string assembly = + R"( +; CHECK: [[init_val:%\w+]] = OpConstantComposite %v2float %float_0 %float_1 +; CHECK: [[undef:%\w+]] = OpUndef %v2float +; CHECK: OpFunction +; CHECK: [[entry_lab:%\w+]] = OpLabel +; CHECK: [[loop_header:%\w+]] = OpLabel +; CHECK: OpPhi %v2float [[init_val]] [[entry_lab]] [[x_insert:%\w+]] {{%\w+}} +; CHECK: [[x_insert:%\w+]] = OpCompositeInsert %v2float %43 [[undef]] 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %OutColor %In0 %In1 %In2 +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_n" +OpName %_ "" +OpName %OutColor "OutColor" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %In2 "In2" +OpMemberDecorate %_Globals_ 0 Offset 0 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +OpDecorate %OutColor Location 0 +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %In2 Location 2 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%16 = OpConstantComposite %v2float %float_0 %float_1 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%_Globals_ = OpTypeStruct %int +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int +%bool = OpTypeBool +%float_0_75 = OpConstant %float 0.75 +%int_1 = OpConstant %int 1 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%_ptr_Input_float = OpTypePointer Input %float +%In1 = OpVariable %_ptr_Input_float Input +%In2 = OpVariable %_ptr_Input_float Input +%main = OpFunction %void None %10 +%29 = OpLabel +OpBranch %30 +%30 = OpLabel +%31 = OpPhi %v2float %16 %29 %32 %33 +%34 = OpPhi %int %int_0 %29 %35 %33 +OpLoopMerge %36 %33 None +OpBranch %37 +%37 = OpLabel +%38 = OpAccessChain %_ptr_Uniform_int %_ %int_0 +%39 = OpLoad %int %38 +%40 = OpSLessThan %bool %34 %39 +OpBranchConditional %40 %41 %36 +%41 = OpLabel +%42 = OpCompositeExtract %float %31 0 +%43 = OpFAdd %float %42 %float_1 +%44 = OpCompositeInsert %v2float %43 %31 0 +%45 = OpCompositeExtract %float %44 1 +%46 = OpFMul %float %45 %float_0_75 +%32 = OpCompositeInsert %v2float %46 %44 1 +OpBranch %33 +%33 = OpLabel +%35 = OpIAdd %int %34 %int_1 +OpBranch %30 +%36 = OpLabel +%47 = OpCompositeExtract %float %31 0 +%48 = OpCompositeConstruct %v4float %47 %47 %47 %47 +OpStore %OutColor %48 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(assembly, true); +} + +TEST_F(VectorDCETest, DeadLoadFeedingCompositeConstruct) { + // Detach the loads feeding the CompositeConstruct for the unused elements. + // TODO: Implement the rewrite for CompositeConstruct. + + const std::string assembly = + R"( +; CHECK: [[undef:%\w+]] = OpUndef %float +; CHECK: [[ac:%\w+]] = OpAccessChain %_ptr_Input_float %In0 %uint_2 +; CHECK: [[load:%\w+]] = OpLoad %float [[ac]] +; CHECK: OpCompositeConstruct %v3float [[load]] [[undef]] [[undef]] +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpName %main "main" +OpName %In0 "In0" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%v3float = OpTypeVector %float 3 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_20 = OpConstant %int 20 +%bool = OpTypeBool +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%23 = OpUndef %v3float +%main = OpFunction %void None %6 +%24 = OpLabel +%25 = OpAccessChain %_ptr_Input_float %In0 %uint_0 +%26 = OpLoad %float %25 +%27 = OpAccessChain %_ptr_Input_float %In0 %uint_1 +%28 = OpLoad %float %27 +%29 = OpAccessChain %_ptr_Input_float %In0 %uint_2 +%30 = OpLoad %float %29 +%31 = OpCompositeConstruct %v3float %30 %28 %26 +OpBranch %32 +%32 = OpLabel +%33 = OpPhi %v3float %31 %24 %34 %35 +%36 = OpPhi %int %int_0 %24 %37 %35 +OpLoopMerge %38 %35 None +OpBranch %39 +%39 = OpLabel +%40 = OpSLessThan %bool %36 %int_20 +OpBranchConditional %40 %41 %38 +%41 = OpLabel +%42 = OpCompositeExtract %float %33 0 +%43 = OpFAdd %float %42 %float_1 +%34 = OpCompositeInsert %v3float %43 %33 0 +OpBranch %35 +%35 = OpLabel +%37 = OpIAdd %int %36 %int_1 +OpBranch %32 +%38 = OpLabel +%44 = OpCompositeExtract %float %33 0 +%45 = OpCompositeConstruct %v4float %44 %44 %44 %44 +OpStore %OutColor %45 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(assembly, true); +} + +TEST_F(VectorDCETest, DeadLoadFeedingVectorShuffle) { + // Detach the loads feeding the CompositeConstruct for the unused elements. + // TODO: Implement the rewrite for CompositeConstruct. + + const std::string assembly = + R"( +; MemPass Type2Undef does not reuse and already existing undef. +; CHECK: {{%\w+}} = OpUndef %v3float +; CHECK: [[undef:%\w+]] = OpUndef %v3float +; CHECK: OpFunction +; CHECK: OpVectorShuffle %v3float {{%\w+}} [[undef]] 0 4 5 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %In0 %OutColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %In0 "In0" + OpName %OutColor "OutColor" + OpDecorate %In0 Location 0 + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %In0 = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_20 = OpConstant %int 20 + %bool = OpTypeBool + %float_1 = OpConstant %float 1 + %vec_const = OpConstantComposite %v3float %float_1 %float_1 %float_1 + %int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OutColor = OpVariable %_ptr_Output_v4float Output + %23 = OpUndef %v3float + %main = OpFunction %void None %6 + %24 = OpLabel + %25 = OpAccessChain %_ptr_Input_float %In0 %uint_0 + %26 = OpLoad %float %25 + %27 = OpAccessChain %_ptr_Input_float %In0 %uint_1 + %28 = OpLoad %float %27 + %29 = OpAccessChain %_ptr_Input_float %In0 %uint_2 + %30 = OpLoad %float %29 + %31 = OpCompositeConstruct %v3float %30 %28 %26 + %sh = OpVectorShuffle %v3float %vec_const %31 0 4 5 + OpBranch %32 + %32 = OpLabel + %33 = OpPhi %v3float %sh %24 %34 %35 + %36 = OpPhi %int %int_0 %24 %37 %35 + OpLoopMerge %38 %35 None + OpBranch %39 + %39 = OpLabel + %40 = OpSLessThan %bool %36 %int_20 + OpBranchConditional %40 %41 %38 + %41 = OpLabel + %42 = OpCompositeExtract %float %33 0 + %43 = OpFAdd %float %42 %float_1 + %34 = OpCompositeInsert %v3float %43 %33 0 + OpBranch %35 + %35 = OpLabel + %37 = OpIAdd %int %36 %int_1 + OpBranch %32 + %38 = OpLabel + %44 = OpCompositeExtract %float %33 0 + %45 = OpCompositeConstruct %v4float %44 %44 %44 %44 + OpStore %OutColor %45 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(assembly, true); +} + +TEST_F(VectorDCETest, DeadInstThroughShuffle) { + // Dead insert in chain with cycle. Demonstrates analysis can handle + // cycles in chains. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // vec2 v; + // v.x = 0.0; + // v.y = 0.1; // dead + // for (int i = 0; i < 20; i++) { + // v.x = v.x + 1; + // v = v * 0.9; + // } + // OutColor = vec4(v.x); + // } + + const std::string assembly = + R"( +; CHECK: OpFunction +; CHECK-NOT: OpCompositeInsert %v2float {{%\w+}} 1 +; CHECK: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %OutColor "OutColor" + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %float_0 = OpConstant %float 0 +%float_0_100000001 = OpConstant %float 0.100000001 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_20 = OpConstant %int 20 + %bool = OpTypeBool + %float_1 = OpConstant %float 1 +%float_0_899999976 = OpConstant %float 0.899999976 + %int_1 = OpConstant %int 1 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OutColor = OpVariable %_ptr_Output_v4float Output + %58 = OpUndef %v2float + %main = OpFunction %void None %3 + %5 = OpLabel + %49 = OpCompositeInsert %v2float %float_0 %58 0 + %51 = OpCompositeInsert %v2float %float_0_100000001 %49 1 + OpBranch %22 + %22 = OpLabel + %60 = OpPhi %v2float %51 %5 %38 %25 + %59 = OpPhi %int %int_0 %5 %41 %25 + OpLoopMerge %24 %25 None + OpBranch %26 + %26 = OpLabel + %30 = OpSLessThan %bool %59 %int_20 + OpBranchConditional %30 %23 %24 + %23 = OpLabel + %53 = OpCompositeExtract %float %60 0 + %34 = OpFAdd %float %53 %float_1 + %55 = OpCompositeInsert %v2float %34 %60 0 + %38 = OpVectorTimesScalar %v2float %55 %float_0_899999976 + OpBranch %25 + %25 = OpLabel + %41 = OpIAdd %int %59 %int_1 + OpBranch %22 + %24 = OpLabel + %57 = OpCompositeExtract %float %60 0 + %47 = OpCompositeConstruct %v4float %57 %57 %57 %57 + OpStore %OutColor %47 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(assembly, true); +} + +TEST_F(VectorDCETest, DeadInsertThroughOtherInst) { + // Dead insert in chain with cycle. Demonstrates analysis can handle + // cycles in chains. + // + // Note: The SPIR-V assembly has had store/load elimination + // performed to allow the inserts and extracts to directly + // reference each other. + // + // #version 450 + // + // layout (location=0) out vec4 OutColor; + // + // void main() + // { + // vec2 v; + // v.x = 0.0; + // v.y = 0.1; // dead + // for (int i = 0; i < 20; i++) { + // v.x = v.x + 1; + // v = v * 0.9; + // } + // OutColor = vec4(v.x); + // } + + const std::string assembly = + R"( +; CHECK: OpFunction +; CHECK-NOT: OpCompositeInsert %v2float {{%\w+}} 1 +; CHECK: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %OutColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" + OpSourceExtension "GL_GOOGLE_include_directive" + OpName %main "main" + OpName %OutColor "OutColor" + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %float_0 = OpConstant %float 0 +%float_0_100000001 = OpConstant %float 0.100000001 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %int_20 = OpConstant %int 20 + %bool = OpTypeBool + %float_1 = OpConstant %float 1 +%float_0_899999976 = OpConstant %float 0.899999976 + %int_1 = OpConstant %int 1 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OutColor = OpVariable %_ptr_Output_v4float Output + %58 = OpUndef %v2float + %main = OpFunction %void None %3 + %5 = OpLabel + %49 = OpCompositeInsert %v2float %float_0 %58 0 + %51 = OpCompositeInsert %v2float %float_0_100000001 %49 1 + OpBranch %22 + %22 = OpLabel + %60 = OpPhi %v2float %51 %5 %38 %25 + %59 = OpPhi %int %int_0 %5 %41 %25 + OpLoopMerge %24 %25 None + OpBranch %26 + %26 = OpLabel + %30 = OpSLessThan %bool %59 %int_20 + OpBranchConditional %30 %23 %24 + %23 = OpLabel + %53 = OpCompositeExtract %float %60 0 + %34 = OpFAdd %float %53 %float_1 + %55 = OpCompositeInsert %v2float %34 %60 0 + %38 = OpVectorTimesScalar %v2float %55 %float_0_899999976 + OpBranch %25 + %25 = OpLabel + %41 = OpIAdd %int %59 %int_1 + OpBranch %22 + %24 = OpLabel + %57 = OpCompositeExtract %float %60 0 + %47 = OpCompositeConstruct %v4float %57 %57 %57 %57 + OpStore %OutColor %47 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(assembly, true); +} + +TEST_F(VectorDCETest, VectorIntoCompositeConstruct) { + const std::string text = R"(OpCapability Linkage +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "EntryPoint_Main" %2 %3 +OpExecutionMode %1 OriginUpperLeft +OpDecorate %2 Location 0 +OpDecorate %_struct_4 Block +OpDecorate %3 Location 0 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%mat4v4float = OpTypeMatrix %v4float 4 +%_ptr_Function_mat4v4float = OpTypePointer Function %mat4v4float +%v3float = OpTypeVector %float 3 +%_ptr_Function_v3float = OpTypePointer Function %v3float +%_struct_14 = OpTypeStruct %v2float %mat4v4float %v3float %v2float %v4float +%_ptr_Function__struct_14 = OpTypePointer Function %_struct_14 +%void = OpTypeVoid +%int = OpTypeInt 32 1 +%int_2 = OpConstant %int 2 +%int_1 = OpConstant %int 1 +%int_4 = OpConstant %int 4 +%int_0 = OpConstant %int 0 +%int_3 = OpConstant %int 3 +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%_ptr_Input_v2float = OpTypePointer Input %v2float +%2 = OpVariable %_ptr_Input_v2float Input +%_ptr_Output_v2float = OpTypePointer Output %v2float +%_struct_4 = OpTypeStruct %v2float +%_ptr_Output__struct_4 = OpTypePointer Output %_struct_4 +%3 = OpVariable %_ptr_Output__struct_4 Output +%28 = OpTypeFunction %void +%29 = OpConstantComposite %v2float %float_0 %float_0 +%30 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%31 = OpConstantComposite %mat4v4float %30 %30 %30 %30 +%32 = OpConstantComposite %v3float %float_0 %float_0 %float_0 +%1 = OpFunction %void None %28 +%33 = OpLabel +%34 = OpVariable %_ptr_Function_v4float Function +%35 = OpVariable %_ptr_Function__struct_14 Function +%36 = OpAccessChain %_ptr_Function_v2float %35 %int_0 +OpStore %36 %29 +%37 = OpAccessChain %_ptr_Function_mat4v4float %35 %int_1 +OpStore %37 %31 +%38 = OpAccessChain %_ptr_Function_v3float %35 %int_2 +OpStore %38 %32 +%39 = OpAccessChain %_ptr_Function_v2float %35 %int_3 +OpStore %39 %29 +%40 = OpAccessChain %_ptr_Function_v4float %35 %int_4 +OpStore %40 %30 +%41 = OpLoad %v2float %2 +OpStore %36 %41 +%42 = OpLoad %v3float %38 +%43 = OpCompositeConstruct %v4float %42 %float_1 +%44 = OpLoad %mat4v4float %37 +%45 = OpVectorTimesMatrix %v4float %43 %44 +OpStore %34 %45 +OpCopyMemory %40 %34 +OpCopyMemory %36 %39 +%46 = OpAccessChain %_ptr_Output_v2float %3 %int_0 +%47 = OpLoad %v2float %36 +OpStore %46 %47 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(VectorDCETest, NotAffectedByDebugValue) { + // It tests that an OpenCL.DebugInfo.100 DebugValue instruction does + // not change the vector DCE pass result. If the composite used for + // the value of DebugValue is killed, the DebugValue must be killed as well. + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In2 %In0 %In1 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +OpName %main "main" +OpName %In2 "In2" +OpName %In0 "In0" +OpName %In1 "In1" +OpName %OutColor "OutColor" +OpName %_Globals_ "_Globals_" +OpMemberName %_Globals_ 0 "g_b" +OpMemberName %_Globals_ 1 "g_n" +OpName %_ "" +OpDecorate %In2 Location 2 +OpDecorate %In0 Location 0 +OpDecorate %In1 Location 1 +OpDecorate %OutColor Location 0 +OpMemberDecorate %_Globals_ 0 Offset 0 +OpMemberDecorate %_Globals_ 1 Offset 4 +OpDecorate %_Globals_ Block +OpDecorate %_ DescriptorSet 0 +OpDecorate %_ Binding 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%In2 = OpVariable %_ptr_Input_v2float Input +%_ptr_Input_float = OpTypePointer Input %float +%In0 = OpVariable %_ptr_Input_float Input +%In1 = OpVariable %_ptr_Input_float Input +%uint = OpTypeInt 32 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%int = OpTypeInt 32 1 +%_Globals_ = OpTypeStruct %uint %int +%_ptr_Uniform__Globals_ = OpTypePointer Uniform %_Globals_ +%_ = OpVariable %_ptr_Uniform__Globals_ Uniform +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal +%main = OpFunction %void None %11 +%25 = OpLabel +%s = OpExtInst %void %ext DebugScope %dbg_main + +; CHECK: [[in2:%\w+]] = OpLoad %v2float %In2 +%26 = OpLoad %v2float %In2 +%27 = OpLoad %float %In0 +%28 = OpLoad %float %In1 +%29 = OpFAdd %float %27 %28 + +; CHECK: OpCompositeInsert %v2float {{%\w+}} [[in2]] 0 +; CHECK-NEXT: OpCompositeInsert %v2float {{%\w+}} [[in2]] 0 +; CHECK-NOT: DebugValue +%35 = OpCompositeInsert %v2float %29 %26 0 +%value = OpExtInst %void %ext DebugValue %dbg_f %35 %null_expr +%37 = OpCompositeInsert %v2float %float_0 %35 0 +%33 = OpVectorShuffle %v4float %37 %37 0 1 0 1 +OpStore %OutColor %33 +OpReturn +OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, true); +} + +TEST_F(VectorDCETest, RemoveDebugValueUsesKilledInstr) { + // It tests that the vector DCE pass removes the OpenCL.DebugInfo.100 + // DebugValue instruction using a killed instruction. + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +%ext = OpExtInstImport "OpenCL.DebugInfo.100" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %In0 %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +%file_name = OpString "test" +%float_name = OpString "float" +%main_name = OpString "main" +%f_name = OpString "f" +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpName %main "main" +OpName %In0 "In0" +OpName %OutColor "OutColor" +OpDecorate %In0 Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%In0 = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_32 = OpConstant %uint 32 +%_ptr_Input_float = OpTypePointer Input %float +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%v3float = OpTypeVector %float 3 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_20 = OpConstant %int 20 +%bool = OpTypeBool +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%23 = OpUndef %v3float +%null_expr = OpExtInst %void %ext DebugExpression +%src = OpExtInst %void %ext DebugSource %file_name +%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL +%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float +%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf +%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main +%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal +%main = OpFunction %void None %6 +%24 = OpLabel +%s0 = OpExtInst %void %ext DebugScope %dbg_main +%25 = OpAccessChain %_ptr_Input_float %In0 %uint_0 +%26 = OpLoad %float %25 +%27 = OpAccessChain %_ptr_Input_float %In0 %uint_1 +%28 = OpLoad %float %27 + +; CHECK: [[undef:%\w+]] = OpUndef %float +; CHECK-NOT: DebugValue +%value = OpExtInst %void %ext DebugValue %dbg_f %28 %null_expr +%29 = OpAccessChain %_ptr_Input_float %In0 %uint_2 +%30 = OpLoad %float %29 + +; CHECK: [[composite:%\w+]] = OpCompositeConstruct %v3float {{%\w+}} [[undef]] [[undef]] +; CHECK-NEXT: DebugValue {{%\w+}} [[composite]] +%31 = OpCompositeConstruct %v3float %30 %28 %26 +%value_live = OpExtInst %void %ext DebugValue %dbg_f %31 %null_expr +OpBranch %32 +%32 = OpLabel +%s1 = OpExtInst %void %ext DebugScope %dbg_main +%33 = OpPhi %v3float %31 %24 %34 %35 +%36 = OpPhi %int %int_0 %24 %37 %35 +OpLoopMerge %38 %35 None +OpBranch %39 +%39 = OpLabel +%s2 = OpExtInst %void %ext DebugScope %dbg_main +%40 = OpSLessThan %bool %36 %int_20 +OpBranchConditional %40 %41 %38 +%41 = OpLabel +%s3 = OpExtInst %void %ext DebugScope %dbg_main +%42 = OpCompositeExtract %float %33 0 +%43 = OpFAdd %float %42 %float_1 +%34 = OpCompositeInsert %v3float %43 %33 0 +OpBranch %35 +%35 = OpLabel +%s4 = OpExtInst %void %ext DebugScope %dbg_main +%37 = OpIAdd %int %36 %int_1 +OpBranch %32 +%38 = OpLabel +%s5 = OpExtInst %void %ext DebugScope %dbg_main +%44 = OpCompositeExtract %float %33 0 +%45 = OpCompositeConstruct %v4float %44 %44 %44 %44 +OpStore %OutColor %45 +OpReturn +OpFunctionEnd +)"; + + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/workaround1209_test.cpp b/third_party/spirv-tools/test/opt/workaround1209_test.cpp new file mode 100644 index 0000000..50d3c09 --- /dev/null +++ b/third_party/spirv-tools/test/opt/workaround1209_test.cpp @@ -0,0 +1,423 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using Workaround1209Test = PassTest<::testing::Test>; + +TEST_F(Workaround1209Test, RemoveOpUnreachableInLoop) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %texcoord %gl_VertexIndex %_ + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpName %texcoord "texcoord" + OpName %buf "buf" + OpMemberName %buf 0 "MVP" + OpMemberName %buf 1 "position" + OpMemberName %buf 2 "attr" + OpName %ubuf "ubuf" + OpName %gl_VertexIndex "gl_VertexIndex" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpDecorate %texcoord Location 0 + OpDecorate %_arr_v4float_uint_72 ArrayStride 16 + OpDecorate %_arr_v4float_uint_72_0 ArrayStride 16 + OpMemberDecorate %buf 0 ColMajor + OpMemberDecorate %buf 0 Offset 0 + OpMemberDecorate %buf 0 MatrixStride 16 + OpMemberDecorate %buf 1 Offset 64 + OpMemberDecorate %buf 2 Offset 1216 + OpDecorate %buf Block + OpDecorate %ubuf DescriptorSet 0 + OpDecorate %ubuf Binding 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %12 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %texcoord = OpVariable %_ptr_Output_v4float Output +%mat4v4float = OpTypeMatrix %v4float 4 + %uint = OpTypeInt 32 0 + %uint_72 = OpConstant %uint 72 +%_arr_v4float_uint_72 = OpTypeArray %v4float %uint_72 +%_arr_v4float_uint_72_0 = OpTypeArray %v4float %uint_72 + %buf = OpTypeStruct %mat4v4float %_arr_v4float_uint_72 %_arr_v4float_uint_72_0 +%_ptr_Uniform_buf = OpTypePointer Uniform %buf + %ubuf = OpVariable %_ptr_Uniform_buf Uniform + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float_1 = OpConstant %float 1 + %28 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %main = OpFunction %void None %12 + %29 = OpLabel + OpBranch %30 + %30 = OpLabel +; CHECK: OpLoopMerge [[merge:%[a-zA-Z_\d]+]] + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel +; CHECK: OpSelectionMerge [[sel_merge:%[a-zA-Z_\d]+]] + OpSelectionMerge %34 None + OpSwitch %int_1 %35 + %35 = OpLabel + %36 = OpLoad %int %gl_VertexIndex + %37 = OpAccessChain %_ptr_Uniform_v4float %ubuf %int_2 %36 + %38 = OpLoad %v4float %37 + OpStore %texcoord %38 + %39 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %39 %28 + OpBranch %31 +; CHECK: [[sel_merge]] = OpLabel + %34 = OpLabel +; CHECK-NEXT: OpBranch [[merge]] + OpUnreachable + %32 = OpLabel + OpBranch %30 + %31 = OpLabel + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(Workaround1209Test, RemoveOpUnreachableInNestedLoop) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "main" %3 %4 %5 + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %2 "main" + OpName %3 "texcoord" + OpName %6 "buf" + OpMemberName %6 0 "MVP" + OpMemberName %6 1 "position" + OpMemberName %6 2 "attr" + OpName %7 "ubuf" + OpName %4 "gl_VertexIndex" + OpName %8 "gl_PerVertex" + OpMemberName %8 0 "gl_Position" + OpName %5 "" + OpDecorate %3 Location 0 + OpDecorate %9 ArrayStride 16 + OpDecorate %10 ArrayStride 16 + OpMemberDecorate %6 0 ColMajor + OpMemberDecorate %6 0 Offset 0 + OpMemberDecorate %6 0 MatrixStride 16 + OpMemberDecorate %6 1 Offset 64 + OpMemberDecorate %6 2 Offset 1216 + OpDecorate %6 Block + OpDecorate %7 DescriptorSet 0 + OpDecorate %7 Binding 0 + OpDecorate %4 BuiltIn VertexIndex + OpMemberDecorate %8 0 BuiltIn Position + OpDecorate %8 Block + %11 = OpTypeVoid + %12 = OpTypeFunction %11 + %13 = OpTypeFloat 32 + %14 = OpTypeVector %13 4 + %15 = OpTypePointer Output %14 + %3 = OpVariable %15 Output + %16 = OpTypeMatrix %14 4 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 72 + %9 = OpTypeArray %14 %18 + %10 = OpTypeArray %14 %18 + %6 = OpTypeStruct %16 %9 %10 + %19 = OpTypePointer Uniform %6 + %7 = OpVariable %19 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 2 + %22 = OpTypePointer Input %20 + %4 = OpVariable %22 Input + %23 = OpTypePointer Uniform %14 + %8 = OpTypeStruct %14 + %24 = OpTypePointer Output %8 + %5 = OpVariable %24 Output + %25 = OpConstant %20 0 + %26 = OpConstant %20 1 + %27 = OpConstant %13 1 + %28 = OpConstantComposite %14 %27 %27 %27 %27 + %2 = OpFunction %11 None %12 + %29 = OpLabel + OpBranch %31 + %31 = OpLabel +; CHECK: OpLoopMerge + OpLoopMerge %32 %33 None + OpBranch %30 + %30 = OpLabel +; CHECK: OpLoopMerge [[merge:%[a-zA-Z_\d]+]] + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel +; CHECK: OpSelectionMerge [[sel_merge:%[a-zA-Z_\d]+]] + OpSelectionMerge %37 None + OpSwitch %26 %38 + %38 = OpLabel + %39 = OpLoad %20 %4 + %40 = OpAccessChain %23 %7 %21 %39 + %41 = OpLoad %14 %40 + OpStore %3 %41 + %42 = OpAccessChain %15 %5 %25 + OpStore %42 %28 + OpBranch %34 +; CHECK: [[sel_merge]] = OpLabel + %37 = OpLabel +; CHECK-NEXT: OpBranch [[merge]] + OpUnreachable + %35 = OpLabel + OpBranch %30 + %34 = OpLabel + OpBranch %32 + %33 = OpLabel + OpBranch %31 + %32 = OpLabel + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(Workaround1209Test, RemoveOpUnreachableInAdjacentLoops) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "main" %3 %4 %5 + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %2 "main" + OpName %3 "texcoord" + OpName %6 "buf" + OpMemberName %6 0 "MVP" + OpMemberName %6 1 "position" + OpMemberName %6 2 "attr" + OpName %7 "ubuf" + OpName %4 "gl_VertexIndex" + OpName %8 "gl_PerVertex" + OpMemberName %8 0 "gl_Position" + OpName %5 "" + OpDecorate %3 Location 0 + OpDecorate %9 ArrayStride 16 + OpDecorate %10 ArrayStride 16 + OpMemberDecorate %6 0 ColMajor + OpMemberDecorate %6 0 Offset 0 + OpMemberDecorate %6 0 MatrixStride 16 + OpMemberDecorate %6 1 Offset 64 + OpMemberDecorate %6 2 Offset 1216 + OpDecorate %6 Block + OpDecorate %7 DescriptorSet 0 + OpDecorate %7 Binding 0 + OpDecorate %4 BuiltIn VertexIndex + OpMemberDecorate %8 0 BuiltIn Position + OpDecorate %8 Block + %11 = OpTypeVoid + %12 = OpTypeFunction %11 + %13 = OpTypeFloat 32 + %14 = OpTypeVector %13 4 + %15 = OpTypePointer Output %14 + %3 = OpVariable %15 Output + %16 = OpTypeMatrix %14 4 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 72 + %9 = OpTypeArray %14 %18 + %10 = OpTypeArray %14 %18 + %6 = OpTypeStruct %16 %9 %10 + %19 = OpTypePointer Uniform %6 + %7 = OpVariable %19 Uniform + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 2 + %22 = OpTypePointer Input %20 + %4 = OpVariable %22 Input + %23 = OpTypePointer Uniform %14 + %8 = OpTypeStruct %14 + %24 = OpTypePointer Output %8 + %5 = OpVariable %24 Output + %25 = OpConstant %20 0 + %26 = OpConstant %20 1 + %27 = OpConstant %13 1 + %28 = OpConstantComposite %14 %27 %27 %27 %27 + %2 = OpFunction %11 None %12 + %29 = OpLabel + OpBranch %30 + %30 = OpLabel +; CHECK: OpLoopMerge [[merge1:%[a-zA-Z_\d]+]] + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel +; CHECK: OpSelectionMerge [[sel_merge1:%[a-zA-Z_\d]+]] + OpSelectionMerge %34 None + OpSwitch %26 %35 + %35 = OpLabel + %36 = OpLoad %20 %4 + %37 = OpAccessChain %23 %7 %21 %36 + %38 = OpLoad %14 %37 + OpStore %3 %38 + %39 = OpAccessChain %15 %5 %25 + OpStore %39 %28 + OpBranch %31 +; CHECK: [[sel_merge1]] = OpLabel + %34 = OpLabel +; CHECK-NEXT: OpBranch [[merge1]] + OpUnreachable + %32 = OpLabel + OpBranch %30 + %31 = OpLabel +; CHECK: OpLoopMerge [[merge2:%[a-zA-Z_\d]+]] + OpLoopMerge %40 %41 None + OpBranch %42 + %42 = OpLabel +; CHECK: OpSelectionMerge [[sel_merge2:%[a-zA-Z_\d]+]] + OpSelectionMerge %43 None + OpSwitch %26 %44 + %44 = OpLabel + %45 = OpLoad %20 %4 + %46 = OpAccessChain %23 %7 %21 %45 + %47 = OpLoad %14 %46 + OpStore %3 %47 + %48 = OpAccessChain %15 %5 %25 + OpStore %48 %28 + OpBranch %40 +; CHECK: [[sel_merge2]] = OpLabel + %43 = OpLabel +; CHECK-NEXT: OpBranch [[merge2]] + OpUnreachable + %41 = OpLabel + OpBranch %31 + %40 = OpLabel + OpReturn + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} + +TEST_F(Workaround1209Test, LeaveUnreachableNotInLoop) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %texcoord %gl_VertexIndex %_ + OpSource GLSL 400 + OpSourceExtension "GL_ARB_separate_shader_objects" + OpSourceExtension "GL_ARB_shading_language_420pack" + OpName %main "main" + OpName %texcoord "texcoord" + OpName %buf "buf" + OpMemberName %buf 0 "MVP" + OpMemberName %buf 1 "position" + OpMemberName %buf 2 "attr" + OpName %ubuf "ubuf" + OpName %gl_VertexIndex "gl_VertexIndex" + OpName %gl_PerVertex "gl_PerVertex" + OpMemberName %gl_PerVertex 0 "gl_Position" + OpName %_ "" + OpDecorate %texcoord Location 0 + OpDecorate %_arr_v4float_uint_72 ArrayStride 16 + OpDecorate %_arr_v4float_uint_72_0 ArrayStride 16 + OpMemberDecorate %buf 0 ColMajor + OpMemberDecorate %buf 0 Offset 0 + OpMemberDecorate %buf 0 MatrixStride 16 + OpMemberDecorate %buf 1 Offset 64 + OpMemberDecorate %buf 2 Offset 1216 + OpDecorate %buf Block + OpDecorate %ubuf DescriptorSet 0 + OpDecorate %ubuf Binding 0 + OpDecorate %gl_VertexIndex BuiltIn VertexIndex + OpMemberDecorate %gl_PerVertex 0 BuiltIn Position + OpDecorate %gl_PerVertex Block + %void = OpTypeVoid + %12 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %texcoord = OpVariable %_ptr_Output_v4float Output +%mat4v4float = OpTypeMatrix %v4float 4 + %uint = OpTypeInt 32 0 + %uint_72 = OpConstant %uint 72 +%_arr_v4float_uint_72 = OpTypeArray %v4float %uint_72 +%_arr_v4float_uint_72_0 = OpTypeArray %v4float %uint_72 + %buf = OpTypeStruct %mat4v4float %_arr_v4float_uint_72 %_arr_v4float_uint_72_0 +%_ptr_Uniform_buf = OpTypePointer Uniform %buf + %ubuf = OpVariable %_ptr_Uniform_buf Uniform + %int = OpTypeInt 32 1 + %int_2 = OpConstant %int 2 +%_ptr_Input_int = OpTypePointer Input %int +%gl_VertexIndex = OpVariable %_ptr_Input_int Input +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%gl_PerVertex = OpTypeStruct %v4float +%_ptr_Output_gl_PerVertex = OpTypePointer Output %gl_PerVertex + %_ = OpVariable %_ptr_Output_gl_PerVertex Output + %int_0 = OpConstant %int 0 + %int_1 = OpConstant %int 1 + %float_1 = OpConstant %float 1 + %28 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %main = OpFunction %void None %12 + %29 = OpLabel + OpBranch %30 + %30 = OpLabel + OpSelectionMerge %34 None + OpSwitch %int_1 %35 + %35 = OpLabel + %36 = OpLoad %int %gl_VertexIndex + %37 = OpAccessChain %_ptr_Uniform_v4float %ubuf %int_2 %36 + %38 = OpLoad %v4float %37 + OpStore %texcoord %38 + %39 = OpAccessChain %_ptr_Output_v4float %_ %int_0 + OpStore %39 %28 + OpReturn + %34 = OpLabel +; CHECK: OpUnreachable + OpUnreachable + OpFunctionEnd)"; + + SinglePassRunAndMatch(text, false); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/wrap_opkill_test.cpp b/third_party/spirv-tools/test/opt/wrap_opkill_test.cpp new file mode 100644 index 0000000..e40d701 --- /dev/null +++ b/third_party/spirv-tools/test/opt/wrap_opkill_test.cpp @@ -0,0 +1,955 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using WrapOpKillTest = PassTest<::testing::Test>; + +TEST_F(WrapOpKillTest, SingleOpKill) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, MultipleOpKillInSameFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill]] +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %true %17 %18 + %17 = OpLabel + OpKill + %18 = OpLabel + OpKill + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, MultipleOpKillInDifferentFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]] +; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]] +; CHECK: [[orig_kill1]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK: [[orig_kill2]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill]] +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpBranchConditional %true %12 %9 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + %13 = OpFunctionCall %void %14 + %15 = OpFunctionCall %void %16 + OpBranch %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %void None %4 + %17 = OpLabel + OpKill + OpFunctionEnd + %16 = OpFunction %void None %4 + %18 = OpLabel + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, SingleOpTerminateInvocation) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpTerminateInvocation +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpTerminateInvocation + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, MultipleTerminateInvocationInSameFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill]] +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpTerminateInvocation +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %true %17 %18 + %17 = OpLabel + OpTerminateInvocation + %18 = OpLabel + OpTerminateInvocation + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, MultipleOpTerminateInvocationDifferentFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]] +; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]] +; CHECK: [[orig_kill1]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK: [[orig_kill2]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill]] +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpTerminateInvocation +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpBranchConditional %true %12 %9 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + %13 = OpFunctionCall %void %14 + %15 = OpFunctionCall %void %16 + OpBranch %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %void None %4 + %17 = OpLabel + OpTerminateInvocation + OpFunctionEnd + %16 = OpFunction %void None %4 + %18 = OpLabel + OpTerminateInvocation + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, KillAndTerminateInvocationSameFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpSelectionMerge +; CHECK-NEXT: OpBranchConditional +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_terminate:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: [[new_terminate]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpTerminateInvocation +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %true %17 %18 + %17 = OpLabel + OpKill + %18 = OpLabel + OpTerminateInvocation + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, KillAndTerminateInvocationDifferentFunc) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill1:%\w+]] +; CHECK-NEXT: OpFunctionCall %void [[orig_kill2:%\w+]] +; CHECK: [[orig_kill1]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_terminate:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK: [[orig_kill2]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd +; CHECK-NEXT: [[new_terminate]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpTerminateInvocation +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpBranchConditional %true %12 %9 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + %13 = OpFunctionCall %void %14 + %15 = OpFunctionCall %void %16 + OpBranch %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %void None %4 + %17 = OpLabel + OpTerminateInvocation + OpFunctionEnd + %16 = OpFunction %void None %4 + %18 = OpLabel + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, FuncWithReturnValue) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %int [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NEXT: [[undef:%\w+]] = OpUndef %int +; CHECK-NEXT: OpReturnValue [[undef]] +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %func_type = OpTypeFunction %int + %bool = OpTypeBool + %true = OpConstantTrue %bool + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %int %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %int None %func_type + %15 = OpLabel + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, IdBoundOverflow1) { + const std::string text = R"( +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %2 None %3 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %10 %11 None +OpBranch %12 +%12 = OpLabel +OpBranchConditional %true %13 %10 +%13 = OpLabel +OpBranch %11 +%11 = OpLabel +%14 = OpFunctionCall %void %kill_ +OpBranch %9 +%10 = OpLabel +OpReturn +OpFunctionEnd +%kill_ = OpFunction %2 Pure|Const %3 +%4194302 = OpLabel +OpKill +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, IdBoundOverflow2) { + const std::string text = R"( +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %2 None %3 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %10 %11 None +OpBranch %12 +%12 = OpLabel +OpBranchConditional %true %13 %10 +%13 = OpLabel +OpBranch %11 +%11 = OpLabel +%14 = OpFunctionCall %void %kill_ +OpBranch %9 +%10 = OpLabel +OpReturn +OpFunctionEnd +%kill_ = OpFunction %2 Pure|Const %3 +%4194301 = OpLabel +OpKill +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, IdBoundOverflow3) { + const std::string text = R"( +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %2 None %3 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %10 %11 None +OpBranch %12 +%12 = OpLabel +OpBranchConditional %true %13 %10 +%13 = OpLabel +OpBranch %11 +%11 = OpLabel +%14 = OpFunctionCall %void %kill_ +OpBranch %9 +%10 = OpLabel +OpReturn +OpFunctionEnd +%kill_ = OpFunction %2 Pure|Const %3 +%4194300 = OpLabel +OpKill +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, IdBoundOverflow4) { + const std::string text = R"( +OpCapability DerivativeControl +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %2 Location 539091968 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %2 None %3 +%8 = OpLabel +OpBranch %9 +%9 = OpLabel +OpLoopMerge %10 %11 None +OpBranch %12 +%12 = OpLabel +OpBranchConditional %true %13 %10 +%13 = OpLabel +OpBranch %11 +%11 = OpLabel +%14 = OpFunctionCall %void %kill_ +OpBranch %9 +%10 = OpLabel +OpReturn +OpFunctionEnd +%kill_ = OpFunction %2 Inline|Pure|Const %3 +%4194302 = OpLabel +OpKill +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, IdBoundOverflow5) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + OpDecorate %void Location 539091968 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %_struct_5 = OpTypeStruct %float %float + %_struct_6 = OpTypeStruct %_struct_5 +%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6 +%_ptr_Output_float = OpTypePointer Output %float + %9 = OpTypeFunction %_struct_5 %_ptr_Function__struct_6 + %bool = OpTypeBool + %true = OpConstantTrue %bool + %1 = OpFunction %void None %3 + %12 = OpLabel + %13 = OpVariable %_ptr_Function__struct_6 Function + OpBranch %14 + %14 = OpLabel + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + OpBranchConditional %true %18 %15 + %18 = OpLabel + OpBranch %16 + %16 = OpLabel + %19 = OpFunctionCall %void %20 + %21 = OpFunctionCall %_struct_5 %22 %13 + OpBranch %14 + %15 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %void Inline|Pure|Const %3 + %23 = OpLabel + %24 = OpVariable %_ptr_Function__struct_6 Function + %25 = OpFunctionCall %_struct_5 %26 %24 + OpKill + OpFunctionEnd + %26 = OpFunction %_struct_5 None %9 + %27 = OpLabel + OpUnreachable + OpFunctionEnd + %22 = OpFunction %_struct_5 Inline %9 + %4194295 = OpLabel + OpKill + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + + std::vector messages = { + {SPV_MSG_ERROR, "", 0, 0, "ID overflow. Try running compact-ids."}}; + SetMessageConsumer(GetTestMessageConsumer(messages)); + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::Failure, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, SkipEntryPoint) { + const std::string text = R"( +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" +OpExecutionMode %4 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpFunction %2 Pure|Const %3 +%5 = OpLabel +OpKill +OpFunctionEnd +)"; + + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, SkipFunctionNotInContinue) { + const std::string text = R"( +OpCapability GeometryStreams +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%main = OpFunction %2 None %3 +%6 = OpLabel +%7 = OpFunctionCall %void %4 +OpReturn +OpFunctionEnd +%4 = OpFunction %2 Pure|Const %3 +%5 = OpLabel +OpKill +OpFunctionEnd +)"; + + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, SetParentBlock) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %merge %continue None +OpBranchConditional %undef %merge %continue +%continue = OpLabel +%call = OpFunctionCall %void %kill_func +OpBranch %loop +%merge = OpLabel +OpReturn +OpFunctionEnd +%kill_func = OpFunction %void None %void_fn +%kill_entry = OpLabel +OpKill +OpFunctionEnd +)"; + + auto result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); + result = SinglePassRunToBinary(text, true); + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); +} + +TEST_F(WrapOpKillTest, KillInSingleBlockLoop) { + const std::string text = R"( +; CHECK: OpFunction %void +; CHECK: OpFunction %void +; CHECK-NOT: OpKill +; CHECK: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK-NOT: OpKill +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %bool = OpTypeBool + %undef = OpUndef %bool + %void_fn = OpTypeFunction %void + %main = OpFunction %void None %void_fn +%main_entry = OpLabel + OpBranch %loop + %loop = OpLabel + %call = OpFunctionCall %void %sub + OpLoopMerge %exit %loop None + OpBranchConditional %undef %loop %exit + %exit = OpLabel + OpReturn + OpFunctionEnd + %sub = OpFunction %void None %void_fn + %sub_entry = OpLabel + OpSelectionMerge %ret None + OpBranchConditional %undef %kill %ret + %kill = OpLabel + OpKill + %ret = OpLabel + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(WrapOpKillTest, DebugInfoSimple) { + const std::string text = R"( +; CHECK: OpEntryPoint Fragment [[main:%\w+]] +; CHECK: [[main]] = OpFunction +; CHECK: OpFunctionCall %void [[orig_kill:%\w+]] +; CHECK: [[orig_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: {{%\d+}} = OpExtInst %void [[ext:%\d+]] DebugScope +; CHECK-NEXT: OpLine [[file:%\d+]] 100 200 +; CHECK-NEXT: OpFunctionCall %void [[new_kill:%\w+]] +; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugNoScope +; CHECK-NEXT: OpReturn +; CHECK: [[new_kill]] = OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: OpKill +; CHECK-NEXT: OpFunctionEnd + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + %2 = OpString "File name" + OpSource GLSL 330 + OpName %main "main" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %3 = OpExtInst %void %1 DebugSource %2 + %4 = OpExtInst %void %1 DebugCompilationUnit 0 0 %3 GLSL + %main = OpFunction %void None %5 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %true %13 %10 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + %14 = OpFunctionCall %void %kill_ + OpBranch %9 + %10 = OpLabel + OpReturn + OpFunctionEnd + %kill_ = OpFunction %void None %5 + %15 = OpLabel + %16 = OpExtInst %void %1 DebugScope %4 + OpLine %2 100 200 + OpKill + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/parse_number_test.cpp b/third_party/spirv-tools/test/parse_number_test.cpp new file mode 100644 index 0000000..c99205c --- /dev/null +++ b/third_party/spirv-tools/test/parse_number_test.cpp @@ -0,0 +1,970 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/util/parse_number.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace utils { +namespace { + +using testing::Eq; +using testing::IsNull; +using testing::NotNull; + +TEST(ParseNarrowSignedIntegers, Sample) { + int16_t i16; + + EXPECT_FALSE(ParseNumber(nullptr, &i16)); + EXPECT_FALSE(ParseNumber("", &i16)); + EXPECT_FALSE(ParseNumber("0=", &i16)); + + EXPECT_TRUE(ParseNumber("0", &i16)); + EXPECT_EQ(0, i16); + EXPECT_TRUE(ParseNumber("32767", &i16)); + EXPECT_EQ(32767, i16); + EXPECT_TRUE(ParseNumber("-32768", &i16)); + EXPECT_EQ(-32768, i16); + EXPECT_TRUE(ParseNumber("-0", &i16)); + EXPECT_EQ(0, i16); + + // These are out of range, so they should return an error. + // The error code depends on whether this is an optional value. + EXPECT_FALSE(ParseNumber("32768", &i16)); + EXPECT_FALSE(ParseNumber("65535", &i16)); + + // Check hex parsing. + EXPECT_TRUE(ParseNumber("0x7fff", &i16)); + EXPECT_EQ(32767, i16); + // This is out of range. + EXPECT_FALSE(ParseNumber("0xffff", &i16)); +} + +TEST(ParseNarrowUnsignedIntegers, Sample) { + uint16_t u16; + + EXPECT_FALSE(ParseNumber(nullptr, &u16)); + EXPECT_FALSE(ParseNumber("", &u16)); + EXPECT_FALSE(ParseNumber("0=", &u16)); + + EXPECT_TRUE(ParseNumber("0", &u16)); + EXPECT_EQ(0, u16); + EXPECT_TRUE(ParseNumber("65535", &u16)); + EXPECT_EQ(65535, u16); + EXPECT_FALSE(ParseNumber("65536", &u16)); + + // We don't care about -0 since it's rejected at a higher level. + EXPECT_FALSE(ParseNumber("-1", &u16)); + EXPECT_TRUE(ParseNumber("0xffff", &u16)); + EXPECT_EQ(0xffff, u16); + EXPECT_FALSE(ParseNumber("0x10000", &u16)); +} + +TEST(ParseSignedIntegers, Sample) { + int32_t i32; + + // Invalid parse. + EXPECT_FALSE(ParseNumber(nullptr, &i32)); + EXPECT_FALSE(ParseNumber("", &i32)); + EXPECT_FALSE(ParseNumber("0=", &i32)); + + // Decimal values. + EXPECT_TRUE(ParseNumber("0", &i32)); + EXPECT_EQ(0, i32); + EXPECT_TRUE(ParseNumber("2147483647", &i32)); + EXPECT_EQ(std::numeric_limits::max(), i32); + EXPECT_FALSE(ParseNumber("2147483648", &i32)); + EXPECT_TRUE(ParseNumber("-0", &i32)); + EXPECT_EQ(0, i32); + EXPECT_TRUE(ParseNumber("-1", &i32)); + EXPECT_EQ(-1, i32); + EXPECT_TRUE(ParseNumber("-2147483648", &i32)); + EXPECT_EQ(std::numeric_limits::min(), i32); + + // Hex values. + EXPECT_TRUE(ParseNumber("0x7fffffff", &i32)); + EXPECT_EQ(std::numeric_limits::max(), i32); + EXPECT_FALSE(ParseNumber("0x80000000", &i32)); + EXPECT_TRUE(ParseNumber("-0x000", &i32)); + EXPECT_EQ(0, i32); + EXPECT_TRUE(ParseNumber("-0x001", &i32)); + EXPECT_EQ(-1, i32); + EXPECT_TRUE(ParseNumber("-0x80000000", &i32)); + EXPECT_EQ(std::numeric_limits::min(), i32); +} + +TEST(ParseUnsignedIntegers, Sample) { + uint32_t u32; + + // Invalid parse. + EXPECT_FALSE(ParseNumber(nullptr, &u32)); + EXPECT_FALSE(ParseNumber("", &u32)); + EXPECT_FALSE(ParseNumber("0=", &u32)); + + // Valid values. + EXPECT_TRUE(ParseNumber("0", &u32)); + EXPECT_EQ(0u, u32); + EXPECT_TRUE(ParseNumber("4294967295", &u32)); + EXPECT_EQ(std::numeric_limits::max(), u32); + EXPECT_FALSE(ParseNumber("4294967296", &u32)); + + // Hex values. + EXPECT_TRUE(ParseNumber("0xffffffff", &u32)); + EXPECT_EQ(std::numeric_limits::max(), u32); + + // We don't care about -0 since it's rejected at a higher level. + EXPECT_FALSE(ParseNumber("-1", &u32)); +} + +TEST(ParseWideSignedIntegers, Sample) { + int64_t i64; + EXPECT_FALSE(ParseNumber(nullptr, &i64)); + EXPECT_FALSE(ParseNumber("", &i64)); + EXPECT_FALSE(ParseNumber("0=", &i64)); + EXPECT_TRUE(ParseNumber("0", &i64)); + EXPECT_EQ(0, i64); + EXPECT_TRUE(ParseNumber("0x7fffffffffffffff", &i64)); + EXPECT_EQ(0x7fffffffffffffff, i64); + EXPECT_TRUE(ParseNumber("-0", &i64)); + EXPECT_EQ(0, i64); + EXPECT_TRUE(ParseNumber("-1", &i64)); + EXPECT_EQ(-1, i64); +} + +TEST(ParseWideUnsignedIntegers, Sample) { + uint64_t u64; + EXPECT_FALSE(ParseNumber(nullptr, &u64)); + EXPECT_FALSE(ParseNumber("", &u64)); + EXPECT_FALSE(ParseNumber("0=", &u64)); + EXPECT_TRUE(ParseNumber("0", &u64)); + EXPECT_EQ(0u, u64); + EXPECT_TRUE(ParseNumber("0xffffffffffffffff", &u64)); + EXPECT_EQ(0xffffffffffffffffULL, u64); + + // We don't care about -0 since it's rejected at a higher level. + EXPECT_FALSE(ParseNumber("-1", &u64)); +} + +TEST(ParseFloat, Sample) { + float f; + + EXPECT_FALSE(ParseNumber(nullptr, &f)); + EXPECT_FALSE(ParseNumber("", &f)); + EXPECT_FALSE(ParseNumber("0=", &f)); + + // These values are exactly representatble. + EXPECT_TRUE(ParseNumber("0", &f)); + EXPECT_EQ(0.0f, f); + EXPECT_TRUE(ParseNumber("42", &f)); + EXPECT_EQ(42.0f, f); + EXPECT_TRUE(ParseNumber("2.5", &f)); + EXPECT_EQ(2.5f, f); + EXPECT_TRUE(ParseNumber("-32.5", &f)); + EXPECT_EQ(-32.5f, f); + EXPECT_TRUE(ParseNumber("1e38", &f)); + EXPECT_EQ(1e38f, f); + EXPECT_TRUE(ParseNumber("-1e38", &f)); + EXPECT_EQ(-1e38f, f); +} + +TEST(ParseFloat, Overflow) { + // The assembler parses using HexFloat>. Make + // sure that succeeds for in-range values, and fails for out of + // range values. When it does overflow, the value is set to the + // nearest finite value, matching C++11 behavior for operator>> + // on floating point. + HexFloat> f(0.0f); + + EXPECT_TRUE(ParseNumber("1e38", &f)); + EXPECT_EQ(1e38f, f.value().getAsFloat()); + EXPECT_TRUE(ParseNumber("-1e38", &f)); + EXPECT_EQ(-1e38f, f.value().getAsFloat()); + EXPECT_FALSE(ParseNumber("1e40", &f)); + EXPECT_FALSE(ParseNumber("-1e40", &f)); + EXPECT_FALSE(ParseNumber("1e400", &f)); + EXPECT_FALSE(ParseNumber("-1e400", &f)); +} + +TEST(ParseDouble, Sample) { + double f; + + EXPECT_FALSE(ParseNumber(nullptr, &f)); + EXPECT_FALSE(ParseNumber("", &f)); + EXPECT_FALSE(ParseNumber("0=", &f)); + + // These values are exactly representatble. + EXPECT_TRUE(ParseNumber("0", &f)); + EXPECT_EQ(0.0, f); + EXPECT_TRUE(ParseNumber("42", &f)); + EXPECT_EQ(42.0, f); + EXPECT_TRUE(ParseNumber("2.5", &f)); + EXPECT_EQ(2.5, f); + EXPECT_TRUE(ParseNumber("-32.5", &f)); + EXPECT_EQ(-32.5, f); + EXPECT_TRUE(ParseNumber("1e38", &f)); + EXPECT_EQ(1e38, f); + EXPECT_TRUE(ParseNumber("-1e38", &f)); + EXPECT_EQ(-1e38, f); + // These are out of range for 32-bit float, but in range for 64-bit float. + EXPECT_TRUE(ParseNumber("1e40", &f)); + EXPECT_EQ(1e40, f); + EXPECT_TRUE(ParseNumber("-1e40", &f)); + EXPECT_EQ(-1e40, f); +} + +TEST(ParseDouble, Overflow) { + // The assembler parses using HexFloat>. Make + // sure that succeeds for in-range values, and fails for out of + // range values. When it does overflow, the value is set to the + // nearest finite value, matching C++11 behavior for operator>> + // on floating point. + HexFloat> f(0.0); + + EXPECT_TRUE(ParseNumber("1e38", &f)); + EXPECT_EQ(1e38, f.value().getAsFloat()); + EXPECT_TRUE(ParseNumber("-1e38", &f)); + EXPECT_EQ(-1e38, f.value().getAsFloat()); + EXPECT_TRUE(ParseNumber("1e40", &f)); + EXPECT_EQ(1e40, f.value().getAsFloat()); + EXPECT_TRUE(ParseNumber("-1e40", &f)); + EXPECT_EQ(-1e40, f.value().getAsFloat()); + EXPECT_FALSE(ParseNumber("1e400", &f)); + EXPECT_FALSE(ParseNumber("-1e400", &f)); +} + +TEST(ParseFloat16, Overflow) { + // The assembler parses using HexFloat>. Make + // sure that succeeds for in-range values, and fails for out of + // range values. When it does overflow, the value is set to the + // nearest finite value, matching C++11 behavior for operator>> + // on floating point. + HexFloat> f(0); + + EXPECT_FALSE(ParseNumber(nullptr, &f)); + EXPECT_TRUE(ParseNumber("-0.0", &f)); + EXPECT_EQ(uint16_t{0x8000}, f.value().getAsFloat().get_value()); + EXPECT_TRUE(ParseNumber("1.0", &f)); + EXPECT_EQ(uint16_t{0x3c00}, f.value().getAsFloat().get_value()); + + // Overflows 16-bit but not 32-bit + EXPECT_FALSE(ParseNumber("1e38", &f)); + EXPECT_FALSE(ParseNumber("-1e38", &f)); + + // Overflows 32-bit but not 64-bit + EXPECT_FALSE(ParseNumber("1e40", &f)); + EXPECT_FALSE(ParseNumber("-1e40", &f)); + + // Overflows 64-bit + EXPECT_FALSE(ParseNumber("1e400", &f)); + EXPECT_FALSE(ParseNumber("-1e400", &f)); +} + +void AssertEmitFunc(uint32_t) { + ASSERT_FALSE(true) + << "Should not call emit() function when the number can not be parsed."; + return; +} + +TEST(ParseAndEncodeNarrowSignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {16, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber(nullptr, type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("The given text is a nullptr", err_msg); + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid signed integer literal: -", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); +} + +TEST(ParseAndEncodeNarrowSignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {16, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("32768", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer 32768 does not fit in a 16-bit signed integer", err_msg); + rc = ParseAndEncodeIntegerNumber("-32769", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer -32769 does not fit in a 16-bit signed integer", err_msg); +} + +TEST(ParseAndEncodeNarrowSignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {16, SPV_NUMBER_SIGNED_INT}; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber( + "0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "-0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "32767", type, [](uint32_t word) { EXPECT_EQ(0x00007fffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "-32768", type, [](uint32_t word) { EXPECT_EQ(0xffff8000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber( + "0x7fff", type, [](uint32_t word) { EXPECT_EQ(0x00007fffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "0xffff", type, [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +TEST(ParseAndEncodeNarrowUnsignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {16, SPV_NUMBER_UNSIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber(nullptr, type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("The given text is a nullptr", err_msg); + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); + rc = ParseAndEncodeIntegerNumber("-0", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("-1", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); +} + +TEST(ParseAndEncodeNarrowUnsignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg("random content"); + NumberType type = {16, SPV_NUMBER_UNSIGNED_INT}; + + // Overflow + rc = ParseAndEncodeIntegerNumber("65536", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer 65536 does not fit in a 16-bit unsigned integer", err_msg); +} + +TEST(ParseAndEncodeNarrowUnsignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {16, SPV_NUMBER_UNSIGNED_INT}; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber( + "0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "65535", type, [](uint32_t word) { EXPECT_EQ(0x0000ffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber( + "0xffff", type, [](uint32_t word) { EXPECT_EQ(0x0000ffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +TEST(ParseAndEncodeSignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber(nullptr, type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("The given text is a nullptr", err_msg); + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid signed integer literal: -", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); +} + +TEST(ParseAndEncodeSignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + rc = + ParseAndEncodeIntegerNumber("2147483648", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer 2147483648 does not fit in a 32-bit signed integer", + err_msg); + rc = ParseAndEncodeIntegerNumber("-2147483649", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer -2147483649 does not fit in a 32-bit signed integer", + err_msg); +} + +TEST(ParseAndEncodeSignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber( + "0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "-0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "2147483647", type, [](uint32_t word) { EXPECT_EQ(0x7fffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "-2147483648", type, [](uint32_t word) { EXPECT_EQ(0x80000000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber( + "0x7fffffff", type, [](uint32_t word) { EXPECT_EQ(0x7fffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "0xffffffff", type, [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +TEST(ParseAndEncodeUnsignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_UNSIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber(nullptr, type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("The given text is a nullptr", err_msg); + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); + rc = ParseAndEncodeIntegerNumber("-0", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("-1", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); +} + +TEST(ParseAndEncodeUnsignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg("random content"); + NumberType type = {32, SPV_NUMBER_UNSIGNED_INT}; + + // Overflow + rc = + ParseAndEncodeIntegerNumber("4294967296", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Integer 4294967296 does not fit in a 32-bit unsigned integer", + err_msg); +} + +TEST(ParseAndEncodeUnsignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {32, SPV_NUMBER_UNSIGNED_INT}; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber( + "0", type, [](uint32_t word) { EXPECT_EQ(0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeIntegerNumber( + "4294967295", type, [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber( + "0xffffffff", type, [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +TEST(ParseAndEncodeWideSignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber(nullptr, type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("The given text is a nullptr", err_msg); + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid signed integer literal: -", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); +} + +TEST(ParseAndEncodeWideSignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("9223372036854775808", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ( + "Integer 9223372036854775808 does not fit in a 64-bit signed integer", + err_msg); + rc = ParseAndEncodeIntegerNumber("-9223372036854775809", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid signed integer literal: -9223372036854775809", err_msg); +} + +TEST(ParseAndEncodeWideSignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {64, SPV_NUMBER_SIGNED_INT}; + std::vector word_buffer; + auto emit = [&word_buffer](uint32_t word) { + if (word_buffer.size() == 2) word_buffer.clear(); + word_buffer.push_back(word); + }; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber("0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0u})); + rc = ParseAndEncodeIntegerNumber("-0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0u})); + rc = ParseAndEncodeIntegerNumber("9223372036854775807", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0x7fffffffu})); + rc = ParseAndEncodeIntegerNumber("-9223372036854775808", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x80000000u})); + rc = ParseAndEncodeIntegerNumber("-1", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0xffffffffu})); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber("0x7fffffffffffffff", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0x7fffffffu})); + rc = ParseAndEncodeIntegerNumber("0xffffffffffffffff", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0xffffffffu})); +} + +TEST(ParseAndEncodeWideUnsignedIntegers, Invalid) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_UNSIGNED_INT}; + + // Invalid + rc = ParseAndEncodeIntegerNumber(nullptr, type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("The given text is a nullptr", err_msg); + rc = ParseAndEncodeIntegerNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: ", err_msg); + rc = ParseAndEncodeIntegerNumber("=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: =", err_msg); + rc = ParseAndEncodeIntegerNumber("-", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 0=", err_msg); + rc = ParseAndEncodeIntegerNumber("-0", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); + rc = ParseAndEncodeIntegerNumber("-1", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("Cannot put a negative number in an unsigned literal", err_msg); +} + +TEST(ParseAndEncodeWideUnsignedIntegers, Overflow) { + // The error message should be overwritten after each parsing call. + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_UNSIGNED_INT}; + + // Overflow + rc = ParseAndEncodeIntegerNumber("18446744073709551616", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: 18446744073709551616", err_msg); +} + +TEST(ParseAndEncodeWideUnsignedIntegers, Success) { + // Don't care the error message in this case. + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + NumberType type = {64, SPV_NUMBER_UNSIGNED_INT}; + std::vector word_buffer; + auto emit = [&word_buffer](uint32_t word) { + if (word_buffer.size() == 2) word_buffer.clear(); + word_buffer.push_back(word); + }; + + // Zero, maximum, and minimum value + rc = ParseAndEncodeIntegerNumber("0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0u})); + rc = ParseAndEncodeIntegerNumber("18446744073709551615", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0xffffffffu})); + + // Hex parsing + rc = ParseAndEncodeIntegerNumber("0xffffffffffffffff", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xffffffffu, 0xffffffffu})); +} + +TEST(ParseAndEncodeIntegerNumber, TypeNone) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_NONE}; + + rc = ParseAndEncodeIntegerNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("The expected type is not a integer type", err_msg); +} + +TEST(ParseAndEncodeIntegerNumber, InvalidCaseWithoutErrorMessageString) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber("invalid", type, AssertEmitFunc, nullptr); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); +} + +TEST(ParseAndEncodeIntegerNumber, DoNotTouchErrorMessageStringOnSuccess) { + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + std::string err_msg("random content"); + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + rc = ParseAndEncodeIntegerNumber( + "100", type, [](uint32_t word) { EXPECT_EQ(100u, word); }, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_EQ("random content", err_msg); +} + +TEST(ParseAndEncodeFloat, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_FLOATING}; + + // Invalid + rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: ", err_msg); + rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: 0=", err_msg); + + // Representative samples + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-0.0", type, [](uint32_t word) { EXPECT_EQ(0x80000000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "42", type, [](uint32_t word) { EXPECT_EQ(0x42280000u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "2.5", type, [](uint32_t word) { EXPECT_EQ(0x40200000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-32.5", type, [](uint32_t word) { EXPECT_EQ(0xc2020000u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "1e38", type, [](uint32_t word) { EXPECT_EQ(0x7e967699u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-1e38", type, [](uint32_t word) { EXPECT_EQ(0xfe967699u, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Overflow + rc = + ParseAndEncodeFloatingPointNumber("1e40", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: 1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e40", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: -1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: 1e400", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 32-bit float literal: -1e400", err_msg); +} + +TEST(ParseAndEncodeDouble, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {64, SPV_NUMBER_FLOATING}; + std::vector word_buffer; + auto emit = [&word_buffer](uint32_t word) { + if (word_buffer.size() == 2) word_buffer.clear(); + word_buffer.push_back(word); + }; + + // Invalid + rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 64-bit float literal: ", err_msg); + rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 64-bit float literal: 0=", err_msg); + + // Representative samples + rc = ParseAndEncodeFloatingPointNumber("0.0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0u})); + rc = ParseAndEncodeFloatingPointNumber("-0.0", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x80000000u})); + rc = ParseAndEncodeFloatingPointNumber("42", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x40450000u})); + rc = ParseAndEncodeFloatingPointNumber("2.5", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x40040000u})); + rc = ParseAndEncodeFloatingPointNumber("32.5", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0u, 0x40404000u})); + rc = ParseAndEncodeFloatingPointNumber("1e38", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0x2a16a1b1u, 0x47d2ced3u})); + rc = ParseAndEncodeFloatingPointNumber("-1e38", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0x2a16a1b1u, 0xc7d2ced3u})); + rc = ParseAndEncodeFloatingPointNumber("1e40", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xf1c35ca5u, 0x483d6329u})); + rc = ParseAndEncodeFloatingPointNumber("-1e40", type, emit, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_THAT(word_buffer, Eq(std::vector{0xf1c35ca5u, 0xc83d6329u})); + + // Overflow + rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 64-bit float literal: 1e400", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 64-bit float literal: -1e400", err_msg); +} + +TEST(ParseAndEncodeFloat16, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {16, SPV_NUMBER_FLOATING}; + + // Invalid + rc = ParseAndEncodeFloatingPointNumber("", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: ", err_msg); + rc = ParseAndEncodeFloatingPointNumber("0=", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: 0=", err_msg); + + // Representative samples + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "-0.0", type, [](uint32_t word) { EXPECT_EQ(0x8000u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "1.0", type, [](uint32_t word) { EXPECT_EQ(0x3c00u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "2.5", type, [](uint32_t word) { EXPECT_EQ(0x4100u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + rc = ParseAndEncodeFloatingPointNumber( + "32.5", type, [](uint32_t word) { EXPECT_EQ(0x5010u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Overflow + rc = + ParseAndEncodeFloatingPointNumber("1e38", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: 1e38", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e38", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: -1e38", err_msg); + rc = + ParseAndEncodeFloatingPointNumber("1e40", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: 1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e40", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: -1e40", err_msg); + rc = ParseAndEncodeFloatingPointNumber("1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: 1e400", err_msg); + rc = ParseAndEncodeFloatingPointNumber("-1e400", type, AssertEmitFunc, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid 16-bit float literal: -1e400", err_msg); +} + +TEST(ParseAndEncodeFloatingPointNumber, TypeNone) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_NONE}; + + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidUsage, rc); + EXPECT_EQ("The expected type is not a float type", err_msg); +} + +TEST(ParseAndEncodeFloatingPointNumber, InvalidCaseWithoutErrorMessageString) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + NumberType type = {32, SPV_NUMBER_FLOATING}; + + rc = ParseAndEncodeFloatingPointNumber("invalid", type, AssertEmitFunc, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); +} + +TEST(ParseAndEncodeFloatingPointNumber, DoNotTouchErrorMessageStringOnSuccess) { + EncodeNumberStatus rc = EncodeNumberStatus::kInvalidText; + std::string err_msg("random content"); + NumberType type = {32, SPV_NUMBER_FLOATING}; + + rc = ParseAndEncodeFloatingPointNumber( + "0.0", type, [](uint32_t word) { EXPECT_EQ(0x0u, word); }, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_EQ("random content", err_msg); +} + +TEST(ParseAndEncodeNumber, Sample) { + EncodeNumberStatus rc = EncodeNumberStatus::kSuccess; + std::string err_msg; + NumberType type = {32, SPV_NUMBER_SIGNED_INT}; + + // Invalid with error message string + rc = ParseAndEncodeNumber("something wrong", type, AssertEmitFunc, &err_msg); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + EXPECT_EQ("Invalid unsigned integer literal: something wrong", err_msg); + + // Invalid without error message string + rc = ParseAndEncodeNumber("something wrong", type, AssertEmitFunc, nullptr); + EXPECT_EQ(EncodeNumberStatus::kInvalidText, rc); + + // Signed integer, should not touch the error message string. + err_msg = "random content"; + rc = ParseAndEncodeNumber("-1", type, + [](uint32_t word) { EXPECT_EQ(0xffffffffu, word); }, + &err_msg); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + EXPECT_EQ("random content", err_msg); + + // Unsigned integer + type = {32, SPV_NUMBER_UNSIGNED_INT}; + rc = ParseAndEncodeNumber( + "1", type, [](uint32_t word) { EXPECT_EQ(1u, word); }, nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); + + // Float + type = {32, SPV_NUMBER_FLOATING}; + rc = ParseAndEncodeNumber("-1.0", type, + [](uint32_t word) { EXPECT_EQ(0xbf800000, word); }, + nullptr); + EXPECT_EQ(EncodeNumberStatus::kSuccess, rc); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/pch_test.cpp b/third_party/spirv-tools/test/pch_test.cpp new file mode 100644 index 0000000..3b06a0a --- /dev/null +++ b/third_party/spirv-tools/test/pch_test.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_test.h" diff --git a/third_party/spirv-tools/test/pch_test.h b/third_party/spirv-tools/test/pch_test.h new file mode 100644 index 0000000..7dac06a --- /dev/null +++ b/third_party/spirv-tools/test/pch_test.h @@ -0,0 +1,18 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "source/spirv_constant.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" diff --git a/third_party/spirv-tools/test/preserve_numeric_ids_test.cpp b/third_party/spirv-tools/test/preserve_numeric_ids_test.cpp new file mode 100644 index 0000000..1c3354d --- /dev/null +++ b/third_party/spirv-tools/test/preserve_numeric_ids_test.cpp @@ -0,0 +1,159 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for unique type declaration rules validator. + +#include + +#include "source/text.h" +#include "source/text_handler.h" +#include "test/test_fixture.h" + +namespace spvtools { +namespace { + +using spvtest::ScopedContext; + +// Converts code to binary and then back to text. +spv_result_t ToBinaryAndBack( + const std::string& before, std::string* after, + uint32_t text_to_binary_options = SPV_TEXT_TO_BINARY_OPTION_NONE, + uint32_t binary_to_text_options = SPV_BINARY_TO_TEXT_OPTION_NONE, + spv_target_env env = SPV_ENV_UNIVERSAL_1_0) { + ScopedContext ctx(env); + spv_binary binary; + spv_text text; + + spv_result_t result = + spvTextToBinaryWithOptions(ctx.context, before.c_str(), before.size(), + text_to_binary_options, &binary, nullptr); + if (result != SPV_SUCCESS) { + return result; + } + + result = spvBinaryToText(ctx.context, binary->code, binary->wordCount, + binary_to_text_options, &text, nullptr); + if (result != SPV_SUCCESS) { + return result; + } + + *after = std::string(text->str, text->length); + + spvBinaryDestroy(binary); + spvTextDestroy(text); + + return SPV_SUCCESS; +} + +TEST(ToBinaryAndBack, DontPreserveNumericIds) { + const std::string before = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +%i32 = OpTypeInt 32 1 +%u32 = OpTypeInt 32 0 +%f32 = OpTypeFloat 32 +%200 = OpTypeVoid +%300 = OpTypeFunction %200 +%main = OpFunction %200 None %300 +%entry = OpLabel +%100 = OpConstant %u32 100 +%1 = OpConstant %u32 200 +%2 = OpConstant %u32 300 +OpReturn +OpFunctionEnd +)"; + + const std::string expected = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +%1 = OpTypeInt 32 1 +%2 = OpTypeInt 32 0 +%3 = OpTypeFloat 32 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +%8 = OpConstant %2 100 +%9 = OpConstant %2 200 +%10 = OpConstant %2 300 +OpReturn +OpFunctionEnd +)"; + + std::string after; + EXPECT_EQ(SPV_SUCCESS, + ToBinaryAndBack(before, &after, SPV_TEXT_TO_BINARY_OPTION_NONE, + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)); + + EXPECT_EQ(expected, after); +} + +TEST(TextHandler, PreserveNumericIds) { + const std::string before = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +%i32 = OpTypeInt 32 1 +%u32 = OpTypeInt 32 0 +%f32 = OpTypeFloat 32 +%200 = OpTypeVoid +%300 = OpTypeFunction %200 +%main = OpFunction %200 None %300 +%entry = OpLabel +%100 = OpConstant %u32 100 +%1 = OpConstant %u32 200 +%2 = OpConstant %u32 300 +OpReturn +OpFunctionEnd +)"; + + const std::string expected = + R"(OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +%3 = OpTypeInt 32 1 +%4 = OpTypeInt 32 0 +%5 = OpTypeFloat 32 +%200 = OpTypeVoid +%300 = OpTypeFunction %200 +%6 = OpFunction %200 None %300 +%7 = OpLabel +%100 = OpConstant %4 100 +%1 = OpConstant %4 200 +%2 = OpConstant %4 300 +OpReturn +OpFunctionEnd +)"; + + std::string after; + EXPECT_EQ(SPV_SUCCESS, + ToBinaryAndBack(before, &after, + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS, + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER)); + + EXPECT_EQ(expected, after); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/CMakeLists.txt b/third_party/spirv-tools/test/reduce/CMakeLists.txt new file mode 100644 index 0000000..652f0ab --- /dev/null +++ b/third_party/spirv-tools/test/reduce/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_spvtools_unittest(TARGET reduce + SRCS + merge_blocks_test.cpp + operand_to_constant_test.cpp + operand_to_undef_test.cpp + operand_to_dominating_id_test.cpp + reduce_test_util.cpp + reduce_test_util.h + reducer_test.cpp + remove_block_test.cpp + remove_function_test.cpp + remove_selection_test.cpp + remove_unused_instruction_test.cpp + remove_unused_struct_member_test.cpp + structured_loop_to_selection_test.cpp + validation_during_reduction_test.cpp + conditional_branch_to_simple_conditional_branch_test.cpp + simple_conditional_branch_to_branch_test.cpp + LIBS SPIRV-Tools-reduce + ) + diff --git a/third_party/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp b/third_party/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp new file mode 100644 index 0000000..0e46114 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/conditional_branch_to_simple_conditional_branch_test.cpp @@ -0,0 +1,501 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/reduction_pass.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3; + +TEST(ConditionalBranchToSimpleConditionalBranchTest, Diamond) { + // A test with the following structure. + // + // selection header + // OpBranchConditional + // | | + // b b + // | | + // selection merge + // + // There should be two opportunities for redirecting the OpBranchConditional + // targets: redirecting the true to false, and vice-versa. E.g. false to true: + // + // selection header + // OpBranchConditional + // || + // b b + // | | + // selection merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %13 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(2, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[0]->TryToApply(); + // The other opportunity should now be disabled. + ASSERT_FALSE(ops[1]->PreconditionHolds()); + + CheckValid(kEnv, context.get()); + + { + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %12 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + } + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); + + // Start again, and apply the other op. + context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(2, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + // The other opportunity should now be disabled. + ASSERT_FALSE(ops[0]->PreconditionHolds()); + + CheckValid(kEnv, context.get()); + + { + std::string after2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %13 %13 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after2, context.get()); + } +} + +TEST(ConditionalBranchToSimpleConditionalBranchTest, AlreadySimplified) { + // A test with the following structure. + // + // selection header + // OpBranchConditional + // || + // b b + // | | + // selection merge + // + // There should be no opportunities for redirecting the OpBranchConditional + // as it is already simplified. + // + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %12 + %12 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(0, ops.size()); +} + +TEST(ConditionalBranchToSimpleConditionalBranchTest, DontRemoveBackEdge) { + // A test with the following structure. The loop has a continue construct that + // ends with OpBranchConditional. The OpBranchConditional can be simplified, + // but only to point to the loop header, otherwise we have removed the + // back-edge. Thus, there should be one opportunity instead of two. + // + // loop header + // | + // loop continue target and back-edge block + // OpBranchConditional + // | | + // loop merge (to loop header^) + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %8 %11 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(ConditionalBranchToSimpleConditionalBranchTest, + DontRemoveBackEdgeCombinedHeaderContinue) { + // A test with the following structure. + // + // loop header and continue target and back-edge block + // OpBranchConditional + // | | + // loop merge (to loop header^) + // + // The OpBranchConditional-to-header edge must not be removed, so there should + // only be one opportunity. It should change both targets to be to the loop + // header. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %10 None + OpBranchConditional %8 %11 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %10 None + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(ConditionalBranchToSimpleConditionalBranchTest, BackEdgeUnreachable) { + // A test with the following structure. I.e. a loop with an unreachable + // continue construct that ends with OpBranchConditional. + // + // loop header + // | + // | loop continue target (unreachable) + // | | + // | back-edge block (unreachable) + // | OpBranchConditional + // | | | + // loop merge (to loop header^) + // + // The branch to the loop header must not be removed, even though the continue + // construct is unreachable. So there should only be one opportunity to make + // the true and false targets of the OpBranchConditional to point to the loop + // header. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %11 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranchConditional %8 %11 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %11 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = ConditionalBranchToSimpleConditionalBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/merge_blocks_test.cpp b/third_party/spirv-tools/test/reduce/merge_blocks_test.cpp new file mode 100644 index 0000000..8506ee0 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/merge_blocks_test.cpp @@ -0,0 +1,652 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(MergeBlocksReductionPassTest, BasicCheck) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %13 + %13 = OpLabel + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %15 + %15 = OpLabel + OpStore %8 %11 + OpBranch %16 + %16 = OpLabel + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(5, ops.size()); + + // Try order 3, 0, 2, 4, 1 + + ASSERT_TRUE(ops[3]->PreconditionHolds()); + ops[3]->TryToApply(); + + std::string after_op_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %13 + %13 = OpLabel + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %15 + %15 = OpLabel + OpStore %8 %11 + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_3, context.get()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %15 + %15 = OpLabel + OpStore %8 %11 + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_0, context.get()); + + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + + std::string after_op_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpStore %8 %11 + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_2, context.get()); + + ASSERT_TRUE(ops[4]->PreconditionHolds()); + ops[4]->TryToApply(); + + std::string after_op_4 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpStore %8 %11 + OpStore %8 %12 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_4, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpStore %8 %10 + OpStore %8 %11 + OpStore %8 %12 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_1, context.get()); +} + +TEST(MergeBlocksReductionPassTest, Loops) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "i" + OpName %29 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %29 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %45 + %45 = OpLabel + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %6 %10 + OpBranch %46 + %46 = OpLabel + %20 = OpSLessThan %19 %17 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %21 = OpLoad %6 %10 + OpBranch %47 + %47 = OpLabel + %22 = OpLoad %6 %8 + %23 = OpIAdd %6 %22 %21 + OpStore %8 %23 + %24 = OpLoad %6 %10 + %25 = OpLoad %6 %8 + %26 = OpIAdd %6 %25 %24 + OpStore %8 %26 + OpBranch %48 + %48 = OpLabel + OpBranch %15 + %15 = OpLabel + %27 = OpLoad %6 %10 + %28 = OpIAdd %6 %27 %9 + OpStore %10 %28 + OpBranch %12 + %14 = OpLabel + OpStore %29 %11 + OpBranch %49 + %49 = OpLabel + OpBranch %30 + %30 = OpLabel + OpLoopMerge %32 %33 None + OpBranch %34 + %34 = OpLabel + %35 = OpLoad %6 %29 + %36 = OpSLessThan %19 %35 %18 + OpBranch %50 + %50 = OpLabel + OpBranchConditional %36 %31 %32 + %31 = OpLabel + %37 = OpLoad %6 %29 + %38 = OpLoad %6 %8 + %39 = OpIAdd %6 %38 %37 + OpStore %8 %39 + %40 = OpLoad %6 %29 + %41 = OpLoad %6 %8 + %42 = OpIAdd %6 %41 %40 + OpStore %8 %42 + OpBranch %33 + %33 = OpLabel + %43 = OpLoad %6 %29 + %44 = OpIAdd %6 %43 %9 + OpBranch %51 + %51 = OpLabel + OpStore %29 %44 + OpBranch %30 + %32 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(11, ops.size()); + + for (auto& ri : ops) { + ASSERT_TRUE(ri->PreconditionHolds()); + ri->TryToApply(); + } + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "i" + OpName %29 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 0 + %18 = OpConstant %6 10 + %19 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %29 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + %17 = OpLoad %6 %10 + %20 = OpSLessThan %19 %17 %18 + OpLoopMerge %14 %13 None + OpBranchConditional %20 %13 %14 + %13 = OpLabel + %21 = OpLoad %6 %10 + %22 = OpLoad %6 %8 + %23 = OpIAdd %6 %22 %21 + OpStore %8 %23 + %24 = OpLoad %6 %10 + %25 = OpLoad %6 %8 + %26 = OpIAdd %6 %25 %24 + OpStore %8 %26 + %27 = OpLoad %6 %10 + %28 = OpIAdd %6 %27 %9 + OpStore %10 %28 + OpBranch %12 + %14 = OpLabel + OpStore %29 %11 + OpBranch %30 + %30 = OpLabel + %35 = OpLoad %6 %29 + %36 = OpSLessThan %19 %35 %18 + OpLoopMerge %32 %31 None + OpBranchConditional %36 %31 %32 + %31 = OpLabel + %37 = OpLoad %6 %29 + %38 = OpLoad %6 %8 + %39 = OpIAdd %6 %38 %37 + OpStore %8 %39 + %40 = OpLoad %6 %29 + %41 = OpLoad %6 %8 + %42 = OpIAdd %6 %41 %40 + OpStore %8 %42 + %43 = OpLoad %6 %29 + %44 = OpIAdd %6 %43 %9 + OpStore %29 %44 + OpBranch %30 + %32 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after, context.get()); +} + +TEST(MergeBlocksReductionPassTest, MergeWithOpPhi) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpBranch %12 + %12 = OpLabel + %13 = OpPhi %6 %11 %5 + OpStore %10 %13 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpStore %10 %11 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after, context.get()); +} + +void MergeBlocksReductionPassTest_LoopReturn_Helper(bool reverse) { + // A merge block opportunity stores a block that can be merged with its + // predecessor. + // Given blocks A -> B -> C: + // This test demonstrates how merging B->C can invalidate + // the opportunity of merging A->B, and vice-versa. E.g. + // B->C are merged: B is now terminated with OpReturn. + // A->B can now no longer be merged because A is a loop header, which + // cannot be terminated with OpReturn. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantFalse %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel ; A (loop header) + OpLoopMerge %13 %12 None + OpBranch %11 + %12 = OpLabel ; (unreachable continue block) + OpBranch %10 + %11 = OpLabel ; B + OpBranch %15 + %15 = OpLabel ; C + OpReturn + %13 = OpLabel ; (unreachable merge block) + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + ASSERT_NE(context.get(), nullptr); + auto opportunities = + MergeBlocksReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + + // A->B and B->C + ASSERT_EQ(opportunities.size(), 2); + + // Test applying opportunities in both orders. + if (reverse) { + std::reverse(opportunities.begin(), opportunities.end()); + } + + size_t num_applied = 0; + for (auto& ri : opportunities) { + if (ri->PreconditionHolds()) { + ri->TryToApply(); + ++num_applied; + } + } + + // Only 1 opportunity can be applied, as both disable each other. + ASSERT_EQ(num_applied, 1); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantFalse %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel ; A-B (loop header) + OpLoopMerge %13 %12 None + OpBranch %15 + %12 = OpLabel ; (unreachable continue block) + OpBranch %10 + %15 = OpLabel ; C + OpReturn + %13 = OpLabel ; (unreachable merge block) + OpReturn + OpFunctionEnd + )"; + + // The only difference is the labels. + std::string after_reversed = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantFalse %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel ; A (loop header) + OpLoopMerge %13 %12 None + OpBranch %11 + %12 = OpLabel ; (unreachable continue block) + OpBranch %10 + %11 = OpLabel ; B-C + OpReturn + %13 = OpLabel ; (unreachable merge block) + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, reverse ? after_reversed : after, context.get()); +} + +TEST(MergeBlocksReductionPassTest, LoopReturn) { + MergeBlocksReductionPassTest_LoopReturn_Helper(false); +} + +TEST(MergeBlocksReductionPassTest, LoopReturnReverse) { + MergeBlocksReductionPassTest_LoopReturn_Helper(true); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/operand_to_constant_test.cpp b/third_party/spirv-tools/test/reduce/operand_to_constant_test.cpp new file mode 100644 index 0000000..f44de51 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/operand_to_constant_test.cpp @@ -0,0 +1,308 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(OperandToConstantReductionPassTest, BasicCheck) { + std::string prologue = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %37 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "buf1" + OpMemberName %9 0 "f" + OpName %11 "" + OpName %24 "buf2" + OpMemberName %24 0 "i" + OpName %26 "" + OpName %37 "_GLF_color" + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 1 + OpMemberDecorate %24 0 Offset 0 + OpDecorate %24 Block + OpDecorate %26 DescriptorSet 0 + OpDecorate %26 Binding 2 + OpDecorate %37 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpTypePointer Uniform %6 + %20 = OpConstant %6 2 + %24 = OpTypeStruct %12 + %25 = OpTypePointer Uniform %24 + %26 = OpVariable %25 Uniform + %27 = OpTypePointer Uniform %12 + %33 = OpConstant %12 3 + %35 = OpTypeVector %6 4 + %36 = OpTypePointer Output %35 + %37 = OpVariable %36 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %15 = OpAccessChain %14 %11 %13 + %16 = OpLoad %6 %15 + %19 = OpFAdd %6 %16 %16 + %21 = OpFAdd %6 %19 %20 + %28 = OpAccessChain %27 %26 %13 + %29 = OpLoad %12 %28 + )"; + + std::string epilogue = R"( + %45 = OpConvertSToF %6 %34 + %46 = OpCompositeConstruct %35 %16 %21 %43 %45 + OpStore %37 %46 + OpReturn + OpFunctionEnd + )"; + + std::string original = prologue + R"( + %32 = OpIAdd %12 %29 %29 + %34 = OpIAdd %12 %32 %33 + %43 = OpConvertSToF %6 %29 + )" + epilogue; + + std::string expected = prologue + R"( + %32 = OpIAdd %12 %13 %13 ; %29 -> %13 x 2 + %34 = OpIAdd %12 %13 %33 ; %32 -> %13 + %43 = OpConvertSToF %6 %13 ; %29 -> %13 + )" + epilogue; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, original, kReduceAssembleOption); + const auto ops = + OperandToConstReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(17, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + ASSERT_TRUE(ops[3]->PreconditionHolds()); + ops[3]->TryToApply(); + + CheckEqual(env, expected, context.get()); +} + +TEST(OperandToConstantReductionPassTest, WithCalledFunction) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %10 %12 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeFunction %7 + %9 = OpTypePointer Output %7 + %10 = OpVariable %9 Output + %11 = OpTypePointer Input %7 + %12 = OpVariable %11 Input + %13 = OpConstant %6 0 + %14 = OpConstantComposite %7 %13 %13 %13 %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %15 = OpFunctionCall %7 %16 + OpReturn + OpFunctionEnd + %16 = OpFunction %7 None %8 + %17 = OpLabel + OpReturnValue %14 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + OperandToConstReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(OperandToConstantReductionPassTest, TargetSpecificFunction) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %17 = OpConstant %6 1 + %20 = OpConstant %6 2 + %23 = OpConstant %6 0 + %24 = OpTypeBool + %35 = OpConstant %6 3 + %53 = OpConstant %6 10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %65 = OpVariable %7 Function + %68 = OpVariable %7 Function + %73 = OpVariable %7 Function + OpStore %65 %35 + %66 = OpLoad %6 %65 + %67 = OpIAdd %6 %66 %17 + OpStore %65 %67 + %69 = OpLoad %6 %65 + OpStore %68 %69 + %70 = OpFunctionCall %6 %13 %68 + %71 = OpLoad %6 %65 + %72 = OpIAdd %6 %71 %70 + OpStore %65 %72 + %74 = OpLoad %6 %65 + OpStore %73 %74 + %75 = OpFunctionCall %6 %10 %73 + %76 = OpLoad %6 %65 + %77 = OpIAdd %6 %76 %75 + OpStore %65 %77 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %15 = OpVariable %7 Function + %16 = OpLoad %6 %9 + %18 = OpIAdd %6 %16 %17 + OpStore %15 %18 + %19 = OpLoad %6 %15 + %21 = OpIAdd %6 %19 %20 + OpStore %15 %21 + %22 = OpLoad %6 %15 + %25 = OpSGreaterThan %24 %22 %23 + OpSelectionMerge %27 None + OpBranchConditional %25 %26 %27 + %26 = OpLabel + %28 = OpLoad %6 %9 + OpReturnValue %28 + %27 = OpLabel + %30 = OpLoad %6 %9 + %31 = OpIAdd %6 %30 %17 + OpReturnValue %31 + OpFunctionEnd + %13 = OpFunction %6 None %8 + %12 = OpFunctionParameter %7 + %14 = OpLabel + %41 = OpVariable %7 Function + %46 = OpVariable %7 Function + %55 = OpVariable %7 Function + %34 = OpLoad %6 %12 + %36 = OpIEqual %24 %34 %35 + OpSelectionMerge %38 None + OpBranchConditional %36 %37 %38 + %37 = OpLabel + %39 = OpLoad %6 %12 + %40 = OpIMul %6 %20 %39 + OpStore %41 %40 + %42 = OpFunctionCall %6 %10 %41 + OpReturnValue %42 + %38 = OpLabel + %44 = OpLoad %6 %12 + %45 = OpIAdd %6 %44 %17 + OpStore %12 %45 + OpStore %46 %23 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %6 %46 + %54 = OpSLessThan %24 %52 %53 + OpBranchConditional %54 %48 %49 + %48 = OpLabel + %56 = OpLoad %6 %12 + OpStore %55 %56 + %57 = OpFunctionCall %6 %10 %55 + %58 = OpLoad %6 %12 + %59 = OpIAdd %6 %58 %57 + OpStore %12 %59 + OpBranch %50 + %50 = OpLabel + %60 = OpLoad %6 %46 + %61 = OpIAdd %6 %60 %17 + OpStore %46 %61 + OpBranch %47 + %49 = OpLabel + %62 = OpLoad %6 %12 + OpReturnValue %62 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + // Targeting all functions, there are quite a few opportunities. To avoid + // making the test too sensitive, we check that there are more than a number + // somewhat lower than the real number. + const auto all_ops = + OperandToConstReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_TRUE(all_ops.size() > 100); + + // Targeting individual functions, there are fewer opportunities. Again, we + // avoid checking against an exact number so that the test is not too + // sensitive. + const auto ops_for_function_4 = + OperandToConstReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 4); + const auto ops_for_function_10 = + OperandToConstReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 10); + const auto ops_for_function_13 = + OperandToConstReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 13); + ASSERT_TRUE(ops_for_function_4.size() < 60); + ASSERT_TRUE(ops_for_function_10.size() < 50); + ASSERT_TRUE(ops_for_function_13.size() < 80); + + // The total number of opportunities should be the sum of the per-function + // opportunities. + ASSERT_EQ(all_ops.size(), ops_for_function_4.size() + + ops_for_function_10.size() + + ops_for_function_13.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/operand_to_dominating_id_test.cpp b/third_party/spirv-tools/test/reduce/operand_to_dominating_id_test.cpp new file mode 100644 index 0000000..697c5cb --- /dev/null +++ b/third_party/spirv-tools/test/reduce/operand_to_dominating_id_test.cpp @@ -0,0 +1,198 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(OperandToDominatingIdReductionPassTest, BasicCheck) { + std::string original = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + %12 = OpLoad %6 %8 + %13 = OpIAdd %6 %11 %12 + OpStore %10 %13 + %15 = OpLoad %6 %10 + OpStore %14 %15 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, original, kReduceAssembleOption); + const auto ops = OperandToDominatingIdReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(10, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + %12 = OpLoad %6 %8 + %13 = OpIAdd %6 %11 %12 + OpStore %8 %13 ; %10 -> %8 + %15 = OpLoad %6 %10 + OpStore %14 %15 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_0, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + %12 = OpLoad %6 %8 + %13 = OpIAdd %6 %11 %12 + OpStore %8 %13 ; %10 -> %8 + %15 = OpLoad %6 %8 ; %10 -> %8 + OpStore %14 %15 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_1, context.get()); + + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + + std::string after_op_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + %12 = OpLoad %6 %8 + %13 = OpIAdd %6 %11 %12 + OpStore %8 %13 ; %10 -> %8 + %15 = OpLoad %6 %8 ; %10 -> %8 + OpStore %8 %15 ; %14 -> %8 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_2, context.get()); + + // The precondition has been disabled by an earlier opportunity's application. + ASSERT_FALSE(ops[3]->PreconditionHolds()); + + ASSERT_TRUE(ops[4]->PreconditionHolds()); + ops[4]->TryToApply(); + + std::string after_op_4 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + %12 = OpLoad %6 %8 + %13 = OpIAdd %6 %11 %11 ; %12 -> %11 + OpStore %8 %13 ; %10 -> %8 + %15 = OpLoad %6 %8 ; %10 -> %8 + OpStore %8 %15 ; %14 -> %8 + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_4, context.get()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/operand_to_undef_test.cpp b/third_party/spirv-tools/test/reduce/operand_to_undef_test.cpp new file mode 100644 index 0000000..4197427 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/operand_to_undef_test.cpp @@ -0,0 +1,230 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(OperandToUndefReductionPassTest, BasicCheck) { + // The following shader has 10 opportunities for replacing with undef. + + // #version 310 es + // + // precision highp float; + // + // layout(location=0) out vec4 _GLF_color; + // + // layout(set = 0, binding = 0) uniform buf0 { + // vec2 uniform1; + // }; + // + // void main() + // { + // _GLF_color = + // vec4( // opportunity + // uniform1.x / 2.0, // opportunity x2 (2.0 is const) + // uniform1.y / uniform1.x, // opportunity x3 + // uniform1.x + uniform1.x, // opportunity x3 + // uniform1.y); // opportunity + // } + + std::string original = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "_GLF_color" + OpName %11 "buf0" + OpMemberName %11 0 "uniform1" + OpName %13 "" + OpDecorate %9 Location 0 + OpMemberDecorate %11 0 Offset 0 + OpDecorate %11 Block + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypeVector %6 2 + %11 = OpTypeStruct %10 + %12 = OpTypePointer Uniform %11 + %13 = OpVariable %12 Uniform + %14 = OpTypeInt 32 1 + %15 = OpConstant %14 0 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 0 + %18 = OpTypePointer Uniform %6 + %21 = OpConstant %6 2 + %23 = OpConstant %16 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpAccessChain %18 %13 %15 %17 + %20 = OpLoad %6 %19 + %22 = OpFDiv %6 %20 %21 ; opportunity %20 (%21 is const) + %24 = OpAccessChain %18 %13 %15 %23 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %18 %13 %15 %17 + %27 = OpLoad %6 %26 + %28 = OpFDiv %6 %25 %27 ; opportunity %25 %27 + %29 = OpAccessChain %18 %13 %15 %17 + %30 = OpLoad %6 %29 + %31 = OpAccessChain %18 %13 %15 %17 + %32 = OpLoad %6 %31 + %33 = OpFAdd %6 %30 %32 ; opportunity %30 %32 + %34 = OpAccessChain %18 %13 %15 %23 + %35 = OpLoad %6 %34 + %36 = OpCompositeConstruct %7 %22 %28 %33 %35 ; opportunity %22 %28 %33 %35 + OpStore %9 %36 ; opportunity %36 + OpReturn + OpFunctionEnd + )"; + + // This is the same as original, except where noted. + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "_GLF_color" + OpName %11 "buf0" + OpMemberName %11 0 "uniform1" + OpName %13 "" + OpDecorate %9 Location 0 + OpMemberDecorate %11 0 Offset 0 + OpDecorate %11 Block + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypeVector %6 2 + %11 = OpTypeStruct %10 + %12 = OpTypePointer Uniform %11 + %13 = OpVariable %12 Uniform + %14 = OpTypeInt 32 1 + %15 = OpConstant %14 0 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 0 + %18 = OpTypePointer Uniform %6 + %21 = OpConstant %6 2 + %23 = OpConstant %16 1 + %37 = OpUndef %6 ; Added undef float as %37 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpAccessChain %18 %13 %15 %17 + %20 = OpLoad %6 %19 + %22 = OpFDiv %6 %37 %21 ; Replaced with %37 + %24 = OpAccessChain %18 %13 %15 %23 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %18 %13 %15 %17 + %27 = OpLoad %6 %26 + %28 = OpFDiv %6 %37 %37 ; Replaced with %37 twice + %29 = OpAccessChain %18 %13 %15 %17 + %30 = OpLoad %6 %29 + %31 = OpAccessChain %18 %13 %15 %17 + %32 = OpLoad %6 %31 + %33 = OpFAdd %6 %30 %32 + %34 = OpAccessChain %18 %13 %15 %23 + %35 = OpLoad %6 %34 + %36 = OpCompositeConstruct %7 %22 %28 %33 %35 + OpStore %9 %36 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, original, kReduceAssembleOption); + const auto ops = + OperandToUndefReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + + ASSERT_EQ(10, ops.size()); + + // Apply first three opportunities. + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + + CheckEqual(env, expected, context.get()); +} + +TEST(OperandToUndefReductionPassTest, WithCalledFunction) { + // The following shader has no opportunities. + // Most importantly, the noted function operand is not changed. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %10 %12 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeFunction %7 + %9 = OpTypePointer Output %7 + %10 = OpVariable %9 Output + %11 = OpTypePointer Input %7 + %12 = OpVariable %11 Input + %13 = OpConstant %6 0 + %14 = OpConstantComposite %7 %13 %13 %13 %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %15 = OpFunctionCall %7 %16 ; do not replace %16 with undef + OpReturn + OpFunctionEnd + %16 = OpFunction %7 None %8 + %17 = OpLabel + OpReturnValue %14 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + OperandToUndefReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/reduce_test_util.cpp b/third_party/spirv-tools/test/reduce/reduce_test_util.cpp new file mode 100644 index 0000000..0c23411 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/reduce_test_util.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/reduce/reduce_test_util.h" + +#include + +#include "tools/io.h" + +namespace spvtools { +namespace reduce { + +void CheckEqual(const spv_target_env env, + const std::vector& expected_binary, + const std::vector& actual_binary) { + if (expected_binary != actual_binary) { + SpirvTools t(env); + std::string expected_disassembled; + std::string actual_disassembled; + ASSERT_TRUE(t.Disassemble(expected_binary, &expected_disassembled, + kReduceDisassembleOption)); + ASSERT_TRUE(t.Disassemble(actual_binary, &actual_disassembled, + kReduceDisassembleOption)); + ASSERT_EQ(expected_disassembled, actual_disassembled); + } +} + +void CheckEqual(const spv_target_env env, const std::string& expected_text, + const std::vector& actual_binary) { + std::vector expected_binary; + SpirvTools t(env); + ASSERT_TRUE( + t.Assemble(expected_text, &expected_binary, kReduceAssembleOption)); + CheckEqual(env, expected_binary, actual_binary); +} + +void CheckEqual(const spv_target_env env, const std::string& expected_text, + const opt::IRContext* actual_ir) { + std::vector actual_binary; + actual_ir->module()->ToBinary(&actual_binary, false); + CheckEqual(env, expected_text, actual_binary); +} + +void CheckValid(spv_target_env env, const opt::IRContext* ir) { + std::vector binary; + ir->module()->ToBinary(&binary, false); + SpirvTools t(env); + ASSERT_TRUE(t.Validate(binary)); +} + +std::string ToString(spv_target_env env, const opt::IRContext* ir) { + std::vector binary; + ir->module()->ToBinary(&binary, false); + SpirvTools t(env); + std::string result; + t.Disassemble(binary, &result, kReduceDisassembleOption); + return result; +} + +void NopDiagnostic(spv_message_level_t /*level*/, const char* /*source*/, + const spv_position_t& /*position*/, + const char* /*message*/) {} + +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +} + +void DumpShader(opt::IRContext* context, const char* filename) { + std::vector binary; + context->module()->ToBinary(&binary, false); + DumpShader(binary, filename); +} + +void DumpShader(const std::vector& binary, const char* filename) { + auto write_file_succeeded = + WriteFile(filename, "wb", &binary[0], binary.size()); + if (!write_file_succeeded) { + std::cerr << "Failed to dump shader" << std::endl; + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/reduce_test_util.h b/third_party/spirv-tools/test/reduce/reduce_test_util.h new file mode 100644 index 0000000..b9ad12f --- /dev/null +++ b/third_party/spirv-tools/test/reduce/reduce_test_util.h @@ -0,0 +1,75 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_REDUCE_REDUCE_TEST_UTIL_H_ +#define TEST_REDUCE_REDUCE_TEST_UTIL_H_ + +#include "gtest/gtest.h" +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace reduce { + +// Checks whether the given binaries are bit-wise equal. +void CheckEqual(spv_target_env env, + const std::vector& expected_binary, + const std::vector& actual_binary); + +// Assembles the given text and check whether the resulting binary is bit-wise +// equal to the given binary. +void CheckEqual(spv_target_env env, const std::string& expected_text, + const std::vector& actual_binary); + +// Assembles the given text and turns the given IR into binary, then checks +// whether the resulting binaries are bit-wise equal. +void CheckEqual(spv_target_env env, const std::string& expected_text, + const opt::IRContext* actual_ir); + +// Assembles the given IR context and checks whether the resulting binary is +// valid. +void CheckValid(spv_target_env env, const opt::IRContext* ir); + +// Assembles the given IR context, then returns its disassembly as a string. +// Useful for debugging. +std::string ToString(spv_target_env env, const opt::IRContext* ir); + +// Assembly options for writing reduction tests. It simplifies matters if +// numeric ids do not change. +const uint32_t kReduceAssembleOption = + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS; +// Disassembly options for writing reduction tests. +const uint32_t kReduceDisassembleOption = + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_INDENT; + +// Don't print reducer info during testing. +void NopDiagnostic(spv_message_level_t /*level*/, const char* /*source*/, + const spv_position_t& /*position*/, const char* /*message*/); + +// Prints reducer messages (for debugging). +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message); + +// Dumps the SPIRV-V module in |context| to file |filename|. Useful for +// interactive debugging. +void DumpShader(opt::IRContext* context, const char* filename); + +// Dumps |binary| to file |filename|. Useful for interactive debugging. +void DumpShader(const std::vector& binary, const char* filename); + +} // namespace reduce +} // namespace spvtools + +#endif // TEST_REDUCE_REDUCE_TEST_UTIL_H_ diff --git a/third_party/spirv-tools/test/reduce/reducer_test.cpp b/third_party/spirv-tools/test/reduce/reducer_test.cpp new file mode 100644 index 0000000..276aedc --- /dev/null +++ b/third_party/spirv-tools/test/reduce/reducer_test.cpp @@ -0,0 +1,627 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reducer.h" + +#include + +#include "source/opt/build_module.h" +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" +#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3; +const MessageConsumer kMessageConsumer = NopDiagnostic; + +// This changes its mind each time IsInteresting is invoked as to whether the +// binary is interesting, until some limit is reached after which the binary is +// always deemed interesting. This is useful to test that reduction passes +// interleave in interesting ways for a while, and then always succeed after +// some point; the latter is important to end up with a predictable final +// reduced binary for tests. +class PingPongInteresting { + public: + explicit PingPongInteresting(uint32_t always_interesting_after) + : is_interesting_(true), + always_interesting_after_(always_interesting_after), + count_(0) {} + + bool IsInteresting() { + bool result; + if (count_ > always_interesting_after_) { + result = true; + } else { + result = is_interesting_; + is_interesting_ = !is_interesting_; + } + count_++; + return result; + } + + private: + bool is_interesting_; + const uint32_t always_interesting_after_; + uint32_t count_; +}; + +TEST(ReducerTest, ExprToConstantAndRemoveUnreferenced) { + // Check that ExprToConstant and RemoveUnreferenced work together; once some + // ID uses have been changed to constants, those IDs can be removed. + std::string original = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %60 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %16 "buf2" + OpMemberName %16 0 "i" + OpName %18 "" + OpName %25 "buf1" + OpMemberName %25 0 "f" + OpName %27 "" + OpName %60 "_GLF_color" + OpMemberDecorate %16 0 Offset 0 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 2 + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 1 + OpDecorate %60 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpTypeStruct %6 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypePointer Uniform %6 + %22 = OpTypeBool + %100 = OpConstantTrue %22 + %24 = OpTypeFloat 32 + %25 = OpTypeStruct %24 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %24 + %31 = OpConstant %24 2 + %56 = OpConstant %6 1 + %58 = OpTypeVector %24 4 + %59 = OpTypePointer Output %58 + %60 = OpVariable %59 Output + %72 = OpUndef %24 + %74 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %73 = OpPhi %6 %74 %5 %77 %34 + %71 = OpPhi %24 %72 %5 %76 %34 + %70 = OpPhi %6 %9 %5 %57 %34 + %20 = OpAccessChain %19 %18 %9 + %21 = OpLoad %6 %20 + %23 = OpSLessThan %22 %70 %21 + OpLoopMerge %12 %34 None + OpBranchConditional %23 %11 %12 + %11 = OpLabel + %29 = OpAccessChain %28 %27 %9 + %30 = OpLoad %24 %29 + %32 = OpFOrdGreaterThan %22 %30 %31 + OpSelectionMerge %90 None + OpBranchConditional %32 %33 %46 + %33 = OpLabel + %40 = OpFAdd %24 %71 %30 + %45 = OpISub %6 %73 %21 + OpBranch %90 + %46 = OpLabel + %50 = OpFMul %24 %71 %30 + %54 = OpSDiv %6 %73 %21 + OpBranch %90 + %90 = OpLabel + %77 = OpPhi %6 %45 %33 %54 %46 + %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel + %57 = OpIAdd %6 %70 %56 + OpBranch %10 + %12 = OpLabel + %61 = OpAccessChain %28 %27 %9 + %62 = OpLoad %24 %61 + %66 = OpConvertSToF %24 %21 + %68 = OpConvertSToF %24 %73 + %69 = OpCompositeConstruct %58 %62 %71 %66 %68 + OpStore %60 %69 + OpReturn + OpFunctionEnd + )"; + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %22 = OpTypeBool + %100 = OpConstantTrue %22 + %24 = OpTypeFloat 32 + %31 = OpConstant %24 2 + %56 = OpConstant %6 1 + %72 = OpUndef %24 + %74 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %34 None + OpBranchConditional %100 %11 %12 + %11 = OpLabel + OpSelectionMerge %90 None + OpBranchConditional %100 %33 %46 + %33 = OpLabel + OpBranch %90 + %46 = OpLabel + OpBranch %90 + %90 = OpLabel + OpBranch %34 + %34 = OpLabel + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + Reducer reducer(kEnv); + PingPongInteresting ping_pong_interesting(10); + reducer.SetMessageConsumer(kMessageConsumer); + reducer.SetInterestingnessFunction( + [&ping_pong_interesting](const std::vector&, uint32_t) -> bool { + return ping_pong_interesting.IsInteresting(); + }); + reducer.AddReductionPass( + MakeUnique(false)); + reducer.AddReductionPass( + MakeUnique()); + + std::vector binary_in; + SpirvTools t(kEnv); + + ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption)); + std::vector binary_out; + spvtools::ReducerOptions reducer_options; + reducer_options.set_step_limit(500); + reducer_options.set_fail_on_validation_error(true); + spvtools::ValidatorOptions validator_options; + + Reducer::ReductionResultStatus status = reducer.Run( + std::move(binary_in), &binary_out, reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); + + CheckEqual(kEnv, expected, binary_out); +} + +bool InterestingWhileOpcodeExists(const std::vector& binary, + uint32_t opcode, uint32_t count, bool dump) { + if (dump) { + std::stringstream ss; + ss << "temp_" << count << ".spv"; + DumpShader(binary, ss.str().c_str()); + } + + std::unique_ptr context = + BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size()); + assert(context); + bool interesting = false; + for (auto& function : *context->module()) { + context->cfg()->ForEachBlockInPostOrder( + &*function.begin(), + [opcode, &interesting](opt::BasicBlock* block) -> void { + for (auto& inst : *block) { + if (inst.opcode() == opcode) { + interesting = true; + break; + } + } + }); + if (interesting) { + break; + } + } + return interesting; +} + +bool InterestingWhileIMulReachable(const std::vector& binary, + uint32_t count) { + return InterestingWhileOpcodeExists(binary, SpvOpIMul, count, false); +} + +bool InterestingWhileSDivReachable(const std::vector& binary, + uint32_t count) { + return InterestingWhileOpcodeExists(binary, SpvOpSDiv, count, false); +} + +// The shader below was derived from the following GLSL, and optimized. +// #version 310 es +// precision highp float; +// layout(location = 0) out vec4 _GLF_color; +// int foo() { +// int x = 1; +// int y; +// x = y / x; // SDiv +// return x; +// } +// void main() { +// int c; +// while (bool(c)) { +// do { +// if (bool(c)) { +// if (bool(c)) { +// ++c; +// } else { +// _GLF_color.x = float(c*c); // IMul +// } +// return; +// } +// } while(bool(foo())); +// return; +// } +// } +const std::string kShaderWithLoopsDivAndMul = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %49 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %49 "_GLF_color" + OpDecorate %49 Location 0 + OpDecorate %52 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %12 = OpConstant %6 1 + %27 = OpTypeBool + %28 = OpTypeInt 32 0 + %29 = OpConstant %28 0 + %46 = OpTypeFloat 32 + %47 = OpTypeVector %46 4 + %48 = OpTypePointer Output %47 + %49 = OpVariable %48 Output + %54 = OpTypePointer Output %46 + %64 = OpConstantFalse %27 + %67 = OpConstantTrue %27 + %81 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %61 + %61 = OpLabel + OpLoopMerge %60 %63 None + OpBranch %20 + %20 = OpLabel + %30 = OpINotEqual %27 %81 %29 + OpLoopMerge %22 %23 None + OpBranchConditional %30 %21 %22 + %21 = OpLabel + OpBranch %31 + %31 = OpLabel + OpLoopMerge %33 %38 None + OpBranch %32 + %32 = OpLabel + OpBranchConditional %30 %37 %38 + %37 = OpLabel + OpSelectionMerge %42 None + OpBranchConditional %30 %41 %45 + %41 = OpLabel + OpBranch %42 + %45 = OpLabel + %52 = OpIMul %6 %81 %81 + %53 = OpConvertSToF %46 %52 + %55 = OpAccessChain %54 %49 %29 + OpStore %55 %53 + OpBranch %42 + %42 = OpLabel + OpBranch %33 + %38 = OpLabel + %77 = OpSDiv %6 %81 %12 + %58 = OpINotEqual %27 %77 %29 + OpBranchConditional %58 %31 %33 + %33 = OpLabel + %86 = OpPhi %27 %67 %42 %64 %38 + OpSelectionMerge %68 None + OpBranchConditional %86 %22 %68 + %68 = OpLabel + OpBranch %22 + %23 = OpLabel + OpBranch %20 + %22 = OpLabel + %90 = OpPhi %27 %64 %20 %86 %33 %67 %68 + OpSelectionMerge %70 None + OpBranchConditional %90 %60 %70 + %70 = OpLabel + OpBranch %60 + %63 = OpLabel + OpBranch %61 + %60 = OpLabel + OpReturn + OpFunctionEnd + )"; + +// The shader below comes from the following GLSL. +// #version 320 es +// +// int baz(int x) { +// int y = x + 1; +// y = y + 2; +// if (y > 0) { +// return x; +// } +// return x + 1; +// } +// +// int bar(int a) { +// if (a == 3) { +// return baz(2*a); +// } +// a = a + 1; +// for (int i = 0; i < 10; i++) { +// a += baz(a); +// } +// return a; +// } +// +// void main() { +// int x; +// x = 3; +// x += 1; +// x += bar(x); +// x += baz(x); +// } +const std::string kShaderWithMultipleFunctions = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %17 = OpConstant %6 1 + %20 = OpConstant %6 2 + %23 = OpConstant %6 0 + %24 = OpTypeBool + %35 = OpConstant %6 3 + %53 = OpConstant %6 10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %65 = OpVariable %7 Function + %68 = OpVariable %7 Function + %73 = OpVariable %7 Function + OpStore %65 %35 + %66 = OpLoad %6 %65 + %67 = OpIAdd %6 %66 %17 + OpStore %65 %67 + %69 = OpLoad %6 %65 + OpStore %68 %69 + %70 = OpFunctionCall %6 %13 %68 + %71 = OpLoad %6 %65 + %72 = OpIAdd %6 %71 %70 + OpStore %65 %72 + %74 = OpLoad %6 %65 + OpStore %73 %74 + %75 = OpFunctionCall %6 %10 %73 + %76 = OpLoad %6 %65 + %77 = OpIAdd %6 %76 %75 + OpStore %65 %77 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %15 = OpVariable %7 Function + %16 = OpLoad %6 %9 + %18 = OpIAdd %6 %16 %17 + OpStore %15 %18 + %19 = OpLoad %6 %15 + %21 = OpIAdd %6 %19 %20 + OpStore %15 %21 + %22 = OpLoad %6 %15 + %25 = OpSGreaterThan %24 %22 %23 + OpSelectionMerge %27 None + OpBranchConditional %25 %26 %27 + %26 = OpLabel + %28 = OpLoad %6 %9 + OpReturnValue %28 + %27 = OpLabel + %30 = OpLoad %6 %9 + %31 = OpIAdd %6 %30 %17 + OpReturnValue %31 + OpFunctionEnd + %13 = OpFunction %6 None %8 + %12 = OpFunctionParameter %7 + %14 = OpLabel + %41 = OpVariable %7 Function + %46 = OpVariable %7 Function + %55 = OpVariable %7 Function + %34 = OpLoad %6 %12 + %36 = OpIEqual %24 %34 %35 + OpSelectionMerge %38 None + OpBranchConditional %36 %37 %38 + %37 = OpLabel + %39 = OpLoad %6 %12 + %40 = OpIMul %6 %20 %39 + OpStore %41 %40 + %42 = OpFunctionCall %6 %10 %41 + OpReturnValue %42 + %38 = OpLabel + %44 = OpLoad %6 %12 + %45 = OpIAdd %6 %44 %17 + OpStore %12 %45 + OpStore %46 %23 + OpBranch %47 + %47 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %6 %46 + %54 = OpSLessThan %24 %52 %53 + OpBranchConditional %54 %48 %49 + %48 = OpLabel + %56 = OpLoad %6 %12 + OpStore %55 %56 + %57 = OpFunctionCall %6 %10 %55 + %58 = OpLoad %6 %12 + %59 = OpIAdd %6 %58 %57 + OpStore %12 %59 + OpBranch %50 + %50 = OpLabel + %60 = OpLoad %6 %46 + %61 = OpIAdd %6 %60 %17 + OpStore %46 %61 + OpBranch %47 + %49 = OpLabel + %62 = OpLoad %6 %12 + OpReturnValue %62 + OpFunctionEnd + )"; + +TEST(ReducerTest, ShaderReduceWhileMulReachable) { + Reducer reducer(kEnv); + + reducer.SetInterestingnessFunction(InterestingWhileIMulReachable); + reducer.AddDefaultReductionPasses(); + reducer.SetMessageConsumer(kMessageConsumer); + + std::vector binary_in; + SpirvTools t(kEnv); + + ASSERT_TRUE( + t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption)); + std::vector binary_out; + spvtools::ReducerOptions reducer_options; + reducer_options.set_step_limit(500); + reducer_options.set_fail_on_validation_error(true); + spvtools::ValidatorOptions validator_options; + + Reducer::ReductionResultStatus status = reducer.Run( + std::move(binary_in), &binary_out, reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); +} + +TEST(ReducerTest, ShaderReduceWhileDivReachable) { + Reducer reducer(kEnv); + + reducer.SetInterestingnessFunction(InterestingWhileSDivReachable); + reducer.AddDefaultReductionPasses(); + reducer.SetMessageConsumer(kMessageConsumer); + + std::vector binary_in; + SpirvTools t(kEnv); + + ASSERT_TRUE( + t.Assemble(kShaderWithLoopsDivAndMul, &binary_in, kReduceAssembleOption)); + std::vector binary_out; + spvtools::ReducerOptions reducer_options; + reducer_options.set_step_limit(500); + reducer_options.set_fail_on_validation_error(true); + spvtools::ValidatorOptions validator_options; + + Reducer::ReductionResultStatus status = reducer.Run( + std::move(binary_in), &binary_out, reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); +} + +// Computes an instruction count for each function in the module represented by +// |binary|. +std::unordered_map GetFunctionInstructionCount( + const std::vector& binary) { + std::unique_ptr context = + BuildModule(kEnv, kMessageConsumer, binary.data(), binary.size()); + assert(context != nullptr && "Failed to build module."); + std::unordered_map result; + for (auto& function : *context->module()) { + uint32_t& count = result[function.result_id()] = 0; + function.ForEachInst([&count](opt::Instruction*) { count++; }); + } + return result; +} + +TEST(ReducerTest, SingleFunctionReduction) { + Reducer reducer(kEnv); + + PingPongInteresting ping_pong_interesting(4); + reducer.SetInterestingnessFunction( + [&ping_pong_interesting](const std::vector&, uint32_t) -> bool { + return ping_pong_interesting.IsInteresting(); + }); + reducer.AddDefaultReductionPasses(); + reducer.SetMessageConsumer(kMessageConsumer); + + std::vector binary_in; + SpirvTools t(kEnv); + + ASSERT_TRUE(t.Assemble(kShaderWithMultipleFunctions, &binary_in, + kReduceAssembleOption)); + + auto original_instruction_count = GetFunctionInstructionCount(binary_in); + + std::vector binary_out; + spvtools::ReducerOptions reducer_options; + reducer_options.set_step_limit(500); + reducer_options.set_fail_on_validation_error(true); + + // Instruct the reducer to only target function 13. + reducer_options.set_target_function(13); + + spvtools::ValidatorOptions validator_options; + + Reducer::ReductionResultStatus status = reducer.Run( + std::move(binary_in), &binary_out, reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); + + auto final_instruction_count = GetFunctionInstructionCount(binary_out); + + // Nothing should have been removed from these functions. + ASSERT_EQ(original_instruction_count.at(4), final_instruction_count.at(4)); + ASSERT_EQ(original_instruction_count.at(10), final_instruction_count.at(10)); + + // Function 13 should have been reduced to these five instructions: + // OpFunction + // OpFunctionParameter + // OpLabel + // OpReturnValue + // OpFunctionEnd + ASSERT_EQ(5, final_instruction_count.at(13)); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/remove_block_test.cpp b/third_party/spirv-tools/test/reduce/remove_block_test.cpp new file mode 100644 index 0000000..2500d0c --- /dev/null +++ b/third_party/spirv-tools/test/reduce/remove_block_test.cpp @@ -0,0 +1,358 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_block_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(RemoveBlockReductionPassTest, BasicCheck) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %14 + %13 = OpLabel ; unreachable + OpStore %8 %9 + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %16 + %15 = OpLabel ; unreachable + OpStore %8 %11 + OpBranch %16 + %16 = OpLabel + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(2, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %16 + %15 = OpLabel + OpStore %8 %11 + OpBranch %16 + %16 = OpLabel + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_0, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %14 + %14 = OpLabel + OpStore %8 %10 + OpBranch %16 + %16 = OpLabel + OpStore %8 %12 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_1, context.get()); +} + +TEST(RemoveBlockReductionPassTest, UnreachableContinueAndMerge) { + // Loop with unreachable merge and continue target. There should be no + // opportunities. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %13 + %13 = OpLabel + OpLoopMerge %16 %15 None + OpBranch %14 + %14 = OpLabel + OpReturn + %15 = OpLabel + OpBranch %13 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveBlockReductionPassTest, OneBlock) { + // Function with just one block. There should be no opportunities. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveBlockReductionPassTest, UnreachableBlocksWithOutsideIdUses) { + // A function with two unreachable blocks A -> B. A defines ID %9 and B uses + // %9. There are no references to A, but removing A would be invalid because + // of B's use of %9, so there should be no opportunities. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeInt 32 1 + %5 = OpTypeFunction %3 + %6 = OpConstant %4 1 + %2 = OpFunction %3 None %5 + %7 = OpLabel + OpReturn + %8 = OpLabel ; A + %9 = OpUndef %4 + OpBranch %10 + %10 = OpLabel ; B + %11 = OpIAdd %4 %6 %9 ; uses %9 from A, so A cannot be removed + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + const auto ops = + RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveBlockReductionPassTest, UnreachableBlocksWithInsideIdUses) { + // Similar to the above test. + + // A function with two unreachable blocks A -> B. Both blocks create and use + // IDs, but the uses are contained within each block, so A should be removed. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeInt 32 1 + %5 = OpTypeFunction %3 + %6 = OpConstant %4 1 + %2 = OpFunction %3 None %5 + %7 = OpLabel + OpReturn + %8 = OpLabel ; A + %9 = OpUndef %4 ; define %9 + %10 = OpIAdd %4 %6 %9 ; use %9 + OpBranch %11 + %11 = OpLabel ; B + %12 = OpUndef %4 ; define %12 + %13 = OpIAdd %4 %6 %12 ; use %12 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + auto ops = RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + + ops[0]->TryToApply(); + + // Same as above, but block A is removed. + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeInt 32 1 + %5 = OpTypeFunction %3 + %6 = OpConstant %4 1 + %2 = OpFunction %3 None %5 + %7 = OpLabel + OpReturn + %11 = OpLabel + %12 = OpUndef %4 + %13 = OpIAdd %4 %6 %12 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_0, context.get()); + + // Find opportunities again. There are no reference to B. B should now be + // removed. + + ops = RemoveBlockReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + + ops[0]->TryToApply(); + + // Same as above, but block B is removed. + std::string after_op_0_again = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeInt 32 1 + %5 = OpTypeFunction %3 + %6 = OpConstant %4 1 + %2 = OpFunction %3 None %5 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_op_0_again, context.get()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/remove_function_test.cpp b/third_party/spirv-tools/test/reduce/remove_function_test.cpp new file mode 100644 index 0000000..e293f4e --- /dev/null +++ b/third_party/spirv-tools/test/reduce/remove_function_test.cpp @@ -0,0 +1,295 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_function_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +// Helper to count the number of functions in the module. +// Remove if there turns out to be a more direct way to do this. +uint32_t count_functions(opt::IRContext* context) { + uint32_t result = 0; + for (auto& function : *context->module()) { + (void)(function); + ++result; + } + return result; +} + +TEST(RemoveFunctionTest, BasicCheck) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %10 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + ASSERT_EQ(3, count_functions(context.get())); + + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + ASSERT_EQ(2, count_functions(context.get())); + + std::string after_first = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_first, context.get()); + + ops = RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + ASSERT_EQ(1, count_functions(context.get())); + + std::string after_second = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after_second, context.get()); +} + +TEST(RemoveFunctionTest, NothingToRemove) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpFunctionCall %2 %8 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %10 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveFunctionTest, TwoRemovableFunctions) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + ASSERT_EQ(3, count_functions(context.get())); + + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(2, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + ASSERT_EQ(2, count_functions(context.get())); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + ASSERT_EQ(1, count_functions(context.get())); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, after, context.get()); +} + +TEST(RemoveFunctionTest, NoRemovalsDueToOpName) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "foo(" + OpName %8 "bar(" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveFunctionTest, NoRemovalDueToLinkageDecoration) { + // The non-entry point function is not removable because it is referenced by a + // linkage decoration. Thus no function can be removed. + std::string shader = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpName %1 "main" + OpDecorate %2 LinkageAttributes "ExportedFunc" Export + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %1 = OpFunction %4 None %5 + %6 = OpLabel + OpReturn + OpFunctionEnd + %2 = OpFunction %4 None %5 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + auto ops = + RemoveFunctionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/remove_selection_test.cpp b/third_party/spirv-tools/test/reduce/remove_selection_test.cpp new file mode 100644 index 0000000..2921bbe --- /dev/null +++ b/third_party/spirv-tools/test/reduce/remove_selection_test.cpp @@ -0,0 +1,557 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_selection_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(RemoveSelectionTest, OpportunityBecauseSameTargetBlock) { + // A test with the following structure. The OpSelectionMerge instruction + // should be removed. + // + // header + // || + // block + // | + // merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %11 %11 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + + auto ops = + RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranchConditional %8 %11 %11 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after, context.get()); + + ops = RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveSelectionTest, OpportunityBecauseSameTargetBlockMerge) { + // A test with the following structure. The OpSelectionMerge instruction + // should be removed. + // + // header + // || + // merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %10 %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + + auto ops = + RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranchConditional %8 %10 %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after, context.get()); + + ops = RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveSelectionTest, NoOpportunityBecauseDifferentTargetBlocksOneMerge) { + // A test with the following structure. The OpSelectionMerge instruction + // should NOT be removed. + // + // header + // | | + // | block + // | | + // merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %10 %11 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + + auto ops = + RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveSelectionTest, NoOpportunityBecauseDifferentTargetBlocks) { + // A test with the following structure. The OpSelectionMerge instruction + // should NOT be removed. + // + // header + // | | + // b b + // | | + // merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %11 %12 + %11 = OpLabel + OpBranch %10 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + + auto ops = + RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveSelectionTest, NoOpportunityBecauseMergeUsed) { + // A test with the following structure. The OpSelectionMerge instruction + // should NOT be removed. + // + // header + // || + // block + // | | + // | block + // | | + // merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %11 %12 + %11 = OpLabel + OpBranchConditional %8 %10 %12 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + + auto ops = + RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveSelectionTest, OpportunityBecauseLoopMergeUsed) { + // A test with the following structure. The OpSelectionMerge instruction + // should be removed. + // + // loop header + // | + // | + // s.header + // || + // block + // | | + // | | + // | | ^ (to loop header) + // s.merge | | + // | / loop continue target (unreachable) + // loop merge + // + // + // which becomes: + // + // loop header + // | + // | + // block + // || + // block + // | | + // | | + // | | ^ (to loop header) + // block | | + // | / loop continue target (unreachable) + // loop merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %13 + %13 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %8 %15 %15 + %15 = OpLabel + OpBranchConditional %8 %14 %11 + %14 = OpLabel + OpBranch %11 + %12 = OpLabel + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + + CheckValid(env, context.get()); + + auto ops = + RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %13 + %13 = OpLabel + OpBranchConditional %8 %15 %15 + %15 = OpLabel + OpBranchConditional %8 %14 %11 + %14 = OpLabel + OpBranch %11 + %12 = OpLabel + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after, context.get()); + + ops = RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveSelectionTest, OpportunityBecauseLoopContinueUsed) { + // A test with the following structure. The OpSelectionMerge instruction + // should be removed. + // + // loop header + // | + // | + // s.header + // || + // block + // | | + // | | + // | | ^ (to loop header) + // s.merge | | + // | loop continue target + // loop merge + // + // + // which becomes: + // + // loop header + // | + // | + // block + // || + // block + // | | + // | | + // | | ^ (to loop header) + // block | | + // | loop continue target + // loop merge + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %13 + %13 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %8 %15 %15 + %15 = OpLabel + OpBranchConditional %8 %14 %12 + %14 = OpLabel + OpBranch %11 + %12 = OpLabel + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + + CheckValid(env, context.get()); + + auto ops = + RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %13 + %13 = OpLabel + OpBranchConditional %8 %15 %15 + %15 = OpLabel + OpBranchConditional %8 %14 %12 + %14 = OpLabel + OpBranch %11 + %12 = OpLabel + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after, context.get()); + + ops = RemoveSelectionReductionOpportunityFinder().GetAvailableOpportunities( + context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/remove_unused_instruction_test.cpp b/third_party/spirv-tools/test/reduce/remove_unused_instruction_test.cpp new file mode 100644 index 0000000..eb548e1 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/remove_unused_instruction_test.cpp @@ -0,0 +1,563 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/util/make_unique.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3; + +TEST(RemoveUnusedInstructionReductionPassTest, RemoveStores) { + // A module with some unused instructions, including some unused OpStore + // instructions. + + RemoveUnusedInstructionReductionOpportunityFinder finder(true); + + const std::string original = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 ; 0 + OpName %4 "main" ; 1 + OpName %8 "a" ; 2 + OpName %10 "b" ; 3 + OpName %12 "c" ; 4 + OpName %14 "d" ; 5 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %11 = OpConstant %6 20 + %13 = OpConstant %6 30 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %8 %9 ; 6 + OpStore %10 %11 ; 7 + OpStore %12 %13 ; 8 + %15 = OpLoad %6 %8 + OpStore %14 %15 ; 9 + OpReturn + OpFunctionEnd + + )"; + + const MessageConsumer consumer = nullptr; + const auto context = + BuildModule(kEnv, consumer, original, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(10, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 ; 0 + %11 = OpConstant %6 20 ; 1 + %13 = OpConstant %6 30 ; 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function ; 3 + %12 = OpVariable %7 Function ; 4 + %14 = OpVariable %7 Function ; 5 + %15 = OpLoad %6 %8 ; 6 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, step_2, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(7, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function ; 0 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, step_3, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_4 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 ; 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, step_4, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_5 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 ; 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, step_5, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + const std::string step_6 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, step_6, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveUnusedInstructionReductionPassTest, Referenced) { + // A module with some unused global variables, constants, and types. Some will + // not be removed initially because of the OpDecorate instructions. + + RemoveUnusedInstructionReductionOpportunityFinder finder(true); + + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 ; 1 + OpName %4 "main" ; 2 + OpName %12 "a" ; 3 + OpDecorate %12 RelaxedPrecision ; 4 + OpDecorate %13 RelaxedPrecision ; 5 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 ; 6 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private + %13 = OpConstant %10 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(6, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool ; 1 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private ; 2 + %13 = OpConstant %10 1 ; 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(3, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + std::string after_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Private %10 ; 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after_2, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + std::string after_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 ; 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after_3, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + CheckValid(kEnv, context.get()); + } + + std::string after_4 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after_4, context.get()); + + ops = finder.GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(0, ops.size()); +} + +TEST(RemoveUnusedResourceVariableTest, RemoveUnusedResourceVariables) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 1 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + OpMemberDecorate %19 0 Offset 0 + OpDecorate %19 BufferBlock + OpDecorate %21 DescriptorSet 1 + OpDecorate %21 Binding 0 + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %29 DescriptorSet 1 + OpDecorate %29 Binding 1 + OpDecorate %32 DescriptorSet 1 + OpDecorate %32 Binding 2 + OpDecorate %32 NonReadable + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %13 = OpTypePointer Uniform %6 + %16 = OpTypeStruct %6 %6 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypeStruct %6 + %20 = OpTypePointer Uniform %19 + %21 = OpVariable %20 Uniform + %22 = OpTypeStruct %6 + %23 = OpTypePointer PushConstant %22 + %24 = OpVariable %23 PushConstant + %25 = OpTypeFloat 32 + %26 = OpTypeImage %25 2D 0 0 0 1 Unknown + %27 = OpTypeSampledImage %26 + %28 = OpTypePointer UniformConstant %27 + %29 = OpVariable %28 UniformConstant + %30 = OpTypeImage %25 2D 0 0 0 2 Unknown + %31 = OpTypePointer UniformConstant %30 + %32 = OpVariable %31 UniformConstant + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + auto ops = RemoveUnusedInstructionReductionOpportunityFinder(true) + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(7, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + } + + std::string expected_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpMemberDecorate %19 0 Offset 0 + OpDecorate %19 BufferBlock + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Uniform %9 + %16 = OpTypeStruct %6 %6 + %17 = OpTypePointer Uniform %16 + %19 = OpTypeStruct %6 + %20 = OpTypePointer Uniform %19 + %22 = OpTypeStruct %6 + %23 = OpTypePointer PushConstant %22 + %25 = OpTypeFloat 32 + %26 = OpTypeImage %25 2D 0 0 0 1 Unknown + %27 = OpTypeSampledImage %26 + %28 = OpTypePointer UniformConstant %27 + %30 = OpTypeImage %25 2D 0 0 0 2 Unknown + %31 = OpTypePointer UniformConstant %30 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, expected_1, context.get()); + + ops = RemoveUnusedInstructionReductionOpportunityFinder(true) + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(6, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + } + + std::string expected_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpDecorate %16 Block + OpMemberDecorate %19 0 Offset 0 + OpDecorate %19 BufferBlock + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeStruct %6 + %16 = OpTypeStruct %6 %6 + %19 = OpTypeStruct %6 + %22 = OpTypeStruct %6 + %25 = OpTypeFloat 32 + %26 = OpTypeImage %25 2D 0 0 0 1 Unknown + %27 = OpTypeSampledImage %26 + %30 = OpTypeImage %25 2D 0 0 0 2 Unknown + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, expected_2, context.get()); + + ops = RemoveUnusedInstructionReductionOpportunityFinder(true) + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(6, ops.size()); + + for (auto& op : ops) { + ASSERT_TRUE(op->PreconditionHolds()); + op->TryToApply(); + } + + std::string expected_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %25 = OpTypeFloat 32 + %26 = OpTypeImage %25 2D 0 0 0 1 Unknown + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, expected_3, context.get()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/remove_unused_struct_member_test.cpp b/third_party/spirv-tools/test/reduce/remove_unused_struct_member_test.cpp new file mode 100644 index 0000000..d3c1487 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/remove_unused_struct_member_test.cpp @@ -0,0 +1,238 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(RemoveUnusedStructMemberTest, RemoveOneMember) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Function %7 + %50 = OpConstant %6 0 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpConstant %6 4 + %14 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %12 + %15 = OpAccessChain %14 %9 %10 + %22 = OpInBoundsAccessChain %14 %9 %10 + %20 = OpLoad %7 %9 + %21 = OpCompositeExtract %6 %20 1 + %23 = OpCompositeInsert %7 %10 %20 1 + OpStore %15 %13 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + auto ops = RemoveUnusedStructMemberReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Function %7 + %50 = OpConstant %6 0 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %11 + %13 = OpConstant %6 4 + %14 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %12 + %15 = OpAccessChain %14 %9 %50 + %22 = OpInBoundsAccessChain %14 %9 %50 + %20 = OpLoad %7 %9 + %21 = OpCompositeExtract %6 %20 0 + %23 = OpCompositeInsert %7 %10 %20 0 + OpStore %15 %13 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, expected, context.get()); +} + +TEST(RemoveUnusedStructMemberTest, RemoveUniformBufferMember) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %10 0 Offset 0 + OpMemberDecorate %10 1 Offset 4 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpTypeInt 32 1 + %10 = OpTypeStruct %9 %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %13 = OpConstant %9 1 + %20 = OpConstant %9 0 + %14 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %15 = OpAccessChain %14 %12 %13 + %16 = OpLoad %6 %15 + OpStore %8 %16 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + auto ops = RemoveUnusedStructMemberReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %10 0 Offset 4 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpTypeInt 32 1 + %10 = OpTypeStruct %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %13 = OpConstant %9 1 + %20 = OpConstant %9 0 + %14 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %15 = OpAccessChain %14 %12 %20 + %16 = OpLoad %6 %15 + OpStore %8 %16 + OpReturn + OpFunctionEnd + )"; + + CheckEqual(env, expected, context.get()); +} + +TEST(RemoveUnusedStructMemberTest, DoNotRemoveNamedMemberRemoveOneMember) { + // This illustrates that naming a member is enough to prevent its removal. + // Removal of names is done by a different pass. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberName %7 0 "someName" + OpMemberName %7 1 "someOtherName" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Function %7 + %50 = OpConstant %6 0 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpConstant %6 4 + %14 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %12 + %15 = OpAccessChain %14 %9 %10 + %22 = OpInBoundsAccessChain %14 %9 %10 + %20 = OpLoad %7 %9 + %21 = OpCompositeExtract %6 %20 1 + %23 = OpCompositeInsert %7 %10 %20 1 + OpStore %15 %13 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kReduceAssembleOption); + + auto ops = RemoveUnusedStructMemberReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp b/third_party/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp new file mode 100644 index 0000000..fcc9d72 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/simple_conditional_branch_to_branch_test.cpp @@ -0,0 +1,486 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/reduction_pass.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +const spv_target_env kEnv = SPV_ENV_UNIVERSAL_1_3; + +TEST(SimpleConditionalBranchToBranchTest, Diamond) { + // A test with the following structure. + // + // selection header + // OpBranchConditional + // || + // b b + // | | + // selection merge + // + // The conditional branch cannot be simplified because selection headers + // cannot end with OpBranch. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %12 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, DiamondNoSelection) { + // A test with the following structure. + // + // OpBranchConditional + // || + // b b + // | / + // b + // + // The conditional branch can be simplified. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranchConditional %8 %12 %12 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CheckEqual(kEnv, after, context.get()); + + ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, ConditionalBranchesButNotSimple) { + // A test with the following structure. + // + // selection header + // OpBranchConditional + // | | + // b OpBranchConditional + // | | | + // | b | + // | | | + // selection merge + // + // None of the conditional branches can be simplified; the first is not simple + // AND part of a selection header; the second is just not simple (where + // "simple" means it only has one target). + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %12 %13 + %12 = OpLabel + OpBranch %11 + %13 = OpLabel + OpBranchConditional %8 %14 %11 + %14 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + auto context = BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, SimplifyBackEdge) { + // A test with the following structure. The loop has a continue construct that + // ends with OpBranchConditional. The OpBranchConditional can be simplified. + // + // loop header + // | + // loop continue target and back-edge block + // OpBranchConditional + // || + // loop merge (to loop header^) + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %12 + %12 = OpLabel + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, + DontRemoveBackEdgeCombinedHeaderContinue) { + // A test with the following structure. + // + // loop header and continue target and back-edge block + // OpBranchConditional + // || + // loop merge (to loop header^) + // + // The conditional branch can be simplified. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %10 None + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %10 None + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(SimpleConditionalBranchToBranchTest, BackEdgeUnreachable) { + // A test with the following structure. I.e. a loop with an unreachable + // continue construct that ends with OpBranchConditional. + // + // loop header + // | + // | loop continue target (unreachable) + // | | + // | back-edge block (unreachable) + // | OpBranchConditional + // | || + // loop merge (to loop header^) + // + // The conditional branch can be simplified. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %11 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranchConditional %8 %10 %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto context = + BuildModule(kEnv, nullptr, shader, kReduceAssembleOption); + + CheckValid(kEnv, context.get()); + + auto ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(kEnv, context.get()); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %11 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %10 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(kEnv, after, context.get()); + + ops = SimpleConditionalBranchToBranchOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp b/third_party/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp new file mode 100644 index 0000000..0cfcfdf --- /dev/null +++ b/third_party/spirv-tools/test/reduce/structured_loop_to_selection_test.cpp @@ -0,0 +1,3628 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" + +#include "source/opt/build_module.h" +#include "source/reduce/reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %22 = OpConstantTrue %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %22 %14 %12 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %12 + %13 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %7 Function + %40 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %25 = OpLoad %6 %19 + %26 = OpSLessThan %17 %25 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + OpBranch %23 + %23 = OpLabel + %27 = OpLoad %6 %19 + %29 = OpIAdd %6 %27 %28 + OpStore %19 %29 + OpBranch %20 + %22 = OpLabel + OpBranch %13 + %13 = OpLabel + %30 = OpLoad %6 %8 + %31 = OpIAdd %6 %30 %28 + OpStore %8 %31 + OpBranch %10 + %12 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %38 = OpLoad %6 %32 + %39 = OpSLessThan %17 %38 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpStore %40 %9 + OpBranch %41 + %41 = OpLabel + OpLoopMerge %43 %44 None + OpBranch %45 + %45 = OpLabel + %46 = OpLoad %6 %40 + %47 = OpSLessThan %17 %46 %16 + OpBranchConditional %47 %42 %43 + %42 = OpLabel + OpBranch %44 + %44 = OpLabel + %48 = OpLoad %6 %40 + %49 = OpIAdd %6 %48 %28 + OpStore %40 %49 + OpBranch %41 + %43 = OpLabel + OpBranch %36 + %36 = OpLabel + %50 = OpLoad %6 %32 + %51 = OpIAdd %6 %50 %28 + OpStore %32 %51 + OpBranch %33 + %35 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(4, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %28 = OpConstant %6 1 + %52 = OpConstantTrue %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %7 Function + %40 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %52 %14 %12 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %25 = OpLoad %6 %19 + %26 = OpSLessThan %17 %25 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + OpBranch %23 + %23 = OpLabel + %27 = OpLoad %6 %19 + %29 = OpIAdd %6 %27 %28 + OpStore %19 %29 + OpBranch %20 + %22 = OpLabel + OpBranch %12 + %13 = OpLabel + %30 = OpLoad %6 %8 + %31 = OpIAdd %6 %30 %28 + OpStore %8 %31 + OpBranch %10 + %12 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %38 = OpLoad %6 %32 + %39 = OpSLessThan %17 %38 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpStore %40 %9 + OpBranch %41 + %41 = OpLabel + OpLoopMerge %43 %44 None + OpBranch %45 + %45 = OpLabel + %46 = OpLoad %6 %40 + %47 = OpSLessThan %17 %46 %16 + OpBranchConditional %47 %42 %43 + %42 = OpLabel + OpBranch %44 + %44 = OpLabel + %48 = OpLoad %6 %40 + %49 = OpIAdd %6 %48 %28 + OpStore %40 %49 + OpBranch %41 + %43 = OpLabel + OpBranch %36 + %36 = OpLabel + %50 = OpLoad %6 %32 + %51 = OpIAdd %6 %50 %28 + OpStore %32 %51 + OpBranch %33 + %35 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %28 = OpConstant %6 1 + %52 = OpConstantTrue %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %7 Function + %40 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %52 %14 %12 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + OpSelectionMerge %22 None + OpBranchConditional %52 %24 %22 + %24 = OpLabel + %25 = OpLoad %6 %19 + %26 = OpSLessThan %17 %25 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + OpBranch %22 + %23 = OpLabel + %27 = OpLoad %6 %19 + %29 = OpIAdd %6 %27 %28 + OpStore %19 %29 + OpBranch %20 + %22 = OpLabel + OpBranch %12 + %13 = OpLabel + %30 = OpLoad %6 %8 + %31 = OpIAdd %6 %30 %28 + OpStore %8 %31 + OpBranch %10 + %12 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %38 = OpLoad %6 %32 + %39 = OpSLessThan %17 %38 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpStore %40 %9 + OpBranch %41 + %41 = OpLabel + OpLoopMerge %43 %44 None + OpBranch %45 + %45 = OpLabel + %46 = OpLoad %6 %40 + %47 = OpSLessThan %17 %46 %16 + OpBranchConditional %47 %42 %43 + %42 = OpLabel + OpBranch %44 + %44 = OpLabel + %48 = OpLoad %6 %40 + %49 = OpIAdd %6 %48 %28 + OpStore %40 %49 + OpBranch %41 + %43 = OpLabel + OpBranch %36 + %36 = OpLabel + %50 = OpLoad %6 %32 + %51 = OpIAdd %6 %50 %28 + OpStore %32 %51 + OpBranch %33 + %35 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_1, context.get()); + + ASSERT_TRUE(ops[2]->PreconditionHolds()); + ops[2]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %28 = OpConstant %6 1 + %52 = OpConstantTrue %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %7 Function + %40 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %52 %14 %12 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + OpSelectionMerge %22 None + OpBranchConditional %52 %24 %22 + %24 = OpLabel + %25 = OpLoad %6 %19 + %26 = OpSLessThan %17 %25 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + OpBranch %22 + %23 = OpLabel + %27 = OpLoad %6 %19 + %29 = OpIAdd %6 %27 %28 + OpStore %19 %29 + OpBranch %20 + %22 = OpLabel + OpBranch %12 + %13 = OpLabel + %30 = OpLoad %6 %8 + %31 = OpIAdd %6 %30 %28 + OpStore %8 %31 + OpBranch %10 + %12 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + OpSelectionMerge %35 None + OpBranchConditional %52 %37 %35 + %37 = OpLabel + %38 = OpLoad %6 %32 + %39 = OpSLessThan %17 %38 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpStore %40 %9 + OpBranch %41 + %41 = OpLabel + OpLoopMerge %43 %44 None + OpBranch %45 + %45 = OpLabel + %46 = OpLoad %6 %40 + %47 = OpSLessThan %17 %46 %16 + OpBranchConditional %47 %42 %43 + %42 = OpLabel + OpBranch %44 + %44 = OpLabel + %48 = OpLoad %6 %40 + %49 = OpIAdd %6 %48 %28 + OpStore %40 %49 + OpBranch %41 + %43 = OpLabel + OpBranch %35 + %36 = OpLabel + %50 = OpLoad %6 %32 + %51 = OpIAdd %6 %50 %28 + OpStore %32 %51 + OpBranch %33 + %35 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_2, context.get()); + + ASSERT_TRUE(ops[3]->PreconditionHolds()); + ops[3]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %28 = OpConstant %6 1 + %52 = OpConstantTrue %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + %32 = OpVariable %7 Function + %40 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %52 %14 %12 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpStore %19 %9 + OpBranch %20 + %20 = OpLabel + OpSelectionMerge %22 None + OpBranchConditional %52 %24 %22 + %24 = OpLabel + %25 = OpLoad %6 %19 + %26 = OpSLessThan %17 %25 %16 + OpBranchConditional %26 %21 %22 + %21 = OpLabel + OpBranch %22 + %23 = OpLabel + %27 = OpLoad %6 %19 + %29 = OpIAdd %6 %27 %28 + OpStore %19 %29 + OpBranch %20 + %22 = OpLabel + OpBranch %12 + %13 = OpLabel + %30 = OpLoad %6 %8 + %31 = OpIAdd %6 %30 %28 + OpStore %8 %31 + OpBranch %10 + %12 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + OpSelectionMerge %35 None + OpBranchConditional %52 %37 %35 + %37 = OpLabel + %38 = OpLoad %6 %32 + %39 = OpSLessThan %17 %38 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpStore %40 %9 + OpBranch %41 + %41 = OpLabel + OpSelectionMerge %43 None + OpBranchConditional %52 %45 %43 + %45 = OpLabel + %46 = OpLoad %6 %40 + %47 = OpSLessThan %17 %46 %16 + OpBranchConditional %47 %42 %43 + %42 = OpLabel + OpBranch %43 + %44 = OpLabel + %48 = OpLoad %6 %40 + %49 = OpIAdd %6 %48 %28 + OpStore %40 %49 + OpBranch %41 + %43 = OpLabel + OpBranch %35 + %36 = OpLabel + %50 = OpLoad %6 %32 + %51 = OpIAdd %6 %50 %28 + OpStore %32 %51 + OpBranch %33 + %35 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_3, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 10 + %16 = OpConstant %6 0 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %23 = OpConstant %6 3 + %40 = OpConstant %6 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSGreaterThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpISub %6 %19 %20 + OpStore %8 %21 + %22 = OpLoad %6 %8 + %24 = OpSLessThan %17 %22 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + OpBranch %13 + %26 = OpLabel + OpBranch %28 + %28 = OpLabel + OpLoopMerge %30 %31 None + OpBranch %29 + %29 = OpLabel + %32 = OpLoad %6 %8 + %33 = OpISub %6 %32 %20 + OpStore %8 %33 + %34 = OpLoad %6 %8 + %35 = OpIEqual %17 %34 %20 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + OpReturn ; This return spoils everything: it means the merge does not post-dominate the header. + %37 = OpLabel + OpBranch %31 + %31 = OpLabel + %39 = OpLoad %6 %8 + %41 = OpSGreaterThan %17 %39 %40 + OpBranchConditional %41 %28 %30 + %30 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, LoopyShader4) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %13 = OpConstant %6 0 + %22 = OpTypeBool + %25 = OpConstant %6 1 + %39 = OpConstant %6 100 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpVariable %7 Function + %46 = OpVariable %7 Function + %47 = OpVariable %7 Function + %32 = OpVariable %7 Function + %42 = OpVariable %7 Function + OpStore %32 %13 + OpBranch %33 + %33 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %38 = OpLoad %6 %32 + %40 = OpSLessThan %22 %38 %39 + OpBranchConditional %40 %34 %35 + %34 = OpLabel + OpBranch %36 + %36 = OpLabel + %41 = OpLoad %6 %32 + OpStore %42 %25 + OpStore %45 %13 + OpStore %46 %13 + OpBranch %48 + %48 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %6 %46 + %53 = OpLoad %6 %42 + %54 = OpSLessThan %22 %52 %53 + OpBranchConditional %54 %55 %49 + %55 = OpLabel + %56 = OpLoad %6 %45 + %57 = OpIAdd %6 %56 %25 + OpStore %45 %57 + OpBranch %50 + %50 = OpLabel + %58 = OpLoad %6 %46 + %59 = OpIAdd %6 %58 %25 + OpStore %46 %59 + OpBranch %48 + %49 = OpLabel + %60 = OpLoad %6 %45 + OpStore %47 %60 + %43 = OpLoad %6 %47 + %44 = OpIAdd %6 %41 %43 + OpStore %32 %44 + OpBranch %33 + %35 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + // Initially there are two opportunities. + ASSERT_EQ(2, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %13 = OpConstant %6 0 + %22 = OpTypeBool + %25 = OpConstant %6 1 + %39 = OpConstant %6 100 + %61 = OpConstantTrue %22 + %62 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %45 = OpVariable %7 Function + %46 = OpVariable %7 Function + %47 = OpVariable %7 Function + %32 = OpVariable %7 Function + %42 = OpVariable %7 Function + OpStore %32 %13 + OpBranch %33 + %33 = OpLabel + OpSelectionMerge %35 None + OpBranchConditional %61 %37 %35 + %37 = OpLabel + %38 = OpLoad %6 %32 + %40 = OpSLessThan %22 %38 %39 + OpBranchConditional %40 %34 %35 + %34 = OpLabel + OpBranch %35 + %36 = OpLabel + %41 = OpLoad %6 %32 + OpStore %42 %25 + OpStore %45 %13 + OpStore %46 %13 + OpBranch %48 + %48 = OpLabel + OpLoopMerge %49 %50 None + OpBranch %51 + %51 = OpLabel + %52 = OpLoad %6 %46 + %53 = OpLoad %6 %42 + %54 = OpSLessThan %22 %52 %53 + OpBranchConditional %54 %55 %49 + %55 = OpLabel + %56 = OpLoad %6 %45 + %57 = OpIAdd %6 %56 %25 + OpStore %45 %57 + OpBranch %50 + %50 = OpLabel + %58 = OpLoad %6 %46 + %59 = OpIAdd %6 %58 %25 + OpStore %46 %59 + OpBranch %48 + %49 = OpLabel + %60 = OpLoad %6 %45 + OpStore %47 %60 + %43 = OpLoad %6 %47 + %44 = OpIAdd %6 %62 %43 + OpStore %32 %44 + OpBranch %33 + %35 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); + + // Applying the first opportunity has killed the second opportunity, because + // there was a loop embedded in the continue target of the loop we have just + // eliminated; the continue-embedded loop is now unreachable. + ASSERT_FALSE(ops[1]->PreconditionHolds()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, ConditionalBreak1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + OpBranch %8 + %13 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranchConditional %11 %6 %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %14 = OpConstantTrue %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %14 %7 %8 + %7 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %8 + %9 = OpLabel + OpBranchConditional %11 %6 %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, ConditionalBreak2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %11 %8 %13 + %13 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranchConditional %11 %6 %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %14 = OpConstantTrue %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %14 %7 %8 + %7 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %11 %13 %13 + %13 = OpLabel + OpBranch %8 + %9 = OpLabel + OpBranchConditional %11 %6 %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, UnconditionalBreak) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranch %7 + %7 = OpLabel + OpBranch %8 + %9 = OpLabel + OpBranch %6 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %11 %7 %8 + %7 = OpLabel + OpBranch %8 + %9 = OpLabel + OpBranch %6 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, Complex) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 4 + OpMemberDecorate %4 2 Offset 8 + OpMemberDecorate %4 3 Offset 12 + OpDecorate %4 Block + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeBool + %9 = OpTypePointer Function %8 + %10 = OpTypeInt 32 1 + %4 = OpTypeStruct %10 %10 %10 %10 + %11 = OpTypePointer Uniform %4 + %5 = OpVariable %11 Uniform + %12 = OpConstant %10 0 + %13 = OpTypePointer Uniform %10 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpConstant %10 1 + %17 = OpConstant %10 2 + %18 = OpConstant %10 3 + %19 = OpTypePointer Function %10 + %20 = OpConstantFalse %8 + %21 = OpTypeFloat 32 + %22 = OpTypeVector %21 4 + %23 = OpTypePointer Output %22 + %3 = OpVariable %23 Output + %2 = OpFunction %6 None %7 + %24 = OpLabel + %25 = OpVariable %9 Function + %26 = OpVariable %9 Function + %27 = OpVariable %9 Function + %28 = OpVariable %9 Function + %29 = OpVariable %9 Function + %30 = OpVariable %19 Function + %31 = OpAccessChain %13 %5 %12 + %32 = OpLoad %10 %31 + %33 = OpINotEqual %8 %32 %15 + OpStore %25 %33 + %34 = OpAccessChain %13 %5 %16 + %35 = OpLoad %10 %34 + %36 = OpINotEqual %8 %35 %15 + OpStore %26 %36 + %37 = OpAccessChain %13 %5 %17 + %38 = OpLoad %10 %37 + %39 = OpINotEqual %8 %38 %15 + OpStore %27 %39 + %40 = OpAccessChain %13 %5 %18 + %41 = OpLoad %10 %40 + %42 = OpINotEqual %8 %41 %15 + OpStore %28 %42 + %43 = OpLoad %8 %25 + OpStore %29 %43 + OpStore %30 %12 + OpBranch %44 + %44 = OpLabel + OpLoopMerge %45 %46 None + OpBranch %47 + %47 = OpLabel + %48 = OpLoad %8 %29 + OpBranchConditional %48 %49 %45 + %49 = OpLabel + %50 = OpLoad %8 %25 + OpSelectionMerge %51 None + OpBranchConditional %50 %52 %51 + %52 = OpLabel + %53 = OpLoad %8 %26 + OpStore %29 %53 + %54 = OpLoad %10 %30 + %55 = OpIAdd %10 %54 %16 + OpStore %30 %55 + OpBranch %51 + %51 = OpLabel + %56 = OpLoad %8 %26 + OpSelectionMerge %57 None + OpBranchConditional %56 %58 %57 + %58 = OpLabel + %59 = OpLoad %10 %30 + %60 = OpIAdd %10 %59 %16 + OpStore %30 %60 + %61 = OpLoad %8 %29 + %62 = OpLoad %8 %25 + %63 = OpLogicalOr %8 %61 %62 + OpStore %29 %63 + %64 = OpLoad %8 %27 + OpSelectionMerge %65 None + OpBranchConditional %64 %66 %65 + %66 = OpLabel + %67 = OpLoad %10 %30 + %68 = OpIAdd %10 %67 %17 + OpStore %30 %68 + %69 = OpLoad %8 %29 + %70 = OpLogicalNot %8 %69 + OpStore %29 %70 + OpBranch %46 + %65 = OpLabel + %71 = OpLoad %8 %29 + %72 = OpLogicalOr %8 %71 %20 + OpStore %29 %72 + OpBranch %46 + %57 = OpLabel + OpBranch %73 + %73 = OpLabel + OpLoopMerge %74 %75 None + OpBranch %76 + %76 = OpLabel + %77 = OpLoad %8 %28 + OpSelectionMerge %78 None + OpBranchConditional %77 %79 %80 + %79 = OpLabel + %81 = OpLoad %10 %30 + OpSelectionMerge %82 None + OpSwitch %81 %83 1 %84 2 %85 + %83 = OpLabel + OpBranch %82 + %84 = OpLabel + %86 = OpLoad %8 %29 + %87 = OpSelect %10 %86 %16 %17 + %88 = OpLoad %10 %30 + %89 = OpIAdd %10 %88 %87 + OpStore %30 %89 + OpBranch %82 + %85 = OpLabel + OpBranch %75 + %82 = OpLabel + %90 = OpLoad %8 %27 + OpSelectionMerge %91 None + OpBranchConditional %90 %92 %91 + %92 = OpLabel + OpBranch %75 + %91 = OpLabel + OpBranch %78 + %80 = OpLabel + OpBranch %74 + %78 = OpLabel + OpBranch %75 + %75 = OpLabel + %93 = OpLoad %8 %29 + OpBranchConditional %93 %73 %74 + %74 = OpLabel + OpBranch %46 + %46 = OpLabel + OpBranch %44 + %45 = OpLabel + %94 = OpLoad %10 %30 + %95 = OpConvertSToF %21 %94 + %96 = OpCompositeConstruct %22 %95 %95 %95 %95 + OpStore %3 %96 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(2, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 4 + OpMemberDecorate %4 2 Offset 8 + OpMemberDecorate %4 3 Offset 12 + OpDecorate %4 Block + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeBool + %9 = OpTypePointer Function %8 + %10 = OpTypeInt 32 1 + %4 = OpTypeStruct %10 %10 %10 %10 + %11 = OpTypePointer Uniform %4 + %5 = OpVariable %11 Uniform + %12 = OpConstant %10 0 + %13 = OpTypePointer Uniform %10 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpConstant %10 1 + %17 = OpConstant %10 2 + %18 = OpConstant %10 3 + %19 = OpTypePointer Function %10 + %20 = OpConstantFalse %8 + %21 = OpTypeFloat 32 + %22 = OpTypeVector %21 4 + %23 = OpTypePointer Output %22 + %3 = OpVariable %23 Output + %97 = OpConstantTrue %8 + %2 = OpFunction %6 None %7 + %24 = OpLabel + %25 = OpVariable %9 Function + %26 = OpVariable %9 Function + %27 = OpVariable %9 Function + %28 = OpVariable %9 Function + %29 = OpVariable %9 Function + %30 = OpVariable %19 Function + %31 = OpAccessChain %13 %5 %12 + %32 = OpLoad %10 %31 + %33 = OpINotEqual %8 %32 %15 + OpStore %25 %33 + %34 = OpAccessChain %13 %5 %16 + %35 = OpLoad %10 %34 + %36 = OpINotEqual %8 %35 %15 + OpStore %26 %36 + %37 = OpAccessChain %13 %5 %17 + %38 = OpLoad %10 %37 + %39 = OpINotEqual %8 %38 %15 + OpStore %27 %39 + %40 = OpAccessChain %13 %5 %18 + %41 = OpLoad %10 %40 + %42 = OpINotEqual %8 %41 %15 + OpStore %28 %42 + %43 = OpLoad %8 %25 + OpStore %29 %43 + OpStore %30 %12 + OpBranch %44 + %44 = OpLabel + OpSelectionMerge %45 None ; Was OpLoopMerge %45 %46 None + OpBranchConditional %97 %47 %45 ; Was OpBranch %47 + %47 = OpLabel + %48 = OpLoad %8 %29 + OpBranchConditional %48 %49 %45 + %49 = OpLabel + %50 = OpLoad %8 %25 + OpSelectionMerge %51 None + OpBranchConditional %50 %52 %51 + %52 = OpLabel + %53 = OpLoad %8 %26 + OpStore %29 %53 + %54 = OpLoad %10 %30 + %55 = OpIAdd %10 %54 %16 + OpStore %30 %55 + OpBranch %51 + %51 = OpLabel + %56 = OpLoad %8 %26 + OpSelectionMerge %57 None + OpBranchConditional %56 %58 %57 + %58 = OpLabel + %59 = OpLoad %10 %30 + %60 = OpIAdd %10 %59 %16 + OpStore %30 %60 + %61 = OpLoad %8 %29 + %62 = OpLoad %8 %25 + %63 = OpLogicalOr %8 %61 %62 + OpStore %29 %63 + %64 = OpLoad %8 %27 + OpSelectionMerge %65 None + OpBranchConditional %64 %66 %65 + %66 = OpLabel + %67 = OpLoad %10 %30 + %68 = OpIAdd %10 %67 %17 + OpStore %30 %68 + %69 = OpLoad %8 %29 + %70 = OpLogicalNot %8 %69 + OpStore %29 %70 + OpBranch %65 ; Was OpBranch %46 + %65 = OpLabel + %71 = OpLoad %8 %29 + %72 = OpLogicalOr %8 %71 %20 + OpStore %29 %72 + OpBranch %57 ; Was OpBranch %46 + %57 = OpLabel + OpBranch %73 + %73 = OpLabel + OpLoopMerge %74 %75 None + OpBranch %76 + %76 = OpLabel + %77 = OpLoad %8 %28 + OpSelectionMerge %78 None + OpBranchConditional %77 %79 %80 + %79 = OpLabel + %81 = OpLoad %10 %30 + OpSelectionMerge %82 None + OpSwitch %81 %83 1 %84 2 %85 + %83 = OpLabel + OpBranch %82 + %84 = OpLabel + %86 = OpLoad %8 %29 + %87 = OpSelect %10 %86 %16 %17 + %88 = OpLoad %10 %30 + %89 = OpIAdd %10 %88 %87 + OpStore %30 %89 + OpBranch %82 + %85 = OpLabel + OpBranch %75 + %82 = OpLabel + %90 = OpLoad %8 %27 + OpSelectionMerge %91 None + OpBranchConditional %90 %92 %91 + %92 = OpLabel + OpBranch %75 + %91 = OpLabel + OpBranch %78 + %80 = OpLabel + OpBranch %74 + %78 = OpLabel + OpBranch %75 + %75 = OpLabel + %93 = OpLoad %8 %29 + OpBranchConditional %93 %73 %74 + %74 = OpLabel + OpBranch %45 ; Was OpBranch %46 + %46 = OpLabel + OpBranch %44 + %45 = OpLabel + %94 = OpLoad %10 %30 + %95 = OpConvertSToF %21 %94 + %96 = OpCompositeConstruct %22 %95 %95 %95 %95 + OpStore %3 %96 + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + CheckValid(env, context.get()); + + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 4 + OpMemberDecorate %4 2 Offset 8 + OpMemberDecorate %4 3 Offset 12 + OpDecorate %4 Block + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeBool + %9 = OpTypePointer Function %8 + %10 = OpTypeInt 32 1 + %4 = OpTypeStruct %10 %10 %10 %10 + %11 = OpTypePointer Uniform %4 + %5 = OpVariable %11 Uniform + %12 = OpConstant %10 0 + %13 = OpTypePointer Uniform %10 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpConstant %10 1 + %17 = OpConstant %10 2 + %18 = OpConstant %10 3 + %19 = OpTypePointer Function %10 + %20 = OpConstantFalse %8 + %21 = OpTypeFloat 32 + %22 = OpTypeVector %21 4 + %23 = OpTypePointer Output %22 + %3 = OpVariable %23 Output + %97 = OpConstantTrue %8 + %2 = OpFunction %6 None %7 + %24 = OpLabel + %25 = OpVariable %9 Function + %26 = OpVariable %9 Function + %27 = OpVariable %9 Function + %28 = OpVariable %9 Function + %29 = OpVariable %9 Function + %30 = OpVariable %19 Function + %31 = OpAccessChain %13 %5 %12 + %32 = OpLoad %10 %31 + %33 = OpINotEqual %8 %32 %15 + OpStore %25 %33 + %34 = OpAccessChain %13 %5 %16 + %35 = OpLoad %10 %34 + %36 = OpINotEqual %8 %35 %15 + OpStore %26 %36 + %37 = OpAccessChain %13 %5 %17 + %38 = OpLoad %10 %37 + %39 = OpINotEqual %8 %38 %15 + OpStore %27 %39 + %40 = OpAccessChain %13 %5 %18 + %41 = OpLoad %10 %40 + %42 = OpINotEqual %8 %41 %15 + OpStore %28 %42 + %43 = OpLoad %8 %25 + OpStore %29 %43 + OpStore %30 %12 + OpBranch %44 + %44 = OpLabel + OpSelectionMerge %45 None ; Was OpLoopMerge %45 %46 None + OpBranchConditional %97 %47 %45 ; Was OpBranch %47 + %47 = OpLabel + %48 = OpLoad %8 %29 + OpBranchConditional %48 %49 %45 + %49 = OpLabel + %50 = OpLoad %8 %25 + OpSelectionMerge %51 None + OpBranchConditional %50 %52 %51 + %52 = OpLabel + %53 = OpLoad %8 %26 + OpStore %29 %53 + %54 = OpLoad %10 %30 + %55 = OpIAdd %10 %54 %16 + OpStore %30 %55 + OpBranch %51 + %51 = OpLabel + %56 = OpLoad %8 %26 + OpSelectionMerge %57 None + OpBranchConditional %56 %58 %57 + %58 = OpLabel + %59 = OpLoad %10 %30 + %60 = OpIAdd %10 %59 %16 + OpStore %30 %60 + %61 = OpLoad %8 %29 + %62 = OpLoad %8 %25 + %63 = OpLogicalOr %8 %61 %62 + OpStore %29 %63 + %64 = OpLoad %8 %27 + OpSelectionMerge %65 None + OpBranchConditional %64 %66 %65 + %66 = OpLabel + %67 = OpLoad %10 %30 + %68 = OpIAdd %10 %67 %17 + OpStore %30 %68 + %69 = OpLoad %8 %29 + %70 = OpLogicalNot %8 %69 + OpStore %29 %70 + OpBranch %65 ; Was OpBranch %46 + %65 = OpLabel + %71 = OpLoad %8 %29 + %72 = OpLogicalOr %8 %71 %20 + OpStore %29 %72 + OpBranch %57 ; Was OpBranch %46 + %57 = OpLabel + OpBranch %73 + %73 = OpLabel + OpSelectionMerge %74 None ; Was OpLoopMerge %74 %75 None + OpBranchConditional %97 %76 %74 ; Was OpBranch %76 + %76 = OpLabel + %77 = OpLoad %8 %28 + OpSelectionMerge %78 None + OpBranchConditional %77 %79 %80 + %79 = OpLabel + %81 = OpLoad %10 %30 + OpSelectionMerge %82 None + OpSwitch %81 %83 1 %84 2 %85 + %83 = OpLabel + OpBranch %82 + %84 = OpLabel + %86 = OpLoad %8 %29 + %87 = OpSelect %10 %86 %16 %17 + %88 = OpLoad %10 %30 + %89 = OpIAdd %10 %88 %87 + OpStore %30 %89 + OpBranch %82 + %85 = OpLabel + OpBranch %82 + %82 = OpLabel + %90 = OpLoad %8 %27 + OpSelectionMerge %91 None + OpBranchConditional %90 %92 %91 + %92 = OpLabel + OpBranch %91 + %91 = OpLabel + OpBranch %78 + %80 = OpLabel + OpBranch %78 ; Was OpBranch %74 + %78 = OpLabel + OpBranch %74 + %75 = OpLabel + %93 = OpLoad %8 %29 + OpBranchConditional %93 %73 %74 + %74 = OpLabel + OpBranch %45 ; Was OpBranch %46 + %46 = OpLabel + OpBranch %44 + %45 = OpLabel + %94 = OpLoad %10 %30 + %95 = OpConvertSToF %21 %94 + %96 = OpCompositeConstruct %22 %95 %95 %95 %95 + OpStore %3 %96 + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_1, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, ComplexOptimized) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 4 + OpMemberDecorate %4 2 Offset 8 + OpMemberDecorate %4 3 Offset 12 + OpDecorate %4 Block + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeBool + %10 = OpTypeInt 32 1 + %4 = OpTypeStruct %10 %10 %10 %10 + %11 = OpTypePointer Uniform %4 + %5 = OpVariable %11 Uniform + %12 = OpConstant %10 0 + %13 = OpTypePointer Uniform %10 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpConstant %10 1 + %17 = OpConstant %10 2 + %18 = OpConstant %10 3 + %20 = OpConstantFalse %8 + %21 = OpTypeFloat 32 + %22 = OpTypeVector %21 4 + %23 = OpTypePointer Output %22 + %3 = OpVariable %23 Output + %2 = OpFunction %6 None %7 + %24 = OpLabel + %31 = OpAccessChain %13 %5 %12 + %32 = OpLoad %10 %31 + %33 = OpINotEqual %8 %32 %15 + %34 = OpAccessChain %13 %5 %16 + %35 = OpLoad %10 %34 + %36 = OpINotEqual %8 %35 %15 + %37 = OpAccessChain %13 %5 %17 + %38 = OpLoad %10 %37 + %39 = OpINotEqual %8 %38 %15 + %40 = OpAccessChain %13 %5 %18 + %41 = OpLoad %10 %40 + %42 = OpINotEqual %8 %41 %15 + OpBranch %44 + %44 = OpLabel + %98 = OpPhi %10 %12 %24 %107 %46 + %97 = OpPhi %8 %33 %24 %105 %46 + OpLoopMerge %45 %46 None + OpBranchConditional %97 %49 %45 + %49 = OpLabel + OpSelectionMerge %51 None + OpBranchConditional %33 %52 %51 + %52 = OpLabel + %55 = OpIAdd %10 %98 %16 + OpBranch %51 + %51 = OpLabel + %100 = OpPhi %10 %98 %49 %55 %52 + %113 = OpSelect %8 %33 %36 %97 + OpSelectionMerge %57 None + OpBranchConditional %36 %58 %57 + %58 = OpLabel + %60 = OpIAdd %10 %100 %16 + %63 = OpLogicalOr %8 %113 %33 + OpSelectionMerge %65 None + OpBranchConditional %39 %66 %65 + %66 = OpLabel + %68 = OpIAdd %10 %100 %18 + %70 = OpLogicalNot %8 %63 + OpBranch %46 + %65 = OpLabel + %72 = OpLogicalOr %8 %63 %20 + OpBranch %46 + %57 = OpLabel + OpBranch %73 + %73 = OpLabel + %99 = OpPhi %10 %100 %57 %109 %75 + OpLoopMerge %74 %75 None + OpBranch %76 + %76 = OpLabel + OpSelectionMerge %78 None + OpBranchConditional %42 %79 %80 + %79 = OpLabel + OpSelectionMerge %82 None + OpSwitch %99 %83 1 %84 2 %85 + %83 = OpLabel + OpBranch %82 + %84 = OpLabel + %87 = OpSelect %10 %113 %16 %17 + %89 = OpIAdd %10 %99 %87 + OpBranch %82 + %85 = OpLabel + OpBranch %75 + %82 = OpLabel + %110 = OpPhi %10 %99 %83 %89 %84 + OpSelectionMerge %91 None + OpBranchConditional %39 %92 %91 + %92 = OpLabel + OpBranch %75 + %91 = OpLabel + OpBranch %78 + %80 = OpLabel + OpBranch %74 + %78 = OpLabel + OpBranch %75 + %75 = OpLabel + %109 = OpPhi %10 %99 %85 %110 %92 %110 %78 + OpBranchConditional %113 %73 %74 + %74 = OpLabel + %108 = OpPhi %10 %99 %80 %109 %75 + OpBranch %46 + %46 = OpLabel + %107 = OpPhi %10 %68 %66 %60 %65 %108 %74 + %105 = OpPhi %8 %70 %66 %72 %65 %113 %74 + OpBranch %44 + %45 = OpLabel + %95 = OpConvertSToF %21 %98 + %96 = OpCompositeConstruct %22 %95 %95 %95 %95 + OpStore %3 %96 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(2, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 4 + OpMemberDecorate %4 2 Offset 8 + OpMemberDecorate %4 3 Offset 12 + OpDecorate %4 Block + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeBool + %10 = OpTypeInt 32 1 + %4 = OpTypeStruct %10 %10 %10 %10 + %11 = OpTypePointer Uniform %4 + %5 = OpVariable %11 Uniform + %12 = OpConstant %10 0 + %13 = OpTypePointer Uniform %10 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpConstant %10 1 + %17 = OpConstant %10 2 + %18 = OpConstant %10 3 + %20 = OpConstantFalse %8 + %21 = OpTypeFloat 32 + %22 = OpTypeVector %21 4 + %23 = OpTypePointer Output %22 + %3 = OpVariable %23 Output + %114 = OpUndef %10 + %115 = OpUndef %8 + %2 = OpFunction %6 None %7 + %24 = OpLabel + %31 = OpAccessChain %13 %5 %12 + %32 = OpLoad %10 %31 + %33 = OpINotEqual %8 %32 %15 + %34 = OpAccessChain %13 %5 %16 + %35 = OpLoad %10 %34 + %36 = OpINotEqual %8 %35 %15 + %37 = OpAccessChain %13 %5 %17 + %38 = OpLoad %10 %37 + %39 = OpINotEqual %8 %38 %15 + %40 = OpAccessChain %13 %5 %18 + %41 = OpLoad %10 %40 + %42 = OpINotEqual %8 %41 %15 + OpBranch %44 + %44 = OpLabel + %98 = OpPhi %10 %12 %24 %114 %46 + %97 = OpPhi %8 %33 %24 %115 %46 + OpSelectionMerge %45 None ; Was OpLoopMerge %45 %46 None + OpBranchConditional %97 %49 %45 + %49 = OpLabel + OpSelectionMerge %51 None + OpBranchConditional %33 %52 %51 + %52 = OpLabel + %55 = OpIAdd %10 %98 %16 + OpBranch %51 + %51 = OpLabel + %100 = OpPhi %10 %98 %49 %55 %52 + %113 = OpSelect %8 %33 %36 %97 + OpSelectionMerge %57 None + OpBranchConditional %36 %58 %57 + %58 = OpLabel + %60 = OpIAdd %10 %100 %16 + %63 = OpLogicalOr %8 %113 %33 + OpSelectionMerge %65 None + OpBranchConditional %39 %66 %65 + %66 = OpLabel + %68 = OpIAdd %10 %100 %18 + %70 = OpLogicalNot %8 %63 + OpBranch %65 ; Was OpBranch %46 + %65 = OpLabel + %72 = OpLogicalOr %8 %63 %20 + OpBranch %57 ; Was OpBranch %46 + %57 = OpLabel + OpBranch %73 + %73 = OpLabel + %99 = OpPhi %10 %100 %57 %109 %75 + OpLoopMerge %74 %75 None + OpBranch %76 + %76 = OpLabel + OpSelectionMerge %78 None + OpBranchConditional %42 %79 %80 + %79 = OpLabel + OpSelectionMerge %82 None + OpSwitch %99 %83 1 %84 2 %85 + %83 = OpLabel + OpBranch %82 + %84 = OpLabel + %87 = OpSelect %10 %113 %16 %17 + %89 = OpIAdd %10 %99 %87 + OpBranch %82 + %85 = OpLabel + OpBranch %75 + %82 = OpLabel + %110 = OpPhi %10 %99 %83 %89 %84 + OpSelectionMerge %91 None + OpBranchConditional %39 %92 %91 + %92 = OpLabel + OpBranch %75 + %91 = OpLabel + OpBranch %78 + %80 = OpLabel + OpBranch %74 + %78 = OpLabel + OpBranch %75 + %75 = OpLabel + %109 = OpPhi %10 %99 %85 %110 %92 %110 %78 + OpBranchConditional %113 %73 %74 + %74 = OpLabel + %108 = OpPhi %10 %99 %80 %109 %75 + OpBranch %45 ; Was OpBranch %46 + %46 = OpLabel + %107 = OpPhi %10 ; Was OpPhi %10 %68 %66 %60 %65 %108 %74 + %105 = OpPhi %8 ; Was OpPhi %8 %70 %66 %72 %65 %113 %74 + OpBranch %44 + %45 = OpLabel + %95 = OpConvertSToF %21 %98 + %96 = OpCompositeConstruct %22 %95 %95 %95 %95 + OpStore %3 %96 + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + CheckValid(env, context.get()); + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 4 + OpMemberDecorate %4 2 Offset 8 + OpMemberDecorate %4 3 Offset 12 + OpDecorate %4 Block + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %3 Location 0 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeBool + %10 = OpTypeInt 32 1 + %4 = OpTypeStruct %10 %10 %10 %10 + %11 = OpTypePointer Uniform %4 + %5 = OpVariable %11 Uniform + %12 = OpConstant %10 0 + %13 = OpTypePointer Uniform %10 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpConstant %10 1 + %17 = OpConstant %10 2 + %18 = OpConstant %10 3 + %20 = OpConstantFalse %8 + %21 = OpTypeFloat 32 + %22 = OpTypeVector %21 4 + %23 = OpTypePointer Output %22 + %3 = OpVariable %23 Output + %114 = OpUndef %10 + %115 = OpUndef %8 + %116 = OpConstantTrue %8 + %2 = OpFunction %6 None %7 + %24 = OpLabel + %31 = OpAccessChain %13 %5 %12 + %32 = OpLoad %10 %31 + %33 = OpINotEqual %8 %32 %15 + %34 = OpAccessChain %13 %5 %16 + %35 = OpLoad %10 %34 + %36 = OpINotEqual %8 %35 %15 + %37 = OpAccessChain %13 %5 %17 + %38 = OpLoad %10 %37 + %39 = OpINotEqual %8 %38 %15 + %40 = OpAccessChain %13 %5 %18 + %41 = OpLoad %10 %40 + %42 = OpINotEqual %8 %41 %15 + OpBranch %44 + %44 = OpLabel + %98 = OpPhi %10 %12 %24 %114 %46 + %97 = OpPhi %8 %33 %24 %115 %46 + OpSelectionMerge %45 None ; Was OpLoopMerge %45 %46 None + OpBranchConditional %97 %49 %45 + %49 = OpLabel + OpSelectionMerge %51 None + OpBranchConditional %33 %52 %51 + %52 = OpLabel + %55 = OpIAdd %10 %98 %16 + OpBranch %51 + %51 = OpLabel + %100 = OpPhi %10 %98 %49 %55 %52 + %113 = OpSelect %8 %33 %36 %97 + OpSelectionMerge %57 None + OpBranchConditional %36 %58 %57 + %58 = OpLabel + %60 = OpIAdd %10 %100 %16 + %63 = OpLogicalOr %8 %113 %33 + OpSelectionMerge %65 None + OpBranchConditional %39 %66 %65 + %66 = OpLabel + %68 = OpIAdd %10 %100 %18 + %70 = OpLogicalNot %8 %63 + OpBranch %65 ; Was OpBranch %46 + %65 = OpLabel + %72 = OpLogicalOr %8 %63 %20 + OpBranch %57 ; Was OpBranch %46 + %57 = OpLabel + OpBranch %73 + %73 = OpLabel + %99 = OpPhi %10 %100 %57 %114 %75 + OpSelectionMerge %74 None ; Was OpLoopMerge %74 %75 None + OpBranchConditional %116 %76 %74 + %76 = OpLabel + OpSelectionMerge %78 None + OpBranchConditional %42 %79 %80 + %79 = OpLabel + OpSelectionMerge %82 None + OpSwitch %99 %83 1 %84 2 %85 + %83 = OpLabel + OpBranch %82 + %84 = OpLabel + %87 = OpSelect %10 %113 %16 %17 + %89 = OpIAdd %10 %99 %87 + OpBranch %82 + %85 = OpLabel + OpBranch %82 ; Was OpBranch %75 + %82 = OpLabel + %110 = OpPhi %10 %99 %83 %89 %84 %114 %85 ; Was OpPhi %10 %99 %83 %89 %84 + OpSelectionMerge %91 None + OpBranchConditional %39 %92 %91 + %92 = OpLabel + OpBranch %91 ; OpBranch %75 + %91 = OpLabel + OpBranch %78 + %80 = OpLabel + OpBranch %78 ; Was OpBranch %74 + %78 = OpLabel + OpBranch %74 ; Was OpBranch %75 + %75 = OpLabel + %109 = OpPhi %10 ; Was OpPhi %10 %99 %85 %110 %92 %110 %78 + OpBranchConditional %115 %73 %74 + %74 = OpLabel + %108 = OpPhi %10 %114 %75 %114 %78 %114 %73 ; Was OpPhi %10 %99 %80 %109 %75 + OpBranch %45 ; Was OpBranch %46 + %46 = OpLabel + %107 = OpPhi %10 ; Was OpPhi %10 %68 %66 %60 %65 %108 %74 + %105 = OpPhi %8 ; Was OpPhi %8 %70 %66 %72 %65 %113 %74 + OpBranch %44 + %45 = OpLabel + %95 = OpConvertSToF %21 %98 + %96 = OpCompositeConstruct %22 %95 %95 %95 %95 + OpStore %3 %96 + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_1, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, DominanceIssue) { + // Exposes a scenario where redirecting edges results in uses of ids being + // non-dominated. We replace such uses with OpUndef to account for this. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %5 = OpTypeInt 32 1 + %7 = OpTypePointer Function %5 + %6 = OpTypeBool + %8 = OpConstantTrue %6 + %9 = OpConstant %5 10 + %10 = OpConstant %5 20 + %11 = OpConstant %5 30 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %8 %18 %19 + %18 = OpLabel + OpBranch %14 + %19 = OpLabel + %20 = OpIAdd %5 %9 %10 + OpBranch %17 + %17 = OpLabel + %21 = OpIAdd %5 %20 %11 + OpBranchConditional %8 %14 %15 + %15 = OpLabel + OpBranch %13 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %5 = OpTypeInt 32 1 + %7 = OpTypePointer Function %5 + %6 = OpTypeBool + %8 = OpConstantTrue %6 + %9 = OpConstant %5 10 + %10 = OpConstant %5 20 + %11 = OpConstant %5 30 + %22 = OpUndef %5 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %8 %16 %14 + %16 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %8 %18 %19 + %18 = OpLabel + OpBranch %17 + %19 = OpLabel + %20 = OpIAdd %5 %9 %10 + OpBranch %17 + %17 = OpLabel + %21 = OpIAdd %5 %22 %11 + OpBranchConditional %8 %14 %14 + %15 = OpLabel + OpBranch %13 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, AccessChainIssue) { + // Exposes a scenario where redirecting edges results in a use of an id + // generated by an access chain being non-dominated. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %56 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %28 0 Offset 0 + OpDecorate %28 Block + OpDecorate %30 DescriptorSet 0 + OpDecorate %30 Binding 0 + OpDecorate %56 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %60 = OpTypePointer Private %7 + %10 = OpConstant %6 0 + %11 = OpConstantComposite %7 %10 %10 + %12 = OpTypePointer Function %6 + %59 = OpTypePointer Private %6 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 0 + %24 = OpConstant %14 100 + %25 = OpTypeBool + %28 = OpTypeStruct %6 + %29 = OpTypePointer Uniform %28 + %30 = OpVariable %29 Uniform + %31 = OpTypePointer Uniform %6 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 1 + %45 = OpConstant %39 0 + %52 = OpConstant %14 1 + %54 = OpTypeVector %6 4 + %55 = OpTypePointer Output %54 + %56 = OpVariable %55 Output + %9 = OpVariable %60 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpVariable %12 Function + %16 = OpVariable %15 Function + %38 = OpVariable %12 Function + OpStore %9 %11 + OpStore %13 %10 + OpStore %16 %17 + OpBranch %18 + %18 = OpLabel + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + %23 = OpLoad %14 %16 + %26 = OpSLessThan %25 %23 %24 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + %27 = OpLoad %14 %16 + %32 = OpAccessChain %31 %30 %17 + %33 = OpLoad %6 %32 + %34 = OpConvertFToS %14 %33 + %35 = OpSLessThan %25 %27 %34 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %44 + %36 = OpLabel + %41 = OpAccessChain %59 %9 %40 + %42 = OpLoad %6 %41 + OpStore %38 %42 + OpBranch %20 + %44 = OpLabel + %46 = OpAccessChain %59 %9 %45 + OpBranch %37 + %37 = OpLabel + %47 = OpLoad %6 %46 + OpStore %38 %47 + %48 = OpLoad %6 %38 + %49 = OpLoad %6 %13 + %50 = OpFAdd %6 %49 %48 + OpStore %13 %50 + OpBranch %21 + %21 = OpLabel + %51 = OpLoad %14 %16 + %53 = OpIAdd %14 %51 %52 + OpStore %16 %53 + OpBranch %18 + %20 = OpLabel + %57 = OpLoad %6 %13 + %58 = OpCompositeConstruct %54 %57 %57 %57 %57 + OpStore %56 %58 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %56 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %28 0 Offset 0 + OpDecorate %28 Block + OpDecorate %30 DescriptorSet 0 + OpDecorate %30 Binding 0 + OpDecorate %56 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %60 = OpTypePointer Private %7 + %10 = OpConstant %6 0 + %11 = OpConstantComposite %7 %10 %10 + %12 = OpTypePointer Function %6 + %59 = OpTypePointer Private %6 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %17 = OpConstant %14 0 + %24 = OpConstant %14 100 + %25 = OpTypeBool + %28 = OpTypeStruct %6 + %29 = OpTypePointer Uniform %28 + %30 = OpVariable %29 Uniform + %31 = OpTypePointer Uniform %6 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 1 + %45 = OpConstant %39 0 + %52 = OpConstant %14 1 + %54 = OpTypeVector %6 4 + %55 = OpTypePointer Output %54 + %56 = OpVariable %55 Output + %9 = OpVariable %60 Private + %61 = OpConstantTrue %25 + %62 = OpVariable %59 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpVariable %12 Function + %16 = OpVariable %15 Function + %38 = OpVariable %12 Function + OpStore %9 %11 + OpStore %13 %10 + OpStore %16 %17 + OpBranch %18 + %18 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %61 %22 %20 + %22 = OpLabel + %23 = OpLoad %14 %16 + %26 = OpSLessThan %25 %23 %24 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + %27 = OpLoad %14 %16 + %32 = OpAccessChain %31 %30 %17 + %33 = OpLoad %6 %32 + %34 = OpConvertFToS %14 %33 + %35 = OpSLessThan %25 %27 %34 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %44 + %36 = OpLabel + %41 = OpAccessChain %59 %9 %40 + %42 = OpLoad %6 %41 + OpStore %38 %42 + OpBranch %37 + %44 = OpLabel + %46 = OpAccessChain %59 %9 %45 + OpBranch %37 + %37 = OpLabel + %47 = OpLoad %6 %62 + OpStore %38 %47 + %48 = OpLoad %6 %38 + %49 = OpLoad %6 %13 + %50 = OpFAdd %6 %49 %48 + OpStore %13 %50 + OpBranch %20 + %21 = OpLabel + %51 = OpLoad %14 %16 + %53 = OpIAdd %14 %51 %52 + OpStore %16 %53 + OpBranch %18 + %20 = OpLabel + %57 = OpLoad %6 %13 + %58 = OpCompositeConstruct %54 %57 %57 %57 %57 + OpStore %56 %58 + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, DominanceAndPhiIssue) { + // Exposes an interesting scenario where a use in a phi stops being dominated + // by the block with which it is associated in the phi. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %17 = OpTypeBool + %18 = OpConstantTrue %17 + %19 = OpConstantFalse %17 + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %22 = OpConstant %20 6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %16 %15 None + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %18 %8 %9 + %8 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %18 %10 %11 + %9 = OpLabel + OpBranch %16 + %10 = OpLabel + OpBranch %16 + %11 = OpLabel + %23 = OpIAdd %20 %21 %22 + OpBranch %12 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + %24 = OpPhi %20 %23 %13 + OpBranchConditional %19 %15 %16 + %15 = OpLabel + OpBranch %6 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %17 = OpTypeBool + %18 = OpConstantTrue %17 + %19 = OpConstantFalse %17 + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %22 = OpConstant %20 6 + %25 = OpUndef %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %18 %7 %16 + %7 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %18 %8 %9 + %8 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %18 %10 %11 + %9 = OpLabel + OpBranch %13 + %10 = OpLabel + OpBranch %12 + %11 = OpLabel + %23 = OpIAdd %20 %21 %22 + OpBranch %12 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + %24 = OpPhi %20 %25 %13 + OpBranchConditional %19 %16 %16 + %15 = OpLabel + OpBranch %6 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, OpLineBeforeOpPhi) { + // Test to ensure the pass knows OpLine and OpPhi instructions can be + // interleaved. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpString "somefile" + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 10 + %8 = OpConstant %6 20 + %9 = OpConstant %6 30 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %2 = OpFunction %4 None %5 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %11 %18 %19 + %18 = OpLabel + %20 = OpIAdd %6 %7 %8 + %21 = OpIAdd %6 %7 %9 + OpBranch %17 + %19 = OpLabel + OpBranch %14 + %17 = OpLabel + %22 = OpPhi %6 %20 %18 + OpLine %3 0 0 + %23 = OpPhi %6 %21 %18 + OpBranch %15 + %15 = OpLabel + OpBranch %13 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpString "somefile" + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 10 + %8 = OpConstant %6 20 + %9 = OpConstant %6 30 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %24 = OpUndef %6 + %2 = OpFunction %4 None %5 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %11 %16 %14 + %16 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %11 %18 %19 + %18 = OpLabel + %20 = OpIAdd %6 %7 %8 + %21 = OpIAdd %6 %7 %9 + OpBranch %17 + %19 = OpLabel + OpBranch %17 + %17 = OpLabel + %22 = OpPhi %6 %20 %18 %24 %19 + OpLine %3 0 0 + %23 = OpPhi %6 %21 %18 %24 %19 + OpBranch %14 + %15 = OpLabel + OpBranch %13 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + SelectionMergeIsContinueTarget) { + // Example where a loop's continue target is also the target of a selection. + // In this scenario we cautiously do not apply the transformation. + std::string shader = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %3 = OpTypeBool + %4 = OpTypeFunction %2 + %1 = OpFunction %2 None %4 + %5 = OpLabel + %6 = OpUndef %3 + OpBranch %7 + %7 = OpLabel + %8 = OpPhi %3 %6 %5 %9 %10 + OpLoopMerge %11 %10 None + OpBranch %12 + %12 = OpLabel + %13 = OpUndef %3 + OpSelectionMerge %10 None + OpBranchConditional %13 %14 %10 + %14 = OpLabel + OpBranch %10 + %10 = OpLabel + %9 = OpUndef %3 + OpBranchConditional %9 %7 %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + // There should be no opportunities. + ASSERT_EQ(0, ops.size()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + SwitchSelectionMergeIsContinueTarget) { + // Another example where a loop's continue target is also the target of a + // selection; this time a selection associated with an OpSwitch. We + // cautiously do not apply the transformation. + std::string shader = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %3 = OpTypeBool + %5 = OpTypeInt 32 1 + %4 = OpTypeFunction %2 + %6 = OpConstant %5 2 + %7 = OpConstantTrue %3 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %14 %15 None + OpBranchConditional %7 %10 %14 + %10 = OpLabel + OpSelectionMerge %15 None + OpSwitch %6 %12 1 %11 2 %11 3 %15 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranch %15 + %15 = OpLabel + OpBranch %9 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + // There should be no opportunities. + ASSERT_EQ(0, ops.size()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, ContinueTargetIsSwitchTarget) { + std::string shader = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %3 = OpTypeBool + %5 = OpTypeInt 32 1 + %4 = OpTypeFunction %2 + %6 = OpConstant %5 2 + %7 = OpConstantTrue %3 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %14 %12 None + OpBranchConditional %7 %10 %14 + %10 = OpLabel + OpSelectionMerge %15 None + OpSwitch %6 %12 1 %11 2 %11 3 %15 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranch %9 + %15 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %3 = OpTypeBool + %5 = OpTypeInt 32 1 + %4 = OpTypeFunction %2 + %6 = OpConstant %5 2 + %7 = OpConstantTrue %3 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %7 %10 %14 + %10 = OpLabel + OpSelectionMerge %15 None + OpSwitch %6 %15 1 %11 2 %11 3 %15 + %11 = OpLabel + OpBranch %15 + %12 = OpLabel + OpBranch %9 + %15 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + MultipleSwitchTargetsAreContinueTarget) { + std::string shader = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %3 = OpTypeBool + %5 = OpTypeInt 32 1 + %4 = OpTypeFunction %2 + %6 = OpConstant %5 2 + %7 = OpConstantTrue %3 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %14 %12 None + OpBranchConditional %7 %10 %14 + %10 = OpLabel + OpSelectionMerge %15 None + OpSwitch %6 %11 1 %12 2 %12 3 %15 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranch %9 + %15 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %3 = OpTypeBool + %5 = OpTypeInt 32 1 + %4 = OpTypeFunction %2 + %6 = OpConstant %5 2 + %7 = OpConstantTrue %3 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %7 %10 %14 + %10 = OpLabel + OpSelectionMerge %15 None + OpSwitch %6 %11 1 %15 2 %15 3 %15 + %11 = OpLabel + OpBranch %15 + %12 = OpLabel + OpBranch %9 + %15 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, LoopBranchesStraightToMerge) { + std::string shader = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %4 = OpTypeFunction %2 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %14 %12 None + OpBranch %14 + %12 = OpLabel + OpBranch %9 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %4 = OpTypeFunction %2 + %15 = OpTypeBool + %16 = OpConstantTrue %15 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %16 %14 %14 + %12 = OpLabel + OpBranch %9 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + LoopConditionallyJumpsToMergeOrContinue) { + std::string shader = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %3 = OpTypeBool + %4 = OpTypeFunction %2 + %7 = OpConstantTrue %3 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %14 %12 None + OpBranchConditional %7 %14 %12 + %12 = OpLabel + OpBranch %9 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + %2 = OpTypeVoid + %3 = OpTypeBool + %4 = OpTypeFunction %2 + %7 = OpConstantTrue %3 + %1 = OpFunction %2 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %7 %14 %14 + %12 = OpLabel + OpBranch %9 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, MultipleAccessChains) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 + %8 = OpTypeStruct %7 + %9 = OpTypePointer Function %8 + %11 = OpConstant %6 3 + %12 = OpConstantComposite %7 %11 + %13 = OpConstantComposite %8 %12 + %14 = OpTypePointer Function %7 + %16 = OpConstant %6 0 + %19 = OpTypePointer Function %6 + %15 = OpTypeBool + %18 = OpConstantTrue %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + %20 = OpVariable %19 Function + OpStore %10 %13 + OpBranch %23 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + OpSelectionMerge %28 None + OpBranchConditional %18 %29 %25 + %29 = OpLabel + %17 = OpAccessChain %14 %10 %16 + OpBranch %28 + %28 = OpLabel + %21 = OpAccessChain %19 %17 %16 + %22 = OpLoad %6 %21 + %24 = OpAccessChain %19 %10 %16 %16 + OpStore %24 %22 + OpBranch %25 + %26 = OpLabel + OpBranch %23 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 + %8 = OpTypeStruct %7 + %9 = OpTypePointer Function %8 + %11 = OpConstant %6 3 + %12 = OpConstantComposite %7 %11 + %13 = OpConstantComposite %8 %12 + %14 = OpTypePointer Function %7 + %16 = OpConstant %6 0 + %19 = OpTypePointer Function %6 + %15 = OpTypeBool + %18 = OpConstantTrue %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + %20 = OpVariable %19 Function + %30 = OpVariable %14 Function + OpStore %10 %13 + OpBranch %23 + %23 = OpLabel + OpSelectionMerge %25 None + OpBranchConditional %18 %27 %25 + %27 = OpLabel + OpSelectionMerge %28 None + OpBranchConditional %18 %29 %28 + %29 = OpLabel + %17 = OpAccessChain %14 %10 %16 + OpBranch %28 + %28 = OpLabel + %21 = OpAccessChain %19 %30 %16 + %22 = OpLoad %6 %21 + %24 = OpAccessChain %19 %10 %16 %16 + OpStore %24 %22 + OpBranch %25 + %26 = OpLabel + OpBranch %23 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + UnreachableInnerLoopContinueBranchingToOuterLoopMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %12 + %13 = OpLabel + OpBranchConditional %6 %9 %11 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(2, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %6 %11 %9 + %11 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %12 + %13 = OpLabel + OpBranchConditional %6 %9 %11 + %12 = OpLabel + OpBranch %9 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + + CheckValid(env, context.get()); + + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %6 %11 %9 + %11 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %6 %12 %12 + %13 = OpLabel + OpBranchConditional %6 %9 %11 + %12 = OpLabel + OpBranch %9 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_1, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + UnreachableInnerLoopContinueBranchingToOuterLoopMerge2) { + // In this test, the branch to the outer loop merge from the inner loop's + // continue is part of a structured selection. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %12 + %13 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %6 %9 %14 + %14 = OpLabel + OpBranch %11 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(2, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %6 %11 %9 + %11 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %12 + %13 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %6 %9 %14 + %14 = OpLabel + OpBranch %11 + %12 = OpLabel + OpBranch %9 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); + + ASSERT_TRUE(ops[1]->PreconditionHolds()); + ops[1]->TryToApply(); + + CheckValid(env, context.get()); + + std::string after_op_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %6 %11 %9 + %11 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %6 %12 %12 + %13 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %6 %9 %14 + %14 = OpLabel + OpBranch %11 + %12 = OpLabel + OpBranch %9 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_1, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + InnerLoopHeaderBranchesToOuterLoopMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %9 %10 None + OpBranch %11 + %11 = OpLabel + OpLoopMerge %12 %13 None + OpBranchConditional %6 %9 %13 + %13 = OpLabel + OpBranchConditional %6 %11 %12 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + // We cannot transform the inner loop due to its header jumping straight to + // the outer loop merge (the inner loop's merge does not post-dominate its + // header). + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %6 %11 %9 + %11 = OpLabel + OpLoopMerge %12 %13 None + OpBranchConditional %6 %12 %13 + %13 = OpLabel + OpBranchConditional %6 %11 %12 + %12 = OpLabel + OpBranch %9 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); + + // Now look again for more opportunities. + ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + // What was the inner loop should now be transformable, as the jump to the + // outer loop's merge has been redirected. + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + std::string after_another_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %2 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %6 %11 %9 + %11 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %6 %12 %12 + %13 = OpLabel + OpBranchConditional %6 %11 %12 + %12 = OpLabel + OpBranch %9 + %10 = OpLabel + OpBranchConditional %6 %9 %8 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_another_op_0, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, LongAccessChains) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 5 + %8 = OpTypeArray %5 %7 + %9 = OpTypeStruct %8 + %10 = OpTypeStruct %9 %9 + %11 = OpConstant %6 2 + %12 = OpTypeArray %10 %11 + %13 = OpTypeStruct %12 + %14 = OpTypePointer Function %13 + %15 = OpConstant %5 0 + %16 = OpConstant %5 1 + %17 = OpConstant %5 2 + %18 = OpConstant %5 3 + %19 = OpConstant %5 4 + %20 = OpConstantComposite %8 %15 %16 %17 %18 %19 + %21 = OpConstantComposite %9 %20 + %22 = OpConstant %5 5 + %23 = OpConstant %5 6 + %24 = OpConstant %5 7 + %25 = OpConstant %5 8 + %26 = OpConstant %5 9 + %27 = OpConstantComposite %8 %22 %23 %24 %25 %26 + %28 = OpConstantComposite %9 %27 + %29 = OpConstantComposite %10 %21 %28 + %30 = OpConstant %5 10 + %31 = OpConstant %5 11 + %32 = OpConstant %5 12 + %33 = OpConstant %5 13 + %34 = OpConstant %5 14 + %35 = OpConstantComposite %8 %30 %31 %32 %33 %34 + %36 = OpConstantComposite %9 %35 + %37 = OpConstant %5 15 + %38 = OpConstant %5 16 + %39 = OpConstant %5 17 + %40 = OpConstant %5 18 + %41 = OpConstant %5 19 + %42 = OpConstantComposite %8 %37 %38 %39 %40 %41 + %43 = OpConstantComposite %9 %42 + %44 = OpConstantComposite %10 %36 %43 + %45 = OpConstantComposite %12 %29 %44 + %46 = OpConstantComposite %13 %45 + %47 = OpTypePointer Function %12 + %48 = OpTypePointer Function %10 + %49 = OpTypePointer Function %9 + %50 = OpTypePointer Function %8 + %51 = OpTypePointer Function %5 + %52 = OpTypeBool + %53 = OpConstantTrue %52 + %2 = OpFunction %3 None %4 + %54 = OpLabel + %55 = OpVariable %14 Function + OpStore %55 %46 + OpBranch %56 + %56 = OpLabel + OpLoopMerge %57 %58 None + OpBranchConditional %53 %57 %59 + %59 = OpLabel + OpSelectionMerge %60 None + OpBranchConditional %53 %61 %57 + %61 = OpLabel + %62 = OpAccessChain %47 %55 %15 + OpBranch %63 + %63 = OpLabel + OpSelectionMerge %64 None + OpBranchConditional %53 %65 %57 + %65 = OpLabel + %66 = OpAccessChain %48 %62 %16 + OpBranch %67 + %67 = OpLabel + OpSelectionMerge %68 None + OpBranchConditional %53 %69 %57 + %69 = OpLabel + %70 = OpAccessChain %49 %66 %16 + OpBranch %71 + %71 = OpLabel + OpSelectionMerge %72 None + OpBranchConditional %53 %73 %57 + %73 = OpLabel + %74 = OpAccessChain %50 %70 %15 + OpBranch %75 + %75 = OpLabel + OpSelectionMerge %76 None + OpBranchConditional %53 %77 %57 + %77 = OpLabel + %78 = OpAccessChain %51 %74 %17 + OpBranch %79 + %79 = OpLabel + OpSelectionMerge %80 None + OpBranchConditional %53 %81 %57 + %81 = OpLabel + %82 = OpLoad %5 %78 + OpBranch %80 + %80 = OpLabel + OpBranch %76 + %76 = OpLabel + OpBranch %72 + %72 = OpLabel + OpBranch %68 + %68 = OpLabel + OpBranch %64 + %64 = OpLabel + OpBranch %60 + %60 = OpLabel + OpBranch %58 + %58 = OpLabel + OpBranch %56 + %57 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + + ASSERT_EQ(1, ops.size()); + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + + CheckValid(env, context.get()); + + // TODO(2183): When we have a more general solution for handling access + // chains, write an expected result for this test. + // std::string expected = R"( + // Expected text for transformed shader + //)"; + // CheckEqual(env, expected, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, LoopyShaderWithOpDecorate) { + // A shader containing a function that contains a loop and some definitions + // that are "used" in OpDecorate instructions (outside the function). These + // "uses" were causing segfaults because we try to calculate their dominance + // information, which doesn't make sense. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "_GLF_color" + OpName %14 "buf0" + OpMemberName %14 0 "a" + OpName %16 "" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpMemberDecorate %14 0 RelaxedPrecision + OpMemberDecorate %14 0 Offset 0 + OpDecorate %14 Block + OpDecorate %16 DescriptorSet 0 + OpDecorate %16 Binding 0 + OpDecorate %21 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 %10 + %14 = OpTypeStruct %6 + %15 = OpTypePointer Uniform %14 + %16 = OpVariable %15 Uniform + %17 = OpTypeInt 32 1 + %18 = OpConstant %17 0 + %19 = OpTypePointer Uniform %6 + %28 = OpConstant %6 2 + %29 = OpTypeBool + %31 = OpTypeInt 32 0 + %32 = OpConstant %31 0 + %33 = OpTypePointer Output %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpStore %9 %11 + %20 = OpAccessChain %19 %16 %18 + %21 = OpLoad %6 %20 + OpBranch %22 + %22 = OpLabel + %40 = OpPhi %6 %21 %5 %39 %23 + %30 = OpFOrdLessThan %29 %40 %28 + OpLoopMerge %24 %23 None + OpBranchConditional %30 %23 %24 + %23 = OpLabel + %34 = OpAccessChain %33 %9 %32 + %35 = OpLoad %6 %34 + %36 = OpFAdd %6 %35 %10 + OpStore %34 %36 + %39 = OpFAdd %6 %40 %10 + OpBranch %22 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(1, ops.size()); + + ASSERT_TRUE(ops[0]->PreconditionHolds()); + ops[0]->TryToApply(); + CheckValid(env, context.get()); + + std::string after_op_0 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "_GLF_color" + OpName %14 "buf0" + OpMemberName %14 0 "a" + OpName %16 "" + OpDecorate %9 RelaxedPrecision + OpDecorate %9 Location 0 + OpMemberDecorate %14 0 RelaxedPrecision + OpMemberDecorate %14 0 Offset 0 + OpDecorate %14 Block + OpDecorate %16 DescriptorSet 0 + OpDecorate %16 Binding 0 + OpDecorate %21 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 %10 + %14 = OpTypeStruct %6 + %15 = OpTypePointer Uniform %14 + %16 = OpVariable %15 Uniform + %17 = OpTypeInt 32 1 + %18 = OpConstant %17 0 + %19 = OpTypePointer Uniform %6 + %28 = OpConstant %6 2 + %29 = OpTypeBool + %31 = OpTypeInt 32 0 + %32 = OpConstant %31 0 + %33 = OpTypePointer Output %6 + %41 = OpUndef %6 ; Added + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpStore %9 %11 + %20 = OpAccessChain %19 %16 %18 + %21 = OpLoad %6 %20 + OpBranch %22 + %22 = OpLabel + %40 = OpPhi %6 %21 %5 %41 %23 ; Changed + %30 = OpFOrdLessThan %29 %40 %28 + OpSelectionMerge %24 None ; Changed + OpBranchConditional %30 %24 %24 + %23 = OpLabel + %34 = OpAccessChain %33 %9 %32 + %35 = OpLoad %6 %34 + %36 = OpFAdd %6 %35 %10 + OpStore %34 %36 + %39 = OpFAdd %6 %41 %10 ; Changed + OpBranch %22 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + CheckEqual(env, after_op_0, context.get()); +} + +TEST(StructuredLoopToSelectionReductionPassTest, + LoopWithCombinedHeaderAndContinue) { + // A shader containing a loop where the header is also the continue target. + // For now, we don't simplify such loops. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %30 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel ; loop header and continue target + OpLoopMerge %12 %10 None + OpBranchConditional %30 %10 %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto context = BuildModule(env, nullptr, shader, kReduceAssembleOption); + const auto ops = StructuredLoopToSelectionReductionOpportunityFinder() + .GetAvailableOpportunities(context.get(), 0); + ASSERT_EQ(0, ops.size()); +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/reduce/validation_during_reduction_test.cpp b/third_party/spirv-tools/test/reduce/validation_during_reduction_test.cpp new file mode 100644 index 0000000..d864344 --- /dev/null +++ b/third_party/spirv-tools/test/reduce/validation_during_reduction_test.cpp @@ -0,0 +1,589 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reducer.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/remove_instruction_reduction_opportunity.h" +#include "test/reduce/reduce_test_util.h" + +namespace spvtools { +namespace reduce { +namespace { + +using opt::Function; +using opt::IRContext; +using opt::Instruction; + +// A reduction opportunity finder that finds opportunities to remove global +// values regardless of whether they are referenced. This is very likely to make +// the resulting module invalid. We use this to test the reducer's behavior in +// the scenario where a bad reduction pass leads to an invalid module. +class BlindlyRemoveGlobalValuesReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + BlindlyRemoveGlobalValuesReductionOpportunityFinder() = default; + + ~BlindlyRemoveGlobalValuesReductionOpportunityFinder() override = default; + + // The name of this pass. + std::string GetName() const final { return "BlindlyRemoveGlobalValuesPass"; } + + // Finds opportunities to remove all global values. Assuming they are all + // referenced (directly or indirectly) from elsewhere in the module, each such + // opportunity will make the module invalid. + std::vector> GetAvailableOpportunities( + IRContext* context, uint32_t /*unused*/) const final { + std::vector> result; + for (auto& inst : context->module()->types_values()) { + if (inst.HasResultId()) { + result.push_back( + MakeUnique(&inst)); + } + } + return result; + } +}; + +// A reduction opportunity that exists at the start of every function whose +// first instruction is an OpVariable instruction. When applied, the OpVariable +// instruction is duplicated (with a fresh result id). This allows each +// reduction step to increase the number of variables to check if the validator +// limits are enforced. +class OpVariableDuplicatorReductionOpportunity : public ReductionOpportunity { + public: + OpVariableDuplicatorReductionOpportunity(Function* function) + : function_(function) {} + + bool PreconditionHolds() override { + Instruction* first_instruction = &*function_->begin()[0].begin(); + return first_instruction->opcode() == SpvOpVariable; + } + + protected: + void Apply() override { + // Duplicate the first OpVariable instruction. + + Instruction* first_instruction = &*function_->begin()[0].begin(); + assert(first_instruction->opcode() == SpvOpVariable && + "Expected first instruction to be OpVariable"); + IRContext* context = first_instruction->context(); + Instruction* cloned_instruction = first_instruction->Clone(context); + cloned_instruction->SetResultId(context->TakeNextId()); + cloned_instruction->InsertBefore(first_instruction); + } + + private: + Function* function_; +}; + +// A reduction opportunity finder that finds +// OpVariableDuplicatorReductionOpportunity. +class OpVariableDuplicatorReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + OpVariableDuplicatorReductionOpportunityFinder() = default; + + ~OpVariableDuplicatorReductionOpportunityFinder() override = default; + + std::string GetName() const final { + return "LocalVariableAdderReductionOpportunityFinder"; + } + + std::vector> GetAvailableOpportunities( + IRContext* context, uint32_t /*unused*/) const final { + std::vector> result; + for (auto& function : *context->module()) { + Instruction* first_instruction = &*function.begin()[0].begin(); + if (first_instruction->opcode() == SpvOpVariable) { + result.push_back( + MakeUnique(&function)); + } + } + return result; + } +}; + +TEST(ValidationDuringReductionTest, CheckInvalidPassMakesNoProgress) { + // A module whose global values are all referenced, so that any application of + // MakeModuleInvalidPass will make the module invalid. Check that the reducer + // makes no progress, as every step will be invalid and treated as + // uninteresting. + std::string original = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %60 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %16 "buf2" + OpMemberName %16 0 "i" + OpName %18 "" + OpName %25 "buf1" + OpMemberName %25 0 "f" + OpName %27 "" + OpName %60 "_GLF_color" + OpMemberDecorate %16 0 Offset 0 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 2 + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 1 + OpDecorate %60 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpTypeStruct %6 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypePointer Uniform %6 + %22 = OpTypeBool + %24 = OpTypeFloat 32 + %25 = OpTypeStruct %24 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %24 + %31 = OpConstant %24 2 + %56 = OpConstant %6 1 + %58 = OpTypeVector %24 4 + %59 = OpTypePointer Output %58 + %60 = OpVariable %59 Output + %72 = OpUndef %24 + %74 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %73 = OpPhi %6 %74 %5 %77 %34 + %71 = OpPhi %24 %72 %5 %76 %34 + %70 = OpPhi %6 %9 %5 %57 %34 + %20 = OpAccessChain %19 %18 %9 + %21 = OpLoad %6 %20 + %23 = OpSLessThan %22 %70 %21 + OpLoopMerge %12 %34 None + OpBranchConditional %23 %11 %12 + %11 = OpLabel + %29 = OpAccessChain %28 %27 %9 + %30 = OpLoad %24 %29 + %32 = OpFOrdGreaterThan %22 %30 %31 + OpSelectionMerge %90 None + OpBranchConditional %32 %33 %46 + %33 = OpLabel + %40 = OpFAdd %24 %71 %30 + %45 = OpISub %6 %73 %21 + OpBranch %90 + %46 = OpLabel + %50 = OpFMul %24 %71 %30 + %54 = OpSDiv %6 %73 %21 + OpBranch %90 + %90 = OpLabel + %77 = OpPhi %6 %45 %33 %54 %46 + %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel + %57 = OpIAdd %6 %70 %56 + OpBranch %10 + %12 = OpLabel + %61 = OpAccessChain %28 %27 %9 + %62 = OpLoad %24 %61 + %66 = OpConvertSToF %24 %21 + %68 = OpConvertSToF %24 %73 + %69 = OpCompositeConstruct %58 %62 %71 %66 %68 + OpStore %60 %69 + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + Reducer reducer(env); + reducer.SetMessageConsumer(NopDiagnostic); + + // Say that every module is interesting. + reducer.SetInterestingnessFunction( + [](const std::vector&, uint32_t) -> bool { return true; }); + + reducer.AddReductionPass( + MakeUnique()); + + std::vector binary_in; + SpirvTools t(env); + + ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption)); + std::vector binary_out; + spvtools::ReducerOptions reducer_options; + reducer_options.set_step_limit(500); + // Don't fail on a validation error; just treat it as uninteresting. + reducer_options.set_fail_on_validation_error(false); + spvtools::ValidatorOptions validator_options; + + Reducer::ReductionResultStatus status = reducer.Run( + std::move(binary_in), &binary_out, reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); + + // The reducer should have no impact. + CheckEqual(env, original, binary_out); +} + +TEST(ValidationDuringReductionTest, CheckNotAlwaysInvalidCanMakeProgress) { + // A module with just one unreferenced global value. All but one application + // of MakeModuleInvalidPass will make the module invalid. + std::string original = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %60 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %16 "buf2" + OpMemberName %16 0 "i" + OpName %18 "" + OpName %25 "buf1" + OpMemberName %25 0 "f" + OpName %27 "" + OpName %60 "_GLF_color" + OpMemberDecorate %16 0 Offset 0 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 2 + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 1 + OpDecorate %60 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpTypeStruct %6 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypePointer Uniform %6 + %22 = OpTypeBool + %24 = OpTypeFloat 32 + %25 = OpTypeStruct %24 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %24 + %31 = OpConstant %24 2 + %56 = OpConstant %6 1 + %1000 = OpConstant %6 1000 ; It should be possible to remove this instruction without making the module invalid. + %58 = OpTypeVector %24 4 + %59 = OpTypePointer Output %58 + %60 = OpVariable %59 Output + %72 = OpUndef %24 + %74 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %73 = OpPhi %6 %74 %5 %77 %34 + %71 = OpPhi %24 %72 %5 %76 %34 + %70 = OpPhi %6 %9 %5 %57 %34 + %20 = OpAccessChain %19 %18 %9 + %21 = OpLoad %6 %20 + %23 = OpSLessThan %22 %70 %21 + OpLoopMerge %12 %34 None + OpBranchConditional %23 %11 %12 + %11 = OpLabel + %29 = OpAccessChain %28 %27 %9 + %30 = OpLoad %24 %29 + %32 = OpFOrdGreaterThan %22 %30 %31 + OpSelectionMerge %90 None + OpBranchConditional %32 %33 %46 + %33 = OpLabel + %40 = OpFAdd %24 %71 %30 + %45 = OpISub %6 %73 %21 + OpBranch %90 + %46 = OpLabel + %50 = OpFMul %24 %71 %30 + %54 = OpSDiv %6 %73 %21 + OpBranch %90 + %90 = OpLabel + %77 = OpPhi %6 %45 %33 %54 %46 + %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel + %57 = OpIAdd %6 %70 %56 + OpBranch %10 + %12 = OpLabel + %61 = OpAccessChain %28 %27 %9 + %62 = OpLoad %24 %61 + %66 = OpConvertSToF %24 %21 + %68 = OpConvertSToF %24 %73 + %69 = OpCompositeConstruct %58 %62 %71 %66 %68 + OpStore %60 %69 + OpReturn + OpFunctionEnd + )"; + + // This is the same as the original, except that the constant declaration of + // 1000 is gone. + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %60 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %16 "buf2" + OpMemberName %16 0 "i" + OpName %18 "" + OpName %25 "buf1" + OpMemberName %25 0 "f" + OpName %27 "" + OpName %60 "_GLF_color" + OpMemberDecorate %16 0 Offset 0 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 2 + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 1 + OpDecorate %60 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpTypeStruct %6 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpTypePointer Uniform %6 + %22 = OpTypeBool + %24 = OpTypeFloat 32 + %25 = OpTypeStruct %24 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %24 + %31 = OpConstant %24 2 + %56 = OpConstant %6 1 + %58 = OpTypeVector %24 4 + %59 = OpTypePointer Output %58 + %60 = OpVariable %59 Output + %72 = OpUndef %24 + %74 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %73 = OpPhi %6 %74 %5 %77 %34 + %71 = OpPhi %24 %72 %5 %76 %34 + %70 = OpPhi %6 %9 %5 %57 %34 + %20 = OpAccessChain %19 %18 %9 + %21 = OpLoad %6 %20 + %23 = OpSLessThan %22 %70 %21 + OpLoopMerge %12 %34 None + OpBranchConditional %23 %11 %12 + %11 = OpLabel + %29 = OpAccessChain %28 %27 %9 + %30 = OpLoad %24 %29 + %32 = OpFOrdGreaterThan %22 %30 %31 + OpSelectionMerge %90 None + OpBranchConditional %32 %33 %46 + %33 = OpLabel + %40 = OpFAdd %24 %71 %30 + %45 = OpISub %6 %73 %21 + OpBranch %90 + %46 = OpLabel + %50 = OpFMul %24 %71 %30 + %54 = OpSDiv %6 %73 %21 + OpBranch %90 + %90 = OpLabel + %77 = OpPhi %6 %45 %33 %54 %46 + %76 = OpPhi %24 %40 %33 %50 %46 + OpBranch %34 + %34 = OpLabel + %57 = OpIAdd %6 %70 %56 + OpBranch %10 + %12 = OpLabel + %61 = OpAccessChain %28 %27 %9 + %62 = OpLoad %24 %61 + %66 = OpConvertSToF %24 %21 + %68 = OpConvertSToF %24 %73 + %69 = OpCompositeConstruct %58 %62 %71 %66 %68 + OpStore %60 %69 + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + Reducer reducer(env); + reducer.SetMessageConsumer(NopDiagnostic); + + // Say that every module is interesting. + reducer.SetInterestingnessFunction( + [](const std::vector&, uint32_t) -> bool { return true; }); + + reducer.AddReductionPass( + MakeUnique()); + + std::vector binary_in; + SpirvTools t(env); + + ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption)); + std::vector binary_out; + spvtools::ReducerOptions reducer_options; + reducer_options.set_step_limit(500); + // Don't fail on a validation error; just treat it as uninteresting. + reducer_options.set_fail_on_validation_error(false); + spvtools::ValidatorOptions validator_options; + + Reducer::ReductionResultStatus status = reducer.Run( + std::move(binary_in), &binary_out, reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kComplete); + + CheckEqual(env, expected, binary_out); +} + +// Sets up a Reducer for use in the CheckValidationOptions test; avoids +// repetition. +void SetupReducerForCheckValidationOptions(Reducer* reducer) { + reducer->SetMessageConsumer(NopDiagnostic); + + // Say that every module is interesting. + reducer->SetInterestingnessFunction( + [](const std::vector&, uint32_t) -> bool { return true; }); + + // Each "reduction" step will duplicate the first OpVariable instruction in + // the function. + reducer->AddReductionPass( + MakeUnique()); +} + +TEST(ValidationDuringReductionTest, CheckValidationOptions) { + // A module that only validates when the "skip-block-layout" validator option + // is used. Also, the entry point's first instruction creates a local + // variable; this instruction will be duplicated on each reduction step. + std::string original = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "Main" %3 + OpSource HLSL 600 + OpDecorate %3 BuiltIn Position + OpDecorate %4 DescriptorSet 0 + OpDecorate %4 Binding 99 + OpDecorate %5 ArrayStride 16 + OpMemberDecorate %6 0 Offset 0 + OpMemberDecorate %6 1 Offset 32 + OpMemberDecorate %6 1 MatrixStride 16 + OpMemberDecorate %6 1 ColMajor + OpMemberDecorate %6 2 Offset 96 + OpMemberDecorate %6 3 Offset 100 + OpMemberDecorate %6 4 Offset 112 + OpMemberDecorate %6 4 MatrixStride 16 + OpMemberDecorate %6 4 ColMajor + OpMemberDecorate %6 5 Offset 176 + OpDecorate %6 Block + %7 = OpTypeFloat 32 + %8 = OpTypeVector %7 4 + %9 = OpTypeMatrix %8 4 + %10 = OpTypeVector %7 2 + %11 = OpTypeInt 32 1 + %12 = OpTypeInt 32 0 + %13 = OpConstant %12 2 + %14 = OpConstant %11 1 + %15 = OpConstant %11 5 + %5 = OpTypeArray %8 %13 + %6 = OpTypeStruct %5 %9 %12 %10 %9 %7 + %16 = OpTypePointer Uniform %6 + %17 = OpTypePointer Output %8 + %18 = OpTypeVoid + %19 = OpTypeFunction %18 + %20 = OpTypePointer Uniform %7 + %4 = OpVariable %16 Uniform + %3 = OpVariable %17 Output + %21 = OpTypePointer Function %11 + %2 = OpFunction %18 None %19 + %22 = OpLabel + %23 = OpVariable %21 Function + %24 = OpAccessChain %20 %4 %15 + %25 = OpLoad %7 %24 + %26 = OpCompositeConstruct %8 %25 %25 %25 %25 + OpStore %3 %26 + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + std::vector binary_in; + SpirvTools t(env); + + ASSERT_TRUE(t.Assemble(original, &binary_in, kReduceAssembleOption)); + std::vector binary_out; + spvtools::ReducerOptions reducer_options; + spvtools::ValidatorOptions validator_options; + + reducer_options.set_step_limit(3); + reducer_options.set_fail_on_validation_error(true); + + // Reduction should fail because the initial state is invalid without the + // "skip-block-layout" validator option. Note that the interestingness test + // always returns true. + { + Reducer reducer(env); + SetupReducerForCheckValidationOptions(&reducer); + + Reducer::ReductionResultStatus status = + reducer.Run(std::vector(binary_in), &binary_out, + reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kInitialStateInvalid); + } + + // Try again with validator option. + validator_options.SetSkipBlockLayout(true); + + // Reduction should hit step limit; module is seen as valid, interestingness + // test always succeeds, and the finder yields infinite opportunities. + { + Reducer reducer(env); + SetupReducerForCheckValidationOptions(&reducer); + + Reducer::ReductionResultStatus status = + reducer.Run(std::vector(binary_in), &binary_out, + reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kReachedStepLimit); + } + + // Now set a limit on the number of local variables. + validator_options.SetUniversalLimit(spv_validator_limit_max_local_variables, + 2); + + // Reduction should now fail due to reaching an invalid state; after one step, + // a local variable is added and the module becomes "invalid" given the + // validator limits. + { + Reducer reducer(env); + SetupReducerForCheckValidationOptions(&reducer); + + Reducer::ReductionResultStatus status = + reducer.Run(std::vector(binary_in), &binary_out, + reducer_options, validator_options); + + ASSERT_EQ(status, Reducer::ReductionResultStatus::kStateInvalid); + } +} + +} // namespace +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/test/scripts/test_compact_ids.py b/third_party/spirv-tools/test/scripts/test_compact_ids.py new file mode 100644 index 0000000..6ca6e67 --- /dev/null +++ b/third_party/spirv-tools/test/scripts/test_compact_ids.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# Copyright (c) 2017 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests correctness of opt pass tools/opt --compact-ids.""" + +import os.path +import sys +import tempfile + +def test_spirv_file(path, temp_dir): + optimized_spv_path = os.path.join(temp_dir, 'optimized.spv') + optimized_dis_path = os.path.join(temp_dir, 'optimized.dis') + converted_spv_path = os.path.join(temp_dir, 'converted.spv') + converted_dis_path = os.path.join(temp_dir, 'converted.dis') + + os.system('tools/spirv-opt ' + path + ' -o ' + optimized_spv_path + + ' --compact-ids') + os.system('tools/spirv-dis ' + optimized_spv_path + ' -o ' + + optimized_dis_path) + + os.system('tools/spirv-dis ' + path + ' -o ' + converted_dis_path) + os.system('tools/spirv-as ' + converted_dis_path + ' -o ' + + converted_spv_path) + os.system('tools/spirv-dis ' + converted_spv_path + ' -o ' + + converted_dis_path) + + with open(converted_dis_path, 'r') as f: + converted_dis = f.readlines()[3:] + + with open(optimized_dis_path, 'r') as f: + optimized_dis = f.readlines()[3:] + + return converted_dis == optimized_dis + +def print_usage(): + template= \ +"""{script} tests correctness of opt pass tools/opt --compact-ids + +USAGE: python {script} [] + +Requires tools/spirv-dis, tools/spirv-as and tools/spirv-opt to be in path +(call the script from the SPIRV-Tools build output directory). + +TIP: In order to test all .spv files under current dir use +find -name "*.spv" -print0 | xargs -0 -s 2000000 python {script} +""" + print(template.format(script=sys.argv[0])); + +def main(): + if not os.path.isfile('tools/spirv-dis'): + print('error: tools/spirv-dis not found') + print_usage() + exit(1) + + if not os.path.isfile('tools/spirv-as'): + print('error: tools/spirv-as not found') + print_usage() + exit(1) + + if not os.path.isfile('tools/spirv-opt'): + print('error: tools/spirv-opt not found') + print_usage() + exit(1) + + paths = sys.argv[1:] + if not paths: + print_usage() + + num_failed = 0 + + temp_dir = tempfile.mkdtemp() + + for path in paths: + success = test_spirv_file(path, temp_dir) + if not success: + print('Test failed for ' + path) + num_failed += 1 + + print('Tested ' + str(len(paths)) + ' files') + + if num_failed: + print(str(num_failed) + ' tests failed') + exit(1) + else: + print('All tests successful') + exit(0) + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/test/software_version_test.cpp b/third_party/spirv-tools/test/software_version_test.cpp new file mode 100644 index 0000000..80b944a --- /dev/null +++ b/third_party/spirv-tools/test/software_version_test.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2015-2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::testing::AnyOf; +using ::testing::Eq; +using ::testing::Ge; +using ::testing::StartsWith; + +void CheckFormOfHighLevelVersion(const std::string& version) { + std::istringstream s(version); + char v = 'x'; + int year = -1; + char period = 'x'; + int index = -1; + s >> v >> year >> period >> index; + EXPECT_THAT(v, Eq('v')); + EXPECT_THAT(year, Ge(2016)); + EXPECT_THAT(period, Eq('.')); + EXPECT_THAT(index, Ge(0)); + EXPECT_TRUE(s.good() || s.eof()); + + std::string rest; + s >> rest; + EXPECT_THAT(rest, AnyOf("", "-dev")); +} + +TEST(SoftwareVersion, ShortIsCorrectForm) { + SCOPED_TRACE("short form"); + CheckFormOfHighLevelVersion(spvSoftwareVersionString()); +} + +TEST(SoftwareVersion, DetailedIsCorrectForm) { + const std::string detailed_version(spvSoftwareVersionDetailsString()); + EXPECT_THAT(detailed_version, StartsWith("SPIRV-Tools v")); + + // Parse the high level version. + const std::string from_v = + detailed_version.substr(detailed_version.find_first_of('v')); + const size_t first_space_after_v_or_npos = from_v.find_first_of(' '); + SCOPED_TRACE(detailed_version); + CheckFormOfHighLevelVersion(from_v.substr(0, first_space_after_v_or_npos)); + + // We don't actually care about what comes after the version number. +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/string_utils_test.cpp b/third_party/spirv-tools/test/string_utils_test.cpp new file mode 100644 index 0000000..5851415 --- /dev/null +++ b/third_party/spirv-tools/test/string_utils_test.cpp @@ -0,0 +1,191 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gtest/gtest.h" +#include "source/util/string_utils.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace utils { +namespace { + +TEST(ToString, Int) { + EXPECT_EQ("0", ToString(0)); + EXPECT_EQ("1000", ToString(1000)); + EXPECT_EQ("-1", ToString(-1)); + EXPECT_EQ("0", ToString(0LL)); + EXPECT_EQ("1000", ToString(1000LL)); + EXPECT_EQ("-1", ToString(-1LL)); +} + +TEST(ToString, Uint) { + EXPECT_EQ("0", ToString(0U)); + EXPECT_EQ("1000", ToString(1000U)); + EXPECT_EQ("0", ToString(0ULL)); + EXPECT_EQ("1000", ToString(1000ULL)); +} + +TEST(ToString, Float) { + EXPECT_EQ("0", ToString(0.f)); + EXPECT_EQ("1000", ToString(1000.f)); + EXPECT_EQ("-1.5", ToString(-1.5f)); +} + +TEST(ToString, Double) { + EXPECT_EQ("0", ToString(0.)); + EXPECT_EQ("1000", ToString(1000.)); + EXPECT_EQ("-1.5", ToString(-1.5)); +} + +TEST(CardinalToOrdinal, Test) { + EXPECT_EQ("1st", CardinalToOrdinal(1)); + EXPECT_EQ("2nd", CardinalToOrdinal(2)); + EXPECT_EQ("3rd", CardinalToOrdinal(3)); + EXPECT_EQ("4th", CardinalToOrdinal(4)); + EXPECT_EQ("5th", CardinalToOrdinal(5)); + EXPECT_EQ("6th", CardinalToOrdinal(6)); + EXPECT_EQ("7th", CardinalToOrdinal(7)); + EXPECT_EQ("8th", CardinalToOrdinal(8)); + EXPECT_EQ("9th", CardinalToOrdinal(9)); + EXPECT_EQ("10th", CardinalToOrdinal(10)); + EXPECT_EQ("11th", CardinalToOrdinal(11)); + EXPECT_EQ("12th", CardinalToOrdinal(12)); + EXPECT_EQ("13th", CardinalToOrdinal(13)); + EXPECT_EQ("14th", CardinalToOrdinal(14)); + EXPECT_EQ("15th", CardinalToOrdinal(15)); + EXPECT_EQ("16th", CardinalToOrdinal(16)); + EXPECT_EQ("17th", CardinalToOrdinal(17)); + EXPECT_EQ("18th", CardinalToOrdinal(18)); + EXPECT_EQ("19th", CardinalToOrdinal(19)); + EXPECT_EQ("20th", CardinalToOrdinal(20)); + EXPECT_EQ("21st", CardinalToOrdinal(21)); + EXPECT_EQ("22nd", CardinalToOrdinal(22)); + EXPECT_EQ("23rd", CardinalToOrdinal(23)); + EXPECT_EQ("24th", CardinalToOrdinal(24)); + EXPECT_EQ("25th", CardinalToOrdinal(25)); + EXPECT_EQ("26th", CardinalToOrdinal(26)); + EXPECT_EQ("27th", CardinalToOrdinal(27)); + EXPECT_EQ("28th", CardinalToOrdinal(28)); + EXPECT_EQ("29th", CardinalToOrdinal(29)); + EXPECT_EQ("30th", CardinalToOrdinal(30)); + EXPECT_EQ("31st", CardinalToOrdinal(31)); + EXPECT_EQ("32nd", CardinalToOrdinal(32)); + EXPECT_EQ("33rd", CardinalToOrdinal(33)); + EXPECT_EQ("34th", CardinalToOrdinal(34)); + EXPECT_EQ("35th", CardinalToOrdinal(35)); + EXPECT_EQ("100th", CardinalToOrdinal(100)); + EXPECT_EQ("101st", CardinalToOrdinal(101)); + EXPECT_EQ("102nd", CardinalToOrdinal(102)); + EXPECT_EQ("103rd", CardinalToOrdinal(103)); + EXPECT_EQ("104th", CardinalToOrdinal(104)); + EXPECT_EQ("105th", CardinalToOrdinal(105)); + EXPECT_EQ("106th", CardinalToOrdinal(106)); + EXPECT_EQ("107th", CardinalToOrdinal(107)); + EXPECT_EQ("108th", CardinalToOrdinal(108)); + EXPECT_EQ("109th", CardinalToOrdinal(109)); + EXPECT_EQ("110th", CardinalToOrdinal(110)); + EXPECT_EQ("111th", CardinalToOrdinal(111)); + EXPECT_EQ("112th", CardinalToOrdinal(112)); + EXPECT_EQ("113th", CardinalToOrdinal(113)); + EXPECT_EQ("114th", CardinalToOrdinal(114)); + EXPECT_EQ("115th", CardinalToOrdinal(115)); + EXPECT_EQ("116th", CardinalToOrdinal(116)); + EXPECT_EQ("117th", CardinalToOrdinal(117)); + EXPECT_EQ("118th", CardinalToOrdinal(118)); + EXPECT_EQ("119th", CardinalToOrdinal(119)); + EXPECT_EQ("120th", CardinalToOrdinal(120)); + EXPECT_EQ("121st", CardinalToOrdinal(121)); + EXPECT_EQ("122nd", CardinalToOrdinal(122)); + EXPECT_EQ("123rd", CardinalToOrdinal(123)); + EXPECT_EQ("124th", CardinalToOrdinal(124)); + EXPECT_EQ("125th", CardinalToOrdinal(125)); + EXPECT_EQ("126th", CardinalToOrdinal(126)); + EXPECT_EQ("127th", CardinalToOrdinal(127)); + EXPECT_EQ("128th", CardinalToOrdinal(128)); + EXPECT_EQ("129th", CardinalToOrdinal(129)); + EXPECT_EQ("130th", CardinalToOrdinal(130)); + EXPECT_EQ("131st", CardinalToOrdinal(131)); + EXPECT_EQ("132nd", CardinalToOrdinal(132)); + EXPECT_EQ("133rd", CardinalToOrdinal(133)); + EXPECT_EQ("134th", CardinalToOrdinal(134)); + EXPECT_EQ("135th", CardinalToOrdinal(135)); + EXPECT_EQ("1000th", CardinalToOrdinal(1000)); + EXPECT_EQ("1001st", CardinalToOrdinal(1001)); + EXPECT_EQ("1002nd", CardinalToOrdinal(1002)); + EXPECT_EQ("1003rd", CardinalToOrdinal(1003)); + EXPECT_EQ("1004th", CardinalToOrdinal(1004)); + EXPECT_EQ("1005th", CardinalToOrdinal(1005)); + EXPECT_EQ("1006th", CardinalToOrdinal(1006)); + EXPECT_EQ("1007th", CardinalToOrdinal(1007)); + EXPECT_EQ("1008th", CardinalToOrdinal(1008)); + EXPECT_EQ("1009th", CardinalToOrdinal(1009)); + EXPECT_EQ("1010th", CardinalToOrdinal(1010)); + EXPECT_EQ("1011th", CardinalToOrdinal(1011)); + EXPECT_EQ("1012th", CardinalToOrdinal(1012)); + EXPECT_EQ("1013th", CardinalToOrdinal(1013)); + EXPECT_EQ("1014th", CardinalToOrdinal(1014)); + EXPECT_EQ("1015th", CardinalToOrdinal(1015)); + EXPECT_EQ("1016th", CardinalToOrdinal(1016)); + EXPECT_EQ("1017th", CardinalToOrdinal(1017)); + EXPECT_EQ("1018th", CardinalToOrdinal(1018)); + EXPECT_EQ("1019th", CardinalToOrdinal(1019)); + EXPECT_EQ("1020th", CardinalToOrdinal(1020)); + EXPECT_EQ("1021st", CardinalToOrdinal(1021)); + EXPECT_EQ("1022nd", CardinalToOrdinal(1022)); + EXPECT_EQ("1023rd", CardinalToOrdinal(1023)); + EXPECT_EQ("1024th", CardinalToOrdinal(1024)); + EXPECT_EQ("1025th", CardinalToOrdinal(1025)); + EXPECT_EQ("1026th", CardinalToOrdinal(1026)); + EXPECT_EQ("1027th", CardinalToOrdinal(1027)); + EXPECT_EQ("1028th", CardinalToOrdinal(1028)); + EXPECT_EQ("1029th", CardinalToOrdinal(1029)); + EXPECT_EQ("1030th", CardinalToOrdinal(1030)); + EXPECT_EQ("1031st", CardinalToOrdinal(1031)); + EXPECT_EQ("1032nd", CardinalToOrdinal(1032)); + EXPECT_EQ("1033rd", CardinalToOrdinal(1033)); + EXPECT_EQ("1034th", CardinalToOrdinal(1034)); + EXPECT_EQ("1035th", CardinalToOrdinal(1035)); + EXPECT_EQ("1200th", CardinalToOrdinal(1200)); + EXPECT_EQ("1201st", CardinalToOrdinal(1201)); + EXPECT_EQ("1202nd", CardinalToOrdinal(1202)); + EXPECT_EQ("1203rd", CardinalToOrdinal(1203)); + EXPECT_EQ("1204th", CardinalToOrdinal(1204)); + EXPECT_EQ("1205th", CardinalToOrdinal(1205)); + EXPECT_EQ("1206th", CardinalToOrdinal(1206)); + EXPECT_EQ("1207th", CardinalToOrdinal(1207)); + EXPECT_EQ("1208th", CardinalToOrdinal(1208)); + EXPECT_EQ("1209th", CardinalToOrdinal(1209)); + EXPECT_EQ("1210th", CardinalToOrdinal(1210)); + EXPECT_EQ("1211th", CardinalToOrdinal(1211)); + EXPECT_EQ("1212th", CardinalToOrdinal(1212)); + EXPECT_EQ("1213th", CardinalToOrdinal(1213)); + EXPECT_EQ("1214th", CardinalToOrdinal(1214)); + EXPECT_EQ("1215th", CardinalToOrdinal(1215)); + EXPECT_EQ("1216th", CardinalToOrdinal(1216)); + EXPECT_EQ("1217th", CardinalToOrdinal(1217)); + EXPECT_EQ("1218th", CardinalToOrdinal(1218)); + EXPECT_EQ("1219th", CardinalToOrdinal(1219)); + EXPECT_EQ("1220th", CardinalToOrdinal(1220)); + EXPECT_EQ("1221st", CardinalToOrdinal(1221)); + EXPECT_EQ("1222nd", CardinalToOrdinal(1222)); + EXPECT_EQ("1223rd", CardinalToOrdinal(1223)); + EXPECT_EQ("1224th", CardinalToOrdinal(1224)); + EXPECT_EQ("1225th", CardinalToOrdinal(1225)); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/target_env_test.cpp b/third_party/spirv-tools/test/target_env_test.cpp new file mode 100644 index 0000000..9c86e2d --- /dev/null +++ b/third_party/spirv-tools/test/target_env_test.cpp @@ -0,0 +1,165 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/spirv_target_env.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::testing::AnyOf; +using ::testing::Eq; +using ::testing::StartsWith; +using ::testing::ValuesIn; + +using TargetEnvTest = ::testing::TestWithParam; +TEST_P(TargetEnvTest, CreateContext) { + spv_target_env env = GetParam(); + spv_context context = spvContextCreate(env); + ASSERT_NE(nullptr, context); + spvContextDestroy(context); // Avoid leaking +} + +TEST_P(TargetEnvTest, ValidDescription) { + const char* description = spvTargetEnvDescription(GetParam()); + ASSERT_NE(nullptr, description); + ASSERT_THAT(description, StartsWith("SPIR-V ")); +} + +TEST_P(TargetEnvTest, ValidSpirvVersion) { + auto spirv_version = spvVersionForTargetEnv(GetParam()); + ASSERT_THAT(spirv_version, AnyOf(0x10000, 0x10100, 0x10200, 0x10300)); +} + +INSTANTIATE_TEST_SUITE_P(AllTargetEnvs, TargetEnvTest, + ValuesIn(spvtest::AllTargetEnvironments())); + +TEST(GetContextTest, InvalidTargetEnvProducesNull) { + // Use a value beyond the last valid enum value. + spv_context context = spvContextCreate(static_cast(30)); + EXPECT_EQ(context, nullptr); +} + +// A test case for parsing an environment string. +struct ParseCase { + const char* input; + bool success; // Expect to successfully parse? + spv_target_env env; // The parsed environment, if successful. +}; + +using TargetParseTest = ::testing::TestWithParam; + +TEST_P(TargetParseTest, Samples) { + spv_target_env env; + bool parsed = spvParseTargetEnv(GetParam().input, &env); + EXPECT_THAT(parsed, Eq(GetParam().success)); + if (parsed) { + EXPECT_THAT(env, Eq(GetParam().env)); + } +} + +INSTANTIATE_TEST_SUITE_P( + TargetParsing, TargetParseTest, + ValuesIn(std::vector{ + {"spv1.0", true, SPV_ENV_UNIVERSAL_1_0}, + {"spv1.1", true, SPV_ENV_UNIVERSAL_1_1}, + {"spv1.2", true, SPV_ENV_UNIVERSAL_1_2}, + {"spv1.3", true, SPV_ENV_UNIVERSAL_1_3}, + {"vulkan1.0", true, SPV_ENV_VULKAN_1_0}, + {"vulkan1.1", true, SPV_ENV_VULKAN_1_1}, + {"vulkan1.2", true, SPV_ENV_VULKAN_1_2}, + {"opencl2.1", true, SPV_ENV_OPENCL_2_1}, + {"opencl2.2", true, SPV_ENV_OPENCL_2_2}, + {"opengl4.0", true, SPV_ENV_OPENGL_4_0}, + {"opengl4.1", true, SPV_ENV_OPENGL_4_1}, + {"opengl4.2", true, SPV_ENV_OPENGL_4_2}, + {"opengl4.3", true, SPV_ENV_OPENGL_4_3}, + {"opengl4.5", true, SPV_ENV_OPENGL_4_5}, + {"opencl1.2", true, SPV_ENV_OPENCL_1_2}, + {"opencl1.2embedded", true, SPV_ENV_OPENCL_EMBEDDED_1_2}, + {"opencl2.0", true, SPV_ENV_OPENCL_2_0}, + {"opencl2.0embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_0}, + {"opencl2.1embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_1}, + {"opencl2.2embedded", true, SPV_ENV_OPENCL_EMBEDDED_2_2}, + {"webgpu0", true, SPV_ENV_WEBGPU_0}, + {"opencl2.3", false, SPV_ENV_UNIVERSAL_1_0}, + {"opencl3.0", false, SPV_ENV_UNIVERSAL_1_0}, + {"vulkan1.9", false, SPV_ENV_UNIVERSAL_1_0}, + {"vulkan2.0", false, SPV_ENV_UNIVERSAL_1_0}, + {nullptr, false, SPV_ENV_UNIVERSAL_1_0}, + {"", false, SPV_ENV_UNIVERSAL_1_0}, + {"abc", false, SPV_ENV_UNIVERSAL_1_0}, + })); + +// A test case for parsing an environment string. +struct ParseVulkanCase { + uint32_t vulkan; + uint32_t spirv; + bool success; // Expect to successfully parse? + spv_target_env env; // The parsed environment, if successful. +}; + +using TargetParseVulkanTest = ::testing::TestWithParam; + +TEST_P(TargetParseVulkanTest, Samples) { + spv_target_env env; + bool parsed = spvParseVulkanEnv(GetParam().vulkan, GetParam().spirv, &env); + EXPECT_THAT(parsed, Eq(GetParam().success)); + if (parsed) { + EXPECT_THAT(env, Eq(GetParam().env)); + } +} + +#define VK(MAJ, MIN) ((MAJ << 22) | (MIN << 12)) +#define SPV(MAJ, MIN) ((MAJ << 16) | (MIN << 8)) +INSTANTIATE_TEST_SUITE_P( + TargetVulkanParsing, TargetParseVulkanTest, + ValuesIn(std::vector{ + // Vulkan 1.0 cases + {VK(1, 0), SPV(1, 0), true, SPV_ENV_VULKAN_1_0}, + {VK(1, 0), SPV(1, 1), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 0), SPV(1, 2), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 0), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 0), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, + {VK(1, 0), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 0), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 1.1 cases + {VK(1, 1), SPV(1, 0), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 1), SPV(1, 1), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 1), SPV(1, 2), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 1), SPV(1, 3), true, SPV_ENV_VULKAN_1_1}, + {VK(1, 1), SPV(1, 4), true, SPV_ENV_VULKAN_1_1_SPIRV_1_4}, + {VK(1, 1), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 1), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 1.2 cases + {VK(1, 2), SPV(1, 0), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 1), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 2), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 3), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 4), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 5), true, SPV_ENV_VULKAN_1_2}, + {VK(1, 2), SPV(1, 6), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 1.3 cases + {VK(1, 3), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 2.0 cases + {VK(2, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + // Vulkan 99.0 cases + {VK(99, 0), SPV(1, 0), false, SPV_ENV_UNIVERSAL_1_0}, + })); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/test_fixture.h b/third_party/spirv-tools/test/test_fixture.h new file mode 100644 index 0000000..436993e --- /dev/null +++ b/third_party/spirv-tools/test/test_fixture.h @@ -0,0 +1,198 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_TEST_FIXTURE_H_ +#define TEST_TEST_FIXTURE_H_ + +#include +#include + +#include "test/unit_spirv.h" + +namespace spvtest { + +// RAII for spv_context. +struct ScopedContext { + ScopedContext(spv_target_env env = SPV_ENV_UNIVERSAL_1_0) + : context(spvContextCreate(env)) {} + ~ScopedContext() { spvContextDestroy(context); } + spv_context context; +}; + +// Common setup for TextToBinary tests. SetText() should be called to populate +// the actual test text. +template +class TextToBinaryTestBase : public T { + public: + // Shorthand for SPIR-V compilation result. + using SpirvVector = std::vector; + + // Offset into a SpirvVector at which the first instruction starts. + static const SpirvVector::size_type kFirstInstruction = 5; + + TextToBinaryTestBase() : diagnostic(nullptr), text(), binary(nullptr) { + char textStr[] = "substitute the text member variable with your test"; + text = {textStr, strlen(textStr)}; + } + + virtual ~TextToBinaryTestBase() { + DestroyBinary(); + if (diagnostic) spvDiagnosticDestroy(diagnostic); + } + + // Returns subvector v[from:end). + SpirvVector Subvector(const SpirvVector& v, SpirvVector::size_type from) { + assert(from <= v.size()); + return SpirvVector(v.begin() + from, v.end()); + } + + // Compiles SPIR-V text in the given assembly syntax format, asserting + // compilation success. Returns the compiled code. + SpirvVector CompileSuccessfully(const std::string& txt, + spv_target_env env = SPV_ENV_UNIVERSAL_1_0) { + DestroyBinary(); + DestroyDiagnostic(); + spv_result_t status = + spvTextToBinary(ScopedContext(env).context, txt.c_str(), txt.size(), + &binary, &diagnostic); + EXPECT_EQ(SPV_SUCCESS, status) << txt; + SpirvVector code_copy; + if (status == SPV_SUCCESS) { + code_copy = SpirvVector(binary->code, binary->code + binary->wordCount); + DestroyBinary(); + } else { + spvDiagnosticPrint(diagnostic); + } + return code_copy; + } + + // Compiles SPIR-V text with the given format, asserting compilation failure. + // Returns the error message(s). + std::string CompileFailure(const std::string& txt, + spv_target_env env = SPV_ENV_UNIVERSAL_1_0) { + DestroyBinary(); + DestroyDiagnostic(); + EXPECT_NE(SPV_SUCCESS, + spvTextToBinary(ScopedContext(env).context, txt.c_str(), + txt.size(), &binary, &diagnostic)) + << txt; + DestroyBinary(); + return diagnostic->error; + } + + // Encodes SPIR-V text into binary and then decodes the binary using + // given options. Returns the decoded text. + std::string EncodeAndDecodeSuccessfully( + const std::string& txt, + uint32_t disassemble_options = SPV_BINARY_TO_TEXT_OPTION_NONE, + spv_target_env env = SPV_ENV_UNIVERSAL_1_0) { + DestroyBinary(); + DestroyDiagnostic(); + ScopedContext context(env); + disassemble_options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER; + spv_result_t error = spvTextToBinary(context.context, txt.c_str(), + txt.size(), &binary, &diagnostic); + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + } + EXPECT_EQ(SPV_SUCCESS, error); + if (!binary) return ""; + + spv_text decoded_text; + error = spvBinaryToText(context.context, binary->code, binary->wordCount, + disassemble_options, &decoded_text, &diagnostic); + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + } + EXPECT_EQ(SPV_SUCCESS, error) << txt; + + const std::string decoded_string = decoded_text->str; + spvTextDestroy(decoded_text); + + return decoded_string; + } + + // Encodes SPIR-V text into binary. This is expected to succeed. + // The given words are then appended to the binary, and the result + // is then decoded. This is expected to fail. + // Returns the error message. + std::string EncodeSuccessfullyDecodeFailed( + const std::string& txt, const SpirvVector& words_to_append) { + DestroyBinary(); + DestroyDiagnostic(); + SpirvVector code = + spvtest::Concatenate({CompileSuccessfully(txt), words_to_append}); + + spv_text decoded_text; + EXPECT_NE(SPV_SUCCESS, + spvBinaryToText(ScopedContext().context, code.data(), code.size(), + SPV_BINARY_TO_TEXT_OPTION_NONE, &decoded_text, + &diagnostic)); + if (diagnostic) { + std::string error_message = diagnostic->error; + spvDiagnosticDestroy(diagnostic); + diagnostic = nullptr; + return error_message; + } + return ""; + } + + // Compiles SPIR-V text, asserts success, and returns the words representing + // the instructions. In particular, skip the words in the SPIR-V header. + SpirvVector CompiledInstructions(const std::string& txt, + spv_target_env env = SPV_ENV_UNIVERSAL_1_0) { + const SpirvVector code = CompileSuccessfully(txt, env); + SpirvVector result; + // Extract just the instructions. + // If the code fails to compile, then return the empty vector. + // In any case, don't crash or invoke undefined behaviour. + if (code.size() >= kFirstInstruction) + result = Subvector(code, kFirstInstruction); + return result; + } + + void SetText(const std::string& code) { + textString = code; + text.str = textString.c_str(); + text.length = textString.size(); + } + + // Destroys the binary, if it exists. + void DestroyBinary() { + spvBinaryDestroy(binary); + binary = nullptr; + } + + // Destroys the diagnostic, if it exists. + void DestroyDiagnostic() { + spvDiagnosticDestroy(diagnostic); + diagnostic = nullptr; + } + + spv_diagnostic diagnostic; + + std::string textString; + spv_text_t text; + spv_binary binary; +}; + +using TextToBinaryTest = TextToBinaryTestBase<::testing::Test>; +} // namespace spvtest + +using RoundTripTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +#endif // TEST_TEST_FIXTURE_H_ diff --git a/third_party/spirv-tools/test/text_advance_test.cpp b/third_party/spirv-tools/test/text_advance_test.cpp new file mode 100644 index 0000000..9de77a8 --- /dev/null +++ b/third_party/spirv-tools/test/text_advance_test.cpp @@ -0,0 +1,134 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::AutoText; + +TEST(TextAdvance, LeadingNewLines) { + AutoText input("\n\nWord"); + AssemblyContext data(input, nullptr); + ASSERT_EQ(SPV_SUCCESS, data.advance()); + ASSERT_EQ(0u, data.position().column); + ASSERT_EQ(2u, data.position().line); + ASSERT_EQ(2u, data.position().index); +} + +TEST(TextAdvance, LeadingSpaces) { + AutoText input(" Word"); + AssemblyContext data(input, nullptr); + ASSERT_EQ(SPV_SUCCESS, data.advance()); + ASSERT_EQ(4u, data.position().column); + ASSERT_EQ(0u, data.position().line); + ASSERT_EQ(4u, data.position().index); +} + +TEST(TextAdvance, LeadingTabs) { + AutoText input("\t\t\tWord"); + AssemblyContext data(input, nullptr); + ASSERT_EQ(SPV_SUCCESS, data.advance()); + ASSERT_EQ(3u, data.position().column); + ASSERT_EQ(0u, data.position().line); + ASSERT_EQ(3u, data.position().index); +} + +TEST(TextAdvance, LeadingNewLinesSpacesAndTabs) { + AutoText input("\n\n\t Word"); + AssemblyContext data(input, nullptr); + ASSERT_EQ(SPV_SUCCESS, data.advance()); + ASSERT_EQ(3u, data.position().column); + ASSERT_EQ(2u, data.position().line); + ASSERT_EQ(5u, data.position().index); +} + +TEST(TextAdvance, LeadingWhitespaceAfterCommentLine) { + AutoText input("; comment\n \t \tWord"); + AssemblyContext data(input, nullptr); + ASSERT_EQ(SPV_SUCCESS, data.advance()); + ASSERT_EQ(4u, data.position().column); + ASSERT_EQ(1u, data.position().line); + ASSERT_EQ(14u, data.position().index); +} + +TEST(TextAdvance, EOFAfterCommentLine) { + AutoText input("; comment"); + AssemblyContext data(input, nullptr); + ASSERT_EQ(SPV_END_OF_STREAM, data.advance()); +} + +TEST(TextAdvance, NullTerminator) { + AutoText input(""); + AssemblyContext data(input, nullptr); + ASSERT_EQ(SPV_END_OF_STREAM, data.advance()); +} + +TEST(TextAdvance, NoNullTerminatorAfterCommentLine) { + std::string input = "; comment|padding beyond the end"; + spv_text_t text = {input.data(), 9}; + AssemblyContext data(&text, nullptr); + ASSERT_EQ(SPV_END_OF_STREAM, data.advance()); + EXPECT_EQ(9u, data.position().index); +} + +TEST(TextAdvance, NoNullTerminator) { + spv_text_t text = {"OpNop\nSomething else in memory", 6}; + AssemblyContext data(&text, nullptr); + const spv_position_t line_break = {1u, 5u, 5u}; + data.setPosition(line_break); + ASSERT_EQ(SPV_END_OF_STREAM, data.advance()); +} + +// Invokes AssemblyContext::advance() on text, asserts success, and returns +// AssemblyContext::position(). +spv_position_t PositionAfterAdvance(const char* text) { + AutoText input(text); + AssemblyContext data(input, nullptr); + EXPECT_EQ(SPV_SUCCESS, data.advance()); + return data.position(); +} + +TEST(TextAdvance, SkipOverCR) { + const auto pos = PositionAfterAdvance("\rWord"); + EXPECT_EQ(1u, pos.column); + EXPECT_EQ(0u, pos.line); + EXPECT_EQ(1u, pos.index); +} + +TEST(TextAdvance, SkipOverCRs) { + const auto pos = PositionAfterAdvance("\r\r\rWord"); + EXPECT_EQ(3u, pos.column); + EXPECT_EQ(0u, pos.line); + EXPECT_EQ(3u, pos.index); +} + +TEST(TextAdvance, SkipOverCRLF) { + const auto pos = PositionAfterAdvance("\r\nWord"); + EXPECT_EQ(0u, pos.column); + EXPECT_EQ(1u, pos.line); + EXPECT_EQ(2u, pos.index); +} + +TEST(TextAdvance, SkipOverCRLFs) { + const auto pos = PositionAfterAdvance("\r\n\r\nWord"); + EXPECT_EQ(0u, pos.column); + EXPECT_EQ(2u, pos.line); + EXPECT_EQ(4u, pos.index); +} +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_destroy_test.cpp b/third_party/spirv-tools/test/text_destroy_test.cpp new file mode 100644 index 0000000..4c2837b --- /dev/null +++ b/third_party/spirv-tools/test/text_destroy_test.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +TEST(TextDestroy, DestroyNull) { spvBinaryDestroy(nullptr); } + +TEST(TextDestroy, Default) { + spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); + char textStr[] = R"( + OpSource OpenCL_C 12 + OpMemoryModel Physical64 OpenCL + OpSourceExtension "PlaceholderExtensionName" + OpEntryPoint Kernel %0 "" + OpExecutionMode %0 LocalSizeHint 1 1 1 + %1 = OpTypeVoid + %2 = OpTypeBool + %3 = OpTypeInt 8 0 + %4 = OpTypeInt 8 1 + %5 = OpTypeInt 16 0 + %6 = OpTypeInt 16 1 + %7 = OpTypeInt 32 0 + %8 = OpTypeInt 32 1 + %9 = OpTypeInt 64 0 + %10 = OpTypeInt 64 1 + %11 = OpTypeFloat 16 + %12 = OpTypeFloat 32 + %13 = OpTypeFloat 64 + %14 = OpTypeVector %3 2 + )"; + + spv_binary binary = nullptr; + spv_diagnostic diagnostic = nullptr; + EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, textStr, strlen(textStr), + &binary, &diagnostic)); + EXPECT_NE(nullptr, binary); + EXPECT_NE(nullptr, binary->code); + EXPECT_NE(0u, binary->wordCount); + if (diagnostic) { + spvDiagnosticPrint(diagnostic); + ASSERT_TRUE(false); + } + + spv_text resultText = nullptr; + EXPECT_EQ(SPV_SUCCESS, + spvBinaryToText(context, binary->code, binary->wordCount, 0, + &resultText, &diagnostic)); + spvBinaryDestroy(binary); + if (diagnostic) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + ASSERT_TRUE(false); + } + EXPECT_NE(nullptr, resultText->str); + EXPECT_NE(0u, resultText->length); + spvTextDestroy(resultText); + spvContextDestroy(context); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_literal_test.cpp b/third_party/spirv-tools/test/text_literal_test.cpp new file mode 100644 index 0000000..28e3500 --- /dev/null +++ b/third_party/spirv-tools/test/text_literal_test.cpp @@ -0,0 +1,412 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::testing::Eq; + +TEST(TextLiteral, GoodI32) { + spv_literal_t l; + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("-0", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_INT_32, l.type); + EXPECT_EQ(0, l.value.i32); + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("-2147483648", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_INT_32, l.type); + EXPECT_EQ((-2147483647L - 1), l.value.i32); +} + +TEST(TextLiteral, GoodU32) { + spv_literal_t l; + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("0", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_UINT_32, l.type); + EXPECT_EQ(0, l.value.i32); + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("4294967295", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_UINT_32, l.type); + EXPECT_EQ(4294967295, l.value.u32); +} + +TEST(TextLiteral, GoodI64) { + spv_literal_t l; + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("-2147483649", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_INT_64, l.type); + EXPECT_EQ(-2147483649LL, l.value.i64); +} + +TEST(TextLiteral, GoodU64) { + spv_literal_t l; + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("4294967296", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_UINT_64, l.type); + EXPECT_EQ(4294967296u, l.value.u64); +} + +TEST(TextLiteral, GoodFloat) { + spv_literal_t l; + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("1.0", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_FLOAT_32, l.type); + EXPECT_EQ(1.0, l.value.f); + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("1.5", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_FLOAT_32, l.type); + EXPECT_EQ(1.5, l.value.f); + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral("-.25", &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_FLOAT_32, l.type); + EXPECT_EQ(-.25, l.value.f); +} + +TEST(TextLiteral, BadString) { + spv_literal_t l; + + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("", &l)); + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("-", &l)); + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("--", &l)); + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("1-2", &l)); + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("123a", &l)); + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("12.2.3", &l)); + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("\"", &l)); + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("\"z", &l)); + EXPECT_EQ(SPV_FAILED_MATCH, spvTextToLiteral("a\"", &l)); +} + +class GoodStringTest + : public ::testing::TestWithParam> {}; + +TEST_P(GoodStringTest, GoodStrings) { + spv_literal_t l; + + ASSERT_EQ(SPV_SUCCESS, spvTextToLiteral(std::get<0>(GetParam()), &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type); + EXPECT_EQ(std::get<1>(GetParam()), l.str); +} + +INSTANTIATE_TEST_SUITE_P( + TextLiteral, GoodStringTest, + ::testing::ValuesIn(std::vector>{ + {R"("-")", "-"}, + {R"("--")", "--"}, + {R"("1-2")", "1-2"}, + {R"("123a")", "123a"}, + {R"("12.2.3")", "12.2.3"}, + {R"("\"")", "\""}, + {R"("\\")", "\\"}, + {"\"\\foo\nbar\"", "foo\nbar"}, + {"\"\\foo\\\nbar\"", "foo\nbar"}, + {"\"\xE4\xBA\xB2\"", "\xE4\xBA\xB2"}, + {"\"\\\xE4\xBA\xB2\"", "\xE4\xBA\xB2"}, + {"\"this \\\" and this \\\\ and \\\xE4\xBA\xB2\"", + "this \" and this \\ and \xE4\xBA\xB2"}})); + +TEST(TextLiteral, StringTooLong) { + spv_literal_t l; + std::string too_long = + std::string("\"") + + std::string(SPV_LIMIT_LITERAL_STRING_BYTES_MAX + 1, 'a') + "\""; + EXPECT_EQ(SPV_ERROR_OUT_OF_MEMORY, spvTextToLiteral(too_long.data(), &l)); +} + +TEST(TextLiteral, GoodLongString) { + spv_literal_t l; + // The universal limit of 65535 Unicode characters might make this + // fail validation, since SPV_LIMIT_LITERAL_STRING_BYTES_MAX is 4*65535. + // However, as an implementation detail, we'll allow the assembler + // to parse it. Otherwise we'd have to scan the string for valid UTF-8 + // characters. + std::string unquoted(SPV_LIMIT_LITERAL_STRING_BYTES_MAX, 'a'); + std::string good_long = std::string("\"") + unquoted + "\""; + EXPECT_EQ(SPV_SUCCESS, spvTextToLiteral(good_long.data(), &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type); + EXPECT_EQ(unquoted.data(), l.str); +} + +TEST(TextLiteral, GoodUTF8String) { + const std::string unquoted = + spvtest::MakeLongUTF8String(SPV_LIMIT_LITERAL_STRING_UTF8_CHARS_MAX); + const std::string good_long = std::string("\"") + unquoted + "\""; + spv_literal_t l; + EXPECT_EQ(SPV_SUCCESS, spvTextToLiteral(good_long.data(), &l)); + EXPECT_EQ(SPV_LITERAL_TYPE_STRING, l.type); + EXPECT_EQ(unquoted.data(), l.str); +} + +// A test case for parsing literal numbers. +struct TextLiteralCase { + uint32_t bitwidth; + const char* text; + bool is_signed; + bool success; + std::vector expected_values; +}; + +using IntegerTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +std::vector successfulEncode(const TextLiteralCase& test, + IdTypeClass type) { + spv_instruction_t inst; + std::string message; + auto capture_message = [&message](spv_message_level_t, const char*, + const spv_position_t&, + const char* m) { message = m; }; + IdType expected_type{test.bitwidth, test.is_signed, type}; + EXPECT_EQ(SPV_SUCCESS, + AssemblyContext(nullptr, capture_message) + .binaryEncodeNumericLiteral(test.text, SPV_ERROR_INVALID_TEXT, + expected_type, &inst)) + << message; + return inst.words; +} + +std::string failedEncode(const TextLiteralCase& test, IdTypeClass type) { + spv_instruction_t inst; + std::string message; + auto capture_message = [&message](spv_message_level_t, const char*, + const spv_position_t&, + const char* m) { message = m; }; + IdType expected_type{test.bitwidth, test.is_signed, type}; + EXPECT_EQ(SPV_ERROR_INVALID_TEXT, + AssemblyContext(nullptr, capture_message) + .binaryEncodeNumericLiteral(test.text, SPV_ERROR_INVALID_TEXT, + expected_type, &inst)); + return message; +} + +TEST_P(IntegerTest, IntegerBounds) { + if (GetParam().success) { + EXPECT_THAT(successfulEncode(GetParam(), IdTypeClass::kScalarIntegerType), + Eq(GetParam().expected_values)); + } else { + std::stringstream ss; + ss << "Integer " << GetParam().text << " does not fit in a " + << GetParam().bitwidth << "-bit " + << (GetParam().is_signed ? "signed" : "unsigned") << " integer"; + EXPECT_THAT(failedEncode(GetParam(), IdTypeClass::kScalarIntegerType), + Eq(ss.str())); + } +} + +// Four nicely named methods for making TextLiteralCase values. +// Their names have underscores in some places to make it easier +// to read the table that follows. +TextLiteralCase Make_Ok__Signed(uint32_t bitwidth, const char* text, + std::vector encoding) { + return TextLiteralCase{bitwidth, text, true, true, encoding}; +} +TextLiteralCase Make_Ok__Unsigned(uint32_t bitwidth, const char* text, + std::vector encoding) { + return TextLiteralCase{bitwidth, text, false, true, encoding}; +} +TextLiteralCase Make_Bad_Signed(uint32_t bitwidth, const char* text) { + return TextLiteralCase{bitwidth, text, true, false, {}}; +} +TextLiteralCase Make_Bad_Unsigned(uint32_t bitwidth, const char* text) { + return TextLiteralCase{bitwidth, text, false, false, {}}; +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + DecimalIntegers, IntegerTest, + ::testing::ValuesIn(std::vector{ + // Check max value and overflow value for 1-bit numbers. + Make_Ok__Signed(1, "0", {0}), + Make_Ok__Unsigned(1, "1", {1}), + Make_Bad_Signed(1, "1"), + Make_Bad_Unsigned(1, "2"), + + // Check max value and overflow value for 2-bit numbers. + Make_Ok__Signed(2, "1", {1}), + Make_Ok__Unsigned(2, "3", {3}), + Make_Bad_Signed(2, "2"), + Make_Bad_Unsigned(2, "4"), + + // Check max negative value and overflow value for signed + // 1- and 2-bit numbers. Signed negative numbers are sign-extended. + Make_Ok__Signed(1, "-0", {uint32_t(0)}), + Make_Ok__Signed(1, "-1", {uint32_t(-1)}), + Make_Ok__Signed(2, "-0", {0}), + Make_Ok__Signed(2, "-1", {uint32_t(-1)}), + Make_Ok__Signed(2, "-2", {uint32_t(-2)}), + Make_Bad_Signed(2, "-3"), + + Make_Bad_Unsigned(2, "2224323424242424"), + Make_Ok__Unsigned(16, "65535", {0xFFFF}), + Make_Bad_Unsigned(16, "65536"), + Make_Bad_Signed(16, "65535"), + Make_Ok__Signed(16, "32767", {0x7FFF}), + Make_Ok__Signed(16, "-32768", {0xFFFF8000}), + + // Check values around 32-bits in magnitude. + Make_Ok__Unsigned(33, "4294967296", {0, 1}), + Make_Ok__Unsigned(33, "4294967297", {1, 1}), + Make_Bad_Unsigned(33, "8589934592"), + Make_Bad_Signed(33, "4294967296"), + Make_Ok__Signed(33, "-4294967296", {0x0, 0xFFFFFFFF}), + Make_Ok__Unsigned(64, "4294967296", {0, 1}), + Make_Ok__Unsigned(64, "4294967297", {1, 1}), + + // Check max value and overflow value for 64-bit numbers. + Make_Ok__Signed(64, "9223372036854775807", {0xffffffff, 0x7fffffff}), + Make_Bad_Signed(64, "9223372036854775808"), + Make_Ok__Unsigned(64, "9223372036854775808", {0x00000000, 0x80000000}), + Make_Ok__Unsigned(64, "18446744073709551615", {0xffffffff, 0xffffffff}), + Make_Ok__Signed(64, "-9223372036854775808", {0x00000000, 0x80000000}), + + })); +// clang-format on + +using IntegerLeadingMinusTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(IntegerLeadingMinusTest, CantHaveLeadingMinusOnUnsigned) { + EXPECT_FALSE(GetParam().success); + EXPECT_THAT(failedEncode(GetParam(), IdTypeClass::kScalarIntegerType), + Eq("Cannot put a negative number in an unsigned literal")); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + DecimalAndHexIntegers, IntegerLeadingMinusTest, + ::testing::ValuesIn(std::vector{ + // Unsigned numbers never allow a leading minus sign. + Make_Bad_Unsigned(16, "-0"), + Make_Bad_Unsigned(16, "-0x0"), + Make_Bad_Unsigned(16, "-0x1"), + Make_Bad_Unsigned(32, "-0"), + Make_Bad_Unsigned(32, "-0x0"), + Make_Bad_Unsigned(32, "-0x1"), + Make_Bad_Unsigned(64, "-0"), + Make_Bad_Unsigned(64, "-0x0"), + Make_Bad_Unsigned(64, "-0x1"), + })); + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + HexIntegers, IntegerTest, + ::testing::ValuesIn(std::vector{ + // Check 0x and 0X prefices. + Make_Ok__Signed(16, "0x1234", {0x1234}), + Make_Ok__Signed(16, "0X1234", {0x1234}), + + // Check 1-bit numbers + Make_Ok__Signed(1, "0x0", {0}), + Make_Ok__Signed(1, "0x1", {uint32_t(-1)}), + Make_Ok__Unsigned(1, "0x0", {0}), + Make_Ok__Unsigned(1, "0x1", {1}), + Make_Bad_Signed(1, "0x2"), + Make_Bad_Unsigned(1, "0x2"), + + // Check 2-bit numbers + Make_Ok__Signed(2, "0x0", {0}), + Make_Ok__Signed(2, "0x1", {1}), + Make_Ok__Signed(2, "0x2", {uint32_t(-2)}), + Make_Ok__Signed(2, "0x3", {uint32_t(-1)}), + Make_Ok__Unsigned(2, "0x0", {0}), + Make_Ok__Unsigned(2, "0x1", {1}), + Make_Ok__Unsigned(2, "0x2", {2}), + Make_Ok__Unsigned(2, "0x3", {3}), + Make_Bad_Signed(2, "0x4"), + Make_Bad_Unsigned(2, "0x4"), + + // Check 8-bit numbers + Make_Ok__Signed(8, "0x7f", {0x7f}), + Make_Ok__Signed(8, "0x80", {0xffffff80}), + Make_Ok__Unsigned(8, "0x80", {0x80}), + Make_Ok__Unsigned(8, "0xff", {0xff}), + Make_Bad_Signed(8, "0x100"), + Make_Bad_Unsigned(8, "0x100"), + + // Check 16-bit numbers + Make_Ok__Signed(16, "0x7fff", {0x7fff}), + Make_Ok__Signed(16, "0x8000", {0xffff8000}), + Make_Ok__Unsigned(16, "0x8000", {0x8000}), + Make_Ok__Unsigned(16, "0xffff", {0xffff}), + Make_Bad_Signed(16, "0x10000"), + Make_Bad_Unsigned(16, "0x10000"), + + // Check 32-bit numbers + Make_Ok__Signed(32, "0x7fffffff", {0x7fffffff}), + Make_Ok__Signed(32, "0x80000000", {0x80000000}), + Make_Ok__Unsigned(32, "0x80000000", {0x80000000}), + Make_Ok__Unsigned(32, "0xffffffff", {0xffffffff}), + Make_Bad_Signed(32, "0x100000000"), + Make_Bad_Unsigned(32, "0x100000000"), + + // Check 48-bit numbers + Make_Ok__Unsigned(48, "0x7ffffffff", {0xffffffff, 7}), + Make_Ok__Unsigned(48, "0x800000000", {0, 8}), + Make_Ok__Signed(48, "0x7fffffffffff", {0xffffffff, 0x7fff}), + Make_Ok__Signed(48, "0x800000000000", {0, 0xffff8000}), + Make_Bad_Signed(48, "0x1000000000000"), + Make_Bad_Unsigned(48, "0x1000000000000"), + + // Check 64-bit numbers + Make_Ok__Signed(64, "0x7fffffffffffffff", {0xffffffff, 0x7fffffff}), + Make_Ok__Signed(64, "0x8000000000000000", {0x00000000, 0x80000000}), + Make_Ok__Unsigned(64, "0x7fffffffffffffff", {0xffffffff, 0x7fffffff}), + Make_Ok__Unsigned(64, "0x8000000000000000", {0x00000000, 0x80000000}), + })); +// clang-format on + +TEST(OverflowIntegerParse, Decimal) { + std::string signed_input = "-18446744073709551616"; + std::string expected_message0 = + "Invalid signed integer literal: " + signed_input; + EXPECT_THAT(failedEncode(Make_Bad_Signed(64, signed_input.c_str()), + IdTypeClass::kScalarIntegerType), + Eq(expected_message0)); + + std::string unsigned_input = "18446744073709551616"; + std::string expected_message1 = + "Invalid unsigned integer literal: " + unsigned_input; + EXPECT_THAT(failedEncode(Make_Bad_Unsigned(64, unsigned_input.c_str()), + IdTypeClass::kScalarIntegerType), + Eq(expected_message1)); + + // TODO(dneto): When the given number doesn't have a leading sign, + // we say we're trying to parse an unsigned number, even when the caller + // asked for a signed number. This is kind of weird, but it's an + // artefact of how we do the parsing. + EXPECT_THAT(failedEncode(Make_Bad_Signed(64, unsigned_input.c_str()), + IdTypeClass::kScalarIntegerType), + Eq(expected_message1)); +} + +TEST(OverflowIntegerParse, Hex) { + std::string input = "0x10000000000000000"; + std::string expected_message = "Invalid unsigned integer literal: " + input; + EXPECT_THAT(failedEncode(Make_Bad_Signed(64, input.c_str()), + IdTypeClass::kScalarIntegerType), + Eq(expected_message)); + EXPECT_THAT(failedEncode(Make_Bad_Unsigned(64, input.c_str()), + IdTypeClass::kScalarIntegerType), + Eq(expected_message)); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_start_new_inst_test.cpp b/third_party/spirv-tools/test/text_start_new_inst_test.cpp new file mode 100644 index 0000000..ff35ac8 --- /dev/null +++ b/third_party/spirv-tools/test/text_start_new_inst_test.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::AutoText; + +TEST(TextStartsWithOp, YesAtStart) { + EXPECT_TRUE(AssemblyContext(AutoText("OpFoo"), nullptr).isStartOfNewInst()); + EXPECT_TRUE(AssemblyContext(AutoText("OpFoo"), nullptr).isStartOfNewInst()); + EXPECT_TRUE(AssemblyContext(AutoText("OpEnCL"), nullptr).isStartOfNewInst()); +} + +TEST(TextStartsWithOp, YesAtMiddle) { + { + AutoText text(" OpFoo"); + AssemblyContext dat(text, nullptr); + dat.seekForward(2); + EXPECT_TRUE(dat.isStartOfNewInst()); + } + { + AutoText text("xx OpFoo"); + AssemblyContext dat(text, nullptr); + dat.seekForward(2); + EXPECT_TRUE(dat.isStartOfNewInst()); + } +} + +TEST(TextStartsWithOp, NoIfTooFar) { + AutoText text(" OpFoo"); + AssemblyContext dat(text, nullptr); + dat.seekForward(3); + EXPECT_FALSE(dat.isStartOfNewInst()); +} + +TEST(TextStartsWithOp, NoRegular) { + EXPECT_FALSE( + AssemblyContext(AutoText("Fee Fi Fo Fum"), nullptr).isStartOfNewInst()); + EXPECT_FALSE(AssemblyContext(AutoText("123456"), nullptr).isStartOfNewInst()); + EXPECT_FALSE(AssemblyContext(AutoText("123456"), nullptr).isStartOfNewInst()); + EXPECT_FALSE(AssemblyContext(AutoText("OpenCL"), nullptr).isStartOfNewInst()); +} + +TEST(TextStartsWithOp, YesForValueGenerationForm) { + EXPECT_TRUE( + AssemblyContext(AutoText("%foo = OpAdd"), nullptr).isStartOfNewInst()); + EXPECT_TRUE( + AssemblyContext(AutoText("%foo = OpAdd"), nullptr).isStartOfNewInst()); +} + +TEST(TextStartsWithOp, NoForNearlyValueGeneration) { + EXPECT_FALSE( + AssemblyContext(AutoText("%foo = "), nullptr).isStartOfNewInst()); + EXPECT_FALSE(AssemblyContext(AutoText("%foo "), nullptr).isStartOfNewInst()); + EXPECT_FALSE(AssemblyContext(AutoText("%foo"), nullptr).isStartOfNewInst()); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.annotation_test.cpp b/third_party/spirv-tools/test/text_to_binary.annotation_test.cpp new file mode 100644 index 0000000..61bdf64 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.annotation_test.cpp @@ -0,0 +1,549 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Annotation" section of the +// SPIR-V spec. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::EnumCase; +using spvtest::MakeInstruction; +using utils::MakeVector; +using spvtest::TextToBinaryTest; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::Values; +using ::testing::ValuesIn; + +// Test OpDecorate + +using OpDecorateSimpleTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam< + std::tuple>>>; + +TEST_P(OpDecorateSimpleTest, AnySimpleDecoration) { + // This string should assemble, but should not validate. + std::stringstream input; + input << "OpDecorate %1 " << std::get<1>(GetParam()).name(); + for (auto operand : std::get<1>(GetParam()).operands()) + input << " " << operand; + input << std::endl; + EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())), + Eq(MakeInstruction(SpvOpDecorate, + {1, uint32_t(std::get<1>(GetParam()).value())}, + std::get<1>(GetParam()).operands()))); + // Also check disassembly. + EXPECT_THAT( + EncodeAndDecodeSuccessfully(input.str(), SPV_BINARY_TO_TEXT_OPTION_NONE, + std::get<0>(GetParam())), + Eq(input.str())); +} + +// Like above, but parameters to the decoration are IDs. +using OpDecorateSimpleIdTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam< + std::tuple>>>; + +TEST_P(OpDecorateSimpleIdTest, AnySimpleDecoration) { + // This string should assemble, but should not validate. + std::stringstream input; + input << "OpDecorateId %1 " << std::get<1>(GetParam()).name(); + for (auto operand : std::get<1>(GetParam()).operands()) + input << " %" << operand; + input << std::endl; + EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())), + Eq(MakeInstruction(SpvOpDecorateId, + {1, uint32_t(std::get<1>(GetParam()).value())}, + std::get<1>(GetParam()).operands()))); + // Also check disassembly. + EXPECT_THAT( + EncodeAndDecodeSuccessfully(input.str(), SPV_BINARY_TO_TEXT_OPTION_NONE, + std::get<0>(GetParam())), + Eq(input.str())); +} + +#define CASE(NAME) SpvDecoration##NAME, #NAME +INSTANTIATE_TEST_SUITE_P( + TextToBinaryDecorateSimple, OpDecorateSimpleTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector>{ + // The operand literal values are arbitrarily chosen, + // but there are the right number of them. + {CASE(RelaxedPrecision), {}}, + {CASE(SpecId), {100}}, + {CASE(Block), {}}, + {CASE(BufferBlock), {}}, + {CASE(RowMajor), {}}, + {CASE(ColMajor), {}}, + {CASE(ArrayStride), {4}}, + {CASE(MatrixStride), {16}}, + {CASE(GLSLShared), {}}, + {CASE(GLSLPacked), {}}, + {CASE(CPacked), {}}, + // Placeholder line for enum value 12 + {CASE(NoPerspective), {}}, + {CASE(Flat), {}}, + {CASE(Patch), {}}, + {CASE(Centroid), {}}, + {CASE(Sample), {}}, + {CASE(Invariant), {}}, + {CASE(Restrict), {}}, + {CASE(Aliased), {}}, + {CASE(Volatile), {}}, + {CASE(Constant), {}}, + {CASE(Coherent), {}}, + {CASE(NonWritable), {}}, + {CASE(NonReadable), {}}, + {CASE(Uniform), {}}, + {CASE(SaturatedConversion), {}}, + {CASE(Stream), {2}}, + {CASE(Location), {6}}, + {CASE(Component), {3}}, + {CASE(Index), {14}}, + {CASE(Binding), {19}}, + {CASE(DescriptorSet), {7}}, + {CASE(Offset), {12}}, + {CASE(XfbBuffer), {1}}, + {CASE(XfbStride), {8}}, + {CASE(NoContraction), {}}, + {CASE(InputAttachmentIndex), {102}}, + {CASE(Alignment), {16}}, + }))); + +INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV11, OpDecorateSimpleTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_1), + Values(EnumCase{ + CASE(MaxByteOffset), {128}}))); + +INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleV14, OpDecorateSimpleTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_4), + ValuesIn(std::vector>{ + {CASE(Uniform), {}}, + }))); + +INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateSimpleIdV14, + OpDecorateSimpleIdTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_4), + ValuesIn(std::vector>{ + // In 1.4, UniformId decoration takes a + // scope Id. + {CASE(UniformId), {1}}, + }))); +#undef CASE + +TEST_F(OpDecorateSimpleTest, WrongDecoration) { + EXPECT_THAT(CompileFailure("OpDecorate %1 xxyyzz"), + Eq("Invalid decoration 'xxyyzz'.")); +} + +TEST_F(OpDecorateSimpleTest, ExtraOperandsOnDecorationExpectingNone) { + EXPECT_THAT(CompileFailure("OpDecorate %1 RelaxedPrecision 99"), + Eq("Expected or at the beginning of an " + "instruction, found '99'.")); +} + +TEST_F(OpDecorateSimpleTest, ExtraOperandsOnDecorationExpectingOne) { + EXPECT_THAT(CompileFailure("OpDecorate %1 SpecId 99 100"), + Eq("Expected or at the beginning of an " + "instruction, found '100'.")); +} + +TEST_F(OpDecorateSimpleTest, ExtraOperandsOnDecorationExpectingTwo) { + EXPECT_THAT( + CompileFailure("OpDecorate %1 LinkageAttributes \"abc\" Import 42"), + Eq("Expected or at the beginning of an " + "instruction, found '42'.")); +} + +// A single test case for an enum decoration. +struct DecorateEnumCase { + // Place the enum value first, so it's easier to read the binary dumps when + // the test fails. + uint32_t value; // The value within the enum, e.g. Position + std::string name; + uint32_t enum_value; // Which enum, e.g. BuiltIn + std::string enum_name; +}; + +using OpDecorateEnumTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpDecorateEnumTest, AnyEnumDecoration) { + // This string should assemble, but should not validate. + const std::string input = + "OpDecorate %1 " + GetParam().enum_name + " " + GetParam().name; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpDecorate, {1, GetParam().enum_value, + GetParam().value}))); +} + +// Test OpDecorate BuiltIn. +// clang-format off +#define CASE(NAME) \ + { SpvBuiltIn##NAME, #NAME, SpvDecorationBuiltIn, "BuiltIn" } +INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateBuiltIn, OpDecorateEnumTest, + ::testing::ValuesIn(std::vector{ + CASE(Position), + CASE(PointSize), + CASE(ClipDistance), + CASE(CullDistance), + CASE(VertexId), + CASE(InstanceId), + CASE(PrimitiveId), + CASE(InvocationId), + CASE(Layer), + CASE(ViewportIndex), + CASE(TessLevelOuter), + CASE(TessLevelInner), + CASE(TessCoord), + CASE(PatchVertices), + CASE(FragCoord), + CASE(PointCoord), + CASE(FrontFacing), + CASE(SampleId), + CASE(SamplePosition), + CASE(SampleMask), + // Value 21 intentionally missing. + CASE(FragDepth), + CASE(HelperInvocation), + CASE(NumWorkgroups), + CASE(WorkgroupSize), + CASE(WorkgroupId), + CASE(LocalInvocationId), + CASE(GlobalInvocationId), + CASE(LocalInvocationIndex), + CASE(WorkDim), + CASE(GlobalSize), + CASE(EnqueuedWorkgroupSize), + CASE(GlobalOffset), + CASE(GlobalLinearId), + // Value 35 intentionally missing. + CASE(SubgroupSize), + CASE(SubgroupMaxSize), + CASE(NumSubgroups), + CASE(NumEnqueuedSubgroups), + CASE(SubgroupId), + CASE(SubgroupLocalInvocationId), + CASE(VertexIndex), + CASE(InstanceIndex), + })); +#undef CASE +// clang-format on + +TEST_F(OpDecorateEnumTest, WrongBuiltIn) { + EXPECT_THAT(CompileFailure("OpDecorate %1 BuiltIn xxyyzz"), + Eq("Invalid built-in 'xxyyzz'.")); +} + +// Test OpDecorate FuncParamAttr +// clang-format off +#define CASE(NAME) \ + { SpvFunctionParameterAttribute##NAME, #NAME, SpvDecorationFuncParamAttr, "FuncParamAttr" } +INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFuncParamAttr, OpDecorateEnumTest, + ::testing::ValuesIn(std::vector{ + CASE(Zext), + CASE(Sext), + CASE(ByVal), + CASE(Sret), + CASE(NoAlias), + CASE(NoCapture), + CASE(NoWrite), + CASE(NoReadWrite), + })); +#undef CASE +// clang-format on + +TEST_F(OpDecorateEnumTest, WrongFuncParamAttr) { + EXPECT_THAT(CompileFailure("OpDecorate %1 FuncParamAttr xxyyzz"), + Eq("Invalid function parameter attribute 'xxyyzz'.")); +} + +// Test OpDecorate FPRoundingMode +// clang-format off +#define CASE(NAME) \ + { SpvFPRoundingMode##NAME, #NAME, SpvDecorationFPRoundingMode, "FPRoundingMode" } +INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFPRoundingMode, OpDecorateEnumTest, + ::testing::ValuesIn(std::vector{ + CASE(RTE), + CASE(RTZ), + CASE(RTP), + CASE(RTN), + })); +#undef CASE +// clang-format on + +TEST_F(OpDecorateEnumTest, WrongFPRoundingMode) { + EXPECT_THAT(CompileFailure("OpDecorate %1 FPRoundingMode xxyyzz"), + Eq("Invalid floating-point rounding mode 'xxyyzz'.")); +} + +// Test OpDecorate FPFastMathMode. +// These can by named enums for the single-bit masks. However, we don't support +// symbolic combinations of the masks. Rather, they can use ! +// syntax, e.g. !0x3 + +// clang-format off +#define CASE(ENUM,NAME) \ + { SpvFPFastMathMode##ENUM, #NAME, SpvDecorationFPFastMathMode, "FPFastMathMode" } +INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateFPFastMathMode, OpDecorateEnumTest, + ::testing::ValuesIn(std::vector{ + CASE(MaskNone, None), + CASE(NotNaNMask, NotNaN), + CASE(NotInfMask, NotInf), + CASE(NSZMask, NSZ), + CASE(AllowRecipMask, AllowRecip), + CASE(FastMask, Fast), + })); +#undef CASE +// clang-format on + +TEST_F(OpDecorateEnumTest, CombinedFPFastMathMask) { + // Sample a single combination. This ensures we've integrated + // the instruction parsing logic with spvTextParseMask. + const std::string input = "OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ"; + const uint32_t expected_enum = SpvDecorationFPFastMathMode; + const uint32_t expected_mask = SpvFPFastMathModeNotNaNMask | + SpvFPFastMathModeNotInfMask | + SpvFPFastMathModeNSZMask; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpDecorate, {1, expected_enum, expected_mask}))); +} + +TEST_F(OpDecorateEnumTest, WrongFPFastMathMode) { + EXPECT_THAT( + CompileFailure("OpDecorate %1 FPFastMathMode NotNaN|xxyyzz"), + Eq("Invalid floating-point fast math mode operand 'NotNaN|xxyyzz'.")); +} + +// Test OpDecorate Linkage + +// A single test case for a linkage +struct DecorateLinkageCase { + uint32_t linkage_type_value; + std::string linkage_type_name; + std::string external_name; +}; + +using OpDecorateLinkageTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>; + +TEST_P(OpDecorateLinkageTest, AnyLinkageDecoration) { + // This string should assemble, but should not validate. + const std::string input = "OpDecorate %1 LinkageAttributes \"" + + GetParam().external_name + "\" " + + GetParam().linkage_type_name; + std::vector expected_operands{1, SpvDecorationLinkageAttributes}; + std::vector encoded_external_name = + MakeVector(GetParam().external_name); + expected_operands.insert(expected_operands.end(), + encoded_external_name.begin(), + encoded_external_name.end()); + expected_operands.push_back(GetParam().linkage_type_value); + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpDecorate, expected_operands))); +} + +// clang-format off +#define CASE(ENUM) SpvLinkageType##ENUM, #ENUM +INSTANTIATE_TEST_SUITE_P(TextToBinaryDecorateLinkage, OpDecorateLinkageTest, + ::testing::ValuesIn(std::vector{ + { CASE(Import), "a" }, + { CASE(Export), "foo" }, + { CASE(Import), "some kind of long name with spaces etc." }, + // TODO(dneto): utf-8, escaping, quoting cases. + })); +#undef CASE +// clang-format on + +TEST_F(OpDecorateLinkageTest, WrongType) { + EXPECT_THAT(CompileFailure("OpDecorate %1 LinkageAttributes \"foo\" xxyyzz"), + Eq("Invalid linkage type 'xxyyzz'.")); +} + +// Test OpGroupMemberDecorate + +TEST_F(TextToBinaryTest, GroupMemberDecorateGoodOneTarget) { + EXPECT_THAT(CompiledInstructions("OpGroupMemberDecorate %group %id0 42"), + Eq(MakeInstruction(SpvOpGroupMemberDecorate, {1, 2, 42}))); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateGoodTwoTargets) { + EXPECT_THAT( + CompiledInstructions("OpGroupMemberDecorate %group %id0 96 %id1 42"), + Eq(MakeInstruction(SpvOpGroupMemberDecorate, {1, 2, 96, 3, 42}))); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateMissingGroupId) { + EXPECT_THAT(CompileFailure("OpGroupMemberDecorate"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidGroupId) { + EXPECT_THAT(CompileFailure("OpGroupMemberDecorate 16"), + Eq("Expected id to start with %.")); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetId) { + EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group 12"), + Eq("Expected id to start with %.")); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateMissingTargetMemberNumber) { + EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidTargetMemberNumber) { + EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 %id1"), + Eq("Invalid unsigned integer literal: %id1")); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetId) { + EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id1 42 12"), + Eq("Expected id to start with %.")); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateMissingSecondTargetMemberNumber) { + EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 42 %id1"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(TextToBinaryTest, GroupMemberDecorateInvalidSecondTargetMemberNumber) { + EXPECT_THAT(CompileFailure("OpGroupMemberDecorate %group %id0 42 %id1 %id2"), + Eq("Invalid unsigned integer literal: %id2")); +} + +// Test OpMemberDecorate + +using OpMemberDecorateSimpleTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam< + std::tuple>>>; + +TEST_P(OpMemberDecorateSimpleTest, AnySimpleDecoration) { + // This string should assemble, but should not validate. + std::stringstream input; + input << "OpMemberDecorate %1 42 " << std::get<1>(GetParam()).name(); + for (auto operand : std::get<1>(GetParam()).operands()) + input << " " << operand; + input << std::endl; + EXPECT_THAT( + CompiledInstructions(input.str(), std::get<0>(GetParam())), + Eq(MakeInstruction(SpvOpMemberDecorate, + {1, 42, uint32_t(std::get<1>(GetParam()).value())}, + std::get<1>(GetParam()).operands()))); + // Also check disassembly. + EXPECT_THAT( + EncodeAndDecodeSuccessfully(input.str(), SPV_BINARY_TO_TEXT_OPTION_NONE, + std::get<0>(GetParam())), + Eq(input.str())); +} + +#define CASE(NAME) SpvDecoration##NAME, #NAME +INSTANTIATE_TEST_SUITE_P( + TextToBinaryDecorateSimple, OpMemberDecorateSimpleTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector>{ + // The operand literal values are arbitrarily chosen, + // but there are the right number of them. + {CASE(RelaxedPrecision), {}}, + {CASE(SpecId), {100}}, + {CASE(Block), {}}, + {CASE(BufferBlock), {}}, + {CASE(RowMajor), {}}, + {CASE(ColMajor), {}}, + {CASE(ArrayStride), {4}}, + {CASE(MatrixStride), {16}}, + {CASE(GLSLShared), {}}, + {CASE(GLSLPacked), {}}, + {CASE(CPacked), {}}, + // Placeholder line for enum value 12 + {CASE(NoPerspective), {}}, + {CASE(Flat), {}}, + {CASE(Patch), {}}, + {CASE(Centroid), {}}, + {CASE(Sample), {}}, + {CASE(Invariant), {}}, + {CASE(Restrict), {}}, + {CASE(Aliased), {}}, + {CASE(Volatile), {}}, + {CASE(Constant), {}}, + {CASE(Coherent), {}}, + {CASE(NonWritable), {}}, + {CASE(NonReadable), {}}, + {CASE(Uniform), {}}, + {CASE(SaturatedConversion), {}}, + {CASE(Stream), {2}}, + {CASE(Location), {6}}, + {CASE(Component), {3}}, + {CASE(Index), {14}}, + {CASE(Binding), {19}}, + {CASE(DescriptorSet), {7}}, + {CASE(Offset), {12}}, + {CASE(XfbBuffer), {1}}, + {CASE(XfbStride), {8}}, + {CASE(NoContraction), {}}, + {CASE(InputAttachmentIndex), {102}}, + {CASE(Alignment), {16}}, + }))); + +INSTANTIATE_TEST_SUITE_P( + TextToBinaryDecorateSimpleV11, OpMemberDecorateSimpleTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_1), + Values(EnumCase{CASE(MaxByteOffset), {128}}))); +#undef CASE + +TEST_F(OpMemberDecorateSimpleTest, WrongDecoration) { + EXPECT_THAT(CompileFailure("OpMemberDecorate %1 9 xxyyzz"), + Eq("Invalid decoration 'xxyyzz'.")); +} + +TEST_F(OpMemberDecorateSimpleTest, ExtraOperandsOnDecorationExpectingNone) { + EXPECT_THAT(CompileFailure("OpMemberDecorate %1 12 RelaxedPrecision 99"), + Eq("Expected or at the beginning of an " + "instruction, found '99'.")); +} + +TEST_F(OpMemberDecorateSimpleTest, ExtraOperandsOnDecorationExpectingOne) { + EXPECT_THAT(CompileFailure("OpMemberDecorate %1 0 SpecId 99 100"), + Eq("Expected or at the beginning of an " + "instruction, found '100'.")); +} + +TEST_F(OpMemberDecorateSimpleTest, ExtraOperandsOnDecorationExpectingTwo) { + EXPECT_THAT(CompileFailure( + "OpMemberDecorate %1 1 LinkageAttributes \"abc\" Import 42"), + Eq("Expected or at the beginning of an " + "instruction, found '42'.")); +} + +// TODO(dneto): OpMemberDecorate cases for decorations with parameters which +// are: not just lists of literal numbers. + +// TODO(dneto): OpDecorationGroup +// TODO(dneto): OpGroupDecorate + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.barrier_test.cpp b/third_party/spirv-tools/test/text_to_binary.barrier_test.cpp new file mode 100644 index 0000000..545d26f --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.barrier_test.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Barrier Instructions" section +// of the SPIR-V spec. + +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::MakeInstruction; +using spvtest::TextToBinaryTest; +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::Eq; + +// Test OpMemoryBarrier + +using OpMemoryBarrier = spvtest::TextToBinaryTest; + +TEST_F(OpMemoryBarrier, Good) { + const std::string input = "OpMemoryBarrier %1 %2\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpMemoryBarrier, {1, 2}))); + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input)); +} + +TEST_F(OpMemoryBarrier, BadMissingScopeId) { + const std::string input = "OpMemoryBarrier\n"; + EXPECT_THAT(CompileFailure(input), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(OpMemoryBarrier, BadInvalidScopeId) { + const std::string input = "OpMemoryBarrier 99\n"; + EXPECT_THAT(CompileFailure(input), Eq("Expected id to start with %.")); +} + +TEST_F(OpMemoryBarrier, BadMissingMemorySemanticsId) { + const std::string input = "OpMemoryBarrier %scope\n"; + EXPECT_THAT(CompileFailure(input), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(OpMemoryBarrier, BadInvalidMemorySemanticsId) { + const std::string input = "OpMemoryBarrier %scope 14\n"; + EXPECT_THAT(CompileFailure(input), Eq("Expected id to start with %.")); +} + +// TODO(dneto): OpControlBarrier +// TODO(dneto): OpGroupAsyncCopy +// TODO(dneto): OpGroupWaitEvents +// TODO(dneto): OpGroupAll +// TODO(dneto): OpGroupAny +// TODO(dneto): OpGroupBroadcast +// TODO(dneto): OpGroupIAdd +// TODO(dneto): OpGroupFAdd +// TODO(dneto): OpGroupFMin +// TODO(dneto): OpGroupUMin +// TODO(dneto): OpGroupSMin +// TODO(dneto): OpGroupFMax +// TODO(dneto): OpGroupUMax +// TODO(dneto): OpGroupSMax + +using NamedMemoryBarrierTest = spvtest::TextToBinaryTest; + +// OpMemoryNamedBarrier is not in 1.0, but it is enabled by a capability. +// We should be able to assemble it. Validation checks are in another test +// file. +TEST_F(NamedMemoryBarrierTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics", + SPV_ENV_UNIVERSAL_1_0), + ElementsAre(spvOpcodeMake(4, SpvOpMemoryNamedBarrier), _, _, _)); +} + +TEST_F(NamedMemoryBarrierTest, ArgumentCount) { + EXPECT_THAT(CompileFailure("OpMemoryNamedBarrier", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT( + CompileFailure("OpMemoryNamedBarrier %bar", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT( + CompileFailure("OpMemoryNamedBarrier %bar %scope", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT( + CompiledInstructions("OpMemoryNamedBarrier %bar %scope %semantics", + SPV_ENV_UNIVERSAL_1_1), + ElementsAre(spvOpcodeMake(4, SpvOpMemoryNamedBarrier), _, _, _)); + EXPECT_THAT( + CompileFailure("OpMemoryNamedBarrier %bar %scope %semantics %extra", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected '=', found end of stream.")); +} + +TEST_F(NamedMemoryBarrierTest, ArgumentTypes) { + EXPECT_THAT(CompileFailure("OpMemoryNamedBarrier 123 %scope %semantics", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); + EXPECT_THAT(CompileFailure("OpMemoryNamedBarrier %bar %scope \"semantics\"", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); +} + +using TypeNamedBarrierTest = spvtest::TextToBinaryTest; + +TEST_F(TypeNamedBarrierTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%t = OpTypeNamedBarrier", SPV_ENV_UNIVERSAL_1_0), + ElementsAre(spvOpcodeMake(2, SpvOpTypeNamedBarrier), _)); +} + +TEST_F(TypeNamedBarrierTest, ArgumentCount) { + EXPECT_THAT(CompileFailure("OpTypeNamedBarrier", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected at the beginning of an instruction, " + "found 'OpTypeNamedBarrier'.")); + EXPECT_THAT( + CompiledInstructions("%t = OpTypeNamedBarrier", SPV_ENV_UNIVERSAL_1_1), + ElementsAre(spvOpcodeMake(2, SpvOpTypeNamedBarrier), _)); + EXPECT_THAT( + CompileFailure("%t = OpTypeNamedBarrier 1 2 3", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected or at the beginning of an instruction, " + "found '1'.")); +} + +using NamedBarrierInitializeTest = spvtest::TextToBinaryTest; + +TEST_F(NamedBarrierInitializeTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count", + SPV_ENV_UNIVERSAL_1_0), + ElementsAre(spvOpcodeMake(4, SpvOpNamedBarrierInitialize), _, _, _)); +} + +TEST_F(NamedBarrierInitializeTest, ArgumentCount) { + EXPECT_THAT( + CompileFailure("%bar = OpNamedBarrierInitialize", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT(CompileFailure("%bar = OpNamedBarrierInitialize %ype", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT( + CompiledInstructions("%bar = OpNamedBarrierInitialize %type %count", + SPV_ENV_UNIVERSAL_1_1), + ElementsAre(spvOpcodeMake(4, SpvOpNamedBarrierInitialize), _, _, _)); + EXPECT_THAT( + CompileFailure("%bar = OpNamedBarrierInitialize %type %count \"extra\"", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected or at the beginning of an instruction, " + "found '\"extra\"'.")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.composite_test.cpp b/third_party/spirv-tools/test/text_to_binary.composite_test.cpp new file mode 100644 index 0000000..6ae1cd3 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.composite_test.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Group Instrucions" section of the +// SPIR-V spec. + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +using ::testing::Eq; +using ::testing::HasSubstr; + +namespace spvtools { +namespace { + +using spvtest::Concatenate; + +using CompositeRoundTripTest = RoundTripTest; + +TEST_F(CompositeRoundTripTest, Good) { + std::string spirv = "%2 = OpCopyLogical %1 %3\n"; + std::string disassembly = EncodeAndDecodeSuccessfully( + spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(CompositeRoundTripTest, V13Bad) { + std::string spirv = "%2 = OpCopyLogical %1 %3\n"; + std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpCopyLogical'")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.constant_test.cpp b/third_party/spirv-tools/test/text_to_binary.constant_test.cpp new file mode 100644 index 0000000..7ab4ca5 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.constant_test.cpp @@ -0,0 +1,841 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Group Instrucions" section of the +// SPIR-V spec. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::EnumCase; +using spvtest::MakeInstruction; +using ::testing::Eq; + +// Test Sampler Addressing Mode enum values + +using SamplerAddressingModeTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(SamplerAddressingModeTest, AnySamplerAddressingMode) { + const std::string input = + "%result = OpConstantSampler %type " + GetParam().name() + " 0 Nearest"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpConstantSampler, + {1, 2, GetParam().value(), 0, 0}))); +} + +// clang-format off +#define CASE(NAME) { SpvSamplerAddressingMode##NAME, #NAME } +INSTANTIATE_TEST_SUITE_P( + TextToBinarySamplerAddressingMode, SamplerAddressingModeTest, + ::testing::ValuesIn(std::vector>{ + CASE(None), + CASE(ClampToEdge), + CASE(Clamp), + CASE(Repeat), + CASE(RepeatMirrored), + })); +#undef CASE +// clang-format on + +TEST_F(SamplerAddressingModeTest, WrongMode) { + EXPECT_THAT(CompileFailure("%r = OpConstantSampler %t xxyyzz 0 Nearest"), + Eq("Invalid sampler addressing mode 'xxyyzz'.")); +} + +// Test Sampler Filter Mode enum values + +using SamplerFilterModeTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(SamplerFilterModeTest, AnySamplerFilterMode) { + const std::string input = + "%result = OpConstantSampler %type Clamp 0 " + GetParam().name(); + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpConstantSampler, + {1, 2, 2, 0, GetParam().value()}))); +} + +// clang-format off +#define CASE(NAME) { SpvSamplerFilterMode##NAME, #NAME} +INSTANTIATE_TEST_SUITE_P( + TextToBinarySamplerFilterMode, SamplerFilterModeTest, + ::testing::ValuesIn(std::vector>{ + CASE(Nearest), + CASE(Linear), + })); +#undef CASE +// clang-format on + +TEST_F(SamplerFilterModeTest, WrongMode) { + EXPECT_THAT(CompileFailure("%r = OpConstantSampler %t Clamp 0 xxyyzz"), + Eq("Invalid sampler filter mode 'xxyyzz'.")); +} + +struct ConstantTestCase { + std::string constant_type; + std::string constant_value; + std::vector expected_instructions; +}; + +using OpConstantValidTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpConstantValidTest, ValidTypes) { + const std::string input = "%1 = " + GetParam().constant_type + + "\n" + "%2 = OpConstant %1 " + + GetParam().constant_value + "\n"; + std::vector instructions; + EXPECT_THAT(CompiledInstructions(input), Eq(GetParam().expected_instructions)) + << " type: " << GetParam().constant_type + << " literal: " << GetParam().constant_value; +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpConstantValid, OpConstantValidTest, + ::testing::ValuesIn(std::vector{ + // Check 16 bits + {"OpTypeInt 16 0", "0x1234", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 0x1234})})}, + {"OpTypeInt 16 0", "0x8000", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 0x8000})})}, + {"OpTypeInt 16 0", "0", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 0})})}, + {"OpTypeInt 16 0", "65535", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 65535})})}, + {"OpTypeInt 16 0", "0xffff", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 65535})})}, + {"OpTypeInt 16 1", "0x8000", // Test sign extension. + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0xffff8000})})}, + {"OpTypeInt 16 1", "-32", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}), + MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-32)})})}, + {"OpTypeInt 16 1", "0", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0})})}, + {"OpTypeInt 16 1", "-0", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0})})}, + {"OpTypeInt 16 1", "-0x0", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0})})}, + {"OpTypeInt 16 1", "-32768", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}), + MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-32768)})})}, + // Check 32 bits + {"OpTypeInt 32 0", "42", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 42})})}, + {"OpTypeInt 32 1", "-32", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-32)})})}, + {"OpTypeInt 32 1", "0", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0})})}, + {"OpTypeInt 32 1", "-0", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0})})}, + {"OpTypeInt 32 1", "-0x0", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0})})}, + {"OpTypeInt 32 1", "-0x001", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-1)})})}, + {"OpTypeInt 32 1", "2147483647", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0x7fffffffu})})}, + {"OpTypeInt 32 1", "-2147483648", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0x80000000u})})}, + {"OpTypeFloat 32", "1.0", + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0x3f800000})})}, + {"OpTypeFloat 32", "10.0", + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0x41200000})})}, + {"OpTypeFloat 32", "-0x1p+128", // -infinity + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0xFF800000})})}, + {"OpTypeFloat 32", "0x1p+128", // +infinity + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0x7F800000})})}, + {"OpTypeFloat 32", "-0x1.8p+128", // A -NaN + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0xFFC00000})})}, + {"OpTypeFloat 32", "-0x1.0002p+128", // A +NaN + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0xFF800100})})}, + // Check 48 bits + {"OpTypeInt 48 0", "0x1234", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 0x1234, 0})})}, + {"OpTypeInt 48 0", "0x800000000001", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 1, 0x00008000})})}, + {"OpTypeInt 48 1", "0x800000000000", // Test sign extension. + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0, 0xffff8000})})}, + {"OpTypeInt 48 1", "-32", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 1}), + MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-32), uint32_t(-1)})})}, + // Check 64 bits + {"OpTypeInt 64 0", "0x1234", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 0x1234, 0})})}, + {"OpTypeInt 64 0", "18446744073709551615", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})}, + {"OpTypeInt 64 0", "0xffffffffffffffff", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})}, + {"OpTypeInt 64 1", "0x1234", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0x1234, 0})})}, + {"OpTypeInt 64 1", "-42", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}), + MakeInstruction(SpvOpConstant, {1, 2, uint32_t(-42), uint32_t(-1)})})}, + {"OpTypeInt 64 1", "-0x01", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0xffffffffu, 0xffffffffu})})}, + {"OpTypeInt 64 1", "9223372036854775807", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0xffffffffu, 0x7fffffffu})})}, + {"OpTypeInt 64 1", "0x7fffffff", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 0x7fffffffu, 0})})}, + })); +// clang-format on + +// A test case for checking OpConstant with invalid literals with a leading +// minus. +struct InvalidLeadingMinusCase { + std::string type; + std::string literal; +}; + +using OpConstantInvalidLeadingMinusTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>; + +TEST_P(OpConstantInvalidLeadingMinusTest, InvalidCase) { + const std::string input = "%1 = " + GetParam().type + + "\n" + "%2 = OpConstant %1 " + + GetParam().literal; + EXPECT_THAT(CompileFailure(input), + Eq("Cannot put a negative number in an unsigned literal")); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpConstantInvalidLeadingMinus, OpConstantInvalidLeadingMinusTest, + ::testing::ValuesIn(std::vector{ + {"OpTypeInt 16 0", "-0"}, + {"OpTypeInt 16 0", "-0x0"}, + {"OpTypeInt 16 0", "-1"}, + {"OpTypeInt 32 0", "-0"}, + {"OpTypeInt 32 0", "-0x0"}, + {"OpTypeInt 32 0", "-1"}, + {"OpTypeInt 64 0", "-0"}, + {"OpTypeInt 64 0", "-0x0"}, + {"OpTypeInt 64 0", "-1"}, + })); +// clang-format on + +// A test case for invalid floating point literals. +struct InvalidFloatConstantCase { + uint32_t width; + std::string literal; +}; + +using OpConstantInvalidFloatConstant = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>; + +TEST_P(OpConstantInvalidFloatConstant, Samples) { + // Check both kinds of instructions that take literal floats. + for (const auto& instruction : {"OpConstant", "OpSpecConstant"}) { + std::stringstream input; + input << "%1 = OpTypeFloat " << GetParam().width << "\n" + << "%2 = " << instruction << " %1 " << GetParam().literal; + std::stringstream expected_error; + expected_error << "Invalid " << GetParam().width + << "-bit float literal: " << GetParam().literal; + EXPECT_THAT(CompileFailure(input.str()), Eq(expected_error.str())); + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TextToBinaryInvalidFloatConstant, OpConstantInvalidFloatConstant, + ::testing::ValuesIn(std::vector{ + {16, "abc"}, + {16, "--1"}, + {16, "-+1"}, + {16, "+-1"}, + {16, "++1"}, + {16, "1e30"}, // Overflow is an error for 16-bit floats. + {16, "-1e30"}, + {16, "1e40"}, + {16, "-1e40"}, + {16, "1e400"}, + {16, "-1e400"}, + {32, "abc"}, + {32, "--1"}, + {32, "-+1"}, + {32, "+-1"}, + {32, "++1"}, + {32, "1e40"}, // Overflow is an error for 32-bit floats. + {32, "-1e40"}, + {32, "1e400"}, + {32, "-1e400"}, + {64, "abc"}, + {64, "--1"}, + {64, "-+1"}, + {64, "+-1"}, + {64, "++1"}, + {32, "1e400"}, // Overflow is an error for 64-bit floats. + {32, "-1e400"}, + })); +// clang-format on + +using OpConstantInvalidTypeTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; +TEST_P(OpConstantInvalidTypeTest, InvalidTypes) { + const std::string input = "%1 = " + GetParam() + + "\n" + "%2 = OpConstant %1 0\n"; + EXPECT_THAT( + CompileFailure(input), + Eq("Type for Constant must be a scalar floating point or integer type")); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpConstantInvalidValidType, OpConstantInvalidTypeTest, + ::testing::ValuesIn(std::vector{ + {"OpTypeVoid", + "OpTypeBool", + "OpTypeVector %a 32", + "OpTypeMatrix %a 32", + "OpTypeImage %a 1D 0 0 0 0 Unknown", + "OpTypeSampler", + "OpTypeSampledImage %a", + "OpTypeArray %a %b", + "OpTypeRuntimeArray %a", + "OpTypeStruct %a", + "OpTypeOpaque \"Foo\"", + "OpTypePointer UniformConstant %a", + "OpTypeFunction %a %b", + "OpTypeEvent", + "OpTypeDeviceEvent", + "OpTypeReserveId", + "OpTypeQueue", + "OpTypePipe ReadOnly", + + // Skip OpTypeForwardPointer doesn't even produce a result ID. + // The assembler errors out if we try to check it in this scenario. + + // Try at least one thing that isn't a type at all + "OpNot %a %b" + }, + })); +// clang-format on + +using OpSpecConstantValidTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpSpecConstantValidTest, ValidTypes) { + const std::string input = "%1 = " + GetParam().constant_type + + "\n" + "%2 = OpSpecConstant %1 " + + GetParam().constant_value + "\n"; + std::vector instructions; + EXPECT_THAT(CompiledInstructions(input), + Eq(GetParam().expected_instructions)); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpSpecConstantValid, OpSpecConstantValidTest, + ::testing::ValuesIn(std::vector{ + // Check 16 bits + {"OpTypeInt 16 0", "0x1234", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0x1234})})}, + {"OpTypeInt 16 0", "0x8000", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 0}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0x8000})})}, + {"OpTypeInt 16 1", "0x8000", // Test sign extension. + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0xffff8000})})}, + {"OpTypeInt 16 1", "-32", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 16, 1}), + MakeInstruction(SpvOpSpecConstant, {1, 2, uint32_t(-32)})})}, + // Check 32 bits + {"OpTypeInt 32 0", "42", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 0}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 42})})}, + {"OpTypeInt 32 1", "-32", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpSpecConstant, {1, 2, uint32_t(-32)})})}, + {"OpTypeFloat 32", "1.0", + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0x3f800000})})}, + {"OpTypeFloat 32", "10.0", + Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0x41200000})})}, + // Check 48 bits + {"OpTypeInt 48 0", "0x1234", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0x1234, 0})})}, + {"OpTypeInt 48 0", "0x800000000001", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 0}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 1, 0x00008000})})}, + {"OpTypeInt 48 1", "0x800000000000", // Test sign extension. + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 1}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0, 0xffff8000})})}, + {"OpTypeInt 48 1", "-32", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 48, 1}), + MakeInstruction(SpvOpSpecConstant, {1, 2, uint32_t(-32), uint32_t(-1)})})}, + // Check 64 bits + {"OpTypeInt 64 0", "0x1234", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 0}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0x1234, 0})})}, + {"OpTypeInt 64 1", "0x1234", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}), + MakeInstruction(SpvOpSpecConstant, {1, 2, 0x1234, 0})})}, + {"OpTypeInt 64 1", "-42", + Concatenate({MakeInstruction(SpvOpTypeInt, {1, 64, 1}), + MakeInstruction(SpvOpSpecConstant, {1, 2, uint32_t(-42), uint32_t(-1)})})}, + })); +// clang-format on + +using OpSpecConstantInvalidTypeTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpSpecConstantInvalidTypeTest, InvalidTypes) { + const std::string input = "%1 = " + GetParam() + + "\n" + "%2 = OpSpecConstant %1 0\n"; + EXPECT_THAT(CompileFailure(input), + Eq("Type for SpecConstant must be a scalar floating point or " + "integer type")); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpSpecConstantInvalidValidType, OpSpecConstantInvalidTypeTest, + ::testing::ValuesIn(std::vector{ + {"OpTypeVoid", + "OpTypeBool", + "OpTypeVector %a 32", + "OpTypeMatrix %a 32", + "OpTypeImage %a 1D 0 0 0 0 Unknown", + "OpTypeSampler", + "OpTypeSampledImage %a", + "OpTypeArray %a %b", + "OpTypeRuntimeArray %a", + "OpTypeStruct %a", + "OpTypeOpaque \"Foo\"", + "OpTypePointer UniformConstant %a", + "OpTypeFunction %a %b", + "OpTypeEvent", + "OpTypeDeviceEvent", + "OpTypeReserveId", + "OpTypeQueue", + "OpTypePipe ReadOnly", + + // Skip testing OpTypeForwardPointer because it doesn't even produce a result ID. + + // Try at least one thing that isn't a type at all + "OpNot %a %b" + }, + })); +// clang-format on + +const int64_t kMaxUnsigned48Bit = (int64_t(1) << 48) - 1; +const int64_t kMaxSigned48Bit = (int64_t(1) << 47) - 1; +const int64_t kMinSigned48Bit = -kMaxSigned48Bit - 1; + +using ConstantRoundTripTest = RoundTripTest; + +TEST_P(ConstantRoundTripTest, DisassemblyEqualsAssemblyInput) { + const std::string assembly = GetParam(); + EXPECT_THAT(EncodeAndDecodeSuccessfully(assembly), Eq(assembly)) << assembly; +} + +INSTANTIATE_TEST_SUITE_P( + OpConstantRoundTrip, ConstantRoundTripTest, + ::testing::ValuesIn(std::vector{ + // 16 bit + "%1 = OpTypeInt 16 0\n%2 = OpConstant %1 0\n", + "%1 = OpTypeInt 16 0\n%2 = OpConstant %1 65535\n", + "%1 = OpTypeInt 16 1\n%2 = OpConstant %1 -32768\n", + "%1 = OpTypeInt 16 1\n%2 = OpConstant %1 32767\n", + "%1 = OpTypeInt 32 0\n%2 = OpConstant %1 0\n", + // 32 bit + std::string("%1 = OpTypeInt 32 0\n%2 = OpConstant %1 0\n"), + std::string("%1 = OpTypeInt 32 0\n%2 = OpConstant %1 ") + + std::to_string(std::numeric_limits::max()) + "\n", + std::string("%1 = OpTypeInt 32 1\n%2 = OpConstant %1 ") + + std::to_string(std::numeric_limits::max()) + "\n", + std::string("%1 = OpTypeInt 32 1\n%2 = OpConstant %1 ") + + std::to_string(std::numeric_limits::min()) + "\n", + // 48 bit + std::string("%1 = OpTypeInt 48 0\n%2 = OpConstant %1 0\n"), + std::string("%1 = OpTypeInt 48 0\n%2 = OpConstant %1 ") + + std::to_string(kMaxUnsigned48Bit) + "\n", + std::string("%1 = OpTypeInt 48 1\n%2 = OpConstant %1 ") + + std::to_string(kMaxSigned48Bit) + "\n", + std::string("%1 = OpTypeInt 48 1\n%2 = OpConstant %1 ") + + std::to_string(kMinSigned48Bit) + "\n", + // 64 bit + std::string("%1 = OpTypeInt 64 0\n%2 = OpConstant %1 0\n"), + std::string("%1 = OpTypeInt 64 0\n%2 = OpConstant %1 ") + + std::to_string(std::numeric_limits::max()) + "\n", + std::string("%1 = OpTypeInt 64 1\n%2 = OpConstant %1 ") + + std::to_string(std::numeric_limits::max()) + "\n", + std::string("%1 = OpTypeInt 64 1\n%2 = OpConstant %1 ") + + std::to_string(std::numeric_limits::min()) + "\n", + // 32-bit float + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0\n", + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 13.5\n", + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -12.5\n", + // 64-bit float + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0\n", + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 1.79767e+308\n", + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -1.79767e+308\n", + })); + +INSTANTIATE_TEST_SUITE_P( + OpConstantHalfRoundTrip, ConstantRoundTripTest, + ::testing::ValuesIn(std::vector{ + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x0p+0\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x0p+0\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1p+0\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.1p+0\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.01p-1\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.8p+1\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ffcp+1\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1p+0\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.1p+0\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.01p-1\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.8p+1\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.ffcp+1\n", + + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1p-16\n", // some denorms + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1p-24\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1p-24\n", + + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1p+16\n", // +inf + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1p+16\n", // -inf + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.01p+16\n", // -inf + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.01p+16\n", // nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.11p+16\n", // nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ffp+16\n", // nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ffcp+16\n", // nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.004p+16\n", // nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.01p+16\n", // -nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.11p+16\n", // -nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.ffp+16\n", // -nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.ffcp+16\n", // -nan + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.004p+16\n", // -nan + })); + +// clang-format off +// (Clang-format really wants to break up these strings across lines. +INSTANTIATE_TEST_SUITE_P( + OpConstantRoundTripNonFinite, ConstantRoundTripTest, + ::testing::ValuesIn(std::vector{ + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128\n", // -inf + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128\n", // inf + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.8p+128\n", // -nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128\n", // -nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0018p+128\n", // -nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.01ep+128\n", // -nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.fffffep+128\n", // -nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128\n", // +nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.0002p+128\n", // +nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.0018p+128\n", // +nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.01ep+128\n", // +nan + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.fffffep+128\n", // +nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024\n", // -inf + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024\n", // +inf + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.8p+1024\n", // -nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0fp+1024\n", // -nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0000000000001p+1024\n", // -nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.00003p+1024\n", // -nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.fffffffffffffp+1024\n", // -nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024\n", // +nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.0fp+1024\n", // +nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.0000000000001p+1024\n", // -nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.00003p+1024\n", // -nan + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.fffffffffffffp+1024\n", // -nan + })); +// clang-format on + +INSTANTIATE_TEST_SUITE_P( + OpSpecConstantRoundTrip, ConstantRoundTripTest, + ::testing::ValuesIn(std::vector{ + // 16 bit + "%1 = OpTypeInt 16 0\n%2 = OpSpecConstant %1 0\n", + "%1 = OpTypeInt 16 0\n%2 = OpSpecConstant %1 65535\n", + "%1 = OpTypeInt 16 1\n%2 = OpSpecConstant %1 -32768\n", + "%1 = OpTypeInt 16 1\n%2 = OpSpecConstant %1 32767\n", + "%1 = OpTypeInt 32 0\n%2 = OpSpecConstant %1 0\n", + // 32 bit + std::string("%1 = OpTypeInt 32 0\n%2 = OpSpecConstant %1 0\n"), + std::string("%1 = OpTypeInt 32 0\n%2 = OpSpecConstant %1 ") + + std::to_string(std::numeric_limits::max()) + "\n", + std::string("%1 = OpTypeInt 32 1\n%2 = OpSpecConstant %1 ") + + std::to_string(std::numeric_limits::max()) + "\n", + std::string("%1 = OpTypeInt 32 1\n%2 = OpSpecConstant %1 ") + + std::to_string(std::numeric_limits::min()) + "\n", + // 48 bit + std::string("%1 = OpTypeInt 48 0\n%2 = OpSpecConstant %1 0\n"), + std::string("%1 = OpTypeInt 48 0\n%2 = OpSpecConstant %1 ") + + std::to_string(kMaxUnsigned48Bit) + "\n", + std::string("%1 = OpTypeInt 48 1\n%2 = OpSpecConstant %1 ") + + std::to_string(kMaxSigned48Bit) + "\n", + std::string("%1 = OpTypeInt 48 1\n%2 = OpSpecConstant %1 ") + + std::to_string(kMinSigned48Bit) + "\n", + // 64 bit + std::string("%1 = OpTypeInt 64 0\n%2 = OpSpecConstant %1 0\n"), + std::string("%1 = OpTypeInt 64 0\n%2 = OpSpecConstant %1 ") + + std::to_string(std::numeric_limits::max()) + "\n", + std::string("%1 = OpTypeInt 64 1\n%2 = OpSpecConstant %1 ") + + std::to_string(std::numeric_limits::max()) + "\n", + std::string("%1 = OpTypeInt 64 1\n%2 = OpSpecConstant %1 ") + + std::to_string(std::numeric_limits::min()) + "\n", + // 32-bit float + "%1 = OpTypeFloat 32\n%2 = OpSpecConstant %1 0\n", + "%1 = OpTypeFloat 32\n%2 = OpSpecConstant %1 13.5\n", + "%1 = OpTypeFloat 32\n%2 = OpSpecConstant %1 -12.5\n", + // 64-bit float + "%1 = OpTypeFloat 64\n%2 = OpSpecConstant %1 0\n", + "%1 = OpTypeFloat 64\n%2 = OpSpecConstant %1 1.79767e+308\n", + "%1 = OpTypeFloat 64\n%2 = OpSpecConstant %1 -1.79767e+308\n", + })); + +// Test OpSpecConstantOp + +using OpSpecConstantOpTestWithIds = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>>; + +// The operands to the OpSpecConstantOp opcode are all Ids. +TEST_P(OpSpecConstantOpTestWithIds, Assembly) { + std::stringstream input; + input << "%2 = OpSpecConstantOp %1 " << GetParam().name(); + for (auto id : GetParam().operands()) input << " %" << id; + input << "\n"; + + EXPECT_THAT(CompiledInstructions(input.str()), + Eq(MakeInstruction(SpvOpSpecConstantOp, + {1, 2, uint32_t(GetParam().value())}, + GetParam().operands()))); + + // Check the disassembler as well. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input.str()), input.str()); +} + +// clang-format off +#define CASE1(NAME) { SpvOp##NAME, #NAME, {3} } +#define CASE2(NAME) { SpvOp##NAME, #NAME, {3, 4} } +#define CASE3(NAME) { SpvOp##NAME, #NAME, {3, 4, 5} } +#define CASE4(NAME) { SpvOp##NAME, #NAME, {3, 4, 5, 6} } +#define CASE5(NAME) { SpvOp##NAME, #NAME, {3, 4, 5, 6, 7} } +#define CASE6(NAME) { SpvOp##NAME, #NAME, {3, 4, 5, 6, 7, 8} } +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpSpecConstantOp, OpSpecConstantOpTestWithIds, + ::testing::ValuesIn(std::vector>{ + // Conversion + CASE1(SConvert), + CASE1(FConvert), + CASE1(ConvertFToS), + CASE1(ConvertSToF), + CASE1(ConvertFToU), + CASE1(ConvertUToF), + CASE1(UConvert), + CASE1(ConvertPtrToU), + CASE1(ConvertUToPtr), + CASE1(GenericCastToPtr), + CASE1(PtrCastToGeneric), + CASE1(Bitcast), + CASE1(QuantizeToF16), + // Arithmetic + CASE1(SNegate), + CASE1(Not), + CASE2(IAdd), + CASE2(ISub), + CASE2(IMul), + CASE2(UDiv), + CASE2(SDiv), + CASE2(UMod), + CASE2(SRem), + CASE2(SMod), + CASE2(ShiftRightLogical), + CASE2(ShiftRightArithmetic), + CASE2(ShiftLeftLogical), + CASE2(BitwiseOr), + CASE2(BitwiseAnd), + CASE2(BitwiseXor), + CASE1(FNegate), + CASE2(FAdd), + CASE2(FSub), + CASE2(FMul), + CASE2(FDiv), + CASE2(FRem), + CASE2(FMod), + // Composite operations use literal numbers. So they're in another test. + // Logical + CASE2(LogicalOr), + CASE2(LogicalAnd), + CASE1(LogicalNot), + CASE2(LogicalEqual), + CASE2(LogicalNotEqual), + CASE3(Select), + // Comparison + CASE2(IEqual), + CASE2(INotEqual), // Allowed in 1.0 Rev 7 + CASE2(ULessThan), + CASE2(SLessThan), + CASE2(UGreaterThan), + CASE2(SGreaterThan), + CASE2(ULessThanEqual), + CASE2(SLessThanEqual), + CASE2(UGreaterThanEqual), + CASE2(SGreaterThanEqual), + // Memory + // For AccessChain, there is a base Id, then a sequence of index Ids. + // Having no index Ids is a corner case. + CASE1(AccessChain), + CASE2(AccessChain), + CASE6(AccessChain), + CASE1(InBoundsAccessChain), + CASE2(InBoundsAccessChain), + CASE6(InBoundsAccessChain), + // PtrAccessChain also has an element Id. + CASE2(PtrAccessChain), + CASE3(PtrAccessChain), + CASE6(PtrAccessChain), + CASE2(InBoundsPtrAccessChain), + CASE3(InBoundsPtrAccessChain), + CASE6(InBoundsPtrAccessChain), + })); +#undef CASE1 +#undef CASE2 +#undef CASE3 +#undef CASE4 +#undef CASE5 +#undef CASE6 +// clang-format on + +using OpSpecConstantOpTestWithTwoIdsThenLiteralNumbers = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>>; + +// The operands to the OpSpecConstantOp opcode are two Ids followed by a +// sequence of literal numbers. +TEST_P(OpSpecConstantOpTestWithTwoIdsThenLiteralNumbers, Assembly) { + std::stringstream input; + input << "%2 = OpSpecConstantOp %1 " << GetParam().name() << " %3 %4"; + for (auto number : GetParam().operands()) input << " " << number; + input << "\n"; + + EXPECT_THAT(CompiledInstructions(input.str()), + Eq(MakeInstruction(SpvOpSpecConstantOp, + {1, 2, uint32_t(GetParam().value()), 3, 4}, + GetParam().operands()))); + + // Check the disassembler as well. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input.str()), input.str()); +} + +#define CASE(NAME) SpvOp##NAME, #NAME +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpSpecConstantOp, + OpSpecConstantOpTestWithTwoIdsThenLiteralNumbers, + ::testing::ValuesIn(std::vector>{ + // For VectorShuffle, there are two vector operands, and at least + // two selector Ids. OpenCL can have up to 16-element vectors. + {CASE(VectorShuffle), {0, 0}}, + {CASE(VectorShuffle), {4, 3, 2, 1}}, + {CASE(VectorShuffle), {0, 2, 4, 6, 1, 3, 5, 7}}, + {CASE(VectorShuffle), + {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}, + // For CompositeInsert, there is an object to insert, the target + // composite, and then literal indices. + {CASE(CompositeInsert), {0}}, + {CASE(CompositeInsert), {4, 3, 99, 1}}, + })); + +using OpSpecConstantOpTestWithOneIdThenLiteralNumbers = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>>; + +// The operands to the OpSpecConstantOp opcode are one Id followed by a +// sequence of literal numbers. +TEST_P(OpSpecConstantOpTestWithOneIdThenLiteralNumbers, Assembly) { + std::stringstream input; + input << "%2 = OpSpecConstantOp %1 " << GetParam().name() << " %3"; + for (auto number : GetParam().operands()) input << " " << number; + input << "\n"; + + EXPECT_THAT(CompiledInstructions(input.str()), + Eq(MakeInstruction(SpvOpSpecConstantOp, + {1, 2, uint32_t(GetParam().value()), 3}, + GetParam().operands()))); + + // Check the disassembler as well. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input.str()), input.str()); +} + +#define CASE(NAME) SpvOp##NAME, #NAME +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpSpecConstantOp, + OpSpecConstantOpTestWithOneIdThenLiteralNumbers, + ::testing::ValuesIn(std::vector>{ + // For CompositeExtract, the universal limit permits up to 255 literal + // indices. Let's only test a few. + {CASE(CompositeExtract), {0}}, + {CASE(CompositeExtract), {0, 99, 42, 16, 17, 12, 19}}, + })); + +// TODO(dneto): OpConstantTrue +// TODO(dneto): OpConstantFalse +// TODO(dneto): OpConstantComposite +// TODO(dneto): OpConstantSampler: other variations Param is 0 or 1 +// TODO(dneto): OpConstantNull +// TODO(dneto): OpSpecConstantTrue +// TODO(dneto): OpSpecConstantFalse +// TODO(dneto): OpSpecConstantComposite +// TODO(dneto): Negative tests for OpSpecConstantOp + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.control_flow_test.cpp b/third_party/spirv-tools/test/text_to_binary.control_flow_test.cpp new file mode 100644 index 0000000..3e117b8 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.control_flow_test.cpp @@ -0,0 +1,427 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Control Flow" section of the +// SPIR-V spec. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::EnumCase; +using spvtest::MakeInstruction; +using spvtest::TextToBinaryTest; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::TestWithParam; +using ::testing::Values; +using ::testing::ValuesIn; + +// Test OpSelectionMerge + +using OpSelectionMergeTest = spvtest::TextToBinaryTestBase< + TestWithParam>>; + +TEST_P(OpSelectionMergeTest, AnySingleSelectionControlMask) { + const std::string input = "OpSelectionMerge %1 " + GetParam().name(); + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpSelectionMerge, {1, GetParam().value()}))); +} + +// clang-format off +#define CASE(VALUE,NAME) { SpvSelectionControl##VALUE, NAME} +INSTANTIATE_TEST_SUITE_P(TextToBinarySelectionMerge, OpSelectionMergeTest, + ValuesIn(std::vector>{ + CASE(MaskNone, "None"), + CASE(FlattenMask, "Flatten"), + CASE(DontFlattenMask, "DontFlatten"), + })); +#undef CASE +// clang-format on + +TEST_F(OpSelectionMergeTest, CombinedSelectionControlMask) { + const std::string input = "OpSelectionMerge %1 Flatten|DontFlatten"; + const uint32_t expected_mask = + SpvSelectionControlFlattenMask | SpvSelectionControlDontFlattenMask; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpSelectionMerge, {1, expected_mask}))); +} + +TEST_F(OpSelectionMergeTest, WrongSelectionControl) { + // Case sensitive: "flatten" != "Flatten" and thus wrong. + EXPECT_THAT(CompileFailure("OpSelectionMerge %1 flatten|DontFlatten"), + Eq("Invalid selection control operand 'flatten|DontFlatten'.")); +} + +// Test OpLoopMerge + +using OpLoopMergeTest = spvtest::TextToBinaryTestBase< + TestWithParam>>>; + +TEST_P(OpLoopMergeTest, AnySingleLoopControlMask) { + const auto ctrl = std::get<1>(GetParam()); + std::ostringstream input; + input << "OpLoopMerge %merge %continue " << ctrl.name(); + for (auto num : ctrl.operands()) input << " " << num; + EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())), + Eq(MakeInstruction(SpvOpLoopMerge, {1, 2, ctrl.value()}, + ctrl.operands()))); +} + +#define CASE(VALUE, NAME) \ + { SpvLoopControl##VALUE, NAME } +#define CASE1(VALUE, NAME, PARM) \ + { \ + SpvLoopControl##VALUE, NAME, { PARM } \ + } +INSTANTIATE_TEST_SUITE_P( + TextToBinaryLoopMerge, OpLoopMergeTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector>{ + // clang-format off + CASE(MaskNone, "None"), + CASE(UnrollMask, "Unroll"), + CASE(DontUnrollMask, "DontUnroll"), + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + TextToBinaryLoopMergeV11, OpLoopMergeTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector>{ + // clang-format off + CASE(DependencyInfiniteMask, "DependencyInfinite"), + CASE1(DependencyLengthMask, "DependencyLength", 234), + {SpvLoopControlUnrollMask|SpvLoopControlDependencyLengthMask, + "DependencyLength|Unroll", {33}}, + // clang-format on + }))); +#undef CASE +#undef CASE1 + +TEST_F(OpLoopMergeTest, CombinedLoopControlMask) { + const std::string input = "OpLoopMerge %merge %continue Unroll|DontUnroll"; + const uint32_t expected_mask = + SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpLoopMerge, {1, 2, expected_mask}))); +} + +TEST_F(OpLoopMergeTest, WrongLoopControl) { + EXPECT_THAT(CompileFailure("OpLoopMerge %m %c none"), + Eq("Invalid loop control operand 'none'.")); +} + +// Test OpSwitch + +TEST_F(TextToBinaryTest, SwitchGoodZeroTargets) { + EXPECT_THAT(CompiledInstructions("OpSwitch %selector %default"), + Eq(MakeInstruction(SpvOpSwitch, {1, 2}))); +} + +TEST_F(TextToBinaryTest, SwitchGoodOneTarget) { + EXPECT_THAT(CompiledInstructions("%1 = OpTypeInt 32 0\n" + "%2 = OpConstant %1 52\n" + "OpSwitch %2 %default 12 %target0"), + Eq(Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 52}), + MakeInstruction(SpvOpSwitch, {2, 3, 12, 4})}))); +} + +TEST_F(TextToBinaryTest, SwitchGoodTwoTargets) { + EXPECT_THAT( + CompiledInstructions("%1 = OpTypeInt 32 0\n" + "%2 = OpConstant %1 52\n" + "OpSwitch %2 %default 12 %target0 42 %target1"), + Eq(Concatenate({ + MakeInstruction(SpvOpTypeInt, {1, 32, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 52}), + MakeInstruction(SpvOpSwitch, {2, 3, 12, 4, 42, 5}), + }))); +} + +TEST_F(TextToBinaryTest, SwitchBadMissingSelector) { + EXPECT_THAT(CompileFailure("OpSwitch"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(TextToBinaryTest, SwitchBadInvalidSelector) { + EXPECT_THAT(CompileFailure("OpSwitch 12"), + Eq("Expected id to start with %.")); +} + +TEST_F(TextToBinaryTest, SwitchBadMissingDefault) { + EXPECT_THAT(CompileFailure("OpSwitch %selector"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(TextToBinaryTest, SwitchBadInvalidDefault) { + EXPECT_THAT(CompileFailure("OpSwitch %selector 12"), + Eq("Expected id to start with %.")); +} + +TEST_F(TextToBinaryTest, SwitchBadInvalidLiteral) { + // The assembler recognizes "OpSwitch %selector %default" as a complete + // instruction. Then it tries to parse "%abc" as the start of a new + // instruction, but can't since it hits the end of stream. + const auto input = R"(%i32 = OpTypeInt 32 0 + %selector = OpConstant %i32 42 + OpSwitch %selector %default %abc)"; + EXPECT_THAT(CompileFailure(input), Eq("Expected '=', found end of stream.")); +} + +TEST_F(TextToBinaryTest, SwitchBadMissingTarget) { + EXPECT_THAT(CompileFailure("%1 = OpTypeInt 32 0\n" + "%2 = OpConstant %1 52\n" + "OpSwitch %2 %default 12"), + Eq("Expected operand, found end of stream.")); +} + +// A test case for an OpSwitch. +// It is also parameterized to test encodings OpConstant +// integer literals. This can capture both single and multi-word +// integer literal tests. +struct SwitchTestCase { + std::string constant_type_args; + std::string constant_value_arg; + std::string case_value_arg; + std::vector expected_instructions; +}; + +using OpSwitchValidTest = + spvtest::TextToBinaryTestBase>; + +// Tests the encoding of OpConstant literal values, and also +// the literal integer cases in an OpSwitch. This can +// test both single and multi-word integer literal encodings. +TEST_P(OpSwitchValidTest, ValidTypes) { + const std::string input = "%1 = OpTypeInt " + GetParam().constant_type_args + + "\n" + "%2 = OpConstant %1 " + + GetParam().constant_value_arg + + "\n" + "OpSwitch %2 %default " + + GetParam().case_value_arg + " %4\n"; + std::vector instructions; + EXPECT_THAT(CompiledInstructions(input), + Eq(GetParam().expected_instructions)); +} + +// Constructs a SwitchTestCase from the given integer_width, signedness, +// constant value string, and expected encoded constant. +SwitchTestCase MakeSwitchTestCase(uint32_t integer_width, + uint32_t integer_signedness, + std::string constant_str, + std::vector encoded_constant, + std::string case_value_str, + std::vector encoded_case_value) { + std::stringstream ss; + ss << integer_width << " " << integer_signedness; + return SwitchTestCase{ + ss.str(), + constant_str, + case_value_str, + {Concatenate( + {MakeInstruction(SpvOpTypeInt, + {1, integer_width, integer_signedness}), + MakeInstruction(SpvOpConstant, + Concatenate({{1, 2}, encoded_constant})), + MakeInstruction(SpvOpSwitch, + Concatenate({{2, 3}, encoded_case_value, {4}}))})}}; +} + +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpSwitchValid1Word, OpSwitchValidTest, + ValuesIn(std::vector({ + MakeSwitchTestCase(32, 0, "42", {42}, "100", {100}), + MakeSwitchTestCase(32, 1, "-1", {0xffffffff}, "100", {100}), + // SPIR-V 1.0 Rev 1 clarified that for an integer narrower than 32-bits, + // its bits will appear in the lower order bits of the 32-bit word, and + // a signed integer is sign-extended. + MakeSwitchTestCase(7, 0, "127", {127}, "100", {100}), + MakeSwitchTestCase(14, 0, "99", {99}, "100", {100}), + MakeSwitchTestCase(16, 0, "65535", {65535}, "100", {100}), + MakeSwitchTestCase(16, 1, "101", {101}, "100", {100}), + // Demonstrate sign extension + MakeSwitchTestCase(16, 1, "-2", {0xfffffffe}, "100", {100}), + // Hex cases + MakeSwitchTestCase(16, 1, "0x7ffe", {0x7ffe}, "0x1234", {0x1234}), + MakeSwitchTestCase(16, 1, "0x8000", {0xffff8000}, "0x8100", + {0xffff8100}), + MakeSwitchTestCase(16, 0, "0x8000", {0x00008000}, "0x8100", {0x8100}), + }))); + +// NB: The words LOW ORDER bits show up first. +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpSwitchValid2Words, OpSwitchValidTest, + ValuesIn(std::vector({ + MakeSwitchTestCase(33, 0, "101", {101, 0}, "500", {500, 0}), + MakeSwitchTestCase(48, 1, "-1", {0xffffffff, 0xffffffff}, "900", + {900, 0}), + MakeSwitchTestCase(64, 1, "-2", {0xfffffffe, 0xffffffff}, "-5", + {0xfffffffb, uint32_t(-1)}), + // Hex cases + MakeSwitchTestCase(48, 1, "0x7fffffffffff", {0xffffffff, 0x00007fff}, + "100", {100, 0}), + MakeSwitchTestCase(48, 1, "0x800000000000", {0x00000000, 0xffff8000}, + "0x800000000000", {0x00000000, 0xffff8000}), + MakeSwitchTestCase(48, 0, "0x800000000000", {0x00000000, 0x00008000}, + "0x800000000000", {0x00000000, 0x00008000}), + MakeSwitchTestCase(63, 0, "0x500000000", {0, 5}, "12", {12, 0}), + MakeSwitchTestCase(64, 0, "0x600000000", {0, 6}, "12", {12, 0}), + MakeSwitchTestCase(64, 1, "0x700000123", {0x123, 7}, "12", {12, 0}), + }))); + +using ControlFlowRoundTripTest = RoundTripTest; + +TEST_P(ControlFlowRoundTripTest, DisassemblyEqualsAssemblyInput) { + const std::string assembly = GetParam(); + EXPECT_THAT(EncodeAndDecodeSuccessfully(assembly), Eq(assembly)) << assembly; +} + +INSTANTIATE_TEST_SUITE_P( + OpSwitchRoundTripUnsignedIntegers, ControlFlowRoundTripTest, + ValuesIn(std::vector({ + // Unsigned 16-bit. + "%1 = OpTypeInt 16 0\n%2 = OpConstant %1 65535\nOpSwitch %2 %3\n", + // Unsigned 32-bit, three non-default cases. + "%1 = OpTypeInt 32 0\n%2 = OpConstant %1 123456\n" + "OpSwitch %2 %3 100 %4 102 %5 1000000 %6\n", + // Unsigned 48-bit, three non-default cases. + "%1 = OpTypeInt 48 0\n%2 = OpConstant %1 5000000000\n" + "OpSwitch %2 %3 100 %4 102 %5 6000000000 %6\n", + // Unsigned 64-bit, three non-default cases. + "%1 = OpTypeInt 64 0\n%2 = OpConstant %1 9223372036854775807\n" + "OpSwitch %2 %3 100 %4 102 %5 9000000000000000000 %6\n", + }))); + +INSTANTIATE_TEST_SUITE_P( + OpSwitchRoundTripSignedIntegers, ControlFlowRoundTripTest, + ValuesIn(std::vector{ + // Signed 16-bit, with two non-default cases + "%1 = OpTypeInt 16 1\n%2 = OpConstant %1 32767\n" + "OpSwitch %2 %3 99 %4 -102 %5\n", + "%1 = OpTypeInt 16 1\n%2 = OpConstant %1 -32768\n" + "OpSwitch %2 %3 99 %4 -102 %5\n", + // Signed 32-bit, two non-default cases. + "%1 = OpTypeInt 32 1\n%2 = OpConstant %1 -123456\n" + "OpSwitch %2 %3 100 %4 -123456 %5\n", + "%1 = OpTypeInt 32 1\n%2 = OpConstant %1 123456\n" + "OpSwitch %2 %3 100 %4 123456 %5\n", + // Signed 48-bit, three non-default cases. + "%1 = OpTypeInt 48 1\n%2 = OpConstant %1 5000000000\n" + "OpSwitch %2 %3 100 %4 -7000000000 %5 6000000000 %6\n", + "%1 = OpTypeInt 48 1\n%2 = OpConstant %1 -5000000000\n" + "OpSwitch %2 %3 100 %4 -7000000000 %5 6000000000 %6\n", + // Signed 64-bit, three non-default cases. + "%1 = OpTypeInt 64 1\n%2 = OpConstant %1 9223372036854775807\n" + "OpSwitch %2 %3 100 %4 7000000000 %5 -1000000000000000000 %6\n", + "%1 = OpTypeInt 64 1\n%2 = OpConstant %1 -9223372036854775808\n" + "OpSwitch %2 %3 100 %4 7000000000 %5 -1000000000000000000 %6\n", + })); + +using OpSwitchInvalidTypeTestCase = + spvtest::TextToBinaryTestBase>; + +TEST_P(OpSwitchInvalidTypeTestCase, InvalidTypes) { + const std::string input = + "%1 = " + GetParam() + + "\n" + "%3 = OpCopyObject %1 %2\n" // We only care the type of the expression + " OpSwitch %3 %default 32 %c\n"; + EXPECT_THAT(CompileFailure(input), + Eq("The selector operand for OpSwitch must be the result of an " + "instruction that generates an integer scalar")); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + TextToBinaryOpSwitchInvalidTests, OpSwitchInvalidTypeTestCase, + ValuesIn(std::vector{ + {"OpTypeVoid", + "OpTypeBool", + "OpTypeFloat 32", + "OpTypeVector %a 32", + "OpTypeMatrix %a 32", + "OpTypeImage %a 1D 0 0 0 0 Unknown", + "OpTypeSampler", + "OpTypeSampledImage %a", + "OpTypeArray %a %b", + "OpTypeRuntimeArray %a", + "OpTypeStruct %a", + "OpTypeOpaque \"Foo\"", + "OpTypePointer UniformConstant %a", + "OpTypeFunction %a %b", + "OpTypeEvent", + "OpTypeDeviceEvent", + "OpTypeReserveId", + "OpTypeQueue", + "OpTypePipe ReadOnly", + + // Skip OpTypeForwardPointer becasuse it doesn't even produce a result + // ID. + + // At least one thing that isn't a type at all + "OpNot %a %b" + }, + })); +// clang-format on + +using OpKillTest = spvtest::TextToBinaryTest; + +INSTANTIATE_TEST_SUITE_P(OpKillTest, ControlFlowRoundTripTest, + Values("OpKill\n")); + +TEST_F(OpKillTest, ExtraArgsAssemblyError) { + const std::string input = "OpKill 1"; + EXPECT_THAT(CompileFailure(input), + Eq("Expected or at the beginning of an " + "instruction, found '1'.")); +} + +using OpTerminateInvocationTest = spvtest::TextToBinaryTest; + +INSTANTIATE_TEST_SUITE_P(OpTerminateInvocationTest, ControlFlowRoundTripTest, + Values("OpTerminateInvocation\n")); + +TEST_F(OpTerminateInvocationTest, ExtraArgsAssemblyError) { + const std::string input = "OpTerminateInvocation 1"; + EXPECT_THAT(CompileFailure(input), + Eq("Expected or at the beginning of an " + "instruction, found '1'.")); +} + +// TODO(dneto): OpPhi +// TODO(dneto): OpLoopMerge +// TODO(dneto): OpLabel +// TODO(dneto): OpBranch +// TODO(dneto): OpSwitch +// TODO(dneto): OpReturn +// TODO(dneto): OpReturnValue +// TODO(dneto): OpUnreachable +// TODO(dneto): OpLifetimeStart +// TODO(dneto): OpLifetimeStop + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.debug_test.cpp b/third_party/spirv-tools/test/text_to_binary.debug_test.cpp new file mode 100644 index 0000000..39ba5c5 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.debug_test.cpp @@ -0,0 +1,215 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Debug" section of the +// SPIR-V spec. + +#include +#include + +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::MakeInstruction; +using utils::MakeVector; +using spvtest::TextToBinaryTest; +using ::testing::Eq; + +// Test OpSource + +// A single test case for OpSource +struct LanguageCase { + uint32_t get_language_value() const { + return static_cast(language_value); + } + const char* language_name; + SpvSourceLanguage language_value; + uint32_t version; +}; + +// clang-format off +// The list of OpSource cases to use. +const LanguageCase kLanguageCases[] = { +#define CASE(NAME, VERSION) \ + { #NAME, SpvSourceLanguage##NAME, VERSION } + CASE(Unknown, 0), + CASE(Unknown, 999), + CASE(ESSL, 310), + CASE(GLSL, 450), + CASE(OpenCL_C, 120), + CASE(OpenCL_C, 200), + CASE(OpenCL_C, 210), + CASE(OpenCL_CPP, 210), + CASE(HLSL, 5), + CASE(HLSL, 6), +#undef CASE +}; +// clang-format on + +using OpSourceTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpSourceTest, AnyLanguage) { + const std::string input = std::string("OpSource ") + + GetParam().language_name + " " + + std::to_string(GetParam().version); + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpSource, {GetParam().get_language_value(), + GetParam().version}))); +} + +INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpSourceTest, + ::testing::ValuesIn(kLanguageCases)); + +TEST_F(OpSourceTest, WrongLanguage) { + EXPECT_THAT(CompileFailure("OpSource xxyyzz 12345"), + Eq("Invalid source language 'xxyyzz'.")); +} + +TEST_F(TextToBinaryTest, OpSourceAcceptsOptionalFileId) { + // In the grammar, the file id is an OperandOptionalId. + const std::string input = "OpSource GLSL 450 %file_id"; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpSource, {SpvSourceLanguageGLSL, 450, 1}))); +} + +TEST_F(TextToBinaryTest, OpSourceAcceptsOptionalSourceText) { + std::string fake_source = "To be or not to be"; + const std::string input = + "OpSource GLSL 450 %file_id \"" + fake_source + "\""; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpSource, {SpvSourceLanguageGLSL, 450, 1}, + MakeVector(fake_source)))); +} + +// Test OpSourceContinued + +using OpSourceContinuedTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpSourceContinuedTest, AnyExtension) { + // TODO(dneto): utf-8, quoting, escaping + const std::string input = + std::string("OpSourceContinued \"") + GetParam() + "\""; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpSourceContinued, MakeVector(GetParam())))); +} + +// TODO(dneto): utf-8, quoting, escaping +INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpSourceContinuedTest, + ::testing::ValuesIn(std::vector{ + "", "foo bar this and that"})); + +// Test OpSourceExtension + +using OpSourceExtensionTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpSourceExtensionTest, AnyExtension) { + // TODO(dneto): utf-8, quoting, escaping + const std::string input = + std::string("OpSourceExtension \"") + GetParam() + "\""; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpSourceExtension, MakeVector(GetParam())))); +} + +// TODO(dneto): utf-8, quoting, escaping +INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpSourceExtensionTest, + ::testing::ValuesIn(std::vector{ + "", "foo bar this and that"})); + +TEST_F(TextToBinaryTest, OpLine) { + EXPECT_THAT(CompiledInstructions("OpLine %srcfile 42 99"), + Eq(MakeInstruction(SpvOpLine, {1, 42, 99}))); +} + +TEST_F(TextToBinaryTest, OpNoLine) { + EXPECT_THAT(CompiledInstructions("OpNoLine"), + Eq(MakeInstruction(SpvOpNoLine, {}))); +} + +using OpStringTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpStringTest, AnyString) { + // TODO(dneto): utf-8, quoting, escaping + const std::string input = + std::string("%result = OpString \"") + GetParam() + "\""; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpString, {1}, MakeVector(GetParam())))); +} + +// TODO(dneto): utf-8, quoting, escaping +INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpStringTest, + ::testing::ValuesIn(std::vector{ + "", "foo bar this and that"})); + +using OpNameTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpNameTest, AnyString) { + const std::string input = + std::string("OpName %target \"") + GetParam() + "\""; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpName, {1}, MakeVector(GetParam())))); +} + +// UTF-8, quoting, escaping, etc. are covered in the StringLiterals tests in +// BinaryToText.Literal.cpp. +INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpNameTest, + ::testing::Values("", "foo bar this and that")); + +using OpMemberNameTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpMemberNameTest, AnyString) { + // TODO(dneto): utf-8, quoting, escaping + const std::string input = + std::string("OpMemberName %type 42 \"") + GetParam() + "\""; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpMemberName, {1, 42}, MakeVector(GetParam())))); +} + +// TODO(dneto): utf-8, quoting, escaping +INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpMemberNameTest, + ::testing::ValuesIn(std::vector{ + "", "foo bar this and that"})); + +// TODO(dneto): Parse failures? + +using OpModuleProcessedTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpModuleProcessedTest, AnyString) { + const std::string input = + std::string("OpModuleProcessed \"") + GetParam() + "\""; + EXPECT_THAT( + CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_1), + Eq(MakeInstruction(SpvOpModuleProcessed, MakeVector(GetParam())))); +} + +INSTANTIATE_TEST_SUITE_P(TextToBinaryTestDebug, OpModuleProcessedTest, + ::testing::Values("", "foo bar this and that")); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.device_side_enqueue_test.cpp b/third_party/spirv-tools/test/text_to_binary.device_side_enqueue_test.cpp new file mode 100644 index 0000000..03d7e74 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.device_side_enqueue_test.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Device-Side Enqueue Instructions" +// section of the SPIR-V spec. + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::MakeInstruction; +using ::testing::Eq; + +// Test OpEnqueueKernel + +struct KernelEnqueueCase { + std::string local_size_source; + std::vector local_size_operands; +}; + +using OpEnqueueKernelGood = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(OpEnqueueKernelGood, Sample) { + const std::string input = + "%result = OpEnqueueKernel %type %queue %flags %NDRange %num_events" + " %wait_events %ret_event %invoke %param %param_size %param_align " + + GetParam().local_size_source; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpEnqueueKernel, + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, + GetParam().local_size_operands))); +} + +INSTANTIATE_TEST_SUITE_P( + TextToBinaryTest, OpEnqueueKernelGood, + ::testing::ValuesIn(std::vector{ + // Provide IDs for pointer-to-local arguments for the + // invoked function. + // Test up to 10 such arguments. + // I (dneto) can't find a limit on the number of kernel + // arguments in OpenCL C 2.0 Rev 29, e.g. in section 6.9 + // Restrictions. + {"", {}}, + {"%l0", {13}}, + {"%l0 %l1", {13, 14}}, + {"%l0 %l1 %l2", {13, 14, 15}}, + {"%l0 %l1 %l2 %l3", {13, 14, 15, 16}}, + {"%l0 %l1 %l2 %l3 %l4", {13, 14, 15, 16, 17}}, + {"%l0 %l1 %l2 %l3 %l4 %l5", {13, 14, 15, 16, 17, 18}}, + {"%l0 %l1 %l2 %l3 %l4 %l5 %l6", {13, 14, 15, 16, 17, 18, 19}}, + {"%l0 %l1 %l2 %l3 %l4 %l5 %l6 %l7", {13, 14, 15, 16, 17, 18, 19, 20}}, + {"%l0 %l1 %l2 %l3 %l4 %l5 %l6 %l7 %l8", + {13, 14, 15, 16, 17, 18, 19, 20, 21}}, + {"%l0 %l1 %l2 %l3 %l4 %l5 %l6 %l7 %l8 %l9", + {13, 14, 15, 16, 17, 18, 19, 20, 21, 22}}, + })); + +// Test some bad parses of OpEnqueueKernel. For other cases, we're relying +// on the uniformity of the parsing algorithm. The following two tests, ensure +// that every required ID operand is specified, and is actually an ID operand. +using OpKernelEnqueueBad = spvtest::TextToBinaryTest; + +TEST_F(OpKernelEnqueueBad, MissingLastOperand) { + EXPECT_THAT( + CompileFailure( + "%result = OpEnqueueKernel %type %queue %flags %NDRange %num_events" + " %wait_events %ret_event %invoke %param %param_size"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(OpKernelEnqueueBad, InvalidLastOperand) { + EXPECT_THAT( + CompileFailure( + "%result = OpEnqueueKernel %type %queue %flags %NDRange %num_events" + " %wait_events %ret_event %invoke %param %param_size 42"), + Eq("Expected id to start with %.")); +} + +// TODO(dneto): OpEnqueueMarker +// TODO(dneto): OpGetKernelNDRangeSubGroupCount +// TODO(dneto): OpGetKernelNDRangeMaxSubGroupSize +// TODO(dneto): OpGetKernelWorkGroupSize +// TODO(dneto): OpGetKernelPreferredWorkGroupSizeMultiple +// TODO(dneto): OpRetainEvent +// TODO(dneto): OpReleaseEvent +// TODO(dneto): OpCreateUserEvent +// TODO(dneto): OpSetUserEventStatus +// TODO(dneto): OpCaptureEventProfilingInfo +// TODO(dneto): OpGetDefaultQueue +// TODO(dneto): OpBuildNDRange +// TODO(dneto): OpBuildNDRange + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.extension_test.cpp b/third_party/spirv-tools/test/text_to_binary.extension_test.cpp new file mode 100644 index 0000000..023763b --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.extension_test.cpp @@ -0,0 +1,909 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Extension Instruction" section +// of the SPIR-V spec. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/latest_version_opencl_std_header.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using utils::MakeVector; +using spvtest::TextToBinaryTest; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::Values; +using ::testing::ValuesIn; + +// Returns a generator of common Vulkan environment values to be tested. +std::vector CommonVulkanEnvs() { + return {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}; +} + +TEST_F(TextToBinaryTest, InvalidExtInstImportName) { + EXPECT_THAT(CompileFailure("%1 = OpExtInstImport \"Haskell.std\""), + Eq("Invalid extended instruction import 'Haskell.std'")); +} + +TEST_F(TextToBinaryTest, InvalidImportId) { + EXPECT_THAT(CompileFailure("%1 = OpTypeVoid\n" + "%2 = OpExtInst %1 %1"), + Eq("Invalid extended instruction import Id 2")); +} + +TEST_F(TextToBinaryTest, InvalidImportInstruction) { + const std::string input = R"(%1 = OpTypeVoid + %2 = OpExtInstImport "OpenCL.std" + %3 = OpExtInst %1 %2 not_in_the_opencl)"; + EXPECT_THAT(CompileFailure(input), + Eq("Invalid extended instruction name 'not_in_the_opencl'.")); +} + +TEST_F(TextToBinaryTest, MultiImport) { + const std::string input = R"(%2 = OpExtInstImport "OpenCL.std" + %2 = OpExtInstImport "OpenCL.std")"; + EXPECT_THAT(CompileFailure(input), + Eq("Import Id is being defined a second time")); +} + +TEST_F(TextToBinaryTest, TooManyArguments) { + const std::string input = R"(%opencl = OpExtInstImport "OpenCL.std" + %2 = OpExtInst %float %opencl cos %x %oops")"; + EXPECT_THAT(CompileFailure(input), Eq("Expected '=', found end of stream.")); +} + +TEST_F(TextToBinaryTest, ExtInstFromTwoDifferentImports) { + const std::string input = R"(%1 = OpExtInstImport "OpenCL.std" +%2 = OpExtInstImport "GLSL.std.450" +%4 = OpExtInst %3 %1 native_sqrt %5 +%7 = OpExtInst %6 %2 MatrixInverse %8 +)"; + + // Make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(input), + Eq(Concatenate({ + MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("OpenCL.std")), + MakeInstruction(SpvOpExtInstImport, {2}, MakeVector("GLSL.std.450")), + MakeInstruction( + SpvOpExtInst, + {3, 4, 1, uint32_t(OpenCLLIB::Entrypoints::Native_sqrt), 5}), + MakeInstruction(SpvOpExtInst, + {6, 7, 2, uint32_t(GLSLstd450MatrixInverse), 8}), + }))); + + // Make sure it disassembles correctly. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input)); +} + +// A test case for assembling into words in an instruction. +struct AssemblyCase { + std::string input; + std::vector expected; +}; + +using ExtensionAssemblyTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(ExtensionAssemblyTest, Samples) { + const spv_target_env& env = std::get<0>(GetParam()); + const AssemblyCase& ac = std::get<1>(GetParam()); + + // Check that it assembles correctly. + EXPECT_THAT(CompiledInstructions(ac.input, env), Eq(ac.expected)); +} + +using ExtensionRoundTripTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(ExtensionRoundTripTest, Samples) { + const spv_target_env& env = std::get<0>(GetParam()); + const AssemblyCase& ac = std::get<1>(GetParam()); + + // Check that it assembles correctly. + EXPECT_THAT(CompiledInstructions(ac.input, env), Eq(ac.expected)); + + // Check round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(ac.input, + SPV_BINARY_TO_TEXT_OPTION_NONE, env), + Eq(ac.input)) + << "target env: " << spvTargetEnvDescription(env) << "\n"; +} + +// SPV_KHR_shader_ballot + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_shader_ballot, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {"OpCapability SubgroupBallotKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilitySubgroupBallotKHR})}, + {"%2 = OpSubgroupBallotKHR %1 %3\n", + MakeInstruction(SpvOpSubgroupBallotKHR, {1, 2, 3})}, + {"%2 = OpSubgroupFirstInvocationKHR %1 %3\n", + MakeInstruction(SpvOpSubgroupFirstInvocationKHR, {1, 2, 3})}, + {"OpDecorate %1 BuiltIn SubgroupEqMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupEqMaskKHR})}, + {"OpDecorate %1 BuiltIn SubgroupGeMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupGeMaskKHR})}, + {"OpDecorate %1 BuiltIn SubgroupGtMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupGtMaskKHR})}, + {"OpDecorate %1 BuiltIn SubgroupLeMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupLeMaskKHR})}, + {"OpDecorate %1 BuiltIn SubgroupLtMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupLtMaskKHR})}, + }))); + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_shader_ballot_vulkan_1_1, ExtensionRoundTripTest, + // In SPIR-V 1.3 and Vulkan 1.1 we can drop the KHR suffix on the + // builtin enums. + Combine(Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1), + ValuesIn(std::vector{ + {"OpCapability SubgroupBallotKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilitySubgroupBallotKHR})}, + {"%2 = OpSubgroupBallotKHR %1 %3\n", + MakeInstruction(SpvOpSubgroupBallotKHR, {1, 2, 3})}, + {"%2 = OpSubgroupFirstInvocationKHR %1 %3\n", + MakeInstruction(SpvOpSubgroupFirstInvocationKHR, {1, 2, 3})}, + {"OpDecorate %1 BuiltIn SubgroupEqMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupEqMask})}, + {"OpDecorate %1 BuiltIn SubgroupGeMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupGeMask})}, + {"OpDecorate %1 BuiltIn SubgroupGtMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupGtMask})}, + {"OpDecorate %1 BuiltIn SubgroupLeMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupLeMask})}, + {"OpDecorate %1 BuiltIn SubgroupLtMask\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupLtMask})}, + }))); + +// The old builtin names (with KHR suffix) still work in the assmebler, and +// map to the enums without the KHR. +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_shader_ballot_vulkan_1_1_alias_check, ExtensionAssemblyTest, + // In SPIR-V 1.3 and Vulkan 1.1 we can drop the KHR suffix on the + // builtin enums. + Combine(Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1), + ValuesIn(std::vector{ + {"OpDecorate %1 BuiltIn SubgroupEqMaskKHR\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupEqMask})}, + {"OpDecorate %1 BuiltIn SubgroupGeMaskKHR\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupGeMask})}, + {"OpDecorate %1 BuiltIn SubgroupGtMaskKHR\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupGtMask})}, + {"OpDecorate %1 BuiltIn SubgroupLeMaskKHR\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupLeMask})}, + {"OpDecorate %1 BuiltIn SubgroupLtMaskKHR\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInSubgroupLtMask})}, + }))); + +// SPV_KHR_shader_draw_parameters + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_shader_draw_parameters, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine( + ValuesIn(CommonVulkanEnvs()), + ValuesIn(std::vector{ + {"OpCapability DrawParameters\n", + MakeInstruction(SpvOpCapability, {SpvCapabilityDrawParameters})}, + {"OpDecorate %1 BuiltIn BaseVertex\n", + MakeInstruction(SpvOpDecorate, + {1, SpvDecorationBuiltIn, SpvBuiltInBaseVertex})}, + {"OpDecorate %1 BuiltIn BaseInstance\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInBaseInstance})}, + {"OpDecorate %1 BuiltIn DrawIndex\n", + MakeInstruction(SpvOpDecorate, + {1, SpvDecorationBuiltIn, SpvBuiltInDrawIndex})}, + }))); + +// SPV_KHR_subgroup_vote + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_subgroup_vote, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine(ValuesIn(CommonVulkanEnvs()), + ValuesIn(std::vector{ + {"OpCapability SubgroupVoteKHR\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilitySubgroupVoteKHR})}, + {"%2 = OpSubgroupAnyKHR %1 %3\n", + MakeInstruction(SpvOpSubgroupAnyKHR, {1, 2, 3})}, + {"%2 = OpSubgroupAllKHR %1 %3\n", + MakeInstruction(SpvOpSubgroupAllKHR, {1, 2, 3})}, + {"%2 = OpSubgroupAllEqualKHR %1 %3\n", + MakeInstruction(SpvOpSubgroupAllEqualKHR, {1, 2, 3})}, + }))); + +// SPV_KHR_16bit_storage + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_16bit_storage, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine(ValuesIn(CommonVulkanEnvs()), + ValuesIn(std::vector{ + {"OpCapability StorageBuffer16BitAccess\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStorageUniformBufferBlock16})}, + {"OpCapability StorageBuffer16BitAccess\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStorageBuffer16BitAccess})}, + {"OpCapability UniformAndStorageBuffer16BitAccess\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityUniformAndStorageBuffer16BitAccess})}, + {"OpCapability UniformAndStorageBuffer16BitAccess\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStorageUniform16})}, + {"OpCapability StoragePushConstant16\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStoragePushConstant16})}, + {"OpCapability StorageInputOutput16\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStorageInputOutput16})}, + }))); + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_16bit_storage_alias_check, ExtensionAssemblyTest, + Combine(ValuesIn(CommonVulkanEnvs()), + ValuesIn(std::vector{ + // The old name maps to the new enum. + {"OpCapability StorageUniformBufferBlock16\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStorageBuffer16BitAccess})}, + // The new name maps to the old enum. + {"OpCapability UniformAndStorageBuffer16BitAccess\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStorageUniform16})}, + }))); + +// SPV_KHR_device_group + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_device_group, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine(ValuesIn(CommonVulkanEnvs()), + ValuesIn(std::vector{ + {"OpCapability DeviceGroup\n", + MakeInstruction(SpvOpCapability, {SpvCapabilityDeviceGroup})}, + {"OpDecorate %1 BuiltIn DeviceIndex\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInDeviceIndex})}, + }))); + +// SPV_KHR_8bit_storage + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_8bit_storage, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine( + ValuesIn(CommonVulkanEnvs()), + ValuesIn(std::vector{ + {"OpCapability StorageBuffer8BitAccess\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStorageBuffer8BitAccess})}, + {"OpCapability UniformAndStorageBuffer8BitAccess\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityUniformAndStorageBuffer8BitAccess})}, + {"OpCapability StoragePushConstant8\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityStoragePushConstant8})}, + }))); + +// SPV_KHR_multiview + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_multiview, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {"OpCapability MultiView\n", + MakeInstruction(SpvOpCapability, {SpvCapabilityMultiView})}, + {"OpDecorate %1 BuiltIn ViewIndex\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInViewIndex})}, + }))); + +// SPV_AMD_shader_explicit_vertex_parameter + +#define PREAMBLE \ + "%1 = OpExtInstImport \"SPV_AMD_shader_explicit_vertex_parameter\"\n" +INSTANTIATE_TEST_SUITE_P( + SPV_AMD_shader_explicit_vertex_parameter, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {PREAMBLE "%3 = OpExtInst %2 %1 InterpolateAtVertexAMD %4 %5\n", + Concatenate( + {MakeInstruction( + SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_explicit_vertex_parameter")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 1, 4, 5})})}, + }))); +#undef PREAMBLE + +// SPV_AMD_shader_trinary_minmax + +#define PREAMBLE "%1 = OpExtInstImport \"SPV_AMD_shader_trinary_minmax\"\n" +INSTANTIATE_TEST_SUITE_P( + SPV_AMD_shader_trinary_minmax, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {PREAMBLE "%3 = OpExtInst %2 %1 FMin3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 1, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 UMin3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 2, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 SMin3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 3, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 FMax3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 4, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 UMax3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 5, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 SMax3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 6, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 FMid3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 7, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 UMid3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 8, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 SMid3AMD %4 %5 %6\n", + Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_trinary_minmax")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 9, 4, 5, 6})})}, + }))); +#undef PREAMBLE + +// SPV_AMD_gcn_shader + +#define PREAMBLE "%1 = OpExtInstImport \"SPV_AMD_gcn_shader\"\n" +INSTANTIATE_TEST_SUITE_P( + SPV_AMD_gcn_shader, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {PREAMBLE "%3 = OpExtInst %2 %1 CubeFaceIndexAMD %4\n", + Concatenate({MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_gcn_shader")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 1, 4})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 CubeFaceCoordAMD %4\n", + Concatenate({MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_gcn_shader")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 2, 4})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 TimeAMD\n", + Concatenate({MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_gcn_shader")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 3})})}, + }))); +#undef PREAMBLE + +// SPV_AMD_shader_ballot + +#define PREAMBLE "%1 = OpExtInstImport \"SPV_AMD_shader_ballot\"\n" +INSTANTIATE_TEST_SUITE_P( + SPV_AMD_shader_ballot, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {PREAMBLE "%3 = OpExtInst %2 %1 SwizzleInvocationsAMD %4 %5\n", + Concatenate({MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_ballot")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 1, 4, 5})})}, + {PREAMBLE + "%3 = OpExtInst %2 %1 SwizzleInvocationsMaskedAMD %4 %5\n", + Concatenate({MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_ballot")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 2, 4, 5})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 WriteInvocationAMD %4 %5 %6\n", + Concatenate({MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_ballot")), + MakeInstruction(SpvOpExtInst, + {2, 3, 1, 3, 4, 5, 6})})}, + {PREAMBLE "%3 = OpExtInst %2 %1 MbcntAMD %4\n", + Concatenate({MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("SPV_AMD_shader_ballot")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, 4, 4})})}, + }))); +#undef PREAMBLE + +// SPV_KHR_variable_pointers + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_variable_pointers, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {"OpCapability VariablePointers\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityVariablePointers})}, + {"OpCapability VariablePointersStorageBuffer\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityVariablePointersStorageBuffer})}, + }))); + +// SPV_KHR_vulkan_memory_model + +INSTANTIATE_TEST_SUITE_P( + SPV_KHR_vulkan_memory_model, ExtensionRoundTripTest, + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + // + // Note: SPV_KHR_vulkan_memory_model adds scope enum value QueueFamilyKHR. + // Scope enums are used in ID definitions elsewhere, that don't know they + // are using particular enums. So the assembler doesn't support assembling + // those enums names into the corresponding values. So there is no asm/dis + // tests for those enums. + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1), + ValuesIn(std::vector{ + {"OpCapability VulkanMemoryModel\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityVulkanMemoryModelKHR})}, + {"OpCapability VulkanMemoryModelDeviceScope\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityVulkanMemoryModelDeviceScopeKHR})}, + {"OpMemoryModel Logical Vulkan\n", + MakeInstruction(SpvOpMemoryModel, {SpvAddressingModelLogical, + SpvMemoryModelVulkanKHR})}, + {"OpStore %1 %2 MakePointerAvailable %3\n", + MakeInstruction(SpvOpStore, + {1, 2, SpvMemoryAccessMakePointerAvailableKHRMask, + 3})}, + {"OpStore %1 %2 Volatile|MakePointerAvailable %3\n", + MakeInstruction(SpvOpStore, + {1, 2, + int(SpvMemoryAccessMakePointerAvailableKHRMask) | + int(SpvMemoryAccessVolatileMask), + 3})}, + {"OpStore %1 %2 Aligned|MakePointerAvailable 4 %3\n", + MakeInstruction(SpvOpStore, + {1, 2, + int(SpvMemoryAccessMakePointerAvailableKHRMask) | + int(SpvMemoryAccessAlignedMask), + 4, 3})}, + {"OpStore %1 %2 MakePointerAvailable|NonPrivatePointer %3\n", + MakeInstruction(SpvOpStore, + {1, 2, + int(SpvMemoryAccessMakePointerAvailableKHRMask) | + int(SpvMemoryAccessNonPrivatePointerKHRMask), + 3})}, + {"%2 = OpLoad %1 %3 MakePointerVisible %4\n", + MakeInstruction(SpvOpLoad, + {1, 2, 3, SpvMemoryAccessMakePointerVisibleKHRMask, + 4})}, + {"%2 = OpLoad %1 %3 Volatile|MakePointerVisible %4\n", + MakeInstruction(SpvOpLoad, + {1, 2, 3, + int(SpvMemoryAccessMakePointerVisibleKHRMask) | + int(SpvMemoryAccessVolatileMask), + 4})}, + {"%2 = OpLoad %1 %3 Aligned|MakePointerVisible 8 %4\n", + MakeInstruction(SpvOpLoad, + {1, 2, 3, + int(SpvMemoryAccessMakePointerVisibleKHRMask) | + int(SpvMemoryAccessAlignedMask), + 8, 4})}, + {"%2 = OpLoad %1 %3 MakePointerVisible|NonPrivatePointer " + "%4\n", + MakeInstruction(SpvOpLoad, + {1, 2, 3, + int(SpvMemoryAccessMakePointerVisibleKHRMask) | + int(SpvMemoryAccessNonPrivatePointerKHRMask), + 4})}, + {"OpCopyMemory %1 %2 " + "MakePointerAvailable|" + "MakePointerVisible|" + "NonPrivatePointer " + "%3 %4\n", + MakeInstruction(SpvOpCopyMemory, + {1, 2, + (int(SpvMemoryAccessMakePointerVisibleKHRMask) | + int(SpvMemoryAccessMakePointerAvailableKHRMask) | + int(SpvMemoryAccessNonPrivatePointerKHRMask)), + 3, 4})}, + {"OpCopyMemorySized %1 %2 %3 " + "MakePointerAvailable|" + "MakePointerVisible|" + "NonPrivatePointer " + "%4 %5\n", + MakeInstruction(SpvOpCopyMemorySized, + {1, 2, 3, + (int(SpvMemoryAccessMakePointerVisibleKHRMask) | + int(SpvMemoryAccessMakePointerAvailableKHRMask) | + int(SpvMemoryAccessNonPrivatePointerKHRMask)), + 4, 5})}, + // Image operands + {"OpImageWrite %1 %2 %3 MakeTexelAvailable " + "%4\n", + MakeInstruction( + SpvOpImageWrite, + {1, 2, 3, int(SpvImageOperandsMakeTexelAvailableKHRMask), 4})}, + {"OpImageWrite %1 %2 %3 MakeTexelAvailable|NonPrivateTexel " + "%4\n", + MakeInstruction(SpvOpImageWrite, + {1, 2, 3, + int(SpvImageOperandsMakeTexelAvailableKHRMask) | + int(SpvImageOperandsNonPrivateTexelKHRMask), + 4})}, + {"OpImageWrite %1 %2 %3 " + "MakeTexelAvailable|NonPrivateTexel|VolatileTexel " + "%4\n", + MakeInstruction(SpvOpImageWrite, + {1, 2, 3, + int(SpvImageOperandsMakeTexelAvailableKHRMask) | + int(SpvImageOperandsNonPrivateTexelKHRMask) | + int(SpvImageOperandsVolatileTexelKHRMask), + 4})}, + {"%2 = OpImageRead %1 %3 %4 MakeTexelVisible " + "%5\n", + MakeInstruction(SpvOpImageRead, + {1, 2, 3, 4, + int(SpvImageOperandsMakeTexelVisibleKHRMask), + 5})}, + {"%2 = OpImageRead %1 %3 %4 " + "MakeTexelVisible|NonPrivateTexel " + "%5\n", + MakeInstruction(SpvOpImageRead, + {1, 2, 3, 4, + int(SpvImageOperandsMakeTexelVisibleKHRMask) | + int(SpvImageOperandsNonPrivateTexelKHRMask), + 5})}, + {"%2 = OpImageRead %1 %3 %4 " + "MakeTexelVisible|NonPrivateTexel|VolatileTexel " + "%5\n", + MakeInstruction(SpvOpImageRead, + {1, 2, 3, 4, + int(SpvImageOperandsMakeTexelVisibleKHRMask) | + int(SpvImageOperandsNonPrivateTexelKHRMask) | + int(SpvImageOperandsVolatileTexelKHRMask), + 5})}, + + // Memory semantics ID values are numbers put into a SPIR-V + // constant integer referenced by Id. There is no token for + // them, and so no assembler or disassembler support required. + // Similar for Scope ID. + }))); + +// SPV_GOOGLE_decorate_string + +// Now that OpDecorateString is the preferred spelling for +// OpDecorateStringGOOGLE use that name in round trip tests, and the GOOGLE +// name in an assembly-only test. + +INSTANTIATE_TEST_SUITE_P( + SPV_GOOGLE_decorate_string, ExtensionRoundTripTest, + Combine( + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {"OpDecorateString %1 UserSemantic \"ABC\"\n", + MakeInstruction(SpvOpDecorateStringGOOGLE, + {1, SpvDecorationHlslSemanticGOOGLE}, + MakeVector("ABC"))}, + {"OpDecorateString %1 UserSemantic \"ABC\"\n", + MakeInstruction(SpvOpDecorateString, + {1, SpvDecorationUserSemantic}, + MakeVector("ABC"))}, + {"OpMemberDecorateString %1 3 UserSemantic \"DEF\"\n", + MakeInstruction(SpvOpMemberDecorateStringGOOGLE, + {1, 3, SpvDecorationUserSemantic}, + MakeVector("DEF"))}, + {"OpMemberDecorateString %1 3 UserSemantic \"DEF\"\n", + MakeInstruction(SpvOpMemberDecorateString, + {1, 3, SpvDecorationUserSemantic}, + MakeVector("DEF"))}, + }))); + +INSTANTIATE_TEST_SUITE_P( + SPV_GOOGLE_decorate_string, ExtensionAssemblyTest, + Combine( + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0), + ValuesIn(std::vector{ + {"OpDecorateStringGOOGLE %1 HlslSemanticGOOGLE \"ABC\"\n", + MakeInstruction(SpvOpDecorateStringGOOGLE, + {1, SpvDecorationHlslSemanticGOOGLE}, + MakeVector("ABC"))}, + {"OpMemberDecorateStringGOOGLE %1 3 HlslSemanticGOOGLE \"DEF\"\n", + MakeInstruction(SpvOpMemberDecorateStringGOOGLE, + {1, 3, SpvDecorationHlslSemanticGOOGLE}, + MakeVector("DEF"))}, + }))); + +// SPV_GOOGLE_hlsl_functionality1 + +// Now that CounterBuffer is the preferred spelling for HlslCounterBufferGOOGLE, +// use that name in round trip tests, and the GOOGLE name in an assembly-only +// test. +INSTANTIATE_TEST_SUITE_P( + SPV_GOOGLE_hlsl_functionality1, ExtensionRoundTripTest, + Combine( + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0), + // HlslSemanticGOOGLE is tested in SPV_GOOGLE_decorate_string, since + // they are coupled together. + ValuesIn(std::vector{ + {"OpDecorateId %1 CounterBuffer %2\n", + MakeInstruction(SpvOpDecorateId, + {1, SpvDecorationHlslCounterBufferGOOGLE, 2})}, + {"OpDecorateId %1 CounterBuffer %2\n", + MakeInstruction(SpvOpDecorateId, + {1, SpvDecorationCounterBuffer, 2})}, + }))); + +INSTANTIATE_TEST_SUITE_P( + SPV_GOOGLE_hlsl_functionality1, ExtensionAssemblyTest, + Combine( + // We'll get coverage over operand tables by trying the universal + // environments, and at least one specific environment. + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0), + // HlslSemanticGOOGLE is tested in SPV_GOOGLE_decorate_string, since + // they are coupled together. + ValuesIn(std::vector{ + {"OpDecorateId %1 HlslCounterBufferGOOGLE %2\n", + MakeInstruction(SpvOpDecorateId, + {1, SpvDecorationHlslCounterBufferGOOGLE, 2})}, + }))); + +// SPV_NV_viewport_array2 + +INSTANTIATE_TEST_SUITE_P( + SPV_NV_viewport_array2, ExtensionRoundTripTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, + SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1), + ValuesIn(std::vector{ + {"OpExtension \"SPV_NV_viewport_array2\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_NV_viewport_array2"))}, + // The EXT and NV extensions have the same token number for this + // capability. + {"OpCapability ShaderViewportIndexLayerEXT\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityShaderViewportIndexLayerNV})}, + // Check the new capability's token number + {"OpCapability ShaderViewportIndexLayerEXT\n", + MakeInstruction(SpvOpCapability, {5254})}, + // Decorations + {"OpDecorate %1 ViewportRelativeNV\n", + MakeInstruction(SpvOpDecorate, + {1, SpvDecorationViewportRelativeNV})}, + {"OpDecorate %1 BuiltIn ViewportMaskNV\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationBuiltIn, + SpvBuiltInViewportMaskNV})}, + }))); + +// SPV_NV_shader_subgroup_partitioned + +INSTANTIATE_TEST_SUITE_P( + SPV_NV_shader_subgroup_partitioned, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1), + ValuesIn(std::vector{ + {"OpExtension \"SPV_NV_shader_subgroup_partitioned\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_NV_shader_subgroup_partitioned"))}, + {"OpCapability GroupNonUniformPartitionedNV\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityGroupNonUniformPartitionedNV})}, + // Check the new capability's token number + {"OpCapability GroupNonUniformPartitionedNV\n", + MakeInstruction(SpvOpCapability, {5297})}, + {"%2 = OpGroupNonUniformPartitionNV %1 %3\n", + MakeInstruction(SpvOpGroupNonUniformPartitionNV, {1, 2, 3})}, + // Check the new instruction's token number + {"%2 = OpGroupNonUniformPartitionNV %1 %3\n", + MakeInstruction(static_cast(5296), {1, 2, 3})}, + // Check the new group operations + {"%2 = OpGroupIAdd %1 %3 PartitionedReduceNV %4\n", + MakeInstruction(SpvOpGroupIAdd, + {1, 2, 3, SpvGroupOperationPartitionedReduceNV, + 4})}, + {"%2 = OpGroupIAdd %1 %3 PartitionedReduceNV %4\n", + MakeInstruction(SpvOpGroupIAdd, {1, 2, 3, 6, 4})}, + {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanNV %4\n", + MakeInstruction(SpvOpGroupIAdd, + {1, 2, 3, + SpvGroupOperationPartitionedInclusiveScanNV, 4})}, + {"%2 = OpGroupIAdd %1 %3 PartitionedInclusiveScanNV %4\n", + MakeInstruction(SpvOpGroupIAdd, {1, 2, 3, 7, 4})}, + {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanNV %4\n", + MakeInstruction(SpvOpGroupIAdd, + {1, 2, 3, + SpvGroupOperationPartitionedExclusiveScanNV, 4})}, + {"%2 = OpGroupIAdd %1 %3 PartitionedExclusiveScanNV %4\n", + MakeInstruction(SpvOpGroupIAdd, {1, 2, 3, 8, 4})}, + }))); + +// SPV_EXT_descriptor_indexing + +INSTANTIATE_TEST_SUITE_P( + SPV_EXT_descriptor_indexing, ExtensionRoundTripTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, + SPV_ENV_VULKAN_1_1), + ValuesIn(std::vector{ + {"OpExtension \"SPV_EXT_descriptor_indexing\"\n", + MakeInstruction(SpvOpExtension, + MakeVector("SPV_EXT_descriptor_indexing"))}, + // Check capabilities, by name + {"OpCapability ShaderNonUniform\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityShaderNonUniformEXT})}, + {"OpCapability RuntimeDescriptorArray\n", + MakeInstruction(SpvOpCapability, + {SpvCapabilityRuntimeDescriptorArrayEXT})}, + {"OpCapability InputAttachmentArrayDynamicIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityInputAttachmentArrayDynamicIndexingEXT})}, + {"OpCapability UniformTexelBufferArrayDynamicIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT})}, + {"OpCapability StorageTexelBufferArrayDynamicIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT})}, + {"OpCapability UniformBufferArrayNonUniformIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityUniformBufferArrayNonUniformIndexingEXT})}, + {"OpCapability SampledImageArrayNonUniformIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilitySampledImageArrayNonUniformIndexingEXT})}, + {"OpCapability StorageBufferArrayNonUniformIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityStorageBufferArrayNonUniformIndexingEXT})}, + {"OpCapability StorageImageArrayNonUniformIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityStorageImageArrayNonUniformIndexingEXT})}, + {"OpCapability InputAttachmentArrayNonUniformIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT})}, + {"OpCapability UniformTexelBufferArrayNonUniformIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT})}, + {"OpCapability StorageTexelBufferArrayNonUniformIndexing\n", + MakeInstruction( + SpvOpCapability, + {SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT})}, + // Check capabilities, by number + {"OpCapability ShaderNonUniform\n", + MakeInstruction(SpvOpCapability, {5301})}, + {"OpCapability RuntimeDescriptorArray\n", + MakeInstruction(SpvOpCapability, {5302})}, + {"OpCapability InputAttachmentArrayDynamicIndexing\n", + MakeInstruction(SpvOpCapability, {5303})}, + {"OpCapability UniformTexelBufferArrayDynamicIndexing\n", + MakeInstruction(SpvOpCapability, {5304})}, + {"OpCapability StorageTexelBufferArrayDynamicIndexing\n", + MakeInstruction(SpvOpCapability, {5305})}, + {"OpCapability UniformBufferArrayNonUniformIndexing\n", + MakeInstruction(SpvOpCapability, {5306})}, + {"OpCapability SampledImageArrayNonUniformIndexing\n", + MakeInstruction(SpvOpCapability, {5307})}, + {"OpCapability StorageBufferArrayNonUniformIndexing\n", + MakeInstruction(SpvOpCapability, {5308})}, + {"OpCapability StorageImageArrayNonUniformIndexing\n", + MakeInstruction(SpvOpCapability, {5309})}, + {"OpCapability InputAttachmentArrayNonUniformIndexing\n", + MakeInstruction(SpvOpCapability, {5310})}, + {"OpCapability UniformTexelBufferArrayNonUniformIndexing\n", + MakeInstruction(SpvOpCapability, {5311})}, + {"OpCapability StorageTexelBufferArrayNonUniformIndexing\n", + MakeInstruction(SpvOpCapability, {5312})}, + + // Check the decoration token + {"OpDecorate %1 NonUniform\n", + MakeInstruction(SpvOpDecorate, {1, SpvDecorationNonUniformEXT})}, + {"OpDecorate %1 NonUniform\n", + MakeInstruction(SpvOpDecorate, {1, 5300})}, + }))); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.function_test.cpp b/third_party/spirv-tools/test/text_to_binary.function_test.cpp new file mode 100644 index 0000000..55a8e6c --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.function_test.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Function" section of the +// SPIR-V spec. + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::EnumCase; +using spvtest::MakeInstruction; +using spvtest::TextToBinaryTest; +using ::testing::Eq; + +// Test OpFunction + +using OpFunctionControlTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(OpFunctionControlTest, AnySingleFunctionControlMask) { + const std::string input = "%result_id = OpFunction %result_type " + + GetParam().name() + " %function_type "; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpFunction, {1, 2, GetParam().value(), 3}))); +} + +// clang-format off +#define CASE(VALUE,NAME) { SpvFunctionControl##VALUE, NAME } +INSTANTIATE_TEST_SUITE_P(TextToBinaryFunctionTest, OpFunctionControlTest, + ::testing::ValuesIn(std::vector>{ + CASE(MaskNone, "None"), + CASE(InlineMask, "Inline"), + CASE(DontInlineMask, "DontInline"), + CASE(PureMask, "Pure"), + CASE(ConstMask, "Const"), + })); +#undef CASE +// clang-format on + +TEST_F(OpFunctionControlTest, CombinedFunctionControlMask) { + // Sample a single combination. This ensures we've integrated + // the instruction parsing logic with spvTextParseMask. + const std::string input = + "%result_id = OpFunction %result_type Inline|Pure|Const %function_type"; + const uint32_t expected_mask = SpvFunctionControlInlineMask | + SpvFunctionControlPureMask | + SpvFunctionControlConstMask; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpFunction, {1, 2, expected_mask, 3}))); +} + +TEST_F(OpFunctionControlTest, WrongFunctionControl) { + EXPECT_THAT(CompileFailure("%r = OpFunction %t Inline|Unroll %ft"), + Eq("Invalid function control operand 'Inline|Unroll'.")); +} + +// TODO(dneto): OpFunctionParameter +// TODO(dneto): OpFunctionEnd +// TODO(dneto): OpFunctionCall + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.group_test.cpp b/third_party/spirv-tools/test/text_to_binary.group_test.cpp new file mode 100644 index 0000000..becc3aa --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.group_test.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Group Instrucions" section of the +// SPIR-V spec. + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::EnumCase; +using spvtest::MakeInstruction; +using ::testing::Eq; + +// Test GroupOperation enum + +using GroupOperationTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(GroupOperationTest, AnyGroupOperation) { + const std::string input = + "%result = OpGroupIAdd %type %scope " + GetParam().name() + " %x"; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpGroupIAdd, {1, 2, 3, GetParam().value(), 4}))); +} + +// clang-format off +#define CASE(NAME) { SpvGroupOperation##NAME, #NAME} +INSTANTIATE_TEST_SUITE_P(TextToBinaryGroupOperation, GroupOperationTest, + ::testing::ValuesIn(std::vector>{ + CASE(Reduce), + CASE(InclusiveScan), + CASE(ExclusiveScan), + })); +#undef CASE +// clang-format on + +TEST_F(GroupOperationTest, WrongGroupOperation) { + EXPECT_THAT(CompileFailure("%r = OpGroupUMin %t %e xxyyzz %x"), + Eq("Invalid group operation 'xxyyzz'.")); +} + +// TODO(dneto): OpGroupAsyncCopy +// TODO(dneto): OpGroupWaitEvents +// TODO(dneto): OpGroupAll +// TODO(dneto): OpGroupAny +// TODO(dneto): OpGroupBroadcast +// TODO(dneto): OpGroupIAdd +// TODO(dneto): OpGroupFAdd +// TODO(dneto): OpGroupFMin +// TODO(dneto): OpGroupUMin +// TODO(dneto): OpGroupSMin +// TODO(dneto): OpGroupFMax +// TODO(dneto): OpGroupUMax +// TODO(dneto): OpGroupSMax + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.image_test.cpp b/third_party/spirv-tools/test/text_to_binary.image_test.cpp new file mode 100644 index 0000000..d445369 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.image_test.cpp @@ -0,0 +1,276 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Image Instructions" section of +// the SPIR-V spec. + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::MakeInstruction; +using spvtest::TextToBinaryTest; +using ::testing::Eq; + +// An example case for a mask value with operands. +struct ImageOperandsCase { + std::string image_operands; + // The expected mask, followed by its operands. + std::vector expected_mask_and_operands; +}; + +// Test all kinds of image operands. + +using ImageOperandsTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(ImageOperandsTest, Sample) { + const std::string input = + "%2 = OpImageFetch %1 %3 %4" + GetParam().image_operands + "\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpImageFetch, {1, 2, 3, 4}, + GetParam().expected_mask_and_operands))); +} + +#define MASK(NAME) SpvImageOperands##NAME##Mask +INSTANTIATE_TEST_SUITE_P( + TextToBinaryImageOperandsAny, ImageOperandsTest, + ::testing::ValuesIn(std::vector{ + // TODO(dneto): Rev32 adds many more values, and rearranges their + // values. + // Image operands are optional. + {"", {}}, + // Test each kind, alone. + {" Bias %5", {MASK(Bias), 5}}, + {" Lod %5", {MASK(Lod), 5}}, + {" Grad %5 %6", {MASK(Grad), 5, 6}}, + {" ConstOffset %5", {MASK(ConstOffset), 5}}, + {" Offset %5", {MASK(Offset), 5}}, + {" ConstOffsets %5", {MASK(ConstOffsets), 5}}, + {" Sample %5", {MASK(Sample), 5}}, + {" MinLod %5", {MASK(MinLod), 5}}, + })); +#undef MASK +#define MASK(NAME) static_cast(SpvImageOperands##NAME##Mask) +INSTANTIATE_TEST_SUITE_P( + TextToBinaryImageOperandsCombination, ImageOperandsTest, + ::testing::ValuesIn(std::vector{ + // TODO(dneto): Rev32 adds many more values, and rearranges their + // values. + // Test adjacent pairs, so we can easily debug the values when it fails. + {" Bias|Lod %5 %6", {MASK(Bias) | MASK(Lod), 5, 6}}, + {" Lod|Grad %5 %6 %7", {MASK(Lod) | MASK(Grad), 5, 6, 7}}, + {" Grad|ConstOffset %5 %6 %7", + {MASK(Grad) | MASK(ConstOffset), 5, 6, 7}}, + {" ConstOffset|Offset %5 %6", {MASK(ConstOffset) | MASK(Offset), 5, 6}}, + {" Offset|ConstOffsets %5 %6", + {MASK(Offset) | MASK(ConstOffsets), 5, 6}}, + {" ConstOffsets|Sample %5 %6", + {MASK(ConstOffsets) | MASK(Sample), 5, 6}}, + // Test all masks together. + {" Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample" + " %5 %6 %7 %8 %9 %10 %11 %12", + {MASK(Bias) | MASK(Lod) | MASK(Grad) | MASK(ConstOffset) | + MASK(Offset) | MASK(ConstOffsets) | MASK(Sample), + 5, 6, 7, 8, 9, 10, 11, 12}}, + // The same, but with mask value names reversed. + {" Sample|ConstOffsets|Offset|ConstOffset|Grad|Lod|Bias" + " %5 %6 %7 %8 %9 %10 %11 %12", + {MASK(Bias) | MASK(Lod) | MASK(Grad) | MASK(ConstOffset) | + MASK(Offset) | MASK(ConstOffsets) | MASK(Sample), + 5, 6, 7, 8, 9, 10, 11, 12}}})); +#undef MASK + +TEST_F(ImageOperandsTest, WrongOperand) { + EXPECT_THAT(CompileFailure("%r = OpImageFetch %t %i %c xxyyzz"), + Eq("Invalid image operand 'xxyyzz'.")); +} + +// Test OpImage + +using OpImageTest = TextToBinaryTest; + +TEST_F(OpImageTest, Valid) { + const std::string input = "%2 = OpImage %1 %3\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpImage, {1, 2, 3}))); + + // Test the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input); +} + +TEST_F(OpImageTest, InvalidTypeOperand) { + EXPECT_THAT(CompileFailure("%2 = OpImage 42"), + Eq("Expected id to start with %.")); +} + +TEST_F(OpImageTest, MissingSampledImageOperand) { + EXPECT_THAT(CompileFailure("%2 = OpImage %1"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(OpImageTest, InvalidSampledImageOperand) { + EXPECT_THAT(CompileFailure("%2 = OpImage %1 1000"), + Eq("Expected id to start with %.")); +} + +TEST_F(OpImageTest, TooManyOperands) { + // We should improve this message, to say what instruction we're trying to + // parse. + EXPECT_THAT(CompileFailure("%2 = OpImage %1 %3 %4"), // an Id + Eq("Expected '=', found end of stream.")); + + EXPECT_THAT(CompileFailure("%2 = OpImage %1 %3 99"), // a number + Eq("Expected or at the beginning of an " + "instruction, found '99'.")); + EXPECT_THAT(CompileFailure("%2 = OpImage %1 %3 \"abc\""), // a string + Eq("Expected or at the beginning of an " + "instruction, found '\"abc\"'.")); +} + +// Test OpImageSparseRead + +using OpImageSparseReadTest = TextToBinaryTest; + +TEST_F(OpImageSparseReadTest, OnlyRequiredOperands) { + const std::string input = "%2 = OpImageSparseRead %1 %3 %4\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpImageSparseRead, {1, 2, 3, 4}))); + // Test the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input); +} + +// Test all kinds of image operands on OpImageSparseRead + +using ImageSparseReadImageOperandsTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(ImageSparseReadImageOperandsTest, Sample) { + const std::string input = + "%2 = OpImageSparseRead %1 %3 %4" + GetParam().image_operands + "\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpImageSparseRead, {1, 2, 3, 4}, + GetParam().expected_mask_and_operands))); + // Test the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input); +} + +#define MASK(NAME) SpvImageOperands##NAME##Mask +INSTANTIATE_TEST_SUITE_P(ImageSparseReadImageOperandsAny, + ImageSparseReadImageOperandsTest, + ::testing::ValuesIn(std::vector{ + // Image operands are optional. + {"", {}}, + // Test each kind, alone. + {" Bias %5", {MASK(Bias), 5}}, + {" Lod %5", {MASK(Lod), 5}}, + {" Grad %5 %6", {MASK(Grad), 5, 6}}, + {" ConstOffset %5", {MASK(ConstOffset), 5}}, + {" Offset %5", {MASK(Offset), 5}}, + {" ConstOffsets %5", {MASK(ConstOffsets), 5}}, + {" Sample %5", {MASK(Sample), 5}}, + {" MinLod %5", {MASK(MinLod), 5}}, + })); +#undef MASK +#define MASK(NAME) static_cast(SpvImageOperands##NAME##Mask) +INSTANTIATE_TEST_SUITE_P( + ImageSparseReadImageOperandsCombination, ImageSparseReadImageOperandsTest, + ::testing::ValuesIn(std::vector{ + // values. + // Test adjacent pairs, so we can easily debug the values when it fails. + {" Bias|Lod %5 %6", {MASK(Bias) | MASK(Lod), 5, 6}}, + {" Lod|Grad %5 %6 %7", {MASK(Lod) | MASK(Grad), 5, 6, 7}}, + {" Grad|ConstOffset %5 %6 %7", + {MASK(Grad) | MASK(ConstOffset), 5, 6, 7}}, + {" ConstOffset|Offset %5 %6", {MASK(ConstOffset) | MASK(Offset), 5, 6}}, + {" Offset|ConstOffsets %5 %6", + {MASK(Offset) | MASK(ConstOffsets), 5, 6}}, + {" ConstOffsets|Sample %5 %6", + {MASK(ConstOffsets) | MASK(Sample), 5, 6}}, + // Test all masks together. + {" Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample" + " %5 %6 %7 %8 %9 %10 %11 %12", + {MASK(Bias) | MASK(Lod) | MASK(Grad) | MASK(ConstOffset) | + MASK(Offset) | MASK(ConstOffsets) | MASK(Sample), + 5, 6, 7, 8, 9, 10, 11, 12}}, + // Don't try the masks reversed, since this is a round trip test, + // and the disassembler will sort them. + })); +#undef MASK + +TEST_F(OpImageSparseReadTest, InvalidTypeOperand) { + EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead 42"), + Eq("Expected id to start with %.")); +} + +TEST_F(OpImageSparseReadTest, MissingImageOperand) { + EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(OpImageSparseReadTest, InvalidImageOperand) { + EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1 1000"), + Eq("Expected id to start with %.")); +} + +TEST_F(OpImageSparseReadTest, MissingCoordinateOperand) { + EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1 %2"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(OpImageSparseReadTest, InvalidCoordinateOperand) { + EXPECT_THAT(CompileFailure("%2 = OpImageSparseRead %1 %2 1000"), + Eq("Expected id to start with %.")); +} + +// TODO(dneto): OpSampledImage +// TODO(dneto): OpImageSampleImplicitLod +// TODO(dneto): OpImageSampleExplicitLod +// TODO(dneto): OpImageSampleDrefImplicitLod +// TODO(dneto): OpImageSampleDrefExplicitLod +// TODO(dneto): OpImageSampleProjImplicitLod +// TODO(dneto): OpImageSampleProjExplicitLod +// TODO(dneto): OpImageSampleProjDrefImplicitLod +// TODO(dneto): OpImageSampleProjDrefExplicitLod +// TODO(dneto): OpImageGather +// TODO(dneto): OpImageDrefGather +// TODO(dneto): OpImageRead +// TODO(dneto): OpImageWrite +// TODO(dneto): OpImageQueryFormat +// TODO(dneto): OpImageQueryOrder +// TODO(dneto): OpImageQuerySizeLod +// TODO(dneto): OpImageQuerySize +// TODO(dneto): OpImageQueryLod +// TODO(dneto): OpImageQueryLevels +// TODO(dneto): OpImageQuerySamples +// TODO(dneto): OpImageSparseSampleImplicitLod +// TODO(dneto): OpImageSparseSampleExplicitLod +// TODO(dneto): OpImageSparseSampleDrefImplicitLod +// TODO(dneto): OpImageSparseSampleDrefExplicitLod +// TODO(dneto): OpImageSparseSampleProjImplicitLod +// TODO(dneto): OpImageSparseSampleProjExplicitLod +// TODO(dneto): OpImageSparseSampleProjDrefImplicitLod +// TODO(dneto): OpImageSparseSampleProjDrefExplicitLod +// TODO(dneto): OpImageSparseFetch +// TODO(dneto): OpImageSparseDrefGather +// TODO(dneto): OpImageSparseTexelsResident + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.literal_test.cpp b/third_party/spirv-tools/test/text_to_binary.literal_test.cpp new file mode 100644 index 0000000..bcbb63e --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.literal_test.cpp @@ -0,0 +1,125 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for literal numbers and literal strings. + +#include + +#include "test/test_fixture.h" + +namespace spvtools { +namespace { + +using spvtest::TextToBinaryTest; + +TEST_F(TextToBinaryTest, LiteralStringInPlaceOfLiteralNumber) { + EXPECT_EQ( + R"(Invalid unsigned integer literal: "I shouldn't be a string")", + CompileFailure(R"(OpSource GLSL "I shouldn't be a string")")); +} + +TEST_F(TextToBinaryTest, GarbageInPlaceOfLiteralString) { + EXPECT_EQ("Invalid literal string 'nice-source-code'.", + CompileFailure("OpSourceExtension nice-source-code")); +} + +TEST_F(TextToBinaryTest, LiteralNumberInPlaceOfLiteralString) { + EXPECT_EQ("Expected literal string, found literal number '1000'.", + CompileFailure("OpSourceExtension 1000")); +} + +TEST_F(TextToBinaryTest, LiteralFloatInPlaceOfLiteralInteger) { + EXPECT_EQ("Invalid unsigned integer literal: 10.5", + CompileFailure("OpSource GLSL 10.5")); + + EXPECT_EQ("Invalid unsigned integer literal: 0.2", + CompileFailure(R"(OpMemberName %type 0.2 "member0.2")")); + + EXPECT_EQ("Invalid unsigned integer literal: 32.42", + CompileFailure("%int = OpTypeInt 32.42 0")); + + EXPECT_EQ("Invalid unsigned integer literal: 4.5", + CompileFailure("%mat = OpTypeMatrix %vec 4.5")); + + EXPECT_EQ("Invalid unsigned integer literal: 1.5", + CompileFailure("OpExecutionMode %main LocalSize 1.5 1.6 1.7")); + + EXPECT_EQ("Invalid unsigned integer literal: 0.123", + CompileFailure("%i32 = OpTypeInt 32 1\n" + "%c = OpConstant %i32 0.123")); +} + +TEST_F(TextToBinaryTest, LiteralInt64) { + const std::string code = + "%1 = OpTypeInt 64 0\n%2 = OpConstant %1 123456789021\n"; + EXPECT_EQ(code, EncodeAndDecodeSuccessfully(code)); +} + +TEST_F(TextToBinaryTest, LiteralDouble) { + const std::string code = + "%1 = OpTypeFloat 64\n%2 = OpSpecConstant %1 3.14159265358979\n"; + EXPECT_EQ(code, EncodeAndDecodeSuccessfully(code)); +} + +TEST_F(TextToBinaryTest, LiteralStringASCIILong) { + // SPIR-V allows strings up to 65535 characters. + // Test the simple case of UTF-8 code points corresponding + // to ASCII characters. + EXPECT_EQ(65535, SPV_LIMIT_LITERAL_STRING_UTF8_CHARS_MAX); + const std::string code = + "OpSourceExtension \"" + + std::string(SPV_LIMIT_LITERAL_STRING_UTF8_CHARS_MAX, 'o') + "\"\n"; + EXPECT_EQ(code, EncodeAndDecodeSuccessfully(code)); +} + +TEST_F(TextToBinaryTest, LiteralStringUTF8LongEncodings) { + // SPIR-V allows strings up to 65535 characters. + // Test the case of many Unicode characters, each of which has + // a 4-byte UTF-8 encoding. + + // An instruction is at most 65535 words long. The first one + // contains the wordcount and opcode. So the worst case number of + // 4-byte UTF-8 characters is 65533, since we also need to + // store a terminating null character. + + // This string fits exactly into 65534 words. + const std::string good_string = + spvtest::MakeLongUTF8String(65533) + // The following single character has a 3 byte encoding, + // which fits snugly against the terminating null. + + "\xe8\x80\x80"; + + // These strings will overflow any instruction with 0 or 1 other + // arguments, respectively. + const std::string bad_0_arg_string = spvtest::MakeLongUTF8String(65534); + const std::string bad_1_arg_string = spvtest::MakeLongUTF8String(65533); + + const std::string good_code = "OpSourceExtension \"" + good_string + "\"\n"; + EXPECT_EQ(good_code, EncodeAndDecodeSuccessfully(good_code)); + + // Prove that it works on more than one instruction. + const std::string good_code_2 = "OpSourceContinued \"" + good_string + "\"\n"; + EXPECT_EQ(good_code, EncodeAndDecodeSuccessfully(good_code)); + + // Failure cases. + EXPECT_EQ("Instruction too long: more than 65535 words.", + CompileFailure("OpSourceExtension \"" + bad_0_arg_string + "\"\n")); + EXPECT_EQ("Instruction too long: more than 65535 words.", + CompileFailure("OpSourceContinued \"" + bad_0_arg_string + "\"\n")); + EXPECT_EQ("Instruction too long: more than 65535 words.", + CompileFailure("OpName %target \"" + bad_1_arg_string + "\"\n")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.memory_test.cpp b/third_party/spirv-tools/test/text_to_binary.memory_test.cpp new file mode 100644 index 0000000..7b09ed5 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.memory_test.cpp @@ -0,0 +1,426 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Memory Instructions" section of +// the SPIR-V spec. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::EnumCase; +using spvtest::MakeInstruction; +using spvtest::TextToBinaryTest; +using ::testing::Eq; +using ::testing::HasSubstr; + +// Test assembly of Memory Access masks + +using MemoryAccessTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(MemoryAccessTest, AnySingleMemoryAccessMask) { + std::stringstream input; + input << "OpStore %ptr %value " << GetParam().name(); + for (auto operand : GetParam().operands()) input << " " << operand; + EXPECT_THAT(CompiledInstructions(input.str()), + Eq(MakeInstruction(SpvOpStore, {1, 2, GetParam().value()}, + GetParam().operands()))); +} + +INSTANTIATE_TEST_SUITE_P( + TextToBinaryMemoryAccessTest, MemoryAccessTest, + ::testing::ValuesIn(std::vector>{ + {SpvMemoryAccessMaskNone, "None", {}}, + {SpvMemoryAccessVolatileMask, "Volatile", {}}, + {SpvMemoryAccessAlignedMask, "Aligned", {16}}, + {SpvMemoryAccessNontemporalMask, "Nontemporal", {}}, + })); + +TEST_F(TextToBinaryTest, CombinedMemoryAccessMask) { + const std::string input = "OpStore %ptr %value Volatile|Aligned 16"; + const uint32_t expected_mask = + SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask; + EXPECT_THAT(expected_mask, Eq(3u)); + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpStore, {1, 2, expected_mask, 16}))); +} + +// Test Storage Class enum values + +using StorageClassTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(StorageClassTest, AnyStorageClass) { + const std::string input = "%1 = OpVariable %2 " + GetParam().name(); + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpVariable, {1, 2, GetParam().value()}))); +} + +// clang-format off +#define CASE(NAME) { SpvStorageClass##NAME, #NAME, {} } +INSTANTIATE_TEST_SUITE_P( + TextToBinaryStorageClassTest, StorageClassTest, + ::testing::ValuesIn(std::vector>{ + CASE(UniformConstant), + CASE(Input), + CASE(Uniform), + CASE(Output), + CASE(Workgroup), + CASE(CrossWorkgroup), + CASE(Private), + CASE(Function), + CASE(Generic), + CASE(PushConstant), + CASE(AtomicCounter), + CASE(Image), + })); +#undef CASE +// clang-format on + +using MemoryRoundTripTest = RoundTripTest; + +// OpPtrEqual appeared in SPIR-V 1.4 + +TEST_F(MemoryRoundTripTest, OpPtrEqualGood) { + std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n"; + EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), + Eq(MakeInstruction(SpvOpPtrEqual, {1, 2, 3, 4}))); + std::string disassembly = EncodeAndDecodeSuccessfully( + spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpPtrEqualV13Bad) { + std::string spirv = "%2 = OpPtrEqual %1 %3 %4\n"; + std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrEqual'")); +} + +// OpPtrNotEqual appeared in SPIR-V 1.4 + +TEST_F(MemoryRoundTripTest, OpPtrNotEqualGood) { + std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n"; + EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), + Eq(MakeInstruction(SpvOpPtrNotEqual, {1, 2, 3, 4}))); + std::string disassembly = EncodeAndDecodeSuccessfully( + spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpPtrNotEqualV13Bad) { + std::string spirv = "%2 = OpPtrNotEqual %1 %3 %4\n"; + std::string err = CompileFailure(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_THAT(err, HasSubstr("Invalid Opcode name 'OpPtrNotEqual'")); +} + +// OpPtrDiff appeared in SPIR-V 1.4 + +TEST_F(MemoryRoundTripTest, OpPtrDiffGood) { + std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n"; + EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), + Eq(MakeInstruction(SpvOpPtrDiff, {1, 2, 3, 4}))); + std::string disassembly = EncodeAndDecodeSuccessfully( + spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpPtrDiffV13Good) { + // OpPtrDiff is enabled by a capability as well, so we can assemble + // it even in older SPIR-V environments. We do that so we can + // write tests. + std::string spirv = "%2 = OpPtrDiff %1 %3 %4\n"; + std::string disassembly = EncodeAndDecodeSuccessfully( + spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_4); +} + +// OpCopyMemory + +TEST_F(MemoryRoundTripTest, OpCopyMemoryNoMemAccessGood) { + std::string spirv = "OpCopyMemory %1 %2\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryTooFewArgsBad) { + std::string spirv = "OpCopyMemory %1\n"; + std::string err = CompileFailure(spirv); + EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream")); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryTooManyArgsBad) { + std::string spirv = "OpCopyMemory %1 %2 %3\n"; + std::string err = CompileFailure(spirv); + EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%3'")); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNoneGood) { + std::string spirv = "OpCopyMemory %1 %2 None\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 0}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVolatileGood) { + std::string spirv = "OpCopyMemory %1 %2 Volatile\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAligned8Good) { + std::string spirv = "OpCopyMemory %1 %2 Aligned 8\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 2, 8}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNontemporalGood) { + std::string spirv = "OpCopyMemory %1 %2 Nontemporal\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 4}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessAvGood) { + std::string spirv = "OpCopyMemory %1 %2 MakePointerAvailable %3\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 8, 3}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessVisGood) { + std::string spirv = "OpCopyMemory %1 %2 MakePointerVisible %3\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 16, 3}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessNonPrivateGood) { + std::string spirv = "OpCopyMemory %1 %2 NonPrivatePointer\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 32}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryAccessMixedGood) { + std::string spirv = + "OpCopyMemory %1 %2 " + "Volatile|Aligned|Nontemporal|MakePointerAvailable|" + "MakePointerVisible|NonPrivatePointer 16 %3 %4\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 63, 16, 3, 4}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV13Good) { + std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n"; + // Note: This will assemble but should not validate for SPIR-V 1.3 + EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1, 1}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessV14Good) { + std::string spirv = "OpCopyMemory %1 %2 Volatile Volatile\n"; + EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 1, 1}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemoryTwoAccessMixedV14Good) { + std::string spirv = + "OpCopyMemory %1 %2 Volatile|Nontemporal|" + "MakePointerVisible %3 " + "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemory, {1, 2, 21, 3, 42, 16, 4}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +// OpCopyMemorySized + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedNoMemAccessGood) { + std::string spirv = "OpCopyMemorySized %1 %2 %3\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooFewArgsBad) { + std::string spirv = "OpCopyMemorySized %1 %2\n"; + std::string err = CompileFailure(spirv); + EXPECT_THAT(err, HasSubstr("Expected operand, found end of stream")); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTooManyArgsBad) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 %4\n"; + std::string err = CompileFailure(spirv); + EXPECT_THAT(err, HasSubstr("Invalid memory access operand '%4'")); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNoneGood) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 None\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 0}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVolatileGood) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAligned8Good) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 Aligned 8\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 2, 8}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNontemporalGood) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 Nontemporal\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 4}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessAvGood) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerAvailable %4\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 8, 4}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessVisGood) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 MakePointerVisible %4\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 16, 4}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessNonPrivateGood) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 NonPrivatePointer\n"; + EXPECT_THAT(CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 32}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedAccessMixedGood) { + std::string spirv = + "OpCopyMemorySized %1 %2 %3 " + "Volatile|Aligned|Nontemporal|MakePointerAvailable|" + "MakePointerVisible|NonPrivatePointer 16 %4 %5\n"; + EXPECT_THAT( + CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 63, 16, 4, 5}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV13Good) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n"; + // Note: This will assemble but should not validate for SPIR-V 1.3 + EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_3), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1, 1}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessV14Good) { + std::string spirv = "OpCopyMemorySized %1 %2 %3 Volatile Volatile\n"; + EXPECT_THAT(CompiledInstructions(spirv, SPV_ENV_UNIVERSAL_1_4), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 1, 1}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(MemoryRoundTripTest, OpCopyMemorySizedTwoAccessMixedV14Good) { + std::string spirv = + "OpCopyMemorySized %1 %2 %3 Volatile|Nontemporal|" + "MakePointerVisible %4 " + "Aligned|MakePointerAvailable|NonPrivatePointer 16 %5\n"; + EXPECT_THAT( + CompiledInstructions(spirv), + Eq(MakeInstruction(SpvOpCopyMemorySized, {1, 2, 3, 21, 4, 42, 16, 5}))); + std::string disassembly = + EncodeAndDecodeSuccessfully(spirv, SPV_BINARY_TO_TEXT_OPTION_NONE); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +// TODO(dneto): OpVariable with initializers +// TODO(dneto): OpImageTexelPointer +// TODO(dneto): OpLoad +// TODO(dneto): OpStore +// TODO(dneto): OpAccessChain +// TODO(dneto): OpInBoundsAccessChain +// TODO(dneto): OpPtrAccessChain +// TODO(dneto): OpArrayLength +// TODO(dneto): OpGenercPtrMemSemantics + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.misc_test.cpp b/third_party/spirv-tools/test/text_to_binary.misc_test.cpp new file mode 100644 index 0000000..03b1e09 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.misc_test.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Miscellaneous" section of the +// SPIR-V spec. + +#include "test/unit_spirv.h" + +#include "gmock/gmock.h" +#include "test/test_fixture.h" + +namespace spvtools { +namespace { + +using SpirvVector = spvtest::TextToBinaryTest::SpirvVector; +using spvtest::MakeInstruction; +using ::testing::Eq; +using TextToBinaryMisc = spvtest::TextToBinaryTest; + +TEST_F(TextToBinaryMisc, OpNop) { + EXPECT_THAT(CompiledInstructions("OpNop"), Eq(MakeInstruction(SpvOpNop, {}))); +} + +TEST_F(TextToBinaryMisc, OpUndef) { + const SpirvVector code = CompiledInstructions(R"(%f32 = OpTypeFloat 32 + %u = OpUndef %f32)"); + const uint32_t typeID = 1; + EXPECT_THAT(code[1], Eq(typeID)); + EXPECT_THAT(Subvector(code, 3), Eq(MakeInstruction(SpvOpUndef, {typeID, 2}))); +} + +TEST_F(TextToBinaryMisc, OpWrong) { + EXPECT_THAT(CompileFailure(" OpWrong %1 %2"), + Eq("Invalid Opcode name 'OpWrong'")); +} + +TEST_F(TextToBinaryMisc, OpWrongAfterRight) { + const auto assembly = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpXYZ +)"; + EXPECT_THAT(CompileFailure(assembly), Eq("Invalid Opcode name 'OpXYZ'")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.mode_setting_test.cpp b/third_party/spirv-tools/test/text_to_binary.mode_setting_test.cpp new file mode 100644 index 0000000..8ddf421 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.mode_setting_test.cpp @@ -0,0 +1,303 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Mode-Setting" section of the +// SPIR-V spec. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::EnumCase; +using spvtest::MakeInstruction; +using utils::MakeVector; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::TestWithParam; +using ::testing::Values; +using ::testing::ValuesIn; + +// Test OpMemoryModel + +// An example case for OpMemoryModel +struct MemoryModelCase { + uint32_t get_addressing_value() const { + return static_cast(addressing_value); + } + uint32_t get_memory_value() const { + return static_cast(memory_value); + } + SpvAddressingModel addressing_value; + std::string addressing_name; + SpvMemoryModel memory_value; + std::string memory_name; +}; + +using OpMemoryModelTest = + spvtest::TextToBinaryTestBase>; + +TEST_P(OpMemoryModelTest, AnyMemoryModelCase) { + const std::string input = "OpMemoryModel " + GetParam().addressing_name + + " " + GetParam().memory_name; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpMemoryModel, {GetParam().get_addressing_value(), + GetParam().get_memory_value()}))); +} + +#define CASE(ADDRESSING, MEMORY) \ + { \ + SpvAddressingModel##ADDRESSING, #ADDRESSING, SpvMemoryModel##MEMORY, \ + #MEMORY \ + } +// clang-format off +INSTANTIATE_TEST_SUITE_P(TextToBinaryMemoryModel, OpMemoryModelTest, + ValuesIn(std::vector{ + // These cases exercise each addressing model, and + // each memory model, but not necessarily in + // combination. + CASE(Logical,Simple), + CASE(Logical,GLSL450), + CASE(Physical32,OpenCL), + CASE(Physical64,OpenCL), + })); +#undef CASE +// clang-format on + +TEST_F(OpMemoryModelTest, WrongModel) { + EXPECT_THAT(CompileFailure("OpMemoryModel xxyyzz Simple"), + Eq("Invalid addressing model 'xxyyzz'.")); + EXPECT_THAT(CompileFailure("OpMemoryModel Logical xxyyzz"), + Eq("Invalid memory model 'xxyyzz'.")); +} + +// Test OpEntryPoint + +// An example case for OpEntryPoint +struct EntryPointCase { + uint32_t get_execution_value() const { + return static_cast(execution_value); + } + SpvExecutionModel execution_value; + std::string execution_name; + std::string entry_point_name; +}; + +using OpEntryPointTest = + spvtest::TextToBinaryTestBase>; + +TEST_P(OpEntryPointTest, AnyEntryPointCase) { + // TODO(dneto): utf-8, escaping, quoting cases for entry point name. + const std::string input = "OpEntryPoint " + GetParam().execution_name + + " %1 \"" + GetParam().entry_point_name + "\""; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpEntryPoint, {GetParam().get_execution_value(), 1}, + MakeVector(GetParam().entry_point_name)))); +} + +// clang-format off +#define CASE(NAME) SpvExecutionModel##NAME, #NAME +INSTANTIATE_TEST_SUITE_P(TextToBinaryEntryPoint, OpEntryPointTest, + ValuesIn(std::vector{ + { CASE(Vertex), "" }, + { CASE(TessellationControl), "my tess" }, + { CASE(TessellationEvaluation), "really fancy" }, + { CASE(Geometry), "Euclid" }, + { CASE(Fragment), "FAT32" }, + { CASE(GLCompute), "cubic" }, + { CASE(Kernel), "Sanders" }, + })); +#undef CASE +// clang-format on + +TEST_F(OpEntryPointTest, WrongModel) { + EXPECT_THAT(CompileFailure("OpEntryPoint xxyyzz %1 \"fun\""), + Eq("Invalid execution model 'xxyyzz'.")); +} + +// Test OpExecutionMode +using OpExecutionModeTest = spvtest::TextToBinaryTestBase< + TestWithParam>>>; + +TEST_P(OpExecutionModeTest, AnyExecutionMode) { + // This string should assemble, but should not validate. + std::stringstream input; + input << "OpExecutionMode %1 " << std::get<1>(GetParam()).name(); + for (auto operand : std::get<1>(GetParam()).operands()) + input << " " << operand; + EXPECT_THAT(CompiledInstructions(input.str(), std::get<0>(GetParam())), + Eq(MakeInstruction(SpvOpExecutionMode, + {1, std::get<1>(GetParam()).value()}, + std::get<1>(GetParam()).operands()))); +} + +#define CASE(NAME) SpvExecutionMode##NAME, #NAME +INSTANTIATE_TEST_SUITE_P( + TextToBinaryExecutionMode, OpExecutionModeTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector>{ + // The operand literal values are arbitrarily chosen, + // but there are the right number of them. + {CASE(Invocations), {101}}, + {CASE(SpacingEqual), {}}, + {CASE(SpacingFractionalEven), {}}, + {CASE(SpacingFractionalOdd), {}}, + {CASE(VertexOrderCw), {}}, + {CASE(VertexOrderCcw), {}}, + {CASE(PixelCenterInteger), {}}, + {CASE(OriginUpperLeft), {}}, + {CASE(OriginLowerLeft), {}}, + {CASE(EarlyFragmentTests), {}}, + {CASE(PointMode), {}}, + {CASE(Xfb), {}}, + {CASE(DepthReplacing), {}}, + {CASE(DepthGreater), {}}, + {CASE(DepthLess), {}}, + {CASE(DepthUnchanged), {}}, + {CASE(LocalSize), {64, 1, 2}}, + {CASE(LocalSizeHint), {8, 2, 4}}, + {CASE(InputPoints), {}}, + {CASE(InputLines), {}}, + {CASE(InputLinesAdjacency), {}}, + {CASE(Triangles), {}}, + {CASE(InputTrianglesAdjacency), {}}, + {CASE(Quads), {}}, + {CASE(Isolines), {}}, + {CASE(OutputVertices), {21}}, + {CASE(OutputPoints), {}}, + {CASE(OutputLineStrip), {}}, + {CASE(OutputTriangleStrip), {}}, + {CASE(VecTypeHint), {96}}, + {CASE(ContractionOff), {}}, + }))); + +INSTANTIATE_TEST_SUITE_P( + TextToBinaryExecutionModeV11, OpExecutionModeTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector>{ + {CASE(Initializer)}, + {CASE(Finalizer)}, + {CASE(SubgroupSize), {12}}, + {CASE(SubgroupsPerWorkgroup), {64}}}))); +#undef CASE + +TEST_F(OpExecutionModeTest, WrongMode) { + EXPECT_THAT(CompileFailure("OpExecutionMode %1 xxyyzz"), + Eq("Invalid execution mode 'xxyyzz'.")); +} + +TEST_F(OpExecutionModeTest, TooManyModes) { + EXPECT_THAT(CompileFailure("OpExecutionMode %1 Xfb PointMode"), + Eq("Expected or at the beginning of an " + "instruction, found 'PointMode'.")); +} + +// Test OpCapability + +using OpCapabilityTest = + spvtest::TextToBinaryTestBase>>; + +TEST_P(OpCapabilityTest, AnyCapability) { + const std::string input = "OpCapability " + GetParam().name(); + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpCapability, {GetParam().value()}))); +} + +// clang-format off +#define CASE(NAME) { SpvCapability##NAME, #NAME } +INSTANTIATE_TEST_SUITE_P(TextToBinaryCapability, OpCapabilityTest, + ValuesIn(std::vector>{ + CASE(Matrix), + CASE(Shader), + CASE(Geometry), + CASE(Tessellation), + CASE(Addresses), + CASE(Linkage), + CASE(Kernel), + CASE(Vector16), + CASE(Float16Buffer), + CASE(Float16), + CASE(Float64), + CASE(Int64), + CASE(Int64Atomics), + CASE(ImageBasic), + CASE(ImageReadWrite), + CASE(ImageMipmap), + // Value 16 intentionally missing + CASE(Pipes), + CASE(Groups), + CASE(DeviceEnqueue), + CASE(LiteralSampler), + CASE(AtomicStorage), + CASE(Int16), + CASE(TessellationPointSize), + CASE(GeometryPointSize), + CASE(ImageGatherExtended), + // Value 26 intentionally missing + CASE(StorageImageMultisample), + CASE(UniformBufferArrayDynamicIndexing), + CASE(SampledImageArrayDynamicIndexing), + CASE(StorageBufferArrayDynamicIndexing), + CASE(StorageImageArrayDynamicIndexing), + CASE(ClipDistance), + CASE(CullDistance), + CASE(ImageCubeArray), + CASE(SampleRateShading), + CASE(ImageRect), + CASE(SampledRect), + CASE(GenericPointer), + CASE(Int8), + CASE(InputAttachment), + CASE(SparseResidency), + CASE(MinLod), + CASE(Sampled1D), + CASE(Image1D), + CASE(SampledCubeArray), + CASE(SampledBuffer), + CASE(ImageBuffer), + CASE(ImageMSArray), + CASE(StorageImageExtendedFormats), + CASE(ImageQuery), + CASE(DerivativeControl), + CASE(InterpolationFunction), + CASE(TransformFeedback), + })); +#undef CASE +// clang-format on + +using TextToBinaryCapability = spvtest::TextToBinaryTest; + +TEST_F(TextToBinaryCapability, BadMissingCapability) { + EXPECT_THAT(CompileFailure("OpCapability"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(TextToBinaryCapability, BadInvalidCapability) { + EXPECT_THAT(CompileFailure("OpCapability 123"), + Eq("Invalid capability '123'.")); +} + +// TODO(dneto): OpExecutionMode + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.pipe_storage_test.cpp b/third_party/spirv-tools/test/text_to_binary.pipe_storage_test.cpp new file mode 100644 index 0000000..f74dbcf --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.pipe_storage_test.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "test/test_fixture.h" + +namespace spvtools { +namespace { + +using ::spvtest::MakeInstruction; +using ::testing::Eq; + +using OpTypePipeStorageTest = spvtest::TextToBinaryTest; + +// It can assemble, but should not validate. Validation checks for version +// and capability are in another test file. +TEST_F(OpTypePipeStorageTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%res = OpTypePipeStorage", SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpTypePipeStorage, {1}))); +} + +TEST_F(OpTypePipeStorageTest, ArgumentCount) { + EXPECT_THAT( + CompileFailure("OpTypePipeStorage", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected at the beginning of an instruction, found " + "'OpTypePipeStorage'.")); + EXPECT_THAT( + CompiledInstructions("%res = OpTypePipeStorage", SPV_ENV_UNIVERSAL_1_1), + Eq(MakeInstruction(SpvOpTypePipeStorage, {1}))); + EXPECT_THAT(CompileFailure("%res = OpTypePipeStorage %1 %2 %3 %4 %5", + SPV_ENV_UNIVERSAL_1_1), + Eq("'=' expected after result id.")); +} + +using OpConstantPipeStorageTest = spvtest::TextToBinaryTest; + +TEST_F(OpConstantPipeStorageTest, OpcodeAssemblesInV10) { + EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5", + SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5}))); +} + +TEST_F(OpConstantPipeStorageTest, ArgumentCount) { + EXPECT_THAT( + CompileFailure("OpConstantPipeStorage", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected at the beginning of an instruction, found " + "'OpConstantPipeStorage'.")); + EXPECT_THAT( + CompileFailure("%1 = OpConstantPipeStorage", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT(CompiledInstructions("%1 = OpConstantPipeStorage %2 3 4 5", + SPV_ENV_UNIVERSAL_1_1), + Eq(MakeInstruction(SpvOpConstantPipeStorage, {1, 2, 3, 4, 5}))); + EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4 5 %6 %7", + SPV_ENV_UNIVERSAL_1_1), + Eq("'=' expected after result id.")); +} + +TEST_F(OpConstantPipeStorageTest, ArgumentTypes) { + EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 %3 4 5", + SPV_ENV_UNIVERSAL_1_1), + Eq("Invalid unsigned integer literal: %3")); + EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 %4 5", + SPV_ENV_UNIVERSAL_1_1), + Eq("Invalid unsigned integer literal: %4")); + EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage 2 3 4 5", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); + EXPECT_THAT(CompileFailure("%1 = OpConstantPipeStorage %2 3 4 \"ab\"", + SPV_ENV_UNIVERSAL_1_1), + Eq("Invalid unsigned integer literal: \"ab\"")); +} + +using OpCreatePipeFromPipeStorageTest = spvtest::TextToBinaryTest; + +TEST_F(OpCreatePipeFromPipeStorageTest, OpcodeAssemblesInV10) { + EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3", + SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3}))); +} + +TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentCount) { + EXPECT_THAT( + CompileFailure("OpCreatePipeFromPipeStorage", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected at the beginning of an instruction, found " + "'OpCreatePipeFromPipeStorage'.")); + EXPECT_THAT( + CompileFailure("%1 = OpCreatePipeFromPipeStorage", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 OpNop", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found next instruction instead.")); + EXPECT_THAT(CompiledInstructions("%1 = OpCreatePipeFromPipeStorage %2 %3", + SPV_ENV_UNIVERSAL_1_1), + Eq(MakeInstruction(SpvOpCreatePipeFromPipeStorage, {1, 2, 3}))); + EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 %3 %4 %5", + SPV_ENV_UNIVERSAL_1_1), + Eq("'=' expected after result id.")); +} + +TEST_F(OpCreatePipeFromPipeStorageTest, ArgumentTypes) { + EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage \"\" %3", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); + EXPECT_THAT(CompileFailure("%1 = OpCreatePipeFromPipeStorage %2 3", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.reserved_sampling_test.cpp b/third_party/spirv-tools/test/text_to_binary.reserved_sampling_test.cpp new file mode 100644 index 0000000..42e4e2a --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.reserved_sampling_test.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for illegal instructions + +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::spvtest::MakeInstruction; +using ::testing::Eq; + +using ReservedSamplingInstTest = RoundTripTest; + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjImplicitLod) { + std::string input = "%2 = OpImageSparseSampleProjImplicitLod %1 %3 %4\n"; + EXPECT_THAT( + CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpImageSparseSampleProjImplicitLod, {1, 2, 3, 4}))); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjExplicitLod) { + std::string input = + "%2 = OpImageSparseSampleProjExplicitLod %1 %3 %4 Lod %5\n"; + EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpImageSparseSampleProjExplicitLod, + {1, 2, 3, 4, SpvImageOperandsLodMask, 5}))); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefImplicitLod) { + std::string input = + "%2 = OpImageSparseSampleProjDrefImplicitLod %1 %3 %4 %5\n"; + EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpImageSparseSampleProjDrefImplicitLod, + {1, 2, 3, 4, 5}))); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefExplicitLod) { + std::string input = + "%2 = OpImageSparseSampleProjDrefExplicitLod %1 %3 %4 %5 Lod %6\n"; + EXPECT_THAT(CompiledInstructions(input, SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpImageSparseSampleProjDrefExplicitLod, + {1, 2, 3, 4, 5, SpvImageOperandsLodMask, 6}))); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.subgroup_dispatch_test.cpp b/third_party/spirv-tools/test/text_to_binary.subgroup_dispatch_test.cpp new file mode 100644 index 0000000..967e3c3 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.subgroup_dispatch_test.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Barrier Instructions" section +// of the SPIR-V spec. + +#include "test/unit_spirv.h" + +#include "gmock/gmock.h" +#include "test/test_fixture.h" + +namespace spvtools { +namespace { + +using ::spvtest::MakeInstruction; +using ::testing::Eq; + +using OpGetKernelLocalSizeForSubgroupCountTest = spvtest::TextToBinaryTest; + +// We should be able to assemble it. Validation checks are in another test +// file. +TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type " + "%sgcount %invoke %param %param_size %param_align", + SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpGetKernelLocalSizeForSubgroupCount, + {1, 2, 3, 4, 5, 6, 7}))); +} + +TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, ArgumentCount) { + EXPECT_THAT(CompileFailure("OpGetKernelLocalSizeForSubgroupCount", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected at the beginning of an instruction, " + "found 'OpGetKernelLocalSizeForSubgroupCount'.")); + EXPECT_THAT(CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT( + CompileFailure("%1 = OpGetKernelLocalSizeForSubgroupCount %2 %3 %4 %5 %6", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT( + CompiledInstructions("%res = OpGetKernelLocalSizeForSubgroupCount %type " + "%sgcount %invoke %param %param_size %param_align", + SPV_ENV_UNIVERSAL_1_1), + Eq(MakeInstruction(SpvOpGetKernelLocalSizeForSubgroupCount, + {1, 2, 3, 4, 5, 6, 7}))); + EXPECT_THAT( + CompileFailure("%res = OpGetKernelLocalSizeForSubgroupCount %type " + "%sgcount %invoke %param %param_size %param_align %extra", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected '=', found end of stream.")); +} + +TEST_F(OpGetKernelLocalSizeForSubgroupCountTest, ArgumentTypes) { + EXPECT_THAT(CompileFailure( + "%1 = OpGetKernelLocalSizeForSubgroupCount 2 %3 %4 %5 %6 %7", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); + EXPECT_THAT( + CompileFailure( + "%1 = OpGetKernelLocalSizeForSubgroupCount %2 %3 %4 %5 %6 \"abc\"", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); +} + +using OpGetKernelMaxNumSubgroupsTest = spvtest::TextToBinaryTest; + +TEST_F(OpGetKernelMaxNumSubgroupsTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type " + "%invoke %param %param_size %param_align", + SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpGetKernelMaxNumSubgroups, {1, 2, 3, 4, 5, 6}))); +} + +TEST_F(OpGetKernelMaxNumSubgroupsTest, ArgumentCount) { + EXPECT_THAT( + CompileFailure("OpGetKernelMaxNumSubgroups", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected at the beginning of an instruction, found " + "'OpGetKernelMaxNumSubgroups'.")); + EXPECT_THAT(CompileFailure("%res = OpGetKernelMaxNumSubgroups", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT(CompileFailure("%1 = OpGetKernelMaxNumSubgroups %2 %3 %4 %5", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found end of stream.")); + EXPECT_THAT( + CompiledInstructions("%res = OpGetKernelMaxNumSubgroups %type " + "%invoke %param %param_size %param_align", + SPV_ENV_UNIVERSAL_1_1), + Eq(MakeInstruction(SpvOpGetKernelMaxNumSubgroups, {1, 2, 3, 4, 5, 6}))); + EXPECT_THAT(CompileFailure("%res = OpGetKernelMaxNumSubgroups %type %invoke " + "%param %param_size %param_align %extra", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected '=', found end of stream.")); +} + +TEST_F(OpGetKernelMaxNumSubgroupsTest, ArgumentTypes) { + EXPECT_THAT(CompileFailure("%1 = OpGetKernelMaxNumSubgroups 2 %3 %4 %5 %6", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); + EXPECT_THAT( + CompileFailure("%1 = OpGetKernelMaxNumSubgroups %2 %3 %4 %5 \"abc\"", + SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary.type_declaration_test.cpp b/third_party/spirv-tools/test/text_to_binary.type_declaration_test.cpp new file mode 100644 index 0000000..1589188 --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary.type_declaration_test.cpp @@ -0,0 +1,293 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for instructions in the "Type-Declaration" section of the +// SPIR-V spec. + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::EnumCase; +using spvtest::MakeInstruction; +using ::testing::Eq; + +// Test Dim enums via OpTypeImage + +using DimTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>>; + +TEST_P(DimTest, AnyDim) { + const std::string input = + "%1 = OpTypeImage %2 " + GetParam().name() + " 2 3 0 4 Rgba8\n"; + EXPECT_THAT( + CompiledInstructions(input), + Eq(MakeInstruction(SpvOpTypeImage, {1, 2, GetParam().value(), 2, 3, 0, 4, + SpvImageFormatRgba8}))); + + // Check the disassembler as well. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input)); +} + +// clang-format off +#define CASE(NAME) {SpvDim##NAME, #NAME} +INSTANTIATE_TEST_SUITE_P( + TextToBinaryDim, DimTest, + ::testing::ValuesIn(std::vector>{ + CASE(1D), + CASE(2D), + CASE(3D), + CASE(Cube), + CASE(Rect), + CASE(Buffer), + CASE(SubpassData), + })); +#undef CASE +// clang-format on + +TEST_F(DimTest, WrongDim) { + EXPECT_THAT(CompileFailure("%i = OpTypeImage %t xxyyzz 1 2 3 4 R8"), + Eq("Invalid dimensionality 'xxyyzz'.")); +} + +// Test ImageFormat enums via OpTypeImage + +using ImageFormatTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(ImageFormatTest, AnyImageFormatAndNoAccessQualifier) { + const std::string input = + "%1 = OpTypeImage %2 1D 2 3 0 4 " + GetParam().name() + "\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpTypeImage, {1, 2, SpvDim1D, 2, 3, 0, 4, + GetParam().value()}))); + // Check the disassembler as well. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input)); +} + +// clang-format off +#define CASE(NAME) {SpvImageFormat##NAME, #NAME} +INSTANTIATE_TEST_SUITE_P( + TextToBinaryImageFormat, ImageFormatTest, + ::testing::ValuesIn(std::vector>{ + CASE(Unknown), + CASE(Rgba32f), + CASE(Rgba16f), + CASE(R32f), + CASE(Rgba8), + CASE(Rgba8Snorm), + CASE(Rg32f), + CASE(Rg16f), + CASE(R11fG11fB10f), + CASE(R16f), + CASE(Rgba16), + CASE(Rgb10A2), + CASE(Rg16), + CASE(Rg8), + CASE(R16), + CASE(R8), + CASE(Rgba16Snorm), + CASE(Rg16Snorm), + CASE(Rg8Snorm), + CASE(R16Snorm), + CASE(R8Snorm), + CASE(Rgba32i), + CASE(Rgba16i), + CASE(Rgba8i), + CASE(R32i), + CASE(Rg32i), + CASE(Rg16i), + CASE(Rg8i), + CASE(R16i), + CASE(R8i), + CASE(Rgba32ui), + CASE(Rgba16ui), + CASE(Rgba8ui), + CASE(R32ui), + CASE(Rgb10a2ui), + CASE(Rg32ui), + CASE(Rg16ui), + CASE(Rg8ui), + CASE(R16ui), + CASE(R8ui), + })); +#undef CASE +// clang-format on + +TEST_F(ImageFormatTest, WrongFormat) { + EXPECT_THAT(CompileFailure("%r = OpTypeImage %t 1D 2 3 0 4 xxyyzz"), + Eq("Invalid image format 'xxyyzz'.")); +} + +// Test AccessQualifier enums via OpTypeImage. +using ImageAccessQualifierTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(ImageAccessQualifierTest, AnyAccessQualifier) { + const std::string input = + "%1 = OpTypeImage %2 1D 2 3 0 4 Rgba8 " + GetParam().name() + "\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpTypeImage, + {1, 2, SpvDim1D, 2, 3, 0, 4, + SpvImageFormatRgba8, GetParam().value()}))); + // Check the disassembler as well. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input)); +} + +// clang-format off +#define CASE(NAME) {SpvAccessQualifier##NAME, #NAME} +INSTANTIATE_TEST_SUITE_P( + AccessQualifier, ImageAccessQualifierTest, + ::testing::ValuesIn(std::vector>{ + CASE(ReadOnly), + CASE(WriteOnly), + CASE(ReadWrite), + })); +// clang-format on +#undef CASE + +// Test AccessQualifier enums via OpTypePipe. + +using OpTypePipeTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(OpTypePipeTest, AnyAccessQualifier) { + const std::string input = "%1 = OpTypePipe " + GetParam().name() + "\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(MakeInstruction(SpvOpTypePipe, {1, GetParam().value()}))); + // Check the disassembler as well. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(input)); +} + +// clang-format off +#define CASE(NAME) {SpvAccessQualifier##NAME, #NAME} +INSTANTIATE_TEST_SUITE_P( + TextToBinaryTypePipe, OpTypePipeTest, + ::testing::ValuesIn(std::vector>{ + CASE(ReadOnly), + CASE(WriteOnly), + CASE(ReadWrite), + })); +#undef CASE +// clang-format on + +TEST_F(OpTypePipeTest, WrongAccessQualifier) { + EXPECT_THAT(CompileFailure("%1 = OpTypePipe xxyyzz"), + Eq("Invalid access qualifier 'xxyyzz'.")); +} + +using OpTypeForwardPointerTest = spvtest::TextToBinaryTest; + +#define CASE(storage_class) \ + do { \ + EXPECT_THAT( \ + CompiledInstructions("OpTypeForwardPointer %pt " #storage_class), \ + Eq(MakeInstruction(SpvOpTypeForwardPointer, \ + {1, SpvStorageClass##storage_class}))); \ + } while (0) + +TEST_F(OpTypeForwardPointerTest, ValidStorageClass) { + CASE(UniformConstant); + CASE(Input); + CASE(Uniform); + CASE(Output); + CASE(Workgroup); + CASE(CrossWorkgroup); + CASE(Private); + CASE(Function); + CASE(Generic); + CASE(PushConstant); + CASE(AtomicCounter); + CASE(Image); + CASE(StorageBuffer); +} + +#undef CASE + +TEST_F(OpTypeForwardPointerTest, MissingType) { + EXPECT_THAT(CompileFailure("OpTypeForwardPointer"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(OpTypeForwardPointerTest, MissingClass) { + EXPECT_THAT(CompileFailure("OpTypeForwardPointer %pt"), + Eq("Expected operand, found end of stream.")); +} + +TEST_F(OpTypeForwardPointerTest, WrongClass) { + EXPECT_THAT(CompileFailure("OpTypeForwardPointer %pt xxyyzz"), + Eq("Invalid storage class 'xxyyzz'.")); +} + +using OpSizeOfTest = spvtest::TextToBinaryTest; + +// We should be able to assemble it. Validation checks are in another test +// file. +TEST_F(OpSizeOfTest, OpcodeAssemblesInV10) { + EXPECT_THAT( + CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_0), + Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3}))); +} + +TEST_F(OpSizeOfTest, ArgumentCount) { + EXPECT_THAT( + CompileFailure("OpSizeOf", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected at the beginning of an instruction, found " + "'OpSizeOf'.")); + EXPECT_THAT(CompileFailure("%res = OpSizeOf OpNop", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected operand, found next instruction instead.")); + EXPECT_THAT( + CompiledInstructions("%1 = OpSizeOf %2 %3", SPV_ENV_UNIVERSAL_1_1), + Eq(MakeInstruction(SpvOpSizeOf, {1, 2, 3}))); + EXPECT_THAT( + CompileFailure("%1 = OpSizeOf %2 %3 44 55 ", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected or at the beginning of an instruction, " + "found '44'.")); +} + +TEST_F(OpSizeOfTest, ArgumentTypes) { + EXPECT_THAT(CompileFailure("%1 = OpSizeOf 2 %3", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); + EXPECT_THAT(CompileFailure("%1 = OpSizeOf %2 \"abc\"", SPV_ENV_UNIVERSAL_1_1), + Eq("Expected id to start with %.")); +} + +// TODO(dneto): OpTypeVoid +// TODO(dneto): OpTypeBool +// TODO(dneto): OpTypeInt +// TODO(dneto): OpTypeFloat +// TODO(dneto): OpTypeVector +// TODO(dneto): OpTypeMatrix +// TODO(dneto): OpTypeImage +// TODO(dneto): OpTypeSampler +// TODO(dneto): OpTypeSampledImage +// TODO(dneto): OpTypeArray +// TODO(dneto): OpTypeRuntimeArray +// TODO(dneto): OpTypeStruct +// TODO(dneto): OpTypeOpaque +// TODO(dneto): OpTypePointer +// TODO(dneto): OpTypeFunction +// TODO(dneto): OpTypeEvent +// TODO(dneto): OpTypeDeviceEvent +// TODO(dneto): OpTypeReserveId +// TODO(dneto): OpTypeQueue + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_to_binary_test.cpp b/third_party/spirv-tools/test/text_to_binary_test.cpp new file mode 100644 index 0000000..57f0a6c --- /dev/null +++ b/third_party/spirv-tools/test/text_to_binary_test.cpp @@ -0,0 +1,269 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/spirv_constant.h" +#include "source/util/bitutils.h" +#include "source/util/hex_float.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::AutoText; +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using spvtest::ScopedContext; +using spvtest::TextToBinaryTest; +using testing::Eq; +using testing::IsNull; +using testing::NotNull; + +// An mask parsing test case. +struct MaskCase { + spv_operand_type_t which_enum; + uint32_t expected_value; + const char* expression; +}; + +using GoodMaskParseTest = ::testing::TestWithParam; + +TEST_P(GoodMaskParseTest, GoodMaskExpressions) { + spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); + + uint32_t value; + EXPECT_EQ(SPV_SUCCESS, + AssemblyGrammar(context).parseMaskOperand( + GetParam().which_enum, GetParam().expression, &value)); + EXPECT_EQ(GetParam().expected_value, value); + + spvContextDestroy(context); +} + +INSTANTIATE_TEST_SUITE_P( + ParseMask, GoodMaskParseTest, + ::testing::ValuesIn(std::vector{ + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 0, "None"}, + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 1, "NotNaN"}, + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 2, "NotInf"}, + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotNaN|NotInf"}, + // Mask experssions are symmetric. + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN"}, + // Repeating a value has no effect. + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 3, "NotInf|NotNaN|NotInf"}, + // Using 3 operands still works. + {SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, 0x13, "NotInf|NotNaN|Fast"}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, 0, "None"}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, 1, "Flatten"}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, 2, "DontFlatten"}, + // Weirdly, you can specify to flatten and don't flatten a selection. + {SPV_OPERAND_TYPE_SELECTION_CONTROL, 3, "Flatten|DontFlatten"}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, 0, "None"}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, 1, "Unroll"}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, 2, "DontUnroll"}, + // Weirdly, you can specify to unroll and don't unroll a loop. + {SPV_OPERAND_TYPE_LOOP_CONTROL, 3, "Unroll|DontUnroll"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 0, "None"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 1, "Inline"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 2, "DontInline"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 4, "Pure"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 8, "Const"}, + {SPV_OPERAND_TYPE_FUNCTION_CONTROL, 0xd, "Inline|Const|Pure"}, + })); + +using BadFPFastMathMaskParseTest = ::testing::TestWithParam; + +TEST_P(BadFPFastMathMaskParseTest, BadMaskExpressions) { + spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); + + uint32_t value; + EXPECT_NE(SPV_SUCCESS, + AssemblyGrammar(context).parseMaskOperand( + SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, GetParam(), &value)); + + spvContextDestroy(context); +} + +INSTANTIATE_TEST_SUITE_P(ParseMask, BadFPFastMathMaskParseTest, + ::testing::ValuesIn(std::vector{ + nullptr, "", "NotValidEnum", "|", "NotInf|", + "|NotInf", "NotInf||NotNaN", + "Unroll" // A good word, but for the wrong enum + })); + +TEST_F(TextToBinaryTest, InvalidText) { + ASSERT_EQ(SPV_ERROR_INVALID_TEXT, + spvTextToBinary(ScopedContext().context, nullptr, 0, &binary, + &diagnostic)); + EXPECT_NE(nullptr, diagnostic); + EXPECT_THAT(diagnostic->error, Eq(std::string("Missing assembly text."))); +} + +TEST_F(TextToBinaryTest, InvalidPointer) { + SetText( + "OpEntryPoint Kernel 0 \"\"\nOpExecutionMode 0 LocalSizeHint 1 1 1\n"); + ASSERT_EQ(SPV_ERROR_INVALID_POINTER, + spvTextToBinary(ScopedContext().context, text.str, text.length, + nullptr, &diagnostic)); +} + +TEST_F(TextToBinaryTest, InvalidPrefix) { + EXPECT_EQ( + "Expected or at the beginning of an instruction, " + "found 'Invalid'.", + CompileFailure("Invalid")); +} + +TEST_F(TextToBinaryTest, EmptyAssemblyString) { + // An empty assembly module is valid! + // It should produce a valid module with zero instructions. + EXPECT_THAT(CompiledInstructions(""), Eq(std::vector{})); +} + +TEST_F(TextToBinaryTest, StringSpace) { + const std::string code = ("OpSourceExtension \"string with spaces\"\n"); + EXPECT_EQ(code, EncodeAndDecodeSuccessfully(code)); +} + +TEST_F(TextToBinaryTest, UnknownBeginningOfInstruction) { + EXPECT_EQ( + "Expected or at the beginning of an instruction, " + "found 'Google'.", + CompileFailure( + "\nOpSource OpenCL_C 12\nOpMemoryModel Physical64 OpenCL\nGoogle\n")); + EXPECT_EQ(4u, diagnostic->position.line + 1); + EXPECT_EQ(1u, diagnostic->position.column + 1); +} + +TEST_F(TextToBinaryTest, NoEqualSign) { + EXPECT_EQ("Expected '=', found end of stream.", + CompileFailure("\nOpSource OpenCL_C 12\n" + "OpMemoryModel Physical64 OpenCL\n%2\n")); + EXPECT_EQ(5u, diagnostic->position.line + 1); + EXPECT_EQ(1u, diagnostic->position.column + 1); +} + +TEST_F(TextToBinaryTest, NoOpCode) { + EXPECT_EQ("Expected opcode, found end of stream.", + CompileFailure("\nOpSource OpenCL_C 12\n" + "OpMemoryModel Physical64 OpenCL\n%2 =\n")); + EXPECT_EQ(5u, diagnostic->position.line + 1); + EXPECT_EQ(1u, diagnostic->position.column + 1); +} + +TEST_F(TextToBinaryTest, WrongOpCode) { + EXPECT_EQ("Invalid Opcode prefix 'Wahahaha'.", + CompileFailure("\nOpSource OpenCL_C 12\n" + "OpMemoryModel Physical64 OpenCL\n%2 = Wahahaha\n")); + EXPECT_EQ(4u, diagnostic->position.line + 1); + EXPECT_EQ(6u, diagnostic->position.column + 1); +} + +TEST_F(TextToBinaryTest, CRLF) { + const std::string input = + "%i32 = OpTypeInt 32 1\r\n%c = OpConstant %i32 123\r\n"; + EXPECT_THAT(CompiledInstructions(input), + Eq(Concatenate({MakeInstruction(SpvOpTypeInt, {1, 32, 1}), + MakeInstruction(SpvOpConstant, {1, 2, 123})}))); +} + +using TextToBinaryFloatValueTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(TextToBinaryFloatValueTest, Samples) { + const std::string input = + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 " + GetParam().first; + EXPECT_THAT(CompiledInstructions(input), + Eq(Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, + {1, 2, GetParam().second})}))); +} + +INSTANTIATE_TEST_SUITE_P( + FloatValues, TextToBinaryFloatValueTest, + ::testing::ValuesIn(std::vector>{ + {"0.0", 0x00000000}, // +0 + {"!0x00000001", 0x00000001}, // +denorm + {"!0x00800000", 0x00800000}, // +norm + {"1.5", 0x3fc00000}, + {"!0x7f800000", 0x7f800000}, // +inf + {"!0x7f800001", 0x7f800001}, // NaN + + {"-0.0", 0x80000000}, // -0 + {"!0x80000001", 0x80000001}, // -denorm + {"!0x80800000", 0x80800000}, // -norm + {"-2.5", 0xc0200000}, + {"!0xff800000", 0xff800000}, // -inf + {"!0xff800001", 0xff800001}, // NaN + })); + +using TextToBinaryHalfValueTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(TextToBinaryHalfValueTest, Samples) { + const std::string input = + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 " + GetParam().first; + EXPECT_THAT(CompiledInstructions(input), + Eq(Concatenate({MakeInstruction(SpvOpTypeFloat, {1, 16}), + MakeInstruction(SpvOpConstant, + {1, 2, GetParam().second})}))); +} + +INSTANTIATE_TEST_SUITE_P( + HalfValues, TextToBinaryHalfValueTest, + ::testing::ValuesIn(std::vector>{ + {"0.0", 0x00000000}, + {"1.0", 0x00003c00}, + {"1.000844", 0x00003c00}, // Truncate to 1.0 + {"1.000977", 0x00003c01}, // Don't have to truncate + {"1.001465", 0x00003c01}, // Truncate to 1.0000977 + {"1.5", 0x00003e00}, + {"-1.0", 0x0000bc00}, + {"2.0", 0x00004000}, + {"-2.0", 0x0000c000}, + {"0x1p1", 0x00004000}, + {"-0x1p1", 0x0000c000}, + {"0x1.8p1", 0x00004200}, + {"0x1.8p4", 0x00004e00}, + {"0x1.801p4", 0x00004e00}, + {"0x1.804p4", 0x00004e01}, + })); + +TEST(CreateContext, InvalidEnvironment) { + spv_target_env env; + std::memset(&env, 99, sizeof(env)); + EXPECT_THAT(spvContextCreate(env), IsNull()); +} + +TEST(CreateContext, UniversalEnvironment) { + auto c = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); + EXPECT_THAT(c, NotNull()); + spvContextDestroy(c); +} + +TEST(CreateContext, VulkanEnvironment) { + auto c = spvContextCreate(SPV_ENV_VULKAN_1_0); + EXPECT_THAT(c, NotNull()); + spvContextDestroy(c); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/text_word_get_test.cpp b/third_party/spirv-tools/test/text_word_get_test.cpp new file mode 100644 index 0000000..b74a680 --- /dev/null +++ b/third_party/spirv-tools/test/text_word_get_test.cpp @@ -0,0 +1,254 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::AutoText; + +#define TAB "\t" +#define NEWLINE "\n" +#define BACKSLASH R"(\)" +#define QUOTE R"(")" + +TEST(TextWordGet, NullTerminator) { + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ( + SPV_SUCCESS, + AssemblyContext(AutoText("Word"), nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(4u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(4u, endPosition.index); + ASSERT_STREQ("Word", word.c_str()); +} + +TEST(TextWordGet, TabTerminator) { + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, AssemblyContext(AutoText("Word\t"), nullptr) + .getWord(&word, &endPosition)); + ASSERT_EQ(4u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(4u, endPosition.index); + ASSERT_STREQ("Word", word.c_str()); +} + +TEST(TextWordGet, SpaceTerminator) { + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ( + SPV_SUCCESS, + AssemblyContext(AutoText("Word "), nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(4u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(4u, endPosition.index); + ASSERT_STREQ("Word", word.c_str()); +} + +TEST(TextWordGet, SemicolonTerminator) { + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ( + SPV_SUCCESS, + AssemblyContext(AutoText("Wo;rd"), nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(2u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(2u, endPosition.index); + ASSERT_STREQ("Wo", word.c_str()); +} + +TEST(TextWordGet, NoTerminator) { + const std::string full_text = "abcdefghijklmn"; + for (size_t len = 1; len <= full_text.size(); ++len) { + std::string word; + spv_text_t text = {full_text.data(), len}; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, + AssemblyContext(&text, nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(len, endPosition.column); + ASSERT_EQ(len, endPosition.index); + ASSERT_EQ(full_text.substr(0, len), word); + } +} + +TEST(TextWordGet, MultipleWords) { + AutoText input("Words in a sentence"); + AssemblyContext data(input, nullptr); + + spv_position_t endPosition = {}; + const char* words[] = {"Words", "in", "a", "sentence"}; + + std::string word; + for (uint32_t wordIndex = 0; wordIndex < 4; ++wordIndex) { + ASSERT_EQ(SPV_SUCCESS, data.getWord(&word, &endPosition)); + ASSERT_EQ(strlen(words[wordIndex]), + endPosition.column - data.position().column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(strlen(words[wordIndex]), + endPosition.index - data.position().index); + ASSERT_STREQ(words[wordIndex], word.c_str()); + + data.setPosition(endPosition); + if (3 != wordIndex) { + ASSERT_EQ(SPV_SUCCESS, data.advance()); + } else { + ASSERT_EQ(SPV_END_OF_STREAM, data.advance()); + } + } +} + +TEST(TextWordGet, QuotesAreKept) { + AutoText input(R"("quotes" "around words")"); + const char* expected[] = {R"("quotes")", R"("around words")"}; + AssemblyContext data(input, nullptr); + + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, data.getWord(&word, &endPosition)); + EXPECT_EQ(8u, endPosition.column); + EXPECT_EQ(0u, endPosition.line); + EXPECT_EQ(8u, endPosition.index); + EXPECT_STREQ(expected[0], word.c_str()); + + // Move to the next word. + data.setPosition(endPosition); + data.seekForward(1); + + ASSERT_EQ(SPV_SUCCESS, data.getWord(&word, &endPosition)); + EXPECT_EQ(23u, endPosition.column); + EXPECT_EQ(0u, endPosition.line); + EXPECT_EQ(23u, endPosition.index); + EXPECT_STREQ(expected[1], word.c_str()); +} + +TEST(TextWordGet, QuotesBetweenWordsActLikeGlue) { + AutoText input(R"(quotes" "between words)"); + const char* expected[] = {R"(quotes" "between)", "words"}; + AssemblyContext data(input, nullptr); + + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, data.getWord(&word, &endPosition)); + EXPECT_EQ(16u, endPosition.column); + EXPECT_EQ(0u, endPosition.line); + EXPECT_EQ(16u, endPosition.index); + EXPECT_STREQ(expected[0], word.c_str()); + + // Move to the next word. + data.setPosition(endPosition); + data.seekForward(1); + + ASSERT_EQ(SPV_SUCCESS, data.getWord(&word, &endPosition)); + EXPECT_EQ(22u, endPosition.column); + EXPECT_EQ(0u, endPosition.line); + EXPECT_EQ(22u, endPosition.index); + EXPECT_STREQ(expected[1], word.c_str()); +} + +TEST(TextWordGet, QuotingWhitespace) { + AutoText input(QUOTE "white " NEWLINE TAB " space" QUOTE); + // Whitespace surrounded by quotes acts like glue. + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, + AssemblyContext(input, nullptr).getWord(&word, &endPosition)); + EXPECT_EQ(input.str.length(), endPosition.column); + EXPECT_EQ(0u, endPosition.line); + EXPECT_EQ(input.str.length(), endPosition.index); + EXPECT_EQ(input.str, word); +} + +TEST(TextWordGet, QuoteAlone) { + AutoText input(QUOTE); + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, + AssemblyContext(input, nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(1u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(1u, endPosition.index); + ASSERT_STREQ(QUOTE, word.c_str()); +} + +TEST(TextWordGet, EscapeAlone) { + AutoText input(BACKSLASH); + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, + AssemblyContext(input, nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(1u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(1u, endPosition.index); + ASSERT_STREQ(BACKSLASH, word.c_str()); +} + +TEST(TextWordGet, EscapeAtEndOfInput) { + AutoText input("word" BACKSLASH); + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, + AssemblyContext(input, nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(5u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(5u, endPosition.index); + ASSERT_STREQ("word" BACKSLASH, word.c_str()); +} + +TEST(TextWordGet, Escaping) { + AutoText input("w" BACKSLASH QUOTE "o" BACKSLASH NEWLINE "r" BACKSLASH ";d"); + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, + AssemblyContext(input, nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(10u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(10u, endPosition.index); + ASSERT_EQ(input.str, word); +} + +TEST(TextWordGet, EscapingEscape) { + AutoText input("word" BACKSLASH BACKSLASH " abc"); + std::string word; + spv_position_t endPosition = {}; + ASSERT_EQ(SPV_SUCCESS, + AssemblyContext(input, nullptr).getWord(&word, &endPosition)); + ASSERT_EQ(6u, endPosition.column); + ASSERT_EQ(0u, endPosition.line); + ASSERT_EQ(6u, endPosition.index); + ASSERT_STREQ("word" BACKSLASH BACKSLASH, word.c_str()); +} + +TEST(TextWordGet, CRLF) { + AutoText input("abc\r\nd"); + AssemblyContext data(input, nullptr); + std::string word; + spv_position_t pos = {}; + ASSERT_EQ(SPV_SUCCESS, data.getWord(&word, &pos)); + EXPECT_EQ(3u, pos.column); + EXPECT_STREQ("abc", word.c_str()); + data.setPosition(pos); + data.advance(); + ASSERT_EQ(SPV_SUCCESS, data.getWord(&word, &pos)); + EXPECT_EQ(1u, pos.column); + EXPECT_STREQ("d", word.c_str()); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/timer_test.cpp b/third_party/spirv-tools/test/timer_test.cpp new file mode 100644 index 0000000..84ab46d --- /dev/null +++ b/third_party/spirv-tools/test/timer_test.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gtest/gtest.h" +#include "source/util/timer.h" + +namespace spvtools { +namespace utils { +namespace { + +// A mock class to mimic Timer class for a testing purpose. It has fixed +// CPU/WALL/USR/SYS time, RSS delta, and the delta of the number of page faults. +class MockTimer : public Timer { + public: + MockTimer(std::ostream* out, bool measure_mem_usage = false) + : Timer(out, measure_mem_usage) {} + double CPUTime() override { return 0.019123; } + double WallTime() override { return 0.019723; } + double UserTime() override { return 0.012723; } + double SystemTime() override { return 0.002723; } + long RSS() const override { return 360L; } + long PageFault() const override { return 3600L; } +}; + +// This unit test checks whether the actual output of MockTimer::Report() is the +// same as fixed CPU/WALL/USR/SYS time, RSS delta, and the delta of the number +// of page faults that are returned by MockTimer. +TEST(MockTimer, DoNothing) { + std::ostringstream buf; + + PrintTimerDescription(&buf); + MockTimer timer(&buf); + timer.Start(); + + // Do nothing. + + timer.Stop(); + timer.Report("TimerTest"); + + EXPECT_EQ(0.019123, timer.CPUTime()); + EXPECT_EQ(0.019723, timer.WallTime()); + EXPECT_EQ(0.012723, timer.UserTime()); + EXPECT_EQ(0.002723, timer.SystemTime()); + EXPECT_EQ( + " PASS name CPU time WALL time USR time" + " SYS time\n TimerTest 0.02 0.02" + " 0.01 0.00\n", + buf.str()); +} + +// This unit test checks whether the ScopedTimer correctly reports +// the fixed CPU/WALL/USR/SYS time, RSS delta, and the delta of the number of +// page faults that are returned by MockTimer. +TEST(MockTimer, TestScopedTimer) { + std::ostringstream buf; + + { + ScopedTimer scopedtimer(&buf, "ScopedTimerTest"); + // Do nothing. + } + + EXPECT_EQ( + " ScopedTimerTest 0.02 0.02 0.01" + " 0.00\n", + buf.str()); +} + +// A mock class to mimic CumulativeTimer class for a testing purpose. It has +// fixed CPU/WALL/USR/SYS time, RSS delta, and the delta of the number of page +// faults for each measurement (i.e., a pair of Start() and Stop()). If the +// number of measurements increases, it increases |count_stop_| by the number of +// calling Stop() and the amount of each resource usage is proportional to +// |count_stop_|. +class MockCumulativeTimer : public CumulativeTimer { + public: + MockCumulativeTimer(std::ostream* out, bool measure_mem_usage = false) + : CumulativeTimer(out, measure_mem_usage), count_stop_(0) {} + double CPUTime() override { return count_stop_ * 0.019123; } + double WallTime() override { return count_stop_ * 0.019723; } + double UserTime() override { return count_stop_ * 0.012723; } + double SystemTime() override { return count_stop_ * 0.002723; } + long RSS() const override { return count_stop_ * 360L; } + long PageFault() const override { return count_stop_ * 3600L; } + + // Calling Stop() does nothing but just increases |count_stop_| by 1. + void Stop() override { ++count_stop_; } + + private: + unsigned int count_stop_; +}; + +// This unit test checks whether the MockCumulativeTimer correctly reports the +// cumulative CPU/WALL/USR/SYS time, RSS delta, and the delta of the number of +// page faults whose values are fixed for each measurement (i.e., a pair of +// Start() and Stop()). +TEST(MockCumulativeTimer, DoNothing) { + CumulativeTimer* ctimer; + std::ostringstream buf; + + { + ctimer = new MockCumulativeTimer(&buf); + ctimer->Start(); + + // Do nothing. + + ctimer->Stop(); + } + + { + ctimer->Start(); + + // Do nothing. + + ctimer->Stop(); + ctimer->Report("CumulativeTimerTest"); + } + + EXPECT_EQ( + " CumulativeTimerTest 0.04 0.04 0.03" + " 0.01\n", + buf.str()); + + if (ctimer) delete ctimer; +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/tools/CMakeLists.txt b/third_party/spirv-tools/test/tools/CMakeLists.txt new file mode 100644 index 0000000..99f9780 --- /dev/null +++ b/third_party/spirv-tools/test/tools/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_test(NAME spirv-tools_expect_unittests + COMMAND ${PYTHON_EXECUTABLE} -m unittest expect_unittest.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +add_test(NAME spirv-tools_spirv_test_framework_unittests + COMMAND ${PYTHON_EXECUTABLE} -m unittest spirv_test_framework_unittest.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +add_subdirectory(opt) diff --git a/third_party/spirv-tools/test/tools/expect.py b/third_party/spirv-tools/test/tools/expect.py new file mode 100755 index 0000000..0b51adc --- /dev/null +++ b/third_party/spirv-tools/test/tools/expect.py @@ -0,0 +1,708 @@ +# Copyright (c) 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""A number of common spirv result checks coded in mixin classes. + +A test case can use these checks by declaring their enclosing mixin classes +as superclass and providing the expected_* variables required by the check_*() +methods in the mixin classes. +""" +import difflib +import functools +import os +import re +import subprocess +import traceback +from spirv_test_framework import SpirvTest +from builtins import bytes + +DEFAULT_SPIRV_VERSION = 0x010000 + +def convert_to_unix_line_endings(source): + """Converts all line endings in source to be unix line endings.""" + result = source.replace('\r\n', '\n').replace('\r', '\n') + return result + + +def substitute_file_extension(filename, extension): + """Substitutes file extension, respecting known shader extensions. + + foo.vert -> foo.vert.[extension] [similarly for .frag, .comp, etc.] + foo.glsl -> foo.[extension] + foo.unknown -> foo.[extension] + foo -> foo.[extension] + """ + if filename[-5:] not in [ + '.vert', '.frag', '.tesc', '.tese', '.geom', '.comp', '.spvasm' + ]: + return filename.rsplit('.', 1)[0] + '.' + extension + else: + return filename + '.' + extension + + +def get_object_filename(source_filename): + """Gets the object filename for the given source file.""" + return substitute_file_extension(source_filename, 'spv') + + +def get_assembly_filename(source_filename): + """Gets the assembly filename for the given source file.""" + return substitute_file_extension(source_filename, 'spvasm') + + +def verify_file_non_empty(filename): + """Checks that a given file exists and is not empty.""" + if not os.path.isfile(filename): + return False, 'Cannot find file: ' + filename + if not os.path.getsize(filename): + return False, 'Empty file: ' + filename + return True, '' + + +class ReturnCodeIsZero(SpirvTest): + """Mixin class for checking that the return code is zero.""" + + def check_return_code_is_zero(self, status): + if status.returncode: + return False, 'Non-zero return code: {ret}\n'.format( + ret=status.returncode) + return True, '' + + +class ReturnCodeIsNonZero(SpirvTest): + """Mixin class for checking that the return code is not zero.""" + + def check_return_code_is_nonzero(self, status): + if not status.returncode: + return False, 'return code is 0' + return True, '' + + +class NoOutputOnStdout(SpirvTest): + """Mixin class for checking that there is no output on stdout.""" + + def check_no_output_on_stdout(self, status): + if status.stdout: + return False, 'Non empty stdout: {out}\n'.format(out=status.stdout) + return True, '' + + +class NoOutputOnStderr(SpirvTest): + """Mixin class for checking that there is no output on stderr.""" + + def check_no_output_on_stderr(self, status): + if status.stderr: + return False, 'Non empty stderr: {err}\n'.format(err=status.stderr) + return True, '' + + +class SuccessfulReturn(ReturnCodeIsZero, NoOutputOnStdout, NoOutputOnStderr): + """Mixin class for checking that return code is zero and no output on + stdout and stderr.""" + pass + + +class NoGeneratedFiles(SpirvTest): + """Mixin class for checking that there is no file generated.""" + + def check_no_generated_files(self, status): + all_files = os.listdir(status.directory) + input_files = status.input_filenames + if all([f.startswith(status.directory) for f in input_files]): + all_files = [os.path.join(status.directory, f) for f in all_files] + generated_files = set(all_files) - set(input_files) + if len(generated_files) == 0: + return True, '' + else: + return False, 'Extra files generated: {}'.format(generated_files) + + +class CorrectBinaryLengthAndPreamble(SpirvTest): + """Provides methods for verifying preamble for a SPIR-V binary.""" + + def verify_binary_length_and_header(self, binary, spv_version=0x10000): + """Checks that the given SPIR-V binary has valid length and header. + + Returns: + False, error string if anything is invalid + True, '' otherwise + Args: + binary: a bytes object containing the SPIR-V binary + spv_version: target SPIR-V version number, with same encoding + as the version word in a SPIR-V header. + """ + + def read_word(binary, index, little_endian): + """Reads the index-th word from the given binary file.""" + word = binary[index * 4:(index + 1) * 4] + if little_endian: + word = reversed(word) + return functools.reduce(lambda w, b: (w << 8) | b, word, 0) + + def check_endianness(binary): + """Checks the endianness of the given SPIR-V binary. + + Returns: + True if it's little endian, False if it's big endian. + None if magic number is wrong. + """ + first_word = read_word(binary, 0, True) + if first_word == 0x07230203: + return True + first_word = read_word(binary, 0, False) + if first_word == 0x07230203: + return False + return None + + num_bytes = len(binary) + if num_bytes % 4 != 0: + return False, ('Incorrect SPV binary: size should be a multiple' + ' of words') + if num_bytes < 20: + return False, 'Incorrect SPV binary: size less than 5 words' + + preamble = binary[0:19] + little_endian = check_endianness(preamble) + # SPIR-V module magic number + if little_endian is None: + return False, 'Incorrect SPV binary: wrong magic number' + + # SPIR-V version number + version = read_word(preamble, 1, little_endian) + # TODO(dneto): Recent Glslang uses version word 0 for opengl_compat + # profile + + if version != spv_version and version != 0: + return False, 'Incorrect SPV binary: wrong version number: ' + hex(version) + ' expected ' + hex(spv_version) + # Shaderc-over-Glslang (0x000d....) or + # SPIRV-Tools (0x0007....) generator number + if read_word(preamble, 2, little_endian) != 0x000d0007 and \ + read_word(preamble, 2, little_endian) != 0x00070000: + return False, ('Incorrect SPV binary: wrong generator magic ' 'number') + # reserved for instruction schema + if read_word(preamble, 4, little_endian) != 0: + return False, 'Incorrect SPV binary: the 5th byte should be 0' + + return True, '' + + +class CorrectObjectFilePreamble(CorrectBinaryLengthAndPreamble): + """Provides methods for verifying preamble for a SPV object file.""" + + def verify_object_file_preamble(self, + filename, + spv_version=DEFAULT_SPIRV_VERSION): + """Checks that the given SPIR-V binary file has correct preamble.""" + + success, message = verify_file_non_empty(filename) + if not success: + return False, message + + with open(filename, 'rb') as object_file: + object_file.seek(0, os.SEEK_END) + num_bytes = object_file.tell() + + object_file.seek(0) + + binary = bytes(object_file.read()) + return self.verify_binary_length_and_header(binary, spv_version) + + return True, '' + + +class CorrectAssemblyFilePreamble(SpirvTest): + """Provides methods for verifying preamble for a SPV assembly file.""" + + def verify_assembly_file_preamble(self, filename): + success, message = verify_file_non_empty(filename) + if not success: + return False, message + + with open(filename) as assembly_file: + line1 = assembly_file.readline() + line2 = assembly_file.readline() + line3 = assembly_file.readline() + + if (line1 != '; SPIR-V\n' or line2 != '; Version: 1.0\n' or + (not line3.startswith('; Generator: Google Shaderc over Glslang;'))): + return False, 'Incorrect SPV assembly' + + return True, '' + + +class ValidObjectFile(SuccessfulReturn, CorrectObjectFilePreamble): + """Mixin class for checking that every input file generates a valid SPIR-V 1.0 + object file following the object file naming rule, and there is no output on + stdout/stderr.""" + + def check_object_file_preamble(self, status): + for input_filename in status.input_filenames: + object_filename = get_object_filename(input_filename) + success, message = self.verify_object_file_preamble( + os.path.join(status.directory, object_filename)) + if not success: + return False, message + return True, '' + + +class ValidObjectFile1_3(ReturnCodeIsZero, CorrectObjectFilePreamble): + """Mixin class for checking that every input file generates a valid SPIR-V 1.3 + object file following the object file naming rule, and there is no output on + stdout/stderr.""" + + def check_object_file_preamble(self, status): + for input_filename in status.input_filenames: + object_filename = get_object_filename(input_filename) + success, message = self.verify_object_file_preamble( + os.path.join(status.directory, object_filename), 0x10300) + if not success: + return False, message + return True, '' + + +class ValidObjectFile1_5(ReturnCodeIsZero, CorrectObjectFilePreamble): + """Mixin class for checking that every input file generates a valid SPIR-V 1.5 + object file following the object file naming rule, and there is no output on + stdout/stderr.""" + + def check_object_file_preamble(self, status): + for input_filename in status.input_filenames: + object_filename = get_object_filename(input_filename) + success, message = self.verify_object_file_preamble( + os.path.join(status.directory, object_filename), 0x10500) + if not success: + return False, message + return True, '' + + +class ValidObjectFileWithAssemblySubstr(SuccessfulReturn, + CorrectObjectFilePreamble): + """Mixin class for checking that every input file generates a valid object + + file following the object file naming rule, there is no output on + stdout/stderr, and the disassmbly contains a specified substring per + input. + """ + + def check_object_file_disassembly(self, status): + for an_input in status.inputs: + object_filename = get_object_filename(an_input.filename) + obj_file = str(os.path.join(status.directory, object_filename)) + success, message = self.verify_object_file_preamble(obj_file) + if not success: + return False, message + cmd = [status.test_manager.disassembler_path, '--no-color', obj_file] + process = subprocess.Popen( + args=cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=status.directory) + output = process.communicate(None) + disassembly = output[0] + if not isinstance(an_input.assembly_substr, str): + return False, 'Missing assembly_substr member' + if an_input.assembly_substr not in disassembly: + return False, ('Incorrect disassembly output:\n{asm}\n' + 'Expected substring not found:\n{exp}'.format( + asm=disassembly, exp=an_input.assembly_substr)) + return True, '' + + +class ValidNamedObjectFile(SuccessfulReturn, CorrectObjectFilePreamble): + """Mixin class for checking that a list of object files with the given + names are correctly generated, and there is no output on stdout/stderr. + + To mix in this class, subclasses need to provide expected_object_filenames + as the expected object filenames. + """ + + def check_object_file_preamble(self, status): + for object_filename in self.expected_object_filenames: + success, message = self.verify_object_file_preamble( + os.path.join(status.directory, object_filename)) + if not success: + return False, message + return True, '' + + +class ValidFileContents(SpirvTest): + """Mixin class to test that a specific file contains specific text + To mix in this class, subclasses need to provide expected_file_contents as + the contents of the file and target_filename to determine the location.""" + + def check_file(self, status): + target_filename = os.path.join(status.directory, self.target_filename) + if not os.path.isfile(target_filename): + return False, 'Cannot find file: ' + target_filename + with open(target_filename, 'r') as target_file: + file_contents = target_file.read() + if isinstance(self.expected_file_contents, str): + if file_contents == self.expected_file_contents: + return True, '' + return False, ('Incorrect file output: \n{act}\n' + 'Expected:\n{exp}' + 'With diff:\n{diff}'.format( + act=file_contents, + exp=self.expected_file_contents, + diff='\n'.join( + list( + difflib.unified_diff( + self.expected_file_contents.split('\n'), + file_contents.split('\n'), + fromfile='expected_output', + tofile='actual_output'))))) + elif isinstance(self.expected_file_contents, type(re.compile(''))): + if self.expected_file_contents.search(file_contents): + return True, '' + return False, ('Incorrect file output: \n{act}\n' + 'Expected matching regex pattern:\n{exp}'.format( + act=file_contents, + exp=self.expected_file_contents.pattern)) + return False, ( + 'Could not open target file ' + target_filename + ' for reading') + + +class ValidAssemblyFile(SuccessfulReturn, CorrectAssemblyFilePreamble): + """Mixin class for checking that every input file generates a valid assembly + file following the assembly file naming rule, and there is no output on + stdout/stderr.""" + + def check_assembly_file_preamble(self, status): + for input_filename in status.input_filenames: + assembly_filename = get_assembly_filename(input_filename) + success, message = self.verify_assembly_file_preamble( + os.path.join(status.directory, assembly_filename)) + if not success: + return False, message + return True, '' + + +class ValidAssemblyFileWithSubstr(ValidAssemblyFile): + """Mixin class for checking that every input file generates a valid assembly + file following the assembly file naming rule, there is no output on + stdout/stderr, and all assembly files have the given substring specified + by expected_assembly_substr. + + To mix in this class, subclasses need to provde expected_assembly_substr + as the expected substring. + """ + + def check_assembly_with_substr(self, status): + for input_filename in status.input_filenames: + assembly_filename = get_assembly_filename(input_filename) + success, message = self.verify_assembly_file_preamble( + os.path.join(status.directory, assembly_filename)) + if not success: + return False, message + with open(assembly_filename, 'r') as f: + content = f.read() + if self.expected_assembly_substr not in convert_to_unix_line_endings( + content): + return False, ('Incorrect assembly output:\n{asm}\n' + 'Expected substring not found:\n{exp}'.format( + asm=content, exp=self.expected_assembly_substr)) + return True, '' + + +class ValidAssemblyFileWithoutSubstr(ValidAssemblyFile): + """Mixin class for checking that every input file generates a valid assembly + file following the assembly file naming rule, there is no output on + stdout/stderr, and no assembly files have the given substring specified + by unexpected_assembly_substr. + + To mix in this class, subclasses need to provde unexpected_assembly_substr + as the substring we expect not to see. + """ + + def check_assembly_for_substr(self, status): + for input_filename in status.input_filenames: + assembly_filename = get_assembly_filename(input_filename) + success, message = self.verify_assembly_file_preamble( + os.path.join(status.directory, assembly_filename)) + if not success: + return False, message + with open(assembly_filename, 'r') as f: + content = f.read() + if self.unexpected_assembly_substr in convert_to_unix_line_endings( + content): + return False, ('Incorrect assembly output:\n{asm}\n' + 'Unexpected substring found:\n{unexp}'.format( + asm=content, exp=self.unexpected_assembly_substr)) + return True, '' + + +class ValidNamedAssemblyFile(SuccessfulReturn, CorrectAssemblyFilePreamble): + """Mixin class for checking that a list of assembly files with the given + names are correctly generated, and there is no output on stdout/stderr. + + To mix in this class, subclasses need to provide expected_assembly_filenames + as the expected assembly filenames. + """ + + def check_object_file_preamble(self, status): + for assembly_filename in self.expected_assembly_filenames: + success, message = self.verify_assembly_file_preamble( + os.path.join(status.directory, assembly_filename)) + if not success: + return False, message + return True, '' + + +class ErrorMessage(SpirvTest): + """Mixin class for tests that fail with a specific error message. + + To mix in this class, subclasses need to provide expected_error as the + expected error message. + + The test should fail if the subprocess was terminated by a signal. + """ + + def check_has_error_message(self, status): + if not status.returncode: + return False, ('Expected error message, but returned success from ' + 'command execution') + if status.returncode < 0: + # On Unix, a negative value -N for Popen.returncode indicates + # termination by signal N. + # https://docs.python.org/2/library/subprocess.html + return False, ('Expected error message, but command was terminated by ' + 'signal ' + str(status.returncode)) + if not status.stderr: + return False, 'Expected error message, but no output on stderr' + if self.expected_error != convert_to_unix_line_endings(status.stderr): + return False, ('Incorrect stderr output:\n{act}\n' + 'Expected:\n{exp}'.format( + act=status.stderr, exp=self.expected_error)) + return True, '' + + +class ErrorMessageSubstr(SpirvTest): + """Mixin class for tests that fail with a specific substring in the error + message. + + To mix in this class, subclasses need to provide expected_error_substr as + the expected error message substring. + + The test should fail if the subprocess was terminated by a signal. + """ + + def check_has_error_message_as_substring(self, status): + if not status.returncode: + return False, ('Expected error message, but returned success from ' + 'command execution') + if status.returncode < 0: + # On Unix, a negative value -N for Popen.returncode indicates + # termination by signal N. + # https://docs.python.org/2/library/subprocess.html + return False, ('Expected error message, but command was terminated by ' + 'signal ' + str(status.returncode)) + if not status.stderr: + return False, 'Expected error message, but no output on stderr' + if self.expected_error_substr not in convert_to_unix_line_endings( + status.stderr): + return False, ('Incorrect stderr output:\n{act}\n' + 'Expected substring not found in stderr:\n{exp}'.format( + act=status.stderr, exp=self.expected_error_substr)) + return True, '' + + +class WarningMessage(SpirvTest): + """Mixin class for tests that succeed but have a specific warning message. + + To mix in this class, subclasses need to provide expected_warning as the + expected warning message. + """ + + def check_has_warning_message(self, status): + if status.returncode: + return False, ('Expected warning message, but returned failure from' + ' command execution') + if not status.stderr: + return False, 'Expected warning message, but no output on stderr' + if self.expected_warning != convert_to_unix_line_endings(status.stderr): + return False, ('Incorrect stderr output:\n{act}\n' + 'Expected:\n{exp}'.format( + act=status.stderr, exp=self.expected_warning)) + return True, '' + + +class ValidObjectFileWithWarning(NoOutputOnStdout, CorrectObjectFilePreamble, + WarningMessage): + """Mixin class for checking that every input file generates a valid object + file following the object file naming rule, with a specific warning message. + """ + + def check_object_file_preamble(self, status): + for input_filename in status.input_filenames: + object_filename = get_object_filename(input_filename) + success, message = self.verify_object_file_preamble( + os.path.join(status.directory, object_filename)) + if not success: + return False, message + return True, '' + + +class ValidAssemblyFileWithWarning(NoOutputOnStdout, + CorrectAssemblyFilePreamble, WarningMessage): + """Mixin class for checking that every input file generates a valid assembly + file following the assembly file naming rule, with a specific warning + message.""" + + def check_assembly_file_preamble(self, status): + for input_filename in status.input_filenames: + assembly_filename = get_assembly_filename(input_filename) + success, message = self.verify_assembly_file_preamble( + os.path.join(status.directory, assembly_filename)) + if not success: + return False, message + return True, '' + + +class StdoutMatch(SpirvTest): + """Mixin class for tests that can expect output on stdout. + + To mix in this class, subclasses need to provide expected_stdout as the + expected stdout output. + + For expected_stdout, if it's True, then they expect something on stdout but + will not check what it is. If it's a string, expect an exact match. If it's + anything else, it is assumed to be a compiled regular expression which will + be matched against re.search(). It will expect + expected_stdout.search(status.stdout) to be true. + """ + + def check_stdout_match(self, status): + # "True" in this case means we expect something on stdout, but we do not + # care what it is, we want to distinguish this from "blah" which means we + # expect exactly the string "blah". + if self.expected_stdout is True: + if not status.stdout: + return False, 'Expected something on stdout' + elif type(self.expected_stdout) == str: + if self.expected_stdout != convert_to_unix_line_endings(status.stdout): + return False, ('Incorrect stdout output:\n{ac}\n' + 'Expected:\n{ex}'.format( + ac=status.stdout, ex=self.expected_stdout)) + else: + converted = convert_to_unix_line_endings(status.stdout) + if not self.expected_stdout.search(converted): + return False, ('Incorrect stdout output:\n{ac}\n' + 'Expected to match regex:\n{ex}'.format( + ac=status.stdout, ex=self.expected_stdout.pattern)) + return True, '' + + +class StderrMatch(SpirvTest): + """Mixin class for tests that can expect output on stderr. + + To mix in this class, subclasses need to provide expected_stderr as the + expected stderr output. + + For expected_stderr, if it's True, then they expect something on stderr, + but will not check what it is. If it's a string, expect an exact match. + If it's anything else, it is assumed to be a compiled regular expression + which will be matched against re.search(). It will expect + expected_stderr.search(status.stderr) to be true. + """ + + def check_stderr_match(self, status): + # "True" in this case means we expect something on stderr, but we do not + # care what it is, we want to distinguish this from "blah" which means we + # expect exactly the string "blah". + if self.expected_stderr is True: + if not status.stderr: + return False, 'Expected something on stderr' + elif type(self.expected_stderr) == str: + if self.expected_stderr != convert_to_unix_line_endings(status.stderr): + return False, ('Incorrect stderr output:\n{ac}\n' + 'Expected:\n{ex}'.format( + ac=status.stderr, ex=self.expected_stderr)) + else: + if not self.expected_stderr.search( + convert_to_unix_line_endings(status.stderr)): + return False, ('Incorrect stderr output:\n{ac}\n' + 'Expected to match regex:\n{ex}'.format( + ac=status.stderr, ex=self.expected_stderr.pattern)) + return True, '' + + +class StdoutNoWiderThan80Columns(SpirvTest): + """Mixin class for tests that require stdout to 80 characters or narrower. + + To mix in this class, subclasses need to provide expected_stdout as the + expected stdout output. + """ + + def check_stdout_not_too_wide(self, status): + if not status.stdout: + return True, '' + else: + for line in status.stdout.splitlines(): + if len(line) > 80: + return False, ('Stdout line longer than 80 columns: %s' % line) + return True, '' + + +class NoObjectFile(SpirvTest): + """Mixin class for checking that no input file has a corresponding object + file.""" + + def check_no_object_file(self, status): + for input_filename in status.input_filenames: + object_filename = get_object_filename(input_filename) + full_object_file = os.path.join(status.directory, object_filename) + print('checking %s' % full_object_file) + if os.path.isfile(full_object_file): + return False, ( + 'Expected no object file, but found: %s' % full_object_file) + return True, '' + + +class NoNamedOutputFiles(SpirvTest): + """Mixin class for checking that no specified output files exist. + + The expected_output_filenames member should be full pathnames.""" + + def check_no_named_output_files(self, status): + for object_filename in self.expected_output_filenames: + if os.path.isfile(object_filename): + return False, ( + 'Expected no output file, but found: %s' % object_filename) + return True, '' + + +class ExecutedListOfPasses(SpirvTest): + """Mixin class for checking that a list of passes where executed. + + It works by analyzing the output of the --print-all flag to spirv-opt. + + For this mixin to work, the class member expected_passes should be a sequence + of pass names as returned by Pass::name(). + """ + + def check_list_of_executed_passes(self, status): + # Collect all the output lines containing a pass name. + pass_names = [] + pass_name_re = re.compile(r'.*IR before pass (?P[\S]+)') + for line in status.stderr.splitlines(): + match = pass_name_re.match(line) + if match: + pass_names.append(match.group('pass_name')) + + for (expected, actual) in zip(self.expected_passes, pass_names): + if expected != actual: + return False, ( + 'Expected pass "%s" but found pass "%s"\n' % (expected, actual)) + + return True, '' diff --git a/third_party/spirv-tools/test/tools/expect_unittest.py b/third_party/spirv-tools/test/tools/expect_unittest.py new file mode 100644 index 0000000..a28de1b --- /dev/null +++ b/third_party/spirv-tools/test/tools/expect_unittest.py @@ -0,0 +1,82 @@ +# Copyright (c) 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests for the expect module.""" + +import expect +from spirv_test_framework import TestStatus +import re +import unittest + + +class TestStdoutMatchADotC(expect.StdoutMatch): + expected_stdout = re.compile('a.c') + + +class TestExpect(unittest.TestCase): + def test_get_object_name(self): + """Tests get_object_filename().""" + source_and_object_names = [('a.vert', 'a.vert.spv'), + ('b.frag', 'b.frag.spv'), + ('c.tesc', 'c.tesc.spv'), + ('d.tese', 'd.tese.spv'), + ('e.geom', 'e.geom.spv'), + ('f.comp', 'f.comp.spv'), + ('file', 'file.spv'), ('file.', 'file.spv'), + ('file.uk', + 'file.spv'), ('file.vert.', + 'file.vert.spv'), + ('file.vert.bla', + 'file.vert.spv')] + actual_object_names = [ + expect.get_object_filename(f[0]) for f in source_and_object_names + ] + expected_object_names = [f[1] for f in source_and_object_names] + + self.assertEqual(actual_object_names, expected_object_names) + + def test_stdout_match_regex_has_match(self): + test = TestStdoutMatchADotC() + status = TestStatus( + test_manager=None, + returncode=0, + stdout=b'0abc1', + stderr=None, + directory=None, + inputs=None, + input_filenames=None) + self.assertTrue(test.check_stdout_match(status)[0]) + + def test_stdout_match_regex_no_match(self): + test = TestStdoutMatchADotC() + status = TestStatus( + test_manager=None, + returncode=0, + stdout=b'ab', + stderr=None, + directory=None, + inputs=None, + input_filenames=None) + self.assertFalse(test.check_stdout_match(status)[0]) + + def test_stdout_match_regex_empty_stdout(self): + test = TestStdoutMatchADotC() + status = TestStatus( + test_manager=None, + returncode=0, + stdout=b'', + stderr=None, + directory=None, + inputs=None, + input_filenames=None) + self.assertFalse(test.check_stdout_match(status)[0]) diff --git a/third_party/spirv-tools/test/tools/opt/CMakeLists.txt b/third_party/spirv-tools/test/tools/opt/CMakeLists.txt new file mode 100644 index 0000000..21aa247 --- /dev/null +++ b/third_party/spirv-tools/test/tools/opt/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(NOT ${SPIRV_SKIP_TESTS}) + if(${PYTHONINTERP_FOUND}) + add_test(NAME spirv_opt_cli_tools_tests + COMMAND ${PYTHON_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/../spirv_test_framework.py + $ $ $ + --test-dir ${CMAKE_CURRENT_SOURCE_DIR}) + else() + message("Skipping CLI tools tests - Python executable not found") + endif() +endif() diff --git a/third_party/spirv-tools/test/tools/opt/flags.py b/third_party/spirv-tools/test/tools/opt/flags.py new file mode 100644 index 0000000..f8117d9 --- /dev/null +++ b/third_party/spirv-tools/test/tools/opt/flags.py @@ -0,0 +1,406 @@ +# Copyright (c) 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import placeholder +import expect +import re + +from spirv_test_framework import inside_spirv_testsuite + + +def empty_main_assembly(): + return """ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd""" + + +@inside_spirv_testsuite('SpirvOptBase') +class TestAssemblyFileAsOnlyParameter(expect.ValidObjectFile1_5): + """Tests that spirv-opt accepts a SPIR-V object file.""" + + shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') + output = placeholder.TempFileName('output.spv') + spirv_args = [shader, '-o', output] + expected_object_filenames = (output) + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestHelpFlag(expect.ReturnCodeIsZero, expect.StdoutMatch): + """Test the --help flag.""" + + spirv_args = ['--help'] + expected_stdout = re.compile(r'.*The SPIR-V binary is read from ') + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestValidPassFlags(expect.ValidObjectFile1_5, + expect.ExecutedListOfPasses): + """Tests that spirv-opt accepts all valid optimization flags.""" + + flags = [ + '--wrap-opkill', '--ccp', '--cfg-cleanup', '--combine-access-chains', '--compact-ids', + '--convert-local-access-chains', '--copy-propagate-arrays', + '--eliminate-dead-branches', + '--eliminate-dead-code-aggressive', '--eliminate-dead-const', + '--eliminate-dead-functions', '--eliminate-dead-inserts', + '--eliminate-dead-variables', '--eliminate-insert-extract', + '--eliminate-local-multi-store', '--eliminate-local-single-block', + '--eliminate-local-single-store', '--flatten-decorations', + '--fold-spec-const-op-composite', '--freeze-spec-const', + '--if-conversion', '--inline-entry-points-exhaustive', '--loop-fission', + '20', '--loop-fusion', '5', '--loop-unroll', '--loop-unroll-partial', '3', + '--loop-peeling', '--merge-blocks', '--merge-return', '--loop-unswitch', + '--private-to-local', '--reduce-load-size', '--redundancy-elimination', + '--remove-duplicates', '--replace-invalid-opcode', '--ssa-rewrite', + '--scalar-replacement', '--scalar-replacement=42', '--strength-reduction', + '--strip-debug', '--strip-reflect', '--vector-dce', '--workaround-1209', + '--unify-const', '--legalize-vector-shuffle', + '--split-invalid-unreachable', '--generate-webgpu-initializers', + '--decompose-initialized-variables', '--graphics-robust-access', + '--wrap-opkill', '--amd-ext-to-khr' + ] + expected_passes = [ + 'wrap-opkill', + 'ccp', + 'cfg-cleanup', + 'combine-access-chains', + 'compact-ids', + 'convert-local-access-chains', + 'copy-propagate-arrays', + 'eliminate-dead-branches', + 'eliminate-dead-code-aggressive', + 'eliminate-dead-const', + 'eliminate-dead-functions', + 'eliminate-dead-inserts', + 'eliminate-dead-variables', + # --eliminate-insert-extract runs the simplify-instructions pass. + 'simplify-instructions', + 'ssa-rewrite', + 'eliminate-local-single-block', + 'eliminate-local-single-store', + 'flatten-decorations', + 'fold-spec-const-op-composite', + 'freeze-spec-const', + 'if-conversion', + 'inline-entry-points-exhaustive', + 'loop-fission', + 'loop-fusion', + 'loop-unroll', + 'loop-unroll', + 'loop-peeling', + 'merge-blocks', + 'merge-return', + 'loop-unswitch', + 'private-to-local', + 'reduce-load-size', + 'redundancy-elimination', + 'remove-duplicates', + 'replace-invalid-opcode', + 'ssa-rewrite', + 'scalar-replacement=100', + 'scalar-replacement=42', + 'strength-reduction', + 'strip-debug', + 'strip-reflect', + 'vector-dce', + 'workaround-1209', + 'unify-const', + 'legalize-vector-shuffle', + 'split-invalid-unreachable', + 'generate-webgpu-initializers', + 'decompose-initialized-variables', + 'graphics-robust-access', + 'wrap-opkill', + 'amd-ext-to-khr' + ] + shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') + output = placeholder.TempFileName('output.spv') + spirv_args = [shader, '-o', output, '--print-all'] + flags + expected_object_filenames = (output) + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestPerformanceOptimizationPasses(expect.ValidObjectFile1_5, + expect.ExecutedListOfPasses): + """Tests that spirv-opt schedules all the passes triggered by -O.""" + + flags = ['-O'] + expected_passes = [ + 'wrap-opkill', + 'eliminate-dead-branches', + 'merge-return', + 'inline-entry-points-exhaustive', + 'eliminate-dead-functions', + 'eliminate-dead-code-aggressive', + 'private-to-local', + 'eliminate-local-single-block', + 'eliminate-local-single-store', + 'eliminate-dead-code-aggressive', + 'scalar-replacement=100', + 'convert-local-access-chains', + 'eliminate-local-single-block', + 'eliminate-local-single-store', + 'eliminate-dead-code-aggressive', + 'ssa-rewrite', + 'eliminate-dead-code-aggressive', + 'ccp', + 'eliminate-dead-code-aggressive', + 'loop-unroll', + 'eliminate-dead-branches', + 'redundancy-elimination', + 'combine-access-chains', + 'simplify-instructions', + 'scalar-replacement=100', + 'convert-local-access-chains', + 'eliminate-local-single-block', + 'eliminate-local-single-store', + 'eliminate-dead-code-aggressive', + 'ssa-rewrite', + 'eliminate-dead-code-aggressive', + 'vector-dce', + 'eliminate-dead-inserts', + 'eliminate-dead-branches', + 'simplify-instructions', + 'if-conversion', + 'copy-propagate-arrays', + 'reduce-load-size', + 'eliminate-dead-code-aggressive', + 'merge-blocks', + 'redundancy-elimination', + 'eliminate-dead-branches', + 'merge-blocks', + 'simplify-instructions', + ] + shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') + output = placeholder.TempFileName('output.spv') + spirv_args = [shader, '-o', output, '--print-all'] + flags + expected_object_filenames = (output) + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestSizeOptimizationPasses(expect.ValidObjectFile1_5, + expect.ExecutedListOfPasses): + """Tests that spirv-opt schedules all the passes triggered by -Os.""" + + flags = ['-Os'] + expected_passes = [ + 'wrap-opkill', + 'eliminate-dead-branches', + 'merge-return', + 'inline-entry-points-exhaustive', + 'eliminate-dead-functions', + 'private-to-local', + 'scalar-replacement=0', + 'ssa-rewrite', + 'ccp', + 'loop-unroll', + 'eliminate-dead-branches', + 'simplify-instructions', + 'scalar-replacement=0', + 'eliminate-local-single-store', + 'if-conversion', + 'simplify-instructions', + 'eliminate-dead-code-aggressive', + 'eliminate-dead-branches', + 'merge-blocks', + 'convert-local-access-chains', + 'eliminate-local-single-block', + 'eliminate-dead-code-aggressive', + 'copy-propagate-arrays', + 'vector-dce', + 'eliminate-dead-inserts', + 'eliminate-dead-members', + 'eliminate-local-single-store', + 'merge-blocks', + 'ssa-rewrite', + 'redundancy-elimination', + 'simplify-instructions', + 'eliminate-dead-code-aggressive', + 'cfg-cleanup', + ] + shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') + output = placeholder.TempFileName('output.spv') + spirv_args = [shader, '-o', output, '--print-all'] + flags + expected_object_filenames = (output) + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLegalizationPasses(expect.ValidObjectFile1_5, + expect.ExecutedListOfPasses): + """Tests that spirv-opt schedules all the passes triggered by --legalize-hlsl. + """ + + flags = ['--legalize-hlsl'] + expected_passes = [ + 'wrap-opkill', + 'eliminate-dead-branches', + 'merge-return', + 'inline-entry-points-exhaustive', + 'eliminate-dead-functions', + 'private-to-local', + 'fix-storage-class', + 'eliminate-local-single-block', + 'eliminate-local-single-store', + 'eliminate-dead-code-aggressive', + 'scalar-replacement=0', + 'eliminate-local-single-block', + 'eliminate-local-single-store', + 'eliminate-dead-code-aggressive', + 'ssa-rewrite', + 'eliminate-dead-code-aggressive', + 'ccp', + 'loop-unroll', + 'eliminate-dead-branches', + 'simplify-instructions', + 'eliminate-dead-code-aggressive', + 'copy-propagate-arrays', + 'vector-dce', + 'eliminate-dead-inserts', + 'reduce-load-size', + 'eliminate-dead-code-aggressive', + ] + shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') + output = placeholder.TempFileName('output.spv') + spirv_args = [shader, '-o', output, '--print-all'] + flags + expected_object_filenames = (output) + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestScalarReplacementArgsNegative(expect.ErrorMessageSubstr): + """Tests invalid arguments to --scalar-replacement.""" + + spirv_args = ['--scalar-replacement=-10'] + expected_error_substr = 'must have no arguments or a non-negative integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestScalarReplacementArgsInvalidNumber(expect.ErrorMessageSubstr): + """Tests invalid arguments to --scalar-replacement.""" + + spirv_args = ['--scalar-replacement=a10f'] + expected_error_substr = 'must have no arguments or a non-negative integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLoopFissionArgsNegative(expect.ErrorMessageSubstr): + """Tests invalid arguments to --loop-fission.""" + + spirv_args = ['--loop-fission=-10'] + expected_error_substr = 'must have a positive integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLoopFissionArgsInvalidNumber(expect.ErrorMessageSubstr): + """Tests invalid arguments to --loop-fission.""" + + spirv_args = ['--loop-fission=a10f'] + expected_error_substr = 'must have a positive integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLoopFusionArgsNegative(expect.ErrorMessageSubstr): + """Tests invalid arguments to --loop-fusion.""" + + spirv_args = ['--loop-fusion=-10'] + expected_error_substr = 'must have a positive integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLoopFusionArgsInvalidNumber(expect.ErrorMessageSubstr): + """Tests invalid arguments to --loop-fusion.""" + + spirv_args = ['--loop-fusion=a10f'] + expected_error_substr = 'must have a positive integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLoopUnrollPartialArgsNegative(expect.ErrorMessageSubstr): + """Tests invalid arguments to --loop-unroll-partial.""" + + spirv_args = ['--loop-unroll-partial=-10'] + expected_error_substr = 'must have a positive integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLoopUnrollPartialArgsInvalidNumber(expect.ErrorMessageSubstr): + """Tests invalid arguments to --loop-unroll-partial.""" + + spirv_args = ['--loop-unroll-partial=a10f'] + expected_error_substr = 'must have a positive integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLoopPeelingThresholdArgsNegative(expect.ErrorMessageSubstr): + """Tests invalid arguments to --loop-peeling-threshold.""" + + spirv_args = ['--loop-peeling-threshold=-10'] + expected_error_substr = 'must have a positive integer argument' + + +@inside_spirv_testsuite('SpirvOptFlags') +class TestLoopPeelingThresholdArgsInvalidNumber(expect.ErrorMessageSubstr): + """Tests invalid arguments to --loop-peeling-threshold.""" + + spirv_args = ['--loop-peeling-threshold=a10f'] + expected_error_substr = 'must have a positive integer argument' + +@inside_spirv_testsuite('SpirvOptFlags') +class TestWebGPUToVulkanThenVulkanToWebGPUIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): + """Tests Vulkan->WebGPU flag cannot be used after WebGPU->Vulkan flag.""" + + spirv_args = ['--webgpu-to-vulkan', '--vulkan-to-webgpu'] + expected_error_substr = 'Cannot use both' + +@inside_spirv_testsuite('SpirvOptFlags') +class TestVulkanToWebGPUThenWebGPUToVulkanIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): + """Tests WebGPU->Vulkan flag cannot be used after Vulkan->WebGPU flag.""" + + spirv_args = ['--vulkan-to-webgpu', '--webgpu-to-vulkan'] + expected_error_substr = 'Cannot use both' + +@inside_spirv_testsuite('SpirvOptFlags') +class TestTargetEnvThenVulkanToWebGPUIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): + """Tests Vulkan->WebGPU flag cannot be used after target env flag.""" + + spirv_args = ['--target-env=opengl4.0', '--vulkan-to-webgpu'] + expected_error_substr = 'defines the target environment' + +@inside_spirv_testsuite('SpirvOptFlags') +class TestVulkanToWebGPUThenTargetEnvIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): + """Tests target env flag cannot be used after Vulkan->WebGPU flag.""" + + spirv_args = ['--vulkan-to-webgpu', '--target-env=opengl4.0'] + expected_error_substr = 'defines the target environment' + +@inside_spirv_testsuite('SpirvOptFlags') +class TestTargetEnvThenWebGPUToVulkanIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): + """Tests WebGPU->Vulkan flag cannot be used after target env flag.""" + + spirv_args = ['--target-env=opengl4.0', '--webgpu-to-vulkan'] + expected_error_substr = 'defines the target environment' + +@inside_spirv_testsuite('SpirvOptFlags') +class TestWebGPUToVulkanThenTargetEnvIsInvalid(expect.ReturnCodeIsNonZero, expect.ErrorMessageSubstr): + """Tests target env flag cannot be used after WebGPU->Vulkan flag.""" + + spirv_args = ['--webgpu-to-vulkan', '--target-env=opengl4.0'] + expected_error_substr = 'defines the target environment' diff --git a/third_party/spirv-tools/test/tools/opt/oconfig.py b/third_party/spirv-tools/test/tools/opt/oconfig.py new file mode 100644 index 0000000..899d93e --- /dev/null +++ b/third_party/spirv-tools/test/tools/opt/oconfig.py @@ -0,0 +1,73 @@ +# Copyright (c) 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import placeholder +import expect +import re + +from spirv_test_framework import inside_spirv_testsuite + + +def empty_main_assembly(): + return """ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd""" + + +@inside_spirv_testsuite('SpirvOptConfigFile') +class TestOconfigEmpty(expect.SuccessfulReturn): + """Tests empty config files are accepted.""" + + shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') + config = placeholder.ConfigFlagsFile('', '.cfg') + spirv_args = [shader, '-o', placeholder.TempFileName('output.spv'), config] + + +@inside_spirv_testsuite('SpirvOptConfigFile') +class TestOconfigComments(expect.SuccessfulReturn): + """Tests empty config files are accepted. + + https://github.com/KhronosGroup/SPIRV-Tools/issues/1778 + """ + + shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') + config = placeholder.ConfigFlagsFile(""" +# This is a comment. +-O +--loop-unroll +""", '.cfg') + spirv_args = [shader, '-o', placeholder.TempFileName('output.spv'), config] + +@inside_spirv_testsuite('SpirvOptConfigFile') +class TestOconfigComments(expect.SuccessfulReturn): + """Tests empty config files are accepted. + + https://github.com/KhronosGroup/SPIRV-Tools/issues/1778 + """ + + shader = placeholder.FileSPIRVShader(empty_main_assembly(), '.spvasm') + config = placeholder.ConfigFlagsFile(""" +# This is a comment. +-O +--relax-struct-store +""", '.cfg') + spirv_args = [shader, '-o', placeholder.TempFileName('output.spv'), config] diff --git a/third_party/spirv-tools/test/tools/placeholder.py b/third_party/spirv-tools/test/tools/placeholder.py new file mode 100755 index 0000000..7de3c46 --- /dev/null +++ b/third_party/spirv-tools/test/tools/placeholder.py @@ -0,0 +1,213 @@ +# Copyright (c) 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""A number of placeholders and their rules for expansion when used in tests. + +These placeholders, when used in spirv_args or expected_* variables of +SpirvTest, have special meanings. In spirv_args, they will be substituted by +the result of instantiate_for_spirv_args(), while in expected_*, by +instantiate_for_expectation(). A TestCase instance will be passed in as +argument to the instantiate_*() methods. +""" + +import os +import subprocess +import tempfile +from string import Template + + +class PlaceHolderException(Exception): + """Exception class for PlaceHolder.""" + pass + + +class PlaceHolder(object): + """Base class for placeholders.""" + + def instantiate_for_spirv_args(self, testcase): + """Instantiation rules for spirv_args. + + This method will be called when the current placeholder appears in + spirv_args. + + Returns: + A string to replace the current placeholder in spirv_args. + """ + raise PlaceHolderException('Subclass should implement this function.') + + def instantiate_for_expectation(self, testcase): + """Instantiation rules for expected_*. + + This method will be called when the current placeholder appears in + expected_*. + + Returns: + A string to replace the current placeholder in expected_*. + """ + raise PlaceHolderException('Subclass should implement this function.') + + +class FileShader(PlaceHolder): + """Stands for a shader whose source code is in a file.""" + + def __init__(self, source, suffix, assembly_substr=None): + assert isinstance(source, str) + assert isinstance(suffix, str) + self.source = source + self.suffix = suffix + self.filename = None + # If provided, this is a substring which is expected to be in + # the disassembly of the module generated from this input file. + self.assembly_substr = assembly_substr + + def instantiate_for_spirv_args(self, testcase): + """Creates a temporary file and writes the source into it. + + Returns: + The name of the temporary file. + """ + shader, self.filename = tempfile.mkstemp( + dir=testcase.directory, suffix=self.suffix) + shader_object = os.fdopen(shader, 'w') + shader_object.write(self.source) + shader_object.close() + return self.filename + + def instantiate_for_expectation(self, testcase): + assert self.filename is not None + return self.filename + + +class ConfigFlagsFile(PlaceHolder): + """Stands for a configuration file for spirv-opt generated out of a string.""" + + def __init__(self, content, suffix): + assert isinstance(content, str) + assert isinstance(suffix, str) + self.content = content + self.suffix = suffix + self.filename = None + + def instantiate_for_spirv_args(self, testcase): + """Creates a temporary file and writes content into it. + + Returns: + The name of the temporary file. + """ + temp_fd, self.filename = tempfile.mkstemp( + dir=testcase.directory, suffix=self.suffix) + fd = os.fdopen(temp_fd, 'w') + fd.write(self.content) + fd.close() + return '-Oconfig=%s' % self.filename + + def instantiate_for_expectation(self, testcase): + assert self.filename is not None + return self.filename + + +class FileSPIRVShader(PlaceHolder): + """Stands for a source shader file which must be converted to SPIR-V.""" + + def __init__(self, source, suffix, assembly_substr=None): + assert isinstance(source, str) + assert isinstance(suffix, str) + self.source = source + self.suffix = suffix + self.filename = None + # If provided, this is a substring which is expected to be in + # the disassembly of the module generated from this input file. + self.assembly_substr = assembly_substr + + def instantiate_for_spirv_args(self, testcase): + """Creates a temporary file, writes the source into it and assembles it. + + Returns: + The name of the assembled temporary file. + """ + shader, asm_filename = tempfile.mkstemp( + dir=testcase.directory, suffix=self.suffix) + shader_object = os.fdopen(shader, 'w') + shader_object.write(self.source) + shader_object.close() + self.filename = '%s.spv' % asm_filename + cmd = [ + testcase.test_manager.assembler_path, asm_filename, '-o', self.filename + ] + process = subprocess.Popen( + args=cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=testcase.directory) + output = process.communicate() + assert process.returncode == 0 and not output[0] and not output[1] + return self.filename + + def instantiate_for_expectation(self, testcase): + assert self.filename is not None + return self.filename + + +class StdinShader(PlaceHolder): + """Stands for a shader whose source code is from stdin.""" + + def __init__(self, source): + assert isinstance(source, str) + self.source = source + self.filename = None + + def instantiate_for_spirv_args(self, testcase): + """Writes the source code back to the TestCase instance.""" + testcase.stdin_shader = self.source + self.filename = '-' + return self.filename + + def instantiate_for_expectation(self, testcase): + assert self.filename is not None + return self.filename + + +class TempFileName(PlaceHolder): + """Stands for a temporary file's name.""" + + def __init__(self, filename): + assert isinstance(filename, str) + assert filename != '' + self.filename = filename + + def instantiate_for_spirv_args(self, testcase): + return os.path.join(testcase.directory, self.filename) + + def instantiate_for_expectation(self, testcase): + return os.path.join(testcase.directory, self.filename) + + +class SpecializedString(PlaceHolder): + """Returns a string that has been specialized based on TestCase. + + The string is specialized by expanding it as a string.Template + with all of the specialization being done with each $param replaced + by the associated member on TestCase. + """ + + def __init__(self, filename): + assert isinstance(filename, str) + assert filename != '' + self.filename = filename + + def instantiate_for_spirv_args(self, testcase): + return Template(self.filename).substitute(vars(testcase)) + + def instantiate_for_expectation(self, testcase): + return Template(self.filename).substitute(vars(testcase)) diff --git a/third_party/spirv-tools/test/tools/spirv_test_framework.py b/third_party/spirv-tools/test/tools/spirv_test_framework.py new file mode 100755 index 0000000..542f144 --- /dev/null +++ b/third_party/spirv-tools/test/tools/spirv_test_framework.py @@ -0,0 +1,394 @@ +# Copyright (c) 2018 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Manages and runs tests from the current working directory. + +This will traverse the current working directory and look for python files that +contain subclasses of SpirvTest. + +If a class has an @inside_spirv_testsuite decorator, an instance of that +class will be created and serve as a test case in that testsuite. The test +case is then run by the following steps: + + 1. A temporary directory will be created. + 2. The spirv_args member variable will be inspected and all placeholders in it + will be expanded by calling instantiate_for_spirv_args() on placeholders. + The transformed list elements are then supplied as arguments to the spirv-* + tool under test. + 3. If the environment member variable exists, its write() method will be + invoked. + 4. All expected_* member variables will be inspected and all placeholders in + them will be expanded by calling instantiate_for_expectation() on those + placeholders. After placeholder expansion, if the expected_* variable is + a list, its element will be joined together with '' to form a single + string. These expected_* variables are to be used by the check_*() methods. + 5. The spirv-* tool will be run with the arguments supplied in spirv_args. + 6. All check_*() member methods will be called by supplying a TestStatus as + argument. Each check_*() method is expected to return a (Success, Message) + pair where Success is a boolean indicating success and Message is an error + message. + 7. If any check_*() method fails, the error message is output and the + current test case fails. + +If --leave-output was not specified, all temporary files and directories will +be deleted. +""" + +import argparse +import fnmatch +import inspect +import os +import shutil +import subprocess +import sys +import tempfile +from collections import defaultdict +from placeholder import PlaceHolder + +EXPECTED_BEHAVIOR_PREFIX = 'expected_' +VALIDATE_METHOD_PREFIX = 'check_' + + +def get_all_variables(instance): + """Returns the names of all the variables in instance.""" + return [v for v in dir(instance) if not callable(getattr(instance, v))] + + +def get_all_methods(instance): + """Returns the names of all methods in instance.""" + return [m for m in dir(instance) if callable(getattr(instance, m))] + + +def get_all_superclasses(cls): + """Returns all superclasses of a given class. Omits root 'object' superclass. + + Returns: + A list of superclasses of the given class. The order guarantees that + * A Base class precedes its derived classes, e.g., for "class B(A)", it + will be [..., A, B, ...]. + * When there are multiple base classes, base classes declared first + precede those declared later, e.g., for "class C(A, B), it will be + [..., A, B, C, ...] + """ + classes = [] + for superclass in cls.__bases__: + for c in get_all_superclasses(superclass): + if c is not object and c not in classes: + classes.append(c) + for superclass in cls.__bases__: + if superclass is not object and superclass not in classes: + classes.append(superclass) + + return classes + + +def get_all_test_methods(test_class): + """Gets all validation methods. + + Returns: + A list of validation methods. The order guarantees that + * A method defined in superclass precedes one defined in subclass, + e.g., for "class A(B)", methods defined in B precedes those defined + in A. + * If a subclass has more than one superclass, e.g., "class C(A, B)", + then methods defined in A precedes those defined in B. + """ + classes = get_all_superclasses(test_class) + classes.append(test_class) + all_tests = [ + m for c in classes for m in get_all_methods(c) + if m.startswith(VALIDATE_METHOD_PREFIX) + ] + unique_tests = [] + for t in all_tests: + if t not in unique_tests: + unique_tests.append(t) + return unique_tests + + +class SpirvTest: + """Base class for spirv test cases. + + Subclasses define test cases' facts (shader source code, spirv command, + result validation), which will be used by the TestCase class for running + tests. Subclasses should define spirv_args (specifying spirv_tool command + arguments), and at least one check_*() method (for result validation) for + a full-fledged test case. All check_*() methods should take a TestStatus + parameter and return a (Success, Message) pair, in which Success is a + boolean indicating success and Message is an error message. The test passes + iff all check_*() methods returns true. + + Often, a test case class will delegate the check_* behaviors by inheriting + from other classes. + """ + + def name(self): + return self.__class__.__name__ + + +class TestStatus: + """A struct for holding run status of a test case.""" + + def __init__(self, test_manager, returncode, stdout, stderr, directory, + inputs, input_filenames): + self.test_manager = test_manager + self.returncode = returncode + # Some of our MacOS bots still run Python 2, so need to be backwards + # compatible here. + if type(stdout) is not str: + if sys.version_info[0] == 2: + self.stdout = stdout.decode('utf-8') + elif sys.version_info[0] == 3: + self.stdout = str(stdout, encoding='utf-8') if stdout is not None else stdout + else: + raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info)) + else: + self.stdout = stdout + + if type(stderr) is not str: + if sys.version_info[0] == 2: + self.stderr = stderr.decode('utf-8') + elif sys.version_info[0] == 3: + self.stderr = str(stderr, encoding='utf-8') if stderr is not None else stderr + else: + raise Exception('Unable to determine if running Python 2 or 3 from {}'.format(sys.version_info)) + else: + self.stderr = stderr + + # temporary directory where the test runs + self.directory = directory + # List of inputs, as PlaceHolder objects. + self.inputs = inputs + # the names of input shader files (potentially including paths) + self.input_filenames = input_filenames + + +class SpirvTestException(Exception): + """SpirvTest exception class.""" + pass + + +def inside_spirv_testsuite(testsuite_name): + """Decorator for subclasses of SpirvTest. + + This decorator checks that a class meets the requirements (see below) + for a test case class, and then puts the class in a certain testsuite. + * The class needs to be a subclass of SpirvTest. + * The class needs to have spirv_args defined as a list. + * The class needs to define at least one check_*() methods. + * All expected_* variables required by check_*() methods can only be + of bool, str, or list type. + * Python runtime will throw an exception if the expected_* member + attributes required by check_*() methods are missing. + """ + + def actual_decorator(cls): + if not inspect.isclass(cls): + raise SpirvTestException('Test case should be a class') + if not issubclass(cls, SpirvTest): + raise SpirvTestException( + 'All test cases should be subclasses of SpirvTest') + if 'spirv_args' not in get_all_variables(cls): + raise SpirvTestException('No spirv_args found in the test case') + if not isinstance(cls.spirv_args, list): + raise SpirvTestException('spirv_args needs to be a list') + if not any( + [m.startswith(VALIDATE_METHOD_PREFIX) for m in get_all_methods(cls)]): + raise SpirvTestException('No check_*() methods found in the test case') + if not all( + [isinstance(v, (bool, str, list)) for v in get_all_variables(cls)]): + raise SpirvTestException( + 'expected_* variables are only allowed to be bool, str, or ' + 'list type.') + cls.parent_testsuite = testsuite_name + return cls + + return actual_decorator + + +class TestManager: + """Manages and runs a set of tests.""" + + def __init__(self, executable_path, assembler_path, disassembler_path): + self.executable_path = executable_path + self.assembler_path = assembler_path + self.disassembler_path = disassembler_path + self.num_successes = 0 + self.num_failures = 0 + self.num_tests = 0 + self.leave_output = False + self.tests = defaultdict(list) + + def notify_result(self, test_case, success, message): + """Call this to notify the manager of the results of a test run.""" + self.num_successes += 1 if success else 0 + self.num_failures += 0 if success else 1 + counter_string = str(self.num_successes + self.num_failures) + '/' + str( + self.num_tests) + print('%-10s %-40s ' % (counter_string, test_case.test.name()) + + ('Passed' if success else '-Failed-')) + if not success: + print(' '.join(test_case.command)) + print(message) + + def add_test(self, testsuite, test): + """Add this to the current list of test cases.""" + self.tests[testsuite].append(TestCase(test, self)) + self.num_tests += 1 + + def run_tests(self): + for suite in self.tests: + print('SPIRV tool test suite: "{suite}"'.format(suite=suite)) + for x in self.tests[suite]: + x.runTest() + + +class TestCase: + """A single test case that runs in its own directory.""" + + def __init__(self, test, test_manager): + self.test = test + self.test_manager = test_manager + self.inputs = [] # inputs, as PlaceHolder objects. + self.file_shaders = [] # filenames of shader files. + self.stdin_shader = None # text to be passed to spirv_tool as stdin + + def setUp(self): + """Creates environment and instantiates placeholders for the test case.""" + + self.directory = tempfile.mkdtemp(dir=os.getcwd()) + spirv_args = self.test.spirv_args + # Instantiate placeholders in spirv_args + self.test.spirv_args = [ + arg.instantiate_for_spirv_args(self) + if isinstance(arg, PlaceHolder) else arg for arg in self.test.spirv_args + ] + # Get all shader files' names + self.inputs = [arg for arg in spirv_args if isinstance(arg, PlaceHolder)] + self.file_shaders = [arg.filename for arg in self.inputs] + + if 'environment' in get_all_variables(self.test): + self.test.environment.write(self.directory) + + expectations = [ + v for v in get_all_variables(self.test) + if v.startswith(EXPECTED_BEHAVIOR_PREFIX) + ] + # Instantiate placeholders in expectations + for expectation_name in expectations: + expectation = getattr(self.test, expectation_name) + if isinstance(expectation, list): + expanded_expections = [ + element.instantiate_for_expectation(self) + if isinstance(element, PlaceHolder) else element + for element in expectation + ] + setattr(self.test, expectation_name, expanded_expections) + elif isinstance(expectation, PlaceHolder): + setattr(self.test, expectation_name, + expectation.instantiate_for_expectation(self)) + + def tearDown(self): + """Removes the directory if we were not instructed to do otherwise.""" + if not self.test_manager.leave_output: + shutil.rmtree(self.directory) + + def runTest(self): + """Sets up and runs a test, reports any failures and then cleans up.""" + self.setUp() + success = False + message = '' + try: + self.command = [self.test_manager.executable_path] + self.command.extend(self.test.spirv_args) + + process = subprocess.Popen( + args=self.command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=self.directory) + output = process.communicate(self.stdin_shader) + test_status = TestStatus(self.test_manager, process.returncode, output[0], + output[1], self.directory, self.inputs, + self.file_shaders) + run_results = [ + getattr(self.test, test_method)(test_status) + for test_method in get_all_test_methods(self.test.__class__) + ] + success, message = zip(*run_results) + success = all(success) + message = '\n'.join(message) + except Exception as e: + success = False + message = str(e) + self.test_manager.notify_result( + self, success, + message + '\nSTDOUT:\n%s\nSTDERR:\n%s' % (output[0], output[1])) + self.tearDown() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + 'spirv_tool', + metavar='path/to/spirv_tool', + type=str, + nargs=1, + help='Path to the spirv-* tool under test') + parser.add_argument( + 'spirv_as', + metavar='path/to/spirv-as', + type=str, + nargs=1, + help='Path to spirv-as') + parser.add_argument( + 'spirv_dis', + metavar='path/to/spirv-dis', + type=str, + nargs=1, + help='Path to spirv-dis') + parser.add_argument( + '--leave-output', + action='store_const', + const=1, + help='Do not clean up temporary directories') + parser.add_argument( + '--test-dir', nargs=1, help='Directory to gather the tests from') + args = parser.parse_args() + default_path = sys.path + root_dir = os.getcwd() + if args.test_dir: + root_dir = args.test_dir[0] + manager = TestManager(args.spirv_tool[0], args.spirv_as[0], args.spirv_dis[0]) + if args.leave_output: + manager.leave_output = True + for root, _, filenames in os.walk(root_dir): + for filename in fnmatch.filter(filenames, '*.py'): + if filename.endswith('nosetest.py'): + # Skip nose tests, which are for testing functions of + # the test framework. + continue + sys.path = default_path + sys.path.append(root) + mod = __import__(os.path.splitext(filename)[0]) + for _, obj, in inspect.getmembers(mod): + if inspect.isclass(obj) and hasattr(obj, 'parent_testsuite'): + manager.add_test(obj.parent_testsuite, obj()) + manager.run_tests() + if manager.num_failures > 0: + sys.exit(-1) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/test/tools/spirv_test_framework_unittest.py b/third_party/spirv-tools/test/tools/spirv_test_framework_unittest.py new file mode 100644 index 0000000..e64e86c --- /dev/null +++ b/third_party/spirv-tools/test/tools/spirv_test_framework_unittest.py @@ -0,0 +1,158 @@ +# Copyright (c) 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests for the spirv test framework module.""" + +from spirv_test_framework import get_all_test_methods, get_all_superclasses +import unittest + +# Classes to be used in testing get_all_{superclasses|test_methods}() + + +class Root: + + def check_root(self): + pass + + +class A(Root): + + def check_a(self): + pass + + +class B(Root): + + def check_b(self): + pass + + +class C(Root): + + def check_c(self): + pass + + +class D(Root): + + def check_d(self): + pass + + +class E(Root): + + def check_e(self): + pass + + +class H(B, C, D): + + def check_h(self): + pass + + +class I(E): + + def check_i(self): + pass + + +class O(H, I): + + def check_o(self): + pass + + +class U(A, O): + + def check_u(self): + pass + + +class X(U, A): + + def check_x(self): + pass + + +class R1: + + def check_r1(self): + pass + + +class R2: + + def check_r2(self): + pass + + +class Multi(R1, R2): + + def check_multi(self): + pass + + +class TestSpirvTestFramework(unittest.TestCase): + def test_get_all_superclasses(self): + self.assertEqual(get_all_superclasses(A), [Root]) + self.assertEqual(get_all_superclasses(B), [Root]) + self.assertEqual(get_all_superclasses(C), [Root]) + self.assertEqual(get_all_superclasses(D), [Root]) + self.assertEqual(get_all_superclasses(E), [Root]) + + self.assertEqual(get_all_superclasses(H), [Root, B, C, D]) + self.assertEqual(get_all_superclasses(I), [Root, E]) + + self.assertEqual(get_all_superclasses(O), [Root, B, C, D, E, H, I]) + + self.assertEqual(get_all_superclasses( + U), [Root, B, C, D, E, H, I, A, O]) + self.assertEqual(get_all_superclasses( + X), [Root, B, C, D, E, H, I, A, O, U]) + + self.assertEqual(get_all_superclasses(Multi), [R1, R2]) + + def test_get_all_methods(self): + self.assertEqual(get_all_test_methods(A), ['check_root', 'check_a']) + self.assertEqual(get_all_test_methods(B), ['check_root', 'check_b']) + self.assertEqual(get_all_test_methods(C), ['check_root', 'check_c']) + self.assertEqual(get_all_test_methods(D), ['check_root', 'check_d']) + self.assertEqual(get_all_test_methods(E), ['check_root', 'check_e']) + + self.assertEqual( + get_all_test_methods(H), + ['check_root', 'check_b', 'check_c', 'check_d', 'check_h']) + self.assertEqual(get_all_test_methods( + I), ['check_root', 'check_e', 'check_i']) + + self.assertEqual( + get_all_test_methods(O), [ + 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', + 'check_i', 'check_o' + ]) + + self.assertEqual( + get_all_test_methods(U), [ + 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', + 'check_i', 'check_a', 'check_o', 'check_u' + ]) + + self.assertEqual( + get_all_test_methods(X), [ + 'check_root', 'check_b', 'check_c', 'check_d', 'check_e', 'check_h', + 'check_i', 'check_a', 'check_o', 'check_u', 'check_x' + ]) + + self.assertEqual( + get_all_test_methods(Multi), ['check_r1', 'check_r2', 'check_multi']) diff --git a/third_party/spirv-tools/test/unit_spirv.cpp b/third_party/spirv-tools/test/unit_spirv.cpp new file mode 100644 index 0000000..33af273 --- /dev/null +++ b/third_party/spirv-tools/test/unit_spirv.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" + +namespace spvtools { +namespace { + +using utils::MakeVector; +using ::testing::Eq; +using Words = std::vector; + +TEST(MakeVector, Samples) { + EXPECT_THAT(MakeVector(""), Eq(Words{0})); + EXPECT_THAT(MakeVector("a"), Eq(Words{0x0061})); + EXPECT_THAT(MakeVector("ab"), Eq(Words{0x006261})); + EXPECT_THAT(MakeVector("abc"), Eq(Words{0x00636261})); + EXPECT_THAT(MakeVector("abcd"), Eq(Words{0x64636261, 0x00})); + EXPECT_THAT(MakeVector("abcde"), Eq(Words{0x64636261, 0x0065})); +} + +TEST(WordVectorPrintTo, PreservesFlagsAndFill) { + std::stringstream s; + s << std::setw(4) << std::oct << std::setfill('x') << 8 << " "; + spvtest::PrintTo(spvtest::WordVector({10, 16}), &s); + // The octal setting and fill character should be preserved + // from before the PrintTo. + // Width is reset after each emission of a regular scalar type. + // So set it explicitly again. + s << std::setw(4) << 9; + + EXPECT_THAT(s.str(), Eq("xx10 0x0000000a 0x00000010 xx11")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/unit_spirv.h b/third_party/spirv-tools/test/unit_spirv.h new file mode 100644 index 0000000..3264662 --- /dev/null +++ b/third_party/spirv-tools/test/unit_spirv.h @@ -0,0 +1,211 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_UNIT_SPIRV_H_ +#define TEST_UNIT_SPIRV_H_ + +#include + +#include +#include +#include + +#include "gtest/gtest.h" +#include "source/assembly_grammar.h" +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/enum_set.h" +#include "source/opcode.h" +#include "source/spirv_endian.h" +#include "source/text.h" +#include "source/text_handler.h" +#include "source/val/validate.h" +#include "spirv-tools/libspirv.h" + +#ifdef __ANDROID__ +#include +namespace std { +template +std::string to_string(const T& val) { + std::ostringstream os; + os << val; + return os.str(); +} +} // namespace std +#endif + +// Determine endianness & predicate tests on it +enum { + I32_ENDIAN_LITTLE = 0x03020100ul, + I32_ENDIAN_BIG = 0x00010203ul, +}; + +static const union { + unsigned char bytes[4]; + uint32_t value; +} o32_host_order = {{0, 1, 2, 3}}; +#define I32_ENDIAN_HOST (o32_host_order.value) + +// A namespace for utilities used in SPIR-V Tools unit tests. +namespace spvtest { + +class WordVector; + +// Emits the given word vector to the given stream. +// This function can be used by the gtest value printer. +void PrintTo(const WordVector& words, ::std::ostream* os); + +// A proxy class to allow us to easily write out vectors of SPIR-V words. +class WordVector { + public: + explicit WordVector(const std::vector& val) : value_(val) {} + explicit WordVector(const spv_binary_t& binary) + : value_(binary.code, binary.code + binary.wordCount) {} + + // Returns the underlying vector. + const std::vector& value() const { return value_; } + + // Returns the string representation of this word vector. + std::string str() const { + std::ostringstream os; + PrintTo(*this, &os); + return os.str(); + } + + private: + const std::vector value_; +}; + +inline void PrintTo(const WordVector& words, ::std::ostream* os) { + size_t count = 0; + const auto saved_flags = os->flags(); + const auto saved_fill = os->fill(); + for (uint32_t value : words.value()) { + *os << "0x" << std::setw(8) << std::setfill('0') << std::hex << value + << " "; + if (count++ % 8 == 7) { + *os << std::endl; + } + } + os->flags(saved_flags); + os->fill(saved_fill); +} + +// Returns a vector of words representing a single instruction with the +// given opcode and operand words as a vector. +inline std::vector MakeInstruction( + SpvOp opcode, const std::vector& args) { + std::vector result{ + spvOpcodeMake(uint16_t(args.size() + 1), opcode)}; + result.insert(result.end(), args.begin(), args.end()); + return result; +} + +// Returns a vector of words representing a single instruction with the +// given opcode and whose operands are the concatenation of the two given +// argument lists. +inline std::vector MakeInstruction( + SpvOp opcode, std::vector args, + const std::vector& extra_args) { + args.insert(args.end(), extra_args.begin(), extra_args.end()); + return MakeInstruction(opcode, args); +} + +// Returns the vector of words representing the concatenation +// of all input vectors. +inline std::vector Concatenate( + const std::vector>& instructions) { + std::vector result; + for (const auto& instruction : instructions) { + result.insert(result.end(), instruction.begin(), instruction.end()); + } + return result; +} + +// A type for easily creating spv_text_t values, with an implicit conversion to +// spv_text. +struct AutoText { + explicit AutoText(const std::string& value) + : str(value), text({str.data(), str.size()}) {} + operator spv_text() { return &text; } + std::string str; + spv_text_t text; +}; + +// An example case for an enumerated value, optionally with operands. +template +class EnumCase { + public: + EnumCase() = default; // Required by ::testing::Combine(). + EnumCase(E val, std::string enum_name, std::vector ops = {}) + : enum_value_(val), name_(enum_name), operands_(ops) {} + // Returns the enum value as a uint32_t. + uint32_t value() const { return static_cast(enum_value_); } + // Returns the name of the enumerant. + const std::string& name() const { return name_; } + // Returns a reference to the operands. + const std::vector& operands() const { return operands_; } + + private: + E enum_value_; + std::string name_; + std::vector operands_; +}; + +// Returns a string with num_4_byte_chars Unicode characters, +// each of which has a 4-byte UTF-8 encoding. +inline std::string MakeLongUTF8String(size_t num_4_byte_chars) { + // An example of a longest valid UTF-8 character. + // Be explicit about the character type because Microsoft compilers can + // otherwise interpret the character string as being over wide (16-bit) + // characters. Ideally, we would just use a C++11 UTF-8 string literal, + // but we want to support older Microsoft compilers. + const std::basic_string earth_africa("\xF0\x9F\x8C\x8D"); + EXPECT_EQ(4u, earth_africa.size()); + + std::string result; + result.reserve(num_4_byte_chars * 4); + for (size_t i = 0; i < num_4_byte_chars; i++) { + result += earth_africa; + } + EXPECT_EQ(4 * num_4_byte_chars, result.size()); + return result; +} + +// Returns a vector of all valid target environment enums. +inline std::vector AllTargetEnvironments() { + return { + SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_EMBEDDED_1_2, + SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_EMBEDDED_2_0, + SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_EMBEDDED_2_1, + SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_EMBEDDED_2_2, + SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_0, + SPV_ENV_OPENGL_4_1, SPV_ENV_OPENGL_4_2, + SPV_ENV_OPENGL_4_3, SPV_ENV_OPENGL_4_5, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, + SPV_ENV_VULKAN_1_1, SPV_ENV_WEBGPU_0, + }; +} + +// Returns the capabilities in a CapabilitySet as an ordered vector. +inline std::vector ElementsIn( + const spvtools::CapabilitySet& capabilities) { + std::vector result; + capabilities.ForEach([&result](SpvCapability c) { result.push_back(c); }); + return result; +} + +} // namespace spvtest +#endif // TEST_UNIT_SPIRV_H_ diff --git a/third_party/spirv-tools/test/util/CMakeLists.txt b/third_party/spirv-tools/test/util/CMakeLists.txt new file mode 100644 index 0000000..6679dba --- /dev/null +++ b/third_party/spirv-tools/test/util/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) 2017 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_spvtools_unittest(TARGET utils + SRCS ilist_test.cpp + bit_vector_test.cpp + bitutils_test.cpp + small_vector_test.cpp + LIBS SPIRV-Tools-opt +) diff --git a/third_party/spirv-tools/test/util/bit_vector_test.cpp b/third_party/spirv-tools/test/util/bit_vector_test.cpp new file mode 100644 index 0000000..8d967f8 --- /dev/null +++ b/third_party/spirv-tools/test/util/bit_vector_test.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" + +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace utils { +namespace { + +using BitVectorTest = ::testing::Test; + +TEST(BitVectorTest, Initialize) { + BitVector bvec; + + // Checks that all values are 0. Also tests checking a bit past the end of + // the vector containing the bits. + for (int i = 1; i < 10000; i *= 2) { + EXPECT_FALSE(bvec.Get(i)); + } +} + +TEST(BitVectorTest, Set) { + BitVector bvec; + + // Since 10,000 is larger than the initial size, this tests the resizing + // code. + for (int i = 3; i < 10000; i *= 2) { + bvec.Set(i); + } + + // Check that bits that were not set are 0. + for (int i = 1; i < 10000; i *= 2) { + EXPECT_FALSE(bvec.Get(i)); + } + + // Check that bits that were set are 1. + for (int i = 3; i < 10000; i *= 2) { + EXPECT_TRUE(bvec.Get(i)); + } +} + +TEST(BitVectorTest, SetReturnValue) { + BitVector bvec; + + // Make sure |Set| returns false when the bit was not set. + for (int i = 3; i < 10000; i *= 2) { + EXPECT_FALSE(bvec.Set(i)); + } + + // Make sure |Set| returns true when the bit was already set. + for (int i = 3; i < 10000; i *= 2) { + EXPECT_TRUE(bvec.Set(i)); + } +} + +TEST(BitVectorTest, Clear) { + BitVector bvec; + for (int i = 3; i < 10000; i *= 2) { + bvec.Set(i); + } + + // Check that the bits were properly set. + for (int i = 3; i < 10000; i *= 2) { + EXPECT_TRUE(bvec.Get(i)); + } + + // Clear all of the bits except for bit 3. + for (int i = 6; i < 10000; i *= 2) { + bvec.Clear(i); + } + + // Make sure bit 3 was not cleared. + EXPECT_TRUE(bvec.Get(3)); + + // Make sure all of the other bits that were set have been cleared. + for (int i = 6; i < 10000; i *= 2) { + EXPECT_FALSE(bvec.Get(i)); + } +} + +TEST(BitVectorTest, ClearReturnValue) { + BitVector bvec; + for (int i = 3; i < 10000; i *= 2) { + bvec.Set(i); + } + + // Make sure |Clear| returns true if the bit was set. + for (int i = 3; i < 10000; i *= 2) { + EXPECT_TRUE(bvec.Clear(i)); + } + + // Make sure |Clear| returns false if the bit was not set. + for (int i = 3; i < 10000; i *= 2) { + EXPECT_FALSE(bvec.Clear(i)); + } +} + +TEST(BitVectorTest, SimpleOrTest) { + BitVector bvec1; + bvec1.Set(3); + bvec1.Set(4); + + BitVector bvec2; + bvec2.Set(2); + bvec2.Set(4); + + // Check that |bvec1| changed when doing the |Or| operation. + EXPECT_TRUE(bvec1.Or(bvec2)); + + // Check that the values are all correct. + EXPECT_FALSE(bvec1.Get(0)); + EXPECT_FALSE(bvec1.Get(1)); + EXPECT_TRUE(bvec1.Get(2)); + EXPECT_TRUE(bvec1.Get(3)); + EXPECT_TRUE(bvec1.Get(4)); +} + +TEST(BitVectorTest, ResizingOrTest) { + BitVector bvec1; + bvec1.Set(3); + bvec1.Set(4); + + BitVector bvec2; + bvec2.Set(10000); + + // Similar to above except with a large value to test resizing. + EXPECT_TRUE(bvec1.Or(bvec2)); + EXPECT_FALSE(bvec1.Get(0)); + EXPECT_FALSE(bvec1.Get(1)); + EXPECT_FALSE(bvec1.Get(2)); + EXPECT_TRUE(bvec1.Get(3)); + EXPECT_TRUE(bvec1.Get(10000)); +} + +TEST(BitVectorTest, SubsetOrTest) { + BitVector bvec1; + bvec1.Set(3); + bvec1.Set(4); + + BitVector bvec2; + bvec2.Set(3); + + // |Or| returns false if |bvec1| does not change. + EXPECT_FALSE(bvec1.Or(bvec2)); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/util/bitutils_test.cpp b/third_party/spirv-tools/test/util/bitutils_test.cpp new file mode 100644 index 0000000..3be7ed2 --- /dev/null +++ b/third_party/spirv-tools/test/util/bitutils_test.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/util/bitutils.h" + +#include "gmock/gmock.h" + +namespace spvtools { +namespace utils { +namespace { + +using BitUtilsTest = ::testing::Test; + +TEST(BitUtilsTest, MutateBitsWholeWord) { + const uint32_t zero_u32 = 0; + const uint32_t max_u32 = ~0; + + EXPECT_EQ(MutateBits(zero_u32, 0, 0, false), zero_u32); + EXPECT_EQ(MutateBits(max_u32, 0, 0, false), max_u32); + EXPECT_EQ(MutateBits(zero_u32, 0, 32, false), zero_u32); + EXPECT_EQ(MutateBits(zero_u32, 0, 32, true), max_u32); + EXPECT_EQ(MutateBits(max_u32, 0, 32, true), max_u32); + EXPECT_EQ(MutateBits(max_u32, 0, 32, false), zero_u32); +} + +TEST(BitUtilsTest, MutateBitsLow) { + const uint32_t zero_u32 = 0; + const uint32_t one_u32 = 1; + const uint32_t max_u32 = ~0; + + EXPECT_EQ(MutateBits(zero_u32, 0, 1, false), zero_u32); + EXPECT_EQ(MutateBits(zero_u32, 0, 1, true), one_u32); + EXPECT_EQ(MutateBits(max_u32, 0, 1, true), max_u32); + EXPECT_EQ(MutateBits(one_u32, 0, 32, false), zero_u32); + EXPECT_EQ(MutateBits(one_u32, 0, 1, true), one_u32); + EXPECT_EQ(MutateBits(one_u32, 0, 1, false), zero_u32); + EXPECT_EQ(MutateBits(zero_u32, 0, 3, true), uint32_t(7)); + EXPECT_EQ(MutateBits(uint32_t(7), 0, 2, false), uint32_t(4)); +} + +TEST(BitUtilsTest, MutateBitsHigh) { + const uint8_t zero_u8 = 0; + const uint8_t one_u8 = 1; + const uint8_t max_u8 = 255; + + EXPECT_EQ(MutateBits(zero_u8, 7, 0, true), zero_u8); + EXPECT_EQ(MutateBits(zero_u8, 7, 1, true), uint8_t(128)); + EXPECT_EQ(MutateBits(one_u8, 7, 1, true), uint8_t(129)); + EXPECT_EQ(MutateBits(max_u8, 7, 1, true), max_u8); + EXPECT_EQ(MutateBits(max_u8, 7, 1, false), uint8_t(127)); + EXPECT_EQ(MutateBits(max_u8, 6, 2, true), max_u8); + EXPECT_EQ(MutateBits(max_u8, 6, 2, false), uint8_t(63)); +} + +TEST(BitUtilsTest, MutateBitsUint8Mid) { + const uint8_t zero_u8 = 0; + const uint8_t max_u8 = 255; + + EXPECT_EQ(MutateBits(zero_u8, 1, 2, true), uint8_t(6)); + EXPECT_EQ(MutateBits(max_u8, 1, 2, true), max_u8); + EXPECT_EQ(MutateBits(max_u8, 1, 2, false), uint8_t(0xF9)); + EXPECT_EQ(MutateBits(zero_u8, 2, 3, true), uint8_t(0x1C)); +} + +TEST(BitUtilsTest, MutateBitsUint64Mid) { + const uint64_t zero_u64 = 0; + const uint64_t max_u64 = ~zero_u64; + + EXPECT_EQ(MutateBits(zero_u64, 1, 2, true), uint64_t(6)); + EXPECT_EQ(MutateBits(max_u64, 1, 2, true), max_u64); + EXPECT_EQ(MutateBits(max_u64, 1, 2, false), uint64_t(0xFFFFFFFFFFFFFFF9)); + EXPECT_EQ(MutateBits(zero_u64, 2, 3, true), uint64_t(0x000000000000001C)); + EXPECT_EQ(MutateBits(zero_u64, 2, 35, true), uint64_t(0x0000001FFFFFFFFC)); + EXPECT_EQ(MutateBits(zero_u64, 36, 4, true), uint64_t(0x000000F000000000)); + EXPECT_EQ(MutateBits(max_u64, 36, 4, false), uint64_t(0xFFFFFF0FFFFFFFFF)); +} + +TEST(BitUtilsTest, SetHighBitsUint32) { + const uint32_t zero_u32 = 0; + const uint32_t one_u32 = 1; + const uint32_t max_u32 = ~zero_u32; + + EXPECT_EQ(SetHighBits(zero_u32, 0), zero_u32); + EXPECT_EQ(SetHighBits(zero_u32, 1), 0x80000000); + EXPECT_EQ(SetHighBits(one_u32, 1), 0x80000001); + EXPECT_EQ(SetHighBits(one_u32, 2), 0xC0000001); + EXPECT_EQ(SetHighBits(zero_u32, 31), 0xFFFFFFFE); + EXPECT_EQ(SetHighBits(zero_u32, 32), max_u32); + EXPECT_EQ(SetHighBits(max_u32, 32), max_u32); +} + +TEST(BitUtilsTest, ClearHighBitsUint32) { + const uint32_t zero_u32 = 0; + const uint32_t one_u32 = 1; + const uint32_t max_u32 = ~zero_u32; + + EXPECT_EQ(ClearHighBits(zero_u32, 0), zero_u32); + EXPECT_EQ(ClearHighBits(zero_u32, 1), zero_u32); + EXPECT_EQ(ClearHighBits(one_u32, 1), one_u32); + EXPECT_EQ(ClearHighBits(one_u32, 31), one_u32); + EXPECT_EQ(ClearHighBits(one_u32, 32), zero_u32); + EXPECT_EQ(ClearHighBits(max_u32, 0), max_u32); + EXPECT_EQ(ClearHighBits(max_u32, 1), 0x7FFFFFFF); + EXPECT_EQ(ClearHighBits(max_u32, 2), 0x3FFFFFFF); + EXPECT_EQ(ClearHighBits(max_u32, 31), one_u32); + EXPECT_EQ(ClearHighBits(max_u32, 32), zero_u32); +} + +TEST(BitUtilsTest, IsBitSetAtPositionZero) { + const uint32_t zero_u32 = 0; + for (size_t i = 0; i != 32; ++i) { + EXPECT_FALSE(IsBitAtPositionSet(zero_u32, i)); + } + + const uint8_t zero_u8 = 0; + for (size_t i = 0; i != 8; ++i) { + EXPECT_FALSE(IsBitAtPositionSet(zero_u8, i)); + } + + const uint64_t zero_u64 = 0; + for (size_t i = 0; i != 64; ++i) { + EXPECT_FALSE(IsBitAtPositionSet(zero_u64, i)); + } +} + +TEST(BitUtilsTest, IsBitSetAtPositionOne) { + const uint32_t one_u32 = 1; + for (size_t i = 0; i != 32; ++i) { + if (i == 0) { + EXPECT_TRUE(IsBitAtPositionSet(one_u32, i)); + } else { + EXPECT_FALSE(IsBitAtPositionSet(one_u32, i)); + } + } + + const uint32_t two_to_17_u32 = 1 << 17; + for (size_t i = 0; i != 32; ++i) { + if (i == 17) { + EXPECT_TRUE(IsBitAtPositionSet(two_to_17_u32, i)); + } else { + EXPECT_FALSE(IsBitAtPositionSet(two_to_17_u32, i)); + } + } + + const uint8_t two_to_4_u8 = 1 << 4; + for (size_t i = 0; i != 8; ++i) { + if (i == 4) { + EXPECT_TRUE(IsBitAtPositionSet(two_to_4_u8, i)); + } else { + EXPECT_FALSE(IsBitAtPositionSet(two_to_4_u8, i)); + } + } + + const uint64_t two_to_55_u64 = uint64_t(1) << 55; + for (size_t i = 0; i != 64; ++i) { + if (i == 55) { + EXPECT_TRUE(IsBitAtPositionSet(two_to_55_u64, i)); + } else { + EXPECT_FALSE(IsBitAtPositionSet(two_to_55_u64, i)); + } + } +} + +TEST(BitUtilsTest, IsBitSetAtPositionAll) { + const uint32_t max_u32 = ~0; + for (size_t i = 0; i != 32; ++i) { + EXPECT_TRUE(IsBitAtPositionSet(max_u32, i)); + } + + const uint32_t max_u8 = ~uint8_t(0); + for (size_t i = 0; i != 8; ++i) { + EXPECT_TRUE(IsBitAtPositionSet(max_u8, i)); + } + + const uint64_t max_u64 = ~uint64_t(0); + for (size_t i = 0; i != 64; ++i) { + EXPECT_TRUE(IsBitAtPositionSet(max_u64, i)); + } +} +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/util/ilist_test.cpp b/third_party/spirv-tools/test/util/ilist_test.cpp new file mode 100644 index 0000000..4a546f9 --- /dev/null +++ b/third_party/spirv-tools/test/util/ilist_test.cpp @@ -0,0 +1,325 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "source/util/ilist.h" + +namespace spvtools { +namespace utils { +namespace { + +using ::testing::ElementsAre; +using IListTest = ::testing::Test; + +class TestNode : public IntrusiveNodeBase { + public: + TestNode() : IntrusiveNodeBase() {} + int data_; +}; + +class TestList : public IntrusiveList { + public: + TestList() = default; + TestList(TestList&& that) : IntrusiveList(std::move(that)) {} + TestList& operator=(TestList&& that) { + static_cast&>(*this) = + static_cast&&>(that); + return *this; + } +}; + +// This test checks the push_back method, as well as using an iterator to +// traverse the list from begin() to end(). This implicitly test the +// PreviousNode and NextNode functions. +TEST(IListTest, PushBack) { + TestNode nodes[10]; + TestList list; + for (int i = 0; i < 10; i++) { + nodes[i].data_ = i; + list.push_back(&nodes[i]); + } + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); +} + +// Returns a list containing the values 0 to n-1 using the first n elements of +// nodes to build the list. +TestList BuildList(TestNode nodes[], int n) { + TestList list; + for (int i = 0; i < n; i++) { + nodes[i].data_ = i; + list.push_back(&nodes[i]); + } + return list; +} + +// Test decrementing begin() +TEST(IListTest, DecrementingBegin) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 10); + EXPECT_EQ(--list.begin(), list.end()); +} + +// Test incrementing end() +TEST(IListTest, IncrementingEnd1) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 10); + EXPECT_EQ((++list.end())->data_, 0); +} + +// Test incrementing end() should equal begin() +TEST(IListTest, IncrementingEnd2) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 10); + EXPECT_EQ(++list.end(), list.begin()); +} + +// Test decrementing end() +TEST(IListTest, DecrementingEnd) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 10); + EXPECT_EQ((--list.end())->data_, 9); +} + +// Test the move constructor for the list class. +TEST(IListTest, MoveConstructor) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 10); + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); +} + +// Using a const list so we can test the const_iterator. +TEST(IListTest, ConstIterator) { + TestNode nodes[10]; + const TestList list = BuildList(nodes, 10); + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); +} + +// Uses the move assignement instead of the move constructor. +TEST(IListTest, MoveAssignment) { + TestNode nodes[10]; + TestList list; + list = BuildList(nodes, 10); + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); +} + +// Test inserting a new element at the end of a list using the IntrusiveNodeBase +// "InsertAfter" function. +TEST(IListTest, InsertAfter1) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 5); + + nodes[5].data_ = 5; + nodes[5].InsertAfter(&nodes[4]); + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5)); +} + +// Test inserting a new element in the middle of a list using the +// IntrusiveNodeBase "InsertAfter" function. +TEST(IListTest, InsertAfter2) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 5); + + nodes[5].data_ = 5; + nodes[5].InsertAfter(&nodes[2]); + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 5, 3, 4)); +} + +// Test moving an element already in the list in the middle of a list using the +// IntrusiveNodeBase "InsertAfter" function. +TEST(IListTest, MoveUsingInsertAfter1) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 6); + + nodes[5].InsertAfter(&nodes[2]); + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 5, 3, 4)); +} + +// Move the element at the start of the list into the middle. +TEST(IListTest, MoveUsingInsertAfter2) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 6); + + nodes[0].InsertAfter(&nodes[2]); + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(1, 2, 0, 3, 4, 5)); +} + +// Move an element in the middle of the list to the end. +TEST(IListTest, MoveUsingInsertAfter3) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 6); + + nodes[2].InsertAfter(&nodes[5]); + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 3, 4, 5, 2)); +} + +// Removing an element from the middle of a list. +TEST(IListTest, Remove1) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 6); + + nodes[2].RemoveFromList(); + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 3, 4, 5)); +} + +// Removing an element from the beginning of the list. +TEST(IListTest, Remove2) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 6); + + nodes[0].RemoveFromList(); + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(1, 2, 3, 4, 5)); +} + +// Removing the last element of a list. +TEST(IListTest, Remove3) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 6); + + nodes[5].RemoveFromList(); + + std::vector output; + for (auto& i : list) output.push_back(i.data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4)); +} + +// Test that operator== and operator!= work properly for the iterator class. +TEST(IListTest, IteratorEqual) { + TestNode nodes[10]; + TestList list = BuildList(nodes, 6); + + std::vector output; + for (auto i = list.begin(); i != list.end(); ++i) + for (auto j = list.begin(); j != list.end(); ++j) + if (i == j) output.push_back(i->data_); + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5)); +} + +// Test MoveBefore. Moving into middle of a list. +TEST(IListTest, MoveBefore1) { + TestNode nodes[10]; + TestList list1 = BuildList(nodes, 6); + TestList list2 = BuildList(nodes + 6, 3); + + TestList::iterator insertion_point = list1.begin(); + ++insertion_point; + insertion_point.MoveBefore(&list2); + + std::vector output; + for (auto i = list1.begin(); i != list1.end(); ++i) { + output.push_back(i->data_); + } + + EXPECT_THAT(output, ElementsAre(0, 0, 1, 2, 1, 2, 3, 4, 5)); +} + +// Test MoveBefore. Moving to the start of a list. +TEST(IListTest, MoveBefore2) { + TestNode nodes[10]; + TestList list1 = BuildList(nodes, 6); + TestList list2 = BuildList(nodes + 6, 3); + + TestList::iterator insertion_point = list1.begin(); + insertion_point.MoveBefore(&list2); + + std::vector output; + for (auto i = list1.begin(); i != list1.end(); ++i) { + output.push_back(i->data_); + } + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 0, 1, 2, 3, 4, 5)); +} + +// Test MoveBefore. Moving to the end of a list. +TEST(IListTest, MoveBefore3) { + TestNode nodes[10]; + TestList list1 = BuildList(nodes, 6); + TestList list2 = BuildList(nodes + 6, 3); + + TestList::iterator insertion_point = list1.end(); + insertion_point.MoveBefore(&list2); + + std::vector output; + for (auto i = list1.begin(); i != list1.end(); ++i) { + output.push_back(i->data_); + } + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5, 0, 1, 2)); +} + +// Test MoveBefore. Moving an empty list. +TEST(IListTest, MoveBefore4) { + TestNode nodes[10]; + TestList list1 = BuildList(nodes, 6); + TestList list2; + + TestList::iterator insertion_point = list1.end(); + insertion_point.MoveBefore(&list2); + + std::vector output; + for (auto i = list1.begin(); i != list1.end(); ++i) { + output.push_back(i->data_); + } + + EXPECT_THAT(output, ElementsAre(0, 1, 2, 3, 4, 5)); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/util/small_vector_test.cpp b/third_party/spirv-tools/test/util/small_vector_test.cpp new file mode 100644 index 0000000..01d7df1 --- /dev/null +++ b/third_party/spirv-tools/test/util/small_vector_test.cpp @@ -0,0 +1,598 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "source/util/small_vector.h" + +namespace spvtools { +namespace utils { +namespace { + +using SmallVectorTest = ::testing::Test; + +TEST(SmallVectorTest, Initialize_default) { + SmallVector vec; + + EXPECT_TRUE(vec.empty()); + EXPECT_EQ(vec.size(), 0); + EXPECT_EQ(vec.begin(), vec.end()); +} + +TEST(SmallVectorTest, Initialize_list1) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_FALSE(vec.empty()); + EXPECT_EQ(vec.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec.size(); ++i) { + EXPECT_EQ(vec[i], result[i]); + } +} + +TEST(SmallVectorTest, Initialize_list2) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_FALSE(vec.empty()); + EXPECT_EQ(vec.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec.size(); ++i) { + EXPECT_EQ(vec[i], result[i]); + } +} + +TEST(SmallVectorTest, Initialize_copy1) { + SmallVector vec1 = {0, 1, 2, 3}; + SmallVector vec2(vec1); + + EXPECT_EQ(vec2.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec2.size(); ++i) { + EXPECT_EQ(vec2[i], result[i]); + } + + EXPECT_EQ(vec1, vec2); +} + +TEST(SmallVectorTest, Initialize_copy2) { + SmallVector vec1 = {0, 1, 2, 3}; + SmallVector vec2(vec1); + + EXPECT_EQ(vec2.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec2.size(); ++i) { + EXPECT_EQ(vec2[i], result[i]); + } + + EXPECT_EQ(vec1, vec2); +} + +TEST(SmallVectorTest, Initialize_copy_vec1) { + std::vector vec1 = {0, 1, 2, 3}; + SmallVector vec2(vec1); + + EXPECT_EQ(vec2.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec2.size(); ++i) { + EXPECT_EQ(vec2[i], result[i]); + } + + EXPECT_EQ(vec1, vec2); +} + +TEST(SmallVectorTest, Initialize_copy_vec2) { + std::vector vec1 = {0, 1, 2, 3}; + SmallVector vec2(vec1); + + EXPECT_EQ(vec2.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec2.size(); ++i) { + EXPECT_EQ(vec2[i], result[i]); + } + + EXPECT_EQ(vec1, vec2); +} + +TEST(SmallVectorTest, Initialize_move1) { + SmallVector vec1 = {0, 1, 2, 3}; + SmallVector vec2(std::move(vec1)); + + EXPECT_EQ(vec2.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec2.size(); ++i) { + EXPECT_EQ(vec2[i], result[i]); + } + EXPECT_TRUE(vec1.empty()); +} + +TEST(SmallVectorTest, Initialize_move2) { + SmallVector vec1 = {0, 1, 2, 3}; + SmallVector vec2(std::move(vec1)); + + EXPECT_EQ(vec2.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec2.size(); ++i) { + EXPECT_EQ(vec2[i], result[i]); + } + EXPECT_TRUE(vec1.empty()); +} + +TEST(SmallVectorTest, Initialize_move_vec1) { + std::vector vec1 = {0, 1, 2, 3}; + SmallVector vec2(std::move(vec1)); + + EXPECT_EQ(vec2.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec2.size(); ++i) { + EXPECT_EQ(vec2[i], result[i]); + } + EXPECT_TRUE(vec1.empty()); +} + +TEST(SmallVectorTest, Initialize_move_vec2) { + std::vector vec1 = {0, 1, 2, 3}; + SmallVector vec2(std::move(vec1)); + + EXPECT_EQ(vec2.size(), 4); + + uint32_t result[] = {0, 1, 2, 3}; + for (uint32_t i = 0; i < vec2.size(); ++i) { + EXPECT_EQ(vec2[i], result[i]); + } + EXPECT_TRUE(vec1.empty()); +} + +TEST(SmallVectorTest, Initialize_iterators1) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.size(), 4); + uint32_t result[] = {0, 1, 2, 3}; + + uint32_t i = 0; + for (uint32_t p : vec) { + EXPECT_EQ(p, result[i]); + i++; + } +} + +TEST(SmallVectorTest, Initialize_iterators2) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.size(), 4); + uint32_t result[] = {0, 1, 2, 3}; + + uint32_t i = 0; + for (uint32_t p : vec) { + EXPECT_EQ(p, result[i]); + i++; + } +} + +TEST(SmallVectorTest, Initialize_iterators3) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.size(), 4); + uint32_t result[] = {0, 1, 2, 3}; + + uint32_t i = 0; + for (SmallVector::iterator it = vec.begin(); it != vec.end(); + ++it) { + EXPECT_EQ(*it, result[i]); + i++; + } +} + +TEST(SmallVectorTest, Initialize_iterators4) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.size(), 4); + uint32_t result[] = {0, 1, 2, 3}; + + uint32_t i = 0; + for (SmallVector::iterator it = vec.begin(); it != vec.end(); + ++it) { + EXPECT_EQ(*it, result[i]); + i++; + } +} + +TEST(SmallVectorTest, Initialize_iterators_write1) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.size(), 4); + for (SmallVector::iterator it = vec.begin(); it != vec.end(); + ++it) { + *it *= 2; + } + + uint32_t result[] = {0, 2, 4, 6}; + + uint32_t i = 0; + for (SmallVector::iterator it = vec.begin(); it != vec.end(); + ++it) { + EXPECT_EQ(*it, result[i]); + i++; + } +} + +TEST(SmallVectorTest, Initialize_iterators_write2) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.size(), 4); + for (SmallVector::iterator it = vec.begin(); it != vec.end(); + ++it) { + *it *= 2; + } + + uint32_t result[] = {0, 2, 4, 6}; + + uint32_t i = 0; + for (SmallVector::iterator it = vec.begin(); it != vec.end(); + ++it) { + EXPECT_EQ(*it, result[i]); + i++; + } +} + +TEST(SmallVectorTest, Initialize_front) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.front(), 0); + for (SmallVector::iterator it = vec.begin(); it != vec.end(); + ++it) { + *it += 2; + } + EXPECT_EQ(vec.front(), 2); +} + +TEST(SmallVectorTest, Erase_element_front1) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.front(), 0); + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin()); + EXPECT_EQ(vec.front(), 1); + EXPECT_EQ(vec.size(), 3); +} + +TEST(SmallVectorTest, Erase_element_front2) { + SmallVector vec = {0, 1, 2, 3}; + + EXPECT_EQ(vec.front(), 0); + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin()); + EXPECT_EQ(vec.front(), 1); + EXPECT_EQ(vec.size(), 3); +} + +TEST(SmallVectorTest, Erase_element_back1) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {0, 1, 2}; + + EXPECT_EQ(vec[3], 3); + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin() + 3); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_element_back2) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {0, 1, 2}; + + EXPECT_EQ(vec[3], 3); + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin() + 3); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_element_middle1) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {0, 1, 3}; + + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin() + 2); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_element_middle2) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {0, 1, 3}; + + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin() + 2); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_range_1) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {}; + + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin(), vec.end()); + EXPECT_EQ(vec.size(), 0); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_range_2) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {}; + + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin(), vec.end()); + EXPECT_EQ(vec.size(), 0); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_range_3) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {2, 3}; + + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin(), vec.begin() + 2); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_range_4) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {2, 3}; + + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin(), vec.begin() + 2); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_range_5) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {0, 3}; + + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin() + 1, vec.begin() + 3); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Erase_range_6) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {0, 3}; + + EXPECT_EQ(vec.size(), 4); + vec.erase(vec.begin() + 1, vec.begin() + 3); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Push_back) { + SmallVector vec; + SmallVector result = {0, 1, 2, 3}; + + EXPECT_EQ(vec.size(), 0); + vec.push_back(0); + EXPECT_EQ(vec.size(), 1); + vec.push_back(1); + EXPECT_EQ(vec.size(), 2); + vec.push_back(2); + EXPECT_EQ(vec.size(), 3); + vec.push_back(3); + EXPECT_EQ(vec.size(), 4); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Emplace_back) { + SmallVector vec; + SmallVector result = {0, 1, 2, 3}; + + EXPECT_EQ(vec.size(), 0); + vec.emplace_back(0); + EXPECT_EQ(vec.size(), 1); + vec.emplace_back(1); + EXPECT_EQ(vec.size(), 2); + vec.emplace_back(2); + EXPECT_EQ(vec.size(), 3); + vec.emplace_back(3); + EXPECT_EQ(vec.size(), 4); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Clear) { + SmallVector vec = {0, 1, 2, 3}; + SmallVector result = {}; + + EXPECT_EQ(vec.size(), 4); + vec.clear(); + EXPECT_EQ(vec.size(), 0); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Insert1) { + SmallVector vec = {}; + SmallVector insert_values = {10, 11}; + SmallVector result = {10, 11}; + + EXPECT_EQ(vec.size(), 0); + auto ret = + vec.insert(vec.begin(), insert_values.begin(), insert_values.end()); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec, result); + EXPECT_EQ(*ret, 10); +} + +TEST(SmallVectorTest, Insert2) { + SmallVector vec = {}; + SmallVector insert_values = {10, 11, 12}; + SmallVector result = {10, 11, 12}; + + EXPECT_EQ(vec.size(), 0); + auto ret = + vec.insert(vec.begin(), insert_values.begin(), insert_values.end()); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); + EXPECT_EQ(*ret, 10); +} + +TEST(SmallVectorTest, Insert3) { + SmallVector vec = {0}; + SmallVector insert_values = {10, 11, 12}; + SmallVector result = {10, 11, 12, 0}; + + EXPECT_EQ(vec.size(), 1); + auto ret = + vec.insert(vec.begin(), insert_values.begin(), insert_values.end()); + EXPECT_EQ(vec.size(), 4); + EXPECT_EQ(vec, result); + EXPECT_EQ(*ret, 10); +} + +TEST(SmallVectorTest, Insert4) { + SmallVector vec = {0}; + SmallVector insert_values = {10, 11, 12}; + SmallVector result = {10, 11, 12, 0}; + + EXPECT_EQ(vec.size(), 1); + auto ret = + vec.insert(vec.begin(), insert_values.begin(), insert_values.end()); + EXPECT_EQ(vec.size(), 4); + EXPECT_EQ(vec, result); + EXPECT_EQ(*ret, 10); +} + +TEST(SmallVectorTest, Insert5) { + SmallVector vec = {0, 1, 2}; + SmallVector insert_values = {10, 11, 12}; + SmallVector result = {0, 1, 2, 10, 11, 12}; + + EXPECT_EQ(vec.size(), 3); + auto ret = vec.insert(vec.end(), insert_values.begin(), insert_values.end()); + EXPECT_EQ(vec.size(), 6); + EXPECT_EQ(vec, result); + EXPECT_EQ(*ret, 10); +} + +TEST(SmallVectorTest, Insert6) { + SmallVector vec = {0, 1, 2}; + SmallVector insert_values = {10, 11, 12}; + SmallVector result = {0, 1, 2, 10, 11, 12}; + + EXPECT_EQ(vec.size(), 3); + auto ret = vec.insert(vec.end(), insert_values.begin(), insert_values.end()); + EXPECT_EQ(vec.size(), 6); + EXPECT_EQ(vec, result); + EXPECT_EQ(*ret, 10); +} + +TEST(SmallVectorTest, Insert7) { + SmallVector vec = {0, 1, 2}; + SmallVector insert_values = {10, 11, 12}; + SmallVector result = {0, 10, 11, 12, 1, 2}; + + EXPECT_EQ(vec.size(), 3); + auto ret = + vec.insert(vec.begin() + 1, insert_values.begin(), insert_values.end()); + EXPECT_EQ(vec.size(), 6); + EXPECT_EQ(vec, result); + EXPECT_EQ(*ret, 10); +} + +TEST(SmallVectorTest, Insert8) { + SmallVector vec = {0, 1, 2}; + SmallVector insert_values = {10, 11, 12}; + SmallVector result = {0, 10, 11, 12, 1, 2}; + + EXPECT_EQ(vec.size(), 3); + auto ret = + vec.insert(vec.begin() + 1, insert_values.begin(), insert_values.end()); + EXPECT_EQ(vec.size(), 6); + EXPECT_EQ(vec, result); + EXPECT_EQ(*ret, 10); +} + +TEST(SmallVectorTest, Resize1) { + SmallVector vec = {0, 1, 2}; + SmallVector result = {0, 1, 2, 10, 10, 10}; + + EXPECT_EQ(vec.size(), 3); + vec.resize(6, 10); + EXPECT_EQ(vec.size(), 6); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Resize2) { + SmallVector vec = {0, 1, 2}; + SmallVector result = {0, 1, 2, 10, 10, 10}; + + EXPECT_EQ(vec.size(), 3); + vec.resize(6, 10); + EXPECT_EQ(vec.size(), 6); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Resize3) { + SmallVector vec = {0, 1, 2}; + SmallVector result = {0, 1, 2, 10, 10, 10}; + + EXPECT_EQ(vec.size(), 3); + vec.resize(6, 10); + EXPECT_EQ(vec.size(), 6); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Resize4) { + SmallVector vec = {0, 1, 2, 10, 10, 10}; + SmallVector result = {0, 1, 2}; + + EXPECT_EQ(vec.size(), 6); + vec.resize(3, 10); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Resize5) { + SmallVector vec = {0, 1, 2, 10, 10, 10}; + SmallVector result = {0, 1, 2}; + + EXPECT_EQ(vec.size(), 6); + vec.resize(3, 10); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +TEST(SmallVectorTest, Resize6) { + SmallVector vec = {0, 1, 2, 10, 10, 10}; + SmallVector result = {0, 1, 2}; + + EXPECT_EQ(vec.size(), 6); + vec.resize(3, 10); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec, result); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/CMakeLists.txt b/third_party/spirv-tools/test/val/CMakeLists.txt new file mode 100644 index 0000000..153a916 --- /dev/null +++ b/third_party/spirv-tools/test/val/CMakeLists.txt @@ -0,0 +1,96 @@ +# Copyright (c) 2016 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(VAL_TEST_COMMON_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/../test_fixture.h + ${CMAKE_CURRENT_SOURCE_DIR}/../unit_spirv.h + ${CMAKE_CURRENT_SOURCE_DIR}/val_code_generator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val_code_generator.h + ${CMAKE_CURRENT_SOURCE_DIR}/val_fixtures.h +) + +add_spvtools_unittest(TARGET val_abcde + SRCS + val_adjacency_test.cpp + val_arithmetics_test.cpp + val_atomics_test.cpp + val_barriers_test.cpp + val_bitwise_test.cpp + val_builtins_test.cpp + val_cfg_test.cpp + val_composites_test.cpp + val_constants_test.cpp + val_conversion_test.cpp + val_data_test.cpp + val_decoration_test.cpp + val_derivatives_test.cpp + val_entry_point.cpp + val_explicit_reserved_test.cpp + val_extensions_test.cpp + val_extension_spv_khr_terminate_invocation.cpp + val_ext_inst_test.cpp + ${VAL_TEST_COMMON_SRCS} + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} + PCH_FILE pch_test_val +) + +add_spvtools_unittest(TARGET val_capability + SRCS + val_capability_test.cpp + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} + PCH_FILE pch_test_val +) + +add_spvtools_unittest(TARGET val_limits + SRCS val_limits_test.cpp + ${VAL_TEST_COMMON_SRCS} + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} + PCH_FILE pch_test_val +) + +add_spvtools_unittest(TARGET val_fghijklmnop + SRCS + val_function_test.cpp + val_id_test.cpp + val_image_test.cpp + val_interfaces_test.cpp + val_layout_test.cpp + val_literals_test.cpp + val_logicals_test.cpp + val_memory_test.cpp + val_misc_test.cpp + val_modes_test.cpp + val_non_semantic_test.cpp + val_non_uniform_test.cpp + val_opencl_test.cpp + val_primitives_test.cpp + ${VAL_TEST_COMMON_SRCS} + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} + PCH_FILE pch_test_val +) + +add_spvtools_unittest(TARGET val_stuvw + SRCS + val_small_type_uses_test.cpp + val_ssa_test.cpp + val_state_test.cpp + val_storage_test.cpp + val_type_unique_test.cpp + val_validation_state_test.cpp + val_version_test.cpp + val_webgpu_test.cpp + ${VAL_TEST_COMMON_SRCS} + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY} + PCH_FILE pch_test_val +) diff --git a/third_party/spirv-tools/test/val/pch_test_val.cpp b/third_party/spirv-tools/test/val/pch_test_val.cpp new file mode 100644 index 0000000..fc92e37 --- /dev/null +++ b/third_party/spirv-tools/test/val/pch_test_val.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_test_val.h" diff --git a/third_party/spirv-tools/test/val/pch_test_val.h b/third_party/spirv-tools/test/val/pch_test_val.h new file mode 100644 index 0000000..7b5881c --- /dev/null +++ b/third_party/spirv-tools/test/val/pch_test_val.h @@ -0,0 +1,19 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" diff --git a/third_party/spirv-tools/test/val/val_adjacency_test.cpp b/third_party/spirv-tools/test/val/val_adjacency_test.cpp new file mode 100644 index 0000000..2959853 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_adjacency_test.cpp @@ -0,0 +1,707 @@ +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateAdjacency = spvtest::ValidateBase; + +TEST_F(ValidateAdjacency, OpPhiBeginsModuleFail) { + const std::string module = R"( +%result = OpPhi %bool %true %true_label %false %false_label +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +OpBranch %true_label +%true_label = OpLabel +OpBranch %false_label +%false_label = OpLabel +OpBranch %end_label +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(module); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 1[%bool] has not been defined")); +} + +TEST_F(ValidateAdjacency, OpLoopMergeEndsModuleFail) { + const std::string module = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %end %loop None +)"; + + CompileSuccessfully(module); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing OpFunctionEnd at end of module")); +} + +TEST_F(ValidateAdjacency, OpSelectionMergeEndsModuleFail) { + const std::string module = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +OpBranch %merge +%merge = OpLabel +OpSelectionMerge %merge None +)"; + + CompileSuccessfully(module); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing OpFunctionEnd at end of module")); +} + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "OpCapability Shader", + const std::string& execution_model = "Fragment") { + std::ostringstream ss; + ss << capabilities_and_extensions << "\n"; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + + ss << R"( +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +TEST_F(ValidateAdjacency, OpPhiPreceededByOpLabelSuccess) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +OpLine %string 0 0 +%result = OpPhi %bool %true %true_label %false %false_label +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpPhiPreceededByOpPhiSuccess) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +%1 = OpPhi %bool %true %true_label %false %false_label +%2 = OpPhi %bool %true %true_label %false %false_label +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpPhiPreceededByOpLineSuccess) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +OpLine %string 0 0 +%result = OpPhi %bool %true %true_label %false %false_label +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpPhiPreceededByBadOpFail) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +OpNop +%result = OpPhi %bool %true %true_label %false %false_label +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi must appear within a non-entry block before all " + "non-OpPhi instructions")); +} + +TEST_F(ValidateAdjacency, OpPhiPreceededByOpLineAndBadOpFail) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +OpNop +OpLine %string 1 1 +%result = OpPhi %bool %true %true_label %false %false_label +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi must appear within a non-entry block before all " + "non-OpPhi instructions")); +} + +TEST_F(ValidateAdjacency, OpPhiFollowedByOpLineGood) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +%result = OpPhi %bool %true %true_label %false %false_label +OpLine %string 1 1 +OpNop +OpNop +OpLine %string 2 1 +OpNop +OpLine %string 3 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpPhiMultipleOpLineAndOpPhiFail) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +OpLine %string 1 1 +%value = OpPhi %int %zero %true_label %int_1 %false_label +OpNop +OpLine %string 2 1 +OpNop +OpLine %string 3 1 +%result = OpPhi %bool %true %true_label %false %false_label +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi must appear within a non-entry block before all " + "non-OpPhi instructions")); +} + +TEST_F(ValidateAdjacency, OpPhiMultipleOpLineAndOpPhiGood) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +OpLine %string 1 1 +%value = OpPhi %int %zero %true_label %int_1 %false_label +OpLine %string 2 1 +%result = OpPhi %bool %true %true_label %false %false_label +OpLine %string 3 1 +OpNop +OpNop +OpLine %string 4 1 +OpNop +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpPhiInEntryBlockBad) { + const std::string body = R"( +OpLine %string 1 1 +%value = OpPhi %int +OpLine %string 2 1 +OpNop +OpLine %string 3 1 +OpNop +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi must appear within a non-entry block before all " + "non-OpPhi instructions")); +} + +TEST_F(ValidateAdjacency, NonSemanticBeforeOpPhiBad) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +%placeholder = OpExtInst %void %extinst 123 %int_1 +%result = OpPhi %bool %true %true_label %false %false_label +)"; + + const std::string extra = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi must appear within a non-entry block before all " + "non-OpPhi instructions")); +} + +TEST_F(ValidateAdjacency, NonSemanticBetweenOpPhiBad) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +%result1 = OpPhi %bool %true %true_label %false %false_label +%placeholder = OpExtInst %void %extinst 123 %int_1 +%result2 = OpPhi %bool %true %true_label %false %false_label +)"; + + const std::string extra = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi must appear within a non-entry block before all " + "non-OpPhi instructions")); +} + +TEST_F(ValidateAdjacency, NonSemanticAfterOpPhiGood) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +OpLine %string 0 0 +%result = OpPhi %bool %true %true_label %false %false_label +%placeholder = OpExtInst %void %extinst 123 %int_1 +)"; + + const std::string extra = R"(OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, NonSemanticBeforeOpFunctionParameterBad) { + const std::string body = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%paramfunc_type = OpTypeFunction %void %int %int + +%paramfunc = OpFunction %void None %paramfunc_type +%placeholder = OpExtInst %void %extinst 123 %int_1 +%a = OpFunctionParameter %int +%b = OpFunctionParameter %int +%paramfunc_entry = OpLabel +OpReturn +OpFunctionEnd + +%main = OpFunction %void None %func +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Non-semantic OpExtInst within function definition " + "must appear in a block")); +} + +TEST_F(ValidateAdjacency, NonSemanticBetweenOpFunctionParameterBad) { + const std::string body = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%paramfunc_type = OpTypeFunction %void %int %int + +%paramfunc = OpFunction %void None %paramfunc_type +%a = OpFunctionParameter %int +%placeholder = OpExtInst %void %extinst 123 %int_1 +%b = OpFunctionParameter %int +%paramfunc_entry = OpLabel +OpReturn +OpFunctionEnd + +%main = OpFunction %void None %func +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Non-semantic OpExtInst within function definition " + "must appear in a block")); +} + +TEST_F(ValidateAdjacency, NonSemanticAfterOpFunctionParameterGood) { + const std::string body = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%paramfunc_type = OpTypeFunction %void %int %int + +%paramfunc = OpFunction %void None %paramfunc_type +%a = OpFunctionParameter %int +%b = OpFunctionParameter %int +%paramfunc_entry = OpLabel +%placeholder = OpExtInst %void %extinst 123 %int_1 +OpReturn +OpFunctionEnd + +%main = OpFunction %void None %func +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, NonSemanticBetweenFunctionsGood) { + const std::string body = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft + +%string = OpString "" +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%zero = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%func = OpTypeFunction %void +%func_int = OpTypePointer Function %int +%paramfunc_type = OpTypeFunction %void %int %int + +%paramfunc = OpFunction %void None %paramfunc_type +%a = OpFunctionParameter %int +%b = OpFunctionParameter %int +%paramfunc_entry = OpLabel +OpReturn +OpFunctionEnd + +%placeholder = OpExtInst %void %extinst 123 %int_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpVariableInFunctionGood) { + const std::string body = R"( +OpLine %string 1 1 +%var = OpVariable %func_int Function +OpLine %string 2 1 +OpNop +OpLine %string 3 1 +OpNop +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleGood) { + const std::string body = R"( +OpLine %string 1 1 +%1 = OpVariable %func_int Function +OpLine %string 2 1 +%2 = OpVariable %func_int Function +%3 = OpVariable %func_int Function +OpNop +OpLine %string 3 1 +OpNop +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpVariableInFunctionBad) { + const std::string body = R"( +%1 = OpUndef %int +%2 = OpVariable %func_int Function +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("All OpVariable instructions in a function must be the " + "first instructions")); +} + +TEST_F(ValidateAdjacency, OpVariableInFunctionMultipleBad) { + const std::string body = R"( +OpNop +%1 = OpVariable %func_int Function +OpLine %string 1 1 +%2 = OpVariable %func_int Function +OpNop +OpNop +OpLine %string 2 1 +%3 = OpVariable %func_int Function +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("All OpVariable instructions in a function must be the " + "first instructions")); +} + +TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchSuccess) { + const std::string body = R"( +OpBranch %loop +%loop = OpLabel +OpLoopMerge %end %loop None +OpBranch %loop +%end = OpLabel +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpLoopMergePreceedsOpBranchConditionalSuccess) { + const std::string body = R"( +OpBranch %loop +%loop = OpLabel +OpLoopMerge %end %loop None +OpBranchConditional %true %loop %end +%end = OpLabel +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpLoopMergePreceedsBadOpFail) { + const std::string body = R"( +OpBranch %loop +%loop = OpLabel +OpLoopMerge %end %loop None +OpNop +OpBranchConditional %true %loop %end +%end = OpLabel +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpLoopMerge must immediately precede either an " + "OpBranch or OpBranchConditional instruction.")); +} + +TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpBranchConditionalSuccess) { + const std::string body = R"( +OpSelectionMerge %end_label None +OpBranchConditional %true %true_label %false_label +%true_label = OpLabel +OpBranch %end_label +%false_label = OpLabel +OpBranch %end_label +%end_label = OpLabel +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpSelectionMergePreceedsOpSwitchSuccess) { + const std::string body = R"( +OpSelectionMerge %merge None +OpSwitch %zero %merge 0 %label +%label = OpLabel +OpBranch %merge +%merge = OpLabel +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAdjacency, OpSelectionMergePreceedsBadOpFail) { + const std::string body = R"( +OpSelectionMerge %merge None +OpNop +OpSwitch %zero %merge 0 %label +%label = OpLabel +OpBranch %merge +%merge = OpLabel +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSelectionMerge must immediately precede either an " + "OpBranchConditional or OpSwitch instruction")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_arithmetics_test.cpp b/third_party/spirv-tools/test/val/val_arithmetics_test.cpp new file mode 100644 index 0000000..b82fc97 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_arithmetics_test.cpp @@ -0,0 +1,1422 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for unique type declaration rules validator. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateArithmetics = spvtest::ValidateBase; + +std::string GenerateCode(const std::string& main_body) { + const std::string prefix = + R"( +OpCapability Shader +OpCapability Int64 +OpCapability Float64 +OpCapability Matrix +%ext_inst = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%f64 = OpTypeFloat 64 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%boolvec2 = OpTypeVector %bool 2 +%s32vec2 = OpTypeVector %s32 2 +%u32vec2 = OpTypeVector %u32 2 +%u64vec2 = OpTypeVector %u64 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%boolvec3 = OpTypeVector %bool 3 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%s32vec3 = OpTypeVector %s32 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%boolvec4 = OpTypeVector %bool 4 +%u32vec4 = OpTypeVector %u32 4 +%u64vec4 = OpTypeVector %u64 4 +%s32vec4 = OpTypeVector %s32 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 + +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 +%f32mat33 = OpTypeMatrix %f32vec3 3 +%f64mat22 = OpTypeMatrix %f64vec2 2 + +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_u32_u32_u32 = OpTypeStruct %u32 %u32 %u32 +%struct_s32_s32 = OpTypeStruct %s32 %s32 +%struct_s32_u32 = OpTypeStruct %s32 %u32 +%struct_u32vec2_u32vec2 = OpTypeStruct %u32vec2 %u32vec2 +%struct_s32vec2_s32vec2 = OpTypeStruct %s32vec2 %s32vec2 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_pi = OpConstant %f32 3.14159 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 +%s32_4 = OpConstant %s32 4 +%s32_m1 = OpConstant %s32 -1 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64_4 = OpConstant %f64 4 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +%s64_4 = OpConstant %s64 4 +%s64_m1 = OpConstant %s64 -1 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_4 = OpConstant %u64 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%s32vec3_012 = OpConstantComposite %s32vec3 %s32_0 %s32_1 %s32_2 +%s32vec3_123 = OpConstantComposite %s32vec3 %s32_1 %s32_2 %s32_3 +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4 + +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec3_123 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_3 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 +%f64vec4_1234 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_3 %f64_4 + +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 +%f32mat32_123123 = OpConstantComposite %f32mat32 %f32vec3_123 %f32vec3_123 +%f32mat33_123123123 = OpConstantComposite %f32mat33 %f32vec3_123 %f32vec3_123 %f32vec3_123 + +%f64mat22_1212 = OpConstantComposite %f64mat22 %f64vec2_12 %f64vec2_12 + +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string suffix = + R"( +OpReturn +OpFunctionEnd)"; + + return prefix + main_body + suffix; +} + +TEST_F(ValidateArithmetics, F32Success) { + const std::string body = R"( +%val1 = OpFMul %f32 %f32_0 %f32_1 +%val2 = OpFSub %f32 %f32_2 %f32_0 +%val3 = OpFAdd %f32 %val1 %val2 +%val4 = OpFNegate %f32 %val3 +%val5 = OpFDiv %f32 %val4 %val1 +%val6 = OpFRem %f32 %val4 %f32_2 +%val7 = OpFMod %f32 %val4 %f32_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, F64Success) { + const std::string body = R"( +%val1 = OpFMul %f64 %f64_0 %f64_1 +%val2 = OpFSub %f64 %f64_2 %f64_0 +%val3 = OpFAdd %f64 %val1 %val2 +%val4 = OpFNegate %f64 %val3 +%val5 = OpFDiv %f64 %val4 %val1 +%val6 = OpFRem %f64 %val4 %f64_2 +%val7 = OpFMod %f64 %val4 %f64_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, Int32Success) { + const std::string body = R"( +%val1 = OpIMul %u32 %s32_0 %u32_1 +%val2 = OpIMul %s32 %s32_2 %u32_1 +%val3 = OpIAdd %u32 %val1 %val2 +%val4 = OpIAdd %s32 %val1 %val2 +%val5 = OpISub %u32 %val3 %val4 +%val6 = OpISub %s32 %val4 %val3 +%val7 = OpSDiv %s32 %val4 %val3 +%val8 = OpSNegate %s32 %val7 +%val9 = OpSRem %s32 %val4 %val3 +%val10 = OpSMod %s32 %val4 %val3 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, Int64Success) { + const std::string body = R"( +%val1 = OpIMul %u64 %s64_0 %u64_1 +%val2 = OpIMul %s64 %s64_2 %u64_1 +%val3 = OpIAdd %u64 %val1 %val2 +%val4 = OpIAdd %s64 %val1 %val2 +%val5 = OpISub %u64 %val3 %val4 +%val6 = OpISub %s64 %val4 %val3 +%val7 = OpSDiv %s64 %val4 %val3 +%val8 = OpSNegate %s64 %val7 +%val9 = OpSRem %s64 %val4 %val3 +%val10 = OpSMod %s64 %val4 %val3 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, F32Vec2Success) { + const std::string body = R"( +%val1 = OpFMul %f32vec2 %f32vec2_01 %f32vec2_12 +%val2 = OpFSub %f32vec2 %f32vec2_12 %f32vec2_01 +%val3 = OpFAdd %f32vec2 %val1 %val2 +%val4 = OpFNegate %f32vec2 %val3 +%val5 = OpFDiv %f32vec2 %val4 %val1 +%val6 = OpFRem %f32vec2 %val4 %f32vec2_12 +%val7 = OpFMod %f32vec2 %val4 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, F64Vec2Success) { + const std::string body = R"( +%val1 = OpFMul %f64vec2 %f64vec2_01 %f64vec2_12 +%val2 = OpFSub %f64vec2 %f64vec2_12 %f64vec2_01 +%val3 = OpFAdd %f64vec2 %val1 %val2 +%val4 = OpFNegate %f64vec2 %val3 +%val5 = OpFDiv %f64vec2 %val4 %val1 +%val6 = OpFRem %f64vec2 %val4 %f64vec2_12 +%val7 = OpFMod %f64vec2 %val4 %f64vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, U32Vec2Success) { + const std::string body = R"( +%val1 = OpIMul %u32vec2 %u32vec2_01 %u32vec2_12 +%val2 = OpISub %u32vec2 %u32vec2_12 %u32vec2_01 +%val3 = OpIAdd %u32vec2 %val1 %val2 +%val4 = OpSNegate %u32vec2 %val3 +%val5 = OpSDiv %u32vec2 %val4 %val1 +%val6 = OpSRem %u32vec2 %val4 %u32vec2_12 +%val7 = OpSMod %u32vec2 %val4 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, FNegateTypeIdU32) { + const std::string body = R"( +%val = OpFNegate %u32 %u32_0 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected floating scalar or vector type as Result Type: FNegate")); +} + +TEST_F(ValidateArithmetics, FNegateTypeIdVec2U32) { + const std::string body = R"( +%val = OpFNegate %u32vec2 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected floating scalar or vector type as Result Type: FNegate")); +} + +TEST_F(ValidateArithmetics, FNegateWrongOperand) { + const std::string body = R"( +%val = OpFNegate %f32 %u32_0 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FNegate operand index 2")); +} + +TEST_F(ValidateArithmetics, FMulTypeIdU32) { + const std::string body = R"( +%val = OpFMul %u32 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected floating scalar or vector type as Result Type: FMul")); +} + +TEST_F(ValidateArithmetics, FMulTypeIdVec2U32) { + const std::string body = R"( +%val = OpFMul %u32vec2 %u32vec2_01 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected floating scalar or vector type as Result Type: FMul")); +} + +TEST_F(ValidateArithmetics, FMulWrongOperand1) { + const std::string body = R"( +%val = OpFMul %f32 %u32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FMul operand index 2")); +} + +TEST_F(ValidateArithmetics, FMulWrongOperand2) { + const std::string body = R"( +%val = OpFMul %f32 %f32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FMul operand index 3")); +} + +TEST_F(ValidateArithmetics, FMulWrongVectorOperand1) { + const std::string body = R"( +%val = OpFMul %f64vec3 %f32vec3_123 %f64vec3_012 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FMul operand index 2")); +} + +TEST_F(ValidateArithmetics, FMulWrongVectorOperand2) { + const std::string body = R"( +%val = OpFMul %f32vec3 %f32vec3_123 %f64vec3_012 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "FMul operand index 3")); +} + +TEST_F(ValidateArithmetics, IMulFloatTypeId) { + const std::string body = R"( +%val = OpIMul %f32 %u32_0 %s32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as Result Type: IMul")); +} + +TEST_F(ValidateArithmetics, IMulFloatOperand1) { + const std::string body = R"( +%val = OpIMul %u32 %f32_0 %s32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as operand: " + "IMul operand index 2")); +} + +TEST_F(ValidateArithmetics, IMulFloatOperand2) { + const std::string body = R"( +%val = OpIMul %u32 %s32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as operand: " + "IMul operand index 3")); +} + +TEST_F(ValidateArithmetics, IMulWrongBitWidthOperand1) { + const std::string body = R"( +%val = OpIMul %u64 %u32_0 %s64_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected arithmetic operands to have the same bit width " + "as Result Type: IMul operand index 2")); +} + +TEST_F(ValidateArithmetics, IMulWrongBitWidthOperand2) { + const std::string body = R"( +%val = OpIMul %u32 %u32_0 %s64_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected arithmetic operands to have the same bit width " + "as Result Type: IMul operand index 3")); +} + +TEST_F(ValidateArithmetics, IMulWrongBitWidthVector) { + const std::string body = R"( +%val = OpIMul %u64vec3 %u32vec3_012 %u32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected arithmetic operands to have the same bit width " + "as Result Type: IMul operand index 2")); +} + +TEST_F(ValidateArithmetics, IMulVectorScalarOperand1) { + const std::string body = R"( +%val = OpIMul %u32vec2 %u32_0 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected arithmetic operands to have the same dimension " + "as Result Type: IMul operand index 2")); +} + +TEST_F(ValidateArithmetics, IMulVectorScalarOperand2) { + const std::string body = R"( +%val = OpIMul %u32vec2 %u32vec2_01 %u32_0 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected arithmetic operands to have the same dimension " + "as Result Type: IMul operand index 3")); +} + +TEST_F(ValidateArithmetics, IMulScalarVectorOperand1) { + const std::string body = R"( +%val = OpIMul %s32 %u32vec2_01 %u32_0 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected arithmetic operands to have the same dimension " + "as Result Type: IMul operand index 2")); +} + +TEST_F(ValidateArithmetics, IMulScalarVectorOperand2) { + const std::string body = R"( +%val = OpIMul %u32 %u32_0 %s32vec2_01 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected arithmetic operands to have the same dimension " + "as Result Type: IMul operand index 3")); +} + +TEST_F(ValidateArithmetics, SNegateFloat) { + const std::string body = R"( +%val = OpSNegate %s32 %f32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as operand: " + "SNegate operand index 2")); +} + +TEST_F(ValidateArithmetics, UDivFloatType) { + const std::string body = R"( +%val = OpUDiv %f32 %u32_2 %u32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected unsigned int scalar or vector type as Result Type: UDiv")); +} + +TEST_F(ValidateArithmetics, UDivSignedIntType) { + const std::string body = R"( +%val = OpUDiv %s32 %u32_2 %u32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected unsigned int scalar or vector type as Result Type: UDiv")); +} + +TEST_F(ValidateArithmetics, UDivWrongOperand1) { + const std::string body = R"( +%val = OpUDiv %u64 %f64_2 %u64_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "UDiv operand index 2")); +} + +TEST_F(ValidateArithmetics, UDivWrongOperand2) { + const std::string body = R"( +%val = OpUDiv %u64 %u64_2 %u32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected arithmetic operands to be of Result Type: " + "UDiv operand index 3")); +} + +TEST_F(ValidateArithmetics, DotSuccess) { + const std::string body = R"( +%val = OpDot %f32 %f32vec2_01 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, DotWrongTypeId) { + const std::string body = R"( +%val = OpDot %u32 %u32vec2_01 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float scalar type as Result Type: Dot")); +} + +TEST_F(ValidateArithmetics, DotNotVectorTypeOperand1) { + const std::string body = R"( +%val = OpDot %f32 %f32 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 6[%float] cannot be a " + "type")); +} + +TEST_F(ValidateArithmetics, DotNotVectorTypeOperand2) { + const std::string body = R"( +%val = OpDot %f32 %f32vec3_012 %f32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected float vector as operand: Dot operand index 3")); +} + +TEST_F(ValidateArithmetics, DotWrongComponentOperand1) { + const std::string body = R"( +%val = OpDot %f64 %f32vec2_01 %f64vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected component type to be equal to Result Type: " + "Dot operand index 2")); +} + +TEST_F(ValidateArithmetics, DotWrongComponentOperand2) { + const std::string body = R"( +%val = OpDot %f32 %f32vec2_01 %f64vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected component type to be equal to Result Type: " + "Dot operand index 3")); +} + +TEST_F(ValidateArithmetics, DotDifferentVectorSize) { + const std::string body = R"( +%val = OpDot %f32 %f32vec2_01 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected operands to have the same number of componenets: Dot")); +} + +TEST_F(ValidateArithmetics, VectorTimesScalarSuccess) { + const std::string body = R"( +%val = OpVectorTimesScalar %f32vec2 %f32vec2_01 %f32_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, VectorTimesScalarWrongTypeId) { + const std::string body = R"( +%val = OpVectorTimesScalar %u32vec2 %f32vec2_01 %f32_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float vector type as Result Type: " + "VectorTimesScalar")); +} + +TEST_F(ValidateArithmetics, VectorTimesScalarWrongVector) { + const std::string body = R"( +%val = OpVectorTimesScalar %f32vec2 %f32vec3_012 %f32_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected vector operand type to be equal to Result Type: " + "VectorTimesScalar")); +} + +TEST_F(ValidateArithmetics, VectorTimesScalarWrongScalar) { + const std::string body = R"( +%val = OpVectorTimesScalar %f32vec2 %f32vec2_01 %f64_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar operand type to be equal to the component " + "type of the vector operand: VectorTimesScalar")); +} + +TEST_F(ValidateArithmetics, MatrixTimesScalarSuccess) { + const std::string body = R"( +%val = OpMatrixTimesScalar %f32mat22 %f32mat22_1212 %f32_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, MatrixTimesScalarWrongTypeId) { + const std::string body = R"( +%val = OpMatrixTimesScalar %f32vec2 %f32mat22_1212 %f32_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float matrix type as Result Type: " + "MatrixTimesScalar")); +} + +TEST_F(ValidateArithmetics, MatrixTimesScalarWrongMatrix) { + const std::string body = R"( +%val = OpMatrixTimesScalar %f32mat22 %f32vec2_01 %f32_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected matrix operand type to be equal to Result Type: " + "MatrixTimesScalar")); +} + +TEST_F(ValidateArithmetics, MatrixTimesScalarWrongScalar) { + const std::string body = R"( +%val = OpMatrixTimesScalar %f32mat22 %f32mat22_1212 %f64_2 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar operand type to be equal to the component " + "type of the matrix operand: MatrixTimesScalar")); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrix2x22Success) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32vec2 %f32vec2_12 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrix3x32Success) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32vec2 %f32vec3_123 %f32mat32_123123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrixWrongTypeId) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32mat22 %f32vec2_12 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float vector type as Result Type: " + "VectorTimesMatrix")); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrixNotFloatVector) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32vec2 %u32vec2_12 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float vector type as left operand: " + "VectorTimesMatrix")); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrixWrongVectorComponent) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32vec2 %f64vec2_12 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected component types of Result Type and vector to be equal: " + "VectorTimesMatrix")); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrixWrongMatrix) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32vec2 %f32vec2_12 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float matrix type as right operand: " + "VectorTimesMatrix")); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrixWrongMatrixComponent) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32vec2 %f32vec2_12 %f64mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected component types of Result Type and matrix to be equal: " + "VectorTimesMatrix")); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrix2eq2x23Fail) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32vec2 %f32vec2_12 %f32mat23_121212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected number of columns of the matrix to be equal to Result Type " + "vector size: VectorTimesMatrix")); +} + +TEST_F(ValidateArithmetics, VectorTimesMatrix2x32Fail) { + const std::string body = R"( +%val = OpVectorTimesMatrix %f32vec2 %f32vec2_12 %f32mat32_123123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected number of rows of the matrix to be equal to the vector " + "operand size: VectorTimesMatrix")); +} + +TEST_F(ValidateArithmetics, MatrixTimesVector22x2Success) { + const std::string body = R"( +%val = OpMatrixTimesVector %f32vec2 %f32mat22_1212 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, MatrixTimesVector23x3Success) { + const std::string body = R"( +%val = OpMatrixTimesVector %f32vec2 %f32mat23_121212 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, MatrixTimesVectorWrongTypeId) { + const std::string body = R"( +%val = OpMatrixTimesVector %f32mat22 %f32mat22_1212 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float vector type as Result Type: " + "MatrixTimesVector")); +} + +TEST_F(ValidateArithmetics, MatrixTimesVectorWrongMatrix) { + const std::string body = R"( +%val = OpMatrixTimesVector %f32vec3 %f32vec3_123 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float matrix type as left operand: " + "MatrixTimesVector")); +} + +TEST_F(ValidateArithmetics, MatrixTimesVectorWrongMatrixCol) { + const std::string body = R"( +%val = OpMatrixTimesVector %f32vec3 %f32mat23_121212 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected column type of the matrix to be equal to Result Type: " + "MatrixTimesVector")); +} + +TEST_F(ValidateArithmetics, MatrixTimesVectorWrongVector) { + const std::string body = R"( +%val = OpMatrixTimesVector %f32vec2 %f32mat22_1212 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float vector type as right operand: " + "MatrixTimesVector")); +} + +TEST_F(ValidateArithmetics, MatrixTimesVectorDifferentComponents) { + const std::string body = R"( +%val = OpMatrixTimesVector %f32vec2 %f32mat22_1212 %f64vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected component types of the operands to be equal: " + "MatrixTimesVector")); +} + +TEST_F(ValidateArithmetics, MatrixTimesVector22x3Fail) { + const std::string body = R"( +%val = OpMatrixTimesVector %f32vec2 %f32mat22_1212 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected number of columns of the matrix to be equal to the vector " + "size: MatrixTimesVector")); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrix22x22Success) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat22 %f32mat22_1212 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrix23x32Success) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat22 %f32mat23_121212 %f32mat32_123123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrix33x33Success) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat33 %f32mat33_123123123 %f32mat33_123123123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrixWrongTypeId) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32vec2 %f32mat22_1212 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected float matrix type as Result Type: MatrixTimesMatrix")); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrixWrongLeftOperand) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat22 %f32vec2_12 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected float matrix type as left operand: MatrixTimesMatrix")); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrixWrongRightOperand) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat22 %f32mat22_1212 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected float matrix type as right operand: MatrixTimesMatrix")); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrix32x23Fail) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat22 %f32mat32_123123 %f32mat23_121212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected column types of Result Type and left matrix to be equal: " + "MatrixTimesMatrix")); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrixDifferentComponents) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat22 %f32mat22_1212 %f64mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected component types of Result Type and right " + "matrix to be equal: " + "MatrixTimesMatrix")); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrix23x23Fail) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat22 %f32mat23_121212 %f32mat23_121212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected number of columns of Result Type and right " + "matrix to be equal: " + "MatrixTimesMatrix")); +} + +TEST_F(ValidateArithmetics, MatrixTimesMatrix23x22Fail) { + const std::string body = R"( +%val = OpMatrixTimesMatrix %f32mat22 %f32mat23_121212 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected number of columns of left matrix and number " + "of rows of right " + "matrix to be equal: MatrixTimesMatrix")); +} + +TEST_F(ValidateArithmetics, OuterProduct2x2Success) { + const std::string body = R"( +%val = OpOuterProduct %f32mat22 %f32vec2_12 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, OuterProduct3x2Success) { + const std::string body = R"( +%val = OpOuterProduct %f32mat32 %f32vec3_123 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, OuterProduct2x3Success) { + const std::string body = R"( +%val = OpOuterProduct %f32mat23 %f32vec2_01 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, OuterProductWrongTypeId) { + const std::string body = R"( +%val = OpOuterProduct %f32vec2 %f32vec2_01 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected float matrix type as Result Type: " + "OuterProduct")); +} + +TEST_F(ValidateArithmetics, OuterProductWrongLeftOperand) { + const std::string body = R"( +%val = OpOuterProduct %f32mat22 %f32vec3_123 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected column type of Result Type to be equal to the type " + "of the left operand: OuterProduct")); +} + +TEST_F(ValidateArithmetics, OuterProductRightOperandNotFloatVector) { + const std::string body = R"( +%val = OpOuterProduct %f32mat22 %f32vec2_12 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected float vector type as right operand: OuterProduct")); +} + +TEST_F(ValidateArithmetics, OuterProductRightOperandWrongComponent) { + const std::string body = R"( +%val = OpOuterProduct %f32mat22 %f32vec2_12 %f64vec2_01 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected component types of the operands to be equal: " + "OuterProduct")); +} + +TEST_F(ValidateArithmetics, OuterProductRightOperandWrongDimension) { + const std::string body = R"( +%val = OpOuterProduct %f32mat22 %f32vec2_12 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected number of columns of the matrix to be equal to the " + "vector size of the right operand: OuterProduct")); +} + +std::string GenerateCoopMatCode(const std::string& extra_types, + const std::string& main_body) { + const std::string prefix = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%u32_16 = OpConstant %u32 16 +%u32_4 = OpConstant %u32 4 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 +%u32mat = OpTypeCooperativeMatrixNV %u32 %subgroup %u32_8 %u32_8 +%s32mat = OpTypeCooperativeMatrixNV %s32 %subgroup %u32_8 %u32_8 + +%f16_1 = OpConstant %f16 1 +%f32_1 = OpConstant %f32 1 +%u32_1 = OpConstant %u32 1 +%s32_1 = OpConstant %s32 1 + +%f16mat_1 = OpConstantComposite %f16mat %f16_1 +%u32mat_1 = OpConstantComposite %u32mat %u32_1 +%s32mat_1 = OpConstantComposite %s32mat %s32_1 + +%u32_c1 = OpSpecConstant %u32 1 +%u32_c2 = OpSpecConstant %u32 2 + +%f16matc = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_c1 %u32_c2 +%f16matc_1 = OpConstantComposite %f16matc %f16_1 + +%mat16x4 = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_16 %u32_4 +%mat4x16 = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_4 %u32_16 +%mat16x16 = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_16 %u32_16 +%f16mat_16x4_1 = OpConstantComposite %mat16x4 %f16_1 +%f16mat_4x16_1 = OpConstantComposite %mat4x16 %f16_1 +%f16mat_16x16_1 = OpConstantComposite %mat16x16 %f16_1)"; + + const std::string func_begin = + R"( +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string suffix = + R"( +OpReturn +OpFunctionEnd)"; + + return prefix + extra_types + func_begin + main_body + suffix; +} + +TEST_F(ValidateArithmetics, CoopMatSuccess) { + const std::string body = R"( +%val1 = OpFAdd %f16mat %f16mat_1 %f16mat_1 +%val2 = OpFSub %f16mat %f16mat_1 %f16mat_1 +%val3 = OpFDiv %f16mat %f16mat_1 %f16mat_1 +%val4 = OpFNegate %f16mat %f16mat_1 +%val5 = OpIAdd %u32mat %u32mat_1 %u32mat_1 +%val6 = OpISub %u32mat %u32mat_1 %u32mat_1 +%val7 = OpUDiv %u32mat %u32mat_1 %u32mat_1 +%val8 = OpIAdd %s32mat %s32mat_1 %s32mat_1 +%val9 = OpISub %s32mat %s32mat_1 %s32mat_1 +%val10 = OpSDiv %s32mat %s32mat_1 %s32mat_1 +%val11 = OpSNegate %s32mat %s32mat_1 +%val12 = OpMatrixTimesScalar %f16mat %f16mat_1 %f16_1 +%val13 = OpMatrixTimesScalar %u32mat %u32mat_1 %u32_1 +%val14 = OpMatrixTimesScalar %s32mat %s32mat_1 %s32_1 +%val15 = OpCooperativeMatrixMulAddNV %mat16x16 %f16mat_16x4_1 %f16mat_4x16_1 %f16mat_16x16_1 +%val16 = OpCooperativeMatrixMulAddNV %f16matc %f16matc_1 %f16matc_1 %f16matc_1 +)"; + + CompileSuccessfully(GenerateCoopMatCode("", body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, CoopMatFMulFail) { + const std::string body = R"( +%val1 = OpFMul %f16mat %f16mat_1 %f16mat_1 +)"; + + CompileSuccessfully(GenerateCoopMatCode("", body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected floating scalar or vector type as Result Type: FMul")); +} + +TEST_F(ValidateArithmetics, CoopMatMatrixTimesScalarMismatchFail) { + const std::string body = R"( +%val1 = OpMatrixTimesScalar %f16mat %f16mat_1 %f32_1 +)"; + + CompileSuccessfully(GenerateCoopMatCode("", body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar operand type to be equal to the component " + "type of the matrix operand: MatrixTimesScalar")); +} + +TEST_F(ValidateArithmetics, CoopMatScopeFail) { + const std::string types = R"( +%workgroup = OpConstant %u32 2 + +%mat16x16_wg = OpTypeCooperativeMatrixNV %f16 %workgroup %u32_16 %u32_16 +%f16matwg_16x16_1 = OpConstantComposite %mat16x16_wg %f16_1 +)"; + + const std::string body = R"( +%val1 = OpCooperativeMatrixMulAddNV %mat16x16 %f16mat_16x4_1 %f16mat_4x16_1 %f16matwg_16x16_1 +)"; + + CompileSuccessfully(GenerateCoopMatCode(types, body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Cooperative matrix scopes must match: CooperativeMatrixMulAddNV")); +} + +TEST_F(ValidateArithmetics, CoopMatDimFail) { + const std::string body = R"( +%val1 = OpCooperativeMatrixMulAddNV %mat16x16 %f16mat_4x16_1 %f16mat_16x4_1 %f16mat_16x16_1 +)"; + + CompileSuccessfully(GenerateCoopMatCode("", body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cooperative matrix 'M' mismatch: CooperativeMatrixMulAddNV")); +} + +TEST_F(ValidateArithmetics, IAddCarrySuccess) { + const std::string body = R"( +%val1 = OpIAddCarry %struct_u32_u32 %u32_0 %u32_1 +%val2 = OpIAddCarry %struct_u32vec2_u32vec2 %u32vec2_01 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, IAddCarryResultTypeNotStruct) { + const std::string body = R"( +%val = OpIAddCarry %u32 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected a struct as Result Type: IAddCarry")); +} + +TEST_F(ValidateArithmetics, IAddCarryResultTypeNotTwoMembers) { + const std::string body = R"( +%val = OpIAddCarry %struct_u32_u32_u32 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type struct to have two members: IAddCarry")); +} + +TEST_F(ValidateArithmetics, IAddCarryResultTypeMemberNotUnsignedInt) { + const std::string body = R"( +%val = OpIAddCarry %struct_s32_s32 %s32_0 %s32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type struct member types to be " + "unsigned integer scalar " + "or vector: IAddCarry")); +} + +TEST_F(ValidateArithmetics, IAddCarryWrongLeftOperand) { + const std::string body = R"( +%val = OpIAddCarry %struct_u32_u32 %s32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected both operands to be of Result Type member " + "type: IAddCarry")); +} + +TEST_F(ValidateArithmetics, IAddCarryWrongRightOperand) { + const std::string body = R"( +%val = OpIAddCarry %struct_u32_u32 %u32_0 %s32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected both operands to be of Result Type member " + "type: IAddCarry")); +} + +TEST_F(ValidateArithmetics, OpSMulExtendedSuccess) { + const std::string body = R"( +%val1 = OpSMulExtended %struct_u32_u32 %u32_0 %u32_1 +%val2 = OpSMulExtended %struct_s32_s32 %s32_0 %s32_1 +%val3 = OpSMulExtended %struct_u32vec2_u32vec2 %u32vec2_01 %u32vec2_12 +%val4 = OpSMulExtended %struct_s32vec2_s32vec2 %s32vec2_01 %s32vec2_12 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateArithmetics, SMulExtendedResultTypeMemberNotInt) { + const std::string body = R"( +%val = OpSMulExtended %struct_f32_f32 %f32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type struct member types to be integer scalar " + "or vector: SMulExtended")); +} + +TEST_F(ValidateArithmetics, SMulExtendedResultTypeMembersNotIdentical) { + const std::string body = R"( +%val = OpSMulExtended %struct_s32_u32 %s32_0 %s32_1 +)"; + + CompileSuccessfully(GenerateCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type struct member types to be identical: " + "SMulExtended")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_atomics_test.cpp b/third_party/spirv-tools/test/val/val_atomics_test.cpp new file mode 100644 index 0000000..aca0f3c --- /dev/null +++ b/third_party/spirv-tools/test/val/val_atomics_test.cpp @@ -0,0 +1,2276 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateAtomics = spvtest::ValidateBase; + +std::string GenerateShaderCodeImpl( + const std::string& body, const std::string& capabilities_and_extensions, + const std::string& definitions, const std::string& memory_model) { + std::ostringstream ss; + ss << R"( +OpCapability Shader +)"; + ss << capabilities_and_extensions; + ss << "OpMemoryModel Logical " << memory_model << "\n"; + ss << R"( +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%f32vec4 = OpTypeVector %f32 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%f32vec4_0000 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0 + +%cross_device = OpConstant %u32 0 +%device = OpConstant %u32 1 +%workgroup = OpConstant %u32 2 +%subgroup = OpConstant %u32 3 +%invocation = OpConstant %u32 4 +%queuefamily = OpConstant %u32 5 + +%relaxed = OpConstant %u32 0 +%acquire = OpConstant %u32 2 +%release = OpConstant %u32 4 +%acquire_release = OpConstant %u32 8 +%acquire_and_release = OpConstant %u32 6 +%sequentially_consistent = OpConstant %u32 16 +%acquire_release_uniform_workgroup = OpConstant %u32 328 + +%f32_ptr = OpTypePointer Workgroup %f32 +%f32_var = OpVariable %f32_ptr Workgroup + +%u32_ptr = OpTypePointer Workgroup %u32 +%u32_var = OpVariable %u32_ptr Workgroup + +%f32vec4_ptr = OpTypePointer Workgroup %f32vec4 +%f32vec4_var = OpVariable %f32vec4_ptr Workgroup + +%f32_ptr_function = OpTypePointer Function %f32 +)"; + ss << definitions; + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + ss << body; + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& memory_model = "GLSL450") { + const std::string defintions = R"( +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 + +%u64_1 = OpConstant %u64 1 +%s64_1 = OpConstant %s64 1 + +%u64_ptr = OpTypePointer Workgroup %u64 +%s64_ptr = OpTypePointer Workgroup %s64 +%u64_var = OpVariable %u64_ptr Workgroup +%s64_var = OpVariable %s64_ptr Workgroup +)"; + return GenerateShaderCodeImpl( + body, "OpCapability Int64\n" + capabilities_and_extensions, defintions, + memory_model); +} + +std::string GenerateWebGPUShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + const std::string vulkan_memory_capability = R"( +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability VulkanMemoryModelKHR +)"; + const std::string vulkan_memory_extension = R"( +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + return GenerateShaderCodeImpl(body, + vulkan_memory_capability + + capabilities_and_extensions + + vulkan_memory_extension, + "", "VulkanKHR"); +} + +std::string GenerateKernelCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + std::ostringstream ss; + ss << R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability Int64 +)"; + + ss << capabilities_and_extensions; + ss << R"( +OpMemoryModel Physical32 OpenCL +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%u64 = OpTypeInt 64 0 +%f32vec4 = OpTypeVector %f32 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u64_1 = OpConstant %u64 1 +%f32vec4_0000 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0 + +%cross_device = OpConstant %u32 0 +%device = OpConstant %u32 1 +%workgroup = OpConstant %u32 2 +%subgroup = OpConstant %u32 3 +%invocation = OpConstant %u32 4 + +%relaxed = OpConstant %u32 0 +%acquire = OpConstant %u32 2 +%release = OpConstant %u32 4 +%acquire_release = OpConstant %u32 8 +%acquire_and_release = OpConstant %u32 6 +%sequentially_consistent = OpConstant %u32 16 +%acquire_release_uniform_workgroup = OpConstant %u32 328 +%acquire_release_atomic_counter_workgroup = OpConstant %u32 1288 + +%f32_ptr = OpTypePointer Workgroup %f32 +%f32_var = OpVariable %f32_ptr Workgroup + +%u32_ptr = OpTypePointer Workgroup %u32 +%u32_var = OpVariable %u32_ptr Workgroup + +%u64_ptr = OpTypePointer Workgroup %u64 +%u64_var = OpVariable %u64_ptr Workgroup + +%f32vec4_ptr = OpTypePointer Workgroup %f32vec4 +%f32vec4_var = OpVariable %f32vec4_ptr Workgroup + +%f32_ptr_function = OpTypePointer Function %f32 +%f32_ptr_uniformconstant = OpTypePointer UniformConstant %f32 +%f32_uc_var = OpVariable %f32_ptr_uniformconstant UniformConstant + +%f32_ptr_image = OpTypePointer Image %f32 +%f32_im_var = OpVariable %f32_ptr_image Image + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +TEST_F(ValidateAtomics, AtomicLoadShaderSuccess) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed +%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire +%val3 = OpAtomicLoad %u64 %u64_var %subgroup %sequentially_consistent +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicLoadKernelSuccess) { + const std::string body = R"( +%val1 = OpAtomicLoad %f32 %f32_var %device %relaxed +%val2 = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent +%val3 = OpAtomicLoad %u64 %u64_var %subgroup %acquire +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicLoadInt32VulkanSuccess) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed +%val2 = OpAtomicLoad %u32 %u32_var %workgroup %acquire +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType1) { + const std::string body = R"( +%val1 = OpAtomicIAdd %f32 %f32_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicIAdd: " + "expected Result Type to be int scalar type")); +} + +TEST_F(ValidateAtomics, AtomicAddIntVulkanWrongType2) { + const std::string body = R"( +%val1 = OpAtomicIAdd %f32vec4 %f32vec4_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicIAdd: " + "expected Result Type to be integer scalar type")); +} + +TEST_F(ValidateAtomics, AtomicAddFloatVulkan) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Opcode AtomicFAddEXT requires one of these capabilities: " + "AtomicFloat32AddEXT AtomicFloat64AddEXT")); +} + +TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType1) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %f32vec4 %f32vec4_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32AddEXT +OpExtension "SPV_EXT_shader_atomic_float_add" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFAddEXT: " + "expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType2) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %u32 %u32_var %device %relaxed %u32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32AddEXT +OpExtension "SPV_EXT_shader_atomic_float_add" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFAddEXT: " + "expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongType3) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %u64 %u64_var %device %relaxed %u64_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32AddEXT +OpExtension "SPV_EXT_shader_atomic_float_add" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFAddEXT: " + "expected Result Type to be float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicAddFloatVulkanWrongCapability) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat64AddEXT +OpExtension "SPV_EXT_shader_atomic_float_add" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFAddEXT: float add atomics " + "require the AtomicFloat32AddEXT capability")); +} + +TEST_F(ValidateAtomics, AtomicAddFloatVulkanSuccess) { + const std::string body = R"( +%val1 = OpAtomicFAddEXT %f32 %f32_var %device %relaxed %f32_1 +)"; + const std::string extra = R"( +OpCapability AtomicFloat32AddEXT +OpExtension "SPV_EXT_shader_atomic_float_add" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicLoadFloatVulkan) { + const std::string body = R"( +%val1 = OpAtomicLoad %f32 %f32_var %device %relaxed +%val2 = OpAtomicLoad %f32 %f32_var %workgroup %acquire +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicStoreFloatVulkan) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicExchangeFloatVulkan) { + const std::string body = R"( +%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicLoadInt64WithCapabilityVulkanSuccess) { + const std::string body = R"( + %val1 = OpAtomicLoad %u64 %u64_var %device %relaxed + %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire + )"; + + CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n"), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicLoadInt64WithoutCapabilityVulkan) { + const std::string body = R"( + %val1 = OpAtomicLoad %u64 %u64_var %device %relaxed + %val2 = OpAtomicLoad %u64 %u64_var %workgroup %acquire + )"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("64-bit atomics require the Int64Atomics capability")); +} + +TEST_F(ValidateAtomics, AtomicStoreOpenCLFunctionPointerStorageTypeSuccess) { + const std::string body = R"( +%f32_var_function = OpVariable %f32_ptr_function Function +OpAtomicStore %f32_var_function %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_OPENCL_1_2); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_1_2)); +} + +TEST_F(ValidateAtomics, AtomicStoreVulkanFunctionPointerStorageType) { + const std::string body = R"( +%f32_var_function = OpVariable %f32_ptr_function Function +OpAtomicStore %f32_var_function %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicStore: Function storage class forbidden when " + "the Shader capability is declared.")); +} + +// TODO(atgoo@github.com): the corresponding check fails Vulkan CTS, +// reenable once fixed. +TEST_F(ValidateAtomics, DISABLED_AtomicLoadVulkanSubgroup) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %subgroup %acquire +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicLoad: in Vulkan environment memory scope is " + "limited to Device, Workgroup and Invocation")); +} + +TEST_F(ValidateAtomics, AtomicLoadVulkanRelease) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %workgroup %release +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " + "Release, AcquireRelease and SequentiallyConsistent")); +} + +TEST_F(ValidateAtomics, AtomicLoadVulkanAcquireRelease) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %workgroup %acquire_release +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " + "Release, AcquireRelease and SequentiallyConsistent")); +} + +TEST_F(ValidateAtomics, AtomicLoadVulkanSequentiallyConsistent) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Vulkan spec disallows OpAtomicLoad with Memory Semantics " + "Release, AcquireRelease and SequentiallyConsistent")); +} + +TEST_F(ValidateAtomics, AtomicLoadShaderFloat) { + const std::string body = R"( +%val1 = OpAtomicLoad %f32 %f32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicLoadVulkanInt64) { + const std::string body = R"( +%val1 = OpAtomicLoad %u64 %u64_var %device %relaxed +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicLoad: 64-bit atomics require the Int64Atomics capability")); +} + +TEST_F(ValidateAtomics, AtomicLoadWebGPUSuccess) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %relaxed +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateAtomics, AtomicLoadWebGPUNonQueueFamilyFailure) { + const std::string body = R"( +%val3 = OpAtomicLoad %u32 %u32_var %invocation %relaxed +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Scope is limited to QueueFamilyKHR for " + "OpAtomic* operations")); +} + +TEST_F(ValidateAtomics, AtomicLoadWebGPUNonRelaxedFailure) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %acquire +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("no bits may be set for Memory Semantics of OpAtomic* " + "instructions")); +} + +TEST_F(ValidateAtomics, VK_KHR_shader_atomic_int64Success) { + const std::string body = R"( +%val1 = OpAtomicUMin %u64 %u64_var %device %relaxed %u64_1 +%val2 = OpAtomicUMax %u64 %u64_var %device %relaxed %u64_1 +%val3 = OpAtomicSMin %u64 %u64_var %device %relaxed %u64_1 +%val4 = OpAtomicSMax %u64 %u64_var %device %relaxed %u64_1 +%val5 = OpAtomicAnd %u64 %u64_var %device %relaxed %u64_1 +%val6 = OpAtomicOr %u64 %u64_var %device %relaxed %u64_1 +%val7 = OpAtomicXor %u64 %u64_var %device %relaxed %u64_1 +%val8 = OpAtomicIAdd %u64 %u64_var %device %relaxed %u64_1 +%val9 = OpAtomicExchange %u64 %u64_var %device %relaxed %u64_1 +%val10 = OpAtomicCompareExchange %u64 %u64_var %device %relaxed %relaxed %u64_1 %u64_1 + +%val11 = OpAtomicUMin %s64 %s64_var %device %relaxed %s64_1 +%val12 = OpAtomicUMax %s64 %s64_var %device %relaxed %s64_1 +%val13 = OpAtomicSMin %s64 %s64_var %device %relaxed %s64_1 +%val14 = OpAtomicSMax %s64 %s64_var %device %relaxed %s64_1 +%val15 = OpAtomicAnd %s64 %s64_var %device %relaxed %s64_1 +%val16 = OpAtomicOr %s64 %s64_var %device %relaxed %s64_1 +%val17 = OpAtomicXor %s64 %s64_var %device %relaxed %s64_1 +%val18 = OpAtomicIAdd %s64 %s64_var %device %relaxed %s64_1 +%val19 = OpAtomicExchange %s64 %s64_var %device %relaxed %s64_1 +%val20 = OpAtomicCompareExchange %s64 %s64_var %device %relaxed %relaxed %s64_1 %s64_1 + +%val21 = OpAtomicLoad %u64 %u64_var %device %relaxed +%val22 = OpAtomicLoad %s64 %s64_var %device %relaxed + +OpAtomicStore %u64_var %device %relaxed %u64_1 +OpAtomicStore %s64_var %device %relaxed %s64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body, "OpCapability Int64Atomics\n"), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, VK_KHR_shader_atomic_int64MissingCapability) { + const std::string body = R"( +%val1 = OpAtomicUMin %u64 %u64_var %device %relaxed %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicUMin: 64-bit atomics require the Int64Atomics capability")); +} + +TEST_F(ValidateAtomics, AtomicLoadWrongResultType) { + const std::string body = R"( +%val1 = OpAtomicLoad %f32vec4 %f32vec4_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicLoad: " + "expected Result Type to be int or float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicLoadWrongPointerType) { + const std::string body = R"( +%val1 = OpAtomicLoad %f32 %f32_ptr %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 27[%_ptr_Workgroup_float] cannot be a type")); +} + +TEST_F(ValidateAtomics, AtomicLoadWrongPointerDataType) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %f32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicLoad: " + "expected Pointer to point to a value of type Result Type")); +} + +TEST_F(ValidateAtomics, AtomicLoadWrongScopeType) { + const std::string body = R"( +%val1 = OpAtomicLoad %f32 %f32_var %f32_1 %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicLoad: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicLoadWrongMemorySemanticsType) { + const std::string body = R"( +%val1 = OpAtomicLoad %f32 %f32_var %device %u64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicLoad: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicStoreKernelSuccess) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +OpAtomicStore %u32_var %subgroup %release %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicStoreShaderSuccess) { + const std::string body = R"( +OpAtomicStore %u32_var %device %release %u32_1 +OpAtomicStore %u32_var %subgroup %sequentially_consistent %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicStoreVulkanSuccess) { + const std::string body = R"( +OpAtomicStore %u32_var %device %release %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateAtomics, AtomicStoreVulkanAcquire) { + const std::string body = R"( +OpAtomicStore %u32_var %device %acquire %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " + "Acquire, AcquireRelease and SequentiallyConsistent")); +} + +TEST_F(ValidateAtomics, AtomicStoreVulkanAcquireRelease) { + const std::string body = R"( +OpAtomicStore %u32_var %device %acquire_release %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " + "Acquire, AcquireRelease and SequentiallyConsistent")); +} + +TEST_F(ValidateAtomics, AtomicStoreVulkanSequentiallyConsistent) { + const std::string body = R"( +OpAtomicStore %u32_var %device %sequentially_consistent %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Vulkan spec disallows OpAtomicStore with Memory Semantics " + "Acquire, AcquireRelease and SequentiallyConsistent")); +} + +TEST_F(ValidateAtomics, AtomicStoreWebGPUSuccess) { + const std::string body = R"( +OpAtomicStore %u32_var %queuefamily %relaxed %u32_1 +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} +TEST_F(ValidateAtomics, AtomicStoreWebGPUNonQueueFamilyFailure) { + const std::string body = R"( +OpAtomicStore %u32_var %workgroup %relaxed %u32_1 +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Scope is limited to QueueFamilyKHR for " + "OpAtomic* operations")); +} + +TEST_F(ValidateAtomics, AtomicStoreWebGPUNonRelaxedFailure) { + const std::string body = R"( +OpAtomicStore %u32_var %queuefamily %release %u32_1 +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("no bits may be set for Memory Semantics of OpAtomic* " + "instructions")); +} + +TEST_F(ValidateAtomics, AtomicStoreWrongPointerType) { + const std::string body = R"( +OpAtomicStore %f32_1 %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicStore: expected Pointer to be of type OpTypePointer")); +} + +TEST_F(ValidateAtomics, AtomicStoreWrongPointerDataType) { + const std::string body = R"( +OpAtomicStore %f32vec4_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicStore: " + "expected Pointer to be a pointer to int or float scalar " + "type")); +} + +TEST_F(ValidateAtomics, AtomicStoreWrongPointerStorageTypeForOpenCL) { + const std::string body = R"( +OpAtomicStore %f32_im_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicStore: storage class must be Function, Workgroup, " + "CrossWorkGroup or Generic in the OpenCL environment.")); +} + +TEST_F(ValidateAtomics, AtomicStoreWrongPointerStorageType) { + const std::string body = R"( +OpAtomicStore %f32_uc_var %device %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicStore: storage class forbidden by universal " + "validation rules.")); +} + +TEST_F(ValidateAtomics, AtomicStoreWrongScopeType) { + const std::string body = R"( +OpAtomicStore %f32_var %f32_1 %relaxed %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicStore: expected scope to be a 32-bit int\n " + "OpAtomicStore %28 %float_1 %uint_0_1 %float_1\n")); +} + +TEST_F(ValidateAtomics, AtomicStoreWrongMemorySemanticsType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %f32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicStore: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicStoreWrongValueType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicStore: " + "expected Value type and the type pointed to by Pointer to " + "be the same")); +} + +TEST_F(ValidateAtomics, AtomicExchangeShaderSuccess) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicExchange %u32 %u32_var %device %relaxed %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicExchangeKernelSuccess) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0 +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val4 = OpAtomicExchange %u32 %u32_var %device %relaxed %u32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicExchangeShaderFloat) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicExchangeWrongResultType) { + const std::string body = R"( +OpStore %f32vec4_var %f32vec4_0000 +%val2 = OpAtomicExchange %f32vec4 %f32vec4_var %device %relaxed %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicExchange: " + "expected Result Type to be int or float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicExchangeWrongPointerType) { + const std::string body = R"( +%val2 = OpAtomicExchange %f32 %f32vec4_ptr %device %relaxed %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 33[%_ptr_Workgroup_v4float] cannot be a " + "type")); +} + +TEST_F(ValidateAtomics, AtomicExchangeWrongPointerDataType) { + const std::string body = R"( +OpStore %f32vec4_var %f32vec4_0000 +%val2 = OpAtomicExchange %f32 %f32vec4_var %device %relaxed %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicExchange: " + "expected Pointer to point to a value of type Result Type")); +} + +TEST_F(ValidateAtomics, AtomicExchangeWrongScopeType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicExchange %f32 %f32_var %f32_1 %relaxed %f32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicExchange: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicExchangeWrongMemorySemanticsType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicExchange %f32 %f32_var %device %f32_1 %f32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicExchange: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicExchangeWrongValueType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicExchange %f32 %f32_var %device %relaxed %u32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicExchange: " + "expected Value to be of type Result Type")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeShaderSuccess) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val2 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeKernelSuccess) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1 +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val4 = OpAtomicCompareExchange %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeShaderFloat) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val1 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchange: " + "expected Result Type to be int scalar type")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWrongResultType) { + const std::string body = R"( +OpStore %f32vec4_var %f32vec4_0000 +%val2 = OpAtomicCompareExchange %f32vec4 %f32vec4_var %device %relaxed %relaxed %f32vec4_0000 %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchange: " + "expected Result Type to be int or float scalar type")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerType) { + const std::string body = R"( +%val2 = OpAtomicCompareExchange %f32 %f32vec4_ptr %device %relaxed %relaxed %f32vec4_0000 %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 33[%_ptr_Workgroup_v4float] cannot be a " + "type")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWrongPointerDataType) { + const std::string body = R"( +OpStore %f32vec4_var %f32vec4_0000 +%val2 = OpAtomicCompareExchange %f32 %f32vec4_var %device %relaxed %relaxed %f32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicCompareExchange: " + "expected Pointer to point to a value of type Result Type")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWrongScopeType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicCompareExchange %f32 %f32_var %f32_1 %relaxed %relaxed %f32_0 %f32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchange: expected scope to be a 32-bit " + "int")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType1) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicCompareExchange %f32 %f32_var %device %f32_1 %relaxed %f32_0 %f32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchange: expected Memory Semantics to " + "be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWrongMemorySemanticsType2) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %f32_1 %f32_0 %f32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchange: expected Memory Semantics to " + "be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeUnequalRelease) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %release %f32_0 %f32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchange: Memory Semantics Release and " + "AcquireRelease cannot be used for operand Unequal")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWrongValueType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %u32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchange: " + "expected Value to be of type Result Type")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWrongComparatorType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicCompareExchange %f32 %f32_var %device %relaxed %relaxed %f32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchange: " + "expected Comparator to be of type Result Type")); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWeakSuccess) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val4 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicCompareExchangeWeakWrongResultType) { + const std::string body = R"( +OpAtomicStore %f32_var %device %relaxed %f32_1 +%val2 = OpAtomicCompareExchangeWeak %f32 %f32_var %device %relaxed %relaxed %f32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicCompareExchangeWeak: " + "expected Result Type to be int scalar type")); +} + +TEST_F(ValidateAtomics, AtomicArithmeticsSuccess) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release +%val2 = OpAtomicIDecrement %u32 %u32_var %device %acquire_release +%val3 = OpAtomicIAdd %u32 %u32_var %device %acquire_release %u32_1 +%val4 = OpAtomicISub %u32 %u32_var %device %acquire_release %u32_1 +%val5 = OpAtomicUMin %u32 %u32_var %device %acquire_release %u32_1 +%val6 = OpAtomicUMax %u32 %u32_var %device %acquire_release %u32_1 +%val7 = OpAtomicSMin %u32 %u32_var %device %sequentially_consistent %u32_1 +%val8 = OpAtomicSMax %u32 %u32_var %device %sequentially_consistent %u32_1 +%val9 = OpAtomicAnd %u32 %u32_var %device %sequentially_consistent %u32_1 +%val10 = OpAtomicOr %u32 %u32_var %device %sequentially_consistent %u32_1 +%val11 = OpAtomicXor %u32 %u32_var %device %sequentially_consistent %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicFlagsSuccess) { + const std::string body = R"( +OpAtomicFlagClear %u32_var %device %release +%val1 = OpAtomicFlagTestAndSet %bool %u32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongResultType) { + const std::string body = R"( +%val1 = OpAtomicFlagTestAndSet %u32 %u32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFlagTestAndSet: " + "expected Result Type to be bool scalar type")); +} + +TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotPointer) { + const std::string body = R"( +%val1 = OpAtomicFlagTestAndSet %bool %u32_1 %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFlagTestAndSet: " + "expected Pointer to be of type OpTypePointer")); +} + +TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotIntPointer) { + const std::string body = R"( +%val1 = OpAtomicFlagTestAndSet %bool %f32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFlagTestAndSet: " + "expected Pointer to point to a value of 32-bit int type")); +} + +TEST_F(ValidateAtomics, AtomicFlagTestAndSetNotInt32Pointer) { + const std::string body = R"( +%val1 = OpAtomicFlagTestAndSet %bool %u64_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFlagTestAndSet: " + "expected Pointer to point to a value of 32-bit int type")); +} + +TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongScopeType) { + const std::string body = R"( +%val1 = OpAtomicFlagTestAndSet %bool %u32_var %u64_1 %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFlagTestAndSet: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicFlagTestAndSetWrongMemorySemanticsType) { + const std::string body = R"( +%val1 = OpAtomicFlagTestAndSet %bool %u32_var %device %u64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFlagTestAndSet: " + "expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicFlagClearAcquire) { + const std::string body = R"( +OpAtomicFlagClear %u32_var %device %acquire +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics Acquire and AcquireRelease cannot be " + "used with AtomicFlagClear")); +} + +TEST_F(ValidateAtomics, AtomicFlagClearNotPointer) { + const std::string body = R"( +OpAtomicFlagClear %u32_1 %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFlagClear: " + "expected Pointer to be of type OpTypePointer")); +} + +TEST_F(ValidateAtomics, AtomicFlagClearNotIntPointer) { + const std::string body = R"( +OpAtomicFlagClear %f32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFlagClear: " + "expected Pointer to point to a value of 32-bit int type")); +} + +TEST_F(ValidateAtomics, AtomicFlagClearNotInt32Pointer) { + const std::string body = R"( +OpAtomicFlagClear %u64_var %device %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicFlagClear: " + "expected Pointer to point to a value of 32-bit int type")); +} + +TEST_F(ValidateAtomics, AtomicFlagClearWrongScopeType) { + const std::string body = R"( +OpAtomicFlagClear %u32_var %u64_1 %relaxed +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicFlagClear: expected scope to be a 32-bit " + "int\n OpAtomicFlagClear %30 %ulong_1 %uint_0_1\n")); +} + +TEST_F(ValidateAtomics, AtomicFlagClearWrongMemorySemanticsType) { + const std::string body = R"( +OpAtomicFlagClear %u32_var %device %u64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicFlagClear: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateAtomics, AtomicIIncrementAcquireAndRelease) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_and_release +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicIIncrement: Memory Semantics can have at most " + "one of the following bits set: Acquire, Release, " + "AcquireRelease or SequentiallyConsistent")); +} + +TEST_F(ValidateAtomics, AtomicUniformMemorySemanticsShader) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicUniformMemorySemanticsKernel) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicIIncrement: Memory Semantics UniformMemory " + "requires capability Shader")); +} + +// Lack of the AtomicStorage capability is intentionally ignored, see +// https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning why. +TEST_F(ValidateAtomics, AtomicCounterMemorySemanticsNoCapability) { + const std::string body = R"( + OpAtomicStore %u32_var %device %relaxed %u32_1 +%val1 = OpAtomicIIncrement %u32 %u32_var %device +%acquire_release_atomic_counter_workgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, AtomicCounterMemorySemanticsWithCapability) { + const std::string body = R"( +OpAtomicStore %u32_var %device %relaxed %u32_1 +%val1 = OpAtomicIIncrement %u32 %u32_var %device %acquire_release_atomic_counter_workgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body, "OpCapability AtomicStorage\n")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicLoad) { + const std::string body = R"( +%ld = OpAtomicLoad %u32 %u32_var %workgroup %sequentially_consistent +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicStore) { + const std::string body = R"( +OpAtomicStore %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, + VulkanMemoryModelBanSequentiallyConsistentAtomicExchange) { + const std::string body = R"( +%ex = OpAtomicExchange %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, + VulkanMemoryModelBanSequentiallyConsistentAtomicCompareExchangeEqual) { + const std::string body = R"( +%ex = OpAtomicCompareExchange %u32 %u32_var %workgroup %sequentially_consistent %relaxed %u32_0 %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, + VulkanMemoryModelBanSequentiallyConsistentAtomicCompareExchangeUnequal) { + const std::string body = R"( +%ex = OpAtomicCompareExchange %u32 %u32_var %workgroup %relaxed %sequentially_consistent %u32_0 %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, + VulkanMemoryModelBanSequentiallyConsistentAtomicIIncrement) { + const std::string body = R"( +%inc = OpAtomicIIncrement %u32 %u32_var %workgroup %sequentially_consistent +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, + VulkanMemoryModelBanSequentiallyConsistentAtomicIDecrement) { + const std::string body = R"( +%dec = OpAtomicIDecrement %u32 %u32_var %workgroup %sequentially_consistent +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicIAdd) { + const std::string body = R"( +%add = OpAtomicIAdd %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicISub) { + const std::string body = R"( +%sub = OpAtomicISub %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicSMin) { + const std::string body = R"( +%min = OpAtomicSMin %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicUMin) { + const std::string body = R"( +%min = OpAtomicUMin %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicSMax) { + const std::string body = R"( +%max = OpAtomicSMax %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicUMax) { + const std::string body = R"( +%max = OpAtomicUMax %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicAnd) { + const std::string body = R"( +%and = OpAtomicAnd %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicOr) { + const std::string body = R"( +%or = OpAtomicOr %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelBanSequentiallyConsistentAtomicXor) { + const std::string body = R"( +%xor = OpAtomicXor %u32 %u32_var %workgroup %sequentially_consistent %u32_0 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateAtomics, OutputMemoryKHRRequiresVulkanMemoryModelKHR) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 4100 +%5 = OpTypeFunction %2 +%workgroup = OpConstant %3 2 +%ptr = OpTypePointer Workgroup %3 +%var = OpVariable %ptr Workgroup +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpAtomicStore %var %workgroup %semantics %workgroup +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicStore: Memory Semantics OutputMemoryKHR " + "requires capability VulkanMemoryModelKHR")); +} + +TEST_F(ValidateAtomics, MakeAvailableKHRRequiresVulkanMemoryModelKHR) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 8196 +%5 = OpTypeFunction %2 +%workgroup = OpConstant %3 2 +%ptr = OpTypePointer Workgroup %3 +%var = OpVariable %ptr Workgroup +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpAtomicStore %var %workgroup %semantics %workgroup +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicStore: Memory Semantics MakeAvailableKHR " + "requires capability VulkanMemoryModelKHR")); +} + +TEST_F(ValidateAtomics, MakeVisibleKHRRequiresVulkanMemoryModelKHR) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 16386 +%5 = OpTypeFunction %2 +%workgroup = OpConstant %3 2 +%ptr = OpTypePointer Workgroup %3 +%var = OpVariable %ptr Workgroup +%1 = OpFunction %2 None %5 +%7 = OpLabel +%ld = OpAtomicLoad %3 %var %workgroup %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicLoad: Memory Semantics MakeVisibleKHR requires " + "capability VulkanMemoryModelKHR")); +} + +TEST_F(ValidateAtomics, MakeAvailableKHRRequiresReleaseSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 8448 +%5 = OpTypeFunction %2 +%workgroup = OpConstant %3 2 +%ptr = OpTypePointer Workgroup %3 +%var = OpVariable %ptr Workgroup +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpAtomicStore %var %workgroup %semantics %workgroup +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicStore: MakeAvailableKHR Memory Semantics also requires " + "either Release or AcquireRelease Memory Semantics")); +} + +TEST_F(ValidateAtomics, MakeVisibleKHRRequiresAcquireSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 16640 +%5 = OpTypeFunction %2 +%workgroup = OpConstant %3 2 +%ptr = OpTypePointer Workgroup %3 +%var = OpVariable %ptr Workgroup +%1 = OpFunction %2 None %5 +%7 = OpLabel +%ld = OpAtomicLoad %3 %var %workgroup %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("AtomicLoad: MakeVisibleKHR Memory Semantics also requires " + "either Acquire or AcquireRelease Memory Semantics")); +} + +TEST_F(ValidateAtomics, MakeAvailableKHRRequiresStorageSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 8196 +%5 = OpTypeFunction %2 +%workgroup = OpConstant %3 2 +%ptr = OpTypePointer Workgroup %3 +%var = OpVariable %ptr Workgroup +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpAtomicStore %var %workgroup %semantics %workgroup +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicStore: expected Memory Semantics to include a storage class")); +} + +TEST_F(ValidateAtomics, MakeVisibleKHRRequiresStorageSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 16386 +%5 = OpTypeFunction %2 +%workgroup = OpConstant %3 2 +%ptr = OpTypePointer Workgroup %3 +%var = OpVariable %ptr Workgroup +%1 = OpFunction %2 None %5 +%7 = OpLabel +%ld = OpAtomicLoad %3 %var %workgroup %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicLoad: expected Memory Semantics to include a storage class")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelAllowsQueueFamilyKHR) { + const std::string body = R"( +%val = OpAtomicAnd %u32 %u32_var %queuefamily %relaxed %u32_1 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateAtomics, NonVulkanMemoryModelDisallowsQueueFamilyKHR) { + const std::string body = R"( +%val = OpAtomicAnd %u32 %u32_var %queuefamily %relaxed %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("AtomicAnd: Memory Scope QueueFamilyKHR requires " + "capability VulkanMemoryModelKHR\n %42 = OpAtomicAnd " + "%uint %29 %uint_5 %uint_0_1 %uint_1\n")); +} + +TEST_F(ValidateAtomics, SemanticsSpecConstantShader) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%spec_const = OpSpecConstant %int 0 +%workgroup = OpConstant %int 2 +%ptr_int_workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_workgroup Workgroup +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%ld = OpAtomicLoad %int %var %workgroup %spec_const +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics ids must be OpConstant when Shader " + "capability is present")); +} + +TEST_F(ValidateAtomics, SemanticsSpecConstantKernel) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%spec_const = OpSpecConstant %int 0 +%workgroup = OpConstant %int 2 +%ptr_int_workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_workgroup Workgroup +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%ld = OpAtomicLoad %int %var %workgroup %spec_const +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, ScopeSpecConstantShader) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%spec_const = OpSpecConstant %int 0 +%relaxed = OpConstant %int 0 +%ptr_int_workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_workgroup Workgroup +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%ld = OpAtomicLoad %int %var %spec_const %relaxed +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Scope ids must be OpConstant when Shader capability is present")); +} + +TEST_F(ValidateAtomics, ScopeSpecConstantKernel) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%spec_const = OpSpecConstant %int 0 +%relaxed = OpConstant %int 0 +%ptr_int_workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_workgroup Workgroup +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%ld = OpAtomicLoad %int %var %spec_const %relaxed +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelDeviceScopeBad) { + const std::string body = R"( +%val = OpAtomicAnd %u32 %u32_var %device %relaxed %u32_1 +)"; + + const std::string extra = R"(OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateAtomics, VulkanMemoryModelDeviceScopeGood) { + const std::string body = R"( +%val = OpAtomicAnd %u32 %u32_var %device %relaxed %u32_1 +)"; + + const std::string extra = R"(OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra, "VulkanKHR"), + SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateAtomics, WebGPUCrossDeviceMemoryScopeBad) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %cross_device %relaxed +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("in WebGPU environment Memory Scope is limited to " + "QueueFamilyKHR for OpAtomic* operations")); +} + +TEST_F(ValidateAtomics, WebGPUDeviceMemoryScopeBad) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %device %relaxed +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("in WebGPU environment Memory Scope is limited to " + "QueueFamilyKHR for OpAtomic* operations")); +} + +TEST_F(ValidateAtomics, WebGPUWorkgroupMemoryScopeBad) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %workgroup %relaxed +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("in WebGPU environment Memory Scope is limited to " + "QueueFamilyKHR for OpAtomic* operations")); +} + +TEST_F(ValidateAtomics, WebGPUSubgroupMemoryScopeBad) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %subgroup %relaxed +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("in WebGPU environment Memory Scope is limited to " + "QueueFamilyKHR for OpAtomic* operations")); +} + +TEST_F(ValidateAtomics, WebGPUInvocationMemoryScopeBad) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %invocation %relaxed +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("in WebGPU environment Memory Scope is limited to " + "QueueFamilyKHR for OpAtomic* operations")); +} + +TEST_F(ValidateAtomics, WebGPUQueueFamilyMemoryScopeGood) { + const std::string body = R"( +%val1 = OpAtomicLoad %u32 %u32_var %queuefamily %relaxed +)"; + + CompileSuccessfully(GenerateWebGPUShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateAtomics, CompareExchangeWeakV13ValV14Good) { + const std::string body = R"( +%val1 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateAtomics, CompareExchangeWeakV14Bad) { + const std::string body = R"( +%val1 = OpAtomicCompareExchangeWeak %u32 %u32_var %device %relaxed %relaxed %u32_0 %u32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_WRONG_VERSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "AtomicCompareExchangeWeak requires SPIR-V version 1.3 or earlier")); +} + +TEST_F(ValidateAtomics, CompareExchangeVolatileMatch) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%volatile = OpConstant %int 32768 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %volatile %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, CompareExchangeVolatileMismatch) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%volatile = OpConstant %int 32768 +%non_volatile = OpConstant %int 0 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %non_volatile %volatile %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Volatile mask setting must match for Equal and " + "Unequal memory semantics")); +} + +TEST_F(ValidateAtomics, CompareExchangeVolatileMismatchCooperativeMatrix) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpCapability CooperativeMatrixNV +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%volatile = OpSpecConstant %int 32768 +%non_volatile = OpSpecConstant %int 32768 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%cmp_ex = OpAtomicCompareExchange %int %wg_var %workgroup %volatile %non_volatile %int_0 %int_1 +OpReturn +OpFunctionEnd +)"; + + // This is ok because we cannot evaluate the spec constant defaults. + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateAtomics, VolatileRequiresVulkanMemoryModel) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%volatile = OpConstant %int 32768 +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicLoad %int %wg_var %workgroup %volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics Volatile requires capability " + "VulkanMemoryModelKHR")); +} + +TEST_F(ValidateAtomics, CooperativeMatrixSemanticsMustBeConstant) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%undef = OpUndef %int +%ptr_wg_int = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%ld = OpAtomicLoad %int %wg_var %workgroup %undef +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics must be a constant instruction when " + "CooperativeMatrixNV capability is present")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_barriers_test.cpp b/third_party/spirv-tools/test/val/val_barriers_test.cpp new file mode 100644 index 0000000..3643883 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_barriers_test.cpp @@ -0,0 +1,1665 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateBarriers = spvtest::ValidateBase; + +std::string GenerateShaderCodeImpl( + const std::string& body, const std::string& capabilities_and_extensions, + const std::string& definitions, const std::string& execution_model, + const std::string& memory_model) { + std::ostringstream ss; + ss << R"( +OpCapability Shader +)"; + + ss << capabilities_and_extensions; + ss << memory_model << std::endl; + ss << "OpEntryPoint " << execution_model << " %main \"main\"\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } else if (execution_model == "Geometry") { + ss << "OpExecutionMode %main InputPoints\n"; + ss << "OpExecutionMode %main OutputPoints\n"; + } else if (execution_model == "GLCompute") { + ss << "OpExecutionMode %main LocalSize 1 1 1\n"; + } + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_4 = OpConstant %u32 4 +)"; + ss << definitions; + ss << R"( +%cross_device = OpConstant %u32 0 +%device = OpConstant %u32 1 +%workgroup = OpConstant %u32 2 +%subgroup = OpConstant %u32 3 +%invocation = OpConstant %u32 4 +%queuefamily = OpConstant %u32 5 +%shadercall = OpConstant %u32 6 + +%none = OpConstant %u32 0 +%acquire = OpConstant %u32 2 +%release = OpConstant %u32 4 +%acquire_release = OpConstant %u32 8 +%acquire_and_release = OpConstant %u32 6 +%sequentially_consistent = OpConstant %u32 16 +%acquire_release_uniform_workgroup = OpConstant %u32 328 +%acquire_uniform_workgroup = OpConstant %u32 322 +%release_uniform_workgroup = OpConstant %u32 324 +%acquire_and_release_uniform = OpConstant %u32 70 +%acquire_release_subgroup = OpConstant %u32 136 +%acquire_release_workgroup = OpConstant %u32 264 +%uniform = OpConstant %u32 64 +%uniform_workgroup = OpConstant %u32 320 +%workgroup_memory = OpConstant %u32 256 +%image_memory = OpConstant %u32 2048 +%uniform_image_memory = OpConstant %u32 2112 + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "GLCompute") { + const std::string int64_capability = R"( +OpCapability Int64 +)"; + const std::string int64_declarations = R"( +%u64 = OpTypeInt 64 0 +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +)"; + const std::string memory_model = "OpMemoryModel Logical GLSL450"; + return GenerateShaderCodeImpl( + body, int64_capability + capabilities_and_extensions, int64_declarations, + execution_model, memory_model); +} + +std::string GenerateWebGPUComputeShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "GLCompute") { + const std::string vulkan_memory_capability = R"( +OpCapability VulkanMemoryModelKHR +)"; + const std::string vulkan_memory_extension = R"( +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + const std::string memory_model = "OpMemoryModel Logical VulkanKHR"; + return GenerateShaderCodeImpl(body, + vulkan_memory_capability + + capabilities_and_extensions + + vulkan_memory_extension, + "", execution_model, memory_model); +} + +std::string GenerateWebGPUVertexShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Vertex") { + const std::string vulkan_memory_capability = R"( +OpCapability VulkanMemoryModelKHR +)"; + const std::string vulkan_memory_extension = R"( +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + const std::string memory_model = "OpMemoryModel Logical VulkanKHR"; + return GenerateShaderCodeImpl(body, + vulkan_memory_capability + + capabilities_and_extensions + + vulkan_memory_extension, + "", execution_model, memory_model); +} + +std::string GenerateKernelCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + std::ostringstream ss; + ss << R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability Int64 +OpCapability NamedBarrier +)"; + + ss << capabilities_and_extensions; + ss << R"( +OpMemoryModel Physical32 OpenCL +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%u64 = OpTypeInt 64 0 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_4 = OpConstant %f32 4 +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_4 = OpConstant %u32 4 +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_4 = OpConstant %u64 4 + +%cross_device = OpConstant %u32 0 +%device = OpConstant %u32 1 +%workgroup = OpConstant %u32 2 +%subgroup = OpConstant %u32 3 +%invocation = OpConstant %u32 4 + +%none = OpConstant %u32 0 +%acquire = OpConstant %u32 2 +%release = OpConstant %u32 4 +%acquire_release = OpConstant %u32 8 +%acquire_and_release = OpConstant %u32 6 +%sequentially_consistent = OpConstant %u32 16 +%acquire_release_workgroup = OpConstant %u32 264 + +%named_barrier = OpTypeNamedBarrier + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +TEST_F(ValidateBarriers, OpControlBarrierGLComputeSuccess) { + const std::string body = R"( +OpControlBarrier %device %device %none +OpControlBarrier %workgroup %workgroup %acquire +OpControlBarrier %workgroup %device %release +OpControlBarrier %cross_device %cross_device %acquire_release +OpControlBarrier %cross_device %cross_device %sequentially_consistent +OpControlBarrier %cross_device %cross_device %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBarriers, OpControlBarrierKernelSuccess) { + const std::string body = R"( +OpControlBarrier %device %device %none +OpControlBarrier %workgroup %workgroup %acquire +OpControlBarrier %workgroup %device %release +OpControlBarrier %cross_device %cross_device %acquire_release +OpControlBarrier %cross_device %cross_device %sequentially_consistent +OpControlBarrier %cross_device %cross_device %acquire_release_workgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateBarriers, OpControlBarrierTesselationControlSuccess) { + const std::string body = R"( +OpControlBarrier %device %device %none +OpControlBarrier %workgroup %workgroup %acquire +OpControlBarrier %workgroup %device %release +OpControlBarrier %cross_device %cross_device %acquire_release +OpControlBarrier %cross_device %cross_device %sequentially_consistent +OpControlBarrier %cross_device %cross_device %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", + "TessellationControl")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkanSuccess) { + const std::string body = R"( +OpControlBarrier %workgroup %device %none +OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateBarriers, OpControlBarrierWebGPUAcquireReleaseSuccess) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateBarriers, OpControlBarrierWebGPURelaxedFailure) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, AcquireRelease must be set for Memory " + "Semantics of OpControlBarrier")); +} + +TEST_F(ValidateBarriers, OpControlBarrierWebGPUMissingWorkgroupFailure) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, WorkgroupMemory must be set for Memory " + "Semantics")); +} + +TEST_F(ValidateBarriers, OpControlBarrierWebGPUUniformFailure) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("For WebGPU only WorkgroupMemory and AcquireRelease may be set " + "for Memory Semantics of OpControlBarrier.")); +} + +TEST_F(ValidateBarriers, OpControlBarrierWebGPUReleaseFailure) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, AcquireRelease must be set for Memory " + "Semantics of OpControlBarrier")); +} + +TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv12) { + const std::string body = R"( +OpControlBarrier %device %device %none +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), + SPV_ENV_UNIVERSAL_1_2); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpControlBarrier requires one of the following Execution " + "Models: TessellationControl, GLCompute or Kernel")); +} + +TEST_F(ValidateBarriers, OpControlBarrierExecutionModelFragmentSpirv13) { + const std::string body = R"( +OpControlBarrier %device %device %none +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), + SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateBarriers, OpControlBarrierFloatExecutionScope) { + const std::string body = R"( +OpControlBarrier %f32_1 %device %none +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpControlBarrierU64ExecutionScope) { + const std::string body = R"( +OpControlBarrier %u64_1 %device %none +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpControlBarrierFloatMemoryScope) { + const std::string body = R"( +OpControlBarrier %device %f32_1 %none +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpControlBarrierU64MemoryScope) { + const std::string body = R"( +OpControlBarrier %device %u64_1 %none +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpControlBarrierFloatMemorySemantics) { + const std::string body = R"( +OpControlBarrier %device %device %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ControlBarrier: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpControlBarrierU64MemorySemantics) { + const std::string body = R"( +OpControlBarrier %device %device %u64_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ControlBarrier: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkanExecutionScopeDevice) { + const std::string body = R"( +OpControlBarrier %device %workgroup %none +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: in Vulkan environment Execution Scope " + "is limited to Workgroup and Subgroup")); +} + +TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeDeviceBad) { + const std::string body = R"( +OpControlBarrier %device %workgroup %none +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: in WebGPU environment Execution Scope " + "is limited to Workgroup")); +} + +TEST_F(ValidateBarriers, OpControlBarrierWebGPUExecutionScopeSubgroupBad) { + const std::string body = R"( +OpControlBarrier %subgroup %workgroup %none +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: in WebGPU environment Execution Scope " + "is limited to Workgroup")); +} + +TEST_F(ValidateBarriers, + OpControlBarrierWebGPUExecutionScopeWorkgroupNonComputeBad) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Workgroup Execution Scope is limited to GLCompute execution model")); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkanMemoryScopeSubgroup) { + const std::string body = R"( +OpControlBarrier %subgroup %subgroup %none +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ControlBarrier: in Vulkan 1.0 environment Memory Scope is " + "limited to Device, Workgroup and Invocation")); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeSubgroup) { + const std::string body = R"( +OpControlBarrier %subgroup %subgroup %none +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBarriers, OpControlBarrierVulkan1p1MemoryScopeCrossDevice) { + const std::string body = R"( +OpControlBarrier %subgroup %cross_device %none +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: in Vulkan environment, Memory Scope " + "cannot be CrossDevice")); +} + +TEST_F(ValidateBarriers, OpControlBarrierWebGPUMemoryScopeNonWorkgroup) { + const std::string body = R"( +OpControlBarrier %workgroup %subgroup %acquire_release_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: in WebGPU environment Memory Scope is " + "limited to Workgroup for OpControlBarrier")); +} + +TEST_F(ValidateBarriers, OpControlBarrierAcquireAndRelease) { + const std::string body = R"( +OpControlBarrier %device %device %acquire_and_release_uniform +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: Memory Semantics can have at most one " + "of the following bits set: Acquire, Release, " + "AcquireRelease or SequentiallyConsistent")); +} + +// TODO(atgoo@github.com): the corresponding check fails Vulkan CTS, +// reenable once fixed. +TEST_F(ValidateBarriers, DISABLED_OpControlBarrierVulkanSubgroupStorageClass) { + const std::string body = R"( +OpControlBarrier %workgroup %device %acquire_release_subgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ControlBarrier: expected Memory Semantics to include a " + "Vulkan-supported storage class if Memory Semantics is not None")); +} + +TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p1) { + const std::string body = R"( +OpControlBarrier %subgroup %subgroup %acquire_release_subgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), + SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionFragment1p1) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), + SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpControlBarrier execution scope must be Subgroup for " + "Fragment, Vertex, Geometry and TessellationEvaluation " + "execution models")); +} + +TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionFragment1p0) { + const std::string body = R"( +OpControlBarrier %subgroup %workgroup %acquire_release +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment"), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpControlBarrier requires one of the following Execution " + "Models: TessellationControl, GLCompute or Kernel")); +} + +TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p1) { + const std::string body = R"( +OpControlBarrier %subgroup %subgroup %acquire_release_subgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), + SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionVertex1p1) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), + SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpControlBarrier execution scope must be Subgroup for " + "Fragment, Vertex, Geometry and TessellationEvaluation " + "execution models")); +} + +TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionVertex1p0) { + const std::string body = R"( +OpControlBarrier %subgroup %workgroup %acquire_release +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Vertex"), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpControlBarrier requires one of the following Execution " + "Models: TessellationControl, GLCompute or Kernel")); +} + +TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p1) { + const std::string body = R"( +OpControlBarrier %subgroup %subgroup %acquire_release_subgroup +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"), + SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBarriers, OpControlBarrierWorkgroupExecutionGeometry1p1) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"), + SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpControlBarrier execution scope must be Subgroup for " + "Fragment, Vertex, Geometry and TessellationEvaluation " + "execution models")); +} + +TEST_F(ValidateBarriers, OpControlBarrierSubgroupExecutionGeometry1p0) { + const std::string body = R"( +OpControlBarrier %subgroup %workgroup %acquire_release +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability Geometry\n", "Geometry"), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpControlBarrier requires one of the following Execution " + "Models: TessellationControl, GLCompute or Kernel")); +} + +TEST_F(ValidateBarriers, + OpControlBarrierSubgroupExecutionTessellationEvaluation1p1) { + const std::string body = R"( +OpControlBarrier %subgroup %subgroup %acquire_release_subgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", + "TessellationEvaluation"), + SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBarriers, + OpControlBarrierWorkgroupExecutionTessellationEvaluation1p1) { + const std::string body = R"( +OpControlBarrier %workgroup %workgroup %acquire_release +)"; + + CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", + "TessellationEvaluation"), + SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpControlBarrier execution scope must be Subgroup for " + "Fragment, Vertex, Geometry and TessellationEvaluation " + "execution models")); +} + +TEST_F(ValidateBarriers, + OpControlBarrierSubgroupExecutionTessellationEvaluation1p0) { + const std::string body = R"( +OpControlBarrier %subgroup %workgroup %acquire_release +)"; + + CompileSuccessfully(GenerateShaderCode(body, "OpCapability Tessellation\n", + "TessellationEvaluation"), + SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpControlBarrier requires one of the following Execution " + "Models: TessellationControl, GLCompute or Kernel")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierSuccess) { + const std::string body = R"( +OpMemoryBarrier %cross_device %acquire_release_uniform_workgroup +OpMemoryBarrier %device %uniform +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierKernelSuccess) { + const std::string body = R"( +OpMemoryBarrier %cross_device %acquire_release_workgroup +OpMemoryBarrier %device %none +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierVulkanSuccess) { + const std::string body = R"( +OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUImageMemorySuccess) { + const std::string body = R"( +OpMemoryBarrier %workgroup %image_memory +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUDeviceFailure) { + const std::string body = R"( +OpMemoryBarrier %subgroup %image_memory +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("in WebGPU environment Memory Scope is limited to " + "Workgroup for OpMemoryBarrier")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireReleaseFailure) { + const std::string body = R"( +OpMemoryBarrier %workgroup %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ImageMemory must be set for Memory Semantics of " + "OpMemoryBarrier")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPURelaxedFailure) { + const std::string body = R"( +OpMemoryBarrier %workgroup %uniform_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ImageMemory must be set for Memory Semantics of " + "OpMemoryBarrier")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUAcquireFailure) { + const std::string body = R"( +OpMemoryBarrier %workgroup %acquire_uniform_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ImageMemory must be set for Memory Semantics of " + "OpMemoryBarrier")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUReleaseFailure) { + const std::string body = R"( +OpMemoryBarrier %workgroup %release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ImageMemory must be set for Memory Semantics of " + "OpMemoryBarrier")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUUniformFailure) { + const std::string body = R"( +OpMemoryBarrier %workgroup %uniform_image_memory +)"; + + CompileSuccessfully(GenerateWebGPUComputeShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("only ImageMemory may be set for Memory Semantics of " + "OpMemoryBarrier")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierWebGPUWorkgroupNonComputeFailure) { + const std::string body = R"( +OpMemoryBarrier %workgroup %image_memory +)"; + + CompileSuccessfully(GenerateWebGPUVertexShaderCode(body), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Workgroup Memory Scope is limited to GLCompute execution model")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemoryScope) { + const std::string body = R"( +OpMemoryBarrier %f32_1 %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryBarrier: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierU64MemoryScope) { + const std::string body = R"( +OpMemoryBarrier %u64_1 %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryBarrier: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierFloatMemorySemantics) { + const std::string body = R"( +OpMemoryBarrier %device %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MemoryBarrier: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierU64MemorySemantics) { + const std::string body = R"( +OpMemoryBarrier %device %u64_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MemoryBarrier: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemoryScopeSubgroup) { + const std::string body = R"( +OpMemoryBarrier %subgroup %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MemoryBarrier: in Vulkan 1.0 environment Memory Scope is " + "limited to Device, Workgroup and Invocation")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierVulkan1p1MemoryScopeSubgroup) { + const std::string body = R"( +OpMemoryBarrier %subgroup %acquire_release_uniform_workgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierAcquireAndRelease) { + const std::string body = R"( +OpMemoryBarrier %device %acquire_and_release_uniform +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryBarrier: Memory Semantics can have at most one " + "of the following bits set: Acquire, Release, " + "AcquireRelease or SequentiallyConsistent")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsNone) { + const std::string body = R"( +OpMemoryBarrier %device %none +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MemoryBarrier: Vulkan specification requires Memory Semantics " + "to have one of the following bits set: Acquire, Release, " + "AcquireRelease or SequentiallyConsistent")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierVulkanMemorySemanticsAcquire) { + const std::string body = R"( +OpMemoryBarrier %device %acquire +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryBarrier: expected Memory Semantics to include a " + "Vulkan-supported storage class")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierVulkanSubgroupStorageClass) { + const std::string body = R"( +OpMemoryBarrier %device %acquire_release_subgroup +)"; + + CompileSuccessfully(GenerateShaderCode(body), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryBarrier: expected Memory Semantics to include a " + "Vulkan-supported storage class")); +} + +TEST_F(ValidateBarriers, OpNamedBarrierInitializeSuccess) { + const std::string body = R"( +%barrier = OpNamedBarrierInitialize %named_barrier %u32_4 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateBarriers, OpNamedBarrierInitializeWrongResultType) { + const std::string body = R"( +%barrier = OpNamedBarrierInitialize %u32 %u32_4 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NamedBarrierInitialize: expected Result Type to be " + "OpTypeNamedBarrier")); +} + +TEST_F(ValidateBarriers, OpNamedBarrierInitializeFloatSubgroupCount) { + const std::string body = R"( +%barrier = OpNamedBarrierInitialize %named_barrier %f32_4 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NamedBarrierInitialize: expected Subgroup Count to be " + "a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpNamedBarrierInitializeU64SubgroupCount) { + const std::string body = R"( +%barrier = OpNamedBarrierInitialize %named_barrier %u64_4 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NamedBarrierInitialize: expected Subgroup Count to be " + "a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpMemoryNamedBarrierSuccess) { + const std::string body = R"( +%barrier = OpNamedBarrierInitialize %named_barrier %u32_4 +OpMemoryNamedBarrier %barrier %workgroup %acquire_release_workgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateBarriers, OpMemoryNamedBarrierNotNamedBarrier) { + const std::string body = R"( +OpMemoryNamedBarrier %u32_1 %workgroup %acquire_release_workgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryNamedBarrier: expected Named Barrier to be of " + "type OpTypeNamedBarrier")); +} + +TEST_F(ValidateBarriers, OpMemoryNamedBarrierFloatMemoryScope) { + const std::string body = R"( +%barrier = OpNamedBarrierInitialize %named_barrier %u32_4 +OpMemoryNamedBarrier %barrier %f32_1 %acquire_release_workgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MemoryNamedBarrier: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpMemoryNamedBarrierFloatMemorySemantics) { + const std::string body = R"( +%barrier = OpNamedBarrierInitialize %named_barrier %u32_4 +OpMemoryNamedBarrier %barrier %workgroup %f32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "MemoryNamedBarrier: expected Memory Semantics to be a 32-bit int")); +} + +TEST_F(ValidateBarriers, OpMemoryNamedBarrierAcquireAndRelease) { + const std::string body = R"( +%barrier = OpNamedBarrierInitialize %named_barrier %u32_4 +OpMemoryNamedBarrier %barrier %workgroup %acquire_and_release +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryNamedBarrier: Memory Semantics can have at most " + "one of the following bits set: Acquire, Release, " + "AcquireRelease or SequentiallyConsistent")); +} + +TEST_F(ValidateBarriers, TypeAsMemoryScope) { + const std::string body = R"( +OpMemoryBarrier %u32 %u32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body), SPV_ENV_UNIVERSAL_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 5[%uint] cannot be a " + "type")); +} + +TEST_F(ValidateBarriers, + OpControlBarrierVulkanMemoryModelBanSequentiallyConsistent) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 16 +%5 = OpTypeFunction %2 +%6 = OpConstant %3 5 +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpControlBarrier %6 %6 %4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateBarriers, + OpMemoryBarrierVulkanMemoryModelBanSequentiallyConsistent) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 16 +%5 = OpTypeFunction %2 +%6 = OpConstant %3 5 +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpMemoryBarrier %6 %4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("SequentiallyConsistent memory semantics cannot be " + "used with the VulkanKHR memory model.")); +} + +TEST_F(ValidateBarriers, OutputMemoryKHRRequireVulkanMemoryModelKHR) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 4104 +%5 = OpTypeFunction %2 +%device = OpConstant %3 1 +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: Memory Semantics OutputMemoryKHR " + "requires capability VulkanMemoryModelKHR")); +} + +TEST_F(ValidateBarriers, MakeAvailableKHRRequireVulkanMemoryModelKHR) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 8264 +%5 = OpTypeFunction %2 +%device = OpConstant %3 1 +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: Memory Semantics MakeAvailableKHR " + "requires capability VulkanMemoryModelKHR")); +} + +TEST_F(ValidateBarriers, MakeVisibleKHRRequireVulkanMemoryModelKHR) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%semantics = OpConstant %3 16456 +%5 = OpTypeFunction %2 +%device = OpConstant %3 1 +%1 = OpFunction %2 None %5 +%7 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ControlBarrier: Memory Semantics MakeVisibleKHR " + "requires capability VulkanMemoryModelKHR")); +} + +TEST_F(ValidateBarriers, MakeAvailableKHRRequiresReleaseSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%workgroup = OpConstant %int 2 +%semantics = OpConstant %int 8448 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpControlBarrier %workgroup %workgroup %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ControlBarrier: MakeAvailableKHR Memory Semantics also " + "requires either Release or AcquireRelease Memory Semantics")); +} + +TEST_F(ValidateBarriers, MakeVisibleKHRRequiresAcquireSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%workgroup = OpConstant %int 2 +%semantics = OpConstant %int 16640 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpControlBarrier %workgroup %workgroup %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ControlBarrier: MakeVisibleKHR Memory Semantics also requires " + "either Acquire or AcquireRelease Memory Semantics")); +} + +TEST_F(ValidateBarriers, MakeAvailableKHRRequiresStorageSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%workgroup = OpConstant %int 2 +%semantics = OpConstant %int 8196 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpMemoryBarrier %workgroup %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryBarrier: expected Memory Semantics to include a " + "storage class")); +} + +TEST_F(ValidateBarriers, MakeVisibleKHRRequiresStorageSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%workgroup = OpConstant %int 2 +%semantics = OpConstant %int 16386 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpMemoryBarrier %workgroup %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MemoryBarrier: expected Memory Semantics to include a " + "storage class")); +} + +TEST_F(ValidateBarriers, SemanticsSpecConstantShader) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_workgroup Workgroup +%voidfn = OpTypeFunction %void +%spec_const = OpSpecConstant %int 0 +%workgroup = OpConstant %int 2 +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpMemoryBarrier %workgroup %spec_const +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics ids must be OpConstant when Shader " + "capability is present")); +} + +TEST_F(ValidateBarriers, SemanticsSpecConstantKernel) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_workgroup Workgroup +%voidfn = OpTypeFunction %void +%spec_const = OpSpecConstant %int 0 +%workgroup = OpConstant %int 2 +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpMemoryBarrier %workgroup %spec_const +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBarriers, ScopeSpecConstantShader) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_workgroup Workgroup +%voidfn = OpTypeFunction %void +%spec_const = OpSpecConstant %int 0 +%relaxed = OpConstant %int 0 +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpMemoryBarrier %spec_const %relaxed +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Scope ids must be OpConstant when Shader " + "capability is present")); +} + +TEST_F(ValidateBarriers, ScopeSpecConstantKernel) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_workgroup Workgroup +%voidfn = OpTypeFunction %void +%spec_const = OpSpecConstant %int 0 +%relaxed = OpConstant %int 0 +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpMemoryBarrier %spec_const %relaxed +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBarriers, VulkanMemoryModelDeviceScopeBad) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpConstant %int 0 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpMemoryBarrier %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateBarriers, VulkanMemoryModelDeviceScopeGood) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpConstant %int 0 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpMemoryBarrier %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateBarriers, VolatileMemoryBarrier) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpConstant %int 32768 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpMemoryBarrier %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics Volatile can only be used with " + "atomic instructions")); +} + +TEST_F(ValidateBarriers, VolatileControlBarrier) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpConstant %int 32768 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics Volatile can only be used with " + "atomic instructions")); +} + +TEST_F(ValidateBarriers, CooperativeMatrixSpecConstantVolatile) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability CooperativeMatrixNV +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpSpecConstant %int 32768 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBarriers, CooperativeMatrixNonConstantSemantics) { + const std::string text = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability CooperativeMatrixNV +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%semantics = OpUndef %int +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpControlBarrier %device %device %semantics +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Memory Semantics must be a constant instruction when " + "CooperativeMatrixNV capability is present")); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierShaderCallRayGenSuccess) { + const std::string body = + "OpMemoryBarrier %shadercall %release_uniform_workgroup"; + + CompileSuccessfully(GenerateShaderCodeImpl(body, + // capabilities_and_extensions + R"( + OpCapability VulkanMemoryModelKHR + OpCapability RayTracingProvisionalKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpExtension "SPV_KHR_ray_tracing" + )", + // definitions + "", + // execution_model + "RayGenerationKHR", + // memory_model + "OpMemoryModel Logical VulkanKHR"), + SPV_ENV_VULKAN_1_1); + + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBarriers, OpMemoryBarrierShaderCallComputeFailure) { + const std::string body = + "OpMemoryBarrier %shadercall %release_uniform_workgroup"; + + CompileSuccessfully(GenerateShaderCodeImpl(body, + // capabilities_and_extensions + R"( + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + )", + // definitions + "", + // execution_model + "GLCompute", + // memory_model + "OpMemoryModel Logical VulkanKHR"), + SPV_ENV_VULKAN_1_1); + + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ShaderCallKHR Memory Scope requires a ray tracing execution model")); +} + +TEST_F(ValidateBarriers, OpControlBarrierShaderCallRayGenFailure) { + const std::string body = "OpControlBarrier %shadercall %shadercall %none"; + + CompileSuccessfully(GenerateShaderCodeImpl(body, + // capabilities_and_extensions + R"( + OpCapability VulkanMemoryModelKHR + OpCapability RayTracingProvisionalKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpExtension "SPV_KHR_ray_tracing" + )", + // definitions + "", + // execution_model + "RayGenerationKHR", + // memory_model + "OpMemoryModel Logical VulkanKHR"), + SPV_ENV_VULKAN_1_1); + + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("in Vulkan environment Execution Scope is limited to " + "Workgroup and Subgroup")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_bitwise_test.cpp b/third_party/spirv-tools/test/val/val_bitwise_test.cpp new file mode 100644 index 0000000..1001def --- /dev/null +++ b/third_party/spirv-tools/test/val/val_bitwise_test.cpp @@ -0,0 +1,549 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for unique type declaration rules validator. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateBitwise = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + const std::string capabilities = + R"( +OpCapability Shader +OpCapability Int64 +OpCapability Float64)"; + + const std::string after_extension_before_body = + R"( +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%f64 = OpTypeFloat 64 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%boolvec2 = OpTypeVector %bool 2 +%s32vec2 = OpTypeVector %s32 2 +%u32vec2 = OpTypeVector %u32 2 +%u64vec2 = OpTypeVector %u64 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%boolvec3 = OpTypeVector %bool 3 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%s32vec3 = OpTypeVector %s32 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%boolvec4 = OpTypeVector %bool 4 +%u32vec4 = OpTypeVector %u32 4 +%u64vec4 = OpTypeVector %u64 4 +%s32vec4 = OpTypeVector %s32 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 +%s32_4 = OpConstant %s32 4 +%s32_m1 = OpConstant %s32 -1 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64_4 = OpConstant %f64 4 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +%s64_4 = OpConstant %s64 4 +%s64_m1 = OpConstant %s64 -1 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_4 = OpConstant %u64 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%s32vec3_012 = OpConstantComposite %s32vec3 %s32_0 %s32_1 %s32_2 +%s32vec3_123 = OpConstantComposite %s32vec3 %s32_1 %s32_2 %s32_3 +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4 + +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string after_body = + R"( +OpReturn +OpFunctionEnd)"; + + return capabilities + capabilities_and_extensions + + after_extension_before_body + body + after_body; +} + +TEST_F(ValidateBitwise, ShiftAllSuccess) { + const std::string body = R"( +%val1 = OpShiftRightLogical %u64 %u64_1 %s32_2 +%val2 = OpShiftRightArithmetic %s32vec2 %s32vec2_12 %s32vec2_12 +%val3 = OpShiftLeftLogical %u32vec2 %s32vec2_12 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBitwise, OpShiftRightLogicalWrongResultType) { + const std::string body = R"( +%val1 = OpShiftRightLogical %bool %u64_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as Result Type: " + "ShiftRightLogical")); +} + +TEST_F(ValidateBitwise, OpShiftRightLogicalBaseNotInt) { + const std::string body = R"( +%val1 = OpShiftRightLogical %u32 %f32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Base to be int scalar or vector: ShiftRightLogical")); +} + +TEST_F(ValidateBitwise, OpShiftRightLogicalBaseWrongDimension) { + const std::string body = R"( +%val1 = OpShiftRightLogical %u32 %u32vec2_12 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Base to have the same dimension as Result Type: " + "ShiftRightLogical")); +} + +TEST_F(ValidateBitwise, OpShiftRightLogicalBaseWrongBitWidth) { + const std::string body = R"( +%val1 = OpShiftRightLogical %u64 %u32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Base to have the same bit width as Result Type: " + "ShiftRightLogical")); +} + +TEST_F(ValidateBitwise, OpShiftRightLogicalShiftNotInt) { + const std::string body = R"( +%val1 = OpShiftRightLogical %u32 %u32_1 %f32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Shift to be int scalar or vector: ShiftRightLogical")); +} + +TEST_F(ValidateBitwise, OpShiftRightLogicalShiftWrongDimension) { + const std::string body = R"( +%val1 = OpShiftRightLogical %u32 %u32_1 %s32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Shift to have the same dimension as Result Type: " + "ShiftRightLogical")); +} + +TEST_F(ValidateBitwise, LogicAllSuccess) { + const std::string body = R"( +%val1 = OpBitwiseOr %u64 %u64_1 %s64_0 +%val2 = OpBitwiseAnd %s64 %s64_1 %u64_0 +%val3 = OpBitwiseXor %s32vec2 %s32vec2_12 %u32vec2_01 +%val4 = OpNot %s32vec2 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBitwise, OpBitwiseAndWrongResultType) { + const std::string body = R"( +%val1 = OpBitwiseAnd %bool %u64_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected int scalar or vector type as Result Type: BitwiseAnd")); +} + +TEST_F(ValidateBitwise, OpBitwiseAndLeftNotInt) { + const std::string body = R"( +%val1 = OpBitwiseAnd %u32 %f32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar or vector as operand: BitwiseAnd " + "operand index 2")); +} + +TEST_F(ValidateBitwise, OpBitwiseAndRightNotInt) { + const std::string body = R"( +%val1 = OpBitwiseAnd %u32 %u32_1 %f32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar or vector as operand: BitwiseAnd " + "operand index 3")); +} + +TEST_F(ValidateBitwise, OpBitwiseAndLeftWrongDimension) { + const std::string body = R"( +%val1 = OpBitwiseAnd %u32 %u32vec2_12 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to have the same dimension as Result Type: " + "BitwiseAnd operand index 2")); +} + +TEST_F(ValidateBitwise, OpBitwiseAndRightWrongDimension) { + const std::string body = R"( +%val1 = OpBitwiseAnd %u32 %s32_2 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to have the same dimension as Result Type: " + "BitwiseAnd operand index 3")); +} + +TEST_F(ValidateBitwise, OpBitwiseAndLeftWrongBitWidth) { + const std::string body = R"( +%val1 = OpBitwiseAnd %u64 %u32_1 %s64_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to have the same bit width as Result Type: " + "BitwiseAnd operand index 2")); +} + +TEST_F(ValidateBitwise, OpBitwiseAndRightWrongBitWidth) { + const std::string body = R"( +%val1 = OpBitwiseAnd %u64 %u64_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to have the same bit width as Result Type: " + "BitwiseAnd operand index 3")); +} + +TEST_F(ValidateBitwise, OpBitFieldInsertSuccess) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %s32_1 %s32_2 +%val2 = OpBitFieldInsert %s32vec2 %s32vec2_12 %s32vec2_12 %s32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBitwise, OpBitFieldInsertWrongResultType) { + const std::string body = R"( +%val1 = OpBitFieldInsert %bool %u64_1 %u64_2 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected int scalar or vector type as Result Type: BitFieldInsert")); +} + +TEST_F(ValidateBitwise, OpBitFieldInsertWrongBaseType) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u64 %s64_1 %u64_2 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Base Type to be equal to Result Type: BitFieldInsert")); +} + +TEST_F(ValidateBitwise, OpBitFieldInsertWrongInsertType) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u64 %u64_1 %s64_2 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Insert Type to be equal to Result Type: BitFieldInsert")); +} + +TEST_F(ValidateBitwise, OpBitFieldInsertOffsetNotInt) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %f32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Offset Type to be int scalar: BitFieldInsert")); +} + +TEST_F(ValidateBitwise, OpBitFieldInsertCountNotInt) { + const std::string body = R"( +%val1 = OpBitFieldInsert %u64 %u64_1 %u64_2 %u32_1 %f32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Count Type to be int scalar: BitFieldInsert")); +} + +TEST_F(ValidateBitwise, OpBitFieldSExtractSuccess) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %u64 %u64_1 %s32_1 %s32_2 +%val2 = OpBitFieldSExtract %s32vec2 %s32vec2_12 %s32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBitwise, OpBitFieldSExtractWrongResultType) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %bool %u64_1 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as Result Type: " + "BitFieldSExtract")); +} + +TEST_F(ValidateBitwise, OpBitFieldSExtractWrongBaseType) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %u64 %s64_1 %s32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Base Type to be equal to Result Type: BitFieldSExtract")); +} + +TEST_F(ValidateBitwise, OpBitFieldSExtractOffsetNotInt) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %u64 %u64_1 %f32_1 %s32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Offset Type to be int scalar: BitFieldSExtract")); +} + +TEST_F(ValidateBitwise, OpBitFieldSExtractCountNotInt) { + const std::string body = R"( +%val1 = OpBitFieldSExtract %u64 %u64_1 %u32_1 %f32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Count Type to be int scalar: BitFieldSExtract")); +} + +TEST_F(ValidateBitwise, OpBitReverseSuccess) { + const std::string body = R"( +%val1 = OpBitReverse %u64 %u64_1 +%val2 = OpBitReverse %s32vec2 %s32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBitwise, OpBitReverseWrongResultType) { + const std::string body = R"( +%val1 = OpBitReverse %bool %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected int scalar or vector type as Result Type: BitReverse")); +} + +TEST_F(ValidateBitwise, OpBitReverseWrongBaseType) { + const std::string body = R"( +%val1 = OpBitReverse %u64 %s64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Base Type to be equal to Result Type: BitReverse")); +} + +TEST_F(ValidateBitwise, OpBitCountSuccess) { + const std::string body = R"( +%val1 = OpBitCount %s32 %u64_1 +%val2 = OpBitCount %u32vec2 %s32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBitwise, OpBitCountWrongResultType) { + const std::string body = R"( +%val1 = OpBitCount %bool %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as Result Type: BitCount")); +} + +TEST_F(ValidateBitwise, OpBitCountBaseNotInt) { + const std::string body = R"( +%val1 = OpBitCount %u32 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Base Type to be int scalar or vector: BitCount")); +} + +TEST_F(ValidateBitwise, OpBitCountBaseWrongDimension) { + const std::string body = R"( +%val1 = OpBitCount %u32 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Base dimension to be equal to Result Type dimension: " + "BitCount")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_builtins_test.cpp b/third_party/spirv-tools/test/val/val_builtins_test.cpp new file mode 100644 index 0000000..74d5c3a --- /dev/null +++ b/third_party/spirv-tools/test/val/val_builtins_test.cpp @@ -0,0 +1,3903 @@ +// Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests validation rules of GLSL.450.std and OpenCL.std extended instructions. +// Doesn't test OpenCL.std vector size 2, 3, 4, 8 or 16 rules (not supported +// by standard SPIR-V). + +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/spirv_target_env.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +struct TestResult { + TestResult(spv_result_t in_validation_result = SPV_SUCCESS, + const char* in_error_str = nullptr, + const char* in_error_str2 = nullptr) + : validation_result(in_validation_result), + error_str(in_error_str), + error_str2(in_error_str2) {} + spv_result_t validation_result; + const char* error_str; + const char* error_str2; +}; + +using ::testing::Combine; +using ::testing::HasSubstr; +using ::testing::Not; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateBuiltIns = spvtest::ValidateBase; +using ValidateVulkanSubgroupBuiltIns = spvtest::ValidateBase< + std::tuple>; +using ValidateVulkanCombineBuiltInExecutionModelDataTypeResult = + spvtest::ValidateBase>; +using ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult = + spvtest::ValidateBase>; +using ValidateVulkanCombineBuiltInArrayedVariable = spvtest::ValidateBase< + std::tuple>; +using ValidateWebGPUCombineBuiltInArrayedVariable = spvtest::ValidateBase< + std::tuple>; +using ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult = + spvtest::ValidateBase< + std::tuple>; + +using ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult = + spvtest::ValidateBase>; + +bool InitializerRequired(spv_target_env env, const char* const storage_class) { + return spvIsWebGPUEnv(env) && (strncmp(storage_class, "Output", 6) == 0 || + strncmp(storage_class, "Private", 7) == 0 || + strncmp(storage_class, "Function", 8) == 0); +} + +CodeGenerator GetInMainCodeGenerator(spv_target_env env, + const char* const built_in, + const char* const execution_model, + const char* const storage_class, + const char* const capabilities, + const char* const extensions, + const char* const data_type) { + CodeGenerator generator = + spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + if (capabilities) { + generator.capabilities_ += capabilities; + } + if (extensions) { + generator.extensions_ += extensions; + } + + generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn "; + generator.before_types_ += built_in; + generator.before_types_ += "\n"; + + std::ostringstream after_types; + + after_types << "%built_in_type = OpTypeStruct " << data_type << "\n"; + if (InitializerRequired(env, storage_class)) { + after_types << "%built_in_null = OpConstantNull %built_in_type\n"; + } + after_types << "%built_in_ptr = OpTypePointer " << storage_class + << " %built_in_type\n"; + after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; + if (InitializerRequired(env, storage_class)) { + after_types << " %built_in_null"; + } + after_types << "\n"; + after_types << "%data_ptr = OpTypePointer " << storage_class << " " + << data_type << "\n"; + generator.after_types_ = after_types.str(); + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = execution_model; + if (strncmp(storage_class, "Input", 5) == 0 || + strncmp(storage_class, "Output", 6) == 0) { + entry_point.interfaces = "%built_in_var"; + } + + std::ostringstream execution_modes; + if (0 == std::strcmp(execution_model, "Fragment")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " OriginUpperLeft\n"; + if (0 == std::strcmp(built_in, "FragDepth")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " DepthReplacing\n"; + } + } + if (0 == std::strcmp(execution_model, "Geometry")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " InputPoints\n"; + execution_modes << "OpExecutionMode %" << entry_point.name + << " OutputPoints\n"; + } + if (0 == std::strcmp(execution_model, "GLCompute")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " LocalSize 1 1 1\n"; + } + entry_point.execution_modes = execution_modes.str(); + + entry_point.body = R"( +%ptr = OpAccessChain %data_ptr %built_in_var %u32_0 +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +// Allows test parameter test to list all possible VUIDs with a delimiter that +// is then split here to check if one VUID was in the error message +MATCHER_P(AnyVUID, vuid_set, "VUID from the set is in error message") { + // use space as delimiter because clang-format will properly line break VUID + // strings which is important the entire VUID is in a single line for script + // to scan + std::string delimiter = " "; + std::string token; + std::string vuids = std::string(vuid_set); + size_t position; + + // Catch case were someone accidentally left spaces by trimming string + // clang-format off + vuids.erase(std::find_if(vuids.rbegin(), vuids.rend(), [](unsigned char c) { + return (c != ' '); + }).base(), vuids.end()); + vuids.erase(vuids.begin(), std::find_if(vuids.begin(), vuids.end(), [](unsigned char c) { + return (c != ' '); + })); + // clang-format on + + do { + position = vuids.find(delimiter); + if (position != std::string::npos) { + token = vuids.substr(0, position); + vuids.erase(0, position + delimiter.length()); + } else { + token = vuids.substr(0); // last item + } + + // arg contains diagnostic message + if (arg.find(token) != std::string::npos) { + return true; + } + } while (position != std::string::npos); + return false; +} + +TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InMain) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const char* const vuid = std::get<4>(GetParam()); + const TestResult& test_result = std::get<5>(GetParam()); + + CodeGenerator generator = + GetInMainCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model, + storage_class, NULL, NULL, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } +} + +TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InMain) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const TestResult& test_result = std::get<4>(GetParam()); + + CodeGenerator generator = + GetInMainCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model, + storage_class, NULL, NULL, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } +} + +TEST_P( + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + InMain) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const char* const capabilities = std::get<4>(GetParam()); + const char* const extensions = std::get<5>(GetParam()); + const char* const vuid = std::get<6>(GetParam()); + const TestResult& test_result = std::get<7>(GetParam()); + + CodeGenerator generator = GetInMainCodeGenerator( + SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, + capabilities, extensions, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } +} + +TEST_P( + ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + InMain) { + const spv_target_env env = std::get<0>(GetParam()); + const char* const built_in = std::get<1>(GetParam()); + const char* const execution_model = std::get<2>(GetParam()); + const char* const storage_class = std::get<3>(GetParam()); + const char* const data_type = std::get<4>(GetParam()); + const char* const capabilities = std::get<5>(GetParam()); + const char* const extensions = std::get<6>(GetParam()); + const char* const vuid = std::get<7>(GetParam()); + const TestResult& test_result = std::get<8>(GetParam()); + + CodeGenerator generator = + GetInMainCodeGenerator(env, built_in, execution_model, storage_class, + capabilities, extensions, data_type); + + CompileSuccessfully(generator.Build(), env); + ASSERT_EQ(test_result.validation_result, ValidateInstructions(env)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } +} + +CodeGenerator GetInFunctionCodeGenerator(spv_target_env env, + const char* const built_in, + const char* const execution_model, + const char* const storage_class, + const char* const capabilities, + const char* const extensions, + const char* const data_type) { + CodeGenerator generator = + spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + if (capabilities) { + generator.capabilities_ += capabilities; + } + if (extensions) { + generator.extensions_ += extensions; + } + + generator.before_types_ = "OpMemberDecorate %built_in_type 0 BuiltIn "; + generator.before_types_ += built_in; + generator.before_types_ += "\n"; + + std::ostringstream after_types; + after_types << "%built_in_type = OpTypeStruct " << data_type << "\n"; + if (InitializerRequired(env, storage_class)) { + after_types << "%built_in_null = OpConstantNull %built_in_type\n"; + } + after_types << "%built_in_ptr = OpTypePointer " << storage_class + << " %built_in_type\n"; + after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; + if (InitializerRequired(env, storage_class)) { + after_types << " %built_in_null"; + } + after_types << "\n"; + after_types << "%data_ptr = OpTypePointer " << storage_class << " " + << data_type << "\n"; + generator.after_types_ = after_types.str(); + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = execution_model; + if (strncmp(storage_class, "Input", 5) == 0 || + strncmp(storage_class, "Output", 6) == 0) { + entry_point.interfaces = "%built_in_var"; + } + + std::ostringstream execution_modes; + if (0 == std::strcmp(execution_model, "Fragment")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " OriginUpperLeft\n"; + if (0 == std::strcmp(built_in, "FragDepth")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " DepthReplacing\n"; + } + } + if (0 == std::strcmp(execution_model, "Geometry")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " InputPoints\n"; + execution_modes << "OpExecutionMode %" << entry_point.name + << " OutputPoints\n"; + } + if (0 == std::strcmp(execution_model, "GLCompute")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " LocalSize 1 1 1\n"; + } + entry_point.execution_modes = execution_modes.str(); + + entry_point.body = R"( +%val2 = OpFunctionCall %void %foo +)"; + + std::string function_body = R"( +%foo = OpFunction %void None %func +%foo_entry = OpLabel +%ptr = OpAccessChain %data_ptr %built_in_var %u32_0 +OpReturn +OpFunctionEnd +)"; + + if (spvIsWebGPUEnv(env)) { + generator.after_types_ += function_body; + } else { + generator.add_at_the_end_ = function_body; + } + + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, InFunction) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const char* const vuid = std::get<4>(GetParam()); + const TestResult& test_result = std::get<5>(GetParam()); + + CodeGenerator generator = + GetInFunctionCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model, + storage_class, NULL, NULL, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } +} + +TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, InFunction) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const TestResult& test_result = std::get<4>(GetParam()); + + CodeGenerator generator = + GetInFunctionCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model, + storage_class, NULL, NULL, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } +} + +TEST_P( + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + InFunction) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const char* const capabilities = std::get<4>(GetParam()); + const char* const extensions = std::get<5>(GetParam()); + const char* const vuid = std::get<6>(GetParam()); + const TestResult& test_result = std::get<7>(GetParam()); + + CodeGenerator generator = GetInFunctionCodeGenerator( + SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, + capabilities, extensions, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } +} + +CodeGenerator GetVariableCodeGenerator(spv_target_env env, + const char* const built_in, + const char* const execution_model, + const char* const storage_class, + const char* const capabilities, + const char* const extensions, + const char* const data_type) { + CodeGenerator generator = + spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + if (capabilities) { + generator.capabilities_ += capabilities; + } + if (extensions) { + generator.extensions_ += extensions; + } + + generator.before_types_ = "OpDecorate %built_in_var BuiltIn "; + generator.before_types_ += built_in; + generator.before_types_ += "\n"; + + std::ostringstream after_types; + if (InitializerRequired(env, storage_class)) { + after_types << "%built_in_null = OpConstantNull " << data_type << "\n"; + } + after_types << "%built_in_ptr = OpTypePointer " << storage_class << " " + << data_type << "\n"; + after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; + if (InitializerRequired(env, storage_class)) { + after_types << " %built_in_null"; + } + after_types << "\n"; + generator.after_types_ = after_types.str(); + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = execution_model; + if (strncmp(storage_class, "Input", 5) == 0 || + strncmp(storage_class, "Output", 6) == 0) { + entry_point.interfaces = "%built_in_var"; + } + // Any kind of reference would do. + entry_point.body = R"( +%val = OpBitcast %u32 %built_in_var +)"; + + std::ostringstream execution_modes; + if (0 == std::strcmp(execution_model, "Fragment")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " OriginUpperLeft\n"; + if (0 == std::strcmp(built_in, "FragDepth")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " DepthReplacing\n"; + } + } + if (0 == std::strcmp(execution_model, "Geometry")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " InputPoints\n"; + execution_modes << "OpExecutionMode %" << entry_point.name + << " OutputPoints\n"; + } + if (0 == std::strcmp(execution_model, "GLCompute")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " LocalSize 1 1 1\n"; + } + entry_point.execution_modes = execution_modes.str(); + + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_P(ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, Variable) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const char* const vuid = std::get<4>(GetParam()); + const TestResult& test_result = std::get<5>(GetParam()); + + CodeGenerator generator = + GetVariableCodeGenerator(SPV_ENV_VULKAN_1_0, built_in, execution_model, + storage_class, NULL, NULL, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } +} + +TEST_P(ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, Variable) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const TestResult& test_result = std::get<4>(GetParam()); + + CodeGenerator generator = + GetVariableCodeGenerator(SPV_ENV_WEBGPU_0, built_in, execution_model, + storage_class, NULL, NULL, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } +} + +TEST_P( + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Variable) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const char* const capabilities = std::get<4>(GetParam()); + const char* const extensions = std::get<5>(GetParam()); + const char* const vuid = std::get<6>(GetParam()); + const TestResult& test_result = std::get<7>(GetParam()); + + CodeGenerator generator = GetVariableCodeGenerator( + SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, + capabilities, extensions, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } + if (vuid) { + EXPECT_THAT(getDiagnosticString(), AnyVUID(vuid)); + } +} + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), + Values("Vertex", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Output"), Values("%f32arr2", "%f32arr4"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), + Values("Fragment", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%f32arr2", "%f32arr4"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceFragmentOutput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), Values("Fragment"), + Values("Output"), Values("%f32arr4"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance " + "to be used for variables with Output storage class if " + "execution model is Fragment.", + "which is called with execution model Fragment.")))); + +INSTANTIATE_TEST_SUITE_P( + VertexIdAndInstanceIdVertexInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("VertexId", "InstanceId"), Values("Vertex"), Values("Input"), + Values("%u32"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow BuiltIn VertexId/InstanceId to be " + "used.")))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceVertexInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), Values("Vertex"), + Values("Input"), Values("%f32arr4"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance " + "to be used for variables with Input storage class if " + "execution model is Vertex.", + "which is called with execution model Vertex.")))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), Values("GLCompute"), + Values("Input", "Output"), Values("%f32arr4"), + Values("VUID-ClipDistance-ClipDistance-04187 " + "VUID-CullDistance-CullDistance-04196"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be used only with Fragment, Vertex, TessellationControl, " + "TessellationEvaluation or Geometry execution models")))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceNotArray, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), Values("Fragment"), + Values("Input"), Values("%f32vec2", "%f32vec4", "%f32"), + Values("VUID-ClipDistance-ClipDistance-04191 " + "VUID-CullDistance-CullDistance-04200"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float array", + "is not an array")))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceNotFloatArray, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), Values("Fragment"), + Values("Input"), Values("%u32arr2", "%u64arr4"), + Values("VUID-ClipDistance-ClipDistance-04191 " + "VUID-CullDistance-CullDistance-04200"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float array", + "components are not float scalar")))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceNotF32Array, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ClipDistance", "CullDistance"), Values("Fragment"), + Values("Input"), Values("%f64arr2", "%f64arr4"), + Values("VUID-ClipDistance-ClipDistance-04191 " + "VUID-CullDistance-CullDistance-04200"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float array", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), + Values("%f32vec4"), Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordSuccess, ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), + Values("%f32vec4"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotFragment, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("FragCoord"), + Values("Vertex", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%f32vec4"), + Values("VUID-FragCoord-FragCoord-04210"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotFragment, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("FragCoord"), Values("Vertex", "GLCompute"), Values("Input"), + Values("%f32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Output"), + Values("%f32vec4"), Values("VUID-FragCoord-FragCoord-04211"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotInput, ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Output"), + Values("%f32vec4"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotFloatVector, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), + Values("%f32arr4", "%u32vec4"), + Values("VUID-FragCoord-FragCoord-04212"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "is not a float vector")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotFloatVector, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), + Values("%f32arr4", "%u32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "is not a float vector")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotFloatVec4, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), + Values("%f32vec3"), Values("VUID-FragCoord-FragCoord-04212"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "has 3 components")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotFloatVec4, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), + Values("%f32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "has 3 components")))); + +INSTANTIATE_TEST_SUITE_P( + FragCoordNotF32Vec4, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragCoord"), Values("Fragment"), Values("Input"), + Values("%f64vec4"), Values("VUID-FragCoord-FragCoord-04212"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), + Values("%f32"), Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthSuccess, ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), + Values("%f32"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthNotFragment, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("FragDepth"), + Values("Vertex", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Output"), Values("%f32"), + Values("VUID-FragDepth-FragDepth-04213"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthNotFragment, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("FragDepth"), Values("Vertex", "GLCompute"), Values("Output"), + Values("%f32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthNotOutput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragDepth"), Values("Fragment"), Values("Input"), + Values("%f32"), Values("VUID-FragDepth-FragDepth-04214"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Output storage class", + "uses storage class Input")))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthNotOutput, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragDepth"), Values("Fragment"), Values("Input"), + Values("%f32"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Output storage class", + "uses storage class Input")))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthNotFloatScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), + Values("%f32vec4", "%u32"), + Values("VUID-FragDepth-FragDepth-04215"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float scalar", + "is not a float scalar")))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthNotFloatScalar, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), + Values("%f32vec4", "%u32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float scalar", + "is not a float scalar")))); + +INSTANTIATE_TEST_SUITE_P( + FragDepthNotF32, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FragDepth"), Values("Fragment"), Values("Output"), + Values("%f64"), Values("VUID-FragDepth-FragDepth-04215"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + FrontFacingAndHelperInvocationSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FrontFacing", "HelperInvocation"), Values("Fragment"), + Values("Input"), Values("%bool"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FrontFacingSuccess, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FrontFacing"), Values("Fragment"), Values("Input"), + Values("%bool"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FrontFacingAndHelperInvocationNotFragment, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("FrontFacing", "HelperInvocation"), + Values("Vertex", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%bool"), + Values("VUID-FrontFacing-FrontFacing-04229 " + "VUID-HelperInvocation-HelperInvocation-04239"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + FrontFacingNotFragment, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("FrontFacing"), Values("Vertex", "GLCompute"), Values("Input"), + Values("%bool"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + FrontFacingAndHelperInvocationNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FrontFacing", "HelperInvocation"), Values("Fragment"), + Values("Output"), Values("%bool"), + Values("VUID-FrontFacing-FrontFacing-04230 " + "VUID-HelperInvocation-HelperInvocation-04240"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + FrontFacingNotInput, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FrontFacing"), Values("Fragment"), Values("Output"), + Values("%bool"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + FrontFacingAndHelperInvocationNotBool, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FrontFacing", "HelperInvocation"), Values("Fragment"), + Values("Input"), Values("%f32", "%u32"), + Values("VUID-FrontFacing-FrontFacing-04231 " + "VUID-HelperInvocation-HelperInvocation-04241"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a bool scalar", + "is not a bool scalar")))); + +INSTANTIATE_TEST_SUITE_P( + FrontFacingNotBool, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("FrontFacing"), Values("Fragment"), Values("Input"), + Values("%f32", "%u32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a bool scalar", + "is not a bool scalar")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3Success, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", + "WorkgroupId"), + Values("GLCompute"), Values("Input"), Values("%u32vec3"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3Success, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), + Values("GLCompute"), Values("Input"), Values("%u32vec3"), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotGLCompute, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", + "WorkgroupId"), + Values("Vertex", "Fragment", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32vec3"), + Values("VUID-GlobalInvocationId-GlobalInvocationId-04236 " + "VUID-LocalInvocationId-LocalInvocationId-04281 " + "VUID-NumWorkgroups-NumWorkgroups-04296 " + "VUID-WorkgroupId-WorkgroupId-04422"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute execution model")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotGLCompute, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), + Values("Vertex", "Fragment"), Values("Input"), Values("%u32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute execution model")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", + "WorkgroupId"), + Values("GLCompute"), Values("Output"), Values("%u32vec3"), + Values("VUID-GlobalInvocationId-GlobalInvocationId-04237 " + "VUID-LocalInvocationId-LocalInvocationId-04282 " + "VUID-NumWorkgroups-NumWorkgroups-04297 " + "VUID-WorkgroupId-WorkgroupId-04423"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotInput, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), + Values("GLCompute"), Values("Output"), Values("%u32vec3"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotIntVector, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", + "WorkgroupId"), + Values("GLCompute"), Values("Input"), + Values("%u32arr3", "%f32vec3"), + Values("VUID-GlobalInvocationId-GlobalInvocationId-04238 " + "VUID-LocalInvocationId-LocalInvocationId-04283 " + "VUID-NumWorkgroups-NumWorkgroups-04298 " + "VUID-WorkgroupId-WorkgroupId-04424"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit int vector", + "is not an int vector")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotIntVector, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), + Values("GLCompute"), Values("Input"), + Values("%u32arr3", "%f32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit int vector", + "is not an int vector")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotIntVec3, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", + "WorkgroupId"), + Values("GLCompute"), Values("Input"), Values("%u32vec4"), + Values("VUID-GlobalInvocationId-GlobalInvocationId-04238 " + "VUID-LocalInvocationId-LocalInvocationId-04283 " + "VUID-NumWorkgroups-NumWorkgroups-04298 " + "VUID-WorkgroupId-WorkgroupId-04424"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit int vector", + "has 4 components")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotIntVec3, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups"), + Values("GLCompute"), Values("Input"), Values("%u32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit int vector", + "has 4 components")))); + +INSTANTIATE_TEST_SUITE_P( + ComputeShaderInputInt32Vec3NotInt32Vec, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("GlobalInvocationId", "LocalInvocationId", "NumWorkgroups", + "WorkgroupId"), + Values("GLCompute"), Values("Input"), Values("%u64vec3"), + Values("VUID-GlobalInvocationId-GlobalInvocationId-04238 " + "VUID-LocalInvocationId-LocalInvocationId-04283 " + "VUID-NumWorkgroups-NumWorkgroups-04298 " + "VUID-WorkgroupId-WorkgroupId-04424"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit int vector", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + InvocationIdSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InvocationId"), Values("Geometry", "TessellationControl"), + Values("Input"), Values("%u32"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + InvocationIdInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InvocationId"), + Values("Vertex", "Fragment", "GLCompute", "TessellationEvaluation"), + Values("Input"), Values("%u32"), + Values("VUID-InvocationId-InvocationId-04257"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with TessellationControl or " + "Geometry execution models")))); + +INSTANTIATE_TEST_SUITE_P( + InvocationIdNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InvocationId"), Values("Geometry", "TessellationControl"), + Values("Output"), Values("%u32"), + Values("VUID-InvocationId-InvocationId-04258"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + InvocationIdNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InvocationId"), Values("Geometry", "TessellationControl"), + Values("Input"), Values("%f32", "%u32vec3"), + Values("VUID-InvocationId-InvocationId-04259"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + InvocationIdNotInt32, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InvocationId"), Values("Geometry", "TessellationControl"), + Values("Input"), Values("%u64"), + Values("VUID-InvocationId-InvocationId-04259"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), + Values("%u32"), Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexSuccess, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), + Values("%u32"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), + Values("Geometry", "Fragment", "GLCompute", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32"), + Values("VUID-InstanceIndex-InstanceIndex-04263"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Vertex execution model")))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexInvalidExecutionModel, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), Values("Fragment", "GLCompute"), + Values("Input"), Values("%u32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Vertex execution model")))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), Values("Vertex"), Values("Output"), + Values("%u32"), Values("VUID-InstanceIndex-InstanceIndex-04264"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexNotInput, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), Values("Vertex"), Values("Output"), + Values("%u32"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), + Values("%f32", "%u32vec3"), + Values("VUID-InstanceIndex-InstanceIndex-04265"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexNotIntScalar, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), + Values("%f32", "%u32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + InstanceIndexNotInt32, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("InstanceIndex"), Values("Vertex"), Values("Input"), + Values("%u64"), Values("VUID-InstanceIndex-InstanceIndex-04265"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + LayerAndViewportIndexInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Layer", "ViewportIndex"), Values("Fragment"), + Values("Input"), Values("%u32"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + LayerAndViewportIndexOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Layer", "ViewportIndex"), Values("Geometry"), + Values("Output"), Values("%u32"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + LayerAndViewportIndexInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("Layer", "ViewportIndex"), + Values("TessellationControl", "GLCompute"), Values("Input"), + Values("%u32"), + Values("VUID-Layer-Layer-04272 VUID-ViewportIndex-ViewportIndex-04404"), + Values( + TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Vertex, TessellationEvaluation, " + "Geometry, or Fragment execution models")))); + +INSTANTIATE_TEST_SUITE_P( + ViewportIndexExecutionModelEnabledByCapability, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("ViewportIndex"), Values("Vertex", "TessellationEvaluation"), + Values("Output"), Values("%u32"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "ShaderViewportIndexLayerEXT or ShaderViewportIndex")))); + +INSTANTIATE_TEST_SUITE_P( + LayerExecutionModelEnabledByCapability, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Layer"), Values("Vertex", "TessellationEvaluation"), + Values("Output"), Values("%u32"), Values(nullptr), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "ShaderViewportIndexLayerEXT or ShaderLayer")))); + +INSTANTIATE_TEST_SUITE_P( + LayerAndViewportIndexFragmentNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("Layer", "ViewportIndex"), Values("Fragment"), Values("Output"), + Values("%u32"), + Values("VUID-Layer-Layer-04275 VUID-ViewportIndex-ViewportIndex-04407"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Output storage class if execution model is Fragment", + "which is called with execution model Fragment")))); + +INSTANTIATE_TEST_SUITE_P( + LayerAndViewportIndexGeometryNotOutput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("Layer", "ViewportIndex"), + Values("Vertex", "TessellationEvaluation", "Geometry"), Values("Input"), + Values("%u32"), + Values("VUID-Layer-Layer-04274 VUID-ViewportIndex-ViewportIndex-04406"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Input storage class if execution model is Vertex, " + "TessellationEvaluation, Geometry, or MeshNV", + "which is called with execution model")))); + +INSTANTIATE_TEST_SUITE_P( + LayerAndViewportIndexNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("Layer", "ViewportIndex"), Values("Fragment"), Values("Input"), + Values("%f32", "%u32vec3"), + Values("VUID-Layer-Layer-04276 VUID-ViewportIndex-ViewportIndex-04408"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + LayerAndViewportIndexNotInt32, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("Layer", "ViewportIndex"), Values("Fragment"), Values("Input"), + Values("%u64"), + Values("VUID-Layer-Layer-04276 VUID-ViewportIndex-ViewportIndex-04408"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + LayerCapability, + ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values(SPV_ENV_VULKAN_1_2), Values("Layer"), Values("Vertex"), + Values("Output"), Values("%u32"), + Values("OpCapability ShaderLayer\n"), Values(nullptr), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ViewportIndexCapability, + ValidateGenericCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values(SPV_ENV_VULKAN_1_2), Values("ViewportIndex"), + Values("Vertex"), Values("Output"), Values("%u32"), + Values("OpCapability ShaderViewportIndex\n"), Values(nullptr), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PatchVerticesSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PatchVertices"), + Values("TessellationEvaluation", "TessellationControl"), + Values("Input"), Values("%u32"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PatchVerticesInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PatchVertices"), + Values("Vertex", "Fragment", "GLCompute", "Geometry"), + Values("Input"), Values("%u32"), + Values("VUID-PatchVertices-PatchVertices-04308"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with TessellationControl or " + "TessellationEvaluation execution models")))); + +INSTANTIATE_TEST_SUITE_P( + PatchVerticesNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PatchVertices"), + Values("TessellationEvaluation", "TessellationControl"), + Values("Output"), Values("%u32"), + Values("VUID-PatchVertices-PatchVertices-04309"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + PatchVerticesNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PatchVertices"), + Values("TessellationEvaluation", "TessellationControl"), + Values("Input"), Values("%f32", "%u32vec3"), + Values("VUID-PatchVertices-PatchVertices-04310"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + PatchVerticesNotInt32, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PatchVertices"), + Values("TessellationEvaluation", "TessellationControl"), + Values("Input"), Values("%u64"), + Values("VUID-PatchVertices-PatchVertices-04310"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + PointCoordSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointCoord"), Values("Fragment"), Values("Input"), + Values("%f32vec2"), Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PointCoordNotFragment, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("PointCoord"), + Values("Vertex", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%f32vec2"), + Values("VUID-PointCoord-PointCoord-04311"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + PointCoordNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointCoord"), Values("Fragment"), Values("Output"), + Values("%f32vec2"), Values("VUID-PointCoord-PointCoord-04312"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + PointCoordNotFloatVector, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointCoord"), Values("Fragment"), Values("Input"), + Values("%f32arr2", "%u32vec2"), + Values("VUID-PointCoord-PointCoord-04313"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float vector", + "is not a float vector")))); + +INSTANTIATE_TEST_SUITE_P( + PointCoordNotFloatVec3, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointCoord"), Values("Fragment"), Values("Input"), + Values("%f32vec3"), Values("VUID-PointCoord-PointCoord-04313"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float vector", + "has 3 components")))); + +INSTANTIATE_TEST_SUITE_P( + PointCoordNotF32Vec4, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointCoord"), Values("Fragment"), Values("Input"), + Values("%f64vec2"), Values("VUID-PointCoord-PointCoord-04313"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float vector", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + PointSizeOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointSize"), + Values("Vertex", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Output"), Values("%f32"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PointSizeInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointSize"), + Values("Geometry", "TessellationControl", "TessellationEvaluation"), + Values("Input"), Values("%f32"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PointSizeVertexInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointSize"), Values("Vertex"), Values("Input"), + Values("%f32"), Values("VUID-PointSize-PointSize-04315"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow BuiltIn PointSize " + "to be used for variables with Input storage class if " + "execution model is Vertex.", + "which is called with execution model Vertex.")))); + +INSTANTIATE_TEST_SUITE_P( + PointSizeInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointSize"), Values("GLCompute", "Fragment"), + Values("Input", "Output"), Values("%f32"), + Values("VUID-PointSize-PointSize-04314"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be used only with Vertex, TessellationControl, " + "TessellationEvaluation or Geometry execution models")))); + +INSTANTIATE_TEST_SUITE_P( + PointSizeNotFloatScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointSize"), Values("Vertex"), Values("Output"), + Values("%f32vec4", "%u32"), + Values("VUID-PointSize-PointSize-04317"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float scalar", + "is not a float scalar")))); + +INSTANTIATE_TEST_SUITE_P( + PointSizeNotF32, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointSize"), Values("Vertex"), Values("Output"), + Values("%f64"), Values("VUID-PointSize-PointSize-04317"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + PositionOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), + Values("Vertex", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Output"), Values("%f32vec4"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PositionOutputSuccess, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), Values("Vertex"), Values("Output"), + Values("%f32vec4"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PositionOutputFailure, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), Values("Fragment", "GLCompute"), + Values("Output"), Values("%f32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "WebGPU spec allows BuiltIn Position to be used " + "only with the Vertex execution model.")))); + +INSTANTIATE_TEST_SUITE_P( + PositionInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), + Values("Geometry", "TessellationControl", "TessellationEvaluation"), + Values("Input"), Values("%f32vec4"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PositionInputFailure, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("Position"), Values("Vertex", "Fragment", "GLCompute"), + Values("Input"), Values("%f32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "WebGPU spec allows BuiltIn Position to be only used " + "for variables with Output storage class")))); + +INSTANTIATE_TEST_SUITE_P( + PositionVertexInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), Values("Vertex"), Values("Input"), + Values("%f32vec4"), Values("VUID-Position-Position-04320"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow BuiltIn Position " + "to be used for variables with Input storage class if " + "execution model is Vertex.", + "which is called with execution model Vertex.")))); + +INSTANTIATE_TEST_SUITE_P( + PositionInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), Values("GLCompute", "Fragment"), + Values("Input", "Output"), Values("%f32vec4"), + Values("VUID-Position-Position-04318"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be used only with Vertex, TessellationControl, " + "TessellationEvaluation or Geometry execution models")))); + +INSTANTIATE_TEST_SUITE_P( + PositionNotFloatVector, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), Values("Geometry"), Values("Input"), + Values("%f32arr4", "%u32vec4"), + Values("VUID-Position-Position-04321"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "is not a float vector")))); + +INSTANTIATE_TEST_SUITE_P( + PositionNotFloatVector, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("Position"), Values("Vertex"), Values("Output"), + Values("%f32arr4", "%u32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector")))); + +INSTANTIATE_TEST_SUITE_P( + PositionNotFloatVec4, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), Values("Geometry"), Values("Input"), + Values("%f32vec3"), Values("VUID-Position-Position-04321"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "has 3 components")))); + +INSTANTIATE_TEST_SUITE_P( + PositionNotFloatVec4, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("Position"), Values("Vertex"), Values("Output"), + Values("%f32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector")))); + +INSTANTIATE_TEST_SUITE_P( + PositionNotF32Vec4, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("Position"), Values("Geometry"), Values("Input"), + Values("%f64vec4"), Values("VUID-Position-Position-04321"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveIdInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PrimitiveId"), + Values("Fragment", "TessellationControl", "TessellationEvaluation", + "Geometry"), + Values("Input"), Values("%u32"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveIdOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PrimitiveId"), Values("Geometry"), Values("Output"), + Values("%u32"), Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveIdInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PrimitiveId"), Values("Vertex", "GLCompute"), + Values("Input"), Values("%u32"), + Values("VUID-PrimitiveId-PrimitiveId-04330"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be used only with Fragment, TessellationControl, " + "TessellationEvaluation or Geometry execution models")))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveIdFragmentNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("PrimitiveId"), Values("Fragment"), Values("Output"), + Values("%u32"), Values("VUID-PrimitiveId-PrimitiveId-04334"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Output storage class if execution model is Fragment", + "which is called with execution model Fragment")))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveIdGeometryNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PrimitiveId"), + Values("TessellationControl", "TessellationEvaluation"), + Values("Output"), Values("%u32"), + Values("VUID-PrimitiveId-PrimitiveId-04334"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Output storage class if execution model is Tessellation", + "which is called with execution model Tessellation")))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveIdNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PrimitiveId"), Values("Fragment"), Values("Input"), + Values("%f32", "%u32vec3"), + Values("VUID-PrimitiveId-PrimitiveId-04337"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveIdNotInt32, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PrimitiveId"), Values("Fragment"), Values("Input"), + Values("%u64"), Values("VUID-PrimitiveId-PrimitiveId-04337"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + SampleIdSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SampleId"), Values("Fragment"), Values("Input"), + Values("%u32"), Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SampleIdInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("SampleId"), + Values("Vertex", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32"), Values("VUID-SampleId-SampleId-04354"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + SampleIdNotInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("SampleId"), Values("Fragment"), Values("Output"), + Values("%u32"), Values("VUID-SampleId-SampleId-04355"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn SampleId to be only used " + "for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + SampleIdNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SampleId"), Values("Fragment"), Values("Input"), + Values("%f32", "%u32vec3"), Values("VUID-SampleId-SampleId-04356"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + SampleIdNotInt32, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SampleId"), Values("Fragment"), Values("Input"), + Values("%u64"), Values("VUID-SampleId-SampleId-04356"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + SampleMaskSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SampleMask"), Values("Fragment"), Values("Input", "Output"), + Values("%u32arr2", "%u32arr4"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SampleMaskInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("SampleMask"), + Values("Vertex", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32arr2"), + Values("VUID-SampleMask-SampleMask-04357"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + SampleMaskWrongStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SampleMask"), Values("Fragment"), Values("Workgroup"), + Values("%u32arr2"), Values("VUID-SampleMask-SampleMask-04358"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn SampleMask to be only used for " + "variables with Input or Output storage class")))); + +INSTANTIATE_TEST_SUITE_P( + SampleMaskNotArray, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SampleMask"), Values("Fragment"), Values("Input"), + Values("%f32", "%u32vec3"), + Values("VUID-SampleMask-SampleMask-04359"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int array", + "is not an array")))); + +INSTANTIATE_TEST_SUITE_P( + SampleMaskNotIntArray, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SampleMask"), Values("Fragment"), Values("Input"), + Values("%f32arr2"), Values("VUID-SampleMask-SampleMask-04359"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int array", + "components are not int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + SampleMaskNotInt32Array, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SampleMask"), Values("Fragment"), Values("Input"), + Values("%u64arr2"), Values("VUID-SampleMask-SampleMask-04359"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int array", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + SamplePositionSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SamplePosition"), Values("Fragment"), Values("Input"), + Values("%f32vec2"), Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SamplePositionNotFragment, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("SamplePosition"), + Values("Vertex", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%f32vec2"), + Values("VUID-SamplePosition-SamplePosition-04360"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Fragment execution model")))); + +INSTANTIATE_TEST_SUITE_P( + SamplePositionNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SamplePosition"), Values("Fragment"), Values("Output"), + Values("%f32vec2"), + Values("VUID-SamplePosition-SamplePosition-04361"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + SamplePositionNotFloatVector, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SamplePosition"), Values("Fragment"), Values("Input"), + Values("%f32arr2", "%u32vec4"), + Values("VUID-SamplePosition-SamplePosition-04362"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float vector", + "is not a float vector")))); + +INSTANTIATE_TEST_SUITE_P( + SamplePositionNotFloatVec2, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SamplePosition"), Values("Fragment"), Values("Input"), + Values("%f32vec3"), + Values("VUID-SamplePosition-SamplePosition-04362"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float vector", + "has 3 components")))); + +INSTANTIATE_TEST_SUITE_P( + SamplePositionNotF32Vec2, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("SamplePosition"), Values("Fragment"), Values("Input"), + Values("%f64vec2"), + Values("VUID-SamplePosition-SamplePosition-04362"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float vector", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + TessCoordSuccess, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessCoord"), Values("TessellationEvaluation"), + Values("Input"), Values("%f32vec3"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + TessCoordNotFragment, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("TessCoord"), + Values("Vertex", "GLCompute", "Geometry", "TessellationControl", + "Fragment"), + Values("Input"), Values("%f32vec3"), + Values("VUID-TessCoord-TessCoord-04387"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be used only with TessellationEvaluation execution model")))); + +INSTANTIATE_TEST_SUITE_P( + TessCoordNotInput, ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessCoord"), Values("Fragment"), Values("Output"), + Values("%f32vec3"), Values("VUID-TessCoord-TessCoord-04388"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + TessCoordNotFloatVector, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessCoord"), Values("Fragment"), Values("Input"), + Values("%f32arr3", "%u32vec4"), + Values("VUID-TessCoord-TessCoord-04389"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit float vector", + "is not a float vector")))); + +INSTANTIATE_TEST_SUITE_P( + TessCoordNotFloatVec3, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessCoord"), Values("Fragment"), Values("Input"), + Values("%f32vec2"), Values("VUID-TessCoord-TessCoord-04389"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit float vector", + "has 2 components")))); + +INSTANTIATE_TEST_SUITE_P( + TessCoordNotF32Vec3, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessCoord"), Values("Fragment"), Values("Input"), + Values("%f64vec3"), Values("VUID-TessCoord-TessCoord-04389"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 3-component 32-bit float vector", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterTeseInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), Values("TessellationEvaluation"), + Values("Input"), Values("%f32arr4"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterTescOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), Values("TessellationControl"), + Values("Output"), Values("%f32arr4"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), + Values("Vertex", "GLCompute", "Geometry", "Fragment"), + Values("Input"), Values("%f32arr4"), + Values("VUID-TessLevelOuter-TessLevelOuter-04390"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with TessellationControl or " + "TessellationEvaluation execution models.")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterOutputTese, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), Values("TessellationEvaluation"), + Values("Output"), Values("%f32arr4"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " + "used for variables with Output storage class if execution " + "model is TessellationEvaluation.")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterInputTesc, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), Values("TessellationControl"), + Values("Input"), Values("%f32arr4"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " + "used for variables with Input storage class if execution " + "model is TessellationControl.")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterNotArray, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), Values("TessellationEvaluation"), + Values("Input"), Values("%f32vec4", "%f32"), + Values("VUID-TessLevelOuter-TessLevelOuter-04393"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float array", + "is not an array")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterNotFloatArray, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), Values("TessellationEvaluation"), + Values("Input"), Values("%u32arr4"), + Values("VUID-TessLevelOuter-TessLevelOuter-04393"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float array", + "components are not float scalar")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterNotFloatArr4, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), Values("TessellationEvaluation"), + Values("Input"), Values("%f32arr3"), + Values("VUID-TessLevelOuter-TessLevelOuter-04393"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float array", + "has 3 components")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelOuterNotF32Arr4, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelOuter"), Values("TessellationEvaluation"), + Values("Input"), Values("%f64arr4"), + Values("VUID-TessLevelOuter-TessLevelOuter-04393"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float array", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerTeseInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), Values("TessellationEvaluation"), + Values("Input"), Values("%f32arr2"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerTescOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), Values("TessellationControl"), + Values("Output"), Values("%f32arr2"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), + Values("Vertex", "GLCompute", "Geometry", "Fragment"), + Values("Input"), Values("%f32arr2"), + Values("VUID-TessLevelInner-TessLevelInner-04394"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with TessellationControl or " + "TessellationEvaluation execution models.")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerOutputTese, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), Values("TessellationEvaluation"), + Values("Output"), Values("%f32arr2"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " + "used for variables with Output storage class if execution " + "model is TessellationEvaluation.")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerInputTesc, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), Values("TessellationControl"), + Values("Input"), Values("%f32arr2"), Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " + "used for variables with Input storage class if execution " + "model is TessellationControl.")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerNotArray, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), Values("TessellationEvaluation"), + Values("Input"), Values("%f32vec2", "%f32"), + Values("VUID-TessLevelInner-TessLevelInner-04397"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float array", + "is not an array")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerNotFloatArray, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), Values("TessellationEvaluation"), + Values("Input"), Values("%u32arr2"), + Values("VUID-TessLevelInner-TessLevelInner-04397"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float array", + "components are not float scalar")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerNotFloatArr2, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), Values("TessellationEvaluation"), + Values("Input"), Values("%f32arr3"), + Values("VUID-TessLevelInner-TessLevelInner-04397"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float array", + "has 3 components")))); + +INSTANTIATE_TEST_SUITE_P( + TessLevelInnerNotF32Arr2, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("TessLevelInner"), Values("TessellationEvaluation"), + Values("Input"), Values("%f64arr2"), + Values("VUID-TessLevelInner-TessLevelInner-04397"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 2-component 32-bit float array", + "has components with bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), + Values("%u32"), Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexSuccess, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), + Values("%u32"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("VertexIndex"), + Values("Fragment", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32"), + Values("VUID-VertexIndex-VertexIndex-04398"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Vertex execution model")))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexInvalidExecutionModel, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("VertexIndex"), Values("Fragment", "GLCompute"), + Values("Input"), Values("%u32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Vertex execution model")))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("VertexIndex"), Values("Vertex"), Values("Output"), + Values("%u32"), Values("VUID-VertexIndex-VertexIndex-04399"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn VertexIndex to be only " + "used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexNotInput, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("VertexIndex"), Values("Vertex"), Values("Output"), + Values("%u32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "WebGPU spec allows BuiltIn VertexIndex to be only " + "used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), + Values("%f32", "%u32vec3"), + Values("VUID-VertexIndex-VertexIndex-04400"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexNotIntScalar, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), + Values("%f32", "%u32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + VertexIndexNotInt32, + ValidateVulkanCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("VertexIndex"), Values("Vertex"), Values("Input"), + Values("%u64"), Values("VUID-VertexIndex-VertexIndex-04400"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + LocalInvocationIndexSuccess, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("LocalInvocationIndex"), Values("GLCompute"), + Values("Input"), Values("%u32"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + LocalInvocationIndexInvalidExecutionModel, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("LocalInvocationIndex"), Values("Fragment", "Vertex"), + Values("Input"), Values("%u32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with GLCompute execution model")))); + +INSTANTIATE_TEST_SUITE_P( + LocalInvocationIndexNotInput, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine( + Values("LocalInvocationIndex"), Values("GLCompute"), Values("Output"), + Values("%u32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "WebGPU spec allows BuiltIn LocalInvocationIndex to " + "be only used for variables with Input storage " + "class")))); + +INSTANTIATE_TEST_SUITE_P( + LocalInvocationIndexNotIntScalar, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("LocalInvocationIndex"), Values("GLCompute"), + Values("Input"), Values("%f32", "%u32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int", "is not an int")))); + +INSTANTIATE_TEST_SUITE_P( + AllowListRejection, + ValidateWebGPUCombineBuiltInExecutionModelDataTypeResult, + Combine(Values("PointSize", "ClipDistance", "CullDistance", "VertexId", + "InstanceId", "PointCoord", "SampleMask", "HelperInvocation", + "WorkgroupId"), + Values("Vertex"), Values("Input"), Values("%u32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "WebGPU does not allow BuiltIn")))); + +INSTANTIATE_TEST_SUITE_P( + BaseInstanceOrVertexSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("BaseInstance", "BaseVertex"), Values("Vertex"), + Values("Input"), Values("%u32"), + Values("OpCapability DrawParameters\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + BaseInstanceOrVertexInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("BaseInstance", "BaseVertex"), + Values("Fragment", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32"), + Values("OpCapability DrawParameters\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"), + Values("VUID-BaseInstance-BaseInstance-04181 " + "VUID-BaseVertex-BaseVertex-04184"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Vertex execution model")))); + +INSTANTIATE_TEST_SUITE_P( + BaseInstanceOrVertexNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("BaseInstance", "BaseVertex"), Values("Vertex"), + Values("Output"), Values("%u32"), + Values("OpCapability DrawParameters\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"), + Values("VUID-BaseInstance-BaseInstance-04182 " + "VUID-BaseVertex-BaseVertex-04185"), + Values(TestResult(SPV_ERROR_INVALID_DATA, "Vulkan spec allows", + "used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + BaseInstanceOrVertexNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("BaseInstance", "BaseVertex"), Values("Vertex"), + Values("Input"), Values("%f32", "%u32vec3"), + Values("OpCapability DrawParameters\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"), + Values("VUID-BaseInstance-BaseInstance-04183 " + "VUID-BaseVertex-BaseVertex-04186"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + DrawIndexSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("DrawIndex"), Values("Vertex"), Values("Input"), + Values("%u32"), Values("OpCapability DrawParameters\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + DrawIndexMeshSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("DrawIndex"), Values("MeshNV", "TaskNV"), Values("Input"), + Values("%u32"), Values("OpCapability MeshShadingNV\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\nOpExtension " + "\"SPV_NV_mesh_shader\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + DrawIndexInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("DrawIndex"), + Values("Fragment", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32"), + Values("OpCapability DrawParameters\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"), + Values("VUID-DrawIndex-DrawIndex-04207"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "to be used only with Vertex, MeshNV, or TaskNV " + "execution model")))); + +INSTANTIATE_TEST_SUITE_P( + DrawIndexNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("DrawIndex"), Values("Vertex"), Values("Output"), + Values("%u32"), Values("OpCapability DrawParameters\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"), + Values("VUID-DrawIndex-DrawIndex-04208"), + Values(TestResult(SPV_ERROR_INVALID_DATA, "Vulkan spec allows", + "used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + DrawIndexNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("DrawIndex"), Values("Vertex"), Values("Input"), + Values("%f32", "%u32vec3"), Values("OpCapability DrawParameters\n"), + Values("OpExtension \"SPV_KHR_shader_draw_parameters\"\n"), + Values("VUID-DrawIndex-DrawIndex-04209"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + ViewIndexSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("ViewIndex"), + Values("Fragment", "Vertex", "Geometry", "TessellationControl", + "TessellationEvaluation"), + Values("Input"), Values("%u32"), Values("OpCapability MultiView\n"), + Values("OpExtension \"SPV_KHR_multiview\"\n"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ViewIndexInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("ViewIndex"), Values("GLCompute"), Values("Input"), + Values("%u32"), Values("OpCapability MultiView\n"), + Values("OpExtension \"SPV_KHR_multiview\"\n"), + Values("VUID-ViewIndex-ViewIndex-04401"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be not be used with GLCompute execution model")))); + +INSTANTIATE_TEST_SUITE_P( + ViewIndexNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("ViewIndex"), Values("Vertex"), Values("Output"), + Values("%u32"), Values("OpCapability MultiView\n"), + Values("OpExtension \"SPV_KHR_multiview\"\n"), + Values("VUID-ViewIndex-ViewIndex-04402"), + Values(TestResult(SPV_ERROR_INVALID_DATA, "Vulkan spec allows", + "used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + ViewIndexNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("ViewIndex"), Values("Vertex"), Values("Input"), + Values("%f32", "%u32vec3"), Values("OpCapability MultiView\n"), + Values("OpExtension \"SPV_KHR_multiview\"\n"), + Values("VUID-ViewIndex-ViewIndex-04403"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + DeviceIndexSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("DeviceIndex"), + Values("Fragment", "Vertex", "Geometry", "TessellationControl", + "TessellationEvaluation", "GLCompute"), + Values("Input"), Values("%u32"), + Values("OpCapability DeviceGroup\n"), + Values("OpExtension \"SPV_KHR_device_group\"\n"), Values(nullptr), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + DeviceIndexNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("DeviceIndex"), Values("Fragment", "Vertex", "GLCompute"), + Values("Output"), Values("%u32"), + Values("OpCapability DeviceGroup\n"), + Values("OpExtension \"SPV_KHR_device_group\"\n"), + Values("VUID-DeviceIndex-DeviceIndex-04205"), + Values(TestResult(SPV_ERROR_INVALID_DATA, "Vulkan spec allows", + "used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + DeviceIndexNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("DeviceIndex"), Values("Fragment", "Vertex", "GLCompute"), + Values("Input"), Values("%f32", "%u32vec3"), + Values("OpCapability DeviceGroup\n"), + Values("OpExtension \"SPV_KHR_device_group\"\n"), + Values("VUID-DeviceIndex-DeviceIndex-04206"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +CodeGenerator GetArrayedVariableCodeGenerator(spv_target_env env, + const char* const built_in, + const char* const execution_model, + const char* const storage_class, + const char* const data_type) { + CodeGenerator generator = + spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = "OpDecorate %built_in_var BuiltIn "; + generator.before_types_ += built_in; + generator.before_types_ += "\n"; + + std::ostringstream after_types; + after_types << "%built_in_array = OpTypeArray " << data_type << " %u32_3\n"; + if (InitializerRequired(env, storage_class)) { + after_types << "%built_in_array_null = OpConstantNull %built_in_array\n"; + } + + after_types << "%built_in_ptr = OpTypePointer " << storage_class + << " %built_in_array\n"; + after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; + if (InitializerRequired(env, storage_class)) { + after_types << " %built_in_array_null"; + } + after_types << "\n"; + generator.after_types_ = after_types.str(); + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = execution_model; + entry_point.interfaces = "%built_in_var"; + // Any kind of reference would do. + entry_point.body = R"( +%val = OpBitcast %u32 %built_in_var +)"; + + std::ostringstream execution_modes; + if (0 == std::strcmp(execution_model, "Fragment")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " OriginUpperLeft\n"; + if (0 == std::strcmp(built_in, "FragDepth")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " DepthReplacing\n"; + } + } + if (0 == std::strcmp(execution_model, "Geometry")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " InputPoints\n"; + execution_modes << "OpExecutionMode %" << entry_point.name + << " OutputPoints\n"; + } + if (0 == std::strcmp(execution_model, "GLCompute")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " LocalSize 1 1 1\n"; + } + entry_point.execution_modes = execution_modes.str(); + + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_P(ValidateVulkanCombineBuiltInArrayedVariable, Variable) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const TestResult& test_result = std::get<4>(GetParam()); + + CodeGenerator generator = GetArrayedVariableCodeGenerator( + SPV_ENV_VULKAN_1_0, built_in, execution_model, storage_class, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } +} + +TEST_P(ValidateWebGPUCombineBuiltInArrayedVariable, Variable) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const TestResult& test_result = std::get<4>(GetParam()); + + CodeGenerator generator = GetArrayedVariableCodeGenerator( + SPV_ENV_WEBGPU_0, built_in, execution_model, storage_class, data_type); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } +} + +INSTANTIATE_TEST_SUITE_P(PointSizeArrayedF32TessControl, + ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("PointSize"), + Values("TessellationControl"), Values("Input"), + Values("%f32"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PointSizeArrayedF64TessControl, ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("PointSize"), Values("TessellationControl"), Values("Input"), + Values("%f64"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float scalar", + "has bit width 64")))); + +INSTANTIATE_TEST_SUITE_P( + PointSizeArrayedF32Vertex, ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("PointSize"), Values("Vertex"), Values("Output"), + Values("%f32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float scalar", + "is not a float scalar")))); + +INSTANTIATE_TEST_SUITE_P(PositionArrayedF32Vec4TessControl, + ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("Position"), + Values("TessellationControl"), Values("Input"), + Values("%f32vec4"), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PositionArrayedF32Vec3TessControl, + ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("Position"), Values("TessellationControl"), Values("Input"), + Values("%f32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "has 3 components")))); + +INSTANTIATE_TEST_SUITE_P( + PositionArrayedF32Vec4Vertex, ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("Position"), Values("Vertex"), Values("Output"), + Values("%f32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "is not a float vector")))); + +INSTANTIATE_TEST_SUITE_P( + PositionArrayedF32Vec4Vertex, ValidateWebGPUCombineBuiltInArrayedVariable, + Combine(Values("Position"), Values("Vertex"), Values("Output"), + Values("%f32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit float vector", + "is not a float vector")))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceOutputSuccess, + ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("ClipDistance", "CullDistance"), + Values("Geometry", "TessellationControl", "TessellationEvaluation"), + Values("Output"), Values("%f32arr2", "%f32arr4"), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceVertexInput, ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("ClipDistance", "CullDistance"), Values("Fragment"), + Values("Input"), Values("%f32arr4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float array", + "components are not float scalar")))); + +INSTANTIATE_TEST_SUITE_P( + ClipAndCullDistanceNotArray, ValidateVulkanCombineBuiltInArrayedVariable, + Combine(Values("ClipDistance", "CullDistance"), + Values("Geometry", "TessellationControl", "TessellationEvaluation"), + Values("Input"), Values("%f32vec2", "%f32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit float array", + "components are not float scalar")))); + +INSTANTIATE_TEST_SUITE_P( + SMBuiltinsInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"), + Values("Vertex", "Fragment", "TessellationControl", + "TessellationEvaluation", "Geometry", "GLCompute"), + Values("Input"), Values("%u32"), + Values("OpCapability ShaderSMBuiltinsNV\n"), + Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SMBuiltinsInputMeshSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"), + Values("MeshNV", "TaskNV"), Values("Input"), Values("%u32"), + Values("OpCapability ShaderSMBuiltinsNV\nOpCapability MeshShadingNV\n"), + Values("OpExtension \"SPV_NV_shader_sm_builtins\"\nOpExtension " + "\"SPV_NV_mesh_shader\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SMBuiltinsInputRaySuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"), + Values("RayGenerationNV", "IntersectionNV", "AnyHitNV", "ClosestHitNV", + "MissNV", "CallableNV"), + Values("Input"), Values("%u32"), + Values("OpCapability ShaderSMBuiltinsNV\nOpCapability RayTracingNV\n"), + Values("OpExtension \"SPV_NV_shader_sm_builtins\"\nOpExtension " + "\"SPV_NV_ray_tracing\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SMBuiltinsNotInput, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"), + Values("Vertex", "Fragment", "TessellationControl", + "TessellationEvaluation", "Geometry", "GLCompute"), + Values("Output"), Values("%u32"), + Values("OpCapability ShaderSMBuiltinsNV\n"), + Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"), + Values(nullptr), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class", + "uses storage class Output")))); + +INSTANTIATE_TEST_SUITE_P( + SMBuiltinsNotIntScalar, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"), + Values("Vertex", "Fragment", "TessellationControl", + "TessellationEvaluation", "Geometry", "GLCompute"), + Values("Input"), Values("%f32", "%u32vec3"), + Values("OpCapability ShaderSMBuiltinsNV\n"), + Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"), + Values(nullptr), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "is not an int scalar")))); + +INSTANTIATE_TEST_SUITE_P( + SMBuiltinsNotInt32, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("SMCountNV", "SMIDNV", "WarpsPerSMNV", "WarpIDNV"), + Values("Vertex", "Fragment", "TessellationControl", + "TessellationEvaluation", "Geometry", "GLCompute"), + Values("Input"), Values("%u64"), + Values("OpCapability ShaderSMBuiltinsNV\n"), + Values("OpExtension \"SPV_NV_shader_sm_builtins\"\n"), + Values(nullptr), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int scalar", + "has bit width 64")))); + +CodeGenerator GetWorkgroupSizeSuccessGenerator(spv_target_env env) { + CodeGenerator generator = + env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpDecorate %workgroup_size BuiltIn WorkgroupSize +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstantComposite %u32vec3 %u32_1 %u32_1 %u32_1 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "GLCompute"; + entry_point.body = R"( +%copy = OpCopyObject %u32vec3 %workgroup_size +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeSuccess) { + CodeGenerator generator = + GetWorkgroupSizeSuccessGenerator(SPV_ENV_VULKAN_1_0); + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeSuccess) { + CodeGenerator generator = GetWorkgroupSizeSuccessGenerator(SPV_ENV_WEBGPU_0); + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +CodeGenerator GetWorkgroupSizeFragmentGenerator(spv_target_env env) { + CodeGenerator generator = + env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpDecorate %workgroup_size BuiltIn WorkgroupSize +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstantComposite %u32vec3 %u32_1 %u32_1 %u32_1 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Fragment"; + entry_point.execution_modes = "OpExecutionMode %main OriginUpperLeft"; + entry_point.body = R"( +%copy = OpCopyObject %u32vec3 %workgroup_size +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeFragment) { + CodeGenerator generator = + GetWorkgroupSizeFragmentGenerator(SPV_ENV_VULKAN_1_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vulkan spec allows BuiltIn WorkgroupSize to be used " + "only with GLCompute execution model")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("is referencing ID <2> (OpConstantComposite) which is " + "decorated with BuiltIn WorkgroupSize in function <1> " + "called with execution model Fragment")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-WorkgroupSize-WorkgroupSize-04425 " + "VUID-WorkgroupSize-WorkgroupSize-04427")); +} + +TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeFragment) { + CodeGenerator generator = GetWorkgroupSizeFragmentGenerator(SPV_ENV_WEBGPU_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU spec allows BuiltIn WorkgroupSize to be used " + "only with GLCompute execution model")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("is referencing ID <2> (OpConstantComposite) which is " + "decorated with BuiltIn WorkgroupSize in function <1> " + "called with execution model Fragment")); +} + +TEST_F(ValidateBuiltIns, WorkgroupSizeNotConstant) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.before_types_ = R"( +OpDecorate %copy BuiltIn WorkgroupSize +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstantComposite %u32vec3 %u32_1 %u32_1 %u32_1 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "GLCompute"; + entry_point.body = R"( +%copy = OpCopyObject %u32vec3 %workgroup_size +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structs or constants")); +} + +CodeGenerator GetWorkgroupSizeNotVectorGenerator(spv_target_env env) { + CodeGenerator generator = + env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpDecorate %workgroup_size BuiltIn WorkgroupSize +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstant %u32 16 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "GLCompute"; + entry_point.body = R"( +%copy = OpCopyObject %u32 %workgroup_size +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotVector) { + CodeGenerator generator = + GetWorkgroupSizeNotVectorGenerator(SPV_ENV_VULKAN_1_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("According to the Vulkan spec BuiltIn WorkgroupSize " + "variable needs to be a 3-component 32-bit int vector. " + "ID <2> (OpConstant) is not an int vector.")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-WorkgroupSize-WorkgroupSize-04427")); +} + +TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeNotVector) { + CodeGenerator generator = + GetWorkgroupSizeNotVectorGenerator(SPV_ENV_WEBGPU_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("According to the WebGPU spec BuiltIn WorkgroupSize " + "variable needs to be a 3-component 32-bit int vector. " + "ID <2> (OpConstant) is not an int vector.")); +} + +CodeGenerator GetWorkgroupSizeNotIntVectorGenerator(spv_target_env env) { + CodeGenerator generator = + env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpDecorate %workgroup_size BuiltIn WorkgroupSize +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstantComposite %f32vec3 %f32_1 %f32_1 %f32_1 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "GLCompute"; + entry_point.body = R"( +%copy = OpCopyObject %f32vec3 %workgroup_size +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotIntVector) { + CodeGenerator generator = + GetWorkgroupSizeNotIntVectorGenerator(SPV_ENV_VULKAN_1_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("According to the Vulkan spec BuiltIn WorkgroupSize " + "variable needs to be a 3-component 32-bit int vector. " + "ID <2> (OpConstantComposite) is not an int vector.")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-WorkgroupSize-WorkgroupSize-04427")); +} + +TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeNotIntVector) { + CodeGenerator generator = + GetWorkgroupSizeNotIntVectorGenerator(SPV_ENV_WEBGPU_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("According to the WebGPU spec BuiltIn WorkgroupSize " + "variable needs to be a 3-component 32-bit int vector. " + "ID <2> (OpConstantComposite) is not an int vector.")); +} + +CodeGenerator GetWorkgroupSizeNotVec3Generator(spv_target_env env) { + CodeGenerator generator = + env == SPV_ENV_WEBGPU_0 ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpDecorate %workgroup_size BuiltIn WorkgroupSize +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstantComposite %u32vec2 %u32_1 %u32_1 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "GLCompute"; + entry_point.body = R"( +%copy = OpCopyObject %u32vec2 %workgroup_size +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_F(ValidateBuiltIns, VulkanWorkgroupSizeNotVec3) { + CodeGenerator generator = + GetWorkgroupSizeNotVec3Generator(SPV_ENV_VULKAN_1_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("According to the Vulkan spec BuiltIn WorkgroupSize " + "variable needs to be a 3-component 32-bit int vector. " + "ID <2> (OpConstantComposite) has 2 components.")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-WorkgroupSize-WorkgroupSize-04427")); +} + +TEST_F(ValidateBuiltIns, WebGPUWorkgroupSizeNotVec3) { + CodeGenerator generator = GetWorkgroupSizeNotVec3Generator(SPV_ENV_WEBGPU_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("According to the WebGPU spec BuiltIn WorkgroupSize " + "variable needs to be a 3-component 32-bit int vector. " + "ID <2> (OpConstantComposite) has 2 components.")); +} + +TEST_F(ValidateBuiltIns, WorkgroupSizeNotInt32Vec) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.before_types_ = R"( +OpDecorate %workgroup_size BuiltIn WorkgroupSize +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstantComposite %u64vec3 %u64_1 %u64_1 %u64_1 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "GLCompute"; + entry_point.body = R"( +%copy = OpCopyObject %u64vec3 %workgroup_size +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("According to the Vulkan spec BuiltIn WorkgroupSize variable " + "needs to be a 3-component 32-bit int vector. ID <2> " + "(OpConstantComposite) has components with bit width 64.")); + EXPECT_THAT(getDiagnosticString(), + AnyVUID("VUID-WorkgroupSize-WorkgroupSize-04427")); +} + +TEST_F(ValidateBuiltIns, WorkgroupSizePrivateVar) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.before_types_ = R"( +OpDecorate %workgroup_size BuiltIn WorkgroupSize +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstantComposite %u32vec3 %u32_1 %u32_1 %u32_1 +%private_ptr_u32vec3 = OpTypePointer Private %u32vec3 +%var = OpVariable %private_ptr_u32vec3 Private %workgroup_size +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "GLCompute"; + entry_point.body = R"( +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateBuiltIns, GeometryPositionInOutSuccess) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpDecorate %input_type Block +OpMemberDecorate %input_type 0 BuiltIn Position +OpDecorate %output_type Block +OpMemberDecorate %output_type 0 BuiltIn Position +)"; + + generator.after_types_ = R"( +%input_type = OpTypeStruct %f32vec4 +%arrayed_input_type = OpTypeArray %input_type %u32_3 +%input_ptr = OpTypePointer Input %arrayed_input_type +%input = OpVariable %input_ptr Input +%input_f32vec4_ptr = OpTypePointer Input %f32vec4 +%output_type = OpTypeStruct %f32vec4 +%output_ptr = OpTypePointer Output %output_type +%output = OpVariable %output_ptr Output +%output_f32vec4_ptr = OpTypePointer Output %f32vec4 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Geometry"; + entry_point.interfaces = "%input %output"; + entry_point.body = R"( +%input_pos = OpAccessChain %input_f32vec4_ptr %input %u32_0 %u32_0 +%output_pos = OpAccessChain %output_f32vec4_ptr %output %u32_0 +%pos = OpLoad %f32vec4 %input_pos +OpStore %output_pos %pos +)"; + generator.entry_points_.push_back(std::move(entry_point)); + generator.entry_points_[0].execution_modes = + "OpExecutionMode %main InputPoints\nOpExecutionMode %main OutputPoints\n"; + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateBuiltIns, WorkgroupIdNotVec3) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.before_types_ = R"( +OpDecorate %workgroup_size BuiltIn WorkgroupSize +OpDecorate %workgroup_id BuiltIn WorkgroupId +)"; + + generator.after_types_ = R"( +%workgroup_size = OpConstantComposite %u32vec3 %u32_1 %u32_1 %u32_1 + %input_ptr = OpTypePointer Input %u32vec2 + %workgroup_id = OpVariable %input_ptr Input +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "GLCompute"; + entry_point.interfaces = "%workgroup_id"; + entry_point.body = R"( +%copy_size = OpCopyObject %u32vec3 %workgroup_size + %load_id = OpLoad %u32vec2 %workgroup_id +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("According to the Vulkan spec BuiltIn WorkgroupId " + "variable needs to be a 3-component 32-bit int vector. " + "ID <2> (OpVariable) has 2 components.")); +} + +TEST_F(ValidateBuiltIns, TwoBuiltInsFirstFails) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpMemberDecorate %input_type 0 BuiltIn FragCoord +OpMemberDecorate %output_type 0 BuiltIn Position +)"; + + generator.after_types_ = R"( +%input_type = OpTypeStruct %f32vec4 +%input_ptr = OpTypePointer Input %input_type +%input = OpVariable %input_ptr Input +%input_f32vec4_ptr = OpTypePointer Input %f32vec4 +%output_type = OpTypeStruct %f32vec4 +%output_ptr = OpTypePointer Output %output_type +%output = OpVariable %output_ptr Output +%output_f32vec4_ptr = OpTypePointer Output %f32vec4 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Geometry"; + entry_point.interfaces = "%input %output"; + entry_point.body = R"( +%input_pos = OpAccessChain %input_f32vec4_ptr %input %u32_0 +%output_pos = OpAccessChain %output_f32vec4_ptr %output %u32_0 +%pos = OpLoad %f32vec4 %input_pos +OpStore %output_pos %pos +)"; + generator.entry_points_.push_back(std::move(entry_point)); + generator.entry_points_[0].execution_modes = + "OpExecutionMode %main InputPoints\nOpExecutionMode %main OutputPoints\n"; + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vulkan spec allows BuiltIn FragCoord to be used only " + "with Fragment execution model")); +} + +TEST_F(ValidateBuiltIns, TwoBuiltInsSecondFails) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpMemberDecorate %input_type 0 BuiltIn Position +OpMemberDecorate %output_type 0 BuiltIn FragCoord +)"; + + generator.after_types_ = R"( +%input_type = OpTypeStruct %f32vec4 +%input_ptr = OpTypePointer Input %input_type +%input = OpVariable %input_ptr Input +%input_f32vec4_ptr = OpTypePointer Input %f32vec4 +%output_type = OpTypeStruct %f32vec4 +%output_ptr = OpTypePointer Output %output_type +%output = OpVariable %output_ptr Output +%output_f32vec4_ptr = OpTypePointer Output %f32vec4 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Geometry"; + entry_point.interfaces = "%input %output"; + entry_point.body = R"( +%input_pos = OpAccessChain %input_f32vec4_ptr %input %u32_0 +%output_pos = OpAccessChain %output_f32vec4_ptr %output %u32_0 +%pos = OpLoad %f32vec4 %input_pos +OpStore %output_pos %pos +)"; + generator.entry_points_.push_back(std::move(entry_point)); + generator.entry_points_[0].execution_modes = + "OpExecutionMode %main InputPoints\nOpExecutionMode %main OutputPoints\n"; + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vulkan spec allows BuiltIn FragCoord to be only used " + "for variables with Input storage class")); +} + +TEST_F(ValidateBuiltIns, VertexPositionVariableSuccess) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.before_types_ = R"( +OpDecorate %position BuiltIn Position +)"; + + generator.after_types_ = R"( +%f32vec4_ptr_output = OpTypePointer Output %f32vec4 +%position = OpVariable %f32vec4_ptr_output Output +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Vertex"; + entry_point.interfaces = "%position"; + entry_point.body = R"( +OpStore %position %f32vec4_0123 +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateBuiltIns, FragmentPositionTwoEntryPoints) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.before_types_ = R"( +OpMemberDecorate %output_type 0 BuiltIn Position +)"; + + generator.after_types_ = R"( +%output_type = OpTypeStruct %f32vec4 +%output_ptr = OpTypePointer Output %output_type +%output = OpVariable %output_ptr Output +%output_f32vec4_ptr = OpTypePointer Output %f32vec4 +)"; + + EntryPoint entry_point; + entry_point.name = "vmain"; + entry_point.execution_model = "Vertex"; + entry_point.interfaces = "%output"; + entry_point.body = R"( +%val1 = OpFunctionCall %void %foo +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + entry_point.name = "fmain"; + entry_point.execution_model = "Fragment"; + entry_point.interfaces = "%output"; + entry_point.execution_modes = "OpExecutionMode %fmain OriginUpperLeft"; + entry_point.body = R"( +%val2 = OpFunctionCall %void %foo +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + generator.add_at_the_end_ = R"( +%foo = OpFunction %void None %func +%foo_entry = OpLabel +%position = OpAccessChain %output_f32vec4_ptr %output %u32_0 +OpStore %position %f32vec4_0123 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vulkan spec allows BuiltIn Position to be used only " + "with Vertex, TessellationControl, " + "TessellationEvaluation or Geometry execution models")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("called with execution model Fragment")); +} + +CodeGenerator GetNoDepthReplacingGenerator(spv_target_env env) { + CodeGenerator generator = + spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpMemberDecorate %output_type 0 BuiltIn FragDepth +)"; + + generator.after_types_ = R"( +%output_type = OpTypeStruct %f32 +%output_null = OpConstantNull %output_type +%output_ptr = OpTypePointer Output %output_type +%output = OpVariable %output_ptr Output %output_null +%output_f32_ptr = OpTypePointer Output %f32 +)"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Fragment"; + entry_point.interfaces = "%output"; + entry_point.execution_modes = "OpExecutionMode %main OriginUpperLeft"; + entry_point.body = R"( +%val2 = OpFunctionCall %void %foo +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + const std::string function_body = R"( +%foo = OpFunction %void None %func +%foo_entry = OpLabel +%frag_depth = OpAccessChain %output_f32_ptr %output %u32_0 +OpStore %frag_depth %f32_1 +OpReturn +OpFunctionEnd +)"; + + if (spvIsWebGPUEnv(env)) { + generator.after_types_ += function_body; + } else { + generator.add_at_the_end_ = function_body; + } + + return generator; +} + +TEST_F(ValidateBuiltIns, VulkanFragmentFragDepthNoDepthReplacing) { + CodeGenerator generator = GetNoDepthReplacingGenerator(SPV_ENV_VULKAN_1_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vulkan spec requires DepthReplacing execution mode to " + "be declared when using BuiltIn FragDepth")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("VUID-FragDepth-FragDepth-04216")); +} + +TEST_F(ValidateBuiltIns, WebGPUFragmentFragDepthNoDepthReplacing) { + CodeGenerator generator = GetNoDepthReplacingGenerator(SPV_ENV_WEBGPU_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU spec requires DepthReplacing execution mode to " + "be declared when using BuiltIn FragDepth")); +} + +CodeGenerator GetOneMainHasDepthReplacingOtherHasntGenerator( + spv_target_env env) { + CodeGenerator generator = + spvIsWebGPUEnv(env) ? CodeGenerator::GetWebGPUShaderCodeGenerator() + : CodeGenerator::GetDefaultShaderCodeGenerator(); + + generator.before_types_ = R"( +OpMemberDecorate %output_type 0 BuiltIn FragDepth +)"; + + generator.after_types_ = R"( +%output_type = OpTypeStruct %f32 +%output_null = OpConstantNull %output_type +%output_ptr = OpTypePointer Output %output_type +%output = OpVariable %output_ptr Output %output_null +%output_f32_ptr = OpTypePointer Output %f32 +)"; + + EntryPoint entry_point; + entry_point.name = "main_d_r"; + entry_point.execution_model = "Fragment"; + entry_point.interfaces = "%output"; + entry_point.execution_modes = + "OpExecutionMode %main_d_r OriginUpperLeft\n" + "OpExecutionMode %main_d_r DepthReplacing"; + entry_point.body = R"( +%val2 = OpFunctionCall %void %foo +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + entry_point.name = "main_no_d_r"; + entry_point.execution_model = "Fragment"; + entry_point.interfaces = "%output"; + entry_point.execution_modes = "OpExecutionMode %main_no_d_r OriginUpperLeft"; + entry_point.body = R"( +%val3 = OpFunctionCall %void %foo +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + const std::string function_body = R"( +%foo = OpFunction %void None %func +%foo_entry = OpLabel +%frag_depth = OpAccessChain %output_f32_ptr %output %u32_0 +OpStore %frag_depth %f32_1 +OpReturn +OpFunctionEnd +)"; + + if (spvIsWebGPUEnv(env)) { + generator.after_types_ += function_body; + } else { + generator.add_at_the_end_ = function_body; + } + + return generator; +} + +TEST_F(ValidateBuiltIns, + VulkanFragmentFragDepthOneMainHasDepthReplacingOtherHasnt) { + CodeGenerator generator = + GetOneMainHasDepthReplacingOtherHasntGenerator(SPV_ENV_VULKAN_1_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vulkan spec requires DepthReplacing execution mode to " + "be declared when using BuiltIn FragDepth")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("VUID-FragDepth-FragDepth-04216")); +} + +TEST_F(ValidateBuiltIns, + WebGPUFragmentFragDepthOneMainHasDepthReplacingOtherHasnt) { + CodeGenerator generator = + GetOneMainHasDepthReplacingOtherHasntGenerator(SPV_ENV_WEBGPU_0); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU spec requires DepthReplacing execution mode to " + "be declared when using BuiltIn FragDepth")); +} + +TEST_F(ValidateBuiltIns, AllowInstanceIdWithIntersectionShader) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.capabilities_ += R"( +OpCapability RayTracingNV +)"; + + generator.extensions_ = R"( +OpExtension "SPV_NV_ray_tracing" +)"; + + generator.before_types_ = R"( +OpMemberDecorate %input_type 0 BuiltIn InstanceId +)"; + + generator.after_types_ = R"( +%input_type = OpTypeStruct %u32 +%input_ptr = OpTypePointer Input %input_type +%input = OpVariable %input_ptr Input +)"; + + EntryPoint entry_point; + entry_point.name = "main_d_r"; + entry_point.execution_model = "IntersectionNV"; + entry_point.interfaces = "%input"; + entry_point.body = R"( +%val2 = OpFunctionCall %void %foo +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + generator.add_at_the_end_ = R"( +%foo = OpFunction %void None %func +%foo_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateBuiltIns, DisallowInstanceIdWithRayGenShader) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.capabilities_ += R"( +OpCapability RayTracingNV +)"; + + generator.extensions_ = R"( +OpExtension "SPV_NV_ray_tracing" +)"; + + generator.before_types_ = R"( +OpMemberDecorate %input_type 0 BuiltIn InstanceId +)"; + + generator.after_types_ = R"( +%input_type = OpTypeStruct %u32 +%input_ptr = OpTypePointer Input %input_type +%input_ptr_u32 = OpTypePointer Input %u32 +%input = OpVariable %input_ptr Input +)"; + + EntryPoint entry_point; + entry_point.name = "main_d_r"; + entry_point.execution_model = "RayGenerationNV"; + entry_point.interfaces = "%input"; + entry_point.body = R"( +%input_member = OpAccessChain %input_ptr_u32 %input %u32_0 +)"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vulkan spec allows BuiltIn InstanceId to be used " + "only with IntersectionNV, ClosestHitNV and " + "AnyHitNV execution models")); +} + +TEST_F(ValidateBuiltIns, ValidBuiltinsForMeshShader) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.capabilities_ += R"( +OpCapability MeshShadingNV +)"; + + generator.extensions_ = R"( +OpExtension "SPV_NV_mesh_shader" +)"; + + generator.before_types_ = R"( +OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId +OpDecorate %gl_PrimitiveID PerPrimitiveNV +OpDecorate %gl_Layer BuiltIn Layer +OpDecorate %gl_Layer PerPrimitiveNV +OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex +OpDecorate %gl_ViewportIndex PerPrimitiveNV +)"; + + generator.after_types_ = R"( +%u32_81 = OpConstant %u32 81 +%_arr_int_uint_81 = OpTypeArray %i32 %u32_81 +%_ptr_Output__arr_int_uint_81 = OpTypePointer Output %_arr_int_uint_81 +%gl_PrimitiveID = OpVariable %_ptr_Output__arr_int_uint_81 Output +%gl_Layer = OpVariable %_ptr_Output__arr_int_uint_81 Output +%gl_ViewportIndex = OpVariable %_ptr_Output__arr_int_uint_81 Output +)"; + + EntryPoint entry_point; + entry_point.name = "main_d_r"; + entry_point.execution_model = "MeshNV"; + entry_point.interfaces = "%gl_PrimitiveID %gl_Layer %gl_ViewportIndex"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateBuiltIns, InvalidBuiltinsForMeshShader) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.capabilities_ += R"( +OpCapability MeshShadingNV +)"; + + generator.extensions_ = R"( +OpExtension "SPV_NV_mesh_shader" +)"; + + generator.before_types_ = R"( +OpDecorate %gl_PrimitiveID BuiltIn PrimitiveId +OpDecorate %gl_PrimitiveID PerPrimitiveNV +OpDecorate %gl_Layer BuiltIn Layer +OpDecorate %gl_Layer PerPrimitiveNV +OpDecorate %gl_ViewportIndex BuiltIn ViewportIndex +OpDecorate %gl_ViewportIndex PerPrimitiveNV +)"; + + generator.after_types_ = R"( +%u32_81 = OpConstant %u32 81 +%_arr_float_uint_81 = OpTypeArray %f32 %u32_81 +%_ptr_Output__arr_float_uint_81 = OpTypePointer Output %_arr_float_uint_81 +%gl_PrimitiveID = OpVariable %_ptr_Output__arr_float_uint_81 Output +%gl_Layer = OpVariable %_ptr_Output__arr_float_uint_81 Output +%gl_ViewportIndex = OpVariable %_ptr_Output__arr_float_uint_81 Output +)"; + + EntryPoint entry_point; + entry_point.name = "main_d_r"; + entry_point.execution_model = "MeshNV"; + entry_point.interfaces = "%gl_PrimitiveID %gl_Layer %gl_ViewportIndex"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("needs to be a 32-bit int scalar")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("is not an int scalar")); +} + +TEST_F(ValidateBuiltIns, GetUnderlyingTypeNoAssert) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "PSMa" %12 %17 + OpExecutionMode %4 OriginUpperLeft + OpDecorate %gl_PointCoord BuiltIn PointCoord + OpDecorate %12 Location 0 + OpDecorate %17 Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %gl_PointCoord = OpTypeStruct %v4float + %_ptr_Input_v4float = OpTypePointer Input %v4float + %_ptr_Output_v4float = OpTypePointer Output %v4float + %12 = OpVariable %_ptr_Input_v4float Input + %17 = OpVariable %_ptr_Output_v4float Output + %4 = OpFunction %void None %3 + %15 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("did not find an member index to get underlying data " + "type")); +} + +TEST_P(ValidateVulkanSubgroupBuiltIns, InMain) { + const char* const built_in = std::get<0>(GetParam()); + const char* const execution_model = std::get<1>(GetParam()); + const char* const storage_class = std::get<2>(GetParam()); + const char* const data_type = std::get<3>(GetParam()); + const TestResult& test_result = std::get<4>(GetParam()); + + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + generator.capabilities_ += R"( +OpCapability GroupNonUniformBallot +)"; + + generator.before_types_ = "OpDecorate %built_in_var BuiltIn "; + generator.before_types_ += built_in; + generator.before_types_ += "\n"; + + std::ostringstream after_types; + after_types << "%built_in_ptr = OpTypePointer " << storage_class << " " + << data_type << "\n"; + after_types << "%built_in_var = OpVariable %built_in_ptr " << storage_class; + after_types << "\n"; + generator.after_types_ = after_types.str(); + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = execution_model; + if (strncmp(storage_class, "Input", 5) == 0 || + strncmp(storage_class, "Output", 6) == 0) { + entry_point.interfaces = "%built_in_var"; + } + entry_point.body = + std::string("%ld = OpLoad ") + data_type + " %built_in_var\n"; + + std::ostringstream execution_modes; + if (0 == std::strcmp(execution_model, "Fragment")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " OriginUpperLeft\n"; + if (0 == std::strcmp(built_in, "FragDepth")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " DepthReplacing\n"; + } + } + if (0 == std::strcmp(execution_model, "Geometry")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " InputPoints\n"; + execution_modes << "OpExecutionMode %" << entry_point.name + << " OutputPoints\n"; + } + if (0 == std::strcmp(execution_model, "GLCompute")) { + execution_modes << "OpExecutionMode %" << entry_point.name + << " LocalSize 1 1 1\n"; + } + entry_point.execution_modes = execution_modes.str(); + + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_1)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str2)); + } +} + +INSTANTIATE_TEST_SUITE_P( + SubgroupMaskNotVec4, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupEqMask", "SubgroupGeMask", "SubgroupGtMask", + "SubgroupLeMask", "SubgroupLtMask"), + Values("GLCompute"), Values("Input"), Values("%u32vec3"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit int vector")))); + +INSTANTIATE_TEST_SUITE_P( + SubgroupMaskNotU32, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupEqMask", "SubgroupGeMask", "SubgroupGtMask", + "SubgroupLeMask", "SubgroupLtMask"), + Values("GLCompute"), Values("Input"), Values("%f32vec4"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 4-component 32-bit int vector")))); + +INSTANTIATE_TEST_SUITE_P( + SubgroupMaskNotInput, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupEqMask", "SubgroupGeMask", "SubgroupGtMask", + "SubgroupLeMask", "SubgroupLtMask"), + Values("GLCompute"), Values("Output", "Workgroup", "Private"), + Values("%u32vec4"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P(SubgroupMaskOk, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupEqMask", "SubgroupGeMask", + "SubgroupGtMask", "SubgroupLeMask", + "SubgroupLtMask"), + Values("GLCompute"), Values("Input"), + Values("%u32vec4"), + Values(TestResult(SPV_SUCCESS, "")))); + +TEST_F(ValidateBuiltIns, SubgroupMaskMemberDecorate) { + const std::string text = R"( +OpCapability Shader +OpCapability GroupNonUniformBallot +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +OpMemberDecorate %struct 0 BuiltIn SubgroupEqMask +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "BuiltIn SubgroupEqMask cannot be used as a member decoration")); +} + +INSTANTIATE_TEST_SUITE_P( + SubgroupInvocationIdAndSizeNotU32, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"), + Values("GLCompute"), Values("Input"), Values("%f32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int")))); + +INSTANTIATE_TEST_SUITE_P( + SubgroupInvocationIdAndSizeNotInput, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"), + Values("GLCompute"), Values("Output", "Workgroup", "Private"), + Values("%u32"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P( + SubgroupInvocationIdAndSizeOk, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupLocalInvocationId", "SubgroupSize"), + Values("GLCompute"), Values("Input"), Values("%u32"), + Values(TestResult(SPV_SUCCESS, "")))); + +TEST_F(ValidateBuiltIns, SubgroupSizeMemberDecorate) { + const std::string text = R"( +OpCapability Shader +OpCapability GroupNonUniform +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +OpMemberDecorate %struct 0 BuiltIn SubgroupSize +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("BuiltIn SubgroupSize cannot be used as a member decoration")); +} + +INSTANTIATE_TEST_SUITE_P( + SubgroupNumAndIdNotU32, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupId", "NumSubgroups"), Values("GLCompute"), + Values("Input"), Values("%f32"), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "needs to be a 32-bit int")))); + +INSTANTIATE_TEST_SUITE_P( + SubgroupNumAndIdNotInput, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupId", "NumSubgroups"), Values("GLCompute"), + Values("Output", "Workgroup", "Private"), Values("%u32"), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "to be only used for variables with Input storage class")))); + +INSTANTIATE_TEST_SUITE_P(SubgroupNumAndIdOk, ValidateVulkanSubgroupBuiltIns, + Combine(Values("SubgroupId", "NumSubgroups"), + Values("GLCompute"), Values("Input"), + Values("%u32"), + Values(TestResult(SPV_SUCCESS, "")))); + +TEST_F(ValidateBuiltIns, SubgroupIdMemberDecorate) { + const std::string text = R"( +OpCapability Shader +OpCapability GroupNonUniform +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +OpMemberDecorate %struct 0 BuiltIn SubgroupId +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%struct = OpTypeStruct %int +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("BuiltIn SubgroupId cannot be used as a member decoration")); +} + +TEST_F(ValidateBuiltIns, TargetIsType) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %void BuiltIn Position +%void = OpTypeVoid +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("BuiltIns can only target variables, structs or constants")); +} + +TEST_F(ValidateBuiltIns, TargetIsVariable) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %wg_var BuiltIn Position +%int = OpTypeInt 32 0 +%int_wg_ptr = OpTypePointer Workgroup %int +%wg_var = OpVariable %int_wg_ptr Workgroup +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBuiltIns, TargetIsStruct) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct BuiltIn Position +%struct = OpTypeStruct +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBuiltIns, TargetIsConstant) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int0 BuiltIn Position +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateBuiltIns, TargetIsSpecConstant) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %int0 BuiltIn Position +%int = OpTypeInt 32 0 +%int0 = OpSpecConstant %int 0 +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +INSTANTIATE_TEST_SUITE_P( + PrimitiveShadingRateOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("PrimitiveShadingRateKHR"), Values("Vertex", "Geometry"), + Values("Output"), Values("%u32"), + Values("OpCapability FragmentShadingRateKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveShadingRateMeshOutputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("PrimitiveShadingRateKHR"), Values("MeshNV"), + Values("Output"), Values("%u32"), + Values("OpCapability FragmentShadingRateKHR\nOpCapability " + "MeshShadingNV\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\nOpExtension " + "\"SPV_NV_mesh_shader\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveShadingRateInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("PrimitiveShadingRateKHR"), Values("Fragment"), Values("Output"), + Values("%u32"), Values("OpCapability FragmentShadingRateKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"), + Values("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04484 "), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn PrimitiveShadingRateKHR to be used " + "only with Vertex, Geometry, or MeshNV execution models.")))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveShadingRateInvalidStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("PrimitiveShadingRateKHR"), Values("Vertex"), Values("Input"), + Values("%u32"), Values("OpCapability FragmentShadingRateKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"), + Values("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04485 "), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn PrimitiveShadingRateKHR to be only " + "used for variables with Output storage class.")))); + +INSTANTIATE_TEST_SUITE_P( + PrimitiveShadingRateInvalidType, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("PrimitiveShadingRateKHR"), Values("Vertex"), Values("Output"), + Values("%f32"), Values("OpCapability FragmentShadingRateKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"), + Values("VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04486 "), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "According to the Vulkan spec BuiltIn PrimitiveShadingRateKHR " + "variable needs to be a 32-bit int scalar.")))); + +INSTANTIATE_TEST_SUITE_P( + ShadingRateInputSuccess, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("ShadingRateKHR"), Values("Fragment"), Values("Input"), + Values("%u32"), Values("OpCapability FragmentShadingRateKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"), + Values(nullptr), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ShadingRateInvalidExecutionModel, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("ShadingRateKHR"), Values("Vertex"), Values("Input"), + Values("%u32"), Values("OpCapability FragmentShadingRateKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"), + Values("VUID-ShadingRateKHR-ShadingRateKHR-04490 "), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn ShadingRateKHR to be used " + "only with the Fragment execution model.")))); + +INSTANTIATE_TEST_SUITE_P( + ShadingRateInvalidStorageClass, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine(Values("ShadingRateKHR"), Values("Fragment"), Values("Output"), + Values("%u32"), Values("OpCapability FragmentShadingRateKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"), + Values("VUID-ShadingRateKHR-ShadingRateKHR-04491 "), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "Vulkan spec allows BuiltIn ShadingRateKHR to be only " + "used for variables with Input storage class.")))); + +INSTANTIATE_TEST_SUITE_P( + ShadingRateInvalidType, + ValidateVulkanCombineBuiltInExecutionModelDataTypeCapabilityExtensionResult, + Combine( + Values("ShadingRateKHR"), Values("Fragment"), Values("Input"), + Values("%f32"), Values("OpCapability FragmentShadingRateKHR\n"), + Values("OpExtension \"SPV_KHR_fragment_shading_rate\"\n"), + Values("VUID-ShadingRateKHR-ShadingRateKHR-04492 "), + Values(TestResult(SPV_ERROR_INVALID_DATA, + "According to the Vulkan spec BuiltIn ShadingRateKHR " + "variable needs to be a 32-bit int scalar.")))); +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_capability_test.cpp b/third_party/spirv-tools/test/val/val_capability_test.cpp new file mode 100644 index 0000000..9705cb8 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_capability_test.cpp @@ -0,0 +1,2915 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for Logical Layout + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/assembly_grammar.h" +#include "source/spirv_target_env.h" +#include "spirv-tools/libspirv.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using spvtest::ScopedContext; +using testing::Combine; +using testing::Eq; +using testing::HasSubstr; +using testing::Values; +using testing::ValuesIn; + +// Parameter for validation test fixtures. The first std::string is a +// capability name that will begin the assembly under test, the second the +// remainder assembly, and the std::vector at the end determines whether the +// test expects success or failure. See below for details and convenience +// methods to access each one. +// +// The assembly to test is composed from a variable top line and a fixed +// remainder. The top line will be an OpCapability instruction, while the +// remainder will be some assembly text that succeeds or fails to assemble +// depending on which capability was chosen. For instance, the following will +// succeed: +// +// OpCapability Pipes ; implies Kernel +// OpLifetimeStop %1 0 ; requires Kernel +// +// and the following will fail: +// +// OpCapability Kernel +// %1 = OpTypeNamedBarrier ; requires NamedBarrier +// +// So how does the test parameter capture which capabilities should cause +// success and which shouldn't? The answer is in the last element: it's a +// std::vector of capabilities that make the remainder assembly succeed. So if +// the first-line capability exists in that std::vector, success is expected; +// otherwise, failure is expected in the tests. +// +// We will use testing::Combine() to vary the first line: when we combine +// AllCapabilities() with a single remainder assembly, we generate enough test +// cases to try the assembly with every possible capability that could be +// declared. However, Combine() only produces tuples -- it cannot produce, say, +// a struct. Therefore, this type must be a tuple. +using CapTestParameter = + std::tuple>>; + +const std::string& Capability(const CapTestParameter& p) { + return std::get<0>(p); +} +const std::string& Remainder(const CapTestParameter& p) { + return std::get<1>(p).first; +} +const std::vector& MustSucceed(const CapTestParameter& p) { + return std::get<1>(p).second; +} + +// Creates assembly to test from p. +std::string MakeAssembly(const CapTestParameter& p) { + std::ostringstream ss; + const std::string& capability = Capability(p); + if (!capability.empty()) { + ss << "OpCapability " << capability << "\n"; + } + ss << Remainder(p); + return ss.str(); +} + +// Expected validation result for p. +spv_result_t ExpectedResult(const CapTestParameter& p) { + const auto& caps = MustSucceed(p); + auto found = find(begin(caps), end(caps), Capability(p)); + return (found == end(caps)) ? SPV_ERROR_INVALID_CAPABILITY : SPV_SUCCESS; +} + +// Assembles using v1.0, unless the parameter's capability requires v1.1. +using ValidateCapability = spvtest::ValidateBase; + +// Always assembles using v1.1. +using ValidateCapabilityV11 = spvtest::ValidateBase; + +// Always assembles using Vulkan 1.0. +// TODO(dneto): Refactor all these tests to scale better across environments. +using ValidateCapabilityVulkan10 = spvtest::ValidateBase; +// Always assembles using OpenGL 4.0. +using ValidateCapabilityOpenGL40 = spvtest::ValidateBase; +// Always assembles using Vulkan 1.1. +using ValidateCapabilityVulkan11 = spvtest::ValidateBase; +// Always assembles using Vulkan 1.2. +using ValidateCapabilityVulkan12 = spvtest::ValidateBase; +// Always assembles using WebGPU. +using ValidateCapabilityWebGPU = spvtest::ValidateBase; + +TEST_F(ValidateCapability, Default) { + const char str[] = R"( + OpCapability Kernel + OpCapability Linkage + OpCapability Matrix + OpMemoryModel Logical OpenCL +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 3 +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// clang-format off +const std::vector& AllCapabilities() { + static const auto r = new std::vector{ + "", + "Matrix", + "Shader", + "Geometry", + "Tessellation", + "Addresses", + "Linkage", + "Kernel", + "Vector16", + "Float16Buffer", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "ImageBasic", + "ImageReadWrite", + "ImageMipmap", + "Pipes", + "Groups", + "DeviceEnqueue", + "LiteralSampler", + "AtomicStorage", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "GenericPointer", + "Int8", + "InputAttachment", + "SparseResidency", + "MinLod", + "Sampled1D", + "Image1D", + "SampledCubeArray", + "SampledBuffer", + "ImageBuffer", + "ImageMSArray", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "SubgroupDispatch", + "NamedBarrier", + "PipeStorage", + "GroupNonUniform", + "GroupNonUniformVote", + "GroupNonUniformArithmetic", + "GroupNonUniformBallot", + "GroupNonUniformShuffle", + "GroupNonUniformShuffleRelative", + "GroupNonUniformClustered", + "GroupNonUniformQuad", + "DrawParameters", + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16", + "UniformAndStorageBuffer16BitAccess", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16", + "DeviceGroup", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers"}; + return *r; +} + +const std::vector& AllSpirV15Capabilities() { + static const auto r = new std::vector{ + "", + "Matrix", + "Shader", + "Geometry", + "Tessellation", + "Addresses", + "Linkage", + "Kernel", + "Vector16", + "Float16Buffer", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "ImageBasic", + "ImageReadWrite", + "ImageMipmap", + "Pipes", + "Groups", + "DeviceEnqueue", + "LiteralSampler", + "AtomicStorage", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "GenericPointer", + "Int8", + "InputAttachment", + "SparseResidency", + "MinLod", + "Sampled1D", + "Image1D", + "SampledCubeArray", + "SampledBuffer", + "ImageBuffer", + "ImageMSArray", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "SubgroupDispatch", + "NamedBarrier", + "PipeStorage", + "GroupNonUniform", + "GroupNonUniformVote", + "GroupNonUniformArithmetic", + "GroupNonUniformBallot", + "GroupNonUniformShuffle", + "GroupNonUniformShuffleRelative", + "GroupNonUniformClustered", + "GroupNonUniformQuad", + "DrawParameters", + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16", + "UniformAndStorageBuffer16BitAccess", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16", + "DeviceGroup", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers", + "DenormPreserve", + "DenormFlushToZero", + "SignedZeroInfNanPreserve", + "RoundingModeRTE", + "RoundingModeRTZ", + // Omitted due to extra validation requirements on memory model. + //"VulkanMemoryModel", + //"VulkanMemoryModelDeviceScope", + "StorageBuffer8BitAccess", + "UniformAndStorageBuffer8BitAccess", + "StoragePushConstant8", + "ShaderViewportIndex", + "ShaderLayer", + "PhysicalStorageBufferAddresses", + "RuntimeDescriptorArray", + "UniformTexelBufferArrayDynamicIndexing", + "StorageTexelBufferArrayDynamicIndexing", + "UniformBufferArrayNonUniformIndexing", + "SampledImageArrayNonUniformIndexing", + "StorageBufferArrayNonUniformIndexing", + "StorageImageArrayNonUniformIndexing", + "InputAttachmentArrayNonUniformIndexing", + "UniformTexelBufferArrayNonUniformIndexing", + "StorageTexelBufferArrayNonUniformIndexing"}; + return *r; +} + +const std::vector& AllSpirV10Capabilities() { + static const auto r = new std::vector{ + "", + "Matrix", + "Shader", + "Geometry", + "Tessellation", + "Addresses", + "Linkage", + "Kernel", + "Vector16", + "Float16Buffer", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "ImageBasic", + "ImageReadWrite", + "ImageMipmap", + "Pipes", + "Groups", + "DeviceEnqueue", + "LiteralSampler", + "AtomicStorage", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "GenericPointer", + "Int8", + "InputAttachment", + "SparseResidency", + "MinLod", + "Sampled1D", + "Image1D", + "SampledCubeArray", + "SampledBuffer", + "ImageBuffer", + "ImageMSArray", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport"}; + return *r; +} + +const std::vector& AllVulkan10Capabilities() { + static const auto r = new std::vector{ + "", + "Matrix", + "Shader", + "InputAttachment", + "Sampled1D", + "Image1D", + "SampledBuffer", + "ImageBuffer", + "ImageQuery", + "DerivativeControl", + "Geometry", + "Tessellation", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "Int8", + "SparseResidency", + "MinLod", + "SampledCubeArray", + "ImageMSArray", + "StorageImageExtendedFormats", + "InterpolationFunction", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "TransformFeedback", + "GeometryStreams"}; + return *r; +} + +const std::vector& AllVulkan11Capabilities() { + static const auto r = new std::vector{ + "", + "Matrix", + "Shader", + "InputAttachment", + "Sampled1D", + "Image1D", + "SampledBuffer", + "ImageBuffer", + "ImageQuery", + "DerivativeControl", + "Geometry", + "Tessellation", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "Int8", + "SparseResidency", + "MinLod", + "SampledCubeArray", + "ImageMSArray", + "StorageImageExtendedFormats", + "InterpolationFunction", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "GroupNonUniform", + "GroupNonUniformVote", + "GroupNonUniformArithmetic", + "GroupNonUniformBallot", + "GroupNonUniformShuffle", + "GroupNonUniformShuffleRelative", + "GroupNonUniformClustered", + "GroupNonUniformQuad", + "DrawParameters", + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16", + "UniformAndStorageBuffer16BitAccess", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16", + "DeviceGroup", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers", + "TransformFeedback", + "GeometryStreams"}; + return *r; +} + +const std::vector& AllVulkan12Capabilities() { + static const auto r = new std::vector{ + "", + "Matrix", + "Shader", + "InputAttachment", + "Sampled1D", + "Image1D", + "SampledBuffer", + "ImageBuffer", + "ImageQuery", + "DerivativeControl", + "Geometry", + "Tessellation", + "Float16", + "Float64", + "Int64", + "Int64Atomics", + "Int16", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "Int8", + "SparseResidency", + "MinLod", + "SampledCubeArray", + "ImageMSArray", + "StorageImageExtendedFormats", + "InterpolationFunction", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "GroupNonUniform", + "GroupNonUniformVote", + "GroupNonUniformArithmetic", + "GroupNonUniformBallot", + "GroupNonUniformShuffle", + "GroupNonUniformShuffleRelative", + "GroupNonUniformClustered", + "GroupNonUniformQuad", + "DrawParameters", + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16", + "UniformAndStorageBuffer16BitAccess", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16", + "DeviceGroup", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers", + "TransformFeedback", + "GeometryStreams", + "DenormPreserve", + "DenormFlushToZero", + "SignedZeroInfNanPreserve", + "RoundingModeRTE", + "RoundingModeRTZ", + "VulkanMemoryModel", + "VulkanMemoryModelDeviceScope", + "StorageBuffer8BitAccess", + "UniformAndStorageBuffer8BitAccess", + "StoragePushConstant8", + "ShaderViewportIndex", + "ShaderLayer", + "PhysicalStorageBufferAddresses", + "RuntimeDescriptorArray", + "UniformTexelBufferArrayDynamicIndexing", + "StorageTexelBufferArrayDynamicIndexing", + "UniformBufferArrayNonUniformIndexing", + "SampledImageArrayNonUniformIndexing", + "StorageBufferArrayNonUniformIndexing", + "StorageImageArrayNonUniformIndexing", + "InputAttachmentArrayNonUniformIndexing", + "UniformTexelBufferArrayNonUniformIndexing", + "StorageTexelBufferArrayNonUniformIndexing"}; + return *r; +} + +const std::vector& AllWebGPUCapabilities() { + static const auto r = new std::vector{ + "", + "Shader", + "Matrix", + "Sampled1D", + "Image1D", + "ImageQuery", + "DerivativeControl"}; + return *r; +} + +const std::vector& MatrixDependencies() { + static const auto r = new std::vector{ + "Matrix", + "Shader", + "Geometry", + "Tessellation", + "AtomicStorage", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "InputAttachment", + "SparseResidency", + "MinLod", + "SampledCubeArray", + "ImageMSArray", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "DrawParameters", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers"}; + return *r; +} + +const std::vector& ShaderDependencies() { + static const auto r = new std::vector{ + "Shader", + "Geometry", + "Tessellation", + "AtomicStorage", + "TessellationPointSize", + "GeometryPointSize", + "ImageGatherExtended", + "StorageImageMultisample", + "UniformBufferArrayDynamicIndexing", + "SampledImageArrayDynamicIndexing", + "StorageBufferArrayDynamicIndexing", + "StorageImageArrayDynamicIndexing", + "ClipDistance", + "CullDistance", + "ImageCubeArray", + "SampleRateShading", + "ImageRect", + "SampledRect", + "InputAttachment", + "SparseResidency", + "MinLod", + "SampledCubeArray", + "ImageMSArray", + "StorageImageExtendedFormats", + "ImageQuery", + "DerivativeControl", + "InterpolationFunction", + "TransformFeedback", + "GeometryStreams", + "StorageImageReadWithoutFormat", + "StorageImageWriteWithoutFormat", + "MultiViewport", + "DrawParameters", + "MultiView", + "VariablePointersStorageBuffer", + "VariablePointers"}; + return *r; +} + +const std::vector& TessellationDependencies() { + static const auto r = new std::vector{ + "Tessellation", + "TessellationPointSize"}; + return *r; +} + +const std::vector& GeometryDependencies() { + static const auto r = new std::vector{ + "Geometry", + "GeometryPointSize", + "GeometryStreams", + "MultiViewport"}; + return *r; +} + +const std::vector& GeometryTessellationDependencies() { + static const auto r = new std::vector{ + "Tessellation", + "TessellationPointSize", + "Geometry", + "GeometryPointSize", + "GeometryStreams", + "MultiViewport"}; + return *r; +} + +// Returns the names of capabilities that directly depend on Kernel, +// plus itself. +const std::vector& KernelDependencies() { + static const auto r = new std::vector{ + "Kernel", + "Vector16", + "Float16Buffer", + "ImageBasic", + "ImageReadWrite", + "ImageMipmap", + "Pipes", + "DeviceEnqueue", + "LiteralSampler", + "SubgroupDispatch", + "NamedBarrier", + "PipeStorage"}; + return *r; +} + +const std::vector& KernelAndGroupNonUniformDependencies() { + static const auto r = new std::vector{ + "Kernel", + "Vector16", + "Float16Buffer", + "ImageBasic", + "ImageReadWrite", + "ImageMipmap", + "Pipes", + "DeviceEnqueue", + "LiteralSampler", + "SubgroupDispatch", + "NamedBarrier", + "PipeStorage", + "GroupNonUniform", + "GroupNonUniformVote", + "GroupNonUniformArithmetic", + "GroupNonUniformBallot", + "GroupNonUniformShuffle", + "GroupNonUniformShuffleRelative", + "GroupNonUniformClustered", + "GroupNonUniformQuad"}; + return *r; +} + +const std::vector& AddressesDependencies() { + static const auto r = new std::vector{ + "Addresses", + "GenericPointer"}; + return *r; +} + +const std::vector& Sampled1DDependencies() { + static const auto r = new std::vector{ + "Sampled1D", + "Image1D"}; + return *r; +} + +const std::vector& SampledRectDependencies() { + static const auto r = new std::vector{ + "SampledRect", + "ImageRect"}; + return *r; +} + +const std::vector& SampledBufferDependencies() { + static const auto r = new std::vector{ + "SampledBuffer", + "ImageBuffer"}; + return *r; +} + +const char kOpenCLMemoryModel[] = \ + " OpCapability Kernel" + " OpMemoryModel Logical OpenCL "; + +const char kGLSL450MemoryModel[] = \ + " OpCapability Shader" + " OpMemoryModel Logical GLSL450 "; + +const char kVulkanMemoryModel[] = \ + " OpCapability Shader" + " OpCapability VulkanMemoryModelKHR" + " OpExtension \"SPV_KHR_vulkan_memory_model\"" + " OpMemoryModel Logical VulkanKHR "; + +const char kVoidFVoid[] = \ + " %void = OpTypeVoid" + " %void_f = OpTypeFunction %void" + " %func = OpFunction %void None %void_f" + " %label = OpLabel" + " OpReturn" + " OpFunctionEnd "; + +const char kVoidFVoid2[] = \ + " %void_f = OpTypeFunction %voidt" + " %func = OpFunction %voidt None %void_f" + " %label = OpLabel" + " OpReturn" + " OpFunctionEnd "; + +INSTANTIATE_TEST_SUITE_P(ExecutionModel, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint TessellationControl %func \"shader\"" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint TessellationEvaluation %func \"shader\"" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint Geometry %func \"shader\"" + + " OpExecutionMode %func InputPoints" + + " OpExecutionMode %func OutputPoints" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint Fragment %func \"shader\"" + + " OpExecutionMode %func OriginUpperLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint GLCompute %func \"shader\"" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Kernel %func \"shader\"" + + std::string(kVoidFVoid), KernelDependencies()) +))); + +INSTANTIATE_TEST_SUITE_P(AddressingAndMemoryModel, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(" OpCapability Shader" + " OpMemoryModel Logical Simple" + " OpEntryPoint Vertex %func \"shader\"" + + std::string(kVoidFVoid), AllCapabilities()), +std::make_pair(" OpCapability Shader" + " OpMemoryModel Logical GLSL450" + " OpEntryPoint Vertex %func \"shader\"" + + std::string(kVoidFVoid), AllCapabilities()), +std::make_pair(" OpCapability Kernel" + " OpMemoryModel Logical OpenCL" + " OpEntryPoint Kernel %func \"compute\"" + + std::string(kVoidFVoid), AllCapabilities()), +std::make_pair(" OpCapability Shader" + " OpMemoryModel Physical32 Simple" + " OpEntryPoint Vertex %func \"shader\"" + + std::string(kVoidFVoid), AddressesDependencies()), +std::make_pair(" OpCapability Shader" + " OpMemoryModel Physical32 GLSL450" + " OpEntryPoint Vertex %func \"shader\"" + + std::string(kVoidFVoid), AddressesDependencies()), +std::make_pair(" OpCapability Kernel" + " OpMemoryModel Physical32 OpenCL" + " OpEntryPoint Kernel %func \"compute\"" + + std::string(kVoidFVoid), AddressesDependencies()), +std::make_pair(" OpCapability Shader" + " OpMemoryModel Physical64 Simple" + " OpEntryPoint Vertex %func \"shader\"" + + std::string(kVoidFVoid), AddressesDependencies()), +std::make_pair(" OpCapability Shader" + " OpMemoryModel Physical64 GLSL450" + " OpEntryPoint Vertex %func \"shader\"" + + std::string(kVoidFVoid), AddressesDependencies()), +std::make_pair(" OpCapability Kernel" + " OpMemoryModel Physical64 OpenCL" + " OpEntryPoint Kernel %func \"compute\"" + + std::string(kVoidFVoid), AddressesDependencies()) +))); + +INSTANTIATE_TEST_SUITE_P(ExecutionMode, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func Invocations 42" + + " OpExecutionMode %func InputPoints" + + " OpExecutionMode %func OutputPoints" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func SpacingEqual" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func SpacingFractionalEven" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func SpacingFractionalOdd" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func VertexOrderCw" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func VertexOrderCcw" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Fragment %func \"shader\" " + "OpExecutionMode %func PixelCenterInteger" + + " OpExecutionMode %func OriginUpperLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Fragment %func \"shader\" " + "OpExecutionMode %func OriginUpperLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Fragment %func \"shader\" " + "OpExecutionMode %func OriginLowerLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Fragment %func \"shader\" " + "OpExecutionMode %func EarlyFragmentTests" + + " OpExecutionMode %func OriginUpperLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func PointMode" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Vertex %func \"shader\" " + "OpExecutionMode %func Xfb" + + std::string(kVoidFVoid), std::vector{"TransformFeedback"}), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Fragment %func \"shader\" " + "OpExecutionMode %func DepthReplacing" + + " OpExecutionMode %func OriginUpperLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Fragment %func \"shader\" " + "OpExecutionMode %func DepthGreater" + + " OpExecutionMode %func OriginUpperLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Fragment %func \"shader\" " + "OpExecutionMode %func DepthLess" + + " OpExecutionMode %func OriginUpperLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Fragment %func \"shader\" " + "OpExecutionMode %func DepthUnchanged" + + " OpExecutionMode %func OriginUpperLeft" + + std::string(kVoidFVoid), ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"shader\" " + "OpExecutionMode %func LocalSize 42 42 42" + + std::string(kVoidFVoid), AllCapabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Kernel %func \"shader\" " + "OpExecutionMode %func LocalSizeHint 42 42 42" + + std::string(kVoidFVoid), KernelDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func InputPoints" + + " OpExecutionMode %func OutputPoints" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func InputLines" + + " OpExecutionMode %func OutputLineStrip" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func InputLinesAdjacency" + + " OpExecutionMode %func OutputLineStrip" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func Triangles" + + " OpExecutionMode %func OutputTriangleStrip" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func Triangles" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func InputTrianglesAdjacency" + + " OpExecutionMode %func OutputTriangleStrip" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func Quads" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func Isolines" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func OutputVertices 42" + + " OpExecutionMode %func OutputPoints" + + " OpExecutionMode %func InputPoints" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint TessellationControl %func \"shader\" " + "OpExecutionMode %func OutputVertices 42" + + std::string(kVoidFVoid), TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func OutputPoints" + + " OpExecutionMode %func InputPoints" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func OutputLineStrip" + + " OpExecutionMode %func InputLines" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Geometry %func \"shader\" " + "OpExecutionMode %func OutputTriangleStrip" + + " OpExecutionMode %func Triangles" + + std::string(kVoidFVoid), GeometryDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Kernel %func \"shader\" " + "OpExecutionMode %func VecTypeHint 2" + + std::string(kVoidFVoid), KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Kernel %func \"shader\" " + "OpExecutionMode %func ContractionOff" + + std::string(kVoidFVoid), KernelDependencies())))); + +// clang-format on + +INSTANTIATE_TEST_SUITE_P( + ExecutionModeV11, ValidateCapabilityV11, + Combine(ValuesIn(AllCapabilities()), + Values(std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"shader\" " + "OpExecutionMode %func SubgroupSize 1" + + std::string(kVoidFVoid), + std::vector{"SubgroupDispatch"}), + std::make_pair( + std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"shader\" " + "OpExecutionMode %func SubgroupsPerWorkgroup 65535" + + std::string(kVoidFVoid), + std::vector{"SubgroupDispatch"})))); +// clang-format off + +INSTANTIATE_TEST_SUITE_P(StorageClass, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer UniformConstant %intt\n" + " %var = OpVariable %ptrt UniformConstant\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint Kernel %func \"compute\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Input %intt" + " %var = OpVariable %ptrt Input\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Uniform %intt\n" + " %var = OpVariable %ptrt Uniform\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Output %intt\n" + " %var = OpVariable %ptrt Output\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Workgroup %intt\n" + " %var = OpVariable %ptrt Workgroup\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer CrossWorkgroup %intt\n" + " %var = OpVariable %ptrt CrossWorkgroup\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint Kernel %func \"compute\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Private %intt\n" + " %var = OpVariable %ptrt Private\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + " OpEntryPoint Kernel %func \"compute\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer PushConstant %intt\n" + " %var = OpVariable %ptrt PushConstant\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer AtomicCounter %intt\n" + " %var = OpVariable %ptrt AtomicCounter\n" + std::string(kVoidFVoid), + std::vector{"AtomicStorage"}), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + + " %intt = OpTypeInt 32 0\n" + " %ptrt = OpTypePointer Image %intt\n" + " %var = OpVariable %ptrt Image\n" + std::string(kVoidFVoid), + AllCapabilities()) +))); + +INSTANTIATE_TEST_SUITE_P(Dim, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(" OpCapability ImageBasic" + + std::string(kOpenCLMemoryModel) + + std::string(" OpEntryPoint Kernel %func \"compute\"") + + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt 1D 0 0 0 0 Unknown" + std::string(kVoidFVoid2), + Sampled1DDependencies()), +std::make_pair(" OpCapability ImageBasic" + + std::string(kOpenCLMemoryModel) + + std::string(" OpEntryPoint Kernel %func \"compute\"") + + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt 2D 0 0 0 0 Unknown" + std::string(kVoidFVoid2), + AllCapabilities()), +std::make_pair(" OpCapability ImageBasic" + + std::string(kOpenCLMemoryModel) + + std::string(" OpEntryPoint Kernel %func \"compute\"") + + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt 3D 0 0 0 0 Unknown" + std::string(kVoidFVoid2), + AllCapabilities()), +std::make_pair(" OpCapability ImageBasic" + + std::string(kOpenCLMemoryModel) + + std::string(" OpEntryPoint Kernel %func \"compute\"") + + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt Cube 0 0 0 0 Unknown" + std::string(kVoidFVoid2), + ShaderDependencies()), +std::make_pair(" OpCapability ImageBasic" + + std::string(kOpenCLMemoryModel) + + std::string(" OpEntryPoint Kernel %func \"compute\"") + + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt Rect 0 0 0 0 Unknown" + std::string(kVoidFVoid2), + SampledRectDependencies()), +std::make_pair(" OpCapability ImageBasic" + + std::string(kOpenCLMemoryModel) + + std::string(" OpEntryPoint Kernel %func \"compute\"") + + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt Buffer 0 0 0 0 Unknown" + std::string(kVoidFVoid2), + SampledBufferDependencies()), +std::make_pair(" OpCapability ImageBasic" + + std::string(kOpenCLMemoryModel) + + std::string(" OpEntryPoint Kernel %func \"compute\"") + + " %voidt = OpTypeVoid" + " %imgt = OpTypeImage %voidt SubpassData 0 0 0 2 Unknown" + std::string(kVoidFVoid2), + std::vector{"InputAttachment"}) +))); + +// NOTE: All Sampler Address Modes require kernel capabilities but the +// OpConstantSampler requires LiteralSampler which depends on Kernel +INSTANTIATE_TEST_SUITE_P(SamplerAddressingMode, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + " %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert None 1 Nearest" + + std::string(kVoidFVoid), + std::vector{"LiteralSampler"}), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + " %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert ClampToEdge 1 Nearest" + + std::string(kVoidFVoid), + std::vector{"LiteralSampler"}), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + " %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert Clamp 1 Nearest" + + std::string(kVoidFVoid), + std::vector{"LiteralSampler"}), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + " %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert Repeat 1 Nearest" + + std::string(kVoidFVoid), + std::vector{"LiteralSampler"}), +std::make_pair(std::string(kGLSL450MemoryModel) + + " OpEntryPoint Vertex %func \"shader\"" + " %samplert = OpTypeSampler" + " %sampler = OpConstantSampler %samplert RepeatMirrored 1 Nearest" + + std::string(kVoidFVoid), + std::vector{"LiteralSampler"}) +))); + +// TODO(umar): Sampler Filter Mode +// TODO(umar): Image Format +// TODO(umar): Image Channel Order +// TODO(umar): Image Channel Data Type +// TODO(umar): Image Operands +// TODO(umar): FP Fast Math Mode +// TODO(umar): FP Rounding Mode +// TODO(umar): Linkage Type +// TODO(umar): Access Qualifier +// TODO(umar): Function Parameter Attribute + +INSTANTIATE_TEST_SUITE_P(Decoration, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt RelaxedPrecision\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + // Block applies to struct type. + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %block Block\n" + "%intt = OpTypeInt 32 0\n" + "%block = OpTypeStruct %intt\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + // BufferBlock applies to struct type. + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %block BufferBlock\n" + "%intt = OpTypeInt 32 0\n" + "%block = OpTypeStruct %intt\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt RowMajor\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + MatrixDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt ColMajor\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + MatrixDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt ArrayStride 1\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt MatrixStride 1\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + MatrixDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt GLSLShared\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt GLSLPacked\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpDecorate %intt CPacked\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt NoPerspective\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Flat\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Patch\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Centroid\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Sample\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + std::vector{"SampleRateShading"}), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Invariant\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Restrict\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Aliased\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Volatile\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpDecorate %intt Constant\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Coherent\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + // NonWritable must target something valid, such as a storage image. + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %var NonWritable " + "%float = OpTypeFloat 32 " + "%imstor = OpTypeImage %float 2D 0 0 0 2 Unknown " + "%ptr = OpTypePointer UniformConstant %imstor " + "%var = OpVariable %ptr UniformConstant " + + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt NonReadable\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + // Uniform must target a non-void value. + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %int0 Uniform\n" + "%intt = OpTypeInt 32 0\n" + + "%int0 = OpConstantNull %intt" + + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpDecorate %intt SaturatedConversion\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Stream 0\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + std::vector{"GeometryStreams"}), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpMemberDecorate %struct 0 Location 0\n" + "%intt = OpTypeInt 32 0\n" + "%struct = OpTypeStruct %intt\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %var Component 0\n" + "%intt = OpTypeInt 32 0\n" + "%ptr = OpTypePointer Input %intt\n" + "%var = OpVariable %ptr Input\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Index 0\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Binding 0\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt DescriptorSet 0\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt Offset 0\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt XfbBuffer 0\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + std::vector{"TransformFeedback"}), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt XfbStride 0\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + std::vector{"TransformFeedback"}), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpDecorate %intt FuncParamAttr Zext\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpDecorate %intt FPFastMathMode Fast\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt LinkageAttributes \"other\" Import\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + std::vector{"Linkage"}), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt NoContraction\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %intt InputAttachmentIndex 0\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + std::vector{"InputAttachment"}), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpDecorate %intt Alignment 4\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + KernelDependencies()) +))); + +// clang-format on +INSTANTIATE_TEST_SUITE_P( + DecorationSpecId, ValidateCapability, + Combine( + ValuesIn(AllSpirV10Capabilities()), + Values(std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %1 SpecId 1\n" + "%intt = OpTypeInt 32 0\n" + "%1 = OpSpecConstant %intt 0\n" + + std::string(kVoidFVoid), + ShaderDependencies())))); + +INSTANTIATE_TEST_SUITE_P( + DecorationV11, ValidateCapabilityV11, + Combine(ValuesIn(AllCapabilities()), + Values(std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %p MaxByteOffset 0 " + "%i32 = OpTypeInt 32 0 " + "%pi32 = OpTypePointer Workgroup %i32 " + "%p = OpVariable %pi32 Workgroup " + + std::string(kVoidFVoid), + AddressesDependencies()), + // Trying to test OpDecorate here, but if this fails due to + // incorrect OpMemoryModel validation, that must also be + // fixed. + std::make_pair( + std::string("OpMemoryModel Logical OpenCL " + "OpEntryPoint Kernel %func \"compute\" \n" + "OpDecorate %1 SpecId 1 " + "%intt = OpTypeInt 32 0 " + "%1 = OpSpecConstant %intt 0") + + std::string(kVoidFVoid), + KernelDependencies()), + std::make_pair( + std::string("OpMemoryModel Logical Simple " + "OpEntryPoint Vertex %func \"shader\" \n" + "OpDecorate %1 SpecId 1 " + "%intt = OpTypeInt 32 0 " + "%1 = OpSpecConstant %intt 0") + + std::string(kVoidFVoid), + ShaderDependencies())))); +// clang-format off + +INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn Position\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +// Just mentioning PointSize, ClipDistance, or CullDistance as a BuiltIn does +// not trigger the requirement for the associated capability. +// See https://github.com/KhronosGroup/SPIRV-Tools/issues/365 +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn PointSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn ClipDistance\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn CullDistance\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn VertexId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn InstanceId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn PrimitiveId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + GeometryTessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn InvocationId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + GeometryTessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn Layer\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + GeometryDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn ViewportIndex\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + std::vector{"MultiViewport"}), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn TessLevelOuter\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn TessLevelInner\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn TessCoord\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn PatchVertices\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + TessellationDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn FragCoord\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn PointCoord\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn FrontFacing\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn SampleId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + std::vector{"SampleRateShading"}), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn SamplePosition\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + std::vector{"SampleRateShading"}), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn SampleMask\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn FragDepth\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn HelperInvocation\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn VertexIndex\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn InstanceIndex\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn NumWorkgroups\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn WorkgroupSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn WorkgroupId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn LocalInvocationId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn GlobalInvocationId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn LocalInvocationIndex\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllCapabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn WorkDim\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn GlobalSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn EnqueuedWorkgroupSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn GlobalOffset\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn GlobalLinearId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn SubgroupSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelAndGroupNonUniformDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn SubgroupMaxSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn NumSubgroups\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelAndGroupNonUniformDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn NumEnqueuedSubgroups\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn SubgroupId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelAndGroupNonUniformDependencies()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn SubgroupLocalInvocationId\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + KernelAndGroupNonUniformDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn VertexIndex\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()), +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "OpDecorate %int0 BuiltIn InstanceIndex\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + ShaderDependencies()) +))); + +// Ensure that mere mention of PointSize, ClipDistance, or CullDistance as +// BuiltIns does not trigger the requirement for the associated +// capability. +// See https://github.com/KhronosGroup/SPIRV-Tools/issues/365 +INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityVulkan10, + Combine( + // All capabilities to try. + ValuesIn(AllSpirV10Capabilities()), + Values( +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpMemberDecorate %block 0 BuiltIn PointSize\n" + "%f32 = OpTypeFloat 32\n" + "%block = OpTypeStruct %f32\n" + "%intt = OpTypeInt 32 0\n" + std::string(kVoidFVoid), + // Capabilities which should succeed. + AllVulkan10Capabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpMemberDecorate %block 0 BuiltIn ClipDistance\n" + "%f32 = OpTypeFloat 32\n" + "%intt = OpTypeInt 32 0\n" + "%intt_4 = OpConstant %intt 4\n" + "%f32arr4 = OpTypeArray %f32 %intt_4\n" + "%block = OpTypeStruct %f32arr4\n" + std::string(kVoidFVoid), + AllVulkan10Capabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + "OpMemberDecorate %block 0 BuiltIn CullDistance\n" + "%f32 = OpTypeFloat 32\n" + "%intt = OpTypeInt 32 0\n" + "%intt_4 = OpConstant %intt 4\n" + "%f32arr4 = OpTypeArray %f32 %intt_4\n" + "%block = OpTypeStruct %f32arr4\n" + std::string(kVoidFVoid), + AllVulkan10Capabilities()) +))); + +INSTANTIATE_TEST_SUITE_P(BuiltIn, ValidateCapabilityOpenGL40, + Combine( + // OpenGL 4.0 is based on SPIR-V 1.0 + ValuesIn(AllSpirV10Capabilities()), + Values( +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn PointSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllSpirV10Capabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn ClipDistance\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllSpirV10Capabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn CullDistance\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllSpirV10Capabilities()) +))); + +INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityWebGPU, + Combine( + // All capabilities to try. + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kVulkanMemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + std::string(kVoidFVoid), + AllWebGPUCapabilities()) +))); + +INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan11, + Combine( + // All capabilities to try. + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn PointSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllVulkan11Capabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn CullDistance\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllVulkan11Capabilities()) +))); + +INSTANTIATE_TEST_SUITE_P(Capabilities, ValidateCapabilityVulkan12, + Combine( + // All capabilities to try. + ValuesIn(AllSpirV15Capabilities()), + Values( +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn PointSize\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllVulkan12Capabilities()), +std::make_pair(std::string(kGLSL450MemoryModel) + + "OpEntryPoint Vertex %func \"shader\" \n" + + "OpDecorate %int0 BuiltIn CullDistance\n" + "%intt = OpTypeInt 32 0\n" + "%int0 = OpConstant %intt 0\n" + std::string(kVoidFVoid), + AllVulkan12Capabilities()) +))); + +// TODO(umar): Selection Control +// TODO(umar): Loop Control +// TODO(umar): Function Control +// TODO(umar): Memory Semantics +// TODO(umar): Memory Access +// TODO(umar): Scope +// TODO(umar): Group Operation +// TODO(umar): Kernel Enqueue Flags +// TODO(umar): Kernel Profiling Flags + +INSTANTIATE_TEST_SUITE_P(MatrixOp, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values( +std::make_pair(std::string(kOpenCLMemoryModel) + + "OpEntryPoint Kernel %func \"compute\" \n" + + "%f32 = OpTypeFloat 32\n" + "%vec3 = OpTypeVector %f32 3\n" + "%mat33 = OpTypeMatrix %vec3 3\n" + std::string(kVoidFVoid), + MatrixDependencies())))); +// clang-format on + +#if 0 +// TODO(atgoo@github.com) The following test is not valid as it generates +// invalid combinations of images, instructions and image operands. +// +// Creates assembly containing an OpImageFetch instruction using operands for +// the image-operands part. The assembly defines constants %fzero and %izero +// that can be used for operands where IDs are required. The assembly is valid, +// apart from not declaring any capabilities required by the operands. +string ImageOperandsTemplate(const std::string& operands) { + ostringstream ss; + // clang-format off + ss << R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL + +%i32 = OpTypeInt 32 0 +%f32 = OpTypeFloat 32 +%v4i32 = OpTypeVector %i32 4 +%timg = OpTypeImage %i32 2D 0 0 0 0 Unknown +%pimg = OpTypePointer UniformConstant %timg +%tfun = OpTypeFunction %i32 + +%vimg = OpVariable %pimg UniformConstant +%izero = OpConstant %i32 0 +%fzero = OpConstant %f32 0. + +%main = OpFunction %i32 None %tfun +%lbl = OpLabel +%img = OpLoad %timg %vimg +%r1 = OpImageFetch %v4i32 %img %izero )" << operands << R"( +OpReturnValue %izero +OpFunctionEnd +)"; + // clang-format on + return ss.str(); +} + +INSTANTIATE_TEST_SUITE_P( + TwoImageOperandsMask, ValidateCapability, + Combine( + ValuesIn(AllCapabilities()), + Values(std::make_pair(ImageOperandsTemplate("Bias|Lod %fzero %fzero"), + ShaderDependencies()), + std::make_pair(ImageOperandsTemplate("Lod|Offset %fzero %izero"), + std::vector{"ImageGatherExtended"}), + std::make_pair(ImageOperandsTemplate("Sample|MinLod %izero %fzero"), + std::vector{"MinLod"}), + std::make_pair(ImageOperandsTemplate("Lod|Sample %fzero %izero"), + AllCapabilities()))), ); +#endif + +// TODO(umar): Instruction capability checks + +spv_result_t spvCoreOperandTableNameLookup(spv_target_env env, + const spv_operand_table table, + const spv_operand_type_t type, + const char* name, + const size_t nameLength) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!name) return SPV_ERROR_INVALID_POINTER; + + for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) { + const auto& group = table->types[typeIndex]; + if (type != group.type) continue; + for (uint64_t index = 0; index < group.count; ++index) { + const auto& entry = group.entries[index]; + // Check for min version only. + if (spvVersionForTargetEnv(env) >= entry.minVersion && + nameLength == strlen(entry.name) && + !strncmp(entry.name, name, nameLength)) { + return SPV_SUCCESS; + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +// True if capability exists in core spec of env. +bool Exists(const std::string& capability, spv_target_env env) { + ScopedContext sc(env); + return SPV_SUCCESS == + spvCoreOperandTableNameLookup(env, sc.context->operand_table, + SPV_OPERAND_TYPE_CAPABILITY, + capability.c_str(), capability.size()); +} + +TEST_P(ValidateCapability, Capability) { + const std::string capability = Capability(GetParam()); + spv_target_env env = SPV_ENV_UNIVERSAL_1_0; + if (!capability.empty()) { + if (Exists(capability, SPV_ENV_UNIVERSAL_1_0)) + env = SPV_ENV_UNIVERSAL_1_0; + else if (Exists(capability, SPV_ENV_UNIVERSAL_1_1)) + env = SPV_ENV_UNIVERSAL_1_1; + else if (Exists(capability, SPV_ENV_UNIVERSAL_1_2)) + env = SPV_ENV_UNIVERSAL_1_2; + else + env = SPV_ENV_UNIVERSAL_1_3; + } + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, env); + ASSERT_EQ(ExpectedResult(GetParam()), ValidateInstructions(env)) + << "target env: " << spvTargetEnvDescription(env) << "\ntest code:\n" + << test_code; +} + +TEST_P(ValidateCapabilityV11, Capability) { + const std::string capability = Capability(GetParam()); + if (Exists(capability, SPV_ENV_UNIVERSAL_1_1)) { + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(ExpectedResult(GetParam()), + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)) + << test_code; + } +} + +TEST_P(ValidateCapabilityVulkan10, Capability) { + const std::string capability = Capability(GetParam()); + if (Exists(capability, SPV_ENV_VULKAN_1_0)) { + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, SPV_ENV_VULKAN_1_0); + ASSERT_EQ(ExpectedResult(GetParam()), + ValidateInstructions(SPV_ENV_VULKAN_1_0)) + << test_code; + } +} + +TEST_P(ValidateCapabilityVulkan11, Capability) { + const std::string capability = Capability(GetParam()); + if (Exists(capability, SPV_ENV_VULKAN_1_1)) { + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, SPV_ENV_VULKAN_1_1); + ASSERT_EQ(ExpectedResult(GetParam()), + ValidateInstructions(SPV_ENV_VULKAN_1_1)) + << test_code; + } +} + +TEST_P(ValidateCapabilityVulkan12, Capability) { + const std::string capability = Capability(GetParam()); + if (Exists(capability, SPV_ENV_VULKAN_1_2)) { + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, SPV_ENV_VULKAN_1_2); + ASSERT_EQ(ExpectedResult(GetParam()), + ValidateInstructions(SPV_ENV_VULKAN_1_2)) + << test_code; + } +} + +TEST_P(ValidateCapabilityOpenGL40, Capability) { + const std::string capability = Capability(GetParam()); + if (Exists(capability, SPV_ENV_OPENGL_4_0)) { + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, SPV_ENV_OPENGL_4_0); + ASSERT_EQ(ExpectedResult(GetParam()), + ValidateInstructions(SPV_ENV_OPENGL_4_0)) + << test_code; + } +} + +TEST_P(ValidateCapabilityWebGPU, Capability) { + const std::string capability = Capability(GetParam()); + if (Exists(capability, SPV_ENV_WEBGPU_0)) { + const std::string test_code = MakeAssembly(GetParam()); + CompileSuccessfully(test_code, SPV_ENV_WEBGPU_0); + ASSERT_EQ(ExpectedResult(GetParam()), + ValidateInstructions(SPV_ENV_WEBGPU_0)) + << test_code; + } +} + +TEST_F(ValidateCapability, SemanticsIdIsAnIdNotALiteral) { + // From https://github.com/KhronosGroup/SPIRV-Tools/issues/248 + // The validator was interpreting the memory semantics ID number + // as the value to be checked rather than an ID that references + // another value to be checked. + // In this case a raw ID of 64 was mistaken to mean a literal + // semantic value of UniformMemory, which would require the Shader + // capability. + const char str[] = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL + +; %i32 has ID 1 +%i32 = OpTypeInt 32 0 +%tf = OpTypeFunction %i32 +%pi32 = OpTypePointer CrossWorkgroup %i32 +%var = OpVariable %pi32 CrossWorkgroup +%c = OpConstant %i32 100 +%scope = OpConstant %i32 1 ; Device scope + +; Fake an instruction with 64 as the result id. +; !64 = OpConstantNull %i32 +!0x3002e !1 !64 + +%f = OpFunction %i32 None %tf +%l = OpLabel +%result = OpAtomicIAdd %i32 %var %scope !64 %c +OpReturnValue %result +OpFunctionEnd +)"; + + CompileSuccessfully(str); + + // Since we are forcing usage of 64, the "id bound" in the binary header + // must be overwritten so that 64 is considered within bound. + // ID Bound is at index 3 of the binary. Set it to 65. + OverwriteAssembledBinary(3, 65); + + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCapability, IntSignednessKernelGood) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%i32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCapability, IntSignednessKernelBad) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +%i32 = OpTypeInt 32 1 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Signedness in OpTypeInt must always be 0 when " + "Kernel capability is used.")); +} + +TEST_F(ValidateCapability, IntSignednessShaderGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%u32 = OpTypeInt 32 0 +%i32 = OpTypeInt 32 1 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCapability, NonVulkan10Capability) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%u32 = OpTypeInt 32 0 +%i32 = OpTypeInt 32 1 +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Linkage is not allowed by Vulkan 1.0")); +} + +TEST_F(ValidateCapability, Vulkan10EnabledByExtension) { + const std::string spirv = R"( +OpCapability Shader +OpCapability DrawParameters +OpExtension "SPV_KHR_shader_draw_parameters" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %func "shader" +OpMemberDecorate %block 0 BuiltIn PointSize +%f32 = OpTypeFloat 32 +%block = OpTypeStruct %f32 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateCapability, Vulkan10NotEnabledByExtension) { + const std::string spirv = R"( +OpCapability Shader +OpCapability DrawParameters +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %func "shader" +OpDecorate %intt BuiltIn PointSize +%intt = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability DrawParameters is not allowed by Vulkan 1.0")); +} + +TEST_F(ValidateCapability, NonOpenCL12FullCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Pipes +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv, SPV_ENV_OPENCL_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability Pipes is not allowed by OpenCL 1.2 Full Profile")); +} + +TEST_F(ValidateCapability, OpenCL12FullEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability ImageBasic +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_1_2)); +} + +TEST_F(ValidateCapability, OpenCL12FullNotEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Capability Sampled1D is not allowed by OpenCL 1.2 Full Profile")); +} + +TEST_F(ValidateCapability, NonOpenCL12EmbeddedCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Int64 +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Capability Int64 is not allowed by OpenCL 1.2 Embedded Profile")); +} + +TEST_F(ValidateCapability, OpenCL12EmbeddedEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability ImageBasic +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_1_2)); +} + +TEST_F(ValidateCapability, OpenCL12EmbeddedNotEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Sampled1D is not allowed by OpenCL 1.2 " + "Embedded Profile")); +} + +TEST_F(ValidateCapability, OpenCL12EmbeddedNoLongerEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Pipes +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Pipes is not allowed by OpenCL 1.2 " + "Embedded Profile")); +} + +TEST_F(ValidateCapability, OpenCL20FullCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Groups +OpCapability Pipes +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_0)); +} + +TEST_F(ValidateCapability, NonOpenCL20FullCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Matrix +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_2_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Capability Matrix is not allowed by OpenCL 2.0/2.1 Full Profile")); +} + +TEST_F(ValidateCapability, OpenCL20FullEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability ImageBasic +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_0)); +} + +TEST_F(ValidateCapability, OpenCL20FullNotEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_2_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Sampled1D is not allowed by OpenCL 2.0/2.1 " + "Full Profile")); +} + +TEST_F(ValidateCapability, NonOpenCL20EmbeddedCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Int64 +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Int64 is not allowed by OpenCL 2.0/2.1 " + "Embedded Profile")); +} + +TEST_F(ValidateCapability, OpenCL20EmbeddedEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability ImageBasic +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_0)); +} + +TEST_F(ValidateCapability, OpenCL20EmbeddedNotEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Sampled1D is not allowed by OpenCL 2.0/2.1 " + "Embedded Profile")); +} + +TEST_F(ValidateCapability, OpenCL22FullCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability PipeStorage +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_2)); +} + +TEST_F(ValidateCapability, NonOpenCL22FullCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Matrix +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_2_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability Matrix is not allowed by OpenCL 2.2 Full Profile")); +} + +TEST_F(ValidateCapability, OpenCL22FullEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability ImageBasic +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_2_2)); +} + +TEST_F(ValidateCapability, OpenCL22FullNotEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_2_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_2_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Capability Sampled1D is not allowed by OpenCL 2.2 Full Profile")); +} + +TEST_F(ValidateCapability, NonOpenCL22EmbeddedCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Int64 +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)"; + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Capability Int64 is not allowed by OpenCL 2.2 Embedded Profile")); +} + +TEST_F(ValidateCapability, OpenCL22EmbeddedEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability ImageBasic +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_2)); +} + +TEST_F(ValidateCapability, OpenCL22EmbeddedNotEnabledByCapability) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability Linkage +OpCapability Sampled1D +OpMemoryModel Physical64 OpenCL +%u32 = OpTypeInt 32 0 +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_OPENCL_EMBEDDED_2_2); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_OPENCL_EMBEDDED_2_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Sampled1D is not allowed by OpenCL 2.2 " + "Embedded Profile")); +} + +// Three tests to check enablement of an enum (a decoration) which is not +// in core, and is directly enabled by a capability, but not directly enabled +// by an extension. See https://github.com/KhronosGroup/SPIRV-Tools/issues/1596 + +TEST_F(ValidateCapability, DecorationFromExtensionMissingEnabledByCapability) { + // Decoration ViewportRelativeNV is enabled by ShaderViewportMaskNV, which in + // turn is enabled by SPV_NV_viewport_array2. + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical Simple +OpDecorate %void ViewportRelativeNV +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 2 of Decorate requires one of these " + "capabilities: ShaderViewportMaskNV")); +} + +TEST_F(ValidateCapability, CapabilityEnabledByMissingExtension) { + // Capability ShaderViewportMaskNV is enabled by SPV_NV_viewport_array2. + const std::string spirv = R"( +OpCapability Shader +OpCapability ShaderViewportMaskNV +OpMemoryModel Logical Simple +)" + std::string(kVoidFVoid); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("operand ShaderViewportMaskNV(5255) requires one of " + "these extensions: SPV_NV_viewport_array2")); +} + +TEST_F(ValidateCapability, + DecorationEnabledByCapabilityEnabledByPresentExtension) { + // Decoration ViewportRelativeNV is enabled by ShaderViewportMaskNV, which in + // turn is enabled by SPV_NV_viewport_array2. + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability ShaderViewportMaskNV +OpExtension "SPV_NV_viewport_array2" +OpMemoryModel Logical Simple +OpDecorate %void ViewportRelativeNV +%void = OpTypeVoid +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)) + << getDiagnosticString(); +} + +// Three tests to check enablement of an instruction which is not in core, and +// is directly enabled by a capability, but not directly enabled by an +// extension. See https://github.com/KhronosGroup/SPIRV-Tools/issues/1624 +// Instruction OpSubgroupShuffleINTEL is enabled by SubgroupShuffleINTEL, which +// in turn is enabled by SPV_INTEL_subgroups. + +TEST_F(ValidateCapability, InstructionFromExtensionMissingEnabledByCapability) { + // Decoration ViewportRelativeNV is enabled by ShaderViewportMaskNV, which in + // turn is enabled by SPV_NV_viewport_array2. + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +; OpCapability SubgroupShuffleINTEL +OpExtension "SPV_INTEL_subgroups" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%voidfn = OpTypeFunction %void +%zero = OpConstant %uint 0 +%main = OpFunction %void None %voidfn +%entry = OpLabel +%foo = OpSubgroupShuffleINTEL %uint %zero %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode SubgroupShuffleINTEL requires one of these " + "capabilities: SubgroupShuffleINTEL")); +} + +TEST_F(ValidateCapability, + InstructionEnablingCapabilityEnabledByMissingExtension) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability SubgroupShuffleINTEL +; OpExtension "SPV_INTEL_subgroups" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%voidfn = OpTypeFunction %void +%zero = OpConstant %uint 0 +%main = OpFunction %void None %voidfn +%entry = OpLabel +%foo = OpSubgroupShuffleINTEL %uint %zero %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("operand SubgroupShuffleINTEL(5568) requires one of " + "these extensions: SPV_INTEL_subgroups")); +} + +TEST_F(ValidateCapability, + InstructionEnabledByCapabilityEnabledByPresentExtension) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Addresses +OpCapability SubgroupShuffleINTEL +OpExtension "SPV_INTEL_subgroups" +OpMemoryModel Physical32 OpenCL +OpEntryPoint Kernel %main "main" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%voidfn = OpTypeFunction %void +%zero = OpConstant %uint 0 +%main = OpFunction %void None %voidfn +%entry = OpLabel +%foo = OpSubgroupShuffleINTEL %uint %zero %zero +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)) + << getDiagnosticString(); +} + +TEST_F(ValidateCapability, VulkanMemoryModelWithVulkanKHR) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)) + << getDiagnosticString(); +} + +TEST_F(ValidateCapability, VulkanMemoryModelWithGLSL450) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical GLSL450 +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("VulkanMemoryModelKHR capability must only be " + "specified if the VulkanKHR memory model is used")); +} + +// In the grammar, SubgroupEqMask and SubgroupMaskKHR have different enabling +// lists of extensions. +TEST_F(ValidateCapability, SubgroupEqMaskEnabledByExtension) { + const std::string spirv = R"( +OpCapability Shader +OpCapability SubgroupBallotKHR +OpExtension "SPV_KHR_shader_ballot" +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %main "main" +OpDecorate %var BuiltIn SubgroupEqMask +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%ptr_uint = OpTypePointer Private %uint +%var = OpVariable %ptr_uint Private +%fn = OpTypeFunction %void +%main = OpFunction %void None %fn +%entry = OpLabel +%val = OpLoad %uint %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)) + << getDiagnosticString(); +} + +// Test that extensions incorporated into SPIR-V 1.5 no longer require +// the associated OpExtension instruction. Test one capability per extension. + +struct CapabilityExtensionVersionCase { + std::string capability; + std::string capability_new_name; + std::string extension; + spv_target_env last_version_requiring_extension; + spv_target_env first_version_in_core; +}; + +using ValidateCapabilityExtensionVersionTest = + spvtest::ValidateBase; + +// Returns a minimal shader module with the given capability instruction. +std::string MinimalShaderModuleWithCapability(std::string cap) { + std::string mem_model = + (cap.find("VulkanMemory") == 0) ? "VulkanKHR" : "GLSL450"; + std::string extra_cap = (cap.find("VulkanMemoryModelDeviceScope") == 0) + ? "\nOpCapability VulkanMemoryModelKHR\n" + : ""; + return std::string("OpCapability ") + cap + extra_cap + R"( +OpCapability Shader +OpMemoryModel Logical )" + mem_model + R"( +OpEntryPoint Vertex %main "main" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; +} + +TEST_P(ValidateCapabilityExtensionVersionTest, FailsInOlderSpirvVersion) { + const auto spirv = MinimalShaderModuleWithCapability(GetParam().capability); + CompileSuccessfully(spirv, GetParam().last_version_requiring_extension); + EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, + ValidateInstructions(GetParam().last_version_requiring_extension)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(std::string("1st operand of Capability: operand ") + + GetParam().capability_new_name)) + << spirv << "\n"; + EXPECT_THAT(getDiagnosticString(), + HasSubstr(std::string("requires one of these extensions: ") + + GetParam().extension)); +} + +TEST_P(ValidateCapabilityExtensionVersionTest, + SucceedsInNewerSpirvVersionWithOldName) { + const auto spirv = MinimalShaderModuleWithCapability(GetParam().capability); + CompileSuccessfully(spirv, GetParam().first_version_in_core); + EXPECT_EQ(SPV_SUCCESS, + ValidateInstructions(GetParam().first_version_in_core)); + EXPECT_THAT(getDiagnosticString(), Eq("")) << spirv << "\n"; +} + +TEST_P(ValidateCapabilityExtensionVersionTest, + SucceedsInNewerSpirvVersionWithNewName) { + const auto spirv = + MinimalShaderModuleWithCapability(GetParam().capability_new_name); + CompileSuccessfully(spirv, GetParam().first_version_in_core); + EXPECT_EQ(SPV_SUCCESS, + ValidateInstructions(GetParam().first_version_in_core)); + EXPECT_THAT(getDiagnosticString(), Eq("")) << spirv << "\n"; +} + +std::vector CapVersionCases1_5() { +#define IN15NOSUFFIX(C, E) \ + { C, C, E, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5 } +#define IN15(C, C_WITHOUT_SUFFIX, E) \ + { C, C_WITHOUT_SUFFIX, E, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5 } + return std::vector{ + // SPV_KHR_8bit_storage + IN15NOSUFFIX("StorageBuffer8BitAccess", "SPV_KHR_8bit_storage"), + IN15NOSUFFIX("UniformAndStorageBuffer8BitAccess", "SPV_KHR_8bit_storage"), + IN15NOSUFFIX("StoragePushConstant8", "SPV_KHR_8bit_storage"), + // SPV_EXT_descriptor_indexing + IN15("ShaderNonUniformEXT", "ShaderNonUniform", + "SPV_EXT_descriptor_indexing"), + IN15("RuntimeDescriptorArrayEXT", "RuntimeDescriptorArray", + "SPV_EXT_descriptor_indexing"), + IN15("InputAttachmentArrayDynamicIndexingEXT", + "InputAttachmentArrayDynamicIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("UniformTexelBufferArrayDynamicIndexingEXT", + "UniformTexelBufferArrayDynamicIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("StorageTexelBufferArrayDynamicIndexingEXT", + "StorageTexelBufferArrayDynamicIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("UniformBufferArrayNonUniformIndexingEXT", + "UniformBufferArrayNonUniformIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("SampledImageArrayNonUniformIndexingEXT", + "SampledImageArrayNonUniformIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("StorageBufferArrayNonUniformIndexingEXT", + "StorageBufferArrayNonUniformIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("StorageImageArrayNonUniformIndexingEXT", + "StorageImageArrayNonUniformIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("InputAttachmentArrayNonUniformIndexingEXT", + "InputAttachmentArrayNonUniformIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("UniformTexelBufferArrayNonUniformIndexingEXT", + "UniformTexelBufferArrayNonUniformIndexing", + "SPV_EXT_descriptor_indexing"), + IN15("StorageTexelBufferArrayNonUniformIndexingEXT", + "StorageTexelBufferArrayNonUniformIndexing", + "SPV_EXT_descriptor_indexing"), + // SPV_EXT_physical_storage_buffer + IN15("PhysicalStorageBufferAddressesEXT", + "PhysicalStorageBufferAddresses", "SPV_EXT_physical_storage_buffer"), + // SPV_KHR_vulkan_memory_model + IN15("VulkanMemoryModelKHR", "VulkanMemoryModel", + "SPV_KHR_vulkan_memory_model"), + IN15("VulkanMemoryModelDeviceScopeKHR", "VulkanMemoryModelDeviceScope", + "SPV_KHR_vulkan_memory_model"), + }; +#undef IN15 +} + +INSTANTIATE_TEST_SUITE_P(NewInSpirv1_5, ValidateCapabilityExtensionVersionTest, + ValuesIn(CapVersionCases1_5())); + +TEST_P(ValidateCapability, + CapShaderViewportIndexLayerFailsInOlderSpirvVersion) { + const auto spirv = + MinimalShaderModuleWithCapability("ShaderViewportIndexLayerEXT"); + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "1st operand of Capability: operand ShaderViewportIndexLayerEXT")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions: " + "SPV_EXT_shader_viewport_index_layer")); +} + +TEST_P(ValidateCapability, CapShaderViewportIndexLayerFailsInNewSpirvVersion) { + const auto spirv = + MinimalShaderModuleWithCapability("ShaderViewportIndexLayerEXT"); + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_MISSING_EXTENSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "1st operand of Capability: operand ShaderViewportIndexLayerEXT")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions: " + "SPV_EXT_shader_viewport_index_layer")); +} + +TEST_F(ValidateCapability, CapShaderViewportIndexSucceedsInNewSpirvVersion) { + const auto spirv = MinimalShaderModuleWithCapability("ShaderViewportIndex"); + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateCapability, CapShaderLayerSucceedsInNewSpirvVersion) { + const auto spirv = MinimalShaderModuleWithCapability("ShaderLayer"); + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_cfg_test.cpp b/third_party/spirv-tools/test/val/val_cfg_test.cpp new file mode 100644 index 0000000..b4d1c28 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_cfg_test.cpp @@ -0,0 +1,4603 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for Control Flow Graph + +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/diagnostic.h" +#include "source/spirv_target_env.h" +#include "source/val/validate.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; + +using ValidateCFG = spvtest::ValidateBase; +using spvtest::ScopedContext; + +std::string nameOps() { return ""; } + +template +std::string nameOps(std::pair head, Args... names) { + return "OpName %" + head.first + " \"" + head.second + "\"\n" + + nameOps(names...); +} + +template +std::string nameOps(std::string head, Args... names) { + return "OpName %" + head + " \"" + head + "\"\n" + nameOps(names...); +} + +/// This class allows the easy creation of complex control flow without writing +/// SPIR-V. This class is used in the test cases below. +class Block { + std::string label_; + std::string body_; + SpvOp type_; + std::vector successors_; + + public: + /// Creates a Block with a given label + /// + /// @param[in]: label the label id of the block + /// @param[in]: type the branch instruciton that ends the block + explicit Block(std::string label, SpvOp type = SpvOpBranch) + : label_(label), body_(), type_(type), successors_() {} + + /// Sets the instructions which will appear in the body of the block + Block& SetBody(std::string body) { + body_ = body; + return *this; + } + + Block& AppendBody(std::string body) { + body_ += body; + return *this; + } + + /// Converts the block into a SPIR-V string + operator std::string() { + std::stringstream out; + out << std::setw(8) << "%" + label_ + " = OpLabel \n"; + if (!body_.empty()) { + out << body_; + } + + switch (type_) { + case SpvOpBranchConditional: + out << "OpBranchConditional %cond "; + for (Block& b : successors_) { + out << "%" + b.label_ + " "; + } + break; + case SpvOpSwitch: { + out << "OpSwitch %one %" + successors_.front().label_; + std::stringstream ss; + for (size_t i = 1; i < successors_.size(); i++) { + ss << " " << i << " %" << successors_[i].label_; + } + out << ss.str(); + } break; + case SpvOpLoopMerge: { + assert(successors_.size() == 2); + out << "OpLoopMerge %" + successors_[0].label_ + " %" + + successors_[0].label_ + "None"; + } break; + + case SpvOpReturn: + assert(successors_.size() == 0); + out << "OpReturn\n"; + break; + case SpvOpUnreachable: + assert(successors_.size() == 0); + out << "OpUnreachable\n"; + break; + case SpvOpBranch: + assert(successors_.size() == 1); + out << "OpBranch %" + successors_.front().label_; + break; + case SpvOpKill: + assert(successors_.size() == 0); + out << "OpKill\n"; + break; + default: + assert(1 == 0 && "Unhandled"); + } + out << "\n"; + + return out.str(); + } + friend Block& operator>>(Block& curr, std::vector successors); + friend Block& operator>>(Block& lhs, Block& successor); +}; + +/// Assigns the successors for the Block on the lhs +Block& operator>>(Block& lhs, std::vector successors) { + if (lhs.type_ == SpvOpBranchConditional) { + assert(successors.size() == 2); + } else if (lhs.type_ == SpvOpSwitch) { + assert(successors.size() > 1); + } + lhs.successors_ = successors; + return lhs; +} + +/// Assigns the successor for the Block on the lhs +Block& operator>>(Block& lhs, Block& successor) { + assert(lhs.type_ == SpvOpBranch); + lhs.successors_.push_back(successor); + return lhs; +} + +const std::string& GetDefaultHeader(SpvCapability cap) { + static const std::string shader_header = + "OpCapability Shader\n" + "OpCapability Linkage\n" + "OpMemoryModel Logical GLSL450\n"; + + static const std::string kernel_header = + "OpCapability Kernel\n" + "OpCapability Linkage\n" + "OpMemoryModel Logical OpenCL\n"; + + return (cap == SpvCapabilityShader) ? shader_header : kernel_header; +} + +const std::string& GetWebGPUHeader() { + static const std::string header = + "OpCapability Shader\n" + "OpCapability VulkanMemoryModelKHR\n" + "OpExtension \"SPV_KHR_vulkan_memory_model\"\n" + "OpMemoryModel Logical VulkanKHR\n"; + return header; +} + +const std::string& types_consts() { + static const std::string types = + "%voidt = OpTypeVoid\n" + "%boolt = OpTypeBool\n" + "%intt = OpTypeInt 32 0\n" + "%one = OpConstant %intt 1\n" + "%two = OpConstant %intt 2\n" + "%ptrt = OpTypePointer Function %intt\n" + "%funct = OpTypeFunction %voidt\n"; + return types; +} + +INSTANTIATE_TEST_SUITE_P(StructuredControlFlow, ValidateCFG, + ::testing::Values(SpvCapabilityShader, + SpvCapabilityKernel)); + +TEST_P(ValidateCFG, LoopReachableFromEntryButNeverLeadingToReturn) { + // In this case, the loop is reachable from a node without a predecessor, + // but never reaches a node with a return. + // + // This motivates the need for the pseudo-exit node to have a node + // from a cycle in its predecessors list. Otherwise the validator's + // post-dominance calculation will go into an infinite loop. + // + // For more motivation, see + // https://github.com/KhronosGroup/SPIRV-Tools/issues/279 + std::string str = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + + OpName %entry "entry" + OpName %loop "loop" + OpName %exit "exit" + +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt + +%main = OpFunction %voidt None %funct +%entry = OpLabel + OpBranch %loop +%loop = OpLabel + OpLoopMerge %exit %loop None + OpBranch %loop +%exit = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str; +} + +TEST_P(ValidateCFG, LoopUnreachableFromEntryButLeadingToReturn) { + // In this case, the loop is not reachable from a node without a + // predecessor, but eventually reaches a node with a return. + // + // This motivates the need for the pseudo-entry node to have a node + // from a cycle in its successors list. Otherwise the validator's + // dominance calculation will go into an infinite loop. + // + // For more motivation, see + // https://github.com/KhronosGroup/SPIRV-Tools/issues/279 + // Before that fix, we'd have an infinite loop when calculating + // post-dominators. + std::string str = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + + OpName %entry "entry" + OpName %loop "loop" + OpName %cont "cont" + OpName %exit "exit" + +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%boolt = OpTypeBool +%false = OpConstantFalse %boolt + +%main = OpFunction %voidt None %funct +%entry = OpLabel + OpReturn + +%loop = OpLabel + OpLoopMerge %exit %cont None + OpBranch %cont + +%cont = OpLabel + OpBranchConditional %false %loop %exit + +%exit = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) + << str << getDiagnosticString(); +} + +TEST_P(ValidateCFG, Simple) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block cont("cont"); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) { + loop.SetBody("OpLoopMerge %merge %cont None\n"); + } + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("loop", "entry", "cont", "merge", + std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> std::vector({cont, merge}); + str += cont >> loop; + str += merge; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateCFG, Variable) { + Block entry("entry"); + Block cont("cont"); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%var = OpVariable %ptrt Function\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps(std::make_pair("func", "Main")) + types_consts() + + " %func = OpFunction %voidt None %funct\n"; + str += entry >> cont; + str += cont >> exit; + str += exit; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateCFG, VariableNotInFirstBlockBad) { + Block entry("entry"); + Block cont("cont"); + Block exit("exit", SpvOpReturn); + + // This operation should only be performed in the entry block + cont.SetBody("%var = OpVariable %ptrt Function\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps(std::make_pair("func", "Main")) + types_consts() + + " %func = OpFunction %voidt None %funct\n"; + + str += entry >> cont; + str += cont >> exit; + str += exit; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("All OpVariable instructions in a function must be the " + "first instructions in the first block")); +} + +TEST_P(ValidateCFG, BlockSelfLoopIsOk) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("loop", "merge", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + // loop branches to itself, but does not trigger an error. + str += loop >> std::vector({merge, loop}); + str += merge; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_P(ValidateCFG, BlockAppearsBeforeDominatorBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block cont("cont"); + Block branch("branch", SpvOpBranchConditional); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) branch.SetBody("OpSelectionMerge %merge None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("cont", "branch", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> branch; + str += cont >> merge; // cont appears before its dominator + str += branch >> std::vector({cont, merge}); + str += merge; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("Block .\\[%cont\\] appears in the binary " + "before its dominator .\\[%branch\\]\n" + " %branch = OpLabel\n")); +} + +TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop"); + Block selection("selection", SpvOpBranchConditional); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n"); + + // cannot share the same merge + if (is_shader) selection.SetBody("OpSelectionMerge %merge None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("merge", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> selection; + str += selection >> std::vector({loop, merge}); + str += merge; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("Block .\\[%merge\\] is already a merge block " + "for another header\n" + " %Main = OpFunction %void None %9\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, MergeBlockTargetedByMultipleHeaderBlocksSelectionBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block selection("selection", SpvOpBranchConditional); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) selection.SetBody(" OpSelectionMerge %merge None\n"); + + // cannot share the same merge + if (is_shader) loop.SetBody(" OpLoopMerge %merge %loop None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("merge", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> selection; + str += selection >> std::vector({merge, loop}); + str += loop >> std::vector({loop, merge}); + str += merge; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("Block .\\[%merge\\] is already a merge block " + "for another header\n" + " %Main = OpFunction %void None %9\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceEntryBlock) { + Block entry("entry"); + Block bad("bad"); + Block end("end", SpvOpReturn); + std::string str = GetDefaultHeader(GetParam()) + + nameOps("entry", "bad", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> bad; + str += bad >> entry; // Cannot target entry block + str += end; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("First block .\\[%entry\\] of function " + ".\\[%Main\\] is targeted by block .\\[%bad\\]\n" + " %Main = OpFunction %void None %10\n")); +} + +TEST_P(ValidateCFG, BranchTargetFirstBlockBadSinceValue) { + Block entry("entry"); + entry.SetBody("%undef = OpUndef %voidt\n"); + Block bad("bad"); + Block end("end", SpvOpReturn); + Block badvalue("undef"); // This referenes the OpUndef. + std::string str = GetDefaultHeader(GetParam()) + + nameOps("entry", "bad", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> bad; + str += + bad >> badvalue; // Check branch to a function value (it's not a block!) + str += end; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("'Target Label' operands for OpBranch must " + "be the ID of an OpLabel instruction")); +} + +TEST_P(ValidateCFG, BranchConditionalTrueTargetFirstBlockBad) { + Block entry("entry"); + Block bad("bad", SpvOpBranchConditional); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + bad.SetBody(" OpLoopMerge %entry %exit None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("entry", "bad", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> bad; + str += bad >> std::vector({entry, exit}); // cannot target entry block + str += exit; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] " + "is targeted by block .\\[%bad\\]\n" + " %Main = OpFunction %void None %10\n")); +} + +TEST_P(ValidateCFG, BranchConditionalFalseTargetFirstBlockBad) { + Block entry("entry"); + Block bad("bad", SpvOpBranchConditional); + Block t("t"); + Block merge("merge"); + Block end("end", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + bad.SetBody("OpLoopMerge %merge %cont None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("entry", "bad", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> bad; + str += bad >> std::vector({t, entry}); + str += merge >> end; + str += end; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] " + "is targeted by block .\\[%bad\\]\n" + " %Main = OpFunction %void None %10\n")); +} + +TEST_P(ValidateCFG, SwitchTargetFirstBlockBad) { + Block entry("entry"); + Block bad("bad", SpvOpSwitch); + Block block1("block1"); + Block block2("block2"); + Block block3("block3"); + Block def("def"); // default block + Block merge("merge"); + Block end("end", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + bad.SetBody("OpSelectionMerge %merge None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("entry", "bad", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> bad; + str += bad >> std::vector({def, block1, block2, block3, entry}); + str += def >> merge; + str += block1 >> merge; + str += block2 >> merge; + str += block3 >> merge; + str += merge >> end; + str += end; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("First block .\\[%entry\\] of function .\\[%Main\\] " + "is targeted by block .\\[%bad\\]\n" + " %Main = OpFunction %void None %10\n")); +} + +TEST_P(ValidateCFG, BranchToBlockInOtherFunctionBad) { + Block entry("entry"); + Block middle("middle", SpvOpBranchConditional); + Block end("end", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + middle.SetBody("OpSelectionMerge %end None\n"); + + Block entry2("entry2"); + Block middle2("middle2"); + Block end2("end2", SpvOpReturn); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("middle2", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> middle; + str += middle >> std::vector({end, middle2}); + str += end; + str += "OpFunctionEnd\n"; + + str += "%func2 = OpFunction %voidt None %funct\n"; + str += entry2 >> middle2; + str += middle2 >> end2; + str += end2; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("Block\\(s\\) \\{.\\[%middle2\\]\\} are referenced but not " + "defined in function .\\[%Main\\]\n" + " %Main = OpFunction %void None %9\n")); +} + +TEST_P(ValidateCFG, HeaderDoesntDominatesMergeBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block head("head", SpvOpBranchConditional); + Block f("f"); + Block merge("merge", SpvOpReturn); + + head.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + + if (is_shader) head.AppendBody("OpSelectionMerge %merge None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("head", "merge", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> merge; + str += head >> std::vector({merge, f}); + str += f >> merge; + str += merge; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("The selection construct with the selection header " + ".\\[%head\\] does not dominate the merge block " + ".\\[%merge\\]\n %merge = OpLabel\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, HeaderDoesntStrictlyDominateMergeBad) { + // If a merge block is reachable, then it must be strictly dominated by + // its header block. + bool is_shader = GetParam() == SpvCapabilityShader; + Block head("head", SpvOpBranchConditional); + Block exit("exit", SpvOpReturn); + + head.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + + if (is_shader) head.AppendBody("OpSelectionMerge %head None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("head", "exit", std::make_pair("func", "Main")) + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += head >> std::vector({exit, exit}); + str += exit; + str += "OpFunctionEnd\n"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("The selection construct with the selection header " + ".\\[%head\\] does not strictly dominate the merge block " + ".\\[%head\\]\n %head = OpLabel\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << str; + } +} + +std::string GetUnreachableMergeNoMergeInst(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + Block entry("entry"); + Block branch("branch", SpvOpBranchConditional); + Block t("t", SpvOpReturn); + Block f("f", SpvOpReturn); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (!spvIsWebGPUEnv(env) && cap == SpvCapabilityShader) + branch.AppendBody("OpSelectionMerge %merge None\n"); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", std::make_pair("func", "Main")); + str += types_consts() + "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({t, f}); + str += t; + str += f; + str += merge; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableMergeNoMergeInst) { + CompileSuccessfully( + GetUnreachableMergeNoMergeInst(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableMergeNoMergeInst) { + CompileSuccessfully( + GetUnreachableMergeNoMergeInst(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, all blocks must be reachable")); +} + +std::string GetUnreachableMergeTerminatedBy(SpvCapability cap, + spv_target_env env, SpvOp op) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block branch("branch", SpvOpBranchConditional); + Block t("t", SpvOpReturn); + Block f("f", SpvOpReturn); + Block merge("merge", op); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) + branch.AppendBody("OpSelectionMerge %merge None\n"); + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({t, f}); + str += t; + str += f; + str += merge; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpUnreachable) { + CompileSuccessfully(GetUnreachableMergeTerminatedBy( + GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpUnreachable)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, UnreachableMergeTerminatedByOpKill) { + CompileSuccessfully(GetUnreachableMergeTerminatedBy( + SpvCapabilityShader, SPV_ENV_UNIVERSAL_1_0, SpvOpKill)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateCFG, UnreachableMergeTerminatedByOpReturn) { + CompileSuccessfully(GetUnreachableMergeTerminatedBy( + GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpReturn)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpUnreachable) { + CompileSuccessfully(GetUnreachableMergeTerminatedBy( + SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpUnreachable)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpKill) { + CompileSuccessfully(GetUnreachableMergeTerminatedBy( + SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpKill)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must terminate with OpUnreachable")); +} + +TEST_P(ValidateCFG, WebGPUUnreachableMergeTerminatedByOpReturn) { + CompileSuccessfully(GetUnreachableMergeTerminatedBy( + SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpReturn)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must terminate with OpUnreachable")); +} + +std::string GetUnreachableContinueTerminatedBy(SpvCapability cap, + spv_target_env env, SpvOp op) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block branch("branch", SpvOpBranch); + Block merge("merge", SpvOpReturn); + Block target("target", op); + + if (op == SpvOpBranch) target >> branch; + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) + branch.AppendBody("OpLoopMerge %merge %target None\n"); + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({merge}); + str += merge; + str += target; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpUnreachable) { + CompileSuccessfully(GetUnreachableContinueTerminatedBy( + GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpUnreachable)); + if (GetParam() == SpvCapabilityShader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("targeted by 0 back-edge blocks")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_F(ValidateCFG, UnreachableContinueTerminatedBySpvOpKill) { + CompileSuccessfully(GetUnreachableContinueTerminatedBy( + SpvCapabilityShader, SPV_ENV_UNIVERSAL_1_0, SpvOpKill)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("targeted by 0 back-edge blocks")); +} + +TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpReturn) { + CompileSuccessfully(GetUnreachableContinueTerminatedBy( + GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpReturn)); + if (GetParam() == SpvCapabilityShader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("targeted by 0 back-edge blocks")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, UnreachableContinueTerminatedBySpvOpBranch) { + CompileSuccessfully(GetUnreachableContinueTerminatedBy( + GetParam(), SPV_ENV_UNIVERSAL_1_0, SpvOpBranch)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpUnreachable) { + CompileSuccessfully(GetUnreachableContinueTerminatedBy( + SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpUnreachable)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, unreachable continue-target must " + "terminate with OpBranch.\n %12 = OpLabel\n")); +} + +TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpKill) { + CompileSuccessfully(GetUnreachableContinueTerminatedBy( + SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpKill)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, unreachable continue-target must " + "terminate with OpBranch.\n %12 = OpLabel\n")); +} + +TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpReturn) { + CompileSuccessfully(GetUnreachableContinueTerminatedBy( + SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpReturn)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, unreachable continue-target must " + "terminate with OpBranch.\n %12 = OpLabel\n")); +} + +TEST_F(ValidateCFG, WebGPUUnreachableContinueTerminatedBySpvOpBranch) { + CompileSuccessfully(GetUnreachableContinueTerminatedBy( + SpvCapabilityShader, SPV_ENV_WEBGPU_0, SpvOpBranch)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +std::string GetUnreachableMergeUnreachableMergeInst(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block body("body", SpvOpReturn); + Block entry("entry"); + Block branch("branch", SpvOpBranchConditional); + Block t("t", SpvOpReturn); + Block f("f", SpvOpReturn); + Block merge("merge", SpvOpUnreachable); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) + branch.AppendBody("OpSelectionMerge %merge None\n"); + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += body; + str += merge; + str += entry >> branch; + str += branch >> std::vector({t, f}); + str += t; + str += f; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableMergeUnreachableMergeInst) { + CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst( + GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableMergeUnreachableMergeInst) { + CompileSuccessfully(GetUnreachableMergeUnreachableMergeInst( + SpvCapabilityShader, SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be referenced by a reachable merge instruction")); +} + +std::string GetUnreachableContinueUnreachableLoopInst(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block body("body", SpvOpReturn); + Block entry("entry"); + Block branch("branch", SpvOpBranch); + Block merge("merge", SpvOpReturn); + Block target("target", SpvOpBranch); + + target >> branch; + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) + branch.AppendBody("OpLoopMerge %merge %target None\n"); + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += body; + str += target; + str += merge; + str += entry >> branch; + str += branch >> std::vector({merge}); + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableContinueUnreachableLoopInst) { + CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst( + GetParam(), SPV_ENV_UNIVERSAL_1_0)); + if (GetParam() == SpvCapabilityShader) { + // Shader causes additional structured CFG checks that cause a failure. + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Back-edges (1[%branch] -> 3[%target]) can only be " + "formed between a block and a loop header.")); + + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_F(ValidateCFG, WebGPUUnreachableContinueUnreachableLoopInst) { + CompileSuccessfully(GetUnreachableContinueUnreachableLoopInst( + SpvCapabilityShader, SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be referenced by a reachable loop instruction")); +} + +std::string GetUnreachableMergeWithComplexBody(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block branch("branch", SpvOpBranchConditional); + Block t("t", SpvOpReturn); + Block f("f", SpvOpReturn); + Block merge("merge", SpvOpUnreachable); + + entry.AppendBody(spvIsWebGPUEnv(env) + ? "%placeholder = OpVariable %intptrt Function %two\n" + : "%placeholder = OpVariable %intptrt Function\n"); + entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n"); + merge.AppendBody("OpStore %placeholder %one\n"); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) + branch.AppendBody("OpSelectionMerge %merge None\n"); + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%intptrt = OpTypePointer Function %intt\n"; + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({t, f}); + str += t; + str += f; + str += merge; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableMergeWithComplexBody) { + CompileSuccessfully( + GetUnreachableMergeWithComplexBody(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableMergeWithComplexBody) { + CompileSuccessfully(GetUnreachableMergeWithComplexBody(SpvCapabilityShader, + SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must only contain an OpLabel and OpUnreachable instruction")); +} + +std::string GetUnreachableContinueWithComplexBody(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block branch("branch", SpvOpBranch); + Block merge("merge", SpvOpReturn); + Block target("target", SpvOpBranch); + + target >> branch; + + entry.AppendBody(spvIsWebGPUEnv(env) + ? "%placeholder = OpVariable %intptrt Function %two\n" + : "%placeholder = OpVariable %intptrt Function\n"); + target.AppendBody("OpStore %placeholder %one\n"); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) + branch.AppendBody("OpLoopMerge %merge %target None\n"); + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%intptrt = OpTypePointer Function %intt\n"; + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({merge}); + str += merge; + str += target; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableContinueWithComplexBody) { + CompileSuccessfully( + GetUnreachableContinueWithComplexBody(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableContinueWithComplexBody) { + CompileSuccessfully(GetUnreachableContinueWithComplexBody(SpvCapabilityShader, + SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must only contain an OpLabel and an OpBranch instruction")); +} + +std::string GetUnreachableMergeWithBranchUse(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block branch("branch", SpvOpBranchConditional); + Block t("t", SpvOpBranch); + Block f("f", SpvOpReturn); + Block merge("merge", SpvOpUnreachable); + + entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n"); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) + branch.AppendBody("OpSelectionMerge %merge None\n"); + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({t, f}); + str += t >> merge; + str += f; + str += merge; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableMergeWithBranchUse) { + CompileSuccessfully( + GetUnreachableMergeWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +std::string GetUnreachableMergeWithMultipleUses(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block branch("branch", SpvOpBranchConditional); + Block t("t", SpvOpReturn); + Block f("f", SpvOpReturn); + Block merge("merge", SpvOpUnreachable); + Block duplicate("duplicate", SpvOpBranchConditional); + + entry.AppendBody("%cond = OpSLessThan %boolt %one %two\n"); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) { + branch.AppendBody("OpSelectionMerge %merge None\n"); + duplicate.AppendBody("OpSelectionMerge %merge None\n"); + } + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({t, f}); + str += duplicate >> std::vector({t, f}); + str += t; + str += f; + str += merge; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableMergeWithMultipleUses) { + CompileSuccessfully( + GetUnreachableMergeWithMultipleUses(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + if (GetParam() == SpvCapabilityShader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("is already a merge block for another header")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_F(ValidateCFG, WebGPUUnreachableMergeWithMultipleUses) { + CompileSuccessfully(GetUnreachableMergeWithMultipleUses(SpvCapabilityShader, + SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("is already a merge block for another header")); +} + +std::string GetUnreachableContinueWithBranchUse(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block foo("foo", SpvOpBranch); + Block branch("branch", SpvOpBranch); + Block merge("merge", SpvOpReturn); + Block target("target", SpvOpBranch); + + foo >> target; + target >> branch; + + entry.AppendBody(spvIsWebGPUEnv(env) + ? "%placeholder = OpVariable %intptrt Function %two\n" + : "%placeholder = OpVariable %intptrt Function\n"); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) + branch.AppendBody("OpLoopMerge %merge %target None\n"); + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", "target", std::make_pair("func", "Main")); + + str += types_consts(); + str += "%intptrt = OpTypePointer Function %intt\n"; + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({merge}); + str += merge; + str += target; + str += foo; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableContinueWithBranchUse) { + CompileSuccessfully( + GetUnreachableContinueWithBranchUse(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableContinueWithBranchUse) { + CompileSuccessfully(GetUnreachableContinueWithBranchUse(SpvCapabilityShader, + SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("cannot be the target of a branch.")); +} + +std::string GetReachableMergeAndContinue(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block branch("branch", SpvOpBranch); + Block merge("merge", SpvOpReturn); + Block target("target", SpvOpBranch); + Block body("body", SpvOpBranchConditional); + Block t("t", SpvOpBranch); + Block f("f", SpvOpBranch); + + target >> branch; + body.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + t >> merge; + f >> target; + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) { + branch.AppendBody("OpLoopMerge %merge %target None\n"); + body.AppendBody("OpSelectionMerge %f None\n"); + } + + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", "target", "body", "t", "f", + std::make_pair("func", "Main")); + + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({body}); + str += body >> std::vector({t, f}); + str += t; + str += f; + str += merge; + str += target; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, ReachableMergeAndContinue) { + CompileSuccessfully( + GetReachableMergeAndContinue(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUReachableMergeAndContinue) { + CompileSuccessfully( + GetReachableMergeAndContinue(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +std::string GetUnreachableMergeAndContinue(SpvCapability cap, + spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block branch("branch", SpvOpBranch); + Block merge("merge", SpvOpReturn); + Block target("target", SpvOpBranch); + Block body("body", SpvOpBranchConditional); + Block t("t", SpvOpReturn); + Block f("f", SpvOpReturn); + + target >> branch; + body.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (cap == SpvCapabilityShader) { + branch.AppendBody("OpLoopMerge %merge %target None\n"); + body.AppendBody("OpSelectionMerge %target None\n"); + } + + if (!spvIsWebGPUEnv(env)) + str += nameOps("branch", "merge", "target", "body", "t", "f", + std::make_pair("func", "Main")); + + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> branch; + str += branch >> std::vector({body}); + str += body >> std::vector({t, f}); + str += t; + str += f; + str += merge; + str += target; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableMergeAndContinue) { + CompileSuccessfully( + GetUnreachableMergeAndContinue(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableMergeAndContinue) { + CompileSuccessfully( + GetUnreachableMergeAndContinue(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("unreachable merge-blocks must terminate with OpUnreachable")); +} + +std::string GetUnreachableBlock(SpvCapability cap, spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block unreachable("unreachable"); + Block exit("exit", SpvOpReturn); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (!spvIsWebGPUEnv(env)) + str += nameOps("unreachable", "exit", std::make_pair("func", "Main")); + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + str += entry >> exit; + str += unreachable >> exit; + str += exit; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableBlock) { + CompileSuccessfully(GetUnreachableBlock(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableBlock) { + CompileSuccessfully( + GetUnreachableBlock(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("all blocks must be reachable")); +} + +std::string GetUnreachableBranch(SpvCapability cap, spv_target_env env) { + std::string header = + spvIsWebGPUEnv(env) ? GetWebGPUHeader() : GetDefaultHeader(cap); + + Block entry("entry"); + Block unreachable("unreachable", SpvOpBranchConditional); + Block unreachablechildt("unreachablechildt"); + Block unreachablechildf("unreachablechildf"); + Block merge("merge"); + Block exit("exit", SpvOpReturn); + + unreachable.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (cap == SpvCapabilityShader) + unreachable.AppendBody("OpSelectionMerge %merge None\n"); + + std::string str = header; + if (spvIsWebGPUEnv(env)) { + str += + "OpEntryPoint Fragment %func \"func\"\n" + "OpExecutionMode %func OriginUpperLeft\n"; + } + if (!spvIsWebGPUEnv(env)) + str += nameOps("unreachable", "exit", std::make_pair("func", "Main")); + str += types_consts(); + str += "%func = OpFunction %voidt None %funct\n"; + + str += entry >> exit; + str += + unreachable >> std::vector({unreachablechildt, unreachablechildf}); + str += unreachablechildt >> merge; + str += unreachablechildf >> merge; + str += merge >> exit; + str += exit; + str += "OpFunctionEnd\n"; + + return str; +} + +TEST_P(ValidateCFG, UnreachableBranch) { + CompileSuccessfully(GetUnreachableBranch(GetParam(), SPV_ENV_UNIVERSAL_1_0)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WebGPUUnreachableBranch) { + CompileSuccessfully( + GetUnreachableBranch(SpvCapabilityShader, SPV_ENV_WEBGPU_0)); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("all blocks must be reachable")); +} + +TEST_P(ValidateCFG, EmptyFunction) { + std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) + + R"(%func = OpFunction %voidt None %funct + %l = OpLabel + OpReturn + OpFunctionEnd)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateCFG, SingleBlockLoop) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) loop.AppendBody("OpLoopMerge %exit %loop None\n"); + + std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> std::vector({loop, exit}); + str += exit; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateCFG, NestedLoops) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop1("loop1"); + Block loop1_cont_break_block("loop1_cont_break_block", + SpvOpBranchConditional); + Block loop2("loop2", SpvOpBranchConditional); + Block loop2_merge("loop2_merge"); + Block loop1_merge("loop1_merge"); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) { + loop1.SetBody("OpLoopMerge %loop1_merge %loop2 None\n"); + loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); + } + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("loop2", "loop2_merge") + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop1; + str += loop1 >> loop1_cont_break_block; + str += loop1_cont_break_block >> std::vector({loop1_merge, loop2}); + str += loop2 >> std::vector({loop2, loop2_merge}); + str += loop2_merge >> loop1; + str += loop1_merge >> exit; + str += exit; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateCFG, NestedSelection) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + const int N = 256; + std::vector if_blocks; + std::vector merge_blocks; + Block inner("inner"); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + + if_blocks.emplace_back("if0", SpvOpBranchConditional); + + if (is_shader) if_blocks[0].SetBody("OpSelectionMerge %if_merge0 None\n"); + merge_blocks.emplace_back("if_merge0", SpvOpReturn); + + for (int i = 1; i < N; i++) { + std::stringstream ss; + ss << i; + if_blocks.emplace_back("if" + ss.str(), SpvOpBranchConditional); + if (is_shader) + if_blocks[i].SetBody("OpSelectionMerge %if_merge" + ss.str() + " None\n"); + merge_blocks.emplace_back("if_merge" + ss.str(), SpvOpBranch); + } + std::string str = GetDefaultHeader(GetParam()) + std::string(types_consts()) + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> if_blocks[0]; + for (int i = 0; i < N - 1; i++) { + str += + if_blocks[i] >> std::vector({if_blocks[i + 1], merge_blocks[i]}); + } + str += if_blocks.back() >> std::vector({inner, merge_blocks.back()}); + str += inner >> merge_blocks.back(); + for (int i = N - 1; i > 0; i--) { + str += merge_blocks[i] >> merge_blocks[i - 1]; + } + str += merge_blocks[0]; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateCFG, BackEdgeBlockDoesntPostDominateContinueTargetBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop1("loop1", SpvOpBranchConditional); + Block loop2("loop2", SpvOpBranchConditional); + Block loop2_merge("loop2_merge"); + Block loop1_cont("loop1_cont", SpvOpBranchConditional); + Block be_block("be_block"); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) { + loop1.SetBody("OpLoopMerge %exit %loop1_cont None\n"); + loop2.SetBody("OpLoopMerge %loop2_merge %loop2 None\n"); + } + + std::string str = + GetDefaultHeader(GetParam()) + + nameOps("loop1", "loop2", "be_block", "loop1_cont", "loop2_merge") + + types_consts() + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop1; + str += loop1 >> std::vector({loop2, exit}); + str += loop2 >> std::vector({loop2, loop2_merge}); + str += loop2_merge >> loop1_cont; + str += loop1_cont >> std::vector({be_block, exit}); + str += be_block >> loop1; + str += exit; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + if (GetParam() == SpvCapabilityShader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("The continue construct with the continue target " + ".\\[%loop1_cont\\] is not post dominated by the " + "back-edge block .\\[%be_block\\]\n" + " %be_block = OpLabel\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, BranchingToNonLoopHeaderBlockBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block split("split", SpvOpBranchConditional); + Block t("t"); + Block f("f"); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) split.SetBody("OpSelectionMerge %exit None\n"); + + std::string str = GetDefaultHeader(GetParam()) + nameOps("split", "f") + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> split; + str += split >> std::vector({t, f}); + str += t >> exit; + str += f >> split; + str += exit; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("Back-edges \\(.\\[%f\\] -> .\\[%split\\]\\) can only " + "be formed between a block and a loop header.\n" + " %f = OpLabel\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, BranchingToSameNonLoopHeaderBlockBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block split("split", SpvOpBranchConditional); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) split.SetBody("OpSelectionMerge %exit None\n"); + + std::string str = GetDefaultHeader(GetParam()) + nameOps("split") + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> split; + str += split >> std::vector({split, exit}); + str += exit; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex( + "Back-edges \\(.\\[%split\\] -> .\\[%split\\]\\) can only be " + "formed between a block and a loop header.\n %split = OpLabel\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, MultipleBackEdgeBlocksToLoopHeaderBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block back0("back0"); + Block back1("back1"); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %back0 None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("loop", "back0", "back1") + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> std::vector({back0, back1}); + str += back0 >> loop; + str += back1 >> loop; + str += merge; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex( + "Loop header .\\[%loop\\] is targeted by 2 back-edge blocks but " + "the standard requires exactly one\n %loop = OpLabel\n")) + << str; + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, ContinueTargetMustBePostDominatedByBackEdge) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block cheader("cheader", SpvOpBranchConditional); + Block be_block("be_block"); + Block merge("merge", SpvOpReturn); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %cheader None\n"); + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("cheader", "be_block") + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> std::vector({cheader, merge}); + str += cheader >> std::vector({exit, be_block}); + str += exit; // Branches out of a continue construct + str += be_block >> loop; + str += merge; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("The continue construct with the continue target " + ".\\[%cheader\\] is not post dominated by the " + "back-edge block .\\[%be_block\\]\n" + " %be_block = OpLabel\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, BranchOutOfConstructToMergeBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block cont("cont", SpvOpBranchConditional); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); + + std::string str = GetDefaultHeader(GetParam()) + nameOps("cont", "loop") + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> std::vector({cont, merge}); + str += cont >> std::vector({loop, merge}); + str += merge; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("The continue construct with the continue target " + ".\\[%loop\\] is not post dominated by the " + "back-edge block .\\[%cont\\]\n" + " %cont = OpLabel\n")) + << str; + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, BranchOutOfConstructBad) { + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block cont("cont", SpvOpBranchConditional); + Block merge("merge"); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) loop.SetBody("OpLoopMerge %merge %loop None\n"); + + std::string str = GetDefaultHeader(GetParam()) + nameOps("cont", "loop") + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> std::vector({cont, merge}); + str += cont >> std::vector({loop, exit}); + str += merge >> exit; + str += exit; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + if (is_shader) { + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("The continue construct with the continue target " + ".\\[%loop\\] is not post dominated by the " + "back-edge block .\\[%cont\\]\n" + " %cont = OpLabel\n")); + } else { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_F(ValidateCFG, OpSwitchToUnreachableBlock) { + Block entry("entry", SpvOpSwitch); + Block case0("case0"); + Block case1("case1"); + Block case2("case2"); + Block def("default", SpvOpUnreachable); + Block phi("phi", SpvOpReturn); + + std::string str = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %id +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 430 +OpName %main "main" +OpDecorate %id BuiltIn GlobalInvocationId +%void = OpTypeVoid +%voidf = OpTypeFunction %void +%u32 = OpTypeInt 32 0 +%f32 = OpTypeFloat 32 +%uvec3 = OpTypeVector %u32 3 +%fvec3 = OpTypeVector %f32 3 +%uvec3ptr = OpTypePointer Input %uvec3 +%id = OpVariable %uvec3ptr Input +%one = OpConstant %u32 1 +%three = OpConstant %u32 3 +%main = OpFunction %void None %voidf +)"; + + entry.SetBody( + "%idval = OpLoad %uvec3 %id\n" + "%x = OpCompositeExtract %u32 %idval 0\n" + "%selector = OpUMod %u32 %x %three\n" + "OpSelectionMerge %phi None\n"); + str += entry >> std::vector({def, case0, case1, case2}); + str += case1 >> phi; + str += def; + str += phi; + str += case0 >> phi; + str += case2 >> phi; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, LoopWithZeroBackEdgesBad) { + std::string str = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %loop "loop" +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%main = OpFunction %voidt None %funct +%loop = OpLabel + OpLoopMerge %exit %loop None + OpBranch %exit +%exit = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("Loop header .\\[%loop\\] is targeted by " + "0 back-edge blocks but the standard requires exactly " + "one\n %loop = OpLabel\n")); +} + +TEST_F(ValidateCFG, LoopWithBackEdgeFromUnreachableContinueConstructGood) { + std::string str = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpName %loop "loop" +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%floatt = OpTypeFloat 32 +%boolt = OpTypeBool +%one = OpConstant %floatt 1 +%two = OpConstant %floatt 2 +%main = OpFunction %voidt None %funct +%entry = OpLabel + OpBranch %loop +%loop = OpLabel + OpLoopMerge %exit %cont None + OpBranch %16 +%16 = OpLabel +%cond = OpFOrdLessThan %boolt %one %two + OpBranchConditional %cond %body %exit +%body = OpLabel + OpReturn +%cont = OpLabel ; Reachable only from OpLoopMerge ContinueTarget parameter + OpBranch %loop ; Should be considered a back-edge +%exit = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_P(ValidateCFG, + NestedConstructWithUnreachableMergeBlockBranchingToOuterMergeBlock) { + // Test for https://github.com/KhronosGroup/SPIRV-Tools/issues/297 + // The nested construct has an unreachable merge block. In the + // augmented CFG that merge block + // we still determine that the + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry", SpvOpBranchConditional); + Block inner_head("inner_head", SpvOpBranchConditional); + Block inner_true("inner_true", SpvOpReturn); + Block inner_false("inner_false", SpvOpReturn); + Block inner_merge("inner_merge"); + Block exit("exit", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) { + entry.AppendBody("OpSelectionMerge %exit None\n"); + inner_head.SetBody("OpSelectionMerge %inner_merge None\n"); + } + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("entry", "inner_merge", "exit") + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> std::vector({inner_head, exit}); + str += inner_head >> std::vector({inner_true, inner_false}); + str += inner_true; + str += inner_false; + str += inner_merge >> exit; + str += exit; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_P(ValidateCFG, ContinueTargetCanBeMergeBlockForNestedStructure) { + // The continue construct cannot be the merge target of a nested selection + // because the loop construct must contain "if_merge" because it contains + // "if_head". + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop"); + Block if_head("if_head", SpvOpBranchConditional); + Block if_true("if_true"); + Block if_merge("if_merge", SpvOpBranchConditional); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) { + loop.SetBody("OpLoopMerge %merge %if_merge None\n"); + if_head.SetBody("OpSelectionMerge %if_merge None\n"); + } + + std::string str = + GetDefaultHeader(GetParam()) + + nameOps("entry", "loop", "if_head", "if_true", "if_merge", "merge") + + types_consts() + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> if_head; + str += if_head >> std::vector({if_true, if_merge}); + str += if_true >> if_merge; + str += if_merge >> std::vector({loop, merge}); + str += merge; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + if (is_shader) { + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Header block 3[%if_head] is contained in the loop construct " + "headed " + "by 2[%loop], but its merge block 5[%if_merge] is not")); + } else { + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); + } +} + +TEST_P(ValidateCFG, SingleLatchBlockMultipleBranchesToLoopHeader) { + // This test case ensures we allow both branches of a loop latch block + // to go back to the loop header. It still counts as a single back edge. + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop", SpvOpBranchConditional); + Block latch("latch", SpvOpBranchConditional); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) { + loop.SetBody("OpLoopMerge %merge %latch None\n"); + } + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("entry", "loop", "latch", "merge") + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> std::vector({latch, merge}); + str += latch >> std::vector({loop, loop}); // This is the key + str += merge; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) + << str << getDiagnosticString(); +} + +TEST_P(ValidateCFG, SingleLatchBlockHeaderContinueTargetIsItselfGood) { + // This test case ensures we don't count a Continue Target from a loop + // header to itself as a self-loop when computing back edges. + // Also, it detects that there is an edge from %latch to the pseudo-exit + // node, rather than from %loop. In particular, it detects that we + // have used the *reverse* textual order of blocks when computing + // predecessor traversal roots. + bool is_shader = GetParam() == SpvCapabilityShader; + Block entry("entry"); + Block loop("loop"); + Block latch("latch"); + Block merge("merge", SpvOpReturn); + + entry.SetBody("%cond = OpSLessThan %boolt %one %two\n"); + if (is_shader) { + loop.SetBody("OpLoopMerge %merge %loop None\n"); + } + + std::string str = GetDefaultHeader(GetParam()) + + nameOps("entry", "loop", "latch", "merge") + + types_consts() + + "%func = OpFunction %voidt None %funct\n"; + + str += entry >> loop; + str += loop >> latch; + str += latch >> loop; + str += merge; + str += "OpFunctionEnd"; + + CompileSuccessfully(str); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) + << str << getDiagnosticString(); +} + +// Unit test to check the case where a basic block is the entry block of 2 +// different constructs. In this case, the basic block is the entry block of a +// continue construct as well as a selection construct. See issue# 517 for more +// details. +TEST_F(ValidateCFG, BasicBlockIsEntryBlockOfTwoConstructsGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %bool = OpTypeBool + %int = OpTypeInt 32 1 + %void_func = OpTypeFunction %void + %int_0 = OpConstant %int 0 + %testfun = OpFunction %void None %void_func + %label_1 = OpLabel + OpBranch %start + %start = OpLabel + %cond = OpSLessThan %bool %int_0 %int_0 + ; + ; Note: In this case, the "target" block is both the entry block of + ; the continue construct of the loop as well as the entry block of + ; the selection construct. + ; + OpLoopMerge %loop_merge %target None + OpBranchConditional %cond %target %loop_merge + %loop_merge = OpLabel + OpReturn + %target = OpLabel + OpSelectionMerge %selection_merge None + OpBranchConditional %cond %do_stuff %do_other_stuff + %do_other_stuff = OpLabel + OpBranch %selection_merge + %selection_merge = OpLabel + OpBranch %start + %do_stuff = OpLabel + OpBranch %selection_merge + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, OpReturnInNonVoidFunc) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %int = OpTypeInt 32 1 + %int_func = OpTypeFunction %int + %testfun = OpFunction %int None %int_func + %label_1 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpReturn can only be called from a function with void return type.\n" + " OpReturn")); +} + +TEST_F(ValidateCFG, StructuredCFGBranchIntoSelectionBody) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %true %then %merge +%merge = OpLabel +OpBranch %then +%then = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("branches to the selection construct, but not to the " + "selection header 6\n %7 = OpLabel")); +} + +TEST_F(ValidateCFG, SwitchDefaultOnly) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpFunction %1 None %4 +%6 = OpLabel +OpSelectionMerge %7 None +OpSwitch %3 %7 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, SwitchSingleCase) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpFunction %1 None %4 +%6 = OpLabel +OpSelectionMerge %7 None +OpSwitch %3 %7 0 %8 +%8 = OpLabel +OpBranch %7 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MultipleFallThroughBlocks) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpTypeBool +%6 = OpConstantTrue %5 +%7 = OpFunction %1 None %4 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %3 %10 0 %11 1 %12 +%10 = OpLabel +OpBranchConditional %6 %11 %12 +%11 = OpLabel +OpBranch %9 +%12 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Case construct that targets 10[%10] has branches to multiple other " + "case construct targets 12[%12] and 11[%11]\n %10 = OpLabel")); +} + +TEST_F(ValidateCFG, MultipleFallThroughToDefault) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpTypeBool +%6 = OpConstantTrue %5 +%7 = OpFunction %1 None %4 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %3 %10 0 %11 1 %12 +%10 = OpLabel +OpBranch %9 +%11 = OpLabel +OpBranch %10 +%12 = OpLabel +OpBranch %10 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Multiple case constructs have branches to the case construct " + "that targets 10[%10]\n %10 = OpLabel")); +} + +TEST_F(ValidateCFG, MultipleFallThroughToNonDefault) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpTypeBool +%6 = OpConstantTrue %5 +%7 = OpFunction %1 None %4 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %3 %10 0 %11 1 %12 +%10 = OpLabel +OpBranch %12 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Multiple case constructs have branches to the case construct " + "that targets 12[%12]\n %12 = OpLabel")); +} + +TEST_F(ValidateCFG, DuplicateTargetWithFallThrough) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpTypeBool +%6 = OpConstantTrue %5 +%7 = OpFunction %1 None %4 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %3 %10 0 %10 1 %11 +%10 = OpLabel +OpBranch %11 +%11 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, WrongOperandList) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpTypeBool +%6 = OpConstantTrue %5 +%7 = OpFunction %1 None %4 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %3 %10 0 %11 1 %12 +%10 = OpLabel +OpBranch %9 +%12 = OpLabel +OpBranch %11 +%11 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Case construct that targets 12[%12] has branches to the case " + "construct that targets 11[%11], but does not immediately " + "precede it in the OpSwitch's target list\n" + " OpSwitch %uint_0 %10 0 %11 1 %12")); +} + +TEST_F(ValidateCFG, WrongOperandListThroughDefault) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpTypeBool +%6 = OpConstantTrue %5 +%7 = OpFunction %1 None %4 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %3 %10 0 %11 1 %12 +%10 = OpLabel +OpBranch %11 +%12 = OpLabel +OpBranch %10 +%11 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Case construct that targets 12[%12] has branches to the case " + "construct that targets 11[%11], but does not immediately " + "precede it in the OpSwitch's target list\n" + " OpSwitch %uint_0 %10 0 %11 1 %12")); +} + +TEST_F(ValidateCFG, WrongOperandListNotLast) { + std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpTypeBool +%6 = OpConstantTrue %5 +%7 = OpFunction %1 None %4 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %3 %10 0 %11 1 %12 2 %13 +%10 = OpLabel +OpBranch %9 +%12 = OpLabel +OpBranch %11 +%11 = OpLabel +OpBranch %9 +%13 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Case construct that targets 12[%12] has branches to the case " + "construct that targets 11[%11], but does not immediately " + "precede it in the OpSwitch's target list\n" + " OpSwitch %uint_0 %10 0 %11 1 %12 2 %13")); +} + +TEST_F(ValidateCFG, GoodUnreachableSwitch) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%5 = OpTypeBool +%6 = OpConstantTrue %5 +%7 = OpTypeInt 32 1 +%9 = OpConstant %7 0 +%2 = OpFunction %3 None %4 +%10 = OpLabel +OpSelectionMerge %11 None +OpBranchConditional %6 %12 %13 +%12 = OpLabel +OpReturn +%13 = OpLabel +OpReturn +%11 = OpLabel +OpSelectionMerge %14 None +OpSwitch %9 %14 0 %15 +%15 = OpLabel +OpBranch %14 +%14 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, InvalidCaseExit) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypeFunction %2 +%5 = OpConstant %3 0 +%1 = OpFunction %2 None %4 +%6 = OpLabel +OpSelectionMerge %7 None +OpSwitch %5 %7 0 %8 1 %9 +%8 = OpLabel +OpBranch %10 +%9 = OpLabel +OpBranch %10 +%10 = OpLabel +OpReturn +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Case construct that targets 8[%8] has invalid branch " + "to block 10[%10] (not another case construct, " + "corresponding merge, outer loop merge or outer loop " + "continue)")); +} + +TEST_F(ValidateCFG, GoodCaseExitsToOuterConstructs) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +OpBranch %2 +%2 = OpLabel +OpLoopMerge %7 %6 None +OpBranch %3 +%3 = OpLabel +OpSelectionMerge %5 None +OpSwitch %int0 %5 0 %4 +%4 = OpLabel +OpBranchConditional %true %6 %7 +%5 = OpLabel +OpBranchConditional %true %6 %7 +%6 = OpLabel +OpBranch %2 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, SwitchCaseOrderingBad1) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %default "default" +OpName %other "other" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %undef %default 0 %other 1 %default +%default = OpLabel +OpBranch %other +%other = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Case construct that targets 1[%default] has branches to the " + "case construct that targets 2[%other], but does not " + "immediately precede it in the OpSwitch's target list")); +} + +TEST_F(ValidateCFG, SwitchCaseOrderingBad2) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %default "default" +OpName %other "other" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %undef %default 0 %default 1 %other +%other = OpLabel +OpBranch %default +%default = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Case construct that targets 2[%other] has branches to the " + "case construct that targets 1[%default], but does not " + "immediately precede it in the OpSwitch's target list")); +} + +TEST_F(ValidateCFG, SwitchMultipleDefaultWithFallThroughGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %first "first" +OpName %second "second" +OpName %third "third" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %undef %second 0 %first 1 %second 2 %third +%first = OpLabel +OpBranch %second +%second = OpLabel +OpBranch %third +%third = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, SwitchMultipleDefaultWithFallThroughBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %first "first" +OpName %second "second" +OpName %third "third" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %undef %second 0 %second 1 %first 2 %third +%first = OpLabel +OpBranch %second +%second = OpLabel +OpBranch %third +%third = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); +} + +TEST_F(ValidateCFG, GoodUnreachableSelection) { + const std::string text = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%8 = OpTypeFunction %void +%bool = OpTypeBool +%false = OpConstantFalse %bool +%main = OpFunction %void None %8 +%15 = OpLabel +OpBranch %16 +%16 = OpLabel +OpLoopMerge %17 %18 None +OpBranch %19 +%19 = OpLabel +OpBranchConditional %false %21 %17 +%21 = OpLabel +OpSelectionMerge %22 None +OpBranchConditional %false %23 %22 +%23 = OpLabel +OpBranch %24 +%24 = OpLabel +OpLoopMerge %25 %26 None +OpBranch %27 +%27 = OpLabel +OpReturn +%26 = OpLabel +OpBranchConditional %false %24 %25 +%25 = OpLabel +OpSelectionMerge %28 None +OpBranchConditional %false %18 %28 +%28 = OpLabel +OpBranch %22 +%22 = OpLabel +OpBranch %18 +%18 = OpLabel +OpBranch %16 +%17 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, ShaderWithPhiPtr) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %void = OpTypeVoid + %5 = OpTypeFunction %void + %1 = OpFunction %void None %5 + %6 = OpLabel + %7 = OpVariable %_ptr_Function_bool Function + %8 = OpVariable %_ptr_Function_bool Function + %9 = OpUndef %bool + OpSelectionMerge %10 None + OpBranchConditional %9 %11 %10 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Using pointers with OpPhi requires capability " + "VariablePointers or VariablePointersStorageBuffer")); +} + +TEST_F(ValidateCFG, VarPtrShaderWithPhiPtr) { + const std::string text = R"( + OpCapability Shader + OpCapability VariablePointers + OpExtension "SPV_KHR_variable_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %void = OpTypeVoid + %5 = OpTypeFunction %void + %1 = OpFunction %void None %5 + %6 = OpLabel + %7 = OpVariable %_ptr_Function_bool Function + %8 = OpVariable %_ptr_Function_bool Function + %9 = OpUndef %bool + OpSelectionMerge %10 None + OpBranchConditional %9 %11 %10 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, VarPtrStgBufShaderWithPhiStgBufPtr) { + const std::string text = R"( + OpCapability Shader + OpCapability VariablePointersStorageBuffer + OpExtension "SPV_KHR_variable_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + %bool = OpTypeBool + %float = OpTypeFloat 32 +%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float + %7 = OpVariable %_ptr_StorageBuffer_float StorageBuffer + %8 = OpVariable %_ptr_StorageBuffer_float StorageBuffer + %void = OpTypeVoid + %5 = OpTypeFunction %void + %1 = OpFunction %void None %5 + %6 = OpLabel + %9 = OpUndef %bool + OpSelectionMerge %10 None + OpBranchConditional %9 %11 %10 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + %12 = OpPhi %_ptr_StorageBuffer_float %7 %6 %8 %11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, KernelWithPhiPtr) { + const std::string text = R"( + OpCapability Kernel + OpCapability Addresses + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + %bool = OpTypeBool +%_ptr_Function_bool = OpTypePointer Function %bool + %void = OpTypeVoid + %5 = OpTypeFunction %void + %1 = OpFunction %void None %5 + %6 = OpLabel + %7 = OpVariable %_ptr_Function_bool Function + %8 = OpVariable %_ptr_Function_bool Function + %9 = OpUndef %bool + OpSelectionMerge %10 None + OpBranchConditional %9 %11 %10 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + %12 = OpPhi %_ptr_Function_bool %7 %6 %8 %11 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, SwitchTargetMustBeLabel) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "foo" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %1 = OpFunction %void None %5 + %6 = OpLabel + %7 = OpCopyObject %uint %uint_0 + OpSelectionMerge %8 None + OpSwitch %uint_0 %8 0 %7 + %8 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("'Target Label' operands for OpSwitch must " + "be IDs of an OpLabel instruction")); +} + +TEST_F(ValidateCFG, BranchTargetMustBeLabel) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "foo" + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %1 = OpFunction %void None %5 + %2 = OpLabel + %7 = OpCopyObject %uint %uint_0 + OpBranch %7 + %8 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("'Target Label' operands for OpBranch must " + "be the ID of an OpLabel instruction")); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableOneBlock) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableOpBranch) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %block +%block = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableOpBranchConditional) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %block None +OpBranchConditional %undef %block %unreachable +%block = OpLabel +OpReturn +%unreachable = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableOpSwitch) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %block1 None +OpSwitch %undef %block1 0 %unreachable 1 %block2 +%block1 = OpLabel +OpReturn +%unreachable = OpLabel +OpUnreachable +%block2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, ReachableOpUnreachableLoop) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %unreachable %loop None +OpBranchConditional %undef %loop %unreachable +%unreachable = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, UnreachableLoopBadBackedge) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %2 "main" +OpExecutionMode %2 OriginUpperLeft +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%8 = OpTypeBool +%13 = OpConstantTrue %8 +%2 = OpFunction %4 None %5 +%14 = OpLabel +OpSelectionMerge %15 None +OpBranchConditional %13 %15 %15 +%16 = OpLabel +OpLoopMerge %17 %18 None +OpBranch %17 +%18 = OpLabel +OpBranch %17 +%17 = OpLabel +OpBranch %15 +%15 = OpLabel +OpReturn +OpFunctionEnd +)"; + + // The back-edge in this test is bad, but the validator fails to identify it + // because it is in an entirely unreachable section of code. Prior to #2488 + // this code failed an assert in Construct::blocks(). + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, OneContinueTwoBackedges) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" +OpExecutionMode %1 LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%5 = OpTypeFunction %void +%1 = OpFunction %void None %5 +%6 = OpLabel +OpBranch %7 +%7 = OpLabel +OpLoopMerge %8 %9 None +OpBranch %10 +%10 = OpLabel +OpLoopMerge %11 %9 None +OpBranchConditional %true %11 %9 +%9 = OpLabel +OpBranchConditional %true %10 %7 +%11 = OpLabel +OpBranch %8 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block 9 branches to the loop construct, but not " + "to the loop header 7")); +} + +TEST_F(ValidateCFG, LoopMergeMergeBlockNotLabel) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %undef "undef" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +OpLoopMerge %undef %2 None +OpBranchConditional %undef %2 %2 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Merge Block 1[%undef] must be an OpLabel")); +} + +TEST_F(ValidateCFG, LoopMergeContinueTargetNotLabel) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %undef "undef" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +OpLoopMerge %2 %undef None +OpBranchConditional %undef %2 %2 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Continue Target 1[%undef] must be an OpLabel")); +} + +TEST_F(ValidateCFG, LoopMergeMergeBlockContinueTargetSameLabel) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %undef "undef" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +OpLoopMerge %2 %2 None +OpBranchConditional %undef %2 %2 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Merge Block and Continue Target must be different ids")); +} + +TEST_F(ValidateCFG, LoopMergeUnrollAndDontUnroll) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %undef "undef" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%5 = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %2 %3 Unroll|DontUnroll +OpBranchConditional %undef %2 %3 +%3 = OpLabel +OpBranch %1 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Unroll and DontUnroll loop controls must not both be specified")); +} + +TEST_F(ValidateCFG, LoopMergePeelCountAndDontUnroll) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %undef "undef" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%5 = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %2 %3 DontUnroll|PeelCount 1 +OpBranchConditional %undef %2 %3 +%3 = OpLabel +OpBranch %1 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "PeelCount and DontUnroll loop controls must not both be specified")); +} + +TEST_F(ValidateCFG, LoopMergePartialCountAndDontUnroll) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %undef "undef" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%5 = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %2 %3 DontUnroll|PartialCount 1 +OpBranchConditional %undef %2 %3 +%3 = OpLabel +OpBranch %1 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("PartialCount and DontUnroll loop controls must not " + "both be specified")); +} + +TEST_F(ValidateCFG, LoopMergeIterationMultipleZero) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %undef "undef" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%5 = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %2 %3 IterationMultiple 0 +OpBranchConditional %undef %2 %3 +%3 = OpLabel +OpBranch %1 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "IterationMultiple loop control operand must be greater than zero")); +} + +TEST_F(ValidateCFG, LoopMergeIterationMultipleZeroMoreOperands) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %undef "undef" +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%5 = OpLabel +OpBranch %1 +%1 = OpLabel +OpLoopMerge %2 %3 MaxIterations|IterationMultiple 4 0 +OpBranchConditional %undef %2 %3 +%3 = OpLabel +OpBranch %1 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "IterationMultiple loop control operand must be greater than zero")); +} + +TEST_F(ValidateCFG, LoopMergeTargetsHeader) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%undef = OpUndef %bool +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %loop %continue None +OpBranch %body +%continue = OpLabel +OpBranch %loop +%body = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Merge Block may not be the block containing the OpLoopMerge")); +} + +TEST_F(ValidateCFG, InvalidSelectionExit) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantTrue %3 +%5 = OpTypeFunction %2 +%1 = OpFunction %2 None %5 +%6 = OpLabel +OpSelectionMerge %7 None +OpBranchConditional %4 %7 %8 +%8 = OpLabel +OpSelectionMerge %9 None +OpBranchConditional %4 %10 %9 +%10 = OpLabel +OpBranch %7 +%9 = OpLabel +OpBranch %7 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block 10[%10] exits the selection headed by " + "8[%8], but not via a structured exit")); +} + +TEST_F(ValidateCFG, InvalidLoopExit) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantTrue %3 +%5 = OpTypeFunction %2 +%1 = OpFunction %2 None %5 +%6 = OpLabel +OpSelectionMerge %7 None +OpBranchConditional %4 %7 %8 +%8 = OpLabel +OpLoopMerge %9 %10 None +OpBranchConditional %4 %9 %11 +%11 = OpLabel +OpBranchConditional %4 %7 %10 +%10 = OpLabel +OpBranch %8 +%9 = OpLabel +OpBranch %7 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block 11[%11] exits the loop headed by " + "8[%8], but not via a structured exit")); +} + +TEST_F(ValidateCFG, InvalidContinueExit) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeBool +%4 = OpConstantTrue %3 +%5 = OpTypeFunction %2 +%1 = OpFunction %2 None %5 +%6 = OpLabel +OpSelectionMerge %7 None +OpBranchConditional %4 %7 %8 +%8 = OpLabel +OpLoopMerge %9 %10 None +OpBranchConditional %4 %9 %10 +%10 = OpLabel +OpBranch %11 +%11 = OpLabel +OpBranchConditional %4 %8 %7 +%9 = OpLabel +OpBranch %7 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block 11[%11] exits the continue headed by " + "10[%10], but not via a structured exit")); +} + +TEST_F(ValidateCFG, InvalidSelectionExitBackedge) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeBool +%3 = OpUndef %2 +%4 = OpTypeFunction %1 +%5 = OpFunction %1 None %4 +%6 = OpLabel +OpBranch %7 +%7 = OpLabel +OpLoopMerge %8 %9 None +OpBranchConditional %3 %8 %9 +%9 = OpLabel +OpSelectionMerge %10 None +OpBranchConditional %3 %11 %12 +%11 = OpLabel +OpBranch %13 +%12 = OpLabel +OpBranch %13 +%13 = OpLabel +OpBranch %7 +%10 = OpLabel +OpUnreachable +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block 13[%13] exits the selection headed by " + "9[%9], but not via a structured exit")); +} + +TEST_F(ValidateCFG, BreakFromSwitch) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeBool +%3 = OpTypeInt 32 0 +%4 = OpUndef %2 +%5 = OpUndef %3 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %5 %9 0 %10 +%10 = OpLabel +OpSelectionMerge %11 None +OpBranchConditional %4 %11 %12 +%12 = OpLabel +OpBranch %9 +%11 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, InvalidBreakFromSwitch) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeBool +%3 = OpTypeInt 32 0 +%4 = OpUndef %2 +%5 = OpUndef %3 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %5 %9 0 %10 +%10 = OpLabel +OpSelectionMerge %11 None +OpSwitch %5 %11 0 %12 +%12 = OpLabel +OpBranch %9 +%11 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block 12[%12] exits the selection headed by " + "10[%10], but not via a structured exit")); +} + +TEST_F(ValidateCFG, BreakToOuterSwitch) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeBool +%3 = OpTypeInt 32 0 +%4 = OpUndef %2 +%5 = OpUndef %3 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %5 %9 0 %10 +%10 = OpLabel +OpSelectionMerge %11 None +OpSwitch %5 %11 0 %12 +%12 = OpLabel +OpSelectionMerge %13 None +OpBranchConditional %4 %13 %14 +%14 = OpLabel +OpBranch %9 +%13 = OpLabel +OpBranch %11 +%11 = OpLabel +OpBranch %9 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block 14[%14] exits the selection headed by " + "10[%10], but not via a structured exit")); +} + +TEST_F(ValidateCFG, BreakToOuterLoop) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeBool +%3 = OpUndef %2 +%4 = OpTypeFunction %1 +%5 = OpFunction %1 None %4 +%6 = OpLabel +OpBranch %7 +%7 = OpLabel +OpLoopMerge %8 %9 None +OpBranch %10 +%10 = OpLabel +OpLoopMerge %11 %12 None +OpBranch %13 +%13 = OpLabel +OpSelectionMerge %14 None +OpBranchConditional %3 %14 %15 +%15 = OpLabel +OpBranch %8 +%14 = OpLabel +OpBranch %12 +%12 = OpLabel +OpBranchConditional %3 %10 %11 +%11 = OpLabel +OpBranch %9 +%9 = OpLabel +OpBranchConditional %3 %7 %8 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("block 15[%15] exits the loop headed by " + "10[%10], but not via a structured exit")); +} + +TEST_F(ValidateCFG, ContinueFromNestedSelection) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%4 = OpFunction %void None %void_fn +%5 = OpLabel +OpBranch %48 +%48 = OpLabel +OpLoopMerge %47 %50 None +OpBranch %10 +%10 = OpLabel +OpLoopMerge %12 %37 None +OpBranchConditional %undef %11 %12 +%11 = OpLabel +OpSelectionMerge %31 None +OpBranchConditional %undef %30 %31 +%30 = OpLabel +OpSelectionMerge %38 None +OpBranchConditional %undef %36 %38 +%36 = OpLabel +OpBranch %38 +%38 = OpLabel +OpBranch %37 +%37 = OpLabel +OpBranch %10 +%31 = OpLabel +OpBranch %12 +%12 = OpLabel +OpSelectionMerge %55 None +OpBranchConditional %undef %47 %55 +%55 = OpLabel +OpBranch %47 +%50 = OpLabel +OpBranch %48 +%47 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeConditionalBranchBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %then %else +%then = OpLabel +OpReturn +%else = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, MissingMergeSwitchBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSwitch %undef %then 0 %else +%then = OpLabel +OpReturn +%else = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, MissingMergeSwitchBad2) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSwitch %undef %then 0 %then 1 %then 2 %else +%then = OpLabel +OpReturn +%else = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, MissingMergeOneBranchToMergeGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %b3 None +OpBranchConditional %undef %b1 %b2 +%b1 = OpLabel +OpBranchConditional %undef %b2 %b3 +%b2 = OpLabel +OpBranch %b3 +%b3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeSameTargetConditionalBranchGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranchConditional %undef %then %then +%then = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeOneTargetSwitchGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef = OpUndef %int +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSwitch %undef %then 0 %then 1 %then +%then = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeOneUnseenTargetSwitchGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%int = OpTypeInt 32 0 +%undef_int = OpUndef %int +%bool = OpTypeBool +%undef_bool = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %undef_bool %merge %b1 +%b1 = OpLabel +OpSwitch %undef_int %b2 0 %b2 1 %merge 2 %b2 +%b2 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeLoopBreakGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpBranchConditional %undef %body2 %exit +%body2 = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeLoopContinueGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpBranchConditional %undef %body2 %continue +%body2 = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeSwitchBreakGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %int_0 %merge 1 %b1 +%b1 = OpLabel +OpBranchConditional %undef %merge %b2 +%b2 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeSwitchFallThroughGood) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpSwitch %int_0 %b1 1 %b2 +%b1 = OpLabel +OpBranchConditional %undef %b3 %b2 +%b2 = OpLabel +OpBranch %merge +%b3 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, MissingMergeInALoopBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpBranchConditional %undef %b1 %b2 +%b1 = OpLabel +OpBranch %exit +%b2 = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, MissingMergeCrissCrossBad) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpSelectionMerge %merge None +OpBranchConditional %undef %b1 %b2 +%b1 = OpLabel +OpBranchConditional %undef %b3 %b4 +%b2 = OpLabel +OpBranchConditional %undef %b3 %b4 +%b3 = OpLabel +OpBranch %merge +%b4 = OpLabel +OpBranch %merge +%merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Selection must be structured")); +} + +TEST_F(ValidateCFG, ContinueCannotBeSelectionMergeTarget) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %loop "loop" +OpName %continue "continue" +OpName %body "body" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranch %body +%body = OpLabel +OpSelectionMerge %continue None +OpBranchConditional %undef %exit %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Header block 3[%body] is contained in the loop construct headed by " + "1[%loop], but its merge block 2[%continue] is not")); +} + +TEST_F(ValidateCFG, ContinueCannotBeLoopMergeTarget) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpName %loop "loop" +OpName %continue "continue" +OpName %inner "inner" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%bool = OpTypeBool +%undef = OpUndef %bool +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpBranch %loop +%loop = OpLabel +OpLoopMerge %exit %continue None +OpBranchConditional %undef %exit %inner +%inner = OpLabel +OpLoopMerge %continue %inner None +OpBranchConditional %undef %inner %continue +%continue = OpLabel +OpBranch %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Header block 3[%inner] is contained in the loop construct headed by " + "1[%loop], but its merge block 2[%continue] is not")); +} + +TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%4 = OpUndef %int +%bool = OpTypeBool +%6 = OpUndef %bool +%7 = OpFunction %void None %2 +%8 = OpLabel +OpSelectionMerge %9 None +OpSwitch %4 %10 0 %11 +%10 = OpLabel +OpBranch %9 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +OpLoopMerge %13 %14 None +OpBranch %15 +%15 = OpLabel +OpSelectionMerge %16 None +OpSwitch %4 %17 1 %18 2 %19 +%17 = OpLabel +OpBranch %16 +%18 = OpLabel +OpBranch %14 +%19 = OpLabel +OpBranch %16 +%16 = OpLabel +OpBranch %14 +%14 = OpLabel +OpBranchConditional %6 %12 %13 +%13 = OpLabel +OpSelectionMerge %20 None +OpBranchConditional %6 %21 %20 +%21 = OpLabel +OpBranch %9 +%20 = OpLabel +OpBranch %10 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, ExitFromConstructWhoseHeaderIsAMerge2) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %int = OpTypeInt 32 1 + %6 = OpUndef %int + %bool = OpTypeBool + %8 = OpUndef %bool + %2 = OpFunction %void None %4 + %9 = OpLabel + OpSelectionMerge %10 None + OpSwitch %6 %11 0 %12 + %11 = OpLabel + OpBranch %10 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + OpSelectionMerge %17 None + OpSwitch %6 %18 1 %19 2 %20 + %18 = OpLabel + OpBranch %17 + %19 = OpLabel + OpBranch %15 + %20 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %15 + %15 = OpLabel + OpBranchConditional %8 %13 %14 + %14 = OpLabel + OpSelectionMerge %21 None + OpBranchConditional %8 %22 %21 + %22 = OpLabel + OpSelectionMerge %23 None + OpBranchConditional %8 %24 %23 + %24 = OpLabel + OpBranch %10 + %23 = OpLabel + OpBranch %21 + %21 = OpLabel + OpBranch %11 + %10 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, PhiResultInvalidSampler) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%undef_bool = OpUndef %bool +%undef_sampler = OpUndef %sampler +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_sampler = OpLoad %sampler %sampler_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type cannot be OpTypeSampler")); +} + +TEST_F(ValidateCFG, PhiResultInvalidImage) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_uc_image = OpTypePointer UniformConstant %image +%image_var = OpVariable %ptr_uc_image UniformConstant +%undef_bool = OpUndef %bool +%undef_image = OpUndef %image +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %image %undef_image %entry %ld_image %loop +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type cannot be OpTypeImage")); +} + +TEST_F(ValidateCFG, PhiResultInvalidSampledImage) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_uc_image = OpTypePointer UniformConstant %image +%image_var = OpVariable %ptr_uc_image UniformConstant +%sampled_image = OpTypeSampledImage %image +%undef_bool = OpUndef %bool +%undef_sampled_image = OpUndef %sampled_image +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +%ld_sampler = OpLoad %sampler %sampler_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop +%sample = OpSampledImage %sampled_image %ld_image %ld_sampler +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type cannot be OpTypeSampledImage")); +} + +TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampler) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%undef_bool = OpUndef %bool +%undef_sampler = OpUndef %sampler +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_sampler = OpLoad %sampler %sampler_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %sampler %undef_sampler %entry %ld_sampler %loop +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->before_hlsl_legalization = true; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, PhiResultValidPreLegalizationImage) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_uc_image = OpTypePointer UniformConstant %image +%image_var = OpVariable %ptr_uc_image UniformConstant +%undef_bool = OpUndef %bool +%undef_image = OpUndef %image +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %image %undef_image %entry %ld_image %loop +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->before_hlsl_legalization = true; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, PhiResultValidPreLegalizationSampledImage) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%sampler = OpTypeSampler +%ptr_uc_sampler = OpTypePointer UniformConstant %sampler +%sampler_var = OpVariable %ptr_uc_sampler UniformConstant +%image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f +%ptr_uc_image = OpTypePointer UniformConstant %image +%image_var = OpVariable %ptr_uc_image UniformConstant +%sampled_image = OpTypeSampledImage %image +%undef_bool = OpUndef %bool +%undef_sampled_image = OpUndef %sampled_image +%void_fn = OpTypeFunction %void +%fn = OpFunction %void None %void_fn +%entry = OpLabel +%ld_image = OpLoad %image %image_var +%ld_sampler = OpLoad %sampler %sampler_var +OpBranch %loop +%loop = OpLabel +%phi = OpPhi %sampled_image %undef_sampled_image %entry %sample %loop +%sample = OpSampledImage %sampled_image %ld_image %ld_sampler +OpLoopMerge %exit %loop None +OpBranchConditional %undef_bool %exit %loop +%exit = OpLabel +OpReturn +OpFunctionEnd +)"; + + options_->before_hlsl_legalization = true; + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateCFG, UnreachableIsStaticallyReachable) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpBranch %5 +%5 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + + auto f = vstate_->function(3); + auto entry = f->GetBlock(4).first; + ASSERT_TRUE(entry->reachable()); + auto end = f->GetBlock(5).first; + ASSERT_TRUE(end->reachable()); +} + +TEST_F(ValidateCFG, BlockOrderDoesNotAffectReachability) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeBool +%4 = OpUndef %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +OpBranch %7 +%7 = OpLabel +OpSelectionMerge %8 None +OpBranchConditional %4 %9 %10 +%8 = OpLabel +OpReturn +%9 = OpLabel +OpBranch %8 +%10 = OpLabel +OpBranch %8 +%11 = OpLabel +OpUnreachable +OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + + auto f = vstate_->function(5); + auto b6 = f->GetBlock(6).first; + auto b7 = f->GetBlock(7).first; + auto b8 = f->GetBlock(8).first; + auto b9 = f->GetBlock(9).first; + auto b10 = f->GetBlock(10).first; + auto b11 = f->GetBlock(11).first; + + ASSERT_TRUE(b6->reachable()); + ASSERT_TRUE(b7->reachable()); + ASSERT_TRUE(b8->reachable()); + ASSERT_TRUE(b9->reachable()); + ASSERT_TRUE(b10->reachable()); + ASSERT_FALSE(b11->reachable()); +} + +TEST_F(ValidateCFG, PhiInstructionWithDuplicateIncomingEdges) { + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + %11 = OpPhi %6 %7 %8 %7 %8 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(text); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi references incoming basic block ")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("multiple times.")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_code_generator.cpp b/third_party/spirv-tools/test/val/val_code_generator.cpp new file mode 100644 index 0000000..62aae9c --- /dev/null +++ b/third_party/spirv-tools/test/val/val_code_generator.cpp @@ -0,0 +1,224 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/val/val_code_generator.h" + +#include + +namespace spvtools { +namespace val { +namespace { + +std::string GetDefaultShaderCapabilities() { + return R"( +OpCapability Shader +OpCapability Geometry +OpCapability Tessellation +OpCapability Float64 +OpCapability Int64 +OpCapability MultiViewport +OpCapability SampleRateShading +)"; +} + +std::string GetWebGPUShaderCapabilities() { + return R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +)"; +} + +std::string GetDefaultShaderTypes() { + return R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%i32 = OpTypeInt 32 1 +%i64 = OpTypeInt 64 1 +%u32 = OpTypeInt 32 0 +%u64 = OpTypeInt 64 0 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f64vec2 = OpTypeVector %f64 2 +%f64vec3 = OpTypeVector %f64 3 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%u32vec4 = OpTypeVector %u32 4 +%u64vec2 = OpTypeVector %u64 2 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 + +%u32arr2 = OpTypeArray %u32 %u32_2 +%u32arr3 = OpTypeArray %u32 %u32_3 +%u32arr4 = OpTypeArray %u32 %u32_4 +%u64arr2 = OpTypeArray %u64 %u32_2 +%u64arr3 = OpTypeArray %u64 %u32_3 +%u64arr4 = OpTypeArray %u64 %u32_4 +%f32arr2 = OpTypeArray %f32 %u32_2 +%f32arr3 = OpTypeArray %f32 %u32_3 +%f32arr4 = OpTypeArray %f32 %u32_4 +%f64arr2 = OpTypeArray %f64 %u32_2 +%f64arr3 = OpTypeArray %f64 %u32_3 +%f64arr4 = OpTypeArray %f64 %u32_4 + +%f32vec3arr3 = OpTypeArray %f32vec3 %u32_3 +%f32vec4arr3 = OpTypeArray %f32vec4 %u32_3 +%f64vec4arr3 = OpTypeArray %f64vec4 %u32_3 +)"; +} + +std::string GetWebGPUShaderTypes() { + return R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%u32vec4 = OpTypeVector %u32 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%u32arr2 = OpTypeArray %u32 %u32_2 +%u32arr3 = OpTypeArray %u32 %u32_3 +%u32arr4 = OpTypeArray %u32 %u32_4 +%f32arr2 = OpTypeArray %f32 %u32_2 +%f32arr3 = OpTypeArray %f32 %u32_3 +%f32arr4 = OpTypeArray %f32 %u32_4 + +%f32vec3arr3 = OpTypeArray %f32vec3 %u32_3 +%f32vec4arr3 = OpTypeArray %f32vec4 %u32_3 +)"; +} + +} // namespace + +CodeGenerator CodeGenerator::GetDefaultShaderCodeGenerator() { + CodeGenerator generator; + generator.capabilities_ = GetDefaultShaderCapabilities(); + generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; + generator.types_ = GetDefaultShaderTypes(); + return generator; +} + +CodeGenerator CodeGenerator::GetWebGPUShaderCodeGenerator() { + CodeGenerator generator; + generator.capabilities_ = GetWebGPUShaderCapabilities(); + generator.memory_model_ = "OpMemoryModel Logical VulkanKHR\n"; + generator.extensions_ = "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"; + generator.types_ = GetWebGPUShaderTypes(); + return generator; +} + +std::string CodeGenerator::Build() const { + std::ostringstream ss; + + ss << capabilities_; + ss << extensions_; + ss << memory_model_; + + for (const EntryPoint& entry_point : entry_points_) { + ss << "OpEntryPoint " << entry_point.execution_model << " %" + << entry_point.name << " \"" << entry_point.name << "\" " + << entry_point.interfaces << "\n"; + } + + for (const EntryPoint& entry_point : entry_points_) { + ss << entry_point.execution_modes << "\n"; + } + + ss << before_types_; + ss << types_; + ss << after_types_; + + for (const EntryPoint& entry_point : entry_points_) { + ss << "\n"; + ss << "%" << entry_point.name << " = OpFunction %void None %func\n"; + ss << "%" << entry_point.name << "_entry = OpLabel\n"; + ss << entry_point.body; + ss << "\nOpReturn\nOpFunctionEnd\n"; + } + + ss << add_at_the_end_; + + return ss.str(); +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_code_generator.h b/third_party/spirv-tools/test/val/val_code_generator.h new file mode 100644 index 0000000..e580ddf --- /dev/null +++ b/third_party/spirv-tools/test/val/val_code_generator.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Utility class used to generate SPIR-V code strings for tests + +#include +#include + +namespace spvtools { +namespace val { + +struct EntryPoint { + std::string name; + std::string execution_model; + std::string execution_modes; + std::string body; + std::string interfaces; +}; + +class CodeGenerator { + public: + static CodeGenerator GetDefaultShaderCodeGenerator(); + static CodeGenerator GetWebGPUShaderCodeGenerator(); + + std::string Build() const; + + std::vector entry_points_; + std::string capabilities_; + std::string extensions_; + std::string memory_model_; + std::string before_types_; + std::string types_; + std::string after_types_; + std::string add_at_the_end_; +}; + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_composites_test.cpp b/third_party/spirv-tools/test/val/val_composites_test.cpp new file mode 100644 index 0000000..e970562 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_composites_test.cpp @@ -0,0 +1,1999 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; +using ::testing::Values; + +using ValidateComposites = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Fragment") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability Float64 +)"; + + ss << capabilities_and_extensions; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f64vec2 = OpTypeVector %f64 2 +%u32vec2 = OpTypeVector %u32 2 +%u32vec4 = OpTypeVector %u32 4 +%f64mat22 = OpTypeMatrix %f64vec2 2 +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 + +%f32vec2arr3 = OpTypeArray %f32vec2 %u32_3 +%f32vec2rarr = OpTypeRuntimeArray %f32vec2 + +%f32u32struct = OpTypeStruct %f32 %u32 +%big_struct = OpTypeStruct %f32 %f32vec4 %f32mat23 %f32vec2arr3 %f32vec2rarr %f32u32struct + +%ptr_big_struct = OpTypePointer Uniform %big_struct +%var_big_struct = OpVariable %ptr_big_struct Uniform + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +// Returns header for legacy tests taken from val_id_test.cpp. +std::string GetHeaderForTestsFromValId() { + return R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpCapability Pipes +OpCapability LiteralSampler +OpCapability DeviceEnqueue +OpCapability Vector16 +OpCapability Int8 +OpCapability Int16 +OpCapability Int64 +OpCapability Float64 +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%int = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%mat4x3 = OpTypeMatrix %v3float 4 +%_ptr_Private_mat4x3 = OpTypePointer Private %mat4x3 +%_ptr_Private_float = OpTypePointer Private %float +%my_matrix = OpVariable %_ptr_Private_mat4x3 Private +%my_float_var = OpVariable %_ptr_Private_float Private +%_ptr_Function_float = OpTypePointer Function %float +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_3 = OpConstant %int 3 +%int_5 = OpConstant %int 5 + +; Making the following nested structures. +; +; struct S { +; bool b; +; vec4 v[5]; +; int i; +; mat4x3 m[5]; +; } +; uniform blockName { +; S s; +; bool cond; +; RunTimeArray arr; +; } + +%f32arr = OpTypeRuntimeArray %float +%v4float = OpTypeVector %float 4 +%array5_mat4x3 = OpTypeArray %mat4x3 %int_5 +%array5_vec4 = OpTypeArray %v4float %int_5 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Function_vec4 = OpTypePointer Function %v4float +%_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float +%struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3 +%struct_blockName = OpTypeStruct %struct_s %int %f32arr +%_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName +%_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s +%_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3 +%_ptr_Uniform_mat4x3 = OpTypePointer Uniform %mat4x3 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%blockName_var = OpVariable %_ptr_Uniform_blockName Uniform +%spec_int = OpSpecConstant %int 2 +%func = OpFunction %void None %void_f +%my_label = OpLabel +)"; +} + +TEST_F(ValidateComposites, VectorExtractDynamicSuccess) { + const std::string body = R"( +%val1 = OpVectorExtractDynamic %f32 %f32vec4_0123 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, VectorExtractDynamicWrongResultType) { + const std::string body = R"( +%val1 = OpVectorExtractDynamic %f32vec4 %f32vec4_0123 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a scalar type")); +} + +TEST_F(ValidateComposites, VectorExtractDynamicNotVector) { + const std::string body = R"( +%val1 = OpVectorExtractDynamic %f32 %f32mat22_1212 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Vector type to be OpTypeVector")); +} + +TEST_F(ValidateComposites, VectorExtractDynamicWrongVectorComponent) { + const std::string body = R"( +%val1 = OpVectorExtractDynamic %f32 %u32vec4_0123 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Vector component type to be equal to Result Type")); +} + +TEST_F(ValidateComposites, VectorExtractDynamicWrongIndexType) { + const std::string body = R"( +%val1 = OpVectorExtractDynamic %f32 %f32vec4_0123 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Index to be int scalar")); +} + +TEST_F(ValidateComposites, VectorInsertDynamicSuccess) { + const std::string body = R"( +%val1 = OpVectorInsertDynamic %f32vec4 %f32vec4_0123 %f32_1 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, VectorInsertDynamicWrongResultType) { + const std::string body = R"( +%val1 = OpVectorInsertDynamic %f32 %f32vec4_0123 %f32_1 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypeVector")); +} + +TEST_F(ValidateComposites, VectorInsertDynamicNotVector) { + const std::string body = R"( +%val1 = OpVectorInsertDynamic %f32vec4 %f32mat22_1212 %f32_1 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Vector type to be equal to Result Type")); +} + +TEST_F(ValidateComposites, VectorInsertDynamicWrongComponentType) { + const std::string body = R"( +%val1 = OpVectorInsertDynamic %f32vec4 %f32vec4_0123 %u32_1 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Component type to be equal to Result Type " + "component type")); +} + +TEST_F(ValidateComposites, VectorInsertDynamicWrongIndexType) { + const std::string body = R"( +%val1 = OpVectorInsertDynamic %f32vec4 %f32vec4_0123 %f32_1 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Index to be int scalar")); +} + +TEST_F(ValidateComposites, CompositeConstructNotComposite) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a composite type")); +} + +TEST_F(ValidateComposites, CompositeConstructVectorSuccess) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec4 %f32vec2_12 %f32vec2_12 +%val2 = OpCompositeConstruct %f32vec4 %f32vec2_12 %f32_0 %f32_0 +%val3 = OpCompositeConstruct %f32vec4 %f32_0 %f32_0 %f32vec2_12 +%val4 = OpCompositeConstruct %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructVectorOnlyOneConstituent) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec4 %f32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected number of constituents to be at least 2")); +} + +TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituent1) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec4 %f32 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 5[%float] cannot be a " + "type")); +} + +TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituent2) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec4 %f32vec2_12 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Constituents to be scalars or vectors of the same " + "type as Result Type components")); +} + +TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituent3) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec4 %f32vec2_12 %u32_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Constituents to be scalars or vectors of the same " + "type as Result Type components")); +} + +TEST_F(ValidateComposites, CompositeConstructVectorWrongComponentNumber1) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec4 %f32vec2_12 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected total number of given components to be equal to the " + "size of Result Type vector")); +} + +TEST_F(ValidateComposites, CompositeConstructVectorWrongComponentNumber2) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec4 %f32vec2_12 %f32vec2_12 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected total number of given components to be equal to the " + "size of Result Type vector")); +} + +TEST_F(ValidateComposites, CompositeConstructMatrixSuccess) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32mat22 %f32vec2_12 %f32vec2_12 +%val2 = OpCompositeConstruct %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituentNumber1) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32mat22 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected total number of Constituents to be equal to the " + "number of columns of Result Type matrix")); +} + +TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituentNumber2) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32mat22 %f32vec2_12 %f32vec2_12 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected total number of Constituents to be equal to the " + "number of columns of Result Type matrix")); +} + +TEST_F(ValidateComposites, CompositeConstructVectorWrongConsituent) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32mat22 %f32vec2_12 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Constituent type to be equal to the column type " + "Result Type matrix")); +} + +TEST_F(ValidateComposites, CompositeConstructArraySuccess) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec2arr3 %f32vec2_12 %f32vec2_12 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructArrayWrongConsituentNumber1) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec2arr3 %f32vec2_12 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected total number of Constituents to be equal to the " + "number of elements of Result Type array")); +} + +TEST_F(ValidateComposites, CompositeConstructArrayWrongConsituentNumber2) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec2arr3 %f32vec2_12 %f32vec2_12 %f32vec2_12 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected total number of Constituents to be equal to the " + "number of elements of Result Type array")); +} + +TEST_F(ValidateComposites, CompositeConstructArrayWrongConsituent) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32vec2arr3 %f32vec2_12 %u32vec2_01 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Constituent type to be equal to the column type " + "Result Type array")); +} + +TEST_F(ValidateComposites, CompositeConstructStructSuccess) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32u32struct %f32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeConstructStructWrongConstituentNumber1) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32u32struct %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected total number of Constituents to be equal to the " + "number of members of Result Type struct")); +} + +TEST_F(ValidateComposites, CompositeConstructStructWrongConstituentNumber2) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32u32struct %f32_0 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected total number of Constituents to be equal to the " + "number of members of Result Type struct")); +} + +TEST_F(ValidateComposites, CompositeConstructStructWrongConstituent) { + const std::string body = R"( +%val1 = OpCompositeConstruct %f32u32struct %f32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Constituent type to be equal to the " + "corresponding member type of Result Type struct")); +} + +TEST_F(ValidateComposites, CopyObjectSuccess) { + const std::string body = R"( +%val1 = OpCopyObject %f32 %f32_0 +%val2 = OpCopyObject %f32vec4 %f32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CopyObjectResultTypeNotType) { + const std::string body = R"( +%val1 = OpCopyObject %f32_0 %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 19[%float_0] is not a type id")); +} + +TEST_F(ValidateComposites, CopyObjectWrongOperandType) { + const std::string body = R"( +%val1 = OpCopyObject %f32 %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type and Operand type to be the same")); +} + +TEST_F(ValidateComposites, TransposeSuccess) { + const std::string body = R"( +%val1 = OpTranspose %f32mat32 %f32mat23_121212 +%val2 = OpTranspose %f32mat22 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, TransposeResultTypeNotMatrix) { + const std::string body = R"( +%val1 = OpTranspose %f32vec4 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a matrix type")); +} + +TEST_F(ValidateComposites, TransposeDifferentComponentTypes) { + const std::string body = R"( +%val1 = OpTranspose %f64mat22 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected component types of Matrix and Result Type to be " + "identical")); +} + +TEST_F(ValidateComposites, TransposeIncompatibleDimensions1) { + const std::string body = R"( +%val1 = OpTranspose %f32mat23 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected number of columns and the column size " + "of Matrix to be the reverse of those of Result Type")); +} + +TEST_F(ValidateComposites, TransposeIncompatibleDimensions2) { + const std::string body = R"( +%val1 = OpTranspose %f32mat32 %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected number of columns and the column size " + "of Matrix to be the reverse of those of Result Type")); +} + +TEST_F(ValidateComposites, TransposeIncompatibleDimensions3) { + const std::string body = R"( +%val1 = OpTranspose %f32mat23 %f32mat23_121212 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected number of columns and the column size " + "of Matrix to be the reverse of those of Result Type")); +} + +TEST_F(ValidateComposites, CompositeExtractSuccess) { + const std::string body = R"( +%val1 = OpCompositeExtract %f32 %f32vec4_0123 1 +%val2 = OpCompositeExtract %u32 %u32vec4_0123 0 +%val3 = OpCompositeExtract %f32 %f32mat22_1212 0 1 +%val4 = OpCompositeExtract %f32vec2 %f32mat22_1212 0 +%array = OpCompositeConstruct %f32vec2arr3 %f32vec2_12 %f32vec2_12 %f32vec2_12 +%val5 = OpCompositeExtract %f32vec2 %array 2 +%val6 = OpCompositeExtract %f32 %array 2 1 +%struct = OpLoad %big_struct %var_big_struct +%val7 = OpCompositeExtract %f32 %struct 0 +%val8 = OpCompositeExtract %f32vec4 %struct 1 +%val9 = OpCompositeExtract %f32 %struct 1 2 +%val10 = OpCompositeExtract %f32mat23 %struct 2 +%val11 = OpCompositeExtract %f32vec2 %struct 2 2 +%val12 = OpCompositeExtract %f32 %struct 2 2 1 +%val13 = OpCompositeExtract %f32vec2 %struct 3 2 +%val14 = OpCompositeExtract %f32 %struct 3 2 1 +%val15 = OpCompositeExtract %f32vec2 %struct 4 100 +%val16 = OpCompositeExtract %f32 %struct 4 1000 1 +%val17 = OpCompositeExtract %f32 %struct 5 0 +%val18 = OpCompositeExtract %u32 %struct 5 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeExtractNotObject) { + const std::string body = R"( +%val1 = OpCompositeExtract %f32 %f32vec4 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 11[%v4float] cannot " + "be a type")); +} + +TEST_F(ValidateComposites, CompositeExtractNotComposite) { + const std::string body = R"( +%val1 = OpCompositeExtract %f32 %f32_1 0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reached non-composite type while indexes still remain " + "to be traversed.")); +} + +TEST_F(ValidateComposites, CompositeExtractVectorOutOfBounds) { + const std::string body = R"( +%val1 = OpCompositeExtract %f32 %f32vec4_0123 4 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector access is out of bounds, " + "vector size is 4, but access index is 4")); +} + +TEST_F(ValidateComposites, CompositeExtractMatrixOutOfCols) { + const std::string body = R"( +%val1 = OpCompositeExtract %f32 %f32mat23_121212 3 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Matrix access is out of bounds, " + "matrix has 3 columns, but access index is 3")); +} + +TEST_F(ValidateComposites, CompositeExtractMatrixOutOfRows) { + const std::string body = R"( +%val1 = OpCompositeExtract %f32 %f32mat23_121212 2 5 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector access is out of bounds, " + "vector size is 2, but access index is 5")); +} + +TEST_F(ValidateComposites, CompositeExtractArrayOutOfBounds) { + const std::string body = R"( +%array = OpCompositeConstruct %f32vec2arr3 %f32vec2_12 %f32vec2_12 %f32vec2_12 +%val1 = OpCompositeExtract %f32vec2 %array 3 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Array access is out of bounds, " + "array size is 3, but access index is 3")); +} + +TEST_F(ValidateComposites, CompositeExtractStructOutOfBounds) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %f32 %struct 6 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index is out of bounds, can not find index 6 in the " + "structure '37'. This structure has 6 members. " + "Largest valid index is 5.")); +} + +TEST_F(ValidateComposites, CompositeExtractNestedVectorOutOfBounds) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %f32 %struct 3 1 5 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector access is out of bounds, " + "vector size is 2, but access index is 5")); +} + +TEST_F(ValidateComposites, CompositeExtractTooManyIndices) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %f32 %struct 3 1 1 2 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reached non-composite type while " + "indexes still remain to be traversed.")); +} + +TEST_F(ValidateComposites, CompositeExtractNoIndices) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %big_struct %struct +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected at least one index to OpCompositeExtract")); +} + +TEST_F(ValidateComposites, CompositeExtractWrongType1) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %f32vec2 %struct 3 1 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Result type (OpTypeVector) does not match the type that results " + "from indexing into the composite (OpTypeFloat).")); +} + +TEST_F(ValidateComposites, CompositeExtractWrongType2) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %f32 %struct 3 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type (OpTypeFloat) does not match the type " + "that results from indexing into the composite " + "(OpTypeVector).")); +} + +TEST_F(ValidateComposites, CompositeExtractWrongType3) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %f32 %struct 2 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type (OpTypeFloat) does not match the type " + "that results from indexing into the composite " + "(OpTypeVector).")); +} + +TEST_F(ValidateComposites, CompositeExtractWrongType4) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %f32 %struct 4 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type (OpTypeFloat) does not match the type " + "that results from indexing into the composite " + "(OpTypeVector).")); +} + +TEST_F(ValidateComposites, CompositeExtractWrongType5) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeExtract %f32 %struct 5 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Result type (OpTypeFloat) does not match the " + "type that results from indexing into the composite (OpTypeInt).")); +} + +TEST_F(ValidateComposites, CompositeInsertSuccess) { + const std::string body = R"( +%val1 = OpCompositeInsert %f32vec4 %f32_1 %f32vec4_0123 0 +%val2 = OpCompositeInsert %u32vec4 %u32_1 %u32vec4_0123 0 +%val3 = OpCompositeInsert %f32mat22 %f32_2 %f32mat22_1212 0 1 +%val4 = OpCompositeInsert %f32mat22 %f32vec2_01 %f32mat22_1212 0 +%array = OpCompositeConstruct %f32vec2arr3 %f32vec2_12 %f32vec2_12 %f32vec2_12 +%val5 = OpCompositeInsert %f32vec2arr3 %f32vec2_01 %array 2 +%val6 = OpCompositeInsert %f32vec2arr3 %f32_3 %array 2 1 +%struct = OpLoad %big_struct %var_big_struct +%val7 = OpCompositeInsert %big_struct %f32_3 %struct 0 +%val8 = OpCompositeInsert %big_struct %f32vec4_0123 %struct 1 +%val9 = OpCompositeInsert %big_struct %f32_3 %struct 1 2 +%val10 = OpCompositeInsert %big_struct %f32mat23_121212 %struct 2 +%val11 = OpCompositeInsert %big_struct %f32vec2_01 %struct 2 2 +%val12 = OpCompositeInsert %big_struct %f32_3 %struct 2 2 1 +%val13 = OpCompositeInsert %big_struct %f32vec2_01 %struct 3 2 +%val14 = OpCompositeInsert %big_struct %f32_3 %struct 3 2 1 +%val15 = OpCompositeInsert %big_struct %f32vec2_01 %struct 4 100 +%val16 = OpCompositeInsert %big_struct %f32_3 %struct 4 1000 1 +%val17 = OpCompositeInsert %big_struct %f32_3 %struct 5 0 +%val18 = OpCompositeInsert %big_struct %u32_3 %struct 5 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CompositeInsertResultTypeDifferentFromComposite) { + const std::string body = R"( +%val1 = OpCompositeInsert %f32 %f32_1 %f32vec4_0123 0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Result Type must be the same as Composite type in " + "OpCompositeInsert yielding Result Id 5.")); +} + +TEST_F(ValidateComposites, CompositeInsertNotComposite) { + const std::string body = R"( +%val1 = OpCompositeInsert %f32 %f32_1 %f32_0 0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reached non-composite type while indexes still remain " + "to be traversed.")); +} + +TEST_F(ValidateComposites, CompositeInsertVectorOutOfBounds) { + const std::string body = R"( +%val1 = OpCompositeInsert %f32vec4 %f32_1 %f32vec4_0123 4 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector access is out of bounds, " + "vector size is 4, but access index is 4")); +} + +TEST_F(ValidateComposites, CompositeInsertMatrixOutOfCols) { + const std::string body = R"( +%val1 = OpCompositeInsert %f32mat23 %f32_1 %f32mat23_121212 3 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Matrix access is out of bounds, " + "matrix has 3 columns, but access index is 3")); +} + +TEST_F(ValidateComposites, CompositeInsertMatrixOutOfRows) { + const std::string body = R"( +%val1 = OpCompositeInsert %f32mat23 %f32_1 %f32mat23_121212 2 5 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector access is out of bounds, " + "vector size is 2, but access index is 5")); +} + +TEST_F(ValidateComposites, CompositeInsertArrayOutOfBounds) { + const std::string body = R"( +%array = OpCompositeConstruct %f32vec2arr3 %f32vec2_12 %f32vec2_12 %f32vec2_12 +%val1 = OpCompositeInsert %f32vec2arr3 %f32vec2_01 %array 3 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Array access is out of bounds, array " + "size is 3, but access index is 3")); +} + +TEST_F(ValidateComposites, CompositeInsertStructOutOfBounds) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeInsert %big_struct %f32_1 %struct 6 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index is out of bounds, can not find index 6 in the " + "structure '37'. This structure has 6 members. " + "Largest valid index is 5.")); +} + +TEST_F(ValidateComposites, CompositeInsertNestedVectorOutOfBounds) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeInsert %big_struct %f32_1 %struct 3 1 5 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Vector access is out of bounds, " + "vector size is 2, but access index is 5")); +} + +TEST_F(ValidateComposites, CompositeInsertTooManyIndices) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeInsert %big_struct %f32_1 %struct 3 1 1 2 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reached non-composite type while indexes still remain " + "to be traversed.")); +} + +TEST_F(ValidateComposites, CompositeInsertWrongType1) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeInsert %big_struct %f32vec2_01 %struct 3 1 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Object type (OpTypeVector) does not match the " + "type that results from indexing into the Composite " + "(OpTypeFloat).")); +} + +TEST_F(ValidateComposites, CompositeInsertWrongType2) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeInsert %big_struct %f32_1 %struct 3 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Object type (OpTypeFloat) does not match the type " + "that results from indexing into the Composite " + "(OpTypeVector).")); +} + +TEST_F(ValidateComposites, CompositeInsertWrongType3) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeInsert %big_struct %f32_1 %struct 2 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Object type (OpTypeFloat) does not match the type " + "that results from indexing into the Composite " + "(OpTypeVector).")); +} + +TEST_F(ValidateComposites, CompositeInsertWrongType4) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeInsert %big_struct %f32_1 %struct 4 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Object type (OpTypeFloat) does not match the type " + "that results from indexing into the Composite " + "(OpTypeVector).")); +} + +TEST_F(ValidateComposites, CompositeInsertWrongType5) { + const std::string body = R"( +%struct = OpLoad %big_struct %var_big_struct +%val1 = OpCompositeInsert %big_struct %f32_1 %struct 5 1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Object type (OpTypeFloat) does not match the type " + "that results from indexing into the Composite " + "(OpTypeInt).")); +} + +// Tests ported from val_id_test.cpp. + +// Valid. Tests both CompositeExtract and CompositeInsert with 255 indexes. +TEST_F(ValidateComposites, CompositeExtractInsertLimitsGood) { + int depth = 255; + std::string header = GetHeaderForTestsFromValId(); + header.erase(header.find("%func")); + std::ostringstream spirv; + spirv << header << std::endl; + + // Build nested structures. Struct 'i' contains struct 'i-1' + spirv << "%s_depth_1 = OpTypeStruct %float\n"; + for (int i = 2; i <= depth; ++i) { + spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n"; + } + + // Define Pointer and Variable to use for CompositeExtract/Insert. + spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_" + << depth << "\n"; + spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n"; + + // Function Start + spirv << R"( + %func = OpFunction %void None %void_f + %my_label = OpLabel + )"; + + // OpCompositeExtract/Insert with 'n' indexes (n = depth) + spirv << "%deep = OpLoad %s_depth_" << depth << " %deep_var" << std::endl; + spirv << "%entry = OpCompositeExtract %float %deep"; + for (int i = 0; i < depth; ++i) { + spirv << " 0"; + } + spirv << std::endl; + spirv << "%new_composite = OpCompositeInsert %s_depth_" << depth + << " %entry %deep"; + for (int i = 0; i < depth; ++i) { + spirv << " 0"; + } + spirv << std::endl; + + // Function end + spirv << R"( + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: 256 indexes passed to OpCompositeExtract. Limit is 255. +TEST_F(ValidateComposites, CompositeExtractArgCountExceededLimitBad) { + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << std::endl; + spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%entry = OpCompositeExtract %float %matrix"; + for (int i = 0; i < 256; ++i) { + spirv << " 0"; + } + spirv << R"( + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The number of indexes in OpCompositeExtract may not " + "exceed 255. Found 256 indexes.")); +} + +// Invalid: 256 indexes passed to OpCompositeInsert. Limit is 255. +TEST_F(ValidateComposites, CompositeInsertArgCountExceededLimitBad) { + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << std::endl; + spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%new_composite = OpCompositeInsert %mat4x3 %int_0 %matrix"; + for (int i = 0; i < 256; ++i) { + spirv << " 0"; + } + spirv << R"( + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The number of indexes in OpCompositeInsert may not " + "exceed 255. Found 256 indexes.")); +} + +// Invalid: In OpCompositeInsert, result type must be the same as composite type +TEST_F(ValidateComposites, CompositeInsertWrongResultTypeBad) { + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << std::endl; + spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%float_entry = OpCompositeExtract %float %matrix 0 1" << std::endl; + spirv << "%new_composite = OpCompositeInsert %float %float_entry %matrix 0 1" + << std::endl; + spirv << R"(OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Result Type must be the same as Composite type")); +} + +// Invalid: No Indexes were passed to OpCompositeExtract. +TEST_F(ValidateComposites, CompositeExtractNoIndices2) { + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << std::endl; + spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%float_entry = OpCompositeExtract %mat4x3 %matrix" << std::endl; + spirv << R"(OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected at least one index to OpCompositeExtract, zero found")); +} + +// Invalid: No Indexes were passed to OpCompositeExtract. +TEST_F(ValidateComposites, CompositeExtractNoIndicesWrongResultType) { + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << std::endl; + spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%float_entry = OpCompositeExtract %float %matrix" << std::endl; + spirv << R"(OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected at least one index to OpCompositeExtract, zero found")); +} + +// Invalid: No Indices were passed to OpCompositeInsert, and the type of the +// Object argument matches the Composite type. +TEST_F(ValidateComposites, CompositeInsertMissingIndices) { + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << std::endl; + spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%matrix_2 = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%new_composite = OpCompositeInsert %mat4x3 %matrix_2 %matrix"; + spirv << R"( + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected at least one index to OpCompositeInsert, zero found")); +} + +// Invalid: No Indices were passed to OpCompositeInsert, but the type of the +// Object argument does not match the Composite type. +TEST_F(ValidateComposites, CompositeInsertMissingIndices2) { + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << std::endl; + spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%new_composite = OpCompositeInsert %mat4x3 %int_0 %matrix"; + spirv << R"( + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected at least one index to OpCompositeInsert, zero found")); +} + +// Valid: Tests that we can index into Struct, Array, Matrix, and Vector! +TEST_F(ValidateComposites, CompositeExtractInsertIndexIntoAllTypesGood) { + // indexes that we are passing are: 0, 3, 1, 2, 0 + // 0 will select the struct_s within the base struct (blockName) + // 3 will select the Array that contains 5 matrices + // 1 will select the Matrix that is at index 1 of the array + // 2 will select the column (which is a vector) within the matrix at index 2 + // 0 will select the element at the index 0 of the vector. (which is a float). + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << R"( + %myblock = OpLoad %struct_blockName %blockName_var + %ss = OpCompositeExtract %struct_s %myblock 0 + %sa = OpCompositeExtract %array5_mat4x3 %myblock 0 3 + %sm = OpCompositeExtract %mat4x3 %myblock 0 3 1 + %sc = OpCompositeExtract %v3float %myblock 0 3 1 2 + %fl = OpCompositeExtract %float %myblock 0 3 1 2 0 + ; + ; Now let's insert back at different levels... + ; + %b1 = OpCompositeInsert %struct_blockName %ss %myblock 0 + %b2 = OpCompositeInsert %struct_blockName %sa %myblock 0 3 + %b3 = OpCompositeInsert %struct_blockName %sm %myblock 0 3 1 + %b4 = OpCompositeInsert %struct_blockName %sc %myblock 0 3 1 2 + %b5 = OpCompositeInsert %struct_blockName %fl %myblock 0 3 1 2 0 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid. More indexes are provided than needed for OpCompositeExtract. +TEST_F(ValidateComposites, CompositeExtractReachedScalarBad) { + // indexes that we are passing are: 0, 3, 1, 2, 0 + // 0 will select the struct_s within the base struct (blockName) + // 3 will select the Array that contains 5 matrices + // 1 will select the Matrix that is at index 1 of the array + // 2 will select the column (which is a vector) within the matrix at index 2 + // 0 will select the element at the index 0 of the vector. (which is a float). + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << R"( + %myblock = OpLoad %struct_blockName %blockName_var + %fl = OpCompositeExtract %float %myblock 0 3 1 2 0 1 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reached non-composite type while indexes still remain " + "to be traversed.")); +} + +// Invalid. More indexes are provided than needed for OpCompositeInsert. +TEST_F(ValidateComposites, CompositeInsertReachedScalarBad) { + // indexes that we are passing are: 0, 3, 1, 2, 0 + // 0 will select the struct_s within the base struct (blockName) + // 3 will select the Array that contains 5 matrices + // 1 will select the Matrix that is at index 1 of the array + // 2 will select the column (which is a vector) within the matrix at index 2 + // 0 will select the element at the index 0 of the vector. (which is a float). + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << R"( + %myblock = OpLoad %struct_blockName %blockName_var + %fl = OpCompositeExtract %float %myblock 0 3 1 2 0 + %b5 = OpCompositeInsert %struct_blockName %fl %myblock 0 3 1 2 0 1 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Reached non-composite type while indexes still remain " + "to be traversed.")); +} + +// Invalid. Result type doesn't match the type we get from indexing into +// the composite. +TEST_F(ValidateComposites, + CompositeExtractResultTypeDoesntMatchIndexedTypeBad) { + // indexes that we are passing are: 0, 3, 1, 2, 0 + // 0 will select the struct_s within the base struct (blockName) + // 3 will select the Array that contains 5 matrices + // 1 will select the Matrix that is at index 1 of the array + // 2 will select the column (which is a vector) within the matrix at index 2 + // 0 will select the element at the index 0 of the vector. (which is a float). + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << R"( + %myblock = OpLoad %struct_blockName %blockName_var + %fl = OpCompositeExtract %int %myblock 0 3 1 2 0 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type (OpTypeInt) does not match the type that " + "results from indexing into the composite " + "(OpTypeFloat).")); +} + +// Invalid. Given object type doesn't match the type we get from indexing into +// the composite. +TEST_F(ValidateComposites, CompositeInsertObjectTypeDoesntMatchIndexedTypeBad) { + // indexes that we are passing are: 0, 3, 1, 2, 0 + // 0 will select the struct_s within the base struct (blockName) + // 3 will select the Array that contains 5 matrices + // 1 will select the Matrix that is at index 1 of the array + // 2 will select the column (which is a vector) within the matrix at index 2 + // 0 will select the element at the index 0 of the vector. (which is a float). + // We are trying to insert an integer where we should be inserting a float. + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << R"( + %myblock = OpLoad %struct_blockName %blockName_var + %b5 = OpCompositeInsert %struct_blockName %int_0 %myblock 0 3 1 2 0 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Object type (OpTypeInt) does not match the type " + "that results from indexing into the Composite " + "(OpTypeFloat).")); +} + +// Invalid. Index into a struct is larger than the number of struct members. +TEST_F(ValidateComposites, CompositeExtractStructIndexOutOfBoundBad) { + // struct_blockName has 3 members (index 0,1,2). We'll try to access index 3. + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << R"( + %myblock = OpLoad %struct_blockName %blockName_var + %ss = OpCompositeExtract %struct_s %myblock 3 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index is out of bounds, can not find index 3 in the " + "structure '25'. This structure has 3 members. " + "Largest valid index is 2.")); +} + +// Invalid. Index into a struct is larger than the number of struct members. +TEST_F(ValidateComposites, CompositeInsertStructIndexOutOfBoundBad) { + // struct_blockName has 3 members (index 0,1,2). We'll try to access index 3. + std::ostringstream spirv; + spirv << GetHeaderForTestsFromValId() << R"( + %myblock = OpLoad %struct_blockName %blockName_var + %ss = OpCompositeExtract %struct_s %myblock 0 + %new_composite = OpCompositeInsert %struct_blockName %ss %myblock 3 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Index is out of bounds, can not find index 3 in the structure " + " '25'. This structure has 3 members. Largest valid index " + "is 2.")); +} + +// #1403: Ensure that the default spec constant value is not used to check the +// extract index. +TEST_F(ValidateComposites, ExtractFromSpecConstantSizedArray) { + std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpDecorate %spec_const SpecId 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%spec_const = OpSpecConstant %uint 3 +%uint_array = OpTypeArray %uint %spec_const +%undef = OpUndef %uint_array +%voidf = OpTypeFunction %void +%func = OpFunction %void None %voidf +%1 = OpLabel +%2 = OpCompositeExtract %uint %undef 4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// #1403: Ensure that spec constant ops do not produce false positives. +TEST_F(ValidateComposites, ExtractFromSpecConstantOpSizedArray) { + std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpDecorate %spec_const SpecId 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%const = OpConstant %uint 1 +%spec_const = OpSpecConstant %uint 3 +%spec_const_op = OpSpecConstantOp %uint IAdd %spec_const %const +%uint_array = OpTypeArray %uint %spec_const_op +%undef = OpUndef %uint_array +%voidf = OpTypeFunction %void +%func = OpFunction %void None %voidf +%1 = OpLabel +%2 = OpCompositeExtract %uint %undef 4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// #1403: Ensure that the default spec constant value is not used to check the +// size of the array for a composite construct. This code has limited actual +// value as it is incorrect unless the specialization constant is assigned the +// value of 2, but it is still a valid module. +TEST_F(ValidateComposites, CompositeConstructSpecConstantSizedArray) { + std::string spirv = R"( +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Logical OpenCL +OpDecorate %spec_const SpecId 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%spec_const = OpSpecConstant %uint 3 +%uint_array = OpTypeArray %uint %spec_const +%voidf = OpTypeFunction %void +%func = OpFunction %void None %voidf +%1 = OpLabel +%2 = OpCompositeConstruct %uint_array %uint_0 %uint_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateComposites, CoopMatConstantCompositeMismatchFail) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 + +%u32_8 = OpConstant %u32 8 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 + +%f32_1 = OpConstant %f32 1 + +%f16mat_1 = OpConstantComposite %f16mat %f32_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent '11[%float_1]' type does " + "not match the Result Type '10[%10]'s component type.")); +} + +TEST_F(ValidateComposites, CoopMatCompositeConstructMismatchFail) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 + +%u32_8 = OpConstant %u32 8 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 + +%f32_1 = OpConstant %f32 1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%f16mat_1 = OpCompositeConstruct %f16mat %f32_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Constituent type to be equal to the component type")); +} + +TEST_F(ValidateComposites, ExtractDynamicLabelIndex) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%void_fn = OpTypeFunction %void +%float_0 = OpConstant %float 0 +%v4float_0 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%func = OpFunction %void None %void_fn +%1 = OpLabel +%ex = OpVectorExtractDynamic %float %v4float_0 %v4float_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Index to be int scalar")); +} + +TEST_F(ValidateComposites, CopyLogicalSameType) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%struct = OpTypeStruct +%const_struct = OpConstantComposite %struct +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%copy = OpCopyLogical %struct %const_struct +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must not equal the Operand type")); +} + +TEST_F(ValidateComposites, CopyLogicalSameStructDifferentId) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%struct1 = OpTypeStruct +%struct2 = OpTypeStruct +%const_struct = OpConstantComposite %struct1 +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%copy = OpCopyLogical %struct2 %const_struct +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateComposites, CopyLogicalArrayDifferentLength) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%int_5 = OpConstant %int 5 +%array1 = OpTypeArray %int %int_4 +%array2 = OpTypeArray %int %int_5 +%const_array = OpConstantComposite %array1 %int_4 %int_4 %int_4 %int_4 +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%copy = OpCopyLogical %array2 %const_array +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Result Type does not logically match the Operand type")); +} + +TEST_F(ValidateComposites, CopyLogicalArrayDifferentElement) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array1 = OpTypeArray %int %int_4 +%array2 = OpTypeArray %float %int_4 +%const_array = OpConstantComposite %array1 %int_4 %int_4 %int_4 %int_4 +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%copy = OpCopyLogical %array2 %const_array +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Result Type does not logically match the Operand type")); +} + +TEST_F(ValidateComposites, CopyLogicalArrayLogicallyMatchedElement) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%inner1 = OpTypeArray %int %int_1 +%inner2 = OpTypeArray %int %int_1 +%array1 = OpTypeArray %inner1 %int_1 +%array2 = OpTypeArray %inner2 %int_1 +%const_inner = OpConstantComposite %inner1 %int_1 +%const_array = OpConstantComposite %array1 %const_inner +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%copy = OpCopyLogical %array2 %const_array +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateComposites, CopyLogicalStructDifferentNumberElements) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%struct1 = OpTypeStruct +%struct2 = OpTypeStruct %int +%const_struct = OpConstantComposite %struct1 +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%copy = OpCopyLogical %struct2 %const_struct +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Result Type does not logically match the Operand type")); +} + +TEST_F(ValidateComposites, CopyLogicalStructDifferentElement) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%struct1 = OpTypeStruct %int %uint +%struct2 = OpTypeStruct %int %int +%const_struct = OpConstantComposite %struct1 %int_0 %uint_0 +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%copy = OpCopyLogical %struct2 %const_struct +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Result Type does not logically match the Operand type")); +} + +TEST_F(ValidateComposites, CopyLogicalStructLogicallyMatch) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +%array1 = OpTypeArray %int %int_1 +%array2 = OpTypeArray %int %int_1 +%struct1 = OpTypeStruct %int %array1 +%struct2 = OpTypeStruct %int %array2 +%const_array = OpConstantComposite %array1 %int_1 +%const_struct = OpConstantComposite %struct1 %int_1 %const_array +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%copy = OpCopyLogical %struct2 %const_struct +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +using ValidateSmallComposites = spvtest::ValidateBase; + +CodeGenerator GetSmallCompositesCodeGenerator() { + CodeGenerator generator; + generator.capabilities_ = R"( +OpCapability Shader +OpCapability Linkage +OpCapability UniformAndStorageBuffer16BitAccess +OpCapability UniformAndStorageBuffer8BitAccess +)"; + generator.extensions_ = R"( +OpExtension "SPV_KHR_16bit_storage" +OpExtension "SPV_KHR_8bit_storage" +)"; + generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; + generator.before_types_ = R"( +OpDecorate %char_block Block +OpMemberDecorate %char_block 0 Offset 0 +OpDecorate %short_block Block +OpMemberDecorate %short_block 0 Offset 0 +OpDecorate %half_block Block +OpMemberDecorate %half_block 0 Offset 0 +)"; + generator.types_ = R"( +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%char = OpTypeInt 8 0 +%char2 = OpTypeVector %char 2 +%short = OpTypeInt 16 0 +%short2 = OpTypeVector %short 2 +%half = OpTypeFloat 16 +%half2 = OpTypeVector %half 2 +%char_block = OpTypeStruct %char2 +%short_block = OpTypeStruct %short2 +%half_block = OpTypeStruct %half2 +%ptr_ssbo_char_block = OpTypePointer StorageBuffer %char_block +%ptr_ssbo_char2 = OpTypePointer StorageBuffer %char2 +%ptr_ssbo_char = OpTypePointer StorageBuffer %char +%ptr_ssbo_short_block = OpTypePointer StorageBuffer %short_block +%ptr_ssbo_short2 = OpTypePointer StorageBuffer %short2 +%ptr_ssbo_short = OpTypePointer StorageBuffer %short +%ptr_ssbo_half_block = OpTypePointer StorageBuffer %half_block +%ptr_ssbo_half2 = OpTypePointer StorageBuffer %half2 +%ptr_ssbo_half = OpTypePointer StorageBuffer %half +%void_fn = OpTypeFunction %void +%char_var = OpVariable %ptr_ssbo_char_block StorageBuffer +%short_var = OpVariable %ptr_ssbo_short_block StorageBuffer +%half_var = OpVariable %ptr_ssbo_half_block StorageBuffer +)"; + generator.after_types_ = R"( +%func = OpFunction %void None %void_fn +%entry = OpLabel +%char2_gep = OpAccessChain %ptr_ssbo_char2 %char_var %int_0 +%ld_char2 = OpLoad %char2 %char2_gep +%char_gep = OpAccessChain %ptr_ssbo_char %char_var %int_0 %int_0 +%ld_char = OpLoad %char %char_gep +%short2_gep = OpAccessChain %ptr_ssbo_short2 %short_var %int_0 +%ld_short2 = OpLoad %short2 %short2_gep +%short_gep = OpAccessChain %ptr_ssbo_short %short_var %int_0 %int_0 +%ld_short = OpLoad %short %short_gep +%half2_gep = OpAccessChain %ptr_ssbo_half2 %half_var %int_0 +%ld_half2 = OpLoad %half2 %half2_gep +%half_gep = OpAccessChain %ptr_ssbo_half %half_var %int_0 %int_0 +%ld_half = OpLoad %half %half_gep +)"; + generator.add_at_the_end_ = R"( +OpReturn +OpFunctionEnd +)"; + return generator; +} + +TEST_P(ValidateSmallComposites, VectorExtractDynamic) { + std::string type = GetParam(); + CodeGenerator generator = GetSmallCompositesCodeGenerator(); + std::string inst = + "%inst = OpVectorExtractDynamic %" + type + " %ld_" + type + "2 %int_0\n"; + generator.after_types_ += inst; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot extract from a vector of 8- or 16-bit types")); +} + +TEST_P(ValidateSmallComposites, VectorInsertDynamic) { + std::string type = GetParam(); + CodeGenerator generator = GetSmallCompositesCodeGenerator(); + std::string inst = "%inst = OpVectorInsertDynamic %" + type + "2 %ld_" + + type + "2 %ld_" + type + " %int_0\n"; + generator.after_types_ += inst; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot insert into a vector of 8- or 16-bit types")); +} + +TEST_P(ValidateSmallComposites, VectorShuffle) { + std::string type = GetParam(); + CodeGenerator generator = GetSmallCompositesCodeGenerator(); + std::string inst = "%inst = OpVectorShuffle %" + type + "2 %ld_" + type + + "2 %ld_" + type + "2 0 0\n"; + generator.after_types_ += inst; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot shuffle a vector of 8- or 16-bit types")); +} + +TEST_P(ValidateSmallComposites, CompositeConstruct) { + std::string type = GetParam(); + CodeGenerator generator = GetSmallCompositesCodeGenerator(); + std::string inst = "%inst = OpCompositeConstruct %" + type + "2 %ld_" + type + + " %ld_" + type + "\n"; + generator.after_types_ += inst; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot create a composite containing 8- or 16-bit types")); +} + +TEST_P(ValidateSmallComposites, CompositeExtract) { + std::string type = GetParam(); + CodeGenerator generator = GetSmallCompositesCodeGenerator(); + std::string inst = + "%inst = OpCompositeExtract %" + type + " %ld_" + type + "2 0\n"; + generator.after_types_ += inst; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot extract from a composite of 8- or 16-bit types")); +} + +TEST_P(ValidateSmallComposites, CompositeInsert) { + std::string type = GetParam(); + CodeGenerator generator = GetSmallCompositesCodeGenerator(); + std::string inst = "%inst = OpCompositeInsert %" + type + "2 %ld_" + type + + " %ld_" + type + "2 0\n"; + generator.after_types_ += inst; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot insert into a composite of 8- or 16-bit types")); +} + +TEST_P(ValidateSmallComposites, CopyObject) { + std::string type = GetParam(); + CodeGenerator generator = GetSmallCompositesCodeGenerator(); + std::string inst = "%inst = OpCopyObject %" + type + "2 %ld_" + type + "2\n"; + generator.after_types_ += inst; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +INSTANTIATE_TEST_SUITE_P(SmallCompositeInstructions, ValidateSmallComposites, + Values("char", "short", "half")); + +TEST_F(ValidateComposites, HalfMatrixCannotTranspose) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability UniformAndStorageBuffer16BitAccess +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 0 RowMajor +OpMemberDecorate %block 0 MatrixStride 8 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 16 +%float2 = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %float2 2 +%block = OpTypeStruct %mat2x2 +%ptr_ssbo_block = OpTypePointer StorageBuffer %block +%ptr_ssbo_mat2x2 = OpTypePointer StorageBuffer %mat2x2 +%var = OpVariable %ptr_ssbo_block StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +%gep = OpAccessChain %ptr_ssbo_mat2x2 %var %int_0 +%ld = OpLoad %mat2x2 %gep +%inst = OpTranspose %mat2x2 %ld +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot transpose matrices of 16-bit floats")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_constants_test.cpp b/third_party/spirv-tools/test/val/val_constants_test.cpp new file mode 100644 index 0000000..301539d --- /dev/null +++ b/third_party/spirv-tools/test/val/val_constants_test.cpp @@ -0,0 +1,483 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Test validation of constants. +// +// This file contains newer tests. Older tests may be in other files such as +// val_id_test.cpp. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateConstant = spvtest::ValidateBase; + +#define kBasicTypes \ + "%bool = OpTypeBool " \ + "%uint = OpTypeInt 32 0 " \ + "%uint2 = OpTypeVector %uint 2 " \ + "%float = OpTypeFloat 32 " \ + "%_ptr_uint = OpTypePointer Workgroup %uint " \ + "%uint_0 = OpConstantNull %uint " \ + "%uint2_0 = OpConstantNull %uint " \ + "%float_0 = OpConstantNull %float " \ + "%false = OpConstantFalse %bool " \ + "%true = OpConstantTrue %bool " \ + "%null = OpConstantNull %_ptr_uint " + +#define kShaderPreamble \ + "OpCapability Shader\n" \ + "OpCapability Linkage\n" \ + "OpMemoryModel Logical Simple\n" + +#define kKernelPreamble \ + "OpCapability Kernel\n" \ + "OpCapability Linkage\n" \ + "OpCapability Addresses\n" \ + "OpMemoryModel Physical32 OpenCL\n" + +struct ConstantOpCase { + spv_target_env env; + std::string assembly; + bool expect_success; + std::string expect_err; +}; + +using ValidateConstantOp = spvtest::ValidateBase; + +TEST_P(ValidateConstantOp, Samples) { + const auto env = GetParam().env; + CompileSuccessfully(GetParam().assembly, env); + const auto result = ValidateInstructions(env); + if (GetParam().expect_success) { + EXPECT_EQ(SPV_SUCCESS, result); + EXPECT_THAT(getDiagnosticString(), Eq("")); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, result); + EXPECT_THAT(getDiagnosticString(), HasSubstr(GetParam().expect_err)); + } +} + +#define GOOD_SHADER_10(STR) \ + { SPV_ENV_UNIVERSAL_1_0, kShaderPreamble kBasicTypes STR, true, "" } +#define GOOD_KERNEL_10(STR) \ + { SPV_ENV_UNIVERSAL_1_0, kKernelPreamble kBasicTypes STR, true, "" } +INSTANTIATE_TEST_SUITE_P( + UniversalInShader, ValidateConstantOp, + ValuesIn(std::vector{ + // TODO(dneto): Conversions must change width. + GOOD_SHADER_10("%v = OpSpecConstantOp %uint SConvert %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %float FConvert %float_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint SNegate %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint Not %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint IAdd %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint ISub %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint IMul %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint UDiv %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint SDiv %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint UMod %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint SRem %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint SMod %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %uint ShiftRightLogical %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %uint ShiftRightArithmetic %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %uint ShiftLeftLogical %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %uint BitwiseOr %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %uint BitwiseXor %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %uint2 VectorShuffle %uint2_0 %uint2_0 1 3"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %uint CompositeExtract %uint2_0 1"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %uint2 CompositeInsert %uint_0 %uint2_0 1"), + GOOD_SHADER_10("%v = OpSpecConstantOp %bool LogicalOr %true %false"), + GOOD_SHADER_10("%v = OpSpecConstantOp %bool LogicalNot %true"), + GOOD_SHADER_10("%v = OpSpecConstantOp %bool LogicalAnd %true %false"), + GOOD_SHADER_10("%v = OpSpecConstantOp %bool LogicalEqual %true %false"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %bool LogicalNotEqual %true %false"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %uint Select %true %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %bool IEqual %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %bool INotEqual %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %bool ULessThan %uint_0 %uint_0"), + GOOD_SHADER_10("%v = OpSpecConstantOp %bool SLessThan %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %bool ULessThanEqual %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %bool SLessThanEqual %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %bool UGreaterThan %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %bool UGreaterThanEqual %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %bool SGreaterThan %uint_0 %uint_0"), + GOOD_SHADER_10( + "%v = OpSpecConstantOp %bool SGreaterThanEqual %uint_0 %uint_0"), + })); + +INSTANTIATE_TEST_SUITE_P( + UniversalInKernel, ValidateConstantOp, + ValuesIn(std::vector{ + // TODO(dneto): Conversions must change width. + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SConvert %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FConvert %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SNegate %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint Not %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint IAdd %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint ISub %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint IMul %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint UDiv %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SDiv %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint UMod %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SRem %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint SMod %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %uint ShiftRightLogical %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %uint ShiftRightArithmetic %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %uint ShiftLeftLogical %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint BitwiseOr %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %uint BitwiseXor %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %uint2 VectorShuffle %uint2_0 %uint2_0 1 3"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %uint CompositeExtract %uint2_0 1"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %uint2 CompositeInsert %uint_0 %uint2_0 1"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %bool LogicalOr %true %false"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %bool LogicalNot %true"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %bool LogicalAnd %true %false"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %bool LogicalEqual %true %false"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %bool LogicalNotEqual %true %false"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %uint Select %true %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %bool IEqual %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %bool INotEqual %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %bool ULessThan %uint_0 %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %bool SLessThan %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %bool ULessThanEqual %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %bool SLessThanEqual %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %bool UGreaterThan %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %bool UGreaterThanEqual %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %bool SGreaterThan %uint_0 %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %bool SGreaterThanEqual %uint_0 %uint_0"), + })); + +INSTANTIATE_TEST_SUITE_P( + UConvert, ValidateConstantOp, + ValuesIn(std::vector{ + // TODO(dneto): Conversions must change width. + {SPV_ENV_UNIVERSAL_1_0, + kKernelPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + true, ""}, + {SPV_ENV_UNIVERSAL_1_1, + kKernelPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + true, ""}, + {SPV_ENV_UNIVERSAL_1_3, + kKernelPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + true, ""}, + {SPV_ENV_UNIVERSAL_1_3, + kKernelPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + true, ""}, + {SPV_ENV_UNIVERSAL_1_4, + kKernelPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + true, ""}, + {SPV_ENV_UNIVERSAL_1_0, + kShaderPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + false, + "Prior to SPIR-V 1.4, specialization constant operation " + "UConvert requires Kernel capability"}, + {SPV_ENV_UNIVERSAL_1_1, + kShaderPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + false, + "Prior to SPIR-V 1.4, specialization constant operation " + "UConvert requires Kernel capability"}, + {SPV_ENV_UNIVERSAL_1_3, + kShaderPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + false, + "Prior to SPIR-V 1.4, specialization constant operation " + "UConvert requires Kernel capability"}, + {SPV_ENV_UNIVERSAL_1_3, + kShaderPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + false, + "Prior to SPIR-V 1.4, specialization constant operation " + "UConvert requires Kernel capability"}, + {SPV_ENV_UNIVERSAL_1_4, + kShaderPreamble kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + true, ""}, + })); + +INSTANTIATE_TEST_SUITE_P( + KernelInKernel, ValidateConstantOp, + ValuesIn(std::vector{ + // TODO(dneto): Conversions must change width. + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint ConvertFToS %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float ConvertSToF %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint ConvertFToU %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float ConvertUToF %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint UConvert %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %_ptr_uint GenericCastToPtr %null"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %_ptr_uint PtrCastToGeneric %null"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %uint Bitcast %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FNegate %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FAdd %float_0 %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FSub %float_0 %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FMul %float_0 %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FDiv %float_0 %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FRem %float_0 %float_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %float FMod %float_0 %float_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %_ptr_uint AccessChain %null %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %_ptr_uint InBoundsAccessChain " + "%null %uint_0"), + GOOD_KERNEL_10( + "%v = OpSpecConstantOp %_ptr_uint PtrAccessChain %null %uint_0"), + GOOD_KERNEL_10("%v = OpSpecConstantOp %_ptr_uint " + "InBoundsPtrAccessChain %null %uint_0"), + })); + +#define BAD_SHADER_10(STR, NAME) \ + { \ + SPV_ENV_UNIVERSAL_1_0, kShaderPreamble kBasicTypes STR, false, \ + "Specialization constant operation " NAME \ + " requires Kernel capability" \ + } +INSTANTIATE_TEST_SUITE_P( + KernelInShader, ValidateConstantOp, + ValuesIn(std::vector{ + // TODO(dneto): Conversions must change width. + BAD_SHADER_10("%v = OpSpecConstantOp %uint ConvertFToS %float_0", + "ConvertFToS"), + BAD_SHADER_10("%v = OpSpecConstantOp %float ConvertSToF %uint_0", + "ConvertSToF"), + BAD_SHADER_10("%v = OpSpecConstantOp %uint ConvertFToU %float_0", + "ConvertFToU"), + BAD_SHADER_10("%v = OpSpecConstantOp %float ConvertUToF %uint_0", + "ConvertUToF"), + BAD_SHADER_10("%v = OpSpecConstantOp %_ptr_uint GenericCastToPtr %null", + "GenericCastToPtr"), + BAD_SHADER_10("%v = OpSpecConstantOp %_ptr_uint PtrCastToGeneric %null", + "PtrCastToGeneric"), + BAD_SHADER_10("%v = OpSpecConstantOp %uint Bitcast %uint_0", "Bitcast"), + BAD_SHADER_10("%v = OpSpecConstantOp %float FNegate %float_0", + "FNegate"), + BAD_SHADER_10("%v = OpSpecConstantOp %float FAdd %float_0 %float_0", + "FAdd"), + BAD_SHADER_10("%v = OpSpecConstantOp %float FSub %float_0 %float_0", + "FSub"), + BAD_SHADER_10("%v = OpSpecConstantOp %float FMul %float_0 %float_0", + "FMul"), + BAD_SHADER_10("%v = OpSpecConstantOp %float FDiv %float_0 %float_0", + "FDiv"), + BAD_SHADER_10("%v = OpSpecConstantOp %float FRem %float_0 %float_0", + "FRem"), + BAD_SHADER_10("%v = OpSpecConstantOp %float FMod %float_0 %float_0", + "FMod"), + BAD_SHADER_10( + "%v = OpSpecConstantOp %_ptr_uint AccessChain %null %uint_0", + "AccessChain"), + BAD_SHADER_10("%v = OpSpecConstantOp %_ptr_uint InBoundsAccessChain " + "%null %uint_0", + "InBoundsAccessChain"), + BAD_SHADER_10( + "%v = OpSpecConstantOp %_ptr_uint PtrAccessChain %null %uint_0", + "PtrAccessChain"), + BAD_SHADER_10("%v = OpSpecConstantOp %_ptr_uint " + "InBoundsPtrAccessChain %null %uint_0", + "InBoundsPtrAccessChain"), + })); + +INSTANTIATE_TEST_SUITE_P( + UConvertInAMD_gpu_shader_int16, ValidateConstantOp, + ValuesIn(std::vector{ + // SPV_AMD_gpu_shader_int16 should enable UConvert for OpSpecConstantOp + // https://github.com/KhronosGroup/glslang/issues/848 + {SPV_ENV_UNIVERSAL_1_0, + "OpCapability Shader " + "OpCapability Linkage ; So we don't need to define a function\n" + "OpExtension \"SPV_AMD_gpu_shader_int16\" " + "OpMemoryModel Logical Simple " kBasicTypes + "%v = OpSpecConstantOp %uint UConvert %uint_0", + true, ""}, + })); + +TEST_F(ValidateConstant, SpecConstantUConvert1p3Binary1p4EnvBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%const = OpSpecConstantOp %int UConvert %int0 +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Prior to SPIR-V 1.4, specialization constant operation UConvert " + "requires Kernel capability or extension SPV_AMD_gpu_shader_int16")); +} + +using SmallStorageConstants = spvtest::ValidateBase; + +CodeGenerator GetSmallStorageCodeGenerator() { + CodeGenerator generator; + generator.capabilities_ = R"( +OpCapability Shader +OpCapability Linkage +OpCapability UniformAndStorageBuffer16BitAccess +OpCapability StoragePushConstant16 +OpCapability StorageInputOutput16 +OpCapability UniformAndStorageBuffer8BitAccess +OpCapability StoragePushConstant8 +)"; + generator.extensions_ = R"( +OpExtension "SPV_KHR_16bit_storage" +OpExtension "SPV_KHR_8bit_storage" +)"; + generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; + generator.types_ = R"( +%short = OpTypeInt 16 0 +%short2 = OpTypeVector %short 2 +%char = OpTypeInt 8 0 +%char2 = OpTypeVector %char 2 +%half = OpTypeFloat 16 +%half2 = OpTypeVector %half 2 +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +)"; + return generator; +} + +TEST_P(SmallStorageConstants, SmallConstant) { + std::string constant = GetParam(); + CodeGenerator generator = GetSmallStorageCodeGenerator(); + generator.after_types_ += constant + "\n"; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Cannot form constants of 8- or 16-bit types")); +} + +// Constant composites would be caught through scalar constants. +INSTANTIATE_TEST_SUITE_P( + SmallConstants, SmallStorageConstants, + Values("%c = OpConstant %char 0", "%c = OpConstantNull %char2", + "%c = OpConstant %short 0", "%c = OpConstantNull %short", + "%c = OpConstant %half 0", "%c = OpConstantNull %half", + "%c = OpSpecConstant %char 0", "%c = OpSpecConstant %short 0", + "%c = OpSpecConstant %half 0", + "%c = OpSpecConstantOp %char SConvert %int_0", + "%c = OpSpecConstantOp %short SConvert %int_0", + "%c = OpSpecConstantOp %half FConvert %float_0")); + +TEST_F(ValidateConstant, NullPointerTo16BitStorageOk) { + std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointersStorageBuffer +OpCapability UniformAndStorageBuffer16BitAccess +OpCapability Linkage +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +%half = OpTypeFloat 16 +%ptr_ssbo_half = OpTypePointer StorageBuffer %half +%null_ptr = OpConstantNull %ptr_ssbo_half +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateConstant, NullMatrix) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%mat2x2 = OpTypeMatrix %v2float 2 +%null_vector = OpConstantNull %v2float +%null_matrix = OpConstantComposite %mat2x2 %null_vector %null_vector +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConstant, NullPhysicalStorageBuffer) { + std::string spirv = R"( +OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpCapability Linkage +OpExtension "SPV_KHR_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpName %ptr "ptr" +%int = OpTypeInt 32 0 +%ptr = OpTypePointer PhysicalStorageBuffer %int +%null = OpConstantNull %ptr +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantNull Result Type '1[%ptr]' cannot have " + "a null value")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_conversion_test.cpp b/third_party/spirv-tools/test/val/val_conversion_test.cpp new file mode 100644 index 0000000..47e6793 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_conversion_test.cpp @@ -0,0 +1,1721 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for unique type declaration rules validator. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; +using ::testing::Values; + +using ValidateConversion = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& decorations = "", const std::string& types = "", + const std::string& variables = "") { + const std::string capabilities = + R"( +OpCapability Shader +OpCapability Int64 +OpCapability Float64)"; + + const std::string after_extension_before_decorations = + R"( +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft)"; + + const std::string after_decorations_before_types = + R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%f64 = OpTypeFloat 64 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%boolvec2 = OpTypeVector %bool 2 +%s32vec2 = OpTypeVector %s32 2 +%u32vec2 = OpTypeVector %u32 2 +%u64vec2 = OpTypeVector %u64 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%boolvec3 = OpTypeVector %bool 3 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%s32vec3 = OpTypeVector %s32 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%boolvec4 = OpTypeVector %bool 4 +%u32vec4 = OpTypeVector %u32 4 +%u64vec4 = OpTypeVector %u64 4 +%s32vec4 = OpTypeVector %s32 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 +%s32_4 = OpConstant %s32 4 +%s32_m1 = OpConstant %s32 -1 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64_4 = OpConstant %f64 4 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +%s64_4 = OpConstant %s64 4 +%s64_m1 = OpConstant %s64 -1 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_4 = OpConstant %u64 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%s32vec3_012 = OpConstantComposite %s32vec3 %s32_0 %s32_1 %s32_2 +%s32vec3_123 = OpConstantComposite %s32vec3 %s32_1 %s32_2 %s32_3 +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4 + +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec3_123 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_3 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 +%f64vec4_1234 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_3 %f64_4 + +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool + +%f32ptr_func = OpTypePointer Function %f32)"; + + const std::string after_variables_before_body = + R"( +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string after_body = + R"( +OpReturn +OpFunctionEnd)"; + + return capabilities + capabilities_and_extensions + + after_extension_before_decorations + decorations + + after_decorations_before_types + types + variables + + after_variables_before_body + body + after_body; +} + +std::string GenerateKernelCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + const std::string capabilities = + R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability GenericPointer +OpCapability Int64 +OpCapability Float64)"; + + const std::string after_extension_before_body = + R"( +OpMemoryModel Physical32 OpenCL +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%f64 = OpTypeFloat 64 +%u64 = OpTypeInt 64 0 +%boolvec2 = OpTypeVector %bool 2 +%u32vec2 = OpTypeVector %u32 2 +%u64vec2 = OpTypeVector %u64 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%boolvec3 = OpTypeVector %bool 3 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%boolvec4 = OpTypeVector %bool 4 +%u32vec4 = OpTypeVector %u32 4 +%u64vec4 = OpTypeVector %u64 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64_4 = OpConstant %f64 4 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_4 = OpConstant %u64 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec3_123 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_3 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 +%f64vec4_1234 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_3 %f64_4 + +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 + +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool + +%f32ptr_func = OpTypePointer Function %f32 +%u32ptr_func = OpTypePointer Function %u32 +%f32ptr_gen = OpTypePointer Generic %f32 +%f32ptr_inp = OpTypePointer Input %f32 +%f32ptr_wg = OpTypePointer Workgroup %f32 +%f32ptr_cwg = OpTypePointer CrossWorkgroup %f32 + +%f32inp = OpVariable %f32ptr_inp Input + +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string after_body = + R"( +OpReturn +OpFunctionEnd)"; + + return capabilities + capabilities_and_extensions + + after_extension_before_body + body + after_body; +} + +TEST_F(ValidateConversion, ConvertFToUSuccess) { + const std::string body = R"( +%val1 = OpConvertFToU %u32 %f32_1 +%val2 = OpConvertFToU %u32 %f64_0 +%val3 = OpConvertFToU %u32vec2 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertFToUWrongResultType) { + const std::string body = R"( +%val = OpConvertFToU %s32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected unsigned int scalar or vector type as Result " + "Type: ConvertFToU")); +} + +TEST_F(ValidateConversion, ConvertFToUWrongInputType) { + const std::string body = R"( +%val = OpConvertFToU %u32 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input to be float scalar or vector: ConvertFToU")); +} + +TEST_F(ValidateConversion, ConvertFToUDifferentDimension) { + const std::string body = R"( +%val = OpConvertFToU %u32 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result " + "Type: ConvertFToU")); +} + +TEST_F(ValidateConversion, ConvertFToSSuccess) { + const std::string body = R"( +%val1 = OpConvertFToS %s32 %f32_1 +%val2 = OpConvertFToS %u32 %f64_0 +%val3 = OpConvertFToS %s32vec2 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertFToSWrongResultType) { + const std::string body = R"( +%val = OpConvertFToS %bool %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected int scalar or vector type as Result Type: ConvertFToS")); +} + +TEST_F(ValidateConversion, ConvertFToSWrongInputType) { + const std::string body = R"( +%val = OpConvertFToS %s32 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input to be float scalar or vector: ConvertFToS")); +} + +TEST_F(ValidateConversion, ConvertFToSDifferentDimension) { + const std::string body = R"( +%val = OpConvertFToS %u32 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result " + "Type: ConvertFToS")); +} + +TEST_F(ValidateConversion, ConvertSToFSuccess) { + const std::string body = R"( +%val1 = OpConvertSToF %f32 %u32_1 +%val2 = OpConvertSToF %f32 %s64_0 +%val3 = OpConvertSToF %f32vec2 %s32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertSToFWrongResultType) { + const std::string body = R"( +%val = OpConvertSToF %u32 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected float scalar or vector type as Result Type: ConvertSToF")); +} + +TEST_F(ValidateConversion, ConvertSToFWrongInputType) { + const std::string body = R"( +%val = OpConvertSToF %f32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input to be int scalar or vector: ConvertSToF")); +} + +TEST_F(ValidateConversion, ConvertSToFDifferentDimension) { + const std::string body = R"( +%val = OpConvertSToF %f32 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result " + "Type: ConvertSToF")); +} + +TEST_F(ValidateConversion, UConvertSuccess) { + const std::string body = R"( +%val1 = OpUConvert %u32 %u64_1 +%val2 = OpUConvert %u64 %s32_0 +%val3 = OpUConvert %u64vec2 %s32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, UConvertWrongResultType) { + const std::string body = R"( +%val = OpUConvert %s32 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected unsigned int scalar or vector type as Result " + "Type: UConvert")); +} + +TEST_F(ValidateConversion, UConvertWrongInputType) { + const std::string body = R"( +%val = OpUConvert %u32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be int scalar or vector: UConvert")); +} + +TEST_F(ValidateConversion, UConvertDifferentDimension) { + const std::string body = R"( +%val = OpUConvert %u32 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result " + "Type: UConvert")); +} + +TEST_F(ValidateConversion, UConvertSameBitWidth) { + const std::string body = R"( +%val = OpUConvert %u32 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have different bit width from " + "Result Type: UConvert")); +} + +TEST_F(ValidateConversion, SConvertSuccess) { + const std::string body = R"( +%val1 = OpSConvert %s32 %u64_1 +%val2 = OpSConvert %s64 %s32_0 +%val3 = OpSConvert %u64vec2 %s32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, SConvertWrongResultType) { + const std::string body = R"( +%val = OpSConvert %f32 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as Result Type: SConvert")); +} + +TEST_F(ValidateConversion, SConvertWrongInputType) { + const std::string body = R"( +%val = OpSConvert %u32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be int scalar or vector: SConvert")); +} + +TEST_F(ValidateConversion, SConvertDifferentDimension) { + const std::string body = R"( +%val = OpSConvert %s32 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result " + "Type: SConvert")); +} + +TEST_F(ValidateConversion, SConvertSameBitWidth) { + const std::string body = R"( +%val = OpSConvert %u32 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have different bit width from " + "Result Type: SConvert")); +} + +TEST_F(ValidateConversion, FConvertSuccess) { + const std::string body = R"( +%val1 = OpFConvert %f32 %f64_1 +%val2 = OpFConvert %f64 %f32_0 +%val3 = OpFConvert %f64vec2 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, FConvertWrongResultType) { + const std::string body = R"( +%val = OpFConvert %u32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected float scalar or vector type as Result Type: FConvert")); +} + +TEST_F(ValidateConversion, FConvertWrongInputType) { + const std::string body = R"( +%val = OpFConvert %f32 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input to be float scalar or vector: FConvert")); +} + +TEST_F(ValidateConversion, FConvertDifferentDimension) { + const std::string body = R"( +%val = OpFConvert %f64 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result " + "Type: FConvert")); +} + +TEST_F(ValidateConversion, FConvertSameBitWidth) { + const std::string body = R"( +%val = OpFConvert %f32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have different bit width from " + "Result Type: FConvert")); +} + +TEST_F(ValidateConversion, QuantizeToF16Success) { + const std::string body = R"( +%val1 = OpQuantizeToF16 %f32 %f32_1 +%val2 = OpQuantizeToF16 %f32 %f32_0 +%val3 = OpQuantizeToF16 %f32vec2 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, QuantizeToF16WrongResultType) { + const std::string body = R"( +%val = OpQuantizeToF16 %u32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit float scalar or vector type as Result Type: " + "QuantizeToF16")); +} + +TEST_F(ValidateConversion, QuantizeToF16WrongResultTypeBitWidth) { + const std::string body = R"( +%val = OpQuantizeToF16 %u64 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected 32-bit float scalar or vector type as Result Type: " + "QuantizeToF16")); +} + +TEST_F(ValidateConversion, QuantizeToF16WrongInputType) { + const std::string body = R"( +%val = OpQuantizeToF16 %f32 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected input type to be equal to Result Type: QuantizeToF16")); +} + +TEST_F(ValidateConversion, ConvertPtrToUSuccess) { + const std::string body = R"( +%ptr = OpVariable %f32ptr_func Function +%val1 = OpConvertPtrToU %u32 %ptr +%val2 = OpConvertPtrToU %u64 %ptr +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertPtrToUWrongResultType) { + const std::string body = R"( +%ptr = OpVariable %f32ptr_func Function +%val = OpConvertPtrToU %f32 %ptr +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected unsigned int scalar type as Result Type: " + "ConvertPtrToU")); +} + +TEST_F(ValidateConversion, ConvertPtrToUNotPointer) { + const std::string body = R"( +%val = OpConvertPtrToU %u32 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a pointer: ConvertPtrToU")); +} + +TEST_F(ValidateConversion, SatConvertSToUSuccess) { + const std::string body = R"( +%val1 = OpSatConvertSToU %u32 %u64_2 +%val2 = OpSatConvertSToU %u64 %u32_1 +%val3 = OpSatConvertSToU %u64vec2 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, SatConvertSToUWrongResultType) { + const std::string body = R"( +%val = OpSatConvertSToU %f32 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar or vector type as Result Type: " + "SatConvertSToU")); +} + +TEST_F(ValidateConversion, SatConvertSToUWrongInputType) { + const std::string body = R"( +%val = OpSatConvertSToU %u32 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected int scalar or vector as input: SatConvertSToU")); +} + +TEST_F(ValidateConversion, SatConvertSToUDifferentDimension) { + const std::string body = R"( +%val = OpSatConvertSToU %u32 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input to have the same dimension as Result Type: " + "SatConvertSToU")); +} + +TEST_F(ValidateConversion, ConvertUToPtrSuccess) { + const std::string body = R"( +%val1 = OpConvertUToPtr %f32ptr_func %u32_1 +%val2 = OpConvertUToPtr %f32ptr_func %u64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertUToPtrWrongResultType) { + const std::string body = R"( +%val = OpConvertUToPtr %f32 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a pointer: ConvertUToPtr")); +} + +TEST_F(ValidateConversion, ConvertUToPtrNotInt) { + const std::string body = R"( +%val = OpConvertUToPtr %f32ptr_func %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar as input: ConvertUToPtr")); +} + +TEST_F(ValidateConversion, ConvertUToPtrNotIntScalar) { + const std::string body = R"( +%val = OpConvertUToPtr %f32ptr_func %u32vec2_12 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected int scalar as input: ConvertUToPtr")); +} + +TEST_F(ValidateConversion, PtrCastToGenericSuccess) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%val = OpPtrCastToGeneric %f32ptr_gen %ptr_func +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, PtrCastToGenericWrongResultType) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%val = OpPtrCastToGeneric %f32 %ptr_func +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be a pointer: PtrCastToGeneric")); +} + +TEST_F(ValidateConversion, PtrCastToGenericWrongResultStorageClass) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%val = OpPtrCastToGeneric %f32ptr_func %ptr_func +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have storage class Generic: " + "PtrCastToGeneric")); +} + +TEST_F(ValidateConversion, PtrCastToGenericWrongInputType) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%val = OpPtrCastToGeneric %f32ptr_gen %f32 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 4[%float] cannot be a " + "type")); +} + +TEST_F(ValidateConversion, PtrCastToGenericWrongInputStorageClass) { + const std::string body = R"( +%val = OpPtrCastToGeneric %f32ptr_gen %f32inp +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have storage class Workgroup, " + "CrossWorkgroup or Function: PtrCastToGeneric")); +} + +TEST_F(ValidateConversion, PtrCastToGenericPointToDifferentType) { + const std::string body = R"( +%ptr_func = OpVariable %u32ptr_func Function +%val = OpPtrCastToGeneric %f32ptr_gen %ptr_func +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input and Result Type to point to the same type: " + "PtrCastToGeneric")); +} + +TEST_F(ValidateConversion, GenericCastToPtrSuccess) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtr %f32ptr_func %ptr_gen +%ptr_wg = OpGenericCastToPtr %f32ptr_wg %ptr_gen +%ptr_cwg = OpGenericCastToPtr %f32ptr_cwg %ptr_gen +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, GenericCastToPtrWrongResultType) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtr %f32 %ptr_gen +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be a pointer: GenericCastToPtr")); +} + +TEST_F(ValidateConversion, GenericCastToPtrWrongResultStorageClass) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtr %f32ptr_gen %ptr_gen +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have storage class Workgroup, " + "CrossWorkgroup or Function: GenericCastToPtr")); +} + +TEST_F(ValidateConversion, GenericCastToPtrWrongInputType) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtr %f32ptr_func %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a pointer: GenericCastToPtr")); +} + +TEST_F(ValidateConversion, GenericCastToPtrWrongInputStorageClass) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_func2 = OpGenericCastToPtr %f32ptr_func %ptr_func +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have storage class Generic: " + "GenericCastToPtr")); +} + +TEST_F(ValidateConversion, GenericCastToPtrPointToDifferentType) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtr %u32ptr_func %ptr_gen +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input and Result Type to point to the same type: " + "GenericCastToPtr")); +} + +TEST_F(ValidateConversion, GenericCastToPtrExplicitSuccess) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtrExplicit %f32ptr_func %ptr_gen Function +%ptr_wg = OpGenericCastToPtrExplicit %f32ptr_wg %ptr_gen Workgroup +%ptr_cwg = OpGenericCastToPtrExplicit %f32ptr_cwg %ptr_gen CrossWorkgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, GenericCastToPtrExplicitWrongResultType) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtrExplicit %f32 %ptr_gen Function +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Result Type to be a pointer: GenericCastToPtrExplicit")); +} + +TEST_F(ValidateConversion, GenericCastToPtrExplicitResultStorageClassDiffers) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtrExplicit %f32ptr_func %ptr_gen Workgroup +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be of target storage class: " + "GenericCastToPtrExplicit")); +} + +TEST_F(ValidateConversion, GenericCastToPtrExplicitWrongResultStorageClass) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtrExplicit %f32ptr_gen %ptr_gen Generic +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected target storage class to be Workgroup, " + "CrossWorkgroup or Function: GenericCastToPtrExplicit")); +} + +TEST_F(ValidateConversion, GenericCastToPtrExplicitWrongInputType) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtrExplicit %f32ptr_func %f32_1 Function +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input to be a pointer: GenericCastToPtrExplicit")); +} + +TEST_F(ValidateConversion, GenericCastToPtrExplicitWrongInputStorageClass) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_func2 = OpGenericCastToPtrExplicit %f32ptr_func %ptr_func Function +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to have storage class Generic: " + "GenericCastToPtrExplicit")); +} + +TEST_F(ValidateConversion, GenericCastToPtrExplicitPointToDifferentType) { + const std::string body = R"( +%ptr_func = OpVariable %f32ptr_func Function +%ptr_gen = OpPtrCastToGeneric %f32ptr_gen %ptr_func +%ptr_func2 = OpGenericCastToPtrExplicit %u32ptr_func %ptr_gen Function +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected input and Result Type to point to the same type: " + "GenericCastToPtrExplicit")); +} + +TEST_F(ValidateConversion, CoopMatConversionSuccess) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u16 = OpTypeInt 16 0 +%u32 = OpTypeInt 32 0 +%s16 = OpTypeInt 16 1 +%s32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 +%f32mat = OpTypeCooperativeMatrixNV %f32 %subgroup %u32_8 %u32_8 +%u16mat = OpTypeCooperativeMatrixNV %u16 %subgroup %u32_8 %u32_8 +%u32mat = OpTypeCooperativeMatrixNV %u32 %subgroup %u32_8 %u32_8 +%s16mat = OpTypeCooperativeMatrixNV %s16 %subgroup %u32_8 %u32_8 +%s32mat = OpTypeCooperativeMatrixNV %s32 %subgroup %u32_8 %u32_8 + +%f16_1 = OpConstant %f16 1 +%f32_1 = OpConstant %f32 1 +%u16_1 = OpConstant %u16 1 +%u32_1 = OpConstant %u32 1 +%s16_1 = OpConstant %s16 1 +%s32_1 = OpConstant %s32 1 + +%f16mat_1 = OpConstantComposite %f16mat %f16_1 +%f32mat_1 = OpConstantComposite %f32mat %f32_1 +%u16mat_1 = OpConstantComposite %u16mat %u16_1 +%u32mat_1 = OpConstantComposite %u32mat %u32_1 +%s16mat_1 = OpConstantComposite %s16mat %s16_1 +%s32mat_1 = OpConstantComposite %s32mat %s32_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%val11 = OpConvertFToU %u16mat %f16mat_1 +%val12 = OpConvertFToU %u32mat %f16mat_1 +%val13 = OpConvertFToS %s16mat %f16mat_1 +%val14 = OpConvertFToS %s32mat %f16mat_1 +%val15 = OpFConvert %f32mat %f16mat_1 + +%val21 = OpConvertFToU %u16mat %f32mat_1 +%val22 = OpConvertFToU %u32mat %f32mat_1 +%val23 = OpConvertFToS %s16mat %f32mat_1 +%val24 = OpConvertFToS %s32mat %f32mat_1 +%val25 = OpFConvert %f16mat %f32mat_1 + +%val31 = OpConvertUToF %f16mat %u16mat_1 +%val32 = OpConvertUToF %f32mat %u16mat_1 +%val33 = OpUConvert %u32mat %u16mat_1 +%val34 = OpSConvert %s32mat %u16mat_1 + +%val41 = OpConvertSToF %f16mat %s16mat_1 +%val42 = OpConvertSToF %f32mat %s16mat_1 +%val43 = OpUConvert %u32mat %s16mat_1 +%val44 = OpSConvert %s32mat %s16mat_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, CoopMatConversionShapesMismatchFail) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u16 = OpTypeInt 16 0 +%u32 = OpTypeInt 32 0 +%s16 = OpTypeInt 16 1 +%s32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%u32_4 = OpConstant %u32 4 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 +%f32mat = OpTypeCooperativeMatrixNV %f32 %subgroup %u32_4 %u32_4 + +%f16_1 = OpConstant %f16 1 + +%f16mat_1 = OpConstantComposite %f16mat %f16_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%val15 = OpFConvert %f32mat %f16mat_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected rows of Matrix type and Result Type to be identical")); +} + +TEST_F(ValidateConversion, CoopMatConversionShapesMismatchPass) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability Int16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%u16 = OpTypeInt 16 0 +%u32 = OpTypeInt 32 0 +%s16 = OpTypeInt 16 1 +%s32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%u32_4 = OpSpecConstant %u32 4 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 +%f32mat = OpTypeCooperativeMatrixNV %f32 %subgroup %u32_4 %u32_4 + +%f16_1 = OpConstant %f16 1 + +%f16mat_1 = OpConstantComposite %f16mat %f16_1 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%val15 = OpFConvert %f32mat %f16mat_1 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, BitcastSuccess) { + const std::string body = R"( +%ptr = OpVariable %f32ptr_func Function +%val1 = OpBitcast %u32 %ptr +%val2 = OpBitcast %u64 %ptr +%val3 = OpBitcast %f32ptr_func %u32_1 +%val4 = OpBitcast %f32ptr_wg %u64_1 +%val5 = OpBitcast %f32 %u32_1 +%val6 = OpBitcast %f32vec2 %u32vec2_12 +%val7 = OpBitcast %f32vec2 %u64_1 +%val8 = OpBitcast %f64 %u32vec2_12 +%val9 = OpBitcast %f32vec4 %f64vec2_12 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, BitcastSuccessSPV1p5) { + const std::string body = R"( +%ptr = OpVariable %f32ptr_func Function +%val1 = OpBitcast %u32 %ptr +%val2 = OpBitcast %u64 %ptr +%val3 = OpBitcast %f32ptr_func %u32_1 +%val4 = OpBitcast %f32ptr_wg %u64_1 +%val5 = OpBitcast %f32 %u32_1 +%val6 = OpBitcast %f32vec2 %u32vec2_12 +%val7 = OpBitcast %f32vec2 %u64_1 +%val8 = OpBitcast %f64 %u32vec2_12 +%val9 = OpBitcast %f32vec4 %f64vec2_12 +%val10 = OpBitcast %u32ptr_func %u32vec2_01 +%val11 = OpBitcast %u32vec2 %ptr +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); +} + +TEST_F(ValidateConversion, BitcastSuccessPhysicalStorageBufferKHR) { + const std::string body = R"( +%ptr = OpVariable %f32ptr_func Function +%val1 = OpBitcast %u32 %ptr +%val2 = OpBitcast %u64 %ptr +%val3 = OpBitcast %f32ptr_func %u32_1 +%val4 = OpBitcast %f32ptr_wg %u64_1 +%val5 = OpBitcast %f32 %u32_1 +%val6 = OpBitcast %f32vec2 %u32vec2_12 +%val7 = OpBitcast %f32vec2 %u64_1 +%val8 = OpBitcast %f64 %u32vec2_12 +%val9 = OpBitcast %f32vec4 %f64vec2_12 +%val10 = OpBitcast %u32ptr_func %u32vec2_01 +%val11 = OpBitcast %u32vec2 %ptr +)"; + + CompileSuccessfully( + GenerateKernelCode(body, + "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, BitcastInputHasNoType) { + const std::string body = R"( +%val = OpBitcast %u32 %f32 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 4[%float] cannot be a " + "type")); +} + +TEST_F(ValidateConversion, BitcastWrongResultType) { + const std::string body = R"( +%val = OpBitcast %bool %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be a pointer or int or float vector " + "or scalar type: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastWrongInputType) { + const std::string body = R"( +%val = OpBitcast %u32 %true +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a pointer or int or float vector " + "or scalar: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastPtrWrongInputType) { + const std::string body = R"( +%val = OpBitcast %u32ptr_func %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a pointer or int scalar if " + "Result Type is pointer: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastPtrWrongInputTypeSPV1p5) { + const std::string body = R"( +%val = OpBitcast %u32ptr_func %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a pointer, int scalar or 32-bit " + "int vector if Result Type is pointer: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastPtrWrongInputTypePhysicalStorageBufferKHR) { + const std::string body = R"( +%val = OpBitcast %u32ptr_func %f32_1 +)"; + + CompileSuccessfully( + GenerateKernelCode(body, + "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a pointer, int scalar or 32-bit " + "int vector if Result Type is pointer: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastPtrWrongInputTypeIntVectorSPV1p5) { + const std::string body = R"( +%val = OpBitcast %u32ptr_func %u64vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a pointer, int scalar or 32-bit " + "int vector if Result Type is pointer: Bitcast")); +} + +TEST_F(ValidateConversion, + BitcastPtrWrongInputTypeIntVectorPhysicalStorageBufferKHR) { + const std::string body = R"( +%val = OpBitcast %u32ptr_func %u64vec2_01 +)"; + + CompileSuccessfully( + GenerateKernelCode(body, + "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected input to be a pointer, int scalar or 32-bit " + "int vector if Result Type is pointer: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastPtrWrongResultType) { + const std::string body = R"( +%val = OpBitcast %f32 %f32inp +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer can only be converted to another pointer or " + "int scalar: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastPtrWrongResultTypeSPV1p5) { + const std::string body = R"( +%val = OpBitcast %f32 %f32inp +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer can only be converted to another pointer, int " + "scalar or 32-bit int vector: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastPtrWrongResultTypePhysicalStorageBufferKHR) { + const std::string body = R"( +%val = OpBitcast %f32 %f32inp +)"; + + CompileSuccessfully( + GenerateKernelCode(body, + "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer can only be converted to another pointer, int " + "scalar or 32-bit int vector: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastPtrWrongResultTypeIntVectorSPV1p5) { + const std::string body = R"( +%val = OpBitcast %u64vec2 %f32inp +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str(), SPV_ENV_UNIVERSAL_1_5); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer can only be converted to another pointer, int " + "scalar or 32-bit int vector: Bitcast")); +} + +TEST_F(ValidateConversion, + BitcastPtrWrongResultTypeIntVectorPhysicalStorageBufferKHR) { + const std::string body = R"( +%val = OpBitcast %u64vec2 %f32inp +)"; + + CompileSuccessfully( + GenerateKernelCode(body, + "\nOpExtension \"SPV_KHR_physical_storage_buffer\"") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer can only be converted to another pointer, int " + "scalar or 32-bit int vector: Bitcast")); +} + +TEST_F(ValidateConversion, BitcastDifferentTotalBitWidth) { + const std::string body = R"( +%val = OpBitcast %f32 %u64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected input to have the same total bit width as Result Type: " + "Bitcast")); +} + +TEST_F(ValidateConversion, ConvertUToPtrInputIsAType) { + const std::string spirv = R"( +OpCapability Addresses +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer Function %int +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%1 = OpConvertUToPtr %ptr_int %int +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%uint] cannot be a " + "type")); +} + +TEST_F(ValidateConversion, ConvertUToPtrPSBSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpConvertUToPtr %ptr %u64_1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertUToPtrPSBStorageClass) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer Function %uint64 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpConvertUToPtr %ptr %u64_1 +%val2 = OpConvertPtrToU %uint64 %val1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer storage class must be " + "PhysicalStorageBufferEXT: ConvertUToPtr")); +} + +TEST_F(ValidateConversion, ConvertPtrToUPSBSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 RestrictPointerEXT +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpConvertPtrToU %uint64 %val2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateConversion, ConvertPtrToUPSBStorageClass) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer Function %uint64 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %ptr Function +%val2 = OpConvertPtrToU %uint64 %val1 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer storage class must be " + "PhysicalStorageBufferEXT: ConvertPtrToU")); +} + +using ValidateSmallConversions = spvtest::ValidateBase; + +CodeGenerator GetSmallConversionsCodeGenerator() { + CodeGenerator generator; + generator.capabilities_ = R"( +OpCapability Shader +OpCapability Linkage +OpCapability UniformAndStorageBuffer16BitAccess +OpCapability UniformAndStorageBuffer8BitAccess +)"; + generator.extensions_ = R"( +OpExtension "SPV_KHR_16bit_storage" +OpExtension "SPV_KHR_8bit_storage" +)"; + generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; + generator.before_types_ = R"( +OpDecorate %char_block Block +OpMemberDecorate %char_block 0 Offset 0 +OpDecorate %short_block Block +OpMemberDecorate %short_block 0 Offset 0 +OpDecorate %half_block Block +OpMemberDecorate %half_block 0 Offset 0 +OpDecorate %int_block Block +OpMemberDecorate %int_block 0 Offset 0 +OpDecorate %float_block Block +OpMemberDecorate %float_block 0 Offset 0 +)"; + generator.types_ = R"( +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int2 = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%float2 = OpTypeVector %float 2 +%char = OpTypeInt 8 0 +%char2 = OpTypeVector %char 2 +%short = OpTypeInt 16 0 +%short2 = OpTypeVector %short 2 +%half = OpTypeFloat 16 +%half2 = OpTypeVector %half 2 +%char_block = OpTypeStruct %char2 +%short_block = OpTypeStruct %short2 +%half_block = OpTypeStruct %half2 +%int_block = OpTypeStruct %int2 +%float_block = OpTypeStruct %float2 +%ptr_ssbo_char_block = OpTypePointer StorageBuffer %char_block +%ptr_ssbo_char2 = OpTypePointer StorageBuffer %char2 +%ptr_ssbo_char = OpTypePointer StorageBuffer %char +%ptr_ssbo_short_block = OpTypePointer StorageBuffer %short_block +%ptr_ssbo_short2 = OpTypePointer StorageBuffer %short2 +%ptr_ssbo_short = OpTypePointer StorageBuffer %short +%ptr_ssbo_half_block = OpTypePointer StorageBuffer %half_block +%ptr_ssbo_half2 = OpTypePointer StorageBuffer %half2 +%ptr_ssbo_half = OpTypePointer StorageBuffer %half +%ptr_ssbo_int_block = OpTypePointer StorageBuffer %int_block +%ptr_ssbo_int2 = OpTypePointer StorageBuffer %int2 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ptr_ssbo_float_block = OpTypePointer StorageBuffer %float_block +%ptr_ssbo_float2 = OpTypePointer StorageBuffer %float2 +%ptr_ssbo_float = OpTypePointer StorageBuffer %float +%void_fn = OpTypeFunction %void +%char_var = OpVariable %ptr_ssbo_char_block StorageBuffer +%short_var = OpVariable %ptr_ssbo_short_block StorageBuffer +%half_var = OpVariable %ptr_ssbo_half_block StorageBuffer +%int_var = OpVariable %ptr_ssbo_int_block StorageBuffer +%float_var = OpVariable %ptr_ssbo_float_block StorageBuffer +)"; + generator.after_types_ = R"( +%func = OpFunction %void None %void_fn +%entry = OpLabel +%char2_gep = OpAccessChain %ptr_ssbo_char2 %char_var %int_0 +%ld_char2 = OpLoad %char2 %char2_gep +%char_gep = OpAccessChain %ptr_ssbo_char %char_var %int_0 %int_0 +%ld_char = OpLoad %char %char_gep +%short2_gep = OpAccessChain %ptr_ssbo_short2 %short_var %int_0 +%ld_short2 = OpLoad %short2 %short2_gep +%short_gep = OpAccessChain %ptr_ssbo_short %short_var %int_0 %int_0 +%ld_short = OpLoad %short %short_gep +%half2_gep = OpAccessChain %ptr_ssbo_half2 %half_var %int_0 +%ld_half2 = OpLoad %half2 %half2_gep +%half_gep = OpAccessChain %ptr_ssbo_half %half_var %int_0 %int_0 +%ld_half = OpLoad %half %half_gep +%int2_gep = OpAccessChain %ptr_ssbo_int2 %int_var %int_0 +%ld_int2 = OpLoad %int2 %int2_gep +%int_gep = OpAccessChain %ptr_ssbo_int %int_var %int_0 %int_0 +%ld_int = OpLoad %int %int_gep +%float2_gep = OpAccessChain %ptr_ssbo_float2 %float_var %int_0 +%ld_float2 = OpLoad %float2 %float2_gep +%float_gep = OpAccessChain %ptr_ssbo_float %float_var %int_0 %int_0 +%ld_float = OpLoad %float %float_gep +)"; + generator.add_at_the_end_ = R"( +OpReturn +OpFunctionEnd +)"; + return generator; +} + +TEST_P(ValidateSmallConversions, Instruction) { + CodeGenerator generator = GetSmallConversionsCodeGenerator(); + generator.after_types_ += GetParam() + "\n"; + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "8- or 16-bit types can only be used with width-only conversions")); +} + +INSTANTIATE_TEST_SUITE_P(SmallConversionInstructions, ValidateSmallConversions, + Values("%inst = OpConvertFToU %char %ld_float", + "%inst = OpConvertFToU %char2 %ld_float2", + "%inst = OpConvertFToU %short %ld_float", + "%inst = OpConvertFToU %short2 %ld_float2", + "%inst = OpConvertFToU %int %ld_half", + "%inst = OpConvertFToU %int2 %ld_half2", + "%inst = OpConvertFToS %char %ld_float", + "%inst = OpConvertFToS %char2 %ld_float2", + "%inst = OpConvertFToS %short %ld_float", + "%inst = OpConvertFToS %short2 %ld_float2", + "%inst = OpConvertFToS %int %ld_half", + "%inst = OpConvertFToS %int2 %ld_half2", + "%inst = OpConvertSToF %float %ld_char", + "%inst = OpConvertSToF %float2 %ld_char2", + "%inst = OpConvertSToF %float %ld_short", + "%inst = OpConvertSToF %float2 %ld_short2", + "%inst = OpConvertSToF %half %ld_int", + "%inst = OpConvertSToF %half2 %ld_int2", + "%inst = OpConvertUToF %float %ld_char", + "%inst = OpConvertUToF %float2 %ld_char2", + "%inst = OpConvertUToF %float %ld_short", + "%inst = OpConvertUToF %float2 %ld_short2", + "%inst = OpConvertUToF %half %ld_int", + "%inst = OpConvertUToF %half2 %ld_int2", + "%inst = OpBitcast %half %ld_short", + "%inst = OpBitcast %half2 %ld_short2", + "%inst = OpBitcast %short %ld_half", + "%inst = OpBitcast %short2 %ld_half2")); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_data_test.cpp b/third_party/spirv-tools/test/val/val_data_test.cpp new file mode 100644 index 0000000..30afd03 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_data_test.cpp @@ -0,0 +1,949 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for Data Rules. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; + +using ValidateData = spvtest::ValidateBase>; + +std::string HeaderWith(std::string cap) { + return std::string("OpCapability Shader OpCapability Linkage OpCapability ") + + cap + " OpMemoryModel Logical GLSL450 "; +} + +std::string WebGPUHeaderWith(std::string cap) { + return R"( +OpCapability Shader +OpCapability )" + + cap + R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +)"; +} + +std::string webgpu_header = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +)"; + +std::string header = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_addresses = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability GenericPointer + OpCapability Linkage + OpMemoryModel Physical32 OpenCL +)"; +std::string header_with_vec16_cap = R"( + OpCapability Shader + OpCapability Vector16 + OpCapability Linkage + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_int8 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Int8 + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_int16 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Int16 + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_int64 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Int64 + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_float16 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Float16 + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_float16_buffer = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Float16Buffer + OpMemoryModel Logical GLSL450 +)"; +std::string header_with_float64 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Float64 + OpMemoryModel Logical GLSL450 +)"; + +std::string invalid_comp_error = "Illegal number of components"; +std::string missing_cap_error = "requires the Vector16 capability"; +std::string missing_int8_cap_error = "requires the Int8 capability"; +std::string missing_int16_cap_error = + "requires the Int16 capability," + " or an extension that explicitly enables 16-bit integers."; +std::string missing_int64_cap_error = "requires the Int64 capability"; +std::string missing_float16_cap_error = + "requires the Float16 or Float16Buffer capability," + " or an extension that explicitly enables 16-bit floating point."; +std::string missing_float64_cap_error = "requires the Float64 capability"; +std::string invalid_num_bits_error = "Invalid number of bits"; + +TEST_F(ValidateData, vec0) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 0 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_comp_error)); +} + +TEST_F(ValidateData, vec1) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 1 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_comp_error)); +} + +TEST_F(ValidateData, vec2) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 2 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, vec3) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 3 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, vec4) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, vec5) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 5 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_comp_error)); +} + +TEST_F(ValidateData, vec8) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 8 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_cap_error)); +} + +TEST_F(ValidateData, vec8_with_capability) { + std::string str = header_with_vec16_cap + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 8 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, vec16) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 8 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_cap_error)); +} + +TEST_F(ValidateData, vec16_with_capability) { + std::string str = header_with_vec16_cap + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 16 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, vec15) { + std::string str = header + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 15 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_comp_error)); +} + +TEST_F(ValidateData, int8_good) { + std::string str = header_with_int8 + "%2 = OpTypeInt 8 0"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, int8_bad) { + std::string str = header + "%2 = OpTypeInt 8 1"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int8_cap_error)); +} + +TEST_F(ValidateData, int8_with_storage_buffer_8bit_access_good) { + std::string str = HeaderWith( + "StorageBuffer8BitAccess " + "OpExtension \"SPV_KHR_8bit_storage\"") + + " %2 = OpTypeInt 8 0"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateData, int8_with_uniform_and_storage_buffer_8bit_access_good) { + std::string str = HeaderWith( + "UniformAndStorageBuffer8BitAccess " + "OpExtension \"SPV_KHR_8bit_storage\"") + + " %2 = OpTypeInt 8 0"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateData, int8_with_storage_push_constant_8_good) { + std::string str = HeaderWith( + "StoragePushConstant8 " + "OpExtension \"SPV_KHR_8bit_storage\"") + + " %2 = OpTypeInt 8 0"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateData, webgpu_int8_bad) { + std::string str = WebGPUHeaderWith("Int8") + "%2 = OpTypeInt 8 0"; + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability Int8 is not allowed by WebGPU specification (or " + "requires extension)\n" + " OpCapability Int8\n")); +} + +TEST_F(ValidateData, int16_good) { + std::string str = header_with_int16 + "%2 = OpTypeInt 16 1"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, storage_uniform_buffer_block_16_good) { + std::string str = HeaderWith( + "StorageUniformBufferBlock16 " + "OpExtension \"SPV_KHR_16bit_storage\"") + + "%2 = OpTypeInt 16 1 %3 = OpTypeFloat 16"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, storage_uniform_16_good) { + std::string str = + HeaderWith("StorageUniform16 OpExtension \"SPV_KHR_16bit_storage\"") + + "%2 = OpTypeInt 16 1 %3 = OpTypeFloat 16"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, storage_push_constant_16_good) { + std::string str = HeaderWith( + "StoragePushConstant16 " + "OpExtension \"SPV_KHR_16bit_storage\"") + + "%2 = OpTypeInt 16 1 %3 = OpTypeFloat 16"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, storage_input_output_16_good) { + std::string str = HeaderWith( + "StorageInputOutput16 " + "OpExtension \"SPV_KHR_16bit_storage\"") + + "%2 = OpTypeInt 16 1 %3 = OpTypeFloat 16"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, amd_gpu_shader_half_float_fetch_16_good) { + std::string str = R"( + OpCapability Shader + OpCapability Linkage + OpExtension "SPV_AMD_gpu_shader_half_float_fetch" + OpMemoryModel Logical GLSL450 + %2 = OpTypeFloat 16)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, int16_bad) { + std::string str = header + "%2 = OpTypeInt 16 1"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int16_cap_error)); +} + +TEST_F(ValidateData, webgpu_int16_bad) { + std::string str = WebGPUHeaderWith("Int16") + "%2 = OpTypeInt 16 1"; + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability Int16 is not allowed by WebGPU specification (or " + "requires extension)\n" + " OpCapability Int16\n")); +} + +TEST_F(ValidateData, webgpu_int32_good) { + std::string str = webgpu_header + R"( + OpEntryPoint Fragment %func "func" + OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 + %void = OpTypeVoid +%func_t = OpTypeFunction %void + %func = OpFunction %void None %func_t + %1 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateData, int64_good) { + std::string str = header_with_int64 + "%2 = OpTypeInt 64 1"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, int64_bad) { + std::string str = header + "%2 = OpTypeInt 64 1"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_int64_cap_error)); +} + +TEST_F(ValidateData, webgpu_int64_bad) { + std::string str = WebGPUHeaderWith("Int64") + "%2 = OpTypeInt 64 1"; + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability Int64 is not allowed by WebGPU specification (or " + "requires extension)\n" + " OpCapability Int64\n")); +} + +// Number of bits in an integer may be only one of: {8,16,32,64} +TEST_F(ValidateData, int_invalid_num_bits) { + std::string str = header + "%2 = OpTypeInt 48 1"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_num_bits_error)); +} + +TEST_F(ValidateData, float16_good) { + std::string str = header_with_float16 + "%2 = OpTypeFloat 16"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, float16_buffer_good) { + std::string str = header_with_float16_buffer + "%2 = OpTypeFloat 16"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, float16_bad) { + std::string str = header + "%2 = OpTypeFloat 16"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float16_cap_error)); +} + +TEST_F(ValidateData, webgpu_float16_bad) { + std::string str = WebGPUHeaderWith("Float16") + "%2 = OpTypeFloat 16"; + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability Float16 is not allowed by WebGPU specification (or " + "requires extension)\n" + " OpCapability Float16\n")); +} + +TEST_F(ValidateData, webgpu_float32_good) { + std::string str = webgpu_header + R"( + OpEntryPoint Fragment %func "func" + OpExecutionMode %func OriginUpperLeft +%float_t = OpTypeFloat 32 + %void = OpTypeVoid + %func_t = OpTypeFunction %void + %func = OpFunction %void None %func_t + %1 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateData, float64_good) { + std::string str = header_with_float64 + "%2 = OpTypeFloat 64"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, float64_bad) { + std::string str = header + "%2 = OpTypeFloat 64"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(missing_float64_cap_error)); +} + +TEST_F(ValidateData, webgpu_float64_bad) { + std::string str = WebGPUHeaderWith("Float64") + "%2 = OpTypeFloat 64"; + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability Float64 is not allowed by WebGPU specification (or " + "requires extension)\n" + " OpCapability Float64\n")); +} + +// Number of bits in a float may be only one of: {16,32,64} +TEST_F(ValidateData, float_invalid_num_bits) { + std::string str = header + "%2 = OpTypeFloat 48"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(invalid_num_bits_error)); +} + +TEST_F(ValidateData, matrix_data_type_float) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 3 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, ids_should_be_validated_before_data) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%mat33 = OpTypeMatrix %vec3 3 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 3[%3] has not been defined")); +} + +TEST_F(ValidateData, matrix_bad_column_type) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%mat33 = OpTypeMatrix %f32 3 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Columns in a matrix must be of type vector")); +} + +TEST_F(ValidateData, matrix_data_type_int) { + std::string str = header + R"( +%int32 = OpTypeInt 32 1 +%vec3 = OpTypeVector %int32 3 +%mat33 = OpTypeMatrix %vec3 3 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("can only be parameterized with floating-point types")); +} + +TEST_F(ValidateData, matrix_data_type_bool) { + std::string str = header + R"( +%boolt = OpTypeBool +%vec3 = OpTypeVector %boolt 3 +%mat33 = OpTypeMatrix %vec3 3 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("can only be parameterized with floating-point types")); +} + +TEST_F(ValidateData, matrix_with_0_columns) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 0 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("can only be parameterized as having only 2, 3, or 4 columns")); +} + +TEST_F(ValidateData, matrix_with_1_column) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 1 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("can only be parameterized as having only 2, 3, or 4 columns")); +} + +TEST_F(ValidateData, matrix_with_2_columns) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 2 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, matrix_with_3_columns) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 3 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, matrix_with_4_columns) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 4 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, matrix_with_5_column) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 5 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("can only be parameterized as having only 2, 3, or 4 columns")); +} + +TEST_F(ValidateData, specialize_int) { + std::string str = header + R"( +%i32 = OpTypeInt 32 1 +%len = OpSpecConstant %i32 2)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, specialize_float) { + std::string str = header + R"( +%f32 = OpTypeFloat 32 +%len = OpSpecConstant %f32 2)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, specialize_boolean) { + std::string str = header + R"( +%2 = OpTypeBool +%3 = OpSpecConstantTrue %2 +%4 = OpSpecConstantFalse %2)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, specialize_boolean_true_to_int) { + std::string str = header + R"( +%2 = OpTypeInt 32 1 +%3 = OpSpecConstantTrue %2)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantTrue Result Type '1[%int]' is not " + "a boolean type")); +} + +TEST_F(ValidateData, specialize_boolean_false_to_int) { + std::string str = header + R"( +%2 = OpTypeInt 32 1 +%4 = OpSpecConstantFalse %2)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantFalse Result Type '1[%int]' is not " + "a boolean type")); +} + +TEST_F(ValidateData, missing_forward_pointer_decl) { + std::string str = header_with_addresses + R"( +%uintt = OpTypeInt 32 0 +%3 = OpTypeStruct %fwd_ptrt %uintt +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 3[%3] requires a previous definition")); +} + +TEST_F(ValidateData, missing_forward_pointer_decl_self_reference) { + std::string str = header_with_addresses + R"( +%uintt = OpTypeInt 32 0 +%3 = OpTypeStruct %3 %uintt +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Operand 2[%_struct_2] requires a previous definition")); +} + +TEST_F(ValidateData, forward_pointer_missing_definition) { + std::string str = header_with_addresses + R"( +OpTypeForwardPointer %_ptr_Generic_struct_A Generic +%uintt = OpTypeInt 32 0 +%struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("forward referenced IDs have not been defined")); +} + +TEST_F(ValidateData, forward_ref_bad_type) { + std::string str = header_with_addresses + R"( +OpTypeForwardPointer %_ptr_Generic_struct_A Generic +%uintt = OpTypeInt 32 0 +%struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A +%_ptr_Generic_struct_A = OpTypeFloat 32 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer type in OpTypeForwardPointer is not a pointer " + "type.\n OpTypeForwardPointer %float Generic\n")); +} + +TEST_F(ValidateData, forward_ref_points_to_non_struct) { + std::string str = header_with_addresses + R"( +OpTypeForwardPointer %_ptr_Generic_struct_A Generic +%uintt = OpTypeInt 32 0 +%struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A +%_ptr_Generic_struct_A = OpTypePointer Generic %uintt +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Forward pointers must point to a structure")); +} + +TEST_F(ValidateData, struct_forward_pointer_good) { + std::string str = header_with_addresses + R"( +OpTypeForwardPointer %_ptr_Generic_struct_A Generic +%uintt = OpTypeInt 32 0 +%struct_B = OpTypeStruct %uintt %_ptr_Generic_struct_A +%struct_C = OpTypeStruct %uintt %struct_B +%struct_A = OpTypeStruct %uintt %struct_C +%_ptr_Generic_struct_A = OpTypePointer Generic %struct_C +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateData, ext_16bit_storage_caps_allow_free_fp_rounding_mode) { + for (const char* cap : {"StorageUniform16", "StorageUniformBufferBlock16"}) { + for (const char* mode : {"RTE", "RTZ", "RTP", "RTN"}) { + std::string str = std::string(R"( + OpCapability Shader + OpCapability Linkage + OpCapability )") + + cap + R"( + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_KHR_variable_pointers" + OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpDecorate %_ FPRoundingMode )" + mode + R"( + %half = OpTypeFloat 16 + %float = OpTypeFloat 32 + %float_1_25 = OpConstant %float 1.25 + %half_ptr = OpTypePointer StorageBuffer %half + %half_ptr_var = OpVariable %half_ptr StorageBuffer + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %main_entry = OpLabel + %_ = OpFConvert %half %float_1_25 + OpStore %half_ptr_var %_ + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + } + } +} + +TEST_F(ValidateData, vulkan_disallow_free_fp_rounding_mode) { + for (const char* mode : {"RTE", "RTZ"}) { + for (const auto env : {SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1}) { + std::string str = std::string(R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_KHR_variable_pointers" + OpMemoryModel Logical GLSL450 + OpDecorate %_ FPRoundingMode )") + + mode + R"( + %half = OpTypeFloat 16 + %float = OpTypeFloat 32 + %float_1_25 = OpConstant %float 1.25 + %half_ptr = OpTypePointer StorageBuffer %half + %half_ptr_var = OpVariable %half_ptr StorageBuffer + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %main_entry = OpLabel + %_ = OpFConvert %half %float_1_25 + OpStore %half_ptr_var %_ + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Operand 2 of Decorate requires one of these capabilities: " + "StorageBuffer16BitAccess UniformAndStorageBuffer16BitAccess " + "StoragePushConstant16 StorageInputOutput16")); + } + } +} + +TEST_F(ValidateData, void_array) { + std::string str = header + R"( + %void = OpTypeVoid + %int = OpTypeInt 32 0 + %int_5 = OpConstant %int 5 + %array = OpTypeArray %void %int_5 + )"; + + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeArray Element Type '1[%void]' is a void type.")); +} + +TEST_F(ValidateData, void_runtime_array) { + std::string str = header + R"( + %void = OpTypeVoid + %array = OpTypeRuntimeArray %void + )"; + + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeRuntimeArray Element Type '1[%void]' is a void type.")); +} + +TEST_F(ValidateData, vulkan_RTA_array_at_end_of_struct) { + std::string str = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %func "func" + OpExecutionMode %func OriginUpperLeft + OpDecorate %array_t ArrayStride 4 + OpMemberDecorate %struct_t 0 Offset 0 + OpMemberDecorate %struct_t 1 Offset 4 + OpDecorate %struct_t Block + %uint_t = OpTypeInt 32 0 + %array_t = OpTypeRuntimeArray %uint_t + %struct_t = OpTypeStruct %uint_t %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t + %2 = OpVariable %struct_ptr StorageBuffer + %void = OpTypeVoid + %func_t = OpTypeFunction %void + %func = OpFunction %void None %func_t + %1 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) { + std::string str = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %func "func" + OpExecutionMode %func OriginUpperLeft + OpDecorate %array_t ArrayStride 4 + OpMemberDecorate %struct_t 0 Offset 0 + OpMemberDecorate %struct_t 1 Offset 4 + OpDecorate %struct_t Block + %uint_t = OpTypeInt 32 0 + %array_t = OpTypeRuntimeArray %uint_t + %struct_t = OpTypeStruct %array_t %uint_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t + %2 = OpVariable %struct_ptr StorageBuffer + %void = OpTypeVoid + %func_t = OpTypeFunction %void + %func = OpFunction %void None %func_t + %1 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str.c_str(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Vulkan, OpTypeRuntimeArray must only be used for " + "the last member of an OpTypeStruct\n %_struct_3 = " + "OpTypeStruct %_runtimearr_uint %uint\n")); +} + +TEST_F(ValidateData, webgpu_RTA_array_at_end_of_struct) { + std::string str = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Fragment %func "func" + OpExecutionMode %func OriginUpperLeft + OpDecorate %array_t ArrayStride 4 + OpMemberDecorate %struct_t 0 Offset 0 + OpMemberDecorate %struct_t 1 Offset 4 + OpDecorate %struct_t Block + %uint_t = OpTypeInt 32 0 + %array_t = OpTypeRuntimeArray %uint_t + %struct_t = OpTypeStruct %uint_t %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t + %2 = OpVariable %struct_ptr StorageBuffer + %void = OpTypeVoid + %func_t = OpTypeFunction %void + %func = OpFunction %void None %func_t + %1 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateData, webgpu_RTA_not_at_end_of_struct) { + std::string str = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Fragment %func "func" + OpExecutionMode %func OriginUpperLeft + OpDecorate %array_t ArrayStride 4 + OpMemberDecorate %struct_t 0 Offset 0 + OpMemberDecorate %struct_t 1 Offset 4 + OpDecorate %struct_t Block + %uint_t = OpTypeInt 32 0 + %array_t = OpTypeRuntimeArray %uint_t + %struct_t = OpTypeStruct %array_t %uint_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t + %2 = OpVariable %struct_ptr StorageBuffer + %void = OpTypeVoid + %func_t = OpTypeFunction %void + %func = OpFunction %void None %func_t + %1 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In WebGPU, OpTypeRuntimeArray must only be used for " + "the last member of an OpTypeStruct\n %_struct_3 = " + "OpTypeStruct %_runtimearr_uint %uint\n")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_decoration_test.cpp b/third_party/spirv-tools/test/val/val_decoration_test.cpp new file mode 100644 index 0000000..e646162 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_decoration_test.cpp @@ -0,0 +1,7164 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for decorations + +#include +#include + +#include "gmock/gmock.h" +#include "source/val/decoration.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Values; + +struct TestResult { + TestResult(spv_result_t in_validation_result = SPV_SUCCESS, + const std::string& in_error_str = "") + : validation_result(in_validation_result), error_str(in_error_str) {} + spv_result_t validation_result; + const std::string error_str; +}; + +using ValidateDecorations = spvtest::ValidateBase; +using ValidateWebGPUCombineDecorationResult = + spvtest::ValidateBase>; + +TEST_F(ValidateDecorations, ValidateOpDecorateRegistration) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %1 ArrayStride 4 + OpDecorate %1 RelaxedPrecision + %2 = OpTypeFloat 32 + %1 = OpTypeRuntimeArray %2 + ; Since %1 is used first in Decoration, it gets id 1. +)"; + const uint32_t id = 1; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + // Must have 2 decorations. + EXPECT_THAT( + vstate_->id_decorations(id), + Eq(std::vector{Decoration(SpvDecorationArrayStride, {4}), + Decoration(SpvDecorationRelaxedPrecision)})); +} + +TEST_F(ValidateDecorations, ValidateOpMemberDecorateRegistration) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %_arr_double_uint_6 ArrayStride 4 + OpMemberDecorate %_struct_115 2 NonReadable + OpMemberDecorate %_struct_115 2 Offset 2 + OpDecorate %_struct_115 BufferBlock + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_6 = OpConstant %uint 6 + %_arr_double_uint_6 = OpTypeArray %float %uint_6 + %_struct_115 = OpTypeStruct %float %float %_arr_double_uint_6 +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + + // The array must have 1 decoration. + const uint32_t arr_id = 1; + EXPECT_THAT( + vstate_->id_decorations(arr_id), + Eq(std::vector{Decoration(SpvDecorationArrayStride, {4})})); + + // The struct must have 3 decorations. + const uint32_t struct_id = 2; + EXPECT_THAT( + vstate_->id_decorations(struct_id), + Eq(std::vector{Decoration(SpvDecorationNonReadable, {}, 2), + Decoration(SpvDecorationOffset, {2}, 2), + Decoration(SpvDecorationBufferBlock)})); +} + +TEST_F(ValidateDecorations, ValidateOpMemberDecorateOutOfBound) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "Main" + OpExecutionMode %1 OriginUpperLeft + OpMemberDecorate %_struct_2 1 RelaxedPrecision + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %_struct_2 = OpTypeStruct %float + %1 = OpFunction %void None %4 + %6 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index 1 provided in OpMemberDecorate for struct " + "2[%_struct_2] is out of bounds. The structure has 1 " + "members. Largest valid index is 0.")); +} + +TEST_F(ValidateDecorations, ValidateGroupDecorateRegistration) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %1 DescriptorSet 0 + OpDecorate %1 RelaxedPrecision + OpDecorate %1 Restrict + %1 = OpDecorationGroup + OpGroupDecorate %1 %2 %3 + OpGroupDecorate %1 %4 + %float = OpTypeFloat 32 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_9 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 + %2 = OpVariable %_ptr_Uniform__struct_9 Uniform + %_struct_10 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_10 = OpTypePointer Uniform %_struct_10 + %3 = OpVariable %_ptr_Uniform__struct_10 Uniform + %_struct_11 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_11 = OpTypePointer Uniform %_struct_11 + %4 = OpVariable %_ptr_Uniform__struct_11 Uniform + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + + // Decoration group has 3 decorations. + auto expected_decorations = + std::vector{Decoration(SpvDecorationDescriptorSet, {0}), + Decoration(SpvDecorationRelaxedPrecision), + Decoration(SpvDecorationRestrict)}; + + // Decoration group is applied to id 1, 2, 3, and 4. Note that id 1 (which is + // the decoration group id) also has all the decorations. + EXPECT_THAT(vstate_->id_decorations(1), Eq(expected_decorations)); + EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations)); + EXPECT_THAT(vstate_->id_decorations(3), Eq(expected_decorations)); + EXPECT_THAT(vstate_->id_decorations(4), Eq(expected_decorations)); +} + +TEST_F(ValidateDecorations, WebGPUOpDecorationGroupBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpDecorate %1 DescriptorSet 0 + OpDecorate %1 NonWritable + OpDecorate %1 Restrict + %1 = OpDecorationGroup + OpGroupDecorate %1 %2 %3 + OpGroupDecorate %1 %4 + %float = OpTypeFloat 32 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_9 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 + %2 = OpVariable %_ptr_Uniform__struct_9 Uniform + %_struct_10 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_10 = OpTypePointer Uniform %_struct_10 + %3 = OpVariable %_ptr_Uniform__struct_10 Uniform + %_struct_11 = OpTypeStruct %_runtimearr_float +%_ptr_Uniform__struct_11 = OpTypePointer Uniform %_struct_11 + %4 = OpVariable %_ptr_Uniform__struct_11 Uniform + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpDecorationGroup is not allowed in the WebGPU " + "execution environment.\n %1 = OpDecorationGroup\n")); +} + +// For WebGPU, OpGroupDecorate does not have a test case, because it requires +// being preceded by OpDecorationGroup, which will cause a validation error. + +// For WebGPU, OpGroupMemberDecorate does not have a test case, because it +// requires being preceded by OpDecorationGroup, which will cause a validation +// error. + +TEST_F(ValidateDecorations, ValidateGroupMemberDecorateRegistration) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %1 Offset 3 + %1 = OpDecorationGroup + OpGroupMemberDecorate %1 %_struct_1 3 %_struct_2 3 %_struct_3 3 + %float = OpTypeFloat 32 +%_runtimearr = OpTypeRuntimeArray %float + %_struct_1 = OpTypeStruct %float %float %float %_runtimearr + %_struct_2 = OpTypeStruct %float %float %float %_runtimearr + %_struct_3 = OpTypeStruct %float %float %float %_runtimearr + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + // Decoration group has 1 decoration. + auto expected_decorations = + std::vector{Decoration(SpvDecorationOffset, {3}, 3)}; + + // Decoration group is applied to id 2, 3, and 4. + EXPECT_THAT(vstate_->id_decorations(2), Eq(expected_decorations)); + EXPECT_THAT(vstate_->id_decorations(3), Eq(expected_decorations)); + EXPECT_THAT(vstate_->id_decorations(4), Eq(expected_decorations)); +} + +TEST_F(ValidateDecorations, LinkageImportUsedForInitializedVariableBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %target LinkageAttributes "link_ptr" Import + %float = OpTypeFloat 32 + %_ptr_float = OpTypePointer Uniform %float + %zero = OpConstantNull %float + %target = OpVariable %_ptr_float Uniform %zero + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("A module-scope OpVariable with initialization value " + "cannot be marked with the Import Linkage Type.")); +} +TEST_F(ValidateDecorations, LinkageExportUsedForInitializedVariableGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %target LinkageAttributes "link_ptr" Export + %float = OpTypeFloat 32 + %_ptr_float = OpTypePointer Uniform %float + %zero = OpConstantNull %float + %target = OpVariable %_ptr_float Uniform %zero + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, StructAllMembersHaveBuiltInDecorationsGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpMemberDecorate %_struct_1 0 BuiltIn Position + OpMemberDecorate %_struct_1 1 BuiltIn Position + OpMemberDecorate %_struct_1 2 BuiltIn Position + OpMemberDecorate %_struct_1 3 BuiltIn Position + %float = OpTypeFloat 32 +%_runtimearr = OpTypeRuntimeArray %float + %_struct_1 = OpTypeStruct %float %float %float %_runtimearr + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, MixedBuiltInDecorationsBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpMemberDecorate %_struct_1 0 BuiltIn Position + OpMemberDecorate %_struct_1 1 BuiltIn Position + %float = OpTypeFloat 32 +%_runtimearr = OpTypeRuntimeArray %float + %_struct_1 = OpTypeStruct %float %float %float %_runtimearr + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("When BuiltIn decoration is applied to a structure-type " + "member, all members of that structure type must also be " + "decorated with BuiltIn (No allowed mixing of built-in " + "variables and non-built-in variables within a single " + "structure). Structure id 1 does not meet this requirement.")); +} + +TEST_F(ValidateDecorations, StructContainsBuiltInStructBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpMemberDecorate %_struct_1 0 BuiltIn Position + OpMemberDecorate %_struct_1 1 BuiltIn Position + OpMemberDecorate %_struct_1 2 BuiltIn Position + OpMemberDecorate %_struct_1 3 BuiltIn Position + %float = OpTypeFloat 32 +%_runtimearr = OpTypeRuntimeArray %float + %_struct_1 = OpTypeStruct %float %float %float %_runtimearr + %_struct_2 = OpTypeStruct %_struct_1 + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure 1[%_struct_1] contains members with " + "BuiltIn decoration. Therefore this structure may not " + "be contained as a member of another structure type. " + "Structure 4[%_struct_4] contains structure " + "1[%_struct_1].")); +} + +TEST_F(ValidateDecorations, StructContainsNonBuiltInStructGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %float = OpTypeFloat 32 + %_struct_1 = OpTypeStruct %float + %_struct_2 = OpTypeStruct %_struct_1 + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, MultipleBuiltInObjectsConsumedByOpEntryPointBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Geometry + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %in_1 %in_2 + OpExecutionMode %main InputPoints + OpExecutionMode %main OutputPoints + OpMemberDecorate %struct_1 0 BuiltIn InvocationId + OpMemberDecorate %struct_2 0 BuiltIn Position + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct_1 = OpTypeStruct %int + %struct_2 = OpTypeStruct %float +%ptr_builtin_1 = OpTypePointer Input %struct_1 +%ptr_builtin_2 = OpTypePointer Input %struct_2 +%in_1 = OpVariable %ptr_builtin_1 Input +%in_2 = OpVariable %ptr_builtin_2 Input + %main = OpFunction %void None %func + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("There must be at most one object per Storage Class " + "that can contain a structure type containing members " + "decorated with BuiltIn, consumed per entry-point.")); +} + +TEST_F(ValidateDecorations, + OneBuiltInObjectPerStorageClassConsumedByOpEntryPointGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Geometry + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %in_1 %out_1 + OpExecutionMode %main InputPoints + OpExecutionMode %main OutputPoints + OpMemberDecorate %struct_1 0 BuiltIn InvocationId + OpMemberDecorate %struct_2 0 BuiltIn Position + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct_1 = OpTypeStruct %int + %struct_2 = OpTypeStruct %float +%ptr_builtin_1 = OpTypePointer Input %struct_1 +%ptr_builtin_2 = OpTypePointer Output %struct_2 +%in_1 = OpVariable %ptr_builtin_1 Input +%out_1 = OpVariable %ptr_builtin_2 Output + %main = OpFunction %void None %func + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, NoBuiltInObjectsConsumedByOpEntryPointGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Geometry + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %in_1 %out_1 + OpExecutionMode %main InputPoints + OpExecutionMode %main OutputPoints + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct_1 = OpTypeStruct %int + %struct_2 = OpTypeStruct %float +%ptr_builtin_1 = OpTypePointer Input %struct_1 +%ptr_builtin_2 = OpTypePointer Output %struct_2 +%in_1 = OpVariable %ptr_builtin_1 Input +%out_1 = OpVariable %ptr_builtin_2 Output + %main = OpFunction %void None %func + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, EntryPointFunctionHasLinkageAttributeBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpDecorate %main LinkageAttributes "import_main" Import +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%main = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The LinkageAttributes Decoration (Linkage name: import_main) " + "cannot be applied to function id 1 because it is targeted by " + "an OpEntryPoint instruction.")); +} + +TEST_F(ValidateDecorations, FunctionDeclarationWithoutImportLinkageBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Function declaration (id 3) must have a LinkageAttributes " + "decoration with the Import Linkage type.")); +} + +TEST_F(ValidateDecorations, FunctionDeclarationWithImportLinkageGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %main LinkageAttributes "link_fn" Import + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, FunctionDeclarationWithExportLinkageBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %main LinkageAttributes "link_fn" Export + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Function declaration (id 1) must have a LinkageAttributes " + "decoration with the Import Linkage type.")); +} + +TEST_F(ValidateDecorations, FunctionDefinitionWithImportLinkageBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %main LinkageAttributes "link_fn" Import + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Function definition (id 1) may not be decorated with " + "Import Linkage type.")); +} + +TEST_F(ValidateDecorations, FunctionDefinitionWithoutImportLinkageGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %func = OpTypeFunction %void + %main = OpFunction %void None %func + %label = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, BuiltinVariablesGoodVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %_entryPointOutput Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%float_0 = OpConstant %float 0 +%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %3 +%5 = OpLabel +OpStore %_entryPointOutput %14 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); +} + +TEST_F(ValidateDecorations, BuiltinVariablesWithLocationDecorationVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %gl_FragCoord Location 0 +OpDecorate %_entryPointOutput Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%float_0 = OpConstant %float 0 +%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %3 +%5 = OpLabel +OpStore %_entryPointOutput %14 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " + "Component decorations")); +} +TEST_F(ValidateDecorations, BuiltinVariablesWithComponentDecorationVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragCoord %_entryPointOutput +OpExecutionMode %main OriginUpperLeft +OpSource HLSL 500 +OpDecorate %gl_FragCoord BuiltIn FragCoord +OpDecorate %gl_FragCoord Component 0 +OpDecorate %_entryPointOutput Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%float_0 = OpConstant %float 0 +%14 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_entryPointOutput = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %3 +%5 = OpLabel +OpStore %_entryPointOutput %14 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("A BuiltIn variable (id 2) cannot have any Location or " + "Component decorations")); +} + +// #version 440 +// #extension GL_EXT_nonuniform_qualifier : enable +// layout(binding = 1) uniform sampler2D s2d[]; +// layout(location = 0) in nonuniformEXT int i; +// void main() +// { +// vec4 v = texture(s2d[i], vec2(0.3)); +// } +TEST_F(ValidateDecorations, RuntimeArrayOfDescriptorSetsIsAllowed) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( + OpCapability Shader + OpCapability ShaderNonUniformEXT + OpCapability RuntimeDescriptorArrayEXT + OpCapability SampledImageArrayNonUniformIndexingEXT + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %i + OpSource GLSL 440 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %v "v" + OpName %s2d "s2d" + OpName %i "i" + OpDecorate %s2d DescriptorSet 0 + OpDecorate %s2d Binding 1 + OpDecorate %i Location 0 + OpDecorate %i NonUniformEXT + OpDecorate %18 NonUniformEXT + OpDecorate %21 NonUniformEXT + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 +%_runtimearr_11 = OpTypeRuntimeArray %11 +%_ptr_Uniform__runtimearr_11 = OpTypePointer Uniform %_runtimearr_11 + %s2d = OpVariable %_ptr_Uniform__runtimearr_11 Uniform + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %i = OpVariable %_ptr_Input_int Input +%_ptr_Uniform_11 = OpTypePointer Uniform %11 + %v2float = OpTypeVector %float 2 +%float_0_300000012 = OpConstant %float 0.300000012 + %24 = OpConstantComposite %v2float %float_0_300000012 %float_0_300000012 + %float_0 = OpConstant %float 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %v = OpVariable %_ptr_Function_v4float Function + %18 = OpLoad %int %i + %20 = OpAccessChain %_ptr_Uniform_11 %s2d %18 + %21 = OpLoad %11 %20 + %26 = OpImageSampleExplicitLod %v4float %21 %24 Lod %float_0 + OpStore %v %26 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, BlockDecoratingArrayBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %Output = OpTypeArray %float %int_3 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Block decoration on a non-struct type")); +} + +TEST_F(ValidateDecorations, BlockDecoratingIntBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %Output = OpTypeInt 32 1 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Block decoration on a non-struct type")); +} + +TEST_F(ValidateDecorations, BlockMissingOffsetBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Output = OpTypeStruct %float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be explicitly laid out with Offset decorations")); +} + +TEST_F(ValidateDecorations, BufferBlockMissingOffsetBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Output = OpTypeStruct %float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be explicitly laid out with Offset decorations")); +} + +TEST_F(ValidateDecorations, BlockNestedStructMissingOffsetBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %S = OpTypeStruct %v3float %int + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be explicitly laid out with Offset decorations")); +} + +TEST_F(ValidateDecorations, BufferBlockNestedStructMissingOffsetBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %S = OpTypeStruct %v3float %int + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must be explicitly laid out with Offset decorations")); +} + +TEST_F(ValidateDecorations, BlockGLSLSharedBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + OpDecorate %Output GLSLShared + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Output = OpTypeStruct %float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not use GLSLShared decoration")); +} + +TEST_F(ValidateDecorations, BufferBlockGLSLSharedBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output BufferBlock + OpDecorate %Output GLSLShared + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Output = OpTypeStruct %float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not use GLSLShared decoration")); +} + +TEST_F(ValidateDecorations, BlockNestedStructGLSLSharedBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %S GLSLShared + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %S = OpTypeStruct %int + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not use GLSLShared decoration")); +} + +TEST_F(ValidateDecorations, BufferBlockNestedStructGLSLSharedBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %S GLSLShared + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %S = OpTypeStruct %int + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not use GLSLShared decoration")); +} + +TEST_F(ValidateDecorations, BlockGLSLPackedBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + OpDecorate %Output GLSLPacked + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Output = OpTypeStruct %float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not use GLSLPacked decoration")); +} + +TEST_F(ValidateDecorations, BufferBlockGLSLPackedBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output BufferBlock + OpDecorate %Output GLSLPacked + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %Output = OpTypeStruct %float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not use GLSLPacked decoration")); +} + +TEST_F(ValidateDecorations, BlockNestedStructGLSLPackedBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %S GLSLPacked + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %S = OpTypeStruct %int + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not use GLSLPacked decoration")); +} + +TEST_F(ValidateDecorations, BufferBlockNestedStructGLSLPackedBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpDecorate %S GLSLPacked + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %S = OpTypeStruct %int + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("must not use GLSLPacked decoration")); +} + +TEST_F(ValidateDecorations, BlockMissingArrayStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %array = OpTypeArray %float %int_3 + %Output = OpTypeStruct %array +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with ArrayStride decorations")); +} + +TEST_F(ValidateDecorations, BufferBlockMissingArrayStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output BufferBlock + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %array = OpTypeArray %float %int_3 + %Output = OpTypeStruct %array +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with ArrayStride decorations")); +} + +TEST_F(ValidateDecorations, BlockNestedStructMissingArrayStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %array = OpTypeArray %float %int_3 + %S = OpTypeStruct %array + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with ArrayStride decorations")); +} + +TEST_F(ValidateDecorations, BufferBlockNestedStructMissingArrayStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %array = OpTypeArray %float %int_3 + %S = OpTypeStruct %array + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with ArrayStride decorations")); +} + +TEST_F(ValidateDecorations, BlockMissingMatrixStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %matrix = OpTypeMatrix %v3float 4 + %Output = OpTypeStruct %matrix +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with MatrixStride decorations")); +} + +TEST_F(ValidateDecorations, BufferBlockMissingMatrixStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output BufferBlock + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %matrix = OpTypeMatrix %v3float 4 + %Output = OpTypeStruct %matrix +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with MatrixStride decorations")); +} + +TEST_F(ValidateDecorations, BlockMissingMatrixStrideArrayBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output Block + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %matrix = OpTypeMatrix %v3float 4 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %array = OpTypeArray %matrix %int_3 + %Output = OpTypeStruct %matrix +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with MatrixStride decorations")); +} + +TEST_F(ValidateDecorations, BufferBlockMissingMatrixStrideArrayBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %Output BufferBlock + OpMemberDecorate %Output 0 Offset 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %matrix = OpTypeMatrix %v3float 4 + %int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3 + %array = OpTypeArray %matrix %int_3 + %Output = OpTypeStruct %matrix +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with MatrixStride decorations")); +} + +TEST_F(ValidateDecorations, BlockNestedStructMissingMatrixStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %matrix = OpTypeMatrix %v3float 4 + %S = OpTypeStruct %matrix + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with MatrixStride decorations")); +} + +TEST_F(ValidateDecorations, BufferBlockNestedStructMissingMatrixStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 16 + OpMemberDecorate %Output 2 Offset 32 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + %matrix = OpTypeMatrix %v3float 4 + %S = OpTypeStruct %matrix + %Output = OpTypeStruct %float %v4float %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("must be explicitly laid out with MatrixStride decorations")); +} + +TEST_F(ValidateDecorations, BlockStandardUniformBufferLayout) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %F 0 Offset 0 + OpMemberDecorate %F 1 Offset 8 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 + OpMemberDecorate %O 0 Offset 0 + OpMemberDecorate %O 1 Offset 16 + OpMemberDecorate %O 2 Offset 32 + OpMemberDecorate %O 3 Offset 64 + OpMemberDecorate %O 4 ColMajor + OpMemberDecorate %O 4 Offset 80 + OpMemberDecorate %O 4 MatrixStride 16 + OpDecorate %_arr_O_uint_2 ArrayStride 176 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 8 + OpMemberDecorate %Output 2 Offset 16 + OpMemberDecorate %Output 3 Offset 32 + OpMemberDecorate %Output 4 Offset 48 + OpMemberDecorate %Output 5 Offset 64 + OpMemberDecorate %Output 6 ColMajor + OpMemberDecorate %Output 6 Offset 96 + OpMemberDecorate %Output 6 MatrixStride 16 + OpMemberDecorate %Output 7 Offset 128 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %F = OpTypeStruct %int %v2uint + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%mat2v3float = OpTypeMatrix %v3float 2 + %v3uint = OpTypeVector %uint 3 +%mat3v3float = OpTypeMatrix %v3float 3 +%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 + %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 +%_arr_O_uint_2 = OpTypeArray %O %uint_2 + %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, BlockLayoutPermitsTightVec3ScalarPackingGood) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 12 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %v3float %float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, BlockCantAppearWithinABlockBad) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587 + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 16 + OpMemberDecorate %S2 0 Offset 0 + OpMemberDecorate %S2 1 Offset 12 + OpDecorate %S Block + OpDecorate %S2 Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %S2 = OpTypeStruct %float %float + %S = OpTypeStruct %float %S2 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("rules: A Block or BufferBlock cannot be nested within " + "another Block or BufferBlock.")); +} + +TEST_F(ValidateDecorations, BufferblockCantAppearWithinABufferblockBad) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587 + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 16 + OpMemberDecorate %S2 0 Offset 0 + OpMemberDecorate %S2 1 Offset 16 + OpMemberDecorate %S3 0 Offset 0 + OpMemberDecorate %S3 1 Offset 12 + OpDecorate %S BufferBlock + OpDecorate %S3 BufferBlock + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %S3 = OpTypeStruct %float %float + %S2 = OpTypeStruct %float %S3 + %S = OpTypeStruct %float %S2 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("rules: A Block or BufferBlock cannot be nested within " + "another Block or BufferBlock.")); +} + +TEST_F(ValidateDecorations, BufferblockCantAppearWithinABlockBad) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587 + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 16 + OpMemberDecorate %S2 0 Offset 0 + OpMemberDecorate %S2 1 Offset 16 + OpMemberDecorate %S3 0 Offset 0 + OpMemberDecorate %S3 1 Offset 12 + OpDecorate %S Block + OpDecorate %S3 BufferBlock + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %S3 = OpTypeStruct %float %float + %S2 = OpTypeStruct %float %S3 + %S = OpTypeStruct %float %S2 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("rules: A Block or BufferBlock cannot be nested within " + "another Block or BufferBlock.")); +} + +TEST_F(ValidateDecorations, BlockCantAppearWithinABufferblockBad) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1587 + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 16 + OpMemberDecorate %S2 0 Offset 0 + OpMemberDecorate %S2 1 Offset 16 + OpMemberDecorate %S3 0 Offset 0 + OpMemberDecorate %S3 1 Offset 16 + OpMemberDecorate %S4 0 Offset 0 + OpMemberDecorate %S4 1 Offset 12 + OpDecorate %S BufferBlock + OpDecorate %S4 Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %S4 = OpTypeStruct %float %float + %S3 = OpTypeStruct %float %S4 + %S2 = OpTypeStruct %float %S3 + %S = OpTypeStruct %float %S2 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("rules: A Block or BufferBlock cannot be nested within " + "another Block or BufferBlock.")); +} + +TEST_F(ValidateDecorations, BlockLayoutForbidsTightScalarVec3PackingBad) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Structure id 2 decorated as Block for variable in Uniform " + "storage class must follow standard uniform buffer layout " + "rules: member 1 at offset 4 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, + BlockLayoutPermitsTightScalarVec3PackingWithRelaxedLayoutGood) { + // Same as previous test, but with explicit option to relax block layout. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, + BlockLayoutPermitsTightScalarVec3PackingBadOffsetWithRelaxedLayoutBad) { + // Same as previous test, but with the vector not aligned to its scalar + // element. Use offset 5 instead of a multiple of 4. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 5 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in Uniform storage " + "class must follow relaxed uniform buffer layout rules: member 1 at " + "offset 5 is not aligned to scalar element size 4")); +} + +TEST_F(ValidateDecorations, + BlockLayoutPermitsTightScalarVec3PackingWithVulkan1_1Good) { + // Same as previous test, but with Vulkan 1.1. Vulkan 1.1 included + // VK_KHR_relaxed_block_layout in core. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, + BlockLayoutPermitsTightScalarVec3PackingWithScalarLayoutGood) { + // Same as previous test, but with scalar block layout. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, + BlockLayoutPermitsScalarAlignedArrayWithScalarLayoutGood) { + // The array at offset 4 is ok with scalar block layout. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + OpDecorate %arr_float ArrayStride 4 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 + %arr_float = OpTypeArray %float %uint_3 + %S = OpTypeStruct %float %arr_float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, + BlockLayoutPermitsScalarAlignedArrayOfVec3WithScalarLayoutGood) { + // The array at offset 4 is ok with scalar block layout, even though + // its elements are vec3. + // This is the same as the previous case, but the array elements are vec3 + // instead of float. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + OpDecorate %arr_vec3 ArrayStride 12 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %float = OpTypeFloat 32 + %vec3 = OpTypeVector %float 3 + %arr_vec3 = OpTypeArray %vec3 %uint_3 + %S = OpTypeStruct %float %arr_vec3 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, + BlockLayoutPermitsScalarAlignedStructWithScalarLayoutGood) { + // Scalar block layout permits the struct at offset 4, even though + // it contains a vector with base alignment 8 and scalar alignment 4. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpMemberDecorate %st 0 Offset 0 + OpMemberDecorate %st 1 Offset 8 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec2 = OpTypeVector %float 2 + %st = OpTypeStruct %vec2 %float + %S = OpTypeStruct %float %st +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F( + ValidateDecorations, + BlockLayoutPermitsFieldsInBaseAlignmentPaddingAtEndOfStructWithScalarLayoutGood) { + // Scalar block layout permits fields in what would normally be the padding at + // the end of a struct. + std::string spirv = R"( + OpCapability Shader + OpCapability Float64 + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %st 0 Offset 0 + OpMemberDecorate %st 1 Offset 8 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 12 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %double = OpTypeFloat 64 + %st = OpTypeStruct %double %float + %S = OpTypeStruct %st %float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F( + ValidateDecorations, + BlockLayoutPermitsStraddlingVectorWithScalarLayoutOverrideRelaxBlockLayoutGood) { + // Same as previous, but set relaxed block layout first. Scalar layout always + // wins. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec4 = OpTypeVector %float 4 + %S = OpTypeStruct %float %vec4 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); + spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F( + ValidateDecorations, + BlockLayoutPermitsStraddlingVectorWithRelaxedLayoutOverridenByScalarBlockLayoutGood) { + // Same as previous, but set scalar block layout first. Scalar layout always + // wins. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %vec4 = OpTypeVector %float 4 + %S = OpTypeStruct %float %vec4 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetScalarBlockLayout(getValidatorOptions(), true); + spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, BufferBlock16bitStandardStorageBufferLayout) { + std::string spirv = R"( + OpCapability Shader + OpCapability StorageUniform16 + OpExtension "SPV_KHR_16bit_storage" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpDecorate %f32arr ArrayStride 4 + OpDecorate %f16arr ArrayStride 2 + OpMemberDecorate %SSBO32 0 Offset 0 + OpMemberDecorate %SSBO16 0 Offset 0 + OpDecorate %SSBO32 BufferBlock + OpDecorate %SSBO16 BufferBlock + %void = OpTypeVoid + %voidf = OpTypeFunction %void + %u32 = OpTypeInt 32 0 + %i32 = OpTypeInt 32 1 + %f32 = OpTypeFloat 32 + %uvec3 = OpTypeVector %u32 3 + %c_i32_32 = OpConstant %i32 32 +%c_i32_128 = OpConstant %i32 128 + %f32arr = OpTypeArray %f32 %c_i32_128 + %f16 = OpTypeFloat 16 + %f16arr = OpTypeArray %f16 %c_i32_128 + %SSBO32 = OpTypeStruct %f32arr + %SSBO16 = OpTypeStruct %f16arr +%_ptr_Uniform_SSBO32 = OpTypePointer Uniform %SSBO32 + %varSSBO32 = OpVariable %_ptr_Uniform_SSBO32 Uniform +%_ptr_Uniform_SSBO16 = OpTypePointer Uniform %SSBO16 + %varSSBO16 = OpVariable %_ptr_Uniform_SSBO16 Uniform + %main = OpFunction %void None %voidf + %label = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, BlockArrayExtendedAlignmentGood) { + // For uniform buffer, Array base alignment is 16, and ArrayStride + // must be a multiple of 16. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 16 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_PushConstant_S = OpTypePointer PushConstant %S + %u = OpVariable %_ptr_PushConstant_S PushConstant + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, BlockArrayBaseAlignmentBad) { + // For uniform buffer, Array base alignment is 16. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 8 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %u = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 3 decorated as Block for variable in Uniform " + "storage class must follow standard uniform buffer layout rules: " + "member 1 at offset 8 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithRelaxedLayoutStillBad) { + // For uniform buffer, Array base alignment is 16, and ArrayStride + // must be a multiple of 16. This case uses relaxed block layout. Relaxed + // layout only relaxes rules for vector alignment, not array alignment. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpDecorate %u DescriptorSet 0 + OpDecorate %u Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 8 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %u = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + spvValidatorOptionsSetRelaxBlockLayout(getValidatorOptions(), true); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 4 decorated as Block for variable in Uniform " + "storage class must follow standard uniform buffer layout rules: " + "member 1 at offset 8 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, BlockArrayBaseAlignmentWithVulkan1_1StillBad) { + // Same as previous test, but with Vulkan 1.1, which includes + // VK_KHR_relaxed_block_layout in core. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpDecorate %u DescriptorSet 0 + OpDecorate %u Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 8 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %u = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 4 decorated as Block for variable in Uniform " + "storage class must follow relaxed uniform buffer layout rules: " + "member 1 at offset 8 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, + BlockArrayBaseAlignmentWithBlockStandardLayoutGood) { + // Same as previous test, but with VK_KHR_uniform_buffer_standard_layout + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpDecorate %u DescriptorSet 0 + OpDecorate %u Binding 0 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 8 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_Uniform_S = OpTypePointer Uniform %S + %u = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetUniformBufferStandardLayout(getValidatorOptions(), + true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, VulkanBufferBlockOnStorageBufferBad) { + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct BufferBlock + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer StorageBuffer %struct + %var = OpVariable %ptr StorageBuffer + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Vulkan, BufferBlock is disallowed on variables in " + "the StorageBuffer storage class")); +} + +TEST_F(ValidateDecorations, PushConstantArrayBaseAlignmentGood) { + // Tests https://github.com/KhronosGroup/SPIRV-Tools/issues/1664 + // From GLSL vertex shader: + // #version 450 + // layout(push_constant) uniform S { vec2 v; float arr[2]; } u; + // void main() { } + + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 4 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 8 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_PushConstant_S = OpTypePointer PushConstant %S + %u = OpVariable %_ptr_PushConstant_S PushConstant + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, PushConstantArrayBadAlignmentBad) { + // Like the previous test, but with offset 7 instead of 8. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 4 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 7 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_PushConstant_S = OpTypePointer PushConstant %S + %u = OpVariable %_ptr_PushConstant_S PushConstant + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 3 decorated as Block for variable in PushConstant " + "storage class must follow standard storage buffer layout rules: " + "member 1 at offset 7 is not aligned to 4")); +} + +TEST_F(ValidateDecorations, + PushConstantLayoutPermitsTightVec3ScalarPackingGood) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 12 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %v3float %float +%_ptr_PushConstant_S = OpTypePointer PushConstant %S + %B = OpVariable %_ptr_PushConstant_S PushConstant + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, + PushConstantLayoutForbidsTightScalarVec3PackingBad) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float +%_ptr_Uniform_S = OpTypePointer PushConstant %S + %B = OpVariable %_ptr_Uniform_S PushConstant + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in PushConstant " + "storage class must follow standard storage buffer layout " + "rules: member 1 at offset 4 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, PushConstantMissingBlockGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %pc = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, VulkanPushConstantMissingBlockBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %pc = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("PushConstant id '2' is missing Block decoration.\n" + "From Vulkan spec, section 14.5.1:\n" + "Such variables must be identified with a Block " + "decoration")); +} + +TEST_F(ValidateDecorations, MultiplePushConstantsSingleEntryPointGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %ptr_float = OpTypePointer PushConstant %float + %pc1 = OpVariable %ptr PushConstant + %pc2 = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %2 = OpAccessChain %ptr_float %pc1 %int_0 + %3 = OpLoad %float %2 + %4 = OpAccessChain %ptr_float %pc2 %int_0 + %5 = OpLoad %float %4 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, + VulkanMultiplePushConstantsDifferentEntryPointGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "func1" + OpEntryPoint Fragment %2 "func2" + OpExecutionMode %2 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %ptr_float = OpTypePointer PushConstant %float + %pc1 = OpVariable %ptr PushConstant + %pc2 = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label1 = OpLabel + %3 = OpAccessChain %ptr_float %pc1 %int_0 + %4 = OpLoad %float %3 + OpReturn + OpFunctionEnd + + %2 = OpFunction %void None %voidfn + %label2 = OpLabel + %5 = OpAccessChain %ptr_float %pc2 %int_0 + %6 = OpLoad %float %5 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, + VulkanMultiplePushConstantsUnusedSingleEntryPointGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %ptr_float = OpTypePointer PushConstant %float + %pc1 = OpVariable %ptr PushConstant + %pc2 = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, VulkanMultiplePushConstantsSingleEntryPointBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %ptr_float = OpTypePointer PushConstant %float + %pc1 = OpVariable %ptr PushConstant + %pc2 = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %2 = OpAccessChain %ptr_float %pc1 %int_0 + %3 = OpLoad %float %2 + %4 = OpAccessChain %ptr_float %pc2 %int_0 + %5 = OpLoad %float %4 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Entry point id '1' uses more than one PushConstant interface.\n" + "From Vulkan spec, section 14.5.1:\n" + "There must be no more than one push constant block " + "statically used per shader entry point.")); +} + +TEST_F(ValidateDecorations, + VulkanMultiplePushConstantsDifferentEntryPointSubFunctionGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "func1" + OpEntryPoint Fragment %2 "func2" + OpExecutionMode %2 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %ptr_float = OpTypePointer PushConstant %float + %pc1 = OpVariable %ptr PushConstant + %pc2 = OpVariable %ptr PushConstant + + %sub1 = OpFunction %void None %voidfn + %label_sub1 = OpLabel + %3 = OpAccessChain %ptr_float %pc1 %int_0 + %4 = OpLoad %float %3 + OpReturn + OpFunctionEnd + + %sub2 = OpFunction %void None %voidfn + %label_sub2 = OpLabel + %5 = OpAccessChain %ptr_float %pc2 %int_0 + %6 = OpLoad %float %5 + OpReturn + OpFunctionEnd + + %1 = OpFunction %void None %voidfn + %label1 = OpLabel + %call1 = OpFunctionCall %void %sub1 + OpReturn + OpFunctionEnd + + %2 = OpFunction %void None %voidfn + %label2 = OpLabel + %call2 = OpFunctionCall %void %sub2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, + VulkanMultiplePushConstantsSingleEntryPointSubFunctionBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %ptr_float = OpTypePointer PushConstant %float + %pc1 = OpVariable %ptr PushConstant + %pc2 = OpVariable %ptr PushConstant + + %sub1 = OpFunction %void None %voidfn + %label_sub1 = OpLabel + %3 = OpAccessChain %ptr_float %pc1 %int_0 + %4 = OpLoad %float %3 + OpReturn + OpFunctionEnd + + %sub2 = OpFunction %void None %voidfn + %label_sub2 = OpLabel + %5 = OpAccessChain %ptr_float %pc2 %int_0 + %6 = OpLoad %float %5 + OpReturn + OpFunctionEnd + + %1 = OpFunction %void None %voidfn + %label1 = OpLabel + %call1 = OpFunctionCall %void %sub1 + %call2 = OpFunctionCall %void %sub2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Entry point id '1' uses more than one PushConstant interface.\n" + "From Vulkan spec, section 14.5.1:\n" + "There must be no more than one push constant block " + "statically used per shader entry point.")); +} + +TEST_F(ValidateDecorations, VulkanUniformMissingDescriptorSetBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + OpDecorate %var Binding 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer Uniform %struct +%ptr_float = OpTypePointer Uniform %float + %var = OpVariable %ptr Uniform + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %2 = OpAccessChain %ptr_float %var %int_0 + %3 = OpLoad %float %2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Uniform id '3' is missing DescriptorSet decoration.\n" + "From Vulkan spec, section 14.5.2:\n" + "These variables must have DescriptorSet and Binding " + "decorations specified")); +} + +TEST_F(ValidateDecorations, VulkanUniformMissingBindingBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + OpDecorate %var DescriptorSet 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer Uniform %struct +%ptr_float = OpTypePointer Uniform %float + %var = OpVariable %ptr Uniform + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %2 = OpAccessChain %ptr_float %var %int_0 + %3 = OpLoad %float %2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Uniform id '3' is missing Binding decoration.\n" + "From Vulkan spec, section 14.5.2:\n" + "These variables must have DescriptorSet and Binding " + "decorations specified")); +} + +TEST_F(ValidateDecorations, VulkanUniformConstantMissingDescriptorSetBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %var Binding 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %sampler = OpTypeSampler + %ptr = OpTypePointer UniformConstant %sampler + %var = OpVariable %ptr UniformConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %2 = OpLoad %sampler %var + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("UniformConstant id '2' is missing DescriptorSet decoration.\n" + "From Vulkan spec, section 14.5.2:\n" + "These variables must have DescriptorSet and Binding " + "decorations specified")); +} + +TEST_F(ValidateDecorations, VulkanUniformConstantMissingBindingBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %var DescriptorSet 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %sampler = OpTypeSampler + %ptr = OpTypePointer UniformConstant %sampler + %var = OpVariable %ptr UniformConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %2 = OpLoad %sampler %var + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("UniformConstant id '2' is missing Binding decoration.\n" + "From Vulkan spec, section 14.5.2:\n" + "These variables must have DescriptorSet and Binding " + "decorations specified")); +} + +TEST_F(ValidateDecorations, VulkanStorageBufferMissingDescriptorSetBad) { + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpDecorate %var Binding 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer StorageBuffer %struct + %var = OpVariable %ptr StorageBuffer +%ptr_float = OpTypePointer StorageBuffer %float + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %2 = OpAccessChain %ptr_float %var %int_0 + %3 = OpLoad %float %2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n" + "From Vulkan spec, section 14.5.2:\n" + "These variables must have DescriptorSet and Binding " + "decorations specified")); +} + +TEST_F(ValidateDecorations, VulkanStorageBufferMissingBindingBad) { + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpDecorate %var DescriptorSet 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer StorageBuffer %struct + %var = OpVariable %ptr StorageBuffer +%ptr_float = OpTypePointer StorageBuffer %float + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %2 = OpAccessChain %ptr_float %var %int_0 + %3 = OpLoad %float %2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("StorageBuffer id '3' is missing Binding decoration.\n" + "From Vulkan spec, section 14.5.2:\n" + "These variables must have DescriptorSet and Binding " + "decorations specified")); +} + +TEST_F(ValidateDecorations, + VulkanStorageBufferMissingDescriptorSetSubFunctionBad) { + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpDecorate %var Binding 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer StorageBuffer %struct + %var = OpVariable %ptr StorageBuffer +%ptr_float = OpTypePointer StorageBuffer %float + %int = OpTypeInt 32 0 + %int_0 = OpConstant %int 0 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + %call = OpFunctionCall %void %2 + OpReturn + OpFunctionEnd + %2 = OpFunction %void None %voidfn + %label2 = OpLabel + %3 = OpAccessChain %ptr_float %var %int_0 + %4 = OpLoad %float %3 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("StorageBuffer id '3' is missing DescriptorSet decoration.\n" + "From Vulkan spec, section 14.5.2:\n" + "These variables must have DescriptorSet and Binding " + "decorations specified")); +} + +TEST_F(ValidateDecorations, + VulkanStorageBufferMissingDescriptorAndBindingUnusedGood) { + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer StorageBuffer %struct + %var = OpVariable %ptr StorageBuffer + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateDecorations, UniformMissingDescriptorSetGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + OpDecorate %var Binding 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer Uniform %struct + %var = OpVariable %ptr Uniform + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, UniformMissingBindingGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + OpDecorate %var DescriptorSet 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer Uniform %struct + %var = OpVariable %ptr Uniform + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, UniformConstantMissingDescriptorSetGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %var Binding 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %sampler = OpTypeSampler + %ptr = OpTypePointer UniformConstant %sampler + %var = OpVariable %ptr UniformConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, UniformConstantMissingBindingGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %var DescriptorSet 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %sampler = OpTypeSampler + %ptr = OpTypePointer UniformConstant %sampler + %var = OpVariable %ptr UniformConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, StorageBufferMissingDescriptorSetGood) { + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct BufferBlock + OpDecorate %var Binding 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer StorageBuffer %struct + %var = OpVariable %ptr StorageBuffer + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, StorageBufferMissingBindingGood) { + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct BufferBlock + OpDecorate %var DescriptorSet 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer StorageBuffer %struct + %var = OpVariable %ptr StorageBuffer + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBaseAlignmentGood) { + // Spot check buffer rules when using StorageBuffer storage class with Block + // decoration. + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 4 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 8 + OpDecorate %S Block + OpDecorate %u DescriptorSet 0 + OpDecorate %u Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_Uniform_S = OpTypePointer StorageBuffer %S + %u = OpVariable %_ptr_Uniform_S StorageBuffer + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, StorageBufferStorageClassArrayBadAlignmentBad) { + // Like the previous test, but with offset 7. + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpDecorate %_arr_float_uint_2 ArrayStride 4 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 7 + OpDecorate %S Block + OpDecorate %u DescriptorSet 0 + OpDecorate %u Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %S = OpTypeStruct %v2float %_arr_float_uint_2 +%_ptr_Uniform_S = OpTypePointer StorageBuffer %S + %u = OpVariable %_ptr_Uniform_S StorageBuffer + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 3 decorated as Block for variable in StorageBuffer " + "storage class must follow standard storage buffer layout rules: " + "member 1 at offset 7 is not aligned to 4")); +} + +TEST_F(ValidateDecorations, BufferBlockStandardStorageBufferLayout) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %F 0 Offset 0 + OpMemberDecorate %F 1 Offset 8 + OpDecorate %_arr_float_uint_2 ArrayStride 4 + OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 + OpMemberDecorate %O 0 Offset 0 + OpMemberDecorate %O 1 Offset 16 + OpMemberDecorate %O 2 Offset 24 + OpMemberDecorate %O 3 Offset 32 + OpMemberDecorate %O 4 ColMajor + OpMemberDecorate %O 4 Offset 48 + OpMemberDecorate %O 4 MatrixStride 16 + OpDecorate %_arr_O_uint_2 ArrayStride 144 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 8 + OpMemberDecorate %Output 2 Offset 16 + OpMemberDecorate %Output 3 Offset 32 + OpMemberDecorate %Output 4 Offset 48 + OpMemberDecorate %Output 5 Offset 52 + OpMemberDecorate %Output 6 ColMajor + OpMemberDecorate %Output 6 Offset 64 + OpMemberDecorate %Output 6 MatrixStride 16 + OpMemberDecorate %Output 7 Offset 96 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %F = OpTypeStruct %int %v2uint + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%mat2v3float = OpTypeMatrix %v3float 2 + %v3uint = OpTypeVector %uint 3 +%mat3v3float = OpTypeMatrix %v3float 3 +%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 + %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 +%_arr_O_uint_2 = OpTypeArray %O %uint_2 + %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, + StorageBufferLayoutPermitsTightVec3ScalarPackingGood) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 12 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %v3float %float +%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S + %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, + StorageBufferLayoutForbidsTightScalarVec3PackingBad) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/1666 + std::string spirv = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 0 + OpMemberDecorate %S 1 Offset 4 + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float +%_ptr_StorageBuffer_S = OpTypePointer StorageBuffer %S + %B = OpVariable %_ptr_StorageBuffer_S StorageBuffer + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in StorageBuffer " + "storage class must follow standard storage buffer layout " + "rules: member 1 at offset 4 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, + BlockStandardUniformBufferLayoutIncorrectOffset0Bad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %F 0 Offset 0 + OpMemberDecorate %F 1 Offset 8 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 + OpMemberDecorate %O 0 Offset 0 + OpMemberDecorate %O 1 Offset 16 + OpMemberDecorate %O 2 Offset 24 + OpMemberDecorate %O 3 Offset 33 + OpMemberDecorate %O 4 ColMajor + OpMemberDecorate %O 4 Offset 80 + OpMemberDecorate %O 4 MatrixStride 16 + OpDecorate %_arr_O_uint_2 ArrayStride 176 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 8 + OpMemberDecorate %Output 2 Offset 16 + OpMemberDecorate %Output 3 Offset 32 + OpMemberDecorate %Output 4 Offset 48 + OpMemberDecorate %Output 5 Offset 64 + OpMemberDecorate %Output 6 ColMajor + OpMemberDecorate %Output 6 Offset 96 + OpMemberDecorate %Output 6 MatrixStride 16 + OpMemberDecorate %Output 7 Offset 128 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %F = OpTypeStruct %int %v2uint + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%mat2v3float = OpTypeMatrix %v3float 2 + %v3uint = OpTypeVector %uint 3 +%mat3v3float = OpTypeMatrix %v3float 3 +%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 + %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 +%_arr_O_uint_2 = OpTypeArray %O %uint_2 + %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Structure id 6 decorated as Block for variable in Uniform " + "storage class must follow standard uniform buffer layout " + "rules: member 2 at offset 152 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, + BlockStandardUniformBufferLayoutIncorrectOffset1Bad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %F 0 Offset 0 + OpMemberDecorate %F 1 Offset 8 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 48 + OpMemberDecorate %O 0 Offset 0 + OpMemberDecorate %O 1 Offset 16 + OpMemberDecorate %O 2 Offset 32 + OpMemberDecorate %O 3 Offset 64 + OpMemberDecorate %O 4 ColMajor + OpMemberDecorate %O 4 Offset 80 + OpMemberDecorate %O 4 MatrixStride 16 + OpDecorate %_arr_O_uint_2 ArrayStride 176 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 8 + OpMemberDecorate %Output 2 Offset 16 + OpMemberDecorate %Output 3 Offset 32 + OpMemberDecorate %Output 4 Offset 48 + OpMemberDecorate %Output 5 Offset 71 + OpMemberDecorate %Output 6 ColMajor + OpMemberDecorate %Output 6 Offset 96 + OpMemberDecorate %Output 6 MatrixStride 16 + OpMemberDecorate %Output 7 Offset 128 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %F = OpTypeStruct %int %v2uint + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%mat2v3float = OpTypeMatrix %v3float 2 + %v3uint = OpTypeVector %uint 3 +%mat3v3float = OpTypeMatrix %v3float 3 +%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 + %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 +%_arr_O_uint_2 = OpTypeArray %O %uint_2 + %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Structure id 8 decorated as Block for variable in Uniform " + "storage class must follow standard uniform buffer layout " + "rules: member 5 at offset 71 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, BlockUniformBufferLayoutIncorrectArrayStrideBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %F 0 Offset 0 + OpMemberDecorate %F 1 Offset 8 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpDecorate %_arr_mat3v3float_uint_2 ArrayStride 49 + OpMemberDecorate %O 0 Offset 0 + OpMemberDecorate %O 1 Offset 16 + OpMemberDecorate %O 2 Offset 32 + OpMemberDecorate %O 3 Offset 64 + OpMemberDecorate %O 4 ColMajor + OpMemberDecorate %O 4 Offset 80 + OpMemberDecorate %O 4 MatrixStride 16 + OpDecorate %_arr_O_uint_2 ArrayStride 176 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 8 + OpMemberDecorate %Output 2 Offset 16 + OpMemberDecorate %Output 3 Offset 32 + OpMemberDecorate %Output 4 Offset 48 + OpMemberDecorate %Output 5 Offset 64 + OpMemberDecorate %Output 6 ColMajor + OpMemberDecorate %Output 6 Offset 96 + OpMemberDecorate %Output 6 MatrixStride 16 + OpMemberDecorate %Output 7 Offset 128 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %F = OpTypeStruct %int %v2uint + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 +%mat2v3float = OpTypeMatrix %v3float 2 + %v3uint = OpTypeVector %uint 3 +%mat3v3float = OpTypeMatrix %v3float 3 +%_arr_mat3v3float_uint_2 = OpTypeArray %mat3v3float %uint_2 + %O = OpTypeStruct %v3uint %v2float %_arr_float_uint_2 %v2float %_arr_mat3v3float_uint_2 +%_arr_O_uint_2 = OpTypeArray %O %uint_2 + %Output = OpTypeStruct %float %v2float %v3float %F %float %_arr_float_uint_2 %mat2v3float %_arr_O_uint_2 +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 6 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 4 " + "contains " + "an array with stride 49 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, + BufferBlockStandardStorageBufferLayoutImproperStraddleBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 8 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %Output = OpTypeStruct %float %v3float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Structure id 3 decorated as BufferBlock for variable in " + "Uniform storage class must follow standard storage buffer " + "layout rules: member 1 at offset 8 is not aligned to 16")); +} + +TEST_F(ValidateDecorations, + BlockUniformBufferLayoutOffsetInsideArrayPaddingBad) { + // In this case the 2nd member fits entirely within the padding. + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpDecorate %_arr_float_uint_2 ArrayStride 16 + OpMemberDecorate %Output 0 Offset 0 + OpMemberDecorate %Output 1 Offset 20 + OpDecorate %Output Block + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %v2uint = OpTypeVector %uint 2 + %uint_2 = OpConstant %uint 2 +%_arr_float_uint_2 = OpTypeArray %float %uint_2 + %Output = OpTypeStruct %_arr_float_uint_2 %float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 4 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 1 at " + "offset 20 overlaps previous member ending at offset 31")); +} + +TEST_F(ValidateDecorations, + BlockUniformBufferLayoutOffsetInsideStructPaddingBad) { + // In this case the 2nd member fits entirely within the padding. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpMemberDecorate %_struct_6 0 Offset 0 + OpMemberDecorate %_struct_2 0 Offset 0 + OpMemberDecorate %_struct_2 1 Offset 4 + OpDecorate %_struct_2 Block + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %_struct_6 = OpTypeStruct %float + %_struct_2 = OpTypeStruct %_struct_6 %float +%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2 + %8 = OpVariable %_ptr_Uniform__struct_2 Uniform + %1 = OpFunction %void None %4 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 3 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 1 at " + "offset 4 overlaps previous member ending at offset 15")); +} + +TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodUniversal1_0) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorate %Outer 0 Offset 4 + OpMemberDecorate %Outer 1 Offset 0 + OpDecorate %Outer Block + OpDecorate %O DescriptorSet 0 + OpDecorate %O Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %Outer = OpTypeStruct %uint %uint +%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer + %O = OpVariable %_ptr_Uniform_Outer Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_UNIVERSAL_1_0)); +} + +TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodOpenGL4_5) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorate %Outer 0 Offset 4 + OpMemberDecorate %Outer 1 Offset 0 + OpDecorate %Outer Block + OpDecorate %O DescriptorSet 0 + OpDecorate %O Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %Outer = OpTypeStruct %uint %uint +%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer + %O = OpVariable %_ptr_Uniform_Outer Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_OPENGL_4_5)); +} + +TEST_F(ValidateDecorations, BlockLayoutOffsetOutOfOrderGoodVulkan1_1) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorate %Outer 0 Offset 4 + OpMemberDecorate %Outer 1 Offset 0 + OpDecorate %Outer Block + OpDecorate %O DescriptorSet 0 + OpDecorate %O Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %Outer = OpTypeStruct %uint %uint +%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer + %O = OpVariable %_ptr_Uniform_Outer Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)) + << getDiagnosticString(); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, BlockLayoutOffsetOverlapBad) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpMemberDecorate %Outer 0 Offset 0 + OpMemberDecorate %Outer 1 Offset 16 + OpMemberDecorate %Inner 0 Offset 0 + OpMemberDecorate %Inner 1 Offset 16 + OpDecorate %Outer Block + OpDecorate %O DescriptorSet 0 + OpDecorate %O Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %Inner = OpTypeStruct %uint %uint + %Outer = OpTypeStruct %Inner %uint +%_ptr_Uniform_Outer = OpTypePointer Uniform %Outer + %O = OpVariable %_ptr_Uniform_Outer Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 3 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 1 at " + "offset 16 overlaps previous member ending at offset 31")); +} + +TEST_F(ValidateDecorations, BufferBlockEmptyStruct) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 430 + OpMemberDecorate %Output 0 Offset 0 + OpDecorate %Output BufferBlock + %void = OpTypeVoid + %3 = OpTypeFunction %void + %S = OpTypeStruct + %Output = OpTypeStruct %S +%_ptr_Uniform_Output = OpTypePointer Uniform %Output + %dataOutput = OpVariable %_ptr_Uniform_Output Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, RowMajorMatrixTightPackingGood) { + // Row major matrix rule: + // A row-major matrix of C columns has a base alignment equal to + // the base alignment of a vector of C matrix components. + // Note: The "matrix component" is the scalar element type. + + // The matrix has 3 columns and 2 rows (C=3, R=2). + // So the base alignment of b is the same as a vector of 3 floats, which is 16 + // bytes. The matrix consists of two of these, and therefore occupies 2 x 16 + // bytes, or 32 bytes. + // + // So the offsets can be: + // a -> 0 + // b -> 16 + // c -> 48 + // d -> 60 ; d fits at bytes 12-15 after offset of c. Tight (vec3;float) + // packing + + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + OpSource GLSL 450 + OpMemberDecorate %_struct_2 0 Offset 0 + OpMemberDecorate %_struct_2 1 RowMajor + OpMemberDecorate %_struct_2 1 Offset 16 + OpMemberDecorate %_struct_2 1 MatrixStride 16 + OpMemberDecorate %_struct_2 2 Offset 48 + OpMemberDecorate %_struct_2 3 Offset 60 + OpDecorate %_struct_2 Block + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%mat3v2float = OpTypeMatrix %v2float 3 + %v3float = OpTypeVector %float 3 + %_struct_2 = OpTypeStruct %v4float %mat3v2float %v3float %float +%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2 + %3 = OpVariable %_ptr_Uniform__struct_2 Uniform + %1 = OpFunction %void None %5 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, ArrayArrayRowMajorMatrixTightPackingGood) { + // Like the previous case, but we have an array of arrays of matrices. + // The RowMajor decoration goes on the struct member (surprisingly). + + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + OpSource GLSL 450 + OpMemberDecorate %_struct_2 0 Offset 0 + OpMemberDecorate %_struct_2 1 RowMajor + OpMemberDecorate %_struct_2 1 Offset 16 + OpMemberDecorate %_struct_2 1 MatrixStride 16 + OpMemberDecorate %_struct_2 2 Offset 80 + OpMemberDecorate %_struct_2 3 Offset 92 + OpDecorate %arr_mat ArrayStride 32 + OpDecorate %arr_arr_mat ArrayStride 32 + OpDecorate %_struct_2 Block + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%mat3v2float = OpTypeMatrix %v2float 3 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 + %arr_mat = OpTypeArray %mat3v2float %uint_1 +%arr_arr_mat = OpTypeArray %arr_mat %uint_2 + %v3float = OpTypeVector %float 3 + %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float +%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2 + %3 = OpVariable %_ptr_Uniform__struct_2 Uniform + %1 = OpFunction %void None %5 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()) + << getDiagnosticString(); +} + +TEST_F(ValidateDecorations, ArrayArrayRowMajorMatrixNextMemberOverlapsBad) { + // Like the previous case, but the offset of member 2 overlaps the matrix. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + OpSource GLSL 450 + OpMemberDecorate %_struct_2 0 Offset 0 + OpMemberDecorate %_struct_2 1 RowMajor + OpMemberDecorate %_struct_2 1 Offset 16 + OpMemberDecorate %_struct_2 1 MatrixStride 16 + OpMemberDecorate %_struct_2 2 Offset 64 + OpMemberDecorate %_struct_2 3 Offset 92 + OpDecorate %arr_mat ArrayStride 32 + OpDecorate %arr_arr_mat ArrayStride 32 + OpDecorate %_struct_2 Block + OpDecorate %3 DescriptorSet 0 + OpDecorate %3 Binding 0 + %void = OpTypeVoid + %5 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %v2float = OpTypeVector %float 2 +%mat3v2float = OpTypeMatrix %v2float 3 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 + %arr_mat = OpTypeArray %mat3v2float %uint_1 +%arr_arr_mat = OpTypeArray %arr_mat %uint_2 + %v3float = OpTypeVector %float 3 + %_struct_2 = OpTypeStruct %v4float %arr_arr_mat %v3float %float +%_ptr_Uniform__struct_2 = OpTypePointer Uniform %_struct_2 + %3 = OpVariable %_ptr_Uniform__struct_2 Uniform + %1 = OpFunction %void None %5 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 2 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 2 at " + "offset 64 overlaps previous member ending at offset 79")); +} + +TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackGood) { + // Original GLSL + + // #version 450 + // layout (set=0,binding=0) buffer S { + // uvec3 arr[2][2]; // first 3 elements are 16 bytes, last is 12 + // uint i; // Can have offset 60 = 3x16 + 12 + // } B; + // void main() {} + + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 + OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 + OpMemberDecorate %_struct_4 0 Offset 0 + OpMemberDecorate %_struct_4 1 Offset 60 + OpDecorate %_struct_4 BufferBlock + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %uint_2 = OpConstant %uint 2 +%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 +%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 + %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint +%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 + %5 = OpVariable %_ptr_Uniform__struct_4 Uniform + %1 = OpFunction %void None %7 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, StorageBufferArraySizeCalculationPackBad) { + // Like previous but, the offset of the second member is too small. + + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 + OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 + OpMemberDecorate %_struct_4 0 Offset 0 + OpMemberDecorate %_struct_4 1 Offset 56 + OpDecorate %_struct_4 BufferBlock + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %uint_2 = OpConstant %uint 2 +%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 +%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 + %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint +%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 + %5 = OpVariable %_ptr_Uniform__struct_4 Uniform + %1 = OpFunction %void None %7 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 4 decorated as BufferBlock for variable " + "in Uniform storage class must follow standard storage " + "buffer layout rules: member 1 at offset 56 overlaps " + "previous member ending at offset 59")); +} + +TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackGood) { + // Like the corresponding buffer block case, but the array padding must + // count for the last element as well, and so the offset of the second + // member must be at least 64. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 + OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 + OpMemberDecorate %_struct_4 0 Offset 0 + OpMemberDecorate %_struct_4 1 Offset 64 + OpDecorate %_struct_4 Block + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %uint_2 = OpConstant %uint 2 +%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 +%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 + %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint +%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 + %5 = OpVariable %_ptr_Uniform__struct_4 Uniform + %1 = OpFunction %void None %7 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, UniformBufferArraySizeCalculationPackBad) { + // Like previous but, the offset of the second member is too small. + + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + OpDecorate %_arr_v3uint_uint_2 ArrayStride 16 + OpDecorate %_arr__arr_v3uint_uint_2_uint_2 ArrayStride 32 + OpMemberDecorate %_struct_4 0 Offset 0 + OpMemberDecorate %_struct_4 1 Offset 60 + OpDecorate %_struct_4 Block + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + %void = OpTypeVoid + %7 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 + %uint_2 = OpConstant %uint 2 +%_arr_v3uint_uint_2 = OpTypeArray %v3uint %uint_2 +%_arr__arr_v3uint_uint_2_uint_2 = OpTypeArray %_arr_v3uint_uint_2 %uint_2 + %_struct_4 = OpTypeStruct %_arr__arr_v3uint_uint_2_uint_2 %uint +%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 + %5 = OpVariable %_ptr_Uniform__struct_4 Uniform + %1 = OpFunction %void None %7 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure id 4 decorated as Block for variable in Uniform storage " + "class must follow standard uniform buffer layout rules: member 1 at " + "offset 60 overlaps previous member ending at offset 63")); +} + +TEST_F(ValidateDecorations, LayoutNotCheckedWhenSkipBlockLayout) { + // Checks that block layout is not verified in skipping block layout mode. + // Even for obviously wrong layout. + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 450 + OpMemberDecorate %S 0 Offset 3 ; wrong alignment + OpMemberDecorate %S 1 Offset 3 ; same offset as before! + OpDecorate %S Block + OpDecorate %B DescriptorSet 0 + OpDecorate %B Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 + %S = OpTypeStruct %float %v3float +%_ptr_Uniform_S = OpTypePointer Uniform %S + %B = OpVariable %_ptr_Uniform_S Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetSkipBlockLayout(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, EntryPointVariableWrongStorageClass) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" %var +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_int_Workgroup = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int_Workgroup Workgroup +%func_ty = OpTypeFunction %void +%1 = OpFunction %void None %func_ty +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpEntryPoint interfaces must be OpVariables with " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class 4 for Entry Point id 1.")); +} + +TEST_F(ValidateDecorations, VulkanMemoryModelNonCoherent) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical VulkanKHR +OpDecorate %1 Coherent +%2 = OpTypeInt 32 0 +%3 = OpTypePointer StorageBuffer %2 +%1 = OpVariable %3 StorageBuffer +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Coherent decoration targeting 1[%1] is " + "banned when using the Vulkan memory model.")); +} + +TEST_F(ValidateDecorations, VulkanMemoryModelNoCoherentMember) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpMemberDecorate %1 0 Coherent +%2 = OpTypeInt 32 0 +%1 = OpTypeStruct %2 %2 +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Coherent decoration targeting 1[%_struct_1] (member index 0) " + "is banned when using the Vulkan memory model.")); +} + +TEST_F(ValidateDecorations, VulkanMemoryModelNoVolatile) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical VulkanKHR +OpDecorate %1 Volatile +%2 = OpTypeInt 32 0 +%3 = OpTypePointer StorageBuffer %2 +%1 = OpVariable %3 StorageBuffer +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Volatile decoration targeting 1[%1] is banned when " + "using the Vulkan memory model.")); +} + +TEST_F(ValidateDecorations, VulkanMemoryModelNoVolatileMember) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpMemberDecorate %1 1 Volatile +%2 = OpTypeInt 32 0 +%1 = OpTypeStruct %2 %2 +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Volatile decoration targeting 1[%_struct_1] (member " + "index 1) is banned when using the Vulkan memory " + "model.")); +} + +TEST_F(ValidateDecorations, FPRoundingModeGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +%half = OpTypeFloat 16 +%float = OpTypeFloat 32 +%float_1_25 = OpConstant %float 1.25 +%half_ptr = OpTypePointer StorageBuffer %half +%half_ptr_var = OpVariable %half_ptr StorageBuffer +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%_ = OpFConvert %half %float_1_25 +OpStore %half_ptr_var %_ +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, FPRoundingModeVectorGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +%half = OpTypeFloat 16 +%float = OpTypeFloat 32 +%v2half = OpTypeVector %half 2 +%v2float = OpTypeVector %float 2 +%float_1_25 = OpConstant %float 1.25 +%floats = OpConstantComposite %v2float %float_1_25 %float_1_25 +%halfs_ptr = OpTypePointer StorageBuffer %v2half +%halfs_ptr_var = OpVariable %halfs_ptr StorageBuffer +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%_ = OpFConvert %v2half %floats +OpStore %halfs_ptr_var %_ +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, FPRoundingModeNotOpFConvert) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +%short = OpTypeInt 16 1 +%int = OpTypeInt 32 1 +%int_17 = OpConstant %int 17 +%short_ptr = OpTypePointer StorageBuffer %short +%short_ptr_var = OpVariable %short_ptr StorageBuffer +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%_ = OpSConvert %short %int_17 +OpStore %short_ptr_var %_ +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FPRoundingMode decoration can be applied only to a " + "width-only conversion instruction for floating-point " + "object.")); +} + +TEST_F(ValidateDecorations, FPRoundingModeNoOpStoreGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +%half = OpTypeFloat 16 +%float = OpTypeFloat 32 +%float_1_25 = OpConstant %float 1.25 +%half_ptr = OpTypePointer StorageBuffer %half +%half_ptr_var = OpVariable %half_ptr StorageBuffer +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%_ = OpFConvert %half %float_1_25 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, FPRoundingModeFConvert64to16Good) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpCapability Float64 +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +%half = OpTypeFloat 16 +%double = OpTypeFloat 64 +%double_1_25 = OpConstant %double 1.25 +%half_ptr = OpTypePointer StorageBuffer %half +%half_ptr_var = OpVariable %half_ptr StorageBuffer +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%_ = OpFConvert %half %double_1_25 +OpStore %half_ptr_var %_ +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, FPRoundingModeNotStoreInFloat16) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpCapability Float64 +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%double_1_25 = OpConstant %double 1.25 +%float_ptr = OpTypePointer StorageBuffer %float +%float_ptr_var = OpVariable %float_ptr StorageBuffer +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%_ = OpFConvert %float %double_1_25 +OpStore %float_ptr_var %_ +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore storing through a " + "pointer to a 16-bit floating-point scalar or vector object.")); +} + +TEST_F(ValidateDecorations, FPRoundingModeMultipleOpStoreGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +%half = OpTypeFloat 16 +%float = OpTypeFloat 32 +%float_1_25 = OpConstant %float 1.25 +%half_ptr = OpTypePointer StorageBuffer %half +%half_ptr_var_0 = OpVariable %half_ptr StorageBuffer +%half_ptr_var_1 = OpVariable %half_ptr StorageBuffer +%half_ptr_var_2 = OpVariable %half_ptr StorageBuffer +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%_ = OpFConvert %half %float_1_25 +OpStore %half_ptr_var_0 %_ +OpStore %half_ptr_var_1 %_ +OpStore %half_ptr_var_2 %_ +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, FPRoundingModeMultipleUsesBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +%half = OpTypeFloat 16 +%float = OpTypeFloat 32 +%float_1_25 = OpConstant %float 1.25 +%half_ptr = OpTypePointer StorageBuffer %half +%half_ptr_var_0 = OpVariable %half_ptr StorageBuffer +%half_ptr_var_1 = OpVariable %half_ptr StorageBuffer +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%_ = OpFConvert %half %float_1_25 +OpStore %half_ptr_var_0 %_ +%result = OpFAdd %half %_ %_ +OpStore %half_ptr_var_1 %_ +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore.")); +} + +TEST_F(ValidateDecorations, GroupDecorateTargetsDecorationGroup) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpDecorationGroup +OpGroupDecorate %1 %1 +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpGroupDecorate may not target OpDecorationGroup " + "'1[%1]'")); +} + +TEST_F(ValidateDecorations, GroupDecorateTargetsDecorationGroup2) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%1 = OpDecorationGroup +OpGroupDecorate %1 %2 %1 +%2 = OpTypeVoid +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpGroupDecorate may not target OpDecorationGroup " + "'1[%1]'")); +} + +TEST_F(ValidateDecorations, RecurseThroughRuntimeArray) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %outer Block +OpMemberDecorate %inner 0 Offset 0 +OpMemberDecorate %inner 1 Offset 1 +OpDecorate %runtime ArrayStride 16 +OpMemberDecorate %outer 0 Offset 0 +%int = OpTypeInt 32 0 +%inner = OpTypeStruct %int %int +%runtime = OpTypeRuntimeArray %inner +%outer = OpTypeStruct %runtime +%outer_ptr = OpTypePointer Uniform %outer +%var = OpVariable %outer_ptr Uniform +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Structure id 2 decorated as Block for variable in Uniform " + "storage class must follow standard uniform buffer layout " + "rules: member 1 at offset 1 is not aligned to 4")); +} + +TEST_F(ValidateDecorations, EmptyStructAtNonZeroOffsetGood) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpMemberDecorate %struct 1 Offset 16 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%empty = OpTypeStruct +%struct = OpTypeStruct %float %empty +%ptr_struct_ubo = OpTypePointer Uniform %struct +%var = OpVariable %ptr_struct_ubo Uniform +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Uniform and UniformId decorations + +TEST_F(ValidateDecorations, UniformDecorationGood) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %int0 Uniform +OpDecorate %var Uniform +OpDecorate %val Uniform +%void = OpTypeVoid +%int = OpTypeInt 32 1 +%int0 = OpConstantNull %int +%intptr = OpTypePointer Private %int +%var = OpVariable %intptr Private +%fn = OpTypeFunction %void +%main = OpFunction %void None %fn +%entry = OpLabel +%val = OpLoad %int %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +// Returns SPIR-V assembly for a shader that uses a given decoration +// instruction. +std::string ShaderWithUniformLikeDecoration(const std::string& inst) { + return std::string(R"( +OpCapability Shader +OpMemoryModel Logical Simple +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %subgroupscope "subgroupscope" +OpName %call "call" +OpName %myfunc "myfunc" +OpName %int0 "int0" +OpName %float0 "float0" +OpName %fn "fn" +)") + inst + + R"( +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 1 +%int0 = OpConstantNull %int +%int_99 = OpConstant %int 99 +%subgroupscope = OpConstant %int 3 +%float0 = OpConstantNull %float +%fn = OpTypeFunction %void +%myfunc = OpFunction %void None %fn +%myfuncentry = OpLabel +OpReturn +OpFunctionEnd +%main = OpFunction %void None %fn +%entry = OpLabel +%call = OpFunctionCall %void %myfunc +OpReturn +OpFunctionEnd +)"; +} + +TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13Bad) { + const std::string spirv = ShaderWithUniformLikeDecoration( + "OpDecorateId %int0 UniformId %subgroupscope"); + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_WRONG_VERSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires SPIR-V version 1.4 or later\n" + " OpDecorateId %int0 UniformId %subgroupscope")) + << spirv; +} + +TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV13BadTargetV14) { + const std::string spirv = ShaderWithUniformLikeDecoration( + "OpDecorateId %int0 UniformId %subgroupscope"); + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_WRONG_VERSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires SPIR-V version 1.4 or later")); +} + +TEST_F(ValidateDecorations, UniformIdDecorationWithScopeIdV14Good) { + const std::string spirv = ShaderWithUniformLikeDecoration( + "OpDecorateId %int0 UniformId %subgroupscope"); + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, UniformDecorationTargetsTypeBad) { + const std::string spirv = + ShaderWithUniformLikeDecoration("OpDecorate %fn Uniform"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Uniform decoration applied to a non-object")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("%fn = OpTypeFunction %void")); +} + +TEST_F(ValidateDecorations, UniformIdDecorationTargetsTypeBad) { + const std::string spirv = ShaderWithUniformLikeDecoration( + "OpDecorateId %fn UniformId %subgroupscope"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("UniformId decoration applied to a non-object")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("%fn = OpTypeFunction %void")); +} + +TEST_F(ValidateDecorations, UniformDecorationTargetsVoidValueBad) { + const std::string spirv = + ShaderWithUniformLikeDecoration("OpDecorate %call Uniform"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Uniform decoration applied to a value with void type\n" + " %call = OpFunctionCall %void %myfunc")); +} + +TEST_F(ValidateDecorations, UniformIdDecorationTargetsVoidValueBad) { + const std::string spirv = ShaderWithUniformLikeDecoration( + "OpDecorateId %call UniformId %subgroupscope"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)) + << spirv; + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("UniformId decoration applied to a value with void type\n" + " %call = OpFunctionCall %void %myfunc")); +} + +TEST_F(ValidateDecorations, + UniformDecorationWithScopeIdV14IdIsFloatValueIsBad) { + const std::string spirv = + ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %float0"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ConstantNull: expected scope to be a 32-bit int")); +} + +TEST_F(ValidateDecorations, + UniformDecorationWithScopeIdV14IdIsInvalidIntValueBad) { + const std::string spirv = + ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int_99"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid scope value:\n %int_99 = OpConstant %int 99\n")); +} + +TEST_F(ValidateDecorations, UniformDecorationWithScopeIdV14VulkanEnv) { + const std::string spirv = + ShaderWithUniformLikeDecoration("OpDecorateId %int0 UniformId %int0"); + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1_SPIRV_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_VULKAN_1_1_SPIRV_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(": in Vulkan environment Execution Scope is limited to " + "Workgroup and Subgroup")); +} + +TEST_F(ValidateDecorations, UniformDecorationWithWrongInstructionBad) { + const std::string spirv = + ShaderWithUniformLikeDecoration("OpDecorateId %int0 Uniform"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_2); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Decorations that don't take ID parameters may not be " + "used with OpDecorateId\n" + " OpDecorateId %int0 Uniform")); +} + +TEST_F(ValidateDecorations, UniformIdDecorationWithWrongInstructionBad) { + const std::string spirv = ShaderWithUniformLikeDecoration( + "OpDecorate %int0 UniformId %subgroupscope"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Decorations taking ID parameters may not be used with OpDecorateId\n" + " OpDecorate %int0 UniformId %subgroupscope")); +} + +TEST_F(ValidateDecorations, MultipleOffsetDecorationsOnSameID) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID '2', member '0' decorated with Offset multiple " + "times is not allowed.")); +} + +TEST_F(ValidateDecorations, MultipleArrayStrideDecorationsOnSameID) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %array ArrayStride 4 + OpDecorate %array ArrayStride 4 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %uint_4 = OpConstant %uint 4 + %array = OpTypeArray %float %uint_4 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID '2' decorated with ArrayStride multiple " + "times is not allowed.")); +} + +TEST_F(ValidateDecorations, MultipleMatrixStrideDecorationsOnSameID) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorate %struct 0 ColMajor + OpMemberDecorate %struct 0 MatrixStride 16 + OpMemberDecorate %struct 0 MatrixStride 16 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %fvec4 = OpTypeVector %float 4 + %fmat4 = OpTypeMatrix %fvec4 4 + %struct = OpTypeStruct %fmat4 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID '2', member '0' decorated with MatrixStride " + "multiple times is not allowed.")); +} + +TEST_F(ValidateDecorations, MultipleRowMajorDecorationsOnSameID) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorate %struct 0 MatrixStride 16 + OpMemberDecorate %struct 0 RowMajor + OpMemberDecorate %struct 0 RowMajor + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %fvec4 = OpTypeVector %float 4 + %fmat4 = OpTypeMatrix %fvec4 4 + %struct = OpTypeStruct %fmat4 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID '2', member '0' decorated with RowMajor multiple " + "times is not allowed.")); +} + +TEST_F(ValidateDecorations, MultipleColMajorDecorationsOnSameID) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorate %struct 0 MatrixStride 16 + OpMemberDecorate %struct 0 ColMajor + OpMemberDecorate %struct 0 ColMajor + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %fvec4 = OpTypeVector %float 4 + %fmat4 = OpTypeMatrix %fvec4 4 + %struct = OpTypeStruct %fmat4 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID '2', member '0' decorated with ColMajor multiple " + "times is not allowed.")); +} + +TEST_F(ValidateDecorations, RowMajorAndColMajorDecorationsOnSameID) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorate %struct 0 MatrixStride 16 + OpMemberDecorate %struct 0 ColMajor + OpMemberDecorate %struct 0 RowMajor + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %fvec4 = OpTypeVector %float 4 + %fmat4 = OpTypeMatrix %fvec4 4 + %struct = OpTypeStruct %fmat4 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID '2', member '0' decorated with both RowMajor and " + "ColMajor is not allowed.")); +} + +TEST_F(ValidateDecorations, BlockAndBufferBlockDecorationsOnSameID) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpDecorate %struct BufferBlock + OpMemberDecorate %struct 0 Offset 0 + OpMemberDecorate %struct 0 MatrixStride 16 + OpMemberDecorate %struct 0 RowMajor + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %fvec4 = OpTypeVector %float 4 + %fmat4 = OpTypeMatrix %fvec4 4 + %struct = OpTypeStruct %fmat4 + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ID '2' decorated with both BufferBlock and Block is not allowed.")); +} + +std::string MakeIntegerShader( + const std::string& decoration, const std::string& inst, + const std::string& extension = + "OpExtension \"SPV_KHR_no_integer_wrap_decoration\"") { + return R"( +OpCapability Shader +OpCapability Linkage +)" + extension + + R"( +%glsl = OpExtInstImport "GLSL.std.450" +%opencl = OpExtInstImport "OpenCL.std" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpName %entry "entry" +)" + decoration + + R"( + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %int = OpTypeInt 32 1 + %zero = OpConstantNull %int + %float = OpTypeFloat 32 + %float0 = OpConstantNull %float + %main = OpFunction %void None %voidfn + %entry = OpLabel +)" + inst + + R"( +OpReturn +OpFunctionEnd)"; +} + +// NoSignedWrap + +TEST_F(ValidateDecorations, NoSignedWrapOnTypeBad) { + std::string spirv = MakeIntegerShader("OpDecorate %void NoSignedWrap", ""); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("NoSignedWrap decoration may not be applied to TypeVoid")); +} + +TEST_F(ValidateDecorations, NoSignedWrapOnLabelBad) { + std::string spirv = MakeIntegerShader("OpDecorate %entry NoSignedWrap", ""); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NoSignedWrap decoration may not be applied to Label")); +} + +TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionBad) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpIAdd %int %zero %zero", ""); + + CompileSuccessfully(spirv); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions: " + "SPV_KHR_no_integer_wrap_decoration")); +} + +TEST_F(ValidateDecorations, NoSignedWrapRequiresExtensionV13Bad) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpIAdd %int %zero %zero", ""); + + CompileSuccessfully(spirv); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions: " + "SPV_KHR_no_integer_wrap_decoration")); +} + +TEST_F(ValidateDecorations, NoSignedWrapOkInSPV14Good) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpIAdd %int %zero %zero", ""); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoSignedWrapIAddGood) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpIAdd %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoSignedWrapISubGood) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpISub %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoSignedWrapIMulGood) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpIMul %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoSignedWrapShiftLeftLogicalGood) { + std::string spirv = + MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpShiftLeftLogical %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoSignedWrapSNegateGood) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpSNegate %int %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoSignedWrapSRemBad) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpSRem %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NoSignedWrap decoration may not be applied to SRem")); +} + +TEST_F(ValidateDecorations, NoSignedWrapFAddBad) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpFAdd %float %float0 %float0"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NoSignedWrap decoration may not be applied to FAdd")); +} + +TEST_F(ValidateDecorations, NoSignedWrapExtInstOpenCLGood) { + std::string spirv = + MakeIntegerShader("OpDecorate %val NoSignedWrap", + "%val = OpExtInst %int %opencl s_abs %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoSignedWrapExtInstGLSLGood) { + std::string spirv = MakeIntegerShader( + "OpDecorate %val NoSignedWrap", "%val = OpExtInst %int %glsl SAbs %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +// TODO(dneto): For NoSignedWrap and NoUnsignedWrap, permit +// "OpExtInst for instruction numbers specified in the extended +// instruction-set specifications as accepting this decoration." + +// NoUnignedWrap + +TEST_F(ValidateDecorations, NoUnsignedWrapOnTypeBad) { + std::string spirv = MakeIntegerShader("OpDecorate %void NoUnsignedWrap", ""); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("NoUnsignedWrap decoration may not be applied to TypeVoid")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapOnLabelBad) { + std::string spirv = MakeIntegerShader("OpDecorate %entry NoUnsignedWrap", ""); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("NoUnsignedWrap decoration may not be applied to Label")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionBad) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpIAdd %int %zero %zero", ""); + + CompileSuccessfully(spirv); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions: " + "SPV_KHR_no_integer_wrap_decoration")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapRequiresExtensionV13Bad) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpIAdd %int %zero %zero", ""); + + CompileSuccessfully(spirv); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions: " + "SPV_KHR_no_integer_wrap_decoration")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapOkInSPV14Good) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpIAdd %int %zero %zero", ""); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapIAddGood) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpIAdd %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapISubGood) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpISub %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapIMulGood) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpIMul %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapShiftLeftLogicalGood) { + std::string spirv = + MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpShiftLeftLogical %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapSNegateGood) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpSNegate %int %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapSRemBad) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpSRem %int %zero %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("NoUnsignedWrap decoration may not be applied to SRem")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapFAddBad) { + std::string spirv = MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpFAdd %float %float0 %float0"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("NoUnsignedWrap decoration may not be applied to FAdd")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapExtInstOpenCLGood) { + std::string spirv = + MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpExtInst %int %opencl s_abs %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NoUnsignedWrapExtInstGLSLGood) { + std::string spirv = + MakeIntegerShader("OpDecorate %val NoUnsignedWrap", + "%val = OpExtInst %int %glsl SAbs %zero"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, AliasedandRestrictBad) { + const std::string body = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 430 +OpMemberDecorate %Output 0 Offset 0 +OpDecorate %Output BufferBlock +OpDecorate %dataOutput Restrict +OpDecorate %dataOutput Aliased +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%Output = OpTypeStruct %float +%_ptr_Uniform_Output = OpTypePointer Uniform %Output +%dataOutput = OpVariable %_ptr_Uniform_Output Uniform +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("decorated with both Aliased and Restrict is not allowed")); +} + +// TODO(dneto): For NoUnsignedWrap and NoUnsignedWrap, permit +// "OpExtInst for instruction numbers specified in the extended +// instruction-set specifications as accepting this decoration." + +TEST_F(ValidateDecorations, PSBAliasedRestrictPointerSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 RestrictPointerEXT +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDecorations, PSBAliasedRestrictPointerMissing) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected AliasedPointerEXT or RestrictPointerEXT for " + "PhysicalStorageBufferEXT pointer")); +} + +TEST_F(ValidateDecorations, PSBAliasedRestrictPointerBoth) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 RestrictPointerEXT +OpDecorate %val1 AliasedPointerEXT +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("can't specify both AliasedPointerEXT and RestrictPointerEXT " + "for PhysicalStorageBufferEXT pointer")); +} + +TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %fparam Restrict +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%fnptr = OpTypeFunction %void %ptr +%main = OpFunction %void None %voidfn +%entry = OpLabel +OpReturn +OpFunctionEnd +%fn = OpFunction %void None %fnptr +%fparam = OpFunctionParameter %ptr +%lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamMissing) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%fnptr = OpTypeFunction %void %ptr +%main = OpFunction %void None %voidfn +%entry = OpLabel +OpReturn +OpFunctionEnd +%fn = OpFunction %void None %fnptr +%fparam = OpFunctionParameter %ptr +%lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected Aliased or Restrict for " + "PhysicalStorageBufferEXT pointer")); +} + +TEST_F(ValidateDecorations, PSBAliasedRestrictFunctionParamBoth) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %fparam Restrict +OpDecorate %fparam Aliased +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%fnptr = OpTypeFunction %void %ptr +%main = OpFunction %void None %voidfn +%entry = OpLabel +OpReturn +OpFunctionEnd +%fn = OpFunction %void None %fnptr +%fparam = OpFunctionParameter %ptr +%lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("can't specify both Aliased and Restrict for " + "PhysicalStorageBufferEXT pointer")); +} + +TEST_F(ValidateDecorations, PSBFPRoundingModeSuccess) { + std::string spirv = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_EXT_physical_storage_buffer" +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_KHR_variable_pointers" +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %_ FPRoundingMode RTE +OpDecorate %half_ptr_var AliasedPointerEXT +%half = OpTypeFloat 16 +%float = OpTypeFloat 32 +%float_1_25 = OpConstant %float 1.25 +%half_ptr = OpTypePointer PhysicalStorageBufferEXT %half +%half_pptr_f = OpTypePointer Function %half_ptr +%void = OpTypeVoid +%func = OpTypeFunction %void +%main = OpFunction %void None %func +%main_entry = OpLabel +%half_ptr_var = OpVariable %half_pptr_f Function +%val1 = OpLoad %half_ptr %half_ptr_var +%_ = OpFConvert %half %float_1_25 +OpStore %val1 %_ Aligned 2 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, InvalidStraddle) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpMemberDecorate %inner_struct 0 Offset 0 +OpMemberDecorate %inner_struct 1 Offset 4 +OpDecorate %outer_struct Block +OpMemberDecorate %outer_struct 0 Offset 0 +OpMemberDecorate %outer_struct 1 Offset 8 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%inner_struct = OpTypeStruct %float %float2 +%outer_struct = OpTypeStruct %float2 %inner_struct +%ptr_ssbo_outer = OpTypePointer StorageBuffer %outer_struct +%var = OpVariable %ptr_ssbo_outer StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 2 decorated as Block for variable in " + "StorageBuffer storage class must follow relaxed " + "storage buffer layout rules: member 1 is an " + "improperly straddling vector at offset 12")); +} + +TEST_F(ValidateDecorations, DescriptorArray) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpMemberDecorate %struct 1 Offset 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%float2 = OpTypeVector %float 2 +%struct = OpTypeStruct %float %float2 +%struct_array = OpTypeArray %struct %int_2 +%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array +%var = OpVariable %ptr_ssbo_array StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 2 decorated as Block for variable in " + "StorageBuffer storage class must follow standard " + "storage buffer layout rules: member 1 at offset 1 is " + "not aligned to 8")); +} + +TEST_F(ValidateDecorations, DescriptorRuntimeArray) { + const std::string spirv = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpMemberDecorate %struct 1 Offset 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%float2 = OpTypeVector %float 2 +%struct = OpTypeStruct %float %float2 +%struct_array = OpTypeRuntimeArray %struct +%ptr_ssbo_array = OpTypePointer StorageBuffer %struct_array +%var = OpVariable %ptr_ssbo_array StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 2 decorated as Block for variable in " + "StorageBuffer storage class must follow standard " + "storage buffer layout rules: member 1 at offset 1 is " + "not aligned to 8")); +} + +TEST_F(ValidateDecorations, MultiDimensionalArray) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %array_4 ArrayStride 4 +OpDecorate %array_3 ArrayStride 48 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_3 = OpConstant %int 3 +%int_4 = OpConstant %int 4 +%array_4 = OpTypeArray %int %int_4 +%array_3 = OpTypeArray %array_4 %int_3 +%struct = OpTypeStruct %array_3 +%ptr_struct = OpTypePointer Uniform %struct +%var = OpVariable %ptr_struct Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 2 decorated as Block for variable in " + "Uniform storage class must follow standard uniform " + "buffer layout rules: member 0 contains an array with " + "stride 4 not satisfying alignment to 16")); +} + +TEST_F(ValidateDecorations, ImproperStraddleInArray) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %array ArrayStride 24 +OpMemberDecorate %inner 0 Offset 0 +OpMemberDecorate %inner 1 Offset 4 +OpMemberDecorate %inner 2 Offset 12 +OpMemberDecorate %inner 3 Offset 16 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%int2 = OpTypeVector %int 2 +%inner = OpTypeStruct %int %int2 %int %int +%array = OpTypeArray %inner %int_2 +%struct = OpTypeStruct %array +%ptr_struct = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structure id 4 decorated as Block for variable in " + "StorageBuffer storage class must follow relaxed " + "storage buffer layout rules: member 1 is an " + "improperly straddling vector at offset 28")); +} + +TEST_F(ValidateDecorations, LargeArray) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %array ArrayStride 24 +OpMemberDecorate %inner 0 Offset 0 +OpMemberDecorate %inner 1 Offset 8 +OpMemberDecorate %inner 2 Offset 16 +OpMemberDecorate %inner 3 Offset 20 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_2000000 = OpConstant %int 2000000 +%int2 = OpTypeVector %int 2 +%inner = OpTypeStruct %int %int2 %int %int +%array = OpTypeArray %inner %int_2000000 +%struct = OpTypeStruct %array +%ptr_struct = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +// NonWritable + +// Returns a SPIR-V shader module with variables in various storage classes, +// parameterizable by which ID should be decorated as NonWritable. +std::string ShaderWithNonWritableTarget(const std::string& target, + bool member_decorate = false) { + const std::string decoration_inst = + std::string(member_decorate ? "OpMemberDecorate " : "OpDecorate ") + + target + (member_decorate ? " 0" : ""); + + return std::string(R"( + OpCapability Shader + OpCapability RuntimeDescriptorArrayEXT + OpExtension "SPV_EXT_descriptor_indexing" + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpName %label "label" + OpName %param_f "param_f" + OpName %param_p "param_p" + OpName %_ptr_imstor "_ptr_imstor" + OpName %_ptr_imsam "_ptr_imsam" + OpName %var_wg "var_wg" + OpName %var_imsam "var_imsam" + OpName %var_priv "var_priv" + OpName %var_func "var_func" + OpName %simple_struct "simple_struct" + + OpDecorate %struct_b Block + OpDecorate %struct_b_rtarr Block + OpMemberDecorate %struct_b 0 Offset 0 + OpMemberDecorate %struct_b_rtarr 0 Offset 0 + OpDecorate %rtarr ArrayStride 4 +)") + decoration_inst + + + R"( NonWritable + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 0 + %int_2 = OpConstant %int 2 + %struct_b = OpTypeStruct %float + %rtarr = OpTypeRuntimeArray %float +%struct_b_rtarr = OpTypeStruct %rtarr +%simple_struct = OpTypeStruct %float + ; storage image + %imstor = OpTypeImage %float 2D 0 0 0 2 R32f + ; sampled image + %imsam = OpTypeImage %float 2D 0 0 0 1 R32f +%array_imstor = OpTypeArray %imstor %int_2 +%rta_imstor = OpTypeRuntimeArray %imstor + +%_ptr_Uniform_stb = OpTypePointer Uniform %struct_b +%_ptr_StorageBuffer_stb = OpTypePointer StorageBuffer %struct_b +%_ptr_StorageBuffer_stb_rtarr = OpTypePointer StorageBuffer %struct_b_rtarr +%_ptr_Workgroup = OpTypePointer Workgroup %float +%_ptr_Private = OpTypePointer Private %float +%_ptr_Function = OpTypePointer Function %float +%_ptr_imstor = OpTypePointer UniformConstant %imstor +%_ptr_imsam = OpTypePointer UniformConstant %imsam +%_ptr_array_imstor = OpTypePointer UniformConstant %array_imstor +%_ptr_rta_imstor = OpTypePointer UniformConstant %rta_imstor + +%extra_fn = OpTypeFunction %void %float %_ptr_Private %_ptr_imstor + +%var_ubo = OpVariable %_ptr_Uniform_stb Uniform +%var_ssbo_sb = OpVariable %_ptr_StorageBuffer_stb StorageBuffer +%var_ssbo_sb_rtarr = OpVariable %_ptr_StorageBuffer_stb_rtarr StorageBuffer +%var_wg = OpVariable %_ptr_Workgroup Workgroup +%var_priv = OpVariable %_ptr_Private Private +%var_imstor = OpVariable %_ptr_imstor UniformConstant +%var_imsam = OpVariable %_ptr_imsam UniformConstant +%var_array_imstor = OpVariable %_ptr_array_imstor UniformConstant +%var_rta_imstor = OpVariable %_ptr_rta_imstor UniformConstant + + %helper = OpFunction %void None %extra_fn + %param_f = OpFunctionParameter %float + %param_p = OpFunctionParameter %_ptr_Private + %param_pimstor = OpFunctionParameter %_ptr_imstor +%helper_label = OpLabel +%helper_func_var = OpVariable %_ptr_Function Function + OpReturn + OpFunctionEnd + + %main = OpFunction %void None %void_fn + %label = OpLabel +%var_func = OpVariable %_ptr_Function Function + OpReturn + OpFunctionEnd +)"; +} + +TEST_F(ValidateDecorations, NonWritableLabelTargetBad) { + std::string spirv = ShaderWithNonWritableTarget("%label"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration must be a " + "memory object declaration (a variable or a function " + "parameter)\n %label = OpLabel")); +} + +TEST_F(ValidateDecorations, NonWritableTypeTargetBad) { + std::string spirv = ShaderWithNonWritableTarget("%void"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration must be a " + "memory object declaration (a variable or a function " + "parameter)\n %void = OpTypeVoid")); +} + +TEST_F(ValidateDecorations, NonWritableValueTargetBad) { + std::string spirv = ShaderWithNonWritableTarget("%float_0"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration must be a " + "memory object declaration (a variable or a function " + "parameter)\n %float_0 = OpConstant %float 0")); +} + +TEST_F(ValidateDecorations, NonWritableValueParamBad) { + std::string spirv = ShaderWithNonWritableTarget("%param_f"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %param_f = OpFunctionParameter %float")); +} + +TEST_F(ValidateDecorations, NonWritablePointerParamButWrongTypeBad) { + std::string spirv = ShaderWithNonWritableTarget("%param_p"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %param_p = OpFunctionParameter %_ptr_Private_float")); +} + +TEST_F(ValidateDecorations, NonWritablePointerParamStorageImageGood) { + std::string spirv = ShaderWithNonWritableTarget("%param_pimstor"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonWritableVarStorageImageGood) { + std::string spirv = ShaderWithNonWritableTarget("%var_imstor"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonWritableVarSampledImageBad) { + std::string spirv = ShaderWithNonWritableTarget("%var_imsam"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %var_imsam")); +} + +TEST_F(ValidateDecorations, NonWritableVarUboGood) { + std::string spirv = ShaderWithNonWritableTarget("%var_ubo"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonWritableVarSsboInUniformGood) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %struct_bb BufferBlock +OpMemberDecorate %struct_bb 0 Offset 0 +OpDecorate %var_ssbo_u NonWritable +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct_bb = OpTypeStruct %float +%_ptr_Uniform_stbb = OpTypePointer Uniform %struct_bb +%var_ssbo_u = OpVariable %_ptr_Uniform_stbb Uniform +%main = OpFunction %void None %void_fn +%label = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonWritableVarSsboInStorageBufferGood) { + std::string spirv = ShaderWithNonWritableTarget("%var_ssbo_sb"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonWritableMemberOfSsboInStorageBufferGood) { + std::string spirv = ShaderWithNonWritableTarget("%struct_b_rtarr", true); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonWritableMemberOfStructGood) { + std::string spirv = ShaderWithNonWritableTarget("%simple_struct", true); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDecorations, NonWritableVarWorkgroupBad) { + std::string spirv = ShaderWithNonWritableTarget("%var_wg"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %var_wg")); +} + +TEST_F(ValidateDecorations, NonWritableVarWorkgroupV14Bad) { + std::string spirv = ShaderWithNonWritableTarget("%var_wg"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, storage " + "buffer, or variable in Private or Function storage " + "class\n %var_wg")); +} + +TEST_F(ValidateDecorations, NonWritableVarPrivateBad) { + std::string spirv = ShaderWithNonWritableTarget("%var_priv"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %var_priv")); +} + +TEST_F(ValidateDecorations, NonWritableVarPrivateV13Bad) { + std::string spirv = ShaderWithNonWritableTarget("%var_priv"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %var_priv")); +} + +TEST_F(ValidateDecorations, NonWritableVarPrivateV14Good) { + std::string spirv = ShaderWithNonWritableTarget("%var_priv"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonWritableVarPrivateV13TargetV14Bad) { + std::string spirv = ShaderWithNonWritableTarget("%var_priv"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %var_priv")); +} + +TEST_F(ValidateDecorations, NonWritableVarFunctionBad) { + std::string spirv = ShaderWithNonWritableTarget("%var_func"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %var_func")); +} + +TEST_F(ValidateDecorations, NonWritableArrayGood) { + std::string spirv = ShaderWithNonWritableTarget("%var_array_imstor"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDecorations, NonWritableRuntimeArrayGood) { + std::string spirv = ShaderWithNonWritableTarget("%var_rta_imstor"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateWebGPUCombineDecorationResult, Decorate) { + const char* const decoration = std::get<0>(GetParam()); + const TestResult& test_result = std::get<1>(GetParam()); + + CodeGenerator generator = CodeGenerator::GetWebGPUShaderCodeGenerator(); + generator.before_types_ = "OpDecorate %u32 "; + generator.before_types_ += decoration; + generator.before_types_ += "\n"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Vertex"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + if (test_result.error_str != "") { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } +} + +TEST_P(ValidateWebGPUCombineDecorationResult, DecorateMember) { + const char* const decoration = std::get<0>(GetParam()); + const TestResult& test_result = std::get<1>(GetParam()); + + CodeGenerator generator = CodeGenerator::GetWebGPUShaderCodeGenerator(); + generator.before_types_ = "OpMemberDecorate %struct_type 0 "; + generator.before_types_ += decoration; + generator.before_types_ += "\n"; + + generator.after_types_ = "%struct_type = OpTypeStruct %u32\n"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Vertex"; + generator.entry_points_.push_back(std::move(entry_point)); + + CompileSuccessfully(generator.Build(), SPV_ENV_WEBGPU_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + if (!test_result.error_str.empty()) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(test_result.error_str)); + } +} + +INSTANTIATE_TEST_SUITE_P( + DecorationCapabilityFailure, ValidateWebGPUCombineDecorationResult, + Combine(Values("CPacked", "Patch", "Sample", "Constant", + "SaturatedConversion", "NonUniformEXT"), + Values(TestResult(SPV_ERROR_INVALID_CAPABILITY, + "requires one of these capabilities")))); + +INSTANTIATE_TEST_SUITE_P( + DecorationAllowListFailure, ValidateWebGPUCombineDecorationResult, + Combine(Values("RelaxedPrecision", "BufferBlock", "GLSLShared", + "GLSLPacked", "Invariant", "Volatile", "Coherent"), + Values(TestResult( + SPV_ERROR_INVALID_ID, + "is not valid for the WebGPU execution environment.")))); + +TEST_F(ValidateDecorations, NonWritableVarFunctionV13Bad) { + std::string spirv = ShaderWithNonWritableTarget("%var_func"); + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %var_func")); +} + +TEST_F(ValidateDecorations, NonWritableVarFunctionV14Good) { + std::string spirv = ShaderWithNonWritableTarget("%var_func"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, NonWritableVarFunctionV13TargetV14Bad) { + std::string spirv = ShaderWithNonWritableTarget("%var_func"); + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of NonWritable decoration is invalid: must " + "point to a storage image, uniform block, or storage " + "buffer\n %var_func")); +} + +TEST_F(ValidateDecorations, BufferBlockV13ValV14Good) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 BufferBlock +%1 = OpTypeStruct +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateDecorations, BufferBlockV14Bad) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %1 BufferBlock +%1 = OpTypeStruct +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_WRONG_VERSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("2nd operand of Decorate: operand BufferBlock(3) " + "requires SPIR-V version 1.3 or earlier")); +} + +// Component + +TEST_F(ValidateDecorations, ComponentDecorationBadTarget) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %t Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%t = OpTypeVector %float 2 +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of Component decoration must be " + "a memory object declaration")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %v Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%t = OpTypeVector %float 2 +%ptr_private = OpTypePointer Private %t +%v = OpVariable %ptr_private Private +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target of Component decoration is invalid: must " + "point to a Storage Class of Input(1) or Output(3)")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBadTypeVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpCapability Matrix +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpDecorate %v Component 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%vtype = OpTypeVector %float 4 +%t = OpTypeMatrix %vtype 4 +%ptr_input = OpTypePointer Input %t +%v = OpVariable %ptr_input Input +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component decoration specified for type")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a scalar or vector")); +} + +std::string ShaderWithComponentDecoration(const std::string& type, + const std::string& decoration) { + return R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %entryPointOutput +OpExecutionMode %main OriginUpperLeft +OpDecorate %entryPointOutput Location 0 +OpDecorate %entryPointOutput )" + + decoration + R"( +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%arr_v3float_uint_2 = OpTypeArray %v3float %uint_2 +%float_0 = OpConstant %float 0 +%_ptr_Output_type = OpTypePointer Output %)" + type + R"( +%entryPointOutput = OpVariable %_ptr_Output_type Output +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood0Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("uint", "Component 0"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood1Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("uint", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood2Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("uint", "Component 2"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntGood3Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("uint", "Component 3"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationIntBad4Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("uint", "Component 4"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 4 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector3GoodVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("v3float", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4GoodVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("v4float", "Component 0"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4Bad1Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("v4float", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 1 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationVector4Bad3Vulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = ShaderWithComponentDecoration("v4float", "Component 3"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 3 " + "and ending with 6 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationArrayGoodVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("arr_v3float_uint_2", "Component 1"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationArrayBadVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = + ShaderWithComponentDecoration("arr_v3float_uint_2", "Component 2"); + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 2 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBlockGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %9 %12 +OpExecutionMode %4 OriginUpperLeft +OpDecorate %9 Location 0 +OpMemberDecorate %block 0 Location 2 +OpMemberDecorate %block 0 Component 1 +OpDecorate %block Block +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%float = OpTypeFloat 32 +%vec3 = OpTypeVector %float 3 +%8 = OpTypePointer Output %vec3 +%9 = OpVariable %8 Output +%block = OpTypeStruct %vec3 +%11 = OpTypePointer Input %block +%12 = OpVariable %11 Input +%int = OpTypeInt 32 1 +%14 = OpConstant %int 0 +%15 = OpTypePointer Input %vec3 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%16 = OpAccessChain %15 %12 %14 +%17 = OpLoad %vec3 %16 +OpStore %9 %17 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, ComponentDecorationBlockBadVulkan) { + const spv_target_env env = SPV_ENV_VULKAN_1_0; + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %9 %12 +OpExecutionMode %4 OriginUpperLeft +OpDecorate %9 Location 0 +OpMemberDecorate %block 0 Location 2 +OpMemberDecorate %block 0 Component 2 +OpDecorate %block Block +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%float = OpTypeFloat 32 +%vec3 = OpTypeVector %float 3 +%8 = OpTypePointer Output %vec3 +%9 = OpVariable %8 Output +%block = OpTypeStruct %vec3 +%11 = OpTypePointer Input %block +%12 = OpVariable %11 Input +%int = OpTypeInt 32 1 +%14 = OpConstant %int 0 +%15 = OpTypePointer Input %vec3 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%16 = OpAccessChain %15 %12 %14 +%17 = OpLoad %vec3 %16 +OpStore %9 %17 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, env); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateAndRetrieveValidationState(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Sequence of components starting with 2 " + "and ending with 4 gets larger than 3")); +} + +TEST_F(ValidateDecorations, ComponentDecorationFunctionParameter) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + + OpDecorate %param_f Component 0 + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %int = OpTypeInt 32 0 + %int_2 = OpConstant %int 2 + %struct_b = OpTypeStruct %float + +%extra_fn = OpTypeFunction %void %float + + %helper = OpFunction %void None %extra_fn + %param_f = OpFunctionParameter %float +%helper_label = OpLabel + OpReturn + OpFunctionEnd + + %main = OpFunction %void None %void_fn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateDecorations, VulkanStorageBufferBlock) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct = OpTypeStruct %uint +%ptr_ssbo = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_ssbo StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, VulkanStorageBufferMissingBlock) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct = OpTypeStruct %uint +%ptr_ssbo = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_ssbo StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + "must be identified with a Block decoration")); +} + +TEST_F(ValidateDecorations, VulkanStorageBufferArrayMissingBlock) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_4 = OpConstant %uint 4 +%struct = OpTypeStruct %uint +%array = OpTypeArray %struct %uint_4 +%ptr_ssbo = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr_ssbo StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + "must be identified with a Block decoration")); +} + +TEST_F(ValidateDecorations, VulkanStorageBufferRuntimeArrayMissingBlock) { + const std::string spirv = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_EXT_descriptor_indexing" +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct = OpTypeStruct %uint +%array = OpTypeRuntimeArray %struct +%ptr_ssbo = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr_ssbo StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables " + "must be identified with a Block decoration")); +} + +TEST_F(ValidateDecorations, VulkanUniformBlock) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct = OpTypeStruct %uint +%ptr_ubo = OpTypePointer Uniform %struct +%var = OpVariable %ptr_ubo Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, VulkanUniformBufferBlock) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct BufferBlock +OpMemberDecorate %struct 0 Offset 0 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct = OpTypeStruct %uint +%ptr_ubo = OpTypePointer Uniform %struct +%var = OpVariable %ptr_ubo Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateDecorations, VulkanUniformMissingBlock) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct = OpTypeStruct %uint +%ptr_ubo = OpTypePointer Uniform %struct +%var = OpVariable %ptr_ubo Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); +} + +TEST_F(ValidateDecorations, VulkanUniformArrayMissingBlock) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_4 = OpConstant %uint 4 +%struct = OpTypeStruct %uint +%array = OpTypeArray %struct %uint_4 +%ptr_ubo = OpTypePointer Uniform %array +%var = OpVariable %ptr_ubo Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); +} + +TEST_F(ValidateDecorations, VulkanUniformRuntimeArrayMissingBlock) { + const std::string spirv = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%struct = OpTypeStruct %uint +%array = OpTypeRuntimeArray %struct +%ptr_ubo = OpTypePointer Uniform %array +%var = OpVariable %ptr_ubo Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nSuch variables must be " + "identified with a Block or BufferBlock decoration")); +} + +TEST_F(ValidateDecorations, VulkanArrayStrideZero) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %array ArrayStride 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%struct = OpTypeStruct %array +%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_ssbo_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("contains an array with stride 0")); +} + +TEST_F(ValidateDecorations, VulkanArrayStrideTooSmall) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %inner ArrayStride 4 +OpDecorate %outer ArrayStride 4 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%inner = OpTypeArray %int %int_4 +%outer = OpTypeArray %inner %int_4 +%struct = OpTypeStruct %outer +%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_ssbo_struct StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "contains an array with stride 4, but with an element size of 16")); +} + +TEST_F(ValidateDecorations, FunctionsWithOpGroupDecorate) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Linkage + OpCapability Kernel + OpCapability Int8 + %1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical32 OpenCL + OpName %foo "foo" + OpName %entry "entry" + OpName %bar "bar" + OpName %entry_0 "entry" + OpName %k "k" + OpName %entry_1 "entry" + OpName %b "b" + OpDecorate %28 FuncParamAttr Zext + %28 = OpDecorationGroup + OpDecorate %k LinkageAttributes "k" Export + OpDecorate %foo LinkageAttributes "foo" Export + OpDecorate %bar LinkageAttributes "bar" Export + OpDecorate %b Alignment 1 + OpGroupDecorate %28 %foo %bar + %uchar = OpTypeInt 8 0 + %bool = OpTypeBool + %3 = OpTypeFunction %bool + %void = OpTypeVoid + %10 = OpTypeFunction %void + %_ptr_Function_uchar = OpTypePointer Function %uchar + %true = OpConstantTrue %bool + %foo = OpFunction %bool DontInline %3 + %entry = OpLabel + OpReturnValue %true + OpFunctionEnd + %bar = OpFunction %bool DontInline %3 + %entry_0 = OpLabel + OpReturnValue %true + OpFunctionEnd + %k = OpFunction %void DontInline %10 + %entry_1 = OpLabel + %b = OpVariable %_ptr_Function_uchar Function + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateDecorations, LocationVariableGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %in_var Location 0 +%float = OpTypeFloat 32 +%ptr_input_float = OpTypePointer Input %float +%in_var = OpVariable %ptr_input_float Input +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDecorations, LocationStructMemberGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpMemberDecorate %struct 0 Location 0 +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDecorations, LocationStructBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %struct Location 0 +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Location decoration can only be applied to a variable " + "or member of a structure type")); +} + +TEST_F(ValidateDecorations, LocationFloatBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +OpDecorate %float Location 0 +%float = OpTypeFloat 32 +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Location decoration can only be applied to a variable " + "or member of a structure type")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_derivatives_test.cpp b/third_party/spirv-tools/test/val/val_derivatives_test.cpp new file mode 100644 index 0000000..606abb9 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_derivatives_test.cpp @@ -0,0 +1,195 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateDerivatives = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Fragment") { + std::stringstream ss; + ss << R"( +OpCapability Shader +OpCapability DerivativeControl +)"; + + ss << capabilities_and_extensions; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"" + << " %f32_var_input" + << " %f32vec4_var_input" + << "\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%f32vec4 = OpTypeVector %f32 4 + +%f32_ptr_input = OpTypePointer Input %f32 +%f32_var_input = OpVariable %f32_ptr_input Input + +%f32vec4_ptr_input = OpTypePointer Input %f32vec4 +%f32vec4_var_input = OpVariable %f32vec4_ptr_input Input +)"; + + if (capabilities_and_extensions.find("OpCapability Float16") != + std::string::npos) { + ss << "%f16 = OpTypeFloat 16\n" + << "%f16vec4 = OpTypeVector %f16 4\n" + << "%f16_0 = OpConstantNull %f16\n" + << "%f16vec4_0 = OpConstantNull %f16vec4\n"; + } + + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +TEST_F(ValidateDerivatives, ScalarSuccess) { + const std::string body = R"( +%f32_var = OpLoad %f32 %f32_var_input +%val1 = OpDPdx %f32 %f32_var +%val2 = OpDPdy %f32 %f32_var +%val3 = OpFwidth %f32 %f32_var +%val4 = OpDPdxFine %f32 %f32_var +%val5 = OpDPdyFine %f32 %f32_var +%val6 = OpFwidthFine %f32 %f32_var +%val7 = OpDPdxCoarse %f32 %f32_var +%val8 = OpDPdyCoarse %f32 %f32_var +%val9 = OpFwidthCoarse %f32 %f32_var +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDerivatives, VectorSuccess) { + const std::string body = R"( +%f32vec4_var = OpLoad %f32vec4 %f32vec4_var_input +%val1 = OpDPdx %f32vec4 %f32vec4_var +%val2 = OpDPdy %f32vec4 %f32vec4_var +%val3 = OpFwidth %f32vec4 %f32vec4_var +%val4 = OpDPdxFine %f32vec4 %f32vec4_var +%val5 = OpDPdyFine %f32vec4 %f32vec4_var +%val6 = OpFwidthFine %f32vec4 %f32vec4_var +%val7 = OpDPdxCoarse %f32vec4 %f32vec4_var +%val8 = OpDPdyCoarse %f32vec4 %f32vec4_var +%val9 = OpFwidthCoarse %f32vec4 %f32vec4_var +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateDerivatives, OpDPdxWrongResultType) { + const std::string body = R"( +%f32_var = OpLoad %f32 %f32_var_input +%val1 = OpDPdx %u32 %f32vec4 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 10[%v4float] cannot " + "be a type")); +} + +TEST_F(ValidateDerivatives, OpDPdxWrongPType) { + const std::string body = R"( +%f32vec4_var = OpLoad %f32vec4 %f32vec4_var_input +%val1 = OpDPdx %f32 %f32vec4_var +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected P type and Result Type to be the same: " + "DPdx")); +} + +TEST_F(ValidateDerivatives, OpDPdxWrongExecutionModel) { + const std::string body = R"( +%f32vec4_var = OpLoad %f32vec4 %f32vec4_var_input +%val1 = OpDPdx %f32vec4 %f32vec4_var +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Vertex").c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Derivative instructions require Fragment or GLCompute " + "execution model: DPdx")); +} + +using ValidateHalfDerivatives = spvtest::ValidateBase; + +TEST_P(ValidateHalfDerivatives, ScalarFailure) { + const std::string op = GetParam(); + const std::string body = "%val = " + op + " %f16 %f16_0\n"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability Float16\n").c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type component width must be 32 bits")); +} + +TEST_P(ValidateHalfDerivatives, VectorFailure) { + const std::string op = GetParam(); + const std::string body = "%val = " + op + " %f16vec4 %f16vec4_0\n"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability Float16\n").c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result type component width must be 32 bits")); +} + +INSTANTIATE_TEST_SUITE_P(HalfDerivatives, ValidateHalfDerivatives, + ::testing::Values("OpDPdx", "OpDPdy", "OpFwidth", + "OpDPdxFine", "OpDPdyFine", + "OpFwidthFine", "OpDPdxCoarse", + "OpDPdyCoarse", "OpFwidthCoarse")); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_entry_point.cpp b/third_party/spirv-tools/test/val/val_entry_point.cpp new file mode 100644 index 0000000..f28cf5d --- /dev/null +++ b/third_party/spirv-tools/test/val/val_entry_point.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2019 Samsung Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; + +using ValidateEntryPoints = spvtest::ValidateBase; + +TEST_F(ValidateEntryPoints, DuplicateEntryPoints) { + const std::string body = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %3 "foo" +OpEntryPoint GLCompute %4 "foo" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%20 = OpLabel +OpReturn +OpFunctionEnd +%4 = OpFunction %1 None %2 +%21 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry points cannot share the same name")); +} + +TEST_F(ValidateEntryPoints, UniqueEntryPoints) { + const std::string body = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %3 "foo" +OpEntryPoint GLCompute %4 "foo2" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%20 = OpLabel +OpReturn +OpFunctionEnd +%4 = OpFunction %1 None %2 +%21 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_explicit_reserved_test.cpp b/third_party/spirv-tools/test/val/val_explicit_reserved_test.cpp new file mode 100644 index 0000000..f01e933 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_explicit_reserved_test.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for illegal instructions + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; + +using ReservedSamplingInstTest = spvtest::ValidateBase; + +// Generate a shader for use with validation tests for sparse sampling +// instructions. +std::string ShaderAssembly(const std::string& instruction_under_test) { + std::ostringstream os; + os << R"( OpCapability Shader + OpCapability SparseResidency + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %float_0 = OpConstant %float 0 + %8 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %9 = OpTypeImage %float 2D 0 0 0 1 Unknown + %10 = OpTypeSampledImage %9 +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %2 = OpVariable %_ptr_UniformConstant_10 UniformConstant + %v2float = OpTypeVector %float 2 + %13 = OpConstantComposite %v2float %float_0 %float_0 + %int = OpTypeInt 32 1 + %_struct_15 = OpTypeStruct %int %v4float + %1 = OpFunction %void None %4 + %16 = OpLabel + %17 = OpLoad %10 %2 +)" << instruction_under_test + << R"( + OpReturn + OpFunctionEnd +)"; + + return os.str(); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjImplicitLod) { + const std::string input = ShaderAssembly( + "%result = OpImageSparseSampleProjImplicitLod %_struct_15 %17 %13"); + CompileSuccessfully(input); + + EXPECT_THAT(ValidateInstructions(), Eq(SPV_ERROR_INVALID_BINARY)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid Opcode name 'OpImageSparseSampleProjImplicitLod'")); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjExplicitLod) { + const std::string input = ShaderAssembly( + "%result = OpImageSparseSampleProjExplicitLod %_struct_15 %17 %13 Lod " + "%float_0\n"); + CompileSuccessfully(input); + + EXPECT_THAT(ValidateInstructions(), Eq(SPV_ERROR_INVALID_BINARY)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid Opcode name 'OpImageSparseSampleProjExplicitLod'")); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefImplicitLod) { + const std::string input = ShaderAssembly( + "%result = OpImageSparseSampleProjDrefImplicitLod %_struct_15 %17 %13 " + "%float_0\n"); + CompileSuccessfully(input); + + EXPECT_THAT(ValidateInstructions(), Eq(SPV_ERROR_INVALID_BINARY)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Invalid Opcode name 'OpImageSparseSampleProjDrefImplicitLod'")); +} + +TEST_F(ReservedSamplingInstTest, OpImageSparseSampleProjDrefExplicitLod) { + const std::string input = ShaderAssembly( + "%result = OpImageSparseSampleProjDrefExplicitLod %_struct_15 %17 %13 " + "%float_0 Lod " + "%float_0\n"); + CompileSuccessfully(input); + + EXPECT_THAT(ValidateInstructions(), Eq(SPV_ERROR_INVALID_BINARY)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Invalid Opcode name 'OpImageSparseSampleProjDrefExplicitLod'")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_ext_inst_test.cpp b/third_party/spirv-tools/test/val/val_ext_inst_test.cpp new file mode 100644 index 0000000..683a76f --- /dev/null +++ b/third_party/spirv-tools/test/val/val_ext_inst_test.cpp @@ -0,0 +1,9337 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests validation rules of GLSL.450.std and OpenCL.std extended instructions. +// Doesn't test OpenCL.std vector size 2, 3, 4, 8 or 16 rules (not supported +// by standard SPIR-V). + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateExtInst = spvtest::ValidateBase; +using ValidateOldDebugInfo = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfo = spvtest::ValidateBase; +using ValidateLocalDebugInfoOutOfFunction = spvtest::ValidateBase; +using ValidateOpenCL100DebugInfoDebugTypedef = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeEnum = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeComposite = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeMember = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugTypeInheritance = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunction = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugFunctionDeclaration = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLexicalBlock = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugLocalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugGlobalVariable = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugDeclare = + spvtest::ValidateBase>; +using ValidateOpenCL100DebugInfoDebugValue = + spvtest::ValidateBase>; +using ValidateGlslStd450SqrtLike = spvtest::ValidateBase; +using ValidateGlslStd450FMinLike = spvtest::ValidateBase; +using ValidateGlslStd450FClampLike = spvtest::ValidateBase; +using ValidateGlslStd450SAbsLike = spvtest::ValidateBase; +using ValidateGlslStd450UMinLike = spvtest::ValidateBase; +using ValidateGlslStd450UClampLike = spvtest::ValidateBase; +using ValidateGlslStd450SinLike = spvtest::ValidateBase; +using ValidateGlslStd450PowLike = spvtest::ValidateBase; +using ValidateGlslStd450Pack = spvtest::ValidateBase; +using ValidateGlslStd450Unpack = spvtest::ValidateBase; +using ValidateOpenCLStdSqrtLike = spvtest::ValidateBase; +using ValidateOpenCLStdFMinLike = spvtest::ValidateBase; +using ValidateOpenCLStdFClampLike = spvtest::ValidateBase; +using ValidateOpenCLStdSAbsLike = spvtest::ValidateBase; +using ValidateOpenCLStdUMinLike = spvtest::ValidateBase; +using ValidateOpenCLStdUClampLike = spvtest::ValidateBase; +using ValidateOpenCLStdUMul24Like = spvtest::ValidateBase; +using ValidateOpenCLStdUMad24Like = spvtest::ValidateBase; +using ValidateOpenCLStdLengthLike = spvtest::ValidateBase; +using ValidateOpenCLStdDistanceLike = spvtest::ValidateBase; +using ValidateOpenCLStdNormalizeLike = spvtest::ValidateBase; +using ValidateOpenCLStdVStoreHalfLike = spvtest::ValidateBase; +using ValidateOpenCLStdVLoadHalfLike = spvtest::ValidateBase; +using ValidateOpenCLStdFractLike = spvtest::ValidateBase; +using ValidateOpenCLStdFrexpLike = spvtest::ValidateBase; +using ValidateOpenCLStdLdexpLike = spvtest::ValidateBase; +using ValidateOpenCLStdUpsampleLike = spvtest::ValidateBase; +using ValidateClspvReflection = spvtest::ValidateBase; + +// Returns number of components in Pack/Unpack extended instructions. +// |ext_inst_name| is expected to be of the format "PackHalf2x16". +// Number of components is assumed to be single-digit. +uint32_t GetPackedNumComponents(const std::string& ext_inst_name) { + const size_t x_index = ext_inst_name.find_last_of('x'); + const std::string num_components_str = + ext_inst_name.substr(x_index - 1, x_index); + return uint32_t(std::stoul(num_components_str)); +} + +// Returns packed bit width in Pack/Unpack extended instructions. +// |ext_inst_name| is expected to be of the format "PackHalf2x16". +uint32_t GetPackedBitWidth(const std::string& ext_inst_name) { + const size_t x_index = ext_inst_name.find_last_of('x'); + const std::string packed_bit_width_str = ext_inst_name.substr(x_index + 1); + return uint32_t(std::stoul(packed_bit_width_str)); +} + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Fragment") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability Float16 +OpCapability Float64 +OpCapability Int16 +OpCapability Int64 +)"; + + ss << capabilities_and_extensions; + ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"" + << " %f32_output" + << " %f32vec2_output" + << " %u32_output" + << " %u32vec2_output" + << " %u64_output" + << " %f32_input" + << " %f32vec2_input" + << " %u32_input" + << " %u32vec2_input" + << " %u64_input" + << "\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%u16 = OpTypeInt 16 0 +%s16 = OpTypeInt 16 1 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f64vec2 = OpTypeVector %f64 2 +%f64vec3 = OpTypeVector %f64 3 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%s32vec2 = OpTypeVector %s32 2 +%u32vec4 = OpTypeVector %u32 4 +%s32vec4 = OpTypeVector %s32 4 +%u64vec2 = OpTypeVector %u64 2 +%s64vec2 = OpTypeVector %s64 2 +%f64mat22 = OpTypeMatrix %f64vec2 2 +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 +%f32mat33 = OpTypeMatrix %f32vec3 3 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 + +%f16_0 = OpConstant %f16 0 +%f16_1 = OpConstant %f16 1 +%f16_h = OpConstant %f16 0.5 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 + +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 + +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1 +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 + +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 + +%f32_ptr_output = OpTypePointer Output %f32 +%f32vec2_ptr_output = OpTypePointer Output %f32vec2 + +%u32_ptr_output = OpTypePointer Output %u32 +%u32vec2_ptr_output = OpTypePointer Output %u32vec2 + +%u64_ptr_output = OpTypePointer Output %u64 + +%f32_output = OpVariable %f32_ptr_output Output +%f32vec2_output = OpVariable %f32vec2_ptr_output Output + +%u32_output = OpVariable %u32_ptr_output Output +%u32vec2_output = OpVariable %u32vec2_ptr_output Output + +%u64_output = OpVariable %u64_ptr_output Output + +%f32_ptr_input = OpTypePointer Input %f32 +%f32vec2_ptr_input = OpTypePointer Input %f32vec2 + +%u32_ptr_input = OpTypePointer Input %u32 +%u32vec2_ptr_input = OpTypePointer Input %u32vec2 + +%u64_ptr_input = OpTypePointer Input %u64 + +%f32_input = OpVariable %f32_ptr_input Input +%f32vec2_input = OpVariable %f32vec2_ptr_input Input + +%u32_input = OpVariable %u32_ptr_input Input +%u32vec2_input = OpVariable %u32vec2_ptr_input Input + +%u64_input = OpVariable %u64_ptr_input Input + +%struct_f16_u16 = OpTypeStruct %f16 %u16 +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 +%struct_f32_u32 = OpTypeStruct %f32 %u32 +%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 +%struct_u32_f32 = OpTypeStruct %u32 %f32 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f64 = OpTypeStruct %f32 %f64 +%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 +%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +std::string GenerateKernelCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& memory_model = "Physical32") { + std::ostringstream ss; + ss << R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability GenericPointer +OpCapability Int8 +OpCapability Int16 +OpCapability Int64 +OpCapability Float16 +OpCapability Float64 +OpCapability Vector16 +OpCapability Matrix +)"; + + ss << capabilities_and_extensions; + ss << "%extinst = OpExtInstImport \"OpenCL.std\"\n"; + ss << "OpMemoryModel " << memory_model << " OpenCL\n"; + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%u64 = OpTypeInt 64 0 +%u16 = OpTypeInt 16 0 +%u8 = OpTypeInt 8 0 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f32vec8 = OpTypeVector %f32 8 +%f16vec8 = OpTypeVector %f16 8 +%f32vec16 = OpTypeVector %f32 16 +%f64vec2 = OpTypeVector %f64 2 +%f64vec3 = OpTypeVector %f64 3 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%u32vec4 = OpTypeVector %u32 4 +%u32vec8 = OpTypeVector %u32 8 +%u64vec2 = OpTypeVector %u64 2 +%f64mat22 = OpTypeMatrix %f64vec2 2 +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 +%f32mat33 = OpTypeMatrix %f32vec3 3 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 +%f32vec8_01010101 = OpConstantComposite %f32vec8 %f32_0 %f32_1 %f32_0 %f32_1 %f32_0 %f32_1 %f32_0 %f32_1 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 + +%f16_0 = OpConstant %f16 0 +%f16_1 = OpConstant %f16 1 + +%u8_0 = OpConstant %u8 0 +%u8_1 = OpConstant %u8 1 +%u8_2 = OpConstant %u8 2 +%u8_3 = OpConstant %u8 3 + +%u16_0 = OpConstant %u16 0 +%u16_1 = OpConstant %u16 1 +%u16_2 = OpConstant %u16 2 +%u16_3 = OpConstant %u16 3 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_256 = OpConstant %u32 256 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_256 = OpConstant %u64 256 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 + +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 + +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 +%struct_f32_u32 = OpTypeStruct %f32 %u32 +%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 +%struct_u32_f32 = OpTypeStruct %u32 %f32 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f64 = OpTypeStruct %f32 %f64 +%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 +%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 + +%f16vec8_ptr_workgroup = OpTypePointer Workgroup %f16vec8 +%f16vec8_workgroup = OpVariable %f16vec8_ptr_workgroup Workgroup +%f16_ptr_workgroup = OpTypePointer Workgroup %f16 + +%u32vec8_ptr_workgroup = OpTypePointer Workgroup %u32vec8 +%u32vec8_workgroup = OpVariable %u32vec8_ptr_workgroup Workgroup +%u32_ptr_workgroup = OpTypePointer Workgroup %u32 + +%f32vec8_ptr_workgroup = OpTypePointer Workgroup %f32vec8 +%f32vec8_workgroup = OpVariable %f32vec8_ptr_workgroup Workgroup +%f32_ptr_workgroup = OpTypePointer Workgroup %f32 + +%u32arr = OpTypeArray %u32 %u32_256 +%u32arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %u32arr +%u32arr_cross_workgroup = OpVariable %u32arr_ptr_cross_workgroup CrossWorkgroup +%u32_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %u32 + +%f32arr = OpTypeArray %f32 %u32_256 +%f32arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32arr +%f32arr_cross_workgroup = OpVariable %f32arr_ptr_cross_workgroup CrossWorkgroup +%f32_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32 + +%f32vec2arr = OpTypeArray %f32vec2 %u32_256 +%f32vec2arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32vec2arr +%f32vec2arr_cross_workgroup = OpVariable %f32vec2arr_ptr_cross_workgroup CrossWorkgroup +%f32vec2_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %f32vec2 + +%struct_arr = OpTypeArray %struct_f32_f32 %u32_256 +%struct_arr_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %struct_arr +%struct_arr_cross_workgroup = OpVariable %struct_arr_ptr_cross_workgroup CrossWorkgroup +%struct_ptr_cross_workgroup = OpTypePointer CrossWorkgroup %struct_f32_f32 + +%f16vec8_ptr_uniform_constant = OpTypePointer UniformConstant %f16vec8 +%f16vec8_uniform_constant = OpVariable %f16vec8_ptr_uniform_constant UniformConstant +%f16_ptr_uniform_constant = OpTypePointer UniformConstant %f16 + +%u32vec8_ptr_uniform_constant = OpTypePointer UniformConstant %u32vec8 +%u32vec8_uniform_constant = OpVariable %u32vec8_ptr_uniform_constant UniformConstant +%u32_ptr_uniform_constant = OpTypePointer UniformConstant %u32 + +%f32vec8_ptr_uniform_constant = OpTypePointer UniformConstant %f32vec8 +%f32vec8_uniform_constant = OpVariable %f32vec8_ptr_uniform_constant UniformConstant +%f32_ptr_uniform_constant = OpTypePointer UniformConstant %f32 + +%f16vec8_ptr_input = OpTypePointer Input %f16vec8 +%f16vec8_input = OpVariable %f16vec8_ptr_input Input +%f16_ptr_input = OpTypePointer Input %f16 + +%u32vec8_ptr_input = OpTypePointer Input %u32vec8 +%u32vec8_input = OpVariable %u32vec8_ptr_input Input +%u32_ptr_input = OpTypePointer Input %u32 + +%f32_ptr_generic = OpTypePointer Generic %f32 +%u32_ptr_generic = OpTypePointer Generic %u32 + +%f32_ptr_function = OpTypePointer Function %f32 +%f32vec2_ptr_function = OpTypePointer Function %f32vec2 +%u32_ptr_function = OpTypePointer Function %u32 +%u64_ptr_function = OpTypePointer Function %u64 +%u32vec2_ptr_function = OpTypePointer Function %u32vec2 + +%u8arr = OpTypeArray %u8 %u32_256 +%u8arr_ptr_uniform_constant = OpTypePointer UniformConstant %u8arr +%u8arr_uniform_constant = OpVariable %u8arr_ptr_uniform_constant UniformConstant +%u8_ptr_uniform_constant = OpTypePointer UniformConstant %u8 +%u8_ptr_generic = OpTypePointer Generic %u8 + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +std::string GenerateShaderCodeForDebugInfo( + const std::string& op_string_instructions, + const std::string& op_const_instructions, + const std::string& debug_instructions_before_main, const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Fragment") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability Float16 +OpCapability Float64 +OpCapability Int16 +OpCapability Int64 +)"; + + ss << capabilities_and_extensions; + ss << "%extinst = OpExtInstImport \"GLSL.std.450\"\n"; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"" + << " %f32_output" + << " %f32vec2_output" + << " %u32_output" + << " %u32vec2_output" + << " %u64_output" + << " %f32_input" + << " %f32vec2_input" + << " %u32_input" + << " %u32vec2_input" + << " %u64_input" + << "\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + + ss << op_string_instructions; + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f16 = OpTypeFloat 16 +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%u16 = OpTypeInt 16 0 +%s16 = OpTypeInt 16 1 +%f32vec2 = OpTypeVector %f32 2 +%f32vec3 = OpTypeVector %f32 3 +%f32vec4 = OpTypeVector %f32 4 +%f64vec2 = OpTypeVector %f64 2 +%f64vec3 = OpTypeVector %f64 3 +%f64vec4 = OpTypeVector %f64 4 +%u32vec2 = OpTypeVector %u32 2 +%u32vec3 = OpTypeVector %u32 3 +%s32vec2 = OpTypeVector %s32 2 +%u32vec4 = OpTypeVector %u32 4 +%s32vec4 = OpTypeVector %s32 4 +%u64vec2 = OpTypeVector %u64 2 +%s64vec2 = OpTypeVector %s64 2 +%f64mat22 = OpTypeMatrix %f64vec2 2 +%f32mat22 = OpTypeMatrix %f32vec2 2 +%f32mat23 = OpTypeMatrix %f32vec2 3 +%f32mat32 = OpTypeMatrix %f32vec3 2 +%f32mat33 = OpTypeMatrix %f32vec3 3 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 +%f32_h = OpConstant %f32 0.5 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 + +%f16_0 = OpConstant %f16 0 +%f16_1 = OpConstant %f16 1 +%f16_h = OpConstant %f16 0.5 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +)"; + + ss << op_const_instructions; + + ss << R"( +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 + +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 + +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%s64vec2_01 = OpConstantComposite %s64vec2 %s64_0 %s64_1 +%u64vec2_01 = OpConstantComposite %u64vec2 %u64_0 %u64_1 + +%f32mat22_1212 = OpConstantComposite %f32mat22 %f32vec2_12 %f32vec2_12 +%f32mat23_121212 = OpConstantComposite %f32mat23 %f32vec2_12 %f32vec2_12 %f32vec2_12 + +%f32_ptr_output = OpTypePointer Output %f32 +%f32vec2_ptr_output = OpTypePointer Output %f32vec2 + +%u32_ptr_output = OpTypePointer Output %u32 +%u32vec2_ptr_output = OpTypePointer Output %u32vec2 + +%u64_ptr_output = OpTypePointer Output %u64 + +%f32_output = OpVariable %f32_ptr_output Output +%f32vec2_output = OpVariable %f32vec2_ptr_output Output + +%u32_output = OpVariable %u32_ptr_output Output +%u32vec2_output = OpVariable %u32vec2_ptr_output Output + +%u64_output = OpVariable %u64_ptr_output Output + +%f32_ptr_input = OpTypePointer Input %f32 +%f32vec2_ptr_input = OpTypePointer Input %f32vec2 + +%u32_ptr_input = OpTypePointer Input %u32 +%u32vec2_ptr_input = OpTypePointer Input %u32vec2 + +%u64_ptr_input = OpTypePointer Input %u64 + +%f32_ptr_function = OpTypePointer Function %f32 + +%f32_input = OpVariable %f32_ptr_input Input +%f32vec2_input = OpVariable %f32vec2_ptr_input Input + +%u32_input = OpVariable %u32_ptr_input Input +%u32vec2_input = OpVariable %u32vec2_ptr_input Input + +%u64_input = OpVariable %u64_ptr_input Input + +%u32_ptr_function = OpTypePointer Function %u32 + +%struct_f16_u16 = OpTypeStruct %f16 %u16 +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_f32_f32_f32 = OpTypeStruct %f32 %f32 %f32 +%struct_f32_u32 = OpTypeStruct %f32 %u32 +%struct_f32_u32_f32 = OpTypeStruct %f32 %u32 %f32 +%struct_u32_f32 = OpTypeStruct %u32 %f32 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f64 = OpTypeStruct %f32 %f64 +%struct_f32vec2_f32vec2 = OpTypeStruct %f32vec2 %f32vec2 +%struct_f32vec2_u32vec2 = OpTypeStruct %f32vec2 %u32vec2 +)"; + + ss << debug_instructions_before_main; + + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +TEST_F(ValidateOldDebugInfo, UseDebugInstructionOutOfFunction) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%cu = OpExtInst %void %DbgExt DebugCompilationUnit %code 1 1 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "DebugInfo" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, UseDebugInstructionOutOfFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceInFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", "", dbg_inst, + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Debug info extension instructions other than DebugScope, " + "DebugNoScope, DebugDeclare, DebugValue must appear between " + "section 9 (types, constants, global variables) and section 10 " + "(function declarations)")); +} + +TEST_P(ValidateLocalDebugInfoOutOfFunction, OpenCLDebugInfo100DebugScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%int_name = OpString "int" +%foo_name = OpString "foo" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %u32_0 Signed +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %int_info %dbg_src 1 1 %main_info FlagIsLocal +%expr = OpExtInst %void %DbgExt DebugExpression +)"; + + const std::string body = R"( +%foo = OpVariable %u32_ptr_function Function +%foo_val = OpLoad %u32 %foo +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header + GetParam(), body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("DebugScope, DebugNoScope, DebugDeclare, DebugValue " + "of debug info extension must appear in a function " + "body")); +} + +INSTANTIATE_TEST_SUITE_P( + AllLocalDebugInfo, ValidateLocalDebugInfoOutOfFunction, + ::testing::ValuesIn(std::vector{ + "%main_scope = OpExtInst %void %DbgExt DebugScope %main_info", + "%no_scope = OpExtInst %void %DbgExt DebugNoScope", + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionForwardReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionMissingOpFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %dbgNone +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeBeforeOpVariableInFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info +%foo = OpVariable %f32_ptr_function Function +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeSizeDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%ty_name = OpString "struct VS_OUTPUT" +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeForwardReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_color_name = OpString "color : COLOR" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%VS_OUTPUT_color_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_color_name %v4float_info %dbg_src 3 3 %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeMissingReference) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; + float4 color : COLOR; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_color_name = OpString "color : COLOR" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %VS_OUTPUT_color_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("forward referenced IDs have not been defined")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugInstructionWrongResultType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %bool %DbgExt DebugSource %src %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected result type must be a result id of " + "OpTypeVoid")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnit) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugCompilationUnitFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %src HLSL +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Source must be a result id of " + "DebugSource")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailFile) { + const std::string src = R"( +%code = OpString "main() {}" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %DbgExt %code +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand File must be a result id of " + "OpString")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceFailSource) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %DbgExt +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Text must be a result id of " + "OpString")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugSourceNoText) { + const std::string src = R"( +%src = OpString "simple.hlsl" +)"; + + const std::string dbg_inst = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst, "", + extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailName) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %int_32 %int_32 Float +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Name must be a result id of " + "OpString")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeBasicFailSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %float_name Float +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Size must be a result id of " + "OpConstant")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointer) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %float_info Function FlagIsLocal +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypePointerFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%pfloat_info = OpExtInst %void %DbgExt DebugTypePointer %dbg_src Function FlagIsLocal +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifier) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %float_info ConstType +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeQualifierFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float4 main(float arg) { + float foo; + return float4(0, 0, 0, 0); +} +" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%cfloat_info = OpExtInst %void %DbgExt DebugTypeQualifier %comp_unit ConstType +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArray) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %int_32 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayWithVariableSize) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%int_name = OpString "int" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%uint_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Unsigned +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %uint_info %dbg_src 1 1 %main_info FlagIsLocal +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailBaseType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %comp_unit %int_32 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type is not a valid debug " + "type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCount) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %f32_4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailComponentCountZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %u32_0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeArrayFailVariableSizeTypeFloat) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%main_name = OpString "main" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 1 %main_info FlagIsLocal +%float_arr_info = OpExtInst %void %DbgExt DebugTypeArray %float_info %foo_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component Count must be OpConstant with a 32- or " + "64-bits integer scalar type or DebugGlobalVariable or " + "DebugLocalVariable with a 32- or 64-bits unsigned " + "integer scalar type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVector) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 4 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentZero) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeVectorFailComponentFive) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%vfloat_info = OpExtInst %void %DbgExt DebugTypeVector %dbg_src 5 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Base Type must be a result id of " + "DebugTypeBasic")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypedef) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypedef %foo_name %float_info %dbg_src 1 1 %comp_unit +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypedef, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypedef )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypedef, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit)", + "Name"), + std::make_pair(R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit)", + "Base Type"), + std::make_pair(R"(%foo_name %float_info %comp_unit 1 1 %comp_unit)", + "Source"), + std::make_pair(R"(%foo_name %float_info %dbg_src 1 1 %dbg_src)", + "Parent"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info1 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_type_info2 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info +%main_type_info3 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %float_info +%main_type_info4 = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void %float_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailReturn) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %dbg_src %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Return Type is not a valid debug type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeFunctionFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +%float_name = OpString "float" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %float_info %void +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("expected operand Parameter Types is not a valid debug type")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeEnum) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%none = OpExtInst %void %DbgExt DebugInfoNone +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info1 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name +%foo_info2 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name %u32_1 %foo_name +%foo_info3 = OpExtInst %void %DbgExt DebugTypeEnum %foo_name %none %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeEnum, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugTypeEnum )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeEnum, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Underlying Types"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %foo_name)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %dbg_src %int_32 FlagIsPublic %u32_0 %foo_name)", + "Parent"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %void FlagIsPublic %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %u32_0 FlagIsPublic %u32_0 %foo_name)", + "Size"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %foo_name %foo_name)", + "Value"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 1 %comp_unit %int_32 FlagIsPublic %u32_0 %u32_1)", + "Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeCompositeFunctionAndInheritance) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeComposite, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +struct foo : VS_OUTPUT { +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%foo_name = OpString "foo" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite )"; + ss << param.first; + ss << R"( +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember %VS_OUTPUT_pos_name %v4float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_128 FlagIsPublic +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %v4float_info %float_info +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance %foo_info %VS_OUTPUT_info %int_128 %int_128 FlagIsPublic +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + " must be ")); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeComposite, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %comp_unit 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %dbg_src %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %int_128 %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Linkage Name"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %dbg_src FlagIsPublic %VS_OUTPUT_pos_info %main_info %child)", + "Size"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %dbg_src %main_info %child)", + "Members"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %dbg_src %child)", + "Members"), + std::make_pair( + R"(%VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_128 FlagIsPublic %VS_OUTPUT_pos_info %main_info %dbg_src)", + "Members"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeMember, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float pos : SV_POSITION; +}; +main() {} +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%float_name = OpString "float" +%VS_OUTPUT_pos_name = OpString "pos : SV_POSITION" +%VS_OUTPUT_linkage_name = OpString "VS_OUTPUT" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_linkage_name %int_32 FlagIsPublic %VS_OUTPUT_pos_info +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%VS_OUTPUT_pos_info = OpExtInst %void %DbgExt DebugTypeMember )"; + ss << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + if (!param.second.empty()) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second + + " must be a result id of ")); + } +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeMember, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%dbg_src %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + "Name"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %dbg_src %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + ""), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %float_info 2 3 %VS_OUTPUT_info %u32_0 %int_32 FlagIsPublic)", + "Source"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %float_info %u32_0 %int_32 FlagIsPublic)", + "Parent"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %void %int_32 FlagIsPublic)", + "Offset"), + std::make_pair( + R"(%VS_OUTPUT_pos_name %float_info %dbg_src 2 3 %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", + "Size"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugTypeInheritance, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT {}; +struct foo : VS_OUTPUT {}; +" +%VS_OUTPUT_name = OpString "struct VS_OUTPUT" +%foo_name = OpString "foo" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%VS_OUTPUT_info = OpExtInst %void %DbgExt DebugTypeComposite %VS_OUTPUT_name Structure %dbg_src 1 1 %comp_unit %VS_OUTPUT_name %u32_0 FlagIsPublic %child +%foo_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Structure %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%bar_info = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Union %dbg_src 1 1 %comp_unit %foo_name %u32_0 FlagIsPublic +%child = OpExtInst %void %DbgExt DebugTypeInheritance )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugTypeInheritance, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", + "Child must be a result id of"), + std::make_pair(R"(%foo_info %dbg_src %u32_0 %u32_0 FlagIsPublic)", + "Parent must be a result id of"), + std::make_pair( + R"(%bar_info %VS_OUTPUT_info %u32_0 %u32_0 FlagIsPublic)", + "Child must be class or struct debug type"), + std::make_pair(R"(%foo_info %bar_info %u32_0 %u32_0 FlagIsPublic)", + "Parent must be class or struct debug type"), + std::make_pair(R"(%foo_info %VS_OUTPUT_info %void %u32_0 FlagIsPublic)", + "Offset"), + std::make_pair(R"(%foo_info %VS_OUTPUT_info %u32_0 %void FlagIsPublic)", + "Size"), + })); +TEST_P(ValidateGlslStd450SqrtLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01\n"; + ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name << " %f64_0\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugFunctionDeclaration) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugFunction, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration %main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic +%main_info = OpExtInst %void %DbgExt DebugFunction )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugFunction, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic 13 %main)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic 13 %main)", + "Linkage Name"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %void)", + "Function"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic 13 %main %dbg_src)", + "Declaration"), + })); + +TEST_P(ValidateOpenCL100DebugInfoDebugFunctionDeclaration, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "struct VS_OUTPUT { + float4 pos : SV_POSITION; +}; +main() {} +" +%main_name = OpString "main" +%main_linkage_name = OpString "v4f_main_f" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_decl = OpExtInst %void %DbgExt DebugFunctionDeclaration )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, + ValidateOpenCL100DebugInfoDebugFunctionDeclaration, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%u32_0 %main_type_info %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Name"), + std::make_pair( + R"(%main_name %dbg_src %dbg_src 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Type"), + std::make_pair( + R"(%main_name %main_type_info %comp_unit 12 1 %comp_unit %main_linkage_name FlagIsPublic)", + "Source"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %dbg_src %main_linkage_name FlagIsPublic)", + "Parent"), + std::make_pair( + R"(%main_name %main_type_info %dbg_src 12 1 %comp_unit %void FlagIsPublic)", + "Linkage Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugLexicalBlock) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock %dbg_src 1 1 %comp_unit %main_name)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugLexicalBlock, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "main() {}" +%main_name = OpString "main" +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_block = OpExtInst %void %DbgExt DebugLexicalBlock )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, "", ss.str(), "", + extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLexicalBlock, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%comp_unit 1 1 %comp_unit %main_name)", "Source"), + std::make_pair(R"(%dbg_src 1 1 %dbg_src %main_name)", "Parent"), + std::make_pair(R"(%dbg_src 1 1 %comp_unit %void)", "Name"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailScope) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %dbg_src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugScopeFailInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %comp_unit %dbg_src +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined At")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugLocalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugLocalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugLocalVariable )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugLocalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 1 10 %comp_unit FlagIsLocal 0)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 1 10 %comp_unit FlagIsLocal 0)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 1 10 %dbg_src FlagIsLocal 0)", + "Parent"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugDeclare) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string body = R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare %foo_info %foo %null_expr +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugDeclareParam) { + CompileSuccessfully(R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %in_var_COLOR + %4 = OpString "test.hlsl" + OpSource HLSL 620 %4 "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %11 = OpString "#line 1 \"test.hlsl\" +void main(float foo:COLOR) {} +" + %14 = OpString "float" + %17 = OpString "src.main" + %20 = OpString "foo" + OpName %in_var_COLOR "in.var.COLOR" + OpName %main "main" + OpName %param_var_foo "param.var.foo" + OpName %src_main "src.main" + OpName %foo "foo" + OpName %bb_entry "bb.entry" + OpDecorate %in_var_COLOR Location 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%_ptr_Input_float = OpTypePointer Input %float + %void = OpTypeVoid + %23 = OpTypeFunction %void +%_ptr_Function_float = OpTypePointer Function %float + %29 = OpTypeFunction %void %_ptr_Function_float + OpLine %4 1 21 +%in_var_COLOR = OpVariable %_ptr_Input_float Input + %10 = OpExtInst %void %1 DebugExpression + %12 = OpExtInst %void %1 DebugSource %4 %11 + %13 = OpExtInst %void %1 DebugCompilationUnit 1 4 %12 HLSL + %15 = OpExtInst %void %1 DebugTypeBasic %14 %uint_32 Float + %16 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %void %15 + %18 = OpExtInst %void %1 DebugFunction %17 %16 %12 1 1 %13 %17 FlagIsProtected|FlagIsPrivate 1 %src_main + %21 = OpExtInst %void %1 DebugLocalVariable %20 %15 %12 1 17 %18 FlagIsLocal 0 + %22 = OpExtInst %void %1 DebugLexicalBlock %12 1 28 %18 + OpLine %4 1 1 + %main = OpFunction %void None %23 + %24 = OpLabel + OpLine %4 1 17 +%param_var_foo = OpVariable %_ptr_Function_float Function + %27 = OpLoad %float %in_var_COLOR + OpLine %4 1 1 + %28 = OpFunctionCall %void %src_main %param_var_foo + OpReturn + OpFunctionEnd + %src_main = OpFunction %void None %29 + OpLine %4 1 17 + %foo = OpFunctionParameter %_ptr_Function_float + %31 = OpExtInst %void %1 DebugDeclare %21 %foo %10 + %bb_entry = OpLabel + OpLine %4 1 29 + OpReturn + OpFunctionEnd +)"); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugDeclare, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%foo = OpVariable %f32_ptr_function Function +%decl = OpExtInst %void %DbgExt DebugDeclare )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugDeclare, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %foo %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %void %null_expr)", "Variable"), + std::make_pair(R"(%foo_info %foo %dbg_src)", "Expression"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugExpression) { + const std::string dbg_inst_header = R"( +%op0 = OpExtInst %void %DbgExt DebugOperation Deref +%op1 = OpExtInst %void %DbgExt DebugOperation Plus +%null_expr = OpExtInst %void %DbgExt DebugExpression %op0 %op1 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugExpressionFail) { + const std::string dbg_inst_header = R"( +%op = OpExtInst %void %DbgExt DebugOperation Deref +%null_expr = OpExtInst %void %DbgExt DebugExpression %op %void +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo("", "", dbg_inst_header, + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Operation must be a result id of DebugOperation")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplate) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateUsedForVariableType) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%opaque = OpExtInst %void %DbgExt DebugTypeComposite %ty_name Class %dbg_src 1 1 %comp_unit %ty_name %dbg_none FlagIsPublic +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %opaque %param +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %temp %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFunction) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailTarget) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %float_info %param +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand Target must be DebugTypeComposite or " + "DebugFunction")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugTypeTemplateFailParam) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "OpaqueType foo; +main() {} +" +%float_name = OpString "float" +%ty_name = OpString "Texture" +%t_name = OpString "T" +%main_name = OpString "main" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +%int_128 = OpConstant %u32 128 +)"; + + const std::string dbg_inst_header = R"( +%dbg_none = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%param = OpExtInst %void %DbgExt DebugTypeTemplateParameter %t_name %float_info %dbg_none %dbg_src 0 0 +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %param %param +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_name FlagIsPublic 1 %main +%temp = OpExtInst %void %DbgExt DebugTypeTemplate %main_info %float_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "expected operand Parameters must be DebugTypeTemplateParameter or " + "DebugTypeTemplateTemplateParameter")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariable) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableStaticMember) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%t = OpExtInst %void %DbgExt DebugTypeComposite %foo_name Class %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsPublic %a +%a = OpExtInst %void %DbgExt DebugTypeMember %foo_name %float_info %dbg_src 0 0 %t %u32_0 %int_32 FlagIsPublic +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate %a +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableDebugInfoNone) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbgNone = OpExtInst %void %DbgExt DebugInfoNone +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %dbgNone FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugGlobalVariableConst) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable %foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %int_32 FlagIsProtected|FlagIsPrivate +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, "", extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugGlobalVariable, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "float foo; void main() {}" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo = OpExtInst %void %DbgExt DebugGlobalVariable )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo(src, size_const, ss.str(), + "", extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugGlobalVariable, + ::testing::ValuesIn(std::vector>{ + std::make_pair( + R"(%void %float_info %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Name"), + std::make_pair( + R"(%foo_name %dbg_src %dbg_src 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Type"), + std::make_pair( + R"(%foo_name %float_info %comp_unit 0 0 %comp_unit %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Source"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %dbg_src %foo_name %f32_input FlagIsProtected|FlagIsPrivate)", + "Scope"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %void %f32_input FlagIsProtected|FlagIsPrivate)", + "Linkage Name"), + std::make_pair( + R"(%foo_name %float_info %dbg_src 0 0 %comp_unit %foo_name %void FlagIsProtected|FlagIsPrivate)", + "Variable"), + })); + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAt) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %inlined_at +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %inlined_at +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Scope")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugInlinedAtFail2) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() {}" +%void_name = OpString "void" +%main_name = OpString "main" +%main_linkage_name = OpString "v_main" +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%main_type_info = OpExtInst %void %DbgExt DebugTypeFunction FlagIsPublic %void +%main_info = OpExtInst %void %DbgExt DebugFunction %main_name %main_type_info %dbg_src 1 1 %comp_unit %main_linkage_name FlagIsPublic 1 %main +%inlined_at = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info +%inlined_at_recursive = OpExtInst %void %DbgExt DebugInlinedAt 0 %main_info %main_info +)"; + + const std::string body = R"( +%main_scope = OpExtInst %void %DbgExt DebugScope %main_info %inlined_at +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, "", dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("expected operand Inlined")); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugValue) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_3 = OpConstant %u32 3 +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %int_3 +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpenCL100DebugInfo, DebugValueWithVariableIndex) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%int_name = OpString "int" +%foo_name = OpString "foo" +%len_name = OpString "length" +)"; + + const std::string size_const = R"( +%int_3 = OpConstant %u32 3 +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%int_info = OpExtInst %void %DbgExt DebugTypeBasic %int_name %int_32 Signed +%v4float_info = OpExtInst %void %DbgExt DebugTypeVector %float_info 4 +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %v4float_info %dbg_src 1 10 %comp_unit FlagIsLocal +%len_info = OpExtInst %void %DbgExt DebugLocalVariable %len_name %int_info %dbg_src 0 0 %comp_unit FlagIsLocal +)"; + + const std::string body = R"( +%value = OpExtInst %void %DbgExt DebugValue %foo_info %int_32 %null_expr %len_info +)"; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, body, extension, "Vertex")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCL100DebugInfoDebugValue, Fail) { + const std::string src = R"( +%src = OpString "simple.hlsl" +%code = OpString "void main() { float foo; }" +%float_name = OpString "float" +%foo_name = OpString "foo" +)"; + + const std::string size_const = R"( +%int_32 = OpConstant %u32 32 +)"; + + const std::string dbg_inst_header = R"( +%dbg_src = OpExtInst %void %DbgExt DebugSource %src %code +%comp_unit = OpExtInst %void %DbgExt DebugCompilationUnit 2 4 %dbg_src HLSL +%null_expr = OpExtInst %void %DbgExt DebugExpression +%float_info = OpExtInst %void %DbgExt DebugTypeBasic %float_name %int_32 Float +%foo_info = OpExtInst %void %DbgExt DebugLocalVariable %foo_name %float_info %dbg_src 1 10 %comp_unit FlagIsLocal 0 +)"; + + const auto& param = GetParam(); + + std::ostringstream ss; + ss << R"( +%decl = OpExtInst %void %DbgExt DebugValue )" + << param.first; + + const std::string extension = R"( +%DbgExt = OpExtInstImport "OpenCL.DebugInfo.100" +)"; + + CompileSuccessfully(GenerateShaderCodeForDebugInfo( + src, size_const, dbg_inst_header, ss.str(), extension, "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("expected operand " + param.second)); +} + +INSTANTIATE_TEST_SUITE_P( + AllOpenCL100DebugInfoFail, ValidateOpenCL100DebugInfoDebugValue, + ::testing::ValuesIn(std::vector>{ + std::make_pair(R"(%dbg_src %int_32 %null_expr)", "Local Variable"), + std::make_pair(R"(%foo_info %int_32 %dbg_src)", "Expression"), + std::make_pair(R"(%foo_info %int_32 %null_expr %dbg_src)", "Indexes"), + })); + +TEST_P(ValidateGlslStd450SqrtLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be a float scalar " + "or vector type")); +} + +TEST_P(ValidateGlslStd450SqrtLike, IntOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllSqrtLike, ValidateGlslStd450SqrtLike, + ::testing::ValuesIn(std::vector{ + "Round", + "RoundEven", + "FAbs", + "Trunc", + "FSign", + "Floor", + "Ceil", + "Fract", + "Sqrt", + "InverseSqrt", + "Normalize", + })); + +TEST_P(ValidateGlslStd450FMinLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %f32_1\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01 %f32vec2_12\n"; + ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name + << " %f64_0 %f64_0\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450FMinLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0 %f32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be a float scalar " + "or vector type")); +} + +TEST_P(ValidateGlslStd450FMinLike, IntOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %f32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +TEST_P(ValidateGlslStd450FMinLike, IntOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %f32_0 %u32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllFMinLike, ValidateGlslStd450FMinLike, + ::testing::ValuesIn(std::vector{ + "FMin", + "FMax", + "Step", + "Reflect", + "NMin", + "NMax", + })); + +TEST_P(ValidateGlslStd450FClampLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %f32_1 %f32_2\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01 %f32vec2_01 %f32vec2_12\n"; + ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name + << " %f64_0 %f64_0 %f64_1\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450FClampLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %f32_0 %f32_1 %f32_2\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be a float scalar " + "or vector type")); +} + +TEST_P(ValidateGlslStd450FClampLike, IntOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %u32_0 %f32_0 %f32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +TEST_P(ValidateGlslStd450FClampLike, IntOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %f32_0 %u32_0 %f32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +TEST_P(ValidateGlslStd450FClampLike, IntOperand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %f32_1 %f32_0 %u32_2\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllFClampLike, ValidateGlslStd450FClampLike, + ::testing::ValuesIn(std::vector{ + "FClamp", + "FMix", + "SmoothStep", + "Fma", + "FaceForward", + "NClamp", + })); + +TEST_P(ValidateGlslStd450SAbsLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %s32 %extinst " << ext_inst_name << " %u32_1\n"; + ss << "%val2 = OpExtInst %s32 %extinst " << ext_inst_name << " %s32_1\n"; + ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n"; + ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name << " %s32_1\n"; + ss << "%val5 = OpExtInst %s32vec2 %extinst " << ext_inst_name + << " %s32vec2_01\n"; + ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01\n"; + ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %s32vec2_01\n"; + ss << "%val8 = OpExtInst %s32vec2 %extinst " << ext_inst_name + << " %u32vec2_01\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450SAbsLike, FloatResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be an int scalar " + "or vector type")); +} + +TEST_P(ValidateGlslStd450SAbsLike, FloatOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + " %f32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or " + "vectors")); +} + +TEST_P(ValidateGlslStd450SAbsLike, WrongDimOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + " %s32vec2_01\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same dimension as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450SAbsLike, WrongBitWidthOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %s32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same bit width as " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllSAbsLike, ValidateGlslStd450SAbsLike, + ::testing::ValuesIn(std::vector{ + "SAbs", + "SSign", + "FindILsb", + "FindUMsb", + "FindSMsb", + })); + +TEST_F(ValidateExtInst, FindUMsbNot32Bit) { + const std::string body = R"( +%val1 = OpExtInst %s64 %extinst FindUMsb %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 FindUMsb: this instruction is currently " + "limited to 32-bit width components")); +} + +TEST_F(ValidateExtInst, FindSMsbNot32Bit) { + const std::string body = R"( +%val1 = OpExtInst %s64 %extinst FindSMsb %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 FindSMsb: this instruction is currently " + "limited to 32-bit width components")); +} + +TEST_P(ValidateGlslStd450UMinLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %s32 %extinst " << ext_inst_name + << " %u32_1 %s32_2\n"; + ss << "%val2 = OpExtInst %s32 %extinst " << ext_inst_name + << " %s32_1 %u32_2\n"; + ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %s32_2\n"; + ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name + << " %s32_1 %u32_2\n"; + ss << "%val5 = OpExtInst %s32vec2 %extinst " << ext_inst_name + << " %s32vec2_01 %u32vec2_01\n"; + ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %s32vec2_01\n"; + ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %s32vec2_01 %u32vec2_01\n"; + ss << "%val8 = OpExtInst %s32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %s32vec2_01\n"; + ss << "%val9 = OpExtInst %s64 %extinst " << ext_inst_name + << " %u64_1 %s64_0\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450UMinLike, FloatResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %u32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be an int scalar " + "or vector type")); +} + +TEST_P(ValidateGlslStd450UMinLike, FloatOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + " %f32_0 %u32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or " + "vectors")); +} + +TEST_P(ValidateGlslStd450UMinLike, FloatOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + " %u32_0 %f32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or " + "vectors")); +} + +TEST_P(ValidateGlslStd450UMinLike, WrongDimOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + + " %s32vec2_01 %s32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same dimension as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450UMinLike, WrongDimOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + + " %s32_0 %s32vec2_01\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same dimension as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450UMinLike, WrongBitWidthOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %s32_0 %s64_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same bit width as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450UMinLike, WrongBitWidthOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + " %s64_0 %s32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same bit width as " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllUMinLike, ValidateGlslStd450UMinLike, + ::testing::ValuesIn(std::vector{ + "UMin", + "SMin", + "UMax", + "SMax", + })); + +TEST_P(ValidateGlslStd450UClampLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %s32 %extinst " << ext_inst_name + << " %s32_0 %u32_1 %s32_2\n"; + ss << "%val2 = OpExtInst %s32 %extinst " << ext_inst_name + << " %u32_0 %s32_1 %u32_2\n"; + ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name + << " %s32_0 %u32_1 %s32_2\n"; + ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %s32_1 %u32_2\n"; + ss << "%val5 = OpExtInst %s32vec2 %extinst " << ext_inst_name + << " %s32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %s32vec2_01 %s32vec2_12\n"; + ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %s32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val8 = OpExtInst %s32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %s32vec2_01 %s32vec2_12\n"; + ss << "%val9 = OpExtInst %s64 %extinst " << ext_inst_name + << " %u64_1 %s64_0 %s64_1\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450UClampLike, FloatResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %u32_0 %u32_0 %u32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be an int scalar " + "or vector type")); +} + +TEST_P(ValidateGlslStd450UClampLike, FloatOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + + " %f32_0 %u32_0 %u32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or " + "vectors")); +} + +TEST_P(ValidateGlslStd450UClampLike, FloatOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + + " %u32_0 %f32_0 %u32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or " + "vectors")); +} + +TEST_P(ValidateGlslStd450UClampLike, FloatOperand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + + " %u32_0 %u32_0 %f32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to be int scalars or " + "vectors")); +} + +TEST_P(ValidateGlslStd450UClampLike, WrongDimOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + + " %s32vec2_01 %s32_0 %u32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same dimension as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450UClampLike, WrongDimOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + + " %s32_0 %s32vec2_01 %u32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same dimension as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450UClampLike, WrongDimOperand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s32 %extinst " + ext_inst_name + + " %s32_0 %u32_1 %s32vec2_01\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same dimension as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %s32_0 %s64_0 %s64_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same bit width as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %s64_0 %s32_0 %s64_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same bit width as " + "Result Type")); +} + +TEST_P(ValidateGlslStd450UClampLike, WrongBitWidthOperand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %s64 %extinst " + ext_inst_name + + " %s64_0 %s64_0 %s32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected all operands to have the same bit width as " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllUClampLike, ValidateGlslStd450UClampLike, + ::testing::ValuesIn(std::vector{ + "UClamp", + "SClamp", + })); + +TEST_P(ValidateGlslStd450SinLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450SinLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be a 16 or 32-bit scalar " + "or vector float type")); +} + +TEST_P(ValidateGlslStd450SinLike, F64ResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f64 %extinst " + ext_inst_name + " %f32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be a 16 or 32-bit scalar " + "or vector float type")); +} + +TEST_P(ValidateGlslStd450SinLike, IntOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllSinLike, ValidateGlslStd450SinLike, + ::testing::ValuesIn(std::vector{ + "Radians", + "Degrees", + "Sin", + "Cos", + "Tan", + "Asin", + "Acos", + "Atan", + "Sinh", + "Cosh", + "Tanh", + "Asinh", + "Acosh", + "Atanh", + "Exp", + "Exp2", + "Log", + "Log2", + })); + +TEST_P(ValidateGlslStd450PowLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_1 %f32_1\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01 %f32vec2_12\n"; + CompileSuccessfully(GenerateShaderCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450PowLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_1 %f32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be a 16 or 32-bit scalar " + "or vector float type")); +} + +TEST_P(ValidateGlslStd450PowLike, F64ResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f64 %extinst " + ext_inst_name + " %f32_1 %f32_0\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected Result Type to be a 16 or 32-bit scalar " + "or vector float type")); +} + +TEST_P(ValidateGlslStd450PowLike, IntOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %f32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +TEST_P(ValidateGlslStd450PowLike, IntOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %f32_0 %u32_1\n"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllPowLike, ValidateGlslStd450PowLike, + ::testing::ValuesIn(std::vector{ + "Atan2", + "Pow", + })); + +TEST_F(ValidateExtInst, GlslStd450DeterminantSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Determinant %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450DeterminantIncompatibleResultType) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst Determinant %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Determinant: " + "expected operand X component type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450DeterminantNotMatrix) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Determinant %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Determinant: " + "expected operand X to be a square matrix")); +} + +TEST_F(ValidateExtInst, GlslStd450DeterminantMatrixNotSquare) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Determinant %f32mat23_121212 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Determinant: " + "expected operand X to be a square matrix")); +} + +TEST_F(ValidateExtInst, GlslStd450MatrixInverseSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32mat22 %extinst MatrixInverse %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450MatrixInverseIncompatibleResultType) { + const std::string body = R"( +%val1 = OpExtInst %f32mat33 %extinst MatrixInverse %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 MatrixInverse: " + "expected operand X type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450MatrixInverseNotMatrix) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst MatrixInverse %f32mat22_1212 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 MatrixInverse: " + "expected Result Type to be a square matrix")); +} + +TEST_F(ValidateExtInst, GlslStd450MatrixInverseMatrixNotSquare) { + const std::string body = R"( +%val1 = OpExtInst %f32mat23 %extinst MatrixInverse %f32mat23_121212 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 MatrixInverse: " + "expected Result Type to be a square matrix")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Modf %f32_h %f32_output +%val2 = OpExtInst %f32vec2 %extinst Modf %f32vec2_01 %f32vec2_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450ModfIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst Modf %f32_h %f32_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Modf: " + "expected Result Type to be a scalar or vector " + "float type")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfXNotOfResultType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Modf %f64_0 %f32_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Modf: " + "expected operand X type to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfINotPointer) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Modf %f32_h %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Modf: " + "expected operand I to be a pointer")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfIDataNotOfResultType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Modf %f32_h %f32vec2_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Modf: " + "expected operand I data type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfStructSuccess) { + const std::string body = R"( +%val1 = OpExtInst %struct_f32_f32 %extinst ModfStruct %f32_h +%val2 = OpExtInst %struct_f32vec2_f32vec2 %extinst ModfStruct %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450ModfStructResultTypeNotStruct) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst ModfStruct %f32_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 ModfStruct: " + "expected Result Type to be a struct with two " + "identical scalar or vector float type members")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfStructResultTypeStructWrongSize) { + const std::string body = R"( +%val1 = OpExtInst %struct_f32_f32_f32 %extinst ModfStruct %f32_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 ModfStruct: " + "expected Result Type to be a struct with two " + "identical scalar or vector float type members")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfStructResultTypeStructWrongFirstMember) { + const std::string body = R"( +%val1 = OpExtInst %struct_u32_f32 %extinst ModfStruct %f32_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 ModfStruct: " + "expected Result Type to be a struct with two " + "identical scalar or vector float type members")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfStructResultTypeStructMembersNotEqual) { + const std::string body = R"( +%val1 = OpExtInst %struct_f32_f64 %extinst ModfStruct %f32_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 ModfStruct: " + "expected Result Type to be a struct with two " + "identical scalar or vector float type members")); +} + +TEST_F(ValidateExtInst, GlslStd450ModfStructXWrongType) { + const std::string body = R"( +%val1 = OpExtInst %struct_f32_f32 %extinst ModfStruct %f64_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 ModfStruct: " + "expected operand X type to be equal to members of " + "Result Type struct")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Frexp %f32_h %u32_output +%val2 = OpExtInst %f32vec2 %extinst Frexp %f32vec2_01 %u32vec2_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst Frexp %f32_h %u32_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Frexp: " + "expected Result Type to be a scalar or vector " + "float type")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpWrongXType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Frexp %u32_1 %u32_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Frexp: " + "expected operand X type to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpExpNotPointer) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Frexp %f32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Frexp: " + "expected operand Exp to be a pointer")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpExpNotInt32Pointer) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Frexp %f32_1 %f32_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Frexp: " + "expected operand Exp data type to be a 32-bit int " + "scalar or vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpExpWrongComponentNumber) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst Frexp %f32vec2_01 %u32_output +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Frexp: " + "expected operand Exp data type to have the same " + "component number as Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450LdexpSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Ldexp %f32_h %u32_2 +%val2 = OpExtInst %f32vec2 %extinst Ldexp %f32vec2_01 %u32vec2_12 +%val3 = OpExtInst %f32 %extinst Ldexp %f32_h %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450LdexpIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst Ldexp %f32_h %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Ldexp: " + "expected Result Type to be a scalar or vector " + "float type")); +} + +TEST_F(ValidateExtInst, GlslStd450LdexpWrongXType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Ldexp %u32_1 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Ldexp: " + "expected operand X type to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450LdexpFloatExp) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Ldexp %f32_1 %f32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Ldexp: " + "expected operand Exp to be a 32-bit int scalar " + "or vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450LdexpExpWrongSize) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst Ldexp %f32vec2_12 %u32_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Ldexp: " + "expected operand Exp to have the same component " + "number as Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpStructSuccess) { + const std::string body = R"( +%val1 = OpExtInst %struct_f32_u32 %extinst FrexpStruct %f32_h +%val2 = OpExtInst %struct_f32vec2_u32vec2 %extinst FrexpStruct %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpStructResultTypeNotStruct) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst FrexpStruct %f32_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 FrexpStruct: " + "expected Result Type to be a struct with two members, " + "first member a float scalar or vector, second member " + "a 32-bit int scalar or vector with the same number of " + "components as the first member")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpStructResultTypeStructWrongSize) { + const std::string body = R"( +%val1 = OpExtInst %struct_f32_u32_f32 %extinst FrexpStruct %f32_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 FrexpStruct: " + "expected Result Type to be a struct with two members, " + "first member a float scalar or vector, second member " + "a 32-bit int scalar or vector with the same number of " + "components as the first member")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpStructResultTypeStructWrongMember1) { + const std::string body = R"( +%val1 = OpExtInst %struct_u32_u32 %extinst FrexpStruct %f32_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 FrexpStruct: " + "expected Result Type to be a struct with two members, " + "first member a float scalar or vector, second member " + "a 32-bit int scalar or vector with the same number of " + "components as the first member")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpStructResultTypeStructWrongMember2) { + const std::string body = R"( +%val1 = OpExtInst %struct_f32_f32 %extinst FrexpStruct %f32_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 FrexpStruct: " + "expected Result Type to be a struct with two members, " + "first member a float scalar or vector, second member " + "a 32-bit int scalar or vector with the same number of " + "components as the first member")); +} + +TEST_F(ValidateExtInst, GlslStd450FrexpStructXWrongType) { + const std::string body = R"( +%val1 = OpExtInst %struct_f32_u32 %extinst FrexpStruct %f64_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 FrexpStruct: " + "expected operand X type to be equal to the first " + "member of Result Type struct")); +} + +TEST_F(ValidateExtInst, + GlslStd450FrexpStructResultTypeStructRightInt16Member2) { + const std::string body = R"( +%val1 = OpExtInst %struct_f16_u16 %extinst FrexpStruct %f16_h +)"; + + const std::string extension = R"( +OpExtension "SPV_AMD_gpu_shader_int16" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extension)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, + GlslStd450FrexpStructResultTypeStructWrongInt16Member2) { + const std::string body = R"( +%val1 = OpExtInst %struct_f16_u16 %extinst FrexpStruct %f16_h +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 FrexpStruct: " + "expected Result Type to be a struct with two members, " + "first member a float scalar or vector, second member " + "a 32-bit int scalar or vector with the same number of " + "components as the first member")); +} + +TEST_P(ValidateGlslStd450Pack, Success) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string vec_str = + num_components == 2 ? " %f32vec2_01\n" : " %f32vec4_0123\n"; + + std::ostringstream body; + body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst " + << ext_inst_name << vec_str; + body << "%val2 = OpExtInst %s" << total_bit_width << " %extinst " + << ext_inst_name << vec_str; + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450Pack, Float32ResultType) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string vec_str = + num_components == 2 ? " %f32vec2_01\n" : " %f32vec4_0123\n"; + + std::ostringstream body; + body << "%val1 = OpExtInst %f" << total_bit_width << " %extinst " + << ext_inst_name << vec_str; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected Result Type to be " << total_bit_width + << "-bit int scalar type"; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Pack, Int16ResultType) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string vec_str = + num_components == 2 ? " %f32vec2_01\n" : " %f32vec4_0123\n"; + + std::ostringstream body; + body << "%val1 = OpExtInst %u16 %extinst " << ext_inst_name << vec_str; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected Result Type to be " << total_bit_width + << "-bit int scalar type"; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Pack, VNotVector) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + + std::ostringstream body; + body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst " + << ext_inst_name << " %f32_1\n"; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected operand V to be a 32-bit float vector of size " + << num_components; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Pack, VNotFloatVector) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string vec_str = + num_components == 2 ? " %u32vec2_01\n" : " %u32vec4_0123\n"; + + std::ostringstream body; + body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst " + << ext_inst_name << vec_str; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected operand V to be a 32-bit float vector of size " + << num_components; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Pack, VNotFloat32Vector) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string vec_str = + num_components == 2 ? " %f64vec2_01\n" : " %f64vec4_0123\n"; + + std::ostringstream body; + body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst " + << ext_inst_name << vec_str; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected operand V to be a 32-bit float vector of size " + << num_components; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Pack, VWrongSizeVector) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string vec_str = + num_components == 4 ? " %f32vec2_01\n" : " %f32vec4_0123\n"; + + std::ostringstream body; + body << "%val1 = OpExtInst %u" << total_bit_width << " %extinst " + << ext_inst_name << vec_str; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected operand V to be a 32-bit float vector of size " + << num_components; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +INSTANTIATE_TEST_SUITE_P(AllPack, ValidateGlslStd450Pack, + ::testing::ValuesIn(std::vector{ + "PackSnorm4x8", + "PackUnorm4x8", + "PackSnorm2x16", + "PackUnorm2x16", + "PackHalf2x16", + })); + +TEST_F(ValidateExtInst, PackDouble2x32Success) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst PackDouble2x32 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, PackDouble2x32Float32ResultType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst PackDouble2x32 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 PackDouble2x32: expected Result Type to " + "be 64-bit float scalar type")); +} + +TEST_F(ValidateExtInst, PackDouble2x32Int64ResultType) { + const std::string body = R"( +%val1 = OpExtInst %u64 %extinst PackDouble2x32 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 PackDouble2x32: expected Result Type to " + "be 64-bit float scalar type")); +} + +TEST_F(ValidateExtInst, PackDouble2x32VNotVector) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst PackDouble2x32 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 PackDouble2x32: expected operand V to be " + "a 32-bit int vector of size 2")); +} + +TEST_F(ValidateExtInst, PackDouble2x32VNotIntVector) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst PackDouble2x32 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 PackDouble2x32: expected operand V to be " + "a 32-bit int vector of size 2")); +} + +TEST_F(ValidateExtInst, PackDouble2x32VNotInt32Vector) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst PackDouble2x32 %u64vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 PackDouble2x32: expected operand V to be " + "a 32-bit int vector of size 2")); +} + +TEST_F(ValidateExtInst, PackDouble2x32VWrongSize) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst PackDouble2x32 %u32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 PackDouble2x32: expected operand V to be " + "a 32-bit int vector of size 2")); +} + +TEST_P(ValidateGlslStd450Unpack, Success) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string result_type_str = + num_components == 2 ? "%f32vec2" : " %f32vec4"; + + std::ostringstream body; + body << "%val1 = OpExtInst " << result_type_str << " %extinst " + << ext_inst_name << " %u" << total_bit_width << "_1\n"; + body << "%val2 = OpExtInst " << result_type_str << " %extinst " + << ext_inst_name << " %s" << total_bit_width << "_1\n"; + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateGlslStd450Unpack, ResultTypeNotVector) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string result_type_str = "%f32"; + + std::ostringstream body; + body << "%val1 = OpExtInst " << result_type_str << " %extinst " + << ext_inst_name << " %u" << total_bit_width << "_1\n"; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected Result Type to be a 32-bit float vector of size " + << num_components; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Unpack, ResultTypeNotFloatVector) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string result_type_str = + num_components == 2 ? "%u32vec2" : " %u32vec4"; + + std::ostringstream body; + body << "%val1 = OpExtInst " << result_type_str << " %extinst " + << ext_inst_name << " %u" << total_bit_width << "_1\n"; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected Result Type to be a 32-bit float vector of size " + << num_components; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Unpack, ResultTypeNotFloat32Vector) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string result_type_str = + num_components == 2 ? "%f64vec2" : " %f64vec4"; + + std::ostringstream body; + body << "%val1 = OpExtInst " << result_type_str << " %extinst " + << ext_inst_name << " %u" << total_bit_width << "_1\n"; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected Result Type to be a 32-bit float vector of size " + << num_components; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Unpack, ResultTypeWrongSize) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string result_type_str = + num_components == 4 ? "%f32vec2" : " %f32vec4"; + + std::ostringstream body; + body << "%val1 = OpExtInst " << result_type_str << " %extinst " + << ext_inst_name << " %u" << total_bit_width << "_1\n"; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected Result Type to be a 32-bit float vector of size " + << num_components; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Unpack, ResultPNotInt) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const std::string result_type_str = + num_components == 2 ? "%f32vec2" : " %f32vec4"; + + std::ostringstream body; + body << "%val1 = OpExtInst " << result_type_str << " %extinst " + << ext_inst_name << " %f" << total_bit_width << "_1\n"; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected operand P to be a " << total_bit_width + << "-bit int scalar"; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +TEST_P(ValidateGlslStd450Unpack, ResultPWrongBitWidth) { + const std::string ext_inst_name = GetParam(); + const uint32_t num_components = GetPackedNumComponents(ext_inst_name); + const uint32_t packed_bit_width = GetPackedBitWidth(ext_inst_name); + const uint32_t total_bit_width = num_components * packed_bit_width; + const uint32_t wrong_bit_width = total_bit_width == 32 ? 64 : 32; + const std::string result_type_str = + num_components == 2 ? "%f32vec2" : " %f32vec4"; + + std::ostringstream body; + body << "%val1 = OpExtInst " << result_type_str << " %extinst " + << ext_inst_name << " %u" << wrong_bit_width << "_1\n"; + + std::ostringstream expected; + expected << "GLSL.std.450 " << ext_inst_name + << ": expected operand P to be a " << total_bit_width + << "-bit int scalar"; + + CompileSuccessfully(GenerateShaderCode(body.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected.str())); +} + +INSTANTIATE_TEST_SUITE_P(AllUnpack, ValidateGlslStd450Unpack, + ::testing::ValuesIn(std::vector{ + "UnpackSnorm4x8", + "UnpackUnorm4x8", + "UnpackSnorm2x16", + "UnpackUnorm2x16", + "UnpackHalf2x16", + })); + +TEST_F(ValidateExtInst, UnpackDouble2x32Success) { + const std::string body = R"( +%val1 = OpExtInst %u32vec2 %extinst UnpackDouble2x32 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, UnpackDouble2x32ResultTypeNotVector) { + const std::string body = R"( +%val1 = OpExtInst %u64 %extinst UnpackDouble2x32 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 UnpackDouble2x32: expected Result Type " + "to be a 32-bit int vector of size 2")); +} + +TEST_F(ValidateExtInst, UnpackDouble2x32ResultTypeNotIntVector) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst UnpackDouble2x32 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 UnpackDouble2x32: expected Result Type " + "to be a 32-bit int vector of size 2")); +} + +TEST_F(ValidateExtInst, UnpackDouble2x32ResultTypeNotInt32Vector) { + const std::string body = R"( +%val1 = OpExtInst %u64vec2 %extinst UnpackDouble2x32 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 UnpackDouble2x32: expected Result Type " + "to be a 32-bit int vector of size 2")); +} + +TEST_F(ValidateExtInst, UnpackDouble2x32ResultTypeWrongSize) { + const std::string body = R"( +%val1 = OpExtInst %u32vec4 %extinst UnpackDouble2x32 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 UnpackDouble2x32: expected Result Type " + "to be a 32-bit int vector of size 2")); +} + +TEST_F(ValidateExtInst, UnpackDouble2x32VNotFloat) { + const std::string body = R"( +%val1 = OpExtInst %u32vec2 %extinst UnpackDouble2x32 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 UnpackDouble2x32: expected operand V to " + "be a 64-bit float scalar")); +} + +TEST_F(ValidateExtInst, UnpackDouble2x32VNotFloat64) { + const std::string body = R"( +%val1 = OpExtInst %u32vec2 %extinst UnpackDouble2x32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 UnpackDouble2x32: expected operand V to " + "be a 64-bit float scalar")); +} + +TEST_F(ValidateExtInst, GlslStd450LengthSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Length %f32_1 +%val2 = OpExtInst %f32 %extinst Length %f32vec2_01 +%val3 = OpExtInst %f32 %extinst Length %f32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450LengthIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst Length %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Length: " + "expected Result Type to be a float scalar type")); +} + +TEST_F(ValidateExtInst, GlslStd450LengthIntX) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Length %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Length: " + "expected operand X to be of float scalar or " + "vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450LengthDifferentType) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst Length %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Length: " + "expected operand X component type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450DistanceSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Distance %f32_0 %f32_1 +%val2 = OpExtInst %f32 %extinst Distance %f32vec2_01 %f32vec2_12 +%val3 = OpExtInst %f32 %extinst Distance %f32vec4_0123 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450DistanceIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst Distance %f32vec2_01 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Distance: " + "expected Result Type to be a float scalar type")); +} + +TEST_F(ValidateExtInst, GlslStd450DistanceIntP0) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Distance %u32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Distance: " + "expected operand P0 to be of float scalar or " + "vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450DistanceF64VectorP0) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Distance %f64vec2_01 %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Distance: " + "expected operand P0 component type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450DistanceIntP1) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Distance %f32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Distance: " + "expected operand P1 to be of float scalar or " + "vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450DistanceF64VectorP1) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Distance %f32vec2_12 %f64vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Distance: " + "expected operand P1 component type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450DistanceDifferentSize) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Distance %f32vec2_01 %f32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Distance: " + "expected operands P0 and P1 to have the same number " + "of components")); +} + +TEST_F(ValidateExtInst, GlslStd450CrossSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32vec3 %extinst Cross %f32vec3_012 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450CrossIntVectorResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32vec3 %extinst Cross %f32vec3_012 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Cross: " + "expected Result Type to be a float vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450CrossResultTypeWrongSize) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst Cross %f32vec3_012 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Cross: " + "expected Result Type to have 3 components")); +} + +TEST_F(ValidateExtInst, GlslStd450CrossXWrongType) { + const std::string body = R"( +%val1 = OpExtInst %f32vec3 %extinst Cross %f64vec3_012 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Cross: " + "expected operand X type to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450CrossYWrongType) { + const std::string body = R"( +%val1 = OpExtInst %f32vec3 %extinst Cross %f32vec3_123 %f64vec3_012 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Cross: " + "expected operand Y type to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450RefractSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst Refract %f32_1 %f32_1 %f32_1 +%val2 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %f16_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450RefractIntVectorResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Refract: " + "expected Result Type to be a float scalar or " + "vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450RefractIntVectorI) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst Refract %u32vec2_01 %f32vec2_01 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Refract: " + "expected operand I to be of type equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450RefractIntVectorN) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %u32vec2_01 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Refract: " + "expected operand N to be of type equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450RefractIntEta) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Refract: " + "expected operand Eta to be a float scalar")); +} + +TEST_F(ValidateExtInst, GlslStd450RefractFloat64Eta) { + // SPIR-V issue 337: Eta can be 64-bit float scalar. + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateExtInst, GlslStd450RefractVectorEta) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst Refract %f32vec2_01 %f32vec2_01 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 Refract: " + "expected operand Eta to be a float scalar")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_input +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtCentroid %f32vec2_input +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidNoCapability) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_input +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid requires " + "capability InterpolationFunction")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst InterpolateAtCentroid %f32_input +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid: " + "expected Result Type to be a 32-bit float scalar " + "or vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidF64ResultType) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst InterpolateAtCentroid %f32_input +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid: " + "expected Result Type to be a 32-bit float scalar " + "or vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidNotPointer) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid: " + "expected Interpolant to be a pointer")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidWrongDataType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32vec2_input +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid: " + "expected Interpolant data type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidWrongStorageClass) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_output +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid: " + "expected Interpolant storage class to be Input")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtCentroidWrongExecutionModel) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtCentroid %f32_input +)"; + + CompileSuccessfully(GenerateShaderCode( + body, "OpCapability InterpolationFunction\n", "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtCentroid requires " + "Fragment execution model")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u32_1 +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtSample %f32vec2_input %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleNoCapability) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample requires " + "capability InterpolationFunction")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst InterpolateAtSample %f32_input %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Result Type to be a 32-bit float scalar " + "or vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleF64ResultType) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst InterpolateAtSample %f32_input %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Result Type to be a 32-bit float scalar " + "or vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleNotPointer) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_1 %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Interpolant to be a pointer")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleWrongDataType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32vec2_input %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Interpolant data type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleWrongStorageClass) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_output %u32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Interpolant storage class to be Input")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleFloatSample) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %f32_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Sample to be 32-bit integer")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleU64Sample) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u64_1 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample: " + "expected Sample to be 32-bit integer")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtSampleWrongExecutionModel) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtSample %f32_input %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode( + body, "OpCapability InterpolationFunction\n", "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtSample requires " + "Fragment execution model")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec2_01 +%val2 = OpExtInst %f32vec2 %extinst InterpolateAtOffset %f32vec2_input %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetNoCapability) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset requires " + "capability InterpolationFunction")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst InterpolateAtOffset %f32_input %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Result Type to be a 32-bit float scalar " + "or vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetF64ResultType) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst InterpolateAtOffset %f32_input %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Result Type to be a 32-bit float scalar " + "or vector type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetNotPointer) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_1 %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Interpolant to be a pointer")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetWrongDataType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32vec2_input %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Interpolant data type to be equal to " + "Result Type")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetWrongStorageClass) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_output %f32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Interpolant storage class to be Input")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetOffsetNotVector) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32_0 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Offset to be a vector of 2 32-bit floats")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetOffsetNotVector2) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec3_012 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Offset to be a vector of 2 32-bit floats")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetOffsetNotFloatVector) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %u32vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Offset to be a vector of 2 32-bit floats")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetOffsetNotFloat32Vector) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f64vec2_01 +)"; + + CompileSuccessfully( + GenerateShaderCode(body, "OpCapability InterpolationFunction\n")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset: " + "expected Offset to be a vector of 2 32-bit floats")); +} + +TEST_F(ValidateExtInst, GlslStd450InterpolateAtOffsetWrongExecutionModel) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst InterpolateAtOffset %f32_input %f32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode( + body, "OpCapability InterpolationFunction\n", "Vertex")); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("GLSL.std.450 InterpolateAtOffset requires " + "Fragment execution model")); +} + +TEST_P(ValidateOpenCLStdSqrtLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01\n"; + ss << "%val3 = OpExtInst %f32vec4 %extinst " << ext_inst_name + << " %f32vec4_0123\n"; + ss << "%val4 = OpExtInst %f64 %extinst " << ext_inst_name << " %f64_0\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdSqrtLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be a float scalar " + "or vector type")); +} + +TEST_P(ValidateOpenCLStdSqrtLike, IntOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P( + AllSqrtLike, ValidateOpenCLStdSqrtLike, + ::testing::ValuesIn(std::vector{ + "acos", "acosh", "acospi", "asin", + "asinh", "asinpi", "atan", "atanh", + "atanpi", "cbrt", "ceil", "cos", + "cosh", "cospi", "erfc", "erf", + "exp", "exp2", "exp10", "expm1", + "fabs", "floor", "log", "log2", + "log10", "log1p", "logb", "rint", + "round", "rsqrt", "sin", "sinh", + "sinpi", "sqrt", "tan", "tanh", + "tanpi", "tgamma", "trunc", "half_cos", + "half_exp", "half_exp2", "half_exp10", "half_log", + "half_log2", "half_log10", "half_recip", "half_rsqrt", + "half_sin", "half_sqrt", "half_tan", "lgamma", + "native_cos", "native_exp", "native_exp2", "native_exp10", + "native_log", "native_log2", "native_log10", "native_recip", + "native_rsqrt", "native_sin", "native_sqrt", "native_tan", + "degrees", "radians", "sign", + })); + +TEST_P(ValidateOpenCLStdFMinLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %f32_1\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01 %f32vec2_12\n"; + ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name + << " %f64_0 %f64_0\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdFMinLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be a float scalar " + "or vector type")); +} + +TEST_P(ValidateOpenCLStdFMinLike, IntOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +TEST_P(ValidateOpenCLStdFMinLike, IntOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %f32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllFMinLike, ValidateOpenCLStdFMinLike, + ::testing::ValuesIn(std::vector{ + "atan2", "atan2pi", "copysign", + "fdim", "fmax", "fmin", + "fmod", "maxmag", "minmag", + "hypot", "nextafter", "pow", + "powr", "remainder", "half_divide", + "half_powr", "native_divide", "native_powr", + "step", "fmax_common", "fmin_common", + })); + +TEST_P(ValidateOpenCLStdFClampLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %f32_1 %f32_2\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01 %f32vec2_01 %f32vec2_12\n"; + ss << "%val3 = OpExtInst %f64 %extinst " << ext_inst_name + << " %f64_0 %f64_0 %f64_1\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdFClampLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %f32_0 %f32_1 %f32_2\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be a float scalar " + "or vector type")); +} + +TEST_P(ValidateOpenCLStdFClampLike, IntOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %u32_0 %f32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +TEST_P(ValidateOpenCLStdFClampLike, IntOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %f32_0 %u32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +TEST_P(ValidateOpenCLStdFClampLike, IntOperand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %f32_1 %f32_0 %u32_2\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllFClampLike, ValidateOpenCLStdFClampLike, + ::testing::ValuesIn(std::vector{ + "fma", + "mad", + "fclamp", + "mix", + "smoothstep", + })); + +TEST_P(ValidateOpenCLStdSAbsLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n"; + ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n"; + ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n"; + ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name << " %u32_1\n"; + ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01\n"; + ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01\n"; + ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01\n"; + ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdSAbsLike, FloatResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be an int scalar " + "or vector type")); +} + +TEST_P(ValidateOpenCLStdSAbsLike, FloatOperand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdSAbsLike, U64Operand) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u64_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllSAbsLike, ValidateOpenCLStdSAbsLike, + ::testing::ValuesIn(std::vector{ + "s_abs", + "clz", + "ctz", + "popcount", + "u_abs", + })); + +TEST_P(ValidateOpenCLStdUMinLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + ss << "%val9 = OpExtInst %u64 %extinst " << ext_inst_name + << " %u64_1 %u64_0\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdUMinLike, FloatResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %u32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be an int scalar " + "or vector type")); +} + +TEST_P(ValidateOpenCLStdUMinLike, FloatOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0 %u32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMinLike, FloatOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u32_0 %f32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMinLike, U64Operand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u64_0 %u32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMinLike, U64Operand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u32_0 %u64_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllUMinLike, ValidateOpenCLStdUMinLike, + ::testing::ValuesIn(std::vector{ + "s_max", + "u_max", + "s_min", + "u_min", + "s_abs_diff", + "s_add_sat", + "u_add_sat", + "s_mul_hi", + "rotate", + "s_sub_sat", + "u_sub_sat", + "s_hadd", + "u_hadd", + "s_rhadd", + "u_rhadd", + "u_abs_diff", + "u_mul_hi", + })); + +TEST_P(ValidateOpenCLStdUClampLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %u32_1 %u32_2\n"; + ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %u32_1 %u32_2\n"; + ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %u32_1 %u32_2\n"; + ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %u32_1 %u32_2\n"; + ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val9 = OpExtInst %u64 %extinst " << ext_inst_name + << " %u64_1 %u64_0 %u64_1\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdUClampLike, FloatResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %u32_0 %u32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be an int scalar " + "or vector type")); +} + +TEST_P(ValidateOpenCLStdUClampLike, FloatOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %f32_0 %u32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUClampLike, FloatOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %u32_0 %f32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUClampLike, FloatOperand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %u32_0 %u32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUClampLike, U64Operand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %f32_0 %u32_0 %u64_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUClampLike, U64Operand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %u32_0 %f32_0 %u64_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUClampLike, U64Operand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %u32_0 %u32_0 %u64_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllUClampLike, ValidateOpenCLStdUClampLike, + ::testing::ValuesIn(std::vector{ + "s_clamp", + "u_clamp", + "s_mad_hi", + "u_mad_sat", + "s_mad_sat", + "u_mad_hi", + })); + +// ------------------------------------------------------------- +TEST_P(ValidateOpenCLStdUMul24Like, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdUMul24Like, FloatResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32_0 %u32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std " + ext_inst_name + + ": expected Result Type to be a 32-bit int scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdUMul24Like, U64ResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u64 %extinst " + ext_inst_name + " %u64_0 %u64_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std " + ext_inst_name + + ": expected Result Type to be a 32-bit int scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdUMul24Like, FloatOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_0 %u32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMul24Like, FloatOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u32_0 %f32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMul24Like, U64Operand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u64_0 %u32_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMul24Like, U64Operand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %u32_0 %u64_0\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllUMul24Like, ValidateOpenCLStdUMul24Like, + ::testing::ValuesIn(std::vector{ + "s_mul24", + "u_mul24", + })); + +TEST_P(ValidateOpenCLStdUMad24Like, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %u32_1 %u32_2\n"; + ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %u32_1 %u32_2\n"; + ss << "%val3 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %u32_1 %u32_2\n"; + ss << "%val4 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u32_0 %u32_1 %u32_2\n"; + ss << "%val5 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val6 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val7 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n"; + ss << "%val8 = OpExtInst %u32vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01 %u32vec2_12\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdUMad24Like, FloatResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %u32_0 %u32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std " + ext_inst_name + + ": expected Result Type to be a 32-bit int scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdUMad24Like, U64ResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u64 %extinst " + ext_inst_name + + " %u64_0 %u64_0 %u64_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std " + ext_inst_name + + ": expected Result Type to be a 32-bit int scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdUMad24Like, FloatOperand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %f32_0 %u32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMad24Like, FloatOperand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %u32_0 %f32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMad24Like, FloatOperand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %u32_0 %u32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMad24Like, U64Operand1) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %f32_0 %u32_0 %u64_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMad24Like, U64Operand2) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %u32_0 %f32_0 %u64_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdUMad24Like, U64Operand3) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %u32_0 %u32_0 %u64_1\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected types of all operands to be equal to Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllUMad24Like, ValidateOpenCLStdUMad24Like, + ::testing::ValuesIn(std::vector{ + "s_mad24", + "u_mad24", + })); + +TEST_F(ValidateExtInst, OpenCLStdCrossSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32vec3 %extinst cross %f32vec3_012 %f32vec3_123 +%val2 = OpExtInst %f32vec4 %extinst cross %f32vec4_0123 %f32vec4_0123 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdCrossIntVectorResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32vec3 %extinst cross %f32vec3_012 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std cross: " + "expected Result Type to be a float vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdCrossResultTypeWrongSize) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst cross %f32vec3_012 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std cross: " + "expected Result Type to have 3 or 4 components")); +} + +TEST_F(ValidateExtInst, OpenCLStdCrossXWrongType) { + const std::string body = R"( +%val1 = OpExtInst %f32vec3 %extinst cross %f64vec3_012 %f32vec3_123 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std cross: " + "expected operand X type to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdCrossYWrongType) { + const std::string body = R"( +%val1 = OpExtInst %f32vec3 %extinst cross %f32vec3_123 %f64vec3_012 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std cross: " + "expected operand Y type to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdLengthLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32vec2_01\n"; + ss << "%val2 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32vec4_0123\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdLengthLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32vec2_01\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected Result Type to be a float scalar type")); +} + +TEST_P(ValidateOpenCLStdLengthLike, IntX) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + " %u32vec2_01\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected operand P to be a float scalar or vector")); +} + +TEST_P(ValidateOpenCLStdLengthLike, VectorTooBig) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %f32vec8_01010101\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected operand P to have no more than 4 components")); +} + +TEST_P(ValidateOpenCLStdLengthLike, DifferentType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f64 %extinst " + ext_inst_name + " %f32vec2_01\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected operand P component type to be equal to " + "Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllLengthLike, ValidateOpenCLStdLengthLike, + ::testing::ValuesIn(std::vector{ + "length", + "fast_length", + })); + +TEST_P(ValidateOpenCLStdDistanceLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32vec2_01 %f32vec2_01\n"; + ss << "%val2 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32vec4_0123 %f32vec4_1234\n"; + ss << "%val3 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdDistanceLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + + " %f32vec2_01 %f32vec2_12\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected Result Type to be a float scalar type")); +} + +TEST_P(ValidateOpenCLStdDistanceLike, IntP0) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %u32vec2_01 %f32vec2_12\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected operand P0 to be of float scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdDistanceLike, VectorTooBig) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %f32vec8_01010101 %f32vec8_01010101\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected operand P0 to have no more than 4 components")); +} + +TEST_P(ValidateOpenCLStdDistanceLike, F64P0) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32 %extinst " + ext_inst_name + + " %f64vec2_01 %f32vec2_12\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std " + ext_inst_name + + ": " + "expected operand P0 component type to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdDistanceLike, DifferentOperands) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f64 %extinst " + ext_inst_name + + " %f64vec2_01 %f32vec2_12\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected operands P0 and P1 to be of the same type")); +} + +INSTANTIATE_TEST_SUITE_P(AllDistanceLike, ValidateOpenCLStdDistanceLike, + ::testing::ValuesIn(std::vector{ + "distance", + "fast_distance", + })); + +TEST_P(ValidateOpenCLStdNormalizeLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01\n"; + ss << "%val2 = OpExtInst %f32vec4 %extinst " << ext_inst_name + << " %f32vec4_0123\n"; + ss << "%val3 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdNormalizeLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %u32 %extinst " + ext_inst_name + " %f32_2\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected Result Type to be a float scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdNormalizeLike, VectorTooBig) { + const std::string ext_inst_name = GetParam(); + const std::string body = "%val1 = OpExtInst %f32vec8 %extinst " + + ext_inst_name + " %f32vec8_01010101\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected Result Type to have no more than 4 components")); +} + +TEST_P(ValidateOpenCLStdNormalizeLike, DifferentType) { + const std::string ext_inst_name = GetParam(); + const std::string body = + "%val1 = OpExtInst %f64vec2 %extinst " + ext_inst_name + " %f32vec2_01\n"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected operand P type to be equal to Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllNormalizeLike, ValidateOpenCLStdNormalizeLike, + ::testing::ValuesIn(std::vector{ + "normalize", + "fast_normalize", + })); + +TEST_F(ValidateExtInst, OpenCLStdBitselectSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst bitselect %f32_2 %f32_1 %f32_1 +%val2 = OpExtInst %f32vec4 %extinst bitselect %f32vec4_0123 %f32vec4_1234 %f32vec4_0123 +%val3 = OpExtInst %u32 %extinst bitselect %u32_2 %u32_1 %u32_1 +%val4 = OpExtInst %u32vec4 %extinst bitselect %u32vec4_0123 %u32vec4_0123 %u32vec4_0123 +%val5 = OpExtInst %u64 %extinst bitselect %u64_2 %u64_1 %u64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdBitselectWrongResultType) { + const std::string body = R"( +%val3 = OpExtInst %struct_f32_f32 %extinst bitselect %u32_2 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std bitselect: " + "expected Result Type to be an int or float scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdBitselectAWrongType) { + const std::string body = R"( +%val3 = OpExtInst %u32 %extinst bitselect %f32_2 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std bitselect: " + "expected types of all operands to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdBitselectBWrongType) { + const std::string body = R"( +%val3 = OpExtInst %u32 %extinst bitselect %u32_2 %f32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std bitselect: " + "expected types of all operands to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdBitselectCWrongType) { + const std::string body = R"( +%val3 = OpExtInst %u32 %extinst bitselect %u32_2 %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std bitselect: " + "expected types of all operands to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdSelectSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst select %f32_2 %f32_1 %u32_1 +%val2 = OpExtInst %f32vec4 %extinst select %f32vec4_0123 %f32vec4_1234 %u32vec4_0123 +%val3 = OpExtInst %u32 %extinst select %u32_2 %u32_1 %u32_1 +%val4 = OpExtInst %u32vec4 %extinst select %u32vec4_0123 %u32vec4_0123 %u32vec4_0123 +%val5 = OpExtInst %u64 %extinst select %u64_2 %u64_1 %u64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdSelectWrongResultType) { + const std::string body = R"( +%val3 = OpExtInst %struct_f32_f32 %extinst select %u32_2 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std select: " + "expected Result Type to be an int or float scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdSelectAWrongType) { + const std::string body = R"( +%val3 = OpExtInst %u32 %extinst select %f32_2 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std select: " + "expected operand A type to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdSelectBWrongType) { + const std::string body = R"( +%val3 = OpExtInst %u32 %extinst select %u32_2 %f32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std select: " + "expected operand B type to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdSelectCWrongType) { + const std::string body = R"( +%val3 = OpExtInst %f32 %extinst select %f32_2 %f32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std select: " + "expected operand C to be an int scalar or vector")); +} + +TEST_F(ValidateExtInst, OpenCLStdSelectCWrongComponentNumber) { + const std::string body = R"( +%val3 = OpExtInst %f32vec2 %extinst select %f32vec2_12 %f32vec2_01 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std select: " + "expected operand C to have the same number of " + "components as Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdSelectCWrongBitWidth) { + const std::string body = R"( +%val3 = OpExtInst %f32vec2 %extinst select %f32vec2_12 %f32vec2_01 %u64vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std select: " + "expected operand C to have the same bit width as Result Type")); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, SuccessPhysical32) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32_1 %u32_1 %ptr" << rounding_mode << "\n"; + ss << "%val2 = OpExtInst %void %extinst " << ext_inst_name + << " %f64_0 %u32_2 %ptr" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n"; + ss << "%val2 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec4_0123 %u32_0 %ptr" << rounding_mode << "\n"; + ss << "%val3 = OpExtInst %void %extinst " << ext_inst_name + << " %f64vec2_01 %u32_2 %ptr" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, SuccessPhysical64) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32_1 %u64_1 %ptr" << rounding_mode << "\n"; + ss << "%val2 = OpExtInst %void %extinst " << ext_inst_name + << " %f64_0 %u64_2 %ptr" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec2_01 %u64_1 %ptr" << rounding_mode << "\n"; + ss << "%val2 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec4_0123 %u64_0 %ptr" << rounding_mode << "\n"; + ss << "%val3 = OpExtInst %void %extinst " << ext_inst_name + << " %f64vec2_01 %u64_2 %ptr" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, NonVoidResultType) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_1 %u32_1 %ptr" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be void")); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, WrongDataType) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f64vec2_01 %u32_1 %ptr" << rounding_mode << "\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Data to be a 32 or 64-bit float scalar")); + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f64_0 %u32_1 %ptr" << rounding_mode << "\n"; + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Data to be a 32 or 64-bit float vector")); + } +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, AddressingModelLogical) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + " can only be used with physical addressing models")); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, OffsetNotSizeT) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": " + "expected operand Offset to be of type size_t (64-bit integer " + "for the addressing model used in the module)")); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, PNotPointer) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32_0 %u32_1 %f16_ptr_workgroup" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec2_01 %u32_1 %f16_ptr_workgroup" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 89[%_ptr_Workgroup_half] cannot be a type")); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, ConstPointer) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant " + "%f16vec8_uniform_constant %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected operand P storage class to be Generic, " + "CrossWorkgroup, Workgroup or Function")); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, PDataTypeInt) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected operand P data type to be 16-bit float scalar")); +} + +TEST_P(ValidateOpenCLStdVStoreHalfLike, PDataTypeFloat32) { + const std::string ext_inst_name = GetParam(); + const std::string rounding_mode = + ext_inst_name.substr(ext_inst_name.length() - 2) == "_r" ? " RTE" : ""; + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + if (std::string::npos == ext_inst_name.find("halfn")) { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32_0 %u32_1 %ptr" << rounding_mode << "\n"; + } else { + ss << "%val1 = OpExtInst %void %extinst " << ext_inst_name + << " %f32vec2_01 %u32_1 %ptr" << rounding_mode << "\n"; + } + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected operand P data type to be 16-bit float scalar")); +} + +INSTANTIATE_TEST_SUITE_P(AllVStoreHalfLike, ValidateOpenCLStdVStoreHalfLike, + ::testing::ValuesIn(std::vector{ + "vstore_half", + "vstore_half_r", + "vstore_halfn", + "vstore_halfn_r", + "vstorea_halfn", + "vstorea_halfn_r", + })); + +TEST_P(ValidateOpenCLStdVLoadHalfLike, SuccessPhysical32) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u32_1 %ptr 2\n"; + ss << "%val2 = OpExtInst %f32vec3 %extinst " << ext_inst_name + << " %u32_1 %ptr 3\n"; + ss << "%val3 = OpExtInst %f32vec4 %extinst " << ext_inst_name + << " %u32_1 %ptr 4\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, SuccessPhysical64) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u64_1 %ptr 2\n"; + ss << "%val2 = OpExtInst %f32vec3 %extinst " << ext_inst_name + << " %u64_1 %ptr 3\n"; + ss << "%val3 = OpExtInst %f32vec4 %extinst " << ext_inst_name + << " %u64_1 %ptr 4\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, ResultTypeNotFloatVector) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %u32_1 %ptr 1\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be a float vector type")); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, AddressingModelLogical) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u32_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + " can only be used with physical addressing models")); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, OffsetNotSizeT) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u64_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected operand Offset to be of type size_t (32-bit " + "integer for the addressing model used in the module)")); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, PNotPointer) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u32_1 %f16_ptr_workgroup 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 89[%_ptr_Workgroup_half] cannot be a type")); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, OffsetWrongStorageType) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_input %f16vec8_input %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u32_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function")); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, PDataTypeInt) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u32_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected operand P data type to be 16-bit float scalar")); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, PDataTypeFloat32) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u32_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected operand P data type to be 16-bit float scalar")); +} + +TEST_P(ValidateOpenCLStdVLoadHalfLike, WrongN) { + const std::string ext_inst_name = GetParam(); + + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_workgroup %f16vec8_workgroup %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %u32_1 %ptr 3\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected literal N to be equal to the number of " + "components of Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllVLoadHalfLike, ValidateOpenCLStdVLoadHalfLike, + ::testing::ValuesIn(std::vector{ + "vload_halfn", + "vloada_halfn", + })); + +TEST_F(ValidateExtInst, VLoadNSuccessFloatPhysical32) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u32_1 %ptr 2\n"; + ss << "%val2 = OpExtInst %f32vec3 %extinst vloadn %u32_1 %ptr 3\n"; + ss << "%val3 = OpExtInst %f32vec4 %extinst vloadn %u32_1 %ptr 4\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VLoadNSuccessIntPhysical32) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %u32_ptr_uniform_constant " + "%u32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %u32vec2 %extinst vloadn %u32_1 %ptr 2\n"; + ss << "%val2 = OpExtInst %u32vec3 %extinst vloadn %u32_1 %ptr 3\n"; + ss << "%val3 = OpExtInst %u32vec4 %extinst vloadn %u32_1 %ptr 4\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VLoadNSuccessFloatPhysical64) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u64_1 %ptr 2\n"; + ss << "%val2 = OpExtInst %f32vec3 %extinst vloadn %u64_1 %ptr 3\n"; + ss << "%val3 = OpExtInst %f32vec4 %extinst vloadn %u64_1 %ptr 4\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VLoadNSuccessIntPhysical64) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %u32_ptr_uniform_constant " + "%u32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %u32vec2 %extinst vloadn %u64_1 %ptr 2\n"; + ss << "%val2 = OpExtInst %u32vec3 %extinst vloadn %u64_1 %ptr 3\n"; + ss << "%val3 = OpExtInst %u32vec4 %extinst vloadn %u64_1 %ptr 4\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VLoadNWrongResultType) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst vloadn %u32_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std vloadn: " + "expected Result Type to be an int or float vector type")); +} + +TEST_F(ValidateExtInst, VLoadNAddressingModelLogical) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u32_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vloadn can only be used with physical " + "addressing models")); +} + +TEST_F(ValidateExtInst, VLoadNOffsetNotSizeT) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u64_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std vloadn: expected operand Offset to be of type size_t " + "(32-bit integer for the addressing model used in the module)")); +} + +TEST_F(ValidateExtInst, VLoadNPNotPointer) { + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u32_1 " + "%f32_ptr_uniform_constant 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 120[%_ptr_UniformConstant_float] cannot be a " + "type")); +} + +TEST_F(ValidateExtInst, VLoadNWrongStorageClass) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %u32_ptr_input %u32vec8_input %u32_1\n"; + ss << "%val1 = OpExtInst %u32vec2 %extinst vloadn %u32_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vloadn: expected operand P storage class " + "to be UniformConstant, Generic, CrossWorkgroup, " + "Workgroup or Function")); +} + +TEST_F(ValidateExtInst, VLoadNWrongComponentType) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %u32vec2 %extinst vloadn %u32_1 %ptr 2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vloadn: expected operand P data type to be " + "equal to component type of Result Type")); +} + +TEST_F(ValidateExtInst, VLoadNWrongN) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst vloadn %u32_1 %ptr 3\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vloadn: expected literal N to be equal to " + "the number of components of Result Type")); +} + +TEST_F(ValidateExtInst, VLoadHalfSuccessPhysical32) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant " + "%f16vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n"; + ss << "%val2 = OpExtInst %f64 %extinst vload_half %u32_1 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VLoadHalfSuccessPhysical64) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant " + "%f16vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst vload_half %u64_1 %ptr\n"; + ss << "%val2 = OpExtInst %f64 %extinst vload_half %u64_1 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VLoadHalfWrongResultType) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant " + "%f16vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %u32 %extinst vload_half %u32_1 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vload_half: " + "expected Result Type to be a float scalar type")); +} + +TEST_F(ValidateExtInst, VLoadHalfAddressingModelLogical) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant " + "%f16vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vload_half can only be used with physical " + "addressing models")); +} + +TEST_F(ValidateExtInst, VLoadHalfOffsetNotSizeT) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_uniform_constant " + "%f16vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst vload_half %u64_1 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std vload_half: expected operand Offset to be of type size_t " + "(32-bit integer for the addressing model used in the module)")); +} + +TEST_F(ValidateExtInst, VLoadHalfPNotPointer) { + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 " + "%f16_ptr_uniform_constant\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 114[%_ptr_UniformConstant_half] cannot be a " + "type")); +} + +TEST_F(ValidateExtInst, VLoadHalfWrongStorageClass) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f16_ptr_input %f16vec8_input %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std vload_half: expected operand P storage class to be " + "UniformConstant, Generic, CrossWorkgroup, Workgroup or Function")); +} + +TEST_F(ValidateExtInst, VLoadHalfPDataTypeInt) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %u32_ptr_uniform_constant " + "%u32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vload_half: expected operand P data type " + "to be 16-bit float scalar")); +} + +TEST_F(ValidateExtInst, VLoadHalfPDataTypeFloat32) { + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst vload_half %u32_1 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vload_half: expected operand P data type " + "to be 16-bit float scalar")); +} + +TEST_F(ValidateExtInst, VStoreNSuccessFloatPhysical32) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 %ptr_g\n"; + ss << "%val2 = OpExtInst %void %extinst vstoren %f32vec4_0123 %u32_1 " + "%ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VStoreNSuccessFloatPhysical64) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u64_1 %ptr_g\n"; + ss << "%val2 = OpExtInst %void %extinst vstoren %f32vec4_0123 %u64_1 " + "%ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VStoreNSuccessIntPhysical32) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %u32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %u32vec2_01 %u32_1 %ptr_g\n"; + ss << "%val2 = OpExtInst %void %extinst vstoren %u32vec4_0123 %u32_1 " + "%ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VStoreNSuccessIntPhysical64) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %u32_ptr_workgroup %u32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %u32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %u32vec2_01 %u64_1 %ptr_g\n"; + ss << "%val2 = OpExtInst %void %extinst vstoren %u32vec4_0123 %u64_1 " + "%ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, VStoreNResultTypeNotVoid) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %f32 %extinst vstoren %f32vec2_01 %u32_1 %ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vstoren: expected Result Type to be void")); +} + +TEST_F(ValidateExtInst, VStoreNDataWrongType) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %f32_1 %u32_1 %ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std vstoren: expected Data to be an int or float vector")); +} + +TEST_F(ValidateExtInst, VStoreNAddressingModelLogical) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 %ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Logical")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vstoren can only be used with physical " + "addressing models")); +} + +TEST_F(ValidateExtInst, VStoreNOffsetNotSizeT) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 %ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str(), "", "Physical64")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std vstoren: expected operand Offset to be of type size_t " + "(64-bit integer for the addressing model used in the module)")); +} + +TEST_F(ValidateExtInst, VStoreNPNotPointer) { + std::ostringstream ss; + ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 " + "%f32_ptr_generic\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 127[%_ptr_Generic_float] cannot be a type")); +} + +TEST_F(ValidateExtInst, VStoreNWrongStorageClass) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %f32vec2_01 %u32_1 %ptr_w\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std vstoren: expected operand P storage class " + "to be Generic, CrossWorkgroup, Workgroup or Function")); +} + +TEST_F(ValidateExtInst, VStorePWrongDataType) { + std::ostringstream ss; + ss << "%ptr_w = OpAccessChain %f32_ptr_workgroup %f32vec8_workgroup %u32_1\n"; + ss << "%ptr_g = OpPtrCastToGeneric %f32_ptr_generic %ptr_w\n"; + ss << "%val1 = OpExtInst %void %extinst vstoren %u32vec2_01 %u32_1 %ptr_g\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std vstoren: expected operand P data type to " + "be equal to the type of operand Data components")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle %f32vec4_0123 %u32vec2_01 +%val2 = OpExtInst %f32vec4 %extinst shuffle %f32vec4_0123 %u32vec4_0123 +%val3 = OpExtInst %u32vec2 %extinst shuffle %u32vec4_0123 %u32vec2_01 +%val4 = OpExtInst %u32vec4 %extinst shuffle %u32vec4_0123 %u32vec4_0123 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleWrongResultType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst shuffle %f32vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std shuffle: " + "expected Result Type to be an int or float vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleResultTypeInvalidNumComponents) { + const std::string body = R"( +%val1 = OpExtInst %f32vec3 %extinst shuffle %f32vec4_0123 %u32vec3_012 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std shuffle: " + "expected Result Type to have 2, 4, 8 or 16 components")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleXWrongType) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle %f32_0 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle: " + "expected operand X to be an int or float vector")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleXInvalidNumComponents) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle %f32vec3_012 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle: " + "expected operand X to have 2, 4, 8 or 16 components")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleXInvalidComponentType) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle %f64vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std shuffle: " + "expected operand X and Result Type to have equal component types")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleShuffleMaskNotIntVector) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle %f32vec4_0123 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle: " + "expected operand Shuffle Mask to be an int vector")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleShuffleMaskInvalidNumComponents) { + const std::string body = R"( +%val1 = OpExtInst %f32vec4 %extinst shuffle %f32vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle: " + "expected operand Shuffle Mask to have the same number " + "of components as Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffleShuffleMaskInvalidBitWidth) { + const std::string body = R"( +%val1 = OpExtInst %f64vec2 %extinst shuffle %f64vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle: " + "expected operand Shuffle Mask components to have the " + "same bit width as Result Type components")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2Success) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec2_01 +%val2 = OpExtInst %f32vec4 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec4_0123 +%val3 = OpExtInst %u32vec2 %extinst shuffle2 %u32vec4_0123 %u32vec4_0123 %u32vec2_01 +%val4 = OpExtInst %u32vec4 %extinst shuffle2 %u32vec4_0123 %u32vec4_0123 %u32vec4_0123 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2WrongResultType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std shuffle2: " + "expected Result Type to be an int or float vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2ResultTypeInvalidNumComponents) { + const std::string body = R"( +%val1 = OpExtInst %f32vec3 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec3_012 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std shuffle2: " + "expected Result Type to have 2, 4, 8 or 16 components")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2XWrongType) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32_0 %f32_0 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle2: " + "expected operand X to be an int or float vector")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2YTypeDifferentFromX) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32vec2_01 %f32vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle2: " + "expected operands X and Y to be of the same type")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2XInvalidNumComponents) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32vec3_012 %f32vec3_012 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle2: " + "expected operand X to have 2, 4, 8 or 16 components")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2XInvalidComponentType) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f64vec4_0123 %f64vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std shuffle2: " + "expected operand X and Result Type to have equal component types")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2ShuffleMaskNotIntVector) { + const std::string body = R"( +%val1 = OpExtInst %f32vec2 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %f32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle2: " + "expected operand Shuffle Mask to be an int vector")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2ShuffleMaskInvalidNumComponents) { + const std::string body = R"( +%val1 = OpExtInst %f32vec4 %extinst shuffle2 %f32vec4_0123 %f32vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle2: " + "expected operand Shuffle Mask to have the same number " + "of components as Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdShuffle2ShuffleMaskInvalidBitWidth) { + const std::string body = R"( +%val1 = OpExtInst %f64vec2 %extinst shuffle2 %f64vec4_0123 %f64vec4_0123 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std shuffle2: " + "expected operand Shuffle Mask components to have the " + "same bit width as Result Type components")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrintfSuccess) { + const std::string body = R"( +%format = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0 +%val1 = OpExtInst %u32 %extinst printf %format %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdPrintfBoolResultType) { + const std::string body = R"( +%format = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0 +%val1 = OpExtInst %bool %extinst printf %format %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std printf: expected Result Type to be a 32-bit int type")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrintfU64ResultType) { + const std::string body = R"( +%format = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0 +%val1 = OpExtInst %u64 %extinst printf %format %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std printf: expected Result Type to be a 32-bit int type")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrintfFormatNotPointer) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst printf %u8_ptr_uniform_constant %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 137[%_ptr_UniformConstant_uchar] cannot be a " + "type")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrintfFormatNotUniformConstStorageClass) { + const std::string body = R"( +%format_const = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0 +%format = OpBitcast %u8_ptr_generic %format_const +%val1 = OpExtInst %u32 %extinst printf %format %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std printf: expected Format storage class to " + "be UniformConstant")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrintfFormatNotU8Pointer) { + const std::string body = R"( +%format = OpAccessChain %u32_ptr_uniform_constant %u32vec8_uniform_constant %u32_0 +%val1 = OpExtInst %u32 %extinst printf %format %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std printf: expected Format data type to be 8-bit int")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchU32Success) { + const std::string body = R"( +%ptr = OpAccessChain %u32_ptr_cross_workgroup %u32arr_cross_workgroup %u32_0 +%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchU32Physical64Success) { + const std::string body = R"( +%ptr = OpAccessChain %u32_ptr_cross_workgroup %u32arr_cross_workgroup %u32_0 +%val1 = OpExtInst %void %extinst prefetch %ptr %u64_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body, "", "Physical64")); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchF32Success) { + const std::string body = R"( +%ptr = OpAccessChain %f32_ptr_cross_workgroup %f32arr_cross_workgroup %u32_0 +%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchF32Vec2Success) { + const std::string body = R"( +%ptr = OpAccessChain %f32vec2_ptr_cross_workgroup %f32vec2arr_cross_workgroup %u32_0 +%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchResultTypeNotVoid) { + const std::string body = R"( +%ptr = OpAccessChain %u32_ptr_cross_workgroup %u32arr_cross_workgroup %u32_0 +%val1 = OpExtInst %u32 %extinst prefetch %ptr %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std prefetch: expected Result Type to be void")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchPtrNotPointer) { + const std::string body = R"( +%val1 = OpExtInst %void %extinst prefetch %u32_ptr_cross_workgroup %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 99[%_ptr_CrossWorkgroup_uint] cannot be a " + "type")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchPtrNotCrossWorkgroup) { + const std::string body = R"( +%ptr = OpAccessChain %u8_ptr_uniform_constant %u8arr_uniform_constant %u32_0 +%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std prefetch: expected operand Ptr storage " + "class to be CrossWorkgroup")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchInvalidDataType) { + const std::string body = R"( +%ptr = OpAccessChain %struct_ptr_cross_workgroup %struct_arr_cross_workgroup %u32_0 +%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std prefetch: expected Ptr data type to be int " + "or float scalar or vector")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchAddressingModelLogical) { + const std::string body = R"( +%ptr = OpAccessChain %u32_ptr_cross_workgroup %u32arr_cross_workgroup %u32_0 +%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body, "", "Logical")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std prefetch can only be used with physical " + "addressing models")); +} + +TEST_F(ValidateExtInst, OpenCLStdPrefetchNumElementsNotSizeT) { + const std::string body = R"( +%ptr = OpAccessChain %f32_ptr_cross_workgroup %f32arr_cross_workgroup %u32_0 +%val1 = OpExtInst %void %extinst prefetch %ptr %u32_256 +)"; + + CompileSuccessfully(GenerateKernelCode(body, "", "Physical64")); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std prefetch: expected operand Num Elements to " + "be of type size_t (64-bit integer for the addressing " + "model used in the module)")); +} + +TEST_P(ValidateOpenCLStdFractLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_f32 = OpVariable %f32_ptr_function Function\n"; + ss << "%var_f32vec2 = OpVariable %f32vec2_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %var_f32\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01 %var_f32vec2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdFractLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_f32 = OpVariable %f32_ptr_function Function\n"; + ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name + << " %f32_0 %var_f32\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be a float scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdFractLike, XWrongType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_f32 = OpVariable %f32_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f64_0 %var_f32\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected type of operand X to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdFractLike, NotPointer) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_f32 = OpVariable %f32_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected the last operand to be a pointer")); +} + +TEST_P(ValidateOpenCLStdFractLike, PointerInvalidStorageClass) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected storage class of the pointer to be " + "Generic, CrossWorkgroup, Workgroup or Function")); +} + +TEST_P(ValidateOpenCLStdFractLike, PointerWrongDataType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_u32 = OpVariable %u32_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %var_u32\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std " + ext_inst_name + + ": expected data type of the pointer to be equal to Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllFractLike, ValidateOpenCLStdFractLike, + ::testing::ValuesIn(std::vector{ + "fract", + "modf", + "sincos", + })); + +TEST_F(ValidateExtInst, OpenCLStdRemquoSuccess) { + const std::string body = R"( +%var_u32 = OpVariable %u32_ptr_function Function +%var_u32vec2 = OpVariable %u32vec2_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32 +%val2 = OpExtInst %f32vec2 %extinst remquo %f32vec2_01 %f32vec2_12 %var_u32vec2 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoIntResultType) { + const std::string body = R"( +%var_u32 = OpVariable %u32_ptr_function Function +%val1 = OpExtInst %u32 %extinst remquo %f32_3 %f32_2 %var_u32 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected Result Type to be a float scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoXWrongType) { + const std::string body = R"( +%var_u32 = OpVariable %f32_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %u32_3 %f32_2 %var_u32 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected type of operand X to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoYWrongType) { + const std::string body = R"( +%var_u32 = OpVariable %f32_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %u32_2 %var_u32 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected type of operand Y to be equal to Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoNotPointer) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected the last operand to be a pointer")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongStorageClass) { + const std::string body = R"( +%ptr = OpAccessChain %f32_ptr_uniform_constant %f32vec8_uniform_constant %u32_1 +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %ptr +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected storage class of the pointer to be Generic, " + "CrossWorkgroup, Workgroup or Function")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataType) { + const std::string body = R"( +%var_f32 = OpVariable %f32_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_f32 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected data type of the pointer to be a 32-bit int " + "scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongDataTypeWidth) { + const std::string body = R"( +%var_u64 = OpVariable %u64_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u64 +)"; + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected data type of the pointer to be a 32-bit int " + "scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdRemquoPointerWrongNumberOfComponents) { + const std::string body = R"( +%var_u32vec2 = OpVariable %u32vec2_ptr_function Function +%val1 = OpExtInst %f32 %extinst remquo %f32_3 %f32_2 %var_u32vec2 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std remquo: " + "expected data type of the pointer to have the same number " + "of components as Result Type")); +} + +TEST_P(ValidateOpenCLStdFrexpLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_u32 = OpVariable %u32_ptr_function Function\n"; + ss << "%var_u32vec2 = OpVariable %u32vec2_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %var_u32\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01 %var_u32vec2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdFrexpLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_u32 = OpVariable %u32_ptr_function Function\n"; + ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name + << " %f32_0 %var_u32\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be a float scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdFrexpLike, XWrongType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_u32 = OpVariable %u32_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f64_0 %var_u32\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected type of operand X to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdFrexpLike, NotPointer) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected the last operand to be a pointer")); +} + +TEST_P(ValidateOpenCLStdFrexpLike, PointerInvalidStorageClass) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%ptr = OpAccessChain %f32_ptr_uniform_constant " + "%f32vec8_uniform_constant %u32_1\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name << " %f32_0 %ptr\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected storage class of the pointer to be " + "Generic, CrossWorkgroup, Workgroup or Function")); +} + +TEST_P(ValidateOpenCLStdFrexpLike, PointerDataTypeFloat) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_f32 = OpVariable %f32_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %var_f32\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected data type of the pointer to be a 32-bit " + "int scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdFrexpLike, PointerDataTypeU64) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_u64 = OpVariable %u64_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %var_u64\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected data type of the pointer to be a 32-bit " + "int scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdFrexpLike, PointerDataTypeDiffSize) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%var_u32 = OpVariable %u32_ptr_function Function\n"; + ss << "%val1 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_01 %var_u32\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected data type of the pointer to have the same " + "number of components as Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllFrexpLike, ValidateOpenCLStdFrexpLike, + ::testing::ValuesIn(std::vector{ + "frexp", + "lgamma_r", + })); + +TEST_F(ValidateExtInst, OpenCLStdIlogbSuccess) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst ilogb %f32_3 +%val2 = OpExtInst %u32vec2 %extinst ilogb %f32vec2_12 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdIlogbFloatResultType) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst ilogb %f32_3 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std ilogb: " + "expected Result Type to be a 32-bit int scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdIlogbIntX) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst ilogb %u32_3 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std ilogb: " + "expected operand X to be a float scalar or vector")); +} + +TEST_F(ValidateExtInst, OpenCLStdIlogbDiffSize) { + const std::string body = R"( +%val2 = OpExtInst %u32vec2 %extinst ilogb %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std ilogb: " + "expected operand X to have the same number of " + "components as Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdNanSuccess) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst nan %u32_3 +%val2 = OpExtInst %f32vec2 %extinst nan %u32vec2_12 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtInst, OpenCLStdNanIntResultType) { + const std::string body = R"( +%val1 = OpExtInst %u32 %extinst nan %u32_3 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std nan: " + "expected Result Type to be a float scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdNanFloatNancode) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst nan %f32_3 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std nan: " + "expected Nancode to be an int scalar or vector type")); +} + +TEST_F(ValidateExtInst, OpenCLStdNanFloatDiffSize) { + const std::string body = R"( +%val1 = OpExtInst %f32 %extinst nan %u32vec2_12 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std nan: " + "expected Nancode to have the same number of " + "components as Result Type")); +} + +TEST_F(ValidateExtInst, OpenCLStdNanFloatDiffBitWidth) { + const std::string body = R"( +%val1 = OpExtInst %f64 %extinst nan %u32_2 +)"; + + CompileSuccessfully(GenerateKernelCode(body)); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std nan: " + "expected Nancode to have the same bit width as Result Type")); +} + +TEST_P(ValidateOpenCLStdLdexpLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %u32_1\n"; + ss << "%val2 = OpExtInst %f32vec2 %extinst " << ext_inst_name + << " %f32vec2_12 %u32vec2_12\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdLdexpLike, IntResultType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u32 %extinst " << ext_inst_name + << " %f32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be a float scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdLdexpLike, XWrongType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %u32_0 %u32_1\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected type of operand X to be equal to Result Type")); +} + +TEST_P(ValidateOpenCLStdLdexpLike, ExponentNotInt) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %f32_1\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected the exponent to be a 32-bit int scalar or vector")); +} + +TEST_P(ValidateOpenCLStdLdexpLike, ExponentNotInt32) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %u64_1\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected the exponent to be a 32-bit int scalar or vector")); +} + +TEST_P(ValidateOpenCLStdLdexpLike, ExponentWrongSize) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f32 %extinst " << ext_inst_name + << " %f32_0 %u32vec2_01\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected the exponent to have the same number of " + "components as Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllLdexpLike, ValidateOpenCLStdLdexpLike, + ::testing::ValuesIn(std::vector{ + "ldexp", + "pown", + "rootn", + })); + +TEST_P(ValidateOpenCLStdUpsampleLike, Success) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u16 %extinst " << ext_inst_name << " %u8_1 %u8_2\n"; + ss << "%val2 = OpExtInst %u32 %extinst " << ext_inst_name + << " %u16_1 %u16_2\n"; + ss << "%val3 = OpExtInst %u64 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + ss << "%val4 = OpExtInst %u64vec2 %extinst " << ext_inst_name + << " %u32vec2_01 %u32vec2_01\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateOpenCLStdUpsampleLike, FloatResultType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %f64 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Result Type to be an int scalar or vector type")); +} + +TEST_P(ValidateOpenCLStdUpsampleLike, InvalidResultTypeBitWidth) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u8 %extinst " << ext_inst_name << " %u8_1 %u8_2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpenCL.std " + ext_inst_name + + ": expected bit width of Result Type components to be 16, 32 or 64")); +} + +TEST_P(ValidateOpenCLStdUpsampleLike, LoHiDiffType) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u64 %extinst " << ext_inst_name + << " %u32_1 %u16_2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Hi and Lo operands to have the same type")); +} + +TEST_P(ValidateOpenCLStdUpsampleLike, DiffNumberOfComponents) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u64vec2 %extinst " << ext_inst_name + << " %u32_1 %u32_2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected Hi and Lo operands to have the same number " + "of components as Result Type")); +} + +TEST_P(ValidateOpenCLStdUpsampleLike, HiLoWrongBitWidth) { + const std::string ext_inst_name = GetParam(); + std::ostringstream ss; + ss << "%val1 = OpExtInst %u64 %extinst " << ext_inst_name + << " %u16_1 %u16_2\n"; + + CompileSuccessfully(GenerateKernelCode(ss.str())); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpenCL.std " + ext_inst_name + + ": expected bit width of components of Hi and Lo operands to " + "be half of the bit width of components of Result Type")); +} + +INSTANTIATE_TEST_SUITE_P(AllUpsampleLike, ValidateOpenCLStdUpsampleLike, + ::testing::ValuesIn(std::vector{ + "u_upsample", + "s_upsample", + })); + +TEST_F(ValidateClspvReflection, RequiresNonSemanticExtension) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonSemantic extended instruction sets cannot be " + "declared without SPV_KHR_non_semantic_info")); +} + +TEST_F(ValidateClspvReflection, MissingVersion) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.ClspvReflection." +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 1 +%5 = OpExtInst %2 %1 SpecConstantWorkDim %4 +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing NonSemantic.ClspvReflection import version")); +} + +TEST_F(ValidateClspvReflection, BadVersion0) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.ClspvReflection.0" +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 1 +%5 = OpExtInst %2 %1 SpecConstantWorkDim %4 +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Unknown NonSemantic.ClspvReflection import version")); +} + +TEST_F(ValidateClspvReflection, BadVersionNotANumber) { + const std::string text = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.ClspvReflection.1a" +OpMemoryModel Logical GLSL450 +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 1 +%5 = OpExtInst %2 %1 SpecConstantWorkDim %4 +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonSemantic.ClspvReflection import does not encode " + "the version correctly")); +} + +TEST_F(ValidateClspvReflection, Kernel) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo %foo_name +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateClspvReflection, KernelNotAFunction) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo_name %foo_name +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Kernel does not reference a function")); +} + +TEST_F(ValidateClspvReflection, KernelNotAnEntryPoint) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%bar = OpFunction %void None %void_fn +%bar_entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %bar %foo_name +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Kernel does not reference an entry-point")); +} + +TEST_F(ValidateClspvReflection, KernelNotGLCompute) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %foo "foo" +OpExecutionMode %foo OriginUpperLeft +%foo_name = OpString "foo" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo %foo_name +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Kernel must refer only to GLCompute entry-points")); +} + +TEST_F(ValidateClspvReflection, KernelNameMismatch) { + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "bar" +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo %foo_name +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Name must match an entry-point for Kernel")); +} + +using ArgumentBasics = + spvtest::ValidateBase>; + +INSTANTIATE_TEST_SUITE_P( + ValidateClspvReflectionArgumentKernel, ArgumentBasics, + ::testing::ValuesIn(std::vector>{ + std::make_pair("ArgumentStorageBuffer", "%int_0 %int_0"), + std::make_pair("ArgumentUniform", "%int_0 %int_0"), + std::make_pair("ArgumentPodStorageBuffer", + "%int_0 %int_0 %int_0 %int_4"), + std::make_pair("ArgumentPodUniform", "%int_0 %int_0 %int_0 %int_4"), + std::make_pair("ArgumentPodPushConstant", "%int_0 %int_4"), + std::make_pair("ArgumentSampledImage", "%int_0 %int_0"), + std::make_pair("ArgumentStorageImage", "%int_0 %int_0"), + std::make_pair("ArgumentSampler", "%int_0 %int_0"), + std::make_pair("ArgumentWorkgroup", "%int_0 %int_0")})); + +TEST_P(ArgumentBasics, KernelNotAnExtendedInstruction) { + const std::string ext_inst = std::get<0>(GetParam()); + const std::string extra = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%in = OpExtInst %void %ext )" + + ext_inst + " %int_0 %int_0 " + extra; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Kernel must be a Kernel extended instruction")); +} + +TEST_P(ArgumentBasics, KernelFromDifferentImport) { + const std::string ext_inst = std::get<0>(GetParam()); + const std::string extra = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +%ext2 = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext2 Kernel %foo %foo_name +%in = OpExtInst %void %ext )" + + ext_inst + " %decl %int_0 " + extra; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Kernel must be from the same extended instruction import")); +} + +TEST_P(ArgumentBasics, KernelWrongExtendedInstruction) { + const std::string ext_inst = std::get<0>(GetParam()); + const std::string extra = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext ArgumentInfo %foo_name +%in = OpExtInst %void %ext )" + + ext_inst + " %decl %int_0 " + extra; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Kernel must be a Kernel extended instruction")); +} + +TEST_P(ArgumentBasics, ArgumentInfo) { + const std::string ext_inst = std::get<0>(GetParam()); + const std::string operands = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%in_name = OpString "in" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo %foo_name +%info = OpExtInst %void %ext ArgumentInfo %in_name +%in = OpExtInst %void %ext )" + + ext_inst + " %decl %int_0 " + operands + " %info"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ArgumentBasics, ArgumentInfoNotAnExtendedInstruction) { + const std::string ext_inst = std::get<0>(GetParam()); + const std::string operands = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo %foo_name +%in = OpExtInst %void %ext )" + + ext_inst + " %decl %int_0 " + operands + " %int_0"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ArgInfo must be an ArgumentInfo extended instruction")); +} + +TEST_P(ArgumentBasics, ArgumentInfoFromDifferentImport) { + const std::string ext_inst = std::get<0>(GetParam()); + const std::string operands = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +%ext2 = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%in_name = OpString "in" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_4 = OpConstant %int 4 +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo %foo_name +%info = OpExtInst %void %ext2 ArgumentInfo %in_name +%in = OpExtInst %void %ext )" + + ext_inst + " %decl %int_0 " + operands + " %info"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ArgInfo must be from the same extended instruction import")); +} + +using Uint32Constant = + spvtest::ValidateBase>; + +INSTANTIATE_TEST_SUITE_P( + ValidateClspvReflectionUint32Constants, Uint32Constant, + ::testing::ValuesIn(std::vector>{ + std::make_pair("ArgumentStorageBuffer %decl %float_0 %int_0 %int_0", + "Ordinal"), + std::make_pair("ArgumentStorageBuffer %decl %null %int_0 %int_0", + "Ordinal"), + std::make_pair("ArgumentStorageBuffer %decl %int_0 %float_0 %int_0", + "DescriptorSet"), + std::make_pair("ArgumentStorageBuffer %decl %int_0 %null %int_0", + "DescriptorSet"), + std::make_pair("ArgumentStorageBuffer %decl %int_0 %int_0 %float_0", + "Binding"), + std::make_pair("ArgumentStorageBuffer %decl %int_0 %int_0 %null", + "Binding"), + std::make_pair("ArgumentUniform %decl %float_0 %int_0 %int_0", + "Ordinal"), + std::make_pair("ArgumentUniform %decl %null %int_0 %int_0", "Ordinal"), + std::make_pair("ArgumentUniform %decl %int_0 %float_0 %int_0", + "DescriptorSet"), + std::make_pair("ArgumentUniform %decl %int_0 %null %int_0", + "DescriptorSet"), + std::make_pair("ArgumentUniform %decl %int_0 %int_0 %float_0", + "Binding"), + std::make_pair("ArgumentUniform %decl %int_0 %int_0 %null", "Binding"), + std::make_pair("ArgumentSampledImage %decl %float_0 %int_0 %int_0", + "Ordinal"), + std::make_pair("ArgumentSampledImage %decl %null %int_0 %int_0", + "Ordinal"), + std::make_pair("ArgumentSampledImage %decl %int_0 %float_0 %int_0", + "DescriptorSet"), + std::make_pair("ArgumentSampledImage %decl %int_0 %null %int_0", + "DescriptorSet"), + std::make_pair("ArgumentSampledImage %decl %int_0 %int_0 %float_0", + "Binding"), + std::make_pair("ArgumentSampledImage %decl %int_0 %int_0 %null", + "Binding"), + std::make_pair("ArgumentStorageImage %decl %float_0 %int_0 %int_0", + "Ordinal"), + std::make_pair("ArgumentStorageImage %decl %null %int_0 %int_0", + "Ordinal"), + std::make_pair("ArgumentStorageImage %decl %int_0 %float_0 %int_0", + "DescriptorSet"), + std::make_pair("ArgumentStorageImage %decl %int_0 %null %int_0", + "DescriptorSet"), + std::make_pair("ArgumentStorageImage %decl %int_0 %int_0 %float_0", + "Binding"), + std::make_pair("ArgumentStorageImage %decl %int_0 %int_0 %null", + "Binding"), + std::make_pair("ArgumentSampler %decl %float_0 %int_0 %int_0", + "Ordinal"), + std::make_pair("ArgumentSampler %decl %null %int_0 %int_0", "Ordinal"), + std::make_pair("ArgumentSampler %decl %int_0 %float_0 %int_0", + "DescriptorSet"), + std::make_pair("ArgumentSampler %decl %int_0 %null %int_0", + "DescriptorSet"), + std::make_pair("ArgumentSampler %decl %int_0 %int_0 %float_0", + "Binding"), + std::make_pair("ArgumentSampler %decl %int_0 %int_0 %null", "Binding"), + std::make_pair("ArgumentPodStorageBuffer %decl %float_0 %int_0 %int_0 " + "%int_0 %int_4", + "Ordinal"), + std::make_pair( + "ArgumentPodStorageBuffer %decl %null %int_0 %int_0 %int_0 %int_4", + "Ordinal"), + std::make_pair("ArgumentPodStorageBuffer %decl %int_0 %float_0 %int_0 " + "%int_0 %int_4", + "DescriptorSet"), + std::make_pair( + "ArgumentPodStorageBuffer %decl %int_0 %null %int_0 %int_0 %int_4", + "DescriptorSet"), + std::make_pair("ArgumentPodStorageBuffer %decl %int_0 %int_0 %float_0 " + "%int_0 %int_4", + "Binding"), + std::make_pair( + "ArgumentPodStorageBuffer %decl %int_0 %int_0 %null %int_0 %int_4", + "Binding"), + std::make_pair("ArgumentPodStorageBuffer %decl %int_0 %int_0 %int_0 " + "%float_0 %int_4", + "Offset"), + std::make_pair( + "ArgumentPodStorageBuffer %decl %int_0 %int_0 %int_0 %null %int_4", + "Offset"), + std::make_pair("ArgumentPodStorageBuffer %decl %int_0 %int_0 %int_0 " + "%int_0 %float_0", + "Size"), + std::make_pair( + "ArgumentPodStorageBuffer %decl %int_0 %int_0 %int_0 %int_0 %null", + "Size"), + std::make_pair( + "ArgumentPodUniform %decl %float_0 %int_0 %int_0 %int_0 %int_4", + "Ordinal"), + std::make_pair( + "ArgumentPodUniform %decl %null %int_0 %int_0 %int_0 %int_4", + "Ordinal"), + std::make_pair( + "ArgumentPodUniform %decl %int_0 %float_0 %int_0 %int_0 %int_4", + "DescriptorSet"), + std::make_pair( + "ArgumentPodUniform %decl %int_0 %null %int_0 %int_0 %int_4", + "DescriptorSet"), + std::make_pair( + "ArgumentPodUniform %decl %int_0 %int_0 %float_0 %int_0 %int_4", + "Binding"), + std::make_pair( + "ArgumentPodUniform %decl %int_0 %int_0 %null %int_0 %int_4", + "Binding"), + std::make_pair( + "ArgumentPodUniform %decl %int_0 %int_0 %int_0 %float_0 %int_4", + "Offset"), + std::make_pair( + "ArgumentPodUniform %decl %int_0 %int_0 %int_0 %null %int_4", + "Offset"), + std::make_pair( + "ArgumentPodUniform %decl %int_0 %int_0 %int_0 %int_0 %float_0", + "Size"), + std::make_pair( + "ArgumentPodUniform %decl %int_0 %int_0 %int_0 %int_0 %null", + "Size"), + std::make_pair("ArgumentPodPushConstant %decl %float_0 %int_0 %int_4", + "Ordinal"), + std::make_pair("ArgumentPodPushConstant %decl %null %int_0 %int_4", + "Ordinal"), + std::make_pair("ArgumentPodPushConstant %decl %int_0 %float_0 %int_4", + "Offset"), + std::make_pair("ArgumentPodPushConstant %decl %int_0 %null %int_4", + "Offset"), + std::make_pair("ArgumentPodPushConstant %decl %int_0 %int_0 %float_0", + "Size"), + std::make_pair("ArgumentPodPushConstant %decl %int_0 %int_0 %null", + "Size"), + std::make_pair("ArgumentWorkgroup %decl %float_0 %int_0 %int_4", + "Ordinal"), + std::make_pair("ArgumentWorkgroup %decl %null %int_0 %int_4", + "Ordinal"), + std::make_pair("ArgumentWorkgroup %decl %int_0 %float_0 %int_4", + "SpecId"), + std::make_pair("ArgumentWorkgroup %decl %int_0 %null %int_4", "SpecId"), + std::make_pair("ArgumentWorkgroup %decl %int_0 %int_0 %float_0", + "ElemSize"), + std::make_pair("ArgumentWorkgroup %decl %int_0 %int_0 %null", + "ElemSize"), + std::make_pair("SpecConstantWorkgroupSize %float_0 %int_0 %int_4", "X"), + std::make_pair("SpecConstantWorkgroupSize %null %int_0 %int_4", "X"), + std::make_pair("SpecConstantWorkgroupSize %int_0 %float_0 %int_4", "Y"), + std::make_pair("SpecConstantWorkgroupSize %int_0 %null %int_4", "Y"), + std::make_pair("SpecConstantWorkgroupSize %int_0 %int_0 %float_0", "Z"), + std::make_pair("SpecConstantWorkgroupSize %int_0 %int_0 %null", "Z"), + std::make_pair("SpecConstantGlobalOffset %float_0 %int_0 %int_4", "X"), + std::make_pair("SpecConstantGlobalOffset %null %int_0 %int_4", "X"), + std::make_pair("SpecConstantGlobalOffset %int_0 %float_0 %int_4", "Y"), + std::make_pair("SpecConstantGlobalOffset %int_0 %null %int_4", "Y"), + std::make_pair("SpecConstantGlobalOffset %int_0 %int_0 %float_0", "Z"), + std::make_pair("SpecConstantGlobalOffset %int_0 %int_0 %null", "Z"), + std::make_pair("SpecConstantWorkDim %float_0", "Dim"), + std::make_pair("SpecConstantWorkDim %null", "Dim"), + std::make_pair("PushConstantGlobalOffset %float_0 %int_0", "Offset"), + std::make_pair("PushConstantGlobalOffset %null %int_0", "Offset"), + std::make_pair("PushConstantGlobalOffset %int_0 %float_0", "Size"), + std::make_pair("PushConstantGlobalOffset %int_0 %null", "Size"), + std::make_pair("PushConstantEnqueuedLocalSize %float_0 %int_0", + "Offset"), + std::make_pair("PushConstantEnqueuedLocalSize %null %int_0", "Offset"), + std::make_pair("PushConstantEnqueuedLocalSize %int_0 %float_0", "Size"), + std::make_pair("PushConstantEnqueuedLocalSize %int_0 %null", "Size"), + std::make_pair("PushConstantGlobalSize %float_0 %int_0", "Offset"), + std::make_pair("PushConstantGlobalSize %null %int_0", "Offset"), + std::make_pair("PushConstantGlobalSize %int_0 %float_0", "Size"), + std::make_pair("PushConstantGlobalSize %int_0 %null", "Size"), + std::make_pair("PushConstantRegionOffset %float_0 %int_0", "Offset"), + std::make_pair("PushConstantRegionOffset %null %int_0", "Offset"), + std::make_pair("PushConstantRegionOffset %int_0 %float_0", "Size"), + std::make_pair("PushConstantRegionOffset %int_0 %null", "Size"), + std::make_pair("PushConstantNumWorkgroups %float_0 %int_0", "Offset"), + std::make_pair("PushConstantNumWorkgroups %null %int_0", "Offset"), + std::make_pair("PushConstantNumWorkgroups %int_0 %float_0", "Size"), + std::make_pair("PushConstantNumWorkgroups %int_0 %null", "Size"), + std::make_pair("PushConstantRegionGroupOffset %float_0 %int_0", + "Offset"), + std::make_pair("PushConstantRegionGroupOffset %null %int_0", "Offset"), + std::make_pair("PushConstantRegionGroupOffset %int_0 %float_0", "Size"), + std::make_pair("PushConstantRegionGroupOffset %int_0 %null", "Size"), + std::make_pair("ConstantDataStorageBuffer %float_0 %int_0 %data", + "DescriptorSet"), + std::make_pair("ConstantDataStorageBuffer %null %int_0 %data", + "DescriptorSet"), + std::make_pair("ConstantDataStorageBuffer %int_0 %float_0 %data", + "Binding"), + std::make_pair("ConstantDataStorageBuffer %int_0 %null %data", + "Binding"), + std::make_pair("ConstantDataUniform %float_0 %int_0 %data", + "DescriptorSet"), + std::make_pair("ConstantDataUniform %null %int_0 %data", + "DescriptorSet"), + std::make_pair("ConstantDataUniform %int_0 %float_0 %data", "Binding"), + std::make_pair("ConstantDataUniform %int_0 %null %data", "Binding"), + std::make_pair("LiteralSampler %float_0 %int_0 %int_4", + "DescriptorSet"), + std::make_pair("LiteralSampler %null %int_0 %int_4", "DescriptorSet"), + std::make_pair("LiteralSampler %int_0 %float_0 %int_4", "Binding"), + std::make_pair("LiteralSampler %int_0 %null %int_4", "Binding"), + std::make_pair("LiteralSampler %int_0 %int_0 %float_0", "Mask"), + std::make_pair("LiteralSampler %int_0 %int_0 %null", "Mask"), + std::make_pair( + "PropertyRequiredWorkgroupSize %decl %float_0 %int_1 %int_4", "X"), + std::make_pair( + "PropertyRequiredWorkgroupSize %decl %null %int_1 %int_4", "X"), + std::make_pair( + "PropertyRequiredWorkgroupSize %decl %int_1 %float_0 %int_4", "Y"), + std::make_pair( + "PropertyRequiredWorkgroupSize %decl %int_1 %null %int_4", "Y"), + std::make_pair( + "PropertyRequiredWorkgroupSize %decl %int_1 %int_1 %float_0", "Z"), + std::make_pair( + "PropertyRequiredWorkgroupSize %decl %int_1 %int_1 %null", "Z")})); + +TEST_P(Uint32Constant, Invalid) { + const std::string ext_inst = std::get<0>(GetParam()); + const std::string name = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%data = OpString "1234" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_4 = OpConstant %int 4 +%null = OpConstantNull %int +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo %foo_name +%inst = OpExtInst %void %ext )" + + ext_inst; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(name + " must be a 32-bit unsigned integer OpConstant")); +} + +using StringOperand = + spvtest::ValidateBase>; + +INSTANTIATE_TEST_SUITE_P( + ValidateClspvReflectionStringOperands, StringOperand, + ::testing::ValuesIn(std::vector>{ + std::make_pair("ConstantDataStorageBuffer %int_0 %int_0 %int_0", + "Data"), + std::make_pair("ConstantDataUniform %int_0 %int_0 %int_0", "Data")})); + +TEST_P(StringOperand, Invalid) { + const std::string ext_inst = std::get<0>(GetParam()); + const std::string name = std::get<1>(GetParam()); + const std::string text = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%ext = OpExtInstImport "NonSemantic.ClspvReflection.1" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %foo "foo" +OpExecutionMode %foo LocalSize 1 1 1 +%foo_name = OpString "foo" +%data = OpString "1234" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_4 = OpConstant %int 4 +%null = OpConstantNull %int +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +%decl = OpExtInst %void %ext Kernel %foo %foo_name +%inst = OpExtInst %void %ext )" + + ext_inst; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(name + " must be an OpString")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_extension_spv_khr_terminate_invocation.cpp b/third_party/spirv-tools/test/val/val_extension_spv_khr_terminate_invocation.cpp new file mode 100644 index 0000000..4cabf9e --- /dev/null +++ b/third_party/spirv-tools/test/val/val_extension_spv_khr_terminate_invocation.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for OpExtension validator rules. + +#include +#include + +#include "gmock/gmock.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/spirv_target_env.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateSpvKHRTerminateInvocation = spvtest::ValidateBase; + +TEST_F(ValidateSpvKHRTerminateInvocation, Valid) { + const std::string str = R"( + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + OpMemoryModel Logical Simple + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpTerminateInvocation + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSpvKHRTerminateInvocation, RequiresExtension) { + const std::string str = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpTerminateInvocation + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("TerminateInvocation requires one of the following " + "extensions: SPV_KHR_terminate_invocation")); +} + +TEST_F(ValidateSpvKHRTerminateInvocation, RequiresShaderCapability) { + const std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpExtension "SPV_KHR_terminate_invocation" + OpMemoryModel Physical32 OpenCL + OpEntryPoint Kernel %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpTerminateInvocation + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "TerminateInvocation requires one of these capabilities: Shader \n")); +} + +TEST_F(ValidateSpvKHRTerminateInvocation, RequiresFragmentShader) { + const std::string str = R"( + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpTerminateInvocation + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTerminateInvocation requires Fragment execution model")); +} + +TEST_F(ValidateSpvKHRTerminateInvocation, IsTerminatorInstruction) { + const std::string str = R"( + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + + %void = OpTypeVoid + %void_fn = OpTypeFunction %void + + %main = OpFunction %void None %void_fn + %entry = OpLabel + OpTerminateInvocation + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str.c_str()); + EXPECT_NE(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Return must appear in a block")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_extensions_test.cpp b/third_party/spirv-tools/test/val/val_extensions_test.cpp new file mode 100644 index 0000000..491a808 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_extensions_test.cpp @@ -0,0 +1,349 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for OpExtension validator rules. + +#include +#include + +#include "gmock/gmock.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/spirv_target_env.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateKnownExtensions = spvtest::ValidateBase; +using ValidateUnknownExtensions = spvtest::ValidateBase; +using ValidateExtensionCapabilities = spvtest::ValidateBase; + +// Returns expected error string if |extension| is not recognized. +std::string GetErrorString(const std::string& extension) { + return "Found unrecognized extension " + extension; +} + +INSTANTIATE_TEST_SUITE_P( + ExpectSuccess, ValidateKnownExtensions, + Values( + // Match the order as published on the SPIR-V Registry. + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_variable_pointers", + "SPV_AMD_gpu_shader_int16", "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", "SPV_AMD_shader_fragment_mask", + "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1", + "SPV_NV_shader_subgroup_partitioned", "SPV_EXT_descriptor_indexing", + "SPV_KHR_terminate_invocation")); + +INSTANTIATE_TEST_SUITE_P(FailSilently, ValidateUnknownExtensions, + Values("ERROR_unknown_extension", "SPV_KHR_", + "SPV_KHR_shader_ballot_ERROR")); + +TEST_P(ValidateKnownExtensions, ExpectSuccess) { + const std::string extension = GetParam(); + const std::string str = + "OpCapability Shader\nOpCapability Linkage\nOpExtension \"" + extension + + "\"\nOpMemoryModel Logical GLSL450"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Not(HasSubstr(GetErrorString(extension)))); +} + +TEST_P(ValidateUnknownExtensions, FailSilently) { + const std::string extension = GetParam(); + const std::string str = + "OpCapability Shader\nOpCapability Linkage\nOpExtension \"" + extension + + "\"\nOpMemoryModel Logical GLSL450"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(extension))); +} + +TEST_F(ValidateUnknownExtensions, HitMaxNumOfWarnings) { + const std::string str = + std::string("OpCapability Shader\n") + "OpCapability Linkage\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpExtension \"bad_ext\"\n" + "OpExtension \"bad_ext\"\n" + + "OpMemoryModel Logical GLSL450"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Other warnings have been suppressed.")); +} + +TEST_F(ValidateExtensionCapabilities, DeclCapabilitySuccess) { + const std::string str = + "OpCapability Shader\nOpCapability Linkage\nOpCapability DeviceGroup\n" + "OpExtension \"SPV_KHR_device_group\"" + "\nOpMemoryModel Logical GLSL450"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateExtensionCapabilities, DeclCapabilityFailure) { + const std::string str = + "OpCapability Shader\nOpCapability Linkage\nOpCapability DeviceGroup\n" + "\nOpMemoryModel Logical GLSL450"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("1st operand of Capability")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("requires one of these extensions")); + EXPECT_THAT(getDiagnosticString(), HasSubstr("SPV_KHR_device_group")); +} + +using ValidateAMDShaderBallotCapabilities = spvtest::ValidateBase; + +// Returns a vector of strings for the prefix of a SPIR-V assembly shader +// that can use the group instructions introduced by SPV_AMD_shader_ballot. +std::vector ShaderPartsForAMDShaderBallot() { + return std::vector{R"( + OpCapability Shader + OpCapability Linkage + )", + R"( + OpMemoryModel Logical GLSL450 + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %scope = OpConstant %uint 3 + %uint_const = OpConstant %uint 42 + %int_const = OpConstant %uint 45 + %float_const = OpConstant %float 3.5 + + %void = OpTypeVoid + %fn_ty = OpTypeFunction %void + %fn = OpFunction %void None %fn_ty + %entry = OpLabel + )"}; +} + +// Returns a list of SPIR-V assembly strings, where each uses only types +// and IDs that can fit with a shader made from parts from the result +// of ShaderPartsForAMDShaderBallot. +std::vector AMDShaderBallotGroupInstructions() { + return std::vector{ + "%iadd_reduce = OpGroupIAddNonUniformAMD %uint %scope Reduce %uint_const", + "%iadd_iscan = OpGroupIAddNonUniformAMD %uint %scope InclusiveScan " + "%uint_const", + "%iadd_escan = OpGroupIAddNonUniformAMD %uint %scope ExclusiveScan " + "%uint_const", + + "%fadd_reduce = OpGroupFAddNonUniformAMD %float %scope Reduce " + "%float_const", + "%fadd_iscan = OpGroupFAddNonUniformAMD %float %scope InclusiveScan " + "%float_const", + "%fadd_escan = OpGroupFAddNonUniformAMD %float %scope ExclusiveScan " + "%float_const", + + "%fmin_reduce = OpGroupFMinNonUniformAMD %float %scope Reduce " + "%float_const", + "%fmin_iscan = OpGroupFMinNonUniformAMD %float %scope InclusiveScan " + "%float_const", + "%fmin_escan = OpGroupFMinNonUniformAMD %float %scope ExclusiveScan " + "%float_const", + + "%umin_reduce = OpGroupUMinNonUniformAMD %uint %scope Reduce %uint_const", + "%umin_iscan = OpGroupUMinNonUniformAMD %uint %scope InclusiveScan " + "%uint_const", + "%umin_escan = OpGroupUMinNonUniformAMD %uint %scope ExclusiveScan " + "%uint_const", + + "%smin_reduce = OpGroupUMinNonUniformAMD %int %scope Reduce %int_const", + "%smin_iscan = OpGroupUMinNonUniformAMD %int %scope InclusiveScan " + "%int_const", + "%smin_escan = OpGroupUMinNonUniformAMD %int %scope ExclusiveScan " + "%int_const", + + "%fmax_reduce = OpGroupFMaxNonUniformAMD %float %scope Reduce " + "%float_const", + "%fmax_iscan = OpGroupFMaxNonUniformAMD %float %scope InclusiveScan " + "%float_const", + "%fmax_escan = OpGroupFMaxNonUniformAMD %float %scope ExclusiveScan " + "%float_const", + + "%umax_reduce = OpGroupUMaxNonUniformAMD %uint %scope Reduce %uint_const", + "%umax_iscan = OpGroupUMaxNonUniformAMD %uint %scope InclusiveScan " + "%uint_const", + "%umax_escan = OpGroupUMaxNonUniformAMD %uint %scope ExclusiveScan " + "%uint_const", + + "%smax_reduce = OpGroupUMaxNonUniformAMD %int %scope Reduce %int_const", + "%smax_iscan = OpGroupUMaxNonUniformAMD %int %scope InclusiveScan " + "%int_const", + "%smax_escan = OpGroupUMaxNonUniformAMD %int %scope ExclusiveScan " + "%int_const"}; +} + +TEST_P(ValidateAMDShaderBallotCapabilities, ExpectSuccess) { + // Succeed because the module specifies the SPV_AMD_shader_ballot extension. + auto parts = ShaderPartsForAMDShaderBallot(); + + const std::string assembly = + parts[0] + "OpExtension \"SPV_AMD_shader_ballot\"\n" + parts[1] + + GetParam() + "\nOpReturn OpFunctionEnd"; + + CompileSuccessfully(assembly.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +INSTANTIATE_TEST_SUITE_P(ExpectSuccess, ValidateAMDShaderBallotCapabilities, + ValuesIn(AMDShaderBallotGroupInstructions())); + +TEST_P(ValidateAMDShaderBallotCapabilities, ExpectFailure) { + // Fail because the module does not specify the SPV_AMD_shader_ballot + // extension. + auto parts = ShaderPartsForAMDShaderBallot(); + + const std::string assembly = + parts[0] + parts[1] + GetParam() + "\nOpReturn OpFunctionEnd"; + + CompileSuccessfully(assembly.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + + // Make sure we get an appropriate error message. + // Find just the opcode name, skipping over the "Op" part. + auto prefix_with_opcode = GetParam().substr(GetParam().find("Group")); + auto opcode = prefix_with_opcode.substr(0, prefix_with_opcode.find(' ')); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr(std::string("Opcode " + opcode + + " requires one of these capabilities: Groups"))); +} + +INSTANTIATE_TEST_SUITE_P(ExpectFailure, ValidateAMDShaderBallotCapabilities, + ValuesIn(AMDShaderBallotGroupInstructions())); + +struct ExtIntoCoreCase { + const char* ext; + const char* cap; + const char* builtin; + spv_target_env env; + bool success; +}; + +using ValidateExtIntoCore = spvtest::ValidateBase; + +// Make sure that we don't panic about missing extensions for using +// functionalities that introduced in extensions but became core SPIR-V later. + +TEST_P(ValidateExtIntoCore, DoNotAskForExtensionInLaterVersion) { + const std::string code = std::string(R"( + OpCapability Shader + OpCapability )") + + GetParam().cap + R"( + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %builtin + OpDecorate %builtin BuiltIn )" + GetParam().builtin + R"( + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %builtin = OpVariable %_ptr_Input_int Input + %main = OpFunction %void None %3 + %5 = OpLabel + %18 = OpLoad %int %builtin + OpReturn + OpFunctionEnd)"; + + CompileSuccessfully(code.c_str(), GetParam().env); + if (GetParam().success) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(GetParam().env)) + << getDiagnosticString(); + } else { + ASSERT_NE(SPV_SUCCESS, ValidateInstructions(GetParam().env)) + << " in " << spvTargetEnvDescription(GetParam().env) << ":\n" + << code; + const std::string message = getDiagnosticString(); + if (spvIsVulkanEnv(GetParam().env)) { + EXPECT_THAT(message, HasSubstr(std::string(GetParam().cap) + + " is not allowed by Vulkan")); + EXPECT_THAT(message, HasSubstr(std::string("or requires extension"))); + } else { + EXPECT_THAT(message, + HasSubstr(std::string("requires one of these extensions: ") + + GetParam().ext)); + } + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + KHR_extensions, ValidateExtIntoCore, + ValuesIn(std::vector{ + // SPV_KHR_shader_draw_parameters became core SPIR-V 1.3 + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_UNIVERSAL_1_3, true}, + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_UNIVERSAL_1_2, false}, + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_UNIVERSAL_1_1, false}, + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_UNIVERSAL_1_0, false}, + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_VULKAN_1_1, true}, + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseVertex", SPV_ENV_VULKAN_1_0, false}, + + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseInstance", SPV_ENV_UNIVERSAL_1_3, true}, + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "BaseInstance", SPV_ENV_VULKAN_1_0, false}, + + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "DrawIndex", SPV_ENV_UNIVERSAL_1_3, true}, + {"SPV_KHR_shader_draw_parameters", "DrawParameters", "DrawIndex", SPV_ENV_UNIVERSAL_1_1, false}, + + // SPV_KHR_multiview became core SPIR-V 1.3 + {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_UNIVERSAL_1_3, true}, + {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_UNIVERSAL_1_2, false}, + {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_UNIVERSAL_1_1, false}, + {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_UNIVERSAL_1_0, false}, + {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_VULKAN_1_1, true}, + {"SPV_KHR_multiview", "MultiView", "ViewIndex", SPV_ENV_VULKAN_1_0, false}, + + // SPV_KHR_device_group became core SPIR-V 1.3 + {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_UNIVERSAL_1_3, true}, + {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_UNIVERSAL_1_2, false}, + {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_UNIVERSAL_1_1, false}, + {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_UNIVERSAL_1_0, false}, + {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_VULKAN_1_1, true}, + {"SPV_KHR_device_group", "DeviceGroup", "DeviceIndex", SPV_ENV_VULKAN_1_0, false}, + })); +// clang-format on + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_fixtures.h b/third_party/spirv-tools/test/val/val_fixtures.h new file mode 100644 index 0000000..5635c78 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_fixtures.h @@ -0,0 +1,186 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Common validation fixtures for unit tests + +#ifndef TEST_VAL_VAL_FIXTURES_H_ +#define TEST_VAL_VAL_FIXTURES_H_ + +#include +#include + +#include "source/val/validation_state.h" +#include "spirv-tools/libspirv.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtest { + +template +class ValidateBase : public ::testing::Test, + public ::testing::WithParamInterface { + public: + ValidateBase(); + + virtual void TearDown(); + + // Returns the a spv_const_binary struct + spv_const_binary get_const_binary(); + + // Assembles the given SPIR-V text, checks that it fails to assemble, + // and returns resulting diagnostic. No internal state is updated. + std::string CompileFailure(std::string code, + spv_target_env env = SPV_ENV_UNIVERSAL_1_0); + + // Checks that 'code' is valid SPIR-V text representation and stores the + // binary version for further method calls. + void CompileSuccessfully(std::string code, + spv_target_env env = SPV_ENV_UNIVERSAL_1_0); + + // Overwrites the word at index 'index' with the given word. + // For testing purposes, it is often useful to be able to manipulate the + // assembled binary before running the validator on it. + // This function overwrites the word at the given index with a new word. + void OverwriteAssembledBinary(uint32_t index, uint32_t word); + + // Performs validation on the SPIR-V code. + spv_result_t ValidateInstructions(spv_target_env env = SPV_ENV_UNIVERSAL_1_0); + + // Performs validation. Returns the status and stores validation state into + // the vstate_ member. + spv_result_t ValidateAndRetrieveValidationState( + spv_target_env env = SPV_ENV_UNIVERSAL_1_0); + + // Destroys the stored binary. + void DestroyBinary() { + spvBinaryDestroy(binary_); + binary_ = nullptr; + } + + // Destroys the stored diagnostic. + void DestroyDiagnostic() { + spvDiagnosticDestroy(diagnostic_); + diagnostic_ = nullptr; + } + + std::string getDiagnosticString(); + spv_position_t getErrorPosition(); + spv_validator_options getValidatorOptions(); + + spv_binary binary_; + spv_diagnostic diagnostic_; + spv_validator_options options_; + std::unique_ptr vstate_; +}; + +template +ValidateBase::ValidateBase() : binary_(nullptr), diagnostic_(nullptr) { + // Initialize to default command line options. Different tests can then + // specialize specific options as necessary. + options_ = spvValidatorOptionsCreate(); +} + +template +spv_const_binary ValidateBase::get_const_binary() { + return spv_const_binary(binary_); +} + +template +void ValidateBase::TearDown() { + if (diagnostic_) { + spvDiagnosticPrint(diagnostic_); + } + DestroyBinary(); + DestroyDiagnostic(); + spvValidatorOptionsDestroy(options_); +} + +template +std::string ValidateBase::CompileFailure(std::string code, + spv_target_env env) { + spv_diagnostic diagnostic = nullptr; + EXPECT_NE(SPV_SUCCESS, + spvTextToBinary(ScopedContext(env).context, code.c_str(), + code.size(), &binary_, &diagnostic)); + std::string result(diagnostic->error); + spvDiagnosticDestroy(diagnostic); + return result; +} + +template +void ValidateBase::CompileSuccessfully(std::string code, + spv_target_env env) { + DestroyBinary(); + spv_diagnostic diagnostic = nullptr; + ScopedContext context(env); + auto status = spvTextToBinary(context.context, code.c_str(), code.size(), + &binary_, &diagnostic); + EXPECT_EQ(SPV_SUCCESS, status) + << "ERROR: " << diagnostic->error + << "\nSPIR-V could not be compiled into binary:\n" + << code; + ASSERT_EQ(SPV_SUCCESS, status); + spvDiagnosticDestroy(diagnostic); +} + +template +void ValidateBase::OverwriteAssembledBinary(uint32_t index, uint32_t word) { + ASSERT_TRUE(index < binary_->wordCount) + << "OverwriteAssembledBinary: The given index is larger than the binary " + "word count."; + binary_->code[index] = word; +} + +template +spv_result_t ValidateBase::ValidateInstructions(spv_target_env env) { + DestroyDiagnostic(); + if (binary_ == nullptr) { + fprintf(stderr, + "ERROR: Attempting to validate a null binary, did you forget to " + "call CompileSuccessfully?"); + fflush(stderr); + } + assert(binary_ != nullptr); + return spvValidateWithOptions(ScopedContext(env).context, options_, + get_const_binary(), &diagnostic_); +} + +template +spv_result_t ValidateBase::ValidateAndRetrieveValidationState( + spv_target_env env) { + DestroyDiagnostic(); + return spvtools::val::ValidateBinaryAndKeepValidationState( + ScopedContext(env).context, options_, get_const_binary()->code, + get_const_binary()->wordCount, &diagnostic_, &vstate_); +} + +template +std::string ValidateBase::getDiagnosticString() { + return diagnostic_ == nullptr ? std::string() + : std::string(diagnostic_->error); +} + +template +spv_validator_options ValidateBase::getValidatorOptions() { + return options_; +} + +template +spv_position_t ValidateBase::getErrorPosition() { + return diagnostic_ == nullptr ? spv_position_t() : diagnostic_->position; +} + +} // namespace spvtest + +#endif // TEST_VAL_VAL_FIXTURES_H_ diff --git a/third_party/spirv-tools/test/val/val_function_test.cpp b/third_party/spirv-tools/test/val/val_function_test.cpp new file mode 100644 index 0000000..af0199a --- /dev/null +++ b/third_party/spirv-tools/test/val/val_function_test.cpp @@ -0,0 +1,843 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::HasSubstr; +using ::testing::Values; + +using ValidateFunctionCall = spvtest::ValidateBase; + +std::string GenerateShader(const std::string& storage_class, + const std::string& capabilities, + const std::string& extensions) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability AtomicStorage +)" + capabilities + R"( +OpExtension "SPV_KHR_storage_buffer_storage_class" +)" + + extensions + R"( +OpMemoryModel Logical GLSL450 +OpName %var "var" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr = OpTypePointer )" + storage_class + R"( %int +%caller_ty = OpTypeFunction %void +%callee_ty = OpTypeFunction %void %ptr +)"; + + if (storage_class != "Function") { + spirv += "%var = OpVariable %ptr " + storage_class; + } + + spirv += R"( +%caller = OpFunction %void None %caller_ty +%1 = OpLabel +)"; + + if (storage_class == "Function") { + spirv += "%var = OpVariable %ptr Function"; + } + + spirv += R"( +%call = OpFunctionCall %void %callee %var +OpReturn +OpFunctionEnd +%callee = OpFunction %void None %callee_ty +%param = OpFunctionParameter %ptr +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + return spirv; +} + +std::string GenerateShaderParameter(const std::string& storage_class, + const std::string& capabilities, + const std::string& extensions) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability AtomicStorage +)" + capabilities + R"( +OpExtension "SPV_KHR_storage_buffer_storage_class" +)" + + extensions + R"( +OpMemoryModel Logical GLSL450 +OpName %p "p" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr = OpTypePointer )" + storage_class + R"( %int +%func_ty = OpTypeFunction %void %ptr +%caller = OpFunction %void None %func_ty +%p = OpFunctionParameter %ptr +%1 = OpLabel +%call = OpFunctionCall %void %callee %p +OpReturn +OpFunctionEnd +%callee = OpFunction %void None %func_ty +%param = OpFunctionParameter %ptr +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + return spirv; +} + +std::string GenerateShaderAccessChain(const std::string& storage_class, + const std::string& capabilities, + const std::string& extensions) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability AtomicStorage +)" + capabilities + R"( +OpExtension "SPV_KHR_storage_buffer_storage_class" +)" + + extensions + R"( +OpMemoryModel Logical GLSL450 +OpName %var "var" +OpName %gep "gep" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int2 = OpTypeVector %int 2 +%int_0 = OpConstant %int 0 +%ptr = OpTypePointer )" + storage_class + R"( %int2 +%ptr2 = OpTypePointer )" + + storage_class + R"( %int +%caller_ty = OpTypeFunction %void +%callee_ty = OpTypeFunction %void %ptr2 +)"; + + if (storage_class != "Function") { + spirv += "%var = OpVariable %ptr " + storage_class; + } + + spirv += R"( +%caller = OpFunction %void None %caller_ty +%1 = OpLabel +)"; + + if (storage_class == "Function") { + spirv += "%var = OpVariable %ptr Function"; + } + + spirv += R"( +%gep = OpAccessChain %ptr2 %var %int_0 +%call = OpFunctionCall %void %callee %gep +OpReturn +OpFunctionEnd +%callee = OpFunction %void None %callee_ty +%param = OpFunctionParameter %ptr2 +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + + return spirv; +} + +TEST_P(ValidateFunctionCall, VariableNoVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShader(storage_class, "", ""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", "Workgroup", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (storage_class == "StorageBuffer") { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("StorageBuffer pointer operand 1[%var] requires a " + "variable pointers capability")); + } else { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%var]")); + } + } +} + +TEST_P(ValidateFunctionCall, VariableVariablePointersStorageClass) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShader( + storage_class, "OpCapability VariablePointersStorageBuffer", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%var]")); + } +} + +TEST_P(ValidateFunctionCall, VariableVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = + GenerateShader(storage_class, "OpCapability VariablePointers", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%var]")); + } +} + +TEST_P(ValidateFunctionCall, ParameterNoVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShaderParameter(storage_class, "", ""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", "Workgroup", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (storage_class == "StorageBuffer") { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("StorageBuffer pointer operand 1[%p] requires a " + "variable pointers capability")); + } else { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%p]")); + } + } +} + +TEST_P(ValidateFunctionCall, ParameterVariablePointersStorageBuffer) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShaderParameter( + storage_class, "OpCapability VariablePointersStorageBuffer", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%p]")); + } +} + +TEST_P(ValidateFunctionCall, ParameterVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = + GenerateShaderParameter(storage_class, "OpCapability VariablePointers", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "UniformConstant", "Function", "Private", + "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + if (valid) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 1[%p]")); + } +} + +TEST_P(ValidateFunctionCall, NonMemoryObjectDeclarationNoVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShaderAccessChain(storage_class, "", ""); + + const std::vector valid_storage_classes = { + "Function", "Private", "Workgroup", "AtomicCounter"}; + bool valid_sc = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + + CompileSuccessfully(spirv); + spv_result_t expected_result = + storage_class == "UniformConstant" ? SPV_SUCCESS : SPV_ERROR_INVALID_ID; + EXPECT_EQ(expected_result, ValidateInstructions()); + if (valid_sc) { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Pointer operand 2[%gep] must be a memory object declaration")); + } else { + if (storage_class == "StorageBuffer") { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("StorageBuffer pointer operand 2[%gep] requires a " + "variable pointers capability")); + } else if (storage_class != "UniformConstant") { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + } + } +} + +TEST_P(ValidateFunctionCall, + NonMemoryObjectDeclarationVariablePointersStorageBuffer) { + const std::string storage_class = GetParam(); + + std::string spirv = GenerateShaderAccessChain( + storage_class, "OpCapability VariablePointersStorageBuffer", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "Function", "Private", "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid_sc = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + bool validate = + storage_class == "StorageBuffer" || storage_class == "UniformConstant"; + + CompileSuccessfully(spirv); + if (validate) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (valid_sc) { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Pointer operand 2[%gep] must be a memory object declaration")); + } else { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + } + } +} + +TEST_P(ValidateFunctionCall, NonMemoryObjectDeclarationVariablePointers) { + const std::string storage_class = GetParam(); + + std::string spirv = + GenerateShaderAccessChain(storage_class, "OpCapability VariablePointers", + "OpExtension \"SPV_KHR_variable_pointers\""); + + const std::vector valid_storage_classes = { + "Function", "Private", "Workgroup", "StorageBuffer", "AtomicCounter"}; + bool valid_sc = + std::find(valid_storage_classes.begin(), valid_storage_classes.end(), + storage_class) != valid_storage_classes.end(); + bool validate = storage_class == "StorageBuffer" || + storage_class == "Workgroup" || + storage_class == "UniformConstant"; + + CompileSuccessfully(spirv); + if (validate) { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + if (valid_sc) { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Pointer operand 2[%gep] must be a memory object declaration")); + } else { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Invalid storage class for pointer operand 2[%gep]")); + } + } +} + +TEST_F(ValidateFunctionCall, LogicallyMatchingPointers) { + std::string spirv = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_runtimearr__struct_3 ArrayStride 4 + OpMemberDecorate %_struct_5 0 Offset 0 + OpDecorate %_struct_5 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %_struct_3 = OpTypeStruct %int +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 + %_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %void = OpTypeVoid + %14 = OpTypeFunction %void + %_struct_15 = OpTypeStruct %int +%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15 +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 + %18 = OpTypeFunction %void %_ptr_Function__struct_15 + %2 = OpVariable %_ptr_Uniform__struct_5 Uniform + %1 = OpFunction %void None %14 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %uint_0 + %21 = OpFunctionCall %void %22 %20 + OpReturn + OpFunctionEnd + %22 = OpFunction %void None %18 + %23 = OpFunctionParameter %_ptr_Function__struct_15 + %24 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv); + spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateFunctionCall, LogicallyMatchingPointersNestedStruct) { + std::string spirv = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpMemberDecorate %_struct_4 0 Offset 0 + OpDecorate %_runtimearr__struct_4 ArrayStride 4 + OpMemberDecorate %_struct_6 0 Offset 0 + OpDecorate %_struct_6 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %_struct_3 = OpTypeStruct %int + %_struct_4 = OpTypeStruct %_struct_3 +%_runtimearr__struct_4 = OpTypeRuntimeArray %_struct_4 + %_struct_6 = OpTypeStruct %_runtimearr__struct_4 +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 + %void = OpTypeVoid + %13 = OpTypeFunction %void + %_struct_14 = OpTypeStruct %int + %_struct_15 = OpTypeStruct %_struct_14 +%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15 +%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 + %18 = OpTypeFunction %void %_ptr_Function__struct_15 + %2 = OpVariable %_ptr_Uniform__struct_6 Uniform + %1 = OpFunction %void None %13 + %19 = OpLabel + %20 = OpVariable %_ptr_Function__struct_15 Function + %21 = OpAccessChain %_ptr_Uniform__struct_4 %2 %int_0 %uint_0 + %22 = OpFunctionCall %void %23 %21 + OpReturn + OpFunctionEnd + %23 = OpFunction %void None %18 + %24 = OpFunctionParameter %_ptr_Function__struct_15 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateFunctionCall, LogicallyMatchingPointersNestedArray) { + std::string spirv = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpDecorate %_arr_int_uint_10 ArrayStride 4 + OpMemberDecorate %_struct_4 0 Offset 0 + OpDecorate %_runtimearr__struct_4 ArrayStride 40 + OpMemberDecorate %_struct_6 0 Offset 0 + OpDecorate %_struct_6 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_10 = OpConstant %uint 10 +%_arr_int_uint_10 = OpTypeArray %int %uint_10 + %_struct_4 = OpTypeStruct %_arr_int_uint_10 +%_runtimearr__struct_4 = OpTypeRuntimeArray %_struct_4 + %_struct_6 = OpTypeStruct %_runtimearr__struct_4 +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 + %void = OpTypeVoid + %14 = OpTypeFunction %void +%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 +%_arr_int_uint_10_0 = OpTypeArray %int %uint_10 + %_struct_17 = OpTypeStruct %_arr_int_uint_10_0 +%_ptr_Function__struct_17 = OpTypePointer Function %_struct_17 + %19 = OpTypeFunction %void %_ptr_Function__struct_17 + %2 = OpVariable %_ptr_Uniform__struct_6 Uniform + %1 = OpFunction %void None %14 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform__struct_4 %2 %int_0 %uint_0 + %22 = OpFunctionCall %void %23 %21 + OpReturn + OpFunctionEnd + %23 = OpFunction %void None %19 + %24 = OpFunctionParameter %_ptr_Function__struct_17 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateFunctionCall, LogicallyMismatchedPointersMissingMember) { + // Validation should fail because the formal parameter type has two members, + // while the actual parameter only has 1. + std::string spirv = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_runtimearr__struct_3 ArrayStride 4 + OpMemberDecorate %_struct_5 0 Offset 0 + OpDecorate %_struct_5 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %_struct_3 = OpTypeStruct %int +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 + %_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %void = OpTypeVoid + %14 = OpTypeFunction %void + %_struct_15 = OpTypeStruct %int %int +%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15 +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 + %18 = OpTypeFunction %void %_ptr_Function__struct_15 + %2 = OpVariable %_ptr_Uniform__struct_5 Uniform + %1 = OpFunction %void None %14 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %uint_0 + %21 = OpFunctionCall %void %22 %20 + OpReturn + OpFunctionEnd + %22 = OpFunction %void None %18 + %23 = OpFunctionParameter %_ptr_Function__struct_15 + %24 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument ")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("type does not match Function ")); +} + +TEST_F(ValidateFunctionCall, LogicallyMismatchedPointersDifferentMemberType) { + // Validation should fail because the formal parameter has a member that is + // a different type than the actual parameter. + std::string spirv = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_runtimearr__struct_3 ArrayStride 4 + OpMemberDecorate %_struct_5 0 Offset 0 + OpDecorate %_struct_5 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %_struct_3 = OpTypeStruct %uint +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 + %_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %void = OpTypeVoid + %14 = OpTypeFunction %void + %_struct_15 = OpTypeStruct %int +%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15 +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 + %18 = OpTypeFunction %void %_ptr_Function__struct_15 + %2 = OpVariable %_ptr_Uniform__struct_5 Uniform + %1 = OpFunction %void None %14 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %uint_0 + %21 = OpFunctionCall %void %22 %20 + OpReturn + OpFunctionEnd + %22 = OpFunction %void None %18 + %23 = OpFunctionParameter %_ptr_Function__struct_15 + %24 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument ")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("type does not match Function ")); +} + +TEST_F(ValidateFunctionCall, + LogicallyMismatchedPointersIncompatableDecorations) { + // Validation should fail because the formal parameter has an incompatible + // decoration. + std::string spirv = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_runtimearr__struct_3 ArrayStride 4 + OpMemberDecorate %_struct_5 0 Offset 0 + OpDecorate %_struct_5 Block + OpMemberDecorate %_struct_15 0 NonWritable + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %_struct_3 = OpTypeStruct %int +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 + %_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_StorageBuffer__struct_5 = OpTypePointer StorageBuffer %_struct_5 + %void = OpTypeVoid + %14 = OpTypeFunction %void + %_struct_15 = OpTypeStruct %int +%_ptr_Function__struct_15 = OpTypePointer Function %_struct_15 +%_ptr_StorageBuffer__struct_3 = OpTypePointer StorageBuffer %_struct_3 + %18 = OpTypeFunction %void %_ptr_Function__struct_15 + %2 = OpVariable %_ptr_StorageBuffer__struct_5 StorageBuffer + %1 = OpFunction %void None %14 + %19 = OpLabel + %20 = OpAccessChain %_ptr_StorageBuffer__struct_3 %2 %int_0 %uint_0 + %21 = OpFunctionCall %void %22 %20 + OpReturn + OpFunctionEnd + %22 = OpFunction %void None %18 + %23 = OpFunctionParameter %_ptr_Function__struct_15 + %24 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument ")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("type does not match Function ")); +} + +TEST_F(ValidateFunctionCall, + LogicallyMismatchedPointersIncompatableDecorations2) { + // Validation should fail because the formal parameter has an incompatible + // decoration. + std::string spirv = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_runtimearr__struct_3 ArrayStride 4 + OpMemberDecorate %_struct_5 0 Offset 0 + OpDecorate %_struct_5 BufferBlock + OpDecorate %_ptr_Uniform__struct_3 ArrayStride 4 + OpDecorate %_ptr_Uniform__struct_3_0 ArrayStride 8 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %_struct_3 = OpTypeStruct %int +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 + %_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 + %void = OpTypeVoid + %14 = OpTypeFunction %void +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 +%_ptr_Uniform__struct_3_0 = OpTypePointer Uniform %_struct_3 + %18 = OpTypeFunction %void %_ptr_Uniform__struct_3_0 + %2 = OpVariable %_ptr_Uniform__struct_5 Uniform + %1 = OpFunction %void None %14 + %19 = OpLabel + %20 = OpAccessChain %_ptr_Uniform__struct_3 %2 %int_0 %uint_0 + %21 = OpFunctionCall %void %22 %20 + OpReturn + OpFunctionEnd + %22 = OpFunction %void None %18 + %23 = OpFunctionParameter %_ptr_Uniform__struct_3_0 + %24 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument ")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("type does not match Function ")); +} + +TEST_F(ValidateFunctionCall, LogicallyMismatchedPointersArraySize) { + // Validation should fail because the formal parameter array has a different + // number of element than the actual parameter. + std::string spirv = + R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 1 1 1 + OpSource HLSL 600 + OpDecorate %2 DescriptorSet 0 + OpDecorate %2 Binding 0 + OpDecorate %_arr_int_uint_10 ArrayStride 4 + OpMemberDecorate %_struct_4 0 Offset 0 + OpDecorate %_runtimearr__struct_4 ArrayStride 40 + OpMemberDecorate %_struct_6 0 Offset 0 + OpDecorate %_struct_6 BufferBlock + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_5 = OpConstant %uint 5 + %uint_10 = OpConstant %uint 10 +%_arr_int_uint_10 = OpTypeArray %int %uint_10 + %_struct_4 = OpTypeStruct %_arr_int_uint_10 +%_runtimearr__struct_4 = OpTypeRuntimeArray %_struct_4 + %_struct_6 = OpTypeStruct %_runtimearr__struct_4 +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 + %void = OpTypeVoid + %14 = OpTypeFunction %void +%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 +%_arr_int_uint_5 = OpTypeArray %int %uint_5 + %_struct_17 = OpTypeStruct %_arr_int_uint_5 +%_ptr_Function__struct_17 = OpTypePointer Function %_struct_17 + %19 = OpTypeFunction %void %_ptr_Function__struct_17 + %2 = OpVariable %_ptr_Uniform__struct_6 Uniform + %1 = OpFunction %void None %14 + %20 = OpLabel + %21 = OpAccessChain %_ptr_Uniform__struct_4 %2 %int_0 %uint_0 + %22 = OpFunctionCall %void %23 %21 + OpReturn + OpFunctionEnd + %23 = OpFunction %void None %19 + %24 = OpFunctionParameter %_ptr_Function__struct_17 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + spvValidatorOptionsSetBeforeHlslLegalization(getValidatorOptions(), true); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpFunctionCall Argument ")); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("type does not match Function ")); +} + +INSTANTIATE_TEST_SUITE_P(StorageClass, ValidateFunctionCall, + Values("UniformConstant", "Input", "Uniform", "Output", + "Workgroup", "Private", "Function", + "PushConstant", "Image", "StorageBuffer", + "AtomicCounter")); +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_id_test.cpp b/third_party/spirv-tools/test/val/val_id_test.cpp new file mode 100644 index 0000000..adea563 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_id_test.cpp @@ -0,0 +1,6588 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +// NOTE: The tests in this file are ONLY testing ID usage, there for the input +// SPIR-V does not follow the logical layout rules from the spec in all cases in +// order to makes the tests smaller. Validation of the whole module is handled +// in stages, ID validation is only one of these stages. All validation stages +// are stand alone. + +namespace spvtools { +namespace val { +namespace { + +using spvtest::ScopedContext; +using ::testing::HasSubstr; +using ::testing::ValuesIn; + +using ValidateIdWithMessage = spvtest::ValidateBase; + +std::string kOpCapabilitySetupWithoutVector16 = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Addresses + OpCapability Int8 + OpCapability Int16 + OpCapability Int64 + OpCapability Float64 + OpCapability LiteralSampler + OpCapability Pipes + OpCapability DeviceEnqueue +)"; + +std::string kOpCapabilitySetup = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Addresses + OpCapability Int8 + OpCapability Int16 + OpCapability Int64 + OpCapability Float64 + OpCapability LiteralSampler + OpCapability Pipes + OpCapability DeviceEnqueue + OpCapability Vector16 +)"; + +std::string kOpVariablePtrSetUp = R"( + OpCapability VariablePointers + OpExtension "SPV_KHR_variable_pointers" +)"; + +std::string kGLSL450MemoryModel = + kOpCapabilitySetup + kOpVariablePtrSetUp + R"( + OpMemoryModel Logical GLSL450 +)"; + +std::string kGLSL450MemoryModelWithoutVector16 = + kOpCapabilitySetupWithoutVector16 + kOpVariablePtrSetUp + R"( + OpMemoryModel Logical GLSL450 +)"; + +std::string kNoKernelGLSL450MemoryModel = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Addresses + OpCapability Int8 + OpCapability Int16 + OpCapability Int64 + OpCapability Float64 + OpMemoryModel Logical GLSL450 +)"; + +std::string kOpenCLMemoryModel32 = R"( + OpCapability Addresses + OpCapability Linkage + OpCapability Kernel +%1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical32 OpenCL +)"; + +std::string kOpenCLMemoryModel64 = R"( + OpCapability Addresses + OpCapability Linkage + OpCapability Kernel + OpCapability Int64 +%1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Physical64 OpenCL +)"; + +std::string sampledImageSetup = R"( + %void = OpTypeVoid + %typeFuncVoid = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + %image_type = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_img = OpTypePointer UniformConstant %image_type + %tex = OpVariable %_ptr_UniformConstant_img UniformConstant + %sampler_type = OpTypeSampler +%_ptr_UniformConstant_sam = OpTypePointer UniformConstant %sampler_type + %s = OpVariable %_ptr_UniformConstant_sam UniformConstant + %sampled_image_type = OpTypeSampledImage %image_type + %v2float = OpTypeVector %float 2 + %float_1 = OpConstant %float 1 + %float_2 = OpConstant %float 2 + %const_vec_1_1 = OpConstantComposite %v2float %float_1 %float_1 + %const_vec_2_2 = OpConstantComposite %v2float %float_2 %float_2 + %bool_type = OpTypeBool + %spec_true = OpSpecConstantTrue %bool_type + %main = OpFunction %void None %typeFuncVoid + %label_1 = OpLabel + %image_inst = OpLoad %image_type %tex + %sampler_inst = OpLoad %sampler_type %s +)"; + +std::string BranchConditionalSetup = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 140 + OpName %main "main" + + ; type definitions + %bool = OpTypeBool + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 + + ; constants + %true = OpConstantTrue %bool + %i0 = OpConstant %int 0 + %i1 = OpConstant %int 1 + %f0 = OpConstant %float 0 + %f1 = OpConstant %float 1 + + + ; main function header + %void = OpTypeVoid + %voidfunc = OpTypeFunction %void + %main = OpFunction %void None %voidfunc + %lmain = OpLabel +)"; + +std::string BranchConditionalTail = R"( + %target_t = OpLabel + OpNop + OpBranch %end + %target_f = OpLabel + OpNop + OpBranch %end + + %end = OpLabel + + OpReturn + OpFunctionEnd +)"; + +// TODO: OpUndef + +TEST_F(ValidateIdWithMessage, OpName) { + std::string spirv = kGLSL450MemoryModel + R"( + OpName %2 "name" +%1 = OpTypeInt 32 0 +%2 = OpTypePointer UniformConstant %1 +%3 = OpVariable %2 UniformConstant)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpMemberNameGood) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberName %2 0 "foo" +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpMemberNameTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberName %1 0 "foo" +%1 = OpTypeInt 32 0)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpMemberName Type '1[%uint]' is not a struct type.")); +} +TEST_F(ValidateIdWithMessage, OpMemberNameMemberBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberName %1 1 "foo" +%2 = OpTypeInt 32 0 +%1 = OpTypeStruct %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpMemberName Member '1[%_struct_1]' index is larger " + "than Type '1[%_struct_1]'s member count.")); +} + +TEST_F(ValidateIdWithMessage, OpLineGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpString "/path/to/source.file" + OpLine %1 0 0 +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Input %2 +%4 = OpVariable %3 Input)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpLineFileBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeInt 32 0 + OpLine %1 0 0 + )"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpLine Target '1[%uint]' is not an OpString.")); +} + +TEST_F(ValidateIdWithMessage, OpDecorateGood) { + std::string spirv = kGLSL450MemoryModel + R"( + OpDecorate %2 GLSLShared +%1 = OpTypeInt 64 0 +%2 = OpTypeStruct %1 %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpDecorateBad) { + std::string spirv = kGLSL450MemoryModel + R"( +OpDecorate %1 GLSLShared)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("forward referenced IDs have not been defined")); +} + +TEST_F(ValidateIdWithMessage, OpMemberDecorateGood) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %2 0 RelaxedPrecision +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpMemberDecorateBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %1 0 RelaxedPrecision +%1 = OpTypeInt 32 0)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpMemberDecorate Structure type '1[%uint]' is " + "not a struct type.")); +} +TEST_F(ValidateIdWithMessage, OpMemberDecorateMemberBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %1 3 RelaxedPrecision +%int = OpTypeInt 32 0 +%1 = OpTypeStruct %int %int)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index 3 provided in OpMemberDecorate for struct " + "1[%_struct_1] is out of bounds. The structure has 2 " + "members. Largest valid index is 1.")); +} + +TEST_F(ValidateIdWithMessage, OpGroupDecorateGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpDecorationGroup + OpDecorate %1 RelaxedPrecision + OpDecorate %1 GLSLShared + OpGroupDecorate %1 %3 %4 +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 42 +%4 = OpConstant %2 23)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpDecorationGroupBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpDecorationGroup + OpDecorate %1 RelaxedPrecision + OpDecorate %1 GLSLShared + OpMemberDecorate %1 0 Constant + )"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result id of OpDecorationGroup can only " + "be targeted by OpName, OpGroupDecorate, " + "OpDecorate, OpDecorateId, and OpGroupMemberDecorate")); +} +TEST_F(ValidateIdWithMessage, OpGroupDecorateDecorationGroupBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpGroupDecorate %1 %2 %3 +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 42)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpGroupDecorate Decoration group '1[%1]' is not " + "a decoration group.")); +} +TEST_F(ValidateIdWithMessage, OpGroupDecorateTargetBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpDecorationGroup + OpDecorate %1 RelaxedPrecision + OpDecorate %1 GLSLShared + OpGroupDecorate %1 %3 +%2 = OpTypeInt 32 0)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("forward referenced IDs have not been defined")); +} +TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateDecorationGroupBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpGroupMemberDecorate %1 %2 0 +%2 = OpTypeInt 32 0)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpGroupMemberDecorate Decoration group '1[%1]' " + "is not a decoration group.")); +} +TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateIdNotStructBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpDecorationGroup + OpGroupMemberDecorate %1 %2 0 +%2 = OpTypeInt 32 0)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpGroupMemberDecorate Structure type '2[%uint]' " + "is not a struct type.")); +} +TEST_F(ValidateIdWithMessage, OpGroupMemberDecorateIndexOutOfBoundBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpDecorate %1 Offset 0 + %1 = OpDecorationGroup + OpGroupMemberDecorate %1 %struct 3 +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float %float +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Index 3 provided in OpGroupMemberDecorate for struct " + " 2[%_struct_2] is out of bounds. The structure " + "has 3 members. Largest valid index is 2.")); +} + +// TODO: OpExtInst + +TEST_F(ValidateIdWithMessage, OpEntryPointGood) { + std::string spirv = kGLSL450MemoryModel + R"( + OpEntryPoint GLCompute %3 "" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpEntryPointFunctionBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpEntryPoint GLCompute %1 "" +%1 = OpTypeVoid)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpEntryPoint Entry Point '1[%void]' is not a " + "function.")); +} +TEST_F(ValidateIdWithMessage, OpEntryPointParameterCountBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpEntryPoint GLCompute %3 "" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpEntryPoint Entry Point '1[%1]'s function " + "parameter count is not zero")); +} +TEST_F(ValidateIdWithMessage, OpEntryPointReturnTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpEntryPoint GLCompute %3 "" +%1 = OpTypeInt 32 0 +%ret = OpConstant %1 0 +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel + OpReturnValue %ret + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpEntryPoint Entry Point '1[%1]'s function " + "return type is not void.")); +} + +TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceIsNotVariableTypeBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Geometry + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %ptr_builtin_1 + OpExecutionMode %main InputPoints + OpExecutionMode %main OutputPoints + OpMemberDecorate %struct_1 0 BuiltIn InvocationId + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %struct_1 = OpTypeStruct %int +%ptr_builtin_1 = OpTypePointer Input %struct_1 + %main = OpFunction %void None %func + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Interfaces passed to OpEntryPoint must be of type " + "OpTypeVariable. Found OpTypePointer.")); +} + +TEST_F(ValidateIdWithMessage, OpEntryPointInterfaceStorageClassBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability Geometry + OpMemoryModel Logical GLSL450 + OpEntryPoint Geometry %main "main" %in_1 + OpExecutionMode %main InputPoints + OpExecutionMode %main OutputPoints + OpMemberDecorate %struct_1 0 BuiltIn InvocationId + %int = OpTypeInt 32 1 + %void = OpTypeVoid + %func = OpTypeFunction %void + %struct_1 = OpTypeStruct %int +%ptr_builtin_1 = OpTypePointer Uniform %struct_1 + %in_1 = OpVariable %ptr_builtin_1 Uniform + %main = OpFunction %void None %func + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpEntryPoint interfaces must be OpVariables with " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class 2 for Entry Point id 1.")); +} + +TEST_F(ValidateIdWithMessage, OpExecutionModeGood) { + std::string spirv = kGLSL450MemoryModel + R"( + OpEntryPoint GLCompute %3 "" + OpExecutionMode %3 LocalSize 1 1 1 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpExecutionModeEntryPointMissing) { + std::string spirv = kGLSL450MemoryModel + R"( + OpExecutionMode %3 LocalSize 1 1 1 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpExecutionMode Entry Point '1[%1]' is not the " + "Entry Point operand of an OpEntryPoint.")); +} + +TEST_F(ValidateIdWithMessage, OpExecutionModeEntryPointBad) { + std::string spirv = kGLSL450MemoryModel + R"( + OpEntryPoint GLCompute %3 "" %a + OpExecutionMode %a LocalSize 1 1 1 +%void = OpTypeVoid +%ptr = OpTypePointer Input %void +%a = OpVariable %ptr Input +%2 = OpTypeFunction %void +%3 = OpFunction %void None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpExecutionMode Entry Point '2[%2]' is not the " + "Entry Point operand of an OpEntryPoint.")); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorFloat) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorInt) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeVector %1 4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorUInt) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 64 0 +%2 = OpTypeVector %1 4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorBool) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeBool +%2 = OpTypeVector %1 4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorComponentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypePointer UniformConstant %1 +%3 = OpTypeVector %2 4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeVector Component Type " + "'2[%_ptr_UniformConstant_float]' is not a scalar type.")); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountLessThanTwoBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal number of components (1) for TypeVector\n %v1float = " + "OpTypeVector %float 1\n")); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountGreaterThanFourBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Illegal number of components (5) for TypeVector\n %v5float = " + "OpTypeVector %float 5\n")); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountEightWithoutVector16Bad) { + std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 8)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Having 8 components for TypeVector requires the Vector16 " + "capability\n %v8float = OpTypeVector %float 8\n")); +} + +TEST_F(ValidateIdWithMessage, + OpTypeVectorColumnCountSixteenWithoutVector16Bad) { + std::string spirv = kGLSL450MemoryModelWithoutVector16 + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 16)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Having 16 components for TypeVector requires the Vector16 " + "capability\n %v16float = OpTypeVector %float 16\n")); +} + +TEST_F(ValidateIdWithMessage, OpTypeVectorColumnCountOfEightWithVector16Good) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 8)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, + OpTypeVectorColumnCountOfSixteenWithVector16Good) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 16)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeMatrixGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 2 +%3 = OpTypeMatrix %2 3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnTypeNonVectorBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeMatrix %1 3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("olumns in a matrix must be of type vector.\n %mat3float = " + "OpTypeMatrix %float 3\n")); +} + +TEST_F(ValidateIdWithMessage, OpTypeMatrixVectorTypeNonFloatBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 16 0 +%2 = OpTypeVector %1 2 +%3 = OpTypeMatrix %2 2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Matrix types can only be parameterized with floating-point " + "types.\n %mat2v2ushort = OpTypeMatrix %v2ushort 2\n")); +} + +TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnCountLessThanTwoBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 2 +%3 = OpTypeMatrix %2 1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Matrix types can only be parameterized as having only 2, 3, " + "or 4 columns.\n %mat1v2float = OpTypeMatrix %v2float 1\n")); +} + +TEST_F(ValidateIdWithMessage, OpTypeMatrixColumnCountGreaterThanFourBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 2 +%3 = OpTypeMatrix %2 8)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Matrix types can only be parameterized as having only 2, 3, " + "or 4 columns.\n %mat8v2float = OpTypeMatrix %v2float 8\n")); +} + +TEST_F(ValidateIdWithMessage, OpTypeSamplerGood) { + // In Rev31, OpTypeSampler takes no arguments. + std::string spirv = kGLSL450MemoryModel + R"( +%s = OpTypeSampler)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeArrayGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 1 +%3 = OpTypeArray %1 %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeArrayElementTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 1 +%3 = OpTypeArray %2 %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeArray Element Type '2[%uint_1]' is not a " + "type.")); +} + +// Signed or unsigned. +enum Signed { kSigned, kUnsigned }; + +// Creates an assembly module declaring OpTypeArray with the given length. +std::string MakeArrayLength(const std::string& len, Signed isSigned, int width, + int max_int_width = 64, + bool use_vulkan_memory_model = false) { + std::ostringstream ss; + ss << R"( + OpCapability Shader + )"; + if (use_vulkan_memory_model) { + ss << " OpCapability VulkanMemoryModel\n"; + } + if (width == 16) { + ss << " OpCapability Int16\n"; + } + if (max_int_width > 32) { + ss << "\n OpCapability Int64\n"; + } + if (use_vulkan_memory_model) { + ss << " OpExtension \"SPV_KHR_vulkan_memory_model\"\n"; + ss << "OpMemoryModel Logical Vulkan\n"; + } else { + ss << "OpMemoryModel Logical GLSL450\n"; + } + ss << "OpEntryPoint GLCompute %main \"main\"\n"; + ss << "OpExecutionMode %main LocalSize 1 1 1\n"; + ss << " %t = OpTypeInt " << width << (isSigned == kSigned ? " 1" : " 0"); + ss << " %l = OpConstant %t " << len; + ss << " %a = OpTypeArray %t %l"; + ss << " %void = OpTypeVoid \n" + " %voidfn = OpTypeFunction %void \n" + " %main = OpFunction %void None %voidfn \n" + " %entry = OpLabel\n" + " OpReturn\n" + " OpFunctionEnd\n"; + return ss.str(); +} + +// Tests OpTypeArray. Parameter is the width (in bits) of the array-length's +// type. +class OpTypeArrayLengthTest + : public spvtest::TextToBinaryTestBase<::testing::TestWithParam> { + protected: + OpTypeArrayLengthTest() + : env_(SPV_ENV_UNIVERSAL_1_0), + position_(spv_position_t{0, 0, 0}), + diagnostic_(spvDiagnosticCreate(&position_, "")) {} + + ~OpTypeArrayLengthTest() { spvDiagnosticDestroy(diagnostic_); } + + // Runs spvValidate() on v, printing any errors via spvDiagnosticPrint(). + spv_result_t Val(const SpirvVector& v, const std::string& expected_err = "") { + spv_const_binary_t cbinary{v.data(), v.size()}; + spvDiagnosticDestroy(diagnostic_); + diagnostic_ = nullptr; + const auto status = + spvValidate(ScopedContext(env_).context, &cbinary, &diagnostic_); + if (status != SPV_SUCCESS) { + spvDiagnosticPrint(diagnostic_); + EXPECT_THAT(std::string(diagnostic_->error), + testing::ContainsRegex(expected_err)); + } + return status; + } + + protected: + spv_target_env env_; + + private: + spv_position_t position_; // For creating diagnostic_. + spv_diagnostic diagnostic_; +}; + +TEST_P(OpTypeArrayLengthTest, LengthPositiveSmall) { + const int width = GetParam(); + EXPECT_EQ(SPV_SUCCESS, + Val(CompileSuccessfully(MakeArrayLength("1", kSigned, width)))); + EXPECT_EQ(SPV_SUCCESS, + Val(CompileSuccessfully(MakeArrayLength("1", kUnsigned, width)))); + EXPECT_EQ(SPV_SUCCESS, + Val(CompileSuccessfully(MakeArrayLength("2", kSigned, width)))); + EXPECT_EQ(SPV_SUCCESS, + Val(CompileSuccessfully(MakeArrayLength("2", kUnsigned, width)))); + EXPECT_EQ(SPV_SUCCESS, + Val(CompileSuccessfully(MakeArrayLength("55", kSigned, width)))); + EXPECT_EQ(SPV_SUCCESS, + Val(CompileSuccessfully(MakeArrayLength("55", kUnsigned, width)))); + const std::string fpad(width / 4 - 1, 'F'); + EXPECT_EQ( + SPV_SUCCESS, + Val(CompileSuccessfully(MakeArrayLength("0x7" + fpad, kSigned, width)))) + << MakeArrayLength("0x7" + fpad, kSigned, width); +} + +TEST_P(OpTypeArrayLengthTest, LengthZero) { + const int width = GetParam(); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + Val(CompileSuccessfully(MakeArrayLength("0", kSigned, width)), + "OpTypeArray Length '3\\[%.*\\]' default value must be at " + "least 1.")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + Val(CompileSuccessfully(MakeArrayLength("0", kUnsigned, width)), + "OpTypeArray Length '3\\[%.*\\]' default value must be at " + "least 1.")); +} + +TEST_P(OpTypeArrayLengthTest, LengthNegative) { + const int width = GetParam(); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + Val(CompileSuccessfully(MakeArrayLength("-1", kSigned, width)), + "OpTypeArray Length '3\\[%.*\\]' default value must be at " + "least 1.")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + Val(CompileSuccessfully(MakeArrayLength("-2", kSigned, width)), + "OpTypeArray Length '3\\[%.*\\]' default value must be at " + "least 1.")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + Val(CompileSuccessfully(MakeArrayLength("-123", kSigned, width)), + "OpTypeArray Length '3\\[%.*\\]' default value must be at " + "least 1.")); + const std::string neg_max = "0x8" + std::string(width / 4 - 1, '0'); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + Val(CompileSuccessfully(MakeArrayLength(neg_max, kSigned, width)), + "OpTypeArray Length '3\\[%.*\\]' default value must be at " + "least 1.")); +} + +// Returns the string form of an integer of the form 0x80....0 of the +// given bit width. +std::string big_num_ending_0(int bit_width) { + return "0x8" + std::string(bit_width / 4 - 1, '0'); +} + +// Returns the string form of an integer of the form 0x80..001 of the +// given bit width. +std::string big_num_ending_1(int bit_width) { + return "0x8" + std::string(bit_width / 4 - 2, '0') + "1"; +} + +TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InVulkan) { + env_ = SPV_ENV_VULKAN_1_0; + const int width = GetParam(); + for (int max_int_width : {32, 64}) { + if (width > max_int_width) { + // Not valid to even make the OpConstant in this case. + continue; + } + const auto module = CompileSuccessfully(MakeArrayLength( + big_num_ending_0(width), kUnsigned, width, max_int_width)); + EXPECT_EQ(SPV_SUCCESS, Val(module)); + } +} + +TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InVulkan) { + env_ = SPV_ENV_VULKAN_1_0; + const int width = GetParam(); + for (int max_int_width : {32, 64}) { + if (width > max_int_width) { + // Not valid to even make the OpConstant in this case. + continue; + } + const auto module = CompileSuccessfully(MakeArrayLength( + big_num_ending_1(width), kUnsigned, width, max_int_width)); + EXPECT_EQ(SPV_SUCCESS, Val(module)); + } +} + +TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding0InWebGPU) { + env_ = SPV_ENV_WEBGPU_0; + const int width = GetParam(); + // WebGPU only has 32 bit integers. + if (width != 32) return; + const int max_int_width = 32; + const auto module = CompileSuccessfully(MakeArrayLength( + big_num_ending_0(width), kUnsigned, width, max_int_width, true)); + EXPECT_EQ(SPV_SUCCESS, Val(module)); +} + +TEST_P(OpTypeArrayLengthTest, LengthPositiveHugeEnding1InWebGPU) { + env_ = SPV_ENV_WEBGPU_0; + const int width = GetParam(); + // WebGPU only has 32 bit integers. + if (width != 32) return; + const int max_int_width = 32; + const auto module = CompileSuccessfully(MakeArrayLength( + big_num_ending_1(width), kUnsigned, width, max_int_width, true)); + EXPECT_EQ(SPV_ERROR_INVALID_ID, + Val(module, + "OpTypeArray Length '3\\[%.*\\]' size exceeds max value " + "2147483648 permitted by WebGPU: got 2147483649")); +} + +// The only valid widths for integers are 8, 16, 32, and 64. +// Since the Int8 capability requires the Kernel capability, and the Kernel +// capability prohibits usage of signed integers, we can skip 8-bit integers +// here since the purpose of these tests is to check the validity of +// OpTypeArray, not OpTypeInt. +INSTANTIATE_TEST_SUITE_P(Widths, OpTypeArrayLengthTest, + ValuesIn(std::vector{16, 32, 64})); + +TEST_F(ValidateIdWithMessage, OpTypeArrayLengthNull) { + std::string spirv = kGLSL450MemoryModel + R"( +%i32 = OpTypeInt 32 0 +%len = OpConstantNull %i32 +%ary = OpTypeArray %i32 %len)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeArray Length '2[%2]' default value must be at least 1.")); +} + +TEST_F(ValidateIdWithMessage, OpTypeArrayLengthSpecConst) { + std::string spirv = kGLSL450MemoryModel + R"( +%i32 = OpTypeInt 32 0 +%len = OpSpecConstant %i32 2 +%ary = OpTypeArray %i32 %len)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeArrayLengthSpecConstOp) { + std::string spirv = kGLSL450MemoryModel + R"( +%i32 = OpTypeInt 32 0 +%c1 = OpConstant %i32 1 +%c2 = OpConstant %i32 2 +%len = OpSpecConstantOp %i32 IAdd %c1 %c2 +%ary = OpTypeArray %i32 %len)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeRuntimeArray %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpTypeRuntimeArrayBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 0 +%3 = OpTypeRuntimeArray %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeRuntimeArray Element Type '2[%uint_0]' is not a " + "type.")); +} +// TODO: Object of this type can only be created with OpVariable using the +// Unifrom Storage Class + +TEST_F(ValidateIdWithMessage, OpTypeStructGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeFloat 64 +%3 = OpTypePointer Input %1 +%4 = OpTypeStruct %1 %2 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpTypeStructMemberTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeFloat 64 +%3 = OpConstant %2 0.0 +%4 = OpTypeStruct %1 %2 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeStruct Member Type '3[%double_0]' is not " + "a type.")); +} + +TEST_F(ValidateIdWithMessage, OpTypeStructOpaqueTypeBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + %1 = OpTypeSampler + %2 = OpTypeStruct %1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeStruct must not contain an opaque type")); +} + +TEST_F(ValidateIdWithMessage, OpTypePointerGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypePointer Input %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpTypePointerBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 0 +%3 = OpTypePointer Input %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypePointer Type '2[%uint_0]' is not a " + "type.")); +} + +TEST_F(ValidateIdWithMessage, OpTypeFunctionGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpTypeFunctionReturnTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 0 +%3 = OpTypeFunction %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeFunction Return Type '2[%uint_0]' is not " + "a type.")); +} +TEST_F(ValidateIdWithMessage, OpTypeFunctionParameterBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 %2 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeFunction Parameter Type '3[%uint_0]' is not a " + "type.")); +} + +TEST_F(ValidateIdWithMessage, OpTypeFunctionParameterTypeVoidBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%4 = OpTypeFunction %1 %2 %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeFunction Parameter Type '1[%void]' cannot " + "be OpTypeVoid.")); +} + +TEST_F(ValidateIdWithMessage, OpTypePipeGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 16 +%3 = OpTypePipe ReadOnly)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpConstantTrueGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeBool +%2 = OpConstantTrue %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantTrueBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpConstantTrue %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantTrue Result Type '1[%void]' is not a boolean " + "type.")); +} + +TEST_F(ValidateIdWithMessage, OpConstantFalseGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeBool +%2 = OpConstantTrue %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantFalseBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpConstantFalse %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantFalse Result Type '1[%void]' is not a boolean " + "type.")); +} + +TEST_F(ValidateIdWithMessage, OpConstantGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpConstant !1 !0)"; + // The expected failure code is implementation dependent (currently + // INVALID_BINARY because the binary parser catches these cases) and may + // change over time, but this must always fail. + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%3 = OpConstant %1 3.14 +%4 = OpConstantComposite %2 %3 %3 %3 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorWithUndefGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%3 = OpConstant %1 3.14 +%9 = OpUndef %1 +%4 = OpConstantComposite %2 %3 %3 %3 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorResultTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%3 = OpConstant %1 3.14 +%4 = OpConstantComposite %1 %3 %3 %3 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantComposite Result Type '1[%float]' is not a " + "composite type.")); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeVectorConstituentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%4 = OpTypeInt 32 0 +%3 = OpConstant %1 3.14 +%5 = OpConstant %4 42 ; bad type for constant value +%6 = OpConstantComposite %2 %3 %5 %3 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent '5[%uint_42]'s type " + "does not match Result Type '2[%v4float]'s vector " + "element type.")); +} +TEST_F(ValidateIdWithMessage, + OpConstantCompositeVectorConstituentUndefTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%4 = OpTypeInt 32 0 +%3 = OpConstant %1 3.14 +%5 = OpUndef %4 ; bad type for undef value +%6 = OpConstantComposite %2 %3 %5 %3 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent '5[%5]'s type does not " + "match Result Type '2[%v4float]'s vector element type.")); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixGood) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %3 = OpTypeMatrix %2 4 + %4 = OpConstant %1 1.0 + %5 = OpConstant %1 0.0 + %6 = OpConstantComposite %2 %4 %5 %5 %5 + %7 = OpConstantComposite %2 %5 %4 %5 %5 + %8 = OpConstantComposite %2 %5 %5 %4 %5 + %9 = OpConstantComposite %2 %5 %5 %5 %4 +%10 = OpConstantComposite %3 %6 %7 %8 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixUndefGood) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %3 = OpTypeMatrix %2 4 + %4 = OpConstant %1 1.0 + %5 = OpConstant %1 0.0 + %6 = OpConstantComposite %2 %4 %5 %5 %5 + %7 = OpConstantComposite %2 %5 %4 %5 %5 + %8 = OpConstantComposite %2 %5 %5 %4 %5 + %9 = OpUndef %2 +%10 = OpConstantComposite %3 %6 %7 %8 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeMatrixConstituentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 +%11 = OpTypeVector %1 3 + %3 = OpTypeMatrix %2 4 + %4 = OpConstant %1 1.0 + %5 = OpConstant %1 0.0 + %6 = OpConstantComposite %2 %4 %5 %5 %5 + %7 = OpConstantComposite %2 %5 %4 %5 %5 + %8 = OpConstantComposite %2 %5 %5 %4 %5 + %9 = OpConstantComposite %11 %5 %5 %5 +%10 = OpConstantComposite %3 %6 %7 %8 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent '10[%10]' vector " + "component count does not match Result Type " + "'4[%mat4v4float]'s vector component count.")); +} +TEST_F(ValidateIdWithMessage, + OpConstantCompositeMatrixConstituentUndefTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 +%11 = OpTypeVector %1 3 + %3 = OpTypeMatrix %2 4 + %4 = OpConstant %1 1.0 + %5 = OpConstant %1 0.0 + %6 = OpConstantComposite %2 %4 %5 %5 %5 + %7 = OpConstantComposite %2 %5 %4 %5 %5 + %8 = OpConstantComposite %2 %5 %5 %4 %5 + %9 = OpUndef %11 +%10 = OpConstantComposite %3 %6 %7 %8 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent '10[%10]' vector " + "component count does not match Result Type " + "'4[%mat4v4float]'s vector component count.")); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%3 = OpTypeArray %1 %2 +%4 = OpConstantComposite %3 %2 %2 %2 %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayWithUndefGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%9 = OpUndef %1 +%3 = OpTypeArray %1 %2 +%4 = OpConstantComposite %3 %2 %2 %2 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%3 = OpTypeArray %1 %2 +%4 = OpConstantComposite %3 %2 %2 %2 %1)"; // Uses a type as operand + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%uint] cannot be a " + "type")); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstConstituentBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%3 = OpTypeArray %1 %2 +%4 = OpTypePointer Uniform %1 +%5 = OpVariable %4 Uniform +%6 = OpConstantComposite %3 %2 %2 %2 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent '5[%5]' is not a " + "constant or undef.")); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstituentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%3 = OpTypeArray %1 %2 +%5 = OpTypeFloat 32 +%6 = OpConstant %5 3.14 ; bad type for const value +%4 = OpConstantComposite %3 %2 %2 %2 %6)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent " + "'5[%float_3_1400001]'s type does not match Result " + "Type '3[%_arr_uint_uint_4]'s array element " + "type.")); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeArrayConstituentUndefTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%3 = OpTypeArray %1 %2 +%5 = OpTypeFloat 32 +%6 = OpUndef %5 ; bad type for undef +%4 = OpConstantComposite %3 %2 %2 %2 %6)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent " + "'5[%5]'s type does not match Result " + "Type '3[%_arr_uint_uint_4]'s array element " + "type.")); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeStructGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpConstant %1 42 +%5 = OpConstant %2 4300000000 +%6 = OpConstantComposite %3 %4 %4 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeStructUndefGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpConstant %1 42 +%5 = OpUndef %2 +%6 = OpConstantComposite %3 %4 %4 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantCompositeStructMemberTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpConstant %1 42 +%5 = OpConstant %2 4300000000 +%6 = OpConstantComposite %3 %4 %5 %4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent " + "'5[%ulong_4300000000]' type does not match the " + "Result Type '3[%_struct_3]'s member type.")); +} + +TEST_F(ValidateIdWithMessage, OpConstantCompositeStructMemberUndefTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpConstant %1 42 +%5 = OpUndef %2 +%6 = OpConstantComposite %3 %4 %5 %4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantComposite Constituent '5[%5]' type " + "does not match the Result Type '3[%_struct_3]'s " + "member type.")); +} + +TEST_F(ValidateIdWithMessage, OpConstantSamplerGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%float = OpTypeFloat 32 +%samplerType = OpTypeSampler +%3 = OpConstantSampler %samplerType ClampToEdge 0 Nearest)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpConstantSamplerResultTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpConstantSampler %1 Clamp 0 Nearest)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpConstantSampler Result Type '1[%float]' is not a sampler " + "type.")); +} + +TEST_F(ValidateIdWithMessage, OpConstantNullGood) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeBool + %2 = OpConstantNull %1 + %3 = OpTypeInt 32 0 + %4 = OpConstantNull %3 + %5 = OpTypeFloat 32 + %6 = OpConstantNull %5 + %7 = OpTypePointer UniformConstant %3 + %8 = OpConstantNull %7 + %9 = OpTypeEvent +%10 = OpConstantNull %9 +%11 = OpTypeDeviceEvent +%12 = OpConstantNull %11 +%13 = OpTypeReserveId +%14 = OpConstantNull %13 +%15 = OpTypeQueue +%16 = OpConstantNull %15 +%17 = OpTypeVector %5 2 +%18 = OpConstantNull %17 +%19 = OpTypeMatrix %17 2 +%20 = OpConstantNull %19 +%25 = OpConstant %3 8 +%21 = OpTypeArray %3 %25 +%22 = OpConstantNull %21 +%23 = OpTypeStruct %3 %5 %1 +%24 = OpConstantNull %23 +%26 = OpTypeArray %17 %25 +%27 = OpConstantNull %26 +%28 = OpTypeStruct %7 %26 %26 %1 +%29 = OpConstantNull %28 +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpConstantNullBasicBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpConstantNull %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpConstantNull Result Type '1[%void]' cannot have a null " + "value.")); +} + +TEST_F(ValidateIdWithMessage, OpConstantNullArrayBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%2 = OpTypeInt 32 0 +%3 = OpTypeSampler +%4 = OpConstant %2 4 +%5 = OpTypeArray %3 %4 +%6 = OpConstantNull %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpConstantNull Result Type '4[%_arr_2_uint_4]' cannot have a " + "null value.")); +} + +TEST_F(ValidateIdWithMessage, OpConstantNullStructBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%2 = OpTypeSampler +%3 = OpTypeStruct %2 %2 +%4 = OpConstantNull %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpConstantNull Result Type '2[%_struct_2]' " + "cannot have a null value.")); +} + +TEST_F(ValidateIdWithMessage, OpConstantNullRuntimeArrayBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%bool = OpTypeBool +%array = OpTypeRuntimeArray %bool +%null = OpConstantNull %array)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpConstantNull Result Type '2[%_runtimearr_bool]' cannot have " + "a null value.")); +} + +TEST_F(ValidateIdWithMessage, OpSpecConstantTrueGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeBool +%2 = OpSpecConstantTrue %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpSpecConstantTrueBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpSpecConstantTrue %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantTrue Result Type '1[%void]' is not " + "a boolean type")); +} + +TEST_F(ValidateIdWithMessage, OpSpecConstantFalseGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeBool +%2 = OpSpecConstantFalse %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpSpecConstantFalseBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpSpecConstantFalse %1)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpSpecConstantFalse Result Type '1[%void]' is not " + "a boolean type")); +} + +TEST_F(ValidateIdWithMessage, OpSpecConstantGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpSpecConstant %1 42)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpSpecConstantBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpSpecConstant !1 !4)"; + // The expected failure code is implementation dependent (currently + // INVALID_BINARY because the binary parser catches these cases) and may + // change over time, but this must always fail. + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Type Id 1 is not a scalar numeric type")); +} + +// Valid: SpecConstantComposite specializes to a vector. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%3 = OpSpecConstant %1 3.14 +%4 = OpConstant %1 3.14 +%5 = OpSpecConstantComposite %2 %3 %3 %4 %4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Valid: Vector of floats and Undefs. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorWithUndefGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%3 = OpSpecConstant %1 3.14 +%5 = OpConstant %1 3.14 +%9 = OpUndef %1 +%4 = OpSpecConstantComposite %2 %3 %5 %3 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: result type is float. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorResultTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%3 = OpSpecConstant %1 3.14 +%4 = OpSpecConstantComposite %1 %3 %3 %3 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a composite type")); +} + +// Invalid: Vector contains a mix of Int and Float. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorConstituentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%4 = OpTypeInt 32 0 +%3 = OpSpecConstant %1 3.14 +%5 = OpConstant %4 42 ; bad type for constant value +%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent " + "'5[%uint_42]'s type does not match Result Type " + "'2[%v4float]'s vector element type.")); +} + +// Invalid: Constituent is not a constant +TEST_F(ValidateIdWithMessage, + OpSpecConstantCompositeVectorConstituentNotConstantBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%3 = OpTypeInt 32 0 +%4 = OpSpecConstant %1 3.14 +%5 = OpTypePointer Uniform %1 +%6 = OpVariable %5 Uniform +%7 = OpSpecConstantComposite %2 %6 %4 %4 %4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '6[%6]' is " + "not a constant or undef.")); +} + +// Invalid: Vector contains a mix of Undef-int and Float. +TEST_F(ValidateIdWithMessage, + OpSpecConstantCompositeVectorConstituentUndefTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 4 +%4 = OpTypeInt 32 0 +%3 = OpSpecConstant %1 3.14 +%5 = OpUndef %4 ; bad type for undef value +%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '5[%5]'s " + "type does not match Result Type '2[%v4float]'s " + "vector element type.")); +} + +// Invalid: Vector expects 3 components, but 4 specified. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeVectorNumComponentsBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeVector %1 3 +%3 = OpConstant %1 3.14 +%5 = OpSpecConstant %1 4.0 +%6 = OpSpecConstantComposite %2 %3 %5 %3 %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent count does " + "not match Result Type '2[%v3float]'s vector " + "component count.")); +} + +// Valid: 4x4 matrix of floats +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixGood) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %3 = OpTypeMatrix %2 4 + %4 = OpConstant %1 1.0 + %5 = OpSpecConstant %1 0.0 + %6 = OpSpecConstantComposite %2 %4 %5 %5 %5 + %7 = OpSpecConstantComposite %2 %5 %4 %5 %5 + %8 = OpSpecConstantComposite %2 %5 %5 %4 %5 + %9 = OpSpecConstantComposite %2 %5 %5 %5 %4 +%10 = OpSpecConstantComposite %3 %6 %7 %8 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Valid: Matrix in which one column is Undef +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixUndefGood) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %3 = OpTypeMatrix %2 4 + %4 = OpConstant %1 1.0 + %5 = OpSpecConstant %1 0.0 + %6 = OpSpecConstantComposite %2 %4 %5 %5 %5 + %7 = OpSpecConstantComposite %2 %5 %4 %5 %5 + %8 = OpSpecConstantComposite %2 %5 %5 %4 %5 + %9 = OpUndef %2 +%10 = OpSpecConstantComposite %3 %6 %7 %8 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Matrix in which the sizes of column vectors are not equal. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixConstituentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %3 = OpTypeVector %1 3 + %4 = OpTypeMatrix %2 4 + %5 = OpSpecConstant %1 1.0 + %6 = OpConstant %1 0.0 + %7 = OpSpecConstantComposite %2 %5 %6 %6 %6 + %8 = OpSpecConstantComposite %2 %6 %5 %6 %6 + %9 = OpSpecConstantComposite %2 %6 %6 %5 %6 + %10 = OpSpecConstantComposite %3 %6 %6 %6 +%11 = OpSpecConstantComposite %4 %7 %8 %9 %10)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '10[%10]' " + "vector component count does not match Result Type " + " '4[%mat4v4float]'s vector component count.")); +} + +// Invalid: Matrix type expects 4 columns but only 3 specified. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixNumColsBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %3 = OpTypeMatrix %2 4 + %4 = OpSpecConstant %1 1.0 + %5 = OpConstant %1 0.0 + %6 = OpSpecConstantComposite %2 %4 %5 %5 %5 + %7 = OpSpecConstantComposite %2 %5 %4 %5 %5 + %8 = OpSpecConstantComposite %2 %5 %5 %4 %5 +%10 = OpSpecConstantComposite %3 %6 %7 %8)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent count does " + "not match Result Type '3[%mat4v4float]'s matrix column " + "count.")); +} + +// Invalid: Composite contains a non-const/undef component +TEST_F(ValidateIdWithMessage, + OpSpecConstantCompositeMatrixConstituentNotConstBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpConstant %1 0.0 + %3 = OpTypeVector %1 4 + %4 = OpTypeMatrix %3 4 + %5 = OpSpecConstantComposite %3 %2 %2 %2 %2 + %6 = OpTypePointer Uniform %1 + %7 = OpVariable %6 Uniform + %8 = OpSpecConstantComposite %4 %5 %5 %5 %7)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '7[%7]' is " + "not a constant or undef.")); +} + +// Invalid: Composite contains a column that is *not* a vector (it's an array) +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeInt 32 0 + %3 = OpSpecConstant %2 4 + %4 = OpConstant %1 0.0 + %5 = OpTypeVector %1 4 + %6 = OpTypeArray %2 %3 + %7 = OpTypeMatrix %5 4 + %8 = OpSpecConstantComposite %6 %3 %3 %3 %3 + %9 = OpSpecConstantComposite %5 %4 %4 %4 %4 + %10 = OpSpecConstantComposite %7 %9 %9 %9 %8)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '8[%8]' type " + "does not match Result Type '7[%mat4v4float]'s " + "matrix column type.")); +} + +// Invalid: Matrix with an Undef column of the wrong size. +TEST_F(ValidateIdWithMessage, + OpSpecConstantCompositeMatrixConstituentUndefTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeFloat 32 + %2 = OpTypeVector %1 4 + %3 = OpTypeVector %1 3 + %4 = OpTypeMatrix %2 4 + %5 = OpSpecConstant %1 1.0 + %6 = OpSpecConstant %1 0.0 + %7 = OpSpecConstantComposite %2 %5 %6 %6 %6 + %8 = OpSpecConstantComposite %2 %6 %5 %6 %6 + %9 = OpSpecConstantComposite %2 %6 %6 %5 %6 + %10 = OpUndef %3 + %11 = OpSpecConstantComposite %4 %7 %8 %9 %10)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '10[%10]' " + "vector component count does not match Result Type " + " '4[%mat4v4float]'s vector component count.")); +} + +// Invalid: Matrix in which some columns are Int and some are Float. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeMatrixColumnTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeInt 32 0 + %2 = OpTypeFloat 32 + %3 = OpTypeVector %1 2 + %4 = OpTypeVector %2 2 + %5 = OpTypeMatrix %4 2 + %6 = OpSpecConstant %1 42 + %7 = OpConstant %2 3.14 + %8 = OpSpecConstantComposite %3 %6 %6 + %9 = OpSpecConstantComposite %4 %7 %7 +%10 = OpSpecConstantComposite %5 %8 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '8[%8]' " + "component type does not match Result Type " + "'5[%mat2v2float]'s matrix column component type.")); +} + +// Valid: Array of integers +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpSpecConstant %1 4 +%5 = OpConstant %1 5 +%3 = OpTypeArray %1 %2 +%6 = OpTypeArray %1 %5 +%4 = OpSpecConstantComposite %3 %2 %2 %2 %2 +%7 = OpSpecConstantComposite %3 %5 %5 %5 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Expecting an array of 4 components, but 3 specified. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayNumComponentsBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%3 = OpTypeArray %1 %2 +%4 = OpSpecConstantComposite %3 %2 %2 %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent count does not " + "match Result Type '3[%_arr_uint_uint_4]'s array " + "length.")); +} + +// Valid: Array of Integers and Undef-int +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayWithUndefGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpSpecConstant %1 4 +%9 = OpUndef %1 +%3 = OpTypeArray %1 %2 +%4 = OpSpecConstantComposite %3 %2 %2 %2 %9)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Array uses a type as operand. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstConstituentBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%3 = OpTypeArray %1 %2 +%4 = OpTypePointer Uniform %1 +%5 = OpVariable %4 Uniform +%6 = OpSpecConstantComposite %3 %2 %2 %2 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '5[%5]' is " + "not a constant or undef.")); +} + +// Invalid: Array has a mix of Int and Float components. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeArrayConstituentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpConstant %1 4 +%3 = OpTypeArray %1 %2 +%4 = OpTypeFloat 32 +%5 = OpSpecConstant %4 3.14 ; bad type for const value +%6 = OpSpecConstantComposite %3 %2 %2 %2 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '5[%5]'s " + "type does not match Result Type " + "'3[%_arr_uint_uint_4]'s array element type.")); +} + +// Invalid: Array has a mix of Int and Undef-float. +TEST_F(ValidateIdWithMessage, + OpSpecConstantCompositeArrayConstituentUndefTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpSpecConstant %1 4 +%3 = OpTypeArray %1 %2 +%5 = OpTypeFloat 32 +%6 = OpUndef %5 ; bad type for undef +%4 = OpSpecConstantComposite %3 %2 %2 %2 %6)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '5[%5]'s " + "type does not match Result Type " + "'3[%_arr_uint_2]'s array element type.")); +} + +// Valid: Struct of {Int32,Int32,Int64}. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpConstant %1 42 +%5 = OpSpecConstant %2 4300000000 +%6 = OpSpecConstantComposite %3 %4 %4 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: missing one int32 struct member. +TEST_F(ValidateIdWithMessage, + OpSpecConstantCompositeStructMissingComponentBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%3 = OpTypeStruct %1 %1 %1 +%4 = OpConstant %1 42 +%5 = OpSpecConstant %1 430 +%6 = OpSpecConstantComposite %3 %4 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent " + "'2[%_struct_2]' count does not match Result Type " + " '2[%_struct_2]'s struct member count.")); +} + +// Valid: Struct uses Undef-int64. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructUndefGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpSpecConstant %1 42 +%5 = OpUndef %2 +%6 = OpSpecConstantComposite %3 %4 %4 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Composite contains non-const/undef component. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructNonConstBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpSpecConstant %1 42 +%5 = OpUndef %2 +%6 = OpTypePointer Uniform %1 +%7 = OpVariable %6 Uniform +%8 = OpSpecConstantComposite %3 %4 %7 %5)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '7[%7]' is " + "not a constant or undef.")); +} + +// Invalid: Struct component type does not match expected specialization type. +// Second component was expected to be Int32, but got Int64. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpConstant %1 42 +%5 = OpSpecConstant %2 4300000000 +%6 = OpSpecConstantComposite %3 %4 %5 %4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '5[%5]' type " + "does not match the Result Type '3[%_struct_3]'s " + "member type.")); +} + +// Invalid: Undef-int64 used when Int32 was expected. +TEST_F(ValidateIdWithMessage, OpSpecConstantCompositeStructMemberUndefTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeInt 64 0 +%3 = OpTypeStruct %1 %1 %2 +%4 = OpSpecConstant %1 42 +%5 = OpUndef %2 +%6 = OpSpecConstantComposite %3 %4 %5 %4)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSpecConstantComposite Constituent '5[%5]' type " + "does not match the Result Type '3[%_struct_3]'s " + "member type.")); +} + +// TODO: OpSpecConstantOp + +TEST_F(ValidateIdWithMessage, OpVariableGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypePointer Input %1 +%3 = OpVariable %2 Input)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpVariableInitializerConstantGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypePointer Input %1 +%3 = OpConstant %1 42 +%4 = OpVariable %2 Input %3)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpVariableInitializerGlobalVariableGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypePointer Uniform %1 +%3 = OpVariable %2 Uniform +%4 = OpTypePointer Private %2 ; pointer to pointer +%5 = OpVariable %4 Private %3 +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +// TODO: Positive test OpVariable with OpConstantNull of OpTypePointer +TEST_F(ValidateIdWithMessage, OpVariableResultTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpVariable %1 Input)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpVariable Result Type '1[%uint]' is not a pointer " + "type.")); +} +TEST_F(ValidateIdWithMessage, OpVariableInitializerIsTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypePointer Input %1 +%3 = OpVariable %2 Input %2)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 2[%_ptr_Input_uint] " + "cannot be a type")); +} + +TEST_F(ValidateIdWithMessage, OpVariableInitializerIsFunctionVarBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%int = OpTypeInt 32 0 +%ptrint = OpTypePointer Function %int +%ptrptrint = OpTypePointer Function %ptrint +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%var = OpVariable %ptrint Function +%varinit = OpVariable %ptrptrint Function %var ; Can't initialize function variable. +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpVariable Initializer '8[%8]' is not a constant " + "or module-scope variable")); +} + +TEST_F(ValidateIdWithMessage, OpVariableInitializerIsModuleVarGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%int = OpTypeInt 32 0 +%ptrint = OpTypePointer Uniform %int +%mvar = OpVariable %ptrint Uniform +%ptrptrint = OpTypePointer Function %ptrint +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%goodvar = OpVariable %ptrptrint Function %mvar ; This is ok +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpVariableContainsBoolBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%block = OpTypeStruct %bool %int +%_ptr_Uniform_block = OpTypePointer Uniform %block +%var = OpVariable %_ptr_Uniform_block Uniform +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%load = OpLoad %block %var +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("If OpTypeBool is stored in conjunction with OpVariable" + ", it can only be used with non-externally visible " + "shader Storage Classes: Workgroup, CrossWorkgroup, " + "Private, and Function")); +} + +TEST_F(ValidateIdWithMessage, OpVariableContainsBoolPointerGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%bool = OpTypeBool +%boolptr = OpTypePointer Uniform %bool +%int = OpTypeInt 32 0 +%block = OpTypeStruct %boolptr %int +%_ptr_Uniform_block = OpTypePointer Uniform %block +%var = OpVariable %_ptr_Uniform_block Uniform +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%load = OpLoad %block %var +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpVariableContainsBuiltinBoolGood) { + std::string spirv = kGLSL450MemoryModel + R"( +OpMemberDecorate %input 0 BuiltIn FrontFacing +%bool = OpTypeBool +%input = OpTypeStruct %bool +%_ptr_input = OpTypePointer Input %input +%var = OpVariable %_ptr_input Input +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%load = OpLoad %input %var +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpVariableContainsRayPayloadBoolGood) { + std::string spirv = R"( +OpCapability RayTracingNV +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_NV_ray_tracing" +OpMemoryModel Logical GLSL450 +%bool = OpTypeBool +%PerRayData = OpTypeStruct %bool +%_ptr_PerRayData = OpTypePointer RayPayloadNV %PerRayData +%var = OpVariable %_ptr_PerRayData RayPayloadNV +%void = OpTypeVoid +%fnty = OpTypeFunction %void +%main = OpFunction %void None %fnty +%entry = OpLabel +%load = OpLoad %PerRayData %var +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpVariablePointerNoVariablePointersBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%_ptr_workgroup_int = OpTypePointer Workgroup %int +%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%var = OpVariable %_ptr_function_ptr Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "In Logical addressing, variables may not allocate a pointer type")); +} + +TEST_F(ValidateIdWithMessage, + OpVariablePointerNoVariablePointersRelaxedLogicalGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%_ptr_workgroup_int = OpTypePointer Workgroup %int +%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%var = OpVariable %_ptr_function_ptr Function +OpReturn +OpFunctionEnd +)"; + + auto options = getValidatorOptions(); + options->relax_logical_pointer = true; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpFunctionWithNonMemoryObject) { + // DXC generates code that looks like when given something like: + // T t; + // t.s.fn_1(); + // This needs to be accepted before legalization takes place, so we + // will include it with the relaxed logical pointer. + + const std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %1 "main" + OpSource HLSL 600 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %void = OpTypeVoid + %9 = OpTypeFunction %void + %_struct_5 = OpTypeStruct + %_struct_6 = OpTypeStruct %_struct_5 +%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6 +%_ptr_Function__struct_5 = OpTypePointer Function %_struct_5 + %23 = OpTypeFunction %void %_ptr_Function__struct_5 + %1 = OpFunction %void None %9 + %10 = OpLabel + %11 = OpVariable %_ptr_Function__struct_6 Function + %20 = OpAccessChain %_ptr_Function__struct_5 %11 %int_0 + %21 = OpFunctionCall %void %12 %20 + OpReturn + OpFunctionEnd + %12 = OpFunction %void None %23 + %13 = OpFunctionParameter %_ptr_Function__struct_5 + %14 = OpLabel + OpReturn + OpFunctionEnd +)"; + + auto options = getValidatorOptions(); + options->relax_logical_pointer = true; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, + OpVariablePointerVariablePointersStorageBufferGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%_ptr_workgroup_int = OpTypePointer Workgroup %int +%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%var = OpVariable %_ptr_function_ptr Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpVariablePointerVariablePointersGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%_ptr_workgroup_int = OpTypePointer Workgroup %int +%_ptr_function_ptr = OpTypePointer Function %_ptr_workgroup_int +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%var = OpVariable %_ptr_function_ptr Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpVariablePointerVariablePointersBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%_ptr_workgroup_int = OpTypePointer Workgroup %int +%_ptr_uniform_ptr = OpTypePointer Uniform %_ptr_workgroup_int +%var = OpVariable %_ptr_uniform_ptr Uniform +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In Logical addressing with variable pointers, " + "variables that allocate pointers must be in Function " + "or Private storage classes")); +} + +TEST_F(ValidateIdWithMessage, OpLoadGood) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeVoid + %2 = OpTypeInt 32 0 + %3 = OpTypePointer UniformConstant %2 + %4 = OpTypeFunction %1 + %5 = OpVariable %3 UniformConstant + %6 = OpFunction %1 None %4 + %7 = OpLabel + %8 = OpLoad %2 %5 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// TODO: Add tests that exercise VariablePointersStorageBuffer instead of +// VariablePointers. +void createVariablePointerSpirvProgram(std::ostringstream* spirv, + std::string result_strategy, + bool use_varptr_cap, + bool add_helper_function) { + *spirv << "OpCapability Shader "; + if (use_varptr_cap) { + *spirv << "OpCapability VariablePointers "; + *spirv << "OpExtension \"SPV_KHR_variable_pointers\" "; + } + *spirv << "OpExtension \"SPV_KHR_storage_buffer_storage_class\" "; + *spirv << R"( + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" + %void = OpTypeVoid + %voidf = OpTypeFunction %void + %bool = OpTypeBool + %i32 = OpTypeInt 32 1 + %f32 = OpTypeFloat 32 + %f32ptr = OpTypePointer StorageBuffer %f32 + %i = OpConstant %i32 1 + %zero = OpConstant %i32 0 + %float_1 = OpConstant %f32 1.0 + %ptr1 = OpVariable %f32ptr StorageBuffer + %ptr2 = OpVariable %f32ptr StorageBuffer + )"; + if (add_helper_function) { + *spirv << R"( + ; //////////////////////////////////////////////////////////// + ;;;; Function that returns a pointer + ; //////////////////////////////////////////////////////////// + %selector_func_type = OpTypeFunction %f32ptr %bool %f32ptr %f32ptr + %choose_input_func = OpFunction %f32ptr None %selector_func_type + %is_neg_param = OpFunctionParameter %bool + %first_ptr_param = OpFunctionParameter %f32ptr + %second_ptr_param = OpFunctionParameter %f32ptr + %selector_func_begin = OpLabel + %result_ptr = OpSelect %f32ptr %is_neg_param %first_ptr_param %second_ptr_param + OpReturnValue %result_ptr + OpFunctionEnd + )"; + } + *spirv << R"( + %main = OpFunction %void None %voidf + %label = OpLabel + )"; + *spirv << result_strategy; + *spirv << R"( + OpReturn + OpFunctionEnd + )"; +} + +// With the VariablePointer Capability, OpLoad should allow loading a +// VaiablePointer. In this test the variable pointer is obtained by an OpSelect +TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpSelectGood) { + std::string result_strategy = R"( + %isneg = OpSLessThan %bool %i %zero + %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 + %result = OpLoad %f32 %varptr + )"; + + std::ostringstream spirv; + createVariablePointerSpirvProgram(&spirv, result_strategy, + true /* Add VariablePointers Capability? */, + false /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Without the VariablePointers Capability, OpLoad will not allow loading +// through a variable pointer. +// Disabled since using OpSelect with pointers without VariablePointers will +// fail LogicalsPass. +TEST_F(ValidateIdWithMessage, DISABLED_OpLoadVarPtrOpSelectBad) { + std::string result_strategy = R"( + %isneg = OpSLessThan %bool %i %zero + %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 + %result = OpLoad %f32 %varptr + )"; + + std::ostringstream spirv; + createVariablePointerSpirvProgram(&spirv, result_strategy, + false /* Add VariablePointers Capability?*/, + false /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a logical pointer.")); +} + +// With the VariablePointer Capability, OpLoad should allow loading a +// VaiablePointer. In this test the variable pointer is obtained by an OpPhi +TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpPhiGood) { + std::string result_strategy = R"( + %is_neg = OpSLessThan %bool %i %zero + OpSelectionMerge %end_label None + OpBranchConditional %is_neg %take_ptr_1 %take_ptr_2 + %take_ptr_1 = OpLabel + OpBranch %end_label + %take_ptr_2 = OpLabel + OpBranch %end_label + %end_label = OpLabel + %varptr = OpPhi %f32ptr %ptr1 %take_ptr_1 %ptr2 %take_ptr_2 + %result = OpLoad %f32 %varptr + )"; + + std::ostringstream spirv; + createVariablePointerSpirvProgram(&spirv, result_strategy, + true /* Add VariablePointers Capability?*/, + false /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Without the VariablePointers Capability, OpPhi can have a pointer result +// type. +TEST_F(ValidateIdWithMessage, OpPhiBad) { + std::string result_strategy = R"( + %is_neg = OpSLessThan %bool %i %zero + OpSelectionMerge %end_label None + OpBranchConditional %is_neg %take_ptr_1 %take_ptr_2 + %take_ptr_1 = OpLabel + OpBranch %end_label + %take_ptr_2 = OpLabel + OpBranch %end_label + %end_label = OpLabel + %varptr = OpPhi %f32ptr %ptr1 %take_ptr_1 %ptr2 %take_ptr_2 + %result = OpLoad %f32 %varptr + )"; + + std::ostringstream spirv; + createVariablePointerSpirvProgram(&spirv, result_strategy, + false /* Add VariablePointers Capability?*/, + false /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Using pointers with OpPhi requires capability " + "VariablePointers or VariablePointersStorageBuffer")); +} + +// With the VariablePointer Capability, OpLoad should allow loading through a +// VaiablePointer. In this test the variable pointer is obtained from an +// OpFunctionCall (return value from a function) +TEST_F(ValidateIdWithMessage, OpLoadVarPtrOpFunctionCallGood) { + std::ostringstream spirv; + std::string result_strategy = R"( + %isneg = OpSLessThan %bool %i %zero + %varptr = OpFunctionCall %f32ptr %choose_input_func %isneg %ptr1 %ptr2 + %result = OpLoad %f32 %varptr + )"; + + createVariablePointerSpirvProgram(&spirv, result_strategy, + true /* Add VariablePointers Capability?*/, + true /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpLoadResultTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer UniformConstant %2 +%4 = OpTypeFunction %1 +%5 = OpVariable %3 UniformConstant +%6 = OpFunction %1 None %4 +%7 = OpLabel +%8 = OpLoad %3 %5 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpLoad Result Type " + "'3[%_ptr_UniformConstant_uint]' does not match " + "Pointer '5[%5]'s type.")); +} + +TEST_F(ValidateIdWithMessage, OpLoadPointerBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer UniformConstant %2 +%4 = OpTypeFunction %1 +%5 = OpFunction %1 None %4 +%6 = OpLabel +%7 = OpLoad %2 %8 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + // Prove that SSA checks trigger for a bad Id value. + // The next test case show the not-a-logical-pointer case. + EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 8[%8] has not been " + "defined")); +} + +// Disabled as bitcasting type to object is now not valid. +TEST_F(ValidateIdWithMessage, DISABLED_OpLoadLogicalPointerBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFloat 32 +%4 = OpTypePointer UniformConstant %2 +%5 = OpTypePointer UniformConstant %3 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +%9 = OpBitcast %5 %4 ; Not valid in logical addressing +%10 = OpLoad %3 %9 ; Should trigger message + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + // Once we start checking bitcasts, we might catch that + // as the error first, instead of catching it here. + // I don't know if it's possible to generate a bad case + // if/when the validator is complete. + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpLoad Pointer '9' is not a logical pointer.")); +} + +TEST_F(ValidateIdWithMessage, OpStoreGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %2 42 +%6 = OpVariable %3 Uniform +%7 = OpFunction %1 None %4 +%8 = OpLabel + OpStore %6 %5 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpStorePointerBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer UniformConstant %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %2 42 +%6 = OpVariable %3 UniformConstant +%7 = OpConstant %2 0 +%8 = OpFunction %1 None %4 +%9 = OpLabel + OpStore %7 %5 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpStore Pointer '7[%uint_0]' is not a logical " + "pointer.")); +} + +// Disabled as bitcasting type to object is now not valid. +TEST_F(ValidateIdWithMessage, DISABLED_OpStoreLogicalPointerBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFloat 32 +%4 = OpTypePointer UniformConstant %2 +%5 = OpTypePointer UniformConstant %3 +%6 = OpTypeFunction %1 +%7 = OpConstantNull %5 +%8 = OpFunction %1 None %6 +%9 = OpLabel +%10 = OpBitcast %5 %4 ; Not valid in logical addressing +%11 = OpStore %10 %7 ; Should trigger message + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpStore Pointer '10' is not a logical pointer.")); +} + +// Without the VariablePointer Capability, OpStore should may not store +// through a variable pointer. +// Disabled since using OpSelect with pointers without VariablePointers will +// fail LogicalsPass. +TEST_F(ValidateIdWithMessage, DISABLED_OpStoreVarPtrBad) { + std::string result_strategy = R"( + %isneg = OpSLessThan %bool %i %zero + %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 + OpStore %varptr %float_1 + )"; + + std::ostringstream spirv; + createVariablePointerSpirvProgram( + &spirv, result_strategy, false /* Add VariablePointers Capability? */, + false /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("is not a logical pointer.")); +} + +// With the VariablePointer Capability, OpStore should allow storing through a +// variable pointer. +TEST_F(ValidateIdWithMessage, OpStoreVarPtrGood) { + std::string result_strategy = R"( + %isneg = OpSLessThan %bool %i %zero + %varptr = OpSelect %f32ptr %isneg %ptr1 %ptr2 + OpStore %varptr %float_1 + )"; + + std::ostringstream spirv; + createVariablePointerSpirvProgram(&spirv, result_strategy, + true /* Add VariablePointers Capability? */, + false /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpStoreObjectGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %2 42 +%6 = OpVariable %3 Uniform +%7 = OpFunction %1 None %4 +%8 = OpLabel +%9 = OpUndef %1 + OpStore %6 %9 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpStore Object '9[%9]'s type is void.")); +} +TEST_F(ValidateIdWithMessage, OpStoreTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%9 = OpTypeFloat 32 +%3 = OpTypePointer Uniform %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %9 3.14 +%6 = OpVariable %3 Uniform +%7 = OpFunction %1 None %4 +%8 = OpLabel + OpStore %6 %5 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpStore Pointer '7[%7]'s type does not match " + "Object '6[%float_3_1400001]'s type.")); +} + +// The next series of test check test a relaxation of the rules for stores to +// structs. The first test checks that we get a failure when the option is not +// set to relax the rule. +// TODO: Add tests for layout compatible arrays and matricies when the validator +// relaxes the rules for them as well. Also need test to check for layout +// decorations specific to those types. +TEST_F(ValidateIdWithMessage, OpStoreTypeBadStruct) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %1 0 Offset 0 + OpMemberDecorate %1 1 Offset 4 + OpMemberDecorate %2 0 Offset 0 + OpMemberDecorate %2 1 Offset 4 +%3 = OpTypeVoid +%4 = OpTypeFloat 32 +%1 = OpTypeStruct %4 %4 +%5 = OpTypePointer Uniform %1 +%2 = OpTypeStruct %4 %4 +%6 = OpTypeFunction %3 +%7 = OpConstant %4 3.14 +%8 = OpVariable %5 Uniform +%9 = OpFunction %3 None %6 +%10 = OpLabel +%11 = OpCompositeConstruct %2 %7 %7 + OpStore %8 %11 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpStore Pointer '8[%8]'s type does not match " + "Object '11[%11]'s type.")); +} + +// Same code as the last test. The difference is that we relax the rule. +// Because the structs %3 and %5 are defined the same way. +TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStruct) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %1 0 Offset 0 + OpMemberDecorate %1 1 Offset 4 + OpMemberDecorate %2 0 Offset 0 + OpMemberDecorate %2 1 Offset 4 +%3 = OpTypeVoid +%4 = OpTypeFloat 32 +%1 = OpTypeStruct %4 %4 +%5 = OpTypePointer Uniform %1 +%2 = OpTypeStruct %4 %4 +%6 = OpTypeFunction %3 +%7 = OpConstant %4 3.14 +%8 = OpVariable %5 Uniform +%9 = OpFunction %3 None %6 +%10 = OpLabel +%11 = OpCompositeConstruct %2 %7 %7 + OpStore %8 %11 + OpReturn + OpFunctionEnd)"; + spvValidatorOptionsSetRelaxStoreStruct(options_, true); + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Same code as the last test excect for an extra decoration on one of the +// members. With the relaxed rules, the code is still valid. +TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedStructWithExtraDecoration) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %1 0 Offset 0 + OpMemberDecorate %1 1 Offset 4 + OpMemberDecorate %1 0 RelaxedPrecision + OpMemberDecorate %2 0 Offset 0 + OpMemberDecorate %2 1 Offset 4 +%3 = OpTypeVoid +%4 = OpTypeFloat 32 +%1 = OpTypeStruct %4 %4 +%5 = OpTypePointer Uniform %1 +%2 = OpTypeStruct %4 %4 +%6 = OpTypeFunction %3 +%7 = OpConstant %4 3.14 +%8 = OpVariable %5 Uniform +%9 = OpFunction %3 None %6 +%10 = OpLabel +%11 = OpCompositeConstruct %2 %7 %7 + OpStore %8 %11 + OpReturn + OpFunctionEnd)"; + spvValidatorOptionsSetRelaxStoreStruct(options_, true); + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// This test check that we recursively traverse the struct to check if they are +// interchangable. +TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedNestedStruct) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %1 0 Offset 0 + OpMemberDecorate %1 1 Offset 4 + OpMemberDecorate %2 0 Offset 0 + OpMemberDecorate %2 1 Offset 8 + OpMemberDecorate %3 0 Offset 0 + OpMemberDecorate %3 1 Offset 4 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 8 +%5 = OpTypeVoid +%6 = OpTypeInt 32 0 +%7 = OpTypeFloat 32 +%1 = OpTypeStruct %7 %6 +%2 = OpTypeStruct %1 %1 +%8 = OpTypePointer Uniform %2 +%3 = OpTypeStruct %7 %6 +%4 = OpTypeStruct %3 %3 +%9 = OpTypeFunction %5 +%10 = OpConstant %6 7 +%11 = OpConstant %7 3.14 +%12 = OpConstantComposite %3 %11 %10 +%13 = OpVariable %8 Uniform +%14 = OpFunction %5 None %9 +%15 = OpLabel +%16 = OpCompositeConstruct %4 %12 %12 + OpStore %13 %16 + OpReturn + OpFunctionEnd)"; + spvValidatorOptionsSetRelaxStoreStruct(options_, true); + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// This test check that the even with the relaxed rules an error is identified +// if the members of the struct are in a different order. +TEST_F(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct1) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %1 0 Offset 0 + OpMemberDecorate %1 1 Offset 4 + OpMemberDecorate %2 0 Offset 0 + OpMemberDecorate %2 1 Offset 8 + OpMemberDecorate %3 0 Offset 0 + OpMemberDecorate %3 1 Offset 4 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 8 +%5 = OpTypeVoid +%6 = OpTypeInt 32 0 +%7 = OpTypeFloat 32 +%1 = OpTypeStruct %6 %7 +%2 = OpTypeStruct %1 %1 +%8 = OpTypePointer Uniform %2 +%3 = OpTypeStruct %7 %6 +%4 = OpTypeStruct %3 %3 +%9 = OpTypeFunction %5 +%10 = OpConstant %6 7 +%11 = OpConstant %7 3.14 +%12 = OpConstantComposite %3 %11 %10 +%13 = OpVariable %8 Uniform +%14 = OpFunction %5 None %9 +%15 = OpLabel +%16 = OpCompositeConstruct %4 %12 %12 + OpStore %13 %16 + OpReturn + OpFunctionEnd)"; + spvValidatorOptionsSetRelaxStoreStruct(options_, true); + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpStore Pointer '13[%13]'s layout does not match Object " + " '16[%16]'s layout.")); +} + +// This test check that the even with the relaxed rules an error is identified +// if the members of the struct are at different offsets. +TEST_F(ValidateIdWithMessage, OpStoreTypeBadRelaxedStruct2) { + std::string spirv = kGLSL450MemoryModel + R"( + OpMemberDecorate %1 0 Offset 4 + OpMemberDecorate %1 1 Offset 0 + OpMemberDecorate %2 0 Offset 0 + OpMemberDecorate %2 1 Offset 8 + OpMemberDecorate %3 0 Offset 0 + OpMemberDecorate %3 1 Offset 4 + OpMemberDecorate %4 0 Offset 0 + OpMemberDecorate %4 1 Offset 8 +%5 = OpTypeVoid +%6 = OpTypeInt 32 0 +%7 = OpTypeFloat 32 +%1 = OpTypeStruct %7 %6 +%2 = OpTypeStruct %1 %1 +%8 = OpTypePointer Uniform %2 +%3 = OpTypeStruct %7 %6 +%4 = OpTypeStruct %3 %3 +%9 = OpTypeFunction %5 +%10 = OpConstant %6 7 +%11 = OpConstant %7 3.14 +%12 = OpConstantComposite %3 %11 %10 +%13 = OpVariable %8 Uniform +%14 = OpFunction %5 None %9 +%15 = OpLabel +%16 = OpCompositeConstruct %4 %12 %12 + OpStore %13 %16 + OpReturn + OpFunctionEnd)"; + spvValidatorOptionsSetRelaxStoreStruct(options_, true); + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpStore Pointer '13[%13]'s layout does not match Object " + " '16[%16]'s layout.")); +} + +TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerReturnPointer) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 1 +%2 = OpTypePointer Function %1 +%3 = OpTypeFunction %2 %2 +%4 = OpFunction %2 None %3 +%5 = OpFunctionParameter %2 +%6 = OpLabel + OpReturnValue %5 + OpFunctionEnd)"; + + spvValidatorOptionsSetRelaxLogicalPointer(options_, true); + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpStoreTypeRelaxedLogicalPointerAllocPointer) { + const std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + %1 = OpTypeVoid + %2 = OpTypeInt 32 1 + %3 = OpTypeFunction %1 ; void(void) + %4 = OpTypePointer Uniform %2 ; int* + %5 = OpTypePointer Private %4 ; int** (Private) + %6 = OpTypePointer Function %4 ; int** (Function) + %7 = OpVariable %5 Private + %8 = OpFunction %1 None %3 + %9 = OpLabel +%10 = OpVariable %6 Function + OpReturn + OpFunctionEnd)"; + + spvValidatorOptionsSetRelaxLogicalPointer(options_, true); + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpStoreVoid) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpTypeFunction %1 +%6 = OpVariable %3 Uniform +%7 = OpFunction %1 None %4 +%8 = OpLabel +%9 = OpFunctionCall %1 %7 + OpStore %6 %9 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpStore Object '8[%8]'s type is void.")); +} + +TEST_F(ValidateIdWithMessage, OpStoreLabel) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpTypeFunction %1 +%6 = OpVariable %3 Uniform +%7 = OpFunction %1 None %4 +%8 = OpLabel + OpStore %6 %8 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 7[%7] requires a type")); +} + +// TODO: enable when this bug is fixed: +// https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404 +TEST_F(ValidateIdWithMessage, DISABLED_OpStoreFunction) { + std::string spirv = kGLSL450MemoryModel + R"( +%2 = OpTypeInt 32 0 +%3 = OpTypePointer UniformConstant %2 +%4 = OpTypeFunction %2 +%5 = OpConstant %2 123 +%6 = OpVariable %3 UniformConstant +%7 = OpFunction %2 None %4 +%8 = OpLabel + OpStore %6 %7 + OpReturnValue %5 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpStoreBuiltin) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID + OpExecutionMode %main LocalSize 1 1 1 + OpSource GLSL 450 + OpName %main "main" + + OpName %gl_GlobalInvocationID "gl_GlobalInvocationID" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + + %int = OpTypeInt 32 1 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input + + %zero = OpConstant %uint 0 + %v3uint_000 = OpConstantComposite %v3uint %zero %zero %zero + + %void = OpTypeVoid + %voidfunc = OpTypeFunction %void + %main = OpFunction %void None %voidfunc + %lmain = OpLabel + + OpStore %gl_GlobalInvocationID %v3uint_000 + + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("storage class is read-only")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemoryGood) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeVoid + %2 = OpTypeInt 32 0 + %3 = OpTypePointer UniformConstant %2 + %4 = OpConstant %2 42 + %5 = OpVariable %3 UniformConstant %4 + %6 = OpTypePointer Function %2 + %7 = OpTypeFunction %1 + %8 = OpFunction %1 None %7 + %9 = OpLabel +%10 = OpVariable %6 Function + OpCopyMemory %10 %5 None + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemoryNonPointerTarget) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpTypeFunction %1 %2 %3 +%5 = OpFunction %1 None %4 +%6 = OpFunctionParameter %2 +%7 = OpFunctionParameter %3 +%8 = OpLabel +OpCopyMemory %6 %7 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target operand '6[%6]' is not a pointer.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemoryNonPointerSource) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpTypeFunction %1 %2 %3 +%5 = OpFunction %1 None %4 +%6 = OpFunctionParameter %2 +%7 = OpFunctionParameter %3 +%8 = OpLabel +OpCopyMemory %7 %6 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Source operand '6[%6]' is not a pointer.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemoryBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeVoid + %2 = OpTypeInt 32 0 + %3 = OpTypePointer UniformConstant %2 + %4 = OpConstant %2 42 + %5 = OpVariable %3 UniformConstant %4 +%11 = OpTypeFloat 32 + %6 = OpTypePointer Function %11 + %7 = OpTypeFunction %1 + %8 = OpFunction %1 None %7 + %9 = OpLabel +%10 = OpVariable %6 Function + OpCopyMemory %10 %5 None + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target '5[%5]'s type does not match " + "Source '2[%uint]'s type.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemoryVoidTarget) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %1 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFunction %1 %3 %4 +%6 = OpFunction %1 None %5 +%7 = OpFunctionParameter %3 +%8 = OpFunctionParameter %4 +%9 = OpLabel +OpCopyMemory %7 %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target operand '7[%7]' cannot be a void " + "pointer.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemoryVoidSource) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %1 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFunction %1 %3 %4 +%6 = OpFunction %1 None %5 +%7 = OpFunctionParameter %3 +%8 = OpFunctionParameter %4 +%9 = OpLabel +OpCopyMemory %8 %7 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Source operand '7[%7]' cannot be a void " + "pointer.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedGood) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeVoid + %2 = OpTypeInt 32 0 + %3 = OpTypePointer UniformConstant %2 + %4 = OpTypePointer Function %2 + %5 = OpConstant %2 4 + %6 = OpVariable %3 UniformConstant %5 + %7 = OpTypeFunction %1 + %8 = OpFunction %1 None %7 + %9 = OpLabel +%10 = OpVariable %4 Function + OpCopyMemorySized %10 %6 %5 None + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedTargetBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer UniformConstant %2 +%4 = OpTypePointer Function %2 +%5 = OpConstant %2 4 +%6 = OpVariable %3 UniformConstant %5 +%7 = OpTypeFunction %1 +%8 = OpFunction %1 None %7 +%9 = OpLabel + OpCopyMemorySized %5 %5 %5 None + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Target operand '5[%uint_4]' is not a pointer.")); +} +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSourceBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer UniformConstant %2 +%4 = OpTypePointer Function %2 +%5 = OpConstant %2 4 +%6 = OpTypeFunction %1 +%7 = OpFunction %1 None %6 +%8 = OpLabel +%9 = OpVariable %4 Function + OpCopyMemorySized %9 %5 %5 None + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Source operand '5[%uint_4]' is not a pointer.")); +} +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeVoid + %2 = OpTypeInt 32 0 + %3 = OpTypePointer UniformConstant %2 + %4 = OpTypePointer Function %2 + %5 = OpConstant %2 4 + %6 = OpVariable %3 UniformConstant %5 + %7 = OpTypeFunction %1 + %8 = OpFunction %1 None %7 + %9 = OpLabel +%10 = OpVariable %4 Function + OpCopyMemorySized %10 %6 %6 None + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Size operand '6[%6]' must be a scalar integer type.")); +} +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeVoid + %2 = OpTypeInt 32 0 + %3 = OpTypePointer UniformConstant %2 + %4 = OpTypePointer Function %2 + %5 = OpConstant %2 4 + %6 = OpVariable %3 UniformConstant %5 + %7 = OpTypeFunction %1 +%11 = OpTypeFloat 32 +%12 = OpConstant %11 1.0 + %8 = OpFunction %1 None %7 + %9 = OpLabel +%10 = OpVariable %4 Function + OpCopyMemorySized %10 %6 %12 None + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Size operand '9[%float_1]' must be a scalar integer " + "type.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNull) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstantNull %2 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFloat 32 +%6 = OpTypePointer UniformConstant %5 +%7 = OpTypeFunction %1 %4 %6 +%8 = OpFunction %1 None %7 +%9 = OpFunctionParameter %4 +%10 = OpFunctionParameter %6 +%11 = OpLabel +OpCopyMemorySized %9 %10 %3 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Size operand '3[%3]' cannot be a constant " + "zero.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 0 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFloat 32 +%6 = OpTypePointer UniformConstant %5 +%7 = OpTypeFunction %1 %4 %6 +%8 = OpFunction %1 None %7 +%9 = OpFunctionParameter %4 +%10 = OpFunctionParameter %6 +%11 = OpLabel +OpCopyMemorySized %9 %10 %3 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Size operand '3[%uint_0]' cannot be a constant " + "zero.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantZero64) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 64 0 +%3 = OpConstant %2 0 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFloat 32 +%6 = OpTypePointer UniformConstant %5 +%7 = OpTypeFunction %1 %4 %6 +%8 = OpFunction %1 None %7 +%9 = OpFunctionParameter %4 +%10 = OpFunctionParameter %6 +%11 = OpLabel +OpCopyMemorySized %9 %10 %3 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Size operand '3[%ulong_0]' cannot be a constant " + "zero.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 1 +%3 = OpConstant %2 -1 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFloat 32 +%6 = OpTypePointer UniformConstant %5 +%7 = OpTypeFunction %1 %4 %6 +%8 = OpFunction %1 None %7 +%9 = OpFunctionParameter %4 +%10 = OpFunctionParameter %6 +%11 = OpLabel +OpCopyMemorySized %9 %10 %3 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Size operand '3[%int_n1]' cannot have the sign bit set " + "to 1.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeConstantNegative64) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 64 1 +%3 = OpConstant %2 -1 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFloat 32 +%6 = OpTypePointer UniformConstant %5 +%7 = OpTypeFunction %1 %4 %6 +%8 = OpFunction %1 None %7 +%9 = OpFunctionParameter %4 +%10 = OpFunctionParameter %6 +%11 = OpLabel +OpCopyMemorySized %9 %10 %3 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Size operand '3[%long_n1]' cannot have the sign bit set " + "to 1.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 2147483648 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFloat 32 +%6 = OpTypePointer UniformConstant %5 +%7 = OpTypeFunction %1 %4 %6 +%8 = OpFunction %1 None %7 +%9 = OpFunctionParameter %4 +%10 = OpFunctionParameter %6 +%11 = OpLabel +OpCopyMemorySized %9 %10 %3 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpCopyMemorySizedSizeUnsignedNegative64) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 64 0 +%3 = OpConstant %2 9223372036854775808 +%4 = OpTypePointer Uniform %2 +%5 = OpTypeFloat 32 +%6 = OpTypePointer UniformConstant %5 +%7 = OpTypeFunction %1 %4 %6 +%8 = OpFunction %1 None %7 +%9 = OpFunctionParameter %4 +%10 = OpFunctionParameter %6 +%11 = OpLabel +OpCopyMemorySized %9 %10 %3 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +const char kDeeplyNestedStructureSetup[] = R"( +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%int = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%mat4x3 = OpTypeMatrix %v3float 4 +%_ptr_Private_mat4x3 = OpTypePointer Private %mat4x3 +%_ptr_Private_float = OpTypePointer Private %float +%my_matrix = OpVariable %_ptr_Private_mat4x3 Private +%my_float_var = OpVariable %_ptr_Private_float Private +%_ptr_Function_float = OpTypePointer Function %float +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_3 = OpConstant %int 3 +%int_5 = OpConstant %int 5 + +; Making the following nested structures. +; +; struct S { +; bool b; +; vec4 v[5]; +; int i; +; mat4x3 m[5]; +; } +; uniform blockName { +; S s; +; bool cond; +; RunTimeArray arr; +; } + +%f32arr = OpTypeRuntimeArray %float +%v4float = OpTypeVector %float 4 +%array5_mat4x3 = OpTypeArray %mat4x3 %int_5 +%array5_vec4 = OpTypeArray %v4float %int_5 +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Function_vec4 = OpTypePointer Function %v4float +%_ptr_Uniform_vec4 = OpTypePointer Uniform %v4float +%struct_s = OpTypeStruct %int %array5_vec4 %int %array5_mat4x3 +%struct_blockName = OpTypeStruct %struct_s %int %f32arr +%_ptr_Uniform_blockName = OpTypePointer Uniform %struct_blockName +%_ptr_Uniform_struct_s = OpTypePointer Uniform %struct_s +%_ptr_Uniform_array5_mat4x3 = OpTypePointer Uniform %array5_mat4x3 +%_ptr_Uniform_mat4x3 = OpTypePointer Uniform %mat4x3 +%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float +%blockName_var = OpVariable %_ptr_Uniform_blockName Uniform +%spec_int = OpSpecConstant %int 2 +%float_0 = OpConstant %float 0 +%func = OpFunction %void None %void_f +%my_label = OpLabel +)"; + +// In what follows, Access Chain Instruction refers to one of the following: +// OpAccessChain, OpInBoundsAccessChain, OpPtrAccessChain, and +// OpInBoundsPtrAccessChain +using AccessChainInstructionTest = spvtest::ValidateBase; + +// Determines whether the access chain instruction requires the 'element id' +// argument. +bool AccessChainRequiresElemId(const std::string& instr) { + return (instr == "OpPtrAccessChain" || instr == "OpInBoundsPtrAccessChain"); +} + +// Valid: Access a float in a matrix using an access chain instruction. +TEST_P(AccessChainInstructionTest, AccessChainGood) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + + "%float_entry = " + instr + + R"( %_ptr_Private_float %my_matrix )" + elem + + R"(%int_0 %int_1 + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid. The result type of an access chain instruction must be a pointer. +TEST_P(AccessChainInstructionTest, AccessChainResultTypeBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%float_entry = )" + + instr + + R"( %float %my_matrix )" + elem + + R"(%int_0 %int_1 +OpReturn +OpFunctionEnd + )"; + + const std::string expected_err = "The Result Type of " + instr + + " '36[%36]' must be " + "OpTypePointer. Found OpTypeFloat."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid. The base type of an access chain instruction must be a pointer. +TEST_P(AccessChainInstructionTest, AccessChainBaseTypeVoidBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%float_entry = )" + + instr + " %_ptr_Private_float %void " + elem + + R"(%int_0 %int_1 +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%void] cannot be a " + "type")); +} + +// Invalid. The base type of an access chain instruction must be a pointer. +TEST_P(AccessChainInstructionTest, AccessChainBaseTypeNonPtrVariableBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Private_float %_ptr_Private_float )" + + elem + + R"(%int_0 %int_1 +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 8[%_ptr_Private_float] cannot be a type")); +} + +// Invalid: The storage class of Base and Result do not match. +TEST_P(AccessChainInstructionTest, + AccessChainResultAndBaseStorageClassDoesntMatchBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Function_float %my_matrix )" + elem + + R"(%int_0 %int_1 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = + "The result pointer storage class and base pointer storage class in " + + instr + " do not match."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid. The base type of an access chain instruction must point to a +// composite object. +TEST_P(AccessChainInstructionTest, + AccessChainBasePtrNotPointingToCompositeBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Private_float %my_float_var )" + elem + + R"(%int_0 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = instr + + " reached non-composite type while " + "indexes still remain to be traversed."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Valid. No Indexes were passed to the access chain instruction. The Result +// Type is the same as the Base type. +TEST_P(AccessChainInstructionTest, AccessChainNoIndexesGood) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Private_float %my_float_var )" + elem + + R"( +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid. No Indexes were passed to the access chain instruction, but the +// Result Type is different from the Base type. +TEST_P(AccessChainInstructionTest, AccessChainNoIndexesBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Private_mat4x3 %my_float_var )" + elem + + R"( +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("result type (OpTypeMatrix) does not match the type that " + "results from indexing into the base (OpTypeFloat).")); +} + +// Valid: 255 indexes passed to the access chain instruction. Limit is 255. +TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesGood) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : ""; + int depth = 255; + std::string header = kGLSL450MemoryModel + kDeeplyNestedStructureSetup; + header.erase(header.find("%func")); + std::ostringstream spirv; + spirv << header << "\n"; + + // Build nested structures. Struct 'i' contains struct 'i-1' + spirv << "%s_depth_1 = OpTypeStruct %float\n"; + for (int i = 2; i <= depth; ++i) { + spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n"; + } + + // Define Pointer and Variable to use for the AccessChain instruction. + spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_" + << depth << "\n"; + spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n"; + + // Function Start + spirv << R"( + %func = OpFunction %void None %void_f + %my_label = OpLabel + )"; + + // AccessChain with 'n' indexes (n = depth) + spirv << "%entry = " << instr << " %_ptr_Uniform_float %deep_var" << elem; + for (int i = 0; i < depth; ++i) { + spirv << " %int_0"; + } + + // Function end + spirv << R"( + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: 256 indexes passed to the access chain instruction. Limit is 255. +TEST_P(AccessChainInstructionTest, AccessChainTooManyIndexesBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : ""; + std::ostringstream spirv; + spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup; + spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem; + for (int i = 0; i < 256; ++i) { + spirv << " %int_0"; + } + spirv << R"( + OpReturn + OpFunctionEnd + )"; + const std::string expected_err = "The number of indexes in " + instr + + " may not exceed 255. Found 256 indexes."; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Valid: 10 indexes passed to the access chain instruction. (Custom limit: 10) +TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesGood) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : ""; + int depth = 10; + std::string header = kGLSL450MemoryModel + kDeeplyNestedStructureSetup; + header.erase(header.find("%func")); + std::ostringstream spirv; + spirv << header << "\n"; + + // Build nested structures. Struct 'i' contains struct 'i-1' + spirv << "%s_depth_1 = OpTypeStruct %float\n"; + for (int i = 2; i <= depth; ++i) { + spirv << "%s_depth_" << i << " = OpTypeStruct %s_depth_" << i - 1 << "\n"; + } + + // Define Pointer and Variable to use for the AccessChain instruction. + spirv << "%_ptr_Uniform_deep_struct = OpTypePointer Uniform %s_depth_" + << depth << "\n"; + spirv << "%deep_var = OpVariable %_ptr_Uniform_deep_struct Uniform\n"; + + // Function Start + spirv << R"( + %func = OpFunction %void None %void_f + %my_label = OpLabel + )"; + + // AccessChain with 'n' indexes (n = depth) + spirv << "%entry = " << instr << " %_ptr_Uniform_float %deep_var" << elem; + for (int i = 0; i < depth; ++i) { + spirv << " %int_0"; + } + + // Function end + spirv << R"( + OpReturn + OpFunctionEnd + )"; + + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_access_chain_indexes, 10u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: 11 indexes passed to the access chain instruction. Custom Limit:10 +TEST_P(AccessChainInstructionTest, CustomizedAccessChainTooManyIndexesBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? " %int_0 " : ""; + std::ostringstream spirv; + spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup; + spirv << "%entry = " << instr << " %_ptr_Private_float %my_matrix" << elem; + for (int i = 0; i < 11; ++i) { + spirv << " %int_0"; + } + spirv << R"( + OpReturn + OpFunctionEnd + )"; + const std::string expected_err = "The number of indexes in " + instr + + " may not exceed 10. Found 11 indexes."; + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_access_chain_indexes, 10u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid: Index passed to the access chain instruction is float (must be +// integer). +TEST_P(AccessChainInstructionTest, AccessChainUndefinedIndexBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Private_float %my_matrix )" + elem + + R"(%float_0 %int_1 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = + "Indexes passed to " + instr + " must be of type integer."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid: The index argument that indexes into a struct must be of type +// OpConstant. +TEST_P(AccessChainInstructionTest, AccessChainStructIndexNotConstantBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%f = )" + + instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + + R"(%int_0 %spec_int %int_2 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = + "The passed to " + instr + + " to index into a structure must be an OpConstant."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid: Indexing up to a vec4 granularity, but result type expected float. +TEST_P(AccessChainInstructionTest, + AccessChainStructResultTypeDoesntMatchIndexedTypeBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + + R"(%int_0 %int_1 %int_2 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = instr + + " result type (OpTypeFloat) does not match " + "the type that results from indexing into " + "the base (OpTypeVector)."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid: Reach non-composite type (bool) when unused indexes remain. +TEST_P(AccessChainInstructionTest, AccessChainStructTooManyIndexesBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + + R"(%int_0 %int_2 %int_2 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = instr + + " reached non-composite type while " + "indexes still remain to be traversed."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid: Trying to find index 3 of the struct that has only 3 members. +TEST_P(AccessChainInstructionTest, AccessChainStructIndexOutOfBoundBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + + R"(%int_3 %int_2 %int_2 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = "Index is out of bounds: " + instr + + " can not find index 3 into the structure " + " '25[%_struct_25]'. This structure " + "has 3 members. Largest valid index is 2."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Valid: Tests that we can index into Struct, Array, Matrix, and Vector! +TEST_P(AccessChainInstructionTest, AccessChainIndexIntoAllTypesGood) { + // indexes that we are passing are: 0, 3, 1, 2, 0 + // 0 will select the struct_s within the base struct (blockName) + // 3 will select the Array that contains 5 matrices + // 1 will select the Matrix that is at index 1 of the array + // 2 will select the column (which is a vector) within the matrix at index 2 + // 0 will select the element at the index 0 of the vector. (which is a float). + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::ostringstream spirv; + spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl; + spirv << "%ss = " << instr << " %_ptr_Uniform_struct_s %blockName_var " + << elem << "%int_0" << std::endl; + spirv << "%sa = " << instr << " %_ptr_Uniform_array5_mat4x3 %blockName_var " + << elem << "%int_0 %int_3" << std::endl; + spirv << "%sm = " << instr << " %_ptr_Uniform_mat4x3 %blockName_var " << elem + << "%int_0 %int_3 %int_1" << std::endl; + spirv << "%sc = " << instr << " %_ptr_Uniform_v3float %blockName_var " << elem + << "%int_0 %int_3 %int_1 %int_2" << std::endl; + spirv << "%entry = " << instr << " %_ptr_Uniform_float %blockName_var " + << elem << "%int_0 %int_3 %int_1 %int_2 %int_0" << std::endl; + spirv << R"( +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Valid: Access an element of OpTypeRuntimeArray. +TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayGood) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%runtime_arr_entry = )" + + instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + + R"(%int_2 %int_0 +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Unused index when accessing OpTypeRuntimeArray. +TEST_P(AccessChainInstructionTest, AccessChainIndexIntoRuntimeArrayBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%runtime_arr_entry = )" + + instr + R"( %_ptr_Uniform_float %blockName_var )" + elem + + R"(%int_2 %int_0 %int_1 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = + instr + + " reached non-composite type while indexes still remain to be traversed."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid: Reached scalar type before arguments to the access chain instruction +// finished. +TEST_P(AccessChainInstructionTest, AccessChainMatrixMoreArgsThanNeededBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Private_float %my_matrix )" + elem + + R"(%int_0 %int_1 %int_0 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = instr + + " reached non-composite type while " + "indexes still remain to be traversed."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Invalid: The result type and the type indexed into do not match. +TEST_P(AccessChainInstructionTest, + AccessChainResultTypeDoesntMatchIndexedTypeBad) { + const std::string instr = GetParam(); + const std::string elem = AccessChainRequiresElemId(instr) ? "%int_0 " : ""; + std::string spirv = kGLSL450MemoryModel + kDeeplyNestedStructureSetup + R"( +%entry = )" + + instr + R"( %_ptr_Private_mat4x3 %my_matrix )" + elem + + R"(%int_0 %int_1 +OpReturn +OpFunctionEnd + )"; + const std::string expected_err = instr + + " result type (OpTypeMatrix) does not match " + "the type that results from indexing into " + "the base (OpTypeFloat)."; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(expected_err)); +} + +// Run tests for Access Chain Instructions. +INSTANTIATE_TEST_SUITE_P( + CheckAccessChainInstructions, AccessChainInstructionTest, + ::testing::Values("OpAccessChain", "OpInBoundsAccessChain", + "OpPtrAccessChain", "OpInBoundsPtrAccessChain")); + +// TODO: OpArrayLength +// TODO: OpImagePointer +// TODO: OpGenericPtrMemSemantics + +TEST_F(ValidateIdWithMessage, OpFunctionGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %1 %2 %2 +%4 = OpFunction %1 None %3 +%5 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpFunctionResultTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 42 +%4 = OpTypeFunction %1 %2 %2 +%5 = OpFunction %2 None %4 +%6 = OpLabel + OpReturnValue %3 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpFunction Result Type '2[%uint]' does not " + "match the Function Type's return type " + "'1[%void]'.")); +} +TEST_F(ValidateIdWithMessage, OpReturnValueTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeFloat 32 +%3 = OpConstant %2 0 +%4 = OpTypeFunction %1 +%5 = OpFunction %1 None %4 +%6 = OpLabel + OpReturnValue %3 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpReturnValue Value '3[%float_0]'s type does " + "not match OpFunction's return type.")); +} +TEST_F(ValidateIdWithMessage, OpFunctionFunctionTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%4 = OpFunction %1 None %2 +%5 = OpLabel + OpReturn +OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpFunction Function Type '2[%uint]' is not a function " + "type.")); +} + +TEST_F(ValidateIdWithMessage, OpFunctionUseBad) { + const std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeFloat 32 +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +OpReturnValue %3 +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid use of function result id 3[%3].")); +} + +TEST_F(ValidateIdWithMessage, OpFunctionParameterGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %1 %2 +%4 = OpFunction %1 None %3 +%5 = OpFunctionParameter %2 +%6 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpFunctionParameterMultipleGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %1 %2 %2 +%4 = OpFunction %1 None %3 +%5 = OpFunctionParameter %2 +%6 = OpFunctionParameter %2 +%7 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpFunctionParameterResultTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %1 %2 +%4 = OpFunction %1 None %3 +%5 = OpFunctionParameter %1 +%6 = OpLabel + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpFunctionParameter Result Type '1[%void]' does not " + "match the OpTypeFunction parameter type of the same index.")); +} + +TEST_F(ValidateIdWithMessage, OpFunctionCallGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %2 42 ;21 + +%6 = OpFunction %2 None %3 +%7 = OpFunctionParameter %2 +%8 = OpLabel + OpReturnValue %7 + OpFunctionEnd + +%10 = OpFunction %1 None %4 +%11 = OpLabel +%12 = OpFunctionCall %2 %6 %5 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpFunctionCallResultTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %2 42 ;21 + +%6 = OpFunction %2 None %3 +%7 = OpFunctionParameter %2 +%8 = OpLabel +%9 = OpIAdd %2 %7 %7 + OpReturnValue %9 + OpFunctionEnd + +%10 = OpFunction %1 None %4 +%11 = OpLabel +%12 = OpFunctionCall %1 %6 %5 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpFunctionCall Result Type '1[%void]'s type " + "does not match Function '2[%uint]'s return " + "type.")); +} +TEST_F(ValidateIdWithMessage, OpFunctionCallFunctionBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %2 42 ;21 + +%10 = OpFunction %1 None %4 +%11 = OpLabel +%12 = OpFunctionCall %2 %5 %5 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpFunctionCall Function '5[%uint_42]' is not a " + "function.")); +} +TEST_F(ValidateIdWithMessage, OpFunctionCallArgumentTypeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %2 42 + +%13 = OpTypeFloat 32 +%14 = OpConstant %13 3.14 + +%6 = OpFunction %2 None %3 +%7 = OpFunctionParameter %2 +%8 = OpLabel +%9 = OpIAdd %2 %7 %7 + OpReturnValue %9 + OpFunctionEnd + +%10 = OpFunction %1 None %4 +%11 = OpLabel +%12 = OpFunctionCall %2 %6 %14 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpFunctionCall Argument '7[%float_3_1400001]'s " + "type does not match Function '2[%uint]'s " + "parameter type.")); +} + +// Valid: OpSampledImage result is used in the same block by +// OpImageSampleImplictLod +TEST_F(ValidateIdWithMessage, OpSampledImageGood) { + std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( +%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst +%si_lod = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: OpSampledImage result is defined in one block and used in a +// different block. +TEST_F(ValidateIdWithMessage, OpSampledImageUsedInDifferentBlockBad) { + std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( +%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst +OpBranch %label_2 +%label_2 = OpLabel +%si_lod = OpImageSampleImplicitLod %v4float %smpld_img %const_vec_1_1 +OpReturn +OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("All OpSampledImage instructions must be in the same block in " + "which their Result are consumed. OpSampledImage Result " + "Type '23[%23]' has a consumer in a different basic " + "block. The consumer instruction is '25[%25]'.")); +} + +// Invalid: OpSampledImage result is used by OpSelect +// Note: According to the Spec, OpSelect parameters must be either a scalar or a +// vector. Therefore, OpTypeSampledImage is an illegal parameter for OpSelect. +// However, the OpSelect validation does not catch this today. Therefore, it is +// caught by the OpSampledImage validation. If the OpSelect validation code is +// updated, the error message for this test may change. +// +// Disabled since OpSelect catches this now. +TEST_F(ValidateIdWithMessage, DISABLED_OpSampledImageUsedInOpSelectBad) { + std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( +%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst +%select_img = OpSelect %sampled_image_type %spec_true %smpld_img %smpld_img +OpReturn +OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result from OpSampledImage instruction must not " + "appear as operands of OpSelect. Found result " + "'23' as an operand of '24'.")); +} + +TEST_F(ValidateIdWithMessage, OpCopyObjectSampledImageGood) { + std::string spirv = kGLSL450MemoryModel + sampledImageSetup + R"( +%smpld_img = OpSampledImage %sampled_image_type %image_inst %sampler_inst +%smpld_img2 = OpCopyObject %sampled_image_type %smpld_img +%image_inst2 = OpCopyObject %image_type %image_inst +OpReturn +OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Valid: Get a float in a matrix using CompositeExtract. +// Valid: Insert float into a matrix using CompositeInsert. +TEST_F(ValidateIdWithMessage, CompositeExtractInsertGood) { + std::ostringstream spirv; + spirv << kGLSL450MemoryModel << kDeeplyNestedStructureSetup << std::endl; + spirv << "%matrix = OpLoad %mat4x3 %my_matrix" << std::endl; + spirv << "%float_entry = OpCompositeExtract %float %matrix 0 1" << std::endl; + + // To test CompositeInsert, insert the object back in after extraction. + spirv << "%new_composite = OpCompositeInsert %mat4x3 %float_entry %matrix 0 1" + << std::endl; + spirv << R"(OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +#if 0 +TEST_F(ValidateIdWithMessage, OpFunctionCallArgumentCountBar) { + const char *spirv = R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 %2 +%4 = OpTypeFunction %1 +%5 = OpConstant %2 42 ;21 + +%6 = OpFunction %2 None %3 +%7 = OpFunctionParameter %2 +%8 = OpLabel +%9 = OpLoad %2 %7 + OpReturnValue %9 + OpFunctionEnd + +%10 = OpFunction %1 None %4 +%11 = OpLabel + OpReturn +%12 = OpFunctionCall %2 %6 %5 + OpFunctionEnd)"; + CHECK(spirv, SPV_ERROR_INVALID_ID); +} +#endif + +// TODO: The many things that changed with how images are used. +// TODO: OpTextureSample +// TODO: OpTextureSampleDref +// TODO: OpTextureSampleLod +// TODO: OpTextureSampleProj +// TODO: OpTextureSampleGrad +// TODO: OpTextureSampleOffset +// TODO: OpTextureSampleProjLod +// TODO: OpTextureSampleProjGrad +// TODO: OpTextureSampleLodOffset +// TODO: OpTextureSampleProjOffset +// TODO: OpTextureSampleGradOffset +// TODO: OpTextureSampleProjLodOffset +// TODO: OpTextureSampleProjGradOffset +// TODO: OpTextureFetchTexelLod +// TODO: OpTextureFetchTexelOffset +// TODO: OpTextureFetchSample +// TODO: OpTextureFetchTexel +// TODO: OpTextureGather +// TODO: OpTextureGatherOffset +// TODO: OpTextureGatherOffsets +// TODO: OpTextureQuerySizeLod +// TODO: OpTextureQuerySize +// TODO: OpTextureQueryLevels +// TODO: OpTextureQuerySamples +// TODO: OpConvertUToF +// TODO: OpConvertFToS +// TODO: OpConvertSToF +// TODO: OpConvertUToF +// TODO: OpUConvert +// TODO: OpSConvert +// TODO: OpFConvert +// TODO: OpConvertPtrToU +// TODO: OpConvertUToPtr +// TODO: OpPtrCastToGeneric +// TODO: OpGenericCastToPtr +// TODO: OpBitcast +// TODO: OpGenericCastToPtrExplicit +// TODO: OpSatConvertSToU +// TODO: OpSatConvertUToS +// TODO: OpVectorExtractDynamic +// TODO: OpVectorInsertDynamic + +TEST_F(ValidateIdWithMessage, OpVectorShuffleIntGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%int = OpTypeInt 32 0 +%ivec3 = OpTypeVector %int 3 +%ivec4 = OpTypeVector %int 4 +%ptr_ivec3 = OpTypePointer Function %ivec3 +%undef = OpUndef %ivec4 +%int_42 = OpConstant %int 42 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2 +%2 = OpTypeFunction %ivec3 +%3 = OpFunction %ivec3 None %2 +%4 = OpLabel +%var = OpVariable %ptr_ivec3 Function %1 +%5 = OpLoad %ivec3 %var +%6 = OpVectorShuffle %ivec3 %5 %undef 2 1 0 + OpReturnValue %6 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpVectorShuffleFloatGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%float = OpTypeFloat 32 +%vec2 = OpTypeVector %float 2 +%vec3 = OpTypeVector %float 3 +%vec4 = OpTypeVector %float 4 +%ptr_vec2 = OpTypePointer Function %vec2 +%ptr_vec3 = OpTypePointer Function %vec3 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%1 = OpConstantComposite %vec2 %float_2 %float_1 +%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 +%3 = OpTypeFunction %vec4 +%4 = OpFunction %vec4 None %3 +%5 = OpLabel +%var = OpVariable %ptr_vec2 Function %1 +%var2 = OpVariable %ptr_vec3 Function %2 +%6 = OpLoad %vec2 %var +%7 = OpLoad %vec3 %var2 +%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff + OpReturnValue %8 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpVectorShuffleScalarResultType) { + std::string spirv = kGLSL450MemoryModel + R"( +%float = OpTypeFloat 32 +%vec2 = OpTypeVector %float 2 +%ptr_vec2 = OpTypePointer Function %vec2 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%1 = OpConstantComposite %vec2 %float_2 %float_1 +%2 = OpTypeFunction %float +%3 = OpFunction %float None %2 +%4 = OpLabel +%var = OpVariable %ptr_vec2 Function %1 +%5 = OpLoad %vec2 %var +%6 = OpVectorShuffle %float %5 %5 0 + OpReturnValue %6 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Result Type of OpVectorShuffle must be OpTypeVector.")); +} + +TEST_F(ValidateIdWithMessage, OpVectorShuffleComponentCount) { + std::string spirv = kGLSL450MemoryModel + R"( +%int = OpTypeInt 32 0 +%ivec3 = OpTypeVector %int 3 +%ptr_ivec3 = OpTypePointer Function %ivec3 +%int_42 = OpConstant %int 42 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2 +%2 = OpTypeFunction %ivec3 +%3 = OpFunction %ivec3 None %2 +%4 = OpLabel +%var = OpVariable %ptr_ivec3 Function %1 +%5 = OpLoad %ivec3 %var +%6 = OpVectorShuffle %ivec3 %5 %5 0 1 + OpReturnValue %6 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpVectorShuffle component literals count does not match " + "Result Type '2[%v3uint]'s vector component count.")); +} + +TEST_F(ValidateIdWithMessage, OpVectorShuffleVector1Type) { + std::string spirv = kGLSL450MemoryModel + R"( +%int = OpTypeInt 32 0 +%ivec2 = OpTypeVector %int 2 +%ptr_int = OpTypePointer Function %int +%undef = OpUndef %ivec2 +%int_42 = OpConstant %int 42 +%2 = OpTypeFunction %ivec2 +%3 = OpFunction %ivec2 None %2 +%4 = OpLabel +%var = OpVariable %ptr_int Function %int_42 +%5 = OpLoad %int %var +%6 = OpVectorShuffle %ivec2 %5 %undef 0 0 + OpReturnValue %6 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The type of Vector 1 must be OpTypeVector.")); +} + +TEST_F(ValidateIdWithMessage, OpVectorShuffleVector2Type) { + std::string spirv = kGLSL450MemoryModel + R"( +%int = OpTypeInt 32 0 +%ivec2 = OpTypeVector %int 2 +%ptr_ivec2 = OpTypePointer Function %ivec2 +%undef = OpUndef %int +%int_42 = OpConstant %int 42 +%1 = OpConstantComposite %ivec2 %int_42 %int_42 +%2 = OpTypeFunction %ivec2 +%3 = OpFunction %ivec2 None %2 +%4 = OpLabel +%var = OpVariable %ptr_ivec2 Function %1 +%5 = OpLoad %ivec2 %var +%6 = OpVectorShuffle %ivec2 %5 %undef 0 1 + OpReturnValue %6 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The type of Vector 2 must be OpTypeVector.")); +} + +TEST_F(ValidateIdWithMessage, OpVectorShuffleVector1ComponentType) { + std::string spirv = kGLSL450MemoryModel + R"( +%int = OpTypeInt 32 0 +%ivec3 = OpTypeVector %int 3 +%ptr_ivec3 = OpTypePointer Function %ivec3 +%int_42 = OpConstant %int 42 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%float = OpTypeFloat 32 +%vec3 = OpTypeVector %float 3 +%vec4 = OpTypeVector %float 4 +%ptr_vec3 = OpTypePointer Function %vec3 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2 +%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 +%3 = OpTypeFunction %vec4 +%4 = OpFunction %vec4 None %3 +%5 = OpLabel +%var = OpVariable %ptr_ivec3 Function %1 +%var2 = OpVariable %ptr_vec3 Function %2 +%6 = OpLoad %ivec3 %var +%7 = OpLoad %vec3 %var2 +%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0 + OpReturnValue %8 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Component Type of Vector 1 must be the same as " + "ResultType.")); +} + +TEST_F(ValidateIdWithMessage, OpVectorShuffleVector2ComponentType) { + std::string spirv = kGLSL450MemoryModel + R"( +%int = OpTypeInt 32 0 +%ivec3 = OpTypeVector %int 3 +%ptr_ivec3 = OpTypePointer Function %ivec3 +%int_42 = OpConstant %int 42 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%float = OpTypeFloat 32 +%vec3 = OpTypeVector %float 3 +%vec4 = OpTypeVector %float 4 +%ptr_vec3 = OpTypePointer Function %vec3 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%1 = OpConstantComposite %ivec3 %int_42 %int_0 %int_2 +%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 +%3 = OpTypeFunction %vec4 +%4 = OpFunction %vec4 None %3 +%5 = OpLabel +%var = OpVariable %ptr_ivec3 Function %1 +%var2 = OpVariable %ptr_vec3 Function %2 +%6 = OpLoad %vec3 %var2 +%7 = OpLoad %ivec3 %var +%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0 + OpReturnValue %8 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Component Type of Vector 2 must be the same as " + "ResultType.")); +} + +TEST_F(ValidateIdWithMessage, OpVectorShuffleLiterals) { + std::string spirv = kGLSL450MemoryModel + R"( +%float = OpTypeFloat 32 +%vec2 = OpTypeVector %float 2 +%vec3 = OpTypeVector %float 3 +%vec4 = OpTypeVector %float 4 +%ptr_vec2 = OpTypePointer Function %vec2 +%ptr_vec3 = OpTypePointer Function %vec3 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%1 = OpConstantComposite %vec2 %float_2 %float_1 +%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 +%3 = OpTypeFunction %vec4 +%4 = OpFunction %vec4 None %3 +%5 = OpLabel +%var = OpVariable %ptr_vec2 Function %1 +%var2 = OpVariable %ptr_vec3 Function %2 +%6 = OpLoad %vec2 %var +%7 = OpLoad %vec3 %var2 +%8 = OpVectorShuffle %vec4 %6 %7 0 8 2 6 + OpReturnValue %8 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Component index 8 is out of bounds for combined (Vector1 + Vector2) " + "size of 5.")); +} + +TEST_F(ValidateIdWithMessage, WebGPUOpVectorShuffle0xFFFFFFFFLiteralBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR +%float = OpTypeFloat 32 +%vec2 = OpTypeVector %float 2 +%vec3 = OpTypeVector %float 3 +%vec4 = OpTypeVector %float 4 +%ptr_vec2 = OpTypePointer Function %vec2 +%ptr_vec3 = OpTypePointer Function %vec3 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%1 = OpConstantComposite %vec2 %float_2 %float_1 +%2 = OpConstantComposite %vec3 %float_1 %float_2 %float_2 +%3 = OpTypeFunction %vec4 +%4 = OpFunction %vec4 None %3 +%5 = OpLabel +%var = OpVariable %ptr_vec2 Function %1 +%var2 = OpVariable %ptr_vec3 Function %2 +%6 = OpLoad %vec2 %var +%7 = OpLoad %vec3 %var2 +%8 = OpVectorShuffle %vec4 %6 %7 4 3 1 0xffffffff + OpReturnValue %8 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Component literal at operand 3 cannot be 0xFFFFFFFF in" + " WebGPU execution environment.")); +} + +// TODO: OpCompositeConstruct +// TODO: OpCompositeExtract +// TODO: OpCompositeInsert +// TODO: OpCopyObject +// TODO: OpTranspose +// TODO: OpSNegate +// TODO: OpFNegate +// TODO: OpNot +// TODO: OpIAdd +// TODO: OpFAdd +// TODO: OpISub +// TODO: OpFSub +// TODO: OpIMul +// TODO: OpFMul +// TODO: OpUDiv +// TODO: OpSDiv +// TODO: OpFDiv +// TODO: OpUMod +// TODO: OpSRem +// TODO: OpSMod +// TODO: OpFRem +// TODO: OpFMod +// TODO: OpVectorTimesScalar +// TODO: OpMatrixTimesScalar +// TODO: OpVectorTimesMatrix +// TODO: OpMatrixTimesVector +// TODO: OpMatrixTimesMatrix +// TODO: OpOuterProduct +// TODO: OpDot +// TODO: OpShiftRightLogical +// TODO: OpShiftRightArithmetic +// TODO: OpShiftLeftLogical +// TODO: OpBitwiseOr +// TODO: OpBitwiseXor +// TODO: OpBitwiseAnd +// TODO: OpAny +// TODO: OpAll +// TODO: OpIsNan +// TODO: OpIsInf +// TODO: OpIsFinite +// TODO: OpIsNormal +// TODO: OpSignBitSet +// TODO: OpLessOrGreater +// TODO: OpOrdered +// TODO: OpUnordered +// TODO: OpLogicalOr +// TODO: OpLogicalXor +// TODO: OpLogicalAnd +// TODO: OpSelect +// TODO: OpIEqual +// TODO: OpFOrdEqual +// TODO: OpFUnordEqual +// TODO: OpINotEqual +// TODO: OpFOrdNotEqual +// TODO: OpFUnordNotEqual +// TODO: OpULessThan +// TODO: OpSLessThan +// TODO: OpFOrdLessThan +// TODO: OpFUnordLessThan +// TODO: OpUGreaterThan +// TODO: OpSGreaterThan +// TODO: OpFOrdGreaterThan +// TODO: OpFUnordGreaterThan +// TODO: OpULessThanEqual +// TODO: OpSLessThanEqual +// TODO: OpFOrdLessThanEqual +// TODO: OpFUnordLessThanEqual +// TODO: OpUGreaterThanEqual +// TODO: OpSGreaterThanEqual +// TODO: OpFOrdGreaterThanEqual +// TODO: OpFUnordGreaterThanEqual +// TODO: OpDPdx +// TODO: OpDPdy +// TODO: OpFWidth +// TODO: OpDPdxFine +// TODO: OpDPdyFine +// TODO: OpFwidthFine +// TODO: OpDPdxCoarse +// TODO: OpDPdyCoarse +// TODO: OpFwidthCoarse +// TODO: OpLoopMerge +// TODO: OpSelectionMerge +// TODO: OpBranch + +TEST_F(ValidateIdWithMessage, OpPhiNotAType) { + std::string spirv = kOpenCLMemoryModel32 + R"( +%2 = OpTypeBool +%3 = OpConstantTrue %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +%9 = OpPhi %3 %3 %7 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 3[%true] is not a type " + "id")); +} + +TEST_F(ValidateIdWithMessage, OpPhiSamePredecessor) { + std::string spirv = kOpenCLMemoryModel32 + R"( +%2 = OpTypeBool +%3 = OpConstantTrue %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +OpBranchConditional %3 %8 %8 +%8 = OpLabel +%9 = OpPhi %2 %3 %7 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpPhiOddArgumentNumber) { + std::string spirv = kOpenCLMemoryModel32 + R"( +%2 = OpTypeBool +%3 = OpConstantTrue %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +%9 = OpPhi %2 %3 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi does not have an equal number of incoming " + "values and basic blocks.")); +} + +TEST_F(ValidateIdWithMessage, OpPhiTooFewPredecessors) { + std::string spirv = kOpenCLMemoryModel32 + R"( +%2 = OpTypeBool +%3 = OpConstantTrue %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +OpBranch %8 +%8 = OpLabel +%9 = OpPhi %2 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi's number of incoming blocks (0) does not match " + "block's predecessor count (1).")); +} + +TEST_F(ValidateIdWithMessage, OpPhiTooManyPredecessors) { + std::string spirv = kOpenCLMemoryModel32 + R"( +%2 = OpTypeBool +%3 = OpConstantTrue %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +OpBranch %8 +%9 = OpLabel +OpReturn +%8 = OpLabel +%10 = OpPhi %2 %3 %7 %3 %9 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi's number of incoming blocks (2) does not match " + "block's predecessor count (1).")); +} + +TEST_F(ValidateIdWithMessage, OpPhiMismatchedTypes) { + std::string spirv = kOpenCLMemoryModel32 + R"( +%2 = OpTypeBool +%3 = OpConstantTrue %2 +%4 = OpTypeVoid +%5 = OpTypeInt 32 0 +%6 = OpConstant %5 0 +%7 = OpTypeFunction %4 +%8 = OpFunction %4 None %7 +%9 = OpLabel +OpBranchConditional %3 %10 %11 +%11 = OpLabel +OpBranch %10 +%10 = OpLabel +%12 = OpPhi %2 %3 %9 %6 %11 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi's result type 2[%bool] does not match " + "incoming value 6[%uint_0] type " + "5[%uint].")); +} + +TEST_F(ValidateIdWithMessage, OpPhiPredecessorNotABlock) { + std::string spirv = kOpenCLMemoryModel32 + R"( +%2 = OpTypeBool +%3 = OpConstantTrue %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +OpBranchConditional %3 %8 %9 +%9 = OpLabel +OpBranch %11 +%11 = OpLabel +OpBranch %8 +%8 = OpLabel +%10 = OpPhi %2 %3 %7 %3 %3 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi's incoming basic block 3[%true] is not an " + "OpLabel.")); +} + +TEST_F(ValidateIdWithMessage, OpPhiNotAPredecessor) { + std::string spirv = kOpenCLMemoryModel32 + R"( +%2 = OpTypeBool +%3 = OpConstantTrue %2 +%4 = OpTypeVoid +%5 = OpTypeFunction %4 +%6 = OpFunction %4 None %5 +%7 = OpLabel +OpBranchConditional %3 %8 %9 +%9 = OpLabel +OpBranch %11 +%11 = OpLabel +OpBranch %8 +%8 = OpLabel +%10 = OpPhi %2 %3 %7 %3 %9 +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpPhi's incoming basic block 9[%9] is not a " + "predecessor of 8[%8].")); +} + +TEST_F(ValidateIdWithMessage, OpBranchConditionalGood) { + std::string spirv = BranchConditionalSetup + R"( + %branch_cond = OpINotEqual %bool %i0 %i1 + OpSelectionMerge %end None + OpBranchConditional %branch_cond %target_t %target_f + )" + BranchConditionalTail; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateIdWithMessage, OpBranchConditionalWithWeightsGood) { + std::string spirv = BranchConditionalSetup + R"( + %branch_cond = OpINotEqual %bool %i0 %i1 + OpSelectionMerge %end None + OpBranchConditional %branch_cond %target_t %target_f 1 1 + )" + BranchConditionalTail; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateIdWithMessage, OpBranchConditional_CondIsScalarInt) { + std::string spirv = BranchConditionalSetup + R"( + OpSelectionMerge %end None + OpBranchConditional %i0 %target_t %target_f + )" + BranchConditionalTail; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Condition operand for OpBranchConditional must be of boolean type")); +} + +TEST_F(ValidateIdWithMessage, OpBranchConditional_TrueTargetIsNotLabel) { + std::string spirv = BranchConditionalSetup + R"( + OpSelectionMerge %end None + OpBranchConditional %true %i0 %target_f + )" + BranchConditionalTail; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The 'True Label' operand for OpBranchConditional must " + "be the ID of an OpLabel instruction")); +} + +TEST_F(ValidateIdWithMessage, OpBranchConditional_FalseTargetIsNotLabel) { + std::string spirv = BranchConditionalSetup + R"( + OpSelectionMerge %end None + OpBranchConditional %true %target_t %i0 + )" + BranchConditionalTail; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The 'False Label' operand for OpBranchConditional " + "must be the ID of an OpLabel instruction")); +} + +TEST_F(ValidateIdWithMessage, OpBranchConditional_NotEnoughWeights) { + std::string spirv = BranchConditionalSetup + R"( + %branch_cond = OpINotEqual %bool %i0 %i1 + OpSelectionMerge %end None + OpBranchConditional %branch_cond %target_t %target_f 1 + )" + BranchConditionalTail; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpBranchConditional requires either 3 or 5 parameters")); +} + +TEST_F(ValidateIdWithMessage, OpBranchConditional_TooManyWeights) { + std::string spirv = BranchConditionalSetup + R"( + %branch_cond = OpINotEqual %bool %i0 %i1 + OpSelectionMerge %end None + OpBranchConditional %branch_cond %target_t %target_f 1 2 3 + )" + BranchConditionalTail; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpBranchConditional requires either 3 or 5 parameters")); +} + +TEST_F(ValidateIdWithMessage, OpBranchConditional_ConditionIsAType) { + std::string spirv = BranchConditionalSetup + R"( +OpBranchConditional %bool %target_t %target_f +)" + BranchConditionalTail; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 3[%bool] cannot be a " + "type")); +} + +// TODO: OpSwitch + +TEST_F(ValidateIdWithMessage, OpReturnValueConstantGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 +%4 = OpConstant %2 42 +%5 = OpFunction %2 None %3 +%6 = OpLabel + OpReturnValue %4 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpReturnValueVariableGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 ;10 +%3 = OpTypeFunction %2 +%8 = OpTypePointer Function %2 ;18 +%4 = OpConstant %2 42 ;22 +%5 = OpFunction %2 None %3 ;27 +%6 = OpLabel ;29 +%7 = OpVariable %8 Function %4 ;34 +%9 = OpLoad %2 %7 + OpReturnValue %9 ;36 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpReturnValueExpressionGood) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 +%4 = OpConstant %2 42 +%5 = OpFunction %2 None %3 +%6 = OpLabel +%7 = OpIAdd %2 %4 %4 + OpReturnValue %7 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpReturnValueIsType) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 +%5 = OpFunction %2 None %3 +%6 = OpLabel + OpReturnValue %1 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 1[%void] cannot be a " + "type")); +} + +TEST_F(ValidateIdWithMessage, OpReturnValueIsLabel) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 +%5 = OpFunction %2 None %3 +%6 = OpLabel + OpReturnValue %6 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 5[%5] requires a type")); +} + +TEST_F(ValidateIdWithMessage, OpReturnValueIsVoid) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %1 +%5 = OpFunction %1 None %3 +%6 = OpLabel +%7 = OpFunctionCall %1 %5 + OpReturnValue %7 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpReturnValue value's type '1[%void]' is missing or " + "void.")); +} + +TEST_F(ValidateIdWithMessage, OpReturnValueIsVariableInPhysical) { + // It's valid to return a pointer in a physical addressing model. + std::string spirv = kOpCapabilitySetup + R"( + OpMemoryModel Physical32 OpenCL +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Function %2 +%4 = OpTypeFunction %3 +%5 = OpFunction %3 None %4 +%6 = OpLabel +%7 = OpVariable %3 Function + OpReturnValue %7 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpReturnValueIsVariableInLogical) { + // It's invalid to return a pointer in a physical addressing model. + std::string spirv = kOpCapabilitySetup + R"( + OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Function %2 +%4 = OpTypeFunction %3 +%5 = OpFunction %3 None %4 +%6 = OpLabel +%7 = OpVariable %3 Function + OpReturnValue %7 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpReturnValue value's type " + "'3[%_ptr_Function_uint]' is a pointer, which is " + "invalid in the Logical addressing model.")); +} + +// With the VariablePointer Capability, the return value of a function is +// allowed to be a pointer. +TEST_F(ValidateIdWithMessage, OpReturnValueVarPtrGood) { + std::ostringstream spirv; + createVariablePointerSpirvProgram(&spirv, + "" /* Instructions to add to "main" */, + true /* Add VariablePointers Capability?*/, + true /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Without the VariablePointer Capability, the return value of a function is +// *not* allowed to be a pointer. +// Disabled since using OpSelect with pointers without VariablePointers will +// fail LogicalsPass. +TEST_F(ValidateIdWithMessage, DISABLED_OpReturnValueVarPtrBad) { + std::ostringstream spirv; + createVariablePointerSpirvProgram(&spirv, + "" /* Instructions to add to "main" */, + false /* Add VariablePointers Capability?*/, + true /* Use Helper Function? */); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpReturnValue value's type '7' is a pointer, " + "which is invalid in the Logical addressing model.")); +} + +// TODO: enable when this bug is fixed: +// https://cvs.khronos.org/bugzilla/show_bug.cgi?id=15404 +TEST_F(ValidateIdWithMessage, DISABLED_OpReturnValueIsFunction) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypeFunction %2 +%5 = OpFunction %2 None %3 +%6 = OpLabel + OpReturnValue %5 + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, UndefinedTypeId) { + std::string spirv = kGLSL450MemoryModel + R"( +%s = OpTypeStruct %i32 +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 2[%2] requires a previous definition")); +} + +TEST_F(ValidateIdWithMessage, UndefinedIdScope) { + std::string spirv = kGLSL450MemoryModel + R"( +%u32 = OpTypeInt 32 0 +%memsem = OpConstant %u32 0 +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%f = OpFunction %void None %void_f +%l = OpLabel + OpMemoryBarrier %undef %memsem + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 7[%7] has not been " + "defined")); +} + +TEST_F(ValidateIdWithMessage, UndefinedIdMemSem) { + std::string spirv = kGLSL450MemoryModel + R"( +%u32 = OpTypeInt 32 0 +%scope = OpConstant %u32 0 +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%f = OpFunction %void None %void_f +%l = OpLabel + OpMemoryBarrier %scope %undef + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 7[%7] has not been " + "defined")); +} + +TEST_F(ValidateIdWithMessage, + KernelOpEntryPointAndOpInBoundsPtrAccessChainGood) { + std::string spirv = kOpenCLMemoryModel32 + R"( + OpEntryPoint Kernel %2 "simple_kernel" + OpSource OpenCL_C 200000 + OpDecorate %3 BuiltIn GlobalInvocationId + OpDecorate %3 Constant + OpDecorate %4 FuncParamAttr NoCapture + OpDecorate %3 LinkageAttributes "__spirv_GlobalInvocationId" Import + %5 = OpTypeInt 32 0 + %6 = OpTypeVector %5 3 + %7 = OpTypePointer UniformConstant %6 + %3 = OpVariable %7 UniformConstant + %8 = OpTypeVoid + %9 = OpTypeStruct %5 +%10 = OpTypePointer CrossWorkgroup %9 +%11 = OpTypeFunction %8 %10 +%12 = OpConstant %5 0 +%13 = OpTypePointer CrossWorkgroup %5 +%14 = OpConstant %5 42 + %2 = OpFunction %8 None %11 + %4 = OpFunctionParameter %10 +%15 = OpLabel +%16 = OpLoad %6 %3 Aligned 0 +%17 = OpCompositeExtract %5 %16 0 +%18 = OpInBoundsPtrAccessChain %13 %4 %17 %12 + OpStore %18 %14 Aligned 4 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpPtrAccessChainGood) { + std::string spirv = kOpenCLMemoryModel64 + R"( + OpEntryPoint Kernel %2 "another_kernel" + OpSource OpenCL_C 200000 + OpDecorate %3 BuiltIn GlobalInvocationId + OpDecorate %3 Constant + OpDecorate %4 FuncParamAttr NoCapture + OpDecorate %3 LinkageAttributes "__spirv_GlobalInvocationId" Import + %5 = OpTypeInt 64 0 + %6 = OpTypeVector %5 3 + %7 = OpTypePointer UniformConstant %6 + %3 = OpVariable %7 UniformConstant + %8 = OpTypeVoid + %9 = OpTypeInt 32 0 +%10 = OpTypeStruct %9 +%11 = OpTypePointer CrossWorkgroup %10 +%12 = OpTypeFunction %8 %11 +%13 = OpConstant %5 4294967295 +%14 = OpConstant %9 0 +%15 = OpTypePointer CrossWorkgroup %9 +%16 = OpConstant %9 42 + %2 = OpFunction %8 None %12 + %4 = OpFunctionParameter %11 +%17 = OpLabel +%18 = OpLoad %6 %3 Aligned 0 +%19 = OpCompositeExtract %5 %18 0 +%20 = OpBitwiseAnd %5 %19 %13 +%21 = OpPtrAccessChain %15 %4 %20 %14 + OpStore %21 %16 Aligned 4 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, StgBufOpPtrAccessChainGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpCapability VariablePointersStorageBuffer + OpExtension "SPV_KHR_variable_pointers" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "" +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%int_4 = OpConstant %int 4 +%struct = OpTypeStruct %int +%array = OpTypeArray %struct %int_4 +%ptr = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr StorageBuffer +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel +%5 = OpPtrAccessChain %ptr %var %int_2 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateIdWithMessage, OpLoadBitcastPointerGood) { + std::string spirv = kOpenCLMemoryModel64 + R"( +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypeFloat 32 +%5 = OpTypePointer UniformConstant %3 +%6 = OpTypePointer UniformConstant %4 +%7 = OpVariable %5 UniformConstant +%8 = OpTypeFunction %2 +%9 = OpFunction %2 None %8 +%10 = OpLabel +%11 = OpBitcast %6 %7 +%12 = OpLoad %4 %11 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpLoadBitcastNonPointerBad) { + std::string spirv = kOpenCLMemoryModel64 + R"( +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypeFloat 32 +%5 = OpTypePointer UniformConstant %3 +%6 = OpTypeFunction %2 +%7 = OpVariable %5 UniformConstant +%8 = OpFunction %2 None %6 +%9 = OpLabel +%10 = OpLoad %3 %7 +%11 = OpBitcast %4 %10 +%12 = OpLoad %3 %11 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpLoad type for pointer '11[%11]' is not a pointer " + "type.")); +} +TEST_F(ValidateIdWithMessage, OpStoreBitcastPointerGood) { + std::string spirv = kOpenCLMemoryModel64 + R"( +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypeFloat 32 +%5 = OpTypePointer Function %3 +%6 = OpTypePointer Function %4 +%7 = OpTypeFunction %2 +%8 = OpConstant %3 42 +%9 = OpFunction %2 None %7 +%10 = OpLabel +%11 = OpVariable %6 Function +%12 = OpBitcast %5 %11 + OpStore %12 %8 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} +TEST_F(ValidateIdWithMessage, OpStoreBitcastNonPointerBad) { + std::string spirv = kOpenCLMemoryModel64 + R"( +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypeFloat 32 +%5 = OpTypePointer Function %4 +%6 = OpTypeFunction %2 +%7 = OpConstant %4 42 +%8 = OpFunction %2 None %6 +%9 = OpLabel +%10 = OpVariable %5 Function +%11 = OpBitcast %3 %7 + OpStore %11 %7 + OpReturn + OpFunctionEnd)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpStore type for pointer '11[%11]' is not a pointer " + "type.")); +} + +// Result resulting from an instruction within a function may not be used +// outside that function. +TEST_F(ValidateIdWithMessage, ResultIdUsedOutsideOfFunctionBad) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Function %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +%7 = OpVariable %4 Function +OpReturn +OpFunctionEnd +%8 = OpFunction %1 None %2 +%9 = OpLabel +%10 = OpLoad %3 %7 +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ID 7[%7] defined in block 6[%6] does not dominate its use in block " + "9[%9]")); +} + +TEST_F(ValidateIdWithMessage, SpecIdTargetNotSpecializationConstant) { + std::string spirv = kGLSL450MemoryModel + R"( +OpDecorate %1 SpecId 200 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%int = OpTypeInt 32 0 +%1 = OpConstant %int 3 +%main = OpFunction %void None %2 +%4 = OpLabel +OpReturnValue %1 +OpFunctionEnd + )"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpDecorate SpecId decoration target " + "'1[%uint_3]' is not a scalar specialization " + "constant.")); +} + +TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantOpBad) { + std::string spirv = kGLSL450MemoryModel + R"( +OpDecorate %1 SpecId 200 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%int = OpTypeInt 32 0 +%3 = OpConstant %int 1 +%4 = OpConstant %int 2 +%1 = OpSpecConstantOp %int IAdd %3 %4 +%main = OpFunction %void None %2 +%6 = OpLabel +OpReturnValue %3 +OpFunctionEnd + )"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpDecorate SpecId decoration target '1[%1]' is " + "not a scalar specialization constant.")); +} + +TEST_F(ValidateIdWithMessage, SpecIdTargetOpSpecConstantCompositeBad) { + std::string spirv = kGLSL450MemoryModel + R"( +OpDecorate %1 SpecId 200 +%void = OpTypeVoid +%2 = OpTypeFunction %void +%int = OpTypeInt 32 0 +%3 = OpConstant %int 1 +%1 = OpSpecConstantComposite %int +%main = OpFunction %void None %2 +%4 = OpLabel +OpReturnValue %3 +OpFunctionEnd + )"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpDecorate SpecId decoration target '1[%1]' is " + "not a scalar specialization constant.")); +} + +TEST_F(ValidateIdWithMessage, SpecIdTargetGood) { + std::string spirv = kGLSL450MemoryModel + R"( +OpDecorate %3 SpecId 200 +OpDecorate %4 SpecId 201 +OpDecorate %5 SpecId 202 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%int = OpTypeInt 32 0 +%bool = OpTypeBool +%3 = OpSpecConstant %int 3 +%4 = OpSpecConstantTrue %bool +%5 = OpSpecConstantFalse %bool +%main = OpFunction %1 None %2 +%6 = OpLabel +OpReturn +OpFunctionEnd + )"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidateIdWithMessage, CorrectErrorForShuffle) { + std::string spirv = kGLSL450MemoryModel + R"( + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%v2float = OpTypeVector %float 2 + %void = OpTypeVoid + %548 = OpTypeFunction %void + %CS = OpFunction %void None %548 + %550 = OpLabel + %6275 = OpUndef %v2float + %6280 = OpUndef %v2float + %6282 = OpVectorShuffle %v4float %6275 %6280 0 1 4 5 + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Component index 4 is out of bounds for combined (Vector1 + Vector2) " + "size of 4.")); + EXPECT_EQ(25, getErrorPosition().index); +} + +TEST_F(ValidateIdWithMessage, VoidStructMember) { + const std::string spirv = kGLSL450MemoryModel + R"( +%void = OpTypeVoid +%struct = OpTypeStruct %void +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Structures cannot contain a void type.")); +} + +TEST_F(ValidateIdWithMessage, TypeFunctionBadUse) { + std::string spirv = kGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypePointer Function %2 +%4 = OpFunction %1 None %2 +%5 = OpLabel + OpReturn + OpFunctionEnd)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid use of function type result id 2[%2].")); +} + +TEST_F(ValidateIdWithMessage, BadTypeId) { + std::string spirv = kGLSL450MemoryModel + R"( + %1 = OpTypeVoid + %2 = OpTypeFunction %1 + %3 = OpTypeFloat 32 + %4 = OpConstant %3 0 + %5 = OpFunction %1 None %2 + %6 = OpLabel + %7 = OpUndef %4 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("ID 4[%float_0] is not a type " + "id")); +} + +TEST_F(ValidateIdWithMessage, VulkanMemoryModelLoadMakePointerVisibleGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypeFunction %1 +%6 = OpConstant %2 2 +%7 = OpFunction %1 None %5 +%8 = OpLabel +%9 = OpLoad %2 %4 NonPrivatePointerKHR|MakePointerVisibleKHR %6 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelLoadMakePointerVisibleMissingNonPrivatePointer) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypeFunction %1 +%6 = OpConstant %2 2 +%7 = OpFunction %1 None %5 +%8 = OpLabel +%9 = OpLoad %2 %4 MakePointerVisibleKHR %6 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR must be specified if " + "MakePointerVisibleKHR is specified.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelLoadNonPrivatePointerBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Private %2 +%4 = OpVariable %3 Private +%5 = OpTypeFunction %1 +%6 = OpConstant %2 2 +%7 = OpFunction %1 None %5 +%8 = OpLabel +%9 = OpLoad %2 %4 NonPrivatePointerKHR +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelLoadMakePointerAvailableCannotBeUsed) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypeFunction %1 +%6 = OpConstant %2 2 +%7 = OpFunction %1 None %5 +%8 = OpLabel +%9 = OpLoad %2 %4 NonPrivatePointerKHR|MakePointerAvailableKHR %6 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MakePointerAvailableKHR cannot be used with OpLoad")); +} + +TEST_F(ValidateIdWithMessage, VulkanMemoryModelStoreMakePointerAvailableGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpVariable %3 Uniform +%5 = OpTypeFunction %1 +%6 = OpConstant %2 5 +%7 = OpFunction %1 None %5 +%8 = OpLabel +OpStore %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR %6 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelStoreMakePointerAvailableMissingNonPrivatePointer) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpVariable %3 Uniform +%5 = OpTypeFunction %1 +%6 = OpConstant %2 5 +%7 = OpFunction %1 None %5 +%8 = OpLabel +OpStore %4 %6 MakePointerAvailableKHR %6 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR must be specified if " + "MakePointerAvailableKHR is specified.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelStoreNonPrivatePointerBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Output %2 +%4 = OpVariable %3 Output +%5 = OpTypeFunction %1 +%6 = OpConstant %2 5 +%7 = OpFunction %1 None %5 +%8 = OpLabel +OpStore %4 %6 NonPrivatePointerKHR +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelStoreMakePointerVisibleCannotBeUsed) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Uniform %2 +%4 = OpVariable %3 Uniform +%5 = OpTypeFunction %1 +%6 = OpConstant %2 5 +%7 = OpFunction %1 None %5 +%8 = OpLabel +OpStore %4 %6 NonPrivatePointerKHR|MakePointerVisibleKHR %6 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MakePointerVisibleKHR cannot be used with OpStore.")); +} + +TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailable) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR %7 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryVisible) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerVisibleKHR %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemoryAvailableAndVisible) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemory %4 %6 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemoryAvailableMissingNonPrivatePointer) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemory %4 %6 MakePointerAvailableKHR %7 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR must be specified if " + "MakePointerAvailableKHR is specified.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemoryVisibleMissingNonPrivatePointer) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemory %4 %6 MakePointerVisibleKHR %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR must be specified if " + "MakePointerVisibleKHR is specified.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemoryAvailableBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Output %2 +%4 = OpVariable %3 Output +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemory %4 %6 NonPrivatePointerKHR +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemoryVisibleBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Input %2 +%6 = OpVariable %5 Input +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemory %4 %6 NonPrivatePointerKHR +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes.")); +} + +TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedAvailable) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR %7 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, VulkanMemoryModelCopyMemorySizedVisible) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerVisibleKHR %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemorySizedAvailableAndVisible) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR|MakePointerAvailableKHR|MakePointerVisibleKHR %7 %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemorySizedAvailableMissingNonPrivatePointer) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemorySized %4 %6 %7 MakePointerAvailableKHR %7 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR must be specified if " + "MakePointerAvailableKHR is specified.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemorySizedVisibleMissingNonPrivatePointer) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemorySized %4 %6 %7 MakePointerVisibleKHR %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR must be specified if " + "MakePointerVisibleKHR is specified.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemorySizedAvailableBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Output %2 +%4 = OpVariable %3 Output +%5 = OpTypePointer Uniform %2 +%6 = OpVariable %5 Uniform +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes.")); +} + +TEST_F(ValidateIdWithMessage, + VulkanMemoryModelCopyMemorySizedVisibleBadStorageClass) { + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%1 = OpTypeVoid +%2 = OpTypeInt 32 0 +%3 = OpTypePointer Workgroup %2 +%4 = OpVariable %3 Workgroup +%5 = OpTypePointer Input %2 +%6 = OpVariable %5 Input +%7 = OpConstant %2 2 +%8 = OpConstant %2 5 +%9 = OpTypeFunction %1 +%10 = OpFunction %1 None %9 +%11 = OpLabel +OpCopyMemorySized %4 %6 %7 NonPrivatePointerKHR +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("NonPrivatePointerKHR requires a pointer in Uniform, " + "Workgroup, CrossWorkgroup, Generic, Image or " + "StorageBuffer storage classes.")); +} + +TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock1) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeFloat 32 +%4 = OpTypeFunction %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +OpReturn +%7 = OpLabel +%8 = OpFunctionCall %3 %9 +OpUnreachable +OpFunctionEnd +%9 = OpFunction %3 None %4 +%10 = OpLabel +OpReturnValue %8 +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 8[%8] defined in block 7[%7] does not dominate its " + "use in block 10[%10]\n %10 = OpLabel")); +} + +TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock2) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeFloat 32 +%4 = OpTypeFunction %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +OpReturn +%7 = OpLabel +%8 = OpFunctionCall %3 %9 +OpUnreachable +OpFunctionEnd +%9 = OpFunction %3 None %4 +%10 = OpLabel +OpReturnValue %8 +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 8[%8] defined in block 7[%7] does not dominate its " + "use in block 10[%10]\n %10 = OpLabel")); +} + +TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock3) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeFloat 32 +%4 = OpTypeFunction %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +OpReturn +%7 = OpLabel +%8 = OpFunctionCall %3 %9 +OpReturn +OpFunctionEnd +%9 = OpFunction %3 None %4 +%10 = OpLabel +OpReturnValue %8 +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 8[%8] defined in block 7[%7] does not dominate its " + "use in block 10[%10]\n %10 = OpLabel")); +} + +TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock4) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeFloat 32 +%4 = OpTypeFunction %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +OpReturn +%7 = OpLabel +%8 = OpUndef %3 +%9 = OpCopyObject %3 %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock5) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeFloat 32 +%4 = OpTypeFunction %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +OpReturn +%7 = OpLabel +%8 = OpUndef %3 +OpBranch %9 +%9 = OpLabel +%10 = OpCopyObject %3 %8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, IdDefInUnreachableBlock6) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeFloat 32 +%4 = OpTypeFunction %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +OpBranch %7 +%8 = OpLabel +%9 = OpUndef %3 +OpBranch %7 +%7 = OpLabel +%10 = OpCopyObject %3 %9 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 9[%9] defined in block 8[%8] does not dominate its " + "use in block 7[%7]\n %7 = OpLabel")); +} + +TEST_F(ValidateIdWithMessage, ReachableDefUnreachableUse) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeFloat 32 +%4 = OpTypeFunction %3 +%5 = OpFunction %1 None %2 +%6 = OpLabel +%7 = OpUndef %3 +OpReturn +%8 = OpLabel +%9 = OpCopyObject %3 %7 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateIdWithMessage, UnreachableDefUsedInPhi) { + const std::string spirv = kNoKernelGLSL450MemoryModel + R"( + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %bool = OpTypeBool + %6 = OpTypeFunction %float + %1 = OpFunction %void None %3 + %7 = OpLabel + %8 = OpUndef %bool + OpSelectionMerge %9 None + OpBranchConditional %8 %10 %9 + %10 = OpLabel + %11 = OpUndef %float + OpBranch %9 + %12 = OpLabel + %13 = OpUndef %float + OpUnreachable + %9 = OpLabel + %14 = OpPhi %float %11 %10 %13 %7 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In OpPhi instruction 14[%14], ID 13[%13] definition does not " + "dominate its parent 7[%7]\n %14 = OpPhi %float %11 %10 %13 " + "%7")); +} + +TEST_F(ValidateIdWithMessage, OpTypeForwardPointerNotAPointerType) { + std::string spirv = R"( + OpCapability GenericPointer + OpCapability VariablePointersStorageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginLowerLeft + OpTypeForwardPointer %2 CrossWorkgroup +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%1 = OpFunction %2 DontInline %3 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Pointer type in OpTypeForwardPointer is not a pointer " + "type.\n OpTypeForwardPointer %void CrossWorkgroup")); +} + +TEST_F(ValidateIdWithMessage, OpTypeForwardPointerWrongStorageClass) { + std::string spirv = R"( + OpCapability GenericPointer + OpCapability VariablePointersStorageBuffer + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginLowerLeft + OpTypeForwardPointer %2 CrossWorkgroup +%int = OpTypeInt 32 1 +%2 = OpTypePointer Function %int +%void = OpTypeVoid +%3 = OpTypeFunction %void +%1 = OpFunction %void None %3 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Storage class in OpTypeForwardPointer does not match the " + "pointer definition.\n OpTypeForwardPointer " + "%_ptr_Function_int CrossWorkgroup")); +} + +TEST_F(ValidateIdWithMessage, MissingForwardPointer) { + const std::string spirv = R"( + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical Simple + %float = OpTypeFloat 32 + %_struct_9 = OpTypeStruct %float %_ptr_Uniform__struct_9 +%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9 + %1278 = OpVariable %_ptr_Uniform__struct_9 Uniform +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Operand 3[%_ptr_Uniform__struct_2] requires a previous definition")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_image_test.cpp b/third_party/spirv-tools/test/val/val_image_test.cpp new file mode 100644 index 0000000..2e84a29 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_image_test.cpp @@ -0,0 +1,5178 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for unique type declaration rules validator. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateImage = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "Fragment", + const std::string& execution_mode = "", + const spv_target_env env = SPV_ENV_UNIVERSAL_1_0, + const std::string& memory_model = "GLSL450", + const std::string& declarations = "") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability InputAttachment +OpCapability ImageGatherExtended +OpCapability MinLod +OpCapability Sampled1D +OpCapability ImageQuery +OpCapability Int64 +OpCapability Float64 +OpCapability SparseResidency +OpCapability ImageBuffer +)"; + + if (env == SPV_ENV_UNIVERSAL_1_0) { + ss << "OpCapability SampledRect\n"; + } + + // In 1.4, the entry point must list all module-scope variables used. Just + // list all of them. + std::string interface_vars = (env != SPV_ENV_UNIVERSAL_1_4) ? "" : + R"( +%uniform_image_f32_1d_0001 +%uniform_image_f32_1d_0002_rgba32f +%uniform_image_f32_2d_0001 +%uniform_image_f32_2d_0010 +%uniform_image_u32_2d_0001 +%uniform_image_u32_2d_0000 +%uniform_image_s32_3d_0001 +%uniform_image_f32_2d_0002 +%uniform_image_s32_2d_0002 +%uniform_image_f32_spd_0002 +%uniform_image_f32_3d_0111 +%uniform_image_f32_cube_0101 +%uniform_image_f32_cube_0102_rgba32f +%uniform_sampler +%private_image_u32_buffer_0002_r32ui +%private_image_u32_spd_0002 +%private_image_f32_buffer_0002_r32ui +)"; + + ss << capabilities_and_extensions; + ss << "OpMemoryModel Logical " << memory_model << "\n"; + ss << "OpEntryPoint " << execution_model + << " %main \"main\" " + interface_vars + "\n"; + if (execution_model == "Fragment") { + ss << "OpExecutionMode %main OriginUpperLeft\n"; + } + ss << execution_mode; + + if (env == SPV_ENV_VULKAN_1_0) { + ss << R"( +OpDecorate %uniform_image_f32_1d_0001 DescriptorSet 0 +OpDecorate %uniform_image_f32_1d_0001 Binding 0 +OpDecorate %uniform_image_f32_1d_0002_rgba32f DescriptorSet 0 +OpDecorate %uniform_image_f32_1d_0002_rgba32f Binding 1 +OpDecorate %uniform_image_f32_2d_0001 DescriptorSet 0 +OpDecorate %uniform_image_f32_2d_0001 Binding 2 +OpDecorate %uniform_image_f32_2d_0010 DescriptorSet 0 +OpDecorate %uniform_image_f32_2d_0010 Binding 3 +OpDecorate %uniform_image_u32_2d_0001 DescriptorSet 1 +OpDecorate %uniform_image_u32_2d_0001 Binding 0 +OpDecorate %uniform_image_u32_2d_0000 DescriptorSet 1 +OpDecorate %uniform_image_u32_2d_0000 Binding 1 +OpDecorate %uniform_image_s32_3d_0001 DescriptorSet 1 +OpDecorate %uniform_image_s32_3d_0001 Binding 2 +OpDecorate %uniform_image_f32_2d_0002 DescriptorSet 1 +OpDecorate %uniform_image_f32_2d_0002 Binding 3 +OpDecorate %uniform_image_s32_2d_0002 DescriptorSet 1 +OpDecorate %uniform_image_s32_2d_0002 Binding 4 +OpDecorate %uniform_image_f32_spd_0002 DescriptorSet 2 +OpDecorate %uniform_image_f32_spd_0002 Binding 0 +OpDecorate %uniform_image_f32_3d_0111 DescriptorSet 2 +OpDecorate %uniform_image_f32_3d_0111 Binding 1 +OpDecorate %uniform_image_f32_cube_0101 DescriptorSet 2 +OpDecorate %uniform_image_f32_cube_0101 Binding 2 +OpDecorate %uniform_image_f32_cube_0102_rgba32f DescriptorSet 2 +OpDecorate %uniform_image_f32_cube_0102_rgba32f Binding 3 +OpDecorate %uniform_sampler DescriptorSet 3 +OpDecorate %uniform_sampler Binding 0 +)"; + } + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%f64 = OpTypeFloat 64 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%u64 = OpTypeInt 64 0 +%s32vec2 = OpTypeVector %s32 2 +%u32vec2 = OpTypeVector %u32 2 +%f32vec2 = OpTypeVector %f32 2 +%u32vec3 = OpTypeVector %u32 3 +%s32vec3 = OpTypeVector %s32 3 +%f32vec3 = OpTypeVector %f32 3 +%u32vec4 = OpTypeVector %u32 4 +%s32vec4 = OpTypeVector %s32 4 +%f32vec4 = OpTypeVector %f32 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_0_5 = OpConstant %f32 0.5 +%f32_0_25 = OpConstant %f32 0.25 +%f32_0_75 = OpConstant %f32 0.75 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 +%s32_4 = OpConstant %s32 4 +%s32_m1 = OpConstant %s32 -1 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 + +%u32vec2arr4 = OpTypeArray %u32vec2 %u32_4 +%u32vec2arr3 = OpTypeArray %u32vec2 %u32_3 +%u32arr4 = OpTypeArray %u32 %u32_4 +%u32vec3arr4 = OpTypeArray %u32vec3 %u32_4 + +%struct_u32_f32vec4 = OpTypeStruct %u32 %f32vec4 +%struct_u64_f32vec4 = OpTypeStruct %u64 %f32vec4 +%struct_u32_u32vec4 = OpTypeStruct %u32 %u32vec4 +%struct_u32_f32vec3 = OpTypeStruct %u32 %f32vec3 +%struct_f32_f32vec4 = OpTypeStruct %f32 %f32vec4 +%struct_u32_u32 = OpTypeStruct %u32 %u32 +%struct_f32_f32 = OpTypeStruct %f32 %f32 +%struct_u32 = OpTypeStruct %u32 +%struct_u32_f32_u32 = OpTypeStruct %u32 %f32 %u32 +%struct_u32_f32vec4_u32 = OpTypeStruct %u32 %f32vec4 %u32 +%struct_u32_u32arr4 = OpTypeStruct %u32 %u32arr4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%s32vec3_012 = OpConstantComposite %s32vec3 %s32_0 %s32_1 %s32_2 +%s32vec3_123 = OpConstantComposite %s32vec3 %s32_1 %s32_2 %s32_3 +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4 + +%f32vec2_00 = OpConstantComposite %f32vec2 %f32_0 %f32_0 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_10 = OpConstantComposite %f32vec2 %f32_1 %f32_0 +%f32vec2_11 = OpConstantComposite %f32vec2 %f32_1 %f32_1 +%f32vec2_hh = OpConstantComposite %f32vec2 %f32_0_5 %f32_0_5 + +%f32vec3_000 = OpConstantComposite %f32vec3 %f32_0 %f32_0 %f32_0 +%f32vec3_hhh = OpConstantComposite %f32vec3 %f32_0_5 %f32_0_5 %f32_0_5 + +%f32vec4_0000 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0 + +%const_offsets = OpConstantComposite %u32vec2arr4 %u32vec2_01 %u32vec2_12 %u32vec2_01 %u32vec2_12 +%const_offsets3x2 = OpConstantComposite %u32vec2arr3 %u32vec2_01 %u32vec2_12 %u32vec2_01 +%const_offsets4xu = OpConstantComposite %u32arr4 %u32_0 %u32_0 %u32_0 %u32_0 +%const_offsets4x3 = OpConstantComposite %u32vec3arr4 %u32vec3_012 %u32vec3_012 %u32vec3_012 %u32vec3_012 + +%type_image_f32_1d_0001 = OpTypeImage %f32 1D 0 0 0 1 Unknown +%ptr_image_f32_1d_0001 = OpTypePointer UniformConstant %type_image_f32_1d_0001 +%uniform_image_f32_1d_0001 = OpVariable %ptr_image_f32_1d_0001 UniformConstant +%type_sampled_image_f32_1d_0001 = OpTypeSampledImage %type_image_f32_1d_0001 + +%type_image_f32_1d_0002_rgba32f = OpTypeImage %f32 1D 0 0 0 2 Rgba32f +%ptr_image_f32_1d_0002_rgba32f = OpTypePointer UniformConstant %type_image_f32_1d_0002_rgba32f +%uniform_image_f32_1d_0002_rgba32f = OpVariable %ptr_image_f32_1d_0002_rgba32f UniformConstant +%type_sampled_image_f32_1d_0002_rgba32f = OpTypeSampledImage %type_image_f32_1d_0002_rgba32f + +%type_image_f32_2d_0001 = OpTypeImage %f32 2D 0 0 0 1 Unknown +%ptr_image_f32_2d_0001 = OpTypePointer UniformConstant %type_image_f32_2d_0001 +%uniform_image_f32_2d_0001 = OpVariable %ptr_image_f32_2d_0001 UniformConstant +%type_sampled_image_f32_2d_0001 = OpTypeSampledImage %type_image_f32_2d_0001 + +%type_image_f32_2d_0010 = OpTypeImage %f32 2D 0 0 1 0 Unknown +%ptr_image_f32_2d_0010 = OpTypePointer UniformConstant %type_image_f32_2d_0010 +%uniform_image_f32_2d_0010 = OpVariable %ptr_image_f32_2d_0010 UniformConstant +%type_sampled_image_f32_2d_0010 = OpTypeSampledImage %type_image_f32_2d_0010 + +%type_image_u32_2d_0001 = OpTypeImage %u32 2D 0 0 0 1 Unknown +%ptr_image_u32_2d_0001 = OpTypePointer UniformConstant %type_image_u32_2d_0001 +%uniform_image_u32_2d_0001 = OpVariable %ptr_image_u32_2d_0001 UniformConstant +%type_sampled_image_u32_2d_0001 = OpTypeSampledImage %type_image_u32_2d_0001 + +%type_image_u32_2d_0000 = OpTypeImage %u32 2D 0 0 0 0 Unknown +%ptr_image_u32_2d_0000 = OpTypePointer UniformConstant %type_image_u32_2d_0000 +%uniform_image_u32_2d_0000 = OpVariable %ptr_image_u32_2d_0000 UniformConstant +%type_sampled_image_u32_2d_0000 = OpTypeSampledImage %type_image_u32_2d_0000 + +%type_image_s32_3d_0001 = OpTypeImage %s32 3D 0 0 0 1 Unknown +%ptr_image_s32_3d_0001 = OpTypePointer UniformConstant %type_image_s32_3d_0001 +%uniform_image_s32_3d_0001 = OpVariable %ptr_image_s32_3d_0001 UniformConstant +%type_sampled_image_s32_3d_0001 = OpTypeSampledImage %type_image_s32_3d_0001 + +%type_image_f32_2d_0002 = OpTypeImage %f32 2D 0 0 0 2 Unknown +%ptr_image_f32_2d_0002 = OpTypePointer UniformConstant %type_image_f32_2d_0002 +%uniform_image_f32_2d_0002 = OpVariable %ptr_image_f32_2d_0002 UniformConstant +%type_sampled_image_f32_2d_0002 = OpTypeSampledImage %type_image_f32_2d_0002 + +%type_image_s32_2d_0002 = OpTypeImage %s32 2D 0 0 0 2 Unknown +%ptr_image_s32_2d_0002 = OpTypePointer UniformConstant %type_image_s32_2d_0002 +%uniform_image_s32_2d_0002 = OpVariable %ptr_image_s32_2d_0002 UniformConstant +%type_sampled_image_s32_2d_0002 = OpTypeSampledImage %type_image_s32_2d_0002 + +%type_image_f32_spd_0002 = OpTypeImage %f32 SubpassData 0 0 0 2 Unknown +%ptr_image_f32_spd_0002 = OpTypePointer UniformConstant %type_image_f32_spd_0002 +%uniform_image_f32_spd_0002 = OpVariable %ptr_image_f32_spd_0002 UniformConstant +%type_sampled_image_f32_spd_0002 = OpTypeSampledImage %type_image_f32_spd_0002 + +%type_image_f32_3d_0111 = OpTypeImage %f32 3D 0 1 1 1 Unknown +%ptr_image_f32_3d_0111 = OpTypePointer UniformConstant %type_image_f32_3d_0111 +%uniform_image_f32_3d_0111 = OpVariable %ptr_image_f32_3d_0111 UniformConstant +%type_sampled_image_f32_3d_0111 = OpTypeSampledImage %type_image_f32_3d_0111 + +%type_image_f32_cube_0101 = OpTypeImage %f32 Cube 0 1 0 1 Unknown +%ptr_image_f32_cube_0101 = OpTypePointer UniformConstant %type_image_f32_cube_0101 +%uniform_image_f32_cube_0101 = OpVariable %ptr_image_f32_cube_0101 UniformConstant +%type_sampled_image_f32_cube_0101 = OpTypeSampledImage %type_image_f32_cube_0101 + +%type_image_f32_cube_0102_rgba32f = OpTypeImage %f32 Cube 0 1 0 2 Rgba32f +%ptr_image_f32_cube_0102_rgba32f = OpTypePointer UniformConstant %type_image_f32_cube_0102_rgba32f +%uniform_image_f32_cube_0102_rgba32f = OpVariable %ptr_image_f32_cube_0102_rgba32f UniformConstant +%type_sampled_image_f32_cube_0102_rgba32f = OpTypeSampledImage %type_image_f32_cube_0102_rgba32f + +%type_sampler = OpTypeSampler +%ptr_sampler = OpTypePointer UniformConstant %type_sampler +%uniform_sampler = OpVariable %ptr_sampler UniformConstant + +%type_image_u32_buffer_0002_r32ui = OpTypeImage %u32 Buffer 0 0 0 2 R32ui +%ptr_Image_u32 = OpTypePointer Image %u32 +%ptr_image_u32_buffer_0002_r32ui = OpTypePointer Private %type_image_u32_buffer_0002_r32ui +%private_image_u32_buffer_0002_r32ui = OpVariable %ptr_image_u32_buffer_0002_r32ui Private + +%ptr_Image_u32arr4 = OpTypePointer Image %u32arr4 + +%type_image_u32_spd_0002 = OpTypeImage %u32 SubpassData 0 0 0 2 Unknown +%ptr_image_u32_spd_0002 = OpTypePointer Private %type_image_u32_spd_0002 +%private_image_u32_spd_0002 = OpVariable %ptr_image_u32_spd_0002 Private + +%type_image_f32_buffer_0002_r32ui = OpTypeImage %f32 Buffer 0 0 0 2 R32ui +%ptr_Image_f32 = OpTypePointer Image %f32 +%ptr_image_f32_buffer_0002_r32ui = OpTypePointer Private %type_image_f32_buffer_0002_r32ui +%private_image_f32_buffer_0002_r32ui = OpVariable %ptr_image_f32_buffer_0002_r32ui Private +)"; + + if (env == SPV_ENV_UNIVERSAL_1_0) { + ss << R"( +%type_image_void_2d_0001 = OpTypeImage %void 2D 0 0 0 1 Unknown +%ptr_image_void_2d_0001 = OpTypePointer UniformConstant %type_image_void_2d_0001 +%uniform_image_void_2d_0001 = OpVariable %ptr_image_void_2d_0001 UniformConstant +%type_sampled_image_void_2d_0001 = OpTypeSampledImage %type_image_void_2d_0001 + +%type_image_void_2d_0002 = OpTypeImage %void 2D 0 0 0 2 Unknown +%ptr_image_void_2d_0002 = OpTypePointer UniformConstant %type_image_void_2d_0002 +%uniform_image_void_2d_0002 = OpVariable %ptr_image_void_2d_0002 UniformConstant +%type_sampled_image_void_2d_0002 = OpTypeSampledImage %type_image_void_2d_0002 + +%type_image_f32_rect_0001 = OpTypeImage %f32 Rect 0 0 0 1 Unknown +%ptr_image_f32_rect_0001 = OpTypePointer UniformConstant %type_image_f32_rect_0001 +%uniform_image_f32_rect_0001 = OpVariable %ptr_image_f32_rect_0001 UniformConstant +%type_sampled_image_f32_rect_0001 = OpTypeSampledImage %type_image_f32_rect_0001 +)"; + } + + ss << declarations; + + ss << R"( +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +std::string GenerateKernelCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + std::ostringstream ss; + ss << R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability ImageQuery +OpCapability ImageGatherExtended +OpCapability InputAttachment +OpCapability SampledRect +)"; + + ss << capabilities_and_extensions; + ss << R"( +OpMemoryModel Physical32 OpenCL +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%u32vec2 = OpTypeVector %u32 2 +%f32vec2 = OpTypeVector %f32 2 +%u32vec3 = OpTypeVector %u32 3 +%f32vec3 = OpTypeVector %f32 3 +%u32vec4 = OpTypeVector %u32 4 +%f32vec4 = OpTypeVector %f32 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_0_5 = OpConstant %f32 0.5 +%f32_0_25 = OpConstant %f32 0.25 +%f32_0_75 = OpConstant %f32 0.75 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%f32vec2_00 = OpConstantComposite %f32vec2 %f32_0 %f32_0 +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_10 = OpConstantComposite %f32vec2 %f32_1 %f32_0 +%f32vec2_11 = OpConstantComposite %f32vec2 %f32_1 %f32_1 +%f32vec2_hh = OpConstantComposite %f32vec2 %f32_0_5 %f32_0_5 + +%f32vec3_000 = OpConstantComposite %f32vec3 %f32_0 %f32_0 %f32_0 +%f32vec3_hhh = OpConstantComposite %f32vec3 %f32_0_5 %f32_0_5 %f32_0_5 + +%f32vec4_0000 = OpConstantComposite %f32vec4 %f32_0 %f32_0 %f32_0 %f32_0 + +%type_image_f32_2d_0001 = OpTypeImage %f32 2D 0 0 0 1 Unknown +%ptr_image_f32_2d_0001 = OpTypePointer UniformConstant %type_image_f32_2d_0001 +%uniform_image_f32_2d_0001 = OpVariable %ptr_image_f32_2d_0001 UniformConstant +%type_sampled_image_f32_2d_0001 = OpTypeSampledImage %type_image_f32_2d_0001 + +%type_image_f32_2d_0010 = OpTypeImage %f32 2D 0 0 1 0 Unknown +%ptr_image_f32_2d_0010 = OpTypePointer UniformConstant %type_image_f32_2d_0010 +%uniform_image_f32_2d_0010 = OpVariable %ptr_image_f32_2d_0010 UniformConstant +%type_sampled_image_f32_2d_0010 = OpTypeSampledImage %type_image_f32_2d_0010 + +%type_image_f32_3d_0010 = OpTypeImage %f32 3D 0 0 1 0 Unknown +%ptr_image_f32_3d_0010 = OpTypePointer UniformConstant %type_image_f32_3d_0010 +%uniform_image_f32_3d_0010 = OpVariable %ptr_image_f32_3d_0010 UniformConstant +%type_sampled_image_f32_3d_0010 = OpTypeSampledImage %type_image_f32_3d_0010 + +%type_image_f32_rect_0001 = OpTypeImage %f32 Rect 0 0 0 1 Unknown +%ptr_image_f32_rect_0001 = OpTypePointer UniformConstant %type_image_f32_rect_0001 +%uniform_image_f32_rect_0001 = OpVariable %ptr_image_f32_rect_0001 UniformConstant +%type_sampled_image_f32_rect_0001 = OpTypeSampledImage %type_image_f32_rect_0001 + +%type_sampler = OpTypeSampler +%ptr_sampler = OpTypePointer UniformConstant %type_sampler +%uniform_sampler = OpVariable %ptr_sampler UniformConstant + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +std::string GetShaderHeader(const std::string& capabilities_and_extensions = "", + bool include_entry_point = true) { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability Int64 +)"; + + ss << capabilities_and_extensions; + if (!include_entry_point) { + ss << "OpCapability Linkage"; + } + + ss << R"( +OpMemoryModel Logical GLSL450 +)"; + + if (include_entry_point) { + ss << "OpEntryPoint Fragment %main \"main\"\n"; + ss << "OpExecutionMode %main OriginUpperLeft"; + } + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%u64 = OpTypeInt 64 0 +%s32 = OpTypeInt 32 1 +)"; + + return ss.str(); +} + +TEST_F(ValidateImage, TypeImageWrongSampledType) { + const std::string code = GetShaderHeader("", false) + R"( +%img_type = OpTypeImage %bool 2D 0 0 0 1 Unknown +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sampled Type to be either void or " + "numerical scalar " + "type")); +} + +TEST_F(ValidateImage, TypeImageVoidSampledTypeVulkan) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %void 2D 0 0 0 1 Unknown +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sampled Type to be a 32-bit int " + "or float scalar type for Vulkan environment")); +} + +TEST_F(ValidateImage, TypeImageU64SampledTypeVulkan) { + const std::string code = GetShaderHeader() + R"( +%img_type = OpTypeImage %u64 2D 0 0 0 1 Unknown +%void_func = OpTypeFunction %void +%main = OpFunction %void None %void_func +%main_lab = OpLabel +OpReturn +OpFunctionEnd +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(code, env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sampled Type to be a 32-bit int " + "or float scalar type for Vulkan environment")); +} + +TEST_F(ValidateImage, TypeImageWrongDepth) { + const std::string code = GetShaderHeader("", false) + R"( +%img_type = OpTypeImage %f32 2D 3 0 0 1 Unknown +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid Depth 3 (must be 0, 1 or 2)")); +} + +TEST_F(ValidateImage, TypeImageWrongArrayed) { + const std::string code = GetShaderHeader("", false) + R"( +%img_type = OpTypeImage %f32 2D 0 2 0 1 Unknown +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid Arrayed 2 (must be 0 or 1)")); +} + +TEST_F(ValidateImage, TypeImageWrongMS) { + const std::string code = GetShaderHeader("", false) + R"( +%img_type = OpTypeImage %f32 2D 0 0 2 1 Unknown +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid MS 2 (must be 0 or 1)")); +} + +TEST_F(ValidateImage, TypeImageWrongSampled) { + const std::string code = GetShaderHeader("", false) + R"( +%img_type = OpTypeImage %f32 2D 0 0 0 3 Unknown +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid Sampled 3 (must be 0, 1 or 2)")); +} + +TEST_F(ValidateImage, TypeImageWrongSampledForSubpassData) { + const std::string code = + GetShaderHeader("OpCapability InputAttachment\n", false) + + R"( +%img_type = OpTypeImage %f32 SubpassData 0 0 0 1 Unknown +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dim SubpassData requires Sampled to be 2")); +} + +TEST_F(ValidateImage, TypeImageWrongFormatForSubpassData) { + const std::string code = + GetShaderHeader("OpCapability InputAttachment\n", false) + + R"( +%img_type = OpTypeImage %f32 SubpassData 0 0 0 2 Rgba32f +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dim SubpassData requires format Unknown")); +} + +TEST_F(ValidateImage, TypeSampledImageNotImage) { + const std::string code = GetShaderHeader("", false) + R"( +%simg_type = OpTypeSampledImage %f32 +)"; + + CompileSuccessfully(code.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, SampledImageSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampledImageVulkanSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env), env); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateImage, SampledImageWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_image_f32_2d_0001 %img %sampler +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampledImageNotImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg1 = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%simg2 = OpSampledImage %type_sampled_image_f32_2d_0001 %simg1 %sampler +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, SampledImageImageNotForSampling) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0002 %img %sampler +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled' parameter to be 0 or 1")); +} + +TEST_F(ValidateImage, SampledImageVulkanUnknownSampled) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_2d_0000 %img %sampler +)"; + + const spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env), env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled' parameter to " + "be 1 for Vulkan environment.")); +} + +TEST_F(ValidateImage, SampledImageNotSampler) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %img +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sampler to be of type OpTypeSampler")); +} + +TEST_F(ValidateImage, ImageTexelPointerSuccess) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ImageTexelPointerResultTypeNotPointer) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %type_image_u32_buffer_0002_r32ui %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypePointer")); +} + +TEST_F(ValidateImage, ImageTexelPointerResultTypeNotImageClass) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_image_f32_cube_0101 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypePointer whose " + "Storage Class operand is Image")); +} + +TEST_F(ValidateImage, ImageTexelPointerResultTypeNotNumericNorVoid) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32arr4 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypePointer whose Type operand " + "must be a scalar numerical type or OpTypeVoid")); +} + +TEST_F(ValidateImage, ImageTexelPointerImageNotResultTypePointer) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %type_image_f32_buffer_0002_r32ui %u32_0 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 141[%141] cannot be a " + "type")); +} + +TEST_F(ValidateImage, ImageTexelPointerImageNotImage) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %uniform_sampler %u32_0 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image to be OpTypePointer with Type OpTypeImage")); +} + +TEST_F(ValidateImage, ImageTexelPointerImageSampledNotResultType) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %uniform_image_f32_cube_0101 %u32_0 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as the " + "Type pointed to by Result Type")); +} + +TEST_F(ValidateImage, ImageTexelPointerImageDimSubpassDataBad) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_spd_0002 %u32_0 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Dim SubpassData cannot be used with OpImageTexelPointer")); +} + +TEST_F(ValidateImage, ImageTexelPointerImageCoordTypeBad) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_f32 %private_image_f32_buffer_0002_r32ui %f32_0 %f32_0 +%sum = OpAtomicIAdd %f32 %texel_ptr %f32_1 %f32_0 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be integer scalar or vector")); +} + +TEST_F(ValidateImage, ImageTexelPointerImageCoordSizeBad) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %uniform_image_u32_2d_0000 %u32vec3_012 %u32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Coordinate to have 2 components, but given 3")); +} + +TEST_F(ValidateImage, ImageTexelPointerSampleNotIntScalar) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_buffer_0002_r32ui %u32_0 %f32_0 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sample to be integer scalar")); +} + +TEST_F(ValidateImage, ImageTexelPointerSampleNotZeroForImageWithMSZero) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u32 %private_image_u32_buffer_0002_r32ui %u32_0 %u32_1 +%sum = OpAtomicIAdd %u32 %texel_ptr %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sample for Image with MS 0 to be a valid " + " for the value 0")); +} + +TEST_F(ValidateImage, SampleImplicitLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Bias %f32_0_25 +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh ConstOffset %s32vec2_01 +%res5 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Offset %s32vec2_01 +%res6 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh MinLod %f32_0_5 +%res7 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Bias|Offset|MinLod %f32_0_25 %s32vec2_01 %f32_0_5 +%res8 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SampleImplicitLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float vector type")); +} + +TEST_F(ValidateImage, SampleImplicitLodWrongNumComponentsResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec3 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateImage, SampleImplicitLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageSampleImplicitLod %f32vec4 %img %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampleImplicitLodWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %u32vec4 %simg %f32vec2_00 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, SampleImplicitLodVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %u32vec4 %simg %f32vec2_00 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleImplicitLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %img +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, SampleImplicitLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 2 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, SampleExplicitLodSuccessShader) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Lod %f32_1 +%res2 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_hh Grad %f32vec2_10 %f32vec2_01 +%res3 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_hh ConstOffset %s32vec2_01 +%res4 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec3_hhh Offset %s32vec2_01 +%res5 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_hh Grad|Offset|MinLod %f32vec2_10 %f32vec2_01 %s32vec2_01 %f32_0_5 +%res6 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Lod|NonPrivateTexelKHR %f32_1 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SampleExplicitLodSuccessKernel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %u32vec4_0123 Lod %f32_1 +%res2 = OpImageSampleExplicitLod %f32vec4 %simg %u32vec2_01 Grad %f32vec2_10 %f32vec2_01 +%res3 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_hh ConstOffset %u32vec2_01 +%res4 = OpImageSampleExplicitLod %f32vec4 %simg %u32vec2_01 Offset %u32vec2_01 +%res5 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_hh Grad|Offset %f32vec2_10 %f32vec2_01 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleExplicitLodSuccessCubeArrayed) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %f32vec3_hhh %f32vec3_hhh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleExplicitLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32 %simg %f32vec2_hh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float vector type")); +} + +TEST_F(ValidateImage, SampleExplicitLodWrongNumComponentsResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec3 %simg %f32vec2_hh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateImage, SampleExplicitLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageSampleExplicitLod %f32vec4 %img %f32vec2_hh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampleExplicitLodWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %u32vec4 %simg %f32vec2_00 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, SampleExplicitLodVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %u32vec4 %simg %f32vec2_00 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleExplicitLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %img Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, SampleExplicitLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32_0_5 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 2 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, SampleExplicitLodBias) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Bias|Lod %f32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Operand Bias can only be used with ImplicitLod opcodes")); +} + +TEST_F(ValidateImage, LodAndGrad) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Lod|Grad %f32_1 %f32vec2_hh %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Operand bits Lod and Grad cannot be set at the same time")); +} + +TEST_F(ValidateImage, ImplicitLodWithLod) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Lod %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operand Lod can only be used with ExplicitLod opcodes " + "and OpImageFetch")); +} + +TEST_F(ValidateImage, LodWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Lod %f32vec2_hh)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand Lod to be float scalar when " + "used with ExplicitLod")); +} + +TEST_F(ValidateImage, LodWrongDim) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_rect_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Lod %f32_0)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod requires 'Dim' parameter to be 1D, " + "2D, 3D or Cube")); +} + +TEST_F(ValidateImage, LodMultisampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0010 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Lod %f32_0)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod requires 'MS' parameter to be 0")); +} + +TEST_F(ValidateImage, MinLodIncompatible) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Lod|MinLod %f32_0 %f32_0)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Operand MinLod can only be used with ImplicitLod opcodes or " + "together with Image Operand Grad")); +} + +TEST_F(ValidateImage, ImplicitLodWithGrad) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Grad %f32vec2_hh %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Operand Grad can only be used with ExplicitLod opcodes")); +} + +TEST_F(ValidateImage, SampleImplicitLod3DArrayedMultisampledSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %s32vec3_012 +%res3 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec3_012 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleImplicitLodCubeArrayedSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Bias %f32_0_25 +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 MinLod %f32_0_5 +%res5 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Bias|MinLod %f32_0_25 %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleImplicitLodBiasWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Bias %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand Bias to be float scalar")); +} + +TEST_F(ValidateImage, SampleImplicitLodBiasWrongDim) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_rect_0001 %img %sampler +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh Bias %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Bias requires 'Dim' parameter to be 1D, " + "2D, 3D or Cube")); +} + +TEST_F(ValidateImage, SampleImplicitLodBiasMultisampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Bias %f32_0_25 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Bias requires 'MS' parameter to be 0")); +} + +TEST_F(ValidateImage, SampleExplicitLodGradDxWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %s32vec3_012 %f32vec3_hhh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected both Image Operand Grad ids to be float " + "scalars or vectors")); +} + +TEST_F(ValidateImage, SampleExplicitLodGradDyWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %f32vec3_hhh %s32vec3_012 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected both Image Operand Grad ids to be float " + "scalars or vectors")); +} + +TEST_F(ValidateImage, SampleExplicitLodGradDxWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %f32vec2_00 %f32vec3_hhh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand Grad dx to have 3 components, but given 2")); +} + +TEST_F(ValidateImage, SampleExplicitLodGradDyWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %f32vec3_hhh %f32vec2_00 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand Grad dy to have 3 components, but given 2")); +} + +TEST_F(ValidateImage, SampleExplicitLodGradMultisampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec4_0000 Grad %f32vec3_000 %f32vec3_000 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Grad requires 'MS' parameter to be 0")); +} + +TEST_F(ValidateImage, SampleImplicitLodConstOffsetCubeDim) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %s32vec3_012 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Operand ConstOffset cannot be used with Cube Image 'Dim'")); +} + +TEST_F(ValidateImage, SampleImplicitLodConstOffsetWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %f32vec3_000 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand ConstOffset to be int scalar or vector")); +} + +TEST_F(ValidateImage, SampleImplicitLodConstOffsetWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %s32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand ConstOffset to have 3 " + "components, but given 2")); +} + +TEST_F(ValidateImage, SampleImplicitLodConstOffsetNotConst) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%offset = OpSNegate %s32vec3 %s32vec3_012 +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset %offset +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand ConstOffset to be a const object")); +} + +TEST_F(ValidateImage, SampleImplicitLodOffsetCubeDim) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec3_012 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operand Offset cannot be used with Cube Image 'Dim'")); +} + +TEST_F(ValidateImage, SampleImplicitLodOffsetWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %f32vec3_000 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand Offset to be int scalar or vector")); +} + +TEST_F(ValidateImage, SampleImplicitLodOffsetWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 Offset %s32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand Offset to have 3 components, but given 2")); +} + +TEST_F(ValidateImage, SampleImplicitLodMoreThanOneOffset) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res4 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 ConstOffset|Offset %s32vec3_012 %s32vec3_012 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operands Offset, ConstOffset, ConstOffsets " + "cannot be used together")); +} + +TEST_F(ValidateImage, SampleImplicitLodMinLodWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 MinLod %s32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand MinLod to be float scalar")); +} + +TEST_F(ValidateImage, SampleImplicitLodMinLodWrongDim) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_rect_0001 %img %sampler +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh MinLod %f32_0_25 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand MinLod requires 'Dim' parameter to be " + "1D, 2D, 3D or Cube")); +} + +TEST_F(ValidateImage, SampleImplicitLodMinLodMultisampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_3d_0111 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec4_0000 MinLod %f32_0_25 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operand MinLod requires 'MS' parameter to be 0")); +} + +TEST_F(ValidateImage, SampleProjExplicitLodSuccess2D) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec3_hhh Lod %f32_1 +%res3 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec3_hhh Grad %f32vec2_10 %f32vec2_01 +%res4 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec3_hhh ConstOffset %s32vec2_01 +%res5 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec3_hhh Offset %s32vec2_01 +%res7 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec3_hhh Grad|Offset %f32vec2_10 %f32vec2_01 %s32vec2_01 +%res8 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec3_hhh Lod|NonPrivateTexelKHR %f32_1 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SampleProjExplicitLodSuccessRect) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_rect_0001 %img %sampler +%res1 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec3_hhh Grad %f32vec2_10 %f32vec2_01 +%res2 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec3_hhh Grad|Offset %f32vec2_10 %f32vec2_01 %s32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleProjExplicitLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjExplicitLod %f32 %simg %f32vec3_hhh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float vector type")); +} + +TEST_F(ValidateImage, SampleProjExplicitLodWrongNumComponentsResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjExplicitLod %f32vec3 %simg %f32vec3_hhh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateImage, SampleProjExplicitLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageSampleProjExplicitLod %f32vec4 %img %f32vec3_hhh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampleProjExplicitLodWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjExplicitLod %u32vec4 %simg %f32vec3_hhh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, SampleProjExplicitLodVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleProjExplicitLod %u32vec4 %simg %f32vec3_hhh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleProjExplicitLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjExplicitLod %f32vec4 %simg %img Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, SampleProjExplicitLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjExplicitLod %f32vec4 %simg %f32vec2_hh Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 3 components, " + "but given only 2")); +} + +TEST_F(ValidateImage, SampleProjImplicitLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec3_hhh +%res2 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec3_hhh Bias %f32_0_25 +%res4 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec3_hhh ConstOffset %s32vec2_01 +%res5 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec3_hhh Offset %s32vec2_01 +%res6 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec3_hhh MinLod %f32_0_5 +%res7 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec3_hhh Bias|Offset|MinLod %f32_0_25 %s32vec2_01 %f32_0_5 +%res8 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec3_hhh NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SampleProjImplicitLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjImplicitLod %f32 %simg %f32vec3_hhh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float vector type")); +} + +TEST_F(ValidateImage, SampleProjImplicitLodWrongNumComponentsResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjImplicitLod %f32vec3 %simg %f32vec3_hhh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateImage, SampleProjImplicitLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageSampleProjImplicitLod %f32vec4 %img %f32vec3_hhh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampleProjImplicitLodWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjImplicitLod %u32vec4 %simg %f32vec3_hhh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, SampleProjImplicitLodVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleProjImplicitLod %u32vec4 %simg %f32vec3_hhh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleProjImplicitLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjImplicitLod %f32vec4 %simg %img +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, SampleProjImplicitLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjImplicitLod %f32vec4 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 3 components, " + "but given only 2")); +} + +TEST_F(ValidateImage, SampleDrefImplicitLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_2d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_hh %f32_1 +%res2 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_hh %f32_1 Bias %f32_0_25 +%res4 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_hh %f32_1 ConstOffset %s32vec2_01 +%res5 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_hh %f32_1 Offset %s32vec2_01 +%res6 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_hh %f32_1 MinLod %f32_0_5 +%res7 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_hh %f32_1 Bias|Offset|MinLod %f32_0_25 %s32vec2_01 %f32_0_5 +%res8 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_hh %f32_1 NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SampleDrefImplicitLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %void %simg %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float scalar type")); +} + +TEST_F(ValidateImage, SampleDrefImplicitLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 +%res1 = OpImageSampleDrefImplicitLod %u32 %img %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampleDrefImplicitLodWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_2d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %f32 %simg %f32vec2_00 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as Result Type")); +} + +TEST_F(ValidateImage, SampleDrefImplicitLodVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_00 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as Result Type")); +} + +TEST_F(ValidateImage, SampleDrefImplicitLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_2d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %u32 %simg %img %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, SampleDrefImplicitLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %f32 %simg %f32_0_5 %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 2 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, SampleDrefImplicitLodWrongDrefType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_2d_0001 %img %sampler +%res1 = OpImageSampleDrefImplicitLod %u32 %simg %f32vec2_00 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Dref to be of 32-bit float type")); +} + +TEST_F(ValidateImage, SampleDrefExplicitLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_s32_3d_0001 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %s32 %simg %f32vec4_0000 %f32_1 Lod %f32_1 +%res3 = OpImageSampleDrefExplicitLod %s32 %simg %f32vec3_hhh %f32_1 Grad %f32vec3_hhh %f32vec3_hhh +%res4 = OpImageSampleDrefExplicitLod %s32 %simg %f32vec3_hhh %f32_1 ConstOffset %s32vec3_012 +%res5 = OpImageSampleDrefExplicitLod %s32 %simg %f32vec4_0000 %f32_1 Offset %s32vec3_012 +%res7 = OpImageSampleDrefExplicitLod %s32 %simg %f32vec3_hhh %f32_1 Grad|Offset %f32vec3_hhh %f32vec3_hhh %s32vec3_012 +%res8 = OpImageSampleDrefExplicitLod %s32 %simg %f32vec4_0000 %f32_1 Lod|NonPrivateTexelKHR %f32_1 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SampleDrefExplicitLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_s32_3d_0001 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %bool %simg %f32vec3_hhh %s32_1 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float scalar type")); +} + +TEST_F(ValidateImage, SampleDrefExplicitLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 +%res1 = OpImageSampleDrefExplicitLod %s32 %img %f32vec3_hhh %s32_1 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampleDrefExplicitLodWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_s32_3d_0001 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %f32 %simg %f32vec3_hhh %s32_1 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as Result Type")); +} + +TEST_F(ValidateImage, SampleDrefExplicitLodVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %u32 %simg %f32vec2_00 %s32_1 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as Result Type")); +} + +TEST_F(ValidateImage, SampleDrefExplicitLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_s32_3d_0001 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %s32 %simg %img %s32_1 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, SampleDrefExplicitLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_s32_3d_0001 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %s32 %simg %f32vec2_hh %s32_1 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 3 components, " + "but given only 2")); +} + +TEST_F(ValidateImage, SampleDrefExplicitLodWrongDrefType) { + const std::string body = R"( +%img = OpLoad %type_image_s32_3d_0001 %uniform_image_s32_3d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_s32_3d_0001 %img %sampler +%res1 = OpImageSampleDrefExplicitLod %s32 %simg %f32vec3_hhh %u32_1 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Dref to be of 32-bit float type")); +} + +TEST_F(ValidateImage, SampleProjDrefImplicitLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjDrefImplicitLod %f32 %simg %f32vec3_hhh %f32_0_5 +%res2 = OpImageSampleProjDrefImplicitLod %f32 %simg %f32vec3_hhh %f32_0_5 Bias %f32_0_25 +%res4 = OpImageSampleProjDrefImplicitLod %f32 %simg %f32vec3_hhh %f32_0_5 ConstOffset %s32vec2_01 +%res5 = OpImageSampleProjDrefImplicitLod %f32 %simg %f32vec3_hhh %f32_0_5 Offset %s32vec2_01 +%res6 = OpImageSampleProjDrefImplicitLod %f32 %simg %f32vec3_hhh %f32_0_5 MinLod %f32_0_5 +%res7 = OpImageSampleProjDrefImplicitLod %f32 %simg %f32vec3_hhh %f32_0_5 Bias|Offset|MinLod %f32_0_25 %s32vec2_01 %f32_0_5 +%res8 = OpImageSampleProjDrefImplicitLod %f32 %simg %f32vec3_hhh %f32_0_5 NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SampleProjDrefImplicitLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjDrefImplicitLod %void %simg %f32vec3_hhh %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float scalar type")); +} + +TEST_F(ValidateImage, SampleProjDrefImplicitLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageSampleProjDrefImplicitLod %f32 %img %f32vec3_hhh %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampleProjDrefImplicitLodWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjDrefImplicitLod %u32 %simg %f32vec3_hhh %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as Result Type")); +} + +TEST_F(ValidateImage, SampleProjDrefImplicitLodVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleProjDrefImplicitLod %u32 %simg %f32vec3_hhh %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as Result Type")); +} + +TEST_F(ValidateImage, SampleProjDrefImplicitLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjDrefImplicitLod %f32 %simg %img %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, SampleProjDrefImplicitLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleProjDrefImplicitLod %f32 %simg %f32vec2_hh %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 3 components, " + "but given only 2")); +} + +TEST_F(ValidateImage, SampleProjDrefImplicitLodWrongDrefType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_2d_0001 %img %sampler +%res1 = OpImageSampleProjDrefImplicitLod %u32 %simg %f32vec3_hhh %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Dref to be of 32-bit float type")); +} + +TEST_F(ValidateImage, SampleProjDrefExplicitLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_1d_0001 %img %sampler +%res1 = OpImageSampleProjDrefExplicitLod %f32 %simg %f32vec2_hh %f32_0_5 Lod %f32_1 +%res2 = OpImageSampleProjDrefExplicitLod %f32 %simg %f32vec3_hhh %f32_0_5 Grad %f32_0_5 %f32_0_5 +%res3 = OpImageSampleProjDrefExplicitLod %f32 %simg %f32vec2_hh %f32_0_5 ConstOffset %s32_1 +%res4 = OpImageSampleProjDrefExplicitLod %f32 %simg %f32vec2_hh %f32_0_5 Offset %s32_1 +%res5 = OpImageSampleProjDrefExplicitLod %f32 %simg %f32vec2_hh %f32_0_5 Grad|Offset %f32_0_5 %f32_0_5 %s32_1 +%res6 = OpImageSampleProjDrefExplicitLod %f32 %simg %f32vec2_hh %f32_0_5 Lod|NonPrivateTexelKHR %f32_1 +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SampleProjDrefExplicitLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_1d_0001 %img %sampler +%res1 = OpImageSampleProjDrefExplicitLod %bool %simg %f32vec2_hh %f32_0_5 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float scalar type")); +} + +TEST_F(ValidateImage, SampleProjDrefExplicitLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 +%res1 = OpImageSampleProjDrefExplicitLod %f32 %img %f32vec2_hh %f32_0_5 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, SampleProjDrefExplicitLodWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_1d_0001 %img %sampler +%res1 = OpImageSampleProjDrefExplicitLod %u32 %simg %f32vec2_hh %f32_0_5 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as Result Type")); +} + +TEST_F(ValidateImage, SampleProjDrefExplicitLodVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageSampleProjDrefExplicitLod %u32 %simg %f32vec3_hhh %f32_0_5 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as Result Type")); +} + +TEST_F(ValidateImage, SampleProjDrefExplicitLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_1d_0001 %img %sampler +%res1 = OpImageSampleProjDrefExplicitLod %f32 %simg %img %f32_0_5 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, SampleProjDrefExplicitLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_1d_0001 %img %sampler +%res1 = OpImageSampleProjDrefExplicitLod %f32 %simg %f32_0_5 %f32_0_5 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 2 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, FetchSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 +%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 +%res2 = OpImageFetch %f32vec4 %img %u32vec2_01 NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, FetchWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageFetch %f32 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float vector type")); +} + +TEST_F(ValidateImage, FetchWrongNumComponentsResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageFetch %f32vec3 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateImage, FetchNotImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageFetch %f32vec4 %sampler %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, FetchSampledImageDirectly) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageFetch %f32vec4 %simg %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpImageFetch")); +} + +TEST_F(ValidateImage, FetchNotSampled) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageFetch %u32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled' parameter to be 1")); +} + +TEST_F(ValidateImage, FetchCube) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%res1 = OpImageFetch %f32vec4 %img %u32vec3_012 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Image 'Dim' cannot be Cube")); +} + +TEST_F(ValidateImage, FetchWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageFetch %u32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, FetchVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 +%res2 = OpImageFetch %u32vec4 %img %u32vec2_01 +%res3 = OpImageFetch %s32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, FetchWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageFetch %f32vec4 %img %f32vec2_00 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be int scalar or vector")); +} + +TEST_F(ValidateImage, FetchCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageFetch %f32vec4 %img %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 2 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, FetchLodNotInt) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageFetch %f32vec4 %img %u32vec2_01 Lod %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand Lod to be int scalar when used " + "with OpImageFetch")); +} + +TEST_F(ValidateImage, GatherSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 +%res2 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %const_offsets +%res3 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, GatherWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32 %simg %f32vec4_0000 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float vector type")); +} + +TEST_F(ValidateImage, GatherWrongNumComponentsResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec3 %simg %f32vec4_0000 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateImage, GatherNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%res1 = OpImageGather %f32vec4 %img %f32vec4_0000 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sampled Image to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, GatherWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %u32vec4 %simg %f32vec4_0000 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, GatherVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageGather %u32vec4 %simg %f32vec2_00 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, GatherWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %u32vec4_0123 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, GatherCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32_0_5 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 4 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, GatherWrongComponentType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Component to be 32-bit int scalar")); +} + +TEST_F(ValidateImage, GatherComponentNot32Bit) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u64_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Component to be 32-bit int scalar")); +} + +TEST_F(ValidateImage, GatherDimCube) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %const_offsets +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Operand ConstOffsets cannot be used with Cube Image 'Dim'")); +} + +TEST_F(ValidateImage, GatherConstOffsetsNotArray) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %u32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand ConstOffsets to be an array of size 4")); +} + +TEST_F(ValidateImage, GatherConstOffsetsArrayWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %const_offsets3x2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image Operand ConstOffsets to be an array of size 4")); +} + +TEST_F(ValidateImage, GatherConstOffsetsArrayNotVector) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %const_offsets4xu +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand ConstOffsets array componenets " + "to be int vectors of size 2")); +} + +TEST_F(ValidateImage, GatherConstOffsetsArrayVectorWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %const_offsets4x3 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand ConstOffsets array componenets " + "to be int vectors of size 2")); +} + +TEST_F(ValidateImage, GatherConstOffsetsArrayNotConst) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%offsets = OpUndef %u32vec2arr4 +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 ConstOffsets %offsets +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image Operand ConstOffsets to be a const object")); +} + +TEST_F(ValidateImage, NotGatherWithConstOffsets) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res2 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh ConstOffsets %const_offsets +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Image Operand ConstOffsets can only be used with OpImageGather " + "and OpImageDrefGather")); +} + +TEST_F(ValidateImage, DrefGatherSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %f32_0_5 +%res2 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %f32_0_5 ConstOffsets %const_offsets +%res3 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %f32_0_5 NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, DrefGatherVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0001 %uniform_image_void_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_void_2d_0001 %img %sampler +%res1 = OpImageDrefGather %u32vec4 %simg %f32vec2_00 %f32_0_5 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, DrefGatherWrongDrefType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0101 %uniform_image_f32_cube_0101 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_cube_0101 %img %sampler +%res1 = OpImageDrefGather %f32vec4 %simg %f32vec4_0000 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Dref to be of 32-bit float type")); +} + +TEST_F(ValidateImage, ReadSuccess1) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ReadSuccess2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability Image1D\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ReadSuccess3) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec3_012 +)"; + + const std::string extra = "\nOpCapability ImageCubeArray\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ReadSuccess4) { + const std::string body = R"( +%img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ReadNeedCapabilityStorageImageReadWithoutFormat) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ReadNeedCapabilityStorageImageReadWithoutFormatVulkan) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(), + env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability StorageImageReadWithoutFormat is required " + "to read storage image")); +} + +TEST_F(ValidateImage, ReadNeedCapabilityImage1D) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Capability Image1D is required to access storage image")); +} + +TEST_F(ValidateImage, ReadNeedCapabilityImageCubeArray) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec3_012 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Capability ImageCubeArray is required to access storage image")); +} + +// TODO(atgoo@github.com) Disabled until the spec is clarified. +TEST_F(ValidateImage, DISABLED_ReadWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %f32 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int or float vector type")); +} + +// TODO(atgoo@github.com) Disabled until the spec is clarified. +TEST_F(ValidateImage, DISABLED_ReadWrongNumComponentsResultType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %f32vec3 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 4 components")); +} + +TEST_F(ValidateImage, ReadNotImage) { + const std::string body = R"( +%sampler = OpLoad %type_sampler %uniform_sampler +%res1 = OpImageRead %f32vec4 %sampler %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, ReadImageSampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled' parameter to be 0 or 2")); +} + +TEST_F(ValidateImage, ReadWrongSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type components")); +} + +TEST_F(ValidateImage, ReadVoidSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_void_2d_0002 %uniform_image_void_2d_0002 +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +%res2 = OpImageRead %u32vec4 %img %u32vec2_01 +%res3 = OpImageRead %s32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ReadWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %f32vec2_00 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be int scalar or vector")); +} + +TEST_F(ValidateImage, ReadCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32_1 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 2 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, WriteSuccess1) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, WriteSuccess2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f +OpImageWrite %img %u32_1 %f32vec4_0000 +)"; + + const std::string extra = "\nOpCapability Image1D\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, WriteSuccess3) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +OpImageWrite %img %u32vec3_012 %f32vec4_0000 +)"; + + const std::string extra = "\nOpCapability ImageCubeArray\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, WriteSuccess4) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +;TODO(atgoo@github.com) Is it legal to write to MS image without sample index? +OpImageWrite %img %u32vec2_01 %f32vec4_0000 +OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, WriteSubpassData) { + const std::string body = R"( +%img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 +OpImageWrite %img %u32vec2_01 %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image 'Dim' cannot be SubpassData")); +} + +TEST_F(ValidateImage, WriteNeedCapabilityStorageImageWriteWithoutFormat) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, WriteNeedCapabilityStorageImageWriteWithoutFormatVulkan) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 +)"; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(GenerateShaderCode(body, "", "Fragment", "", env).c_str(), + env); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Capability StorageImageWriteWithoutFormat is required to write to " + "storage image")); +} + +TEST_F(ValidateImage, WriteNeedCapabilityImage1D) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f +OpImageWrite %img %u32vec2_01 %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Image1D is required to access storage " + "image")); +} + +TEST_F(ValidateImage, WriteNeedCapabilityImageCubeArray) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +OpImageWrite %img %u32vec3_012 %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Capability ImageCubeArray is required to access storage image")); +} + +TEST_F(ValidateImage, WriteNotImage) { + const std::string body = R"( +%sampler = OpLoad %type_sampler %uniform_sampler +OpImageWrite %sampler %u32vec2_01 %f32vec4_0000 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, WriteImageSampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +OpImageWrite %img %u32vec2_01 %f32vec4_0000 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled' parameter to be 0 or 2")); +} + +TEST_F(ValidateImage, WriteWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %f32vec2_00 %u32vec4_0123 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be int scalar or vector")); +} + +TEST_F(ValidateImage, WriteCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32_1 %u32vec4_0123 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 2 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, WriteTexelWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %img +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Texel to be int or float vector or scalar")); +} + +TEST_F(ValidateImage, DISABLED_WriteTexelNotVector4) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec3_012 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Texel to have 4 components")); +} + +TEST_F(ValidateImage, WriteTexelWrongComponentType) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %f32vec4_0000 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected Image 'Sampled Type' to be the same as Texel components")); +} + +TEST_F(ValidateImage, WriteSampleNotInteger) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %f32_1 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image Operand Sample to be int scalar")); +} + +TEST_F(ValidateImage, SampleNotMultisampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +OpImageWrite %img %u32vec2_01 %f32vec4_0000 Sample %u32_1 +)"; + + const std::string extra = "\nOpCapability StorageImageWriteWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operand Sample requires non-zero 'MS' parameter")); +} + +TEST_F(ValidateImage, SampleWrongOpcode) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0010 %img %sampler +%res1 = OpImageSampleExplicitLod %f32vec4 %simg %f32vec2_00 Sample %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Sample can only be used with " + "OpImageFetch, OpImageRead, OpImageWrite, " + "OpImageSparseFetch and OpImageSparseRead")); +} + +TEST_F(ValidateImage, SampleImageToImageSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%img2 = OpImage %type_image_f32_2d_0001 %simg +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SampleImageToImageWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%img2 = OpImage %type_sampled_image_f32_2d_0001 %simg +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypeImage")); +} + +TEST_F(ValidateImage, SampleImageToImageNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%img2 = OpImage %type_image_f32_2d_0001 %img +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Sample Image to be of type OpTypeSampleImage")); +} + +TEST_F(ValidateImage, SampleImageToImageNotTheSameImageType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%img2 = OpImage %type_image_f32_2d_0002 %simg +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sample Image image type to be equal to " + "Result Type")); +} + +TEST_F(ValidateImage, QueryFormatSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQueryFormat %u32 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QueryFormatWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQueryFormat %bool %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int scalar type")); +} + +TEST_F(ValidateImage, QueryFormatNotImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryFormat %u32 %sampler +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected operand to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, QueryOrderSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQueryOrder %u32 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QueryOrderWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQueryOrder %bool %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int scalar type")); +} + +TEST_F(ValidateImage, QueryOrderNotImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryOrder %u32 %sampler +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected operand to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, QuerySizeLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQuerySizeLod %u32vec2 %img %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QuerySizeLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQuerySizeLod %f32vec2 %img %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be int scalar or vector type")); +} + +TEST_F(ValidateImage, QuerySizeLodResultTypeWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQuerySizeLod %u32 %img %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type has 1 components, but 2 expected")); +} + +TEST_F(ValidateImage, QuerySizeLodNotImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQuerySizeLod %u32vec2 %sampler %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, QuerySizeLodSampledImageDirectly) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQuerySizeLod %u32vec2 %simg %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpImageQuerySizeLod")); +} + +TEST_F(ValidateImage, QuerySizeLodWrongImageDim) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageQuerySizeLod %u32vec2 %img %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image 'Dim' must be 1D, 2D, 3D or Cube")); +} + +TEST_F(ValidateImage, QuerySizeLodMultisampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%res1 = OpImageQuerySizeLod %u32vec2 %img %u32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Image 'MS' must be 0")); +} + +TEST_F(ValidateImage, QuerySizeLodWrongLodType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQuerySizeLod %u32vec2 %img %f32_0 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Level of Detail to be int scalar")); +} + +TEST_F(ValidateImage, QuerySizeSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%res1 = OpImageQuerySize %u32vec2 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QuerySizeWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%res1 = OpImageQuerySize %f32vec2 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be int scalar or vector type")); +} + +TEST_F(ValidateImage, QuerySizeNotImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQuerySize %u32vec2 %sampler +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, QuerySizeSampledImageDirectly) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQuerySize %u32vec2 %simg +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpImageQuerySize")); +} + +TEST_F(ValidateImage, QuerySizeDimSubpassDataBad) { + const std::string body = R"( +%img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 +%res1 = OpImageQuerySize %u32vec2 %img +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect")); +} + +TEST_F(ValidateImage, QuerySizeWrongSampling) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQuerySize %u32vec2 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2")); +} + +TEST_F(ValidateImage, QuerySizeWrongNumberOfComponents) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0111 %uniform_image_f32_3d_0111 +%res1 = OpImageQuerySize %u32vec2 %img +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type has 2 components, but 4 expected")); +} + +TEST_F(ValidateImage, QueryLodSuccessKernel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh +%res2 = OpImageQueryLod %f32vec2 %simg %u32vec2_01 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QueryLodSuccessShader) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QueryLodWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %u32vec2 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be float vector type")); +} + +TEST_F(ValidateImage, QueryLodResultTypeWrongSize) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec3 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to have 2 components")); +} + +TEST_F(ValidateImage, QueryLodNotSampledImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQueryLod %f32vec2 %img %f32vec2_hh +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Image operand to be of type OpTypeSampledImage")); +} + +TEST_F(ValidateImage, QueryLodWrongDim) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_rect_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image 'Dim' must be 1D, 2D, 3D or Cube")); +} + +TEST_F(ValidateImage, QueryLodWrongCoordinateType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to be float scalar or vector")); +} + +TEST_F(ValidateImage, QueryLodCoordinateSizeTooSmall) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Coordinate to have at least 2 components, " + "but given only 1")); +} + +TEST_F(ValidateImage, QueryLevelsSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQueryLevels %u32 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QueryLevelsWrongResultType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQueryLevels %f32 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be int scalar type")); +} + +TEST_F(ValidateImage, QueryLevelsNotImage) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLevels %u32 %sampler +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image to be of type OpTypeImage")); +} + +TEST_F(ValidateImage, QueryLevelsSampledImageDirectly) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLevels %u32 %simg +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpImageQueryLevels")); +} + +TEST_F(ValidateImage, QueryLevelsWrongDim) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageQueryLevels %u32 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image 'Dim' must be 1D, 2D, 3D or Cube")); +} + +TEST_F(ValidateImage, QuerySamplesSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0010 %uniform_image_f32_2d_0010 +%res1 = OpImageQuerySamples %u32 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QuerySamplesNot2D) { + const std::string body = R"( +%img = OpLoad %type_image_f32_3d_0010 %uniform_image_f32_3d_0010 +%res1 = OpImageQuerySamples %u32 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Image 'Dim' must be 2D")); +} + +TEST_F(ValidateImage, QuerySamplesNotMultisampled) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%res1 = OpImageQuerySamples %u32 %img +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Image 'MS' must be 1")); +} + +TEST_F(ValidateImage, QueryLodWrongExecutionModel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Vertex").c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpImageQueryLod requires Fragment or GLCompute execution model")); +} + +TEST_F(ValidateImage, QueryLodWrongExecutionModelWithFunc) { + const std::string body = R"( +%call_ret = OpFunctionCall %void %my_func +OpReturn +OpFunctionEnd +%my_func = OpFunction %void None %func +%my_func_entry = OpLabel +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Vertex").c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpImageQueryLod requires Fragment or GLCompute execution model")); +} + +TEST_F(ValidateImage, QueryLodComputeShaderDerivatives) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh +)"; + + const std::string extra = R"( +OpCapability ComputeDerivativeGroupLinearNV +OpExtension "SPV_NV_compute_shader_derivatives" +)"; + const std::string mode = R"( +OpExecutionMode %main LocalSize 8 8 1 +OpExecutionMode %main DerivativeGroupLinearNV +)"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "GLCompute", mode).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, QueryLodComputeShaderDerivativesMissingMode) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageQueryLod %f32vec2 %simg %f32vec2_hh +)"; + + const std::string extra = R"( +OpCapability ComputeDerivativeGroupLinearNV +OpExtension "SPV_NV_compute_shader_derivatives" +)"; + const std::string mode = R"( +OpExecutionMode %main LocalSize 8 8 1 +)"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "GLCompute", mode).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpImageQueryLod requires DerivativeGroupQuadsNV or " + "DerivativeGroupLinearNV execution mode for GLCompute " + "execution model")); +} + +TEST_F(ValidateImage, ImplicitLodWrongExecutionModel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body, "", "Vertex").c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ImplicitLod instructions require Fragment or " + "GLCompute execution model")); +} + +TEST_F(ValidateImage, ImplicitLodComputeShaderDerivatives) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh +)"; + + const std::string extra = R"( +OpCapability ComputeDerivativeGroupLinearNV +OpExtension "SPV_NV_compute_shader_derivatives" +)"; + const std::string mode = R"( +OpExecutionMode %main LocalSize 8 8 1 +OpExecutionMode %main DerivativeGroupLinearNV +)"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "GLCompute", mode).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ImplicitLodComputeShaderDerivativesMissingMode) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh +)"; + + const std::string extra = R"( +OpCapability ComputeDerivativeGroupLinearNV +OpExtension "SPV_NV_compute_shader_derivatives" +)"; + const std::string mode = R"( +OpExecutionMode %main LocalSize 8 8 1 +)"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "GLCompute", mode).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ImplicitLod instructions require DerivativeGroupQuadsNV or " + "DerivativeGroupLinearNV execution mode for GLCompute " + "execution model")); +} + +TEST_F(ValidateImage, ReadSubpassDataWrongExecutionModel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Vertex").c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Dim SubpassData requires Fragment execution model")); +} + +TEST_F(ValidateImage, SparseSampleImplicitLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleImplicitLod %struct_u32_f32vec4 %simg %f32vec2_hh +%res2 = OpImageSparseSampleImplicitLod %struct_u32_f32vec4 %simg %f32vec2_hh Bias %f32_0_25 +%res4 = OpImageSparseSampleImplicitLod %struct_u32_f32vec4 %simg %f32vec2_hh ConstOffset %s32vec2_01 +%res5 = OpImageSparseSampleImplicitLod %struct_u32_f32vec4 %simg %f32vec2_hh Offset %s32vec2_01 +%res6 = OpImageSparseSampleImplicitLod %struct_u32_f32vec4 %simg %f32vec2_hh MinLod %f32_0_5 +%res7 = OpImageSparseSampleImplicitLod %struct_u64_f32vec4 %simg %f32vec2_hh Bias|Offset|MinLod %f32_0_25 %s32vec2_01 %f32_0_5 +%res8 = OpImageSparseSampleImplicitLod %struct_u32_f32vec4 %simg %f32vec2_hh NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SparseSampleImplicitLodResultTypeNotStruct) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleImplicitLod %f32 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypeStruct")); +} + +TEST_F(ValidateImage, SparseSampleImplicitLodResultTypeNotTwoMembers1) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleImplicitLod %struct_u32 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an int " + "scalar and a texel")); +} + +TEST_F(ValidateImage, SparseSampleImplicitLodResultTypeNotTwoMembers2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleImplicitLod %struct_u32_f32vec4_u32 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseSampleImplicitLodResultTypeFirstMemberNotInt) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleImplicitLod %struct_f32_f32vec4 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseSampleImplicitLodResultTypeTexelNotVector) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleImplicitLod %struct_u32_u32 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type's second member to be int or " + "float vector type")); +} + +TEST_F(ValidateImage, SparseSampleImplicitLodWrongNumComponentsTexel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleImplicitLod %struct_u32_f32vec3 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type's second member to have 4 " + "components")); +} + +TEST_F(ValidateImage, SparseSampleImplicitLodWrongComponentTypeTexel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleImplicitLod %struct_u32_u32vec4 %simg %f32vec2_hh +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type's second member components")); +} + +TEST_F(ValidateImage, SparseSampleDrefImplicitLodSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0001 %uniform_image_u32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_u32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleDrefImplicitLod %struct_u32_u32 %simg %f32vec2_hh %f32_1 +%res2 = OpImageSparseSampleDrefImplicitLod %struct_u32_u32 %simg %f32vec2_hh %f32_1 Bias %f32_0_25 +%res4 = OpImageSparseSampleDrefImplicitLod %struct_u32_u32 %simg %f32vec2_hh %f32_1 ConstOffset %s32vec2_01 +%res5 = OpImageSparseSampleDrefImplicitLod %struct_u32_u32 %simg %f32vec2_hh %f32_1 Offset %s32vec2_01 +%res6 = OpImageSparseSampleDrefImplicitLod %struct_u32_u32 %simg %f32vec2_hh %f32_1 MinLod %f32_0_5 +%res7 = OpImageSparseSampleDrefImplicitLod %struct_u32_u32 %simg %f32vec2_hh %f32_1 Bias|Offset|MinLod %f32_0_25 %s32vec2_01 %f32_0_5 +%res8 = OpImageSparseSampleDrefImplicitLod %struct_u32_u32 %simg %f32vec2_hh %f32_1 NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SparseSampleDrefImplicitLodResultTypeNotStruct) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleDrefImplicitLod %f32 %simg %f32vec2_hh %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypeStruct")); +} + +TEST_F(ValidateImage, SparseSampleDrefImplicitLodResultTypeNotTwoMembers1) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleDrefImplicitLod %struct_u32 %simg %f32vec2_hh %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an int scalar " + "and a texel")); +} + +TEST_F(ValidateImage, SparseSampleDrefImplicitLodResultTypeNotTwoMembers2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleDrefImplicitLod %struct_u32_f32_u32 %simg %f32vec2_hh %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an int scalar " + "and a texel")); +} + +TEST_F(ValidateImage, SparseSampleDrefImplicitLodResultTypeFirstMemberNotInt) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleDrefImplicitLod %struct_f32_f32 %simg %f32vec2_hh %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an int scalar " + "and a texel")); +} + +TEST_F(ValidateImage, SparseSampleDrefImplicitLodDifferentSampledType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseSampleDrefImplicitLod %struct_u32_u32 %simg %f32vec2_hh %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type's second member")); +} + +TEST_F(ValidateImage, SparseFetchSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0001 %uniform_image_f32_1d_0001 +%res1 = OpImageSparseFetch %struct_u32_f32vec4 %img %u32vec2_01 +%res2 = OpImageSparseFetch %struct_u32_f32vec4 %img %u32vec2_01 NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SparseFetchResultTypeNotStruct) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageSparseFetch %f32 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypeStruct")); +} + +TEST_F(ValidateImage, SparseFetchResultTypeNotTwoMembers1) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageSparseFetch %struct_u32 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseFetchResultTypeNotTwoMembers2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageSparseFetch %struct_u32_f32vec4_u32 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseFetchResultTypeFirstMemberNotInt) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageSparseFetch %struct_f32_f32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseFetchResultTypeTexelNotVector) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageSparseFetch %struct_u32_u32 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type's second member to be int or " + "float vector type")); +} + +TEST_F(ValidateImage, SparseFetchWrongNumComponentsTexel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageSparseFetch %struct_u32_f32vec3 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type's second member to have 4 " + "components")); +} + +TEST_F(ValidateImage, SparseFetchWrongComponentTypeTexel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_rect_0001 %uniform_image_f32_rect_0001 +%res1 = OpImageSparseFetch %struct_u32_u32vec4 %img %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type's second member components")); +} + +TEST_F(ValidateImage, SparseReadSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SparseReadResultTypeNotStruct) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %f32 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypeStruct")); +} + +TEST_F(ValidateImage, SparseReadResultTypeNotTwoMembers1) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseReadResultTypeNotTwoMembers2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_f32vec4_u32 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseReadResultTypeFirstMemberNotInt) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_f32_f32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseReadResultTypeTexelWrongType) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_u32arr4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type's second member to be int or " + "float scalar or vector type")); +} + +TEST_F(ValidateImage, SparseReadWrongComponentTypeTexel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_u32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type's second member components")); +} + +TEST_F(ValidateImage, SparseReadSubpassDataNotAllowed) { + const std::string body = R"( +%img = OpLoad %type_image_f32_spd_0002 %uniform_image_f32_spd_0002 +%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment").c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Dim SubpassData cannot be used with ImageSparseRead")); +} + +TEST_F(ValidateImage, SparseGatherSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_u32_f32vec4 %simg %f32vec4_0000 %u32_1 +%res2 = OpImageSparseGather %struct_u32_f32vec4 %simg %f32vec4_0000 %u32_1 NonPrivateTexelKHR +)"; + + const std::string extra = R"( +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, SparseGatherResultTypeNotStruct) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %f32 %simg %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypeStruct")); +} + +TEST_F(ValidateImage, SparseGatherResultTypeNotTwoMembers1) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_u32 %simg %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an int " + "scalar and a texel")); +} + +TEST_F(ValidateImage, SparseGatherResultTypeNotTwoMembers2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_u32_f32vec4_u32 %simg %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an int " + "scalar and a texel")); +} + +TEST_F(ValidateImage, SparseGatherResultTypeFirstMemberNotInt) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_f32_f32vec4 %simg %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be a struct containing an " + "int scalar and a texel")); +} + +TEST_F(ValidateImage, SparseGatherResultTypeTexelNotVector) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_u32_u32 %simg %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type's second member to be int or " + "float vector type")); +} + +TEST_F(ValidateImage, SparseGatherWrongNumComponentsTexel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_u32_f32vec3 %simg %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type's second member to have 4 " + "components")); +} + +TEST_F(ValidateImage, SparseGatherWrongComponentTypeTexel) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_u32_u32vec4 %simg %f32vec2_hh %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Image 'Sampled Type' to be the same as " + "Result Type's second member components")); +} + +TEST_F(ValidateImage, SparseTexelsResidentSuccess) { + const std::string body = R"( +%res1 = OpImageSparseTexelsResident %bool %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SparseTexelsResidentResultTypeNotBool) { + const std::string body = R"( +%res1 = OpImageSparseTexelsResident %u32 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be bool scalar type")); +} + +TEST_F(ValidateImage, MakeTexelVisibleKHRSuccessImageRead) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 MakeTexelVisibleKHR|NonPrivateTexelKHR %u32_2 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, MakeTexelVisibleKHRSuccessImageSparseRead) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 MakeTexelVisibleKHR|NonPrivateTexelKHR %u32_2 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, MakeTexelVisibleKHRFailureOpcode) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh MakeTexelVisibleKHR|NonPrivateTexelKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Image Operand MakeTexelVisibleKHR can only be used with " + "OpImageRead or OpImageSparseRead: OpImageSampleImplicitLod")); +} + +TEST_F(ValidateImage, MakeTexelVisibleKHRFailureMissingNonPrivate) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 MakeTexelVisibleKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand MakeTexelVisibleKHR requires " + "NonPrivateTexelKHR is also specified: OpImageRead")); +} + +TEST_F(ValidateImage, MakeTexelAvailableKHRSuccessImageWrite) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_2 +)"; + + const std::string extra = R"( +OpCapability StorageImageWriteWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, MakeTexelAvailableKHRFailureOpcode) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSampleImplicitLod %f32vec4 %simg %f32vec2_hh MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand MakeTexelAvailableKHR can only be used " + "with OpImageWrite: OpImageSampleImplicitLod")); +} + +TEST_F(ValidateImage, MakeTexelAvailableKHRFailureMissingNonPrivate) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability StorageImageWriteWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand MakeTexelAvailableKHR requires " + "NonPrivateTexelKHR is also specified: OpImageWrite")); +} + +TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageWriteBad) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability StorageImageWriteWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageWriteGood) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 MakeTexelAvailableKHR|NonPrivateTexelKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability StorageImageWriteWithoutFormat +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageReadBad) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 MakeTexelVisibleKHR|NonPrivateTexelKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateImage, VulkanMemoryModelDeviceScopeImageReadGood) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 MakeTexelVisibleKHR|NonPrivateTexelKHR %u32_1 +)"; + + const std::string extra = R"( +OpCapability StorageImageReadWithoutFormat +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpExtension "SPV_KHR_vulkan_memory_model" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "VulkanKHR") + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +// This example used to cause a seg fault on OpReturnValue, verifying it doesn't +// anymore. +TEST_F(ValidateImage, Issue2463NoSegFault) { + const std::string spirv = R"( + OpCapability Linkage + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + %void = OpTypeVoid + %6 = OpTypeFunction %void + %float = OpTypeFloat 32 + %8 = OpTypeImage %float 3D 0 0 0 1 Unknown +%_ptr_UniformConstant_8 = OpTypePointer UniformConstant %8 + %10 = OpTypeSampler +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %12 = OpTypeSampledImage %8 + %13 = OpTypeFunction %12 %_ptr_UniformConstant_8 %_ptr_UniformConstant_10 + %23 = OpFunction %12 None %13 + %24 = OpFunctionParameter %_ptr_UniformConstant_8 + %25 = OpFunctionParameter %_ptr_UniformConstant_10 + %26 = OpLabel + %27 = OpLoad %8 %24 + %28 = OpLoad %10 %25 + %29 = OpSampledImage %12 %27 %28 + OpReturnValue %29 + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpSampledImage instruction must not appear as operand " + "for OpReturnValue")); +} + +TEST_F(ValidateImage, SignExtendV13Bad) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend +)"; + + EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "", + SPV_ENV_UNIVERSAL_1_3)), + HasSubstr("Invalid image operand 'SignExtend'")); +} + +TEST_F(ValidateImage, ZeroExtendV13Bad) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend +)"; + + EXPECT_THAT(CompileFailure(GenerateShaderCode(body, "", "Fragment", "", + SPV_ENV_UNIVERSAL_1_3)), + HasSubstr("Invalid image operand 'ZeroExtend'")); +} + +TEST_F(ValidateImage, SignExtendScalarUIntTexelV14Good) { + // Unsigned int sampled type + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32 %img %u32vec2_01 SignExtend +)"; + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), + SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, SignExtendScalarSIntTexelV14Good) { + // Signed int sampled type + const std::string body = R"( +%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002 +%res1 = OpImageRead %s32 %img %u32vec2_01 SignExtend +)"; + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), + SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, SignExtendScalarVectorUIntTexelV14Good) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 SignExtend +)"; + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), + SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, SignExtendVectorSIntTexelV14Good) { + const std::string body = R"( +%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002 +%res1 = OpImageRead %s32vec4 %img %u32vec2_01 SignExtend +)"; + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), + SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +// No negative tests for SignExtend since we don't truly know the +// texel format. + +TEST_F(ValidateImage, ZeroExtendScalarUIntTexelV14Good) { + // Unsigned int sampled type + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32 %img %u32vec2_01 ZeroExtend +)"; + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), + SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, ZeroExtendScalarSIntTexelV14Good) { + // Zeroed int sampled type + const std::string body = R"( +%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002 +%res1 = OpImageRead %s32 %img %u32vec2_01 ZeroExtend +)"; + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), + SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, ZeroExtendScalarVectorUIntTexelV14Good) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 ZeroExtend +)"; + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), + SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, ZeroExtendVectorSIntTexelV14Good) { + const std::string body = R"( +%img = OpLoad %type_image_s32_2d_0002 %uniform_image_s32_2d_0002 +%res1 = OpImageRead %s32vec4 %img %u32vec2_01 ZeroExtend +)"; + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_4), + SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateImage, ReadLodAMDSuccess1) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +%res1 = OpImageRead %u32vec4 %img %u32vec2_01 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability StorageImageReadWithoutFormat\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, ReadLodAMDSuccess2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec2_01 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability Image1D\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, ReadLodAMDSuccess3) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec3_012 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability ImageCubeArray\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, ReadLodAMDNeedCapability) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +%res1 = OpImageRead %f32vec4 %img %u32vec3_012 Lod %u32_0 +)"; + + const std::string extra = "\nOpCapability ImageCubeArray\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod can only be used with ExplicitLod " + "opcodes and OpImageFetch")); +} + +TEST_F(ValidateImage, WriteLodAMDSuccess1) { + const std::string body = R"( +%img = OpLoad %type_image_u32_2d_0000 %uniform_image_u32_2d_0000 +OpImageWrite %img %u32vec2_01 %u32vec4_0123 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability StorageImageWriteWithoutFormat\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, WriteLodAMDSuccess2) { + const std::string body = R"( +%img = OpLoad %type_image_f32_1d_0002_rgba32f %uniform_image_f32_1d_0002_rgba32f +OpImageWrite %img %u32_1 %f32vec4_0000 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability Image1D\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, WriteLodAMDSuccess3) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +OpImageWrite %img %u32vec3_012 %f32vec4_0000 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability ImageCubeArray\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, WriteLodAMDNeedCapability) { + const std::string body = R"( +%img = OpLoad %type_image_f32_cube_0102_rgba32f %uniform_image_f32_cube_0102_rgba32f +OpImageWrite %img %u32vec3_012 %f32vec4_0000 Lod %u32_0 +)"; + + const std::string extra = "\nOpCapability ImageCubeArray\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod can only be used with ExplicitLod " + "opcodes and OpImageFetch")); +} + +TEST_F(ValidateImage, SparseReadLodAMDSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 Lod %u32_0 +)"; + + const std::string extra = + "\nOpCapability StorageImageReadWithoutFormat\n" + "OpCapability ImageReadWriteLodAMD\n" + "OpExtension \"SPV_AMD_shader_image_load_store_lod\"\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); +} + +TEST_F(ValidateImage, SparseReadLodAMDNeedCapability) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0002 %uniform_image_f32_2d_0002 +%res1 = OpImageSparseRead %struct_u32_f32vec4 %img %u32vec2_01 Lod %u32_0 +)"; + + const std::string extra = "\nOpCapability StorageImageReadWithoutFormat\n"; + CompileSuccessfully( + GenerateShaderCode(body, extra, "Fragment", "", SPV_ENV_UNIVERSAL_1_1), + SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Image Operand Lod can only be used with ExplicitLod " + "opcodes and OpImageFetch")); +} + +TEST_F(ValidateImage, GatherBiasAMDSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 Bias %f32_1 +)"; + + const std::string extra = R"( +OpCapability ImageGatherBiasLodAMD +OpExtension "SPV_AMD_texture_gather_bias_lod" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, GatherLodAMDSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageGather %f32vec4 %simg %f32vec4_0000 %u32_1 Lod %f32_1 +)"; + + const std::string extra = R"( +OpCapability ImageGatherBiasLodAMD +OpExtension "SPV_AMD_texture_gather_bias_lod" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SparseGatherBiasAMDSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_u32_f32vec4 %simg %f32vec4_0000 %u32_1 Bias %f32_1 +)"; + + const std::string extra = R"( +OpCapability ImageGatherBiasLodAMD +OpExtension "SPV_AMD_texture_gather_bias_lod" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, SparseGatherLodAMDSuccess) { + const std::string body = R"( +%img = OpLoad %type_image_f32_2d_0001 %uniform_image_f32_2d_0001 +%sampler = OpLoad %type_sampler %uniform_sampler +%simg = OpSampledImage %type_sampled_image_f32_2d_0001 %img %sampler +%res1 = OpImageSparseGather %struct_u32_f32vec4 %simg %f32vec4_0000 %u32_1 Lod %f32_1 +)"; + + const std::string extra = R"( +OpCapability ImageGatherBiasLodAMD +OpExtension "SPV_AMD_texture_gather_bias_lod" +)"; + CompileSuccessfully(GenerateShaderCode(body, extra).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// No negative tests for ZeroExtend since we don't truly know the +// texel format. + +// Tests for 64-bit images +static const std::string capabilities_and_extensions_image64 = R"( +OpCapability Int64ImageEXT +OpExtension "SPV_EXT_shader_image_int64" +)"; +static const std::string declarations_image64 = R"( +%type_image_u64_buffer_0002_r64ui = OpTypeImage %u64 Buffer 0 0 0 2 R64ui +%ptr_Image_u64 = OpTypePointer Image %u64 +%ptr_image_u64_buffer_0002_r64ui = OpTypePointer Private %type_image_u64_buffer_0002_r64ui +%private_image_u64_buffer_0002_r64ui = OpVariable %ptr_image_u64_buffer_0002_r64ui Private +)"; + +TEST_F(ValidateImage, Image64MissingCapability) { + CompileSuccessfully(GenerateShaderCode("", "", "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "GLSL450", + declarations_image64) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); +} + +TEST_F(ValidateImage, Image64MissingExtension) { + const std::string extra = R"( +OpCapability Int64ImageEXT +)"; + + CompileSuccessfully(GenerateShaderCode("", extra, "Fragment", "", + SPV_ENV_UNIVERSAL_1_3, "GLSL450", + declarations_image64) + .c_str()); + ASSERT_EQ(SPV_ERROR_MISSING_EXTENSION, ValidateInstructions()); +} + +TEST_F(ValidateImage, ImageTexelPointer64Success) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u64 %private_image_u64_buffer_0002_r64ui %u32_0 %u32_0 +%sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body, + capabilities_and_extensions_image64, + "Fragment", "", SPV_ENV_UNIVERSAL_1_3, + "GLSL450", declarations_image64) + .c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateImage, ImageTexelPointer64ResultTypeNotPointer) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %type_image_u64_buffer_0002_r64ui %private_image_u64_buffer_0002_r64ui %u32_0 %u32_0 +%sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body, + capabilities_and_extensions_image64, + "Fragment", "", SPV_ENV_UNIVERSAL_1_3, + "GLSL450", declarations_image64) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypePointer")); +} + +TEST_F(ValidateImage, ImageTexelPointer64ResultTypeNotImageClass) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_image_f32_cube_0101 %private_image_u64_buffer_0002_r64ui %u32_0 %u32_0 +%sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body, + capabilities_and_extensions_image64, + "Fragment", "", SPV_ENV_UNIVERSAL_1_3, + "GLSL450", declarations_image64) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Result Type to be OpTypePointer whose " + "Storage Class operand is Image")); +} + +TEST_F(ValidateImage, ImageTexelPointer64SampleNotZeroForImageWithMSZero) { + const std::string body = R"( +%texel_ptr = OpImageTexelPointer %ptr_Image_u64 %private_image_u64_buffer_0002_r64ui %u32_0 %u32_1 +%sum = OpAtomicIAdd %u64 %texel_ptr %u32_1 %u32_0 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body, + capabilities_and_extensions_image64, + "Fragment", "", SPV_ENV_UNIVERSAL_1_3, + "GLSL450", declarations_image64) + .c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected Sample for Image with MS 0 to be a valid " + " for the value 0")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_interfaces_test.cpp b/third_party/spirv-tools/test/val/val_interfaces_test.cpp new file mode 100644 index 0000000..6869e79 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_interfaces_test.cpp @@ -0,0 +1,1415 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateInterfacesTest = spvtest::ValidateBase; + +TEST_F(ValidateInterfacesTest, EntryPointMissingInput) { + std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Input %3 +%5 = OpVariable %4 Input +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +%8 = OpLoad %3 %5 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Interface variable id <5> is used by entry point 'func' id <1>, " + "but is not listed as an interface")); +} + +TEST_F(ValidateInterfacesTest, EntryPointMissingOutput) { + std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Output %3 +%5 = OpVariable %4 Output +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +%8 = OpLoad %3 %5 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Interface variable id <5> is used by entry point 'func' id <1>, " + "but is not listed as an interface")); +} + +TEST_F(ValidateInterfacesTest, InterfaceMissingUseInSubfunction) { + std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Input %3 +%5 = OpVariable %4 Input +%6 = OpTypeFunction %2 +%1 = OpFunction %2 None %6 +%7 = OpLabel +%8 = OpFunctionCall %2 %9 +OpReturn +OpFunctionEnd +%9 = OpFunction %2 None %6 +%10 = OpLabel +%11 = OpLoad %3 %5 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Interface variable id <5> is used by entry point 'func' id <1>, " + "but is not listed as an interface")); +} + +TEST_F(ValidateInterfacesTest, TwoEntryPointsOneFunction) { + std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" %2 +OpEntryPoint Fragment %1 "func2" +OpExecutionMode %1 OriginUpperLeft +%3 = OpTypeVoid +%4 = OpTypeInt 32 0 +%5 = OpTypePointer Input %4 +%2 = OpVariable %5 Input +%6 = OpTypeFunction %3 +%1 = OpFunction %3 None %6 +%7 = OpLabel +%8 = OpLoad %4 %2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Interface variable id <2> is used by entry point 'func2' id <1>, " + "but is not listed as an interface")); +} + +TEST_F(ValidateInterfacesTest, MissingInterfaceThroughInitializer) { + const std::string text = R"( +OpCapability Shader +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" +OpExecutionMode %1 OriginUpperLeft +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypePointer Input %3 +%5 = OpTypePointer Function %4 +%6 = OpVariable %4 Input +%7 = OpTypeFunction %2 +%1 = OpFunction %2 None %7 +%8 = OpLabel +%9 = OpVariable %5 Function %6 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Interface variable id <6> is used by entry point 'func' id <1>, " + "but is not listed as an interface")); +} + +TEST_F(ValidateInterfacesTest, NonUniqueInterfacesSPV1p3) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var %var +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint3 = OpTypeVector %uint 3 +%struct = OpTypeStruct %uint3 +%ptr_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_struct Input +%func_ty = OpTypeFunction %void +%main = OpFunction %void None %func_ty +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateInterfacesTest, NonUniqueInterfacesSPV1p4) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var %var +OpExecutionMode %main LocalSize 1 1 1 +OpName %main "main" +OpName %var "var" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint3 = OpTypeVector %uint 3 +%struct = OpTypeStruct %uint3 +%ptr_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_struct Input +%func_ty = OpTypeFunction %void +%main = OpFunction %void None %func_ty +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Non-unique OpEntryPoint interface 2[%var] is disallowed")); +} + +TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p3) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint3 = OpTypeVector %uint 3 +%struct = OpTypeStruct %uint3 +%ptr_struct = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_struct StorageBuffer +%func_ty = OpTypeFunction %void +%main = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %struct %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateInterfacesTest, MissingGlobalVarSPV1p4) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %var "var" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint3 = OpTypeVector %uint 3 +%struct = OpTypeStruct %uint3 +%ptr_struct = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_struct StorageBuffer +%func_ty = OpTypeFunction %void +%main = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %struct %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Interface variable id <2> is used by entry point " + "'main' id <1>, but is not listed as an interface")); +} + +TEST_F(ValidateInterfacesTest, FunctionInterfaceVarSPV1p3) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var +OpExecutionMode %main LocalSize 1 1 1 +OpName %var "var" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint3 = OpTypeVector %uint 3 +%struct = OpTypeStruct %uint3 +%ptr_struct = OpTypePointer Function %struct +%func_ty = OpTypeFunction %void +%main = OpFunction %void None %func_ty +%1 = OpLabel +%var = OpVariable %ptr_struct Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpEntryPoint interfaces must be OpVariables with " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class 7 for Entry Point id 1.")); +} + +TEST_F(ValidateInterfacesTest, FunctionInterfaceVarSPV1p4) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %var +OpExecutionMode %main LocalSize 1 1 1 +OpName %var "var" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint3 = OpTypeVector %uint 3 +%struct = OpTypeStruct %uint3 +%ptr_struct = OpTypePointer Function %struct +%func_ty = OpTypeFunction %void +%main = OpFunction %void None %func_ty +%1 = OpLabel +%var = OpVariable %ptr_struct Function +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpEntryPoint interfaces should only list global variables")); +} + +TEST_F(ValidateInterfacesTest, ModuleSPV1p3ValidateSPV1p4_NotAllUsedGlobals) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpName %var "var" +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint3 = OpTypeVector %uint 3 +%struct = OpTypeStruct %uint3 +%ptr_struct = OpTypePointer StorageBuffer %struct +%var = OpVariable %ptr_struct StorageBuffer +%func_ty = OpTypeFunction %void +%main = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %struct %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateInterfacesTest, ModuleSPV1p3ValidateSPV1p4_DuplicateInterface) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gid %gid +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %gid BuiltIn GlobalInvocationId +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%ptr_input_int3 = OpTypePointer Input %int3 +%gid = OpVariable %ptr_input_int3 Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateInterfacesTest, SPV14MultipleEntryPointsSameFunction) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main1" %gid +OpEntryPoint GLCompute %main "main2" %gid +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %gid BuiltIn GlobalInvocationId +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%ptr_input_int3 = OpTypePointer Input %int3 +%gid = OpVariable %ptr_input_int3 Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsDoubleAssignmentVariable) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %var Location 0 +OpDecorate %var Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%ptr_input_float = OpTypePointer Input %float +%var = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable has conflicting location decorations")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsVariableAndMemberAssigned) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %var Location 0 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float +%ptr_input_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Members cannot be assigned a location")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsMemberAndSubMemberAssigned) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %outer Block +OpMemberDecorate %outer 0 Location 0 +OpMemberDecorate %struct 0 Location 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float +%outer = OpTypeStruct %struct +%ptr_input_outer = OpTypePointer Input %outer +%var = OpVariable %ptr_input_outer Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Members cannot be assigned a location")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsDoubleAssignmentStructMember) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 1 Location 0 +OpMemberDecorate %struct 1 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_input_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Member index 1 has conflicting location assignments")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsMissingAssignmentStructMember) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 1 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_input_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Member index 0 is missing a location assignment")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsMissingAssignmentNonBlockStruct) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_input_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable must be decorated with a location")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictInput) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_input_struct = OpTypePointer Input %struct +%var1 = OpVariable %ptr_input_struct Input +%var2 = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 0")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictOutput) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 1 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_output_struct = OpTypePointer Output %struct +%var1 = OpVariable %ptr_output_struct Output +%var2 = OpVariable %ptr_output_struct Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Entry-point has conflicting output location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, + VulkanLocationsSameLocationInputAndOutputNoConflict) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 1 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_input_struct = OpTypePointer Input %struct +%ptr_output_struct = OpTypePointer Output %struct +%var1 = OpVariable %ptr_input_struct Input +%var2 = OpVariable %ptr_output_struct Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsVariableInGap) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 1 Location 2 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_input_struct = OpTypePointer Input %struct +%ptr_input_float = OpTypePointer Input %float +%var1 = OpVariable %ptr_input_struct Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsLargeFloatVectorConflict) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%vector = OpTypeVector %double 3 +%ptr_input_float = OpTypePointer Input %float +%ptr_input_vector = OpTypePointer Input %vector +%var1 = OpVariable %ptr_input_vector Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsLargeIntVectorConflict) { + const std::string text = R"( +OpCapability Shader +OpCapability Int64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%long = OpTypeInt 64 0 +%vector = OpTypeVector %long 4 +%ptr_input_float = OpTypePointer Input %float +%ptr_input_vector = OpTypePointer Input %vector +%var1 = OpVariable %ptr_input_vector Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix2x2Conflict) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%vector = OpTypeVector %float 2 +%matrix = OpTypeMatrix %vector 2 +%ptr_input_float = OpTypePointer Input %float +%ptr_input_matrix = OpTypePointer Input %matrix +%var1 = OpVariable %ptr_input_matrix Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix3x3Conflict) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 2 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%vector = OpTypeVector %float 3 +%matrix = OpTypeMatrix %vector 3 +%ptr_input_float = OpTypePointer Input %float +%ptr_input_matrix = OpTypePointer Input %matrix +%var1 = OpVariable %ptr_input_matrix Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 2")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix4x4Conflict) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 3 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%vector = OpTypeVector %float 4 +%matrix = OpTypeMatrix %vector 4 +%ptr_input_float = OpTypePointer Input %float +%ptr_input_matrix = OpTypePointer Input %matrix +%var1 = OpVariable %ptr_input_matrix Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 3")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix2x2Conflict) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%vector = OpTypeVector %double 2 +%matrix = OpTypeMatrix %vector 2 +%ptr_input_float = OpTypePointer Input %float +%ptr_input_matrix = OpTypePointer Input %matrix +%var1 = OpVariable %ptr_input_matrix Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix3x3Conflict) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 5 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%vector = OpTypeVector %double 3 +%matrix = OpTypeMatrix %vector 3 +%ptr_input_float = OpTypePointer Input %float +%ptr_input_matrix = OpTypePointer Input %matrix +%var1 = OpVariable %ptr_input_matrix Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 5")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsLargeMatrix4x4Conflict) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 7 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%vector = OpTypeVector %double 4 +%matrix = OpTypeMatrix %vector 4 +%ptr_input_float = OpTypePointer Input %float +%ptr_input_matrix = OpTypePointer Input %matrix +%var1 = OpVariable %ptr_input_matrix Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 7")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsArray2Conflict) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_2 = OpConstant %int 2 +%array = OpTypeArray %int %int_2 +%struct = OpTypeStruct %array +%ptr_input_float = OpTypePointer Input %float +%ptr_input_struct = OpTypePointer Input %struct +%var1 = OpVariable %ptr_input_struct Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsArray4Conflict) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 3 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%array = OpTypeArray %int %int_4 +%struct = OpTypeStruct %array +%ptr_input_float = OpTypePointer Input %float +%ptr_input_struct = OpTypePointer Input %struct +%var1 = OpVariable %ptr_input_struct Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 3")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsMatrix4x4Array4Conflict) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 0 +OpDecorate %var2 Location 15 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%vector = OpTypeVector %float 4 +%matrix = OpTypeMatrix %vector 4 +%array = OpTypeArray %matrix %int_4 +%struct = OpTypeStruct %array +%ptr_input_float = OpTypePointer Input %float +%ptr_input_struct = OpTypePointer Input %struct +%var1 = OpVariable %ptr_input_struct Input +%var2 = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 15")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsComponentDisambiguates) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpMemberDecorate %struct 1 Location 0 +OpMemberDecorate %struct 1 Component 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_input_struct = OpTypePointer Input %struct +%var1 = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsComponentIn64BitVec3) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 1 Location 1 +OpMemberDecorate %struct 1 Component 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%double3 = OpTypeVector %double 3 +%struct = OpTypeStruct %double3 %float +%ptr_input_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting input location assignment " + "at location 1, component 1")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsComponentAfter64BitVec3) { + const std::string text = R"( +OpCapability Shader +OpCapability Float64 +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 1 Location 1 +OpMemberDecorate %struct 1 Component 2 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%double3 = OpTypeVector %double 3 +%struct = OpTypeStruct %double3 %float +%ptr_input_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsConflictingComponentVariable) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %var Location 0 +OpDecorate %var Component 0 +OpDecorate %var Component 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%ptr_input_float = OpTypePointer Input %float +%var = OpVariable %ptr_input_float Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variable has conflicting component decorations")); +} + +TEST_F(ValidateInterfacesTest, + VulkanLocationsConflictingComponentStructMember) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var +OpExecutionMode %main OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Location 0 +OpMemberDecorate %struct 0 Component 0 +OpMemberDecorate %struct 0 Component 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float +%ptr_input_struct = OpTypePointer Input %struct +%var = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Member index 0 has conflicting component assignments")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsVariableConflictOutputIndex1) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 1 +OpDecorate %var1 Index 1 +OpDecorate %var2 Location 1 +OpDecorate %var2 Index 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_output_struct = OpTypePointer Output %struct +%var1 = OpVariable %ptr_output_struct Output +%var2 = OpVariable %ptr_output_struct Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Entry-point has conflicting output location assignment " + "at location 1")); +} + +TEST_F(ValidateInterfacesTest, + VulkanLocationsVariableNoConflictDifferentIndex) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 %var2 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 1 +OpDecorate %var1 Index 0 +OpDecorate %var2 Location 1 +OpDecorate %var2 Index 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_output_struct = OpTypePointer Output %struct +%var1 = OpVariable %ptr_output_struct Output +%var2 = OpVariable %ptr_output_struct Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsIndexGLCompute) { + const std::string text = R"( +OpCapability Shader +OpCapability Geometry +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %var1 +OpExecutionMode %main Triangles +OpExecutionMode %main OutputPoints +OpDecorate %var1 Location 1 +OpDecorate %var1 Index 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_output_struct = OpTypePointer Output %struct +%var1 = OpVariable %ptr_output_struct Output +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Index can only be applied to Fragment output variables")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsIndexInput) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %var1 +OpExecutionMode %main OriginUpperLeft +OpDecorate %var1 Location 1 +OpDecorate %var1 Index 1 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%float = OpTypeFloat 32 +%struct = OpTypeStruct %float %float +%ptr_input_struct = OpTypePointer Input %struct +%var1 = OpVariable %ptr_input_struct Input +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Index can only be applied to Fragment output variables")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponent) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %11 %18 %28 %36 %40 +OpExecutionMode %4 OriginUpperLeft +OpDecorate %11 Location 0 +OpDecorate %18 Component 0 +OpDecorate %18 Location 0 +OpDecorate %28 Component 1 +OpDecorate %28 Location 0 +OpDecorate %36 Location 1 +OpDecorate %40 Component 0 +OpDecorate %40 Location 1 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%11 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_float = OpTypePointer Output %float +%18 = OpVariable %_ptr_Output_float Output +%uint = OpTypeInt 32 0 +%v3float = OpTypeVector %float 3 +%uint_2 = OpConstant %uint 2 +%_arr_v3float_uint_2 = OpTypeArray %v3float %uint_2 +%_ptr_Output__arr_v3float_uint_2 = OpTypePointer Output %_arr_v3float_uint_2 +%28 = OpVariable %_ptr_Output__arr_v3float_uint_2 Output +%_ptr_Output_v3float = OpTypePointer Output %v3float +%36 = OpVariable %_ptr_Input_v4float Input +%40 = OpVariable %_ptr_Output_float Output +%4 = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsArrayWithComponentBad) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %4 "main" %11 %18 %28 %36 %40 +OpExecutionMode %4 OriginUpperLeft +OpDecorate %11 Location 0 +OpDecorate %18 Component 0 +OpDecorate %18 Location 0 +OpDecorate %28 Component 1 +OpDecorate %28 Location 0 +OpDecorate %36 Location 1 +OpDecorate %40 Component 1 +OpDecorate %40 Location 1 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%11 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_float = OpTypePointer Output %float +%18 = OpVariable %_ptr_Output_float Output +%uint = OpTypeInt 32 0 +%v3float = OpTypeVector %float 3 +%uint_2 = OpConstant %uint 2 +%_arr_v3float_uint_2 = OpTypeArray %v3float %uint_2 +%_ptr_Output__arr_v3float_uint_2 = OpTypePointer Output %_arr_v3float_uint_2 +%28 = OpVariable %_ptr_Output__arr_v3float_uint_2 Output +%_ptr_Output_v3float = OpTypePointer Output %v3float +%36 = OpVariable %_ptr_Input_v4float Input +%40 = OpVariable %_ptr_Output_float Output +%4 = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry-point has conflicting output location " + "assignment at location 1, component 1")); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationsLargeLocation) { + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "????????" %17 + OpExecutionMode %4 OriginUpperLeft + OpDecorate %17 Location 4227868160 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v3float = OpTypeVector %float 3 +%_ptr_Input_v3float = OpTypePointer Input %v3float + %17 = OpVariable %_ptr_Input_v3float Input + %4 = OpFunction %void None %3 + %5 = OpLabel + OpUnreachable + OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_0)); +} + +TEST_F(ValidateInterfacesTest, VulkanLocationMeshShader) { + const std::string text = R"( +OpCapability Shader +OpCapability MeshShadingNV +OpExtension "SPV_NV_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshNV %foo "foo" %in +OpExecutionMode %foo LocalSize 1 1 1 +OpDecorate %block Block +OpMemberDecorate %block 0 PerTaskNV +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_32 = OpConstant %int 32 +%array = OpTypeArray %int %int_32 +%block = OpTypeStruct %array +%ptr_input_block = OpTypePointer Input %block +%in = OpVariable %ptr_input_block Input +%void_fn = OpTypeFunction %void +%foo = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(text, SPV_ENV_VULKAN_1_2); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_2)); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_layout_test.cpp b/third_party/spirv-tools/test/val/val_layout_test.cpp new file mode 100644 index 0000000..43fa046 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_layout_test.cpp @@ -0,0 +1,725 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for Logical Layout + +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/diagnostic.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::StrEq; + +using pred_type = std::function; +using ValidateLayout = spvtest::ValidateBase< + std::tuple>>; + +// returns true if order is equal to VAL +template +spv_result_t Equals(int order) { + return order == VAL ? SPV_SUCCESS : RET; +} + +// returns true if order is between MIN and MAX(inclusive) +template +struct Range { + explicit Range(bool inverse = false) : inverse_(inverse) {} + spv_result_t operator()(int order) { + return (inverse_ ^ (order >= MIN && order <= MAX)) ? SPV_SUCCESS : RET; + } + + private: + bool inverse_; +}; + +template +spv_result_t InvalidSet(int order) { + for (spv_result_t val : {T(true)(order)...}) + if (val != SPV_SUCCESS) return val; + return SPV_SUCCESS; +} + +// SPIRV source used to test the logical layout +const std::vector& getInstructions() { + // clang-format off + static const std::vector instructions = { + "OpCapability Shader", + "OpExtension \"TestExtension\"", + "%inst = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint GLCompute %func \"\"", + "OpExecutionMode %func LocalSize 1 1 1", + "OpExecutionModeId %func LocalSizeId %one %one %one", + "%str = OpString \"Test String\"", + "%str2 = OpString \"blabla\"", + "OpSource GLSL 450 %str \"uniform vec3 var = vec3(4.0);\"", + "OpSourceContinued \"void main(){return;}\"", + "OpSourceExtension \"Test extension\"", + "OpName %func \"MyFunction\"", + "OpMemberName %struct 1 \"my_member\"", + "OpDecorate %dgrp RowMajor", + "OpMemberDecorate %struct 1 RowMajor", + "%dgrp = OpDecorationGroup", + "OpGroupDecorate %dgrp %mat33 %mat44", + "%intt = OpTypeInt 32 1", + "%floatt = OpTypeFloat 32", + "%voidt = OpTypeVoid", + "%boolt = OpTypeBool", + "%vec4 = OpTypeVector %floatt 4", + "%vec3 = OpTypeVector %floatt 3", + "%mat33 = OpTypeMatrix %vec3 3", + "%mat44 = OpTypeMatrix %vec4 4", + "%struct = OpTypeStruct %intt %mat33", + "%vfunct = OpTypeFunction %voidt", + "%viifunct = OpTypeFunction %voidt %intt %intt", + "%one = OpConstant %intt 1", + // TODO(umar): OpConstant fails because the type is not defined + // TODO(umar): OpGroupMemberDecorate + "OpLine %str 3 4", + "OpNoLine", + "%func = OpFunction %voidt None %vfunct", + "%l = OpLabel", + "OpReturn ; %func return", + "OpFunctionEnd ; %func end", + "%func2 = OpFunction %voidt None %viifunct", + "%funcp1 = OpFunctionParameter %intt", + "%funcp2 = OpFunctionParameter %intt", + "%fLabel = OpLabel", + "OpNop", + "OpReturn ; %func2 return", + "OpFunctionEnd" + }; + return instructions; +} + +static const int kRangeEnd = 1000; +pred_type All = Range<0, kRangeEnd>(); + +INSTANTIATE_TEST_SUITE_P(InstructionsOrder, + ValidateLayout, + ::testing::Combine(::testing::Range((int)0, (int)getInstructions().size()), + // Note: Because of ID dependencies between instructions, some instructions + // are not free to be placed anywhere without triggering an non-layout + // validation error. Therefore, "Lines to compile" for some instructions + // are not "All" in the below. + // + // | Instruction | Line(s) valid | Lines to compile + ::testing::Values(std::make_tuple(std::string("OpCapability") , Equals<0> , Range<0, 2>()) + , std::make_tuple(std::string("OpExtension") , Equals<1> , All) + , std::make_tuple(std::string("OpExtInstImport") , Equals<2> , All) + , std::make_tuple(std::string("OpMemoryModel") , Equals<3> , Range<1, kRangeEnd>()) + , std::make_tuple(std::string("OpEntryPoint") , Equals<4> , All) + , std::make_tuple(std::string("OpExecutionMode ") , Range<5, 6>() , All) + , std::make_tuple(std::string("OpExecutionModeId") , Range<5, 6>() , All) + , std::make_tuple(std::string("OpSource ") , Range<7, 11>() , Range<8, kRangeEnd>()) + , std::make_tuple(std::string("OpSourceContinued ") , Range<7, 11>() , All) + , std::make_tuple(std::string("OpSourceExtension ") , Range<7, 11>() , All) + , std::make_tuple(std::string("%str2 = OpString ") , Range<7, 11>() , All) + , std::make_tuple(std::string("OpName ") , Range<12, 13>() , All) + , std::make_tuple(std::string("OpMemberName ") , Range<12, 13>() , All) + , std::make_tuple(std::string("OpDecorate ") , Range<14, 17>() , All) + , std::make_tuple(std::string("OpMemberDecorate ") , Range<14, 17>() , All) + , std::make_tuple(std::string("OpGroupDecorate ") , Range<14, 17>() , Range<17, kRangeEnd>()) + , std::make_tuple(std::string("OpDecorationGroup") , Range<14, 17>() , Range<0, 16>()) + , std::make_tuple(std::string("OpTypeBool") , Range<18, 31>() , All) + , std::make_tuple(std::string("OpTypeVoid") , Range<18, 31>() , Range<0, 26>()) + , std::make_tuple(std::string("OpTypeFloat") , Range<18, 31>() , Range<0,21>()) + , std::make_tuple(std::string("OpTypeInt") , Range<18, 31>() , Range<0, 21>()) + , std::make_tuple(std::string("OpTypeVector %floatt 4") , Range<18, 31>() , Range<20, 24>()) + , std::make_tuple(std::string("OpTypeMatrix %vec4 4") , Range<18, 31>() , Range<23, kRangeEnd>()) + , std::make_tuple(std::string("OpTypeStruct") , Range<18, 31>() , Range<25, kRangeEnd>()) + , std::make_tuple(std::string("%vfunct = OpTypeFunction"), Range<18, 31>() , Range<21, 31>()) + , std::make_tuple(std::string("OpConstant") , Range<18, 31>() , Range<21, kRangeEnd>()) + , std::make_tuple(std::string("OpLine ") , Range<18, kRangeEnd>() , Range<8, kRangeEnd>()) + , std::make_tuple(std::string("OpNoLine") , Range<18, kRangeEnd>() , All) + , std::make_tuple(std::string("%fLabel = OpLabel") , Equals<39> , All) + , std::make_tuple(std::string("OpNop") , Equals<40> , Range<40,kRangeEnd>()) + , std::make_tuple(std::string("OpReturn ; %func2 return") , Equals<41> , All) + ))); +// clang-format on + +// Creates a new vector which removes the string if the substr is found in the +// instructions vector and reinserts it in the location specified by order. +// NOTE: This will not work correctly if there are two instances of substr in +// instructions +std::vector GenerateCode(std::string substr, int order) { + std::vector code(getInstructions().size()); + std::vector inst(1); + partition_copy(std::begin(getInstructions()), std::end(getInstructions()), + std::begin(code), std::begin(inst), + [=](const std::string& str) { + return std::string::npos == str.find(substr); + }); + + code.insert(std::begin(code) + order, inst.front()); + return code; +} + +// This test will check the logical layout of a binary by removing each +// instruction in the pair of the INSTANTIATE_TEST_SUITE_P call and moving it in +// the SPIRV source formed by combining the vector "instructions". +TEST_P(ValidateLayout, Layout) { + int order; + std::string instruction; + pred_type pred; + pred_type test_pred; // Predicate to determine if the test should be build + std::tuple testCase; + + std::tie(order, testCase) = GetParam(); + std::tie(instruction, pred, test_pred) = testCase; + + // Skip test which break the code generation + if (test_pred(order)) return; + + std::vector code = GenerateCode(instruction, order); + + std::stringstream ss; + std::copy(std::begin(code), std::end(code), + std::ostream_iterator(ss, "\n")); + + const auto env = SPV_ENV_UNIVERSAL_1_3; + // printf("code: \n%s\n", ss.str().c_str()); + CompileSuccessfully(ss.str(), env); + spv_result_t result; + // clang-format off + ASSERT_EQ(pred(order), result = ValidateInstructions(env)) + << "Actual: " << spvResultToString(result) + << "\nExpected: " << spvResultToString(pred(order)) + << "\nOrder: " << order + << "\nInstruction: " << instruction + << "\nCode: \n" << ss.str(); + // clang-format on +} + +TEST_F(ValidateLayout, MemoryModelMissingBeforeEntryPoint) { + std::string str = R"( + OpCapability Matrix + OpExtension "TestExtension" + %inst = OpExtInstImport "GLSL.std.450" + OpEntryPoint GLCompute %func "" + OpExecutionMode %func LocalSize 1 1 1 + )"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "EntryPoint cannot appear before the memory model instruction")); +} + +TEST_F(ValidateLayout, MemoryModelMissing) { + char str[] = R"(OpCapability Linkage)"; + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Missing required OpMemoryModel instruction")); +} + +TEST_F(ValidateLayout, MemoryModelSpecifiedTwice) { + char str[] = R"( + OpCapability Linkage + OpCapability Shader + OpMemoryModel Logical Simple + OpMemoryModel Logical Simple + )"; + + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpMemoryModel should only be provided once")); +} + +TEST_F(ValidateLayout, FunctionDefinitionBeforeDeclarationBad) { + char str[] = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpDecorate %var Restrict +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%vifunct = OpTypeFunction %voidt %intt +%ptrt = OpTypePointer Function %intt +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel + OpNop + OpReturn + OpFunctionEnd +%func2 = OpFunction %voidt None %vifunct ; must appear before definition +%func2p = OpFunctionParameter %intt + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Function declarations must appear before function definitions.")); +} + +// TODO(umar): Passes but gives incorrect error message. Should be fixed after +// type checking +TEST_F(ValidateLayout, LabelBeforeFunctionParameterBad) { + char str[] = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpDecorate %var Restrict +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%vifunct = OpTypeFunction %voidt %intt +%ptrt = OpTypePointer Function %intt +%func = OpFunction %voidt None %vifunct +%funcl = OpLabel ; Label appears before function parameter +%func2p = OpFunctionParameter %intt + OpNop + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Function parameters must only appear immediately " + "after the function definition")); +} + +TEST_F(ValidateLayout, FuncParameterNotImmediatlyAfterFuncBad) { + char str[] = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpDecorate %var Restrict +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%vifunct = OpTypeFunction %voidt %intt +%ptrt = OpTypePointer Function %intt +%func = OpFunction %voidt None %vifunct +%funcl = OpLabel + OpNop + OpBranch %next +%func2p = OpFunctionParameter %intt ;FunctionParameter appears in a function but not immediately afterwards +%next = OpLabel + OpNop + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Function parameters must only appear immediately " + "after the function definition")); +} + +TEST_F(ValidateLayout, OpUndefCanAppearInTypeDeclarationSection) { + std::string str = R"( + OpCapability Kernel + OpCapability Linkage + OpMemoryModel Logical OpenCL +%voidt = OpTypeVoid +%uintt = OpTypeInt 32 0 +%funct = OpTypeFunction %voidt +%udef = OpUndef %uintt +%func = OpFunction %voidt None %funct +%entry = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLayout, OpUndefCanAppearInBlock) { + std::string str = R"( + OpCapability Kernel + OpCapability Linkage + OpMemoryModel Logical OpenCL +%voidt = OpTypeVoid +%uintt = OpTypeInt 32 0 +%funct = OpTypeFunction %voidt +%func = OpFunction %voidt None %funct +%entry = OpLabel +%udef = OpUndef %uintt + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLayout, MissingFunctionEndForFunctionWithBody) { + const auto s = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%tf = OpTypeFunction %void +%f = OpFunction %void None %tf +%l = OpLabel +OpReturn +)"; + + CompileSuccessfully(s); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + StrEq("Missing OpFunctionEnd at end of module.")); +} + +TEST_F(ValidateLayout, MissingFunctionEndForFunctionPrototype) { + const auto s = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%tf = OpTypeFunction %void +%f = OpFunction %void None %tf +)"; + + CompileSuccessfully(s); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + StrEq("Missing OpFunctionEnd at end of module.")); +} + +using ValidateOpFunctionParameter = spvtest::ValidateBase; + +TEST_F(ValidateOpFunctionParameter, OpLineBetweenParameters) { + const auto s = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%foo_frag = OpString "foo.frag" +%i32 = OpTypeInt 32 1 +%tf = OpTypeFunction %i32 %i32 %i32 +%c = OpConstant %i32 123 +%f = OpFunction %i32 None %tf +OpLine %foo_frag 1 1 +%p1 = OpFunctionParameter %i32 +OpNoLine +%p2 = OpFunctionParameter %i32 +%l = OpLabel +OpReturnValue %c +OpFunctionEnd +)"; + CompileSuccessfully(s); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateOpFunctionParameter, TooManyParameters) { + const auto s = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%i32 = OpTypeInt 32 1 +%tf = OpTypeFunction %i32 %i32 %i32 +%c = OpConstant %i32 123 +%f = OpFunction %i32 None %tf +%p1 = OpFunctionParameter %i32 +%p2 = OpFunctionParameter %i32 +%xp3 = OpFunctionParameter %i32 +%xp4 = OpFunctionParameter %i32 +%xp5 = OpFunctionParameter %i32 +%xp6 = OpFunctionParameter %i32 +%xp7 = OpFunctionParameter %i32 +%l = OpLabel +OpReturnValue %c +OpFunctionEnd +)"; + CompileSuccessfully(s); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); +} + +using ValidateEntryPoint = spvtest::ValidateBase; + +// Tests that not having OpEntryPoint causes an error. +TEST_F(ValidateEntryPoint, NoEntryPointBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("No OpEntryPoint instruction was found. This is only " + "allowed if the Linkage capability is being used.")); +} + +// Invalid. A function may not be a target of both OpEntryPoint and +// OpFunctionCall. +TEST_F(ValidateEntryPoint, FunctionIsTargetOfEntryPointAndFunctionCallBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %foo "foo" + OpExecutionMode %foo OriginUpperLeft +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%foo = OpFunction %voidt None %funct +%entry = OpLabel +%recurse = OpFunctionCall %voidt %foo + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("A function (1) may not be targeted by both an OpEntryPoint " + "instruction and an OpFunctionCall instruction.")); +} + +// Invalid. Must be within a function to make a function call. +TEST_F(ValidateEntryPoint, FunctionCallOutsideFunctionBody) { + std::string spirv = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpName %variableName "variableName" + %34 = OpFunctionCall %variableName %1 + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("FunctionCall must happen within a function body.")); +} + +// Valid. Module with a function but no entry point is valid when Linkage +// Capability is used. +TEST_F(ValidateEntryPoint, NoEntryPointWithLinkageCapGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%foo = OpFunction %voidt None %funct +%entry = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLayout, ModuleProcessedInvalidIn10) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %void "void" + OpModuleProcessed "this is ok in 1.1 and later" + OpDecorate %void Volatile ; bogus, but makes the example short +%void = OpTypeVoid +)"; + + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_WRONG_VERSION, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_0)); + // In a 1.0 environment the version check fails. + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid SPIR-V binary version 1.1 for target " + "environment SPIR-V 1.0.")); +} + +TEST_F(ValidateLayout, ModuleProcessedValidIn11) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %void "void" + OpModuleProcessed "this is ok in 1.1 and later" + OpDecorate %void Volatile ; bogus, but makes the example short +%void = OpTypeVoid +)"; + + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateLayout, LayoutOrderMixedUp) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %fragmentFloat "fragmentFloat" + OpExecutionMode %fragmentFloat OriginUpperLeft + OpEntryPoint Fragment %fragmentUint "fragmentUint" + OpExecutionMode %fragmentUint OriginUpperLeft +)"; + + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + // By the mechanics of the validator, we assume ModuleProcessed is in the + // right spot, but then that OpName is in the wrong spot. + EXPECT_THAT(getDiagnosticString(), + HasSubstr("EntryPoint is in an invalid layout section")); +} + +TEST_F(ValidateLayout, ModuleProcessedBeforeLastNameIsTooEarly) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpModuleProcessed "this is too early" + OpName %void "void" +%void = OpTypeVoid +)"; + + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + // By the mechanics of the validator, we assume ModuleProcessed is in the + // right spot, but then that OpName is in the wrong spot. + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Name is in an invalid layout section")); +} + +TEST_F(ValidateLayout, ModuleProcessedInvalidAfterFirstAnnotation) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %void Volatile ; this is bogus, but keeps the example short + OpModuleProcessed "this is too late" +%void = OpTypeVoid +)"; + + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ModuleProcessed is in an invalid layout section")); +} + +TEST_F(ValidateLayout, ModuleProcessedInvalidInFunctionBeforeLabel) { + char str[] = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn + OpModuleProcessed "this is too late, in function before label" +%entry = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ModuleProcessed cannot appear in a function declaration")); +} + +TEST_F(ValidateLayout, ModuleProcessedInvalidInBasicBlock) { + char str[] = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel + OpModuleProcessed "this is too late, in basic block" + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str, SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("ModuleProcessed cannot appear in a function declaration")); +} + +TEST_F(ValidateLayout, WebGPUCallerBeforeCalleeBad) { + char str[] = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%1 = OpLabel +%2 = OpFunctionCall %void %callee + OpReturn + OpFunctionEnd +%callee = OpFunction %void None %voidfn +%3 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str, SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, functions need to be defined before being " + "called.\n %5 = OpFunctionCall %void %6\n")); +} + +TEST_F(ValidateLayout, WebGPUCalleeBeforeCallerGood) { + char str[] = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%callee = OpFunction %void None %voidfn +%3 = OpLabel + OpReturn + OpFunctionEnd +%main = OpFunction %void None %voidfn +%1 = OpLabel +%2 = OpFunctionCall %void %callee + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str, SPV_ENV_WEBGPU_0); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +// TODO(umar): Test optional instructions + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_limits_test.cpp b/third_party/spirv-tools/test/val/val_limits_test.cpp new file mode 100644 index 0000000..0ef61e2 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_limits_test.cpp @@ -0,0 +1,778 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for Universal Limits. (Section 2.17 of the SPIR-V Spec) + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; + +using ValidateLimits = spvtest::ValidateBase; + +std::string header = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +)"; + +TEST_F(ValidateLimits, IdLargerThanBoundBad) { + std::string str = header + R"( +; %i32 has ID 1 +%i32 = OpTypeInt 32 1 +%c = OpConstant %i32 100 + +; Fake an instruction with 64 as the result id. +; !64 = OpConstantNull %i32 +!0x3002e !1 !64 +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Result '64' must be less than the ID bound '3'.")); +} + +TEST_F(ValidateLimits, IdEqualToBoundBad) { + std::string str = header + R"( +; %i32 has ID 1 +%i32 = OpTypeInt 32 1 +%c = OpConstant %i32 100 + +; Fake an instruction with 64 as the result id. +; !64 = OpConstantNull %i32 +!0x3002e !1 !64 +)"; + + CompileSuccessfully(str); + + // The largest ID used in this program is 64. Let's overwrite the ID bound in + // the header to be 64. This should result in an error because all IDs must + // satisfy: 0 < id < bound. + OverwriteAssembledBinary(3, 64); + + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Result '64' must be less than the ID bound '64'.")); +} + +TEST_F(ValidateLimits, IdBoundTooBigDeaultLimit) { + std::string str = header; + + CompileSuccessfully(str); + + // The largest ID used in this program is 64. Let's overwrite the ID bound in + // the header to be 64. This should result in an error because all IDs must + // satisfy: 0 < id < bound. + OverwriteAssembledBinary(3, 0x4FFFFF); + + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid SPIR-V. The id bound is larger than the max " + "id bound 4194303.")); +} + +TEST_F(ValidateLimits, IdBoundAtSetLimit) { + std::string str = header; + + CompileSuccessfully(str); + + // The largest ID used in this program is 64. Let's overwrite the ID bound in + // the header to be 64. This should result in an error because all IDs must + // satisfy: 0 < id < bound. + uint32_t id_bound = 0x4FFFFF; + + OverwriteAssembledBinary(3, id_bound); + getValidatorOptions()->universal_limits_.max_id_bound = id_bound; + + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLimits, IdBoundJustAboveSetLimit) { + std::string str = header; + + CompileSuccessfully(str); + + // The largest ID used in this program is 64. Let's overwrite the ID bound in + // the header to be 64. This should result in an error because all IDs must + // satisfy: 0 < id < bound. + uint32_t id_bound = 5242878; + + OverwriteAssembledBinary(3, id_bound); + getValidatorOptions()->universal_limits_.max_id_bound = id_bound - 1; + + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid SPIR-V. The id bound is larger than the max " + "id bound 5242877.")); +} + +TEST_F(ValidateLimits, IdBoundAtInMaxLimit) { + std::string str = header; + + CompileSuccessfully(str); + + uint32_t id_bound = std::numeric_limits::max(); + + OverwriteAssembledBinary(3, id_bound); + getValidatorOptions()->universal_limits_.max_id_bound = id_bound; + + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLimits, StructNumMembersGood) { + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct)"; + for (int i = 0; i < 16383; ++i) { + spirv << " %1"; + } + CompileSuccessfully(spirv.str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLimits, StructNumMembersExceededBad) { + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct)"; + for (int i = 0; i < 16384; ++i) { + spirv << " %1"; + } + CompileSuccessfully(spirv.str()); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Number of OpTypeStruct members (16384) has exceeded " + "the limit (16383).")); +} + +TEST_F(ValidateLimits, CustomizedStructNumMembersGood) { + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct)"; + for (int i = 0; i < 32000; ++i) { + spirv << " %1"; + } + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_struct_members, 32000u); + CompileSuccessfully(spirv.str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLimits, CustomizedStructNumMembersBad) { + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct)"; + for (int i = 0; i < 32001; ++i) { + spirv << " %1"; + } + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_struct_members, 32000u); + CompileSuccessfully(spirv.str()); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Number of OpTypeStruct members (32001) has exceeded " + "the limit (32000).")); +} + +// Valid: Switch statement has 16,383 branches. +TEST_F(ValidateLimits, SwitchNumBranchesGood) { + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 1234 +%5 = OpFunction %1 None %2 +%7 = OpLabel +%8 = OpIAdd %3 %4 %4 + OpSwitch %4 %10)"; + + // Now add the (literal, label) pairs + for (int i = 0; i < 16383; ++i) { + spirv << " 1 %10"; + } + + spirv << R"( +%10 = OpLabel +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Switch statement has 16,384 branches. +TEST_F(ValidateLimits, SwitchNumBranchesBad) { + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 1234 +%5 = OpFunction %1 None %2 +%7 = OpLabel +%8 = OpIAdd %3 %4 %4 + OpSwitch %4 %10)"; + + // Now add the (literal, label) pairs + for (int i = 0; i < 16384; ++i) { + spirv << " 1 %10"; + } + + spirv << R"( +%10 = OpLabel +OpReturn +OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Number of (literal, label) pairs in OpSwitch (16384) " + "exceeds the limit (16383).")); +} + +// Valid: Switch statement has 10 branches (limit is 10) +TEST_F(ValidateLimits, CustomizedSwitchNumBranchesGood) { + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 1234 +%5 = OpFunction %1 None %2 +%7 = OpLabel +%8 = OpIAdd %3 %4 %4 + OpSwitch %4 %10)"; + + // Now add the (literal, label) pairs + for (int i = 0; i < 10; ++i) { + spirv << " 1 %10"; + } + + spirv << R"( +%10 = OpLabel +OpReturn +OpFunctionEnd + )"; + + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_switch_branches, 10u); + CompileSuccessfully(spirv.str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Switch statement has 11 branches (limit is 10) +TEST_F(ValidateLimits, CustomizedSwitchNumBranchesBad) { + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeInt 32 0 +%4 = OpConstant %3 1234 +%5 = OpFunction %1 None %2 +%7 = OpLabel +%8 = OpIAdd %3 %4 %4 + OpSwitch %4 %10)"; + + // Now add the (literal, label) pairs + for (int i = 0; i < 11; ++i) { + spirv << " 1 %10"; + } + + spirv << R"( +%10 = OpLabel +OpReturn +OpFunctionEnd + )"; + + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_switch_branches, 10u); + CompileSuccessfully(spirv.str()); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Number of (literal, label) pairs in OpSwitch (11) " + "exceeds the limit (10).")); +} + +// Valid: OpTypeFunction with 255 arguments. +TEST_F(ValidateLimits, OpTypeFunctionGood) { + int num_args = 255; + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeFunction %1)"; + // add parameters + for (int i = 0; i < num_args; ++i) { + spirv << " %1"; + } + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: OpTypeFunction with 256 arguments. (limit is 255 according to the +// spec Universal Limits (2.17). +TEST_F(ValidateLimits, OpTypeFunctionBad) { + int num_args = 256; + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeFunction %1)"; + for (int i = 0; i < num_args; ++i) { + spirv << " %1"; + } + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeFunction may not take more than 255 arguments. " + "OpTypeFunction '2[%2]' has 256 arguments.")); +} + +// Valid: OpTypeFunction with 100 arguments (Custom limit: 100) +TEST_F(ValidateLimits, CustomizedOpTypeFunctionGood) { + int num_args = 100; + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeFunction %1)"; + // add parameters + for (int i = 0; i < num_args; ++i) { + spirv << " %1"; + } + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_function_args, 100u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: OpTypeFunction with 101 arguments. (Custom limit: 100) +TEST_F(ValidateLimits, CustomizedOpTypeFunctionBad) { + int num_args = 101; + std::ostringstream spirv; + spirv << header << R"( +%1 = OpTypeInt 32 0 +%2 = OpTypeFunction %1)"; + for (int i = 0; i < num_args; ++i) { + spirv << " %1"; + } + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_function_args, 100u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeFunction may not take more than 100 arguments. " + "OpTypeFunction '2[%2]' has 101 arguments.")); +} + +// Valid: module has 65,535 global variables. +TEST_F(ValidateLimits, NumGlobalVarsGood) { + int num_globals = 65535; + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 +%_ptr_int = OpTypePointer Input %int + )"; + + for (int i = 0; i < num_globals; ++i) { + spirv << "%var_" << i << " = OpVariable %_ptr_int Input\n"; + } + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: module has 65,536 global variables (limit is 65,535). +TEST_F(ValidateLimits, NumGlobalVarsBad) { + int num_globals = 65536; + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 +%_ptr_int = OpTypePointer Input %int + )"; + + for (int i = 0; i < num_globals; ++i) { + spirv << "%var_" << i << " = OpVariable %_ptr_int Input\n"; + } + + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Number of Global Variables (Storage Class other than " + "'Function') exceeded the valid limit (65535).")); +} + +// Valid: module has 50 global variables (limit is 50) +TEST_F(ValidateLimits, CustomizedNumGlobalVarsGood) { + int num_globals = 50; + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 +%_ptr_int = OpTypePointer Input %int + )"; + + for (int i = 0; i < num_globals; ++i) { + spirv << "%var_" << i << " = OpVariable %_ptr_int Input\n"; + } + + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_global_variables, 50u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: module has 51 global variables (limit is 50). +TEST_F(ValidateLimits, CustomizedNumGlobalVarsBad) { + int num_globals = 51; + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 +%_ptr_int = OpTypePointer Input %int + )"; + + for (int i = 0; i < num_globals; ++i) { + spirv << "%var_" << i << " = OpVariable %_ptr_int Input\n"; + } + + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_global_variables, 50u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Number of Global Variables (Storage Class other than " + "'Function') exceeded the valid limit (50).")); +} + +// Valid: module has 524,287 local variables. +// Note: AppVeyor limits process time to 300s. For a VisualStudio Debug +// build, going up to 524287 local variables gets too close to that +// limit. So test with an artificially lowered limit. +TEST_F(ValidateLimits, NumLocalVarsGoodArtificiallyLowLimit5K) { + int num_locals = 5000; + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 + %_ptr_int = OpTypePointer Function %int + %voidt = OpTypeVoid + %funct = OpTypeFunction %voidt + %main = OpFunction %voidt None %funct + %entry = OpLabel + )"; + + for (int i = 0; i < num_locals; ++i) { + spirv << "%var_" << i << " = OpVariable %_ptr_int Function\n"; + } + + spirv << R"( + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + // Artificially limit it. + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_local_variables, num_locals); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: module has 524,288 local variables (limit is 524,287). +// Artificially limit the check to 5001. +TEST_F(ValidateLimits, NumLocalVarsBadArtificiallyLowLimit5K) { + int num_locals = 5001; + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 + %_ptr_int = OpTypePointer Function %int + %voidt = OpTypeVoid + %funct = OpTypeFunction %voidt + %main = OpFunction %voidt None %funct + %entry = OpLabel + )"; + + for (int i = 0; i < num_locals; ++i) { + spirv << "%var_" << i << " = OpVariable %_ptr_int Function\n"; + } + + spirv << R"( + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(spirv.str()); + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_local_variables, 5000u); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Number of local variables ('Function' Storage Class) " + "exceeded the valid limit (5000).")); +} + +// Valid: module has 100 local variables (limit is 100). +TEST_F(ValidateLimits, CustomizedNumLocalVarsGood) { + int num_locals = 100; + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 + %_ptr_int = OpTypePointer Function %int + %voidt = OpTypeVoid + %funct = OpTypeFunction %voidt + %main = OpFunction %voidt None %funct + %entry = OpLabel + )"; + + for (int i = 0; i < num_locals; ++i) { + spirv << "%var_" << i << " = OpVariable %_ptr_int Function\n"; + } + + spirv << R"( + OpReturn + OpFunctionEnd + )"; + + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_local_variables, 100u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: module has 101 local variables (limit is 100). +TEST_F(ValidateLimits, CustomizedNumLocalVarsBad) { + int num_locals = 101; + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 + %_ptr_int = OpTypePointer Function %int + %voidt = OpTypeVoid + %funct = OpTypeFunction %voidt + %main = OpFunction %voidt None %funct + %entry = OpLabel + )"; + + for (int i = 0; i < num_locals; ++i) { + spirv << "%var_" << i << " = OpVariable %_ptr_int Function\n"; + } + + spirv << R"( + OpReturn + OpFunctionEnd + )"; + + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_local_variables, 100u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Number of local variables ('Function' Storage Class) " + "exceeded the valid limit (100).")); +} + +// Valid: Structure nesting depth of 255. +TEST_F(ValidateLimits, StructNestingDepthGood) { + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 + %s_depth_1 = OpTypeStruct %int + )"; + for (auto i = 2; i <= 255; ++i) { + spirv << "%s_depth_" << i << " = OpTypeStruct %int %s_depth_" << i - 1; + spirv << "\n"; + } + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Structure nesting depth of 256. +TEST_F(ValidateLimits, StructNestingDepthBad) { + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 + %s_depth_1 = OpTypeStruct %int + )"; + for (auto i = 2; i <= 256; ++i) { + spirv << "%s_depth_" << i << " = OpTypeStruct %int %s_depth_" << i - 1; + spirv << "\n"; + } + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure Nesting Depth may not be larger than 255. Found 256.")); +} + +// Valid: Structure nesting depth of 100 (limit is 100). +TEST_F(ValidateLimits, CustomizedStructNestingDepthGood) { + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 + %s_depth_1 = OpTypeStruct %int + )"; + for (auto i = 2; i <= 100; ++i) { + spirv << "%s_depth_" << i << " = OpTypeStruct %int %s_depth_" << i - 1; + spirv << "\n"; + } + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_struct_depth, 100u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Structure nesting depth of 101 (limit is 100). +TEST_F(ValidateLimits, CustomizedStructNestingDepthBad) { + std::ostringstream spirv; + spirv << header << R"( + %int = OpTypeInt 32 0 + %s_depth_1 = OpTypeStruct %int + )"; + for (auto i = 2; i <= 101; ++i) { + spirv << "%s_depth_" << i << " = OpTypeStruct %int %s_depth_" << i - 1; + spirv << "\n"; + } + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_struct_depth, 100u); + CompileSuccessfully(spirv.str()); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Structure Nesting Depth may not be larger than 100. Found 101.")); +} + +// clang-format off +// Generates an SPIRV program with the given control flow nesting depth +void GenerateSpirvProgramWithCfgNestingDepth(std::string& str, int depth) { + std::ostringstream spirv; + spirv << header << R"( + %void = OpTypeVoid + %3 = OpTypeFunction %void + %bool = OpTypeBool + %12 = OpConstantTrue %bool + %main = OpFunction %void None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranch %10 + %10 = OpLabel + OpBranchConditional %12 %7 %8 + %7 = OpLabel + )"; + int first_id = 13; + int last_id = 14; + // We already have 1 level of nesting due to the Loop. + int num_if_conditions = depth-1; + int largest_index = first_id + 2*num_if_conditions - 2; + for (int i = first_id; i <= largest_index; i = i + 2) { + spirv << "OpSelectionMerge %" << i+1 << " None" << "\n"; + spirv << "OpBranchConditional %12 " << "%" << i << " %" << i+1 << "\n"; + spirv << "%" << i << " = OpLabel" << "\n"; + } + spirv << "OpBranch %9" << "\n"; + + for (int i = largest_index+1; i > last_id; i = i - 2) { + spirv << "%" << i << " = OpLabel" << "\n"; + spirv << "OpBranch %" << i-2 << "\n"; + } + spirv << "%" << last_id << " = OpLabel" << "\n"; + spirv << "OpBranch %9" << "\n"; + spirv << R"( + %9 = OpLabel + OpBranch %6 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + str = spirv.str(); +} +// clang-format on + +// Invalid: Control Flow Nesting depth is 1024. (limit is 1023). +TEST_F(ValidateLimits, ControlFlowDepthBad) { + std::string spirv; + GenerateSpirvProgramWithCfgNestingDepth(spirv, 1024); + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Maximum Control Flow nesting depth exceeded.")); +} + +// Valid: Control Flow Nesting depth is 10 (custom limit: 10). +TEST_F(ValidateLimits, CustomizedControlFlowDepthGood) { + std::string spirv; + GenerateSpirvProgramWithCfgNestingDepth(spirv, 10); + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_control_flow_nesting_depth, 10u); + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// Invalid: Control Flow Nesting depth is 11. (custom limit: 10). +TEST_F(ValidateLimits, CustomizedControlFlowDepthBad) { + std::string spirv; + GenerateSpirvProgramWithCfgNestingDepth(spirv, 11); + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_control_flow_nesting_depth, 10u); + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_CFG, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Maximum Control Flow nesting depth exceeded.")); +} + +// Valid. The purpose here is to test the CFG depth calculation code when a loop +// continue target is the loop iteself. It also exercises the case where a loop +// is unreachable. +TEST_F(ValidateLimits, ControlFlowNoEntryToLoopGood) { + std::string str = header + R"( + OpName %entry "entry" + OpName %loop "loop" + OpName %exit "exit" +%voidt = OpTypeVoid +%boolt = OpTypeBool +%undef = OpUndef %boolt +%funct = OpTypeFunction %voidt +%main = OpFunction %voidt None %funct +%entry = OpLabel + OpBranch %exit +%loop = OpLabel + OpLoopMerge %dead %loop None + OpBranchConditional %undef %loop %loop +%dead = OpLabel + OpUnreachable +%exit = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_literals_test.cpp b/third_party/spirv-tools/test/val/val_literals_test.cpp new file mode 100644 index 0000000..6eadf32 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_literals_test.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for ilegal literals + +#include +#include + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateLiterals = spvtest::ValidateBase; +using ValidateLiteralsShader = spvtest::ValidateBase; +using ValidateLiteralsKernel = spvtest::ValidateBase; + +std::string GenerateShaderCode() { + std::string str = R"( + OpCapability Shader + OpCapability Linkage + OpCapability Int16 + OpCapability Int64 + OpCapability Float16 + OpCapability Float64 + OpMemoryModel Logical GLSL450 +%int16 = OpTypeInt 16 1 +%uint16 = OpTypeInt 16 0 +%int32 = OpTypeInt 32 1 +%uint32 = OpTypeInt 32 0 +%int64 = OpTypeInt 64 1 +%uint64 = OpTypeInt 64 0 +%half = OpTypeFloat 16 +%float = OpTypeFloat 32 +%double = OpTypeFloat 64 +%10 = OpTypeVoid + )"; + return str; +} + +std::string GenerateKernelCode() { + std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpCapability Int8 + OpMemoryModel Physical64 OpenCL +%uint8 = OpTypeInt 8 0 + )"; + return str; +} + +TEST_F(ValidateLiterals, LiteralsShaderGood) { + std::string str = GenerateShaderCode() + R"( +%11 = OpConstant %int16 !0x00007FFF +%12 = OpConstant %int16 !0xFFFF8000 +%13 = OpConstant %int16 !0xFFFFABCD +%14 = OpConstant %uint16 !0x0000ABCD +%15 = OpConstant %int16 -32768 +%16 = OpConstant %uint16 65535 +%17 = OpConstant %int32 -2147483648 +%18 = OpConstant %uint32 4294967295 +%19 = OpConstant %int64 -9223372036854775808 +%20 = OpConstant %uint64 18446744073709551615 +%21 = OpConstant %half !0x0000FFFF +%22 = OpConstant %float !0xFFFFFFFF +%23 = OpConstant %double !0xFFFFFFFF !0xFFFFFFFF + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLiterals, InvalidInt) { + std::string str = GenerateShaderCode() + R"( +%11 = OpTypeInt 32 90 + )"; + CompileSuccessfully(str); + EXPECT_EQ(SPV_ERROR_INVALID_VALUE, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpTypeInt has invalid signedness:")); +} + +TEST_P(ValidateLiteralsShader, LiteralsShaderBad) { + std::string str = GenerateShaderCode() + GetParam(); + std::string inst_id = "11"; + CompileSuccessfully(str); + EXPECT_EQ(SPV_ERROR_INVALID_VALUE, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The high-order bits of a literal number in instruction " + + inst_id + + " must be 0 for a floating-point type, " + "or 0 for an integer type with Signedness of 0, " + "or sign extended when Signedness is 1")); +} + +INSTANTIATE_TEST_SUITE_P( + LiteralsShaderCases, ValidateLiteralsShader, + ::testing::Values("%11 = OpConstant %int16 !0xFFFF0000", // Sign bit is 0 + "%11 = OpConstant %int16 !0x00008000", // Sign bit is 1 + "%11 = OpConstant %int16 !0xABCD8000", // Sign bit is 1 + "%11 = OpConstant %int16 !0xABCD0000", + "%11 = OpConstant %uint16 !0xABCD0000", + "%11 = OpConstant %half !0xABCD0000", + "%11 = OpConstant %half !0x00010000")); + +TEST_F(ValidateLiterals, LiteralsKernelGood) { + std::string str = GenerateKernelCode() + R"( +%4 = OpConstant %uint8 !0x000000AB +%6 = OpConstant %uint8 255 + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateLiteralsKernel, LiteralsKernelBad) { + std::string str = GenerateKernelCode() + GetParam(); + std::string inst_id = "2"; + CompileSuccessfully(str); + EXPECT_EQ(SPV_ERROR_INVALID_VALUE, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The high-order bits of a literal number in instruction " + + inst_id + + " must be 0 for a floating-point type, " + "or 0 for an integer type with Signedness of 0, " + "or sign extended when Signedness is 1")); +} + +INSTANTIATE_TEST_SUITE_P( + LiteralsKernelCases, ValidateLiteralsKernel, + ::testing::Values("%2 = OpConstant %uint8 !0xABCDEF00", + "%2 = OpConstant %uint8 !0xABCDEFFF")); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_logicals_test.cpp b/third_party/spirv-tools/test/val/val_logicals_test.cpp new file mode 100644 index 0000000..b57c743 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_logicals_test.cpp @@ -0,0 +1,1164 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for unique type declaration rules validator. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateLogicals = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + const std::string capabilities = + R"( +OpCapability Shader +OpCapability Int64 +OpCapability Float64)"; + + const std::string after_extension_before_body = + R"( +%ext_inst = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%s32 = OpTypeInt 32 1 +%f64 = OpTypeFloat 64 +%u64 = OpTypeInt 64 0 +%s64 = OpTypeInt 64 1 +%boolvec2 = OpTypeVector %bool 2 +%s32vec2 = OpTypeVector %s32 2 +%u32vec2 = OpTypeVector %u32 2 +%u64vec2 = OpTypeVector %u64 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%boolvec3 = OpTypeVector %bool 3 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%s32vec3 = OpTypeVector %s32 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%boolvec4 = OpTypeVector %bool 4 +%u32vec4 = OpTypeVector %u32 4 +%u64vec4 = OpTypeVector %u64 4 +%s32vec4 = OpTypeVector %s32 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 + +%s32_0 = OpConstant %s32 0 +%s32_1 = OpConstant %s32 1 +%s32_2 = OpConstant %s32 2 +%s32_3 = OpConstant %s32 3 +%s32_4 = OpConstant %s32 4 +%s32_m1 = OpConstant %s32 -1 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64_4 = OpConstant %f64 4 + +%s64_0 = OpConstant %s64 0 +%s64_1 = OpConstant %s64 1 +%s64_2 = OpConstant %s64 2 +%s64_3 = OpConstant %s64 3 +%s64_4 = OpConstant %s64 4 +%s64_m1 = OpConstant %s64 -1 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_4 = OpConstant %u64 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%s32vec2_01 = OpConstantComposite %s32vec2 %s32_0 %s32_1 +%s32vec2_12 = OpConstantComposite %s32vec2 %s32_1 %s32_2 +%s32vec3_012 = OpConstantComposite %s32vec3 %s32_0 %s32_1 %s32_2 +%s32vec3_123 = OpConstantComposite %s32vec3 %s32_1 %s32_2 %s32_3 +%s32vec4_0123 = OpConstantComposite %s32vec4 %s32_0 %s32_1 %s32_2 %s32_3 +%s32vec4_1234 = OpConstantComposite %s32vec4 %s32_1 %s32_2 %s32_3 %s32_4 + +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec3_123 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_3 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 +%f64vec4_1234 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_3 %f64_4 + +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%boolvec2_tf = OpConstantComposite %boolvec2 %true %false +%boolvec3_tft = OpConstantComposite %boolvec3 %true %false %true +%boolvec4_tftf = OpConstantComposite %boolvec4 %true %false %true %false + +%arr_u32_2 = OpTypeArray %u32 %u32_2 +%st_u32_u32 = OpTypeStruct %u32 %u32 +%mat_f32_2_2 = OpTypeMatrix %f32vec2 2 + +%nul_arr_u32_2 = OpConstantNull %arr_u32_2 +%nul_st_u32_u32 = OpConstantNull %st_u32_u32 +%nul_mat_f32_2_2 = OpConstantNull %mat_f32_2_2 + +%arr_u32_2_1_2 = OpConstantComposite %arr_u32_2 %u32_1 %u32_2 +%st_u32_u32_1_2 = OpConstantComposite %st_u32_u32 %u32_1 %u32_2 +%mat_f32_2_2_01_12 = OpConstantComposite %mat_f32_2_2 %f32vec2_01 %f32vec2_12 + +%f32vec4ptr = OpTypePointer Function %f32vec4 + +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string after_body = + R"( +OpReturn +OpFunctionEnd)"; + + return capabilities + capabilities_and_extensions + + after_extension_before_body + body + after_body; +} + +std::string GenerateKernelCode( + const std::string& body, + const std::string& capabilities_and_extensions = "") { + const std::string capabilities = + R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability Int64 +OpCapability Float64)"; + + const std::string after_extension_before_body = + R"( +OpMemoryModel Physical32 OpenCL +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%f64 = OpTypeFloat 64 +%u64 = OpTypeInt 64 0 +%boolvec2 = OpTypeVector %bool 2 +%u32vec2 = OpTypeVector %u32 2 +%u64vec2 = OpTypeVector %u64 2 +%f32vec2 = OpTypeVector %f32 2 +%f64vec2 = OpTypeVector %f64 2 +%boolvec3 = OpTypeVector %bool 3 +%u32vec3 = OpTypeVector %u32 3 +%u64vec3 = OpTypeVector %u64 3 +%f32vec3 = OpTypeVector %f32 3 +%f64vec3 = OpTypeVector %f64 3 +%boolvec4 = OpTypeVector %bool 4 +%u32vec4 = OpTypeVector %u32 4 +%u64vec4 = OpTypeVector %u64 4 +%f32vec4 = OpTypeVector %f32 4 +%f64vec4 = OpTypeVector %f64 4 + +%f32_0 = OpConstant %f32 0 +%f32_1 = OpConstant %f32 1 +%f32_2 = OpConstant %f32 2 +%f32_3 = OpConstant %f32 3 +%f32_4 = OpConstant %f32 4 + +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32_4 = OpConstant %u32 4 + +%f64_0 = OpConstant %f64 0 +%f64_1 = OpConstant %f64 1 +%f64_2 = OpConstant %f64 2 +%f64_3 = OpConstant %f64 3 +%f64_4 = OpConstant %f64 4 + +%u64_0 = OpConstant %u64 0 +%u64_1 = OpConstant %u64 1 +%u64_2 = OpConstant %u64 2 +%u64_3 = OpConstant %u64 3 +%u64_4 = OpConstant %u64 4 + +%u32vec2_01 = OpConstantComposite %u32vec2 %u32_0 %u32_1 +%u32vec2_12 = OpConstantComposite %u32vec2 %u32_1 %u32_2 +%u32vec3_012 = OpConstantComposite %u32vec3 %u32_0 %u32_1 %u32_2 +%u32vec3_123 = OpConstantComposite %u32vec3 %u32_1 %u32_2 %u32_3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 +%u32vec4_1234 = OpConstantComposite %u32vec4 %u32_1 %u32_2 %u32_3 %u32_4 + +%f32vec2_01 = OpConstantComposite %f32vec2 %f32_0 %f32_1 +%f32vec2_12 = OpConstantComposite %f32vec2 %f32_1 %f32_2 +%f32vec3_012 = OpConstantComposite %f32vec3 %f32_0 %f32_1 %f32_2 +%f32vec3_123 = OpConstantComposite %f32vec3 %f32_1 %f32_2 %f32_3 +%f32vec4_0123 = OpConstantComposite %f32vec4 %f32_0 %f32_1 %f32_2 %f32_3 +%f32vec4_1234 = OpConstantComposite %f32vec4 %f32_1 %f32_2 %f32_3 %f32_4 + +%f64vec2_01 = OpConstantComposite %f64vec2 %f64_0 %f64_1 +%f64vec2_12 = OpConstantComposite %f64vec2 %f64_1 %f64_2 +%f64vec3_012 = OpConstantComposite %f64vec3 %f64_0 %f64_1 %f64_2 +%f64vec3_123 = OpConstantComposite %f64vec3 %f64_1 %f64_2 %f64_3 +%f64vec4_0123 = OpConstantComposite %f64vec4 %f64_0 %f64_1 %f64_2 %f64_3 +%f64vec4_1234 = OpConstantComposite %f64vec4 %f64_1 %f64_2 %f64_3 %f64_4 + +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool +%boolvec2_tf = OpConstantComposite %boolvec2 %true %false +%boolvec3_tft = OpConstantComposite %boolvec3 %true %false %true +%boolvec4_tftf = OpConstantComposite %boolvec4 %true %false %true %false + +%f32vec4ptr = OpTypePointer Function %f32vec4 + +%main = OpFunction %void None %func +%main_entry = OpLabel)"; + + const std::string after_body = + R"( +OpReturn +OpFunctionEnd)"; + + return capabilities + capabilities_and_extensions + + after_extension_before_body + body + after_body; +} + +TEST_F(ValidateLogicals, OpAnySuccess) { + const std::string body = R"( +%val1 = OpAny %bool %boolvec2_tf +%val2 = OpAny %bool %boolvec3_tft +%val3 = OpAny %bool %boolvec4_tftf +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpAnyWrongTypeId) { + const std::string body = R"( +%val = OpAny %u32 %boolvec2_tf +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected bool scalar type as Result Type: Any")); +} + +TEST_F(ValidateLogicals, OpAnyWrongOperand) { + const std::string body = R"( +%val = OpAny %bool %u32vec3_123 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected operand to be vector bool: Any")); +} + +TEST_F(ValidateLogicals, OpIsNanSuccess) { + const std::string body = R"( +%val1 = OpIsNan %bool %f32_1 +%val2 = OpIsNan %bool %f64_0 +%val3 = OpIsNan %boolvec2 %f32vec2_12 +%val4 = OpIsNan %boolvec3 %f32vec3_123 +%val5 = OpIsNan %boolvec4 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpIsNanWrongTypeId) { + const std::string body = R"( +%val1 = OpIsNan %u32 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected bool scalar or vector type as Result Type: IsNan")); +} + +TEST_F(ValidateLogicals, OpIsNanOperandNotFloat) { + const std::string body = R"( +%val1 = OpIsNan %bool %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operand to be scalar or vector float: IsNan")); +} + +TEST_F(ValidateLogicals, OpIsNanOperandWrongSize) { + const std::string body = R"( +%val1 = OpIsNan %bool %f32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected vector sizes of Result Type and the operand to be equal: " + "IsNan")); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterSuccess) { + const std::string body = R"( +%val1 = OpLessOrGreater %bool %f32_0 %f32_1 +%val2 = OpLessOrGreater %bool %f64_0 %f64_0 +%val3 = OpLessOrGreater %boolvec2 %f32vec2_12 %f32vec2_12 +%val4 = OpLessOrGreater %boolvec3 %f32vec3_123 %f32vec3_123 +%val5 = OpLessOrGreater %boolvec4 %f32vec4_1234 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterWrongTypeId) { + const std::string body = R"( +%val1 = OpLessOrGreater %u32 %f32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected bool scalar or vector type as Result Type: LessOrGreater")); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterLeftOperandNotFloat) { + const std::string body = R"( +%val1 = OpLessOrGreater %bool %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected operands to be scalar or vector float: LessOrGreater")); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpLessOrGreater %bool %f32vec2_12 %f32_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "LessOrGreater")); +} + +TEST_F(ValidateLogicals, OpLessOrGreaterOperandsDifferentType) { + const std::string body = R"( +%val1 = OpLessOrGreater %bool %f32_1 %f64_1 +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected left and right operands to have the same type: " + "LessOrGreater")); +} + +TEST_F(ValidateLogicals, OpFOrdEqualSuccess) { + const std::string body = R"( +%val1 = OpFOrdEqual %bool %f32_0 %f32_1 +%val2 = OpFOrdEqual %bool %f64_0 %f64_0 +%val3 = OpFOrdEqual %boolvec2 %f32vec2_12 %f32vec2_12 +%val4 = OpFOrdEqual %boolvec3 %f32vec3_123 %f32vec3_123 +%val5 = OpFOrdEqual %boolvec4 %f32vec4_1234 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpFOrdEqualWrongTypeId) { + const std::string body = R"( +%val1 = OpFOrdEqual %u32 %f32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected bool scalar or vector type as Result Type: FOrdEqual")); +} + +TEST_F(ValidateLogicals, OpFOrdEqualLeftOperandNotFloat) { + const std::string body = R"( +%val1 = OpFOrdEqual %bool %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to be scalar or vector float: FOrdEqual")); +} + +TEST_F(ValidateLogicals, OpFOrdEqualLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpFOrdEqual %bool %f32vec2_12 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "FOrdEqual")); +} + +TEST_F(ValidateLogicals, OpFOrdEqualOperandsDifferentType) { + const std::string body = R"( +%val1 = OpFOrdEqual %bool %f32_1 %f64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected left and right operands to have the same type: " + "FOrdEqual")); +} + +TEST_F(ValidateLogicals, OpLogicalEqualSuccess) { + const std::string body = R"( +%val1 = OpLogicalEqual %bool %true %false +%val2 = OpLogicalEqual %boolvec2 %boolvec2_tf %boolvec2_tf +%val3 = OpLogicalEqual %boolvec3 %boolvec3_tft %boolvec3_tft +%val4 = OpLogicalEqual %boolvec4 %boolvec4_tftf %boolvec4_tftf +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpLogicalEqualWrongTypeId) { + const std::string body = R"( +%val1 = OpLogicalEqual %u32 %true %false +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected bool scalar or vector type as Result Type: LogicalEqual")); +} + +TEST_F(ValidateLogicals, OpLogicalEqualWrongLeftOperand) { + const std::string body = R"( +%val1 = OpLogicalEqual %bool %boolvec2_tf %false +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected both operands to be of Result Type: LogicalEqual")); +} + +TEST_F(ValidateLogicals, OpLogicalEqualWrongRightOperand) { + const std::string body = R"( +%val1 = OpLogicalEqual %boolvec2 %boolvec2_tf %false +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected both operands to be of Result Type: LogicalEqual")); +} + +TEST_F(ValidateLogicals, OpLogicalNotSuccess) { + const std::string body = R"( +%val1 = OpLogicalNot %bool %true +%val2 = OpLogicalNot %boolvec2 %boolvec2_tf +%val3 = OpLogicalNot %boolvec3 %boolvec3_tft +%val4 = OpLogicalNot %boolvec4 %boolvec4_tftf +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpLogicalNotWrongTypeId) { + const std::string body = R"( +%val1 = OpLogicalNot %u32 %true +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected bool scalar or vector type as Result Type: LogicalNot")); +} + +TEST_F(ValidateLogicals, OpLogicalNotWrongOperand) { + const std::string body = R"( +%val1 = OpLogicalNot %bool %boolvec2_tf +)"; + + CompileSuccessfully(GenerateKernelCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected operand to be of Result Type: LogicalNot")); +} + +TEST_F(ValidateLogicals, OpSelectSuccess) { + const std::string body = R"( +%val1 = OpSelect %u32 %true %u32_0 %u32_1 +%val2 = OpSelect %f32 %true %f32_0 %f32_1 +%val3 = OpSelect %f64 %true %f64_0 %f64_1 +%val4 = OpSelect %f32vec2 %boolvec2_tf %f32vec2_01 %f32vec2_12 +%val5 = OpSelect %f32vec4 %boolvec4_tftf %f32vec4_0123 %f32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpSelectWrongTypeId) { + const std::string body = R"( +%val1 = OpSelect %void %true %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar or vector type as Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectWrongTypeIdV14) { + // In 1.4, the message changes to allow composites. + const std::string body = R"( +%val1 = OpSelect %void %true %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar or composite type as Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectPointerNoCapability) { + const std::string body = R"( +%x = OpVariable %f32vec4ptr Function +%y = OpVariable %f32vec4ptr Function +OpStore %x %f32vec4_0123 +OpStore %y %f32vec4_1234 +%val1 = OpSelect %f32vec4ptr %true %x %y +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Using pointers with OpSelect requires capability VariablePointers " + "or VariablePointersStorageBuffer")); +} + +TEST_F(ValidateLogicals, OpSelectPointerWithCapability1) { + const std::string body = R"( +%x = OpVariable %f32vec4ptr Function +%y = OpVariable %f32vec4ptr Function +OpStore %x %f32vec4_0123 +OpStore %y %f32vec4_1234 +%val1 = OpSelect %f32vec4ptr %true %x %y +)"; + + const std::string extra_cap_ext = R"( +OpCapability VariablePointers +OpExtension "SPV_KHR_variable_pointers" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra_cap_ext).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpSelectPointerWithCapability2) { + const std::string body = R"( +%x = OpVariable %f32vec4ptr Function +%y = OpVariable %f32vec4ptr Function +OpStore %x %f32vec4_0123 +OpStore %y %f32vec4_1234 +%val1 = OpSelect %f32vec4ptr %true %x %y +)"; + + const std::string extra_cap_ext = R"( +OpCapability VariablePointersStorageBuffer +OpExtension "SPV_KHR_variable_pointers" +)"; + + CompileSuccessfully(GenerateShaderCode(body, extra_cap_ext).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpSelectWrongCondition) { + const std::string body = R"( +%val1 = OpSelect %u32 %u32_1 %u32_0 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected bool scalar or vector type as condition: Select")); +} + +TEST_F(ValidateLogicals, OpSelectWrongConditionDimension) { + const std::string body = R"( +%val1 = OpSelect %u32vec2 %true %u32vec2_01 %u32vec2_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected vector sizes of Result Type and the condition to be equal: " + "Select")); +} + +TEST_F(ValidateLogicals, OpSelectWrongLeftObject) { + const std::string body = R"( +%val1 = OpSelect %bool %true %u32vec2_01 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected both objects to be of Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectWrongRightObject) { + const std::string body = R"( +%val1 = OpSelect %bool %true %u32_1 %u32vec2_01 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected both objects to be of Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectArrayV13Bad) { + const std::string body = R"( +%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar or vector type as Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectArrayV13TargetV14Bad) { + const std::string body = R"( +%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected scalar or vector type as Result Type")); +} + +TEST_F(ValidateLogicals, OpSelectArrayV14Good) { + const std::string body = R"( +%val1 = OpSelect %arr_u32_2 %true %nul_arr_u32_2 %arr_u32_2_1_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateLogicals, OpSelectStructV13Bad) { + const std::string body = R"( +%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar or vector type as Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectStructV13TargetV14Bad) { + const std::string body = R"( +%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected scalar or vector type as Result Type")); +} + +TEST_F(ValidateLogicals, OpSelectStructV14Good) { + const std::string body = R"( +%val1 = OpSelect %st_u32_u32 %true %nul_st_u32_u32 %st_u32_u32_1_2 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateLogicals, OpSelectMatrixV13Bad) { + const std::string body = R"( +%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected scalar or vector type as Result Type: Select")); +} + +TEST_F(ValidateLogicals, OpSelectMatrixV13TargetV14Bad) { + const std::string body = R"( +%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected scalar or vector type as Result Type")); +} + +TEST_F(ValidateLogicals, OpSelectMatrixV14Good) { + const std::string body = R"( +%val1 = OpSelect %mat_f32_2_2 %true %nul_mat_f32_2_2 %mat_f32_2_2_01_12 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str(), SPV_ENV_UNIVERSAL_1_4); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateLogicals, OpIEqualSuccess) { + const std::string body = R"( +%val1 = OpIEqual %bool %u32_0 %s32_1 +%val2 = OpIEqual %bool %s64_0 %u64_0 +%val3 = OpIEqual %boolvec2 %s32vec2_12 %u32vec2_12 +%val4 = OpIEqual %boolvec3 %s32vec3_123 %u32vec3_123 +%val5 = OpIEqual %boolvec4 %s32vec4_1234 %u32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpIEqualWrongTypeId) { + const std::string body = R"( +%val1 = OpIEqual %u32 %s32_1 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected bool scalar or vector type as Result Type: IEqual")); +} + +TEST_F(ValidateLogicals, OpIEqualLeftOperandNotInt) { + const std::string body = R"( +%val1 = OpIEqual %bool %f32_1 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to be scalar or vector int: IEqual")); +} + +TEST_F(ValidateLogicals, OpIEqualLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpIEqual %bool %s32vec2_12 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "IEqual")); +} + +TEST_F(ValidateLogicals, OpIEqualRightOperandNotInt) { + const std::string body = R"( +%val1 = OpIEqual %bool %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to be scalar or vector int: IEqual")); +} + +TEST_F(ValidateLogicals, OpIEqualDifferentBitWidth) { + const std::string body = R"( +%val1 = OpIEqual %bool %u32_1 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected both operands to have the same component bit " + "width: IEqual")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanSuccess) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %u32_0 %u32_1 +%val2 = OpUGreaterThan %bool %s32_0 %u32_1 +%val3 = OpUGreaterThan %bool %u64_0 %u64_0 +%val4 = OpUGreaterThan %bool %u64_0 %s64_0 +%val5 = OpUGreaterThan %boolvec2 %u32vec2_12 %u32vec2_12 +%val6 = OpUGreaterThan %boolvec3 %s32vec3_123 %u32vec3_123 +%val7 = OpUGreaterThan %boolvec4 %u32vec4_1234 %u32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpUGreaterThanWrongTypeId) { + const std::string body = R"( +%val1 = OpUGreaterThan %u32 %u32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected bool scalar or vector type as Result Type: UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanLeftOperandNotInt) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %f32_1 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to be scalar or vector int: UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %u32vec2_12 %u32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanRightOperandNotInt) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %u32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to be scalar or vector int: UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpUGreaterThanDifferentBitWidth) { + const std::string body = R"( +%val1 = OpUGreaterThan %bool %u32_1 %u64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected both operands to have the same component bit width: " + "UGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanSuccess) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %s32_0 %s32_1 +%val2 = OpSGreaterThan %bool %u32_0 %s32_1 +%val3 = OpSGreaterThan %bool %s64_0 %s64_0 +%val4 = OpSGreaterThan %bool %s64_0 %u64_0 +%val5 = OpSGreaterThan %boolvec2 %s32vec2_12 %s32vec2_12 +%val6 = OpSGreaterThan %boolvec3 %s32vec3_123 %u32vec3_123 +%val7 = OpSGreaterThan %boolvec4 %s32vec4_1234 %s32vec4_1234 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, OpSGreaterThanWrongTypeId) { + const std::string body = R"( +%val1 = OpSGreaterThan %s32 %s32_1 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected bool scalar or vector type as Result Type: SGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanLeftOperandNotInt) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %f32_1 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to be scalar or vector int: SGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanLeftOperandWrongSize) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %s32vec2_12 %s32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Expected vector sizes of Result Type and the operands to be equal: " + "SGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanRightOperandNotInt) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %s32_1 %f32_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Expected operands to be scalar or vector int: SGreaterThan")); +} + +TEST_F(ValidateLogicals, OpSGreaterThanDifferentBitWidth) { + const std::string body = R"( +%val1 = OpSGreaterThan %bool %s32_1 %s64_1 +)"; + + CompileSuccessfully(GenerateShaderCode(body).c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected both operands to have the same component bit " + "width: SGreaterThan")); +} + +TEST_F(ValidateLogicals, PSBSelectSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointerEXT +%uint64 = OpTypeInt 64 0 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpSelect %ptr %true %val2 %val2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateLogicals, SelectVectorsScalarCondition) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%int4 = OpTypeVector %int 4 +%int4_0 = OpConstantNull %int4 +%true = OpConstantTrue %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%select = OpSelect %int4 %true %int4_0 %int4_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected vector sizes of Result Type and the " + "condition to be equal: Select")); +} + +TEST_F(ValidateLogicals, SelectVectorsScalarCondition1p4) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%int4 = OpTypeVector %int 4 +%int4_0 = OpConstantNull %int4 +%true = OpConstantTrue %bool +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%select = OpSelect %int4 %true %int4_0 %int4_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_F(ValidateLogicals, SelectVectorsVectorConditionMismatchedDimensions1p4) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%bool3 = OpTypeVector %bool 3 +%int = OpTypeInt 32 0 +%int4 = OpTypeVector %int 4 +%int4_0 = OpConstantNull %int4 +%bool3_null = OpConstantNull %bool3 +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%1 = OpLabel +%select = OpSelect %int4 %bool3_null %int4_0 %int4_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected vector sizes of Result Type and the " + "condition to be equal: Select")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_memory_test.cpp b/third_party/spirv-tools/test/val/val_memory_test.cpp new file mode 100644 index 0000000..b32867b --- /dev/null +++ b/third_party/spirv-tools/test/val/val_memory_test.cpp @@ -0,0 +1,4485 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for memory/storage + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Values; + +using ValidateMemory = spvtest::ValidateBase; + +TEST_F(ValidateMemory, VulkanUniformConstantOnNonOpaqueResourceBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer UniformConstant %float +%2 = OpVariable %float_ptr UniformConstant +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\n" + "Variables identified with the UniformConstant storage class " + "are used only as handles to refer to opaque resources. Such " + "variables must be typed as OpTypeImage, OpTypeSampler, " + "OpTypeSampledImage, OpTypeAccelerationStructureNV, " + "OpTypeAccelerationStructureKHR, OpTypeRayQueryProvisionalKHR, " + "or an array of one of these types.")); +} + +TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +%sampler = OpTypeSampler +%sampler_ptr = OpTypePointer UniformConstant %sampler +%2 = OpVariable %sampler_ptr UniformConstant +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanUniformConstantOnNonOpaqueResourceArrayBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%uint = OpTypeInt 32 0 +%array_size = OpConstant %uint 5 +%array = OpTypeArray %float %array_size +%array_ptr = OpTypePointer UniformConstant %array +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\n" + "Variables identified with the UniformConstant storage class " + "are used only as handles to refer to opaque resources. Such " + "variables must be typed as OpTypeImage, OpTypeSampler, " + "OpTypeSampledImage, OpTypeAccelerationStructureNV, " + "OpTypeAccelerationStructureKHR, OpTypeRayQueryProvisionalKHR, " + "or an array of one of these types.")); +} + +TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceArrayGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +%sampler = OpTypeSampler +%uint = OpTypeInt 32 0 +%array_size = OpConstant %uint 5 +%array = OpTypeArray %sampler %array_size +%array_ptr = OpTypePointer UniformConstant %array +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanUniformConstantOnOpaqueResourceRuntimeArrayGood) { + std::string spirv = R"( +OpCapability RuntimeDescriptorArrayEXT +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +%sampler = OpTypeSampler +%uint = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %sampler +%array_ptr = OpTypePointer UniformConstant %array +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanUniformOnIntBad) { + char src[] = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %kernel "main" + OpExecutionMode %kernel LocalSize 1 1 1 + + OpDecorate %var DescriptorSet 0 + OpDecorate %var Binding 0 + + %voidty = OpTypeVoid +%kernelty = OpTypeFunction %voidty + %intty = OpTypeInt 32 0 + %varty = OpTypePointer Uniform %intty + %value = OpConstant %intty 42 + + %var = OpVariable %varty Uniform + + %kernel = OpFunction %voidty None %kernelty + %label = OpLabel + OpStore %var %value + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\n" + "Variables identified with the Uniform storage class are used " + "to access transparent buffer backed resources. Such variables " + "must be typed as OpTypeStruct, or an array of this type")); +} + +// #version 440 +// #extension GL_EXT_nonuniform_qualifier : enable +// layout(binding = 1) uniform sampler2D s2d[][2]; +// layout(location = 0) in nonuniformEXT int i; +// void main() +// { +// vec4 v = texture(s2d[i][i], vec2(0.3)); +// } +TEST_F(ValidateMemory, VulkanUniformOnRuntimeArrayOfArrayBad) { + char src[] = R"( + OpCapability Shader + OpCapability ShaderNonUniformEXT + OpCapability RuntimeDescriptorArrayEXT + OpCapability SampledImageArrayNonUniformIndexingEXT + OpExtension "SPV_EXT_descriptor_indexing" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" %i + OpSource GLSL 440 + OpSourceExtension "GL_EXT_nonuniform_qualifier" + OpName %main "main" + OpName %v "v" + OpName %s2d "s2d" + OpName %i "i" + OpDecorate %s2d DescriptorSet 0 + OpDecorate %s2d Binding 1 + OpDecorate %i Location 0 + OpDecorate %i NonUniformEXT + OpDecorate %21 NonUniformEXT + OpDecorate %22 NonUniformEXT + OpDecorate %25 NonUniformEXT + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float + %10 = OpTypeImage %float 2D 0 0 0 1 Unknown + %11 = OpTypeSampledImage %10 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_11_uint_2 = OpTypeArray %11 %uint_2 +%_runtimearr__arr_11_uint_2 = OpTypeRuntimeArray %_arr_11_uint_2 +%_ptr_Uniform__runtimearr__arr_11_uint_2 = OpTypePointer Uniform %_runtimearr__arr_11_uint_2 + %s2d = OpVariable %_ptr_Uniform__runtimearr__arr_11_uint_2 Uniform + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %i = OpVariable %_ptr_Input_int Input +%_ptr_Uniform_11 = OpTypePointer Uniform %11 + %v2float = OpTypeVector %float 2 +%float_0_300000012 = OpConstant %float 0.300000012 + %28 = OpConstantComposite %v2float %float_0_300000012 %float_0_300000012 + %float_0 = OpConstant %float 0 + %main = OpFunction %void None %3 + %5 = OpLabel + %v = OpVariable %_ptr_Function_v4float Function + %21 = OpLoad %int %i + %22 = OpLoad %int %i + %24 = OpAccessChain %_ptr_Uniform_11 %s2d %21 %22 + %25 = OpLoad %11 %24 + %30 = OpImageSampleExplicitLod %v4float %25 %28 Lod %float_0 + OpStore %v %30 + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\n" + "Variables identified with the Uniform storage class are used " + "to access transparent buffer backed resources. Such variables " + "must be typed as OpTypeStruct, or an array of this type")); +} + +// #version 440 +// layout (set=1, binding=1) uniform sampler2D variableName[2][2]; +// void main() { +// } +TEST_F(ValidateMemory, VulkanUniformOnArrayOfArrayBad) { + char src[] = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpSource GLSL 440 + OpName %main "main" + OpName %variableName "variableName" + OpDecorate %variableName DescriptorSet 1 + OpDecorate %variableName Binding 1 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %7 = OpTypeImage %float 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %uint = OpTypeInt 32 0 + %uint_2 = OpConstant %uint 2 +%_arr_8_uint_2 = OpTypeArray %8 %uint_2 +%_arr__arr_8_uint_2_uint_2 = OpTypeArray %_arr_8_uint_2 %uint_2 +%_ptr_Uniform__arr__arr_8_uint_2_uint_2 = OpTypePointer Uniform %_arr__arr_8_uint_2_uint_2 +%variableName = OpVariable %_ptr_Uniform__arr__arr_8_uint_2_uint_2 Uniform + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(src, SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\n" + "Variables identified with the Uniform storage class are used " + "to access transparent buffer backed resources. Such variables " + "must be typed as OpTypeStruct, or an array of this type")); +} + +TEST_F(ValidateMemory, MismatchingStorageClassesBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Uniform %float +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +%2 = OpVariable %float_ptr Function +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "From SPIR-V spec, section 3.32.8 on OpVariable:\n" + "Its Storage Class operand must be the same as the Storage Class " + "operand of the result type.")); +} + +TEST_F(ValidateMemory, MatchingStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Function %float +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +%2 = OpVariable %float_ptr Function +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, WebGPUInitializerWithOutputStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Output %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Output %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateMemory, WebGPUInitializerWithFunctionStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Function %float +%init_val = OpConstant %float 1.0 +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +%2 = OpVariable %float_ptr Function %init_val +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateMemory, WebGPUInitializerWithPrivateStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Private %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Private %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateMemory, WebGPUInitializerWithDisallowedStorageClassesBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Uniform %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Uniform %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpVariable, '5[%5]', has a disallowed initializer & " + "storage class combination.\nFrom WebGPU spec:\nVariable " + "declarations that include initializers must have one of the " + "following storage classes: Output, Private, or Function\n %5 " + "= OpVariable %_ptr_Uniform_float Uniform %float_1\n")); +} + +TEST_F(ValidateMemory, WebGPUOutputStorageClassWithoutInitializerBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Output %float +%1 = OpVariable %float_ptr Output +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpVariable, '4[%4]', must have an initializer.\n" + "From WebGPU execution environment spec:\n" + "All variables in the following storage classes must have an " + "initializer: Output, Private, or Function\n" + " %4 = OpVariable %_ptr_Output_float Output\n")); +} + +TEST_F(ValidateMemory, WebGPUFunctionStorageClassWithoutInitializerBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Function %float +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +%2 = OpVariable %float_ptr Function +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpVariable, '7[%7]', must have an initializer.\n" + "From WebGPU execution environment spec:\n" + "All variables in the following storage classes must have an " + "initializer: Output, Private, or Function\n" + " %7 = OpVariable %_ptr_Function_float Function\n")); +} + +TEST_F(ValidateMemory, WebGPUPrivateStorageClassWithoutInitializerBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Private %float +%1 = OpVariable %float_ptr Private +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpVariable, '4[%4]', must have an initializer.\n" + "From WebGPU execution environment spec:\n" + "All variables in the following storage classes must have an " + "initializer: Output, Private, or Function\n" + " %4 = OpVariable %_ptr_Private_float Private\n")); +} + +TEST_F(ValidateMemory, VulkanInitializerWithOutputStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Output %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Output %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanInitializerWithFunctionStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Function %float +%init_val = OpConstant %float 1.0 +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +%2 = OpVariable %float_ptr Function %init_val +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanInitializerWithPrivateStorageClassesGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Private %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Private %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanInitializerWithDisallowedStorageClassesBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%float = OpTypeFloat 32 +%float_ptr = OpTypePointer Input %float +%init_val = OpConstant %float 1.0 +%1 = OpVariable %float_ptr Input %init_val +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%2 = OpLabel +OpReturn +OpFunctionEnd +)"; + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpVariable, '5[%5]', has a disallowed initializer & " + "storage class combination.\nFrom Vulkan spec:\nVariable " + "declarations that include initializers must have one of the " + "following storage classes: Output, Private, or Function\n %5 " + "= OpVariable %_ptr_Input_float Input %float_1\n")); +} + +TEST_F(ValidateMemory, ArrayLenCorrectResultType) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %11 = OpArrayLength %uint %10 0 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, ArrayLenIndexCorrectWith2Members) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %float %_runtimearr_float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %11 = OpArrayLength %uint %10 1 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, ArrayLenResultNotIntType) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_6 = OpTypeStruct %_runtimearr_float +%_ptr_Function__struct_6 = OpTypePointer Function %_struct_6 + %1 = OpFunction %void None %3 + %8 = OpLabel + %9 = OpVariable %_ptr_Function__struct_6 Function + %10 = OpArrayLength %float %9 0 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "The Result Type of OpArrayLength '10[%10]' must be OpTypeInt " + "with width 32 and signedness 0.\n %10 = OpArrayLength %float %9 " + "0\n")); +} + +TEST_F(ValidateMemory, ArrayLenResultNot32bits) { + std::string spirv = R"( + OpCapability Shader + OpCapability Int16 + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %ushort = OpTypeInt 16 0 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %11 = OpArrayLength %ushort %10 0 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "The Result Type of OpArrayLength '11[%11]' must be OpTypeInt " + "with width 32 and signedness 0.\n %11 = OpArrayLength %ushort %10 " + "0\n")); +} + +TEST_F(ValidateMemory, ArrayLenResultSigned) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %int = OpTypeInt 32 1 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %11 = OpArrayLength %int %10 0 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "The Result Type of OpArrayLength '11[%11]' must be OpTypeInt " + "with width 32 and signedness 0.\n %11 = OpArrayLength %int %10 " + "0\n")); +} + +TEST_F(ValidateMemory, ArrayLenInputNotStruct) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float +%_ptr_Function_float = OpTypePointer Function %float + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function_float Function + %11 = OpArrayLength %uint %10 0 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The Struture's type in OpArrayLength '11[%11]' " + "must be a pointer to an OpTypeStruct.")); +} + +TEST_F(ValidateMemory, ArrayLenInputLastMemberNoRTA) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %11 = OpArrayLength %uint %10 0 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The Struture's last member in OpArrayLength '11[%11]' " + "must be an OpTypeRuntimeArray.\n %11 = OpArrayLength %uint " + "%10 0\n")); +} + +TEST_F(ValidateMemory, ArrayLenInputLastMemberNoRTA2) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %_runtimearr_float %float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %11 = OpArrayLength %uint %10 1 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The Struture's last member in OpArrayLength '11[%11]' " + "must be an OpTypeRuntimeArray.\n %11 = OpArrayLength %uint " + "%10 1\n")); +} + +TEST_F(ValidateMemory, ArrayLenIndexNotLastMember) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %float %_runtimearr_float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %11 = OpArrayLength %uint %10 0 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "The array member in OpArrayLength '11[%11]' must be an the " + "last member of the struct.\n %11 = OpArrayLength %uint %10 0\n")); +} + +TEST_F(ValidateMemory, ArrayLenIndexNotPointerToStruct) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 +%_runtimearr_float = OpTypeRuntimeArray %float + %_struct_7 = OpTypeStruct %float %_runtimearr_float +%_ptr_Function__struct_7 = OpTypePointer Function %_struct_7 + %1 = OpFunction %void None %3 + %9 = OpLabel + %10 = OpVariable %_ptr_Function__struct_7 Function + %11 = OpLoad %_struct_7 %10 + %12 = OpArrayLength %uint %11 0 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "The Struture's type in OpArrayLength '12[%12]' must be a " + "pointer to an OpTypeStruct.\n %12 = OpArrayLength %uint %11 0\n")); +} + +TEST_F(ValidateMemory, ArrayLenPointerIsAType) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %uint = OpTypeInt 32 0 + %1 = OpFunction %void None %3 + %9 = OpLabel + %12 = OpArrayLength %uint %float 0 + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv.c_str()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Operand 4[%float] cannot be a " + "type")); +} + +TEST_F(ValidateMemory, PushConstantNotStructGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %ptr = OpTypePointer PushConstant %float + %pc = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, VulkanPushConstantNotStructBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %ptr = OpTypePointer PushConstant %float + %pc = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("PushConstant OpVariable '6[%6]' has illegal " + "type.\nFrom Vulkan spec, section 14.5.1:\n" + "Such variables must be typed as OpTypeStruct, " + "or an array of this type")); +} + +TEST_F(ValidateMemory, VulkanPushConstant) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "main" + OpExecutionMode %1 OriginUpperLeft + + OpDecorate %struct Block + OpMemberDecorate %struct 0 Offset 0 + + %void = OpTypeVoid + %voidfn = OpTypeFunction %void + %float = OpTypeFloat 32 + %struct = OpTypeStruct %float + %ptr = OpTypePointer PushConstant %struct + %pc = OpVariable %ptr PushConstant + + %1 = OpFunction %void None %voidfn + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeLoadBad1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%load = OpLoad %int %var MakePointerVisibleKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeLoadBad2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%load = OpLoad %int %var Aligned|MakePointerVisibleKHR|NonPrivatePointerKHR 4 %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeLoadGood1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%load = OpLoad %int %var MakePointerVisibleKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeLoadGood2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +%load = OpLoad %int %var Aligned|MakePointerVisibleKHR|NonPrivatePointerKHR 4 %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeStoreBad1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpStore %var %device MakePointerAvailableKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeStoreBad2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpStore %var %device Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeStoreGood1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpStore %var %device MakePointerAvailableKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeStoreGood2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpStore %var %device Aligned|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryBad1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 MakePointerAvailableKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryBad2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 Aligned|MakePointerVisibleKHR|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %device %workgroup +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryBad3) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 Aligned|MakePointerVisibleKHR|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %workgroup %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryGood2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 Aligned|MakePointerVisibleKHR|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %device %workgroup +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemoryGood3) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 Aligned|MakePointerVisibleKHR|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %workgroup %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessAvVisBadBinaryV13) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 + MakePointerAvailableKHR|NonPrivatePointerKHR %device + MakePointerVisibleKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "with two memory access operands requires SPIR-V 1.4 or later")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessAvVisGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 + MakePointerAvailableKHR|NonPrivatePointerKHR %device + MakePointerVisibleKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessFirstWithAvBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 + MakePointerAvailableKHR|NonPrivatePointerKHR %device + MakePointerAvailableKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Source memory access must not include MakePointerAvailableKHR\n" + " OpCopyMemory %5 %6 MakePointerAvailable|NonPrivatePointer" + " %uint_1 MakePointerAvailable|NonPrivatePointer %uint_1")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelCopyMemoryTwoAccessSecondWithVisBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 + MakePointerVisibleKHR|NonPrivatePointerKHR %device + MakePointerVisibleKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Target memory access must not include MakePointerVisibleKHR\n" + " OpCopyMemory %5 %6 MakePointerVisible|NonPrivatePointer" + " %uint_1 MakePointerVisible|NonPrivatePointer %uint_1")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedBad1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpCapability Addresses +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %device MakePointerAvailableKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedBad2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpCapability Addresses +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %device Aligned|MakePointerVisibleKHR|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %device %workgroup +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedBad3) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability Linkage +OpCapability Addresses +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %device Aligned|MakePointerVisibleKHR|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %workgroup %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Use of device scope with VulkanKHR memory model requires the " + "VulkanMemoryModelDeviceScopeKHR capability")); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedGood1) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpCapability Addresses +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %device MakePointerAvailableKHR|NonPrivatePointerKHR %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedGood2) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpCapability Addresses +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %device Aligned|MakePointerVisibleKHR|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %device %workgroup +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanMemoryModelDeviceScopeCopyMemorySizedGood3) { + const std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpCapability VulkanMemoryModelDeviceScopeKHR +OpCapability Linkage +OpCapability Addresses +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%device = OpConstant %int 1 +%workgroup = OpConstant %int 2 +%int_ptr_ssbo = OpTypePointer StorageBuffer %int +%var1 = OpVariable %int_ptr_ssbo StorageBuffer +%var2 = OpVariable %int_ptr_ssbo StorageBuffer +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %device Aligned|MakePointerVisibleKHR|MakePointerAvailableKHR|NonPrivatePointerKHR 4 %workgroup %device +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, ArrayLengthStructIsLabel) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpName %20 "incorrect" +%void = OpTypeVoid +%3 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%uint = OpTypeInt 32 0 +%4 = OpFunction %void None %3 +%20 = OpLabel +%24 = OpArrayLength %uint %20 0 +%25 = OpLoad %v4float %24 +OpReturnValue %25 +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 1[%incorrect] requires a type")); +} + +TEST_F(ValidateMemory, PSBLoadAlignedSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointerEXT +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %uint64 %val2 Aligned 8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, PSBLoadAlignedMissing) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointerEXT +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +%val3 = OpLoad %uint64 %val2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Memory accesses with PhysicalStorageBufferEXT must use Aligned")); +} + +TEST_F(ValidateMemory, PSBStoreAlignedSuccess) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointerEXT +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +OpStore %val2 %u64_1 Aligned 8 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, PSBStoreAlignedMissing) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointerEXT +%uint64 = OpTypeInt 64 0 +%u64_1 = OpConstant %uint64 1 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%pptr_f = OpTypePointer Function %ptr +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +%val1 = OpVariable %pptr_f Function +%val2 = OpLoad %ptr %val1 +OpStore %val2 %u64_1 None +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Memory accesses with PhysicalStorageBufferEXT must use Aligned")); +} + +TEST_F(ValidateMemory, PSBVariable) { + const std::string body = R"( +OpCapability PhysicalStorageBufferAddressesEXT +OpCapability Int64 +OpCapability Shader +OpExtension "SPV_EXT_physical_storage_buffer" +OpMemoryModel PhysicalStorageBuffer64EXT GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %val1 AliasedPointerEXT +%uint64 = OpTypeInt 64 0 +%ptr = OpTypePointer PhysicalStorageBufferEXT %uint64 +%val1 = OpVariable %ptr PhysicalStorageBufferEXT +%void = OpTypeVoid +%voidfn = OpTypeFunction %void +%main = OpFunction %void None %voidfn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(body); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("PhysicalStorageBufferEXT must not be used with OpVariable")); +} + +std::string GenCoopMatLoadStoreShader(const std::string& storeMemoryAccess, + const std::string& loadMemoryAccess) { + std::string s = R"( +OpCapability Shader +OpCapability GroupNonUniform +OpCapability VulkanMemoryModelKHR +OpCapability CooperativeMatrixNV +OpExtension "SPV_KHR_vulkan_memory_model" +OpExtension "SPV_NV_cooperative_matrix" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical VulkanKHR +OpEntryPoint GLCompute %4 "main" %11 %21 +OpExecutionMode %4 LocalSize 1 1 1 +OpDecorate %11 BuiltIn SubgroupId +OpDecorate %21 BuiltIn WorkgroupId +OpDecorate %74 ArrayStride 4 +OpMemberDecorate %75 0 Offset 0 +OpDecorate %75 Block +OpDecorate %77 DescriptorSet 0 +OpDecorate %77 Binding 0 +OpDecorate %92 ArrayStride 4 +OpMemberDecorate %93 0 Offset 0 +OpDecorate %93 Block +OpDecorate %95 DescriptorSet 0 +OpDecorate %95 Binding 1 +OpDecorate %102 ArrayStride 4 +OpMemberDecorate %103 0 Offset 0 +OpDecorate %103 Block +OpDecorate %105 DescriptorSet 0 +OpDecorate %105 Binding 2 +OpDecorate %117 ArrayStride 4 +OpMemberDecorate %118 0 Offset 0 +OpDecorate %118 Block +OpDecorate %120 DescriptorSet 0 +OpDecorate %120 Binding 3 +OpDecorate %123 SpecId 2 +OpDecorate %124 SpecId 3 +OpDecorate %125 SpecId 4 +OpDecorate %126 SpecId 5 +OpDecorate %127 SpecId 0 +OpDecorate %128 SpecId 1 +OpDecorate %129 BuiltIn WorkgroupSize +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%6 = OpTypeInt 32 0 +%7 = OpTypeVector %6 2 +%8 = OpTypePointer Function %7 +%10 = OpTypePointer Input %6 +%11 = OpVariable %10 Input +%13 = OpConstant %6 2 +%19 = OpTypeVector %6 3 +%20 = OpTypePointer Input %19 +%21 = OpVariable %20 Input +%27 = OpConstantComposite %7 %13 %13 +%31 = OpTypePointer Function %6 +%33 = OpConstant %6 1024 +%34 = OpConstant %6 1 +%38 = OpConstant %6 8 +%39 = OpConstant %6 0 +%68 = OpTypeFloat 32 +%69 = OpConstant %6 16 +%70 = OpConstant %6 3 +%71 = OpTypeCooperativeMatrixNV %68 %70 %69 %38 +%72 = OpTypePointer Function %71 +%74 = OpTypeRuntimeArray %68 +%75 = OpTypeStruct %74 +%76 = OpTypePointer StorageBuffer %75 +%77 = OpVariable %76 StorageBuffer +%78 = OpTypeInt 32 1 +%79 = OpConstant %78 0 +%81 = OpConstant %6 5 +%82 = OpTypePointer StorageBuffer %68 +%84 = OpConstant %6 64 +%85 = OpTypeBool +%86 = OpConstantFalse %85 +%88 = OpTypePointer Private %71 +%89 = OpVariable %88 Private +%92 = OpTypeRuntimeArray %68 +%93 = OpTypeStruct %92 +%94 = OpTypePointer StorageBuffer %93 +%95 = OpVariable %94 StorageBuffer +%99 = OpVariable %88 Private +%102 = OpTypeRuntimeArray %68 +%103 = OpTypeStruct %102 +%104 = OpTypePointer StorageBuffer %103 +%105 = OpVariable %104 StorageBuffer +%109 = OpVariable %88 Private +%111 = OpVariable %88 Private +%112 = OpSpecConstantOp %6 CooperativeMatrixLengthNV %71 +%113 = OpSpecConstantOp %78 IAdd %112 %79 +%117 = OpTypeRuntimeArray %68 +%118 = OpTypeStruct %117 +%119 = OpTypePointer StorageBuffer %118 +%120 = OpVariable %119 StorageBuffer +%123 = OpSpecConstant %78 1 +%124 = OpSpecConstant %78 1 +%125 = OpSpecConstant %78 1 +%126 = OpSpecConstant %78 1 +%127 = OpSpecConstant %6 1 +%128 = OpSpecConstant %6 1 +%129 = OpSpecConstantComposite %19 %127 %128 %34 +%4 = OpFunction %2 None %3 +%5 = OpLabel +%9 = OpVariable %8 Function +%18 = OpVariable %8 Function +%32 = OpVariable %31 Function +%44 = OpVariable %31 Function +%52 = OpVariable %31 Function +%60 = OpVariable %31 Function +%73 = OpVariable %72 Function +%91 = OpVariable %72 Function +%101 = OpVariable %72 Function +%12 = OpLoad %6 %11 +%14 = OpUMod %6 %12 %13 +%15 = OpLoad %6 %11 +%16 = OpUDiv %6 %15 %13 +%17 = OpCompositeConstruct %7 %14 %16 +OpStore %9 %17 +%22 = OpLoad %19 %21 +%23 = OpVectorShuffle %7 %22 %22 0 1 +%24 = OpCompositeExtract %6 %23 0 +%25 = OpCompositeExtract %6 %23 1 +%26 = OpCompositeConstruct %7 %24 %25 +%28 = OpIMul %7 %26 %27 +%29 = OpLoad %7 %9 +%30 = OpIAdd %7 %28 %29 +OpStore %18 %30 +%35 = OpAccessChain %31 %18 %34 +%36 = OpLoad %6 %35 +%37 = OpIMul %6 %33 %36 +%40 = OpAccessChain %31 %18 %39 +%41 = OpLoad %6 %40 +%42 = OpIMul %6 %38 %41 +%43 = OpIAdd %6 %37 %42 +OpStore %32 %43 +%45 = OpAccessChain %31 %18 %34 +%46 = OpLoad %6 %45 +%47 = OpIMul %6 %33 %46 +%48 = OpAccessChain %31 %18 %39 +%49 = OpLoad %6 %48 +%50 = OpIMul %6 %38 %49 +%51 = OpIAdd %6 %47 %50 +OpStore %44 %51 +%53 = OpAccessChain %31 %18 %34 +%54 = OpLoad %6 %53 +%55 = OpIMul %6 %33 %54 +%56 = OpAccessChain %31 %18 %39 +%57 = OpLoad %6 %56 +%58 = OpIMul %6 %38 %57 +%59 = OpIAdd %6 %55 %58 +OpStore %52 %59 +%61 = OpAccessChain %31 %18 %34 +%62 = OpLoad %6 %61 +%63 = OpIMul %6 %33 %62 +%64 = OpAccessChain %31 %18 %39 +%65 = OpLoad %6 %64 +%66 = OpIMul %6 %38 %65 +%67 = OpIAdd %6 %63 %66 +OpStore %60 %67 +%80 = OpLoad %6 %32 +%83 = OpAccessChain %82 %77 %79 %80 +%87 = OpCooperativeMatrixLoadNV %71 %83 %84 %86 )" + + loadMemoryAccess + R"( %81 +OpStore %73 %87 +%90 = OpLoad %71 %73 +OpStore %89 %90 +%96 = OpLoad %6 %44 +%97 = OpAccessChain %82 %95 %79 %96 +%98 = OpCooperativeMatrixLoadNV %71 %97 %84 %86 MakePointerVisibleKHR|NonPrivatePointerKHR %81 +OpStore %91 %98 +%100 = OpLoad %71 %91 +OpStore %99 %100 +%106 = OpLoad %6 %52 +%107 = OpAccessChain %82 %105 %79 %106 +%108 = OpCooperativeMatrixLoadNV %71 %107 %84 %86 MakePointerVisibleKHR|NonPrivatePointerKHR %81 +OpStore %101 %108 +%110 = OpLoad %71 %101 +OpStore %109 %110 +%114 = OpConvertSToF %68 %113 +%115 = OpCompositeConstruct %71 %114 +OpStore %111 %115 +%116 = OpLoad %71 %111 +%121 = OpLoad %6 %60 +%122 = OpAccessChain %82 %120 %79 %121 +OpCooperativeMatrixStoreNV %122 %116 %84 %86 )" + storeMemoryAccess + R"( %81 +OpReturn +OpFunctionEnd +)"; + + return s; +} + +TEST_F(ValidateMemory, CoopMatLoadStoreSuccess) { + std::string spirv = + GenCoopMatLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerVisibleKHR|NonPrivatePointerKHR"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, CoopMatStoreMemoryAccessFail) { + std::string spirv = + GenCoopMatLoadStoreShader("MakePointerVisibleKHR|NonPrivatePointerKHR", + "MakePointerVisibleKHR|NonPrivatePointerKHR"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MakePointerVisibleKHR cannot be used with OpStore")); +} + +TEST_F(ValidateMemory, CoopMatLoadMemoryAccessFail) { + std::string spirv = + GenCoopMatLoadStoreShader("MakePointerAvailableKHR|NonPrivatePointerKHR", + "MakePointerAvailableKHR|NonPrivatePointerKHR"); + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("MakePointerAvailableKHR cannot be used with OpLoad")); +} + +TEST_F(ValidateMemory, CoopMatInvalidStorageClassFail) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%u32 = OpTypeInt 32 0 + +%u32_8 = OpConstant %u32 8 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 + +%str = OpTypeStruct %f16mat +%str_ptr = OpTypePointer Workgroup %str +%sh = OpVariable %str_ptr Workgroup + +%main = OpFunction %void None %func +%main_entry = OpLabel + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Cooperative matrix types (or types containing them) can only be " + "allocated in Function or Private storage classes or as function " + "parameters")); +} + +TEST_F(ValidateMemory, CoopMatMatrixLengthResultTypeBad) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%u32 = OpTypeInt 32 0 +%i32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%1 = OpCooperativeMatrixLengthNV %i32 %f16mat + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The Result Type of OpCooperativeMatrixLengthNV " + "'11[%11]' must be OpTypeInt with width 32 and signedness 0")); +} + +TEST_F(ValidateMemory, CoopMatMatrixLengthOperandTypeBad) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%u32 = OpTypeInt 32 0 +%i32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%1 = OpCooperativeMatrixLengthNV %u32 %u32 + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("The type in OpCooperativeMatrixLengthNV '5[%uint]' " + "must be OpTypeCooperativeMatrixNV")); +} + +TEST_F(ValidateMemory, CoopMatMatrixLengthGood) { + const std::string body = + R"( +OpCapability Shader +OpCapability Float16 +OpCapability CooperativeMatrixNV +OpExtension "SPV_NV_cooperative_matrix" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +%void = OpTypeVoid +%func = OpTypeFunction %void +%f16 = OpTypeFloat 16 +%u32 = OpTypeInt 32 0 +%i32 = OpTypeInt 32 1 + +%u32_8 = OpConstant %u32 8 +%subgroup = OpConstant %u32 3 + +%f16mat = OpTypeCooperativeMatrixNV %f16 %subgroup %u32_8 %u32_8 + +%main = OpFunction %void None %func +%main_entry = OpLabel + +%1 = OpCooperativeMatrixLengthNV %u32 %f16mat + +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(body.c_str()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, VulkanRTAOutsideOfStructBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%sampler_t = OpTypeSampler +%array_t = OpTypeRuntimeArray %sampler_t +%array_ptr = OpTypePointer UniformConstant %array_t +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpVariable, '5[%5]', is attempting to create memory for an " + "illegal type, OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray " + "can only appear as the final member of an OpTypeStruct, thus cannot " + "be instantiated via OpVariable\n %5 = OpVariable " + "%_ptr_UniformConstant__runtimearr_2 UniformConstant\n")); +} + +TEST_F(ValidateMemory, WebGPURTAOutsideOfStructBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%sampler_t = OpTypeSampler +%array_t = OpTypeRuntimeArray %sampler_t +%array_ptr = OpTypePointer UniformConstant %array_t +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpVariable, '5[%5]', is attempting to create memory for an " + "illegal type, OpTypeRuntimeArray.\nFor WebGPU OpTypeRuntimeArray " + "can only appear as the final member of an OpTypeStruct, thus cannot " + "be instantiated via OpVariable\n %5 = OpVariable " + "%_ptr_UniformConstant__runtimearr_2 UniformConstant\n")); +} + +TEST_F(ValidateMemory, VulkanRTAOutsideOfStructWithRuntimeDescriptorArrayGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +%sampler_t = OpTypeSampler +%uint = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %sampler_t +%struct = OpTypeStruct %uint +%sb_array_t = OpTypeRuntimeArray %struct +%array_sb_ptr = OpTypePointer StorageBuffer %sb_array_t +%2 = OpVariable %array_sb_ptr StorageBuffer +%array_uc_ptr = OpTypePointer UniformConstant %array_t +%3 = OpVariable %array_uc_ptr UniformConstant +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F( + ValidateMemory, + VulkanRTAOutsideOfStructWithRuntimeDescriptorArrayAndWrongStorageClassBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%array_ptr = OpTypePointer Workgroup %array_t +%2 = OpVariable %array_ptr Workgroup +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("For Vulkan with RuntimeDescriptorArrayEXT, a variable " + "containing OpTypeRuntimeArray must have storage class of " + "StorageBuffer, Uniform, or UniformConstant.\n %5 = " + "OpVariable %_ptr_Workgroup__runtimearr_uint Workgroup\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideStorageBufferStructGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t +%2 = OpVariable %struct_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, WebGPURTAInsideStorageBufferStructGood) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t +%2 = OpVariable %struct_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateMemory, VulkanRTAInsideWrongStorageClassStructBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer Workgroup %struct_t +%2 = OpVariable %struct_ptr Workgroup +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "For Vulkan, OpTypeStruct variables containing OpTypeRuntimeArray " + "must have storage class of StorageBuffer or Uniform.\n %6 = " + "OpVariable %_ptr_Workgroup__struct_4 Workgroup\n")); +} + +TEST_F(ValidateMemory, WebGPURTAInsideWrongStorageClassStructBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer Workgroup %struct_t +%2 = OpVariable %struct_ptr Workgroup +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("For WebGPU, OpTypeStruct variables containing " + "OpTypeRuntimeArray must have storage class of StorageBuffer\n " + " %6 = OpVariable %_ptr_Workgroup__struct_4 Workgroup\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideStorageBufferStructWithoutBlockBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t +%2 = OpVariable %struct_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For Vulkan, an OpTypeStruct variable containing an " + "OpTypeRuntimeArray must be decorated with Block if it " + "has storage class StorageBuffer.\n %6 = OpVariable " + "%_ptr_StorageBuffer__struct_4 StorageBuffer\n")); +} + +TEST_F(ValidateMemory, WebGPURTAInsideStorageBufferStructWithoutBlockBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t +%2 = OpVariable %struct_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, an OpTypeStruct variable containing an " + "OpTypeRuntimeArray must be decorated with Block if it " + "has storage class StorageBuffer.\n %6 = OpVariable " + "%_ptr_StorageBuffer__struct_4 StorageBuffer\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideUniformStructGood) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t BufferBlock +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer Uniform %struct_t +%2 = OpVariable %struct_ptr Uniform +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, WebGPURTAInsideUniformStructBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer Uniform %struct_t +%2 = OpVariable %struct_ptr Uniform +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("For WebGPU, OpTypeStruct variables containing " + "OpTypeRuntimeArray must have storage class of StorageBuffer\n " + " %6 = OpVariable %_ptr_Uniform__struct_3 Uniform\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlockBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 +%array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer Uniform %struct_t +%2 = OpVariable %struct_ptr Uniform +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For Vulkan, an OpTypeStruct variable containing an " + "OpTypeRuntimeArray must be decorated with BufferBlock " + "if it has storage class Uniform.\n %6 = OpVariable " + "%_ptr_Uniform__struct_4 Uniform\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideRTABad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%sampler_t = OpTypeSampler +%inner_array_t = OpTypeRuntimeArray %sampler_t +%array_t = OpTypeRuntimeArray %inner_array_t +%array_ptr = OpTypePointer UniformConstant %array_t +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeRuntimeArray Element Type '3[%_runtimearr_2]' is not " + "valid in Vulkan environments.\n %_runtimearr__runtimearr_2 = " + "OpTypeRuntimeArray %_runtimearr_2\n")); +} + +TEST_F(ValidateMemory, WebGPURTAInsideRTABad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%sampler_t = OpTypeSampler +%inner_array_t = OpTypeRuntimeArray %sampler_t +%array_t = OpTypeRuntimeArray %inner_array_t +%array_ptr = OpTypePointer UniformConstant %array_t +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeRuntimeArray Element Type '3[%_runtimearr_2]' is not " + "valid in WebGPU environments.\n %_runtimearr__runtimearr_2 = " + "OpTypeRuntimeArray %_runtimearr_2\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideRTAWithRuntimeDescriptorArrayBad) { + std::string spirv = R"( +OpCapability RuntimeDescriptorArrayEXT +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t Block +%uint_t = OpTypeInt 32 0 +%inner_array_t = OpTypeRuntimeArray %uint_t +%array_t = OpTypeRuntimeArray %inner_array_t +%array_ptr = OpTypePointer StorageBuffer %array_t +%2 = OpVariable %array_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeRuntimeArray Element Type '4[%_runtimearr_uint]' is not " + "valid in Vulkan environments.\n %_runtimearr__runtimearr_uint = " + "OpTypeRuntimeArray %_runtimearr_uint\n")); +} + +TEST_F(ValidateMemory, + VulkanUniformStructInsideRTAWithRuntimeDescriptorArrayGood) { + std::string spirv = R"( +OpCapability RuntimeDescriptorArrayEXT +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%struct_t = OpTypeStruct %uint_t +%array_t = OpTypeRuntimeArray %struct_t +%array_ptr = OpTypePointer Uniform %array_t +%2 = OpVariable %array_ptr Uniform +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanRTAInsideRTAInsideStructBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%inner_array_t = OpTypeRuntimeArray %uint_t +%array_t = OpTypeRuntimeArray %inner_array_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t +%2 = OpVariable %struct_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeRuntimeArray Element Type '5[%_runtimearr_uint]' is not " + "valid in Vulkan environments.\n %_runtimearr__runtimearr_uint = " + "OpTypeRuntimeArray %_runtimearr_uint\n")); +} + +TEST_F(ValidateMemory, + VulkanRTAInsideRTAInsideStructWithRuntimeDescriptorArrayBad) { + std::string spirv = R"( +OpCapability RuntimeDescriptorArrayEXT +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%inner_array_t = OpTypeRuntimeArray %uint_t +%array_t = OpTypeRuntimeArray %inner_array_t +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t +%2 = OpVariable %struct_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeRuntimeArray Element Type '5[%_runtimearr_uint]' is not " + "valid in Vulkan environments.\n %_runtimearr__runtimearr_uint = " + "OpTypeRuntimeArray %_runtimearr_uint\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideArrayBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 +%dim = OpConstant %uint_t 1 +%sampler_t = OpTypeSampler +%inner_array_t = OpTypeRuntimeArray %sampler_t +%array_t = OpTypeArray %inner_array_t %dim +%array_ptr = OpTypePointer UniformConstant %array_t +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeArray Element Type '5[%_runtimearr_4]' is not " + "valid in Vulkan environments.\n %_arr__runtimearr_4_uint_1 = " + "OpTypeArray %_runtimearr_4 %uint_1\n")); +} + +TEST_F(ValidateMemory, WebGPURTAInsideArrayBad) { + std::string spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%uint_t = OpTypeInt 32 0 +%dim = OpConstant %uint_t 1 +%sampler_t = OpTypeSampler +%inner_array_t = OpTypeRuntimeArray %sampler_t +%array_t = OpTypeArray %inner_array_t %dim +%array_ptr = OpTypePointer UniformConstant %array_t +%2 = OpVariable %array_ptr UniformConstant +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpTypeArray Element Type '5[%_runtimearr_4]' is not " + "valid in WebGPU environments.\n %_arr__runtimearr_4_uint_1 = " + "OpTypeArray %_runtimearr_4 %uint_1\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideArrayWithRuntimeDescriptorArrayBad) { + std::string spirv = R"( +OpCapability RuntimeDescriptorArrayEXT +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t Block +%uint_t = OpTypeInt 32 0 +%dim = OpConstant %uint_t 1 +%sampler_t = OpTypeSampler +%inner_array_t = OpTypeRuntimeArray %uint_t +%array_t = OpTypeRuntimeArray %inner_array_t +%array_ptr = OpTypePointer StorageBuffer %array_t +%2 = OpVariable %array_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeRuntimeArray Element Type '6[%_runtimearr_uint]' is not " + "valid in Vulkan environments.\n %_runtimearr__runtimearr_uint = " + "OpTypeRuntimeArray %_runtimearr_uint\n")); +} + +TEST_F(ValidateMemory, VulkanRTAInsideArrayInsideStructBad) { + std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%dim = OpConstant %uint_t 1 +%inner_array_t = OpTypeRuntimeArray %uint_t +%array_t = OpTypeArray %inner_array_t %dim +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t +%2 = OpVariable %struct_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeArray Element Type '6[%_runtimearr_uint]' is not " + "valid in Vulkan environments.\n %_arr__runtimearr_uint_uint_1 " + "= OpTypeArray %_runtimearr_uint %uint_1\n")); +} + +TEST_F(ValidateMemory, + VulkanRTAInsideArrayInsideStructWithRuntimeDescriptorArrayBad) { + std::string spirv = R"( +OpCapability RuntimeDescriptorArrayEXT +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%dim = OpConstant %uint_t 1 +%inner_array_t = OpTypeRuntimeArray %uint_t +%array_t = OpTypeArray %inner_array_t %dim +%struct_t = OpTypeStruct %array_t +%struct_ptr = OpTypePointer StorageBuffer %struct_t +%2 = OpVariable %struct_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpTypeArray Element Type '6[%_runtimearr_uint]' is not " + "valid in Vulkan environments.\n %_arr__runtimearr_uint_uint_1 " + "= OpTypeArray %_runtimearr_uint %uint_1\n")); +} + +TEST_F(ValidateMemory, VulkanRTAStructInsideRTAWithRuntimeDescriptorArrayGood) { + std::string spirv = R"( +OpCapability RuntimeDescriptorArrayEXT +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %inner_array_t ArrayStride 4 +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%inner_array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %inner_array_t +%array_t = OpTypeRuntimeArray %struct_t +%array_ptr = OpTypePointer StorageBuffer %array_t +%2 = OpVariable %array_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, VulkanRTAStructInsideArrayGood) { + std::string spirv = R"( +OpCapability RuntimeDescriptorArrayEXT +OpCapability Shader +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +OpDecorate %inner_array_t ArrayStride 4 +OpDecorate %array_t ArrayStride 4 +OpMemberDecorate %struct_t 0 Offset 0 +OpDecorate %struct_t Block +%uint_t = OpTypeInt 32 0 +%inner_array_t = OpTypeRuntimeArray %uint_t +%struct_t = OpTypeStruct %inner_array_t +%array_size = OpConstant %uint_t 5 +%array_t = OpTypeArray %struct_t %array_size +%array_ptr = OpTypePointer StorageBuffer %array_t +%2 = OpVariable %array_ptr StorageBuffer +%void = OpTypeVoid +%func_t = OpTypeFunction %void +%func = OpFunction %void None %func_t +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv.c_str(), SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, CopyMemoryNoAccessGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_ptr_priv = OpTypePointer Private %int +%var1 = OpVariable %int_ptr_priv Private +%var2 = OpVariable %int_ptr_priv Private +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateMemory, CopyMemorySimpleMixedAccessGood) { + // Test one memory access operand using features that don't require the + // Vulkan memory model. + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_ptr_priv = OpTypePointer Private %int +%var1 = OpVariable %int_ptr_priv Private +%var2 = OpVariable %int_ptr_priv Private +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 Volatile|Aligned|Nontemporal 4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateMemory, CopyMemorySimpleTwoMixedAccessV13Bad) { + // Two memory access operands is invalid up to SPIR-V 1.3 + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_ptr_priv = OpTypePointer Private %int +%var1 = OpVariable %int_ptr_priv Private +%var2 = OpVariable %int_ptr_priv Private +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 Volatile Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("CopyMemory with two memory access operands requires " + "SPIR-V 1.4 or later")); +} + +TEST_F(ValidateMemory, CopyMemorySimpleTwoMixedAccessV14Good) { + // Two memory access operands is valid in SPIR-V 1.4 + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_ptr_priv = OpTypePointer Private %int +%var1 = OpVariable %int_ptr_priv Private +%var2 = OpVariable %int_ptr_priv Private +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemory %var1 %var2 Volatile Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateMemory, CopyMemorySizedNoAccessGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_16 = OpConstant %int 16 +%int_ptr_priv = OpTypePointer Private %int +%var1 = OpVariable %int_ptr_priv Private +%var2 = OpVariable %int_ptr_priv Private +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %int_16 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +TEST_F(ValidateMemory, CopyMemorySizedSimpleMixedAccessGood) { + // Test one memory access operand using features that don't require the + // Vulkan memory model. + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_16 = OpConstant %int 16 +%int_ptr_priv = OpTypePointer Private %int +%var1 = OpVariable %int_ptr_priv Private +%var2 = OpVariable %int_ptr_priv Private +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %int_16 Volatile|Aligned|Nontemporal 4 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, CopyMemorySizedSimpleTwoMixedAccessV13Bad) { + // Two memory access operands is invalid up to SPIR-V 1.3 + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_16 = OpConstant %int 16 +%int_ptr_priv = OpTypePointer Private %int +%var1 = OpVariable %int_ptr_priv Private +%var2 = OpVariable %int_ptr_priv Private +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %int_16 Volatile Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("CopyMemorySized with two memory access operands requires " + "SPIR-V 1.4 or later")); +} + +TEST_F(ValidateMemory, CopyMemorySizedSimpleTwoMixedAccessV14Good) { + // Two memory access operands is valid in SPIR-V 1.4 + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability Addresses +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_16 = OpConstant %int 16 +%int_ptr_priv = OpTypePointer Private %int +%var1 = OpVariable %int_ptr_priv Private +%var2 = OpVariable %int_ptr_priv Private +%voidfn = OpTypeFunction %void +%func = OpFunction %void None %voidfn +%entry = OpLabel +OpCopyMemorySized %var1 %var2 %int_16 Volatile Volatile +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), Eq("")); +} + +using ValidatePointerComparisons = spvtest::ValidateBase; + +TEST_P(ValidatePointerComparisons, Good) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_int StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_P(ValidatePointerComparisons, GoodWorkgroup) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointers +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int Workgroup +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); +} + +TEST_P(ValidatePointerComparisons, BadResultType) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_int StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %bool "; + } else { + spirv += " %int "; + } + + spirv += R"(%var %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + if (operation == "OpPtrDiff") { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be an integer scalar")); + } else { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Result Type must be OpTypeBool")); + } +} + +TEST_P(ValidatePointerComparisons, BadCapabilities) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_int StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + if (operation == "OpPtrDiff") { + // Gets caught by the grammar. + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Instruction cannot be used without a variable " + "pointers capability")); + } +} + +TEST_P(ValidatePointerComparisons, BadOperandType) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer StorageBuffer %int +%var = OpVariable %ptr_int StorageBuffer +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %int %var +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%ld %ld +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand type must be a pointer")); +} + +TEST_P(ValidatePointerComparisons, BadStorageClassWorkgroup) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer Workgroup %int +%var = OpVariable %ptr_int Workgroup +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Workgroup storage class pointer requires " + "VariablePointers capability to be specified")); +} + +TEST_P(ValidatePointerComparisons, BadStorageClass) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer Private %int +%var = OpVariable %ptr_int Private +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %var +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid pointer storage class")); +} + +TEST_P(ValidatePointerComparisons, BadDiffOperandTypes) { + const std::string operation = GetParam(); + + std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%bool = OpTypeBool +%int = OpTypeInt 32 0 +%ptr_int = OpTypePointer Private %int +%var = OpVariable %ptr_int Private +%func_ty = OpTypeFunction %void +%func = OpFunction %void None %func_ty +%1 = OpLabel +%ld = OpLoad %int %var +%equal = )" + operation; + + if (operation == "OpPtrDiff") { + spirv += " %int "; + } else { + spirv += " %bool "; + } + + spirv += R"(%var %ld +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_4); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_4)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The types of Operand 1 and Operand 2 must match")); +} + +INSTANTIATE_TEST_SUITE_P(PointerComparisons, ValidatePointerComparisons, + Values("OpPtrEqual", "OpPtrNotEqual", "OpPtrDiff")); + +TEST_F(ValidateMemory, VariableInitializerWrongType) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability VariablePointersStorageBuffer +OpMemoryModel Logical GLSL450 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%ptr_wg_int = OpTypePointer Workgroup %int +%ptr_wg_float = OpTypePointer Workgroup %int +%wg_var = OpVariable %ptr_wg_int Workgroup +%ptr_private_wg_float = OpTypePointer Private %ptr_wg_float +%priv_var = OpVariable %ptr_private_wg_float Private %wg_var +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Initializer type must match the type pointed to by " + "the Result Type")); +} + +TEST_F(ValidateMemory, StoreToUniformBlock) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_struct Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 +%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMemory, StoreToUniformBlockVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_struct Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 +%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +// This test requires that the struct is not id 2. +TEST_F(ValidateMemory, StoreToUniformBlockVulkan2) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gid_var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %3 Block +OpMemberDecorate %3 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %gid_var BuiltIn GlobalInvocationId +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int3 = OpTypeVector %int 3 +%int4 = OpTypeVector %int 4 +%3 = OpTypeStruct %int4 +%ptr_uniform_struct = OpTypePointer Uniform %3 +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_struct Uniform +%ptr_input_int3 = OpTypePointer Input %int3 +%gid_var = OpVariable %ptr_input_int3 Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 +%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +TEST_F(ValidateMemory, StoreToUniformBufferBlockVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct BufferBlock +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_struct Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 +%gep2 = OpAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidateMemory, StoreToUniformBlockVulkanArray) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%array_struct = OpTypeArray %struct %int_1 +%ptr_uniform_array = OpTypePointer Uniform %array_struct +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_array Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int %var %int_0 %int_0 %int_0 +%gep2 = OpCopyObject %ptr_uniform_int %gep1 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +// This test requires that the struct is not id 2. +TEST_F(ValidateMemory, StoreToUniformBlockVulkanArray2) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %gid_var +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +OpDecorate %gid_var BuiltIn GlobalInvocationId +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int3 = OpTypeVector %int 3 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%array_struct = OpTypeArray %struct %int_1 +%ptr_uniform_array = OpTypePointer Uniform %array_struct +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_array Uniform +%ptr_input_int3 = OpTypePointer Input %int3 +%gid_var = OpVariable %ptr_input_int3 Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int %var %int_0 %int_0 %int_0 +%gep2 = OpCopyObject %ptr_uniform_int %gep1 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +TEST_F(ValidateMemory, StoreToUniformBlockVulkanRuntimeArray) { + const std::string spirv = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpDecorate %struct Block +OpMemberDecorate %struct 0 Offset 0 +OpDecorate %var DescriptorSet 0 +OpDecorate %var Binding 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int4 = OpTypeVector %int 4 +%struct = OpTypeStruct %int4 +%array_struct = OpTypeRuntimeArray %struct +%ptr_uniform_array = OpTypePointer Uniform %array_struct +%ptr_uniform_struct = OpTypePointer Uniform %struct +%ptr_uniform_int4 = OpTypePointer Uniform %int4 +%ptr_uniform_int = OpTypePointer Uniform %int +%var = OpVariable %ptr_uniform_array Uniform +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%gep1 = OpAccessChain %ptr_uniform_int4 %var %int_0 %int_0 +%gep2 = OpInBoundsAccessChain %ptr_uniform_int %gep1 %int_0 +OpStore %gep2 %int_0 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, cannot store to Uniform Blocks")); +} + +using ValidateSizedVariable = + spvtest::ValidateBase>; + +CodeGenerator GetSizedVariableCodeGenerator(bool is_8bit) { + CodeGenerator generator; + generator.capabilities_ = "OpCapability Shader\nOpCapability Linkage\n"; + generator.extensions_ = + "OpExtension \"SPV_KHR_16bit_storage\"\nOpExtension " + "\"SPV_KHR_8bit_storage\"\n"; + generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; + if (is_8bit) { + generator.before_types_ = R"(OpDecorate %char_buffer_block BufferBlock +OpMemberDecorate %char_buffer_block 0 Offset 0 +)"; + generator.types_ = R"(%void = OpTypeVoid +%char = OpTypeInt 8 0 +%char4 = OpTypeVector %char 4 +%char_buffer_block = OpTypeStruct %char +)"; + } else { + generator.before_types_ = R"(OpDecorate %half_buffer_block BufferBlock +OpDecorate %short_buffer_block BufferBlock +OpMemberDecorate %half_buffer_block 0 Offset 0 +OpMemberDecorate %short_buffer_block 0 Offset 0 +)"; + generator.types_ = R"(%void = OpTypeVoid +%short = OpTypeInt 16 0 +%half = OpTypeFloat 16 +%short4 = OpTypeVector %short 4 +%half4 = OpTypeVector %half 4 +%mat4x4 = OpTypeMatrix %half4 4 +%short_buffer_block = OpTypeStruct %short +%half_buffer_block = OpTypeStruct %half +)"; + } + generator.after_types_ = R"(%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +)"; + generator.add_at_the_end_ = "OpReturn\nOpFunctionEnd\n"; + return generator; +} + +TEST_P(ValidateSizedVariable, Capability) { + const std::string storage_class = std::get<0>(GetParam()); + const std::string capability = std::get<1>(GetParam()); + const std::string var_type = std::get<2>(GetParam()); + + bool type_8bit = false; + if (var_type == "%char" || var_type == "%char4" || + var_type == "%char_buffer_block") { + type_8bit = true; + } + + auto generator = GetSizedVariableCodeGenerator(type_8bit); + generator.types_ += "%ptr_type = OpTypePointer " + storage_class + " " + + var_type + "\n%var = OpVariable %ptr_type " + + storage_class + "\n"; + generator.capabilities_ += "OpCapability " + capability + "\n"; + + bool capability_ok = false; + bool storage_class_ok = false; + if (storage_class == "Input" || storage_class == "Output") { + if (!type_8bit) { + capability_ok = capability == "StorageInputOutput16"; + storage_class_ok = true; + } + } else if (storage_class == "StorageBuffer") { + if (type_8bit) { + capability_ok = capability == "StorageBuffer8BitAccess" || + capability == "UniformAndStorageBuffer8BitAccess"; + } else { + capability_ok = capability == "StorageBuffer16BitAccess" || + capability == "UniformAndStorageBuffer16BitAccess"; + } + storage_class_ok = true; + } else if (storage_class == "PushConstant") { + if (type_8bit) { + capability_ok = capability == "StoragePushConstant8"; + } else { + capability_ok = capability == "StoragePushConstant16"; + } + storage_class_ok = true; + } else if (storage_class == "Uniform") { + bool buffer_block = var_type.find("buffer_block") != std::string::npos; + if (type_8bit) { + capability_ok = capability == "UniformAndStorageBuffer8BitAccess" || + (capability == "StorageBuffer8BitAccess" && buffer_block); + } else { + capability_ok = + capability == "UniformAndStorageBuffer16BitAccess" || + (capability == "StorageBuffer16BitAccess" && buffer_block); + } + storage_class_ok = true; + } + + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + spv_result_t result = ValidateInstructions(SPV_ENV_UNIVERSAL_1_3); + if (capability_ok) { + EXPECT_EQ(SPV_SUCCESS, result); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_ID, result); + if (storage_class_ok) { + std::string message = std::string("Allocating a variable containing a ") + + (type_8bit ? "8" : "16") + "-bit element in " + + storage_class + + " storage class requires an additional capability"; + EXPECT_THAT(getDiagnosticString(), HasSubstr(message)); + } else { + std::string message = + std::string("Cannot allocate a variable containing a ") + + (type_8bit ? "8" : "16") + "-bit type in " + storage_class + + " storage class"; + EXPECT_THAT(getDiagnosticString(), HasSubstr(message)); + } + } +} + +INSTANTIATE_TEST_SUITE_P( + Storage8, ValidateSizedVariable, + Combine(Values("UniformConstant", "Input", "Output", "Workgroup", + "CrossWorkgroup", "Private", "StorageBuffer", "Uniform"), + Values("StorageBuffer8BitAccess", + "UniformAndStorageBuffer8BitAccess", "StoragePushConstant8"), + Values("%char", "%char4", "%char_buffer_block"))); + +INSTANTIATE_TEST_SUITE_P( + Storage16, ValidateSizedVariable, + Combine(Values("UniformConstant", "Input", "Output", "Workgroup", + "CrossWorkgroup", "Private", "StorageBuffer", "Uniform"), + Values("StorageBuffer16BitAccess", + "UniformAndStorageBuffer16BitAccess", + "StoragePushConstant16", "StorageInputOutput16"), + Values("%short", "%half", "%short4", "%half4", "%mat4x4", + "%short_buffer_block", "%half_buffer_block"))); + +using ValidateSizedLoadStore = + spvtest::ValidateBase>; + +CodeGenerator GetSizedLoadStoreCodeGenerator(const std::string& base_type, + uint32_t width) { + CodeGenerator generator; + generator.capabilities_ = "OpCapability Shader\nOpCapability Linkage\n"; + if (width == 8) { + generator.capabilities_ += + "OpCapability UniformAndStorageBuffer8BitAccess\n"; + generator.extensions_ = "OpExtension \"SPV_KHR_8bit_storage\"\n"; + } else { + generator.capabilities_ += + "OpCapability UniformAndStorageBuffer16BitAccess\n"; + generator.extensions_ = "OpExtension \"SPV_KHR_16bit_storage\"\n"; + } + generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; + generator.before_types_ = R"(OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %struct 0 Offset 0 +)"; + generator.types_ = R"(%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_3 = OpConstant %int 3 +)"; + + if (width == 8) { + generator.types_ += R"(%scalar = OpTypeInt 8 0 +%vector = OpTypeVector %scalar 4 +%struct = OpTypeStruct %vector +)"; + } else if (base_type == "int") { + generator.types_ += R"(%scalar = OpTypeInt 16 0 +%vector = OpTypeVector %scalar 4 +%struct = OpTypeStruct %vector +)"; + } else { + generator.types_ += R"(%scalar = OpTypeFloat 16 +%vector = OpTypeVector %scalar 4 +%matrix = OpTypeMatrix %vector 4 +%struct = OpTypeStruct %matrix +%ptr_ssbo_matrix = OpTypePointer StorageBuffer %matrix +)"; + generator.before_types_ += R"(OpMemberDecorate %struct 0 RowMajor +OpMemberDecorate %struct 0 MatrixStride 16 +)"; + } + generator.types_ += R"(%block = OpTypeStruct %struct +%ptr_ssbo_block = OpTypePointer StorageBuffer %block +%ptr_ssbo_struct = OpTypePointer StorageBuffer %struct +%ptr_ssbo_vector = OpTypePointer StorageBuffer %vector +%ptr_ssbo_scalar = OpTypePointer StorageBuffer %scalar +%ld_var = OpVariable %ptr_ssbo_block StorageBuffer +%st_var = OpVariable %ptr_ssbo_block StorageBuffer +)"; + + generator.after_types_ = R"(%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +)"; + generator.add_at_the_end_ = "OpReturn\nOpFunctionEnd\n"; + return generator; +} + +TEST_P(ValidateSizedLoadStore, Load) { + std::string base_type = std::get<0>(GetParam()); + uint32_t width = std::get<1>(GetParam()); + std::string mem_type = std::get<2>(GetParam()); + + CodeGenerator generator = GetSizedLoadStoreCodeGenerator(base_type, width); + generator.after_types_ += + "%ld_gep = OpAccessChain %ptr_ssbo_" + mem_type + " %ld_var %int_0"; + if (mem_type != "struct") { + generator.after_types_ += " %int_0"; + if (mem_type != "matrix" && base_type == "float") { + generator.after_types_ += " %int_0"; + } + if (mem_type == "scalar") { + generator.after_types_ += " %int_0"; + } + } + generator.after_types_ += "\n"; + generator.after_types_ += "%ld = OpLoad %" + mem_type + " %ld_gep\n"; + + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + if (mem_type == "struct") { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "8- or 16-bit loads must be a scalar, vector or matrix type")); + } else { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + } +} + +TEST_P(ValidateSizedLoadStore, Store) { + std::string base_type = std::get<0>(GetParam()); + uint32_t width = std::get<1>(GetParam()); + std::string mem_type = std::get<2>(GetParam()); + + CodeGenerator generator = GetSizedLoadStoreCodeGenerator(base_type, width); + generator.after_types_ += + "%ld_gep = OpAccessChain %ptr_ssbo_" + mem_type + " %ld_var %int_0"; + if (mem_type != "struct") { + generator.after_types_ += " %int_0"; + if (mem_type != "matrix" && base_type == "float") { + generator.after_types_ += " %int_0"; + } + if (mem_type == "scalar") { + generator.after_types_ += " %int_0"; + } + } + generator.after_types_ += "\n"; + generator.after_types_ += "%ld = OpLoad %" + mem_type + " %ld_gep\n"; + generator.after_types_ += + "%st_gep = OpAccessChain %ptr_ssbo_" + mem_type + " %st_var %int_0"; + if (mem_type != "struct") { + generator.after_types_ += " %int_0"; + if (mem_type != "matrix" && base_type == "float") { + generator.after_types_ += " %int_0"; + } + if (mem_type == "scalar") { + generator.after_types_ += " %int_0"; + } + } + generator.after_types_ += "\n"; + generator.after_types_ += "OpStore %st_gep %ld\n"; + + CompileSuccessfully(generator.Build(), SPV_ENV_UNIVERSAL_1_3); + if (mem_type == "struct") { + EXPECT_EQ(SPV_ERROR_INVALID_ID, + ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + // Can only catch the load. + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "8- or 16-bit loads must be a scalar, vector or matrix type")); + } else { + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + } +} + +INSTANTIATE_TEST_SUITE_P(LoadStoreInt8, ValidateSizedLoadStore, + Combine(Values("int"), Values(8u), + Values("scalar", "vector", "struct"))); +INSTANTIATE_TEST_SUITE_P(LoadStoreInt16, ValidateSizedLoadStore, + Combine(Values("int"), Values(16u), + Values("scalar", "vector", "struct"))); +INSTANTIATE_TEST_SUITE_P(LoadStoreFloat16, ValidateSizedLoadStore, + Combine(Values("float"), Values(16u), + Values("scalar", "vector", "matrix", + "struct"))); + +TEST_F(ValidateMemory, SmallStorageCopyMemoryChar) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability UniformAndStorageBuffer8BitAccess +OpExtension "SPV_KHR_8bit_storage" +OpMemoryModel Logical GLSL450 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%char = OpTypeInt 8 0 +%block = OpTypeStruct %char +%ptr_ssbo_block = OpTypePointer StorageBuffer %block +%in = OpVariable %ptr_ssbo_block StorageBuffer +%out = OpVariable %ptr_ssbo_block StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %out %in +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot copy memory of objects containing 8- or 16-bit types")); +} + +TEST_F(ValidateMemory, SmallStorageCopyMemoryShort) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability UniformAndStorageBuffer16BitAccess +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%short = OpTypeInt 16 0 +%block = OpTypeStruct %short +%ptr_ssbo_block = OpTypePointer StorageBuffer %block +%in = OpVariable %ptr_ssbo_block StorageBuffer +%out = OpVariable %ptr_ssbo_block StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %out %in +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot copy memory of objects containing 8- or 16-bit types")); +} + +TEST_F(ValidateMemory, SmallStorageCopyMemoryHalf) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability UniformAndStorageBuffer16BitAccess +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int_0 = OpConstant %int 0 +%half = OpTypeFloat 16 +%block = OpTypeStruct %half +%ptr_ssbo_block = OpTypePointer StorageBuffer %block +%in = OpVariable %ptr_ssbo_block StorageBuffer +%out = OpVariable %ptr_ssbo_block StorageBuffer +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entry = OpLabel +OpCopyMemory %out %in +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot copy memory of objects containing 8- or 16-bit types")); +} + +TEST_F(ValidateMemory, SmallStorageVariableArrayBufferBlockShort) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpDecorate %block BufferBlock +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%short = OpTypeInt 16 0 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%block = OpTypeStruct %short +%block_array = OpTypeArray %block %int_4 +%ptr_block_array = OpTypePointer Uniform %block_array +%var = OpVariable %ptr_block_array Uniform +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, SmallStorageVariableArrayBufferBlockChar) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer8BitAccess +OpExtension "SPV_KHR_8bit_storage" +OpMemoryModel Logical GLSL450 +OpDecorate %block BufferBlock +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%char = OpTypeInt 8 0 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%block = OpTypeStruct %char +%block_array = OpTypeArray %block %int_4 +%ptr_block_array = OpTypePointer Uniform %block_array +%var = OpVariable %ptr_block_array Uniform +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, SmallStorageVariableArrayBufferBlockHalf) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +OpDecorate %block BufferBlock +OpMemberDecorate %block 0 Offset 0 +%void = OpTypeVoid +%half = OpTypeFloat 16 +%int = OpTypeInt 32 0 +%int_4 = OpConstant %int 4 +%block = OpTypeStruct %half +%block_array = OpTypeArray %block %int_4 +%ptr_block_array = OpTypePointer Uniform %block_array +%var = OpVariable %ptr_block_array Uniform +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_3); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); +} + +TEST_F(ValidateMemory, VulkanStorageBufferNotAStruct) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%ptr_ssbo = OpTypePointer StorageBuffer %uint +%var = OpVariable %ptr_ssbo StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with " + "the StorageBuffer storage class are used to access " + "transparent buffer backed resources. Such variables must be " + "typed as OpTypeStruct, or an array of this type")); +} + +TEST_F(ValidateMemory, VulkanStorageBufferRuntimeArrayNotAStruct) { + const std::string spirv = R"( +OpCapability Shader +OpCapability RuntimeDescriptorArrayEXT +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_EXT_descriptor_indexing" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%array = OpTypeRuntimeArray %uint +%ptr_ssbo = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr_ssbo StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with " + "the StorageBuffer storage class are used to access " + "transparent buffer backed resources. Such variables must be " + "typed as OpTypeStruct, or an array of this type")); +} + +TEST_F(ValidateMemory, VulkanStorageBufferArrayNotAStruct) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_4 = OpConstant %uint 4 +%array = OpTypeArray %uint %uint_4 +%ptr_ssbo = OpTypePointer StorageBuffer %array +%var = OpVariable %ptr_ssbo StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_0); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("From Vulkan spec, section 14.5.2:\nVariables identified with " + "the StorageBuffer storage class are used to access " + "transparent buffer backed resources. Such variables must be " + "typed as OpTypeStruct, or an array of this type")); +} + +TEST_F(ValidateMemory, PhysicalStorageBufferPtrEqual) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%long = OpTypeInt 64 0 +%long_0 = OpConstant %long 0 +%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%conv = OpConvertUToPtr %ptr_pssbo_long %long_0 +%eq = OpPtrEqual %bool %conv %conv +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Cannot use a pointer in the PhysicalStorageBuffer storage class")); +} + +TEST_F(ValidateMemory, PhysicalStorageBufferPtrNotEqual) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%bool = OpTypeBool +%long = OpTypeInt 64 0 +%long_0 = OpConstant %long 0 +%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%conv = OpConvertUToPtr %ptr_pssbo_long %long_0 +%neq = OpPtrNotEqual %bool %conv %conv +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Cannot use a pointer in the PhysicalStorageBuffer storage class")); +} + +TEST_F(ValidateMemory, PhysicalStorageBufferPtrDiff) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Int64 +OpCapability PhysicalStorageBufferAddresses +OpCapability VariablePointers +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +%void = OpTypeVoid +%long = OpTypeInt 64 0 +%long_0 = OpConstant %long 0 +%ptr_pssbo_long = OpTypePointer PhysicalStorageBuffer %long +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +%conv = OpConvertUToPtr %ptr_pssbo_long %long_0 +%diff = OpPtrDiff %long %conv %conv +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_UNIVERSAL_1_5); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_UNIVERSAL_1_5)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Cannot use a pointer in the PhysicalStorageBuffer storage class")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_misc_test.cpp b/third_party/spirv-tools/test/val/val_misc_test.cpp new file mode 100644 index 0000000..9395484 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_misc_test.cpp @@ -0,0 +1,231 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for misc instructions + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; + +using ValidateMisc = spvtest::ValidateBase; + +TEST_F(ValidateMisc, UndefRestrictedShort) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +%short = OpTypeInt 16 0 +%undef = OpUndef %short +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot create undefined values with 8- or 16-bit types")); +} + +TEST_F(ValidateMisc, UndefRestrictedChar) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer8BitAccess +OpExtension "SPV_KHR_8bit_storage" +OpMemoryModel Logical GLSL450 +%char = OpTypeInt 8 0 +%undef = OpUndef %char +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot create undefined values with 8- or 16-bit types")); +} + +TEST_F(ValidateMisc, UndefRestrictedHalf) { + const std::string spirv = R"( +OpCapability Shader +OpCapability Linkage +OpCapability StorageBuffer16BitAccess +OpExtension "SPV_KHR_16bit_storage" +OpMemoryModel Logical GLSL450 +%half = OpTypeFloat 16 +%undef = OpUndef %half +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Cannot create undefined values with 8- or 16-bit types")); +} + +const std::string ShaderClockSpriv = R"( +OpCapability Shader +OpCapability Int64 +OpCapability ShaderClockKHR +OpExtension "SPV_KHR_shader_clock" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpSourceExtension "GL_ARB_gpu_shader_int64" +OpSourceExtension "GL_ARB_shader_clock" +OpSourceExtension "GL_EXT_shader_realtime_clock" +OpName %main "main" +OpName %time1 "time1" +%void = OpTypeVoid +)"; + +TEST_F(ValidateMisc, ShaderClockInt64) { + const std::string spirv = ShaderClockSpriv + R"( +%3 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%_ptr_Function_uint = OpTypePointer Function %uint +%uint_3 = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%main = OpFunction %void None %3 +%5 = OpLabel +%time1 = OpVariable %_ptr_Function_uint Function +%11 = OpReadClockKHR %uint %uint_3 +OpStore %time1 %11 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("or 64bit unsigned integer")); +} + +TEST_F(ValidateMisc, ShaderClockVec2) { + const std::string spirv = ShaderClockSpriv + R"( +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong +%uint = OpTypeInt 32 0 +%uint_3 = OpConstant %uint 3 +%v2uint = OpTypeVector %ulong 2 +%_ptr_Function_v2uint = OpTypePointer Function %v2uint +%main = OpFunction %void None %3 +%5 = OpLabel +%time1 = OpVariable %_ptr_Function_v2uint Function +%15 = OpReadClockKHR %v2uint %uint_3 +OpStore %time1 %15 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("vector of two components")); +} + +TEST_F(ValidateMisc, ShaderClockInvalidScopeValue) { + const std::string spirv = ShaderClockSpriv + R"( +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%uint = OpTypeInt 32 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong +%uint_10 = OpConstant %uint 10 +%uint_1 = OpConstant %uint 1 +%main = OpFunction %void None %3 +%5 = OpLabel +%time1 = OpVariable %_ptr_Function_ulong Function +%11 = OpReadClockKHR %ulong %uint_10 +OpStore %time1 %11 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("Invalid scope value")); +} + +TEST_F(ValidateMisc, ShaderClockSubgroupScope) { + const std::string spirv = ShaderClockSpriv + R"( +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%uint = OpTypeInt 32 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong +%subgroup = OpConstant %uint 3 +%uint_1 = OpConstant %uint 1 +%main = OpFunction %void None %3 +%5 = OpLabel +%time1 = OpVariable %_ptr_Function_ulong Function +%11 = OpReadClockKHR %ulong %subgroup +OpStore %time1 %11 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMisc, ShaderClockDeviceScope) { + const std::string spirv = ShaderClockSpriv + R"( +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%uint = OpTypeInt 32 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong +%device = OpConstant %uint 1 +%uint_1 = OpConstant %uint 1 +%main = OpFunction %void None %3 +%5 = OpLabel +%time1 = OpVariable %_ptr_Function_ulong Function +%11 = OpReadClockKHR %ulong %device +OpStore %time1 %11 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMisc, ShaderClockWorkgroupScope) { + const std::string spirv = ShaderClockSpriv + R"( +%3 = OpTypeFunction %void +%ulong = OpTypeInt 64 0 +%uint = OpTypeInt 32 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong +%workgroup = OpConstant %uint 2 +%uint_1 = OpConstant %uint 1 +%main = OpFunction %void None %3 +%5 = OpLabel +%time1 = OpVariable %_ptr_Function_ulong Function +%11 = OpReadClockKHR %ulong %workgroup +OpStore %time1 %11 +OpReturn +OpFunctionEnd)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Scope must be Subgroup or Device")); +} +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_modes_test.cpp b/third_party/spirv-tools/test/val/val_modes_test.cpp new file mode 100644 index 0000000..0a1476e --- /dev/null +++ b/third_party/spirv-tools/test/val/val_modes_test.cpp @@ -0,0 +1,1183 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/spirv_target_env.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateMode = spvtest::ValidateBase; + +const std::string kVoidFunction = R"(%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + +TEST_F(ValidateMode, GLComputeNoMode) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMode, GLComputeNoModeVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the Vulkan environment, GLCompute execution model entry " + "points require either the LocalSize execution mode or an " + "object decorated with WorkgroupSize must be specified.")); +} + +TEST_F(ValidateMode, GLComputeNoModeVulkanWorkgroupSize) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpDecorate %int3_1 BuiltIn WorkgroupSize +%int = OpTypeInt 32 0 +%int3 = OpTypeVector %int 3 +%int_1 = OpConstant %int 1 +%int3_1 = OpConstantComposite %int3 %int_1 %int_1 %int_1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateMode, GLComputeVulkanLocalSize) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateMode, FragmentOriginLowerLeftVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginLowerLeft +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In the Vulkan environment, the OriginLowerLeft " + "execution mode must not be used.")); +} + +TEST_F(ValidateMode, FragmentPixelCenterIntegerVulkan) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main PixelCenterInteger +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_VULKAN_1_0; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In the Vulkan environment, the PixelCenterInteger " + "execution mode must not be used.")); +} + +TEST_F(ValidateMode, GeometryNoOutputMode) { + const std::string spirv = R"( +OpCapability Geometry +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" +OpExecutionMode %main InputPoints +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Geometry execution model entry points must specify " + "exactly one of OutputPoints, OutputLineStrip or " + "OutputTriangleStrip execution modes.")); +} + +TEST_F(ValidateMode, GeometryNoInputMode) { + const std::string spirv = R"( +OpCapability Geometry +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" +OpExecutionMode %main OutputPoints +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Geometry execution model entry points must specify exactly " + "one of InputPoints, InputLines, InputLinesAdjacency, " + "Triangles or InputTrianglesAdjacency execution modes.")); +} + +TEST_F(ValidateMode, FragmentNoOrigin) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Fragment execution model entry points require either an " + "OriginUpperLeft or OriginLowerLeft execution mode.")); +} + +TEST_F(ValidateMode, FragmentBothOrigins) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main OriginLowerLeft +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Fragment execution model entry points can only specify one of " + "OriginUpperLeft or OriginLowerLeft execution modes.")); +} + +TEST_F(ValidateMode, FragmentDepthGreaterAndLess) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main DepthGreater +OpExecutionMode %main DepthLess +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Fragment execution model entry points can specify at " + "most one of DepthGreater, DepthLess or DepthUnchanged " + "execution modes.")); +} + +TEST_F(ValidateMode, FragmentDepthGreaterAndUnchanged) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main DepthGreater +OpExecutionMode %main DepthUnchanged +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Fragment execution model entry points can specify at " + "most one of DepthGreater, DepthLess or DepthUnchanged " + "execution modes.")); +} + +TEST_F(ValidateMode, FragmentDepthLessAndUnchanged) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main DepthLess +OpExecutionMode %main DepthUnchanged +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Fragment execution model entry points can specify at " + "most one of DepthGreater, DepthLess or DepthUnchanged " + "execution modes.")); +} + +TEST_F(ValidateMode, FragmentAllDepths) { + const std::string spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main DepthGreater +OpExecutionMode %main DepthLess +OpExecutionMode %main DepthUnchanged +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Fragment execution model entry points can specify at " + "most one of DepthGreater, DepthLess or DepthUnchanged " + "execution modes.")); +} + +TEST_F(ValidateMode, TessellationControlSpacingEqualAndFractionalOdd) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %main "main" +OpExecutionMode %main SpacingEqual +OpExecutionMode %main SpacingFractionalOdd +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify " + "at most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes.")); +} + +TEST_F(ValidateMode, TessellationControlSpacingEqualAndSpacingFractionalEven) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %main "main" +OpExecutionMode %main SpacingEqual +OpExecutionMode %main SpacingFractionalEven +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify " + "at most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes.")); +} + +TEST_F(ValidateMode, + TessellationControlSpacingFractionalOddAndSpacingFractionalEven) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %main "main" +OpExecutionMode %main SpacingFractionalOdd +OpExecutionMode %main SpacingFractionalEven +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify " + "at most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes.")); +} + +TEST_F(ValidateMode, TessellationControlAllSpacing) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %main "main" +OpExecutionMode %main SpacingEqual +OpExecutionMode %main SpacingFractionalOdd +OpExecutionMode %main SpacingFractionalEven +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify " + "at most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes.")); +} + +TEST_F(ValidateMode, + TessellationEvaluationSpacingEqualAndSpacingFractionalOdd) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationEvaluation %main "main" +OpExecutionMode %main SpacingEqual +OpExecutionMode %main SpacingFractionalOdd +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify " + "at most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes.")); +} + +TEST_F(ValidateMode, + TessellationEvaluationSpacingEqualAndSpacingFractionalEven) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationEvaluation %main "main" +OpExecutionMode %main SpacingEqual +OpExecutionMode %main SpacingFractionalEven +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify " + "at most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes.")); +} + +TEST_F(ValidateMode, + TessellationEvaluationSpacingFractionalOddAndSpacingFractionalEven) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationEvaluation %main "main" +OpExecutionMode %main SpacingFractionalOdd +OpExecutionMode %main SpacingFractionalEven +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify " + "at most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes.")); +} + +TEST_F(ValidateMode, TessellationEvaluationAllSpacing) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationEvaluation %main "main" +OpExecutionMode %main SpacingEqual +OpExecutionMode %main SpacingFractionalOdd +OpExecutionMode %main SpacingFractionalEven +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify " + "at most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes.")); +} + +TEST_F(ValidateMode, TessellationControlBothVertex) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationControl %main "main" +OpExecutionMode %main VertexOrderCw +OpExecutionMode %main VertexOrderCcw +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify at most " + "one of VertexOrderCw or VertexOrderCcw execution modes.")); +} + +TEST_F(ValidateMode, TessellationEvaluationBothVertex) { + const std::string spirv = R"( +OpCapability Tessellation +OpMemoryModel Logical GLSL450 +OpEntryPoint TessellationEvaluation %main "main" +OpExecutionMode %main VertexOrderCw +OpExecutionMode %main VertexOrderCcw +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Tessellation execution model entry points can specify at most " + "one of VertexOrderCw or VertexOrderCcw execution modes.")); +} + +using ValidateModeGeometry = spvtest::ValidateBase, + std::tuple>>; + +TEST_P(ValidateModeGeometry, ExecutionMode) { + std::vector input_modes; + std::vector output_modes; + input_modes.push_back(std::get<0>(std::get<0>(GetParam()))); + input_modes.push_back(std::get<1>(std::get<0>(GetParam()))); + input_modes.push_back(std::get<2>(std::get<0>(GetParam()))); + input_modes.push_back(std::get<3>(std::get<0>(GetParam()))); + input_modes.push_back(std::get<4>(std::get<0>(GetParam()))); + output_modes.push_back(std::get<0>(std::get<1>(GetParam()))); + output_modes.push_back(std::get<1>(std::get<1>(GetParam()))); + output_modes.push_back(std::get<2>(std::get<1>(GetParam()))); + + std::ostringstream sstr; + sstr << "OpCapability Geometry\n"; + sstr << "OpMemoryModel Logical GLSL450\n"; + sstr << "OpEntryPoint Geometry %main \"main\"\n"; + size_t num_input_modes = 0; + for (auto input : input_modes) { + if (!input.empty()) { + num_input_modes++; + sstr << "OpExecutionMode %main " << input << "\n"; + } + } + size_t num_output_modes = 0; + for (auto output : output_modes) { + if (!output.empty()) { + num_output_modes++; + sstr << "OpExecutionMode %main " << output << "\n"; + } + } + sstr << "%void = OpTypeVoid\n"; + sstr << "%void_fn = OpTypeFunction %void\n"; + sstr << "%int = OpTypeInt 32 0\n"; + sstr << "%int1 = OpConstant %int 1\n"; + sstr << "%main = OpFunction %void None %void_fn\n"; + sstr << "%entry = OpLabel\n"; + sstr << "OpReturn\n"; + sstr << "OpFunctionEnd\n"; + + CompileSuccessfully(sstr.str()); + if (num_input_modes == 1 && num_output_modes == 1) { + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); + } else { + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + if (num_input_modes != 1) { + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Geometry execution model entry points must " + "specify exactly one of InputPoints, InputLines, " + "InputLinesAdjacency, Triangles or " + "InputTrianglesAdjacency execution modes.")); + } else { + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Geometry execution model entry points must specify " + "exactly one of OutputPoints, OutputLineStrip or " + "OutputTriangleStrip execution modes.")); + } + } +} + +INSTANTIATE_TEST_SUITE_P( + GeometryRequiredModes, ValidateModeGeometry, + Combine(Combine(Values("InputPoints", ""), Values("InputLines", ""), + Values("InputLinesAdjacency", ""), Values("Triangles", ""), + Values("InputTrianglesAdjacency", "")), + Combine(Values("OutputPoints", ""), Values("OutputLineStrip", ""), + Values("OutputTriangleStrip", "")))); + +using ValidateModeExecution = + spvtest::ValidateBase>; + +TEST_P(ValidateModeExecution, ExecutionMode) { + const spv_result_t expectation = std::get<0>(GetParam()); + const std::string error = std::get<1>(GetParam()); + const std::string model = std::get<2>(GetParam()); + const std::string mode = std::get<3>(GetParam()); + const spv_target_env env = std::get<4>(GetParam()); + + std::ostringstream sstr; + sstr << "OpCapability Shader\n"; + if (!spvIsWebGPUEnv(env)) { + sstr << "OpCapability Geometry\n"; + sstr << "OpCapability Tessellation\n"; + sstr << "OpCapability TransformFeedback\n"; + } + if (!spvIsVulkanOrWebGPUEnv(env)) { + sstr << "OpCapability Kernel\n"; + if (env == SPV_ENV_UNIVERSAL_1_3) { + sstr << "OpCapability SubgroupDispatch\n"; + } + } + if (spvIsWebGPUEnv(env)) { + sstr << "OpCapability VulkanMemoryModelKHR\n"; + sstr << "OpExtension \"SPV_KHR_vulkan_memory_model\"\n"; + sstr << "OpMemoryModel Logical VulkanKHR\n"; + } else { + sstr << "OpMemoryModel Logical GLSL450\n"; + } + sstr << "OpEntryPoint " << model << " %main \"main\"\n"; + if (mode.find("LocalSizeId") == 0 || mode.find("LocalSizeHintId") == 0 || + mode.find("SubgroupsPerWorkgroupId") == 0) { + sstr << "OpExecutionModeId %main " << mode << "\n"; + } else { + sstr << "OpExecutionMode %main " << mode << "\n"; + } + if (model == "Geometry") { + if (!(mode.find("InputPoints") == 0 || mode.find("InputLines") == 0 || + mode.find("InputLinesAdjacency") == 0 || + mode.find("Triangles") == 0 || + mode.find("InputTrianglesAdjacency") == 0)) { + // Exactly one of the above modes is required for Geometry shaders. + sstr << "OpExecutionMode %main InputPoints\n"; + } + if (!(mode.find("OutputPoints") == 0 || mode.find("OutputLineStrip") == 0 || + mode.find("OutputTriangleStrip") == 0)) { + // Exactly one of the above modes is required for Geometry shaders. + sstr << "OpExecutionMode %main OutputPoints\n"; + } + } else if (model == "Fragment") { + if (!(mode.find("OriginUpperLeft") == 0 || + mode.find("OriginLowerLeft") == 0)) { + // Exactly one of the above modes is required for Fragment shaders. + sstr << "OpExecutionMode %main OriginUpperLeft\n"; + } + } + sstr << "%void = OpTypeVoid\n"; + sstr << "%void_fn = OpTypeFunction %void\n"; + sstr << "%int = OpTypeInt 32 0\n"; + sstr << "%int1 = OpConstant %int 1\n"; + sstr << "%main = OpFunction %void None %void_fn\n"; + sstr << "%entry = OpLabel\n"; + sstr << "OpReturn\n"; + sstr << "OpFunctionEnd\n"; + + CompileSuccessfully(sstr.str(), env); + EXPECT_THAT(expectation, ValidateInstructions(env)); + if (expectation != SPV_SUCCESS) { + EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); + } +} + +INSTANTIATE_TEST_SUITE_P( + ValidateModeGeometryOnlyGoodSpv10, ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), Values("Geometry"), + Values("Invocations 3", "InputPoints", "InputLines", + "InputLinesAdjacency", "InputTrianglesAdjacency", + "OutputPoints", "OutputLineStrip", "OutputTriangleStrip"), + Values(SPV_ENV_UNIVERSAL_1_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeGeometryOnlyBadSpv10, ValidateModeExecution, + Combine(Values(SPV_ERROR_INVALID_DATA), + Values("Execution mode can only be used with the Geometry " + "execution model."), + Values("Fragment", "TessellationEvaluation", "TessellationControl", + "GLCompute", "Vertex", "Kernel"), + Values("Invocations 3", "InputPoints", "InputLines", + "InputLinesAdjacency", "InputTrianglesAdjacency", + "OutputPoints", "OutputLineStrip", "OutputTriangleStrip"), + Values(SPV_ENV_UNIVERSAL_1_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeTessellationOnlyGoodSpv10, ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), + Values("TessellationControl", "TessellationEvaluation"), + Values("SpacingEqual", "SpacingFractionalEven", + "SpacingFractionalOdd", "VertexOrderCw", "VertexOrderCcw", + "PointMode", "Quads", "Isolines"), + Values(SPV_ENV_UNIVERSAL_1_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeTessellationOnlyBadSpv10, ValidateModeExecution, + Combine(Values(SPV_ERROR_INVALID_DATA), + Values("Execution mode can only be used with a tessellation " + "execution model."), + Values("Fragment", "Geometry", "GLCompute", "Vertex", "Kernel"), + Values("SpacingEqual", "SpacingFractionalEven", + "SpacingFractionalOdd", "VertexOrderCw", "VertexOrderCcw", + "PointMode", "Quads", "Isolines"), + Values(SPV_ENV_UNIVERSAL_1_0))); + +INSTANTIATE_TEST_SUITE_P(ValidateModeGeometryAndTessellationGoodSpv10, + ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), + Values("TessellationControl", + "TessellationEvaluation", "Geometry"), + Values("Triangles", "OutputVertices 3"), + Values(SPV_ENV_UNIVERSAL_1_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeGeometryAndTessellationBadSpv10, ValidateModeExecution, + Combine(Values(SPV_ERROR_INVALID_DATA), + Values("Execution mode can only be used with a Geometry or " + "tessellation execution model."), + Values("Fragment", "GLCompute", "Vertex", "Kernel"), + Values("Triangles", "OutputVertices 3"), + Values(SPV_ENV_UNIVERSAL_1_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeFragmentOnlyGoodSpv10, ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"), + Values("PixelCenterInteger", "OriginUpperLeft", "OriginLowerLeft", + "EarlyFragmentTests", "DepthReplacing", "DepthLess", + "DepthUnchanged"), + Values(SPV_ENV_UNIVERSAL_1_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeFragmentOnlyBadSpv10, ValidateModeExecution, + Combine(Values(SPV_ERROR_INVALID_DATA), + Values("Execution mode can only be used with the Fragment " + "execution model."), + Values("Geometry", "TessellationControl", "TessellationEvaluation", + "GLCompute", "Vertex", "Kernel"), + Values("PixelCenterInteger", "OriginUpperLeft", "OriginLowerLeft", + "EarlyFragmentTests", "DepthReplacing", "DepthGreater", + "DepthLess", "DepthUnchanged"), + Values(SPV_ENV_UNIVERSAL_1_0))); + +INSTANTIATE_TEST_SUITE_P(ValidateModeKernelOnlyGoodSpv13, ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), + Values("Kernel"), + Values("LocalSizeHint 1 1 1", "VecTypeHint 4", + "ContractionOff", + "LocalSizeHintId %int1"), + Values(SPV_ENV_UNIVERSAL_1_3))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeKernelOnlyBadSpv13, ValidateModeExecution, + Combine( + Values(SPV_ERROR_INVALID_DATA), + Values( + "Execution mode can only be used with the Kernel execution model."), + Values("Geometry", "TessellationControl", "TessellationEvaluation", + "GLCompute", "Vertex", "Fragment"), + Values("LocalSizeHint 1 1 1", "VecTypeHint 4", "ContractionOff", + "LocalSizeHintId %int1"), + Values(SPV_ENV_UNIVERSAL_1_3))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeGLComputeAndKernelGoodSpv13, ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), Values("Kernel", "GLCompute"), + Values("LocalSize 1 1 1", "LocalSizeId %int1 %int1 %int1"), + Values(SPV_ENV_UNIVERSAL_1_3))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeGLComputeAndKernelBadSpv13, ValidateModeExecution, + Combine(Values(SPV_ERROR_INVALID_DATA), + Values("Execution mode can only be used with a Kernel or GLCompute " + "execution model."), + Values("Geometry", "TessellationControl", "TessellationEvaluation", + "Fragment", "Vertex"), + Values("LocalSize 1 1 1", "LocalSizeId %int1 %int1 %int1"), + Values(SPV_ENV_UNIVERSAL_1_3))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeAllGoodSpv13, ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), + Values("Kernel", "GLCompute", "Geometry", "TessellationControl", + "TessellationEvaluation", "Fragment", "Vertex"), + Values("Xfb", "Initializer", "Finalizer", "SubgroupSize 1", + "SubgroupsPerWorkgroup 1", "SubgroupsPerWorkgroupId %int1"), + Values(SPV_ENV_UNIVERSAL_1_3))); + +INSTANTIATE_TEST_SUITE_P(ValidateModeGLComputeWebGPUAllowListGood, + ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), + Values("GLCompute"), Values("LocalSize 1 1 1"), + Values(SPV_ENV_WEBGPU_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeGLComputeWebGPUAllowListBad, ValidateModeExecution, + Combine(Values(SPV_ERROR_INVALID_DATA), + Values("Execution mode must be one of OriginUpperLeft, " + "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, " + "LocalSize, or LocalSizeHint for WebGPU environment"), + Values("GLCompute"), Values("LocalSizeId %int1 %int1 %int1"), + Values(SPV_ENV_WEBGPU_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeFragmentWebGPUAllowListGood, ValidateModeExecution, + Combine(Values(SPV_SUCCESS), Values(""), Values("Fragment"), + Values("OriginUpperLeft", "DepthReplacing", "DepthGreater", + "DepthLess", "DepthUnchanged"), + Values(SPV_ENV_WEBGPU_0))); + +INSTANTIATE_TEST_SUITE_P( + ValidateModeFragmentWebGPUAllowListBad, ValidateModeExecution, + Combine(Values(SPV_ERROR_INVALID_DATA), + Values("Execution mode must be one of OriginUpperLeft, " + "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, " + "LocalSize, or LocalSizeHint for WebGPU environment"), + Values("Fragment"), + Values("PixelCenterInteger", "OriginLowerLeft", + "EarlyFragmentTests"), + Values(SPV_ENV_WEBGPU_0))); + +TEST_F(ValidateModeExecution, MeshNVLocalSize) { + const std::string spirv = R"( +OpCapability Shader +OpCapability MeshShadingNV +OpExtension "SPV_NV_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshNV %main "main" +OpExecutionMode %main LocalSize 1 1 1 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateModeExecution, TaskNVLocalSize) { + const std::string spirv = R"( +OpCapability Shader +OpCapability MeshShadingNV +OpExtension "SPV_NV_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint TaskNV %main "main" +OpExecutionMode %main LocalSize 1 1 1 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateModeExecution, MeshNVOutputPoints) { + const std::string spirv = R"( +OpCapability Shader +OpCapability MeshShadingNV +OpExtension "SPV_NV_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshNV %main "main" +OpExecutionMode %main OutputPoints +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateModeExecution, MeshNVOutputVertices) { + const std::string spirv = R"( +OpCapability Shader +OpCapability MeshShadingNV +OpExtension "SPV_NV_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshNV %main "main" +OpExecutionMode %main OutputVertices 42 +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateModeExecution, MeshNVLocalSizeId) { + const std::string spirv = R"( +OpCapability Shader +OpCapability MeshShadingNV +OpExtension "SPV_NV_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint MeshNV %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateModeExecution, TaskNVLocalSizeId) { + const std::string spirv = R"( +OpCapability Shader +OpCapability MeshShadingNV +OpExtension "SPV_NV_mesh_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint TaskNV %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateModeExecution, ExecModeSubgroupsPerWorkgroupIdBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability SubgroupDispatch +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpExecutionMode %main SubgroupsPerWorkgroupId %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand " + "is an execution mode that takes no Extra Operands")); +} + +TEST_F(ValidateModeExecution, ExecModeIdSubgroupsPerWorkgroupIdGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability SubgroupDispatch +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpExecutionModeId %main SubgroupsPerWorkgroupId %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateModeExecution, ExecModeIdSubgroupsPerWorkgroupIdNonConstantBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability SubgroupDispatch +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpExecutionModeId %main SubgroupsPerWorkgroupId %int_1 +%int = OpTypeInt 32 0 +%int_ptr = OpTypePointer Private %int +%int_1 = OpVariable %int_ptr Private +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For OpExecutionModeId all Extra Operand ids must be " + "constant instructions.")); +} + +TEST_F(ValidateModeExecution, ExecModeLocalSizeHintIdBad) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Kernel %main "main" +OpExecutionMode %main LocalSizeHintId %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand " + "is an execution mode that takes no Extra Operands")); +} + +TEST_F(ValidateModeExecution, ExecModeIdLocalSizeHintIdGood) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Kernel %main "main" +OpExecutionModeId %main LocalSizeHintId %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateModeExecution, ExecModeIdLocalSizeHintIdNonConstantBad) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpExecutionModeId %main LocalSizeHintId %int_1 +%int = OpTypeInt 32 0 +%int_ptr = OpTypePointer Private %int +%int_1 = OpVariable %int_ptr Private +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For OpExecutionModeId all Extra Operand ids must be " + "constant instructions.")); +} + +TEST_F(ValidateModeExecution, ExecModeLocalSizeIdBad) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Kernel %main "main" +OpExecutionMode %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpExecutionMode is only valid when the Mode operand " + "is an execution mode that takes no Extra Operands")); +} + +TEST_F(ValidateModeExecution, ExecModeIdLocalSizeIdGood) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Kernel %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_1 = OpConstant %int 1 +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions(env)); +} + +TEST_F(ValidateModeExecution, ExecModeIdLocalSizeIdNonConstantBad) { + const std::string spirv = R"( +OpCapability Kernel +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpExecutionModeId %main LocalSizeId %int_1 %int_1 %int_1 +%int = OpTypeInt 32 0 +%int_ptr = OpTypePointer Private %int +%int_1 = OpVariable %int_ptr Private +)" + kVoidFunction; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + CompileSuccessfully(spirv, env); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(env)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For OpExecutionModeId all Extra Operand ids must be " + "constant instructions.")); +} + +TEST_F(ValidateMode, FragmentShaderInterlockVertexBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FragmentShaderPixelInterlockEXT +OpExtension "SPV_EXT_fragment_shader_interlock" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpExecutionMode %main PixelInterlockOrderedEXT +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Execution mode can only be used with the Fragment execution model")); +} + +TEST_F(ValidateMode, FragmentShaderInterlockTooManyModesBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FragmentShaderPixelInterlockEXT +OpCapability FragmentShaderSampleInterlockEXT +OpExtension "SPV_EXT_fragment_shader_interlock" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main PixelInterlockOrderedEXT +OpExecutionMode %main SampleInterlockOrderedEXT +)" + kVoidFunction; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Fragment execution model entry points can specify at most " + "one fragment shader interlock execution mode")); +} + +TEST_F(ValidateMode, FragmentShaderInterlockNoModeBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FragmentShaderPixelInterlockEXT +OpExtension "SPV_EXT_fragment_shader_interlock" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entryf = OpLabel +OpBeginInvocationInterlockEXT +OpEndInvocationInterlockEXT +OpReturn +OpFunctionEnd +%main = OpFunction %void None %void_fn +%entry = OpLabel +%1 = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT require a " + "fragment shader interlock execution mode")); +} + +TEST_F(ValidateMode, FragmentShaderInterlockGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability FragmentShaderPixelInterlockEXT +OpExtension "SPV_EXT_fragment_shader_interlock" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpExecutionMode %main PixelInterlockOrderedEXT +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%func = OpFunction %void None %void_fn +%entryf = OpLabel +OpBeginInvocationInterlockEXT +OpEndInvocationInterlockEXT +OpReturn +OpFunctionEnd +%main = OpFunction %void None %void_fn +%entry = OpLabel +%1 = OpFunctionCall %void %func +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMode, FragmentShaderDemoteVertexBad) { + const std::string spirv = R"( +OpCapability Shader +OpCapability DemoteToHelperInvocationEXT +OpExtension "SPV_EXT_demote_to_helper_invocation" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +%bool = OpTypeBool +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpDemoteToHelperInvocationEXT +%1 = OpIsHelperInvocationEXT %bool +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "OpDemoteToHelperInvocationEXT requires Fragment execution model")); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("OpIsHelperInvocationEXT requires Fragment execution model")); +} + +TEST_F(ValidateMode, FragmentShaderDemoteGood) { + const std::string spirv = R"( +OpCapability Shader +OpCapability DemoteToHelperInvocationEXT +OpExtension "SPV_EXT_demote_to_helper_invocation" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%bool = OpTypeBool +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpDemoteToHelperInvocationEXT +%1 = OpIsHelperInvocationEXT %bool +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateMode, FragmentShaderDemoteBadType) { + const std::string spirv = R"( +OpCapability Shader +OpCapability DemoteToHelperInvocationEXT +OpExtension "SPV_EXT_demote_to_helper_invocation" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +%u32 = OpTypeInt 32 0 +%void = OpTypeVoid +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpDemoteToHelperInvocationEXT +%1 = OpIsHelperInvocationEXT %u32 +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Expected bool scalar type as Result Type")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_non_semantic_test.cpp b/third_party/spirv-tools/test/val/val_non_semantic_test.cpp new file mode 100644 index 0000000..b80bb1a --- /dev/null +++ b/third_party/spirv-tools/test/val/val_non_semantic_test.cpp @@ -0,0 +1,195 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for non-semantic instructions + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +struct TestResult { + TestResult(spv_result_t in_validation_result = SPV_SUCCESS, + const char* in_error_str = nullptr, + const char* in_error_str2 = nullptr) + : validation_result(in_validation_result), + error_str(in_error_str), + error_str2(in_error_str2) {} + spv_result_t validation_result; + const char* error_str; + const char* error_str2; +}; + +using ::testing::Combine; +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +using ValidateNonSemanticGenerated = spvtest::ValidateBase< + std::tuple>; +using ValidateNonSemanticString = spvtest::ValidateBase; + +CodeGenerator GetNonSemanticCodeGenerator(const bool declare_ext, + const bool declare_extinst, + const char* const global_extinsts, + const char* const function_extinsts) { + CodeGenerator generator = CodeGenerator::GetDefaultShaderCodeGenerator(); + + if (declare_ext) { + generator.extensions_ += "OpExtension \"SPV_KHR_non_semantic_info\"\n"; + } + if (declare_extinst) { + generator.extensions_ += + "%extinst = OpExtInstImport \"NonSemantic.Testing.Set\"\n"; + } + + generator.after_types_ = global_extinsts; + + generator.before_types_ = "%decorate_group = OpDecorationGroup"; + + EntryPoint entry_point; + entry_point.name = "main"; + entry_point.execution_model = "Vertex"; + + entry_point.body = R"( +)"; + entry_point.body += function_extinsts; + generator.entry_points_.push_back(std::move(entry_point)); + + return generator; +} + +TEST_P(ValidateNonSemanticGenerated, InTest) { + const bool declare_ext = std::get<0>(GetParam()); + const bool declare_extinst = std::get<1>(GetParam()); + const char* const global_extinsts = std::get<2>(GetParam()); + const char* const function_extinsts = std::get<3>(GetParam()); + const TestResult& test_result = std::get<4>(GetParam()); + + CodeGenerator generator = GetNonSemanticCodeGenerator( + declare_ext, declare_extinst, global_extinsts, function_extinsts); + + CompileSuccessfully(generator.Build(), SPV_ENV_VULKAN_1_0); + ASSERT_EQ(test_result.validation_result, + ValidateInstructions(SPV_ENV_VULKAN_1_0)); + if (test_result.error_str) { + EXPECT_THAT(getDiagnosticString(), + testing::ContainsRegex(test_result.error_str)); + } + if (test_result.error_str2) { + EXPECT_THAT(getDiagnosticString(), + testing::ContainsRegex(test_result.error_str2)); + } +} + +INSTANTIATE_TEST_SUITE_P(OnlyOpExtension, ValidateNonSemanticGenerated, + Combine(Values(true), Values(false), Values(""), + Values(""), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + MissingOpExtension, ValidateNonSemanticGenerated, + Combine(Values(false), Values(true), Values(""), Values(""), + Values(TestResult( + SPV_ERROR_INVALID_DATA, + "NonSemantic extended instruction sets cannot be declared " + "without SPV_KHR_non_semantic_info.")))); + +INSTANTIATE_TEST_SUITE_P(NoExtInst, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), Values(""), + Values(""), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SimpleGlobalExtInst, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %void %extinst 123 %i32"), Values(""), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + ComplexGlobalExtInst, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %void %extinst 123 %i32 %u32_2 " + "%f32vec4_1234 %u32_0"), + Values(""), Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + SimpleFunctionLevelExtInst, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), Values(""), + Values("%result = OpExtInst %void %extinst 123 %i32"), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + FunctionTypeReference, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %void %extinst 123 %func"), Values(""), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + EntryPointReference, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), Values(""), + Values("%result = OpExtInst %void %extinst 123 %main"), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + DecorationGroupReference, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), Values(""), + Values("%result = OpExtInst %void %extinst 123 %decorate_group"), + Values(TestResult()))); + +INSTANTIATE_TEST_SUITE_P( + UnknownIDReference, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %void %extinst 123 %undefined_id"), + Values(""), + Values(TestResult(SPV_ERROR_INVALID_ID, + "ID .* has not been defined")))); + +INSTANTIATE_TEST_SUITE_P( + NonSemanticUseInSemantic, ValidateNonSemanticGenerated, + Combine(Values(true), Values(true), + Values("%result = OpExtInst %f32 %extinst 123 %i32\n" + "%invalid = OpConstantComposite %f32vec2 %f32_0 %result"), + Values(""), + Values(TestResult(SPV_ERROR_INVALID_ID, + "in semantic instruction cannot be a " + "non-semantic instruction")))); + +TEST_F(ValidateNonSemanticString, InvalidSectionOpExtInst) { + const std::string spirv = R"( +OpCapability Shader +OpExtension "SPV_KHR_non_semantic_info" +%extinst = OpExtInstImport "NonSemantic.Testing.Set" +%test = OpExtInst %void %extinst 4 %void +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +)"; + + CompileSuccessfully(spirv); + EXPECT_THAT(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_VULKAN_1_0)); + + // there's no specific error for using an OpExtInst too early, it requires a + // type so by definition any use of a type in it will be an undefined ID + EXPECT_THAT(getDiagnosticString(), + HasSubstr("ID 2[%2] has not been defined")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_non_uniform_test.cpp b/third_party/spirv-tools/test/val/val_non_uniform_test.cpp new file mode 100644 index 0000000..fbd11a9 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_non_uniform_test.cpp @@ -0,0 +1,293 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Combine; +using ::testing::HasSubstr; +using ::testing::Values; +using ::testing::ValuesIn; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = "", + const std::string& execution_model = "GLCompute") { + std::ostringstream ss; + ss << R"( +OpCapability Shader +OpCapability GroupNonUniform +OpCapability GroupNonUniformVote +OpCapability GroupNonUniformBallot +OpCapability GroupNonUniformShuffle +OpCapability GroupNonUniformShuffleRelative +OpCapability GroupNonUniformArithmetic +OpCapability GroupNonUniformClustered +OpCapability GroupNonUniformQuad +)"; + + ss << capabilities_and_extensions; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"\n"; + if (execution_model == "GLCompute") { + ss << "OpExecutionMode %main LocalSize 1 1 1\n"; + } + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%bool = OpTypeBool +%u32 = OpTypeInt 32 0 +%int = OpTypeInt 32 1 +%float = OpTypeFloat 32 +%u32vec4 = OpTypeVector %u32 4 +%u32vec3 = OpTypeVector %u32 3 + +%true = OpConstantTrue %bool +%false = OpConstantFalse %bool + +%u32_0 = OpConstant %u32 0 + +%float_0 = OpConstant %float 0 + +%u32vec4_null = OpConstantComposite %u32vec4 %u32_0 %u32_0 %u32_0 %u32_0 +%u32vec3_null = OpConstantComposite %u32vec3 %u32_0 %u32_0 %u32_0 + +%cross_device = OpConstant %u32 0 +%device = OpConstant %u32 1 +%workgroup = OpConstant %u32 2 +%subgroup = OpConstant %u32 3 +%invocation = OpConstant %u32 4 + +%reduce = OpConstant %u32 0 +%inclusive_scan = OpConstant %u32 1 +%exclusive_scan = OpConstant %u32 2 +%clustered_reduce = OpConstant %u32 3 + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +SpvScope scopes[] = {SpvScopeCrossDevice, SpvScopeDevice, SpvScopeWorkgroup, + SpvScopeSubgroup, SpvScopeInvocation}; + +using GroupNonUniform = spvtest::ValidateBase< + std::tuple>; + +std::string ConvertScope(SpvScope scope) { + switch (scope) { + case SpvScopeCrossDevice: + return "%cross_device"; + case SpvScopeDevice: + return "%device"; + case SpvScopeWorkgroup: + return "%workgroup"; + case SpvScopeSubgroup: + return "%subgroup"; + case SpvScopeInvocation: + return "%invocation"; + default: + return ""; + } +} + +TEST_P(GroupNonUniform, Vulkan1p1) { + std::string opcode = std::get<0>(GetParam()); + std::string type = std::get<1>(GetParam()); + SpvScope execution_scope = std::get<2>(GetParam()); + std::string args = std::get<3>(GetParam()); + std::string error = std::get<4>(GetParam()); + + std::ostringstream sstr; + sstr << "%result = " << opcode << " "; + sstr << type << " "; + sstr << ConvertScope(execution_scope) << " "; + sstr << args << "\n"; + + CompileSuccessfully(GenerateShaderCode(sstr.str()), SPV_ENV_VULKAN_1_1); + spv_result_t result = ValidateInstructions(SPV_ENV_VULKAN_1_1); + if (error == "") { + if (execution_scope == SpvScopeSubgroup) { + EXPECT_EQ(SPV_SUCCESS, result); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "in Vulkan environment Execution scope is limited to Subgroup")); + } + } else { + EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); + EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); + } +} + +TEST_P(GroupNonUniform, Spirv1p3) { + std::string opcode = std::get<0>(GetParam()); + std::string type = std::get<1>(GetParam()); + SpvScope execution_scope = std::get<2>(GetParam()); + std::string args = std::get<3>(GetParam()); + std::string error = std::get<4>(GetParam()); + + std::ostringstream sstr; + sstr << "%result = " << opcode << " "; + sstr << type << " "; + sstr << ConvertScope(execution_scope) << " "; + sstr << args << "\n"; + + CompileSuccessfully(GenerateShaderCode(sstr.str()), SPV_ENV_UNIVERSAL_1_3); + spv_result_t result = ValidateInstructions(SPV_ENV_UNIVERSAL_1_3); + if (error == "") { + if (execution_scope == SpvScopeSubgroup || + execution_scope == SpvScopeWorkgroup) { + EXPECT_EQ(SPV_SUCCESS, result); + } else { + EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Execution scope is limited to Subgroup or Workgroup")); + } + } else { + EXPECT_EQ(SPV_ERROR_INVALID_DATA, result); + EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); + } +} + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformElect, GroupNonUniform, + Combine(Values("OpGroupNonUniformElect"), + Values("%bool"), ValuesIn(scopes), Values(""), + Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformVote, GroupNonUniform, + Combine(Values("OpGroupNonUniformAll", + "OpGroupNonUniformAny", + "OpGroupNonUniformAllEqual"), + Values("%bool"), ValuesIn(scopes), + Values("%true"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcast, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcast"), + Values("%bool"), ValuesIn(scopes), + Values("%true %u32_0"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBroadcastFirst, GroupNonUniform, + Combine(Values("OpGroupNonUniformBroadcastFirst"), + Values("%bool"), ValuesIn(scopes), + Values("%true"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallot, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallot"), + Values("%u32vec4"), ValuesIn(scopes), + Values("%true"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformInverseBallot, GroupNonUniform, + Combine(Values("OpGroupNonUniformInverseBallot"), + Values("%bool"), ValuesIn(scopes), + Values("%u32vec4_null"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitExtract, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotBitExtract"), + Values("%bool"), ValuesIn(scopes), + Values("%u32vec4_null %u32_0"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitCount, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotBitCount"), + Values("%u32"), ValuesIn(scopes), + Values("Reduce %u32vec4_null"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotFind, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotFindLSB", + "OpGroupNonUniformBallotFindMSB"), + Values("%u32"), ValuesIn(scopes), + Values("%u32vec4_null"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformShuffle, GroupNonUniform, + Combine(Values("OpGroupNonUniformShuffle", + "OpGroupNonUniformShuffleXor", + "OpGroupNonUniformShuffleUp", + "OpGroupNonUniformShuffleDown"), + Values("%u32"), ValuesIn(scopes), + Values("%u32_0 %u32_0"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformIntegerArithmetic, GroupNonUniform, + Combine(Values("OpGroupNonUniformIAdd", "OpGroupNonUniformIMul", + "OpGroupNonUniformSMin", "OpGroupNonUniformUMin", + "OpGroupNonUniformSMax", "OpGroupNonUniformUMax", + "OpGroupNonUniformBitwiseAnd", "OpGroupNonUniformBitwiseOr", + "OpGroupNonUniformBitwiseXor"), + Values("%u32"), ValuesIn(scopes), Values("Reduce %u32_0"), + Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformFloatArithmetic, GroupNonUniform, + Combine(Values("OpGroupNonUniformFAdd", "OpGroupNonUniformFMul", + "OpGroupNonUniformFMin", "OpGroupNonUniformFMax"), + Values("%float"), ValuesIn(scopes), Values("Reduce %float_0"), + Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformLogicalArithmetic, GroupNonUniform, + Combine(Values("OpGroupNonUniformLogicalAnd", + "OpGroupNonUniformLogicalOr", + "OpGroupNonUniformLogicalXor"), + Values("%bool"), ValuesIn(scopes), + Values("Reduce %true"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformQuad, GroupNonUniform, + Combine(Values("OpGroupNonUniformQuadBroadcast", + "OpGroupNonUniformQuadSwap"), + Values("%u32"), ValuesIn(scopes), + Values("%u32_0 %u32_0"), Values(""))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitCountScope, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotBitCount"), + Values("%u32"), ValuesIn(scopes), + Values("Reduce %u32vec4_null"), Values(""))); + +INSTANTIATE_TEST_SUITE_P( + GroupNonUniformBallotBitCountBadResultType, GroupNonUniform, + Combine( + Values("OpGroupNonUniformBallotBitCount"), Values("%float", "%int"), + Values(SpvScopeSubgroup), Values("Reduce %u32vec4_null"), + Values("Expected Result Type to be an unsigned integer type scalar."))); + +INSTANTIATE_TEST_SUITE_P(GroupNonUniformBallotBitCountBadValue, GroupNonUniform, + Combine(Values("OpGroupNonUniformBallotBitCount"), + Values("%u32"), Values(SpvScopeSubgroup), + Values("Reduce %u32vec3_null", "Reduce %u32_0", + "Reduce %float_0"), + Values("Expected Value to be a vector of four " + "components of integer type scalar"))); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_opencl_test.cpp b/third_party/spirv-tools/test/val/val_opencl_test.cpp new file mode 100644 index 0000000..1064158 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_opencl_test.cpp @@ -0,0 +1,275 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for OpenCL env specific checks + +#include + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using testing::HasSubstr; + +using ValidateOpenCL = spvtest::ValidateBase; + +TEST_F(ValidateOpenCL, NonPhysicalAddressingModelBad) { + std::string spirv = R"( + OpCapability Kernel + OpMemoryModel Logical OpenCL +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Addressing model must be Physical32 or Physical64 " + "in the OpenCL environment.\n OpMemoryModel Logical " + "OpenCL\n")); +} + +TEST_F(ValidateOpenCL, NonOpenCLMemoryModelBad) { + std::string spirv = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Physical32 VulkanKHR +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Memory model must be OpenCL in the OpenCL environment.")); +} + +TEST_F(ValidateOpenCL, NonVoidSampledTypeImageBad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical32 OpenCL + %1 = OpTypeInt 32 0 + %2 = OpTypeImage %1 2D 0 0 0 0 Unknown ReadOnly +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Sampled Type must be OpTypeVoid in the OpenCL environment." + "\n %2 = OpTypeImage %uint 2D 0 0 0 0 Unknown ReadOnly\n")); +} + +TEST_F(ValidateOpenCL, NonZeroMSImageBad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical32 OpenCL + %1 = OpTypeVoid + %2 = OpTypeImage %1 2D 0 0 1 0 Unknown ReadOnly +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("MS must be 0 in the OpenCL environement." + "\n %2 = OpTypeImage %void 2D 0 0 1 0 Unknown ReadOnly\n")); +} + +TEST_F(ValidateOpenCL, Non1D2DArrayedImageBad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical32 OpenCL + %1 = OpTypeVoid + %2 = OpTypeImage %1 3D 0 1 0 0 Unknown ReadOnly +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("In the OpenCL environment, Arrayed may only be set to 1 " + "when Dim is either 1D or 2D." + "\n %2 = OpTypeImage %void 3D 0 1 0 0 Unknown ReadOnly\n")); +} + +TEST_F(ValidateOpenCL, NonZeroSampledImageBad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical32 OpenCL + %1 = OpTypeVoid + %2 = OpTypeImage %1 3D 0 0 0 1 Unknown ReadOnly +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Sampled must be 0 in the OpenCL environment." + "\n %2 = OpTypeImage %void 3D 0 0 0 1 Unknown ReadOnly\n")); +} + +TEST_F(ValidateOpenCL, NoAccessQualifierImageBad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpMemoryModel Physical32 OpenCL + %1 = OpTypeVoid + %2 = OpTypeImage %1 3D 0 0 0 0 Unknown +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("In the OpenCL environment, the optional " + "Access Qualifier must be present." + "\n %2 = OpTypeImage %void 3D 0 0 0 0 Unknown\n")); +} + +TEST_F(ValidateOpenCL, ImageWriteWithOptionalImageOperandsBad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "test" + %uint = OpTypeInt 32 0 + %uint_7 = OpConstant %uint 7 + %uint_3 = OpConstant %uint 3 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_4 = OpConstant %uint 4 + %void = OpTypeVoid + %3 = OpTypeImage %void 2D 0 0 0 0 Unknown WriteOnly + %4 = OpTypeFunction %void %3 + %v2uint = OpTypeVector %uint 2 + %v4uint = OpTypeVector %uint 4 + %12 = OpConstantComposite %v2uint %uint_7 %uint_3 + %17 = OpConstantComposite %v4uint %uint_1 %uint_2 %uint_3 %uint_4 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + OpImageWrite %img %12 %17 ConstOffset %12 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Optional Image Operands are not allowed in the " + "OpenCL environment." + "\n OpImageWrite %15 %13 %14 ConstOffset %13\n")); +} + +TEST_F(ValidateOpenCL, ImageReadWithConstOffsetBad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %uint_7 = OpConstant %uint 7 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %3 = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %v4uint = OpTypeVector %uint 4 + %v2uint = OpTypeVector %uint 2 + %coord = OpConstantComposite %v2uint %uint_7 %uint_3 + %5 = OpFunction %void None %4 + %img = OpFunctionParameter %3 + %entry = OpLabel + %call = OpImageRead %v4uint %img %coord ConstOffset %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ConstOffset image operand not allowed in the OpenCL environment." + "\n %call = OpImageRead %v4uint %img %coord ConstOffset %coord\n")); +} + +TEST_F(ValidateOpenCL, ImageSampleExplicitLodWithConstOffsetBad) { + std::string spirv = R"( + OpCapability Addresses + OpCapability Kernel + OpCapability ImageBasic + OpCapability LiteralSampler + OpMemoryModel Physical64 OpenCL + OpEntryPoint Kernel %5 "image_kernel" + OpName %img "img" + OpName %coord "coord" + OpName %call "call" + %uint = OpTypeInt 32 0 + %uint_7 = OpConstant %uint 7 + %uint_3 = OpConstant %uint 3 + %void = OpTypeVoid + %3 = OpTypeImage %void 2D 0 0 0 0 Unknown ReadOnly + %4 = OpTypeFunction %void %3 + %8 = OpTypeSampler + %10 = OpTypeSampledImage %3 + %v4uint = OpTypeVector %uint 4 + %v2uint = OpTypeVector %uint 2 + %float = OpTypeFloat 32 + %9 = OpConstantSampler %8 None 0 Nearest + %coord = OpConstantComposite %v2uint %uint_7 %uint_3 + %float_0 = OpConstant %float 0 + %5 = OpFunction %void None %4 + %6 = OpFunctionParameter %3 + %entry = OpLabel + %img = OpSampledImage %10 %6 %9 + %call = OpImageSampleExplicitLod %v4uint %img %coord + Lod|ConstOffset %float_0 %coord + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_OPENCL_1_2)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "ConstOffset image operand not allowed in the OpenCL environment." + "\n %call = OpImageSampleExplicitLod %v4uint %img " + "%coord Lod|ConstOffset %float_0 %coord\n")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_primitives_test.cpp b/third_party/spirv-tools/test/val/val_primitives_test.cpp new file mode 100644 index 0000000..8a61723 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_primitives_test.cpp @@ -0,0 +1,321 @@ +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidatePrimitives = spvtest::ValidateBase; + +std::string GenerateShaderCode( + const std::string& body, + const std::string& capabilities_and_extensions = + "OpCapability GeometryStreams", + const std::string& execution_model = "Geometry") { + std::ostringstream ss; + ss << capabilities_and_extensions << "\n"; + ss << "OpMemoryModel Logical GLSL450\n"; + ss << "OpEntryPoint " << execution_model << " %main \"main\"\n"; + if (execution_model == "Geometry") { + ss << "OpExecutionMode %main InputPoints\n"; + ss << "OpExecutionMode %main OutputPoints\n"; + } + + ss << R"( +%void = OpTypeVoid +%func = OpTypeFunction %void +%f32 = OpTypeFloat 32 +%u32 = OpTypeInt 32 0 +%u32vec4 = OpTypeVector %u32 4 + +%f32_0 = OpConstant %f32 0 +%u32_0 = OpConstant %u32 0 +%u32_1 = OpConstant %u32 1 +%u32_2 = OpConstant %u32 2 +%u32_3 = OpConstant %u32 3 +%u32vec4_0123 = OpConstantComposite %u32vec4 %u32_0 %u32_1 %u32_2 %u32_3 + +%main = OpFunction %void None %func +%main_entry = OpLabel +)"; + + ss << body; + + ss << R"( +OpReturn +OpFunctionEnd)"; + + return ss.str(); +} + +// Returns SPIR-V assembly fragment representing a function call, +// the end of the callee body, and the preamble and body of the called +// function with the given body, but missing the final return and +// function-end. The result is of the form where it can be used in the +// |body| argument to GenerateShaderCode. +std::string CallAndCallee(const std::string& body) { + std::ostringstream ss; + ss << R"( +%placeholder = OpFunctionCall %void %foo +OpReturn +OpFunctionEnd + +%foo = OpFunction %void None %func +%foo_entry = OpLabel +)"; + + ss << body; + + return ss.str(); +} + +// OpEmitVertex doesn't have any parameters, so other validation +// is handled by the binary parser, and generic dominance checks. +TEST_F(ValidatePrimitives, EmitVertexSuccess) { + CompileSuccessfully( + GenerateShaderCode("OpEmitVertex", "OpCapability Geometry")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidatePrimitives, EmitVertexFailMissingCapability) { + CompileSuccessfully( + GenerateShaderCode("OpEmitVertex", "OpCapability Shader", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Opcode EmitVertex requires one of these capabilities: Geometry")); +} + +TEST_F(ValidatePrimitives, EmitVertexFailWrongExecutionMode) { + CompileSuccessfully( + GenerateShaderCode("OpEmitVertex", "OpCapability Geometry", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("EmitVertex instructions require Geometry execution model")); +} + +TEST_F(ValidatePrimitives, EmitVertexFailWrongExecutionModeNestedFunction) { + CompileSuccessfully(GenerateShaderCode(CallAndCallee("OpEmitVertex"), + "OpCapability Geometry", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("EmitVertex instructions require Geometry execution model")); +} + +// OpEndPrimitive doesn't have any parameters, so other validation +// is handled by the binary parser, and generic dominance checks. +TEST_F(ValidatePrimitives, EndPrimitiveSuccess) { + CompileSuccessfully( + GenerateShaderCode("OpEndPrimitive", "OpCapability Geometry")); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidatePrimitives, EndPrimitiveFailMissingCapability) { + CompileSuccessfully( + GenerateShaderCode("OpEndPrimitive", "OpCapability Shader", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "Opcode EndPrimitive requires one of these capabilities: Geometry")); +} + +TEST_F(ValidatePrimitives, EndPrimitiveFailWrongExecutionMode) { + CompileSuccessfully( + GenerateShaderCode("OpEndPrimitive", "OpCapability Geometry", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("EndPrimitive instructions require Geometry execution model")); +} + +TEST_F(ValidatePrimitives, EndPrimitiveFailWrongExecutionModeNestedFunction) { + CompileSuccessfully(GenerateShaderCode(CallAndCallee("OpEndPrimitive"), + "OpCapability Geometry", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("EndPrimitive instructions require Geometry execution model")); +} + +TEST_F(ValidatePrimitives, EmitStreamVertexSuccess) { + const std::string body = R"( +OpEmitStreamVertex %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidatePrimitives, EmitStreamVertexFailMissingCapability) { + CompileSuccessfully(GenerateShaderCode("OpEmitStreamVertex %u32_0", + "OpCapability Shader", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode EmitStreamVertex requires one of these " + "capabilities: GeometryStreams")); +} + +TEST_F(ValidatePrimitives, EmitStreamVertexFailWrongExecutionMode) { + CompileSuccessfully(GenerateShaderCode( + "OpEmitStreamVertex %u32_0", "OpCapability GeometryStreams", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "EmitStreamVertex instructions require Geometry execution model")); +} + +TEST_F(ValidatePrimitives, + EmitStreamVertexFailWrongExecutionModeNestedFunction) { + CompileSuccessfully( + GenerateShaderCode(CallAndCallee("OpEmitStreamVertex %u32_0"), + "OpCapability GeometryStreams", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "EmitStreamVertex instructions require Geometry execution model")); +} + +TEST_F(ValidatePrimitives, EmitStreamVertexNonInt) { + const std::string body = R"( +OpEmitStreamVertex %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("EmitStreamVertex: " + "expected Stream to be int scalar")); +} + +TEST_F(ValidatePrimitives, EmitStreamVertexNonScalar) { + const std::string body = R"( +OpEmitStreamVertex %u32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("EmitStreamVertex: " + "expected Stream to be int scalar")); +} + +TEST_F(ValidatePrimitives, EmitStreamVertexNonConstant) { + const std::string body = R"( +%val1 = OpIAdd %u32 %u32_0 %u32_1 +OpEmitStreamVertex %val1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("EmitStreamVertex: " + "expected Stream to be constant instruction")); +} + +TEST_F(ValidatePrimitives, EndStreamPrimitiveSuccess) { + const std::string body = R"( +OpEndStreamPrimitive %u32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidatePrimitives, EndStreamPrimitiveFailMissingCapability) { + CompileSuccessfully(GenerateShaderCode("OpEndStreamPrimitive %u32_0", + "OpCapability Shader", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Opcode EndStreamPrimitive requires one of these " + "capabilities: GeometryStreams")); +} + +TEST_F(ValidatePrimitives, EndStreamPrimitiveFailWrongExecutionMode) { + CompileSuccessfully(GenerateShaderCode( + "OpEndStreamPrimitive %u32_0", "OpCapability GeometryStreams", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "EndStreamPrimitive instructions require Geometry execution model")); +} + +TEST_F(ValidatePrimitives, + EndStreamPrimitiveFailWrongExecutionModeNestedFunction) { + CompileSuccessfully( + GenerateShaderCode(CallAndCallee("OpEndStreamPrimitive %u32_0"), + "OpCapability GeometryStreams", "Vertex")); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr( + "EndStreamPrimitive instructions require Geometry execution model")); +} + +TEST_F(ValidatePrimitives, EndStreamPrimitiveNonInt) { + const std::string body = R"( +OpEndStreamPrimitive %f32_0 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("EndStreamPrimitive: " + "expected Stream to be int scalar")); +} + +TEST_F(ValidatePrimitives, EndStreamPrimitiveNonScalar) { + const std::string body = R"( +OpEndStreamPrimitive %u32vec4_0123 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("EndStreamPrimitive: " + "expected Stream to be int scalar")); +} + +TEST_F(ValidatePrimitives, EndStreamPrimitiveNonConstant) { + const std::string body = R"( +%val1 = OpIAdd %u32 %u32_0 %u32_1 +OpEndStreamPrimitive %val1 +)"; + + CompileSuccessfully(GenerateShaderCode(body)); + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("EndStreamPrimitive: " + "expected Stream to be constant instruction")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_small_type_uses_test.cpp b/third_party/spirv-tools/test/val/val_small_type_uses_test.cpp new file mode 100644 index 0000000..b950af5 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_small_type_uses_test.cpp @@ -0,0 +1,338 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for 8- and 16-bit type uses. + +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_code_generator.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Values; + +using ValidateSmallTypeUses = spvtest::ValidateBase; + +CodeGenerator GetSmallTypesGenerator() { + CodeGenerator generator; + generator.capabilities_ = R"( +OpCapability Shader +OpCapability StorageBuffer16BitAccess +OpCapability StorageBuffer8BitAccess +)"; + generator.extensions_ = R"( +OpExtension "SPV_KHR_16bit_storage" +OpExtension "SPV_KHR_8bit_storage" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%ext = OpExtInstImport "GLSL.std.450" +)"; + generator.memory_model_ = "OpMemoryModel Logical GLSL450\n"; + std::string body = R"( +%short_gep = OpAccessChain %ptr_ssbo_short %var %int_0 %int_0 +%ld_short = OpLoad %short %short_gep +%short_to_int = OpSConvert %int %ld_short +%short_to_uint = OpUConvert %int %ld_short +%short_to_char = OpSConvert %char %ld_short +%short_to_uchar = OpSConvert %char %ld_short +%short2_gep = OpAccessChain %ptr_ssbo_short2 %var %int_0 +%ld_short2 = OpLoad %short2 %short2_gep +%short2_to_int2 = OpSConvert %int2 %ld_short2 +%short2_to_uint2 = OpUConvert %int2 %ld_short2 +%short2_to_char2 = OpSConvert %char2 %ld_short2 +%short2_to_uchar2 = OpSConvert %char2 %ld_short2 + +%char_gep = OpAccessChain %ptr_ssbo_char %var %int_2 %int_0 +%ld_char = OpLoad %char %char_gep +%char_to_int = OpSConvert %int %ld_char +%char_to_uint = OpUConvert %int %ld_char +%char_to_short = OpSConvert %short %ld_char +%char_to_ushort = OpSConvert %short %ld_char +%char2_gep = OpAccessChain %ptr_ssbo_char2 %var %int_2 +%ld_char2 = OpLoad %char2 %char2_gep +%char2_to_int2 = OpSConvert %int2 %ld_char2 +%char2_to_uint2 = OpUConvert %int2 %ld_char2 +%char2_to_short2 = OpSConvert %short2 %ld_char2 +%char2_to_ushort2 = OpSConvert %short2 %ld_char2 + +%half_gep = OpAccessChain %ptr_ssbo_half %var %int_1 %int_0 +%ld_half = OpLoad %half %half_gep +%half_to_float = OpFConvert %float %ld_half +%half2_gep = OpAccessChain %ptr_ssbo_half2 %var %int_1 +%ld_half2 = OpLoad %half2 %half2_gep +%half2_to_float2 = OpFConvert %float2 %ld_half2 + +%int_to_short = OpSConvert %short %int_0 +%int_to_ushort = OpUConvert %short %int_0 +%int_to_char = OpSConvert %char %int_0 +%int_to_uchar = OpUConvert %char %int_0 +%int2_to_short2 = OpSConvert %short2 %int2_0 +%int2_to_ushort2 = OpUConvert %short2 %int2_0 +%int2_to_char2 = OpSConvert %char2 %int2_0 +%int2_to_uchar2 = OpUConvert %char2 %int2_0 +%int_gep = OpAccessChain %ptr_ssbo_int %var %int_3 %int_0 +%int2_gep = OpAccessChain %ptr_ssbo_int2 %var %int_3 + +%float_to_half = OpFConvert %half %float_0 +%float2_to_half2 = OpFConvert %half2 %float2_0 +%float_gep = OpAccessChain %ptr_ssbo_float %var %int_4 %int_0 +%float2_gep = OpAccessChain %ptr_ssbo_float2 %var %int_4 +)"; + generator.entry_points_.push_back( + {"foo", "GLCompute", "OpExecutionMode %foo LocalSize 1 1 1", body, ""}); + generator.before_types_ = R"( +OpDecorate %block Block +OpMemberDecorate %block 0 Offset 0 +OpMemberDecorate %block 1 Offset 8 +OpMemberDecorate %block 2 Offset 16 +OpMemberDecorate %block 3 Offset 32 +OpMemberDecorate %block 4 Offset 64 +)"; + generator.types_ = R"( +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int2 = OpTypeVector %int 2 +%float = OpTypeFloat 32 +%float2 = OpTypeVector %float 2 +%bool = OpTypeBool +%bool2 = OpTypeVector %bool 2 +%char = OpTypeInt 8 0 +%char2 = OpTypeVector %char 2 +%ptr_ssbo_char = OpTypePointer StorageBuffer %char +%ptr_ssbo_char2 = OpTypePointer StorageBuffer %char2 +%short = OpTypeInt 16 0 +%short2 = OpTypeVector %short 2 +%ptr_ssbo_short = OpTypePointer StorageBuffer %short +%ptr_ssbo_short2 = OpTypePointer StorageBuffer %short2 +%half = OpTypeFloat 16 +%half2 = OpTypeVector %half 2 +%ptr_ssbo_half = OpTypePointer StorageBuffer %half +%ptr_ssbo_half2 = OpTypePointer StorageBuffer %half2 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%ptr_ssbo_int2 = OpTypePointer StorageBuffer %int2 +%ptr_ssbo_float = OpTypePointer StorageBuffer %float +%ptr_ssbo_float2 = OpTypePointer StorageBuffer %float2 +%block = OpTypeStruct %short2 %half2 %char2 %int2 %float2 +%ptr_ssbo_block = OpTypePointer StorageBuffer %block +%func = OpTypeFunction %void +)"; + generator.after_types_ = R"( +%var = OpVariable %ptr_ssbo_block StorageBuffer +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%int_2 = OpConstant %int 2 +%int_3 = OpConstant %int 3 +%int_4 = OpConstant %int 4 +%int2_0 = OpConstantComposite %int2 %int_0 %int_0 +%float_0 = OpConstant %float 0 +%float2_0 = OpConstantComposite %float2 %float_0 %float_0 + +%short_func_ty = OpTypeFunction %void %short +%char_func_ty = OpTypeFunction %void %char +%half_func_ty = OpTypeFunction %void %half +)"; + generator.add_at_the_end_ = R"( +%short_func = OpFunction %void None %short_func_ty +%short_param = OpFunctionParameter %short +%short_func_entry = OpLabel +OpReturn +OpFunctionEnd +%char_func = OpFunction %void None %char_func_ty +%char_param = OpFunctionParameter %char +%char_func_entry = OpLabel +OpReturn +OpFunctionEnd +%half_func = OpFunction %void None %half_func_ty +%half_param = OpFunctionParameter %half +%half_func_entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + return generator; +} + +TEST_F(ValidateSmallTypeUses, BadCharPhi) { + CodeGenerator generator = GetSmallTypesGenerator(); + generator.entry_points_[0].body += R"( +OpBranch %next_block +%next_block = OpLabel +%phi = OpPhi %char %ld_char %foo_entry +)"; + + CompileSuccessfully(generator.Build()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid use of 8- or 16-bit result")); +} + +TEST_F(ValidateSmallTypeUses, BadShortPhi) { + CodeGenerator generator = GetSmallTypesGenerator(); + generator.entry_points_[0].body += R"( +OpBranch %next_block +%next_block = OpLabel +%phi = OpPhi %short %ld_short %foo_entry +)"; + + CompileSuccessfully(generator.Build()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid use of 8- or 16-bit result")); +} + +TEST_F(ValidateSmallTypeUses, BadHalfPhi) { + CodeGenerator generator = GetSmallTypesGenerator(); + generator.entry_points_[0].body += R"( +OpBranch %next_block +%next_block = OpLabel +%phi = OpPhi %half %ld_half %foo_entry +)"; + + CompileSuccessfully(generator.Build()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid use of 8- or 16-bit result")); +} + +using ValidateGoodUses = spvtest::ValidateBase; + +TEST_P(ValidateGoodUses, Inst) { + const std::string inst = GetParam(); + CodeGenerator generator = GetSmallTypesGenerator(); + generator.entry_points_[0].body += inst + "\n"; + + CompileSuccessfully(generator.Build()); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +INSTANTIATE_TEST_SUITE_P( + SmallTypeUsesValid, ValidateGoodUses, + Values( + "%inst = OpIAdd %int %short_to_int %int_0", + "%inst = OpIAdd %int %short_to_uint %int_0", + "%inst = OpIAdd %int2 %short2_to_int2 %int2_0", + "%inst = OpIAdd %int2 %short2_to_uint2 %int2_0", + "%inst = OpIAdd %int %char_to_int %int_0", + "%inst = OpIAdd %int %char_to_uint %int_0", + "%inst = OpIAdd %int2 %char2_to_int2 %int2_0", + "%inst = OpIAdd %int2 %char2_to_uint2 %int2_0", + "%inst = OpUConvert %int %ld_short", + "%inst = OpSConvert %int %ld_short", + "%inst = OpUConvert %char %ld_short", + "%inst = OpSConvert %char %ld_short", + "%inst = OpUConvert %int %ld_char", "%inst = OpSConvert %int %ld_char", + "%inst = OpUConvert %short %ld_char", + "%inst = OpSConvert %short %ld_char", + "%inst = OpUConvert %int2 %ld_short2", + "%inst = OpSConvert %int2 %ld_short2", + "%inst = OpUConvert %char2 %ld_short2", + "%inst = OpSConvert %char2 %ld_short2", + "%inst = OpUConvert %int2 %ld_char2", + "%inst = OpSConvert %int2 %ld_char2", + "%inst = OpUConvert %short2 %ld_char2", + "%inst = OpSConvert %short2 %ld_char2", + "OpStore %short_gep %int_to_short", "OpStore %short_gep %int_to_ushort", + "OpStore %short_gep %char_to_short", + "OpStore %short_gep %char_to_ushort", + "OpStore %short2_gep %int2_to_short2", + "OpStore %short2_gep %int2_to_ushort2", + "OpStore %short2_gep %char2_to_short2", + "OpStore %short2_gep %char2_to_ushort2", + "OpStore %char_gep %int_to_char", "OpStore %char_gep %int_to_uchar", + "OpStore %char_gep %short_to_char", "OpStore %char_gep %short_to_uchar", + "OpStore %char2_gep %int2_to_char2", + "OpStore %char2_gep %int2_to_uchar2", + "OpStore %char2_gep %short2_to_char2", + "OpStore %char2_gep %short2_to_uchar2", + "OpStore %int_gep %short_to_int", "OpStore %int_gep %short_to_uint", + "OpStore %int_gep %char_to_int", "OpStore %int2_gep %char2_to_uint2", + "OpStore %int2_gep %short2_to_int2", + "OpStore %int2_gep %short2_to_uint2", + "OpStore %int2_gep %char2_to_int2", "OpStore %int2_gep %char2_to_uint2", + "%inst = OpFAdd %float %half_to_float %float_0", + "%inst = OpFAdd %float2 %half2_to_float2 %float2_0", + "%inst = OpFConvert %float %ld_half", + "%inst = OpFConvert %float2 %ld_half2", + "OpStore %half_gep %float_to_half", "OpStore %half_gep %ld_half", + "OpStore %half2_gep %float2_to_half2", "OpStore %half2_gep %ld_half2", + "OpStore %float_gep %half_to_float", + "OpStore %float2_gep %half2_to_float2")); + +using ValidateBadUses = spvtest::ValidateBase; + +TEST_P(ValidateBadUses, Inst) { + const std::string inst = GetParam(); + CodeGenerator generator = GetSmallTypesGenerator(); + generator.entry_points_[0].body += inst + "\n"; + + CompileSuccessfully(generator.Build()); + EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Invalid use of 8- or 16-bit result")); +} + +// A smattering of unacceptable use cases. Far too vast to cover exhaustively. +INSTANTIATE_TEST_SUITE_P( + SmallTypeUsesInvalid, ValidateBadUses, + Values("%inst = OpIAdd %short %ld_short %ld_short", + "%inst = OpIAdd %short %char_to_short %char_to_short", + "%inst = OpIAdd %short %char_to_ushort %char_to_ushort", + "%inst = OpIAdd %short %int_to_short %int_to_short", + "%inst = OpIAdd %short %int_to_ushort %int_to_ushort", + "%inst = OpIAdd %short2 %ld_short2 %ld_short2", + "%inst = OpIAdd %short2 %char2_to_short2 %char2_to_short2", + "%inst = OpIAdd %short2 %char2_to_ushort2 %char2_to_ushort2", + "%inst = OpIAdd %short2 %int2_to_short2 %int2_to_short2", + "%inst = OpIAdd %short2 %int2_to_ushort2 %int2_to_ushort2", + "%inst = OpIEqual %bool %ld_short %ld_short", + "%inst = OpIEqual %bool %char_to_short %char_to_short", + "%inst = OpIEqual %bool %char_to_ushort %char_to_ushort", + "%inst = OpIEqual %bool %int_to_short %int_to_short", + "%inst = OpIEqual %bool %int_to_ushort %int_to_ushort", + "%inst = OpIEqual %bool2 %ld_short2 %ld_short2", + "%inst = OpIEqual %bool2 %char2_to_short2 %char2_to_short2", + "%inst = OpIEqual %bool2 %char2_to_ushort2 %char2_to_ushort2", + "%inst = OpIEqual %bool2 %int2_to_short2 %int2_to_short2", + "%inst = OpIEqual %bool2 %int2_to_ushort2 %int2_to_ushort2", + "%inst = OpFAdd %half %ld_half %ld_half", + "%inst = OpFAdd %half %float_to_half %float_to_half", + "%inst = OpFAdd %half2 %ld_half2 %ld_half2", + "%inst = OpFAdd %half2 %float2_to_half2 %float2_to_half2", + "%inst = OpFOrdGreaterThan %bool %ld_half %ld_half", + "%inst = OpFOrdGreaterThan %bool %float_to_half %float_to_half", + "%inst = OpFOrdGreaterThan %bool2 %ld_half2 %ld_half2", + "%inst = OpFOrdGreaterThan %bool2 %float2_to_half2 %float2_to_half2", + "%inst = OpFunctionCall %void %short_func %ld_short", + "%inst = OpFunctionCall %void %short_func %char_to_short", + "%inst = OpFunctionCall %void %short_func %char_to_ushort", + "%inst = OpFunctionCall %void %short_func %int_to_short", + "%inst = OpFunctionCall %void %short_func %int_to_ushort", + "%inst = OpFunctionCall %void %char_func %ld_char", + "%inst = OpFunctionCall %void %char_func %short_to_char", + "%inst = OpFunctionCall %void %char_func %short_to_uchar", + "%inst = OpFunctionCall %void %char_func %int_to_char", + "%inst = OpFunctionCall %void %char_func %int_to_uchar", + "%inst = OpFunctionCall %void %half_func %ld_half", + "%inst = OpFunctionCall %void %half_func %float_to_half")); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_ssa_test.cpp b/third_party/spirv-tools/test/val/val_ssa_test.cpp new file mode 100644 index 0000000..035c710 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_ssa_test.cpp @@ -0,0 +1,1450 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for SSA + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::MatchesRegex; + +using ValidateSSA = spvtest::ValidateBase>; + +TEST_F(ValidateSSA, Default) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %3 "" + OpExecutionMode %3 LocalSize 1 1 1 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, IdUndefinedBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %missing "missing" +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%func = OpFunction %vfunct None %missing +%flabel = OpLabel + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, IdRedefinedBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %2 "redefined" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%2 = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); +} + +TEST_F(ValidateSSA, DominateUsageBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %1 "not_dominant" +%2 = OpTypeFunction %1 ; uses %1 before it's definition +%1 = OpTypeVoid +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("not_dominant")); +} + +TEST_F(ValidateSSA, DominateUsageWithinBlockBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %bad "bad" +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%uintt = OpTypeInt 32 0 +%one = OpConstant %uintt 1 +%func = OpFunction %voidt None %funct +%entry = OpLabel +%sum = OpIAdd %uintt %one %bad +%bad = OpCopyObject %uintt %sum + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("ID .\\[%bad\\] has not been defined\n" + " %8 = OpIAdd %uint %uint_1 %bad\n")); +} + +TEST_F(ValidateSSA, DominateUsageSameInstructionBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %sum "sum" +%voidt = OpTypeVoid +%funct = OpTypeFunction %voidt +%uintt = OpTypeInt 32 0 +%one = OpConstant %uintt 1 +%func = OpFunction %voidt None %funct +%entry = OpLabel +%sum = OpIAdd %uintt %one %sum + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("ID .\\[%sum\\] has not been defined\n" + " %sum = OpIAdd %uint %uint_1 %sum\n")); +} + +TEST_F(ValidateSSA, ForwardNameGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %3 "main" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardNameMissingTargetBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %5 "main" ; Target never defined +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("main")); +} + +TEST_F(ValidateSSA, ForwardMemberNameGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpMemberName %struct 0 "value" + OpMemberName %struct 1 "size" +%intt = OpTypeInt 32 1 +%uintt = OpTypeInt 32 0 +%struct = OpTypeStruct %intt %uintt +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardMemberNameMissingTargetBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpMemberName %struct 0 "value" + OpMemberName %bad 1 "size" ; Target is not defined +%intt = OpTypeInt 32 1 +%uintt = OpTypeInt 32 0 +%struct = OpTypeStruct %intt %uintt +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("The following forward referenced IDs have not been " + "defined:\n2[%2]")); +} + +TEST_F(ValidateSSA, ForwardDecorateGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %var Restrict +%intt = OpTypeInt 32 1 +%ptrt = OpTypePointer UniformConstant %intt +%var = OpVariable %ptrt UniformConstant +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardDecorateInvalidIDBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %missing "missing" + OpDecorate %missing Restrict ;Missing ID +%voidt = OpTypeVoid +%intt = OpTypeInt 32 1 +%ptrt = OpTypePointer UniformConstant %intt +%var = OpVariable %ptrt UniformConstant +%2 = OpTypeFunction %voidt +%3 = OpFunction %voidt None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, ForwardMemberDecorateGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpMemberDecorate %struct 1 RowMajor +%intt = OpTypeInt 32 1 +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 3 +%struct = OpTypeStruct %intt %mat33 +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardMemberDecorateInvalidIdBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %missing "missing" + OpMemberDecorate %missing 1 RowMajor ; Target not defined +%intt = OpTypeInt 32 1 +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%mat33 = OpTypeMatrix %vec3 3 +%struct = OpTypeStruct %intt %mat33 +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, ForwardGroupDecorateGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpDecorate %dgrp RowMajor +%dgrp = OpDecorationGroup + OpGroupDecorate %dgrp %mat33 %mat44 +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%vec4 = OpTypeVector %f32 4 +%mat33 = OpTypeMatrix %vec3 3 +%mat44 = OpTypeMatrix %vec4 4 +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardGroupDecorateMissingGroupBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %missing "missing" + OpDecorate %dgrp RowMajor +%dgrp = OpDecorationGroup + OpGroupDecorate %missing %mat33 %mat44 ; Target not defined +%intt = OpTypeInt 32 1 +%vec3 = OpTypeVector %intt 3 +%vec4 = OpTypeVector %intt 4 +%mat33 = OpTypeMatrix %vec3 3 +%mat44 = OpTypeMatrix %vec4 4 +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, ForwardGroupDecorateMissingTargetBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %missing "missing" + OpDecorate %dgrp RowMajor +%dgrp = OpDecorationGroup + OpGroupDecorate %dgrp %missing %mat44 ; Target not defined +%f32 = OpTypeFloat 32 +%vec3 = OpTypeVector %f32 3 +%vec4 = OpTypeVector %f32 4 +%mat33 = OpTypeMatrix %vec3 3 +%mat44 = OpTypeMatrix %vec4 4 +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, ForwardGroupDecorateDecorationGroupDominateBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %dgrp "group" + OpDecorate %dgrp RowMajor + OpGroupDecorate %dgrp %mat33 %mat44 ; Decoration group does not dominate usage +%dgrp = OpDecorationGroup +%intt = OpTypeInt 32 1 +%vec3 = OpTypeVector %intt 3 +%vec4 = OpTypeVector %intt 4 +%mat33 = OpTypeMatrix %vec3 3 +%mat44 = OpTypeMatrix %vec4 4 +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("group")); +} + +TEST_F(ValidateSSA, ForwardDecorateInvalidIdBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %missing "missing" + OpDecorate %missing Restrict ; Missing target +%voidt = OpTypeVoid +%intt = OpTypeInt 32 1 +%ptrt = OpTypePointer UniformConstant %intt +%var = OpVariable %ptrt UniformConstant +%2 = OpTypeFunction %voidt +%3 = OpFunction %voidt None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, FunctionCallGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 1 +%3 = OpTypeInt 32 0 +%4 = OpTypeFunction %1 +%8 = OpTypeFunction %1 %2 %3 +%four = OpConstant %2 4 +%five = OpConstant %3 5 +%9 = OpFunction %1 None %8 +%10 = OpFunctionParameter %2 +%11 = OpFunctionParameter %3 +%12 = OpLabel + OpReturn + OpFunctionEnd +%5 = OpFunction %1 None %4 +%6 = OpLabel +%7 = OpFunctionCall %1 %9 %four %five + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardFunctionCallGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%1 = OpTypeVoid +%2 = OpTypeInt 32 1 +%3 = OpTypeInt 32 0 +%four = OpConstant %2 4 +%five = OpConstant %3 5 +%8 = OpTypeFunction %1 %2 %3 +%4 = OpTypeFunction %1 +%5 = OpFunction %1 None %4 +%6 = OpLabel +%7 = OpFunctionCall %1 %9 %four %five + OpReturn + OpFunctionEnd +%9 = OpFunction %1 None %8 +%10 = OpFunctionParameter %2 +%11 = OpFunctionParameter %3 +%12 = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardBranchConditionalGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%voidt = OpTypeVoid +%boolt = OpTypeBool +%vfunct = OpTypeFunction %voidt +%true = OpConstantTrue %boolt +%main = OpFunction %voidt None %vfunct +%mainl = OpLabel + OpSelectionMerge %endl None + OpBranchConditional %true %truel %falsel +%truel = OpLabel + OpNop + OpBranch %endl +%falsel = OpLabel + OpNop + OpBranch %endl +%endl = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardBranchConditionalWithWeightsGood) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%voidt = OpTypeVoid +%boolt = OpTypeBool +%vfunct = OpTypeFunction %voidt +%true = OpConstantTrue %boolt +%main = OpFunction %voidt None %vfunct +%mainl = OpLabel + OpSelectionMerge %endl None + OpBranchConditional %true %truel %falsel 1 9 +%truel = OpLabel + OpNop + OpBranch %endl +%falsel = OpLabel + OpNop + OpBranch %endl +%endl = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardBranchConditionalNonDominantConditionBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %tcpy "conditional" +%voidt = OpTypeVoid +%boolt = OpTypeBool +%vfunct = OpTypeFunction %voidt +%true = OpConstantTrue %boolt +%main = OpFunction %voidt None %vfunct +%mainl = OpLabel + OpSelectionMerge %endl None + OpBranchConditional %tcpy %truel %falsel ; +%truel = OpLabel + OpNop + OpBranch %endl +%falsel = OpLabel + OpNop + OpBranch %endl +%endl = OpLabel +%tcpy = OpCopyObject %boolt %true + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("conditional")); +} + +TEST_F(ValidateSSA, ForwardBranchConditionalMissingTargetBad) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 + OpName %missing "missing" +%voidt = OpTypeVoid +%boolt = OpTypeBool +%vfunct = OpTypeFunction %voidt +%true = OpConstantTrue %boolt +%main = OpFunction %voidt None %vfunct +%mainl = OpLabel + OpSelectionMerge %endl None + OpBranchConditional %true %missing %falsel +%truel = OpLabel + OpNop + OpBranch %endl +%falsel = OpLabel + OpNop + OpBranch %endl +%endl = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +// Since Int8 requires the Kernel capability, the signedness of int types may +// not be "1". +const std::string kHeader = R"( +OpCapability Int8 +OpCapability DeviceEnqueue +OpCapability Linkage +OpMemoryModel Logical OpenCL +)"; + +const std::string kBasicTypes = R"( +%voidt = OpTypeVoid +%boolt = OpTypeBool +%int8t = OpTypeInt 8 0 +%uintt = OpTypeInt 32 0 +%vfunct = OpTypeFunction %voidt +%intptrt = OpTypePointer UniformConstant %uintt +%zero = OpConstant %uintt 0 +%one = OpConstant %uintt 1 +%ten = OpConstant %uintt 10 +%false = OpConstantFalse %boolt +)"; + +const std::string kKernelTypesAndConstants = R"( +%queuet = OpTypeQueue + +%three = OpConstant %uintt 3 +%arr3t = OpTypeArray %uintt %three +%ndt = OpTypeStruct %uintt %arr3t %arr3t %arr3t + +%eventt = OpTypeEvent + +%offset = OpConstant %uintt 0 +%local = OpConstant %uintt 1 +%gl = OpConstant %uintt 1 + +%nevent = OpConstant %uintt 0 +%event = OpConstantNull %eventt + +%firstp = OpConstant %int8t 0 +%psize = OpConstant %uintt 0 +%palign = OpConstant %uintt 32 +%lsize = OpConstant %uintt 1 +%flags = OpConstant %uintt 0 ; NoWait + +%kfunct = OpTypeFunction %voidt %intptrt +)"; + +const std::string kKernelSetup = R"( +%dqueue = OpGetDefaultQueue %queuet +%ndval = OpBuildNDRange %ndt %gl %local %offset +%revent = OpUndef %eventt + +)"; + +const std::string kKernelDefinition = R"( +%kfunc = OpFunction %voidt None %kfunct +%iparam = OpFunctionParameter %intptrt +%kfuncl = OpLabel + OpNop + OpReturn + OpFunctionEnd +)"; + +TEST_F(ValidateSSA, EnqueueKernelGood) { + std::string str = kHeader + kBasicTypes + kKernelTypesAndConstants + + kKernelDefinition + R"( + %main = OpFunction %voidt None %vfunct + %mainl = OpLabel + )" + kKernelSetup + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event %revent %kfunc %firstp %psize + %palign %lsize + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelGood) { + std::string str = kHeader + kBasicTypes + kKernelTypesAndConstants + R"( + %main = OpFunction %voidt None %vfunct + %mainl = OpLabel + )" + + kKernelSetup + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event %revent %kfunc %firstp %psize + %palign %lsize + OpReturn + OpFunctionEnd + )" + kKernelDefinition; + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, EnqueueMissingFunctionBad) { + std::string str = kHeader + "OpName %kfunc \"kfunc\"" + kBasicTypes + + kKernelTypesAndConstants + R"( + %main = OpFunction %voidt None %vfunct + %mainl = OpLabel + )" + kKernelSetup + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event %revent %kfunc %firstp %psize + %palign %lsize + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("kfunc")); +} + +std::string forwardKernelNonDominantParameterBaseCode( + std::string name = std::string()) { + std::string op_name; + if (name.empty()) { + op_name = ""; + } else { + op_name = "\nOpName %" + name + " \"" + name + "\"\n"; + } + std::string out = kHeader + op_name + kBasicTypes + kKernelTypesAndConstants + + kKernelDefinition + + R"( + %main = OpFunction %voidt None %vfunct + %mainl = OpLabel + )" + kKernelSetup; + return out; +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelMissingParameter1Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("missing") + R"( + %err = OpEnqueueKernel %missing %dqueue %flags %ndval + %nevent %event %revent %kfunc %firstp + %psize %palign %lsize + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter2Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("dqueue2") + R"( + %err = OpEnqueueKernel %uintt %dqueue2 %flags %ndval + %nevent %event %revent %kfunc + %firstp %psize %palign %lsize + %dqueue2 = OpGetDefaultQueue %queuet + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("dqueue2")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter3Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("ndval2") + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval2 + %nevent %event %revent %kfunc %firstp + %psize %palign %lsize + %ndval2 = OpBuildNDRange %ndt %gl %local %offset + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("ndval2")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter4Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("nevent2") + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent2 + %event %revent %kfunc %firstp %psize + %palign %lsize + %nevent2 = OpCopyObject %uintt %nevent + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("nevent2")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter5Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("event2") + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event2 %revent %kfunc %firstp %psize + %palign %lsize + %event2 = OpCopyObject %eventt %event + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("event2")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter6Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("revent2") + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event %revent2 %kfunc %firstp %psize + %palign %lsize + %revent2 = OpCopyObject %eventt %revent + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("revent2")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter8Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("firstp2") + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event %revent %kfunc %firstp2 %psize + %palign %lsize + %firstp2 = OpCopyObject %int8t %firstp + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("firstp2")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter9Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("psize2") + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event %revent %kfunc %firstp %psize2 + %palign %lsize + %psize2 = OpCopyObject %uintt %psize + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("psize2")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter10Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("palign2") + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event %revent %kfunc %firstp %psize + %palign2 %lsize + %palign2 = OpCopyObject %uintt %palign + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("palign2")); +} + +TEST_F(ValidateSSA, ForwardEnqueueKernelNonDominantParameter11Bad) { + std::string str = forwardKernelNonDominantParameterBaseCode("lsize2") + R"( + %err = OpEnqueueKernel %uintt %dqueue %flags %ndval %nevent + %event %revent %kfunc %firstp %psize + %palign %lsize2 + %lsize2 = OpCopyObject %uintt %lsize + OpReturn + OpFunctionEnd + )"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("lsize2")); +} + +static const bool kWithNDrange = true; +static const bool kNoNDrange = false; +std::pair cases[] = { + {"OpGetKernelNDrangeSubGroupCount", kWithNDrange}, + {"OpGetKernelNDrangeMaxSubGroupSize", kWithNDrange}, + {"OpGetKernelWorkGroupSize", kNoNDrange}, + {"OpGetKernelPreferredWorkGroupSizeMultiple", kNoNDrange}}; + +INSTANTIATE_TEST_SUITE_P(KernelArgs, ValidateSSA, ::testing::ValuesIn(cases)); + +static const std::string return_instructions = R"( + OpReturn + OpFunctionEnd +)"; + +TEST_P(ValidateSSA, GetKernelGood) { + std::string instruction = GetParam().first; + bool with_ndrange = GetParam().second; + std::string ndrange_param = with_ndrange ? " %ndval " : " "; + + std::stringstream ss; + // clang-format off + ss << forwardKernelNonDominantParameterBaseCode() + " %numsg = " + << instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize %palign" + << return_instructions; + // clang-format on + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateSSA, ForwardGetKernelGood) { + std::string instruction = GetParam().first; + bool with_ndrange = GetParam().second; + std::string ndrange_param = with_ndrange ? " %ndval " : " "; + + // clang-format off + std::string str = kHeader + kBasicTypes + kKernelTypesAndConstants + + R"( + %main = OpFunction %voidt None %vfunct + %mainl = OpLabel + )" + + kKernelSetup + " %numsg = " + + instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize %palign" + + return_instructions + kKernelDefinition; + // clang-format on + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_P(ValidateSSA, ForwardGetKernelMissingDefinitionBad) { + std::string instruction = GetParam().first; + bool with_ndrange = GetParam().second; + std::string ndrange_param = with_ndrange ? " %ndval " : " "; + + std::stringstream ss; + // clang-format off + ss << forwardKernelNonDominantParameterBaseCode("missing") + " %numsg = " + << instruction + " %uintt" + ndrange_param + "%missing %firstp %psize %palign" + << return_instructions; + // clang-format on + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_P(ValidateSSA, ForwardGetKernelNDrangeSubGroupCountMissingParameter1Bad) { + std::string instruction = GetParam().first; + bool with_ndrange = GetParam().second; + std::string ndrange_param = with_ndrange ? " %ndval " : " "; + + std::stringstream ss; + // clang-format off + ss << forwardKernelNonDominantParameterBaseCode("missing") + " %numsg = " + << instruction + " %missing" + ndrange_param + "%kfunc %firstp %psize %palign" + << return_instructions; + // clang-format on + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_P(ValidateSSA, + ForwardGetKernelNDrangeSubGroupCountNonDominantParameter2Bad) { + std::string instruction = GetParam().first; + bool with_ndrange = GetParam().second; + std::string ndrange_param = with_ndrange ? " %ndval2 " : " "; + + std::stringstream ss; + // clang-format off + ss << forwardKernelNonDominantParameterBaseCode("ndval2") + " %numsg = " + << instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize %palign" + << "\n %ndval2 = OpBuildNDRange %ndt %gl %local %offset" + << return_instructions; + // clang-format on + + if (GetParam().second) { + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("ndval2")); + } +} + +TEST_P(ValidateSSA, + ForwardGetKernelNDrangeSubGroupCountNonDominantParameter4Bad) { + std::string instruction = GetParam().first; + bool with_ndrange = GetParam().second; + std::string ndrange_param = with_ndrange ? " %ndval " : " "; + + std::stringstream ss; + // clang-format off + ss << forwardKernelNonDominantParameterBaseCode("firstp2") + " %numsg = " + << instruction + " %uintt" + ndrange_param + "%kfunc %firstp2 %psize %palign" + << "\n %firstp2 = OpCopyObject %int8t %firstp" + << return_instructions; + // clang-format on + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("firstp2")); +} + +TEST_P(ValidateSSA, + ForwardGetKernelNDrangeSubGroupCountNonDominantParameter5Bad) { + std::string instruction = GetParam().first; + bool with_ndrange = GetParam().second; + std::string ndrange_param = with_ndrange ? " %ndval " : " "; + + std::stringstream ss; + // clang-format off + ss << forwardKernelNonDominantParameterBaseCode("psize2") + " %numsg = " + << instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize2 %palign" + << "\n %psize2 = OpCopyObject %uintt %psize" + << return_instructions; + // clang-format on + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("psize2")); +} + +TEST_P(ValidateSSA, + ForwardGetKernelNDrangeSubGroupCountNonDominantParameter6Bad) { + std::string instruction = GetParam().first; + bool with_ndrange = GetParam().second; + std::string ndrange_param = with_ndrange ? " %ndval " : " "; + + std::stringstream ss; + // clang-format off + ss << forwardKernelNonDominantParameterBaseCode("palign2") + " %numsg = " + << instruction + " %uintt" + ndrange_param + "%kfunc %firstp %psize %palign2" + << "\n %palign2 = OpCopyObject %uintt %palign" + << return_instructions; + // clang-format on + + if (GetParam().second) { + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("palign2")); + } +} + +TEST_F(ValidateSSA, PhiGood) { + std::string str = kHeader + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%preheader = OpLabel +%init = OpCopyObject %uintt %zero + OpBranch %loop +%loop = OpLabel +%i = OpPhi %uintt %init %preheader %loopi %loop +%loopi = OpIAdd %uintt %i %one + OpNop +%cond = OpSLessThan %boolt %i %ten + OpLoopMerge %endl %loop None + OpBranchConditional %cond %loop %endl +%endl = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, PhiMissingTypeBad) { + std::string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%preheader = OpLabel +%init = OpCopyObject %uintt %zero + OpBranch %loop +%loop = OpLabel +%i = OpPhi %missing %init %preheader %loopi %loop +%loopi = OpIAdd %uintt %i %one + OpNop +%cond = OpSLessThan %boolt %i %ten + OpLoopMerge %endl %loop None + OpBranchConditional %cond %loop %endl +%endl = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, PhiMissingIdBad) { + std::string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%preheader = OpLabel +%init = OpCopyObject %uintt %zero + OpBranch %loop +%loop = OpLabel +%i = OpPhi %uintt %missing %preheader %loopi %loop +%loopi = OpIAdd %uintt %i %one + OpNop +%cond = OpSLessThan %boolt %i %ten + OpLoopMerge %endl %loop None + OpBranchConditional %cond %loop %endl +%endl = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, PhiMissingLabelBad) { + std::string str = kHeader + "OpName %missing \"missing\"" + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%preheader = OpLabel +%init = OpCopyObject %uintt %zero + OpBranch %loop +%loop = OpLabel +%i = OpPhi %uintt %init %missing %loopi %loop +%loopi = OpIAdd %uintt %i %one + OpNop +%cond = OpSLessThan %boolt %i %ten + OpLoopMerge %endl %loop None + OpBranchConditional %cond %loop %endl +%endl = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr("missing")); +} + +TEST_F(ValidateSSA, IdDominatesItsUseGood) { + std::string str = kHeader + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%cond = OpSLessThan %boolt %one %ten +%eleven = OpIAdd %uintt %one %ten + OpSelectionMerge %merge None + OpBranchConditional %cond %t %f +%t = OpLabel +%twelve = OpIAdd %uintt %eleven %one + OpBranch %merge +%f = OpLabel +%twentytwo = OpIAdd %uintt %eleven %ten + OpBranch %merge +%merge = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, IdDoesNotDominateItsUseBad) { + std::string str = kHeader + + "OpName %eleven \"eleven\"\n" + "OpName %true_block \"true_block\"\n" + "OpName %false_block \"false_block\"" + + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%cond = OpSLessThan %boolt %one %ten + OpSelectionMerge %merge None + OpBranchConditional %cond %true_block %false_block +%true_block = OpLabel +%eleven = OpIAdd %uintt %one %ten +%twelve = OpIAdd %uintt %eleven %one + OpBranch %merge +%false_block = OpLabel +%twentytwo = OpIAdd %uintt %eleven %ten + OpBranch %merge +%merge = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("ID .\\[%eleven\\] defined in block .\\[%true_block\\] " + "does not dominate its use in block .\\[%false_block\\]\n" + " %false_block = OpLabel\n")); +} + +TEST_F(ValidateSSA, PhiUseDoesntDominateDefinitionGood) { + std::string str = kHeader + kBasicTypes + + R"( +%funcintptrt = OpTypePointer Function %uintt +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%var_one = OpVariable %funcintptrt Function %one +%one_val = OpLoad %uintt %var_one + OpBranch %loop +%loop = OpLabel +%i = OpPhi %uintt %one_val %entry %inew %cont +%cond = OpSLessThan %boolt %one %ten + OpLoopMerge %merge %cont None + OpBranchConditional %cond %body %merge +%body = OpLabel + OpBranch %cont +%cont = OpLabel +%inew = OpIAdd %uintt %i %one + OpBranch %loop +%merge = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, + PhiUseDoesntDominateUseOfPhiOperandUsedBeforeDefinitionBad) { + std::string str = kHeader + "OpName %inew \"inew\"" + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%var_one = OpVariable %intptrt Function %one +%one_val = OpLoad %uintt %var_one + OpBranch %loop +%loop = OpLabel +%i = OpPhi %uintt %one_val %entry %inew %cont +%bad = OpIAdd %uintt %inew %one +%cond = OpSLessThan %boolt %one %ten + OpLoopMerge %merge %cont None + OpBranchConditional %cond %body %merge +%body = OpLabel + OpBranch %cont +%cont = OpLabel +%inew = OpIAdd %uintt %i %one + OpBranch %loop +%merge = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + MatchesRegex("ID .\\[%inew\\] has not been defined\n" + " %19 = OpIAdd %uint %inew %uint_1\n")); +} + +TEST_F(ValidateSSA, PhiUseMayComeFromNonDominatingBlockGood) { + std::string str = kHeader + "OpName %if_true \"if_true\"\n" + + "OpName %exit \"exit\"\n" + "OpName %copy \"copy\"\n" + + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel + OpBranchConditional %false %if_true %exit + +%if_true = OpLabel +%copy = OpCopyObject %boolt %false + OpBranch %exit + +; The use of %copy here is ok, even though it was defined +; in a block that does not dominate %exit. That's the point +; of an OpPhi. +%exit = OpLabel +%value = OpPhi %boolt %false %entry %copy %if_true + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateSSA, PhiUsesItsOwnDefinitionGood) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/415 + // + // Non-phi instructions can't use their own definitions, as + // already checked in test DominateUsageSameInstructionBad. + std::string str = kHeader + "OpName %loop \"loop\"\n" + + "OpName %value \"value\"\n" + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel + OpBranch %loop + +%loop = OpLabel +%value = OpPhi %boolt %false %entry %value %loop + OpBranch %loop + + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateSSA, PhiVariableDefNotDominatedByParentBlockBad) { + std::string str = kHeader + "OpName %if_true \"if_true\"\n" + + "OpName %if_false \"if_false\"\n" + + "OpName %exit \"exit\"\n" + "OpName %value \"phi\"\n" + + "OpName %true_copy \"true_copy\"\n" + + "OpName %false_copy \"false_copy\"\n" + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel + OpBranchConditional %false %if_true %if_false + +%if_true = OpLabel +%true_copy = OpCopyObject %boolt %false + OpBranch %exit + +%if_false = OpLabel +%false_copy = OpCopyObject %boolt %false + OpBranch %exit + +; The (variable,Id) pairs are swapped. +%exit = OpLabel +%value = OpPhi %boolt %true_copy %if_false %false_copy %if_true + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("In OpPhi instruction .\\[%phi\\], ID .\\[%true_copy\\] " + "definition does not dominate its parent .\\[%if_false\\]\n" + " %phi = OpPhi %bool %true_copy %if_false %false_copy " + "%if_true\n")); +} + +TEST_F(ValidateSSA, PhiVariableDefDominatesButNotDefinedInParentBlock) { + std::string str = kHeader + "OpName %if_true \"if_true\"\n" + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel + OpBranchConditional %false %if_true %if_false + +%if_true = OpLabel +%true_copy = OpCopyObject %boolt %false + OpBranch %if_tnext +%if_tnext = OpLabel + OpBranch %exit + +%if_false = OpLabel +%false_copy = OpCopyObject %boolt %false + OpBranch %if_fnext +%if_fnext = OpLabel + OpBranch %exit + +%exit = OpLabel +%value = OpPhi %boolt %true_copy %if_tnext %false_copy %if_fnext + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, + DominanceCheckIgnoresUsesInUnreachableBlocksDefInBlockGood) { + std::string str = kHeader + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel +%def = OpCopyObject %boolt %false + OpReturn + +%unreach = OpLabel +%use = OpCopyObject %boolt %def + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateSSA, PhiVariableUnreachableDefNotInParentBlock) { + std::string str = kHeader + "OpName %unreachable \"unreachable\"\n" + + kBasicTypes + + R"( +%func = OpFunction %voidt None %vfunct +%entry = OpLabel + OpBranch %if_false + +%unreachable = OpLabel +%copy = OpCopyObject %boolt %false + OpBranch %if_tnext +%if_tnext = OpLabel + OpBranch %exit + +%if_false = OpLabel +%false_copy = OpCopyObject %boolt %false + OpBranch %if_fnext +%if_fnext = OpLabel + OpBranch %exit + +%exit = OpLabel +%value = OpPhi %boolt %copy %if_tnext %false_copy %if_fnext + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, + DominanceCheckIgnoresUsesInUnreachableBlocksDefIsParamGood) { + std::string str = kHeader + kBasicTypes + + R"( +%void_fn_int = OpTypeFunction %voidt %uintt +%func = OpFunction %voidt None %void_fn_int +%int_param = OpFunctionParameter %uintt +%entry = OpLabel + OpReturn + +%unreach = OpLabel +%use = OpCopyObject %uintt %int_param + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions()) << getDiagnosticString(); +} + +TEST_F(ValidateSSA, UseFunctionParameterFromOtherFunctionBad) { + std::string str = kHeader + + "OpName %first \"first\"\n" + "OpName %func \"func\"\n" + + "OpName %func2 \"func2\"\n" + kBasicTypes + + R"( +%viifunct = OpTypeFunction %voidt %uintt %uintt +%func = OpFunction %voidt None %viifunct +%first = OpFunctionParameter %uintt +%second = OpFunctionParameter %uintt + OpFunctionEnd +%func2 = OpFunction %voidt None %viifunct +%first2 = OpFunctionParameter %uintt +%second2 = OpFunctionParameter %uintt +%entry2 = OpLabel +%baduse = OpIAdd %uintt %first %first2 + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT( + getDiagnosticString(), + MatchesRegex("ID .\\[%first\\] used in function .\\[%func2\\] is used " + "outside of it's defining function .\\[%func\\]\n" + " %func = OpFunction %void None %14\n")); +} + +TEST_F(ValidateSSA, TypeForwardPointerForwardReference) { + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/429 + // + // ForwardPointers can references instructions that have not been defined + std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %intptrt "intptrt" + OpTypeForwardPointer %intptrt UniformConstant + %uint = OpTypeInt 32 0 + %struct = OpTypeStruct %uint + %intptrt = OpTypePointer UniformConstant %struct +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateSSA, TypeStructForwardReference) { + std::string str = R"( + OpCapability Kernel + OpCapability Addresses + OpCapability Linkage + OpMemoryModel Logical OpenCL + OpName %structptr "structptr" + OpTypeForwardPointer %structptr UniformConstant + %uint = OpTypeInt 32 0 + %structt1 = OpTypeStruct %structptr %uint + %structt2 = OpTypeStruct %uint %structptr + %structt3 = OpTypeStruct %uint %uint %structptr + %structt4 = OpTypeStruct %uint %uint %uint %structptr + %structptr = OpTypePointer UniformConstant %structt1 +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// TODO(umar): OpGroupMemberDecorate + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_state_test.cpp b/third_party/spirv-tools/test/val/val_state_test.cpp new file mode 100644 index 0000000..b2d2604 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_state_test.cpp @@ -0,0 +1,190 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Unit tests for ValidationState_t. + +#include + +#include "gtest/gtest.h" +#include "source/latest_version_spirv_header.h" + +#include "source/enum_set.h" +#include "source/extensions.h" +#include "source/spirv_validator_options.h" +#include "source/val/construct.h" +#include "source/val/function.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// This is all we need for these tests. +static uint32_t kFakeBinary[] = {0}; + +// A test with a ValidationState_t member transparently. +class ValidationStateTest : public testing::Test { + public: + ValidationStateTest() + : context_(spvContextCreate(SPV_ENV_UNIVERSAL_1_0)), + options_(spvValidatorOptionsCreate()), + state_(context_, options_, kFakeBinary, 0, 1) {} + + ~ValidationStateTest() { + spvContextDestroy(context_); + spvValidatorOptionsDestroy(options_); + } + + protected: + spv_context context_; + spv_validator_options options_; + ValidationState_t state_; +}; + +// A test of ValidationState_t::HasAnyOfCapabilities(). +using ValidationState_HasAnyOfCapabilities = ValidationStateTest; + +TEST_F(ValidationState_HasAnyOfCapabilities, EmptyMask) { + EXPECT_TRUE(state_.HasAnyOfCapabilities({})); + state_.RegisterCapability(SpvCapabilityMatrix); + EXPECT_TRUE(state_.HasAnyOfCapabilities({})); + state_.RegisterCapability(SpvCapabilityImageMipmap); + EXPECT_TRUE(state_.HasAnyOfCapabilities({})); + state_.RegisterCapability(SpvCapabilityPipes); + EXPECT_TRUE(state_.HasAnyOfCapabilities({})); + state_.RegisterCapability(SpvCapabilityStorageImageArrayDynamicIndexing); + EXPECT_TRUE(state_.HasAnyOfCapabilities({})); + state_.RegisterCapability(SpvCapabilityClipDistance); + EXPECT_TRUE(state_.HasAnyOfCapabilities({})); + state_.RegisterCapability(SpvCapabilityStorageImageWriteWithoutFormat); + EXPECT_TRUE(state_.HasAnyOfCapabilities({})); +} + +TEST_F(ValidationState_HasAnyOfCapabilities, SingleCapMask) { + EXPECT_FALSE(state_.HasAnyOfCapabilities({SpvCapabilityMatrix})); + EXPECT_FALSE(state_.HasAnyOfCapabilities({SpvCapabilityImageMipmap})); + state_.RegisterCapability(SpvCapabilityMatrix); + EXPECT_TRUE(state_.HasAnyOfCapabilities({SpvCapabilityMatrix})); + EXPECT_FALSE(state_.HasAnyOfCapabilities({SpvCapabilityImageMipmap})); + state_.RegisterCapability(SpvCapabilityImageMipmap); + EXPECT_TRUE(state_.HasAnyOfCapabilities({SpvCapabilityMatrix})); + EXPECT_TRUE(state_.HasAnyOfCapabilities({SpvCapabilityImageMipmap})); +} + +TEST_F(ValidationState_HasAnyOfCapabilities, MultiCapMask) { + const auto set1 = + CapabilitySet{SpvCapabilitySampledRect, SpvCapabilityImageBuffer}; + const auto set2 = CapabilitySet{SpvCapabilityStorageImageWriteWithoutFormat, + SpvCapabilityStorageImageReadWithoutFormat, + SpvCapabilityGeometryStreams}; + EXPECT_FALSE(state_.HasAnyOfCapabilities(set1)); + EXPECT_FALSE(state_.HasAnyOfCapabilities(set2)); + state_.RegisterCapability(SpvCapabilityImageBuffer); + EXPECT_TRUE(state_.HasAnyOfCapabilities(set1)); + EXPECT_FALSE(state_.HasAnyOfCapabilities(set2)); +} + +// A test of ValidationState_t::HasAnyOfExtensions(). +using ValidationState_HasAnyOfExtensions = ValidationStateTest; + +TEST_F(ValidationState_HasAnyOfExtensions, EmptyMask) { + EXPECT_TRUE(state_.HasAnyOfExtensions({})); + state_.RegisterExtension(Extension::kSPV_KHR_shader_ballot); + EXPECT_TRUE(state_.HasAnyOfExtensions({})); + state_.RegisterExtension(Extension::kSPV_KHR_16bit_storage); + EXPECT_TRUE(state_.HasAnyOfExtensions({})); + state_.RegisterExtension(Extension::kSPV_NV_viewport_array2); + EXPECT_TRUE(state_.HasAnyOfExtensions({})); +} + +TEST_F(ValidationState_HasAnyOfExtensions, SingleCapMask) { + EXPECT_FALSE(state_.HasAnyOfExtensions({Extension::kSPV_KHR_shader_ballot})); + EXPECT_FALSE(state_.HasAnyOfExtensions({Extension::kSPV_KHR_16bit_storage})); + state_.RegisterExtension(Extension::kSPV_KHR_shader_ballot); + EXPECT_TRUE(state_.HasAnyOfExtensions({Extension::kSPV_KHR_shader_ballot})); + EXPECT_FALSE(state_.HasAnyOfExtensions({Extension::kSPV_KHR_16bit_storage})); + state_.RegisterExtension(Extension::kSPV_KHR_16bit_storage); + EXPECT_TRUE(state_.HasAnyOfExtensions({Extension::kSPV_KHR_shader_ballot})); + EXPECT_TRUE(state_.HasAnyOfExtensions({Extension::kSPV_KHR_16bit_storage})); +} + +TEST_F(ValidationState_HasAnyOfExtensions, MultiCapMask) { + const auto set1 = ExtensionSet{Extension::kSPV_KHR_multiview, + Extension::kSPV_KHR_16bit_storage}; + const auto set2 = ExtensionSet{Extension::kSPV_KHR_shader_draw_parameters, + Extension::kSPV_NV_stereo_view_rendering, + Extension::kSPV_KHR_shader_ballot}; + EXPECT_FALSE(state_.HasAnyOfExtensions(set1)); + EXPECT_FALSE(state_.HasAnyOfExtensions(set2)); + state_.RegisterExtension(Extension::kSPV_KHR_multiview); + EXPECT_TRUE(state_.HasAnyOfExtensions(set1)); + EXPECT_FALSE(state_.HasAnyOfExtensions(set2)); +} + +// A test of ValidationState_t::IsOpcodeInCurrentLayoutSection(). +using ValidationState_InLayoutState = ValidationStateTest; + +TEST_F(ValidationState_InLayoutState, Variable) { + state_.SetCurrentLayoutSectionForTesting(kLayoutTypes); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpVariable)); + + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpVariable)); +} + +TEST_F(ValidationState_InLayoutState, ExtInst) { + state_.SetCurrentLayoutSectionForTesting(kLayoutTypes); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpExtInst)); + + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpExtInst)); +} + +TEST_F(ValidationState_InLayoutState, Undef) { + state_.SetCurrentLayoutSectionForTesting(kLayoutTypes); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpUndef)); + + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpUndef)); +} + +TEST_F(ValidationState_InLayoutState, Function) { + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDeclarations); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunction)); + + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunction)); +} + +TEST_F(ValidationState_InLayoutState, FunctionParameter) { + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDeclarations); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunctionParameter)); + + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunctionParameter)); +} + +TEST_F(ValidationState_InLayoutState, FunctionEnd) { + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDeclarations); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunctionEnd)); + + state_.SetCurrentLayoutSectionForTesting(kLayoutFunctionDefinitions); + EXPECT_TRUE(state_.IsOpcodeInCurrentLayoutSection(SpvOpFunctionEnd)); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_storage_test.cpp b/third_party/spirv-tools/test/val/val_storage_test.cpp new file mode 100644 index 0000000..fe37a93 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_storage_test.cpp @@ -0,0 +1,319 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for OpVariable storage class + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Values; +using ValidateStorage = spvtest::ValidateBase; +using ValidateStorageClass = + spvtest::ValidateBase>; + +TEST_F(ValidateStorage, FunctionStorageInsideFunction) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%ptrt = OpTypePointer Function %intt +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel +%var = OpVariable %ptrt Function + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateStorage, FunctionStorageOutsideFunction) { + char str[] = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%ptrt = OpTypePointer Function %intt +%var = OpVariable %ptrt Function +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Variables can not have a function[7] storage class " + "outside of a function")); +} + +TEST_F(ValidateStorage, OtherStorageOutsideFunction) { + char str[] = R"( + OpCapability Shader + OpCapability Kernel + OpCapability AtomicStorage + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%intt = OpTypeInt 32 0 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%uniconptrt = OpTypePointer UniformConstant %intt +%unicon = OpVariable %uniconptrt UniformConstant +%inputptrt = OpTypePointer Input %intt +%input = OpVariable %inputptrt Input +%unifptrt = OpTypePointer Uniform %intt +%unif = OpVariable %unifptrt Uniform +%outputptrt = OpTypePointer Output %intt +%output = OpVariable %outputptrt Output +%wgroupptrt = OpTypePointer Workgroup %intt +%wgroup = OpVariable %wgroupptrt Workgroup +%xwgrpptrt = OpTypePointer CrossWorkgroup %intt +%xwgrp = OpVariable %xwgrpptrt CrossWorkgroup +%privptrt = OpTypePointer Private %intt +%priv = OpVariable %privptrt Private +%pushcoptrt = OpTypePointer PushConstant %intt +%pushco = OpVariable %pushcoptrt PushConstant +%atomcptrt = OpTypePointer AtomicCounter %intt +%atomct = OpVariable %atomcptrt AtomicCounter +%imageptrt = OpTypePointer Image %intt +%image = OpVariable %imageptrt Image +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(str); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +// clang-format off +TEST_P(ValidateStorage, OtherStorageInsideFunction) { + std::stringstream ss; + ss << R"( + OpCapability Shader + OpCapability Kernel + OpCapability AtomicStorage + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%intt = OpTypeInt 32 0 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%ptrt = OpTypePointer Function %intt +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel +%var = OpVariable %ptrt )" << GetParam() << R"( + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(ss.str()); + ASSERT_EQ(SPV_ERROR_INVALID_LAYOUT, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr( + "Variables must have a function[7] storage class inside of a function")); +} + +INSTANTIATE_TEST_SUITE_P(MatrixOp, ValidateStorage, + ::testing::Values( + "Input", + "Uniform", + "Output", + "Workgroup", + "CrossWorkgroup", + "Private", + "PushConstant", + "AtomicCounter", + "Image")); +// clang-format on + +TEST_F(ValidateStorage, GenericVariableOutsideFunction) { + const auto str = R"( + OpCapability Kernel + OpCapability Linkage + OpCapability GenericPointer + OpMemoryModel Logical OpenCL +%intt = OpTypeInt 32 0 +%ptrt = OpTypePointer Function %intt +%var = OpVariable %ptrt Generic +)"; + CompileSuccessfully(str); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpVariable storage class cannot be Generic")); +} + +TEST_F(ValidateStorage, GenericVariableInsideFunction) { + const auto str = R"( + OpCapability Shader + OpCapability Linkage + OpCapability GenericPointer + OpMemoryModel Logical GLSL450 +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%ptrt = OpTypePointer Function %intt +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel +%var = OpVariable %ptrt Generic + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpVariable storage class cannot be Generic")); +} + +TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParam) { + const auto str = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%ptrt = OpTypePointer Function %intt +%vfunct = OpTypeFunction %voidt +%vifunct = OpTypeFunction %voidt %ptrt +%wgroupptrt = OpTypePointer Workgroup %intt +%wgroup = OpVariable %wgroupptrt Workgroup +%main = OpFunction %voidt None %vfunct +%mainl = OpLabel +%ret = OpFunctionCall %voidt %func %wgroup + OpReturn + OpFunctionEnd +%func = OpFunction %voidt None %vifunct +%arg = OpFunctionParameter %ptrt +%funcl = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + getValidatorOptions()->before_hlsl_legalization = true; + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateStorage, RelaxedLogicalPointerFunctionParamBad) { + const auto str = R"( + OpCapability Shader + OpCapability Linkage + OpMemoryModel Logical GLSL450 +%floatt = OpTypeFloat 32 +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%ptrt = OpTypePointer Function %intt +%vfunct = OpTypeFunction %voidt +%vifunct = OpTypeFunction %voidt %ptrt +%wgroupptrt = OpTypePointer Workgroup %floatt +%wgroup = OpVariable %wgroupptrt Workgroup +%main = OpFunction %voidt None %vfunct +%mainl = OpLabel +%ret = OpFunctionCall %voidt %func %wgroup + OpReturn + OpFunctionEnd +%func = OpFunction %voidt None %vifunct +%arg = OpFunctionParameter %ptrt +%funcl = OpLabel + OpReturn + OpFunctionEnd +)"; + CompileSuccessfully(str); + getValidatorOptions()->relax_logical_pointer = true; + ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("OpFunctionCall Argument '")); +} + +std::string GetVarDeclStr(const std::string& storage_class) { + if (storage_class != "Output" && storage_class != "Private" && + storage_class != "Function") { + return "%var = OpVariable %ptrt " + storage_class + "\n"; + } else { + return "%var = OpVariable %ptrt " + storage_class + " %null\n"; + } +} + +TEST_P(ValidateStorageClass, WebGPU) { + std::string storage_class = std::get<0>(GetParam()); + bool is_local = std::get<1>(GetParam()); + bool is_valid = std::get<2>(GetParam()); + std::string error = std::get<3>(GetParam()); + + std::string str = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Fragment %func "func" + OpExecutionMode %func OriginUpperLeft +%intt = OpTypeInt 32 1 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%null = OpConstantNull %intt +)"; + str += "%ptrt = OpTypePointer " + storage_class + " %intt\n"; + if (!is_local) str += GetVarDeclStr(storage_class); + str += R"( +%func = OpFunction %voidt None %vfunct +%funcl = OpLabel +)"; + if (is_local) str += GetVarDeclStr(storage_class); + str += R"( +OpReturn +OpFunctionEnd +)"; + + CompileSuccessfully(str, SPV_ENV_WEBGPU_0); + if (is_valid) { + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); + } else { + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), HasSubstr(error)); + } +} + +INSTANTIATE_TEST_SUITE_P( + StorageClass, ValidateStorageClass, + Values(std::make_tuple("UniformConstant", false, true, ""), + std::make_tuple("Uniform", false, true, ""), + std::make_tuple("StorageBuffer", false, true, ""), + std::make_tuple("Input", false, true, ""), + std::make_tuple("Output", false, true, ""), + std::make_tuple("Image", false, true, ""), + std::make_tuple("Workgroup", false, true, ""), + std::make_tuple("Private", false, true, ""), + std::make_tuple("Function", true, true, ""), + std::make_tuple("CrossWorkgroup", false, false, + "Invalid storage class for target environment"), + std::make_tuple("PushConstant", false, false, + "Invalid storage class for target environment"))); + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_type_unique_test.cpp b/third_party/spirv-tools/test/val/val_type_unique_test.cpp new file mode 100644 index 0000000..45a4d50 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_type_unique_test.cpp @@ -0,0 +1,271 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for unique type declaration rules validator. + +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; +using ::testing::Not; + +using ValidateTypeUnique = spvtest::ValidateBase; + +const spv_result_t kDuplicateTypeError = SPV_ERROR_INVALID_DATA; + +const std::string& GetHeader() { + static const std::string header = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%floatt = OpTypeFloat 32 +%vec2t = OpTypeVector %floatt 2 +%vec3t = OpTypeVector %floatt 3 +%vec4t = OpTypeVector %floatt 4 +%mat22t = OpTypeMatrix %vec2t 2 +%mat33t = OpTypeMatrix %vec3t 3 +%mat44t = OpTypeMatrix %vec4t 4 +%intt = OpTypeInt 32 1 +%uintt = OpTypeInt 32 0 +%num3 = OpConstant %uintt 3 +%const3 = OpConstant %uintt 3 +%val3 = OpConstant %uintt 3 +%array = OpTypeArray %vec3t %num3 +%struct = OpTypeStruct %floatt %floatt %vec3t +%boolt = OpTypeBool +%array2 = OpTypeArray %vec3t %num3 +%voidt = OpTypeVoid +%vfunct = OpTypeFunction %voidt +%struct2 = OpTypeStruct %floatt %floatt %vec3t +%false = OpConstantFalse %boolt +%true = OpConstantTrue %boolt +%runtime_arrayt = OpTypeRuntimeArray %floatt +%runtime_arrayt2 = OpTypeRuntimeArray %floatt +)"; + + return header; +} + +const std::string& GetBody() { + static const std::string body = R"( +%main = OpFunction %voidt None %vfunct +%mainl = OpLabel +%a = OpIAdd %uintt %const3 %val3 +%b = OpIAdd %uintt %const3 %val3 +OpSelectionMerge %endl None +OpBranchConditional %true %truel %falsel +%truel = OpLabel +%add1 = OpIAdd %uintt %a %b +%add2 = OpIAdd %uintt %a %b +OpBranch %endl +%falsel = OpLabel +%sub1 = OpISub %uintt %a %b +%sub2 = OpISub %uintt %a %b +OpBranch %endl +%endl = OpLabel +OpReturn +OpFunctionEnd +)"; + + return body; +} + +// Returns expected error string if |opcode| produces a duplicate type +// declaration. +std::string GetErrorString(SpvOp opcode) { + return "Duplicate non-aggregate type declarations are not allowed. Opcode: " + + std::string(spvOpcodeString(opcode)); +} + +TEST_F(ValidateTypeUnique, success) { + std::string str = GetHeader() + GetBody(); + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateTypeUnique, duplicate_void) { + std::string str = GetHeader() + R"( +%boolt2 = OpTypeVoid +)" + GetBody(); + CompileSuccessfully(str.c_str()); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(SpvOpTypeVoid))); +} + +TEST_F(ValidateTypeUnique, duplicate_bool) { + std::string str = GetHeader() + R"( +%boolt2 = OpTypeBool +)" + GetBody(); + CompileSuccessfully(str.c_str()); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(SpvOpTypeBool))); +} + +TEST_F(ValidateTypeUnique, duplicate_int) { + std::string str = GetHeader() + R"( +%uintt2 = OpTypeInt 32 0 +)" + GetBody(); + CompileSuccessfully(str.c_str()); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(SpvOpTypeInt))); +} + +TEST_F(ValidateTypeUnique, duplicate_float) { + std::string str = GetHeader() + R"( +%floatt2 = OpTypeFloat 32 +)" + GetBody(); + CompileSuccessfully(str.c_str()); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), HasSubstr(GetErrorString(SpvOpTypeFloat))); +} + +TEST_F(ValidateTypeUnique, duplicate_vec3) { + std::string str = GetHeader() + R"( +%vec3t2 = OpTypeVector %floatt 3 +)" + GetBody(); + CompileSuccessfully(str.c_str()); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(GetErrorString(SpvOpTypeVector))); +} + +TEST_F(ValidateTypeUnique, duplicate_mat33) { + std::string str = GetHeader() + R"( +%mat33t2 = OpTypeMatrix %vec3t 3 +)" + GetBody(); + CompileSuccessfully(str.c_str()); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(GetErrorString(SpvOpTypeMatrix))); +} + +TEST_F(ValidateTypeUnique, duplicate_vfunc) { + std::string str = GetHeader() + R"( +%vfunct2 = OpTypeFunction %voidt +)" + GetBody(); + CompileSuccessfully(str.c_str()); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(GetErrorString(SpvOpTypeFunction))); +} + +TEST_F(ValidateTypeUnique, duplicate_pipe_storage) { + std::string str = R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability Pipes +OpCapability PipeStorage +OpMemoryModel Physical32 OpenCL +%ps = OpTypePipeStorage +%ps2 = OpTypePipeStorage +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(GetErrorString(SpvOpTypePipeStorage))); +} + +TEST_F(ValidateTypeUnique, duplicate_named_barrier) { + std::string str = R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability NamedBarrier +OpMemoryModel Physical32 OpenCL +%nb = OpTypeNamedBarrier +%nb2 = OpTypeNamedBarrier +)"; + CompileSuccessfully(str.c_str(), SPV_ENV_UNIVERSAL_1_1); + ASSERT_EQ(kDuplicateTypeError, ValidateInstructions(SPV_ENV_UNIVERSAL_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr(GetErrorString(SpvOpTypeNamedBarrier))); +} + +TEST_F(ValidateTypeUnique, duplicate_forward_pointer) { + std::string str = R"( +OpCapability Addresses +OpCapability Kernel +OpCapability GenericPointer +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +OpTypeForwardPointer %ptr Generic +OpTypeForwardPointer %ptr2 Generic +%intt = OpTypeInt 32 0 +%int_struct = OpTypeStruct %intt +%floatt = OpTypeFloat 32 +%ptr = OpTypePointer Generic %int_struct +%float_struct = OpTypeStruct %floatt +%ptr2 = OpTypePointer Generic %float_struct +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateTypeUnique, duplicate_void_with_extension) { + std::string str = R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpCapability Pipes +OpExtension "SPV_VALIDATOR_ignore_type_decl_unique" +OpMemoryModel Physical32 OpenCL +%voidt = OpTypeVoid +%voidt2 = OpTypeVoid +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + Not(HasSubstr(GetErrorString(SpvOpTypeVoid)))); +} + +TEST_F(ValidateTypeUnique, DuplicatePointerTypesNoExtension) { + std::string str = R"( +OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +%u32 = OpTypeInt 32 0 +%ptr1 = OpTypePointer Input %u32 +%ptr2 = OpTypePointer Input %u32 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); +} + +TEST_F(ValidateTypeUnique, DuplicatePointerTypesWithExtension) { + std::string str = R"( +OpCapability Shader +OpCapability Linkage +OpExtension "SPV_KHR_variable_pointers" +OpMemoryModel Logical GLSL450 +%u32 = OpTypeInt 32 0 +%ptr1 = OpTypePointer Input %u32 +%ptr2 = OpTypePointer Input %u32 +)"; + CompileSuccessfully(str.c_str()); + ASSERT_EQ(SPV_SUCCESS, ValidateInstructions()); + EXPECT_THAT(getDiagnosticString(), + Not(HasSubstr(GetErrorString(SpvOpTypePointer)))); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_validation_state_test.cpp b/third_party/spirv-tools/test/val/val_validation_state_test.cpp new file mode 100644 index 0000000..4581579 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_validation_state_test.cpp @@ -0,0 +1,361 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Basic tests for the ValidationState_t datastructure. + +#include + +#include "gmock/gmock.h" +#include "source/spirv_validator_options.h" +#include "test/unit_spirv.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidationStateTest = spvtest::ValidateBase; + +const char kHeader[] = + " OpCapability Shader" + " OpCapability Linkage" + " OpMemoryModel Logical GLSL450 "; + +const char kVulkanMemoryHeader[] = + " OpCapability Shader" + " OpCapability VulkanMemoryModelKHR" + " OpExtension \"SPV_KHR_vulkan_memory_model\"" + " OpMemoryModel Logical VulkanKHR "; + +const char kVoidFVoid[] = + " %void = OpTypeVoid" + " %void_f = OpTypeFunction %void" + " %func = OpFunction %void None %void_f" + " %label = OpLabel" + " OpReturn" + " OpFunctionEnd "; + +// k*RecursiveBody examples originally from test/opt/function_test.cpp +const char* kNonRecursiveBody = R"( +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%null = OpConstantNull %_struct_6 +%7 = OpTypeFunction %_struct_6 +%12 = OpFunction %_struct_6 None %7 +%13 = OpLabel +OpReturnValue %null +OpFunctionEnd +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %12 +OpReturnValue %null +OpFunctionEnd +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +)"; + +const char* kDirectlyRecursiveBody = R"( +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%7 = OpTypeFunction %_struct_6 +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpReturn +OpFunctionEnd +)"; + +const char* kIndirectlyRecursiveBody = R"( +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_struct_6 = OpTypeStruct %float %float +%null = OpConstantNull %_struct_6 +%7 = OpTypeFunction %_struct_6 +%9 = OpFunction %_struct_6 None %7 +%10 = OpLabel +%11 = OpFunctionCall %_struct_6 %12 +OpReturnValue %null +OpFunctionEnd +%12 = OpFunction %_struct_6 None %7 +%13 = OpLabel +%14 = OpFunctionCall %_struct_6 %9 +OpReturnValue %null +OpFunctionEnd +%1 = OpFunction %void Pure|Const %4 +%8 = OpLabel +%2 = OpFunctionCall %_struct_6 %9 +OpKill +OpFunctionEnd +)"; + +// Tests that the instruction count in ValidationState is correct. +TEST_F(ValidationStateTest, CheckNumInstructions) { + std::string spirv = std::string(kHeader) + "%int = OpTypeInt 32 0"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_EQ(size_t(4), vstate_->ordered_instructions().size()); +} + +// Tests that the number of global variables in ValidationState is correct. +TEST_F(ValidationStateTest, CheckNumGlobalVars) { + std::string spirv = std::string(kHeader) + R"( + %int = OpTypeInt 32 0 +%_ptr_int = OpTypePointer Input %int + %var_1 = OpVariable %_ptr_int Input + %var_2 = OpVariable %_ptr_int Input + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_EQ(unsigned(2), vstate_->num_global_vars()); +} + +// Tests that the number of local variables in ValidationState is correct. +TEST_F(ValidationStateTest, CheckNumLocalVars) { + std::string spirv = std::string(kHeader) + R"( + %int = OpTypeInt 32 0 + %_ptr_int = OpTypePointer Function %int + %voidt = OpTypeVoid + %funct = OpTypeFunction %voidt + %main = OpFunction %voidt None %funct + %entry = OpLabel + %var_1 = OpVariable %_ptr_int Function + %var_2 = OpVariable %_ptr_int Function + %var_3 = OpVariable %_ptr_int Function + OpReturn + OpFunctionEnd + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_EQ(unsigned(3), vstate_->num_local_vars()); +} + +// Tests that the "id bound" in ValidationState is correct. +TEST_F(ValidationStateTest, CheckIdBound) { + std::string spirv = std::string(kHeader) + R"( + %int = OpTypeInt 32 0 + %voidt = OpTypeVoid + )"; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_EQ(unsigned(3), vstate_->getIdBound()); +} + +// Tests that the entry_points in ValidationState is correct. +TEST_F(ValidationStateTest, CheckEntryPoints) { + std::string spirv = std::string(kHeader) + + " OpEntryPoint Vertex %func \"shader\"" + + std::string(kVoidFVoid); + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); + EXPECT_EQ(size_t(1), vstate_->entry_points().size()); + EXPECT_EQ(SpvOpFunction, + vstate_->FindDef(vstate_->entry_points()[0])->opcode()); +} + +TEST_F(ValidationStateTest, CheckStructMemberLimitOption) { + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_struct_members, 32000u); + EXPECT_EQ(32000u, options_->universal_limits_.max_struct_members); +} + +TEST_F(ValidationStateTest, CheckNumGlobalVarsLimitOption) { + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_global_variables, 100u); + EXPECT_EQ(100u, options_->universal_limits_.max_global_variables); +} + +TEST_F(ValidationStateTest, CheckNumLocalVarsLimitOption) { + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_local_variables, 100u); + EXPECT_EQ(100u, options_->universal_limits_.max_local_variables); +} + +TEST_F(ValidationStateTest, CheckStructDepthLimitOption) { + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_struct_depth, 100u); + EXPECT_EQ(100u, options_->universal_limits_.max_struct_depth); +} + +TEST_F(ValidationStateTest, CheckSwitchBranchesLimitOption) { + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_switch_branches, 100u); + EXPECT_EQ(100u, options_->universal_limits_.max_switch_branches); +} + +TEST_F(ValidationStateTest, CheckFunctionArgsLimitOption) { + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_function_args, 100u); + EXPECT_EQ(100u, options_->universal_limits_.max_function_args); +} + +TEST_F(ValidationStateTest, CheckCFGDepthLimitOption) { + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_control_flow_nesting_depth, 100u); + EXPECT_EQ(100u, options_->universal_limits_.max_control_flow_nesting_depth); +} + +TEST_F(ValidationStateTest, CheckAccessChainIndexesLimitOption) { + spvValidatorOptionsSetUniversalLimit( + options_, spv_validator_limit_max_access_chain_indexes, 100u); + EXPECT_EQ(100u, options_->universal_limits_.max_access_chain_indexes); +} + +TEST_F(ValidationStateTest, CheckNonRecursiveBodyGood) { + std::string spirv = std::string(kHeader) + kNonRecursiveBody; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidationStateTest, CheckVulkanNonRecursiveBodyGood) { + std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_SUCCESS, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); +} + +TEST_F(ValidationStateTest, CheckWebGPUNonRecursiveBodyGood) { + std::string spirv = std::string(kVulkanMemoryHeader) + kNonRecursiveBody; + CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidationStateTest, CheckDirectlyRecursiveBodyGood) { + std::string spirv = std::string(kHeader) + kDirectlyRecursiveBody; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidationStateTest, CheckVulkanDirectlyRecursiveBodyBad) { + std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry points may not have a call graph with cycles.\n " + " %1 = OpFunction %void Pure|Const %3\n")); +} + +TEST_F(ValidationStateTest, CheckWebGPUDirectlyRecursiveBodyBad) { + std::string spirv = std::string(kVulkanMemoryHeader) + kDirectlyRecursiveBody; + CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry points may not have a call graph with cycles.\n " + " %1 = OpFunction %void Pure|Const %3\n")); +} + +TEST_F(ValidationStateTest, CheckIndirectlyRecursiveBodyGood) { + std::string spirv = std::string(kHeader) + kIndirectlyRecursiveBody; + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateAndRetrieveValidationState()); +} + +TEST_F(ValidationStateTest, CheckVulkanIndirectlyRecursiveBodyBad) { + std::string spirv = + std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody; + CompileSuccessfully(spirv, SPV_ENV_VULKAN_1_1); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + ValidateAndRetrieveValidationState(SPV_ENV_VULKAN_1_1)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Entry points may not have a call graph with cycles.\n " + " %1 = OpFunction %void Pure|Const %3\n")); +} + +// Indirectly recursive functions are caught by the function definition layout +// rules, because they cause a situation where there are 2 functions that have +// to be before each other, and layout is checked earlier. +TEST_F(ValidationStateTest, CheckWebGPUIndirectlyRecursiveBodyBad) { + std::string spirv = + std::string(kVulkanMemoryHeader) + kIndirectlyRecursiveBody; + CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, + ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, functions need to be defined before being " + "called.\n %10 = OpFunctionCall %_struct_5 %11\n")); +} + +TEST_F(ValidationStateTest, + CheckWebGPUDuplicateEntryNamesDifferentFunctionsBad) { + std::string spirv = std::string(kVulkanMemoryHeader) + R"( +OpEntryPoint Fragment %func_1 "main" +OpEntryPoint Vertex %func_2 "main" +OpExecutionMode %func_1 OriginUpperLeft +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func_1 = OpFunction %void None %void_f +%label_1 = OpLabel + OpReturn + OpFunctionEnd +%func_2 = OpFunction %void None %void_f +%label_2 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Entry point name \"main\" is not unique, which is not allow " + "in WebGPU env.\n %1 = OpFunction %void None %4\n")); +} + +TEST_F(ValidationStateTest, CheckWebGPUDuplicateEntryNamesSameFunctionBad) { + std::string spirv = std::string(kVulkanMemoryHeader) + R"( +OpEntryPoint GLCompute %func_1 "main" +OpEntryPoint Vertex %func_1 "main" +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func_1 = OpFunction %void None %void_f +%label_1 = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv, SPV_ENV_WEBGPU_0); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + ValidateAndRetrieveValidationState(SPV_ENV_WEBGPU_0)); + EXPECT_THAT( + getDiagnosticString(), + HasSubstr("Entry point name \"main\" is not unique, which is not allow " + "in WebGPU env.\n %1 = OpFunction %void None %3\n")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_version_test.cpp b/third_party/spirv-tools/test/val/val_version_test.cpp new file mode 100644 index 0000000..2b9542a --- /dev/null +++ b/third_party/spirv-tools/test/val/val_version_test.cpp @@ -0,0 +1,300 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using ::testing::HasSubstr; + +using ValidateVersion = spvtest::ValidateBase< + std::tuple>; + +const std::string vulkan_spirv = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + +const std::string webgpu_spirv = R"( +OpCapability Shader +OpCapability VulkanMemoryModelKHR +OpExtension "SPV_KHR_vulkan_memory_model" +OpMemoryModel Logical VulkanKHR +OpEntryPoint Fragment %func "func" +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%1 = OpLabel +OpReturn +OpFunctionEnd +)"; + +const std::string opencl_spirv = R"( +OpCapability Addresses +OpCapability Kernel +OpCapability Linkage +OpMemoryModel Physical32 OpenCL +)"; + +std::string version(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + return "1.0"; + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + return "1.1"; + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + return "1.2"; + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_WEBGPU_0: + return "1.3"; + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + return "1.4"; + default: + return "0"; + } +} + +TEST_P(ValidateVersion, version) { + CompileSuccessfully(std::get<2>(GetParam()), std::get<0>(GetParam())); + spv_result_t res = ValidateInstructions(std::get<1>(GetParam())); + if (std::get<3>(GetParam())) { + ASSERT_EQ(SPV_SUCCESS, res); + } else { + ASSERT_EQ(SPV_ERROR_WRONG_VERSION, res); + + std::string msg = "Invalid SPIR-V binary version "; + msg += version(std::get<0>(GetParam())); + msg += " for target environment "; + msg += spvTargetEnvDescription(std::get<1>(GetParam())); + EXPECT_THAT(getDiagnosticString(), HasSubstr(msg)); + } +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(Universal, ValidateVersion, + ::testing::Values( + // Binary version, Target environment + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_WEBGPU_0, webgpu_spirv, true), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_1, SPV_ENV_WEBGPU_0, webgpu_spirv, true), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_WEBGPU_0, webgpu_spirv, true), + + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_UNIVERSAL_1_3, SPV_ENV_WEBGPU_0, webgpu_spirv, true) + ) +); + +INSTANTIATE_TEST_SUITE_P(Vulkan, ValidateVersion, + ::testing::Values( + // Binary version, Target environment + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_0, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_0, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_2, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_OPENGL_4_5, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_0, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true), + + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_UNIVERSAL_1_3, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1, vulkan_spirv, true), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_0, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_1, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_2, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_3, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_OPENGL_4_5, vulkan_spirv, false), + std::make_tuple(SPV_ENV_VULKAN_1_1, SPV_ENV_VULKAN_1_1_SPIRV_1_4, vulkan_spirv, true) + ) +); + +INSTANTIATE_TEST_SUITE_P(OpenCL, ValidateVersion, + ::testing::Values( + // Binary version, Target environment + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_EMBEDDED_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_EMBEDDED_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_EMBEDDED_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_0, SPV_ENV_OPENCL_1_2, opencl_spirv, true), + + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_EMBEDDED_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_EMBEDDED_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_EMBEDDED_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_1, SPV_ENV_OPENCL_1_2, opencl_spirv, true), + + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_0, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_0, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_1, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_EMBEDDED_2_0, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_EMBEDDED_2_1, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_EMBEDDED_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_2_2, SPV_ENV_OPENCL_1_2, opencl_spirv, false), + + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_EMBEDDED_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_EMBEDDED_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_EMBEDDED_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_1_2, SPV_ENV_OPENCL_1_2, opencl_spirv, true) + ) +); + +INSTANTIATE_TEST_SUITE_P(OpenCLEmbedded, ValidateVersion, + ::testing::Values( + // Binary version, Target environment + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_UNIVERSAL_1_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_OPENCL_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_OPENCL_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_OPENCL_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_OPENCL_EMBEDDED_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_OPENCL_EMBEDDED_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_OPENCL_EMBEDDED_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_0, SPV_ENV_OPENCL_1_2, opencl_spirv, true), + + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_UNIVERSAL_1_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_EMBEDDED_2_0, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_EMBEDDED_2_1, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_EMBEDDED_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_1, SPV_ENV_OPENCL_1_2, opencl_spirv, true), + + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_UNIVERSAL_1_0, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_UNIVERSAL_1_1, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_UNIVERSAL_1_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_UNIVERSAL_1_3, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_OPENCL_2_0, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_OPENCL_2_1, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_OPENCL_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_OPENCL_EMBEDDED_2_0, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_OPENCL_EMBEDDED_2_1, opencl_spirv, false), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_OPENCL_EMBEDDED_2_2, opencl_spirv, true), + std::make_tuple(SPV_ENV_OPENCL_EMBEDDED_2_2, SPV_ENV_OPENCL_1_2, opencl_spirv, false) + ) +); +// clang-format on + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/test/val/val_webgpu_test.cpp b/third_party/spirv-tools/test/val/val_webgpu_test.cpp new file mode 100644 index 0000000..62fa6a7 --- /dev/null +++ b/third_party/spirv-tools/test/val/val_webgpu_test.cpp @@ -0,0 +1,424 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validation tests for WebGPU env specific checks + +#include + +#include "gmock/gmock.h" +#include "test/val/val_fixtures.h" + +namespace spvtools { +namespace val { +namespace { + +using testing::HasSubstr; + +using ValidateWebGPU = spvtest::ValidateBase; + +TEST_F(ValidateWebGPU, OpUndefIsDisallowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" +%float = OpTypeFloat 32 +%1 = OpUndef %float +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func = OpFunction %void None %void_f +%label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + // Control case: OpUndef is allowed in SPIR-V 1.3 + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3)); + + // Control case: OpUndef is disallowed in the WebGPU env + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed")); +} + +TEST_F(ValidateWebGPU, OpNameIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpName %1 "foo" + %1 = OpTypeFloat 32 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpMemberName %2 0 "foo" + %1 = OpTypeFloat 32 + %2 = OpTypeStruct %1 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd + +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpSourceIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSource GLSL 450 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSource GLSL 450 + OpSourceContinued "I am a happy shader! Yay! ;" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpSourceExtension "bar" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpStringIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + %1 = OpString "foo" + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpLineIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + %1 = OpString "minimal.vert" + OpLine %1 1 1 + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, OpNoLineIsAllowed) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" + OpNoLine + %void = OpTypeVoid + %void_f = OpTypeFunction %void + %func = OpFunction %void None %void_f + %label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func = OpFunction %void None %void_f +%label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, LogicalAddressingGLSL450MemoryGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %func "shader" +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func = OpFunction %void None %void_f +%label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, LogicalAddressingSimpleMemoryGood) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Vertex %func "shader" +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func = OpFunction %void None %void_f +%label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, KernelIsBad) { + std::string spirv = R"( + OpCapability Kernel + OpMemoryModel Logical Simple + OpNoLine +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Capability Kernel is not allowed by WebGPU " + "specification (or requires extension)")); +} + +TEST_F(ValidateWebGPU, OpenCLMemoryModelBad) { + std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical OpenCL + OpNoLine +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_CAPABILITY, + ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("Operand 2 of MemoryModel requires one of these " + "capabilities: Kernel")); +} + +TEST_F(ValidateWebGPU, AllowListedExtendedInstructionsImportGood) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" +%1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical VulkanKHR + OpEntryPoint Vertex %func "shader" +%void = OpTypeVoid +%void_f = OpTypeFunction %void +%func = OpFunction %void None %void_f +%label = OpLabel + OpReturn + OpFunctionEnd +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, NonAllowListedExtendedInstructionsImportBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_vulkan_memory_model" +%1 = OpExtInstImport "OpenCL.std" + OpMemoryModel Logical VulkanKHR +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, the only valid parameter to " + "OpExtInstImport is \"GLSL.std.450\".\n %1 = " + "OpExtInstImport \"OpenCL.std\"\n")); +} + +TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelExtensionBad) { + std::string spirv = R"( + OpCapability Shader + OpCapability VulkanMemoryModelKHR + OpExtension "SPV_KHR_8bit_storage" + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical VulkanKHR +)"; + + CompileSuccessfully(spirv); + + EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("For WebGPU, the only valid parameter to OpExtension " + "is \"SPV_KHR_vulkan_memory_model\".\n OpExtension " + "\"SPV_KHR_8bit_storage\"\n")); +} + +spv_binary GenerateTrivialBinary(bool need_little_endian) { + // Smallest possible valid WebGPU SPIR-V binary in little endian. Contains all + // the required boilerplate and a trivial entry point function. + static const uint8_t binary_bytes[] = { + // clang-format off + 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0xE1, 0x14, 0x00, 0x00, + 0x0A, 0x00, 0x08, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F, + 0x76, 0x75, 0x6C, 0x6B, 0x61, 0x6E, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72, + 0x79, 0x5F, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x00, 0x0E, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64, + 0x65, 0x72, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, + 0x04, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00 + // clang-format on + }; + static const size_t word_count = sizeof(binary_bytes) / sizeof(uint32_t); + std::unique_ptr result(new spv_binary_t); + if (!result) return nullptr; + + result->wordCount = word_count; + result->code = new uint32_t[word_count]; + if (!result->code) return nullptr; + + if (need_little_endian) { + memcpy(result->code, binary_bytes, sizeof(binary_bytes)); + } else { + uint8_t* code_bytes = reinterpret_cast(result->code); + for (size_t word = 0; word < word_count; ++word) { + code_bytes[4 * word] = binary_bytes[4 * word + 3]; + code_bytes[4 * word + 1] = binary_bytes[4 * word + 2]; + code_bytes[4 * word + 2] = binary_bytes[4 * word + 1]; + code_bytes[4 * word + 3] = binary_bytes[4 * word]; + } + } + + return result.release(); +} + +TEST_F(ValidateWebGPU, LittleEndianGood) { + DestroyBinary(); + binary_ = GenerateTrivialBinary(true); + EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0)); +} + +TEST_F(ValidateWebGPU, BigEndianBad) { + DestroyBinary(); + binary_ = GenerateTrivialBinary(false); + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0)); + EXPECT_THAT(getDiagnosticString(), + HasSubstr("WebGPU requires SPIR-V to be little endian.")); +} + +} // namespace +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/tools/CMakeLists.txt b/third_party/spirv-tools/tools/CMakeLists.txt new file mode 100644 index 0000000..124a332 --- /dev/null +++ b/third_party/spirv-tools/tools/CMakeLists.txt @@ -0,0 +1,75 @@ +# Copyright (c) 2015-2016 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if (NOT ${SPIRV_SKIP_EXECUTABLES}) + add_subdirectory(lesspipe) +endif() +add_subdirectory(emacs) + +# Add a SPIR-V Tools command line tool. Signature: +# add_spvtools_tool( +# TARGET target_name +# SRCS src_file1.cpp src_file2.cpp +# LIBS lib_target1 lib_target2 +# ) +function(add_spvtools_tool) + set(one_value_args TARGET) + set(multi_value_args SRCS LIBS) + cmake_parse_arguments( + ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN}) + + add_executable(${ARG_TARGET} ${ARG_SRCS}) + spvtools_default_compile_options(${ARG_TARGET}) + target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS}) + target_include_directories(${ARG_TARGET} PRIVATE + ${spirv-tools_SOURCE_DIR} + ${spirv-tools_BINARY_DIR} + ) + set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools executables") +endfunction() + +if (NOT ${SPIRV_SKIP_EXECUTABLES}) + add_spvtools_tool(TARGET spirv-as SRCS as/as.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-dis SRCS dis/dis.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-val SRCS val/val.cpp util/cli_consumer.cpp LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-opt SRCS opt/opt.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-opt ${SPIRV_TOOLS_FULL_VISIBILITY}) + if (NOT DEFINED IOS_PLATFORM) # iOS does not allow std::system calls which spirv-reduce requires + add_spvtools_tool(TARGET spirv-reduce SRCS reduce/reduce.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-reduce ${SPIRV_TOOLS_FULL_VISIBILITY}) + endif() + add_spvtools_tool(TARGET spirv-link SRCS link/linker.cpp LIBS SPIRV-Tools-link ${SPIRV_TOOLS_FULL_VISIBILITY}) + add_spvtools_tool(TARGET spirv-cfg + SRCS cfg/cfg.cpp + cfg/bin_to_dot.h + cfg/bin_to_dot.cpp + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + target_include_directories(spirv-cfg PRIVATE ${spirv-tools_SOURCE_DIR} + ${SPIRV_HEADER_INCLUDE_DIR}) + set(SPIRV_INSTALL_TARGETS spirv-as spirv-dis spirv-val spirv-opt + spirv-cfg spirv-link) + if(NOT DEFINED IOS_PLATFORM) + set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-reduce) + endif() + + if(SPIRV_BUILD_FUZZER) + add_spvtools_tool(TARGET spirv-fuzz SRCS fuzz/fuzz.cpp util/cli_consumer.cpp LIBS SPIRV-Tools-fuzz ${SPIRV_TOOLS_FULL_VISIBILITY}) + set(SPIRV_INSTALL_TARGETS ${SPIRV_INSTALL_TARGETS} spirv-fuzz) + endif(SPIRV_BUILD_FUZZER) + + if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS ${SPIRV_INSTALL_TARGETS} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif(ENABLE_SPIRV_TOOLS_INSTALL) +endif() diff --git a/third_party/spirv-tools/tools/as/as.cpp b/third_party/spirv-tools/tools/as/as.cpp new file mode 100644 index 0000000..f6e9629 --- /dev/null +++ b/third_party/spirv-tools/tools/as/as.cpp @@ -0,0 +1,154 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/spirv_target_env.h" +#include "spirv-tools/libspirv.h" +#include "tools/io.h" + +void print_usage(char* argv0) { + std::string target_env_list = spvTargetEnvList(19, 80); + printf( + R"(%s - Create a SPIR-V binary module from SPIR-V assembly text + +Usage: %s [options] [] + +The SPIR-V assembly text is read from . If no file is specified, +or if the filename is "-", then the assembly text is read from standard input. +The SPIR-V binary module is written to file "out.spv", unless the -o option +is used. + +Options: + + -h, --help Print this help. + + -o Set the output filename. Use '-' to mean stdout. + --version Display assembler version information. + --preserve-numeric-ids + Numeric IDs in the binary will have the same values as in the + source. Non-numeric IDs are allocated by filling in the gaps, + starting with 1 and going up. + --target-env {%s} + Use specified environment. +)", + argv0, argv0, target_env_list.c_str()); +} + +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; + +int main(int argc, char** argv) { + const char* inFile = nullptr; + const char* outFile = nullptr; + uint32_t options = 0; + spv_target_env target_env = kDefaultEnvironment; + for (int argi = 1; argi < argc; ++argi) { + if ('-' == argv[argi][0]) { + switch (argv[argi][1]) { + case 'h': { + print_usage(argv[0]); + return 0; + } + case 'o': { + if (!outFile && argi + 1 < argc) { + outFile = argv[++argi]; + } else { + print_usage(argv[0]); + return 1; + } + } break; + case 0: { + // Setting a filename of "-" to indicate stdin. + if (!inFile) { + inFile = argv[argi]; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return 1; + } + } break; + case '-': { + // Long options + if (0 == strcmp(argv[argi], "--version")) { + printf("%s\n", spvSoftwareVersionDetailsString()); + printf("Target: %s\n", + spvTargetEnvDescription(kDefaultEnvironment)); + return 0; + } else if (0 == strcmp(argv[argi], "--help")) { + print_usage(argv[0]); + return 0; + } else if (0 == strcmp(argv[argi], "--preserve-numeric-ids")) { + options |= SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS; + } else if (0 == strcmp(argv[argi], "--target-env")) { + if (argi + 1 < argc) { + const auto env_str = argv[++argi]; + if (!spvParseTargetEnv(env_str, &target_env)) { + fprintf(stderr, "error: Unrecognized target env: %s\n", + env_str); + return 1; + } + } else { + fprintf(stderr, "error: Missing argument to --target-env\n"); + return 1; + } + } else { + fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]); + print_usage(argv[0]); + return 1; + } + } break; + default: + fprintf(stderr, "error: Unrecognized option: %s\n\n", argv[argi]); + print_usage(argv[0]); + return 1; + } + } else { + if (!inFile) { + inFile = argv[argi]; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return 1; + } + } + } + + if (!outFile) { + outFile = "out.spv"; + } + + std::vector contents; + if (!ReadFile(inFile, "r", &contents)) return 1; + + spv_binary binary; + spv_diagnostic diagnostic = nullptr; + spv_context context = spvContextCreate(target_env); + spv_result_t error = spvTextToBinaryWithOptions( + context, contents.data(), contents.size(), options, &binary, &diagnostic); + spvContextDestroy(context); + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + return error; + } + + if (!WriteFile(outFile, "wb", binary->code, binary->wordCount)) { + spvBinaryDestroy(binary); + return 1; + } + + spvBinaryDestroy(binary); + + return 0; +} diff --git a/third_party/spirv-tools/tools/cfg/bin_to_dot.cpp b/third_party/spirv-tools/tools/cfg/bin_to_dot.cpp new file mode 100644 index 0000000..2561eea --- /dev/null +++ b/third_party/spirv-tools/tools/cfg/bin_to_dot.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tools/cfg/bin_to_dot.h" + +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/name_mapper.h" + +namespace { + +const char* kMergeStyle = "style=dashed"; +const char* kContinueStyle = "style=dotted"; + +// A DotConverter can be used to dump the GraphViz "dot" graph for +// a SPIR-V module. +class DotConverter { + public: + DotConverter(spvtools::NameMapper name_mapper, std::iostream* out) + : name_mapper_(std::move(name_mapper)), out_(*out) {} + + // Emits the graph preamble. + void Begin() const { + out_ << "digraph {\n"; + // Emit a simple legend + out_ << "legend_merge_src [shape=plaintext, label=\"\"];\n" + << "legend_merge_dest [shape=plaintext, label=\"\"];\n" + << "legend_merge_src -> legend_merge_dest [label=\" merge\"," + << kMergeStyle << "];\n" + << "legend_continue_src [shape=plaintext, label=\"\"];\n" + << "legend_continue_dest [shape=plaintext, label=\"\"];\n" + << "legend_continue_src -> legend_continue_dest [label=\" continue\"," + << kContinueStyle << "];\n"; + } + // Emits the graph postamble. + void End() const { out_ << "}\n"; } + + // Emits the Dot commands for the given instruction. + spv_result_t HandleInstruction(const spv_parsed_instruction_t& inst); + + private: + // Ends processing for the current block, emitting its dot code. + void FlushBlock(const std::vector& successors); + + // The ID of the current functio, or 0 if outside of a function. + uint32_t current_function_id_ = 0; + + // The ID of the current basic block, or 0 if outside of a block. + uint32_t current_block_id_ = 0; + + // Have we completed processing for the entry block to this fuction? + bool seen_function_entry_block_ = false; + + // The Id of the merge block for this block if it exists, or 0 otherwise. + uint32_t merge_ = 0; + // The Id of the continue target block for this block if it exists, or 0 + // otherwise. + uint32_t continue_target_ = 0; + + // An object for mapping Ids to names. + spvtools::NameMapper name_mapper_; + + // The output stream. + std::ostream& out_; +}; + +spv_result_t DotConverter::HandleInstruction( + const spv_parsed_instruction_t& inst) { + switch (inst.opcode) { + case SpvOpFunction: + current_function_id_ = inst.result_id; + seen_function_entry_block_ = false; + break; + case SpvOpFunctionEnd: + current_function_id_ = 0; + break; + + case SpvOpLabel: + current_block_id_ = inst.result_id; + break; + + case SpvOpBranch: + FlushBlock({inst.words[1]}); + break; + case SpvOpBranchConditional: + FlushBlock({inst.words[2], inst.words[3]}); + break; + case SpvOpSwitch: { + std::vector successors{inst.words[2]}; + for (size_t i = 3; i < inst.num_operands; i += 2) { + successors.push_back(inst.words[inst.operands[i].offset]); + } + FlushBlock(successors); + } break; + + case SpvOpKill: + case SpvOpReturn: + case SpvOpUnreachable: + case SpvOpReturnValue: + FlushBlock({}); + break; + + case SpvOpLoopMerge: + merge_ = inst.words[1]; + continue_target_ = inst.words[2]; + break; + case SpvOpSelectionMerge: + merge_ = inst.words[1]; + break; + default: + break; + } + return SPV_SUCCESS; +} + +void DotConverter::FlushBlock(const std::vector& successors) { + out_ << current_block_id_; + if (!seen_function_entry_block_) { + out_ << " [label=\"" << name_mapper_(current_block_id_) << "\nFn " + << name_mapper_(current_function_id_) << " entry\", shape=box];\n"; + } else { + out_ << " [label=\"" << name_mapper_(current_block_id_) << "\"];\n"; + } + + for (auto successor : successors) { + out_ << current_block_id_ << " -> " << successor << ";\n"; + } + + if (merge_) { + out_ << current_block_id_ << " -> " << merge_ << " [" << kMergeStyle + << "];\n"; + } + if (continue_target_) { + out_ << current_block_id_ << " -> " << continue_target_ << " [" + << kContinueStyle << "];\n"; + } + + // Reset the book-keeping for a block. + seen_function_entry_block_ = true; + merge_ = 0; + continue_target_ = 0; +} + +spv_result_t HandleInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto converter = static_cast(user_data); + return converter->HandleInstruction(*parsed_instruction); +} + +} // anonymous namespace + +spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words, + size_t num_words, std::iostream* out, + spv_diagnostic* diagnostic) { + // Invalid arguments return error codes, but don't necessarily generate + // diagnostics. These are programmer errors, not user errors. + if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC; + const spvtools::AssemblyGrammar grammar(context); + if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; + + spvtools::FriendlyNameMapper friendly_mapper(context, words, num_words); + DotConverter converter(friendly_mapper.GetNameMapper(), out); + converter.Begin(); + if (auto error = spvBinaryParse(context, &converter, words, num_words, + nullptr, HandleInstruction, diagnostic)) { + return error; + } + converter.End(); + + return SPV_SUCCESS; +} diff --git a/third_party/spirv-tools/tools/cfg/bin_to_dot.h b/third_party/spirv-tools/tools/cfg/bin_to_dot.h new file mode 100644 index 0000000..4de2e07 --- /dev/null +++ b/third_party/spirv-tools/tools/cfg/bin_to_dot.h @@ -0,0 +1,28 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TOOLS_CFG_BIN_TO_DOT_H_ +#define TOOLS_CFG_BIN_TO_DOT_H_ + +#include + +#include "spirv-tools/libspirv.h" + +// Dumps the control flow graph for the given module to the output stream. +// Returns SPV_SUCCESS on succes. +spv_result_t BinaryToDot(const spv_const_context context, const uint32_t* words, + size_t num_words, std::iostream* out, + spv_diagnostic* diagnostic); + +#endif // TOOLS_CFG_BIN_TO_DOT_H_ diff --git a/third_party/spirv-tools/tools/cfg/cfg.cpp b/third_party/spirv-tools/tools/cfg/cfg.cpp new file mode 100644 index 0000000..ce7f1c2 --- /dev/null +++ b/third_party/spirv-tools/tools/cfg/cfg.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "spirv-tools/libspirv.h" +#include "tools/cfg/bin_to_dot.h" +#include "tools/io.h" + +// Prints a program usage message to stdout. +static void print_usage(const char* argv0) { + printf( + R"(%s - Show the control flow graph in GraphiViz "dot" form. EXPERIMENTAL + +Usage: %s [options] [] + +The SPIR-V binary is read from . If no file is specified, +or if the filename is "-", then the binary is read from standard input. + +Options: + + -h, --help Print this help. + --version Display version information. + + -o Set the output filename. + Output goes to standard output if this option is + not specified, or if the filename is "-". +)", + argv0, argv0); +} + +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; + +int main(int argc, char** argv) { + const char* inFile = nullptr; + const char* outFile = nullptr; // Stays nullptr if printing to stdout. + + for (int argi = 1; argi < argc; ++argi) { + if ('-' == argv[argi][0]) { + switch (argv[argi][1]) { + case 'h': + print_usage(argv[0]); + return 0; + case 'o': { + if (!outFile && argi + 1 < argc) { + outFile = argv[++argi]; + } else { + print_usage(argv[0]); + return 1; + } + } break; + case '-': { + // Long options + if (0 == strcmp(argv[argi], "--help")) { + print_usage(argv[0]); + return 0; + } else if (0 == strcmp(argv[argi], "--version")) { + printf("%s EXPERIMENTAL\n", spvSoftwareVersionDetailsString()); + printf("Target: %s\n", + spvTargetEnvDescription(kDefaultEnvironment)); + return 0; + } else { + print_usage(argv[0]); + return 1; + } + } break; + case 0: { + // Setting a filename of "-" to indicate stdin. + if (!inFile) { + inFile = argv[argi]; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return 1; + } + } break; + default: + print_usage(argv[0]); + return 1; + } + } else { + if (!inFile) { + inFile = argv[argi]; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return 1; + } + } + } + + // Read the input binary. + std::vector contents; + if (!ReadFile(inFile, "rb", &contents)) return 1; + spv_context context = spvContextCreate(kDefaultEnvironment); + spv_diagnostic diagnostic = nullptr; + + std::stringstream ss; + auto error = + BinaryToDot(context, contents.data(), contents.size(), &ss, &diagnostic); + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); + return error; + } + std::string str = ss.str(); + WriteFile(outFile, "w", str.data(), str.size()); + + spvDiagnosticDestroy(diagnostic); + spvContextDestroy(context); + + return 0; +} diff --git a/third_party/spirv-tools/tools/dis/dis.cpp b/third_party/spirv-tools/tools/dis/dis.cpp new file mode 100644 index 0000000..bdeeef1 --- /dev/null +++ b/third_party/spirv-tools/tools/dis/dis.cpp @@ -0,0 +1,216 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +#include // Need fileno +#include +#endif + +#include +#include +#include +#include + +#include "spirv-tools/libspirv.h" +#include "tools/io.h" + +static void print_usage(char* argv0) { + printf( + R"(%s - Disassemble a SPIR-V binary module + +Usage: %s [options] [] + +The SPIR-V binary is read from . If no file is specified, +or if the filename is "-", then the binary is read from standard input. + +Options: + + -h, --help Print this help. + --version Display disassembler version information. + + -o Set the output filename. + Output goes to standard output if this option is + not specified, or if the filename is "-". + + --color Force color output. The default when printing to a terminal. + Overrides a previous --no-color option. + --no-color Don't print in color. Overrides a previous --color option. + The default when output goes to something other than a + terminal (e.g. a file, a pipe, or a shell redirection). + + --no-indent Don't indent instructions. + + --no-header Don't output the header as leading comments. + + --raw-id Show raw Id values instead of friendly names. + + --offsets Show byte offsets for each instruction. + + --comment Add comments to make reading easier +)", + argv0, argv0); +} + +static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; + +int main(int argc, char** argv) { + const char* inFile = nullptr; + const char* outFile = nullptr; + + bool color_is_possible = +#if SPIRV_COLOR_TERMINAL + true; +#else + false; +#endif + bool force_color = false; + bool force_no_color = false; + + bool allow_indent = true; + bool show_byte_offsets = false; + bool no_header = false; + bool friendly_names = true; + bool comments = false; + + for (int argi = 1; argi < argc; ++argi) { + if ('-' == argv[argi][0]) { + switch (argv[argi][1]) { + case 'h': + print_usage(argv[0]); + return 0; + case 'o': { + if (!outFile && argi + 1 < argc) { + outFile = argv[++argi]; + } else { + print_usage(argv[0]); + return 1; + } + } break; + case '-': { + // Long options + if (0 == strcmp(argv[argi], "--no-color")) { + force_no_color = true; + force_color = false; + } else if (0 == strcmp(argv[argi], "--color")) { + force_no_color = false; + force_color = true; + } else if (0 == strcmp(argv[argi], "--comment")) { + comments = true; + } else if (0 == strcmp(argv[argi], "--no-indent")) { + allow_indent = false; + } else if (0 == strcmp(argv[argi], "--offsets")) { + show_byte_offsets = true; + } else if (0 == strcmp(argv[argi], "--no-header")) { + no_header = true; + } else if (0 == strcmp(argv[argi], "--raw-id")) { + friendly_names = false; + } else if (0 == strcmp(argv[argi], "--help")) { + print_usage(argv[0]); + return 0; + } else if (0 == strcmp(argv[argi], "--version")) { + printf("%s\n", spvSoftwareVersionDetailsString()); + printf("Target: %s\n", + spvTargetEnvDescription(kDefaultEnvironment)); + return 0; + } else { + print_usage(argv[0]); + return 1; + } + } break; + case 0: { + // Setting a filename of "-" to indicate stdin. + if (!inFile) { + inFile = argv[argi]; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return 1; + } + } break; + default: + print_usage(argv[0]); + return 1; + } + } else { + if (!inFile) { + inFile = argv[argi]; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + return 1; + } + } + } + + uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE; + + if (allow_indent) options |= SPV_BINARY_TO_TEXT_OPTION_INDENT; + + if (show_byte_offsets) options |= SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET; + + if (no_header) options |= SPV_BINARY_TO_TEXT_OPTION_NO_HEADER; + + if (friendly_names) options |= SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES; + + if (comments) options |= SPV_BINARY_TO_TEXT_OPTION_COMMENT; + + if (!outFile || (0 == strcmp("-", outFile))) { + // Print to standard output. + options |= SPV_BINARY_TO_TEXT_OPTION_PRINT; + + if (color_is_possible && !force_no_color) { + bool output_is_tty = true; +#if defined(_POSIX_VERSION) + output_is_tty = isatty(fileno(stdout)); +#endif + if (output_is_tty || force_color) { + options |= SPV_BINARY_TO_TEXT_OPTION_COLOR; + } + } + } + + // Read the input binary. + std::vector contents; + if (!ReadFile(inFile, "rb", &contents)) return 1; + + // If printing to standard output, then spvBinaryToText should + // do the printing. In particular, colour printing on Windows is + // controlled by modifying console objects synchronously while + // outputting to the stream rather than by injecting escape codes + // into the output stream. + // If the printing option is off, then save the text in memory, so + // it can be emitted later in this function. + const bool print_to_stdout = SPV_BINARY_TO_TEXT_OPTION_PRINT & options; + spv_text text = nullptr; + spv_text* textOrNull = print_to_stdout ? nullptr : &text; + spv_diagnostic diagnostic = nullptr; + spv_context context = spvContextCreate(kDefaultEnvironment); + spv_result_t error = + spvBinaryToText(context, contents.data(), contents.size(), options, + textOrNull, &diagnostic); + spvContextDestroy(context); + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + return error; + } + + if (!print_to_stdout) { + if (!WriteFile(outFile, "w", text->str, text->length)) { + spvTextDestroy(text); + return 1; + } + } + spvTextDestroy(text); + + return 0; +} diff --git a/third_party/spirv-tools/tools/emacs/50spirv-tools.el b/third_party/spirv-tools/tools/emacs/50spirv-tools.el new file mode 100644 index 0000000..1d4fbee --- /dev/null +++ b/third_party/spirv-tools/tools/emacs/50spirv-tools.el @@ -0,0 +1,40 @@ +;; Copyright (c) 2016 LunarG Inc. +;; +;; Licensed under the Apache License, Version 2.0 (the "License"); +;; you may not use this file except in compliance with the License. +;; You may obtain a copy of the License at +;; +;; http://www.apache.org/licenses/LICENSE-2.0 +;; +;; Unless required by applicable law or agreed to in writing, software +;; distributed under the License is distributed on an "AS IS" BASIS, +;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +;; See the License for the specific language governing permissions and +;; limitations under the License. + +;; Upon loading a file with the .spv extension into emacs, the file +;; will be disassembled using spirv-dis, and the result colorized with +;; asm-mode in emacs. The file may be edited within the constraints +;; of validity, and when re-saved will be re-assembled using spirv-as. + +;; Note that symbol IDs are not preserved through a load/edit/save operation. +;; This may change if the ability is added to spirv-as. + +;; It is required that those tools be in your PATH. If that is not the case +;; when starting emacs, the path can be modified as in this example: +;; (setenv "PATH" (concat (getenv "PATH") ":/path/to/spirv/tools")) +;; +;; See https://github.com/KhronosGroup/SPIRV-Tools/issues/359 + +(require 'jka-compr) +(require 'asm-mode) + +(add-to-list 'jka-compr-compression-info-list + '["\\.spv\\'" + "Assembling SPIRV" "spirv-as" ("-o" "-") + "Disassembling SPIRV" "spirv-dis" ("--no-color" "--raw-id") + t nil "\003\002\043\007"]) + +(add-to-list 'auto-mode-alist '("\\.spv\\'" . asm-mode)) + +(jka-compr-update) diff --git a/third_party/spirv-tools/tools/emacs/CMakeLists.txt b/third_party/spirv-tools/tools/emacs/CMakeLists.txt new file mode 100644 index 0000000..ecd7c27 --- /dev/null +++ b/third_party/spirv-tools/tools/emacs/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright (c) 2016 LunarG Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Install a script for use with the auto-compression feature of emacs(1). +# Upon loading a file with the .spv extension, the file will be disassembled +# using spirv-dis, and the result colorized with asm-mode in emacs. The file +# may be edited within the constraints of validity, and when re-saved will be +# re-assembled using spirv-as. + +# It is required that those tools be in your PATH. If that is not the case +# when starting emacs, the path can be modified as in this example: +# (setenv "PATH" (concat (getenv "PATH") ":/path/to/spirv/tools")) +# +# See https://github.com/KhronosGroup/SPIRV-Tools/issues/359 + +# This is an absolute directory, and ignores CMAKE_INSTALL_PREFIX, or +# it will not be found by emacs upon startup. It is only installed if +# both of the following are true: +# 1. SPIRV_TOOLS_INSTALL_EMACS_HELPERS is defined +# 2. The directory /etc/emacs/site-start.d already exists at the time of +# cmake invocation (not at the time of make install). This is +# typically true if emacs is installed on the system. + +# Note that symbol IDs are not preserved through a load/edit/save operation. +# This may change if the ability is added to spirv-as. + +option(SPIRV_TOOLS_INSTALL_EMACS_HELPERS + "Install Emacs helper to disassemble/assemble SPIR-V binaries on file load/save." + ${SPIRV_TOOLS_INSTALL_EMACS_HELPERS}) +if (${SPIRV_TOOLS_INSTALL_EMACS_HELPERS}) + if(EXISTS /etc/emacs/site-start.d) + if(ENABLE_SPIRV_TOOLS_INSTALL) + install(FILES 50spirv-tools.el DESTINATION /etc/emacs/site-start.d) + endif(ENABLE_SPIRV_TOOLS_INSTALL) + endif() +endif() + diff --git a/third_party/spirv-tools/tools/fuzz/fuzz.cpp b/third_party/spirv-tools/tools/fuzz/fuzz.cpp new file mode 100644 index 0000000..5ee0fb1 --- /dev/null +++ b/third_party/spirv-tools/tools/fuzz/fuzz.cpp @@ -0,0 +1,786 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/fuzz/force_render_red.h" +#include "source/fuzz/fuzzer.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/replayer.h" +#include "source/fuzz/shrinker.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/opt/log.h" +#include "source/spirv_fuzzer_options.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +namespace { + +// Check that the std::system function can actually be used. +bool CheckExecuteCommand() { + int res = std::system(nullptr); + return res != 0; +} + +// Execute a command using the shell. +// Returns true if and only if the command's exit status was 0. +bool ExecuteCommand(const std::string& command) { + errno = 0; + int status = std::system(command.c_str()); + assert(errno == 0 && "failed to execute command"); + // The result returned by 'system' is implementation-defined, but is + // usually the case that the returned value is 0 when the command's exit + // code was 0. We are assuming that here, and that's all we depend on. + return status == 0; +} + +// Status and actions to perform after parsing command-line arguments. +enum class FuzzActions { + FORCE_RENDER_RED, // Turn the shader into a form such that it is guaranteed + // to render a red image. + FUZZ, // Run the fuzzer to apply transformations in a randomized fashion. + REPLAY, // Replay an existing sequence of transformations. + SHRINK, // Shrink an existing sequence of transformations with respect to an + // interestingness function. + STOP // Do nothing. +}; + +struct FuzzStatus { + FuzzActions action; + int code; +}; + +void PrintUsage(const char* program) { + // NOTE: Please maintain flags in lexicographical order. + printf( + R"(%s - Fuzzes an equivalent SPIR-V binary based on a given binary. + +USAGE: %s [options] -o \ + --donors= +USAGE: %s [options] -o \ + --shrink= -- [args...] + +The SPIR-V binary is read from . If is also present, +facts about the SPIR-V binary are read from this file. + +The transformed SPIR-V binary is written to . Human-readable and +binary representations of the transformations that were applied are written to + and , respectively. + +When passing --shrink= an +must also be provided; this is the path to a script that returns 0 if and only +if a given SPIR-V binary is interesting. The SPIR-V binary will be passed to +the script as an argument after any other provided arguments [args...]. The +"--" characters are optional but denote that all arguments that follow are +positional arguments and thus will be forwarded to the interestingness script, +and not parsed by %s. + +NOTE: The fuzzer is a work in progress. + +Options (in lexicographical order): + + -h, --help + Print this help. + --donors= + File specifying a series of donor files, one per line. Must be + provided if the tool is invoked in fuzzing mode; incompatible + with replay and shrink modes. The file should be empty if no + donors are to be used. + --enable-all-passes + By default, spirv-fuzz follows the philosophy of "swarm testing" + (Groce et al., 2012): only a subset of fuzzer passes are enabled + on any given fuzzer run, with the subset being chosen randomly. + This flag instead forces *all* fuzzer passes to be enabled. When + running spirv-fuzz many times this is likely to produce *less* + diverse fuzzed modules than when swarm testing is used. The + purpose of the flag is to allow that hypothesis to be tested. + --force-render-red + Transforms the input shader into a shader that writes red to the + output buffer, and then captures the original shader as the body + of a conditional with a dynamically false guard. Exploits input + facts to make the guard non-obviously false. This option is a + helper for massaging crash-inducing tests into a runnable + format; it does not perform any fuzzing. + --fuzzer-pass-validation + Run the validator after applying each fuzzer pass during + fuzzing. Aborts fuzzing early if an invalid binary is created. + Useful for debugging spirv-fuzz. + --repeated-pass-strategy= + Available strategies are: + - looped (the default): a sequence of fuzzer passes is chosen at + the start of fuzzing, via randomly choosing enabled passes, and + augmenting these choices with fuzzer passes that it is + recommended to run subsequently. Fuzzing then involves + repeatedly applying this fixed sequence of passes. + - random: each time a fuzzer pass is requested, this strategy + either provides one at random from the set of enabled passes, + or provides a pass that has been recommended based on a pass + that was used previously. + - simple: each time a fuzzer pass is requested, one is provided + at random from the set of enabled passes. + --replay + File from which to read a sequence of transformations to replay + (instead of fuzzing) + --replay-range= + Signed 32-bit integer. If set to a positive value N, only the + first N transformations will be applied during replay. If set to + a negative value -N, all but the final N transformations will be + applied during replay. If set to 0 (the default), all + transformations will be applied during replay. Ignored unless + --replay is used. + --replay-validation + Run the validator after applying each transformation during + replay (including the replay that occurs during shrinking). + Aborts if an invalid binary is created. Useful for debugging + spirv-fuzz. + --seed= + Unsigned 32-bit integer seed to control random number + generation. + --shrink= + File from which to read a sequence of transformations to shrink + (instead of fuzzing) + --shrinker-step-limit= + Unsigned 32-bit integer specifying maximum number of steps the + shrinker will take before giving up. Ignored unless --shrink + is used. + --shrinker-temp-file-prefix= + Specifies a temporary file prefix that will be used to output + temporary shader files during shrinking. A number and .spv + extension will be added. The default is "temp_", which will + cause files like "temp_0001.spv" to be output to the current + directory. Ignored unless --shrink is used. + --version + Display fuzzer version information. + +Supported validator options are as follows. See `spirv-val --help` for details. + --before-hlsl-legalization + --relax-block-layout + --relax-logical-pointer + --relax-struct-store + --scalar-block-layout + --skip-block-layout +)", + program, program, program, program); +} + +// Message consumer for this tool. Used to emit diagnostics during +// initialization and setup. Note that |source| and |position| are irrelevant +// here because we are still not processing a SPIR-V input file. +void FuzzDiagnostic(spv_message_level_t level, const char* /*source*/, + const spv_position_t& /*position*/, const char* message) { + if (level == SPV_MSG_ERROR) { + fprintf(stderr, "error: "); + } + fprintf(stderr, "%s\n", message); +} + +FuzzStatus ParseFlags( + int argc, const char** argv, std::string* in_binary_file, + std::string* out_binary_file, std::string* donors_file, + std::string* replay_transformations_file, + std::vector* interestingness_test, + std::string* shrink_transformations_file, + std::string* shrink_temp_file_prefix, + spvtools::fuzz::Fuzzer::RepeatedPassStrategy* repeated_pass_strategy, + spvtools::FuzzerOptions* fuzzer_options, + spvtools::ValidatorOptions* validator_options) { + uint32_t positional_arg_index = 0; + bool only_positional_arguments_remain = false; + bool force_render_red = false; + + *repeated_pass_strategy = + spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations; + + for (int argi = 1; argi < argc; ++argi) { + const char* cur_arg = argv[argi]; + if ('-' == cur_arg[0] && !only_positional_arguments_remain) { + if (0 == strcmp(cur_arg, "--version")) { + spvtools::Logf(FuzzDiagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n", + spvSoftwareVersionDetailsString()); + return {FuzzActions::STOP, 0}; + } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { + PrintUsage(argv[0]); + return {FuzzActions::STOP, 0}; + } else if (0 == strcmp(cur_arg, "-o")) { + if (out_binary_file->empty() && argi + 1 < argc) { + *out_binary_file = std::string(argv[++argi]); + } else { + PrintUsage(argv[0]); + return {FuzzActions::STOP, 1}; + } + } else if (0 == strncmp(cur_arg, "--donors=", sizeof("--donors=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + *donors_file = std::string(split_flag.second); + } else if (0 == strncmp(cur_arg, "--enable-all-passes", + sizeof("--enable-all-passes") - 1)) { + fuzzer_options->enable_all_passes(); + } else if (0 == strncmp(cur_arg, "--force-render-red", + sizeof("--force-render-red") - 1)) { + force_render_red = true; + } else if (0 == strncmp(cur_arg, "--fuzzer-pass-validation", + sizeof("--fuzzer-pass-validation") - 1)) { + fuzzer_options->enable_fuzzer_pass_validation(); + } else if (0 == strncmp(cur_arg, "--replay=", sizeof("--replay=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + *replay_transformations_file = std::string(split_flag.second); + } else if (0 == strncmp(cur_arg, "--repeated-pass-strategy=", + sizeof("--repeated-pass-strategy=") - 1)) { + std::string strategy = spvtools::utils::SplitFlagArgs(cur_arg).second; + if (strategy == "looped") { + *repeated_pass_strategy = spvtools::fuzz::Fuzzer:: + RepeatedPassStrategy::kLoopedWithRecommendations; + } else if (strategy == "random") { + *repeated_pass_strategy = spvtools::fuzz::Fuzzer:: + RepeatedPassStrategy::kRandomWithRecommendations; + } else if (strategy == "simple") { + *repeated_pass_strategy = + spvtools::fuzz::Fuzzer::RepeatedPassStrategy::kSimple; + } else { + std::stringstream ss; + ss << "Unknown repeated pass strategy '" << strategy << "'" + << std::endl; + ss << "Valid options are 'looped', 'random' and 'simple'."; + spvtools::Error(FuzzDiagnostic, nullptr, {}, ss.str().c_str()); + return {FuzzActions::STOP, 1}; + } + } else if (0 == strncmp(cur_arg, "--replay-range=", + sizeof("--replay-range=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + char* end = nullptr; + errno = 0; + const auto replay_range = + static_cast(strtol(split_flag.second.c_str(), &end, 10)); + assert(end != split_flag.second.c_str() && errno == 0); + fuzzer_options->set_replay_range(replay_range); + } else if (0 == strncmp(cur_arg, "--replay-validation", + sizeof("--replay-validation") - 1)) { + fuzzer_options->enable_replay_validation(); + } else if (0 == strncmp(cur_arg, "--shrink=", sizeof("--shrink=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + *shrink_transformations_file = std::string(split_flag.second); + } else if (0 == strncmp(cur_arg, "--seed=", sizeof("--seed=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + char* end = nullptr; + errno = 0; + const auto seed = + static_cast(strtol(split_flag.second.c_str(), &end, 10)); + assert(end != split_flag.second.c_str() && errno == 0); + fuzzer_options->set_random_seed(seed); + } else if (0 == strncmp(cur_arg, "--shrinker-step-limit=", + sizeof("--shrinker-step-limit=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + char* end = nullptr; + errno = 0; + const auto step_limit = + static_cast(strtol(split_flag.second.c_str(), &end, 10)); + assert(end != split_flag.second.c_str() && errno == 0); + fuzzer_options->set_shrinker_step_limit(step_limit); + } else if (0 == strncmp(cur_arg, "--shrinker-temp-file-prefix=", + sizeof("--shrinker-temp-file-prefix=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + *shrink_temp_file_prefix = std::string(split_flag.second); + } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) { + validator_options->SetBeforeHlslLegalization(true); + } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) { + validator_options->SetRelaxLogicalPointer(true); + } else if (0 == strcmp(cur_arg, "--relax-block-layout")) { + validator_options->SetRelaxBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) { + validator_options->SetScalarBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { + validator_options->SetSkipBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { + validator_options->SetRelaxStructStore(true); + } else if (0 == strcmp(cur_arg, "--")) { + only_positional_arguments_remain = true; + } else { + std::stringstream ss; + ss << "Unrecognized argument: " << cur_arg << std::endl; + spvtools::Error(FuzzDiagnostic, nullptr, {}, ss.str().c_str()); + PrintUsage(argv[0]); + return {FuzzActions::STOP, 1}; + } + } else if (positional_arg_index == 0) { + // Binary input file name + assert(in_binary_file->empty()); + *in_binary_file = std::string(cur_arg); + positional_arg_index++; + } else { + interestingness_test->push_back(std::string(cur_arg)); + } + } + + if (in_binary_file->empty()) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, "No input file specified"); + return {FuzzActions::STOP, 1}; + } + + if (out_binary_file->empty()) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, "-o required"); + return {FuzzActions::STOP, 1}; + } + + auto const_fuzzer_options = + static_cast(*fuzzer_options); + if (force_render_red) { + if (!replay_transformations_file->empty() || + !shrink_transformations_file->empty() || + const_fuzzer_options->replay_validation_enabled) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, + "The --force-render-red argument cannot be used with any " + "other arguments except -o."); + return {FuzzActions::STOP, 1}; + } + return {FuzzActions::FORCE_RENDER_RED, 0}; + } + + if (replay_transformations_file->empty() && + shrink_transformations_file->empty() && + static_cast(*fuzzer_options) + ->replay_validation_enabled) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, + "The --replay-validation argument can only be used with " + "one of the --replay or --shrink arguments."); + return {FuzzActions::STOP, 1}; + } + + if (shrink_transformations_file->empty() && !interestingness_test->empty()) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, + "Too many positional arguments specified; extra positional " + "arguments are used as the interestingness function, which " + "are only valid with the --shrink option."); + return {FuzzActions::STOP, 1}; + } + + if (!shrink_transformations_file->empty() && interestingness_test->empty()) { + spvtools::Error( + FuzzDiagnostic, nullptr, {}, + "The --shrink option requires an interestingness function."); + return {FuzzActions::STOP, 1}; + } + + if (!replay_transformations_file->empty() || + !shrink_transformations_file->empty()) { + // Donors should not be provided when replaying or shrinking: they only make + // sense during fuzzing. + if (!donors_file->empty()) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, + "The --donors argument is not compatible with --replay " + "nor --shrink."); + return {FuzzActions::STOP, 1}; + } + } + + if (!replay_transformations_file->empty()) { + // A replay transformations file was given, thus the tool is being invoked + // in replay mode. + if (!shrink_transformations_file->empty()) { + spvtools::Error( + FuzzDiagnostic, nullptr, {}, + "The --replay and --shrink arguments are mutually exclusive."); + return {FuzzActions::STOP, 1}; + } + return {FuzzActions::REPLAY, 0}; + } + + if (!shrink_transformations_file->empty()) { + // The tool is being invoked in shrink mode. + assert(!interestingness_test->empty() && + "An error should have been raised if --shrink was provided without " + "an interestingness test."); + return {FuzzActions::SHRINK, 0}; + } + + // The tool is being invoked in fuzz mode. + if (donors_file->empty()) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, + "Fuzzing requires that the --donors option is used."); + return {FuzzActions::STOP, 1}; + } + return {FuzzActions::FUZZ, 0}; +} + +bool ParseTransformations( + const std::string& transformations_file, + spvtools::fuzz::protobufs::TransformationSequence* transformations) { + std::ifstream transformations_stream; + transformations_stream.open(transformations_file, + std::ios::in | std::ios::binary); + auto parse_success = + transformations->ParseFromIstream(&transformations_stream); + transformations_stream.close(); + if (!parse_success) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, + ("Error reading transformations from file '" + + transformations_file + "'") + .c_str()); + return false; + } + return true; +} + +bool Replay(const spv_target_env& target_env, + spv_const_fuzzer_options fuzzer_options, + spv_validator_options validator_options, + const std::vector& binary_in, + const spvtools::fuzz::protobufs::FactSequence& initial_facts, + const std::string& replay_transformations_file, + std::vector* binary_out, + spvtools::fuzz::protobufs::TransformationSequence* + transformations_applied) { + spvtools::fuzz::protobufs::TransformationSequence transformation_sequence; + if (!ParseTransformations(replay_transformations_file, + &transformation_sequence)) { + return false; + } + + uint32_t num_transformations_to_apply; + if (fuzzer_options->replay_range > 0) { + // We have a positive replay range, N. We would like transformations + // [0, N), truncated to the number of available transformations if N is too + // large. + num_transformations_to_apply = static_cast( + std::min(fuzzer_options->replay_range, + transformation_sequence.transformation_size())); + } else { + // We have non-positive replay range, -N (where N may be 0). We would like + // transformations [0, num_transformations - N), or no transformations if N + // is too large. + num_transformations_to_apply = static_cast( + std::max(0, transformation_sequence.transformation_size() + + fuzzer_options->replay_range)); + } + + auto replay_result = + spvtools::fuzz::Replayer( + target_env, spvtools::utils::CLIMessageConsumer, binary_in, + initial_facts, transformation_sequence, num_transformations_to_apply, + fuzzer_options->replay_validation_enabled, validator_options) + .Run(); + replay_result.transformed_module->module()->ToBinary(binary_out, false); + *transformations_applied = std::move(replay_result.applied_transformations); + return replay_result.status == + spvtools::fuzz::Replayer::ReplayerResultStatus::kComplete; +} + +bool Shrink(const spv_target_env& target_env, + spv_const_fuzzer_options fuzzer_options, + spv_validator_options validator_options, + const std::vector& binary_in, + const spvtools::fuzz::protobufs::FactSequence& initial_facts, + const std::string& shrink_transformations_file, + const std::string& shrink_temp_file_prefix, + const std::vector& interestingness_command, + std::vector* binary_out, + spvtools::fuzz::protobufs::TransformationSequence* + transformations_applied) { + spvtools::fuzz::protobufs::TransformationSequence transformation_sequence; + if (!ParseTransformations(shrink_transformations_file, + &transformation_sequence)) { + return false; + } + assert(!interestingness_command.empty() && + "An error should have been raised because the interestingness_command " + "is empty."); + std::stringstream joined; + joined << interestingness_command[0]; + for (size_t i = 1, size = interestingness_command.size(); i < size; ++i) { + joined << " " << interestingness_command[i]; + } + std::string interestingness_command_joined = joined.str(); + + spvtools::fuzz::Shrinker::InterestingnessFunction interestingness_function = + [interestingness_command_joined, shrink_temp_file_prefix]( + std::vector binary, uint32_t reductions_applied) -> bool { + std::stringstream ss; + ss << shrink_temp_file_prefix << std::setw(4) << std::setfill('0') + << reductions_applied << ".spv"; + const auto spv_file = ss.str(); + const std::string command = interestingness_command_joined + " " + spv_file; + auto write_file_succeeded = + WriteFile(spv_file.c_str(), "wb", &binary[0], binary.size()); + (void)(write_file_succeeded); + assert(write_file_succeeded); + return ExecuteCommand(command); + }; + + auto shrink_result = + spvtools::fuzz::Shrinker( + target_env, spvtools::utils::CLIMessageConsumer, binary_in, + initial_facts, transformation_sequence, interestingness_function, + fuzzer_options->shrinker_step_limit, + fuzzer_options->replay_validation_enabled, validator_options) + .Run(); + + *binary_out = std::move(shrink_result.transformed_binary); + *transformations_applied = std::move(shrink_result.applied_transformations); + return spvtools::fuzz::Shrinker::ShrinkerResultStatus::kComplete == + shrink_result.status || + spvtools::fuzz::Shrinker::ShrinkerResultStatus::kStepLimitReached == + shrink_result.status; +} + +bool Fuzz(const spv_target_env& target_env, + spv_const_fuzzer_options fuzzer_options, + spv_validator_options validator_options, + const std::vector& binary_in, + const spvtools::fuzz::protobufs::FactSequence& initial_facts, + const std::string& donors, + spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy, + std::vector* binary_out, + spvtools::fuzz::protobufs::TransformationSequence* + transformations_applied) { + auto message_consumer = spvtools::utils::CLIMessageConsumer; + + std::vector donor_suppliers; + + std::ifstream donors_file(donors); + if (!donors_file) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error opening donors file"); + return false; + } + std::string donor_filename; + while (std::getline(donors_file, donor_filename)) { + donor_suppliers.emplace_back( + [donor_filename, message_consumer, + target_env]() -> std::unique_ptr { + std::vector donor_binary; + if (!ReadFile(donor_filename.c_str(), "rb", + &donor_binary)) { + return nullptr; + } + return spvtools::BuildModule(target_env, message_consumer, + donor_binary.data(), + donor_binary.size()); + }); + } + + auto fuzz_result = + spvtools::fuzz::Fuzzer( + target_env, message_consumer, binary_in, initial_facts, + donor_suppliers, + spvtools::MakeUnique( + fuzzer_options->has_random_seed + ? fuzzer_options->random_seed + : static_cast(std::random_device()())), + fuzzer_options->all_passes_enabled, repeated_pass_strategy, + fuzzer_options->fuzzer_pass_validation_enabled, validator_options) + .Run(); + *binary_out = std::move(fuzz_result.transformed_binary); + *transformations_applied = std::move(fuzz_result.applied_transformations); + if (fuzz_result.status != + spvtools::fuzz::Fuzzer::FuzzerResultStatus::kComplete) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error running fuzzer"); + return false; + } + return true; +} + +} // namespace + +// Dumps |binary| to file |filename|. Useful for interactive debugging. +void DumpShader(const std::vector& binary, const char* filename) { + auto write_file_succeeded = + WriteFile(filename, "wb", &binary[0], binary.size()); + if (!write_file_succeeded) { + std::cerr << "Failed to dump shader" << std::endl; + } +} + +// Dumps the SPIRV-V module in |context| to file |filename|. Useful for +// interactive debugging. +void DumpShader(spvtools::opt::IRContext* context, const char* filename) { + std::vector binary; + context->module()->ToBinary(&binary, false); + DumpShader(binary, filename); +} + +// Dumps |transformations| to file |filename| in binary format. Useful for +// interactive debugging. +void DumpTransformationsBinary( + const spvtools::fuzz::protobufs::TransformationSequence& transformations, + const char* filename) { + std::ofstream transformations_file; + transformations_file.open(filename, std::ios::out | std::ios::binary); + transformations.SerializeToOstream(&transformations_file); + transformations_file.close(); +} + +// Dumps |transformations| to file |filename| in JSON format. Useful for +// interactive debugging. +void DumpTransformationsJson( + const spvtools::fuzz::protobufs::TransformationSequence& transformations, + const char* filename) { + std::string json_string; + auto json_options = google::protobuf::util::JsonOptions(); + json_options.add_whitespace = true; + auto json_generation_status = google::protobuf::util::MessageToJsonString( + transformations, &json_string, json_options); + if (json_generation_status == google::protobuf::util::Status::OK) { + std::ofstream transformations_json_file(filename); + transformations_json_file << json_string; + transformations_json_file.close(); + } +} + +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_3; + +int main(int argc, const char** argv) { + std::string in_binary_file; + std::string out_binary_file; + std::string donors_file; + std::string replay_transformations_file; + std::vector interestingness_test; + std::string shrink_transformations_file; + std::string shrink_temp_file_prefix = "temp_"; + spvtools::fuzz::Fuzzer::RepeatedPassStrategy repeated_pass_strategy; + + spvtools::FuzzerOptions fuzzer_options; + spvtools::ValidatorOptions validator_options; + + FuzzStatus status = + ParseFlags(argc, argv, &in_binary_file, &out_binary_file, &donors_file, + &replay_transformations_file, &interestingness_test, + &shrink_transformations_file, &shrink_temp_file_prefix, + &repeated_pass_strategy, &fuzzer_options, &validator_options); + + if (status.action == FuzzActions::STOP) { + return status.code; + } + + std::vector binary_in; + if (!ReadFile(in_binary_file.c_str(), "rb", &binary_in)) { + return 1; + } + + spvtools::fuzz::protobufs::FactSequence initial_facts; + + // If not found, dot_pos will be std::string::npos, which can be used in + // substr to mean "the end of the string"; there is no need to check the + // result. + size_t dot_pos = in_binary_file.rfind('.'); + std::string in_facts_file = in_binary_file.substr(0, dot_pos) + ".facts"; + std::ifstream facts_input(in_facts_file); + if (facts_input) { + std::string facts_json_string((std::istreambuf_iterator(facts_input)), + std::istreambuf_iterator()); + facts_input.close(); + if (google::protobuf::util::Status::OK != + google::protobuf::util::JsonStringToMessage(facts_json_string, + &initial_facts)) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error reading facts data"); + return 1; + } + } + + std::vector binary_out; + spvtools::fuzz::protobufs::TransformationSequence transformations_applied; + + spv_target_env target_env = kDefaultEnvironment; + + switch (status.action) { + case FuzzActions::FORCE_RENDER_RED: + if (!spvtools::fuzz::ForceRenderRed(target_env, validator_options, + binary_in, initial_facts, + &binary_out)) { + return 1; + } + break; + case FuzzActions::FUZZ: + if (!Fuzz(target_env, fuzzer_options, validator_options, binary_in, + initial_facts, donors_file, repeated_pass_strategy, &binary_out, + &transformations_applied)) { + return 1; + } + break; + case FuzzActions::REPLAY: + if (!Replay(target_env, fuzzer_options, validator_options, binary_in, + initial_facts, replay_transformations_file, &binary_out, + &transformations_applied)) { + return 1; + } + break; + case FuzzActions::SHRINK: { + if (!CheckExecuteCommand()) { + std::cerr << "could not find shell interpreter for executing a command" + << std::endl; + return 1; + } + if (!Shrink(target_env, fuzzer_options, validator_options, binary_in, + initial_facts, shrink_transformations_file, + shrink_temp_file_prefix, interestingness_test, &binary_out, + &transformations_applied)) { + return 1; + } + } break; + default: + assert(false && "Unknown fuzzer action."); + break; + } + + if (!WriteFile(out_binary_file.c_str(), "wb", binary_out.data(), + binary_out.size())) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, "Error writing out binary"); + return 1; + } + + if (status.action != FuzzActions::FORCE_RENDER_RED) { + // If not found, dot_pos will be std::string::npos, which can be used in + // substr to mean "the end of the string"; there is no need to check the + // result. + dot_pos = out_binary_file.rfind('.'); + std::string output_file_prefix = out_binary_file.substr(0, dot_pos); + std::ofstream transformations_file; + transformations_file.open(output_file_prefix + ".transformations", + std::ios::out | std::ios::binary); + bool success = + transformations_applied.SerializeToOstream(&transformations_file); + transformations_file.close(); + if (!success) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, + "Error writing out transformations binary"); + return 1; + } + + std::string json_string; + auto json_options = google::protobuf::util::JsonOptions(); + json_options.add_whitespace = true; + auto json_generation_status = google::protobuf::util::MessageToJsonString( + transformations_applied, &json_string, json_options); + if (json_generation_status != google::protobuf::util::Status::OK) { + spvtools::Error(FuzzDiagnostic, nullptr, {}, + "Error writing out transformations in JSON format"); + return 1; + } + + std::ofstream transformations_json_file(output_file_prefix + + ".transformations_json"); + transformations_json_file << json_string; + transformations_json_file.close(); + } + + return 0; +} diff --git a/third_party/spirv-tools/tools/io.h b/third_party/spirv-tools/tools/io.h new file mode 100644 index 0000000..f9cfd9d --- /dev/null +++ b/third_party/spirv-tools/tools/io.h @@ -0,0 +1,86 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TOOLS_IO_H_ +#define TOOLS_IO_H_ + +#include +#include +#include +#include + +// Appends the content from the file named as |filename| to |data|, assuming +// each element in the file is of type |T|. The file is opened with the given +// |mode|. If |filename| is nullptr or "-", reads from the standard input, but +// reopened with the given mode. If any error occurs, writes error messages to +// standard error and returns false. +template +bool ReadFile(const char* filename, const char* mode, std::vector* data) { + const int buf_size = 1024; + const bool use_file = filename && strcmp("-", filename); + if (FILE* fp = + (use_file ? fopen(filename, mode) : freopen(nullptr, mode, stdin))) { + T buf[buf_size]; + while (size_t len = fread(buf, sizeof(T), buf_size, fp)) { + data->insert(data->end(), buf, buf + len); + } + if (ftell(fp) == -1L) { + if (ferror(fp)) { + fprintf(stderr, "error: error reading file '%s'\n", filename); + if (use_file) fclose(fp); + return false; + } + } else { + if (sizeof(T) != 1 && (ftell(fp) % sizeof(T))) { + fprintf( + stderr, + "error: file size should be a multiple of %zd; file '%s' corrupt\n", + sizeof(T), filename); + if (use_file) fclose(fp); + return false; + } + } + if (use_file) fclose(fp); + } else { + fprintf(stderr, "error: file does not exist '%s'\n", filename); + return false; + } + return true; +} + +// Writes the given |data| into the file named as |filename| using the given +// |mode|, assuming |data| is an array of |count| elements of type |T|. If +// |filename| is nullptr or "-", writes to standard output. If any error occurs, +// returns false and outputs error message to standard error. +template +bool WriteFile(const char* filename, const char* mode, const T* data, + size_t count) { + const bool use_stdout = + !filename || (filename[0] == '-' && filename[1] == '\0'); + if (FILE* fp = (use_stdout ? stdout : fopen(filename, mode))) { + size_t written = fwrite(data, sizeof(T), count, fp); + if (count != written) { + fprintf(stderr, "error: could not write to file '%s'\n", filename); + if (!use_stdout) fclose(fp); + return false; + } + if (!use_stdout) fclose(fp); + } else { + fprintf(stderr, "error: could not open file '%s'\n", filename); + return false; + } + return true; +} + +#endif // TOOLS_IO_H_ diff --git a/third_party/spirv-tools/tools/lesspipe/CMakeLists.txt b/third_party/spirv-tools/tools/lesspipe/CMakeLists.txt new file mode 100644 index 0000000..484e51e --- /dev/null +++ b/third_party/spirv-tools/tools/lesspipe/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Install a script for use with the LESSOPEN of less(1). +# For example, after installation into /usr/local do: +# export LESSOPEN='|/usr/local/bin "%s"' +# less -R foo.spv +# +# See https://github.com/KhronosGroup/SPIRV-Tools/issues/359 + +# The script will be installed with everyone having read and execute +# permissions. +# We have a .sh extension because Windows users often configure +# executable settings via filename extension. +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(PROGRAMS spirv-lesspipe.sh DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/third_party/spirv-tools/tools/lesspipe/spirv-lesspipe.sh b/third_party/spirv-tools/tools/lesspipe/spirv-lesspipe.sh new file mode 100644 index 0000000..57684a2 --- /dev/null +++ b/third_party/spirv-tools/tools/lesspipe/spirv-lesspipe.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env sh +# Copyright (c) 2016 The Khronos Group Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# A script for automatically disassembling a .spv file +# for less(1). This assumes spirv-dis is on our PATH. +# +# See https://github.com/KhronosGroup/SPIRV-Tools/issues/359 + +case "$1" in + *.spv) spirv-dis "$@" 2>/dev/null;; + *) exit 1;; +esac + +exit $? + diff --git a/third_party/spirv-tools/tools/link/linker.cpp b/third_party/spirv-tools/tools/link/linker.cpp new file mode 100644 index 0000000..82d430e --- /dev/null +++ b/third_party/spirv-tools/tools/link/linker.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/spirv_target_env.h" +#include "source/table.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv-tools/linker.hpp" +#include "tools/io.h" + +void print_usage(char* argv0) { + std::string target_env_list = spvTargetEnvList(27, 95); + printf( + R"(%s - Link SPIR-V binary files together. + +USAGE: %s [options] [ ...] + +The SPIR-V binaries are read from the different . + +NOTE: The linker is a work in progress. + +Options: + -h, --help Print this help. + -o Name of the resulting linked SPIR-V binary. + --create-library Link the binaries into a library, keeping all exported symbols. + --allow-partial-linkage Allow partial linkage by accepting imported symbols to be unresolved. + --verify-ids Verify that IDs in the resulting modules are truly unique. + --version Display linker version information + --target-env {%s} + Use validation rules from the specified environment. +)", + argv0, argv0, target_env_list.c_str()); +} + +int main(int argc, char** argv) { + std::vector inFiles; + const char* outFile = nullptr; + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0; + spvtools::LinkerOptions options; + bool continue_processing = true; + int return_code = 0; + + for (int argi = 1; continue_processing && argi < argc; ++argi) { + const char* cur_arg = argv[argi]; + if ('-' == cur_arg[0]) { + if (0 == strcmp(cur_arg, "-o")) { + if (argi + 1 < argc) { + if (!outFile) { + outFile = argv[++argi]; + } else { + fprintf(stderr, "error: More than one output file specified\n"); + continue_processing = false; + return_code = 1; + } + } else { + fprintf(stderr, "error: Missing argument to %s\n", cur_arg); + continue_processing = false; + return_code = 1; + } + } else if (0 == strcmp(cur_arg, "--create-library")) { + options.SetCreateLibrary(true); + } else if (0 == strcmp(cur_arg, "--verify-ids")) { + options.SetVerifyIds(true); + } else if (0 == strcmp(cur_arg, "--allow-partial-linkage")) { + options.SetAllowPartialLinkage(true); + } else if (0 == strcmp(cur_arg, "--version")) { + printf("%s\n", spvSoftwareVersionDetailsString()); + // TODO(dneto): Add OpenCL 2.2 at least. + printf("Targets:\n %s\n %s\n %s\n", + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_0), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2)); + continue_processing = false; + return_code = 0; + } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { + print_usage(argv[0]); + continue_processing = false; + return_code = 0; + } else if (0 == strcmp(cur_arg, "--target-env")) { + if (argi + 1 < argc) { + const auto env_str = argv[++argi]; + if (!spvParseTargetEnv(env_str, &target_env)) { + fprintf(stderr, "error: Unrecognized target env: %s\n", env_str); + continue_processing = false; + return_code = 1; + } + } else { + fprintf(stderr, "error: Missing argument to --target-env\n"); + continue_processing = false; + return_code = 1; + } + } + } else { + inFiles.push_back(cur_arg); + } + } + + // Exit if command line parsing was not successful. + if (!continue_processing) { + return return_code; + } + + if (inFiles.empty()) { + fprintf(stderr, "error: No input file specified\n"); + return 1; + } + + std::vector> contents(inFiles.size()); + for (size_t i = 0u; i < inFiles.size(); ++i) { + if (!ReadFile(inFiles[i], "rb", &contents[i])) return 1; + } + + const spvtools::MessageConsumer consumer = [](spv_message_level_t level, + const char*, + const spv_position_t& position, + const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: " << position.index << ": " << message << std::endl; + break; + default: + break; + } + }; + spvtools::Context context(target_env); + context.SetMessageConsumer(consumer); + + std::vector linkingResult; + spv_result_t status = Link(context, contents, &linkingResult, options); + + if (!WriteFile(outFile, "wb", linkingResult.data(), + linkingResult.size())) + return 1; + + return status == SPV_SUCCESS ? 0 : 1; +} diff --git a/third_party/spirv-tools/tools/opt/opt.cpp b/third_party/spirv-tools/tools/opt/opt.cpp new file mode 100644 index 0000000..66f9228 --- /dev/null +++ b/third_party/spirv-tools/tools/opt/opt.cpp @@ -0,0 +1,926 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/log.h" +#include "source/spirv_target_env.h" +#include "source/util/string_utils.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv-tools/optimizer.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +namespace { + +// Status and actions to perform after parsing command-line arguments. +enum OptActions { OPT_CONTINUE, OPT_STOP }; + +struct OptStatus { + OptActions action; + int code; +}; + +// Message consumer for this tool. Used to emit diagnostics during +// initialization and setup. Note that |source| and |position| are irrelevant +// here because we are still not processing a SPIR-V input file. +void opt_diagnostic(spv_message_level_t level, const char* /*source*/, + const spv_position_t& /*positon*/, const char* message) { + if (level == SPV_MSG_ERROR) { + fprintf(stderr, "error: "); + } + fprintf(stderr, "%s\n", message); +} + +std::string GetListOfPassesAsString(const spvtools::Optimizer& optimizer) { + std::stringstream ss; + for (const auto& name : optimizer.GetPassNames()) { + ss << "\n\t\t" << name; + } + return ss.str(); +} + +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; + +std::string GetLegalizationPasses() { + spvtools::Optimizer optimizer(kDefaultEnvironment); + optimizer.RegisterLegalizationPasses(); + return GetListOfPassesAsString(optimizer); +} + +std::string GetOptimizationPasses() { + spvtools::Optimizer optimizer(kDefaultEnvironment); + optimizer.RegisterPerformancePasses(); + return GetListOfPassesAsString(optimizer); +} + +std::string GetSizePasses() { + spvtools::Optimizer optimizer(kDefaultEnvironment); + optimizer.RegisterSizePasses(); + return GetListOfPassesAsString(optimizer); +} + +std::string GetVulkanToWebGPUPasses() { + spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1); + optimizer.RegisterVulkanToWebGPUPasses(); + return GetListOfPassesAsString(optimizer); +} + +std::string GetWebGPUToVulkanPasses() { + spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0); + optimizer.RegisterWebGPUToVulkanPasses(); + return GetListOfPassesAsString(optimizer); +} + +void PrintUsage(const char* program) { + std::string target_env_list = spvTargetEnvList(16, 80); + // NOTE: Please maintain flags in lexicographical order. + printf( + R"(%s - Optimize a SPIR-V binary file. + +USAGE: %s [options] [] -o + +The SPIR-V binary is read from . If no file is specified, +or if is "-", then the binary is read from standard input. +if is "-", then the optimized output is written to +standard output. + +NOTE: The optimizer is a work in progress. + +Options (in lexicographical order):)", + program, program); + printf(R"( + --amd-ext-to-khr + Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader, + and VK_AMD_shader_trinary_minmax with equivalent code using core + instructions and capabilities.)"); + printf(R"( + --before-hlsl-legalization + Forwards this option to the validator. See the validator help + for details.)"); + printf(R"( + --ccp + Apply the conditional constant propagation transform. This will + propagate constant values throughout the program, and simplify + expressions and conditional jumps with known predicate + values. Performed on entry point call tree functions and + exported functions.)"); + printf(R"( + --cfg-cleanup + Cleanup the control flow graph. This will remove any unnecessary + code from the CFG like unreachable code. Performed on entry + point call tree functions and exported functions.)"); + printf(R"( + --combine-access-chains + Combines chained access chains to produce a single instruction + where possible.)"); + printf(R"( + --compact-ids + Remap result ids to a compact range starting from %%1 and without + any gaps.)"); + printf(R"( + --convert-local-access-chains + Convert constant index access chain loads/stores into + equivalent load/stores with inserts and extracts. Performed + on function scope variables referenced only with load, store, + and constant index access chains in entry point call tree + functions.)"); + printf(R"( + --convert-relaxed-to-half + Convert all RelaxedPrecision arithmetic operations to half + precision, inserting conversion operations where needed. + Run after function scope variable load and store elimination + for better results. Simplify-instructions, redundancy-elimination + and DCE should be run after this pass to eliminate excess + conversions. This conversion is useful when the target platform + does not support RelaxedPrecision or ignores it. This pass also + removes all RelaxedPrecision decorations.)"); + printf(R"( + --copy-propagate-arrays + Does propagation of memory references when an array is a copy of + another. It will only propagate an array if the source is never + written to, and the only store to the target is the copy.)"); + printf(R"( + --decompose-initialized-variables + Decomposes initialized variable declarations into a declaration + followed by a store of the initial value. This is done to work + around known issues with some Vulkan drivers for initialize + variables.)"); + printf(R"( + --descriptor-scalar-replacement + Replaces every array variable |desc| that has a DescriptorSet + and Binding decorations with a new variable for each element of + the array. Suppose |desc| was bound at binding |b|. Then the + variable corresponding to |desc[i]| will have binding |b+i|. + The descriptor set will be the same. All accesses to |desc| + must be in OpAccessChain instructions with a literal index for + the first index.)"); + printf(R"( + --eliminate-dead-branches + Convert conditional branches with constant condition to the + indicated unconditional branch. Delete all resulting dead + code. Performed only on entry point call tree functions.)"); + printf(R"( + --eliminate-dead-code-aggressive + Delete instructions which do not contribute to a function's + output. Performed only on entry point call tree functions.)"); + printf(R"( + --eliminate-dead-const + Eliminate dead constants.)"); + printf(R"( + --eliminate-dead-functions + Deletes functions that cannot be reached from entry points or + exported functions.)"); + printf(R"( + --eliminate-dead-inserts + Deletes unreferenced inserts into composites, most notably + unused stores to vector components, that are not removed by + aggressive dead code elimination.)"); + printf(R"( + --eliminate-dead-variables + Deletes module scope variables that are not referenced.)"); + printf(R"( + --eliminate-insert-extract + DEPRECATED. This pass has been replaced by the simplification + pass, and that pass will be run instead. + See --simplify-instructions.)"); + printf(R"( + --eliminate-local-multi-store + Replace stores and loads of function scope variables that are + stored multiple times. Performed on variables referenceed only + with loads and stores. Performed only on entry point call tree + functions.)"); + printf(R"( + --eliminate-local-single-block + Perform single-block store/load and load/load elimination. + Performed only on function scope variables in entry point + call tree functions.)"); + printf(R"( + --eliminate-local-single-store + Replace stores and loads of function scope variables that are + only stored once. Performed on variables referenceed only with + loads and stores. Performed only on entry point call tree + functions.)"); + printf(R"( + --flatten-decorations + Replace decoration groups with repeated OpDecorate and + OpMemberDecorate instructions.)"); + printf(R"( + --fold-spec-const-op-composite + Fold the spec constants defined by OpSpecConstantOp or + OpSpecConstantComposite instructions to front-end constants + when possible.)"); + printf(R"( + --freeze-spec-const + Freeze the values of specialization constants to their default + values.)"); + printf(R"( + --graphics-robust-access + Clamp indices used to access buffers and internal composite + values, providing guarantees that satisfy Vulkan's + robustBufferAccess rules.)"); + printf(R"( + --generate-webgpu-initializers + Adds initial values to OpVariable instructions that are missing + them, due to their storage type requiring them for WebGPU.)"); + printf(R"( + --if-conversion + Convert if-then-else like assignments into OpSelect.)"); + printf(R"( + --inline-entry-points-exhaustive + Exhaustively inline all function calls in entry point call tree + functions. Currently does not inline calls to functions with + early return in a loop.)"); + printf(R"( + --legalize-hlsl + Runs a series of optimizations that attempts to take SPIR-V + generated by an HLSL front-end and generates legal Vulkan SPIR-V. + The optimizations are: + %s + + Note this does not guarantee legal code. This option passes the + option --relax-logical-pointer to the validator.)", + GetLegalizationPasses().c_str()); + printf(R"( + --legalize-vector-shuffle + Converts any usages of 0xFFFFFFFF for the literals in + OpVectorShuffle to a literal 0. This is done since 0xFFFFFFFF is + forbidden in WebGPU.)"); + printf(R"( + --local-redundancy-elimination + Looks for instructions in the same basic block that compute the + same value, and deletes the redundant ones.)"); + printf(R"( + --loop-fission + Splits any top level loops in which the register pressure has + exceeded a given threshold. The threshold must follow the use of + this flag and must be a positive integer value.)"); + printf(R"( + --loop-fusion + Identifies adjacent loops with the same lower and upper bound. + If this is legal, then merge the loops into a single loop. + Includes heuristics to ensure it does not increase number of + registers too much, while reducing the number of loads from + memory. Takes an additional positive integer argument to set + the maximum number of registers.)"); + printf(R"( + --loop-invariant-code-motion + Identifies code in loops that has the same value for every + iteration of the loop, and move it to the loop pre-header.)"); + printf(R"( + --loop-unroll + Fully unrolls loops marked with the Unroll flag)"); + printf(R"( + --loop-unroll-partial + Partially unrolls loops marked with the Unroll flag. Takes an + additional non-0 integer argument to set the unroll factor, or + how many times a loop body should be duplicated)"); + printf(R"( + --loop-peeling + Execute few first (respectively last) iterations before + (respectively after) the loop if it can elide some branches.)"); + printf(R"( + --loop-peeling-threshold + Takes a non-0 integer argument to set the loop peeling code size + growth threshold. The threshold prevents the loop peeling + from happening if the code size increase created by + the optimization is above the threshold.)"); + printf(R"( + --max-id-bound= + Sets the maximum value for the id bound for the module. The + default is the minimum value for this limit, 0x3FFFFF. See + section 2.17 of the Spir-V specification.)"); + printf(R"( + --merge-blocks + Join two blocks into a single block if the second has the + first as its only predecessor. Performed only on entry point + call tree functions.)"); + printf(R"( + --merge-return + Changes functions that have multiple return statements so they + have a single return statement. + + For structured control flow it is assumed that the only + unreachable blocks in the function are trivial merge and continue + blocks. + + A trivial merge block contains the label and an OpUnreachable + instructions, nothing else. A trivial continue block contain a + label and an OpBranch to the header, nothing else. + + These conditions are guaranteed to be met after running + dead-branch elimination.)"); + printf(R"( + --loop-unswitch + Hoists loop-invariant conditionals out of loops by duplicating + the loop on each branch of the conditional and adjusting each + copy of the loop.)"); + printf(R"( + -O + Optimize for performance. Apply a sequence of transformations + in an attempt to improve the performance of the generated + code. For this version of the optimizer, this flag is equivalent + to specifying the following optimization code names: + %s)", + GetOptimizationPasses().c_str()); + printf(R"( + -Os + Optimize for size. Apply a sequence of transformations in an + attempt to minimize the size of the generated code. For this + version of the optimizer, this flag is equivalent to specifying + the following optimization code names: + %s + + NOTE: The specific transformations done by -O and -Os change + from release to release.)", + GetSizePasses().c_str()); + printf(R"( + -Oconfig= + Apply the sequence of transformations indicated in . + This file contains a sequence of strings separated by whitespace + (tabs, newlines or blanks). Each string is one of the flags + accepted by spirv-opt. Optimizations will be applied in the + sequence they appear in the file. This is equivalent to + specifying all the flags on the command line. For example, + given the file opts.cfg with the content: + + --inline-entry-points-exhaustive + --eliminate-dead-code-aggressive + + The following two invocations to spirv-opt are equivalent: + + $ spirv-opt -Oconfig=opts.cfg program.spv + + $ spirv-opt --inline-entry-points-exhaustive \ + --eliminate-dead-code-aggressive program.spv + + Lines starting with the character '#' in the configuration + file indicate a comment and will be ignored. + + The -O, -Os, and -Oconfig flags act as macros. Using one of them + is equivalent to explicitly inserting the underlying flags at + that position in the command line. For example, the invocation + 'spirv-opt --merge-blocks -O ...' applies the transformation + --merge-blocks followed by all the transformations implied by + -O.)"); + printf(R"( + --preserve-bindings + Ensure that the optimizer preserves all bindings declared within + the module, even when those bindings are unused.)"); + printf(R"( + --preserve-spec-constants + Ensure that the optimizer preserves all specialization constants declared + within the module, even when those constants are unused.)"); + printf(R"( + --print-all + Print SPIR-V assembly to standard error output before each pass + and after the last pass.)"); + printf(R"( + --private-to-local + Change the scope of private variables that are used in a single + function to that function.)"); + printf(R"( + --reduce-load-size + Replaces loads of composite objects where not every component is + used by loads of just the elements that are used.)"); + printf(R"( + --redundancy-elimination + Looks for instructions in the same function that compute the + same value, and deletes the redundant ones.)"); + printf(R"( + --relax-block-layout + Forwards this option to the validator. See the validator help + for details.)"); + printf(R"( + --relax-float-ops + Decorate all float operations with RelaxedPrecision if not already + so decorated. This does not decorate types or variables.)"); + printf(R"( + --relax-logical-pointer + Forwards this option to the validator. See the validator help + for details.)"); + printf(R"( + --relax-struct-store + Forwards this option to the validator. See the validator help + for details.)"); + printf(R"( + --remove-duplicates + Removes duplicate types, decorations, capabilities and extension + instructions.)"); + printf(R"( + --replace-invalid-opcode + Replaces instructions whose opcode is valid for shader modules, + but not for the current shader stage. To have an effect, all + entry points must have the same execution model.)"); + printf(R"( + --ssa-rewrite + Replace loads and stores to function local variables with + operations on SSA IDs.)"); + printf(R"( + --scalar-block-layout + Forwards this option to the validator. See the validator help + for details.)"); + printf(R"( + --scalar-replacement[=] + Replace aggregate function scope variables that are only accessed + via their elements with new function variables representing each + element. is a limit on the size of the aggregates that will + be replaced. 0 means there is no limit. The default value is + 100.)"); + printf(R"( + --set-spec-const-default-value ": ..." + Set the default values of the specialization constants with + : pairs specified in a double-quoted + string. : pairs must be separated by + blank spaces, and in each pair, spec id and default value must + be separated with colon ':' without any blank spaces in between. + e.g.: --set-spec-const-default-value "1:100 2:400")"); + printf(R"( + --simplify-instructions + Will simplify all instructions in the function as much as + possible.)"); + printf(R"( + --skip-block-layout + Forwards this option to the validator. See the validator help + for details.)"); + printf(R"( + --split-invalid-unreachable + Attempts to legalize for WebGPU cases where an unreachable + merge-block is also a continue-target by splitting it into two + separate blocks. There exist legal, for Vulkan, instances of this + pattern that cannot be converted into legal WebGPU, so this + conversion may not succeed.)"); + printf(R"( + --skip-validation + Will not validate the SPIR-V before optimizing. If the SPIR-V + is invalid, the optimizer may fail or generate incorrect code. + This options should be used rarely, and with caution.)"); + printf(R"( + --strength-reduction + Replaces instructions with equivalent and less expensive ones.)"); + printf(R"( + --strip-atomic-counter-memory + Removes AtomicCountMemory bit from memory semantics values.)"); + printf(R"( + --strip-debug + Remove all debug instructions.)"); + printf(R"( + --strip-reflect + Remove all reflection information. For now, this covers + reflection information defined by SPV_GOOGLE_hlsl_functionality1 + and SPV_KHR_non_semantic_info)"); + printf(R"( + --target-env= + Set the target environment. Without this flag the target + environment defaults to spv1.5. must be one of + {%s})", + target_env_list.c_str()); + printf(R"( + --time-report + Print the resource utilization of each pass (e.g., CPU time, + RSS) to standard error output. Currently it supports only Unix + systems. This option is the same as -ftime-report in GCC. It + prints CPU/WALL/USR/SYS time (and RSS if possible), but note that + USR/SYS time are returned by getrusage() and can have a small + error.)"); + printf(R"( + --upgrade-memory-model + Upgrades the Logical GLSL450 memory model to Logical VulkanKHR. + Transforms memory, image, atomic and barrier operations to conform + to that model's requirements.)"); + printf(R"( + --vector-dce + This pass looks for components of vectors that are unused, and + removes them from the vector. Note this would still leave around + lots of dead code that a pass of ADCE will be able to remove.)"); + printf(R"( + --vulkan-to-webgpu + Turns on the prescribed passes for converting from Vulkan to + WebGPU and sets the target environment to webgpu0. Other passes + may be turned on via additional flags, but such combinations are + not tested. + Using --target-env with this flag is not allowed. + + This flag is the equivalent of passing in --target-env=webgpu0 + and specifying the following optimization code names: + %s + + NOTE: This flag is a WIP and its behaviour is subject to change.)", + GetVulkanToWebGPUPasses().c_str()); + printf(R"( + --webgpu-to-vulkan + Turns on the prescribed passes for converting from WebGPU to + Vulkan and sets the target environment to vulkan1.1. Other passes + may be turned on via additional flags, but such combinations are + not tested. + Using --target-env with this flag is not allowed. + + This flag is the equivalent of passing in --target-env=vulkan1.1 + and specifying the following optimization code names: + %s + + NOTE: This flag is a WIP and its behaviour is subject to change.)", + GetWebGPUToVulkanPasses().c_str()); + printf(R"( + --workaround-1209 + Rewrites instructions for which there are known driver bugs to + avoid triggering those bugs. + Current workarounds: Avoid OpUnreachable in loops.)"); + printf(R"( + --wrap-opkill + Replaces all OpKill instructions in functions that can be called + from a continue construct with a function call to a function + whose only instruction is an OpKill. This is done to enable + inlining on these functions. + )"); + printf(R"( + --unify-const + Remove the duplicated constants.)"); + printf(R"( + --validate-after-all + Validate the module after each pass is performed.)"); + printf(R"( + -h, --help + Print this help.)"); + printf(R"( + --version + Display optimizer version information. +)"); +} + +// Reads command-line flags the file specified in |oconfig_flag|. This string +// is assumed to have the form "-Oconfig=FILENAME". This function parses the +// string and extracts the file name after the '=' sign. +// +// Flags found in |FILENAME| are pushed at the end of the vector |file_flags|. +// +// This function returns true on success, false on failure. +bool ReadFlagsFromFile(const char* oconfig_flag, + std::vector* file_flags) { + const char* fname = strchr(oconfig_flag, '='); + if (fname == nullptr || fname[0] != '=') { + spvtools::Errorf(opt_diagnostic, nullptr, {}, "Invalid -Oconfig flag %s", + oconfig_flag); + return false; + } + fname++; + + std::ifstream input_file; + input_file.open(fname); + if (input_file.fail()) { + spvtools::Errorf(opt_diagnostic, nullptr, {}, "Could not open file '%s'", + fname); + return false; + } + + std::string line; + while (std::getline(input_file, line)) { + // Ignore empty lines and lines starting with the comment marker '#'. + if (line.length() == 0 || line[0] == '#') { + continue; + } + + // Tokenize the line. Add all found tokens to the list of found flags. This + // mimics the way the shell will parse whitespace on the command line. NOTE: + // This does not support quoting and it is not intended to. + std::istringstream iss(line); + while (!iss.eof()) { + std::string flag; + iss >> flag; + file_flags->push_back(flag); + } + } + + return true; +} + +OptStatus ParseFlags(int argc, const char** argv, + spvtools::Optimizer* optimizer, const char** in_file, + const char** out_file, + spvtools::ValidatorOptions* validator_options, + spvtools::OptimizerOptions* optimizer_options); + +// Parses and handles the -Oconfig flag. |prog_name| contains the name of +// the spirv-opt binary (used to build a new argv vector for the recursive +// invocation to ParseFlags). |opt_flag| contains the -Oconfig=FILENAME flag. +// |optimizer|, |in_file|, |out_file|, |validator_options|, and +// |optimizer_options| are as in ParseFlags. +// +// This returns the same OptStatus instance returned by ParseFlags. +OptStatus ParseOconfigFlag(const char* prog_name, const char* opt_flag, + spvtools::Optimizer* optimizer, const char** in_file, + const char** out_file, + spvtools::ValidatorOptions* validator_options, + spvtools::OptimizerOptions* optimizer_options) { + std::vector flags; + flags.push_back(prog_name); + + std::vector file_flags; + if (!ReadFlagsFromFile(opt_flag, &file_flags)) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "Could not read optimizer flags from configuration file"); + return {OPT_STOP, 1}; + } + flags.insert(flags.end(), file_flags.begin(), file_flags.end()); + + const char** new_argv = new const char*[flags.size()]; + for (size_t i = 0; i < flags.size(); i++) { + if (flags[i].find("-Oconfig=") != std::string::npos) { + spvtools::Error( + opt_diagnostic, nullptr, {}, + "Flag -Oconfig= may not be used inside the configuration file"); + return {OPT_STOP, 1}; + } + new_argv[i] = flags[i].c_str(); + } + + auto ret_val = + ParseFlags(static_cast(flags.size()), new_argv, optimizer, in_file, + out_file, validator_options, optimizer_options); + delete[] new_argv; + return ret_val; +} + +// Canonicalize the flag in |argv[argi]| of the form '--pass arg' into +// '--pass=arg'. The optimizer only accepts arguments to pass names that use the +// form '--pass_name=arg'. Since spirv-opt also accepts the other form, this +// function makes the necessary conversion. +// +// Pass flags that require additional arguments should be handled here. Note +// that additional arguments should be given as a single string. If the flag +// requires more than one argument, the pass creator in +// Optimizer::GetPassFromFlag() should parse it accordingly (e.g., see the +// handler for --set-spec-const-default-value). +// +// If the argument requests one of the passes that need an additional argument, +// |argi| is modified to point past the current argument, and the string +// "argv[argi]=argv[argi + 1]" is returned. Otherwise, |argi| is unmodified and +// the string "|argv[argi]|" is returned. +std::string CanonicalizeFlag(const char** argv, int argc, int* argi) { + const char* cur_arg = argv[*argi]; + const char* next_arg = (*argi + 1 < argc) ? argv[*argi + 1] : nullptr; + std::ostringstream canonical_arg; + canonical_arg << cur_arg; + + // NOTE: DO NOT ADD NEW FLAGS HERE. + // + // These flags are supported for backwards compatibility. When adding new + // passes that need extra arguments in its command-line flag, please make them + // use the syntax "--pass_name[=pass_arg]. + if (0 == strcmp(cur_arg, "--set-spec-const-default-value") || + 0 == strcmp(cur_arg, "--loop-fission") || + 0 == strcmp(cur_arg, "--loop-fusion") || + 0 == strcmp(cur_arg, "--loop-unroll-partial") || + 0 == strcmp(cur_arg, "--loop-peeling-threshold")) { + if (next_arg) { + canonical_arg << "=" << next_arg; + ++(*argi); + } + } + + return canonical_arg.str(); +} + +// Parses command-line flags. |argc| contains the number of command-line flags. +// |argv| points to an array of strings holding the flags. |optimizer| is the +// Optimizer instance used to optimize the program. +// +// On return, this function stores the name of the input program in |in_file|. +// The name of the output file in |out_file|. The return value indicates whether +// optimization should continue and a status code indicating an error or +// success. +OptStatus ParseFlags(int argc, const char** argv, + spvtools::Optimizer* optimizer, const char** in_file, + const char** out_file, + spvtools::ValidatorOptions* validator_options, + spvtools::OptimizerOptions* optimizer_options) { + std::vector pass_flags; + bool target_env_set = false; + bool vulkan_to_webgpu_set = false; + bool webgpu_to_vulkan_set = false; + for (int argi = 1; argi < argc; ++argi) { + const char* cur_arg = argv[argi]; + if ('-' == cur_arg[0]) { + if (0 == strcmp(cur_arg, "--version")) { + spvtools::Logf(opt_diagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n", + spvSoftwareVersionDetailsString()); + return {OPT_STOP, 0}; + } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { + PrintUsage(argv[0]); + return {OPT_STOP, 0}; + } else if (0 == strcmp(cur_arg, "-o")) { + if (!*out_file && argi + 1 < argc) { + *out_file = argv[++argi]; + } else { + PrintUsage(argv[0]); + return {OPT_STOP, 1}; + } + } else if ('\0' == cur_arg[1]) { + // Setting a filename of "-" to indicate stdin. + if (!*in_file) { + *in_file = cur_arg; + } else { + spvtools::Error(opt_diagnostic, nullptr, {}, + "More than one input file specified"); + return {OPT_STOP, 1}; + } + } else if (0 == strncmp(cur_arg, "-Oconfig=", sizeof("-Oconfig=") - 1)) { + OptStatus status = + ParseOconfigFlag(argv[0], cur_arg, optimizer, in_file, out_file, + validator_options, optimizer_options); + if (status.action != OPT_CONTINUE) { + return status; + } + } else if (0 == strcmp(cur_arg, "--skip-validation")) { + optimizer_options->set_run_validator(false); + } else if (0 == strcmp(cur_arg, "--print-all")) { + optimizer->SetPrintAll(&std::cerr); + } else if (0 == strcmp(cur_arg, "--preserve-bindings")) { + optimizer_options->set_preserve_bindings(true); + } else if (0 == strcmp(cur_arg, "--preserve-spec-constants")) { + optimizer_options->set_preserve_spec_constants(true); + } else if (0 == strcmp(cur_arg, "--time-report")) { + optimizer->SetTimeReport(&std::cerr); + } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { + validator_options->SetRelaxStructStore(true); + } else if (0 == strncmp(cur_arg, "--max-id-bound=", + sizeof("--max-id-bound=") - 1)) { + auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + // Will not allow values in the range [2^31,2^32). + uint32_t max_id_bound = + static_cast(atoi(split_flag.second.c_str())); + + // That SPIR-V mandates the minimum value for max id bound but + // implementations may allow higher minimum bounds. + if (max_id_bound < kDefaultMaxIdBound) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "The max id bound must be at least 0x3FFFFF"); + return {OPT_STOP, 1}; + } + optimizer_options->set_max_id_bound(max_id_bound); + validator_options->SetUniversalLimit(spv_validator_limit_max_id_bound, + max_id_bound); + } else if (0 == strncmp(cur_arg, + "--target-env=", sizeof("--target-env=") - 1)) { + target_env_set = true; + if (vulkan_to_webgpu_set) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "--vulkan-to-webgpu defines the target environment, " + "so --target-env cannot be set at the same time"); + return {OPT_STOP, 1}; + } + if (webgpu_to_vulkan_set) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "--webgpu-to-vulkan defines the target environment, " + "so --target-env cannot be set at the same time"); + return {OPT_STOP, 1}; + } + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + const auto target_env_str = split_flag.second.c_str(); + spv_target_env target_env; + if (!spvParseTargetEnv(target_env_str, &target_env)) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "Invalid value passed to --target-env"); + return {OPT_STOP, 1}; + } + optimizer->SetTargetEnv(target_env); + } else if (0 == strcmp(cur_arg, "--vulkan-to-webgpu")) { + vulkan_to_webgpu_set = true; + if (target_env_set) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "--vulkan-to-webgpu defines the target environment, " + "so --target-env cannot be set at the same time"); + return {OPT_STOP, 1}; + } + if (webgpu_to_vulkan_set) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "Cannot use both --webgpu-to-vulkan and " + "--vulkan-to-webgpu at the same time, invoke twice " + "if you are wanting to go to and from"); + return {OPT_STOP, 1}; + } + + optimizer->SetTargetEnv(SPV_ENV_VULKAN_1_1); + optimizer->RegisterVulkanToWebGPUPasses(); + } else if (0 == strcmp(cur_arg, "--webgpu-to-vulkan")) { + webgpu_to_vulkan_set = true; + if (target_env_set) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "--webgpu-to-vulkan defines the target environment, " + "so --target-env cannot be set at the same time"); + return {OPT_STOP, 1}; + } + if (vulkan_to_webgpu_set) { + spvtools::Error(opt_diagnostic, nullptr, {}, + "Cannot use both --webgpu-to-vulkan and " + "--vulkan-to-webgpu at the same time, invoke twice " + "if you are wanting to go to and from"); + return {OPT_STOP, 1}; + } + + optimizer->SetTargetEnv(SPV_ENV_WEBGPU_0); + optimizer->RegisterWebGPUToVulkanPasses(); + } else if (0 == strcmp(cur_arg, "--validate-after-all")) { + optimizer->SetValidateAfterAll(true); + } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) { + validator_options->SetBeforeHlslLegalization(true); + } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) { + validator_options->SetRelaxLogicalPointer(true); + } else if (0 == strcmp(cur_arg, "--relax-block-layout")) { + validator_options->SetRelaxBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) { + validator_options->SetScalarBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { + validator_options->SetSkipBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { + validator_options->SetRelaxStructStore(true); + } else { + // Some passes used to accept the form '--pass arg', canonicalize them + // to '--pass=arg'. + pass_flags.push_back(CanonicalizeFlag(argv, argc, &argi)); + + // If we were requested to legalize SPIR-V generated from the HLSL + // front-end, skip validation. + if (0 == strcmp(cur_arg, "--legalize-hlsl")) { + validator_options->SetBeforeHlslLegalization(true); + } + } + } else { + if (!*in_file) { + *in_file = cur_arg; + } else { + spvtools::Error(opt_diagnostic, nullptr, {}, + "More than one input file specified"); + return {OPT_STOP, 1}; + } + } + } + + if (!optimizer->RegisterPassesFromFlags(pass_flags)) { + return {OPT_STOP, 1}; + } + + return {OPT_CONTINUE, 0}; +} + +} // namespace + +int main(int argc, const char** argv) { + const char* in_file = nullptr; + const char* out_file = nullptr; + + spv_target_env target_env = kDefaultEnvironment; + + spvtools::Optimizer optimizer(target_env); + optimizer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + + spvtools::ValidatorOptions validator_options; + spvtools::OptimizerOptions optimizer_options; + OptStatus status = ParseFlags(argc, argv, &optimizer, &in_file, &out_file, + &validator_options, &optimizer_options); + optimizer_options.set_validator_options(validator_options); + + if (status.action == OPT_STOP) { + return status.code; + } + + if (out_file == nullptr) { + spvtools::Error(opt_diagnostic, nullptr, {}, "-o required"); + return 1; + } + + std::vector binary; + if (!ReadFile(in_file, "rb", &binary)) { + return 1; + } + + // By using the same vector as input and output, we save time in the case + // that there was no change. + bool ok = + optimizer.Run(binary.data(), binary.size(), &binary, optimizer_options); + + if (!WriteFile(out_file, "wb", binary.data(), binary.size())) { + return 1; + } + + return ok ? 0 : 1; +} diff --git a/third_party/spirv-tools/tools/reduce/reduce.cpp b/third_party/spirv-tools/tools/reduce/reduce.cpp new file mode 100644 index 0000000..49a5efe --- /dev/null +++ b/third_party/spirv-tools/tools/reduce/reduce.cpp @@ -0,0 +1,368 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/opt/log.h" +#include "source/reduce/reducer.h" +#include "source/spirv_reducer_options.h" +#include "source/util/string_utils.h" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +namespace { + +// Check that the std::system function can actually be used. +bool CheckExecuteCommand() { + int res = std::system(nullptr); + return res != 0; +} + +// Execute a command using the shell. +// Returns true if and only if the command's exit status was 0. +bool ExecuteCommand(const std::string& command) { + errno = 0; + int status = std::system(command.c_str()); + assert(errno == 0 && "failed to execute command"); + // The result returned by 'system' is implementation-defined, but is + // usually the case that the returned value is 0 when the command's exit + // code was 0. We are assuming that here, and that's all we depend on. + return status == 0; +} + +// Status and actions to perform after parsing command-line arguments. +enum ReduceActions { REDUCE_CONTINUE, REDUCE_STOP }; + +struct ReduceStatus { + ReduceActions action; + int code; +}; + +void PrintUsage(const char* program) { + // NOTE: Please maintain flags in lexicographical order. + printf( + R"(%s - Reduce a SPIR-V binary file with respect to a user-provided +interestingness test. + +USAGE: %s [options] -o -- [args...] + +The SPIR-V binary is read from . The reduced SPIR-V binary is +written to . + +Whether a binary is interesting is determined by , which +should be the path to a script. The "--" characters are optional but denote +that all arguments that follow are positional arguments and thus will be +forwarded to the interestingness test, and not parsed by %s. + + * The script must be executable. + + * The script should take the path to a SPIR-V binary file (.spv) as an + argument, and exit with code 0 if and only if the binary file is + interesting. The binary will be passed to the script as an argument after + any other provided arguments [args...]. + + * Example: an interestingness test for reducing a SPIR-V binary file that + causes tool "foo" to exit with error code 1 and print "Fatal error: bar" to + standard error should: + - invoke "foo" on the binary passed as the script argument; + - capture the return code and standard error from "bar"; + - exit with code 0 if and only if the return code of "foo" was 1 and the + standard error from "bar" contained "Fatal error: bar". + + * The reducer does not place a time limit on how long the interestingness test + takes to run, so it is advisable to use per-command timeouts inside the + script when invoking SPIR-V-processing tools (such as "foo" in the above + example). + +NOTE: The reducer is a work in progress. + +Options (in lexicographical order): + + --fail-on-validation-error + Stop reduction with an error if any reduction step produces a + SPIR-V module that fails to validate. + -h, --help + Print this help. + --step-limit= + 32-bit unsigned integer specifying maximum number of steps the + reducer will take before giving up. + --target-function= + 32-bit unsigned integer specifying the id of a function in the + input module. The reducer will restrict attention to this + function, and will not make changes to other functions or to + instructions outside of functions, except that some global + instructions may be added in support of reducing the target + function. If 0 is specified (the default) then all functions are + reduced. + --temp-file-prefix= + Specifies a temporary file prefix that will be used to output + temporary shader files during reduction. A number and .spv + extension will be added. The default is "temp_", which will + cause files like "temp_0001.spv" to be output to the current + directory. + --version + Display reducer version information. + +Supported validator options are as follows. See `spirv-val --help` for details. + --before-hlsl-legalization + --relax-block-layout + --relax-logical-pointer + --relax-struct-store + --scalar-block-layout + --skip-block-layout +)", + program, program, program); +} + +// Message consumer for this tool. Used to emit diagnostics during +// initialization and setup. Note that |source| and |position| are irrelevant +// here because we are still not processing a SPIR-V input file. +void ReduceDiagnostic(spv_message_level_t level, const char* /*source*/, + const spv_position_t& /*position*/, const char* message) { + if (level == SPV_MSG_ERROR) { + fprintf(stderr, "error: "); + } + fprintf(stderr, "%s\n", message); +} + +ReduceStatus ParseFlags(int argc, const char** argv, + std::string* in_binary_file, + std::string* out_binary_file, + std::vector* interestingness_test, + std::string* temp_file_prefix, + spvtools::ReducerOptions* reducer_options, + spvtools::ValidatorOptions* validator_options) { + uint32_t positional_arg_index = 0; + bool only_positional_arguments_remain = false; + + for (int argi = 1; argi < argc; ++argi) { + const char* cur_arg = argv[argi]; + if ('-' == cur_arg[0] && !only_positional_arguments_remain) { + if (0 == strcmp(cur_arg, "--version")) { + spvtools::Logf(ReduceDiagnostic, SPV_MSG_INFO, nullptr, {}, "%s\n", + spvSoftwareVersionDetailsString()); + return {REDUCE_STOP, 0}; + } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { + PrintUsage(argv[0]); + return {REDUCE_STOP, 0}; + } else if (0 == strcmp(cur_arg, "-o")) { + if (out_binary_file->empty() && argi + 1 < argc) { + *out_binary_file = std::string(argv[++argi]); + } else { + PrintUsage(argv[0]); + return {REDUCE_STOP, 1}; + } + } else if (0 == strncmp(cur_arg, + "--step-limit=", sizeof("--step-limit=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + char* end = nullptr; + errno = 0; + const auto step_limit = + static_cast(strtol(split_flag.second.c_str(), &end, 10)); + assert(end != split_flag.second.c_str() && errno == 0); + reducer_options->set_step_limit(step_limit); + } else if (0 == strncmp(cur_arg, "--target-function=", + sizeof("--target-function=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + char* end = nullptr; + errno = 0; + const auto target_function = + static_cast(strtol(split_flag.second.c_str(), &end, 10)); + assert(end != split_flag.second.c_str() && errno == 0); + reducer_options->set_target_function(target_function); + } else if (0 == strcmp(cur_arg, "--fail-on-validation-error")) { + reducer_options->set_fail_on_validation_error(true); + } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) { + validator_options->SetBeforeHlslLegalization(true); + } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) { + validator_options->SetRelaxLogicalPointer(true); + } else if (0 == strcmp(cur_arg, "--relax-block-layout")) { + validator_options->SetRelaxBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) { + validator_options->SetScalarBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { + validator_options->SetSkipBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { + validator_options->SetRelaxStructStore(true); + } else if (0 == strncmp(cur_arg, "--temp-file-prefix=", + sizeof("--temp-file-prefix=") - 1)) { + const auto split_flag = spvtools::utils::SplitFlagArgs(cur_arg); + *temp_file_prefix = std::string(split_flag.second); + } else if (0 == strcmp(cur_arg, "--")) { + only_positional_arguments_remain = true; + } else { + std::stringstream ss; + ss << "Unrecognized argument: " << cur_arg << std::endl; + spvtools::Error(ReduceDiagnostic, nullptr, {}, ss.str().c_str()); + PrintUsage(argv[0]); + return {REDUCE_STOP, 1}; + } + } else if (positional_arg_index == 0) { + // Binary input file name + assert(in_binary_file->empty()); + *in_binary_file = std::string(cur_arg); + positional_arg_index++; + } else { + interestingness_test->push_back(std::string(cur_arg)); + } + } + + if (in_binary_file->empty()) { + spvtools::Error(ReduceDiagnostic, nullptr, {}, "No input file specified"); + return {REDUCE_STOP, 1}; + } + + if (out_binary_file->empty()) { + spvtools::Error(ReduceDiagnostic, nullptr, {}, "-o required"); + return {REDUCE_STOP, 1}; + } + + if (interestingness_test->empty()) { + spvtools::Error(ReduceDiagnostic, nullptr, {}, + "No interestingness test specified"); + return {REDUCE_STOP, 1}; + } + + return {REDUCE_CONTINUE, 0}; +} + +} // namespace + +// Dumps |binary| to file |filename|. Useful for interactive debugging. +void DumpShader(const std::vector& binary, const char* filename) { + auto write_file_succeeded = + WriteFile(filename, "wb", &binary[0], binary.size()); + if (!write_file_succeeded) { + std::cerr << "Failed to dump shader" << std::endl; + } +} + +// Dumps the SPIRV-V module in |context| to file |filename|. Useful for +// interactive debugging. +void DumpShader(spvtools::opt::IRContext* context, const char* filename) { + std::vector binary; + context->module()->ToBinary(&binary, false); + DumpShader(binary, filename); +} + +const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5; + +int main(int argc, const char** argv) { + std::string in_binary_file; + std::string out_binary_file; + std::vector interestingness_test; + std::string temp_file_prefix = "temp_"; + + spv_target_env target_env = kDefaultEnvironment; + spvtools::ReducerOptions reducer_options; + spvtools::ValidatorOptions validator_options; + + ReduceStatus status = ParseFlags( + argc, argv, &in_binary_file, &out_binary_file, &interestingness_test, + &temp_file_prefix, &reducer_options, &validator_options); + + if (status.action == REDUCE_STOP) { + return status.code; + } + + if (!CheckExecuteCommand()) { + std::cerr << "could not find shell interpreter for executing a command" + << std::endl; + return 2; + } + + spvtools::reduce::Reducer reducer(target_env); + + std::stringstream joined; + joined << interestingness_test[0]; + for (size_t i = 1, size = interestingness_test.size(); i < size; ++i) { + joined << " " << interestingness_test[i]; + } + std::string interestingness_command_joined = joined.str(); + + reducer.SetInterestingnessFunction( + [interestingness_command_joined, temp_file_prefix]( + std::vector binary, uint32_t reductions_applied) -> bool { + std::stringstream ss; + ss << temp_file_prefix << std::setw(4) << std::setfill('0') + << reductions_applied << ".spv"; + const auto spv_file = ss.str(); + const std::string command = + interestingness_command_joined + " " + spv_file; + auto write_file_succeeded = + WriteFile(spv_file.c_str(), "wb", &binary[0], binary.size()); + (void)(write_file_succeeded); + assert(write_file_succeeded); + return ExecuteCommand(command); + }); + + reducer.AddDefaultReductionPasses(); + + reducer.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + + std::vector binary_in; + if (!ReadFile(in_binary_file.c_str(), "rb", &binary_in)) { + return 1; + } + + const uint32_t target_function = (*reducer_options).target_function; + if (target_function) { + // A target function was specified; check that it exists. + std::unique_ptr context = spvtools::BuildModule( + kDefaultEnvironment, spvtools::utils::CLIMessageConsumer, + binary_in.data(), binary_in.size()); + bool found_target_function = false; + for (auto& function : *context->module()) { + if (function.result_id() == target_function) { + found_target_function = true; + break; + } + } + if (!found_target_function) { + std::stringstream strstr; + strstr << "Target function with id " << target_function + << " was requested, but not found in the module; stopping."; + spvtools::utils::CLIMessageConsumer(SPV_MSG_ERROR, nullptr, {}, + strstr.str().c_str()); + return 1; + } + } + + std::vector binary_out; + const auto reduction_status = reducer.Run(std::move(binary_in), &binary_out, + reducer_options, validator_options); + + // Always try to write the output file, even if the reduction failed. + if (!WriteFile(out_binary_file.c_str(), "wb", binary_out.data(), + binary_out.size())) { + return 1; + } + + // These are the only successful statuses. + switch (reduction_status) { + case spvtools::reduce::Reducer::ReductionResultStatus::kComplete: + case spvtools::reduce::Reducer::ReductionResultStatus::kReachedStepLimit: + return 0; + default: + break; + } + + return 1; +} diff --git a/third_party/spirv-tools/tools/sva/.eslintrc.json b/third_party/spirv-tools/tools/sva/.eslintrc.json new file mode 100644 index 0000000..2f07726 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "env": { + "browser": true, + "es6": true, + "node": true, + "mocha": true + }, + "extends": "eslint:recommended", + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "rules": { + "block-scoped-var": "error", + "consistent-return": "error", + "eqeqeq": ["error", "always"], + "indent": [ "error", 2 ], + "linebreak-style": [ "error", "unix" ], + "no-eval": "error", + "no-shadow": "error", + "no-shadow-restricted-names": "error", + "quotes": [ "error", "double" ], + "semi": [ "error", "always" ] + } +} diff --git a/third_party/spirv-tools/tools/sva/.gitignore b/third_party/spirv-tools/tools/sva/.gitignore new file mode 100644 index 0000000..88e64c3 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +node_modules +third_party/spirv-headers +o.sva +build +yarn-error.log diff --git a/third_party/spirv-tools/tools/sva/README.md b/third_party/spirv-tools/tools/sva/README.md new file mode 100644 index 0000000..d80b4d2 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/README.md @@ -0,0 +1,41 @@ +# SVA + +SPIR-V Assember for WebGPU. The SPIR-V Assembler is a JavaScript library to +convert SPIR-V assembly (as produced by spirv-dis in SPIR-V Tools) into a +SPIR-V binary. The assembler assumes it is generating WebGPU SPIR-V and thus has +the following limitations. + + * Only 32 bit integers and floats supported + * Only GLSL accepted as an extended instruction set + * Doesn't support ! syntax for integers + * Doesn't support hex encoding for float + +```shell +yarn install +yarn test +``` + +You can also use `yarn watch` to watch all of the files and re-run tests as +needed. + +## Webserver +Using `yarn serve` will start a webserver on localhost:5000. If you load the +`tests/index.html` file this will load the SVA files into browser. + +## Command Line +There is a simple assembler binary with can be executed from the command line. + +```shell +yarn sva tests/simple.spv_asm +``` + +The above will generate a `o.sva` file in the current directory. + +## Update spirv.data.json + +If there is a new spirv-headers release update the externals folder checkout +and then: + +```shell +./tools/process_grammar.rb > src/spirv.data.json +``` diff --git a/third_party/spirv-tools/tools/sva/bin/sva.js b/third_party/spirv-tools/tools/sva/bin/sva.js new file mode 100755 index 0000000..e2448d6 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/bin/sva.js @@ -0,0 +1,32 @@ +#!/usr/bin/env node +// +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +"use strict"; + +const fs = require("fs"); + +import SVA from "../src/sva.js"; + +let input = fs.readFileSync(process.argv[2], "utf-8"); +let u = SVA.assemble(input); + +if (typeof u === "string") { + console.log(u); +} else { + fs.writeFileSync("o.sva", new Buffer(u.buffer), (err) => { + console.log(["ERROR", err]); + }); +} diff --git a/third_party/spirv-tools/tools/sva/mocha.opts b/third_party/spirv-tools/tools/sva/mocha.opts new file mode 100644 index 0000000..4a52320 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/mocha.opts @@ -0,0 +1 @@ +--recursive diff --git a/third_party/spirv-tools/tools/sva/package.json b/third_party/spirv-tools/tools/sva/package.json new file mode 100644 index 0000000..3072d4c --- /dev/null +++ b/third_party/spirv-tools/tools/sva/package.json @@ -0,0 +1,25 @@ +{ + "name": "sva", + "version": "0.1.0", + "description": "SPIR-V Assembler", + "main": "index.js", + "author": "dan sinclair ", + "license": "Apache-2.0", + "private": true, + "scripts": { + "sva": "node -r esm bin/sva.js", + "lint": "eslint --fix --ext .js .", + "test": "mocha --require esm src/**/*_test.js", + "watch": "mocha --require esm --watch --watch-extension js \"src/**/*_test.js\"", + "serve": "serve", + "bundle": "rollup -c" + }, + "devDependencies": { + "chai": "^4.2.0", + "eslint": "^6.3.0", + "esm": "^3.2.25", + "mocha": "^6.2.0", + "rollup": "^1.21.4", + "serve": "^11.1.0" + } +} diff --git a/third_party/spirv-tools/tools/sva/rollup.config.js b/third_party/spirv-tools/tools/sva/rollup.config.js new file mode 100644 index 0000000..2056e16 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/rollup.config.js @@ -0,0 +1,7 @@ +export default { + input: 'src/sva.js', + output: { + file: 'build/sva.js', + format: 'esm', + } +} diff --git a/third_party/spirv-tools/tools/sva/src/assembler.js b/third_party/spirv-tools/tools/sva/src/assembler.js new file mode 100644 index 0000000..7bc208e --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/assembler.js @@ -0,0 +1,98 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +export default class Assembler { + static get GENERATOR_ID() { return 0; } + + /** + * @param {AST} the AST to build the SPIR-V from + */ + constructor(ast) { + this.ast_ = ast; + } + + /** + * Assembles the AST into binary SPIR-V. + * @return {Uint32Array} The SPIR-V binary data. + */ + assemble() { + let total_size = 5; + for (const inst of this.ast_.instructions()) { + total_size += 1; + for (const op of inst.operands()) { + total_size += op.length(); + } + } + + let u = new Uint32Array(total_size); + u[0] = 0x07230203; // Magic + u[1] = 0x00010500; // Version 1.5 + u[2] = Assembler.GENERATOR_ID; // Generator magic number + u[3] = this.ast_.getIdBounds(); // ID bounds + u[4] = 0; // Reserved + + let idx = 5; + for (const inst of this.ast_.instructions()) { + let op_size = 1; + for (const op of inst.operands()) { + op_size += op.length(); + } + + u[idx++] = op_size << 16 | inst.opcode(); + for (const op of inst.operands()) { + idx = this.processOp(u, idx, op); + } + } + + return u; + } + + processOp(u, idx, op) { + if (op.type() === "string") { + let len = 0; + let v = 0; + for (const ch of op.value()) { + v = v | (ch.charCodeAt(0) << (len * 8)); + len += 1; + + if (len === 4) { + u[idx++] = v; + len = 0; + v = 0; + } + } + // Make sure either the terminating 0 byte is written or the last + // partial word is written. + u[idx++] = v; + + } else if (op.type() === "float") { + // TODO(dsinclair): Handle 64 bit floats ... + let b = new ArrayBuffer(4); + let f = new Float32Array(b); + f[0] = op.value(); + + let u2 = new Uint32Array(b); + + u[idx++] = u2[0]; + } else { + u[idx++] = op.value(); + } + + for (const param of op.params()) { + idx = this.processOp(u, idx, param); + } + + return idx; + } +} diff --git a/third_party/spirv-tools/tools/sva/src/assembler_test.js b/third_party/spirv-tools/tools/sva/src/assembler_test.js new file mode 100644 index 0000000..a23d211 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/assembler_test.js @@ -0,0 +1,165 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { assert } from "chai"; +import Lexer from "./lexer"; +import Parser from "./parser"; +import grammar from "./spirv.data.js"; +import Assembler from "./assembler"; + +describe("assembler", () => { + it("generates SPIR-V magic number", () => { + let input = `; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 6 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 440 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + + let a = new Assembler(ast); + let res = a.assemble(); + assert.equal(res[0], 0x07230203); + }); + + it("assembles enumerant params", () => { + let input = "OpExecutionMode %main LocalSize 2 3 4"; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + + let a = new Assembler(ast); + let res = a.assemble(); + + assert.lengthOf(res, 11); + assert.equal(res[5], (6 /* word count */ << 16) | 16 /* opcode */); + assert.equal(res[6], 1 /* %main */); + assert.equal(res[7], 17 /* LocalSize */); + assert.equal(res[8], 2); + assert.equal(res[9], 3); + assert.equal(res[10], 4); + }); + + it("assembles float 32 values", () => { + let input = `%float = OpTypeFloat 32 + %float1 = OpConstant %float 0.400000006`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + + let a = new Assembler(ast); + let res = a.assemble(); + + assert.lengthOf(res, 12); + assert.equal(res[8], (4 /* word count */ << 16) | 43 /* opcode */); + assert.equal(res[9], 1 /* %float */); + assert.equal(res[10], 2 /* %float */); + assert.equal(res[11], 0x3ecccccd /* 0.400000006 */); + }); + + describe("strings", () => { + it("assembles 'abcd'", () => { + let input = `OpName %mains "abcd"`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + + let a = new Assembler(ast); + let res = a.assemble(); + + assert.lengthOf(res, 9); + assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */); + assert.equal(res[6], 1 /* %mains */); + assert.equal(res[7], 0x64636261 /* food */); + assert.equal(res[8], 0x00000000 /* null byte */); + }); + + it("assembles 'abcde'", () => { + let input = `OpName %mains "abcde"`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + + let a = new Assembler(ast); + let res = a.assemble(); + + assert.lengthOf(res, 9); + assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */); + assert.equal(res[6], 1 /* %mains */); + assert.equal(res[7], 0x64636261 /* abcd */); + assert.equal(res[8], 0x00000065 /* e */); + }); + + it("assembles 'abcdef'", () => { + let input = `OpName %mains "abcdef"`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + + let a = new Assembler(ast); + let res = a.assemble(); + + assert.lengthOf(res, 9); + assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */); + assert.equal(res[6], 1 /* %mains */); + assert.equal(res[7], 0x64636261 /* abcd */); + assert.equal(res[8], 0x00006665 /* ef */); + }); + + it("assembles 'abcdefg'", () => { + let input = `OpName %mains "abcdefg"`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + + let a = new Assembler(ast); + let res = a.assemble(); + + assert.lengthOf(res, 9); + assert.equal(res[5], (4 /* word count */ << 16) | 5 /* opcode */); + assert.equal(res[6], 1 /* %mains */); + assert.equal(res[7], 0x64636261 /* abcd */); + assert.equal(res[8], 0x00676665 /* efg */); + }); + }); +}); diff --git a/third_party/spirv-tools/tools/sva/src/ast.js b/third_party/spirv-tools/tools/sva/src/ast.js new file mode 100644 index 0000000..d396d2f --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/ast.js @@ -0,0 +1,141 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +class Module { + constructor() { + this.instructions_ = []; + this.next_id_ = 1; + + /** + * Maps {string, hash} where the string is the type name and the hash is: + * type- 'float' or 'int' + * width- number of bits needed to store number + * signed- the sign of the number + */ + this.types_ = {}; + + /** + * Maps {string, number} where the string is the type name and the number is + * the id value. + */ + this.assigned_ids_ = {}; + } + + instructions() { return this.instructions_; } + + instruction(val) { return this.instructions_[val]; } + + addInstruction(inst) { + this.instructions_.push(inst); + + // Record type information + if (inst.name() === "OpTypeInt" || inst.name() === "OpTypeFloat") { + let is_int = inst.name() === "OpTypeInt"; + + this.types_[inst.operand(0).name()] = { + type: is_int ? "int" : "float", + width: inst.operand(1).value(), + signed: is_int ? inst.operand(2).value() : 1 + }; + } + + // Record operand result id's + inst.operands().forEach((op) => { + if (op.rawValue() !== undefined && op.type() === "result_id") { + this.next_id_ = Math.max(this.next_id_, op.rawValue() + 1); + } + }); + } + + getType(name) { return this.types_[name]; } + + getId(name) { + if (this.assigned_ids_[name] !== undefined) { + return this.assigned_ids_[name]; + } + + let next = this.next_id_; + this.assigned_ids_[name] = next; + + this.next_id_ += 1; + return next; + } + + getIdBounds() { return this.next_id_; } +} + +class Instruction { + constructor(name, opcode, operands) { + this.name_ = name; + this.opcode_ = opcode; + this.operands_ = operands; + } + + name() { return this.name_; } + + opcode() { return this.opcode_; } + + operands() { return this.operands_; } + + operand(val) { return this.operands_[val]; } +} + +class Operand { + constructor(mod, name, type, value, params) { + this.module_ = mod; + this.name_ = name; + this.type_ = type; + this.value_ = value; + this.params_ = params; + } + + name() { return this.name_; } + + length() { + // Get the value just to force it to be filled. + this.value(); + + if (this.type_ === "string") { + return Math.ceil((this.value_.length + 1) / 4); + } + + let size = 1; + for (const param of this.params_) { + size += param.length(); + } + return size; + } + + type() { return this.type_; } + + rawValue() { return this.value_; } + + // This method should only be called on ResultId's after the full parse is + // complete. This is because the AST will only have the maximum seen numeric + // ResultId when the parse is done. + value() { + if (this.value_ === undefined) { + this.value_ = this.module_.getId(this.name_); + } + return this.value_; + } + + params() { return this.params_; } +} + +export { + Module, + Instruction, + Operand +}; diff --git a/third_party/spirv-tools/tools/sva/src/lexer.js b/third_party/spirv-tools/tools/sva/src/lexer.js new file mode 100644 index 0000000..b39f93a --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/lexer.js @@ -0,0 +1,363 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Token, TokenType } from "./token.js"; + +export default class Lexer { + /** + * @param {String} input The input string to tokenize. + */ + constructor(input) { + this.input_ = input; + this.len_ = input.length; + this.cur_pos_ = 0; + this.cur_line_ = 1; + + this.num_regex_ = /^[0-9]+$/; + this.alpha_regex_ = /^[a-zA-Z_]+$/; + this.op_regex_ = /^Op[A-Z][^\s]*$/; + this.hex_regex_ = /^[0-9a-fA-F]$/; + } + + /** + * Parses the next token from the input stream. + * @return {Token} the next token. + */ + next() { + this.skipWhitespace(); + this.skipComments(); + + if (this.cur_pos_ >= this.len_) + return new Token(TokenType.kEOF, this.cur_line_); + + let n = this.tryHexInteger(); + if (n !== undefined) + return n; + + n = this.tryFloat(); + if (n !== undefined) + return n; + + n = this.tryInteger(); + if (n !== undefined) + return n; + + n = this.tryString(); + if (n !== undefined) + return n; + + n = this.tryOp(); + if (n !== undefined) + return n; + + n = this.tryPunctuation(); + if (n !== undefined) + return n; + + n = this.tryResultId(); + if (n !== undefined) + return n; + + n = this.tryIdent(); + if (n !== undefined) + return n; + + return new Token(TokenType.kError, this.cur_line_, "Failed to match token"); + } + + is(str) { + if (this.len_ <= this.cur_pos_ + (str.length - 1)) + return false; + + for (let i = 0; i < str.length; ++i) { + if (this.input_[this.cur_pos_ + i] !== str[i]) + return false; + } + + return true; + } + + isNum(ch) { + return ch.match(this.num_regex_); + } + + isAlpha(ch) { + return ch.match(this.alpha_regex_); + } + + isAlphaNum(ch) { + return this.isNum(ch) || this.isAlpha(ch); + } + + isHex(char) { + return char.match(this.hex_regex_); + } + + isCurWhitespace() { + return this.is(" ") || this.is("\t") || this.is("\r") || this.is("\n"); + } + + skipWhitespace() { + for(;;) { + let cur_pos = this.cur_pos_; + while (this.cur_pos_ < this.len_ && + this.isCurWhitespace()) { + if (this.is("\n")) + this.cur_line_ += 1; + + this.cur_pos_ += 1; + } + + this.skipComments(); + + // Cursor didn't move so no whitespace matched. + if (cur_pos === this.cur_pos_) + break; + } + } + + skipComments() { + if (!this.is(";")) + return; + + while (this.cur_pos_ < this.len_ && !this.is("\n")) + this.cur_pos_ += 1; + } + + /** + * Attempt to parse the next part of the input as a float. + * @return {Token|undefined} returns a Token if a float is matched, + * undefined otherwise. + */ + tryFloat() { + let start = this.cur_pos_; + let end = start; + + if (this.cur_pos_ >= this.len_) + return undefined; + if (this.input_[end] === "-") + end += 1; + + while (end < this.len_ && this.isNum(this.input_[end])) + end += 1; + + // Must have a "." in a float + if (end >= this.len_ || this.input_[end] !== ".") + return undefined; + + end += 1; + while (end < this.len_ && this.isNum(this.input_[end])) + end += 1; + + let substr = this.input_.substr(start, end - start); + if (substr === "." || substr === "-.") + return undefined; + + this.cur_pos_ = end; + + return new Token(TokenType.kFloatLiteral, this.cur_line_, parseFloat(substr)); + } + + /** + * Attempt to parse a hex encoded integer. + * @return {Token|undefined} returns a Token if a Hex number is matched, + * undefined otherwise. + */ + tryHexInteger() { + let start = this.cur_pos_; + let end = start; + + if (this.cur_pos_ >= this.len_) + return undefined; + if (end + 2 >= this.len_ || this.input_[end] !== "0" || + this.input_[end + 1] !== "x") { + return undefined; + } + + end += 2; + + while (end < this.len_ && this.isHex(this.input_[end])) + end += 1; + + this.cur_pos_ = end; + + let val = parseInt(this.input_.substr(start, end - start), 16); + return new Token(TokenType.kIntegerLiteral, this.cur_line_, val); + } + + /** + * Attempt to parse an encoded integer. + * @return {Token|undefined} returns a Token if a number is matched, + * undefined otherwise. + */ + tryInteger() { + let start = this.cur_pos_; + let end = start; + + if (this.cur_pos_ >= this.len_) + return undefined; + if (this.input_[end] === "-") + end += 1; + + if (end >= this.len_ || !this.isNum(this.input_[end])) + return undefined; + + while (end < this.len_ && this.isNum(this.input_[end])) + end += 1; + + this.cur_pos_ = end; + + let val = parseInt(this.input_.substr(start, end - start), 10); + return new Token(TokenType.kIntegerLiteral, this.cur_line_, val); + } + + /** + * Attempt to parse a result id. + * @return {Token|undefined} returns a Token if a result id is matched, + * undefined otherwise. + */ + tryResultId() { + let start = this.cur_pos_; + if (start >= this.len_) + return undefined; + if (!this.is("%")) + return undefined; + + start += 1; + this.cur_pos_ += 1; + while (this.cur_pos_ < this.len_ && + (this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) { + this.cur_pos_ += 1; + } + + let ident = this.input_.substr(start, this.cur_pos_ - start); + let value = undefined; + if (ident.match(this.num_regex_)) + value = parseInt(ident, 10); + + return new Token(TokenType.kResultId, this.cur_line_, { + name: ident, + val: value + }); + } + + /** + * Attempt to parse an identifier. + * @return {Token|undefined} returns a Token if an identifier is matched, + * undefined otherwise. + */ + tryIdent() { + let start = this.cur_pos_; + if (start >= this.len_) + return undefined; + + while (this.cur_pos_ < this.len_ && + (this.isAlphaNum(this.input_[this.cur_pos_]) || this.is("_"))) { + this.cur_pos_ += 1; + } + + let ident = this.input_.substr(start, this.cur_pos_ - start); + return new Token(TokenType.kIdentifier, this.cur_line_, ident); + } + + /** + * Attempt to parse an Op command. + * @return {Token|undefined} returns a Token if an Op command is matched, + * undefined otherwise. + */ + tryOp() { + let start = this.cur_pos_; + if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_)) + return undefined; + + if (this.input_[this.cur_pos_] !== "O" || + this.input_[this.cur_pos_ + 1] !== "p") { + return undefined; + } + + while (this.cur_pos_ < this.len_ && + !this.isCurWhitespace()) { + this.cur_pos_ += 1; + } + + return new Token(TokenType.kOp, this.cur_line_, { + name: this.input_.substr(start, this.cur_pos_ - start) + }); + } + + /** + * Attempts to match punctuation strings against the input + * @return {Token|undefined} Returns the Token for the punctuation or + * undefined if no matches found. + */ + tryPunctuation() { + let type = undefined; + if (this.is("=")) + type = TokenType.kEqual; + else if (this.is("|")) + type = TokenType.kPipe; + + if (type === undefined) + return undefined; + + this.cur_pos_ += type.length; + return new Token(type, this.cur_line_, type); + } + + /** + * Attempts to match strings against the input + * @return {Token|undefined} Returns the Token for the string or undefined + * if no match found. + */ + tryString() { + let start = this.cur_pos_; + + // Must have at least 2 chars for a string. + if (this.cur_pos_ >= this.len_ || (this.cur_pos_ + 1 >= this.len_)) + return undefined; + if (!this.is("\"")) + return undefined; + + this.cur_pos_ += 1; + let str = ""; + while (this.cur_pos_ <= this.len_) { + if (this.is("\"")) + break; + + if (this.is("\\")) { + this.cur_pos_ += 1; + if (this.cur_pos_ >= this.len_) + return undefined; + + if (this.is("\\")) { + str += "\\"; + } else if (this.is("\"")) { + str += '"'; + } else { + str += this.input_[this.cur_pos_]; + } + } else { + str += this.input_[this.cur_pos_]; + } + this.cur_pos_ += 1; + } + + if (this.cur_pos_ >= this.len_) + return undefined; + + this.cur_pos_ += 1; + + return new Token(TokenType.kStringLiteral, this.cur_line_, str); + } +} diff --git a/third_party/spirv-tools/tools/sva/src/lexer_test.js b/third_party/spirv-tools/tools/sva/src/lexer_test.js new file mode 100644 index 0000000..32b24c7 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/lexer_test.js @@ -0,0 +1,191 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { assert } from "chai"; +import Lexer from "./lexer"; +import { TokenType } from "./token"; + +describe("lexer", () => { + describe("skipped content", () => { + it("skips whitespace", () => { + let input = " \t\r\n\t \tOpKill\t\n\t \r "; + let l = new Lexer(input); + + let t = l.next(); + assert.equal(t.type, TokenType.kOp); + assert.equal(t.line, 2); + assert.equal(t.data.name, "OpKill"); + + t = l.next(); + assert.equal(t.type, TokenType.kEOF); + assert.equal(t.line, 3); + }); + + it("skips ; comments", () => { + let input = `; start with comment +OpKill ; end of line comment +; another comment +%1`; + + let l = new Lexer(input); + let t = l.next(); + assert.equal(t.type, TokenType.kOp); + assert.equal(t.data.name, "OpKill"); + assert.equal(t.line, 2); + + t = l.next(); + assert.equal(t.type, TokenType.kResultId); + assert.equal(t.data.name, "1"); + assert.equal(t.data.val, 1); + assert.equal(t.line, 4); + }); + }); + + describe("numerics", () => { + it("parses floats", () => { + let input = ["0.0", "0.", ".0", "5.7", "5.", ".7", "-0.0", "-.0", + "-0.", "-5.7", "-5.", "-.7"]; + + let results = [0.0, 0.0, 0.0, 5.7, 5.0, 0.7, 0.0, 0.0, 0.0, -5.7, -5.0, + -0.7]; + input.forEach((val, idx) => { + let l = new Lexer(val); + let t = l.next(); + + assert.equal(t.type, TokenType.kFloatLiteral, + `expected ${val} to be a float got ${t.type}`); + assert.equal(t.data, results[idx], + `expected ${results[idx]} === ${t.data}`); + + t = l.next(); + assert.equal(t.type, TokenType.kEOF); + assert.equal(t.data, undefined); + }); + }); + + it("handles invalid floats", () => { + let input = [".", "-."]; + input.forEach((val) => { + let l = new Lexer(val); + let t = l.next(); + + assert.notEqual(t.type, TokenType.kFloatLiteral, + `expect ${val} to not match type float`); + }); + }); + + it("parses integers", () => { + let input = ["0", "-0", "123", "-123", "2147483647", "-2147483648", + "4294967295", "0x00", "0x24"]; + let results = [0, 0, 123, -123,2147483647, -2147483648, 4294967295, + 0x0, 0x24]; + + input.forEach((val, idx) => { + let l = new Lexer(val); + let t = l.next(); + + assert.equal(t.type, TokenType.kIntegerLiteral, + `expected ${val} to be an integer got ${t.type}`); + assert.equal(t.data, results[idx], + `expected ${results[idx]} === ${t.data}`); + + t = l.next(); + assert.equal(t.type, TokenType.kEOF); + assert.equal(t.data, undefined); + }); + }); + }); + + it("matches result_ids", () => { + let input = `%123 +%001 +%main +%_a_b_c`; + + let result = [ + {name: "123", val: 123}, + {name: "001", val: 1}, + {name: "main", val: undefined}, + {name: "_a_b_c", val: undefined} + ]; + + let l = new Lexer(input); + for (let i = 0; i < result.length; ++i) { + let t = l.next(); + assert.equal(t.type, TokenType.kResultId); + assert.equal(t.data.name, result[i].name); + assert.equal(t.data.val, result[i].val); + } + }); + + it("matches punctuation", () => { + let input = "="; + let results = [TokenType.kEqual]; + + let l = new Lexer(input); + for (let i = 0; i < results.length; ++i) { + let t = l.next(); + assert.equal(t.type, results[i]); + assert.equal(t.line, i + 1); + } + + let t = l.next(); + assert.equal(t.type, TokenType.kEOF); + }); + + describe("strings", () => { + it("matches strings", () => { + let input = "\"GLSL.std.450\""; + + let l = new Lexer(input); + let t = l.next(); + assert.equal(t.type, TokenType.kStringLiteral); + assert.equal(t.data, "GLSL.std.450"); + }); + + it("handles unfinished strings", () => { + let input = "\"GLSL.std.450"; + + let l = new Lexer(input); + let t = l.next(); + assert.equal(t.type, TokenType.kError); + }); + + it("handles escapes", () => { + let input = `"embedded\\"quote" +"embedded\\\\slash" +"embedded\\nchar"`; + let results = [`embedded\"quote`, `embedded\\slash`, `embeddednchar`]; + + let l = new Lexer(input); + for (let i = 0; i < results.length; ++i) { + let t = l.next(); + assert.equal(t.type, TokenType.kStringLiteral, results[i]); + assert.equal(t.data, results[i]); + } + }); + }); + + it("matches keywords", () => { + let input = "GLSL Function"; + let results = ["GLSL", "Function"]; + + let l = new Lexer(input); + for (let i = 0; i < results.length; ++i) { + let t = l.next(); + assert.equal(t.type, TokenType.kIdentifier, results[i]); + assert.equal(t.data, results[i]); + } + }); +}); diff --git a/third_party/spirv-tools/tools/sva/src/parser.js b/third_party/spirv-tools/tools/sva/src/parser.js new file mode 100644 index 0000000..ccf872a --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/parser.js @@ -0,0 +1,283 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { TokenType } from "./token.js"; +import * as AST from "./ast.js"; + +export default class Parser { + /** + * @param {Hash} The SPIR-V grammar + * @param {Lexer} The lexer + * @return {AST} Attempts to build an AST from the tokens returned by the + * given lexer + */ + constructor(grammar, lexer) { + this.grammar_ = grammar; + this.lexer_ = lexer; + + this.peek_ = []; + this.error_ = ""; + } + + get error() { return this.error_; } + + next() { + return this.peek_.shift() || this.lexer_.next(); + } + + peek(idx) { + while (this.peek_.length <= idx) { + this.peek_.push(this.lexer_.next()); + } + return this.peek_[idx]; + } + + /** + * Executes the parser. + * + * @return {AST|undefined} returns a parsed AST on success or undefined + * on error. The error message can be retrieved by + * calling error(). + */ + parse() { + let ast = new AST.Module(); + for(;;) { + let token = this.next(); + if (token === TokenType.kError) { + this.error_ = token.line() + ": " + token.data(); + return undefined; + } + if (token.type === TokenType.kEOF) + break; + + let result_id = undefined; + if (token.type === TokenType.kResultId) { + result_id = token; + + token = this.next(); + if (token.type !== TokenType.kEqual) { + this.error_ = token.line + ": expected = after result id"; + return undefined; + } + + token = this.next(); + } + + if (token.type !== TokenType.kOp) { + this.error_ = token.line + ": expected Op got " + token.type; + return undefined; + } + + let name = token.data.name; + let data = this.getInstructionData(name); + let operands = []; + let result_type = undefined; + + for (let operand of data.operands) { + if (operand.kind === "IdResult") { + if (result_id === undefined) { + this.error_ = token.line + ": expected result id"; + return undefined; + } + let o = new AST.Operand(ast, result_id.data.name, "result_id", + result_id.data.val, []); + if (o === undefined) { + return undefined; + } + operands.push(o); + } else { + if (operand.quantifier === "?") { + if (this.nextIsNewInstr()) { + break; + } + } else if (operand.quantifier === "*") { + while (!this.nextIsNewInstr()) { + let o = this.extractOperand(ast, result_type, operand); + if (o === undefined) { + return undefined; + } + operands.push(o); + } + break; + } + + let o = this.extractOperand(ast, result_type, operand); + if (o === undefined) { + return undefined; + } + + // Store the result type away so we can use it for context dependent + // numbers if needed. + if (operand.kind === "IdResultType") { + result_type = ast.getType(o.name()); + } + + operands.push(o); + } + } + + // Verify only GLSL extended instructions are used + if (name === "OpExtInstImport" && operands[1].value() !== "GLSL.std.450") { + this.error_ = token.line + ": Only GLSL.std.450 external instructions supported"; + return undefined; + } + + let inst = new AST.Instruction(name, data.opcode, operands); + + ast.addInstruction(inst); + } + return ast; + } + + getInstructionData(name) { + return this.grammar_["instructions"][name]; + } + + nextIsNewInstr() { + let n0 = this.peek(0); + if (n0.type === TokenType.kOp || n0.type === TokenType.kEOF) { + return true; + } + + let n1 = this.peek(1); + if (n1.type === TokenType.kEOF) { + return false; + } + if (n0.type === TokenType.kResultId && n1.type === TokenType.kEqual) + return true; + + return false; + } + + extractOperand(ast, result_type, data) { + let t = this.next(); + + let name = undefined; + let kind = undefined; + let value = undefined; + let params = []; + + // TODO(dsinclair): There are a bunch of missing types here. See + // https://github.com/KhronosGroup/SPIRV-Tools/blob/master/source/text.cpp#L210 + // + // LiteralSpecConstantOpInteger + // PairLiteralIntegerIdRef + // PairIdRefLiteralInteger + // PairIdRefIdRef + if (data.kind === "IdResult" || data.kind === "IdRef" + || data.kind === "IdResultType" || data.kind === "IdScope" + || data.kind === "IdMemorySemantics") { + if (t.type !== TokenType.kResultId) { + this.error_ = t.line + ": expected result id"; + return undefined; + } + + name = t.data.name; + kind = "result_id"; + value = t.data.val; + } else if (data.kind === "LiteralString") { + if (t.type !== TokenType.kStringLiteral) { + this.error_ = t.line + ": expected string not found"; + return undefined; + } + + name = t.data; + kind = "string"; + value = t.data; + } else if (data.kind === "LiteralInteger") { + if (t.type !== TokenType.kIntegerLiteral) { + this.error_ = t.line + ": expected integer not found"; + return undefined; + } + + name = "" + t.data; + kind = t.type; + value = t.data; + } else if (data.kind === "LiteralContextDependentNumber") { + if (result_type === undefined) { + this.error_ = t.line + + ": missing result type for context dependent number"; + return undefined; + } + if (t.type !== TokenType.kIntegerLiteral + && t.type !== TokenType.kFloatLiteral) { + this.error_ = t.line + ": expected number not found"; + return undefined; + } + + name = "" + t.data; + kind = result_type.type; + value = t.data; + + } else if (data.kind === "LiteralExtInstInteger") { + if (t.type !== TokenType.kIdentifier) { + this.error_ = t.line + ": expected instruction identifier"; + return undefined; + } + + if (this.grammar_.ext[t.data] === undefined) { + this.error_ = t.line + `: unable to find extended instruction (${t.data})`; + return undefined; + } + + name = t.data; + kind = "integer"; + value = this.grammar_.ext[t.data]; + + } else { + let d = this.grammar_.operand_kinds[data.kind]; + if (d === undefined) { + this.error_ = t.line + ": expected " + data.kind + " not found"; + return undefined; + } + + let val = d.values[t.data]["value"]; + let names = [t.data]; + if (d.type === "BitEnum") { + for(;;) { + let tmp = this.peek(0); + if (tmp.type !== TokenType.kPipe) { + break; + } + + this.next(); // skip pipe + tmp = this.next(); + + if (tmp.type !== TokenType.kIdentifier) { + this.error_ = tmp.line() + ": expected identifier"; + return undefined; + } + + val |= d.values[tmp.data]["value"]; + names.push(tmp.data); + } + } + + name = names.join("|"); + kind = d.type; + value = val; + + for (const op_name of names) { + if (d.values[op_name]['params'] === undefined) { + continue; + } + + for (const param of d.values[op_name]["params"]) { + params.push(this.extractOperand(ast, result_type, { kind: param })); + } + } + } + return new AST.Operand(ast, name, kind, value, params); + } +} diff --git a/third_party/spirv-tools/tools/sva/src/parser_test.js b/third_party/spirv-tools/tools/sva/src/parser_test.js new file mode 100644 index 0000000..dffc0b3 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/parser_test.js @@ -0,0 +1,489 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { assert } from "chai"; +import Lexer from "./lexer"; +import Parser from "./parser"; +import grammar from "./spirv.data.js"; + +describe("parser", () => { + it("parses an opcode", () => { + let input = "OpKill"; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpKill"); + assert.equal(inst.opcode(), 252); + assert.lengthOf(inst.operands, 0); + }); + + it("parses an opcode with an identifier", () => { + let input = "OpCapability Shader"; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpCapability"); + assert.equal(inst.opcode(), 17); + assert.lengthOf(inst.operands(), 1); + + let op = inst.operand(0); + assert.equal(op.name(), "Shader"); + assert.equal(op.type(), "ValueEnum"); + assert.equal(op.value(), 1); + }); + + it("parses an opcode with a result", () => { + let input = "%void = OpTypeVoid"; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpTypeVoid"); + assert.equal(inst.opcode(), 19); + assert.lengthOf(inst.operands(), 1); + + let op = inst.operand(0); + assert.equal(op.name(), "void"); + assert.equal(op.value(), 1); + }); + + it("sets module bounds based on numeric result", () => { + let input = "%3 = OpTypeVoid"; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.equal(ast.getId("next"), 4); + }); + + it("returns the same value for a named result_id", () => { + let input = "%3 = OpTypeFunction %int %int"; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + let op1 = inst.operand(1); + assert.equal(op1.name(), "int"); + assert.equal(op1.value(), 4); + + let op2 = inst.operand(2); + assert.equal(op2.name(), "int"); + assert.equal(op2.value(), 4); + }); + + it("parses an opcode with a string", () => { + let input = "OpEntryPoint Fragment %main \"main\""; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + let op = inst.operand(2); + assert.equal(op.name(), "main"); + assert.equal(op.value(), "main"); + }); + + describe("numerics", () => { + describe("integers", () => { + it("parses an opcode with an integer", () => { + let input = "OpSource GLSL 440"; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + let op0 = inst.operand(0); + assert.equal(op0.name(), "GLSL"); + assert.equal(op0.type(), "ValueEnum"); + assert.equal(op0.value(), 2); + + let op1 = inst.operand(1); + assert.equal(op1.name(), "440"); + assert.equal(op1.value(), 440); + }); + + it("parses an opcode with a hex integer", () => { + let input = "OpSource GLSL 0x440"; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + let op0 = inst.operand(0); + assert.equal(op0.name(), "GLSL"); + assert.equal(op0.type(), "ValueEnum"); + assert.equal(op0.value(), 2); + + let op1 = inst.operand(1); + assert.equal(op1.name(), "1088"); + assert.equal(op1.value(), 0x440); + }); + + it.skip("parses immediate integers", () => { + // TODO(dsinclair): Support or skip? + }); + }); + + describe("floats", () => { + it("parses floats", () => { + let input = `%float = OpTypeFloat 32 + %float1 = OpConstant %float 0.400000006`; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(1); + let op2 = inst.operand(2); + assert.equal(op2.value(), 0.400000006); + }); + + // TODO(dsinclair): Make hex encoded floats parse ... + it.skip("parses hex floats", () => { + let input = `%float = OpTypeFloat 32 + %nfloat = OpConstant %float -0.4p+2 + %pfloat = OpConstant %float 0.4p-2 + %inf = OpConstant %float32 0x1p+128 + %neginf = OpConstant %float32 -0x1p+128 + %aNaN = OpConstant %float32 0x1.8p+128 + %moreNaN = OpConstant %float32 -0x1.0002p+128`; + + let results = [-40.0, .004, 0x00000, 0x00000, 0x7fc00000, 0xff800100]; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + assert.lengthOf(ast.instructions(), 7); + + for (const idx in results) { + let inst = ast.instruction(idx); + let op2 = inst.operand(2); + assert.equal(op2.value(), results[idx]); + } + }); + + it("parses a float that looks like an int", () => { + let input = `%float = OpTypeFloat 32 + %float1 = OpConstant %float 1`; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(1); + let op2 = inst.operand(2); + assert.equal(op2.value(), 1); + assert.equal(op2.type(), "float"); + }); + }); + }); + + describe("enums", () => { + it("parses enum values", () => { + let input = `%1 = OpTypeFloat 32 + %30 = OpImageSampleExplicitLod %1 %20 %18 Grad|ConstOffset %22 %24 %29`; + + let vals = [{val: 1, name: "1"}, + {val: 30, name: "30"}, + {val: 20, name: "20"}, + {val: 18, name: "18"}, + {val: 12, name: "Grad|ConstOffset"}]; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(1); + for (let idx in vals) { + let op = inst.operand(idx); + assert.equal(op.name(), vals[idx].name); + assert.equal(op.value(), vals[idx].val); + } + + // BitEnum + let params = inst.operand(4).params(); + assert.lengthOf(params, 3); + assert.equal(params[0].name(), "22"); + assert.equal(params[0].value(), 22); + assert.equal(params[1].name(), "24"); + assert.equal(params[1].value(), 24); + assert.equal(params[2].name(), "29"); + assert.equal(params[2].value(), 29); + }); + + it("parses enumerants with parameters", () => { + let input ="OpExecutionMode %main LocalSize 2 3 4"; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpExecutionMode"); + assert.lengthOf(inst.operands(), 2); + assert.equal(inst.operand(0).name(), "main"); + assert.equal(inst.operand(1).name(), "LocalSize"); + + let params = inst.operand(1).params(); + assert.lengthOf(params, 3); + assert.equal(params[0].name(), "2"); + assert.equal(params[1].name(), "3"); + assert.equal(params[2].name(), "4"); + }); + }); + + it("parses result into second operand if needed", () => { + let input = `%int = OpTypeInt 32 1 + %int_3 = OpConstant %int 3`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(1); + assert.equal(inst.name(), "OpConstant"); + assert.equal(inst.opcode(), 43); + assert.lengthOf(inst.operands(), 3); + + let op0 = inst.operand(0); + assert.equal(op0.name(), "int"); + assert.equal(op0.value(), 1); + + let op1 = inst.operand(1); + assert.equal(op1.name(), "int_3"); + assert.equal(op1.value(), 2); + + let op2 = inst.operand(2); + assert.equal(op2.name(), "3"); + assert.equal(op2.value(), 3); + }); + + describe("quantifiers", () => { + describe("?", () => { + it("skips if missing", () => { + let input = `OpImageWrite %1 %2 %3 +OpKill`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpImageWrite"); + assert.lengthOf(inst.operands(), 3); + }); + + it("skips if missing at EOF", () => { + let input = "OpImageWrite %1 %2 %3"; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpImageWrite"); + assert.lengthOf(inst.operands(), 3); + }); + + it("extracts if available", () => { + let input = `OpImageWrite %1 %2 %3 ConstOffset %2 +OpKill`; + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpImageWrite"); + assert.lengthOf(inst.operands(), 4); + assert.equal(inst.operand(3).name(), "ConstOffset"); + }); + }); + + describe("*", () => { + it("skips if missing", () => { + let input = `OpEntryPoint Fragment %main "main" +OpKill`; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpEntryPoint"); + assert.lengthOf(inst.operands(), 3); + assert.equal(inst.operand(2).name(), "main"); + }); + + it("extracts one if available", () => { + let input = `OpEntryPoint Fragment %main "main" %2 +OpKill`; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpEntryPoint"); + assert.lengthOf(inst.operands(), 4); + assert.equal(inst.operand(3).name(), "2"); + }); + + it("extracts multiple if available", () => { + let input = `OpEntryPoint Fragment %main "main" %2 %3 %4 %5 +OpKill`; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(0); + assert.equal(inst.name(), "OpEntryPoint"); + assert.lengthOf(inst.operands(), 7); + assert.equal(inst.operand(3).name(), "2"); + assert.equal(inst.operand(4).name(), "3"); + assert.equal(inst.operand(5).name(), "4"); + assert.equal(inst.operand(6).name(), "5"); + }); + }); + }); + + describe("extended instructions", () => { + it("errors on non-glsl extensions", () => { + let input = "%1 = OpExtInstImport \"OpenCL.std.100\""; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + assert.isUndefined(p.parse()); + }); + + it("handles extended instructions", () => { + let input = `%1 = OpExtInstImport "GLSL.std.450" + %44 = OpExtInst %7 %1 Sqrt %43`; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + assert.lengthOf(ast.instructions(), 2); + + let inst = ast.instruction(1); + assert.lengthOf(inst.operands(), 5); + assert.equal(inst.operand(3).value(), 31); + assert.equal(inst.operand(3).name(), "Sqrt"); + assert.equal(inst.operand(4).value(), 43); + assert.equal(inst.operand(4).name(), "43"); + }); + }); + + it.skip("handles spec constant ops", () => { + // let input = "%sum = OpSpecConstantOp %i32 IAdd %a %b"; + }); + + it("handles OpCopyMemory", () => { + let input = "OpCopyMemory %1 %2 " + + "Volatile|Nontemporal|MakePointerVisible %3 " + + "Aligned|MakePointerAvailable|NonPrivatePointer 16 %4"; + + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + assert.exists(ast, p.error); + assert.lengthOf(ast.instructions(), 1); + + let inst = ast.instruction(0); + assert.lengthOf(inst.operands(), 4); + assert.equal(inst.operand(0).value(), 1); + assert.equal(inst.operand(1).value(), 2); + + assert.equal(inst.operand(2).name(), + "Volatile|Nontemporal|MakePointerVisible"); + assert.equal(inst.operand(2).value(), 21); + assert.lengthOf(inst.operand(2).params(), 1); + assert.equal(inst.operand(2).params()[0].value(), 3); + + assert.equal(inst.operand(3).name(), + "Aligned|MakePointerAvailable|NonPrivatePointer"); + assert.equal(inst.operand(3).value(), 42); + assert.lengthOf(inst.operand(3).params(), 2); + assert.equal(inst.operand(3).params()[0].value(), 16); + assert.equal(inst.operand(3).params()[1].value(), 4); + }); +}); diff --git a/third_party/spirv-tools/tools/sva/src/spirv.data.js b/third_party/spirv-tools/tools/sva/src/spirv.data.js new file mode 100644 index 0000000..ba969d8 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/spirv.data.js @@ -0,0 +1,4567 @@ +/*Copyright (c) 2014-2016 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and/or associated documentation files (the "Materials"), +to deal in the Materials without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Materials, and to permit persons to whom the +Materials are furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Materials. + +MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +IN THE MATERIALS.*/ + +// THIS FILE IS GENERATED WITH tools/process_grammar.rb + +export default { + "magic": "0x07230203", + "version": [ + 1, + 5 + ], + "instructions": { + "OpNop": { + "opcode": 0, + "operands": [ + + ] + }, + "OpUndef": { + "opcode": 1, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + } + ] + }, + "OpSourceContinued": { + "opcode": 2, + "operands": [ + { + "kind": "LiteralString" + } + ] + }, + "OpSource": { + "opcode": 3, + "operands": [ + { + "kind": "SourceLanguage" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "IdRef", + "quantifier": "?" + }, + { + "kind": "LiteralString", + "quantifier": "?" + } + ] + }, + "OpSourceExtension": { + "opcode": 4, + "operands": [ + { + "kind": "LiteralString" + } + ] + }, + "OpName": { + "opcode": 5, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "LiteralString" + } + ] + }, + "OpMemberName": { + "opcode": 6, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "LiteralString" + } + ] + }, + "OpString": { + "opcode": 7, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "LiteralString" + } + ] + }, + "OpLine": { + "opcode": 8, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "LiteralInteger" + } + ] + }, + "OpExtension": { + "opcode": 10, + "operands": [ + { + "kind": "LiteralString" + } + ] + }, + "OpExtInstImport": { + "opcode": 11, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "LiteralString" + } + ] + }, + "OpExtInst": { + "opcode": 12, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralExtInstInteger" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpMemoryModel": { + "opcode": 14, + "operands": [ + { + "kind": "AddressingModel" + }, + { + "kind": "MemoryModel" + } + ] + }, + "OpEntryPoint": { + "opcode": 15, + "operands": [ + { + "kind": "ExecutionModel" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralString" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpExecutionMode": { + "opcode": 16, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "ExecutionMode" + } + ] + }, + "OpCapability": { + "opcode": 17, + "operands": [ + { + "kind": "Capability" + } + ] + }, + "OpTypeVoid": { + "opcode": 19, + "operands": [ + { + "kind": "IdResult" + } + ] + }, + "OpTypeBool": { + "opcode": 20, + "operands": [ + { + "kind": "IdResult" + } + ] + }, + "OpTypeInt": { + "opcode": 21, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "LiteralInteger" + } + ] + }, + "OpTypeFloat": { + "opcode": 22, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "LiteralInteger" + } + ] + }, + "OpTypeVector": { + "opcode": 23, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger" + } + ] + }, + "OpTypeMatrix": { + "opcode": 24, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger" + } + ] + }, + "OpTypeImage": { + "opcode": 25, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "Dim" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "ImageFormat" + }, + { + "kind": "AccessQualifier", + "quantifier": "?" + } + ] + }, + "OpTypeSampler": { + "opcode": 26, + "operands": [ + { + "kind": "IdResult" + } + ] + }, + "OpTypeSampledImage": { + "opcode": 27, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpTypeArray": { + "opcode": 28, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpTypeRuntimeArray": { + "opcode": 29, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpTypeStruct": { + "opcode": 30, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpTypePointer": { + "opcode": 32, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "StorageClass" + }, + { + "kind": "IdRef" + } + ] + }, + "OpTypeFunction": { + "opcode": 33, + "operands": [ + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpConstantTrue": { + "opcode": 41, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + } + ] + }, + "OpConstantFalse": { + "opcode": 42, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + } + ] + }, + "OpConstant": { + "opcode": 43, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "LiteralContextDependentNumber" + } + ] + }, + "OpConstantComposite": { + "opcode": 44, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpConstantNull": { + "opcode": 46, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + } + ] + }, + "OpSpecConstantTrue": { + "opcode": 48, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + } + ] + }, + "OpSpecConstantFalse": { + "opcode": 49, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + } + ] + }, + "OpSpecConstant": { + "opcode": 50, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "LiteralContextDependentNumber" + } + ] + }, + "OpSpecConstantComposite": { + "opcode": 51, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpSpecConstantOp": { + "opcode": 52, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "LiteralSpecConstantOpInteger" + } + ] + }, + "OpFunction": { + "opcode": 54, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "FunctionControl" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFunctionParameter": { + "opcode": 55, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + } + ] + }, + "OpFunctionEnd": { + "opcode": 56, + "operands": [ + + ] + }, + "OpFunctionCall": { + "opcode": 57, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpVariable": { + "opcode": 59, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "StorageClass" + }, + { + "kind": "IdRef", + "quantifier": "?" + } + ] + }, + "OpImageTexelPointer": { + "opcode": 60, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpLoad": { + "opcode": 61, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "MemoryAccess", + "quantifier": "?" + } + ] + }, + "OpStore": { + "opcode": 62, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "MemoryAccess", + "quantifier": "?" + } + ] + }, + "OpCopyMemory": { + "opcode": 63, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "MemoryAccess", + "quantifier": "?" + }, + { + "kind": "MemoryAccess", + "quantifier": "?" + } + ] + }, + "OpAccessChain": { + "opcode": 65, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpInBoundsAccessChain": { + "opcode": 66, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpArrayLength": { + "opcode": 68, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger" + } + ] + }, + "OpDecorate": { + "opcode": 71, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "Decoration" + } + ] + }, + "OpMemberDecorate": { + "opcode": 72, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "Decoration" + } + ] + }, + "OpDecorationGroup": { + "opcode": 73, + "operands": [ + { + "kind": "IdResult" + } + ] + }, + "OpGroupDecorate": { + "opcode": 74, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpGroupMemberDecorate": { + "opcode": 75, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "PairIdRefLiteralInteger", + "quantifier": "*" + } + ] + }, + "OpVectorExtractDynamic": { + "opcode": 77, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpVectorInsertDynamic": { + "opcode": 78, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpVectorShuffle": { + "opcode": 79, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger", + "quantifier": "*" + } + ] + }, + "OpCompositeConstruct": { + "opcode": 80, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef", + "quantifier": "*" + } + ] + }, + "OpCompositeExtract": { + "opcode": 81, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger", + "quantifier": "*" + } + ] + }, + "OpCompositeInsert": { + "opcode": 82, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger", + "quantifier": "*" + } + ] + }, + "OpCopyObject": { + "opcode": 83, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpTranspose": { + "opcode": 84, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSampledImage": { + "opcode": 86, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpImageSampleImplicitLod": { + "opcode": 87, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImageSampleExplicitLod": { + "opcode": 88, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands" + } + ] + }, + "OpImageSampleDrefImplicitLod": { + "opcode": 89, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImageSampleDrefExplicitLod": { + "opcode": 90, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands" + } + ] + }, + "OpImageSampleProjImplicitLod": { + "opcode": 91, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImageSampleProjExplicitLod": { + "opcode": 92, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands" + } + ] + }, + "OpImageSampleProjDrefImplicitLod": { + "opcode": 93, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImageSampleProjDrefExplicitLod": { + "opcode": 94, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands" + } + ] + }, + "OpImageFetch": { + "opcode": 95, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImageGather": { + "opcode": 96, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImageDrefGather": { + "opcode": 97, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImageRead": { + "opcode": 98, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImageWrite": { + "opcode": 99, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "ImageOperands", + "quantifier": "?" + } + ] + }, + "OpImage": { + "opcode": 100, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpImageQuerySizeLod": { + "opcode": 103, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpImageQuerySize": { + "opcode": 104, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpImageQueryLod": { + "opcode": 105, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpImageQueryLevels": { + "opcode": 106, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpImageQuerySamples": { + "opcode": 107, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpConvertFToU": { + "opcode": 109, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpConvertFToS": { + "opcode": 110, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpConvertSToF": { + "opcode": 111, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpConvertUToF": { + "opcode": 112, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpUConvert": { + "opcode": 113, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSConvert": { + "opcode": 114, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFConvert": { + "opcode": 115, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpQuantizeToF16": { + "opcode": 116, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitcast": { + "opcode": 124, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSNegate": { + "opcode": 126, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFNegate": { + "opcode": 127, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpIAdd": { + "opcode": 128, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFAdd": { + "opcode": 129, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpISub": { + "opcode": 130, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFSub": { + "opcode": 131, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpIMul": { + "opcode": 132, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFMul": { + "opcode": 133, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpUDiv": { + "opcode": 134, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSDiv": { + "opcode": 135, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFDiv": { + "opcode": 136, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpUMod": { + "opcode": 137, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSRem": { + "opcode": 138, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSMod": { + "opcode": 139, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFRem": { + "opcode": 140, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFMod": { + "opcode": 141, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpVectorTimesScalar": { + "opcode": 142, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpMatrixTimesScalar": { + "opcode": 143, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpVectorTimesMatrix": { + "opcode": 144, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpMatrixTimesVector": { + "opcode": 145, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpMatrixTimesMatrix": { + "opcode": 146, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpOuterProduct": { + "opcode": 147, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpDot": { + "opcode": 148, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpIAddCarry": { + "opcode": 149, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpISubBorrow": { + "opcode": 150, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpUMulExtended": { + "opcode": 151, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSMulExtended": { + "opcode": 152, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAny": { + "opcode": 154, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAll": { + "opcode": 155, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpIsNan": { + "opcode": 156, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpIsInf": { + "opcode": 157, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpLogicalEqual": { + "opcode": 164, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpLogicalNotEqual": { + "opcode": 165, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpLogicalOr": { + "opcode": 166, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpLogicalAnd": { + "opcode": 167, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpLogicalNot": { + "opcode": 168, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSelect": { + "opcode": 169, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpIEqual": { + "opcode": 170, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpINotEqual": { + "opcode": 171, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpUGreaterThan": { + "opcode": 172, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSGreaterThan": { + "opcode": 173, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpUGreaterThanEqual": { + "opcode": 174, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSGreaterThanEqual": { + "opcode": 175, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpULessThan": { + "opcode": 176, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSLessThan": { + "opcode": 177, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpULessThanEqual": { + "opcode": 178, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpSLessThanEqual": { + "opcode": 179, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFOrdEqual": { + "opcode": 180, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFUnordEqual": { + "opcode": 181, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFOrdNotEqual": { + "opcode": 182, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFUnordNotEqual": { + "opcode": 183, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFOrdLessThan": { + "opcode": 184, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFUnordLessThan": { + "opcode": 185, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFOrdGreaterThan": { + "opcode": 186, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFUnordGreaterThan": { + "opcode": 187, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFOrdLessThanEqual": { + "opcode": 188, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFUnordLessThanEqual": { + "opcode": 189, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFOrdGreaterThanEqual": { + "opcode": 190, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFUnordGreaterThanEqual": { + "opcode": 191, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpShiftRightLogical": { + "opcode": 194, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpShiftRightArithmetic": { + "opcode": 195, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpShiftLeftLogical": { + "opcode": 196, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitwiseOr": { + "opcode": 197, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitwiseXor": { + "opcode": 198, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitwiseAnd": { + "opcode": 199, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpNot": { + "opcode": 200, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitFieldInsert": { + "opcode": 201, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitFieldSExtract": { + "opcode": 202, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitFieldUExtract": { + "opcode": 203, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitReverse": { + "opcode": 204, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpBitCount": { + "opcode": 205, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpDPdx": { + "opcode": 207, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpDPdy": { + "opcode": 208, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFwidth": { + "opcode": 209, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpDPdxFine": { + "opcode": 210, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpDPdyFine": { + "opcode": 211, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFwidthFine": { + "opcode": 212, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpDPdxCoarse": { + "opcode": 213, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpDPdyCoarse": { + "opcode": 214, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpFwidthCoarse": { + "opcode": 215, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpControlBarrier": { + "opcode": 224, + "operands": [ + { + "kind": "IdScope" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + } + ] + }, + "OpMemoryBarrier": { + "opcode": 225, + "operands": [ + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + } + ] + }, + "OpAtomicLoad": { + "opcode": 227, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + } + ] + }, + "OpAtomicStore": { + "opcode": 228, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicExchange": { + "opcode": 229, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicCompareExchange": { + "opcode": 230, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicIIncrement": { + "opcode": 232, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + } + ] + }, + "OpAtomicIDecrement": { + "opcode": 233, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + } + ] + }, + "OpAtomicIAdd": { + "opcode": 234, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicISub": { + "opcode": 235, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicSMin": { + "opcode": 236, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicUMin": { + "opcode": 237, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicSMax": { + "opcode": 238, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicUMax": { + "opcode": 239, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicAnd": { + "opcode": 240, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicOr": { + "opcode": 241, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpAtomicXor": { + "opcode": 242, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdScope" + }, + { + "kind": "IdMemorySemantics" + }, + { + "kind": "IdRef" + } + ] + }, + "OpPhi": { + "opcode": 245, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "PairIdRefIdRef", + "quantifier": "*" + } + ] + }, + "OpLoopMerge": { + "opcode": 246, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "LoopControl" + } + ] + }, + "OpSelectionMerge": { + "opcode": 247, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "SelectionControl" + } + ] + }, + "OpLabel": { + "opcode": 248, + "operands": [ + { + "kind": "IdResult" + } + ] + }, + "OpBranch": { + "opcode": 249, + "operands": [ + { + "kind": "IdRef" + } + ] + }, + "OpBranchConditional": { + "opcode": 250, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger", + "quantifier": "*" + } + ] + }, + "OpSwitch": { + "opcode": 251, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + }, + { + "kind": "PairLiteralIntegerIdRef", + "quantifier": "*" + } + ] + }, + "OpKill": { + "opcode": 252, + "operands": [ + + ] + }, + "OpReturn": { + "opcode": 253, + "operands": [ + + ] + }, + "OpReturnValue": { + "opcode": 254, + "operands": [ + { + "kind": "IdRef" + } + ] + }, + "OpUnreachable": { + "opcode": 255, + "operands": [ + + ] + }, + "OpNoLine": { + "opcode": 317, + "operands": [ + + ] + }, + "OpModuleProcessed": { + "opcode": 330, + "operands": [ + { + "kind": "LiteralString" + } + ] + }, + "OpExecutionModeId": { + "opcode": 331, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "ExecutionMode" + } + ] + }, + "OpDecorateId": { + "opcode": 332, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "Decoration" + } + ] + }, + "OpCopyLogical": { + "opcode": 400, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + } + ] + }, + "OpPtrEqual": { + "opcode": 401, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpPtrNotEqual": { + "opcode": 402, + "operands": [ + { + "kind": "IdResultType" + }, + { + "kind": "IdResult" + }, + { + "kind": "IdRef" + }, + { + "kind": "IdRef" + } + ] + }, + "OpDecorateString": { + "opcode": 5632, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "Decoration" + } + ] + }, + "OpDecorateStringGOOGLE": { + "opcode": 5632, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "Decoration" + } + ] + }, + "OpMemberDecorateString": { + "opcode": 5633, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "Decoration" + } + ] + }, + "OpMemberDecorateStringGOOGLE": { + "opcode": 5633, + "operands": [ + { + "kind": "IdRef" + }, + { + "kind": "LiteralInteger" + }, + { + "kind": "Decoration" + } + ] + } + }, + "operand_kinds": { + "ImageOperands": { + "type": "BitEnum", + "values": { + "None": { + "value": 0 + }, + "Bias": { + "value": 1, + "params": [ + "IdRef" + ] + }, + "Lod": { + "value": 2, + "params": [ + "IdRef" + ] + }, + "Grad": { + "value": 4, + "params": [ + "IdRef", + "IdRef" + ] + }, + "ConstOffset": { + "value": 8, + "params": [ + "IdRef" + ] + }, + "Sample": { + "value": 64, + "params": [ + "IdRef" + ] + }, + "MakeTexelAvailable": { + "value": 256, + "params": [ + "IdScope" + ] + }, + "MakeTexelAvailableKHR": { + "value": 256, + "params": [ + "IdScope" + ] + }, + "MakeTexelVisible": { + "value": 512, + "params": [ + "IdScope" + ] + }, + "MakeTexelVisibleKHR": { + "value": 512, + "params": [ + "IdScope" + ] + }, + "NonPrivateTexel": { + "value": 1024 + }, + "NonPrivateTexelKHR": { + "value": 1024 + }, + "VolatileTexel": { + "value": 2048 + }, + "VolatileTexelKHR": { + "value": 2048 + }, + "SignExtend": { + "value": 4096 + }, + "ZeroExtend": { + "value": 8192 + } + } + }, + "FPFastMathMode": { + "type": "BitEnum", + "values": { + "None": { + "value": 0 + } + } + }, + "SelectionControl": { + "type": "BitEnum", + "values": { + "None": { + "value": 0 + }, + "Flatten": { + "value": 1 + }, + "DontFlatten": { + "value": 2 + } + } + }, + "LoopControl": { + "type": "BitEnum", + "values": { + "None": { + "value": 0 + }, + "Unroll": { + "value": 1 + }, + "DontUnroll": { + "value": 2 + }, + "DependencyInfinite": { + "value": 4 + }, + "DependencyLength": { + "value": 8, + "params": [ + "LiteralInteger" + ] + }, + "MinIterations": { + "value": 16, + "params": [ + "LiteralInteger" + ] + }, + "MaxIterations": { + "value": 32, + "params": [ + "LiteralInteger" + ] + }, + "IterationMultiple": { + "value": 64, + "params": [ + "LiteralInteger" + ] + }, + "PeelCount": { + "value": 128, + "params": [ + "LiteralInteger" + ] + }, + "PartialCount": { + "value": 256, + "params": [ + "LiteralInteger" + ] + } + } + }, + "FunctionControl": { + "type": "BitEnum", + "values": { + "None": { + "value": 0 + }, + "Inline": { + "value": 1 + }, + "DontInline": { + "value": 2 + }, + "Pure": { + "value": 4 + }, + "Const": { + "value": 8 + } + } + }, + "MemorySemantics": { + "type": "BitEnum", + "values": { + "Relaxed": { + "value": 0 + }, + "None": { + "value": 0 + }, + "Acquire": { + "value": 2 + }, + "Release": { + "value": 4 + }, + "AcquireRelease": { + "value": 8 + }, + "SequentiallyConsistent": { + "value": 16 + }, + "UniformMemory": { + "value": 64 + }, + "SubgroupMemory": { + "value": 128 + }, + "WorkgroupMemory": { + "value": 256 + }, + "CrossWorkgroupMemory": { + "value": 512 + }, + "ImageMemory": { + "value": 2048 + }, + "OutputMemory": { + "value": 4096 + }, + "OutputMemoryKHR": { + "value": 4096 + }, + "MakeAvailable": { + "value": 8192 + }, + "MakeAvailableKHR": { + "value": 8192 + }, + "MakeVisible": { + "value": 16384 + }, + "MakeVisibleKHR": { + "value": 16384 + }, + "Volatile": { + "value": 32768 + } + } + }, + "MemoryAccess": { + "type": "BitEnum", + "values": { + "None": { + "value": 0 + }, + "Volatile": { + "value": 1 + }, + "Aligned": { + "value": 2, + "params": [ + "LiteralInteger" + ] + }, + "Nontemporal": { + "value": 4 + }, + "MakePointerAvailable": { + "value": 8, + "params": [ + "IdScope" + ] + }, + "MakePointerAvailableKHR": { + "value": 8, + "params": [ + "IdScope" + ] + }, + "MakePointerVisible": { + "value": 16, + "params": [ + "IdScope" + ] + }, + "MakePointerVisibleKHR": { + "value": 16, + "params": [ + "IdScope" + ] + }, + "NonPrivatePointer": { + "value": 32 + }, + "NonPrivatePointerKHR": { + "value": 32 + } + } + }, + "KernelProfilingInfo": { + "type": "BitEnum", + "values": { + "None": { + "value": 0 + } + } + }, + "SourceLanguage": { + "type": "ValueEnum", + "values": { + "Unknown": { + "value": 0 + }, + "ESSL": { + "value": 1 + }, + "GLSL": { + "value": 2 + }, + "OpenCL_C": { + "value": 3 + }, + "OpenCL_CPP": { + "value": 4 + }, + "HLSL": { + "value": 5 + } + } + }, + "ExecutionModel": { + "type": "ValueEnum", + "values": { + "Vertex": { + "value": 0 + }, + "Fragment": { + "value": 4 + }, + "GLCompute": { + "value": 5 + } + } + }, + "AddressingModel": { + "type": "ValueEnum", + "values": { + "Logical": { + "value": 0 + } + } + }, + "MemoryModel": { + "type": "ValueEnum", + "values": { + "Simple": { + "value": 0 + }, + "GLSL450": { + "value": 1 + }, + "Vulkan": { + "value": 3 + }, + "VulkanKHR": { + "value": 3 + } + } + }, + "ExecutionMode": { + "type": "ValueEnum", + "values": { + "PixelCenterInteger": { + "value": 6 + }, + "OriginUpperLeft": { + "value": 7 + }, + "OriginLowerLeft": { + "value": 8 + }, + "EarlyFragmentTests": { + "value": 9 + }, + "DepthReplacing": { + "value": 12 + }, + "DepthGreater": { + "value": 14 + }, + "DepthLess": { + "value": 15 + }, + "DepthUnchanged": { + "value": 16 + }, + "LocalSize": { + "value": 17, + "params": [ + "LiteralInteger", + "LiteralInteger", + "LiteralInteger" + ] + }, + "LocalSizeId": { + "value": 38, + "params": [ + "IdRef", + "IdRef", + "IdRef" + ] + } + } + }, + "StorageClass": { + "type": "ValueEnum", + "values": { + "UniformConstant": { + "value": 0 + }, + "Input": { + "value": 1 + }, + "Uniform": { + "value": 2 + }, + "Output": { + "value": 3 + }, + "Workgroup": { + "value": 4 + }, + "CrossWorkgroup": { + "value": 5 + }, + "Private": { + "value": 6 + }, + "Function": { + "value": 7 + }, + "PushConstant": { + "value": 9 + }, + "Image": { + "value": 11 + }, + "StorageBuffer": { + "value": 12 + } + } + }, + "Dim": { + "type": "ValueEnum", + "values": { + "1D": { + "value": 0 + }, + "2D": { + "value": 1 + }, + "3D": { + "value": 2 + }, + "Cube": { + "value": 3 + } + } + }, + "ImageFormat": { + "type": "ValueEnum", + "values": { + "Unknown": { + "value": 0 + }, + "Rgba32f": { + "value": 1 + }, + "Rgba16f": { + "value": 2 + }, + "R32f": { + "value": 3 + }, + "Rgba8": { + "value": 4 + }, + "Rgba8Snorm": { + "value": 5 + }, + "Rgba32i": { + "value": 21 + }, + "Rgba16i": { + "value": 22 + }, + "Rgba8i": { + "value": 23 + }, + "R32i": { + "value": 24 + }, + "Rgba32ui": { + "value": 30 + }, + "Rgba16ui": { + "value": 31 + }, + "Rgba8ui": { + "value": 32 + }, + "R32ui": { + "value": 33 + } + } + }, + "FPRoundingMode": { + "type": "ValueEnum", + "values": { + "RTE": { + "value": 0 + }, + "RTZ": { + "value": 1 + }, + "RTP": { + "value": 2 + }, + "RTN": { + "value": 3 + } + } + }, + "Decoration": { + "type": "ValueEnum", + "values": { + "RelaxedPrecision": { + "value": 0 + }, + "SpecId": { + "value": 1, + "params": [ + "LiteralInteger" + ] + }, + "Block": { + "value": 2 + }, + "BufferBlock": { + "value": 3 + }, + "RowMajor": { + "value": 4 + }, + "ColMajor": { + "value": 5 + }, + "ArrayStride": { + "value": 6, + "params": [ + "LiteralInteger" + ] + }, + "MatrixStride": { + "value": 7, + "params": [ + "LiteralInteger" + ] + }, + "GLSLShared": { + "value": 8 + }, + "GLSLPacked": { + "value": 9 + }, + "BuiltIn": { + "value": 11, + "params": [ + "BuiltIn" + ] + }, + "NoPerspective": { + "value": 13 + }, + "Flat": { + "value": 14 + }, + "Centroid": { + "value": 16 + }, + "Invariant": { + "value": 18 + }, + "Restrict": { + "value": 19 + }, + "Aliased": { + "value": 20 + }, + "Volatile": { + "value": 21 + }, + "Coherent": { + "value": 23 + }, + "NonWritable": { + "value": 24 + }, + "NonReadable": { + "value": 25 + }, + "Uniform": { + "value": 26 + }, + "UniformId": { + "value": 27, + "params": [ + "IdScope" + ] + }, + "Location": { + "value": 30, + "params": [ + "LiteralInteger" + ] + }, + "Component": { + "value": 31, + "params": [ + "LiteralInteger" + ] + }, + "Index": { + "value": 32, + "params": [ + "LiteralInteger" + ] + }, + "Binding": { + "value": 33, + "params": [ + "LiteralInteger" + ] + }, + "DescriptorSet": { + "value": 34, + "params": [ + "LiteralInteger" + ] + }, + "Offset": { + "value": 35, + "params": [ + "LiteralInteger" + ] + }, + "FPRoundingMode": { + "value": 39, + "params": [ + "FPRoundingMode" + ] + }, + "NoContraction": { + "value": 42 + }, + "NoSignedWrap": { + "value": 4469 + }, + "NoUnsignedWrap": { + "value": 4470 + }, + "ExplicitInterpAMD": { + "value": 4999 + }, + "CounterBuffer": { + "value": 5634, + "params": [ + "IdRef" + ] + }, + "HlslCounterBufferGOOGLE": { + "value": 5634, + "params": [ + "IdRef" + ] + }, + "UserSemantic": { + "value": 5635, + "params": [ + "LiteralString" + ] + }, + "HlslSemanticGOOGLE": { + "value": 5635, + "params": [ + "LiteralString" + ] + }, + "UserTypeGOOGLE": { + "value": 5636, + "params": [ + "LiteralString" + ] + } + } + }, + "BuiltIn": { + "type": "ValueEnum", + "values": { + "Position": { + "value": 0 + }, + "PointSize": { + "value": 1 + }, + "VertexId": { + "value": 5 + }, + "InstanceId": { + "value": 6 + }, + "FragCoord": { + "value": 15 + }, + "PointCoord": { + "value": 16 + }, + "FrontFacing": { + "value": 17 + }, + "SampleMask": { + "value": 20 + }, + "FragDepth": { + "value": 22 + }, + "HelperInvocation": { + "value": 23 + }, + "NumWorkgroups": { + "value": 24 + }, + "WorkgroupSize": { + "value": 25 + }, + "WorkgroupId": { + "value": 26 + }, + "LocalInvocationId": { + "value": 27 + }, + "GlobalInvocationId": { + "value": 28 + }, + "LocalInvocationIndex": { + "value": 29 + }, + "VertexIndex": { + "value": 42 + }, + "InstanceIndex": { + "value": 43 + }, + "BaryCoordNoPerspAMD": { + "value": 4992 + }, + "BaryCoordNoPerspCentroidAMD": { + "value": 4993 + }, + "BaryCoordNoPerspSampleAMD": { + "value": 4994 + }, + "BaryCoordSmoothAMD": { + "value": 4995 + }, + "BaryCoordSmoothCentroidAMD": { + "value": 4996 + }, + "BaryCoordSmoothSampleAMD": { + "value": 4997 + }, + "BaryCoordPullModelAMD": { + "value": 4998 + } + } + }, + "Scope": { + "type": "ValueEnum", + "values": { + "CrossDevice": { + "value": 0 + }, + "Device": { + "value": 1 + }, + "Workgroup": { + "value": 2 + }, + "Subgroup": { + "value": 3 + }, + "Invocation": { + "value": 4 + }, + "QueueFamily": { + "value": 5 + }, + "QueueFamilyKHR": { + "value": 5 + } + } + }, + "Capability": { + "type": "ValueEnum", + "values": { + "Matrix": { + "value": 0 + }, + "Shader": { + "value": 1 + }, + "Geometry": { + "value": 2 + }, + "Tessellation": { + "value": 3 + }, + "Addresses": { + "value": 4 + }, + "Linkage": { + "value": 5 + }, + "Kernel": { + "value": 6 + }, + "Float16": { + "value": 9 + }, + "Float64": { + "value": 10 + }, + "Int64": { + "value": 11 + }, + "Groups": { + "value": 18 + }, + "AtomicStorage": { + "value": 21 + }, + "Int16": { + "value": 22 + }, + "ImageGatherExtended": { + "value": 25 + }, + "StorageImageMultisample": { + "value": 27 + }, + "UniformBufferArrayDynamicIndexing": { + "value": 28 + }, + "SampledImageArrayDynamicIndexing": { + "value": 29 + }, + "StorageBufferArrayDynamicIndexing": { + "value": 30 + }, + "StorageImageArrayDynamicIndexing": { + "value": 31 + }, + "ClipDistance": { + "value": 32 + }, + "CullDistance": { + "value": 33 + }, + "SampleRateShading": { + "value": 35 + }, + "SampledRect": { + "value": 37 + }, + "Int8": { + "value": 39 + }, + "InputAttachment": { + "value": 40 + }, + "SparseResidency": { + "value": 41 + }, + "MinLod": { + "value": 42 + }, + "Sampled1D": { + "value": 43 + }, + "Image1D": { + "value": 44 + }, + "SampledCubeArray": { + "value": 45 + }, + "SampledBuffer": { + "value": 46 + }, + "ImageMSArray": { + "value": 48 + }, + "StorageImageExtendedFormats": { + "value": 49 + }, + "ImageQuery": { + "value": 50 + }, + "DerivativeControl": { + "value": 51 + }, + "InterpolationFunction": { + "value": 52 + }, + "TransformFeedback": { + "value": 53 + }, + "StorageImageReadWithoutFormat": { + "value": 55 + }, + "StorageImageWriteWithoutFormat": { + "value": 56 + }, + "GroupNonUniform": { + "value": 61 + }, + "ShaderLayer": { + "value": 69 + }, + "ShaderViewportIndex": { + "value": 70 + }, + "SubgroupBallotKHR": { + "value": 4423 + }, + "DrawParameters": { + "value": 4427 + }, + "SubgroupVoteKHR": { + "value": 4431 + }, + "StorageBuffer16BitAccess": { + "value": 4433 + }, + "StorageUniformBufferBlock16": { + "value": 4433 + }, + "StoragePushConstant16": { + "value": 4435 + }, + "StorageInputOutput16": { + "value": 4436 + }, + "DeviceGroup": { + "value": 4437 + }, + "MultiView": { + "value": 4439 + }, + "VariablePointersStorageBuffer": { + "value": 4441 + }, + "AtomicStorageOps": { + "value": 4445 + }, + "SampleMaskPostDepthCoverage": { + "value": 4447 + }, + "StorageBuffer8BitAccess": { + "value": 4448 + }, + "StoragePushConstant8": { + "value": 4450 + }, + "DenormPreserve": { + "value": 4464 + }, + "DenormFlushToZero": { + "value": 4465 + }, + "SignedZeroInfNanPreserve": { + "value": 4466 + }, + "RoundingModeRTE": { + "value": 4467 + }, + "RoundingModeRTZ": { + "value": 4468 + }, + "Float16ImageAMD": { + "value": 5008 + }, + "ImageGatherBiasLodAMD": { + "value": 5009 + }, + "FragmentMaskAMD": { + "value": 5010 + }, + "StencilExportEXT": { + "value": 5013 + }, + "ImageReadWriteLodAMD": { + "value": 5015 + }, + "ShaderClockKHR": { + "value": 5055 + }, + "FragmentFullyCoveredEXT": { + "value": 5265 + }, + "MeshShadingNV": { + "value": 5266 + }, + "ImageFootprintNV": { + "value": 5282 + }, + "FragmentBarycentricNV": { + "value": 5284 + }, + "ComputeDerivativeGroupQuadsNV": { + "value": 5288 + }, + "FragmentDensityEXT": { + "value": 5291 + }, + "ShadingRateNV": { + "value": 5291 + }, + "GroupNonUniformPartitionedNV": { + "value": 5297 + }, + "ShaderNonUniform": { + "value": 5301 + }, + "ShaderNonUniformEXT": { + "value": 5301 + }, + "RuntimeDescriptorArray": { + "value": 5302 + }, + "RuntimeDescriptorArrayEXT": { + "value": 5302 + }, + "RayTracingNV": { + "value": 5340 + }, + "VulkanMemoryModel": { + "value": 5345 + }, + "VulkanMemoryModelKHR": { + "value": 5345 + }, + "VulkanMemoryModelDeviceScope": { + "value": 5346 + }, + "VulkanMemoryModelDeviceScopeKHR": { + "value": 5346 + }, + "PhysicalStorageBufferAddresses": { + "value": 5347 + }, + "PhysicalStorageBufferAddressesEXT": { + "value": 5347 + }, + "ComputeDerivativeGroupLinearNV": { + "value": 5350 + }, + "CooperativeMatrixNV": { + "value": 5357 + }, + "FragmentShaderSampleInterlockEXT": { + "value": 5363 + }, + "FragmentShaderShadingRateInterlockEXT": { + "value": 5372 + }, + "ShaderSMBuiltinsNV": { + "value": 5373 + }, + "FragmentShaderPixelInterlockEXT": { + "value": 5378 + }, + "DemoteToHelperInvocationEXT": { + "value": 5379 + }, + "SubgroupShuffleINTEL": { + "value": 5568 + }, + "SubgroupBufferBlockIOINTEL": { + "value": 5569 + }, + "SubgroupImageBlockIOINTEL": { + "value": 5570 + }, + "SubgroupImageMediaBlockIOINTEL": { + "value": 5579 + }, + "IntegerFunctions2INTEL": { + "value": 5584 + }, + "SubgroupAvcMotionEstimationINTEL": { + "value": 5696 + }, + "SubgroupAvcMotionEstimationIntraINTEL": { + "value": 5697 + }, + "SubgroupAvcMotionEstimationChromaINTEL": { + "value": 5698 + } + } + } + }, + "ext": { + "Round": 1, + "RoundEven": 2, + "Trunc": 3, + "FAbs": 4, + "SAbs": 5, + "FSign": 6, + "SSign": 7, + "Floor": 8, + "Ceil": 9, + "Fract": 10, + "Radians": 11, + "Degrees": 12, + "Sin": 13, + "Cos": 14, + "Tan": 15, + "Asin": 16, + "Acos": 17, + "Atan": 18, + "Sinh": 19, + "Cosh": 20, + "Tanh": 21, + "Asinh": 22, + "Acosh": 23, + "Atanh": 24, + "Atan2": 25, + "Pow": 26, + "Exp": 27, + "Log": 28, + "Exp2": 29, + "Log2": 30, + "Sqrt": 31, + "InverseSqrt": 32, + "Determinant": 33, + "MatrixInverse": 34, + "Modf": 35, + "ModfStruct": 36, + "FMin": 37, + "UMin": 38, + "SMin": 39, + "FMax": 40, + "UMax": 41, + "SMax": 42, + "FClamp": 43, + "UClamp": 44, + "SClamp": 45, + "FMix": 46, + "IMix": 47, + "Step": 48, + "SmoothStep": 49, + "Fma": 50, + "Frexp": 51, + "FrexpStruct": 52, + "Ldexp": 53, + "PackSnorm4x8": 54, + "PackUnorm4x8": 55, + "PackSnorm2x16": 56, + "PackUnorm2x16": 57, + "PackHalf2x16": 58, + "PackDouble2x32": 59, + "UnpackSnorm2x16": 60, + "UnpackUnorm2x16": 61, + "UnpackHalf2x16": 62, + "UnpackSnorm4x8": 63, + "UnpackUnorm4x8": 64, + "UnpackDouble2x32": 65, + "Length": 66, + "Distance": 67, + "Cross": 68, + "Normalize": 69, + "FaceForward": 70, + "Reflect": 71, + "Refract": 72, + "FindILsb": 73, + "FindSMsb": 74, + "FindUMsb": 75, + "InterpolateAtCentroid": 76, + "InterpolateAtSample": 77, + "InterpolateAtOffset": 78, + "NMin": 79, + "NMax": 80, + "NClamp": 81 + } +} diff --git a/third_party/spirv-tools/tools/sva/src/sva.js b/third_party/spirv-tools/tools/sva/src/sva.js new file mode 100644 index 0000000..c76ed29 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/sva.js @@ -0,0 +1,40 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Parser from "./parser.js"; +import Lexer from "./lexer.js"; +import Assembler from "./assembler.js"; + +import grammar from "./spirv.data.js"; + +export default class SVA { + /** + * Attempts to convert |input| SPIR-V assembly into SPIR-V binary. + * + * @param {String} the input string containing the assembly + * @return {Uint32Array|string} returns a Uint32Array containing the binary + * SPIR-V or a string on error. + */ + static assemble(input) { + let l = new Lexer(input); + let p = new Parser(grammar, l); + + let ast = p.parse(); + if (ast === undefined) + return p.error; + + let a = new Assembler(ast); + return a.assemble(); + } +} diff --git a/third_party/spirv-tools/tools/sva/src/token.js b/third_party/spirv-tools/tools/sva/src/token.js new file mode 100644 index 0000000..3813191 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/src/token.js @@ -0,0 +1,55 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const TokenType = { + kEOF: "end of file", + kError: "error", + + kIdentifier: "identifier", + + kIntegerLiteral: "integer_literal", + kFloatLiteral: "float_literal", + kStringLiteral: "string_literal", + kResultId: "result_id", + + kOp: "Op", + kEqual: "=", + kPipe: "|", +}; + +class Token { + /** + * @param {TokenType} type The type of token + * @param {Integer} line The line number this token was on + * @param {Any} data Data attached to the token + * @param {Integer} bits If the type is a float or integer the bit width + */ + constructor(type, line, data) { + this.type_ = type; + this.line_ = line; + this.data_ = data; + this.bits_ = 0; + } + + get type() { return this.type_; } + get line() { return this.line_; } + + get data() { return this.data_; } + set data(val) { this.data_ = val; } + + get bits() { return this.bits_; } + set bits(val) { this.bits_ = val; } +} + +export {Token, TokenType}; diff --git a/third_party/spirv-tools/tools/sva/tests/empty_main.spv_asm b/third_party/spirv-tools/tools/sva/tests/empty_main.spv_asm new file mode 100644 index 0000000..ad6e64b --- /dev/null +++ b/third_party/spirv-tools/tools/sva/tests/empty_main.spv_asm @@ -0,0 +1,18 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 6 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 440 + OpName %main "main" + %void = OpTypeVoid + %3 = OpTypeFunction %void + %main = OpFunction %void None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd diff --git a/third_party/spirv-tools/tools/sva/tests/index.html b/third_party/spirv-tools/tools/sva/tests/index.html new file mode 100644 index 0000000..dd02847 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/tests/index.html @@ -0,0 +1,23 @@ + + + + + + +
+ + + + diff --git a/third_party/spirv-tools/tools/sva/tests/simple.spv_asm b/third_party/spirv-tools/tools/sva/tests/simple.spv_asm new file mode 100644 index 0000000..b4b3f67 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/tests/simple.spv_asm @@ -0,0 +1,30 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 7 +; Bound: 14 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 330 + OpName %main "main" + OpName %gl_FragColor "gl_FragColor" + OpDecorate %gl_FragColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%float_0_400000006 = OpConstant %float 0.400000006 +%float_0_800000012 = OpConstant %float 0.800000012 + %float_1 = OpConstant %float 1 + %13 = OpConstantComposite %v4float %float_0_400000006 %float_0_400000006 %float_0_800000012 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + OpStore %gl_FragColor %13 + OpReturn + OpFunctionEnd + diff --git a/third_party/spirv-tools/tools/sva/tools/process_grammar.rb b/third_party/spirv-tools/tools/sva/tools/process_grammar.rb new file mode 100755 index 0000000..1bbff68 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/tools/process_grammar.rb @@ -0,0 +1,119 @@ +#!/usr/bin/env ruby + +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'json' + +GRAMMAR = "../../external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json" +GLSL = "../../external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json" + +CAPABILITIES = %w( + Matrix + Shader + Sampled1D + Image1D + DerivativeControl + ImageQuery + VulkanMemoryModel +) + +g = JSON.parse(File.open(GRAMMAR).read) +magic = g['magic_number'] +vers = [g['major_version'], g['minor_version']] +instructions = {} + +g['instructions'].each do |inst| + if (inst.has_key?('capabilities')) + skip = true + inst['capabilities'].each do |cap| + if CAPABILITIES.include?(cap) + skip = false + break + end + end + next if skip + end + + op = { + opcode: inst['opcode'], + operands: [] + } + + if !inst['operands'].nil? + inst['operands'].each do |operand| + operand.delete('name') + op[:operands] << operand + end + end + + instructions[inst['opname']] = op +end + +operand_kinds = {} +g['operand_kinds'].each do |op_kind| + next if op_kind['category'] !~ /Enum/ + + kind = { + type: op_kind['category'], + values: {} + } + + op_kind['enumerants'].each do |enum| + if (enum.has_key?('capabilities')) + skip = true + enum['capabilities'].each do |cap| + if CAPABILITIES.include?(cap) + skip = false + break + end + end + next if skip + end + + v = if op_kind['category'] == 'BitEnum' + enum['value'].to_i(16) + else + enum['value'].to_i + end + params = [] + if enum.has_key?('parameters') + enum['parameters'].each do |param| + params << param['kind'] + end + end + kind[:values][enum['enumerant']] = {value: v} + kind[:values][enum['enumerant']][:params] = params unless params.empty? + end + + next if kind[:values].empty? + operand_kinds[op_kind['kind']] = kind +end + +# We only support GLSL extensions at the moment. +ext = {} +glsl = JSON.parse(File.open(GLSL).read) +glsl['instructions'].each do |inst| + ext[inst['opname']] = inst['opcode'] +end + +puts "/*#{g['copyright'].join("\n")}*/" +puts "\n// THIS FILE IS GENERATED WITH tools/process_grammar.rb\n\n" +puts "export default " + JSON.pretty_generate({ + magic: magic, + version: vers, + instructions: instructions, + operand_kinds: operand_kinds, + ext: ext +}) diff --git a/third_party/spirv-tools/tools/sva/yarn.lock b/third_party/spirv-tools/tools/sva/yarn.lock new file mode 100644 index 0000000..34a1808 --- /dev/null +++ b/third_party/spirv-tools/tools/sva/yarn.lock @@ -0,0 +1,1778 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" + integrity sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw== + dependencies: + "@babel/highlight" "^7.0.0" + +"@babel/highlight@^7.0.0": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.5.0.tgz#56d11312bd9248fa619591d02472be6e8cb32540" + integrity sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ== + dependencies: + chalk "^2.0.0" + esutils "^2.0.2" + js-tokens "^4.0.0" + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/node@^12.7.5": + version "12.7.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.5.tgz#e19436e7f8e9b4601005d73673b6dc4784ffcc2f" + integrity sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w== + +"@zeit/schemas@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@zeit/schemas/-/schemas-2.6.0.tgz#004e8e553b4cd53d538bd38eac7bcbf58a867fe3" + integrity sha512-uUrgZ8AxS+Lio0fZKAipJjAh415JyrOZowliZAzmnJSsf7piVL5w+G0+gFJ0KSu3QRhvui/7zuvpLz03YjXAhg== + +accepts@~1.3.5: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-jsx@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" + integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== + +acorn@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== + +ajv@6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9" + integrity sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^6.10.0, ajv@^6.10.2: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-align@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" + integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= + dependencies: + string-width "^2.0.0" + +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +arch@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" + integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== + +arg@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-2.0.0.tgz#c06e7ff69ab05b3a4a03ebe0407fac4cba657545" + integrity sha512-XxNTUzKnz1ctK3ZIcI2XUPlD96wbHP2nGqkPKpvk/HNRlPveYrXIVSTk9m3LcqOgDPg3B1nMvdV/K8wZd7PG4w== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +boxen@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" + integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== + dependencies: + ansi-align "^2.0.0" + camelcase "^4.0.0" + chalk "^2.0.1" + cli-boxes "^1.0.0" + string-width "^2.0.0" + term-size "^1.2.0" + widest-line "^2.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + +chalk@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= + +clipboardy@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" + integrity sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA== + dependencies: + arch "^2.1.0" + execa "^0.8.0" + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + integrity sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ== + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +compressible@~2.0.14: + version "2.0.17" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1" + integrity sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw== + dependencies: + mime-db ">= 1.40.0 < 2" + +compression@1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.3.tgz#27e0e176aaf260f7f2c2813c3e440adb9f1993db" + integrity sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.14" + debug "2.6.9" + on-headers "~1.0.1" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +debug@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== + dependencies: + once "^1.4.0" + +es-abstract@^1.5.1: + version "1.14.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.14.2.tgz#7ce108fad83068c8783c3cdf62e504e084d8c497" + integrity sha512-DgoQmbpFNOofkjJtKwr87Ma5EW4Dc8fWhD0R+ndq7Oc456ivUfGOOP6oAZTTKl5/CcNMP+EN+e3/iUzgE0veZg== + dependencies: + es-to-primitive "^1.2.0" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.0" + is-callable "^1.1.4" + is-regex "^1.0.4" + object-inspect "^1.6.0" + object-keys "^1.1.1" + string.prototype.trimleft "^2.0.0" + string.prototype.trimright "^2.0.0" + +es-to-primitive@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" + integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" + integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== + dependencies: + eslint-visitor-keys "^1.0.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +eslint@^6.3.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.4.0.tgz#5aa9227c3fbe921982b2eda94ba0d7fae858611a" + integrity sha512-WTVEzK3lSFoXUovDHEbkJqCVPEPwbhCq4trDktNI6ygs7aO41d4cDT0JFAT5MivzZeVLWlg7vHL+bgrQv/t3vA== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^1.4.2" + eslint-visitor-keys "^1.1.0" + espree "^6.1.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.4.1" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.14" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^6.1.2" + strip-ansi "^5.2.0" + strip-json-comments "^3.0.1" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +esm@^3.2.25: + version "3.2.25" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== + +espree@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" + integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== + dependencies: + acorn "^7.0.0" + acorn-jsx "^5.0.2" + eslint-visitor-keys "^1.1.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" + integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-url-parser@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha1-9K8+qfNNiicc9YrSs3WfQx8LMY0= + dependencies: + punycode "^1.3.2" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +find-up@3.0.0, find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + +flatted@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" + integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +glob-parent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" + integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== + dependencies: + is-glob "^4.0.1" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= + +has@^1.0.1, has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.1.0.tgz#6d33fa1dcef6df930fae003446f33415af905118" + integrity sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== + +inquirer@^6.4.1: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +is-buffer@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + +is-callable@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= + dependencies: + has "^1.0.1" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-symbol@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" + integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== + dependencies: + has-symbols "^1.0.0" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@3.13.1, js-yaml@^3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14: + version "4.17.19" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b" + integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ== + +log-symbols@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + +"mime-db@>= 1.40.0 < 2": + version "1.41.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.41.0.tgz#9110408e1f6aa1b34aef51f2c9df3caddf46b6a0" + integrity sha512-B5gxBI+2K431XW8C2rcc/lhppbuji67nf9v39eH8pkWoZDxnAL0PxdpH32KYRScniF8qDHBDlI+ipgg5WrCUYw== + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + integrity sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ== + +mime-types@2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + integrity sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ== + dependencies: + mime-db "~1.33.0" + +mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + +mkdirp@0.5.1, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mocha@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-6.2.0.tgz#f896b642843445d1bb8bca60eabd9206b8916e56" + integrity sha512-qwfFgY+7EKAAUAdv7VYMZQknI7YJSGesxHyhn6qD52DV8UcSZs5XwCifcZGMVIE4a5fbmhvbotxC0DLQ0oKohQ== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "2.2.0" + minimatch "3.0.4" + mkdirp "0.5.1" + ms "2.1.1" + node-environment-flags "1.0.5" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.2.2" + yargs-parser "13.0.0" + yargs-unparser "1.5.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-environment-flags@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.5.tgz#fa930275f5bf5dae188d6192b24b4c8bbac3d76a" + integrity sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +object-inspect@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + +on-headers@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-locale@^3.0.0, os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" + integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== + dependencies: + p-try "^2.0.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-to-regexp@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45" + integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ== + +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +range-parser@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +rc@^1.0.1, rc@^1.1.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +registry-auth-token@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" + integrity sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ== + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= + dependencies: + rc "^1.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rollup@^1.21.4: + version "1.21.4" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.21.4.tgz#00a41a30f90095db890301b226cbe2918e4cf54d" + integrity sha512-Pl512XVCmVzgcBz5h/3Li4oTaoDcmpuFZ+kdhS/wLreALz//WuDAMfomD3QEYl84NkDu6Z6wV9twlcREb4qQsw== + dependencies: + "@types/estree" "0.0.39" + "@types/node" "^12.7.5" + acorn "^7.0.0" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + +rxjs@^6.4.0: + version "6.5.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" + integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.5.0, semver@^5.7.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.1.2: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +serve-handler@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/serve-handler/-/serve-handler-6.1.0.tgz#f1606dc6ff8f9029a1ee042c11dfe7903a5cb92e" + integrity sha512-63N075Tn3PsFYcu0NVV7tb367UbiW3gnC+/50ohL4oqOhAG6bmbaWqiRcXQgbzqc0ALBjSAzg7VTfa0Qw4E3hA== + dependencies: + bytes "3.0.0" + content-disposition "0.5.2" + fast-url-parser "1.1.3" + mime-types "2.1.18" + minimatch "3.0.4" + path-is-inside "1.0.2" + path-to-regexp "2.2.1" + range-parser "1.2.0" + +serve@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/serve/-/serve-11.1.0.tgz#1bfe2f4a08d0130cbf44711cdb7996cb742172e0" + integrity sha512-+4wpDtOSS+4ZLyDWMxThutA3iOTawX2+yDovOI8cjOUOmemyvNlHyFAsezBlSgbZKTYChI3tzA1Mh0z6XZ62qA== + dependencies: + "@zeit/schemas" "2.6.0" + ajv "6.5.3" + arg "2.0.0" + boxen "1.3.0" + chalk "2.4.1" + clipboardy "1.2.3" + compression "1.7.3" + serve-handler "6.1.0" + update-check "1.5.2" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string.prototype.trimleft@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" + integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +string.prototype.trimright@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" + integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== + dependencies: + define-properties "^1.1.3" + function-bind "^1.1.1" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-json-comments@2.0.1, strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +strip-json-comments@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== + +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +term-size@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= + dependencies: + execa "^0.7.0" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tslib@^1.9.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" + integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +update-check@1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/update-check/-/update-check-1.5.2.tgz#2fe09f725c543440b3d7dabe8971f2d5caaedc28" + integrity sha512-1TrmYLuLj/5ZovwUS7fFd1jMH3NnFDN1y1A8dboedIDt7zs/zJMo6TwwlhYKkSeEwzleeiSBV5/3c9ufAQWDaQ== + dependencies: + registry-auth-token "3.3.2" + registry-url "3.1.0" + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@1.3.1, which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + +widest-line@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" + integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== + dependencies: + string-width "^2.1.1" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yargs-parser@13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.0.0.tgz#3fc44f3e76a8bdb1cc3602e860108602e5ccde8b" + integrity sha512-w2LXjoL8oRdRQN+hOyppuXs+V/fVAYtpcrRxZuF7Kt/Oc+Jr2uAcVntaUTNT6w5ihoWfFDpNY8CPx1QskxZ/pw== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^11.1.1: + version "11.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" + integrity sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^13.0.0: + version "13.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" + integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-unparser@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.5.0.tgz#f2bb2a7e83cbc87bb95c8e572828a06c9add6e0d" + integrity sha512-HK25qidFTCVuj/D1VfNiEndpLIeJN78aqgR23nL3y4N0U/91cOAzqfHlF8n2BvoNDcZmJKin3ddNSvOxSr8flw== + dependencies: + flat "^4.1.0" + lodash "^4.17.11" + yargs "^12.0.5" + +yargs@13.2.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.2.tgz#0c101f580ae95cea7f39d927e7770e3fdc97f993" + integrity sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA== + dependencies: + cliui "^4.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.0.0" + +yargs@^12.0.5: + version "12.0.5" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" + integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw== + dependencies: + cliui "^4.0.0" + decamelize "^1.2.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^3.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^11.1.1" diff --git a/third_party/spirv-tools/tools/util/cli_consumer.cpp b/third_party/spirv-tools/tools/util/cli_consumer.cpp new file mode 100644 index 0000000..77db734 --- /dev/null +++ b/third_party/spirv-tools/tools/util/cli_consumer.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tools/util/cli_consumer.h" + +#include + +namespace spvtools { +namespace utils { + +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message) { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +} + +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/tools/util/cli_consumer.h b/third_party/spirv-tools/tools/util/cli_consumer.h new file mode 100644 index 0000000..64a5e46 --- /dev/null +++ b/third_party/spirv-tools/tools/util/cli_consumer.h @@ -0,0 +1,31 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_CLI_CONSUMMER_H_ +#define SOURCE_UTIL_CLI_CONSUMMER_H_ + +#include "include/spirv-tools/libspirv.h" + +namespace spvtools { +namespace utils { + +// A message consumer that can be used by command line tools like spirv-opt and +// spirv-val to display messages. +void CLIMessageConsumer(spv_message_level_t level, const char*, + const spv_position_t& position, const char* message); + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_CLI_CONSUMMER_H_ diff --git a/third_party/spirv-tools/tools/val/val.cpp b/third_party/spirv-tools/tools/val/val.cpp new file mode 100644 index 0000000..19b8c77 --- /dev/null +++ b/third_party/spirv-tools/tools/val/val.cpp @@ -0,0 +1,195 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "source/spirv_target_env.h" +#include "source/spirv_validator_options.h" +#include "spirv-tools/libspirv.hpp" +#include "tools/io.h" +#include "tools/util/cli_consumer.h" + +void print_usage(char* argv0) { + std::string target_env_list = spvTargetEnvList(36, 105); + printf( + R"(%s - Validate a SPIR-V binary file. + +USAGE: %s [options] [] + +The SPIR-V binary is read from . If no file is specified, +or if the filename is "-", then the binary is read from standard input. + +NOTE: The validator is a work in progress. + +Options: + -h, --help Print this help. + --max-struct-members + --max-struct-depth + --max-local-variables + --max-global-variables + --max-switch-branches + --max-function-args + --max-control-flow-nesting-depth + --max-access-chain-indexes + --max-id-bound + --relax-logical-pointer Allow allocating an object of a pointer type and returning + a pointer value from a function in logical addressing mode + --relax-block-layout Enable VK_KHR_relaxed_block_layout when checking standard + uniform, storage buffer, and push constant layouts. + This is the default when targeting Vulkan 1.1 or later. + --uniform-buffer-standard-layout Enable VK_KHR_uniform_buffer_standard_layout when checking standard + uniform buffer layouts. + --scalar-block-layout Enable VK_EXT_scalar_block_layout when checking standard + uniform, storage buffer, and push constant layouts. Scalar layout + rules are more permissive than relaxed block layout so in effect + this will override the --relax-block-layout option. + --skip-block-layout Skip checking standard uniform/storage buffer layout. + Overrides any --relax-block-layout or --scalar-block-layout option. + --relax-struct-store Allow store from one struct type to a + different type with compatible layout and + members. + --before-hlsl-legalization Allows code patterns that are intended to be + fixed by spirv-opt's legalization passes. + --version Display validator version information. + --target-env {%s} + Use validation rules from the specified environment. +)", + argv0, argv0, target_env_list.c_str()); +} + +int main(int argc, char** argv) { + const char* inFile = nullptr; + spv_target_env target_env = SPV_ENV_UNIVERSAL_1_5; + spvtools::ValidatorOptions options; + bool continue_processing = true; + int return_code = 0; + + for (int argi = 1; continue_processing && argi < argc; ++argi) { + const char* cur_arg = argv[argi]; + if ('-' == cur_arg[0]) { + if (0 == strncmp(cur_arg, "--max-", 6)) { + if (argi + 1 < argc) { + spv_validator_limit limit_type; + if (spvParseUniversalLimitsOptions(cur_arg, &limit_type)) { + uint32_t limit = 0; + if (sscanf(argv[++argi], "%u", &limit)) { + options.SetUniversalLimit(limit_type, limit); + } else { + fprintf(stderr, "error: missing argument to %s\n", cur_arg); + continue_processing = false; + return_code = 1; + } + } else { + fprintf(stderr, "error: unrecognized option: %s\n", cur_arg); + continue_processing = false; + return_code = 1; + } + } else { + fprintf(stderr, "error: Missing argument to %s\n", cur_arg); + continue_processing = false; + return_code = 1; + } + } else if (0 == strcmp(cur_arg, "--version")) { + printf("%s\n", spvSoftwareVersionDetailsString()); + printf( + "Targets:\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n " + "%s\n %s\n", + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_0), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_1), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_2), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_3), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_4), + spvTargetEnvDescription(SPV_ENV_UNIVERSAL_1_5), + spvTargetEnvDescription(SPV_ENV_OPENCL_2_2), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_0), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_1), + spvTargetEnvDescription(SPV_ENV_VULKAN_1_1_SPIRV_1_4), + spvTargetEnvDescription(SPV_ENV_WEBGPU_0)); + continue_processing = false; + return_code = 0; + } else if (0 == strcmp(cur_arg, "--help") || 0 == strcmp(cur_arg, "-h")) { + print_usage(argv[0]); + continue_processing = false; + return_code = 0; + } else if (0 == strcmp(cur_arg, "--target-env")) { + if (argi + 1 < argc) { + const auto env_str = argv[++argi]; + if (!spvParseTargetEnv(env_str, &target_env)) { + fprintf(stderr, "error: Unrecognized target env: %s\n", env_str); + continue_processing = false; + return_code = 1; + } + } else { + fprintf(stderr, "error: Missing argument to --target-env\n"); + continue_processing = false; + return_code = 1; + } + } else if (0 == strcmp(cur_arg, "--before-hlsl-legalization")) { + options.SetBeforeHlslLegalization(true); + } else if (0 == strcmp(cur_arg, "--relax-logical-pointer")) { + options.SetRelaxLogicalPointer(true); + } else if (0 == strcmp(cur_arg, "--relax-block-layout")) { + options.SetRelaxBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--uniform-buffer-standard-layout")) { + options.SetUniformBufferStandardLayout(true); + } else if (0 == strcmp(cur_arg, "--scalar-block-layout")) { + options.SetScalarBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--skip-block-layout")) { + options.SetSkipBlockLayout(true); + } else if (0 == strcmp(cur_arg, "--relax-struct-store")) { + options.SetRelaxStructStore(true); + } else if (0 == cur_arg[1]) { + // Setting a filename of "-" to indicate stdin. + if (!inFile) { + inFile = cur_arg; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + continue_processing = false; + return_code = 1; + } + } else { + print_usage(argv[0]); + continue_processing = false; + return_code = 1; + } + } else { + if (!inFile) { + inFile = cur_arg; + } else { + fprintf(stderr, "error: More than one input file specified\n"); + continue_processing = false; + return_code = 1; + } + } + } + + // Exit if command line parsing was not successful. + if (!continue_processing) { + return return_code; + } + + std::vector contents; + if (!ReadFile(inFile, "rb", &contents)) return 1; + + spvtools::SpirvTools tools(target_env); + tools.SetMessageConsumer(spvtools::utils::CLIMessageConsumer); + + bool succeed = tools.Validate(contents.data(), contents.size(), options); + + return !succeed; +} diff --git a/third_party/spirv-tools/utils/check_code_format.sh b/third_party/spirv-tools/utils/check_code_format.sh new file mode 100755 index 0000000..7994740 --- /dev/null +++ b/third_party/spirv-tools/utils/check_code_format.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Copyright (c) 2017 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Script to determine if source code in Pull Request is properly formatted. +# Exits with non 0 exit code if formatting is needed. +# +# This script assumes to be invoked at the project root directory. + +BASE_BRANCH=${1:-master} + +FILES_TO_CHECK=$(git diff --name-only ${BASE_BRANCH} | grep -E ".*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$") + +if [ -z "${FILES_TO_CHECK}" ]; then + echo "No source code to check for formatting." + exit 0 +fi + +FORMAT_DIFF=$(git diff -U0 ${BASE_BRANCH} -- ${FILES_TO_CHECK} | python ./utils/clang-format-diff.py -p1 -style=file) + +if [ -z "${FORMAT_DIFF}" ]; then + echo "All source code in PR properly formatted." + exit 0 +else + echo "Found formatting errors!" + echo "${FORMAT_DIFF}" + exit 1 +fi diff --git a/third_party/spirv-tools/utils/check_copyright.py b/third_party/spirv-tools/utils/check_copyright.py new file mode 100755 index 0000000..39d27cb --- /dev/null +++ b/third_party/spirv-tools/utils/check_copyright.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python +# coding=utf-8 +# Copyright (c) 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Checks for copyright notices in all the files that need them under the +current directory. Optionally insert them. When inserting, replaces +an MIT or Khronos free use license with Apache 2. +""" + +import argparse +import fileinput +import fnmatch +import inspect +import os +import re +import sys + +# List of designated copyright owners. +AUTHORS = ['The Khronos Group Inc.', + 'LunarG Inc.', + 'Google Inc.', + 'Google LLC', + 'Pierre Moreau', + 'Samsung Inc', + 'André Perez Maselco', + 'Vasyl Teliman', + 'Advanced Micro Devices, Inc.', + 'Stefano Milizia'] +CURRENT_YEAR='2020' + +YEARS = '(2014-2016|2015-2016|2015-2020|2016|2016-2017|2017|2017-2019|2018|2019|2020)' +COPYRIGHT_RE = re.compile( + 'Copyright \(c\) {} ({})'.format(YEARS, '|'.join(AUTHORS))) + +MIT_BEGIN_RE = re.compile('Permission is hereby granted, ' + 'free of charge, to any person obtaining a') +MIT_END_RE = re.compile('MATERIALS OR THE USE OR OTHER DEALINGS IN ' + 'THE MATERIALS.') +APACHE2_BEGIN_RE = re.compile('Licensed under the Apache License, ' + 'Version 2.0 \(the "License"\);') +APACHE2_END_RE = re.compile('limitations under the License.') + +LICENSED = """Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.""" +LICENSED_LEN = 10 # Number of lines in LICENSED + + +def find(top, filename_glob, skip_glob_dir_list, skip_glob_files_list): + """Returns files in the tree rooted at top matching filename_glob but not + in directories matching skip_glob_dir_list nor files matching + skip_glob_dir_list.""" + + file_list = [] + for path, dirs, files in os.walk(top): + for glob in skip_glob_dir_list: + for match in fnmatch.filter(dirs, glob): + dirs.remove(match) + for filename in fnmatch.filter(files, filename_glob): + full_file = os.path.join(path, filename) + if full_file not in skip_glob_files_list: + file_list.append(full_file) + return file_list + + +def filtered_descendants(glob): + """Returns glob-matching filenames under the current directory, but skips + some irrelevant paths.""" + return find('.', glob, ['third_party', 'external', 'CompilerIdCXX', + 'build*', 'out*'], ['./utils/clang-format-diff.py']) + + +def skip(line): + """Returns true if line is all whitespace or shebang.""" + stripped = line.lstrip() + return stripped == '' or stripped.startswith('#!') + + +def comment(text, prefix): + """Returns commented-out text. + + Each line of text will be prefixed by prefix and a space character. Any + trailing whitespace will be trimmed. + """ + accum = ['{} {}'.format(prefix, line).rstrip() for line in text.split('\n')] + return '\n'.join(accum) + + +def insert_copyright(author, glob, comment_prefix): + """Finds all glob-matching files under the current directory and inserts the + copyright message, and license notice. An MIT license or Khronos free + use license (modified MIT) is replaced with an Apache 2 license. + + The copyright message goes into the first non-whitespace, non-shebang line + in a file. The license notice follows it. Both are prefixed on each line + by comment_prefix and a space. + """ + + copyright = comment('Copyright (c) {} {}'.format(CURRENT_YEAR, author), + comment_prefix) + '\n\n' + licensed = comment(LICENSED, comment_prefix) + '\n\n' + for file in filtered_descendants(glob): + # Parsing states are: + # 0 Initial: Have not seen a copyright declaration. + # 1 Seen a copyright line and no other interesting lines + # 2 In the middle of an MIT or Khronos free use license + # 9 Exited any of the above + state = 0 + update_file = False + for line in fileinput.input(file, inplace=1): + emit = True + if state is 0: + if COPYRIGHT_RE.search(line): + state = 1 + elif skip(line): + pass + else: + # Didn't see a copyright. Inject copyright and license. + sys.stdout.write(copyright) + sys.stdout.write(licensed) + # Assume there isn't a previous license notice. + state = 1 + elif state is 1: + if MIT_BEGIN_RE.search(line): + state = 2 + emit = False + elif APACHE2_BEGIN_RE.search(line): + # Assume an Apache license is preceded by a copyright + # notice. So just emit it like the rest of the file. + state = 9 + elif state is 2: + # Replace the MIT license with Apache 2 + emit = False + if MIT_END_RE.search(line): + state = 9 + sys.stdout.write(licensed) + if emit: + sys.stdout.write(line) + + +def alert_if_no_copyright(glob, comment_prefix): + """Prints names of all files missing either a copyright or Apache 2 license. + + Finds all glob-matching files under the current directory and checks if they + contain the copyright message and license notice. Prints the names of all the + files that don't meet both criteria. + + Returns the total number of file names printed. + """ + printed_count = 0 + for file in filtered_descendants(glob): + has_copyright = False + has_apache2 = False + line_num = 0 + apache_expected_end = 0 + with open(file, encoding='utf-8') as contents: + for line in contents: + line_num += 1 + if COPYRIGHT_RE.search(line): + has_copyright = True + if APACHE2_BEGIN_RE.search(line): + apache_expected_end = line_num + LICENSED_LEN + if (line_num is apache_expected_end) and APACHE2_END_RE.search(line): + has_apache2 = True + if not (has_copyright and has_apache2): + message = file + if not has_copyright: + message += ' has no copyright' + if not has_apache2: + message += ' has no Apache 2 license notice' + print(message) + printed_count += 1 + return printed_count + + +class ArgParser(argparse.ArgumentParser): + def __init__(self): + super(ArgParser, self).__init__( + description=inspect.getdoc(sys.modules[__name__])) + self.add_argument('--update', dest='author', action='store', + help='For files missing a copyright notice, insert ' + 'one for the given author, and add a license ' + 'notice. The author must be in the AUTHORS ' + 'list in the script.') + + +def main(): + glob_comment_pairs = [('*.h', '//'), ('*.hpp', '//'), ('*.sh', '#'), + ('*.py', '#'), ('*.cpp', '//'), + ('CMakeLists.txt', '#')] + argparser = ArgParser() + args = argparser.parse_args() + + if args.author: + if args.author not in AUTHORS: + print('error: --update argument must be in the AUTHORS list in ' + 'check_copyright.py: {}'.format(AUTHORS)) + sys.exit(1) + for pair in glob_comment_pairs: + insert_copyright(args.author, *pair) + sys.exit(0) + else: + count = sum([alert_if_no_copyright(*p) for p in glob_comment_pairs]) + sys.exit(count > 0) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/utils/check_symbol_exports.py b/third_party/spirv-tools/utils/check_symbol_exports.py new file mode 100755 index 0000000..bcd77da --- /dev/null +++ b/third_party/spirv-tools/utils/check_symbol_exports.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# Copyright (c) 2017 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Checks names of global exports from a library.""" + +import os.path +import re +import subprocess +import sys + + +PROG = 'check_symbol_exports' + + +def command_output(cmd, directory): + """Runs a command in a directory and returns its standard output stream. + + Captures the standard error stream. + + Raises a RuntimeError if the command fails to launch or otherwise fails. + """ + p = subprocess.Popen(cmd, + cwd=directory, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + (stdout, _) = p.communicate() + if p.returncode != 0: + raise RuntimeError('Failed to run %s in %s' % (cmd, directory)) + return stdout + + +def check_library(library): + """Scans the given library file for global exports. If all such + exports are namespaced or begin with spv (in either C or C++ styles) + then return 0. Otherwise emit a message and return 1.""" + + # The pattern for a global symbol record + symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ g *F \.text.*[0-9A-Fa-f]+ +(.*)') + + # Ok patterns are as follows, assuming Itanium name mangling: + # spv[A-Z] : extern "C" symbol starting with spv + # _ZN : something in a namespace + # _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_] + symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])') + + # In addition, the following pattern allowlists global functions that are added + # by the protobuf compiler: + # - AddDescriptors_spvtoolsfuzz_2eproto() + # - InitDefaults_spvtoolsfuzz_2eproto() + symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov') + + seen = set() + result = 0 + for line in command_output(['objdump', '-t', library], '.').split('\n'): + match = symbol_pattern.search(line) + if match: + symbol = match.group(1) + if symbol not in seen: + seen.add(symbol) + #print("look at '{}'".format(symbol)) + if not (symbol_allowlist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)): + print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol)) + result = 1 + return result + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Check global names exported from a library') + parser.add_argument('library', help='The static library to examine') + args = parser.parse_args() + + if not os.path.isfile(args.library): + print('{}: error: {} does not exist'.format(PROG, args.library)) + sys.exit(1) + + if os.name == 'posix': + status = check_library(args.library) + sys.exit(status) + else: + print('Passing test since not on Posix') + sys.exit(0) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/utils/fixup_fuzz_result.py b/third_party/spirv-tools/utils/fixup_fuzz_result.py new file mode 100755 index 0000000..9fe54a3 --- /dev/null +++ b/third_party/spirv-tools/utils/fixup_fuzz_result.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# Copyright (c) 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +if len(sys.argv) < 1: + print("Need file to chop"); + +with open(sys.argv[1], mode='rb') as file: + file_content = file.read() + content = file_content[:len(file_content) - (len(file_content) % 4)] + sys.stdout.write(content) + diff --git a/third_party/spirv-tools/utils/generate_grammar_tables.py b/third_party/spirv-tools/utils/generate_grammar_tables.py new file mode 100755 index 0000000..2a67733 --- /dev/null +++ b/third_party/spirv-tools/utils/generate_grammar_tables.py @@ -0,0 +1,874 @@ +#!/usr/bin/env python +# Copyright (c) 2016 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Generates various info tables from SPIR-V JSON grammar.""" + +import errno +import json +import os.path +import re + +# Prefix for all C variables generated by this script. +PYGEN_VARIABLE_PREFIX = 'pygen_variable' + +# Extensions to recognize, but which don't necessarily come from the SPIR-V +# core or KHR grammar files. Get this list from the SPIR-V registery web page. +# NOTE: Only put things on this list if it is not in those grammar files. +EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ +SPV_AMD_gcn_shader +SPV_AMD_gpu_shader_half_float +SPV_AMD_gpu_shader_int16 +SPV_AMD_shader_trinary_minmax +SPV_KHR_non_semantic_info +""" + + +def make_path_to_file(f): + """Makes all ancestor directories to the given file, if they don't yet + exist. + + Arguments: + f: The file whose ancestor directories are to be created. + """ + dir = os.path.dirname(os.path.abspath(f)) + try: + os.makedirs(dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(dir): + pass + else: + raise + + +def convert_min_required_version(version): + """Converts the minimal required SPIR-V version encoded in the grammar to + the symbol in SPIRV-Tools.""" + if version is None: + return 'SPV_SPIRV_VERSION_WORD(1, 0)' + if version == 'None': + return '0xffffffffu' + return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) + + +def convert_max_required_version(version): + """Converts the maximum required SPIR-V version encoded in the grammar to + the symbol in SPIRV-Tools.""" + if version is None: + return '0xffffffffu' + return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) + + +def compose_capability_list(caps): + """Returns a string containing a braced list of capabilities as enums. + + Arguments: + - caps: a sequence of capability names + + Returns: + a string containing the braced list of SpvCapability* enums named by caps. + """ + return '{' + ', '.join(['SpvCapability{}'.format(c) for c in caps]) + '}' + + +def get_capability_array_name(caps): + """Returns the name of the array containing all the given capabilities. + + Args: + - caps: a sequence of capability names + """ + if not caps: + return 'nullptr' + return '{}_caps_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(caps)) + + +def generate_capability_arrays(caps): + """Returns the arrays of capabilities. + + Arguments: + - caps: a sequence of sequence of capability names + """ + caps = sorted(set([tuple(c) for c in caps if c])) + arrays = [ + 'static const SpvCapability {}[] = {};'.format( + get_capability_array_name(c), compose_capability_list(c)) + for c in caps] + return '\n'.join(arrays) + + +def compose_extension_list(exts): + """Returns a string containing a braced list of extensions as enums. + + Arguments: + - exts: a sequence of extension names + + Returns: + a string containing the braced list of extensions named by exts. + """ + return '{' + ', '.join( + ['spvtools::Extension::k{}'.format(e) for e in exts]) + '}' + + +def get_extension_array_name(extensions): + """Returns the name of the array containing all the given extensions. + + Args: + - extensions: a sequence of extension names + """ + if not extensions: + return 'nullptr' + else: + return '{}_exts_{}'.format( + PYGEN_VARIABLE_PREFIX, ''.join(extensions)) + + +def generate_extension_arrays(extensions): + """Returns the arrays of extensions. + + Arguments: + - caps: a sequence of sequence of extension names + """ + extensions = sorted(set([tuple(e) for e in extensions if e])) + arrays = [ + 'static const spvtools::Extension {}[] = {};'.format( + get_extension_array_name(e), compose_extension_list(e)) + for e in extensions] + return '\n'.join(arrays) + + +def convert_operand_kind(operand_tuple): + """Returns the corresponding operand type used in spirv-tools for the given + operand kind and quantifier used in the JSON grammar. + + Arguments: + - operand_tuple: a tuple of two elements: + - operand kind: used in the JSON grammar + - quantifier: '', '?', or '*' + + Returns: + a string of the enumerant name in spv_operand_type_t + """ + kind, quantifier = operand_tuple + # The following cases are where we differ between the JSON grammar and + # spirv-tools. + if kind == 'IdResultType': + kind = 'TypeId' + elif kind == 'IdResult': + kind = 'ResultId' + elif kind == 'IdMemorySemantics' or kind == 'MemorySemantics': + kind = 'MemorySemanticsId' + elif kind == 'IdScope' or kind == 'Scope': + kind = 'ScopeId' + elif kind == 'IdRef': + kind = 'Id' + + elif kind == 'ImageOperands': + kind = 'Image' + elif kind == 'Dim': + kind = 'Dimensionality' + elif kind == 'ImageFormat': + kind = 'SamplerImageFormat' + elif kind == 'KernelEnqueueFlags': + kind = 'KernelEnqFlags' + + elif kind == 'LiteralExtInstInteger': + kind = 'ExtensionInstructionNumber' + elif kind == 'LiteralSpecConstantOpInteger': + kind = 'SpecConstantOpNumber' + elif kind == 'LiteralContextDependentNumber': + kind = 'TypedLiteralNumber' + + elif kind == 'PairLiteralIntegerIdRef': + kind = 'LiteralIntegerId' + elif kind == 'PairIdRefLiteralInteger': + kind = 'IdLiteralInteger' + elif kind == 'PairIdRefIdRef': # Used by OpPhi in the grammar + kind = 'Id' + + if kind == 'FPRoundingMode': + kind = 'FpRoundingMode' + elif kind == 'FPFastMathMode': + kind = 'FpFastMathMode' + + if quantifier == '?': + kind = 'Optional{}'.format(kind) + elif quantifier == '*': + kind = 'Variable{}'.format(kind) + + return 'SPV_OPERAND_TYPE_{}'.format( + re.sub(r'([a-z])([A-Z])', r'\1_\2', kind).upper()) + + +class InstInitializer(object): + """Instances holds a SPIR-V instruction suitable for printing as the + initializer for spv_opcode_desc_t.""" + + def __init__(self, opname, caps, exts, operands, version, lastVersion): + """Initialization. + + Arguments: + - opname: opcode name (with the 'Op' prefix) + - caps: a sequence of capability names required by this opcode + - exts: a sequence of names of extensions enabling this enumerant + - operands: a sequence of (operand-kind, operand-quantifier) tuples + - version: minimal SPIR-V version required for this opcode + - lastVersion: last version of SPIR-V that includes this opcode + """ + + assert opname.startswith('Op') + self.opname = opname[2:] # Remove the "Op" prefix. + self.num_caps = len(caps) + self.caps_mask = get_capability_array_name(caps) + self.num_exts = len(exts) + self.exts = get_extension_array_name(exts) + self.operands = [convert_operand_kind(o) for o in operands] + + self.fix_syntax() + + operands = [o[0] for o in operands] + self.ref_type_id = 'IdResultType' in operands + self.def_result_id = 'IdResult' in operands + + self.version = convert_min_required_version(version) + self.lastVersion = convert_max_required_version(lastVersion) + + def fix_syntax(self): + """Fix an instruction's syntax, adjusting for differences between the + officially released grammar and how SPIRV-Tools uses the grammar. + + Fixes: + - ExtInst should not end with SPV_OPERAND_VARIABLE_ID. + https://github.com/KhronosGroup/SPIRV-Tools/issues/233 + """ + if (self.opname == 'ExtInst' + and self.operands[-1] == 'SPV_OPERAND_TYPE_VARIABLE_ID'): + self.operands.pop() + + def __str__(self): + template = ['{{"{opname}"', 'SpvOp{opname}', + '{num_caps}', '{caps_mask}', + '{num_operands}', '{{{operands}}}', + '{def_result_id}', '{ref_type_id}', + '{num_exts}', '{exts}', + '{min_version}', '{max_version}}}'] + return ', '.join(template).format( + opname=self.opname, + num_caps=self.num_caps, + caps_mask=self.caps_mask, + num_operands=len(self.operands), + operands=', '.join(self.operands), + def_result_id=(1 if self.def_result_id else 0), + ref_type_id=(1 if self.ref_type_id else 0), + num_exts=self.num_exts, + exts=self.exts, + min_version=self.version, + max_version=self.lastVersion) + + +class ExtInstInitializer(object): + """Instances holds a SPIR-V extended instruction suitable for printing as + the initializer for spv_ext_inst_desc_t.""" + + def __init__(self, opname, opcode, caps, operands): + """Initialization. + + Arguments: + - opname: opcode name + - opcode: enumerant value for this opcode + - caps: a sequence of capability names required by this opcode + - operands: a sequence of (operand-kind, operand-quantifier) tuples + """ + self.opname = opname + self.opcode = opcode + self.num_caps = len(caps) + self.caps_mask = get_capability_array_name(caps) + self.operands = [convert_operand_kind(o) for o in operands] + self.operands.append('SPV_OPERAND_TYPE_NONE') + + def __str__(self): + template = ['{{"{opname}"', '{opcode}', '{num_caps}', '{caps_mask}', + '{{{operands}}}}}'] + return ', '.join(template).format( + opname=self.opname, + opcode=self.opcode, + num_caps=self.num_caps, + caps_mask=self.caps_mask, + operands=', '.join(self.operands)) + + +def generate_instruction(inst, is_ext_inst): + """Returns the C initializer for the given SPIR-V instruction. + + Arguments: + - inst: a dict containing information about a SPIR-V instruction + - is_ext_inst: a bool indicating whether |inst| is an extended + instruction. + + Returns: + a string containing the C initializer for spv_opcode_desc_t or + spv_ext_inst_desc_t + """ + opname = inst.get('opname') + opcode = inst.get('opcode') + caps = inst.get('capabilities', []) + exts = inst.get('extensions', []) + operands = inst.get('operands', {}) + operands = [(o['kind'], o.get('quantifier', '')) for o in operands] + min_version = inst.get('version', None) + max_version = inst.get('lastVersion', None) + + assert opname is not None + + if is_ext_inst: + return str(ExtInstInitializer(opname, opcode, caps, operands)) + else: + return str(InstInitializer(opname, caps, exts, operands, min_version, max_version)) + + +def generate_instruction_table(inst_table): + """Returns the info table containing all SPIR-V instructions, sorted by + opcode, and prefixed by capability arrays. + + Note: + - the built-in sorted() function is guaranteed to be stable. + https://docs.python.org/3/library/functions.html#sorted + + Arguments: + - inst_table: a list containing all SPIR-V instructions. + """ + inst_table = sorted(inst_table, key=lambda k: (k['opcode'], k['opname'])) + + caps_arrays = generate_capability_arrays( + [inst.get('capabilities', []) for inst in inst_table]) + exts_arrays = generate_extension_arrays( + [inst.get('extensions', []) for inst in inst_table]) + + insts = [generate_instruction(inst, False) for inst in inst_table] + insts = ['static const spv_opcode_desc_t kOpcodeTableEntries[] = {{\n' + ' {}\n}};'.format(',\n '.join(insts))] + + return '{}\n\n{}\n\n{}'.format(caps_arrays, exts_arrays, '\n'.join(insts)) + + +def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""): + """Returns the info table containing all SPIR-V extended instructions, + sorted by opcode, and prefixed by capability arrays. + + Arguments: + - inst_table: a list containing all SPIR-V instructions. + - set_name: the name of the extended instruction set. + - operand_kind_prefix: the prefix, if any, to add to the front + of operand kind names. + """ + if operand_kind_prefix: + prefix_operand_kind_names(operand_kind_prefix, json_grammar) + + inst_table = json_grammar["instructions"] + set_name = set_name.replace(".", "_") + + inst_table = sorted(inst_table, key=lambda k: k['opcode']) + caps = [inst.get('capabilities', []) for inst in inst_table] + caps_arrays = generate_capability_arrays(caps) + insts = [generate_instruction(inst, True) for inst in inst_table] + insts = ['static const spv_ext_inst_desc_t {}_entries[] = {{\n' + ' {}\n}};'.format(set_name, ',\n '.join(insts))] + + return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts)) + + +class EnumerantInitializer(object): + """Prints an enumerant as the initializer for spv_operand_desc_t.""" + + def __init__(self, enumerant, value, caps, exts, parameters, version, lastVersion): + """Initialization. + + Arguments: + - enumerant: enumerant name + - value: enumerant value + - caps: a sequence of capability names required by this enumerant + - exts: a sequence of names of extensions enabling this enumerant + - parameters: a sequence of (operand-kind, operand-quantifier) tuples + - version: minimal SPIR-V version required for this opcode + - lastVersion: last SPIR-V version this opode appears + """ + self.enumerant = enumerant + self.value = value + self.num_caps = len(caps) + self.caps = get_capability_array_name(caps) + self.num_exts = len(exts) + self.exts = get_extension_array_name(exts) + self.parameters = [convert_operand_kind(p) for p in parameters] + self.version = convert_min_required_version(version) + self.lastVersion = convert_max_required_version(lastVersion) + + def __str__(self): + template = ['{{"{enumerant}"', '{value}', '{num_caps}', + '{caps}', '{num_exts}', '{exts}', + '{{{parameters}}}', '{min_version}', + '{max_version}}}'] + return ', '.join(template).format( + enumerant=self.enumerant, + value=self.value, + num_caps=self.num_caps, + caps=self.caps, + num_exts=self.num_exts, + exts=self.exts, + parameters=', '.join(self.parameters), + min_version=self.version, + max_version=self.lastVersion) + + +def generate_enum_operand_kind_entry(entry, extension_map): + """Returns the C initializer for the given operand enum entry. + + Arguments: + - entry: a dict containing information about an enum entry + - extension_map: a dict mapping enum value to list of extensions + + Returns: + a string containing the C initializer for spv_operand_desc_t + """ + enumerant = entry.get('enumerant') + value = entry.get('value') + caps = entry.get('capabilities', []) + if value in extension_map: + exts = extension_map[value] + else: + exts = [] + params = entry.get('parameters', []) + params = [p.get('kind') for p in params] + params = zip(params, [''] * len(params)) + version = entry.get('version', None) + max_version = entry.get('lastVersion', None) + + assert enumerant is not None + assert value is not None + + return str(EnumerantInitializer( + enumerant, value, caps, exts, params, version, max_version)) + + +def generate_enum_operand_kind(enum, synthetic_exts_list): + """Returns the C definition for the given operand kind. + It's a static const named array of spv_operand_desc_t. + + Also appends to |synthetic_exts_list| a list of extension lists + used. + """ + kind = enum.get('kind') + assert kind is not None + + # Sort all enumerants according to their values, but otherwise + # preserve their order so the first name listed in the grammar + # as the preferred name for disassembly. + if enum.get('category') == 'ValueEnum': + def functor(k): return (k['value']) + else: + def functor(k): return (int(k['value'], 16)) + entries = sorted(enum.get('enumerants', []), key=functor) + + # SubgroupEqMask and SubgroupEqMaskKHR are the same number with + # same semantics, but one has no extension list while the other + # does. Both should have the extension list. + # So create a mapping from enum value to the union of the extensions + # across all those grammar entries. Preserve order. + extension_map = {} + for e in entries: + value = e.get('value') + extension_map[value] = [] + for e in entries: + value = e.get('value') + exts = e.get('extensions', []) + for ext in exts: + if ext not in extension_map[value]: + extension_map[value].append(ext) + synthetic_exts_list.extend(extension_map.values()) + + name = '{}_{}Entries'.format(PYGEN_VARIABLE_PREFIX, kind) + entries = [' {}'.format(generate_enum_operand_kind_entry(e, extension_map)) + for e in entries] + + template = ['static const spv_operand_desc_t {name}[] = {{', + '{entries}', '}};'] + entries = '\n'.join(template).format( + name=name, + entries=',\n'.join(entries)) + + return kind, name, entries + + +def generate_operand_kind_table(enums): + """Returns the info table containing all SPIR-V operand kinds.""" + # We only need to output info tables for those operand kinds that are enums. + enums = [e for e in enums if e.get('category') in ['ValueEnum', 'BitEnum']] + + caps = [entry.get('capabilities', []) + for enum in enums + for entry in enum.get('enumerants', [])] + caps_arrays = generate_capability_arrays(caps) + + exts = [entry.get('extensions', []) + for enum in enums + for entry in enum.get('enumerants', [])] + enums = [generate_enum_operand_kind(e, exts) for e in enums] + exts_arrays = generate_extension_arrays(exts) + + # We have three operand kinds that requires their optional counterpart to + # exist in the operand info table. + three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess'] + three_optional_enums = [e for e in enums if e[0] in three_optional_enums] + enums.extend(three_optional_enums) + + enum_kinds, enum_names, enum_entries = zip(*enums) + # Mark the last three as optional ones. + enum_quantifiers = [''] * (len(enums) - 3) + ['?'] * 3 + # And we don't want redefinition of them. + enum_entries = enum_entries[:-3] + enum_kinds = [convert_operand_kind(e) + for e in zip(enum_kinds, enum_quantifiers)] + table_entries = zip(enum_kinds, enum_names, enum_names) + table_entries = [' {{{}, ARRAY_SIZE({}), {}}}'.format(*e) + for e in table_entries] + + template = [ + 'static const spv_operand_desc_group_t {p}_OperandInfoTable[] = {{', + '{enums}', '}};'] + table = '\n'.join(template).format( + p=PYGEN_VARIABLE_PREFIX, enums=',\n'.join(table_entries)) + + return '\n\n'.join((caps_arrays,) + (exts_arrays,) + enum_entries + (table,)) + + +def get_extension_list(instructions, operand_kinds): + """Returns extensions as an alphabetically sorted list of strings.""" + + things_with_an_extensions_field = [item for item in instructions] + + enumerants = sum([item.get('enumerants', []) + for item in operand_kinds], []) + + things_with_an_extensions_field.extend(enumerants) + + extensions = sum([item.get('extensions', []) + for item in things_with_an_extensions_field + if item.get('extensions')], []) + + for item in EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split(): + # If it's already listed in a grammar, then don't put it in the + # special exceptions list. + assert item not in extensions, 'Extension %s is already in a grammar file' % item + + extensions.extend( + EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split()) + + # Validator would ignore type declaration unique check. Should only be used + # for legacy autogenerated test files containing multiple instances of the + # same type declaration, if fixing the test by other methods is too + # difficult. Shouldn't be used for any other reasons. + extensions.append('SPV_VALIDATOR_ignore_type_decl_unique') + + return sorted(set(extensions)) + + +def get_capabilities(operand_kinds): + """Returns capabilities as a list of JSON objects, in order of + appearance.""" + enumerants = sum([item.get('enumerants', []) for item in operand_kinds + if item.get('kind') in ['Capability']], []) + return enumerants + + +def generate_extension_enum(extensions): + """Returns enumeration containing extensions declared in the grammar.""" + return ',\n'.join(['k' + extension for extension in extensions]) + + +def generate_extension_to_string_mapping(extensions): + """Returns mapping function from extensions to corresponding strings.""" + function = 'const char* ExtensionToString(Extension extension) {\n' + function += ' switch (extension) {\n' + template = ' case Extension::k{extension}:\n' \ + ' return "{extension}";\n' + function += ''.join([template.format(extension=extension) + for extension in extensions]) + function += ' };\n\n return "";\n}' + return function + + +def generate_string_to_extension_mapping(extensions): + """Returns mapping function from strings to corresponding extensions.""" + + function = ''' + bool GetExtensionFromString(const char* str, Extension* extension) {{ + static const char* known_ext_strs[] = {{ {strs} }}; + static const Extension known_ext_ids[] = {{ {ids} }}; + const auto b = std::begin(known_ext_strs); + const auto e = std::end(known_ext_strs); + const auto found = std::equal_range( + b, e, str, [](const char* str1, const char* str2) {{ + return std::strcmp(str1, str2) < 0; + }}); + if (found.first == e || found.first == found.second) return false; + + *extension = known_ext_ids[found.first - b]; + return true; + }} + '''.format(strs=', '.join(['"{}"'.format(e) for e in extensions]), + ids=', '.join(['Extension::k{}'.format(e) for e in extensions])) + + return function + + +def generate_capability_to_string_mapping(operand_kinds): + """Returns mapping function from capabilities to corresponding strings. + + We take care to avoid emitting duplicate values. + """ + function = 'const char* CapabilityToString(SpvCapability capability) {\n' + function += ' switch (capability) {\n' + template = ' case SpvCapability{capability}:\n' \ + ' return "{capability}";\n' + emitted = set() # The values of capabilities we already have emitted + for capability in get_capabilities(operand_kinds): + value = capability.get('value') + if value not in emitted: + emitted.add(value) + function += template.format(capability=capability.get('enumerant')) + function += ' case SpvCapabilityMax:\n' \ + ' assert(0 && "Attempting to convert SpvCapabilityMax to string");\n' \ + ' return "";\n' + function += ' };\n\n return "";\n}' + return function + + +def generate_all_string_enum_mappings(extensions, operand_kinds): + """Returns all string-to-enum / enum-to-string mapping tables.""" + tables = [] + tables.append(generate_extension_to_string_mapping(extensions)) + tables.append(generate_string_to_extension_mapping(extensions)) + tables.append(generate_capability_to_string_mapping(operand_kinds)) + return '\n\n'.join(tables) + + +def precondition_operand_kinds(operand_kinds): + """For operand kinds that have the same number, make sure they all have the + same extension list.""" + + # Map operand kind and value to list of the union of extensions + # for same-valued enumerants. + exts = {} + for kind_entry in operand_kinds: + kind = kind_entry.get('kind') + for enum_entry in kind_entry.get('enumerants', []): + value = enum_entry.get('value') + key = kind + '.' + str(value) + if key in exts: + exts[key].extend(enum_entry.get('extensions', [])) + else: + exts[key] = enum_entry.get('extensions', []) + exts[key] = sorted(set(exts[key])) + + # Now make each entry the same list. + for kind_entry in operand_kinds: + kind = kind_entry.get('kind') + for enum_entry in kind_entry.get('enumerants', []): + value = enum_entry.get('value') + key = kind + '.' + str(value) + if len(exts[key]) > 0: + enum_entry['extensions'] = exts[key] + + return operand_kinds + + +def prefix_operand_kind_names(prefix, json_dict): + """Modifies json_dict, by prefixing all the operand kind names + with the given prefix. Also modifies their uses in the instructions + to match. + """ + + old_to_new = {} + for operand_kind in json_dict["operand_kinds"]: + old_name = operand_kind["kind"] + new_name = prefix + old_name + operand_kind["kind"] = new_name + old_to_new[old_name] = new_name + + for instruction in json_dict["instructions"]: + for operand in instruction.get("operands", []): + replacement = old_to_new.get(operand["kind"]) + if replacement is not None: + operand["kind"] = replacement + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') + + parser.add_argument('--spirv-core-grammar', metavar='', + type=str, required=False, + help='input JSON grammar file for core SPIR-V ' + 'instructions') + parser.add_argument('--extinst-debuginfo-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for DebugInfo extended ' + 'instruction set') + parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for OpenCL.DebugInfo.100 ' + 'extended instruction set') + parser.add_argument('--extinst-glsl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for GLSL extended ' + 'instruction set') + parser.add_argument('--extinst-opencl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for OpenCL extended ' + 'instruction set') + + parser.add_argument('--core-insts-output', metavar='', + type=str, required=False, default=None, + help='output file for core SPIR-V instructions') + parser.add_argument('--glsl-insts-output', metavar='', + type=str, required=False, default=None, + help='output file for GLSL extended instruction set') + parser.add_argument('--opencl-insts-output', metavar='', + type=str, required=False, default=None, + help='output file for OpenCL extended instruction set') + parser.add_argument('--operand-kinds-output', metavar='', + type=str, required=False, default=None, + help='output file for operand kinds') + parser.add_argument('--extension-enum-output', metavar='', + type=str, required=False, default=None, + help='output file for extension enumeration') + parser.add_argument('--enum-string-mapping-output', metavar='', + type=str, required=False, default=None, + help='output file for enum-string mappings') + parser.add_argument('--extinst-vendor-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for vendor extended ' + 'instruction set'), + parser.add_argument('--vendor-insts-output', metavar='', + type=str, required=False, default=None, + help='output file for vendor extended instruction set') + parser.add_argument('--vendor-operand-kind-prefix', metavar='', + type=str, required=False, default=None, + help='prefix for operand kinds (to disambiguate operand type enums)') + args = parser.parse_args() + + # The GN build system needs this because it doesn't handle quoting + # empty string arguments well. + if args.vendor_operand_kind_prefix == "...nil...": + args.vendor_operand_kind_prefix = "" + + if (args.core_insts_output is None) != \ + (args.operand_kinds_output is None): + print('error: --core-insts-output and --operand-kinds-output ' + 'should be specified together.') + exit(1) + if args.operand_kinds_output and not (args.spirv_core_grammar and + args.extinst_debuginfo_grammar and + args.extinst_cldebuginfo100_grammar): + print('error: --operand-kinds-output requires --spirv-core-grammar ' + 'and --extinst-debuginfo-grammar ' + 'and --extinst-cldebuginfo100-grammar') + exit(1) + if (args.glsl_insts_output is None) != \ + (args.extinst_glsl_grammar is None): + print('error: --glsl-insts-output and --extinst-glsl-grammar ' + 'should be specified together.') + exit(1) + if (args.opencl_insts_output is None) != \ + (args.extinst_opencl_grammar is None): + print('error: --opencl-insts-output and --extinst-opencl-grammar ' + 'should be specified together.') + exit(1) + if (args.vendor_insts_output is None) != \ + (args.extinst_vendor_grammar is None): + print('error: --vendor-insts-output and ' + '--extinst-vendor-grammar should be specified together.') + exit(1) + if all([args.core_insts_output is None, + args.glsl_insts_output is None, + args.opencl_insts_output is None, + args.vendor_insts_output is None, + args.extension_enum_output is None, + args.enum_string_mapping_output is None]): + print('error: at least one output should be specified.') + exit(1) + + if args.spirv_core_grammar is not None: + with open(args.spirv_core_grammar) as json_file: + core_grammar = json.loads(json_file.read()) + with open(args.extinst_debuginfo_grammar) as debuginfo_json_file: + debuginfo_grammar = json.loads(debuginfo_json_file.read()) + with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file: + cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read()) + prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar) + instructions = [] + instructions.extend(core_grammar['instructions']) + instructions.extend(debuginfo_grammar['instructions']) + instructions.extend(cldebuginfo100_grammar['instructions']) + operand_kinds = [] + operand_kinds.extend(core_grammar['operand_kinds']) + operand_kinds.extend(debuginfo_grammar['operand_kinds']) + operand_kinds.extend(cldebuginfo100_grammar['operand_kinds']) + extensions = get_extension_list(instructions, operand_kinds) + operand_kinds = precondition_operand_kinds(operand_kinds) + if args.core_insts_output is not None: + make_path_to_file(args.core_insts_output) + make_path_to_file(args.operand_kinds_output) + with open(args.core_insts_output, 'w') as f: + f.write(generate_instruction_table( + core_grammar['instructions'])) + with open(args.operand_kinds_output, 'w') as f: + f.write(generate_operand_kind_table(operand_kinds)) + if args.extension_enum_output is not None: + make_path_to_file(args.extension_enum_output) + with open(args.extension_enum_output, 'w') as f: + f.write(generate_extension_enum(extensions)) + if args.enum_string_mapping_output is not None: + make_path_to_file(args.enum_string_mapping_output) + with open(args.enum_string_mapping_output, 'w') as f: + f.write(generate_all_string_enum_mappings( + extensions, operand_kinds)) + + if args.extinst_glsl_grammar is not None: + with open(args.extinst_glsl_grammar) as json_file: + grammar = json.loads(json_file.read()) + make_path_to_file(args.glsl_insts_output) + with open(args.glsl_insts_output, 'w') as f: + f.write(generate_extended_instruction_table( + grammar, 'glsl')) + + if args.extinst_opencl_grammar is not None: + with open(args.extinst_opencl_grammar) as json_file: + grammar = json.loads(json_file.read()) + make_path_to_file(args.opencl_insts_output) + with open(args.opencl_insts_output, 'w') as f: + f.write(generate_extended_instruction_table( + grammar, 'opencl')) + + if args.extinst_vendor_grammar is not None: + with open(args.extinst_vendor_grammar) as json_file: + grammar = json.loads(json_file.read()) + make_path_to_file(args.vendor_insts_output) + name = args.extinst_vendor_grammar + start = name.find('extinst.') + len('extinst.') + name = name[start:-len('.grammar.json')].replace('-', '_') + with open(args.vendor_insts_output, 'w') as f: + f.write(generate_extended_instruction_table( + grammar, name, args.vendor_operand_kind_prefix)) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/utils/generate_language_headers.py b/third_party/spirv-tools/utils/generate_language_headers.py new file mode 100755 index 0000000..83fa99e --- /dev/null +++ b/third_party/spirv-tools/utils/generate_language_headers.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# Copyright (c) 2017 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Generates language headers from a JSON grammar file""" + +import errno +import json +import os.path +import re + + +def make_path_to_file(f): + """Makes all ancestor directories to the given file, if they + don't yet exist. + + Arguments: + f: The file whose ancestor directories are to be created. + """ + dir = os.path.dirname(os.path.abspath(f)) + try: + os.makedirs(dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(dir): + pass + else: + raise + +class ExtInstGrammar: + """The grammar for an extended instruction set""" + + def __init__(self, name, copyright, instructions, operand_kinds, version = None, revision = None): + self.name = name + self.copyright = copyright + self.instructions = instructions + self.operand_kinds = operand_kinds + self.version = version + self.revision = revision + + +class LangGenerator: + """A language-specific generator""" + + def __init__(self): + self.upper_case_initial = re.compile('^[A-Z]') + pass + + def comment_prefix(self): + return "" + + def namespace_prefix(self): + return "" + + def uses_guards(self): + return False + + def cpp_guard_preamble(self): + return "" + + def cpp_guard_postamble(self): + return "" + + def enum_value(self, prefix, name, value): + if self.upper_case_initial.match(name): + use_name = name + else: + use_name = '_' + name + + return " {}{} = {},".format(prefix, use_name, value) + + def generate(self, grammar): + """Returns a string that is the language-specific header for the given grammar""" + + parts = [] + if grammar.copyright: + parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright]) + parts.append('') + + guard = 'SPIRV_EXTINST_{}_H_'.format(grammar.name) + if self.uses_guards: + parts.append('#ifndef {}'.format(guard)) + parts.append('#define {}'.format(guard)) + parts.append('') + + parts.append(self.cpp_guard_preamble()) + + if grammar.version: + parts.append(self.const_definition(grammar.name, 'Version', grammar.version)) + + if grammar.revision is not None: + parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision)) + + parts.append('') + + if grammar.instructions: + parts.append(self.enum_prefix(grammar.name, 'Instructions')) + for inst in grammar.instructions: + parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode'])) + parts.append(self.enum_end(grammar.name, 'Instructions')) + parts.append('') + + if grammar.operand_kinds: + for kind in grammar.operand_kinds: + parts.append(self.enum_prefix(grammar.name, kind['kind'])) + for e in kind['enumerants']: + parts.append(self.enum_value(grammar.name, e['enumerant'], e['value'])) + parts.append(self.enum_end(grammar.name, kind['kind'])) + parts.append('') + + parts.append(self.cpp_guard_postamble()) + + if self.uses_guards: + parts.append('#endif // {}'.format(guard)) + + return '\n'.join(parts) + + +class CLikeGenerator(LangGenerator): + def uses_guards(self): + return True + + def comment_prefix(self): + return "// " + + def const_definition(self, prefix, var, value): + # Use an anonymous enum. Don't use a static const int variable because + # that can bloat binary size. + return 'enum {0} {1}{2} = {3}, {1}{2}_BitWidthPadding = 0x7fffffff {4};'.format( + '{', prefix, var, value, '}') + + def enum_prefix(self, prefix, name): + return 'enum {}{} {}'.format(prefix, name, '{') + + def enum_end(self, prefix, enum): + return ' {}{}Max = 0x7ffffff\n{};\n'.format(prefix, enum, '}') + + def cpp_guard_preamble(self): + return '#ifdef __cplusplus\nextern "C" {\n#endif\n' + + def cpp_guard_postamble(self): + return '#ifdef __cplusplus\n}\n#endif\n' + + +class CGenerator(CLikeGenerator): + pass + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar') + + parser.add_argument('--extinst-grammar', metavar='', + type=str, required=True, + help='input JSON grammar file for extended instruction set') + parser.add_argument('--extinst-output-path', metavar='', + type=str, required=True, + help='Path of the language-specific output file.') + args = parser.parse_args() + + with open(args.extinst_grammar) as json_file: + grammar_json = json.loads(json_file.read()) + grammar_name = os.path.splitext(os.path.basename(args.extinst_output_path))[0] + grammar = ExtInstGrammar(name = grammar_name, + copyright = grammar_json['copyright'], + instructions = grammar_json['instructions'], + operand_kinds = grammar_json['operand_kinds'], + version = grammar_json['version'], + revision = grammar_json['revision']) + make_path_to_file(args.extinst_output_path) + with open(args.extinst_output_path, 'w') as f: + f.write(CGenerator().generate(grammar)) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/utils/generate_registry_tables.py b/third_party/spirv-tools/utils/generate_registry_tables.py new file mode 100755 index 0000000..28152ef --- /dev/null +++ b/third_party/spirv-tools/utils/generate_registry_tables.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# Copyright (c) 2016 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Generates the vendor tool table from the SPIR-V XML registry.""" + +import errno +import os.path +import xml.etree.ElementTree + + +def mkdir_p(directory): + """Make the directory, and all its ancestors as required. Any of the + directories are allowed to already exist. + This is compatible with Python down to 3.0. + """ + + if directory == "": + # We're being asked to make the current directory. + return + + try: + os.makedirs(directory) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(directory): + pass + else: + raise + + +def generate_vendor_table(registry): + """Returns a list of C style initializers for the registered vendors + and their tools. + + Args: + registry: The SPIR-V XMLregistry as an xml.ElementTree + """ + + lines = [] + for ids in registry.iter('ids'): + if 'vendor' == ids.attrib['type']: + for an_id in ids.iter('id'): + value = an_id.attrib['value'] + vendor = an_id.attrib['vendor'] + if 'tool' in an_id.attrib: + tool = an_id.attrib['tool'] + vendor_tool = vendor + ' ' + tool + else: + tool = '' + vendor_tool = vendor + line = '{' + '{}, "{}", "{}", "{}"'.format(value, + vendor, + tool, + vendor_tool) + '},' + lines.append(line) + return '\n'.join(lines) + + +def main(): + import argparse + parser = argparse.ArgumentParser(description= + 'Generate tables from SPIR-V XML registry') + parser.add_argument('--xml', metavar='', + type=str, required=True, + help='SPIR-V XML Registry file') + parser.add_argument('--generator-output', metavar='', + type=str, required=True, + help='output file for SPIR-V generators table') + args = parser.parse_args() + + with open(args.xml) as xml_in: + registry = xml.etree.ElementTree.fromstring(xml_in.read()) + + mkdir_p(os.path.dirname(args.generator_output)) + with open(args.generator_output, 'w') as f: + f.write(generate_vendor_table(registry)) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/utils/generate_vim_syntax.py b/third_party/spirv-tools/utils/generate_vim_syntax.py new file mode 100755 index 0000000..da7e99b --- /dev/null +++ b/third_party/spirv-tools/utils/generate_vim_syntax.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python +# Copyright (c) 2016 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Generates Vim syntax rules for SPIR-V assembly (.spvasm) files""" + +import json + +PREAMBLE="""" Vim syntax file +" Language: spvasm +" Generated by SPIRV-Tools + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn case match +""" + +POSTAMBLE=""" + +syntax keyword spvasmTodo TODO FIXME contained + +syn match spvasmIdNumber /%\d\+\>/ + +" The assembler treats the leading minus sign as part of the number token. +" This applies to integers, and to floats below. +syn match spvasmNumber /-\?\<\d\+\>/ + +" Floating point literals. +" In general, C++ requires at least digit in the mantissa, and the +" floating point is optional. This applies to both the regular decimal float +" case and the hex float case. + +" First case: digits before the optional decimal, no trailing digits. +syn match spvasmFloat /-\?\d\+\.\?\(e[+-]\d\+\)\?/ +" Second case: optional digits before decimal, trailing digits +syn match spvasmFloat /-\?\d*\.\d\+\(e[+-]\d\+\)\?/ + +" First case: hex digits before the optional decimal, no trailing hex digits. +syn match spvasmFloat /-\?0[xX]\\x\+\.\?p[-+]\d\+/ +" Second case: optional hex digits before decimal, trailing hex digits +syn match spvasmFloat /-\?0[xX]\\x*\.\\x\+p[-+]\d\+/ + +syn match spvasmComment /;.*$/ contains=spvasmTodo +syn region spvasmString start=/"/ skip=/\\\\"/ end=/"/ +syn match spvasmId /%[a-zA-Z_][a-zA-Z_0-9]*/ + +" Highlight unknown constants and statements as errors +syn match spvasmError /[a-zA-Z][a-zA-Z_0-9]*/ + + +if version >= 508 || !exists("did_c_syn_inits") + if version < 508 + let did_c_syn_inits = 1 + command -nargs=+ HiLink hi link + else + command -nargs=+ HiLink hi def link + endif + + HiLink spvasmStatement Statement + HiLink spvasmNumber Number + HiLink spvasmComment Comment + HiLink spvasmString String + HiLink spvasmFloat Float + HiLink spvasmConstant Constant + HiLink spvasmIdNumber Identifier + HiLink spvasmId Identifier + HiLink spvasmTodo Todo + + delcommand HiLink +endif + +let b:current_syntax = "spvasm" +""" + +# This list is taken from the description of OpSpecConstantOp in SPIR-V 1.1. +# TODO(dneto): Propose that this information be embedded in the grammar file. +SPEC_CONSTANT_OP_OPCODES = """ + OpSConvert, OpFConvert + OpSNegate, OpNot + OpIAdd, OpISub + OpIMul, OpUDiv, OpSDiv, OpUMod, OpSRem, OpSMod + OpShiftRightLogical, OpShiftRightArithmetic, OpShiftLeftLogical + OpBitwiseOr, OpBitwiseXor, OpBitwiseAnd + OpVectorShuffle, OpCompositeExtract, OpCompositeInsert + OpLogicalOr, OpLogicalAnd, OpLogicalNot, + OpLogicalEqual, OpLogicalNotEqual + OpSelect + OpIEqual, OpINotEqual + OpULessThan, OpSLessThan + OpUGreaterThan, OpSGreaterThan + OpULessThanEqual, OpSLessThanEqual + OpUGreaterThanEqual, OpSGreaterThanEqual + + OpQuantizeToF16 + + OpConvertFToS, OpConvertSToF + OpConvertFToU, OpConvertUToF + OpUConvert + OpConvertPtrToU, OpConvertUToPtr + OpGenericCastToPtr, OpPtrCastToGeneric + OpBitcast + OpFNegate + OpFAdd, OpFSub + OpFMul, OpFDiv + OpFRem, OpFMod + OpAccessChain, OpInBoundsAccessChain + OpPtrAccessChain, OpInBoundsPtrAccessChain""" + + +def EmitAsStatement(name): + """Emits the given name as a statement token""" + print('syn keyword spvasmStatement', name) + + +def EmitAsEnumerant(name): + """Emits the given name as an named operand token""" + print('syn keyword spvasmConstant', name) + + +def main(): + """Parses arguments, then generates the Vim syntax rules for SPIR-V assembly + on stdout.""" + import argparse + parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') + parser.add_argument('--spirv-core-grammar', metavar='', + type=str, required=True, + help='input JSON grammar file for core SPIR-V ' + 'instructions') + parser.add_argument('--extinst-glsl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for GLSL extended ' + 'instruction set') + parser.add_argument('--extinst-opencl-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for OpenGL extended ' + 'instruction set') + parser.add_argument('--extinst-debuginfo-grammar', metavar='', + type=str, required=False, default=None, + help='input JSON grammar file for DebugInfo extended ' + 'instruction set') + args = parser.parse_args() + + # Generate the syntax rules. + print(PREAMBLE) + + core = json.loads(open(args.spirv_core_grammar).read()) + print('\n" Core instructions') + for inst in core["instructions"]: + EmitAsStatement(inst['opname']) + print('\n" Core operand enums') + for operand_kind in core["operand_kinds"]: + if 'enumerants' in operand_kind: + for e in operand_kind['enumerants']: + EmitAsEnumerant(e['enumerant']) + + if args.extinst_glsl_grammar is not None: + print('\n" GLSL.std.450 extended instructions') + glsl = json.loads(open(args.extinst_glsl_grammar).read()) + # These opcodes are really enumerant operands for the OpExtInst + # instruction. + for inst in glsl["instructions"]: + EmitAsEnumerant(inst['opname']) + + if args.extinst_opencl_grammar is not None: + print('\n" OpenCL.std extended instructions') + opencl = json.loads(open(args.extinst_opencl_grammar).read()) + for inst in opencl["instructions"]: + EmitAsEnumerant(inst['opname']) + + if args.extinst_debuginfo_grammar is not None: + print('\n" DebugInfo extended instructions') + debuginfo = json.loads(open(args.extinst_debuginfo_grammar).read()) + for inst in debuginfo["instructions"]: + EmitAsEnumerant(inst['opname']) + print('\n" DebugInfo operand enums') + for operand_kind in debuginfo["operand_kinds"]: + if 'enumerants' in operand_kind: + for e in operand_kind['enumerants']: + EmitAsEnumerant(e['enumerant']) + + print('\n" OpSpecConstantOp opcodes') + for word in SPEC_CONSTANT_OP_OPCODES.split(' '): + stripped = word.strip('\n,') + if stripped != "": + # Treat as an enumerant, but without the leading "Op" + EmitAsEnumerant(stripped[2:]) + print(POSTAMBLE) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/utils/git-sync-deps b/third_party/spirv-tools/utils/git-sync-deps new file mode 100755 index 0000000..0575641 --- /dev/null +++ b/third_party/spirv-tools/utils/git-sync-deps @@ -0,0 +1,282 @@ +#!/usr/bin/env python +# Copyright 2014 Google Inc. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Parse a DEPS file and git checkout all of the dependencies. + +Args: + An optional list of deps_os values. + +Environment Variables: + GIT_EXECUTABLE: path to "git" binary; if unset, will look for one of + ['git', 'git.exe', 'git.bat'] in your default path. + + GIT_SYNC_DEPS_PATH: file to get the dependency list from; if unset, + will use the file ../DEPS relative to this script's directory. + + GIT_SYNC_DEPS_QUIET: if set to non-empty string, suppress messages. + +Git Config: + To disable syncing of a single repository: + cd path/to/repository + git config sync-deps.disable true + + To re-enable sync: + cd path/to/repository + git config --unset sync-deps.disable +""" + + +import os +import re +import subprocess +import sys +import threading +from builtins import bytes + + +def git_executable(): + """Find the git executable. + + Returns: + A string suitable for passing to subprocess functions, or None. + """ + envgit = os.environ.get('GIT_EXECUTABLE') + searchlist = ['git', 'git.exe', 'git.bat'] + if envgit: + searchlist.insert(0, envgit) + with open(os.devnull, 'w') as devnull: + for git in searchlist: + try: + subprocess.call([git, '--version'], stdout=devnull) + except (OSError,): + continue + return git + return None + + +DEFAULT_DEPS_PATH = os.path.normpath( + os.path.join(os.path.dirname(__file__), os.pardir, 'DEPS')) + + +def usage(deps_file_path = None): + sys.stderr.write( + 'Usage: run to grab dependencies, with optional platform support:\n') + sys.stderr.write(' %s %s' % (sys.executable, __file__)) + if deps_file_path: + parsed_deps = parse_file_to_dict(deps_file_path) + if 'deps_os' in parsed_deps: + for deps_os in parsed_deps['deps_os']: + sys.stderr.write(' [%s]' % deps_os) + sys.stderr.write('\n\n') + sys.stderr.write(__doc__) + + +def git_repository_sync_is_disabled(git, directory): + try: + disable = subprocess.check_output( + [git, 'config', 'sync-deps.disable'], cwd=directory) + return disable.lower().strip() in ['true', '1', 'yes', 'on'] + except subprocess.CalledProcessError: + return False + + +def is_git_toplevel(git, directory): + """Return true iff the directory is the top level of a Git repository. + + Args: + git (string) the git executable + + directory (string) the path into which the repository + is expected to be checked out. + """ + try: + toplevel = subprocess.check_output( + [git, 'rev-parse', '--show-toplevel'], cwd=directory).strip() + return os.path.realpath(bytes(directory, 'utf8')) == os.path.realpath(toplevel) + except subprocess.CalledProcessError: + return False + + +def status(directory, checkoutable): + def truncate(s, length): + return s if len(s) <= length else s[:(length - 3)] + '...' + dlen = 36 + directory = truncate(directory, dlen) + checkoutable = truncate(checkoutable, 40) + sys.stdout.write('%-*s @ %s\n' % (dlen, directory, checkoutable)) + + +def git_checkout_to_directory(git, repo, checkoutable, directory, verbose): + """Checkout (and clone if needed) a Git repository. + + Args: + git (string) the git executable + + repo (string) the location of the repository, suitable + for passing to `git clone`. + + checkoutable (string) a tag, branch, or commit, suitable for + passing to `git checkout` + + directory (string) the path into which the repository + should be checked out. + + verbose (boolean) + + Raises an exception if any calls to git fail. + """ + if not os.path.isdir(directory): + subprocess.check_call( + [git, 'clone', '--quiet', repo, directory]) + + if not is_git_toplevel(git, directory): + # if the directory exists, but isn't a git repo, you will modify + # the parent repostory, which isn't what you want. + sys.stdout.write('%s\n IS NOT TOP-LEVEL GIT DIRECTORY.\n' % directory) + return + + # Check to see if this repo is disabled. Quick return. + if git_repository_sync_is_disabled(git, directory): + sys.stdout.write('%s\n SYNC IS DISABLED.\n' % directory) + return + + with open(os.devnull, 'w') as devnull: + # If this fails, we will fetch before trying again. Don't spam user + # with error infomation. + if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable], + cwd=directory, stderr=devnull): + # if this succeeds, skip slow `git fetch`. + if verbose: + status(directory, checkoutable) # Success. + return + + # If the repo has changed, always force use of the correct repo. + # If origin already points to repo, this is a quick no-op. + subprocess.check_call( + [git, 'remote', 'set-url', 'origin', repo], cwd=directory) + + subprocess.check_call([git, 'fetch', '--quiet'], cwd=directory) + + subprocess.check_call([git, 'checkout', '--quiet', checkoutable], cwd=directory) + + if verbose: + status(directory, checkoutable) # Success. + + +def parse_file_to_dict(path): + dictionary = {} + contents = open(path).read() + # Need to convert Var() to vars[], so that the DEPS is actually Python. Var() + # comes from Autoroller using gclient which has a slightly different DEPS + # format. + contents = re.sub(r"Var\((.*?)\)", r"vars[\1]", contents) + exec(contents, dictionary) + return dictionary + + +def git_sync_deps(deps_file_path, command_line_os_requests, verbose): + """Grab dependencies, with optional platform support. + + Args: + deps_file_path (string) Path to the DEPS file. + + command_line_os_requests (list of strings) Can be empty list. + List of strings that should each be a key in the deps_os + dictionary in the DEPS file. + + Raises git Exceptions. + """ + git = git_executable() + assert git + + deps_file_directory = os.path.dirname(deps_file_path) + deps_file = parse_file_to_dict(deps_file_path) + dependencies = deps_file['deps'].copy() + os_specific_dependencies = deps_file.get('deps_os', dict()) + if 'all' in command_line_os_requests: + for value in list(os_specific_dependencies.values()): + dependencies.update(value) + else: + for os_name in command_line_os_requests: + # Add OS-specific dependencies + if os_name in os_specific_dependencies: + dependencies.update(os_specific_dependencies[os_name]) + for directory in dependencies: + for other_dir in dependencies: + if directory.startswith(other_dir + '/'): + raise Exception('%r is parent of %r' % (other_dir, directory)) + list_of_arg_lists = [] + for directory in sorted(dependencies): + if '@' in dependencies[directory]: + repo, checkoutable = dependencies[directory].split('@', 1) + else: + raise Exception("please specify commit or tag") + + relative_directory = os.path.join(deps_file_directory, directory) + + list_of_arg_lists.append( + (git, repo, checkoutable, relative_directory, verbose)) + + multithread(git_checkout_to_directory, list_of_arg_lists) + + for directory in deps_file.get('recursedeps', []): + recursive_path = os.path.join(deps_file_directory, directory, 'DEPS') + git_sync_deps(recursive_path, command_line_os_requests, verbose) + + +def multithread(function, list_of_arg_lists): + # for args in list_of_arg_lists: + # function(*args) + # return + threads = [] + for args in list_of_arg_lists: + thread = threading.Thread(None, function, None, args) + thread.start() + threads.append(thread) + for thread in threads: + thread.join() + + +def main(argv): + deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH) + verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False)) + + if '--help' in argv or '-h' in argv: + usage(deps_file_path) + return 1 + + git_sync_deps(deps_file_path, argv, verbose) + # subprocess.check_call( + # [sys.executable, + # os.path.join(os.path.dirname(deps_file_path), 'bin', 'fetch-gn')]) + return 0 + + +if __name__ == '__main__': + exit(main(sys.argv[1:])) diff --git a/third_party/spirv-tools/utils/roll_deps.sh b/third_party/spirv-tools/utils/roll_deps.sh new file mode 100755 index 0000000..7ecfdd3 --- /dev/null +++ b/third_party/spirv-tools/utils/roll_deps.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Copyright (c) 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Attempts to roll all entries in DEPS to tip-of-tree and create a commit. +# +# Depends on roll-dep from depot_path being in PATH. + +effcee_dir="external/effcee/" +effcee_trunk="origin/main" +googletest_dir="external/googletest/" +googletest_trunk="origin/master" +re2_dir="external/re2/" +re2_trunk="origin/master" +spirv_headers_dir="external/spirv-headers/" +spirv_headers_trunk="origin/master" + +# This script assumes it's parent directory is the repo root. +repo_path=$(dirname "$0")/.. + +cd "$repo_path" + +if [[ $(git diff --stat) != '' ]]; then + echo "Working tree is dirty, commit changes before attempting to roll DEPS" + exit 1 +fi + +old_head=$(git rev-parse HEAD) + +roll-dep --ignore-dirty-tree --roll-to="${effcee_trunk}" "${effcee_dir}" +roll-dep --ignore-dirty-tree --roll-to="${googletest_trunk}" "${googletest_dir}" +roll-dep --ignore-dirty-tree --roll-to="${re2_trunk}" "${re2_dir}" +roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}" + +git rebase --interactive "${old_head}" diff --git a/third_party/spirv-tools/utils/update_build_version.py b/third_party/spirv-tools/utils/update_build_version.py new file mode 100755 index 0000000..321de74 --- /dev/null +++ b/third_party/spirv-tools/utils/update_build_version.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +# Copyright (c) 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Updates an output file with version info unless the new content is the same +# as the existing content. +# +# Args: +# +# The output file will contain a line of text consisting of two C source syntax +# string literals separated by a comma: +# - The software version deduced from the CHANGES file in the given directory. +# - A longer string with the project name, the software version number, and +# git commit information for the directory. The commit information +# is the output of "git describe" if that succeeds, or "git rev-parse HEAD" +# if that succeeds, or otherwise a message containing the phrase +# "unknown hash". +# The string contents are escaped as necessary. + +import datetime +import errno +import os +import os.path +import re +import subprocess +import sys +import time + + +def mkdir_p(directory): + """Make the directory, and all its ancestors as required. Any of the + directories are allowed to already exist.""" + + if directory == "": + # We're being asked to make the current directory. + return + + try: + os.makedirs(directory) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(directory): + pass + else: + raise + + +def command_output(cmd, directory): + """Runs a command in a directory and returns its standard output stream. + + Captures the standard error stream. + + Raises a RuntimeError if the command fails to launch or otherwise fails. + """ + p = subprocess.Popen(cmd, + cwd=directory, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (stdout, _) = p.communicate() + if p.returncode != 0: + raise RuntimeError('Failed to run %s in %s' % (cmd, directory)) + return stdout + + +def deduce_software_version(directory): + """Returns a software version number parsed from the CHANGES file + in the given directory. + + The CHANGES file describes most recent versions first. + """ + + # Match the first well-formed version-and-date line. + # Allow trailing whitespace in the checked-out source code has + # unexpected carriage returns on a linefeed-only system such as + # Linux. + pattern = re.compile(r'^(v\d+\.\d+(-dev)?) \d\d\d\d-\d\d-\d\d\s*$') + changes_file = os.path.join(directory, 'CHANGES') + with open(changes_file, mode='r') as f: + for line in f.readlines(): + match = pattern.match(line) + if match: + return match.group(1) + raise Exception('No version number found in {}'.format(changes_file)) + + +def describe(directory): + """Returns a string describing the current Git HEAD version as descriptively + as possible. + + Runs 'git describe', or alternately 'git rev-parse HEAD', in directory. If + successful, returns the output; otherwise returns 'unknown hash, '.""" + try: + # decode() is needed here for Python3 compatibility. In Python2, + # str and bytes are the same type, but not in Python3. + # Popen.communicate() returns a bytes instance, which needs to be + # decoded into text data first in Python3. And this decode() won't + # hurt Python2. + return command_output(['git', 'describe'], directory).rstrip().decode() + except: + try: + return command_output( + ['git', 'rev-parse', 'HEAD'], directory).rstrip().decode() + except: + # This is the fallback case where git gives us no information, + # e.g. because the source tree might not be in a git tree. + # In this case, usually use a timestamp. However, to ensure + # reproducible builds, allow the builder to override the wall + # clock time with environment variable SOURCE_DATE_EPOCH + # containing a (presumably) fixed timestamp. + timestamp = int(os.environ.get('SOURCE_DATE_EPOCH', time.time())) + formatted = datetime.datetime.utcfromtimestamp(timestamp).isoformat() + return 'unknown hash, {}'.format(formatted) + + +def main(): + if len(sys.argv) != 3: + print('usage: {} '.format(sys.argv[0])) + sys.exit(1) + + output_file = sys.argv[2] + mkdir_p(os.path.dirname(output_file)) + + software_version = deduce_software_version(sys.argv[1]) + new_content = '"{}", "SPIRV-Tools {} {}"\n'.format( + software_version, software_version, + describe(sys.argv[1]).replace('"', '\\"')) + + if os.path.isfile(output_file): + with open(output_file, 'r') as f: + if new_content == f.read(): + return + + with open(output_file, 'w') as f: + f.write(new_content) + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/utils/vscode/.gitignore b/third_party/spirv-tools/utils/vscode/.gitignore new file mode 100644 index 0000000..e934adf --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/.gitignore @@ -0,0 +1 @@ +cache/ diff --git a/third_party/spirv-tools/utils/vscode/README.md b/third_party/spirv-tools/utils/vscode/README.md new file mode 100644 index 0000000..bc02211 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/README.md @@ -0,0 +1,17 @@ +# Visual Studio Code extension for SPIR-V disassembly files + +This directory holds a Visual Studio Code extension adding syntax highlighting for SPIR-V assembly files (`.spvasm`) + +## Dependencies + +In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies: +* [`npm`](https://www.npmjs.com/) +* [`golang`](https://golang.org/) + +## Installing (macOS / Linux) + +Run `install.sh` + +## Installing (Windows) + +Run `install.bat` diff --git a/third_party/spirv-tools/utils/vscode/extension.js b/third_party/spirv-tools/utils/vscode/extension.js new file mode 100644 index 0000000..e7fec28 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/extension.js @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var path = require('path'); +var vscode = require('vscode'); +var langClient = require('vscode-languageclient'); + +var LanguageClient = langClient.LanguageClient; + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +function activate(context) { + let serverModule = path.join(context.extensionPath, 'langsvr'); + let debugOptions = {}; + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + let serverOptions = { + run: { command: serverModule, transport: langClient.stdio }, + debug: { command: serverModule, transport: langClient.stdio, options: debugOptions } + } + + // Options to control the language client + let clientOptions = { + documentSelector: ['spirv'], + synchronize: { + // Synchronize the setting section 'spirv' to the server + configurationSection: 'spirv', + // Notify the server about file changes to .spvasm files contained in the workspace + fileEvents: vscode.workspace.createFileSystemWatcher('**/*.spvasm') + } + } + + // Create the language client and start the client. + let disposable = new LanguageClient('spirv', serverOptions, clientOptions).start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); + + // Set the language configuration here instead of a language configuration + // file to work around https://github.com/microsoft/vscode/issues/42649. + vscode.languages.setLanguageConfiguration("spirv", { + comments: { "lineComment": ";" }, + wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\@\#\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, + }); +} +exports.activate = activate; + +// this method is called when your extension is deactivated +function deactivate() { +} +exports.deactivate = deactivate; diff --git a/third_party/spirv-tools/utils/vscode/install.bat b/third_party/spirv-tools/utils/vscode/install.bat new file mode 100644 index 0000000..21a52ec --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/install.bat @@ -0,0 +1,30 @@ +@REM Copyright (c) 2019 Google Inc. +@REM +@REM Licensed under the Apache License, Version 2.0 (the "License"); +@REM you may not use this file except in compliance with the License. +@REM You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, software +@REM distributed under the License is distributed on an "AS IS" BASIS, +@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@REM See the License for the specific language governing permissions and +@REM limitations under the License. + +@set EXT_PATH=%userprofile%\.vscode\extensions\google.spirvls-0.0.1 +@set ROOT_PATH=%~dp0 + +go run %ROOT_PATH%\src\tools\gen-grammar.go --cache %ROOT_PATH%\cache --template %ROOT_PATH%\spirv.json.tmpl --out %ROOT_PATH%\spirv.json +go run %ROOT_PATH%\src\tools\gen-grammar.go --cache %ROOT_PATH%\cache --template %ROOT_PATH%\src\schema\schema.go.tmpl --out %ROOT_PATH%\src\schema\schema.go + +if not exist %EXT_PATH% mkdir -p %EXT_PATH% +copy %ROOT_PATH%\extension.js %EXT_PATH% +copy %ROOT_PATH%\package.json %EXT_PATH% +copy %ROOT_PATH%\spirv.json %EXT_PATH% + +go build -o %EXT_PATH%\langsvr %ROOT_PATH%\src\langsvr.go + +@pushd %EXT_PATH% +call npm install +@popd diff --git a/third_party/spirv-tools/utils/vscode/install.sh b/third_party/spirv-tools/utils/vscode/install.sh new file mode 100755 index 0000000..01fc914 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/install.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Copyright (c) 2019 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e # Fail on any error. + +EXT_PATH=~/.vscode/extensions/google.spirvls-0.0.1 +ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/spirv.json.tmpl --out ${ROOT_PATH}/spirv.json +go run ${ROOT_PATH}/src/tools/gen-grammar.go --cache ${ROOT_PATH}/cache --template ${ROOT_PATH}/src/schema/schema.go.tmpl --out ${ROOT_PATH}/src/schema/schema.go + +mkdir -p ${EXT_PATH} +cp ${ROOT_PATH}/extension.js ${EXT_PATH} +cp ${ROOT_PATH}/package.json ${EXT_PATH} +cp ${ROOT_PATH}/spirv.json ${EXT_PATH} + +go build -o ${EXT_PATH}/langsvr ${ROOT_PATH}/src/langsvr.go + +cd ${EXT_PATH} +npm install diff --git a/third_party/spirv-tools/utils/vscode/package.json b/third_party/spirv-tools/utils/vscode/package.json new file mode 100644 index 0000000..76fb348 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/package.json @@ -0,0 +1,39 @@ +{ + "name": "spirvls", + "description": "Language support for SPIR-V disassembly files", + "author": "Google", + "license": "Apache-2.0", + "version": "0.0.1", + "private": true, + "publisher": "Google", + "engines": { + "vscode": "^0.10.10" + }, + "categories": [ + "Programming Languages" + ], + "contributes": { + "languages": [ + { + "id": "spirv", + "extensions": [ + "spvasm" + ] + } + ], + "grammars": [ + { + "language": "spirv", + "scopeName": "source.spirv", + "path": "spirv.json" + } + ] + }, + "dependencies": { + "vscode-languageclient": "~4.3.0" + }, + "activationEvents": [ + "*" + ], + "main": "./extension.js" +} diff --git a/third_party/spirv-tools/utils/vscode/spirv.json b/third_party/spirv-tools/utils/vscode/spirv.json new file mode 100644 index 0000000..7999b52 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/spirv.json @@ -0,0 +1,247 @@ +{ + "scopeName": "source.spirv", + "name": "SPIR-V", + "comment": "Generated by gen-grammar.go --template=../../spirv.json.tmpl --out=../../spirv.json. Do not modify this file directly.", + "patterns": [ + { "include": "#BitEnum_ImageOperands" }, + { "include": "#BitEnum_FPFastMathMode" }, + { "include": "#BitEnum_SelectionControl" }, + { "include": "#BitEnum_LoopControl" }, + { "include": "#BitEnum_FunctionControl" }, + { "include": "#BitEnum_MemorySemantics" }, + { "include": "#BitEnum_MemoryAccess" }, + { "include": "#BitEnum_KernelProfilingInfo" }, + { "include": "#ValueEnum_SourceLanguage" }, + { "include": "#ValueEnum_ExecutionModel" }, + { "include": "#ValueEnum_AddressingModel" }, + { "include": "#ValueEnum_MemoryModel" }, + { "include": "#ValueEnum_ExecutionMode" }, + { "include": "#ValueEnum_StorageClass" }, + { "include": "#ValueEnum_Dim" }, + { "include": "#ValueEnum_SamplerAddressingMode" }, + { "include": "#ValueEnum_SamplerFilterMode" }, + { "include": "#ValueEnum_ImageFormat" }, + { "include": "#ValueEnum_ImageChannelOrder" }, + { "include": "#ValueEnum_ImageChannelDataType" }, + { "include": "#ValueEnum_FPRoundingMode" }, + { "include": "#ValueEnum_LinkageType" }, + { "include": "#ValueEnum_AccessQualifier" }, + { "include": "#ValueEnum_FunctionParameterAttribute" }, + { "include": "#ValueEnum_Decoration" }, + { "include": "#ValueEnum_BuiltIn" }, + { "include": "#ValueEnum_Scope" }, + { "include": "#ValueEnum_GroupOperation" }, + { "include": "#ValueEnum_KernelEnqueueFlags" }, + { "include": "#ValueEnum_Capability" }, + { "include": "#BitEnum_DebugInfoFlags" }, + { "include": "#ValueEnum_DebugBaseTypeAttributeEncoding" }, + { "include": "#ValueEnum_DebugCompositeType" }, + { "include": "#ValueEnum_DebugTypeQualifier" }, + { "include": "#ValueEnum_DebugOperation" }, + { "include": "#ValueEnum_DebugImportedEntity" }, + { "include": "#opcode" }, + { "include": "#extopcode" }, + { "include": "#identifier" }, + { "include": "#number" }, + { "include": "#string" }, + { "include": "#comment" }, + { "include": "#operator" } + ], + "repository": { + "BitEnum_ImageOperands": { + "match": "\\b(None|Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod|MakeTexelAvailable|MakeTexelAvailableKHR|MakeTexelVisible|MakeTexelVisibleKHR|NonPrivateTexel|NonPrivateTexelKHR|VolatileTexel|VolatileTexelKHR|SignExtend|ZeroExtend)\\b", + "name": "keyword.spirv" + }, + "BitEnum_FPFastMathMode": { + "match": "\\b(None|NotNaN|NotInf|NSZ|AllowRecip|Fast)\\b", + "name": "keyword.spirv" + }, + "BitEnum_SelectionControl": { + "match": "\\b(None|Flatten|DontFlatten)\\b", + "name": "keyword.spirv" + }, + "BitEnum_LoopControl": { + "match": "\\b(None|Unroll|DontUnroll|DependencyInfinite|DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount)\\b", + "name": "keyword.spirv" + }, + "BitEnum_FunctionControl": { + "match": "\\b(None|Inline|DontInline|Pure|Const)\\b", + "name": "keyword.spirv" + }, + "BitEnum_MemorySemantics": { + "match": "\\b(Relaxed|None|Acquire|Release|AcquireRelease|SequentiallyConsistent|UniformMemory|SubgroupMemory|WorkgroupMemory|CrossWorkgroupMemory|AtomicCounterMemory|ImageMemory|OutputMemory|OutputMemoryKHR|MakeAvailable|MakeAvailableKHR|MakeVisible|MakeVisibleKHR|Volatile)\\b", + "name": "keyword.spirv" + }, + "BitEnum_MemoryAccess": { + "match": "\\b(None|Volatile|Aligned|Nontemporal|MakePointerAvailable|MakePointerAvailableKHR|MakePointerVisible|MakePointerVisibleKHR|NonPrivatePointer|NonPrivatePointerKHR)\\b", + "name": "keyword.spirv" + }, + "BitEnum_KernelProfilingInfo": { + "match": "\\b(None|CmdExecTime)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_SourceLanguage": { + "match": "\\b(Unknown|ESSL|GLSL|OpenCL_C|OpenCL_CPP|HLSL)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ExecutionModel": { + "match": "\\b(Vertex|TessellationControl|TessellationEvaluation|Geometry|Fragment|GLCompute|Kernel|TaskNV|MeshNV|RayGenerationNV|IntersectionNV|AnyHitNV|ClosestHitNV|MissNV|CallableNV)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_AddressingModel": { + "match": "\\b(Logical|Physical32|Physical64|PhysicalStorageBuffer64|PhysicalStorageBuffer64EXT)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_MemoryModel": { + "match": "\\b(Simple|GLSL450|OpenCL|Vulkan|VulkanKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ExecutionMode": { + "match": "\\b(Invocations|SpacingEqual|SpacingFractionalEven|SpacingFractionalOdd|VertexOrderCw|VertexOrderCcw|PixelCenterInteger|OriginUpperLeft|OriginLowerLeft|EarlyFragmentTests|PointMode|Xfb|DepthReplacing|DepthGreater|DepthLess|DepthUnchanged|LocalSize|LocalSizeHint|InputPoints|InputLines|InputLinesAdjacency|Triangles|InputTrianglesAdjacency|Quads|Isolines|OutputVertices|OutputPoints|OutputLineStrip|OutputTriangleStrip|VecTypeHint|ContractionOff|Initializer|Finalizer|SubgroupSize|SubgroupsPerWorkgroup|SubgroupsPerWorkgroupId|LocalSizeId|LocalSizeHintId|PostDepthCoverage|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|StencilRefReplacingEXT|OutputLinesNV|OutputPrimitivesNV|DerivativeGroupQuadsNV|DerivativeGroupLinearNV|OutputTrianglesNV|PixelInterlockOrderedEXT|PixelInterlockUnorderedEXT|SampleInterlockOrderedEXT|SampleInterlockUnorderedEXT|ShadingRateInterlockOrderedEXT|ShadingRateInterlockUnorderedEXT)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_StorageClass": { + "match": "\\b(UniformConstant|Input|Uniform|Output|Workgroup|CrossWorkgroup|Private|Function|Generic|PushConstant|AtomicCounter|Image|StorageBuffer|CallableDataNV|IncomingCallableDataNV|RayPayloadNV|HitAttributeNV|IncomingRayPayloadNV|ShaderRecordBufferNV|PhysicalStorageBuffer|PhysicalStorageBufferEXT)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_Dim": { + "match": "\\b(1D|2D|3D|Cube|Rect|Buffer|SubpassData)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_SamplerAddressingMode": { + "match": "\\b(None|ClampToEdge|Clamp|Repeat|RepeatMirrored)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_SamplerFilterMode": { + "match": "\\b(Nearest|Linear)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ImageFormat": { + "match": "\\b(Unknown|Rgba32f|Rgba16f|R32f|Rgba8|Rgba8Snorm|Rg32f|Rg16f|R11fG11fB10f|R16f|Rgba16|Rgb10A2|Rg16|Rg8|R16|R8|Rgba16Snorm|Rg16Snorm|Rg8Snorm|R16Snorm|R8Snorm|Rgba32i|Rgba16i|Rgba8i|R32i|Rg32i|Rg16i|Rg8i|R16i|R8i|Rgba32ui|Rgba16ui|Rgba8ui|R32ui|Rgb10a2ui|Rg32ui|Rg16ui|Rg8ui|R16ui|R8ui)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ImageChannelOrder": { + "match": "\\b(R|A|RG|RA|RGB|RGBA|BGRA|ARGB|Intensity|Luminance|Rx|RGx|RGBx|Depth|DepthStencil|sRGB|sRGBx|sRGBA|sBGRA|ABGR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_ImageChannelDataType": { + "match": "\\b(SnormInt8|SnormInt16|UnormInt8|UnormInt16|UnormShort565|UnormShort555|UnormInt101010|SignedInt8|SignedInt16|SignedInt32|UnsignedInt8|UnsignedInt16|UnsignedInt32|HalfFloat|Float|UnormInt24|UnormInt101010_2)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_FPRoundingMode": { + "match": "\\b(RTE|RTZ|RTP|RTN)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_LinkageType": { + "match": "\\b(Export|Import)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_AccessQualifier": { + "match": "\\b(ReadOnly|WriteOnly|ReadWrite)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_FunctionParameterAttribute": { + "match": "\\b(Zext|Sext|ByVal|Sret|NoAlias|NoCapture|NoWrite|NoReadWrite)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_Decoration": { + "match": "\\b(RelaxedPrecision|SpecId|Block|BufferBlock|RowMajor|ColMajor|ArrayStride|MatrixStride|GLSLShared|GLSLPacked|CPacked|BuiltIn|NoPerspective|Flat|Patch|Centroid|Sample|Invariant|Restrict|Aliased|Volatile|Constant|Coherent|NonWritable|NonReadable|Uniform|UniformId|SaturatedConversion|Stream|Location|Component|Index|Binding|DescriptorSet|Offset|XfbBuffer|XfbStride|FuncParamAttr|FPRoundingMode|FPFastMathMode|LinkageAttributes|NoContraction|InputAttachmentIndex|Alignment|MaxByteOffset|AlignmentId|MaxByteOffsetId|NoSignedWrap|NoUnsignedWrap|ExplicitInterpAMD|OverrideCoverageNV|PassthroughNV|ViewportRelativeNV|SecondaryViewportRelativeNV|PerPrimitiveNV|PerViewNV|PerTaskNV|PerVertexNV|NonUniform|NonUniformEXT|RestrictPointer|RestrictPointerEXT|AliasedPointer|AliasedPointerEXT|CounterBuffer|HlslCounterBufferGOOGLE|UserSemantic|HlslSemanticGOOGLE|UserTypeGOOGLE)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_BuiltIn": { + "match": "\\b(Position|PointSize|ClipDistance|CullDistance|VertexId|InstanceId|PrimitiveId|InvocationId|Layer|ViewportIndex|TessLevelOuter|TessLevelInner|TessCoord|PatchVertices|FragCoord|PointCoord|FrontFacing|SampleId|SamplePosition|SampleMask|FragDepth|HelperInvocation|NumWorkgroups|WorkgroupSize|WorkgroupId|LocalInvocationId|GlobalInvocationId|LocalInvocationIndex|WorkDim|GlobalSize|EnqueuedWorkgroupSize|GlobalOffset|GlobalLinearId|SubgroupSize|SubgroupMaxSize|NumSubgroups|NumEnqueuedSubgroups|SubgroupId|SubgroupLocalInvocationId|VertexIndex|InstanceIndex|SubgroupEqMask|SubgroupGeMask|SubgroupGtMask|SubgroupLeMask|SubgroupLtMask|SubgroupEqMaskKHR|SubgroupGeMaskKHR|SubgroupGtMaskKHR|SubgroupLeMaskKHR|SubgroupLtMaskKHR|BaseVertex|BaseInstance|DrawIndex|DeviceIndex|ViewIndex|BaryCoordNoPerspAMD|BaryCoordNoPerspCentroidAMD|BaryCoordNoPerspSampleAMD|BaryCoordSmoothAMD|BaryCoordSmoothCentroidAMD|BaryCoordSmoothSampleAMD|BaryCoordPullModelAMD|FragStencilRefEXT|ViewportMaskNV|SecondaryPositionNV|SecondaryViewportMaskNV|PositionPerViewNV|ViewportMaskPerViewNV|FullyCoveredEXT|TaskCountNV|PrimitiveCountNV|PrimitiveIndicesNV|ClipDistancePerViewNV|CullDistancePerViewNV|LayerPerViewNV|MeshViewCountNV|MeshViewIndicesNV|BaryCoordNV|BaryCoordNoPerspNV|FragSizeEXT|FragmentSizeNV|FragInvocationCountEXT|InvocationsPerPixelNV|LaunchIdNV|LaunchSizeNV|WorldRayOriginNV|WorldRayDirectionNV|ObjectRayOriginNV|ObjectRayDirectionNV|RayTminNV|RayTmaxNV|InstanceCustomIndexNV|ObjectToWorldNV|WorldToObjectNV|HitTNV|HitKindNV|IncomingRayFlagsNV|WarpsPerSMNV|SMCountNV|WarpIDNV|SMIDNV)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_Scope": { + "match": "\\b(CrossDevice|Device|Workgroup|Subgroup|Invocation|QueueFamily|QueueFamilyKHR)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_GroupOperation": { + "match": "\\b(Reduce|InclusiveScan|ExclusiveScan|ClusteredReduce|PartitionedReduceNV|PartitionedInclusiveScanNV|PartitionedExclusiveScanNV)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_KernelEnqueueFlags": { + "match": "\\b(NoWait|WaitKernel|WaitWorkGroup)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_Capability": { + "match": "\\b(Matrix|Shader|Geometry|Tessellation|Addresses|Linkage|Kernel|Vector16|Float16Buffer|Float16|Float64|Int64|Int64Atomics|ImageBasic|ImageReadWrite|ImageMipmap|Pipes|Groups|DeviceEnqueue|LiteralSampler|AtomicStorage|Int16|TessellationPointSize|GeometryPointSize|ImageGatherExtended|StorageImageMultisample|UniformBufferArrayDynamicIndexing|SampledImageArrayDynamicIndexing|StorageBufferArrayDynamicIndexing|StorageImageArrayDynamicIndexing|ClipDistance|CullDistance|ImageCubeArray|SampleRateShading|ImageRect|SampledRect|GenericPointer|Int8|InputAttachment|SparseResidency|MinLod|Sampled1D|Image1D|SampledCubeArray|SampledBuffer|ImageBuffer|ImageMSArray|StorageImageExtendedFormats|ImageQuery|DerivativeControl|InterpolationFunction|TransformFeedback|GeometryStreams|StorageImageReadWithoutFormat|StorageImageWriteWithoutFormat|MultiViewport|SubgroupDispatch|NamedBarrier|PipeStorage|GroupNonUniform|GroupNonUniformVote|GroupNonUniformArithmetic|GroupNonUniformBallot|GroupNonUniformShuffle|GroupNonUniformShuffleRelative|GroupNonUniformClustered|GroupNonUniformQuad|ShaderLayer|ShaderViewportIndex|SubgroupBallotKHR|DrawParameters|SubgroupVoteKHR|StorageBuffer16BitAccess|StorageUniformBufferBlock16|UniformAndStorageBuffer16BitAccess|StorageUniform16|StoragePushConstant16|StorageInputOutput16|DeviceGroup|MultiView|VariablePointersStorageBuffer|VariablePointers|AtomicStorageOps|SampleMaskPostDepthCoverage|StorageBuffer8BitAccess|UniformAndStorageBuffer8BitAccess|StoragePushConstant8|DenormPreserve|DenormFlushToZero|SignedZeroInfNanPreserve|RoundingModeRTE|RoundingModeRTZ|Float16ImageAMD|ImageGatherBiasLodAMD|FragmentMaskAMD|StencilExportEXT|ImageReadWriteLodAMD|ShaderClockKHR|SampleMaskOverrideCoverageNV|GeometryShaderPassthroughNV|ShaderViewportIndexLayerEXT|ShaderViewportIndexLayerNV|ShaderViewportMaskNV|ShaderStereoViewNV|PerViewAttributesNV|FragmentFullyCoveredEXT|MeshShadingNV|ImageFootprintNV|FragmentBarycentricNV|ComputeDerivativeGroupQuadsNV|FragmentDensityEXT|ShadingRateNV|GroupNonUniformPartitionedNV|ShaderNonUniform|ShaderNonUniformEXT|RuntimeDescriptorArray|RuntimeDescriptorArrayEXT|InputAttachmentArrayDynamicIndexing|InputAttachmentArrayDynamicIndexingEXT|UniformTexelBufferArrayDynamicIndexing|UniformTexelBufferArrayDynamicIndexingEXT|StorageTexelBufferArrayDynamicIndexing|StorageTexelBufferArrayDynamicIndexingEXT|UniformBufferArrayNonUniformIndexing|UniformBufferArrayNonUniformIndexingEXT|SampledImageArrayNonUniformIndexing|SampledImageArrayNonUniformIndexingEXT|StorageBufferArrayNonUniformIndexing|StorageBufferArrayNonUniformIndexingEXT|StorageImageArrayNonUniformIndexing|StorageImageArrayNonUniformIndexingEXT|InputAttachmentArrayNonUniformIndexing|InputAttachmentArrayNonUniformIndexingEXT|UniformTexelBufferArrayNonUniformIndexing|UniformTexelBufferArrayNonUniformIndexingEXT|StorageTexelBufferArrayNonUniformIndexing|StorageTexelBufferArrayNonUniformIndexingEXT|RayTracingNV|VulkanMemoryModel|VulkanMemoryModelKHR|VulkanMemoryModelDeviceScope|VulkanMemoryModelDeviceScopeKHR|PhysicalStorageBufferAddresses|PhysicalStorageBufferAddressesEXT|ComputeDerivativeGroupLinearNV|CooperativeMatrixNV|FragmentShaderSampleInterlockEXT|FragmentShaderShadingRateInterlockEXT|ShaderSMBuiltinsNV|FragmentShaderPixelInterlockEXT|DemoteToHelperInvocationEXT|SubgroupShuffleINTEL|SubgroupBufferBlockIOINTEL|SubgroupImageBlockIOINTEL|SubgroupImageMediaBlockIOINTEL|IntegerFunctions2INTEL|SubgroupAvcMotionEstimationINTEL|SubgroupAvcMotionEstimationIntraINTEL|SubgroupAvcMotionEstimationChromaINTEL)\\b", + "name": "keyword.spirv" + }, + "BitEnum_DebugInfoFlags": { + "match": "\\b(FlagIsProtected|FlagIsPrivate|FlagIsPublic|FlagIsLocal|FlagIsDefinition|FlagFwdDecl|FlagArtificial|FlagExplicit|FlagPrototyped|FlagObjectPointer|FlagStaticMember|FlagIndirectVariable|FlagLValueReference|FlagRValueReference|FlagIsOptimized|FlagIsEnumClass|FlagTypePassByValue|FlagTypePassByReference)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugBaseTypeAttributeEncoding": { + "match": "\\b(Unspecified|Address|Boolean|Float|Signed|SignedChar|Unsigned|UnsignedChar)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugCompositeType": { + "match": "\\b(Class|Structure|Union)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugTypeQualifier": { + "match": "\\b(ConstType|VolatileType|RestrictType|AtomicType)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugOperation": { + "match": "\\b(Deref|Plus|Minus|PlusUconst|BitPiece|Swap|Xderef|StackValue|Constu|Fragment)\\b", + "name": "keyword.spirv" + }, + "ValueEnum_DebugImportedEntity": { + "match": "\\b(ImportedModule|ImportedDeclaration)\\b", + "name": "keyword.spirv" + }, + "opcode": { + "match": "(Op[a-zA-Z]+)", + "name": "entity.name.function.spirv" + }, + "extopcode": { + "match": "(Round|RoundEven|Trunc|FAbs|SAbs|FSign|SSign|Floor|Ceil|Fract|Radians|Degrees|Sin|Cos|Tan|Asin|Acos|Atan|Sinh|Cosh|Tanh|Asinh|Acosh|Atanh|Atan2|Pow|Exp|Log|Exp2|Log2|Sqrt|InverseSqrt|Determinant|MatrixInverse|Modf|ModfStruct|FMin|UMin|SMin|FMax|UMax|SMax|FClamp|UClamp|SClamp|FMix|IMix|Step|SmoothStep|Fma|Frexp|FrexpStruct|Ldexp|PackSnorm4x8|PackUnorm4x8|PackSnorm2x16|PackUnorm2x16|PackHalf2x16|PackDouble2x32|UnpackSnorm2x16|UnpackUnorm2x16|UnpackHalf2x16|UnpackSnorm4x8|UnpackUnorm4x8|UnpackDouble2x32|Length|Distance|Cross|Normalize|FaceForward|Reflect|Refract|FindILsb|FindSMsb|FindUMsb|InterpolateAtCentroid|InterpolateAtSample|InterpolateAtOffset|NMin|NMax|NClamp|acos|acosh|acospi|asin|asinh|asinpi|atan|atan2|atanh|atanpi|atan2pi|cbrt|ceil|copysign|cos|cosh|cospi|erfc|erf|exp|exp2|exp10|expm1|fabs|fdim|floor|fma|fmax|fmin|fmod|fract|frexp|hypot|ilogb|ldexp|lgamma|lgamma_r|log|log2|log10|log1p|logb|mad|maxmag|minmag|modf|nan|nextafter|pow|pown|powr|remainder|remquo|rint|rootn|round|rsqrt|sin|sincos|sinh|sinpi|sqrt|tan|tanh|tanpi|tgamma|trunc|half_cos|half_divide|half_exp|half_exp2|half_exp10|half_log|half_log2|half_log10|half_powr|half_recip|half_rsqrt|half_sin|half_sqrt|half_tan|native_cos|native_divide|native_exp|native_exp2|native_exp10|native_log|native_log2|native_log10|native_powr|native_recip|native_rsqrt|native_sin|native_sqrt|native_tan|s_abs|s_abs_diff|s_add_sat|u_add_sat|s_hadd|u_hadd|s_rhadd|u_rhadd|s_clamp|u_clamp|clz|ctz|s_mad_hi|u_mad_sat|s_mad_sat|s_max|u_max|s_min|u_min|s_mul_hi|rotate|s_sub_sat|u_sub_sat|u_upsample|s_upsample|popcount|s_mad24|u_mad24|s_mul24|u_mul24|u_abs|u_abs_diff|u_mul_hi|u_mad_hi|fclamp|degrees|fmax_common|fmin_common|mix|radians|step|smoothstep|sign|cross|distance|length|normalize|fast_distance|fast_length|fast_normalize|bitselect|select|vloadn|vstoren|vload_half|vload_halfn|vstore_half|vstore_half_r|vstore_halfn|vstore_halfn_r|vloada_halfn|vstorea_halfn|vstorea_halfn_r|shuffle|shuffle2|printf|prefetch|DebugInfoNone|DebugCompilationUnit|DebugTypeBasic|DebugTypePointer|DebugTypeQualifier|DebugTypeArray|DebugTypeVector|DebugTypedef|DebugTypeFunction|DebugTypeEnum|DebugTypeComposite|DebugTypeMember|DebugTypeInheritance|DebugTypePtrToMember|DebugTypeTemplate|DebugTypeTemplateParameter|DebugTypeTemplateTemplateParameter|DebugTypeTemplateParameterPack|DebugGlobalVariable|DebugFunctionDeclaration|DebugFunction|DebugLexicalBlock|DebugLexicalBlockDiscriminator|DebugScope|DebugNoScope|DebugInlinedAt|DebugLocalVariable|DebugInlinedVariable|DebugDeclare|DebugValue|DebugOperation|DebugExpression|DebugMacroDef|DebugMacroUndef|DebugImportedEntity|DebugSource)", + "name": "entity.name.function.ext" + }, + "identifier": { + "match": "%[a-zA-Z0-9_]+", + "name": "variable.spirv" + }, + "number": { + "match": "\\b[0-9]+.?[0-9]*\\b", + "name": "constant.numeric.spirv" + }, + "comment": { + "match": ";[^\n]*", + "name": "comment.line.spirv" + }, + "operator": { + "match": "=", + "name": "keyword.operator.spirv" + }, + "string": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.spirv" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.spirv" + } + }, + "name": "string.quoted.double.spirv", + "patterns": [ { "include": "#string_escaped_char" } ] + }, + "string_escaped_char": { + "patterns": [ + { + "match": "\\\\([0-7]{3}|[abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})", + "name": "constant.character.escape.spirv" + }, { + "match": "\\\\[^0-7xuUabfnrtv\\'\"]", + "name": "invalid.illegal.unknown-escape.spirv" + } + ] + } + } +} diff --git a/third_party/spirv-tools/utils/vscode/spirv.json.tmpl b/third_party/spirv-tools/utils/vscode/spirv.json.tmpl new file mode 100644 index 0000000..f655e52 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/spirv.json.tmpl @@ -0,0 +1,72 @@ +{ + "scopeName": "source.spirv", + "name": "SPIR-V", + "comment": "Generated by {{GenerateArguments}}. Do not modify this file directly.", + "patterns": [ +{{range $o := .All.OperandKinds}}{{if len $o.Enumerants}} { "include": "#{{$o.Category}}_{{$o.Kind}}" }, +{{end}}{{end}} { "include": "#opcode" }, + { "include": "#extopcode" }, + { "include": "#identifier" }, + { "include": "#number" }, + { "include": "#string" }, + { "include": "#comment" }, + { "include": "#operator" } + ], + "repository": { {{range $o := .All.OperandKinds}}{{if len $o.Enumerants}} + "{{$o.Category}}_{{$o.Kind}}": { + "match": "\\b({{OperandKindsMatch $o}})\\b", + "name": "keyword.spirv" + },{{end}}{{end}} + "opcode": { + "match": "(Op[a-zA-Z]+)", + "name": "entity.name.function.spirv" + }, + "extopcode": { + "match": "({{AllExtOpcodes}})", + "name": "entity.name.function.ext" + }, + "identifier": { + "match": "%[a-zA-Z0-9_]+", + "name": "variable.spirv" + }, + "number": { + "match": "\\b[0-9]+.?[0-9]*\\b", + "name": "constant.numeric.spirv" + }, + "comment": { + "match": ";[^\n]*", + "name": "comment.line.spirv" + }, + "operator": { + "match": "=", + "name": "keyword.operator.spirv" + }, + "string": { + "begin": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.spirv" + } + }, + "end": "\"", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.spirv" + } + }, + "name": "string.quoted.double.spirv", + "patterns": [ { "include": "#string_escaped_char" } ] + }, + "string_escaped_char": { + "patterns": [ + { + "match": "\\\\([0-7]{3}|[abfnrtv\\\\'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})", + "name": "constant.character.escape.spirv" + }, { + "match": "\\\\[^0-7xuUabfnrtv\\'\"]", + "name": "invalid.illegal.unknown-escape.spirv" + } + ] + } + } +} diff --git a/third_party/spirv-tools/utils/vscode/src/grammar/grammar.go b/third_party/spirv-tools/utils/vscode/src/grammar/grammar.go new file mode 100644 index 0000000..857a193 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/grammar/grammar.go @@ -0,0 +1,81 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package grammar holds the JSON type definitions for the SPIR-V grammar schema. +// +// See https://www.khronos.org/registry/spir-v/specs/unified1/MachineReadableGrammar.html +// for more information. +package grammar + +// Root is the top-level structure of the JSON grammar. +type Root struct { + MagicNumber string `json:"magic_number"` + MajorVersion int `json:"major_version"` + MinorVersion int `json:"minor_version"` + Revision int `json:"revision"` + Instructions []Instruction `json:"instructions"` + OperandKinds []OperandKind `json:"operand_kinds"` +} + +// Instruction holds information about a specific SPIR-V instruction. +type Instruction struct { + Opname string `json:"opname"` + Class string `json:"class"` + Opcode int `json:"opcode"` + Operands []Operand `json:"operands"` +} + +// Operand contains information about a logical operand for an instruction. +type Operand struct { + Kind string `json:"kind"` + Name string `json:"name"` + Quantifier Quantifier `json:"quantifier"` +} + +// OperandKind contains information about a specific operand kind. +type OperandKind struct { + Category string `json:"category"` + Kind string `json:"kind"` + Enumerants []Enumerant `json:"enumerants"` + Bases []string `json:"bases"` +} + +// Enumerant contains information about an enumerant in an enum. +type Enumerant struct { + Enumerant string `json:"enumerant"` + Value interface{} `json:"value"` + Capabilities []string `json:"capabilities"` + Parameters []Parameter `json:"parameters"` + Version string `json:"version"` +} + +// Parameter contains information about a logical parameter for an enumerant. +type Parameter struct { + Kind string `json:"kind"` + Name string `json:"name"` +} + +// Quantifier indicates the number of times the quantified term may appear. +type Quantifier string + +const ( + // Once indicates the quantified term may appear exactly once. + Once Quantifier = "" + // ZeroOrOnce indicates the quantified term may appear zero or one + // time; an optional term. + ZeroOrOnce Quantifier = "?" + // ZeroOrMany indicates the quantified term may appear any number of + // times. + ZeroOrMany Quantifier = "*" +) diff --git a/third_party/spirv-tools/utils/vscode/src/langsvr.go b/third_party/spirv-tools/utils/vscode/src/langsvr.go new file mode 100644 index 0000000..d1b80dc --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/langsvr.go @@ -0,0 +1,527 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// langsvr implements a Language Server for the SPIRV assembly language. +package main + +import ( + "context" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path" + "sort" + "strings" + "sync" + "unicode/utf8" + + "./parser" + "./schema" + + "./lsp/jsonrpc2" + lsp "./lsp/protocol" +) + +// rSpy is a reader 'spy' that wraps an io.Reader, and logs all data that passes +// through it. +type rSpy struct { + prefix string + r io.Reader +} + +func (s rSpy) Read(p []byte) (n int, err error) { + n, err = s.r.Read(p) + log.Printf("%v %v", s.prefix, string(p[:n])) + return n, err +} + +// wSpy is a reader 'spy' that wraps an io.Writer, and logs all data that passes +// through it. +type wSpy struct { + prefix string + w io.Writer +} + +func (s wSpy) Write(p []byte) (n int, err error) { + n, err = s.w.Write(p) + log.Printf("%v %v", s.prefix, string(p)) + return n, err +} + +// main entry point. +func main() { + // create a log file in the executable's directory. + if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil { + defer logfile.Close() + log.SetOutput(logfile) + } else { + log.SetOutput(ioutil.Discard) + } + + log.Println("language server started") + + stream := jsonrpc2.NewHeaderStream(rSpy{"IDE", os.Stdin}, wSpy{"LS", os.Stdout}) + s := server{ + files: map[string]*file{}, + } + s.ctx, s.conn, s.client = lsp.NewServer(context.Background(), stream, &s) + if err := s.conn.Run(s.ctx); err != nil { + log.Panicln(err) + os.Exit(1) + } + + log.Println("language server stopped") +} + +type server struct { + ctx context.Context + conn *jsonrpc2.Conn + client lsp.Client + + files map[string]*file + filesMutex sync.Mutex +} + +// file represents a source file +type file struct { + fullRange parser.Range + res parser.Results +} + +// tokAt returns the parser token at the given position lp +func (f *file) tokAt(lp lsp.Position) *parser.Token { + toks := f.res.Tokens + p := parser.Position{Line: int(lp.Line) + 1, Column: int(lp.Character) + 1} + i := sort.Search(len(toks), func(i int) bool { return p.LessThan(toks[i].Range.End) }) + if i == len(toks) { + return nil + } + if toks[i].Range.Contains(p) { + return toks[i] + } + return nil +} + +func (s *server) DidChangeWorkspaceFolders(ctx context.Context, p *lsp.DidChangeWorkspaceFoldersParams) error { + log.Println("server.DidChangeWorkspaceFolders()") + return nil +} +func (s *server) Initialized(ctx context.Context, p *lsp.InitializedParams) error { + log.Println("server.Initialized()") + return nil +} +func (s *server) Exit(ctx context.Context) error { + log.Println("server.Exit()") + return nil +} +func (s *server) DidChangeConfiguration(ctx context.Context, p *lsp.DidChangeConfigurationParams) error { + log.Println("server.DidChangeConfiguration()") + return nil +} +func (s *server) DidOpen(ctx context.Context, p *lsp.DidOpenTextDocumentParams) error { + log.Println("server.DidOpen()") + return s.processFile(ctx, p.TextDocument.URI, p.TextDocument.Text) +} +func (s *server) DidChange(ctx context.Context, p *lsp.DidChangeTextDocumentParams) error { + log.Println("server.DidChange()") + return s.processFile(ctx, p.TextDocument.URI, p.ContentChanges[0].Text) +} +func (s *server) DidClose(ctx context.Context, p *lsp.DidCloseTextDocumentParams) error { + log.Println("server.DidClose()") + return nil +} +func (s *server) DidSave(ctx context.Context, p *lsp.DidSaveTextDocumentParams) error { + log.Println("server.DidSave()") + return nil +} +func (s *server) WillSave(ctx context.Context, p *lsp.WillSaveTextDocumentParams) error { + log.Println("server.WillSave()") + return nil +} +func (s *server) DidChangeWatchedFiles(ctx context.Context, p *lsp.DidChangeWatchedFilesParams) error { + log.Println("server.DidChangeWatchedFiles()") + return nil +} +func (s *server) Progress(ctx context.Context, p *lsp.ProgressParams) error { + log.Println("server.Progress()") + return nil +} +func (s *server) SetTraceNotification(ctx context.Context, p *lsp.SetTraceParams) error { + log.Println("server.SetTraceNotification()") + return nil +} +func (s *server) LogTraceNotification(ctx context.Context, p *lsp.LogTraceParams) error { + log.Println("server.LogTraceNotification()") + return nil +} +func (s *server) Implementation(ctx context.Context, p *lsp.ImplementationParams) ([]lsp.Location, error) { + log.Println("server.Implementation()") + return nil, nil +} +func (s *server) TypeDefinition(ctx context.Context, p *lsp.TypeDefinitionParams) ([]lsp.Location, error) { + log.Println("server.TypeDefinition()") + return nil, nil +} +func (s *server) DocumentColor(ctx context.Context, p *lsp.DocumentColorParams) ([]lsp.ColorInformation, error) { + log.Println("server.DocumentColor()") + return nil, nil +} +func (s *server) ColorPresentation(ctx context.Context, p *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, error) { + log.Println("server.ColorPresentation()") + return nil, nil +} +func (s *server) FoldingRange(ctx context.Context, p *lsp.FoldingRangeParams) ([]lsp.FoldingRange, error) { + log.Println("server.FoldingRange()") + return nil, nil +} +func (s *server) Declaration(ctx context.Context, p *lsp.DeclarationParams) ([]lsp.DeclarationLink, error) { + log.Println("server.Declaration()") + return nil, nil +} +func (s *server) SelectionRange(ctx context.Context, p *lsp.SelectionRangeParams) ([]lsp.SelectionRange, error) { + log.Println("server.SelectionRange()") + return nil, nil +} +func (s *server) Initialize(ctx context.Context, p *lsp.ParamInitia) (*lsp.InitializeResult, error) { + log.Println("server.Initialize()") + res := lsp.InitializeResult{ + Capabilities: lsp.ServerCapabilities{ + TextDocumentSync: lsp.TextDocumentSyncOptions{ + OpenClose: true, + Change: lsp.Full, // TODO: Implement incremental + }, + HoverProvider: true, + DefinitionProvider: true, + ReferencesProvider: true, + RenameProvider: true, + DocumentFormattingProvider: true, + }, + } + return &res, nil +} +func (s *server) Shutdown(ctx context.Context) error { + log.Println("server.Shutdown()") + return nil +} +func (s *server) WillSaveWaitUntil(ctx context.Context, p *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, error) { + log.Println("server.WillSaveWaitUntil()") + return nil, nil +} +func (s *server) Completion(ctx context.Context, p *lsp.CompletionParams) (*lsp.CompletionList, error) { + log.Println("server.Completion()") + return nil, nil +} +func (s *server) Resolve(ctx context.Context, p *lsp.CompletionItem) (*lsp.CompletionItem, error) { + log.Println("server.Resolve()") + return nil, nil +} +func (s *server) Hover(ctx context.Context, p *lsp.HoverParams) (*lsp.Hover, error) { + log.Println("server.Hover()") + f := s.getFile(p.TextDocument.URI) + if f == nil { + return nil, fmt.Errorf("Unknown file") + } + + if tok := f.tokAt(p.Position); tok != nil { + sb := strings.Builder{} + switch v := f.res.Mappings[tok].(type) { + default: + sb.WriteString(fmt.Sprintf("", v)) + case *parser.Instruction: + sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Opcode.Opname)) + case *parser.Identifier: + sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Definition.Range.Text(f.res.Lines))) + case *parser.Operand: + if v.Name != "" { + sb.WriteString(strings.Trim(v.Name, `'`)) + sb.WriteString("\n\n") + } + + switch v.Kind.Category { + case schema.OperandCategoryBitEnum: + case schema.OperandCategoryValueEnum: + sb.WriteString("```\n") + sb.WriteString(strings.Trim(v.Kind.Kind, `'`)) + sb.WriteString("\n```") + case schema.OperandCategoryID: + if s := tok.Text(f.res.Lines); s != "" { + if id, ok := f.res.Identifiers[s]; ok && id.Definition != nil { + sb.WriteString("```\n") + sb.WriteString(id.Definition.Range.Text(f.res.Lines)) + sb.WriteString("\n```") + } + } + case schema.OperandCategoryLiteral: + case schema.OperandCategoryComposite: + } + case nil: + } + + if sb.Len() > 0 { + res := lsp.Hover{ + Contents: lsp.MarkupContent{ + Kind: "markdown", + Value: sb.String(), + }, + } + return &res, nil + } + } + + return nil, nil +} +func (s *server) SignatureHelp(ctx context.Context, p *lsp.SignatureHelpParams) (*lsp.SignatureHelp, error) { + log.Println("server.SignatureHelp()") + return nil, nil +} +func (s *server) Definition(ctx context.Context, p *lsp.DefinitionParams) ([]lsp.Location, error) { + log.Println("server.Definition()") + if f := s.getFile(p.TextDocument.URI); f != nil { + if tok := f.tokAt(p.Position); tok != nil { + if s := tok.Text(f.res.Lines); s != "" { + if id, ok := f.res.Identifiers[s]; ok { + loc := lsp.Location{ + URI: p.TextDocument.URI, + Range: rangeToLSP(id.Definition.Range), + } + return []lsp.Location{loc}, nil + } + } + } + } + return nil, nil +} +func (s *server) References(ctx context.Context, p *lsp.ReferenceParams) ([]lsp.Location, error) { + log.Println("server.References()") + if f := s.getFile(p.TextDocument.URI); f != nil { + if tok := f.tokAt(p.Position); tok != nil { + if s := tok.Text(f.res.Lines); s != "" { + if id, ok := f.res.Identifiers[s]; ok { + locs := make([]lsp.Location, len(id.References)) + for i, r := range id.References { + locs[i] = lsp.Location{ + URI: p.TextDocument.URI, + Range: rangeToLSP(r.Range), + } + } + return locs, nil + } + } + } + } + return nil, nil +} +func (s *server) DocumentHighlight(ctx context.Context, p *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, error) { + log.Println("server.DocumentHighlight()") + return nil, nil +} +func (s *server) DocumentSymbol(ctx context.Context, p *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, error) { + log.Println("server.DocumentSymbol()") + return nil, nil +} +func (s *server) CodeAction(ctx context.Context, p *lsp.CodeActionParams) ([]lsp.CodeAction, error) { + log.Println("server.CodeAction()") + return nil, nil +} +func (s *server) Symbol(ctx context.Context, p *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, error) { + log.Println("server.Symbol()") + return nil, nil +} +func (s *server) CodeLens(ctx context.Context, p *lsp.CodeLensParams) ([]lsp.CodeLens, error) { + log.Println("server.CodeLens()") + return nil, nil +} +func (s *server) ResolveCodeLens(ctx context.Context, p *lsp.CodeLens) (*lsp.CodeLens, error) { + log.Println("server.ResolveCodeLens()") + return nil, nil +} +func (s *server) DocumentLink(ctx context.Context, p *lsp.DocumentLinkParams) ([]lsp.DocumentLink, error) { + log.Println("server.DocumentLink()") + return nil, nil +} +func (s *server) ResolveDocumentLink(ctx context.Context, p *lsp.DocumentLink) (*lsp.DocumentLink, error) { + log.Println("server.ResolveDocumentLink()") + return nil, nil +} +func (s *server) Formatting(ctx context.Context, p *lsp.DocumentFormattingParams) ([]lsp.TextEdit, error) { + log.Println("server.Formatting()") + if f := s.getFile(p.TextDocument.URI); f != nil { + // Start by measuring the distance from the start of each line to the + // first opcode on that line. + lineInstOffsets, maxInstOffset, instOffset, curOffset := []int{}, 0, 0, -1 + for _, t := range f.res.Tokens { + curOffset++ // whitespace between tokens + switch t.Type { + case parser.Ident: + if _, isInst := schema.Opcodes[t.Text(f.res.Lines)]; isInst && instOffset == 0 { + instOffset = curOffset + continue + } + case parser.Newline: + lineInstOffsets = append(lineInstOffsets, instOffset) + if instOffset > maxInstOffset { + maxInstOffset = instOffset + } + curOffset, instOffset = -1, 0 + default: + curOffset += utf8.RuneCountInString(t.Text(f.res.Lines)) + } + } + lineInstOffsets = append(lineInstOffsets, instOffset) + + // Now rewrite each of the lines, adding padding at the start of the + // line for alignment. + sb, newline := strings.Builder{}, true + for _, t := range f.res.Tokens { + if newline { + newline = false + indent := maxInstOffset - lineInstOffsets[0] + lineInstOffsets = lineInstOffsets[1:] + switch t.Type { + case parser.Newline, parser.Comment: + default: + for s := 0; s < indent; s++ { + sb.WriteRune(' ') + } + } + } else if t.Type != parser.Newline { + sb.WriteString(" ") + } + + sb.WriteString(t.Text(f.res.Lines)) + if t.Type == parser.Newline { + newline = true + } + } + + // Every good file ends with a new line. + sb.WriteString("\n") + + return []lsp.TextEdit{ + lsp.TextEdit{ + Range: rangeToLSP(f.fullRange), + NewText: sb.String(), + }, + }, nil + } + return nil, nil +} +func (s *server) RangeFormatting(ctx context.Context, p *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, error) { + log.Println("server.RangeFormatting()") + return nil, nil +} +func (s *server) OnTypeFormatting(ctx context.Context, p *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, error) { + log.Println("server.OnTypeFormatting()") + return nil, nil +} +func (s *server) Rename(ctx context.Context, p *lsp.RenameParams) (*lsp.WorkspaceEdit, error) { + log.Println("server.Rename()") + if f := s.getFile(p.TextDocument.URI); f != nil { + if tok := f.tokAt(p.Position); tok != nil { + if s := tok.Text(f.res.Lines); s != "" { + if id, ok := f.res.Identifiers[s]; ok { + changes := make([]lsp.TextEdit, len(id.References)) + for i, r := range id.References { + changes[i].Range = rangeToLSP(r.Range) + changes[i].NewText = p.NewName + } + m := map[string][]lsp.TextEdit{} + m[p.TextDocument.URI] = changes + return &lsp.WorkspaceEdit{Changes: &m}, nil + } + } + } + } + return nil, nil +} +func (s *server) PrepareRename(ctx context.Context, p *lsp.PrepareRenameParams) (*lsp.Range, error) { + log.Println("server.PrepareRename()") + return nil, nil +} +func (s *server) ExecuteCommand(ctx context.Context, p *lsp.ExecuteCommandParams) (interface{}, error) { + log.Println("server.ExecuteCommand()") + return nil, nil +} + +func (s *server) processFile(ctx context.Context, uri, source string) error { + log.Println("server.DidOpen()") + res, err := parser.Parse(source) + if err != nil { + return err + } + fullRange := parser.Range{ + Start: parser.Position{Line: 1, Column: 1}, + End: parser.Position{Line: len(res.Lines), Column: utf8.RuneCountInString(res.Lines[len(res.Lines)-1]) + 1}, + } + + s.filesMutex.Lock() + s.files[uri] = &file{ + fullRange: fullRange, + res: res, + } + s.filesMutex.Unlock() + + dp := lsp.PublishDiagnosticsParams{URI: uri, Diagnostics: make([]lsp.Diagnostic, len(res.Diagnostics))} + for i, d := range res.Diagnostics { + dp.Diagnostics[i] = diagnosticToLSP(d) + } + s.client.PublishDiagnostics(ctx, &dp) + return nil +} + +func (s *server) getFile(uri string) *file { + s.filesMutex.Lock() + defer s.filesMutex.Unlock() + return s.files[uri] +} + +func diagnosticToLSP(d parser.Diagnostic) lsp.Diagnostic { + return lsp.Diagnostic{ + Range: rangeToLSP(d.Range), + Severity: severityToLSP(d.Severity), + Message: d.Message, + } +} + +func severityToLSP(s parser.Severity) lsp.DiagnosticSeverity { + switch s { + case parser.SeverityError: + return lsp.SeverityError + case parser.SeverityWarning: + return lsp.SeverityWarning + case parser.SeverityInformation: + return lsp.SeverityInformation + case parser.SeverityHint: + return lsp.SeverityHint + default: + log.Panicf("Invalid severity '%d'", int(s)) + return lsp.SeverityError + } +} + +func rangeToLSP(r parser.Range) lsp.Range { + return lsp.Range{ + Start: positionToLSP(r.Start), + End: positionToLSP(r.End), + } +} + +func positionToLSP(r parser.Position) lsp.Position { + return lsp.Position{ + Line: float64(r.Line - 1), + Character: float64(r.Column - 1), + } +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/LICENSE b/third_party/spirv-tools/utils/vscode/src/lsp/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/README.md b/third_party/spirv-tools/utils/vscode/src/lsp/README.md new file mode 100644 index 0000000..c78f183 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/README.md @@ -0,0 +1,5 @@ +This directory contains code forked from https://github.com/golang/tools/tree/master/internal/lsp. + +This code has been modified to remove unneeded features and dependencies. + +Submitted on behalf of a third-party: The Go Authors diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/handler.go b/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/handler.go new file mode 100644 index 0000000..20d4841 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/handler.go @@ -0,0 +1,134 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jsonrpc2 + +import ( + "context" +) + +// Handler is the interface used to hook into the message handling of an rpc +// connection. +type Handler interface { + // Deliver is invoked to handle incoming requests. + // If the request returns false from IsNotify then the Handler must eventually + // call Reply on the Conn with the supplied request. + // Handlers are called synchronously, they should pass the work off to a go + // routine if they are going to take a long time. + // If Deliver returns true all subsequent handlers will be invoked with + // delivered set to true, and should not attempt to deliver the message. + Deliver(ctx context.Context, r *Request, delivered bool) bool + + // Cancel is invoked for cancelled outgoing requests. + // It is okay to use the connection to send notifications, but the context will + // be in the cancelled state, so you must do it with the background context + // instead. + // If Cancel returns true all subsequent handlers will be invoked with + // cancelled set to true, and should not attempt to cancel the message. + Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool + + // Log is invoked for all messages flowing through a Conn. + // direction indicates if the message being received or sent + // id is the message id, if not set it was a notification + // elapsed is the time between a call being seen and the response, and is + // negative for anything that is not a response. + // method is the method name specified in the message + // payload is the parameters for a call or notification, and the result for a + // response + + // Request is called near the start of processing any request. + Request(ctx context.Context, conn *Conn, direction Direction, r *WireRequest) context.Context + // Response is called near the start of processing any response. + Response(ctx context.Context, conn *Conn, direction Direction, r *WireResponse) context.Context + // Done is called when any request is fully processed. + // For calls, this means the response has also been processed, for notifies + // this is as soon as the message has been written to the stream. + // If err is set, it implies the request failed. + Done(ctx context.Context, err error) + // Read is called with a count each time some data is read from the stream. + // The read calls are delayed until after the data has been interpreted so + // that it can be attributed to a request/response. + Read(ctx context.Context, bytes int64) context.Context + // Wrote is called each time some data is written to the stream. + Wrote(ctx context.Context, bytes int64) context.Context + // Error is called with errors that cannot be delivered through the normal + // mechanisms, for instance a failure to process a notify cannot be delivered + // back to the other party. + Error(ctx context.Context, err error) +} + +// Direction is used to indicate to a logger whether the logged message was being +// sent or received. +type Direction bool + +const ( + // Send indicates the message is outgoing. + Send = Direction(true) + // Receive indicates the message is incoming. + Receive = Direction(false) +) + +func (d Direction) String() string { + switch d { + case Send: + return "send" + case Receive: + return "receive" + default: + panic("unreachable") + } +} + +type EmptyHandler struct{} + +func (EmptyHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool { + return false +} + +func (EmptyHandler) Cancel(ctx context.Context, conn *Conn, id ID, cancelled bool) bool { + return false +} + +func (EmptyHandler) Request(ctx context.Context, conn *Conn, direction Direction, r *WireRequest) context.Context { + return ctx +} + +func (EmptyHandler) Response(ctx context.Context, conn *Conn, direction Direction, r *WireResponse) context.Context { + return ctx +} + +func (EmptyHandler) Done(ctx context.Context, err error) { +} + +func (EmptyHandler) Read(ctx context.Context, bytes int64) context.Context { + return ctx +} + +func (EmptyHandler) Wrote(ctx context.Context, bytes int64) context.Context { + return ctx +} + +func (EmptyHandler) Error(ctx context.Context, err error) {} + +type defaultHandler struct{ EmptyHandler } + +func (defaultHandler) Deliver(ctx context.Context, r *Request, delivered bool) bool { + if delivered { + return false + } + if !r.IsNotify() { + r.Reply(ctx, nil, NewErrorf(CodeMethodNotFound, "method %q not found", r.Method)) + } + return true +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go b/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go new file mode 100644 index 0000000..b8436d2 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/jsonrpc2.go @@ -0,0 +1,416 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec. +// https://www.jsonrpc.org/specification +// It is intended to be compatible with other implementations at the wire level. +package jsonrpc2 + +import ( + "context" + "encoding/json" + "fmt" + "sync" + "sync/atomic" +) + +// Conn is a JSON RPC 2 client server connection. +// Conn is bidirectional; it does not have a designated server or client end. +type Conn struct { + seq int64 // must only be accessed using atomic operations + handlers []Handler + stream Stream + err error + pendingMu sync.Mutex // protects the pending map + pending map[ID]chan *WireResponse + handlingMu sync.Mutex // protects the handling map + handling map[ID]*Request +} + +type requestState int + +const ( + requestWaiting = requestState(iota) + requestSerial + requestParallel + requestReplied + requestDone +) + +// Request is sent to a server to represent a Call or Notify operaton. +type Request struct { + conn *Conn + cancel context.CancelFunc + state requestState + nextRequest chan struct{} + + // The Wire values of the request. + WireRequest +} + +// NewErrorf builds a Error struct for the supplied message and code. +// If args is not empty, message and args will be passed to Sprintf. +func NewErrorf(code int64, format string, args ...interface{}) *Error { + return &Error{ + Code: code, + Message: fmt.Sprintf(format, args...), + } +} + +// NewConn creates a new connection object around the supplied stream. +// You must call Run for the connection to be active. +func NewConn(s Stream) *Conn { + conn := &Conn{ + handlers: []Handler{defaultHandler{}}, + stream: s, + pending: make(map[ID]chan *WireResponse), + handling: make(map[ID]*Request), + } + return conn +} + +// AddHandler adds a new handler to the set the connection will invoke. +// Handlers are invoked in the reverse order of how they were added, this +// allows the most recent addition to be the first one to attempt to handle a +// message. +func (c *Conn) AddHandler(handler Handler) { + // prepend the new handlers so we use them first + c.handlers = append([]Handler{handler}, c.handlers...) +} + +// Cancel cancels a pending Call on the server side. +// The call is identified by its id. +// JSON RPC 2 does not specify a cancel message, so cancellation support is not +// directly wired in. This method allows a higher level protocol to choose how +// to propagate the cancel. +func (c *Conn) Cancel(id ID) { + c.handlingMu.Lock() + handling, found := c.handling[id] + c.handlingMu.Unlock() + if found { + handling.cancel() + } +} + +// Notify is called to send a notification request over the connection. +// It will return as soon as the notification has been sent, as no response is +// possible. +func (c *Conn) Notify(ctx context.Context, method string, params interface{}) (err error) { + jsonParams, err := marshalToRaw(params) + if err != nil { + return fmt.Errorf("marshalling notify parameters: %v", err) + } + request := &WireRequest{ + Method: method, + Params: jsonParams, + } + data, err := json.Marshal(request) + if err != nil { + return fmt.Errorf("marshalling notify request: %v", err) + } + for _, h := range c.handlers { + ctx = h.Request(ctx, c, Send, request) + } + defer func() { + for _, h := range c.handlers { + h.Done(ctx, err) + } + }() + n, err := c.stream.Write(ctx, data) + for _, h := range c.handlers { + ctx = h.Wrote(ctx, n) + } + return err +} + +// Call sends a request over the connection and then waits for a response. +// If the response is not an error, it will be decoded into result. +// result must be of a type you an pass to json.Unmarshal. +func (c *Conn) Call(ctx context.Context, method string, params, result interface{}) (err error) { + // generate a new request identifier + id := ID{Number: atomic.AddInt64(&c.seq, 1)} + jsonParams, err := marshalToRaw(params) + if err != nil { + return fmt.Errorf("marshalling call parameters: %v", err) + } + request := &WireRequest{ + ID: &id, + Method: method, + Params: jsonParams, + } + // marshal the request now it is complete + data, err := json.Marshal(request) + if err != nil { + return fmt.Errorf("marshalling call request: %v", err) + } + for _, h := range c.handlers { + ctx = h.Request(ctx, c, Send, request) + } + // we have to add ourselves to the pending map before we send, otherwise we + // are racing the response + rchan := make(chan *WireResponse) + c.pendingMu.Lock() + c.pending[id] = rchan + c.pendingMu.Unlock() + defer func() { + // clean up the pending response handler on the way out + c.pendingMu.Lock() + delete(c.pending, id) + c.pendingMu.Unlock() + for _, h := range c.handlers { + h.Done(ctx, err) + } + }() + // now we are ready to send + n, err := c.stream.Write(ctx, data) + for _, h := range c.handlers { + ctx = h.Wrote(ctx, n) + } + if err != nil { + // sending failed, we will never get a response, so don't leave it pending + return err + } + // now wait for the response + select { + case response := <-rchan: + for _, h := range c.handlers { + ctx = h.Response(ctx, c, Receive, response) + } + // is it an error response? + if response.Error != nil { + return response.Error + } + if result == nil || response.Result == nil { + return nil + } + if err := json.Unmarshal(*response.Result, result); err != nil { + return fmt.Errorf("unmarshalling result: %v", err) + } + return nil + case <-ctx.Done(): + // allow the handler to propagate the cancel + cancelled := false + for _, h := range c.handlers { + if h.Cancel(ctx, c, id, cancelled) { + cancelled = true + } + } + return ctx.Err() + } +} + +// Conn returns the connection that created this request. +func (r *Request) Conn() *Conn { return r.conn } + +// IsNotify returns true if this request is a notification. +func (r *Request) IsNotify() bool { + return r.ID == nil +} + +// Parallel indicates that the system is now allowed to process other requests +// in parallel with this one. +// It is safe to call any number of times, but must only be called from the +// request handling go routine. +// It is implied by both reply and by the handler returning. +func (r *Request) Parallel() { + if r.state >= requestParallel { + return + } + r.state = requestParallel + close(r.nextRequest) +} + +// Reply sends a reply to the given request. +// It is an error to call this if request was not a call. +// You must call this exactly once for any given request. +// It should only be called from the handler go routine. +// If err is set then result will be ignored. +// If the request has not yet dropped into parallel mode +// it will be before this function returns. +func (r *Request) Reply(ctx context.Context, result interface{}, err error) error { + if r.state >= requestReplied { + return fmt.Errorf("reply invoked more than once") + } + if r.IsNotify() { + return fmt.Errorf("reply not invoked with a valid call") + } + // reply ends the handling phase of a call, so if we are not yet + // parallel we should be now. The go routine is allowed to continue + // to do work after replying, which is why it is important to unlock + // the rpc system at this point. + r.Parallel() + r.state = requestReplied + + var raw *json.RawMessage + if err == nil { + raw, err = marshalToRaw(result) + } + response := &WireResponse{ + Result: raw, + ID: r.ID, + } + if err != nil { + if callErr, ok := err.(*Error); ok { + response.Error = callErr + } else { + response.Error = NewErrorf(0, "%s", err) + } + } + data, err := json.Marshal(response) + if err != nil { + return err + } + for _, h := range r.conn.handlers { + ctx = h.Response(ctx, r.conn, Send, response) + } + n, err := r.conn.stream.Write(ctx, data) + for _, h := range r.conn.handlers { + ctx = h.Wrote(ctx, n) + } + + if err != nil { + // TODO(iancottrell): if a stream write fails, we really need to shut down + // the whole stream + return err + } + return nil +} + +func (c *Conn) setHandling(r *Request, active bool) { + if r.ID == nil { + return + } + r.conn.handlingMu.Lock() + defer r.conn.handlingMu.Unlock() + if active { + r.conn.handling[*r.ID] = r + } else { + delete(r.conn.handling, *r.ID) + } +} + +// combined has all the fields of both Request and Response. +// We can decode this and then work out which it is. +type combined struct { + VersionTag VersionTag `json:"jsonrpc"` + ID *ID `json:"id,omitempty"` + Method string `json:"method"` + Params *json.RawMessage `json:"params,omitempty"` + Result *json.RawMessage `json:"result,omitempty"` + Error *Error `json:"error,omitempty"` +} + +// Run blocks until the connection is terminated, and returns any error that +// caused the termination. +// It must be called exactly once for each Conn. +// It returns only when the reader is closed or there is an error in the stream. +func (c *Conn) Run(runCtx context.Context) error { + // we need to make the next request "lock" in an unlocked state to allow + // the first incoming request to proceed. All later requests are unlocked + // by the preceding request going to parallel mode. + nextRequest := make(chan struct{}) + close(nextRequest) + for { + // get the data for a message + data, n, err := c.stream.Read(runCtx) + if err != nil { + // the stream failed, we cannot continue + return err + } + // read a combined message + msg := &combined{} + if err := json.Unmarshal(data, msg); err != nil { + // a badly formed message arrived, log it and continue + // we trust the stream to have isolated the error to just this message + for _, h := range c.handlers { + h.Error(runCtx, fmt.Errorf("unmarshal failed: %v", err)) + } + continue + } + // work out which kind of message we have + switch { + case msg.Method != "": + // if method is set it must be a request + reqCtx, cancelReq := context.WithCancel(runCtx) + thisRequest := nextRequest + nextRequest = make(chan struct{}) + req := &Request{ + conn: c, + cancel: cancelReq, + nextRequest: nextRequest, + WireRequest: WireRequest{ + VersionTag: msg.VersionTag, + Method: msg.Method, + Params: msg.Params, + ID: msg.ID, + }, + } + for _, h := range c.handlers { + reqCtx = h.Request(reqCtx, c, Receive, &req.WireRequest) + reqCtx = h.Read(reqCtx, n) + } + c.setHandling(req, true) + go func() { + <-thisRequest + req.state = requestSerial + defer func() { + c.setHandling(req, false) + if !req.IsNotify() && req.state < requestReplied { + req.Reply(reqCtx, nil, NewErrorf(CodeInternalError, "method %q did not reply", req.Method)) + } + req.Parallel() + for _, h := range c.handlers { + h.Done(reqCtx, err) + } + cancelReq() + }() + delivered := false + for _, h := range c.handlers { + if h.Deliver(reqCtx, req, delivered) { + delivered = true + } + } + }() + case msg.ID != nil: + // we have a response, get the pending entry from the map + c.pendingMu.Lock() + rchan := c.pending[*msg.ID] + if rchan != nil { + delete(c.pending, *msg.ID) + } + c.pendingMu.Unlock() + // and send the reply to the channel + response := &WireResponse{ + Result: msg.Result, + Error: msg.Error, + ID: msg.ID, + } + rchan <- response + close(rchan) + default: + for _, h := range c.handlers { + h.Error(runCtx, fmt.Errorf("message not a call, notify or response, ignoring")) + } + } + } +} + +func marshalToRaw(obj interface{}) (*json.RawMessage, error) { + data, err := json.Marshal(obj) + if err != nil { + return nil, err + } + raw := json.RawMessage(data) + return &raw, nil +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/stream.go b/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/stream.go new file mode 100644 index 0000000..2c6ad6d --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/stream.go @@ -0,0 +1,160 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jsonrpc2 + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "io" + "strconv" + "strings" + "sync" +) + +// Stream abstracts the transport mechanics from the JSON RPC protocol. +// A Conn reads and writes messages using the stream it was provided on +// construction, and assumes that each call to Read or Write fully transfers +// a single message, or returns an error. +type Stream interface { + // Read gets the next message from the stream. + // It is never called concurrently. + Read(context.Context) ([]byte, int64, error) + // Write sends a message to the stream. + // It must be safe for concurrent use. + Write(context.Context, []byte) (int64, error) +} + +// NewStream returns a Stream built on top of an io.Reader and io.Writer +// The messages are sent with no wrapping, and rely on json decode consistency +// to determine message boundaries. +func NewStream(in io.Reader, out io.Writer) Stream { + return &plainStream{ + in: json.NewDecoder(in), + out: out, + } +} + +type plainStream struct { + in *json.Decoder + outMu sync.Mutex + out io.Writer +} + +func (s *plainStream) Read(ctx context.Context) ([]byte, int64, error) { + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + default: + } + var raw json.RawMessage + if err := s.in.Decode(&raw); err != nil { + return nil, 0, err + } + return raw, int64(len(raw)), nil +} + +func (s *plainStream) Write(ctx context.Context, data []byte) (int64, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + s.outMu.Lock() + n, err := s.out.Write(data) + s.outMu.Unlock() + return int64(n), err +} + +// NewHeaderStream returns a Stream built on top of an io.Reader and io.Writer +// The messages are sent with HTTP content length and MIME type headers. +// This is the format used by LSP and others. +func NewHeaderStream(in io.Reader, out io.Writer) Stream { + return &headerStream{ + in: bufio.NewReader(in), + out: out, + } +} + +type headerStream struct { + in *bufio.Reader + outMu sync.Mutex + out io.Writer +} + +func (s *headerStream) Read(ctx context.Context) ([]byte, int64, error) { + select { + case <-ctx.Done(): + return nil, 0, ctx.Err() + default: + } + var total, length int64 + // read the header, stop on the first empty line + for { + line, err := s.in.ReadString('\n') + total += int64(len(line)) + if err != nil { + return nil, total, fmt.Errorf("failed reading header line %q", err) + } + line = strings.TrimSpace(line) + // check we have a header line + if line == "" { + break + } + colon := strings.IndexRune(line, ':') + if colon < 0 { + return nil, total, fmt.Errorf("invalid header line %q", line) + } + name, value := line[:colon], strings.TrimSpace(line[colon+1:]) + switch name { + case "Content-Length": + if length, err = strconv.ParseInt(value, 10, 32); err != nil { + return nil, total, fmt.Errorf("failed parsing Content-Length: %v", value) + } + if length <= 0 { + return nil, total, fmt.Errorf("invalid Content-Length: %v", length) + } + default: + // ignoring unknown headers + } + } + if length == 0 { + return nil, total, fmt.Errorf("missing Content-Length header") + } + data := make([]byte, length) + if _, err := io.ReadFull(s.in, data); err != nil { + return nil, total, err + } + total += length + return data, total, nil +} + +func (s *headerStream) Write(ctx context.Context, data []byte) (int64, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + default: + } + s.outMu.Lock() + defer s.outMu.Unlock() + n, err := fmt.Fprintf(s.out, "Content-Length: %v\r\n\r\n", len(data)) + total := int64(n) + if err == nil { + n, err = s.out.Write(data) + total += int64(n) + } + return total, err +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/wire.go b/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/wire.go new file mode 100644 index 0000000..3e31c34 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/jsonrpc2/wire.go @@ -0,0 +1,148 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package jsonrpc2 + +import ( + "encoding/json" + "fmt" + "strconv" +) + +// this file contains the go forms of the wire specification +// see http://www.jsonrpc.org/specification for details + +const ( + // CodeUnknownError should be used for all non coded errors. + CodeUnknownError = -32001 + // CodeParseError is used when invalid JSON was received by the server. + CodeParseError = -32700 + //CodeInvalidRequest is used when the JSON sent is not a valid Request object. + CodeInvalidRequest = -32600 + // CodeMethodNotFound should be returned by the handler when the method does + // not exist / is not available. + CodeMethodNotFound = -32601 + // CodeInvalidParams should be returned by the handler when method + // parameter(s) were invalid. + CodeInvalidParams = -32602 + // CodeInternalError is not currently returned but defined for completeness. + CodeInternalError = -32603 + + //CodeServerOverloaded is returned when a message was refused due to a + //server being temporarily unable to accept any new messages. + CodeServerOverloaded = -32000 +) + +// WireRequest is sent to a server to represent a Call or Notify operaton. +type WireRequest struct { + // VersionTag is always encoded as the string "2.0" + VersionTag VersionTag `json:"jsonrpc"` + // Method is a string containing the method name to invoke. + Method string `json:"method"` + // Params is either a struct or an array with the parameters of the method. + Params *json.RawMessage `json:"params,omitempty"` + // The id of this request, used to tie the Response back to the request. + // Will be either a string or a number. If not set, the Request is a notify, + // and no response is possible. + ID *ID `json:"id,omitempty"` +} + +// WireResponse is a reply to a Request. +// It will always have the ID field set to tie it back to a request, and will +// have either the Result or Error fields set depending on whether it is a +// success or failure response. +type WireResponse struct { + // VersionTag is always encoded as the string "2.0" + VersionTag VersionTag `json:"jsonrpc"` + // Result is the response value, and is required on success. + Result *json.RawMessage `json:"result,omitempty"` + // Error is a structured error response if the call fails. + Error *Error `json:"error,omitempty"` + // ID must be set and is the identifier of the Request this is a response to. + ID *ID `json:"id,omitempty"` +} + +// Error represents a structured error in a Response. +type Error struct { + // Code is an error code indicating the type of failure. + Code int64 `json:"code"` + // Message is a short description of the error. + Message string `json:"message"` + // Data is optional structured data containing additional information about the error. + Data *json.RawMessage `json:"data"` +} + +// VersionTag is a special 0 sized struct that encodes as the jsonrpc version +// tag. +// It will fail during decode if it is not the correct version tag in the +// stream. +type VersionTag struct{} + +// ID is a Request identifier. +// Only one of either the Name or Number members will be set, using the +// number form if the Name is the empty string. +type ID struct { + Name string + Number int64 +} + +func (err *Error) Error() string { + if err == nil { + return "" + } + return err.Message +} + +func (VersionTag) MarshalJSON() ([]byte, error) { + return json.Marshal("2.0") +} + +func (VersionTag) UnmarshalJSON(data []byte) error { + version := "" + if err := json.Unmarshal(data, &version); err != nil { + return err + } + if version != "2.0" { + return fmt.Errorf("Invalid RPC version %v", version) + } + return nil +} + +// String returns a string representation of the ID. +// The representation is non ambiguous, string forms are quoted, number forms +// are preceded by a # +func (id *ID) String() string { + if id == nil { + return "" + } + if id.Name != "" { + return strconv.Quote(id.Name) + } + return "#" + strconv.FormatInt(id.Number, 10) +} + +func (id *ID) MarshalJSON() ([]byte, error) { + if id.Name != "" { + return json.Marshal(id.Name) + } + return json.Marshal(id.Number) +} + +func (id *ID) UnmarshalJSON(data []byte) error { + *id = ID{} + if err := json.Unmarshal(data, &id.Number); err == nil { + return nil + } + return json.Unmarshal(data, &id.Name) +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/context.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/context.go new file mode 100644 index 0000000..7833d40 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/context.go @@ -0,0 +1,29 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" +) + +type contextKey int + +const ( + clientKey = contextKey(iota) +) + +func WithClient(ctx context.Context, client Client) context.Context { + return context.WithValue(ctx, clientKey, client) +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/doc.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/doc.go new file mode 100644 index 0000000..3c9198d --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/doc.go @@ -0,0 +1,26 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package protocol contains the structs that map directly to the wire format +// of the "Language Server Protocol". +// +// It is a literal transcription, with unmodified comments, and only the changes +// required to make it go code. +// Names are uppercased to export them. +// All fields have JSON tags added to correct the names. +// Fields marked with a ? are also marked as "omitempty" +// Fields that are "|| null" are made pointers +// Fields that are string or number are left as string +// Fields that are type "number" are made float64 +package protocol diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/enums.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/enums.go new file mode 100644 index 0000000..db76ca3 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/enums.go @@ -0,0 +1,256 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "fmt" +) + +var ( + namesTextDocumentSyncKind [int(Incremental) + 1]string + namesInitializeError [int(UnknownProtocolVersion) + 1]string + namesMessageType [int(Log) + 1]string + namesFileChangeType [int(Deleted) + 1]string + namesWatchKind [int(WatchDelete) + 1]string + namesCompletionTriggerKind [int(TriggerForIncompleteCompletions) + 1]string + namesDiagnosticSeverity [int(SeverityHint) + 1]string + namesDiagnosticTag [int(Unnecessary) + 1]string + namesCompletionItemKind [int(TypeParameterCompletion) + 1]string + namesInsertTextFormat [int(SnippetTextFormat) + 1]string + namesDocumentHighlightKind [int(Write) + 1]string + namesSymbolKind [int(TypeParameter) + 1]string + namesTextDocumentSaveReason [int(FocusOut) + 1]string +) + +func init() { + namesTextDocumentSyncKind[int(None)] = "None" + namesTextDocumentSyncKind[int(Full)] = "Full" + namesTextDocumentSyncKind[int(Incremental)] = "Incremental" + + namesInitializeError[int(UnknownProtocolVersion)] = "UnknownProtocolVersion" + + namesMessageType[int(Error)] = "Error" + namesMessageType[int(Warning)] = "Warning" + namesMessageType[int(Info)] = "Info" + namesMessageType[int(Log)] = "Log" + + namesFileChangeType[int(Created)] = "Created" + namesFileChangeType[int(Changed)] = "Changed" + namesFileChangeType[int(Deleted)] = "Deleted" + + namesWatchKind[int(WatchCreate)] = "WatchCreate" + namesWatchKind[int(WatchChange)] = "WatchChange" + namesWatchKind[int(WatchDelete)] = "WatchDelete" + + namesCompletionTriggerKind[int(Invoked)] = "Invoked" + namesCompletionTriggerKind[int(TriggerCharacter)] = "TriggerCharacter" + namesCompletionTriggerKind[int(TriggerForIncompleteCompletions)] = "TriggerForIncompleteCompletions" + + namesDiagnosticSeverity[int(SeverityError)] = "Error" + namesDiagnosticSeverity[int(SeverityWarning)] = "Warning" + namesDiagnosticSeverity[int(SeverityInformation)] = "Information" + namesDiagnosticSeverity[int(SeverityHint)] = "Hint" + + namesDiagnosticTag[int(Unnecessary)] = "Unnecessary" + + namesCompletionItemKind[int(TextCompletion)] = "text" + namesCompletionItemKind[int(MethodCompletion)] = "method" + namesCompletionItemKind[int(FunctionCompletion)] = "func" + namesCompletionItemKind[int(ConstructorCompletion)] = "constructor" + namesCompletionItemKind[int(FieldCompletion)] = "field" + namesCompletionItemKind[int(VariableCompletion)] = "var" + namesCompletionItemKind[int(ClassCompletion)] = "type" + namesCompletionItemKind[int(InterfaceCompletion)] = "interface" + namesCompletionItemKind[int(ModuleCompletion)] = "package" + namesCompletionItemKind[int(PropertyCompletion)] = "property" + namesCompletionItemKind[int(UnitCompletion)] = "unit" + namesCompletionItemKind[int(ValueCompletion)] = "value" + namesCompletionItemKind[int(EnumCompletion)] = "enum" + namesCompletionItemKind[int(KeywordCompletion)] = "keyword" + namesCompletionItemKind[int(SnippetCompletion)] = "snippet" + namesCompletionItemKind[int(ColorCompletion)] = "color" + namesCompletionItemKind[int(FileCompletion)] = "file" + namesCompletionItemKind[int(ReferenceCompletion)] = "reference" + namesCompletionItemKind[int(FolderCompletion)] = "folder" + namesCompletionItemKind[int(EnumMemberCompletion)] = "enumMember" + namesCompletionItemKind[int(ConstantCompletion)] = "const" + namesCompletionItemKind[int(StructCompletion)] = "struct" + namesCompletionItemKind[int(EventCompletion)] = "event" + namesCompletionItemKind[int(OperatorCompletion)] = "operator" + namesCompletionItemKind[int(TypeParameterCompletion)] = "typeParam" + + namesInsertTextFormat[int(PlainTextTextFormat)] = "PlainText" + namesInsertTextFormat[int(SnippetTextFormat)] = "Snippet" + + namesDocumentHighlightKind[int(Text)] = "Text" + namesDocumentHighlightKind[int(Read)] = "Read" + namesDocumentHighlightKind[int(Write)] = "Write" + + namesSymbolKind[int(File)] = "File" + namesSymbolKind[int(Module)] = "Module" + namesSymbolKind[int(Namespace)] = "Namespace" + namesSymbolKind[int(Package)] = "Package" + namesSymbolKind[int(Class)] = "Class" + namesSymbolKind[int(Method)] = "Method" + namesSymbolKind[int(Property)] = "Property" + namesSymbolKind[int(Field)] = "Field" + namesSymbolKind[int(Constructor)] = "Constructor" + namesSymbolKind[int(Enum)] = "Enum" + namesSymbolKind[int(Interface)] = "Interface" + namesSymbolKind[int(Function)] = "Function" + namesSymbolKind[int(Variable)] = "Variable" + namesSymbolKind[int(Constant)] = "Constant" + namesSymbolKind[int(String)] = "String" + namesSymbolKind[int(Number)] = "Number" + namesSymbolKind[int(Boolean)] = "Boolean" + namesSymbolKind[int(Array)] = "Array" + namesSymbolKind[int(Object)] = "Object" + namesSymbolKind[int(Key)] = "Key" + namesSymbolKind[int(Null)] = "Null" + namesSymbolKind[int(EnumMember)] = "EnumMember" + namesSymbolKind[int(Struct)] = "Struct" + namesSymbolKind[int(Event)] = "Event" + namesSymbolKind[int(Operator)] = "Operator" + namesSymbolKind[int(TypeParameter)] = "TypeParameter" + + namesTextDocumentSaveReason[int(Manual)] = "Manual" + namesTextDocumentSaveReason[int(AfterDelay)] = "AfterDelay" + namesTextDocumentSaveReason[int(FocusOut)] = "FocusOut" +} + +func formatEnum(f fmt.State, c rune, i int, names []string, unknown string) { + s := "" + if i >= 0 && i < len(names) { + s = names[i] + } + if s != "" { + fmt.Fprint(f, s) + } else { + fmt.Fprintf(f, "%s(%d)", unknown, i) + } +} + +func parseEnum(s string, names []string) int { + for i, name := range names { + if s == name { + return i + } + } + return 0 +} + +func (e TextDocumentSyncKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesTextDocumentSyncKind[:], "TextDocumentSyncKind") +} + +func ParseTextDocumentSyncKind(s string) TextDocumentSyncKind { + return TextDocumentSyncKind(parseEnum(s, namesTextDocumentSyncKind[:])) +} + +func (e InitializeError) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesInitializeError[:], "InitializeError") +} + +func ParseInitializeError(s string) InitializeError { + return InitializeError(parseEnum(s, namesInitializeError[:])) +} + +func (e MessageType) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesMessageType[:], "MessageType") +} + +func ParseMessageType(s string) MessageType { + return MessageType(parseEnum(s, namesMessageType[:])) +} + +func (e FileChangeType) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesFileChangeType[:], "FileChangeType") +} + +func ParseFileChangeType(s string) FileChangeType { + return FileChangeType(parseEnum(s, namesFileChangeType[:])) +} + +func (e WatchKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesWatchKind[:], "WatchKind") +} + +func ParseWatchKind(s string) WatchKind { + return WatchKind(parseEnum(s, namesWatchKind[:])) +} + +func (e CompletionTriggerKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesCompletionTriggerKind[:], "CompletionTriggerKind") +} + +func ParseCompletionTriggerKind(s string) CompletionTriggerKind { + return CompletionTriggerKind(parseEnum(s, namesCompletionTriggerKind[:])) +} + +func (e DiagnosticSeverity) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDiagnosticSeverity[:], "DiagnosticSeverity") +} + +func ParseDiagnosticSeverity(s string) DiagnosticSeverity { + return DiagnosticSeverity(parseEnum(s, namesDiagnosticSeverity[:])) +} + +func (e DiagnosticTag) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDiagnosticTag[:], "DiagnosticTag") +} + +func ParseDiagnosticTag(s string) DiagnosticTag { + return DiagnosticTag(parseEnum(s, namesDiagnosticTag[:])) +} + +func (e CompletionItemKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesCompletionItemKind[:], "CompletionItemKind") +} + +func ParseCompletionItemKind(s string) CompletionItemKind { + return CompletionItemKind(parseEnum(s, namesCompletionItemKind[:])) +} + +func (e InsertTextFormat) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesInsertTextFormat[:], "InsertTextFormat") +} + +func ParseInsertTextFormat(s string) InsertTextFormat { + return InsertTextFormat(parseEnum(s, namesInsertTextFormat[:])) +} + +func (e DocumentHighlightKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesDocumentHighlightKind[:], "DocumentHighlightKind") +} + +func ParseDocumentHighlightKind(s string) DocumentHighlightKind { + return DocumentHighlightKind(parseEnum(s, namesDocumentHighlightKind[:])) +} + +func (e SymbolKind) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesSymbolKind[:], "SymbolKind") +} + +func ParseSymbolKind(s string) SymbolKind { + return SymbolKind(parseEnum(s, namesSymbolKind[:])) +} + +func (e TextDocumentSaveReason) Format(f fmt.State, c rune) { + formatEnum(f, c, int(e), namesTextDocumentSaveReason[:], "TextDocumentSaveReason") +} + +func ParseTextDocumentSaveReason(s string) TextDocumentSaveReason { + return TextDocumentSaveReason(parseEnum(s, namesTextDocumentSaveReason[:])) +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/log.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/log.go new file mode 100644 index 0000000..f245881 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/log.go @@ -0,0 +1,258 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" + "encoding/json" + "fmt" + "io" + "strings" + "sync" + "time" + + "../jsonrpc2" +) + +type loggingStream struct { + stream jsonrpc2.Stream + log io.Writer +} + +// LoggingStream returns a stream that does LSP protocol logging too +func LoggingStream(str jsonrpc2.Stream, w io.Writer) jsonrpc2.Stream { + return &loggingStream{str, w} +} + +func (s *loggingStream) Read(ctx context.Context) ([]byte, int64, error) { + data, count, err := s.stream.Read(ctx) + if err == nil { + logIn(s.log, data) + } + return data, count, err +} + +func (s *loggingStream) Write(ctx context.Context, data []byte) (int64, error) { + logOut(s.log, data) + count, err := s.stream.Write(ctx, data) + return count, err +} + +// Combined has all the fields of both Request and Response. +// We can decode this and then work out which it is. +type Combined struct { + VersionTag jsonrpc2.VersionTag `json:"jsonrpc"` + ID *jsonrpc2.ID `json:"id,omitempty"` + Method string `json:"method"` + Params *json.RawMessage `json:"params,omitempty"` + Result *json.RawMessage `json:"result,omitempty"` + Error *jsonrpc2.Error `json:"error,omitempty"` +} + +type req struct { + method string + start time.Time +} + +type mapped struct { + mu sync.Mutex + clientCalls map[string]req + serverCalls map[string]req +} + +var maps = &mapped{ + sync.Mutex{}, + make(map[string]req), + make(map[string]req), +} + +// these 4 methods are each used exactly once, but it seemed +// better to have the encapsulation rather than ad hoc mutex +// code in 4 places +func (m *mapped) client(id string, del bool) req { + m.mu.Lock() + defer m.mu.Unlock() + v := m.clientCalls[id] + if del { + delete(m.clientCalls, id) + } + return v +} + +func (m *mapped) server(id string, del bool) req { + m.mu.Lock() + defer m.mu.Unlock() + v := m.serverCalls[id] + if del { + delete(m.serverCalls, id) + } + return v +} + +func (m *mapped) setClient(id string, r req) { + m.mu.Lock() + defer m.mu.Unlock() + m.clientCalls[id] = r +} + +func (m *mapped) setServer(id string, r req) { + m.mu.Lock() + defer m.mu.Unlock() + m.serverCalls[id] = r +} + +const eor = "\r\n\r\n\r\n" + +func strID(x *jsonrpc2.ID) string { + if x == nil { + // should never happen, but we need a number + return "999999999" + } + if x.Name != "" { + return x.Name + } + return fmt.Sprintf("%d", x.Number) +} + +func logCommon(outfd io.Writer, data []byte) (*Combined, time.Time, string) { + if outfd == nil { + return nil, time.Time{}, "" + } + var v Combined + err := json.Unmarshal(data, &v) + if err != nil { + fmt.Fprintf(outfd, "Unmarshal %v\n", err) + panic(err) // do better + } + tm := time.Now() + tmfmt := tm.Format("15:04:05.000 PM") + return &v, tm, tmfmt +} + +// logOut and logIn could be combined. "received"<->"Sending", serverCalls<->clientCalls +// but it wouldn't be a lot shorter or clearer and "shutdown" is a special case + +// Writing a message to the client, log it +func logOut(outfd io.Writer, data []byte) { + v, tm, tmfmt := logCommon(outfd, data) + if v == nil { + return + } + if v.Error != nil { + id := strID(v.ID) + fmt.Fprintf(outfd, "[Error - %s] Received #%s %s%s", tmfmt, id, v.Error, eor) + return + } + buf := strings.Builder{} + id := strID(v.ID) + fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning + if v.ID != nil && v.Method != "" && v.Params != nil { + fmt.Fprintf(&buf, "Received request '%s - (%s)'.\n", v.Method, id) + fmt.Fprintf(&buf, "Params: %s%s", *v.Params, eor) + maps.setServer(id, req{method: v.Method, start: tm}) + } else if v.ID != nil && v.Method == "" && v.Params == nil { + cc := maps.client(id, true) + elapsed := tm.Sub(cc.start) + fmt.Fprintf(&buf, "Received response '%s - (%s)' in %dms.\n", + cc.method, id, elapsed/time.Millisecond) + if v.Result == nil { + fmt.Fprintf(&buf, "Result: {}%s", eor) + } else { + fmt.Fprintf(&buf, "Result: %s%s", string(*v.Result), eor) + } + } else if v.ID == nil && v.Method != "" && v.Params != nil { + p := "null" + if v.Params != nil { + p = string(*v.Params) + } + fmt.Fprintf(&buf, "Received notification '%s'.\n", v.Method) + fmt.Fprintf(&buf, "Params: %s%s", p, eor) + } else { // for completeness, as it should never happen + buf = strings.Builder{} // undo common Trace + fmt.Fprintf(&buf, "[Error - %s] on write ID?%v method:%q Params:%v Result:%v Error:%v%s", + tmfmt, v.ID != nil, v.Method, v.Params != nil, + v.Result != nil, v.Error != nil, eor) + p := "null" + if v.Params != nil { + p = string(*v.Params) + } + r := "null" + if v.Result != nil { + r = string(*v.Result) + } + fmt.Fprintf(&buf, "%s\n%s\n%s%s", p, r, v.Error, eor) + } + outfd.Write([]byte(buf.String())) +} + +// Got a message from the client, log it +func logIn(outfd io.Writer, data []byte) { + v, tm, tmfmt := logCommon(outfd, data) + if v == nil { + return + } + // ID Method Params => Sending request + // ID !Method Result(might be null, but !Params) => Sending response (could we get an Error?) + // !ID Method Params => Sending notification + if v.Error != nil { // does this ever happen? + id := strID(v.ID) + fmt.Fprintf(outfd, "[Error - %s] Sent #%s %s%s", tmfmt, id, v.Error, eor) + return + } + buf := strings.Builder{} + id := strID(v.ID) + fmt.Fprintf(&buf, "[Trace - %s] ", tmfmt) // common beginning + if v.ID != nil && v.Method != "" && (v.Params != nil || v.Method == "shutdown") { + fmt.Fprintf(&buf, "Sending request '%s - (%s)'.\n", v.Method, id) + x := "{}" + if v.Params != nil { + x = string(*v.Params) + } + fmt.Fprintf(&buf, "Params: %s%s", x, eor) + maps.setClient(id, req{method: v.Method, start: tm}) + } else if v.ID != nil && v.Method == "" && v.Params == nil { + sc := maps.server(id, true) + elapsed := tm.Sub(sc.start) + fmt.Fprintf(&buf, "Sending response '%s - (%s)' took %dms.\n", + sc.method, id, elapsed/time.Millisecond) + if v.Result == nil { + fmt.Fprintf(&buf, "Result: {}%s", eor) + } else { + fmt.Fprintf(&buf, "Result: %s%s", string(*v.Result), eor) + } + } else if v.ID == nil && v.Method != "" { + p := "null" + if v.Params != nil { + p = string(*v.Params) + } + fmt.Fprintf(&buf, "Sending notification '%s'.\n", v.Method) + fmt.Fprintf(&buf, "Params: %s%s", p, eor) + } else { // for completeness, as it should never happen + buf = strings.Builder{} // undo common Trace + fmt.Fprintf(&buf, "[Error - %s] on read ID?%v method:%q Params:%v Result:%v Error:%v%s", + tmfmt, v.ID != nil, v.Method, v.Params != nil, + v.Result != nil, v.Error != nil, eor) + p := "null" + if v.Params != nil { + p = string(*v.Params) + } + r := "null" + if v.Result != nil { + r = string(*v.Result) + } + fmt.Fprintf(&buf, "%s\n%s\n%s%s", p, r, v.Error, eor) + } + outfd.Write([]byte(buf.String())) +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/protocol.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/protocol.go new file mode 100644 index 0000000..e396c83 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/protocol.go @@ -0,0 +1,86 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" + "encoding/json" + "log" + + "../jsonrpc2" +) + +const ( + // RequestCancelledError should be used when a request is cancelled early. + RequestCancelledError = -32800 +) + +type DocumentUri = string + +type canceller struct{ jsonrpc2.EmptyHandler } + +type clientHandler struct { + canceller + client Client +} + +type serverHandler struct { + canceller + server Server +} + +func (canceller) Request(ctx context.Context, conn *jsonrpc2.Conn, direction jsonrpc2.Direction, r *jsonrpc2.WireRequest) context.Context { + if direction == jsonrpc2.Receive && r.Method == "$/cancelRequest" { + var params CancelParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + log.Printf("%v", err) + } else { + conn.Cancel(params.ID) + } + } + return ctx +} + +func (canceller) Cancel(ctx context.Context, conn *jsonrpc2.Conn, id jsonrpc2.ID, cancelled bool) bool { + if cancelled { + return false + } + conn.Notify(ctx, "$/cancelRequest", &CancelParams{ID: id}) + return true +} + +func NewClient(ctx context.Context, stream jsonrpc2.Stream, client Client) (context.Context, *jsonrpc2.Conn, Server) { + ctx = WithClient(ctx, client) + conn := jsonrpc2.NewConn(stream) + conn.AddHandler(&clientHandler{client: client}) + return ctx, conn, &serverDispatcher{Conn: conn} +} + +func NewServer(ctx context.Context, stream jsonrpc2.Stream, server Server) (context.Context, *jsonrpc2.Conn, Client) { + conn := jsonrpc2.NewConn(stream) + client := &clientDispatcher{Conn: conn} + ctx = WithClient(ctx, client) + conn.AddHandler(&serverHandler{server: server}) + return ctx, conn, client +} + +func sendParseError(ctx context.Context, req *jsonrpc2.Request, err error) { + if _, ok := err.(*jsonrpc2.Error); !ok { + err = jsonrpc2.NewErrorf(jsonrpc2.CodeParseError, "%v", err) + } + if err := req.Reply(ctx, nil, err); err != nil { + log.Printf("%v", err) + } +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/span.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/span.go new file mode 100644 index 0000000..33cc2a6 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/span.go @@ -0,0 +1,137 @@ +// Copyright 2018 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// this file contains protocol<->span converters + +package protocol + +import ( + "fmt" + + "../span" + errors "golang.org/x/xerrors" +) + +type ColumnMapper struct { + URI span.URI + Converter *span.TokenConverter + Content []byte +} + +func NewURI(uri span.URI) string { + return string(uri) +} + +func (m *ColumnMapper) Location(s span.Span) (Location, error) { + rng, err := m.Range(s) + if err != nil { + return Location{}, err + } + return Location{URI: NewURI(s.URI()), Range: rng}, nil +} + +func (m *ColumnMapper) Range(s span.Span) (Range, error) { + if span.CompareURI(m.URI, s.URI()) != 0 { + return Range{}, errors.Errorf("column mapper is for file %q instead of %q", m.URI, s.URI()) + } + s, err := s.WithAll(m.Converter) + if err != nil { + return Range{}, err + } + start, err := m.Position(s.Start()) + if err != nil { + return Range{}, err + } + end, err := m.Position(s.End()) + if err != nil { + return Range{}, err + } + return Range{Start: start, End: end}, nil +} + +func (m *ColumnMapper) Position(p span.Point) (Position, error) { + chr, err := span.ToUTF16Column(p, m.Content) + if err != nil { + return Position{}, err + } + return Position{ + Line: float64(p.Line() - 1), + Character: float64(chr - 1), + }, nil +} + +func (m *ColumnMapper) Span(l Location) (span.Span, error) { + return m.RangeSpan(l.Range) +} + +func (m *ColumnMapper) RangeSpan(r Range) (span.Span, error) { + start, err := m.Point(r.Start) + if err != nil { + return span.Span{}, err + } + end, err := m.Point(r.End) + if err != nil { + return span.Span{}, err + } + return span.New(m.URI, start, end).WithAll(m.Converter) +} + +func (m *ColumnMapper) PointSpan(p Position) (span.Span, error) { + start, err := m.Point(p) + if err != nil { + return span.Span{}, err + } + return span.New(m.URI, start, start).WithAll(m.Converter) +} + +func (m *ColumnMapper) Point(p Position) (span.Point, error) { + line := int(p.Line) + 1 + offset, err := m.Converter.ToOffset(line, 1) + if err != nil { + return span.Point{}, err + } + lineStart := span.NewPoint(line, 1, offset) + return span.FromUTF16Column(lineStart, int(p.Character)+1, m.Content) +} + +func IsPoint(r Range) bool { + return r.Start.Line == r.End.Line && r.Start.Character == r.End.Character +} + +func CompareRange(a, b Range) int { + if r := ComparePosition(a.Start, b.Start); r != 0 { + return r + } + return ComparePosition(a.End, b.End) +} + +func ComparePosition(a, b Position) int { + if a.Line < b.Line { + return -1 + } + if a.Line > b.Line { + return 1 + } + if a.Character < b.Character { + return -1 + } + if a.Character > b.Character { + return 1 + } + return 0 +} + +func (r Range) Format(f fmt.State, _ rune) { + fmt.Fprintf(f, "%v:%v-%v:%v", r.Start.Line, r.Start.Character, r.End.Line, r.End.Character) +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsclient.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsclient.go new file mode 100644 index 0000000..2f9beef --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsclient.go @@ -0,0 +1,221 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" + "encoding/json" + "log" + + "../jsonrpc2" +) + +type Client interface { + ShowMessage(context.Context, *ShowMessageParams) error + LogMessage(context.Context, *LogMessageParams) error + Event(context.Context, *interface{}) error + PublishDiagnostics(context.Context, *PublishDiagnosticsParams) error + WorkspaceFolders(context.Context) ([]WorkspaceFolder, error) + Configuration(context.Context, *ParamConfig) ([]interface{}, error) + RegisterCapability(context.Context, *RegistrationParams) error + UnregisterCapability(context.Context, *UnregistrationParams) error + ShowMessageRequest(context.Context, *ShowMessageRequestParams) (*MessageActionItem, error) + ApplyEdit(context.Context, *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error) +} + +func (h clientHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { + if delivered { + return false + } + if ctx.Err() != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, "")) + return true + } + switch r.Method { + case "window/showMessage": // notif + var params ShowMessageParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.client.ShowMessage(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "window/logMessage": // notif + var params LogMessageParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.client.LogMessage(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "telemetry/event": // notif + var params interface{} + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.client.Event(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/publishDiagnostics": // notif + var params PublishDiagnosticsParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.client.PublishDiagnostics(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/workspaceFolders": // req + if r.Params != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) + return true + } + resp, err := h.client.WorkspaceFolders(ctx) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/configuration": // req + var params ParamConfig + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.client.Configuration(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "client/registerCapability": // req + var params RegistrationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + err := h.client.RegisterCapability(ctx, ¶ms) + if err := r.Reply(ctx, nil, err); err != nil { + log.Printf("%v", err) + } + return true + case "client/unregisterCapability": // req + var params UnregistrationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + err := h.client.UnregisterCapability(ctx, ¶ms) + if err := r.Reply(ctx, nil, err); err != nil { + log.Printf("%v", err) + } + return true + case "window/showMessageRequest": // req + var params ShowMessageRequestParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.client.ShowMessageRequest(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/applyEdit": // req + var params ApplyWorkspaceEditParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.client.ApplyEdit(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + + default: + return false + } +} + +type clientDispatcher struct { + *jsonrpc2.Conn +} + +func (s *clientDispatcher) ShowMessage(ctx context.Context, params *ShowMessageParams) error { + return s.Conn.Notify(ctx, "window/showMessage", params) +} + +func (s *clientDispatcher) LogMessage(ctx context.Context, params *LogMessageParams) error { + return s.Conn.Notify(ctx, "window/logMessage", params) +} + +func (s *clientDispatcher) Event(ctx context.Context, params *interface{}) error { + return s.Conn.Notify(ctx, "telemetry/event", params) +} + +func (s *clientDispatcher) PublishDiagnostics(ctx context.Context, params *PublishDiagnosticsParams) error { + return s.Conn.Notify(ctx, "textDocument/publishDiagnostics", params) +} +func (s *clientDispatcher) WorkspaceFolders(ctx context.Context) ([]WorkspaceFolder, error) { + var result []WorkspaceFolder + if err := s.Conn.Call(ctx, "workspace/workspaceFolders", nil, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *clientDispatcher) Configuration(ctx context.Context, params *ParamConfig) ([]interface{}, error) { + var result []interface{} + if err := s.Conn.Call(ctx, "workspace/configuration", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *clientDispatcher) RegisterCapability(ctx context.Context, params *RegistrationParams) error { + return s.Conn.Call(ctx, "client/registerCapability", params, nil) // Call, not Notify +} + +func (s *clientDispatcher) UnregisterCapability(ctx context.Context, params *UnregistrationParams) error { + return s.Conn.Call(ctx, "client/unregisterCapability", params, nil) // Call, not Notify +} + +func (s *clientDispatcher) ShowMessageRequest(ctx context.Context, params *ShowMessageRequestParams) (*MessageActionItem, error) { + var result MessageActionItem + if err := s.Conn.Call(ctx, "window/showMessageRequest", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *clientDispatcher) ApplyEdit(ctx context.Context, params *ApplyWorkspaceEditParams) (*ApplyWorkspaceEditResponse, error) { + var result ApplyWorkspaceEditResponse + if err := s.Conn.Call(ctx, "workspace/applyEdit", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +// Types constructed to avoid structs as formal argument types +type ParamConfig struct { + ConfigurationParams + PartialResultParams +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsprotocol.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsprotocol.go new file mode 100644 index 0000000..50543fc --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsprotocol.go @@ -0,0 +1,4630 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package protocol contains data types and code for LSP jsonrpcs +// generated automatically from vscode-languageserver-node +// commit: 36ac51f057215e6e2e0408384e07ecf564a938da +// last fetched Tue Sep 24 2019 17:44:28 GMT-0400 (Eastern Daylight Time) +package protocol + +// Code generated (see typescript/README.md) DO NOT EDIT. + +/*ImplementationClientCapabilities defined: + * Since 3.6.0 + */ +type ImplementationClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `ImplementationRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*LinkSupport defined: + * The client supports additional metadata in the form of definition links. + * + * Since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +// ImplementationOptions is +type ImplementationOptions struct { + WorkDoneProgressOptions +} + +// ImplementationRegistrationOptions is +type ImplementationRegistrationOptions struct { + TextDocumentRegistrationOptions + ImplementationOptions + StaticRegistrationOptions +} + +// ImplementationParams is +type ImplementationParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*TypeDefinitionClientCapabilities defined: + * Since 3.6.0 + */ +type TypeDefinitionClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `TypeDefinitionRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*LinkSupport defined: + * The client supports additional metadata in the form of definition links. + * + * Since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +// TypeDefinitionOptions is +type TypeDefinitionOptions struct { + WorkDoneProgressOptions +} + +// TypeDefinitionRegistrationOptions is +type TypeDefinitionRegistrationOptions struct { + TextDocumentRegistrationOptions + TypeDefinitionOptions + StaticRegistrationOptions +} + +// TypeDefinitionParams is +type TypeDefinitionParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +// WorkspaceFoldersInitializeParams is +type WorkspaceFoldersInitializeParams struct { + + /*WorkspaceFolders defined: + * The actual configured workspace folders. + */ + WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders"` +} + +// WorkspaceFoldersClientCapabilities is +type WorkspaceFoldersClientCapabilities struct { + + /*Workspace defined: + * The workspace client capabilities + */ + Workspace *struct { + + /*WorkspaceFolders defined: + * The client has support for workspace folders + */ + WorkspaceFolders bool `json:"workspaceFolders,omitempty"` + } `json:"workspace,omitempty"` +} + +// WorkspaceFoldersServerCapabilities is +type WorkspaceFoldersServerCapabilities struct { + + /*Workspace defined: + * The workspace server capabilities + */ + Workspace *struct { + + // WorkspaceFolders is + WorkspaceFolders *struct { + + /*Supported defined: + * The Server has support for workspace folders + */ + Supported bool `json:"supported,omitempty"` + + /*ChangeNotifications defined: + * Whether the server wants to receive workspace folder + * change notifications. + * + * If a strings is provided the string is treated as a ID + * under which the notification is registed on the client + * side. The ID can be used to unregister for these events + * using the `client/unregisterCapability` request. + */ + ChangeNotifications string `json:"changeNotifications,omitempty"` // string | boolean + } `json:"workspaceFolders,omitempty"` + } `json:"workspace,omitempty"` +} + +// WorkspaceFolder is +type WorkspaceFolder struct { + + /*URI defined: + * The associated URI for this workspace folder. + */ + URI string `json:"uri"` + + /*Name defined: + * The name of the workspace folder. Used to refer to this + * workspace folder in thge user interface. + */ + Name string `json:"name"` +} + +/*DidChangeWorkspaceFoldersParams defined: + * The parameters of a `workspace/didChangeWorkspaceFolders` notification. + */ +type DidChangeWorkspaceFoldersParams struct { + + /*Event defined: + * The actual workspace folder change event. + */ + Event WorkspaceFoldersChangeEvent `json:"event"` +} + +/*WorkspaceFoldersChangeEvent defined: + * The workspace folder change event. + */ +type WorkspaceFoldersChangeEvent struct { + + /*Added defined: + * The array of added workspace folders + */ + Added []WorkspaceFolder `json:"added"` + + /*Removed defined: + * The array of the removed workspace folders + */ + Removed []WorkspaceFolder `json:"removed"` +} + +// ConfigurationClientCapabilities is +type ConfigurationClientCapabilities struct { + + /*Workspace defined: + * The workspace client capabilities + */ + Workspace *struct { + + /*Configuration defined: + * The client supports `workspace/configuration` requests. + */ + Configuration bool `json:"configuration,omitempty"` + } `json:"workspace,omitempty"` +} + +// ConfigurationItem is +type ConfigurationItem struct { + + /*ScopeURI defined: + * The scope to get the configuration section for. + */ + ScopeURI string `json:"scopeUri,omitempty"` + + /*Section defined: + * The configuration section asked for. + */ + Section string `json:"section,omitempty"` +} + +/*ConfigurationParams defined: + * The parameters of a configuration request. + */ +type ConfigurationParams struct { + + // Items is + Items []ConfigurationItem `json:"items"` +} + +// DocumentColorClientCapabilities is +type DocumentColorClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration. If this is set to `true` + * the client supports the new `DocumentColorRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// DocumentColorOptions is +type DocumentColorOptions struct { + + /*ResolveProvider defined: + * Code lens has a resolve provider as well. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +// DocumentColorRegistrationOptions is +type DocumentColorRegistrationOptions struct { + TextDocumentRegistrationOptions + StaticRegistrationOptions + DocumentColorOptions +} + +/*DocumentColorParams defined: + * Parameters for a [DocumentColorRequest](#DocumentColorRequest). + */ +type DocumentColorParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*ColorPresentationParams defined: + * Parameters for a [ColorPresentationRequest](#ColorPresentationRequest). + */ +type ColorPresentationParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Color defined: + * The color to request presentations for. + */ + Color Color `json:"color"` + + /*Range defined: + * The range where the color would be inserted. Serves as a context. + */ + Range Range `json:"range"` + WorkDoneProgressParams + PartialResultParams +} + +// FoldingRangeClientCapabilities is +type FoldingRangeClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration for folding range providers. If this is set to `true` + * the client supports the new `FoldingRangeRegistrationOptions` return value for the corresponding server + * capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*RangeLimit defined: + * The maximum number of folding ranges that the client prefers to receive per document. The value serves as a + * hint, servers are free to follow the limit. + */ + RangeLimit float64 `json:"rangeLimit,omitempty"` + + /*LineFoldingOnly defined: + * If set, the client signals that it only supports folding complete lines. If set, client will + * ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange. + */ + LineFoldingOnly bool `json:"lineFoldingOnly,omitempty"` +} + +// FoldingRangeOptions is +type FoldingRangeOptions struct { + WorkDoneProgressOptions +} + +// FoldingRangeRegistrationOptions is +type FoldingRangeRegistrationOptions struct { + TextDocumentRegistrationOptions + FoldingRangeOptions + StaticRegistrationOptions +} + +/*FoldingRange defined: + * Represents a folding range. + */ +type FoldingRange struct { + + /*StartLine defined: + * The zero-based line number from where the folded range starts. + */ + StartLine float64 `json:"startLine"` + + /*StartCharacter defined: + * The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line. + */ + StartCharacter float64 `json:"startCharacter,omitempty"` + + /*EndLine defined: + * The zero-based line number where the folded range ends. + */ + EndLine float64 `json:"endLine"` + + /*EndCharacter defined: + * The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line. + */ + EndCharacter float64 `json:"endCharacter,omitempty"` + + /*Kind defined: + * Describes the kind of the folding range such as `comment' or 'region'. The kind + * is used to categorize folding ranges and used by commands like 'Fold all comments'. See + * [FoldingRangeKind](#FoldingRangeKind) for an enumeration of standardized kinds. + */ + Kind string `json:"kind,omitempty"` +} + +/*FoldingRangeParams defined: + * Parameters for a [FoldingRangeRequest](#FoldingRangeRequest). + */ +type FoldingRangeParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*DeclarationClientCapabilities defined: + * Since 3.14.0 + */ +type DeclarationClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether declaration supports dynamic registration. If this is set to `true` + * the client supports the new `DeclarationRegistrationOptions` return value + * for the corresponding server capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*LinkSupport defined: + * The client supports additional metadata in the form of declaration links. + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +// DeclarationOptions is +type DeclarationOptions struct { + WorkDoneProgressOptions +} + +// DeclarationRegistrationOptions is +type DeclarationRegistrationOptions struct { + DeclarationOptions + TextDocumentRegistrationOptions + StaticRegistrationOptions +} + +// DeclarationParams is +type DeclarationParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +// SelectionRangeClientCapabilities is +type SelectionRangeClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration for selection range providers. If this is set to `true` + * the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server + * capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// SelectionRangeOptions is +type SelectionRangeOptions struct { + WorkDoneProgressOptions +} + +// SelectionRangeRegistrationOptions is +type SelectionRangeRegistrationOptions struct { + SelectionRangeOptions + TextDocumentRegistrationOptions + StaticRegistrationOptions +} + +/*SelectionRangeParams defined: + * A parameter literal used in selection range requests. + */ +type SelectionRangeParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Positions defined: + * The positions inside the text document. + */ + Positions []Position `json:"positions"` + WorkDoneProgressParams + PartialResultParams +} + +/*Registration defined: + * General parameters to to register for an notification or to register a provider. + */ +type Registration struct { + + /*ID defined: + * The id used to register the request. The id can be used to deregister + * the request again. + */ + ID string `json:"id"` + + /*Method defined: + * The method to register for. + */ + Method string `json:"method"` + + /*RegisterOptions defined: + * Options necessary for the registration. + */ + RegisterOptions interface{} `json:"registerOptions,omitempty"` +} + +// RegistrationParams is +type RegistrationParams struct { + + // Registrations is + Registrations []Registration `json:"registrations"` +} + +/*Unregistration defined: + * General parameters to unregister a request or notification. + */ +type Unregistration struct { + + /*ID defined: + * The id used to unregister the request or notification. Usually an id + * provided during the register request. + */ + ID string `json:"id"` + + /*Method defined: + * The method to unregister for. + */ + Method string `json:"method"` +} + +// UnregistrationParams is +type UnregistrationParams struct { + + // Unregisterations is + Unregisterations []Unregistration `json:"unregisterations"` +} + +// WorkDoneProgressParams is +type WorkDoneProgressParams struct { + + /*WorkDoneToken defined: + * An optional token that a server can use to report work done progress. + */ + WorkDoneToken *ProgressToken `json:"workDoneToken,omitempty"` +} + +// PartialResultParams is +type PartialResultParams struct { + + /*PartialResultToken defined: + * An optional token that a server can use to report partial results (e.g. streaming) to + * the client. + */ + PartialResultToken *ProgressToken `json:"partialResultToken,omitempty"` +} + +/*TextDocumentPositionParams defined: + * A parameter literal used in requests to pass a text document and a position inside that + * document. + */ +type TextDocumentPositionParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Position defined: + * The position inside the text document. + */ + Position Position `json:"position"` +} + +/*WorkspaceClientCapabilities defined: + * Workspace specific client capabilities. + */ +type WorkspaceClientCapabilities struct { + + /*ApplyEdit defined: + * The client supports applying batch edits + * to the workspace by supporting the request + * 'workspace/applyEdit' + */ + ApplyEdit bool `json:"applyEdit,omitempty"` + + /*WorkspaceEdit defined: + * Capabilities specific to `WorkspaceEdit`s + */ + WorkspaceEdit *WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"` + + /*DidChangeConfiguration defined: + * Capabilities specific to the `workspace/didChangeConfiguration` notification. + */ + DidChangeConfiguration *DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"` + + /*DidChangeWatchedFiles defined: + * Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + */ + DidChangeWatchedFiles *DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"` + + /*Symbol defined: + * Capabilities specific to the `workspace/symbol` request. + */ + Symbol *WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"` + + /*ExecuteCommand defined: + * Capabilities specific to the `workspace/executeCommand` request. + */ + ExecuteCommand *ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"` +} + +/*TextDocumentClientCapabilities defined: + * Text document specific client capabilities. + */ +type TextDocumentClientCapabilities struct { + + /*Synchronization defined: + * Defines which synchronization capabilities the client supports. + */ + Synchronization *TextDocumentSyncClientCapabilities `json:"synchronization,omitempty"` + + /*Completion defined: + * Capabilities specific to the `textDocument/completion` + */ + Completion *CompletionClientCapabilities `json:"completion,omitempty"` + + /*Hover defined: + * Capabilities specific to the `textDocument/hover` + */ + Hover *HoverClientCapabilities `json:"hover,omitempty"` + + /*SignatureHelp defined: + * Capabilities specific to the `textDocument/signatureHelp` + */ + SignatureHelp *SignatureHelpClientCapabilities `json:"signatureHelp,omitempty"` + + /*Declaration defined: + * Capabilities specific to the `textDocument/declaration` + * + * @since 3.14.0 + */ + Declaration *DeclarationClientCapabilities `json:"declaration,omitempty"` + + /*Definition defined: + * Capabilities specific to the `textDocument/definition` + */ + Definition *DefinitionClientCapabilities `json:"definition,omitempty"` + + /*TypeDefinition defined: + * Capabilities specific to the `textDocument/typeDefinition` + * + * @since 3.6.0 + */ + TypeDefinition *TypeDefinitionClientCapabilities `json:"typeDefinition,omitempty"` + + /*Implementation defined: + * Capabilities specific to the `textDocument/implementation` + * + * @since 3.6.0 + */ + Implementation *ImplementationClientCapabilities `json:"implementation,omitempty"` + + /*References defined: + * Capabilities specific to the `textDocument/references` + */ + References *ReferenceClientCapabilities `json:"references,omitempty"` + + /*DocumentHighlight defined: + * Capabilities specific to the `textDocument/documentHighlight` + */ + DocumentHighlight *DocumentHighlightClientCapabilities `json:"documentHighlight,omitempty"` + + /*DocumentSymbol defined: + * Capabilities specific to the `textDocument/documentSymbol` + */ + DocumentSymbol *DocumentSymbolClientCapabilities `json:"documentSymbol,omitempty"` + + /*CodeAction defined: + * Capabilities specific to the `textDocument/codeAction` + */ + CodeAction *CodeActionClientCapabilities `json:"codeAction,omitempty"` + + /*CodeLens defined: + * Capabilities specific to the `textDocument/codeLens` + */ + CodeLens *CodeLensClientCapabilities `json:"codeLens,omitempty"` + + /*DocumentLink defined: + * Capabilities specific to the `textDocument/documentLink` + */ + DocumentLink *DocumentLinkClientCapabilities `json:"documentLink,omitempty"` + + /*ColorProvider defined: + * Capabilities specific to the `textDocument/documentColor` + */ + ColorProvider *DocumentColorClientCapabilities `json:"colorProvider,omitempty"` + + /*Formatting defined: + * Capabilities specific to the `textDocument/formatting` + */ + Formatting *DocumentFormattingClientCapabilities `json:"formatting,omitempty"` + + /*RangeFormatting defined: + * Capabilities specific to the `textDocument/rangeFormatting` + */ + RangeFormatting *DocumentRangeFormattingClientCapabilities `json:"rangeFormatting,omitempty"` + + /*OnTypeFormatting defined: + * Capabilities specific to the `textDocument/onTypeFormatting` + */ + OnTypeFormatting *DocumentOnTypeFormattingClientCapabilities `json:"onTypeFormatting,omitempty"` + + /*Rename defined: + * Capabilities specific to the `textDocument/rename` + */ + Rename *RenameClientCapabilities `json:"rename,omitempty"` + + /*FoldingRange defined: + * Capabilities specific to `textDocument/foldingRange` requests. + * + * @since 3.10.0 + */ + FoldingRange *FoldingRangeClientCapabilities `json:"foldingRange,omitempty"` + + /*SelectionRange defined: + * Capabilities specific to `textDocument/selectionRange` requests + * + * @since 3.15.0 + */ + SelectionRange *SelectionRangeClientCapabilities `json:"selectionRange,omitempty"` + + /*PublishDiagnostics defined: + * Capabilities specific to `textDocument/publishDiagnostics`. + */ + PublishDiagnostics *PublishDiagnosticsClientCapabilities `json:"publishDiagnostics,omitempty"` +} + +/*InnerClientCapabilities defined: + * Defines the capabilities provided by the client. + */ +type InnerClientCapabilities struct { + + /*Workspace defined: + * Workspace specific client capabilities. + */ + Workspace *WorkspaceClientCapabilities `json:"workspace,omitempty"` + + /*TextDocument defined: + * Text document specific client capabilities. + */ + TextDocument *TextDocumentClientCapabilities `json:"textDocument,omitempty"` + + /*Window defined: + * Window specific client capabilities. + */ + Window interface{} `json:"window,omitempty"` + + /*Experimental defined: + * Experimental client capabilities. + */ + Experimental interface{} `json:"experimental,omitempty"` +} + +// ClientCapabilities is +type ClientCapabilities struct { + + /*Workspace defined: + * Workspace specific client capabilities. + */ + Workspace struct { + + /*ApplyEdit defined: + * The client supports applying batch edits + * to the workspace by supporting the request + * 'workspace/applyEdit' + */ + ApplyEdit bool `json:"applyEdit,omitempty"` + + /*WorkspaceEdit defined: + * Capabilities specific to `WorkspaceEdit`s + */ + WorkspaceEdit WorkspaceEditClientCapabilities `json:"workspaceEdit,omitempty"` + + /*DidChangeConfiguration defined: + * Capabilities specific to the `workspace/didChangeConfiguration` notification. + */ + DidChangeConfiguration DidChangeConfigurationClientCapabilities `json:"didChangeConfiguration,omitempty"` + + /*DidChangeWatchedFiles defined: + * Capabilities specific to the `workspace/didChangeWatchedFiles` notification. + */ + DidChangeWatchedFiles DidChangeWatchedFilesClientCapabilities `json:"didChangeWatchedFiles,omitempty"` + + /*Symbol defined: + * Capabilities specific to the `workspace/symbol` request. + */ + Symbol WorkspaceSymbolClientCapabilities `json:"symbol,omitempty"` + + /*ExecuteCommand defined: + * Capabilities specific to the `workspace/executeCommand` request. + */ + ExecuteCommand ExecuteCommandClientCapabilities `json:"executeCommand,omitempty"` + + /*WorkspaceFolders defined: + * The client has support for workspace folders + */ + WorkspaceFolders bool `json:"workspaceFolders,omitempty"` + + /*Configuration defined: + * The client supports `workspace/configuration` requests. + */ + Configuration bool `json:"configuration,omitempty"` + } `json:"workspace,omitempty"` + + /*TextDocument defined: + * Text document specific client capabilities. + */ + TextDocument TextDocumentClientCapabilities `json:"textDocument,omitempty"` + + /*Window defined: + * Window specific client capabilities. + */ + Window interface{} `json:"window,omitempty"` + + /*Experimental defined: + * Experimental client capabilities. + */ + Experimental interface{} `json:"experimental,omitempty"` + + /*DynamicRegistration defined: + * Whether implementation supports dynamic registration for selection range providers. If this is set to `true` + * the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server + * capability as well. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*StaticRegistrationOptions defined: + * Static registration options to be returned in the initialize + * request. + */ +type StaticRegistrationOptions struct { + + /*ID defined: + * The id used to register the request. The id can be used to deregister + * the request again. See also Registration#id. + */ + ID string `json:"id,omitempty"` +} + +/*TextDocumentRegistrationOptions defined: + * General text document registration options. + */ +type TextDocumentRegistrationOptions struct { + + /*DocumentSelector defined: + * A document selector to identify the scope of the registration. If set to null + * the document selector provided on the client side will be used. + */ + DocumentSelector DocumentSelector `json:"documentSelector"` +} + +/*SaveOptions defined: + * Save options. + */ +type SaveOptions struct { + + /*IncludeText defined: + * The client is supposed to include the content on save. + */ + IncludeText bool `json:"includeText,omitempty"` +} + +// WorkDoneProgressOptions is +type WorkDoneProgressOptions struct { + + // WorkDoneProgress is + WorkDoneProgress bool `json:"workDoneProgress,omitempty"` +} + +/*InnerServerCapabilities defined: + * Defines the capabilities provided by a language + * server. + */ +type InnerServerCapabilities struct { + + /*TextDocumentSync defined: + * Defines how text documents are synced. Is either a detailed structure defining each notification or + * for backwards compatibility the TextDocumentSyncKind number. + */ + TextDocumentSync interface{} `json:"textDocumentSync,omitempty"` // TextDocumentSyncOptions | TextDocumentSyncKind + + /*CompletionProvider defined: + * The server provides completion support. + */ + CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"` + + /*HoverProvider defined: + * The server provides hover support. + */ + HoverProvider bool `json:"hoverProvider,omitempty"` // boolean | HoverOptions + + /*SignatureHelpProvider defined: + * The server provides signature help support. + */ + SignatureHelpProvider *SignatureHelpOptions `json:"signatureHelpProvider,omitempty"` + + /*DeclarationProvider defined: + * The server provides Goto Declaration support. + */ + DeclarationProvider bool `json:"declarationProvider,omitempty"` // boolean | DeclarationOptions | DeclarationRegistrationOptions + + /*DefinitionProvider defined: + * The server provides goto definition support. + */ + DefinitionProvider bool `json:"definitionProvider,omitempty"` // boolean | DefinitionOptions + + /*TypeDefinitionProvider defined: + * The server provides Goto Type Definition support. + */ + TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"` // boolean | TypeDefinitionOptions | TypeDefinitionRegistrationOptions + + /*ImplementationProvider defined: + * The server provides Goto Implementation support. + */ + ImplementationProvider bool `json:"implementationProvider,omitempty"` // boolean | ImplementationOptions | ImplementationRegistrationOptions + + /*ReferencesProvider defined: + * The server provides find references support. + */ + ReferencesProvider bool `json:"referencesProvider,omitempty"` // boolean | ReferenceOptions + + /*DocumentHighlightProvider defined: + * The server provides document highlight support. + */ + DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` // boolean | DocumentHighlightOptions + + /*DocumentSymbolProvider defined: + * The server provides document symbol support. + */ + DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` // boolean | DocumentSymbolOptions + + /*CodeActionProvider defined: + * The server provides code actions. CodeActionOptions may only be + * specified if the client states that it supports + * `codeActionLiteralSupport` in its initial `initialize` request. + */ + CodeActionProvider interface{} `json:"codeActionProvider,omitempty"` // boolean | CodeActionOptions + + /*CodeLensProvider defined: + * The server provides code lens. + */ + CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"` + + /*DocumentLinkProvider defined: + * The server provides document link support. + */ + DocumentLinkProvider *DocumentLinkOptions `json:"documentLinkProvider,omitempty"` + + /*ColorProvider defined: + * The server provides color provider support. + */ + ColorProvider bool `json:"colorProvider,omitempty"` // boolean | DocumentColorOptions | DocumentColorRegistrationOptions + + /*WorkspaceSymbolProvider defined: + * The server provides workspace symbol support. + */ + WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` // boolean | WorkspaceSymbolOptions + + /*DocumentFormattingProvider defined: + * The server provides document formatting. + */ + DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` // boolean | DocumentFormattingOptions + + /*DocumentRangeFormattingProvider defined: + * The server provides document range formatting. + */ + DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` // boolean | DocumentRangeFormattingOptions + + /*DocumentOnTypeFormattingProvider defined: + * The server provides document formatting on typing. + */ + DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"` + + /*RenameProvider defined: + * The server provides rename support. RenameOptions may only be + * specified if the client states that it supports + * `prepareSupport` in its initial `initialize` request. + */ + RenameProvider interface{} `json:"renameProvider,omitempty"` // boolean | RenameOptions + + /*FoldingRangeProvider defined: + * The server provides folding provider support. + */ + FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"` // boolean | FoldingRangeOptions | FoldingRangeRegistrationOptions + + /*SelectionRangeProvider defined: + * The server provides selection range support. + */ + SelectionRangeProvider bool `json:"selectionRangeProvider,omitempty"` // boolean | SelectionRangeOptions | SelectionRangeRegistrationOptions + + /*ExecuteCommandProvider defined: + * The server provides execute command support. + */ + ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` + + /*Experimental defined: + * Experimental server capabilities. + */ + Experimental interface{} `json:"experimental,omitempty"` +} + +// ServerCapabilities is +type ServerCapabilities struct { + + /*TextDocumentSync defined: + * Defines how text documents are synced. Is either a detailed structure defining each notification or + * for backwards compatibility the TextDocumentSyncKind number. + */ + TextDocumentSync interface{} `json:"textDocumentSync,omitempty"` // TextDocumentSyncOptions | TextDocumentSyncKind + + /*CompletionProvider defined: + * The server provides completion support. + */ + CompletionProvider *CompletionOptions `json:"completionProvider,omitempty"` + + /*HoverProvider defined: + * The server provides hover support. + */ + HoverProvider bool `json:"hoverProvider,omitempty"` // boolean | HoverOptions + + /*SignatureHelpProvider defined: + * The server provides signature help support. + */ + SignatureHelpProvider *SignatureHelpOptions `json:"signatureHelpProvider,omitempty"` + + /*DeclarationProvider defined: + * The server provides Goto Declaration support. + */ + DeclarationProvider bool `json:"declarationProvider,omitempty"` // boolean | DeclarationOptions | DeclarationRegistrationOptions + + /*DefinitionProvider defined: + * The server provides goto definition support. + */ + DefinitionProvider bool `json:"definitionProvider,omitempty"` // boolean | DefinitionOptions + + /*TypeDefinitionProvider defined: + * The server provides Goto Type Definition support. + */ + TypeDefinitionProvider bool `json:"typeDefinitionProvider,omitempty"` // boolean | TypeDefinitionOptions | TypeDefinitionRegistrationOptions + + /*ImplementationProvider defined: + * The server provides Goto Implementation support. + */ + ImplementationProvider bool `json:"implementationProvider,omitempty"` // boolean | ImplementationOptions | ImplementationRegistrationOptions + + /*ReferencesProvider defined: + * The server provides find references support. + */ + ReferencesProvider bool `json:"referencesProvider,omitempty"` // boolean | ReferenceOptions + + /*DocumentHighlightProvider defined: + * The server provides document highlight support. + */ + DocumentHighlightProvider bool `json:"documentHighlightProvider,omitempty"` // boolean | DocumentHighlightOptions + + /*DocumentSymbolProvider defined: + * The server provides document symbol support. + */ + DocumentSymbolProvider bool `json:"documentSymbolProvider,omitempty"` // boolean | DocumentSymbolOptions + + /*CodeActionProvider defined: + * The server provides code actions. CodeActionOptions may only be + * specified if the client states that it supports + * `codeActionLiteralSupport` in its initial `initialize` request. + */ + CodeActionProvider interface{} `json:"codeActionProvider,omitempty"` // boolean | CodeActionOptions + + /*CodeLensProvider defined: + * The server provides code lens. + */ + CodeLensProvider *CodeLensOptions `json:"codeLensProvider,omitempty"` + + /*DocumentLinkProvider defined: + * The server provides document link support. + */ + DocumentLinkProvider *DocumentLinkOptions `json:"documentLinkProvider,omitempty"` + + /*ColorProvider defined: + * The server provides color provider support. + */ + ColorProvider bool `json:"colorProvider,omitempty"` // boolean | DocumentColorOptions | DocumentColorRegistrationOptions + + /*WorkspaceSymbolProvider defined: + * The server provides workspace symbol support. + */ + WorkspaceSymbolProvider bool `json:"workspaceSymbolProvider,omitempty"` // boolean | WorkspaceSymbolOptions + + /*DocumentFormattingProvider defined: + * The server provides document formatting. + */ + DocumentFormattingProvider bool `json:"documentFormattingProvider,omitempty"` // boolean | DocumentFormattingOptions + + /*DocumentRangeFormattingProvider defined: + * The server provides document range formatting. + */ + DocumentRangeFormattingProvider bool `json:"documentRangeFormattingProvider,omitempty"` // boolean | DocumentRangeFormattingOptions + + /*DocumentOnTypeFormattingProvider defined: + * The server provides document formatting on typing. + */ + DocumentOnTypeFormattingProvider *DocumentOnTypeFormattingOptions `json:"documentOnTypeFormattingProvider,omitempty"` + + /*RenameProvider defined: + * The server provides rename support. RenameOptions may only be + * specified if the client states that it supports + * `prepareSupport` in its initial `initialize` request. + */ + RenameProvider interface{} `json:"renameProvider,omitempty"` // boolean | RenameOptions + + /*FoldingRangeProvider defined: + * The server provides folding provider support. + */ + FoldingRangeProvider bool `json:"foldingRangeProvider,omitempty"` // boolean | FoldingRangeOptions | FoldingRangeRegistrationOptions + + /*SelectionRangeProvider defined: + * The server provides selection range support. + */ + SelectionRangeProvider bool `json:"selectionRangeProvider,omitempty"` // boolean | SelectionRangeOptions | SelectionRangeRegistrationOptions + + /*ExecuteCommandProvider defined: + * The server provides execute command support. + */ + ExecuteCommandProvider *ExecuteCommandOptions `json:"executeCommandProvider,omitempty"` + + /*Experimental defined: + * Experimental server capabilities. + */ + Experimental interface{} `json:"experimental,omitempty"` + + /*Workspace defined: + * The workspace server capabilities + */ + Workspace *struct { + + // WorkspaceFolders is + WorkspaceFolders *struct { + + /*Supported defined: + * The Server has support for workspace folders + */ + Supported bool `json:"supported,omitempty"` + + /*ChangeNotifications defined: + * Whether the server wants to receive workspace folder + * change notifications. + * + * If a strings is provided the string is treated as a ID + * under which the notification is registed on the client + * side. The ID can be used to unregister for these events + * using the `client/unregisterCapability` request. + */ + ChangeNotifications string `json:"changeNotifications,omitempty"` // string | boolean + } `json:"workspaceFolders,omitempty"` + } `json:"workspace,omitempty"` +} + +/*InnerInitializeParams defined: + * The initialize parameters + */ +type InnerInitializeParams struct { + + /*ProcessID defined: + * The process Id of the parent process that started + * the server. + */ + ProcessID float64 `json:"processId"` + + /*ClientInfo defined: + * Information about the client + * + * @since 3.15.0 + */ + ClientInfo *struct { + + /*Name defined: + * The name of the client as defined by the client. + */ + Name string `json:"name"` + + /*Version defined: + * The client's version as defined by the client. + */ + Version string `json:"version,omitempty"` + } `json:"clientInfo,omitempty"` + + /*RootPath defined: + * The rootPath of the workspace. Is null + * if no folder is open. + * + * @deprecated in favour of rootUri. + */ + RootPath string `json:"rootPath,omitempty"` + + /*RootURI defined: + * The rootUri of the workspace. Is null if no + * folder is open. If both `rootPath` and `rootUri` are set + * `rootUri` wins. + * + * @deprecated in favour of workspaceFolders. + */ + RootURI DocumentURI `json:"rootUri"` + + /*Capabilities defined: + * The capabilities provided by the client (editor or tool) + */ + Capabilities ClientCapabilities `json:"capabilities"` + + /*InitializationOptions defined: + * User provided initialization options. + */ + InitializationOptions interface{} `json:"initializationOptions,omitempty"` + + /*Trace defined: + * The initial trace setting. If omitted trace is disabled ('off'). + */ + Trace string `json:"trace,omitempty"` // 'off' | 'messages' | 'verbose' + WorkDoneProgressParams +} + +// InitializeParams is +type InitializeParams struct { + + /*ProcessID defined: + * The process Id of the parent process that started + * the server. + */ + ProcessID float64 `json:"processId"` + + /*ClientInfo defined: + * Information about the client + * + * @since 3.15.0 + */ + ClientInfo *struct { + + /*Name defined: + * The name of the client as defined by the client. + */ + Name string `json:"name"` + + /*Version defined: + * The client's version as defined by the client. + */ + Version string `json:"version,omitempty"` + } `json:"clientInfo,omitempty"` + + /*RootPath defined: + * The rootPath of the workspace. Is null + * if no folder is open. + * + * @deprecated in favour of rootUri. + */ + RootPath string `json:"rootPath,omitempty"` + + /*RootURI defined: + * The rootUri of the workspace. Is null if no + * folder is open. If both `rootPath` and `rootUri` are set + * `rootUri` wins. + * + * @deprecated in favour of workspaceFolders. + */ + RootURI DocumentURI `json:"rootUri"` + + /*Capabilities defined: + * The capabilities provided by the client (editor or tool) + */ + Capabilities ClientCapabilities `json:"capabilities"` + + /*InitializationOptions defined: + * User provided initialization options. + */ + InitializationOptions interface{} `json:"initializationOptions,omitempty"` + + /*Trace defined: + * The initial trace setting. If omitted trace is disabled ('off'). + */ + Trace string `json:"trace,omitempty"` // 'off' | 'messages' | 'verbose' + + /*WorkspaceFolders defined: + * The actual configured workspace folders. + */ + WorkspaceFolders []WorkspaceFolder `json:"workspaceFolders"` +} + +/*InitializeResult defined: + * The result returned from an initialize request. + */ +type InitializeResult struct { + + /*Capabilities defined: + * The capabilities the language server provides. + */ + Capabilities ServerCapabilities `json:"capabilities"` + + /*ServerInfo defined: + * Information about the server. + * + * @since 3.15.0 + */ + ServerInfo *struct { + + /*Name defined: + * The name of the server as defined by the server. + */ + Name string `json:"name"` + + /*Version defined: + * The servers's version as defined by the server. + */ + Version string `json:"version,omitempty"` + } `json:"serverInfo,omitempty"` + + /*Custom defined: + * Custom initialization results. + */ + Custom map[string]interface{} `json:"custom"` // [custom: string]: any; +} + +// InitializedParams is +type InitializedParams struct { +} + +// DidChangeConfigurationClientCapabilities is +type DidChangeConfigurationClientCapabilities struct { + + /*DynamicRegistration defined: + * Did change configuration notification supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +// DidChangeConfigurationRegistrationOptions is +type DidChangeConfigurationRegistrationOptions struct { + + // Section is + Section string `json:"section,omitempty"` // string | string[] +} + +/*DidChangeConfigurationParams defined: + * The parameters of a change configuration notification. + */ +type DidChangeConfigurationParams struct { + + /*Settings defined: + * The actual changed settings + */ + Settings interface{} `json:"settings"` +} + +/*ShowMessageParams defined: + * The parameters of a notification message. + */ +type ShowMessageParams struct { + + /*Type defined: + * The message type. See {@link MessageType} + */ + Type MessageType `json:"type"` + + /*Message defined: + * The actual message + */ + Message string `json:"message"` +} + +// MessageActionItem is +type MessageActionItem struct { + + /*Title defined: + * A short title like 'Retry', 'Open Log' etc. + */ + Title string `json:"title"` +} + +// ShowMessageRequestParams is +type ShowMessageRequestParams struct { + + /*Type defined: + * The message type. See {@link MessageType} + */ + Type MessageType `json:"type"` + + /*Message defined: + * The actual message + */ + Message string `json:"message"` + + /*Actions defined: + * The message action items to present. + */ + Actions []MessageActionItem `json:"actions,omitempty"` +} + +/*LogMessageParams defined: + * The log message parameters. + */ +type LogMessageParams struct { + + /*Type defined: + * The message type. See {@link MessageType} + */ + Type MessageType `json:"type"` + + /*Message defined: + * The actual message + */ + Message string `json:"message"` +} + +// TextDocumentSyncClientCapabilities is +type TextDocumentSyncClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether text document synchronization supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*WillSave defined: + * The client supports sending will save notifications. + */ + WillSave bool `json:"willSave,omitempty"` + + /*WillSaveWaitUntil defined: + * The client supports sending a will save request and + * waits for a response providing text edits which will + * be applied to the document before it is saved. + */ + WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` + + /*DidSave defined: + * The client supports did save notifications. + */ + DidSave bool `json:"didSave,omitempty"` +} + +// TextDocumentSyncOptions is +type TextDocumentSyncOptions struct { + + /*OpenClose defined: + * Open and close notifications are sent to the server. If omitted open close notification should not + * be sent. + */ + OpenClose bool `json:"openClose,omitempty"` + + /*Change defined: + * Change notifications are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full + * and TextDocumentSyncKind.Incremental. If omitted it defaults to TextDocumentSyncKind.None. + */ + Change TextDocumentSyncKind `json:"change,omitempty"` + + /*WillSave defined: + * If present will save notifications are sent to the server. If omitted the notification should not be + * sent. + */ + WillSave bool `json:"willSave,omitempty"` + + /*WillSaveWaitUntil defined: + * If present will save wait until requests are sent to the server. If omitted the request should not be + * sent. + */ + WillSaveWaitUntil bool `json:"willSaveWaitUntil,omitempty"` + + /*Save defined: + * If present save notifications are sent to the server. If omitted the notification should not be + * sent. + */ + Save *SaveOptions `json:"save,omitempty"` +} + +/*DidOpenTextDocumentParams defined: + * The parameters send in a open text document notification + */ +type DidOpenTextDocumentParams struct { + + /*TextDocument defined: + * The document that was opened. + */ + TextDocument TextDocumentItem `json:"textDocument"` +} + +/*DidChangeTextDocumentParams defined: + * The change text document notification's parameters. + */ +type DidChangeTextDocumentParams struct { + + /*TextDocument defined: + * The document that did change. The version number points + * to the version after all provided content changes have + * been applied. + */ + TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` + + /*ContentChanges defined: + * The actual content changes. The content changes describe single state changes + * to the document. So if there are two content changes c1 and c2 for a document + * in state S then c1 move the document to S' and c2 to S''. + */ + ContentChanges []TextDocumentContentChangeEvent `json:"contentChanges"` +} + +/*TextDocumentChangeRegistrationOptions defined: + * Describe options to be used when registered for text document change events. + */ +type TextDocumentChangeRegistrationOptions struct { + + /*SyncKind defined: + * How documents are synced to the server. + */ + SyncKind TextDocumentSyncKind `json:"syncKind"` + TextDocumentRegistrationOptions +} + +/*DidCloseTextDocumentParams defined: + * The parameters send in a close text document notification + */ +type DidCloseTextDocumentParams struct { + + /*TextDocument defined: + * The document that was closed. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` +} + +/*DidSaveTextDocumentParams defined: + * The parameters send in a save text document notification + */ +type DidSaveTextDocumentParams struct { + + /*TextDocument defined: + * The document that was closed. + */ + TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` + + /*Text defined: + * Optional the content when saved. Depends on the includeText value + * when the save notification was requested. + */ + Text string `json:"text,omitempty"` +} + +/*TextDocumentSaveRegistrationOptions defined: + * Save registration options. + */ +type TextDocumentSaveRegistrationOptions struct { + TextDocumentRegistrationOptions + SaveOptions +} + +/*WillSaveTextDocumentParams defined: + * The parameters send in a will save text document notification. + */ +type WillSaveTextDocumentParams struct { + + /*TextDocument defined: + * The document that will be saved. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Reason defined: + * The 'TextDocumentSaveReason'. + */ + Reason TextDocumentSaveReason `json:"reason"` +} + +// DidChangeWatchedFilesClientCapabilities is +type DidChangeWatchedFilesClientCapabilities struct { + + /*DynamicRegistration defined: + * Did change watched files notification supports dynamic registration. Please note + * that the current protocol doesn't support static configuration for file changes + * from the server side. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DidChangeWatchedFilesParams defined: + * The watched files change notification's parameters. + */ +type DidChangeWatchedFilesParams struct { + + /*Changes defined: + * The actual file events. + */ + Changes []FileEvent `json:"changes"` +} + +/*FileEvent defined: + * An event describing a file change. + */ +type FileEvent struct { + + /*URI defined: + * The file's uri. + */ + URI DocumentURI `json:"uri"` + + /*Type defined: + * The change type. + */ + Type FileChangeType `json:"type"` +} + +/*DidChangeWatchedFilesRegistrationOptions defined: + * Describe options to be used when registered for text document change events. + */ +type DidChangeWatchedFilesRegistrationOptions struct { + + /*Watchers defined: + * The watchers to register. + */ + Watchers []FileSystemWatcher `json:"watchers"` +} + +// FileSystemWatcher is +type FileSystemWatcher struct { + + /*GlobPattern defined: + * The glob pattern to watch. Glob patterns can have the following syntax: + * - `*` to match one or more characters in a path segment + * - `?` to match on one character in a path segment + * - `**` to match any number of path segments, including none + * - `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + */ + GlobPattern string `json:"globPattern"` + + /*Kind defined: + * The kind of events of interest. If omitted it defaults + * to WatchKind.Create | WatchKind.Change | WatchKind.Delete + * which is 7. + */ + Kind float64 `json:"kind,omitempty"` +} + +/*PublishDiagnosticsClientCapabilities defined: + * The publish diagnostic client capabilities. + */ +type PublishDiagnosticsClientCapabilities struct { + + /*RelatedInformation defined: + * Whether the clients accepts diagnostics with related information. + */ + RelatedInformation bool `json:"relatedInformation,omitempty"` + + /*TagSupport defined: + * Client supports the tag property to provide meta data about a diagnostic. + * Clients supporting tags have to handle unknown tags gracefully. + * + * @since 3.15.0 + */ + TagSupport *struct { + + /*ValueSet defined: + * The tags supported by the client. + */ + ValueSet []DiagnosticTag `json:"valueSet"` + } `json:"tagSupport,omitempty"` +} + +/*PublishDiagnosticsParams defined: + * The publish diagnostic notification's parameters. + */ +type PublishDiagnosticsParams struct { + + /*URI defined: + * The URI for which diagnostic information is reported. + */ + URI DocumentURI `json:"uri"` + + /*Version defined: + * Optional the version number of the document the diagnostics are published for. + * + * @since 3.15.0 + */ + Version float64 `json:"version,omitempty"` + + /*Diagnostics defined: + * An array of diagnostic information items. + */ + Diagnostics []Diagnostic `json:"diagnostics"` +} + +/*CompletionClientCapabilities defined: + * Completion client capabilities + */ +type CompletionClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether completion supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*CompletionItem defined: + * The client supports the following `CompletionItem` specific + * capabilities. + */ + CompletionItem *struct { + + /*SnippetSupport defined: + * Client supports snippets as insert text. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Placeholders with equal identifiers are linked, + * that is typing in one will update others too. + */ + SnippetSupport bool `json:"snippetSupport,omitempty"` + + /*CommitCharactersSupport defined: + * Client supports commit characters on a completion item. + */ + CommitCharactersSupport bool `json:"commitCharactersSupport,omitempty"` + + /*DocumentationFormat defined: + * Client supports the follow content formats for the documentation + * property. The order describes the preferred format of the client. + */ + DocumentationFormat []MarkupKind `json:"documentationFormat,omitempty"` + + /*DeprecatedSupport defined: + * Client supports the deprecated property on a completion item. + */ + DeprecatedSupport bool `json:"deprecatedSupport,omitempty"` + + /*PreselectSupport defined: + * Client supports the preselect property on a completion item. + */ + PreselectSupport bool `json:"preselectSupport,omitempty"` + + /*TagSupport defined: + * Client supports the tag property on a completion item. Clients supporting + * tags have to handle unknown tags gracefully. Clients especially need to + * preserve unknown tags when sending a completion item back to the server in + * a resolve call. + * + * @since 3.15.0 + */ + TagSupport *struct { + + /*ValueSet defined: + * The tags supported by the client. + */ + ValueSet []CompletionItemTag `json:"valueSet"` + } `json:"tagSupport,omitempty"` + } `json:"completionItem,omitempty"` + + // CompletionItemKind is + CompletionItemKind *struct { + + /*ValueSet defined: + * The completion item kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the completion items kinds from `Text` to `Reference` as defined in + * the initial version of the protocol. + */ + ValueSet []CompletionItemKind `json:"valueSet,omitempty"` + } `json:"completionItemKind,omitempty"` + + /*ContextSupport defined: + * The client supports to send additional context information for a + * `textDocument/completion` requestion. + */ + ContextSupport bool `json:"contextSupport,omitempty"` +} + +/*CompletionContext defined: + * Contains additional information about the context in which a completion request is triggered. + */ +type CompletionContext struct { + + /*TriggerKind defined: + * How the completion was triggered. + */ + TriggerKind CompletionTriggerKind `json:"triggerKind"` + + /*TriggerCharacter defined: + * The trigger character (a single character) that has trigger code complete. + * Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` + */ + TriggerCharacter string `json:"triggerCharacter,omitempty"` +} + +/*CompletionParams defined: + * Completion parameters + */ +type CompletionParams struct { + + /*Context defined: + * The completion context. This is only available it the client specifies + * to send this using the client capability `textDocument.completion.contextSupport === true` + */ + Context *CompletionContext `json:"context,omitempty"` + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*CompletionOptions defined: + * Completion options. + */ +type CompletionOptions struct { + + /*TriggerCharacters defined: + * Most tools trigger completion request automatically without explicitly requesting + * it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user + * starts to type an identifier. For example if the user types `c` in a JavaScript file + * code complete will automatically pop up present `console` besides others as a + * completion item. Characters that make up identifiers don't need to be listed here. + * + * If code complete should automatically be trigger on characters not being valid inside + * an identifier (for example `.` in JavaScript) list them in `triggerCharacters`. + */ + TriggerCharacters []string `json:"triggerCharacters,omitempty"` + + /*AllCommitCharacters defined: + * The list of all possible characters that commit a completion. This field can be used + * if clients don't support individual commmit characters per completion item. See + * `ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` + * + * @since 3.2.0 + */ + AllCommitCharacters []string `json:"allCommitCharacters,omitempty"` + + /*ResolveProvider defined: + * The server provides support to resolve additional + * information for a completion item. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +/*CompletionRegistrationOptions defined: + * Registration options for a [CompletionRequest](#CompletionRequest). + */ +type CompletionRegistrationOptions struct { + TextDocumentRegistrationOptions + CompletionOptions +} + +// HoverClientCapabilities is +type HoverClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether hover supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*ContentFormat defined: + * Client supports the follow content formats for the content + * property. The order describes the preferred format of the client. + */ + ContentFormat []MarkupKind `json:"contentFormat,omitempty"` +} + +/*HoverOptions defined: + * Hover options. + */ +type HoverOptions struct { + WorkDoneProgressOptions +} + +/*HoverParams defined: + * Parameters for a [HoverRequest](#HoverRequest). + */ +type HoverParams struct { + TextDocumentPositionParams + WorkDoneProgressParams +} + +/*HoverRegistrationOptions defined: + * Registration options for a [HoverRequest](#HoverRequest). + */ +type HoverRegistrationOptions struct { + TextDocumentRegistrationOptions + HoverOptions +} + +/*SignatureHelpClientCapabilities defined: + * Client Capabilities for a [SignatureHelpRequest](#SignatureHelpRequest). + */ +type SignatureHelpClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether signature help supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*SignatureInformation defined: + * The client supports the following `SignatureInformation` + * specific properties. + */ + SignatureInformation *struct { + + /*DocumentationFormat defined: + * Client supports the follow content formats for the documentation + * property. The order describes the preferred format of the client. + */ + DocumentationFormat []MarkupKind `json:"documentationFormat,omitempty"` + + /*ParameterInformation defined: + * Client capabilities specific to parameter information. + */ + ParameterInformation *struct { + + /*LabelOffsetSupport defined: + * The client supports processing label offsets instead of a + * simple label string. + * + * @since 3.14.0 + */ + LabelOffsetSupport bool `json:"labelOffsetSupport,omitempty"` + } `json:"parameterInformation,omitempty"` + } `json:"signatureInformation,omitempty"` + + /*ContextSupport defined: + * The client supports to send additional context information for a + * `textDocument/signatureHelp` request. A client that opts into + * contextSupport will also support the `retriggerCharacters` on + * `SignatureHelpOptions`. + * + * @since 3.15.0 + */ + ContextSupport bool `json:"contextSupport,omitempty"` +} + +/*SignatureHelpOptions defined: + * Server Capabilities for a [SignatureHelpRequest](#SignatureHelpRequest). + */ +type SignatureHelpOptions struct { + + /*TriggerCharacters defined: + * List of characters that trigger signature help. + */ + TriggerCharacters []string `json:"triggerCharacters,omitempty"` + + /*RetriggerCharacters defined: + * List of characters that re-trigger signature help. + * + * These trigger characters are only active when signature help is already showing. All trigger characters + * are also counted as re-trigger characters. + * + * @since 3.15.0 + */ + RetriggerCharacters []string `json:"retriggerCharacters,omitempty"` + WorkDoneProgressOptions +} + +/*SignatureHelpContext defined: + * Additional information about the context in which a signature help request was triggered. + * + * @since 3.15.0 + */ +type SignatureHelpContext struct { + + /*TriggerKind defined: + * Action that caused signature help to be triggered. + */ + TriggerKind SignatureHelpTriggerKind `json:"triggerKind"` + + /*TriggerCharacter defined: + * Character that caused signature help to be triggered. + * + * This is undefined when `triggerKind !== SignatureHelpTriggerKind.TriggerCharacter` + */ + TriggerCharacter string `json:"triggerCharacter,omitempty"` + + /*IsRetrigger defined: + * `true` if signature help was already showing when it was triggered. + * + * Retriggers occur when the signature help is already active and can be caused by actions such as + * typing a trigger character, a cursor move, or document content changes. + */ + IsRetrigger bool `json:"isRetrigger"` + + /*ActiveSignatureHelp defined: + * The currently active `SignatureHelp`. + * + * The `activeSignatureHelp` has its `SignatureHelp.activeSignature` field updated based on + * the user navigating through available signatures. + */ + ActiveSignatureHelp *SignatureHelp `json:"activeSignatureHelp,omitempty"` +} + +/*SignatureHelpParams defined: + * Parameters for a [SignatureHelpRequest](#SignatureHelpRequest). + */ +type SignatureHelpParams struct { + + /*Context defined: + * The signature help context. This is only available if the client specifies + * to send this using the client capability `textDocument.signatureHelp.contextSupport === true` + * + * @since 3.15.0 + */ + Context *SignatureHelpContext `json:"context,omitempty"` + TextDocumentPositionParams + WorkDoneProgressParams +} + +/*SignatureHelpRegistrationOptions defined: + * Registration options for a [SignatureHelpRequest](#SignatureHelpRequest). + */ +type SignatureHelpRegistrationOptions struct { + TextDocumentRegistrationOptions + SignatureHelpOptions +} + +/*DefinitionClientCapabilities defined: + * Client Capabilities for a [DefinitionRequest](#DefinitionRequest). + */ +type DefinitionClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether definition supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*LinkSupport defined: + * The client supports additional metadata in the form of definition links. + * + * @since 3.14.0 + */ + LinkSupport bool `json:"linkSupport,omitempty"` +} + +/*DefinitionOptions defined: + * Server Capabilities for a [DefinitionRequest](#DefinitionRequest). + */ +type DefinitionOptions struct { + WorkDoneProgressOptions +} + +/*DefinitionParams defined: + * Parameters for a [DefinitionRequest](#DefinitionRequest). + */ +type DefinitionParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*DefinitionRegistrationOptions defined: + * Registration options for a [DefinitionRequest](#DefinitionRequest). + */ +type DefinitionRegistrationOptions struct { + TextDocumentRegistrationOptions + DefinitionOptions +} + +/*ReferenceClientCapabilities defined: + * Client Capabilities for a [ReferencesRequest](#ReferencesRequest). + */ +type ReferenceClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether references supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*ReferenceParams defined: + * Parameters for a [ReferencesRequest](#ReferencesRequest). + */ +type ReferenceParams struct { + + // Context is + Context ReferenceContext `json:"context"` + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*ReferenceOptions defined: + * Reference options. + */ +type ReferenceOptions struct { + WorkDoneProgressOptions +} + +/*ReferenceRegistrationOptions defined: + * Registration options for a [ReferencesRequest](#ReferencesRequest). + */ +type ReferenceRegistrationOptions struct { + TextDocumentRegistrationOptions + ReferenceOptions +} + +/*DocumentHighlightClientCapabilities defined: + * Client Capabilities for a [DocumentHighlightRequest](#DocumentHighlightRequest). + */ +type DocumentHighlightClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether document highlight supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DocumentHighlightParams defined: + * Parameters for a [DocumentHighlightRequest](#DocumentHighlightRequest). + */ +type DocumentHighlightParams struct { + TextDocumentPositionParams + WorkDoneProgressParams + PartialResultParams +} + +/*DocumentHighlightOptions defined: + * Provider options for a [DocumentHighlightRequest](#DocumentHighlightRequest). + */ +type DocumentHighlightOptions struct { + WorkDoneProgressOptions +} + +/*DocumentHighlightRegistrationOptions defined: + * Registration options for a [DocumentHighlightRequest](#DocumentHighlightRequest). + */ +type DocumentHighlightRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentHighlightOptions +} + +/*DocumentSymbolClientCapabilities defined: + * Client Capabilities for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +type DocumentSymbolClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether document symbol supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*SymbolKind defined: + * Specific capabilities for the `SymbolKind`. + */ + SymbolKind *struct { + + /*ValueSet defined: + * The symbol kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the symbol kinds from `File` to `Array` as defined in + * the initial version of the protocol. + */ + ValueSet []SymbolKind `json:"valueSet,omitempty"` + } `json:"symbolKind,omitempty"` + + /*HierarchicalDocumentSymbolSupport defined: + * The client support hierarchical document symbols. + */ + HierarchicalDocumentSymbolSupport bool `json:"hierarchicalDocumentSymbolSupport,omitempty"` +} + +/*DocumentSymbolParams defined: + * Parameters for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +type DocumentSymbolParams struct { + + /*TextDocument defined: + * The text document. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*DocumentSymbolOptions defined: + * Provider options for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +type DocumentSymbolOptions struct { + WorkDoneProgressOptions +} + +/*DocumentSymbolRegistrationOptions defined: + * Registration options for a [DocumentSymbolRequest](#DocumentSymbolRequest). + */ +type DocumentSymbolRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentSymbolOptions +} + +/*CodeActionClientCapabilities defined: + * The Client Capabilities of a [CodeActionRequest](#CodeActionRequest). + */ +type CodeActionClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether code action supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*CodeActionLiteralSupport defined: + * The client support code action literals as a valid + * response of the `textDocument/codeAction` request. + * + * @since 3.8.0 + */ + CodeActionLiteralSupport *struct { + + /*CodeActionKind defined: + * The code action kind is support with the following value + * set. + */ + CodeActionKind struct { + + /*ValueSet defined: + * The code action kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + */ + ValueSet []CodeActionKind `json:"valueSet"` + } `json:"codeActionKind"` + } `json:"codeActionLiteralSupport,omitempty"` + + /*IsPreferredSupport defined: + * Whether code action supports the `isPreferred` property. + * @since 3.15.0 + */ + IsPreferredSupport bool `json:"isPreferredSupport,omitempty"` +} + +/*CodeActionParams defined: + * The parameters of a [CodeActionRequest](#CodeActionRequest). + */ +type CodeActionParams struct { + + /*TextDocument defined: + * The document in which the command was invoked. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Range defined: + * The range for which the command was invoked. + */ + Range Range `json:"range"` + + /*Context defined: + * Context carrying additional information. + */ + Context CodeActionContext `json:"context"` + WorkDoneProgressParams + PartialResultParams +} + +/*CodeActionOptions defined: + * Provider options for a [CodeActionRequest](#CodeActionRequest). + */ +type CodeActionOptions struct { + + /*CodeActionKinds defined: + * CodeActionKinds that this server may return. + * + * The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server + * may list out every specific kind they provide. + */ + CodeActionKinds []CodeActionKind `json:"codeActionKinds,omitempty"` + WorkDoneProgressOptions +} + +/*CodeActionRegistrationOptions defined: + * Registration options for a [CodeActionRequest](#CodeActionRequest). + */ +type CodeActionRegistrationOptions struct { + TextDocumentRegistrationOptions + CodeActionOptions +} + +/*WorkspaceSymbolClientCapabilities defined: + * Client capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +type WorkspaceSymbolClientCapabilities struct { + + /*DynamicRegistration defined: + * Symbol request supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*SymbolKind defined: + * Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. + */ + SymbolKind *struct { + + /*ValueSet defined: + * The symbol kind values the client supports. When this + * property exists the client also guarantees that it will + * handle values outside its set gracefully and falls back + * to a default value when unknown. + * + * If this property is not present the client only supports + * the symbol kinds from `File` to `Array` as defined in + * the initial version of the protocol. + */ + ValueSet []SymbolKind `json:"valueSet,omitempty"` + } `json:"symbolKind,omitempty"` +} + +/*WorkspaceSymbolParams defined: + * The parameters of a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +type WorkspaceSymbolParams struct { + + /*Query defined: + * A query string to filter symbols by. Clients may send an empty + * string here to request all symbols. + */ + Query string `json:"query"` + WorkDoneProgressParams + PartialResultParams +} + +/*WorkspaceSymbolOptions defined: + * Server capabilities for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +type WorkspaceSymbolOptions struct { + WorkDoneProgressOptions +} + +/*WorkspaceSymbolRegistrationOptions defined: + * Registration options for a [WorkspaceSymbolRequest](#WorkspaceSymbolRequest). + */ +type WorkspaceSymbolRegistrationOptions struct { + WorkspaceSymbolOptions +} + +/*CodeLensClientCapabilities defined: + * The client capabilities of a [CodeLensRequest](#CodeLensRequest). + */ +type CodeLensClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether code lens supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*CodeLensParams defined: + * The parameters of a [CodeLensRequest](#CodeLensRequest). + */ +type CodeLensParams struct { + + /*TextDocument defined: + * The document to request code lens for. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*CodeLensOptions defined: + * Code Lens provider options of a [CodeLensRequest](#CodeLensRequest). + */ +type CodeLensOptions struct { + + /*ResolveProvider defined: + * Code lens has a resolve provider as well. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +/*CodeLensRegistrationOptions defined: + * Registration options for a [CodeLensRequest](#CodeLensRequest). + */ +type CodeLensRegistrationOptions struct { + TextDocumentRegistrationOptions + CodeLensOptions +} + +/*DocumentLinkClientCapabilities defined: + * The client capabilities of a [DocumentLinkRequest](#DocumentLinkRequest). + */ +type DocumentLinkClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether document link supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*TooltipSupport defined: + * Whether the client support the `tooltip` property on `DocumentLink`. + * + * @since 3.15.0 + */ + TooltipSupport bool `json:"tooltipSupport,omitempty"` +} + +/*DocumentLinkParams defined: + * The parameters of a [DocumentLinkRequest](#DocumentLinkRequest). + */ +type DocumentLinkParams struct { + + /*TextDocument defined: + * The document to provide document links for. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + WorkDoneProgressParams + PartialResultParams +} + +/*DocumentLinkOptions defined: + * Provider options for a [DocumentLinkRequest](#DocumentLinkRequest). + */ +type DocumentLinkOptions struct { + + /*ResolveProvider defined: + * Document links have a resolve provider as well. + */ + ResolveProvider bool `json:"resolveProvider,omitempty"` + WorkDoneProgressOptions +} + +/*DocumentLinkRegistrationOptions defined: + * Registration options for a [DocumentLinkRequest](#DocumentLinkRequest). + */ +type DocumentLinkRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentLinkOptions +} + +/*DocumentFormattingClientCapabilities defined: + * Client capabilities of a [DocumentFormattingRequest](#DocumentFormattingRequest). + */ +type DocumentFormattingClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether formatting supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DocumentFormattingParams defined: + * The parameters of a [DocumentFormattingRequest](#DocumentFormattingRequest). + */ +type DocumentFormattingParams struct { + + /*TextDocument defined: + * The document to format. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Options defined: + * The format options + */ + Options FormattingOptions `json:"options"` + WorkDoneProgressParams +} + +/*DocumentFormattingOptions defined: + * Provider options for a [DocumentFormattingRequest](#DocumentFormattingRequest). + */ +type DocumentFormattingOptions struct { + WorkDoneProgressOptions +} + +/*DocumentFormattingRegistrationOptions defined: + * Registration options for a [DocumentFormattingRequest](#DocumentFormattingRequest). + */ +type DocumentFormattingRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentFormattingOptions +} + +/*DocumentRangeFormattingClientCapabilities defined: + * Client capabilities of a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). + */ +type DocumentRangeFormattingClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether range formatting supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DocumentRangeFormattingParams defined: + * The parameters of a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). + */ +type DocumentRangeFormattingParams struct { + + /*TextDocument defined: + * The document to format. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Range defined: + * The range to format + */ + Range Range `json:"range"` + + /*Options defined: + * The format options + */ + Options FormattingOptions `json:"options"` + WorkDoneProgressParams +} + +/*DocumentRangeFormattingOptions defined: + * Provider options for a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). + */ +type DocumentRangeFormattingOptions struct { + WorkDoneProgressOptions +} + +/*DocumentRangeFormattingRegistrationOptions defined: + * Registration options for a [DocumentRangeFormattingRequest](#DocumentRangeFormattingRequest). + */ +type DocumentRangeFormattingRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentRangeFormattingOptions +} + +/*DocumentOnTypeFormattingClientCapabilities defined: + * Client capabilities of a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). + */ +type DocumentOnTypeFormattingClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether on type formatting supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*DocumentOnTypeFormattingParams defined: + * The parameters of a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). + */ +type DocumentOnTypeFormattingParams struct { + + /*TextDocument defined: + * The document to format. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Position defined: + * The position at which this request was send. + */ + Position Position `json:"position"` + + /*Ch defined: + * The character that has been typed. + */ + Ch string `json:"ch"` + + /*Options defined: + * The format options. + */ + Options FormattingOptions `json:"options"` +} + +/*DocumentOnTypeFormattingOptions defined: + * Provider options for a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). + */ +type DocumentOnTypeFormattingOptions struct { + + /*FirstTriggerCharacter defined: + * A character on which formatting should be triggered, like `}`. + */ + FirstTriggerCharacter string `json:"firstTriggerCharacter"` + + /*MoreTriggerCharacter defined: + * More trigger characters. + */ + MoreTriggerCharacter []string `json:"moreTriggerCharacter,omitempty"` +} + +/*DocumentOnTypeFormattingRegistrationOptions defined: + * Registration options for a [DocumentOnTypeFormattingRequest](#DocumentOnTypeFormattingRequest). + */ +type DocumentOnTypeFormattingRegistrationOptions struct { + TextDocumentRegistrationOptions + DocumentOnTypeFormattingOptions +} + +// RenameClientCapabilities is +type RenameClientCapabilities struct { + + /*DynamicRegistration defined: + * Whether rename supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` + + /*PrepareSupport defined: + * Client supports testing for validity of rename operations + * before execution. + * + * @since version 3.12.0 + */ + PrepareSupport bool `json:"prepareSupport,omitempty"` +} + +/*RenameParams defined: + * The parameters of a [RenameRequest](#RenameRequest). + */ +type RenameParams struct { + + /*TextDocument defined: + * The document to rename. + */ + TextDocument TextDocumentIdentifier `json:"textDocument"` + + /*Position defined: + * The position at which this request was sent. + */ + Position Position `json:"position"` + + /*NewName defined: + * The new name of the symbol. If the given name is not valid the + * request must return a [ResponseError](#ResponseError) with an + * appropriate message set. + */ + NewName string `json:"newName"` + WorkDoneProgressParams +} + +/*RenameOptions defined: + * Provider options for a [RenameRequest](#RenameRequest). + */ +type RenameOptions struct { + + /*PrepareProvider defined: + * Renames should be checked and tested before being executed. + * + * @since version 3.12.0 + */ + PrepareProvider bool `json:"prepareProvider,omitempty"` + WorkDoneProgressOptions +} + +/*RenameRegistrationOptions defined: + * Registration options for a [RenameRequest](#RenameRequest). + */ +type RenameRegistrationOptions struct { + TextDocumentRegistrationOptions + RenameOptions +} + +// PrepareRenameParams is +type PrepareRenameParams struct { + TextDocumentPositionParams + WorkDoneProgressParams +} + +/*ExecuteCommandClientCapabilities defined: + * The client capabilities of a [ExecuteCommandRequest](#ExecuteCommandRequest). + */ +type ExecuteCommandClientCapabilities struct { + + /*DynamicRegistration defined: + * Execute command supports dynamic registration. + */ + DynamicRegistration bool `json:"dynamicRegistration,omitempty"` +} + +/*ExecuteCommandParams defined: + * The parameters of a [ExecuteCommandRequest](#ExecuteCommandRequest). + */ +type ExecuteCommandParams struct { + + /*Command defined: + * The identifier of the actual command handler. + */ + Command string `json:"command"` + + /*Arguments defined: + * Arguments that the command should be invoked with. + */ + Arguments []interface{} `json:"arguments,omitempty"` + WorkDoneProgressParams +} + +/*ExecuteCommandOptions defined: + * The server capabilities of a [ExecuteCommandRequest](#ExecuteCommandRequest). + */ +type ExecuteCommandOptions struct { + + /*Commands defined: + * The commands to be executed on the server + */ + Commands []string `json:"commands"` + WorkDoneProgressOptions +} + +/*ExecuteCommandRegistrationOptions defined: + * Registration options for a [ExecuteCommandRequest](#ExecuteCommandRequest). + */ +type ExecuteCommandRegistrationOptions struct { + ExecuteCommandOptions +} + +// WorkspaceEditClientCapabilities is +type WorkspaceEditClientCapabilities struct { + + /*DocumentChanges defined: + * The client supports versioned document changes in `WorkspaceEdit`s + */ + DocumentChanges bool `json:"documentChanges,omitempty"` + + /*ResourceOperations defined: + * The resource operations the client supports. Clients should at least + * support 'create', 'rename' and 'delete' files and folders. + * + * @since 3.13.0 + */ + ResourceOperations []ResourceOperationKind `json:"resourceOperations,omitempty"` + + /*FailureHandling defined: + * The failure handling strategy of a client if applying the workspace edit + * fails. + * + * @since 3.13.0 + */ + FailureHandling FailureHandlingKind `json:"failureHandling,omitempty"` +} + +/*ApplyWorkspaceEditParams defined: + * The parameters passed via a apply workspace edit request. + */ +type ApplyWorkspaceEditParams struct { + + /*Label defined: + * An optional label of the workspace edit. This label is + * presented in the user interface for example on an undo + * stack to undo the workspace edit. + */ + Label string `json:"label,omitempty"` + + /*Edit defined: + * The edits to apply. + */ + Edit WorkspaceEdit `json:"edit"` +} + +/*ApplyWorkspaceEditResponse defined: + * A response returned from the apply workspace edit request. + */ +type ApplyWorkspaceEditResponse struct { + + /*Applied defined: + * Indicates whether the edit was applied or not. + */ + Applied bool `json:"applied"` + + /*FailureReason defined: + * An optional textual description for why the edit was not applied. + * This may be used by the server for diagnostic logging or to provide + * a suitable error for a request that triggered the edit. + */ + FailureReason string `json:"failureReason,omitempty"` + + /*FailedChange defined: + * Depending on the client's failure handling strategy `failedChange` might + * contain the index of the change that failed. This property is only available + * if the client signals a `failureHandlingStrategy` in its client capabilities. + */ + FailedChange float64 `json:"failedChange,omitempty"` +} + +/*Position defined: + * Position in a text document expressed as zero-based line and character offset. + * The offsets are based on a UTF-16 string representation. So a string of the form + * `a𐐀b` the character offset of the character `a` is 0, the character offset of `𐐀` + * is 1 and the character offset of b is 3 since `𐐀` is represented using two code + * units in UTF-16. + * + * Positions are line end character agnostic. So you can not specify a position that + * denotes `\r|\n` or `\n|` where `|` represents the character offset. + */ +type Position struct { + + /*Line defined: + * Line position in a document (zero-based). + * If a line number is greater than the number of lines in a document, it defaults back to the number of lines in the document. + * If a line number is negative, it defaults to 0. + */ + Line float64 `json:"line"` + + /*Character defined: + * Character offset on a line in a document (zero-based). Assuming that the line is + * represented as a string, the `character` value represents the gap between the + * `character` and `character + 1`. + * + * If the character value is greater than the line length it defaults back to the + * line length. + * If a line number is negative, it defaults to 0. + */ + Character float64 `json:"character"` +} + +/*Range defined: + * A range in a text document expressed as (zero-based) start and end positions. + * + * If you want to specify a range that contains a line including the line ending + * character(s) then use an end position denoting the start of the next line. + * For example: + * ```ts + * { + * start: { line: 5, character: 23 } + * end : { line 6, character : 0 } + * } + * ``` + */ +type Range struct { + + /*Start defined: + * The range's start position + */ + Start Position `json:"start"` + + /*End defined: + * The range's end position. + */ + End Position `json:"end"` +} + +/*Location defined: + * Represents a location inside a resource, such as a line + * inside a text file. + */ +type Location struct { + + // URI is + URI DocumentURI `json:"uri"` + + // Range is + Range Range `json:"range"` +} + +/*LocationLink defined: + * Represents the connection of two locations. Provides additional metadata over normal [locations](#Location), + * including an origin range. + */ +type LocationLink struct { + + /*OriginSelectionRange defined: + * Span of the origin of this link. + * + * Used as the underlined span for mouse definition hover. Defaults to the word range at + * the definition position. + */ + OriginSelectionRange *Range `json:"originSelectionRange,omitempty"` + + /*TargetURI defined: + * The target resource identifier of this link. + */ + TargetURI DocumentURI `json:"targetUri"` + + /*TargetRange defined: + * The full target range of this link. If the target for example is a symbol then target range is the + * range enclosing this symbol not including leading/trailing whitespace but everything else + * like comments. This information is typically used to highlight the range in the editor. + */ + TargetRange Range `json:"targetRange"` + + /*TargetSelectionRange defined: + * The range that should be selected and revealed when this link is being followed, e.g the name of a function. + * Must be contained by the the `targetRange`. See also `DocumentSymbol#range` + */ + TargetSelectionRange Range `json:"targetSelectionRange"` +} + +/*Color defined: + * Represents a color in RGBA space. + */ +type Color struct { + + /*Red defined: + * The red component of this color in the range [0-1]. + */ + Red float64 `json:"red"` + + /*Green defined: + * The green component of this color in the range [0-1]. + */ + Green float64 `json:"green"` + + /*Blue defined: + * The blue component of this color in the range [0-1]. + */ + Blue float64 `json:"blue"` + + /*Alpha defined: + * The alpha component of this color in the range [0-1]. + */ + Alpha float64 `json:"alpha"` +} + +/*ColorInformation defined: + * Represents a color range from a document. + */ +type ColorInformation struct { + + /*Range defined: + * The range in the document where this color appers. + */ + Range Range `json:"range"` + + /*Color defined: + * The actual color value for this color range. + */ + Color Color `json:"color"` +} + +// ColorPresentation is +type ColorPresentation struct { + + /*Label defined: + * The label of this color presentation. It will be shown on the color + * picker header. By default this is also the text that is inserted when selecting + * this color presentation. + */ + Label string `json:"label"` + + /*TextEdit defined: + * An [edit](#TextEdit) which is applied to a document when selecting + * this presentation for the color. When `falsy` the [label](#ColorPresentation.label) + * is used. + */ + TextEdit *TextEdit `json:"textEdit,omitempty"` + + /*AdditionalTextEdits defined: + * An optional array of additional [text edits](#TextEdit) that are applied when + * selecting this color presentation. Edits must not overlap with the main [edit](#ColorPresentation.textEdit) nor with themselves. + */ + AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"` +} + +/*DiagnosticRelatedInformation defined: + * Represents a related message and source code location for a diagnostic. This should be + * used to point to code locations that cause or related to a diagnostics, e.g when duplicating + * a symbol in a scope. + */ +type DiagnosticRelatedInformation struct { + + /*Location defined: + * The location of this related diagnostic information. + */ + Location Location `json:"location"` + + /*Message defined: + * The message of this related diagnostic information. + */ + Message string `json:"message"` +} + +/*Diagnostic defined: + * Represents a diagnostic, such as a compiler error or warning. Diagnostic objects + * are only valid in the scope of a resource. + */ +type Diagnostic struct { + + /*Range defined: + * The range at which the message applies + */ + Range Range `json:"range"` + + /*Severity defined: + * The diagnostic's severity. Can be omitted. If omitted it is up to the + * client to interpret diagnostics as error, warning, info or hint. + */ + Severity DiagnosticSeverity `json:"severity,omitempty"` + + /*Code defined: + * The diagnostic's code, which usually appear in the user interface. + */ + Code interface{} `json:"code,omitempty"` // number | string + + /*Source defined: + * A human-readable string describing the source of this + * diagnostic, e.g. 'typescript' or 'super lint'. It usually + * appears in the user interface. + */ + Source string `json:"source,omitempty"` + + /*Message defined: + * The diagnostic's message. It usually appears in the user interface + */ + Message string `json:"message"` + + /*Tags defined: + * Additional metadata about the diagnostic. + */ + Tags []DiagnosticTag `json:"tags,omitempty"` + + /*RelatedInformation defined: + * An array of related diagnostic information, e.g. when symbol-names within + * a scope collide all definitions can be marked via this property. + */ + RelatedInformation []DiagnosticRelatedInformation `json:"relatedInformation,omitempty"` +} + +/*Command defined: + * Represents a reference to a command. Provides a title which + * will be used to represent a command in the UI and, optionally, + * an array of arguments which will be passed to the command handler + * function when invoked. + */ +type Command struct { + + /*Title defined: + * Title of the command, like `save`. + */ + Title string `json:"title"` + + /*Command defined: + * The identifier of the actual command handler. + */ + Command string `json:"command"` + + /*Arguments defined: + * Arguments that the command handler should be + * invoked with. + */ + Arguments []interface{} `json:"arguments,omitempty"` +} + +/*TextEdit defined: + * A text edit applicable to a text document. + */ +type TextEdit struct { + + /*Range defined: + * The range of the text document to be manipulated. To insert + * text into a document create a range where start === end. + */ + Range Range `json:"range"` + + /*NewText defined: + * The string to be inserted. For delete operations use an + * empty string. + */ + NewText string `json:"newText"` +} + +/*TextDocumentEdit defined: + * Describes textual changes on a text document. + */ +type TextDocumentEdit struct { + + /*TextDocument defined: + * The text document to change. + */ + TextDocument VersionedTextDocumentIdentifier `json:"textDocument"` + + /*Edits defined: + * The edits to be applied. + */ + Edits []TextEdit `json:"edits"` +} + +// ResourceOperation is +type ResourceOperation struct { + + // Kind is + Kind string `json:"kind"` +} + +/*CreateFileOptions defined: + * Options to create a file. + */ +type CreateFileOptions struct { + + /*Overwrite defined: + * Overwrite existing file. Overwrite wins over `ignoreIfExists` + */ + Overwrite bool `json:"overwrite,omitempty"` + + /*IgnoreIfExists defined: + * Ignore if exists. + */ + IgnoreIfExists bool `json:"ignoreIfExists,omitempty"` +} + +/*CreateFile defined: + * Create file operation. + */ +type CreateFile struct { + + /*Kind defined: + * A create + */ + Kind string `json:"kind"` // 'create' + + /*URI defined: + * The resource to create. + */ + URI DocumentURI `json:"uri"` + + /*Options defined: + * Additional options + */ + Options *CreateFileOptions `json:"options,omitempty"` +} + +/*RenameFileOptions defined: + * Rename file options + */ +type RenameFileOptions struct { + + /*Overwrite defined: + * Overwrite target if existing. Overwrite wins over `ignoreIfExists` + */ + Overwrite bool `json:"overwrite,omitempty"` + + /*IgnoreIfExists defined: + * Ignores if target exists. + */ + IgnoreIfExists bool `json:"ignoreIfExists,omitempty"` +} + +/*RenameFile defined: + * Rename file operation + */ +type RenameFile struct { + + /*Kind defined: + * A rename + */ + Kind string `json:"kind"` // 'rename' + + /*OldURI defined: + * The old (existing) location. + */ + OldURI DocumentURI `json:"oldUri"` + + /*NewURI defined: + * The new location. + */ + NewURI DocumentURI `json:"newUri"` + + /*Options defined: + * Rename options. + */ + Options *RenameFileOptions `json:"options,omitempty"` +} + +/*DeleteFileOptions defined: + * Delete file options + */ +type DeleteFileOptions struct { + + /*Recursive defined: + * Delete the content recursively if a folder is denoted. + */ + Recursive bool `json:"recursive,omitempty"` + + /*IgnoreIfNotExists defined: + * Ignore the operation if the file doesn't exist. + */ + IgnoreIfNotExists bool `json:"ignoreIfNotExists,omitempty"` +} + +/*DeleteFile defined: + * Delete file operation + */ +type DeleteFile struct { + + /*Kind defined: + * A delete + */ + Kind string `json:"kind"` // 'delete' + + /*URI defined: + * The file to delete. + */ + URI DocumentURI `json:"uri"` + + /*Options defined: + * Delete options. + */ + Options *DeleteFileOptions `json:"options,omitempty"` +} + +/*WorkspaceEdit defined: + * A workspace edit represents changes to many resources managed in the workspace. The edit + * should either provide `changes` or `documentChanges`. If documentChanges are present + * they are preferred over `changes` if the client can handle versioned document edits. + */ +type WorkspaceEdit struct { + + /*Changes defined: + * Holds changes to existing resources. + */ + Changes *map[string][]TextEdit `json:"changes,omitempty"` // [uri: string]: TextEdit[]; + + /*DocumentChanges defined: + * Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes + * are either an array of `TextDocumentEdit`s to express changes to n different text documents + * where each text document edit addresses a specific version of a text document. Or it can contain + * above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations. + * + * Whether a client supports versioned document edits is expressed via + * `workspace.workspaceEdit.documentChanges` client capability. + * + * If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then + * only plain `TextEdit`s using the `changes` property are supported. + */ + DocumentChanges []TextDocumentEdit `json:"documentChanges,omitempty"` // (TextDocumentEdit | CreateFile | RenameFile | DeleteFile) +} + +/*TextEditChange defined: + * A change to capture text edits for existing resources. + */ +type TextEditChange struct { +} + +/*TextDocumentIdentifier defined: + * A literal to identify a text document in the client. + */ +type TextDocumentIdentifier struct { + + /*URI defined: + * The text document's uri. + */ + URI DocumentURI `json:"uri"` +} + +/*VersionedTextDocumentIdentifier defined: + * An identifier to denote a specific version of a text document. + */ +type VersionedTextDocumentIdentifier struct { + + /*Version defined: + * The version number of this document. If a versioned text document identifier + * is sent from the server to the client and the file is not open in the editor + * (the server has not received an open notification before) the server can send + * `null` to indicate that the version is unknown and the content on disk is the + * truth (as speced with document content ownership). + */ + Version float64 `json:"version"` + TextDocumentIdentifier +} + +/*TextDocumentItem defined: + * An item to transfer a text document from the client to the + * server. + */ +type TextDocumentItem struct { + + /*URI defined: + * The text document's uri. + */ + URI DocumentURI `json:"uri"` + + /*LanguageID defined: + * The text document's language identifier + */ + LanguageID string `json:"languageId"` + + /*Version defined: + * The version number of this document (it will increase after each + * change, including undo/redo). + */ + Version float64 `json:"version"` + + /*Text defined: + * The content of the opened text document. + */ + Text string `json:"text"` +} + +/*MarkupContent defined: + * A `MarkupContent` literal represents a string value which content is interpreted base on its + * kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds. + * + * If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues. + * See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting + * + * Here is an example how such a string can be constructed using JavaScript / TypeScript: + * ```ts + * let markdown: MarkdownContent = { + * kind: MarkupKind.Markdown, + * value: [ + * '# Header', + * 'Some text', + * '```typescript', + * 'someCode();', + * '```' + * ].join('\n') + * }; + * ``` + * + * *Please Note* that clients might sanitize the return markdown. A client could decide to + * remove HTML from the markdown to avoid script execution. + */ +type MarkupContent struct { + + /*Kind defined: + * The type of the Markup + */ + Kind MarkupKind `json:"kind"` + + /*Value defined: + * The content itself + */ + Value string `json:"value"` +} + +/*CompletionItem defined: + * A completion item represents a text snippet that is + * proposed to complete text that is being typed. + */ +type CompletionItem struct { + + /*Label defined: + * The label of this completion item. By default + * also the text that is inserted when selecting + * this completion. + */ + Label string `json:"label"` + + /*Kind defined: + * The kind of this completion item. Based of the kind + * an icon is chosen by the editor. + */ + Kind CompletionItemKind `json:"kind,omitempty"` + + /*Tags defined: + * Tags for this completion item. + * + * @since 3.15.0 + */ + Tags []CompletionItemTag `json:"tags,omitempty"` + + /*Detail defined: + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + Detail string `json:"detail,omitempty"` + + /*Documentation defined: + * A human-readable string that represents a doc-comment. + */ + Documentation string `json:"documentation,omitempty"` // string | MarkupContent + + /*Deprecated defined: + * Indicates if this item is deprecated. + * @deprecated Use `tags` instead. + */ + Deprecated bool `json:"deprecated,omitempty"` + + /*Preselect defined: + * Select this item when showing. + * + * *Note* that only one completion item can be selected and that the + * tool / client decides which item that is. The rule is that the *first* + * item of those that match best is selected. + */ + Preselect bool `json:"preselect,omitempty"` + + /*SortText defined: + * A string that should be used when comparing this item + * with other items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + SortText string `json:"sortText,omitempty"` + + /*FilterText defined: + * A string that should be used when filtering a set of + * completion items. When `falsy` the [label](#CompletionItem.label) + * is used. + */ + FilterText string `json:"filterText,omitempty"` + + /*InsertText defined: + * A string that should be inserted into a document when selecting + * this completion. When `falsy` the [label](#CompletionItem.label) + * is used. + * + * The `insertText` is subject to interpretation by the client side. + * Some tools might not take the string literally. For example + * VS Code when code complete is requested in this example `con` + * and a completion item with an `insertText` of `console` is provided it + * will only insert `sole`. Therefore it is recommended to use `textEdit` instead + * since it avoids additional client side interpretation. + */ + InsertText string `json:"insertText,omitempty"` + + /*InsertTextFormat defined: + * The format of the insert text. The format applies to both the `insertText` property + * and the `newText` property of a provided `textEdit`. + */ + InsertTextFormat InsertTextFormat `json:"insertTextFormat,omitempty"` + + /*TextEdit defined: + * An [edit](#TextEdit) which is applied to a document when selecting + * this completion. When an edit is provided the value of + * [insertText](#CompletionItem.insertText) is ignored. + * + * *Note:* The text edit's range must be a [single line] and it must contain the position + * at which completion has been requested. + */ + TextEdit *TextEdit `json:"textEdit,omitempty"` + + /*AdditionalTextEdits defined: + * An optional array of additional [text edits](#TextEdit) that are applied when + * selecting this completion. Edits must not overlap (including the same insert position) + * with the main [edit](#CompletionItem.textEdit) nor with themselves. + * + * Additional text edits should be used to change text unrelated to the current cursor position + * (for example adding an import statement at the top of the file if the completion item will + * insert an unqualified type). + */ + AdditionalTextEdits []TextEdit `json:"additionalTextEdits,omitempty"` + + /*CommitCharacters defined: + * An optional set of characters that when pressed while this completion is active will accept it first and + * then type that character. *Note* that all commit characters should have `length=1` and that superfluous + * characters will be ignored. + */ + CommitCharacters []string `json:"commitCharacters,omitempty"` + + /*Command defined: + * An optional [command](#Command) that is executed *after* inserting this completion. *Note* that + * additional modifications to the current document should be described with the + * [additionalTextEdits](#CompletionItem.additionalTextEdits)-property. + */ + Command *Command `json:"command,omitempty"` + + /*Data defined: + * An data entry field that is preserved on a completion item between + * a [CompletionRequest](#CompletionRequest) and a [CompletionResolveRequest] + * (#CompletionResolveRequest) + */ + Data interface{} `json:"data,omitempty"` +} + +/*CompletionList defined: + * Represents a collection of [completion items](#CompletionItem) to be presented + * in the editor. + */ +type CompletionList struct { + + /*IsIncomplete defined: + * This list it not complete. Further typing results in recomputing this list. + */ + IsIncomplete bool `json:"isIncomplete"` + + /*Items defined: + * The completion items. + */ + Items []CompletionItem `json:"items"` +} + +/*Hover defined: + * The result of a hover request. + */ +type Hover struct { + + /*Contents defined: + * The hover's content + */ + Contents MarkupContent `json:"contents"` // MarkupContent | MarkedString | MarkedString[] + + /*Range defined: + * An optional range + */ + Range *Range `json:"range,omitempty"` +} + +/*ParameterInformation defined: + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ +type ParameterInformation struct { + + /*Label defined: + * The label of this parameter information. + * + * Either a string or an inclusive start and exclusive end offsets within its containing + * signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 + * string representation as `Position` and `Range` does. + * + * *Note*: a label of type string should be a substring of its containing signature label. + * Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. + */ + Label string `json:"label"` // string | [number, number] + + /*Documentation defined: + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + Documentation string `json:"documentation,omitempty"` // string | MarkupContent +} + +/*SignatureInformation defined: + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ +type SignatureInformation struct { + + /*Label defined: + * The label of this signature. Will be shown in + * the UI. + */ + Label string `json:"label"` + + /*Documentation defined: + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + Documentation string `json:"documentation,omitempty"` // string | MarkupContent + + /*Parameters defined: + * The parameters of this signature. + */ + Parameters []ParameterInformation `json:"parameters,omitempty"` +} + +/*SignatureHelp defined: + * Signature help represents the signature of something + * callable. There can be multiple signature but only one + * active and only one active parameter. + */ +type SignatureHelp struct { + + /*Signatures defined: + * One or more signatures. + */ + Signatures []SignatureInformation `json:"signatures"` + + /*ActiveSignature defined: + * The active signature. Set to `null` if no + * signatures exist. + */ + ActiveSignature float64 `json:"activeSignature"` + + /*ActiveParameter defined: + * The active parameter of the active signature. Set to `null` + * if the active signature has no parameters. + */ + ActiveParameter float64 `json:"activeParameter"` +} + +/*ReferenceContext defined: + * Value-object that contains additional information when + * requesting references. + */ +type ReferenceContext struct { + + /*IncludeDeclaration defined: + * Include the declaration of the current symbol. + */ + IncludeDeclaration bool `json:"includeDeclaration"` +} + +/*DocumentHighlight defined: + * A document highlight is a range inside a text document which deserves + * special attention. Usually a document highlight is visualized by changing + * the background color of its range. + */ +type DocumentHighlight struct { + + /*Range defined: + * The range this highlight applies to. + */ + Range Range `json:"range"` + + /*Kind defined: + * The highlight kind, default is [text](#DocumentHighlightKind.Text). + */ + Kind *DocumentHighlightKind `json:"kind,omitempty"` +} + +/*SymbolInformation defined: + * Represents information about programming constructs like variables, classes, + * interfaces etc. + */ +type SymbolInformation struct { + + /*Name defined: + * The name of this symbol. + */ + Name string `json:"name"` + + /*Kind defined: + * The kind of this symbol. + */ + Kind SymbolKind `json:"kind"` + + /*Deprecated defined: + * Indicates if this symbol is deprecated. + */ + Deprecated bool `json:"deprecated,omitempty"` + + /*Location defined: + * The location of this symbol. The location's range is used by a tool + * to reveal the location in the editor. If the symbol is selected in the + * tool the range's start information is used to position the cursor. So + * the range usually spans more than the actual symbol's name and does + * normally include thinks like visibility modifiers. + * + * The range doesn't have to denote a node range in the sense of a abstract + * syntax tree. It can therefore not be used to re-construct a hierarchy of + * the symbols. + */ + Location Location `json:"location"` + + /*ContainerName defined: + * The name of the symbol containing this symbol. This information is for + * user interface purposes (e.g. to render a qualifier in the user interface + * if necessary). It can't be used to re-infer a hierarchy for the document + * symbols. + */ + ContainerName string `json:"containerName,omitempty"` +} + +/*DocumentSymbol defined: + * Represents programming constructs like variables, classes, interfaces etc. + * that appear in a document. Document symbols can be hierarchical and they + * have two ranges: one that encloses its definition and one that points to + * its most interesting range, e.g. the range of an identifier. + */ +type DocumentSymbol struct { + + /*Name defined: + * The name of this symbol. Will be displayed in the user interface and therefore must not be + * an empty string or a string only consisting of white spaces. + */ + Name string `json:"name"` + + /*Detail defined: + * More detail for this symbol, e.g the signature of a function. + */ + Detail string `json:"detail,omitempty"` + + /*Kind defined: + * The kind of this symbol. + */ + Kind SymbolKind `json:"kind"` + + /*Deprecated defined: + * Indicates if this symbol is deprecated. + */ + Deprecated bool `json:"deprecated,omitempty"` + + /*Range defined: + * The range enclosing this symbol not including leading/trailing whitespace but everything else + * like comments. This information is typically used to determine if the the clients cursor is + * inside the symbol to reveal in the symbol in the UI. + */ + Range Range `json:"range"` + + /*SelectionRange defined: + * The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. + * Must be contained by the the `range`. + */ + SelectionRange Range `json:"selectionRange"` + + /*Children defined: + * Children of this symbol, e.g. properties of a class. + */ + Children []DocumentSymbol `json:"children,omitempty"` +} + +/*CodeActionContext defined: + * Contains additional diagnostic information about the context in which + * a [code action](#CodeActionProvider.provideCodeActions) is run. + */ +type CodeActionContext struct { + + /*Diagnostics defined: + * An array of diagnostics known on the client side overlapping the range provided to the + * `textDocument/codeAction` request. They are provied so that the server knows which + * errors are currently presented to the user for the given range. There is no guarantee + * that these accurately reflect the error state of the resource. The primary parameter + * to compute code actions is the provided range. + */ + Diagnostics []Diagnostic `json:"diagnostics"` + + /*Only defined: + * Requested kind of actions to return. + * + * Actions not of this kind are filtered out by the client before being shown. So servers + * can omit computing them. + */ + Only []CodeActionKind `json:"only,omitempty"` +} + +/*CodeAction defined: + * A code action represents a change that can be performed in code, e.g. to fix a problem or + * to refactor code. + * + * A CodeAction must set either `edit` and/or a `command`. If both are supplied, the `edit` is applied first, then the `command` is executed. + */ +type CodeAction struct { + + /*Title defined: + * A short, human-readable, title for this code action. + */ + Title string `json:"title"` + + /*Kind defined: + * The kind of the code action. + * + * Used to filter code actions. + */ + Kind CodeActionKind `json:"kind,omitempty"` + + /*Diagnostics defined: + * The diagnostics that this code action resolves. + */ + Diagnostics []Diagnostic `json:"diagnostics,omitempty"` + + /*IsPreferred defined: + * Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted + * by keybindings. + * + * A quick fix should be marked preferred if it properly addresses the underlying error. + * A refactoring should be marked preferred if it is the most reasonable choice of actions to take. + * + * @since 3.15.0 + */ + IsPreferred bool `json:"isPreferred,omitempty"` + + /*Edit defined: + * The workspace edit this code action performs. + */ + Edit *WorkspaceEdit `json:"edit,omitempty"` + + /*Command defined: + * A command this code action executes. If a code action + * provides a edit and a command, first the edit is + * executed and then the command. + */ + Command *Command `json:"command,omitempty"` +} + +/*CodeLens defined: + * A code lens represents a [command](#Command) that should be shown along with + * source text, like the number of references, a way to run tests, etc. + * + * A code lens is _unresolved_ when no command is associated to it. For performance + * reasons the creation of a code lens and resolving should be done to two stages. + */ +type CodeLens struct { + + /*Range defined: + * The range in which this code lens is valid. Should only span a single line. + */ + Range Range `json:"range"` + + /*Command defined: + * The command this code lens represents. + */ + Command *Command `json:"command,omitempty"` + + /*Data defined: + * An data entry field that is preserved on a code lens item between + * a [CodeLensRequest](#CodeLensRequest) and a [CodeLensResolveRequest] + * (#CodeLensResolveRequest) + */ + Data interface{} `json:"data,omitempty"` +} + +/*FormattingOptions defined: + * Value-object describing what options formatting should use. + */ +type FormattingOptions struct { + + /*TabSize defined: + * Size of a tab in spaces. + */ + TabSize float64 `json:"tabSize"` + + /*InsertSpaces defined: + * Prefer spaces over tabs. + */ + InsertSpaces bool `json:"insertSpaces"` + + /*TrimTrailingWhitespace defined: + * Trim trailing whitespaces on a line. + * + * @since 3.15.0 + */ + TrimTrailingWhitespace bool `json:"trimTrailingWhitespace,omitempty"` + + /*InsertFinalNewline defined: + * Insert a newline character at the end of the file if one does not exist. + * + * @since 3.15.0 + */ + InsertFinalNewline bool `json:"insertFinalNewline,omitempty"` + + /*TrimFinalNewlines defined: + * Trim all newlines after the final newline at the end of the file. + * + * @since 3.15.0 + */ + TrimFinalNewlines bool `json:"trimFinalNewlines,omitempty"` + + /*Key defined: + * Signature for further properties. + */ + Key map[string]bool `json:"key"` // [key: string]: boolean | number | string | undefined; +} + +/*DocumentLink defined: + * A document link is a range in a text document that links to an internal or external resource, like another + * text document or a web site. + */ +type DocumentLink struct { + + /*Range defined: + * The range this link applies to. + */ + Range Range `json:"range"` + + /*Target defined: + * The uri this link points to. + */ + Target string `json:"target,omitempty"` + + /*Tooltip defined: + * The tooltip text when you hover over this link. + * + * If a tooltip is provided, is will be displayed in a string that includes instructions on how to + * trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS, + * user settings, and localization. + * + * @since 3.15.0 + */ + Tooltip string `json:"tooltip,omitempty"` + + /*Data defined: + * A data entry field that is preserved on a document link between a + * DocumentLinkRequest and a DocumentLinkResolveRequest. + */ + Data interface{} `json:"data,omitempty"` +} + +/*SelectionRange defined: + * A selection range represents a part of a selection hierarchy. A selection range + * may have a parent selection range that contains it. + */ +type SelectionRange struct { + + /*Range defined: + * The [range](#Range) of this selection range. + */ + Range Range `json:"range"` + + /*Parent defined: + * The parent selection range containing this range. Therefore `parent.range` must contain `this.range`. + */ + Parent *SelectionRange `json:"parent,omitempty"` +} + +/*TextDocument defined: + * A simple text document. Not to be implemented. + */ +type TextDocument struct { + + /*URI defined: + * The associated URI for this document. Most documents have the __file__-scheme, indicating that they + * represent files on disk. However, some documents may have other schemes indicating that they are not + * available on disk. + * + * @readonly + */ + URI DocumentURI `json:"uri"` + + /*LanguageID defined: + * The identifier of the language associated with this document. + * + * @readonly + */ + LanguageID string `json:"languageId"` + + /*Version defined: + * The version number of this document (it will increase after each + * change, including undo/redo). + * + * @readonly + */ + Version float64 `json:"version"` + + /*LineCount defined: + * The number of lines in this document. + * + * @readonly + */ + LineCount float64 `json:"lineCount"` +} + +/*TextDocumentChangeEvent defined: + * Event to signal changes to a simple text document. + */ +type TextDocumentChangeEvent struct { + + /*Document defined: + * The document that has changed. + */ + Document TextDocument `json:"document"` +} + +// TextDocumentWillSaveEvent is +type TextDocumentWillSaveEvent struct { + + /*Document defined: + * The document that will be saved + */ + Document TextDocument `json:"document"` + + /*Reason defined: + * The reason why save was triggered. + */ + Reason TextDocumentSaveReason `json:"reason"` +} + +/*TextDocumentContentChangeEvent defined: + * An event describing a change to a text document. If range and rangeLength are omitted + * the new text is considered to be the full content of the document. + */ +type TextDocumentContentChangeEvent struct { + + /*Range defined: + * The range of the document that changed. + */ + Range *Range `json:"range,omitempty"` + + /*RangeLength defined: + * The length of the range that got replaced. + */ + RangeLength float64 `json:"rangeLength,omitempty"` + + /*Text defined: + * The new text of the document. + */ + Text string `json:"text"` +} + +// ProgressParams is +type ProgressParams struct { + + /*Token defined: + * The progress token provided by the client or server. + */ + Token ProgressToken `json:"token"` + + /*Value defined: + * The progress data. + */ + Value interface{} `json:"value"` +} + +// SetTraceParams is +type SetTraceParams struct { + + // Value is + Value TraceValues `json:"value"` +} + +// LogTraceParams is +type LogTraceParams struct { + + // Message is + Message string `json:"message"` + + // Verbose is + Verbose string `json:"verbose,omitempty"` +} + +// Tracer is +type Tracer struct { +} + +// FoldingRangeKind defines constants +type FoldingRangeKind string + +// ResourceOperationKind defines constants +type ResourceOperationKind string + +// FailureHandlingKind defines constants +type FailureHandlingKind string + +// InitializeError defines constants +type InitializeError float64 + +// MessageType defines constants +type MessageType float64 + +// TextDocumentSyncKind defines constants +type TextDocumentSyncKind float64 + +// FileChangeType defines constants +type FileChangeType float64 + +// WatchKind defines constants +type WatchKind float64 + +// CompletionTriggerKind defines constants +type CompletionTriggerKind float64 + +// SignatureHelpTriggerKind defines constants +type SignatureHelpTriggerKind float64 + +// DiagnosticSeverity defines constants +type DiagnosticSeverity float64 + +// DiagnosticTag defines constants +type DiagnosticTag float64 + +// MarkupKind defines constants +type MarkupKind string + +// CompletionItemKind defines constants +type CompletionItemKind float64 + +// InsertTextFormat defines constants +type InsertTextFormat float64 + +// CompletionItemTag defines constants +type CompletionItemTag float64 + +// DocumentHighlightKind defines constants +type DocumentHighlightKind float64 + +// SymbolKind defines constants +type SymbolKind float64 + +// CodeActionKind defines constants +type CodeActionKind string + +// TextDocumentSaveReason defines constants +type TextDocumentSaveReason float64 + +// ErrorCodes defines constants +type ErrorCodes float64 + +// Touch defines constants +type Touch float64 + +// Trace defines constants +type Trace string + +// TraceFormat defines constants +type TraceFormat string + +// ConnectionErrors defines constants +type ConnectionErrors float64 + +// ConnectionState defines constants +type ConnectionState float64 + +const ( + + /*Comment defined: + * Folding range for a comment + */ + Comment FoldingRangeKind = "comment" + + /*Imports defined: + * Folding range for a imports or includes + */ + Imports FoldingRangeKind = "imports" + + /*Region defined: + * Folding range for a region (e.g. `#region`) + */ + Region FoldingRangeKind = "region" + + /*Create defined: + * Supports creating new files and folders. + */ + Create ResourceOperationKind = "create" + + /*Rename defined: + * Supports renaming existing files and folders. + */ + Rename ResourceOperationKind = "rename" + + /*Delete defined: + * Supports deleting existing files and folders. + */ + Delete ResourceOperationKind = "delete" + + /*Abort defined: + * Applying the workspace change is simply aborted if one of the changes provided + * fails. All operations executed before the failing operation stay executed. + */ + Abort FailureHandlingKind = "abort" + + /*Transactional defined: + * All operations are executed transactional. That means they either all + * succeed or no changes at all are applied to the workspace. + */ + Transactional FailureHandlingKind = "transactional" + + /*TextOnlyTransactional defined: + * If the workspace edit contains only textual file changes they are executed transactional. + * If resource changes (create, rename or delete file) are part of the change the failure + * handling startegy is abort. + */ + TextOnlyTransactional FailureHandlingKind = "textOnlyTransactional" + + /*Undo defined: + * The client tries to undo the operations already executed. But there is no + * guaruntee that this is succeeding. + */ + Undo FailureHandlingKind = "undo" + + /*UnknownProtocolVersion defined: + * If the protocol version provided by the client can't be handled by the server. + * @deprecated This initialize error got replaced by client capabilities. There is + * no version handshake in version 3.0x + */ + UnknownProtocolVersion InitializeError = 1 + + /*Error defined: + * An error message. + */ + Error MessageType = 1 + + /*Warning defined: + * A warning message. + */ + Warning MessageType = 2 + + /*Info defined: + * An information message. + */ + Info MessageType = 3 + + /*Log defined: + * A log message. + */ + Log MessageType = 4 + + /*None defined: + * Documents should not be synced at all. + */ + None TextDocumentSyncKind = 0 + + /*Full defined: + * Documents are synced by always sending the full content + * of the document. + */ + Full TextDocumentSyncKind = 1 + + /*Incremental defined: + * Documents are synced by sending the full content on open. + * After that only incremental updates to the document are + * send. + */ + Incremental TextDocumentSyncKind = 2 + + /*Created defined: + * The file got created. + */ + Created FileChangeType = 1 + + /*Changed defined: + * The file got changed. + */ + Changed FileChangeType = 2 + + /*Deleted defined: + * The file got deleted. + */ + Deleted FileChangeType = 3 + + /*WatchCreate defined: + * Interested in create events. + */ + WatchCreate WatchKind = 1 + + /*WatchChange defined: + * Interested in change events + */ + WatchChange WatchKind = 2 + + /*WatchDelete defined: + * Interested in delete events + */ + WatchDelete WatchKind = 4 + + /*Invoked defined: + * Completion was triggered by typing an identifier (24x7 code + * complete), manual invocation (e.g Ctrl+Space) or via API. + */ + Invoked CompletionTriggerKind = 1 + + /*TriggerCharacter defined: + * Completion was triggered by a trigger character specified by + * the `triggerCharacters` properties of the `CompletionRegistrationOptions`. + */ + TriggerCharacter CompletionTriggerKind = 2 + + /*TriggerForIncompleteCompletions defined: + * Completion was re-triggered as current completion list is incomplete + */ + TriggerForIncompleteCompletions CompletionTriggerKind = 3 + + /*ContentChange defined: + * Signature help was triggered by the cursor moving or by the document content changing. + */ + ContentChange SignatureHelpTriggerKind = 3 + + /*SeverityError defined: + * Reports an error. + */ + SeverityError DiagnosticSeverity = 1 + + /*SeverityWarning defined: + * Reports a warning. + */ + SeverityWarning DiagnosticSeverity = 2 + + /*SeverityInformation defined: + * Reports an information. + */ + SeverityInformation DiagnosticSeverity = 3 + + /*SeverityHint defined: + * Reports a hint. + */ + SeverityHint DiagnosticSeverity = 4 + + /*Unnecessary defined: + * Unused or unnecessary code. + * + * Clients are allowed to render diagnostics with this tag faded out instead of having + * an error squiggle. + */ + Unnecessary DiagnosticTag = 1 + + /*Deprecated defined: + * Deprecated or obsolete code. + * + * Clients are allowed to rendered diagnostics with this tag strike through. + */ + Deprecated DiagnosticTag = 2 + + /*PlainText defined: + * Plain text is supported as a content format + */ + PlainText MarkupKind = "plaintext" + + /*Markdown defined: + * Markdown is supported as a content format + */ + Markdown MarkupKind = "markdown" + + // TextCompletion is + TextCompletion CompletionItemKind = 1 + + // MethodCompletion is + MethodCompletion CompletionItemKind = 2 + + // FunctionCompletion is + FunctionCompletion CompletionItemKind = 3 + + // ConstructorCompletion is + ConstructorCompletion CompletionItemKind = 4 + + // FieldCompletion is + FieldCompletion CompletionItemKind = 5 + + // VariableCompletion is + VariableCompletion CompletionItemKind = 6 + + // ClassCompletion is + ClassCompletion CompletionItemKind = 7 + + // InterfaceCompletion is + InterfaceCompletion CompletionItemKind = 8 + + // ModuleCompletion is + ModuleCompletion CompletionItemKind = 9 + + // PropertyCompletion is + PropertyCompletion CompletionItemKind = 10 + + // UnitCompletion is + UnitCompletion CompletionItemKind = 11 + + // ValueCompletion is + ValueCompletion CompletionItemKind = 12 + + // EnumCompletion is + EnumCompletion CompletionItemKind = 13 + + // KeywordCompletion is + KeywordCompletion CompletionItemKind = 14 + + // SnippetCompletion is + SnippetCompletion CompletionItemKind = 15 + + // ColorCompletion is + ColorCompletion CompletionItemKind = 16 + + // FileCompletion is + FileCompletion CompletionItemKind = 17 + + // ReferenceCompletion is + ReferenceCompletion CompletionItemKind = 18 + + // FolderCompletion is + FolderCompletion CompletionItemKind = 19 + + // EnumMemberCompletion is + EnumMemberCompletion CompletionItemKind = 20 + + // ConstantCompletion is + ConstantCompletion CompletionItemKind = 21 + + // StructCompletion is + StructCompletion CompletionItemKind = 22 + + // EventCompletion is + EventCompletion CompletionItemKind = 23 + + // OperatorCompletion is + OperatorCompletion CompletionItemKind = 24 + + // TypeParameterCompletion is + TypeParameterCompletion CompletionItemKind = 25 + + /*PlainTextTextFormat defined: + * The primary text to be inserted is treated as a plain string. + */ + PlainTextTextFormat InsertTextFormat = 1 + + /*SnippetTextFormat defined: + * The primary text to be inserted is treated as a snippet. + * + * A snippet can define tab stops and placeholders with `$1`, `$2` + * and `${3:foo}`. `$0` defines the final tab stop, it defaults to + * the end of the snippet. Placeholders with equal identifiers are linked, + * that is typing in one will update others too. + * + * See also: https://github.com/Microsoft/vscode/blob/master/src/vs/editor/contrib/snippet/common/snippet.md + */ + SnippetTextFormat InsertTextFormat = 2 + + /*Text defined: + * A textual occurrence. + */ + Text DocumentHighlightKind = 1 + + /*Read defined: + * Read-access of a symbol, like reading a variable. + */ + Read DocumentHighlightKind = 2 + + /*Write defined: + * Write-access of a symbol, like writing to a variable. + */ + Write DocumentHighlightKind = 3 + + // File is + File SymbolKind = 1 + + // Module is + Module SymbolKind = 2 + + // Namespace is + Namespace SymbolKind = 3 + + // Package is + Package SymbolKind = 4 + + // Class is + Class SymbolKind = 5 + + // Method is + Method SymbolKind = 6 + + // Property is + Property SymbolKind = 7 + + // Field is + Field SymbolKind = 8 + + // Constructor is + Constructor SymbolKind = 9 + + // Enum is + Enum SymbolKind = 10 + + // Interface is + Interface SymbolKind = 11 + + // Function is + Function SymbolKind = 12 + + // Variable is + Variable SymbolKind = 13 + + // Constant is + Constant SymbolKind = 14 + + // String is + String SymbolKind = 15 + + // Number is + Number SymbolKind = 16 + + // Boolean is + Boolean SymbolKind = 17 + + // Array is + Array SymbolKind = 18 + + // Object is + Object SymbolKind = 19 + + // Key is + Key SymbolKind = 20 + + // Null is + Null SymbolKind = 21 + + // EnumMember is + EnumMember SymbolKind = 22 + + // Struct is + Struct SymbolKind = 23 + + // Event is + Event SymbolKind = 24 + + // Operator is + Operator SymbolKind = 25 + + // TypeParameter is + TypeParameter SymbolKind = 26 + + /*Empty defined: + * Empty kind. + */ + Empty CodeActionKind = "" + + /*QuickFix defined: + * Base kind for quickfix actions: 'quickfix' + */ + QuickFix CodeActionKind = "quickfix" + + /*Refactor defined: + * Base kind for refactoring actions: 'refactor' + */ + Refactor CodeActionKind = "refactor" + + /*RefactorExtract defined: + * Base kind for refactoring extraction actions: 'refactor.extract' + * + * Example extract actions: + * + * - Extract method + * - Extract function + * - Extract variable + * - Extract interface from class + * - ... + */ + RefactorExtract CodeActionKind = "refactor.extract" + + /*RefactorInline defined: + * Base kind for refactoring inline actions: 'refactor.inline' + * + * Example inline actions: + * + * - Inline function + * - Inline variable + * - Inline constant + * - ... + */ + RefactorInline CodeActionKind = "refactor.inline" + + /*RefactorRewrite defined: + * Base kind for refactoring rewrite actions: 'refactor.rewrite' + * + * Example rewrite actions: + * + * - Convert JavaScript function to class + * - Add or remove parameter + * - Encapsulate field + * - Make method static + * - Move method to base class + * - ... + */ + RefactorRewrite CodeActionKind = "refactor.rewrite" + + /*Source defined: + * Base kind for source actions: `source` + * + * Source code actions apply to the entire file. + */ + Source CodeActionKind = "source" + + /*SourceOrganizeImports defined: + * Base kind for an organize imports source action: `source.organizeImports` + */ + SourceOrganizeImports CodeActionKind = "source.organizeImports" + + /*Manual defined: + * Manually triggered, e.g. by the user pressing save, by starting debugging, + * or by an API call. + */ + Manual TextDocumentSaveReason = 1 + + /*AfterDelay defined: + * Automatic after a delay. + */ + AfterDelay TextDocumentSaveReason = 2 + + /*FocusOut defined: + * When the editor lost focus. + */ + FocusOut TextDocumentSaveReason = 3 + + // MessageWriteError is + MessageWriteError ErrorCodes = 1 + + // MessageReadError is + MessageReadError ErrorCodes = 2 + + // First is + First Touch = 1 + + // Last is + Last Touch = 2 + + // JSON is + JSON TraceFormat = "json" + + /*Closed defined: + * The connection is closed. + */ + Closed ConnectionErrors = 1 + + /*Disposed defined: + * The connection got disposed. + */ + Disposed ConnectionErrors = 2 + + /*AlreadyListening defined: + * The connection is already in listening mode. + */ + AlreadyListening ConnectionErrors = 3 + + // New is + New ConnectionState = 1 + + // Listening is + Listening ConnectionState = 2 +) + +// DocumentFilter is a type +/** + * A document filter denotes a document by different properties like + * the [language](#TextDocument.languageId), the [scheme](#Uri.scheme) of + * its resource, or a glob-pattern that is applied to the [path](#TextDocument.fileName). + * + * Glob patterns can have the following syntax: + * - `*` to match one or more characters in a path segment + * - `?` to match on one character in a path segment + * - `**` to match any number of path segments, including none + * - `{}` to group conditions (e.g. `**​/*.{ts,js}` matches all TypeScript and JavaScript files) + * - `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) + * - `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) + * + * @sample A language filter that applies to typescript files on disk: `{ language: 'typescript', scheme: 'file' }` + * @sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }` + */ +type DocumentFilter = struct { + + /*Language defined: A language id, like `typescript`. */ + Language string `json:"language,omitempty"` + + /*Scheme defined: A Uri [scheme](#Uri.scheme), like `file` or `untitled`. */ + Scheme string `json:"scheme,omitempty"` + + /*Pattern defined: A glob pattern, like `*.{ts,js}`. */ + Pattern string `json:"pattern,omitempty"` +} + +// DocumentSelector is a type +/** + * A document selector is the combination of one or many document filters. + * + * @sample `let sel:DocumentSelector = [{ language: 'typescript' }, { language: 'json', pattern: '**∕tsconfig.json' }]`; + */ +type DocumentSelector = []DocumentFilter + +// DocumentURI is a type +/** + * A tagging type for string properties that are actually URIs. + */ +type DocumentURI = string + +// MarkedString is a type +/** + * MarkedString can be used to render human readable text. It is either a markdown string + * or a code-block that provides a language and a code snippet. The language identifier + * is semantically equal to the optional language identifier in fenced code blocks in GitHub + * issues. See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting + * + * The pair of a language and a value is an equivalent to markdown: + * ```${language} + * ${value} + * ``` + * + * Note that markdown strings will be sanitized - that means html will be escaped. + * @deprecated use MarkupContent instead. + */ +type MarkedString = string + +// DefinitionLink is a type +/** + * Information about where a symbol is defined. + * + * Provides additional metadata over normal [location](#Location) definitions, including the range of + * the defining symbol + */ +type DefinitionLink = LocationLink + +// DeclarationLink is a type +/** + * Information about where a symbol is declared. + * + * Provides additional metadata over normal [location](#Location) declarations, including the range of + * the declaring symbol. + * + * Servers should prefer returning `DeclarationLink` over `Declaration` if supported + * by the client. + */ +type DeclarationLink = LocationLink + +// LSPMessageType is a type +/** + * A LSP Log Entry. + */ +type LSPMessageType = string + +// ProgressToken is a type +type ProgressToken = interface{} // number | string +// TraceValues is a type +type TraceValues = string diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsserver.go b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsserver.go new file mode 100644 index 0000000..d760501 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/protocol/tsserver.go @@ -0,0 +1,842 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package protocol + +import ( + "context" + "encoding/json" + "log" + + "../jsonrpc2" +) + +type Server interface { + DidChangeWorkspaceFolders(context.Context, *DidChangeWorkspaceFoldersParams) error + Initialized(context.Context, *InitializedParams) error + Exit(context.Context) error + DidChangeConfiguration(context.Context, *DidChangeConfigurationParams) error + DidOpen(context.Context, *DidOpenTextDocumentParams) error + DidChange(context.Context, *DidChangeTextDocumentParams) error + DidClose(context.Context, *DidCloseTextDocumentParams) error + DidSave(context.Context, *DidSaveTextDocumentParams) error + WillSave(context.Context, *WillSaveTextDocumentParams) error + DidChangeWatchedFiles(context.Context, *DidChangeWatchedFilesParams) error + Progress(context.Context, *ProgressParams) error + SetTraceNotification(context.Context, *SetTraceParams) error + LogTraceNotification(context.Context, *LogTraceParams) error + Implementation(context.Context, *ImplementationParams) ([]Location, error) + TypeDefinition(context.Context, *TypeDefinitionParams) ([]Location, error) + DocumentColor(context.Context, *DocumentColorParams) ([]ColorInformation, error) + ColorPresentation(context.Context, *ColorPresentationParams) ([]ColorPresentation, error) + FoldingRange(context.Context, *FoldingRangeParams) ([]FoldingRange, error) + Declaration(context.Context, *DeclarationParams) ([]DeclarationLink, error) + SelectionRange(context.Context, *SelectionRangeParams) ([]SelectionRange, error) + Initialize(context.Context, *ParamInitia) (*InitializeResult, error) + Shutdown(context.Context) error + WillSaveWaitUntil(context.Context, *WillSaveTextDocumentParams) ([]TextEdit, error) + Completion(context.Context, *CompletionParams) (*CompletionList, error) + Resolve(context.Context, *CompletionItem) (*CompletionItem, error) + Hover(context.Context, *HoverParams) (*Hover, error) + SignatureHelp(context.Context, *SignatureHelpParams) (*SignatureHelp, error) + Definition(context.Context, *DefinitionParams) ([]Location, error) + References(context.Context, *ReferenceParams) ([]Location, error) + DocumentHighlight(context.Context, *DocumentHighlightParams) ([]DocumentHighlight, error) + DocumentSymbol(context.Context, *DocumentSymbolParams) ([]DocumentSymbol, error) + CodeAction(context.Context, *CodeActionParams) ([]CodeAction, error) + Symbol(context.Context, *WorkspaceSymbolParams) ([]SymbolInformation, error) + CodeLens(context.Context, *CodeLensParams) ([]CodeLens, error) + ResolveCodeLens(context.Context, *CodeLens) (*CodeLens, error) + DocumentLink(context.Context, *DocumentLinkParams) ([]DocumentLink, error) + ResolveDocumentLink(context.Context, *DocumentLink) (*DocumentLink, error) + Formatting(context.Context, *DocumentFormattingParams) ([]TextEdit, error) + RangeFormatting(context.Context, *DocumentRangeFormattingParams) ([]TextEdit, error) + OnTypeFormatting(context.Context, *DocumentOnTypeFormattingParams) ([]TextEdit, error) + Rename(context.Context, *RenameParams) (*WorkspaceEdit, error) + PrepareRename(context.Context, *PrepareRenameParams) (*Range, error) + ExecuteCommand(context.Context, *ExecuteCommandParams) (interface{}, error) +} + +func (h serverHandler) Deliver(ctx context.Context, r *jsonrpc2.Request, delivered bool) bool { + if delivered { + return false + } + if ctx.Err() != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(RequestCancelledError, "")) + return true + } + switch r.Method { + case "workspace/didChangeWorkspaceFolders": // notif + var params DidChangeWorkspaceFoldersParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidChangeWorkspaceFolders(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "initialized": // notif + var params InitializedParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.Initialized(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "exit": // notif + if err := h.server.Exit(ctx); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/didChangeConfiguration": // notif + var params DidChangeConfigurationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidChangeConfiguration(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/didOpen": // notif + var params DidOpenTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidOpen(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/didChange": // notif + var params DidChangeTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidChange(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/didClose": // notif + var params DidCloseTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidClose(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/didSave": // notif + var params DidSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidSave(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/willSave": // notif + var params WillSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.WillSave(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/didChangeWatchedFiles": // notif + var params DidChangeWatchedFilesParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.DidChangeWatchedFiles(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "$/progress": // notif + var params ProgressParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.Progress(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "$/setTraceNotification": // notif + var params SetTraceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.SetTraceNotification(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "$/logTraceNotification": // notif + var params LogTraceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + if err := h.server.LogTraceNotification(ctx, ¶ms); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/implementation": // req + var params ImplementationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Implementation(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/typeDefinition": // req + var params TypeDefinitionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.TypeDefinition(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/documentColor": // req + var params DocumentColorParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.DocumentColor(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/colorPresentation": // req + var params ColorPresentationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.ColorPresentation(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/foldingRange": // req + var params FoldingRangeParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.FoldingRange(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/declaration": // req + var params DeclarationParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Declaration(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/selectionRange": // req + var params SelectionRangeParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.SelectionRange(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "initialize": // req + var params ParamInitia + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Initialize(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "shutdown": // req + if r.Params != nil { + r.Reply(ctx, nil, jsonrpc2.NewErrorf(jsonrpc2.CodeInvalidParams, "Expected no params")) + return true + } + err := h.server.Shutdown(ctx) + if err := r.Reply(ctx, nil, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/willSaveWaitUntil": // req + var params WillSaveTextDocumentParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.WillSaveWaitUntil(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/completion": // req + var params CompletionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Completion(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "completionItem/resolve": // req + var params CompletionItem + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Resolve(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/hover": // req + var params HoverParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Hover(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/signatureHelp": // req + var params SignatureHelpParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.SignatureHelp(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/definition": // req + var params DefinitionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Definition(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/references": // req + var params ReferenceParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.References(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/documentHighlight": // req + var params DocumentHighlightParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.DocumentHighlight(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/documentSymbol": // req + var params DocumentSymbolParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.DocumentSymbol(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/codeAction": // req + var params CodeActionParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.CodeAction(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/symbol": // req + var params WorkspaceSymbolParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Symbol(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/codeLens": // req + var params CodeLensParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.CodeLens(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "codeLens/resolve": // req + var params CodeLens + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.ResolveCodeLens(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/documentLink": // req + var params DocumentLinkParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.DocumentLink(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "documentLink/resolve": // req + var params DocumentLink + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.ResolveDocumentLink(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/formatting": // req + var params DocumentFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Formatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/rangeFormatting": // req + var params DocumentRangeFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.RangeFormatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/onTypeFormatting": // req + var params DocumentOnTypeFormattingParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.OnTypeFormatting(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/rename": // req + var params RenameParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.Rename(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "textDocument/prepareRename": // req + var params PrepareRenameParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.PrepareRename(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + case "workspace/executeCommand": // req + var params ExecuteCommandParams + if err := json.Unmarshal(*r.Params, ¶ms); err != nil { + sendParseError(ctx, r, err) + return true + } + resp, err := h.server.ExecuteCommand(ctx, ¶ms) + if err := r.Reply(ctx, resp, err); err != nil { + log.Printf("%v", err) + } + return true + + default: + return false + } +} + +type serverDispatcher struct { + *jsonrpc2.Conn +} + +func (s *serverDispatcher) DidChangeWorkspaceFolders(ctx context.Context, params *DidChangeWorkspaceFoldersParams) error { + return s.Conn.Notify(ctx, "workspace/didChangeWorkspaceFolders", params) +} + +func (s *serverDispatcher) Initialized(ctx context.Context, params *InitializedParams) error { + return s.Conn.Notify(ctx, "initialized", params) +} + +func (s *serverDispatcher) Exit(ctx context.Context) error { + return s.Conn.Notify(ctx, "exit", nil) +} + +func (s *serverDispatcher) DidChangeConfiguration(ctx context.Context, params *DidChangeConfigurationParams) error { + return s.Conn.Notify(ctx, "workspace/didChangeConfiguration", params) +} + +func (s *serverDispatcher) DidOpen(ctx context.Context, params *DidOpenTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/didOpen", params) +} + +func (s *serverDispatcher) DidChange(ctx context.Context, params *DidChangeTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/didChange", params) +} + +func (s *serverDispatcher) DidClose(ctx context.Context, params *DidCloseTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/didClose", params) +} + +func (s *serverDispatcher) DidSave(ctx context.Context, params *DidSaveTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/didSave", params) +} + +func (s *serverDispatcher) WillSave(ctx context.Context, params *WillSaveTextDocumentParams) error { + return s.Conn.Notify(ctx, "textDocument/willSave", params) +} + +func (s *serverDispatcher) DidChangeWatchedFiles(ctx context.Context, params *DidChangeWatchedFilesParams) error { + return s.Conn.Notify(ctx, "workspace/didChangeWatchedFiles", params) +} + +func (s *serverDispatcher) Progress(ctx context.Context, params *ProgressParams) error { + return s.Conn.Notify(ctx, "$/progress", params) +} + +func (s *serverDispatcher) SetTraceNotification(ctx context.Context, params *SetTraceParams) error { + return s.Conn.Notify(ctx, "$/setTraceNotification", params) +} + +func (s *serverDispatcher) LogTraceNotification(ctx context.Context, params *LogTraceParams) error { + return s.Conn.Notify(ctx, "$/logTraceNotification", params) +} +func (s *serverDispatcher) Implementation(ctx context.Context, params *ImplementationParams) ([]Location, error) { + var result []Location + if err := s.Conn.Call(ctx, "textDocument/implementation", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) TypeDefinition(ctx context.Context, params *TypeDefinitionParams) ([]Location, error) { + var result []Location + if err := s.Conn.Call(ctx, "textDocument/typeDefinition", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) DocumentColor(ctx context.Context, params *DocumentColorParams) ([]ColorInformation, error) { + var result []ColorInformation + if err := s.Conn.Call(ctx, "textDocument/documentColor", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) ColorPresentation(ctx context.Context, params *ColorPresentationParams) ([]ColorPresentation, error) { + var result []ColorPresentation + if err := s.Conn.Call(ctx, "textDocument/colorPresentation", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) FoldingRange(ctx context.Context, params *FoldingRangeParams) ([]FoldingRange, error) { + var result []FoldingRange + if err := s.Conn.Call(ctx, "textDocument/foldingRange", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Declaration(ctx context.Context, params *DeclarationParams) ([]DeclarationLink, error) { + var result []DeclarationLink + if err := s.Conn.Call(ctx, "textDocument/declaration", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) SelectionRange(ctx context.Context, params *SelectionRangeParams) ([]SelectionRange, error) { + var result []SelectionRange + if err := s.Conn.Call(ctx, "textDocument/selectionRange", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Initialize(ctx context.Context, params *ParamInitia) (*InitializeResult, error) { + var result InitializeResult + if err := s.Conn.Call(ctx, "initialize", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Shutdown(ctx context.Context) error { + return s.Conn.Call(ctx, "shutdown", nil, nil) +} + +func (s *serverDispatcher) WillSaveWaitUntil(ctx context.Context, params *WillSaveTextDocumentParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.Conn.Call(ctx, "textDocument/willSaveWaitUntil", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Completion(ctx context.Context, params *CompletionParams) (*CompletionList, error) { + var result CompletionList + if err := s.Conn.Call(ctx, "textDocument/completion", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Resolve(ctx context.Context, params *CompletionItem) (*CompletionItem, error) { + var result CompletionItem + if err := s.Conn.Call(ctx, "completionItem/resolve", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Hover(ctx context.Context, params *HoverParams) (*Hover, error) { + var result Hover + if err := s.Conn.Call(ctx, "textDocument/hover", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) SignatureHelp(ctx context.Context, params *SignatureHelpParams) (*SignatureHelp, error) { + var result SignatureHelp + if err := s.Conn.Call(ctx, "textDocument/signatureHelp", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Definition(ctx context.Context, params *DefinitionParams) ([]Location, error) { + var result []Location + if err := s.Conn.Call(ctx, "textDocument/definition", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) References(ctx context.Context, params *ReferenceParams) ([]Location, error) { + var result []Location + if err := s.Conn.Call(ctx, "textDocument/references", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) DocumentHighlight(ctx context.Context, params *DocumentHighlightParams) ([]DocumentHighlight, error) { + var result []DocumentHighlight + if err := s.Conn.Call(ctx, "textDocument/documentHighlight", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) DocumentSymbol(ctx context.Context, params *DocumentSymbolParams) ([]DocumentSymbol, error) { + var result []DocumentSymbol + if err := s.Conn.Call(ctx, "textDocument/documentSymbol", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) CodeAction(ctx context.Context, params *CodeActionParams) ([]CodeAction, error) { + var result []CodeAction + if err := s.Conn.Call(ctx, "textDocument/codeAction", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Symbol(ctx context.Context, params *WorkspaceSymbolParams) ([]SymbolInformation, error) { + var result []SymbolInformation + if err := s.Conn.Call(ctx, "workspace/symbol", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) CodeLens(ctx context.Context, params *CodeLensParams) ([]CodeLens, error) { + var result []CodeLens + if err := s.Conn.Call(ctx, "textDocument/codeLens", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) ResolveCodeLens(ctx context.Context, params *CodeLens) (*CodeLens, error) { + var result CodeLens + if err := s.Conn.Call(ctx, "codeLens/resolve", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) DocumentLink(ctx context.Context, params *DocumentLinkParams) ([]DocumentLink, error) { + var result []DocumentLink + if err := s.Conn.Call(ctx, "textDocument/documentLink", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) ResolveDocumentLink(ctx context.Context, params *DocumentLink) (*DocumentLink, error) { + var result DocumentLink + if err := s.Conn.Call(ctx, "documentLink/resolve", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) Formatting(ctx context.Context, params *DocumentFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.Conn.Call(ctx, "textDocument/formatting", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) RangeFormatting(ctx context.Context, params *DocumentRangeFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.Conn.Call(ctx, "textDocument/rangeFormatting", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) OnTypeFormatting(ctx context.Context, params *DocumentOnTypeFormattingParams) ([]TextEdit, error) { + var result []TextEdit + if err := s.Conn.Call(ctx, "textDocument/onTypeFormatting", params, &result); err != nil { + return nil, err + } + return result, nil +} + +func (s *serverDispatcher) Rename(ctx context.Context, params *RenameParams) (*WorkspaceEdit, error) { + var result WorkspaceEdit + if err := s.Conn.Call(ctx, "textDocument/rename", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) PrepareRename(ctx context.Context, params *PrepareRenameParams) (*Range, error) { + var result Range + if err := s.Conn.Call(ctx, "textDocument/prepareRename", params, &result); err != nil { + return nil, err + } + return &result, nil +} + +func (s *serverDispatcher) ExecuteCommand(ctx context.Context, params *ExecuteCommandParams) (interface{}, error) { + var result interface{} + if err := s.Conn.Call(ctx, "workspace/executeCommand", params, &result); err != nil { + return nil, err + } + return result, nil +} + +type CancelParams struct { + /** + * The request id to cancel. + */ + ID jsonrpc2.ID `json:"id"` +} + +// Types constructed to avoid structs as formal argument types +type ParamInitia struct { + InitializeParams + WorkDoneProgressParams +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/span/parse.go b/third_party/spirv-tools/utils/vscode/src/lsp/span/parse.go new file mode 100644 index 0000000..ec393c2 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/span/parse.go @@ -0,0 +1,110 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package span + +import ( + "strconv" + "strings" + "unicode/utf8" +) + +// Parse returns the location represented by the input. +// All inputs are valid locations, as they can always be a pure filename. +// The returned span will be normalized, and thus if printed may produce a +// different string. +func Parse(input string) Span { + // :0:0#0-0:0#0 + valid := input + var hold, offset int + hadCol := false + suf := rstripSuffix(input) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep == ":" { + valid = suf.remains + hold = suf.num + hadCol = true + suf = rstripSuffix(suf.remains) + } + switch { + case suf.sep == ":": + return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), Point{}) + case suf.sep == "-": + // we have a span, fall out of the case to continue + default: + // separator not valid, rewind to either the : or the start + return New(NewURI(valid), NewPoint(hold, 0, offset), Point{}) + } + // only the span form can get here + // at this point we still don't know what the numbers we have mean + // if have not yet seen a : then we might have either a line or a column depending + // on whether start has a column or not + // we build an end point and will fix it later if needed + end := NewPoint(suf.num, hold, offset) + hold, offset = 0, 0 + suf = rstripSuffix(suf.remains) + if suf.sep == "#" { + offset = suf.num + suf = rstripSuffix(suf.remains) + } + if suf.sep != ":" { + // turns out we don't have a span after all, rewind + return New(NewURI(valid), end, Point{}) + } + valid = suf.remains + hold = suf.num + suf = rstripSuffix(suf.remains) + if suf.sep != ":" { + // line#offset only + return New(NewURI(valid), NewPoint(hold, 0, offset), end) + } + // we have a column, so if end only had one number, it is also the column + if !hadCol { + end = NewPoint(suf.num, end.v.Line, end.v.Offset) + } + return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), end) +} + +type suffix struct { + remains string + sep string + num int +} + +func rstripSuffix(input string) suffix { + if len(input) == 0 { + return suffix{"", "", -1} + } + remains := input + num := -1 + // first see if we have a number at the end + last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) + if last >= 0 && last < len(remains)-1 { + number, err := strconv.ParseInt(remains[last+1:], 10, 64) + if err == nil { + num = int(number) + remains = remains[:last+1] + } + } + // now see if we have a trailing separator + r, w := utf8.DecodeLastRuneInString(remains) + if r != ':' && r != '#' && r == '#' { + return suffix{input, "", -1} + } + remains = remains[:len(remains)-w] + return suffix{remains, string(r), num} +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/span/span.go b/third_party/spirv-tools/utils/vscode/src/lsp/span/span.go new file mode 100644 index 0000000..aefd4f3 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/span/span.go @@ -0,0 +1,295 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package span contains support for representing with positions and ranges in +// text files. +package span + +import ( + "encoding/json" + "fmt" + "path" +) + +// Span represents a source code range in standardized form. +type Span struct { + v span +} + +// Point represents a single point within a file. +// In general this should only be used as part of a Span, as on its own it +// does not carry enough information. +type Point struct { + v point +} + +type span struct { + URI URI `json:"uri"` + Start point `json:"start"` + End point `json:"end"` +} + +type point struct { + Line int `json:"line"` + Column int `json:"column"` + Offset int `json:"offset"` +} + +// Invalid is a span that reports false from IsValid +var Invalid = Span{v: span{Start: invalidPoint.v, End: invalidPoint.v}} + +var invalidPoint = Point{v: point{Line: 0, Column: 0, Offset: -1}} + +// Converter is the interface to an object that can convert between line:column +// and offset forms for a single file. +type Converter interface { + //ToPosition converts from an offset to a line:column pair. + ToPosition(offset int) (int, int, error) + //ToOffset converts from a line:column pair to an offset. + ToOffset(line, col int) (int, error) +} + +func New(uri URI, start Point, end Point) Span { + s := Span{v: span{URI: uri, Start: start.v, End: end.v}} + s.v.clean() + return s +} + +func NewPoint(line, col, offset int) Point { + p := Point{v: point{Line: line, Column: col, Offset: offset}} + p.v.clean() + return p +} + +func Compare(a, b Span) int { + if r := CompareURI(a.URI(), b.URI()); r != 0 { + return r + } + if r := comparePoint(a.v.Start, b.v.Start); r != 0 { + return r + } + return comparePoint(a.v.End, b.v.End) +} + +func ComparePoint(a, b Point) int { + return comparePoint(a.v, b.v) +} + +func comparePoint(a, b point) int { + if !a.hasPosition() { + if a.Offset < b.Offset { + return -1 + } + if a.Offset > b.Offset { + return 1 + } + return 0 + } + if a.Line < b.Line { + return -1 + } + if a.Line > b.Line { + return 1 + } + if a.Column < b.Column { + return -1 + } + if a.Column > b.Column { + return 1 + } + return 0 +} + +func (s Span) HasPosition() bool { return s.v.Start.hasPosition() } +func (s Span) HasOffset() bool { return s.v.Start.hasOffset() } +func (s Span) IsValid() bool { return s.v.Start.isValid() } +func (s Span) IsPoint() bool { return s.v.Start == s.v.End } +func (s Span) URI() URI { return s.v.URI } +func (s Span) Start() Point { return Point{s.v.Start} } +func (s Span) End() Point { return Point{s.v.End} } +func (s *Span) MarshalJSON() ([]byte, error) { return json.Marshal(&s.v) } +func (s *Span) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &s.v) } + +func (p Point) HasPosition() bool { return p.v.hasPosition() } +func (p Point) HasOffset() bool { return p.v.hasOffset() } +func (p Point) IsValid() bool { return p.v.isValid() } +func (p *Point) MarshalJSON() ([]byte, error) { return json.Marshal(&p.v) } +func (p *Point) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, &p.v) } +func (p Point) Line() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Line +} +func (p Point) Column() int { + if !p.v.hasPosition() { + panic(fmt.Errorf("position not set in %v", p.v)) + } + return p.v.Column +} +func (p Point) Offset() int { + if !p.v.hasOffset() { + panic(fmt.Errorf("offset not set in %v", p.v)) + } + return p.v.Offset +} + +func (p point) hasPosition() bool { return p.Line > 0 } +func (p point) hasOffset() bool { return p.Offset >= 0 } +func (p point) isValid() bool { return p.hasPosition() || p.hasOffset() } +func (p point) isZero() bool { + return (p.Line == 1 && p.Column == 1) || (!p.hasPosition() && p.Offset == 0) +} + +func (s *span) clean() { + //this presumes the points are already clean + if !s.End.isValid() || (s.End == point{}) { + s.End = s.Start + } +} + +func (p *point) clean() { + if p.Line < 0 { + p.Line = 0 + } + if p.Column <= 0 { + if p.Line > 0 { + p.Column = 1 + } else { + p.Column = 0 + } + } + if p.Offset == 0 && (p.Line > 1 || p.Column > 1) { + p.Offset = -1 + } +} + +// Format implements fmt.Formatter to print the Location in a standard form. +// The format produced is one that can be read back in using Parse. +func (s Span) Format(f fmt.State, c rune) { + fullForm := f.Flag('+') + preferOffset := f.Flag('#') + // we should always have a uri, simplify if it is file format + //TODO: make sure the end of the uri is unambiguous + uri := string(s.v.URI) + if c == 'f' { + uri = path.Base(uri) + } else if !fullForm { + uri = s.v.URI.Filename() + } + fmt.Fprint(f, uri) + if !s.IsValid() || (!fullForm && s.v.Start.isZero() && s.v.End.isZero()) { + return + } + // see which bits of start to write + printOffset := s.HasOffset() && (fullForm || preferOffset || !s.HasPosition()) + printLine := s.HasPosition() && (fullForm || !printOffset) + printColumn := printLine && (fullForm || (s.v.Start.Column > 1 || s.v.End.Column > 1)) + fmt.Fprint(f, ":") + if printLine { + fmt.Fprintf(f, "%d", s.v.Start.Line) + } + if printColumn { + fmt.Fprintf(f, ":%d", s.v.Start.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.Start.Offset) + } + // start is written, do we need end? + if s.IsPoint() { + return + } + // we don't print the line if it did not change + printLine = fullForm || (printLine && s.v.End.Line > s.v.Start.Line) + fmt.Fprint(f, "-") + if printLine { + fmt.Fprintf(f, "%d", s.v.End.Line) + } + if printColumn { + if printLine { + fmt.Fprint(f, ":") + } + fmt.Fprintf(f, "%d", s.v.End.Column) + } + if printOffset { + fmt.Fprintf(f, "#%d", s.v.End.Offset) + } +} + +func (s Span) WithPosition(c Converter) (Span, error) { + if err := s.update(c, true, false); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithOffset(c Converter) (Span, error) { + if err := s.update(c, false, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s Span) WithAll(c Converter) (Span, error) { + if err := s.update(c, true, true); err != nil { + return Span{}, err + } + return s, nil +} + +func (s *Span) update(c Converter, withPos, withOffset bool) error { + if !s.IsValid() { + return fmt.Errorf("cannot add information to an invalid span") + } + if withPos && !s.HasPosition() { + if err := s.v.Start.updatePosition(c); err != nil { + return err + } + if s.v.End.Offset == s.v.Start.Offset { + s.v.End = s.v.Start + } else if err := s.v.End.updatePosition(c); err != nil { + return err + } + } + if withOffset && (!s.HasOffset() || (s.v.End.hasPosition() && !s.v.End.hasOffset())) { + if err := s.v.Start.updateOffset(c); err != nil { + return err + } + if s.v.End.Line == s.v.Start.Line && s.v.End.Column == s.v.Start.Column { + s.v.End.Offset = s.v.Start.Offset + } else if err := s.v.End.updateOffset(c); err != nil { + return err + } + } + return nil +} + +func (p *point) updatePosition(c Converter) error { + line, col, err := c.ToPosition(p.Offset) + if err != nil { + return err + } + p.Line = line + p.Column = col + return nil +} + +func (p *point) updateOffset(c Converter) error { + offset, err := c.ToOffset(p.Line, p.Column) + if err != nil { + return err + } + p.Offset = offset + return nil +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/span/token.go b/third_party/spirv-tools/utils/vscode/src/lsp/span/token.go new file mode 100644 index 0000000..f5a3870 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/span/token.go @@ -0,0 +1,161 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package span + +import ( + "fmt" + "go/token" +) + +// Range represents a source code range in token.Pos form. +// It also carries the FileSet that produced the positions, so that it is +// self contained. +type Range struct { + FileSet *token.FileSet + Start token.Pos + End token.Pos +} + +// TokenConverter is a Converter backed by a token file set and file. +// It uses the file set methods to work out the conversions, which +// makes it fast and does not require the file contents. +type TokenConverter struct { + fset *token.FileSet + file *token.File +} + +// NewRange creates a new Range from a FileSet and two positions. +// To represent a point pass a 0 as the end pos. +func NewRange(fset *token.FileSet, start, end token.Pos) Range { + return Range{ + FileSet: fset, + Start: start, + End: end, + } +} + +// NewTokenConverter returns an implementation of Converter backed by a +// token.File. +func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter { + return &TokenConverter{fset: fset, file: f} +} + +// NewContentConverter returns an implementation of Converter for the +// given file content. +func NewContentConverter(filename string, content []byte) *TokenConverter { + fset := token.NewFileSet() + f := fset.AddFile(filename, -1, len(content)) + f.SetLinesForContent(content) + return &TokenConverter{fset: fset, file: f} +} + +// IsPoint returns true if the range represents a single point. +func (r Range) IsPoint() bool { + return r.Start == r.End +} + +// Span converts a Range to a Span that represents the Range. +// It will fill in all the members of the Span, calculating the line and column +// information. +func (r Range) Span() (Span, error) { + f := r.FileSet.File(r.Start) + if f == nil { + return Span{}, fmt.Errorf("file not found in FileSet") + } + s := Span{v: span{URI: FileURI(f.Name())}} + var err error + s.v.Start.Offset, err = offset(f, r.Start) + if err != nil { + return Span{}, err + } + if r.End.IsValid() { + s.v.End.Offset, err = offset(f, r.End) + if err != nil { + return Span{}, err + } + } + s.v.Start.clean() + s.v.End.clean() + s.v.clean() + converter := NewTokenConverter(r.FileSet, f) + return s.WithPosition(converter) +} + +// offset is a copy of the Offset function in go/token, but with the adjustment +// that it does not panic on invalid positions. +func offset(f *token.File, pos token.Pos) (int, error) { + if int(pos) < f.Base() || int(pos) > f.Base()+f.Size() { + return 0, fmt.Errorf("invalid pos") + } + return int(pos) - f.Base(), nil +} + +// Range converts a Span to a Range that represents the Span for the supplied +// File. +func (s Span) Range(converter *TokenConverter) (Range, error) { + s, err := s.WithOffset(converter) + if err != nil { + return Range{}, err + } + // go/token will panic if the offset is larger than the file's size, + // so check here to avoid panicking. + if s.Start().Offset() > converter.file.Size() { + return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), converter.file.Size()) + } + if s.End().Offset() > converter.file.Size() { + return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), converter.file.Size()) + } + return Range{ + FileSet: converter.fset, + Start: converter.file.Pos(s.Start().Offset()), + End: converter.file.Pos(s.End().Offset()), + }, nil +} + +func (l *TokenConverter) ToPosition(offset int) (int, int, error) { + if offset > l.file.Size() { + return 0, 0, fmt.Errorf("offset %v is past the end of the file %v", offset, l.file.Size()) + } + pos := l.file.Pos(offset) + p := l.fset.Position(pos) + if offset == l.file.Size() { + return p.Line + 1, 1, nil + } + return p.Line, p.Column, nil +} + +func (l *TokenConverter) ToOffset(line, col int) (int, error) { + if line < 0 { + return -1, fmt.Errorf("line is not valid") + } + lineMax := l.file.LineCount() + 1 + if line > lineMax { + return -1, fmt.Errorf("line is beyond end of file %v", lineMax) + } else if line == lineMax { + if col > 1 { + return -1, fmt.Errorf("column is beyond end of file") + } + // at the end of the file, allowing for a trailing eol + return l.file.Size(), nil + } + pos := lineStart(l.file, line) + if !pos.IsValid() { + return -1, fmt.Errorf("line is not in file") + } + // we assume that column is in bytes here, and that the first byte of a + // line is at column 1 + pos += token.Pos(col - 1) + return offset(l.file, pos) +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/span/token111.go b/third_party/spirv-tools/utils/vscode/src/lsp/span/token111.go new file mode 100644 index 0000000..768419a --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/span/token111.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !go1.12 + +package span + +import ( + "go/token" +) + +// lineStart is the pre-Go 1.12 version of (*token.File).LineStart. For Go +// versions <= 1.11, we borrow logic from the analysisutil package. +// TODO(rstambler): Delete this file when we no longer support Go 1.11. +func lineStart(f *token.File, line int) token.Pos { + // Use binary search to find the start offset of this line. + + min := 0 // inclusive + max := f.Size() // exclusive + for { + offset := (min + max) / 2 + pos := f.Pos(offset) + posn := f.Position(pos) + if posn.Line == line { + return pos - (token.Pos(posn.Column) - 1) + } + + if min+1 >= max { + return token.NoPos + } + + if posn.Line < line { + min = offset + } else { + max = offset + } + } +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/span/token112.go b/third_party/spirv-tools/utils/vscode/src/lsp/span/token112.go new file mode 100644 index 0000000..abff500 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/span/token112.go @@ -0,0 +1,26 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build go1.12 + +package span + +import ( + "go/token" +) + +// TODO(rstambler): Delete this file when we no longer support Go 1.11. +func lineStart(f *token.File, line int) token.Pos { + return f.LineStart(line) +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/span/uri.go b/third_party/spirv-tools/utils/vscode/src/lsp/span/uri.go new file mode 100644 index 0000000..525518b --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/span/uri.go @@ -0,0 +1,162 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package span + +import ( + "fmt" + "net/url" + "os" + "path" + "path/filepath" + "runtime" + "strings" + "unicode" +) + +const fileScheme = "file" + +// URI represents the full URI for a file. +type URI string + +// Filename returns the file path for the given URI. +// It is an error to call this on a URI that is not a valid filename. +func (uri URI) Filename() string { + filename, err := filename(uri) + if err != nil { + panic(err) + } + return filepath.FromSlash(filename) +} + +func filename(uri URI) (string, error) { + if uri == "" { + return "", nil + } + u, err := url.ParseRequestURI(string(uri)) + if err != nil { + return "", err + } + if u.Scheme != fileScheme { + return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri) + } + if isWindowsDriveURI(u.Path) { + u.Path = u.Path[1:] + } + return u.Path, nil +} + +// NewURI returns a span URI for the string. +// It will attempt to detect if the string is a file path or uri. +func NewURI(s string) URI { + if u, err := url.PathUnescape(s); err == nil { + s = u + } + if strings.HasPrefix(s, fileScheme+"://") { + return URI(s) + } + return FileURI(s) +} + +func CompareURI(a, b URI) int { + if equalURI(a, b) { + return 0 + } + if a < b { + return -1 + } + return 1 +} + +func equalURI(a, b URI) bool { + if a == b { + return true + } + // If we have the same URI basename, we may still have the same file URIs. + if !strings.EqualFold(path.Base(string(a)), path.Base(string(b))) { + return false + } + fa, err := filename(a) + if err != nil { + return false + } + fb, err := filename(b) + if err != nil { + return false + } + // Stat the files to check if they are equal. + infoa, err := os.Stat(filepath.FromSlash(fa)) + if err != nil { + return false + } + infob, err := os.Stat(filepath.FromSlash(fb)) + if err != nil { + return false + } + return os.SameFile(infoa, infob) +} + +// FileURI returns a span URI for the supplied file path. +// It will always have the file scheme. +func FileURI(path string) URI { + if path == "" { + return "" + } + // Handle standard library paths that contain the literal "$GOROOT". + // TODO(rstambler): The go/packages API should allow one to determine a user's $GOROOT. + const prefix = "$GOROOT" + if len(path) >= len(prefix) && strings.EqualFold(prefix, path[:len(prefix)]) { + suffix := path[len(prefix):] + path = runtime.GOROOT() + suffix + } + if !isWindowsDrivePath(path) { + if abs, err := filepath.Abs(path); err == nil { + path = abs + } + } + // Check the file path again, in case it became absolute. + if isWindowsDrivePath(path) { + path = "/" + path + } + path = filepath.ToSlash(path) + u := url.URL{ + Scheme: fileScheme, + Path: path, + } + uri := u.String() + if unescaped, err := url.PathUnescape(uri); err == nil { + uri = unescaped + } + return URI(uri) +} + +// isWindowsDrivePath returns true if the file path is of the form used by +// Windows. We check if the path begins with a drive letter, followed by a ":". +func isWindowsDrivePath(path string) bool { + if len(path) < 4 { + return false + } + return unicode.IsLetter(rune(path[0])) && path[1] == ':' +} + +// isWindowsDriveURI returns true if the file URI is of the format used by +// Windows URIs. The url.Parse package does not specially handle Windows paths +// (see https://golang.org/issue/6027). We check if the URI path has +// a drive prefix (e.g. "/C:"). If so, we trim the leading "/". +func isWindowsDriveURI(uri string) bool { + if len(uri) < 4 { + return false + } + return uri[0] == '/' && unicode.IsLetter(rune(uri[1])) && uri[2] == ':' +} diff --git a/third_party/spirv-tools/utils/vscode/src/lsp/span/utf16.go b/third_party/spirv-tools/utils/vscode/src/lsp/span/utf16.go new file mode 100644 index 0000000..6821851 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/lsp/span/utf16.go @@ -0,0 +1,104 @@ +// Copyright 2019 The Go Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package span + +import ( + "fmt" + "unicode/utf16" + "unicode/utf8" +) + +// ToUTF16Column calculates the utf16 column expressed by the point given the +// supplied file contents. +// This is used to convert from the native (always in bytes) column +// representation and the utf16 counts used by some editors. +func ToUTF16Column(p Point, content []byte) (int, error) { + if content == nil { + return -1, fmt.Errorf("ToUTF16Column: missing content") + } + if !p.HasPosition() { + return -1, fmt.Errorf("ToUTF16Column: point is missing position") + } + if !p.HasOffset() { + return -1, fmt.Errorf("ToUTF16Column: point is missing offset") + } + offset := p.Offset() // 0-based + colZero := p.Column() - 1 // 0-based + if colZero == 0 { + // 0-based column 0, so it must be chr 1 + return 1, nil + } else if colZero < 0 { + return -1, fmt.Errorf("ToUTF16Column: column is invalid (%v)", colZero) + } + // work out the offset at the start of the line using the column + lineOffset := offset - colZero + if lineOffset < 0 || offset > len(content) { + return -1, fmt.Errorf("ToUTF16Column: offsets %v-%v outside file contents (%v)", lineOffset, offset, len(content)) + } + // Use the offset to pick out the line start. + // This cannot panic: offset > len(content) and lineOffset < offset. + start := content[lineOffset:] + + // Now, truncate down to the supplied column. + start = start[:colZero] + + // and count the number of utf16 characters + // in theory we could do this by hand more efficiently... + return len(utf16.Encode([]rune(string(start)))) + 1, nil +} + +// FromUTF16Column advances the point by the utf16 character offset given the +// supplied line contents. +// This is used to convert from the utf16 counts used by some editors to the +// native (always in bytes) column representation. +func FromUTF16Column(p Point, chr int, content []byte) (Point, error) { + if !p.HasOffset() { + return Point{}, fmt.Errorf("FromUTF16Column: point is missing offset") + } + // if chr is 1 then no adjustment needed + if chr <= 1 { + return p, nil + } + if p.Offset() >= len(content) { + return p, fmt.Errorf("FromUTF16Column: offset (%v) greater than length of content (%v)", p.Offset(), len(content)) + } + remains := content[p.Offset():] + // scan forward the specified number of characters + for count := 1; count < chr; count++ { + if len(remains) <= 0 { + return Point{}, fmt.Errorf("FromUTF16Column: chr goes beyond the content") + } + r, w := utf8.DecodeRune(remains) + if r == '\n' { + // Per the LSP spec: + // + // > If the character value is greater than the line length it + // > defaults back to the line length. + break + } + remains = remains[w:] + if r >= 0x10000 { + // a two point rune + count++ + // if we finished in a two point rune, do not advance past the first + if count >= chr { + break + } + } + p.v.Column += w + p.v.Offset += w + } + return p, nil +} diff --git a/third_party/spirv-tools/utils/vscode/src/parser/parser.go b/third_party/spirv-tools/utils/vscode/src/parser/parser.go new file mode 100644 index 0000000..260a616 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/parser/parser.go @@ -0,0 +1,818 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package parser implements a SPIR-V assembly parser. +package parser + +import ( + "fmt" + "io" + "log" + "strings" + "unicode" + "unicode/utf8" + + "../schema" +) + +// Type is an enumerator of token types. +type Type int + +// Type enumerators +const ( + Ident Type = iota // Foo + PIdent // %32, %foo + Integer + Float + String + Operator + Comment + Newline +) + +func (t Type) String() string { + switch t { + case Ident: + return "Ident" + case PIdent: + return "PIdent" + case Integer: + return "Integer" + case Float: + return "Float" + case String: + return "String" + case Operator: + return "Operator" + case Comment: + return "Comment" + default: + return "" + } +} + +// Token represents a single lexed token. +type Token struct { + Type Type + Range Range +} + +func (t Token) String() string { return fmt.Sprintf("{%v %v}", t.Type, t.Range) } + +// Text returns the tokens text from the source. +func (t Token) Text(lines []string) string { return t.Range.Text(lines) } + +// Range represents an interval in a text file. +type Range struct { + Start Position + End Position +} + +func (r Range) String() string { return fmt.Sprintf("[%v %v]", r.Start, r.End) } + +// Text returns the text for the given Range in the provided lines. +func (r Range) Text(lines []string) string { + sl, sc := r.Start.Line-1, r.Start.Column-1 + if sl < 0 || sc < 0 || sl > len(lines) || sc > len(lines[sl]) { + return fmt.Sprintf("", r.Start) + } + el, ec := r.End.Line-1, r.End.Column-1 + if el < 0 || ec < 0 || el > len(lines) || ec > len(lines[sl]) { + return fmt.Sprintf("", r.End) + } + + sb := strings.Builder{} + if sl != el { + sb.WriteString(lines[sl][sc:]) + for l := sl + 1; l < el; l++ { + sb.WriteString(lines[l]) + } + sb.WriteString(lines[el][:ec]) + } else { + sb.WriteString(lines[sl][sc:ec]) + } + return sb.String() +} + +// Contains returns true if p is in r. +func (r Range) Contains(p Position) bool { + return !(p.LessThan(r.Start) || p.GreaterThan(r.End)) +} + +func (r *Range) grow(o Range) { + if !r.Start.IsValid() || o.Start.LessThan(r.Start) { + r.Start = o.Start + } + if !r.End.IsValid() || o.End.GreaterThan(r.End) { + r.End = o.End + } +} + +// Position holds a line and column position in a text file. +type Position struct { + Line, Column int +} + +func (p Position) String() string { return fmt.Sprintf("%v:%v", p.Line, p.Column) } + +// IsValid returns true if the position has a line and column greater than 1. +func (p Position) IsValid() bool { return p.Line > 0 && p.Column > 0 } + +// LessThan returns true iff o is before p. +func (p Position) LessThan(o Position) bool { + switch { + case !p.IsValid() || !o.IsValid(): + return false + case p.Line < o.Line: + return true + case p.Line > o.Line: + return false + case p.Column < o.Column: + return true + default: + return false + } +} + +// GreaterThan returns true iff o is greater than p. +func (p Position) GreaterThan(o Position) bool { + switch { + case !p.IsValid() || !o.IsValid(): + return false + case p.Line > o.Line: + return true + case p.Line < o.Line: + return false + case p.Column > o.Column: + return true + default: + return false + } +} + +type lexer struct { + source string + lexerState + diags []Diagnostic + e error +} + +type lexerState struct { + offset int // byte offset in source + toks []*Token // all the lexed tokens + pos Position // current position +} + +// err appends an fmt.Printf style error into l.diags for the given token. +func (l *lexer) err(tok *Token, msg string, args ...interface{}) { + rng := Range{} + if tok != nil { + rng = tok.Range + } + l.diags = append(l.diags, Diagnostic{ + Range: rng, + Severity: SeverityError, + Message: fmt.Sprintf(msg, args...), + }) +} + +// next returns the next rune, or io.EOF if the last rune has already been +// consumed. +func (l *lexer) next() rune { + if l.offset >= len(l.source) { + l.e = io.EOF + return 0 + } + r, n := utf8.DecodeRuneInString(l.source[l.offset:]) + l.offset += n + if n == 0 { + l.e = io.EOF + return 0 + } + if r == '\n' { + l.pos.Line++ + l.pos.Column = 1 + } else { + l.pos.Column++ + } + return r +} + +// save returns the current lexerState. +func (l *lexer) save() lexerState { + return l.lexerState +} + +// restore restores the current lexer state with s. +func (l *lexer) restore(s lexerState) { + l.lexerState = s +} + +// pident processes the PIdent token at the current position. +// The lexer *must* know the next token is a PIdent before calling. +func (l *lexer) pident() { + tok := &Token{Type: PIdent, Range: Range{Start: l.pos, End: l.pos}} + if r := l.next(); r != '%' { + log.Fatalf("lexer expected '%%', got '%v'", r) + return + } + for l.e == nil { + s := l.save() + r := l.next() + if !isAlphaNumeric(r) && r != '_' { + l.restore(s) + break + } + } + tok.Range.End = l.pos + l.toks = append(l.toks, tok) +} + +// numberOrIdent processes the Ident, Float or Integer token at the current +// position. +func (l *lexer) numberOrIdent() { + const Unknown Type = -1 + tok := &Token{Type: Unknown, Range: Range{Start: l.pos, End: l.pos}} +loop: + for l.e == nil { + s := l.save() + r := l.next() + switch { + case r == '-', r == '+', isNumeric(r): + continue + case isAlpha(r), r == '_': + switch tok.Type { + case Unknown: + tok.Type = Ident + case Float, Integer: + l.err(tok, "invalid number") + return + } + case r == '.': + switch tok.Type { + case Unknown: + tok.Type = Float + default: + l.restore(s) + break loop + } + default: + if tok.Type == Unknown { + tok.Type = Integer + } + l.restore(s) + break loop + } + } + tok.Range.End = l.pos + l.toks = append(l.toks, tok) +} + +// string processes the String token at the current position. +// The lexer *must* know the next token is a String before calling. +func (l *lexer) string() { + tok := &Token{Type: String, Range: Range{Start: l.pos, End: l.pos}} + if r := l.next(); r != '"' { + log.Fatalf("lexer expected '\"', got '%v'", r) + return + } + escape := false + for l.e == nil { + switch l.next() { + case '"': + if !escape { + tok.Range.End = l.pos + l.toks = append(l.toks, tok) + return + } + case '\\': + escape = !escape + default: + escape = false + } + } +} + +// operator processes the Operator token at the current position. +// The lexer *must* know the next token is a Operator before calling. +func (l *lexer) operator() { + tok := &Token{Type: Operator, Range: Range{Start: l.pos, End: l.pos}} + for l.e == nil { + switch l.next() { + case '=', '|': + tok.Range.End = l.pos + l.toks = append(l.toks, tok) + return + } + } +} + +// lineComment processes the Comment token at the current position. +// The lexer *must* know the next token is a Comment before calling. +func (l *lexer) lineComment() { + tok := &Token{Type: Comment, Range: Range{Start: l.pos, End: l.pos}} + if r := l.next(); r != ';' { + log.Fatalf("lexer expected ';', got '%v'", r) + return + } + for l.e == nil { + s := l.save() + switch l.next() { + case '\n': + l.restore(s) + tok.Range.End = l.pos + l.toks = append(l.toks, tok) + return + } + } +} + +// newline processes the Newline token at the current position. +// The lexer *must* know the next token is a Newline before calling. +func (l *lexer) newline() { + tok := &Token{Type: Newline, Range: Range{Start: l.pos, End: l.pos}} + if r := l.next(); r != '\n' { + log.Fatalf("lexer expected '\n', got '%v'", r) + return + } + tok.Range.End = l.pos + l.toks = append(l.toks, tok) +} + +// lex returns all the tokens and diagnostics after lexing source. +func lex(source string) ([]*Token, []Diagnostic, error) { + l := lexer{source: source, lexerState: lexerState{pos: Position{1, 1}}} + + lastPos := Position{} + for l.e == nil { + // Integrity check that the parser is making progress + if l.pos == lastPos { + log.Panicf("Parsing stuck at %v", l.pos) + } + lastPos = l.pos + + s := l.save() + r := l.next() + switch { + case r == '%': + l.restore(s) + l.pident() + case r == '+' || r == '-' || r == '_' || isAlphaNumeric(r): + l.restore(s) + l.numberOrIdent() + case r == '"': + l.restore(s) + l.string() + case r == '=', r == '|': + l.restore(s) + l.operator() + case r == ';': + l.restore(s) + l.lineComment() + case r == '\n': + l.restore(s) + l.newline() + } + } + if l.e != nil && l.e != io.EOF { + return nil, nil, l.e + } + return l.toks, l.diags, nil +} + +func isNumeric(r rune) bool { return unicode.IsDigit(r) } +func isAlpha(r rune) bool { return unicode.IsLetter(r) } +func isAlphaNumeric(r rune) bool { return isAlpha(r) || isNumeric(r) } + +type parser struct { + lines []string // all source lines + toks []*Token // all tokens + diags []Diagnostic // parser emitted diagnostics + idents map[string]*Identifier // identifiers by name + mappings map[*Token]interface{} // tokens to semantic map + extInstImports map[string]schema.OpcodeMap // extension imports by identifier + insts []*Instruction // all instructions +} + +func (p *parser) parse() error { + for i := 0; i < len(p.toks); { + if p.newline(i) || p.comment(i) { + i++ + continue + } + if n := p.instruction(i); n > 0 { + i += n + } else { + p.unexpected(i) + i++ + } + } + return nil +} + +// instruction parses the instruction starting at the i'th token. +func (p *parser) instruction(i int) (n int) { + inst := &Instruction{} + + switch { + case p.opcode(i) != nil: + inst.Opcode = p.opcode(i) + inst.Tokens = []*Token{p.tok(i)} + p.mappings[p.tok(i)] = inst + n++ + case p.opcode(i+2) != nil: // try '%id' '=' + inst.Result, inst.Opcode = p.pident(i), p.opcode(i+2) + if inst.Result == nil || p.operator(i+1) != "=" { + return 0 + } + n += 3 + inst.Tokens = []*Token{p.tok(i), p.tok(i + 1), p.tok(i + 2)} + p.mappings[p.tok(i+2)] = inst + default: + return + } + + expectsResult := len(inst.Opcode.Operands) > 0 && IsResult(inst.Opcode.Operands[0].Kind) + operands := inst.Opcode.Operands + switch { + case inst.Result != nil && !expectsResult: + p.err(inst.Result, "'%s' does not have a result", inst.Opcode.Opname) + return + case inst.Result == nil && expectsResult: + p.err(p.tok(i), "'%s' expects a result", inst.Opcode.Opname) + return + case inst.Result != nil && expectsResult: + // Check the result is of the correct type + o := inst.Opcode.Operands[0] + p.operand(o.Name, o.Kind, i, false) + operands = operands[1:] + p.addIdentDef(inst.Result.Text(p.lines), inst, p.tok(i)) + } + + processOperand := func(o schema.Operand) bool { + if p.newline(i + n) { + return false + } + + switch o.Quantifier { + case schema.Once: + if op, c := p.operand(o.Name, o.Kind, i+n, false); op != nil { + inst.Tokens = append(inst.Tokens, op.Tokens...) + n += c + } + case schema.ZeroOrOnce: + if op, c := p.operand(o.Name, o.Kind, i+n, true); op != nil { + inst.Tokens = append(inst.Tokens, op.Tokens...) + n += c + } + case schema.ZeroOrMany: + for !p.newline(i + n) { + if op, c := p.operand(o.Name, o.Kind, i+n, true); op != nil { + inst.Tokens = append(inst.Tokens, op.Tokens...) + n += c + } else { + return false + } + } + } + return true + } + + for _, o := range operands { + if !processOperand(o) { + break + } + + if inst.Opcode == schema.OpExtInst && n == 4 { + extImportTok, extNameTok := p.tok(i+n), p.tok(i+n+1) + extImport := extImportTok.Text(p.lines) + if extOpcodes, ok := p.extInstImports[extImport]; ok { + extName := extNameTok.Text(p.lines) + if extOpcode, ok := extOpcodes[extName]; ok { + n += 2 // skip ext import, ext name + for _, o := range extOpcode.Operands { + if !processOperand(o) { + break + } + } + } else { + p.err(extNameTok, "Unknown extension opcode '%s'", extName) + } + } else { + p.err(extImportTok, "Expected identifier to OpExtInstImport") + } + } + } + + for _, t := range inst.Tokens { + inst.Range.grow(t.Range) + } + + p.insts = append(p.insts, inst) + + if inst.Opcode == schema.OpExtInstImport && len(inst.Tokens) >= 4 { + // Instruction is a OpExtInstImport. Keep track of this. + extTok := inst.Tokens[3] + extName := strings.Trim(extTok.Text(p.lines), `"`) + extOpcodes, ok := schema.ExtOpcodes[extName] + if !ok { + p.err(extTok, "Unknown extension '%s'", extName) + } + extImport := inst.Result.Text(p.lines) + p.extInstImports[extImport] = extOpcodes + } + + return +} + +// operand parses the operand with the name n, kind k, starting at the i'th +// token. +func (p *parser) operand(n string, k *schema.OperandKind, i int, optional bool) (*Operand, int) { + tok := p.tok(i) + if tok == nil { + return nil, 0 + } + + op := &Operand{ + Name: n, + Kind: k, + Tokens: []*Token{tok}, + } + p.mappings[tok] = op + + switch k.Category { + case schema.OperandCategoryBitEnum, schema.OperandCategoryValueEnum: + s := tok.Text(p.lines) + for _, e := range k.Enumerants { + if e.Enumerant == s { + count := 1 + for _, param := range e.Parameters { + p, c := p.operand(param.Name, param.Kind, i+count, false) + if p != nil { + op.Tokens = append(op.Tokens, p.Tokens...) + op.Parameters = append(op.Parameters, p) + } + count += c + } + + // Handle bitfield '|' chains + if p.tok(i+count).Text(p.lines) == "|" { + count++ // '|' + p, c := p.operand(n, k, i+count, false) + if p != nil { + op.Tokens = append(op.Tokens, p.Tokens...) + op.Parameters = append(op.Parameters, p) + } + count += c + } + + return op, count + } + } + if !optional { + p.err(p.tok(i), "invalid operand value '%s'", s) + } + + return nil, 0 + + case schema.OperandCategoryID: + id := p.pident(i) + if id != nil { + p.addIdentRef(p.tok(i)) + return op, 1 + } + if !optional { + p.err(p.tok(i), "operand requires id, got '%s'", tok.Text(p.lines)) + } + return nil, 0 + + case schema.OperandCategoryLiteral: + switch tok.Type { + case String, Integer, Float, Ident: + return op, 1 + } + if !optional { + p.err(p.tok(i), "operand requires literal, got '%s'", tok.Text(p.lines)) + } + return nil, 0 + + case schema.OperandCategoryComposite: + n := 1 + for _, b := range k.Bases { + o, c := p.operand(b.Kind, b, i+n, optional) + if o != nil { + op.Tokens = append(op.Tokens, o.Tokens...) + } + n += c + } + return op, n + + default: + p.err(p.tok(i), "OperandKind '%s' has unexpected category '%s'", k.Kind, k.Category) + return nil, 0 + } +} + +// tok returns the i'th token, or nil if i is out of bounds. +func (p *parser) tok(i int) *Token { + if i < 0 || i >= len(p.toks) { + return nil + } + return p.toks[i] +} + +// opcode returns the schema.Opcode for the i'th token, or nil if the i'th token +// does not represent an opcode. +func (p *parser) opcode(i int) *schema.Opcode { + if tok := p.ident(i); tok != nil { + name := tok.Text(p.lines) + if inst, found := schema.Opcodes[name]; found { + return inst + } + } + return nil +} + +// operator returns the operator for the i'th token, or and empty string if the +// i'th token is not an operator. +func (p *parser) operator(i int) string { + if tok := p.tok(i); tok != nil && tok.Type == Operator { + return tok.Text(p.lines) + } + return "" +} + +// ident returns the i'th token if it is an Ident, otherwise nil. +func (p *parser) ident(i int) *Token { + if tok := p.tok(i); tok != nil && tok.Type == Ident { + return tok + } + return nil +} + +// pident returns the i'th token if it is an PIdent, otherwise nil. +func (p *parser) pident(i int) *Token { + if tok := p.tok(i); tok != nil && tok.Type == PIdent { + return tok + } + return nil +} + +// comment returns true if the i'th token is a Comment, otherwise false. +func (p *parser) comment(i int) bool { + if tok := p.tok(i); tok != nil && tok.Type == Comment { + return true + } + return false +} + +// newline returns true if the i'th token is a Newline, otherwise false. +func (p *parser) newline(i int) bool { + if tok := p.tok(i); tok != nil && tok.Type == Newline { + return true + } + return false +} + +// unexpected emits an 'unexpected token error' for the i'th token. +func (p *parser) unexpected(i int) { + p.err(p.toks[i], "syntax error: unexpected '%s'", p.toks[i].Text(p.lines)) +} + +// addIdentDef records the token definition for the instruction inst with the +// given id. +func (p *parser) addIdentDef(id string, inst *Instruction, def *Token) { + i, existing := p.idents[id] + if !existing { + i = &Identifier{} + p.idents[id] = i + } + if i.Definition == nil { + i.Definition = inst + } else { + p.err(def, "id '%v' redeclared", id) + } +} + +// addIdentRef adds a identifier reference for the token ref. +func (p *parser) addIdentRef(ref *Token) { + id := ref.Text(p.lines) + i, existing := p.idents[id] + if !existing { + i = &Identifier{} + p.idents[id] = i + } + i.References = append(i.References, ref) +} + +// err appends an fmt.Printf style error into l.diags for the given token. +func (p *parser) err(tok *Token, msg string, args ...interface{}) { + rng := Range{} + if tok != nil { + rng = tok.Range + } + p.diags = append(p.diags, Diagnostic{ + Range: rng, + Severity: SeverityError, + Message: fmt.Sprintf(msg, args...), + }) +} + +// Parse parses the SPIR-V assembly string source, returning the parse results. +func Parse(source string) (Results, error) { + toks, diags, err := lex(source) + if err != nil { + return Results{}, err + } + lines := strings.SplitAfter(source, "\n") + p := parser{ + lines: lines, + toks: toks, + idents: map[string]*Identifier{}, + mappings: map[*Token]interface{}{}, + extInstImports: map[string]schema.OpcodeMap{}, + } + if err := p.parse(); err != nil { + return Results{}, err + } + diags = append(diags, p.diags...) + return Results{ + Lines: lines, + Tokens: toks, + Diagnostics: p.diags, + Identifiers: p.idents, + Mappings: p.mappings, + }, nil +} + +// IsResult returns true if k is used to store the result of an instruction. +func IsResult(k *schema.OperandKind) bool { + switch k { + case schema.OperandKindIdResult, schema.OperandKindIdResultType: + return true + default: + return false + } +} + +// Results holds the output of Parse(). +type Results struct { + Lines []string + Tokens []*Token + Diagnostics []Diagnostic + Identifiers map[string]*Identifier // identifiers by name + Mappings map[*Token]interface{} // tokens to semantic map +} + +// Instruction describes a single instruction instance +type Instruction struct { + Tokens []*Token // all the tokens that make up the instruction + Result *Token // the token that represents the result of the instruction, or nil + Operands []*Operand // the operands of the instruction + Range Range // the textual range of the instruction + Opcode *schema.Opcode // the opcode for the instruction +} + +// Operand describes a single operand instance +type Operand struct { + Name string // name of the operand + Kind *schema.OperandKind // kind of the operand + Tokens []*Token // all the tokens that make up the operand + Parameters []*Operand // all the parameters for the operand +} + +// Identifier describes a single, unique SPIR-V identifier (i.e. %32) +type Identifier struct { + Definition *Instruction // where the identifier was defined + References []*Token // all the places the identifier was referenced +} + +// Severity is an enumerator of diagnositc seeverities +type Severity int + +// Severity levels +const ( + SeverityError Severity = iota + SeverityWarning + SeverityInformation + SeverityHint +) + +// Diagnostic holds a single diagnostic message that was generated while +// parsing. +type Diagnostic struct { + Range Range + Severity Severity + Message string +} diff --git a/third_party/spirv-tools/utils/vscode/src/schema/schema.go b/third_party/spirv-tools/utils/vscode/src/schema/schema.go new file mode 100755 index 0000000..0d57cb1 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/schema/schema.go @@ -0,0 +1,25094 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated by gen-grammar.go --template=../schema/schema.go.tmpl --out=../schema/schema.go +// Do not modify this file directly. + +package schema + +// Opcode holds information about a specific SPIR-V opcode. +type Opcode struct { + Opname string + Class string + Opcode int + Operands []Operand +} + +// Operand contains information about a logical operand for an instruction. +type Operand struct { + Kind *OperandKind + Name string + Quantifier Quantifier +} + +// OperandKind contains information about a specific operand kind. +type OperandKind struct { + Category OperandCategory + Kind string + Enumerants []Enumerant + Bases []*OperandKind +} + +// Enumerant contains information about an enumerant in an enum. +type Enumerant struct { + Enumerant string + Value interface{} + Capabilities []string + Parameters []Parameter + Version string +} + +// Parameter contains information about a logical parameter for an enumerant. +type Parameter struct { + Kind *OperandKind + Name string +} + +// Quantifier indicates the number of times the quantified term may appear. +type Quantifier string + +const ( + // Once indicates the quantified term may appear exactly once. + Once Quantifier = "" + + // ZeroOrOnce indicates the quantified term may appear zero or one + // time; an optional term. + ZeroOrOnce Quantifier = "?" + + // ZeroOrMany indicates the quantified term may appear any number of + // times. + ZeroOrMany Quantifier = "*" +) + +// OperandCategory is an enumerator that groups operand kinds. +type OperandCategory string + +const ( + // OperandCategoryBitEnum describes an operand kind where its value is a + // mask, which is formed by combining the bits specified as enumerants in an + // enum. + OperandCategoryBitEnum = "BitEnum" + + // OperandCategoryValueEnum describes an operand kind where its value is an + // enumerant from an enum. + OperandCategoryValueEnum = "ValueEnum" + + // OperandCategoryID describes and operand kind where its value is an + // definition or reference. + OperandCategoryID = "Id" + + // OperandCategoryLiteral describes and operand kind where its value is an + // literal number or string. + OperandCategoryLiteral = "Literal" + + // OperandCategoryComposite describes and operand kind where its value is + // composed from operand values from the above categories. + OperandCategoryComposite = "Composite" +) + +// OpcodeMap is a map of opcode name to Opcode type. +type OpcodeMap map[string]*Opcode + +var ( + // Opcodes is a map of opcode name to Opcode description. + Opcodes = OpcodeMap { + "OpNop": OpNop, + "OpUndef": OpUndef, + "OpSourceContinued": OpSourceContinued, + "OpSource": OpSource, + "OpSourceExtension": OpSourceExtension, + "OpName": OpName, + "OpMemberName": OpMemberName, + "OpString": OpString, + "OpLine": OpLine, + "OpExtension": OpExtension, + "OpExtInstImport": OpExtInstImport, + "OpExtInst": OpExtInst, + "OpMemoryModel": OpMemoryModel, + "OpEntryPoint": OpEntryPoint, + "OpExecutionMode": OpExecutionMode, + "OpCapability": OpCapability, + "OpTypeVoid": OpTypeVoid, + "OpTypeBool": OpTypeBool, + "OpTypeInt": OpTypeInt, + "OpTypeFloat": OpTypeFloat, + "OpTypeVector": OpTypeVector, + "OpTypeMatrix": OpTypeMatrix, + "OpTypeImage": OpTypeImage, + "OpTypeSampler": OpTypeSampler, + "OpTypeSampledImage": OpTypeSampledImage, + "OpTypeArray": OpTypeArray, + "OpTypeRuntimeArray": OpTypeRuntimeArray, + "OpTypeStruct": OpTypeStruct, + "OpTypeOpaque": OpTypeOpaque, + "OpTypePointer": OpTypePointer, + "OpTypeFunction": OpTypeFunction, + "OpTypeEvent": OpTypeEvent, + "OpTypeDeviceEvent": OpTypeDeviceEvent, + "OpTypeReserveId": OpTypeReserveId, + "OpTypeQueue": OpTypeQueue, + "OpTypePipe": OpTypePipe, + "OpTypeForwardPointer": OpTypeForwardPointer, + "OpConstantTrue": OpConstantTrue, + "OpConstantFalse": OpConstantFalse, + "OpConstant": OpConstant, + "OpConstantComposite": OpConstantComposite, + "OpConstantSampler": OpConstantSampler, + "OpConstantNull": OpConstantNull, + "OpSpecConstantTrue": OpSpecConstantTrue, + "OpSpecConstantFalse": OpSpecConstantFalse, + "OpSpecConstant": OpSpecConstant, + "OpSpecConstantComposite": OpSpecConstantComposite, + "OpSpecConstantOp": OpSpecConstantOp, + "OpFunction": OpFunction, + "OpFunctionParameter": OpFunctionParameter, + "OpFunctionEnd": OpFunctionEnd, + "OpFunctionCall": OpFunctionCall, + "OpVariable": OpVariable, + "OpImageTexelPointer": OpImageTexelPointer, + "OpLoad": OpLoad, + "OpStore": OpStore, + "OpCopyMemory": OpCopyMemory, + "OpCopyMemorySized": OpCopyMemorySized, + "OpAccessChain": OpAccessChain, + "OpInBoundsAccessChain": OpInBoundsAccessChain, + "OpPtrAccessChain": OpPtrAccessChain, + "OpArrayLength": OpArrayLength, + "OpGenericPtrMemSemantics": OpGenericPtrMemSemantics, + "OpInBoundsPtrAccessChain": OpInBoundsPtrAccessChain, + "OpDecorate": OpDecorate, + "OpMemberDecorate": OpMemberDecorate, + "OpDecorationGroup": OpDecorationGroup, + "OpGroupDecorate": OpGroupDecorate, + "OpGroupMemberDecorate": OpGroupMemberDecorate, + "OpVectorExtractDynamic": OpVectorExtractDynamic, + "OpVectorInsertDynamic": OpVectorInsertDynamic, + "OpVectorShuffle": OpVectorShuffle, + "OpCompositeConstruct": OpCompositeConstruct, + "OpCompositeExtract": OpCompositeExtract, + "OpCompositeInsert": OpCompositeInsert, + "OpCopyObject": OpCopyObject, + "OpTranspose": OpTranspose, + "OpSampledImage": OpSampledImage, + "OpImageSampleImplicitLod": OpImageSampleImplicitLod, + "OpImageSampleExplicitLod": OpImageSampleExplicitLod, + "OpImageSampleDrefImplicitLod": OpImageSampleDrefImplicitLod, + "OpImageSampleDrefExplicitLod": OpImageSampleDrefExplicitLod, + "OpImageSampleProjImplicitLod": OpImageSampleProjImplicitLod, + "OpImageSampleProjExplicitLod": OpImageSampleProjExplicitLod, + "OpImageSampleProjDrefImplicitLod": OpImageSampleProjDrefImplicitLod, + "OpImageSampleProjDrefExplicitLod": OpImageSampleProjDrefExplicitLod, + "OpImageFetch": OpImageFetch, + "OpImageGather": OpImageGather, + "OpImageDrefGather": OpImageDrefGather, + "OpImageRead": OpImageRead, + "OpImageWrite": OpImageWrite, + "OpImage": OpImage, + "OpImageQueryFormat": OpImageQueryFormat, + "OpImageQueryOrder": OpImageQueryOrder, + "OpImageQuerySizeLod": OpImageQuerySizeLod, + "OpImageQuerySize": OpImageQuerySize, + "OpImageQueryLod": OpImageQueryLod, + "OpImageQueryLevels": OpImageQueryLevels, + "OpImageQuerySamples": OpImageQuerySamples, + "OpConvertFToU": OpConvertFToU, + "OpConvertFToS": OpConvertFToS, + "OpConvertSToF": OpConvertSToF, + "OpConvertUToF": OpConvertUToF, + "OpUConvert": OpUConvert, + "OpSConvert": OpSConvert, + "OpFConvert": OpFConvert, + "OpQuantizeToF16": OpQuantizeToF16, + "OpConvertPtrToU": OpConvertPtrToU, + "OpSatConvertSToU": OpSatConvertSToU, + "OpSatConvertUToS": OpSatConvertUToS, + "OpConvertUToPtr": OpConvertUToPtr, + "OpPtrCastToGeneric": OpPtrCastToGeneric, + "OpGenericCastToPtr": OpGenericCastToPtr, + "OpGenericCastToPtrExplicit": OpGenericCastToPtrExplicit, + "OpBitcast": OpBitcast, + "OpSNegate": OpSNegate, + "OpFNegate": OpFNegate, + "OpIAdd": OpIAdd, + "OpFAdd": OpFAdd, + "OpISub": OpISub, + "OpFSub": OpFSub, + "OpIMul": OpIMul, + "OpFMul": OpFMul, + "OpUDiv": OpUDiv, + "OpSDiv": OpSDiv, + "OpFDiv": OpFDiv, + "OpUMod": OpUMod, + "OpSRem": OpSRem, + "OpSMod": OpSMod, + "OpFRem": OpFRem, + "OpFMod": OpFMod, + "OpVectorTimesScalar": OpVectorTimesScalar, + "OpMatrixTimesScalar": OpMatrixTimesScalar, + "OpVectorTimesMatrix": OpVectorTimesMatrix, + "OpMatrixTimesVector": OpMatrixTimesVector, + "OpMatrixTimesMatrix": OpMatrixTimesMatrix, + "OpOuterProduct": OpOuterProduct, + "OpDot": OpDot, + "OpIAddCarry": OpIAddCarry, + "OpISubBorrow": OpISubBorrow, + "OpUMulExtended": OpUMulExtended, + "OpSMulExtended": OpSMulExtended, + "OpAny": OpAny, + "OpAll": OpAll, + "OpIsNan": OpIsNan, + "OpIsInf": OpIsInf, + "OpIsFinite": OpIsFinite, + "OpIsNormal": OpIsNormal, + "OpSignBitSet": OpSignBitSet, + "OpLessOrGreater": OpLessOrGreater, + "OpOrdered": OpOrdered, + "OpUnordered": OpUnordered, + "OpLogicalEqual": OpLogicalEqual, + "OpLogicalNotEqual": OpLogicalNotEqual, + "OpLogicalOr": OpLogicalOr, + "OpLogicalAnd": OpLogicalAnd, + "OpLogicalNot": OpLogicalNot, + "OpSelect": OpSelect, + "OpIEqual": OpIEqual, + "OpINotEqual": OpINotEqual, + "OpUGreaterThan": OpUGreaterThan, + "OpSGreaterThan": OpSGreaterThan, + "OpUGreaterThanEqual": OpUGreaterThanEqual, + "OpSGreaterThanEqual": OpSGreaterThanEqual, + "OpULessThan": OpULessThan, + "OpSLessThan": OpSLessThan, + "OpULessThanEqual": OpULessThanEqual, + "OpSLessThanEqual": OpSLessThanEqual, + "OpFOrdEqual": OpFOrdEqual, + "OpFUnordEqual": OpFUnordEqual, + "OpFOrdNotEqual": OpFOrdNotEqual, + "OpFUnordNotEqual": OpFUnordNotEqual, + "OpFOrdLessThan": OpFOrdLessThan, + "OpFUnordLessThan": OpFUnordLessThan, + "OpFOrdGreaterThan": OpFOrdGreaterThan, + "OpFUnordGreaterThan": OpFUnordGreaterThan, + "OpFOrdLessThanEqual": OpFOrdLessThanEqual, + "OpFUnordLessThanEqual": OpFUnordLessThanEqual, + "OpFOrdGreaterThanEqual": OpFOrdGreaterThanEqual, + "OpFUnordGreaterThanEqual": OpFUnordGreaterThanEqual, + "OpShiftRightLogical": OpShiftRightLogical, + "OpShiftRightArithmetic": OpShiftRightArithmetic, + "OpShiftLeftLogical": OpShiftLeftLogical, + "OpBitwiseOr": OpBitwiseOr, + "OpBitwiseXor": OpBitwiseXor, + "OpBitwiseAnd": OpBitwiseAnd, + "OpNot": OpNot, + "OpBitFieldInsert": OpBitFieldInsert, + "OpBitFieldSExtract": OpBitFieldSExtract, + "OpBitFieldUExtract": OpBitFieldUExtract, + "OpBitReverse": OpBitReverse, + "OpBitCount": OpBitCount, + "OpDPdx": OpDPdx, + "OpDPdy": OpDPdy, + "OpFwidth": OpFwidth, + "OpDPdxFine": OpDPdxFine, + "OpDPdyFine": OpDPdyFine, + "OpFwidthFine": OpFwidthFine, + "OpDPdxCoarse": OpDPdxCoarse, + "OpDPdyCoarse": OpDPdyCoarse, + "OpFwidthCoarse": OpFwidthCoarse, + "OpEmitVertex": OpEmitVertex, + "OpEndPrimitive": OpEndPrimitive, + "OpEmitStreamVertex": OpEmitStreamVertex, + "OpEndStreamPrimitive": OpEndStreamPrimitive, + "OpControlBarrier": OpControlBarrier, + "OpMemoryBarrier": OpMemoryBarrier, + "OpAtomicLoad": OpAtomicLoad, + "OpAtomicStore": OpAtomicStore, + "OpAtomicExchange": OpAtomicExchange, + "OpAtomicCompareExchange": OpAtomicCompareExchange, + "OpAtomicCompareExchangeWeak": OpAtomicCompareExchangeWeak, + "OpAtomicIIncrement": OpAtomicIIncrement, + "OpAtomicIDecrement": OpAtomicIDecrement, + "OpAtomicIAdd": OpAtomicIAdd, + "OpAtomicISub": OpAtomicISub, + "OpAtomicSMin": OpAtomicSMin, + "OpAtomicUMin": OpAtomicUMin, + "OpAtomicSMax": OpAtomicSMax, + "OpAtomicUMax": OpAtomicUMax, + "OpAtomicAnd": OpAtomicAnd, + "OpAtomicOr": OpAtomicOr, + "OpAtomicXor": OpAtomicXor, + "OpPhi": OpPhi, + "OpLoopMerge": OpLoopMerge, + "OpSelectionMerge": OpSelectionMerge, + "OpLabel": OpLabel, + "OpBranch": OpBranch, + "OpBranchConditional": OpBranchConditional, + "OpSwitch": OpSwitch, + "OpKill": OpKill, + "OpReturn": OpReturn, + "OpReturnValue": OpReturnValue, + "OpUnreachable": OpUnreachable, + "OpLifetimeStart": OpLifetimeStart, + "OpLifetimeStop": OpLifetimeStop, + "OpGroupAsyncCopy": OpGroupAsyncCopy, + "OpGroupWaitEvents": OpGroupWaitEvents, + "OpGroupAll": OpGroupAll, + "OpGroupAny": OpGroupAny, + "OpGroupBroadcast": OpGroupBroadcast, + "OpGroupIAdd": OpGroupIAdd, + "OpGroupFAdd": OpGroupFAdd, + "OpGroupFMin": OpGroupFMin, + "OpGroupUMin": OpGroupUMin, + "OpGroupSMin": OpGroupSMin, + "OpGroupFMax": OpGroupFMax, + "OpGroupUMax": OpGroupUMax, + "OpGroupSMax": OpGroupSMax, + "OpReadPipe": OpReadPipe, + "OpWritePipe": OpWritePipe, + "OpReservedReadPipe": OpReservedReadPipe, + "OpReservedWritePipe": OpReservedWritePipe, + "OpReserveReadPipePackets": OpReserveReadPipePackets, + "OpReserveWritePipePackets": OpReserveWritePipePackets, + "OpCommitReadPipe": OpCommitReadPipe, + "OpCommitWritePipe": OpCommitWritePipe, + "OpIsValidReserveId": OpIsValidReserveId, + "OpGetNumPipePackets": OpGetNumPipePackets, + "OpGetMaxPipePackets": OpGetMaxPipePackets, + "OpGroupReserveReadPipePackets": OpGroupReserveReadPipePackets, + "OpGroupReserveWritePipePackets": OpGroupReserveWritePipePackets, + "OpGroupCommitReadPipe": OpGroupCommitReadPipe, + "OpGroupCommitWritePipe": OpGroupCommitWritePipe, + "OpEnqueueMarker": OpEnqueueMarker, + "OpEnqueueKernel": OpEnqueueKernel, + "OpGetKernelNDrangeSubGroupCount": OpGetKernelNDrangeSubGroupCount, + "OpGetKernelNDrangeMaxSubGroupSize": OpGetKernelNDrangeMaxSubGroupSize, + "OpGetKernelWorkGroupSize": OpGetKernelWorkGroupSize, + "OpGetKernelPreferredWorkGroupSizeMultiple": OpGetKernelPreferredWorkGroupSizeMultiple, + "OpRetainEvent": OpRetainEvent, + "OpReleaseEvent": OpReleaseEvent, + "OpCreateUserEvent": OpCreateUserEvent, + "OpIsValidEvent": OpIsValidEvent, + "OpSetUserEventStatus": OpSetUserEventStatus, + "OpCaptureEventProfilingInfo": OpCaptureEventProfilingInfo, + "OpGetDefaultQueue": OpGetDefaultQueue, + "OpBuildNDRange": OpBuildNDRange, + "OpImageSparseSampleImplicitLod": OpImageSparseSampleImplicitLod, + "OpImageSparseSampleExplicitLod": OpImageSparseSampleExplicitLod, + "OpImageSparseSampleDrefImplicitLod": OpImageSparseSampleDrefImplicitLod, + "OpImageSparseSampleDrefExplicitLod": OpImageSparseSampleDrefExplicitLod, + "OpImageSparseSampleProjImplicitLod": OpImageSparseSampleProjImplicitLod, + "OpImageSparseSampleProjExplicitLod": OpImageSparseSampleProjExplicitLod, + "OpImageSparseSampleProjDrefImplicitLod": OpImageSparseSampleProjDrefImplicitLod, + "OpImageSparseSampleProjDrefExplicitLod": OpImageSparseSampleProjDrefExplicitLod, + "OpImageSparseFetch": OpImageSparseFetch, + "OpImageSparseGather": OpImageSparseGather, + "OpImageSparseDrefGather": OpImageSparseDrefGather, + "OpImageSparseTexelsResident": OpImageSparseTexelsResident, + "OpNoLine": OpNoLine, + "OpAtomicFlagTestAndSet": OpAtomicFlagTestAndSet, + "OpAtomicFlagClear": OpAtomicFlagClear, + "OpImageSparseRead": OpImageSparseRead, + "OpSizeOf": OpSizeOf, + "OpTypePipeStorage": OpTypePipeStorage, + "OpConstantPipeStorage": OpConstantPipeStorage, + "OpCreatePipeFromPipeStorage": OpCreatePipeFromPipeStorage, + "OpGetKernelLocalSizeForSubgroupCount": OpGetKernelLocalSizeForSubgroupCount, + "OpGetKernelMaxNumSubgroups": OpGetKernelMaxNumSubgroups, + "OpTypeNamedBarrier": OpTypeNamedBarrier, + "OpNamedBarrierInitialize": OpNamedBarrierInitialize, + "OpMemoryNamedBarrier": OpMemoryNamedBarrier, + "OpModuleProcessed": OpModuleProcessed, + "OpExecutionModeId": OpExecutionModeId, + "OpDecorateId": OpDecorateId, + "OpGroupNonUniformElect": OpGroupNonUniformElect, + "OpGroupNonUniformAll": OpGroupNonUniformAll, + "OpGroupNonUniformAny": OpGroupNonUniformAny, + "OpGroupNonUniformAllEqual": OpGroupNonUniformAllEqual, + "OpGroupNonUniformBroadcast": OpGroupNonUniformBroadcast, + "OpGroupNonUniformBroadcastFirst": OpGroupNonUniformBroadcastFirst, + "OpGroupNonUniformBallot": OpGroupNonUniformBallot, + "OpGroupNonUniformInverseBallot": OpGroupNonUniformInverseBallot, + "OpGroupNonUniformBallotBitExtract": OpGroupNonUniformBallotBitExtract, + "OpGroupNonUniformBallotBitCount": OpGroupNonUniformBallotBitCount, + "OpGroupNonUniformBallotFindLSB": OpGroupNonUniformBallotFindLSB, + "OpGroupNonUniformBallotFindMSB": OpGroupNonUniformBallotFindMSB, + "OpGroupNonUniformShuffle": OpGroupNonUniformShuffle, + "OpGroupNonUniformShuffleXor": OpGroupNonUniformShuffleXor, + "OpGroupNonUniformShuffleUp": OpGroupNonUniformShuffleUp, + "OpGroupNonUniformShuffleDown": OpGroupNonUniformShuffleDown, + "OpGroupNonUniformIAdd": OpGroupNonUniformIAdd, + "OpGroupNonUniformFAdd": OpGroupNonUniformFAdd, + "OpGroupNonUniformIMul": OpGroupNonUniformIMul, + "OpGroupNonUniformFMul": OpGroupNonUniformFMul, + "OpGroupNonUniformSMin": OpGroupNonUniformSMin, + "OpGroupNonUniformUMin": OpGroupNonUniformUMin, + "OpGroupNonUniformFMin": OpGroupNonUniformFMin, + "OpGroupNonUniformSMax": OpGroupNonUniformSMax, + "OpGroupNonUniformUMax": OpGroupNonUniformUMax, + "OpGroupNonUniformFMax": OpGroupNonUniformFMax, + "OpGroupNonUniformBitwiseAnd": OpGroupNonUniformBitwiseAnd, + "OpGroupNonUniformBitwiseOr": OpGroupNonUniformBitwiseOr, + "OpGroupNonUniformBitwiseXor": OpGroupNonUniformBitwiseXor, + "OpGroupNonUniformLogicalAnd": OpGroupNonUniformLogicalAnd, + "OpGroupNonUniformLogicalOr": OpGroupNonUniformLogicalOr, + "OpGroupNonUniformLogicalXor": OpGroupNonUniformLogicalXor, + "OpGroupNonUniformQuadBroadcast": OpGroupNonUniformQuadBroadcast, + "OpGroupNonUniformQuadSwap": OpGroupNonUniformQuadSwap, + "OpCopyLogical": OpCopyLogical, + "OpPtrEqual": OpPtrEqual, + "OpPtrNotEqual": OpPtrNotEqual, + "OpPtrDiff": OpPtrDiff, + "OpSubgroupBallotKHR": OpSubgroupBallotKHR, + "OpSubgroupFirstInvocationKHR": OpSubgroupFirstInvocationKHR, + "OpSubgroupAllKHR": OpSubgroupAllKHR, + "OpSubgroupAnyKHR": OpSubgroupAnyKHR, + "OpSubgroupAllEqualKHR": OpSubgroupAllEqualKHR, + "OpSubgroupReadInvocationKHR": OpSubgroupReadInvocationKHR, + "OpGroupIAddNonUniformAMD": OpGroupIAddNonUniformAMD, + "OpGroupFAddNonUniformAMD": OpGroupFAddNonUniformAMD, + "OpGroupFMinNonUniformAMD": OpGroupFMinNonUniformAMD, + "OpGroupUMinNonUniformAMD": OpGroupUMinNonUniformAMD, + "OpGroupSMinNonUniformAMD": OpGroupSMinNonUniformAMD, + "OpGroupFMaxNonUniformAMD": OpGroupFMaxNonUniformAMD, + "OpGroupUMaxNonUniformAMD": OpGroupUMaxNonUniformAMD, + "OpGroupSMaxNonUniformAMD": OpGroupSMaxNonUniformAMD, + "OpFragmentMaskFetchAMD": OpFragmentMaskFetchAMD, + "OpFragmentFetchAMD": OpFragmentFetchAMD, + "OpReadClockKHR": OpReadClockKHR, + "OpImageSampleFootprintNV": OpImageSampleFootprintNV, + "OpGroupNonUniformPartitionNV": OpGroupNonUniformPartitionNV, + "OpWritePackedPrimitiveIndices4x8NV": OpWritePackedPrimitiveIndices4x8NV, + "OpReportIntersectionNV": OpReportIntersectionNV, + "OpReportIntersectionKHR": OpReportIntersectionKHR, + "OpIgnoreIntersectionNV": OpIgnoreIntersectionNV, + "OpIgnoreIntersectionKHR": OpIgnoreIntersectionKHR, + "OpTerminateRayNV": OpTerminateRayNV, + "OpTerminateRayKHR": OpTerminateRayKHR, + "OpTraceNV": OpTraceNV, + "OpTraceRayKHR": OpTraceRayKHR, + "OpTypeAccelerationStructureNV": OpTypeAccelerationStructureNV, + "OpTypeAccelerationStructureKHR": OpTypeAccelerationStructureKHR, + "OpTypeRayQueryProvisionalKHR": OpTypeRayQueryProvisionalKHR, + "OpRayQueryInitializeKHR": OpRayQueryInitializeKHR, + "OpRayQueryTerminateKHR": OpRayQueryTerminateKHR, + "OpRayQueryGenerateIntersectionKHR": OpRayQueryGenerateIntersectionKHR, + "OpRayQueryConfirmIntersectionKHR": OpRayQueryConfirmIntersectionKHR, + "OpRayQueryProceedKHR": OpRayQueryProceedKHR, + "OpRayQueryGetIntersectionTypeKHR": OpRayQueryGetIntersectionTypeKHR, + "OpRayQueryGetRayTMinKHR": OpRayQueryGetRayTMinKHR, + "OpRayQueryGetRayFlagsKHR": OpRayQueryGetRayFlagsKHR, + "OpRayQueryGetIntersectionTKHR": OpRayQueryGetIntersectionTKHR, + "OpRayQueryGetIntersectionInstanceCustomIndexKHR": OpRayQueryGetIntersectionInstanceCustomIndexKHR, + "OpRayQueryGetIntersectionInstanceIdKHR": OpRayQueryGetIntersectionInstanceIdKHR, + "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR, + "OpRayQueryGetIntersectionGeometryIndexKHR": OpRayQueryGetIntersectionGeometryIndexKHR, + "OpRayQueryGetIntersectionPrimitiveIndexKHR": OpRayQueryGetIntersectionPrimitiveIndexKHR, + "OpRayQueryGetIntersectionBarycentricsKHR": OpRayQueryGetIntersectionBarycentricsKHR, + "OpRayQueryGetIntersectionFrontFaceKHR": OpRayQueryGetIntersectionFrontFaceKHR, + "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": OpRayQueryGetIntersectionCandidateAABBOpaqueKHR, + "OpRayQueryGetIntersectionObjectRayDirectionKHR": OpRayQueryGetIntersectionObjectRayDirectionKHR, + "OpRayQueryGetIntersectionObjectRayOriginKHR": OpRayQueryGetIntersectionObjectRayOriginKHR, + "OpRayQueryGetWorldRayDirectionKHR": OpRayQueryGetWorldRayDirectionKHR, + "OpRayQueryGetWorldRayOriginKHR": OpRayQueryGetWorldRayOriginKHR, + "OpRayQueryGetIntersectionObjectToWorldKHR": OpRayQueryGetIntersectionObjectToWorldKHR, + "OpRayQueryGetIntersectionWorldToObjectKHR": OpRayQueryGetIntersectionWorldToObjectKHR, + "OpExecuteCallableNV": OpExecuteCallableNV, + "OpExecuteCallableKHR": OpExecuteCallableKHR, + "OpTypeCooperativeMatrixNV": OpTypeCooperativeMatrixNV, + "OpCooperativeMatrixLoadNV": OpCooperativeMatrixLoadNV, + "OpCooperativeMatrixStoreNV": OpCooperativeMatrixStoreNV, + "OpCooperativeMatrixMulAddNV": OpCooperativeMatrixMulAddNV, + "OpCooperativeMatrixLengthNV": OpCooperativeMatrixLengthNV, + "OpBeginInvocationInterlockEXT": OpBeginInvocationInterlockEXT, + "OpEndInvocationInterlockEXT": OpEndInvocationInterlockEXT, + "OpDemoteToHelperInvocationEXT": OpDemoteToHelperInvocationEXT, + "OpIsHelperInvocationEXT": OpIsHelperInvocationEXT, + "OpSubgroupShuffleINTEL": OpSubgroupShuffleINTEL, + "OpSubgroupShuffleDownINTEL": OpSubgroupShuffleDownINTEL, + "OpSubgroupShuffleUpINTEL": OpSubgroupShuffleUpINTEL, + "OpSubgroupShuffleXorINTEL": OpSubgroupShuffleXorINTEL, + "OpSubgroupBlockReadINTEL": OpSubgroupBlockReadINTEL, + "OpSubgroupBlockWriteINTEL": OpSubgroupBlockWriteINTEL, + "OpSubgroupImageBlockReadINTEL": OpSubgroupImageBlockReadINTEL, + "OpSubgroupImageBlockWriteINTEL": OpSubgroupImageBlockWriteINTEL, + "OpSubgroupImageMediaBlockReadINTEL": OpSubgroupImageMediaBlockReadINTEL, + "OpSubgroupImageMediaBlockWriteINTEL": OpSubgroupImageMediaBlockWriteINTEL, + "OpUCountLeadingZerosINTEL": OpUCountLeadingZerosINTEL, + "OpUCountTrailingZerosINTEL": OpUCountTrailingZerosINTEL, + "OpAbsISubINTEL": OpAbsISubINTEL, + "OpAbsUSubINTEL": OpAbsUSubINTEL, + "OpIAddSatINTEL": OpIAddSatINTEL, + "OpUAddSatINTEL": OpUAddSatINTEL, + "OpIAverageINTEL": OpIAverageINTEL, + "OpUAverageINTEL": OpUAverageINTEL, + "OpIAverageRoundedINTEL": OpIAverageRoundedINTEL, + "OpUAverageRoundedINTEL": OpUAverageRoundedINTEL, + "OpISubSatINTEL": OpISubSatINTEL, + "OpUSubSatINTEL": OpUSubSatINTEL, + "OpIMul32x16INTEL": OpIMul32x16INTEL, + "OpUMul32x16INTEL": OpUMul32x16INTEL, + "OpDecorateString": OpDecorateString, + "OpDecorateStringGOOGLE": OpDecorateStringGOOGLE, + "OpMemberDecorateString": OpMemberDecorateString, + "OpMemberDecorateStringGOOGLE": OpMemberDecorateStringGOOGLE, + "OpVmeImageINTEL": OpVmeImageINTEL, + "OpTypeVmeImageINTEL": OpTypeVmeImageINTEL, + "OpTypeAvcImePayloadINTEL": OpTypeAvcImePayloadINTEL, + "OpTypeAvcRefPayloadINTEL": OpTypeAvcRefPayloadINTEL, + "OpTypeAvcSicPayloadINTEL": OpTypeAvcSicPayloadINTEL, + "OpTypeAvcMcePayloadINTEL": OpTypeAvcMcePayloadINTEL, + "OpTypeAvcMceResultINTEL": OpTypeAvcMceResultINTEL, + "OpTypeAvcImeResultINTEL": OpTypeAvcImeResultINTEL, + "OpTypeAvcImeResultSingleReferenceStreamoutINTEL": OpTypeAvcImeResultSingleReferenceStreamoutINTEL, + "OpTypeAvcImeResultDualReferenceStreamoutINTEL": OpTypeAvcImeResultDualReferenceStreamoutINTEL, + "OpTypeAvcImeSingleReferenceStreaminINTEL": OpTypeAvcImeSingleReferenceStreaminINTEL, + "OpTypeAvcImeDualReferenceStreaminINTEL": OpTypeAvcImeDualReferenceStreaminINTEL, + "OpTypeAvcRefResultINTEL": OpTypeAvcRefResultINTEL, + "OpTypeAvcSicResultINTEL": OpTypeAvcSicResultINTEL, + "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL": OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL, + "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL": OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL, + "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL": OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL, + "OpSubgroupAvcMceSetInterShapePenaltyINTEL": OpSubgroupAvcMceSetInterShapePenaltyINTEL, + "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL": OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL, + "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL": OpSubgroupAvcMceSetInterDirectionPenaltyINTEL, + "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL, + "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL": OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL, + "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL, + "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL, + "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL": OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL, + "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL": OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL, + "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL, + "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL": OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL, + "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL": OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL, + "OpSubgroupAvcMceSetAcOnlyHaarINTEL": OpSubgroupAvcMceSetAcOnlyHaarINTEL, + "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL": OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL, + "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL": OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL, + "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL": OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL, + "OpSubgroupAvcMceConvertToImePayloadINTEL": OpSubgroupAvcMceConvertToImePayloadINTEL, + "OpSubgroupAvcMceConvertToImeResultINTEL": OpSubgroupAvcMceConvertToImeResultINTEL, + "OpSubgroupAvcMceConvertToRefPayloadINTEL": OpSubgroupAvcMceConvertToRefPayloadINTEL, + "OpSubgroupAvcMceConvertToRefResultINTEL": OpSubgroupAvcMceConvertToRefResultINTEL, + "OpSubgroupAvcMceConvertToSicPayloadINTEL": OpSubgroupAvcMceConvertToSicPayloadINTEL, + "OpSubgroupAvcMceConvertToSicResultINTEL": OpSubgroupAvcMceConvertToSicResultINTEL, + "OpSubgroupAvcMceGetMotionVectorsINTEL": OpSubgroupAvcMceGetMotionVectorsINTEL, + "OpSubgroupAvcMceGetInterDistortionsINTEL": OpSubgroupAvcMceGetInterDistortionsINTEL, + "OpSubgroupAvcMceGetBestInterDistortionsINTEL": OpSubgroupAvcMceGetBestInterDistortionsINTEL, + "OpSubgroupAvcMceGetInterMajorShapeINTEL": OpSubgroupAvcMceGetInterMajorShapeINTEL, + "OpSubgroupAvcMceGetInterMinorShapeINTEL": OpSubgroupAvcMceGetInterMinorShapeINTEL, + "OpSubgroupAvcMceGetInterDirectionsINTEL": OpSubgroupAvcMceGetInterDirectionsINTEL, + "OpSubgroupAvcMceGetInterMotionVectorCountINTEL": OpSubgroupAvcMceGetInterMotionVectorCountINTEL, + "OpSubgroupAvcMceGetInterReferenceIdsINTEL": OpSubgroupAvcMceGetInterReferenceIdsINTEL, + "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL": OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL, + "OpSubgroupAvcImeInitializeINTEL": OpSubgroupAvcImeInitializeINTEL, + "OpSubgroupAvcImeSetSingleReferenceINTEL": OpSubgroupAvcImeSetSingleReferenceINTEL, + "OpSubgroupAvcImeSetDualReferenceINTEL": OpSubgroupAvcImeSetDualReferenceINTEL, + "OpSubgroupAvcImeRefWindowSizeINTEL": OpSubgroupAvcImeRefWindowSizeINTEL, + "OpSubgroupAvcImeAdjustRefOffsetINTEL": OpSubgroupAvcImeAdjustRefOffsetINTEL, + "OpSubgroupAvcImeConvertToMcePayloadINTEL": OpSubgroupAvcImeConvertToMcePayloadINTEL, + "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL": OpSubgroupAvcImeSetMaxMotionVectorCountINTEL, + "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL": OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL, + "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL": OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL, + "OpSubgroupAvcImeSetWeightedSadINTEL": OpSubgroupAvcImeSetWeightedSadINTEL, + "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL, + "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceINTEL, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL": OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL": OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL, + "OpSubgroupAvcImeConvertToMceResultINTEL": OpSubgroupAvcImeConvertToMceResultINTEL, + "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL": OpSubgroupAvcImeGetSingleReferenceStreaminINTEL, + "OpSubgroupAvcImeGetDualReferenceStreaminINTEL": OpSubgroupAvcImeGetDualReferenceStreaminINTEL, + "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL": OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL, + "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL": OpSubgroupAvcImeStripDualReferenceStreamoutINTEL, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL": OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL": OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL, + "OpSubgroupAvcImeGetBorderReachedINTEL": OpSubgroupAvcImeGetBorderReachedINTEL, + "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL": OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL, + "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL": OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL, + "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL": OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL, + "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL": OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL, + "OpSubgroupAvcFmeInitializeINTEL": OpSubgroupAvcFmeInitializeINTEL, + "OpSubgroupAvcBmeInitializeINTEL": OpSubgroupAvcBmeInitializeINTEL, + "OpSubgroupAvcRefConvertToMcePayloadINTEL": OpSubgroupAvcRefConvertToMcePayloadINTEL, + "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL": OpSubgroupAvcRefSetBidirectionalMixDisableINTEL, + "OpSubgroupAvcRefSetBilinearFilterEnableINTEL": OpSubgroupAvcRefSetBilinearFilterEnableINTEL, + "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL": OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL, + "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL": OpSubgroupAvcRefEvaluateWithDualReferenceINTEL, + "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL": OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL, + "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL": OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL, + "OpSubgroupAvcRefConvertToMceResultINTEL": OpSubgroupAvcRefConvertToMceResultINTEL, + "OpSubgroupAvcSicInitializeINTEL": OpSubgroupAvcSicInitializeINTEL, + "OpSubgroupAvcSicConfigureSkcINTEL": OpSubgroupAvcSicConfigureSkcINTEL, + "OpSubgroupAvcSicConfigureIpeLumaINTEL": OpSubgroupAvcSicConfigureIpeLumaINTEL, + "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL": OpSubgroupAvcSicConfigureIpeLumaChromaINTEL, + "OpSubgroupAvcSicGetMotionVectorMaskINTEL": OpSubgroupAvcSicGetMotionVectorMaskINTEL, + "OpSubgroupAvcSicConvertToMcePayloadINTEL": OpSubgroupAvcSicConvertToMcePayloadINTEL, + "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL": OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL, + "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL": OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL, + "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL": OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL, + "OpSubgroupAvcSicSetBilinearFilterEnableINTEL": OpSubgroupAvcSicSetBilinearFilterEnableINTEL, + "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL": OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL, + "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL": OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL, + "OpSubgroupAvcSicEvaluateIpeINTEL": OpSubgroupAvcSicEvaluateIpeINTEL, + "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL": OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL, + "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL": OpSubgroupAvcSicEvaluateWithDualReferenceINTEL, + "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL": OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL, + "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL": OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL, + "OpSubgroupAvcSicConvertToMceResultINTEL": OpSubgroupAvcSicConvertToMceResultINTEL, + "OpSubgroupAvcSicGetIpeLumaShapeINTEL": OpSubgroupAvcSicGetIpeLumaShapeINTEL, + "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL": OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL, + "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL": OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL, + "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL": OpSubgroupAvcSicGetPackedIpeLumaModesINTEL, + "OpSubgroupAvcSicGetIpeChromaModeINTEL": OpSubgroupAvcSicGetIpeChromaModeINTEL, + "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL, + "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL": OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL, + "OpSubgroupAvcSicGetInterRawSadsINTEL": OpSubgroupAvcSicGetInterRawSadsINTEL, + } + + // ExtOpcodes is a map of extension name to Opcode description list. + ExtOpcodes = map[string]OpcodeMap { + "GLSL.std.450": { + "Round": GLSLStd450_Round, + "RoundEven": GLSLStd450_RoundEven, + "Trunc": GLSLStd450_Trunc, + "FAbs": GLSLStd450_FAbs, + "SAbs": GLSLStd450_SAbs, + "FSign": GLSLStd450_FSign, + "SSign": GLSLStd450_SSign, + "Floor": GLSLStd450_Floor, + "Ceil": GLSLStd450_Ceil, + "Fract": GLSLStd450_Fract, + "Radians": GLSLStd450_Radians, + "Degrees": GLSLStd450_Degrees, + "Sin": GLSLStd450_Sin, + "Cos": GLSLStd450_Cos, + "Tan": GLSLStd450_Tan, + "Asin": GLSLStd450_Asin, + "Acos": GLSLStd450_Acos, + "Atan": GLSLStd450_Atan, + "Sinh": GLSLStd450_Sinh, + "Cosh": GLSLStd450_Cosh, + "Tanh": GLSLStd450_Tanh, + "Asinh": GLSLStd450_Asinh, + "Acosh": GLSLStd450_Acosh, + "Atanh": GLSLStd450_Atanh, + "Atan2": GLSLStd450_Atan2, + "Pow": GLSLStd450_Pow, + "Exp": GLSLStd450_Exp, + "Log": GLSLStd450_Log, + "Exp2": GLSLStd450_Exp2, + "Log2": GLSLStd450_Log2, + "Sqrt": GLSLStd450_Sqrt, + "InverseSqrt": GLSLStd450_InverseSqrt, + "Determinant": GLSLStd450_Determinant, + "MatrixInverse": GLSLStd450_MatrixInverse, + "Modf": GLSLStd450_Modf, + "ModfStruct": GLSLStd450_ModfStruct, + "FMin": GLSLStd450_FMin, + "UMin": GLSLStd450_UMin, + "SMin": GLSLStd450_SMin, + "FMax": GLSLStd450_FMax, + "UMax": GLSLStd450_UMax, + "SMax": GLSLStd450_SMax, + "FClamp": GLSLStd450_FClamp, + "UClamp": GLSLStd450_UClamp, + "SClamp": GLSLStd450_SClamp, + "FMix": GLSLStd450_FMix, + "IMix": GLSLStd450_IMix, + "Step": GLSLStd450_Step, + "SmoothStep": GLSLStd450_SmoothStep, + "Fma": GLSLStd450_Fma, + "Frexp": GLSLStd450_Frexp, + "FrexpStruct": GLSLStd450_FrexpStruct, + "Ldexp": GLSLStd450_Ldexp, + "PackSnorm4x8": GLSLStd450_PackSnorm4x8, + "PackUnorm4x8": GLSLStd450_PackUnorm4x8, + "PackSnorm2x16": GLSLStd450_PackSnorm2x16, + "PackUnorm2x16": GLSLStd450_PackUnorm2x16, + "PackHalf2x16": GLSLStd450_PackHalf2x16, + "PackDouble2x32": GLSLStd450_PackDouble2x32, + "UnpackSnorm2x16": GLSLStd450_UnpackSnorm2x16, + "UnpackUnorm2x16": GLSLStd450_UnpackUnorm2x16, + "UnpackHalf2x16": GLSLStd450_UnpackHalf2x16, + "UnpackSnorm4x8": GLSLStd450_UnpackSnorm4x8, + "UnpackUnorm4x8": GLSLStd450_UnpackUnorm4x8, + "UnpackDouble2x32": GLSLStd450_UnpackDouble2x32, + "Length": GLSLStd450_Length, + "Distance": GLSLStd450_Distance, + "Cross": GLSLStd450_Cross, + "Normalize": GLSLStd450_Normalize, + "FaceForward": GLSLStd450_FaceForward, + "Reflect": GLSLStd450_Reflect, + "Refract": GLSLStd450_Refract, + "FindILsb": GLSLStd450_FindILsb, + "FindSMsb": GLSLStd450_FindSMsb, + "FindUMsb": GLSLStd450_FindUMsb, + "InterpolateAtCentroid": GLSLStd450_InterpolateAtCentroid, + "InterpolateAtSample": GLSLStd450_InterpolateAtSample, + "InterpolateAtOffset": GLSLStd450_InterpolateAtOffset, + "NMin": GLSLStd450_NMin, + "NMax": GLSLStd450_NMax, + "NClamp": GLSLStd450_NClamp, + }, + "OpenCL.std": { + "acos": OpenCLStd_acos, + "acosh": OpenCLStd_acosh, + "acospi": OpenCLStd_acospi, + "asin": OpenCLStd_asin, + "asinh": OpenCLStd_asinh, + "asinpi": OpenCLStd_asinpi, + "atan": OpenCLStd_atan, + "atan2": OpenCLStd_atan2, + "atanh": OpenCLStd_atanh, + "atanpi": OpenCLStd_atanpi, + "atan2pi": OpenCLStd_atan2pi, + "cbrt": OpenCLStd_cbrt, + "ceil": OpenCLStd_ceil, + "copysign": OpenCLStd_copysign, + "cos": OpenCLStd_cos, + "cosh": OpenCLStd_cosh, + "cospi": OpenCLStd_cospi, + "erfc": OpenCLStd_erfc, + "erf": OpenCLStd_erf, + "exp": OpenCLStd_exp, + "exp2": OpenCLStd_exp2, + "exp10": OpenCLStd_exp10, + "expm1": OpenCLStd_expm1, + "fabs": OpenCLStd_fabs, + "fdim": OpenCLStd_fdim, + "floor": OpenCLStd_floor, + "fma": OpenCLStd_fma, + "fmax": OpenCLStd_fmax, + "fmin": OpenCLStd_fmin, + "fmod": OpenCLStd_fmod, + "fract": OpenCLStd_fract, + "frexp": OpenCLStd_frexp, + "hypot": OpenCLStd_hypot, + "ilogb": OpenCLStd_ilogb, + "ldexp": OpenCLStd_ldexp, + "lgamma": OpenCLStd_lgamma, + "lgamma_r": OpenCLStd_lgamma_r, + "log": OpenCLStd_log, + "log2": OpenCLStd_log2, + "log10": OpenCLStd_log10, + "log1p": OpenCLStd_log1p, + "logb": OpenCLStd_logb, + "mad": OpenCLStd_mad, + "maxmag": OpenCLStd_maxmag, + "minmag": OpenCLStd_minmag, + "modf": OpenCLStd_modf, + "nan": OpenCLStd_nan, + "nextafter": OpenCLStd_nextafter, + "pow": OpenCLStd_pow, + "pown": OpenCLStd_pown, + "powr": OpenCLStd_powr, + "remainder": OpenCLStd_remainder, + "remquo": OpenCLStd_remquo, + "rint": OpenCLStd_rint, + "rootn": OpenCLStd_rootn, + "round": OpenCLStd_round, + "rsqrt": OpenCLStd_rsqrt, + "sin": OpenCLStd_sin, + "sincos": OpenCLStd_sincos, + "sinh": OpenCLStd_sinh, + "sinpi": OpenCLStd_sinpi, + "sqrt": OpenCLStd_sqrt, + "tan": OpenCLStd_tan, + "tanh": OpenCLStd_tanh, + "tanpi": OpenCLStd_tanpi, + "tgamma": OpenCLStd_tgamma, + "trunc": OpenCLStd_trunc, + "half_cos": OpenCLStd_half_cos, + "half_divide": OpenCLStd_half_divide, + "half_exp": OpenCLStd_half_exp, + "half_exp2": OpenCLStd_half_exp2, + "half_exp10": OpenCLStd_half_exp10, + "half_log": OpenCLStd_half_log, + "half_log2": OpenCLStd_half_log2, + "half_log10": OpenCLStd_half_log10, + "half_powr": OpenCLStd_half_powr, + "half_recip": OpenCLStd_half_recip, + "half_rsqrt": OpenCLStd_half_rsqrt, + "half_sin": OpenCLStd_half_sin, + "half_sqrt": OpenCLStd_half_sqrt, + "half_tan": OpenCLStd_half_tan, + "native_cos": OpenCLStd_native_cos, + "native_divide": OpenCLStd_native_divide, + "native_exp": OpenCLStd_native_exp, + "native_exp2": OpenCLStd_native_exp2, + "native_exp10": OpenCLStd_native_exp10, + "native_log": OpenCLStd_native_log, + "native_log2": OpenCLStd_native_log2, + "native_log10": OpenCLStd_native_log10, + "native_powr": OpenCLStd_native_powr, + "native_recip": OpenCLStd_native_recip, + "native_rsqrt": OpenCLStd_native_rsqrt, + "native_sin": OpenCLStd_native_sin, + "native_sqrt": OpenCLStd_native_sqrt, + "native_tan": OpenCLStd_native_tan, + "s_abs": OpenCLStd_s_abs, + "s_abs_diff": OpenCLStd_s_abs_diff, + "s_add_sat": OpenCLStd_s_add_sat, + "u_add_sat": OpenCLStd_u_add_sat, + "s_hadd": OpenCLStd_s_hadd, + "u_hadd": OpenCLStd_u_hadd, + "s_rhadd": OpenCLStd_s_rhadd, + "u_rhadd": OpenCLStd_u_rhadd, + "s_clamp": OpenCLStd_s_clamp, + "u_clamp": OpenCLStd_u_clamp, + "clz": OpenCLStd_clz, + "ctz": OpenCLStd_ctz, + "s_mad_hi": OpenCLStd_s_mad_hi, + "u_mad_sat": OpenCLStd_u_mad_sat, + "s_mad_sat": OpenCLStd_s_mad_sat, + "s_max": OpenCLStd_s_max, + "u_max": OpenCLStd_u_max, + "s_min": OpenCLStd_s_min, + "u_min": OpenCLStd_u_min, + "s_mul_hi": OpenCLStd_s_mul_hi, + "rotate": OpenCLStd_rotate, + "s_sub_sat": OpenCLStd_s_sub_sat, + "u_sub_sat": OpenCLStd_u_sub_sat, + "u_upsample": OpenCLStd_u_upsample, + "s_upsample": OpenCLStd_s_upsample, + "popcount": OpenCLStd_popcount, + "s_mad24": OpenCLStd_s_mad24, + "u_mad24": OpenCLStd_u_mad24, + "s_mul24": OpenCLStd_s_mul24, + "u_mul24": OpenCLStd_u_mul24, + "u_abs": OpenCLStd_u_abs, + "u_abs_diff": OpenCLStd_u_abs_diff, + "u_mul_hi": OpenCLStd_u_mul_hi, + "u_mad_hi": OpenCLStd_u_mad_hi, + "fclamp": OpenCLStd_fclamp, + "degrees": OpenCLStd_degrees, + "fmax_common": OpenCLStd_fmax_common, + "fmin_common": OpenCLStd_fmin_common, + "mix": OpenCLStd_mix, + "radians": OpenCLStd_radians, + "step": OpenCLStd_step, + "smoothstep": OpenCLStd_smoothstep, + "sign": OpenCLStd_sign, + "cross": OpenCLStd_cross, + "distance": OpenCLStd_distance, + "length": OpenCLStd_length, + "normalize": OpenCLStd_normalize, + "fast_distance": OpenCLStd_fast_distance, + "fast_length": OpenCLStd_fast_length, + "fast_normalize": OpenCLStd_fast_normalize, + "bitselect": OpenCLStd_bitselect, + "select": OpenCLStd_select, + "vloadn": OpenCLStd_vloadn, + "vstoren": OpenCLStd_vstoren, + "vload_half": OpenCLStd_vload_half, + "vload_halfn": OpenCLStd_vload_halfn, + "vstore_half": OpenCLStd_vstore_half, + "vstore_half_r": OpenCLStd_vstore_half_r, + "vstore_halfn": OpenCLStd_vstore_halfn, + "vstore_halfn_r": OpenCLStd_vstore_halfn_r, + "vloada_halfn": OpenCLStd_vloada_halfn, + "vstorea_halfn": OpenCLStd_vstorea_halfn, + "vstorea_halfn_r": OpenCLStd_vstorea_halfn_r, + "shuffle": OpenCLStd_shuffle, + "shuffle2": OpenCLStd_shuffle2, + "printf": OpenCLStd_printf, + "prefetch": OpenCLStd_prefetch, + }, + "OpenCL.DebugInfo.100": { + "DebugInfoNone": OpenCLDebugInfo100_DebugInfoNone, + "DebugCompilationUnit": OpenCLDebugInfo100_DebugCompilationUnit, + "DebugTypeBasic": OpenCLDebugInfo100_DebugTypeBasic, + "DebugTypePointer": OpenCLDebugInfo100_DebugTypePointer, + "DebugTypeQualifier": OpenCLDebugInfo100_DebugTypeQualifier, + "DebugTypeArray": OpenCLDebugInfo100_DebugTypeArray, + "DebugTypeVector": OpenCLDebugInfo100_DebugTypeVector, + "DebugTypedef": OpenCLDebugInfo100_DebugTypedef, + "DebugTypeFunction": OpenCLDebugInfo100_DebugTypeFunction, + "DebugTypeEnum": OpenCLDebugInfo100_DebugTypeEnum, + "DebugTypeComposite": OpenCLDebugInfo100_DebugTypeComposite, + "DebugTypeMember": OpenCLDebugInfo100_DebugTypeMember, + "DebugTypeInheritance": OpenCLDebugInfo100_DebugTypeInheritance, + "DebugTypePtrToMember": OpenCLDebugInfo100_DebugTypePtrToMember, + "DebugTypeTemplate": OpenCLDebugInfo100_DebugTypeTemplate, + "DebugTypeTemplateParameter": OpenCLDebugInfo100_DebugTypeTemplateParameter, + "DebugTypeTemplateTemplateParameter": OpenCLDebugInfo100_DebugTypeTemplateTemplateParameter, + "DebugTypeTemplateParameterPack": OpenCLDebugInfo100_DebugTypeTemplateParameterPack, + "DebugGlobalVariable": OpenCLDebugInfo100_DebugGlobalVariable, + "DebugFunctionDeclaration": OpenCLDebugInfo100_DebugFunctionDeclaration, + "DebugFunction": OpenCLDebugInfo100_DebugFunction, + "DebugLexicalBlock": OpenCLDebugInfo100_DebugLexicalBlock, + "DebugLexicalBlockDiscriminator": OpenCLDebugInfo100_DebugLexicalBlockDiscriminator, + "DebugScope": OpenCLDebugInfo100_DebugScope, + "DebugNoScope": OpenCLDebugInfo100_DebugNoScope, + "DebugInlinedAt": OpenCLDebugInfo100_DebugInlinedAt, + "DebugLocalVariable": OpenCLDebugInfo100_DebugLocalVariable, + "DebugInlinedVariable": OpenCLDebugInfo100_DebugInlinedVariable, + "DebugDeclare": OpenCLDebugInfo100_DebugDeclare, + "DebugValue": OpenCLDebugInfo100_DebugValue, + "DebugOperation": OpenCLDebugInfo100_DebugOperation, + "DebugExpression": OpenCLDebugInfo100_DebugExpression, + "DebugMacroDef": OpenCLDebugInfo100_DebugMacroDef, + "DebugMacroUndef": OpenCLDebugInfo100_DebugMacroUndef, + "DebugImportedEntity": OpenCLDebugInfo100_DebugImportedEntity, + "DebugSource": OpenCLDebugInfo100_DebugSource, + }, + } + + OpNop = &Opcode { + Opname: "OpNop", + Class: "Miscellaneous", + Opcode: 0, + Operands: []Operand { + }, + } + OpUndef = &Opcode { + Opname: "OpUndef", + Class: "Miscellaneous", + Opcode: 1, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSourceContinued = &Opcode { + Opname: "OpSourceContinued", + Class: "Debug", + Opcode: 2, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralString, + Name: "'Continued Source'", + Quantifier: "", + }, + }, + } + OpSource = &Opcode { + Opname: "OpSource", + Class: "Debug", + Opcode: 3, + Operands: []Operand { + Operand { + Kind: OperandKindSourceLanguage, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Version'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'File'", + Quantifier: "?", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Source'", + Quantifier: "?", + }, + }, + } + OpSourceExtension = &Opcode { + Opname: "OpSourceExtension", + Class: "Debug", + Opcode: 4, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralString, + Name: "'Extension'", + Quantifier: "", + }, + }, + } + OpName = &Opcode { + Opname: "OpName", + Class: "Debug", + Opcode: 5, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + }, + } + OpMemberName = &Opcode { + Opname: "OpMemberName", + Class: "Debug", + Opcode: 6, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Member'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + }, + } + OpString = &Opcode { + Opname: "OpString", + Class: "Debug", + Opcode: 7, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'String'", + Quantifier: "", + }, + }, + } + OpLine = &Opcode { + Opname: "OpLine", + Class: "Debug", + Opcode: 8, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'File'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + }, + } + OpExtension = &Opcode { + Opname: "OpExtension", + Class: "Extension", + Opcode: 10, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + }, + } + OpExtInstImport = &Opcode { + Opname: "OpExtInstImport", + Class: "Extension", + Opcode: 11, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + }, + } + OpExtInst = &Opcode { + Opname: "OpExtInst", + Class: "Extension", + Opcode: 12, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Set'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralExtInstInteger, + Name: "'Instruction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1', + 'Operand 2', + ...", + Quantifier: "*", + }, + }, + } + OpMemoryModel = &Opcode { + Opname: "OpMemoryModel", + Class: "Mode-Setting", + Opcode: 14, + Operands: []Operand { + Operand { + Kind: OperandKindAddressingModel, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryModel, + Name: "", + Quantifier: "", + }, + }, + } + OpEntryPoint = &Opcode { + Opname: "OpEntryPoint", + Class: "Mode-Setting", + Opcode: 15, + Operands: []Operand { + Operand { + Kind: OperandKindExecutionModel, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Entry Point'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Interface'", + Quantifier: "*", + }, + }, + } + OpExecutionMode = &Opcode { + Opname: "OpExecutionMode", + Class: "Mode-Setting", + Opcode: 16, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Entry Point'", + Quantifier: "", + }, + Operand { + Kind: OperandKindExecutionMode, + Name: "'Mode'", + Quantifier: "", + }, + }, + } + OpCapability = &Opcode { + Opname: "OpCapability", + Class: "Mode-Setting", + Opcode: 17, + Operands: []Operand { + Operand { + Kind: OperandKindCapability, + Name: "'Capability'", + Quantifier: "", + }, + }, + } + OpTypeVoid = &Opcode { + Opname: "OpTypeVoid", + Class: "Type-Declaration", + Opcode: 19, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeBool = &Opcode { + Opname: "OpTypeBool", + Class: "Type-Declaration", + Opcode: 20, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeInt = &Opcode { + Opname: "OpTypeInt", + Class: "Type-Declaration", + Opcode: 21, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Width'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Signedness'", + Quantifier: "", + }, + }, + } + OpTypeFloat = &Opcode { + Opname: "OpTypeFloat", + Class: "Type-Declaration", + Opcode: 22, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Width'", + Quantifier: "", + }, + }, + } + OpTypeVector = &Opcode { + Opname: "OpTypeVector", + Class: "Type-Declaration", + Opcode: 23, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Component Count'", + Quantifier: "", + }, + }, + } + OpTypeMatrix = &Opcode { + Opname: "OpTypeMatrix", + Class: "Type-Declaration", + Opcode: 24, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Column Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column Count'", + Quantifier: "", + }, + }, + } + OpTypeImage = &Opcode { + Opname: "OpTypeImage", + Class: "Type-Declaration", + Opcode: 25, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDim, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Depth'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Arrayed'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'MS'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Sampled'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageFormat, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindAccessQualifier, + Name: "", + Quantifier: "?", + }, + }, + } + OpTypeSampler = &Opcode { + Opname: "OpTypeSampler", + Class: "Type-Declaration", + Opcode: 26, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeSampledImage = &Opcode { + Opname: "OpTypeSampledImage", + Class: "Type-Declaration", + Opcode: 27, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Type'", + Quantifier: "", + }, + }, + } + OpTypeArray = &Opcode { + Opname: "OpTypeArray", + Class: "Type-Declaration", + Opcode: 28, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Length'", + Quantifier: "", + }, + }, + } + OpTypeRuntimeArray = &Opcode { + Opname: "OpTypeRuntimeArray", + Class: "Type-Declaration", + Opcode: 29, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element Type'", + Quantifier: "", + }, + }, + } + OpTypeStruct = &Opcode { + Opname: "OpTypeStruct", + Class: "Type-Declaration", + Opcode: 30, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Member 0 type', + 'member 1 type', + ...", + Quantifier: "*", + }, + }, + } + OpTypeOpaque = &Opcode { + Opname: "OpTypeOpaque", + Class: "Type-Declaration", + Opcode: 31, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralString, + Name: "The name of the opaque type.", + Quantifier: "", + }, + }, + } + OpTypePointer = &Opcode { + Opname: "OpTypePointer", + Class: "Type-Declaration", + Opcode: 32, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + }, + } + OpTypeFunction = &Opcode { + Opname: "OpTypeFunction", + Class: "Type-Declaration", + Opcode: 33, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Return Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parameter 0 Type', + 'Parameter 1 Type', + ...", + Quantifier: "*", + }, + }, + } + OpTypeEvent = &Opcode { + Opname: "OpTypeEvent", + Class: "Type-Declaration", + Opcode: 34, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeDeviceEvent = &Opcode { + Opname: "OpTypeDeviceEvent", + Class: "Type-Declaration", + Opcode: 35, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeReserveId = &Opcode { + Opname: "OpTypeReserveId", + Class: "Type-Declaration", + Opcode: 36, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeQueue = &Opcode { + Opname: "OpTypeQueue", + Class: "Type-Declaration", + Opcode: 37, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypePipe = &Opcode { + Opname: "OpTypePipe", + Class: "Type-Declaration", + Opcode: 38, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindAccessQualifier, + Name: "'Qualifier'", + Quantifier: "", + }, + }, + } + OpTypeForwardPointer = &Opcode { + Opname: "OpTypeForwardPointer", + Class: "Type-Declaration", + Opcode: 39, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + }, + } + OpConstantTrue = &Opcode { + Opname: "OpConstantTrue", + Class: "Constant-Creation", + Opcode: 41, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpConstantFalse = &Opcode { + Opname: "OpConstantFalse", + Class: "Constant-Creation", + Opcode: 42, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpConstant = &Opcode { + Opname: "OpConstant", + Class: "Constant-Creation", + Opcode: 43, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralContextDependentNumber, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpConstantComposite = &Opcode { + Opname: "OpConstantComposite", + Class: "Constant-Creation", + Opcode: 44, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpConstantSampler = &Opcode { + Opname: "OpConstantSampler", + Class: "Constant-Creation", + Opcode: 45, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindSamplerAddressingMode, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindSamplerFilterMode, + Name: "", + Quantifier: "", + }, + }, + } + OpConstantNull = &Opcode { + Opname: "OpConstantNull", + Class: "Constant-Creation", + Opcode: 46, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSpecConstantTrue = &Opcode { + Opname: "OpSpecConstantTrue", + Class: "Constant-Creation", + Opcode: 48, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSpecConstantFalse = &Opcode { + Opname: "OpSpecConstantFalse", + Class: "Constant-Creation", + Opcode: 49, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSpecConstant = &Opcode { + Opname: "OpSpecConstant", + Class: "Constant-Creation", + Opcode: 50, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralContextDependentNumber, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpSpecConstantComposite = &Opcode { + Opname: "OpSpecConstantComposite", + Class: "Constant-Creation", + Opcode: 51, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpSpecConstantOp = &Opcode { + Opname: "OpSpecConstantOp", + Class: "Constant-Creation", + Opcode: 52, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralSpecConstantOpInteger, + Name: "'Opcode'", + Quantifier: "", + }, + }, + } + OpFunction = &Opcode { + Opname: "OpFunction", + Class: "Function", + Opcode: 54, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindFunctionControl, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function Type'", + Quantifier: "", + }, + }, + } + OpFunctionParameter = &Opcode { + Opname: "OpFunctionParameter", + Class: "Function", + Opcode: 55, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpFunctionEnd = &Opcode { + Opname: "OpFunctionEnd", + Class: "Function", + Opcode: 56, + Operands: []Operand { + }, + } + OpFunctionCall = &Opcode { + Opname: "OpFunctionCall", + Class: "Function", + Opcode: 57, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Argument 0', + 'Argument 1', + ...", + Quantifier: "*", + }, + }, + } + OpVariable = &Opcode { + Opname: "OpVariable", + Class: "Memory", + Opcode: 59, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Initializer'", + Quantifier: "?", + }, + }, + } + OpImageTexelPointer = &Opcode { + Opname: "OpImageTexelPointer", + Class: "Memory", + Opcode: 60, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sample'", + Quantifier: "", + }, + }, + } + OpLoad = &Opcode { + Opname: "OpLoad", + Class: "Memory", + Opcode: 61, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpStore = &Opcode { + Opname: "OpStore", + Class: "Memory", + Opcode: 62, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpCopyMemory = &Opcode { + Opname: "OpCopyMemory", + Class: "Memory", + Opcode: 63, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpCopyMemorySized = &Opcode { + Opname: "OpCopyMemorySized", + Class: "Memory", + Opcode: 64, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpAccessChain = &Opcode { + Opname: "OpAccessChain", + Class: "Memory", + Opcode: 65, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpInBoundsAccessChain = &Opcode { + Opname: "OpInBoundsAccessChain", + Class: "Memory", + Opcode: 66, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpPtrAccessChain = &Opcode { + Opname: "OpPtrAccessChain", + Class: "Memory", + Opcode: 67, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpArrayLength = &Opcode { + Opname: "OpArrayLength", + Class: "Memory", + Opcode: 68, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Structure'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Array member'", + Quantifier: "", + }, + }, + } + OpGenericPtrMemSemantics = &Opcode { + Opname: "OpGenericPtrMemSemantics", + Class: "Memory", + Opcode: 69, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpInBoundsPtrAccessChain = &Opcode { + Opname: "OpInBoundsPtrAccessChain", + Class: "Memory", + Opcode: 70, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Element'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpDecorate = &Opcode { + Opname: "OpDecorate", + Class: "Annotation", + Opcode: 71, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpMemberDecorate = &Opcode { + Opname: "OpMemberDecorate", + Class: "Annotation", + Opcode: 72, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Structure Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Member'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpDecorationGroup = &Opcode { + Opname: "OpDecorationGroup", + Class: "Annotation", + Opcode: 73, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpGroupDecorate = &Opcode { + Opname: "OpGroupDecorate", + Class: "Annotation", + Opcode: 74, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Decoration Group'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Targets'", + Quantifier: "*", + }, + }, + } + OpGroupMemberDecorate = &Opcode { + Opname: "OpGroupMemberDecorate", + Class: "Annotation", + Opcode: 75, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Decoration Group'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairIdRefLiteralInteger, + Name: "'Targets'", + Quantifier: "*", + }, + }, + } + OpVectorExtractDynamic = &Opcode { + Opname: "OpVectorExtractDynamic", + Class: "Composite", + Opcode: 77, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpVectorInsertDynamic = &Opcode { + Opname: "OpVectorInsertDynamic", + Class: "Composite", + Opcode: 78, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpVectorShuffle = &Opcode { + Opname: "OpVectorShuffle", + Class: "Composite", + Opcode: 79, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Components'", + Quantifier: "*", + }, + }, + } + OpCompositeConstruct = &Opcode { + Opname: "OpCompositeConstruct", + Class: "Composite", + Opcode: 80, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Constituents'", + Quantifier: "*", + }, + }, + } + OpCompositeExtract = &Opcode { + Opname: "OpCompositeExtract", + Class: "Composite", + Opcode: 81, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Composite'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpCompositeInsert = &Opcode { + Opname: "OpCompositeInsert", + Class: "Composite", + Opcode: 82, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Composite'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpCopyObject = &Opcode { + Opname: "OpCopyObject", + Class: "Composite", + Opcode: 83, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpTranspose = &Opcode { + Opname: "OpTranspose", + Class: "Composite", + Opcode: 84, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + }, + } + OpSampledImage = &Opcode { + Opname: "OpSampledImage", + Class: "Image", + Opcode: 86, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampler'", + Quantifier: "", + }, + }, + } + OpImageSampleImplicitLod = &Opcode { + Opname: "OpImageSampleImplicitLod", + Class: "Image", + Opcode: 87, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSampleExplicitLod = &Opcode { + Opname: "OpImageSampleExplicitLod", + Class: "Image", + Opcode: 88, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSampleDrefImplicitLod = &Opcode { + Opname: "OpImageSampleDrefImplicitLod", + Class: "Image", + Opcode: 89, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSampleDrefExplicitLod = &Opcode { + Opname: "OpImageSampleDrefExplicitLod", + Class: "Image", + Opcode: 90, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSampleProjImplicitLod = &Opcode { + Opname: "OpImageSampleProjImplicitLod", + Class: "Image", + Opcode: 91, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSampleProjExplicitLod = &Opcode { + Opname: "OpImageSampleProjExplicitLod", + Class: "Image", + Opcode: 92, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSampleProjDrefImplicitLod = &Opcode { + Opname: "OpImageSampleProjDrefImplicitLod", + Class: "Image", + Opcode: 93, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSampleProjDrefExplicitLod = &Opcode { + Opname: "OpImageSampleProjDrefExplicitLod", + Class: "Image", + Opcode: 94, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageFetch = &Opcode { + Opname: "OpImageFetch", + Class: "Image", + Opcode: 95, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageGather = &Opcode { + Opname: "OpImageGather", + Class: "Image", + Opcode: 96, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageDrefGather = &Opcode { + Opname: "OpImageDrefGather", + Class: "Image", + Opcode: 97, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageRead = &Opcode { + Opname: "OpImageRead", + Class: "Image", + Opcode: 98, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageWrite = &Opcode { + Opname: "OpImageWrite", + Class: "Image", + Opcode: 99, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Texel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImage = &Opcode { + Opname: "OpImage", + Class: "Image", + Opcode: 100, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + }, + } + OpImageQueryFormat = &Opcode { + Opname: "OpImageQueryFormat", + Class: "Image", + Opcode: 101, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpImageQueryOrder = &Opcode { + Opname: "OpImageQueryOrder", + Class: "Image", + Opcode: 102, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpImageQuerySizeLod = &Opcode { + Opname: "OpImageQuerySizeLod", + Class: "Image", + Opcode: 103, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Level of Detail'", + Quantifier: "", + }, + }, + } + OpImageQuerySize = &Opcode { + Opname: "OpImageQuerySize", + Class: "Image", + Opcode: 104, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpImageQueryLod = &Opcode { + Opname: "OpImageQueryLod", + Class: "Image", + Opcode: 105, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + }, + } + OpImageQueryLevels = &Opcode { + Opname: "OpImageQueryLevels", + Class: "Image", + Opcode: 106, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpImageQuerySamples = &Opcode { + Opname: "OpImageQuerySamples", + Class: "Image", + Opcode: 107, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + }, + } + OpConvertFToU = &Opcode { + Opname: "OpConvertFToU", + Class: "Conversion", + Opcode: 109, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Float Value'", + Quantifier: "", + }, + }, + } + OpConvertFToS = &Opcode { + Opname: "OpConvertFToS", + Class: "Conversion", + Opcode: 110, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Float Value'", + Quantifier: "", + }, + }, + } + OpConvertSToF = &Opcode { + Opname: "OpConvertSToF", + Class: "Conversion", + Opcode: 111, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Signed Value'", + Quantifier: "", + }, + }, + } + OpConvertUToF = &Opcode { + Opname: "OpConvertUToF", + Class: "Conversion", + Opcode: 112, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Unsigned Value'", + Quantifier: "", + }, + }, + } + OpUConvert = &Opcode { + Opname: "OpUConvert", + Class: "Conversion", + Opcode: 113, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Unsigned Value'", + Quantifier: "", + }, + }, + } + OpSConvert = &Opcode { + Opname: "OpSConvert", + Class: "Conversion", + Opcode: 114, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Signed Value'", + Quantifier: "", + }, + }, + } + OpFConvert = &Opcode { + Opname: "OpFConvert", + Class: "Conversion", + Opcode: 115, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Float Value'", + Quantifier: "", + }, + }, + } + OpQuantizeToF16 = &Opcode { + Opname: "OpQuantizeToF16", + Class: "Conversion", + Opcode: 116, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpConvertPtrToU = &Opcode { + Opname: "OpConvertPtrToU", + Class: "Conversion", + Opcode: 117, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpSatConvertSToU = &Opcode { + Opname: "OpSatConvertSToU", + Class: "Conversion", + Opcode: 118, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Signed Value'", + Quantifier: "", + }, + }, + } + OpSatConvertUToS = &Opcode { + Opname: "OpSatConvertUToS", + Class: "Conversion", + Opcode: 119, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Unsigned Value'", + Quantifier: "", + }, + }, + } + OpConvertUToPtr = &Opcode { + Opname: "OpConvertUToPtr", + Class: "Conversion", + Opcode: 120, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Integer Value'", + Quantifier: "", + }, + }, + } + OpPtrCastToGeneric = &Opcode { + Opname: "OpPtrCastToGeneric", + Class: "Conversion", + Opcode: 121, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpGenericCastToPtr = &Opcode { + Opname: "OpGenericCastToPtr", + Class: "Conversion", + Opcode: 122, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpGenericCastToPtrExplicit = &Opcode { + Opname: "OpGenericCastToPtrExplicit", + Class: "Conversion", + Opcode: 123, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "'Storage'", + Quantifier: "", + }, + }, + } + OpBitcast = &Opcode { + Opname: "OpBitcast", + Class: "Conversion", + Opcode: 124, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpSNegate = &Opcode { + Opname: "OpSNegate", + Class: "Arithmetic", + Opcode: 126, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpFNegate = &Opcode { + Opname: "OpFNegate", + Class: "Arithmetic", + Opcode: 127, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpIAdd = &Opcode { + Opname: "OpIAdd", + Class: "Arithmetic", + Opcode: 128, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFAdd = &Opcode { + Opname: "OpFAdd", + Class: "Arithmetic", + Opcode: 129, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpISub = &Opcode { + Opname: "OpISub", + Class: "Arithmetic", + Opcode: 130, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFSub = &Opcode { + Opname: "OpFSub", + Class: "Arithmetic", + Opcode: 131, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIMul = &Opcode { + Opname: "OpIMul", + Class: "Arithmetic", + Opcode: 132, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFMul = &Opcode { + Opname: "OpFMul", + Class: "Arithmetic", + Opcode: 133, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUDiv = &Opcode { + Opname: "OpUDiv", + Class: "Arithmetic", + Opcode: 134, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSDiv = &Opcode { + Opname: "OpSDiv", + Class: "Arithmetic", + Opcode: 135, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFDiv = &Opcode { + Opname: "OpFDiv", + Class: "Arithmetic", + Opcode: 136, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUMod = &Opcode { + Opname: "OpUMod", + Class: "Arithmetic", + Opcode: 137, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSRem = &Opcode { + Opname: "OpSRem", + Class: "Arithmetic", + Opcode: 138, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSMod = &Opcode { + Opname: "OpSMod", + Class: "Arithmetic", + Opcode: 139, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFRem = &Opcode { + Opname: "OpFRem", + Class: "Arithmetic", + Opcode: 140, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFMod = &Opcode { + Opname: "OpFMod", + Class: "Arithmetic", + Opcode: 141, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpVectorTimesScalar = &Opcode { + Opname: "OpVectorTimesScalar", + Class: "Arithmetic", + Opcode: 142, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Scalar'", + Quantifier: "", + }, + }, + } + OpMatrixTimesScalar = &Opcode { + Opname: "OpMatrixTimesScalar", + Class: "Arithmetic", + Opcode: 143, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Scalar'", + Quantifier: "", + }, + }, + } + OpVectorTimesMatrix = &Opcode { + Opname: "OpVectorTimesMatrix", + Class: "Arithmetic", + Opcode: 144, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + }, + } + OpMatrixTimesVector = &Opcode { + Opname: "OpMatrixTimesVector", + Class: "Arithmetic", + Opcode: 145, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Matrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + }, + } + OpMatrixTimesMatrix = &Opcode { + Opname: "OpMatrixTimesMatrix", + Class: "Arithmetic", + Opcode: 146, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'LeftMatrix'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RightMatrix'", + Quantifier: "", + }, + }, + } + OpOuterProduct = &Opcode { + Opname: "OpOuterProduct", + Class: "Arithmetic", + Opcode: 147, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + }, + } + OpDot = &Opcode { + Opname: "OpDot", + Class: "Arithmetic", + Opcode: 148, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector 2'", + Quantifier: "", + }, + }, + } + OpIAddCarry = &Opcode { + Opname: "OpIAddCarry", + Class: "Arithmetic", + Opcode: 149, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpISubBorrow = &Opcode { + Opname: "OpISubBorrow", + Class: "Arithmetic", + Opcode: 150, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUMulExtended = &Opcode { + Opname: "OpUMulExtended", + Class: "Arithmetic", + Opcode: 151, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSMulExtended = &Opcode { + Opname: "OpSMulExtended", + Class: "Arithmetic", + Opcode: 152, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpAny = &Opcode { + Opname: "OpAny", + Class: "Relational_and_Logical", + Opcode: 154, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + }, + } + OpAll = &Opcode { + Opname: "OpAll", + Class: "Relational_and_Logical", + Opcode: 155, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Vector'", + Quantifier: "", + }, + }, + } + OpIsNan = &Opcode { + Opname: "OpIsNan", + Class: "Relational_and_Logical", + Opcode: 156, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpIsInf = &Opcode { + Opname: "OpIsInf", + Class: "Relational_and_Logical", + Opcode: 157, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpIsFinite = &Opcode { + Opname: "OpIsFinite", + Class: "Relational_and_Logical", + Opcode: 158, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpIsNormal = &Opcode { + Opname: "OpIsNormal", + Class: "Relational_and_Logical", + Opcode: 159, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpSignBitSet = &Opcode { + Opname: "OpSignBitSet", + Class: "Relational_and_Logical", + Opcode: 160, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpLessOrGreater = &Opcode { + Opname: "OpLessOrGreater", + Class: "Relational_and_Logical", + Opcode: 161, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpOrdered = &Opcode { + Opname: "OpOrdered", + Class: "Relational_and_Logical", + Opcode: 162, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpUnordered = &Opcode { + Opname: "OpUnordered", + Class: "Relational_and_Logical", + Opcode: 163, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpLogicalEqual = &Opcode { + Opname: "OpLogicalEqual", + Class: "Relational_and_Logical", + Opcode: 164, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpLogicalNotEqual = &Opcode { + Opname: "OpLogicalNotEqual", + Class: "Relational_and_Logical", + Opcode: 165, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpLogicalOr = &Opcode { + Opname: "OpLogicalOr", + Class: "Relational_and_Logical", + Opcode: 166, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpLogicalAnd = &Opcode { + Opname: "OpLogicalAnd", + Class: "Relational_and_Logical", + Opcode: 167, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpLogicalNot = &Opcode { + Opname: "OpLogicalNot", + Class: "Relational_and_Logical", + Opcode: 168, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpSelect = &Opcode { + Opname: "OpSelect", + Class: "Relational_and_Logical", + Opcode: 169, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Condition'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object 2'", + Quantifier: "", + }, + }, + } + OpIEqual = &Opcode { + Opname: "OpIEqual", + Class: "Relational_and_Logical", + Opcode: 170, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpINotEqual = &Opcode { + Opname: "OpINotEqual", + Class: "Relational_and_Logical", + Opcode: 171, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUGreaterThan = &Opcode { + Opname: "OpUGreaterThan", + Class: "Relational_and_Logical", + Opcode: 172, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSGreaterThan = &Opcode { + Opname: "OpSGreaterThan", + Class: "Relational_and_Logical", + Opcode: 173, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUGreaterThanEqual = &Opcode { + Opname: "OpUGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 174, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSGreaterThanEqual = &Opcode { + Opname: "OpSGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 175, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpULessThan = &Opcode { + Opname: "OpULessThan", + Class: "Relational_and_Logical", + Opcode: 176, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSLessThan = &Opcode { + Opname: "OpSLessThan", + Class: "Relational_and_Logical", + Opcode: 177, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpULessThanEqual = &Opcode { + Opname: "OpULessThanEqual", + Class: "Relational_and_Logical", + Opcode: 178, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSLessThanEqual = &Opcode { + Opname: "OpSLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 179, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdEqual = &Opcode { + Opname: "OpFOrdEqual", + Class: "Relational_and_Logical", + Opcode: 180, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordEqual = &Opcode { + Opname: "OpFUnordEqual", + Class: "Relational_and_Logical", + Opcode: 181, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdNotEqual = &Opcode { + Opname: "OpFOrdNotEqual", + Class: "Relational_and_Logical", + Opcode: 182, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordNotEqual = &Opcode { + Opname: "OpFUnordNotEqual", + Class: "Relational_and_Logical", + Opcode: 183, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdLessThan = &Opcode { + Opname: "OpFOrdLessThan", + Class: "Relational_and_Logical", + Opcode: 184, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordLessThan = &Opcode { + Opname: "OpFUnordLessThan", + Class: "Relational_and_Logical", + Opcode: 185, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdGreaterThan = &Opcode { + Opname: "OpFOrdGreaterThan", + Class: "Relational_and_Logical", + Opcode: 186, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordGreaterThan = &Opcode { + Opname: "OpFUnordGreaterThan", + Class: "Relational_and_Logical", + Opcode: 187, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdLessThanEqual = &Opcode { + Opname: "OpFOrdLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 188, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordLessThanEqual = &Opcode { + Opname: "OpFUnordLessThanEqual", + Class: "Relational_and_Logical", + Opcode: 189, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFOrdGreaterThanEqual = &Opcode { + Opname: "OpFOrdGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 190, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpFUnordGreaterThanEqual = &Opcode { + Opname: "OpFUnordGreaterThanEqual", + Class: "Relational_and_Logical", + Opcode: 191, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpShiftRightLogical = &Opcode { + Opname: "OpShiftRightLogical", + Class: "Bit", + Opcode: 194, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Shift'", + Quantifier: "", + }, + }, + } + OpShiftRightArithmetic = &Opcode { + Opname: "OpShiftRightArithmetic", + Class: "Bit", + Opcode: 195, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Shift'", + Quantifier: "", + }, + }, + } + OpShiftLeftLogical = &Opcode { + Opname: "OpShiftLeftLogical", + Class: "Bit", + Opcode: 196, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Shift'", + Quantifier: "", + }, + }, + } + OpBitwiseOr = &Opcode { + Opname: "OpBitwiseOr", + Class: "Bit", + Opcode: 197, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpBitwiseXor = &Opcode { + Opname: "OpBitwiseXor", + Class: "Bit", + Opcode: 198, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpBitwiseAnd = &Opcode { + Opname: "OpBitwiseAnd", + Class: "Bit", + Opcode: 199, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpNot = &Opcode { + Opname: "OpNot", + Class: "Bit", + Opcode: 200, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpBitFieldInsert = &Opcode { + Opname: "OpBitFieldInsert", + Class: "Bit", + Opcode: 201, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Insert'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Count'", + Quantifier: "", + }, + }, + } + OpBitFieldSExtract = &Opcode { + Opname: "OpBitFieldSExtract", + Class: "Bit", + Opcode: 202, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Count'", + Quantifier: "", + }, + }, + } + OpBitFieldUExtract = &Opcode { + Opname: "OpBitFieldUExtract", + Class: "Bit", + Opcode: 203, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Count'", + Quantifier: "", + }, + }, + } + OpBitReverse = &Opcode { + Opname: "OpBitReverse", + Class: "Bit", + Opcode: 204, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + }, + } + OpBitCount = &Opcode { + Opname: "OpBitCount", + Class: "Bit", + Opcode: 205, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base'", + Quantifier: "", + }, + }, + } + OpDPdx = &Opcode { + Opname: "OpDPdx", + Class: "Derivative", + Opcode: 207, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdy = &Opcode { + Opname: "OpDPdy", + Class: "Derivative", + Opcode: 208, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpFwidth = &Opcode { + Opname: "OpFwidth", + Class: "Derivative", + Opcode: 209, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdxFine = &Opcode { + Opname: "OpDPdxFine", + Class: "Derivative", + Opcode: 210, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdyFine = &Opcode { + Opname: "OpDPdyFine", + Class: "Derivative", + Opcode: 211, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpFwidthFine = &Opcode { + Opname: "OpFwidthFine", + Class: "Derivative", + Opcode: 212, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdxCoarse = &Opcode { + Opname: "OpDPdxCoarse", + Class: "Derivative", + Opcode: 213, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpDPdyCoarse = &Opcode { + Opname: "OpDPdyCoarse", + Class: "Derivative", + Opcode: 214, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpFwidthCoarse = &Opcode { + Opname: "OpFwidthCoarse", + Class: "Derivative", + Opcode: 215, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'P'", + Quantifier: "", + }, + }, + } + OpEmitVertex = &Opcode { + Opname: "OpEmitVertex", + Class: "Primitive", + Opcode: 218, + Operands: []Operand { + }, + } + OpEndPrimitive = &Opcode { + Opname: "OpEndPrimitive", + Class: "Primitive", + Opcode: 219, + Operands: []Operand { + }, + } + OpEmitStreamVertex = &Opcode { + Opname: "OpEmitStreamVertex", + Class: "Primitive", + Opcode: 220, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Stream'", + Quantifier: "", + }, + }, + } + OpEndStreamPrimitive = &Opcode { + Opname: "OpEndStreamPrimitive", + Class: "Primitive", + Opcode: 221, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Stream'", + Quantifier: "", + }, + }, + } + OpControlBarrier = &Opcode { + Opname: "OpControlBarrier", + Class: "Barrier", + Opcode: 224, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpMemoryBarrier = &Opcode { + Opname: "OpMemoryBarrier", + Class: "Barrier", + Opcode: 225, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicLoad = &Opcode { + Opname: "OpAtomicLoad", + Class: "Atomic", + Opcode: 227, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicStore = &Opcode { + Opname: "OpAtomicStore", + Class: "Atomic", + Opcode: 228, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicExchange = &Opcode { + Opname: "OpAtomicExchange", + Class: "Atomic", + Opcode: 229, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicCompareExchange = &Opcode { + Opname: "OpAtomicCompareExchange", + Class: "Atomic", + Opcode: 230, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Equal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Unequal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Comparator'", + Quantifier: "", + }, + }, + } + OpAtomicCompareExchangeWeak = &Opcode { + Opname: "OpAtomicCompareExchangeWeak", + Class: "Atomic", + Opcode: 231, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Equal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Unequal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Comparator'", + Quantifier: "", + }, + }, + } + OpAtomicIIncrement = &Opcode { + Opname: "OpAtomicIIncrement", + Class: "Atomic", + Opcode: 232, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicIDecrement = &Opcode { + Opname: "OpAtomicIDecrement", + Class: "Atomic", + Opcode: 233, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicIAdd = &Opcode { + Opname: "OpAtomicIAdd", + Class: "Atomic", + Opcode: 234, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicISub = &Opcode { + Opname: "OpAtomicISub", + Class: "Atomic", + Opcode: 235, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicSMin = &Opcode { + Opname: "OpAtomicSMin", + Class: "Atomic", + Opcode: 236, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicUMin = &Opcode { + Opname: "OpAtomicUMin", + Class: "Atomic", + Opcode: 237, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicSMax = &Opcode { + Opname: "OpAtomicSMax", + Class: "Atomic", + Opcode: 238, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicUMax = &Opcode { + Opname: "OpAtomicUMax", + Class: "Atomic", + Opcode: 239, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicAnd = &Opcode { + Opname: "OpAtomicAnd", + Class: "Atomic", + Opcode: 240, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicOr = &Opcode { + Opname: "OpAtomicOr", + Class: "Atomic", + Opcode: 241, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpAtomicXor = &Opcode { + Opname: "OpAtomicXor", + Class: "Atomic", + Opcode: 242, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpPhi = &Opcode { + Opname: "OpPhi", + Class: "Control-Flow", + Opcode: 245, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairIdRefIdRef, + Name: "'Variable, Parent, ...'", + Quantifier: "*", + }, + }, + } + OpLoopMerge = &Opcode { + Opname: "OpLoopMerge", + Class: "Control-Flow", + Opcode: 246, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Merge Block'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Continue Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLoopControl, + Name: "", + Quantifier: "", + }, + }, + } + OpSelectionMerge = &Opcode { + Opname: "OpSelectionMerge", + Class: "Control-Flow", + Opcode: 247, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Merge Block'", + Quantifier: "", + }, + Operand { + Kind: OperandKindSelectionControl, + Name: "", + Quantifier: "", + }, + }, + } + OpLabel = &Opcode { + Opname: "OpLabel", + Class: "Control-Flow", + Opcode: 248, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpBranch = &Opcode { + Opname: "OpBranch", + Class: "Control-Flow", + Opcode: 249, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target Label'", + Quantifier: "", + }, + }, + } + OpBranchConditional = &Opcode { + Opname: "OpBranchConditional", + Class: "Control-Flow", + Opcode: 250, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Condition'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'True Label'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'False Label'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Branch weights'", + Quantifier: "*", + }, + }, + } + OpSwitch = &Opcode { + Opname: "OpSwitch", + Class: "Control-Flow", + Opcode: 251, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Selector'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Default'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairLiteralIntegerIdRef, + Name: "'Target'", + Quantifier: "*", + }, + }, + } + OpKill = &Opcode { + Opname: "OpKill", + Class: "Control-Flow", + Opcode: 252, + Operands: []Operand { + }, + } + OpReturn = &Opcode { + Opname: "OpReturn", + Class: "Control-Flow", + Opcode: 253, + Operands: []Operand { + }, + } + OpReturnValue = &Opcode { + Opname: "OpReturnValue", + Class: "Control-Flow", + Opcode: 254, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpUnreachable = &Opcode { + Opname: "OpUnreachable", + Class: "Control-Flow", + Opcode: 255, + Operands: []Operand { + }, + } + OpLifetimeStart = &Opcode { + Opname: "OpLifetimeStart", + Class: "Control-Flow", + Opcode: 256, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Size'", + Quantifier: "", + }, + }, + } + OpLifetimeStop = &Opcode { + Opname: "OpLifetimeStop", + Class: "Control-Flow", + Opcode: 257, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Size'", + Quantifier: "", + }, + }, + } + OpGroupAsyncCopy = &Opcode { + Opname: "OpGroupAsyncCopy", + Class: "Group", + Opcode: 259, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Destination'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Elements'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + }, + } + OpGroupWaitEvents = &Opcode { + Opname: "OpGroupWaitEvents", + Class: "Group", + Opcode: 260, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Events List'", + Quantifier: "", + }, + }, + } + OpGroupAll = &Opcode { + Opname: "OpGroupAll", + Class: "Group", + Opcode: 261, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupAny = &Opcode { + Opname: "OpGroupAny", + Class: "Group", + Opcode: 262, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupBroadcast = &Opcode { + Opname: "OpGroupBroadcast", + Class: "Group", + Opcode: 263, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'LocalId'", + Quantifier: "", + }, + }, + } + OpGroupIAdd = &Opcode { + Opname: "OpGroupIAdd", + Class: "Group", + Opcode: 264, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFAdd = &Opcode { + Opname: "OpGroupFAdd", + Class: "Group", + Opcode: 265, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMin = &Opcode { + Opname: "OpGroupFMin", + Class: "Group", + Opcode: 266, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupUMin = &Opcode { + Opname: "OpGroupUMin", + Class: "Group", + Opcode: 267, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupSMin = &Opcode { + Opname: "OpGroupSMin", + Class: "Group", + Opcode: 268, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMax = &Opcode { + Opname: "OpGroupFMax", + Class: "Group", + Opcode: 269, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupUMax = &Opcode { + Opname: "OpGroupUMax", + Class: "Group", + Opcode: 270, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupSMax = &Opcode { + Opname: "OpGroupSMax", + Class: "Group", + Opcode: 271, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpReadPipe = &Opcode { + Opname: "OpReadPipe", + Class: "Pipe", + Opcode: 274, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpWritePipe = &Opcode { + Opname: "OpWritePipe", + Class: "Pipe", + Opcode: 275, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpReservedReadPipe = &Opcode { + Opname: "OpReservedReadPipe", + Class: "Pipe", + Opcode: 276, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpReservedWritePipe = &Opcode { + Opname: "OpReservedWritePipe", + Class: "Pipe", + Opcode: 277, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpReserveReadPipePackets = &Opcode { + Opname: "OpReserveReadPipePackets", + Class: "Pipe", + Opcode: 278, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Packets'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpReserveWritePipePackets = &Opcode { + Opname: "OpReserveWritePipePackets", + Class: "Pipe", + Opcode: 279, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Packets'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpCommitReadPipe = &Opcode { + Opname: "OpCommitReadPipe", + Class: "Pipe", + Opcode: 280, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpCommitWritePipe = &Opcode { + Opname: "OpCommitWritePipe", + Class: "Pipe", + Opcode: 281, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpIsValidReserveId = &Opcode { + Opname: "OpIsValidReserveId", + Class: "Pipe", + Opcode: 282, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + }, + } + OpGetNumPipePackets = &Opcode { + Opname: "OpGetNumPipePackets", + Class: "Pipe", + Opcode: 283, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGetMaxPipePackets = &Opcode { + Opname: "OpGetMaxPipePackets", + Class: "Pipe", + Opcode: 284, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGroupReserveReadPipePackets = &Opcode { + Opname: "OpGroupReserveReadPipePackets", + Class: "Pipe", + Opcode: 285, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Packets'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGroupReserveWritePipePackets = &Opcode { + Opname: "OpGroupReserveWritePipePackets", + Class: "Pipe", + Opcode: 286, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Packets'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGroupCommitReadPipe = &Opcode { + Opname: "OpGroupCommitReadPipe", + Class: "Pipe", + Opcode: 287, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpGroupCommitWritePipe = &Opcode { + Opname: "OpGroupCommitWritePipe", + Class: "Pipe", + Opcode: 288, + Operands: []Operand { + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reserve Id'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packet Alignment'", + Quantifier: "", + }, + }, + } + OpEnqueueMarker = &Opcode { + Opname: "OpEnqueueMarker", + Class: "Device-Side_Enqueue", + Opcode: 291, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Queue'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Wait Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ret Event'", + Quantifier: "", + }, + }, + } + OpEnqueueKernel = &Opcode { + Opname: "OpEnqueueKernel", + Class: "Device-Side_Enqueue", + Opcode: 292, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Queue'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ND Range'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Num Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Wait Events'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ret Event'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Local Size'", + Quantifier: "*", + }, + }, + } + OpGetKernelNDrangeSubGroupCount = &Opcode { + Opname: "OpGetKernelNDrangeSubGroupCount", + Class: "Device-Side_Enqueue", + Opcode: 293, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ND Range'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpGetKernelNDrangeMaxSubGroupSize = &Opcode { + Opname: "OpGetKernelNDrangeMaxSubGroupSize", + Class: "Device-Side_Enqueue", + Opcode: 294, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ND Range'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpGetKernelWorkGroupSize = &Opcode { + Opname: "OpGetKernelWorkGroupSize", + Class: "Device-Side_Enqueue", + Opcode: 295, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpGetKernelPreferredWorkGroupSizeMultiple = &Opcode { + Opname: "OpGetKernelPreferredWorkGroupSizeMultiple", + Class: "Device-Side_Enqueue", + Opcode: 296, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpRetainEvent = &Opcode { + Opname: "OpRetainEvent", + Class: "Device-Side_Enqueue", + Opcode: 297, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + }, + } + OpReleaseEvent = &Opcode { + Opname: "OpReleaseEvent", + Class: "Device-Side_Enqueue", + Opcode: 298, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + }, + } + OpCreateUserEvent = &Opcode { + Opname: "OpCreateUserEvent", + Class: "Device-Side_Enqueue", + Opcode: 299, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpIsValidEvent = &Opcode { + Opname: "OpIsValidEvent", + Class: "Device-Side_Enqueue", + Opcode: 300, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + }, + } + OpSetUserEventStatus = &Opcode { + Opname: "OpSetUserEventStatus", + Class: "Device-Side_Enqueue", + Opcode: 301, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Status'", + Quantifier: "", + }, + }, + } + OpCaptureEventProfilingInfo = &Opcode { + Opname: "OpCaptureEventProfilingInfo", + Class: "Device-Side_Enqueue", + Opcode: 302, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Event'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Profiling Info'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGetDefaultQueue = &Opcode { + Opname: "OpGetDefaultQueue", + Class: "Device-Side_Enqueue", + Opcode: 303, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpBuildNDRange = &Opcode { + Opname: "OpBuildNDRange", + Class: "Device-Side_Enqueue", + Opcode: 304, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GlobalWorkSize'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'LocalWorkSize'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'GlobalWorkOffset'", + Quantifier: "", + }, + }, + } + OpImageSparseSampleImplicitLod = &Opcode { + Opname: "OpImageSparseSampleImplicitLod", + Class: "Image", + Opcode: 305, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseSampleExplicitLod = &Opcode { + Opname: "OpImageSparseSampleExplicitLod", + Class: "Image", + Opcode: 306, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSparseSampleDrefImplicitLod = &Opcode { + Opname: "OpImageSparseSampleDrefImplicitLod", + Class: "Image", + Opcode: 307, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseSampleDrefExplicitLod = &Opcode { + Opname: "OpImageSparseSampleDrefExplicitLod", + Class: "Image", + Opcode: 308, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSparseSampleProjImplicitLod = &Opcode { + Opname: "OpImageSparseSampleProjImplicitLod", + Class: "Image", + Opcode: 309, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseSampleProjExplicitLod = &Opcode { + Opname: "OpImageSparseSampleProjExplicitLod", + Class: "Image", + Opcode: 310, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSparseSampleProjDrefImplicitLod = &Opcode { + Opname: "OpImageSparseSampleProjDrefImplicitLod", + Class: "Image", + Opcode: 311, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseSampleProjDrefExplicitLod = &Opcode { + Opname: "OpImageSparseSampleProjDrefExplicitLod", + Class: "Image", + Opcode: 312, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "", + }, + }, + } + OpImageSparseFetch = &Opcode { + Opname: "OpImageSparseFetch", + Class: "Image", + Opcode: 313, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseGather = &Opcode { + Opname: "OpImageSparseGather", + Class: "Image", + Opcode: 314, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseDrefGather = &Opcode { + Opname: "OpImageSparseDrefGather", + Class: "Image", + Opcode: 315, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'D~ref~'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpImageSparseTexelsResident = &Opcode { + Opname: "OpImageSparseTexelsResident", + Class: "Image", + Opcode: 316, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Resident Code'", + Quantifier: "", + }, + }, + } + OpNoLine = &Opcode { + Opname: "OpNoLine", + Class: "Debug", + Opcode: 317, + Operands: []Operand { + }, + } + OpAtomicFlagTestAndSet = &Opcode { + Opname: "OpAtomicFlagTestAndSet", + Class: "Atomic", + Opcode: 318, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpAtomicFlagClear = &Opcode { + Opname: "OpAtomicFlagClear", + Class: "Atomic", + Opcode: 319, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpImageSparseRead = &Opcode { + Opname: "OpImageSparseRead", + Class: "Image", + Opcode: 320, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpSizeOf = &Opcode { + Opname: "OpSizeOf", + Class: "Miscellaneous", + Opcode: 321, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + }, + } + OpTypePipeStorage = &Opcode { + Opname: "OpTypePipeStorage", + Class: "Type-Declaration", + Opcode: 322, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpConstantPipeStorage = &Opcode { + Opname: "OpConstantPipeStorage", + Class: "Pipe", + Opcode: 323, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Packet Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Packet Alignment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Capacity'", + Quantifier: "", + }, + }, + } + OpCreatePipeFromPipeStorage = &Opcode { + Opname: "OpCreatePipeFromPipeStorage", + Class: "Pipe", + Opcode: 324, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pipe Storage'", + Quantifier: "", + }, + }, + } + OpGetKernelLocalSizeForSubgroupCount = &Opcode { + Opname: "OpGetKernelLocalSizeForSubgroupCount", + Class: "Device-Side_Enqueue", + Opcode: 325, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Subgroup Count'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpGetKernelMaxNumSubgroups = &Opcode { + Opname: "OpGetKernelMaxNumSubgroups", + Class: "Device-Side_Enqueue", + Opcode: 326, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Invoke'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Param Align'", + Quantifier: "", + }, + }, + } + OpTypeNamedBarrier = &Opcode { + Opname: "OpTypeNamedBarrier", + Class: "Type-Declaration", + Opcode: 327, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpNamedBarrierInitialize = &Opcode { + Opname: "OpNamedBarrierInitialize", + Class: "Barrier", + Opcode: 328, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Subgroup Count'", + Quantifier: "", + }, + }, + } + OpMemoryNamedBarrier = &Opcode { + Opname: "OpMemoryNamedBarrier", + Class: "Barrier", + Opcode: 329, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Named Barrier'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Memory'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdMemorySemantics, + Name: "'Semantics'", + Quantifier: "", + }, + }, + } + OpModuleProcessed = &Opcode { + Opname: "OpModuleProcessed", + Class: "Debug", + Opcode: 330, + Operands: []Operand { + Operand { + Kind: OperandKindLiteralString, + Name: "'Process'", + Quantifier: "", + }, + }, + } + OpExecutionModeId = &Opcode { + Opname: "OpExecutionModeId", + Class: "Mode-Setting", + Opcode: 331, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Entry Point'", + Quantifier: "", + }, + Operand { + Kind: OperandKindExecutionMode, + Name: "'Mode'", + Quantifier: "", + }, + }, + } + OpDecorateId = &Opcode { + Opname: "OpDecorateId", + Class: "Annotation", + Opcode: 332, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpGroupNonUniformElect = &Opcode { + Opname: "OpGroupNonUniformElect", + Class: "Non-Uniform", + Opcode: 333, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformAll = &Opcode { + Opname: "OpGroupNonUniformAll", + Class: "Non-Uniform", + Opcode: 334, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformAny = &Opcode { + Opname: "OpGroupNonUniformAny", + Class: "Non-Uniform", + Opcode: 335, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformAllEqual = &Opcode { + Opname: "OpGroupNonUniformAllEqual", + Class: "Non-Uniform", + Opcode: 336, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBroadcast = &Opcode { + Opname: "OpGroupNonUniformBroadcast", + Class: "Non-Uniform", + Opcode: 337, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Id'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBroadcastFirst = &Opcode { + Opname: "OpGroupNonUniformBroadcastFirst", + Class: "Non-Uniform", + Opcode: 338, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallot = &Opcode { + Opname: "OpGroupNonUniformBallot", + Class: "Non-Uniform", + Opcode: 339, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformInverseBallot = &Opcode { + Opname: "OpGroupNonUniformInverseBallot", + Class: "Non-Uniform", + Opcode: 340, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallotBitExtract = &Opcode { + Opname: "OpGroupNonUniformBallotBitExtract", + Class: "Non-Uniform", + Opcode: 341, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallotBitCount = &Opcode { + Opname: "OpGroupNonUniformBallotBitCount", + Class: "Non-Uniform", + Opcode: 342, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallotFindLSB = &Opcode { + Opname: "OpGroupNonUniformBallotFindLSB", + Class: "Non-Uniform", + Opcode: 343, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformBallotFindMSB = &Opcode { + Opname: "OpGroupNonUniformBallotFindMSB", + Class: "Non-Uniform", + Opcode: 344, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformShuffle = &Opcode { + Opname: "OpGroupNonUniformShuffle", + Class: "Non-Uniform", + Opcode: 345, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Id'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformShuffleXor = &Opcode { + Opname: "OpGroupNonUniformShuffleXor", + Class: "Non-Uniform", + Opcode: 346, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Mask'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformShuffleUp = &Opcode { + Opname: "OpGroupNonUniformShuffleUp", + Class: "Non-Uniform", + Opcode: 347, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformShuffleDown = &Opcode { + Opname: "OpGroupNonUniformShuffleDown", + Class: "Non-Uniform", + Opcode: 348, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformIAdd = &Opcode { + Opname: "OpGroupNonUniformIAdd", + Class: "Non-Uniform", + Opcode: 349, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformFAdd = &Opcode { + Opname: "OpGroupNonUniformFAdd", + Class: "Non-Uniform", + Opcode: 350, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformIMul = &Opcode { + Opname: "OpGroupNonUniformIMul", + Class: "Non-Uniform", + Opcode: 351, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformFMul = &Opcode { + Opname: "OpGroupNonUniformFMul", + Class: "Non-Uniform", + Opcode: 352, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformSMin = &Opcode { + Opname: "OpGroupNonUniformSMin", + Class: "Non-Uniform", + Opcode: 353, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformUMin = &Opcode { + Opname: "OpGroupNonUniformUMin", + Class: "Non-Uniform", + Opcode: 354, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformFMin = &Opcode { + Opname: "OpGroupNonUniformFMin", + Class: "Non-Uniform", + Opcode: 355, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformSMax = &Opcode { + Opname: "OpGroupNonUniformSMax", + Class: "Non-Uniform", + Opcode: 356, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformUMax = &Opcode { + Opname: "OpGroupNonUniformUMax", + Class: "Non-Uniform", + Opcode: 357, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformFMax = &Opcode { + Opname: "OpGroupNonUniformFMax", + Class: "Non-Uniform", + Opcode: 358, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformBitwiseAnd = &Opcode { + Opname: "OpGroupNonUniformBitwiseAnd", + Class: "Non-Uniform", + Opcode: 359, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformBitwiseOr = &Opcode { + Opname: "OpGroupNonUniformBitwiseOr", + Class: "Non-Uniform", + Opcode: 360, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformBitwiseXor = &Opcode { + Opname: "OpGroupNonUniformBitwiseXor", + Class: "Non-Uniform", + Opcode: 361, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformLogicalAnd = &Opcode { + Opname: "OpGroupNonUniformLogicalAnd", + Class: "Non-Uniform", + Opcode: 362, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformLogicalOr = &Opcode { + Opname: "OpGroupNonUniformLogicalOr", + Class: "Non-Uniform", + Opcode: 363, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformLogicalXor = &Opcode { + Opname: "OpGroupNonUniformLogicalXor", + Class: "Non-Uniform", + Opcode: 364, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ClusterSize'", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformQuadBroadcast = &Opcode { + Opname: "OpGroupNonUniformQuadBroadcast", + Class: "Non-Uniform", + Opcode: 365, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpGroupNonUniformQuadSwap = &Opcode { + Opname: "OpGroupNonUniformQuadSwap", + Class: "Non-Uniform", + Opcode: 366, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpCopyLogical = &Opcode { + Opname: "OpCopyLogical", + Class: "Composite", + Opcode: 400, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpPtrEqual = &Opcode { + Opname: "OpPtrEqual", + Class: "Memory", + Opcode: 401, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpPtrNotEqual = &Opcode { + Opname: "OpPtrNotEqual", + Class: "Memory", + Opcode: 402, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpPtrDiff = &Opcode { + Opname: "OpPtrDiff", + Class: "Memory", + Opcode: 403, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpSubgroupBallotKHR = &Opcode { + Opname: "OpSubgroupBallotKHR", + Class: "Group", + Opcode: 4421, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpSubgroupFirstInvocationKHR = &Opcode { + Opname: "OpSubgroupFirstInvocationKHR", + Class: "Group", + Opcode: 4422, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpSubgroupAllKHR = &Opcode { + Opname: "OpSubgroupAllKHR", + Class: "Group", + Opcode: 4428, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpSubgroupAnyKHR = &Opcode { + Opname: "OpSubgroupAnyKHR", + Class: "Group", + Opcode: 4429, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpSubgroupAllEqualKHR = &Opcode { + Opname: "OpSubgroupAllEqualKHR", + Class: "Group", + Opcode: 4430, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Predicate'", + Quantifier: "", + }, + }, + } + OpSubgroupReadInvocationKHR = &Opcode { + Opname: "OpSubgroupReadInvocationKHR", + Class: "Group", + Opcode: 4432, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Index'", + Quantifier: "", + }, + }, + } + OpGroupIAddNonUniformAMD = &Opcode { + Opname: "OpGroupIAddNonUniformAMD", + Class: "Group", + Opcode: 5000, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFAddNonUniformAMD = &Opcode { + Opname: "OpGroupFAddNonUniformAMD", + Class: "Group", + Opcode: 5001, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMinNonUniformAMD = &Opcode { + Opname: "OpGroupFMinNonUniformAMD", + Class: "Group", + Opcode: 5002, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupUMinNonUniformAMD = &Opcode { + Opname: "OpGroupUMinNonUniformAMD", + Class: "Group", + Opcode: 5003, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupSMinNonUniformAMD = &Opcode { + Opname: "OpGroupSMinNonUniformAMD", + Class: "Group", + Opcode: 5004, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupFMaxNonUniformAMD = &Opcode { + Opname: "OpGroupFMaxNonUniformAMD", + Class: "Group", + Opcode: 5005, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupUMaxNonUniformAMD = &Opcode { + Opname: "OpGroupUMaxNonUniformAMD", + Class: "Group", + Opcode: 5006, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpGroupSMaxNonUniformAMD = &Opcode { + Opname: "OpGroupSMaxNonUniformAMD", + Class: "Group", + Opcode: 5007, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindGroupOperation, + Name: "'Operation'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'X'", + Quantifier: "", + }, + }, + } + OpFragmentMaskFetchAMD = &Opcode { + Opname: "OpFragmentMaskFetchAMD", + Class: "Reserved", + Opcode: 5011, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + }, + } + OpFragmentFetchAMD = &Opcode { + Opname: "OpFragmentFetchAMD", + Class: "Reserved", + Opcode: 5012, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fragment Index'", + Quantifier: "", + }, + }, + } + OpReadClockKHR = &Opcode { + Opname: "OpReadClockKHR", + Class: "Reserved", + Opcode: 5056, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + }, + } + OpImageSampleFootprintNV = &Opcode { + Opname: "OpImageSampleFootprintNV", + Class: "Image", + Opcode: 5283, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampled Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Granularity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coarse'", + Quantifier: "", + }, + Operand { + Kind: OperandKindImageOperands, + Name: "", + Quantifier: "?", + }, + }, + } + OpGroupNonUniformPartitionNV = &Opcode { + Opname: "OpGroupNonUniformPartitionNV", + Class: "Non-Uniform", + Opcode: 5296, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpWritePackedPrimitiveIndices4x8NV = &Opcode { + Opname: "OpWritePackedPrimitiveIndices4x8NV", + Class: "Reserved", + Opcode: 5299, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Index Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Indices'", + Quantifier: "", + }, + }, + } + OpReportIntersectionNV = &Opcode { + Opname: "OpReportIntersectionNV", + Class: "Reserved", + Opcode: 5334, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitKind'", + Quantifier: "", + }, + }, + } + OpReportIntersectionKHR = &Opcode { + Opname: "OpReportIntersectionKHR", + Class: "Reserved", + Opcode: 5334, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Hit'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitKind'", + Quantifier: "", + }, + }, + } + OpIgnoreIntersectionNV = &Opcode { + Opname: "OpIgnoreIntersectionNV", + Class: "Reserved", + Opcode: 5335, + Operands: []Operand { + }, + } + OpIgnoreIntersectionKHR = &Opcode { + Opname: "OpIgnoreIntersectionKHR", + Class: "Reserved", + Opcode: 5335, + Operands: []Operand { + }, + } + OpTerminateRayNV = &Opcode { + Opname: "OpTerminateRayNV", + Class: "Reserved", + Opcode: 5336, + Operands: []Operand { + }, + } + OpTerminateRayKHR = &Opcode { + Opname: "OpTerminateRayKHR", + Class: "Reserved", + Opcode: 5336, + Operands: []Operand { + }, + } + OpTraceNV = &Opcode { + Opname: "OpTraceNV", + Class: "Reserved", + Opcode: 5337, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cull Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PayloadId'", + Quantifier: "", + }, + }, + } + OpTraceRayKHR = &Opcode { + Opname: "OpTraceRayKHR", + Class: "Reserved", + Opcode: 5337, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cull Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Miss Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Origin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ray Tmax'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'PayloadId'", + Quantifier: "", + }, + }, + } + OpTypeAccelerationStructureNV = &Opcode { + Opname: "OpTypeAccelerationStructureNV", + Class: "Reserved", + Opcode: 5341, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAccelerationStructureKHR = &Opcode { + Opname: "OpTypeAccelerationStructureKHR", + Class: "Reserved", + Opcode: 5341, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeRayQueryProvisionalKHR = &Opcode { + Opname: "OpTypeRayQueryProvisionalKHR", + Class: "Reserved", + Opcode: 4472, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpRayQueryInitializeKHR = &Opcode { + Opname: "OpRayQueryInitializeKHR", + Class: "Reserved", + Opcode: 4473, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Accel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayFlags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'CullMask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayOrigin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayTMin'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayDirection'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayTMax'", + Quantifier: "", + }, + }, + } + OpRayQueryTerminateKHR = &Opcode { + Opname: "OpRayQueryTerminateKHR", + Class: "Reserved", + Opcode: 4474, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGenerateIntersectionKHR = &Opcode { + Opname: "OpRayQueryGenerateIntersectionKHR", + Class: "Reserved", + Opcode: 4475, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'HitT'", + Quantifier: "", + }, + }, + } + OpRayQueryConfirmIntersectionKHR = &Opcode { + Opname: "OpRayQueryConfirmIntersectionKHR", + Class: "Reserved", + Opcode: 4476, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryProceedKHR = &Opcode { + Opname: "OpRayQueryProceedKHR", + Class: "Reserved", + Opcode: 4477, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTypeKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTypeKHR", + Class: "Reserved", + Opcode: 4479, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetRayTMinKHR = &Opcode { + Opname: "OpRayQueryGetRayTMinKHR", + Class: "Reserved", + Opcode: 6016, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetRayFlagsKHR = &Opcode { + Opname: "OpRayQueryGetRayFlagsKHR", + Class: "Reserved", + Opcode: 6017, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionTKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionTKHR", + Class: "Reserved", + Opcode: 6018, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceCustomIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceCustomIndexKHR", + Class: "Reserved", + Opcode: 6019, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceIdKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceIdKHR", + Class: "Reserved", + Opcode: 6020, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", + Class: "Reserved", + Opcode: 6021, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionGeometryIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionGeometryIndexKHR", + Class: "Reserved", + Opcode: 6022, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionPrimitiveIndexKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionPrimitiveIndexKHR", + Class: "Reserved", + Opcode: 6023, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionBarycentricsKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionBarycentricsKHR", + Class: "Reserved", + Opcode: 6024, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionFrontFaceKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionFrontFaceKHR", + Class: "Reserved", + Opcode: 6025, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", + Class: "Reserved", + Opcode: 6026, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectRayDirectionKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectRayDirectionKHR", + Class: "Reserved", + Opcode: 6027, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectRayOriginKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectRayOriginKHR", + Class: "Reserved", + Opcode: 6028, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetWorldRayDirectionKHR = &Opcode { + Opname: "OpRayQueryGetWorldRayDirectionKHR", + Class: "Reserved", + Opcode: 6029, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetWorldRayOriginKHR = &Opcode { + Opname: "OpRayQueryGetWorldRayOriginKHR", + Class: "Reserved", + Opcode: 6030, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionObjectToWorldKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionObjectToWorldKHR", + Class: "Reserved", + Opcode: 6031, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpRayQueryGetIntersectionWorldToObjectKHR = &Opcode { + Opname: "OpRayQueryGetIntersectionWorldToObjectKHR", + Class: "Reserved", + Opcode: 6032, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'RayQuery'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intersection'", + Quantifier: "", + }, + }, + } + OpExecuteCallableNV = &Opcode { + Opname: "OpExecuteCallableNV", + Class: "Reserved", + Opcode: 5344, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Callable DataId'", + Quantifier: "", + }, + }, + } + OpExecuteCallableKHR = &Opcode { + Opname: "OpExecuteCallableKHR", + Class: "Reserved", + Opcode: 5344, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'SBT Index'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Callable DataId'", + Quantifier: "", + }, + }, + } + OpTypeCooperativeMatrixNV = &Opcode { + Opname: "OpTypeCooperativeMatrixNV", + Class: "Reserved", + Opcode: 5358, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdScope, + Name: "'Execution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Rows'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Columns'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixLoadNV = &Opcode { + Opname: "OpCooperativeMatrixLoadNV", + Class: "Reserved", + Opcode: 5359, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Column Major'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixStoreNV = &Opcode { + Opname: "OpCooperativeMatrixStoreNV", + Class: "Reserved", + Opcode: 5360, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Pointer'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Object'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Stride'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Column Major'", + Quantifier: "", + }, + Operand { + Kind: OperandKindMemoryAccess, + Name: "", + Quantifier: "?", + }, + }, + } + OpCooperativeMatrixMulAddNV = &Opcode { + Opname: "OpCooperativeMatrixMulAddNV", + Class: "Reserved", + Opcode: 5361, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'A'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'B'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'C'", + Quantifier: "", + }, + }, + } + OpCooperativeMatrixLengthNV = &Opcode { + Opname: "OpCooperativeMatrixLengthNV", + Class: "Reserved", + Opcode: 5362, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + }, + } + OpBeginInvocationInterlockEXT = &Opcode { + Opname: "OpBeginInvocationInterlockEXT", + Class: "Reserved", + Opcode: 5364, + Operands: []Operand { + }, + } + OpEndInvocationInterlockEXT = &Opcode { + Opname: "OpEndInvocationInterlockEXT", + Class: "Reserved", + Opcode: 5365, + Operands: []Operand { + }, + } + OpDemoteToHelperInvocationEXT = &Opcode { + Opname: "OpDemoteToHelperInvocationEXT", + Class: "Reserved", + Opcode: 5380, + Operands: []Operand { + }, + } + OpIsHelperInvocationEXT = &Opcode { + Opname: "OpIsHelperInvocationEXT", + Class: "Reserved", + Opcode: 5381, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupShuffleINTEL = &Opcode { + Opname: "OpSubgroupShuffleINTEL", + Class: "Group", + Opcode: 5571, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'InvocationId'", + Quantifier: "", + }, + }, + } + OpSubgroupShuffleDownINTEL = &Opcode { + Opname: "OpSubgroupShuffleDownINTEL", + Class: "Group", + Opcode: 5572, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Next'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + }, + } + OpSubgroupShuffleUpINTEL = &Opcode { + Opname: "OpSubgroupShuffleUpINTEL", + Class: "Group", + Opcode: 5573, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Previous'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Current'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Delta'", + Quantifier: "", + }, + }, + } + OpSubgroupShuffleXorINTEL = &Opcode { + Opname: "OpSubgroupShuffleXorINTEL", + Class: "Group", + Opcode: 5574, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + OpSubgroupBlockReadINTEL = &Opcode { + Opname: "OpSubgroupBlockReadINTEL", + Class: "Group", + Opcode: 5575, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ptr'", + Quantifier: "", + }, + }, + } + OpSubgroupBlockWriteINTEL = &Opcode { + Opname: "OpSubgroupBlockWriteINTEL", + Class: "Group", + Opcode: 5576, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Ptr'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + }, + } + OpSubgroupImageBlockReadINTEL = &Opcode { + Opname: "OpSubgroupImageBlockReadINTEL", + Class: "Group", + Opcode: 5577, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + }, + } + OpSubgroupImageBlockWriteINTEL = &Opcode { + Opname: "OpSubgroupImageBlockWriteINTEL", + Class: "Group", + Opcode: 5578, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + }, + } + OpSubgroupImageMediaBlockReadINTEL = &Opcode { + Opname: "OpSubgroupImageMediaBlockReadINTEL", + Class: "Group", + Opcode: 5580, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Width'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Height'", + Quantifier: "", + }, + }, + } + OpSubgroupImageMediaBlockWriteINTEL = &Opcode { + Opname: "OpSubgroupImageMediaBlockWriteINTEL", + Class: "Group", + Opcode: 5581, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Coordinate'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Width'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Height'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Data'", + Quantifier: "", + }, + }, + } + OpUCountLeadingZerosINTEL = &Opcode { + Opname: "OpUCountLeadingZerosINTEL", + Class: "Reserved", + Opcode: 5585, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpUCountTrailingZerosINTEL = &Opcode { + Opname: "OpUCountTrailingZerosINTEL", + Class: "Reserved", + Opcode: 5586, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand'", + Quantifier: "", + }, + }, + } + OpAbsISubINTEL = &Opcode { + Opname: "OpAbsISubINTEL", + Class: "Reserved", + Opcode: 5587, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpAbsUSubINTEL = &Opcode { + Opname: "OpAbsUSubINTEL", + Class: "Reserved", + Opcode: 5588, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIAddSatINTEL = &Opcode { + Opname: "OpIAddSatINTEL", + Class: "Reserved", + Opcode: 5589, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUAddSatINTEL = &Opcode { + Opname: "OpUAddSatINTEL", + Class: "Reserved", + Opcode: 5590, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIAverageINTEL = &Opcode { + Opname: "OpIAverageINTEL", + Class: "Reserved", + Opcode: 5591, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUAverageINTEL = &Opcode { + Opname: "OpUAverageINTEL", + Class: "Reserved", + Opcode: 5592, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIAverageRoundedINTEL = &Opcode { + Opname: "OpIAverageRoundedINTEL", + Class: "Reserved", + Opcode: 5593, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUAverageRoundedINTEL = &Opcode { + Opname: "OpUAverageRoundedINTEL", + Class: "Reserved", + Opcode: 5594, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpISubSatINTEL = &Opcode { + Opname: "OpISubSatINTEL", + Class: "Reserved", + Opcode: 5595, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUSubSatINTEL = &Opcode { + Opname: "OpUSubSatINTEL", + Class: "Reserved", + Opcode: 5596, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpIMul32x16INTEL = &Opcode { + Opname: "OpIMul32x16INTEL", + Class: "Reserved", + Opcode: 5597, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpUMul32x16INTEL = &Opcode { + Opname: "OpUMul32x16INTEL", + Class: "Reserved", + Opcode: 5598, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Operand 2'", + Quantifier: "", + }, + }, + } + OpDecorateString = &Opcode { + Opname: "OpDecorateString", + Class: "Annotation", + Opcode: 5632, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpDecorateStringGOOGLE = &Opcode { + Opname: "OpDecorateStringGOOGLE", + Class: "Annotation", + Opcode: 5632, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpMemberDecorateString = &Opcode { + Opname: "OpMemberDecorateString", + Class: "Annotation", + Opcode: 5633, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Struct Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Member'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpMemberDecorateStringGOOGLE = &Opcode { + Opname: "OpMemberDecorateStringGOOGLE", + Class: "Annotation", + Opcode: 5633, + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Struct Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Member'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDecoration, + Name: "", + Quantifier: "", + }, + }, + } + OpVmeImageINTEL = &Opcode { + Opname: "OpVmeImageINTEL", + Class: "@exclude", + Opcode: 5699, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sampler'", + Quantifier: "", + }, + }, + } + OpTypeVmeImageINTEL = &Opcode { + Opname: "OpTypeVmeImageINTEL", + Class: "@exclude", + Opcode: 5700, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Type'", + Quantifier: "", + }, + }, + } + OpTypeAvcImePayloadINTEL = &Opcode { + Opname: "OpTypeAvcImePayloadINTEL", + Class: "@exclude", + Opcode: 5701, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcRefPayloadINTEL = &Opcode { + Opname: "OpTypeAvcRefPayloadINTEL", + Class: "@exclude", + Opcode: 5702, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcSicPayloadINTEL = &Opcode { + Opname: "OpTypeAvcSicPayloadINTEL", + Class: "@exclude", + Opcode: 5703, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcMcePayloadINTEL = &Opcode { + Opname: "OpTypeAvcMcePayloadINTEL", + Class: "@exclude", + Opcode: 5704, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcMceResultINTEL = &Opcode { + Opname: "OpTypeAvcMceResultINTEL", + Class: "@exclude", + Opcode: 5705, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeResultINTEL = &Opcode { + Opname: "OpTypeAvcImeResultINTEL", + Class: "@exclude", + Opcode: 5706, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = &Opcode { + Opname: "OpTypeAvcImeResultSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5707, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeResultDualReferenceStreamoutINTEL = &Opcode { + Opname: "OpTypeAvcImeResultDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5708, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeSingleReferenceStreaminINTEL = &Opcode { + Opname: "OpTypeAvcImeSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5709, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcImeDualReferenceStreaminINTEL = &Opcode { + Opname: "OpTypeAvcImeDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5710, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcRefResultINTEL = &Opcode { + Opname: "OpTypeAvcRefResultINTEL", + Class: "@exclude", + Opcode: 5711, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpTypeAvcSicResultINTEL = &Opcode { + Opname: "OpTypeAvcSicResultINTEL", + Class: "@exclude", + Opcode: 5712, + Operands: []Operand { + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL", + Class: "@exclude", + Opcode: 5713, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL", + Class: "@exclude", + Opcode: 5714, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Base Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5715, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetInterShapePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetInterShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5716, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Shape Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL", + Class: "@exclude", + Opcode: 5717, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL", + Class: "@exclude", + Opcode: 5718, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction Cost'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5719, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL", + Class: "@exclude", + Opcode: 5720, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5721, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5722, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL", + Class: "@exclude", + Opcode: 5723, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL", + Class: "@exclude", + Opcode: 5724, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Cost Center Delta'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Cost Table'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Cost Precision'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL", + Class: "@exclude", + Opcode: 5725, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Slice Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Qp'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL", + Class: "@exclude", + Opcode: 5726, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL", + Class: "@exclude", + Opcode: 5727, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetAcOnlyHaarINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetAcOnlyHaarINTEL", + Class: "@exclude", + Opcode: 5728, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL", + Class: "@exclude", + Opcode: 5729, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source Field Polarity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL", + Class: "@exclude", + Opcode: 5730, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Reference Field Polarity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = &Opcode { + Opname: "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL", + Class: "@exclude", + Opcode: 5731, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Forward Reference Field Polarity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Backward Reference Field Polarity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToImePayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToImePayloadINTEL", + Class: "@exclude", + Opcode: 5732, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToImeResultINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToImeResultINTEL", + Class: "@exclude", + Opcode: 5733, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToRefPayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToRefPayloadINTEL", + Class: "@exclude", + Opcode: 5734, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToRefResultINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToRefResultINTEL", + Class: "@exclude", + Opcode: 5735, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToSicPayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToSicPayloadINTEL", + Class: "@exclude", + Opcode: 5736, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceConvertToSicResultINTEL = &Opcode { + Opname: "OpSubgroupAvcMceConvertToSicResultINTEL", + Class: "@exclude", + Opcode: 5737, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetMotionVectorsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5738, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterDistortionsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterDistortionsINTEL", + Class: "@exclude", + Opcode: 5739, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetBestInterDistortionsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetBestInterDistortionsINTEL", + Class: "@exclude", + Opcode: 5740, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterMajorShapeINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterMajorShapeINTEL", + Class: "@exclude", + Opcode: 5741, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterMinorShapeINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterMinorShapeINTEL", + Class: "@exclude", + Opcode: 5742, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterDirectionsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterDirectionsINTEL", + Class: "@exclude", + Opcode: 5743, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterMotionVectorCountINTEL", + Class: "@exclude", + Opcode: 5744, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterReferenceIdsINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5745, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = &Opcode { + Opname: "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL", + Class: "@exclude", + Opcode: 5746, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Parameter Field Polarities'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeInitializeINTEL = &Opcode { + Opname: "OpSubgroupAvcImeInitializeINTEL", + Class: "@exclude", + Opcode: 5747, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Partition Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'SAD Adjustment'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetSingleReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5748, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Search Window Config'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetDualReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetDualReferenceINTEL", + Class: "@exclude", + Opcode: 5749, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'id> Search Window Config'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeRefWindowSizeINTEL = &Opcode { + Opname: "OpSubgroupAvcImeRefWindowSizeINTEL", + Class: "@exclude", + Opcode: 5750, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Search Window Config'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Dual Ref'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeAdjustRefOffsetINTEL = &Opcode { + Opname: "OpSubgroupAvcImeAdjustRefOffsetINTEL", + Class: "@exclude", + Opcode: 5751, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Window Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Size'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeConvertToMcePayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcImeConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5752, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL", + Class: "@exclude", + Opcode: 5753, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Max Motion Vector Count'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL", + Class: "@exclude", + Opcode: 5754, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL", + Class: "@exclude", + Opcode: 5755, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Threshold'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeSetWeightedSadINTEL = &Opcode { + Opname: "OpSubgroupAvcImeSetWeightedSadINTEL", + Class: "@exclude", + Opcode: 5756, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Sad Weights'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5757, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5758, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5759, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Streamin Components'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5760, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Streamin Components'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5761, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5762, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL", + Class: "@exclude", + Opcode: 5763, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Streamin Components'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL", + Class: "@exclude", + Opcode: 5764, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Streamin Components'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeConvertToMceResultINTEL = &Opcode { + Opname: "OpSubgroupAvcImeConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5765, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5766, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetDualReferenceStreaminINTEL", + Class: "@exclude", + Opcode: 5767, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5768, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = &Opcode { + Opname: "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL", + Class: "@exclude", + Opcode: 5769, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5770, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL", + Class: "@exclude", + Opcode: 5771, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5772, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL", + Class: "@exclude", + Opcode: 5773, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL", + Class: "@exclude", + Opcode: 5774, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL", + Class: "@exclude", + Opcode: 5775, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shape'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetBorderReachedINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetBorderReachedINTEL", + Class: "@exclude", + Opcode: 5776, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Image Select'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL", + Class: "@exclude", + Opcode: 5777, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL", + Class: "@exclude", + Opcode: 5778, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL", + Class: "@exclude", + Opcode: 5779, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = &Opcode { + Opname: "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL", + Class: "@exclude", + Opcode: 5780, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcFmeInitializeINTEL = &Opcode { + Opname: "OpSubgroupAvcFmeInitializeINTEL", + Class: "@exclude", + Opcode: 5781, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Motion Vectors'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shapes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Minor Shapes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pixel Resolution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcBmeInitializeINTEL = &Opcode { + Opname: "OpSubgroupAvcBmeInitializeINTEL", + Class: "@exclude", + Opcode: 5782, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Motion Vectors'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Major Shapes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Minor Shapes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Pixel Resolution'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bidirectional Weight'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefConvertToMcePayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcRefConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5783, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = &Opcode { + Opname: "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL", + Class: "@exclude", + Opcode: 5784, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = &Opcode { + Opname: "OpSubgroupAvcRefSetBilinearFilterEnableINTEL", + Class: "@exclude", + Opcode: 5785, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5786, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5787, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL", + Class: "@exclude", + Opcode: 5788, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = &Opcode { + Opname: "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL", + Class: "@exclude", + Opcode: 5789, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Field Polarities'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcRefConvertToMceResultINTEL = &Opcode { + Opname: "OpSubgroupAvcRefConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5790, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicInitializeINTEL = &Opcode { + Opname: "OpSubgroupAvcSicInitializeINTEL", + Class: "@exclude", + Opcode: 5791, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Coord'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConfigureSkcINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConfigureSkcINTEL", + Class: "@exclude", + Opcode: 5792, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Skip Block Partition Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Skip Motion Vector Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Motion Vectors'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bidirectional Weight'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConfigureIpeLumaINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConfigureIpeLumaINTEL", + Class: "@exclude", + Opcode: 5793, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Intra Partition Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intra Neighbour Availabilty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Left Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Left Corner Luma Pixel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Right Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL", + Class: "@exclude", + Opcode: 5794, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Intra Partition Mask'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Intra Neighbour Availabilty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Left Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Left Corner Luma Pixel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Right Edge Luma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Left Edge Chroma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Left Corner Chroma Pixel'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Upper Edge Chroma Pixels'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Sad Adjustment'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetMotionVectorMaskINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetMotionVectorMaskINTEL", + Class: "@exclude", + Opcode: 5795, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Skip Block Partition Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Direction'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConvertToMcePayloadINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConvertToMcePayloadINTEL", + Class: "@exclude", + Opcode: 5796, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL", + Class: "@exclude", + Opcode: 5797, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Shape Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL", + Class: "@exclude", + Opcode: 5798, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Mode Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Packed Neighbor Modes'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Luma Packed Non Dc Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL", + Class: "@exclude", + Opcode: 5799, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Chroma Mode Base Penalty'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetBilinearFilterEnableINTEL", + Class: "@exclude", + Opcode: 5800, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL", + Class: "@exclude", + Opcode: 5801, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Sad Coefficients'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = &Opcode { + Opname: "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL", + Class: "@exclude", + Opcode: 5802, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Block Based Skip Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateIpeINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateIpeINTEL", + Class: "@exclude", + Opcode: 5803, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL", + Class: "@exclude", + Opcode: 5804, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL", + Class: "@exclude", + Opcode: 5805, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Fwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Bwd Ref Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL", + Class: "@exclude", + Opcode: 5806, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = &Opcode { + Opname: "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL", + Class: "@exclude", + Opcode: 5807, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Src Image'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Ids'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Packed Reference Field Polarities'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicConvertToMceResultINTEL = &Opcode { + Opname: "OpSubgroupAvcSicConvertToMceResultINTEL", + Class: "@exclude", + Opcode: 5808, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetIpeLumaShapeINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetIpeLumaShapeINTEL", + Class: "@exclude", + Opcode: 5809, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL", + Class: "@exclude", + Opcode: 5810, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL", + Class: "@exclude", + Opcode: 5811, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL", + Class: "@exclude", + Opcode: 5812, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetIpeChromaModeINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetIpeChromaModeINTEL", + Class: "@exclude", + Opcode: 5813, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL", + Class: "@exclude", + Opcode: 5814, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL", + Class: "@exclude", + Opcode: 5815, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + OpSubgroupAvcSicGetInterRawSadsINTEL = &Opcode { + Opname: "OpSubgroupAvcSicGetInterRawSadsINTEL", + Class: "@exclude", + Opcode: 5816, + Operands: []Operand { + Operand { + Kind: OperandKindIdResultType, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdResult, + Name: "", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Payload'", + Quantifier: "", + }, + }, + } + + GLSLStd450_Round = &Opcode { + Opname: "Round", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_RoundEven = &Opcode { + Opname: "RoundEven", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Trunc = &Opcode { + Opname: "Trunc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FAbs = &Opcode { + Opname: "FAbs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SAbs = &Opcode { + Opname: "SAbs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FSign = &Opcode { + Opname: "FSign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SSign = &Opcode { + Opname: "SSign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Floor = &Opcode { + Opname: "Floor", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Ceil = &Opcode { + Opname: "Ceil", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Fract = &Opcode { + Opname: "Fract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Radians = &Opcode { + Opname: "Radians", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'degrees'", + Quantifier: "", + }, + }, + } + GLSLStd450_Degrees = &Opcode { + Opname: "Degrees", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'radians'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sin = &Opcode { + Opname: "Sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cos = &Opcode { + Opname: "Cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Tan = &Opcode { + Opname: "Tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Asin = &Opcode { + Opname: "Asin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Acos = &Opcode { + Opname: "Acos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atan = &Opcode { + Opname: "Atan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y_over_x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sinh = &Opcode { + Opname: "Sinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cosh = &Opcode { + Opname: "Cosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Tanh = &Opcode { + Opname: "Tanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Asinh = &Opcode { + Opname: "Asinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Acosh = &Opcode { + Opname: "Acosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atanh = &Opcode { + Opname: "Atanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Atan2 = &Opcode { + Opname: "Atan2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Pow = &Opcode { + Opname: "Pow", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_Exp = &Opcode { + Opname: "Exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Log = &Opcode { + Opname: "Log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Exp2 = &Opcode { + Opname: "Exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Log2 = &Opcode { + Opname: "Log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Sqrt = &Opcode { + Opname: "Sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_InverseSqrt = &Opcode { + Opname: "InverseSqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Determinant = &Opcode { + Opname: "Determinant", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_MatrixInverse = &Opcode { + Opname: "MatrixInverse", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Modf = &Opcode { + Opname: "Modf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'i'", + Quantifier: "", + }, + }, + } + GLSLStd450_ModfStruct = &Opcode { + Opname: "ModfStruct", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMin = &Opcode { + Opname: "FMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_UMin = &Opcode { + Opname: "UMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_SMin = &Opcode { + Opname: "SMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMax = &Opcode { + Opname: "FMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_UMax = &Opcode { + Opname: "UMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_SMax = &Opcode { + Opname: "SMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_FClamp = &Opcode { + Opname: "FClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_UClamp = &Opcode { + Opname: "UClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_SClamp = &Opcode { + Opname: "SClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + GLSLStd450_FMix = &Opcode { + Opname: "FMix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + GLSLStd450_IMix = &Opcode { + Opname: "IMix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + GLSLStd450_Step = &Opcode { + Opname: "Step", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_SmoothStep = &Opcode { + Opname: "SmoothStep", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'edge1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Fma = &Opcode { + Opname: "Fma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + GLSLStd450_Frexp = &Opcode { + Opname: "Frexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + GLSLStd450_FrexpStruct = &Opcode { + Opname: "FrexpStruct", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Ldexp = &Opcode { + Opname: "Ldexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackSnorm4x8 = &Opcode { + Opname: "PackSnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackUnorm4x8 = &Opcode { + Opname: "PackUnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackSnorm2x16 = &Opcode { + Opname: "PackSnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackUnorm2x16 = &Opcode { + Opname: "PackUnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackHalf2x16 = &Opcode { + Opname: "PackHalf2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_PackDouble2x32 = &Opcode { + Opname: "PackDouble2x32", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackSnorm2x16 = &Opcode { + Opname: "UnpackSnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackUnorm2x16 = &Opcode { + Opname: "UnpackUnorm2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackHalf2x16 = &Opcode { + Opname: "UnpackHalf2x16", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackSnorm4x8 = &Opcode { + Opname: "UnpackSnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackUnorm4x8 = &Opcode { + Opname: "UnpackUnorm4x8", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + GLSLStd450_UnpackDouble2x32 = &Opcode { + Opname: "UnpackDouble2x32", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + }, + } + GLSLStd450_Length = &Opcode { + Opname: "Length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_Distance = &Opcode { + Opname: "Distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + GLSLStd450_Cross = &Opcode { + Opname: "Cross", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_Normalize = &Opcode { + Opname: "Normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + GLSLStd450_FaceForward = &Opcode { + Opname: "FaceForward", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Nref'", + Quantifier: "", + }, + }, + } + GLSLStd450_Reflect = &Opcode { + Opname: "Reflect", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + }, + } + GLSLStd450_Refract = &Opcode { + Opname: "Refract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'I'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'N'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'eta'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindILsb = &Opcode { + Opname: "FindILsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindSMsb = &Opcode { + Opname: "FindSMsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_FindUMsb = &Opcode { + Opname: "FindUMsb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtCentroid = &Opcode { + Opname: "InterpolateAtCentroid", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtSample = &Opcode { + Opname: "InterpolateAtSample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'sample'", + Quantifier: "", + }, + }, + } + GLSLStd450_InterpolateAtOffset = &Opcode { + Opname: "InterpolateAtOffset", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'interpolant'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + }, + } + GLSLStd450_NMin = &Opcode { + Opname: "NMin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_NMax = &Opcode { + Opname: "NMax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + GLSLStd450_NClamp = &Opcode { + Opname: "NClamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minVal'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxVal'", + Quantifier: "", + }, + }, + } + OpenCLStd_acos = &Opcode { + Opname: "acos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_acosh = &Opcode { + Opname: "acosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_acospi = &Opcode { + Opname: "acospi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asin = &Opcode { + Opname: "asin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asinh = &Opcode { + Opname: "asinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_asinpi = &Opcode { + Opname: "asinpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan = &Opcode { + Opname: "atan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan2 = &Opcode { + Opname: "atan2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atanh = &Opcode { + Opname: "atanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atanpi = &Opcode { + Opname: "atanpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_atan2pi = &Opcode { + Opname: "atan2pi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cbrt = &Opcode { + Opname: "cbrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ceil = &Opcode { + Opname: "ceil", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_copysign = &Opcode { + Opname: "copysign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_cos = &Opcode { + Opname: "cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cosh = &Opcode { + Opname: "cosh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cospi = &Opcode { + Opname: "cospi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_erfc = &Opcode { + Opname: "erfc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_erf = &Opcode { + Opname: "erf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp = &Opcode { + Opname: "exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp2 = &Opcode { + Opname: "exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_exp10 = &Opcode { + Opname: "exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_expm1 = &Opcode { + Opname: "expm1", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fabs = &Opcode { + Opname: "fabs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fdim = &Opcode { + Opname: "fdim", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_floor = &Opcode { + Opname: "floor", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_fma = &Opcode { + Opname: "fma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmax = &Opcode { + Opname: "fmax", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmin = &Opcode { + Opname: "fmin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmod = &Opcode { + Opname: "fmod", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fract = &Opcode { + Opname: "fract", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'ptr'", + Quantifier: "", + }, + }, + } + OpenCLStd_frexp = &Opcode { + Opname: "frexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'exp'", + Quantifier: "", + }, + }, + } + OpenCLStd_hypot = &Opcode { + Opname: "hypot", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_ilogb = &Opcode { + Opname: "ilogb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ldexp = &Opcode { + Opname: "ldexp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'k'", + Quantifier: "", + }, + }, + } + OpenCLStd_lgamma = &Opcode { + Opname: "lgamma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_lgamma_r = &Opcode { + Opname: "lgamma_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'signp'", + Quantifier: "", + }, + }, + } + OpenCLStd_log = &Opcode { + Opname: "log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log2 = &Opcode { + Opname: "log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log10 = &Opcode { + Opname: "log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_log1p = &Opcode { + Opname: "log1p", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_logb = &Opcode { + Opname: "logb", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_mad = &Opcode { + Opname: "mad", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_maxmag = &Opcode { + Opname: "maxmag", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_minmag = &Opcode { + Opname: "minmag", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_modf = &Opcode { + Opname: "modf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'iptr'", + Quantifier: "", + }, + }, + } + OpenCLStd_nan = &Opcode { + Opname: "nan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'nancode'", + Quantifier: "", + }, + }, + } + OpenCLStd_nextafter = &Opcode { + Opname: "nextafter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_pow = &Opcode { + Opname: "pow", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y", + Quantifier: "", + }, + }, + } + OpenCLStd_pown = &Opcode { + Opname: "pown", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_powr = &Opcode { + Opname: "powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_remainder = &Opcode { + Opname: "remainder", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_remquo = &Opcode { + Opname: "remquo", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'quo'", + Quantifier: "", + }, + }, + } + OpenCLStd_rint = &Opcode { + Opname: "rint", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_rootn = &Opcode { + Opname: "rootn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_round = &Opcode { + Opname: "round", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_rsqrt = &Opcode { + Opname: "rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sin = &Opcode { + Opname: "sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sincos = &Opcode { + Opname: "sincos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'cosval'", + Quantifier: "", + }, + }, + } + OpenCLStd_sinh = &Opcode { + Opname: "sinh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sinpi = &Opcode { + Opname: "sinpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sqrt = &Opcode { + Opname: "sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tan = &Opcode { + Opname: "tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tanh = &Opcode { + Opname: "tanh", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tanpi = &Opcode { + Opname: "tanpi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_tgamma = &Opcode { + Opname: "tgamma", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_trunc = &Opcode { + Opname: "trunc", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_cos = &Opcode { + Opname: "half_cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_divide = &Opcode { + Opname: "half_divide", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp = &Opcode { + Opname: "half_exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp2 = &Opcode { + Opname: "half_exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_exp10 = &Opcode { + Opname: "half_exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log = &Opcode { + Opname: "half_log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log2 = &Opcode { + Opname: "half_log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_log10 = &Opcode { + Opname: "half_log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_powr = &Opcode { + Opname: "half_powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_recip = &Opcode { + Opname: "half_recip", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_rsqrt = &Opcode { + Opname: "half_rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_sin = &Opcode { + Opname: "half_sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_sqrt = &Opcode { + Opname: "half_sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_half_tan = &Opcode { + Opname: "half_tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_cos = &Opcode { + Opname: "native_cos", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_divide = &Opcode { + Opname: "native_divide", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp = &Opcode { + Opname: "native_exp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp2 = &Opcode { + Opname: "native_exp2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_exp10 = &Opcode { + Opname: "native_exp10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log = &Opcode { + Opname: "native_log", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log2 = &Opcode { + Opname: "native_log2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_log10 = &Opcode { + Opname: "native_log10", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_powr = &Opcode { + Opname: "native_powr", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_recip = &Opcode { + Opname: "native_recip", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_rsqrt = &Opcode { + Opname: "native_rsqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_sin = &Opcode { + Opname: "native_sin", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_sqrt = &Opcode { + Opname: "native_sqrt", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_native_tan = &Opcode { + Opname: "native_tan", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_abs = &Opcode { + Opname: "s_abs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_abs_diff = &Opcode { + Opname: "s_abs_diff", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_add_sat = &Opcode { + Opname: "s_add_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_add_sat = &Opcode { + Opname: "u_add_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_hadd = &Opcode { + Opname: "s_hadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_hadd = &Opcode { + Opname: "u_hadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_rhadd = &Opcode { + Opname: "s_rhadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_rhadd = &Opcode { + Opname: "u_rhadd", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_clamp = &Opcode { + Opname: "s_clamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_clamp = &Opcode { + Opname: "u_clamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_clz = &Opcode { + Opname: "clz", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_ctz = &Opcode { + Opname: "ctz", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad_hi = &Opcode { + Opname: "s_mad_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad_sat = &Opcode { + Opname: "u_mad_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad_sat = &Opcode { + Opname: "s_mad_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_max = &Opcode { + Opname: "s_max", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_max = &Opcode { + Opname: "u_max", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_min = &Opcode { + Opname: "s_min", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_min = &Opcode { + Opname: "u_min", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mul_hi = &Opcode { + Opname: "s_mul_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_rotate = &Opcode { + Opname: "rotate", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'v'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'i'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_sub_sat = &Opcode { + Opname: "s_sub_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_sub_sat = &Opcode { + Opname: "u_sub_sat", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_upsample = &Opcode { + Opname: "u_upsample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'hi'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'lo'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_upsample = &Opcode { + Opname: "s_upsample", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'hi'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'lo'", + Quantifier: "", + }, + }, + } + OpenCLStd_popcount = &Opcode { + Opname: "popcount", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mad24 = &Opcode { + Opname: "s_mad24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad24 = &Opcode { + Opname: "u_mad24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'z'", + Quantifier: "", + }, + }, + } + OpenCLStd_s_mul24 = &Opcode { + Opname: "s_mul24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mul24 = &Opcode { + Opname: "u_mul24", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_abs = &Opcode { + Opname: "u_abs", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_abs_diff = &Opcode { + Opname: "u_abs_diff", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mul_hi = &Opcode { + Opname: "u_mul_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_u_mad_hi = &Opcode { + Opname: "u_mad_hi", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_fclamp = &Opcode { + Opname: "fclamp", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'minval'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'maxval'", + Quantifier: "", + }, + }, + } + OpenCLStd_degrees = &Opcode { + Opname: "degrees", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'radians'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmax_common = &Opcode { + Opname: "fmax_common", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_fmin_common = &Opcode { + Opname: "fmin_common", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + }, + } + OpenCLStd_mix = &Opcode { + Opname: "mix", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + }, + } + OpenCLStd_radians = &Opcode { + Opname: "radians", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'degrees'", + Quantifier: "", + }, + }, + } + OpenCLStd_step = &Opcode { + Opname: "step", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_smoothstep = &Opcode { + Opname: "smoothstep", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'edge0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'edge1'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_sign = &Opcode { + Opname: "sign", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + }, + } + OpenCLStd_cross = &Opcode { + Opname: "cross", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_distance = &Opcode { + Opname: "distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_length = &Opcode { + Opname: "length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_normalize = &Opcode { + Opname: "normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_distance = &Opcode { + Opname: "fast_distance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p0'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p1'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_length = &Opcode { + Opname: "fast_length", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_fast_normalize = &Opcode { + Opname: "fast_normalize", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_bitselect = &Opcode { + Opname: "bitselect", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_select = &Opcode { + Opname: "select", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'a'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'b'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'c'", + Quantifier: "", + }, + }, + } + OpenCLStd_vloadn = &Opcode { + Opname: "vloadn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstoren = &Opcode { + Opname: "vstoren", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vload_half = &Opcode { + Opname: "vload_half", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vload_halfn = &Opcode { + Opname: "vload_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_half = &Opcode { + Opname: "vstore_half", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_half_r = &Opcode { + Opname: "vstore_half_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_halfn = &Opcode { + Opname: "vstore_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstore_halfn_r = &Opcode { + Opname: "vstore_halfn_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_vloada_halfn = &Opcode { + Opname: "vloada_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'n'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstorea_halfn = &Opcode { + Opname: "vstorea_halfn", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + }, + } + OpenCLStd_vstorea_halfn_r = &Opcode { + Opname: "vstorea_halfn_r", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'data'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'p'", + Quantifier: "", + }, + Operand { + Kind: OperandKindFPRoundingMode, + Name: "'mode'", + Quantifier: "", + }, + }, + } + OpenCLStd_shuffle = &Opcode { + Opname: "shuffle", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'shuffle mask'", + Quantifier: "", + }, + }, + } + OpenCLStd_shuffle2 = &Opcode { + Opname: "shuffle2", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'x'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'y'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'shuffle mask'", + Quantifier: "", + }, + }, + } + OpenCLStd_printf = &Opcode { + Opname: "printf", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'format'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'additional arguments'", + Quantifier: "*", + }, + }, + } + OpenCLStd_prefetch = &Opcode { + Opname: "prefetch", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'ptr'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'num elements'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugInfoNone = &Opcode { + Opname: "DebugInfoNone", + Operands: []Operand { + }, + } + OpenCLDebugInfo100_DebugCompilationUnit = &Opcode { + Opname: "DebugCompilationUnit", + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Version'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'DWARF Version'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindSourceLanguage, + Name: "'Language'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeBasic = &Opcode { + Opname: "DebugTypeBasic", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugBaseTypeAttributeEncoding, + Name: "'Encoding'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypePointer = &Opcode { + Opname: "DebugTypePointer", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindStorageClass, + Name: "'Storage Class'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeQualifier = &Opcode { + Opname: "DebugTypeQualifier", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugTypeQualifier, + Name: "'Type Qualifier'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeArray = &Opcode { + Opname: "DebugTypeArray", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Component Counts'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeVector = &Opcode { + Opname: "DebugTypeVector", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Component Count'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypedef = &Opcode { + Opname: "DebugTypedef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Base Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeFunction = &Opcode { + Opname: "DebugTypeFunction", + Operands: []Operand { + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Return Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parameter Types'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeEnum = &Opcode { + Opname: "DebugTypeEnum", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Underlying Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindPairIdRefIdRef, + Name: "'Value, Name, Value, Name, ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeComposite = &Opcode { + Opname: "DebugTypeComposite", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugCompositeType, + Name: "'Tag'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Members'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeMember = &Opcode { + Opname: "DebugTypeMember", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugTypeInheritance = &Opcode { + Opname: "DebugTypeInheritance", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Child'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Offset'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Size'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypePtrToMember = &Opcode { + Opname: "DebugTypePtrToMember", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Member Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplate = &Opcode { + Opname: "DebugTypeTemplate", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Target'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parameters'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateParameter = &Opcode { + Opname: "DebugTypeTemplateParameter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Actual Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateTemplateParameter = &Opcode { + Opname: "DebugTypeTemplateTemplateParameter", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Template Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugTypeTemplateParameterPack = &Opcode { + Opname: "DebugTypeTemplateParameterPack", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Template Parameters'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugGlobalVariable = &Opcode { + Opname: "DebugGlobalVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Static Member Declaration'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugFunctionDeclaration = &Opcode { + Opname: "DebugFunctionDeclaration", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugFunction = &Opcode { + Opname: "DebugFunction", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Linkage Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Scope Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Function'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Declaration'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLexicalBlock = &Opcode { + Opname: "DebugLexicalBlock", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLexicalBlockDiscriminator = &Opcode { + Opname: "DebugLexicalBlockDiscriminator", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Discriminator'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugScope = &Opcode { + Opname: "DebugScope", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Scope'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined At'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugNoScope = &Opcode { + Opname: "DebugNoScope", + Operands: []Operand { + }, + } + OpenCLDebugInfo100_DebugInlinedAt = &Opcode { + Opname: "DebugInlinedAt", + Operands: []Operand { + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Scope'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugLocalVariable = &Opcode { + Opname: "DebugLocalVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Type'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugInfoFlags, + Name: "'Flags'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Arg Number'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugInlinedVariable = &Opcode { + Opname: "DebugInlinedVariable", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Inlined'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugDeclare = &Opcode { + Opname: "DebugDeclare", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Local Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Expression'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugValue = &Opcode { + Opname: "DebugValue", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Local Variable'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Expression'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Indexes'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugOperation = &Opcode { + Opname: "DebugOperation", + Operands: []Operand { + Operand { + Kind: OperandKindDebugOperation, + Name: "'OpCode'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Operands ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugExpression = &Opcode { + Opname: "DebugExpression", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Operands ...'", + Quantifier: "*", + }, + }, + } + OpenCLDebugInfo100_DebugMacroDef = &Opcode { + Opname: "DebugMacroDef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Value'", + Quantifier: "?", + }, + }, + } + OpenCLDebugInfo100_DebugMacroUndef = &Opcode { + Opname: "DebugMacroUndef", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Macro'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugImportedEntity = &Opcode { + Opname: "DebugImportedEntity", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'Name'", + Quantifier: "", + }, + Operand { + Kind: OperandKindDebugImportedEntity, + Name: "'Tag'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Source'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Entity'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Line'", + Quantifier: "", + }, + Operand { + Kind: OperandKindLiteralInteger, + Name: "'Column'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Parent'", + Quantifier: "", + }, + }, + } + OpenCLDebugInfo100_DebugSource = &Opcode { + Opname: "DebugSource", + Operands: []Operand { + Operand { + Kind: OperandKindIdRef, + Name: "'File'", + Quantifier: "", + }, + Operand { + Kind: OperandKindIdRef, + Name: "'Text'", + Quantifier: "?", + }, + }, + } + + + OperandKindImageOperands = &OperandKind { + Kind: "ImageOperands", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Bias", + Value: 0x0001, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Lod", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Grad", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "ConstOffset", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Offset", + Value: 0x0010, + Capabilities: []string{"ImageGatherExtended",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "ConstOffsets", + Value: 0x0020, + Capabilities: []string{"ImageGatherExtended",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Sample", + Value: 0x0040, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "MinLod", + Value: 0x0080, + Capabilities: []string{"MinLod",}, + Parameters: []Parameter{{OperandKindIdRef, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "MakeTexelAvailable", + Value: 0x0100, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeTexelAvailableKHR", + Value: 0x0100, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeTexelVisible", + Value: 0x0200, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeTexelVisibleKHR", + Value: 0x0200, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonPrivateTexel", + Value: 0x0400, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonPrivateTexelKHR", + Value: 0x0400, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VolatileTexel", + Value: 0x0800, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VolatileTexelKHR", + Value: 0x0800, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "SignExtend", + Value: 0x1000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "ZeroExtend", + Value: 0x2000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPFastMathMode = &OperandKind { + Kind: "FPFastMathMode", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NotNaN", + Value: 0x0001, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NotInf", + Value: 0x0002, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NSZ", + Value: 0x0004, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "AllowRecip", + Value: 0x0008, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Fast", + Value: 0x0010, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSelectionControl = &OperandKind { + Kind: "SelectionControl", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Flatten", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DontFlatten", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindLoopControl = &OperandKind { + Kind: "LoopControl", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Unroll", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DontUnroll", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DependencyInfinite", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "DependencyLength", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "MinIterations", + Value: 0x0010, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "MaxIterations", + Value: 0x0020, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "IterationMultiple", + Value: 0x0040, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "PeelCount", + Value: 0x0080, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "PartialCount", + Value: 0x0100, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "1.4", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFunctionControl = &OperandKind { + Kind: "FunctionControl", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Inline", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DontInline", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Pure", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Const", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindMemorySemantics = &OperandKind { + Kind: "MemorySemantics", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Relaxed", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Acquire", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Release", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "AcquireRelease", + Value: 0x0008, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SequentiallyConsistent", + Value: 0x0010, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UniformMemory", + Value: 0x0040, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupMemory", + Value: 0x0080, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WorkgroupMemory", + Value: 0x0100, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CrossWorkgroupMemory", + Value: 0x0200, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "AtomicCounterMemory", + Value: 0x0400, + Capabilities: []string{"AtomicStorage",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageMemory", + Value: 0x0800, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OutputMemory", + Value: 0x1000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "OutputMemoryKHR", + Value: 0x1000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeAvailable", + Value: 0x2000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeAvailableKHR", + Value: 0x2000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeVisible", + Value: 0x4000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakeVisibleKHR", + Value: 0x4000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "Volatile", + Value: 0x8000, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindMemoryAccess = &OperandKind { + Kind: "MemoryAccess", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Volatile", + Value: 0x0001, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Aligned", + Value: 0x0002, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Nontemporal", + Value: 0x0004, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "MakePointerAvailable", + Value: 0x0008, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakePointerAvailableKHR", + Value: 0x0008, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakePointerVisible", + Value: 0x0010, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "MakePointerVisibleKHR", + Value: 0x0010, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{{OperandKindIdScope, ""},}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonPrivatePointer", + Value: 0x0020, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonPrivatePointerKHR", + Value: 0x0020, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindKernelProfilingInfo = &OperandKind { + Kind: "KernelProfilingInfo", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0x0000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CmdExecTime", + Value: 0x0001, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRayFlags = &OperandKind { + Kind: "RayFlags", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "NoneKHR", + Value: 0x0000, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OpaqueKHR", + Value: 0x0001, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NoOpaqueKHR", + Value: 0x0002, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TerminateOnFirstHitKHR", + Value: 0x0004, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SkipClosestHitShaderKHR", + Value: 0x0008, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CullBackFacingTrianglesKHR", + Value: 0x0010, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CullFrontFacingTrianglesKHR", + Value: 0x0020, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CullOpaqueKHR", + Value: 0x0040, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CullNoOpaqueKHR", + Value: 0x0080, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SkipTrianglesKHR", + Value: 0x0100, + Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SkipAABBsKHR", + Value: 0x0200, + Capabilities: []string{"RayTraversalPrimitiveCullingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSourceLanguage = &OperandKind { + Kind: "SourceLanguage", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Unknown", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ESSL", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GLSL", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OpenCL_C", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OpenCL_CPP", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "HLSL", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindExecutionModel = &OperandKind { + Kind: "ExecutionModel", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Vertex", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessellationControl", + Value: 1, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessellationEvaluation", + Value: 2, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Geometry", + Value: 3, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Fragment", + Value: 4, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GLCompute", + Value: 5, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Kernel", + Value: 6, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TaskNV", + Value: 5267, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshNV", + Value: 5268, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayGenerationNV", + Value: 5313, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayGenerationKHR", + Value: 5313, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IntersectionNV", + Value: 5314, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IntersectionKHR", + Value: 5314, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AnyHitNV", + Value: 5315, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "AnyHitKHR", + Value: 5315, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClosestHitNV", + Value: 5316, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClosestHitKHR", + Value: 5316, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MissNV", + Value: 5317, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MissKHR", + Value: 5317, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CallableNV", + Value: 5318, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CallableKHR", + Value: 5318, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindAddressingModel = &OperandKind { + Kind: "AddressingModel", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Logical", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Physical32", + Value: 1, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Physical64", + Value: 2, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PhysicalStorageBuffer64", + Value: 5348, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "PhysicalStorageBuffer64EXT", + Value: 5348, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindMemoryModel = &OperandKind { + Kind: "MemoryModel", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Simple", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GLSL450", + Value: 1, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OpenCL", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Vulkan", + Value: 3, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VulkanKHR", + Value: 3, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindExecutionMode = &OperandKind { + Kind: "ExecutionMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Invocations", + Value: 0, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Number of <>'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "SpacingEqual", + Value: 1, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SpacingFractionalEven", + Value: 2, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SpacingFractionalOdd", + Value: 3, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "VertexOrderCw", + Value: 4, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "VertexOrderCcw", + Value: 5, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PixelCenterInteger", + Value: 6, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OriginUpperLeft", + Value: 7, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OriginLowerLeft", + Value: 8, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "EarlyFragmentTests", + Value: 9, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PointMode", + Value: 10, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Xfb", + Value: 11, + Capabilities: []string{"TransformFeedback",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DepthReplacing", + Value: 12, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DepthGreater", + Value: 14, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DepthLess", + Value: 15, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DepthUnchanged", + Value: 16, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "LocalSize", + Value: 17, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "LocalSizeHint", + Value: 18, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'x size'"},{OperandKindLiteralInteger, "'y size'"},{OperandKindLiteralInteger, "'z size'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "InputPoints", + Value: 19, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InputLines", + Value: 20, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InputLinesAdjacency", + Value: 21, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Triangles", + Value: 22, + Capabilities: []string{"Geometry","Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InputTrianglesAdjacency", + Value: 23, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Quads", + Value: 24, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Isolines", + Value: 25, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OutputVertices", + Value: 26, + Capabilities: []string{"Geometry","Tessellation","MeshShadingNV",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Vertex count'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "OutputPoints", + Value: 27, + Capabilities: []string{"Geometry","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OutputLineStrip", + Value: 28, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "OutputTriangleStrip", + Value: 29, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "VecTypeHint", + Value: 30, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Vector type'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "ContractionOff", + Value: 31, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Initializer", + Value: 33, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "Finalizer", + Value: 34, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "SubgroupSize", + Value: 35, + Capabilities: []string{"SubgroupDispatch",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Subgroup Size'"},}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "SubgroupsPerWorkgroup", + Value: 36, + Capabilities: []string{"SubgroupDispatch",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Subgroups Per Workgroup'"},}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "SubgroupsPerWorkgroupId", + Value: 37, + Capabilities: []string{"SubgroupDispatch",}, + Parameters: []Parameter{{OperandKindIdRef, "'Subgroups Per Workgroup'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "LocalSizeId", + Value: 38, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, "'x size'"},{OperandKindIdRef, "'y size'"},{OperandKindIdRef, "'z size'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "LocalSizeHintId", + Value: 39, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindIdRef, "'Local Size Hint'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "PostDepthCoverage", + Value: 4446, + Capabilities: []string{"SampleMaskPostDepthCoverage",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DenormPreserve", + Value: 4459, + Capabilities: []string{"DenormPreserve",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "DenormFlushToZero", + Value: 4460, + Capabilities: []string{"DenormFlushToZero",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "SignedZeroInfNanPreserve", + Value: 4461, + Capabilities: []string{"SignedZeroInfNanPreserve",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RoundingModeRTE", + Value: 4462, + Capabilities: []string{"RoundingModeRTE",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RoundingModeRTZ", + Value: 4463, + Capabilities: []string{"RoundingModeRTZ",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Target Width'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "StencilRefReplacingEXT", + Value: 5027, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OutputLinesNV", + Value: 5269, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OutputPrimitivesNV", + Value: 5270, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Primitive count'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "DerivativeGroupQuadsNV", + Value: 5289, + Capabilities: []string{"ComputeDerivativeGroupQuadsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DerivativeGroupLinearNV", + Value: 5290, + Capabilities: []string{"ComputeDerivativeGroupLinearNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OutputTrianglesNV", + Value: 5298, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PixelInterlockOrderedEXT", + Value: 5366, + Capabilities: []string{"FragmentShaderPixelInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PixelInterlockUnorderedEXT", + Value: 5367, + Capabilities: []string{"FragmentShaderPixelInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleInterlockOrderedEXT", + Value: 5368, + Capabilities: []string{"FragmentShaderSampleInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleInterlockUnorderedEXT", + Value: 5369, + Capabilities: []string{"FragmentShaderSampleInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShadingRateInterlockOrderedEXT", + Value: 5370, + Capabilities: []string{"FragmentShaderShadingRateInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShadingRateInterlockUnorderedEXT", + Value: 5371, + Capabilities: []string{"FragmentShaderShadingRateInterlockEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindStorageClass = &OperandKind { + Kind: "StorageClass", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "UniformConstant", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Input", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Uniform", + Value: 2, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Output", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Workgroup", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CrossWorkgroup", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Private", + Value: 6, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Function", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Generic", + Value: 8, + Capabilities: []string{"GenericPointer",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PushConstant", + Value: 9, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "AtomicCounter", + Value: 10, + Capabilities: []string{"AtomicStorage",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Image", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StorageBuffer", + Value: 12, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "CallableDataNV", + Value: 5328, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CallableDataKHR", + Value: 5328, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingCallableDataNV", + Value: 5329, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingCallableDataKHR", + Value: 5329, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayPayloadNV", + Value: 5338, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayPayloadKHR", + Value: 5338, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitAttributeNV", + Value: 5339, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitAttributeKHR", + Value: 5339, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingRayPayloadNV", + Value: 5342, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingRayPayloadKHR", + Value: 5342, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderRecordBufferNV", + Value: 5343, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderRecordBufferKHR", + Value: 5343, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PhysicalStorageBuffer", + Value: 5349, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "PhysicalStorageBufferEXT", + Value: 5349, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDim = &OperandKind { + Kind: "Dim", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "1D", + Value: 0, + Capabilities: []string{"Sampled1D","Image1D",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "2D", + Value: 1, + Capabilities: []string{"Shader","Kernel","ImageMSArray",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "3D", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Cube", + Value: 3, + Capabilities: []string{"Shader","ImageCubeArray",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rect", + Value: 4, + Capabilities: []string{"SampledRect","ImageRect",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Buffer", + Value: 5, + Capabilities: []string{"SampledBuffer","ImageBuffer",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubpassData", + Value: 6, + Capabilities: []string{"InputAttachment",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSamplerAddressingMode = &OperandKind { + Kind: "SamplerAddressingMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "None", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ClampToEdge", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Clamp", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Repeat", + Value: 3, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RepeatMirrored", + Value: 4, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindSamplerFilterMode = &OperandKind { + Kind: "SamplerFilterMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Nearest", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Linear", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindImageFormat = &OperandKind { + Kind: "ImageFormat", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Unknown", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba32f", + Value: 1, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba16f", + Value: 2, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R32f", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba8", + Value: 4, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba8Snorm", + Value: 5, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg32f", + Value: 6, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg16f", + Value: 7, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R11fG11fB10f", + Value: 8, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R16f", + Value: 9, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba16", + Value: 10, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgb10A2", + Value: 11, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg16", + Value: 12, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg8", + Value: 13, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R16", + Value: 14, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R8", + Value: 15, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba16Snorm", + Value: 16, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg16Snorm", + Value: 17, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg8Snorm", + Value: 18, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R16Snorm", + Value: 19, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R8Snorm", + Value: 20, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba32i", + Value: 21, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba16i", + Value: 22, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba8i", + Value: 23, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R32i", + Value: 24, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg32i", + Value: 25, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg16i", + Value: 26, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg8i", + Value: 27, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R16i", + Value: 28, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R8i", + Value: 29, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba32ui", + Value: 30, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba16ui", + Value: 31, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgba8ui", + Value: 32, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R32ui", + Value: 33, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rgb10a2ui", + Value: 34, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg32ui", + Value: 35, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg16ui", + Value: 36, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rg8ui", + Value: 37, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R16ui", + Value: 38, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "R8ui", + Value: 39, + Capabilities: []string{"StorageImageExtendedFormats",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindImageChannelOrder = &OperandKind { + Kind: "ImageChannelOrder", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "R", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "A", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RG", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RA", + Value: 3, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RGB", + Value: 4, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RGBA", + Value: 5, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "BGRA", + Value: 6, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ARGB", + Value: 7, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Intensity", + Value: 8, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Luminance", + Value: 9, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Rx", + Value: 10, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RGx", + Value: 11, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RGBx", + Value: 12, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Depth", + Value: 13, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DepthStencil", + Value: 14, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "sRGB", + Value: 15, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "sRGBx", + Value: 16, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "sRGBA", + Value: 17, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "sBGRA", + Value: 18, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ABGR", + Value: 19, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindImageChannelDataType = &OperandKind { + Kind: "ImageChannelDataType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "SnormInt8", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SnormInt16", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnormInt8", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnormInt16", + Value: 3, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnormShort565", + Value: 4, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnormShort555", + Value: 5, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnormInt101010", + Value: 6, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SignedInt8", + Value: 7, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SignedInt16", + Value: 8, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SignedInt32", + Value: 9, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnsignedInt8", + Value: 10, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnsignedInt16", + Value: 11, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnsignedInt32", + Value: 12, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "HalfFloat", + Value: 13, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Float", + Value: 14, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnormInt24", + Value: 15, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnormInt101010_2", + Value: 16, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFPRoundingMode = &OperandKind { + Kind: "FPRoundingMode", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RTE", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RTZ", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RTP", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RTN", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindLinkageType = &OperandKind { + Kind: "LinkageType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Export", + Value: 0, + Capabilities: []string{"Linkage",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Import", + Value: 1, + Capabilities: []string{"Linkage",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindAccessQualifier = &OperandKind { + Kind: "AccessQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "ReadOnly", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WriteOnly", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ReadWrite", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindFunctionParameterAttribute = &OperandKind { + Kind: "FunctionParameterAttribute", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Zext", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Sext", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ByVal", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Sret", + Value: 3, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NoAlias", + Value: 4, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NoCapture", + Value: 5, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NoWrite", + Value: 6, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NoReadWrite", + Value: 7, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDecoration = &OperandKind { + Kind: "Decoration", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RelaxedPrecision", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SpecId", + Value: 1, + Capabilities: []string{"Shader","Kernel",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Specialization Constant ID'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "Block", + Value: 2, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "BufferBlock", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RowMajor", + Value: 4, + Capabilities: []string{"Matrix",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ColMajor", + Value: 5, + Capabilities: []string{"Matrix",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ArrayStride", + Value: 6, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Array Stride'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "MatrixStride", + Value: 7, + Capabilities: []string{"Matrix",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Matrix Stride'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "GLSLShared", + Value: 8, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GLSLPacked", + Value: 9, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CPacked", + Value: 10, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "BuiltIn", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindBuiltIn, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "NoPerspective", + Value: 13, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Flat", + Value: 14, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Patch", + Value: 15, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Centroid", + Value: 16, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Sample", + Value: 17, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Invariant", + Value: 18, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Restrict", + Value: 19, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Aliased", + Value: 20, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Volatile", + Value: 21, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Constant", + Value: 22, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Coherent", + Value: 23, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NonWritable", + Value: 24, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NonReadable", + Value: 25, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Uniform", + Value: 26, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UniformId", + Value: 27, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindIdScope, "'Execution'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "SaturatedConversion", + Value: 28, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Stream", + Value: 29, + Capabilities: []string{"GeometryStreams",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Stream Number'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "Location", + Value: 30, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Location'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "Component", + Value: 31, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Component'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "Index", + Value: 32, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Index'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "Binding", + Value: 33, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Binding Point'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "DescriptorSet", + Value: 34, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Descriptor Set'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "Offset", + Value: 35, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Byte Offset'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "XfbBuffer", + Value: 36, + Capabilities: []string{"TransformFeedback",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'XFB Buffer Number'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "XfbStride", + Value: 37, + Capabilities: []string{"TransformFeedback",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'XFB Stride'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "FuncParamAttr", + Value: 38, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindFunctionParameterAttribute, "'Function Parameter Attribute'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "FPRoundingMode", + Value: 39, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindFPRoundingMode, "'Floating-Point Rounding Mode'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "FPFastMathMode", + Value: 40, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindFPFastMathMode, "'Fast-Math Mode'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "LinkageAttributes", + Value: 41, + Capabilities: []string{"Linkage",}, + Parameters: []Parameter{{OperandKindLiteralString, "'Name'"},{OperandKindLinkageType, "'Linkage Type'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "NoContraction", + Value: 42, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InputAttachmentIndex", + Value: 43, + Capabilities: []string{"InputAttachment",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Attachment Index'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "Alignment", + Value: 44, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Alignment'"},}, + Version: "", + }, + Enumerant{ + Enumerant: "MaxByteOffset", + Value: 45, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Max Byte Offset'"},}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "AlignmentId", + Value: 46, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{{OperandKindIdRef, "'Alignment'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "MaxByteOffsetId", + Value: 47, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{{OperandKindIdRef, "'Max Byte Offset'"},}, + Version: "1.2", + }, + Enumerant{ + Enumerant: "NoSignedWrap", + Value: 4469, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "NoUnsignedWrap", + Value: 4470, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "ExplicitInterpAMD", + Value: 4999, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "OverrideCoverageNV", + Value: 5248, + Capabilities: []string{"SampleMaskOverrideCoverageNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PassthroughNV", + Value: 5250, + Capabilities: []string{"GeometryShaderPassthroughNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ViewportRelativeNV", + Value: 5252, + Capabilities: []string{"ShaderViewportMaskNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SecondaryViewportRelativeNV", + Value: 5256, + Capabilities: []string{"ShaderStereoViewNV",}, + Parameters: []Parameter{{OperandKindLiteralInteger, "'Offset'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerPrimitiveNV", + Value: 5271, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerViewNV", + Value: 5272, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerTaskNV", + Value: 5273, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerVertexNV", + Value: 5285, + Capabilities: []string{"FragmentBarycentricNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "NonUniform", + Value: 5300, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "NonUniformEXT", + Value: 5300, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "RestrictPointer", + Value: 5355, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "RestrictPointerEXT", + Value: 5355, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "AliasedPointer", + Value: 5356, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "AliasedPointerEXT", + Value: 5356, + Capabilities: []string{"PhysicalStorageBufferAddresses",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "CounterBuffer", + Value: 5634, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, "'Counter Buffer'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "HlslCounterBufferGOOGLE", + Value: 5634, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindIdRef, "'Counter Buffer'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "UserSemantic", + Value: 5635, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralString, "'Semantic'"},}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "HlslSemanticGOOGLE", + Value: 5635, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralString, "'Semantic'"},}, + Version: "None", + }, + Enumerant{ + Enumerant: "UserTypeGOOGLE", + Value: 5636, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralString, "'User Type'"},}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindBuiltIn = &OperandKind { + Kind: "BuiltIn", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Position", + Value: 0, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PointSize", + Value: 1, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ClipDistance", + Value: 3, + Capabilities: []string{"ClipDistance",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CullDistance", + Value: 4, + Capabilities: []string{"CullDistance",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "VertexId", + Value: 5, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InstanceId", + Value: 6, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PrimitiveId", + Value: 7, + Capabilities: []string{"Geometry","Tessellation","RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InvocationId", + Value: 8, + Capabilities: []string{"Geometry","Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Layer", + Value: 9, + Capabilities: []string{"Geometry","ShaderLayer","ShaderViewportIndexLayerEXT",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ViewportIndex", + Value: 10, + Capabilities: []string{"MultiViewport","ShaderViewportIndex","ShaderViewportIndexLayerEXT",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessLevelOuter", + Value: 11, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessLevelInner", + Value: 12, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessCoord", + Value: 13, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PatchVertices", + Value: 14, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FragCoord", + Value: 15, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PointCoord", + Value: 16, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FrontFacing", + Value: 17, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampleId", + Value: 18, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SamplePosition", + Value: 19, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampleMask", + Value: 20, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FragDepth", + Value: 22, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "HelperInvocation", + Value: 23, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NumWorkgroups", + Value: 24, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WorkgroupSize", + Value: 25, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WorkgroupId", + Value: 26, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "LocalInvocationId", + Value: 27, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GlobalInvocationId", + Value: 28, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "LocalInvocationIndex", + Value: 29, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WorkDim", + Value: 30, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GlobalSize", + Value: 31, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "EnqueuedWorkgroupSize", + Value: 32, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GlobalOffset", + Value: 33, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GlobalLinearId", + Value: 34, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupSize", + Value: 36, + Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupMaxSize", + Value: 37, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NumSubgroups", + Value: 38, + Capabilities: []string{"Kernel","GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "NumEnqueuedSubgroups", + Value: 39, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupId", + Value: 40, + Capabilities: []string{"Kernel","GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupLocalInvocationId", + Value: 41, + Capabilities: []string{"Kernel","GroupNonUniform","SubgroupBallotKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "VertexIndex", + Value: 42, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InstanceIndex", + Value: 43, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupEqMask", + Value: 4416, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupGeMask", + Value: 4417, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupGtMask", + Value: 4418, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupLeMask", + Value: 4419, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupLtMask", + Value: 4420, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupEqMaskKHR", + Value: 4416, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupGeMaskKHR", + Value: 4417, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupGtMaskKHR", + Value: 4418, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupLeMaskKHR", + Value: 4419, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupLtMaskKHR", + Value: 4420, + Capabilities: []string{"SubgroupBallotKHR","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "BaseVertex", + Value: 4424, + Capabilities: []string{"DrawParameters",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "BaseInstance", + Value: 4425, + Capabilities: []string{"DrawParameters",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "DrawIndex", + Value: 4426, + Capabilities: []string{"DrawParameters","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "DeviceIndex", + Value: 4438, + Capabilities: []string{"DeviceGroup",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "ViewIndex", + Value: 4440, + Capabilities: []string{"MultiView",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "BaryCoordNoPerspAMD", + Value: 4992, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordNoPerspCentroidAMD", + Value: 4993, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordNoPerspSampleAMD", + Value: 4994, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordSmoothAMD", + Value: 4995, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordSmoothCentroidAMD", + Value: 4996, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordSmoothSampleAMD", + Value: 4997, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordPullModelAMD", + Value: 4998, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragStencilRefEXT", + Value: 5014, + Capabilities: []string{"StencilExportEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ViewportMaskNV", + Value: 5253, + Capabilities: []string{"ShaderViewportMaskNV","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SecondaryPositionNV", + Value: 5257, + Capabilities: []string{"ShaderStereoViewNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SecondaryViewportMaskNV", + Value: 5258, + Capabilities: []string{"ShaderStereoViewNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PositionPerViewNV", + Value: 5261, + Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ViewportMaskPerViewNV", + Value: 5262, + Capabilities: []string{"PerViewAttributesNV","MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FullyCoveredEXT", + Value: 5264, + Capabilities: []string{"FragmentFullyCoveredEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "TaskCountNV", + Value: 5274, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrimitiveCountNV", + Value: 5275, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PrimitiveIndicesNV", + Value: 5276, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ClipDistancePerViewNV", + Value: 5277, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CullDistancePerViewNV", + Value: 5278, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LayerPerViewNV", + Value: 5279, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshViewCountNV", + Value: 5280, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshViewIndicesNV", + Value: 5281, + Capabilities: []string{"MeshShadingNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordNV", + Value: 5286, + Capabilities: []string{"FragmentBarycentricNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "BaryCoordNoPerspNV", + Value: 5287, + Capabilities: []string{"FragmentBarycentricNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragSizeEXT", + Value: 5292, + Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentSizeNV", + Value: 5292, + Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragInvocationCountEXT", + Value: 5293, + Capabilities: []string{"FragmentDensityEXT","ShadingRateNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "InvocationsPerPixelNV", + Value: 5293, + Capabilities: []string{"ShadingRateNV","FragmentDensityEXT",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LaunchIdNV", + Value: 5319, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LaunchIdKHR", + Value: 5319, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LaunchSizeNV", + Value: 5320, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "LaunchSizeKHR", + Value: 5320, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldRayOriginNV", + Value: 5321, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldRayOriginKHR", + Value: 5321, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldRayDirectionNV", + Value: 5322, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldRayDirectionKHR", + Value: 5322, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectRayOriginNV", + Value: 5323, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectRayOriginKHR", + Value: 5323, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectRayDirectionNV", + Value: 5324, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectRayDirectionKHR", + Value: 5324, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTminNV", + Value: 5325, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTminKHR", + Value: 5325, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTmaxNV", + Value: 5326, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTmaxKHR", + Value: 5326, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "InstanceCustomIndexNV", + Value: 5327, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "InstanceCustomIndexKHR", + Value: 5327, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectToWorldNV", + Value: 5330, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ObjectToWorldKHR", + Value: 5330, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldToObjectNV", + Value: 5331, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WorldToObjectKHR", + Value: 5331, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitTNV", + Value: 5332, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitTKHR", + Value: 5332, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitKindNV", + Value: 5333, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "HitKindKHR", + Value: 5333, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingRayFlagsNV", + Value: 5351, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IncomingRayFlagsKHR", + Value: 5351, + Capabilities: []string{"RayTracingNV","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayGeometryIndexKHR", + Value: 5352, + Capabilities: []string{"RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WarpsPerSMNV", + Value: 5374, + Capabilities: []string{"ShaderSMBuiltinsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SMCountNV", + Value: 5375, + Capabilities: []string{"ShaderSMBuiltinsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "WarpIDNV", + Value: 5376, + Capabilities: []string{"ShaderSMBuiltinsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SMIDNV", + Value: 5377, + Capabilities: []string{"ShaderSMBuiltinsNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindScope = &OperandKind { + Kind: "Scope", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "CrossDevice", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Device", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Workgroup", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Subgroup", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Invocation", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "QueueFamily", + Value: 5, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "QueueFamilyKHR", + Value: 5, + Capabilities: []string{"VulkanMemoryModel",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "ShaderCallKHR", + Value: 6, + Capabilities: []string{"RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindGroupOperation = &OperandKind { + Kind: "GroupOperation", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Reduce", + Value: 0, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InclusiveScan", + Value: 1, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ExclusiveScan", + Value: 2, + Capabilities: []string{"Kernel","GroupNonUniformArithmetic","GroupNonUniformBallot",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ClusteredReduce", + Value: 3, + Capabilities: []string{"GroupNonUniformClustered",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "PartitionedReduceNV", + Value: 6, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PartitionedInclusiveScanNV", + Value: 7, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PartitionedExclusiveScanNV", + Value: 8, + Capabilities: []string{"GroupNonUniformPartitionedNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindKernelEnqueueFlags = &OperandKind { + Kind: "KernelEnqueueFlags", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "NoWait", + Value: 0, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WaitKernel", + Value: 1, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "WaitWorkGroup", + Value: 2, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindCapability = &OperandKind { + Kind: "Capability", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Matrix", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Shader", + Value: 1, + Capabilities: []string{"Matrix",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Geometry", + Value: 2, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Tessellation", + Value: 3, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Addresses", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Linkage", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Kernel", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Vector16", + Value: 7, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Float16Buffer", + Value: 8, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Float16", + Value: 9, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Float64", + Value: 10, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Int64", + Value: 11, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Int64Atomics", + Value: 12, + Capabilities: []string{"Int64",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageBasic", + Value: 13, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageReadWrite", + Value: 14, + Capabilities: []string{"ImageBasic",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageMipmap", + Value: 15, + Capabilities: []string{"ImageBasic",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Pipes", + Value: 17, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Groups", + Value: 18, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DeviceEnqueue", + Value: 19, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "LiteralSampler", + Value: 20, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "AtomicStorage", + Value: 21, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Int16", + Value: 22, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TessellationPointSize", + Value: 23, + Capabilities: []string{"Tessellation",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GeometryPointSize", + Value: 24, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageGatherExtended", + Value: 25, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StorageImageMultisample", + Value: 27, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UniformBufferArrayDynamicIndexing", + Value: 28, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampledImageArrayDynamicIndexing", + Value: 29, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StorageBufferArrayDynamicIndexing", + Value: 30, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StorageImageArrayDynamicIndexing", + Value: 31, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ClipDistance", + Value: 32, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "CullDistance", + Value: 33, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageCubeArray", + Value: 34, + Capabilities: []string{"SampledCubeArray",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampleRateShading", + Value: 35, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageRect", + Value: 36, + Capabilities: []string{"SampledRect",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampledRect", + Value: 37, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GenericPointer", + Value: 38, + Capabilities: []string{"Addresses",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Int8", + Value: 39, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InputAttachment", + Value: 40, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SparseResidency", + Value: 41, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "MinLod", + Value: 42, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Sampled1D", + Value: 43, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Image1D", + Value: 44, + Capabilities: []string{"Sampled1D",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampledCubeArray", + Value: 45, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SampledBuffer", + Value: 46, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageBuffer", + Value: 47, + Capabilities: []string{"SampledBuffer",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageMSArray", + Value: 48, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StorageImageExtendedFormats", + Value: 49, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImageQuery", + Value: 50, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "DerivativeControl", + Value: 51, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "InterpolationFunction", + Value: 52, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "TransformFeedback", + Value: 53, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "GeometryStreams", + Value: 54, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StorageImageReadWithoutFormat", + Value: 55, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StorageImageWriteWithoutFormat", + Value: 56, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "MultiViewport", + Value: 57, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SubgroupDispatch", + Value: 58, + Capabilities: []string{"DeviceEnqueue",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "NamedBarrier", + Value: 59, + Capabilities: []string{"Kernel",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "PipeStorage", + Value: 60, + Capabilities: []string{"Pipes",}, + Parameters: []Parameter{}, + Version: "1.1", + }, + Enumerant{ + Enumerant: "GroupNonUniform", + Value: 61, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformVote", + Value: 62, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformArithmetic", + Value: 63, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformBallot", + Value: 64, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformShuffle", + Value: 65, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformShuffleRelative", + Value: 66, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformClustered", + Value: 67, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "GroupNonUniformQuad", + Value: 68, + Capabilities: []string{"GroupNonUniform",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "ShaderLayer", + Value: 69, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "ShaderViewportIndex", + Value: 70, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "SubgroupBallotKHR", + Value: 4423, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DrawParameters", + Value: 4427, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "SubgroupVoteKHR", + Value: 4431, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StorageBuffer16BitAccess", + Value: 4433, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "StorageUniformBufferBlock16", + Value: 4433, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "UniformAndStorageBuffer16BitAccess", + Value: 4434, + Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "StorageUniform16", + Value: 4434, + Capabilities: []string{"StorageBuffer16BitAccess","StorageUniformBufferBlock16",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "StoragePushConstant16", + Value: 4435, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "StorageInputOutput16", + Value: 4436, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "DeviceGroup", + Value: 4437, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "MultiView", + Value: 4439, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "VariablePointersStorageBuffer", + Value: 4441, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "VariablePointers", + Value: 4442, + Capabilities: []string{"VariablePointersStorageBuffer",}, + Parameters: []Parameter{}, + Version: "1.3", + }, + Enumerant{ + Enumerant: "AtomicStorageOps", + Value: 4445, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleMaskPostDepthCoverage", + Value: 4447, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StorageBuffer8BitAccess", + Value: 4448, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformAndStorageBuffer8BitAccess", + Value: 4449, + Capabilities: []string{"StorageBuffer8BitAccess",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StoragePushConstant8", + Value: 4450, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "DenormPreserve", + Value: 4464, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "DenormFlushToZero", + Value: 4465, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "SignedZeroInfNanPreserve", + Value: 4466, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RoundingModeRTE", + Value: 4467, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RoundingModeRTZ", + Value: 4468, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.4", + }, + Enumerant{ + Enumerant: "RayQueryProvisionalKHR", + Value: 4471, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTraversalPrimitiveCullingProvisionalKHR", + Value: 4478, + Capabilities: []string{"RayQueryProvisionalKHR","RayTracingProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "Float16ImageAMD", + Value: 5008, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ImageGatherBiasLodAMD", + Value: 5009, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentMaskAMD", + Value: 5010, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "StencilExportEXT", + Value: 5013, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ImageReadWriteLodAMD", + Value: 5015, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderClockKHR", + Value: 5055, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SampleMaskOverrideCoverageNV", + Value: 5249, + Capabilities: []string{"SampleRateShading",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GeometryShaderPassthroughNV", + Value: 5251, + Capabilities: []string{"Geometry",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderViewportIndexLayerEXT", + Value: 5254, + Capabilities: []string{"MultiViewport",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderViewportIndexLayerNV", + Value: 5254, + Capabilities: []string{"MultiViewport",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderViewportMaskNV", + Value: 5255, + Capabilities: []string{"ShaderViewportIndexLayerNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderStereoViewNV", + Value: 5259, + Capabilities: []string{"ShaderViewportMaskNV",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "PerViewAttributesNV", + Value: 5260, + Capabilities: []string{"MultiView",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentFullyCoveredEXT", + Value: 5265, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "MeshShadingNV", + Value: 5266, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ImageFootprintNV", + Value: 5282, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentBarycentricNV", + Value: 5284, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ComputeDerivativeGroupQuadsNV", + Value: 5288, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentDensityEXT", + Value: 5291, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShadingRateNV", + Value: 5291, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "GroupNonUniformPartitionedNV", + Value: 5297, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderNonUniform", + Value: 5301, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "ShaderNonUniformEXT", + Value: 5301, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "RuntimeDescriptorArray", + Value: 5302, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "RuntimeDescriptorArrayEXT", + Value: 5302, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "InputAttachmentArrayDynamicIndexing", + Value: 5303, + Capabilities: []string{"InputAttachment",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "InputAttachmentArrayDynamicIndexingEXT", + Value: 5303, + Capabilities: []string{"InputAttachment",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformTexelBufferArrayDynamicIndexing", + Value: 5304, + Capabilities: []string{"SampledBuffer",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformTexelBufferArrayDynamicIndexingEXT", + Value: 5304, + Capabilities: []string{"SampledBuffer",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageTexelBufferArrayDynamicIndexing", + Value: 5305, + Capabilities: []string{"ImageBuffer",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageTexelBufferArrayDynamicIndexingEXT", + Value: 5305, + Capabilities: []string{"ImageBuffer",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformBufferArrayNonUniformIndexing", + Value: 5306, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformBufferArrayNonUniformIndexingEXT", + Value: 5306, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "SampledImageArrayNonUniformIndexing", + Value: 5307, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "SampledImageArrayNonUniformIndexingEXT", + Value: 5307, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageBufferArrayNonUniformIndexing", + Value: 5308, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageBufferArrayNonUniformIndexingEXT", + Value: 5308, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageImageArrayNonUniformIndexing", + Value: 5309, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageImageArrayNonUniformIndexingEXT", + Value: 5309, + Capabilities: []string{"ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "InputAttachmentArrayNonUniformIndexing", + Value: 5310, + Capabilities: []string{"InputAttachment","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "InputAttachmentArrayNonUniformIndexingEXT", + Value: 5310, + Capabilities: []string{"InputAttachment","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformTexelBufferArrayNonUniformIndexing", + Value: 5311, + Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "UniformTexelBufferArrayNonUniformIndexingEXT", + Value: 5311, + Capabilities: []string{"SampledBuffer","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageTexelBufferArrayNonUniformIndexing", + Value: 5312, + Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "StorageTexelBufferArrayNonUniformIndexingEXT", + Value: 5312, + Capabilities: []string{"ImageBuffer","ShaderNonUniform",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "RayTracingNV", + Value: 5340, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "VulkanMemoryModel", + Value: 5345, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VulkanMemoryModelKHR", + Value: 5345, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VulkanMemoryModelDeviceScope", + Value: 5346, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "VulkanMemoryModelDeviceScopeKHR", + Value: 5346, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "PhysicalStorageBufferAddresses", + Value: 5347, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "PhysicalStorageBufferAddressesEXT", + Value: 5347, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "1.5", + }, + Enumerant{ + Enumerant: "ComputeDerivativeGroupLinearNV", + Value: 5350, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "RayTracingProvisionalKHR", + Value: 5353, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "CooperativeMatrixNV", + Value: 5357, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentShaderSampleInterlockEXT", + Value: 5363, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentShaderShadingRateInterlockEXT", + Value: 5372, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "ShaderSMBuiltinsNV", + Value: 5373, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "FragmentShaderPixelInterlockEXT", + Value: 5378, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "DemoteToHelperInvocationEXT", + Value: 5379, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupShuffleINTEL", + Value: 5568, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupBufferBlockIOINTEL", + Value: 5569, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupImageBlockIOINTEL", + Value: 5570, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupImageMediaBlockIOINTEL", + Value: 5579, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "IntegerFunctions2INTEL", + Value: 5584, + Capabilities: []string{"Shader",}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupAvcMotionEstimationINTEL", + Value: 5696, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupAvcMotionEstimationIntraINTEL", + Value: 5697, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + Enumerant{ + Enumerant: "SubgroupAvcMotionEstimationChromaINTEL", + Value: 5698, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "None", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRayQueryIntersection = &OperandKind { + Kind: "RayQueryIntersection", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RayQueryCandidateIntersectionKHR", + Value: 0, + Capabilities: []string{"RayQueryProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RayQueryCommittedIntersectionKHR", + Value: 1, + Capabilities: []string{"RayQueryProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRayQueryCommittedIntersectionType = &OperandKind { + Kind: "RayQueryCommittedIntersectionType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RayQueryCommittedIntersectionNoneKHR", + Value: 0, + Capabilities: []string{"RayQueryProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RayQueryCommittedIntersectionTriangleKHR", + Value: 1, + Capabilities: []string{"RayQueryProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RayQueryCommittedIntersectionGeneratedKHR", + Value: 2, + Capabilities: []string{"RayQueryProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindRayQueryCandidateIntersectionType = &OperandKind { + Kind: "RayQueryCandidateIntersectionType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "RayQueryCandidateIntersectionTriangleKHR", + Value: 0, + Capabilities: []string{"RayQueryProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RayQueryCandidateIntersectionAABBKHR", + Value: 1, + Capabilities: []string{"RayQueryProvisionalKHR",}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindIdResultType = &OperandKind { + Kind: "IdResultType", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdResult = &OperandKind { + Kind: "IdResult", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdMemorySemantics = &OperandKind { + Kind: "IdMemorySemantics", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdScope = &OperandKind { + Kind: "IdScope", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindIdRef = &OperandKind { + Kind: "IdRef", + Category: "Id", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralInteger = &OperandKind { + Kind: "LiteralInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralString = &OperandKind { + Kind: "LiteralString", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralContextDependentNumber = &OperandKind { + Kind: "LiteralContextDependentNumber", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralExtInstInteger = &OperandKind { + Kind: "LiteralExtInstInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindLiteralSpecConstantOpInteger = &OperandKind { + Kind: "LiteralSpecConstantOpInteger", + Category: "Literal", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {}, + } + OperandKindPairLiteralIntegerIdRef = &OperandKind { + Kind: "PairLiteralIntegerIdRef", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindLiteralInteger,OperandKindIdRef,}, + } + OperandKindPairIdRefLiteralInteger = &OperandKind { + Kind: "PairIdRefLiteralInteger", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindIdRef,OperandKindLiteralInteger,}, + } + OperandKindPairIdRefIdRef = &OperandKind { + Kind: "PairIdRefIdRef", + Category: "Composite", + Enumerants: []Enumerant { + }, + Bases: []*OperandKind {OperandKindIdRef,OperandKindIdRef,}, + } + OperandKindDebugInfoFlags = &OperandKind { + Kind: "DebugInfoFlags", + Category: "BitEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "FlagIsProtected", + Value: 0x01, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsPrivate", + Value: 0x02, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsPublic", + Value: 0x03, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsLocal", + Value: 0x04, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsDefinition", + Value: 0x08, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagFwdDecl", + Value: 0x10, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagArtificial", + Value: 0x20, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagExplicit", + Value: 0x40, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagPrototyped", + Value: 0x80, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagObjectPointer", + Value: 0x100, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagStaticMember", + Value: 0x200, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIndirectVariable", + Value: 0x400, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagLValueReference", + Value: 0x800, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagRValueReference", + Value: 0x1000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsOptimized", + Value: 0x2000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagIsEnumClass", + Value: 0x4000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagTypePassByValue", + Value: 0x8000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "FlagTypePassByReference", + Value: 0x10000, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugBaseTypeAttributeEncoding = &OperandKind { + Kind: "DebugBaseTypeAttributeEncoding", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Unspecified", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Address", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Boolean", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Float", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Signed", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "SignedChar", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Unsigned", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "UnsignedChar", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugCompositeType = &OperandKind { + Kind: "DebugCompositeType", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Class", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Structure", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Union", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugTypeQualifier = &OperandKind { + Kind: "DebugTypeQualifier", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "ConstType", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "VolatileType", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "RestrictType", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "AtomicType", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugOperation = &OperandKind { + Kind: "DebugOperation", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "Deref", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Plus", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Minus", + Value: 2, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "PlusUconst", + Value: 3, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "BitPiece", + Value: 4, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},{OperandKindLiteralInteger, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Swap", + Value: 5, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Xderef", + Value: 6, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "StackValue", + Value: 7, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "Constu", + Value: 8, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},}, + Version: "", + }, + Enumerant{ + Enumerant: "Fragment", + Value: 9, + Capabilities: []string{}, + Parameters: []Parameter{{OperandKindLiteralInteger, ""},{OperandKindLiteralInteger, ""},}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + OperandKindDebugImportedEntity = &OperandKind { + Kind: "DebugImportedEntity", + Category: "ValueEnum", + Enumerants: []Enumerant { + Enumerant{ + Enumerant: "ImportedModule", + Value: 0, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + Enumerant{ + Enumerant: "ImportedDeclaration", + Value: 1, + Capabilities: []string{}, + Parameters: []Parameter{}, + Version: "", + }, + }, + Bases: []*OperandKind {}, + } + +) diff --git a/third_party/spirv-tools/utils/vscode/src/schema/schema.go.tmpl b/third_party/spirv-tools/utils/vscode/src/schema/schema.go.tmpl new file mode 100644 index 0000000..987fcd0 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/schema/schema.go.tmpl @@ -0,0 +1,156 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Generated by {{GenerateArguments}} +// Do not modify this file directly. + +package schema + +// Opcode holds information about a specific SPIR-V opcode. +type Opcode struct { + Opname string + Class string + Opcode int + Operands []Operand +} + +// Operand contains information about a logical operand for an instruction. +type Operand struct { + Kind *OperandKind + Name string + Quantifier Quantifier +} + +// OperandKind contains information about a specific operand kind. +type OperandKind struct { + Category OperandCategory + Kind string + Enumerants []Enumerant + Bases []*OperandKind +} + +// Enumerant contains information about an enumerant in an enum. +type Enumerant struct { + Enumerant string + Value interface{} + Capabilities []string + Parameters []Parameter + Version string +} + +// Parameter contains information about a logical parameter for an enumerant. +type Parameter struct { + Kind *OperandKind + Name string +} + +// Quantifier indicates the number of times the quantified term may appear. +type Quantifier string + +const ( + // Once indicates the quantified term may appear exactly once. + Once Quantifier = "" + + // ZeroOrOnce indicates the quantified term may appear zero or one + // time; an optional term. + ZeroOrOnce Quantifier = "?" + + // ZeroOrMany indicates the quantified term may appear any number of + // times. + ZeroOrMany Quantifier = "*" +) + +// OperandCategory is an enumerator that groups operand kinds. +type OperandCategory string + +const ( + // OperandCategoryBitEnum describes an operand kind where its value is a + // mask, which is formed by combining the bits specified as enumerants in an + // enum. + OperandCategoryBitEnum = "BitEnum" + + // OperandCategoryValueEnum describes an operand kind where its value is an + // enumerant from an enum. + OperandCategoryValueEnum = "ValueEnum" + + // OperandCategoryID describes and operand kind where its value is an + // definition or reference. + OperandCategoryID = "Id" + + // OperandCategoryLiteral describes and operand kind where its value is an + // literal number or string. + OperandCategoryLiteral = "Literal" + + // OperandCategoryComposite describes and operand kind where its value is + // composed from operand values from the above categories. + OperandCategoryComposite = "Composite" +) + +// OpcodeMap is a map of opcode name to Opcode type. +type OpcodeMap map[string]*Opcode + +var ( + // Opcodes is a map of opcode name to Opcode description. + Opcodes = OpcodeMap {•{{range $i := .SPIRV.Instructions}} + "{{$i.Opname}}": {{Title $i.Opname}},{{end}} + } + + // ExtOpcodes is a map of extension name to Opcode description list. + ExtOpcodes = map[string]OpcodeMap {•{{range $ext := .Extensions}} + "{{$ext.Name}}": {•{{range $i := $ext.Instructions}} + "{{$i.Opname}}": {{Global $ext.Name}}_{{$i.Opname}},{{end}} + },{{end}} + } + +{{range $i := .SPIRV.Instructions}} {{Title $i.Opname}} = &Opcode { + Opname: "{{$i.Opname}}", + Class: "{{$i.Class}}", + Opcode: {{$i.Opcode}}, + Operands: []Operand {•{{range $i := $i.Operands}} + Operand { + Kind: OperandKind{{$i.Kind}}, + Name: "{{Replace $i.Name "\n" " "}}", + Quantifier: "{{$i.Quantifier}}", + }, {{end}} + }, + } +{{end}} +{{range $ext := .Extensions}}{{range $i := $ext.Instructions}} {{Global $ext.Name}}_{{$i.Opname}} = &Opcode { + Opname: "{{$i.Opname}}", + Operands: []Operand {•{{range $i := $i.Operands}} + Operand { + Kind: OperandKind{{$i.Kind}}, + Name: "{{Replace $i.Name "\n" " "}}", + Quantifier: "{{$i.Quantifier}}", + }, {{end}} + }, + } +{{end}}{{end}} + +{{range $o := .All.OperandKinds}} OperandKind{{$o.Kind}} = &OperandKind { + Kind: "{{$o.Kind}}", + Category: "{{$o.Category}}", + Enumerants: []Enumerant {•{{range $e := $o.Enumerants}} + Enumerant{ + Enumerant: "{{$e.Enumerant}}", + Value: {{$e.Value}}, + Capabilities: []string{•{{range $c := $e.Capabilities}}"{{$c}}",{{end}}•}, + Parameters: []Parameter{•{{range $p := $e.Parameters}}{•OperandKind{{$p.Kind}}, "{{$p.Name}}"•},{{end}}•}, + Version: "{{$e.Version}}", + },{{end}} + }, + Bases: []*OperandKind {•{{range $b := $o.Bases}}OperandKind{{$b}},{{end}}•}, + } +{{end}} +) diff --git a/third_party/spirv-tools/utils/vscode/src/tools/gen-grammar.go b/third_party/spirv-tools/utils/vscode/src/tools/gen-grammar.go new file mode 100644 index 0000000..200f695 --- /dev/null +++ b/third_party/spirv-tools/utils/vscode/src/tools/gen-grammar.go @@ -0,0 +1,239 @@ +// Copyright (C) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// gen-grammar generates the spirv.json grammar file from the official SPIR-V +// grammar JSON file. +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + "strings" + "text/template" + + "github.com/pkg/errors" + + "../grammar" +) + +type grammarDefinition struct { + name string + url string +} + +var ( + spirvGrammar = grammarDefinition{ + name: "SPIR-V", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json", + } + + extensionGrammars = []grammarDefinition{ + { + name: "GLSL.std.450", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.glsl.std.450.grammar.json", + }, { + name: "OpenCL.std", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/extinst.opencl.std.100.grammar.json", + }, { + name: "OpenCL.DebugInfo.100", + url: "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Tools/master/source/extinst.opencl.debuginfo.100.grammar.json", + }, + } + + templatePath = flag.String("template", "", "Path to input template file (required)") + outputPath = flag.String("out", "", "Path to output generated file (required)") + cachePath = flag.String("cache", "", "Cache directory for downloaded files (optional)") + + thisDir = func() string { + _, file, _, _ := runtime.Caller(1) + return filepath.Dir(file) + }() +) + +func main() { + flag.Parse() + if *templatePath == "" || *outputPath == "" { + flag.Usage() + os.Exit(1) + } + if err := run(); err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} + +func run() error { + tf, err := ioutil.ReadFile(*templatePath) + if err != nil { + return errors.Wrap(err, "Could not open template file") + } + + type extension struct { + grammar.Root + Name string + } + + args := struct { + SPIRV grammar.Root + Extensions []extension + All grammar.Root // Combination of SPIRV + Extensions + }{} + + if args.SPIRV, err = parseGrammar(spirvGrammar); err != nil { + return errors.Wrap(err, "Failed to parse SPIR-V grammar file") + } + args.All.Instructions = append(args.All.Instructions, args.SPIRV.Instructions...) + args.All.OperandKinds = append(args.All.OperandKinds, args.SPIRV.OperandKinds...) + + for _, ext := range extensionGrammars { + root, err := parseGrammar(ext) + if err != nil { + return errors.Wrap(err, "Failed to parse extension grammar file") + } + args.Extensions = append(args.Extensions, extension{Root: root, Name: ext.name}) + args.All.Instructions = append(args.All.Instructions, root.Instructions...) + args.All.OperandKinds = append(args.All.OperandKinds, root.OperandKinds...) + } + + t, err := template.New("tmpl"). + Funcs(template.FuncMap{ + "GenerateArguments": func() string { + relPath := func(path string) string { + rel, err := filepath.Rel(thisDir, path) + if err != nil { + return path + } + return rel + } + escape := func(str string) string { + return strings.ReplaceAll(str, `\`, `/`) + } + args := []string{ + "--template=" + escape(relPath(*templatePath)), + "--out=" + escape(relPath(*outputPath)), + } + return "gen-grammar.go " + strings.Join(args, " ") + }, + "OperandKindsMatch": func(k grammar.OperandKind) string { + sb := strings.Builder{} + for i, e := range k.Enumerants { + if i > 0 { + sb.WriteString("|") + } + sb.WriteString(e.Enumerant) + } + return sb.String() + }, + "AllExtOpcodes": func() string { + sb := strings.Builder{} + for _, ext := range args.Extensions { + for _, inst := range ext.Root.Instructions { + if sb.Len() > 0 { + sb.WriteString("|") + } + sb.WriteString(inst.Opname) + } + } + return sb.String() + }, + "Title": strings.Title, + "Replace": strings.ReplaceAll, + "Global": func(s string) string { + return strings.ReplaceAll(strings.Title(s), ".", "") + }, + }).Parse(string(tf)) + if err != nil { + return errors.Wrap(err, "Failed to parse template") + } + + buf := bytes.Buffer{} + if err := t.Execute(&buf, args); err != nil { + return errors.Wrap(err, "Failed to execute template") + } + + out := buf.String() + out = strings.ReplaceAll(out, "•", "") + + if err := ioutil.WriteFile(*outputPath, []byte(out), 0777); err != nil { + return errors.Wrap(err, "Failed to write output file") + } + + return nil +} + +// parseGrammar downloads (or loads from the cache) the grammar file and returns +// the parsed grammar.Root. +func parseGrammar(def grammarDefinition) (grammar.Root, error) { + file, err := getOrDownload(def.name, def.url) + if err != nil { + return grammar.Root{}, errors.Wrap(err, "Failed to load grammar file") + } + + g := grammar.Root{} + if err := json.NewDecoder(bytes.NewReader(file)).Decode(&g); err != nil { + return grammar.Root{}, errors.Wrap(err, "Failed to parse grammar file") + } + + return g, nil +} + +// getOrDownload loads the specific file from the cache, or downloads the file +// from the given url. +func getOrDownload(name, url string) ([]byte, error) { + if *cachePath != "" { + if err := os.MkdirAll(*cachePath, 0777); err == nil { + path := filepath.Join(*cachePath, name) + if isFile(path) { + return ioutil.ReadFile(path) + } + } + } + resp, err := http.Get(url) + if err != nil { + return nil, err + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + if *cachePath != "" { + ioutil.WriteFile(filepath.Join(*cachePath, name), data, 0777) + } + return data, nil +} + +// isFile returns true if path is a file. +func isFile(path string) bool { + s, err := os.Stat(path) + if err != nil { + return false + } + return !s.IsDir() +} + +// isDir returns true if path is a directory. +func isDir(path string) bool { + s, err := os.Stat(path) + if err != nil { + return false + } + return s.IsDir() +} -- 2.30.2

(args)...); + var.self = id; + return var; + } + + template + T &get(uint32_t id) + { + return variant_get(ir.ids[id]); + } + + template + T *maybe_get(uint32_t id) + { + if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } + + template + const T &get(uint32_t id) const + { + return variant_get(ir.ids[id]); + } + + template + const T *maybe_get(uint32_t id) const + { + if (ir.ids[id].get_type() == T::type) + return &get(id); + else + return nullptr; + } + + // This must be an ordered data structure so we always pick the same type aliases. + SmallVector global_struct_cache; + SmallVector> forward_pointer_fixups; + + bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; + bool variable_storage_is_aliased(const SPIRVariable &v) const; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/spirv_reflect.cpp b/third_party/spirv-cross/spirv_reflect.cpp new file mode 100644 index 0000000..dbe837f --- /dev/null +++ b/third_party/spirv-cross/spirv_reflect.cpp @@ -0,0 +1,699 @@ +/* + * Copyright 2018-2020 Bradley Austin Davis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "spirv_reflect.hpp" +#include "spirv_glsl.hpp" +#include + +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +namespace simple_json +{ +enum class Type +{ + Object, + Array, +}; + +using State = std::pair; +using Stack = std::stack; + +class Stream +{ + Stack stack; + StringStream<> buffer; + uint32_t indent{ 0 }; + char current_locale_radix_character = '.'; + +public: + void set_current_locale_radix_character(char c) + { + current_locale_radix_character = c; + } + + void begin_json_object(); + void end_json_object(); + void emit_json_key(const std::string &key); + void emit_json_key_value(const std::string &key, const std::string &value); + void emit_json_key_value(const std::string &key, bool value); + void emit_json_key_value(const std::string &key, uint32_t value); + void emit_json_key_value(const std::string &key, int32_t value); + void emit_json_key_value(const std::string &key, float value); + void emit_json_key_object(const std::string &key); + void emit_json_key_array(const std::string &key); + + void begin_json_array(); + void end_json_array(); + void emit_json_array_value(const std::string &value); + void emit_json_array_value(uint32_t value); + void emit_json_array_value(bool value); + + std::string str() const + { + return buffer.str(); + } + +private: + inline void statement_indent() + { + for (uint32_t i = 0; i < indent; i++) + buffer << " "; + } + + template + inline void statement_inner(T &&t) + { + buffer << std::forward(t); + } + + template + inline void statement_inner(T &&t, Ts &&... ts) + { + buffer << std::forward(t); + statement_inner(std::forward(ts)...); + } + + template + inline void statement(Ts &&... ts) + { + statement_indent(); + statement_inner(std::forward(ts)...); + buffer << '\n'; + } + + template + void statement_no_return(Ts &&... ts) + { + statement_indent(); + statement_inner(std::forward(ts)...); + } +}; +} // namespace simple_json + +using namespace simple_json; + +// Hackery to emit JSON without using nlohmann/json C++ library (which requires a +// higher level of compiler compliance than is required by SPIRV-Cross +void Stream::begin_json_array() +{ + if (!stack.empty() && stack.top().second) + { + statement_inner(",\n"); + } + statement("["); + ++indent; + stack.emplace(Type::Array, false); +} + +void Stream::end_json_array() +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + { + statement_inner("\n"); + } + --indent; + statement_no_return("]"); + stack.pop(); + if (!stack.empty()) + { + stack.top().second = true; + } +} + +void Stream::emit_json_array_value(const std::string &value) +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + + if (stack.top().second) + statement_inner(",\n"); + + statement_no_return("\"", value, "\""); + stack.top().second = true; +} + +void Stream::emit_json_array_value(uint32_t value) +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + statement_inner(",\n"); + statement_no_return(std::to_string(value)); + stack.top().second = true; +} + +void Stream::emit_json_array_value(bool value) +{ + if (stack.empty() || stack.top().first != Type::Array) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + statement_inner(",\n"); + statement_no_return(value ? "true" : "false"); + stack.top().second = true; +} + +void Stream::begin_json_object() +{ + if (!stack.empty() && stack.top().second) + { + statement_inner(",\n"); + } + statement("{"); + ++indent; + stack.emplace(Type::Object, false); +} + +void Stream::end_json_object() +{ + if (stack.empty() || stack.top().first != Type::Object) + SPIRV_CROSS_THROW("Invalid JSON state"); + if (stack.top().second) + { + statement_inner("\n"); + } + --indent; + statement_no_return("}"); + stack.pop(); + if (!stack.empty()) + { + stack.top().second = true; + } +} + +void Stream::emit_json_key(const std::string &key) +{ + if (stack.empty() || stack.top().first != Type::Object) + SPIRV_CROSS_THROW("Invalid JSON state"); + + if (stack.top().second) + statement_inner(",\n"); + statement_no_return("\"", key, "\" : "); + stack.top().second = true; +} + +void Stream::emit_json_key_value(const std::string &key, const std::string &value) +{ + emit_json_key(key); + statement_inner("\"", value, "\""); +} + +void Stream::emit_json_key_value(const std::string &key, uint32_t value) +{ + emit_json_key(key); + statement_inner(value); +} + +void Stream::emit_json_key_value(const std::string &key, int32_t value) +{ + emit_json_key(key); + statement_inner(value); +} + +void Stream::emit_json_key_value(const std::string &key, float value) +{ + emit_json_key(key); + statement_inner(convert_to_string(value, current_locale_radix_character)); +} + +void Stream::emit_json_key_value(const std::string &key, bool value) +{ + emit_json_key(key); + statement_inner(value ? "true" : "false"); +} + +void Stream::emit_json_key_object(const std::string &key) +{ + emit_json_key(key); + statement_inner("{\n"); + ++indent; + stack.emplace(Type::Object, false); +} + +void Stream::emit_json_key_array(const std::string &key) +{ + emit_json_key(key); + statement_inner("[\n"); + ++indent; + stack.emplace(Type::Array, false); +} + +void CompilerReflection::set_format(const std::string &format) +{ + if (format != "json") + { + SPIRV_CROSS_THROW("Unsupported format"); + } +} + +string CompilerReflection::compile() +{ + json_stream = std::make_shared(); + json_stream->set_current_locale_radix_character(current_locale_radix_character); + json_stream->begin_json_object(); + reorder_type_alias(); + emit_entry_points(); + emit_types(); + emit_resources(); + emit_specialization_constants(); + json_stream->end_json_object(); + return json_stream->str(); +} + +static bool naturally_emit_type(const SPIRType &type) +{ + return type.basetype == SPIRType::Struct && !type.pointer && type.array.empty(); +} + +bool CompilerReflection::type_is_reference(const SPIRType &type) const +{ + // Physical pointers and arrays of physical pointers need to refer to the pointee's type. + return type_is_top_level_physical_pointer(type) || + (!type.array.empty() && type_is_top_level_physical_pointer(get(type.parent_type))); +} + +void CompilerReflection::emit_types() +{ + bool emitted_open_tag = false; + + SmallVector physical_pointee_types; + + // If we have physical pointers or arrays of physical pointers, it's also helpful to emit the pointee type + // and chain the type hierarchy. For POD, arrays can emit the entire type in-place. + ir.for_each_typed_id([&](uint32_t self, SPIRType &type) { + if (naturally_emit_type(type)) + { + emit_type(self, emitted_open_tag); + } + else if (type_is_reference(type)) + { + if (!naturally_emit_type(this->get(type.parent_type)) && + find(physical_pointee_types.begin(), physical_pointee_types.end(), type.parent_type) == + physical_pointee_types.end()) + { + physical_pointee_types.push_back(type.parent_type); + } + } + }); + + for (uint32_t pointee_type : physical_pointee_types) + emit_type(pointee_type, emitted_open_tag); + + if (emitted_open_tag) + { + json_stream->end_json_object(); + } +} + +void CompilerReflection::emit_type(uint32_t type_id, bool &emitted_open_tag) +{ + auto &type = get(type_id); + auto name = type_to_glsl(type); + + if (!emitted_open_tag) + { + json_stream->emit_json_key_object("types"); + emitted_open_tag = true; + } + json_stream->emit_json_key_object("_" + std::to_string(type_id)); + json_stream->emit_json_key_value("name", name); + + if (type_is_top_level_physical_pointer(type)) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(type.parent_type)); + json_stream->emit_json_key_value("physical_pointer", true); + } + else if (!type.array.empty()) + { + emit_type_array(type); + json_stream->emit_json_key_value("type", "_" + std::to_string(type.parent_type)); + json_stream->emit_json_key_value("array_stride", get_decoration(type_id, DecorationArrayStride)); + } + else + { + json_stream->emit_json_key_array("members"); + // FIXME ideally we'd like to emit the size of a structure as a + // convenience to people parsing the reflected JSON. The problem + // is that there's no implicit size for a type. It's final size + // will be determined by the top level declaration in which it's + // included. So there might be one size for the struct if it's + // included in a std140 uniform block and another if it's included + // in a std430 uniform block. + // The solution is to include *all* potential sizes as a map of + // layout type name to integer, but that will probably require + // some additional logic being written in this class, or in the + // parent CompilerGLSL class. + auto size = type.member_types.size(); + for (uint32_t i = 0; i < size; ++i) + { + emit_type_member(type, i); + } + json_stream->end_json_array(); + } + + json_stream->end_json_object(); +} + +void CompilerReflection::emit_type_member(const SPIRType &type, uint32_t index) +{ + auto &membertype = get(type.member_types[index]); + json_stream->begin_json_object(); + auto name = to_member_name(type, index); + // FIXME we'd like to emit the offset of each member, but such offsets are + // context dependent. See the comment above regarding structure sizes + json_stream->emit_json_key_value("name", name); + + if (type_is_reference(membertype)) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.parent_type)); + } + else if (membertype.basetype == SPIRType::Struct) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.self)); + } + else + { + json_stream->emit_json_key_value("type", type_to_glsl(membertype)); + } + emit_type_member_qualifiers(type, index); + json_stream->end_json_object(); +} + +void CompilerReflection::emit_type_array(const SPIRType &type) +{ + if (!type_is_top_level_physical_pointer(type) && !type.array.empty()) + { + json_stream->emit_json_key_array("array"); + // Note that we emit the zeros here as a means of identifying + // unbounded arrays. This is necessary as otherwise there would + // be no way of differentiating between float[4] and float[4][] + for (const auto &value : type.array) + json_stream->emit_json_array_value(value); + json_stream->end_json_array(); + + json_stream->emit_json_key_array("array_size_is_literal"); + for (const auto &value : type.array_size_literal) + json_stream->emit_json_array_value(value); + json_stream->end_json_array(); + } +} + +void CompilerReflection::emit_type_member_qualifiers(const SPIRType &type, uint32_t index) +{ + auto &membertype = get(type.member_types[index]); + emit_type_array(membertype); + auto &memb = ir.meta[type.self].members; + if (index < memb.size()) + { + auto &dec = memb[index]; + if (dec.decoration_flags.get(DecorationLocation)) + json_stream->emit_json_key_value("location", dec.location); + if (dec.decoration_flags.get(DecorationOffset)) + json_stream->emit_json_key_value("offset", dec.offset); + + // Array stride is a property of the array type, not the struct. + if (has_decoration(type.member_types[index], DecorationArrayStride)) + json_stream->emit_json_key_value("array_stride", + get_decoration(type.member_types[index], DecorationArrayStride)); + + if (dec.decoration_flags.get(DecorationMatrixStride)) + json_stream->emit_json_key_value("matrix_stride", dec.matrix_stride); + if (dec.decoration_flags.get(DecorationRowMajor)) + json_stream->emit_json_key_value("row_major", true); + + if (type_is_top_level_physical_pointer(membertype)) + json_stream->emit_json_key_value("physical_pointer", true); + } +} + +string CompilerReflection::execution_model_to_str(spv::ExecutionModel model) +{ + switch (model) + { + case ExecutionModelVertex: + return "vert"; + case ExecutionModelTessellationControl: + return "tesc"; + case ExecutionModelTessellationEvaluation: + return "tese"; + case ExecutionModelGeometry: + return "geom"; + case ExecutionModelFragment: + return "frag"; + case ExecutionModelGLCompute: + return "comp"; + case ExecutionModelRayGenerationNV: + return "rgen"; + case ExecutionModelIntersectionNV: + return "rint"; + case ExecutionModelAnyHitNV: + return "rahit"; + case ExecutionModelClosestHitNV: + return "rchit"; + case ExecutionModelMissNV: + return "rmiss"; + case ExecutionModelCallableNV: + return "rcall"; + default: + return "???"; + } +} + +// FIXME include things like the local_size dimensions, geometry output vertex count, etc +void CompilerReflection::emit_entry_points() +{ + auto entries = get_entry_points_and_stages(); + if (!entries.empty()) + { + // Needed to make output deterministic. + sort(begin(entries), end(entries), [](const EntryPoint &a, const EntryPoint &b) -> bool { + if (a.execution_model < b.execution_model) + return true; + else if (a.execution_model > b.execution_model) + return false; + else + return a.name < b.name; + }); + + json_stream->emit_json_key_array("entryPoints"); + for (auto &e : entries) + { + json_stream->begin_json_object(); + json_stream->emit_json_key_value("name", e.name); + json_stream->emit_json_key_value("mode", execution_model_to_str(e.execution_model)); + if (e.execution_model == ExecutionModelGLCompute) + { + const auto &spv_entry = get_entry_point(e.name, e.execution_model); + + SpecializationConstant spec_x, spec_y, spec_z; + get_work_group_size_specialization_constants(spec_x, spec_y, spec_z); + + json_stream->emit_json_key_array("workgroup_size"); + json_stream->emit_json_array_value(spec_x.id != ID(0) ? spec_x.constant_id : + spv_entry.workgroup_size.x); + json_stream->emit_json_array_value(spec_y.id != ID(0) ? spec_y.constant_id : + spv_entry.workgroup_size.y); + json_stream->emit_json_array_value(spec_z.id != ID(0) ? spec_z.constant_id : + spv_entry.workgroup_size.z); + json_stream->end_json_array(); + + json_stream->emit_json_key_array("workgroup_size_is_spec_constant_id"); + json_stream->emit_json_array_value(spec_x.id != ID(0)); + json_stream->emit_json_array_value(spec_y.id != ID(0)); + json_stream->emit_json_array_value(spec_z.id != ID(0)); + json_stream->end_json_array(); + } + json_stream->end_json_object(); + } + json_stream->end_json_array(); + } +} + +void CompilerReflection::emit_resources() +{ + auto res = get_shader_resources(); + emit_resources("subpass_inputs", res.subpass_inputs); + emit_resources("inputs", res.stage_inputs); + emit_resources("outputs", res.stage_outputs); + emit_resources("textures", res.sampled_images); + emit_resources("separate_images", res.separate_images); + emit_resources("separate_samplers", res.separate_samplers); + emit_resources("images", res.storage_images); + emit_resources("ssbos", res.storage_buffers); + emit_resources("ubos", res.uniform_buffers); + emit_resources("push_constants", res.push_constant_buffers); + emit_resources("counters", res.atomic_counters); + emit_resources("acceleration_structures", res.acceleration_structures); +} + +void CompilerReflection::emit_resources(const char *tag, const SmallVector &resources) +{ + if (resources.empty()) + { + return; + } + + json_stream->emit_json_key_array(tag); + for (auto &res : resources) + { + auto &type = get_type(res.type_id); + auto typeflags = ir.meta[type.self].decoration.decoration_flags; + auto &mask = get_decoration_bitset(res.id); + + // If we don't have a name, use the fallback for the type instead of the variable + // for SSBOs and UBOs since those are the only meaningful names to use externally. + // Push constant blocks are still accessed by name and not block name, even though they are technically Blocks. + bool is_push_constant = get_storage_class(res.id) == StorageClassPushConstant; + bool is_block = get_decoration_bitset(type.self).get(DecorationBlock) || + get_decoration_bitset(type.self).get(DecorationBufferBlock); + + ID fallback_id = !is_push_constant && is_block ? ID(res.base_type_id) : ID(res.id); + + json_stream->begin_json_object(); + + if (type.basetype == SPIRType::Struct) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(res.base_type_id)); + } + else + { + json_stream->emit_json_key_value("type", type_to_glsl(type)); + } + + json_stream->emit_json_key_value("name", !res.name.empty() ? res.name : get_fallback_name(fallback_id)); + { + bool ssbo_block = type.storage == StorageClassStorageBuffer || + (type.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock)); + if (ssbo_block) + { + auto buffer_flags = get_buffer_block_flags(res.id); + if (buffer_flags.get(DecorationNonReadable)) + json_stream->emit_json_key_value("writeonly", true); + if (buffer_flags.get(DecorationNonWritable)) + json_stream->emit_json_key_value("readonly", true); + if (buffer_flags.get(DecorationRestrict)) + json_stream->emit_json_key_value("restrict", true); + if (buffer_flags.get(DecorationCoherent)) + json_stream->emit_json_key_value("coherent", true); + } + } + + emit_type_array(type); + + { + bool is_sized_block = is_block && (get_storage_class(res.id) == StorageClassUniform || + get_storage_class(res.id) == StorageClassUniformConstant || + get_storage_class(res.id) == StorageClassStorageBuffer); + if (is_sized_block) + { + uint32_t block_size = uint32_t(get_declared_struct_size(get_type(res.base_type_id))); + json_stream->emit_json_key_value("block_size", block_size); + } + } + + if (type.storage == StorageClassPushConstant) + json_stream->emit_json_key_value("push_constant", true); + if (mask.get(DecorationLocation)) + json_stream->emit_json_key_value("location", get_decoration(res.id, DecorationLocation)); + if (mask.get(DecorationRowMajor)) + json_stream->emit_json_key_value("row_major", true); + if (mask.get(DecorationColMajor)) + json_stream->emit_json_key_value("column_major", true); + if (mask.get(DecorationIndex)) + json_stream->emit_json_key_value("index", get_decoration(res.id, DecorationIndex)); + if (type.storage != StorageClassPushConstant && mask.get(DecorationDescriptorSet)) + json_stream->emit_json_key_value("set", get_decoration(res.id, DecorationDescriptorSet)); + if (mask.get(DecorationBinding)) + json_stream->emit_json_key_value("binding", get_decoration(res.id, DecorationBinding)); + if (mask.get(DecorationInputAttachmentIndex)) + json_stream->emit_json_key_value("input_attachment_index", + get_decoration(res.id, DecorationInputAttachmentIndex)); + if (mask.get(DecorationOffset)) + json_stream->emit_json_key_value("offset", get_decoration(res.id, DecorationOffset)); + + // For images, the type itself adds a layout qualifer. + // Only emit the format for storage images. + if (type.basetype == SPIRType::Image && type.image.sampled == 2) + { + const char *fmt = format_to_glsl(type.image.format); + if (fmt != nullptr) + json_stream->emit_json_key_value("format", std::string(fmt)); + } + json_stream->end_json_object(); + } + json_stream->end_json_array(); +} + +void CompilerReflection::emit_specialization_constants() +{ + auto specialization_constants = get_specialization_constants(); + if (specialization_constants.empty()) + return; + + json_stream->emit_json_key_array("specialization_constants"); + for (const auto &spec_const : specialization_constants) + { + auto &c = get(spec_const.id); + auto type = get(c.constant_type); + json_stream->begin_json_object(); + json_stream->emit_json_key_value("name", get_name(spec_const.id)); + json_stream->emit_json_key_value("id", spec_const.constant_id); + json_stream->emit_json_key_value("type", type_to_glsl(type)); + json_stream->emit_json_key_value("variable_id", spec_const.id); + switch (type.basetype) + { + case SPIRType::UInt: + json_stream->emit_json_key_value("default_value", c.scalar()); + break; + + case SPIRType::Int: + json_stream->emit_json_key_value("default_value", c.scalar_i32()); + break; + + case SPIRType::Float: + json_stream->emit_json_key_value("default_value", c.scalar_f32()); + break; + + case SPIRType::Boolean: + json_stream->emit_json_key_value("default_value", c.scalar() != 0); + break; + + default: + break; + } + json_stream->end_json_object(); + } + json_stream->end_json_array(); +} + +string CompilerReflection::to_member_name(const SPIRType &type, uint32_t index) const +{ + auto *type_meta = ir.find_meta(type.self); + + if (type_meta) + { + auto &memb = type_meta->members; + if (index < memb.size() && !memb[index].alias.empty()) + return memb[index].alias; + else + return join("_m", index); + } + else + return join("_m", index); +} diff --git a/third_party/spirv-cross/spirv_reflect.hpp b/third_party/spirv-cross/spirv_reflect.hpp new file mode 100644 index 0000000..9f60e72 --- /dev/null +++ b/third_party/spirv-cross/spirv_reflect.hpp @@ -0,0 +1,84 @@ +/* + * Copyright 2018-2020 Bradley Austin Davis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SPIRV_CROSS_REFLECT_HPP +#define SPIRV_CROSS_REFLECT_HPP + +#include "spirv_glsl.hpp" +#include + +namespace simple_json +{ +class Stream; +} + +namespace SPIRV_CROSS_NAMESPACE +{ +class CompilerReflection : public CompilerGLSL +{ + using Parent = CompilerGLSL; + +public: + explicit CompilerReflection(std::vector spirv_) + : Parent(std::move(spirv_)) + { + options.vulkan_semantics = true; + } + + CompilerReflection(const uint32_t *ir_, size_t word_count) + : Parent(ir_, word_count) + { + options.vulkan_semantics = true; + } + + explicit CompilerReflection(const ParsedIR &ir_) + : CompilerGLSL(ir_) + { + options.vulkan_semantics = true; + } + + explicit CompilerReflection(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) + { + options.vulkan_semantics = true; + } + + void set_format(const std::string &format); + std::string compile() override; + +private: + static std::string execution_model_to_str(spv::ExecutionModel model); + + void emit_entry_points(); + void emit_types(); + void emit_resources(); + void emit_specialization_constants(); + + void emit_type(uint32_t type_id, bool &emitted_open_tag); + void emit_type_member(const SPIRType &type, uint32_t index); + void emit_type_member_qualifiers(const SPIRType &type, uint32_t index); + void emit_type_array(const SPIRType &type); + void emit_resources(const char *tag, const SmallVector &resources); + bool type_is_reference(const SPIRType &type) const; + + std::string to_member_name(const SPIRType &type, uint32_t index) const; + + std::shared_ptr json_stream; +}; + +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/third_party/spirv-cross/test_shaders.py b/third_party/spirv-cross/test_shaders.py new file mode 100755 index 0000000..119617a --- /dev/null +++ b/third_party/spirv-cross/test_shaders.py @@ -0,0 +1,894 @@ +#!/usr/bin/env python3 + +# Copyright 2015-2020 Arm Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import os +import os.path +import subprocess +import tempfile +import re +import itertools +import hashlib +import shutil +import argparse +import codecs +import json +import multiprocessing +import errno +from functools import partial + +class Paths(): + def __init__(self, spirv_cross, glslang, spirv_as, spirv_val, spirv_opt): + self.spirv_cross = spirv_cross + self.glslang = glslang + self.spirv_as = spirv_as + self.spirv_val = spirv_val + self.spirv_opt = spirv_opt + +def remove_file(path): + #print('Removing file:', path) + os.remove(path) + +def create_temporary(suff = ''): + f, path = tempfile.mkstemp(suffix = suff) + os.close(f) + #print('Creating temporary:', path) + return path + +def parse_stats(stats): + m = re.search('([0-9]+) work registers', stats) + registers = int(m.group(1)) if m else 0 + + m = re.search('([0-9]+) uniform registers', stats) + uniform_regs = int(m.group(1)) if m else 0 + + m_list = re.findall('(-?[0-9]+)\s+(-?[0-9]+)\s+(-?[0-9]+)', stats) + alu_short = float(m_list[1][0]) if m_list else 0 + ls_short = float(m_list[1][1]) if m_list else 0 + tex_short = float(m_list[1][2]) if m_list else 0 + alu_long = float(m_list[2][0]) if m_list else 0 + ls_long = float(m_list[2][1]) if m_list else 0 + tex_long = float(m_list[2][2]) if m_list else 0 + + return (registers, uniform_regs, alu_short, ls_short, tex_short, alu_long, ls_long, tex_long) + +def get_shader_type(shader): + _, ext = os.path.splitext(shader) + if ext == '.vert': + return '--vertex' + elif ext == '.frag': + return '--fragment' + elif ext == '.comp': + return '--compute' + elif ext == '.tesc': + return '--tessellation_control' + elif ext == '.tese': + return '--tessellation_evaluation' + elif ext == '.geom': + return '--geometry' + else: + return '' + +def get_shader_stats(shader): + path = create_temporary() + + p = subprocess.Popen(['malisc', get_shader_type(shader), '--core', 'Mali-T760', '-V', shader], stdout = subprocess.PIPE, stderr = subprocess.PIPE) + stdout, stderr = p.communicate() + remove_file(path) + + if p.returncode != 0: + print(stderr.decode('utf-8')) + raise OSError('malisc failed') + p.wait() + + returned = stdout.decode('utf-8') + return parse_stats(returned) + +def print_msl_compiler_version(): + try: + subprocess.check_call(['xcrun', '--sdk', 'iphoneos', 'metal', '--version']) + print('...are the Metal compiler characteristics.\n') # display after so xcrun FNF is silent + except OSError as e: + if (e.errno != errno.ENOENT): # Ignore xcrun not found error + raise + except subprocess.CalledProcessError: + pass + +def msl_compiler_supports_version(version): + try: + subprocess.check_call(['xcrun', '--sdk', 'macosx', 'metal', '-x', 'metal', '-std=macos-metal' + version, '-'], + stdin = subprocess.DEVNULL, stdout = subprocess.DEVNULL, stderr = subprocess.DEVNULL) + print('Current SDK supports MSL {0}. Enabling validation for MSL {0} shaders.'.format(version)) + return True + except OSError as e: + print('Failed to check if MSL {} is not supported. It probably is not.'.format(version)) + return False + except subprocess.CalledProcessError: + print('Current SDK does NOT support MSL {0}. Disabling validation for MSL {0} shaders.'.format(version)) + return False + +def path_to_msl_standard(shader): + if '.ios.' in shader: + if '.msl2.' in shader: + return '-std=ios-metal2.0' + elif '.msl21.' in shader: + return '-std=ios-metal2.1' + elif '.msl22.' in shader: + return '-std=ios-metal2.2' + elif '.msl23.' in shader: + return '-std=ios-metal2.3' + elif '.msl11.' in shader: + return '-std=ios-metal1.1' + elif '.msl10.' in shader: + return '-std=ios-metal1.0' + else: + return '-std=ios-metal1.2' + else: + if '.msl2.' in shader: + return '-std=macos-metal2.0' + elif '.msl21.' in shader: + return '-std=macos-metal2.1' + elif '.msl22.' in shader: + return '-std=macos-metal2.2' + elif '.msl23.' in shader: + return '-std=macos-metal2.3' + elif '.msl11.' in shader: + return '-std=macos-metal1.1' + else: + return '-std=macos-metal1.2' + +def path_to_msl_standard_cli(shader): + if '.msl2.' in shader: + return '20000' + elif '.msl21.' in shader: + return '20100' + elif '.msl22.' in shader: + return '20200' + elif '.msl23.' in shader: + return '20300' + elif '.msl11.' in shader: + return '10100' + else: + return '10200' + +def validate_shader_msl(shader, opt): + msl_path = reference_path(shader[0], shader[1], opt) + try: + if '.ios.' in msl_path: + msl_os = 'iphoneos' + else: + msl_os = 'macosx' + subprocess.check_call(['xcrun', '--sdk', msl_os, 'metal', '-x', 'metal', path_to_msl_standard(msl_path), '-Werror', '-Wno-unused-variable', msl_path]) + print('Compiled Metal shader: ' + msl_path) # display after so xcrun FNF is silent + except OSError as oe: + if (oe.errno != errno.ENOENT): # Ignore xcrun not found error + raise + except subprocess.CalledProcessError: + print('Error compiling Metal shader: ' + msl_path) + raise RuntimeError('Failed to compile Metal shader') + +def cross_compile_msl(shader, spirv, opt, iterations, paths): + spirv_path = create_temporary() + msl_path = create_temporary(os.path.basename(shader)) + + spirv_env = 'vulkan1.1spv1.4' if ('.spv14.' in shader) else 'vulkan1.1' + + spirv_cmd = [paths.spirv_as, '--target-env', spirv_env, '-o', spirv_path, shader] + if '.preserve.' in shader: + spirv_cmd.append('--preserve-numeric-ids') + + if spirv: + subprocess.check_call(spirv_cmd) + else: + subprocess.check_call([paths.glslang, '--amb' ,'--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + + if opt and (not shader_is_invalid_spirv(shader)): + if '.graphics-robust-access.' in shader: + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '--graphics-robust-access', '-o', spirv_path, spirv_path]) + else: + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path]) + + spirv_cross_path = paths.spirv_cross + + msl_args = [spirv_cross_path, '--entry', 'main', '--output', msl_path, spirv_path, '--msl', '--iterations', str(iterations)] + msl_args.append('--msl-version') + msl_args.append(path_to_msl_standard_cli(shader)) + if '.swizzle.' in shader: + msl_args.append('--msl-swizzle-texture-samples') + if '.ios.' in shader: + msl_args.append('--msl-ios') + if '.pad-fragment.' in shader: + msl_args.append('--msl-pad-fragment-output') + if '.capture.' in shader: + msl_args.append('--msl-capture-output') + if '.domain.' in shader: + msl_args.append('--msl-domain-lower-left') + if '.argument.' in shader: + msl_args.append('--msl-argument-buffers') + if '.texture-buffer-native.' in shader: + msl_args.append('--msl-texture-buffer-native') + if '.framebuffer-fetch.' in shader: + msl_args.append('--msl-framebuffer-fetch') + if '.invariant-float-math.' in shader: + msl_args.append('--msl-invariant-float-math') + if '.emulate-cube-array.' in shader: + msl_args.append('--msl-emulate-cube-array') + if '.discrete.' in shader: + # Arbitrary for testing purposes. + msl_args.append('--msl-discrete-descriptor-set') + msl_args.append('2') + msl_args.append('--msl-discrete-descriptor-set') + msl_args.append('3') + if '.force-active.' in shader: + msl_args.append('--msl-force-active-argument-buffer-resources') + if '.line.' in shader: + msl_args.append('--emit-line-directives') + if '.multiview.' in shader: + msl_args.append('--msl-multiview') + if '.no-layered.' in shader: + msl_args.append('--msl-multiview-no-layered-rendering') + if '.viewfromdev.' in shader: + msl_args.append('--msl-view-index-from-device-index') + if '.dispatchbase.' in shader: + msl_args.append('--msl-dispatch-base') + if '.dynamic-buffer.' in shader: + # Arbitrary for testing purposes. + msl_args.append('--msl-dynamic-buffer') + msl_args.append('0') + msl_args.append('0') + msl_args.append('--msl-dynamic-buffer') + msl_args.append('1') + msl_args.append('2') + if '.inline-block.' in shader: + # Arbitrary for testing purposes. + msl_args.append('--msl-inline-uniform-block') + msl_args.append('0') + msl_args.append('0') + if '.device-argument-buffer.' in shader: + msl_args.append('--msl-device-argument-buffer') + msl_args.append('0') + msl_args.append('--msl-device-argument-buffer') + msl_args.append('1') + if '.force-native-array.' in shader: + msl_args.append('--msl-force-native-arrays') + if '.zero-initialize.' in shader: + msl_args.append('--force-zero-initialized-variables') + if '.frag-output.' in shader: + # Arbitrary for testing purposes. + msl_args.append('--msl-disable-frag-depth-builtin') + msl_args.append('--msl-disable-frag-stencil-ref-builtin') + msl_args.append('--msl-enable-frag-output-mask') + msl_args.append('0x000000ca') + if '.no-user-varying.' in shader: + msl_args.append('--msl-no-clip-distance-user-varying') + if '.shader-inputs.' in shader: + # Arbitrary for testing purposes. + msl_args.append('--msl-shader-input') + msl_args.append('0') + msl_args.append('u8') + msl_args.append('2') + msl_args.append('--msl-shader-input') + msl_args.append('1') + msl_args.append('u16') + msl_args.append('3') + msl_args.append('--msl-shader-input') + msl_args.append('6') + msl_args.append('other') + msl_args.append('4') + if '.multi-patch.' in shader: + msl_args.append('--msl-multi-patch-workgroup') + # Arbitrary for testing purposes. + msl_args.append('--msl-shader-input') + msl_args.append('0') + msl_args.append('any32') + msl_args.append('3') + msl_args.append('--msl-shader-input') + msl_args.append('1') + msl_args.append('any16') + msl_args.append('2') + if '.for-tess.' in shader: + msl_args.append('--msl-vertex-for-tessellation') + if '.fixed-sample-mask.' in shader: + msl_args.append('--msl-additional-fixed-sample-mask') + msl_args.append('0x00000022') + if '.arrayed-subpass.' in shader: + msl_args.append('--msl-arrayed-subpass-input') + if '.1d-as-2d.' in shader: + msl_args.append('--msl-texture-1d-as-2d') + + subprocess.check_call(msl_args) + + if not shader_is_invalid_spirv(msl_path): + subprocess.check_call([paths.spirv_val, '--scalar-block-layout', '--target-env', spirv_env, spirv_path]) + + return (spirv_path, msl_path) + +def shader_model_hlsl(shader): + if '.vert' in shader: + if '.sm30.' in shader: + return '-Tvs_3_0' + else: + return '-Tvs_5_1' + elif '.frag' in shader: + if '.sm30.' in shader: + return '-Tps_3_0' + else: + return '-Tps_5_1' + elif '.comp' in shader: + return '-Tcs_5_1' + else: + return None + +def shader_to_win_path(shader): + # It's (very) convenient to be able to run HLSL testing in wine on Unix-likes, so support that. + try: + with subprocess.Popen(['winepath', '-w', shader], stdout = subprocess.PIPE, stderr = subprocess.PIPE) as f: + stdout_data, stderr_data = f.communicate() + return stdout_data.decode('utf-8') + except OSError as oe: + if (oe.errno != errno.ENOENT): # Ignore not found errors + return shader + except subprocess.CalledProcessError: + raise + + return shader + +ignore_fxc = False +def validate_shader_hlsl(shader, force_no_external_validation, paths): + test_glslang = True + if '.nonuniformresource.' in shader: + test_glslang = False + if '.fxconly.' in shader: + test_glslang = False + + hlsl_args = [paths.glslang, '--amb', '-e', 'main', '-D', '--target-env', 'vulkan1.1', '-V', shader] + if '.sm30.' in shader: + hlsl_args.append('--hlsl-dx9-compatible') + + if test_glslang: + subprocess.check_call(hlsl_args) + + is_no_fxc = '.nofxc.' in shader + global ignore_fxc + if (not ignore_fxc) and (not force_no_external_validation) and (not is_no_fxc): + try: + win_path = shader_to_win_path(shader) + args = ['fxc', '-nologo', shader_model_hlsl(shader), win_path] + if '.nonuniformresource.' in shader: + args.append('/enable_unbounded_descriptor_tables') + subprocess.check_call(args) + except OSError as oe: + if (oe.errno != errno.ENOENT): # Ignore not found errors + print('Failed to run FXC.') + ignore_fxc = True + raise + else: + print('Could not find FXC.') + ignore_fxc = True + except subprocess.CalledProcessError: + print('Failed compiling HLSL shader:', shader, 'with FXC.') + raise RuntimeError('Failed compiling HLSL shader') + +def shader_to_sm(shader): + if '.sm62.' in shader: + return '62' + elif '.sm60.' in shader: + return '60' + elif '.sm51.' in shader: + return '51' + elif '.sm30.' in shader: + return '30' + else: + return '50' + +def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation, iterations, paths): + spirv_path = create_temporary() + hlsl_path = create_temporary(os.path.basename(shader)) + + spirv_cmd = [paths.spirv_as, '--target-env', 'vulkan1.1', '-o', spirv_path, shader] + if '.preserve.' in shader: + spirv_cmd.append('--preserve-numeric-ids') + + if spirv: + subprocess.check_call(spirv_cmd) + else: + subprocess.check_call([paths.glslang, '--amb', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + + if opt and (not shader_is_invalid_spirv(hlsl_path)): + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path]) + + spirv_cross_path = paths.spirv_cross + + sm = shader_to_sm(shader) + + hlsl_args = [spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl-enable-compat', '--hlsl', '--shader-model', sm, '--iterations', str(iterations)] + if '.line.' in shader: + hlsl_args.append('--emit-line-directives') + if '.force-uav.' in shader: + hlsl_args.append('--hlsl-force-storage-buffer-as-uav') + if '.zero-initialize.' in shader: + hlsl_args.append('--force-zero-initialized-variables') + if '.nonwritable-uav-texture.' in shader: + hlsl_args.append('--hlsl-nonwritable-uav-texture-as-srv') + if '.native-16bit.' in shader: + hlsl_args.append('--hlsl-enable-16bit-types') + if '.flatten-matrix-vertex-input.' in shader: + hlsl_args.append('--hlsl-flatten-matrix-vertex-input-semantics') + + subprocess.check_call(hlsl_args) + + if not shader_is_invalid_spirv(hlsl_path): + subprocess.check_call([paths.spirv_val, '--scalar-block-layout', '--target-env', 'vulkan1.1', spirv_path]) + + validate_shader_hlsl(hlsl_path, force_no_external_validation, paths) + + return (spirv_path, hlsl_path) + +def cross_compile_reflect(shader, spirv, opt, iterations, paths): + spirv_path = create_temporary() + reflect_path = create_temporary(os.path.basename(shader)) + + spirv_cmd = [paths.spirv_as, '--target-env', 'vulkan1.1', '-o', spirv_path, shader] + if '.preserve.' in shader: + spirv_cmd.append('--preserve-numeric-ids') + + if spirv: + subprocess.check_call(spirv_cmd) + else: + subprocess.check_call([paths.glslang, '--amb', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + + if opt and (not shader_is_invalid_spirv(reflect_path)): + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path]) + + spirv_cross_path = paths.spirv_cross + + sm = shader_to_sm(shader) + subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', reflect_path, spirv_path, '--reflect', '--iterations', str(iterations)]) + return (spirv_path, reflect_path) + +def validate_shader(shader, vulkan, paths): + if vulkan: + subprocess.check_call([paths.glslang, '--amb', '--target-env', 'vulkan1.1', '-V', shader]) + else: + subprocess.check_call([paths.glslang, shader]) + +def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt, push_ubo, iterations, paths): + spirv_path = create_temporary() + glsl_path = create_temporary(os.path.basename(shader)) + + spirv_env = 'vulkan1.1spv1.4' if ('.spv14.' in shader) else 'vulkan1.1' + + if vulkan or spirv: + vulkan_glsl_path = create_temporary('vk' + os.path.basename(shader)) + + spirv_cmd = [paths.spirv_as, '--target-env', spirv_env, '-o', spirv_path, shader] + if '.preserve.' in shader: + spirv_cmd.append('--preserve-numeric-ids') + + if spirv: + subprocess.check_call(spirv_cmd) + else: + subprocess.check_call([paths.glslang, '--amb', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader]) + + if opt and (not invalid_spirv): + subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path]) + + if not invalid_spirv: + subprocess.check_call([paths.spirv_val, '--scalar-block-layout', '--target-env', spirv_env, spirv_path]) + + extra_args = ['--iterations', str(iterations)] + if eliminate: + extra_args += ['--remove-unused-variables'] + if is_legacy: + extra_args += ['--version', '100', '--es'] + if flatten_ubo: + extra_args += ['--flatten-ubo'] + if sso: + extra_args += ['--separate-shader-objects'] + if flatten_dim: + extra_args += ['--flatten-multidimensional-arrays'] + if push_ubo: + extra_args += ['--glsl-emit-push-constant-as-ubo'] + if '.line.' in shader: + extra_args += ['--emit-line-directives'] + if '.no-samplerless.' in shader: + extra_args += ['--vulkan-glsl-disable-ext-samplerless-texture-functions'] + if '.no-qualifier-deduction.' in shader: + extra_args += ['--disable-storage-image-qualifier-deduction'] + if '.framebuffer-fetch.' in shader: + extra_args += ['--glsl-remap-ext-framebuffer-fetch', '0', '0'] + extra_args += ['--glsl-remap-ext-framebuffer-fetch', '1', '1'] + extra_args += ['--glsl-remap-ext-framebuffer-fetch', '2', '2'] + extra_args += ['--glsl-remap-ext-framebuffer-fetch', '3', '3'] + if '.zero-initialize.' in shader: + extra_args += ['--force-zero-initialized-variables'] + if '.force-flattened-io.' in shader: + extra_args += ['--glsl-force-flattened-io-blocks'] + + spirv_cross_path = paths.spirv_cross + + # A shader might not be possible to make valid GLSL from, skip validation for this case. + if (not ('nocompat' in glsl_path)) or (not vulkan): + subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', glsl_path, spirv_path] + extra_args) + if not 'nocompat' in glsl_path: + validate_shader(glsl_path, False, paths) + else: + remove_file(glsl_path) + glsl_path = None + + if (vulkan or spirv) and (not is_legacy): + subprocess.check_call([spirv_cross_path, '--entry', 'main', '--vulkan-semantics', '--output', vulkan_glsl_path, spirv_path] + extra_args) + validate_shader(vulkan_glsl_path, True, paths) + # SPIR-V shaders might just want to validate Vulkan GLSL output, we don't always care about the output. + if not vulkan: + remove_file(vulkan_glsl_path) + + return (spirv_path, glsl_path, vulkan_glsl_path if vulkan else None) + +def make_unix_newline(buf): + decoded = codecs.decode(buf, 'utf-8') + decoded = decoded.replace('\r', '') + return codecs.encode(decoded, 'utf-8') + +def md5_for_file(path): + md5 = hashlib.md5() + with open(path, 'rb') as f: + for chunk in iter(lambda: make_unix_newline(f.read(8192)), b''): + md5.update(chunk) + return md5.digest() + +def make_reference_dir(path): + base = os.path.dirname(path) + if not os.path.exists(base): + os.makedirs(base) + +def reference_path(directory, relpath, opt): + split_paths = os.path.split(directory) + reference_dir = os.path.join(split_paths[0], 'reference/' + ('opt/' if opt else '')) + reference_dir = os.path.join(reference_dir, split_paths[1]) + return os.path.join(reference_dir, relpath) + +def regression_check_reflect(shader, json_file, args): + reference = reference_path(shader[0], shader[1], args.opt) + '.json' + joined_path = os.path.join(shader[0], shader[1]) + print('Reference shader reflection path:', reference) + if os.path.exists(reference): + actual = md5_for_file(json_file) + expected = md5_for_file(reference) + if actual != expected: + if args.update: + print('Generated reflection json has changed for {}!'.format(reference)) + # If we expect changes, update the reference file. + if os.path.exists(reference): + remove_file(reference) + make_reference_dir(reference) + shutil.move(json_file, reference) + else: + print('Generated reflection json in {} does not match reference {}!'.format(json_file, reference)) + with open(json_file, 'r') as f: + print('') + print('Generated:') + print('======================') + print(f.read()) + print('======================') + print('') + + # Otherwise, fail the test. Keep the shader file around so we can inspect. + if not args.keep: + remove_file(json_file) + + raise RuntimeError('Does not match reference') + else: + remove_file(json_file) + else: + print('Found new shader {}. Placing generated source code in {}'.format(joined_path, reference)) + make_reference_dir(reference) + shutil.move(json_file, reference) + +def regression_check(shader, glsl, args): + reference = reference_path(shader[0], shader[1], args.opt) + joined_path = os.path.join(shader[0], shader[1]) + print('Reference shader path:', reference) + + if os.path.exists(reference): + if md5_for_file(glsl) != md5_for_file(reference): + if args.update: + print('Generated source code has changed for {}!'.format(reference)) + # If we expect changes, update the reference file. + if os.path.exists(reference): + remove_file(reference) + make_reference_dir(reference) + shutil.move(glsl, reference) + else: + print('Generated source code in {} does not match reference {}!'.format(glsl, reference)) + with open(glsl, 'r') as f: + print('') + print('Generated:') + print('======================') + print(f.read()) + print('======================') + print('') + + # Otherwise, fail the test. Keep the shader file around so we can inspect. + if not args.keep: + remove_file(glsl) + raise RuntimeError('Does not match reference') + else: + remove_file(glsl) + else: + print('Found new shader {}. Placing generated source code in {}'.format(joined_path, reference)) + make_reference_dir(reference) + shutil.move(glsl, reference) + +def shader_is_vulkan(shader): + return '.vk.' in shader + +def shader_is_desktop(shader): + return '.desktop.' in shader + +def shader_is_eliminate_dead_variables(shader): + return '.noeliminate.' not in shader + +def shader_is_spirv(shader): + return '.asm.' in shader + +def shader_is_invalid_spirv(shader): + return '.invalid.' in shader + +def shader_is_legacy(shader): + return '.legacy.' in shader + +def shader_is_flatten_ubo(shader): + return '.flatten.' in shader + +def shader_is_sso(shader): + return '.sso.' in shader + +def shader_is_flatten_dimensions(shader): + return '.flatten_dim.' in shader + +def shader_is_noopt(shader): + return '.noopt.' in shader + +def shader_is_push_ubo(shader): + return '.push-ubo.' in shader + +def test_shader(stats, shader, args, paths): + joined_path = os.path.join(shader[0], shader[1]) + vulkan = shader_is_vulkan(shader[1]) + desktop = shader_is_desktop(shader[1]) + eliminate = shader_is_eliminate_dead_variables(shader[1]) + is_spirv = shader_is_spirv(shader[1]) + invalid_spirv = shader_is_invalid_spirv(shader[1]) + is_legacy = shader_is_legacy(shader[1]) + flatten_ubo = shader_is_flatten_ubo(shader[1]) + sso = shader_is_sso(shader[1]) + flatten_dim = shader_is_flatten_dimensions(shader[1]) + noopt = shader_is_noopt(shader[1]) + push_ubo = shader_is_push_ubo(shader[1]) + + print('Testing shader:', joined_path) + spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, args.opt and (not noopt), push_ubo, args.iterations, paths) + + # Only test GLSL stats if we have a shader following GL semantics. + if stats and (not vulkan) and (not is_spirv) and (not desktop): + cross_stats = get_shader_stats(glsl) + + if glsl: + regression_check(shader, glsl, args) + if vulkan_glsl: + regression_check((shader[0], shader[1] + '.vk'), vulkan_glsl, args) + + remove_file(spirv) + + if stats and (not vulkan) and (not is_spirv) and (not desktop): + pristine_stats = get_shader_stats(joined_path) + + a = [] + a.append(shader[1]) + for i in pristine_stats: + a.append(str(i)) + for i in cross_stats: + a.append(str(i)) + print(','.join(a), file = stats) + +def test_shader_msl(stats, shader, args, paths): + joined_path = os.path.join(shader[0], shader[1]) + print('\nTesting MSL shader:', joined_path) + is_spirv = shader_is_spirv(shader[1]) + noopt = shader_is_noopt(shader[1]) + spirv, msl = cross_compile_msl(joined_path, is_spirv, args.opt and (not noopt), args.iterations, paths) + regression_check(shader, msl, args) + + # Uncomment the following line to print the temp SPIR-V file path. + # This temp SPIR-V file is not deleted until after the Metal validation step below. + # If Metal validation fails, the temp SPIR-V file can be copied out and + # used as input to an invocation of spirv-cross to debug from Xcode directly. + # To do so, build spriv-cross using `make DEBUG=1`, then run the spriv-cross + # executable from Xcode using args: `--msl --entry main --output msl_path spirv_path`. +# print('SPRIV shader: ' + spirv) + + shader_is_msl22 = 'msl22' in joined_path + shader_is_msl23 = 'msl23' in joined_path + skip_validation = (shader_is_msl22 and (not args.msl22)) or (shader_is_msl23 and (not args.msl23)) + if '.invalid.' in joined_path: + skip_validation = True + + if (not args.force_no_external_validation) and (not skip_validation): + validate_shader_msl(shader, args.opt) + + remove_file(spirv) + +def test_shader_hlsl(stats, shader, args, paths): + joined_path = os.path.join(shader[0], shader[1]) + print('Testing HLSL shader:', joined_path) + is_spirv = shader_is_spirv(shader[1]) + noopt = shader_is_noopt(shader[1]) + spirv, hlsl = cross_compile_hlsl(joined_path, is_spirv, args.opt and (not noopt), args.force_no_external_validation, args.iterations, paths) + regression_check(shader, hlsl, args) + remove_file(spirv) + +def test_shader_reflect(stats, shader, args, paths): + joined_path = os.path.join(shader[0], shader[1]) + print('Testing shader reflection:', joined_path) + is_spirv = shader_is_spirv(shader[1]) + noopt = shader_is_noopt(shader[1]) + spirv, reflect = cross_compile_reflect(joined_path, is_spirv, args.opt and (not noopt), args.iterations, paths) + regression_check_reflect(shader, reflect, args) + remove_file(spirv) + +def test_shader_file(relpath, stats, args, backend): + paths = Paths(args.spirv_cross, args.glslang, args.spirv_as, args.spirv_val, args.spirv_opt) + try: + if backend == 'msl': + test_shader_msl(stats, (args.folder, relpath), args, paths) + elif backend == 'hlsl': + test_shader_hlsl(stats, (args.folder, relpath), args, paths) + elif backend == 'reflect': + test_shader_reflect(stats, (args.folder, relpath), args, paths) + else: + test_shader(stats, (args.folder, relpath), args, paths) + return None + except Exception as e: + return e + +def test_shaders_helper(stats, backend, args): + all_files = [] + for root, dirs, files in os.walk(os.path.join(args.folder)): + files = [ f for f in files if not f.startswith(".") ] #ignore system files (esp OSX) + for i in files: + path = os.path.join(root, i) + relpath = os.path.relpath(path, args.folder) + all_files.append(relpath) + + # The child processes in parallel execution mode don't have the proper state for the global args variable, so + # at this point we need to switch to explicit arguments + if args.parallel: + with multiprocessing.Pool(multiprocessing.cpu_count()) as pool: + results = [] + for f in all_files: + results.append(pool.apply_async(test_shader_file, + args = (f, stats, args, backend))) + + pool.close() + pool.join() + results_completed = [res.get() for res in results] + + for error in results_completed: + if error is not None: + print('Error:', error) + sys.exit(1) + + else: + for i in all_files: + e = test_shader_file(i, stats, args, backend) + if e is not None: + print('Error:', e) + sys.exit(1) + +def test_shaders(backend, args): + if args.malisc: + with open('stats.csv', 'w') as stats: + print('Shader,OrigRegs,OrigUniRegs,OrigALUShort,OrigLSShort,OrigTEXShort,OrigALULong,OrigLSLong,OrigTEXLong,CrossRegs,CrossUniRegs,CrossALUShort,CrossLSShort,CrossTEXShort,CrossALULong,CrossLSLong,CrossTEXLong', file = stats) + test_shaders_helper(stats, backend, args) + else: + test_shaders_helper(None, backend, args) + +def main(): + parser = argparse.ArgumentParser(description = 'Script for regression testing.') + parser.add_argument('folder', + help = 'Folder containing shader files to test.') + parser.add_argument('--update', + action = 'store_true', + help = 'Updates reference files if there is a mismatch. Use when legitimate changes in output is found.') + parser.add_argument('--keep', + action = 'store_true', + help = 'Leave failed GLSL shaders on disk if they fail regression. Useful for debugging.') + parser.add_argument('--malisc', + action = 'store_true', + help = 'Use malisc offline compiler to determine static cycle counts before and after spirv-cross.') + parser.add_argument('--msl', + action = 'store_true', + help = 'Test Metal backend.') + parser.add_argument('--metal', + action = 'store_true', + help = 'Deprecated Metal option. Use --msl instead.') + parser.add_argument('--hlsl', + action = 'store_true', + help = 'Test HLSL backend.') + parser.add_argument('--force-no-external-validation', + action = 'store_true', + help = 'Disable all external validation.') + parser.add_argument('--opt', + action = 'store_true', + help = 'Run SPIRV-Tools optimization passes as well.') + parser.add_argument('--reflect', + action = 'store_true', + help = 'Test reflection backend.') + parser.add_argument('--parallel', + action = 'store_true', + help = 'Execute tests in parallel. Useful for doing regression quickly, but bad for debugging and stat output.') + parser.add_argument('--spirv-cross', + default = './spirv-cross', + help = 'Explicit path to spirv-cross') + parser.add_argument('--glslang', + default = 'glslangValidator', + help = 'Explicit path to glslangValidator') + parser.add_argument('--spirv-as', + default = 'spirv-as', + help = 'Explicit path to spirv-as') + parser.add_argument('--spirv-val', + default = 'spirv-val', + help = 'Explicit path to spirv-val') + parser.add_argument('--spirv-opt', + default = 'spirv-opt', + help = 'Explicit path to spirv-opt') + parser.add_argument('--iterations', + default = 1, + type = int, + help = 'Number of iterations to run SPIRV-Cross (benchmarking)') + + args = parser.parse_args() + if not args.folder: + sys.stderr.write('Need shader folder.\n') + sys.exit(1) + + if (args.parallel and (args.malisc or args.force_no_external_validation or args.update)): + sys.stderr.write('Parallel execution is disabled when using the flags --update, --malisc or --force-no-external-validation\n') + args.parallel = False + + args.msl22 = False + args.msl23 = False + if args.msl: + print_msl_compiler_version() + args.msl22 = msl_compiler_supports_version('2.2') + args.msl23 = msl_compiler_supports_version('2.3') + + backend = 'glsl' + if (args.msl or args.metal): + backend = 'msl' + elif args.hlsl: + backend = 'hlsl' + elif args.reflect: + backend = 'reflect' + + test_shaders(backend, args) + if args.malisc: + print('Stats in stats.csv!') + print('Tests completed!') + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-cross/test_shaders.sh b/third_party/spirv-cross/test_shaders.sh new file mode 100755 index 0000000..dccdf34 --- /dev/null +++ b/third_party/spirv-cross/test_shaders.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +OPTS=$@ + +if [ -z "$SPIRV_CROSS_PATH" ]; then + echo "Building spirv-cross" + make -j$(nproc) + SPIRV_CROSS_PATH="./spirv-cross" +fi + +export PATH="./external/glslang-build/output/bin:./external/spirv-tools-build/output/bin:.:$PATH" +echo "Using glslangValidation in: $(which glslangValidator)." +echo "Using spirv-opt in: $(which spirv-opt)." +echo "Using SPIRV-Cross in: \"$SPIRV_CROSS_PATH\"." + +./test_shaders.py shaders ${OPTS} --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders ${OPTS} --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-no-opt ${OPTS} --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-msl ${OPTS} --msl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-msl ${OPTS} --msl --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-msl-no-opt ${OPTS} --msl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-hlsl ${OPTS} --hlsl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-hlsl ${OPTS} --hlsl --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-hlsl-no-opt ${OPTS} --hlsl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-reflection ${OPTS} --reflect --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-ue4 ${OPTS} --msl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-ue4 ${OPTS} --msl --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 +./test_shaders.py shaders-ue4-no-opt ${OPTS} --msl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1 + diff --git a/third_party/spirv-cross/tests-other/c_api_test.c b/third_party/spirv-cross/tests-other/c_api_test.c new file mode 100644 index 0000000..413e204 --- /dev/null +++ b/third_party/spirv-cross/tests-other/c_api_test.c @@ -0,0 +1,189 @@ +/* Smoke test for the C API. */ + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#define SPVC_CHECKED_CALL(x) do { \ + if ((x) != SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ +} while(0) +#define SPVC_CHECKED_CALL_NEGATIVE(x) do { \ + g_fail_on_error = SPVC_FALSE; \ + if ((x) == SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ + g_fail_on_error = SPVC_TRUE; \ +} while(0) + +static int read_file(const char *path, SpvId **buffer, size_t *word_count) +{ + long len; + FILE *file = fopen(path, "rb"); + + if (!file) + return -1; + + fseek(file, 0, SEEK_END); + len = ftell(file); + rewind(file); + + *buffer = malloc(len); + if (fread(*buffer, 1, len, file) != (size_t)len) + { + fclose(file); + free(*buffer); + return -1; + } + + fclose(file); + *word_count = len / sizeof(SpvId); + return 0; +} + +static spvc_bool g_fail_on_error = SPVC_TRUE; + +static void error_callback(void *userdata, const char *error) +{ + (void)userdata; + if (g_fail_on_error) + { + fprintf(stderr, "Error: %s\n", error); + exit(1); + } + else + printf("Expected error hit: %s.\n", error); +} + +static void dump_resource_list(spvc_compiler compiler, spvc_resources resources, spvc_resource_type type, const char *tag) +{ + const spvc_reflected_resource *list = NULL; + size_t count = 0; + size_t i; + SPVC_CHECKED_CALL(spvc_resources_get_resource_list_for_type(resources, type, &list, &count)); + printf("%s\n", tag); + for (i = 0; i < count; i++) + { + printf("ID: %u, BaseTypeID: %u, TypeID: %u, Name: %s\n", list[i].id, list[i].base_type_id, list[i].type_id, + list[i].name); + printf(" Set: %u, Binding: %u\n", + spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationDescriptorSet), + spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding)); + } +} + +static void dump_resources(spvc_compiler compiler, spvc_resources resources) +{ + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, "UBO"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_STORAGE_BUFFER, "SSBO"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_PUSH_CONSTANT, "Push"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS, "Samplers"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_SEPARATE_IMAGE, "Image"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, "Combined image samplers"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_STAGE_INPUT, "Stage input"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_STAGE_OUTPUT, "Stage output"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_STORAGE_IMAGE, "Storage image"); + dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_SUBPASS_INPUT, "Subpass input"); +} + +static void compile(spvc_compiler compiler, const char *tag) +{ + const char *result = NULL; + SPVC_CHECKED_CALL(spvc_compiler_compile(compiler, &result)); + printf("\n%s\n=======\n", tag); + printf("%s\n=======\n", result); +} + +int main(int argc, char **argv) +{ + const char *rev = NULL; + + spvc_context context = NULL; + spvc_parsed_ir ir = NULL; + spvc_compiler compiler_glsl = NULL; + spvc_compiler compiler_hlsl = NULL; + spvc_compiler compiler_msl = NULL; + spvc_compiler compiler_cpp = NULL; + spvc_compiler compiler_json = NULL; + spvc_compiler compiler_none = NULL; + spvc_compiler_options options = NULL; + spvc_resources resources = NULL; + SpvId *buffer = NULL; + size_t word_count = 0; + + rev = spvc_get_commit_revision_and_timestamp(); + if (!rev || *rev == '\0') + return 1; + + printf("Revision: %s\n", rev); + + if (argc != 5) + return 1; + + if (read_file(argv[1], &buffer, &word_count) < 0) + return 1; + + unsigned abi_major, abi_minor, abi_patch; + spvc_get_version(&abi_major, &abi_minor, &abi_patch); + if (abi_major != strtoul(argv[2], NULL, 0)) + { + fprintf(stderr, "VERSION_MAJOR mismatch!\n"); + return 1; + } + + if (abi_minor != strtoul(argv[3], NULL, 0)) + { + fprintf(stderr, "VERSION_MINOR mismatch!\n"); + return 1; + } + + if (abi_patch != strtoul(argv[4], NULL, 0)) + { + fprintf(stderr, "VERSION_PATCH mismatch!\n"); + return 1; + } + + SPVC_CHECKED_CALL(spvc_context_create(&context)); + spvc_context_set_error_callback(context, error_callback, NULL); + SPVC_CHECKED_CALL(spvc_context_parse_spirv(context, buffer, word_count, &ir)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_COPY, &compiler_glsl)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_HLSL, ir, SPVC_CAPTURE_MODE_COPY, &compiler_hlsl)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_MSL, ir, SPVC_CAPTURE_MODE_COPY, &compiler_msl)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_CPP, ir, SPVC_CAPTURE_MODE_COPY, &compiler_cpp)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_JSON, ir, SPVC_CAPTURE_MODE_COPY, &compiler_json)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_NONE, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler_none)); + + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_none, &options)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_none, options)); + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_json, &options)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_json, options)); + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_cpp, &options)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_cpp, options)); + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_msl, &options)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_msl, options)); + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_hlsl, &options)); + SPVC_CHECKED_CALL(spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL, 50)); + SPVC_CHECKED_CALL_NEGATIVE(spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_MSL_PLATFORM, 1)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_hlsl, options)); + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_glsl, &options)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_glsl, options)); + + SPVC_CHECKED_CALL(spvc_compiler_create_shader_resources(compiler_none, &resources)); + dump_resources(compiler_none, resources); + compile(compiler_glsl, "GLSL"); + compile(compiler_hlsl, "HLSL"); + compile(compiler_msl, "MSL"); + compile(compiler_json, "JSON"); + compile(compiler_cpp, "CPP"); + + spvc_context_destroy(context); + free(buffer); + return 0; +} diff --git a/third_party/spirv-cross/tests-other/c_api_test.spv b/third_party/spirv-cross/tests-other/c_api_test.spv new file mode 100644 index 0000000000000000000000000000000000000000..488680046dab7db539dc5b1ded393bb9f49fa8f9 GIT binary patch literal 1268 zcmZvcTT2{45QS@J95otmF&Zz48869;AU;G11OxiA3POa9KVX7`5nPp-UC66{Ex*c_ zfamLrw33pt>r|an(_P(co^6hqb!^PW?X!8CvrHMYNgH)*_q2Dq(+)0nzU=M7n6}&z z;mlZ}KeG{ezq-1iFe@E+P7Wt}E?p-4SBXN5R0GyVtk*mGp~ZCoW0joxE3Iw&>y})h zUaz&Di_X`GG0)AlN3)@*=-wzk8@9ix!v$lWTi9F0O?`RQ4(g^=%!xi|9saEU42Dgu z(Df% z*A;i<=;IkvJvn9ypEIX=FnSDqJa?)m7xkPu)q~N;dikObrtbHj8+cjHUg(qdB8JPI zQ)fPD9ZbED&+Chs!@L;({Y6Eiic9&LV&>-Yh+eMeko%_IVBQKn7KA6Xmhz5#JK#yh z7`>Qb>@}qr9XWb|nIpMUcqYW~(K5fNomb3v$)N@Fw#cDf2r>L+WmmKkv#-=5URD29 z72u(NA4ufr59W?A`mJgGUqAMa(c_~J9hV;9GUTYQhnR0t2X8#(GWT}jZG{|f2_Bd` z#P)TIe3QGzxWfl!xZ6YhXNI$K=E^-Bh{?YBy&w;VSuM($U&?tG^7rDPS(2k8AA9o# RnK`=e`Z^fiKTZ0p{1MZ#J$3*9 literal 0 HcmV?d00001 diff --git a/third_party/spirv-cross/tests-other/hlsl_resource_binding.spv b/third_party/spirv-cross/tests-other/hlsl_resource_binding.spv new file mode 100644 index 0000000000000000000000000000000000000000..c48dc49ea00c0d7753a027d29dcbc8f66247a7d4 GIT binary patch literal 796 zcmYk2%}PRH5Qe`r&9c;f%*qPQWe_bYf+&a}TeM25wUmgU6-Vn1y0NaRP0;fkk8noD znR(AUGw*!aOlu*8xlrJ@7pArvG9V!oLq3k}`_BE*c=CF5a(XPI6tXc5wmvJ0q#w+x=F51Ywwxy?^1hO;947P z?)JuY{*BG}>M6EKo(F5-HRkyy*3NJ6Um*Ib;ulG+MzL+wE)nIcFKx5B4ztJydCpr- zNMg>j_D0S9Wuo)cDZfH=mbiRtZx-8UwzsKGZk4DuF}c>hu~?P5Mr`2r9wzk`xn}=1 zQLgjOcuO_-4x0D=g5-#opz1H-YPVM38m_iySk73-<%nN{Ie!ONlY8xxSI5P_lfGSY Pd&RHVtR}HPl%M1OI}s?= literal 0 HcmV?d00001 diff --git a/third_party/spirv-cross/tests-other/hlsl_resource_bindings.cpp b/third_party/spirv-cross/tests-other/hlsl_resource_bindings.cpp new file mode 100644 index 0000000..1a938da --- /dev/null +++ b/third_party/spirv-cross/tests-other/hlsl_resource_bindings.cpp @@ -0,0 +1,89 @@ +// Testbench for HLSL resource binding APIs. +// It does not validate output at the moment, but it's useful for ad-hoc testing. + +#include +#include +#include +#include + +#define SPVC_CHECKED_CALL(x) do { \ + if ((x) != SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ +} while(0) + +static std::vector read_file(const char *path) +{ + long len; + FILE *file = fopen(path, "rb"); + + if (!file) + return {}; + + fseek(file, 0, SEEK_END); + len = ftell(file); + rewind(file); + + std::vector buffer(len / sizeof(SpvId)); + if (fread(buffer.data(), 1, len, file) != (size_t)len) + { + fclose(file); + return {}; + } + + fclose(file); + return buffer; +} + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + auto buffer = read_file(argv[1]); + if (buffer.empty()) + return EXIT_FAILURE; + + spvc_context ctx; + spvc_parsed_ir parsed_ir; + spvc_compiler compiler; + + SPVC_CHECKED_CALL(spvc_context_create(&ctx)); + SPVC_CHECKED_CALL(spvc_context_parse_spirv(ctx, buffer.data(), buffer.size(), &parsed_ir)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(ctx, SPVC_BACKEND_HLSL, parsed_ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler)); + + spvc_compiler_options opts; + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler, &opts)); + SPVC_CHECKED_CALL(spvc_compiler_options_set_uint(opts, SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL, 51)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler, opts)); + + spvc_hlsl_resource_binding binding; + spvc_hlsl_resource_binding_init(&binding); + binding.stage = SpvExecutionModelFragment; + binding.desc_set = 1; + binding.binding = 4; + binding.srv.register_space = 2; + binding.srv.register_binding = 3; + binding.sampler.register_space = 4; + binding.sampler.register_binding = 5; + SPVC_CHECKED_CALL(spvc_compiler_hlsl_add_resource_binding(compiler, &binding)); + + binding.desc_set = SPVC_HLSL_PUSH_CONSTANT_DESC_SET; + binding.binding = SPVC_HLSL_PUSH_CONSTANT_BINDING; + binding.cbv.register_space = 0; + binding.cbv.register_binding = 4; + SPVC_CHECKED_CALL(spvc_compiler_hlsl_add_resource_binding(compiler, &binding)); + + const char *str; + SPVC_CHECKED_CALL(spvc_compiler_compile(compiler, &str)); + + fprintf(stderr, "Output:\n%s\n", str); + + if (!spvc_compiler_hlsl_is_resource_used(compiler, SpvExecutionModelFragment, 1, 4)) + return EXIT_FAILURE; + + if (!spvc_compiler_hlsl_is_resource_used(compiler, SpvExecutionModelFragment, SPVC_HLSL_PUSH_CONSTANT_DESC_SET, SPVC_HLSL_PUSH_CONSTANT_BINDING)) + return EXIT_FAILURE; +} + diff --git a/third_party/spirv-cross/tests-other/hlsl_wave_mask.cpp b/third_party/spirv-cross/tests-other/hlsl_wave_mask.cpp new file mode 100644 index 0000000..de11dd9 --- /dev/null +++ b/third_party/spirv-cross/tests-other/hlsl_wave_mask.cpp @@ -0,0 +1,73 @@ +// Ad-hoc test that the wave op masks work as expected. +#include +#include + +using namespace glm; + +static uvec4 gl_SubgroupEqMask; +static uvec4 gl_SubgroupGeMask; +static uvec4 gl_SubgroupGtMask; +static uvec4 gl_SubgroupLeMask; +static uvec4 gl_SubgroupLtMask; +using uint4 = uvec4; + +static void test_main(unsigned wave_index) +{ + const auto WaveGetLaneIndex = [&]() { return wave_index; }; + + gl_SubgroupEqMask = 1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96)); + if (WaveGetLaneIndex() >= 32) gl_SubgroupEqMask.x = 0; + if (WaveGetLaneIndex() >= 64 || WaveGetLaneIndex() < 32) gl_SubgroupEqMask.y = 0; + if (WaveGetLaneIndex() >= 96 || WaveGetLaneIndex() < 64) gl_SubgroupEqMask.z = 0; + if (WaveGetLaneIndex() < 96) gl_SubgroupEqMask.w = 0; + gl_SubgroupGeMask = ~((1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u); + if (WaveGetLaneIndex() >= 32) gl_SubgroupGeMask.x = 0u; + if (WaveGetLaneIndex() >= 64) gl_SubgroupGeMask.y = 0u; + if (WaveGetLaneIndex() >= 96) gl_SubgroupGeMask.z = 0u; + if (WaveGetLaneIndex() < 32) gl_SubgroupGeMask.y = ~0u; + if (WaveGetLaneIndex() < 64) gl_SubgroupGeMask.z = ~0u; + if (WaveGetLaneIndex() < 96) gl_SubgroupGeMask.w = ~0u; + uint gt_lane_index = WaveGetLaneIndex() + 1; + gl_SubgroupGtMask = ~((1u << (gt_lane_index - uint4(0, 32, 64, 96))) - 1u); + if (gt_lane_index >= 32) gl_SubgroupGtMask.x = 0u; + if (gt_lane_index >= 64) gl_SubgroupGtMask.y = 0u; + if (gt_lane_index >= 96) gl_SubgroupGtMask.z = 0u; + if (gt_lane_index >= 128) gl_SubgroupGtMask.w = 0u; + if (gt_lane_index < 32) gl_SubgroupGtMask.y = ~0u; + if (gt_lane_index < 64) gl_SubgroupGtMask.z = ~0u; + if (gt_lane_index < 96) gl_SubgroupGtMask.w = ~0u; + uint le_lane_index = WaveGetLaneIndex() + 1; + gl_SubgroupLeMask = (1u << (le_lane_index - uint4(0, 32, 64, 96))) - 1u; + if (le_lane_index >= 32) gl_SubgroupLeMask.x = ~0u; + if (le_lane_index >= 64) gl_SubgroupLeMask.y = ~0u; + if (le_lane_index >= 96) gl_SubgroupLeMask.z = ~0u; + if (le_lane_index >= 128) gl_SubgroupLeMask.w = ~0u; + if (le_lane_index < 32) gl_SubgroupLeMask.y = 0u; + if (le_lane_index < 64) gl_SubgroupLeMask.z = 0u; + if (le_lane_index < 96) gl_SubgroupLeMask.w = 0u; + gl_SubgroupLtMask = (1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u; + if (WaveGetLaneIndex() >= 32) gl_SubgroupLtMask.x = ~0u; + if (WaveGetLaneIndex() >= 64) gl_SubgroupLtMask.y = ~0u; + if (WaveGetLaneIndex() >= 96) gl_SubgroupLtMask.z = ~0u; + if (WaveGetLaneIndex() < 32) gl_SubgroupLtMask.y = 0u; + if (WaveGetLaneIndex() < 64) gl_SubgroupLtMask.z = 0u; + if (WaveGetLaneIndex() < 96) gl_SubgroupLtMask.w = 0u; +} + +int main() +{ + for (unsigned subgroup_id = 0; subgroup_id < 128; subgroup_id++) + { + test_main(subgroup_id); + + for (unsigned bit = 0; bit < 128; bit++) + { + assert(bool(gl_SubgroupEqMask[bit / 32] & (1u << (bit & 31))) == (bit == subgroup_id)); + assert(bool(gl_SubgroupGtMask[bit / 32] & (1u << (bit & 31))) == (bit > subgroup_id)); + assert(bool(gl_SubgroupGeMask[bit / 32] & (1u << (bit & 31))) == (bit >= subgroup_id)); + assert(bool(gl_SubgroupLtMask[bit / 32] & (1u << (bit & 31))) == (bit < subgroup_id)); + assert(bool(gl_SubgroupLeMask[bit / 32] & (1u << (bit & 31))) == (bit <= subgroup_id)); + } + } +} + diff --git a/third_party/spirv-cross/tests-other/msl_constexpr_test.cpp b/third_party/spirv-cross/tests-other/msl_constexpr_test.cpp new file mode 100644 index 0000000..d0378ee --- /dev/null +++ b/third_party/spirv-cross/tests-other/msl_constexpr_test.cpp @@ -0,0 +1,133 @@ +// Testbench for MSL constexpr samplers. +// It does not validate output, but it's useful for ad-hoc testing. + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include + +#define SPVC_CHECKED_CALL(x) do { \ + if ((x) != SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ +} while(0) +#define SPVC_CHECKED_CALL_NEGATIVE(x) do { \ + g_fail_on_error = SPVC_FALSE; \ + if ((x) == SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ + g_fail_on_error = SPVC_TRUE; \ +} while(0) + +static std::vector read_file(const char *path) +{ + long len; + FILE *file = fopen(path, "rb"); + + if (!file) + return {}; + + fseek(file, 0, SEEK_END); + len = ftell(file); + rewind(file); + + std::vector buffer(len / sizeof(SpvId)); + if (fread(buffer.data(), 1, len, file) != (size_t)len) + { + fclose(file); + return {}; + } + + fclose(file); + return buffer; +} + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + auto buffer = read_file(argv[1]); + if (buffer.empty()) + return EXIT_FAILURE; + + spvc_context ctx; + spvc_parsed_ir parsed_ir; + spvc_compiler compiler; + + SPVC_CHECKED_CALL(spvc_context_create(&ctx)); + SPVC_CHECKED_CALL(spvc_context_parse_spirv(ctx, buffer.data(), buffer.size(), &parsed_ir)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(ctx, SPVC_BACKEND_MSL, parsed_ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler)); + + spvc_msl_resource_binding binding; + spvc_msl_resource_binding_init(&binding); + binding.desc_set = 1; + binding.binding = 2; + binding.stage = SpvExecutionModelFragment; + binding.msl_texture = 0; + binding.msl_sampler = 0; + SPVC_CHECKED_CALL(spvc_compiler_msl_add_resource_binding(compiler, &binding)); + + binding.binding = 3; + binding.msl_texture = 1; + binding.msl_sampler = 1000; // Will be remapped anyways, sanity check. + SPVC_CHECKED_CALL(spvc_compiler_msl_add_resource_binding(compiler, &binding)); + + binding.desc_set = 2; + binding.binding = 2; + binding.msl_texture = 2; + SPVC_CHECKED_CALL(spvc_compiler_msl_add_resource_binding(compiler, &binding)); + + binding.binding = 3; + binding.msl_texture = 3; + SPVC_CHECKED_CALL(spvc_compiler_msl_add_resource_binding(compiler, &binding)); + + spvc_msl_constexpr_sampler samp; + spvc_msl_constexpr_sampler_init(&samp); + samp.s_address = SPVC_MSL_SAMPLER_ADDRESS_REPEAT; + samp.t_address = SPVC_MSL_SAMPLER_ADDRESS_REPEAT; + samp.r_address = SPVC_MSL_SAMPLER_ADDRESS_REPEAT; + SPVC_CHECKED_CALL(spvc_compiler_msl_remap_constexpr_sampler_by_binding(compiler, 1, 3, &samp)); + + samp.s_address = SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + samp.t_address = SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + samp.r_address = SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + SPVC_CHECKED_CALL(spvc_compiler_msl_remap_constexpr_sampler_by_binding(compiler, 2, 4, &samp)); + + samp.compare_enable = SPVC_TRUE; + samp.compare_func = SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS; + samp.s_address = SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + samp.t_address = SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + samp.r_address = SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + SPVC_CHECKED_CALL(spvc_compiler_msl_remap_constexpr_sampler_by_binding(compiler, 2, 5, &samp)); + + const char *str; + SPVC_CHECKED_CALL(spvc_compiler_compile(compiler, &str)); + + // Should not be marked as used. + if (spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 2, 4)) + return EXIT_FAILURE; + + // Should not be marked as used. + if (spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 2, 5)) + return EXIT_FAILURE; + + // Should be marked, as a sanity check. + if (!spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 1, 2)) + return EXIT_FAILURE; + if (!spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 1, 3)) + return EXIT_FAILURE; + if (!spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 2, 2)) + return EXIT_FAILURE; + if (!spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 2, 3)) + return EXIT_FAILURE; + + fprintf(stderr, "Output:\n%s\n", str); +} + diff --git a/third_party/spirv-cross/tests-other/msl_constexpr_test.spv b/third_party/spirv-cross/tests-other/msl_constexpr_test.spv new file mode 100644 index 0000000000000000000000000000000000000000..5201d5106b38dc92f6a437201a791622b427f344 GIT binary patch literal 1656 zcmYk6+fGzL5Jj80fudYQK)}n4ctO!o5fDW1!9-$0V$4L1j}t*O0mh7?&;FWU<%@}H zojGN?NTqx4+Fe~$r-y1~aX6)+G@M4#^E9x@RDnrpG&PEKcdxV8{`P&py}I^L#aOBq zLN()@tg-6H-7hBw&f>H91Uh*xU{@t;O20W&ht2ik2C+}4Cm)ws>-?GHWxsp)qIcBm z7hEl!XFYv&+&w&4DKwMfPWSlh(LsNu&{T_if4qH1ypeAF*LOa5_j^AFnnt>nor{Qf z+FaygTjY3>aRY5iyVPraR~2DmYrBryW8hYXtu>E}jXMYR&MVJn?YUm8-pQ-ChdeGe zUN7VJVtgpW#w#VZuRJb}{8OmZwTiDcI5We31g=?lEuCSt7P&3z5jzvPInXGpG4IbU zaFa9V%79vPh@GQ0PX*(w>Lv=tw>Hp;ou9VAjZ>`Vm_<9y>b=(C*%?;vl=lv?vzAkT ziB-M*iWf@kcbNuXVQu2ta*5+R*{3s%KFt{xduv!MNwwA>_Iqk#`|J zF?&=i_S66%;Fp;8=v<+esSXzu}RFKljj9g6bo#{UXAuXygc}HFrx0FKTRIK z*x@V2n;Mvo^WGT&%UUdVfL_7-~=>eV{)(5_@&POaE`2(9x7?UfV0)r#xPt?;*9 zrFI^EbGZVq^6qPFsPhchvq;W&i#Iaod~Z_Q&u>wGi)=4>^Tl4 z$ +#include +#include +#include + +#define SPVC_CHECKED_CALL(x) do { \ + if ((x) != SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ +} while(0) + +static std::vector read_file(const char *path) +{ + long len; + FILE *file = fopen(path, "rb"); + + if (!file) + return {}; + + fseek(file, 0, SEEK_END); + len = ftell(file); + rewind(file); + + std::vector buffer(len / sizeof(SpvId)); + if (fread(buffer.data(), 1, len, file) != (size_t)len) + { + fclose(file); + return {}; + } + + fclose(file); + return buffer; +} + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + auto buffer = read_file(argv[1]); + if (buffer.empty()) + return EXIT_FAILURE; + + spvc_context ctx; + spvc_parsed_ir parsed_ir; + spvc_compiler compiler; + + SPVC_CHECKED_CALL(spvc_context_create(&ctx)); + SPVC_CHECKED_CALL(spvc_context_parse_spirv(ctx, buffer.data(), buffer.size(), &parsed_ir)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(ctx, SPVC_BACKEND_MSL, parsed_ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler)); + SPVC_CHECKED_CALL(spvc_compiler_msl_add_discrete_descriptor_set(compiler, 3)); + + spvc_compiler_options opts; + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler, &opts)); + SPVC_CHECKED_CALL(spvc_compiler_options_set_bool(opts, SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS, SPVC_TRUE)); + SPVC_CHECKED_CALL(spvc_compiler_options_set_uint(opts, SPVC_COMPILER_OPTION_MSL_VERSION, 20000)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler, opts)); + + spvc_msl_resource_binding binding; + spvc_msl_resource_binding_init(&binding); + binding.binding = SPVC_MSL_ARGUMENT_BUFFER_BINDING; + binding.stage = SpvExecutionModelFragment; + binding.desc_set = 0; + binding.msl_buffer = 2; + SPVC_CHECKED_CALL(spvc_compiler_msl_add_resource_binding(compiler, &binding)); + + binding.desc_set = 1; + binding.msl_buffer = 3; + SPVC_CHECKED_CALL(spvc_compiler_msl_add_resource_binding(compiler, &binding)); + + const char *str; + SPVC_CHECKED_CALL(spvc_compiler_compile(compiler, &str)); + + fprintf(stderr, "Output:\n%s\n", str); + + if (!spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 0, SPVC_MSL_ARGUMENT_BUFFER_BINDING)) + return EXIT_FAILURE; + + if (!spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 1, SPVC_MSL_ARGUMENT_BUFFER_BINDING)) + return EXIT_FAILURE; +} + diff --git a/third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test.cpp b/third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test.cpp new file mode 100644 index 0000000..deab27b --- /dev/null +++ b/third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test.cpp @@ -0,0 +1,103 @@ +// Testbench for MSL constexpr samplers, with Y'CbCr conversion. +// It does not validate output, but it's useful for ad-hoc testing. + +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include +#include + +#define SPVC_CHECKED_CALL(x) do { \ + if ((x) != SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ +} while(0) +#define SPVC_CHECKED_CALL_NEGATIVE(x) do { \ + g_fail_on_error = SPVC_FALSE; \ + if ((x) == SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ + g_fail_on_error = SPVC_TRUE; \ +} while(0) + +static std::vector read_file(const char *path) +{ + long len; + FILE *file = fopen(path, "rb"); + + if (!file) + return {}; + + fseek(file, 0, SEEK_END); + len = ftell(file); + rewind(file); + + std::vector buffer(len / sizeof(SpvId)); + if (fread(buffer.data(), 1, len, file) != (size_t)len) + { + fclose(file); + return {}; + } + + fclose(file); + return buffer; +} + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + auto buffer = read_file(argv[1]); + if (buffer.empty()) + return EXIT_FAILURE; + + spvc_context ctx; + spvc_parsed_ir parsed_ir; + spvc_compiler compiler; + spvc_compiler_options options; + + SPVC_CHECKED_CALL(spvc_context_create(&ctx)); + SPVC_CHECKED_CALL(spvc_context_parse_spirv(ctx, buffer.data(), buffer.size(), &parsed_ir)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(ctx, SPVC_BACKEND_MSL, parsed_ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler)); + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler, &options)); + SPVC_CHECKED_CALL(spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_MSL_VERSION, SPVC_MAKE_MSL_VERSION(2, 0, 0))); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler, options)); + + spvc_msl_resource_binding binding; + spvc_msl_resource_binding_init(&binding); + binding.desc_set = 1; + binding.binding = 2; + binding.stage = SpvExecutionModelFragment; + binding.msl_texture = 0; + binding.msl_sampler = 0; + SPVC_CHECKED_CALL(spvc_compiler_msl_add_resource_binding(compiler, &binding)); + + spvc_msl_constexpr_sampler samp; + spvc_msl_sampler_ycbcr_conversion conv; + spvc_msl_constexpr_sampler_init(&samp); + spvc_msl_sampler_ycbcr_conversion_init(&conv); + conv.planes = 3; + conv.resolution = SPVC_MSL_FORMAT_RESOLUTION_422; + conv.chroma_filter = SPVC_MSL_SAMPLER_FILTER_LINEAR; + conv.x_chroma_offset = SPVC_MSL_CHROMA_LOCATION_MIDPOINT; + conv.ycbcr_model = SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020; + conv.ycbcr_range = SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW; + conv.bpc = 8; + SPVC_CHECKED_CALL(spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(compiler, 1, 2, &samp, &conv)); + + const char *str; + SPVC_CHECKED_CALL(spvc_compiler_compile(compiler, &str)); + + // Should be marked, as a sanity check. + if (!spvc_compiler_msl_is_resource_used(compiler, SpvExecutionModelFragment, 1, 2)) + return EXIT_FAILURE; + + fprintf(stderr, "Output:\n%s\n", str); +} + diff --git a/third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test.spv b/third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test.spv new file mode 100644 index 0000000000000000000000000000000000000000..62372d5c652d451a87af8389c634f934b8acd3f0 GIT binary patch literal 728 zcmYk3%Syvw5QWF2Y1C>j)>_-Dq`DD=;zC6c-3WG55Gm?9c%#sZROLN^58?qCH{{Z2a4wN!GUt#7SrcL*+4&f+|J|oo7s3acbxBfem^|Acv0M97*9UN zuY|3yC^dTk378r3n3{blyAcO>rOZ;-mpQxYvMh_gC4WUWa$2xeS>AxVRDFiwT4GCS zjQ)Aaa9eMzDbP`uby@P8>Qk1bCw(H}iYztoyaQ$zIP$BqdCnm$L>2)c;s4_<92Y_O#hADApZaW literal 0 HcmV?d00001 diff --git a/third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test_2.spv b/third_party/spirv-cross/tests-other/msl_ycbcr_conversion_test_2.spv new file mode 100644 index 0000000000000000000000000000000000000000..10fa7690d0dbc416e42af9b2e65ace610c182950 GIT binary patch literal 840 zcmZ9KNlyYn5QPhdVQ~df5OD#$a3X5l2+@m)T(}YSJjNv^;22O3{ycw`7Zcyt%$SiP z#p`;tcaN9e%bAz5oaL=yQz=>6Qaa9C)?u$d=(i_t_wA$OeIl03bAiMxD>EZRN5S(8 zjx}jbT2vf-PU`8G7Jezo7n_Xt#VpA87>2FM;XzkNF`oLHP2Aa6AG5n){LjDi=g(Nl z`9E6%<@#=ht8wsj84km-<2*Oh$Muc#Z;5*!1f$pC18(bSBIe%4kYKYz(k5qD%HH^~ zn^N{F>mO{oO{ZK%=d8Gfv}Of&57^`7C4W(Pu6%Z^L_9V#ZAh8tR$bVsE+q#3LUq^;Znhua3X<%{w?e<7 euoLaFsWY?To2|hfNWABY&T$^!1U + +using namespace spirv_cross; + +// Test the tricky bits of the implementation. +// Running the entire test suite on this implementation should find all other potential issues. + +static int allocations = 0; +static int deallocations = 0; + +#define SPVC_ASSERT(x) do { \ + if (!(x)) SPIRV_CROSS_THROW("Assert: " #x " failed!"); \ +} while(0) + +struct RAIIInt +{ + RAIIInt(int v_) : v(v_) { allocations++; } + ~RAIIInt() { deallocations++; } + RAIIInt() { allocations++; } + RAIIInt(const RAIIInt &other) { v = other.v; allocations++; } + RAIIInt(RAIIInt &&other) SPIRV_CROSS_NOEXCEPT { v = other.v; allocations++; } + RAIIInt &operator=(RAIIInt &&) = default; + RAIIInt &operator=(const RAIIInt &) = default; + + int v = 0; +}; + +static void propagate_stack_to_heap() +{ + SmallVector ints; + ints.emplace_back(1); + ints.emplace_back(2); + auto *old_data = ints.data(); + SPVC_ASSERT(ints[0].v == 1); + SPVC_ASSERT(ints[1].v == 2); + ints.emplace_back(3); + SPVC_ASSERT(old_data != ints.data()); + SPVC_ASSERT(ints[0].v == 1); + SPVC_ASSERT(ints[1].v == 2); + SPVC_ASSERT(ints[2].v == 3); + SPVC_ASSERT(ints.size() == 3); +} + +static void insert_end() +{ + SmallVector ints; + ints.emplace_back(1); + ints.emplace_back(2); + + const RAIIInt new_ints[3] = { 10, 20, 30 }; + ints.insert(ints.end(), new_ints, new_ints + 3); + SPVC_ASSERT(ints.size() == 5); + + SPVC_ASSERT(ints[0].v == 1); + SPVC_ASSERT(ints[1].v == 2); + SPVC_ASSERT(ints[2].v == 10); + SPVC_ASSERT(ints[3].v == 20); + SPVC_ASSERT(ints[4].v == 30); +} + +static void insert_begin_realloc() +{ + SmallVector ints; + ints.emplace_back(1); + ints.emplace_back(2); + + const RAIIInt new_ints[3] = { 10, 20, 30 }; + ints.insert(ints.begin(), new_ints, new_ints + 3); + SPVC_ASSERT(ints.size() == 5); + + SPVC_ASSERT(ints[0].v == 10); + SPVC_ASSERT(ints[1].v == 20); + SPVC_ASSERT(ints[2].v == 30); + SPVC_ASSERT(ints[3].v == 1); + SPVC_ASSERT(ints[4].v == 2); +} + +static void insert_middle_realloc() +{ + SmallVector ints; + ints.emplace_back(1); + ints.emplace_back(2); + + const RAIIInt new_ints[3] = { 10, 20, 30 }; + ints.insert(ints.begin() + 1, new_ints, new_ints + 3); + SPVC_ASSERT(ints.size() == 5); + + SPVC_ASSERT(ints[0].v == 1); + SPVC_ASSERT(ints[1].v == 10); + SPVC_ASSERT(ints[2].v == 20); + SPVC_ASSERT(ints[3].v == 30); + SPVC_ASSERT(ints[4].v == 2); +} + +static void insert_begin_no_realloc() +{ + SmallVector ints; + ints.reserve(10); + ints.emplace_back(1); + ints.emplace_back(2); + + const RAIIInt new_ints[3] = { 10, 20, 30 }; + ints.insert(ints.begin(), new_ints, new_ints + 3); + SPVC_ASSERT(ints.size() == 5); + + SPVC_ASSERT(ints[0].v == 10); + SPVC_ASSERT(ints[1].v == 20); + SPVC_ASSERT(ints[2].v == 30); + SPVC_ASSERT(ints[3].v == 1); + SPVC_ASSERT(ints[4].v == 2); +} + +static void insert_middle_no_realloc() +{ + SmallVector ints; + ints.reserve(10); + ints.emplace_back(1); + ints.emplace_back(2); + + const RAIIInt new_ints[3] = { 10, 20, 30 }; + ints.insert(ints.begin() + 1, new_ints, new_ints + 3); + SPVC_ASSERT(ints.size() == 5); + + SPVC_ASSERT(ints[0].v == 1); + SPVC_ASSERT(ints[1].v == 10); + SPVC_ASSERT(ints[2].v == 20); + SPVC_ASSERT(ints[3].v == 30); + SPVC_ASSERT(ints[4].v == 2); +} + +static void erase_end() +{ + SmallVector ints; + ints.emplace_back(1); + ints.emplace_back(2); + ints.emplace_back(3); + ints.emplace_back(4); + ints.erase(ints.begin() + 1, ints.end()); + + SPVC_ASSERT(ints.size() == 1); + SPVC_ASSERT(ints[0].v == 1); +} + +static void erase_middle() +{ + SmallVector ints; + ints.emplace_back(1); + ints.emplace_back(2); + ints.emplace_back(3); + ints.emplace_back(4); + ints.erase(ints.begin() + 1, ints.end() - 1); + + SPVC_ASSERT(ints.size() == 2); + SPVC_ASSERT(ints[0].v == 1); + SPVC_ASSERT(ints[1].v == 4); +} + +static void erase_start() +{ + SmallVector ints; + ints.emplace_back(1); + ints.emplace_back(2); + ints.emplace_back(3); + ints.emplace_back(4); + ints.erase(ints.begin(), ints.end() - 2); + + SPVC_ASSERT(ints.size() == 2); + SPVC_ASSERT(ints[0].v == 3); + SPVC_ASSERT(ints[1].v == 4); +} + +static void convert_to_std_vector() +{ + SmallVector foo; + foo.push_back(1); + foo.push_back(2); + std::vector ints(foo); + SPVC_ASSERT(ints.size() == 2); + SPVC_ASSERT(foo.size() == 2); + SPVC_ASSERT(ints[0].v == 1); + SPVC_ASSERT(ints[1].v == 2); + + // This doesn't work on MSVC 2013. Ignore it. +#if !(defined(_MSC_VER) && _MSC_VER < 1900) + SmallVector> move_only_buffer; + move_only_buffer.emplace_back(new RAIIInt(40)); + std::vector> move_only_vector(std::move(move_only_buffer)); + SPVC_ASSERT(move_only_vector.size() == 1); + SPVC_ASSERT(move_only_vector[0]->v == 40); +#endif +} + +int main() +{ + propagate_stack_to_heap(); + insert_end(); + insert_begin_realloc(); + insert_begin_no_realloc(); + insert_middle_realloc(); + insert_middle_no_realloc(); + erase_end(); + erase_middle(); + erase_start(); + + convert_to_std_vector(); + + SPVC_ASSERT(allocations > 0 && deallocations > 0 && deallocations == allocations); +} + diff --git a/third_party/spirv-cross/tests-other/typed_id_test.cpp b/third_party/spirv-cross/tests-other/typed_id_test.cpp new file mode 100644 index 0000000..e8ecb16 --- /dev/null +++ b/third_party/spirv-cross/tests-other/typed_id_test.cpp @@ -0,0 +1,49 @@ +#include "spirv_common.hpp" + +using namespace SPIRV_CROSS_NAMESPACE; + +int main() +{ + // Construct from uint32_t. + VariableID var_id = 10; + TypeID type_id = 20; + ConstantID constant_id = 30; + + // Assign from uint32_t. + var_id = 100; + type_id = 40; + constant_id = 60; + + // Construct generic ID. + ID generic_var_id = var_id; + ID generic_type_id = type_id; + ID generic_constant_id = constant_id; + + // Assign generic id. + generic_var_id = var_id; + generic_type_id = type_id; + generic_constant_id = constant_id; + + // Assign generic ID to typed ID + var_id = generic_var_id; + type_id = generic_type_id; + constant_id = generic_constant_id; + + // Implicit conversion to uint32_t. + uint32_t a; + a = var_id; + a = type_id; + a = constant_id; + a = generic_var_id; + a = generic_type_id; + a = generic_constant_id; + + // Copy assignment. + var_id = VariableID(10); + type_id = TypeID(10); + constant_id = ConstantID(10); + + // These operations are blocked, assign or construction from mismatched types. + //var_id = type_id; + //var_id = TypeID(100); +} \ No newline at end of file diff --git a/third_party/spirv-cross/tnt/CMakeLists.txt b/third_party/spirv-cross/tnt/CMakeLists.txt new file mode 100644 index 0000000..25da487 --- /dev/null +++ b/third_party/spirv-cross/tnt/CMakeLists.txt @@ -0,0 +1,94 @@ +# Copyright 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.19) +project(SPIRV-Cross) + +# Use assertions instead of exceptions so we can succesfully compile with -fno-exceptions. +option(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS "Instead of throwing exceptions assert" ON) + +if(${CMAKE_GENERATOR} MATCHES "Makefile") + if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) + message(FATAL_ERROR "Build out of tree to avoid overwriting Makefile") + endif() +endif() + +set(spirv-compiler-options "") +set(spirv-compiler-defines "") +set(spirv-compiler-public-defines "") + +if(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) + # This needs to be public, because exceptions are exposed in public headers. + set(spirv-compiler-public-defines ${spirv-compiler-public-defines} SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) +endif() + +# To specify special debug or optimization options, use +# -DCMAKE_CXX_COMPILE_FLAGS +# However, we require the C++11 dialect. +if (NOT "${MSVC}") + set(spirv-compiler-options ${spirv-compiler-options} -std=c++11 -Wall -Wextra -Werror -Wshadow) + set(spirv-compiler-defines ${spirv-compiler-defines} __STDC_LIMIT_MACROS) + + if(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) + set(spirv-compiler-options ${spirv-compiler-options} -fno-exceptions) + endif() +endif() + +macro(extract_headers out_abs file_list) + set(${out_abs}) # absolute paths + foreach(_a ${file_list}) + # get_filename_component only returns the longest extension, so use a regex + string(REGEX REPLACE ".*\\.(h|hpp)" "\\1" ext ${_a}) + if(("${ext}" STREQUAL "h") OR ("${ext}" STREQUAL "hpp")) + list(APPEND ${out_abs} "${_a}") + endif() + endforeach() +endmacro() + +macro(spirv_cross_add_library name config_name) + add_library(${name} ${ARGN}) + extract_headers(hdrs "${ARGN}") + target_include_directories(${name} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..) + set_target_properties(${name} PROPERTIES + PUBLIC_HEADERS "${hdrs}") + target_compile_options(${name} PRIVATE ${spirv-compiler-options}) + target_compile_definitions(${name} PRIVATE ${spirv-compiler-defines}) + target_compile_definitions(${name} PUBLIC ${spirv-compiler-public-defines}) + target_compile_options(${name} PRIVATE $<$:-fPIC>) +endmacro() + +spirv_cross_add_library(spirv-cross-core spirv_cross_core STATIC + ../GLSL.std.450.h + ../spirv_common.hpp + ../spirv_cross_containers.hpp + ../spirv_cross_error_handling.hpp + ../spirv.hpp + ../spirv_cross.hpp + ../spirv_cross.cpp + ../spirv_parser.hpp + ../spirv_parser.cpp + ../spirv_cross_parsed_ir.hpp + ../spirv_cross_parsed_ir.cpp + ../spirv_cfg.hpp + ../spirv_cfg.cpp) + +spirv_cross_add_library(spirv-cross-glsl spirv_cross_glsl STATIC + ../spirv_glsl.cpp + ../spirv_glsl.hpp) + +spirv_cross_add_library(spirv-cross-msl spirv_cross_msl STATIC + ../spirv_msl.cpp + ../spirv_msl.hpp) + +target_link_libraries(spirv-cross-glsl spirv-cross-msl spirv-cross-core) diff --git a/third_party/spirv-cross/tnt/README.md b/third_party/spirv-cross/tnt/README.md new file mode 100644 index 0000000..2f92a2d --- /dev/null +++ b/third_party/spirv-cross/tnt/README.md @@ -0,0 +1,31 @@ +## Updating + +To update to the spirv-cross that's currently on GitHub master, do the following. + +``` +cd third_party +curl -L https://github.com/KhronosGroup/SPIRV-Cross/archive/master.zip > master.zip +unzip master.zip +rsync -r SPIRV-Cross-master/ spirv-cross/ --delete +git checkout spirv-cross/tnt/* +rm -rf SPIRV-Cross-master master.zip +git add spirv-cross +``` + +Please be sure to test Filament before uploading your CL. + +## Filament-specific changes to CMakeLists.txt + +The Filament-specific `CMakeLists.txt` under the `tnt` directory has the following changes made from +`spirv-cross`'s provided `CMakeLists.txt`: +- Exceptions turned off in favor of assertions +- Removal of installation rules +- Removal of unused `spirv-cross` libraries +- Removal of the `spirv-cross` executable +- Removal of `spirv-cross` test cases + +To see all changes, run the following diff command from the `third_party/spirv-cross` directory: + +``` +diff CMakeLists.txt tnt/CMakeLists.txt +``` diff --git a/third_party/spirv-cross/update_test_shaders.sh b/third_party/spirv-cross/update_test_shaders.sh new file mode 100755 index 0000000..c622eef --- /dev/null +++ b/third_party/spirv-cross/update_test_shaders.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +./test_shaders.sh --update + diff --git a/third_party/spirv-tools/.appveyor.yml b/third_party/spirv-tools/.appveyor.yml new file mode 100644 index 0000000..0a4cca0 --- /dev/null +++ b/third_party/spirv-tools/.appveyor.yml @@ -0,0 +1,90 @@ +# Windows Build Configuration for AppVeyor +# http://www.appveyor.com/docs/appveyor-yml + +# version format +version: "{build}" + +# The most recent compiler gives the most interesting new results. +# Put it first so we get its feedback first. +os: + - Visual Studio 2017 + #- Visual Studio 2013 + +platform: + - x64 + +configuration: + - Debug + #- Release + +branches: + only: + - master + +# Travis advances the master-tot tag to current top of the tree after +# each push into the master branch, because it relies on that tag to +# upload build artifacts to the master-tot release. This will cause +# double testing for each push on Appveyor: one for the push, one for +# the tag advance. Disable testing tags. +skip_tags: true + +clone_depth: 1 + +matrix: + fast_finish: true # Show final status immediately if a test fails. + #exclude: + # - os: Visual Studio 2013 + # configuration: Debug + +# scripts that run after cloning repository +install: + # Install ninja + - set NINJA_URL="https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-win.zip" + - appveyor DownloadFile %NINJA_URL% -FileName ninja.zip + - 7z x ninja.zip -oC:\ninja > nul + - set PATH=C:\ninja;C:\Python36;%PATH% + +before_build: + - git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers + - git clone https://github.com/google/googletest.git external/googletest + - cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. + - git clone --depth=1 https://github.com/google/effcee.git external/effcee + - git clone --depth=1 https://github.com/google/re2.git external/re2 + # Set path and environment variables for the current Visual Studio version + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" (call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64) + - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86_amd64) + +build: + parallel: true # enable MSBuild parallel builds + verbosity: minimal + +build_script: + - mkdir build && cd build + - cmake -GNinja -DCMAKE_BUILD_TYPE=%CONFIGURATION% -DCMAKE_INSTALL_PREFIX=install -DRE2_BUILD_TESTING=OFF .. + - ninja install + +test_script: + - ctest -C %CONFIGURATION% --output-on-failure --timeout 310 + +after_test: + # Zip build artifacts for uploading and deploying + - cd install + - 7z a SPIRV-Tools-master-windows-"%PLATFORM%"-"%CONFIGURATION%".zip *\* + +artifacts: + - path: build\install\*.zip + name: artifacts-zip + +deploy: + - provider: GitHub + auth_token: + secure: TMfcScKzzFIm1YgeV/PwCRXFDCw8Xm0wY2Vb2FU6WKlbzb5eUITTpr6I5vHPnAxS + release: master-tot + description: "Continuous build of the latest master branch by Appveyor and Travis CI" + artifact: artifacts-zip + draft: false + prerelease: false + force_update: true + on: + branch: master + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 diff --git a/third_party/spirv-tools/.clang-format b/third_party/spirv-tools/.clang-format new file mode 100644 index 0000000..1d43c4e --- /dev/null +++ b/third_party/spirv-tools/.clang-format @@ -0,0 +1,6 @@ +--- +Language: Cpp +BasedOnStyle: Google +DerivePointerAlignment: false +SortIncludes: true +... diff --git a/third_party/spirv-tools/.gitignore b/third_party/spirv-tools/.gitignore new file mode 100644 index 0000000..22fb106 --- /dev/null +++ b/third_party/spirv-tools/.gitignore @@ -0,0 +1,32 @@ +.clang_complete +.ycm_extra_conf.py* +*.pyc +compile_commands.json +/build/ +/buildtools/ +/external/googletest +/external/effcee +/external/re2 +/external/protobuf +/out +/TAGS +/third_party/llvm-build/ +/testing +/tools/clang/ +/utils/clang-format-diff.py +bazel-bin +bazel-genfiles +bazel-out +bazel-spirv-tools +bazel-testlogs + +# Vim +[._]*.s[a-w][a-z] +*~ + +# C-Lion +/.idea/ +/cmake-build-*/ + +# VSCode +/.vscode/* diff --git a/third_party/spirv-tools/.gn b/third_party/spirv-tools/.gn new file mode 100644 index 0000000..377a97a --- /dev/null +++ b/third_party/spirv-tools/.gn @@ -0,0 +1,20 @@ +# Copyright 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +buildconfig = "//build/config/BUILDCONFIG.gn" + +default_args = { + clang_use_chrome_plugins = false + use_custom_libcxx = false +} diff --git a/third_party/spirv-tools/Android.mk b/third_party/spirv-tools/Android.mk new file mode 100644 index 0000000..0b64ea6 --- /dev/null +++ b/third_party/spirv-tools/Android.mk @@ -0,0 +1,337 @@ +LOCAL_PATH := $(call my-dir) +SPVTOOLS_OUT_PATH=$(if $(call host-path-is-absolute,$(TARGET_OUT)),$(TARGET_OUT),$(abspath $(TARGET_OUT))) + +ifeq ($(SPVHEADERS_LOCAL_PATH),) + SPVHEADERS_LOCAL_PATH := $(LOCAL_PATH)/external/spirv-headers +endif + +SPVTOOLS_SRC_FILES := \ + source/assembly_grammar.cpp \ + source/binary.cpp \ + source/diagnostic.cpp \ + source/disassemble.cpp \ + source/ext_inst.cpp \ + source/enum_string_mapping.cpp \ + source/extensions.cpp \ + source/libspirv.cpp \ + source/name_mapper.cpp \ + source/opcode.cpp \ + source/operand.cpp \ + source/parsed_operand.cpp \ + source/print.cpp \ + source/software_version.cpp \ + source/spirv_endian.cpp \ + source/spirv_optimizer_options.cpp \ + source/spirv_target_env.cpp \ + source/spirv_validator_options.cpp \ + source/table.cpp \ + source/text.cpp \ + source/text_handler.cpp \ + source/util/bit_vector.cpp \ + source/util/parse_number.cpp \ + source/util/string_utils.cpp \ + source/util/timer.cpp \ + source/val/basic_block.cpp \ + source/val/construct.cpp \ + source/val/function.cpp \ + source/val/instruction.cpp \ + source/val/validation_state.cpp \ + source/val/validate.cpp \ + source/val/validate_adjacency.cpp \ + source/val/validate_annotation.cpp \ + source/val/validate_arithmetics.cpp \ + source/val/validate_atomics.cpp \ + source/val/validate_barriers.cpp \ + source/val/validate_bitwise.cpp \ + source/val/validate_builtins.cpp \ + source/val/validate_capability.cpp \ + source/val/validate_cfg.cpp \ + source/val/validate_composites.cpp \ + source/val/validate_constants.cpp \ + source/val/validate_conversion.cpp \ + source/val/validate_debug.cpp \ + source/val/validate_decorations.cpp \ + source/val/validate_derivatives.cpp \ + source/val/validate_extensions.cpp \ + source/val/validate_execution_limitations.cpp \ + source/val/validate_function.cpp \ + source/val/validate_id.cpp \ + source/val/validate_image.cpp \ + source/val/validate_interfaces.cpp \ + source/val/validate_instruction.cpp \ + source/val/validate_memory.cpp \ + source/val/validate_memory_semantics.cpp \ + source/val/validate_misc.cpp \ + source/val/validate_mode_setting.cpp \ + source/val/validate_layout.cpp \ + source/val/validate_literals.cpp \ + source/val/validate_logicals.cpp \ + source/val/validate_non_uniform.cpp \ + source/val/validate_primitives.cpp \ + source/val/validate_scopes.cpp \ + source/val/validate_small_type_uses.cpp \ + source/val/validate_type.cpp + +SPVTOOLS_OPT_SRC_FILES := \ + source/opt/aggressive_dead_code_elim_pass.cpp \ + source/opt/amd_ext_to_khr.cpp \ + source/opt/basic_block.cpp \ + source/opt/block_merge_pass.cpp \ + source/opt/block_merge_util.cpp \ + source/opt/build_module.cpp \ + source/opt/cfg.cpp \ + source/opt/cfg_cleanup_pass.cpp \ + source/opt/ccp_pass.cpp \ + source/opt/code_sink.cpp \ + source/opt/combine_access_chains.cpp \ + source/opt/compact_ids_pass.cpp \ + source/opt/composite.cpp \ + source/opt/const_folding_rules.cpp \ + source/opt/constants.cpp \ + source/opt/convert_to_half_pass.cpp \ + source/opt/copy_prop_arrays.cpp \ + source/opt/dead_branch_elim_pass.cpp \ + source/opt/dead_insert_elim_pass.cpp \ + source/opt/dead_variable_elimination.cpp \ + source/opt/decompose_initialized_variables_pass.cpp \ + source/opt/decoration_manager.cpp \ + source/opt/debug_info_manager.cpp \ + source/opt/def_use_manager.cpp \ + source/opt/desc_sroa.cpp \ + source/opt/dominator_analysis.cpp \ + source/opt/dominator_tree.cpp \ + source/opt/eliminate_dead_constant_pass.cpp \ + source/opt/eliminate_dead_functions_pass.cpp \ + source/opt/eliminate_dead_functions_util.cpp \ + source/opt/eliminate_dead_members_pass.cpp \ + source/opt/feature_manager.cpp \ + source/opt/fix_storage_class.cpp \ + source/opt/flatten_decoration_pass.cpp \ + source/opt/fold.cpp \ + source/opt/folding_rules.cpp \ + source/opt/fold_spec_constant_op_and_composite_pass.cpp \ + source/opt/freeze_spec_constant_value_pass.cpp \ + source/opt/function.cpp \ + source/opt/generate_webgpu_initializers_pass.cpp \ + source/opt/graphics_robust_access_pass.cpp \ + source/opt/if_conversion.cpp \ + source/opt/inline_pass.cpp \ + source/opt/inline_exhaustive_pass.cpp \ + source/opt/inline_opaque_pass.cpp \ + source/opt/inst_bindless_check_pass.cpp \ + source/opt/inst_buff_addr_check_pass.cpp \ + source/opt/inst_debug_printf_pass.cpp \ + source/opt/instruction.cpp \ + source/opt/instruction_list.cpp \ + source/opt/instrument_pass.cpp \ + source/opt/ir_context.cpp \ + source/opt/ir_loader.cpp \ + source/opt/legalize_vector_shuffle_pass.cpp \ + source/opt/licm_pass.cpp \ + source/opt/local_access_chain_convert_pass.cpp \ + source/opt/local_redundancy_elimination.cpp \ + source/opt/local_single_block_elim_pass.cpp \ + source/opt/local_single_store_elim_pass.cpp \ + source/opt/loop_dependence.cpp \ + source/opt/loop_dependence_helpers.cpp \ + source/opt/loop_descriptor.cpp \ + source/opt/loop_fission.cpp \ + source/opt/loop_fusion.cpp \ + source/opt/loop_fusion_pass.cpp \ + source/opt/loop_peeling.cpp \ + source/opt/loop_unroller.cpp \ + source/opt/loop_unswitch_pass.cpp \ + source/opt/loop_utils.cpp \ + source/opt/mem_pass.cpp \ + source/opt/merge_return_pass.cpp \ + source/opt/module.cpp \ + source/opt/optimizer.cpp \ + source/opt/pass.cpp \ + source/opt/pass_manager.cpp \ + source/opt/private_to_local_pass.cpp \ + source/opt/propagator.cpp \ + source/opt/reduce_load_size.cpp \ + source/opt/redundancy_elimination.cpp \ + source/opt/register_pressure.cpp \ + source/opt/relax_float_ops_pass.cpp \ + source/opt/remove_duplicates_pass.cpp \ + source/opt/replace_invalid_opc.cpp \ + source/opt/scalar_analysis.cpp \ + source/opt/scalar_analysis_simplification.cpp \ + source/opt/scalar_replacement_pass.cpp \ + source/opt/set_spec_constant_default_value_pass.cpp \ + source/opt/simplification_pass.cpp \ + source/opt/split_invalid_unreachable_pass.cpp \ + source/opt/ssa_rewrite_pass.cpp \ + source/opt/strength_reduction_pass.cpp \ + source/opt/strip_atomic_counter_memory_pass.cpp \ + source/opt/strip_debug_info_pass.cpp \ + source/opt/strip_reflect_info_pass.cpp \ + source/opt/struct_cfg_analysis.cpp \ + source/opt/type_manager.cpp \ + source/opt/types.cpp \ + source/opt/unify_const_pass.cpp \ + source/opt/upgrade_memory_model.cpp \ + source/opt/value_number_table.cpp \ + source/opt/vector_dce.cpp \ + source/opt/workaround1209.cpp \ + source/opt/wrap_opkill.cpp + +# Locations of grammar files. +# +SPV_COREUNIFIED1_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/spirv.core.grammar.json +SPV_GLSL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.glsl.std.450.grammar.json +SPV_OPENCL_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.std.100.grammar.json +SPV_DEBUGINFO_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.debuginfo.grammar.json +SPV_CLDEBUGINFO100_GRAMMAR=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json + +define gen_spvtools_grammar_tables +$(call generate-file-dir,$(1)/core.insts-unified1.inc) +$(1)/core.insts-unified1.inc $(1)/operand.kinds-unified1.inc \ +$(1)/glsl.std.450.insts.inc \ +$(1)/opencl.std.insts.inc \ +: \ + $(LOCAL_PATH)/utils/generate_grammar_tables.py \ + $(SPV_COREUNIFIED1_GRAMMAR) \ + $(SPV_GLSL_GRAMMAR) \ + $(SPV_OpenCL_GRAMMAR) \ + $(SPV_DEBUGINFO_GRAMMAR) \ + $(SPV_CLDEBUGINFO100_GRAMMAR) + @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \ + --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \ + --extinst-glsl-grammar=$(SPV_GLSL_GRAMMAR) \ + --extinst-opencl-grammar=$(SPV_OPENCL_GRAMMAR) \ + --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \ + --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \ + --core-insts-output=$(1)/core.insts-unified1.inc \ + --glsl-insts-output=$(1)/glsl.std.450.insts.inc \ + --opencl-insts-output=$(1)/opencl.std.insts.inc \ + --operand-kinds-output=$(1)/operand.kinds-unified1.inc + @echo "[$(TARGET_ARCH_ABI)] Grammar (from unified1) : instructions & operands <= grammar JSON files" +$(LOCAL_PATH)/source/opcode.cpp: $(1)/core.insts-unified1.inc +$(LOCAL_PATH)/source/operand.cpp: $(1)/operand.kinds-unified1.inc +$(LOCAL_PATH)/source/ext_inst.cpp: \ + $(1)/glsl.std.450.insts.inc \ + $(1)/opencl.std.insts.inc \ + $(1)/debuginfo.insts.inc \ + $(1)/opencl.debuginfo.100.insts.inc \ + $(1)/spv-amd-gcn-shader.insts.inc \ + $(1)/spv-amd-shader-ballot.insts.inc \ + $(1)/spv-amd-shader-explicit-vertex-parameter.insts.inc \ + $(1)/spv-amd-shader-trinary-minmax.insts.inc +$(LOCAL_PATH)/source/opt/amd_ext_to_khr.cpp: \ + $(1)/spv-amd-shader-ballot.insts.inc +endef +$(eval $(call gen_spvtools_grammar_tables,$(SPVTOOLS_OUT_PATH))) + + +define gen_spvtools_lang_headers +# Generate language-specific headers. So far we only generate C headers +# $1 is the output directory. +# $2 is the base name of the header file, e.g. "DebugInfo". +# $3 is the grammar file containing token definitions. +$(call generate-file-dir,$(1)/$(2).h) +$(1)/$(2).h : \ + $(LOCAL_PATH)/utils/generate_language_headers.py \ + $(3) + @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_language_headers.py \ + --extinst-grammar=$(3) \ + --extinst-output-path=$(1)/$(2).h + @echo "[$(TARGET_ARCH_ABI)] Generate language specific header for $(2): headers <= grammar" +$(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \ + : $(1)/$(2).h +endef +# We generate language-specific headers for DebugInfo and OpenCL.DebugInfo.100 +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),DebugInfo,$(SPV_DEBUGINFO_GRAMMAR))) +$(eval $(call gen_spvtools_lang_headers,$(SPVTOOLS_OUT_PATH),OpenCLDebugInfo100,$(SPV_CLDEBUGINFO100_GRAMMAR))) + + +define gen_spvtools_vendor_tables +$(call generate-file-dir,$(1)/$(2).insts.inc) +$(1)/$(2).insts.inc : \ + $(LOCAL_PATH)/utils/generate_grammar_tables.py \ + $(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.$(2).grammar.json + @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \ + --extinst-vendor-grammar=$(SPVHEADERS_LOCAL_PATH)/include/spirv/unified1/extinst.$(2).grammar.json \ + --vendor-insts-output=$(1)/$(2).insts.inc \ + --vendor-operand-kind-prefix=$(3) + @echo "[$(TARGET_ARCH_ABI)] Vendor extended instruction set: $(2) tables <= grammar" +$(LOCAL_PATH)/source/ext_inst.cpp: $(1)/$(2).insts.inc +endef +# Vendor and debug extended instruction sets, with grammars from SPIRV-Tools source tree. +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),debuginfo,"")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),opencl.debuginfo.100,"CLDEBUG100_")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-gcn-shader,"")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-ballot,"")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-explicit-vertex-parameter,"")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-trinary-minmax,"")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.clspvreflection,"")) + +define gen_spvtools_enum_string_mapping +$(call generate-file-dir,$(1)/extension_enum.inc.inc) +$(1)/extension_enum.inc $(1)/enum_string_mapping.inc: \ + $(LOCAL_PATH)/utils/generate_grammar_tables.py \ + $(SPV_COREUNIFIED1_GRAMMAR) + @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_grammar_tables.py \ + --spirv-core-grammar=$(SPV_COREUNIFIED1_GRAMMAR) \ + --extinst-debuginfo-grammar=$(SPV_DEBUGINFO_GRAMMAR) \ + --extinst-cldebuginfo100-grammar=$(SPV_CLDEBUGINFO100_GRAMMAR) \ + --extension-enum-output=$(1)/extension_enum.inc \ + --enum-string-mapping-output=$(1)/enum_string_mapping.inc + @echo "[$(TARGET_ARCH_ABI)] Generate enum<->string mapping <= grammar JSON files" +# Generated header extension_enum.inc is transitively included by table.h, which is +# used pervasively. Capture the pervasive dependency. +$(foreach F,$(SPVTOOLS_SRC_FILES) $(SPVTOOLS_OPT_SRC_FILES),$(LOCAL_PATH)/$F ) \ + : $(1)/extension_enum.inc +$(LOCAL_PATH)/source/enum_string_mapping.cpp: $(1)/enum_string_mapping.inc +endef +$(eval $(call gen_spvtools_enum_string_mapping,$(SPVTOOLS_OUT_PATH))) + +define gen_spvtools_build_version_inc +$(call generate-file-dir,$(1)/dummy_filename) +$(1)/build-version.inc: \ + $(LOCAL_PATH)/utils/update_build_version.py \ + $(LOCAL_PATH)/CHANGES + @$(HOST_PYTHON) $(LOCAL_PATH)/utils/update_build_version.py \ + $(LOCAL_PATH) $(1)/build-version.inc + @echo "[$(TARGET_ARCH_ABI)] Generate : build-version.inc <= CHANGES" +$(LOCAL_PATH)/source/software_version.cpp: $(1)/build-version.inc +endef +$(eval $(call gen_spvtools_build_version_inc,$(SPVTOOLS_OUT_PATH))) + +define gen_spvtools_generators_inc +$(call generate-file-dir,$(1)/dummy_filename) +$(1)/generators.inc: \ + $(LOCAL_PATH)/utils/generate_registry_tables.py \ + $(SPVHEADERS_LOCAL_PATH)/include/spirv/spir-v.xml + @$(HOST_PYTHON) $(LOCAL_PATH)/utils/generate_registry_tables.py \ + --xml=$(SPVHEADERS_LOCAL_PATH)/include/spirv/spir-v.xml \ + --generator-output=$(1)/generators.inc + @echo "[$(TARGET_ARCH_ABI)] Generate : generators.inc <= spir-v.xml" +$(LOCAL_PATH)/source/opcode.cpp: $(1)/generators.inc +endef +$(eval $(call gen_spvtools_generators_inc,$(SPVTOOLS_OUT_PATH))) + +include $(CLEAR_VARS) +LOCAL_MODULE := SPIRV-Tools +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include \ + $(SPVHEADERS_LOCAL_PATH)/include \ + $(SPVTOOLS_OUT_PATH) +LOCAL_EXPORT_C_INCLUDES := \ + $(LOCAL_PATH)/include +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror +LOCAL_SRC_FILES:= $(SPVTOOLS_SRC_FILES) +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) +LOCAL_MODULE := SPIRV-Tools-opt +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include \ + $(LOCAL_PATH)/source \ + $(SPVHEADERS_LOCAL_PATH)/include \ + $(SPVTOOLS_OUT_PATH) +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror +LOCAL_STATIC_LIBRARIES:=SPIRV-Tools +LOCAL_SRC_FILES:= $(SPVTOOLS_OPT_SRC_FILES) +include $(BUILD_STATIC_LIBRARY) diff --git a/third_party/spirv-tools/BUILD.bazel b/third_party/spirv-tools/BUILD.bazel new file mode 100644 index 0000000..52290cf --- /dev/null +++ b/third_party/spirv-tools/BUILD.bazel @@ -0,0 +1,514 @@ +load( + ":build_defs.bzl", + "COMMON_COPTS", + "DEBUGINFO_GRAMMAR_JSON_FILE", + "CLDEBUGINFO100_GRAMMAR_JSON_FILE", + "TEST_COPTS", + "base_test", + "generate_core_tables", + "generate_enum_string_mapping", + "generate_extinst_lang_headers", + "generate_glsl_tables", + "generate_opencl_tables", + "generate_vendor_tables", + "link_test", + "opt_test", + "reduce_test", + "util_test", + "val_test", +) + +package( + default_visibility = ["//visibility:private"], +) + +licenses(["notice"]) + +exports_files([ + "CHANGES", + "LICENSE", +]) + +py_binary( + name = "generate_grammar_tables", + srcs = ["utils/generate_grammar_tables.py"], +) + +py_binary( + name = "generate_language_headers", + srcs = ["utils/generate_language_headers.py"], +) + +generate_core_tables("unified1") + +generate_enum_string_mapping("unified1") + +generate_opencl_tables("unified1") + +generate_glsl_tables("unified1") + +generate_vendor_tables("spv-amd-shader-explicit-vertex-parameter") + +generate_vendor_tables("spv-amd-shader-trinary-minmax") + +generate_vendor_tables("spv-amd-gcn-shader") + +generate_vendor_tables("spv-amd-shader-ballot") + +generate_vendor_tables("debuginfo") + +generate_vendor_tables("opencl.debuginfo.100", "CLDEBUG100_") + +generate_vendor_tables("nonsemantic.clspvreflection") + +generate_extinst_lang_headers("DebugInfo", DEBUGINFO_GRAMMAR_JSON_FILE) + +generate_extinst_lang_headers("OpenCLDebugInfo100", CLDEBUGINFO100_GRAMMAR_JSON_FILE) + +py_binary( + name = "generate_registry_tables", + srcs = ["utils/generate_registry_tables.py"], +) + +genrule( + name = "gen_registry_tables", + srcs = ["@spirv_headers//:spirv_xml_registry"], + outs = ["generators.inc"], + cmd = "$(location generate_registry_tables) --xml=$(location @spirv_headers//:spirv_xml_registry) --generator-output=$(location generators.inc)", + tools = [":generate_registry_tables"], +) + +py_binary( + name = "update_build_version", + srcs = ["utils/update_build_version.py"], +) + +genrule( + name = "gen_build_version", + srcs = ["CHANGES"], + outs = ["build-version.inc"], + cmd = "SOURCE_DATE_EPOCH=0 $(location update_build_version) $$(dirname $(location CHANGES)) $(location build-version.inc)", + tools = [":update_build_version"], +) + +# Libraries + +cc_library( + name = "generated_headers", + hdrs = [ + ":gen_build_version", + ":gen_core_tables_unified1", + ":gen_enum_string_mapping", + ":gen_extinst_lang_headers_DebugInfo", + ":gen_extinst_lang_headers_OpenCLDebugInfo100", + ":gen_glsl_tables_unified1", + ":gen_opencl_tables_unified1", + ":gen_registry_tables", + ":gen_vendor_tables_debuginfo", + ":gen_vendor_tables_nonsemantic_clspvreflection", + ":gen_vendor_tables_opencl_debuginfo_100", + ":gen_vendor_tables_spv_amd_gcn_shader", + ":gen_vendor_tables_spv_amd_shader_ballot", + ":gen_vendor_tables_spv_amd_shader_explicit_vertex_parameter", + ":gen_vendor_tables_spv_amd_shader_trinary_minmax", + ], + copts = COMMON_COPTS, +) + +cc_library( + name = "spirv_tools_headers", + hdrs = glob([ + "include/spirv-tools/libspirv.h", + "include/spirv-tools/libspirv.hpp", + "source/*.h", + "source/util/*.h", + "source/val/*.h", + ]), + copts = COMMON_COPTS, + includes = ["source"], + deps = [ + "@spirv_headers//:spirv_c_headers", + ], +) + +cc_library( + name = "spirv_tools", + srcs = glob([ + "source/*.cpp", + "source/util/*.cpp", + "source/val/*.cpp", + ]), + hdrs = [ + "include/spirv-tools/libspirv.h", + "include/spirv-tools/libspirv.hpp", + ], + copts = COMMON_COPTS + select({ + "@bazel_tools//src/conditions:windows": [""], + "//conditions:default": ["-Wno-implicit-fallthrough"], + }), + includes = ["include"], + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + ":generated_headers", + ":spirv_tools_headers", + "@spirv_headers//:spirv_c_headers", + "@spirv_headers//:spirv_common_headers", + ], +) + +cc_library( + name = "spirv_tools_comp", + srcs = glob([ + "source/comp/*.cpp", + "source/comp/*.h", + ]), + copts = COMMON_COPTS, + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + ":generated_headers", + ":spirv_tools", + ":spirv_tools_headers", + "@spirv_headers//:spirv_common_headers", + ], +) + +cc_library( + name = "spirv_tools_opt_headers", + hdrs = glob(["source/opt/*.h"]), + copts = COMMON_COPTS, +) + +cc_library( + name = "spirv_tools_opt", + srcs = glob(["source/opt/*.cpp"]), + hdrs = [ + "include/spirv-tools/instrument.hpp", + "include/spirv-tools/optimizer.hpp", + ], + copts = COMMON_COPTS, + includes = ["include"], + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_headers", + ":spirv_tools_opt_headers", + "@spirv_headers//:spirv_common_headers", + ], +) + +cc_library( + name = "spirv_tools_reduce", + srcs = glob(["source/reduce/*.cpp"]), + hdrs = glob(["source/reduce/*.h"]), + copts = COMMON_COPTS, + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_opt", + ], +) + +cc_library( + name = "spirv_tools_link", + srcs = glob(["source/link/*.cpp"]), + hdrs = ["include/spirv-tools/linker.hpp"], + copts = COMMON_COPTS, + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_opt", + ], +) + +cc_library( + name = "tools_util", + srcs = glob(["tools/util/*.cpp"]), + hdrs = glob(["tools/util/*.h"]), + copts = COMMON_COPTS, + linkstatic = 1, + visibility = ["//visibility:public"], + deps = [":spirv_tools"], +) + +# Tools + +cc_binary( + name = "spirv-as", + srcs = [ + "tools/as/as.cpp", + "tools/io.h", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ], +) + +cc_binary( + name = "spirv-dis", + srcs = [ + "tools/dis/dis.cpp", + "tools/io.h", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ], +) + +cc_binary( + name = "spirv-val", + srcs = [ + "tools/io.h", + "tools/val/val.cpp", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":tools_util", + ], +) + +cc_binary( + name = "spirv-opt", + srcs = [ + "tools/io.h", + "tools/opt/opt.cpp", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_opt", + ":tools_util", + ], +) + +cc_binary( + name = "spirv-reduce", + srcs = [ + "tools/io.h", + "tools/reduce/reduce.cpp", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_opt", + ":spirv_tools_reduce", + ":tools_util", + ], +) + +cc_binary( + name = "spirv-link", + srcs = [ + "tools/io.h", + "tools/link/linker.cpp", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [ + ":spirv_tools", + ":spirv_tools_link", + ], +) + +cc_binary( + name = "spirv-cfg", + srcs = [ + "tools/cfg/bin_to_dot.cpp", + "tools/cfg/bin_to_dot.h", + "tools/cfg/cfg.cpp", + "tools/io.h", + ], + copts = COMMON_COPTS, + visibility = ["//visibility:public"], + deps = [":spirv_tools"], +) + +# Unit tests + +cc_library( + name = "test_common", + testonly = 1, + srcs = [ + "test/test_fixture.h", + "test/unit_spirv.cpp", + "test/unit_spirv.h", + ], + compatible_with = [], + copts = TEST_COPTS, + includes = ["test"], + linkstatic = 1, + deps = [ + ":spirv_tools", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "link_test_common", + testonly = 1, + srcs = ["test/link/linker_fixture.h"], + compatible_with = [], + copts = TEST_COPTS, + linkstatic = 1, + deps = [ + ":spirv_tools_link", + ":test_common", + ], +) + +cc_library( + name = "opt_test_common", + testonly = 1, + srcs = ["test/opt/pass_utils.cpp"], + hdrs = [ + "test/opt/assembly_builder.h", + "test/opt/function_utils.h", + "test/opt/module_utils.h", + "test/opt/pass_fixture.h", + "test/opt/pass_utils.h", + ], + compatible_with = [], + copts = TEST_COPTS, + linkstatic = 1, + deps = [ + ":spirv_tools_opt", + ":test_common", + ], +) + +cc_library( + name = "reduce_test_common", + testonly = 1, + srcs = [ + "test/reduce/reduce_test_util.cpp", + "tools/io.h", + ], + hdrs = ["test/reduce/reduce_test_util.h"], + compatible_with = [], + copts = TEST_COPTS, + linkstatic = 1, + deps = [ + ":spirv_tools_reduce", + ":test_common", + ], +) + +cc_library( + name = "val_test_common", + testonly = 1, + srcs = [ + "test/val/val_code_generator.cpp", + "test/val/val_fixtures.h", + ], + hdrs = [ + "test/val/val_code_generator.h", + ], + compatible_with = [], + copts = TEST_COPTS, + linkstatic = 1, + deps = [":test_common"], +) + +# PCH (precompiled header) tests only work when using CMake and MSVC on Windows, +# so they will be skipped in the Bazel builds. + +[base_test( + name = f[5:-4], # strip test/, .cpp + srcs = [f], +) for f in glob( + ["test/*.cpp"], + exclude = [ + "test/cpp_interface_test.cpp", # has its own base_test below. + "test/log_test.cpp", # has its own base_test below. + "test/pch_test.cpp", # pch tests are skipped. + "test/timer_test.cpp", # has its own base_test below. + ], +)] + +# This test uses unistd.h and does not run on Windows. +base_test( + name = "timer_test", + srcs = select({ + "@bazel_tools//src/conditions:windows": [], + "//conditions:default": ["test/timer_test.cpp"], + }), +) + +base_test( + name = "cpp_interface_test", + srcs = ["test/cpp_interface_test.cpp"], + deps = [":spirv_tools_opt"], +) + +base_test( + name = "log_test", + srcs = ["test/log_test.cpp"], + deps = [":spirv_tools_opt"], +) + +[link_test( + name = f[10:-4], # strip test/link/, .cpp + srcs = [f], +) for f in glob( + ["test/link/*.cpp"], +)] + +[opt_test( + name = f[9:-4], # strip test/opt/, .cpp + srcs = [f], +) for f in glob( + ["test/opt/*.cpp"], + # pch tests are skipped. + exclude = ["test/opt/pch_test_opt.cpp"], +)] + +[opt_test( + name = "dom_tree_" + f[24:-4], # strip test/opt/dominator_tree/, .cpp + srcs = [f], +) for f in glob( + ["test/opt/dominator_tree/*.cpp"], + # pch tests are skipped. + exclude = ["test/opt/dominator_tree/pch_test_opt_dom.cpp"], +)] + +[opt_test( + name = "loop_" + f[28:-4], # strip test/opt/loop_optimizations/, .cpp + srcs = [f], +) for f in glob( + ["test/opt/loop_optimizations/*.cpp"], + # pch tests are skipped. + exclude = ["test/opt/loop_optimizations/pch_test_opt_loop.cpp"], +)] + +[reduce_test( + name = f[12:-4], # strip test/reduce/, .cpp + srcs = [f], +) for f in glob(["test/reduce/*.cpp"])] + +[util_test( + name = f[10:-4], # strip test/util/, .cpp + srcs = [f], +) for f in glob(["test/util/*.cpp"])] + +[val_test( + name = f[9:-4], # strip test/val/, .cpp + srcs = [f], +) for f in glob( + ["test/val/*.cpp"], + exclude = [ + "test/val/pch_test_val.cpp", # pch tests are skipped. + ], +)] + diff --git a/third_party/spirv-tools/BUILD.gn b/third_party/spirv-tools/BUILD.gn new file mode 100644 index 0000000..2387fc6 --- /dev/null +++ b/third_party/spirv-tools/BUILD.gn @@ -0,0 +1,1059 @@ +# Copyright 2018 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build_overrides/build.gni") +import("//build_overrides/spirv_tools.gni") +if (build_with_chromium) { + import("//testing/test.gni") +} + +spirv_headers = spirv_tools_spirv_headers_dir + +template("spvtools_core_tables") { + assert(defined(invoker.version), "Need version in $target_name generation.") + + action("spvtools_core_tables_" + target_name) { + script = "utils/generate_grammar_tables.py" + + version = invoker.version + + core_json_file = + "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" + core_insts_file = "${target_gen_dir}/core.insts-$version.inc" + operand_kinds_file = "${target_gen_dir}/operand.kinds-$version.inc" + debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" + + sources = [ + core_json_file, + debuginfo_insts_file, + cldebuginfo100_insts_file, + ] + outputs = [ + core_insts_file, + operand_kinds_file, + ] + args = [ + "--spirv-core-grammar", + rebase_path(core_json_file, root_build_dir), + "--core-insts-output", + rebase_path(core_insts_file, root_build_dir), + "--extinst-debuginfo-grammar", + rebase_path(debuginfo_insts_file, root_build_dir), + "--extinst-cldebuginfo100-grammar", + rebase_path(cldebuginfo100_insts_file, root_build_dir), + "--operand-kinds-output", + rebase_path(operand_kinds_file, root_build_dir), + ] + } +} + +template("spvtools_core_enums") { + assert(defined(invoker.version), "Need version in $target_name generation.") + + action("spvtools_core_enums_" + target_name) { + script = "utils/generate_grammar_tables.py" + + version = invoker.version + + core_json_file = + "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" + debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" + + extension_enum_file = "${target_gen_dir}/extension_enum.inc" + extension_map_file = "${target_gen_dir}/enum_string_mapping.inc" + + args = [ + "--spirv-core-grammar", + rebase_path(core_json_file, root_build_dir), + "--extinst-debuginfo-grammar", + rebase_path(debuginfo_insts_file, root_build_dir), + "--extinst-cldebuginfo100-grammar", + rebase_path(cldebuginfo100_insts_file, root_build_dir), + "--extension-enum-output", + rebase_path(extension_enum_file, root_build_dir), + "--enum-string-mapping-output", + rebase_path(extension_map_file, root_build_dir), + ] + inputs = [ + core_json_file, + debuginfo_insts_file, + cldebuginfo100_insts_file, + ] + outputs = [ + extension_enum_file, + extension_map_file, + ] + } +} + +template("spvtools_glsl_tables") { + assert(defined(invoker.version), "Need version in $target_name generation.") + + action("spvtools_glsl_tables_" + target_name) { + script = "utils/generate_grammar_tables.py" + + version = invoker.version + + core_json_file = + "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" + glsl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.glsl.std.450.grammar.json" + debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" + + glsl_insts_file = "${target_gen_dir}/glsl.std.450.insts.inc" + + args = [ + "--spirv-core-grammar", + rebase_path(core_json_file, root_build_dir), + "--extinst-debuginfo-grammar", + rebase_path(debuginfo_insts_file, root_build_dir), + "--extinst-cldebuginfo100-grammar", + rebase_path(cldebuginfo100_insts_file, root_build_dir), + "--extinst-glsl-grammar", + rebase_path(glsl_json_file, root_build_dir), + "--glsl-insts-output", + rebase_path(glsl_insts_file, root_build_dir), + ] + inputs = [ + core_json_file, + glsl_json_file, + debuginfo_insts_file, + cldebuginfo100_insts_file, + ] + outputs = [ + glsl_insts_file, + ] + } +} + +template("spvtools_opencl_tables") { + assert(defined(invoker.version), "Need version in $target_name generation.") + + action("spvtools_opencl_tables_" + target_name) { + script = "utils/generate_grammar_tables.py" + + version = invoker.version + + core_json_file = + "${spirv_headers}/include/spirv/$version/spirv.core.grammar.json" + opencl_json_file = "${spirv_headers}/include/spirv/${version}/extinst.opencl.std.100.grammar.json" + debuginfo_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" + cldebuginfo100_insts_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" + + opencl_insts_file = "${target_gen_dir}/opencl.std.insts.inc" + + args = [ + "--spirv-core-grammar", + rebase_path(core_json_file, root_build_dir), + "--extinst-debuginfo-grammar", + rebase_path(debuginfo_insts_file, root_build_dir), + "--extinst-cldebuginfo100-grammar", + rebase_path(cldebuginfo100_insts_file, root_build_dir), + "--extinst-opencl-grammar", + rebase_path(opencl_json_file, root_build_dir), + "--opencl-insts-output", + rebase_path(opencl_insts_file, root_build_dir), + ] + inputs = [ + core_json_file, + opencl_json_file, + debuginfo_insts_file, + cldebuginfo100_insts_file, + ] + outputs = [ + opencl_insts_file, + ] + } +} + +template("spvtools_language_header") { + assert(defined(invoker.name), "Need name in $target_name generation.") + + action("spvtools_language_header_" + target_name) { + script = "utils/generate_language_headers.py" + + name = invoker.name + extinst_output_path = "${target_gen_dir}/${name}.h" + + args = [ + "--extinst-grammar", + rebase_path(invoker.grammar_file, root_build_dir), + "--extinst-output-path", + rebase_path(extinst_output_path, root_build_dir), + ] + inputs = [ + invoker.grammar_file, + ] + outputs = [ + "${extinst_output_path}", + ] + } +} + +template("spvtools_vendor_table") { + assert(defined(invoker.name), "Need name in $target_name generation.") + + action("spvtools_vendor_tables_" + target_name) { + script = "utils/generate_grammar_tables.py" + + name = invoker.name + extinst_vendor_grammar = "${spirv_headers}/include/spirv/unified1/extinst.${name}.grammar.json" + extinst_file = "${target_gen_dir}/${name}.insts.inc" + + args = [ + "--extinst-vendor-grammar", + rebase_path(extinst_vendor_grammar, root_build_dir), + "--vendor-insts-output", + rebase_path(extinst_file, root_build_dir), + "--vendor-operand-kind-prefix", + invoker.operand_kind_prefix + ] + inputs = [ + extinst_vendor_grammar, + ] + outputs = [ + extinst_file, + ] + } +} + +action("spvtools_generators_inc") { + script = "utils/generate_registry_tables.py" + + # TODO(dsinclair): Make work for chrome + xml_file = "${spirv_headers}/include/spirv/spir-v.xml" + inc_file = "${target_gen_dir}/generators.inc" + + sources = [ + xml_file, + ] + outputs = [ + inc_file, + ] + args = [ + "--xml", + rebase_path(xml_file, root_build_dir), + "--generator", + rebase_path(inc_file, root_build_dir), + ] +} + +action("spvtools_build_version") { + script = "utils/update_build_version.py" + + src_dir = "." + inc_file = "${target_gen_dir}/build-version.inc" + + outputs = [ + inc_file, + ] + args = [ + rebase_path(src_dir, root_build_dir), + rebase_path(inc_file, root_build_dir), + ] +} + +spvtools_core_tables("unified1") { + version = "unified1" +} +spvtools_core_enums("unified1") { + version = "unified1" +} +spvtools_glsl_tables("glsl1-0") { + version = "1.0" +} +spvtools_opencl_tables("opencl1-0") { + version = "1.0" +} +spvtools_language_header("debuginfo") { + name = "DebugInfo" + grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.debuginfo.grammar.json" +} +spvtools_language_header("cldebuginfo100") { + name = "OpenCLDebugInfo100" + grammar_file = "${spirv_headers}/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json" +} + +spvtools_vendor_tables = [ + ["spv-amd-shader-explicit-vertex-parameter", "...nil..."], + ["spv-amd-shader-trinary-minmax", "...nil..."], + ["spv-amd-gcn-shader", "...nil..."], + ["spv-amd-shader-ballot", "...nil..."], + ["debuginfo", "...nil..."], + ["opencl.debuginfo.100", "CLDEBUG100_"], + ["nonsemantic.clspvreflection", "...nil..."], +] + +foreach(table_def, spvtools_vendor_tables) { + spvtools_vendor_table(table_def[0]) { + name = table_def[0] + operand_kind_prefix = table_def[1] + } +} + +config("spvtools_public_config") { + include_dirs = [ "include" ] +} + +config("spvtools_internal_config") { + include_dirs = [ + ".", + "$target_gen_dir", + "${spirv_headers}/include", + ] + + configs = [ ":spvtools_public_config" ] + + if (is_clang) { + cflags = [ + "-Wno-implicit-fallthrough", + "-Wno-newline-eof", + ] + } +} + +source_set("spvtools_headers") { + sources = [ + "include/spirv-tools/instrument.hpp", + "include/spirv-tools/libspirv.h", + "include/spirv-tools/libspirv.hpp", + "include/spirv-tools/linker.hpp", + "include/spirv-tools/optimizer.hpp", + ] + + public_configs = [ ":spvtools_public_config" ] +} + +static_library("spvtools") { + deps = [ + ":spvtools_core_tables_unified1", + ":spvtools_generators_inc", + ":spvtools_glsl_tables_glsl1-0", + ":spvtools_language_header_debuginfo", + ":spvtools_language_header_cldebuginfo100", + ":spvtools_opencl_tables_opencl1-0", + ] + foreach(table_def, spvtools_vendor_tables) { + target_name = table_def[0] + deps += [ ":spvtools_vendor_tables_$target_name" ] + } + + sources = [ + "source/assembly_grammar.cpp", + "source/assembly_grammar.h", + "source/binary.cpp", + "source/binary.h", + "source/cfa.h", + "source/diagnostic.cpp", + "source/diagnostic.h", + "source/disassemble.cpp", + "source/disassemble.h", + "source/enum_set.h", + "source/enum_string_mapping.cpp", + "source/enum_string_mapping.h", + "source/ext_inst.cpp", + "source/ext_inst.h", + "source/extensions.cpp", + "source/extensions.h", + "source/instruction.h", + "source/latest_version_glsl_std_450_header.h", + "source/latest_version_opencl_std_header.h", + "source/latest_version_spirv_header.h", + "source/libspirv.cpp", + "source/macro.h", + "source/name_mapper.cpp", + "source/name_mapper.h", + "source/opcode.cpp", + "source/opcode.h", + "source/operand.cpp", + "source/operand.h", + "source/parsed_operand.cpp", + "source/parsed_operand.h", + "source/print.cpp", + "source/print.h", + "source/spirv_constant.h", + "source/spirv_definition.h", + "source/spirv_endian.cpp", + "source/spirv_endian.h", + "source/spirv_optimizer_options.cpp", + "source/spirv_optimizer_options.h", + "source/spirv_target_env.cpp", + "source/spirv_target_env.h", + "source/spirv_validator_options.cpp", + "source/spirv_validator_options.h", + "source/table.cpp", + "source/table.h", + "source/text.cpp", + "source/text.h", + "source/text_handler.cpp", + "source/text_handler.h", + "source/util/bit_vector.cpp", + "source/util/bit_vector.h", + "source/util/bitutils.h", + "source/util/hex_float.h", + "source/util/ilist.h", + "source/util/ilist_node.h", + "source/util/make_unique.h", + "source/util/parse_number.cpp", + "source/util/parse_number.h", + "source/util/small_vector.h", + "source/util/string_utils.cpp", + "source/util/string_utils.h", + "source/util/timer.cpp", + "source/util/timer.h", + ] + + public_deps = [ + ":spvtools_core_enums_unified1", + ":spvtools_headers", + "${spirv_headers}:spv_headers", + ] + + if (build_with_chromium) { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } + configs += [ ":spvtools_internal_config" ] +} + +static_library("spvtools_val") { + sources = [ + "source/val/basic_block.cpp", + "source/val/basic_block.h", + "source/val/construct.cpp", + "source/val/construct.h", + "source/val/decoration.h", + "source/val/function.cpp", + "source/val/function.h", + "source/val/instruction.cpp", + "source/val/validate.cpp", + "source/val/validate.h", + "source/val/validate_adjacency.cpp", + "source/val/validate_annotation.cpp", + "source/val/validate_arithmetics.cpp", + "source/val/validate_atomics.cpp", + "source/val/validate_barriers.cpp", + "source/val/validate_bitwise.cpp", + "source/val/validate_builtins.cpp", + "source/val/validate_capability.cpp", + "source/val/validate_cfg.cpp", + "source/val/validate_composites.cpp", + "source/val/validate_constants.cpp", + "source/val/validate_conversion.cpp", + "source/val/validate_debug.cpp", + "source/val/validate_decorations.cpp", + "source/val/validate_derivatives.cpp", + "source/val/validate_execution_limitations.cpp", + "source/val/validate_extensions.cpp", + "source/val/validate_function.cpp", + "source/val/validate_id.cpp", + "source/val/validate_image.cpp", + "source/val/validate_instruction.cpp", + "source/val/validate_interfaces.cpp", + "source/val/validate_layout.cpp", + "source/val/validate_literals.cpp", + "source/val/validate_logicals.cpp", + "source/val/validate_memory.cpp", + "source/val/validate_memory_semantics.cpp", + "source/val/validate_memory_semantics.h", + "source/val/validate_misc.cpp", + "source/val/validate_mode_setting.cpp", + "source/val/validate_non_uniform.cpp", + "source/val/validate_primitives.cpp", + "source/val/validate_scopes.cpp", + "source/val/validate_scopes.h", + "source/val/validate_small_type_uses.cpp", + "source/val/validate_type.cpp", + "source/val/validation_state.cpp", + "source/val/validation_state.h", + ] + + deps = [ + ":spvtools", + ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_debuginfo", + ] + public_deps = [ + ":spvtools_headers", + ] + + if (build_with_chromium) { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } + configs += [ ":spvtools_internal_config" ] +} + +static_library("spvtools_opt") { + sources = [ + "source/opt/aggressive_dead_code_elim_pass.cpp", + "source/opt/aggressive_dead_code_elim_pass.h", + "source/opt/amd_ext_to_khr.cpp", + "source/opt/amd_ext_to_khr.h", + "source/opt/basic_block.cpp", + "source/opt/basic_block.h", + "source/opt/block_merge_pass.cpp", + "source/opt/block_merge_pass.h", + "source/opt/block_merge_util.cpp", + "source/opt/block_merge_util.h", + "source/opt/build_module.cpp", + "source/opt/build_module.h", + "source/opt/ccp_pass.cpp", + "source/opt/ccp_pass.h", + "source/opt/cfg.cpp", + "source/opt/cfg.h", + "source/opt/cfg_cleanup_pass.cpp", + "source/opt/cfg_cleanup_pass.h", + "source/opt/code_sink.cpp", + "source/opt/code_sink.h", + "source/opt/combine_access_chains.cpp", + "source/opt/combine_access_chains.h", + "source/opt/compact_ids_pass.cpp", + "source/opt/compact_ids_pass.h", + "source/opt/composite.cpp", + "source/opt/composite.h", + "source/opt/const_folding_rules.cpp", + "source/opt/const_folding_rules.h", + "source/opt/constants.cpp", + "source/opt/constants.h", + "source/opt/convert_to_half_pass.cpp", + "source/opt/convert_to_half_pass.h", + "source/opt/copy_prop_arrays.cpp", + "source/opt/copy_prop_arrays.h", + "source/opt/dead_branch_elim_pass.cpp", + "source/opt/dead_branch_elim_pass.h", + "source/opt/dead_insert_elim_pass.cpp", + "source/opt/dead_insert_elim_pass.h", + "source/opt/dead_variable_elimination.cpp", + "source/opt/dead_variable_elimination.h", + "source/opt/decompose_initialized_variables_pass.cpp", + "source/opt/decompose_initialized_variables_pass.h", + "source/opt/decoration_manager.cpp", + "source/opt/decoration_manager.h", + "source/opt/debug_info_manager.cpp", + "source/opt/debug_info_manager.h", + "source/opt/def_use_manager.cpp", + "source/opt/def_use_manager.h", + "source/opt/desc_sroa.cpp", + "source/opt/desc_sroa.h", + "source/opt/dominator_analysis.cpp", + "source/opt/dominator_analysis.h", + "source/opt/dominator_tree.cpp", + "source/opt/dominator_tree.h", + "source/opt/eliminate_dead_constant_pass.cpp", + "source/opt/eliminate_dead_constant_pass.h", + "source/opt/eliminate_dead_functions_pass.cpp", + "source/opt/eliminate_dead_functions_pass.h", + "source/opt/eliminate_dead_functions_util.cpp", + "source/opt/eliminate_dead_functions_util.h", + "source/opt/eliminate_dead_members_pass.cpp", + "source/opt/eliminate_dead_members_pass.h", + "source/opt/empty_pass.h", + "source/opt/feature_manager.cpp", + "source/opt/feature_manager.h", + "source/opt/fix_storage_class.cpp", + "source/opt/fix_storage_class.h", + "source/opt/flatten_decoration_pass.cpp", + "source/opt/flatten_decoration_pass.h", + "source/opt/fold.cpp", + "source/opt/fold.h", + "source/opt/fold_spec_constant_op_and_composite_pass.cpp", + "source/opt/fold_spec_constant_op_and_composite_pass.h", + "source/opt/folding_rules.cpp", + "source/opt/folding_rules.h", + "source/opt/freeze_spec_constant_value_pass.cpp", + "source/opt/freeze_spec_constant_value_pass.h", + "source/opt/function.cpp", + "source/opt/function.h", + "source/opt/generate_webgpu_initializers_pass.cpp", + "source/opt/generate_webgpu_initializers_pass.h", + "source/opt/graphics_robust_access_pass.cpp", + "source/opt/graphics_robust_access_pass.h", + "source/opt/if_conversion.cpp", + "source/opt/if_conversion.h", + "source/opt/inline_exhaustive_pass.cpp", + "source/opt/inline_exhaustive_pass.h", + "source/opt/inline_opaque_pass.cpp", + "source/opt/inline_opaque_pass.h", + "source/opt/inline_pass.cpp", + "source/opt/inline_pass.h", + "source/opt/inst_bindless_check_pass.cpp", + "source/opt/inst_bindless_check_pass.h", + "source/opt/inst_buff_addr_check_pass.cpp", + "source/opt/inst_buff_addr_check_pass.h", + "source/opt/inst_debug_printf_pass.cpp", + "source/opt/inst_debug_printf_pass.h", + "source/opt/instruction.cpp", + "source/opt/instruction.h", + "source/opt/instruction_list.cpp", + "source/opt/instruction_list.h", + "source/opt/instrument_pass.cpp", + "source/opt/instrument_pass.h", + "source/opt/ir_builder.h", + "source/opt/ir_context.cpp", + "source/opt/ir_context.h", + "source/opt/ir_loader.cpp", + "source/opt/ir_loader.h", + "source/opt/iterator.h", + "source/opt/legalize_vector_shuffle_pass.cpp", + "source/opt/legalize_vector_shuffle_pass.h", + "source/opt/licm_pass.cpp", + "source/opt/licm_pass.h", + "source/opt/local_access_chain_convert_pass.cpp", + "source/opt/local_access_chain_convert_pass.h", + "source/opt/local_redundancy_elimination.cpp", + "source/opt/local_redundancy_elimination.h", + "source/opt/local_single_block_elim_pass.cpp", + "source/opt/local_single_block_elim_pass.h", + "source/opt/local_single_store_elim_pass.cpp", + "source/opt/local_single_store_elim_pass.h", + "source/opt/log.h", + "source/opt/loop_dependence.cpp", + "source/opt/loop_dependence.h", + "source/opt/loop_dependence_helpers.cpp", + "source/opt/loop_descriptor.cpp", + "source/opt/loop_descriptor.h", + "source/opt/loop_fission.cpp", + "source/opt/loop_fission.h", + "source/opt/loop_fusion.cpp", + "source/opt/loop_fusion.h", + "source/opt/loop_fusion_pass.cpp", + "source/opt/loop_fusion_pass.h", + "source/opt/loop_peeling.cpp", + "source/opt/loop_peeling.h", + "source/opt/loop_unroller.cpp", + "source/opt/loop_unroller.h", + "source/opt/loop_unswitch_pass.cpp", + "source/opt/loop_unswitch_pass.h", + "source/opt/loop_utils.cpp", + "source/opt/loop_utils.h", + "source/opt/mem_pass.cpp", + "source/opt/mem_pass.h", + "source/opt/merge_return_pass.cpp", + "source/opt/merge_return_pass.h", + "source/opt/module.cpp", + "source/opt/module.h", + "source/opt/null_pass.h", + "source/opt/optimizer.cpp", + "source/opt/pass.cpp", + "source/opt/pass.h", + "source/opt/pass_manager.cpp", + "source/opt/pass_manager.h", + "source/opt/passes.h", + "source/opt/private_to_local_pass.cpp", + "source/opt/private_to_local_pass.h", + "source/opt/propagator.cpp", + "source/opt/propagator.h", + "source/opt/reduce_load_size.cpp", + "source/opt/reduce_load_size.h", + "source/opt/redundancy_elimination.cpp", + "source/opt/redundancy_elimination.h", + "source/opt/reflect.h", + "source/opt/register_pressure.cpp", + "source/opt/register_pressure.h", + "source/opt/relax_float_ops_pass.cpp", + "source/opt/relax_float_ops_pass.h", + "source/opt/remove_duplicates_pass.cpp", + "source/opt/remove_duplicates_pass.h", + "source/opt/replace_invalid_opc.cpp", + "source/opt/replace_invalid_opc.h", + "source/opt/scalar_analysis.cpp", + "source/opt/scalar_analysis.h", + "source/opt/scalar_analysis_nodes.h", + "source/opt/scalar_analysis_simplification.cpp", + "source/opt/scalar_replacement_pass.cpp", + "source/opt/scalar_replacement_pass.h", + "source/opt/set_spec_constant_default_value_pass.cpp", + "source/opt/set_spec_constant_default_value_pass.h", + "source/opt/simplification_pass.cpp", + "source/opt/simplification_pass.h", + "source/opt/split_invalid_unreachable_pass.cpp", + "source/opt/split_invalid_unreachable_pass.h", + "source/opt/ssa_rewrite_pass.cpp", + "source/opt/ssa_rewrite_pass.h", + "source/opt/strength_reduction_pass.cpp", + "source/opt/strength_reduction_pass.h", + "source/opt/strip_atomic_counter_memory_pass.cpp", + "source/opt/strip_atomic_counter_memory_pass.h", + "source/opt/strip_debug_info_pass.cpp", + "source/opt/strip_debug_info_pass.h", + "source/opt/strip_reflect_info_pass.cpp", + "source/opt/strip_reflect_info_pass.h", + "source/opt/struct_cfg_analysis.cpp", + "source/opt/struct_cfg_analysis.h", + "source/opt/tree_iterator.h", + "source/opt/type_manager.cpp", + "source/opt/type_manager.h", + "source/opt/types.cpp", + "source/opt/types.h", + "source/opt/unify_const_pass.cpp", + "source/opt/unify_const_pass.h", + "source/opt/upgrade_memory_model.cpp", + "source/opt/upgrade_memory_model.h", + "source/opt/value_number_table.cpp", + "source/opt/value_number_table.h", + "source/opt/vector_dce.cpp", + "source/opt/vector_dce.h", + "source/opt/workaround1209.cpp", + "source/opt/workaround1209.h", + "source/opt/wrap_opkill.cpp", + "source/opt/wrap_opkill.h", + ] + + deps = [ + ":spvtools", + ":spvtools_language_header_cldebuginfo100", + ":spvtools_language_header_debuginfo", + ":spvtools_vendor_tables_spv-amd-shader-ballot", + ] + public_deps = [ + ":spvtools_headers", + ] + + if (build_with_chromium) { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } + configs += [ ":spvtools_internal_config" ] +} + +static_library("spvtools_link") { + sources = [ + "source/link/linker.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_opt", + ":spvtools_val", + ] + public_deps = [ + ":spvtools_headers", + ] + if (build_with_chromium) { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } + configs += [ ":spvtools_internal_config" ] +} + +static_library("spvtools_reduce") { + sources = [ + "source/reduce/change_operand_reduction_opportunity.cpp", + "source/reduce/change_operand_reduction_opportunity.h", + "source/reduce/change_operand_to_undef_reduction_opportunity.cpp", + "source/reduce/change_operand_to_undef_reduction_opportunity.h", + "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp", + "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h", + "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp", + "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h", + "source/reduce/merge_blocks_reduction_opportunity.cpp", + "source/reduce/merge_blocks_reduction_opportunity.h", + "source/reduce/merge_blocks_reduction_opportunity_finder.cpp", + "source/reduce/merge_blocks_reduction_opportunity_finder.h", + "source/reduce/operand_to_const_reduction_opportunity_finder.cpp", + "source/reduce/operand_to_const_reduction_opportunity_finder.h", + "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp", + "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h", + "source/reduce/operand_to_undef_reduction_opportunity_finder.cpp", + "source/reduce/operand_to_undef_reduction_opportunity_finder.h", + "source/reduce/reducer.cpp", + "source/reduce/reducer.h", + "source/reduce/reduction_opportunity.cpp", + "source/reduce/reduction_opportunity.h", + "source/reduce/reduction_opportunity_finder.cpp", + "source/reduce/reduction_opportunity_finder.h", + "source/reduce/reduction_pass.cpp", + "source/reduce/reduction_pass.h", + "source/reduce/reduction_util.cpp", + "source/reduce/reduction_util.h", + "source/reduce/remove_block_reduction_opportunity.cpp", + "source/reduce/remove_block_reduction_opportunity.h", + "source/reduce/remove_block_reduction_opportunity_finder.cpp", + "source/reduce/remove_block_reduction_opportunity_finder.h", + "source/reduce/remove_function_reduction_opportunity.cpp", + "source/reduce/remove_function_reduction_opportunity.h", + "source/reduce/remove_function_reduction_opportunity_finder.cpp", + "source/reduce/remove_function_reduction_opportunity_finder.h", + "source/reduce/remove_instruction_reduction_opportunity.cpp", + "source/reduce/remove_instruction_reduction_opportunity.h", + "source/reduce/remove_selection_reduction_opportunity.cpp", + "source/reduce/remove_selection_reduction_opportunity.h", + "source/reduce/remove_selection_reduction_opportunity_finder.cpp", + "source/reduce/remove_selection_reduction_opportunity_finder.h", + "source/reduce/remove_struct_member_reduction_opportunity.cpp", + "source/reduce/remove_struct_member_reduction_opportunity.h", + "source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp", + "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h", + "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp", + "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h", + "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp", + "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h", + "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp", + "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h", + "source/reduce/structured_loop_to_selection_reduction_opportunity.cpp", + "source/reduce/structured_loop_to_selection_reduction_opportunity.h", + "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp", + "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h", + "source/spirv_reducer_options.cpp", + "source/spirv_reducer_options.h", + ] + deps = [ + ":spvtools", + ":spvtools_opt", + ] + public_deps = [ + ":spvtools_headers", + ] + if (build_with_chromium) { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } + configs += [ ":spvtools_internal_config" ] +} + +group("SPIRV-Tools") { + public_deps = [ + ":spvtools", + ":spvtools_link", + ":spvtools_opt", + ":spvtools_reduce", + ":spvtools_val", + ] +} + +# The tests are scoped to Chromium to avoid needing to write gtest integration. +# See Chromium's third_party/googletest/BUILD.gn for a complete integration. +if (build_with_chromium) { + test("spvtools_test") { + sources = [ + "test/assembly_context_test.cpp", + "test/assembly_format_test.cpp", + "test/binary_destroy_test.cpp", + "test/binary_endianness_test.cpp", + "test/binary_header_get_test.cpp", + "test/binary_parse_test.cpp", + "test/binary_strnlen_s_test.cpp", + "test/binary_to_text.literal_test.cpp", + "test/binary_to_text_test.cpp", + "test/comment_test.cpp", + "test/enum_set_test.cpp", + "test/enum_string_mapping_test.cpp", + "test/ext_inst.debuginfo_test.cpp", + "test/ext_inst.glsl_test.cpp", + "test/ext_inst.opencl_test.cpp", + "test/ext_inst.cldebug100_test.cpp", + "test/fix_word_test.cpp", + "test/generator_magic_number_test.cpp", + "test/hex_float_test.cpp", + "test/immediate_int_test.cpp", + "test/libspirv_macros_test.cpp", + "test/name_mapper_test.cpp", + "test/named_id_test.cpp", + "test/opcode_make_test.cpp", + "test/opcode_require_capabilities_test.cpp", + "test/opcode_split_test.cpp", + "test/opcode_table_get_test.cpp", + "test/operand_capabilities_test.cpp", + "test/operand_pattern_test.cpp", + "test/operand_test.cpp", + "test/target_env_test.cpp", + "test/test_fixture.h", + "test/text_advance_test.cpp", + "test/text_destroy_test.cpp", + "test/text_literal_test.cpp", + "test/text_start_new_inst_test.cpp", + "test/text_to_binary.annotation_test.cpp", + "test/text_to_binary.barrier_test.cpp", + "test/text_to_binary.constant_test.cpp", + "test/text_to_binary.control_flow_test.cpp", + "test/text_to_binary.debug_test.cpp", + "test/text_to_binary.device_side_enqueue_test.cpp", + "test/text_to_binary.extension_test.cpp", + "test/text_to_binary.function_test.cpp", + "test/text_to_binary.group_test.cpp", + "test/text_to_binary.image_test.cpp", + "test/text_to_binary.literal_test.cpp", + "test/text_to_binary.memory_test.cpp", + "test/text_to_binary.misc_test.cpp", + "test/text_to_binary.mode_setting_test.cpp", + "test/text_to_binary.pipe_storage_test.cpp", + "test/text_to_binary.reserved_sampling_test.cpp", + "test/text_to_binary.subgroup_dispatch_test.cpp", + "test/text_to_binary.type_declaration_test.cpp", + "test/text_to_binary_test.cpp", + "test/text_word_get_test.cpp", + "test/unit_spirv.cpp", + "test/unit_spirv.h", + ] + + deps = [ + ":spvtools", + ":spvtools_language_header_debuginfo", + ":spvtools_language_header_cldebuginfo100", + ":spvtools_val", + "//testing/gmock", + "//testing/gtest", + "//testing/gtest:gtest_main", + "//third_party/googletest:gmock", + "//third_party/googletest:gtest", + ] + + if (is_clang) { + cflags_cc = [ "-Wno-self-assign" ] + } + + configs += [ ":spvtools_internal_config" ] + } +} + +if (spirv_tools_standalone) { + group("fuzzers") { + testonly = true + deps = [ + "test/fuzzers", + ] + } +} + +source_set("spvtools_util_cli_consumer") { + sources = [ + "tools/util/cli_consumer.cpp", + "tools/util/cli_consumer.h", + ] + deps = [ + ":spvtools_headers", + ] + configs += [ ":spvtools_internal_config" ] +} + +source_set("spvtools_software_version") { + sources = [ + "source/software_version.cpp", + ] + deps = [ + ":spvtools_build_version", + ":spvtools_headers", + ] + configs += [ ":spvtools_internal_config" ] +} + +executable("spirv-as") { + sources = [ + "tools/as/as.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_software_version", + ] + configs += [ ":spvtools_internal_config" ] +} + +executable("spirv-dis") { + sources = [ + "tools/dis/dis.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_software_version", + ] + configs += [ ":spvtools_internal_config" ] +} + +executable("spirv-val") { + sources = [ + "tools/val/val.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_software_version", + ":spvtools_util_cli_consumer", + ":spvtools_val", + ] + configs += [ ":spvtools_internal_config" ] +} + +executable("spirv-cfg") { + sources = [ + "tools/cfg/bin_to_dot.cpp", + "tools/cfg/bin_to_dot.h", + "tools/cfg/cfg.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_software_version", + ] + configs += [ ":spvtools_internal_config" ] +} + +executable("spirv-opt") { + sources = [ + "tools/opt/opt.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_opt", + ":spvtools_software_version", + ":spvtools_util_cli_consumer", + ":spvtools_val", + ] + configs += [ ":spvtools_internal_config" ] +} + +executable("spirv-link") { + sources = [ + "tools/link/linker.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_link", + ":spvtools_opt", + ":spvtools_software_version", + ":spvtools_val", + ] + configs += [ ":spvtools_internal_config" ] +} + +if (!is_ios) { + # iOS does not allow std::system calls which spirv-reduce requires + executable("spirv-reduce") { + sources = [ + "tools/reduce/reduce.cpp", + ] + deps = [ + ":spvtools", + ":spvtools_opt", + ":spvtools_reduce", + ":spvtools_software_version", + ":spvtools_util_cli_consumer", + ":spvtools_val", + ] + configs += [ ":spvtools_internal_config" ] + } +} + +group("all_spirv_tools") { + deps = [ + ":spirv-as", + ":spirv-cfg", + ":spirv-dis", + ":spirv-link", + ":spirv-opt", + ":spirv-val", + ] + if (!is_ios) { + deps += [ ":spirv-reduce" ] + } +} diff --git a/third_party/spirv-tools/CHANGES b/third_party/spirv-tools/CHANGES new file mode 100644 index 0000000..3c81249 --- /dev/null +++ b/third_party/spirv-tools/CHANGES @@ -0,0 +1,1157 @@ +Revision history for SPIRV-Tools + +v2020.6 2020-09-24 + - Start SPIRV-Tools v2020.6 + +v2020.5 2020-09-22 + - General + - Enable building with BUILD_SHARED_LIBS=1 (#3490) + - Avoid using /MP4 for clang on windows. (#3662) + - Fix compiler error on macOS with XCode12. (#3836) + - Optimizer + - Preserve OpenCL.DebugInfo.100 through private-to-local pass (#3571) + - Preserve debug info in scalar replacement pass (#3461) + - Debug info preservation in loop-unroll pass (#3548) + - Preserve debug info in dead-insert-elim pass (#3652) + - Improve non-semantic instruction handling in the optimizer (#3693) + - Let ADCE pass check DebugScope (#3703) + - Add undef for inlined void function (#3720) + - Fix SSA-rewrite to remove DebugDeclare for variables without loads (#3719) + - Handle DebugScope in compact-ids pass (#3724) + - Add buffer oob check to bindless instrumentation (#3800) + - Validator + - Update OpenCL capabilities validation (#3149) + - Validator support for non-semantic clspv reflection (#3618) + - OpenCL.DebugInfo.100 DebugTypeArray with variable size (#3549) + - Only validation locations for appropriate execution models (#3656) + - Validate more OpenCL.DebugInfo.100 instructions (#3684) + - Allow DebugTypeTemplate for Type operand (#3702) + - spirv-val: Add Vulkan VUID labels to BuiltIn (#3756) + - Allow SPV_KHR_8bit_storage extension. (#3780) + - Validate SPIRV Version number when parsing binary header (#3834) + - Reduce + - Support reducing a specific function (#3774) + - Fuzz + - adds TransformationReplaceCopyObjectWithStoreLoad (#3567) + - adds TransformationReplaceCopyMemoryWithLoadStore (#3575) + - adds TransformationReplaceLoadStoreWithCopyMemory (#3586) + - Implement the OpOuterProduct linear algebra case (#3617) + - Pass to replace int operands with ints of opposite signedness (#3612) + - TransformationMoveInstructionDown (#3477) + - Add TransformationMakeVectorOperationDynamic (#3597) + - TransformationReplaceAddSubMulWithCarryingExtended (#3598) + - FuzzerPassPropagateInstructionsUp (#3478) + - add FuzzerPassAddCompositeInserts (#3606) + - Add inline function transformation (#3517) + - Transformation to replace the use of an irrelevant id (#3697) + - Add SPIRV_FUZZ_PROTOC_COMMAND (#3789) + - Add TransformationDuplicateRegionWithSelection (#3773) + - Transformation to flatten conditional branch (#3667) + - Handle OpPhis in TransformationInlineFunction (#3833) + - Create synonym of int constant using a loop (#3790) + - Support dead blocks in TransformationAddSynonym (#3832) + - Linker + +v2020.4 2020-07-22 + - General + - Changed variable names to be more descriptive (#3433) + - Add support to GPU-AV instrumentation for Task and Mesh shaders (#3512) + - Permit Simple and GLSL450 memory model in WEBGPU_0 (#3463) + - Support SPV_KHR_terminate_invocation (#3568) + - Optimizer + - Preserving debug information in optimizations + (#3389,#3420,#3425,#3356,#3459,#3444,#3492,#3451,#3497i,#3498,#3542) + - Eliminate branches with condition of OpConstantNull (#3438) + - Use structured order to unroll loops. (#3443) + - Updated desc_sroa to support flattening structures (#3448) + - Support OpCompositeExtract pattern in desc_sroa (#3456) + - Fix ADCE pass bug for mulitple entries (#3470) + - Sink pointer instructions in merge return (#3569) + - Validator + - Validate location assignments (#3308) + - Fix reachability in the validator (#3541) + - Reduce + - Fuzz + - Add support for OpSpecConstant* (#3373) + - Add replace linear algebra instruction transformation (#3402) + - Implement vector shuffle fuzzer pass (#3412) + - Swap operands in OpBranchConditional (#3423) + - Permute OpPhi instruction operands (#3421) + - Add FuzzerPassAddCopyMemoryInstructions (#3391) + - TransformationInvertComparisonOperator (#3475) + - Add variables with workgroup storage class (#3485) + - Add image sample unused components transformation (#3439) + - TransformationReplaceParameterWithGlobal (#3434) + - Support adding dead break from back-edge block (#3519) + - Fuzzer pass to interchange zero-like constants (#3524) + - Linker + +v2020.3 2020-05-27 + - General + - Prevent Effcee from installing things when building spirv-tools with testing enabled (#3256) + - Update acorn version (#3294) + - If SPIRV-Headers is in our tree, include it as subproject (#3299) + - allow cross compiling for Windows Store, UWP, etc. (#3330) + - Optimizer + - Remove deprecated interfaces from instrument passes (#3361) + - Preserve debug info in inline pass (#3349) + - Handle more cases in dead member elim (#3289) + - Preserve debug info in eliminate-dead-functions (#3251) + - Fix Struct CFG analysis for single block loop (#3293) + - Add tests for recently added command line option (#3297) + - Consider sampled images as read-only storage (#3295) + - Allow various validation options to be passed to spirv-opt (#3314) + - Add debug information analysis (#3305) + - Preserve debug info for wrap-opkill (#3331) + - refactor inlining pass (#3328) + - Add unrolling to performance passes (#3082) + - Validator + - Add validation support for ImageGatherBiasLodAMD (#3363) + - Validate ShaderCallKHR memory scope (#3332) + - Validate Buffer and BufferBlock apply only to struct types (#3259) + - Reduce + - increase default step limit (#3327) + - Remove unused uniforms and similar (#3321) + - Fuzz + - Add support for StorageBuffer (#3348) + - Add validator options (#3254) + - Limit adding of new variables to 'basic' types (#3257) + - Transformation to add OpConstantNull (#3273) + - Handling of more fuzzing opportunities (#3277, #3280, #3281, #3290, #3292) + - Respect rules for OpSampledImage (#3287) + - Do not outline regions that produce pointer outputs (#3291) + - Linker + +v2020.2 2020-03-26 + - General: + - Support extended instructions in the vscode language server + - Make spvOpcodeString part of the public API (#3174) + - Added guide to writing a spirv-fuzz fuzzer pass (#3190) + - Add support for KHR_ray_{query,tracing} extensions (#3235) + - Optimizer + - Debug Printf support (#3215) + - Add data structure for DebugScope, DebugDeclare in spirv-opt (#3183) + - Fix identification of Vulkan images and buffers (#3253) + - Validator + - Add support for SPV_AMD_shader_image_load_store_lod (#3186) + - Add validation rules for OpenCL.DebugInfo.100 extension (#3133) + - Adding WebGPU specific Workgroup scope rule (#3204) + - Disallow phis of images, samplers and sampled images (#3246) + - Reduce + - Fuzz + - Fuzzer passes to add local and global variables (#3175) + - Add fuzzer passes to add loads/stores (#3176) + - Fuzzer pass to add function calls (#3178) + - Fuzzer pass that adds access chains (#3182) + - Fuzzer pass to add equation instructions (#3202) + - Add swap commutable operands transformation (#3205) + - Add fuzzer pass to permute function parameters (#3212) + - Allow OpPhi operand to be replaced with a composite synonym (#3221) + - Linker + +v2020.1 2020-02-03 + - General: + - Add support for SPV_KHR_non_semantic_info (#3110) + - Support OpenCL.DebugInfo.100 extended instruction set (#3080) + - Added support for Vulkan 1.2 + - Add API function to better handle getting the necessary environment (#3142) + - Clarify mapping of target env to SPIR-V version (#3150) + - Implement constant folding for many transcendentals (#3166) + - Optimizer + - Change default version for CreatInstBindlessCheckPass to 2 (#3096, #3119) + - Better handling of OpLine on merge blocks (#3130) + - Use placeholder switch instead of placeholder loop in MergeReturn pass. (#3151) + - Handle TimeAMD in AmdExtensionToKhrPass. (#3168) + - Validator + - Fix structured exit validation (#3141) + - Reduce + - Fuzz + - Fuzzer pass to merge blocks (#3097) + - Transformation to add a new function to a module (#3114) + - Add fuzzer pass to perform module donation (#3117) + - Fuzzer passes to create and branch to new dead blocks (#3135) + - Fuzzer pass to add composite types (#3171) + - Linker: + - Remove names and decorations of imported symbols (#3081) + +v2019.5 2019-12-11 + - General: + - Export SPIRV-Tools targets on installation + - SPIRV-Tools support for SPIR-V 1.5 (#2865) + - Add WebGPU SPIR-V Assembler in JavaScript. (#2876) + - Add Bazel build configuration. (#2891) + - Add support for building with emscripten (#2948) + - Update SPIR-V binary header test for SPIR-V 1.5 (#2967) + - Add fuzzer for spirv-as call path (#2976) + - Improved CMake install step. (#2963) + - Add fuzzer for spirv-dis call path (#2977) + - Ensure timestamp does not vary with timezone. (#2982) + - Add a vscode extension for SPIR-V disassembly files (#2987) + - Add iOS as a supported platform (#3001) + - utils/vscode: Add SPIR-V language server support + - Respect CMAKE_INSTALL_LIBDIR in installed CMake files (#3054) + - Permit the debug instructions in WebGPU SPIR-V (#3063) + - Add support for Fuchsia. (#3062) + - Optimizer + - Add descriptor array scalar replacement (#2742) + - Add pass to wrap OpKill in a function call (#2790) + - Fold FMix during constant folding. (#2818) + - Add pass to replace AMD shader ballot extension (#2811) + - Add pass to make Float32 operation relax precision (#2808) + - Add pass to make relax precision operation Float16 (#2808) + - Add pass to replace uses of 3 AMD extensions (#2814) + - Fold Min, Max, and Clamp instructions. (#2836) + - Better handling of OpKill in continues (#2842,#2922,#2933) + - Enable OpTypeCooperativeMatrix specialization (#2927) + - Support constant-folding UConvert and SConvert (#2960) + - Update Offset to ConstOffset bitmask if operand is constant. (#3024) + - Improve RegisterSizePasses (#3059) + - Folding: perform add and sub on mismatched integer types (#3084) + - Graphics robust access: use signed clamp (#3073) + Fixes: + - Instrument: Fix version 2 output record write for tess eval shaders. (#2782) + - Instrument: Add support for Buffer Device Address extension (#2792) + - Fix check for changed binary in API call. (#2798) + - For WebGPU<->Vulkan optimization, set correct execution environment (#2834) + - Handle OpConstantNull in copy-prop-arrays. (#2870) + - Use OpReturn* in wrap-opkill (#2886) + - Validator + - Add generic builtin validation of target (#2843) + - Extra resource interface validation (#2864) + - Adding valilidation checks for OpEntryPoint duplicate names and execution mode (#2862) + - Relaxed bitcast with pointers (#2878) + - Validate physical storage buffer restrictions (#2930) + - Add SPV_KHR_shader_clock validation (#2879, #3013) + - Validate that selections are structured (#2962) + - Disallow use of OpCompositeExtract/OpCompositeInsert with no indices (#2980) + - Check that derivatives operate on 32-bit values (#2983) + - Validate array stride does not cause overlap (#3028) + - Validate nested constructs (#3068) + Fixes: + - Fix validation of constant matrices (#2794) + - Update "remquor" validation + - Only allow previously declared forward refs in structs (#2920) + - Reduce + - Remove relaxed precision decorations (#2797) + - Reduce/fuzz: improve command line args (#2932) + - Improve remove unref instr pass (#2945) + Fixes: + - Fuzz + - Fix add-dead-break and add-dead-continue passes to respect dominance (#2838) + - Add fuzzer pass to copy objects (#2853) + - Add fuzzer pass to replace ids with synonyms (#2857) + - Allow validation during spirv-fuzz replay (#2873) + - Employ the "swarm testing" idea in spirv-fuzz (#2890) + - reduce/fuzz: improve command line args (#2932) + - option to convert shader into a form that renders red (#2934) + - Add fuzzer pass to change selection controls (#2944) + - add transformation and pass to construct composites (#2941) + - Add fuzzer pass to change loop controls (#2949) + - Add fuzzer pass to change function controls (#2951) + - Add fuzzer pass to add NoContraction decorations (#2950) + - Add missing functionality for matrix composites (#2974) + - Fuzzer pass to adjust memory access operands (#2968) + - Transformation to extract from a composite object (#2991) + - Vector shuffle transformation (#3015) + - Improve debugging facilities (#3074) + - Function outlining fuzzer pass (#3078) + + +v2019.4 2019-08-08 + - General: + - Memory model support for SPIR-V 1.4 + - Add new spirv-fuzz tool + - Add option for base branch in check_code_format.sh + - Removed MarkV and Stats code. (#2576) + - Instrument: Add version 2 of record formats (#2630) + - Linker: Better type comparison for OpTypeArray and OpTypeForwardPointer (#2580) + - Optimizer + - Bindless Validation: Instrument descriptor-based loads and stores (#2583) + - Better folding for OpSpecConstantOp (#2585, #2614) + - Add in individual flags for Vulkan <-> WebGPU passes (#2615) + - Handle nested breaks from switches. (#2624) + - Optimizer: Handle array type with OpSpecConstantOp length (#2652) + - Perform merge return with single return in loop. (#2714) + - Add --preserve-bindings and --preserve-spec-constants (#2693) + - Remove Common Uniform Elimination Pass (#2731) + - Allow ray tracing shaders in inst bindle check pass. (#2733) + - Add pass to inject code for robust-buffer-access semantics (#2771) + - Treat access chain indexes as signed in SROA (#2776) + - Handle RelaxedPrecision in SROA (#2788) + - Add descriptor array scalar replacement (#2742) + Fixes: + - Handle decorations better in some optimizations (#2716) + - Change the order branches are simplified in dead branch elim (#2728) + - Fix bug in merge return (#2734) + - SSA rewriter: Don't use trivial phis (#2757) + - Record correct dominators in merge return (#2760) + - Process OpDecorateId in ADCE (#2761) + - Fix check for unreachable blocks in merge-return (#2762) + - Handle out-of-bounds scalar replacements. (#2767) + - Don't move debug or decorations when folding (#2772) + - Protect against out-of-bounds references when folding OpCompositeExtract (#2774) + - Validator + - Validate loop merge (#2579) + - Validate construct exits (#2459) + - Validate OpenCL memory and addressing model environment rules (#2589) + - Validate OpenCL environment rules for OpTypeImage (#2606) + - Allow breaks to switch merge from nested construct (#2604) + - Validate OpenCL environment rules for OpImageWrite (#2619) + - Allow arrays of out per-primitive builtins for mesh shaders (#2617) + - Validate OpenCL rules for ImageRead and OpImageSampleExplicitLod (#2643) + - Add validation for SPV_EXT_fragment_shader_interlock (#2650) + - Add builtin validation for SPV_NV_shader_sm_builtins (#2656) + - Add validation for Subgroup builtins (#2637) + - Validate variable initializer type (#2668) + - Disallow stores to UBOs (#2651)A + - Validate Volatile memory semantics bit (#2672) + - Basic validation for Component decorations (#2679) + - Validate that in OpenGL env block variables have Binding (#2685) + - Validate usage of 8- and 16-bit types with only storage capabilities (#2704) + - Add validation for SPV_EXT_demote_to_helper_invocation (#2707) + - Extra small storage validation (#2732) + - For Vulkan, disallow structures containing opaque types (#2546) + - Validate storage class OpenCL environment rules for atomics (#2750) + - Update OpControlBarriers rules for WebGPU (#2769) + - Update OpMemoryBarriers rules for WebGPU (#2775) + - Update WebGPU validation rules of OpAtomic*s (#2777) + Fixes: + - Disallow merge targeting block with OpLoopMerge (#2610) + - Update vloadn and vstoren validation to match the OpenCL Extended + Instruction Set Specification (#2599) + - Update memory scope rules for WebGPU (#2725) + - Allow LOD ops in compute shaders with derivative group execution modes (#2752) + - Reduce + Fixes: + +v2019.3 2019-05-14 + - General: + - Require Python 3 since Python 2 will out of service soon. + - Add a continuous test that does memory checks using the address sanitizer. + - Fix the build files so the SPIRV_USE_SANITIZER=address build works. + - Packaging top of tree build artifacts again. + - Added support for SPIR-V 1.4. (#2550) + - Optimizer + - Remove duplicates from list of interface IDs in OpEntryPoint instruction (#2449) + - Bindless Validation: Descriptor Initialization Check (#2419) + - Add option to validate after each pass (#2462) + - Add legalization pass to fix mismatched pointer (#2430, #2535) + - Add error messages when the input contains unknown instructions. (#2487) + - Add pass to convert from WebGPU Spir-V to Vulkan Spir-V and back. (#2495) + Fixes: + - #2412: Dead memeber elimination should not change input and output variables. + - #2405: Fix OpDot folding of half float vectors. + - #2391: Dead branch elim should not fold away back edges. + - #2441: Removing decorations when doing constant propagation. + - #2455: Maintain inst to block mapping in merge return. + - #2453: Fix merge return in the face of breaks. + - #2456: Handle dead infinite loops in DCE. + - #2458: Handle variable pointer in some optimizations. + - #2452: Fix dead branch elimination to handle unreachable blocks better. + - #2528: Fix undefined bit shift in sroa. + - #2539: Change implementation of post order CFG traversal. + - Validator + - Add validation of storage classes for WebGPU (#2446) + - Add validation for ExecutionMode in WebGPU (#2443) + - Implement WebGPU specific CFG validation (#2386) + - Allow NonWritable to target struct members. (#2420) + - Allow storage type mismatch for parameter in relaxed addressing mode. + - Allow non memory objects as parameter in relaxed addressing mode. + - Disallow nested Blocks and buffer blocks (#2410). + - Add validation for SPV_NV_cooperative_matrix (#2404) + - Add --strip-atomic-counter-memory (#2413) + - Check OpSampledImage is only passed into valid instructions (#2467) + - Handle function decls in Structured CFG analysis (#2474) + - Validate that OpUnreacahble is not statically reachable (#2473) + - Add pass to generate needed initializers for WebGPU (#2481) + - Allow images without format for OpenCL. (#2470) + - Remove unreachable block validation (#2525) + - Reduce runtime of array layout checks (#2534) + - Add validation specific to OpExecutionModeId (#2536) + - Validate sign of int types. (#2549) + - VK_KHR_uniform_buffer_standard_layout validation (#2562) + Fixes: + - #2439: Add missing DepthGreater case to Fragment only check. + - #2168: Disallow BufferBlock on StorageBuffer variables for Vulkan. + - #2408: Restrict and Aliased decorations cannot be applied to the same id. + - #2447: Improve function call parameter check. + - Reduce + - Add Pass to remove unreferenced blocks. (#2398) + - Allows passing options to the validator. (#2401) + - Improve reducer algorithm and other changes (#2472) + - Add Pass to remove selections (#2485) + - Add passes to simplify branches (#2507) + Fixes: + - #2478: fix loop to selection pass for loops with combined header/continue block + +v2019.2 2019-02-20 + - General: + - Support SPV_EXT_physical_storage_buffer + - A number of memory leak have been fixed. + - Removed use of deprecated Google test macro: + - Changed the BUILD.gn to only build tests in Chromium. + - Optimizer + - Upgrade memory model improvments for modf and frexp. + - Add a new pass to move loads closer to their uses: code sinking. + - Invalidating the type manager now invalidates the constnat manager. + - Expand instrumentation pass for bindless bounds checking to runtime-sized descriptor arrays. + - Add a new pass that removes members from structs that are not used: dead member elimination. + Fixes: + - #2292: Remove undefined behaviour when folding bit shifts. + - #2294: Fixes for instrumentation code. + - #2293: Fix overflow when folding -INT_MIN. + - #2374: Don't merge unreachable blocks when merging blocks. + - Validator + - Support SPV_KHR_no_integer_wrap and related decorations. + - Validate Vulkan rules for OpTypeRuntimeArray. + - Validate NonWritable decoration. + - Many WebGPU specific validation rules were added. + - Validate variable pointer related function call rules. + - Better error messages. + Fixes: + - #2307: Check forwards references in OpTypeArray. + - #2315, #2303: Fixed the layout check for relaxed layout. + - #1628: Emit an error when an OpSwitch target is not an OpLabel. + - Reduce + - Added more documentation for spirv-reduce. + - Add ability to remove OpPhi instructions. + - Add ability to merge two basic blocks. + - Add ability to remove unused functions and unused basic blocks. + Fixes: + +v2019.1 2019-01-07 + - General: + - Created a new tool called spirv-reduce. + - Add cmake option to turn off SPIRV_TIMER_ENABLED (#2103) + - New optimization pass to update the memory model from GLSL450 to VulkanKHR. + - Recognize OpTypeAccelerationStructureNV as a type instruction and ray tracing storage classes. + - Fix GCC8 build. + - Add --target-env flag to spirv-opt. + - Add --webgpu-mode flag to run optimizations for webgpu. + - The output disassembled line number stead of byte offset in validation errors. (#2091) + - Optimizer + - Added the instrumentation passes for bindless validation. + - Added passes to help preserve OpLine information (#2027) + - Add basic support for EXT_fragment_invocation_density (#2100) + - Fix invalid OpPhi generated by merge-return. (#2172) + - Constant and type manager have been turned into analysies. (#2251) + Fixes: + - #2018: Don't inline functions with a return in a structured CFG contstruct. + - #2047: Fix bug in folding when volatile stores are present. + - #2053: Fix check for when folding floating pointer values is allowed. + - #2130: Don't inline recursive functions. + - #2202: Handle multiple edges between two basic blocks in SSA-rewriter. + - #2205: Don't unswitch a latch condition during loop unswitch. + - #2245: Don't fold branch in loop unswitch. Run dead branch elimination to fold them. + - #2204: Fix eliminate common uniform to place OpPhi instructions correctly. + - #2247: Fix type mismatches caused by scalar replacement. + - #2248: Fix missing OpPhi after merge return. + - #2211: After merge return, fix invalid continue target. + - #2210: Fix loop invariant code motion to not place code between merge instruction and branch. + - #2258: Handle CompositeInsert with no indices in VDCE. + - #2261: Have replace load size handle extact with no index. + - Validator + - Changed the naming convention of outputing ids with names in diagnostic messages. + - Added validation rules for UniformConstant variables in Vulkan. + - #1949: Validate uniform variable type in Vulkan + - Ensure for OpVariable that result type and storage class operand agree (#2052) + - Validator: Support VK_EXT_scalar_block_layout + - Added Vulkan memory model semantics validation + - Added validation checkes spefic to WebGPU environment. + - Add support for VK_EXT_Transform_feedback capabilities (#2088) + - Add validation for OpArrayLength. (#2117) + - Ensure that function parameter's type is not void (#2118) + - Validate pointer variables (#2111) + - Add check for QueueFamilyKHMR memory scope (#2144) + - Validate PushConstants annotation and type (#2140) + - Allow Float16/Int8 for Vulkan 1.0 (#2153) + - Check binding annotations in resource variables (#2151, #2167) + - Validate OpForwardPointer (#2156) + - Validate operation for OpSpecConstantOp (#2260) + Fixes: + - #2049: Allow InstanceId for NV ray tracing + - Reduce + - Initial commit wit a few passes to reduce test cases. + - Validation is run after each reduction step. + Fixes: + + +v2018.6 2018-11-07 + - General: + - Added support for the Nvidia Turing and ray tracing extensions. + - Make C++11 the CXX standard in CMakeLists.txt. + - Enabled a parallel build for MSVC. + - Enable pre-compiled headers for MSVC. + - Added a code of conduct. + - EFFCEE and RE2 are now required when build the tests. + - Optimizer + - Unrolling loops marked for unrolling in the legalization passes. + - Improved the compile time of loop unrolling. + - Changee merge-return to create a placeholder loop around the function. + - Small improvement to merge-blocks to allow it to merge more often. + - Enforce an upper bound for the ids, and add option to set it. + - #1966: Report error if there are unreachable block before running merge return + Fixes: + - #1917: Allow 0 (meaning unlimited) as a parameter to --scalar-replacement + - #1915: Improve handling of group decorations. + - #1942: Fix incorrect uses of the constant manager. Avoids type mismatches in generated code. + - #1997: Fix dead branch elimination when there is a loop in folded selection. + - #1991: Fixes legality check in if-conversion. + - #1987: Add nullptr check to array copy propagation. + - #1984: Better handling of OpUnreachable in ADCE. + - #1983: Run merge return on reachable functions only. + - #1956: Handled atomic operations in ADCE. + - #1963: Fold integer divisions by 0 to 0. + - #2019: Handle MemberDecorateStringGOOGLE in ADCE and strip reflect. + - Validator + - Added validation for OpGroupNonUniformBallotBitCount. + - Added validation for the Vulkan memory model. + - Added support for VK_KHR_shader_atddomic_int64. + - Added validation for execution modes. + - Added validation for runtime array layouts. + - Added validation for 8-bit storage. + - Added validation of OpPhi instructions with pointer result type. + - Added checks for the Vulkan memory model. + - Validate MakeTexelAvailableKHR and MakeTexelVisibleKHR + - Allow atomic function pointer for OpenCL. + - FPRounding mode checks were implemented. + - Added validation for the id bound with an option to set the max id bound. + Fixes: + - #1882: Improve the validation of decorations to reduce memory usage. + - #1891: Fix an potential infinite loop in dead-branch-elimination. + - #1405: Validate the storage class of boolean objects. + - #1880: Identify arrays of type void as invalid. + - #487: Validate OpImageTexelPointer. + - #1922: Validate OpPhi instructions are at the start of a block correctly. + - #1923: Validate function scope variable are at the start of the entry block. + +v2018.5 2018-09-07 + - General: + - Support SPV_KHR_vulkan_memory_model + - Update Dim capabilities, to match SPIR-V 1.3 Rev 4 + - Automated build bots no run tests for the VS2013 case + - Support Chromium GN build + - Use Kokoro bots: + - Disable Travis-CI bots + - Disable AppVeyor VisualStudio Release builds. Keep VS 2017 Debug build + - Don't check export symbols on OSX (Darwin): some installations don't have 'objdump' + - Reorganize source files and namespaces + - Fixes for ClangTidy, and whitespace (passes 'git cl presumit --all -uf') + - Fix unused param compile warnings/errors when Effcee not present + - Avoid including time headers when timer functionality is disabled + - Avoid too-stringent warnings flags for Clang on Windows + - Internal refactoring + - Add hooks for automated fuzzing + - Add testing of command line executables + - #1688: Use binary mode on stdin; fixes "spirv-dis . versioning, with "-dev" indicating + work in progress. The intent is to more easly report + and summarize functionality when SPIRV-Tools is incorporated + in downstream projects. + + - Summary of functionality (See the README.md for more): + - Supports SPIR-V 1.1 Rev 1 + - Supports SPIR-V 1.0 Rev 5 + - Supports GLSL std450 extended instructions 1.0 Rev 3 + - Supports OpenCL extended instructions 1.0 Rev 2 + - Assembler, disassembler are complete + - Supports floating point widths of 16, 32, 64 bits + - Supports integer widths up to 64 bits + - Validator is incomplete + - Checks capability requirements in most cases + - Checks module layout constraints + - Checks ID use-definition ordering constraints, + ignoring control flow + - Checks some control flow graph rules + - Optimizer is introduced, with few available transforms. + - Supported on Linux, OSX, Android, Windows + + - Fixes bugs: + - #143: OpenCL pow and pown arguments diff --git a/third_party/spirv-tools/CMakeLists.txt b/third_party/spirv-tools/CMakeLists.txt new file mode 100755 index 0000000..9ea29ab --- /dev/null +++ b/third_party/spirv-tools/CMakeLists.txt @@ -0,0 +1,404 @@ +# Copyright (c) 2015-2016 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 2.8.12) +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif() +if (POLICY CMP0054) + # Avoid dereferencing variables or interpret keywords that have been + # quoted or bracketed. + # https://cmake.org/cmake/help/v3.1/policy/CMP0054.html + cmake_policy(SET CMP0054 NEW) +endif() +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# Filament specific changes +if (APPLE) + set(CMAKE_MACOSX_RPATH ON) +endif (APPLE) +set(SPIRV_SKIP_EXECUTABLES_OPTION ON) +set(SPIRV_SKIP_TESTS_OPTION ON) +set(SKIP_SPIRV_TOOLS_INSTALL ON) +# End Filament specific changes + +project(spirv-tools) +# Filament specific changes +# enable_testing() +# End Filament specific changes +set(SPIRV_TOOLS "SPIRV-Tools") + +include(GNUInstallDirs) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_CXX_STANDARD 11) + +option(SPIRV_ALLOW_TIMERS "Allow timers via clock_gettime on supported platforms" ON) + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + add_definitions(-DSPIRV_LINUX) + set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS}) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Emscripten") + add_definitions(-DSPIRV_EMSCRIPTEN) +elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Windows") + add_definitions(-DSPIRV_WINDOWS) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "CYGWIN") + add_definitions(-DSPIRV_WINDOWS) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + add_definitions(-DSPIRV_MAC) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "iOS") + add_definitions(-DSPIRV_IOS) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Android") + add_definitions(-DSPIRV_ANDROID) + set(SPIRV_TIMER_ENABLED ${SPIRV_ALLOW_TIMERS}) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") + add_definitions(-DSPIRV_FREEBSD) +elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") + add_definitions(-DSPIRV_FUCHSIA) +else() + message(FATAL_ERROR "Your platform '${CMAKE_SYSTEM_NAME}' is not supported!") +endif() + +if (${SPIRV_TIMER_ENABLED}) + add_definitions(-DSPIRV_TIMER_ENABLED) +endif() + +if ("${CMAKE_BUILD_TYPE}" STREQUAL "") + message(STATUS "No build type selected, default to Debug") + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# Filament specific changes +# option(SKIP_SPIRV_TOOLS_INSTALL "Skip installation" ${SKIP_SPIRV_TOOLS_INSTALL}) +# End Filament specific changes +if(NOT ${SKIP_SPIRV_TOOLS_INSTALL}) + set(ENABLE_SPIRV_TOOLS_INSTALL ON) +endif() + +option(SPIRV_BUILD_COMPRESSION "Build SPIR-V compressing codec" OFF) +if(SPIRV_BUILD_COMPRESSION) + message(FATAL_ERROR "SPIR-V compression codec has been removed from SPIR-V tools. " + "Please remove SPIRV_BUILD_COMPRESSION from your build options.") +endif(SPIRV_BUILD_COMPRESSION) + +option(SPIRV_BUILD_FUZZER "Build spirv-fuzz" OFF) + +option(SPIRV_WERROR "Enable error on warning" ON) +if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR (("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") AND (NOT CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC"))) + set(COMPILER_IS_LIKE_GNU TRUE) +endif() +if(${COMPILER_IS_LIKE_GNU}) + set(SPIRV_WARNINGS -Wall -Wextra -Wnon-virtual-dtor -Wno-missing-field-initializers) + + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Wno-self-assign) + endif() + + option(SPIRV_WARN_EVERYTHING "Enable -Weverything" ${SPIRV_WARN_EVERYTHING}) + if(${SPIRV_WARN_EVERYTHING}) + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} + -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Wpedantic -pedantic-errors) + else() + message(STATUS "Unknown compiler ${CMAKE_CXX_COMPILER_ID}, " + "so SPIRV_WARN_EVERYTHING has no effect") + endif() + endif() + + if(${SPIRV_WERROR}) + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} -Werror) + endif() +elseif(MSVC) + set(SPIRV_WARNINGS -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS /wd4800) + + if(${SPIRV_WERROR}) + set(SPIRV_WARNINGS ${SPIRV_WARNINGS} /WX) + endif() +endif() + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/) + +option(SPIRV_COLOR_TERMINAL "Enable color terminal output" ON) +if(${SPIRV_COLOR_TERMINAL}) + add_definitions(-DSPIRV_COLOR_TERMINAL) +endif() + +option(SPIRV_LOG_DEBUG "Enable excessive debug output" OFF) +if(${SPIRV_LOG_DEBUG}) + add_definitions(-DSPIRV_LOG_DEBUG) +endif() + +if (DEFINED SPIRV_TOOLS_EXTRA_DEFINITIONS) + add_definitions(${SPIRV_TOOLS_EXTRA_DEFINITIONS}) +endif() + +# Library build setting definitions: +# +# * SPIRV_TOOLS_BUILD_STATIC - ON or OFF - Defaults to ON. +# If enabled the following targets will be created: +# ${SPIRV_TOOLS}-static - STATIC library. +# Has full public symbol visibility. +# ${SPIRV_TOOLS}-shared - SHARED library. +# Has default-hidden symbol visibility. +# ${SPIRV_TOOLS} - will alias to one of above, based on BUILD_SHARED_LIBS. +# If disabled the following targets will be created: +# ${SPIRV_TOOLS} - either STATIC or SHARED based on SPIRV_TOOLS_LIBRARY_TYPE. +# Has full public symbol visibility. +# ${SPIRV_TOOLS}-shared - SHARED library. +# Has default-hidden symbol visibility. +# +# * SPIRV_TOOLS_LIBRARY_TYPE - SHARED or STATIC. +# Specifies the library type used for building SPIRV-Tools libraries. +# Defaults to SHARED when BUILD_SHARED_LIBS=1, otherwise STATIC. +# +# * SPIRV_TOOLS_FULL_VISIBILITY - "${SPIRV_TOOLS}-static" or "${SPIRV_TOOLS}" +# Evaluates to the SPIRV_TOOLS target library name that has no hidden symbols. +# This is used by internal targets for accessing symbols that are non-public. +# Note this target provides no API stability guarantees. +# +# Ideally, all of these will go away - see https://github.com/KhronosGroup/SPIRV-Tools/issues/3909. +option(SPIRV_TOOLS_BUILD_STATIC "Build ${SPIRV_TOOLS}-static target. ${SPIRV_TOOLS} will alias to ${SPIRV_TOOLS}-static or ${SPIRV_TOOLS}-shared based on BUILD_SHARED_LIBS" ON) +if(SPIRV_TOOLS_BUILD_STATIC) + set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}-static) + set(SPIRV_TOOLS_LIBRARY_TYPE "STATIC") +else(SPIRV_TOOLS_BUILD_STATIC) + set(SPIRV_TOOLS_FULL_VISIBILITY ${SPIRV_TOOLS}) + if (NOT DEFINED SPIRV_TOOLS_LIBRARY_TYPE) + if(BUILD_SHARED_LIBS) + set(SPIRV_TOOLS_LIBRARY_TYPE "SHARED") + else() + set(SPIRV_TOOLS_LIBRARY_TYPE "STATIC") + endif() + endif() +endif(SPIRV_TOOLS_BUILD_STATIC) + +function(spvtools_default_compile_options TARGET) + target_compile_options(${TARGET} PRIVATE ${SPIRV_WARNINGS}) + + if (${COMPILER_IS_LIKE_GNU}) + target_compile_options(${TARGET} PRIVATE + -std=c++11 -fno-exceptions -fno-rtti) + target_compile_options(${TARGET} PRIVATE + -Wall -Wextra -Wno-long-long -Wshadow -Wundef -Wconversion + -Wno-sign-conversion) + # For good call stacks in profiles, keep the frame pointers. + if(NOT "${SPIRV_PERF}" STREQUAL "") + target_compile_options(${TARGET} PRIVATE -fno-omit-frame-pointer) + endif() + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(SPIRV_USE_SANITIZER "" CACHE STRING + "Use the clang sanitizer [address|memory|thread|...]") + if(NOT "${SPIRV_USE_SANITIZER}" STREQUAL "") + target_compile_options(${TARGET} PRIVATE + -fsanitize=${SPIRV_USE_SANITIZER}) + set_target_properties(${TARGET} PROPERTIES + LINK_FLAGS -fsanitize=${SPIRV_USE_SANITIZER}) + endif() + target_compile_options(${TARGET} PRIVATE + -ftemplate-depth=1024) + else() + target_compile_options(${TARGET} PRIVATE + -Wno-missing-field-initializers) + endif() + endif() + + if (MSVC) + # Specify /EHs for exception handling. This makes using SPIRV-Tools as + # dependencies in other projects easier. + target_compile_options(${TARGET} PRIVATE /EHs) + endif() + + # For MinGW cross compile, statically link to the C++ runtime. + # But it still depends on MSVCRT.dll. + if (${CMAKE_SYSTEM_NAME} MATCHES "Windows") + if (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") + set_target_properties(${TARGET} PROPERTIES + LINK_FLAGS -static -static-libgcc -static-libstdc++) + endif() + endif() +endfunction() + +if(NOT COMMAND find_host_package) + macro(find_host_package) + find_package(${ARGN}) + endmacro() +endif() +if(NOT COMMAND find_host_program) + macro(find_host_program) + find_program(${ARGN}) + endmacro() +endif() + +# Tests require Python3 +if(CMAKE_VERSION VERSION_LESS "3.12" OR ${CMAKE_SYSTEM_NAME} MATCHES "Windows") + find_host_package(PythonInterp 3 REQUIRED) +else() + find_package(Python3 COMPONENTS Interpreter) +endif() + +# Check for symbol exports on Linux. +# At the moment, this check will fail on the OSX build machines for the Android NDK. +# It appears they don't have objdump. +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + macro(spvtools_check_symbol_exports TARGET) + if (NOT "${SPIRV_SKIP_TESTS}") + add_test(NAME spirv-tools-symbol-exports-${TARGET} + COMMAND ${PYTHON_EXECUTABLE} + ${spirv-tools_SOURCE_DIR}/utils/check_symbol_exports.py "$") + endif() + endmacro() +else() + macro(spvtools_check_symbol_exports TARGET) + if (NOT "${SPIRV_SKIP_TESTS}") + message("Skipping symbol exports test for ${TARGET}") + endif() + endmacro() +endif() + +if(ENABLE_SPIRV_TOOLS_INSTALL) + if(WIN32) + macro(spvtools_config_package_dir TARGET PATH) + set(${PATH} ${TARGET}/cmake) + endmacro() + else() + macro(spvtools_config_package_dir TARGET PATH) + set(${PATH} ${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET}) + endmacro() + endif() + + macro(spvtools_generate_config_file TARGET) + file(WRITE ${CMAKE_BINARY_DIR}/${TARGET}Config.cmake + "include(CMakeFindDependencyMacro)\n" + "find_dependency(${SPIRV_TOOLS})\n" + "include(\${CMAKE_CURRENT_LIST_DIR}/${TARGET}Targets.cmake)\n" + "set(${TARGET}_LIBRARIES ${TARGET})\n" + "get_target_property(${TARGET}_INCLUDE_DIRS ${TARGET} INTERFACE_INCLUDE_DIRECTORIES)\n") + endmacro() +endif() + +# Defaults to OFF if the user didn't set it. +# Filament specific changes +option(SPIRV_SKIP_EXECUTABLES + "Skip building the executable and tests along with the library" + ${SPIRV_SKIP_EXECUTABLES_OPTION}) +option(SPIRV_SKIP_TESTS + "Skip building tests along with the library" ${SPIRV_SKIP_TESTS_OPTION}) +# End Filament specific changes +if ("${SPIRV_SKIP_EXECUTABLES}") + set(SPIRV_SKIP_TESTS ON) +endif() + +# Defaults to ON. The checks can be time consuming. +# Turn off if they take too long. +option(SPIRV_CHECK_CONTEXT "In a debug build, check if the IR context is in a valid state." ON) +if (${SPIRV_CHECK_CONTEXT}) + add_definitions(-DSPIRV_CHECK_CONTEXT) +endif() + +# Precompiled header macro. Parameters are source file list and filename for pch cpp file. +macro(spvtools_pch SRCS PCHPREFIX) + if(MSVC AND CMAKE_GENERATOR MATCHES "^Visual Studio" AND NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(PCH_NAME "$(IntDir)\\${PCHPREFIX}.pch") + # make source files use/depend on PCH_NAME + set_source_files_properties(${${SRCS}} PROPERTIES COMPILE_FLAGS "/Yu${PCHPREFIX}.h /FI${PCHPREFIX}.h /Fp${PCH_NAME} /Zm300" OBJECT_DEPENDS "${PCH_NAME}") + # make PCHPREFIX.cpp file compile and generate PCH_NAME + set_source_files_properties("${PCHPREFIX}.cpp" PROPERTIES COMPILE_FLAGS "/Yc${PCHPREFIX}.h /Fp${PCH_NAME} /Zm300" OBJECT_OUTPUTS "${PCH_NAME}") + list(APPEND ${SRCS} "${PCHPREFIX}.cpp") + endif() +endmacro(spvtools_pch) + +add_subdirectory(external) + +# Warning about extra semi-colons. +# +# This is not supported on all compilers/versions. so enabling only +# for clang, since that works for all versions that our bots run. +# +# This is intentionally done after adding the external subdirectory, +# so we don't enforce this flag on our dependencies, some of which do +# not pass it. +# +# If the minimum version of CMake supported is updated to 3.0 or +# later, then check_cxx_compiler_flag could be used instead. +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + add_compile_options("-Wextra-semi") +endif() + +add_subdirectory(source) +add_subdirectory(tools) + +# Filament specific changes +#add_subdirectory(test) +#add_subdirectory(examples) +# End Filament specific changes + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/libspirv.h + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/libspirv.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/optimizer.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/linker.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv-tools/instrument.hpp + DESTINATION + ${CMAKE_INSTALL_INCLUDEDIR}/spirv-tools/) +endif(ENABLE_SPIRV_TOOLS_INSTALL) + +if (NOT "${SPIRV_SKIP_TESTS}") + add_test(NAME spirv-tools-copyrights + COMMAND ${PYTHON_EXECUTABLE} utils/check_copyright.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endif() + +set(SPIRV_LIBRARIES "-lSPIRV-Tools-opt -lSPIRV-Tools -lSPIRV-Tools-link") +set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared") + +# Build pkg-config file +# Use a first-class target so it's regenerated when relevant files are updated. +# Filament specific changes +# add_custom_target(spirv-tools-pkg-config ALL +# COMMAND ${CMAKE_COMMAND} +# -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES +# -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in +# -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc +# -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} +# -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} +# -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} +# -DSPIRV_LIBRARIES=${SPIRV_LIBRARIES} +# -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake +# DEPENDS "CHANGES" "cmake/SPIRV-Tools.pc.in" "cmake/write_pkg_config.cmake") +# add_custom_target(spirv-tools-shared-pkg-config ALL +# COMMAND ${CMAKE_COMMAND} +# -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES +# -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in +# -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc +# -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} +# -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} +# -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} +# -DSPIRV_SHARED_LIBRARIES=${SPIRV_SHARED_LIBRARIES} +# -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake +# DEPENDS "CHANGES" "cmake/SPIRV-Tools-shared.pc.in" "cmake/write_pkg_config.cmake") +# End Filament specific changes + +# Install pkg-config file +if (ENABLE_SPIRV_TOOLS_INSTALL) + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc + ${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc + DESTINATION + ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +endif() diff --git a/third_party/spirv-tools/CODE_OF_CONDUCT.md b/third_party/spirv-tools/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a11610b --- /dev/null +++ b/third_party/spirv-tools/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +A reminder that this issue tracker is managed by the Khronos Group. Interactions here should follow the Khronos Code of Conduct (https://www.khronos.org/developers/code-of-conduct), which prohibits aggressive or derogatory language. Please keep the discussion friendly and civil. diff --git a/third_party/spirv-tools/CONTRIBUTING.md b/third_party/spirv-tools/CONTRIBUTING.md new file mode 100644 index 0000000..b46ae31 --- /dev/null +++ b/third_party/spirv-tools/CONTRIBUTING.md @@ -0,0 +1,192 @@ +# Contributing to SPIR-V Tools + +## For users: Reporting bugs and requesting features + +We organize known future work in GitHub projects. See [Tracking SPIRV-Tools work +with GitHub +projects](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/docs/projects.md) +for more. + +To report a new bug or request a new feature, please file a GitHub issue. Please +ensure the bug has not already been reported by searching +[issues](https://github.com/KhronosGroup/SPIRV-Tools/issues) and +[projects](https://github.com/KhronosGroup/SPIRV-Tools/projects). If the bug has +not already been reported open a new one +[here](https://github.com/KhronosGroup/SPIRV-Tools/issues/new). + +When opening a new issue for a bug, make sure you provide the following: + +* A clear and descriptive title. + * We want a title that will make it easy for people to remember what the + issue is about. Simply using "Segfault in spirv-opt" is not helpful + because there could be (but hopefully aren't) multiple bugs with + segmentation faults with different causes. +* A test case that exposes the bug, with the steps and commands to reproduce + it. + * The easier it is for a developer to reproduce the problem, the quicker a + fix can be found and verified. It will also make it easier for someone + to possibly realize the bug is related to another issue. + +For feature requests, we use +[issues](https://github.com/KhronosGroup/SPIRV-Tools/issues) as well. Please +create a new issue, as with bugs. In the issue provide + +* A description of the problem that needs to be solved. +* Examples that demonstrate the problem. + +## For developers: Contributing a patch + +Before we can use your code, you must sign the [Khronos Open Source Contributor +License Agreement](https://cla-assistant.io/KhronosGroup/SPIRV-Tools) (CLA), +which you can do online. The CLA is necessary mainly because you own the +copyright to your changes, even after your contribution becomes part of our +codebase, so we need your permission to use and distribute your code. We also +need to be sure of various other things -- for instance that you'll tell us if +you know that your code infringes on other people's patents. You don't have to +sign the CLA until after you've submitted your code for review and a member has +approved it, but you must do it before we can put your code into our codebase. + +See +[README.md](https://github.com/KhronosGroup/SPIRV-Tools/blob/master/README.md) +for instruction on how to get, build, and test the source. Once you have made +your changes: + +* Ensure the code follows the [Google C++ Style + Guide](https://google.github.io/styleguide/cppguide.html). Running + `clang-format -style=file -i [modified-files]` can help. +* Create a pull request (PR) with your patch. +* Make sure the PR description clearly identified the problem, explains the + solution, and references the issue if applicable. +* If your patch completely fixes bug 1234, the commit message should say + `Fixes https://github.com/KhronosGroup/SPIRV-Tools/issues/1234` + When you do this, the issue will be closed automatically when the commit + goes into master. Also, this helps us update the [CHANGES](CHANGES) file. +* Watch the continuous builds to make sure they pass. +* Request a code review. + +The reviewer can either approve your PR or request changes. If changes are +requested: + +* Please add new commits to your branch, instead of amending your commit. + Adding new commits makes it easier for the reviewer to see what has changed + since the last review. +* Once you are ready for another round of reviews, add a comment at the + bottom, such as "Ready for review" or "Please take a look" (or "PTAL"). This + explicit handoff is useful when responding with multiple small commits. + +After the PR has been reviewed it is the job of the reviewer to merge the PR. +Instructions for this are given below. + +## For maintainers: Reviewing a PR + +The formal code reviews are done on GitHub. Reviewers are to look for all of the +usual things: + +* Coding style follows the [Google C++ Style + Guide](https://google.github.io/styleguide/cppguide.html) +* Identify potential functional problems. +* Identify code duplication. +* Ensure the unit tests have enough coverage. +* Ensure continuous integration (CI) bots run on the PR. If not run (in the + case of PRs by external contributors), add the "kokoro:run" label to the + pull request which will trigger running all CI jobs. + +When looking for functional problems, there are some common problems reviewers +should pay particular attention to: + +* Does the code work for both Shader (Vulkan and OpenGL) and Kernel (OpenCL) + scenarios? The respective SPIR-V dialects are slightly different. +* Changes are made to a container while iterating through it. You have to be + careful that iterators are not invalidated or that elements are not skipped. +* C++11 and VS2013. We generally assume that we have a C++11 compliant + compiler. However, on Windows, we still support Visual Studio 2013, which is + not fully C++11 compliant. See + [here](https://msdn.microsoft.com/en-us/library/hh567368.aspx). In + particular, note that it does not provide default move-constructors or + move-assignments for classes. In general, r-value references do not work the + way you might assume they do. +* For SPIR-V transforms: The module is changed, but the analyses are not + updated. For example, a new instruction is added, but the def-use manager is + not updated. Later on, it is possible that the def-use manager will be used, + and give wrong results. + +## For maintainers: Merging a PR + +We intend to maintain a linear history on the GitHub master branch, and the +build and its tests should pass at each commit in that history. A linear +always-working history is easier to understand and to bisect in case we want to +find which commit introduced a bug. + +### Initial merge setup + +The following steps should be done exactly once (when you are about to merge a +PR for the first time): + +* It is assumed that upstream points to + [git@github.com](mailto:git@github.com):KhronosGroup/SPIRV-Tools.git or + https://github.com/KhronosGroup/SPIRV-Tools.git. + +* Find out the local name for the main github repo in your git configuration. + For example, in this configuration, it is labeled `upstream`. + + ``` + git remote -v + [ ... ] + upstream https://github.com/KhronosGroup/SPIRV-Tools.git (fetch) + upstream https://github.com/KhronosGroup/SPIRV-Tools.git (push) + ``` + +* Make sure that the `upstream` remote is set to fetch from the `refs/pull` + namespace: + + ``` + git config --get-all remote.upstream.fetch + +refs/heads/*:refs/remotes/upstream/* + +refs/pull/*/head:refs/remotes/upstream/pr/* + ``` + +* If the line `+refs/pull/*/head:refs/remotes/upstream/pr/*` is not present in + your configuration, you can add it with the command: + + ``` + git config --local --add remote.upstream.fetch '+refs/pull/*/head:refs/remotes/upstream/pr/*' + ``` + +### Merge workflow + +The following steps should be done for every PR that you intend to merge: + +* Make sure your local copy of the master branch is up to date: + + ``` + git checkout master + git pull + ``` + +* Fetch all pull requests refs: + + ``` + git fetch upstream + ``` + +* Checkout the particular pull request you are going to review: + + ``` + git checkout pr/1048 + ``` + +* Rebase the PR on top of the master branch. If there are conflicts, send it + back to the author and ask them to rebase. During the interactive rebase be + sure to squash all of the commits down to a single commit. + + ``` + git rebase -i master + ``` + +* **Build and test the PR.** + +* If all of the tests pass, push the commit `git push upstream HEAD:master` + +* Close the PR and add a comment saying it was push using the commit that you + just pushed. See https://github.com/KhronosGroup/SPIRV-Tools/pull/935 as an + example. diff --git a/third_party/spirv-tools/DEPS b/third_party/spirv-tools/DEPS new file mode 100644 index 0000000..ef3ee5d --- /dev/null +++ b/third_party/spirv-tools/DEPS @@ -0,0 +1,26 @@ +use_relative_paths = True + +vars = { + 'github': 'https://github.com', + + 'effcee_revision': '2ec8f8738118cc483b67c04a759fee53496c5659', + 'googletest_revision': '3af06fe1664d30f98de1e78c53a7087e842a2547', + 're2_revision': 'ca11026a032ce2a3de4b3c389ee53d2bdc8794d6', + 'spirv_headers_revision': '05836bdba63e7debce9fa9feaed42f20cd43af9d', +} + +deps = { + 'external/effcee': + Var('github') + '/google/effcee.git@' + Var('effcee_revision'), + + 'external/googletest': + Var('github') + '/google/googletest.git@' + Var('googletest_revision'), + + 'external/re2': + Var('github') + '/google/re2.git@' + Var('re2_revision'), + + 'external/spirv-headers': + Var('github') + '/KhronosGroup/SPIRV-Headers.git@' + + Var('spirv_headers_revision'), +} + diff --git a/third_party/spirv-tools/FILAMENT_README.md b/third_party/spirv-tools/FILAMENT_README.md new file mode 100644 index 0000000..23bb8a0 --- /dev/null +++ b/third_party/spirv-tools/FILAMENT_README.md @@ -0,0 +1,30 @@ +When updating spirv-tools to a new version, make sure to preserve all the changes marked with +the following in `CMakeLists.txt` and `source/CMakeLists.txt`: + +`# Filament specific changes` + +You can easily apply these changes by running the following command at Filament's root: + +``` +git apply third_party/spirv-tools/filament-specific-changes.patch +``` + +The following procedure can be used to update spirv-tools. Note that there is a secondary repository +that needs to be downloaded (spirv-headers). + +``` +curl -L https://github.com/KhronosGroup/spirv-tools/archive/master.zip > master.zip +unzip master.zip +rsync -r SPIRV-Tools-master/ spirv-tools/ --delete +rm -rf SPIRV-Tools-master master.zip +curl -L https://github.com/KhronosGroup/spirv-headers/archive/master.zip > master.zip +unzip master.zip +mv SPIRV-Headers-master/ spirv-tools/external/spirv-headers +rm master.zip + +git add spirv-tools +``` + +Edit the .gitignore so it doesn't prevent `spirv-headers` from being committed. + +Finally, remember to bring back the Filament-specific changes in CMakeLists. diff --git a/third_party/spirv-tools/LICENSE b/third_party/spirv-tools/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/third_party/spirv-tools/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/spirv-tools/PRESUBMIT.py b/third_party/spirv-tools/PRESUBMIT.py new file mode 100644 index 0000000..dd3117f --- /dev/null +++ b/third_party/spirv-tools/PRESUBMIT.py @@ -0,0 +1,40 @@ +# Copyright (c) 2018 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Presubmit script for SPIRV-Tools. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details about the presubmit API built into depot_tools. +""" + +LINT_FILTERS = [ + "-build/storage_class", + "-readability/casting", + "-readability/fn_size", + "-readability/todo", + "-runtime/explicit", + "-runtime/int", + "-runtime/printf", + "-runtime/references", + "-runtime/string", +] + + +def CheckChangeOnUpload(input_api, output_api): + results = [] + results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api) + results += input_api.canned_checks.CheckChangeLintsClean( + input_api, output_api, None, LINT_FILTERS) + + return results diff --git a/third_party/spirv-tools/README.md b/third_party/spirv-tools/README.md new file mode 100644 index 0000000..44f582f --- /dev/null +++ b/third_party/spirv-tools/README.md @@ -0,0 +1,709 @@ +# SPIR-V Tools + +## Overview + +The SPIR-V Tools project provides an API and commands for processing SPIR-V +modules. + +The project includes an assembler, binary module parser, disassembler, +validator, and optimizer for SPIR-V. Except for the optimizer, all are based +on a common static library. The library contains all of the implementation +details, and is used in the standalone tools whilst also enabling integration +into other code bases directly. The optimizer implementation resides in its +own library, which depends on the core library. + +The interfaces have stabilized: +We don't anticipate making a breaking change for existing features. + +SPIR-V is defined by the Khronos Group Inc. +See the [SPIR-V Registry][spirv-registry] for the SPIR-V specification, +headers, and XML registry. + +## Downloads + +[![Build status](https://ci.appveyor.com/api/projects/status/gpue87cesrx3pi0d/branch/master?svg=true)](https://ci.appveyor.com/project/Khronoswebmaster/spirv-tools/branch/master) +Linux[![Linux Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_linux_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) +MacOS[![MacOS Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_macos_clang_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) +Windows[![Windows Build Status](https://storage.googleapis.com/spirv-tools/badges/build_status_windows_release.svg)](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) + +[More downloads](docs/downloads.md) + +## Versioning SPIRV-Tools + +See [`CHANGES`](CHANGES) for a high level summary of recent changes, by version. + +SPIRV-Tools project version numbers are of the form `v`*year*`.`*index* and with +an optional `-dev` suffix to indicate work in progress. For example, the +following versions are ordered from oldest to newest: + +* `v2016.0` +* `v2016.1-dev` +* `v2016.1` +* `v2016.2-dev` +* `v2016.2` + +Use the `--version` option on each command line tool to see the software +version. An API call reports the software version as a C-style string. + +## Supported features + +### Assembler, binary parser, and disassembler + +* Support for SPIR-V 1.0, through 1.5 + * Based on SPIR-V syntax described by JSON grammar files in the + [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) repository. + * Usually, support for a new version of SPIR-V is ready within days after + publication. +* Support for extended instruction sets: + * GLSL std450 version 1.0 Rev 3 + * OpenCL version 1.0 Rev 2 +* Assembler only does basic syntax checking. No cross validation of + IDs or types is performed, except to check literal arguments to + `OpConstant`, `OpSpecConstant`, and `OpSwitch`. + +See [`docs/syntax.md`](docs/syntax.md) for the assembly language syntax. + +### Validator + +The validator checks validation rules described by the SPIR-V specification. + +Khronos recommends that tools that create or transform SPIR-V modules use the +validator to ensure their outputs are valid, and that tools that consume SPIR-V +modules optionally use the validator to protect themselves from bad inputs. +This is especially encouraged for debug and development scenarios. + +The validator has one-sided error: it will only return an error when it has +implemented a rule check and the module violates that rule. + +The validator is incomplete. +See the [CHANGES](CHANGES) file for reports on completed work, and +the [Validator +sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/1) for planned +and in-progress work. + +*Note*: The validator checks some Universal Limits, from section 2.17 of the SPIR-V spec. +The validator will fail on a module that exceeds those minimum upper bound limits. +It is [future work](https://github.com/KhronosGroup/SPIRV-Tools/projects/1#card-1052403) +to parameterize the validator to allow larger +limits accepted by a more than minimally capable SPIR-V consumer. + + +### Optimizer + +The optimizer is a collection of code transforms, or "passes". +Transforms are written for a diverse set of reasons: + +* To restructure, simplify, or normalize the code for further processing. +* To eliminate undesirable code. +* To improve code quality in some metric such as size or performance. + **Note**: These transforms are not guaranteed to actually improve any + given metric. Users should always measure results for their own situation. + +As of this writing, there are 67 transforms including examples such as: +* Simplification + * Strip debug info + * Strip reflection info +* Specialization Constants + * Set spec constant default value + * Freeze spec constant to default value + * Fold `OpSpecConstantOp` and `OpSpecConstantComposite` + * Unify constants + * Eliminate dead constant +* Code Reduction + * Inline all function calls exhaustively + * Convert local access chains to inserts/extracts + * Eliminate local load/store in single block + * Eliminate local load/store with single store + * Eliminate local load/store with multiple stores + * Eliminate local extract from insert + * Eliminate dead instructions (aggressive) + * Eliminate dead branches + * Merge single successor / single predecessor block pairs + * Eliminate common uniform loads + * Remove duplicates: Capabilities, extended instruction imports, types, and + decorations. +* Normalization + * Compact IDs + * CFG cleanup + * Flatten decorations + * Merge returns + * Convert AMD-specific instructions to KHR instructions +* Code improvement + * Conditional constant propagation + * If-conversion + * Loop fission + * Loop fusion + * Loop-invariant code motion + * Loop unroll +* Other + * Generate WebGPU initializers + * Graphics robust access + * Upgrade memory model to VulkanKHR + +Additionally, certain sets of transformations have been packaged into +higher-level recipes. These include: + +* Optimization for size (`spirv-opt -Os`) +* Optimization for performance (`spirv-opt -O`) + +For the latest list with detailed documentation, please refer to +[`include/spirv-tools/optimizer.hpp`](include/spirv-tools/optimizer.hpp). + +For suggestions on using the code reduction options, please refer to this [white paper](https://www.lunarg.com/shader-compiler-technologies/white-paper-spirv-opt/). + + +### Linker + +*Note:* The linker is still under development. + +Current features: +* Combine multiple SPIR-V binary modules together. +* Combine into a library (exports are retained) or an executable (no symbols + are exported). + +See the [CHANGES](CHANGES) file for reports on completed work, and the [General +sub-project](https://github.com/KhronosGroup/SPIRV-Tools/projects/2) for +planned and in-progress work. + + +### Reducer + +*Note:* The reducer is still under development. + +The reducer simplifies and shrinks a SPIR-V module with respect to a +user-supplied *interestingness function*. For example, given a large +SPIR-V module that cause some SPIR-V compiler to fail with a given +fatal error message, the reducer could be used to look for a smaller +version of the module that causes the compiler to fail with the same +fatal error message. + +To suggest an additional capability for the reducer, [file an +issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with +"Reducer:" as the start of its title. + + +### Fuzzer + +*Note:* The fuzzer is still under development. + +The fuzzer applies semantics-preserving transformations to a SPIR-V binary +module, to produce an equivalent module. The original and transformed modules +should produce essentially identical results when executed on identical inputs: +their results should differ only due to floating-point round-off, if at all. +Significant differences in results can pinpoint bugs in tools that process +SPIR-V binaries, such as miscompilations. This *metamorphic testing* approach +is similar to the method used by the [GraphicsFuzz +project](https://github.com/google/graphicsfuzz) for fuzzing of GLSL shaders. + +To suggest an additional capability for the fuzzer, [file an +issue](https://github.com/KhronosGroup/SPIRV-Tools/issues]) with +"Fuzzer:" as the start of its title. + + +### Extras + +* [Utility filters](#utility-filters) +* Build target `spirv-tools-vimsyntax` generates file `spvasm.vim`. + Copy that file into your `$HOME/.vim/syntax` directory to get SPIR-V assembly syntax + highlighting in Vim. This build target is not built by default. + +## Contributing + +The SPIR-V Tools project is maintained by members of the The Khronos Group Inc., +and is hosted at https://github.com/KhronosGroup/SPIRV-Tools. + +Consider joining the `public_spirv_tools_dev@khronos.org` mailing list, via +[https://www.khronos.org/spir/spirv-tools-mailing-list/](https://www.khronos.org/spir/spirv-tools-mailing-list/). +The mailing list is used to discuss development plans for the SPIRV-Tools as an open source project. +Once discussion is resolved, +specific work is tracked via issues and sometimes in one of the +[projects][spirv-tools-projects]. + +(To provide feedback on the SPIR-V _specification_, file an issue on the +[SPIRV-Headers][spirv-headers] GitHub repository.) + +See [`docs/projects.md`](docs/projects.md) to see how we use the +[GitHub Project +feature](https://help.github.com/articles/tracking-the-progress-of-your-work-with-projects/) +to organize planned and in-progress work. + +Contributions via merge request are welcome. Changes should: +* Be provided under the [Apache 2.0](#license). +* You'll be prompted with a one-time "click-through" + [Khronos Open Source Contributor License Agreement][spirv-tools-cla] + (CLA) dialog as part of submitting your pull request or + other contribution to GitHub. +* Include tests to cover updated functionality. +* C++ code should follow the [Google C++ Style Guide][cpp-style-guide]. +* Code should be formatted with `clang-format`. + [kokoro/check-format/build.sh](kokoro/check-format/build.sh) + shows how to download it. Note that we currently use + `clang-format version 5.0.0` for SPIRV-Tools. Settings are defined by + the included [.clang-format](.clang-format) file. + +We intend to maintain a linear history on the GitHub `master` branch. + +### Source code organization + +* `example`: demo code of using SPIRV-Tools APIs +* `external/googletest`: Intended location for the + [googletest][googletest] sources, not provided +* `external/effcee`: Location of [Effcee][effcee] sources, if the `effcee` library + is not already configured by an enclosing project. +* `external/re2`: Location of [RE2][re2] sources, if the `re2` library is not already + configured by an enclosing project. + (The Effcee project already requires RE2.) +* `include/`: API clients should add this directory to the include search path +* `external/spirv-headers`: Intended location for + [SPIR-V headers][spirv-headers], not provided +* `include/spirv-tools/libspirv.h`: C API public interface +* `source/`: API implementation +* `test/`: Tests, using the [googletest][googletest] framework +* `tools/`: Command line executables + +Example of getting sources, assuming SPIRV-Tools is configured as a standalone project: + + git clone https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools + git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers + git clone https://github.com/google/googletest.git spirv-tools/external/googletest + git clone https://github.com/google/effcee.git spirv-tools/external/effcee + git clone https://github.com/google/re2.git spirv-tools/external/re2 + +### Tests + +The project contains a number of tests, used to drive development +and ensure correctness. The tests are written using the +[googletest][googletest] framework. The `googletest` +source is not provided with this project. There are two ways to enable +tests: +* If SPIR-V Tools is configured as part of an enclosing project, then the + enclosing project should configure `googletest` before configuring SPIR-V Tools. +* If SPIR-V Tools is configured as a standalone project, then download the + `googletest` source into the `/external/googletest` directory before + configuring and building the project. + +*Note*: You must use a version of googletest that includes +[a fix][googletest-pull-612] for [googletest issue 610][googletest-issue-610]. +The fix is included on the googletest master branch any time after 2015-11-10. +In particular, googletest must be newer than version 1.7.0. + +### Dependency on Effcee + +Some tests depend on the [Effcee][effcee] library for stateful matching. +Effcee itself depends on [RE2][re2]. + +* If SPIRV-Tools is configured as part of a larger project that already uses + Effcee, then that project should include Effcee before SPIRV-Tools. +* Otherwise, SPIRV-Tools expects Effcee sources to appear in `external/effcee` + and RE2 sources to appear in `external/re2`. + + +## Build + +Instead of building manually, you can also download the binaries for your +platform directly from the [master-tot release][master-tot-release] on GitHub. +Those binaries are automatically uploaded by the buildbots after successful +testing and they always reflect the current top of the tree of the master +branch. + +In order to build the code, you first need to sync the external repositories +that it depends on. Assume that `` is the root directory of the +checked out code: + +```sh +cd +git clone https://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers +git clone https://github.com/google/effcee.git external/effcee +git clone https://github.com/google/re2.git external/re2 +git clone https://github.com/google/googletest.git external/googletest # optional + +``` + +*Note*: +The script `utils/git-sync-deps` can be used to checkout and/or update the +contents of the repos under `external/` instead of manually maintaining them. + +### Build using CMake +You can build the project using [CMake][cmake]: + +```sh +cd +mkdir build && cd build +cmake [-G ] +``` + +Once the build files have been generated, build using the appropriate build +command (e.g. `ninja`, `make`, `msbuild`, etc.; this depends on the platform +generator used above), or use your IDE, or use CMake to run the appropriate build +command for you: + +```sh +cmake --build . [--config Debug] # runs `make` or `ninja` or `msbuild` etc. +``` + +#### Note about the fuzzer + +The SPIR-V fuzzer, `spirv-fuzz`, can only be built via CMake, and is disabled by +default. To build it, clone protobuf and use the `SPIRV_BUILD_FUZZER` CMake +option, like so: + +```sh +# In (the SPIRV-Tools repo root): +git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf + +# In your build directory: +cmake [-G ] -DSPIRV_BUILD_FUZZER=ON +cmake --build . --config Debug +``` + +You can also add `-DSPIRV_ENABLE_LONG_FUZZER_TESTS=ON` to build additional +fuzzer tests. + + +### Build using Bazel +You can also use [Bazel](https://bazel.build/) to build the project. +```sh +cd +bazel build :all +``` + +### Tools you'll need + +For building and testing SPIRV-Tools, the following tools should be +installed regardless of your OS: + +- [CMake](http://www.cmake.org/): if using CMake for generating compilation +targets, you need to install CMake Version 2.8.12 or later. +- [Python 3](http://www.python.org/): for utility scripts and running the test +suite. +- [Bazel](https://bazel.build/) (optional): if building the source with Bazel, +you need to install Bazel Version 0.29.1 on your machine. Other versions may +also work, but are not verified. + +SPIRV-Tools is regularly tested with the following compilers: + +On Linux +- GCC version 4.8.5 +- Clang version 3.8 + +On MacOS +- AppleClang 10.0 + +On Windows +- Visual Studio 2015 +- Visual Studio 2017 + +Other compilers or later versions may work, but they are not tested. + +### CMake options + +The following CMake options are supported: + +* `SPIRV_BUILD_FUZZER={ON|OFF}`, default `OFF` - Build the spirv-fuzz tool. +* `SPIRV_COLOR_TERMINAL={ON|OFF}`, default `ON` - Enables color console output. +* `SPIRV_SKIP_TESTS={ON|OFF}`, default `OFF`- Build only the library and + the command line tools. This will prevent the tests from being built. +* `SPIRV_SKIP_EXECUTABLES={ON|OFF}`, default `OFF`- Build only the library, not + the command line tools and tests. +* `SPIRV_USE_SANITIZER=`, default is no sanitizing - On UNIX + platforms with an appropriate version of `clang` this option enables the use + of the sanitizers documented [here][clang-sanitizers]. + This should only be used with a debug build. +* `SPIRV_WARN_EVERYTHING={ON|OFF}`, default `OFF` - On UNIX platforms enable + more strict warnings. The code might not compile with this option enabled. + For Clang, enables `-Weverything`. For GCC, enables `-Wpedantic`. + See [`CMakeLists.txt`](CMakeLists.txt) for details. +* `SPIRV_WERROR={ON|OFF}`, default `ON` - Forces a compilation error on any + warnings encountered by enabling the compiler-specific compiler front-end + option. No compiler front-end options are enabled when this option is OFF. + +Additionally, you can pass additional C preprocessor definitions to SPIRV-Tools +via setting `SPIRV_TOOLS_EXTRA_DEFINITIONS`. For example, by setting it to +`/D_ITERATOR_DEBUG_LEVEL=0` on Windows, you can disable checked iterators and +iterator debugging. + +### Android + +SPIR-V Tools supports building static libraries `libSPIRV-Tools.a` and +`libSPIRV-Tools-opt.a` for Android: + +``` +cd + +export ANDROID_NDK=/path/to/your/ndk + +mkdir build && cd build +mkdir libs +mkdir app + +$ANDROID_NDK/ndk-build -C ../android_test \ + NDK_PROJECT_PATH=. \ + NDK_LIBS_OUT=`pwd`/libs \ + NDK_APP_OUT=`pwd`/app +``` + +### Updating DEPS +Occasionally the entries in DEPS will need to be updated. This is done on demand +when there is a request to do this, often due to downstream breakages. There is +a script `utils/roll_deps.sh` provided, which will generate a patch with the +updated DEPS values. This will still need to be tested in your checkout to +confirm that there are no integration issues that need to be resolved. + +## Library + +### Usage + +The internals of the library use C++11 features, and are exposed via both a C +and C++ API. + +In order to use the library from an application, the include path should point +to `/include`, which will enable the application to include the +header `/include/spirv-tools/libspirv.h{|pp}` then linking against +the static library in `/source/libSPIRV-Tools.a` or +`/source/SPIRV-Tools.lib`. +For optimization, the header file is +`/include/spirv-tools/optimizer.hpp`, and the static library is +`/source/libSPIRV-Tools-opt.a` or +`/source/SPIRV-Tools-opt.lib`. + +* `SPIRV-Tools` CMake target: Creates the static library: + * `/source/libSPIRV-Tools.a` on Linux and OS X. + * `/source/libSPIRV-Tools.lib` on Windows. +* `SPIRV-Tools-opt` CMake target: Creates the static library: + * `/source/libSPIRV-Tools-opt.a` on Linux and OS X. + * `/source/libSPIRV-Tools-opt.lib` on Windows. + +#### Entry points + +The interfaces are still under development, and are expected to change. + +There are five main entry points into the library in the C interface: + +* `spvTextToBinary`: An assembler, translating text to a binary SPIR-V module. +* `spvBinaryToText`: A disassembler, translating a binary SPIR-V module to + text. +* `spvBinaryParse`: The entry point to a binary parser API. It issues callbacks + for the header and each parsed instruction. The disassembler is implemented + as a client of `spvBinaryParse`. +* `spvValidate` implements the validator functionality. *Incomplete* +* `spvValidateBinary` implements the validator functionality. *Incomplete* + +The C++ interface is comprised of three classes, `SpirvTools`, `Optimizer` and +`Linker`, all in the `spvtools` namespace. +* `SpirvTools` provides `Assemble`, `Disassemble`, and `Validate` methods. +* `Optimizer` provides methods for registering and running optimization passes. +* `Linker` provides methods for combining together multiple binaries. + +## Command line tools + +Command line tools, which wrap the above library functions, are provided to +assemble or disassemble shader files. It's a convention to name SPIR-V +assembly and binary files with suffix `.spvasm` and `.spv`, respectively. + +### Assembler tool + +The assembler reads the assembly language text, and emits the binary form. + +The standalone assembler is the executable called `spirv-as`, and is located in +`/tools/spirv-as`. The functionality of the assembler is implemented +by the `spvTextToBinary` library function. + +* `spirv-as` - the standalone assembler + * `/tools/as` + +Use option `-h` to print help. + +### Disassembler tool + +The disassembler reads the binary form, and emits assembly language text. + +The standalone disassembler is the executable called `spirv-dis`, and is located in +`/tools/spirv-dis`. The functionality of the disassembler is implemented +by the `spvBinaryToText` library function. + +* `spirv-dis` - the standalone disassembler + * `/tools/dis` + +Use option `-h` to print help. + +The output includes syntax colouring when printing to the standard output stream, +on Linux, Windows, and OS X. + +### Linker tool + +The linker combines multiple SPIR-V binary modules together, resulting in a single +binary module as output. + +This is a work in progress. +The linker does not support OpenCL program linking options related to math +flags. (See section 5.6.5.2 in OpenCL 1.2) + +* `spirv-link` - the standalone linker + * `/tools/link` + +### Optimizer tool + +The optimizer processes a SPIR-V binary module, applying transformations +in the specified order. + +This is a work in progress, with initially only few available transformations. + +* `spirv-opt` - the standalone optimizer + * `/tools/opt` + +### Validator tool + +*Warning:* This functionality is under development, and is incomplete. + +The standalone validator is the executable called `spirv-val`, and is located in +`/tools/spirv-val`. The functionality of the validator is implemented +by the `spvValidate` library function. + +The validator operates on the binary form. + +* `spirv-val` - the standalone validator + * `/tools/val` + +### Reducer tool + +The reducer shrinks a SPIR-V binary module, guided by a user-supplied +*interestingness test*. + +This is a work in progress, with initially only shrinks a module in a few ways. + +* `spirv-reduce` - the standalone reducer + * `/tools/reduce` + +Run `spirv-reduce --help` to see how to specify interestingness. + +### Fuzzer tool + +The fuzzer transforms a SPIR-V binary module into a semantically-equivalent +SPIR-V binary module by applying transformations in a randomized fashion. + +This is a work in progress, with initially only a few semantics-preserving +transformations. + +* `spirv-fuzz` - the standalone fuzzer + * `/tools/fuzz` + +Run `spirv-fuzz --help` for a detailed list of options. + +### Control flow dumper tool + +The control flow dumper prints the control flow graph for a SPIR-V module as a +[GraphViz](http://www.graphviz.org/) graph. + +This is experimental. + +* `spirv-cfg` - the control flow graph dumper + * `/tools/cfg` + +### Utility filters + +* `spirv-lesspipe.sh` - Automatically disassembles `.spv` binary files for the + `less` program, on compatible systems. For example, set the `LESSOPEN` + environment variable as follows, assuming both `spirv-lesspipe.sh` and + `spirv-dis` are on your executable search path: + ``` + export LESSOPEN='| spirv-lesspipe.sh "%s"' + ``` + Then you page through a disassembled module as follows: + ``` + less foo.spv + ``` + * The `spirv-lesspipe.sh` script will pass through any extra arguments to + `spirv-dis`. So, for example, you can turn off colours and friendly ID + naming as follows: + ``` + export LESSOPEN='| spirv-lesspipe.sh "%s" --no-color --raw-id' + ``` + +* [vim-spirv](https://github.com/kbenzie/vim-spirv) - A vim plugin which + supports automatic disassembly of `.spv` files using the `:edit` command and + assembly using the `:write` command. The plugin also provides additional + features which include; syntax highlighting; highlighting of all ID's matching + the ID under the cursor; and highlighting errors where the `Instruction` + operand of `OpExtInst` is used without an appropriate `OpExtInstImport`. + +* `50spirv-tools.el` - Automatically disassembles '.spv' binary files when + loaded into the emacs text editor, and re-assembles them when saved, + provided any modifications to the file are valid. This functionality + must be explicitly requested by defining the symbol + SPIRV_TOOLS_INSTALL_EMACS_HELPERS as follows: + ``` + cmake -DSPIRV_TOOLS_INSTALL_EMACS_HELPERS=true ... + ``` + + In addition, this helper is only installed if the directory /etc/emacs/site-start.d + exists, which is typically true if emacs is installed on the system. + + Note that symbol IDs are not currently preserved through a load/edit/save operation. + This may change if the ability is added to spirv-as. + + +### Tests + +Tests are only built when googletest is found. Use `ctest` to run all the +tests. + +## Future Work + + +_See the [projects pages](https://github.com/KhronosGroup/SPIRV-Tools/projects) +for more information._ + +### Assembler and disassembler + +* The disassembler could emit helpful annotations in comments. For example: + * Use variable name information from debug instructions to annotate + key operations on variables. + * Show control flow information by annotating `OpLabel` instructions with + that basic block's predecessors. +* Error messages could be improved. + +### Validator + +This is a work in progress. + +### Linker + +* The linker could accept math transformations such as allowing MADs, or other + math flags passed at linking-time in OpenCL. +* Linkage attributes can not be applied through a group. +* Check decorations of linked functions attributes. +* Remove dead instructions, such as OpName targeting imported symbols. + +## Licence + +Full license terms are in [LICENSE](LICENSE) +``` +Copyright (c) 2015-2016 The Khronos Group Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +[spirv-tools-cla]: https://cla-assistant.io/KhronosGroup/SPIRV-Tools +[spirv-tools-projects]: https://github.com/KhronosGroup/SPIRV-Tools/projects +[spirv-tools-mailing-list]: https://www.khronos.org/spir/spirv-tools-mailing-list +[spirv-registry]: https://www.khronos.org/registry/spir-v/ +[spirv-headers]: https://github.com/KhronosGroup/SPIRV-Headers +[googletest]: https://github.com/google/googletest +[googletest-pull-612]: https://github.com/google/googletest/pull/612 +[googletest-issue-610]: https://github.com/google/googletest/issues/610 +[effcee]: https://github.com/google/effcee +[re2]: https://github.com/google/re2 +[CMake]: https://cmake.org/ +[cpp-style-guide]: https://google.github.io/styleguide/cppguide.html +[clang-sanitizers]: http://clang.llvm.org/docs/UsersManual.html#controlling-code-generation +[master-tot-release]: https://github.com/KhronosGroup/SPIRV-Tools/releases/tag/master-tot diff --git a/third_party/spirv-tools/WORKSPACE b/third_party/spirv-tools/WORKSPACE new file mode 100644 index 0000000..5abfc98 --- /dev/null +++ b/third_party/spirv-tools/WORKSPACE @@ -0,0 +1,19 @@ +local_repository( + name = "spirv_headers", + path = "external/spirv-headers", +) + +local_repository( + name = "com_google_googletest", + path = "external/googletest", +) + +local_repository( + name = "com_googlesource_code_re2", + path = "external/re2", +) + +local_repository( + name = "com_google_effcee", + path = "external/effcee", +) diff --git a/third_party/spirv-tools/android_test/Android.mk b/third_party/spirv-tools/android_test/Android.mk new file mode 100644 index 0000000..dbaf93b --- /dev/null +++ b/third_party/spirv-tools/android_test/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_CPP_EXTENSION := .cc .cpp .cxx +LOCAL_SRC_FILES:=test.cpp +LOCAL_MODULE:=spirvtools_test +LOCAL_LDLIBS:=-landroid +LOCAL_CXXFLAGS:=-std=c++11 -fno-exceptions -fno-rtti -Werror +LOCAL_STATIC_LIBRARIES=SPIRV-Tools SPIRV-Tools-opt +include $(BUILD_SHARED_LIBRARY) + +include $(LOCAL_PATH)/../Android.mk diff --git a/third_party/spirv-tools/android_test/jni/Application.mk b/third_party/spirv-tools/android_test/jni/Application.mk new file mode 100644 index 0000000..d7ccd34 --- /dev/null +++ b/third_party/spirv-tools/android_test/jni/Application.mk @@ -0,0 +1,5 @@ +APP_ABI := all +APP_BUILD_SCRIPT := Android.mk +APP_STL := gnustl_static +APP_PLATFORM := android-9 +NDK_TOOLCHAIN_VERSION := 4.9 diff --git a/third_party/spirv-tools/android_test/test.cpp b/third_party/spirv-tools/android_test/test.cpp new file mode 100644 index 0000000..e6a57c1 --- /dev/null +++ b/third_party/spirv-tools/android_test/test.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "spirv-tools/libspirv.hpp" +#include "spirv-tools/optimizer.hpp" + +void android_main(struct android_app* /*state*/) { + spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_2); + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_2); +} diff --git a/third_party/spirv-tools/build_defs.bzl b/third_party/spirv-tools/build_defs.bzl new file mode 100644 index 0000000..30af3bd --- /dev/null +++ b/third_party/spirv-tools/build_defs.bzl @@ -0,0 +1,291 @@ +COMMON_COPTS = [ + "-DSPIRV_CHECK_CONTEXT", + "-DSPIRV_COLOR_TERMINAL", + ] + select({ + "@bazel_tools//src/conditions:windows": [""], + "//conditions:default": [ + "-DSPIRV_LINUX", + "-DSPIRV_TIMER_ENABLED", + "-Wall", + "-Wextra", + "-Wnon-virtual-dtor", + "-Wno-missing-field-initializers", + "-Werror", + "-std=c++11", + "-fvisibility=hidden", + "-fno-exceptions", + "-fno-rtti", + "-Wno-long-long", + "-Wshadow", + "-Wundef", + "-Wconversion", + "-Wno-sign-conversion", + ], +}) + +TEST_COPTS = COMMON_COPTS + select({ + "@bazel_tools//src/conditions:windows": [ + # Disable C4503 "decorated name length exceeded" warning, + # triggered by some heavily templated types. + # We don't care much about that in test code. + # Important to do since we have warnings-as-errors. + "/wd4503" + ], + "//conditions:default": [ + "-Wno-undef", + "-Wno-self-assign", + "-Wno-shadow", + "-Wno-unused-parameter" + ], +}) + +DEBUGINFO_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_debuginfo_grammar_unified1" +CLDEBUGINFO100_GRAMMAR_JSON_FILE = "@spirv_headers//:spirv_ext_inst_opencl_debuginfo_100_grammar_unified1" + +def generate_core_tables(version = None): + if not version: + fail("Must specify version", "version") + grammars = [ + "@spirv_headers//:spirv_core_grammar_" + version, + DEBUGINFO_GRAMMAR_JSON_FILE, + CLDEBUGINFO100_GRAMMAR_JSON_FILE, + ] + outs = [ + "core.insts-{}.inc".format(version), + "operand.kinds-{}.inc".format(version), + ] + fmtargs = grammars + outs + native.genrule( + name = "gen_core_tables_" + version, + srcs = grammars, + outs = outs, + cmd = ( + "$(location :generate_grammar_tables) " + + "--spirv-core-grammar=$(location {0}) " + + "--extinst-debuginfo-grammar=$(location {1}) " + + "--extinst-cldebuginfo100-grammar=$(location {2}) " + + "--core-insts-output=$(location {3}) " + + "--operand-kinds-output=$(location {4})" + ).format(*fmtargs), + tools = [":generate_grammar_tables"], + visibility = ["//visibility:private"], + ) + +def generate_enum_string_mapping(version = None): + if not version: + fail("Must specify version", "version") + grammars = [ + "@spirv_headers//:spirv_core_grammar_" + version, + DEBUGINFO_GRAMMAR_JSON_FILE, + CLDEBUGINFO100_GRAMMAR_JSON_FILE, + ] + outs = [ + "extension_enum.inc", + "enum_string_mapping.inc", + ] + fmtargs = grammars + outs + native.genrule( + name = "gen_enum_string_mapping", + srcs = grammars, + outs = outs, + cmd = ( + "$(location :generate_grammar_tables) " + + "--spirv-core-grammar=$(location {0}) " + + "--extinst-debuginfo-grammar=$(location {1}) " + + "--extinst-cldebuginfo100-grammar=$(location {2}) " + + "--extension-enum-output=$(location {3}) " + + "--enum-string-mapping-output=$(location {4})" + ).format(*fmtargs), + tools = [":generate_grammar_tables"], + visibility = ["//visibility:private"], + ) + +def generate_opencl_tables(version = None): + if not version: + fail("Must specify version", "version") + grammars = [ + "@spirv_headers//:spirv_opencl_grammar_" + version, + ] + outs = ["opencl.std.insts.inc"] + fmtargs = grammars + outs + native.genrule( + name = "gen_opencl_tables_" + version, + srcs = grammars, + outs = outs, + cmd = ( + "$(location :generate_grammar_tables) " + + "--extinst-opencl-grammar=$(location {0}) " + + "--opencl-insts-output=$(location {1})" + ).format(*fmtargs), + tools = [":generate_grammar_tables"], + visibility = ["//visibility:private"], + ) + +def generate_glsl_tables(version = None): + if not version: + fail("Must specify version", "version") + grammars = [ + "@spirv_headers//:spirv_glsl_grammar_" + version, + ] + outs = ["glsl.std.450.insts.inc"] + fmtargs = grammars + outs + native.genrule( + name = "gen_glsl_tables_" + version, + srcs = grammars, + outs = outs, + cmd = ( + "$(location :generate_grammar_tables) " + + "--extinst-glsl-grammar=$(location {0}) " + + "--glsl-insts-output=$(location {1})" + ).format(*fmtargs), + tools = [":generate_grammar_tables"], + visibility = ["//visibility:private"], + ) + +def generate_vendor_tables(extension, operand_kind_prefix = ""): + if not extension: + fail("Must specify extension", "extension") + extension_rule = extension.replace("-", "_").replace(".", "_") + grammars = ["@spirv_headers//:spirv_ext_inst_{}_grammar_unified1".format(extension_rule)] + outs = ["{}.insts.inc".format(extension)] + prefices = [operand_kind_prefix] + fmtargs = grammars + outs + prefices + native.genrule( + name = "gen_vendor_tables_" + extension_rule, + srcs = grammars, + outs = outs, + cmd = ( + "$(location :generate_grammar_tables) " + + "--extinst-vendor-grammar=$(location {0}) " + + "--vendor-insts-output=$(location {1}) " + + "--vendor-operand-kind-prefix={2}" + ).format(*fmtargs), + tools = [":generate_grammar_tables"], + visibility = ["//visibility:private"], + ) + +def generate_extinst_lang_headers(name, grammar = None): + if not grammar: + fail("Must specify grammar", "grammar") + outs = [name + ".h"] + fmtargs = outs + native.genrule( + name = "gen_extinst_lang_headers_" + name, + srcs = [grammar], + outs = outs, + cmd = ( + "$(location :generate_language_headers) " + + "--extinst-grammar=$< " + + "--extinst-output-path=$(location {0})" + ).format(*fmtargs), + tools = [":generate_language_headers"], + visibility = ["//visibility:private"], + ) + +def base_test(name, srcs, deps = []): + if srcs == []: + return + if name[-5:] != "_test": + name = name + "_test" + native.cc_test( + name = "base_" + name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS, + size = "large", + deps = [ + ":test_common", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + ) + +def link_test(name, srcs, deps = []): + if name[-5:] != "_test": + name = name + "_test" + native.cc_test( + name = "link_" + name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS, + size = "large", + deps = [ + ":link_test_common", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + ) + +def opt_test(name, srcs, deps = []): + if name[-5:] != "_test": + name = name + "_test" + native.cc_test( + name = "opt_" + name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS, + size = "large", + deps = [ + ":opt_test_common", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + ) + +def reduce_test(name, srcs, deps = []): + if name[-5:] != "_test": + name = name + "_test" + native.cc_test( + name = "reduce_" + name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS, + size = "large", + deps = [ + ":reduce_test_common", + ":spirv_tools_reduce", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + ) + +def util_test(name, srcs, deps = []): + if name[-5:] != "_test": + name = name + "_test" + native.cc_test( + name = "util_" + name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS, + size = "large", + deps = [ + ":opt_test_common", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + ) + +def val_test(name, srcs = [], copts = [], deps = [], **kwargs): + if name[-5:] != "_test": + name = name + "_test" + if name[:4] != "val_": + name = "val_" + name + native.cc_test( + name = name, + srcs = srcs, + compatible_with = [], + copts = TEST_COPTS + copts, + size = "large", + deps = [ + ":val_test_common", + "@com_google_googletest//:gtest_main", + "@com_google_googletest//:gtest", + "@com_google_effcee//:effcee", + ] + deps, + **kwargs + ) diff --git a/third_party/spirv-tools/build_overrides/build.gni b/third_party/spirv-tools/build_overrides/build.gni new file mode 100644 index 0000000..833fcd3 --- /dev/null +++ b/third_party/spirv-tools/build_overrides/build.gni @@ -0,0 +1,46 @@ +# Copyright 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Variable that can be used to support multiple build scenarios, like having +# Chromium specific targets in a client project's GN file etc. +build_with_chromium = false + +# Don't use Chromium's third_party/binutils. +linux_use_bundled_binutils_override = false + +declare_args() { + # Android 32-bit non-component, non-clang builds cannot have symbol_level=2 + # due to 4GiB file size limit, see https://crbug.com/648948. + # Set this flag to true to skip the assertion. + ignore_elf32_limitations = false + + # Use the system install of Xcode for tools like ibtool, libtool, etc. + # This does not affect the compiler. When this variable is false, targets will + # instead use a hermetic install of Xcode. [The hermetic install can be + # obtained with gclient sync after setting the environment variable + # FORCE_MAC_TOOLCHAIN]. + use_system_xcode = "" +} + +if (use_system_xcode == "") { + if (target_os == "mac") { + _result = exec_script("//build/mac/should_use_hermetic_xcode.py", + [ target_os ], + "value") + use_system_xcode = _result == 0 + } + if (target_os == "ios") { + use_system_xcode = true + } +} diff --git a/third_party/spirv-tools/build_overrides/gtest.gni b/third_party/spirv-tools/build_overrides/gtest.gni new file mode 100644 index 0000000..c8b1bae --- /dev/null +++ b/third_party/spirv-tools/build_overrides/gtest.gni @@ -0,0 +1,25 @@ +# Copyright 2018 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Exclude support for registering main function in multi-process tests. +gtest_include_multiprocess = false + +# Exclude support for platform-specific operations across unit tests. +gtest_include_platform_test = false + +# Exclude support for testing Objective C code on OS X and iOS. +gtest_include_objc_support = false + +# Exclude support for flushing coverage files on iOS. +gtest_include_ios_coverage = false diff --git a/third_party/spirv-tools/build_overrides/spirv_tools.gni b/third_party/spirv-tools/build_overrides/spirv_tools.gni new file mode 100644 index 0000000..24aa033 --- /dev/null +++ b/third_party/spirv-tools/build_overrides/spirv_tools.gni @@ -0,0 +1,25 @@ +# Copyright 2018 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# These are variables that are overridable by projects that include +# SPIRV-Tools. The values in this file are the defaults for when we are +# building from SPIRV-Tools' repository. + +# Whether we are building from SPIRV-Tools' repository. +# MUST be set to false in other projects. +spirv_tools_standalone = true + +# The path to SPIRV-Tools' dependencies +spirv_tools_googletest_dir = "//external/googletest" +spirv_tools_spirv_headers_dir = "//external/spirv-headers" diff --git a/third_party/spirv-tools/cmake/SPIRV-Tools-shared.pc.in b/third_party/spirv-tools/cmake/SPIRV-Tools-shared.pc.in new file mode 100644 index 0000000..0dcaa27 --- /dev/null +++ b/third_party/spirv-tools/cmake/SPIRV-Tools-shared.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: SPIRV-Tools +Description: Tools for SPIR-V +Version: @CURRENT_VERSION@ +URL: https://github.com/KhronosGroup/SPIRV-Tools + +Libs: -L${libdir} @SPIRV_SHARED_LIBRARIES@ +Cflags: -I${includedir} diff --git a/third_party/spirv-tools/cmake/SPIRV-Tools.pc.in b/third_party/spirv-tools/cmake/SPIRV-Tools.pc.in new file mode 100644 index 0000000..2984dc5 --- /dev/null +++ b/third_party/spirv-tools/cmake/SPIRV-Tools.pc.in @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: SPIRV-Tools +Description: Tools for SPIR-V +Version: @CURRENT_VERSION@ +URL: https://github.com/KhronosGroup/SPIRV-Tools + +Libs: -L${libdir} @SPIRV_LIBRARIES@ +Cflags: -I${includedir} diff --git a/third_party/spirv-tools/cmake/write_pkg_config.cmake b/third_party/spirv-tools/cmake/write_pkg_config.cmake new file mode 100644 index 0000000..d367ce3 --- /dev/null +++ b/third_party/spirv-tools/cmake/write_pkg_config.cmake @@ -0,0 +1,31 @@ +# Copyright (c) 2017 Pierre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# First, retrieve the current version from CHANGES +file(STRINGS ${CHANGES_FILE} CHANGES_CONTENT) +string( +REGEX + MATCH "v[0-9]+(.[0-9]+)?(-dev)? [0-9]+-[0-9]+-[0-9]+" + FIRST_VERSION_LINE + ${CHANGES_CONTENT}) +string( +REGEX + REPLACE "^v([^ ]+) .+$" "\\1" + CURRENT_VERSION + "${FIRST_VERSION_LINE}") +# If this is a development version, replace "-dev" by ".0" as pkg-config nor +# CMake support "-dev" in the version. +# If it's not a "-dev" version then ensure it ends with ".1" +string(REGEX REPLACE "-dev.1" ".0" CURRENT_VERSION "${CURRENT_VERSION}.1") +configure_file(${TEMPLATE_FILE} ${OUT_FILE} @ONLY) diff --git a/third_party/spirv-tools/codereview.settings b/third_party/spirv-tools/codereview.settings new file mode 100644 index 0000000..ef84cf8 --- /dev/null +++ b/third_party/spirv-tools/codereview.settings @@ -0,0 +1,2 @@ +# This file is used by git cl to get repository specific information. +CODE_REVIEW_SERVER: github.com diff --git a/third_party/spirv-tools/docs/downloads.md b/third_party/spirv-tools/docs/downloads.md new file mode 100644 index 0000000..9c7d856 --- /dev/null +++ b/third_party/spirv-tools/docs/downloads.md @@ -0,0 +1,14 @@ +# Downloads +Download the latest builds. + +## Release +| Windows | Linux | MacOS | +| --- | --- | --- | +| [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_release.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_release.html) | +| | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_release.html) | | + +## Debug +| Windows | Linux | MacOS | +| --- | --- | --- | +| [MSVC 2017](https://storage.googleapis.com/spirv-tools/badges/build_link_windows_vs2017_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_clang_debug.html) | [clang](https://storage.googleapis.com/spirv-tools/badges/build_link_macos_clang_debug.html) | +| | [gcc](https://storage.googleapis.com/spirv-tools/badges/build_link_linux_gcc_debug.html) | | diff --git a/third_party/spirv-tools/docs/projects.md b/third_party/spirv-tools/docs/projects.md new file mode 100644 index 0000000..8f7f0bc --- /dev/null +++ b/third_party/spirv-tools/docs/projects.md @@ -0,0 +1,82 @@ +# Tracking SPIRV-Tools work with GitHub projects + +We are experimenting with using the [GitHub Project +feature](https://help.github.com/articles/tracking-the-progress-of-your-work-with-projects/) +to track progress toward large goals. + +For more on GitHub Projects in general, see: +* [Introductory blog post](https://github.com/blog/2256-a-whole-new-github-universe-announcing-new-tools-forums-and-features) +* [Introductory video](https://www.youtube.com/watch?v=C6MGKHkNtxU) + +The current SPIRV-Tools project list can be found at +[https://github.com/KhronosGroup/SPIRV-Tools/projects](https://github.com/KhronosGroup/SPIRV-Tools/projects) + +## How we use a Project + +A GitHub Project is a set of work with an overall purpose, and +consists of a collection of *Cards*. +Each card is either a *Note* or a regular GitHub *Issue.* +A Note can be converted to an Issue. + +In our projects, a card represents work, i.e. a change that can +be applied to the repository. +The work could be a feature, a bug to be fixed, documentation to be +updated, etc. + +A project and its cards are used as a [Kanban +board](https://en.wikipedia.org/wiki/Kanban_board), where cards progress +through a workflow starting with ideas through to implementation and completion. + +In our usage, a *project manager* is someone who organizes the work. +They manage the creation and movement of cards +through the project workflow: +* They create cards to capture ideas, or to decompose large ideas into smaller + ones. +* They determine if the work for a card has been completed. +* Normally they are the person (or persons) who can approve and merge a pull + request into the `master` branch. + +Our projects organize cards into the following columns: +* `Ideas`: Work which could be done, captured either as Cards or Notes. + * A card in this column could be marked as a [PLACEHOLDER](#placeholders). +* `Ready to start`: Issues which represent work we'd like to do, and which + are not blocked by other work. + * The issue should be narrow enough that it can usually be addressed by a + single pull request. + * We want these to be Issues (not Notes) so that someone can claim the work + by updating the Issue with their intent to do the work. + Once an Issue is claimed, the project manager moves the corresponding card + from `Ready to start` to `In progress`. +* `In progress`: Issues which were in `Ready to start` but which have been + claimed by someone. +* `Done`: Issues which have been resolved, by completing their work. + * The changes have been applied to the repository, typically by being pushed + into the `master` branch. + * Other kinds of work could update repository settings, for example. +* `Rejected ideas`: Work which has been considered, but which we don't want + implemented. + * We keep rejected ideas so they are not proposed again. This serves + as a form of institutional memory. + * We should record why an idea is rejected. For this reason, a rejected + idea is likely to be an Issue which has been closed. + +## Prioritization + +We are considering prioritizing cards in the `Ideas` and `Ready to start` +columns so that things that should be considered first float up to the top. + +Experience will tell us if we stick to that rule, and if it proves helpful. + +## Placeholders + +A *placeholder* is a Note or Issue that represents a possibly large amount +of work that can be broadly defined but which may not have been broken down +into small implementable pieces of work. + +Use a placeholder to capture a big idea, but without doing the upfront work +to consider all the details of how it should be implemented. +Over time, break off pieces of the placeholder into implementable Issues. +Move those Issues into the `Ready to start` column when they become unblocked. + +We delete the placeholder when all its work has been decomposed into +implementable cards. diff --git a/third_party/spirv-tools/docs/spirv-fuzz.md b/third_party/spirv-tools/docs/spirv-fuzz.md new file mode 100644 index 0000000..e5439e3 --- /dev/null +++ b/third_party/spirv-tools/docs/spirv-fuzz.md @@ -0,0 +1,83 @@ +# Guide to writing a spirv-fuzz fuzzer pass + +Writing a spirv-fuzz fuzzer pass usually requires two main contributions: + +- A *transformation*, capturing a small semantics-preserving change that can be made to a SPIR-V module. This requires adding a protobuf message representing the transformation, and a corresponding class that implements the `Transformation` interface. +- A new *fuzzer pass* class, implementing the `FuzzerPass` interface, that knows how to walk a SPIR-V module and apply the new transformation in a randomized fashion. + +In some cases, more than one kind of transformation is required for a single fuzzer pass, and in some cases the transformations that a new fuzzer pass requires have already been introduced by existing passes. But the most common case is to introduce a transformation and fuzzer pass together. + +As an example, let's consider the `TransformationSetSelectionControl` transformation. In SPIR-V, an `OpSelectionMerge` instruction (which intuitively indicates the start of an `if` or `switch` statement in a function) has a *selection control* mask, that can be one of `None`, `Flatten` or `DontFlatten`. The details of these do not matter much for this little tutorial, but in brief, this parameter provides a hint to the shader compiler as to whether it would be profitable to attempt to flatten a piece of conditional code so that all of its statements are executed in a predicated fashion. + +As the selection control mask is just a hint, changing the value of this mask should have no semantic impact on the module. The `TransformationSelectionControl` transformation specifies a new value for a given selection control mask. + +## Adding a new protobuf message + +Take a look at the `Transformation` message in `spvtoolsfuzz.proto`. This has a `oneof` field that can be any one of the different spirv-fuzz transformations. Observe that one of the options is `TransformationSetSelectionControl`. When adding a transformation you first need to add an option for your transformation to the end of the `oneof` declaration. + +Now look at the `TransformationSetSelectionControl` message. If adding your own transformation you need to add a new message for your transformation, and it should be placed alphabetically with respect to other transformations. + +The fields of `TransformationSetSelectionControl` provide just enough information to (a) determine whether a given example of this transformation is actually applicable, and (b) apply the transformation in the case that it is applicable. The details of the transformation message will vary a lot between transformations. In this case, the message has a `block_id` field, specifying a block that must end with `OpSelectionMerge`, and a `selection_control` field, which is the new value for the selection control mask of the `OpSelectionMerge` instruction. + +## Adding a new transformation class + +If your transformation is called `TransformationSomeThing`, you need to add `transformation_some_thing.h` and `transformation_some_thing.cpp` to `source/fuzz` and the corresponding `CMakeLists.txt` file. So for `TransformationSetSelectionControl` we have `transformation_selection_control.h` and `transformation_selection_control.cpp`, and we will use this as an example to illustrate the expected contents of these files. + +The header file contains the specification of a class, `TransformationSetSelectionControl`, that implements the `Transformation` interface (from `transformation.h`). + +A transformation class should always have a single field, which should be the associated protobuf message; in our case: + +``` + private: + protobufs::TransformationSetSelectionControl message_; +``` + +and two public constructors, one that takes a protobuf message; in our case: + +``` + explicit TransformationSetSelectionControl( + const protobufs::TransformationSetSelectionControl& message); +``` + +and one that takes a parameter for each protobuf message field; in our case: + +``` + TransformationSetSelectionControl(uint32_t block_id); +``` + +The first constructor allows an instance of the class to be created from a corresponding protobuf message. The second should provide the ingredients necessary to populate a protobuf message. + +The class should also override the `IsApplicable`, `Apply` and `ToMessage` methods from `Transformation`. + +See `transformation_set_selection_control.h` for an example. + +The `IsApplicable` method should have a comment in the header file describing the conditions for applicability in simple terms. These conditions should be implemented in the body of this method in the `.cpp` file. + +In the case of `TransformationSetSelectionControl`, `IsApplicable` involves checking that `block_id` is indeed the id of a block that has an `OpSelectoinMerge` instruction, and that `selection_control` is a valid selection mask. + +The `Apply` method should have a comment in the header file summarising the result of applying the transformation. It should be implemented in the `.cpp` file, and you should assume that `IsApplicable` holds whenever `Apply` is invoked. + +## Writing tests for the transformation class + +Whenever you add a transformation class, `TransformationSomeThing`, you should add an associated test file, `transformation_some_thing_test.cpp`, under `test/fuzz`, adding it to the associated `CMakeLists.txt` file. + +For example `test/fuzz/transformation_set_selection_control_test.cpp` contains tests for `TransformationSetSelectionControl`. Your tests should aim to cover one example from each scenario where the transformation is inapplicable, and check that it is indeed deemed inapplicable, and then check that the transformation does the right thing when applied in a few different ways. + +For example, the tests for `TransformationSetSelectionControl` check that a transformation of this kind is inapplicable if the `block_id` field of the transformation is not a block, or does not end in `OpSelectionMerge`, or if the `selection_control` mask has an illegal value. It also checks that applying a sequence of valid transformations to a SPIR-V shader leads to a shader with appropriately modified selection controls. + +## Adding a new fuzzer pass class + +A *fuzzer pass* traverses a SPIR-V module looking for places to apply a certain kind of transformation, and randomly decides at which of these points to actually apply the transformation. It might be necessary to apply other transformations in order to apply a given transformation (for example, if a transformation requires a certain type to be present in the module, said type can be added if not already present via another transformation). + +A fuzzer pass implements the `FuzzerPass` interface, and overrides its `Apply` method. If your fuzzer pass is named `FuzzerPassSomeThing` then it should be represented by `fuzzer_pass_some_thing.h` and `fuzzer_pass_some_thing.cpp`, under `source/fuzz`; these should be added to the associated `CMakeLists.txt` file. + +Have a look at the source filed for `FuzzerPassAdjustSelectionControls`. This pass considers every block that ends with `OpSelectionMerge`. It decides randomly whether to adjust the selection control of this merge instruction via: + +``` +if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) { + continue; +} +``` + +The `GetChanceOfAddingSelectionControl()` method has been added to `FuzzerContext` specifically to support this pass, and returns a percentage between 0 and 100. It returns the `chance_of_adjusting_selection_control_` of `FuzzerContext`, which is randomly initialized to lie with the interval defined by `kChanceOfAdjustingSelectionControl` in `fuzzer_context.cpp`. For any pass you write, you will need to add an analogous `GetChanceOf...` method to `FuzzerContext`, backed by an appropriate field, and you will need to decide on lower and upper bounds for this field and specify these via a `kChanceOf...` constant. diff --git a/third_party/spirv-tools/docs/syntax.md b/third_party/spirv-tools/docs/syntax.md new file mode 100644 index 0000000..c135d01 --- /dev/null +++ b/third_party/spirv-tools/docs/syntax.md @@ -0,0 +1,238 @@ +# SPIR-V Assembly language syntax + +## Overview + +The assembly attempts to adhere to the binary form from Section 3 of the SPIR-V +spec as closely as possible, with one exception aiming at improving the text's +readability. The `` generated by an instruction is moved to the +beginning of that instruction and followed by an `=` sign. This allows us to +distinguish between variable definitions and uses and locate value definitions +more easily. + +Here is an example: + +``` + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %3 "main" + OpExecutionMode %3 LocalSize 64 64 1 +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +%4 = OpLabel + OpReturn + OpFunctionEnd +``` + +A module is a sequence of instructions, separated by whitespace. +An instruction is an opcode name followed by operands, separated by +whitespace. Typically each instruction is presented on its own line, +but the assembler does not enforce this rule. + +The opcode names and expected operands are described in Section 3 of +the SPIR-V specification. An operand is one of: +* a literal integer: A decimal integer, or a hexadecimal integer. + A hexadecimal integer is indicated by a leading `0x` or `0X`. A hex + integer supplied for a signed integer value will be sign-extended. + For example, `0xffff` supplied as the literal for an `OpConstant` + on a signed 16-bit integer type will be interpreted as the value `-1`. +* a literal floating point number, in decimal or hexadecimal form. + See [below](#floats). +* a literal string. + * A literal string is everything following a double-quote `"` until the + following un-escaped double-quote. This includes special characters such + as newlines. + * A backslash `\` may be used to escape characters in the string. The `\` + may be used to escape a double-quote or a `\` but is simply ignored when + preceding any other character. +* a named enumerated value, specific to that operand position. For example, + the `OpMemoryModel` takes a named Addressing Model operand (e.g. `Logical` or + `Physical32`), and a named Memory Model operand (e.g. `Simple` or `OpenCL`). + Named enumerated values are only meaningful in specific positions, and will + otherwise generate an error. +* a mask expression, consisting of one or more mask enum names separated + by `|`. For example, the expression `NotNaN|NotInf|NSZ` denotes the mask + which is the combination of the `NotNaN`, `NotInf`, and `NSZ` flags. +* an injected immediate integer: `!`. See [below](#immediate). +* an ID, e.g. `%foo`. See [below](#id). +* the name of an extended instruction. For example, `sqrt` in an extended + instruction such as `%f = OpExtInst %f32 %OpenCLImport sqrt %arg` +* the name of an opcode for OpSpecConstantOp, but where the `Op` prefix + is removed. For example, the following indicates the use of an integer + addition in a specialization constant computation: + `%sum = OpSpecConstantOp %i32 IAdd %a %b` + +## ID Definitions & Usage + + +An ID _definition_ pertains to the `` of an instruction, and ID +_usage_ is a use of an ID as an input to an instruction. + +An ID in the assembly language begins with `%` and must be followed by a name +consisting of one or more letters, numbers or underscore characters. + +For every ID in the assembly program, the assembler generates a unique number +called the ID's internal number. Then each ID reference translates into its +internal number in the SPIR-V output. Internal numbers are unique within the +compilation unit: no two IDs in the same unit will share internal numbers. + +The disassembler generates IDs where the name is always a decimal number +greater than 0. + +So the example can be rewritten using more user-friendly names, as follows: +``` + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint GLCompute %main "main" + OpExecutionMode %main LocalSize 64 64 1 + %void = OpTypeVoid +%fnMain = OpTypeFunction %void + %main = OpFunction %void None %fnMain +%lbMain = OpLabel + OpReturn + OpFunctionEnd +``` + +## Floating point literals + + +The assembler and disassembler support floating point literals in both +decimal and hexadecimal form. + +The syntax for a floating point literal is the same as floating point +constants in the C programming language, except: +* An optional leading minus (`-`) is part of the literal. +* An optional type specifier suffix is not allowed. +Infinity and NaN values are expressed in hexadecimal float literals +by using the maximum representable exponent for the bit width. + +For example, in 32-bit floating point, 8 bits are used for the exponent, and the +exponent bias is 127. So the maximum representable unbiased exponent is 128. +Therefore, we represent the infinities and some NaNs as follows: + +``` +%float32 = OpTypeFloat 32 +%inf = OpConstant %float32 0x1p+128 +%neginf = OpConstant %float32 -0x1p+128 +%aNaN = OpConstant %float32 0x1.8p+128 +%moreNaN = OpConstant %float32 -0x1.0002p+128 +``` +The assembler preserves all the bits of a NaN value. For example, the encoding +of `%aNaN` in the previous example is the same as the word with bits +`0x7fc00000`, and `%moreNaN` is encoded as `0xff800100`. + +The disassembler prints infinite, NaN, and subnormal values in hexadecimal form. +Zero and normal values are printed in decimal form with enough digits +to preserve all significand bits. + +## Arbitrary Integers + + +When writing tests it can be useful to emit an invalid 32 bit word into the +binary stream at arbitrary positions within the assembly. To specify an +arbitrary word into the stream the prefix `!` is used, this takes the form +`!`. Here is an example. + +``` +OpCapability !0x0000FF00 +``` + +Any token in a valid assembly program may be replaced by `!` -- even +tokens that dictate how the rest of the instruction is parsed. Consider, for +example, the following assembly program: + +``` +%4 = OpConstant %1 123 456 789 OpExecutionMode %2 LocalSize 11 22 33 +OpExecutionMode %3 InputLines +``` + +The tokens `OpConstant`, `LocalSize`, and `InputLines` may be replaced by random +`!` values, and the assembler will still assemble an output binary with +three instructions. It will not necessarily be valid SPIR-V, but it will +faithfully reflect the input text. + +You may wonder how the assembler recognizes the instruction structure (including +instruction boundaries) in the text with certain crucial tokens replaced by +arbitrary integers. If, say, `OpConstant` becomes a `!` whose value +differs from the binary representation of `OpConstant` (remember that this +feature is intended for fine-grain control in SPIR-V testing), the assembler +generally has no idea what that value stands for. So how does it know there is +exactly one `` and three number literals following in that instruction, +before the next one begins? And if `LocalSize` is replaced by an arbitrary +`!`, how does it know to take the next three tokens (instead of zero or +one, both of which are possible in the absence of certainty that `LocalSize` +provided)? The answer is a simple rule governing the parsing of instructions +with `!` in them: + +When a token in the assembly program is a `!`, that integer value is +emitted into the binary output, and parsing proceeds differently than before: +each subsequent token not recognized as an OpCode or a `` is emitted +into the binary output without any checking; when a recognizable OpCode or a +`` is eventually encountered, it begins a new instruction and parsing +returns to normal. (If a subsequent OpCode is never found, then this alternate +parsing mode handles all the remaining tokens in the program.) + +The assembler processes the tokens encountered in alternate parsing mode as +follows: + +* If the token is a number literal, since context may be lost, the number + is interpreted as a 32-bit value and output as a single word. In order to + specify multiple-word literals in alternate-parsing mode, further uses of + `!` tokens may be required. + All formats supported by `strtoul()` are accepted. +* If the token is a string literal, it outputs a sequence of words representing + the string as defined in the SPIR-V specification for Literal String. +* If the token is an ID, it outputs the ID's internal number. +* If the token is another `!`, it outputs that integer. +* Any other token causes the assembler to quit with an error. + +Note that this has some interesting consequences, including: + +* When an OpCode is replaced by `!`, the integer value should encode + the instruction's word count, as specified in the physical-layout section of + the SPIR-V specification. + +* Consecutive instructions may have their OpCode replaced by `!` and + still produce valid SPIR-V. For example, `!262187 %1 %2 "abc" !327739 %1 %3 6 + %2` will successfully assemble into SPIR-V declaring a constant and a + PrivateGlobal variable. + +* Enums (such as `DontInline` or `SubgroupMemory`, for instance) are not handled + by the alternate parsing mode. They must be replaced by `!` for + successful assembly. + +* The `` on the left-hand side of an assignment cannot be a + `!`. The `` can be still be manually controlled if desired + by expressing the entire instruction as `!` tokens for its opcode and + operands. + +* The `=` sign cannot be processed by the alternate parsing mode if the OpCode + following it is a `!`. + +* When replacing a named ID with `!`, it is possible to generate + unintentionally valid SPIR-V. If the integer provided happens to equal a + number generated for an existing named ID, it will result in a reference to + that named ID being output. This may be valid SPIR-V, contrary to the + presumed intention of the writer. + +## Notes + +* Some enumerants cannot be used by name, because the target instruction +in which they are meaningful take an ID reference instead of a literal value. +For example: + * Named enumerated value `CmdExecTime` from section 3.30 Kernel + Profiling Info is used in constructing a mask value supplied as + an ID for `OpCaptureEventProfilingInfo`. But no other instruction + has enough context to bring the enumerant names from section 3.30 + into scope. + * Similarly, the names in section 3.29 Kernel Enqueue Flags are used to + construct a value supplied as an ID to the Flags argument of + OpEnqueueKernel. + * Similarly for the names in section 3.25 Memory Semantics. + * Similarly for the names in section 3.27 Scope. +* Some enumerants cannot be used by name, because they only name values +returned by an instruction: + * Enumerants from 3.12 Image Channel Order name possible values returned + by the `OpImageQueryOrder` instruction. + * Enumerants from 3.13 Image Channel Data Type name possible values + returned by the `OpImageQueryFormat` instruction. diff --git a/third_party/spirv-tools/examples/CMakeLists.txt b/third_party/spirv-tools/examples/CMakeLists.txt new file mode 100644 index 0000000..fd627cb --- /dev/null +++ b/third_party/spirv-tools/examples/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Add a SPIR-V Tools example. Signature: +# add_spvtools_example( +# TARGET target_name +# SRCS src_file1.cpp src_file2.cpp +# LIBS lib_target1 lib_target2 +# ) +function(add_spvtools_example) + if (NOT ${SPIRV_SKIP_EXECUTABLES}) + set(one_value_args TARGET) + set(multi_value_args SRCS LIBS) + cmake_parse_arguments( + ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN}) + + add_executable(${ARG_TARGET} ${ARG_SRCS}) + spvtools_default_compile_options(${ARG_TARGET}) + target_link_libraries(${ARG_TARGET} PRIVATE ${ARG_LIBS}) + set_property(TARGET ${ARG_TARGET} PROPERTY FOLDER "SPIRV-Tools examples") + endif() +endfunction() + +add_subdirectory(cpp-interface) diff --git a/third_party/spirv-tools/examples/cpp-interface/CMakeLists.txt b/third_party/spirv-tools/examples/cpp-interface/CMakeLists.txt new file mode 100644 index 0000000..7887ee7 --- /dev/null +++ b/third_party/spirv-tools/examples/cpp-interface/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_spvtools_example( + TARGET spirv-tools-cpp-example + SRCS main.cpp + LIBS SPIRV-Tools-opt +) diff --git a/third_party/spirv-tools/examples/cpp-interface/main.cpp b/third_party/spirv-tools/examples/cpp-interface/main.cpp new file mode 100644 index 0000000..a1e22c7 --- /dev/null +++ b/third_party/spirv-tools/examples/cpp-interface/main.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This program demonstrates basic SPIR-V module processing using +// SPIRV-Tools C++ API: +// * Assembling +// * Validating +// * Optimizing +// * Disassembling + +#include +#include +#include + +#include "spirv-tools/libspirv.hpp" +#include "spirv-tools/optimizer.hpp" + +int main() { + const std::string source = + " OpCapability Linkage " + " OpCapability Shader " + " OpMemoryModel Logical GLSL450 " + " OpSource GLSL 450 " + " OpDecorate %spec SpecId 1 " + " %int = OpTypeInt 32 1 " + " %spec = OpSpecConstant %int 0 " + "%const = OpConstant %int 42"; + + spvtools::SpirvTools core(SPV_ENV_UNIVERSAL_1_3); + spvtools::Optimizer opt(SPV_ENV_UNIVERSAL_1_3); + + auto print_msg_to_stderr = [](spv_message_level_t, const char*, + const spv_position_t&, const char* m) { + std::cerr << "error: " << m << std::endl; + }; + core.SetMessageConsumer(print_msg_to_stderr); + opt.SetMessageConsumer(print_msg_to_stderr); + + std::vector spirv; + if (!core.Assemble(source, &spirv)) return 1; + if (!core.Validate(spirv)) return 1; + + opt.RegisterPass(spvtools::CreateSetSpecConstantDefaultValuePass({{1, "42"}})) + .RegisterPass(spvtools::CreateFreezeSpecConstantValuePass()) + .RegisterPass(spvtools::CreateUnifyConstantPass()) + .RegisterPass(spvtools::CreateStripDebugInfoPass()); + if (!opt.Run(spirv.data(), spirv.size(), &spirv)) return 1; + + std::string disassembly; + if (!core.Disassemble(spirv, &disassembly)) return 1; + std::cout << disassembly << "\n"; + + return 0; +} diff --git a/third_party/spirv-tools/external/CMakeLists.txt b/third_party/spirv-tools/external/CMakeLists.txt new file mode 100644 index 0000000..152cfc4 --- /dev/null +++ b/third_party/spirv-tools/external/CMakeLists.txt @@ -0,0 +1,181 @@ +# Copyright (c) 2015-2016 The Khronos Group Inc. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Utility functions for pushing & popping variables. +function(push_variable var val) + set("${var}_SAVE_STACK" "${${var}}" "${${var}_SAVE_STACK}" PARENT_SCOPE) + set(${var} ${val} PARENT_SCOPE) +endfunction() +function(pop_variable var) + set(save_stack "${${var}_SAVE_STACK}") + list(GET save_stack 0 val) + list(REMOVE_AT save_stack 0) + set("${var}_SAVE_STACK" "${save_stack}" PARENT_SCOPE) + set(${var} ${val} PARENT_SCOPE) +endfunction() + +if (DEFINED SPIRV-Headers_SOURCE_DIR) + # This allows flexible position of the SPIRV-Headers repo. + set(SPIRV_HEADER_DIR ${SPIRV-Headers_SOURCE_DIR}) +else() + if (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/SPIRV-Headers) + set(SPIRV_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/SPIRV-Headers) + else() + set(SPIRV_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spirv-headers) + endif() +endif() + +if (IS_DIRECTORY ${SPIRV_HEADER_DIR}) + # TODO(dneto): We should not be modifying the parent scope. + set(SPIRV_HEADER_INCLUDE_DIR ${SPIRV_HEADER_DIR}/include PARENT_SCOPE) + + # Add SPIRV-Headers as a sub-project if it isn't already defined. + # Do this so enclosing projects can use SPIRV-Headers_SOURCE_DIR to find + # headers to include. + if (NOT DEFINED SPIRV-Headers_SOURCE_DIR) +# Filament specific changes + #set(SPIRV_HEADERS_SKIP_INSTALL ON) + #set(SPIRV_HEADERS_SKIP_EXAMPLES ON) +# End Filament specific changes + add_subdirectory(${SPIRV_HEADER_DIR}) + endif() +else() + message(FATAL_ERROR + "SPIRV-Headers was not found - please checkout a copy under external/.") +endif() + +if (NOT ${SPIRV_SKIP_TESTS}) + # Find gmock if we can. If it's not already configured, then try finding + # it in external/googletest. + if (TARGET gmock) + message(STATUS "Google Mock already configured") + else() + set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest) + if(EXISTS ${GMOCK_DIR}) + if(MSVC) + # Our tests use ::testing::Combine. Work around a compiler + # detection problem in googletest, where that template is + # accidentally disabled for VS 2017. + # See https://github.com/google/googletest/issues/1352 + add_definitions(-DGTEST_HAS_COMBINE=1) + endif() + if(WIN32) + option(gtest_force_shared_crt + "Use shared (DLL) run-time lib even when Google Test is built as static lib." + ON) + endif() + # gtest requires special defines for building as a shared + # library, simply always build as static. + push_variable(BUILD_SHARED_LIBS 0) + add_subdirectory(${GMOCK_DIR} EXCLUDE_FROM_ALL) + pop_variable(BUILD_SHARED_LIBS) + endif() + endif() + if (TARGET gmock) + set(GTEST_TARGETS + gtest + gtest_main + gmock + gmock_main + ) + foreach(target ${GTEST_TARGETS}) + set_property(TARGET ${target} PROPERTY FOLDER GoogleTest) + endforeach() + endif() + + # Find Effcee and RE2, for testing. + + # First find RE2, since Effcee depends on it. + # If already configured, then use that. Otherwise, prefer to find it under 're2' + # in this directory. + if (NOT TARGET re2) + # If we are configuring RE2, then turn off its testing. It takes a long time and + # does not add much value for us. If an enclosing project configured RE2, then it + # has already chosen whether to enable RE2 testing. + set(RE2_BUILD_TESTING OFF CACHE STRING "Run RE2 Tests") + if (NOT RE2_SOURCE_DIR) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/re2) + set(RE2_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/re2" CACHE STRING "RE2 source dir" ) + endif() + endif() + endif() + + if (NOT TARGET effcee) + # Expect to find effcee in this directory. + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/effcee) + # If we're configuring RE2 (via Effcee), then turn off RE2 testing. + if (NOT TARGET re2) + set(RE2_BUILD_TESTING OFF) + endif() + if (MSVC) + # SPIRV-Tools uses the shared CRT with MSVC. Tell Effcee to do the same. + set(EFFCEE_ENABLE_SHARED_CRT ON) + endif() + set(EFFCEE_BUILD_SAMPLES OFF CACHE BOOL "Do not build Effcee examples") + if (NOT TARGET effcee) + set(EFFCEE_BUILD_TESTING OFF CACHE BOOL "Do not build Effcee test suite") + endif() + push_variable(BUILD_SHARED_LIBS 0) # effcee does not export any symbols for building as a DLL. Always build as static. + add_subdirectory(effcee EXCLUDE_FROM_ALL) + pop_variable(BUILD_SHARED_LIBS) + set_property(TARGET effcee PROPERTY FOLDER Effcee) + # Turn off warnings for effcee and re2 + set_property(TARGET effcee APPEND PROPERTY COMPILE_OPTIONS -w) + set_property(TARGET re2 APPEND PROPERTY COMPILE_OPTIONS -w) + endif() + endif() +endif() + +if(SPIRV_BUILD_FUZZER) + + function(backup_compile_options) + get_property( + SPIRV_TOOLS_BACKUP_EXTERNAL_COMPILE_OPTIONS + DIRECTORY + PROPERTY COMPILE_OPTIONS + ) + endfunction() + + function(restore_compile_options) + set_property( + DIRECTORY + PROPERTY COMPILE_OPTIONS + ${SPIRV_TOOLS_BACKUP_EXTERNAL_COMPILE_OPTIONS} + ) + endfunction() + + if(NOT TARGET protobuf::libprotobuf OR NOT TARGET protobuf::protoc) + + set(SPIRV_TOOLS_PROTOBUF_DIR ${CMAKE_CURRENT_SOURCE_DIR}/protobuf/cmake) + if (NOT IS_DIRECTORY ${SPIRV_TOOLS_PROTOBUF_DIR}) + message( + FATAL_ERROR + "protobuf not found - please checkout a copy under external/.") + endif() + set(protobuf_BUILD_TESTS OFF CACHE BOOL "Disable protobuf tests") + set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Do not build protobuf static runtime") + + backup_compile_options() + + if (${CMAKE_CXX_COMPILER_ID} MATCHES Clang) + add_compile_options(-Wno-inconsistent-missing-override) + endif() + + add_subdirectory(${SPIRV_TOOLS_PROTOBUF_DIR} EXCLUDE_FROM_ALL) + + restore_compile_options() + + endif() +endif() diff --git a/third_party/spirv-tools/external/spirv-headers/.gitattributes b/third_party/spirv-tools/external/spirv-headers/.gitattributes new file mode 100644 index 0000000..3fa5aaf --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/.gitattributes @@ -0,0 +1,7 @@ +*.json text +*.h text +*.hpp text +*.hpp11 text +*.lua text +*.py text +*.xml diff --git a/third_party/spirv-tools/external/spirv-headers/.gitignore b/third_party/spirv-tools/external/spirv-headers/.gitignore new file mode 100644 index 0000000..f33592c --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/.gitignore @@ -0,0 +1,3 @@ +build +out +.DS_Store diff --git a/third_party/spirv-tools/external/spirv-headers/BUILD.bazel b/third_party/spirv-tools/external/spirv-headers/BUILD.bazel new file mode 100644 index 0000000..9cb46bf --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/BUILD.bazel @@ -0,0 +1,166 @@ +package( + default_visibility = ["//visibility:public"], +) + +licenses(["notice"]) + +exports_files(["LICENSE"]) + +filegroup( + name = "spirv_core_grammar_1.0", + srcs = ["include/spirv/1.0/spirv.core.grammar.json"], +) + +filegroup( + name = "spirv_glsl_grammar_1.0", + srcs = ["include/spirv/1.0/extinst.glsl.std.450.grammar.json"], +) + +filegroup( + name = "spirv_opencl_grammar_1.0", + srcs = ["include/spirv/1.0/extinst.opencl.std.100.grammar.json"], +) + +filegroup( + name = "spirv_core_grammar_1.1", + srcs = ["include/spirv/1.1/spirv.core.grammar.json"], +) + +filegroup( + name = "spirv_glsl_grammar_1.1", + srcs = ["include/spirv/1.1/extinst.glsl.std.450.grammar.json"], +) + +filegroup( + name = "spirv_opencl_grammar_1.1", + srcs = ["include/spirv/1.1/extinst.opencl.std.100.grammar.json"], +) + +filegroup( + name = "spirv_core_grammar_1.2", + srcs = ["include/spirv/1.2/spirv.core.grammar.json"], +) + +filegroup( + name = "spirv_glsl_grammar_1.2", + srcs = ["include/spirv/1.2/extinst.glsl.std.450.grammar.json"], +) + +filegroup( + name = "spirv_opencl_grammar_1.2", + srcs = ["include/spirv/1.2/extinst.opencl.std.100.grammar.json"], +) + +filegroup( + name = "spirv_core_grammar_unified1", + srcs = ["include/spirv/unified1/spirv.core.grammar.json"], +) + +filegroup( + name = "spirv_glsl_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.glsl.std.450.grammar.json"], +) + +filegroup( + name = "spirv_opencl_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.opencl.std.100.grammar.json"], +) + +filegroup( + name = "spirv_xml_registry", + srcs = ["include/spirv/spir-v.xml"], +) + +filegroup( + name = "spirv_ext_inst_debuginfo_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.debuginfo.grammar.json"], +) + +filegroup( + name = "spirv_ext_inst_nonsemantic_clspvreflection_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json"], +) + +filegroup( + name = "spirv_ext_inst_nonsemantic_debugprintf_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.nonsemantic.debugprintf.grammar.json"], +) + +filegroup( + name = "spirv_ext_inst_opencl_debuginfo_100_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json"], +) + +filegroup( + name = "spirv_ext_inst_spv_amd_gcn_shader_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json"], +) + +filegroup( + name = "spirv_ext_inst_spv_amd_shader_ballot_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.spv-amd-shader-ballot.grammar.json"], +) + +filegroup( + name = "spirv_ext_inst_spv_amd_shader_explicit_vertex_parameter_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json"], +) + +filegroup( + name = "spirv_ext_inst_spv_amd_shader_trinary_minmax_grammar_unified1", + srcs = ["include/spirv/unified1/extinst.spv-amd-shader-trinary-minmax.grammar.json"], +) + +cc_library( + name = "spirv_common_headers", + hdrs = [ + "include/spirv/1.0/GLSL.std.450.h", + "include/spirv/1.0/OpenCL.std.h", + "include/spirv/1.1/GLSL.std.450.h", + "include/spirv/1.1/OpenCL.std.h", + "include/spirv/1.2/GLSL.std.450.h", + "include/spirv/1.2/OpenCL.std.h", + "include/spirv/unified1/GLSL.std.450.h", + "include/spirv/unified1/NonSemanticClspvReflection.h", + "include/spirv/unified1/NonSemanticDebugPrintf.h", + "include/spirv/unified1/OpenCL.std.h", + ], + includes = ["include"], +) + +cc_library( + name = "spirv_c_headers", + hdrs = [ + "include/spirv/1.0/spirv.h", + "include/spirv/1.1/spirv.h", + "include/spirv/1.2/spirv.h", + "include/spirv/unified1/spirv.h", + ], + includes = ["include"], + deps = [":spirv_common_headers"], +) + +cc_library( + name = "spirv_cpp_headers", + hdrs = [ + "include/spirv/1.0/spirv.hpp", + "include/spirv/1.1/spirv.hpp", + "include/spirv/1.2/spirv.hpp", + "include/spirv/unified1/spirv.hpp", + ], + includes = ["include"], + deps = [":spirv_common_headers"], +) + +cc_library( + name = "spirv_cpp11_headers", + hdrs = [ + "include/spirv/1.0/spirv.hpp11", + "include/spirv/1.1/spirv.hpp11", + "include/spirv/1.2/spirv.hpp11", + "include/spirv/unified1/spirv.hpp11", + ], + includes = ["include"], + deps = [":spirv_common_headers"], +) + diff --git a/third_party/spirv-tools/external/spirv-headers/BUILD.gn b/third_party/spirv-tools/external/spirv-headers/BUILD.gn new file mode 100644 index 0000000..be3f43b --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/BUILD.gn @@ -0,0 +1,44 @@ +# Copyright (c) 2020 Google LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +config("spv_headers_public_config") { + include_dirs = [ "include" ] +} + +source_set("spv_headers") { + sources = [ + "include/spirv/1.2/GLSL.std.450.h", + "include/spirv/1.2/OpenCL.std.h", + "include/spirv/1.2/spirv.h", + "include/spirv/1.2/spirv.hpp", + "include/spirv/unified1/GLSL.std.450.h", + "include/spirv/unified1/NonSemanticClspvReflection.h", + "include/spirv/unified1/NonSemanticDebugPrintf.h", + "include/spirv/unified1/OpenCL.std.h", + "include/spirv/unified1/spirv.h", + "include/spirv/unified1/spirv.hpp", + ] + + public_configs = [ ":spv_headers_public_config" ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt b/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt new file mode 100644 index 0000000..d394053 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt @@ -0,0 +1,127 @@ +# Copyright (c) 2015-2016 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and/or associated documentation files (the +# "Materials"), to deal in the Materials without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Materials, and to +# permit persons to whom the Materials are furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +# KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +# SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +# https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +# +# The SPIR-V headers from the SPIR-V Registry +# https://www.khronos.org/registry/spir-v/ +# +cmake_minimum_required(VERSION 3.0) +project(SPIRV-Headers VERSION 1.5.1) + +# There are two ways to use this project. +# +# Using this source tree directly from a CMake-based project: +# 1. Add an add_subdirectory directive to include this sub directory. +# 2. Use ${SPIRV-Headers_SOURCE_DIR}/include} in a target_include_directories +# command. +# +# Installing the headers first, then using them with an implicit include +# directory. To install the headers: +# 1. mkdir build ; cd build +# 2. cmake .. +# 3. cmake --build . --target install + +# legacy +add_custom_target(install-headers + COMMAND cmake -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv + $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/include/spirv) + +# Filament specific changes +option(SPIRV_HEADERS_SKIP_EXAMPLES "Skip building examples" ON) + +option(SPIRV_HEADERS_SKIP_INSTALL "Skip install" ON) +# End Filament specific changes + +if(NOT ${SPIRV_HEADERS_SKIP_EXAMPLES}) + set(SPIRV_HEADERS_ENABLE_EXAMPLES ON) +endif() + +if(NOT ${SPIRV_HEADERS_SKIP_INSTALL}) + set(SPIRV_HEADERS_ENABLE_INSTALL ON) +endif() + +if (SPIRV_HEADERS_ENABLE_EXAMPLES) + message(STATUS "Building SPIRV-Header examples") + add_subdirectory(example) +endif() + +include(GNUInstallDirs) +add_library(${PROJECT_NAME} INTERFACE) +target_include_directories(${PROJECT_NAME} INTERFACE + $ +) + +# Installation + +if (SPIRV_HEADERS_ENABLE_INSTALL) + message(STATUS "Installing SPIRV-Header") + + set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") + + set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") + set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") + set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") + set(namespace "${PROJECT_NAME}::") + + include(CMakePackageConfigHelpers) + write_basic_package_version_file( + "${version_config}" + COMPATIBILITY SameMajorVersion + ) + + configure_package_config_file( + "cmake/Config.cmake.in" + "${project_config}" + INSTALL_DESTINATION "${config_install_dir}" + ) + + install( + TARGETS ${PROJECT_NAME} + EXPORT "${TARGETS_EXPORT_NAME}" + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) + + install( + DIRECTORY include/spirv + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) + + install( + FILES "${project_config}" "${version_config}" + DESTINATION "${config_install_dir}" + ) + + install( + EXPORT "${TARGETS_EXPORT_NAME}" + NAMESPACE "${namespace}" + DESTINATION "${config_install_dir}" + ) +endif() diff --git a/third_party/spirv-tools/external/spirv-headers/CODE_OF_CONDUCT.md b/third_party/spirv-tools/external/spirv-headers/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..a11610b --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/CODE_OF_CONDUCT.md @@ -0,0 +1 @@ +A reminder that this issue tracker is managed by the Khronos Group. Interactions here should follow the Khronos Code of Conduct (https://www.khronos.org/developers/code-of-conduct), which prohibits aggressive or derogatory language. Please keep the discussion friendly and civil. diff --git a/third_party/spirv-tools/external/spirv-headers/LICENSE b/third_party/spirv-tools/external/spirv-headers/LICENSE new file mode 100644 index 0000000..47974f8 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2015-2018 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + https://www.khronos.org/registry/ + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. diff --git a/third_party/spirv-tools/external/spirv-headers/README.md b/third_party/spirv-tools/external/spirv-headers/README.md new file mode 100644 index 0000000..b1aa964 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/README.md @@ -0,0 +1,215 @@ +# SPIR-V Headers + +This repository contains machine-readable files for the +[SPIR-V Registry](https://www.khronos.org/registry/spir-v/). +This includes: + +* Header files for various languages. +* JSON files describing the grammar for the SPIR-V core instruction set + and the extended instruction sets. +* The XML registry file. +* A tool to build the headers from the JSON grammar. + +Headers are provided in the [include](include) directory, with up-to-date +headers in the `unified1` subdirectory. Older headers are provided according to +their version. + +In contrast, the XML registry file has a linear history, so it is +not tied to SPIR-V specification versions. + +## How is this repository updated? + +When a new version or revision of the SPIR-V specification is published, +the SPIR-V Working Group will push new commits onto master, updating +the files under [include](include). + +[The SPIR-V XML registry file](include/spirv/spir-v.xml) +is updated by Khronos whenever a new enum range is allocated. + +Pull requests can be made to +- request allocation of new enum ranges in the XML registry file +- register a new magic number for a SPIR-V generator +- reserve specific tokens in the JSON grammar + +### Registering a SPIR-V Generator Magic Number + +Tools that generate SPIR-V should use a magic number in the SPIR-V to help identify the +generator. + +Care should be taken to follow existing precedent in populating the details of reserved tokens. +This includes: +- keeping generator numbers in numeric order +- filling out all the existing fields + +### Reserving tokens in the JSON grammar + +Care should be taken to follow existing precedent in populating the details of reserved tokens. +This includes: +- pointing to what extension has more information, when possible +- keeping enumerants in numeric order +- when there are aliases, listing the preferred spelling first +- adding the statement `"version" : "None"` + +## How to install the headers + +``` +mkdir build +cd build +cmake .. +cmake --build . --target install +``` + +Then, for example, you will have `/usr/local/include/spirv/unified1/spirv.h` + +If you want to install them somewhere else, then use +`-DCMAKE_INSTALL_PREFIX=/other/path` on the first `cmake` command. + +## Using the headers without installing + +### Using CMake +A CMake-based project can use the headers without installing, as follows: + +1. Add an `add_subdirectory` directive to include this source tree. +2. Use `${SPIRV-Headers_SOURCE_DIR}/include}` in a `target_include_directories` + directive. +3. In your C or C++ source code use `#include` directives that explicitly mention + the `spirv` path component. +``` +#include "spirv/unified1/GLSL.std.450.h" +#include "spirv/unified1/OpenCL.std.h" +#include "spirv/unified1/spirv.hpp" +``` + +See also the [example](example/) subdirectory. But since that example is +*inside* this repostory, it doesn't use and `add_subdirectory` directive. + +### Using Bazel +A Bazel-based project can use the headers without installing, as follows: + +1. Add SPIRV-Headers as a submodule of your project, and add a +`local_repository` to your `WORKSPACE` file. For example, if you place +SPIRV-Headers under `external/spirv-headers`, then add the following to your +`WORKSPACE` file: + +``` +local_repository( + name = "spirv_headers", + path = "external/spirv-headers", +) +``` + +2. Add one of the following to the `deps` attribute of your build target based +on your needs: +``` +@spirv_headers//:spirv_c_headers +@spirv_headers//:spirv_cpp_headers +@spirv_headers//:spirv_cpp11_headers +``` + +For example: + +``` +cc_library( + name = "project", + srcs = [ + # Path to project sources + ], + hdrs = [ + # Path to project headers + ], + deps = [ + "@spirv_tools//:spirv_c_headers", + # Other dependencies, + ], +) +``` + +3. In your C or C++ source code use `#include` directives that explicitly mention + the `spirv` path component. +``` +#include "spirv/unified1/GLSL.std.450.h" +#include "spirv/unified1/OpenCL.std.h" +#include "spirv/unified1/spirv.hpp" +``` + +## Generating headers from the JSON grammar for the SPIR-V core instruction set + +This will generally be done by Khronos, for a change to the JSON grammar. +However, the project for the tool to do this is included in this repository, +and can be used to test a PR, or even to include the results in the PR. +This is not required though. + +The header-generation project is under the `tools/buildHeaders` directory. +Use CMake to build the project, in a `build` subdirectory (under `tools/buildHeaders`). +There is then a bash script at `bin/makeHeaders` that shows how to use the built +header-generator binary to generate the headers from the JSON grammar. +(Execute `bin/makeHeaders` from the `tools/buildHeaders` directory.) + +Notes: +- this generator is used in a broader context within Khronos to generate the specification, + and that influences the languages used, for legacy reasons +- the C++ structures built may similarly include more than strictly necessary, for the same reason + +## Generating C headers for extended instruction sets + +The [GLSL.std.450.h](include/spirv/unified1/GLSL.std.450.h) +and [OpenCL.std.h](include/spirv/unified1/OpenCL.std.h) extended instruction set headers +are maintained manually. + +The C/C++ header for each of the other extended instruction sets +is generated from the corresponding JSON grammar file. For example, the +[OpenCLDebugInfo100.h](include/spirv/unified1/OpenCLDebugInfo100.h) header +is generated from the +[extinst.opencl.debuginfo.100.grammar.json](include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json) +grammar file. + +To generate these C/C++ headers, first make sure `python3` is in your PATH, then +invoke the build script as follows: +``` +cd tools/buildHeaders +python3 bin/makeExtinstHeaders.py +``` + +## FAQ + +* *How are different versions published?* + + The multiple versions of the headers have been simplified into a + single `unified1` view. The JSON grammar has a "version" field saying + what version things first showed up in. + +* *How do you handle the evolution of extended instruction sets?* + + Extended instruction sets evolve asynchronously from the core spec. + Right now there is only a single version of both the GLSL and OpenCL + headers. So we don't yet have a problematic example to resolve. + +## License + +``` +Copyright (c) 2015-2018 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + https://www.khronos.org/registry/ + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +``` diff --git a/third_party/spirv-tools/external/spirv-headers/WORKSPACE b/third_party/spirv-tools/external/spirv-headers/WORKSPACE new file mode 100644 index 0000000..e69de29 diff --git a/third_party/spirv-tools/external/spirv-headers/cmake/Config.cmake.in b/third_party/spirv-tools/external/spirv-headers/cmake/Config.cmake.in new file mode 100644 index 0000000..38bbde7 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/cmake/Config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/third_party/spirv-tools/external/spirv-headers/example/CMakeLists.txt b/third_party/spirv-tools/external/spirv-headers/example/CMakeLists.txt new file mode 100644 index 0000000..8b22f60 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/example/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(SPIRV-Headers-example + ${CMAKE_CURRENT_SOURCE_DIR}/example.cpp) +target_include_directories(SPIRV-Headers-example + PRIVATE ${SPIRV-Headers_SOURCE_DIR}/include) diff --git a/third_party/spirv-tools/external/spirv-headers/example/example.cpp b/third_party/spirv-tools/external/spirv-headers/example/example.cpp new file mode 100644 index 0000000..d79b62f --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/example/example.cpp @@ -0,0 +1,37 @@ +// Copyright (c) 2016 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +#include +#include +#include + +namespace { + +const GLSLstd450 kSin = GLSLstd450Sin; +const OpenCLLIB::Entrypoints kNative_cos = OpenCLLIB::Native_cos; +const spv::Op kNop = spv::OpNop; + +} // anonymous namespace diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/GLSL.std.450.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/GLSL.std.450.h new file mode 100644 index 0000000..54cc00e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/OpenCL.std.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/OpenCL.std.h new file mode 100644 index 0000000..19a6688 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/OpenCL.std.h @@ -0,0 +1,210 @@ +/* +** Copyright (c) 2015-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +namespace OpenCLLIB { + +enum Entrypoints { + + // Section 2.1: Math extended instructions + Acos = 0, + Acosh = 1, + Acospi = 2, + Asin = 3, + Asinh = 4, + Asinpi = 5, + Atan = 6, + Atan2 = 7, + Atanh = 8, + Atanpi = 9, + Atan2pi = 10, + Cbrt = 11, + Ceil = 12, + Copysign = 13, + Cos = 14, + Cosh = 15, + Cospi = 16, + Erfc = 17, + Erf = 18, + Exp = 19, + Exp2 = 20, + Exp10 = 21, + Expm1 = 22, + Fabs = 23, + Fdim = 24, + Floor = 25, + Fma = 26, + Fmax = 27, + Fmin = 28, + Fmod = 29, + Fract = 30, + Frexp = 31, + Hypot = 32, + Ilogb = 33, + Ldexp = 34, + Lgamma = 35, + Lgamma_r = 36, + Log = 37, + Log2 = 38, + Log10 = 39, + Log1p = 40, + Logb = 41, + Mad = 42, + Maxmag = 43, + Minmag = 44, + Modf = 45, + Nan = 46, + Nextafter = 47, + Pow = 48, + Pown = 49, + Powr = 50, + Remainder = 51, + Remquo = 52, + Rint = 53, + Rootn = 54, + Round = 55, + Rsqrt = 56, + Sin = 57, + Sincos = 58, + Sinh = 59, + Sinpi = 60, + Sqrt = 61, + Tan = 62, + Tanh = 63, + Tanpi = 64, + Tgamma = 65, + Trunc = 66, + Half_cos = 67, + Half_divide = 68, + Half_exp = 69, + Half_exp2 = 70, + Half_exp10 = 71, + Half_log = 72, + Half_log2 = 73, + Half_log10 = 74, + Half_powr = 75, + Half_recip = 76, + Half_rsqrt = 77, + Half_sin = 78, + Half_sqrt = 79, + Half_tan = 80, + Native_cos = 81, + Native_divide = 82, + Native_exp = 83, + Native_exp2 = 84, + Native_exp10 = 85, + Native_log = 86, + Native_log2 = 87, + Native_log10 = 88, + Native_powr = 89, + Native_recip = 90, + Native_rsqrt = 91, + Native_sin = 92, + Native_sqrt = 93, + Native_tan = 94, + + // Section 2.2: Integer instructions + SAbs = 141, + SAbs_diff = 142, + SAdd_sat = 143, + UAdd_sat = 144, + SHadd = 145, + UHadd = 146, + SRhadd = 147, + URhadd = 148, + SClamp = 149, + UClamp = 150, + Clz = 151, + Ctz = 152, + SMad_hi = 153, + UMad_sat = 154, + SMad_sat = 155, + SMax = 156, + UMax = 157, + SMin = 158, + UMin = 159, + SMul_hi = 160, + Rotate = 161, + SSub_sat = 162, + USub_sat = 163, + U_Upsample = 164, + S_Upsample = 165, + Popcount = 166, + SMad24 = 167, + UMad24 = 168, + SMul24 = 169, + UMul24 = 170, + UAbs = 201, + UAbs_diff = 202, + UMul_hi = 203, + UMad_hi = 204, + + // Section 2.3: Common instructions + FClamp = 95, + Degrees = 96, + FMax_common = 97, + FMin_common = 98, + Mix = 99, + Radians = 100, + Step = 101, + Smoothstep = 102, + Sign = 103, + + // Section 2.4: Geometric instructions + Cross = 104, + Distance = 105, + Length = 106, + Normalize = 107, + Fast_distance = 108, + Fast_length = 109, + Fast_normalize = 110, + + // Section 2.5: Relational instructions + Bitselect = 186, + Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + Vloadn = 171, + Vstoren = 172, + Vload_half = 173, + Vload_halfn = 174, + Vstore_half = 175, + Vstore_half_r = 176, + Vstore_halfn = 177, + Vstore_halfn_r = 178, + Vloada_halfn = 179, + Vstorea_halfn = 180, + Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + Shuffle = 182, + Shuffle2 = 183, + + // Section 2.8: Misc instructions + Printf = 184, + Prefetch = 185, +}; + +} // end namespace OpenCLLIB diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.glsl.std.450.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.glsl.std.450.grammar.json new file mode 100644 index 0000000..3d9f39e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.glsl.std.450.grammar.json @@ -0,0 +1,642 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "Round", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "RoundEven", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Trunc", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FAbs", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SAbs", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FSign", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SSign", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Floor", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ceil", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fract", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Radians", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "Degrees", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "Sin", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tan", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asin", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acos", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'y_over_x'" } + ] + }, + { + "opname" : "Sinh", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cosh", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tanh", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asinh", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acosh", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atanh", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan2", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Pow", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Exp", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Exp2", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log2", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Sqrt", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "InverseSqrt", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Determinant", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "MatrixInverse", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Modf", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "ModfStruct", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FMin", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMin", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMin", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FMax", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMax", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMax", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FClamp", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "UClamp", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "SClamp", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "FMix", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "IMix", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "Step", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SmoothStep", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fma", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "Frexp", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "FrexpStruct", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ldexp", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "PackSnorm4x8", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm4x8", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackSnorm2x16", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm2x16", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackHalf2x16", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackDouble2x32", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "UnpackSnorm2x16", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm2x16", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackHalf2x16", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "UnpackSnorm4x8", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm4x8", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackDouble2x32", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "Length", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Distance", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "Cross", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Normalize", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FaceForward", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'Nref'" } + ] + }, + { + "opname" : "Reflect", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" } + ] + }, + { + "opname" : "Refract", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'eta'" } + ] + }, + { + "opname" : "FindILsb", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindSMsb", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindUMsb", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "InterpolateAtCentroid", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtSample", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'sample'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtOffset", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "NMin", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NMax", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NClamp", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.opencl.std.100.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.opencl.std.100.grammar.json new file mode 100644 index 0000000..4fe4506 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/extinst.opencl.std.100.grammar.json @@ -0,0 +1,1279 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "acos", + "opcode" : 0, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acosh", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acospi", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asin", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinh", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinpi", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanh", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanpi", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2pi", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cbrt", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ceil", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "copysign", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cosh", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cospi", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erfc", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erf", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp2", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp10", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "expm1", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fabs", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fdim", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "floor", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fma", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fmax", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmod", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fract", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'ptr'" } + ] + }, + { + "opname" : "frexp", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "hypot", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "ilogb", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ldexp", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'k'" } + ] + }, + { + "opname" : "lgamma", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "lgamma_r", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'signp'" } + ] + }, + { + "opname" : "log", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log2", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log10", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log1p", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "logb", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "mad", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "maxmag", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "minmag", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "modf", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'iptr'" } + ] + }, + { + "opname" : "nan", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'nancode'" } + ] + }, + { + "opname" : "nextafter", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "pow", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y" } + ] + }, + { + "opname" : "pown", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "powr", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remainder", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remquo", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'quo'" } + ] + }, + { + "opname" : "rint", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rootn", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "round", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rsqrt", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sin", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sincos", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'cosval'" } + ] + }, + { + "opname" : "sinh", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sinpi", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sqrt", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tan", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanh", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanpi", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tgamma", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "trunc", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_cos", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_divide", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_exp", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp2", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp10", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log2", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log10", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_powr", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_recip", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_rsqrt", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sin", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sqrt", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_tan", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_cos", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_divide", + "opcode" : 82, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_exp", + "opcode" : 83, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp2", + "opcode" : 84, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp10", + "opcode" : 85, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log", + "opcode" : 86, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log2", + "opcode" : 87, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log10", + "opcode" : 88, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_powr", + "opcode" : 89, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_recip", + "opcode" : 90, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_rsqrt", + "opcode" : 91, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sin", + "opcode" : 92, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sqrt", + "opcode" : 93, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_tan", + "opcode" : 94, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs", + "opcode" : 141, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs_diff", + "opcode" : 142, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_add_sat", + "opcode" : 143, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_add_sat", + "opcode" : 144, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_hadd", + "opcode" : 145, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_hadd", + "opcode" : 146, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_rhadd", + "opcode" : 147, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_rhadd", + "opcode" : 148, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_clamp", + "opcode" : 149, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "u_clamp", + "opcode" : 150, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "clz", + "opcode" : 151, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ctz", + "opcode" : 152, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad_hi", + "opcode" : 153, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "u_mad_sat", + "opcode" : 154, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mad_sat", + "opcode" : 155, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_max", + "opcode" : 156, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_max", + "opcode" : 157, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_min", + "opcode" : 158, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_min", + "opcode" : 159, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_mul_hi", + "opcode" : 160, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "rotate", + "opcode" : 161, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "s_sub_sat", + "opcode" : 162, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_sub_sat", + "opcode" : 163, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_upsample", + "opcode" : 164, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "s_upsample", + "opcode" : 165, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "popcount", + "opcode" : 166, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad24", + "opcode" : 167, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "u_mad24", + "opcode" : 168, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mul24", + "opcode" : 169, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul24", + "opcode" : 170, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_abs", + "opcode" : 201, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "u_abs_diff", + "opcode" : 202, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul_hi", + "opcode" : 203, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mad_hi", + "opcode" : 204, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fclamp", + "opcode" : 95, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "degrees", + "opcode" :96, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "fmax_common", + "opcode" : 97, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin_common", + "opcode" : 98, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "mix", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "radians", + "opcode" : 100, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "step", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "smoothstep", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sign", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cross", + "opcode" : 104, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "distance", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "length", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "normalize", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_distance", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "fast_length", + "opcode" : 109, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_normalize", + "opcode" : 110, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "bitselect", + "opcode" : 186, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "select", + "opcode" : 187, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "vloadn", + "opcode" : 171, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstoren", + "opcode" : 172, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_half", + "opcode" : 173, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_halfn", + "opcode" : 174, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstore_half", + "opcode" : 175, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_half_r", + "opcode" : 176, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vstore_halfn", + "opcode" : 177, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_halfn_r", + "opcode" : 178, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vloada_halfn", + "opcode" : 179, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstorea_halfn", + "opcode" : 180, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstorea_halfn_r", + "opcode" : 181, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "shuffle", + "opcode" : 182, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "shuffle2", + "opcode" : 183, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "printf", + "opcode" : 184, + "operands" : [ + { "kind" : "IdRef", "name" : "'format'" }, + { "kind" : "IdRef", "name" : "'additional arguments'", "quantifier" : "*" } + ] + }, + { + "opname" : "prefetch", + "opcode" : 185, + "operands" : [ + { "kind" : "IdRef", "name" : "'ptr'" }, + { "kind" : "IdRef", "name" : "'num elements'" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.core.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.core.grammar.json new file mode 100644 index 0000000..f3cfc4c --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.core.grammar.json @@ -0,0 +1,5775 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "magic_number" : "0x07230203", + "major_version" : 1, + "minor_version" : 0, + "revision" : 12, + "instructions" : [ + { + "opname" : "OpNop", + "opcode" : 0 + }, + { + "opname" : "OpUndef", + "opcode" : 1, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSourceContinued", + "opcode" : 2, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Continued Source'" } + ] + }, + { + "opname" : "OpSource", + "opcode" : 3, + "operands" : [ + { "kind" : "SourceLanguage" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'File'" }, + { "kind" : "LiteralString", "quantifier" : "?", "name" : "'Source'" } + ] + }, + { + "opname" : "OpSourceExtension", + "opcode" : 4, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Extension'" } + ] + }, + { + "opname" : "OpName", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpMemberName", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpString", + "opcode" : 7, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'String'" } + ] + }, + { + "opname" : "OpLine", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "OpExtension", + "opcode" : 10, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInstImport", + "opcode" : 11, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInst", + "opcode" : 12, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ] + }, + { + "opname" : "OpMemoryModel", + "opcode" : 14, + "operands" : [ + { "kind" : "AddressingModel" }, + { "kind" : "MemoryModel" } + ] + }, + { + "opname" : "OpEntryPoint", + "opcode" : 15, + "operands" : [ + { "kind" : "ExecutionModel" }, + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Interface'" } + ] + }, + { + "opname" : "OpExecutionMode", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpCapability", + "opcode" : 17, + "operands" : [ + { "kind" : "Capability", "name" : "'Capability'" } + ] + }, + { + "opname" : "OpTypeVoid", + "opcode" : 19, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeBool", + "opcode" : 20, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeInt", + "opcode" : 21, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "LiteralInteger", "name" : "'Signedness'" } + ] + }, + { + "opname" : "OpTypeFloat", + "opcode" : 22, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" } + ] + }, + { + "opname" : "OpTypeVector", + "opcode" : 23, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "OpTypeMatrix", + "opcode" : 24, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Column Type'" }, + { "kind" : "LiteralInteger", "name" : "'Column Count'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpTypeImage", + "opcode" : 25, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Type'" }, + { "kind" : "Dim" }, + { "kind" : "LiteralInteger", "name" : "'Depth'" }, + { "kind" : "LiteralInteger", "name" : "'Arrayed'" }, + { "kind" : "LiteralInteger", "name" : "'MS'" }, + { "kind" : "LiteralInteger", "name" : "'Sampled'" }, + { "kind" : "ImageFormat" }, + { "kind" : "AccessQualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "OpTypeSampler", + "opcode" : 26, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeSampledImage", + "opcode" : 27, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ] + }, + { + "opname" : "OpTypeArray", + "opcode" : 28, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" }, + { "kind" : "IdRef", "name" : "'Length'" } + ] + }, + { + "opname" : "OpTypeRuntimeArray", + "opcode" : 29, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpTypeStruct", + "opcode" : 30, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ] + }, + { + "opname" : "OpTypeOpaque", + "opcode" : 31, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "The name of the opaque type." } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypePointer", + "opcode" : 32, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "name" : "'Type'" } + ] + }, + { + "opname" : "OpTypeFunction", + "opcode" : 33, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..." } + ] + }, + { + "opname" : "OpTypeEvent", + "opcode" : 34, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypeDeviceEvent", + "opcode" : 35, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypeReserveId", + "opcode" : 36, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeQueue", + "opcode" : 37, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypePipe", + "opcode" : 38, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "AccessQualifier", "name" : "'Qualifier'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeForwardPointer", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "StorageClass" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpConstantTrue", + "opcode" : 41, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstantFalse", + "opcode" : 42, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstant", + "opcode" : 43, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConstantComposite", + "opcode" : 44, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpConstantSampler", + "opcode" : 45, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "SamplerAddressingMode" }, + { "kind" : "LiteralInteger", "name" : "'Param'" }, + { "kind" : "SamplerFilterMode" } + ], + "capabilities" : [ "LiteralSampler" ] + }, + { + "opname" : "OpConstantNull", + "opcode" : 46, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantTrue", + "opcode" : 48, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantFalse", + "opcode" : 49, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstant", + "opcode" : 50, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpSpecConstantComposite", + "opcode" : 51, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpSpecConstantOp", + "opcode" : 52, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralSpecConstantOpInteger", "name" : "'Opcode'" } + ] + }, + { + "opname" : "OpFunction", + "opcode" : 54, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "FunctionControl" }, + { "kind" : "IdRef", "name" : "'Function Type'" } + ] + }, + { + "opname" : "OpFunctionParameter", + "opcode" : 55, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpFunctionEnd", + "opcode" : 56 + }, + { + "opname" : "OpFunctionCall", + "opcode" : 57, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0', +\n'Argument 1', +\n..." } + ] + }, + { + "opname" : "OpVariable", + "opcode" : 59, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ] + }, + { + "opname" : "OpImageTexelPointer", + "opcode" : 60, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Sample'" } + ] + }, + { + "opname" : "OpLoad", + "opcode" : 61, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpStore", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemory", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemorySized", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpAccessChain", + "opcode" : 65, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpInBoundsAccessChain", + "opcode" : 66, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpPtrAccessChain", + "opcode" : 67, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ + "Addresses", + "VariablePointers", + "VariablePointersStorageBuffer" + ] + }, + { + "opname" : "OpArrayLength", + "opcode" : 68, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpGenericPtrMemSemantics", + "opcode" : 69, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpInBoundsPtrAccessChain", + "opcode" : 70, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpDecorate", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpMemberDecorate", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'Structure Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpDecorationGroup", + "opcode" : 73, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpGroupDecorate", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpGroupMemberDecorate", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "PairIdRefLiteralInteger", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpVectorExtractDynamic", + "opcode" : 77, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorInsertDynamic", + "opcode" : 78, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorShuffle", + "opcode" : 79, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Components'" } + ] + }, + { + "opname" : "OpCompositeConstruct", + "opcode" : 80, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpCompositeExtract", + "opcode" : 81, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCompositeInsert", + "opcode" : 82, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCopyObject", + "opcode" : 83, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpTranspose", + "opcode" : 84, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpSampledImage", + "opcode" : 86, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ] + }, + { + "opname" : "OpImageSampleImplicitLod", + "opcode" : 87, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleExplicitLod", + "opcode" : 88, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ] + }, + { + "opname" : "OpImageSampleDrefImplicitLod", + "opcode" : 89, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleDrefExplicitLod", + "opcode" : 90, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjImplicitLod", + "opcode" : 91, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjExplicitLod", + "opcode" : 92, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefImplicitLod", + "opcode" : 93, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefExplicitLod", + "opcode" : 94, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageFetch", + "opcode" : 95, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageGather", + "opcode" : 96, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageDrefGather", + "opcode" : 97, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageRead", + "opcode" : 98, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageWrite", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Texel'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImage", + "opcode" : 100, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" } + ] + }, + { + "opname" : "OpImageQueryFormat", + "opcode" : 101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQueryOrder", + "opcode" : 102, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQuerySizeLod", + "opcode" : 103, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Level of Detail'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySize", + "opcode" : 104, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLod", + "opcode" : 105, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLevels", + "opcode" : 106, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySamples", + "opcode" : 107, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpConvertFToU", + "opcode" : 109, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertFToS", + "opcode" : 110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertSToF", + "opcode" : 111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpConvertUToF", + "opcode" : 112, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpUConvert", + "opcode" : 113, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpSConvert", + "opcode" : 114, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpFConvert", + "opcode" : 115, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpQuantizeToF16", + "opcode" : 116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConvertPtrToU", + "opcode" : 117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpSatConvertSToU", + "opcode" : 118, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSatConvertUToS", + "opcode" : 119, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpConvertUToPtr", + "opcode" : 120, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Integer Value'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpPtrCastToGeneric", + "opcode" : 121, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtr", + "opcode" : 122, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtrExplicit", + "opcode" : 123, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "StorageClass", "name" : "'Storage'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpBitcast", + "opcode" : 124, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSNegate", + "opcode" : 126, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpFNegate", + "opcode" : 127, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpIAdd", + "opcode" : 128, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFAdd", + "opcode" : 129, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISub", + "opcode" : 130, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFSub", + "opcode" : 131, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpIMul", + "opcode" : 132, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMul", + "opcode" : 133, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUDiv", + "opcode" : 134, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSDiv", + "opcode" : 135, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFDiv", + "opcode" : 136, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMod", + "opcode" : 137, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSRem", + "opcode" : 138, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMod", + "opcode" : 139, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFRem", + "opcode" : 140, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMod", + "opcode" : 141, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpVectorTimesScalar", + "opcode" : 142, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ] + }, + { + "opname" : "OpMatrixTimesScalar", + "opcode" : 143, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpVectorTimesMatrix", + "opcode" : 144, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesVector", + "opcode" : 145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesMatrix", + "opcode" : 146, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'LeftMatrix'" }, + { "kind" : "IdRef", "name" : "'RightMatrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpOuterProduct", + "opcode" : 147, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpDot", + "opcode" : 148, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ] + }, + { + "opname" : "OpIAddCarry", + "opcode" : 149, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISubBorrow", + "opcode" : 150, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMulExtended", + "opcode" : 151, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMulExtended", + "opcode" : 152, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpAny", + "opcode" : 154, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpAll", + "opcode" : 155, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpIsNan", + "opcode" : 156, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsInf", + "opcode" : 157, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsFinite", + "opcode" : 158, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpIsNormal", + "opcode" : 159, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSignBitSet", + "opcode" : 160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLessOrGreater", + "opcode" : 161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpOrdered", + "opcode" : 162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpUnordered", + "opcode" : 163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLogicalEqual", + "opcode" : 164, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNotEqual", + "opcode" : 165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalOr", + "opcode" : 166, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalAnd", + "opcode" : 167, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNot", + "opcode" : 168, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSelect", + "opcode" : 169, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'Object 1'" }, + { "kind" : "IdRef", "name" : "'Object 2'" } + ] + }, + { + "opname" : "OpIEqual", + "opcode" : 170, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpINotEqual", + "opcode" : 171, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThan", + "opcode" : 172, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThan", + "opcode" : 173, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThanEqual", + "opcode" : 174, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThanEqual", + "opcode" : 175, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThan", + "opcode" : 176, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThan", + "opcode" : 177, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThanEqual", + "opcode" : 178, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThanEqual", + "opcode" : 179, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdEqual", + "opcode" : 180, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordEqual", + "opcode" : 181, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdNotEqual", + "opcode" : 182, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordNotEqual", + "opcode" : 183, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThan", + "opcode" : 184, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThan", + "opcode" : 185, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThan", + "opcode" : 186, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThan", + "opcode" : 187, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThanEqual", + "opcode" : 188, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThanEqual", + "opcode" : 189, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThanEqual", + "opcode" : 190, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThanEqual", + "opcode" : 191, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpShiftRightLogical", + "opcode" : 194, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftRightArithmetic", + "opcode" : 195, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftLeftLogical", + "opcode" : 196, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpBitwiseOr", + "opcode" : 197, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseXor", + "opcode" : 198, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseAnd", + "opcode" : 199, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpNot", + "opcode" : 200, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpBitFieldInsert", + "opcode" : 201, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Insert'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldSExtract", + "opcode" : 202, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldUExtract", + "opcode" : 203, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitReverse", + "opcode" : 204, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitCount", + "opcode" : 205, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ] + }, + { + "opname" : "OpDPdx", + "opcode" : 207, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdy", + "opcode" : 208, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpFwidth", + "opcode" : 209, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdxFine", + "opcode" : 210, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyFine", + "opcode" : 211, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthFine", + "opcode" : 212, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdxCoarse", + "opcode" : 213, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyCoarse", + "opcode" : 214, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthCoarse", + "opcode" : 215, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpEmitVertex", + "opcode" : 218, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEndPrimitive", + "opcode" : 219, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEmitStreamVertex", + "opcode" : 220, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpEndStreamPrimitive", + "opcode" : 221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpControlBarrier", + "opcode" : 224, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpMemoryBarrier", + "opcode" : 225, + "operands" : [ + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicLoad", + "opcode" : 227, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicStore", + "opcode" : 228, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicExchange", + "opcode" : 229, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicCompareExchange", + "opcode" : 230, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ] + }, + { + "opname" : "OpAtomicCompareExchangeWeak", + "opcode" : 231, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicIIncrement", + "opcode" : 232, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIDecrement", + "opcode" : 233, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIAdd", + "opcode" : 234, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicISub", + "opcode" : 235, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMin", + "opcode" : 236, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMin", + "opcode" : 237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMax", + "opcode" : 238, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMax", + "opcode" : 239, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicAnd", + "opcode" : 240, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicOr", + "opcode" : 241, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicXor", + "opcode" : 242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpPhi", + "opcode" : 245, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "PairIdRefIdRef", "quantifier" : "*", "name" : "'Variable, Parent, ...'" } + ] + }, + { + "opname" : "OpLoopMerge", + "opcode" : 246, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "IdRef", "name" : "'Continue Target'" }, + { "kind" : "LoopControl" } + ] + }, + { + "opname" : "OpSelectionMerge", + "opcode" : 247, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "SelectionControl" } + ] + }, + { + "opname" : "OpLabel", + "opcode" : 248, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpBranch", + "opcode" : 249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target Label'" } + ] + }, + { + "opname" : "OpBranchConditional", + "opcode" : 250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'True Label'" }, + { "kind" : "IdRef", "name" : "'False Label'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Branch weights'" } + ] + }, + { + "opname" : "OpSwitch", + "opcode" : 251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Selector'" }, + { "kind" : "IdRef", "name" : "'Default'" }, + { "kind" : "PairLiteralIntegerIdRef", "quantifier" : "*", "name" : "'Target'" } + ] + }, + { + "opname" : "OpKill", + "opcode" : 252, + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpReturn", + "opcode" : 253 + }, + { + "opname" : "OpReturnValue", + "opcode" : 254, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpUnreachable", + "opcode" : 255 + }, + { + "opname" : "OpLifetimeStart", + "opcode" : 256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLifetimeStop", + "opcode" : 257, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAsyncCopy", + "opcode" : 259, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Destination'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Num Elements'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupWaitEvents", + "opcode" : 260, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Events List'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAll", + "opcode" : 261, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupAny", + "opcode" : 262, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupBroadcast", + "opcode" : 263, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'LocalId'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupIAdd", + "opcode" : 264, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAdd", + "opcode" : 265, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMin", + "opcode" : 266, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMin", + "opcode" : 267, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMin", + "opcode" : 268, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMax", + "opcode" : 269, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMax", + "opcode" : 270, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMax", + "opcode" : 271, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpReadPipe", + "opcode" : 274, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpWritePipe", + "opcode" : 275, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedReadPipe", + "opcode" : 276, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedWritePipe", + "opcode" : 277, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveReadPipePackets", + "opcode" : 278, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveWritePipePackets", + "opcode" : 279, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitReadPipe", + "opcode" : 280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitWritePipe", + "opcode" : 281, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpIsValidReserveId", + "opcode" : 282, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetNumPipePackets", + "opcode" : 283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetMaxPipePackets", + "opcode" : 284, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveReadPipePackets", + "opcode" : 285, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveWritePipePackets", + "opcode" : 286, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitReadPipe", + "opcode" : 287, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitWritePipe", + "opcode" : 288, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpEnqueueMarker", + "opcode" : 291, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpEnqueueKernel", + "opcode" : 292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Local Size'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeSubGroupCount", + "opcode" : 293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeMaxSubGroupSize", + "opcode" : 294, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelWorkGroupSize", + "opcode" : 295, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelPreferredWorkGroupSizeMultiple", + "opcode" : 296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpRetainEvent", + "opcode" : 297, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpReleaseEvent", + "opcode" : 298, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCreateUserEvent", + "opcode" : 299, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpIsValidEvent", + "opcode" : 300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpSetUserEventStatus", + "opcode" : 301, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Status'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCaptureEventProfilingInfo", + "opcode" : 302, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Profiling Info'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetDefaultQueue", + "opcode" : 303, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpBuildNDRange", + "opcode" : 304, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'GlobalWorkSize'" }, + { "kind" : "IdRef", "name" : "'LocalWorkSize'" }, + { "kind" : "IdRef", "name" : "'GlobalWorkOffset'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpImageSparseSampleImplicitLod", + "opcode" : 305, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleExplicitLod", + "opcode" : 306, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefImplicitLod", + "opcode" : 307, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefExplicitLod", + "opcode" : 308, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjImplicitLod", + "opcode" : 309, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjExplicitLod", + "opcode" : 310, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefImplicitLod", + "opcode" : 311, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefExplicitLod", + "opcode" : 312, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseFetch", + "opcode" : 313, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseGather", + "opcode" : 314, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseDrefGather", + "opcode" : 315, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseTexelsResident", + "opcode" : 316, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Resident Code'" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpNoLine", + "opcode" : 317 + }, + { + "opname" : "OpAtomicFlagTestAndSet", + "opcode" : 318, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicFlagClear", + "opcode" : 319, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageSparseRead", + "opcode" : 320, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpDecorateId", + "opcode" : 332, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "opname" : "OpSubgroupBallotKHR", + "opcode" : 4421, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupFirstInvocationKHR", + "opcode" : 4422, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupAllKHR", + "opcode" : 4428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAnyKHR", + "opcode" : 4429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAllEqualKHR", + "opcode" : 4430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupReadInvocationKHR", + "opcode" : 4432, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpGroupIAddNonUniformAMD", + "opcode" : 5000, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAddNonUniformAMD", + "opcode" : 5001, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMinNonUniformAMD", + "opcode" : 5002, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMinNonUniformAMD", + "opcode" : 5003, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMinNonUniformAMD", + "opcode" : 5004, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMaxNonUniformAMD", + "opcode" : 5005, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMaxNonUniformAMD", + "opcode" : 5006, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMaxNonUniformAMD", + "opcode" : 5007, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpFragmentMaskFetchAMD", + "opcode" : 5011, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpFragmentFetchAMD", + "opcode" : 5012, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Fragment Index'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpSubgroupShuffleINTEL", + "opcode" : 5571, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'InvocationId'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleDownINTEL", + "opcode" : 5572, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Next'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleUpINTEL", + "opcode" : 5573, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Previous'" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleXorINTEL", + "opcode" : 5574, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupBlockReadINTEL", + "opcode" : 5575, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupBlockWriteINTEL", + "opcode" : 5576, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockReadINTEL", + "opcode" : 5577, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockWriteINTEL", + "opcode" : 5578, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpDecorateStringGOOGLE", + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + }, + { + "opname" : "OpMemberDecorateStringGOOGLE", + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "ImageOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Bias", + "value" : "0x0001", + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Lod", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Grad", + "value" : "0x0004", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffset", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Offset", + "value" : "0x0010", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffsets", + "value" : "0x0020", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Sample", + "value" : "0x0040", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "MinLod", + "value" : "0x0080", + "capabilities" : [ "MinLod" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FPFastMathMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "NotNaN", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NotInf", + "value" : "0x0002", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NSZ", + "value" : "0x0004", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AllowRecip", + "value" : "0x0008", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Fast", + "value" : "0x0010", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "SelectionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Flatten", + "value" : "0x0001" + }, + { + "enumerant" : "DontFlatten", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "LoopControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Unroll", + "value" : "0x0001" + }, + { + "enumerant" : "DontUnroll", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FunctionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Inline", + "value" : "0x0001" + }, + { + "enumerant" : "DontInline", + "value" : "0x0002" + }, + { + "enumerant" : "Pure", + "value" : "0x0004" + }, + { + "enumerant" : "Const", + "value" : "0x0008" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemorySemantics", + "enumerants" : [ + { + "enumerant" : "Relaxed", + "value" : "0x0000" + }, + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Acquire", + "value" : "0x0002" + }, + { + "enumerant" : "Release", + "value" : "0x0004" + }, + { + "enumerant" : "AcquireRelease", + "value" : "0x0008" + }, + { + "enumerant" : "SequentiallyConsistent", + "value" : "0x0010" + }, + { + "enumerant" : "UniformMemory", + "value" : "0x0040", + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupMemory", + "value" : "0x0080" + }, + { + "enumerant" : "WorkgroupMemory", + "value" : "0x0100" + }, + { + "enumerant" : "CrossWorkgroupMemory", + "value" : "0x0200" + }, + { + "enumerant" : "AtomicCounterMemory", + "value" : "0x0400", + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "ImageMemory", + "value" : "0x0800" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemoryAccess", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Volatile", + "value" : "0x0001" + }, + { + "enumerant" : "Aligned", + "value" : "0x0002", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Nontemporal", + "value" : "0x0004" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "KernelProfilingInfo", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "CmdExecTime", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SourceLanguage", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "ESSL", + "value" : 1 + }, + { + "enumerant" : "GLSL", + "value" : 2 + }, + { + "enumerant" : "OpenCL_C", + "value" : 3 + }, + { + "enumerant" : "OpenCL_CPP", + "value" : 4 + }, + { + "enumerant" : "HLSL", + "value" : 5 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionModel", + "enumerants" : [ + { + "enumerant" : "Vertex", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TessellationControl", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessellationEvaluation", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Geometry", + "value" : 3, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Fragment", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLCompute", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Kernel", + "value" : 6, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AddressingModel", + "enumerants" : [ + { + "enumerant" : "Logical", + "value" : 0 + }, + { + "enumerant" : "Physical32", + "value" : 1, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Physical64", + "value" : 2, + "capabilities" : [ "Addresses" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "MemoryModel", + "enumerants" : [ + { + "enumerant" : "Simple", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSL450", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OpenCL", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionMode", + "enumerants" : [ + { + "enumerant" : "Invocations", + "value" : 0, + "capabilities" : [ "Geometry" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of <>'" } + ] + }, + { + "enumerant" : "SpacingEqual", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalEven", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalOdd", + "value" : 3, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCw", + "value" : 4, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCcw", + "value" : 5, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PixelCenterInteger", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginUpperLeft", + "value" : 7, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginLowerLeft", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "EarlyFragmentTests", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointMode", + "value" : 10, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Xfb", + "value" : 11, + "capabilities" : [ "TransformFeedback" ] + }, + { + "enumerant" : "DepthReplacing", + "value" : 12, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthGreater", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthLess", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthUnchanged", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "LocalSize", + "value" : 17, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHint", + "value" : 18, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "InputPoints", + "value" : 19, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLines", + "value" : 20, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLinesAdjacency", + "value" : 21, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Triangles", + "value" : 22, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InputTrianglesAdjacency", + "value" : 23, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Quads", + "value" : 24, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Isolines", + "value" : 25, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "OutputVertices", + "value" : 26, + "capabilities" : [ "Geometry", "Tessellation" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vertex count'" } + ] + }, + { + "enumerant" : "OutputPoints", + "value" : 27, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputLineStrip", + "value" : 28, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputTriangleStrip", + "value" : 29, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "VecTypeHint", + "value" : 30, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vector type'" } + ] + }, + { + "enumerant" : "ContractionOff", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "PostDepthCoverage", + "value" : 4446, + "capabilities" : [ "SampleMaskPostDepthCoverage" ] + }, + { + "enumerant" : "StencilRefReplacingEXT", + "value" : 5027, + "capabilities" : [ "StencilExportEXT" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StorageClass", + "enumerants" : [ + { + "enumerant" : "UniformConstant", + "value" : 0 + }, + { + "enumerant" : "Input", + "value" : 1 + }, + { + "enumerant" : "Uniform", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Output", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Workgroup", + "value" : 4 + }, + { + "enumerant" : "CrossWorkgroup", + "value" : 5 + }, + { + "enumerant" : "Private", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Function", + "value" : 7 + }, + { + "enumerant" : "Generic", + "value" : 8, + "capabilities" : [ "GenericPointer" ] + }, + { + "enumerant" : "PushConstant", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "AtomicCounter", + "value" : 10, + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "Image", + "value" : 11 + }, + { + "enumerant" : "StorageBuffer", + "value" : 12, + "extensions" : [ + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers" + ], + "capabilities" : [ "Shader" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Dim", + "enumerants" : [ + { + "enumerant" : "1D", + "value" : 0, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "2D", + "value" : 1 + }, + { + "enumerant" : "3D", + "value" : 2 + }, + { + "enumerant" : "Cube", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rect", + "value" : 4, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "Buffer", + "value" : 5, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "SubpassData", + "value" : 6, + "capabilities" : [ "InputAttachment" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerAddressingMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ClampToEdge", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Clamp", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Repeat", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerFilterMode", + "enumerants" : [ + { + "enumerant" : "Nearest", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Linear", + "value" : 1, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageFormat", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "Rgba32f", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16f", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32f", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8Snorm", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32f", + "value" : 6, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16f", + "value" : 7, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R11fG11fB10f", + "value" : 8, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16f", + "value" : 9, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16", + "value" : 10, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgb10A2", + "value" : 11, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16", + "value" : 12, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8", + "value" : 13, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16", + "value" : 14, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8", + "value" : 15, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16Snorm", + "value" : 16, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16Snorm", + "value" : 17, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8Snorm", + "value" : 18, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16Snorm", + "value" : 19, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8Snorm", + "value" : 20, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32i", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16i", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8i", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32i", + "value" : 24, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32i", + "value" : 25, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16i", + "value" : 26, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8i", + "value" : 27, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16i", + "value" : 28, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8i", + "value" : 29, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32ui", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16ui", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8ui", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32ui", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgb10a2ui", + "value" : 34, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg32ui", + "value" : 35, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16ui", + "value" : 36, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8ui", + "value" : 37, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16ui", + "value" : 38, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8ui", + "value" : 39, + "capabilities" : [ "StorageImageExtendedFormats" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelOrder", + "enumerants" : [ + { + "enumerant" : "R", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "A", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RG", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RA", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGB", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBA", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BGRA", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ARGB", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Intensity", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Luminance", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Rx", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGx", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBx", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Depth", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "DepthStencil", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGB", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBx", + "value" : 16, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBA", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sBGRA", + "value" : 18, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ABGR", + "value" : 19, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelDataType", + "enumerants" : [ + { + "enumerant" : "SnormInt8", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SnormInt16", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt8", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt16", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort565", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort555", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt8", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt16", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt32", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt8", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt16", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt32", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "HalfFloat", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt24", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010_2", + "value" : 16, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPRoundingMode", + "enumerants" : [ + { + "enumerant" : "RTE", + "value" : 0, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTZ", + "value" : 1, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTP", + "value" : 2, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTN", + "value" : 3, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LinkageType", + "enumerants" : [ + { + "enumerant" : "Export", + "value" : 0, + "capabilities" : [ "Linkage" ] + }, + { + "enumerant" : "Import", + "value" : 1, + "capabilities" : [ "Linkage" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AccessQualifier", + "enumerants" : [ + { + "enumerant" : "ReadOnly", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WriteOnly", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ReadWrite", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FunctionParameterAttribute", + "enumerants" : [ + { + "enumerant" : "Zext", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sext", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ByVal", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sret", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoAlias", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoCapture", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoWrite", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoReadWrite", + "value" : 7, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Decoration", + "enumerants" : [ + { + "enumerant" : "RelaxedPrecision", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SpecId", + "value" : 1, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Specialization Constant ID'" } + ] + }, + { + "enumerant" : "Block", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "BufferBlock", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "RowMajor", + "value" : 4, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ColMajor", + "value" : 5, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ArrayStride", + "value" : 6, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Array Stride'" } + ] + }, + { + "enumerant" : "MatrixStride", + "value" : 7, + "capabilities" : [ "Matrix" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Matrix Stride'" } + ] + }, + { + "enumerant" : "GLSLShared", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSLPacked", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CPacked", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BuiltIn", + "value" : 11, + "parameters" : [ + { "kind" : "BuiltIn" } + ] + }, + { + "enumerant" : "NoPerspective", + "value" : 13, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Flat", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Patch", + "value" : 15, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Centroid", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sample", + "value" : 17, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "Invariant", + "value" : 18, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Restrict", + "value" : 19 + }, + { + "enumerant" : "Aliased", + "value" : 20 + }, + { + "enumerant" : "Volatile", + "value" : 21 + }, + { + "enumerant" : "Constant", + "value" : 22, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Coherent", + "value" : 23 + }, + { + "enumerant" : "NonWritable", + "value" : 24 + }, + { + "enumerant" : "NonReadable", + "value" : 25 + }, + { + "enumerant" : "Uniform", + "value" : 26, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SaturatedConversion", + "value" : 28, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Stream", + "value" : 29, + "capabilities" : [ "GeometryStreams" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stream Number'" } + ] + }, + { + "enumerant" : "Location", + "value" : 30, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Location'" } + ] + }, + { + "enumerant" : "Component", + "value" : 31, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Component'" } + ] + }, + { + "enumerant" : "Index", + "value" : 32, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Index'" } + ] + }, + { + "enumerant" : "Binding", + "value" : 33, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Binding Point'" } + ] + }, + { + "enumerant" : "DescriptorSet", + "value" : 34, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Descriptor Set'" } + ] + }, + { + "enumerant" : "Offset", + "value" : 35, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Byte Offset'" } + ] + }, + { + "enumerant" : "XfbBuffer", + "value" : 36, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Buffer Number'" } + ] + }, + { + "enumerant" : "XfbStride", + "value" : 37, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Stride'" } + ] + }, + { + "enumerant" : "FuncParamAttr", + "value" : 38, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FunctionParameterAttribute", "name" : "'Function Parameter Attribute'" } + ] + }, + { + "enumerant" : "FPRoundingMode", + "value" : 39, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ], + "parameters" : [ + { "kind" : "FPRoundingMode", "name" : "'Floating-Point Rounding Mode'" } + ] + }, + { + "enumerant" : "FPFastMathMode", + "value" : 40, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FPFastMathMode", "name" : "'Fast-Math Mode'" } + ] + }, + { + "enumerant" : "LinkageAttributes", + "value" : 41, + "capabilities" : [ "Linkage" ], + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "LinkageType", "name" : "'Linkage Type'" } + ] + }, + { + "enumerant" : "NoContraction", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InputAttachmentIndex", + "value" : 43, + "capabilities" : [ "InputAttachment" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Attachment Index'" } + ] + }, + { + "enumerant" : "Alignment", + "value" : 44, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "ExplicitInterpAMD", + "value" : 4999 + }, + { + "enumerant" : "OverrideCoverageNV", + "value" : 5248, + "capabilities" : [ "SampleMaskOverrideCoverageNV" ] + }, + { + "enumerant" : "PassthroughNV", + "value" : 5250, + "capabilities" : [ "GeometryShaderPassthroughNV" ] + }, + { + "enumerant" : "ViewportRelativeNV", + "value" : 5252, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryViewportRelativeNV", + "value" : 5256, + "capabilities" : [ "ShaderStereoViewNV" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ] + }, + { + "enumerant" : "HlslCounterBufferGOOGLE", + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "enumerant" : "HlslSemanticGOOGLE", + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "BuiltIn", + "enumerants" : [ + { + "enumerant" : "Position", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointSize", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 3, + "capabilities" : [ "ClipDistance" ] + }, + { + "enumerant" : "CullDistance", + "value" : 4, + "capabilities" : [ "CullDistance" ] + }, + { + "enumerant" : "VertexId", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceId", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PrimitiveId", + "value" : 7, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InvocationId", + "value" : 8, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "Layer", + "value" : 9, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ViewportIndex", + "value" : 10, + "capabilities" : [ "MultiViewport" ] + }, + { + "enumerant" : "TessLevelOuter", + "value" : 11, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessLevelInner", + "value" : 12, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessCoord", + "value" : 13, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PatchVertices", + "value" : 14, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "FragCoord", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointCoord", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FrontFacing", + "value" : 17, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampleId", + "value" : 18, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SamplePosition", + "value" : 19, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SampleMask", + "value" : 20, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FragDepth", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "HelperInvocation", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "NumWorkgroups", + "value" : 24 + }, + { + "enumerant" : "WorkgroupSize", + "value" : 25 + }, + { + "enumerant" : "WorkgroupId", + "value" : 26 + }, + { + "enumerant" : "LocalInvocationId", + "value" : 27 + }, + { + "enumerant" : "GlobalInvocationId", + "value" : 28 + }, + { + "enumerant" : "LocalInvocationIndex", + "value" : 29 + }, + { + "enumerant" : "WorkDim", + "value" : 30, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalSize", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "EnqueuedWorkgroupSize", + "value" : 32, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalOffset", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalLinearId", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 36, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupMaxSize", + "value" : 37, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumSubgroups", + "value" : 38, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumEnqueuedSubgroups", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupId", + "value" : 40, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupLocalInvocationId", + "value" : 41, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "VertexIndex", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceIndex", + "value" : 43, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupEqMaskKHR", + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGeMaskKHR", + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGtMaskKHR", + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLeMaskKHR", + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLtMaskKHR", + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "BaseVertex", + "value" : 4424, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "BaseInstance", + "value" : 4425, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DrawIndex", + "value" : 4426, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DeviceIndex", + "value" : 4438, + "capabilities" : [ "DeviceGroup" ] + }, + { + "enumerant" : "ViewIndex", + "value" : 4440, + "capabilities" : [ "MultiView" ] + }, + { + "enumerant" : "BaryCoordNoPerspAMD", + "value" : 4992 + }, + { + "enumerant" : "BaryCoordNoPerspCentroidAMD", + "value" : 4993 + }, + { + "enumerant" : "BaryCoordNoPerspSampleAMD", + "value" : 4994 + }, + { + "enumerant" : "BaryCoordSmoothAMD", + "value" : 4995 + }, + { + "enumerant" : "BaryCoordSmoothCentroidAMD", + "value" : 4996 + }, + { + "enumerant" : "BaryCoordSmoothSampleAMD", + "value" : 4997 + }, + { + "enumerant" : "BaryCoordPullModelAMD", + "value" : 4998 + }, + { + "enumerant" : "FragStencilRefEXT", + "value" : 5014, + "capabilities" : [ "StencilExportEXT" ] + }, + { + "enumerant" : "ViewportMaskNV", + "value" : 5253, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryPositionNV", + "value" : 5257, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "SecondaryViewportMaskNV", + "value" : 5258, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "PositionPerViewNV", + "value" : 5261, + "capabilities" : [ "PerViewAttributesNV" ] + }, + { + "enumerant" : "ViewportMaskPerViewNV", + "value" : 5262, + "capabilities" : [ "PerViewAttributesNV" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Scope", + "enumerants" : [ + { + "enumerant" : "CrossDevice", + "value" : 0 + }, + { + "enumerant" : "Device", + "value" : 1 + }, + { + "enumerant" : "Workgroup", + "value" : 2 + }, + { + "enumerant" : "Subgroup", + "value" : 3 + }, + { + "enumerant" : "Invocation", + "value" : 4 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "GroupOperation", + "enumerants" : [ + { + "enumerant" : "Reduce", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InclusiveScan", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ExclusiveScan", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "KernelEnqueueFlags", + "enumerants" : [ + { + "enumerant" : "NoWait", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitKernel", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitWorkGroup", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Capability", + "enumerants" : [ + { + "enumerant" : "Matrix", + "value" : 0 + }, + { + "enumerant" : "Shader", + "value" : 1, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "Geometry", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Tessellation", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Addresses", + "value" : 4 + }, + { + "enumerant" : "Linkage", + "value" : 5 + }, + { + "enumerant" : "Kernel", + "value" : 6 + }, + { + "enumerant" : "Vector16", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16Buffer", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16", + "value" : 9 + }, + { + "enumerant" : "Float64", + "value" : 10 + }, + { + "enumerant" : "Int64", + "value" : 11 + }, + { + "enumerant" : "Int64Atomics", + "value" : 12, + "capabilities" : [ "Int64" ] + }, + { + "enumerant" : "ImageBasic", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ImageReadWrite", + "value" : 14, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "ImageMipmap", + "value" : 15, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "Pipes", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Groups", + "value" : 18 + }, + { + "enumerant" : "DeviceEnqueue", + "value" : 19, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "LiteralSampler", + "value" : 20, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AtomicStorage", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Int16", + "value" : 22 + }, + { + "enumerant" : "TessellationPointSize", + "value" : 23, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "GeometryPointSize", + "value" : 24, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ImageGatherExtended", + "value" : 25, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageMultisample", + "value" : 27, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "UniformBufferArrayDynamicIndexing", + "value" : 28, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledImageArrayDynamicIndexing", + "value" : 29, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageBufferArrayDynamicIndexing", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageArrayDynamicIndexing", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CullDistance", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageCubeArray", + "value" : 34, + "capabilities" : [ "SampledCubeArray" ] + }, + { + "enumerant" : "SampleRateShading", + "value" : 35, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageRect", + "value" : 36, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "SampledRect", + "value" : 37, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GenericPointer", + "value" : 38, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Int8", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InputAttachment", + "value" : 40, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SparseResidency", + "value" : 41, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MinLod", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sampled1D", + "value" : 43 + }, + { + "enumerant" : "Image1D", + "value" : 44, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "SampledCubeArray", + "value" : 45, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledBuffer", + "value" : 46 + }, + { + "enumerant" : "ImageBuffer", + "value" : 47, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "ImageMSArray", + "value" : 48, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageExtendedFormats", + "value" : 49, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageQuery", + "value" : 50, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DerivativeControl", + "value" : 51, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InterpolationFunction", + "value" : 52, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TransformFeedback", + "value" : 53, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GeometryStreams", + "value" : 54, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "StorageImageReadWithoutFormat", + "value" : 55, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageWriteWithoutFormat", + "value" : 56, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MultiViewport", + "value" : 57, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "SubgroupBallotKHR", + "value" : 4423, + "extensions" : [ "SPV_KHR_shader_ballot" ] + }, + { + "enumerant" : "DrawParameters", + "value" : 4427, + "extensions" : [ "SPV_KHR_shader_draw_parameters" ] + }, + { + "enumerant" : "SubgroupVoteKHR", + "value" : 4431, + "extensions" : [ "SPV_KHR_subgroup_vote" ] + }, + { + "enumerant" : "StorageBuffer16BitAccess", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniformBufferBlock16", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "UniformAndStorageBuffer16BitAccess", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniform16", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StoragePushConstant16", + "value" : 4435, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageInputOutput16", + "value" : 4436, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "DeviceGroup", + "value" : 4437, + "extensions" : [ "SPV_KHR_device_group" ] + }, + { + "enumerant" : "MultiView", + "value" : 4439, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_multiview" ] + }, + { + "enumerant" : "VariablePointersStorageBuffer", + "value" : 4441, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant" : "VariablePointers", + "value" : 4442, + "capabilities" : [ "VariablePointersStorageBuffer" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant": "AtomicStorageOps", + "value": 4445, + "extensions": [ "SPV_KHR_shader_atomic_counter_ops" ] + }, + { + "enumerant" : "SampleMaskPostDepthCoverage", + "value" : 4447, + "extensions" : [ "SPV_KHR_post_depth_coverage" ] + }, + { + "enumerant" : "ImageGatherBiasLodAMD", + "value" : 5009, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_texture_gather_bias_lod" ] + }, + { + "enumerant" : "FragmentMaskAMD", + "value" : 5010, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ] + }, + { + "enumerant" : "StencilExportEXT", + "value" : 5013, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ] + }, + { + "enumerant" : "ImageReadWriteLodAMD", + "value" : 5015, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_image_load_store_lod" ] + }, + { + "enumerant" : "SampleMaskOverrideCoverageNV", + "value" : 5249, + "capabilities" : [ "SampleRateShading" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ] + }, + { + "enumerant" : "GeometryShaderPassthroughNV", + "value" : 5251, + "capabilities" : [ "Geometry" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerEXT", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_EXT_shader_viewport_index_layer" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerNV", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderViewportMaskNV", + "value" : 5255, + "capabilities" : [ "ShaderViewportIndexLayerNV" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderStereoViewNV", + "value" : 5259, + "capabilities" : [ "ShaderViewportMaskNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ] + }, + { + "enumerant" : "PerViewAttributesNV", + "value" : 5260, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes" ] + }, + { + "enumerant" : "SubgroupShuffleINTEL", + "value" : 5568, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupBufferBlockIOINTEL", + "value" : 5569, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupImageBlockIOINTEL", + "value" : 5570, + "extensions" : [ "SPV_INTEL_subgroups" ] + } + ] + }, + { + "category" : "Id", + "kind" : "IdResultType", + "doc" : "Reference to an representing the result's type of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdResult", + "doc" : "Definition of an representing the result of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdMemorySemantics", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the MemorySemantics operand kind" + }, + { + "category" : "Id", + "kind" : "IdScope", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the Scope operand kind" + }, + { + "category" : "Id", + "kind" : "IdRef", + "doc" : "Reference to an " + }, + { + "category" : "Literal", + "kind" : "LiteralInteger", + "doc" : "An integer consuming one or more words" + }, + { + "category" : "Literal", + "kind" : "LiteralString", + "doc" : "A null-terminated stream of characters consuming an integral number of words" + }, + { + "category" : "Literal", + "kind" : "LiteralContextDependentNumber", + "doc" : "A literal number whose size and format are determined by a previous operand in the enclosing instruction" + }, + { + "category" : "Literal", + "kind" : "LiteralExtInstInteger", + "doc" : "A 32-bit unsigned integer indicating which instruction to use and determining the layout of following operands (for OpExtInst)" + }, + { + "category" : "Literal", + "kind" : "LiteralSpecConstantOpInteger", + "doc" : "An opcode indicating the operation to be performed and determining the layout of following operands (for OpSpecConstantOp)" + }, + { + "category" : "Composite", + "kind" : "PairLiteralIntegerIdRef", + "bases" : [ "LiteralInteger", "IdRef" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefLiteralInteger", + "bases" : [ "IdRef", "LiteralInteger" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefIdRef", + "bases" : [ "IdRef", "IdRef" ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.cs b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.cs new file mode 100644 index 0000000..de325cc --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.cs @@ -0,0 +1,993 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C# +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + + public static class Specification + { + public const uint MagicNumber = 0x07230203; + public const uint Version = 0x00010000; + public const uint Revision = 12; + public const uint OpCodeMask = 0xffff; + public const uint WordCountShift = 16; + + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + } + + public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + } + + public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + } + + public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + } + + public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + } + + public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + } + + public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + } + + public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + } + + public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + } + + public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + } + + public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + } + + public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + } + + public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + } + + public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + } + + public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + } + + public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + } + } +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.h new file mode 100644 index 0000000..bd5a9b9 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.h @@ -0,0 +1,993 @@ +/* +** Copyright (c) 2014-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10000 +#define SPV_REVISION 12 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010000; +static const unsigned int SpvRevision = 12; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpDecorateId = 332, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#endif // #ifndef spirv_H + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp new file mode 100644 index 0000000..e98a89c --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp @@ -0,0 +1,1002 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10000 +#define SPV_REVISION 12 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010000; +static const unsigned int Revision = 12; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilityMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + OpMax = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp11 b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp11 new file mode 100644 index 0000000..8896e81 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.hpp11 @@ -0,0 +1,1002 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10000 +#define SPV_REVISION 12 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010000; +static const unsigned int Revision = 12; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum class SourceLanguage : unsigned { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, +}; + +enum class ExecutionModel : unsigned { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + Max = 0x7fffffff, +}; + +enum class AddressingModel : unsigned { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + Max = 0x7fffffff, +}; + +enum class MemoryModel : unsigned { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Max = 0x7fffffff, +}; + +enum class ExecutionMode : unsigned { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + Max = 0x7fffffff, +}; + +enum class StorageClass : unsigned { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + Max = 0x7fffffff, +}; + +enum class Dim : unsigned { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, +}; + +enum class SamplerAddressingMode : unsigned { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class SamplerFilterMode : unsigned { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +}; + +enum class ImageFormat : unsigned { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + Max = 0x7fffffff, +}; + +enum class ImageChannelOrder : unsigned { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +}; + +enum class ImageChannelDataType : unsigned { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, +}; + +enum class ImageOperandsShift : unsigned { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + Max = 0x7fffffff, +}; + +enum class ImageOperandsMask : unsigned { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, +}; + +enum class FPFastMathModeShift : unsigned { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + Max = 0x7fffffff, +}; + +enum class FPFastMathModeMask : unsigned { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, +}; + +enum class FPRoundingMode : unsigned { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +}; + +enum class LinkageType : unsigned { + Export = 0, + Import = 1, + Max = 0x7fffffff, +}; + +enum class AccessQualifier : unsigned { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +}; + +enum class FunctionParameterAttribute : unsigned { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, +}; + +enum class Decoration : unsigned { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + Max = 0x7fffffff, +}; + +enum class BuiltIn : unsigned { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + Max = 0x7fffffff, +}; + +enum class SelectionControlShift : unsigned { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +}; + +enum class SelectionControlMask : unsigned { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +}; + +enum class LoopControlShift : unsigned { + Unroll = 0, + DontUnroll = 1, + Max = 0x7fffffff, +}; + +enum class LoopControlMask : unsigned { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, +}; + +enum class FunctionControlShift : unsigned { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + Max = 0x7fffffff, +}; + +enum class FunctionControlMask : unsigned { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, +}; + +enum class MemorySemanticsShift : unsigned { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + Max = 0x7fffffff, +}; + +enum class MemorySemanticsMask : unsigned { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, +}; + +enum class MemoryAccessShift : unsigned { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + Max = 0x7fffffff, +}; + +enum class MemoryAccessMask : unsigned { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, +}; + +enum class Scope : unsigned { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + Max = 0x7fffffff, +}; + +enum class GroupOperation : unsigned { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + Max = 0x7fffffff, +}; + +enum class KernelEnqueueFlags : unsigned { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoShift : unsigned { + CmdExecTime = 0, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoMask : unsigned { + MaskNone = 0, + CmdExecTime = 0x00000001, +}; + +enum class Capability : unsigned { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + Max = 0x7fffffff, +}; + +enum class Op : unsigned { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + Max = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.json new file mode 100644 index 0000000..9b0a8f3 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.json @@ -0,0 +1,1020 @@ +{ + "spv": + { + "meta": + { + "Comment": + [ + [ + "Copyright (c) 2014-2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + [ + "This header is automatically generated by the same tool that creates", + "the Binary Section of the SPIR-V specification." + ], + [ + "Enumeration tokens for SPIR-V, in various styles:", + " C, C++, C++11, JSON, Lua, Python", + "", + "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", + "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", + "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", + "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", + "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", + "", + "Some tokens act like mask values, which can be OR'd together,", + "while others are mutually exclusive. The mask-like ones have", + "\"Mask\" in their name, and a parallel enum that has the shift", + "amount (1 << x) for each corresponding enumerant." + ] + ], + "MagicNumber": 119734787, + "Version": 65536, + "Revision": 12, + "OpCodeMask": 65535, + "WordCountShift": 16 + }, + "enum": + [ + { + "Name": "SourceLanguage", + "Type": "Value", + "Values": + { + "Unknown": 0, + "ESSL": 1, + "GLSL": 2, + "OpenCL_C": 3, + "OpenCL_CPP": 4, + "HLSL": 5 + } + }, + { + "Name": "ExecutionModel", + "Type": "Value", + "Values": + { + "Vertex": 0, + "TessellationControl": 1, + "TessellationEvaluation": 2, + "Geometry": 3, + "Fragment": 4, + "GLCompute": 5, + "Kernel": 6 + } + }, + { + "Name": "AddressingModel", + "Type": "Value", + "Values": + { + "Logical": 0, + "Physical32": 1, + "Physical64": 2 + } + }, + { + "Name": "MemoryModel", + "Type": "Value", + "Values": + { + "Simple": 0, + "GLSL450": 1, + "OpenCL": 2 + } + }, + { + "Name": "ExecutionMode", + "Type": "Value", + "Values": + { + "Invocations": 0, + "SpacingEqual": 1, + "SpacingFractionalEven": 2, + "SpacingFractionalOdd": 3, + "VertexOrderCw": 4, + "VertexOrderCcw": 5, + "PixelCenterInteger": 6, + "OriginUpperLeft": 7, + "OriginLowerLeft": 8, + "EarlyFragmentTests": 9, + "PointMode": 10, + "Xfb": 11, + "DepthReplacing": 12, + "DepthGreater": 14, + "DepthLess": 15, + "DepthUnchanged": 16, + "LocalSize": 17, + "LocalSizeHint": 18, + "InputPoints": 19, + "InputLines": 20, + "InputLinesAdjacency": 21, + "Triangles": 22, + "InputTrianglesAdjacency": 23, + "Quads": 24, + "Isolines": 25, + "OutputVertices": 26, + "OutputPoints": 27, + "OutputLineStrip": 28, + "OutputTriangleStrip": 29, + "VecTypeHint": 30, + "ContractionOff": 31, + "PostDepthCoverage": 4446, + "StencilRefReplacingEXT": 5027 + } + }, + { + "Name": "StorageClass", + "Type": "Value", + "Values": + { + "UniformConstant": 0, + "Input": 1, + "Uniform": 2, + "Output": 3, + "Workgroup": 4, + "CrossWorkgroup": 5, + "Private": 6, + "Function": 7, + "Generic": 8, + "PushConstant": 9, + "AtomicCounter": 10, + "Image": 11, + "StorageBuffer": 12 + } + }, + { + "Name": "Dim", + "Type": "Value", + "Values": + { + "Dim1D": 0, + "Dim2D": 1, + "Dim3D": 2, + "Cube": 3, + "Rect": 4, + "Buffer": 5, + "SubpassData": 6 + } + }, + { + "Name": "SamplerAddressingMode", + "Type": "Value", + "Values": + { + "None": 0, + "ClampToEdge": 1, + "Clamp": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "SamplerFilterMode", + "Type": "Value", + "Values": + { + "Nearest": 0, + "Linear": 1 + } + }, + { + "Name": "ImageFormat", + "Type": "Value", + "Values": + { + "Unknown": 0, + "Rgba32f": 1, + "Rgba16f": 2, + "R32f": 3, + "Rgba8": 4, + "Rgba8Snorm": 5, + "Rg32f": 6, + "Rg16f": 7, + "R11fG11fB10f": 8, + "R16f": 9, + "Rgba16": 10, + "Rgb10A2": 11, + "Rg16": 12, + "Rg8": 13, + "R16": 14, + "R8": 15, + "Rgba16Snorm": 16, + "Rg16Snorm": 17, + "Rg8Snorm": 18, + "R16Snorm": 19, + "R8Snorm": 20, + "Rgba32i": 21, + "Rgba16i": 22, + "Rgba8i": 23, + "R32i": 24, + "Rg32i": 25, + "Rg16i": 26, + "Rg8i": 27, + "R16i": 28, + "R8i": 29, + "Rgba32ui": 30, + "Rgba16ui": 31, + "Rgba8ui": 32, + "R32ui": 33, + "Rgb10a2ui": 34, + "Rg32ui": 35, + "Rg16ui": 36, + "Rg8ui": 37, + "R16ui": 38, + "R8ui": 39 + } + }, + { + "Name": "ImageChannelOrder", + "Type": "Value", + "Values": + { + "R": 0, + "A": 1, + "RG": 2, + "RA": 3, + "RGB": 4, + "RGBA": 5, + "BGRA": 6, + "ARGB": 7, + "Intensity": 8, + "Luminance": 9, + "Rx": 10, + "RGx": 11, + "RGBx": 12, + "Depth": 13, + "DepthStencil": 14, + "sRGB": 15, + "sRGBx": 16, + "sRGBA": 17, + "sBGRA": 18, + "ABGR": 19 + } + }, + { + "Name": "ImageChannelDataType", + "Type": "Value", + "Values": + { + "SnormInt8": 0, + "SnormInt16": 1, + "UnormInt8": 2, + "UnormInt16": 3, + "UnormShort565": 4, + "UnormShort555": 5, + "UnormInt101010": 6, + "SignedInt8": 7, + "SignedInt16": 8, + "SignedInt32": 9, + "UnsignedInt8": 10, + "UnsignedInt16": 11, + "UnsignedInt32": 12, + "HalfFloat": 13, + "Float": 14, + "UnormInt24": 15, + "UnormInt101010_2": 16 + } + }, + { + "Name": "ImageOperands", + "Type": "Bit", + "Values": + { + "Bias": 0, + "Lod": 1, + "Grad": 2, + "ConstOffset": 3, + "Offset": 4, + "ConstOffsets": 5, + "Sample": 6, + "MinLod": 7 + } + }, + { + "Name": "FPFastMathMode", + "Type": "Bit", + "Values": + { + "NotNaN": 0, + "NotInf": 1, + "NSZ": 2, + "AllowRecip": 3, + "Fast": 4 + } + }, + { + "Name": "FPRoundingMode", + "Type": "Value", + "Values": + { + "RTE": 0, + "RTZ": 1, + "RTP": 2, + "RTN": 3 + } + }, + { + "Name": "LinkageType", + "Type": "Value", + "Values": + { + "Export": 0, + "Import": 1 + } + }, + { + "Name": "AccessQualifier", + "Type": "Value", + "Values": + { + "ReadOnly": 0, + "WriteOnly": 1, + "ReadWrite": 2 + } + }, + { + "Name": "FunctionParameterAttribute", + "Type": "Value", + "Values": + { + "Zext": 0, + "Sext": 1, + "ByVal": 2, + "Sret": 3, + "NoAlias": 4, + "NoCapture": 5, + "NoWrite": 6, + "NoReadWrite": 7 + } + }, + { + "Name": "Decoration", + "Type": "Value", + "Values": + { + "RelaxedPrecision": 0, + "SpecId": 1, + "Block": 2, + "BufferBlock": 3, + "RowMajor": 4, + "ColMajor": 5, + "ArrayStride": 6, + "MatrixStride": 7, + "GLSLShared": 8, + "GLSLPacked": 9, + "CPacked": 10, + "BuiltIn": 11, + "NoPerspective": 13, + "Flat": 14, + "Patch": 15, + "Centroid": 16, + "Sample": 17, + "Invariant": 18, + "Restrict": 19, + "Aliased": 20, + "Volatile": 21, + "Constant": 22, + "Coherent": 23, + "NonWritable": 24, + "NonReadable": 25, + "Uniform": 26, + "SaturatedConversion": 28, + "Stream": 29, + "Location": 30, + "Component": 31, + "Index": 32, + "Binding": 33, + "DescriptorSet": 34, + "Offset": 35, + "XfbBuffer": 36, + "XfbStride": 37, + "FuncParamAttr": 38, + "FPRoundingMode": 39, + "FPFastMathMode": 40, + "LinkageAttributes": 41, + "NoContraction": 42, + "InputAttachmentIndex": 43, + "Alignment": 44, + "ExplicitInterpAMD": 4999, + "OverrideCoverageNV": 5248, + "PassthroughNV": 5250, + "ViewportRelativeNV": 5252, + "SecondaryViewportRelativeNV": 5256, + "HlslCounterBufferGOOGLE": 5634, + "HlslSemanticGOOGLE": 5635 + } + }, + { + "Name": "BuiltIn", + "Type": "Value", + "Values": + { + "Position": 0, + "PointSize": 1, + "ClipDistance": 3, + "CullDistance": 4, + "VertexId": 5, + "InstanceId": 6, + "PrimitiveId": 7, + "InvocationId": 8, + "Layer": 9, + "ViewportIndex": 10, + "TessLevelOuter": 11, + "TessLevelInner": 12, + "TessCoord": 13, + "PatchVertices": 14, + "FragCoord": 15, + "PointCoord": 16, + "FrontFacing": 17, + "SampleId": 18, + "SamplePosition": 19, + "SampleMask": 20, + "FragDepth": 22, + "HelperInvocation": 23, + "NumWorkgroups": 24, + "WorkgroupSize": 25, + "WorkgroupId": 26, + "LocalInvocationId": 27, + "GlobalInvocationId": 28, + "LocalInvocationIndex": 29, + "WorkDim": 30, + "GlobalSize": 31, + "EnqueuedWorkgroupSize": 32, + "GlobalOffset": 33, + "GlobalLinearId": 34, + "SubgroupSize": 36, + "SubgroupMaxSize": 37, + "NumSubgroups": 38, + "NumEnqueuedSubgroups": 39, + "SubgroupId": 40, + "SubgroupLocalInvocationId": 41, + "VertexIndex": 42, + "InstanceIndex": 43, + "SubgroupEqMaskKHR": 4416, + "SubgroupGeMaskKHR": 4417, + "SubgroupGtMaskKHR": 4418, + "SubgroupLeMaskKHR": 4419, + "SubgroupLtMaskKHR": 4420, + "BaseVertex": 4424, + "BaseInstance": 4425, + "DrawIndex": 4426, + "DeviceIndex": 4438, + "ViewIndex": 4440, + "BaryCoordNoPerspAMD": 4992, + "BaryCoordNoPerspCentroidAMD": 4993, + "BaryCoordNoPerspSampleAMD": 4994, + "BaryCoordSmoothAMD": 4995, + "BaryCoordSmoothCentroidAMD": 4996, + "BaryCoordSmoothSampleAMD": 4997, + "BaryCoordPullModelAMD": 4998, + "FragStencilRefEXT": 5014, + "ViewportMaskNV": 5253, + "SecondaryPositionNV": 5257, + "SecondaryViewportMaskNV": 5258, + "PositionPerViewNV": 5261, + "ViewportMaskPerViewNV": 5262 + } + }, + { + "Name": "SelectionControl", + "Type": "Bit", + "Values": + { + "Flatten": 0, + "DontFlatten": 1 + } + }, + { + "Name": "LoopControl", + "Type": "Bit", + "Values": + { + "Unroll": 0, + "DontUnroll": 1 + } + }, + { + "Name": "FunctionControl", + "Type": "Bit", + "Values": + { + "Inline": 0, + "DontInline": 1, + "Pure": 2, + "Const": 3 + } + }, + { + "Name": "MemorySemantics", + "Type": "Bit", + "Values": + { + "Acquire": 1, + "Release": 2, + "AcquireRelease": 3, + "SequentiallyConsistent": 4, + "UniformMemory": 6, + "SubgroupMemory": 7, + "WorkgroupMemory": 8, + "CrossWorkgroupMemory": 9, + "AtomicCounterMemory": 10, + "ImageMemory": 11 + } + }, + { + "Name": "MemoryAccess", + "Type": "Bit", + "Values": + { + "Volatile": 0, + "Aligned": 1, + "Nontemporal": 2 + } + }, + { + "Name": "Scope", + "Type": "Value", + "Values": + { + "CrossDevice": 0, + "Device": 1, + "Workgroup": 2, + "Subgroup": 3, + "Invocation": 4 + } + }, + { + "Name": "GroupOperation", + "Type": "Value", + "Values": + { + "Reduce": 0, + "InclusiveScan": 1, + "ExclusiveScan": 2 + } + }, + { + "Name": "KernelEnqueueFlags", + "Type": "Value", + "Values": + { + "NoWait": 0, + "WaitKernel": 1, + "WaitWorkGroup": 2 + } + }, + { + "Name": "KernelProfilingInfo", + "Type": "Bit", + "Values": + { + "CmdExecTime": 0 + } + }, + { + "Name": "Capability", + "Type": "Value", + "Values": + { + "Matrix": 0, + "Shader": 1, + "Geometry": 2, + "Tessellation": 3, + "Addresses": 4, + "Linkage": 5, + "Kernel": 6, + "Vector16": 7, + "Float16Buffer": 8, + "Float16": 9, + "Float64": 10, + "Int64": 11, + "Int64Atomics": 12, + "ImageBasic": 13, + "ImageReadWrite": 14, + "ImageMipmap": 15, + "Pipes": 17, + "Groups": 18, + "DeviceEnqueue": 19, + "LiteralSampler": 20, + "AtomicStorage": 21, + "Int16": 22, + "TessellationPointSize": 23, + "GeometryPointSize": 24, + "ImageGatherExtended": 25, + "StorageImageMultisample": 27, + "UniformBufferArrayDynamicIndexing": 28, + "SampledImageArrayDynamicIndexing": 29, + "StorageBufferArrayDynamicIndexing": 30, + "StorageImageArrayDynamicIndexing": 31, + "ClipDistance": 32, + "CullDistance": 33, + "ImageCubeArray": 34, + "SampleRateShading": 35, + "ImageRect": 36, + "SampledRect": 37, + "GenericPointer": 38, + "Int8": 39, + "InputAttachment": 40, + "SparseResidency": 41, + "MinLod": 42, + "Sampled1D": 43, + "Image1D": 44, + "SampledCubeArray": 45, + "SampledBuffer": 46, + "ImageBuffer": 47, + "ImageMSArray": 48, + "StorageImageExtendedFormats": 49, + "ImageQuery": 50, + "DerivativeControl": 51, + "InterpolationFunction": 52, + "TransformFeedback": 53, + "GeometryStreams": 54, + "StorageImageReadWithoutFormat": 55, + "StorageImageWriteWithoutFormat": 56, + "MultiViewport": 57, + "SubgroupBallotKHR": 4423, + "DrawParameters": 4427, + "SubgroupVoteKHR": 4431, + "StorageBuffer16BitAccess": 4433, + "StorageUniformBufferBlock16": 4433, + "StorageUniform16": 4434, + "UniformAndStorageBuffer16BitAccess": 4434, + "StoragePushConstant16": 4435, + "StorageInputOutput16": 4436, + "DeviceGroup": 4437, + "MultiView": 4439, + "VariablePointersStorageBuffer": 4441, + "VariablePointers": 4442, + "AtomicStorageOps": 4445, + "SampleMaskPostDepthCoverage": 4447, + "ImageGatherBiasLodAMD": 5009, + "FragmentMaskAMD": 5010, + "StencilExportEXT": 5013, + "ImageReadWriteLodAMD": 5015, + "SampleMaskOverrideCoverageNV": 5249, + "GeometryShaderPassthroughNV": 5251, + "ShaderViewportIndexLayerEXT": 5254, + "ShaderViewportIndexLayerNV": 5254, + "ShaderViewportMaskNV": 5255, + "ShaderStereoViewNV": 5259, + "PerViewAttributesNV": 5260, + "SubgroupShuffleINTEL": 5568, + "SubgroupBufferBlockIOINTEL": 5569, + "SubgroupImageBlockIOINTEL": 5570 + } + }, + { + "Name": "Op", + "Type": "Value", + "Values": + { + "OpNop": 0, + "OpUndef": 1, + "OpSourceContinued": 2, + "OpSource": 3, + "OpSourceExtension": 4, + "OpName": 5, + "OpMemberName": 6, + "OpString": 7, + "OpLine": 8, + "OpExtension": 10, + "OpExtInstImport": 11, + "OpExtInst": 12, + "OpMemoryModel": 14, + "OpEntryPoint": 15, + "OpExecutionMode": 16, + "OpCapability": 17, + "OpTypeVoid": 19, + "OpTypeBool": 20, + "OpTypeInt": 21, + "OpTypeFloat": 22, + "OpTypeVector": 23, + "OpTypeMatrix": 24, + "OpTypeImage": 25, + "OpTypeSampler": 26, + "OpTypeSampledImage": 27, + "OpTypeArray": 28, + "OpTypeRuntimeArray": 29, + "OpTypeStruct": 30, + "OpTypeOpaque": 31, + "OpTypePointer": 32, + "OpTypeFunction": 33, + "OpTypeEvent": 34, + "OpTypeDeviceEvent": 35, + "OpTypeReserveId": 36, + "OpTypeQueue": 37, + "OpTypePipe": 38, + "OpTypeForwardPointer": 39, + "OpConstantTrue": 41, + "OpConstantFalse": 42, + "OpConstant": 43, + "OpConstantComposite": 44, + "OpConstantSampler": 45, + "OpConstantNull": 46, + "OpSpecConstantTrue": 48, + "OpSpecConstantFalse": 49, + "OpSpecConstant": 50, + "OpSpecConstantComposite": 51, + "OpSpecConstantOp": 52, + "OpFunction": 54, + "OpFunctionParameter": 55, + "OpFunctionEnd": 56, + "OpFunctionCall": 57, + "OpVariable": 59, + "OpImageTexelPointer": 60, + "OpLoad": 61, + "OpStore": 62, + "OpCopyMemory": 63, + "OpCopyMemorySized": 64, + "OpAccessChain": 65, + "OpInBoundsAccessChain": 66, + "OpPtrAccessChain": 67, + "OpArrayLength": 68, + "OpGenericPtrMemSemantics": 69, + "OpInBoundsPtrAccessChain": 70, + "OpDecorate": 71, + "OpMemberDecorate": 72, + "OpDecorationGroup": 73, + "OpGroupDecorate": 74, + "OpGroupMemberDecorate": 75, + "OpVectorExtractDynamic": 77, + "OpVectorInsertDynamic": 78, + "OpVectorShuffle": 79, + "OpCompositeConstruct": 80, + "OpCompositeExtract": 81, + "OpCompositeInsert": 82, + "OpCopyObject": 83, + "OpTranspose": 84, + "OpSampledImage": 86, + "OpImageSampleImplicitLod": 87, + "OpImageSampleExplicitLod": 88, + "OpImageSampleDrefImplicitLod": 89, + "OpImageSampleDrefExplicitLod": 90, + "OpImageSampleProjImplicitLod": 91, + "OpImageSampleProjExplicitLod": 92, + "OpImageSampleProjDrefImplicitLod": 93, + "OpImageSampleProjDrefExplicitLod": 94, + "OpImageFetch": 95, + "OpImageGather": 96, + "OpImageDrefGather": 97, + "OpImageRead": 98, + "OpImageWrite": 99, + "OpImage": 100, + "OpImageQueryFormat": 101, + "OpImageQueryOrder": 102, + "OpImageQuerySizeLod": 103, + "OpImageQuerySize": 104, + "OpImageQueryLod": 105, + "OpImageQueryLevels": 106, + "OpImageQuerySamples": 107, + "OpConvertFToU": 109, + "OpConvertFToS": 110, + "OpConvertSToF": 111, + "OpConvertUToF": 112, + "OpUConvert": 113, + "OpSConvert": 114, + "OpFConvert": 115, + "OpQuantizeToF16": 116, + "OpConvertPtrToU": 117, + "OpSatConvertSToU": 118, + "OpSatConvertUToS": 119, + "OpConvertUToPtr": 120, + "OpPtrCastToGeneric": 121, + "OpGenericCastToPtr": 122, + "OpGenericCastToPtrExplicit": 123, + "OpBitcast": 124, + "OpSNegate": 126, + "OpFNegate": 127, + "OpIAdd": 128, + "OpFAdd": 129, + "OpISub": 130, + "OpFSub": 131, + "OpIMul": 132, + "OpFMul": 133, + "OpUDiv": 134, + "OpSDiv": 135, + "OpFDiv": 136, + "OpUMod": 137, + "OpSRem": 138, + "OpSMod": 139, + "OpFRem": 140, + "OpFMod": 141, + "OpVectorTimesScalar": 142, + "OpMatrixTimesScalar": 143, + "OpVectorTimesMatrix": 144, + "OpMatrixTimesVector": 145, + "OpMatrixTimesMatrix": 146, + "OpOuterProduct": 147, + "OpDot": 148, + "OpIAddCarry": 149, + "OpISubBorrow": 150, + "OpUMulExtended": 151, + "OpSMulExtended": 152, + "OpAny": 154, + "OpAll": 155, + "OpIsNan": 156, + "OpIsInf": 157, + "OpIsFinite": 158, + "OpIsNormal": 159, + "OpSignBitSet": 160, + "OpLessOrGreater": 161, + "OpOrdered": 162, + "OpUnordered": 163, + "OpLogicalEqual": 164, + "OpLogicalNotEqual": 165, + "OpLogicalOr": 166, + "OpLogicalAnd": 167, + "OpLogicalNot": 168, + "OpSelect": 169, + "OpIEqual": 170, + "OpINotEqual": 171, + "OpUGreaterThan": 172, + "OpSGreaterThan": 173, + "OpUGreaterThanEqual": 174, + "OpSGreaterThanEqual": 175, + "OpULessThan": 176, + "OpSLessThan": 177, + "OpULessThanEqual": 178, + "OpSLessThanEqual": 179, + "OpFOrdEqual": 180, + "OpFUnordEqual": 181, + "OpFOrdNotEqual": 182, + "OpFUnordNotEqual": 183, + "OpFOrdLessThan": 184, + "OpFUnordLessThan": 185, + "OpFOrdGreaterThan": 186, + "OpFUnordGreaterThan": 187, + "OpFOrdLessThanEqual": 188, + "OpFUnordLessThanEqual": 189, + "OpFOrdGreaterThanEqual": 190, + "OpFUnordGreaterThanEqual": 191, + "OpShiftRightLogical": 194, + "OpShiftRightArithmetic": 195, + "OpShiftLeftLogical": 196, + "OpBitwiseOr": 197, + "OpBitwiseXor": 198, + "OpBitwiseAnd": 199, + "OpNot": 200, + "OpBitFieldInsert": 201, + "OpBitFieldSExtract": 202, + "OpBitFieldUExtract": 203, + "OpBitReverse": 204, + "OpBitCount": 205, + "OpDPdx": 207, + "OpDPdy": 208, + "OpFwidth": 209, + "OpDPdxFine": 210, + "OpDPdyFine": 211, + "OpFwidthFine": 212, + "OpDPdxCoarse": 213, + "OpDPdyCoarse": 214, + "OpFwidthCoarse": 215, + "OpEmitVertex": 218, + "OpEndPrimitive": 219, + "OpEmitStreamVertex": 220, + "OpEndStreamPrimitive": 221, + "OpControlBarrier": 224, + "OpMemoryBarrier": 225, + "OpAtomicLoad": 227, + "OpAtomicStore": 228, + "OpAtomicExchange": 229, + "OpAtomicCompareExchange": 230, + "OpAtomicCompareExchangeWeak": 231, + "OpAtomicIIncrement": 232, + "OpAtomicIDecrement": 233, + "OpAtomicIAdd": 234, + "OpAtomicISub": 235, + "OpAtomicSMin": 236, + "OpAtomicUMin": 237, + "OpAtomicSMax": 238, + "OpAtomicUMax": 239, + "OpAtomicAnd": 240, + "OpAtomicOr": 241, + "OpAtomicXor": 242, + "OpPhi": 245, + "OpLoopMerge": 246, + "OpSelectionMerge": 247, + "OpLabel": 248, + "OpBranch": 249, + "OpBranchConditional": 250, + "OpSwitch": 251, + "OpKill": 252, + "OpReturn": 253, + "OpReturnValue": 254, + "OpUnreachable": 255, + "OpLifetimeStart": 256, + "OpLifetimeStop": 257, + "OpGroupAsyncCopy": 259, + "OpGroupWaitEvents": 260, + "OpGroupAll": 261, + "OpGroupAny": 262, + "OpGroupBroadcast": 263, + "OpGroupIAdd": 264, + "OpGroupFAdd": 265, + "OpGroupFMin": 266, + "OpGroupUMin": 267, + "OpGroupSMin": 268, + "OpGroupFMax": 269, + "OpGroupUMax": 270, + "OpGroupSMax": 271, + "OpReadPipe": 274, + "OpWritePipe": 275, + "OpReservedReadPipe": 276, + "OpReservedWritePipe": 277, + "OpReserveReadPipePackets": 278, + "OpReserveWritePipePackets": 279, + "OpCommitReadPipe": 280, + "OpCommitWritePipe": 281, + "OpIsValidReserveId": 282, + "OpGetNumPipePackets": 283, + "OpGetMaxPipePackets": 284, + "OpGroupReserveReadPipePackets": 285, + "OpGroupReserveWritePipePackets": 286, + "OpGroupCommitReadPipe": 287, + "OpGroupCommitWritePipe": 288, + "OpEnqueueMarker": 291, + "OpEnqueueKernel": 292, + "OpGetKernelNDrangeSubGroupCount": 293, + "OpGetKernelNDrangeMaxSubGroupSize": 294, + "OpGetKernelWorkGroupSize": 295, + "OpGetKernelPreferredWorkGroupSizeMultiple": 296, + "OpRetainEvent": 297, + "OpReleaseEvent": 298, + "OpCreateUserEvent": 299, + "OpIsValidEvent": 300, + "OpSetUserEventStatus": 301, + "OpCaptureEventProfilingInfo": 302, + "OpGetDefaultQueue": 303, + "OpBuildNDRange": 304, + "OpImageSparseSampleImplicitLod": 305, + "OpImageSparseSampleExplicitLod": 306, + "OpImageSparseSampleDrefImplicitLod": 307, + "OpImageSparseSampleDrefExplicitLod": 308, + "OpImageSparseSampleProjImplicitLod": 309, + "OpImageSparseSampleProjExplicitLod": 310, + "OpImageSparseSampleProjDrefImplicitLod": 311, + "OpImageSparseSampleProjDrefExplicitLod": 312, + "OpImageSparseFetch": 313, + "OpImageSparseGather": 314, + "OpImageSparseDrefGather": 315, + "OpImageSparseTexelsResident": 316, + "OpNoLine": 317, + "OpAtomicFlagTestAndSet": 318, + "OpAtomicFlagClear": 319, + "OpImageSparseRead": 320, + "OpDecorateId": 332, + "OpSubgroupBallotKHR": 4421, + "OpSubgroupFirstInvocationKHR": 4422, + "OpSubgroupAllKHR": 4428, + "OpSubgroupAnyKHR": 4429, + "OpSubgroupAllEqualKHR": 4430, + "OpSubgroupReadInvocationKHR": 4432, + "OpGroupIAddNonUniformAMD": 5000, + "OpGroupFAddNonUniformAMD": 5001, + "OpGroupFMinNonUniformAMD": 5002, + "OpGroupUMinNonUniformAMD": 5003, + "OpGroupSMinNonUniformAMD": 5004, + "OpGroupFMaxNonUniformAMD": 5005, + "OpGroupUMaxNonUniformAMD": 5006, + "OpGroupSMaxNonUniformAMD": 5007, + "OpFragmentMaskFetchAMD": 5011, + "OpFragmentFetchAMD": 5012, + "OpSubgroupShuffleINTEL": 5571, + "OpSubgroupShuffleDownINTEL": 5572, + "OpSubgroupShuffleUpINTEL": 5573, + "OpSubgroupShuffleXorINTEL": 5574, + "OpSubgroupBlockReadINTEL": 5575, + "OpSubgroupBlockWriteINTEL": 5576, + "OpSubgroupImageBlockReadINTEL": 5577, + "OpSubgroupImageBlockWriteINTEL": 5578, + "OpDecorateStringGOOGLE": 5632, + "OpMemberDecorateStringGOOGLE": 5633 + } + } + ] + } +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.lua b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.lua new file mode 100644 index 0000000..2bd33ba --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.lua @@ -0,0 +1,949 @@ +-- Copyright (c) 2014-2018 The Khronos Group Inc. +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and/or associated documentation files (the "Materials"), +-- to deal in the Materials without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Materials, and to permit persons to whom the +-- Materials are furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Materials. +-- +-- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +-- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +-- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +-- +-- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +-- IN THE MATERIALS. + +-- This header is automatically generated by the same tool that creates +-- the Binary Section of the SPIR-V specification. + +-- Enumeration tokens for SPIR-V, in various styles: +-- C, C++, C++11, JSON, Lua, Python +-- +-- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +-- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +-- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +-- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +-- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +-- +-- Some tokens act like mask values, which can be OR'd together, +-- while others are mutually exclusive. The mask-like ones have +-- "Mask" in their name, and a parallel enum that has the shift +-- amount (1 << x) for each corresponding enumerant. + +spv = { + MagicNumber = 0x07230203, + Version = 0x00010000, + Revision = 12, + OpCodeMask = 0xffff, + WordCountShift = 16, + + SourceLanguage = { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + }, + + ExecutionModel = { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + }, + + AddressingModel = { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + }, + + MemoryModel = { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + }, + + ExecutionMode = { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + }, + + StorageClass = { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + }, + + Dim = { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + }, + + SamplerAddressingMode = { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + }, + + SamplerFilterMode = { + Nearest = 0, + Linear = 1, + }, + + ImageFormat = { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + }, + + ImageChannelOrder = { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + }, + + ImageChannelDataType = { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + }, + + ImageOperandsShift = { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + }, + + ImageOperandsMask = { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + }, + + FPFastMathModeShift = { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + }, + + FPFastMathModeMask = { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + }, + + FPRoundingMode = { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + }, + + LinkageType = { + Export = 0, + Import = 1, + }, + + AccessQualifier = { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + }, + + FunctionParameterAttribute = { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + }, + + Decoration = { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + }, + + BuiltIn = { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + }, + + SelectionControlShift = { + Flatten = 0, + DontFlatten = 1, + }, + + SelectionControlMask = { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + }, + + LoopControlShift = { + Unroll = 0, + DontUnroll = 1, + }, + + LoopControlMask = { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + }, + + FunctionControlShift = { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + }, + + FunctionControlMask = { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + }, + + MemorySemanticsShift = { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + }, + + MemorySemanticsMask = { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + }, + + MemoryAccessShift = { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + }, + + MemoryAccessMask = { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + }, + + Scope = { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + }, + + GroupOperation = { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + }, + + KernelEnqueueFlags = { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + }, + + KernelProfilingInfoShift = { + CmdExecTime = 0, + }, + + KernelProfilingInfoMask = { + MaskNone = 0, + CmdExecTime = 0x00000001, + }, + + Capability = { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + }, + + Op = { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + }, + +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.py b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.py new file mode 100644 index 0000000..8a200e7 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.0/spirv.py @@ -0,0 +1,949 @@ +# Copyright (c) 2014-2018 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +# This header is automatically generated by the same tool that creates +# the Binary Section of the SPIR-V specification. + +# Enumeration tokens for SPIR-V, in various styles: +# C, C++, C++11, JSON, Lua, Python +# +# - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +# - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +# - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +# - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +# - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +# +# Some tokens act like mask values, which can be OR'd together, +# while others are mutually exclusive. The mask-like ones have +# "Mask" in their name, and a parallel enum that has the shift +# amount (1 << x) for each corresponding enumerant. + +spv = { + 'MagicNumber' : 0x07230203, + 'Version' : 0x00010000, + 'Revision' : 12, + 'OpCodeMask' : 0xffff, + 'WordCountShift' : 16, + + 'SourceLanguage' : { + 'Unknown' : 0, + 'ESSL' : 1, + 'GLSL' : 2, + 'OpenCL_C' : 3, + 'OpenCL_CPP' : 4, + 'HLSL' : 5, + }, + + 'ExecutionModel' : { + 'Vertex' : 0, + 'TessellationControl' : 1, + 'TessellationEvaluation' : 2, + 'Geometry' : 3, + 'Fragment' : 4, + 'GLCompute' : 5, + 'Kernel' : 6, + }, + + 'AddressingModel' : { + 'Logical' : 0, + 'Physical32' : 1, + 'Physical64' : 2, + }, + + 'MemoryModel' : { + 'Simple' : 0, + 'GLSL450' : 1, + 'OpenCL' : 2, + }, + + 'ExecutionMode' : { + 'Invocations' : 0, + 'SpacingEqual' : 1, + 'SpacingFractionalEven' : 2, + 'SpacingFractionalOdd' : 3, + 'VertexOrderCw' : 4, + 'VertexOrderCcw' : 5, + 'PixelCenterInteger' : 6, + 'OriginUpperLeft' : 7, + 'OriginLowerLeft' : 8, + 'EarlyFragmentTests' : 9, + 'PointMode' : 10, + 'Xfb' : 11, + 'DepthReplacing' : 12, + 'DepthGreater' : 14, + 'DepthLess' : 15, + 'DepthUnchanged' : 16, + 'LocalSize' : 17, + 'LocalSizeHint' : 18, + 'InputPoints' : 19, + 'InputLines' : 20, + 'InputLinesAdjacency' : 21, + 'Triangles' : 22, + 'InputTrianglesAdjacency' : 23, + 'Quads' : 24, + 'Isolines' : 25, + 'OutputVertices' : 26, + 'OutputPoints' : 27, + 'OutputLineStrip' : 28, + 'OutputTriangleStrip' : 29, + 'VecTypeHint' : 30, + 'ContractionOff' : 31, + 'PostDepthCoverage' : 4446, + 'StencilRefReplacingEXT' : 5027, + }, + + 'StorageClass' : { + 'UniformConstant' : 0, + 'Input' : 1, + 'Uniform' : 2, + 'Output' : 3, + 'Workgroup' : 4, + 'CrossWorkgroup' : 5, + 'Private' : 6, + 'Function' : 7, + 'Generic' : 8, + 'PushConstant' : 9, + 'AtomicCounter' : 10, + 'Image' : 11, + 'StorageBuffer' : 12, + }, + + 'Dim' : { + 'Dim1D' : 0, + 'Dim2D' : 1, + 'Dim3D' : 2, + 'Cube' : 3, + 'Rect' : 4, + 'Buffer' : 5, + 'SubpassData' : 6, + }, + + 'SamplerAddressingMode' : { + 'None' : 0, + 'ClampToEdge' : 1, + 'Clamp' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'SamplerFilterMode' : { + 'Nearest' : 0, + 'Linear' : 1, + }, + + 'ImageFormat' : { + 'Unknown' : 0, + 'Rgba32f' : 1, + 'Rgba16f' : 2, + 'R32f' : 3, + 'Rgba8' : 4, + 'Rgba8Snorm' : 5, + 'Rg32f' : 6, + 'Rg16f' : 7, + 'R11fG11fB10f' : 8, + 'R16f' : 9, + 'Rgba16' : 10, + 'Rgb10A2' : 11, + 'Rg16' : 12, + 'Rg8' : 13, + 'R16' : 14, + 'R8' : 15, + 'Rgba16Snorm' : 16, + 'Rg16Snorm' : 17, + 'Rg8Snorm' : 18, + 'R16Snorm' : 19, + 'R8Snorm' : 20, + 'Rgba32i' : 21, + 'Rgba16i' : 22, + 'Rgba8i' : 23, + 'R32i' : 24, + 'Rg32i' : 25, + 'Rg16i' : 26, + 'Rg8i' : 27, + 'R16i' : 28, + 'R8i' : 29, + 'Rgba32ui' : 30, + 'Rgba16ui' : 31, + 'Rgba8ui' : 32, + 'R32ui' : 33, + 'Rgb10a2ui' : 34, + 'Rg32ui' : 35, + 'Rg16ui' : 36, + 'Rg8ui' : 37, + 'R16ui' : 38, + 'R8ui' : 39, + }, + + 'ImageChannelOrder' : { + 'R' : 0, + 'A' : 1, + 'RG' : 2, + 'RA' : 3, + 'RGB' : 4, + 'RGBA' : 5, + 'BGRA' : 6, + 'ARGB' : 7, + 'Intensity' : 8, + 'Luminance' : 9, + 'Rx' : 10, + 'RGx' : 11, + 'RGBx' : 12, + 'Depth' : 13, + 'DepthStencil' : 14, + 'sRGB' : 15, + 'sRGBx' : 16, + 'sRGBA' : 17, + 'sBGRA' : 18, + 'ABGR' : 19, + }, + + 'ImageChannelDataType' : { + 'SnormInt8' : 0, + 'SnormInt16' : 1, + 'UnormInt8' : 2, + 'UnormInt16' : 3, + 'UnormShort565' : 4, + 'UnormShort555' : 5, + 'UnormInt101010' : 6, + 'SignedInt8' : 7, + 'SignedInt16' : 8, + 'SignedInt32' : 9, + 'UnsignedInt8' : 10, + 'UnsignedInt16' : 11, + 'UnsignedInt32' : 12, + 'HalfFloat' : 13, + 'Float' : 14, + 'UnormInt24' : 15, + 'UnormInt101010_2' : 16, + }, + + 'ImageOperandsShift' : { + 'Bias' : 0, + 'Lod' : 1, + 'Grad' : 2, + 'ConstOffset' : 3, + 'Offset' : 4, + 'ConstOffsets' : 5, + 'Sample' : 6, + 'MinLod' : 7, + }, + + 'ImageOperandsMask' : { + 'MaskNone' : 0, + 'Bias' : 0x00000001, + 'Lod' : 0x00000002, + 'Grad' : 0x00000004, + 'ConstOffset' : 0x00000008, + 'Offset' : 0x00000010, + 'ConstOffsets' : 0x00000020, + 'Sample' : 0x00000040, + 'MinLod' : 0x00000080, + }, + + 'FPFastMathModeShift' : { + 'NotNaN' : 0, + 'NotInf' : 1, + 'NSZ' : 2, + 'AllowRecip' : 3, + 'Fast' : 4, + }, + + 'FPFastMathModeMask' : { + 'MaskNone' : 0, + 'NotNaN' : 0x00000001, + 'NotInf' : 0x00000002, + 'NSZ' : 0x00000004, + 'AllowRecip' : 0x00000008, + 'Fast' : 0x00000010, + }, + + 'FPRoundingMode' : { + 'RTE' : 0, + 'RTZ' : 1, + 'RTP' : 2, + 'RTN' : 3, + }, + + 'LinkageType' : { + 'Export' : 0, + 'Import' : 1, + }, + + 'AccessQualifier' : { + 'ReadOnly' : 0, + 'WriteOnly' : 1, + 'ReadWrite' : 2, + }, + + 'FunctionParameterAttribute' : { + 'Zext' : 0, + 'Sext' : 1, + 'ByVal' : 2, + 'Sret' : 3, + 'NoAlias' : 4, + 'NoCapture' : 5, + 'NoWrite' : 6, + 'NoReadWrite' : 7, + }, + + 'Decoration' : { + 'RelaxedPrecision' : 0, + 'SpecId' : 1, + 'Block' : 2, + 'BufferBlock' : 3, + 'RowMajor' : 4, + 'ColMajor' : 5, + 'ArrayStride' : 6, + 'MatrixStride' : 7, + 'GLSLShared' : 8, + 'GLSLPacked' : 9, + 'CPacked' : 10, + 'BuiltIn' : 11, + 'NoPerspective' : 13, + 'Flat' : 14, + 'Patch' : 15, + 'Centroid' : 16, + 'Sample' : 17, + 'Invariant' : 18, + 'Restrict' : 19, + 'Aliased' : 20, + 'Volatile' : 21, + 'Constant' : 22, + 'Coherent' : 23, + 'NonWritable' : 24, + 'NonReadable' : 25, + 'Uniform' : 26, + 'SaturatedConversion' : 28, + 'Stream' : 29, + 'Location' : 30, + 'Component' : 31, + 'Index' : 32, + 'Binding' : 33, + 'DescriptorSet' : 34, + 'Offset' : 35, + 'XfbBuffer' : 36, + 'XfbStride' : 37, + 'FuncParamAttr' : 38, + 'FPRoundingMode' : 39, + 'FPFastMathMode' : 40, + 'LinkageAttributes' : 41, + 'NoContraction' : 42, + 'InputAttachmentIndex' : 43, + 'Alignment' : 44, + 'ExplicitInterpAMD' : 4999, + 'OverrideCoverageNV' : 5248, + 'PassthroughNV' : 5250, + 'ViewportRelativeNV' : 5252, + 'SecondaryViewportRelativeNV' : 5256, + 'HlslCounterBufferGOOGLE' : 5634, + 'HlslSemanticGOOGLE' : 5635, + }, + + 'BuiltIn' : { + 'Position' : 0, + 'PointSize' : 1, + 'ClipDistance' : 3, + 'CullDistance' : 4, + 'VertexId' : 5, + 'InstanceId' : 6, + 'PrimitiveId' : 7, + 'InvocationId' : 8, + 'Layer' : 9, + 'ViewportIndex' : 10, + 'TessLevelOuter' : 11, + 'TessLevelInner' : 12, + 'TessCoord' : 13, + 'PatchVertices' : 14, + 'FragCoord' : 15, + 'PointCoord' : 16, + 'FrontFacing' : 17, + 'SampleId' : 18, + 'SamplePosition' : 19, + 'SampleMask' : 20, + 'FragDepth' : 22, + 'HelperInvocation' : 23, + 'NumWorkgroups' : 24, + 'WorkgroupSize' : 25, + 'WorkgroupId' : 26, + 'LocalInvocationId' : 27, + 'GlobalInvocationId' : 28, + 'LocalInvocationIndex' : 29, + 'WorkDim' : 30, + 'GlobalSize' : 31, + 'EnqueuedWorkgroupSize' : 32, + 'GlobalOffset' : 33, + 'GlobalLinearId' : 34, + 'SubgroupSize' : 36, + 'SubgroupMaxSize' : 37, + 'NumSubgroups' : 38, + 'NumEnqueuedSubgroups' : 39, + 'SubgroupId' : 40, + 'SubgroupLocalInvocationId' : 41, + 'VertexIndex' : 42, + 'InstanceIndex' : 43, + 'SubgroupEqMaskKHR' : 4416, + 'SubgroupGeMaskKHR' : 4417, + 'SubgroupGtMaskKHR' : 4418, + 'SubgroupLeMaskKHR' : 4419, + 'SubgroupLtMaskKHR' : 4420, + 'BaseVertex' : 4424, + 'BaseInstance' : 4425, + 'DrawIndex' : 4426, + 'DeviceIndex' : 4438, + 'ViewIndex' : 4440, + 'BaryCoordNoPerspAMD' : 4992, + 'BaryCoordNoPerspCentroidAMD' : 4993, + 'BaryCoordNoPerspSampleAMD' : 4994, + 'BaryCoordSmoothAMD' : 4995, + 'BaryCoordSmoothCentroidAMD' : 4996, + 'BaryCoordSmoothSampleAMD' : 4997, + 'BaryCoordPullModelAMD' : 4998, + 'FragStencilRefEXT' : 5014, + 'ViewportMaskNV' : 5253, + 'SecondaryPositionNV' : 5257, + 'SecondaryViewportMaskNV' : 5258, + 'PositionPerViewNV' : 5261, + 'ViewportMaskPerViewNV' : 5262, + }, + + 'SelectionControlShift' : { + 'Flatten' : 0, + 'DontFlatten' : 1, + }, + + 'SelectionControlMask' : { + 'MaskNone' : 0, + 'Flatten' : 0x00000001, + 'DontFlatten' : 0x00000002, + }, + + 'LoopControlShift' : { + 'Unroll' : 0, + 'DontUnroll' : 1, + }, + + 'LoopControlMask' : { + 'MaskNone' : 0, + 'Unroll' : 0x00000001, + 'DontUnroll' : 0x00000002, + }, + + 'FunctionControlShift' : { + 'Inline' : 0, + 'DontInline' : 1, + 'Pure' : 2, + 'Const' : 3, + }, + + 'FunctionControlMask' : { + 'MaskNone' : 0, + 'Inline' : 0x00000001, + 'DontInline' : 0x00000002, + 'Pure' : 0x00000004, + 'Const' : 0x00000008, + }, + + 'MemorySemanticsShift' : { + 'Acquire' : 1, + 'Release' : 2, + 'AcquireRelease' : 3, + 'SequentiallyConsistent' : 4, + 'UniformMemory' : 6, + 'SubgroupMemory' : 7, + 'WorkgroupMemory' : 8, + 'CrossWorkgroupMemory' : 9, + 'AtomicCounterMemory' : 10, + 'ImageMemory' : 11, + }, + + 'MemorySemanticsMask' : { + 'MaskNone' : 0, + 'Acquire' : 0x00000002, + 'Release' : 0x00000004, + 'AcquireRelease' : 0x00000008, + 'SequentiallyConsistent' : 0x00000010, + 'UniformMemory' : 0x00000040, + 'SubgroupMemory' : 0x00000080, + 'WorkgroupMemory' : 0x00000100, + 'CrossWorkgroupMemory' : 0x00000200, + 'AtomicCounterMemory' : 0x00000400, + 'ImageMemory' : 0x00000800, + }, + + 'MemoryAccessShift' : { + 'Volatile' : 0, + 'Aligned' : 1, + 'Nontemporal' : 2, + }, + + 'MemoryAccessMask' : { + 'MaskNone' : 0, + 'Volatile' : 0x00000001, + 'Aligned' : 0x00000002, + 'Nontemporal' : 0x00000004, + }, + + 'Scope' : { + 'CrossDevice' : 0, + 'Device' : 1, + 'Workgroup' : 2, + 'Subgroup' : 3, + 'Invocation' : 4, + }, + + 'GroupOperation' : { + 'Reduce' : 0, + 'InclusiveScan' : 1, + 'ExclusiveScan' : 2, + }, + + 'KernelEnqueueFlags' : { + 'NoWait' : 0, + 'WaitKernel' : 1, + 'WaitWorkGroup' : 2, + }, + + 'KernelProfilingInfoShift' : { + 'CmdExecTime' : 0, + }, + + 'KernelProfilingInfoMask' : { + 'MaskNone' : 0, + 'CmdExecTime' : 0x00000001, + }, + + 'Capability' : { + 'Matrix' : 0, + 'Shader' : 1, + 'Geometry' : 2, + 'Tessellation' : 3, + 'Addresses' : 4, + 'Linkage' : 5, + 'Kernel' : 6, + 'Vector16' : 7, + 'Float16Buffer' : 8, + 'Float16' : 9, + 'Float64' : 10, + 'Int64' : 11, + 'Int64Atomics' : 12, + 'ImageBasic' : 13, + 'ImageReadWrite' : 14, + 'ImageMipmap' : 15, + 'Pipes' : 17, + 'Groups' : 18, + 'DeviceEnqueue' : 19, + 'LiteralSampler' : 20, + 'AtomicStorage' : 21, + 'Int16' : 22, + 'TessellationPointSize' : 23, + 'GeometryPointSize' : 24, + 'ImageGatherExtended' : 25, + 'StorageImageMultisample' : 27, + 'UniformBufferArrayDynamicIndexing' : 28, + 'SampledImageArrayDynamicIndexing' : 29, + 'StorageBufferArrayDynamicIndexing' : 30, + 'StorageImageArrayDynamicIndexing' : 31, + 'ClipDistance' : 32, + 'CullDistance' : 33, + 'ImageCubeArray' : 34, + 'SampleRateShading' : 35, + 'ImageRect' : 36, + 'SampledRect' : 37, + 'GenericPointer' : 38, + 'Int8' : 39, + 'InputAttachment' : 40, + 'SparseResidency' : 41, + 'MinLod' : 42, + 'Sampled1D' : 43, + 'Image1D' : 44, + 'SampledCubeArray' : 45, + 'SampledBuffer' : 46, + 'ImageBuffer' : 47, + 'ImageMSArray' : 48, + 'StorageImageExtendedFormats' : 49, + 'ImageQuery' : 50, + 'DerivativeControl' : 51, + 'InterpolationFunction' : 52, + 'TransformFeedback' : 53, + 'GeometryStreams' : 54, + 'StorageImageReadWithoutFormat' : 55, + 'StorageImageWriteWithoutFormat' : 56, + 'MultiViewport' : 57, + 'SubgroupBallotKHR' : 4423, + 'DrawParameters' : 4427, + 'SubgroupVoteKHR' : 4431, + 'StorageBuffer16BitAccess' : 4433, + 'StorageUniformBufferBlock16' : 4433, + 'StorageUniform16' : 4434, + 'UniformAndStorageBuffer16BitAccess' : 4434, + 'StoragePushConstant16' : 4435, + 'StorageInputOutput16' : 4436, + 'DeviceGroup' : 4437, + 'MultiView' : 4439, + 'VariablePointersStorageBuffer' : 4441, + 'VariablePointers' : 4442, + 'AtomicStorageOps' : 4445, + 'SampleMaskPostDepthCoverage' : 4447, + 'ImageGatherBiasLodAMD' : 5009, + 'FragmentMaskAMD' : 5010, + 'StencilExportEXT' : 5013, + 'ImageReadWriteLodAMD' : 5015, + 'SampleMaskOverrideCoverageNV' : 5249, + 'GeometryShaderPassthroughNV' : 5251, + 'ShaderViewportIndexLayerEXT' : 5254, + 'ShaderViewportIndexLayerNV' : 5254, + 'ShaderViewportMaskNV' : 5255, + 'ShaderStereoViewNV' : 5259, + 'PerViewAttributesNV' : 5260, + 'SubgroupShuffleINTEL' : 5568, + 'SubgroupBufferBlockIOINTEL' : 5569, + 'SubgroupImageBlockIOINTEL' : 5570, + }, + + 'Op' : { + 'OpNop' : 0, + 'OpUndef' : 1, + 'OpSourceContinued' : 2, + 'OpSource' : 3, + 'OpSourceExtension' : 4, + 'OpName' : 5, + 'OpMemberName' : 6, + 'OpString' : 7, + 'OpLine' : 8, + 'OpExtension' : 10, + 'OpExtInstImport' : 11, + 'OpExtInst' : 12, + 'OpMemoryModel' : 14, + 'OpEntryPoint' : 15, + 'OpExecutionMode' : 16, + 'OpCapability' : 17, + 'OpTypeVoid' : 19, + 'OpTypeBool' : 20, + 'OpTypeInt' : 21, + 'OpTypeFloat' : 22, + 'OpTypeVector' : 23, + 'OpTypeMatrix' : 24, + 'OpTypeImage' : 25, + 'OpTypeSampler' : 26, + 'OpTypeSampledImage' : 27, + 'OpTypeArray' : 28, + 'OpTypeRuntimeArray' : 29, + 'OpTypeStruct' : 30, + 'OpTypeOpaque' : 31, + 'OpTypePointer' : 32, + 'OpTypeFunction' : 33, + 'OpTypeEvent' : 34, + 'OpTypeDeviceEvent' : 35, + 'OpTypeReserveId' : 36, + 'OpTypeQueue' : 37, + 'OpTypePipe' : 38, + 'OpTypeForwardPointer' : 39, + 'OpConstantTrue' : 41, + 'OpConstantFalse' : 42, + 'OpConstant' : 43, + 'OpConstantComposite' : 44, + 'OpConstantSampler' : 45, + 'OpConstantNull' : 46, + 'OpSpecConstantTrue' : 48, + 'OpSpecConstantFalse' : 49, + 'OpSpecConstant' : 50, + 'OpSpecConstantComposite' : 51, + 'OpSpecConstantOp' : 52, + 'OpFunction' : 54, + 'OpFunctionParameter' : 55, + 'OpFunctionEnd' : 56, + 'OpFunctionCall' : 57, + 'OpVariable' : 59, + 'OpImageTexelPointer' : 60, + 'OpLoad' : 61, + 'OpStore' : 62, + 'OpCopyMemory' : 63, + 'OpCopyMemorySized' : 64, + 'OpAccessChain' : 65, + 'OpInBoundsAccessChain' : 66, + 'OpPtrAccessChain' : 67, + 'OpArrayLength' : 68, + 'OpGenericPtrMemSemantics' : 69, + 'OpInBoundsPtrAccessChain' : 70, + 'OpDecorate' : 71, + 'OpMemberDecorate' : 72, + 'OpDecorationGroup' : 73, + 'OpGroupDecorate' : 74, + 'OpGroupMemberDecorate' : 75, + 'OpVectorExtractDynamic' : 77, + 'OpVectorInsertDynamic' : 78, + 'OpVectorShuffle' : 79, + 'OpCompositeConstruct' : 80, + 'OpCompositeExtract' : 81, + 'OpCompositeInsert' : 82, + 'OpCopyObject' : 83, + 'OpTranspose' : 84, + 'OpSampledImage' : 86, + 'OpImageSampleImplicitLod' : 87, + 'OpImageSampleExplicitLod' : 88, + 'OpImageSampleDrefImplicitLod' : 89, + 'OpImageSampleDrefExplicitLod' : 90, + 'OpImageSampleProjImplicitLod' : 91, + 'OpImageSampleProjExplicitLod' : 92, + 'OpImageSampleProjDrefImplicitLod' : 93, + 'OpImageSampleProjDrefExplicitLod' : 94, + 'OpImageFetch' : 95, + 'OpImageGather' : 96, + 'OpImageDrefGather' : 97, + 'OpImageRead' : 98, + 'OpImageWrite' : 99, + 'OpImage' : 100, + 'OpImageQueryFormat' : 101, + 'OpImageQueryOrder' : 102, + 'OpImageQuerySizeLod' : 103, + 'OpImageQuerySize' : 104, + 'OpImageQueryLod' : 105, + 'OpImageQueryLevels' : 106, + 'OpImageQuerySamples' : 107, + 'OpConvertFToU' : 109, + 'OpConvertFToS' : 110, + 'OpConvertSToF' : 111, + 'OpConvertUToF' : 112, + 'OpUConvert' : 113, + 'OpSConvert' : 114, + 'OpFConvert' : 115, + 'OpQuantizeToF16' : 116, + 'OpConvertPtrToU' : 117, + 'OpSatConvertSToU' : 118, + 'OpSatConvertUToS' : 119, + 'OpConvertUToPtr' : 120, + 'OpPtrCastToGeneric' : 121, + 'OpGenericCastToPtr' : 122, + 'OpGenericCastToPtrExplicit' : 123, + 'OpBitcast' : 124, + 'OpSNegate' : 126, + 'OpFNegate' : 127, + 'OpIAdd' : 128, + 'OpFAdd' : 129, + 'OpISub' : 130, + 'OpFSub' : 131, + 'OpIMul' : 132, + 'OpFMul' : 133, + 'OpUDiv' : 134, + 'OpSDiv' : 135, + 'OpFDiv' : 136, + 'OpUMod' : 137, + 'OpSRem' : 138, + 'OpSMod' : 139, + 'OpFRem' : 140, + 'OpFMod' : 141, + 'OpVectorTimesScalar' : 142, + 'OpMatrixTimesScalar' : 143, + 'OpVectorTimesMatrix' : 144, + 'OpMatrixTimesVector' : 145, + 'OpMatrixTimesMatrix' : 146, + 'OpOuterProduct' : 147, + 'OpDot' : 148, + 'OpIAddCarry' : 149, + 'OpISubBorrow' : 150, + 'OpUMulExtended' : 151, + 'OpSMulExtended' : 152, + 'OpAny' : 154, + 'OpAll' : 155, + 'OpIsNan' : 156, + 'OpIsInf' : 157, + 'OpIsFinite' : 158, + 'OpIsNormal' : 159, + 'OpSignBitSet' : 160, + 'OpLessOrGreater' : 161, + 'OpOrdered' : 162, + 'OpUnordered' : 163, + 'OpLogicalEqual' : 164, + 'OpLogicalNotEqual' : 165, + 'OpLogicalOr' : 166, + 'OpLogicalAnd' : 167, + 'OpLogicalNot' : 168, + 'OpSelect' : 169, + 'OpIEqual' : 170, + 'OpINotEqual' : 171, + 'OpUGreaterThan' : 172, + 'OpSGreaterThan' : 173, + 'OpUGreaterThanEqual' : 174, + 'OpSGreaterThanEqual' : 175, + 'OpULessThan' : 176, + 'OpSLessThan' : 177, + 'OpULessThanEqual' : 178, + 'OpSLessThanEqual' : 179, + 'OpFOrdEqual' : 180, + 'OpFUnordEqual' : 181, + 'OpFOrdNotEqual' : 182, + 'OpFUnordNotEqual' : 183, + 'OpFOrdLessThan' : 184, + 'OpFUnordLessThan' : 185, + 'OpFOrdGreaterThan' : 186, + 'OpFUnordGreaterThan' : 187, + 'OpFOrdLessThanEqual' : 188, + 'OpFUnordLessThanEqual' : 189, + 'OpFOrdGreaterThanEqual' : 190, + 'OpFUnordGreaterThanEqual' : 191, + 'OpShiftRightLogical' : 194, + 'OpShiftRightArithmetic' : 195, + 'OpShiftLeftLogical' : 196, + 'OpBitwiseOr' : 197, + 'OpBitwiseXor' : 198, + 'OpBitwiseAnd' : 199, + 'OpNot' : 200, + 'OpBitFieldInsert' : 201, + 'OpBitFieldSExtract' : 202, + 'OpBitFieldUExtract' : 203, + 'OpBitReverse' : 204, + 'OpBitCount' : 205, + 'OpDPdx' : 207, + 'OpDPdy' : 208, + 'OpFwidth' : 209, + 'OpDPdxFine' : 210, + 'OpDPdyFine' : 211, + 'OpFwidthFine' : 212, + 'OpDPdxCoarse' : 213, + 'OpDPdyCoarse' : 214, + 'OpFwidthCoarse' : 215, + 'OpEmitVertex' : 218, + 'OpEndPrimitive' : 219, + 'OpEmitStreamVertex' : 220, + 'OpEndStreamPrimitive' : 221, + 'OpControlBarrier' : 224, + 'OpMemoryBarrier' : 225, + 'OpAtomicLoad' : 227, + 'OpAtomicStore' : 228, + 'OpAtomicExchange' : 229, + 'OpAtomicCompareExchange' : 230, + 'OpAtomicCompareExchangeWeak' : 231, + 'OpAtomicIIncrement' : 232, + 'OpAtomicIDecrement' : 233, + 'OpAtomicIAdd' : 234, + 'OpAtomicISub' : 235, + 'OpAtomicSMin' : 236, + 'OpAtomicUMin' : 237, + 'OpAtomicSMax' : 238, + 'OpAtomicUMax' : 239, + 'OpAtomicAnd' : 240, + 'OpAtomicOr' : 241, + 'OpAtomicXor' : 242, + 'OpPhi' : 245, + 'OpLoopMerge' : 246, + 'OpSelectionMerge' : 247, + 'OpLabel' : 248, + 'OpBranch' : 249, + 'OpBranchConditional' : 250, + 'OpSwitch' : 251, + 'OpKill' : 252, + 'OpReturn' : 253, + 'OpReturnValue' : 254, + 'OpUnreachable' : 255, + 'OpLifetimeStart' : 256, + 'OpLifetimeStop' : 257, + 'OpGroupAsyncCopy' : 259, + 'OpGroupWaitEvents' : 260, + 'OpGroupAll' : 261, + 'OpGroupAny' : 262, + 'OpGroupBroadcast' : 263, + 'OpGroupIAdd' : 264, + 'OpGroupFAdd' : 265, + 'OpGroupFMin' : 266, + 'OpGroupUMin' : 267, + 'OpGroupSMin' : 268, + 'OpGroupFMax' : 269, + 'OpGroupUMax' : 270, + 'OpGroupSMax' : 271, + 'OpReadPipe' : 274, + 'OpWritePipe' : 275, + 'OpReservedReadPipe' : 276, + 'OpReservedWritePipe' : 277, + 'OpReserveReadPipePackets' : 278, + 'OpReserveWritePipePackets' : 279, + 'OpCommitReadPipe' : 280, + 'OpCommitWritePipe' : 281, + 'OpIsValidReserveId' : 282, + 'OpGetNumPipePackets' : 283, + 'OpGetMaxPipePackets' : 284, + 'OpGroupReserveReadPipePackets' : 285, + 'OpGroupReserveWritePipePackets' : 286, + 'OpGroupCommitReadPipe' : 287, + 'OpGroupCommitWritePipe' : 288, + 'OpEnqueueMarker' : 291, + 'OpEnqueueKernel' : 292, + 'OpGetKernelNDrangeSubGroupCount' : 293, + 'OpGetKernelNDrangeMaxSubGroupSize' : 294, + 'OpGetKernelWorkGroupSize' : 295, + 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, + 'OpRetainEvent' : 297, + 'OpReleaseEvent' : 298, + 'OpCreateUserEvent' : 299, + 'OpIsValidEvent' : 300, + 'OpSetUserEventStatus' : 301, + 'OpCaptureEventProfilingInfo' : 302, + 'OpGetDefaultQueue' : 303, + 'OpBuildNDRange' : 304, + 'OpImageSparseSampleImplicitLod' : 305, + 'OpImageSparseSampleExplicitLod' : 306, + 'OpImageSparseSampleDrefImplicitLod' : 307, + 'OpImageSparseSampleDrefExplicitLod' : 308, + 'OpImageSparseSampleProjImplicitLod' : 309, + 'OpImageSparseSampleProjExplicitLod' : 310, + 'OpImageSparseSampleProjDrefImplicitLod' : 311, + 'OpImageSparseSampleProjDrefExplicitLod' : 312, + 'OpImageSparseFetch' : 313, + 'OpImageSparseGather' : 314, + 'OpImageSparseDrefGather' : 315, + 'OpImageSparseTexelsResident' : 316, + 'OpNoLine' : 317, + 'OpAtomicFlagTestAndSet' : 318, + 'OpAtomicFlagClear' : 319, + 'OpImageSparseRead' : 320, + 'OpDecorateId' : 332, + 'OpSubgroupBallotKHR' : 4421, + 'OpSubgroupFirstInvocationKHR' : 4422, + 'OpSubgroupAllKHR' : 4428, + 'OpSubgroupAnyKHR' : 4429, + 'OpSubgroupAllEqualKHR' : 4430, + 'OpSubgroupReadInvocationKHR' : 4432, + 'OpGroupIAddNonUniformAMD' : 5000, + 'OpGroupFAddNonUniformAMD' : 5001, + 'OpGroupFMinNonUniformAMD' : 5002, + 'OpGroupUMinNonUniformAMD' : 5003, + 'OpGroupSMinNonUniformAMD' : 5004, + 'OpGroupFMaxNonUniformAMD' : 5005, + 'OpGroupUMaxNonUniformAMD' : 5006, + 'OpGroupSMaxNonUniformAMD' : 5007, + 'OpFragmentMaskFetchAMD' : 5011, + 'OpFragmentFetchAMD' : 5012, + 'OpSubgroupShuffleINTEL' : 5571, + 'OpSubgroupShuffleDownINTEL' : 5572, + 'OpSubgroupShuffleUpINTEL' : 5573, + 'OpSubgroupShuffleXorINTEL' : 5574, + 'OpSubgroupBlockReadINTEL' : 5575, + 'OpSubgroupBlockWriteINTEL' : 5576, + 'OpSubgroupImageBlockReadINTEL' : 5577, + 'OpSubgroupImageBlockWriteINTEL' : 5578, + 'OpDecorateStringGOOGLE' : 5632, + 'OpMemberDecorateStringGOOGLE' : 5633, + }, + +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/GLSL.std.450.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/GLSL.std.450.h new file mode 100644 index 0000000..54cc00e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/OpenCL.std.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/OpenCL.std.h new file mode 100644 index 0000000..19a6688 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/OpenCL.std.h @@ -0,0 +1,210 @@ +/* +** Copyright (c) 2015-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +namespace OpenCLLIB { + +enum Entrypoints { + + // Section 2.1: Math extended instructions + Acos = 0, + Acosh = 1, + Acospi = 2, + Asin = 3, + Asinh = 4, + Asinpi = 5, + Atan = 6, + Atan2 = 7, + Atanh = 8, + Atanpi = 9, + Atan2pi = 10, + Cbrt = 11, + Ceil = 12, + Copysign = 13, + Cos = 14, + Cosh = 15, + Cospi = 16, + Erfc = 17, + Erf = 18, + Exp = 19, + Exp2 = 20, + Exp10 = 21, + Expm1 = 22, + Fabs = 23, + Fdim = 24, + Floor = 25, + Fma = 26, + Fmax = 27, + Fmin = 28, + Fmod = 29, + Fract = 30, + Frexp = 31, + Hypot = 32, + Ilogb = 33, + Ldexp = 34, + Lgamma = 35, + Lgamma_r = 36, + Log = 37, + Log2 = 38, + Log10 = 39, + Log1p = 40, + Logb = 41, + Mad = 42, + Maxmag = 43, + Minmag = 44, + Modf = 45, + Nan = 46, + Nextafter = 47, + Pow = 48, + Pown = 49, + Powr = 50, + Remainder = 51, + Remquo = 52, + Rint = 53, + Rootn = 54, + Round = 55, + Rsqrt = 56, + Sin = 57, + Sincos = 58, + Sinh = 59, + Sinpi = 60, + Sqrt = 61, + Tan = 62, + Tanh = 63, + Tanpi = 64, + Tgamma = 65, + Trunc = 66, + Half_cos = 67, + Half_divide = 68, + Half_exp = 69, + Half_exp2 = 70, + Half_exp10 = 71, + Half_log = 72, + Half_log2 = 73, + Half_log10 = 74, + Half_powr = 75, + Half_recip = 76, + Half_rsqrt = 77, + Half_sin = 78, + Half_sqrt = 79, + Half_tan = 80, + Native_cos = 81, + Native_divide = 82, + Native_exp = 83, + Native_exp2 = 84, + Native_exp10 = 85, + Native_log = 86, + Native_log2 = 87, + Native_log10 = 88, + Native_powr = 89, + Native_recip = 90, + Native_rsqrt = 91, + Native_sin = 92, + Native_sqrt = 93, + Native_tan = 94, + + // Section 2.2: Integer instructions + SAbs = 141, + SAbs_diff = 142, + SAdd_sat = 143, + UAdd_sat = 144, + SHadd = 145, + UHadd = 146, + SRhadd = 147, + URhadd = 148, + SClamp = 149, + UClamp = 150, + Clz = 151, + Ctz = 152, + SMad_hi = 153, + UMad_sat = 154, + SMad_sat = 155, + SMax = 156, + UMax = 157, + SMin = 158, + UMin = 159, + SMul_hi = 160, + Rotate = 161, + SSub_sat = 162, + USub_sat = 163, + U_Upsample = 164, + S_Upsample = 165, + Popcount = 166, + SMad24 = 167, + UMad24 = 168, + SMul24 = 169, + UMul24 = 170, + UAbs = 201, + UAbs_diff = 202, + UMul_hi = 203, + UMad_hi = 204, + + // Section 2.3: Common instructions + FClamp = 95, + Degrees = 96, + FMax_common = 97, + FMin_common = 98, + Mix = 99, + Radians = 100, + Step = 101, + Smoothstep = 102, + Sign = 103, + + // Section 2.4: Geometric instructions + Cross = 104, + Distance = 105, + Length = 106, + Normalize = 107, + Fast_distance = 108, + Fast_length = 109, + Fast_normalize = 110, + + // Section 2.5: Relational instructions + Bitselect = 186, + Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + Vloadn = 171, + Vstoren = 172, + Vload_half = 173, + Vload_halfn = 174, + Vstore_half = 175, + Vstore_half_r = 176, + Vstore_halfn = 177, + Vstore_halfn_r = 178, + Vloada_halfn = 179, + Vstorea_halfn = 180, + Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + Shuffle = 182, + Shuffle2 = 183, + + // Section 2.8: Misc instructions + Printf = 184, + Prefetch = 185, +}; + +} // end namespace OpenCLLIB diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.glsl.std.450.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.glsl.std.450.grammar.json new file mode 100644 index 0000000..3d9f39e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.glsl.std.450.grammar.json @@ -0,0 +1,642 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "Round", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "RoundEven", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Trunc", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FAbs", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SAbs", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FSign", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SSign", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Floor", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ceil", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fract", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Radians", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "Degrees", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "Sin", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tan", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asin", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acos", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'y_over_x'" } + ] + }, + { + "opname" : "Sinh", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cosh", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tanh", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asinh", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acosh", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atanh", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan2", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Pow", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Exp", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Exp2", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log2", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Sqrt", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "InverseSqrt", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Determinant", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "MatrixInverse", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Modf", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "ModfStruct", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FMin", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMin", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMin", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FMax", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMax", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMax", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FClamp", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "UClamp", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "SClamp", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "FMix", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "IMix", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "Step", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SmoothStep", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fma", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "Frexp", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "FrexpStruct", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ldexp", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "PackSnorm4x8", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm4x8", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackSnorm2x16", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm2x16", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackHalf2x16", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackDouble2x32", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "UnpackSnorm2x16", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm2x16", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackHalf2x16", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "UnpackSnorm4x8", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm4x8", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackDouble2x32", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "Length", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Distance", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "Cross", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Normalize", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FaceForward", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'Nref'" } + ] + }, + { + "opname" : "Reflect", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" } + ] + }, + { + "opname" : "Refract", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'eta'" } + ] + }, + { + "opname" : "FindILsb", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindSMsb", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindUMsb", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "InterpolateAtCentroid", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtSample", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'sample'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtOffset", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "NMin", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NMax", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NClamp", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.opencl.std.100.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.opencl.std.100.grammar.json new file mode 100644 index 0000000..4fe4506 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/extinst.opencl.std.100.grammar.json @@ -0,0 +1,1279 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "acos", + "opcode" : 0, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acosh", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acospi", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asin", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinh", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinpi", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanh", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanpi", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2pi", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cbrt", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ceil", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "copysign", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cosh", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cospi", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erfc", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erf", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp2", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp10", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "expm1", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fabs", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fdim", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "floor", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fma", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fmax", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmod", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fract", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'ptr'" } + ] + }, + { + "opname" : "frexp", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "hypot", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "ilogb", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ldexp", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'k'" } + ] + }, + { + "opname" : "lgamma", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "lgamma_r", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'signp'" } + ] + }, + { + "opname" : "log", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log2", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log10", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log1p", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "logb", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "mad", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "maxmag", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "minmag", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "modf", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'iptr'" } + ] + }, + { + "opname" : "nan", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'nancode'" } + ] + }, + { + "opname" : "nextafter", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "pow", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y" } + ] + }, + { + "opname" : "pown", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "powr", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remainder", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remquo", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'quo'" } + ] + }, + { + "opname" : "rint", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rootn", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "round", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rsqrt", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sin", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sincos", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'cosval'" } + ] + }, + { + "opname" : "sinh", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sinpi", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sqrt", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tan", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanh", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanpi", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tgamma", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "trunc", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_cos", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_divide", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_exp", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp2", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp10", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log2", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log10", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_powr", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_recip", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_rsqrt", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sin", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sqrt", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_tan", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_cos", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_divide", + "opcode" : 82, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_exp", + "opcode" : 83, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp2", + "opcode" : 84, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp10", + "opcode" : 85, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log", + "opcode" : 86, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log2", + "opcode" : 87, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log10", + "opcode" : 88, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_powr", + "opcode" : 89, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_recip", + "opcode" : 90, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_rsqrt", + "opcode" : 91, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sin", + "opcode" : 92, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sqrt", + "opcode" : 93, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_tan", + "opcode" : 94, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs", + "opcode" : 141, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs_diff", + "opcode" : 142, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_add_sat", + "opcode" : 143, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_add_sat", + "opcode" : 144, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_hadd", + "opcode" : 145, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_hadd", + "opcode" : 146, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_rhadd", + "opcode" : 147, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_rhadd", + "opcode" : 148, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_clamp", + "opcode" : 149, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "u_clamp", + "opcode" : 150, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "clz", + "opcode" : 151, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ctz", + "opcode" : 152, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad_hi", + "opcode" : 153, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "u_mad_sat", + "opcode" : 154, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mad_sat", + "opcode" : 155, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_max", + "opcode" : 156, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_max", + "opcode" : 157, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_min", + "opcode" : 158, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_min", + "opcode" : 159, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_mul_hi", + "opcode" : 160, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "rotate", + "opcode" : 161, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "s_sub_sat", + "opcode" : 162, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_sub_sat", + "opcode" : 163, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_upsample", + "opcode" : 164, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "s_upsample", + "opcode" : 165, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "popcount", + "opcode" : 166, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad24", + "opcode" : 167, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "u_mad24", + "opcode" : 168, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mul24", + "opcode" : 169, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul24", + "opcode" : 170, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_abs", + "opcode" : 201, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "u_abs_diff", + "opcode" : 202, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul_hi", + "opcode" : 203, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mad_hi", + "opcode" : 204, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fclamp", + "opcode" : 95, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "degrees", + "opcode" :96, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "fmax_common", + "opcode" : 97, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin_common", + "opcode" : 98, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "mix", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "radians", + "opcode" : 100, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "step", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "smoothstep", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sign", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cross", + "opcode" : 104, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "distance", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "length", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "normalize", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_distance", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "fast_length", + "opcode" : 109, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_normalize", + "opcode" : 110, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "bitselect", + "opcode" : 186, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "select", + "opcode" : 187, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "vloadn", + "opcode" : 171, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstoren", + "opcode" : 172, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_half", + "opcode" : 173, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_halfn", + "opcode" : 174, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstore_half", + "opcode" : 175, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_half_r", + "opcode" : 176, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vstore_halfn", + "opcode" : 177, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_halfn_r", + "opcode" : 178, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vloada_halfn", + "opcode" : 179, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstorea_halfn", + "opcode" : 180, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstorea_halfn_r", + "opcode" : 181, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "shuffle", + "opcode" : 182, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "shuffle2", + "opcode" : 183, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "printf", + "opcode" : 184, + "operands" : [ + { "kind" : "IdRef", "name" : "'format'" }, + { "kind" : "IdRef", "name" : "'additional arguments'", "quantifier" : "*" } + ] + }, + { + "opname" : "prefetch", + "opcode" : 185, + "operands" : [ + { "kind" : "IdRef", "name" : "'ptr'" }, + { "kind" : "IdRef", "name" : "'num elements'" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.core.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.core.grammar.json new file mode 100644 index 0000000..c142e60 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.core.grammar.json @@ -0,0 +1,5938 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "magic_number" : "0x07230203", + "major_version" : 1, + "minor_version" : 1, + "revision" : 8, + "instructions" : [ + { + "opname" : "OpNop", + "opcode" : 0 + }, + { + "opname" : "OpUndef", + "opcode" : 1, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSourceContinued", + "opcode" : 2, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Continued Source'" } + ] + }, + { + "opname" : "OpSource", + "opcode" : 3, + "operands" : [ + { "kind" : "SourceLanguage" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'File'" }, + { "kind" : "LiteralString", "quantifier" : "?", "name" : "'Source'" } + ] + }, + { + "opname" : "OpSourceExtension", + "opcode" : 4, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Extension'" } + ] + }, + { + "opname" : "OpName", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpMemberName", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpString", + "opcode" : 7, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'String'" } + ] + }, + { + "opname" : "OpLine", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "OpExtension", + "opcode" : 10, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInstImport", + "opcode" : 11, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInst", + "opcode" : 12, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ] + }, + { + "opname" : "OpMemoryModel", + "opcode" : 14, + "operands" : [ + { "kind" : "AddressingModel" }, + { "kind" : "MemoryModel" } + ] + }, + { + "opname" : "OpEntryPoint", + "opcode" : 15, + "operands" : [ + { "kind" : "ExecutionModel" }, + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Interface'" } + ] + }, + { + "opname" : "OpExecutionMode", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpCapability", + "opcode" : 17, + "operands" : [ + { "kind" : "Capability", "name" : "'Capability'" } + ] + }, + { + "opname" : "OpTypeVoid", + "opcode" : 19, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeBool", + "opcode" : 20, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeInt", + "opcode" : 21, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "LiteralInteger", "name" : "'Signedness'" } + ] + }, + { + "opname" : "OpTypeFloat", + "opcode" : 22, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" } + ] + }, + { + "opname" : "OpTypeVector", + "opcode" : 23, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "OpTypeMatrix", + "opcode" : 24, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Column Type'" }, + { "kind" : "LiteralInteger", "name" : "'Column Count'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpTypeImage", + "opcode" : 25, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Type'" }, + { "kind" : "Dim" }, + { "kind" : "LiteralInteger", "name" : "'Depth'" }, + { "kind" : "LiteralInteger", "name" : "'Arrayed'" }, + { "kind" : "LiteralInteger", "name" : "'MS'" }, + { "kind" : "LiteralInteger", "name" : "'Sampled'" }, + { "kind" : "ImageFormat" }, + { "kind" : "AccessQualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "OpTypeSampler", + "opcode" : 26, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeSampledImage", + "opcode" : 27, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ] + }, + { + "opname" : "OpTypeArray", + "opcode" : 28, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" }, + { "kind" : "IdRef", "name" : "'Length'" } + ] + }, + { + "opname" : "OpTypeRuntimeArray", + "opcode" : 29, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpTypeStruct", + "opcode" : 30, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ] + }, + { + "opname" : "OpTypeOpaque", + "opcode" : 31, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "The name of the opaque type." } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypePointer", + "opcode" : 32, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "name" : "'Type'" } + ] + }, + { + "opname" : "OpTypeFunction", + "opcode" : 33, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..." } + ] + }, + { + "opname" : "OpTypeEvent", + "opcode" : 34, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypeDeviceEvent", + "opcode" : 35, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypeReserveId", + "opcode" : 36, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeQueue", + "opcode" : 37, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypePipe", + "opcode" : 38, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "AccessQualifier", "name" : "'Qualifier'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeForwardPointer", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "StorageClass" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpConstantTrue", + "opcode" : 41, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstantFalse", + "opcode" : 42, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstant", + "opcode" : 43, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConstantComposite", + "opcode" : 44, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpConstantSampler", + "opcode" : 45, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "SamplerAddressingMode" }, + { "kind" : "LiteralInteger", "name" : "'Param'" }, + { "kind" : "SamplerFilterMode" } + ], + "capabilities" : [ "LiteralSampler" ] + }, + { + "opname" : "OpConstantNull", + "opcode" : 46, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantTrue", + "opcode" : 48, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantFalse", + "opcode" : 49, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstant", + "opcode" : 50, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpSpecConstantComposite", + "opcode" : 51, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpSpecConstantOp", + "opcode" : 52, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralSpecConstantOpInteger", "name" : "'Opcode'" } + ] + }, + { + "opname" : "OpFunction", + "opcode" : 54, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "FunctionControl" }, + { "kind" : "IdRef", "name" : "'Function Type'" } + ] + }, + { + "opname" : "OpFunctionParameter", + "opcode" : 55, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpFunctionEnd", + "opcode" : 56 + }, + { + "opname" : "OpFunctionCall", + "opcode" : 57, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0', +\n'Argument 1', +\n..." } + ] + }, + { + "opname" : "OpVariable", + "opcode" : 59, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ] + }, + { + "opname" : "OpImageTexelPointer", + "opcode" : 60, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Sample'" } + ] + }, + { + "opname" : "OpLoad", + "opcode" : 61, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpStore", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemory", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemorySized", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpAccessChain", + "opcode" : 65, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpInBoundsAccessChain", + "opcode" : 66, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpPtrAccessChain", + "opcode" : 67, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ + "Addresses", + "VariablePointers", + "VariablePointersStorageBuffer" + ] + }, + { + "opname" : "OpArrayLength", + "opcode" : 68, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpGenericPtrMemSemantics", + "opcode" : 69, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpInBoundsPtrAccessChain", + "opcode" : 70, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpDecorate", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpMemberDecorate", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'Structure Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpDecorationGroup", + "opcode" : 73, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpGroupDecorate", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpGroupMemberDecorate", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "PairIdRefLiteralInteger", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpVectorExtractDynamic", + "opcode" : 77, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorInsertDynamic", + "opcode" : 78, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorShuffle", + "opcode" : 79, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Components'" } + ] + }, + { + "opname" : "OpCompositeConstruct", + "opcode" : 80, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpCompositeExtract", + "opcode" : 81, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCompositeInsert", + "opcode" : 82, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCopyObject", + "opcode" : 83, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpTranspose", + "opcode" : 84, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpSampledImage", + "opcode" : 86, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ] + }, + { + "opname" : "OpImageSampleImplicitLod", + "opcode" : 87, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleExplicitLod", + "opcode" : 88, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ] + }, + { + "opname" : "OpImageSampleDrefImplicitLod", + "opcode" : 89, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleDrefExplicitLod", + "opcode" : 90, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjImplicitLod", + "opcode" : 91, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjExplicitLod", + "opcode" : 92, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefImplicitLod", + "opcode" : 93, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefExplicitLod", + "opcode" : 94, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageFetch", + "opcode" : 95, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageGather", + "opcode" : 96, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageDrefGather", + "opcode" : 97, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageRead", + "opcode" : 98, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageWrite", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Texel'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImage", + "opcode" : 100, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" } + ] + }, + { + "opname" : "OpImageQueryFormat", + "opcode" : 101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQueryOrder", + "opcode" : 102, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQuerySizeLod", + "opcode" : 103, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Level of Detail'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySize", + "opcode" : 104, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLod", + "opcode" : 105, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLevels", + "opcode" : 106, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySamples", + "opcode" : 107, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpConvertFToU", + "opcode" : 109, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertFToS", + "opcode" : 110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertSToF", + "opcode" : 111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpConvertUToF", + "opcode" : 112, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpUConvert", + "opcode" : 113, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpSConvert", + "opcode" : 114, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpFConvert", + "opcode" : 115, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpQuantizeToF16", + "opcode" : 116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConvertPtrToU", + "opcode" : 117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpSatConvertSToU", + "opcode" : 118, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSatConvertUToS", + "opcode" : 119, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpConvertUToPtr", + "opcode" : 120, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Integer Value'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpPtrCastToGeneric", + "opcode" : 121, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtr", + "opcode" : 122, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtrExplicit", + "opcode" : 123, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "StorageClass", "name" : "'Storage'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpBitcast", + "opcode" : 124, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSNegate", + "opcode" : 126, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpFNegate", + "opcode" : 127, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpIAdd", + "opcode" : 128, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFAdd", + "opcode" : 129, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISub", + "opcode" : 130, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFSub", + "opcode" : 131, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpIMul", + "opcode" : 132, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMul", + "opcode" : 133, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUDiv", + "opcode" : 134, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSDiv", + "opcode" : 135, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFDiv", + "opcode" : 136, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMod", + "opcode" : 137, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSRem", + "opcode" : 138, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMod", + "opcode" : 139, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFRem", + "opcode" : 140, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMod", + "opcode" : 141, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpVectorTimesScalar", + "opcode" : 142, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ] + }, + { + "opname" : "OpMatrixTimesScalar", + "opcode" : 143, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpVectorTimesMatrix", + "opcode" : 144, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesVector", + "opcode" : 145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesMatrix", + "opcode" : 146, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'LeftMatrix'" }, + { "kind" : "IdRef", "name" : "'RightMatrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpOuterProduct", + "opcode" : 147, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpDot", + "opcode" : 148, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ] + }, + { + "opname" : "OpIAddCarry", + "opcode" : 149, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISubBorrow", + "opcode" : 150, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMulExtended", + "opcode" : 151, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMulExtended", + "opcode" : 152, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpAny", + "opcode" : 154, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpAll", + "opcode" : 155, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpIsNan", + "opcode" : 156, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsInf", + "opcode" : 157, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsFinite", + "opcode" : 158, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpIsNormal", + "opcode" : 159, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSignBitSet", + "opcode" : 160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLessOrGreater", + "opcode" : 161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpOrdered", + "opcode" : 162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpUnordered", + "opcode" : 163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLogicalEqual", + "opcode" : 164, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNotEqual", + "opcode" : 165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalOr", + "opcode" : 166, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalAnd", + "opcode" : 167, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNot", + "opcode" : 168, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSelect", + "opcode" : 169, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'Object 1'" }, + { "kind" : "IdRef", "name" : "'Object 2'" } + ] + }, + { + "opname" : "OpIEqual", + "opcode" : 170, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpINotEqual", + "opcode" : 171, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThan", + "opcode" : 172, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThan", + "opcode" : 173, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThanEqual", + "opcode" : 174, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThanEqual", + "opcode" : 175, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThan", + "opcode" : 176, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThan", + "opcode" : 177, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThanEqual", + "opcode" : 178, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThanEqual", + "opcode" : 179, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdEqual", + "opcode" : 180, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordEqual", + "opcode" : 181, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdNotEqual", + "opcode" : 182, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordNotEqual", + "opcode" : 183, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThan", + "opcode" : 184, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThan", + "opcode" : 185, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThan", + "opcode" : 186, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThan", + "opcode" : 187, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThanEqual", + "opcode" : 188, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThanEqual", + "opcode" : 189, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThanEqual", + "opcode" : 190, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThanEqual", + "opcode" : 191, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpShiftRightLogical", + "opcode" : 194, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftRightArithmetic", + "opcode" : 195, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftLeftLogical", + "opcode" : 196, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpBitwiseOr", + "opcode" : 197, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseXor", + "opcode" : 198, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseAnd", + "opcode" : 199, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpNot", + "opcode" : 200, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpBitFieldInsert", + "opcode" : 201, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Insert'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldSExtract", + "opcode" : 202, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldUExtract", + "opcode" : 203, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitReverse", + "opcode" : 204, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitCount", + "opcode" : 205, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ] + }, + { + "opname" : "OpDPdx", + "opcode" : 207, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdy", + "opcode" : 208, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpFwidth", + "opcode" : 209, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdxFine", + "opcode" : 210, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyFine", + "opcode" : 211, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthFine", + "opcode" : 212, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdxCoarse", + "opcode" : 213, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyCoarse", + "opcode" : 214, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthCoarse", + "opcode" : 215, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpEmitVertex", + "opcode" : 218, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEndPrimitive", + "opcode" : 219, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEmitStreamVertex", + "opcode" : 220, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpEndStreamPrimitive", + "opcode" : 221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpControlBarrier", + "opcode" : 224, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpMemoryBarrier", + "opcode" : 225, + "operands" : [ + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicLoad", + "opcode" : 227, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicStore", + "opcode" : 228, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicExchange", + "opcode" : 229, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicCompareExchange", + "opcode" : 230, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ] + }, + { + "opname" : "OpAtomicCompareExchangeWeak", + "opcode" : 231, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicIIncrement", + "opcode" : 232, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIDecrement", + "opcode" : 233, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIAdd", + "opcode" : 234, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicISub", + "opcode" : 235, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMin", + "opcode" : 236, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMin", + "opcode" : 237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMax", + "opcode" : 238, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMax", + "opcode" : 239, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicAnd", + "opcode" : 240, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicOr", + "opcode" : 241, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicXor", + "opcode" : 242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpPhi", + "opcode" : 245, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "PairIdRefIdRef", "quantifier" : "*", "name" : "'Variable, Parent, ...'" } + ] + }, + { + "opname" : "OpLoopMerge", + "opcode" : 246, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "IdRef", "name" : "'Continue Target'" }, + { "kind" : "LoopControl" } + ] + }, + { + "opname" : "OpSelectionMerge", + "opcode" : 247, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "SelectionControl" } + ] + }, + { + "opname" : "OpLabel", + "opcode" : 248, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpBranch", + "opcode" : 249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target Label'" } + ] + }, + { + "opname" : "OpBranchConditional", + "opcode" : 250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'True Label'" }, + { "kind" : "IdRef", "name" : "'False Label'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Branch weights'" } + ] + }, + { + "opname" : "OpSwitch", + "opcode" : 251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Selector'" }, + { "kind" : "IdRef", "name" : "'Default'" }, + { "kind" : "PairLiteralIntegerIdRef", "quantifier" : "*", "name" : "'Target'" } + ] + }, + { + "opname" : "OpKill", + "opcode" : 252, + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpReturn", + "opcode" : 253 + }, + { + "opname" : "OpReturnValue", + "opcode" : 254, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpUnreachable", + "opcode" : 255 + }, + { + "opname" : "OpLifetimeStart", + "opcode" : 256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLifetimeStop", + "opcode" : 257, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAsyncCopy", + "opcode" : 259, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Destination'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Num Elements'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupWaitEvents", + "opcode" : 260, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Events List'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAll", + "opcode" : 261, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupAny", + "opcode" : 262, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupBroadcast", + "opcode" : 263, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'LocalId'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupIAdd", + "opcode" : 264, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAdd", + "opcode" : 265, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMin", + "opcode" : 266, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMin", + "opcode" : 267, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMin", + "opcode" : 268, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMax", + "opcode" : 269, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMax", + "opcode" : 270, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMax", + "opcode" : 271, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpReadPipe", + "opcode" : 274, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpWritePipe", + "opcode" : 275, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedReadPipe", + "opcode" : 276, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedWritePipe", + "opcode" : 277, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveReadPipePackets", + "opcode" : 278, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveWritePipePackets", + "opcode" : 279, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitReadPipe", + "opcode" : 280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitWritePipe", + "opcode" : 281, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpIsValidReserveId", + "opcode" : 282, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetNumPipePackets", + "opcode" : 283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetMaxPipePackets", + "opcode" : 284, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveReadPipePackets", + "opcode" : 285, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveWritePipePackets", + "opcode" : 286, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitReadPipe", + "opcode" : 287, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitWritePipe", + "opcode" : 288, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpEnqueueMarker", + "opcode" : 291, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpEnqueueKernel", + "opcode" : 292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Local Size'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeSubGroupCount", + "opcode" : 293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeMaxSubGroupSize", + "opcode" : 294, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelWorkGroupSize", + "opcode" : 295, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelPreferredWorkGroupSizeMultiple", + "opcode" : 296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpRetainEvent", + "opcode" : 297, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpReleaseEvent", + "opcode" : 298, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCreateUserEvent", + "opcode" : 299, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpIsValidEvent", + "opcode" : 300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpSetUserEventStatus", + "opcode" : 301, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Status'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCaptureEventProfilingInfo", + "opcode" : 302, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Profiling Info'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetDefaultQueue", + "opcode" : 303, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpBuildNDRange", + "opcode" : 304, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'GlobalWorkSize'" }, + { "kind" : "IdRef", "name" : "'LocalWorkSize'" }, + { "kind" : "IdRef", "name" : "'GlobalWorkOffset'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpImageSparseSampleImplicitLod", + "opcode" : 305, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleExplicitLod", + "opcode" : 306, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefImplicitLod", + "opcode" : 307, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefExplicitLod", + "opcode" : 308, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjImplicitLod", + "opcode" : 309, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjExplicitLod", + "opcode" : 310, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefImplicitLod", + "opcode" : 311, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefExplicitLod", + "opcode" : 312, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseFetch", + "opcode" : 313, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseGather", + "opcode" : 314, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseDrefGather", + "opcode" : 315, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseTexelsResident", + "opcode" : 316, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Resident Code'" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpNoLine", + "opcode" : 317 + }, + { + "opname" : "OpAtomicFlagTestAndSet", + "opcode" : 318, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicFlagClear", + "opcode" : 319, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageSparseRead", + "opcode" : 320, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpSizeOf", + "opcode" : 321, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpTypePipeStorage", + "opcode" : 322, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpConstantPipeStorage", + "opcode" : 323, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Packet Size'" }, + { "kind" : "LiteralInteger", "name" : "'Packet Alignment'" }, + { "kind" : "LiteralInteger", "name" : "'Capacity'" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpCreatePipeFromPipeStorage", + "opcode" : 324, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe Storage'" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpGetKernelLocalSizeForSubgroupCount", + "opcode" : 325, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ] + }, + { + "opname" : "OpGetKernelMaxNumSubgroups", + "opcode" : 326, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ] + }, + { + "opname" : "OpTypeNamedBarrier", + "opcode" : 327, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpNamedBarrierInitialize", + "opcode" : 328, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpMemoryNamedBarrier", + "opcode" : 329, + "operands" : [ + { "kind" : "IdRef", "name" : "'Named Barrier'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpModuleProcessed", + "opcode" : 330, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Process'" } + ] + }, + { + "opname" : "OpDecorateId", + "opcode" : 332, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "opname" : "OpSubgroupBallotKHR", + "opcode" : 4421, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupFirstInvocationKHR", + "opcode" : 4422, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupAllKHR", + "opcode" : 4428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAnyKHR", + "opcode" : 4429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAllEqualKHR", + "opcode" : 4430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupReadInvocationKHR", + "opcode" : 4432, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpGroupIAddNonUniformAMD", + "opcode" : 5000, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAddNonUniformAMD", + "opcode" : 5001, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMinNonUniformAMD", + "opcode" : 5002, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMinNonUniformAMD", + "opcode" : 5003, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMinNonUniformAMD", + "opcode" : 5004, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMaxNonUniformAMD", + "opcode" : 5005, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMaxNonUniformAMD", + "opcode" : 5006, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMaxNonUniformAMD", + "opcode" : 5007, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpFragmentMaskFetchAMD", + "opcode" : 5011, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpFragmentFetchAMD", + "opcode" : 5012, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Fragment Index'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpSubgroupShuffleINTEL", + "opcode" : 5571, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'InvocationId'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleDownINTEL", + "opcode" : 5572, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Next'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleUpINTEL", + "opcode" : 5573, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Previous'" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleXorINTEL", + "opcode" : 5574, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupBlockReadINTEL", + "opcode" : 5575, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupBlockWriteINTEL", + "opcode" : 5576, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockReadINTEL", + "opcode" : 5577, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockWriteINTEL", + "opcode" : 5578, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpDecorateStringGOOGLE", + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + }, + { + "opname" : "OpMemberDecorateStringGOOGLE", + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "ImageOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Bias", + "value" : "0x0001", + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Lod", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Grad", + "value" : "0x0004", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffset", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Offset", + "value" : "0x0010", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffsets", + "value" : "0x0020", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Sample", + "value" : "0x0040", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "MinLod", + "value" : "0x0080", + "capabilities" : [ "MinLod" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FPFastMathMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "NotNaN", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NotInf", + "value" : "0x0002", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NSZ", + "value" : "0x0004", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AllowRecip", + "value" : "0x0008", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Fast", + "value" : "0x0010", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "SelectionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Flatten", + "value" : "0x0001" + }, + { + "enumerant" : "DontFlatten", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "LoopControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Unroll", + "value" : "0x0001" + }, + { + "enumerant" : "DontUnroll", + "value" : "0x0002" + }, + { + "enumerant" : "DependencyInfinite", + "value" : "0x0004" + }, + { + "enumerant" : "DependencyLength", + "value" : "0x0008", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FunctionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Inline", + "value" : "0x0001" + }, + { + "enumerant" : "DontInline", + "value" : "0x0002" + }, + { + "enumerant" : "Pure", + "value" : "0x0004" + }, + { + "enumerant" : "Const", + "value" : "0x0008" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemorySemantics", + "enumerants" : [ + { + "enumerant" : "Relaxed", + "value" : "0x0000" + }, + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Acquire", + "value" : "0x0002" + }, + { + "enumerant" : "Release", + "value" : "0x0004" + }, + { + "enumerant" : "AcquireRelease", + "value" : "0x0008" + }, + { + "enumerant" : "SequentiallyConsistent", + "value" : "0x0010" + }, + { + "enumerant" : "UniformMemory", + "value" : "0x0040", + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupMemory", + "value" : "0x0080" + }, + { + "enumerant" : "WorkgroupMemory", + "value" : "0x0100" + }, + { + "enumerant" : "CrossWorkgroupMemory", + "value" : "0x0200" + }, + { + "enumerant" : "AtomicCounterMemory", + "value" : "0x0400", + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "ImageMemory", + "value" : "0x0800" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemoryAccess", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Volatile", + "value" : "0x0001" + }, + { + "enumerant" : "Aligned", + "value" : "0x0002", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Nontemporal", + "value" : "0x0004" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "KernelProfilingInfo", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "CmdExecTime", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SourceLanguage", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "ESSL", + "value" : 1 + }, + { + "enumerant" : "GLSL", + "value" : 2 + }, + { + "enumerant" : "OpenCL_C", + "value" : 3 + }, + { + "enumerant" : "OpenCL_CPP", + "value" : 4 + }, + { + "enumerant" : "HLSL", + "value" : 5 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionModel", + "enumerants" : [ + { + "enumerant" : "Vertex", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TessellationControl", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessellationEvaluation", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Geometry", + "value" : 3, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Fragment", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLCompute", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Kernel", + "value" : 6, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AddressingModel", + "enumerants" : [ + { + "enumerant" : "Logical", + "value" : 0 + }, + { + "enumerant" : "Physical32", + "value" : 1, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Physical64", + "value" : 2, + "capabilities" : [ "Addresses" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "MemoryModel", + "enumerants" : [ + { + "enumerant" : "Simple", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSL450", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OpenCL", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionMode", + "enumerants" : [ + { + "enumerant" : "Invocations", + "value" : 0, + "capabilities" : [ "Geometry" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of <>'" } + ] + }, + { + "enumerant" : "SpacingEqual", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalEven", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalOdd", + "value" : 3, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCw", + "value" : 4, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCcw", + "value" : 5, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PixelCenterInteger", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginUpperLeft", + "value" : 7, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginLowerLeft", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "EarlyFragmentTests", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointMode", + "value" : 10, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Xfb", + "value" : 11, + "capabilities" : [ "TransformFeedback" ] + }, + { + "enumerant" : "DepthReplacing", + "value" : 12, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthGreater", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthLess", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthUnchanged", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "LocalSize", + "value" : 17, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHint", + "value" : 18, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "InputPoints", + "value" : 19, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLines", + "value" : 20, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLinesAdjacency", + "value" : 21, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Triangles", + "value" : 22, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InputTrianglesAdjacency", + "value" : 23, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Quads", + "value" : 24, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Isolines", + "value" : 25, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "OutputVertices", + "value" : 26, + "capabilities" : [ "Geometry", "Tessellation" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vertex count'" } + ] + }, + { + "enumerant" : "OutputPoints", + "value" : 27, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputLineStrip", + "value" : 28, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputTriangleStrip", + "value" : 29, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "VecTypeHint", + "value" : 30, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vector type'" } + ] + }, + { + "enumerant" : "ContractionOff", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Initializer", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Finalizer", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 35, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroup Size'" } + ] + }, + { + "enumerant" : "SubgroupsPerWorkgroup", + "value" : 36, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroups Per Workgroup'" } + ] + }, + { + "enumerant" : "PostDepthCoverage", + "value" : 4446, + "capabilities" : [ "SampleMaskPostDepthCoverage" ] + }, + { + "enumerant" : "StencilRefReplacingEXT", + "value" : 5027, + "capabilities" : [ "StencilExportEXT" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StorageClass", + "enumerants" : [ + { + "enumerant" : "UniformConstant", + "value" : 0 + }, + { + "enumerant" : "Input", + "value" : 1 + }, + { + "enumerant" : "Uniform", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Output", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Workgroup", + "value" : 4 + }, + { + "enumerant" : "CrossWorkgroup", + "value" : 5 + }, + { + "enumerant" : "Private", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Function", + "value" : 7 + }, + { + "enumerant" : "Generic", + "value" : 8, + "capabilities" : [ "GenericPointer" ] + }, + { + "enumerant" : "PushConstant", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "AtomicCounter", + "value" : 10, + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "Image", + "value" : 11 + }, + { + "enumerant" : "StorageBuffer", + "value" : 12, + "extensions" : [ + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers" + ], + "capabilities" : [ "Shader" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Dim", + "enumerants" : [ + { + "enumerant" : "1D", + "value" : 0, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "2D", + "value" : 1 + }, + { + "enumerant" : "3D", + "value" : 2 + }, + { + "enumerant" : "Cube", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rect", + "value" : 4, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "Buffer", + "value" : 5, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "SubpassData", + "value" : 6, + "capabilities" : [ "InputAttachment" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerAddressingMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ClampToEdge", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Clamp", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Repeat", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerFilterMode", + "enumerants" : [ + { + "enumerant" : "Nearest", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Linear", + "value" : 1, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageFormat", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "Rgba32f", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16f", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32f", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8Snorm", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32f", + "value" : 6, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16f", + "value" : 7, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R11fG11fB10f", + "value" : 8, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16f", + "value" : 9, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16", + "value" : 10, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgb10A2", + "value" : 11, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16", + "value" : 12, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8", + "value" : 13, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16", + "value" : 14, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8", + "value" : 15, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16Snorm", + "value" : 16, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16Snorm", + "value" : 17, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8Snorm", + "value" : 18, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16Snorm", + "value" : 19, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8Snorm", + "value" : 20, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32i", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16i", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8i", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32i", + "value" : 24, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32i", + "value" : 25, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16i", + "value" : 26, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8i", + "value" : 27, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16i", + "value" : 28, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8i", + "value" : 29, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32ui", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16ui", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8ui", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32ui", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgb10a2ui", + "value" : 34, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg32ui", + "value" : 35, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16ui", + "value" : 36, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8ui", + "value" : 37, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16ui", + "value" : 38, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8ui", + "value" : 39, + "capabilities" : [ "StorageImageExtendedFormats" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelOrder", + "enumerants" : [ + { + "enumerant" : "R", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "A", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RG", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RA", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGB", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBA", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BGRA", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ARGB", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Intensity", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Luminance", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Rx", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGx", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBx", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Depth", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "DepthStencil", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGB", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBx", + "value" : 16, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBA", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sBGRA", + "value" : 18, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ABGR", + "value" : 19, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelDataType", + "enumerants" : [ + { + "enumerant" : "SnormInt8", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SnormInt16", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt8", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt16", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort565", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort555", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt8", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt16", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt32", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt8", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt16", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt32", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "HalfFloat", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt24", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010_2", + "value" : 16, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPRoundingMode", + "enumerants" : [ + { + "enumerant" : "RTE", + "value" : 0, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTZ", + "value" : 1, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTP", + "value" : 2, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTN", + "value" : 3, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LinkageType", + "enumerants" : [ + { + "enumerant" : "Export", + "value" : 0, + "capabilities" : [ "Linkage" ] + }, + { + "enumerant" : "Import", + "value" : 1, + "capabilities" : [ "Linkage" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AccessQualifier", + "enumerants" : [ + { + "enumerant" : "ReadOnly", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WriteOnly", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ReadWrite", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FunctionParameterAttribute", + "enumerants" : [ + { + "enumerant" : "Zext", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sext", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ByVal", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sret", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoAlias", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoCapture", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoWrite", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoReadWrite", + "value" : 7, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Decoration", + "enumerants" : [ + { + "enumerant" : "RelaxedPrecision", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SpecId", + "value" : 1, + "capabilities" : [ "Shader", "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Specialization Constant ID'" } + ] + }, + { + "enumerant" : "Block", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "BufferBlock", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "RowMajor", + "value" : 4, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ColMajor", + "value" : 5, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ArrayStride", + "value" : 6, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Array Stride'" } + ] + }, + { + "enumerant" : "MatrixStride", + "value" : 7, + "capabilities" : [ "Matrix" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Matrix Stride'" } + ] + }, + { + "enumerant" : "GLSLShared", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSLPacked", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CPacked", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BuiltIn", + "value" : 11, + "parameters" : [ + { "kind" : "BuiltIn" } + ] + }, + { + "enumerant" : "NoPerspective", + "value" : 13, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Flat", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Patch", + "value" : 15, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Centroid", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sample", + "value" : 17, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "Invariant", + "value" : 18, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Restrict", + "value" : 19 + }, + { + "enumerant" : "Aliased", + "value" : 20 + }, + { + "enumerant" : "Volatile", + "value" : 21 + }, + { + "enumerant" : "Constant", + "value" : 22, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Coherent", + "value" : 23 + }, + { + "enumerant" : "NonWritable", + "value" : 24 + }, + { + "enumerant" : "NonReadable", + "value" : 25 + }, + { + "enumerant" : "Uniform", + "value" : 26, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SaturatedConversion", + "value" : 28, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Stream", + "value" : 29, + "capabilities" : [ "GeometryStreams" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stream Number'" } + ] + }, + { + "enumerant" : "Location", + "value" : 30, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Location'" } + ] + }, + { + "enumerant" : "Component", + "value" : 31, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Component'" } + ] + }, + { + "enumerant" : "Index", + "value" : 32, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Index'" } + ] + }, + { + "enumerant" : "Binding", + "value" : 33, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Binding Point'" } + ] + }, + { + "enumerant" : "DescriptorSet", + "value" : 34, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Descriptor Set'" } + ] + }, + { + "enumerant" : "Offset", + "value" : 35, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Byte Offset'" } + ] + }, + { + "enumerant" : "XfbBuffer", + "value" : 36, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Buffer Number'" } + ] + }, + { + "enumerant" : "XfbStride", + "value" : 37, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Stride'" } + ] + }, + { + "enumerant" : "FuncParamAttr", + "value" : 38, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FunctionParameterAttribute", "name" : "'Function Parameter Attribute'" } + ] + }, + { + "enumerant" : "FPRoundingMode", + "value" : 39, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ], + "parameters" : [ + { "kind" : "FPRoundingMode", "name" : "'Floating-Point Rounding Mode'" } + ] + }, + { + "enumerant" : "FPFastMathMode", + "value" : 40, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FPFastMathMode", "name" : "'Fast-Math Mode'" } + ] + }, + { + "enumerant" : "LinkageAttributes", + "value" : 41, + "capabilities" : [ "Linkage" ], + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "LinkageType", "name" : "'Linkage Type'" } + ] + }, + { + "enumerant" : "NoContraction", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InputAttachmentIndex", + "value" : 43, + "capabilities" : [ "InputAttachment" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Attachment Index'" } + ] + }, + { + "enumerant" : "Alignment", + "value" : 44, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "MaxByteOffset", + "value" : 45, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Max Byte Offset'" } + ] + }, + { + "enumerant" : "ExplicitInterpAMD", + "value" : 4999 + }, + { + "enumerant" : "OverrideCoverageNV", + "value" : 5248, + "capabilities" : [ "SampleMaskOverrideCoverageNV" ] + }, + { + "enumerant" : "PassthroughNV", + "value" : 5250, + "capabilities" : [ "GeometryShaderPassthroughNV" ] + }, + { + "enumerant" : "ViewportRelativeNV", + "value" : 5252, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryViewportRelativeNV", + "value" : 5256, + "capabilities" : [ "ShaderStereoViewNV" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ] + }, + { + "enumerant" : "HlslCounterBufferGOOGLE", + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "enumerant" : "HlslSemanticGOOGLE", + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "BuiltIn", + "enumerants" : [ + { + "enumerant" : "Position", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointSize", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 3, + "capabilities" : [ "ClipDistance" ] + }, + { + "enumerant" : "CullDistance", + "value" : 4, + "capabilities" : [ "CullDistance" ] + }, + { + "enumerant" : "VertexId", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceId", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PrimitiveId", + "value" : 7, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InvocationId", + "value" : 8, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "Layer", + "value" : 9, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ViewportIndex", + "value" : 10, + "capabilities" : [ "MultiViewport" ] + }, + { + "enumerant" : "TessLevelOuter", + "value" : 11, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessLevelInner", + "value" : 12, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessCoord", + "value" : 13, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PatchVertices", + "value" : 14, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "FragCoord", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointCoord", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FrontFacing", + "value" : 17, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampleId", + "value" : 18, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SamplePosition", + "value" : 19, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SampleMask", + "value" : 20, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FragDepth", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "HelperInvocation", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "NumWorkgroups", + "value" : 24 + }, + { + "enumerant" : "WorkgroupSize", + "value" : 25 + }, + { + "enumerant" : "WorkgroupId", + "value" : 26 + }, + { + "enumerant" : "LocalInvocationId", + "value" : 27 + }, + { + "enumerant" : "GlobalInvocationId", + "value" : 28 + }, + { + "enumerant" : "LocalInvocationIndex", + "value" : 29 + }, + { + "enumerant" : "WorkDim", + "value" : 30, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalSize", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "EnqueuedWorkgroupSize", + "value" : 32, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalOffset", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalLinearId", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 36, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupMaxSize", + "value" : 37, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumSubgroups", + "value" : 38, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumEnqueuedSubgroups", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupId", + "value" : 40, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupLocalInvocationId", + "value" : 41, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "VertexIndex", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceIndex", + "value" : 43, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupEqMaskKHR", + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGeMaskKHR", + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGtMaskKHR", + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLeMaskKHR", + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLtMaskKHR", + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "BaseVertex", + "value" : 4424, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "BaseInstance", + "value" : 4425, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DrawIndex", + "value" : 4426, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DeviceIndex", + "value" : 4438, + "capabilities" : [ "DeviceGroup" ] + }, + { + "enumerant" : "ViewIndex", + "value" : 4440, + "capabilities" : [ "MultiView" ] + }, + { + "enumerant" : "BaryCoordNoPerspAMD", + "value" : 4992 + }, + { + "enumerant" : "BaryCoordNoPerspCentroidAMD", + "value" : 4993 + }, + { + "enumerant" : "BaryCoordNoPerspSampleAMD", + "value" : 4994 + }, + { + "enumerant" : "BaryCoordSmoothAMD", + "value" : 4995 + }, + { + "enumerant" : "BaryCoordSmoothCentroidAMD", + "value" : 4996 + }, + { + "enumerant" : "BaryCoordSmoothSampleAMD", + "value" : 4997 + }, + { + "enumerant" : "BaryCoordPullModelAMD", + "value" : 4998 + }, + { + "enumerant" : "FragStencilRefEXT", + "value" : 5014, + "capabilities" : [ "StencilExportEXT" ] + }, + { + "enumerant" : "ViewportMaskNV", + "value" : 5253, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryPositionNV", + "value" : 5257, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "SecondaryViewportMaskNV", + "value" : 5258, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "PositionPerViewNV", + "value" : 5261, + "capabilities" : [ "PerViewAttributesNV" ] + }, + { + "enumerant" : "ViewportMaskPerViewNV", + "value" : 5262, + "capabilities" : [ "PerViewAttributesNV" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Scope", + "enumerants" : [ + { + "enumerant" : "CrossDevice", + "value" : 0 + }, + { + "enumerant" : "Device", + "value" : 1 + }, + { + "enumerant" : "Workgroup", + "value" : 2 + }, + { + "enumerant" : "Subgroup", + "value" : 3 + }, + { + "enumerant" : "Invocation", + "value" : 4 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "GroupOperation", + "enumerants" : [ + { + "enumerant" : "Reduce", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InclusiveScan", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ExclusiveScan", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "KernelEnqueueFlags", + "enumerants" : [ + { + "enumerant" : "NoWait", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitKernel", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitWorkGroup", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Capability", + "enumerants" : [ + { + "enumerant" : "Matrix", + "value" : 0 + }, + { + "enumerant" : "Shader", + "value" : 1, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "Geometry", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Tessellation", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Addresses", + "value" : 4 + }, + { + "enumerant" : "Linkage", + "value" : 5 + }, + { + "enumerant" : "Kernel", + "value" : 6 + }, + { + "enumerant" : "Vector16", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16Buffer", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16", + "value" : 9 + }, + { + "enumerant" : "Float64", + "value" : 10 + }, + { + "enumerant" : "Int64", + "value" : 11 + }, + { + "enumerant" : "Int64Atomics", + "value" : 12, + "capabilities" : [ "Int64" ] + }, + { + "enumerant" : "ImageBasic", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ImageReadWrite", + "value" : 14, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "ImageMipmap", + "value" : 15, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "Pipes", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Groups", + "value" : 18 + }, + { + "enumerant" : "DeviceEnqueue", + "value" : 19, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "LiteralSampler", + "value" : 20, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AtomicStorage", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Int16", + "value" : 22 + }, + { + "enumerant" : "TessellationPointSize", + "value" : 23, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "GeometryPointSize", + "value" : 24, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ImageGatherExtended", + "value" : 25, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageMultisample", + "value" : 27, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "UniformBufferArrayDynamicIndexing", + "value" : 28, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledImageArrayDynamicIndexing", + "value" : 29, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageBufferArrayDynamicIndexing", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageArrayDynamicIndexing", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CullDistance", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageCubeArray", + "value" : 34, + "capabilities" : [ "SampledCubeArray" ] + }, + { + "enumerant" : "SampleRateShading", + "value" : 35, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageRect", + "value" : 36, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "SampledRect", + "value" : 37, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GenericPointer", + "value" : 38, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Int8", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InputAttachment", + "value" : 40, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SparseResidency", + "value" : 41, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MinLod", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sampled1D", + "value" : 43 + }, + { + "enumerant" : "Image1D", + "value" : 44, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "SampledCubeArray", + "value" : 45, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledBuffer", + "value" : 46 + }, + { + "enumerant" : "ImageBuffer", + "value" : 47, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "ImageMSArray", + "value" : 48, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageExtendedFormats", + "value" : 49, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageQuery", + "value" : 50, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DerivativeControl", + "value" : 51, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InterpolationFunction", + "value" : 52, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TransformFeedback", + "value" : 53, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GeometryStreams", + "value" : 54, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "StorageImageReadWithoutFormat", + "value" : 55, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageWriteWithoutFormat", + "value" : 56, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MultiViewport", + "value" : 57, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "SubgroupDispatch", + "value" : 58, + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "enumerant" : "NamedBarrier", + "value" : 59, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "PipeStorage", + "value" : 60, + "capabilities" : [ "Pipes" ] + }, + { + "enumerant" : "SubgroupBallotKHR", + "value" : 4423, + "extensions" : [ "SPV_KHR_shader_ballot" ] + }, + { + "enumerant" : "DrawParameters", + "value" : 4427, + "extensions" : [ "SPV_KHR_shader_draw_parameters" ] + }, + { + "enumerant" : "SubgroupVoteKHR", + "value" : 4431, + "extensions" : [ "SPV_KHR_subgroup_vote" ] + }, + { + "enumerant" : "StorageBuffer16BitAccess", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniformBufferBlock16", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "UniformAndStorageBuffer16BitAccess", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniform16", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StoragePushConstant16", + "value" : 4435, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageInputOutput16", + "value" : 4436, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "DeviceGroup", + "value" : 4437, + "extensions" : [ "SPV_KHR_device_group" ] + }, + { + "enumerant" : "MultiView", + "value" : 4439, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_multiview" ] + }, + { + "enumerant" : "VariablePointersStorageBuffer", + "value" : 4441, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant" : "VariablePointers", + "value" : 4442, + "capabilities" : [ "VariablePointersStorageBuffer" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant": "AtomicStorageOps", + "value": 4445, + "extensions": [ "SPV_KHR_shader_atomic_counter_ops" ] + }, + { + "enumerant" : "SampleMaskPostDepthCoverage", + "value" : 4447, + "extensions" : [ "SPV_KHR_post_depth_coverage" ] + }, + { + "enumerant" : "ImageGatherBiasLodAMD", + "value" : 5009, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_texture_gather_bias_lod" ] + }, + { + "enumerant" : "FragmentMaskAMD", + "value" : 5010, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ] + }, + { + "enumerant" : "StencilExportEXT", + "value" : 5013, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ] + }, + { + "enumerant" : "ImageReadWriteLodAMD", + "value" : 5015, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_image_load_store_lod" ] + }, + { + "enumerant" : "SampleMaskOverrideCoverageNV", + "value" : 5249, + "capabilities" : [ "SampleRateShading" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ] + }, + { + "enumerant" : "GeometryShaderPassthroughNV", + "value" : 5251, + "capabilities" : [ "Geometry" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerEXT", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_EXT_shader_viewport_index_layer" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerNV", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderViewportMaskNV", + "value" : 5255, + "capabilities" : [ "ShaderViewportIndexLayerNV" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderStereoViewNV", + "value" : 5259, + "capabilities" : [ "ShaderViewportMaskNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ] + }, + { + "enumerant" : "PerViewAttributesNV", + "value" : 5260, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes" ] + }, + { + "enumerant" : "SubgroupShuffleINTEL", + "value" : 5568, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupBufferBlockIOINTEL", + "value" : 5569, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupImageBlockIOINTEL", + "value" : 5570, + "extensions" : [ "SPV_INTEL_subgroups" ] + } + ] + }, + { + "category" : "Id", + "kind" : "IdResultType", + "doc" : "Reference to an representing the result's type of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdResult", + "doc" : "Definition of an representing the result of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdMemorySemantics", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the MemorySemantics operand kind" + }, + { + "category" : "Id", + "kind" : "IdScope", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the Scope operand kind" + }, + { + "category" : "Id", + "kind" : "IdRef", + "doc" : "Reference to an " + }, + { + "category" : "Literal", + "kind" : "LiteralInteger", + "doc" : "An integer consuming one or more words" + }, + { + "category" : "Literal", + "kind" : "LiteralString", + "doc" : "A null-terminated stream of characters consuming an integral number of words" + }, + { + "category" : "Literal", + "kind" : "LiteralContextDependentNumber", + "doc" : "A literal number whose size and format are determined by a previous operand in the enclosing instruction" + }, + { + "category" : "Literal", + "kind" : "LiteralExtInstInteger", + "doc" : "A 32-bit unsigned integer indicating which instruction to use and determining the layout of following operands (for OpExtInst)" + }, + { + "category" : "Literal", + "kind" : "LiteralSpecConstantOpInteger", + "doc" : "An opcode indicating the operation to be performed and determining the layout of following operands (for OpSpecConstantOp)" + }, + { + "category" : "Composite", + "kind" : "PairLiteralIntegerIdRef", + "bases" : [ "LiteralInteger", "IdRef" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefLiteralInteger", + "bases" : [ "IdRef", "LiteralInteger" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefIdRef", + "bases" : [ "IdRef", "IdRef" ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.cs b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.cs new file mode 100644 index 0000000..99194e5 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.cs @@ -0,0 +1,1015 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C# +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + + public static class Specification + { + public const uint MagicNumber = 0x07230203; + public const uint Version = 0x00010100; + public const uint Revision = 8; + public const uint OpCodeMask = 0xffff; + public const uint WordCountShift = 16; + + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + } + + public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + } + + public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + } + + public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + } + + public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + } + + public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + } + + public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + } + + public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + } + + public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + } + + public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + } + + public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + } + + public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + } + + public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + } + + public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + } + + public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + } + + public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + } + } +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.h new file mode 100644 index 0000000..971c3be --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.h @@ -0,0 +1,1015 @@ +/* +** Copyright (c) 2014-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10100 +#define SPV_REVISION 8 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010100; +static const unsigned int SpvRevision = 8; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpDecorateId = 332, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#endif // #ifndef spirv_H + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp new file mode 100644 index 0000000..c26ac1f --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp @@ -0,0 +1,1024 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10100 +#define SPV_REVISION 8 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010100; +static const unsigned int Revision = 8; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilityMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + OpMax = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp11 b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp11 new file mode 100644 index 0000000..992d43b --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.hpp11 @@ -0,0 +1,1024 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10100 +#define SPV_REVISION 8 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010100; +static const unsigned int Revision = 8; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum class SourceLanguage : unsigned { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, +}; + +enum class ExecutionModel : unsigned { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + Max = 0x7fffffff, +}; + +enum class AddressingModel : unsigned { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + Max = 0x7fffffff, +}; + +enum class MemoryModel : unsigned { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Max = 0x7fffffff, +}; + +enum class ExecutionMode : unsigned { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + Max = 0x7fffffff, +}; + +enum class StorageClass : unsigned { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + Max = 0x7fffffff, +}; + +enum class Dim : unsigned { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, +}; + +enum class SamplerAddressingMode : unsigned { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class SamplerFilterMode : unsigned { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +}; + +enum class ImageFormat : unsigned { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + Max = 0x7fffffff, +}; + +enum class ImageChannelOrder : unsigned { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +}; + +enum class ImageChannelDataType : unsigned { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, +}; + +enum class ImageOperandsShift : unsigned { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + Max = 0x7fffffff, +}; + +enum class ImageOperandsMask : unsigned { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, +}; + +enum class FPFastMathModeShift : unsigned { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + Max = 0x7fffffff, +}; + +enum class FPFastMathModeMask : unsigned { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, +}; + +enum class FPRoundingMode : unsigned { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +}; + +enum class LinkageType : unsigned { + Export = 0, + Import = 1, + Max = 0x7fffffff, +}; + +enum class AccessQualifier : unsigned { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +}; + +enum class FunctionParameterAttribute : unsigned { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, +}; + +enum class Decoration : unsigned { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + Max = 0x7fffffff, +}; + +enum class BuiltIn : unsigned { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + Max = 0x7fffffff, +}; + +enum class SelectionControlShift : unsigned { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +}; + +enum class SelectionControlMask : unsigned { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +}; + +enum class LoopControlShift : unsigned { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + Max = 0x7fffffff, +}; + +enum class LoopControlMask : unsigned { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, +}; + +enum class FunctionControlShift : unsigned { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + Max = 0x7fffffff, +}; + +enum class FunctionControlMask : unsigned { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, +}; + +enum class MemorySemanticsShift : unsigned { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + Max = 0x7fffffff, +}; + +enum class MemorySemanticsMask : unsigned { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, +}; + +enum class MemoryAccessShift : unsigned { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + Max = 0x7fffffff, +}; + +enum class MemoryAccessMask : unsigned { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, +}; + +enum class Scope : unsigned { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + Max = 0x7fffffff, +}; + +enum class GroupOperation : unsigned { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + Max = 0x7fffffff, +}; + +enum class KernelEnqueueFlags : unsigned { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoShift : unsigned { + CmdExecTime = 0, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoMask : unsigned { + MaskNone = 0, + CmdExecTime = 0x00000001, +}; + +enum class Capability : unsigned { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + Max = 0x7fffffff, +}; + +enum class Op : unsigned { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + Max = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.json new file mode 100644 index 0000000..4c18e01 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.json @@ -0,0 +1,1040 @@ +{ + "spv": + { + "meta": + { + "Comment": + [ + [ + "Copyright (c) 2014-2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + [ + "This header is automatically generated by the same tool that creates", + "the Binary Section of the SPIR-V specification." + ], + [ + "Enumeration tokens for SPIR-V, in various styles:", + " C, C++, C++11, JSON, Lua, Python", + "", + "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", + "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", + "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", + "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", + "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", + "", + "Some tokens act like mask values, which can be OR'd together,", + "while others are mutually exclusive. The mask-like ones have", + "\"Mask\" in their name, and a parallel enum that has the shift", + "amount (1 << x) for each corresponding enumerant." + ] + ], + "MagicNumber": 119734787, + "Version": 65792, + "Revision": 8, + "OpCodeMask": 65535, + "WordCountShift": 16 + }, + "enum": + [ + { + "Name": "SourceLanguage", + "Type": "Value", + "Values": + { + "Unknown": 0, + "ESSL": 1, + "GLSL": 2, + "OpenCL_C": 3, + "OpenCL_CPP": 4, + "HLSL": 5 + } + }, + { + "Name": "ExecutionModel", + "Type": "Value", + "Values": + { + "Vertex": 0, + "TessellationControl": 1, + "TessellationEvaluation": 2, + "Geometry": 3, + "Fragment": 4, + "GLCompute": 5, + "Kernel": 6 + } + }, + { + "Name": "AddressingModel", + "Type": "Value", + "Values": + { + "Logical": 0, + "Physical32": 1, + "Physical64": 2 + } + }, + { + "Name": "MemoryModel", + "Type": "Value", + "Values": + { + "Simple": 0, + "GLSL450": 1, + "OpenCL": 2 + } + }, + { + "Name": "ExecutionMode", + "Type": "Value", + "Values": + { + "Invocations": 0, + "SpacingEqual": 1, + "SpacingFractionalEven": 2, + "SpacingFractionalOdd": 3, + "VertexOrderCw": 4, + "VertexOrderCcw": 5, + "PixelCenterInteger": 6, + "OriginUpperLeft": 7, + "OriginLowerLeft": 8, + "EarlyFragmentTests": 9, + "PointMode": 10, + "Xfb": 11, + "DepthReplacing": 12, + "DepthGreater": 14, + "DepthLess": 15, + "DepthUnchanged": 16, + "LocalSize": 17, + "LocalSizeHint": 18, + "InputPoints": 19, + "InputLines": 20, + "InputLinesAdjacency": 21, + "Triangles": 22, + "InputTrianglesAdjacency": 23, + "Quads": 24, + "Isolines": 25, + "OutputVertices": 26, + "OutputPoints": 27, + "OutputLineStrip": 28, + "OutputTriangleStrip": 29, + "VecTypeHint": 30, + "ContractionOff": 31, + "Initializer": 33, + "Finalizer": 34, + "SubgroupSize": 35, + "SubgroupsPerWorkgroup": 36, + "PostDepthCoverage": 4446, + "StencilRefReplacingEXT": 5027 + } + }, + { + "Name": "StorageClass", + "Type": "Value", + "Values": + { + "UniformConstant": 0, + "Input": 1, + "Uniform": 2, + "Output": 3, + "Workgroup": 4, + "CrossWorkgroup": 5, + "Private": 6, + "Function": 7, + "Generic": 8, + "PushConstant": 9, + "AtomicCounter": 10, + "Image": 11, + "StorageBuffer": 12 + } + }, + { + "Name": "Dim", + "Type": "Value", + "Values": + { + "Dim1D": 0, + "Dim2D": 1, + "Dim3D": 2, + "Cube": 3, + "Rect": 4, + "Buffer": 5, + "SubpassData": 6 + } + }, + { + "Name": "SamplerAddressingMode", + "Type": "Value", + "Values": + { + "None": 0, + "ClampToEdge": 1, + "Clamp": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "SamplerFilterMode", + "Type": "Value", + "Values": + { + "Nearest": 0, + "Linear": 1 + } + }, + { + "Name": "ImageFormat", + "Type": "Value", + "Values": + { + "Unknown": 0, + "Rgba32f": 1, + "Rgba16f": 2, + "R32f": 3, + "Rgba8": 4, + "Rgba8Snorm": 5, + "Rg32f": 6, + "Rg16f": 7, + "R11fG11fB10f": 8, + "R16f": 9, + "Rgba16": 10, + "Rgb10A2": 11, + "Rg16": 12, + "Rg8": 13, + "R16": 14, + "R8": 15, + "Rgba16Snorm": 16, + "Rg16Snorm": 17, + "Rg8Snorm": 18, + "R16Snorm": 19, + "R8Snorm": 20, + "Rgba32i": 21, + "Rgba16i": 22, + "Rgba8i": 23, + "R32i": 24, + "Rg32i": 25, + "Rg16i": 26, + "Rg8i": 27, + "R16i": 28, + "R8i": 29, + "Rgba32ui": 30, + "Rgba16ui": 31, + "Rgba8ui": 32, + "R32ui": 33, + "Rgb10a2ui": 34, + "Rg32ui": 35, + "Rg16ui": 36, + "Rg8ui": 37, + "R16ui": 38, + "R8ui": 39 + } + }, + { + "Name": "ImageChannelOrder", + "Type": "Value", + "Values": + { + "R": 0, + "A": 1, + "RG": 2, + "RA": 3, + "RGB": 4, + "RGBA": 5, + "BGRA": 6, + "ARGB": 7, + "Intensity": 8, + "Luminance": 9, + "Rx": 10, + "RGx": 11, + "RGBx": 12, + "Depth": 13, + "DepthStencil": 14, + "sRGB": 15, + "sRGBx": 16, + "sRGBA": 17, + "sBGRA": 18, + "ABGR": 19 + } + }, + { + "Name": "ImageChannelDataType", + "Type": "Value", + "Values": + { + "SnormInt8": 0, + "SnormInt16": 1, + "UnormInt8": 2, + "UnormInt16": 3, + "UnormShort565": 4, + "UnormShort555": 5, + "UnormInt101010": 6, + "SignedInt8": 7, + "SignedInt16": 8, + "SignedInt32": 9, + "UnsignedInt8": 10, + "UnsignedInt16": 11, + "UnsignedInt32": 12, + "HalfFloat": 13, + "Float": 14, + "UnormInt24": 15, + "UnormInt101010_2": 16 + } + }, + { + "Name": "ImageOperands", + "Type": "Bit", + "Values": + { + "Bias": 0, + "Lod": 1, + "Grad": 2, + "ConstOffset": 3, + "Offset": 4, + "ConstOffsets": 5, + "Sample": 6, + "MinLod": 7 + } + }, + { + "Name": "FPFastMathMode", + "Type": "Bit", + "Values": + { + "NotNaN": 0, + "NotInf": 1, + "NSZ": 2, + "AllowRecip": 3, + "Fast": 4 + } + }, + { + "Name": "FPRoundingMode", + "Type": "Value", + "Values": + { + "RTE": 0, + "RTZ": 1, + "RTP": 2, + "RTN": 3 + } + }, + { + "Name": "LinkageType", + "Type": "Value", + "Values": + { + "Export": 0, + "Import": 1 + } + }, + { + "Name": "AccessQualifier", + "Type": "Value", + "Values": + { + "ReadOnly": 0, + "WriteOnly": 1, + "ReadWrite": 2 + } + }, + { + "Name": "FunctionParameterAttribute", + "Type": "Value", + "Values": + { + "Zext": 0, + "Sext": 1, + "ByVal": 2, + "Sret": 3, + "NoAlias": 4, + "NoCapture": 5, + "NoWrite": 6, + "NoReadWrite": 7 + } + }, + { + "Name": "Decoration", + "Type": "Value", + "Values": + { + "RelaxedPrecision": 0, + "SpecId": 1, + "Block": 2, + "BufferBlock": 3, + "RowMajor": 4, + "ColMajor": 5, + "ArrayStride": 6, + "MatrixStride": 7, + "GLSLShared": 8, + "GLSLPacked": 9, + "CPacked": 10, + "BuiltIn": 11, + "NoPerspective": 13, + "Flat": 14, + "Patch": 15, + "Centroid": 16, + "Sample": 17, + "Invariant": 18, + "Restrict": 19, + "Aliased": 20, + "Volatile": 21, + "Constant": 22, + "Coherent": 23, + "NonWritable": 24, + "NonReadable": 25, + "Uniform": 26, + "SaturatedConversion": 28, + "Stream": 29, + "Location": 30, + "Component": 31, + "Index": 32, + "Binding": 33, + "DescriptorSet": 34, + "Offset": 35, + "XfbBuffer": 36, + "XfbStride": 37, + "FuncParamAttr": 38, + "FPRoundingMode": 39, + "FPFastMathMode": 40, + "LinkageAttributes": 41, + "NoContraction": 42, + "InputAttachmentIndex": 43, + "Alignment": 44, + "MaxByteOffset": 45, + "ExplicitInterpAMD": 4999, + "OverrideCoverageNV": 5248, + "PassthroughNV": 5250, + "ViewportRelativeNV": 5252, + "SecondaryViewportRelativeNV": 5256, + "HlslCounterBufferGOOGLE": 5634, + "HlslSemanticGOOGLE": 5635 + } + }, + { + "Name": "BuiltIn", + "Type": "Value", + "Values": + { + "Position": 0, + "PointSize": 1, + "ClipDistance": 3, + "CullDistance": 4, + "VertexId": 5, + "InstanceId": 6, + "PrimitiveId": 7, + "InvocationId": 8, + "Layer": 9, + "ViewportIndex": 10, + "TessLevelOuter": 11, + "TessLevelInner": 12, + "TessCoord": 13, + "PatchVertices": 14, + "FragCoord": 15, + "PointCoord": 16, + "FrontFacing": 17, + "SampleId": 18, + "SamplePosition": 19, + "SampleMask": 20, + "FragDepth": 22, + "HelperInvocation": 23, + "NumWorkgroups": 24, + "WorkgroupSize": 25, + "WorkgroupId": 26, + "LocalInvocationId": 27, + "GlobalInvocationId": 28, + "LocalInvocationIndex": 29, + "WorkDim": 30, + "GlobalSize": 31, + "EnqueuedWorkgroupSize": 32, + "GlobalOffset": 33, + "GlobalLinearId": 34, + "SubgroupSize": 36, + "SubgroupMaxSize": 37, + "NumSubgroups": 38, + "NumEnqueuedSubgroups": 39, + "SubgroupId": 40, + "SubgroupLocalInvocationId": 41, + "VertexIndex": 42, + "InstanceIndex": 43, + "SubgroupEqMaskKHR": 4416, + "SubgroupGeMaskKHR": 4417, + "SubgroupGtMaskKHR": 4418, + "SubgroupLeMaskKHR": 4419, + "SubgroupLtMaskKHR": 4420, + "BaseVertex": 4424, + "BaseInstance": 4425, + "DrawIndex": 4426, + "DeviceIndex": 4438, + "ViewIndex": 4440, + "BaryCoordNoPerspAMD": 4992, + "BaryCoordNoPerspCentroidAMD": 4993, + "BaryCoordNoPerspSampleAMD": 4994, + "BaryCoordSmoothAMD": 4995, + "BaryCoordSmoothCentroidAMD": 4996, + "BaryCoordSmoothSampleAMD": 4997, + "BaryCoordPullModelAMD": 4998, + "FragStencilRefEXT": 5014, + "ViewportMaskNV": 5253, + "SecondaryPositionNV": 5257, + "SecondaryViewportMaskNV": 5258, + "PositionPerViewNV": 5261, + "ViewportMaskPerViewNV": 5262 + } + }, + { + "Name": "SelectionControl", + "Type": "Bit", + "Values": + { + "Flatten": 0, + "DontFlatten": 1 + } + }, + { + "Name": "LoopControl", + "Type": "Bit", + "Values": + { + "Unroll": 0, + "DontUnroll": 1, + "DependencyInfinite": 2, + "DependencyLength": 3 + } + }, + { + "Name": "FunctionControl", + "Type": "Bit", + "Values": + { + "Inline": 0, + "DontInline": 1, + "Pure": 2, + "Const": 3 + } + }, + { + "Name": "MemorySemantics", + "Type": "Bit", + "Values": + { + "Acquire": 1, + "Release": 2, + "AcquireRelease": 3, + "SequentiallyConsistent": 4, + "UniformMemory": 6, + "SubgroupMemory": 7, + "WorkgroupMemory": 8, + "CrossWorkgroupMemory": 9, + "AtomicCounterMemory": 10, + "ImageMemory": 11 + } + }, + { + "Name": "MemoryAccess", + "Type": "Bit", + "Values": + { + "Volatile": 0, + "Aligned": 1, + "Nontemporal": 2 + } + }, + { + "Name": "Scope", + "Type": "Value", + "Values": + { + "CrossDevice": 0, + "Device": 1, + "Workgroup": 2, + "Subgroup": 3, + "Invocation": 4 + } + }, + { + "Name": "GroupOperation", + "Type": "Value", + "Values": + { + "Reduce": 0, + "InclusiveScan": 1, + "ExclusiveScan": 2 + } + }, + { + "Name": "KernelEnqueueFlags", + "Type": "Value", + "Values": + { + "NoWait": 0, + "WaitKernel": 1, + "WaitWorkGroup": 2 + } + }, + { + "Name": "KernelProfilingInfo", + "Type": "Bit", + "Values": + { + "CmdExecTime": 0 + } + }, + { + "Name": "Capability", + "Type": "Value", + "Values": + { + "Matrix": 0, + "Shader": 1, + "Geometry": 2, + "Tessellation": 3, + "Addresses": 4, + "Linkage": 5, + "Kernel": 6, + "Vector16": 7, + "Float16Buffer": 8, + "Float16": 9, + "Float64": 10, + "Int64": 11, + "Int64Atomics": 12, + "ImageBasic": 13, + "ImageReadWrite": 14, + "ImageMipmap": 15, + "Pipes": 17, + "Groups": 18, + "DeviceEnqueue": 19, + "LiteralSampler": 20, + "AtomicStorage": 21, + "Int16": 22, + "TessellationPointSize": 23, + "GeometryPointSize": 24, + "ImageGatherExtended": 25, + "StorageImageMultisample": 27, + "UniformBufferArrayDynamicIndexing": 28, + "SampledImageArrayDynamicIndexing": 29, + "StorageBufferArrayDynamicIndexing": 30, + "StorageImageArrayDynamicIndexing": 31, + "ClipDistance": 32, + "CullDistance": 33, + "ImageCubeArray": 34, + "SampleRateShading": 35, + "ImageRect": 36, + "SampledRect": 37, + "GenericPointer": 38, + "Int8": 39, + "InputAttachment": 40, + "SparseResidency": 41, + "MinLod": 42, + "Sampled1D": 43, + "Image1D": 44, + "SampledCubeArray": 45, + "SampledBuffer": 46, + "ImageBuffer": 47, + "ImageMSArray": 48, + "StorageImageExtendedFormats": 49, + "ImageQuery": 50, + "DerivativeControl": 51, + "InterpolationFunction": 52, + "TransformFeedback": 53, + "GeometryStreams": 54, + "StorageImageReadWithoutFormat": 55, + "StorageImageWriteWithoutFormat": 56, + "MultiViewport": 57, + "SubgroupDispatch": 58, + "NamedBarrier": 59, + "PipeStorage": 60, + "SubgroupBallotKHR": 4423, + "DrawParameters": 4427, + "SubgroupVoteKHR": 4431, + "StorageBuffer16BitAccess": 4433, + "StorageUniformBufferBlock16": 4433, + "StorageUniform16": 4434, + "UniformAndStorageBuffer16BitAccess": 4434, + "StoragePushConstant16": 4435, + "StorageInputOutput16": 4436, + "DeviceGroup": 4437, + "MultiView": 4439, + "VariablePointersStorageBuffer": 4441, + "VariablePointers": 4442, + "AtomicStorageOps": 4445, + "SampleMaskPostDepthCoverage": 4447, + "ImageGatherBiasLodAMD": 5009, + "FragmentMaskAMD": 5010, + "StencilExportEXT": 5013, + "ImageReadWriteLodAMD": 5015, + "SampleMaskOverrideCoverageNV": 5249, + "GeometryShaderPassthroughNV": 5251, + "ShaderViewportIndexLayerEXT": 5254, + "ShaderViewportIndexLayerNV": 5254, + "ShaderViewportMaskNV": 5255, + "ShaderStereoViewNV": 5259, + "PerViewAttributesNV": 5260, + "SubgroupShuffleINTEL": 5568, + "SubgroupBufferBlockIOINTEL": 5569, + "SubgroupImageBlockIOINTEL": 5570 + } + }, + { + "Name": "Op", + "Type": "Value", + "Values": + { + "OpNop": 0, + "OpUndef": 1, + "OpSourceContinued": 2, + "OpSource": 3, + "OpSourceExtension": 4, + "OpName": 5, + "OpMemberName": 6, + "OpString": 7, + "OpLine": 8, + "OpExtension": 10, + "OpExtInstImport": 11, + "OpExtInst": 12, + "OpMemoryModel": 14, + "OpEntryPoint": 15, + "OpExecutionMode": 16, + "OpCapability": 17, + "OpTypeVoid": 19, + "OpTypeBool": 20, + "OpTypeInt": 21, + "OpTypeFloat": 22, + "OpTypeVector": 23, + "OpTypeMatrix": 24, + "OpTypeImage": 25, + "OpTypeSampler": 26, + "OpTypeSampledImage": 27, + "OpTypeArray": 28, + "OpTypeRuntimeArray": 29, + "OpTypeStruct": 30, + "OpTypeOpaque": 31, + "OpTypePointer": 32, + "OpTypeFunction": 33, + "OpTypeEvent": 34, + "OpTypeDeviceEvent": 35, + "OpTypeReserveId": 36, + "OpTypeQueue": 37, + "OpTypePipe": 38, + "OpTypeForwardPointer": 39, + "OpConstantTrue": 41, + "OpConstantFalse": 42, + "OpConstant": 43, + "OpConstantComposite": 44, + "OpConstantSampler": 45, + "OpConstantNull": 46, + "OpSpecConstantTrue": 48, + "OpSpecConstantFalse": 49, + "OpSpecConstant": 50, + "OpSpecConstantComposite": 51, + "OpSpecConstantOp": 52, + "OpFunction": 54, + "OpFunctionParameter": 55, + "OpFunctionEnd": 56, + "OpFunctionCall": 57, + "OpVariable": 59, + "OpImageTexelPointer": 60, + "OpLoad": 61, + "OpStore": 62, + "OpCopyMemory": 63, + "OpCopyMemorySized": 64, + "OpAccessChain": 65, + "OpInBoundsAccessChain": 66, + "OpPtrAccessChain": 67, + "OpArrayLength": 68, + "OpGenericPtrMemSemantics": 69, + "OpInBoundsPtrAccessChain": 70, + "OpDecorate": 71, + "OpMemberDecorate": 72, + "OpDecorationGroup": 73, + "OpGroupDecorate": 74, + "OpGroupMemberDecorate": 75, + "OpVectorExtractDynamic": 77, + "OpVectorInsertDynamic": 78, + "OpVectorShuffle": 79, + "OpCompositeConstruct": 80, + "OpCompositeExtract": 81, + "OpCompositeInsert": 82, + "OpCopyObject": 83, + "OpTranspose": 84, + "OpSampledImage": 86, + "OpImageSampleImplicitLod": 87, + "OpImageSampleExplicitLod": 88, + "OpImageSampleDrefImplicitLod": 89, + "OpImageSampleDrefExplicitLod": 90, + "OpImageSampleProjImplicitLod": 91, + "OpImageSampleProjExplicitLod": 92, + "OpImageSampleProjDrefImplicitLod": 93, + "OpImageSampleProjDrefExplicitLod": 94, + "OpImageFetch": 95, + "OpImageGather": 96, + "OpImageDrefGather": 97, + "OpImageRead": 98, + "OpImageWrite": 99, + "OpImage": 100, + "OpImageQueryFormat": 101, + "OpImageQueryOrder": 102, + "OpImageQuerySizeLod": 103, + "OpImageQuerySize": 104, + "OpImageQueryLod": 105, + "OpImageQueryLevels": 106, + "OpImageQuerySamples": 107, + "OpConvertFToU": 109, + "OpConvertFToS": 110, + "OpConvertSToF": 111, + "OpConvertUToF": 112, + "OpUConvert": 113, + "OpSConvert": 114, + "OpFConvert": 115, + "OpQuantizeToF16": 116, + "OpConvertPtrToU": 117, + "OpSatConvertSToU": 118, + "OpSatConvertUToS": 119, + "OpConvertUToPtr": 120, + "OpPtrCastToGeneric": 121, + "OpGenericCastToPtr": 122, + "OpGenericCastToPtrExplicit": 123, + "OpBitcast": 124, + "OpSNegate": 126, + "OpFNegate": 127, + "OpIAdd": 128, + "OpFAdd": 129, + "OpISub": 130, + "OpFSub": 131, + "OpIMul": 132, + "OpFMul": 133, + "OpUDiv": 134, + "OpSDiv": 135, + "OpFDiv": 136, + "OpUMod": 137, + "OpSRem": 138, + "OpSMod": 139, + "OpFRem": 140, + "OpFMod": 141, + "OpVectorTimesScalar": 142, + "OpMatrixTimesScalar": 143, + "OpVectorTimesMatrix": 144, + "OpMatrixTimesVector": 145, + "OpMatrixTimesMatrix": 146, + "OpOuterProduct": 147, + "OpDot": 148, + "OpIAddCarry": 149, + "OpISubBorrow": 150, + "OpUMulExtended": 151, + "OpSMulExtended": 152, + "OpAny": 154, + "OpAll": 155, + "OpIsNan": 156, + "OpIsInf": 157, + "OpIsFinite": 158, + "OpIsNormal": 159, + "OpSignBitSet": 160, + "OpLessOrGreater": 161, + "OpOrdered": 162, + "OpUnordered": 163, + "OpLogicalEqual": 164, + "OpLogicalNotEqual": 165, + "OpLogicalOr": 166, + "OpLogicalAnd": 167, + "OpLogicalNot": 168, + "OpSelect": 169, + "OpIEqual": 170, + "OpINotEqual": 171, + "OpUGreaterThan": 172, + "OpSGreaterThan": 173, + "OpUGreaterThanEqual": 174, + "OpSGreaterThanEqual": 175, + "OpULessThan": 176, + "OpSLessThan": 177, + "OpULessThanEqual": 178, + "OpSLessThanEqual": 179, + "OpFOrdEqual": 180, + "OpFUnordEqual": 181, + "OpFOrdNotEqual": 182, + "OpFUnordNotEqual": 183, + "OpFOrdLessThan": 184, + "OpFUnordLessThan": 185, + "OpFOrdGreaterThan": 186, + "OpFUnordGreaterThan": 187, + "OpFOrdLessThanEqual": 188, + "OpFUnordLessThanEqual": 189, + "OpFOrdGreaterThanEqual": 190, + "OpFUnordGreaterThanEqual": 191, + "OpShiftRightLogical": 194, + "OpShiftRightArithmetic": 195, + "OpShiftLeftLogical": 196, + "OpBitwiseOr": 197, + "OpBitwiseXor": 198, + "OpBitwiseAnd": 199, + "OpNot": 200, + "OpBitFieldInsert": 201, + "OpBitFieldSExtract": 202, + "OpBitFieldUExtract": 203, + "OpBitReverse": 204, + "OpBitCount": 205, + "OpDPdx": 207, + "OpDPdy": 208, + "OpFwidth": 209, + "OpDPdxFine": 210, + "OpDPdyFine": 211, + "OpFwidthFine": 212, + "OpDPdxCoarse": 213, + "OpDPdyCoarse": 214, + "OpFwidthCoarse": 215, + "OpEmitVertex": 218, + "OpEndPrimitive": 219, + "OpEmitStreamVertex": 220, + "OpEndStreamPrimitive": 221, + "OpControlBarrier": 224, + "OpMemoryBarrier": 225, + "OpAtomicLoad": 227, + "OpAtomicStore": 228, + "OpAtomicExchange": 229, + "OpAtomicCompareExchange": 230, + "OpAtomicCompareExchangeWeak": 231, + "OpAtomicIIncrement": 232, + "OpAtomicIDecrement": 233, + "OpAtomicIAdd": 234, + "OpAtomicISub": 235, + "OpAtomicSMin": 236, + "OpAtomicUMin": 237, + "OpAtomicSMax": 238, + "OpAtomicUMax": 239, + "OpAtomicAnd": 240, + "OpAtomicOr": 241, + "OpAtomicXor": 242, + "OpPhi": 245, + "OpLoopMerge": 246, + "OpSelectionMerge": 247, + "OpLabel": 248, + "OpBranch": 249, + "OpBranchConditional": 250, + "OpSwitch": 251, + "OpKill": 252, + "OpReturn": 253, + "OpReturnValue": 254, + "OpUnreachable": 255, + "OpLifetimeStart": 256, + "OpLifetimeStop": 257, + "OpGroupAsyncCopy": 259, + "OpGroupWaitEvents": 260, + "OpGroupAll": 261, + "OpGroupAny": 262, + "OpGroupBroadcast": 263, + "OpGroupIAdd": 264, + "OpGroupFAdd": 265, + "OpGroupFMin": 266, + "OpGroupUMin": 267, + "OpGroupSMin": 268, + "OpGroupFMax": 269, + "OpGroupUMax": 270, + "OpGroupSMax": 271, + "OpReadPipe": 274, + "OpWritePipe": 275, + "OpReservedReadPipe": 276, + "OpReservedWritePipe": 277, + "OpReserveReadPipePackets": 278, + "OpReserveWritePipePackets": 279, + "OpCommitReadPipe": 280, + "OpCommitWritePipe": 281, + "OpIsValidReserveId": 282, + "OpGetNumPipePackets": 283, + "OpGetMaxPipePackets": 284, + "OpGroupReserveReadPipePackets": 285, + "OpGroupReserveWritePipePackets": 286, + "OpGroupCommitReadPipe": 287, + "OpGroupCommitWritePipe": 288, + "OpEnqueueMarker": 291, + "OpEnqueueKernel": 292, + "OpGetKernelNDrangeSubGroupCount": 293, + "OpGetKernelNDrangeMaxSubGroupSize": 294, + "OpGetKernelWorkGroupSize": 295, + "OpGetKernelPreferredWorkGroupSizeMultiple": 296, + "OpRetainEvent": 297, + "OpReleaseEvent": 298, + "OpCreateUserEvent": 299, + "OpIsValidEvent": 300, + "OpSetUserEventStatus": 301, + "OpCaptureEventProfilingInfo": 302, + "OpGetDefaultQueue": 303, + "OpBuildNDRange": 304, + "OpImageSparseSampleImplicitLod": 305, + "OpImageSparseSampleExplicitLod": 306, + "OpImageSparseSampleDrefImplicitLod": 307, + "OpImageSparseSampleDrefExplicitLod": 308, + "OpImageSparseSampleProjImplicitLod": 309, + "OpImageSparseSampleProjExplicitLod": 310, + "OpImageSparseSampleProjDrefImplicitLod": 311, + "OpImageSparseSampleProjDrefExplicitLod": 312, + "OpImageSparseFetch": 313, + "OpImageSparseGather": 314, + "OpImageSparseDrefGather": 315, + "OpImageSparseTexelsResident": 316, + "OpNoLine": 317, + "OpAtomicFlagTestAndSet": 318, + "OpAtomicFlagClear": 319, + "OpImageSparseRead": 320, + "OpSizeOf": 321, + "OpTypePipeStorage": 322, + "OpConstantPipeStorage": 323, + "OpCreatePipeFromPipeStorage": 324, + "OpGetKernelLocalSizeForSubgroupCount": 325, + "OpGetKernelMaxNumSubgroups": 326, + "OpTypeNamedBarrier": 327, + "OpNamedBarrierInitialize": 328, + "OpMemoryNamedBarrier": 329, + "OpModuleProcessed": 330, + "OpDecorateId": 332, + "OpSubgroupBallotKHR": 4421, + "OpSubgroupFirstInvocationKHR": 4422, + "OpSubgroupAllKHR": 4428, + "OpSubgroupAnyKHR": 4429, + "OpSubgroupAllEqualKHR": 4430, + "OpSubgroupReadInvocationKHR": 4432, + "OpGroupIAddNonUniformAMD": 5000, + "OpGroupFAddNonUniformAMD": 5001, + "OpGroupFMinNonUniformAMD": 5002, + "OpGroupUMinNonUniformAMD": 5003, + "OpGroupSMinNonUniformAMD": 5004, + "OpGroupFMaxNonUniformAMD": 5005, + "OpGroupUMaxNonUniformAMD": 5006, + "OpGroupSMaxNonUniformAMD": 5007, + "OpFragmentMaskFetchAMD": 5011, + "OpFragmentFetchAMD": 5012, + "OpSubgroupShuffleINTEL": 5571, + "OpSubgroupShuffleDownINTEL": 5572, + "OpSubgroupShuffleUpINTEL": 5573, + "OpSubgroupShuffleXorINTEL": 5574, + "OpSubgroupBlockReadINTEL": 5575, + "OpSubgroupBlockWriteINTEL": 5576, + "OpSubgroupImageBlockReadINTEL": 5577, + "OpSubgroupImageBlockWriteINTEL": 5578, + "OpDecorateStringGOOGLE": 5632, + "OpMemberDecorateStringGOOGLE": 5633 + } + } + ] + } +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.lua b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.lua new file mode 100644 index 0000000..ad34e0a --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.lua @@ -0,0 +1,971 @@ +-- Copyright (c) 2014-2018 The Khronos Group Inc. +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and/or associated documentation files (the "Materials"), +-- to deal in the Materials without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Materials, and to permit persons to whom the +-- Materials are furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Materials. +-- +-- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +-- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +-- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +-- +-- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +-- IN THE MATERIALS. + +-- This header is automatically generated by the same tool that creates +-- the Binary Section of the SPIR-V specification. + +-- Enumeration tokens for SPIR-V, in various styles: +-- C, C++, C++11, JSON, Lua, Python +-- +-- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +-- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +-- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +-- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +-- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +-- +-- Some tokens act like mask values, which can be OR'd together, +-- while others are mutually exclusive. The mask-like ones have +-- "Mask" in their name, and a parallel enum that has the shift +-- amount (1 << x) for each corresponding enumerant. + +spv = { + MagicNumber = 0x07230203, + Version = 0x00010100, + Revision = 8, + OpCodeMask = 0xffff, + WordCountShift = 16, + + SourceLanguage = { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + }, + + ExecutionModel = { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + }, + + AddressingModel = { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + }, + + MemoryModel = { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + }, + + ExecutionMode = { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + }, + + StorageClass = { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + }, + + Dim = { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + }, + + SamplerAddressingMode = { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + }, + + SamplerFilterMode = { + Nearest = 0, + Linear = 1, + }, + + ImageFormat = { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + }, + + ImageChannelOrder = { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + }, + + ImageChannelDataType = { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + }, + + ImageOperandsShift = { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + }, + + ImageOperandsMask = { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + }, + + FPFastMathModeShift = { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + }, + + FPFastMathModeMask = { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + }, + + FPRoundingMode = { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + }, + + LinkageType = { + Export = 0, + Import = 1, + }, + + AccessQualifier = { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + }, + + FunctionParameterAttribute = { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + }, + + Decoration = { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + }, + + BuiltIn = { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + }, + + SelectionControlShift = { + Flatten = 0, + DontFlatten = 1, + }, + + SelectionControlMask = { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + }, + + LoopControlShift = { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + }, + + LoopControlMask = { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + }, + + FunctionControlShift = { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + }, + + FunctionControlMask = { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + }, + + MemorySemanticsShift = { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + }, + + MemorySemanticsMask = { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + }, + + MemoryAccessShift = { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + }, + + MemoryAccessMask = { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + }, + + Scope = { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + }, + + GroupOperation = { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + }, + + KernelEnqueueFlags = { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + }, + + KernelProfilingInfoShift = { + CmdExecTime = 0, + }, + + KernelProfilingInfoMask = { + MaskNone = 0, + CmdExecTime = 0x00000001, + }, + + Capability = { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + }, + + Op = { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + }, + +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.py b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.py new file mode 100644 index 0000000..519a597 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.1/spirv.py @@ -0,0 +1,971 @@ +# Copyright (c) 2014-2018 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +# This header is automatically generated by the same tool that creates +# the Binary Section of the SPIR-V specification. + +# Enumeration tokens for SPIR-V, in various styles: +# C, C++, C++11, JSON, Lua, Python +# +# - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +# - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +# - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +# - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +# - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +# +# Some tokens act like mask values, which can be OR'd together, +# while others are mutually exclusive. The mask-like ones have +# "Mask" in their name, and a parallel enum that has the shift +# amount (1 << x) for each corresponding enumerant. + +spv = { + 'MagicNumber' : 0x07230203, + 'Version' : 0x00010100, + 'Revision' : 8, + 'OpCodeMask' : 0xffff, + 'WordCountShift' : 16, + + 'SourceLanguage' : { + 'Unknown' : 0, + 'ESSL' : 1, + 'GLSL' : 2, + 'OpenCL_C' : 3, + 'OpenCL_CPP' : 4, + 'HLSL' : 5, + }, + + 'ExecutionModel' : { + 'Vertex' : 0, + 'TessellationControl' : 1, + 'TessellationEvaluation' : 2, + 'Geometry' : 3, + 'Fragment' : 4, + 'GLCompute' : 5, + 'Kernel' : 6, + }, + + 'AddressingModel' : { + 'Logical' : 0, + 'Physical32' : 1, + 'Physical64' : 2, + }, + + 'MemoryModel' : { + 'Simple' : 0, + 'GLSL450' : 1, + 'OpenCL' : 2, + }, + + 'ExecutionMode' : { + 'Invocations' : 0, + 'SpacingEqual' : 1, + 'SpacingFractionalEven' : 2, + 'SpacingFractionalOdd' : 3, + 'VertexOrderCw' : 4, + 'VertexOrderCcw' : 5, + 'PixelCenterInteger' : 6, + 'OriginUpperLeft' : 7, + 'OriginLowerLeft' : 8, + 'EarlyFragmentTests' : 9, + 'PointMode' : 10, + 'Xfb' : 11, + 'DepthReplacing' : 12, + 'DepthGreater' : 14, + 'DepthLess' : 15, + 'DepthUnchanged' : 16, + 'LocalSize' : 17, + 'LocalSizeHint' : 18, + 'InputPoints' : 19, + 'InputLines' : 20, + 'InputLinesAdjacency' : 21, + 'Triangles' : 22, + 'InputTrianglesAdjacency' : 23, + 'Quads' : 24, + 'Isolines' : 25, + 'OutputVertices' : 26, + 'OutputPoints' : 27, + 'OutputLineStrip' : 28, + 'OutputTriangleStrip' : 29, + 'VecTypeHint' : 30, + 'ContractionOff' : 31, + 'Initializer' : 33, + 'Finalizer' : 34, + 'SubgroupSize' : 35, + 'SubgroupsPerWorkgroup' : 36, + 'PostDepthCoverage' : 4446, + 'StencilRefReplacingEXT' : 5027, + }, + + 'StorageClass' : { + 'UniformConstant' : 0, + 'Input' : 1, + 'Uniform' : 2, + 'Output' : 3, + 'Workgroup' : 4, + 'CrossWorkgroup' : 5, + 'Private' : 6, + 'Function' : 7, + 'Generic' : 8, + 'PushConstant' : 9, + 'AtomicCounter' : 10, + 'Image' : 11, + 'StorageBuffer' : 12, + }, + + 'Dim' : { + 'Dim1D' : 0, + 'Dim2D' : 1, + 'Dim3D' : 2, + 'Cube' : 3, + 'Rect' : 4, + 'Buffer' : 5, + 'SubpassData' : 6, + }, + + 'SamplerAddressingMode' : { + 'None' : 0, + 'ClampToEdge' : 1, + 'Clamp' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'SamplerFilterMode' : { + 'Nearest' : 0, + 'Linear' : 1, + }, + + 'ImageFormat' : { + 'Unknown' : 0, + 'Rgba32f' : 1, + 'Rgba16f' : 2, + 'R32f' : 3, + 'Rgba8' : 4, + 'Rgba8Snorm' : 5, + 'Rg32f' : 6, + 'Rg16f' : 7, + 'R11fG11fB10f' : 8, + 'R16f' : 9, + 'Rgba16' : 10, + 'Rgb10A2' : 11, + 'Rg16' : 12, + 'Rg8' : 13, + 'R16' : 14, + 'R8' : 15, + 'Rgba16Snorm' : 16, + 'Rg16Snorm' : 17, + 'Rg8Snorm' : 18, + 'R16Snorm' : 19, + 'R8Snorm' : 20, + 'Rgba32i' : 21, + 'Rgba16i' : 22, + 'Rgba8i' : 23, + 'R32i' : 24, + 'Rg32i' : 25, + 'Rg16i' : 26, + 'Rg8i' : 27, + 'R16i' : 28, + 'R8i' : 29, + 'Rgba32ui' : 30, + 'Rgba16ui' : 31, + 'Rgba8ui' : 32, + 'R32ui' : 33, + 'Rgb10a2ui' : 34, + 'Rg32ui' : 35, + 'Rg16ui' : 36, + 'Rg8ui' : 37, + 'R16ui' : 38, + 'R8ui' : 39, + }, + + 'ImageChannelOrder' : { + 'R' : 0, + 'A' : 1, + 'RG' : 2, + 'RA' : 3, + 'RGB' : 4, + 'RGBA' : 5, + 'BGRA' : 6, + 'ARGB' : 7, + 'Intensity' : 8, + 'Luminance' : 9, + 'Rx' : 10, + 'RGx' : 11, + 'RGBx' : 12, + 'Depth' : 13, + 'DepthStencil' : 14, + 'sRGB' : 15, + 'sRGBx' : 16, + 'sRGBA' : 17, + 'sBGRA' : 18, + 'ABGR' : 19, + }, + + 'ImageChannelDataType' : { + 'SnormInt8' : 0, + 'SnormInt16' : 1, + 'UnormInt8' : 2, + 'UnormInt16' : 3, + 'UnormShort565' : 4, + 'UnormShort555' : 5, + 'UnormInt101010' : 6, + 'SignedInt8' : 7, + 'SignedInt16' : 8, + 'SignedInt32' : 9, + 'UnsignedInt8' : 10, + 'UnsignedInt16' : 11, + 'UnsignedInt32' : 12, + 'HalfFloat' : 13, + 'Float' : 14, + 'UnormInt24' : 15, + 'UnormInt101010_2' : 16, + }, + + 'ImageOperandsShift' : { + 'Bias' : 0, + 'Lod' : 1, + 'Grad' : 2, + 'ConstOffset' : 3, + 'Offset' : 4, + 'ConstOffsets' : 5, + 'Sample' : 6, + 'MinLod' : 7, + }, + + 'ImageOperandsMask' : { + 'MaskNone' : 0, + 'Bias' : 0x00000001, + 'Lod' : 0x00000002, + 'Grad' : 0x00000004, + 'ConstOffset' : 0x00000008, + 'Offset' : 0x00000010, + 'ConstOffsets' : 0x00000020, + 'Sample' : 0x00000040, + 'MinLod' : 0x00000080, + }, + + 'FPFastMathModeShift' : { + 'NotNaN' : 0, + 'NotInf' : 1, + 'NSZ' : 2, + 'AllowRecip' : 3, + 'Fast' : 4, + }, + + 'FPFastMathModeMask' : { + 'MaskNone' : 0, + 'NotNaN' : 0x00000001, + 'NotInf' : 0x00000002, + 'NSZ' : 0x00000004, + 'AllowRecip' : 0x00000008, + 'Fast' : 0x00000010, + }, + + 'FPRoundingMode' : { + 'RTE' : 0, + 'RTZ' : 1, + 'RTP' : 2, + 'RTN' : 3, + }, + + 'LinkageType' : { + 'Export' : 0, + 'Import' : 1, + }, + + 'AccessQualifier' : { + 'ReadOnly' : 0, + 'WriteOnly' : 1, + 'ReadWrite' : 2, + }, + + 'FunctionParameterAttribute' : { + 'Zext' : 0, + 'Sext' : 1, + 'ByVal' : 2, + 'Sret' : 3, + 'NoAlias' : 4, + 'NoCapture' : 5, + 'NoWrite' : 6, + 'NoReadWrite' : 7, + }, + + 'Decoration' : { + 'RelaxedPrecision' : 0, + 'SpecId' : 1, + 'Block' : 2, + 'BufferBlock' : 3, + 'RowMajor' : 4, + 'ColMajor' : 5, + 'ArrayStride' : 6, + 'MatrixStride' : 7, + 'GLSLShared' : 8, + 'GLSLPacked' : 9, + 'CPacked' : 10, + 'BuiltIn' : 11, + 'NoPerspective' : 13, + 'Flat' : 14, + 'Patch' : 15, + 'Centroid' : 16, + 'Sample' : 17, + 'Invariant' : 18, + 'Restrict' : 19, + 'Aliased' : 20, + 'Volatile' : 21, + 'Constant' : 22, + 'Coherent' : 23, + 'NonWritable' : 24, + 'NonReadable' : 25, + 'Uniform' : 26, + 'SaturatedConversion' : 28, + 'Stream' : 29, + 'Location' : 30, + 'Component' : 31, + 'Index' : 32, + 'Binding' : 33, + 'DescriptorSet' : 34, + 'Offset' : 35, + 'XfbBuffer' : 36, + 'XfbStride' : 37, + 'FuncParamAttr' : 38, + 'FPRoundingMode' : 39, + 'FPFastMathMode' : 40, + 'LinkageAttributes' : 41, + 'NoContraction' : 42, + 'InputAttachmentIndex' : 43, + 'Alignment' : 44, + 'MaxByteOffset' : 45, + 'ExplicitInterpAMD' : 4999, + 'OverrideCoverageNV' : 5248, + 'PassthroughNV' : 5250, + 'ViewportRelativeNV' : 5252, + 'SecondaryViewportRelativeNV' : 5256, + 'HlslCounterBufferGOOGLE' : 5634, + 'HlslSemanticGOOGLE' : 5635, + }, + + 'BuiltIn' : { + 'Position' : 0, + 'PointSize' : 1, + 'ClipDistance' : 3, + 'CullDistance' : 4, + 'VertexId' : 5, + 'InstanceId' : 6, + 'PrimitiveId' : 7, + 'InvocationId' : 8, + 'Layer' : 9, + 'ViewportIndex' : 10, + 'TessLevelOuter' : 11, + 'TessLevelInner' : 12, + 'TessCoord' : 13, + 'PatchVertices' : 14, + 'FragCoord' : 15, + 'PointCoord' : 16, + 'FrontFacing' : 17, + 'SampleId' : 18, + 'SamplePosition' : 19, + 'SampleMask' : 20, + 'FragDepth' : 22, + 'HelperInvocation' : 23, + 'NumWorkgroups' : 24, + 'WorkgroupSize' : 25, + 'WorkgroupId' : 26, + 'LocalInvocationId' : 27, + 'GlobalInvocationId' : 28, + 'LocalInvocationIndex' : 29, + 'WorkDim' : 30, + 'GlobalSize' : 31, + 'EnqueuedWorkgroupSize' : 32, + 'GlobalOffset' : 33, + 'GlobalLinearId' : 34, + 'SubgroupSize' : 36, + 'SubgroupMaxSize' : 37, + 'NumSubgroups' : 38, + 'NumEnqueuedSubgroups' : 39, + 'SubgroupId' : 40, + 'SubgroupLocalInvocationId' : 41, + 'VertexIndex' : 42, + 'InstanceIndex' : 43, + 'SubgroupEqMaskKHR' : 4416, + 'SubgroupGeMaskKHR' : 4417, + 'SubgroupGtMaskKHR' : 4418, + 'SubgroupLeMaskKHR' : 4419, + 'SubgroupLtMaskKHR' : 4420, + 'BaseVertex' : 4424, + 'BaseInstance' : 4425, + 'DrawIndex' : 4426, + 'DeviceIndex' : 4438, + 'ViewIndex' : 4440, + 'BaryCoordNoPerspAMD' : 4992, + 'BaryCoordNoPerspCentroidAMD' : 4993, + 'BaryCoordNoPerspSampleAMD' : 4994, + 'BaryCoordSmoothAMD' : 4995, + 'BaryCoordSmoothCentroidAMD' : 4996, + 'BaryCoordSmoothSampleAMD' : 4997, + 'BaryCoordPullModelAMD' : 4998, + 'FragStencilRefEXT' : 5014, + 'ViewportMaskNV' : 5253, + 'SecondaryPositionNV' : 5257, + 'SecondaryViewportMaskNV' : 5258, + 'PositionPerViewNV' : 5261, + 'ViewportMaskPerViewNV' : 5262, + }, + + 'SelectionControlShift' : { + 'Flatten' : 0, + 'DontFlatten' : 1, + }, + + 'SelectionControlMask' : { + 'MaskNone' : 0, + 'Flatten' : 0x00000001, + 'DontFlatten' : 0x00000002, + }, + + 'LoopControlShift' : { + 'Unroll' : 0, + 'DontUnroll' : 1, + 'DependencyInfinite' : 2, + 'DependencyLength' : 3, + }, + + 'LoopControlMask' : { + 'MaskNone' : 0, + 'Unroll' : 0x00000001, + 'DontUnroll' : 0x00000002, + 'DependencyInfinite' : 0x00000004, + 'DependencyLength' : 0x00000008, + }, + + 'FunctionControlShift' : { + 'Inline' : 0, + 'DontInline' : 1, + 'Pure' : 2, + 'Const' : 3, + }, + + 'FunctionControlMask' : { + 'MaskNone' : 0, + 'Inline' : 0x00000001, + 'DontInline' : 0x00000002, + 'Pure' : 0x00000004, + 'Const' : 0x00000008, + }, + + 'MemorySemanticsShift' : { + 'Acquire' : 1, + 'Release' : 2, + 'AcquireRelease' : 3, + 'SequentiallyConsistent' : 4, + 'UniformMemory' : 6, + 'SubgroupMemory' : 7, + 'WorkgroupMemory' : 8, + 'CrossWorkgroupMemory' : 9, + 'AtomicCounterMemory' : 10, + 'ImageMemory' : 11, + }, + + 'MemorySemanticsMask' : { + 'MaskNone' : 0, + 'Acquire' : 0x00000002, + 'Release' : 0x00000004, + 'AcquireRelease' : 0x00000008, + 'SequentiallyConsistent' : 0x00000010, + 'UniformMemory' : 0x00000040, + 'SubgroupMemory' : 0x00000080, + 'WorkgroupMemory' : 0x00000100, + 'CrossWorkgroupMemory' : 0x00000200, + 'AtomicCounterMemory' : 0x00000400, + 'ImageMemory' : 0x00000800, + }, + + 'MemoryAccessShift' : { + 'Volatile' : 0, + 'Aligned' : 1, + 'Nontemporal' : 2, + }, + + 'MemoryAccessMask' : { + 'MaskNone' : 0, + 'Volatile' : 0x00000001, + 'Aligned' : 0x00000002, + 'Nontemporal' : 0x00000004, + }, + + 'Scope' : { + 'CrossDevice' : 0, + 'Device' : 1, + 'Workgroup' : 2, + 'Subgroup' : 3, + 'Invocation' : 4, + }, + + 'GroupOperation' : { + 'Reduce' : 0, + 'InclusiveScan' : 1, + 'ExclusiveScan' : 2, + }, + + 'KernelEnqueueFlags' : { + 'NoWait' : 0, + 'WaitKernel' : 1, + 'WaitWorkGroup' : 2, + }, + + 'KernelProfilingInfoShift' : { + 'CmdExecTime' : 0, + }, + + 'KernelProfilingInfoMask' : { + 'MaskNone' : 0, + 'CmdExecTime' : 0x00000001, + }, + + 'Capability' : { + 'Matrix' : 0, + 'Shader' : 1, + 'Geometry' : 2, + 'Tessellation' : 3, + 'Addresses' : 4, + 'Linkage' : 5, + 'Kernel' : 6, + 'Vector16' : 7, + 'Float16Buffer' : 8, + 'Float16' : 9, + 'Float64' : 10, + 'Int64' : 11, + 'Int64Atomics' : 12, + 'ImageBasic' : 13, + 'ImageReadWrite' : 14, + 'ImageMipmap' : 15, + 'Pipes' : 17, + 'Groups' : 18, + 'DeviceEnqueue' : 19, + 'LiteralSampler' : 20, + 'AtomicStorage' : 21, + 'Int16' : 22, + 'TessellationPointSize' : 23, + 'GeometryPointSize' : 24, + 'ImageGatherExtended' : 25, + 'StorageImageMultisample' : 27, + 'UniformBufferArrayDynamicIndexing' : 28, + 'SampledImageArrayDynamicIndexing' : 29, + 'StorageBufferArrayDynamicIndexing' : 30, + 'StorageImageArrayDynamicIndexing' : 31, + 'ClipDistance' : 32, + 'CullDistance' : 33, + 'ImageCubeArray' : 34, + 'SampleRateShading' : 35, + 'ImageRect' : 36, + 'SampledRect' : 37, + 'GenericPointer' : 38, + 'Int8' : 39, + 'InputAttachment' : 40, + 'SparseResidency' : 41, + 'MinLod' : 42, + 'Sampled1D' : 43, + 'Image1D' : 44, + 'SampledCubeArray' : 45, + 'SampledBuffer' : 46, + 'ImageBuffer' : 47, + 'ImageMSArray' : 48, + 'StorageImageExtendedFormats' : 49, + 'ImageQuery' : 50, + 'DerivativeControl' : 51, + 'InterpolationFunction' : 52, + 'TransformFeedback' : 53, + 'GeometryStreams' : 54, + 'StorageImageReadWithoutFormat' : 55, + 'StorageImageWriteWithoutFormat' : 56, + 'MultiViewport' : 57, + 'SubgroupDispatch' : 58, + 'NamedBarrier' : 59, + 'PipeStorage' : 60, + 'SubgroupBallotKHR' : 4423, + 'DrawParameters' : 4427, + 'SubgroupVoteKHR' : 4431, + 'StorageBuffer16BitAccess' : 4433, + 'StorageUniformBufferBlock16' : 4433, + 'StorageUniform16' : 4434, + 'UniformAndStorageBuffer16BitAccess' : 4434, + 'StoragePushConstant16' : 4435, + 'StorageInputOutput16' : 4436, + 'DeviceGroup' : 4437, + 'MultiView' : 4439, + 'VariablePointersStorageBuffer' : 4441, + 'VariablePointers' : 4442, + 'AtomicStorageOps' : 4445, + 'SampleMaskPostDepthCoverage' : 4447, + 'ImageGatherBiasLodAMD' : 5009, + 'FragmentMaskAMD' : 5010, + 'StencilExportEXT' : 5013, + 'ImageReadWriteLodAMD' : 5015, + 'SampleMaskOverrideCoverageNV' : 5249, + 'GeometryShaderPassthroughNV' : 5251, + 'ShaderViewportIndexLayerEXT' : 5254, + 'ShaderViewportIndexLayerNV' : 5254, + 'ShaderViewportMaskNV' : 5255, + 'ShaderStereoViewNV' : 5259, + 'PerViewAttributesNV' : 5260, + 'SubgroupShuffleINTEL' : 5568, + 'SubgroupBufferBlockIOINTEL' : 5569, + 'SubgroupImageBlockIOINTEL' : 5570, + }, + + 'Op' : { + 'OpNop' : 0, + 'OpUndef' : 1, + 'OpSourceContinued' : 2, + 'OpSource' : 3, + 'OpSourceExtension' : 4, + 'OpName' : 5, + 'OpMemberName' : 6, + 'OpString' : 7, + 'OpLine' : 8, + 'OpExtension' : 10, + 'OpExtInstImport' : 11, + 'OpExtInst' : 12, + 'OpMemoryModel' : 14, + 'OpEntryPoint' : 15, + 'OpExecutionMode' : 16, + 'OpCapability' : 17, + 'OpTypeVoid' : 19, + 'OpTypeBool' : 20, + 'OpTypeInt' : 21, + 'OpTypeFloat' : 22, + 'OpTypeVector' : 23, + 'OpTypeMatrix' : 24, + 'OpTypeImage' : 25, + 'OpTypeSampler' : 26, + 'OpTypeSampledImage' : 27, + 'OpTypeArray' : 28, + 'OpTypeRuntimeArray' : 29, + 'OpTypeStruct' : 30, + 'OpTypeOpaque' : 31, + 'OpTypePointer' : 32, + 'OpTypeFunction' : 33, + 'OpTypeEvent' : 34, + 'OpTypeDeviceEvent' : 35, + 'OpTypeReserveId' : 36, + 'OpTypeQueue' : 37, + 'OpTypePipe' : 38, + 'OpTypeForwardPointer' : 39, + 'OpConstantTrue' : 41, + 'OpConstantFalse' : 42, + 'OpConstant' : 43, + 'OpConstantComposite' : 44, + 'OpConstantSampler' : 45, + 'OpConstantNull' : 46, + 'OpSpecConstantTrue' : 48, + 'OpSpecConstantFalse' : 49, + 'OpSpecConstant' : 50, + 'OpSpecConstantComposite' : 51, + 'OpSpecConstantOp' : 52, + 'OpFunction' : 54, + 'OpFunctionParameter' : 55, + 'OpFunctionEnd' : 56, + 'OpFunctionCall' : 57, + 'OpVariable' : 59, + 'OpImageTexelPointer' : 60, + 'OpLoad' : 61, + 'OpStore' : 62, + 'OpCopyMemory' : 63, + 'OpCopyMemorySized' : 64, + 'OpAccessChain' : 65, + 'OpInBoundsAccessChain' : 66, + 'OpPtrAccessChain' : 67, + 'OpArrayLength' : 68, + 'OpGenericPtrMemSemantics' : 69, + 'OpInBoundsPtrAccessChain' : 70, + 'OpDecorate' : 71, + 'OpMemberDecorate' : 72, + 'OpDecorationGroup' : 73, + 'OpGroupDecorate' : 74, + 'OpGroupMemberDecorate' : 75, + 'OpVectorExtractDynamic' : 77, + 'OpVectorInsertDynamic' : 78, + 'OpVectorShuffle' : 79, + 'OpCompositeConstruct' : 80, + 'OpCompositeExtract' : 81, + 'OpCompositeInsert' : 82, + 'OpCopyObject' : 83, + 'OpTranspose' : 84, + 'OpSampledImage' : 86, + 'OpImageSampleImplicitLod' : 87, + 'OpImageSampleExplicitLod' : 88, + 'OpImageSampleDrefImplicitLod' : 89, + 'OpImageSampleDrefExplicitLod' : 90, + 'OpImageSampleProjImplicitLod' : 91, + 'OpImageSampleProjExplicitLod' : 92, + 'OpImageSampleProjDrefImplicitLod' : 93, + 'OpImageSampleProjDrefExplicitLod' : 94, + 'OpImageFetch' : 95, + 'OpImageGather' : 96, + 'OpImageDrefGather' : 97, + 'OpImageRead' : 98, + 'OpImageWrite' : 99, + 'OpImage' : 100, + 'OpImageQueryFormat' : 101, + 'OpImageQueryOrder' : 102, + 'OpImageQuerySizeLod' : 103, + 'OpImageQuerySize' : 104, + 'OpImageQueryLod' : 105, + 'OpImageQueryLevels' : 106, + 'OpImageQuerySamples' : 107, + 'OpConvertFToU' : 109, + 'OpConvertFToS' : 110, + 'OpConvertSToF' : 111, + 'OpConvertUToF' : 112, + 'OpUConvert' : 113, + 'OpSConvert' : 114, + 'OpFConvert' : 115, + 'OpQuantizeToF16' : 116, + 'OpConvertPtrToU' : 117, + 'OpSatConvertSToU' : 118, + 'OpSatConvertUToS' : 119, + 'OpConvertUToPtr' : 120, + 'OpPtrCastToGeneric' : 121, + 'OpGenericCastToPtr' : 122, + 'OpGenericCastToPtrExplicit' : 123, + 'OpBitcast' : 124, + 'OpSNegate' : 126, + 'OpFNegate' : 127, + 'OpIAdd' : 128, + 'OpFAdd' : 129, + 'OpISub' : 130, + 'OpFSub' : 131, + 'OpIMul' : 132, + 'OpFMul' : 133, + 'OpUDiv' : 134, + 'OpSDiv' : 135, + 'OpFDiv' : 136, + 'OpUMod' : 137, + 'OpSRem' : 138, + 'OpSMod' : 139, + 'OpFRem' : 140, + 'OpFMod' : 141, + 'OpVectorTimesScalar' : 142, + 'OpMatrixTimesScalar' : 143, + 'OpVectorTimesMatrix' : 144, + 'OpMatrixTimesVector' : 145, + 'OpMatrixTimesMatrix' : 146, + 'OpOuterProduct' : 147, + 'OpDot' : 148, + 'OpIAddCarry' : 149, + 'OpISubBorrow' : 150, + 'OpUMulExtended' : 151, + 'OpSMulExtended' : 152, + 'OpAny' : 154, + 'OpAll' : 155, + 'OpIsNan' : 156, + 'OpIsInf' : 157, + 'OpIsFinite' : 158, + 'OpIsNormal' : 159, + 'OpSignBitSet' : 160, + 'OpLessOrGreater' : 161, + 'OpOrdered' : 162, + 'OpUnordered' : 163, + 'OpLogicalEqual' : 164, + 'OpLogicalNotEqual' : 165, + 'OpLogicalOr' : 166, + 'OpLogicalAnd' : 167, + 'OpLogicalNot' : 168, + 'OpSelect' : 169, + 'OpIEqual' : 170, + 'OpINotEqual' : 171, + 'OpUGreaterThan' : 172, + 'OpSGreaterThan' : 173, + 'OpUGreaterThanEqual' : 174, + 'OpSGreaterThanEqual' : 175, + 'OpULessThan' : 176, + 'OpSLessThan' : 177, + 'OpULessThanEqual' : 178, + 'OpSLessThanEqual' : 179, + 'OpFOrdEqual' : 180, + 'OpFUnordEqual' : 181, + 'OpFOrdNotEqual' : 182, + 'OpFUnordNotEqual' : 183, + 'OpFOrdLessThan' : 184, + 'OpFUnordLessThan' : 185, + 'OpFOrdGreaterThan' : 186, + 'OpFUnordGreaterThan' : 187, + 'OpFOrdLessThanEqual' : 188, + 'OpFUnordLessThanEqual' : 189, + 'OpFOrdGreaterThanEqual' : 190, + 'OpFUnordGreaterThanEqual' : 191, + 'OpShiftRightLogical' : 194, + 'OpShiftRightArithmetic' : 195, + 'OpShiftLeftLogical' : 196, + 'OpBitwiseOr' : 197, + 'OpBitwiseXor' : 198, + 'OpBitwiseAnd' : 199, + 'OpNot' : 200, + 'OpBitFieldInsert' : 201, + 'OpBitFieldSExtract' : 202, + 'OpBitFieldUExtract' : 203, + 'OpBitReverse' : 204, + 'OpBitCount' : 205, + 'OpDPdx' : 207, + 'OpDPdy' : 208, + 'OpFwidth' : 209, + 'OpDPdxFine' : 210, + 'OpDPdyFine' : 211, + 'OpFwidthFine' : 212, + 'OpDPdxCoarse' : 213, + 'OpDPdyCoarse' : 214, + 'OpFwidthCoarse' : 215, + 'OpEmitVertex' : 218, + 'OpEndPrimitive' : 219, + 'OpEmitStreamVertex' : 220, + 'OpEndStreamPrimitive' : 221, + 'OpControlBarrier' : 224, + 'OpMemoryBarrier' : 225, + 'OpAtomicLoad' : 227, + 'OpAtomicStore' : 228, + 'OpAtomicExchange' : 229, + 'OpAtomicCompareExchange' : 230, + 'OpAtomicCompareExchangeWeak' : 231, + 'OpAtomicIIncrement' : 232, + 'OpAtomicIDecrement' : 233, + 'OpAtomicIAdd' : 234, + 'OpAtomicISub' : 235, + 'OpAtomicSMin' : 236, + 'OpAtomicUMin' : 237, + 'OpAtomicSMax' : 238, + 'OpAtomicUMax' : 239, + 'OpAtomicAnd' : 240, + 'OpAtomicOr' : 241, + 'OpAtomicXor' : 242, + 'OpPhi' : 245, + 'OpLoopMerge' : 246, + 'OpSelectionMerge' : 247, + 'OpLabel' : 248, + 'OpBranch' : 249, + 'OpBranchConditional' : 250, + 'OpSwitch' : 251, + 'OpKill' : 252, + 'OpReturn' : 253, + 'OpReturnValue' : 254, + 'OpUnreachable' : 255, + 'OpLifetimeStart' : 256, + 'OpLifetimeStop' : 257, + 'OpGroupAsyncCopy' : 259, + 'OpGroupWaitEvents' : 260, + 'OpGroupAll' : 261, + 'OpGroupAny' : 262, + 'OpGroupBroadcast' : 263, + 'OpGroupIAdd' : 264, + 'OpGroupFAdd' : 265, + 'OpGroupFMin' : 266, + 'OpGroupUMin' : 267, + 'OpGroupSMin' : 268, + 'OpGroupFMax' : 269, + 'OpGroupUMax' : 270, + 'OpGroupSMax' : 271, + 'OpReadPipe' : 274, + 'OpWritePipe' : 275, + 'OpReservedReadPipe' : 276, + 'OpReservedWritePipe' : 277, + 'OpReserveReadPipePackets' : 278, + 'OpReserveWritePipePackets' : 279, + 'OpCommitReadPipe' : 280, + 'OpCommitWritePipe' : 281, + 'OpIsValidReserveId' : 282, + 'OpGetNumPipePackets' : 283, + 'OpGetMaxPipePackets' : 284, + 'OpGroupReserveReadPipePackets' : 285, + 'OpGroupReserveWritePipePackets' : 286, + 'OpGroupCommitReadPipe' : 287, + 'OpGroupCommitWritePipe' : 288, + 'OpEnqueueMarker' : 291, + 'OpEnqueueKernel' : 292, + 'OpGetKernelNDrangeSubGroupCount' : 293, + 'OpGetKernelNDrangeMaxSubGroupSize' : 294, + 'OpGetKernelWorkGroupSize' : 295, + 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, + 'OpRetainEvent' : 297, + 'OpReleaseEvent' : 298, + 'OpCreateUserEvent' : 299, + 'OpIsValidEvent' : 300, + 'OpSetUserEventStatus' : 301, + 'OpCaptureEventProfilingInfo' : 302, + 'OpGetDefaultQueue' : 303, + 'OpBuildNDRange' : 304, + 'OpImageSparseSampleImplicitLod' : 305, + 'OpImageSparseSampleExplicitLod' : 306, + 'OpImageSparseSampleDrefImplicitLod' : 307, + 'OpImageSparseSampleDrefExplicitLod' : 308, + 'OpImageSparseSampleProjImplicitLod' : 309, + 'OpImageSparseSampleProjExplicitLod' : 310, + 'OpImageSparseSampleProjDrefImplicitLod' : 311, + 'OpImageSparseSampleProjDrefExplicitLod' : 312, + 'OpImageSparseFetch' : 313, + 'OpImageSparseGather' : 314, + 'OpImageSparseDrefGather' : 315, + 'OpImageSparseTexelsResident' : 316, + 'OpNoLine' : 317, + 'OpAtomicFlagTestAndSet' : 318, + 'OpAtomicFlagClear' : 319, + 'OpImageSparseRead' : 320, + 'OpSizeOf' : 321, + 'OpTypePipeStorage' : 322, + 'OpConstantPipeStorage' : 323, + 'OpCreatePipeFromPipeStorage' : 324, + 'OpGetKernelLocalSizeForSubgroupCount' : 325, + 'OpGetKernelMaxNumSubgroups' : 326, + 'OpTypeNamedBarrier' : 327, + 'OpNamedBarrierInitialize' : 328, + 'OpMemoryNamedBarrier' : 329, + 'OpModuleProcessed' : 330, + 'OpDecorateId' : 332, + 'OpSubgroupBallotKHR' : 4421, + 'OpSubgroupFirstInvocationKHR' : 4422, + 'OpSubgroupAllKHR' : 4428, + 'OpSubgroupAnyKHR' : 4429, + 'OpSubgroupAllEqualKHR' : 4430, + 'OpSubgroupReadInvocationKHR' : 4432, + 'OpGroupIAddNonUniformAMD' : 5000, + 'OpGroupFAddNonUniformAMD' : 5001, + 'OpGroupFMinNonUniformAMD' : 5002, + 'OpGroupUMinNonUniformAMD' : 5003, + 'OpGroupSMinNonUniformAMD' : 5004, + 'OpGroupFMaxNonUniformAMD' : 5005, + 'OpGroupUMaxNonUniformAMD' : 5006, + 'OpGroupSMaxNonUniformAMD' : 5007, + 'OpFragmentMaskFetchAMD' : 5011, + 'OpFragmentFetchAMD' : 5012, + 'OpSubgroupShuffleINTEL' : 5571, + 'OpSubgroupShuffleDownINTEL' : 5572, + 'OpSubgroupShuffleUpINTEL' : 5573, + 'OpSubgroupShuffleXorINTEL' : 5574, + 'OpSubgroupBlockReadINTEL' : 5575, + 'OpSubgroupBlockWriteINTEL' : 5576, + 'OpSubgroupImageBlockReadINTEL' : 5577, + 'OpSubgroupImageBlockWriteINTEL' : 5578, + 'OpDecorateStringGOOGLE' : 5632, + 'OpMemberDecorateStringGOOGLE' : 5633, + }, + +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/GLSL.std.450.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/GLSL.std.450.h new file mode 100644 index 0000000..54cc00e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/OpenCL.std.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/OpenCL.std.h new file mode 100644 index 0000000..19a6688 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/OpenCL.std.h @@ -0,0 +1,210 @@ +/* +** Copyright (c) 2015-2017 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +namespace OpenCLLIB { + +enum Entrypoints { + + // Section 2.1: Math extended instructions + Acos = 0, + Acosh = 1, + Acospi = 2, + Asin = 3, + Asinh = 4, + Asinpi = 5, + Atan = 6, + Atan2 = 7, + Atanh = 8, + Atanpi = 9, + Atan2pi = 10, + Cbrt = 11, + Ceil = 12, + Copysign = 13, + Cos = 14, + Cosh = 15, + Cospi = 16, + Erfc = 17, + Erf = 18, + Exp = 19, + Exp2 = 20, + Exp10 = 21, + Expm1 = 22, + Fabs = 23, + Fdim = 24, + Floor = 25, + Fma = 26, + Fmax = 27, + Fmin = 28, + Fmod = 29, + Fract = 30, + Frexp = 31, + Hypot = 32, + Ilogb = 33, + Ldexp = 34, + Lgamma = 35, + Lgamma_r = 36, + Log = 37, + Log2 = 38, + Log10 = 39, + Log1p = 40, + Logb = 41, + Mad = 42, + Maxmag = 43, + Minmag = 44, + Modf = 45, + Nan = 46, + Nextafter = 47, + Pow = 48, + Pown = 49, + Powr = 50, + Remainder = 51, + Remquo = 52, + Rint = 53, + Rootn = 54, + Round = 55, + Rsqrt = 56, + Sin = 57, + Sincos = 58, + Sinh = 59, + Sinpi = 60, + Sqrt = 61, + Tan = 62, + Tanh = 63, + Tanpi = 64, + Tgamma = 65, + Trunc = 66, + Half_cos = 67, + Half_divide = 68, + Half_exp = 69, + Half_exp2 = 70, + Half_exp10 = 71, + Half_log = 72, + Half_log2 = 73, + Half_log10 = 74, + Half_powr = 75, + Half_recip = 76, + Half_rsqrt = 77, + Half_sin = 78, + Half_sqrt = 79, + Half_tan = 80, + Native_cos = 81, + Native_divide = 82, + Native_exp = 83, + Native_exp2 = 84, + Native_exp10 = 85, + Native_log = 86, + Native_log2 = 87, + Native_log10 = 88, + Native_powr = 89, + Native_recip = 90, + Native_rsqrt = 91, + Native_sin = 92, + Native_sqrt = 93, + Native_tan = 94, + + // Section 2.2: Integer instructions + SAbs = 141, + SAbs_diff = 142, + SAdd_sat = 143, + UAdd_sat = 144, + SHadd = 145, + UHadd = 146, + SRhadd = 147, + URhadd = 148, + SClamp = 149, + UClamp = 150, + Clz = 151, + Ctz = 152, + SMad_hi = 153, + UMad_sat = 154, + SMad_sat = 155, + SMax = 156, + UMax = 157, + SMin = 158, + UMin = 159, + SMul_hi = 160, + Rotate = 161, + SSub_sat = 162, + USub_sat = 163, + U_Upsample = 164, + S_Upsample = 165, + Popcount = 166, + SMad24 = 167, + UMad24 = 168, + SMul24 = 169, + UMul24 = 170, + UAbs = 201, + UAbs_diff = 202, + UMul_hi = 203, + UMad_hi = 204, + + // Section 2.3: Common instructions + FClamp = 95, + Degrees = 96, + FMax_common = 97, + FMin_common = 98, + Mix = 99, + Radians = 100, + Step = 101, + Smoothstep = 102, + Sign = 103, + + // Section 2.4: Geometric instructions + Cross = 104, + Distance = 105, + Length = 106, + Normalize = 107, + Fast_distance = 108, + Fast_length = 109, + Fast_normalize = 110, + + // Section 2.5: Relational instructions + Bitselect = 186, + Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + Vloadn = 171, + Vstoren = 172, + Vload_half = 173, + Vload_halfn = 174, + Vstore_half = 175, + Vstore_half_r = 176, + Vstore_halfn = 177, + Vstore_halfn_r = 178, + Vloada_halfn = 179, + Vstorea_halfn = 180, + Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + Shuffle = 182, + Shuffle2 = 183, + + // Section 2.8: Misc instructions + Printf = 184, + Prefetch = 185, +}; + +} // end namespace OpenCLLIB diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.glsl.std.450.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.glsl.std.450.grammar.json new file mode 100644 index 0000000..3d9f39e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.glsl.std.450.grammar.json @@ -0,0 +1,642 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "Round", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "RoundEven", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Trunc", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FAbs", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SAbs", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FSign", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SSign", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Floor", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ceil", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fract", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Radians", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "Degrees", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "Sin", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tan", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asin", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acos", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'y_over_x'" } + ] + }, + { + "opname" : "Sinh", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cosh", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tanh", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asinh", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acosh", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atanh", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan2", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Pow", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Exp", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Exp2", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log2", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Sqrt", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "InverseSqrt", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Determinant", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "MatrixInverse", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Modf", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "ModfStruct", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FMin", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMin", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMin", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FMax", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMax", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMax", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FClamp", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "UClamp", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "SClamp", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "FMix", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "IMix", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "Step", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SmoothStep", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fma", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "Frexp", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "FrexpStruct", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ldexp", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "PackSnorm4x8", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm4x8", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackSnorm2x16", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm2x16", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackHalf2x16", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackDouble2x32", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "UnpackSnorm2x16", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm2x16", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackHalf2x16", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "UnpackSnorm4x8", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm4x8", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackDouble2x32", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "Length", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Distance", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "Cross", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Normalize", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FaceForward", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'Nref'" } + ] + }, + { + "opname" : "Reflect", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" } + ] + }, + { + "opname" : "Refract", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'eta'" } + ] + }, + { + "opname" : "FindILsb", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindSMsb", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindUMsb", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "InterpolateAtCentroid", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtSample", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'sample'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtOffset", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "NMin", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NMax", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NClamp", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.opencl.std.100.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.opencl.std.100.grammar.json new file mode 100644 index 0000000..4fe4506 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/extinst.opencl.std.100.grammar.json @@ -0,0 +1,1279 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "acos", + "opcode" : 0, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acosh", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acospi", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asin", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinh", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinpi", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanh", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanpi", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2pi", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cbrt", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ceil", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "copysign", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cosh", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cospi", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erfc", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erf", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp2", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp10", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "expm1", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fabs", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fdim", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "floor", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fma", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fmax", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmod", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fract", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'ptr'" } + ] + }, + { + "opname" : "frexp", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "hypot", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "ilogb", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ldexp", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'k'" } + ] + }, + { + "opname" : "lgamma", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "lgamma_r", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'signp'" } + ] + }, + { + "opname" : "log", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log2", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log10", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log1p", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "logb", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "mad", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "maxmag", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "minmag", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "modf", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'iptr'" } + ] + }, + { + "opname" : "nan", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'nancode'" } + ] + }, + { + "opname" : "nextafter", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "pow", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y" } + ] + }, + { + "opname" : "pown", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "powr", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remainder", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remquo", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'quo'" } + ] + }, + { + "opname" : "rint", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rootn", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "round", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rsqrt", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sin", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sincos", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'cosval'" } + ] + }, + { + "opname" : "sinh", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sinpi", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sqrt", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tan", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanh", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanpi", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tgamma", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "trunc", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_cos", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_divide", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_exp", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp2", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp10", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log2", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log10", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_powr", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_recip", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_rsqrt", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sin", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sqrt", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_tan", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_cos", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_divide", + "opcode" : 82, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_exp", + "opcode" : 83, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp2", + "opcode" : 84, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp10", + "opcode" : 85, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log", + "opcode" : 86, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log2", + "opcode" : 87, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log10", + "opcode" : 88, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_powr", + "opcode" : 89, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_recip", + "opcode" : 90, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_rsqrt", + "opcode" : 91, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sin", + "opcode" : 92, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sqrt", + "opcode" : 93, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_tan", + "opcode" : 94, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs", + "opcode" : 141, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs_diff", + "opcode" : 142, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_add_sat", + "opcode" : 143, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_add_sat", + "opcode" : 144, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_hadd", + "opcode" : 145, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_hadd", + "opcode" : 146, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_rhadd", + "opcode" : 147, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_rhadd", + "opcode" : 148, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_clamp", + "opcode" : 149, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "u_clamp", + "opcode" : 150, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "clz", + "opcode" : 151, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ctz", + "opcode" : 152, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad_hi", + "opcode" : 153, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "u_mad_sat", + "opcode" : 154, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mad_sat", + "opcode" : 155, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_max", + "opcode" : 156, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_max", + "opcode" : 157, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_min", + "opcode" : 158, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_min", + "opcode" : 159, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_mul_hi", + "opcode" : 160, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "rotate", + "opcode" : 161, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "s_sub_sat", + "opcode" : 162, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_sub_sat", + "opcode" : 163, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_upsample", + "opcode" : 164, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "s_upsample", + "opcode" : 165, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "popcount", + "opcode" : 166, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad24", + "opcode" : 167, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "u_mad24", + "opcode" : 168, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mul24", + "opcode" : 169, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul24", + "opcode" : 170, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_abs", + "opcode" : 201, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "u_abs_diff", + "opcode" : 202, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul_hi", + "opcode" : 203, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mad_hi", + "opcode" : 204, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fclamp", + "opcode" : 95, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "degrees", + "opcode" :96, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "fmax_common", + "opcode" : 97, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin_common", + "opcode" : 98, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "mix", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "radians", + "opcode" : 100, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "step", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "smoothstep", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sign", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cross", + "opcode" : 104, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "distance", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "length", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "normalize", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_distance", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "fast_length", + "opcode" : 109, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_normalize", + "opcode" : 110, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "bitselect", + "opcode" : 186, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "select", + "opcode" : 187, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "vloadn", + "opcode" : 171, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstoren", + "opcode" : 172, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_half", + "opcode" : 173, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_halfn", + "opcode" : 174, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstore_half", + "opcode" : 175, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_half_r", + "opcode" : 176, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vstore_halfn", + "opcode" : 177, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_halfn_r", + "opcode" : 178, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vloada_halfn", + "opcode" : 179, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstorea_halfn", + "opcode" : 180, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstorea_halfn_r", + "opcode" : 181, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "shuffle", + "opcode" : 182, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "shuffle2", + "opcode" : 183, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "printf", + "opcode" : 184, + "operands" : [ + { "kind" : "IdRef", "name" : "'format'" }, + { "kind" : "IdRef", "name" : "'additional arguments'", "quantifier" : "*" } + ] + }, + { + "opname" : "prefetch", + "opcode" : 185, + "operands" : [ + { "kind" : "IdRef", "name" : "'ptr'" }, + { "kind" : "IdRef", "name" : "'num elements'" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.core.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.core.grammar.json new file mode 100644 index 0000000..393ee3c --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.core.grammar.json @@ -0,0 +1,5986 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "magic_number" : "0x07230203", + "major_version" : 1, + "minor_version" : 2, + "revision" : 2, + "instructions" : [ + { + "opname" : "OpNop", + "opcode" : 0 + }, + { + "opname" : "OpUndef", + "opcode" : 1, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSourceContinued", + "opcode" : 2, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Continued Source'" } + ] + }, + { + "opname" : "OpSource", + "opcode" : 3, + "operands" : [ + { "kind" : "SourceLanguage" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'File'" }, + { "kind" : "LiteralString", "quantifier" : "?", "name" : "'Source'" } + ] + }, + { + "opname" : "OpSourceExtension", + "opcode" : 4, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Extension'" } + ] + }, + { + "opname" : "OpName", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpMemberName", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpString", + "opcode" : 7, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'String'" } + ] + }, + { + "opname" : "OpLine", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "OpExtension", + "opcode" : 10, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInstImport", + "opcode" : 11, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInst", + "opcode" : 12, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ] + }, + { + "opname" : "OpMemoryModel", + "opcode" : 14, + "operands" : [ + { "kind" : "AddressingModel" }, + { "kind" : "MemoryModel" } + ] + }, + { + "opname" : "OpEntryPoint", + "opcode" : 15, + "operands" : [ + { "kind" : "ExecutionModel" }, + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Interface'" } + ] + }, + { + "opname" : "OpExecutionMode", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpCapability", + "opcode" : 17, + "operands" : [ + { "kind" : "Capability", "name" : "'Capability'" } + ] + }, + { + "opname" : "OpTypeVoid", + "opcode" : 19, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeBool", + "opcode" : 20, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeInt", + "opcode" : 21, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "LiteralInteger", "name" : "'Signedness'" } + ] + }, + { + "opname" : "OpTypeFloat", + "opcode" : 22, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" } + ] + }, + { + "opname" : "OpTypeVector", + "opcode" : 23, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "OpTypeMatrix", + "opcode" : 24, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Column Type'" }, + { "kind" : "LiteralInteger", "name" : "'Column Count'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpTypeImage", + "opcode" : 25, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Type'" }, + { "kind" : "Dim" }, + { "kind" : "LiteralInteger", "name" : "'Depth'" }, + { "kind" : "LiteralInteger", "name" : "'Arrayed'" }, + { "kind" : "LiteralInteger", "name" : "'MS'" }, + { "kind" : "LiteralInteger", "name" : "'Sampled'" }, + { "kind" : "ImageFormat" }, + { "kind" : "AccessQualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "OpTypeSampler", + "opcode" : 26, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeSampledImage", + "opcode" : 27, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ] + }, + { + "opname" : "OpTypeArray", + "opcode" : 28, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" }, + { "kind" : "IdRef", "name" : "'Length'" } + ] + }, + { + "opname" : "OpTypeRuntimeArray", + "opcode" : 29, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpTypeStruct", + "opcode" : 30, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ] + }, + { + "opname" : "OpTypeOpaque", + "opcode" : 31, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "The name of the opaque type." } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypePointer", + "opcode" : 32, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "name" : "'Type'" } + ] + }, + { + "opname" : "OpTypeFunction", + "opcode" : 33, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..." } + ] + }, + { + "opname" : "OpTypeEvent", + "opcode" : 34, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypeDeviceEvent", + "opcode" : 35, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypeReserveId", + "opcode" : 36, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeQueue", + "opcode" : 37, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypePipe", + "opcode" : 38, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "AccessQualifier", "name" : "'Qualifier'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeForwardPointer", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "StorageClass" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpConstantTrue", + "opcode" : 41, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstantFalse", + "opcode" : 42, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstant", + "opcode" : 43, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConstantComposite", + "opcode" : 44, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpConstantSampler", + "opcode" : 45, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "SamplerAddressingMode" }, + { "kind" : "LiteralInteger", "name" : "'Param'" }, + { "kind" : "SamplerFilterMode" } + ], + "capabilities" : [ "LiteralSampler" ] + }, + { + "opname" : "OpConstantNull", + "opcode" : 46, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantTrue", + "opcode" : 48, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantFalse", + "opcode" : 49, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstant", + "opcode" : 50, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpSpecConstantComposite", + "opcode" : 51, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpSpecConstantOp", + "opcode" : 52, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralSpecConstantOpInteger", "name" : "'Opcode'" } + ] + }, + { + "opname" : "OpFunction", + "opcode" : 54, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "FunctionControl" }, + { "kind" : "IdRef", "name" : "'Function Type'" } + ] + }, + { + "opname" : "OpFunctionParameter", + "opcode" : 55, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpFunctionEnd", + "opcode" : 56 + }, + { + "opname" : "OpFunctionCall", + "opcode" : 57, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0', +\n'Argument 1', +\n..." } + ] + }, + { + "opname" : "OpVariable", + "opcode" : 59, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ] + }, + { + "opname" : "OpImageTexelPointer", + "opcode" : 60, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Sample'" } + ] + }, + { + "opname" : "OpLoad", + "opcode" : 61, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpStore", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemory", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemorySized", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpAccessChain", + "opcode" : 65, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpInBoundsAccessChain", + "opcode" : 66, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpPtrAccessChain", + "opcode" : 67, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ + "Addresses", + "VariablePointers", + "VariablePointersStorageBuffer" + ] + }, + { + "opname" : "OpArrayLength", + "opcode" : 68, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpGenericPtrMemSemantics", + "opcode" : 69, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpInBoundsPtrAccessChain", + "opcode" : 70, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpDecorate", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpMemberDecorate", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'Structure Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpDecorationGroup", + "opcode" : 73, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpGroupDecorate", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpGroupMemberDecorate", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "PairIdRefLiteralInteger", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpVectorExtractDynamic", + "opcode" : 77, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorInsertDynamic", + "opcode" : 78, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorShuffle", + "opcode" : 79, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Components'" } + ] + }, + { + "opname" : "OpCompositeConstruct", + "opcode" : 80, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpCompositeExtract", + "opcode" : 81, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCompositeInsert", + "opcode" : 82, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCopyObject", + "opcode" : 83, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpTranspose", + "opcode" : 84, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpSampledImage", + "opcode" : 86, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ] + }, + { + "opname" : "OpImageSampleImplicitLod", + "opcode" : 87, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleExplicitLod", + "opcode" : 88, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ] + }, + { + "opname" : "OpImageSampleDrefImplicitLod", + "opcode" : 89, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleDrefExplicitLod", + "opcode" : 90, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjImplicitLod", + "opcode" : 91, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjExplicitLod", + "opcode" : 92, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefImplicitLod", + "opcode" : 93, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefExplicitLod", + "opcode" : 94, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageFetch", + "opcode" : 95, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageGather", + "opcode" : 96, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageDrefGather", + "opcode" : 97, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageRead", + "opcode" : 98, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageWrite", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Texel'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImage", + "opcode" : 100, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" } + ] + }, + { + "opname" : "OpImageQueryFormat", + "opcode" : 101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQueryOrder", + "opcode" : 102, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQuerySizeLod", + "opcode" : 103, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Level of Detail'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySize", + "opcode" : 104, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLod", + "opcode" : 105, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLevels", + "opcode" : 106, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySamples", + "opcode" : 107, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpConvertFToU", + "opcode" : 109, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertFToS", + "opcode" : 110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertSToF", + "opcode" : 111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpConvertUToF", + "opcode" : 112, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpUConvert", + "opcode" : 113, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpSConvert", + "opcode" : 114, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpFConvert", + "opcode" : 115, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpQuantizeToF16", + "opcode" : 116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConvertPtrToU", + "opcode" : 117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpSatConvertSToU", + "opcode" : 118, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSatConvertUToS", + "opcode" : 119, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpConvertUToPtr", + "opcode" : 120, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Integer Value'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpPtrCastToGeneric", + "opcode" : 121, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtr", + "opcode" : 122, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtrExplicit", + "opcode" : 123, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "StorageClass", "name" : "'Storage'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpBitcast", + "opcode" : 124, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSNegate", + "opcode" : 126, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpFNegate", + "opcode" : 127, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpIAdd", + "opcode" : 128, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFAdd", + "opcode" : 129, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISub", + "opcode" : 130, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFSub", + "opcode" : 131, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpIMul", + "opcode" : 132, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMul", + "opcode" : 133, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUDiv", + "opcode" : 134, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSDiv", + "opcode" : 135, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFDiv", + "opcode" : 136, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMod", + "opcode" : 137, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSRem", + "opcode" : 138, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMod", + "opcode" : 139, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFRem", + "opcode" : 140, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMod", + "opcode" : 141, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpVectorTimesScalar", + "opcode" : 142, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ] + }, + { + "opname" : "OpMatrixTimesScalar", + "opcode" : 143, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpVectorTimesMatrix", + "opcode" : 144, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesVector", + "opcode" : 145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesMatrix", + "opcode" : 146, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'LeftMatrix'" }, + { "kind" : "IdRef", "name" : "'RightMatrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpOuterProduct", + "opcode" : 147, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpDot", + "opcode" : 148, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ] + }, + { + "opname" : "OpIAddCarry", + "opcode" : 149, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISubBorrow", + "opcode" : 150, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMulExtended", + "opcode" : 151, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMulExtended", + "opcode" : 152, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpAny", + "opcode" : 154, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpAll", + "opcode" : 155, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpIsNan", + "opcode" : 156, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsInf", + "opcode" : 157, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsFinite", + "opcode" : 158, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpIsNormal", + "opcode" : 159, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSignBitSet", + "opcode" : 160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLessOrGreater", + "opcode" : 161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpOrdered", + "opcode" : 162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpUnordered", + "opcode" : 163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLogicalEqual", + "opcode" : 164, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNotEqual", + "opcode" : 165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalOr", + "opcode" : 166, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalAnd", + "opcode" : 167, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNot", + "opcode" : 168, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSelect", + "opcode" : 169, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'Object 1'" }, + { "kind" : "IdRef", "name" : "'Object 2'" } + ] + }, + { + "opname" : "OpIEqual", + "opcode" : 170, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpINotEqual", + "opcode" : 171, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThan", + "opcode" : 172, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThan", + "opcode" : 173, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThanEqual", + "opcode" : 174, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThanEqual", + "opcode" : 175, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThan", + "opcode" : 176, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThan", + "opcode" : 177, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThanEqual", + "opcode" : 178, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThanEqual", + "opcode" : 179, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdEqual", + "opcode" : 180, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordEqual", + "opcode" : 181, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdNotEqual", + "opcode" : 182, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordNotEqual", + "opcode" : 183, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThan", + "opcode" : 184, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThan", + "opcode" : 185, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThan", + "opcode" : 186, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThan", + "opcode" : 187, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThanEqual", + "opcode" : 188, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThanEqual", + "opcode" : 189, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThanEqual", + "opcode" : 190, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThanEqual", + "opcode" : 191, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpShiftRightLogical", + "opcode" : 194, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftRightArithmetic", + "opcode" : 195, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftLeftLogical", + "opcode" : 196, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpBitwiseOr", + "opcode" : 197, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseXor", + "opcode" : 198, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseAnd", + "opcode" : 199, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpNot", + "opcode" : 200, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpBitFieldInsert", + "opcode" : 201, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Insert'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldSExtract", + "opcode" : 202, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldUExtract", + "opcode" : 203, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitReverse", + "opcode" : 204, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitCount", + "opcode" : 205, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ] + }, + { + "opname" : "OpDPdx", + "opcode" : 207, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdy", + "opcode" : 208, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpFwidth", + "opcode" : 209, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdxFine", + "opcode" : 210, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyFine", + "opcode" : 211, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthFine", + "opcode" : 212, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdxCoarse", + "opcode" : 213, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyCoarse", + "opcode" : 214, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthCoarse", + "opcode" : 215, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpEmitVertex", + "opcode" : 218, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEndPrimitive", + "opcode" : 219, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEmitStreamVertex", + "opcode" : 220, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpEndStreamPrimitive", + "opcode" : 221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpControlBarrier", + "opcode" : 224, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpMemoryBarrier", + "opcode" : 225, + "operands" : [ + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicLoad", + "opcode" : 227, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicStore", + "opcode" : 228, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicExchange", + "opcode" : 229, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicCompareExchange", + "opcode" : 230, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ] + }, + { + "opname" : "OpAtomicCompareExchangeWeak", + "opcode" : 231, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicIIncrement", + "opcode" : 232, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIDecrement", + "opcode" : 233, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIAdd", + "opcode" : 234, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicISub", + "opcode" : 235, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMin", + "opcode" : 236, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMin", + "opcode" : 237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMax", + "opcode" : 238, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMax", + "opcode" : 239, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicAnd", + "opcode" : 240, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicOr", + "opcode" : 241, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicXor", + "opcode" : 242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpPhi", + "opcode" : 245, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "PairIdRefIdRef", "quantifier" : "*", "name" : "'Variable, Parent, ...'" } + ] + }, + { + "opname" : "OpLoopMerge", + "opcode" : 246, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "IdRef", "name" : "'Continue Target'" }, + { "kind" : "LoopControl" } + ] + }, + { + "opname" : "OpSelectionMerge", + "opcode" : 247, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "SelectionControl" } + ] + }, + { + "opname" : "OpLabel", + "opcode" : 248, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpBranch", + "opcode" : 249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target Label'" } + ] + }, + { + "opname" : "OpBranchConditional", + "opcode" : 250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'True Label'" }, + { "kind" : "IdRef", "name" : "'False Label'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Branch weights'" } + ] + }, + { + "opname" : "OpSwitch", + "opcode" : 251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Selector'" }, + { "kind" : "IdRef", "name" : "'Default'" }, + { "kind" : "PairLiteralIntegerIdRef", "quantifier" : "*", "name" : "'Target'" } + ] + }, + { + "opname" : "OpKill", + "opcode" : 252, + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpReturn", + "opcode" : 253 + }, + { + "opname" : "OpReturnValue", + "opcode" : 254, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpUnreachable", + "opcode" : 255 + }, + { + "opname" : "OpLifetimeStart", + "opcode" : 256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLifetimeStop", + "opcode" : 257, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAsyncCopy", + "opcode" : 259, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Destination'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Num Elements'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupWaitEvents", + "opcode" : 260, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Events List'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAll", + "opcode" : 261, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupAny", + "opcode" : 262, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupBroadcast", + "opcode" : 263, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'LocalId'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupIAdd", + "opcode" : 264, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAdd", + "opcode" : 265, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMin", + "opcode" : 266, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMin", + "opcode" : 267, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMin", + "opcode" : 268, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMax", + "opcode" : 269, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMax", + "opcode" : 270, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMax", + "opcode" : 271, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpReadPipe", + "opcode" : 274, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpWritePipe", + "opcode" : 275, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedReadPipe", + "opcode" : 276, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedWritePipe", + "opcode" : 277, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveReadPipePackets", + "opcode" : 278, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveWritePipePackets", + "opcode" : 279, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitReadPipe", + "opcode" : 280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitWritePipe", + "opcode" : 281, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpIsValidReserveId", + "opcode" : 282, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetNumPipePackets", + "opcode" : 283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetMaxPipePackets", + "opcode" : 284, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveReadPipePackets", + "opcode" : 285, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveWritePipePackets", + "opcode" : 286, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitReadPipe", + "opcode" : 287, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitWritePipe", + "opcode" : 288, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpEnqueueMarker", + "opcode" : 291, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpEnqueueKernel", + "opcode" : 292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Local Size'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeSubGroupCount", + "opcode" : 293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeMaxSubGroupSize", + "opcode" : 294, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelWorkGroupSize", + "opcode" : 295, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelPreferredWorkGroupSizeMultiple", + "opcode" : 296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpRetainEvent", + "opcode" : 297, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpReleaseEvent", + "opcode" : 298, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCreateUserEvent", + "opcode" : 299, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpIsValidEvent", + "opcode" : 300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpSetUserEventStatus", + "opcode" : 301, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Status'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCaptureEventProfilingInfo", + "opcode" : 302, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Profiling Info'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetDefaultQueue", + "opcode" : 303, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpBuildNDRange", + "opcode" : 304, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'GlobalWorkSize'" }, + { "kind" : "IdRef", "name" : "'LocalWorkSize'" }, + { "kind" : "IdRef", "name" : "'GlobalWorkOffset'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpImageSparseSampleImplicitLod", + "opcode" : 305, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleExplicitLod", + "opcode" : 306, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefImplicitLod", + "opcode" : 307, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefExplicitLod", + "opcode" : 308, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjImplicitLod", + "opcode" : 309, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjExplicitLod", + "opcode" : 310, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefImplicitLod", + "opcode" : 311, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjDrefExplicitLod", + "opcode" : 312, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseFetch", + "opcode" : 313, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseGather", + "opcode" : 314, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseDrefGather", + "opcode" : 315, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseTexelsResident", + "opcode" : 316, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Resident Code'" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpNoLine", + "opcode" : 317 + }, + { + "opname" : "OpAtomicFlagTestAndSet", + "opcode" : 318, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicFlagClear", + "opcode" : 319, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Scope'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageSparseRead", + "opcode" : 320, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpSizeOf", + "opcode" : 321, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpTypePipeStorage", + "opcode" : 322, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpConstantPipeStorage", + "opcode" : 323, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Packet Size'" }, + { "kind" : "LiteralInteger", "name" : "'Packet Alignment'" }, + { "kind" : "LiteralInteger", "name" : "'Capacity'" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpCreatePipeFromPipeStorage", + "opcode" : 324, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe Storage'" } + ], + "capabilities" : [ "PipeStorage" ] + }, + { + "opname" : "OpGetKernelLocalSizeForSubgroupCount", + "opcode" : 325, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ] + }, + { + "opname" : "OpGetKernelMaxNumSubgroups", + "opcode" : 326, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ] + }, + { + "opname" : "OpTypeNamedBarrier", + "opcode" : 327, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpNamedBarrierInitialize", + "opcode" : 328, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpMemoryNamedBarrier", + "opcode" : 329, + "operands" : [ + { "kind" : "IdRef", "name" : "'Named Barrier'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "NamedBarrier" ] + }, + { + "opname" : "OpModuleProcessed", + "opcode" : 330, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Process'" } + ] + }, + { + "opname" : "OpExecutionModeId", + "opcode" : 331, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpDecorateId", + "opcode" : 332, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpSubgroupBallotKHR", + "opcode" : 4421, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupFirstInvocationKHR", + "opcode" : 4422, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpSubgroupAllKHR", + "opcode" : 4428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAnyKHR", + "opcode" : 4429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupAllEqualKHR", + "opcode" : 4430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupVoteKHR" ] + }, + { + "opname" : "OpSubgroupReadInvocationKHR", + "opcode" : 4432, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "opname" : "OpGroupIAddNonUniformAMD", + "opcode" : 5000, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAddNonUniformAMD", + "opcode" : 5001, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMinNonUniformAMD", + "opcode" : 5002, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMinNonUniformAMD", + "opcode" : 5003, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMinNonUniformAMD", + "opcode" : 5004, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMaxNonUniformAMD", + "opcode" : 5005, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMaxNonUniformAMD", + "opcode" : 5006, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMaxNonUniformAMD", + "opcode" : 5007, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpFragmentMaskFetchAMD", + "opcode" : 5011, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpFragmentFetchAMD", + "opcode" : 5012, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Fragment Index'" } + ], + "capabilities" : [ "FragmentMaskAMD" ] + }, + { + "opname" : "OpSubgroupShuffleINTEL", + "opcode" : 5571, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'InvocationId'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleDownINTEL", + "opcode" : 5572, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Next'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleUpINTEL", + "opcode" : 5573, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Previous'" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupShuffleXorINTEL", + "opcode" : 5574, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ] + }, + { + "opname" : "OpSubgroupBlockReadINTEL", + "opcode" : 5575, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupBlockWriteINTEL", + "opcode" : 5576, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockReadINTEL", + "opcode" : 5577, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpSubgroupImageBlockWriteINTEL", + "opcode" : 5578, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ] + }, + { + "opname" : "OpDecorateStringGOOGLE", + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + }, + { + "opname" : "OpMemberDecorateStringGOOGLE", + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string" ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "ImageOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Bias", + "value" : "0x0001", + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Lod", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Grad", + "value" : "0x0004", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffset", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Offset", + "value" : "0x0010", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffsets", + "value" : "0x0020", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Sample", + "value" : "0x0040", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "MinLod", + "value" : "0x0080", + "capabilities" : [ "MinLod" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FPFastMathMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "NotNaN", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NotInf", + "value" : "0x0002", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NSZ", + "value" : "0x0004", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AllowRecip", + "value" : "0x0008", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Fast", + "value" : "0x0010", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "SelectionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Flatten", + "value" : "0x0001" + }, + { + "enumerant" : "DontFlatten", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "LoopControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Unroll", + "value" : "0x0001" + }, + { + "enumerant" : "DontUnroll", + "value" : "0x0002" + }, + { + "enumerant" : "DependencyInfinite", + "value" : "0x0004" + }, + { + "enumerant" : "DependencyLength", + "value" : "0x0008", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FunctionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Inline", + "value" : "0x0001" + }, + { + "enumerant" : "DontInline", + "value" : "0x0002" + }, + { + "enumerant" : "Pure", + "value" : "0x0004" + }, + { + "enumerant" : "Const", + "value" : "0x0008" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemorySemantics", + "enumerants" : [ + { + "enumerant" : "Relaxed", + "value" : "0x0000" + }, + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Acquire", + "value" : "0x0002" + }, + { + "enumerant" : "Release", + "value" : "0x0004" + }, + { + "enumerant" : "AcquireRelease", + "value" : "0x0008" + }, + { + "enumerant" : "SequentiallyConsistent", + "value" : "0x0010" + }, + { + "enumerant" : "UniformMemory", + "value" : "0x0040", + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupMemory", + "value" : "0x0080" + }, + { + "enumerant" : "WorkgroupMemory", + "value" : "0x0100" + }, + { + "enumerant" : "CrossWorkgroupMemory", + "value" : "0x0200" + }, + { + "enumerant" : "AtomicCounterMemory", + "value" : "0x0400", + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "ImageMemory", + "value" : "0x0800" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemoryAccess", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Volatile", + "value" : "0x0001" + }, + { + "enumerant" : "Aligned", + "value" : "0x0002", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Nontemporal", + "value" : "0x0004" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "KernelProfilingInfo", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "CmdExecTime", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SourceLanguage", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "ESSL", + "value" : 1 + }, + { + "enumerant" : "GLSL", + "value" : 2 + }, + { + "enumerant" : "OpenCL_C", + "value" : 3 + }, + { + "enumerant" : "OpenCL_CPP", + "value" : 4 + }, + { + "enumerant" : "HLSL", + "value" : 5 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionModel", + "enumerants" : [ + { + "enumerant" : "Vertex", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TessellationControl", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessellationEvaluation", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Geometry", + "value" : 3, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Fragment", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLCompute", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Kernel", + "value" : 6, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AddressingModel", + "enumerants" : [ + { + "enumerant" : "Logical", + "value" : 0 + }, + { + "enumerant" : "Physical32", + "value" : 1, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Physical64", + "value" : 2, + "capabilities" : [ "Addresses" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "MemoryModel", + "enumerants" : [ + { + "enumerant" : "Simple", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSL450", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OpenCL", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionMode", + "enumerants" : [ + { + "enumerant" : "Invocations", + "value" : 0, + "capabilities" : [ "Geometry" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of <>'" } + ] + }, + { + "enumerant" : "SpacingEqual", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalEven", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalOdd", + "value" : 3, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCw", + "value" : 4, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCcw", + "value" : 5, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PixelCenterInteger", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginUpperLeft", + "value" : 7, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginLowerLeft", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "EarlyFragmentTests", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointMode", + "value" : 10, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Xfb", + "value" : 11, + "capabilities" : [ "TransformFeedback" ] + }, + { + "enumerant" : "DepthReplacing", + "value" : 12, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthGreater", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthLess", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthUnchanged", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "LocalSize", + "value" : 17, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHint", + "value" : 18, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "InputPoints", + "value" : 19, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLines", + "value" : 20, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLinesAdjacency", + "value" : 21, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Triangles", + "value" : 22, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InputTrianglesAdjacency", + "value" : 23, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Quads", + "value" : 24, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Isolines", + "value" : 25, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "OutputVertices", + "value" : 26, + "capabilities" : [ "Geometry", "Tessellation" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vertex count'" } + ] + }, + { + "enumerant" : "OutputPoints", + "value" : 27, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputLineStrip", + "value" : 28, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputTriangleStrip", + "value" : 29, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "VecTypeHint", + "value" : 30, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vector type'" } + ] + }, + { + "enumerant" : "ContractionOff", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Initializer", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Finalizer", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 35, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroup Size'" } + ] + }, + { + "enumerant" : "SubgroupsPerWorkgroup", + "value" : 36, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroups Per Workgroup'" } + ] + }, + { + "enumerant" : "SubgroupsPerWorkgroupId", + "value" : 37, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Subgroups Per Workgroup'" } + ] + }, + { + "enumerant" : "LocalSizeId", + "value" : 38, + "parameters" : [ + { "kind" : "IdRef", "name" : "'x size'" }, + { "kind" : "IdRef", "name" : "'y size'" }, + { "kind" : "IdRef", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHintId", + "value" : 39, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Local Size Hint'" } + ] + }, + { + "enumerant" : "PostDepthCoverage", + "value" : 4446, + "capabilities" : [ "SampleMaskPostDepthCoverage" ] + }, + { + "enumerant" : "StencilRefReplacingEXT", + "value" : 5027, + "capabilities" : [ "StencilExportEXT" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StorageClass", + "enumerants" : [ + { + "enumerant" : "UniformConstant", + "value" : 0 + }, + { + "enumerant" : "Input", + "value" : 1 + }, + { + "enumerant" : "Uniform", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Output", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Workgroup", + "value" : 4 + }, + { + "enumerant" : "CrossWorkgroup", + "value" : 5 + }, + { + "enumerant" : "Private", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Function", + "value" : 7 + }, + { + "enumerant" : "Generic", + "value" : 8, + "capabilities" : [ "GenericPointer" ] + }, + { + "enumerant" : "PushConstant", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "AtomicCounter", + "value" : 10, + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "Image", + "value" : 11 + }, + { + "enumerant" : "StorageBuffer", + "value" : 12, + "extensions" : [ + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers" + ], + "capabilities" : [ "Shader" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Dim", + "enumerants" : [ + { + "enumerant" : "1D", + "value" : 0, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "2D", + "value" : 1 + }, + { + "enumerant" : "3D", + "value" : 2 + }, + { + "enumerant" : "Cube", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rect", + "value" : 4, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "Buffer", + "value" : 5, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "SubpassData", + "value" : 6, + "capabilities" : [ "InputAttachment" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerAddressingMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ClampToEdge", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Clamp", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Repeat", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerFilterMode", + "enumerants" : [ + { + "enumerant" : "Nearest", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Linear", + "value" : 1, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageFormat", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "Rgba32f", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16f", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32f", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8Snorm", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32f", + "value" : 6, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16f", + "value" : 7, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R11fG11fB10f", + "value" : 8, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16f", + "value" : 9, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16", + "value" : 10, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgb10A2", + "value" : 11, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16", + "value" : 12, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8", + "value" : 13, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16", + "value" : 14, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8", + "value" : 15, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16Snorm", + "value" : 16, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16Snorm", + "value" : 17, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8Snorm", + "value" : 18, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16Snorm", + "value" : 19, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8Snorm", + "value" : 20, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32i", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16i", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8i", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32i", + "value" : 24, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32i", + "value" : 25, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16i", + "value" : 26, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8i", + "value" : 27, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16i", + "value" : 28, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8i", + "value" : 29, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32ui", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16ui", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8ui", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32ui", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgb10a2ui", + "value" : 34, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg32ui", + "value" : 35, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16ui", + "value" : 36, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8ui", + "value" : 37, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16ui", + "value" : 38, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8ui", + "value" : 39, + "capabilities" : [ "StorageImageExtendedFormats" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelOrder", + "enumerants" : [ + { + "enumerant" : "R", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "A", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RG", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RA", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGB", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBA", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BGRA", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ARGB", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Intensity", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Luminance", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Rx", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGx", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBx", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Depth", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "DepthStencil", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGB", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBx", + "value" : 16, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBA", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sBGRA", + "value" : 18, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ABGR", + "value" : 19, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelDataType", + "enumerants" : [ + { + "enumerant" : "SnormInt8", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SnormInt16", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt8", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt16", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort565", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort555", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt8", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt16", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt32", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt8", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt16", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt32", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "HalfFloat", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt24", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010_2", + "value" : 16, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPRoundingMode", + "enumerants" : [ + { + "enumerant" : "RTE", + "value" : 0, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTZ", + "value" : 1, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTP", + "value" : 2, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + }, + { + "enumerant" : "RTN", + "value" : 3, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LinkageType", + "enumerants" : [ + { + "enumerant" : "Export", + "value" : 0, + "capabilities" : [ "Linkage" ] + }, + { + "enumerant" : "Import", + "value" : 1, + "capabilities" : [ "Linkage" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AccessQualifier", + "enumerants" : [ + { + "enumerant" : "ReadOnly", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WriteOnly", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ReadWrite", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FunctionParameterAttribute", + "enumerants" : [ + { + "enumerant" : "Zext", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sext", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ByVal", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sret", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoAlias", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoCapture", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoWrite", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoReadWrite", + "value" : 7, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Decoration", + "enumerants" : [ + { + "enumerant" : "RelaxedPrecision", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SpecId", + "value" : 1, + "capabilities" : [ "Shader", "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Specialization Constant ID'" } + ] + }, + { + "enumerant" : "Block", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "BufferBlock", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "RowMajor", + "value" : 4, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ColMajor", + "value" : 5, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ArrayStride", + "value" : 6, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Array Stride'" } + ] + }, + { + "enumerant" : "MatrixStride", + "value" : 7, + "capabilities" : [ "Matrix" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Matrix Stride'" } + ] + }, + { + "enumerant" : "GLSLShared", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSLPacked", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CPacked", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BuiltIn", + "value" : 11, + "parameters" : [ + { "kind" : "BuiltIn" } + ] + }, + { + "enumerant" : "NoPerspective", + "value" : 13, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Flat", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Patch", + "value" : 15, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Centroid", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sample", + "value" : 17, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "Invariant", + "value" : 18, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Restrict", + "value" : 19 + }, + { + "enumerant" : "Aliased", + "value" : 20 + }, + { + "enumerant" : "Volatile", + "value" : 21 + }, + { + "enumerant" : "Constant", + "value" : 22, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Coherent", + "value" : 23 + }, + { + "enumerant" : "NonWritable", + "value" : 24 + }, + { + "enumerant" : "NonReadable", + "value" : 25 + }, + { + "enumerant" : "Uniform", + "value" : 26, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SaturatedConversion", + "value" : 28, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Stream", + "value" : 29, + "capabilities" : [ "GeometryStreams" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stream Number'" } + ] + }, + { + "enumerant" : "Location", + "value" : 30, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Location'" } + ] + }, + { + "enumerant" : "Component", + "value" : 31, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Component'" } + ] + }, + { + "enumerant" : "Index", + "value" : 32, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Index'" } + ] + }, + { + "enumerant" : "Binding", + "value" : 33, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Binding Point'" } + ] + }, + { + "enumerant" : "DescriptorSet", + "value" : 34, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Descriptor Set'" } + ] + }, + { + "enumerant" : "Offset", + "value" : 35, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Byte Offset'" } + ] + }, + { + "enumerant" : "XfbBuffer", + "value" : 36, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Buffer Number'" } + ] + }, + { + "enumerant" : "XfbStride", + "value" : 37, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Stride'" } + ] + }, + { + "enumerant" : "FuncParamAttr", + "value" : 38, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FunctionParameterAttribute", "name" : "'Function Parameter Attribute'" } + ] + }, + { + "enumerant" : "FPRoundingMode", + "value" : 39, + "capabilities" : [ + "Kernel", + "StorageUniformBufferBlock16", + "StorageUniform16", + "StoragePushConstant16", + "StorageInputOutput16" + ], + "parameters" : [ + { "kind" : "FPRoundingMode", "name" : "'Floating-Point Rounding Mode'" } + ] + }, + { + "enumerant" : "FPFastMathMode", + "value" : 40, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FPFastMathMode", "name" : "'Fast-Math Mode'" } + ] + }, + { + "enumerant" : "LinkageAttributes", + "value" : 41, + "capabilities" : [ "Linkage" ], + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "LinkageType", "name" : "'Linkage Type'" } + ] + }, + { + "enumerant" : "NoContraction", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InputAttachmentIndex", + "value" : 43, + "capabilities" : [ "InputAttachment" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Attachment Index'" } + ] + }, + { + "enumerant" : "Alignment", + "value" : 44, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "MaxByteOffset", + "value" : 45, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Max Byte Offset'" } + ] + }, + { + "enumerant" : "AlignmentId", + "value" : 46, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "MaxByteOffsetId", + "value" : 47, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Max Byte Offset'" } + ] + }, + { + "enumerant" : "ExplicitInterpAMD", + "value" : 4999 + }, + { + "enumerant" : "OverrideCoverageNV", + "value" : 5248, + "capabilities" : [ "SampleMaskOverrideCoverageNV" ] + }, + { + "enumerant" : "PassthroughNV", + "value" : 5250, + "capabilities" : [ "GeometryShaderPassthroughNV" ] + }, + { + "enumerant" : "ViewportRelativeNV", + "value" : 5252, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryViewportRelativeNV", + "value" : 5256, + "capabilities" : [ "ShaderStereoViewNV" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ] + }, + { + "enumerant" : "HlslCounterBufferGOOGLE", + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + }, + { + "enumerant" : "HlslSemanticGOOGLE", + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "BuiltIn", + "enumerants" : [ + { + "enumerant" : "Position", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointSize", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 3, + "capabilities" : [ "ClipDistance" ] + }, + { + "enumerant" : "CullDistance", + "value" : 4, + "capabilities" : [ "CullDistance" ] + }, + { + "enumerant" : "VertexId", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceId", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PrimitiveId", + "value" : 7, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InvocationId", + "value" : 8, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "Layer", + "value" : 9, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ViewportIndex", + "value" : 10, + "capabilities" : [ "MultiViewport" ] + }, + { + "enumerant" : "TessLevelOuter", + "value" : 11, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessLevelInner", + "value" : 12, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessCoord", + "value" : 13, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PatchVertices", + "value" : 14, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "FragCoord", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointCoord", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FrontFacing", + "value" : 17, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampleId", + "value" : 18, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SamplePosition", + "value" : 19, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SampleMask", + "value" : 20, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FragDepth", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "HelperInvocation", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "NumWorkgroups", + "value" : 24 + }, + { + "enumerant" : "WorkgroupSize", + "value" : 25 + }, + { + "enumerant" : "WorkgroupId", + "value" : 26 + }, + { + "enumerant" : "LocalInvocationId", + "value" : 27 + }, + { + "enumerant" : "GlobalInvocationId", + "value" : 28 + }, + { + "enumerant" : "LocalInvocationIndex", + "value" : 29 + }, + { + "enumerant" : "WorkDim", + "value" : 30, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalSize", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "EnqueuedWorkgroupSize", + "value" : 32, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalOffset", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalLinearId", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 36, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupMaxSize", + "value" : 37, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumSubgroups", + "value" : 38, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumEnqueuedSubgroups", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupId", + "value" : 40, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupLocalInvocationId", + "value" : 41, + "capabilities" : [ "Kernel", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "VertexIndex", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceIndex", + "value" : 43, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupEqMaskKHR", + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGeMaskKHR", + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupGtMaskKHR", + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLeMaskKHR", + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupLtMaskKHR", + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR" ] + }, + { + "enumerant" : "BaseVertex", + "value" : 4424, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "BaseInstance", + "value" : 4425, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DrawIndex", + "value" : 4426, + "capabilities" : [ "DrawParameters" ] + }, + { + "enumerant" : "DeviceIndex", + "value" : 4438, + "capabilities" : [ "DeviceGroup" ] + }, + { + "enumerant" : "ViewIndex", + "value" : 4440, + "capabilities" : [ "MultiView" ] + }, + { + "enumerant" : "BaryCoordNoPerspAMD", + "value" : 4992 + }, + { + "enumerant" : "BaryCoordNoPerspCentroidAMD", + "value" : 4993 + }, + { + "enumerant" : "BaryCoordNoPerspSampleAMD", + "value" : 4994 + }, + { + "enumerant" : "BaryCoordSmoothAMD", + "value" : 4995 + }, + { + "enumerant" : "BaryCoordSmoothCentroidAMD", + "value" : 4996 + }, + { + "enumerant" : "BaryCoordSmoothSampleAMD", + "value" : 4997 + }, + { + "enumerant" : "BaryCoordPullModelAMD", + "value" : 4998 + }, + { + "enumerant" : "FragStencilRefEXT", + "value" : 5014, + "capabilities" : [ "StencilExportEXT" ] + }, + { + "enumerant" : "ViewportMaskNV", + "value" : 5253, + "capabilities" : [ "ShaderViewportMaskNV" ] + }, + { + "enumerant" : "SecondaryPositionNV", + "value" : 5257, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "SecondaryViewportMaskNV", + "value" : 5258, + "capabilities" : [ "ShaderStereoViewNV" ] + }, + { + "enumerant" : "PositionPerViewNV", + "value" : 5261, + "capabilities" : [ "PerViewAttributesNV" ] + }, + { + "enumerant" : "ViewportMaskPerViewNV", + "value" : 5262, + "capabilities" : [ "PerViewAttributesNV" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Scope", + "enumerants" : [ + { + "enumerant" : "CrossDevice", + "value" : 0 + }, + { + "enumerant" : "Device", + "value" : 1 + }, + { + "enumerant" : "Workgroup", + "value" : 2 + }, + { + "enumerant" : "Subgroup", + "value" : 3 + }, + { + "enumerant" : "Invocation", + "value" : 4 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "GroupOperation", + "enumerants" : [ + { + "enumerant" : "Reduce", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InclusiveScan", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ExclusiveScan", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "KernelEnqueueFlags", + "enumerants" : [ + { + "enumerant" : "NoWait", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitKernel", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitWorkGroup", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Capability", + "enumerants" : [ + { + "enumerant" : "Matrix", + "value" : 0 + }, + { + "enumerant" : "Shader", + "value" : 1, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "Geometry", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Tessellation", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Addresses", + "value" : 4 + }, + { + "enumerant" : "Linkage", + "value" : 5 + }, + { + "enumerant" : "Kernel", + "value" : 6 + }, + { + "enumerant" : "Vector16", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16Buffer", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16", + "value" : 9 + }, + { + "enumerant" : "Float64", + "value" : 10 + }, + { + "enumerant" : "Int64", + "value" : 11 + }, + { + "enumerant" : "Int64Atomics", + "value" : 12, + "capabilities" : [ "Int64" ] + }, + { + "enumerant" : "ImageBasic", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ImageReadWrite", + "value" : 14, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "ImageMipmap", + "value" : 15, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "Pipes", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Groups", + "value" : 18 + }, + { + "enumerant" : "DeviceEnqueue", + "value" : 19, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "LiteralSampler", + "value" : 20, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AtomicStorage", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Int16", + "value" : 22 + }, + { + "enumerant" : "TessellationPointSize", + "value" : 23, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "GeometryPointSize", + "value" : 24, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ImageGatherExtended", + "value" : 25, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageMultisample", + "value" : 27, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "UniformBufferArrayDynamicIndexing", + "value" : 28, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledImageArrayDynamicIndexing", + "value" : 29, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageBufferArrayDynamicIndexing", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageArrayDynamicIndexing", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CullDistance", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageCubeArray", + "value" : 34, + "capabilities" : [ "SampledCubeArray" ] + }, + { + "enumerant" : "SampleRateShading", + "value" : 35, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageRect", + "value" : 36, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "SampledRect", + "value" : 37, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GenericPointer", + "value" : 38, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Int8", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "InputAttachment", + "value" : 40, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SparseResidency", + "value" : 41, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MinLod", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sampled1D", + "value" : 43 + }, + { + "enumerant" : "Image1D", + "value" : 44, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "SampledCubeArray", + "value" : 45, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledBuffer", + "value" : 46 + }, + { + "enumerant" : "ImageBuffer", + "value" : 47, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "ImageMSArray", + "value" : 48, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageExtendedFormats", + "value" : 49, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageQuery", + "value" : 50, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DerivativeControl", + "value" : 51, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InterpolationFunction", + "value" : 52, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TransformFeedback", + "value" : 53, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GeometryStreams", + "value" : 54, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "StorageImageReadWithoutFormat", + "value" : 55, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageWriteWithoutFormat", + "value" : 56, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MultiViewport", + "value" : 57, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "SubgroupDispatch", + "value" : 58, + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "enumerant" : "NamedBarrier", + "value" : 59, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "PipeStorage", + "value" : 60, + "capabilities" : [ "Pipes" ] + }, + { + "enumerant" : "SubgroupBallotKHR", + "value" : 4423, + "extensions" : [ "SPV_KHR_shader_ballot" ] + }, + { + "enumerant" : "DrawParameters", + "value" : 4427, + "extensions" : [ "SPV_KHR_shader_draw_parameters" ] + }, + { + "enumerant" : "SubgroupVoteKHR", + "value" : 4431, + "extensions" : [ "SPV_KHR_subgroup_vote" ] + }, + { + "enumerant" : "StorageBuffer16BitAccess", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniformBufferBlock16", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "UniformAndStorageBuffer16BitAccess", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageUniform16", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StoragePushConstant16", + "value" : 4435, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "StorageInputOutput16", + "value" : 4436, + "extensions" : [ "SPV_KHR_16bit_storage" ] + }, + { + "enumerant" : "DeviceGroup", + "value" : 4437, + "extensions" : [ "SPV_KHR_device_group" ] + }, + { + "enumerant" : "MultiView", + "value" : 4439, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_multiview" ] + }, + { + "enumerant" : "VariablePointersStorageBuffer", + "value" : 4441, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant" : "VariablePointers", + "value" : 4442, + "capabilities" : [ "VariablePointersStorageBuffer" ], + "extensions" : [ "SPV_KHR_variable_pointers" ] + }, + { + "enumerant": "AtomicStorageOps", + "value": 4445, + "extensions": [ "SPV_KHR_shader_atomic_counter_ops" ] + }, + { + "enumerant" : "SampleMaskPostDepthCoverage", + "value" : 4447, + "extensions" : [ "SPV_KHR_post_depth_coverage" ] + }, + { + "enumerant" : "ImageGatherBiasLodAMD", + "value" : 5009, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_texture_gather_bias_lod" ] + }, + { + "enumerant" : "FragmentMaskAMD", + "value" : 5010, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ] + }, + { + "enumerant" : "StencilExportEXT", + "value" : 5013, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ] + }, + { + "enumerant" : "ImageReadWriteLodAMD", + "value" : 5015, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_image_load_store_lod" ] + }, + { + "enumerant" : "SampleMaskOverrideCoverageNV", + "value" : 5249, + "capabilities" : [ "SampleRateShading" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ] + }, + { + "enumerant" : "GeometryShaderPassthroughNV", + "value" : 5251, + "capabilities" : [ "Geometry" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerEXT", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_EXT_shader_viewport_index_layer" ] + }, + { + "enumerant" : "ShaderViewportIndexLayerNV", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderViewportMaskNV", + "value" : 5255, + "capabilities" : [ "ShaderViewportIndexLayerNV" ], + "extensions" : [ "SPV_NV_viewport_array2" ] + }, + { + "enumerant" : "ShaderStereoViewNV", + "value" : 5259, + "capabilities" : [ "ShaderViewportMaskNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ] + }, + { + "enumerant" : "PerViewAttributesNV", + "value" : 5260, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes" ] + }, + { + "enumerant" : "SubgroupShuffleINTEL", + "value" : 5568, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupBufferBlockIOINTEL", + "value" : 5569, + "extensions" : [ "SPV_INTEL_subgroups" ] + }, + { + "enumerant" : "SubgroupImageBlockIOINTEL", + "value" : 5570, + "extensions" : [ "SPV_INTEL_subgroups" ] + } + ] + }, + { + "category" : "Id", + "kind" : "IdResultType", + "doc" : "Reference to an representing the result's type of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdResult", + "doc" : "Definition of an representing the result of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdMemorySemantics", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the MemorySemantics operand kind" + }, + { + "category" : "Id", + "kind" : "IdScope", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the Scope operand kind" + }, + { + "category" : "Id", + "kind" : "IdRef", + "doc" : "Reference to an " + }, + { + "category" : "Literal", + "kind" : "LiteralInteger", + "doc" : "An integer consuming one or more words" + }, + { + "category" : "Literal", + "kind" : "LiteralString", + "doc" : "A null-terminated stream of characters consuming an integral number of words" + }, + { + "category" : "Literal", + "kind" : "LiteralContextDependentNumber", + "doc" : "A literal number whose size and format are determined by a previous operand in the enclosing instruction" + }, + { + "category" : "Literal", + "kind" : "LiteralExtInstInteger", + "doc" : "A 32-bit unsigned integer indicating which instruction to use and determining the layout of following operands (for OpExtInst)" + }, + { + "category" : "Literal", + "kind" : "LiteralSpecConstantOpInteger", + "doc" : "An opcode indicating the operation to be performed and determining the layout of following operands (for OpSpecConstantOp)" + }, + { + "category" : "Composite", + "kind" : "PairLiteralIntegerIdRef", + "bases" : [ "LiteralInteger", "IdRef" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefLiteralInteger", + "bases" : [ "IdRef", "LiteralInteger" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefIdRef", + "bases" : [ "IdRef", "IdRef" ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.cs b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.cs new file mode 100644 index 0000000..493303d --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.cs @@ -0,0 +1,1021 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C# +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + + public static class Specification + { + public const uint MagicNumber = 0x07230203; + public const uint Version = 0x00010200; + public const uint Revision = 2; + public const uint OpCodeMask = 0xffff; + public const uint WordCountShift = 16; + + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + } + + public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + } + + public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + } + + public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + } + + public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + } + + public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + } + + public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + } + + public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + } + + public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + } + + public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + } + + public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + } + + public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + } + + public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + } + + public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + } + + public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + } + + public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + } + } +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.h new file mode 100644 index 0000000..7c6d884 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.h @@ -0,0 +1,1021 @@ +/* +** Copyright (c) 2014-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10200 +#define SPV_REVISION 2 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010200; +static const unsigned int SpvRevision = 2; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModeSubgroupsPerWorkgroupId = 37, + SpvExecutionModeLocalSizeId = 38, + SpvExecutionModeLocalSizeHintId = 39, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationAlignmentId = 46, + SpvDecorationMaxByteOffsetId = 47, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpExecutionModeId = 331, + SpvOpDecorateId = 332, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#endif // #ifndef spirv_H + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp new file mode 100644 index 0000000..57bd97a --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp @@ -0,0 +1,1030 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10200 +#define SPV_REVISION 2 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010200; +static const unsigned int Revision = 2; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilityMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + OpMax = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp11 b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp11 new file mode 100644 index 0000000..7a875fd --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.hpp11 @@ -0,0 +1,1030 @@ +// Copyright (c) 2014-2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10200 +#define SPV_REVISION 2 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010200; +static const unsigned int Revision = 2; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum class SourceLanguage : unsigned { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, +}; + +enum class ExecutionModel : unsigned { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + Max = 0x7fffffff, +}; + +enum class AddressingModel : unsigned { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + Max = 0x7fffffff, +}; + +enum class MemoryModel : unsigned { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Max = 0x7fffffff, +}; + +enum class ExecutionMode : unsigned { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + Max = 0x7fffffff, +}; + +enum class StorageClass : unsigned { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + Max = 0x7fffffff, +}; + +enum class Dim : unsigned { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, +}; + +enum class SamplerAddressingMode : unsigned { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class SamplerFilterMode : unsigned { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +}; + +enum class ImageFormat : unsigned { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + Max = 0x7fffffff, +}; + +enum class ImageChannelOrder : unsigned { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +}; + +enum class ImageChannelDataType : unsigned { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, +}; + +enum class ImageOperandsShift : unsigned { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + Max = 0x7fffffff, +}; + +enum class ImageOperandsMask : unsigned { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, +}; + +enum class FPFastMathModeShift : unsigned { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + Max = 0x7fffffff, +}; + +enum class FPFastMathModeMask : unsigned { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, +}; + +enum class FPRoundingMode : unsigned { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +}; + +enum class LinkageType : unsigned { + Export = 0, + Import = 1, + Max = 0x7fffffff, +}; + +enum class AccessQualifier : unsigned { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +}; + +enum class FunctionParameterAttribute : unsigned { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, +}; + +enum class Decoration : unsigned { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + Max = 0x7fffffff, +}; + +enum class BuiltIn : unsigned { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + Max = 0x7fffffff, +}; + +enum class SelectionControlShift : unsigned { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +}; + +enum class SelectionControlMask : unsigned { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +}; + +enum class LoopControlShift : unsigned { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + Max = 0x7fffffff, +}; + +enum class LoopControlMask : unsigned { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, +}; + +enum class FunctionControlShift : unsigned { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + Max = 0x7fffffff, +}; + +enum class FunctionControlMask : unsigned { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, +}; + +enum class MemorySemanticsShift : unsigned { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + Max = 0x7fffffff, +}; + +enum class MemorySemanticsMask : unsigned { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, +}; + +enum class MemoryAccessShift : unsigned { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + Max = 0x7fffffff, +}; + +enum class MemoryAccessMask : unsigned { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, +}; + +enum class Scope : unsigned { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + Max = 0x7fffffff, +}; + +enum class GroupOperation : unsigned { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + Max = 0x7fffffff, +}; + +enum class KernelEnqueueFlags : unsigned { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoShift : unsigned { + CmdExecTime = 0, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoMask : unsigned { + MaskNone = 0, + CmdExecTime = 0x00000001, +}; + +enum class Capability : unsigned { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + Max = 0x7fffffff, +}; + +enum class Op : unsigned { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + Max = 0x7fffffff, +}; + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.json new file mode 100644 index 0000000..9c0ff0a --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.json @@ -0,0 +1,1046 @@ +{ + "spv": + { + "meta": + { + "Comment": + [ + [ + "Copyright (c) 2014-2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + [ + "This header is automatically generated by the same tool that creates", + "the Binary Section of the SPIR-V specification." + ], + [ + "Enumeration tokens for SPIR-V, in various styles:", + " C, C++, C++11, JSON, Lua, Python", + "", + "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", + "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", + "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", + "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", + "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", + "", + "Some tokens act like mask values, which can be OR'd together,", + "while others are mutually exclusive. The mask-like ones have", + "\"Mask\" in their name, and a parallel enum that has the shift", + "amount (1 << x) for each corresponding enumerant." + ] + ], + "MagicNumber": 119734787, + "Version": 66048, + "Revision": 2, + "OpCodeMask": 65535, + "WordCountShift": 16 + }, + "enum": + [ + { + "Name": "SourceLanguage", + "Type": "Value", + "Values": + { + "Unknown": 0, + "ESSL": 1, + "GLSL": 2, + "OpenCL_C": 3, + "OpenCL_CPP": 4, + "HLSL": 5 + } + }, + { + "Name": "ExecutionModel", + "Type": "Value", + "Values": + { + "Vertex": 0, + "TessellationControl": 1, + "TessellationEvaluation": 2, + "Geometry": 3, + "Fragment": 4, + "GLCompute": 5, + "Kernel": 6 + } + }, + { + "Name": "AddressingModel", + "Type": "Value", + "Values": + { + "Logical": 0, + "Physical32": 1, + "Physical64": 2 + } + }, + { + "Name": "MemoryModel", + "Type": "Value", + "Values": + { + "Simple": 0, + "GLSL450": 1, + "OpenCL": 2 + } + }, + { + "Name": "ExecutionMode", + "Type": "Value", + "Values": + { + "Invocations": 0, + "SpacingEqual": 1, + "SpacingFractionalEven": 2, + "SpacingFractionalOdd": 3, + "VertexOrderCw": 4, + "VertexOrderCcw": 5, + "PixelCenterInteger": 6, + "OriginUpperLeft": 7, + "OriginLowerLeft": 8, + "EarlyFragmentTests": 9, + "PointMode": 10, + "Xfb": 11, + "DepthReplacing": 12, + "DepthGreater": 14, + "DepthLess": 15, + "DepthUnchanged": 16, + "LocalSize": 17, + "LocalSizeHint": 18, + "InputPoints": 19, + "InputLines": 20, + "InputLinesAdjacency": 21, + "Triangles": 22, + "InputTrianglesAdjacency": 23, + "Quads": 24, + "Isolines": 25, + "OutputVertices": 26, + "OutputPoints": 27, + "OutputLineStrip": 28, + "OutputTriangleStrip": 29, + "VecTypeHint": 30, + "ContractionOff": 31, + "Initializer": 33, + "Finalizer": 34, + "SubgroupSize": 35, + "SubgroupsPerWorkgroup": 36, + "SubgroupsPerWorkgroupId": 37, + "LocalSizeId": 38, + "LocalSizeHintId": 39, + "PostDepthCoverage": 4446, + "StencilRefReplacingEXT": 5027 + } + }, + { + "Name": "StorageClass", + "Type": "Value", + "Values": + { + "UniformConstant": 0, + "Input": 1, + "Uniform": 2, + "Output": 3, + "Workgroup": 4, + "CrossWorkgroup": 5, + "Private": 6, + "Function": 7, + "Generic": 8, + "PushConstant": 9, + "AtomicCounter": 10, + "Image": 11, + "StorageBuffer": 12 + } + }, + { + "Name": "Dim", + "Type": "Value", + "Values": + { + "Dim1D": 0, + "Dim2D": 1, + "Dim3D": 2, + "Cube": 3, + "Rect": 4, + "Buffer": 5, + "SubpassData": 6 + } + }, + { + "Name": "SamplerAddressingMode", + "Type": "Value", + "Values": + { + "None": 0, + "ClampToEdge": 1, + "Clamp": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "SamplerFilterMode", + "Type": "Value", + "Values": + { + "Nearest": 0, + "Linear": 1 + } + }, + { + "Name": "ImageFormat", + "Type": "Value", + "Values": + { + "Unknown": 0, + "Rgba32f": 1, + "Rgba16f": 2, + "R32f": 3, + "Rgba8": 4, + "Rgba8Snorm": 5, + "Rg32f": 6, + "Rg16f": 7, + "R11fG11fB10f": 8, + "R16f": 9, + "Rgba16": 10, + "Rgb10A2": 11, + "Rg16": 12, + "Rg8": 13, + "R16": 14, + "R8": 15, + "Rgba16Snorm": 16, + "Rg16Snorm": 17, + "Rg8Snorm": 18, + "R16Snorm": 19, + "R8Snorm": 20, + "Rgba32i": 21, + "Rgba16i": 22, + "Rgba8i": 23, + "R32i": 24, + "Rg32i": 25, + "Rg16i": 26, + "Rg8i": 27, + "R16i": 28, + "R8i": 29, + "Rgba32ui": 30, + "Rgba16ui": 31, + "Rgba8ui": 32, + "R32ui": 33, + "Rgb10a2ui": 34, + "Rg32ui": 35, + "Rg16ui": 36, + "Rg8ui": 37, + "R16ui": 38, + "R8ui": 39 + } + }, + { + "Name": "ImageChannelOrder", + "Type": "Value", + "Values": + { + "R": 0, + "A": 1, + "RG": 2, + "RA": 3, + "RGB": 4, + "RGBA": 5, + "BGRA": 6, + "ARGB": 7, + "Intensity": 8, + "Luminance": 9, + "Rx": 10, + "RGx": 11, + "RGBx": 12, + "Depth": 13, + "DepthStencil": 14, + "sRGB": 15, + "sRGBx": 16, + "sRGBA": 17, + "sBGRA": 18, + "ABGR": 19 + } + }, + { + "Name": "ImageChannelDataType", + "Type": "Value", + "Values": + { + "SnormInt8": 0, + "SnormInt16": 1, + "UnormInt8": 2, + "UnormInt16": 3, + "UnormShort565": 4, + "UnormShort555": 5, + "UnormInt101010": 6, + "SignedInt8": 7, + "SignedInt16": 8, + "SignedInt32": 9, + "UnsignedInt8": 10, + "UnsignedInt16": 11, + "UnsignedInt32": 12, + "HalfFloat": 13, + "Float": 14, + "UnormInt24": 15, + "UnormInt101010_2": 16 + } + }, + { + "Name": "ImageOperands", + "Type": "Bit", + "Values": + { + "Bias": 0, + "Lod": 1, + "Grad": 2, + "ConstOffset": 3, + "Offset": 4, + "ConstOffsets": 5, + "Sample": 6, + "MinLod": 7 + } + }, + { + "Name": "FPFastMathMode", + "Type": "Bit", + "Values": + { + "NotNaN": 0, + "NotInf": 1, + "NSZ": 2, + "AllowRecip": 3, + "Fast": 4 + } + }, + { + "Name": "FPRoundingMode", + "Type": "Value", + "Values": + { + "RTE": 0, + "RTZ": 1, + "RTP": 2, + "RTN": 3 + } + }, + { + "Name": "LinkageType", + "Type": "Value", + "Values": + { + "Export": 0, + "Import": 1 + } + }, + { + "Name": "AccessQualifier", + "Type": "Value", + "Values": + { + "ReadOnly": 0, + "WriteOnly": 1, + "ReadWrite": 2 + } + }, + { + "Name": "FunctionParameterAttribute", + "Type": "Value", + "Values": + { + "Zext": 0, + "Sext": 1, + "ByVal": 2, + "Sret": 3, + "NoAlias": 4, + "NoCapture": 5, + "NoWrite": 6, + "NoReadWrite": 7 + } + }, + { + "Name": "Decoration", + "Type": "Value", + "Values": + { + "RelaxedPrecision": 0, + "SpecId": 1, + "Block": 2, + "BufferBlock": 3, + "RowMajor": 4, + "ColMajor": 5, + "ArrayStride": 6, + "MatrixStride": 7, + "GLSLShared": 8, + "GLSLPacked": 9, + "CPacked": 10, + "BuiltIn": 11, + "NoPerspective": 13, + "Flat": 14, + "Patch": 15, + "Centroid": 16, + "Sample": 17, + "Invariant": 18, + "Restrict": 19, + "Aliased": 20, + "Volatile": 21, + "Constant": 22, + "Coherent": 23, + "NonWritable": 24, + "NonReadable": 25, + "Uniform": 26, + "SaturatedConversion": 28, + "Stream": 29, + "Location": 30, + "Component": 31, + "Index": 32, + "Binding": 33, + "DescriptorSet": 34, + "Offset": 35, + "XfbBuffer": 36, + "XfbStride": 37, + "FuncParamAttr": 38, + "FPRoundingMode": 39, + "FPFastMathMode": 40, + "LinkageAttributes": 41, + "NoContraction": 42, + "InputAttachmentIndex": 43, + "Alignment": 44, + "MaxByteOffset": 45, + "AlignmentId": 46, + "MaxByteOffsetId": 47, + "ExplicitInterpAMD": 4999, + "OverrideCoverageNV": 5248, + "PassthroughNV": 5250, + "ViewportRelativeNV": 5252, + "SecondaryViewportRelativeNV": 5256, + "HlslCounterBufferGOOGLE": 5634, + "HlslSemanticGOOGLE": 5635 + } + }, + { + "Name": "BuiltIn", + "Type": "Value", + "Values": + { + "Position": 0, + "PointSize": 1, + "ClipDistance": 3, + "CullDistance": 4, + "VertexId": 5, + "InstanceId": 6, + "PrimitiveId": 7, + "InvocationId": 8, + "Layer": 9, + "ViewportIndex": 10, + "TessLevelOuter": 11, + "TessLevelInner": 12, + "TessCoord": 13, + "PatchVertices": 14, + "FragCoord": 15, + "PointCoord": 16, + "FrontFacing": 17, + "SampleId": 18, + "SamplePosition": 19, + "SampleMask": 20, + "FragDepth": 22, + "HelperInvocation": 23, + "NumWorkgroups": 24, + "WorkgroupSize": 25, + "WorkgroupId": 26, + "LocalInvocationId": 27, + "GlobalInvocationId": 28, + "LocalInvocationIndex": 29, + "WorkDim": 30, + "GlobalSize": 31, + "EnqueuedWorkgroupSize": 32, + "GlobalOffset": 33, + "GlobalLinearId": 34, + "SubgroupSize": 36, + "SubgroupMaxSize": 37, + "NumSubgroups": 38, + "NumEnqueuedSubgroups": 39, + "SubgroupId": 40, + "SubgroupLocalInvocationId": 41, + "VertexIndex": 42, + "InstanceIndex": 43, + "SubgroupEqMaskKHR": 4416, + "SubgroupGeMaskKHR": 4417, + "SubgroupGtMaskKHR": 4418, + "SubgroupLeMaskKHR": 4419, + "SubgroupLtMaskKHR": 4420, + "BaseVertex": 4424, + "BaseInstance": 4425, + "DrawIndex": 4426, + "DeviceIndex": 4438, + "ViewIndex": 4440, + "BaryCoordNoPerspAMD": 4992, + "BaryCoordNoPerspCentroidAMD": 4993, + "BaryCoordNoPerspSampleAMD": 4994, + "BaryCoordSmoothAMD": 4995, + "BaryCoordSmoothCentroidAMD": 4996, + "BaryCoordSmoothSampleAMD": 4997, + "BaryCoordPullModelAMD": 4998, + "FragStencilRefEXT": 5014, + "ViewportMaskNV": 5253, + "SecondaryPositionNV": 5257, + "SecondaryViewportMaskNV": 5258, + "PositionPerViewNV": 5261, + "ViewportMaskPerViewNV": 5262 + } + }, + { + "Name": "SelectionControl", + "Type": "Bit", + "Values": + { + "Flatten": 0, + "DontFlatten": 1 + } + }, + { + "Name": "LoopControl", + "Type": "Bit", + "Values": + { + "Unroll": 0, + "DontUnroll": 1, + "DependencyInfinite": 2, + "DependencyLength": 3 + } + }, + { + "Name": "FunctionControl", + "Type": "Bit", + "Values": + { + "Inline": 0, + "DontInline": 1, + "Pure": 2, + "Const": 3 + } + }, + { + "Name": "MemorySemantics", + "Type": "Bit", + "Values": + { + "Acquire": 1, + "Release": 2, + "AcquireRelease": 3, + "SequentiallyConsistent": 4, + "UniformMemory": 6, + "SubgroupMemory": 7, + "WorkgroupMemory": 8, + "CrossWorkgroupMemory": 9, + "AtomicCounterMemory": 10, + "ImageMemory": 11 + } + }, + { + "Name": "MemoryAccess", + "Type": "Bit", + "Values": + { + "Volatile": 0, + "Aligned": 1, + "Nontemporal": 2 + } + }, + { + "Name": "Scope", + "Type": "Value", + "Values": + { + "CrossDevice": 0, + "Device": 1, + "Workgroup": 2, + "Subgroup": 3, + "Invocation": 4 + } + }, + { + "Name": "GroupOperation", + "Type": "Value", + "Values": + { + "Reduce": 0, + "InclusiveScan": 1, + "ExclusiveScan": 2 + } + }, + { + "Name": "KernelEnqueueFlags", + "Type": "Value", + "Values": + { + "NoWait": 0, + "WaitKernel": 1, + "WaitWorkGroup": 2 + } + }, + { + "Name": "KernelProfilingInfo", + "Type": "Bit", + "Values": + { + "CmdExecTime": 0 + } + }, + { + "Name": "Capability", + "Type": "Value", + "Values": + { + "Matrix": 0, + "Shader": 1, + "Geometry": 2, + "Tessellation": 3, + "Addresses": 4, + "Linkage": 5, + "Kernel": 6, + "Vector16": 7, + "Float16Buffer": 8, + "Float16": 9, + "Float64": 10, + "Int64": 11, + "Int64Atomics": 12, + "ImageBasic": 13, + "ImageReadWrite": 14, + "ImageMipmap": 15, + "Pipes": 17, + "Groups": 18, + "DeviceEnqueue": 19, + "LiteralSampler": 20, + "AtomicStorage": 21, + "Int16": 22, + "TessellationPointSize": 23, + "GeometryPointSize": 24, + "ImageGatherExtended": 25, + "StorageImageMultisample": 27, + "UniformBufferArrayDynamicIndexing": 28, + "SampledImageArrayDynamicIndexing": 29, + "StorageBufferArrayDynamicIndexing": 30, + "StorageImageArrayDynamicIndexing": 31, + "ClipDistance": 32, + "CullDistance": 33, + "ImageCubeArray": 34, + "SampleRateShading": 35, + "ImageRect": 36, + "SampledRect": 37, + "GenericPointer": 38, + "Int8": 39, + "InputAttachment": 40, + "SparseResidency": 41, + "MinLod": 42, + "Sampled1D": 43, + "Image1D": 44, + "SampledCubeArray": 45, + "SampledBuffer": 46, + "ImageBuffer": 47, + "ImageMSArray": 48, + "StorageImageExtendedFormats": 49, + "ImageQuery": 50, + "DerivativeControl": 51, + "InterpolationFunction": 52, + "TransformFeedback": 53, + "GeometryStreams": 54, + "StorageImageReadWithoutFormat": 55, + "StorageImageWriteWithoutFormat": 56, + "MultiViewport": 57, + "SubgroupDispatch": 58, + "NamedBarrier": 59, + "PipeStorage": 60, + "SubgroupBallotKHR": 4423, + "DrawParameters": 4427, + "SubgroupVoteKHR": 4431, + "StorageBuffer16BitAccess": 4433, + "StorageUniformBufferBlock16": 4433, + "StorageUniform16": 4434, + "UniformAndStorageBuffer16BitAccess": 4434, + "StoragePushConstant16": 4435, + "StorageInputOutput16": 4436, + "DeviceGroup": 4437, + "MultiView": 4439, + "VariablePointersStorageBuffer": 4441, + "VariablePointers": 4442, + "AtomicStorageOps": 4445, + "SampleMaskPostDepthCoverage": 4447, + "ImageGatherBiasLodAMD": 5009, + "FragmentMaskAMD": 5010, + "StencilExportEXT": 5013, + "ImageReadWriteLodAMD": 5015, + "SampleMaskOverrideCoverageNV": 5249, + "GeometryShaderPassthroughNV": 5251, + "ShaderViewportIndexLayerEXT": 5254, + "ShaderViewportIndexLayerNV": 5254, + "ShaderViewportMaskNV": 5255, + "ShaderStereoViewNV": 5259, + "PerViewAttributesNV": 5260, + "SubgroupShuffleINTEL": 5568, + "SubgroupBufferBlockIOINTEL": 5569, + "SubgroupImageBlockIOINTEL": 5570 + } + }, + { + "Name": "Op", + "Type": "Value", + "Values": + { + "OpNop": 0, + "OpUndef": 1, + "OpSourceContinued": 2, + "OpSource": 3, + "OpSourceExtension": 4, + "OpName": 5, + "OpMemberName": 6, + "OpString": 7, + "OpLine": 8, + "OpExtension": 10, + "OpExtInstImport": 11, + "OpExtInst": 12, + "OpMemoryModel": 14, + "OpEntryPoint": 15, + "OpExecutionMode": 16, + "OpCapability": 17, + "OpTypeVoid": 19, + "OpTypeBool": 20, + "OpTypeInt": 21, + "OpTypeFloat": 22, + "OpTypeVector": 23, + "OpTypeMatrix": 24, + "OpTypeImage": 25, + "OpTypeSampler": 26, + "OpTypeSampledImage": 27, + "OpTypeArray": 28, + "OpTypeRuntimeArray": 29, + "OpTypeStruct": 30, + "OpTypeOpaque": 31, + "OpTypePointer": 32, + "OpTypeFunction": 33, + "OpTypeEvent": 34, + "OpTypeDeviceEvent": 35, + "OpTypeReserveId": 36, + "OpTypeQueue": 37, + "OpTypePipe": 38, + "OpTypeForwardPointer": 39, + "OpConstantTrue": 41, + "OpConstantFalse": 42, + "OpConstant": 43, + "OpConstantComposite": 44, + "OpConstantSampler": 45, + "OpConstantNull": 46, + "OpSpecConstantTrue": 48, + "OpSpecConstantFalse": 49, + "OpSpecConstant": 50, + "OpSpecConstantComposite": 51, + "OpSpecConstantOp": 52, + "OpFunction": 54, + "OpFunctionParameter": 55, + "OpFunctionEnd": 56, + "OpFunctionCall": 57, + "OpVariable": 59, + "OpImageTexelPointer": 60, + "OpLoad": 61, + "OpStore": 62, + "OpCopyMemory": 63, + "OpCopyMemorySized": 64, + "OpAccessChain": 65, + "OpInBoundsAccessChain": 66, + "OpPtrAccessChain": 67, + "OpArrayLength": 68, + "OpGenericPtrMemSemantics": 69, + "OpInBoundsPtrAccessChain": 70, + "OpDecorate": 71, + "OpMemberDecorate": 72, + "OpDecorationGroup": 73, + "OpGroupDecorate": 74, + "OpGroupMemberDecorate": 75, + "OpVectorExtractDynamic": 77, + "OpVectorInsertDynamic": 78, + "OpVectorShuffle": 79, + "OpCompositeConstruct": 80, + "OpCompositeExtract": 81, + "OpCompositeInsert": 82, + "OpCopyObject": 83, + "OpTranspose": 84, + "OpSampledImage": 86, + "OpImageSampleImplicitLod": 87, + "OpImageSampleExplicitLod": 88, + "OpImageSampleDrefImplicitLod": 89, + "OpImageSampleDrefExplicitLod": 90, + "OpImageSampleProjImplicitLod": 91, + "OpImageSampleProjExplicitLod": 92, + "OpImageSampleProjDrefImplicitLod": 93, + "OpImageSampleProjDrefExplicitLod": 94, + "OpImageFetch": 95, + "OpImageGather": 96, + "OpImageDrefGather": 97, + "OpImageRead": 98, + "OpImageWrite": 99, + "OpImage": 100, + "OpImageQueryFormat": 101, + "OpImageQueryOrder": 102, + "OpImageQuerySizeLod": 103, + "OpImageQuerySize": 104, + "OpImageQueryLod": 105, + "OpImageQueryLevels": 106, + "OpImageQuerySamples": 107, + "OpConvertFToU": 109, + "OpConvertFToS": 110, + "OpConvertSToF": 111, + "OpConvertUToF": 112, + "OpUConvert": 113, + "OpSConvert": 114, + "OpFConvert": 115, + "OpQuantizeToF16": 116, + "OpConvertPtrToU": 117, + "OpSatConvertSToU": 118, + "OpSatConvertUToS": 119, + "OpConvertUToPtr": 120, + "OpPtrCastToGeneric": 121, + "OpGenericCastToPtr": 122, + "OpGenericCastToPtrExplicit": 123, + "OpBitcast": 124, + "OpSNegate": 126, + "OpFNegate": 127, + "OpIAdd": 128, + "OpFAdd": 129, + "OpISub": 130, + "OpFSub": 131, + "OpIMul": 132, + "OpFMul": 133, + "OpUDiv": 134, + "OpSDiv": 135, + "OpFDiv": 136, + "OpUMod": 137, + "OpSRem": 138, + "OpSMod": 139, + "OpFRem": 140, + "OpFMod": 141, + "OpVectorTimesScalar": 142, + "OpMatrixTimesScalar": 143, + "OpVectorTimesMatrix": 144, + "OpMatrixTimesVector": 145, + "OpMatrixTimesMatrix": 146, + "OpOuterProduct": 147, + "OpDot": 148, + "OpIAddCarry": 149, + "OpISubBorrow": 150, + "OpUMulExtended": 151, + "OpSMulExtended": 152, + "OpAny": 154, + "OpAll": 155, + "OpIsNan": 156, + "OpIsInf": 157, + "OpIsFinite": 158, + "OpIsNormal": 159, + "OpSignBitSet": 160, + "OpLessOrGreater": 161, + "OpOrdered": 162, + "OpUnordered": 163, + "OpLogicalEqual": 164, + "OpLogicalNotEqual": 165, + "OpLogicalOr": 166, + "OpLogicalAnd": 167, + "OpLogicalNot": 168, + "OpSelect": 169, + "OpIEqual": 170, + "OpINotEqual": 171, + "OpUGreaterThan": 172, + "OpSGreaterThan": 173, + "OpUGreaterThanEqual": 174, + "OpSGreaterThanEqual": 175, + "OpULessThan": 176, + "OpSLessThan": 177, + "OpULessThanEqual": 178, + "OpSLessThanEqual": 179, + "OpFOrdEqual": 180, + "OpFUnordEqual": 181, + "OpFOrdNotEqual": 182, + "OpFUnordNotEqual": 183, + "OpFOrdLessThan": 184, + "OpFUnordLessThan": 185, + "OpFOrdGreaterThan": 186, + "OpFUnordGreaterThan": 187, + "OpFOrdLessThanEqual": 188, + "OpFUnordLessThanEqual": 189, + "OpFOrdGreaterThanEqual": 190, + "OpFUnordGreaterThanEqual": 191, + "OpShiftRightLogical": 194, + "OpShiftRightArithmetic": 195, + "OpShiftLeftLogical": 196, + "OpBitwiseOr": 197, + "OpBitwiseXor": 198, + "OpBitwiseAnd": 199, + "OpNot": 200, + "OpBitFieldInsert": 201, + "OpBitFieldSExtract": 202, + "OpBitFieldUExtract": 203, + "OpBitReverse": 204, + "OpBitCount": 205, + "OpDPdx": 207, + "OpDPdy": 208, + "OpFwidth": 209, + "OpDPdxFine": 210, + "OpDPdyFine": 211, + "OpFwidthFine": 212, + "OpDPdxCoarse": 213, + "OpDPdyCoarse": 214, + "OpFwidthCoarse": 215, + "OpEmitVertex": 218, + "OpEndPrimitive": 219, + "OpEmitStreamVertex": 220, + "OpEndStreamPrimitive": 221, + "OpControlBarrier": 224, + "OpMemoryBarrier": 225, + "OpAtomicLoad": 227, + "OpAtomicStore": 228, + "OpAtomicExchange": 229, + "OpAtomicCompareExchange": 230, + "OpAtomicCompareExchangeWeak": 231, + "OpAtomicIIncrement": 232, + "OpAtomicIDecrement": 233, + "OpAtomicIAdd": 234, + "OpAtomicISub": 235, + "OpAtomicSMin": 236, + "OpAtomicUMin": 237, + "OpAtomicSMax": 238, + "OpAtomicUMax": 239, + "OpAtomicAnd": 240, + "OpAtomicOr": 241, + "OpAtomicXor": 242, + "OpPhi": 245, + "OpLoopMerge": 246, + "OpSelectionMerge": 247, + "OpLabel": 248, + "OpBranch": 249, + "OpBranchConditional": 250, + "OpSwitch": 251, + "OpKill": 252, + "OpReturn": 253, + "OpReturnValue": 254, + "OpUnreachable": 255, + "OpLifetimeStart": 256, + "OpLifetimeStop": 257, + "OpGroupAsyncCopy": 259, + "OpGroupWaitEvents": 260, + "OpGroupAll": 261, + "OpGroupAny": 262, + "OpGroupBroadcast": 263, + "OpGroupIAdd": 264, + "OpGroupFAdd": 265, + "OpGroupFMin": 266, + "OpGroupUMin": 267, + "OpGroupSMin": 268, + "OpGroupFMax": 269, + "OpGroupUMax": 270, + "OpGroupSMax": 271, + "OpReadPipe": 274, + "OpWritePipe": 275, + "OpReservedReadPipe": 276, + "OpReservedWritePipe": 277, + "OpReserveReadPipePackets": 278, + "OpReserveWritePipePackets": 279, + "OpCommitReadPipe": 280, + "OpCommitWritePipe": 281, + "OpIsValidReserveId": 282, + "OpGetNumPipePackets": 283, + "OpGetMaxPipePackets": 284, + "OpGroupReserveReadPipePackets": 285, + "OpGroupReserveWritePipePackets": 286, + "OpGroupCommitReadPipe": 287, + "OpGroupCommitWritePipe": 288, + "OpEnqueueMarker": 291, + "OpEnqueueKernel": 292, + "OpGetKernelNDrangeSubGroupCount": 293, + "OpGetKernelNDrangeMaxSubGroupSize": 294, + "OpGetKernelWorkGroupSize": 295, + "OpGetKernelPreferredWorkGroupSizeMultiple": 296, + "OpRetainEvent": 297, + "OpReleaseEvent": 298, + "OpCreateUserEvent": 299, + "OpIsValidEvent": 300, + "OpSetUserEventStatus": 301, + "OpCaptureEventProfilingInfo": 302, + "OpGetDefaultQueue": 303, + "OpBuildNDRange": 304, + "OpImageSparseSampleImplicitLod": 305, + "OpImageSparseSampleExplicitLod": 306, + "OpImageSparseSampleDrefImplicitLod": 307, + "OpImageSparseSampleDrefExplicitLod": 308, + "OpImageSparseSampleProjImplicitLod": 309, + "OpImageSparseSampleProjExplicitLod": 310, + "OpImageSparseSampleProjDrefImplicitLod": 311, + "OpImageSparseSampleProjDrefExplicitLod": 312, + "OpImageSparseFetch": 313, + "OpImageSparseGather": 314, + "OpImageSparseDrefGather": 315, + "OpImageSparseTexelsResident": 316, + "OpNoLine": 317, + "OpAtomicFlagTestAndSet": 318, + "OpAtomicFlagClear": 319, + "OpImageSparseRead": 320, + "OpSizeOf": 321, + "OpTypePipeStorage": 322, + "OpConstantPipeStorage": 323, + "OpCreatePipeFromPipeStorage": 324, + "OpGetKernelLocalSizeForSubgroupCount": 325, + "OpGetKernelMaxNumSubgroups": 326, + "OpTypeNamedBarrier": 327, + "OpNamedBarrierInitialize": 328, + "OpMemoryNamedBarrier": 329, + "OpModuleProcessed": 330, + "OpExecutionModeId": 331, + "OpDecorateId": 332, + "OpSubgroupBallotKHR": 4421, + "OpSubgroupFirstInvocationKHR": 4422, + "OpSubgroupAllKHR": 4428, + "OpSubgroupAnyKHR": 4429, + "OpSubgroupAllEqualKHR": 4430, + "OpSubgroupReadInvocationKHR": 4432, + "OpGroupIAddNonUniformAMD": 5000, + "OpGroupFAddNonUniformAMD": 5001, + "OpGroupFMinNonUniformAMD": 5002, + "OpGroupUMinNonUniformAMD": 5003, + "OpGroupSMinNonUniformAMD": 5004, + "OpGroupFMaxNonUniformAMD": 5005, + "OpGroupUMaxNonUniformAMD": 5006, + "OpGroupSMaxNonUniformAMD": 5007, + "OpFragmentMaskFetchAMD": 5011, + "OpFragmentFetchAMD": 5012, + "OpSubgroupShuffleINTEL": 5571, + "OpSubgroupShuffleDownINTEL": 5572, + "OpSubgroupShuffleUpINTEL": 5573, + "OpSubgroupShuffleXorINTEL": 5574, + "OpSubgroupBlockReadINTEL": 5575, + "OpSubgroupBlockWriteINTEL": 5576, + "OpSubgroupImageBlockReadINTEL": 5577, + "OpSubgroupImageBlockWriteINTEL": 5578, + "OpDecorateStringGOOGLE": 5632, + "OpMemberDecorateStringGOOGLE": 5633 + } + } + ] + } +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.lua b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.lua new file mode 100644 index 0000000..0de507d --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.lua @@ -0,0 +1,977 @@ +-- Copyright (c) 2014-2018 The Khronos Group Inc. +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and/or associated documentation files (the "Materials"), +-- to deal in the Materials without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Materials, and to permit persons to whom the +-- Materials are furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Materials. +-- +-- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +-- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +-- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +-- +-- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +-- IN THE MATERIALS. + +-- This header is automatically generated by the same tool that creates +-- the Binary Section of the SPIR-V specification. + +-- Enumeration tokens for SPIR-V, in various styles: +-- C, C++, C++11, JSON, Lua, Python +-- +-- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +-- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +-- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +-- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +-- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +-- +-- Some tokens act like mask values, which can be OR'd together, +-- while others are mutually exclusive. The mask-like ones have +-- "Mask" in their name, and a parallel enum that has the shift +-- amount (1 << x) for each corresponding enumerant. + +spv = { + MagicNumber = 0x07230203, + Version = 0x00010200, + Revision = 2, + OpCodeMask = 0xffff, + WordCountShift = 16, + + SourceLanguage = { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + }, + + ExecutionModel = { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + }, + + AddressingModel = { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + }, + + MemoryModel = { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + }, + + ExecutionMode = { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + StencilRefReplacingEXT = 5027, + }, + + StorageClass = { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + }, + + Dim = { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + }, + + SamplerAddressingMode = { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + }, + + SamplerFilterMode = { + Nearest = 0, + Linear = 1, + }, + + ImageFormat = { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + }, + + ImageChannelOrder = { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + }, + + ImageChannelDataType = { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + }, + + ImageOperandsShift = { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + }, + + ImageOperandsMask = { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + }, + + FPFastMathModeShift = { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + }, + + FPFastMathModeMask = { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + }, + + FPRoundingMode = { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + }, + + LinkageType = { + Export = 0, + Import = 1, + }, + + AccessQualifier = { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + }, + + FunctionParameterAttribute = { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + }, + + Decoration = { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + }, + + BuiltIn = { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMaskKHR = 4416, + SubgroupGeMaskKHR = 4417, + SubgroupGtMaskKHR = 4418, + SubgroupLeMaskKHR = 4419, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + DeviceIndex = 4438, + ViewIndex = 4440, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + }, + + SelectionControlShift = { + Flatten = 0, + DontFlatten = 1, + }, + + SelectionControlMask = { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + }, + + LoopControlShift = { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + }, + + LoopControlMask = { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + }, + + FunctionControlShift = { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + }, + + FunctionControlMask = { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + }, + + MemorySemanticsShift = { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + }, + + MemorySemanticsMask = { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + }, + + MemoryAccessShift = { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + }, + + MemoryAccessMask = { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + }, + + Scope = { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + }, + + GroupOperation = { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + }, + + KernelEnqueueFlags = { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + }, + + KernelProfilingInfoShift = { + CmdExecTime = 0, + }, + + KernelProfilingInfoMask = { + MaskNone = 0, + CmdExecTime = 0x00000001, + }, + + Capability = { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + }, + + Op = { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateStringGOOGLE = 5633, + }, + +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.py b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.py new file mode 100644 index 0000000..cefee4d --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/1.2/spirv.py @@ -0,0 +1,977 @@ +# Copyright (c) 2014-2018 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +# This header is automatically generated by the same tool that creates +# the Binary Section of the SPIR-V specification. + +# Enumeration tokens for SPIR-V, in various styles: +# C, C++, C++11, JSON, Lua, Python +# +# - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +# - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +# - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +# - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +# - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +# +# Some tokens act like mask values, which can be OR'd together, +# while others are mutually exclusive. The mask-like ones have +# "Mask" in their name, and a parallel enum that has the shift +# amount (1 << x) for each corresponding enumerant. + +spv = { + 'MagicNumber' : 0x07230203, + 'Version' : 0x00010200, + 'Revision' : 2, + 'OpCodeMask' : 0xffff, + 'WordCountShift' : 16, + + 'SourceLanguage' : { + 'Unknown' : 0, + 'ESSL' : 1, + 'GLSL' : 2, + 'OpenCL_C' : 3, + 'OpenCL_CPP' : 4, + 'HLSL' : 5, + }, + + 'ExecutionModel' : { + 'Vertex' : 0, + 'TessellationControl' : 1, + 'TessellationEvaluation' : 2, + 'Geometry' : 3, + 'Fragment' : 4, + 'GLCompute' : 5, + 'Kernel' : 6, + }, + + 'AddressingModel' : { + 'Logical' : 0, + 'Physical32' : 1, + 'Physical64' : 2, + }, + + 'MemoryModel' : { + 'Simple' : 0, + 'GLSL450' : 1, + 'OpenCL' : 2, + }, + + 'ExecutionMode' : { + 'Invocations' : 0, + 'SpacingEqual' : 1, + 'SpacingFractionalEven' : 2, + 'SpacingFractionalOdd' : 3, + 'VertexOrderCw' : 4, + 'VertexOrderCcw' : 5, + 'PixelCenterInteger' : 6, + 'OriginUpperLeft' : 7, + 'OriginLowerLeft' : 8, + 'EarlyFragmentTests' : 9, + 'PointMode' : 10, + 'Xfb' : 11, + 'DepthReplacing' : 12, + 'DepthGreater' : 14, + 'DepthLess' : 15, + 'DepthUnchanged' : 16, + 'LocalSize' : 17, + 'LocalSizeHint' : 18, + 'InputPoints' : 19, + 'InputLines' : 20, + 'InputLinesAdjacency' : 21, + 'Triangles' : 22, + 'InputTrianglesAdjacency' : 23, + 'Quads' : 24, + 'Isolines' : 25, + 'OutputVertices' : 26, + 'OutputPoints' : 27, + 'OutputLineStrip' : 28, + 'OutputTriangleStrip' : 29, + 'VecTypeHint' : 30, + 'ContractionOff' : 31, + 'Initializer' : 33, + 'Finalizer' : 34, + 'SubgroupSize' : 35, + 'SubgroupsPerWorkgroup' : 36, + 'SubgroupsPerWorkgroupId' : 37, + 'LocalSizeId' : 38, + 'LocalSizeHintId' : 39, + 'PostDepthCoverage' : 4446, + 'StencilRefReplacingEXT' : 5027, + }, + + 'StorageClass' : { + 'UniformConstant' : 0, + 'Input' : 1, + 'Uniform' : 2, + 'Output' : 3, + 'Workgroup' : 4, + 'CrossWorkgroup' : 5, + 'Private' : 6, + 'Function' : 7, + 'Generic' : 8, + 'PushConstant' : 9, + 'AtomicCounter' : 10, + 'Image' : 11, + 'StorageBuffer' : 12, + }, + + 'Dim' : { + 'Dim1D' : 0, + 'Dim2D' : 1, + 'Dim3D' : 2, + 'Cube' : 3, + 'Rect' : 4, + 'Buffer' : 5, + 'SubpassData' : 6, + }, + + 'SamplerAddressingMode' : { + 'None' : 0, + 'ClampToEdge' : 1, + 'Clamp' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'SamplerFilterMode' : { + 'Nearest' : 0, + 'Linear' : 1, + }, + + 'ImageFormat' : { + 'Unknown' : 0, + 'Rgba32f' : 1, + 'Rgba16f' : 2, + 'R32f' : 3, + 'Rgba8' : 4, + 'Rgba8Snorm' : 5, + 'Rg32f' : 6, + 'Rg16f' : 7, + 'R11fG11fB10f' : 8, + 'R16f' : 9, + 'Rgba16' : 10, + 'Rgb10A2' : 11, + 'Rg16' : 12, + 'Rg8' : 13, + 'R16' : 14, + 'R8' : 15, + 'Rgba16Snorm' : 16, + 'Rg16Snorm' : 17, + 'Rg8Snorm' : 18, + 'R16Snorm' : 19, + 'R8Snorm' : 20, + 'Rgba32i' : 21, + 'Rgba16i' : 22, + 'Rgba8i' : 23, + 'R32i' : 24, + 'Rg32i' : 25, + 'Rg16i' : 26, + 'Rg8i' : 27, + 'R16i' : 28, + 'R8i' : 29, + 'Rgba32ui' : 30, + 'Rgba16ui' : 31, + 'Rgba8ui' : 32, + 'R32ui' : 33, + 'Rgb10a2ui' : 34, + 'Rg32ui' : 35, + 'Rg16ui' : 36, + 'Rg8ui' : 37, + 'R16ui' : 38, + 'R8ui' : 39, + }, + + 'ImageChannelOrder' : { + 'R' : 0, + 'A' : 1, + 'RG' : 2, + 'RA' : 3, + 'RGB' : 4, + 'RGBA' : 5, + 'BGRA' : 6, + 'ARGB' : 7, + 'Intensity' : 8, + 'Luminance' : 9, + 'Rx' : 10, + 'RGx' : 11, + 'RGBx' : 12, + 'Depth' : 13, + 'DepthStencil' : 14, + 'sRGB' : 15, + 'sRGBx' : 16, + 'sRGBA' : 17, + 'sBGRA' : 18, + 'ABGR' : 19, + }, + + 'ImageChannelDataType' : { + 'SnormInt8' : 0, + 'SnormInt16' : 1, + 'UnormInt8' : 2, + 'UnormInt16' : 3, + 'UnormShort565' : 4, + 'UnormShort555' : 5, + 'UnormInt101010' : 6, + 'SignedInt8' : 7, + 'SignedInt16' : 8, + 'SignedInt32' : 9, + 'UnsignedInt8' : 10, + 'UnsignedInt16' : 11, + 'UnsignedInt32' : 12, + 'HalfFloat' : 13, + 'Float' : 14, + 'UnormInt24' : 15, + 'UnormInt101010_2' : 16, + }, + + 'ImageOperandsShift' : { + 'Bias' : 0, + 'Lod' : 1, + 'Grad' : 2, + 'ConstOffset' : 3, + 'Offset' : 4, + 'ConstOffsets' : 5, + 'Sample' : 6, + 'MinLod' : 7, + }, + + 'ImageOperandsMask' : { + 'MaskNone' : 0, + 'Bias' : 0x00000001, + 'Lod' : 0x00000002, + 'Grad' : 0x00000004, + 'ConstOffset' : 0x00000008, + 'Offset' : 0x00000010, + 'ConstOffsets' : 0x00000020, + 'Sample' : 0x00000040, + 'MinLod' : 0x00000080, + }, + + 'FPFastMathModeShift' : { + 'NotNaN' : 0, + 'NotInf' : 1, + 'NSZ' : 2, + 'AllowRecip' : 3, + 'Fast' : 4, + }, + + 'FPFastMathModeMask' : { + 'MaskNone' : 0, + 'NotNaN' : 0x00000001, + 'NotInf' : 0x00000002, + 'NSZ' : 0x00000004, + 'AllowRecip' : 0x00000008, + 'Fast' : 0x00000010, + }, + + 'FPRoundingMode' : { + 'RTE' : 0, + 'RTZ' : 1, + 'RTP' : 2, + 'RTN' : 3, + }, + + 'LinkageType' : { + 'Export' : 0, + 'Import' : 1, + }, + + 'AccessQualifier' : { + 'ReadOnly' : 0, + 'WriteOnly' : 1, + 'ReadWrite' : 2, + }, + + 'FunctionParameterAttribute' : { + 'Zext' : 0, + 'Sext' : 1, + 'ByVal' : 2, + 'Sret' : 3, + 'NoAlias' : 4, + 'NoCapture' : 5, + 'NoWrite' : 6, + 'NoReadWrite' : 7, + }, + + 'Decoration' : { + 'RelaxedPrecision' : 0, + 'SpecId' : 1, + 'Block' : 2, + 'BufferBlock' : 3, + 'RowMajor' : 4, + 'ColMajor' : 5, + 'ArrayStride' : 6, + 'MatrixStride' : 7, + 'GLSLShared' : 8, + 'GLSLPacked' : 9, + 'CPacked' : 10, + 'BuiltIn' : 11, + 'NoPerspective' : 13, + 'Flat' : 14, + 'Patch' : 15, + 'Centroid' : 16, + 'Sample' : 17, + 'Invariant' : 18, + 'Restrict' : 19, + 'Aliased' : 20, + 'Volatile' : 21, + 'Constant' : 22, + 'Coherent' : 23, + 'NonWritable' : 24, + 'NonReadable' : 25, + 'Uniform' : 26, + 'SaturatedConversion' : 28, + 'Stream' : 29, + 'Location' : 30, + 'Component' : 31, + 'Index' : 32, + 'Binding' : 33, + 'DescriptorSet' : 34, + 'Offset' : 35, + 'XfbBuffer' : 36, + 'XfbStride' : 37, + 'FuncParamAttr' : 38, + 'FPRoundingMode' : 39, + 'FPFastMathMode' : 40, + 'LinkageAttributes' : 41, + 'NoContraction' : 42, + 'InputAttachmentIndex' : 43, + 'Alignment' : 44, + 'MaxByteOffset' : 45, + 'AlignmentId' : 46, + 'MaxByteOffsetId' : 47, + 'ExplicitInterpAMD' : 4999, + 'OverrideCoverageNV' : 5248, + 'PassthroughNV' : 5250, + 'ViewportRelativeNV' : 5252, + 'SecondaryViewportRelativeNV' : 5256, + 'HlslCounterBufferGOOGLE' : 5634, + 'HlslSemanticGOOGLE' : 5635, + }, + + 'BuiltIn' : { + 'Position' : 0, + 'PointSize' : 1, + 'ClipDistance' : 3, + 'CullDistance' : 4, + 'VertexId' : 5, + 'InstanceId' : 6, + 'PrimitiveId' : 7, + 'InvocationId' : 8, + 'Layer' : 9, + 'ViewportIndex' : 10, + 'TessLevelOuter' : 11, + 'TessLevelInner' : 12, + 'TessCoord' : 13, + 'PatchVertices' : 14, + 'FragCoord' : 15, + 'PointCoord' : 16, + 'FrontFacing' : 17, + 'SampleId' : 18, + 'SamplePosition' : 19, + 'SampleMask' : 20, + 'FragDepth' : 22, + 'HelperInvocation' : 23, + 'NumWorkgroups' : 24, + 'WorkgroupSize' : 25, + 'WorkgroupId' : 26, + 'LocalInvocationId' : 27, + 'GlobalInvocationId' : 28, + 'LocalInvocationIndex' : 29, + 'WorkDim' : 30, + 'GlobalSize' : 31, + 'EnqueuedWorkgroupSize' : 32, + 'GlobalOffset' : 33, + 'GlobalLinearId' : 34, + 'SubgroupSize' : 36, + 'SubgroupMaxSize' : 37, + 'NumSubgroups' : 38, + 'NumEnqueuedSubgroups' : 39, + 'SubgroupId' : 40, + 'SubgroupLocalInvocationId' : 41, + 'VertexIndex' : 42, + 'InstanceIndex' : 43, + 'SubgroupEqMaskKHR' : 4416, + 'SubgroupGeMaskKHR' : 4417, + 'SubgroupGtMaskKHR' : 4418, + 'SubgroupLeMaskKHR' : 4419, + 'SubgroupLtMaskKHR' : 4420, + 'BaseVertex' : 4424, + 'BaseInstance' : 4425, + 'DrawIndex' : 4426, + 'DeviceIndex' : 4438, + 'ViewIndex' : 4440, + 'BaryCoordNoPerspAMD' : 4992, + 'BaryCoordNoPerspCentroidAMD' : 4993, + 'BaryCoordNoPerspSampleAMD' : 4994, + 'BaryCoordSmoothAMD' : 4995, + 'BaryCoordSmoothCentroidAMD' : 4996, + 'BaryCoordSmoothSampleAMD' : 4997, + 'BaryCoordPullModelAMD' : 4998, + 'FragStencilRefEXT' : 5014, + 'ViewportMaskNV' : 5253, + 'SecondaryPositionNV' : 5257, + 'SecondaryViewportMaskNV' : 5258, + 'PositionPerViewNV' : 5261, + 'ViewportMaskPerViewNV' : 5262, + }, + + 'SelectionControlShift' : { + 'Flatten' : 0, + 'DontFlatten' : 1, + }, + + 'SelectionControlMask' : { + 'MaskNone' : 0, + 'Flatten' : 0x00000001, + 'DontFlatten' : 0x00000002, + }, + + 'LoopControlShift' : { + 'Unroll' : 0, + 'DontUnroll' : 1, + 'DependencyInfinite' : 2, + 'DependencyLength' : 3, + }, + + 'LoopControlMask' : { + 'MaskNone' : 0, + 'Unroll' : 0x00000001, + 'DontUnroll' : 0x00000002, + 'DependencyInfinite' : 0x00000004, + 'DependencyLength' : 0x00000008, + }, + + 'FunctionControlShift' : { + 'Inline' : 0, + 'DontInline' : 1, + 'Pure' : 2, + 'Const' : 3, + }, + + 'FunctionControlMask' : { + 'MaskNone' : 0, + 'Inline' : 0x00000001, + 'DontInline' : 0x00000002, + 'Pure' : 0x00000004, + 'Const' : 0x00000008, + }, + + 'MemorySemanticsShift' : { + 'Acquire' : 1, + 'Release' : 2, + 'AcquireRelease' : 3, + 'SequentiallyConsistent' : 4, + 'UniformMemory' : 6, + 'SubgroupMemory' : 7, + 'WorkgroupMemory' : 8, + 'CrossWorkgroupMemory' : 9, + 'AtomicCounterMemory' : 10, + 'ImageMemory' : 11, + }, + + 'MemorySemanticsMask' : { + 'MaskNone' : 0, + 'Acquire' : 0x00000002, + 'Release' : 0x00000004, + 'AcquireRelease' : 0x00000008, + 'SequentiallyConsistent' : 0x00000010, + 'UniformMemory' : 0x00000040, + 'SubgroupMemory' : 0x00000080, + 'WorkgroupMemory' : 0x00000100, + 'CrossWorkgroupMemory' : 0x00000200, + 'AtomicCounterMemory' : 0x00000400, + 'ImageMemory' : 0x00000800, + }, + + 'MemoryAccessShift' : { + 'Volatile' : 0, + 'Aligned' : 1, + 'Nontemporal' : 2, + }, + + 'MemoryAccessMask' : { + 'MaskNone' : 0, + 'Volatile' : 0x00000001, + 'Aligned' : 0x00000002, + 'Nontemporal' : 0x00000004, + }, + + 'Scope' : { + 'CrossDevice' : 0, + 'Device' : 1, + 'Workgroup' : 2, + 'Subgroup' : 3, + 'Invocation' : 4, + }, + + 'GroupOperation' : { + 'Reduce' : 0, + 'InclusiveScan' : 1, + 'ExclusiveScan' : 2, + }, + + 'KernelEnqueueFlags' : { + 'NoWait' : 0, + 'WaitKernel' : 1, + 'WaitWorkGroup' : 2, + }, + + 'KernelProfilingInfoShift' : { + 'CmdExecTime' : 0, + }, + + 'KernelProfilingInfoMask' : { + 'MaskNone' : 0, + 'CmdExecTime' : 0x00000001, + }, + + 'Capability' : { + 'Matrix' : 0, + 'Shader' : 1, + 'Geometry' : 2, + 'Tessellation' : 3, + 'Addresses' : 4, + 'Linkage' : 5, + 'Kernel' : 6, + 'Vector16' : 7, + 'Float16Buffer' : 8, + 'Float16' : 9, + 'Float64' : 10, + 'Int64' : 11, + 'Int64Atomics' : 12, + 'ImageBasic' : 13, + 'ImageReadWrite' : 14, + 'ImageMipmap' : 15, + 'Pipes' : 17, + 'Groups' : 18, + 'DeviceEnqueue' : 19, + 'LiteralSampler' : 20, + 'AtomicStorage' : 21, + 'Int16' : 22, + 'TessellationPointSize' : 23, + 'GeometryPointSize' : 24, + 'ImageGatherExtended' : 25, + 'StorageImageMultisample' : 27, + 'UniformBufferArrayDynamicIndexing' : 28, + 'SampledImageArrayDynamicIndexing' : 29, + 'StorageBufferArrayDynamicIndexing' : 30, + 'StorageImageArrayDynamicIndexing' : 31, + 'ClipDistance' : 32, + 'CullDistance' : 33, + 'ImageCubeArray' : 34, + 'SampleRateShading' : 35, + 'ImageRect' : 36, + 'SampledRect' : 37, + 'GenericPointer' : 38, + 'Int8' : 39, + 'InputAttachment' : 40, + 'SparseResidency' : 41, + 'MinLod' : 42, + 'Sampled1D' : 43, + 'Image1D' : 44, + 'SampledCubeArray' : 45, + 'SampledBuffer' : 46, + 'ImageBuffer' : 47, + 'ImageMSArray' : 48, + 'StorageImageExtendedFormats' : 49, + 'ImageQuery' : 50, + 'DerivativeControl' : 51, + 'InterpolationFunction' : 52, + 'TransformFeedback' : 53, + 'GeometryStreams' : 54, + 'StorageImageReadWithoutFormat' : 55, + 'StorageImageWriteWithoutFormat' : 56, + 'MultiViewport' : 57, + 'SubgroupDispatch' : 58, + 'NamedBarrier' : 59, + 'PipeStorage' : 60, + 'SubgroupBallotKHR' : 4423, + 'DrawParameters' : 4427, + 'SubgroupVoteKHR' : 4431, + 'StorageBuffer16BitAccess' : 4433, + 'StorageUniformBufferBlock16' : 4433, + 'StorageUniform16' : 4434, + 'UniformAndStorageBuffer16BitAccess' : 4434, + 'StoragePushConstant16' : 4435, + 'StorageInputOutput16' : 4436, + 'DeviceGroup' : 4437, + 'MultiView' : 4439, + 'VariablePointersStorageBuffer' : 4441, + 'VariablePointers' : 4442, + 'AtomicStorageOps' : 4445, + 'SampleMaskPostDepthCoverage' : 4447, + 'ImageGatherBiasLodAMD' : 5009, + 'FragmentMaskAMD' : 5010, + 'StencilExportEXT' : 5013, + 'ImageReadWriteLodAMD' : 5015, + 'SampleMaskOverrideCoverageNV' : 5249, + 'GeometryShaderPassthroughNV' : 5251, + 'ShaderViewportIndexLayerEXT' : 5254, + 'ShaderViewportIndexLayerNV' : 5254, + 'ShaderViewportMaskNV' : 5255, + 'ShaderStereoViewNV' : 5259, + 'PerViewAttributesNV' : 5260, + 'SubgroupShuffleINTEL' : 5568, + 'SubgroupBufferBlockIOINTEL' : 5569, + 'SubgroupImageBlockIOINTEL' : 5570, + }, + + 'Op' : { + 'OpNop' : 0, + 'OpUndef' : 1, + 'OpSourceContinued' : 2, + 'OpSource' : 3, + 'OpSourceExtension' : 4, + 'OpName' : 5, + 'OpMemberName' : 6, + 'OpString' : 7, + 'OpLine' : 8, + 'OpExtension' : 10, + 'OpExtInstImport' : 11, + 'OpExtInst' : 12, + 'OpMemoryModel' : 14, + 'OpEntryPoint' : 15, + 'OpExecutionMode' : 16, + 'OpCapability' : 17, + 'OpTypeVoid' : 19, + 'OpTypeBool' : 20, + 'OpTypeInt' : 21, + 'OpTypeFloat' : 22, + 'OpTypeVector' : 23, + 'OpTypeMatrix' : 24, + 'OpTypeImage' : 25, + 'OpTypeSampler' : 26, + 'OpTypeSampledImage' : 27, + 'OpTypeArray' : 28, + 'OpTypeRuntimeArray' : 29, + 'OpTypeStruct' : 30, + 'OpTypeOpaque' : 31, + 'OpTypePointer' : 32, + 'OpTypeFunction' : 33, + 'OpTypeEvent' : 34, + 'OpTypeDeviceEvent' : 35, + 'OpTypeReserveId' : 36, + 'OpTypeQueue' : 37, + 'OpTypePipe' : 38, + 'OpTypeForwardPointer' : 39, + 'OpConstantTrue' : 41, + 'OpConstantFalse' : 42, + 'OpConstant' : 43, + 'OpConstantComposite' : 44, + 'OpConstantSampler' : 45, + 'OpConstantNull' : 46, + 'OpSpecConstantTrue' : 48, + 'OpSpecConstantFalse' : 49, + 'OpSpecConstant' : 50, + 'OpSpecConstantComposite' : 51, + 'OpSpecConstantOp' : 52, + 'OpFunction' : 54, + 'OpFunctionParameter' : 55, + 'OpFunctionEnd' : 56, + 'OpFunctionCall' : 57, + 'OpVariable' : 59, + 'OpImageTexelPointer' : 60, + 'OpLoad' : 61, + 'OpStore' : 62, + 'OpCopyMemory' : 63, + 'OpCopyMemorySized' : 64, + 'OpAccessChain' : 65, + 'OpInBoundsAccessChain' : 66, + 'OpPtrAccessChain' : 67, + 'OpArrayLength' : 68, + 'OpGenericPtrMemSemantics' : 69, + 'OpInBoundsPtrAccessChain' : 70, + 'OpDecorate' : 71, + 'OpMemberDecorate' : 72, + 'OpDecorationGroup' : 73, + 'OpGroupDecorate' : 74, + 'OpGroupMemberDecorate' : 75, + 'OpVectorExtractDynamic' : 77, + 'OpVectorInsertDynamic' : 78, + 'OpVectorShuffle' : 79, + 'OpCompositeConstruct' : 80, + 'OpCompositeExtract' : 81, + 'OpCompositeInsert' : 82, + 'OpCopyObject' : 83, + 'OpTranspose' : 84, + 'OpSampledImage' : 86, + 'OpImageSampleImplicitLod' : 87, + 'OpImageSampleExplicitLod' : 88, + 'OpImageSampleDrefImplicitLod' : 89, + 'OpImageSampleDrefExplicitLod' : 90, + 'OpImageSampleProjImplicitLod' : 91, + 'OpImageSampleProjExplicitLod' : 92, + 'OpImageSampleProjDrefImplicitLod' : 93, + 'OpImageSampleProjDrefExplicitLod' : 94, + 'OpImageFetch' : 95, + 'OpImageGather' : 96, + 'OpImageDrefGather' : 97, + 'OpImageRead' : 98, + 'OpImageWrite' : 99, + 'OpImage' : 100, + 'OpImageQueryFormat' : 101, + 'OpImageQueryOrder' : 102, + 'OpImageQuerySizeLod' : 103, + 'OpImageQuerySize' : 104, + 'OpImageQueryLod' : 105, + 'OpImageQueryLevels' : 106, + 'OpImageQuerySamples' : 107, + 'OpConvertFToU' : 109, + 'OpConvertFToS' : 110, + 'OpConvertSToF' : 111, + 'OpConvertUToF' : 112, + 'OpUConvert' : 113, + 'OpSConvert' : 114, + 'OpFConvert' : 115, + 'OpQuantizeToF16' : 116, + 'OpConvertPtrToU' : 117, + 'OpSatConvertSToU' : 118, + 'OpSatConvertUToS' : 119, + 'OpConvertUToPtr' : 120, + 'OpPtrCastToGeneric' : 121, + 'OpGenericCastToPtr' : 122, + 'OpGenericCastToPtrExplicit' : 123, + 'OpBitcast' : 124, + 'OpSNegate' : 126, + 'OpFNegate' : 127, + 'OpIAdd' : 128, + 'OpFAdd' : 129, + 'OpISub' : 130, + 'OpFSub' : 131, + 'OpIMul' : 132, + 'OpFMul' : 133, + 'OpUDiv' : 134, + 'OpSDiv' : 135, + 'OpFDiv' : 136, + 'OpUMod' : 137, + 'OpSRem' : 138, + 'OpSMod' : 139, + 'OpFRem' : 140, + 'OpFMod' : 141, + 'OpVectorTimesScalar' : 142, + 'OpMatrixTimesScalar' : 143, + 'OpVectorTimesMatrix' : 144, + 'OpMatrixTimesVector' : 145, + 'OpMatrixTimesMatrix' : 146, + 'OpOuterProduct' : 147, + 'OpDot' : 148, + 'OpIAddCarry' : 149, + 'OpISubBorrow' : 150, + 'OpUMulExtended' : 151, + 'OpSMulExtended' : 152, + 'OpAny' : 154, + 'OpAll' : 155, + 'OpIsNan' : 156, + 'OpIsInf' : 157, + 'OpIsFinite' : 158, + 'OpIsNormal' : 159, + 'OpSignBitSet' : 160, + 'OpLessOrGreater' : 161, + 'OpOrdered' : 162, + 'OpUnordered' : 163, + 'OpLogicalEqual' : 164, + 'OpLogicalNotEqual' : 165, + 'OpLogicalOr' : 166, + 'OpLogicalAnd' : 167, + 'OpLogicalNot' : 168, + 'OpSelect' : 169, + 'OpIEqual' : 170, + 'OpINotEqual' : 171, + 'OpUGreaterThan' : 172, + 'OpSGreaterThan' : 173, + 'OpUGreaterThanEqual' : 174, + 'OpSGreaterThanEqual' : 175, + 'OpULessThan' : 176, + 'OpSLessThan' : 177, + 'OpULessThanEqual' : 178, + 'OpSLessThanEqual' : 179, + 'OpFOrdEqual' : 180, + 'OpFUnordEqual' : 181, + 'OpFOrdNotEqual' : 182, + 'OpFUnordNotEqual' : 183, + 'OpFOrdLessThan' : 184, + 'OpFUnordLessThan' : 185, + 'OpFOrdGreaterThan' : 186, + 'OpFUnordGreaterThan' : 187, + 'OpFOrdLessThanEqual' : 188, + 'OpFUnordLessThanEqual' : 189, + 'OpFOrdGreaterThanEqual' : 190, + 'OpFUnordGreaterThanEqual' : 191, + 'OpShiftRightLogical' : 194, + 'OpShiftRightArithmetic' : 195, + 'OpShiftLeftLogical' : 196, + 'OpBitwiseOr' : 197, + 'OpBitwiseXor' : 198, + 'OpBitwiseAnd' : 199, + 'OpNot' : 200, + 'OpBitFieldInsert' : 201, + 'OpBitFieldSExtract' : 202, + 'OpBitFieldUExtract' : 203, + 'OpBitReverse' : 204, + 'OpBitCount' : 205, + 'OpDPdx' : 207, + 'OpDPdy' : 208, + 'OpFwidth' : 209, + 'OpDPdxFine' : 210, + 'OpDPdyFine' : 211, + 'OpFwidthFine' : 212, + 'OpDPdxCoarse' : 213, + 'OpDPdyCoarse' : 214, + 'OpFwidthCoarse' : 215, + 'OpEmitVertex' : 218, + 'OpEndPrimitive' : 219, + 'OpEmitStreamVertex' : 220, + 'OpEndStreamPrimitive' : 221, + 'OpControlBarrier' : 224, + 'OpMemoryBarrier' : 225, + 'OpAtomicLoad' : 227, + 'OpAtomicStore' : 228, + 'OpAtomicExchange' : 229, + 'OpAtomicCompareExchange' : 230, + 'OpAtomicCompareExchangeWeak' : 231, + 'OpAtomicIIncrement' : 232, + 'OpAtomicIDecrement' : 233, + 'OpAtomicIAdd' : 234, + 'OpAtomicISub' : 235, + 'OpAtomicSMin' : 236, + 'OpAtomicUMin' : 237, + 'OpAtomicSMax' : 238, + 'OpAtomicUMax' : 239, + 'OpAtomicAnd' : 240, + 'OpAtomicOr' : 241, + 'OpAtomicXor' : 242, + 'OpPhi' : 245, + 'OpLoopMerge' : 246, + 'OpSelectionMerge' : 247, + 'OpLabel' : 248, + 'OpBranch' : 249, + 'OpBranchConditional' : 250, + 'OpSwitch' : 251, + 'OpKill' : 252, + 'OpReturn' : 253, + 'OpReturnValue' : 254, + 'OpUnreachable' : 255, + 'OpLifetimeStart' : 256, + 'OpLifetimeStop' : 257, + 'OpGroupAsyncCopy' : 259, + 'OpGroupWaitEvents' : 260, + 'OpGroupAll' : 261, + 'OpGroupAny' : 262, + 'OpGroupBroadcast' : 263, + 'OpGroupIAdd' : 264, + 'OpGroupFAdd' : 265, + 'OpGroupFMin' : 266, + 'OpGroupUMin' : 267, + 'OpGroupSMin' : 268, + 'OpGroupFMax' : 269, + 'OpGroupUMax' : 270, + 'OpGroupSMax' : 271, + 'OpReadPipe' : 274, + 'OpWritePipe' : 275, + 'OpReservedReadPipe' : 276, + 'OpReservedWritePipe' : 277, + 'OpReserveReadPipePackets' : 278, + 'OpReserveWritePipePackets' : 279, + 'OpCommitReadPipe' : 280, + 'OpCommitWritePipe' : 281, + 'OpIsValidReserveId' : 282, + 'OpGetNumPipePackets' : 283, + 'OpGetMaxPipePackets' : 284, + 'OpGroupReserveReadPipePackets' : 285, + 'OpGroupReserveWritePipePackets' : 286, + 'OpGroupCommitReadPipe' : 287, + 'OpGroupCommitWritePipe' : 288, + 'OpEnqueueMarker' : 291, + 'OpEnqueueKernel' : 292, + 'OpGetKernelNDrangeSubGroupCount' : 293, + 'OpGetKernelNDrangeMaxSubGroupSize' : 294, + 'OpGetKernelWorkGroupSize' : 295, + 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, + 'OpRetainEvent' : 297, + 'OpReleaseEvent' : 298, + 'OpCreateUserEvent' : 299, + 'OpIsValidEvent' : 300, + 'OpSetUserEventStatus' : 301, + 'OpCaptureEventProfilingInfo' : 302, + 'OpGetDefaultQueue' : 303, + 'OpBuildNDRange' : 304, + 'OpImageSparseSampleImplicitLod' : 305, + 'OpImageSparseSampleExplicitLod' : 306, + 'OpImageSparseSampleDrefImplicitLod' : 307, + 'OpImageSparseSampleDrefExplicitLod' : 308, + 'OpImageSparseSampleProjImplicitLod' : 309, + 'OpImageSparseSampleProjExplicitLod' : 310, + 'OpImageSparseSampleProjDrefImplicitLod' : 311, + 'OpImageSparseSampleProjDrefExplicitLod' : 312, + 'OpImageSparseFetch' : 313, + 'OpImageSparseGather' : 314, + 'OpImageSparseDrefGather' : 315, + 'OpImageSparseTexelsResident' : 316, + 'OpNoLine' : 317, + 'OpAtomicFlagTestAndSet' : 318, + 'OpAtomicFlagClear' : 319, + 'OpImageSparseRead' : 320, + 'OpSizeOf' : 321, + 'OpTypePipeStorage' : 322, + 'OpConstantPipeStorage' : 323, + 'OpCreatePipeFromPipeStorage' : 324, + 'OpGetKernelLocalSizeForSubgroupCount' : 325, + 'OpGetKernelMaxNumSubgroups' : 326, + 'OpTypeNamedBarrier' : 327, + 'OpNamedBarrierInitialize' : 328, + 'OpMemoryNamedBarrier' : 329, + 'OpModuleProcessed' : 330, + 'OpExecutionModeId' : 331, + 'OpDecorateId' : 332, + 'OpSubgroupBallotKHR' : 4421, + 'OpSubgroupFirstInvocationKHR' : 4422, + 'OpSubgroupAllKHR' : 4428, + 'OpSubgroupAnyKHR' : 4429, + 'OpSubgroupAllEqualKHR' : 4430, + 'OpSubgroupReadInvocationKHR' : 4432, + 'OpGroupIAddNonUniformAMD' : 5000, + 'OpGroupFAddNonUniformAMD' : 5001, + 'OpGroupFMinNonUniformAMD' : 5002, + 'OpGroupUMinNonUniformAMD' : 5003, + 'OpGroupSMinNonUniformAMD' : 5004, + 'OpGroupFMaxNonUniformAMD' : 5005, + 'OpGroupUMaxNonUniformAMD' : 5006, + 'OpGroupSMaxNonUniformAMD' : 5007, + 'OpFragmentMaskFetchAMD' : 5011, + 'OpFragmentFetchAMD' : 5012, + 'OpSubgroupShuffleINTEL' : 5571, + 'OpSubgroupShuffleDownINTEL' : 5572, + 'OpSubgroupShuffleUpINTEL' : 5573, + 'OpSubgroupShuffleXorINTEL' : 5574, + 'OpSubgroupBlockReadINTEL' : 5575, + 'OpSubgroupBlockWriteINTEL' : 5576, + 'OpSubgroupImageBlockReadINTEL' : 5577, + 'OpSubgroupImageBlockWriteINTEL' : 5578, + 'OpDecorateStringGOOGLE' : 5632, + 'OpMemberDecorateStringGOOGLE' : 5633, + }, + +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml b/third_party/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml new file mode 100644 index 0000000..d1ebaab --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/spir-v.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_gcn_shader.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_gcn_shader.h new file mode 100644 index 0000000..80165ae --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_gcn_shader.h @@ -0,0 +1,52 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_AMD_gcn_shader_H_ +#define SPIRV_UNIFIED1_AMD_gcn_shader_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AMD_gcn_shaderRevision = 2, + AMD_gcn_shaderRevision_BitWidthPadding = 0x7fffffff +}; + +enum AMD_gcn_shaderInstructions { + AMD_gcn_shaderCubeFaceIndexAMD = 1, + AMD_gcn_shaderCubeFaceCoordAMD = 2, + AMD_gcn_shaderTimeAMD = 3, + AMD_gcn_shaderInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_AMD_gcn_shader_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_ballot.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_ballot.h new file mode 100644 index 0000000..8a8bb6e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_ballot.h @@ -0,0 +1,53 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_AMD_shader_ballot_H_ +#define SPIRV_UNIFIED1_AMD_shader_ballot_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AMD_shader_ballotRevision = 5, + AMD_shader_ballotRevision_BitWidthPadding = 0x7fffffff +}; + +enum AMD_shader_ballotInstructions { + AMD_shader_ballotSwizzleInvocationsAMD = 1, + AMD_shader_ballotSwizzleInvocationsMaskedAMD = 2, + AMD_shader_ballotWriteInvocationAMD = 3, + AMD_shader_ballotMbcntAMD = 4, + AMD_shader_ballotInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_AMD_shader_ballot_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_explicit_vertex_parameter.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_explicit_vertex_parameter.h new file mode 100644 index 0000000..12b6480 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_explicit_vertex_parameter.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_AMD_shader_explicit_vertex_parameter_H_ +#define SPIRV_UNIFIED1_AMD_shader_explicit_vertex_parameter_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AMD_shader_explicit_vertex_parameterRevision = 4, + AMD_shader_explicit_vertex_parameterRevision_BitWidthPadding = 0x7fffffff +}; + +enum AMD_shader_explicit_vertex_parameterInstructions { + AMD_shader_explicit_vertex_parameterInterpolateAtVertexAMD = 1, + AMD_shader_explicit_vertex_parameterInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_AMD_shader_explicit_vertex_parameter_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_trinary_minmax.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_trinary_minmax.h new file mode 100644 index 0000000..1b14997 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/AMD_shader_trinary_minmax.h @@ -0,0 +1,58 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_AMD_shader_trinary_minmax_H_ +#define SPIRV_UNIFIED1_AMD_shader_trinary_minmax_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + AMD_shader_trinary_minmaxRevision = 4, + AMD_shader_trinary_minmaxRevision_BitWidthPadding = 0x7fffffff +}; + +enum AMD_shader_trinary_minmaxInstructions { + AMD_shader_trinary_minmaxFMin3AMD = 1, + AMD_shader_trinary_minmaxUMin3AMD = 2, + AMD_shader_trinary_minmaxSMin3AMD = 3, + AMD_shader_trinary_minmaxFMax3AMD = 4, + AMD_shader_trinary_minmaxUMax3AMD = 5, + AMD_shader_trinary_minmaxSMax3AMD = 6, + AMD_shader_trinary_minmaxFMid3AMD = 7, + AMD_shader_trinary_minmaxUMid3AMD = 8, + AMD_shader_trinary_minmaxSMid3AMD = 9, + AMD_shader_trinary_minmaxInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_AMD_shader_trinary_minmax_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/DebugInfo.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/DebugInfo.h new file mode 100644 index 0000000..c50a131 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/DebugInfo.h @@ -0,0 +1,143 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#ifndef SPIRV_UNIFIED1_DebugInfo_H_ +#define SPIRV_UNIFIED1_DebugInfo_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + DebugInfoVersion = 100, + DebugInfoVersion_BitWidthPadding = 0x7fffffff +}; +enum { + DebugInfoRevision = 1, + DebugInfoRevision_BitWidthPadding = 0x7fffffff +}; + +enum DebugInfoInstructions { + DebugInfoDebugInfoNone = 0, + DebugInfoDebugCompilationUnit = 1, + DebugInfoDebugTypeBasic = 2, + DebugInfoDebugTypePointer = 3, + DebugInfoDebugTypeQualifier = 4, + DebugInfoDebugTypeArray = 5, + DebugInfoDebugTypeVector = 6, + DebugInfoDebugTypedef = 7, + DebugInfoDebugTypeFunction = 8, + DebugInfoDebugTypeEnum = 9, + DebugInfoDebugTypeComposite = 10, + DebugInfoDebugTypeMember = 11, + DebugInfoDebugTypeInheritance = 12, + DebugInfoDebugTypePtrToMember = 13, + DebugInfoDebugTypeTemplate = 14, + DebugInfoDebugTypeTemplateParameter = 15, + DebugInfoDebugTypeTemplateTemplateParameter = 16, + DebugInfoDebugTypeTemplateParameterPack = 17, + DebugInfoDebugGlobalVariable = 18, + DebugInfoDebugFunctionDeclaration = 19, + DebugInfoDebugFunction = 20, + DebugInfoDebugLexicalBlock = 21, + DebugInfoDebugLexicalBlockDiscriminator = 22, + DebugInfoDebugScope = 23, + DebugInfoDebugNoScope = 24, + DebugInfoDebugInlinedAt = 25, + DebugInfoDebugLocalVariable = 26, + DebugInfoDebugInlinedVariable = 27, + DebugInfoDebugDeclare = 28, + DebugInfoDebugValue = 29, + DebugInfoDebugOperation = 30, + DebugInfoDebugExpression = 31, + DebugInfoDebugMacroDef = 32, + DebugInfoDebugMacroUndef = 33, + DebugInfoInstructionsMax = 0x7fffffff +}; + + +enum DebugInfoDebugInfoFlags { + DebugInfoFlagIsProtected = 0x01, + DebugInfoFlagIsPrivate = 0x02, + DebugInfoFlagIsPublic = 0x03, + DebugInfoFlagIsLocal = 0x04, + DebugInfoFlagIsDefinition = 0x08, + DebugInfoFlagFwdDecl = 0x10, + DebugInfoFlagArtificial = 0x20, + DebugInfoFlagExplicit = 0x40, + DebugInfoFlagPrototyped = 0x80, + DebugInfoFlagObjectPointer = 0x100, + DebugInfoFlagStaticMember = 0x200, + DebugInfoFlagIndirectVariable = 0x400, + DebugInfoFlagLValueReference = 0x800, + DebugInfoFlagRValueReference = 0x1000, + DebugInfoFlagIsOptimized = 0x2000, + DebugInfoDebugInfoFlagsMax = 0x7fffffff +}; + +enum DebugInfoDebugBaseTypeAttributeEncoding { + DebugInfoUnspecified = 0, + DebugInfoAddress = 1, + DebugInfoBoolean = 2, + DebugInfoFloat = 4, + DebugInfoSigned = 5, + DebugInfoSignedChar = 6, + DebugInfoUnsigned = 7, + DebugInfoUnsignedChar = 8, + DebugInfoDebugBaseTypeAttributeEncodingMax = 0x7fffffff +}; + +enum DebugInfoDebugCompositeType { + DebugInfoClass = 0, + DebugInfoStructure = 1, + DebugInfoUnion = 2, + DebugInfoDebugCompositeTypeMax = 0x7fffffff +}; + +enum DebugInfoDebugTypeQualifier { + DebugInfoConstType = 0, + DebugInfoVolatileType = 1, + DebugInfoRestrictType = 2, + DebugInfoDebugTypeQualifierMax = 0x7fffffff +}; + +enum DebugInfoDebugOperation { + DebugInfoDeref = 0, + DebugInfoPlus = 1, + DebugInfoMinus = 2, + DebugInfoPlusUconst = 3, + DebugInfoBitPiece = 4, + DebugInfoSwap = 5, + DebugInfoXderef = 6, + DebugInfoStackValue = 7, + DebugInfoConstu = 8, + DebugInfoDebugOperationMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_DebugInfo_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/GLSL.std.450.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/GLSL.std.450.h new file mode 100644 index 0000000..54cc00e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/GLSL.std.450.h @@ -0,0 +1,131 @@ +/* +** Copyright (c) 2014-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h new file mode 100644 index 0000000..fa7061d --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticClspvReflection.h @@ -0,0 +1,73 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticClspvReflection_H_ +#define SPIRV_UNIFIED1_NonSemanticClspvReflection_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticClspvReflectionRevision = 1, + NonSemanticClspvReflectionRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticClspvReflectionInstructions { + NonSemanticClspvReflectionKernel = 1, + NonSemanticClspvReflectionArgumentInfo = 2, + NonSemanticClspvReflectionArgumentStorageBuffer = 3, + NonSemanticClspvReflectionArgumentUniform = 4, + NonSemanticClspvReflectionArgumentPodStorageBuffer = 5, + NonSemanticClspvReflectionArgumentPodUniform = 6, + NonSemanticClspvReflectionArgumentPodPushConstant = 7, + NonSemanticClspvReflectionArgumentSampledImage = 8, + NonSemanticClspvReflectionArgumentStorageImage = 9, + NonSemanticClspvReflectionArgumentSampler = 10, + NonSemanticClspvReflectionArgumentWorkgroup = 11, + NonSemanticClspvReflectionSpecConstantWorkgroupSize = 12, + NonSemanticClspvReflectionSpecConstantGlobalOffset = 13, + NonSemanticClspvReflectionSpecConstantWorkDim = 14, + NonSemanticClspvReflectionPushConstantGlobalOffset = 15, + NonSemanticClspvReflectionPushConstantEnqueuedLocalSize = 16, + NonSemanticClspvReflectionPushConstantGlobalSize = 17, + NonSemanticClspvReflectionPushConstantRegionOffset = 18, + NonSemanticClspvReflectionPushConstantNumWorkgroups = 19, + NonSemanticClspvReflectionPushConstantRegionGroupOffset = 20, + NonSemanticClspvReflectionConstantDataStorageBuffer = 21, + NonSemanticClspvReflectionConstantDataUniform = 22, + NonSemanticClspvReflectionLiteralSampler = 23, + NonSemanticClspvReflectionPropertyRequiredWorkgroupSize = 24, + NonSemanticClspvReflectionInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticClspvReflection_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugPrintf.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugPrintf.h new file mode 100644 index 0000000..83796d7 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/NonSemanticDebugPrintf.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and/or associated documentation files (the +// "Materials"), to deal in the Materials without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Materials, and to +// permit persons to whom the Materials are furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +// + +#ifndef SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ +#define SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticDebugPrintfRevision = 1, + NonSemanticDebugPrintfRevision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticDebugPrintfInstructions { + NonSemanticDebugPrintfDebugPrintf = 1, + NonSemanticDebugPrintfInstructionsMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticDebugPrintf_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCL.std.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCL.std.h new file mode 100644 index 0000000..2745e30 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCL.std.h @@ -0,0 +1,401 @@ +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +#ifndef OPENCLstd_H +#define OPENCLstd_H + +#ifdef __cplusplus +namespace OpenCLLIB { + +enum Entrypoints { + + // Section 2.1: Math extended instructions + Acos = 0, + Acosh = 1, + Acospi = 2, + Asin = 3, + Asinh = 4, + Asinpi = 5, + Atan = 6, + Atan2 = 7, + Atanh = 8, + Atanpi = 9, + Atan2pi = 10, + Cbrt = 11, + Ceil = 12, + Copysign = 13, + Cos = 14, + Cosh = 15, + Cospi = 16, + Erfc = 17, + Erf = 18, + Exp = 19, + Exp2 = 20, + Exp10 = 21, + Expm1 = 22, + Fabs = 23, + Fdim = 24, + Floor = 25, + Fma = 26, + Fmax = 27, + Fmin = 28, + Fmod = 29, + Fract = 30, + Frexp = 31, + Hypot = 32, + Ilogb = 33, + Ldexp = 34, + Lgamma = 35, + Lgamma_r = 36, + Log = 37, + Log2 = 38, + Log10 = 39, + Log1p = 40, + Logb = 41, + Mad = 42, + Maxmag = 43, + Minmag = 44, + Modf = 45, + Nan = 46, + Nextafter = 47, + Pow = 48, + Pown = 49, + Powr = 50, + Remainder = 51, + Remquo = 52, + Rint = 53, + Rootn = 54, + Round = 55, + Rsqrt = 56, + Sin = 57, + Sincos = 58, + Sinh = 59, + Sinpi = 60, + Sqrt = 61, + Tan = 62, + Tanh = 63, + Tanpi = 64, + Tgamma = 65, + Trunc = 66, + Half_cos = 67, + Half_divide = 68, + Half_exp = 69, + Half_exp2 = 70, + Half_exp10 = 71, + Half_log = 72, + Half_log2 = 73, + Half_log10 = 74, + Half_powr = 75, + Half_recip = 76, + Half_rsqrt = 77, + Half_sin = 78, + Half_sqrt = 79, + Half_tan = 80, + Native_cos = 81, + Native_divide = 82, + Native_exp = 83, + Native_exp2 = 84, + Native_exp10 = 85, + Native_log = 86, + Native_log2 = 87, + Native_log10 = 88, + Native_powr = 89, + Native_recip = 90, + Native_rsqrt = 91, + Native_sin = 92, + Native_sqrt = 93, + Native_tan = 94, + + // Section 2.2: Integer instructions + SAbs = 141, + SAbs_diff = 142, + SAdd_sat = 143, + UAdd_sat = 144, + SHadd = 145, + UHadd = 146, + SRhadd = 147, + URhadd = 148, + SClamp = 149, + UClamp = 150, + Clz = 151, + Ctz = 152, + SMad_hi = 153, + UMad_sat = 154, + SMad_sat = 155, + SMax = 156, + UMax = 157, + SMin = 158, + UMin = 159, + SMul_hi = 160, + Rotate = 161, + SSub_sat = 162, + USub_sat = 163, + U_Upsample = 164, + S_Upsample = 165, + Popcount = 166, + SMad24 = 167, + UMad24 = 168, + SMul24 = 169, + UMul24 = 170, + UAbs = 201, + UAbs_diff = 202, + UMul_hi = 203, + UMad_hi = 204, + + // Section 2.3: Common instructions + FClamp = 95, + Degrees = 96, + FMax_common = 97, + FMin_common = 98, + Mix = 99, + Radians = 100, + Step = 101, + Smoothstep = 102, + Sign = 103, + + // Section 2.4: Geometric instructions + Cross = 104, + Distance = 105, + Length = 106, + Normalize = 107, + Fast_distance = 108, + Fast_length = 109, + Fast_normalize = 110, + + // Section 2.5: Relational instructions + Bitselect = 186, + Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + Vloadn = 171, + Vstoren = 172, + Vload_half = 173, + Vload_halfn = 174, + Vstore_half = 175, + Vstore_half_r = 176, + Vstore_halfn = 177, + Vstore_halfn_r = 178, + Vloada_halfn = 179, + Vstorea_halfn = 180, + Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + Shuffle = 182, + Shuffle2 = 183, + + // Section 2.8: Misc instructions + Printf = 184, + Prefetch = 185, +}; + +} // end namespace OpenCLLIB + +#else + +enum OpenCLstd_Entrypoints { + + // Section 2.1: Math extended instructions + OpenCLstd_Acos = 0, + OpenCLstd_Acosh = 1, + OpenCLstd_Acospi = 2, + OpenCLstd_Asin = 3, + OpenCLstd_Asinh = 4, + OpenCLstd_Asinpi = 5, + OpenCLstd_Atan = 6, + OpenCLstd_Atan2 = 7, + OpenCLstd_Atanh = 8, + OpenCLstd_Atanpi = 9, + OpenCLstd_Atan2pi = 10, + OpenCLstd_Cbrt = 11, + OpenCLstd_Ceil = 12, + OpenCLstd_Copysign = 13, + OpenCLstd_Cos = 14, + OpenCLstd_Cosh = 15, + OpenCLstd_Cospi = 16, + OpenCLstd_Erfc = 17, + OpenCLstd_Erf = 18, + OpenCLstd_Exp = 19, + OpenCLstd_Exp2 = 20, + OpenCLstd_Exp10 = 21, + OpenCLstd_Expm1 = 22, + OpenCLstd_Fabs = 23, + OpenCLstd_Fdim = 24, + OpenCLstd_Floor = 25, + OpenCLstd_Fma = 26, + OpenCLstd_Fmax = 27, + OpenCLstd_Fmin = 28, + OpenCLstd_Fmod = 29, + OpenCLstd_Fract = 30, + OpenCLstd_Frexp = 31, + OpenCLstd_Hypot = 32, + OpenCLstd_Ilogb = 33, + OpenCLstd_Ldexp = 34, + OpenCLstd_Lgamma = 35, + OpenCLstd_Lgamma_r = 36, + OpenCLstd_Log = 37, + OpenCLstd_Log2 = 38, + OpenCLstd_Log10 = 39, + OpenCLstd_Log1p = 40, + OpenCLstd_Logb = 41, + OpenCLstd_Mad = 42, + OpenCLstd_Maxmag = 43, + OpenCLstd_Minmag = 44, + OpenCLstd_Modf = 45, + OpenCLstd_Nan = 46, + OpenCLstd_Nextafter = 47, + OpenCLstd_Pow = 48, + OpenCLstd_Pown = 49, + OpenCLstd_Powr = 50, + OpenCLstd_Remainder = 51, + OpenCLstd_Remquo = 52, + OpenCLstd_Rint = 53, + OpenCLstd_Rootn = 54, + OpenCLstd_Round = 55, + OpenCLstd_Rsqrt = 56, + OpenCLstd_Sin = 57, + OpenCLstd_Sincos = 58, + OpenCLstd_Sinh = 59, + OpenCLstd_Sinpi = 60, + OpenCLstd_Sqrt = 61, + OpenCLstd_Tan = 62, + OpenCLstd_Tanh = 63, + OpenCLstd_Tanpi = 64, + OpenCLstd_Tgamma = 65, + OpenCLstd_Trunc = 66, + OpenCLstd_Half_cos = 67, + OpenCLstd_Half_divide = 68, + OpenCLstd_Half_exp = 69, + OpenCLstd_Half_exp2 = 70, + OpenCLstd_Half_exp10 = 71, + OpenCLstd_Half_log = 72, + OpenCLstd_Half_log2 = 73, + OpenCLstd_Half_log10 = 74, + OpenCLstd_Half_powr = 75, + OpenCLstd_Half_recip = 76, + OpenCLstd_Half_rsqrt = 77, + OpenCLstd_Half_sin = 78, + OpenCLstd_Half_sqrt = 79, + OpenCLstd_Half_tan = 80, + OpenCLstd_Native_cos = 81, + OpenCLstd_Native_divide = 82, + OpenCLstd_Native_exp = 83, + OpenCLstd_Native_exp2 = 84, + OpenCLstd_Native_exp10 = 85, + OpenCLstd_Native_log = 86, + OpenCLstd_Native_log2 = 87, + OpenCLstd_Native_log10 = 88, + OpenCLstd_Native_powr = 89, + OpenCLstd_Native_recip = 90, + OpenCLstd_Native_rsqrt = 91, + OpenCLstd_Native_sin = 92, + OpenCLstd_Native_sqrt = 93, + OpenCLstd_Native_tan = 94, + + // Section 2.2: Integer instructions + OpenCLstd_SAbs = 141, + OpenCLstd_SAbs_diff = 142, + OpenCLstd_SAdd_sat = 143, + OpenCLstd_UAdd_sat = 144, + OpenCLstd_SHadd = 145, + OpenCLstd_UHadd = 146, + OpenCLstd_SRhadd = 147, + OpenCLstd_URhadd = 148, + OpenCLstd_SClamp = 149, + OpenCLstd_UClamp = 150, + OpenCLstd_Clz = 151, + OpenCLstd_Ctz = 152, + OpenCLstd_SMad_hi = 153, + OpenCLstd_UMad_sat = 154, + OpenCLstd_SMad_sat = 155, + OpenCLstd_SMax = 156, + OpenCLstd_UMax = 157, + OpenCLstd_SMin = 158, + OpenCLstd_UMin = 159, + OpenCLstd_SMul_hi = 160, + OpenCLstd_Rotate = 161, + OpenCLstd_SSub_sat = 162, + OpenCLstd_USub_sat = 163, + OpenCLstd_U_Upsample = 164, + OpenCLstd_S_Upsample = 165, + OpenCLstd_Popcount = 166, + OpenCLstd_SMad24 = 167, + OpenCLstd_UMad24 = 168, + OpenCLstd_SMul24 = 169, + OpenCLstd_UMul24 = 170, + OpenCLstd_UAbs = 201, + OpenCLstd_UAbs_diff = 202, + OpenCLstd_UMul_hi = 203, + OpenCLstd_UMad_hi = 204, + + // Section 2.3: Common instructions + OpenCLstd_FClamp = 95, + OpenCLstd_Degrees = 96, + OpenCLstd_FMax_common = 97, + OpenCLstd_FMin_common = 98, + OpenCLstd_Mix = 99, + OpenCLstd_Radians = 100, + OpenCLstd_Step = 101, + OpenCLstd_Smoothstep = 102, + OpenCLstd_Sign = 103, + + // Section 2.4: Geometric instructions + OpenCLstd_Cross = 104, + OpenCLstd_Distance = 105, + OpenCLstd_Length = 106, + OpenCLstd_Normalize = 107, + OpenCLstd_Fast_distance = 108, + OpenCLstd_Fast_length = 109, + OpenCLstd_Fast_normalize = 110, + + // Section 2.5: Relational instructions + OpenCLstd_Bitselect = 186, + OpenCLstd_Select = 187, + + // Section 2.6: Vector Data Load and Store instructions + OpenCLstd_Vloadn = 171, + OpenCLstd_Vstoren = 172, + OpenCLstd_Vload_half = 173, + OpenCLstd_Vload_halfn = 174, + OpenCLstd_Vstore_half = 175, + OpenCLstd_Vstore_half_r = 176, + OpenCLstd_Vstore_halfn = 177, + OpenCLstd_Vstore_halfn_r = 178, + OpenCLstd_Vloada_halfn = 179, + OpenCLstd_Vstorea_halfn = 180, + OpenCLstd_Vstorea_halfn_r = 181, + + // Section 2.7: Miscellaneous Vector instructions + OpenCLstd_Shuffle = 182, + OpenCLstd_Shuffle2 = 183, + + // Section 2.8: Misc instructions + OpenCLstd_Printf = 184, + OpenCLstd_Prefetch = 185, +}; + +#endif + +#endif // #ifndef OPENCLstd_H diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCLDebugInfo100.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCLDebugInfo100.h new file mode 100644 index 0000000..1149980 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/OpenCLDebugInfo100.h @@ -0,0 +1,156 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#ifndef SPIRV_UNIFIED1_OpenCLDebugInfo100_H_ +#define SPIRV_UNIFIED1_OpenCLDebugInfo100_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + OpenCLDebugInfo100Version = 200, + OpenCLDebugInfo100Version_BitWidthPadding = 0x7fffffff +}; +enum { + OpenCLDebugInfo100Revision = 2, + OpenCLDebugInfo100Revision_BitWidthPadding = 0x7fffffff +}; + +enum OpenCLDebugInfo100Instructions { + OpenCLDebugInfo100DebugInfoNone = 0, + OpenCLDebugInfo100DebugCompilationUnit = 1, + OpenCLDebugInfo100DebugTypeBasic = 2, + OpenCLDebugInfo100DebugTypePointer = 3, + OpenCLDebugInfo100DebugTypeQualifier = 4, + OpenCLDebugInfo100DebugTypeArray = 5, + OpenCLDebugInfo100DebugTypeVector = 6, + OpenCLDebugInfo100DebugTypedef = 7, + OpenCLDebugInfo100DebugTypeFunction = 8, + OpenCLDebugInfo100DebugTypeEnum = 9, + OpenCLDebugInfo100DebugTypeComposite = 10, + OpenCLDebugInfo100DebugTypeMember = 11, + OpenCLDebugInfo100DebugTypeInheritance = 12, + OpenCLDebugInfo100DebugTypePtrToMember = 13, + OpenCLDebugInfo100DebugTypeTemplate = 14, + OpenCLDebugInfo100DebugTypeTemplateParameter = 15, + OpenCLDebugInfo100DebugTypeTemplateTemplateParameter = 16, + OpenCLDebugInfo100DebugTypeTemplateParameterPack = 17, + OpenCLDebugInfo100DebugGlobalVariable = 18, + OpenCLDebugInfo100DebugFunctionDeclaration = 19, + OpenCLDebugInfo100DebugFunction = 20, + OpenCLDebugInfo100DebugLexicalBlock = 21, + OpenCLDebugInfo100DebugLexicalBlockDiscriminator = 22, + OpenCLDebugInfo100DebugScope = 23, + OpenCLDebugInfo100DebugNoScope = 24, + OpenCLDebugInfo100DebugInlinedAt = 25, + OpenCLDebugInfo100DebugLocalVariable = 26, + OpenCLDebugInfo100DebugInlinedVariable = 27, + OpenCLDebugInfo100DebugDeclare = 28, + OpenCLDebugInfo100DebugValue = 29, + OpenCLDebugInfo100DebugOperation = 30, + OpenCLDebugInfo100DebugExpression = 31, + OpenCLDebugInfo100DebugMacroDef = 32, + OpenCLDebugInfo100DebugMacroUndef = 33, + OpenCLDebugInfo100DebugImportedEntity = 34, + OpenCLDebugInfo100DebugSource = 35, + OpenCLDebugInfo100InstructionsMax = 0x7fffffff +}; + + +enum OpenCLDebugInfo100DebugInfoFlags { + OpenCLDebugInfo100FlagIsProtected = 0x01, + OpenCLDebugInfo100FlagIsPrivate = 0x02, + OpenCLDebugInfo100FlagIsPublic = 0x03, + OpenCLDebugInfo100FlagIsLocal = 0x04, + OpenCLDebugInfo100FlagIsDefinition = 0x08, + OpenCLDebugInfo100FlagFwdDecl = 0x10, + OpenCLDebugInfo100FlagArtificial = 0x20, + OpenCLDebugInfo100FlagExplicit = 0x40, + OpenCLDebugInfo100FlagPrototyped = 0x80, + OpenCLDebugInfo100FlagObjectPointer = 0x100, + OpenCLDebugInfo100FlagStaticMember = 0x200, + OpenCLDebugInfo100FlagIndirectVariable = 0x400, + OpenCLDebugInfo100FlagLValueReference = 0x800, + OpenCLDebugInfo100FlagRValueReference = 0x1000, + OpenCLDebugInfo100FlagIsOptimized = 0x2000, + OpenCLDebugInfo100FlagIsEnumClass = 0x4000, + OpenCLDebugInfo100FlagTypePassByValue = 0x8000, + OpenCLDebugInfo100FlagTypePassByReference = 0x10000, + OpenCLDebugInfo100DebugInfoFlagsMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugBaseTypeAttributeEncoding { + OpenCLDebugInfo100Unspecified = 0, + OpenCLDebugInfo100Address = 1, + OpenCLDebugInfo100Boolean = 2, + OpenCLDebugInfo100Float = 3, + OpenCLDebugInfo100Signed = 4, + OpenCLDebugInfo100SignedChar = 5, + OpenCLDebugInfo100Unsigned = 6, + OpenCLDebugInfo100UnsignedChar = 7, + OpenCLDebugInfo100DebugBaseTypeAttributeEncodingMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugCompositeType { + OpenCLDebugInfo100Class = 0, + OpenCLDebugInfo100Structure = 1, + OpenCLDebugInfo100Union = 2, + OpenCLDebugInfo100DebugCompositeTypeMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugTypeQualifier { + OpenCLDebugInfo100ConstType = 0, + OpenCLDebugInfo100VolatileType = 1, + OpenCLDebugInfo100RestrictType = 2, + OpenCLDebugInfo100AtomicType = 3, + OpenCLDebugInfo100DebugTypeQualifierMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugOperation { + OpenCLDebugInfo100Deref = 0, + OpenCLDebugInfo100Plus = 1, + OpenCLDebugInfo100Minus = 2, + OpenCLDebugInfo100PlusUconst = 3, + OpenCLDebugInfo100BitPiece = 4, + OpenCLDebugInfo100Swap = 5, + OpenCLDebugInfo100Xderef = 6, + OpenCLDebugInfo100StackValue = 7, + OpenCLDebugInfo100Constu = 8, + OpenCLDebugInfo100Fragment = 9, + OpenCLDebugInfo100DebugOperationMax = 0x7fffffff +}; + +enum OpenCLDebugInfo100DebugImportedEntity { + OpenCLDebugInfo100ImportedModule = 0, + OpenCLDebugInfo100ImportedDeclaration = 1, + OpenCLDebugInfo100DebugImportedEntityMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_OpenCLDebugInfo100_H_ diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.debuginfo.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.debuginfo.grammar.json new file mode 100644 index 0000000..9212f6f --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.debuginfo.grammar.json @@ -0,0 +1,568 @@ +{ + "copyright" : [ + "Copyright (c) 2017 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 1, + "instructions" : [ + { + "opname" : "DebugInfoNone", + "opcode" : 0 + }, + { + "opname" : "DebugCompilationUnit", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "LiteralInteger", "name" : "'DWARF Version'" } + ] + }, + { + "opname" : "DebugTypeBasic", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" } + ] + }, + { + "opname" : "DebugTypePointer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "StorageClass", "name" : "'Storage Class'" }, + { "kind" : "DebugInfoFlags", "name" : "'Literal Flags'" } + ] + }, + { + "opname" : "DebugTypeQualifier", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" } + ] + }, + { + "opname" : "DebugTypeArray", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeVector", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "DebugTypedef", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeFunction", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "name" : "'Paramter Types'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeEnum", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Underlying Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeComposite", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "DebugCompositeType", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeMember", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugTypeInheritance", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'Child'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePtrToMember", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'Member Type'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeTemplate", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeTemplateParameter", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Actual Type'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateTemplateParameter", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Template Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateParameterPack", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugGlobalVariable", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDeclaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugFunction", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "LiteralInteger", "name" : "'Scope Line'" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlock", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlockDiscriminator", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "LiteralInteger", "name" : "'Discriminator'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugScope", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugNoScope", + "opcode" : 24 + }, + { + "opname" : "DebugInlinedAt", + "opcode" : 25, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLocalVariable", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugInlinedVariable", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Inlined'" } + ] + }, + { + "opname" : "DebugDeclare", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Expression'" } + ] + }, + { + "opname" : "DebugValue", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Expression'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugOperation", + "opcode" : 30, + "operands" : [ + { "kind" : "DebugOperation", "name" : "'OpCode'" }, + { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugExpression", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugMacroDef", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugMacroUndef", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Macro'" } + ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "DebugInfoFlags", + "enumerants" : [ + { + "enumerant" : "FlagIsProtected", + "value" : "0x01" + }, + { + "enumerant" : "FlagIsPrivate", + "value" : "0x02" + }, + { + "enumerant" : "FlagIsPublic", + "value" : "0x03" + }, + { + "enumerant" : "FlagIsLocal", + "value" : "0x04" + }, + { + "enumerant" : "FlagIsDefinition", + "value" : "0x08" + }, + { + "enumerant" : "FlagFwdDecl", + "value" : "0x10" + }, + { + "enumerant" : "FlagArtificial", + "value" : "0x20" + }, + { + "enumerant" : "FlagExplicit", + "value" : "0x40" + }, + { + "enumerant" : "FlagPrototyped", + "value" : "0x80" + }, + { + "enumerant" : "FlagObjectPointer", + "value" : "0x100" + }, + { + "enumerant" : "FlagStaticMember", + "value" : "0x200" + }, + { + "enumerant" : "FlagIndirectVariable", + "value" : "0x400" + }, + { + "enumerant" : "FlagLValueReference", + "value" : "0x800" + }, + { + "enumerant" : "FlagRValueReference", + "value" : "0x1000" + }, + { + "enumerant" : "FlagIsOptimized", + "value" : "0x2000" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugBaseTypeAttributeEncoding", + "enumerants" : [ + { + "enumerant" : "Unspecified", + "value" : "0" + }, + { + "enumerant" : "Address", + "value" : "1" + }, + { + "enumerant" : "Boolean", + "value" : "2" + }, + { + "enumerant" : "Float", + "value" : "4" + }, + { + "enumerant" : "Signed", + "value" : "5" + }, + { + "enumerant" : "SignedChar", + "value" : "6" + }, + { + "enumerant" : "Unsigned", + "value" : "7" + }, + { + "enumerant" : "UnsignedChar", + "value" : "8" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugCompositeType", + "enumerants" : [ + { + "enumerant" : "Class", + "value" : "0" + }, + { + "enumerant" : "Structure", + "value" : "1" + }, + { + "enumerant" : "Union", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugTypeQualifier", + "enumerants" : [ + { + "enumerant" : "ConstType", + "value" : "0" + }, + { + "enumerant" : "VolatileType", + "value" : "1" + }, + { + "enumerant" : "RestrictType", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugOperation", + "enumerants" : [ + { + "enumerant" : "Deref", + "value" : "0" + }, + { + "enumerant" : "Plus", + "value" : "1" + }, + { + "enumerant" : "Minus", + "value" : "2" + }, + { + "enumerant" : "PlusUconst", + "value" : "3", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "BitPiece", + "value" : "4", + "parameters" : [ + { "kind" : "LiteralInteger" }, + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Swap", + "value" : "5" + }, + { + "enumerant" : "Xderef", + "value" : "6" + }, + { + "enumerant" : "StackValue", + "value" : "7" + }, + { + "enumerant" : "Constu", + "value" : "8", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json new file mode 100644 index 0000000..3d9f39e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.glsl.std.450.grammar.json @@ -0,0 +1,642 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "Round", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "RoundEven", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Trunc", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FAbs", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SAbs", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FSign", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SSign", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Floor", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ceil", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fract", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Radians", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "Degrees", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "Sin", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tan", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asin", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acos", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'y_over_x'" } + ] + }, + { + "opname" : "Sinh", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Cosh", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Tanh", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Asinh", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Acosh", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atanh", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Atan2", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Pow", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Exp", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Exp2", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Log2", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Sqrt", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "InverseSqrt", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Determinant", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "MatrixInverse", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Modf", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "ModfStruct", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FMin", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMin", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMin", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FMax", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "UMax", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "SMax", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "FClamp", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "UClamp", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "SClamp", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + }, + { + "opname" : "FMix", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "IMix", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "Step", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "SmoothStep", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Fma", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "Frexp", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "FrexpStruct", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Ldexp", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "PackSnorm4x8", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm4x8", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackSnorm2x16", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackUnorm2x16", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackHalf2x16", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "PackDouble2x32", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "UnpackSnorm2x16", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm2x16", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackHalf2x16", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ] + }, + { + "opname" : "UnpackSnorm4x8", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackUnorm4x8", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "UnpackDouble2x32", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" } + ], + "capabilities" : [ "Float64" ] + }, + { + "opname" : "Length", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "Distance", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "Cross", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "Normalize", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "FaceForward", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'Nref'" } + ] + }, + { + "opname" : "Reflect", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" } + ] + }, + { + "opname" : "Refract", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'I'" }, + { "kind" : "IdRef", "name" : "'N'" }, + { "kind" : "IdRef", "name" : "'eta'" } + ] + }, + { + "opname" : "FindILsb", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindSMsb", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "FindUMsb", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "InterpolateAtCentroid", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtSample", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'sample'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "InterpolateAtOffset", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "capabilities" : [ "InterpolationFunction" ] + }, + { + "opname" : "NMin", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NMax", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "NClamp", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minVal'" }, + { "kind" : "IdRef", "name" : "'maxVal'" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json new file mode 100644 index 0000000..15e5699 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.clspvreflection.grammar.json @@ -0,0 +1,237 @@ +{ + "revision" : 1, + "instructions" : [ + { + "opname" : "Kernel", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "Name" } + ] + }, + { + "opname" : "ArgumentInfo", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "Name" }, + { "kind" : "IdRef", "name" : "Type Name", "quantifier" : "?" }, + { "kind" : "IdRef", "name" : "Address Qualifier", "quantifier" : "?" }, + { "kind" : "IdRef", "name" : "Access Qualifier", "quantifier" : "?" }, + { "kind" : "IdRef", "name" : "Type Qualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentStorageBuffer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentUniform", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentPodStorageBuffer", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentPodUniform", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentPodPushConstant", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentSampledImage", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentStorageImage", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentSampler", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "ArgumentWorkgroup", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "Decl" }, + { "kind" : "IdRef", "name" : "Ordinal" }, + { "kind" : "IdRef", "name" : "SpecId" }, + { "kind" : "IdRef", "name" : "ElemSize" }, + { "kind" : "IdRef", "name" : "ArgInfo", "quantifier" : "?" } + ] + }, + { + "opname" : "SpecConstantWorkgroupSize", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "X" }, + { "kind" : "IdRef", "name" : "Y" }, + { "kind" : "IdRef", "name" : "Z" } + ] + }, + { + "opname" : "SpecConstantGlobalOffset", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "X" }, + { "kind" : "IdRef", "name" : "Y" }, + { "kind" : "IdRef", "name" : "Z" } + ] + }, + { + "opname" : "SpecConstantWorkDim", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "Dim" } + ] + }, + { + "opname" : "PushConstantGlobalOffset", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantEnqueuedLocalSize", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantGlobalSize", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantRegionOffset", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantNumWorkgroups", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "PushConstantRegionGroupOffset", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "Offset" }, + { "kind" : "IdRef", "name" : "Size" } + ] + }, + { + "opname" : "ConstantDataStorageBuffer", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Data" } + ] + }, + { + "opname" : "ConstantDataUniform", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Data" } + ] + }, + { + "opname" : "LiteralSampler", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "DescriptorSet" }, + { "kind" : "IdRef", "name" : "Binding" }, + { "kind" : "IdRef", "name" : "Mask" } + ] + }, + { + "opname" : "PropertyRequiredWorkgroupSize", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "Kernel" }, + { "kind" : "IdRef", "name" : "X" }, + { "kind" : "IdRef", "name" : "Y" }, + { "kind" : "IdRef", "name" : "Z" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugprintf.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugprintf.grammar.json new file mode 100644 index 0000000..71fa711 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.nonsemantic.debugprintf.grammar.json @@ -0,0 +1,13 @@ +{ + "revision" : 1, + "instructions" : [ + { + "opname" : "DebugPrintf", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'Format'" }, + { "kind" : "IdRef", "quantifier" : "*" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json new file mode 100644 index 0000000..08062be --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json @@ -0,0 +1,632 @@ +{ + "copyright" : [ + "Copyright (c) 2018 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 200, + "revision" : 2, + "instructions" : [ + { + "opname" : "DebugInfoNone", + "opcode" : 0 + }, + { + "opname" : "DebugCompilationUnit", + "opcode" : 1, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "LiteralInteger", "name" : "'DWARF Version'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "SourceLanguage", "name" : "'Language'" } + ] + }, + { + "opname" : "DebugTypeBasic", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugBaseTypeAttributeEncoding", "name" : "'Encoding'" } + ] + }, + { + "opname" : "DebugTypePointer", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "StorageClass", "name" : "'Storage Class'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypeQualifier", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "DebugTypeQualifier", "name" : "'Type Qualifier'" } + ] + }, + { + "opname" : "DebugTypeArray", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Component Counts'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeVector", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "DebugTypedef", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Base Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeFunction", + "opcode" : 8, + "operands" : [ + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "name" : "'Parameter Types'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeEnum", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Underlying Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "PairIdRefIdRef", "name" : "'Value, Name, Value, Name, ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeComposite", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "DebugCompositeType", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Members'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeMember", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugTypeInheritance", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'Child'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugTypePtrToMember", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'Member Type'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugTypeTemplate", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugTypeTemplateParameter", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Actual Type'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateTemplateParameter", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Template Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "DebugTypeTemplateParameterPack", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Template Parameters'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugGlobalVariable", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'Static Member Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugFunctionDeclaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" } + ] + }, + { + "opname" : "DebugFunction", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Linkage Name'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "LiteralInteger", "name" : "'Scope Line'" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "name" : "'Declaration'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlock", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "IdRef", "name" : "'Name'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLexicalBlockDiscriminator", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Discriminator'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugScope", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined At'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugNoScope", + "opcode" : 24 + }, + { + "opname" : "DebugInlinedAt", + "opcode" : 25, + "operands" : [ + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Scope'" }, + { "kind" : "IdRef", "name" : "'Inlined'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugLocalVariable", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" }, + { "kind" : "DebugInfoFlags", "name" : "'Flags'" }, + { "kind" : "LiteralInteger", "name" : "'Arg Number'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugInlinedVariable", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Inlined'" } + ] + }, + { + "opname" : "DebugDeclare", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Variable'" }, + { "kind" : "IdRef", "name" : "'Expression'" } + ] + }, + { + "opname" : "DebugValue", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'Local Variable'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Expression'" }, + { "kind" : "IdRef", "name" : "'Indexes'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugOperation", + "opcode" : 30, + "operands" : [ + { "kind" : "DebugOperation", "name" : "'OpCode'" }, + { "kind" : "LiteralInteger", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugExpression", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'Operands ...'", "quantifier" : "*" } + ] + }, + { + "opname" : "DebugMacroDef", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "IdRef", "name" : "'Value'", "quantifier" : "?" } + ] + }, + { + "opname" : "DebugMacroUndef", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "IdRef", "name" : "'Macro'" } + ] + }, + { + "opname" : "DebugImportedEntity", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'Name'" }, + { "kind" : "DebugImportedEntity", "name" : "'Tag'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Entity'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" }, + { "kind" : "IdRef", "name" : "'Parent'" } + ] + }, + { + "opname" : "DebugSource", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "IdRef", "name" : "'Text'", "quantifier" : "?" } + ] + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "DebugInfoFlags", + "enumerants" : [ + { + "enumerant" : "FlagIsProtected", + "value" : "0x01" + }, + { + "enumerant" : "FlagIsPrivate", + "value" : "0x02" + }, + { + "enumerant" : "FlagIsPublic", + "value" : "0x03" + }, + { + "enumerant" : "FlagIsLocal", + "value" : "0x04" + }, + { + "enumerant" : "FlagIsDefinition", + "value" : "0x08" + }, + { + "enumerant" : "FlagFwdDecl", + "value" : "0x10" + }, + { + "enumerant" : "FlagArtificial", + "value" : "0x20" + }, + { + "enumerant" : "FlagExplicit", + "value" : "0x40" + }, + { + "enumerant" : "FlagPrototyped", + "value" : "0x80" + }, + { + "enumerant" : "FlagObjectPointer", + "value" : "0x100" + }, + { + "enumerant" : "FlagStaticMember", + "value" : "0x200" + }, + { + "enumerant" : "FlagIndirectVariable", + "value" : "0x400" + }, + { + "enumerant" : "FlagLValueReference", + "value" : "0x800" + }, + { + "enumerant" : "FlagRValueReference", + "value" : "0x1000" + }, + { + "enumerant" : "FlagIsOptimized", + "value" : "0x2000" + }, + { + "enumerant" : "FlagIsEnumClass", + "value" : "0x4000" + }, + { + "enumerant" : "FlagTypePassByValue", + "value" : "0x8000" + }, + { + "enumerant" : "FlagTypePassByReference", + "value" : "0x10000" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugBaseTypeAttributeEncoding", + "enumerants" : [ + { + "enumerant" : "Unspecified", + "value" : "0" + }, + { + "enumerant" : "Address", + "value" : "1" + }, + { + "enumerant" : "Boolean", + "value" : "2" + }, + { + "enumerant" : "Float", + "value" : "3" + }, + { + "enumerant" : "Signed", + "value" : "4" + }, + { + "enumerant" : "SignedChar", + "value" : "5" + }, + { + "enumerant" : "Unsigned", + "value" : "6" + }, + { + "enumerant" : "UnsignedChar", + "value" : "7" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugCompositeType", + "enumerants" : [ + { + "enumerant" : "Class", + "value" : "0" + }, + { + "enumerant" : "Structure", + "value" : "1" + }, + { + "enumerant" : "Union", + "value" : "2" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugTypeQualifier", + "enumerants" : [ + { + "enumerant" : "ConstType", + "value" : "0" + }, + { + "enumerant" : "VolatileType", + "value" : "1" + }, + { + "enumerant" : "RestrictType", + "value" : "2" + }, + { + "enumerant" : "AtomicType", + "value" : "3" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugOperation", + "enumerants" : [ + { + "enumerant" : "Deref", + "value" : "0" + }, + { + "enumerant" : "Plus", + "value" : "1" + }, + { + "enumerant" : "Minus", + "value" : "2" + }, + { + "enumerant" : "PlusUconst", + "value" : "3", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "BitPiece", + "value" : "4", + "parameters" : [ + { "kind" : "LiteralInteger" }, + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Swap", + "value" : "5" + }, + { + "enumerant" : "Xderef", + "value" : "6" + }, + { + "enumerant" : "StackValue", + "value" : "7" + }, + { + "enumerant" : "Constu", + "value" : "8", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Fragment", + "value" : "9", + "parameters" : [ + { "kind" : "LiteralInteger" }, + { "kind" : "LiteralInteger" } + ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "DebugImportedEntity", + "enumerants" : [ + { + "enumerant" : "ImportedModule", + "value" : "0" + }, + { + "enumerant" : "ImportedDeclaration", + "value" : "1" + } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.std.100.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.std.100.grammar.json new file mode 100644 index 0000000..4fe4506 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.opencl.std.100.grammar.json @@ -0,0 +1,1279 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2016 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "version" : 100, + "revision" : 2, + "instructions" : [ + { + "opname" : "acos", + "opcode" : 0, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acosh", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "acospi", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asin", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinh", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "asinpi", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanh", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atanpi", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "atan2pi", + "opcode" : 10, + "operands" : [ + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cbrt", + "opcode" : 11, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ceil", + "opcode" : 12, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "copysign", + "opcode" : 13, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "cos", + "opcode" : 14, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cosh", + "opcode" : 15, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cospi", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erfc", + "opcode" : 17, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "erf", + "opcode" : 18, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp", + "opcode" : 19, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp2", + "opcode" : 20, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "exp10", + "opcode" : 21, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "expm1", + "opcode" : 22, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fabs", + "opcode" : 23, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fdim", + "opcode" : 24, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "floor", + "opcode" : 25, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "fma", + "opcode" : 26, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fmax", + "opcode" : 27, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin", + "opcode" : 28, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmod", + "opcode" : 29, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fract", + "opcode" : 30, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'ptr'" } + ] + }, + { + "opname" : "frexp", + "opcode" : 31, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'exp'" } + ] + }, + { + "opname" : "hypot", + "opcode" : 32, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "ilogb", + "opcode" : 33, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ldexp", + "opcode" : 34, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'k'" } + ] + }, + { + "opname" : "lgamma", + "opcode" : 35, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "lgamma_r", + "opcode" : 36, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'signp'" } + ] + }, + { + "opname" : "log", + "opcode" : 37, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log2", + "opcode" : 38, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log10", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "log1p", + "opcode" : 40, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "logb", + "opcode" : 41, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "mad", + "opcode" : 42, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "maxmag", + "opcode" : 43, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "minmag", + "opcode" : 44, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "modf", + "opcode" : 45, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'iptr'" } + ] + }, + { + "opname" : "nan", + "opcode" : 46, + "operands" : [ + { "kind" : "IdRef", "name" : "'nancode'" } + ] + }, + { + "opname" : "nextafter", + "opcode" : 47, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "pow", + "opcode" : 48, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y" } + ] + }, + { + "opname" : "pown", + "opcode" : 49, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "powr", + "opcode" : 50, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remainder", + "opcode" : 51, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "remquo", + "opcode" : 52, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'quo'" } + ] + }, + { + "opname" : "rint", + "opcode" : 53, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rootn", + "opcode" : 54, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "round", + "opcode" : 55, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "rsqrt", + "opcode" : 56, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sin", + "opcode" : 57, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sincos", + "opcode" : 58, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'cosval'" } + ] + }, + { + "opname" : "sinh", + "opcode" : 59, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sinpi", + "opcode" : 60, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sqrt", + "opcode" : 61, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tan", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanh", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tanpi", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "tgamma", + "opcode" : 65, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "trunc", + "opcode" : 66, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_cos", + "opcode" : 67, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_divide", + "opcode" : 68, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_exp", + "opcode" : 69, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp2", + "opcode" : 70, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_exp10", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log2", + "opcode" : 73, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_log10", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_powr", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "half_recip", + "opcode" : 76, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_rsqrt", + "opcode" : 77, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sin", + "opcode" : 78, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_sqrt", + "opcode" : 79, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "half_tan", + "opcode" : 80, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_cos", + "opcode" : 81, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_divide", + "opcode" : 82, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_exp", + "opcode" : 83, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp2", + "opcode" : 84, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_exp10", + "opcode" : 85, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log", + "opcode" : 86, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log2", + "opcode" : 87, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_log10", + "opcode" : 88, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_powr", + "opcode" : 89, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "native_recip", + "opcode" : 90, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_rsqrt", + "opcode" : 91, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sin", + "opcode" : 92, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_sqrt", + "opcode" : 93, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "native_tan", + "opcode" : 94, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs", + "opcode" : 141, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_abs_diff", + "opcode" : 142, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_add_sat", + "opcode" : 143, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_add_sat", + "opcode" : 144, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_hadd", + "opcode" : 145, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_hadd", + "opcode" : 146, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_rhadd", + "opcode" : 147, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_rhadd", + "opcode" : 148, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_clamp", + "opcode" : 149, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "u_clamp", + "opcode" : 150, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "clz", + "opcode" : 151, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "ctz", + "opcode" : 152, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad_hi", + "opcode" : 153, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "u_mad_sat", + "opcode" : 154, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mad_sat", + "opcode" : 155, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_max", + "opcode" : 156, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_max", + "opcode" : 157, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_min", + "opcode" : 158, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_min", + "opcode" : 159, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "s_mul_hi", + "opcode" : 160, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "rotate", + "opcode" : 161, + "operands" : [ + { "kind" : "IdRef", "name" : "'v'" }, + { "kind" : "IdRef", "name" : "'i'" } + ] + }, + { + "opname" : "s_sub_sat", + "opcode" : 162, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_sub_sat", + "opcode" : 163, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_upsample", + "opcode" : 164, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "s_upsample", + "opcode" : 165, + "operands" : [ + { "kind" : "IdRef", "name" : "'hi'" }, + { "kind" : "IdRef", "name" : "'lo'" } + ] + }, + { + "opname" : "popcount", + "opcode" : 166, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "s_mad24", + "opcode" : 167, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "u_mad24", + "opcode" : 168, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ] + }, + { + "opname" : "s_mul24", + "opcode" : 169, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul24", + "opcode" : 170, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_abs", + "opcode" : 201, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "u_abs_diff", + "opcode" : 202, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mul_hi", + "opcode" : 203, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "u_mad_hi", + "opcode" : 204, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "fclamp", + "opcode" : 95, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'minval'" }, + { "kind" : "IdRef", "name" : "'maxval'" } + ] + }, + { + "opname" : "degrees", + "opcode" :96, + "operands" : [ + { "kind" : "IdRef", "name" : "'radians'" } + ] + }, + { + "opname" : "fmax_common", + "opcode" : 97, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "fmin_common", + "opcode" : 98, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ] + }, + { + "opname" : "mix", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'a'" } + ] + }, + { + "opname" : "radians", + "opcode" : 100, + "operands" : [ + { "kind" : "IdRef", "name" : "'degrees'" } + ] + }, + { + "opname" : "step", + "opcode" : 101, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "smoothstep", + "opcode" : 102, + "operands" : [ + { "kind" : "IdRef", "name" : "'edge0'" }, + { "kind" : "IdRef", "name" : "'edge1'" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "sign", + "opcode" : 103, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "cross", + "opcode" : 104, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "distance", + "opcode" : 105, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "length", + "opcode" : 106, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "normalize", + "opcode" : 107, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_distance", + "opcode" : 108, + "operands" : [ + { "kind" : "IdRef", "name" : "'p0'" }, + { "kind" : "IdRef", "name" : "'p1'" } + ] + }, + { + "opname" : "fast_length", + "opcode" : 109, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "fast_normalize", + "opcode" : 110, + "operands" : [ + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "bitselect", + "opcode" : 186, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "select", + "opcode" : 187, + "operands" : [ + { "kind" : "IdRef", "name" : "'a'" }, + { "kind" : "IdRef", "name" : "'b'" }, + { "kind" : "IdRef", "name" : "'c'" } + ] + }, + { + "opname" : "vloadn", + "opcode" : 171, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstoren", + "opcode" : 172, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_half", + "opcode" : 173, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vload_halfn", + "opcode" : 174, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstore_half", + "opcode" : 175, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_half_r", + "opcode" : 176, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vstore_halfn", + "opcode" : 177, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstore_halfn_r", + "opcode" : 178, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "vloada_halfn", + "opcode" : 179, + "operands" : [ + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "LiteralInteger", "name" : "'n'" } + ] + }, + { + "opname" : "vstorea_halfn", + "opcode" : 180, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" } + ] + }, + { + "opname" : "vstorea_halfn_r", + "opcode" : 181, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" }, + { "kind" : "IdRef", "name" : "'p'" }, + { "kind" : "FPRoundingMode", "name" : "'mode'" } + ] + }, + { + "opname" : "shuffle", + "opcode" : 182, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "shuffle2", + "opcode" : 183, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'shuffle mask'" } + ] + }, + { + "opname" : "printf", + "opcode" : 184, + "operands" : [ + { "kind" : "IdRef", "name" : "'format'" }, + { "kind" : "IdRef", "name" : "'additional arguments'", "quantifier" : "*" } + ] + }, + { + "opname" : "prefetch", + "opcode" : 185, + "operands" : [ + { "kind" : "IdRef", "name" : "'ptr'" }, + { "kind" : "IdRef", "name" : "'num elements'" } + ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json new file mode 100644 index 0000000..e18251b --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-gcn-shader.grammar.json @@ -0,0 +1,26 @@ +{ + "revision" : 2, + "instructions" : [ + { + "opname" : "CubeFaceIndexAMD", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'P'" } + ], + "extensions" : [ "SPV_AMD_gcn_shader" ] + }, + { + "opname" : "CubeFaceCoordAMD", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'P'" } + ], + "extensions" : [ "SPV_AMD_gcn_shader" ] + }, + { + "opname" : "TimeAMD", + "opcode" : 3, + "extensions" : [ "SPV_AMD_gcn_shader" ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-ballot.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-ballot.grammar.json new file mode 100644 index 0000000..62a470e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-ballot.grammar.json @@ -0,0 +1,41 @@ +{ + "revision" : 5, + "instructions" : [ + { + "opname" : "SwizzleInvocationsAMD", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'offset'" } + ], + "extensions" : [ "SPV_AMD_shader_ballot" ] + }, + { + "opname" : "SwizzleInvocationsMaskedAMD", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'data'" }, + { "kind" : "IdRef", "name" : "'mask'" } + ], + "extensions" : [ "SPV_AMD_shader_ballot" ] + }, + { + "opname" : "WriteInvocationAMD", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'inputValue'" }, + { "kind" : "IdRef", "name" : "'writeValue'" }, + { "kind" : "IdRef", "name" : "'invocationIndex'" } + ], + "extensions" : [ "SPV_AMD_shader_ballot" ] + }, + { + "opname" : "MbcntAMD", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'mask'" } + ], + "extensions" : [ "SPV_AMD_shader_ballot" ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json new file mode 100644 index 0000000..e156b1b --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json @@ -0,0 +1,14 @@ +{ + "revision" : 4, + "instructions" : [ + { + "opname" : "InterpolateAtVertexAMD", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'interpolant'" }, + { "kind" : "IdRef", "name" : "'vertexIdx'" } + ], + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-trinary-minmax.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-trinary-minmax.grammar.json new file mode 100644 index 0000000..c681976 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/extinst.spv-amd-shader-trinary-minmax.grammar.json @@ -0,0 +1,95 @@ +{ + "revision" : 4, + "instructions" : [ + { + "opname" : "FMin3AMD", + "opcode" : 1, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "UMin3AMD", + "opcode" : 2, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "SMin3AMD", + "opcode" : 3, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "FMax3AMD", + "opcode" : 4, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "UMax3AMD", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "SMax3AMD", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "FMid3AMD", + "opcode" : 7, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "UMid3AMD", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + }, + { + "opname" : "SMid3AMD", + "opcode" : 9, + "operands" : [ + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" }, + { "kind" : "IdRef", "name" : "'z'" } + ], + "extensions" : [ "SPV_AMD_shader_trinary_minmax" ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json new file mode 100644 index 0000000..18901cd --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.core.grammar.json @@ -0,0 +1,11779 @@ +{ + "copyright" : [ + "Copyright (c) 2014-2020 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + "magic_number" : "0x07230203", + "major_version" : 1, + "minor_version" : 5, + "revision" : 4, + "instruction_printing_class" : [ + { + "tag" : "@exclude" + }, + { + "tag" : "Miscellaneous", + "heading" : "Miscellaneous Instructions" + }, + { + "tag" : "Debug", + "heading" : "Debug Instructions" + }, + { + "tag" : "Annotation", + "heading" : "Annotation Instructions" + }, + { + "tag" : "Extension", + "heading" : "Extension Instructions" + }, + { + "tag" : "Mode-Setting", + "heading" : "Mode-Setting Instructions" + }, + { + "tag" : "Type-Declaration", + "heading" : "Type-Declaration Instructions" + }, + { + "tag" : "Constant-Creation", + "heading" : "Constant-Creation Instructions" + }, + { + "tag" : "Memory", + "heading" : "Memory Instructions" + }, + { + "tag" : "Function", + "heading" : "Function Instructions" + }, + { + "tag" : "Image", + "heading" : "Image Instructions" + }, + { + "tag" : "Conversion", + "heading" : "Conversion Instructions" + }, + { + "tag" : "Composite", + "heading" : "Composite Instructions" + }, + { + "tag" : "Arithmetic", + "heading" : "Arithmetic Instructions" + }, + { + "tag" : "Bit", + "heading" : "Bit Instructions" + }, + { + "tag" : "Relational_and_Logical", + "heading" : "Relational and Logical Instructions" + }, + { + "tag" : "Derivative", + "heading" : "Derivative Instructions" + }, + { + "tag" : "Control-Flow", + "heading" : "Control-Flow Instructions" + }, + { + "tag" : "Atomic", + "heading" : "Atomic Instructions" + }, + { + "tag" : "Primitive", + "heading" : "Primitive Instructions" + }, + { + "tag" : "Barrier", + "heading" : "Barrier Instructions" + }, + { + "tag" : "Group", + "heading" : "Group and Subgroup Instructions" + }, + { + "tag" : "Device-Side_Enqueue", + "heading" : "Device-Side Enqueue Instructions" + }, + { + "tag" : "Pipe", + "heading" : "Pipe Instructions" + }, + { + "tag" : "Non-Uniform", + "heading" : "Non-Uniform Instructions" + }, + { + "tag" : "Reserved", + "heading" : "Reserved Instructions" + } + ], + "instructions" : [ + { + "opname" : "OpNop", + "class" : "Miscellaneous", + "opcode" : 0 + }, + { + "opname" : "OpUndef", + "class" : "Miscellaneous", + "opcode" : 1, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSourceContinued", + "class" : "Debug", + "opcode" : 2, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Continued Source'" } + ] + }, + { + "opname" : "OpSource", + "class" : "Debug", + "opcode" : 3, + "operands" : [ + { "kind" : "SourceLanguage" }, + { "kind" : "LiteralInteger", "name" : "'Version'" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'File'" }, + { "kind" : "LiteralString", "quantifier" : "?", "name" : "'Source'" } + ] + }, + { + "opname" : "OpSourceExtension", + "class" : "Debug", + "opcode" : 4, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Extension'" } + ] + }, + { + "opname" : "OpName", + "class" : "Debug", + "opcode" : 5, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpMemberName", + "class" : "Debug", + "opcode" : 6, + "operands" : [ + { "kind" : "IdRef", "name" : "'Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpString", + "class" : "Debug", + "opcode" : 7, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'String'" } + ] + }, + { + "opname" : "OpLine", + "class" : "Debug", + "opcode" : 8, + "operands" : [ + { "kind" : "IdRef", "name" : "'File'" }, + { "kind" : "LiteralInteger", "name" : "'Line'" }, + { "kind" : "LiteralInteger", "name" : "'Column'" } + ] + }, + { + "opname" : "OpExtension", + "class" : "Extension", + "opcode" : 10, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInstImport", + "class" : "Extension", + "opcode" : 11, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "'Name'" } + ] + }, + { + "opname" : "OpExtInst", + "class" : "Extension", + "opcode" : 12, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Set'" }, + { "kind" : "LiteralExtInstInteger", "name" : "'Instruction'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1', +\n'Operand 2', +\n..." } + ] + }, + { + "opname" : "OpMemoryModel", + "class" : "Mode-Setting", + "opcode" : 14, + "operands" : [ + { "kind" : "AddressingModel" }, + { "kind" : "MemoryModel" } + ] + }, + { + "opname" : "OpEntryPoint", + "class" : "Mode-Setting", + "opcode" : 15, + "operands" : [ + { "kind" : "ExecutionModel" }, + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Interface'" } + ] + }, + { + "opname" : "OpExecutionMode", + "class" : "Mode-Setting", + "opcode" : 16, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ] + }, + { + "opname" : "OpCapability", + "class" : "Mode-Setting", + "opcode" : 17, + "operands" : [ + { "kind" : "Capability", "name" : "'Capability'" } + ] + }, + { + "opname" : "OpTypeVoid", + "class" : "Type-Declaration", + "opcode" : 19, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeBool", + "class" : "Type-Declaration", + "opcode" : 20, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeInt", + "class" : "Type-Declaration", + "opcode" : 21, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" }, + { "kind" : "LiteralInteger", "name" : "'Signedness'" } + ] + }, + { + "opname" : "OpTypeFloat", + "class" : "Type-Declaration", + "opcode" : 22, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Width'" } + ] + }, + { + "opname" : "OpTypeVector", + "class" : "Type-Declaration", + "opcode" : 23, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "LiteralInteger", "name" : "'Component Count'" } + ] + }, + { + "opname" : "OpTypeMatrix", + "class" : "Type-Declaration", + "opcode" : 24, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Column Type'" }, + { "kind" : "LiteralInteger", "name" : "'Column Count'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpTypeImage", + "class" : "Type-Declaration", + "opcode" : 25, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Type'" }, + { "kind" : "Dim" }, + { "kind" : "LiteralInteger", "name" : "'Depth'" }, + { "kind" : "LiteralInteger", "name" : "'Arrayed'" }, + { "kind" : "LiteralInteger", "name" : "'MS'" }, + { "kind" : "LiteralInteger", "name" : "'Sampled'" }, + { "kind" : "ImageFormat" }, + { "kind" : "AccessQualifier", "quantifier" : "?" } + ] + }, + { + "opname" : "OpTypeSampler", + "class" : "Type-Declaration", + "opcode" : 26, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpTypeSampledImage", + "class" : "Type-Declaration", + "opcode" : 27, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ] + }, + { + "opname" : "OpTypeArray", + "class" : "Type-Declaration", + "opcode" : 28, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" }, + { "kind" : "IdRef", "name" : "'Length'" } + ] + }, + { + "opname" : "OpTypeRuntimeArray", + "class" : "Type-Declaration", + "opcode" : 29, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Element Type'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpTypeStruct", + "class" : "Type-Declaration", + "opcode" : 30, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Member 0 type', +\n'member 1 type', +\n..." } + ] + }, + { + "opname" : "OpTypeOpaque", + "class" : "Type-Declaration", + "opcode" : 31, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "LiteralString", "name" : "The name of the opaque type." } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypePointer", + "class" : "Type-Declaration", + "opcode" : 32, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "name" : "'Type'" } + ] + }, + { + "opname" : "OpTypeFunction", + "class" : "Type-Declaration", + "opcode" : 33, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Return Type'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Parameter 0 Type', +\n'Parameter 1 Type', +\n..." } + ] + }, + { + "opname" : "OpTypeEvent", + "class" : "Type-Declaration", + "opcode" : 34, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpTypeDeviceEvent", + "class" : "Type-Declaration", + "opcode" : 35, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypeReserveId", + "class" : "Type-Declaration", + "opcode" : 36, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeQueue", + "class" : "Type-Declaration", + "opcode" : 37, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpTypePipe", + "class" : "Type-Declaration", + "opcode" : 38, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "AccessQualifier", "name" : "'Qualifier'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpTypeForwardPointer", + "class" : "Type-Declaration", + "opcode" : 39, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer Type'" }, + { "kind" : "StorageClass" } + ], + "capabilities" : [ + "Addresses", + "PhysicalStorageBufferAddresses" + ] + }, + { + "opname" : "OpConstantTrue", + "class" : "Constant-Creation", + "opcode" : 41, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstantFalse", + "class" : "Constant-Creation", + "opcode" : 42, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpConstant", + "class" : "Constant-Creation", + "opcode" : 43, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConstantComposite", + "class" : "Constant-Creation", + "opcode" : 44, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpConstantSampler", + "class" : "Constant-Creation", + "opcode" : 45, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "SamplerAddressingMode" }, + { "kind" : "LiteralInteger", "name" : "'Param'" }, + { "kind" : "SamplerFilterMode" } + ], + "capabilities" : [ "LiteralSampler" ] + }, + { + "opname" : "OpConstantNull", + "class" : "Constant-Creation", + "opcode" : 46, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantTrue", + "class" : "Constant-Creation", + "opcode" : 48, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstantFalse", + "class" : "Constant-Creation", + "opcode" : 49, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpSpecConstant", + "class" : "Constant-Creation", + "opcode" : 50, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralContextDependentNumber", "name" : "'Value'" } + ] + }, + { + "opname" : "OpSpecConstantComposite", + "class" : "Constant-Creation", + "opcode" : 51, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpSpecConstantOp", + "class" : "Constant-Creation", + "opcode" : 52, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralSpecConstantOpInteger", "name" : "'Opcode'" } + ] + }, + { + "opname" : "OpFunction", + "class" : "Function", + "opcode" : 54, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "FunctionControl" }, + { "kind" : "IdRef", "name" : "'Function Type'" } + ] + }, + { + "opname" : "OpFunctionParameter", + "class" : "Function", + "opcode" : 55, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpFunctionEnd", + "class" : "Function", + "opcode" : 56 + }, + { + "opname" : "OpFunctionCall", + "class" : "Function", + "opcode" : 57, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Argument 0', +\n'Argument 1', +\n..." } + ] + }, + { + "opname" : "OpVariable", + "class" : "Memory", + "opcode" : 59, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "StorageClass" }, + { "kind" : "IdRef", "quantifier" : "?", "name" : "'Initializer'" } + ] + }, + { + "opname" : "OpImageTexelPointer", + "class" : "Memory", + "opcode" : 60, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Sample'" } + ] + }, + { + "opname" : "OpLoad", + "class" : "Memory", + "opcode" : 61, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpStore", + "class" : "Memory", + "opcode" : 62, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemory", + "class" : "Memory", + "opcode" : 63, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ] + }, + { + "opname" : "OpCopyMemorySized", + "class" : "Memory", + "opcode" : 64, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Size'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpAccessChain", + "class" : "Memory", + "opcode" : 65, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpInBoundsAccessChain", + "class" : "Memory", + "opcode" : 66, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpPtrAccessChain", + "class" : "Memory", + "opcode" : 67, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ + "Addresses", + "VariablePointers", + "VariablePointersStorageBuffer", + "PhysicalStorageBufferAddresses" + ] + }, + { + "opname" : "OpArrayLength", + "class" : "Memory", + "opcode" : 68, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Structure'" }, + { "kind" : "LiteralInteger", "name" : "'Array member'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpGenericPtrMemSemantics", + "class" : "Memory", + "opcode" : 69, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpInBoundsPtrAccessChain", + "class" : "Memory", + "opcode" : 70, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Element'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Indexes'" } + ], + "capabilities" : [ "Addresses" ] + }, + { + "opname" : "OpDecorate", + "class" : "Annotation", + "opcode" : 71, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpMemberDecorate", + "class" : "Annotation", + "opcode" : 72, + "operands" : [ + { "kind" : "IdRef", "name" : "'Structure Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ] + }, + { + "opname" : "OpDecorationGroup", + "class" : "Annotation", + "opcode" : 73, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpGroupDecorate", + "class" : "Annotation", + "opcode" : 74, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpGroupMemberDecorate", + "class" : "Annotation", + "opcode" : 75, + "operands" : [ + { "kind" : "IdRef", "name" : "'Decoration Group'" }, + { "kind" : "PairIdRefLiteralInteger", "quantifier" : "*", "name" : "'Targets'" } + ] + }, + { + "opname" : "OpVectorExtractDynamic", + "class" : "Composite", + "opcode" : 77, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorInsertDynamic", + "class" : "Composite", + "opcode" : 78, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ] + }, + { + "opname" : "OpVectorShuffle", + "class" : "Composite", + "opcode" : 79, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Components'" } + ] + }, + { + "opname" : "OpCompositeConstruct", + "class" : "Composite", + "opcode" : 80, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Constituents'" } + ] + }, + { + "opname" : "OpCompositeExtract", + "class" : "Composite", + "opcode" : 81, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCompositeInsert", + "class" : "Composite", + "opcode" : 82, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Composite'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Indexes'" } + ] + }, + { + "opname" : "OpCopyObject", + "class" : "Composite", + "opcode" : 83, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpTranspose", + "class" : "Composite", + "opcode" : 84, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpSampledImage", + "class" : "Image", + "opcode" : 86, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ] + }, + { + "opname" : "OpImageSampleImplicitLod", + "class" : "Image", + "opcode" : 87, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleExplicitLod", + "class" : "Image", + "opcode" : 88, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ] + }, + { + "opname" : "OpImageSampleDrefImplicitLod", + "class" : "Image", + "opcode" : 89, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleDrefExplicitLod", + "class" : "Image", + "opcode" : 90, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjImplicitLod", + "class" : "Image", + "opcode" : 91, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjExplicitLod", + "class" : "Image", + "opcode" : 92, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefImplicitLod", + "class" : "Image", + "opcode" : 93, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageSampleProjDrefExplicitLod", + "class" : "Image", + "opcode" : 94, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageFetch", + "class" : "Image", + "opcode" : 95, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageGather", + "class" : "Image", + "opcode" : 96, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageDrefGather", + "class" : "Image", + "opcode" : 97, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpImageRead", + "class" : "Image", + "opcode" : 98, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImageWrite", + "class" : "Image", + "opcode" : 99, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Texel'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ] + }, + { + "opname" : "OpImage", + "class" : "Image", + "opcode" : 100, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" } + ] + }, + { + "opname" : "OpImageQueryFormat", + "class" : "Image", + "opcode" : 101, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQueryOrder", + "class" : "Image", + "opcode" : 102, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageQuerySizeLod", + "class" : "Image", + "opcode" : 103, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Level of Detail'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySize", + "class" : "Image", + "opcode" : 104, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLod", + "class" : "Image", + "opcode" : 105, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "ImageQuery" ] + }, + { + "opname" : "OpImageQueryLevels", + "class" : "Image", + "opcode" : 106, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpImageQuerySamples", + "class" : "Image", + "opcode" : 107, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" } + ], + "capabilities" : [ "Kernel", "ImageQuery" ] + }, + { + "opname" : "OpConvertFToU", + "class" : "Conversion", + "opcode" : 109, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertFToS", + "class" : "Conversion", + "opcode" : 110, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpConvertSToF", + "class" : "Conversion", + "opcode" : 111, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpConvertUToF", + "class" : "Conversion", + "opcode" : 112, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpUConvert", + "class" : "Conversion", + "opcode" : 113, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ] + }, + { + "opname" : "OpSConvert", + "class" : "Conversion", + "opcode" : 114, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ] + }, + { + "opname" : "OpFConvert", + "class" : "Conversion", + "opcode" : 115, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Float Value'" } + ] + }, + { + "opname" : "OpQuantizeToF16", + "class" : "Conversion", + "opcode" : 116, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpConvertPtrToU", + "class" : "Conversion", + "opcode" : 117, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ + "Addresses", + "PhysicalStorageBufferAddresses" + ] + }, + { + "opname" : "OpSatConvertSToU", + "class" : "Conversion", + "opcode" : 118, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Signed Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSatConvertUToS", + "class" : "Conversion", + "opcode" : 119, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Unsigned Value'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpConvertUToPtr", + "class" : "Conversion", + "opcode" : 120, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Integer Value'" } + ], + "capabilities" : [ + "Addresses", + "PhysicalStorageBufferAddresses" + ] + }, + { + "opname" : "OpPtrCastToGeneric", + "class" : "Conversion", + "opcode" : 121, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtr", + "class" : "Conversion", + "opcode" : 122, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGenericCastToPtrExplicit", + "class" : "Conversion", + "opcode" : 123, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "StorageClass", "name" : "'Storage'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpBitcast", + "class" : "Conversion", + "opcode" : 124, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSNegate", + "class" : "Arithmetic", + "opcode" : 126, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpFNegate", + "class" : "Arithmetic", + "opcode" : 127, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpIAdd", + "class" : "Arithmetic", + "opcode" : 128, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFAdd", + "class" : "Arithmetic", + "opcode" : 129, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISub", + "class" : "Arithmetic", + "opcode" : 130, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFSub", + "class" : "Arithmetic", + "opcode" : 131, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpIMul", + "class" : "Arithmetic", + "opcode" : 132, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMul", + "class" : "Arithmetic", + "opcode" : 133, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUDiv", + "class" : "Arithmetic", + "opcode" : 134, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSDiv", + "class" : "Arithmetic", + "opcode" : 135, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFDiv", + "class" : "Arithmetic", + "opcode" : 136, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMod", + "class" : "Arithmetic", + "opcode" : 137, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSRem", + "class" : "Arithmetic", + "opcode" : 138, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMod", + "class" : "Arithmetic", + "opcode" : 139, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFRem", + "class" : "Arithmetic", + "opcode" : 140, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFMod", + "class" : "Arithmetic", + "opcode" : 141, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpVectorTimesScalar", + "class" : "Arithmetic", + "opcode" : 142, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ] + }, + { + "opname" : "OpMatrixTimesScalar", + "class" : "Arithmetic", + "opcode" : 143, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Scalar'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpVectorTimesMatrix", + "class" : "Arithmetic", + "opcode" : 144, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" }, + { "kind" : "IdRef", "name" : "'Matrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesVector", + "class" : "Arithmetic", + "opcode" : 145, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Matrix'" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpMatrixTimesMatrix", + "class" : "Arithmetic", + "opcode" : 146, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'LeftMatrix'" }, + { "kind" : "IdRef", "name" : "'RightMatrix'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpOuterProduct", + "class" : "Arithmetic", + "opcode" : 147, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ], + "capabilities" : [ "Matrix" ] + }, + { + "opname" : "OpDot", + "class" : "Arithmetic", + "opcode" : 148, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector 1'" }, + { "kind" : "IdRef", "name" : "'Vector 2'" } + ] + }, + { + "opname" : "OpIAddCarry", + "class" : "Arithmetic", + "opcode" : 149, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpISubBorrow", + "class" : "Arithmetic", + "opcode" : 150, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUMulExtended", + "class" : "Arithmetic", + "opcode" : 151, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSMulExtended", + "class" : "Arithmetic", + "opcode" : 152, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpAny", + "class" : "Relational_and_Logical", + "opcode" : 154, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpAll", + "class" : "Relational_and_Logical", + "opcode" : 155, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Vector'" } + ] + }, + { + "opname" : "OpIsNan", + "class" : "Relational_and_Logical", + "opcode" : 156, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsInf", + "class" : "Relational_and_Logical", + "opcode" : 157, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ] + }, + { + "opname" : "OpIsFinite", + "class" : "Relational_and_Logical", + "opcode" : 158, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpIsNormal", + "class" : "Relational_and_Logical", + "opcode" : 159, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpSignBitSet", + "class" : "Relational_and_Logical", + "opcode" : 160, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLessOrGreater", + "class" : "Relational_and_Logical", + "opcode" : 161, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpOrdered", + "class" : "Relational_and_Logical", + "opcode" : 162, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpUnordered", + "class" : "Relational_and_Logical", + "opcode" : 163, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'x'" }, + { "kind" : "IdRef", "name" : "'y'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLogicalEqual", + "class" : "Relational_and_Logical", + "opcode" : 164, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNotEqual", + "class" : "Relational_and_Logical", + "opcode" : 165, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalOr", + "class" : "Relational_and_Logical", + "opcode" : 166, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalAnd", + "class" : "Relational_and_Logical", + "opcode" : 167, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpLogicalNot", + "class" : "Relational_and_Logical", + "opcode" : 168, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpSelect", + "class" : "Relational_and_Logical", + "opcode" : 169, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'Object 1'" }, + { "kind" : "IdRef", "name" : "'Object 2'" } + ] + }, + { + "opname" : "OpIEqual", + "class" : "Relational_and_Logical", + "opcode" : 170, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpINotEqual", + "class" : "Relational_and_Logical", + "opcode" : 171, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThan", + "class" : "Relational_and_Logical", + "opcode" : 172, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThan", + "class" : "Relational_and_Logical", + "opcode" : 173, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpUGreaterThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 174, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSGreaterThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 175, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThan", + "class" : "Relational_and_Logical", + "opcode" : 176, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThan", + "class" : "Relational_and_Logical", + "opcode" : 177, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpULessThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 178, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpSLessThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 179, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdEqual", + "class" : "Relational_and_Logical", + "opcode" : 180, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordEqual", + "class" : "Relational_and_Logical", + "opcode" : 181, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdNotEqual", + "class" : "Relational_and_Logical", + "opcode" : 182, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordNotEqual", + "class" : "Relational_and_Logical", + "opcode" : 183, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThan", + "class" : "Relational_and_Logical", + "opcode" : 184, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThan", + "class" : "Relational_and_Logical", + "opcode" : 185, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThan", + "class" : "Relational_and_Logical", + "opcode" : 186, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThan", + "class" : "Relational_and_Logical", + "opcode" : 187, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdLessThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 188, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordLessThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 189, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFOrdGreaterThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 190, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpFUnordGreaterThanEqual", + "class" : "Relational_and_Logical", + "opcode" : 191, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpShiftRightLogical", + "class" : "Bit", + "opcode" : 194, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftRightArithmetic", + "class" : "Bit", + "opcode" : 195, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpShiftLeftLogical", + "class" : "Bit", + "opcode" : 196, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Shift'" } + ] + }, + { + "opname" : "OpBitwiseOr", + "class" : "Bit", + "opcode" : 197, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseXor", + "class" : "Bit", + "opcode" : 198, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpBitwiseAnd", + "class" : "Bit", + "opcode" : 199, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ] + }, + { + "opname" : "OpNot", + "class" : "Bit", + "opcode" : 200, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ] + }, + { + "opname" : "OpBitFieldInsert", + "class" : "Bit", + "opcode" : 201, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Insert'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldSExtract", + "class" : "Bit", + "opcode" : 202, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitFieldUExtract", + "class" : "Bit", + "opcode" : 203, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" }, + { "kind" : "IdRef", "name" : "'Offset'" }, + { "kind" : "IdRef", "name" : "'Count'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitReverse", + "class" : "Bit", + "opcode" : 204, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpBitCount", + "class" : "Bit", + "opcode" : 205, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Base'" } + ] + }, + { + "opname" : "OpDPdx", + "class" : "Derivative", + "opcode" : 207, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdy", + "class" : "Derivative", + "opcode" : 208, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpFwidth", + "class" : "Derivative", + "opcode" : 209, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpDPdxFine", + "class" : "Derivative", + "opcode" : 210, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyFine", + "class" : "Derivative", + "opcode" : 211, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthFine", + "class" : "Derivative", + "opcode" : 212, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdxCoarse", + "class" : "Derivative", + "opcode" : 213, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpDPdyCoarse", + "class" : "Derivative", + "opcode" : 214, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpFwidthCoarse", + "class" : "Derivative", + "opcode" : 215, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'P'" } + ], + "capabilities" : [ "DerivativeControl" ] + }, + { + "opname" : "OpEmitVertex", + "class" : "Primitive", + "opcode" : 218, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEndPrimitive", + "class" : "Primitive", + "opcode" : 219, + "capabilities" : [ "Geometry" ] + }, + { + "opname" : "OpEmitStreamVertex", + "class" : "Primitive", + "opcode" : 220, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpEndStreamPrimitive", + "class" : "Primitive", + "opcode" : 221, + "operands" : [ + { "kind" : "IdRef", "name" : "'Stream'" } + ], + "capabilities" : [ "GeometryStreams" ] + }, + { + "opname" : "OpControlBarrier", + "class" : "Barrier", + "opcode" : 224, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpMemoryBarrier", + "class" : "Barrier", + "opcode" : 225, + "operands" : [ + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicLoad", + "class" : "Atomic", + "opcode" : 227, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicStore", + "class" : "Atomic", + "opcode" : 228, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicExchange", + "class" : "Atomic", + "opcode" : 229, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicCompareExchange", + "class" : "Atomic", + "opcode" : 230, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ] + }, + { + "opname" : "OpAtomicCompareExchangeWeak", + "class" : "Atomic", + "opcode" : 231, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Equal'" }, + { "kind" : "IdMemorySemantics", "name" : "'Unequal'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Comparator'" } + ], + "capabilities" : [ "Kernel" ], + "lastVersion" : "1.3" + }, + { + "opname" : "OpAtomicIIncrement", + "class" : "Atomic", + "opcode" : 232, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIDecrement", + "class" : "Atomic", + "opcode" : 233, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ] + }, + { + "opname" : "OpAtomicIAdd", + "class" : "Atomic", + "opcode" : 234, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicISub", + "class" : "Atomic", + "opcode" : 235, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMin", + "class" : "Atomic", + "opcode" : 236, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMin", + "class" : "Atomic", + "opcode" : 237, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicSMax", + "class" : "Atomic", + "opcode" : 238, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicUMax", + "class" : "Atomic", + "opcode" : 239, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicAnd", + "class" : "Atomic", + "opcode" : 240, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicOr", + "class" : "Atomic", + "opcode" : 241, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpAtomicXor", + "class" : "Atomic", + "opcode" : 242, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpPhi", + "class" : "Control-Flow", + "opcode" : 245, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "PairIdRefIdRef", "quantifier" : "*", "name" : "'Variable, Parent, ...'" } + ] + }, + { + "opname" : "OpLoopMerge", + "class" : "Control-Flow", + "opcode" : 246, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "IdRef", "name" : "'Continue Target'" }, + { "kind" : "LoopControl" } + ] + }, + { + "opname" : "OpSelectionMerge", + "class" : "Control-Flow", + "opcode" : 247, + "operands" : [ + { "kind" : "IdRef", "name" : "'Merge Block'" }, + { "kind" : "SelectionControl" } + ] + }, + { + "opname" : "OpLabel", + "class" : "Control-Flow", + "opcode" : 248, + "operands" : [ + { "kind" : "IdResult" } + ] + }, + { + "opname" : "OpBranch", + "class" : "Control-Flow", + "opcode" : 249, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target Label'" } + ] + }, + { + "opname" : "OpBranchConditional", + "class" : "Control-Flow", + "opcode" : 250, + "operands" : [ + { "kind" : "IdRef", "name" : "'Condition'" }, + { "kind" : "IdRef", "name" : "'True Label'" }, + { "kind" : "IdRef", "name" : "'False Label'" }, + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Branch weights'" } + ] + }, + { + "opname" : "OpSwitch", + "class" : "Control-Flow", + "opcode" : 251, + "operands" : [ + { "kind" : "IdRef", "name" : "'Selector'" }, + { "kind" : "IdRef", "name" : "'Default'" }, + { "kind" : "PairLiteralIntegerIdRef", "quantifier" : "*", "name" : "'Target'" } + ] + }, + { + "opname" : "OpKill", + "class" : "Control-Flow", + "opcode" : 252, + "capabilities" : [ "Shader" ] + }, + { + "opname" : "OpReturn", + "class" : "Control-Flow", + "opcode" : 253 + }, + { + "opname" : "OpReturnValue", + "class" : "Control-Flow", + "opcode" : 254, + "operands" : [ + { "kind" : "IdRef", "name" : "'Value'" } + ] + }, + { + "opname" : "OpUnreachable", + "class" : "Control-Flow", + "opcode" : 255 + }, + { + "opname" : "OpLifetimeStart", + "class" : "Control-Flow", + "opcode" : 256, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpLifetimeStop", + "class" : "Control-Flow", + "opcode" : 257, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "LiteralInteger", "name" : "'Size'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAsyncCopy", + "class" : "Group", + "opcode" : 259, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Destination'" }, + { "kind" : "IdRef", "name" : "'Source'" }, + { "kind" : "IdRef", "name" : "'Num Elements'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupWaitEvents", + "class" : "Group", + "opcode" : 260, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Events List'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpGroupAll", + "class" : "Group", + "opcode" : 261, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupAny", + "class" : "Group", + "opcode" : 262, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupBroadcast", + "class" : "Group", + "opcode" : 263, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'LocalId'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupIAdd", + "class" : "Group", + "opcode" : 264, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFAdd", + "class" : "Group", + "opcode" : 265, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMin", + "class" : "Group", + "opcode" : 266, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMin", + "class" : "Group", + "opcode" : 267, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMin", + "class" : "Group", + "opcode" : 268, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupFMax", + "class" : "Group", + "opcode" : 269, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupUMax", + "class" : "Group", + "opcode" : 270, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpGroupSMax", + "class" : "Group", + "opcode" : 271, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ] + }, + { + "opname" : "OpReadPipe", + "class" : "Pipe", + "opcode" : 274, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpWritePipe", + "class" : "Pipe", + "opcode" : 275, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedReadPipe", + "class" : "Pipe", + "opcode" : 276, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReservedWritePipe", + "class" : "Pipe", + "opcode" : 277, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Index'" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveReadPipePackets", + "class" : "Pipe", + "opcode" : 278, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpReserveWritePipePackets", + "class" : "Pipe", + "opcode" : 279, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitReadPipe", + "class" : "Pipe", + "opcode" : 280, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpCommitWritePipe", + "class" : "Pipe", + "opcode" : 281, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpIsValidReserveId", + "class" : "Pipe", + "opcode" : 282, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetNumPipePackets", + "class" : "Pipe", + "opcode" : 283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGetMaxPipePackets", + "class" : "Pipe", + "opcode" : 284, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveReadPipePackets", + "class" : "Pipe", + "opcode" : 285, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupReserveWritePipePackets", + "class" : "Pipe", + "opcode" : 286, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Num Packets'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitReadPipe", + "class" : "Pipe", + "opcode" : 287, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpGroupCommitWritePipe", + "class" : "Pipe", + "opcode" : 288, + "operands" : [ + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Pipe'" }, + { "kind" : "IdRef", "name" : "'Reserve Id'" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "Pipes" ] + }, + { + "opname" : "OpEnqueueMarker", + "class" : "Device-Side_Enqueue", + "opcode" : 291, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpEnqueueKernel", + "class" : "Device-Side_Enqueue", + "opcode" : 292, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Queue'" }, + { "kind" : "IdRef", "name" : "'Flags'" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Num Events'" }, + { "kind" : "IdRef", "name" : "'Wait Events'" }, + { "kind" : "IdRef", "name" : "'Ret Event'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Local Size'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeSubGroupCount", + "class" : "Device-Side_Enqueue", + "opcode" : 293, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelNDrangeMaxSubGroupSize", + "class" : "Device-Side_Enqueue", + "opcode" : 294, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'ND Range'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelWorkGroupSize", + "class" : "Device-Side_Enqueue", + "opcode" : 295, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetKernelPreferredWorkGroupSizeMultiple", + "class" : "Device-Side_Enqueue", + "opcode" : 296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpRetainEvent", + "class" : "Device-Side_Enqueue", + "opcode" : 297, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpReleaseEvent", + "class" : "Device-Side_Enqueue", + "opcode" : 298, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCreateUserEvent", + "class" : "Device-Side_Enqueue", + "opcode" : 299, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpIsValidEvent", + "class" : "Device-Side_Enqueue", + "opcode" : 300, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Event'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpSetUserEventStatus", + "class" : "Device-Side_Enqueue", + "opcode" : 301, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Status'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpCaptureEventProfilingInfo", + "class" : "Device-Side_Enqueue", + "opcode" : 302, + "operands" : [ + { "kind" : "IdRef", "name" : "'Event'" }, + { "kind" : "IdRef", "name" : "'Profiling Info'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpGetDefaultQueue", + "class" : "Device-Side_Enqueue", + "opcode" : 303, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpBuildNDRange", + "class" : "Device-Side_Enqueue", + "opcode" : 304, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'GlobalWorkSize'" }, + { "kind" : "IdRef", "name" : "'LocalWorkSize'" }, + { "kind" : "IdRef", "name" : "'GlobalWorkOffset'" } + ], + "capabilities" : [ "DeviceEnqueue" ] + }, + { + "opname" : "OpImageSparseSampleImplicitLod", + "class" : "Image", + "opcode" : 305, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleExplicitLod", + "class" : "Image", + "opcode" : 306, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefImplicitLod", + "class" : "Image", + "opcode" : 307, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleDrefExplicitLod", + "class" : "Image", + "opcode" : 308, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseSampleProjImplicitLod", + "class" : "Image", + "opcode" : 309, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version" : "None" + }, + { + "opname" : "OpImageSparseSampleProjExplicitLod", + "class" : "Image", + "opcode" : 310, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ], + "version" : "None" + }, + { + "opname" : "OpImageSparseSampleProjDrefImplicitLod", + "class" : "Image", + "opcode" : 311, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ], + "version" : "None" + }, + { + "opname" : "OpImageSparseSampleProjDrefExplicitLod", + "class" : "Image", + "opcode" : 312, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands" } + ], + "capabilities" : [ "SparseResidency" ], + "version" : "None" + }, + { + "opname" : "OpImageSparseFetch", + "class" : "Image", + "opcode" : 313, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseGather", + "class" : "Image", + "opcode" : 314, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Component'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseDrefGather", + "class" : "Image", + "opcode" : 315, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'D~ref~'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpImageSparseTexelsResident", + "class" : "Image", + "opcode" : 316, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Resident Code'" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpNoLine", + "class" : "Debug", + "opcode" : 317 + }, + { + "opname" : "OpAtomicFlagTestAndSet", + "class" : "Atomic", + "opcode" : 318, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpAtomicFlagClear", + "class" : "Atomic", + "opcode" : 319, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "Kernel" ] + }, + { + "opname" : "OpImageSparseRead", + "class" : "Image", + "opcode" : 320, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "SparseResidency" ] + }, + { + "opname" : "OpSizeOf", + "class" : "Miscellaneous", + "opcode" : 321, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" } + ], + "capabilities" : [ "Addresses" ], + "version" : "1.1" + }, + { + "opname" : "OpTypePipeStorage", + "class" : "Type-Declaration", + "opcode" : 322, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "PipeStorage" ], + "version" : "1.1" + }, + { + "opname" : "OpConstantPipeStorage", + "class" : "Pipe", + "opcode" : 323, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "LiteralInteger", "name" : "'Packet Size'" }, + { "kind" : "LiteralInteger", "name" : "'Packet Alignment'" }, + { "kind" : "LiteralInteger", "name" : "'Capacity'" } + ], + "capabilities" : [ "PipeStorage" ], + "version" : "1.1" + }, + { + "opname" : "OpCreatePipeFromPipeStorage", + "class" : "Pipe", + "opcode" : 324, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pipe Storage'" } + ], + "capabilities" : [ "PipeStorage" ], + "version" : "1.1" + }, + { + "opname" : "OpGetKernelLocalSizeForSubgroupCount", + "class" : "Device-Side_Enqueue", + "opcode" : 325, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ], + "version" : "1.1" + }, + { + "opname" : "OpGetKernelMaxNumSubgroups", + "class" : "Device-Side_Enqueue", + "opcode" : 326, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Invoke'" }, + { "kind" : "IdRef", "name" : "'Param'" }, + { "kind" : "IdRef", "name" : "'Param Size'" }, + { "kind" : "IdRef", "name" : "'Param Align'" } + ], + "capabilities" : [ "SubgroupDispatch" ], + "version" : "1.1" + }, + { + "opname" : "OpTypeNamedBarrier", + "class" : "Type-Declaration", + "opcode" : 327, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "NamedBarrier" ], + "version" : "1.1" + }, + { + "opname" : "OpNamedBarrierInitialize", + "class" : "Barrier", + "opcode" : 328, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Subgroup Count'" } + ], + "capabilities" : [ "NamedBarrier" ], + "version" : "1.1" + }, + { + "opname" : "OpMemoryNamedBarrier", + "class" : "Barrier", + "opcode" : 329, + "operands" : [ + { "kind" : "IdRef", "name" : "'Named Barrier'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" } + ], + "capabilities" : [ "NamedBarrier" ], + "version" : "1.1" + }, + { + "opname" : "OpModuleProcessed", + "class" : "Debug", + "opcode" : 330, + "operands" : [ + { "kind" : "LiteralString", "name" : "'Process'" } + ], + "version" : "1.1" + }, + { + "opname" : "OpExecutionModeId", + "class" : "Mode-Setting", + "opcode" : 331, + "operands" : [ + { "kind" : "IdRef", "name" : "'Entry Point'" }, + { "kind" : "ExecutionMode", "name" : "'Mode'" } + ], + "version" : "1.2" + }, + { + "opname" : "OpDecorateId", + "class" : "Annotation", + "opcode" : 332, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.2" + }, + { + "opname" : "OpGroupNonUniformElect", + "class" : "Non-Uniform", + "opcode" : 333, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" } + ], + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformAll", + "class" : "Non-Uniform", + "opcode" : 334, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "GroupNonUniformVote" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformAny", + "class" : "Non-Uniform", + "opcode" : 335, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "GroupNonUniformVote" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformAllEqual", + "class" : "Non-Uniform", + "opcode" : 336, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformVote" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBroadcast", + "class" : "Non-Uniform", + "opcode" : 337, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Id'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBroadcastFirst", + "class" : "Non-Uniform", + "opcode" : 338, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallot", + "class" : "Non-Uniform", + "opcode" : 339, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformInverseBallot", + "class" : "Non-Uniform", + "opcode" : 340, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallotBitExtract", + "class" : "Non-Uniform", + "opcode" : 341, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallotBitCount", + "class" : "Non-Uniform", + "opcode" : 342, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallotFindLSB", + "class" : "Non-Uniform", + "opcode" : 343, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBallotFindMSB", + "class" : "Non-Uniform", + "opcode" : 344, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformShuffle", + "class" : "Non-Uniform", + "opcode" : 345, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Id'" } + ], + "capabilities" : [ "GroupNonUniformShuffle" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformShuffleXor", + "class" : "Non-Uniform", + "opcode" : 346, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Mask'" } + ], + "capabilities" : [ "GroupNonUniformShuffle" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformShuffleUp", + "class" : "Non-Uniform", + "opcode" : 347, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "GroupNonUniformShuffleRelative" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformShuffleDown", + "class" : "Non-Uniform", + "opcode" : 348, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "GroupNonUniformShuffleRelative" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformIAdd", + "class" : "Non-Uniform", + "opcode" : 349, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformFAdd", + "class" : "Non-Uniform", + "opcode" : 350, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformIMul", + "class" : "Non-Uniform", + "opcode" : 351, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformFMul", + "class" : "Non-Uniform", + "opcode" : 352, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformSMin", + "class" : "Non-Uniform", + "opcode" : 353, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformUMin", + "class" : "Non-Uniform", + "opcode" : 354, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformFMin", + "class" : "Non-Uniform", + "opcode" : 355, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformSMax", + "class" : "Non-Uniform", + "opcode" : 356, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformUMax", + "class" : "Non-Uniform", + "opcode" : 357, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformFMax", + "class" : "Non-Uniform", + "opcode" : 358, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBitwiseAnd", + "class" : "Non-Uniform", + "opcode" : 359, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBitwiseOr", + "class" : "Non-Uniform", + "opcode" : 360, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformBitwiseXor", + "class" : "Non-Uniform", + "opcode" : 361, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformLogicalAnd", + "class" : "Non-Uniform", + "opcode" : 362, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformLogicalOr", + "class" : "Non-Uniform", + "opcode" : 363, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformLogicalXor", + "class" : "Non-Uniform", + "opcode" : 364, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'ClusterSize'", "quantifier" : "?" } + ], + "capabilities" : [ "GroupNonUniformArithmetic", "GroupNonUniformClustered", "GroupNonUniformPartitionedNV" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformQuadBroadcast", + "class" : "Non-Uniform", + "opcode" : 365, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "GroupNonUniformQuad" ], + "version" : "1.3" + }, + { + "opname" : "OpGroupNonUniformQuadSwap", + "class" : "Non-Uniform", + "opcode" : 366, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "GroupNonUniformQuad" ], + "version" : "1.3" + }, + { + "opname" : "OpCopyLogical", + "class" : "Composite", + "opcode" : 400, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "version" : "1.4" + }, + { + "opname" : "OpPtrEqual", + "class" : "Memory", + "opcode" : 401, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version" : "1.4" + }, + { + "opname" : "OpPtrNotEqual", + "class" : "Memory", + "opcode" : 402, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "version" : "1.4" + }, + { + "opname" : "OpPtrDiff", + "class" : "Memory", + "opcode" : 403, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "Addresses", "VariablePointers", "VariablePointersStorageBuffer" ], + "version" : "1.4" + }, + { + "opname" : "OpTerminateInvocation", + "class" : "Control-Flow", + "opcode" : 4416, + "extensions" : [ + "SPV_KHR_terminate_invocation" + ], + "capabilities" : [ "Shader" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupBallotKHR", + "class" : "Group", + "opcode" : 4421, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupFirstInvocationKHR", + "class" : "Group", + "opcode" : 4422, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAllKHR", + "class" : "Group", + "opcode" : 4428, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "extensions" : [ + "SPV_KHR_subgroup_vote" + ], + "capabilities" : [ "SubgroupVoteKHR" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAnyKHR", + "class" : "Group", + "opcode" : 4429, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "extensions" : [ + "SPV_KHR_subgroup_vote" + ], + "capabilities" : [ "SubgroupVoteKHR" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAllEqualKHR", + "class" : "Group", + "opcode" : 4430, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Predicate'" } + ], + "extensions" : [ + "SPV_KHR_subgroup_vote" + ], + "capabilities" : [ "SubgroupVoteKHR" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupReadInvocationKHR", + "class" : "Group", + "opcode" : 4432, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" }, + { "kind" : "IdRef", "name" : "'Index'" } + ], + "capabilities" : [ "SubgroupBallotKHR" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpTypeRayQueryProvisionalKHR", + "class" : "Reserved", + "opcode" : 4472, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryInitializeKHR", + "class" : "Reserved", + "opcode" : 4473, + "operands" : [ + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Accel'" + }, + { + "kind" : "IdRef", + "name" : "'RayFlags'" + }, + { + "kind" : "IdRef", + "name" : "'CullMask'" + }, + { + "kind" : "IdRef", + "name" : "'RayOrigin'" + }, + { + "kind" : "IdRef", + "name" : "'RayTMin'" + }, + { + "kind" : "IdRef", + "name" : "'RayDirection'" + }, + { + "kind" : "IdRef", + "name" : "'RayTMax'" + } + + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryTerminateKHR", + "class" : "Reserved", + "opcode" : 4474, + "operands" : [ + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGenerateIntersectionKHR", + "class" : "Reserved", + "opcode" : 4475, + "operands" : [ + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'HitT'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryConfirmIntersectionKHR", + "class" : "Reserved", + "opcode" : 4476, + "operands" : [ + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryProceedKHR", + "class" : "Reserved", + "opcode" : 4477, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionTypeKHR", + "class" : "Reserved", + "opcode" : 4479, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpGroupIAddNonUniformAMD", + "class" : "Group", + "opcode" : 5000, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupFAddNonUniformAMD", + "class" : "Group", + "opcode" : 5001, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupFMinNonUniformAMD", + "class" : "Group", + "opcode" : 5002, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupUMinNonUniformAMD", + "class" : "Group", + "opcode" : 5003, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupSMinNonUniformAMD", + "class" : "Group", + "opcode" : 5004, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupFMaxNonUniformAMD", + "class" : "Group", + "opcode" : 5005, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupUMaxNonUniformAMD", + "class" : "Group", + "opcode" : 5006, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpGroupSMaxNonUniformAMD", + "class" : "Group", + "opcode" : 5007, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "GroupOperation", "name" : "'Operation'" }, + { "kind" : "IdRef", "name" : "'X'" } + ], + "capabilities" : [ "Groups" ], + "extensions" : [ "SPV_AMD_shader_ballot" ], + "version" : "None" + }, + { + "opname" : "OpFragmentMaskFetchAMD", + "class" : "Reserved", + "opcode" : 5011, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "FragmentMaskAMD" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ], + "version" : "None" + }, + { + "opname" : "OpFragmentFetchAMD", + "class" : "Reserved", + "opcode" : 5012, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Fragment Index'" } + ], + "capabilities" : [ "FragmentMaskAMD" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ], + "version" : "None" + }, + { + "opname" : "OpReadClockKHR", + "class" : "Reserved", + "opcode" : 5056, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdScope", "name" : "'Execution'" } + ], + "capabilities" : [ "ShaderClockKHR" ], + "extensions" : [ "SPV_KHR_shader_clock" ], + "version" : "None" + }, + { + "opname" : "OpImageSampleFootprintNV", + "class" : "Image", + "opcode" : 5283, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Sampled Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Granularity'" }, + { "kind" : "IdRef", "name" : "'Coarse'" }, + { "kind" : "ImageOperands", "quantifier" : "?" } + ], + "capabilities" : [ "ImageFootprintNV" ], + "extensions" : [ "SPV_NV_shader_image_footprint" ], + "version" : "None" + }, + { + "opname" : "OpGroupNonUniformPartitionNV", + "class" : "Non-Uniform", + "opcode" : 5296, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "GroupNonUniformPartitionedNV" ], + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + }, + { + "opname" : "OpWritePackedPrimitiveIndices4x8NV", + "class" : "Reserved", + "opcode" : 5299, + "operands" : [ + { "kind" : "IdRef", "name" : "'Index Offset'" }, + { "kind" : "IdRef", "name" : "'Packed Indices'" } + ], + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "opname" : "OpReportIntersectionNV", + "class" : "Reserved", + "opcode" : 5334, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Hit'" }, + { "kind" : "IdRef", "name" : "'HitKind'" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpReportIntersectionKHR", + "class" : "Reserved", + "opcode" : 5334, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Hit'" }, + { "kind" : "IdRef", "name" : "'HitKind'" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpIgnoreIntersectionNV", + "class" : "Reserved", + "opcode" : 5335, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpIgnoreIntersectionKHR", + "class" : "Reserved", + "opcode" : 5335, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTerminateRayNV", + "class" : "Reserved", + "opcode" : 5336, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTerminateRayKHR", + "class" : "Reserved", + "opcode" : 5336, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTraceNV", + "class" : "Reserved", + "opcode" : 5337, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'PayloadId'" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTraceRayKHR", + "class" : "Reserved", + "opcode" : 5337, + "operands" : [ + + { "kind" : "IdRef", "name" : "'Accel'" }, + { "kind" : "IdRef", "name" : "'Ray Flags'" }, + { "kind" : "IdRef", "name" : "'Cull Mask'" }, + { "kind" : "IdRef", "name" : "'SBT Offset'" }, + { "kind" : "IdRef", "name" : "'SBT Stride'" }, + { "kind" : "IdRef", "name" : "'Miss Index'" }, + { "kind" : "IdRef", "name" : "'Ray Origin'" }, + { "kind" : "IdRef", "name" : "'Ray Tmin'" }, + { "kind" : "IdRef", "name" : "'Ray Direction'" }, + { "kind" : "IdRef", "name" : "'Ray Tmax'" }, + { "kind" : "IdRef", "name" : "'PayloadId'" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTypeAccelerationStructureNV", + "class" : "Reserved", + "opcode" : 5341, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR", "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing", "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpTypeAccelerationStructureKHR", + "class" : "Reserved", + "opcode" : 5341, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR", "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing", "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpExecuteCallableNV", + "class" : "Reserved", + "opcode" : 5344, + "operands" : [ + + { "kind" : "IdRef", "name" : "'SBT Index'" }, + { "kind" : "IdRef", "name" : "'Callable DataId'" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpExecuteCallableKHR", + "class" : "Reserved", + "opcode" : 5344, + "operands" : [ + + { "kind" : "IdRef", "name" : "'SBT Index'" }, + { "kind" : "IdRef", "name" : "'Callable DataId'" } + ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "opname" : "OpTypeCooperativeMatrixNV", + "class" : "Reserved", + "opcode" : 5358, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Component Type'" }, + { "kind" : "IdScope", "name" : "'Execution'" }, + { "kind" : "IdRef", "name" : "'Rows'" }, + { "kind" : "IdRef", "name" : "'Columns'" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixLoadNV", + "class" : "Reserved", + "opcode" : 5359, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Column Major'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixStoreNV", + "class" : "Reserved", + "opcode" : 5360, + "operands" : [ + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdRef", "name" : "'Object'" }, + { "kind" : "IdRef", "name" : "'Stride'" }, + { "kind" : "IdRef", "name" : "'Column Major'" }, + { "kind" : "MemoryAccess", "quantifier" : "?" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixMulAddNV", + "class" : "Reserved", + "opcode" : 5361, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'A'" }, + { "kind" : "IdRef", "name" : "'B'" }, + { "kind" : "IdRef", "name" : "'C'" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpCooperativeMatrixLengthNV", + "class" : "Reserved", + "opcode" : 5362, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Type'" } + ], + "capabilities" : [ "CooperativeMatrixNV" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "opname" : "OpBeginInvocationInterlockEXT", + "class" : "Reserved", + "opcode" : 5364, + "capabilities" : [ "FragmentShaderSampleInterlockEXT", "FragmentShaderPixelInterlockEXT", "FragmentShaderShadingRateInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "opname" : "OpEndInvocationInterlockEXT", + "class" : "Reserved", + "opcode" : 5365, + "capabilities" : [ "FragmentShaderSampleInterlockEXT", "FragmentShaderPixelInterlockEXT", "FragmentShaderShadingRateInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "opname" : "OpDemoteToHelperInvocationEXT", + "class" : "Reserved", + "opcode" : 5380, + "capabilities" : [ "DemoteToHelperInvocationEXT" ], + "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], + "version" : "None" + }, + { + "opname" : "OpIsHelperInvocationEXT", + "class" : "Reserved", + "opcode" : 5381, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "DemoteToHelperInvocationEXT" ], + "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupShuffleINTEL", + "class" : "Group", + "opcode" : 5571, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'InvocationId'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupShuffleDownINTEL", + "class" : "Group", + "opcode" : 5572, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Next'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupShuffleUpINTEL", + "class" : "Group", + "opcode" : 5573, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Previous'" }, + { "kind" : "IdRef", "name" : "'Current'" }, + { "kind" : "IdRef", "name" : "'Delta'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupShuffleXorINTEL", + "class" : "Group", + "opcode" : 5574, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Data'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "SubgroupShuffleINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupBlockReadINTEL", + "class" : "Group", + "opcode" : 5575, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ptr'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupBlockWriteINTEL", + "class" : "Group", + "opcode" : 5576, + "operands" : [ + { "kind" : "IdRef", "name" : "'Ptr'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupBufferBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupImageBlockReadINTEL", + "class" : "Group", + "opcode" : 5577, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupImageBlockWriteINTEL", + "class" : "Group", + "opcode" : 5578, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupImageMediaBlockReadINTEL", + "class" : "Group", + "opcode" : 5580, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Width'" }, + { "kind" : "IdRef", "name" : "'Height'" } + ], + "capabilities" : [ "SubgroupImageMediaBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupImageMediaBlockWriteINTEL", + "class" : "Group", + "opcode" : 5581, + "operands" : [ + { "kind" : "IdRef", "name" : "'Image'" }, + { "kind" : "IdRef", "name" : "'Coordinate'" }, + { "kind" : "IdRef", "name" : "'Width'" }, + { "kind" : "IdRef", "name" : "'Height'" }, + { "kind" : "IdRef", "name" : "'Data'" } + ], + "capabilities" : [ "SubgroupImageMediaBlockIOINTEL" ], + "version" : "None" + }, + { + "opname" : "OpUCountLeadingZerosINTEL", + "class" : "Reserved", + "opcode" : 5585, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUCountTrailingZerosINTEL", + "class" : "Reserved", + "opcode" : 5586, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpAbsISubINTEL", + "class" : "Reserved", + "opcode" : 5587, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpAbsUSubINTEL", + "class" : "Reserved", + "opcode" : 5588, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpIAddSatINTEL", + "class" : "Reserved", + "opcode" : 5589, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUAddSatINTEL", + "class" : "Reserved", + "opcode" : 5590, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpIAverageINTEL", + "class" : "Reserved", + "opcode" : 5591, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUAverageINTEL", + "class" : "Reserved", + "opcode" : 5592, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpIAverageRoundedINTEL", + "class" : "Reserved", + "opcode" : 5593, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUAverageRoundedINTEL", + "class" : "Reserved", + "opcode" : 5594, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpISubSatINTEL", + "class" : "Reserved", + "opcode" : 5595, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUSubSatINTEL", + "class" : "Reserved", + "opcode" : 5596, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpIMul32x16INTEL", + "class" : "Reserved", + "opcode" : 5597, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpUMul32x16INTEL", + "class" : "Reserved", + "opcode" : 5598, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Operand 1'" }, + { "kind" : "IdRef", "name" : "'Operand 2'" } + ], + "capabilities" : [ "IntegerFunctions2INTEL" ], + "version" : "None" + }, + { + "opname" : "OpFunctionPointerINTEL", + "class" : "@exclude", + "opcode" : 5600, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Function'" } + ], + "capabilities" : [ "FunctionPointersINTEL" ], + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "opname" : "OpFunctionPointerCallINTEL", + "class" : "@exclude", + "opcode" : 5601, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "quantifier" : "*", "name" : "'Operand 1'" } + ], + "capabilities" : [ "FunctionPointersINTEL" ], + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "opname" : "OpDecorateString", + "class" : "Annotation", + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.4" + }, + { + "opname" : "OpDecorateStringGOOGLE", + "class" : "Annotation", + "opcode" : 5632, + "operands" : [ + { "kind" : "IdRef", "name" : "'Target'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.4" + }, + { + "opname" : "OpMemberDecorateString", + "class" : "Annotation", + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.4" + }, + { + "opname" : "OpMemberDecorateStringGOOGLE", + "class" : "Annotation", + "opcode" : 5633, + "operands" : [ + { "kind" : "IdRef", "name" : "'Struct Type'" }, + { "kind" : "LiteralInteger", "name" : "'Member'" }, + { "kind" : "Decoration" } + ], + "extensions" : [ "SPV_GOOGLE_decorate_string", "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "1.4" + }, + { + "opname" : "OpVmeImageINTEL", + "class" : "@exclude", + "opcode" : 5699, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" }, + { "kind" : "IdRef", "name" : "'Sampler'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeVmeImageINTEL", + "class" : "@exclude", + "opcode" : 5700, + "operands" : [ + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Type'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImePayloadINTEL", + "class" : "@exclude", + "opcode" : 5701, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcRefPayloadINTEL", + "class" : "@exclude", + "opcode" : 5702, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcSicPayloadINTEL", + "class" : "@exclude", + "opcode" : 5703, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcMcePayloadINTEL", + "class" : "@exclude", + "opcode" : 5704, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcMceResultINTEL", + "class" : "@exclude", + "opcode" : 5705, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeResultINTEL", + "class" : "@exclude", + "opcode" : 5706, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeResultSingleReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5707, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeResultDualReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5708, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeSingleReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5709, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcImeDualReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5710, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcRefResultINTEL", + "class" : "@exclude", + "opcode" : 5711, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpTypeAvcSicResultINTEL", + "class" : "@exclude", + "opcode" : 5712, + "operands" : [ + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5713, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5714, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reference Base Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5715, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetInterShapePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5716, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Shape Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL", + "class" : "@exclude", + "opcode" : 5717, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL", + "class" : "@exclude", + "opcode" : 5718, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Direction Cost'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5719, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL", + "class" : "@exclude", + "opcode" : 5720, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL", + "class" : "@exclude", + "opcode" : 5721, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL", + "class" : "@exclude", + "opcode" : 5722, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL", + "class" : "@exclude", + "opcode" : 5723, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL", + "class" : "@exclude", + "opcode" : 5724, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Cost Center Delta'" }, + { "kind" : "IdRef", "name" : "'Packed Cost Table'" }, + { "kind" : "IdRef", "name" : "'Cost Precision'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5725, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Slice Type'" }, + { "kind" : "IdRef", "name" : "'Qp'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL", + "class" : "@exclude", + "opcode" : 5726, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5727, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationChromaINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetAcOnlyHaarINTEL", + "class" : "@exclude", + "opcode" : 5728, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL", + "class" : "@exclude", + "opcode" : 5729, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Source Field Polarity'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL", + "class" : "@exclude", + "opcode" : 5730, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Reference Field Polarity'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL", + "class" : "@exclude", + "opcode" : 5731, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Forward Reference Field Polarity'" }, + { "kind" : "IdRef", "name" : "'Backward Reference Field Polarity'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToImePayloadINTEL", + "class" : "@exclude", + "opcode" : 5732, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToImeResultINTEL", + "class" : "@exclude", + "opcode" : 5733, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToRefPayloadINTEL", + "class" : "@exclude", + "opcode" : 5734, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToRefResultINTEL", + "class" : "@exclude", + "opcode" : 5735, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToSicPayloadINTEL", + "class" : "@exclude", + "opcode" : 5736, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceConvertToSicResultINTEL", + "class" : "@exclude", + "opcode" : 5737, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetMotionVectorsINTEL", + "class" : "@exclude", + "opcode" : 5738, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterDistortionsINTEL", + "class" : "@exclude", + "opcode" : 5739, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetBestInterDistortionsINTEL", + "class" : "@exclude", + "opcode" : 5740, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterMajorShapeINTEL", + "class" : "@exclude", + "opcode" : 5741, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterMinorShapeINTEL", + "class" : "@exclude", + "opcode" : 5742, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterDirectionsINTEL", + "class" : "@exclude", + "opcode" : 5743, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterMotionVectorCountINTEL", + "class" : "@exclude", + "opcode" : 5744, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterReferenceIdsINTEL", + "class" : "@exclude", + "opcode" : 5745, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL", + "class" : "@exclude", + "opcode" : 5746, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Parameter Field Polarities'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeInitializeINTEL", + "class" : "@exclude", + "opcode" : 5747, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Coord'" }, + { "kind" : "IdRef", "name" : "'Partition Mask'" }, + { "kind" : "IdRef", "name" : "'SAD Adjustment'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetSingleReferenceINTEL", + "class" : "@exclude", + "opcode" : 5748, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ref Offset'" }, + { "kind" : "IdRef", "name" : "'Search Window Config'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetDualReferenceINTEL", + "class" : "@exclude", + "opcode" : 5749, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Offset'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Offset'" }, + { "kind" : "IdRef", "name" : "'id> Search Window Config'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeRefWindowSizeINTEL", + "class" : "@exclude", + "opcode" : 5750, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Search Window Config'" }, + { "kind" : "IdRef", "name" : "'Dual Ref'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeAdjustRefOffsetINTEL", + "class" : "@exclude", + "opcode" : 5751, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Ref Offset'" }, + { "kind" : "IdRef", "name" : "'Src Coord'" }, + { "kind" : "IdRef", "name" : "'Ref Window Size'" }, + { "kind" : "IdRef", "name" : "'Image Size'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeConvertToMcePayloadINTEL", + "class" : "@exclude", + "opcode" : 5752, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL", + "class" : "@exclude", + "opcode" : 5753, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Max Motion Vector Count'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL", + "class" : "@exclude", + "opcode" : 5754, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL", + "class" : "@exclude", + "opcode" : 5755, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Threshold'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeSetWeightedSadINTEL", + "class" : "@exclude", + "opcode" : 5756, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Sad Weights'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL", + "class" : "@exclude", + "opcode" : 5757, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL", + "class" : "@exclude", + "opcode" : 5758, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5759, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Streamin Components'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5760, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Streamin Components'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5761, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5762, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL", + "class" : "@exclude", + "opcode" : 5763, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Streamin Components'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL", + "class" : "@exclude", + "opcode" : 5764, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Streamin Components'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeConvertToMceResultINTEL", + "class" : "@exclude", + "opcode" : 5765, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5766, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetDualReferenceStreaminINTEL", + "class" : "@exclude", + "opcode" : 5767, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5768, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL", + "class" : "@exclude", + "opcode" : 5769, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL", + "class" : "@exclude", + "opcode" : 5770, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL", + "class" : "@exclude", + "opcode" : 5771, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL", + "class" : "@exclude", + "opcode" : 5772, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL", + "class" : "@exclude", + "opcode" : 5773, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL", + "class" : "@exclude", + "opcode" : 5774, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL", + "class" : "@exclude", + "opcode" : 5775, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" }, + { "kind" : "IdRef", "name" : "'Major Shape'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetBorderReachedINTEL", + "class" : "@exclude", + "opcode" : 5776, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Image Select'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL", + "class" : "@exclude", + "opcode" : 5777, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL", + "class" : "@exclude", + "opcode" : 5778, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL", + "class" : "@exclude", + "opcode" : 5779, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL", + "class" : "@exclude", + "opcode" : 5780, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcFmeInitializeINTEL", + "class" : "@exclude", + "opcode" : 5781, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Coord'" }, + { "kind" : "IdRef", "name" : "'Motion Vectors'" }, + { "kind" : "IdRef", "name" : "'Major Shapes'" }, + { "kind" : "IdRef", "name" : "'Minor Shapes'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'Pixel Resolution'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcBmeInitializeINTEL", + "class" : "@exclude", + "opcode" : 5782, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Coord'" }, + { "kind" : "IdRef", "name" : "'Motion Vectors'" }, + { "kind" : "IdRef", "name" : "'Major Shapes'" }, + { "kind" : "IdRef", "name" : "'Minor Shapes'" }, + { "kind" : "IdRef", "name" : "'Direction'" }, + { "kind" : "IdRef", "name" : "'Pixel Resolution'" }, + { "kind" : "IdRef", "name" : "'Bidirectional Weight'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefConvertToMcePayloadINTEL", + "class" : "@exclude", + "opcode" : 5783, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL", + "class" : "@exclude", + "opcode" : 5784, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefSetBilinearFilterEnableINTEL", + "class" : "@exclude", + "opcode" : 5785, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL", + "class" : "@exclude", + "opcode" : 5786, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL", + "class" : "@exclude", + "opcode" : 5787, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL", + "class" : "@exclude", + "opcode" : 5788, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL", + "class" : "@exclude", + "opcode" : 5789, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Field Polarities'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcRefConvertToMceResultINTEL", + "class" : "@exclude", + "opcode" : 5790, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicInitializeINTEL", + "class" : "@exclude", + "opcode" : 5791, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Coord'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConfigureSkcINTEL", + "class" : "@exclude", + "opcode" : 5792, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Skip Block Partition Type'" }, + { "kind" : "IdRef", "name" : "'Skip Motion Vector Mask'" }, + { "kind" : "IdRef", "name" : "'Motion Vectors'" }, + { "kind" : "IdRef", "name" : "'Bidirectional Weight'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConfigureIpeLumaINTEL", + "class" : "@exclude", + "opcode" : 5793, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Luma Intra Partition Mask'" }, + { "kind" : "IdRef", "name" : "'Intra Neighbour Availabilty'" }, + { "kind" : "IdRef", "name" : "'Left Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Left Corner Luma Pixel'" }, + { "kind" : "IdRef", "name" : "'Upper Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Right Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL", + "class" : "@exclude", + "opcode" : 5794, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Luma Intra Partition Mask'" }, + { "kind" : "IdRef", "name" : "'Intra Neighbour Availabilty'" }, + { "kind" : "IdRef", "name" : "'Left Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Left Corner Luma Pixel'" }, + { "kind" : "IdRef", "name" : "'Upper Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Right Edge Luma Pixels'" }, + { "kind" : "IdRef", "name" : "'Left Edge Chroma Pixels'" }, + { "kind" : "IdRef", "name" : "'Upper Left Corner Chroma Pixel'" }, + { "kind" : "IdRef", "name" : "'Upper Edge Chroma Pixels'" }, + { "kind" : "IdRef", "name" : "'Sad Adjustment'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationChromaINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetMotionVectorMaskINTEL", + "class" : "@exclude", + "opcode" : 5795, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Skip Block Partition Type'" }, + { "kind" : "IdRef", "name" : "'Direction'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConvertToMcePayloadINTEL", + "class" : "@exclude", + "opcode" : 5796, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL", + "class" : "@exclude", + "opcode" : 5797, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Shape Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL", + "class" : "@exclude", + "opcode" : 5798, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Luma Mode Penalty'" }, + { "kind" : "IdRef", "name" : "'Luma Packed Neighbor Modes'" }, + { "kind" : "IdRef", "name" : "'Luma Packed Non Dc Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL", + "class" : "@exclude", + "opcode" : 5799, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Chroma Mode Base Penalty'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationChromaINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetBilinearFilterEnableINTEL", + "class" : "@exclude", + "opcode" : 5800, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL", + "class" : "@exclude", + "opcode" : 5801, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packed Sad Coefficients'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL", + "class" : "@exclude", + "opcode" : 5802, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Block Based Skip Type'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateIpeINTEL", + "class" : "@exclude", + "opcode" : 5803, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL", + "class" : "@exclude", + "opcode" : 5804, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL", + "class" : "@exclude", + "opcode" : 5805, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Fwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Bwd Ref Image'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL", + "class" : "@exclude", + "opcode" : 5806, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL", + "class" : "@exclude", + "opcode" : 5807, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Src Image'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Ids'" }, + { "kind" : "IdRef", "name" : "'Packed Reference Field Polarities'" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicConvertToMceResultINTEL", + "class" : "@exclude", + "opcode" : 5808, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetIpeLumaShapeINTEL", + "class" : "@exclude", + "opcode" : 5809, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL", + "class" : "@exclude", + "opcode" : 5810, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL", + "class" : "@exclude", + "opcode" : 5811, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL", + "class" : "@exclude", + "opcode" : 5812, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetIpeChromaModeINTEL", + "class" : "@exclude", + "opcode" : 5813, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationChromaINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL", + "class" : "@exclude", + "opcode" : 5814, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL", + "class" : "@exclude", + "opcode" : 5815, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL", "SubgroupAvcMotionEstimationIntraINTEL" ], + "version" : "None" + }, + { + "opname" : "OpSubgroupAvcSicGetInterRawSadsINTEL", + "class" : "@exclude", + "opcode" : 5816, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Payload'" } + ], + "capabilities" : [ "SubgroupAvcMotionEstimationINTEL" ], + "version" : "None" + }, + { + "opname" : "OpLoopControlINTEL", + "class" : "Reserved", + "opcode" : 5887, + "operands" : [ + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Loop Control Parameters'" } + ], + "capabilities" : [ "UnstructuredLoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_unstructured_loop_controls" ], + "version" : "None" + }, + { + "opname" : "OpReadPipeBlockingINTEL", + "class" : "Pipe", + "opcode" : 5946, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "BlockingPipesINTEL" ], + "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "version" : "None" + }, + { + "opname" : "OpWritePipeBlockingINTEL", + "class" : "Pipe", + "opcode" : 5947, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Packet Size'" }, + { "kind" : "IdRef", "name" : "'Packet Alignment'" } + ], + "capabilities" : [ "BlockingPipesINTEL" ], + "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "version" : "None" + }, + { + "opname" : "OpFPGARegINTEL", + "class" : "Reserved", + "opcode" : 5949, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Result'" }, + { "kind" : "IdRef", "name" : "'Input'" } + ], + "capabilities" : [ "FPGARegINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_reg" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetRayTMinKHR", + "class" : "Reserved", + "opcode" : 6016, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetRayFlagsKHR", + "class" : "Reserved", + "opcode" : 6017, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionTKHR", + "class" : "Reserved", + "opcode" : 6018, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionInstanceCustomIndexKHR", + "class" : "Reserved", + "opcode" : 6019, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionInstanceIdKHR", + "class" : "Reserved", + "opcode" : 6020, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR", + "class" : "Reserved", + "opcode" : 6021, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionGeometryIndexKHR", + "class" : "Reserved", + "opcode" : 6022, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionPrimitiveIndexKHR", + "class" : "Reserved", + "opcode" : 6023, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionBarycentricsKHR", + "class" : "Reserved", + "opcode" : 6024, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionFrontFaceKHR", + "class" : "Reserved", + "opcode" : 6025, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR", + "class" : "Reserved", + "opcode" : 6026, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionObjectRayDirectionKHR", + "class" : "Reserved", + "opcode" : 6027, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionObjectRayOriginKHR", + "class" : "Reserved", + "opcode" : 6028, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetWorldRayDirectionKHR", + "class" : "Reserved", + "opcode" : 6029, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetWorldRayOriginKHR", + "class" : "Reserved", + "opcode" : 6030, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionObjectToWorldKHR", + "class" : "Reserved", + "opcode" : 6031, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpRayQueryGetIntersectionWorldToObjectKHR", + "class" : "Reserved", + "opcode" : 6032, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { + "kind" : "IdRef", + "name" : "'RayQuery'" + }, + { + "kind" : "IdRef", + "name" : "'Intersection'" + } + ], + "capabilities" : [ "RayQueryProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "opname" : "OpAtomicFAddEXT", + "class" : "Atomic", + "opcode" : 6035, + "operands" : [ + { "kind" : "IdResultType" }, + { "kind" : "IdResult" }, + { "kind" : "IdRef", "name" : "'Pointer'" }, + { "kind" : "IdScope", "name" : "'Memory'" }, + { "kind" : "IdMemorySemantics", "name" : "'Semantics'" }, + { "kind" : "IdRef", "name" : "'Value'" } + ], + "capabilities" : [ "AtomicFloat32AddEXT", "AtomicFloat64AddEXT" ], + "extensions" : [ "SPV_EXT_shader_atomic_float_add" ], + "version" : "None" + } + ], + "operand_kinds" : [ + { + "category" : "BitEnum", + "kind" : "ImageOperands", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Bias", + "value" : "0x0001", + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Lod", + "value" : "0x0002", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Grad", + "value" : "0x0004", + "parameters" : [ + { "kind" : "IdRef" }, + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffset", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Offset", + "value" : "0x0010", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "ConstOffsets", + "value" : "0x0020", + "capabilities" : [ "ImageGatherExtended" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "Sample", + "value" : "0x0040", + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "MinLod", + "value" : "0x0080", + "capabilities" : [ "MinLod" ], + "parameters" : [ + { "kind" : "IdRef" } + ] + }, + { + "enumerant" : "MakeTexelAvailable", + "value" : "0x0100", + "capabilities" : [ "VulkanMemoryModel" ], + "parameters" : [ + { "kind" : "IdScope" } + ], + "version" : "1.5" + }, + { + "enumerant" : "MakeTexelAvailableKHR", + "value" : "0x0100", + "capabilities" : [ "VulkanMemoryModel" ], + "parameters" : [ + { "kind" : "IdScope" } + ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "MakeTexelVisible", + "value" : "0x0200", + "capabilities" : [ "VulkanMemoryModel" ], + "parameters" : [ + { "kind" : "IdScope" } + ], + "version" : "1.5" + }, + { + "enumerant" : "MakeTexelVisibleKHR", + "value" : "0x0200", + "capabilities" : [ "VulkanMemoryModel" ], + "parameters" : [ + { "kind" : "IdScope" } + ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "NonPrivateTexel", + "value" : "0x0400", + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "NonPrivateTexelKHR", + "value" : "0x0400", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "VolatileTexel", + "value" : "0x0800", + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "VolatileTexelKHR", + "value" : "0x0800", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "SignExtend", + "value" : "0x1000", + "version" : "1.4" + }, + { + "enumerant" : "ZeroExtend", + "value" : "0x2000", + "version" : "1.4" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FPFastMathMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "NotNaN", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NotInf", + "value" : "0x0002", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NSZ", + "value" : "0x0004", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AllowRecip", + "value" : "0x0008", + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Fast", + "value" : "0x0010", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "SelectionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Flatten", + "value" : "0x0001" + }, + { + "enumerant" : "DontFlatten", + "value" : "0x0002" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "LoopControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Unroll", + "value" : "0x0001" + }, + { + "enumerant" : "DontUnroll", + "value" : "0x0002" + }, + { + "enumerant" : "DependencyInfinite", + "value" : "0x0004", + "version" : "1.1" + }, + { + "enumerant" : "DependencyLength", + "value" : "0x0008", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.1" + }, + { + "enumerant" : "MinIterations", + "value" : "0x0010", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "MaxIterations", + "value" : "0x0020", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "IterationMultiple", + "value" : "0x0040", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "PeelCount", + "value" : "0x0080", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "PartialCount", + "value" : "0x0100", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "version" : "1.4" + }, + { + "enumerant" : "InitiationIntervalINTEL", + "value" : "0x10000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "MaxConcurrencyINTEL", + "value" : "0x20000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "DependencyArrayINTEL", + "value" : "0x40000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "PipelineEnableINTEL", + "value" : "0x80000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "LoopCoalesceINTEL", + "value" : "0x100000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "MaxInterleavingINTEL", + "value" : "0x200000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "SpeculatedIterationsINTEL", + "value" : "0x400000", + "parameters" : [ + { "kind" : "LiteralInteger" } + ], + "capabilities" : [ "FPGALoopControlsINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FunctionControl", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Inline", + "value" : "0x0001" + }, + { + "enumerant" : "DontInline", + "value" : "0x0002" + }, + { + "enumerant" : "Pure", + "value" : "0x0004" + }, + { + "enumerant" : "Const", + "value" : "0x0008" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemorySemantics", + "enumerants" : [ + { + "enumerant" : "Relaxed", + "value" : "0x0000" + }, + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Acquire", + "value" : "0x0002" + }, + { + "enumerant" : "Release", + "value" : "0x0004" + }, + { + "enumerant" : "AcquireRelease", + "value" : "0x0008" + }, + { + "enumerant" : "SequentiallyConsistent", + "value" : "0x0010" + }, + { + "enumerant" : "UniformMemory", + "value" : "0x0040", + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupMemory", + "value" : "0x0080" + }, + { + "enumerant" : "WorkgroupMemory", + "value" : "0x0100" + }, + { + "enumerant" : "CrossWorkgroupMemory", + "value" : "0x0200" + }, + { + "enumerant" : "AtomicCounterMemory", + "value" : "0x0400", + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "ImageMemory", + "value" : "0x0800" + }, + { + "enumerant" : "OutputMemory", + "value" : "0x1000", + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "OutputMemoryKHR", + "value" : "0x1000", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "MakeAvailable", + "value" : "0x2000", + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "MakeAvailableKHR", + "value" : "0x2000", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "MakeVisible", + "value" : "0x4000", + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "MakeVisibleKHR", + "value" : "0x4000", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "Volatile", + "value" : "0x8000", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "MemoryAccess", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "Volatile", + "value" : "0x0001" + }, + { + "enumerant" : "Aligned", + "value" : "0x0002", + "parameters" : [ + { "kind" : "LiteralInteger" } + ] + }, + { + "enumerant" : "Nontemporal", + "value" : "0x0004" + }, + { + "enumerant" : "MakePointerAvailable", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdScope" } + ], + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "MakePointerAvailableKHR", + "value" : "0x0008", + "parameters" : [ + { "kind" : "IdScope" } + ], + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "MakePointerVisible", + "value" : "0x0010", + "parameters" : [ + { "kind" : "IdScope" } + ], + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "MakePointerVisibleKHR", + "value" : "0x0010", + "parameters" : [ + { "kind" : "IdScope" } + ], + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "NonPrivatePointer", + "value" : "0x0020", + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "NonPrivatePointerKHR", + "value" : "0x0020", + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "KernelProfilingInfo", + "enumerants" : [ + { + "enumerant" : "None", + "value" : "0x0000" + }, + { + "enumerant" : "CmdExecTime", + "value" : "0x0001", + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "BitEnum", + "kind" : "RayFlags", + "enumerants" : [ + { + "enumerant" : "NoneKHR", + "value" : "0x0000", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "OpaqueKHR", + "value" : "0x0001", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "NoOpaqueKHR", + "value" : "0x0002", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "TerminateOnFirstHitKHR", + "value" : "0x0004", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "SkipClosestHitShaderKHR", + "value" : "0x0008", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "CullBackFacingTrianglesKHR", + "value" : "0x0010", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "CullFrontFacingTrianglesKHR", + "value" : "0x0020", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "CullOpaqueKHR", + "value" : "0x0040", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "CullNoOpaqueKHR", + "value" : "0x0080", + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "SkipTrianglesKHR", + "value" : "0x0100", + "capabilities" : [ "RayTraversalPrimitiveCullingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "SkipAABBsKHR", + "value" : "0x0200", + "capabilities" : [ "RayTraversalPrimitiveCullingProvisionalKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "BitEnum", + "kind" : "FragmentShadingRate", + "enumerants" : [ + { + "enumerant" : "Vertical2Pixels", + "value" : "0x0001", + "capabilities" : [ "FragmentShadingRateKHR" ], + "version" : "None" + }, + { + "enumerant" : "Vertical4Pixels", + "value" : "0x0002", + "capabilities" : [ "FragmentShadingRateKHR" ], + "version" : "None" + }, + { + "enumerant" : "Horizontal2Pixels", + "value" : "0x0004", + "capabilities" : [ "FragmentShadingRateKHR" ], + "version" : "None" + }, + { + "enumerant" : "Horizontal4Pixels", + "value" : "0x0008", + "capabilities" : [ "FragmentShadingRateKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SourceLanguage", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "ESSL", + "value" : 1 + }, + { + "enumerant" : "GLSL", + "value" : 2 + }, + { + "enumerant" : "OpenCL_C", + "value" : 3 + }, + { + "enumerant" : "OpenCL_CPP", + "value" : 4 + }, + { + "enumerant" : "HLSL", + "value" : 5 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionModel", + "enumerants" : [ + { + "enumerant" : "Vertex", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TessellationControl", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessellationEvaluation", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Geometry", + "value" : 3, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Fragment", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLCompute", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Kernel", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "TaskNV", + "value" : 5267, + "capabilities" : [ "MeshShadingNV" ], + "version" : "None" + }, + { + "enumerant" : "MeshNV", + "value" : 5268, + "capabilities" : [ "MeshShadingNV" ], + "version" : "None" + }, + { + "enumerant" : "RayGenerationNV", + "value" : 5313, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayGenerationKHR", + "value" : 5313, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "IntersectionNV", + "value" : 5314, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "IntersectionKHR", + "value" : 5314, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "AnyHitNV", + "value" : 5315, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "AnyHitKHR", + "value" : 5315, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "ClosestHitNV", + "value" : 5316, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "ClosestHitKHR", + "value" : 5316, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "MissNV", + "value" : 5317, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "MissKHR", + "value" : 5317, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "CallableNV", + "value" : 5318, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "CallableKHR", + "value" : 5318, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AddressingModel", + "enumerants" : [ + { + "enumerant" : "Logical", + "value" : 0 + }, + { + "enumerant" : "Physical32", + "value" : 1, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Physical64", + "value" : 2, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "PhysicalStorageBuffer64", + "value" : 5348, + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "version" : "1.5" + }, + { + "enumerant" : "PhysicalStorageBuffer64EXT", + "value" : 5348, + "extensions" : [ "SPV_EXT_physical_storage_buffer" ], + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "version" : "1.5" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "MemoryModel", + "enumerants" : [ + { + "enumerant" : "Simple", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSL450", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OpenCL", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Vulkan", + "value" : 3, + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "VulkanKHR", + "value" : 3, + "capabilities" : [ "VulkanMemoryModel" ], + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ExecutionMode", + "enumerants" : [ + { + "enumerant" : "Invocations", + "value" : 0, + "capabilities" : [ "Geometry" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Number of <>'" } + ] + }, + { + "enumerant" : "SpacingEqual", + "value" : 1, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalEven", + "value" : 2, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "SpacingFractionalOdd", + "value" : 3, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCw", + "value" : 4, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "VertexOrderCcw", + "value" : 5, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PixelCenterInteger", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginUpperLeft", + "value" : 7, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "OriginLowerLeft", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "EarlyFragmentTests", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointMode", + "value" : 10, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Xfb", + "value" : 11, + "capabilities" : [ "TransformFeedback" ] + }, + { + "enumerant" : "DepthReplacing", + "value" : 12, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthGreater", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthLess", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DepthUnchanged", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "LocalSize", + "value" : 17, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "LocalSizeHint", + "value" : 18, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'x size'" }, + { "kind" : "LiteralInteger", "name" : "'y size'" }, + { "kind" : "LiteralInteger", "name" : "'z size'" } + ] + }, + { + "enumerant" : "InputPoints", + "value" : 19, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLines", + "value" : 20, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "InputLinesAdjacency", + "value" : 21, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Triangles", + "value" : 22, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "InputTrianglesAdjacency", + "value" : 23, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "Quads", + "value" : 24, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Isolines", + "value" : 25, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "OutputVertices", + "value" : 26, + "capabilities" : [ "Geometry", "Tessellation", "MeshShadingNV" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vertex count'" } + ] + }, + { + "enumerant" : "OutputPoints", + "value" : 27, + "capabilities" : [ "Geometry", "MeshShadingNV" ] + }, + { + "enumerant" : "OutputLineStrip", + "value" : 28, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "OutputTriangleStrip", + "value" : 29, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "VecTypeHint", + "value" : 30, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Vector type'" } + ] + }, + { + "enumerant" : "ContractionOff", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Initializer", + "value" : 33, + "capabilities" : [ "Kernel" ], + "version" : "1.1" + }, + { + "enumerant" : "Finalizer", + "value" : 34, + "capabilities" : [ "Kernel" ], + "version" : "1.1" + }, + { + "enumerant" : "SubgroupSize", + "value" : 35, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroup Size'" } + ], + "version" : "1.1" + }, + { + "enumerant" : "SubgroupsPerWorkgroup", + "value" : 36, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Subgroups Per Workgroup'" } + ], + "version" : "1.1" + }, + { + "enumerant" : "SubgroupsPerWorkgroupId", + "value" : 37, + "capabilities" : [ "SubgroupDispatch" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Subgroups Per Workgroup'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "LocalSizeId", + "value" : 38, + "parameters" : [ + { "kind" : "IdRef", "name" : "'x size'" }, + { "kind" : "IdRef", "name" : "'y size'" }, + { "kind" : "IdRef", "name" : "'z size'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "LocalSizeHintId", + "value" : 39, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Local Size Hint'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "PostDepthCoverage", + "value" : 4446, + "capabilities" : [ "SampleMaskPostDepthCoverage" ], + "extensions" : [ "SPV_KHR_post_depth_coverage" ], + "version" : "None" + }, + { + "enumerant" : "DenormPreserve", + "value" : 4459, + "capabilities" : [ "DenormPreserve" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "DenormFlushToZero", + "value" : 4460, + "capabilities" : [ "DenormFlushToZero" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "SignedZeroInfNanPreserve", + "value" : 4461, + "capabilities" : [ "SignedZeroInfNanPreserve" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "RoundingModeRTE", + "value" : 4462, + "capabilities" : [ "RoundingModeRTE" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "RoundingModeRTZ", + "value" : 4463, + "capabilities" : [ "RoundingModeRTZ" ], + "extensions" : [ "SPV_KHR_float_controls" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Target Width'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "StencilRefReplacingEXT", + "value" : 5027, + "capabilities" : [ "StencilExportEXT" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ], + "version" : "None" + }, + { + "enumerant" : "OutputLinesNV", + "value" : 5269, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "OutputPrimitivesNV", + "value" : 5270, + "capabilities" : [ "MeshShadingNV" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Primitive count'" } + ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "DerivativeGroupQuadsNV", + "value" : 5289, + "capabilities" : [ "ComputeDerivativeGroupQuadsNV" ], + "extensions" : [ "SPV_NV_compute_shader_derivatives" ], + "version" : "None" + }, + { + "enumerant" : "DerivativeGroupLinearNV", + "value" : 5290, + "capabilities" : [ "ComputeDerivativeGroupLinearNV" ], + "extensions" : [ "SPV_NV_compute_shader_derivatives" ], + "version" : "None" + }, + { + "enumerant" : "OutputTrianglesNV", + "value" : 5298, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PixelInterlockOrderedEXT", + "value" : 5366, + "capabilities" : [ "FragmentShaderPixelInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "PixelInterlockUnorderedEXT", + "value" : 5367, + "capabilities" : [ "FragmentShaderPixelInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "SampleInterlockOrderedEXT", + "value" : 5368, + "capabilities" : [ "FragmentShaderSampleInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "SampleInterlockUnorderedEXT", + "value" : 5369, + "capabilities" : [ "FragmentShaderSampleInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "ShadingRateInterlockOrderedEXT", + "value" : 5370, + "capabilities" : [ "FragmentShaderShadingRateInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "ShadingRateInterlockUnorderedEXT", + "value" : 5371, + "capabilities" : [ "FragmentShaderShadingRateInterlockEXT" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "MaxWorkgroupSizeINTEL", + "value" : 5893, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'max_x_size'" }, + { "kind" : "LiteralInteger", "name" : "'max_y_size'" }, + { "kind" : "LiteralInteger", "name" : "'max_z_size'" } + ], + "capabilities" : [ "KernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MaxWorkDimINTEL", + "value" : 5894, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'max_dimensions'" } + ], + "capabilities" : [ "KernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "NoGlobalOffsetINTEL", + "value" : 5895, + "capabilities" : [ "KernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "NumSIMDWorkitemsINTEL", + "value" : 5896, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'vector_width'" } + ], + "capabilities" : [ "FPGAKernelAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "StorageClass", + "enumerants" : [ + { + "enumerant" : "UniformConstant", + "value" : 0 + }, + { + "enumerant" : "Input", + "value" : 1 + }, + { + "enumerant" : "Uniform", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Output", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Workgroup", + "value" : 4 + }, + { + "enumerant" : "CrossWorkgroup", + "value" : 5 + }, + { + "enumerant" : "Private", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Function", + "value" : 7 + }, + { + "enumerant" : "Generic", + "value" : 8, + "capabilities" : [ "GenericPointer" ] + }, + { + "enumerant" : "PushConstant", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "AtomicCounter", + "value" : 10, + "capabilities" : [ "AtomicStorage" ] + }, + { + "enumerant" : "Image", + "value" : 11 + }, + { + "enumerant" : "StorageBuffer", + "value" : 12, + "extensions" : [ + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers" + ], + "capabilities" : [ "Shader" ], + "version" : "1.3" + }, + { + "enumerant" : "CallableDataNV", + "value" : 5328, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "CallableDataKHR", + "value" : 5328, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "IncomingCallableDataNV", + "value" : 5329, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "IncomingCallableDataKHR", + "value" : 5329, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayPayloadNV", + "value" : 5338, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayPayloadKHR", + "value" : 5338, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "HitAttributeNV", + "value" : 5339, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "HitAttributeKHR", + "value" : 5339, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "IncomingRayPayloadNV", + "value" : 5342, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "IncomingRayPayloadKHR", + "value" : 5342, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "ShaderRecordBufferNV", + "value" : 5343, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "ShaderRecordBufferKHR", + "value" : 5343, + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "PhysicalStorageBuffer", + "value" : 5349, + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "version" : "1.5" + }, + { + "enumerant" : "PhysicalStorageBufferEXT", + "value" : 5349, + "extensions" : [ "SPV_EXT_physical_storage_buffer" ], + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "version" : "1.5" + }, + { + "enumerant" : "CodeSectionINTEL", + "value" : 5605, + "extensions" : [ "SPV_INTEL_function_pointers" ], + "capabilities" : [ "FunctionPointersINTEL" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Dim", + "enumerants" : [ + { + "enumerant" : "1D", + "value" : 0, + "capabilities" : [ "Sampled1D", "Image1D" ] + }, + { + "enumerant" : "2D", + "value" : 1, + "capabilities" : [ "Shader", "Kernel", "ImageMSArray" ] + }, + { + "enumerant" : "3D", + "value" : 2 + }, + { + "enumerant" : "Cube", + "value" : 3, + "capabilities" : [ "Shader", "ImageCubeArray" ] + }, + { + "enumerant" : "Rect", + "value" : 4, + "capabilities" : [ "SampledRect", "ImageRect" ] + }, + { + "enumerant" : "Buffer", + "value" : 5, + "capabilities" : [ "SampledBuffer", "ImageBuffer" ] + }, + { + "enumerant" : "SubpassData", + "value" : 6, + "capabilities" : [ "InputAttachment" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerAddressingMode", + "enumerants" : [ + { + "enumerant" : "None", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ClampToEdge", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Clamp", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Repeat", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RepeatMirrored", + "value" : 4, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "SamplerFilterMode", + "enumerants" : [ + { + "enumerant" : "Nearest", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Linear", + "value" : 1, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageFormat", + "enumerants" : [ + { + "enumerant" : "Unknown", + "value" : 0 + }, + { + "enumerant" : "Rgba32f", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16f", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32f", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8", + "value" : 4, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8Snorm", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32f", + "value" : 6, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16f", + "value" : 7, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R11fG11fB10f", + "value" : 8, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16f", + "value" : 9, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16", + "value" : 10, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgb10A2", + "value" : 11, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16", + "value" : 12, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8", + "value" : 13, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16", + "value" : 14, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8", + "value" : 15, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba16Snorm", + "value" : 16, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16Snorm", + "value" : 17, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8Snorm", + "value" : 18, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16Snorm", + "value" : 19, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8Snorm", + "value" : 20, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32i", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16i", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8i", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32i", + "value" : 24, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rg32i", + "value" : 25, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16i", + "value" : 26, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8i", + "value" : 27, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16i", + "value" : 28, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8i", + "value" : 29, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rgba32ui", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba16ui", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgba8ui", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "R32ui", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Rgb10a2ui", + "value" : 34, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg32ui", + "value" : 35, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg16ui", + "value" : 36, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "Rg8ui", + "value" : 37, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R16ui", + "value" : 38, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R8ui", + "value" : 39, + "capabilities" : [ "StorageImageExtendedFormats" ] + }, + { + "enumerant" : "R64ui", + "value" : 40, + "capabilities" : [ "Int64ImageEXT" ] + }, + { + "enumerant" : "R64i", + "value" : 41, + "capabilities" : [ "Int64ImageEXT" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelOrder", + "enumerants" : [ + { + "enumerant" : "R", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "A", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RG", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RA", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGB", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBA", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BGRA", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ARGB", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Intensity", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Luminance", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Rx", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGx", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "RGBx", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Depth", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "DepthStencil", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGB", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBx", + "value" : 16, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sRGBA", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "sBGRA", + "value" : 18, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ABGR", + "value" : 19, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "ImageChannelDataType", + "enumerants" : [ + { + "enumerant" : "SnormInt8", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SnormInt16", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt8", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt16", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort565", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormShort555", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt8", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt16", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SignedInt32", + "value" : 9, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt8", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt16", + "value" : 11, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnsignedInt32", + "value" : 12, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "HalfFloat", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float", + "value" : 14, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt24", + "value" : 15, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "UnormInt101010_2", + "value" : 16, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FPRoundingMode", + "enumerants" : [ + { + "enumerant" : "RTE", + "value" : 0 + }, + { + "enumerant" : "RTZ", + "value" : 1 + }, + { + "enumerant" : "RTP", + "value" : 2 + }, + { + "enumerant" : "RTN", + "value" : 3 + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "LinkageType", + "enumerants" : [ + { + "enumerant" : "Export", + "value" : 0, + "capabilities" : [ "Linkage" ] + }, + { + "enumerant" : "Import", + "value" : 1, + "capabilities" : [ "Linkage" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "AccessQualifier", + "enumerants" : [ + { + "enumerant" : "ReadOnly", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WriteOnly", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ReadWrite", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "FunctionParameterAttribute", + "enumerants" : [ + { + "enumerant" : "Zext", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sext", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ByVal", + "value" : 2, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Sret", + "value" : 3, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoAlias", + "value" : 4, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoCapture", + "value" : 5, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoWrite", + "value" : 6, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NoReadWrite", + "value" : 7, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Decoration", + "enumerants" : [ + { + "enumerant" : "RelaxedPrecision", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SpecId", + "value" : 1, + "capabilities" : [ "Shader", "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Specialization Constant ID'" } + ] + }, + { + "enumerant" : "Block", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "BufferBlock", + "value" : 3, + "capabilities" : [ "Shader" ], + "lastVersion" : "1.3" + }, + { + "enumerant" : "RowMajor", + "value" : 4, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ColMajor", + "value" : 5, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "ArrayStride", + "value" : 6, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Array Stride'" } + ] + }, + { + "enumerant" : "MatrixStride", + "value" : 7, + "capabilities" : [ "Matrix" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Matrix Stride'" } + ] + }, + { + "enumerant" : "GLSLShared", + "value" : 8, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GLSLPacked", + "value" : 9, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CPacked", + "value" : 10, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "BuiltIn", + "value" : 11, + "parameters" : [ + { "kind" : "BuiltIn" } + ] + }, + { + "enumerant" : "NoPerspective", + "value" : 13, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Flat", + "value" : 14, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Patch", + "value" : 15, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "Centroid", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sample", + "value" : 17, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "Invariant", + "value" : 18, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Restrict", + "value" : 19 + }, + { + "enumerant" : "Aliased", + "value" : 20 + }, + { + "enumerant" : "Volatile", + "value" : 21 + }, + { + "enumerant" : "Constant", + "value" : 22, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Coherent", + "value" : 23 + }, + { + "enumerant" : "NonWritable", + "value" : 24 + }, + { + "enumerant" : "NonReadable", + "value" : 25 + }, + { + "enumerant" : "Uniform", + "value" : 26, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "UniformId", + "value" : 27, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "IdScope", "name" : "'Execution'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "SaturatedConversion", + "value" : 28, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Stream", + "value" : 29, + "capabilities" : [ "GeometryStreams" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Stream Number'" } + ] + }, + { + "enumerant" : "Location", + "value" : 30, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Location'" } + ] + }, + { + "enumerant" : "Component", + "value" : 31, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Component'" } + ] + }, + { + "enumerant" : "Index", + "value" : 32, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Index'" } + ] + }, + { + "enumerant" : "Binding", + "value" : 33, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Binding Point'" } + ] + }, + { + "enumerant" : "DescriptorSet", + "value" : 34, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Descriptor Set'" } + ] + }, + { + "enumerant" : "Offset", + "value" : 35, + "capabilities" : [ "Shader" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Byte Offset'" } + ] + }, + { + "enumerant" : "XfbBuffer", + "value" : 36, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Buffer Number'" } + ] + }, + { + "enumerant" : "XfbStride", + "value" : 37, + "capabilities" : [ "TransformFeedback" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'XFB Stride'" } + ] + }, + { + "enumerant" : "FuncParamAttr", + "value" : 38, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FunctionParameterAttribute", "name" : "'Function Parameter Attribute'" } + ] + }, + { + "enumerant" : "FPRoundingMode", + "value" : 39, + "parameters" : [ + { "kind" : "FPRoundingMode", "name" : "'Floating-Point Rounding Mode'" } + ] + }, + { + "enumerant" : "FPFastMathMode", + "value" : 40, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "FPFastMathMode", "name" : "'Fast-Math Mode'" } + ] + }, + { + "enumerant" : "LinkageAttributes", + "value" : 41, + "capabilities" : [ "Linkage" ], + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Name'" }, + { "kind" : "LinkageType", "name" : "'Linkage Type'" } + ] + }, + { + "enumerant" : "NoContraction", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InputAttachmentIndex", + "value" : 43, + "capabilities" : [ "InputAttachment" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Attachment Index'" } + ] + }, + { + "enumerant" : "Alignment", + "value" : 44, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Alignment'" } + ] + }, + { + "enumerant" : "MaxByteOffset", + "value" : 45, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Max Byte Offset'" } + ], + "version" : "1.1" + }, + { + "enumerant" : "AlignmentId", + "value" : 46, + "capabilities" : [ "Kernel" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Alignment'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "MaxByteOffsetId", + "value" : 47, + "capabilities" : [ "Addresses" ], + "parameters" : [ + { "kind" : "IdRef", "name" : "'Max Byte Offset'" } + ], + "version" : "1.2" + }, + { + "enumerant" : "NoSignedWrap", + "value" : 4469, + "extensions" : [ "SPV_KHR_no_integer_wrap_decoration" ], + "version" : "1.4" + }, + { + "enumerant" : "NoUnsignedWrap", + "value" : 4470, + "extensions" : [ "SPV_KHR_no_integer_wrap_decoration" ], + "version" : "1.4" + }, + { + "enumerant" : "ExplicitInterpAMD", + "value" : 4999, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "OverrideCoverageNV", + "value" : 5248, + "capabilities" : [ "SampleMaskOverrideCoverageNV" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ], + "version" : "None" + }, + { + "enumerant" : "PassthroughNV", + "value" : 5250, + "capabilities" : [ "GeometryShaderPassthroughNV" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ], + "version" : "None" + }, + { + "enumerant" : "ViewportRelativeNV", + "value" : 5252, + "capabilities" : [ "ShaderViewportMaskNV" ], + "version" : "None" + }, + { + "enumerant" : "SecondaryViewportRelativeNV", + "value" : 5256, + "capabilities" : [ "ShaderStereoViewNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ], + "version" : "None", + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Offset'" } + ] + }, + { + "enumerant" : "PerPrimitiveNV", + "value" : 5271, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PerViewNV", + "value" : 5272, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PerTaskNV", + "value" : 5273, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PerVertexNV", + "value" : 5285, + "capabilities" : [ "FragmentBarycentricNV" ], + "extensions" : [ "SPV_NV_fragment_shader_barycentric" ], + "version" : "None" + }, + { + "enumerant" : "NonUniform", + "value" : 5300, + "capabilities" : [ "ShaderNonUniform" ], + "version" : "1.5" + }, + { + "enumerant" : "NonUniformEXT", + "value" : 5300, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "RestrictPointer", + "value" : 5355, + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "RestrictPointerEXT", + "value" : 5355, + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "AliasedPointer", + "value" : 5356, + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "AliasedPointerEXT", + "value" : 5356, + "capabilities" : [ "PhysicalStorageBufferAddresses" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "ReferencedIndirectlyINTEL", + "value" : 5602, + "capabilities" : [ "IndirectReferencesINTEL" ], + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "enumerant" : "CounterBuffer", + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "HlslCounterBufferGOOGLE", + "value" : 5634, + "parameters" : [ + { "kind" : "IdRef", "name" : "'Counter Buffer'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "None" + }, + { + "enumerant" : "UserSemantic", + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "version" : "1.4" + }, + { + "enumerant" : "HlslSemanticGOOGLE", + "value" : 5635, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Semantic'" } + ], + "extensions" : [ "SPV_GOOGLE_hlsl_functionality1" ], + "version" : "None" + }, + { + "enumerant" : "UserTypeGOOGLE", + "value" : 5636, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'User Type'" } + ], + "extensions" : [ "SPV_GOOGLE_user_type" ], + "version" : "None" + }, + { + "enumerant" : "RegisterINTEL", + "value" : 5825, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MemoryINTEL", + "value" : 5826, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Memory Type'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "NumbanksINTEL", + "value" : 5827, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Banks'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "BankwidthINTEL", + "value" : 5828, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Bank Width'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MaxPrivateCopiesINTEL", + "value" : 5829, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Maximum Copies'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "SinglepumpINTEL", + "value" : 5830, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "DoublepumpINTEL", + "value" : 5831, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MaxReplicatesINTEL", + "value" : 5832, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Maximum Replicates'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "SimpleDualPortINTEL", + "value" : 5833, + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "MergeINTEL", + "value" : 5834, + "parameters" : [ + { "kind" : "LiteralString", "name" : "'Merge Key'" }, + { "kind" : "LiteralString", "name" : "'Merge Type'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "BankBitsINTEL", + "value" : 5835, + "parameters" : [ + { "kind" : "LiteralInteger", "quantifier" : "*", "name" : "'Bank Bits'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "ForcePow2DepthINTEL", + "value" : 5836, + "parameters" : [ + { "kind" : "LiteralInteger", "name" : "'Force Key'" } + ], + "capabilities" : [ "FPGAMemoryAttributesINTEL" ], + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "BuiltIn", + "enumerants" : [ + { + "enumerant" : "Position", + "value" : 0, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointSize", + "value" : 1, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 3, + "capabilities" : [ "ClipDistance" ] + }, + { + "enumerant" : "CullDistance", + "value" : 4, + "capabilities" : [ "CullDistance" ] + }, + { + "enumerant" : "VertexId", + "value" : 5, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceId", + "value" : 6, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PrimitiveId", + "value" : 7, + "capabilities" : [ "Geometry", "Tessellation", "RayTracingNV", "RayTracingProvisionalKHR" ] + }, + { + "enumerant" : "InvocationId", + "value" : 8, + "capabilities" : [ "Geometry", "Tessellation" ] + }, + { + "enumerant" : "Layer", + "value" : 9, + "capabilities" : [ "Geometry", "ShaderLayer", "ShaderViewportIndexLayerEXT" ] + }, + { + "enumerant" : "ViewportIndex", + "value" : 10, + "capabilities" : [ "MultiViewport", "ShaderViewportIndex", "ShaderViewportIndexLayerEXT" ] + }, + { + "enumerant" : "TessLevelOuter", + "value" : 11, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessLevelInner", + "value" : 12, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "TessCoord", + "value" : 13, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "PatchVertices", + "value" : 14, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "FragCoord", + "value" : 15, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "PointCoord", + "value" : 16, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FrontFacing", + "value" : 17, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampleId", + "value" : 18, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SamplePosition", + "value" : 19, + "capabilities" : [ "SampleRateShading" ] + }, + { + "enumerant" : "SampleMask", + "value" : 20, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "FragDepth", + "value" : 22, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "HelperInvocation", + "value" : 23, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "NumWorkgroups", + "value" : 24 + }, + { + "enumerant" : "WorkgroupSize", + "value" : 25 + }, + { + "enumerant" : "WorkgroupId", + "value" : 26 + }, + { + "enumerant" : "LocalInvocationId", + "value" : 27 + }, + { + "enumerant" : "GlobalInvocationId", + "value" : 28 + }, + { + "enumerant" : "LocalInvocationIndex", + "value" : 29 + }, + { + "enumerant" : "WorkDim", + "value" : 30, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalSize", + "value" : 31, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "EnqueuedWorkgroupSize", + "value" : 32, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalOffset", + "value" : 33, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "GlobalLinearId", + "value" : 34, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupSize", + "value" : 36, + "capabilities" : [ "Kernel", "GroupNonUniform", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "SubgroupMaxSize", + "value" : 37, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "NumSubgroups", + "value" : 38, + "capabilities" : [ "Kernel", "GroupNonUniform" ] + }, + { + "enumerant" : "NumEnqueuedSubgroups", + "value" : 39, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "SubgroupId", + "value" : 40, + "capabilities" : [ "Kernel", "GroupNonUniform" ] + }, + { + "enumerant" : "SubgroupLocalInvocationId", + "value" : 41, + "capabilities" : [ "Kernel", "GroupNonUniform", "SubgroupBallotKHR" ] + }, + { + "enumerant" : "VertexIndex", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InstanceIndex", + "value" : 43, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SubgroupEqMask", + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupGeMask", + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupGtMask", + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupLeMask", + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupLtMask", + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupEqMaskKHR", + "value" : 4416, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupGeMaskKHR", + "value" : 4417, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupGtMaskKHR", + "value" : 4418, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupLeMaskKHR", + "value" : 4419, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupLtMaskKHR", + "value" : 4420, + "capabilities" : [ "SubgroupBallotKHR", "GroupNonUniformBallot" ], + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "1.3" + }, + { + "enumerant" : "BaseVertex", + "value" : 4424, + "capabilities" : [ "DrawParameters" ], + "extensions" : [ "SPV_KHR_shader_draw_parameters" ], + "version" : "1.3" + }, + { + "enumerant" : "BaseInstance", + "value" : 4425, + "capabilities" : [ "DrawParameters" ], + "extensions" : [ "SPV_KHR_shader_draw_parameters" ], + "version" : "1.3" + }, + { + "enumerant" : "DrawIndex", + "value" : 4426, + "capabilities" : [ "DrawParameters", "MeshShadingNV" ], + "extensions" : [ "SPV_KHR_shader_draw_parameters", "SPV_NV_mesh_shader" ], + "version" : "1.3" + }, + { + "enumerant" : "PrimitiveShadingRateKHR", + "value" : 4432, + "capabilities" : [ "FragmentShadingRateKHR" ], + "extensions" : [ "SPV_KHR_fragment_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "DeviceIndex", + "value" : 4438, + "capabilities" : [ "DeviceGroup" ], + "extensions" : [ "SPV_KHR_device_group" ], + "version" : "1.3" + }, + { + "enumerant" : "ViewIndex", + "value" : 4440, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_KHR_multiview" ], + "version" : "1.3" + }, + { + "enumerant" : "ShadingRateKHR", + "value" : 4444, + "capabilities" : [ "FragmentShadingRateKHR" ], + "extensions" : [ "SPV_KHR_fragment_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNoPerspAMD", + "value" : 4992, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNoPerspCentroidAMD", + "value" : 4993, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNoPerspSampleAMD", + "value" : 4994, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordSmoothAMD", + "value" : 4995, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordSmoothCentroidAMD", + "value" : 4996, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordSmoothSampleAMD", + "value" : 4997, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordPullModelAMD", + "value" : 4998, + "extensions" : [ "SPV_AMD_shader_explicit_vertex_parameter" ], + "version" : "None" + }, + { + "enumerant" : "FragStencilRefEXT", + "value" : 5014, + "capabilities" : [ "StencilExportEXT" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ], + "version" : "None" + }, + { + "enumerant" : "ViewportMaskNV", + "value" : 5253, + "capabilities" : [ "ShaderViewportMaskNV", "MeshShadingNV" ], + "extensions" : [ "SPV_NV_viewport_array2", "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "SecondaryPositionNV", + "value" : 5257, + "capabilities" : [ "ShaderStereoViewNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ], + "version" : "None" + }, + { + "enumerant" : "SecondaryViewportMaskNV", + "value" : 5258, + "capabilities" : [ "ShaderStereoViewNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ], + "version" : "None" + }, + { + "enumerant" : "PositionPerViewNV", + "value" : 5261, + "capabilities" : [ "PerViewAttributesNV", "MeshShadingNV" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes", "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "ViewportMaskPerViewNV", + "value" : 5262, + "capabilities" : [ "PerViewAttributesNV", "MeshShadingNV" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes", "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "FullyCoveredEXT", + "value" : 5264, + "capabilities" : [ "FragmentFullyCoveredEXT" ], + "extensions" : [ "SPV_EXT_fragment_fully_covered" ], + "version" : "None" + }, + { + "enumerant" : "TaskCountNV", + "value" : 5274, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PrimitiveCountNV", + "value" : 5275, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "PrimitiveIndicesNV", + "value" : 5276, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "ClipDistancePerViewNV", + "value" : 5277, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "CullDistancePerViewNV", + "value" : 5278, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "LayerPerViewNV", + "value" : 5279, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "MeshViewCountNV", + "value" : 5280, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "MeshViewIndicesNV", + "value" : 5281, + "capabilities" : [ "MeshShadingNV" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNV", + "value" : 5286, + "capabilities" : [ "FragmentBarycentricNV" ], + "extensions" : [ "SPV_NV_fragment_shader_barycentric" ], + "version" : "None" + }, + { + "enumerant" : "BaryCoordNoPerspNV", + "value" : 5287, + "capabilities" : [ "FragmentBarycentricNV" ], + "extensions" : [ "SPV_NV_fragment_shader_barycentric" ], + "version" : "None" + }, + { + "enumerant" : "FragSizeEXT", + "value" : 5292 , + "capabilities" : [ "FragmentDensityEXT", "ShadingRateNV" ], + "extensions" : [ "SPV_EXT_fragment_invocation_density", "SPV_NV_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "FragmentSizeNV", + "value" : 5292 , + "capabilities" : [ "ShadingRateNV", "FragmentDensityEXT" ], + "extensions" : [ "SPV_NV_shading_rate", "SPV_EXT_fragment_invocation_density" ], + "version" : "None" + }, + { + "enumerant" : "FragInvocationCountEXT", + "value" : 5293, + "capabilities" : [ "FragmentDensityEXT", "ShadingRateNV" ], + "extensions" : [ "SPV_EXT_fragment_invocation_density", "SPV_NV_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "InvocationsPerPixelNV", + "value" : 5293, + "capabilities" : [ "ShadingRateNV", "FragmentDensityEXT" ], + "extensions" : [ "SPV_NV_shading_rate", "SPV_EXT_fragment_invocation_density" ], + "version" : "None" + }, + { + "enumerant" : "LaunchIdNV", + "value" : 5319, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "LaunchIdKHR", + "value" : 5319, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "LaunchSizeNV", + "value" : 5320, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "LaunchSizeKHR", + "value" : 5320, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldRayOriginNV", + "value" : 5321, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldRayOriginKHR", + "value" : 5321, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldRayDirectionNV", + "value" : 5322, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldRayDirectionKHR", + "value" : 5322, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectRayOriginNV", + "value" : 5323, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectRayOriginKHR", + "value" : 5323, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectRayDirectionNV", + "value" : 5324, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectRayDirectionKHR", + "value" : 5324, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayTminNV", + "value" : 5325, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayTminKHR", + "value" : 5325, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayTmaxNV", + "value" : 5326, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayTmaxKHR", + "value" : 5326, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "InstanceCustomIndexNV", + "value" : 5327, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "InstanceCustomIndexKHR", + "value" : 5327, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectToWorldNV", + "value" : 5330, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "ObjectToWorldKHR", + "value" : 5330, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldToObjectNV", + "value" : 5331, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WorldToObjectKHR", + "value" : 5331, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "HitTNV", + "value" : 5332, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "HitTKHR", + "value" : 5332, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "HitKindNV", + "value" : 5333, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "HitKindKHR", + "value" : 5333, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "IncomingRayFlagsNV", + "value" : 5351, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "IncomingRayFlagsKHR", + "value" : 5351, + "capabilities" : [ "RayTracingNV" , "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_NV_ray_tracing" , "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "RayGeometryIndexKHR", + "value" : 5352, + "capabilities" : [ "RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "WarpsPerSMNV", + "value" : 5374, + "capabilities" : [ "ShaderSMBuiltinsNV" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "SMCountNV", + "value" : 5375, + "capabilities" : [ "ShaderSMBuiltinsNV" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "WarpIDNV", + "value" : 5376, + "capabilities" : [ "ShaderSMBuiltinsNV" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "SMIDNV", + "value" : 5377, + "capabilities" : [ "ShaderSMBuiltinsNV" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Scope", + "enumerants" : [ + { + "enumerant" : "CrossDevice", + "value" : 0 + }, + { + "enumerant" : "Device", + "value" : 1 + }, + { + "enumerant" : "Workgroup", + "value" : 2 + }, + { + "enumerant" : "Subgroup", + "value" : 3 + }, + { + "enumerant" : "Invocation", + "value" : 4 + }, + { + "enumerant" : "QueueFamily", + "value" : 5, + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "QueueFamilyKHR", + "value" : 5, + "capabilities" : [ "VulkanMemoryModel" ], + "version" : "1.5" + }, + { + "enumerant" : "ShaderCallKHR", + "value" : 6, + "capabilities" : [ "RayTracingProvisionalKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "GroupOperation", + "enumerants" : [ + { + "enumerant" : "Reduce", + "value" : 0, + "capabilities" : [ "Kernel", "GroupNonUniformArithmetic", "GroupNonUniformBallot" ] + }, + { + "enumerant" : "InclusiveScan", + "value" : 1, + "capabilities" : [ "Kernel", "GroupNonUniformArithmetic", "GroupNonUniformBallot" ] + }, + { + "enumerant" : "ExclusiveScan", + "value" : 2, + "capabilities" : [ "Kernel", "GroupNonUniformArithmetic", "GroupNonUniformBallot" ] + }, + { + "enumerant" : "ClusteredReduce", + "value" : 3, + "capabilities" : [ "GroupNonUniformClustered" ], + "version" : "1.3" + }, + { + "enumerant" : "PartitionedReduceNV", + "value" : 6, + "capabilities" : [ "GroupNonUniformPartitionedNV" ], + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + }, + { + "enumerant" : "PartitionedInclusiveScanNV", + "value" : 7, + "capabilities" : [ "GroupNonUniformPartitionedNV" ], + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + }, + { + "enumerant" : "PartitionedExclusiveScanNV", + "value" : 8, + "capabilities" : [ "GroupNonUniformPartitionedNV" ], + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "KernelEnqueueFlags", + "enumerants" : [ + { + "enumerant" : "NoWait", + "value" : 0, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitKernel", + "value" : 1, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "WaitWorkGroup", + "value" : 2, + "capabilities" : [ "Kernel" ] + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "Capability", + "enumerants" : [ + { + "enumerant" : "Matrix", + "value" : 0 + }, + { + "enumerant" : "Shader", + "value" : 1, + "capabilities" : [ "Matrix" ] + }, + { + "enumerant" : "Geometry", + "value" : 2, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Tessellation", + "value" : 3, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Addresses", + "value" : 4 + }, + { + "enumerant" : "Linkage", + "value" : 5 + }, + { + "enumerant" : "Kernel", + "value" : 6 + }, + { + "enumerant" : "Vector16", + "value" : 7, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16Buffer", + "value" : 8, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Float16", + "value" : 9 + }, + { + "enumerant" : "Float64", + "value" : 10 + }, + { + "enumerant" : "Int64", + "value" : 11 + }, + { + "enumerant" : "Int64Atomics", + "value" : 12, + "capabilities" : [ "Int64" ] + }, + { + "enumerant" : "ImageBasic", + "value" : 13, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "ImageReadWrite", + "value" : 14, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "ImageMipmap", + "value" : 15, + "capabilities" : [ "ImageBasic" ] + }, + { + "enumerant" : "Pipes", + "value" : 17, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "Groups", + "value" : 18, + "extensions" : [ "SPV_AMD_shader_ballot" ] + }, + { + "enumerant" : "DeviceEnqueue", + "value" : 19, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "LiteralSampler", + "value" : 20, + "capabilities" : [ "Kernel" ] + }, + { + "enumerant" : "AtomicStorage", + "value" : 21, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Int16", + "value" : 22 + }, + { + "enumerant" : "TessellationPointSize", + "value" : 23, + "capabilities" : [ "Tessellation" ] + }, + { + "enumerant" : "GeometryPointSize", + "value" : 24, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "ImageGatherExtended", + "value" : 25, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageMultisample", + "value" : 27, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "UniformBufferArrayDynamicIndexing", + "value" : 28, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledImageArrayDynamicIndexing", + "value" : 29, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageBufferArrayDynamicIndexing", + "value" : 30, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageArrayDynamicIndexing", + "value" : 31, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ClipDistance", + "value" : 32, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "CullDistance", + "value" : 33, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageCubeArray", + "value" : 34, + "capabilities" : [ "SampledCubeArray" ] + }, + { + "enumerant" : "SampleRateShading", + "value" : 35, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageRect", + "value" : 36, + "capabilities" : [ "SampledRect" ] + }, + { + "enumerant" : "SampledRect", + "value" : 37, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GenericPointer", + "value" : 38, + "capabilities" : [ "Addresses" ] + }, + { + "enumerant" : "Int8", + "value" : 39 + }, + { + "enumerant" : "InputAttachment", + "value" : 40, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SparseResidency", + "value" : 41, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MinLod", + "value" : 42, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "Sampled1D", + "value" : 43 + }, + { + "enumerant" : "Image1D", + "value" : 44, + "capabilities" : [ "Sampled1D" ] + }, + { + "enumerant" : "SampledCubeArray", + "value" : 45, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "SampledBuffer", + "value" : 46 + }, + { + "enumerant" : "ImageBuffer", + "value" : 47, + "capabilities" : [ "SampledBuffer" ] + }, + { + "enumerant" : "ImageMSArray", + "value" : 48, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageExtendedFormats", + "value" : 49, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "ImageQuery", + "value" : 50, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "DerivativeControl", + "value" : 51, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "InterpolationFunction", + "value" : 52, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "TransformFeedback", + "value" : 53, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "GeometryStreams", + "value" : 54, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "StorageImageReadWithoutFormat", + "value" : 55, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "StorageImageWriteWithoutFormat", + "value" : 56, + "capabilities" : [ "Shader" ] + }, + { + "enumerant" : "MultiViewport", + "value" : 57, + "capabilities" : [ "Geometry" ] + }, + { + "enumerant" : "SubgroupDispatch", + "value" : 58, + "capabilities" : [ "DeviceEnqueue" ], + "version" : "1.1" + }, + { + "enumerant" : "NamedBarrier", + "value" : 59, + "capabilities" : [ "Kernel" ], + "version" : "1.1" + }, + { + "enumerant" : "PipeStorage", + "value" : 60, + "capabilities" : [ "Pipes" ], + "version" : "1.1" + }, + { + "enumerant" : "GroupNonUniform", + "value" : 61, + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformVote", + "value" : 62, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformArithmetic", + "value" : 63, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformBallot", + "value" : 64, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformShuffle", + "value" : 65, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformShuffleRelative", + "value" : 66, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformClustered", + "value" : 67, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "GroupNonUniformQuad", + "value" : 68, + "capabilities" : [ "GroupNonUniform" ], + "version" : "1.3" + }, + { + "enumerant" : "ShaderLayer", + "value" : 69, + "version" : "1.5" + }, + { + "enumerant" : "ShaderViewportIndex", + "value" : 70, + "version" : "1.5" + }, + { + "enumerant" : "FragmentShadingRateKHR", + "value" : 4422, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_fragment_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupBallotKHR", + "value" : 4423, + "extensions" : [ "SPV_KHR_shader_ballot" ], + "version" : "None" + }, + { + "enumerant" : "DrawParameters", + "value" : 4427, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_shader_draw_parameters" ], + "version" : "1.3" + }, + { + "enumerant" : "SubgroupVoteKHR", + "value" : 4431, + "extensions" : [ "SPV_KHR_subgroup_vote" ], + "version" : "None" + }, + { + "enumerant" : "StorageBuffer16BitAccess", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "StorageUniformBufferBlock16", + "value" : 4433, + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "UniformAndStorageBuffer16BitAccess", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "StorageUniform16", + "value" : 4434, + "capabilities" : [ + "StorageBuffer16BitAccess", + "StorageUniformBufferBlock16" + ], + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "StoragePushConstant16", + "value" : 4435, + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "StorageInputOutput16", + "value" : 4436, + "extensions" : [ "SPV_KHR_16bit_storage" ], + "version" : "1.3" + }, + { + "enumerant" : "DeviceGroup", + "value" : 4437, + "extensions" : [ "SPV_KHR_device_group" ], + "version" : "1.3" + }, + { + "enumerant" : "MultiView", + "value" : 4439, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_multiview" ], + "version" : "1.3" + }, + { + "enumerant" : "VariablePointersStorageBuffer", + "value" : 4441, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_variable_pointers" ], + "version" : "1.3" + }, + { + "enumerant" : "VariablePointers", + "value" : 4442, + "capabilities" : [ "VariablePointersStorageBuffer" ], + "extensions" : [ "SPV_KHR_variable_pointers" ], + "version" : "1.3" + }, + { + "enumerant" : "AtomicStorageOps", + "value" : 4445, + "extensions" : [ "SPV_KHR_shader_atomic_counter_ops" ], + "version" : "None" + }, + { + "enumerant" : "SampleMaskPostDepthCoverage", + "value" : 4447, + "extensions" : [ "SPV_KHR_post_depth_coverage" ], + "version" : "None" + }, + { + "enumerant" : "StorageBuffer8BitAccess", + "value" : 4448, + "extensions" : [ "SPV_KHR_8bit_storage" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformAndStorageBuffer8BitAccess", + "value" : 4449, + "capabilities" : [ "StorageBuffer8BitAccess" ], + "extensions" : [ "SPV_KHR_8bit_storage" ], + "version" : "1.5" + }, + { + "enumerant" : "StoragePushConstant8", + "value" : 4450, + "extensions" : [ "SPV_KHR_8bit_storage" ], + "version" : "1.5" + }, + { + "enumerant" : "DenormPreserve", + "value" : 4464, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "DenormFlushToZero", + "value" : 4465, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "SignedZeroInfNanPreserve", + "value" : 4466, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "RoundingModeRTE", + "value" : 4467, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "RoundingModeRTZ", + "value" : 4468, + "extensions" : [ "SPV_KHR_float_controls" ], + "version" : "1.4" + }, + { + "enumerant" : "RayQueryProvisionalKHR", + "value" : 4471, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_ray_query" ], + "version" : "None" + }, + { + "enumerant" : "RayTraversalPrimitiveCullingProvisionalKHR", + "value" : 4478, + "capabilities" : [ "RayQueryProvisionalKHR","RayTracingProvisionalKHR" ], + "extensions" : [ "SPV_KHR_ray_query","SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "Float16ImageAMD", + "value" : 5008, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_gpu_shader_half_float_fetch" ], + "version" : "None" + }, + { + "enumerant" : "ImageGatherBiasLodAMD", + "value" : 5009, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_texture_gather_bias_lod" ], + "version" : "None" + }, + { + "enumerant" : "FragmentMaskAMD", + "value" : 5010, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_fragment_mask" ], + "version" : "None" + }, + { + "enumerant" : "StencilExportEXT", + "value" : 5013, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_stencil_export" ], + "version" : "None" + }, + { + "enumerant" : "ImageReadWriteLodAMD", + "value" : 5015, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_AMD_shader_image_load_store_lod" ], + "version" : "None" + }, + { + "enumerant" : "Int64ImageEXT", + "value" : 5016, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_image_int64" ], + "version" : "None" + }, + { + "enumerant" : "ShaderClockKHR", + "value" : 5055, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_shader_clock" ], + "version" : "None" + }, + { + "enumerant" : "SampleMaskOverrideCoverageNV", + "value" : 5249, + "capabilities" : [ "SampleRateShading" ], + "extensions" : [ "SPV_NV_sample_mask_override_coverage" ], + "version" : "None" + }, + { + "enumerant" : "GeometryShaderPassthroughNV", + "value" : 5251, + "capabilities" : [ "Geometry" ], + "extensions" : [ "SPV_NV_geometry_shader_passthrough" ], + "version" : "None" + }, + { + "enumerant" : "ShaderViewportIndexLayerEXT", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_EXT_shader_viewport_index_layer" ], + "version" : "None" + }, + { + "enumerant" : "ShaderViewportIndexLayerNV", + "value" : 5254, + "capabilities" : [ "MultiViewport" ], + "extensions" : [ "SPV_NV_viewport_array2" ], + "version" : "None" + }, + { + "enumerant" : "ShaderViewportMaskNV", + "value" : 5255, + "capabilities" : [ "ShaderViewportIndexLayerNV" ], + "extensions" : [ "SPV_NV_viewport_array2" ], + "version" : "None" + }, + { + "enumerant" : "ShaderStereoViewNV", + "value" : 5259, + "capabilities" : [ "ShaderViewportMaskNV" ], + "extensions" : [ "SPV_NV_stereo_view_rendering" ], + "version" : "None" + }, + { + "enumerant" : "PerViewAttributesNV", + "value" : 5260, + "capabilities" : [ "MultiView" ], + "extensions" : [ "SPV_NVX_multiview_per_view_attributes" ], + "version" : "None" + }, + { + "enumerant" : "FragmentFullyCoveredEXT", + "value" : 5265, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_fully_covered" ], + "version" : "None" + }, + { + "enumerant" : "MeshShadingNV", + "value" : 5266, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_mesh_shader" ], + "version" : "None" + }, + { + "enumerant" : "ImageFootprintNV", + "value" : 5282, + "extensions" : [ "SPV_NV_shader_image_footprint" ], + "version" : "None" + }, + { + "enumerant" : "FragmentBarycentricNV", + "value" : 5284, + "extensions" : [ "SPV_NV_fragment_shader_barycentric" ], + "version" : "None" + }, + { + "enumerant" : "ComputeDerivativeGroupQuadsNV", + "value" : 5288, + "extensions" : [ "SPV_NV_compute_shader_derivatives" ], + "version" : "None" + }, + { + "enumerant" : "FragmentDensityEXT", + "value" : 5291, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_invocation_density", "SPV_NV_shading_rate" ], + "version" : "None" + }, + { + "enumerant" : "ShadingRateNV", + "value" : 5291, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_shading_rate", "SPV_EXT_fragment_invocation_density" ], + "version" : "None" + }, + { + "enumerant" : "GroupNonUniformPartitionedNV", + "value" : 5297, + "extensions" : [ "SPV_NV_shader_subgroup_partitioned" ], + "version" : "None" + }, + { + "enumerant" : "ShaderNonUniform", + "value" : 5301, + "capabilities" : [ "Shader" ], + "version" : "1.5" + }, + { + "enumerant" : "ShaderNonUniformEXT", + "value" : 5301, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "RuntimeDescriptorArray", + "value" : 5302, + "capabilities" : [ "Shader" ], + "version" : "1.5" + }, + { + "enumerant" : "RuntimeDescriptorArrayEXT", + "value" : 5302, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "InputAttachmentArrayDynamicIndexing", + "value" : 5303, + "capabilities" : [ "InputAttachment" ], + "version" : "1.5" + }, + { + "enumerant" : "InputAttachmentArrayDynamicIndexingEXT", + "value" : 5303, + "capabilities" : [ "InputAttachment" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformTexelBufferArrayDynamicIndexing", + "value" : 5304, + "capabilities" : [ "SampledBuffer" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformTexelBufferArrayDynamicIndexingEXT", + "value" : 5304, + "capabilities" : [ "SampledBuffer" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageTexelBufferArrayDynamicIndexing", + "value" : 5305, + "capabilities" : [ "ImageBuffer" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageTexelBufferArrayDynamicIndexingEXT", + "value" : 5305, + "capabilities" : [ "ImageBuffer" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformBufferArrayNonUniformIndexing", + "value" : 5306, + "capabilities" : [ "ShaderNonUniform" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformBufferArrayNonUniformIndexingEXT", + "value" : 5306, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "SampledImageArrayNonUniformIndexing", + "value" : 5307, + "capabilities" : [ "ShaderNonUniform" ], + "version" : "1.5" + }, + { + "enumerant" : "SampledImageArrayNonUniformIndexingEXT", + "value" : 5307, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageBufferArrayNonUniformIndexing", + "value" : 5308, + "capabilities" : [ "ShaderNonUniform" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageBufferArrayNonUniformIndexingEXT", + "value" : 5308, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageImageArrayNonUniformIndexing", + "value" : 5309, + "capabilities" : [ "ShaderNonUniform" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageImageArrayNonUniformIndexingEXT", + "value" : 5309, + "capabilities" : [ "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "InputAttachmentArrayNonUniformIndexing", + "value" : 5310, + "capabilities" : [ "InputAttachment", "ShaderNonUniform" ], + "version" : "1.5" + }, + { + "enumerant" : "InputAttachmentArrayNonUniformIndexingEXT", + "value" : 5310, + "capabilities" : [ "InputAttachment", "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformTexelBufferArrayNonUniformIndexing", + "value" : 5311, + "capabilities" : [ "SampledBuffer", "ShaderNonUniform" ], + "version" : "1.5" + }, + { + "enumerant" : "UniformTexelBufferArrayNonUniformIndexingEXT", + "value" : 5311, + "capabilities" : [ "SampledBuffer", "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageTexelBufferArrayNonUniformIndexing", + "value" : 5312, + "capabilities" : [ "ImageBuffer", "ShaderNonUniform" ], + "version" : "1.5" + }, + { + "enumerant" : "StorageTexelBufferArrayNonUniformIndexingEXT", + "value" : 5312, + "capabilities" : [ "ImageBuffer", "ShaderNonUniform" ], + "extensions" : [ "SPV_EXT_descriptor_indexing" ], + "version" : "1.5" + }, + { + "enumerant" : "RayTracingNV", + "value" : 5340, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "VulkanMemoryModel", + "value" : 5345, + "version" : "1.5" + }, + { + "enumerant" : "VulkanMemoryModelKHR", + "value" : 5345, + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "VulkanMemoryModelDeviceScope", + "value" : 5346, + "version" : "1.5" + }, + { + "enumerant" : "VulkanMemoryModelDeviceScopeKHR", + "value" : 5346, + "extensions" : [ "SPV_KHR_vulkan_memory_model" ], + "version" : "1.5" + }, + { + "enumerant" : "PhysicalStorageBufferAddresses", + "value" : 5347, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer", "SPV_KHR_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "PhysicalStorageBufferAddressesEXT", + "value" : 5347, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_physical_storage_buffer" ], + "version" : "1.5" + }, + { + "enumerant" : "ComputeDerivativeGroupLinearNV", + "value" : 5350, + "extensions" : [ "SPV_NV_compute_shader_derivatives" ], + "version" : "None" + }, + { + "enumerant" : "RayTracingProvisionalKHR", + "value" : 5353, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_KHR_ray_tracing" ], + "version" : "None" + }, + { + "enumerant" : "CooperativeMatrixNV", + "value" : 5357, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_cooperative_matrix" ], + "version" : "None" + }, + { + "enumerant" : "FragmentShaderSampleInterlockEXT", + "value" : 5363, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "FragmentShaderShadingRateInterlockEXT", + "value" : 5372, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "ShaderSMBuiltinsNV", + "value" : 5373, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_NV_shader_sm_builtins" ], + "version" : "None" + }, + { + "enumerant" : "FragmentShaderPixelInterlockEXT", + "value" : 5378, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_fragment_shader_interlock" ], + "version" : "None" + }, + { + "enumerant" : "DemoteToHelperInvocationEXT", + "value" : 5379, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_demote_to_helper_invocation" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupShuffleINTEL", + "value" : 5568, + "extensions" : [ "SPV_INTEL_subgroups" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupBufferBlockIOINTEL", + "value" : 5569, + "extensions" : [ "SPV_INTEL_subgroups" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupImageBlockIOINTEL", + "value" : 5570, + "extensions" : [ "SPV_INTEL_subgroups" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupImageMediaBlockIOINTEL", + "value" : 5579, + "extensions" : [ "SPV_INTEL_media_block_io" ], + "version" : "None" + }, + { + "enumerant" : "IntegerFunctions2INTEL", + "value" : 5584, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_INTEL_shader_integer_functions2" ], + "version" : "None" + }, + { + "enumerant" : "FunctionPointersINTEL", + "value" : 5603, + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "enumerant" : "IndirectReferencesINTEL", + "value" : 5604, + "extensions" : [ "SPV_INTEL_function_pointers" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupAvcMotionEstimationINTEL", + "value" : 5696, + "extensions" : [ "SPV_INTEL_device_side_avc_motion_estimation" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupAvcMotionEstimationIntraINTEL", + "value" : 5697, + "extensions" : [ "SPV_INTEL_device_side_avc_motion_estimation" ], + "version" : "None" + }, + { + "enumerant" : "SubgroupAvcMotionEstimationChromaINTEL", + "value" : 5698, + "extensions" : [ "SPV_INTEL_device_side_avc_motion_estimation" ], + "version" : "None" + }, + { + "enumerant" : "FPGAMemoryAttributesINTEL", + "value" : 5824, + "extensions" : [ "SPV_INTEL_fpga_memory_attributes" ], + "version" : "None" + }, + { + "enumerant" : "UnstructuredLoopControlsINTEL", + "value" : 5886, + "extensions" : [ "SPV_INTEL_unstructured_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "FPGALoopControlsINTEL", + "value" : 5888, + "extensions" : [ "SPV_INTEL_fpga_loop_controls" ], + "version" : "None" + }, + { + "enumerant" : "KernelAttributesINTEL", + "value" : 5892, + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "FPGAKernelAttributesINTEL", + "value" : 5897, + "extensions" : [ "SPV_INTEL_kernel_attributes" ], + "version" : "None" + }, + { + "enumerant" : "BlockingPipesINTEL", + "value" : 5945, + "extensions" : [ "SPV_INTEL_blocking_pipes" ], + "version" : "None" + }, + { + "enumerant" : "FPGARegINTEL", + "value" : 5948, + "extensions" : [ "SPV_INTEL_fpga_reg" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat32AddEXT", + "value" : 6033, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_atomic_float_add" ], + "version" : "None" + }, + { + "enumerant" : "AtomicFloat64AddEXT", + "value" : 6034, + "capabilities" : [ "Shader" ], + "extensions" : [ "SPV_EXT_shader_atomic_float_add" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "RayQueryIntersection", + "enumerants" : [ + { + "enumerant" : "RayQueryCandidateIntersectionKHR", + "value" : 0, + "capabilities" : [ "RayQueryProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryCommittedIntersectionKHR", + "value" : 1, + "capabilities" : [ "RayQueryProvisionalKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "RayQueryCommittedIntersectionType", + "enumerants" : [ + { + "enumerant" : "RayQueryCommittedIntersectionNoneKHR", + "value" : 0, + "capabilities" : [ "RayQueryProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryCommittedIntersectionTriangleKHR", + "value" : 1, + "capabilities" : [ "RayQueryProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryCommittedIntersectionGeneratedKHR", + "value" : 2, + "capabilities" : [ "RayQueryProvisionalKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "ValueEnum", + "kind" : "RayQueryCandidateIntersectionType", + "enumerants" : [ + { + "enumerant" : "RayQueryCandidateIntersectionTriangleKHR", + "value" : 0, + "capabilities" : [ "RayQueryProvisionalKHR" ], + "version" : "None" + }, + { + "enumerant" : "RayQueryCandidateIntersectionAABBKHR", + "value" : 1, + "capabilities" : [ "RayQueryProvisionalKHR" ], + "version" : "None" + } + ] + }, + { + "category" : "Id", + "kind" : "IdResultType", + "doc" : "Reference to an representing the result's type of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdResult", + "doc" : "Definition of an representing the result of the enclosing instruction" + }, + { + "category" : "Id", + "kind" : "IdMemorySemantics", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the MemorySemantics operand kind" + }, + { + "category" : "Id", + "kind" : "IdScope", + "doc" : "Reference to an representing a 32-bit integer that is a mask from the Scope operand kind" + }, + { + "category" : "Id", + "kind" : "IdRef", + "doc" : "Reference to an " + }, + { + "category" : "Literal", + "kind" : "LiteralInteger", + "doc" : "An integer consuming one or more words" + }, + { + "category" : "Literal", + "kind" : "LiteralString", + "doc" : "A null-terminated stream of characters consuming an integral number of words" + }, + { + "category" : "Literal", + "kind" : "LiteralContextDependentNumber", + "doc" : "A literal number whose size and format are determined by a previous operand in the enclosing instruction" + }, + { + "category" : "Literal", + "kind" : "LiteralExtInstInteger", + "doc" : "A 32-bit unsigned integer indicating which instruction to use and determining the layout of following operands (for OpExtInst)" + }, + { + "category" : "Literal", + "kind" : "LiteralSpecConstantOpInteger", + "doc" : "An opcode indicating the operation to be performed and determining the layout of following operands (for OpSpecConstantOp)" + }, + { + "category" : "Composite", + "kind" : "PairLiteralIntegerIdRef", + "bases" : [ "LiteralInteger", "IdRef" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefLiteralInteger", + "bases" : [ "IdRef", "LiteralInteger" ] + }, + { + "category" : "Composite", + "kind" : "PairIdRefIdRef", + "bases" : [ "IdRef", "IdRef" ] + } + ] +} diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs new file mode 100644 index 0000000..0419c0f --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.cs @@ -0,0 +1,1622 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +namespace Spv +{ + + public static class Specification + { + public const uint MagicNumber = 0x07230203; + public const uint Version = 0x00010500; + public const uint Revision = 4; + public const uint OpCodeMask = 0xffff; + public const uint WordCountShift = 16; + + public enum SourceLanguage + { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + } + + public enum ExecutionModel + { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, + } + + public enum AddressingModel + { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + } + + public enum MemoryModel + { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + } + + public enum ExecutionMode + { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + StencilRefReplacingEXT = 5027, + OutputLinesNV = 5269, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearNV = 5290, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + } + + public enum StorageClass + { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + CodeSectionINTEL = 5605, + } + + public enum Dim + { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + } + + public enum SamplerAddressingMode + { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + } + + public enum SamplerFilterMode + { + Nearest = 0, + Linear = 1, + } + + public enum ImageFormat + { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + } + + public enum ImageChannelOrder + { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + } + + public enum ImageChannelDataType + { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + } + + public enum ImageOperandsShift + { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, + } + + public enum ImageOperandsMask + { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, + } + + public enum FPFastMathModeShift + { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + } + + public enum FPFastMathModeMask + { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + } + + public enum FPRoundingMode + { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + } + + public enum LinkageType + { + Export = 0, + Import = 1, + } + + public enum AccessQualifier + { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + } + + public enum FunctionParameterAttribute + { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + } + + public enum Decoration + { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + ReferencedIndirectlyINTEL = 5602, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + } + + public enum BuiltIn + { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordNV = 5286, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTKHR = 5332, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + } + + public enum SelectionControlShift + { + Flatten = 0, + DontFlatten = 1, + } + + public enum SelectionControlMask + { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + } + + public enum LoopControlShift + { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, + } + + public enum LoopControlMask + { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, + } + + public enum FunctionControlShift + { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + } + + public enum FunctionControlMask + { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + } + + public enum MemorySemanticsShift + { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, + } + + public enum MemorySemanticsMask + { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, + } + + public enum MemoryAccessShift + { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, + } + + public enum MemoryAccessMask + { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, + } + + public enum Scope + { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + } + + public enum GroupOperation + { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + } + + public enum KernelEnqueueFlags + { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + } + + public enum KernelProfilingInfoShift + { + CmdExecTime = 0, + } + + public enum KernelProfilingInfoMask + { + MaskNone = 0, + CmdExecTime = 0x00000001, + } + + public enum Capability + { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayTraversalPrimitiveCullingProvisionalKHR = 4478, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingNV = 5340, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocationEXT = 5379, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + FPGAMemoryAttributesINTEL = 5824, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + } + + public enum RayFlagsShift + { + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, + } + + public enum RayFlagsMask + { + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, + } + + public enum RayQueryIntersection + { + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + } + + public enum RayQueryCommittedIntersectionType + { + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + } + + public enum RayQueryCandidateIntersectionType + { + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + } + + public enum FragmentShadingRateShift + { + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, + } + + public enum FragmentShadingRateMask + { + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, + } + + public enum Op + { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpTerminateInvocation = 4416, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpLoopControlINTEL = 5887, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + } + } +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h new file mode 100644 index 0000000..4e36b3a --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.h @@ -0,0 +1,2185 @@ +/* +** Copyright (c) 2014-2020 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and/or associated documentation files (the "Materials"), +** to deal in the Materials without restriction, including without limitation +** the rights to use, copy, modify, merge, publish, distribute, sublicense, +** and/or sell copies of the Materials, and to permit persons to whom the +** Materials are furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Materials. +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +** IN THE MATERIALS. +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python, C#, D +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** - C# will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10500 +#define SPV_REVISION 4 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010500; +static const unsigned int SpvRevision = 4; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelTaskNV = 5267, + SpvExecutionModelMeshNV = 5268, + SpvExecutionModelRayGenerationKHR = 5313, + SpvExecutionModelRayGenerationNV = 5313, + SpvExecutionModelIntersectionKHR = 5314, + SpvExecutionModelIntersectionNV = 5314, + SpvExecutionModelAnyHitKHR = 5315, + SpvExecutionModelAnyHitNV = 5315, + SpvExecutionModelClosestHitKHR = 5316, + SpvExecutionModelClosestHitNV = 5316, + SpvExecutionModelMissKHR = 5317, + SpvExecutionModelMissNV = 5317, + SpvExecutionModelCallableKHR = 5318, + SpvExecutionModelCallableNV = 5318, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelPhysicalStorageBuffer64 = 5348, + SpvAddressingModelPhysicalStorageBuffer64EXT = 5348, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelVulkan = 3, + SpvMemoryModelVulkanKHR = 3, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModeSubgroupsPerWorkgroupId = 37, + SpvExecutionModeLocalSizeId = 38, + SpvExecutionModeLocalSizeHintId = 39, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeDenormPreserve = 4459, + SpvExecutionModeDenormFlushToZero = 4460, + SpvExecutionModeSignedZeroInfNanPreserve = 4461, + SpvExecutionModeRoundingModeRTE = 4462, + SpvExecutionModeRoundingModeRTZ = 4463, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeOutputLinesNV = 5269, + SpvExecutionModeOutputPrimitivesNV = 5270, + SpvExecutionModeDerivativeGroupQuadsNV = 5289, + SpvExecutionModeDerivativeGroupLinearNV = 5290, + SpvExecutionModeOutputTrianglesNV = 5298, + SpvExecutionModePixelInterlockOrderedEXT = 5366, + SpvExecutionModePixelInterlockUnorderedEXT = 5367, + SpvExecutionModeSampleInterlockOrderedEXT = 5368, + SpvExecutionModeSampleInterlockUnorderedEXT = 5369, + SpvExecutionModeShadingRateInterlockOrderedEXT = 5370, + SpvExecutionModeShadingRateInterlockUnorderedEXT = 5371, + SpvExecutionModeMaxWorkgroupSizeINTEL = 5893, + SpvExecutionModeMaxWorkDimINTEL = 5894, + SpvExecutionModeNoGlobalOffsetINTEL = 5895, + SpvExecutionModeNumSIMDWorkitemsINTEL = 5896, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassCallableDataKHR = 5328, + SpvStorageClassCallableDataNV = 5328, + SpvStorageClassIncomingCallableDataKHR = 5329, + SpvStorageClassIncomingCallableDataNV = 5329, + SpvStorageClassRayPayloadKHR = 5338, + SpvStorageClassRayPayloadNV = 5338, + SpvStorageClassHitAttributeKHR = 5339, + SpvStorageClassHitAttributeNV = 5339, + SpvStorageClassIncomingRayPayloadKHR = 5342, + SpvStorageClassIncomingRayPayloadNV = 5342, + SpvStorageClassShaderRecordBufferKHR = 5343, + SpvStorageClassShaderRecordBufferNV = 5343, + SpvStorageClassPhysicalStorageBuffer = 5349, + SpvStorageClassPhysicalStorageBufferEXT = 5349, + SpvStorageClassCodeSectionINTEL = 5605, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatR64ui = 40, + SpvImageFormatR64i = 41, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMakeTexelAvailableShift = 8, + SpvImageOperandsMakeTexelAvailableKHRShift = 8, + SpvImageOperandsMakeTexelVisibleShift = 9, + SpvImageOperandsMakeTexelVisibleKHRShift = 9, + SpvImageOperandsNonPrivateTexelShift = 10, + SpvImageOperandsNonPrivateTexelKHRShift = 10, + SpvImageOperandsVolatileTexelShift = 11, + SpvImageOperandsVolatileTexelKHRShift = 11, + SpvImageOperandsSignExtendShift = 12, + SpvImageOperandsZeroExtendShift = 13, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, + SpvImageOperandsMakeTexelAvailableMask = 0x00000100, + SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + SpvImageOperandsMakeTexelVisibleMask = 0x00000200, + SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + SpvImageOperandsNonPrivateTexelMask = 0x00000400, + SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400, + SpvImageOperandsVolatileTexelMask = 0x00000800, + SpvImageOperandsVolatileTexelKHRMask = 0x00000800, + SpvImageOperandsSignExtendMask = 0x00001000, + SpvImageOperandsZeroExtendMask = 0x00002000, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationUniformId = 27, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationAlignmentId = 46, + SpvDecorationMaxByteOffsetId = 47, + SpvDecorationNoSignedWrap = 4469, + SpvDecorationNoUnsignedWrap = 4470, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationPerPrimitiveNV = 5271, + SpvDecorationPerViewNV = 5272, + SpvDecorationPerTaskNV = 5273, + SpvDecorationPerVertexNV = 5285, + SpvDecorationNonUniform = 5300, + SpvDecorationNonUniformEXT = 5300, + SpvDecorationRestrictPointer = 5355, + SpvDecorationRestrictPointerEXT = 5355, + SpvDecorationAliasedPointer = 5356, + SpvDecorationAliasedPointerEXT = 5356, + SpvDecorationReferencedIndirectlyINTEL = 5602, + SpvDecorationCounterBuffer = 5634, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationUserSemantic = 5635, + SpvDecorationUserTypeGOOGLE = 5636, + SpvDecorationRegisterINTEL = 5825, + SpvDecorationMemoryINTEL = 5826, + SpvDecorationNumbanksINTEL = 5827, + SpvDecorationBankwidthINTEL = 5828, + SpvDecorationMaxPrivateCopiesINTEL = 5829, + SpvDecorationSinglepumpINTEL = 5830, + SpvDecorationDoublepumpINTEL = 5831, + SpvDecorationMaxReplicatesINTEL = 5832, + SpvDecorationSimpleDualPortINTEL = 5833, + SpvDecorationMergeINTEL = 5834, + SpvDecorationBankBitsINTEL = 5835, + SpvDecorationForcePow2DepthINTEL = 5836, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInSubgroupEqMask = 4416, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMask = 4417, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMask = 4418, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMask = 4419, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMask = 4420, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInPrimitiveShadingRateKHR = 4432, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInShadingRateKHR = 4444, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInFullyCoveredEXT = 5264, + SpvBuiltInTaskCountNV = 5274, + SpvBuiltInPrimitiveCountNV = 5275, + SpvBuiltInPrimitiveIndicesNV = 5276, + SpvBuiltInClipDistancePerViewNV = 5277, + SpvBuiltInCullDistancePerViewNV = 5278, + SpvBuiltInLayerPerViewNV = 5279, + SpvBuiltInMeshViewCountNV = 5280, + SpvBuiltInMeshViewIndicesNV = 5281, + SpvBuiltInBaryCoordNV = 5286, + SpvBuiltInBaryCoordNoPerspNV = 5287, + SpvBuiltInFragSizeEXT = 5292, + SpvBuiltInFragmentSizeNV = 5292, + SpvBuiltInFragInvocationCountEXT = 5293, + SpvBuiltInInvocationsPerPixelNV = 5293, + SpvBuiltInLaunchIdKHR = 5319, + SpvBuiltInLaunchIdNV = 5319, + SpvBuiltInLaunchSizeKHR = 5320, + SpvBuiltInLaunchSizeNV = 5320, + SpvBuiltInWorldRayOriginKHR = 5321, + SpvBuiltInWorldRayOriginNV = 5321, + SpvBuiltInWorldRayDirectionKHR = 5322, + SpvBuiltInWorldRayDirectionNV = 5322, + SpvBuiltInObjectRayOriginKHR = 5323, + SpvBuiltInObjectRayOriginNV = 5323, + SpvBuiltInObjectRayDirectionKHR = 5324, + SpvBuiltInObjectRayDirectionNV = 5324, + SpvBuiltInRayTminKHR = 5325, + SpvBuiltInRayTminNV = 5325, + SpvBuiltInRayTmaxKHR = 5326, + SpvBuiltInRayTmaxNV = 5326, + SpvBuiltInInstanceCustomIndexKHR = 5327, + SpvBuiltInInstanceCustomIndexNV = 5327, + SpvBuiltInObjectToWorldKHR = 5330, + SpvBuiltInObjectToWorldNV = 5330, + SpvBuiltInWorldToObjectKHR = 5331, + SpvBuiltInWorldToObjectNV = 5331, + SpvBuiltInHitTKHR = 5332, + SpvBuiltInHitTNV = 5332, + SpvBuiltInHitKindKHR = 5333, + SpvBuiltInHitKindNV = 5333, + SpvBuiltInIncomingRayFlagsKHR = 5351, + SpvBuiltInIncomingRayFlagsNV = 5351, + SpvBuiltInRayGeometryIndexKHR = 5352, + SpvBuiltInWarpsPerSMNV = 5374, + SpvBuiltInSMCountNV = 5375, + SpvBuiltInWarpIDNV = 5376, + SpvBuiltInSMIDNV = 5377, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMinIterationsShift = 4, + SpvLoopControlMaxIterationsShift = 5, + SpvLoopControlIterationMultipleShift = 6, + SpvLoopControlPeelCountShift = 7, + SpvLoopControlPartialCountShift = 8, + SpvLoopControlInitiationIntervalINTELShift = 16, + SpvLoopControlMaxConcurrencyINTELShift = 17, + SpvLoopControlDependencyArrayINTELShift = 18, + SpvLoopControlPipelineEnableINTELShift = 19, + SpvLoopControlLoopCoalesceINTELShift = 20, + SpvLoopControlMaxInterleavingINTELShift = 21, + SpvLoopControlSpeculatedIterationsINTELShift = 22, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, + SpvLoopControlMinIterationsMask = 0x00000010, + SpvLoopControlMaxIterationsMask = 0x00000020, + SpvLoopControlIterationMultipleMask = 0x00000040, + SpvLoopControlPeelCountMask = 0x00000080, + SpvLoopControlPartialCountMask = 0x00000100, + SpvLoopControlInitiationIntervalINTELMask = 0x00010000, + SpvLoopControlMaxConcurrencyINTELMask = 0x00020000, + SpvLoopControlDependencyArrayINTELMask = 0x00040000, + SpvLoopControlPipelineEnableINTELMask = 0x00080000, + SpvLoopControlLoopCoalesceINTELMask = 0x00100000, + SpvLoopControlMaxInterleavingINTELMask = 0x00200000, + SpvLoopControlSpeculatedIterationsINTELMask = 0x00400000, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsOutputMemoryShift = 12, + SpvMemorySemanticsOutputMemoryKHRShift = 12, + SpvMemorySemanticsMakeAvailableShift = 13, + SpvMemorySemanticsMakeAvailableKHRShift = 13, + SpvMemorySemanticsMakeVisibleShift = 14, + SpvMemorySemanticsMakeVisibleKHRShift = 14, + SpvMemorySemanticsVolatileShift = 15, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, + SpvMemorySemanticsOutputMemoryMask = 0x00001000, + SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000, + SpvMemorySemanticsMakeAvailableMask = 0x00002000, + SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000, + SpvMemorySemanticsMakeVisibleMask = 0x00004000, + SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000, + SpvMemorySemanticsVolatileMask = 0x00008000, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMakePointerAvailableShift = 3, + SpvMemoryAccessMakePointerAvailableKHRShift = 3, + SpvMemoryAccessMakePointerVisibleShift = 4, + SpvMemoryAccessMakePointerVisibleKHRShift = 4, + SpvMemoryAccessNonPrivatePointerShift = 5, + SpvMemoryAccessNonPrivatePointerKHRShift = 5, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, + SpvMemoryAccessMakePointerAvailableMask = 0x00000008, + SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008, + SpvMemoryAccessMakePointerVisibleMask = 0x00000010, + SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010, + SpvMemoryAccessNonPrivatePointerMask = 0x00000020, + SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeQueueFamily = 5, + SpvScopeQueueFamilyKHR = 5, + SpvScopeShaderCallKHR = 6, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationClusteredReduce = 3, + SpvGroupOperationPartitionedReduceNV = 6, + SpvGroupOperationPartitionedInclusiveScanNV = 7, + SpvGroupOperationPartitionedExclusiveScanNV = 8, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilityGroupNonUniform = 61, + SpvCapabilityGroupNonUniformVote = 62, + SpvCapabilityGroupNonUniformArithmetic = 63, + SpvCapabilityGroupNonUniformBallot = 64, + SpvCapabilityGroupNonUniformShuffle = 65, + SpvCapabilityGroupNonUniformShuffleRelative = 66, + SpvCapabilityGroupNonUniformClustered = 67, + SpvCapabilityGroupNonUniformQuad = 68, + SpvCapabilityShaderLayer = 69, + SpvCapabilityShaderViewportIndex = 70, + SpvCapabilityFragmentShadingRateKHR = 4422, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityStorageBuffer8BitAccess = 4448, + SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449, + SpvCapabilityStoragePushConstant8 = 4450, + SpvCapabilityDenormPreserve = 4464, + SpvCapabilityDenormFlushToZero = 4465, + SpvCapabilitySignedZeroInfNanPreserve = 4466, + SpvCapabilityRoundingModeRTE = 4467, + SpvCapabilityRoundingModeRTZ = 4468, + SpvCapabilityRayQueryProvisionalKHR = 4471, + SpvCapabilityRayTraversalPrimitiveCullingProvisionalKHR = 4478, + SpvCapabilityFloat16ImageAMD = 5008, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilityInt64ImageEXT = 5016, + SpvCapabilityShaderClockKHR = 5055, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilityFragmentFullyCoveredEXT = 5265, + SpvCapabilityMeshShadingNV = 5266, + SpvCapabilityImageFootprintNV = 5282, + SpvCapabilityFragmentBarycentricNV = 5284, + SpvCapabilityComputeDerivativeGroupQuadsNV = 5288, + SpvCapabilityFragmentDensityEXT = 5291, + SpvCapabilityShadingRateNV = 5291, + SpvCapabilityGroupNonUniformPartitionedNV = 5297, + SpvCapabilityShaderNonUniform = 5301, + SpvCapabilityShaderNonUniformEXT = 5301, + SpvCapabilityRuntimeDescriptorArray = 5302, + SpvCapabilityRuntimeDescriptorArrayEXT = 5302, + SpvCapabilityInputAttachmentArrayDynamicIndexing = 5303, + SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + SpvCapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + SpvCapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + SpvCapabilityUniformBufferArrayNonUniformIndexing = 5306, + SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + SpvCapabilitySampledImageArrayNonUniformIndexing = 5307, + SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + SpvCapabilityStorageBufferArrayNonUniformIndexing = 5308, + SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + SpvCapabilityStorageImageArrayNonUniformIndexing = 5309, + SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + SpvCapabilityInputAttachmentArrayNonUniformIndexing = 5310, + SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + SpvCapabilityRayTracingNV = 5340, + SpvCapabilityVulkanMemoryModel = 5345, + SpvCapabilityVulkanMemoryModelKHR = 5345, + SpvCapabilityVulkanMemoryModelDeviceScope = 5346, + SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + SpvCapabilityPhysicalStorageBufferAddresses = 5347, + SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347, + SpvCapabilityComputeDerivativeGroupLinearNV = 5350, + SpvCapabilityRayTracingProvisionalKHR = 5353, + SpvCapabilityCooperativeMatrixNV = 5357, + SpvCapabilityFragmentShaderSampleInterlockEXT = 5363, + SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372, + SpvCapabilityShaderSMBuiltinsNV = 5373, + SpvCapabilityFragmentShaderPixelInterlockEXT = 5378, + SpvCapabilityDemoteToHelperInvocationEXT = 5379, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579, + SpvCapabilityIntegerFunctions2INTEL = 5584, + SpvCapabilityFunctionPointersINTEL = 5603, + SpvCapabilityIndirectReferencesINTEL = 5604, + SpvCapabilitySubgroupAvcMotionEstimationINTEL = 5696, + SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + SpvCapabilityFPGAMemoryAttributesINTEL = 5824, + SpvCapabilityUnstructuredLoopControlsINTEL = 5886, + SpvCapabilityFPGALoopControlsINTEL = 5888, + SpvCapabilityKernelAttributesINTEL = 5892, + SpvCapabilityFPGAKernelAttributesINTEL = 5897, + SpvCapabilityBlockingPipesINTEL = 5945, + SpvCapabilityFPGARegINTEL = 5948, + SpvCapabilityAtomicFloat32AddEXT = 6033, + SpvCapabilityAtomicFloat64AddEXT = 6034, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvRayFlagsShift_ { + SpvRayFlagsOpaqueKHRShift = 0, + SpvRayFlagsNoOpaqueKHRShift = 1, + SpvRayFlagsTerminateOnFirstHitKHRShift = 2, + SpvRayFlagsSkipClosestHitShaderKHRShift = 3, + SpvRayFlagsCullBackFacingTrianglesKHRShift = 4, + SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5, + SpvRayFlagsCullOpaqueKHRShift = 6, + SpvRayFlagsCullNoOpaqueKHRShift = 7, + SpvRayFlagsSkipTrianglesKHRShift = 8, + SpvRayFlagsSkipAABBsKHRShift = 9, + SpvRayFlagsMax = 0x7fffffff, +} SpvRayFlagsShift; + +typedef enum SpvRayFlagsMask_ { + SpvRayFlagsMaskNone = 0, + SpvRayFlagsOpaqueKHRMask = 0x00000001, + SpvRayFlagsNoOpaqueKHRMask = 0x00000002, + SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + SpvRayFlagsCullOpaqueKHRMask = 0x00000040, + SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080, + SpvRayFlagsSkipTrianglesKHRMask = 0x00000100, + SpvRayFlagsSkipAABBsKHRMask = 0x00000200, +} SpvRayFlagsMask; + +typedef enum SpvRayQueryIntersection_ { + SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + SpvRayQueryIntersectionMax = 0x7fffffff, +} SpvRayQueryIntersection; + +typedef enum SpvRayQueryCommittedIntersectionType_ { + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCommittedIntersectionType; + +typedef enum SpvRayQueryCandidateIntersectionType_ { + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCandidateIntersectionType; + +typedef enum SpvFragmentShadingRateShift_ { + SpvFragmentShadingRateVertical2PixelsShift = 0, + SpvFragmentShadingRateVertical4PixelsShift = 1, + SpvFragmentShadingRateHorizontal2PixelsShift = 2, + SpvFragmentShadingRateHorizontal4PixelsShift = 3, + SpvFragmentShadingRateMax = 0x7fffffff, +} SpvFragmentShadingRateShift; + +typedef enum SpvFragmentShadingRateMask_ { + SpvFragmentShadingRateMaskNone = 0, + SpvFragmentShadingRateVertical2PixelsMask = 0x00000001, + SpvFragmentShadingRateVertical4PixelsMask = 0x00000002, + SpvFragmentShadingRateHorizontal2PixelsMask = 0x00000004, + SpvFragmentShadingRateHorizontal4PixelsMask = 0x00000008, +} SpvFragmentShadingRateMask; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpExecutionModeId = 331, + SpvOpDecorateId = 332, + SpvOpGroupNonUniformElect = 333, + SpvOpGroupNonUniformAll = 334, + SpvOpGroupNonUniformAny = 335, + SpvOpGroupNonUniformAllEqual = 336, + SpvOpGroupNonUniformBroadcast = 337, + SpvOpGroupNonUniformBroadcastFirst = 338, + SpvOpGroupNonUniformBallot = 339, + SpvOpGroupNonUniformInverseBallot = 340, + SpvOpGroupNonUniformBallotBitExtract = 341, + SpvOpGroupNonUniformBallotBitCount = 342, + SpvOpGroupNonUniformBallotFindLSB = 343, + SpvOpGroupNonUniformBallotFindMSB = 344, + SpvOpGroupNonUniformShuffle = 345, + SpvOpGroupNonUniformShuffleXor = 346, + SpvOpGroupNonUniformShuffleUp = 347, + SpvOpGroupNonUniformShuffleDown = 348, + SpvOpGroupNonUniformIAdd = 349, + SpvOpGroupNonUniformFAdd = 350, + SpvOpGroupNonUniformIMul = 351, + SpvOpGroupNonUniformFMul = 352, + SpvOpGroupNonUniformSMin = 353, + SpvOpGroupNonUniformUMin = 354, + SpvOpGroupNonUniformFMin = 355, + SpvOpGroupNonUniformSMax = 356, + SpvOpGroupNonUniformUMax = 357, + SpvOpGroupNonUniformFMax = 358, + SpvOpGroupNonUniformBitwiseAnd = 359, + SpvOpGroupNonUniformBitwiseOr = 360, + SpvOpGroupNonUniformBitwiseXor = 361, + SpvOpGroupNonUniformLogicalAnd = 362, + SpvOpGroupNonUniformLogicalOr = 363, + SpvOpGroupNonUniformLogicalXor = 364, + SpvOpGroupNonUniformQuadBroadcast = 365, + SpvOpGroupNonUniformQuadSwap = 366, + SpvOpCopyLogical = 400, + SpvOpPtrEqual = 401, + SpvOpPtrNotEqual = 402, + SpvOpPtrDiff = 403, + SpvOpTerminateInvocation = 4416, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpTypeRayQueryProvisionalKHR = 4472, + SpvOpRayQueryInitializeKHR = 4473, + SpvOpRayQueryTerminateKHR = 4474, + SpvOpRayQueryGenerateIntersectionKHR = 4475, + SpvOpRayQueryConfirmIntersectionKHR = 4476, + SpvOpRayQueryProceedKHR = 4477, + SpvOpRayQueryGetIntersectionTypeKHR = 4479, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpReadClockKHR = 5056, + SpvOpImageSampleFootprintNV = 5283, + SpvOpGroupNonUniformPartitionNV = 5296, + SpvOpWritePackedPrimitiveIndices4x8NV = 5299, + SpvOpReportIntersectionKHR = 5334, + SpvOpReportIntersectionNV = 5334, + SpvOpIgnoreIntersectionKHR = 5335, + SpvOpIgnoreIntersectionNV = 5335, + SpvOpTerminateRayKHR = 5336, + SpvOpTerminateRayNV = 5336, + SpvOpTraceNV = 5337, + SpvOpTraceRayKHR = 5337, + SpvOpTypeAccelerationStructureKHR = 5341, + SpvOpTypeAccelerationStructureNV = 5341, + SpvOpExecuteCallableKHR = 5344, + SpvOpExecuteCallableNV = 5344, + SpvOpTypeCooperativeMatrixNV = 5358, + SpvOpCooperativeMatrixLoadNV = 5359, + SpvOpCooperativeMatrixStoreNV = 5360, + SpvOpCooperativeMatrixMulAddNV = 5361, + SpvOpCooperativeMatrixLengthNV = 5362, + SpvOpBeginInvocationInterlockEXT = 5364, + SpvOpEndInvocationInterlockEXT = 5365, + SpvOpDemoteToHelperInvocationEXT = 5380, + SpvOpIsHelperInvocationEXT = 5381, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpSubgroupImageMediaBlockReadINTEL = 5580, + SpvOpSubgroupImageMediaBlockWriteINTEL = 5581, + SpvOpUCountLeadingZerosINTEL = 5585, + SpvOpUCountTrailingZerosINTEL = 5586, + SpvOpAbsISubINTEL = 5587, + SpvOpAbsUSubINTEL = 5588, + SpvOpIAddSatINTEL = 5589, + SpvOpUAddSatINTEL = 5590, + SpvOpIAverageINTEL = 5591, + SpvOpUAverageINTEL = 5592, + SpvOpIAverageRoundedINTEL = 5593, + SpvOpUAverageRoundedINTEL = 5594, + SpvOpISubSatINTEL = 5595, + SpvOpUSubSatINTEL = 5596, + SpvOpIMul32x16INTEL = 5597, + SpvOpUMul32x16INTEL = 5598, + SpvOpFunctionPointerINTEL = 5600, + SpvOpFunctionPointerCallINTEL = 5601, + SpvOpDecorateString = 5632, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateString = 5633, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpVmeImageINTEL = 5699, + SpvOpTypeVmeImageINTEL = 5700, + SpvOpTypeAvcImePayloadINTEL = 5701, + SpvOpTypeAvcRefPayloadINTEL = 5702, + SpvOpTypeAvcSicPayloadINTEL = 5703, + SpvOpTypeAvcMcePayloadINTEL = 5704, + SpvOpTypeAvcMceResultINTEL = 5705, + SpvOpTypeAvcImeResultINTEL = 5706, + SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + SpvOpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + SpvOpTypeAvcImeDualReferenceStreaminINTEL = 5710, + SpvOpTypeAvcRefResultINTEL = 5711, + SpvOpTypeAvcSicResultINTEL = 5712, + SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + SpvOpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + SpvOpSubgroupAvcMceConvertToImeResultINTEL = 5733, + SpvOpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + SpvOpSubgroupAvcMceConvertToRefResultINTEL = 5735, + SpvOpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + SpvOpSubgroupAvcMceConvertToSicResultINTEL = 5737, + SpvOpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + SpvOpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + SpvOpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + SpvOpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + SpvOpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + SpvOpSubgroupAvcImeInitializeINTEL = 5747, + SpvOpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + SpvOpSubgroupAvcImeSetDualReferenceINTEL = 5749, + SpvOpSubgroupAvcImeRefWindowSizeINTEL = 5750, + SpvOpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + SpvOpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + SpvOpSubgroupAvcImeSetWeightedSadINTEL = 5756, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + SpvOpSubgroupAvcImeConvertToMceResultINTEL = 5765, + SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + SpvOpSubgroupAvcImeGetBorderReachedINTEL = 5776, + SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + SpvOpSubgroupAvcFmeInitializeINTEL = 5781, + SpvOpSubgroupAvcBmeInitializeINTEL = 5782, + SpvOpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + SpvOpSubgroupAvcRefConvertToMceResultINTEL = 5790, + SpvOpSubgroupAvcSicInitializeINTEL = 5791, + SpvOpSubgroupAvcSicConfigureSkcINTEL = 5792, + SpvOpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + SpvOpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + SpvOpSubgroupAvcSicEvaluateIpeINTEL = 5803, + SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + SpvOpSubgroupAvcSicConvertToMceResultINTEL = 5808, + SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + SpvOpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + SpvOpLoopControlINTEL = 5887, + SpvOpReadPipeBlockingINTEL = 5946, + SpvOpWritePipeBlockingINTEL = 5947, + SpvOpFPGARegINTEL = 5949, + SpvOpRayQueryGetRayTMinKHR = 6016, + SpvOpRayQueryGetRayFlagsKHR = 6017, + SpvOpRayQueryGetIntersectionTKHR = 6018, + SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020, + SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022, + SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024, + SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025, + SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + SpvOpRayQueryGetWorldRayDirectionKHR = 6029, + SpvOpRayQueryGetWorldRayOriginKHR = 6030, + SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031, + SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032, + SpvOpAtomicFAddEXT = 6035, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#ifdef SPV_ENABLE_UTILITY_CODE +inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case SpvOpNop: *hasResult = false; *hasResultType = false; break; + case SpvOpUndef: *hasResult = true; *hasResultType = true; break; + case SpvOpSourceContinued: *hasResult = false; *hasResultType = false; break; + case SpvOpSource: *hasResult = false; *hasResultType = false; break; + case SpvOpSourceExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpName: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberName: *hasResult = false; *hasResultType = false; break; + case SpvOpString: *hasResult = true; *hasResultType = false; break; + case SpvOpLine: *hasResult = false; *hasResultType = false; break; + case SpvOpExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpExtInstImport: *hasResult = true; *hasResultType = false; break; + case SpvOpExtInst: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryModel: *hasResult = false; *hasResultType = false; break; + case SpvOpEntryPoint: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionMode: *hasResult = false; *hasResultType = false; break; + case SpvOpCapability: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeVoid: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeBool: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeInt: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFloat: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeVector: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampler: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStruct: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePointer: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFunction: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeQueue: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePipe: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantSampler: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantNull: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case SpvOpFunction: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case SpvOpFunctionCall: *hasResult = true; *hasResultType = true; break; + case SpvOpVariable: *hasResult = true; *hasResultType = true; break; + case SpvOpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case SpvOpLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpStore: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemory: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case SpvOpAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpArrayLength: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case SpvOpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyObject: *hasResult = true; *hasResultType = true; break; + case SpvOpTranspose: *hasResult = true; *hasResultType = true; break; + case SpvOpSampledImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageRead: *hasResult = true; *hasResultType = true; break; + case SpvOpImageWrite: *hasResult = false; *hasResultType = false; break; + case SpvOpImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToU: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSToF: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToF: *hasResult = true; *hasResultType = true; break; + case SpvOpUConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpSConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpFConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case SpvOpBitcast: *hasResult = true; *hasResultType = true; break; + case SpvOpSNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpFNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpISub: *hasResult = true; *hasResultType = true; break; + case SpvOpFSub: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpUDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpSDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpFDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpUMod: *hasResult = true; *hasResultType = true; break; + case SpvOpSRem: *hasResult = true; *hasResultType = true; break; + case SpvOpSMod: *hasResult = true; *hasResultType = true; break; + case SpvOpFRem: *hasResult = true; *hasResultType = true; break; + case SpvOpFMod: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpOuterProduct: *hasResult = true; *hasResultType = true; break; + case SpvOpDot: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddCarry: *hasResult = true; *hasResultType = true; break; + case SpvOpISubBorrow: *hasResult = true; *hasResultType = true; break; + case SpvOpUMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpSMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpAny: *hasResult = true; *hasResultType = true; break; + case SpvOpAll: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNan: *hasResult = true; *hasResultType = true; break; + case SpvOpIsInf: *hasResult = true; *hasResultType = true; break; + case SpvOpIsFinite: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNormal: *hasResult = true; *hasResultType = true; break; + case SpvOpSignBitSet: *hasResult = true; *hasResultType = true; break; + case SpvOpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case SpvOpOrdered: *hasResult = true; *hasResultType = true; break; + case SpvOpUnordered: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNot: *hasResult = true; *hasResultType = true; break; + case SpvOpSelect: *hasResult = true; *hasResultType = true; break; + case SpvOpIEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpINotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpNot: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitReverse: *hasResult = true; *hasResultType = true; break; + case SpvOpBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdx: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdy: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidth: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyFine: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicStore: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicISub: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicOr: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicXor: *hasResult = true; *hasResultType = true; break; + case SpvOpPhi: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpLabel: *hasResult = true; *hasResultType = false; break; + case SpvOpBranch: *hasResult = false; *hasResultType = false; break; + case SpvOpBranchConditional: *hasResult = false; *hasResultType = false; break; + case SpvOpSwitch: *hasResult = false; *hasResultType = false; break; + case SpvOpKill: *hasResult = false; *hasResultType = false; break; + case SpvOpReturn: *hasResult = false; *hasResultType = false; break; + case SpvOpReturnValue: *hasResult = false; *hasResultType = false; break; + case SpvOpUnreachable: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case SpvOpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case SpvOpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case SpvOpRetainEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case SpvOpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case SpvOpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case SpvOpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case SpvOpNoLine: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case SpvOpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case SpvOpSizeOf: *hasResult = true; *hasResultType = true; break; + case SpvOpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case SpvOpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case SpvOpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorateId: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrDiff: *hasResult = true; *hasResultType = true; break; + case SpvOpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case SpvOpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case SpvOpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +#endif + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp new file mode 100644 index 0000000..988545c --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp @@ -0,0 +1,2196 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10500 +#define SPV_REVISION 4 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010500; +static const unsigned int Revision = 4; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelTaskNV = 5267, + ExecutionModelMeshNV = 5268, + ExecutionModelRayGenerationKHR = 5313, + ExecutionModelRayGenerationNV = 5313, + ExecutionModelIntersectionKHR = 5314, + ExecutionModelIntersectionNV = 5314, + ExecutionModelAnyHitKHR = 5315, + ExecutionModelAnyHitNV = 5315, + ExecutionModelClosestHitKHR = 5316, + ExecutionModelClosestHitNV = 5316, + ExecutionModelMissKHR = 5317, + ExecutionModelMissNV = 5317, + ExecutionModelCallableKHR = 5318, + ExecutionModelCallableNV = 5318, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelPhysicalStorageBuffer64 = 5348, + AddressingModelPhysicalStorageBuffer64EXT = 5348, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelVulkan = 3, + MemoryModelVulkanKHR = 3, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeDenormPreserve = 4459, + ExecutionModeDenormFlushToZero = 4460, + ExecutionModeSignedZeroInfNanPreserve = 4461, + ExecutionModeRoundingModeRTE = 4462, + ExecutionModeRoundingModeRTZ = 4463, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeOutputLinesNV = 5269, + ExecutionModeOutputPrimitivesNV = 5270, + ExecutionModeDerivativeGroupQuadsNV = 5289, + ExecutionModeDerivativeGroupLinearNV = 5290, + ExecutionModeOutputTrianglesNV = 5298, + ExecutionModePixelInterlockOrderedEXT = 5366, + ExecutionModePixelInterlockUnorderedEXT = 5367, + ExecutionModeSampleInterlockOrderedEXT = 5368, + ExecutionModeSampleInterlockUnorderedEXT = 5369, + ExecutionModeShadingRateInterlockOrderedEXT = 5370, + ExecutionModeShadingRateInterlockUnorderedEXT = 5371, + ExecutionModeMaxWorkgroupSizeINTEL = 5893, + ExecutionModeMaxWorkDimINTEL = 5894, + ExecutionModeNoGlobalOffsetINTEL = 5895, + ExecutionModeNumSIMDWorkitemsINTEL = 5896, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassCallableDataKHR = 5328, + StorageClassCallableDataNV = 5328, + StorageClassIncomingCallableDataKHR = 5329, + StorageClassIncomingCallableDataNV = 5329, + StorageClassRayPayloadKHR = 5338, + StorageClassRayPayloadNV = 5338, + StorageClassHitAttributeKHR = 5339, + StorageClassHitAttributeNV = 5339, + StorageClassIncomingRayPayloadKHR = 5342, + StorageClassIncomingRayPayloadNV = 5342, + StorageClassShaderRecordBufferKHR = 5343, + StorageClassShaderRecordBufferNV = 5343, + StorageClassPhysicalStorageBuffer = 5349, + StorageClassPhysicalStorageBufferEXT = 5349, + StorageClassCodeSectionINTEL = 5605, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatR64ui = 40, + ImageFormatR64i = 41, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMakeTexelAvailableShift = 8, + ImageOperandsMakeTexelAvailableKHRShift = 8, + ImageOperandsMakeTexelVisibleShift = 9, + ImageOperandsMakeTexelVisibleKHRShift = 9, + ImageOperandsNonPrivateTexelShift = 10, + ImageOperandsNonPrivateTexelKHRShift = 10, + ImageOperandsVolatileTexelShift = 11, + ImageOperandsVolatileTexelKHRShift = 11, + ImageOperandsSignExtendShift = 12, + ImageOperandsZeroExtendShift = 13, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, + ImageOperandsMakeTexelAvailableMask = 0x00000100, + ImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + ImageOperandsMakeTexelVisibleMask = 0x00000200, + ImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + ImageOperandsNonPrivateTexelMask = 0x00000400, + ImageOperandsNonPrivateTexelKHRMask = 0x00000400, + ImageOperandsVolatileTexelMask = 0x00000800, + ImageOperandsVolatileTexelKHRMask = 0x00000800, + ImageOperandsSignExtendMask = 0x00001000, + ImageOperandsZeroExtendMask = 0x00002000, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationUniformId = 27, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationNoSignedWrap = 4469, + DecorationNoUnsignedWrap = 4470, + DecorationExplicitInterpAMD = 4999, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationPerPrimitiveNV = 5271, + DecorationPerViewNV = 5272, + DecorationPerTaskNV = 5273, + DecorationPerVertexNV = 5285, + DecorationNonUniform = 5300, + DecorationNonUniformEXT = 5300, + DecorationRestrictPointer = 5355, + DecorationRestrictPointerEXT = 5355, + DecorationAliasedPointer = 5356, + DecorationAliasedPointerEXT = 5356, + DecorationReferencedIndirectlyINTEL = 5602, + DecorationCounterBuffer = 5634, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationUserSemantic = 5635, + DecorationUserTypeGOOGLE = 5636, + DecorationRegisterINTEL = 5825, + DecorationMemoryINTEL = 5826, + DecorationNumbanksINTEL = 5827, + DecorationBankwidthINTEL = 5828, + DecorationMaxPrivateCopiesINTEL = 5829, + DecorationSinglepumpINTEL = 5830, + DecorationDoublepumpINTEL = 5831, + DecorationMaxReplicatesINTEL = 5832, + DecorationSimpleDualPortINTEL = 5833, + DecorationMergeINTEL = 5834, + DecorationBankBitsINTEL = 5835, + DecorationForcePow2DepthINTEL = 5836, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInSubgroupEqMask = 4416, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMask = 4417, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMask = 4418, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMask = 4419, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMask = 4420, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInPrimitiveShadingRateKHR = 4432, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInShadingRateKHR = 4444, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInFullyCoveredEXT = 5264, + BuiltInTaskCountNV = 5274, + BuiltInPrimitiveCountNV = 5275, + BuiltInPrimitiveIndicesNV = 5276, + BuiltInClipDistancePerViewNV = 5277, + BuiltInCullDistancePerViewNV = 5278, + BuiltInLayerPerViewNV = 5279, + BuiltInMeshViewCountNV = 5280, + BuiltInMeshViewIndicesNV = 5281, + BuiltInBaryCoordNV = 5286, + BuiltInBaryCoordNoPerspNV = 5287, + BuiltInFragSizeEXT = 5292, + BuiltInFragmentSizeNV = 5292, + BuiltInFragInvocationCountEXT = 5293, + BuiltInInvocationsPerPixelNV = 5293, + BuiltInLaunchIdKHR = 5319, + BuiltInLaunchIdNV = 5319, + BuiltInLaunchSizeKHR = 5320, + BuiltInLaunchSizeNV = 5320, + BuiltInWorldRayOriginKHR = 5321, + BuiltInWorldRayOriginNV = 5321, + BuiltInWorldRayDirectionKHR = 5322, + BuiltInWorldRayDirectionNV = 5322, + BuiltInObjectRayOriginKHR = 5323, + BuiltInObjectRayOriginNV = 5323, + BuiltInObjectRayDirectionKHR = 5324, + BuiltInObjectRayDirectionNV = 5324, + BuiltInRayTminKHR = 5325, + BuiltInRayTminNV = 5325, + BuiltInRayTmaxKHR = 5326, + BuiltInRayTmaxNV = 5326, + BuiltInInstanceCustomIndexKHR = 5327, + BuiltInInstanceCustomIndexNV = 5327, + BuiltInObjectToWorldKHR = 5330, + BuiltInObjectToWorldNV = 5330, + BuiltInWorldToObjectKHR = 5331, + BuiltInWorldToObjectNV = 5331, + BuiltInHitTKHR = 5332, + BuiltInHitTNV = 5332, + BuiltInHitKindKHR = 5333, + BuiltInHitKindNV = 5333, + BuiltInIncomingRayFlagsKHR = 5351, + BuiltInIncomingRayFlagsNV = 5351, + BuiltInRayGeometryIndexKHR = 5352, + BuiltInWarpsPerSMNV = 5374, + BuiltInSMCountNV = 5375, + BuiltInWarpIDNV = 5376, + BuiltInSMIDNV = 5377, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMinIterationsShift = 4, + LoopControlMaxIterationsShift = 5, + LoopControlIterationMultipleShift = 6, + LoopControlPeelCountShift = 7, + LoopControlPartialCountShift = 8, + LoopControlInitiationIntervalINTELShift = 16, + LoopControlMaxConcurrencyINTELShift = 17, + LoopControlDependencyArrayINTELShift = 18, + LoopControlPipelineEnableINTELShift = 19, + LoopControlLoopCoalesceINTELShift = 20, + LoopControlMaxInterleavingINTELShift = 21, + LoopControlSpeculatedIterationsINTELShift = 22, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, + LoopControlMinIterationsMask = 0x00000010, + LoopControlMaxIterationsMask = 0x00000020, + LoopControlIterationMultipleMask = 0x00000040, + LoopControlPeelCountMask = 0x00000080, + LoopControlPartialCountMask = 0x00000100, + LoopControlInitiationIntervalINTELMask = 0x00010000, + LoopControlMaxConcurrencyINTELMask = 0x00020000, + LoopControlDependencyArrayINTELMask = 0x00040000, + LoopControlPipelineEnableINTELMask = 0x00080000, + LoopControlLoopCoalesceINTELMask = 0x00100000, + LoopControlMaxInterleavingINTELMask = 0x00200000, + LoopControlSpeculatedIterationsINTELMask = 0x00400000, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsOutputMemoryShift = 12, + MemorySemanticsOutputMemoryKHRShift = 12, + MemorySemanticsMakeAvailableShift = 13, + MemorySemanticsMakeAvailableKHRShift = 13, + MemorySemanticsMakeVisibleShift = 14, + MemorySemanticsMakeVisibleKHRShift = 14, + MemorySemanticsVolatileShift = 15, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, + MemorySemanticsOutputMemoryMask = 0x00001000, + MemorySemanticsOutputMemoryKHRMask = 0x00001000, + MemorySemanticsMakeAvailableMask = 0x00002000, + MemorySemanticsMakeAvailableKHRMask = 0x00002000, + MemorySemanticsMakeVisibleMask = 0x00004000, + MemorySemanticsMakeVisibleKHRMask = 0x00004000, + MemorySemanticsVolatileMask = 0x00008000, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMakePointerAvailableShift = 3, + MemoryAccessMakePointerAvailableKHRShift = 3, + MemoryAccessMakePointerVisibleShift = 4, + MemoryAccessMakePointerVisibleKHRShift = 4, + MemoryAccessNonPrivatePointerShift = 5, + MemoryAccessNonPrivatePointerKHRShift = 5, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, + MemoryAccessMakePointerAvailableMask = 0x00000008, + MemoryAccessMakePointerAvailableKHRMask = 0x00000008, + MemoryAccessMakePointerVisibleMask = 0x00000010, + MemoryAccessMakePointerVisibleKHRMask = 0x00000010, + MemoryAccessNonPrivatePointerMask = 0x00000020, + MemoryAccessNonPrivatePointerKHRMask = 0x00000020, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeQueueFamily = 5, + ScopeQueueFamilyKHR = 5, + ScopeShaderCallKHR = 6, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationClusteredReduce = 3, + GroupOperationPartitionedReduceNV = 6, + GroupOperationPartitionedInclusiveScanNV = 7, + GroupOperationPartitionedExclusiveScanNV = 8, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilityGroupNonUniform = 61, + CapabilityGroupNonUniformVote = 62, + CapabilityGroupNonUniformArithmetic = 63, + CapabilityGroupNonUniformBallot = 64, + CapabilityGroupNonUniformShuffle = 65, + CapabilityGroupNonUniformShuffleRelative = 66, + CapabilityGroupNonUniformClustered = 67, + CapabilityGroupNonUniformQuad = 68, + CapabilityShaderLayer = 69, + CapabilityShaderViewportIndex = 70, + CapabilityFragmentShadingRateKHR = 4422, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityStorageBuffer8BitAccess = 4448, + CapabilityUniformAndStorageBuffer8BitAccess = 4449, + CapabilityStoragePushConstant8 = 4450, + CapabilityDenormPreserve = 4464, + CapabilityDenormFlushToZero = 4465, + CapabilitySignedZeroInfNanPreserve = 4466, + CapabilityRoundingModeRTE = 4467, + CapabilityRoundingModeRTZ = 4468, + CapabilityRayQueryProvisionalKHR = 4471, + CapabilityRayTraversalPrimitiveCullingProvisionalKHR = 4478, + CapabilityFloat16ImageAMD = 5008, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilityInt64ImageEXT = 5016, + CapabilityShaderClockKHR = 5055, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilityFragmentFullyCoveredEXT = 5265, + CapabilityMeshShadingNV = 5266, + CapabilityImageFootprintNV = 5282, + CapabilityFragmentBarycentricNV = 5284, + CapabilityComputeDerivativeGroupQuadsNV = 5288, + CapabilityFragmentDensityEXT = 5291, + CapabilityShadingRateNV = 5291, + CapabilityGroupNonUniformPartitionedNV = 5297, + CapabilityShaderNonUniform = 5301, + CapabilityShaderNonUniformEXT = 5301, + CapabilityRuntimeDescriptorArray = 5302, + CapabilityRuntimeDescriptorArrayEXT = 5302, + CapabilityInputAttachmentArrayDynamicIndexing = 5303, + CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + CapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + CapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + CapabilityUniformBufferArrayNonUniformIndexing = 5306, + CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + CapabilitySampledImageArrayNonUniformIndexing = 5307, + CapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + CapabilityStorageBufferArrayNonUniformIndexing = 5308, + CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + CapabilityStorageImageArrayNonUniformIndexing = 5309, + CapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + CapabilityInputAttachmentArrayNonUniformIndexing = 5310, + CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + CapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + CapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + CapabilityRayTracingNV = 5340, + CapabilityVulkanMemoryModel = 5345, + CapabilityVulkanMemoryModelKHR = 5345, + CapabilityVulkanMemoryModelDeviceScope = 5346, + CapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + CapabilityPhysicalStorageBufferAddresses = 5347, + CapabilityPhysicalStorageBufferAddressesEXT = 5347, + CapabilityComputeDerivativeGroupLinearNV = 5350, + CapabilityRayTracingProvisionalKHR = 5353, + CapabilityCooperativeMatrixNV = 5357, + CapabilityFragmentShaderSampleInterlockEXT = 5363, + CapabilityFragmentShaderShadingRateInterlockEXT = 5372, + CapabilityShaderSMBuiltinsNV = 5373, + CapabilityFragmentShaderPixelInterlockEXT = 5378, + CapabilityDemoteToHelperInvocationEXT = 5379, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilitySubgroupImageMediaBlockIOINTEL = 5579, + CapabilityIntegerFunctions2INTEL = 5584, + CapabilityFunctionPointersINTEL = 5603, + CapabilityIndirectReferencesINTEL = 5604, + CapabilitySubgroupAvcMotionEstimationINTEL = 5696, + CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + CapabilityFPGAMemoryAttributesINTEL = 5824, + CapabilityUnstructuredLoopControlsINTEL = 5886, + CapabilityFPGALoopControlsINTEL = 5888, + CapabilityKernelAttributesINTEL = 5892, + CapabilityFPGAKernelAttributesINTEL = 5897, + CapabilityBlockingPipesINTEL = 5945, + CapabilityFPGARegINTEL = 5948, + CapabilityAtomicFloat32AddEXT = 6033, + CapabilityAtomicFloat64AddEXT = 6034, + CapabilityMax = 0x7fffffff, +}; + +enum RayFlagsShift { + RayFlagsOpaqueKHRShift = 0, + RayFlagsNoOpaqueKHRShift = 1, + RayFlagsTerminateOnFirstHitKHRShift = 2, + RayFlagsSkipClosestHitShaderKHRShift = 3, + RayFlagsCullBackFacingTrianglesKHRShift = 4, + RayFlagsCullFrontFacingTrianglesKHRShift = 5, + RayFlagsCullOpaqueKHRShift = 6, + RayFlagsCullNoOpaqueKHRShift = 7, + RayFlagsSkipTrianglesKHRShift = 8, + RayFlagsSkipAABBsKHRShift = 9, + RayFlagsMax = 0x7fffffff, +}; + +enum RayFlagsMask { + RayFlagsMaskNone = 0, + RayFlagsOpaqueKHRMask = 0x00000001, + RayFlagsNoOpaqueKHRMask = 0x00000002, + RayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + RayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + RayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + RayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + RayFlagsCullOpaqueKHRMask = 0x00000040, + RayFlagsCullNoOpaqueKHRMask = 0x00000080, + RayFlagsSkipTrianglesKHRMask = 0x00000100, + RayFlagsSkipAABBsKHRMask = 0x00000200, +}; + +enum RayQueryIntersection { + RayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + RayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + RayQueryIntersectionMax = 0x7fffffff, +}; + +enum RayQueryCommittedIntersectionType { + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + RayQueryCommittedIntersectionTypeMax = 0x7fffffff, +}; + +enum RayQueryCandidateIntersectionType { + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + RayQueryCandidateIntersectionTypeMax = 0x7fffffff, +}; + +enum FragmentShadingRateShift { + FragmentShadingRateVertical2PixelsShift = 0, + FragmentShadingRateVertical4PixelsShift = 1, + FragmentShadingRateHorizontal2PixelsShift = 2, + FragmentShadingRateHorizontal4PixelsShift = 3, + FragmentShadingRateMax = 0x7fffffff, +}; + +enum FragmentShadingRateMask { + FragmentShadingRateMaskNone = 0, + FragmentShadingRateVertical2PixelsMask = 0x00000001, + FragmentShadingRateVertical4PixelsMask = 0x00000002, + FragmentShadingRateHorizontal2PixelsMask = 0x00000004, + FragmentShadingRateHorizontal4PixelsMask = 0x00000008, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpTerminateInvocation = 4416, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpLoopControlINTEL = 5887, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpMax = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case OpNop: *hasResult = false; *hasResultType = false; break; + case OpUndef: *hasResult = true; *hasResultType = true; break; + case OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case OpSource: *hasResult = false; *hasResultType = false; break; + case OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case OpName: *hasResult = false; *hasResultType = false; break; + case OpMemberName: *hasResult = false; *hasResultType = false; break; + case OpString: *hasResult = true; *hasResultType = false; break; + case OpLine: *hasResult = false; *hasResultType = false; break; + case OpExtension: *hasResult = false; *hasResultType = false; break; + case OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case OpExtInst: *hasResult = true; *hasResultType = true; break; + case OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case OpCapability: *hasResult = false; *hasResultType = false; break; + case OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case OpTypeBool: *hasResult = true; *hasResultType = false; break; + case OpTypeInt: *hasResult = true; *hasResultType = false; break; + case OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case OpTypeVector: *hasResult = true; *hasResultType = false; break; + case OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case OpTypeImage: *hasResult = true; *hasResultType = false; break; + case OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case OpTypeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case OpTypePointer: *hasResult = true; *hasResultType = false; break; + case OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case OpTypePipe: *hasResult = true; *hasResultType = false; break; + case OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpConstant: *hasResult = true; *hasResultType = true; break; + case OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case OpConstantNull: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case OpFunction: *hasResult = true; *hasResultType = true; break; + case OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case OpVariable: *hasResult = true; *hasResultType = true; break; + case OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case OpLoad: *hasResult = true; *hasResultType = true; break; + case OpStore: *hasResult = false; *hasResultType = false; break; + case OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case OpAccessChain: *hasResult = true; *hasResultType = true; break; + case OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpArrayLength: *hasResult = true; *hasResultType = true; break; + case OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpDecorate: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case OpCopyObject: *hasResult = true; *hasResultType = true; break; + case OpTranspose: *hasResult = true; *hasResultType = true; break; + case OpSampledImage: *hasResult = true; *hasResultType = true; break; + case OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageFetch: *hasResult = true; *hasResultType = true; break; + case OpImageGather: *hasResult = true; *hasResultType = true; break; + case OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageRead: *hasResult = true; *hasResultType = true; break; + case OpImageWrite: *hasResult = false; *hasResultType = false; break; + case OpImage: *hasResult = true; *hasResultType = true; break; + case OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case OpUConvert: *hasResult = true; *hasResultType = true; break; + case OpSConvert: *hasResult = true; *hasResultType = true; break; + case OpFConvert: *hasResult = true; *hasResultType = true; break; + case OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case OpBitcast: *hasResult = true; *hasResultType = true; break; + case OpSNegate: *hasResult = true; *hasResultType = true; break; + case OpFNegate: *hasResult = true; *hasResultType = true; break; + case OpIAdd: *hasResult = true; *hasResultType = true; break; + case OpFAdd: *hasResult = true; *hasResultType = true; break; + case OpISub: *hasResult = true; *hasResultType = true; break; + case OpFSub: *hasResult = true; *hasResultType = true; break; + case OpIMul: *hasResult = true; *hasResultType = true; break; + case OpFMul: *hasResult = true; *hasResultType = true; break; + case OpUDiv: *hasResult = true; *hasResultType = true; break; + case OpSDiv: *hasResult = true; *hasResultType = true; break; + case OpFDiv: *hasResult = true; *hasResultType = true; break; + case OpUMod: *hasResult = true; *hasResultType = true; break; + case OpSRem: *hasResult = true; *hasResultType = true; break; + case OpSMod: *hasResult = true; *hasResultType = true; break; + case OpFRem: *hasResult = true; *hasResultType = true; break; + case OpFMod: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case OpDot: *hasResult = true; *hasResultType = true; break; + case OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case OpAny: *hasResult = true; *hasResultType = true; break; + case OpAll: *hasResult = true; *hasResultType = true; break; + case OpIsNan: *hasResult = true; *hasResultType = true; break; + case OpIsInf: *hasResult = true; *hasResultType = true; break; + case OpIsFinite: *hasResult = true; *hasResultType = true; break; + case OpIsNormal: *hasResult = true; *hasResultType = true; break; + case OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case OpOrdered: *hasResult = true; *hasResultType = true; break; + case OpUnordered: *hasResult = true; *hasResultType = true; break; + case OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case OpSelect: *hasResult = true; *hasResultType = true; break; + case OpIEqual: *hasResult = true; *hasResultType = true; break; + case OpINotEqual: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpULessThan: *hasResult = true; *hasResultType = true; break; + case OpSLessThan: *hasResult = true; *hasResultType = true; break; + case OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpNot: *hasResult = true; *hasResultType = true; break; + case OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case OpBitReverse: *hasResult = true; *hasResultType = true; break; + case OpBitCount: *hasResult = true; *hasResultType = true; break; + case OpDPdx: *hasResult = true; *hasResultType = true; break; + case OpDPdy: *hasResult = true; *hasResultType = true; break; + case OpFwidth: *hasResult = true; *hasResultType = true; break; + case OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case OpPhi: *hasResult = true; *hasResultType = true; break; + case OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case OpLabel: *hasResult = true; *hasResultType = false; break; + case OpBranch: *hasResult = false; *hasResultType = false; break; + case OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case OpSwitch: *hasResult = false; *hasResultType = false; break; + case OpKill: *hasResult = false; *hasResultType = false; break; + case OpReturn: *hasResult = false; *hasResultType = false; break; + case OpReturnValue: *hasResult = false; *hasResultType = false; break; + case OpUnreachable: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case OpGroupAll: *hasResult = true; *hasResultType = true; break; + case OpGroupAny: *hasResult = true; *hasResultType = true; break; + case OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case OpReadPipe: *hasResult = true; *hasResultType = true; break; + case OpWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case OpNoLine: *hasResult = false; *hasResultType = false; break; + case OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case OpSizeOf: *hasResult = true; *hasResultType = true; break; + case OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case OpDecorateId: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case OpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case OpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case OpTraceNV: *hasResult = false; *hasResultType = false; break; + case OpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; + case OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case OpDecorateString: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case OpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case OpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case OpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case OpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case OpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } +inline FragmentShadingRateMask operator|(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 new file mode 100644 index 0000000..1e7d12e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.hpp11 @@ -0,0 +1,2196 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10500 +#define SPV_REVISION 4 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010500; +static const unsigned int Revision = 4; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum class SourceLanguage : unsigned { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + Max = 0x7fffffff, +}; + +enum class ExecutionModel : unsigned { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, + Max = 0x7fffffff, +}; + +enum class AddressingModel : unsigned { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + Max = 0x7fffffff, +}; + +enum class MemoryModel : unsigned { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + Max = 0x7fffffff, +}; + +enum class ExecutionMode : unsigned { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + StencilRefReplacingEXT = 5027, + OutputLinesNV = 5269, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearNV = 5290, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + Max = 0x7fffffff, +}; + +enum class StorageClass : unsigned { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + CodeSectionINTEL = 5605, + Max = 0x7fffffff, +}; + +enum class Dim : unsigned { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + Max = 0x7fffffff, +}; + +enum class SamplerAddressingMode : unsigned { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + Max = 0x7fffffff, +}; + +enum class SamplerFilterMode : unsigned { + Nearest = 0, + Linear = 1, + Max = 0x7fffffff, +}; + +enum class ImageFormat : unsigned { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + Max = 0x7fffffff, +}; + +enum class ImageChannelOrder : unsigned { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + Max = 0x7fffffff, +}; + +enum class ImageChannelDataType : unsigned { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + Max = 0x7fffffff, +}; + +enum class ImageOperandsShift : unsigned { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, + Max = 0x7fffffff, +}; + +enum class ImageOperandsMask : unsigned { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, +}; + +enum class FPFastMathModeShift : unsigned { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + Max = 0x7fffffff, +}; + +enum class FPFastMathModeMask : unsigned { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, +}; + +enum class FPRoundingMode : unsigned { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + Max = 0x7fffffff, +}; + +enum class LinkageType : unsigned { + Export = 0, + Import = 1, + Max = 0x7fffffff, +}; + +enum class AccessQualifier : unsigned { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + Max = 0x7fffffff, +}; + +enum class FunctionParameterAttribute : unsigned { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + Max = 0x7fffffff, +}; + +enum class Decoration : unsigned { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + ReferencedIndirectlyINTEL = 5602, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + Max = 0x7fffffff, +}; + +enum class BuiltIn : unsigned { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordNV = 5286, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTKHR = 5332, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + Max = 0x7fffffff, +}; + +enum class SelectionControlShift : unsigned { + Flatten = 0, + DontFlatten = 1, + Max = 0x7fffffff, +}; + +enum class SelectionControlMask : unsigned { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +}; + +enum class LoopControlShift : unsigned { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, + Max = 0x7fffffff, +}; + +enum class LoopControlMask : unsigned { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, +}; + +enum class FunctionControlShift : unsigned { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + Max = 0x7fffffff, +}; + +enum class FunctionControlMask : unsigned { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, +}; + +enum class MemorySemanticsShift : unsigned { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, + Max = 0x7fffffff, +}; + +enum class MemorySemanticsMask : unsigned { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, +}; + +enum class MemoryAccessShift : unsigned { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, + Max = 0x7fffffff, +}; + +enum class MemoryAccessMask : unsigned { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, +}; + +enum class Scope : unsigned { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + Max = 0x7fffffff, +}; + +enum class GroupOperation : unsigned { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + Max = 0x7fffffff, +}; + +enum class KernelEnqueueFlags : unsigned { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoShift : unsigned { + CmdExecTime = 0, + Max = 0x7fffffff, +}; + +enum class KernelProfilingInfoMask : unsigned { + MaskNone = 0, + CmdExecTime = 0x00000001, +}; + +enum class Capability : unsigned { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayTraversalPrimitiveCullingProvisionalKHR = 4478, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingNV = 5340, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocationEXT = 5379, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + FPGAMemoryAttributesINTEL = 5824, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + Max = 0x7fffffff, +}; + +enum class RayFlagsShift : unsigned { + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, + Max = 0x7fffffff, +}; + +enum class RayFlagsMask : unsigned { + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, +}; + +enum class RayQueryIntersection : unsigned { + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + Max = 0x7fffffff, +}; + +enum class RayQueryCommittedIntersectionType : unsigned { + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + Max = 0x7fffffff, +}; + +enum class RayQueryCandidateIntersectionType : unsigned { + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + Max = 0x7fffffff, +}; + +enum class FragmentShadingRateShift : unsigned { + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, + Max = 0x7fffffff, +}; + +enum class FragmentShadingRateMask : unsigned { + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, +}; + +enum class Op : unsigned { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpTerminateInvocation = 4416, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpLoopControlINTEL = 5887, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + Max = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case Op::OpNop: *hasResult = false; *hasResultType = false; break; + case Op::OpUndef: *hasResult = true; *hasResultType = true; break; + case Op::OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case Op::OpSource: *hasResult = false; *hasResultType = false; break; + case Op::OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case Op::OpName: *hasResult = false; *hasResultType = false; break; + case Op::OpMemberName: *hasResult = false; *hasResultType = false; break; + case Op::OpString: *hasResult = true; *hasResultType = false; break; + case Op::OpLine: *hasResult = false; *hasResultType = false; break; + case Op::OpExtension: *hasResult = false; *hasResultType = false; break; + case Op::OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case Op::OpExtInst: *hasResult = true; *hasResultType = true; break; + case Op::OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case Op::OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case Op::OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case Op::OpCapability: *hasResult = false; *hasResultType = false; break; + case Op::OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeBool: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeInt: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeVector: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeImage: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeArray: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case Op::OpTypePointer: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case Op::OpTypePipe: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case Op::OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case Op::OpConstant: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case Op::OpConstantNull: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case Op::OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case Op::OpFunction: *hasResult = true; *hasResultType = true; break; + case Op::OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case Op::OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case Op::OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case Op::OpVariable: *hasResult = true; *hasResultType = true; break; + case Op::OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case Op::OpLoad: *hasResult = true; *hasResultType = true; break; + case Op::OpStore: *hasResult = false; *hasResultType = false; break; + case Op::OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case Op::OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case Op::OpAccessChain: *hasResult = true; *hasResultType = true; break; + case Op::OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case Op::OpArrayLength: *hasResult = true; *hasResultType = true; break; + case Op::OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case Op::OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case Op::OpDecorate: *hasResult = false; *hasResultType = false; break; + case Op::OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case Op::OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case Op::OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case Op::OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case Op::OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case Op::OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case Op::OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case Op::OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case Op::OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case Op::OpCopyObject: *hasResult = true; *hasResultType = true; break; + case Op::OpTranspose: *hasResult = true; *hasResultType = true; break; + case Op::OpSampledImage: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageFetch: *hasResult = true; *hasResultType = true; break; + case Op::OpImageGather: *hasResult = true; *hasResultType = true; break; + case Op::OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case Op::OpImageRead: *hasResult = true; *hasResultType = true; break; + case Op::OpImageWrite: *hasResult = false; *hasResultType = false; break; + case Op::OpImage: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case Op::OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case Op::OpUConvert: *hasResult = true; *hasResultType = true; break; + case Op::OpSConvert: *hasResult = true; *hasResultType = true; break; + case Op::OpFConvert: *hasResult = true; *hasResultType = true; break; + case Op::OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case Op::OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case Op::OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case Op::OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case Op::OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case Op::OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case Op::OpBitcast: *hasResult = true; *hasResultType = true; break; + case Op::OpSNegate: *hasResult = true; *hasResultType = true; break; + case Op::OpFNegate: *hasResult = true; *hasResultType = true; break; + case Op::OpIAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpFAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpISub: *hasResult = true; *hasResultType = true; break; + case Op::OpFSub: *hasResult = true; *hasResultType = true; break; + case Op::OpIMul: *hasResult = true; *hasResultType = true; break; + case Op::OpFMul: *hasResult = true; *hasResultType = true; break; + case Op::OpUDiv: *hasResult = true; *hasResultType = true; break; + case Op::OpSDiv: *hasResult = true; *hasResultType = true; break; + case Op::OpFDiv: *hasResult = true; *hasResultType = true; break; + case Op::OpUMod: *hasResult = true; *hasResultType = true; break; + case Op::OpSRem: *hasResult = true; *hasResultType = true; break; + case Op::OpSMod: *hasResult = true; *hasResultType = true; break; + case Op::OpFRem: *hasResult = true; *hasResultType = true; break; + case Op::OpFMod: *hasResult = true; *hasResultType = true; break; + case Op::OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case Op::OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case Op::OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case Op::OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case Op::OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case Op::OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case Op::OpDot: *hasResult = true; *hasResultType = true; break; + case Op::OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case Op::OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case Op::OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case Op::OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case Op::OpAny: *hasResult = true; *hasResultType = true; break; + case Op::OpAll: *hasResult = true; *hasResultType = true; break; + case Op::OpIsNan: *hasResult = true; *hasResultType = true; break; + case Op::OpIsInf: *hasResult = true; *hasResultType = true; break; + case Op::OpIsFinite: *hasResult = true; *hasResultType = true; break; + case Op::OpIsNormal: *hasResult = true; *hasResultType = true; break; + case Op::OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case Op::OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case Op::OpOrdered: *hasResult = true; *hasResultType = true; break; + case Op::OpUnordered: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case Op::OpSelect: *hasResult = true; *hasResultType = true; break; + case Op::OpIEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpINotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case Op::OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case Op::OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpULessThan: *hasResult = true; *hasResultType = true; break; + case Op::OpSLessThan: *hasResult = true; *hasResultType = true; break; + case Op::OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case Op::OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case Op::OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case Op::OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case Op::OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case Op::OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpNot: *hasResult = true; *hasResultType = true; break; + case Op::OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case Op::OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case Op::OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case Op::OpBitReverse: *hasResult = true; *hasResultType = true; break; + case Op::OpBitCount: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdx: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdy: *hasResult = true; *hasResultType = true; break; + case Op::OpFwidth: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case Op::OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case Op::OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case Op::OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case Op::OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case Op::OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case Op::OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case Op::OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case Op::OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case Op::OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case Op::OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case Op::OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case Op::OpPhi: *hasResult = true; *hasResultType = true; break; + case Op::OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case Op::OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case Op::OpLabel: *hasResult = true; *hasResultType = false; break; + case Op::OpBranch: *hasResult = false; *hasResultType = false; break; + case Op::OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case Op::OpSwitch: *hasResult = false; *hasResultType = false; break; + case Op::OpKill: *hasResult = false; *hasResultType = false; break; + case Op::OpReturn: *hasResult = false; *hasResultType = false; break; + case Op::OpReturnValue: *hasResult = false; *hasResultType = false; break; + case Op::OpUnreachable: *hasResult = false; *hasResultType = false; break; + case Op::OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case Op::OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupAll: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupAny: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case Op::OpReadPipe: *hasResult = true; *hasResultType = true; break; + case Op::OpWritePipe: *hasResult = true; *hasResultType = true; break; + case Op::OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case Op::OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case Op::OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case Op::OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case Op::OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case Op::OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case Op::OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case Op::OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case Op::OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case Op::OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case Op::OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case Op::OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case Op::OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case Op::OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case Op::OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case Op::OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case Op::OpNoLine: *hasResult = false; *hasResultType = false; break; + case Op::OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case Op::OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case Op::OpSizeOf: *hasResult = true; *hasResultType = true; break; + case Op::OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case Op::OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case Op::OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case Op::OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case Op::OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case Op::OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case Op::OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case Op::OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case Op::OpDecorateId: *hasResult = false; *hasResultType = false; break; + case Op::OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case Op::OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case Op::OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case Op::OpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeRayQueryProvisionalKHR: *hasResult = true; *hasResultType = false; break; + case Op::OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case Op::OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case Op::OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case Op::OpGroupNonUniformPartitionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case Op::OpReportIntersectionNV: *hasResult = true; *hasResultType = true; break; + case Op::OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTraceNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTypeAccelerationStructureNV: *hasResult = true; *hasResultType = false; break; + case Op::OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case Op::OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case Op::OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case Op::OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case Op::OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case Op::OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case Op::OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case Op::OpDemoteToHelperInvocationEXT: *hasResult = false; *hasResultType = false; break; + case Op::OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpDecorateString: *hasResult = false; *hasResultType = false; break; + case Op::OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case Op::OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case Op::OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case Op::OpReadPipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpWritePipeBlockingINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpFPGARegINTEL: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case Op::OpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + } +} +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload operator| for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } +inline FragmentShadingRateMask operator|(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) | unsigned(b)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json new file mode 100644 index 0000000..8353274 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.json @@ -0,0 +1,1604 @@ +{ + "spv": + { + "meta": + { + "Comment": + [ + [ + "Copyright (c) 2014-2020 The Khronos Group Inc.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and/or associated documentation files (the \"Materials\"),", + "to deal in the Materials without restriction, including without limitation", + "the rights to use, copy, modify, merge, publish, distribute, sublicense,", + "and/or sell copies of the Materials, and to permit persons to whom the", + "Materials are furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Materials.", + "", + "MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS", + "STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND", + "HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ ", + "", + "THE MATERIALS ARE PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS", + "OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL", + "THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING", + "FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS", + "IN THE MATERIALS." + ], + [ + "This header is automatically generated by the same tool that creates", + "the Binary Section of the SPIR-V specification." + ], + [ + "Enumeration tokens for SPIR-V, in various styles:", + " C, C++, C++11, JSON, Lua, Python, C#, D", + "", + "- C will have tokens with a \"Spv\" prefix, e.g.: SpvSourceLanguageGLSL", + "- C++ will have tokens in the \"spv\" name space, e.g.: spv::SourceLanguageGLSL", + "- C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL", + "- Lua will use tables, e.g.: spv.SourceLanguage.GLSL", + "- Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']", + "- C# will use enum classes in the Specification class located in the \"Spv\" namespace,", + " e.g.: Spv.Specification.SourceLanguage.GLSL", + "- D will have tokens under the \"spv\" module, e.g: spv.SourceLanguage.GLSL", + "", + "Some tokens act like mask values, which can be OR'd together,", + "while others are mutually exclusive. The mask-like ones have", + "\"Mask\" in their name, and a parallel enum that has the shift", + "amount (1 << x) for each corresponding enumerant." + ] + ], + "MagicNumber": 119734787, + "Version": 66816, + "Revision": 4, + "OpCodeMask": 65535, + "WordCountShift": 16 + }, + "enum": + [ + { + "Name": "SourceLanguage", + "Type": "Value", + "Values": + { + "Unknown": 0, + "ESSL": 1, + "GLSL": 2, + "OpenCL_C": 3, + "OpenCL_CPP": 4, + "HLSL": 5 + } + }, + { + "Name": "ExecutionModel", + "Type": "Value", + "Values": + { + "Vertex": 0, + "TessellationControl": 1, + "TessellationEvaluation": 2, + "Geometry": 3, + "Fragment": 4, + "GLCompute": 5, + "Kernel": 6, + "TaskNV": 5267, + "MeshNV": 5268, + "RayGenerationKHR": 5313, + "RayGenerationNV": 5313, + "IntersectionKHR": 5314, + "IntersectionNV": 5314, + "AnyHitKHR": 5315, + "AnyHitNV": 5315, + "ClosestHitKHR": 5316, + "ClosestHitNV": 5316, + "MissKHR": 5317, + "MissNV": 5317, + "CallableKHR": 5318, + "CallableNV": 5318 + } + }, + { + "Name": "AddressingModel", + "Type": "Value", + "Values": + { + "Logical": 0, + "Physical32": 1, + "Physical64": 2, + "PhysicalStorageBuffer64": 5348, + "PhysicalStorageBuffer64EXT": 5348 + } + }, + { + "Name": "MemoryModel", + "Type": "Value", + "Values": + { + "Simple": 0, + "GLSL450": 1, + "OpenCL": 2, + "Vulkan": 3, + "VulkanKHR": 3 + } + }, + { + "Name": "ExecutionMode", + "Type": "Value", + "Values": + { + "Invocations": 0, + "SpacingEqual": 1, + "SpacingFractionalEven": 2, + "SpacingFractionalOdd": 3, + "VertexOrderCw": 4, + "VertexOrderCcw": 5, + "PixelCenterInteger": 6, + "OriginUpperLeft": 7, + "OriginLowerLeft": 8, + "EarlyFragmentTests": 9, + "PointMode": 10, + "Xfb": 11, + "DepthReplacing": 12, + "DepthGreater": 14, + "DepthLess": 15, + "DepthUnchanged": 16, + "LocalSize": 17, + "LocalSizeHint": 18, + "InputPoints": 19, + "InputLines": 20, + "InputLinesAdjacency": 21, + "Triangles": 22, + "InputTrianglesAdjacency": 23, + "Quads": 24, + "Isolines": 25, + "OutputVertices": 26, + "OutputPoints": 27, + "OutputLineStrip": 28, + "OutputTriangleStrip": 29, + "VecTypeHint": 30, + "ContractionOff": 31, + "Initializer": 33, + "Finalizer": 34, + "SubgroupSize": 35, + "SubgroupsPerWorkgroup": 36, + "SubgroupsPerWorkgroupId": 37, + "LocalSizeId": 38, + "LocalSizeHintId": 39, + "PostDepthCoverage": 4446, + "DenormPreserve": 4459, + "DenormFlushToZero": 4460, + "SignedZeroInfNanPreserve": 4461, + "RoundingModeRTE": 4462, + "RoundingModeRTZ": 4463, + "StencilRefReplacingEXT": 5027, + "OutputLinesNV": 5269, + "OutputPrimitivesNV": 5270, + "DerivativeGroupQuadsNV": 5289, + "DerivativeGroupLinearNV": 5290, + "OutputTrianglesNV": 5298, + "PixelInterlockOrderedEXT": 5366, + "PixelInterlockUnorderedEXT": 5367, + "SampleInterlockOrderedEXT": 5368, + "SampleInterlockUnorderedEXT": 5369, + "ShadingRateInterlockOrderedEXT": 5370, + "ShadingRateInterlockUnorderedEXT": 5371, + "MaxWorkgroupSizeINTEL": 5893, + "MaxWorkDimINTEL": 5894, + "NoGlobalOffsetINTEL": 5895, + "NumSIMDWorkitemsINTEL": 5896 + } + }, + { + "Name": "StorageClass", + "Type": "Value", + "Values": + { + "UniformConstant": 0, + "Input": 1, + "Uniform": 2, + "Output": 3, + "Workgroup": 4, + "CrossWorkgroup": 5, + "Private": 6, + "Function": 7, + "Generic": 8, + "PushConstant": 9, + "AtomicCounter": 10, + "Image": 11, + "StorageBuffer": 12, + "CallableDataKHR": 5328, + "CallableDataNV": 5328, + "IncomingCallableDataKHR": 5329, + "IncomingCallableDataNV": 5329, + "RayPayloadKHR": 5338, + "RayPayloadNV": 5338, + "HitAttributeKHR": 5339, + "HitAttributeNV": 5339, + "IncomingRayPayloadKHR": 5342, + "IncomingRayPayloadNV": 5342, + "ShaderRecordBufferKHR": 5343, + "ShaderRecordBufferNV": 5343, + "PhysicalStorageBuffer": 5349, + "PhysicalStorageBufferEXT": 5349, + "CodeSectionINTEL": 5605 + } + }, + { + "Name": "Dim", + "Type": "Value", + "Values": + { + "Dim1D": 0, + "Dim2D": 1, + "Dim3D": 2, + "Cube": 3, + "Rect": 4, + "Buffer": 5, + "SubpassData": 6 + } + }, + { + "Name": "SamplerAddressingMode", + "Type": "Value", + "Values": + { + "None": 0, + "ClampToEdge": 1, + "Clamp": 2, + "Repeat": 3, + "RepeatMirrored": 4 + } + }, + { + "Name": "SamplerFilterMode", + "Type": "Value", + "Values": + { + "Nearest": 0, + "Linear": 1 + } + }, + { + "Name": "ImageFormat", + "Type": "Value", + "Values": + { + "Unknown": 0, + "Rgba32f": 1, + "Rgba16f": 2, + "R32f": 3, + "Rgba8": 4, + "Rgba8Snorm": 5, + "Rg32f": 6, + "Rg16f": 7, + "R11fG11fB10f": 8, + "R16f": 9, + "Rgba16": 10, + "Rgb10A2": 11, + "Rg16": 12, + "Rg8": 13, + "R16": 14, + "R8": 15, + "Rgba16Snorm": 16, + "Rg16Snorm": 17, + "Rg8Snorm": 18, + "R16Snorm": 19, + "R8Snorm": 20, + "Rgba32i": 21, + "Rgba16i": 22, + "Rgba8i": 23, + "R32i": 24, + "Rg32i": 25, + "Rg16i": 26, + "Rg8i": 27, + "R16i": 28, + "R8i": 29, + "Rgba32ui": 30, + "Rgba16ui": 31, + "Rgba8ui": 32, + "R32ui": 33, + "Rgb10a2ui": 34, + "Rg32ui": 35, + "Rg16ui": 36, + "Rg8ui": 37, + "R16ui": 38, + "R8ui": 39, + "R64ui": 40, + "R64i": 41 + } + }, + { + "Name": "ImageChannelOrder", + "Type": "Value", + "Values": + { + "R": 0, + "A": 1, + "RG": 2, + "RA": 3, + "RGB": 4, + "RGBA": 5, + "BGRA": 6, + "ARGB": 7, + "Intensity": 8, + "Luminance": 9, + "Rx": 10, + "RGx": 11, + "RGBx": 12, + "Depth": 13, + "DepthStencil": 14, + "sRGB": 15, + "sRGBx": 16, + "sRGBA": 17, + "sBGRA": 18, + "ABGR": 19 + } + }, + { + "Name": "ImageChannelDataType", + "Type": "Value", + "Values": + { + "SnormInt8": 0, + "SnormInt16": 1, + "UnormInt8": 2, + "UnormInt16": 3, + "UnormShort565": 4, + "UnormShort555": 5, + "UnormInt101010": 6, + "SignedInt8": 7, + "SignedInt16": 8, + "SignedInt32": 9, + "UnsignedInt8": 10, + "UnsignedInt16": 11, + "UnsignedInt32": 12, + "HalfFloat": 13, + "Float": 14, + "UnormInt24": 15, + "UnormInt101010_2": 16 + } + }, + { + "Name": "ImageOperands", + "Type": "Bit", + "Values": + { + "Bias": 0, + "Lod": 1, + "Grad": 2, + "ConstOffset": 3, + "Offset": 4, + "ConstOffsets": 5, + "Sample": 6, + "MinLod": 7, + "MakeTexelAvailable": 8, + "MakeTexelAvailableKHR": 8, + "MakeTexelVisible": 9, + "MakeTexelVisibleKHR": 9, + "NonPrivateTexel": 10, + "NonPrivateTexelKHR": 10, + "VolatileTexel": 11, + "VolatileTexelKHR": 11, + "SignExtend": 12, + "ZeroExtend": 13 + } + }, + { + "Name": "FPFastMathMode", + "Type": "Bit", + "Values": + { + "NotNaN": 0, + "NotInf": 1, + "NSZ": 2, + "AllowRecip": 3, + "Fast": 4 + } + }, + { + "Name": "FPRoundingMode", + "Type": "Value", + "Values": + { + "RTE": 0, + "RTZ": 1, + "RTP": 2, + "RTN": 3 + } + }, + { + "Name": "LinkageType", + "Type": "Value", + "Values": + { + "Export": 0, + "Import": 1 + } + }, + { + "Name": "AccessQualifier", + "Type": "Value", + "Values": + { + "ReadOnly": 0, + "WriteOnly": 1, + "ReadWrite": 2 + } + }, + { + "Name": "FunctionParameterAttribute", + "Type": "Value", + "Values": + { + "Zext": 0, + "Sext": 1, + "ByVal": 2, + "Sret": 3, + "NoAlias": 4, + "NoCapture": 5, + "NoWrite": 6, + "NoReadWrite": 7 + } + }, + { + "Name": "Decoration", + "Type": "Value", + "Values": + { + "RelaxedPrecision": 0, + "SpecId": 1, + "Block": 2, + "BufferBlock": 3, + "RowMajor": 4, + "ColMajor": 5, + "ArrayStride": 6, + "MatrixStride": 7, + "GLSLShared": 8, + "GLSLPacked": 9, + "CPacked": 10, + "BuiltIn": 11, + "NoPerspective": 13, + "Flat": 14, + "Patch": 15, + "Centroid": 16, + "Sample": 17, + "Invariant": 18, + "Restrict": 19, + "Aliased": 20, + "Volatile": 21, + "Constant": 22, + "Coherent": 23, + "NonWritable": 24, + "NonReadable": 25, + "Uniform": 26, + "UniformId": 27, + "SaturatedConversion": 28, + "Stream": 29, + "Location": 30, + "Component": 31, + "Index": 32, + "Binding": 33, + "DescriptorSet": 34, + "Offset": 35, + "XfbBuffer": 36, + "XfbStride": 37, + "FuncParamAttr": 38, + "FPRoundingMode": 39, + "FPFastMathMode": 40, + "LinkageAttributes": 41, + "NoContraction": 42, + "InputAttachmentIndex": 43, + "Alignment": 44, + "MaxByteOffset": 45, + "AlignmentId": 46, + "MaxByteOffsetId": 47, + "NoSignedWrap": 4469, + "NoUnsignedWrap": 4470, + "ExplicitInterpAMD": 4999, + "OverrideCoverageNV": 5248, + "PassthroughNV": 5250, + "ViewportRelativeNV": 5252, + "SecondaryViewportRelativeNV": 5256, + "PerPrimitiveNV": 5271, + "PerViewNV": 5272, + "PerTaskNV": 5273, + "PerVertexNV": 5285, + "NonUniform": 5300, + "NonUniformEXT": 5300, + "RestrictPointer": 5355, + "RestrictPointerEXT": 5355, + "AliasedPointer": 5356, + "AliasedPointerEXT": 5356, + "ReferencedIndirectlyINTEL": 5602, + "CounterBuffer": 5634, + "HlslCounterBufferGOOGLE": 5634, + "HlslSemanticGOOGLE": 5635, + "UserSemantic": 5635, + "UserTypeGOOGLE": 5636, + "RegisterINTEL": 5825, + "MemoryINTEL": 5826, + "NumbanksINTEL": 5827, + "BankwidthINTEL": 5828, + "MaxPrivateCopiesINTEL": 5829, + "SinglepumpINTEL": 5830, + "DoublepumpINTEL": 5831, + "MaxReplicatesINTEL": 5832, + "SimpleDualPortINTEL": 5833, + "MergeINTEL": 5834, + "BankBitsINTEL": 5835, + "ForcePow2DepthINTEL": 5836 + } + }, + { + "Name": "BuiltIn", + "Type": "Value", + "Values": + { + "Position": 0, + "PointSize": 1, + "ClipDistance": 3, + "CullDistance": 4, + "VertexId": 5, + "InstanceId": 6, + "PrimitiveId": 7, + "InvocationId": 8, + "Layer": 9, + "ViewportIndex": 10, + "TessLevelOuter": 11, + "TessLevelInner": 12, + "TessCoord": 13, + "PatchVertices": 14, + "FragCoord": 15, + "PointCoord": 16, + "FrontFacing": 17, + "SampleId": 18, + "SamplePosition": 19, + "SampleMask": 20, + "FragDepth": 22, + "HelperInvocation": 23, + "NumWorkgroups": 24, + "WorkgroupSize": 25, + "WorkgroupId": 26, + "LocalInvocationId": 27, + "GlobalInvocationId": 28, + "LocalInvocationIndex": 29, + "WorkDim": 30, + "GlobalSize": 31, + "EnqueuedWorkgroupSize": 32, + "GlobalOffset": 33, + "GlobalLinearId": 34, + "SubgroupSize": 36, + "SubgroupMaxSize": 37, + "NumSubgroups": 38, + "NumEnqueuedSubgroups": 39, + "SubgroupId": 40, + "SubgroupLocalInvocationId": 41, + "VertexIndex": 42, + "InstanceIndex": 43, + "SubgroupEqMask": 4416, + "SubgroupEqMaskKHR": 4416, + "SubgroupGeMask": 4417, + "SubgroupGeMaskKHR": 4417, + "SubgroupGtMask": 4418, + "SubgroupGtMaskKHR": 4418, + "SubgroupLeMask": 4419, + "SubgroupLeMaskKHR": 4419, + "SubgroupLtMask": 4420, + "SubgroupLtMaskKHR": 4420, + "BaseVertex": 4424, + "BaseInstance": 4425, + "DrawIndex": 4426, + "PrimitiveShadingRateKHR": 4432, + "DeviceIndex": 4438, + "ViewIndex": 4440, + "ShadingRateKHR": 4444, + "BaryCoordNoPerspAMD": 4992, + "BaryCoordNoPerspCentroidAMD": 4993, + "BaryCoordNoPerspSampleAMD": 4994, + "BaryCoordSmoothAMD": 4995, + "BaryCoordSmoothCentroidAMD": 4996, + "BaryCoordSmoothSampleAMD": 4997, + "BaryCoordPullModelAMD": 4998, + "FragStencilRefEXT": 5014, + "ViewportMaskNV": 5253, + "SecondaryPositionNV": 5257, + "SecondaryViewportMaskNV": 5258, + "PositionPerViewNV": 5261, + "ViewportMaskPerViewNV": 5262, + "FullyCoveredEXT": 5264, + "TaskCountNV": 5274, + "PrimitiveCountNV": 5275, + "PrimitiveIndicesNV": 5276, + "ClipDistancePerViewNV": 5277, + "CullDistancePerViewNV": 5278, + "LayerPerViewNV": 5279, + "MeshViewCountNV": 5280, + "MeshViewIndicesNV": 5281, + "BaryCoordNV": 5286, + "BaryCoordNoPerspNV": 5287, + "FragSizeEXT": 5292, + "FragmentSizeNV": 5292, + "FragInvocationCountEXT": 5293, + "InvocationsPerPixelNV": 5293, + "LaunchIdKHR": 5319, + "LaunchIdNV": 5319, + "LaunchSizeKHR": 5320, + "LaunchSizeNV": 5320, + "WorldRayOriginKHR": 5321, + "WorldRayOriginNV": 5321, + "WorldRayDirectionKHR": 5322, + "WorldRayDirectionNV": 5322, + "ObjectRayOriginKHR": 5323, + "ObjectRayOriginNV": 5323, + "ObjectRayDirectionKHR": 5324, + "ObjectRayDirectionNV": 5324, + "RayTminKHR": 5325, + "RayTminNV": 5325, + "RayTmaxKHR": 5326, + "RayTmaxNV": 5326, + "InstanceCustomIndexKHR": 5327, + "InstanceCustomIndexNV": 5327, + "ObjectToWorldKHR": 5330, + "ObjectToWorldNV": 5330, + "WorldToObjectKHR": 5331, + "WorldToObjectNV": 5331, + "HitTKHR": 5332, + "HitTNV": 5332, + "HitKindKHR": 5333, + "HitKindNV": 5333, + "IncomingRayFlagsKHR": 5351, + "IncomingRayFlagsNV": 5351, + "RayGeometryIndexKHR": 5352, + "WarpsPerSMNV": 5374, + "SMCountNV": 5375, + "WarpIDNV": 5376, + "SMIDNV": 5377 + } + }, + { + "Name": "SelectionControl", + "Type": "Bit", + "Values": + { + "Flatten": 0, + "DontFlatten": 1 + } + }, + { + "Name": "LoopControl", + "Type": "Bit", + "Values": + { + "Unroll": 0, + "DontUnroll": 1, + "DependencyInfinite": 2, + "DependencyLength": 3, + "MinIterations": 4, + "MaxIterations": 5, + "IterationMultiple": 6, + "PeelCount": 7, + "PartialCount": 8, + "InitiationIntervalINTEL": 16, + "MaxConcurrencyINTEL": 17, + "DependencyArrayINTEL": 18, + "PipelineEnableINTEL": 19, + "LoopCoalesceINTEL": 20, + "MaxInterleavingINTEL": 21, + "SpeculatedIterationsINTEL": 22 + } + }, + { + "Name": "FunctionControl", + "Type": "Bit", + "Values": + { + "Inline": 0, + "DontInline": 1, + "Pure": 2, + "Const": 3 + } + }, + { + "Name": "MemorySemantics", + "Type": "Bit", + "Values": + { + "Acquire": 1, + "Release": 2, + "AcquireRelease": 3, + "SequentiallyConsistent": 4, + "UniformMemory": 6, + "SubgroupMemory": 7, + "WorkgroupMemory": 8, + "CrossWorkgroupMemory": 9, + "AtomicCounterMemory": 10, + "ImageMemory": 11, + "OutputMemory": 12, + "OutputMemoryKHR": 12, + "MakeAvailable": 13, + "MakeAvailableKHR": 13, + "MakeVisible": 14, + "MakeVisibleKHR": 14, + "Volatile": 15 + } + }, + { + "Name": "MemoryAccess", + "Type": "Bit", + "Values": + { + "Volatile": 0, + "Aligned": 1, + "Nontemporal": 2, + "MakePointerAvailable": 3, + "MakePointerAvailableKHR": 3, + "MakePointerVisible": 4, + "MakePointerVisibleKHR": 4, + "NonPrivatePointer": 5, + "NonPrivatePointerKHR": 5 + } + }, + { + "Name": "Scope", + "Type": "Value", + "Values": + { + "CrossDevice": 0, + "Device": 1, + "Workgroup": 2, + "Subgroup": 3, + "Invocation": 4, + "QueueFamily": 5, + "QueueFamilyKHR": 5, + "ShaderCallKHR": 6 + } + }, + { + "Name": "GroupOperation", + "Type": "Value", + "Values": + { + "Reduce": 0, + "InclusiveScan": 1, + "ExclusiveScan": 2, + "ClusteredReduce": 3, + "PartitionedReduceNV": 6, + "PartitionedInclusiveScanNV": 7, + "PartitionedExclusiveScanNV": 8 + } + }, + { + "Name": "KernelEnqueueFlags", + "Type": "Value", + "Values": + { + "NoWait": 0, + "WaitKernel": 1, + "WaitWorkGroup": 2 + } + }, + { + "Name": "KernelProfilingInfo", + "Type": "Bit", + "Values": + { + "CmdExecTime": 0 + } + }, + { + "Name": "Capability", + "Type": "Value", + "Values": + { + "Matrix": 0, + "Shader": 1, + "Geometry": 2, + "Tessellation": 3, + "Addresses": 4, + "Linkage": 5, + "Kernel": 6, + "Vector16": 7, + "Float16Buffer": 8, + "Float16": 9, + "Float64": 10, + "Int64": 11, + "Int64Atomics": 12, + "ImageBasic": 13, + "ImageReadWrite": 14, + "ImageMipmap": 15, + "Pipes": 17, + "Groups": 18, + "DeviceEnqueue": 19, + "LiteralSampler": 20, + "AtomicStorage": 21, + "Int16": 22, + "TessellationPointSize": 23, + "GeometryPointSize": 24, + "ImageGatherExtended": 25, + "StorageImageMultisample": 27, + "UniformBufferArrayDynamicIndexing": 28, + "SampledImageArrayDynamicIndexing": 29, + "StorageBufferArrayDynamicIndexing": 30, + "StorageImageArrayDynamicIndexing": 31, + "ClipDistance": 32, + "CullDistance": 33, + "ImageCubeArray": 34, + "SampleRateShading": 35, + "ImageRect": 36, + "SampledRect": 37, + "GenericPointer": 38, + "Int8": 39, + "InputAttachment": 40, + "SparseResidency": 41, + "MinLod": 42, + "Sampled1D": 43, + "Image1D": 44, + "SampledCubeArray": 45, + "SampledBuffer": 46, + "ImageBuffer": 47, + "ImageMSArray": 48, + "StorageImageExtendedFormats": 49, + "ImageQuery": 50, + "DerivativeControl": 51, + "InterpolationFunction": 52, + "TransformFeedback": 53, + "GeometryStreams": 54, + "StorageImageReadWithoutFormat": 55, + "StorageImageWriteWithoutFormat": 56, + "MultiViewport": 57, + "SubgroupDispatch": 58, + "NamedBarrier": 59, + "PipeStorage": 60, + "GroupNonUniform": 61, + "GroupNonUniformVote": 62, + "GroupNonUniformArithmetic": 63, + "GroupNonUniformBallot": 64, + "GroupNonUniformShuffle": 65, + "GroupNonUniformShuffleRelative": 66, + "GroupNonUniformClustered": 67, + "GroupNonUniformQuad": 68, + "ShaderLayer": 69, + "ShaderViewportIndex": 70, + "FragmentShadingRateKHR": 4422, + "SubgroupBallotKHR": 4423, + "DrawParameters": 4427, + "SubgroupVoteKHR": 4431, + "StorageBuffer16BitAccess": 4433, + "StorageUniformBufferBlock16": 4433, + "StorageUniform16": 4434, + "UniformAndStorageBuffer16BitAccess": 4434, + "StoragePushConstant16": 4435, + "StorageInputOutput16": 4436, + "DeviceGroup": 4437, + "MultiView": 4439, + "VariablePointersStorageBuffer": 4441, + "VariablePointers": 4442, + "AtomicStorageOps": 4445, + "SampleMaskPostDepthCoverage": 4447, + "StorageBuffer8BitAccess": 4448, + "UniformAndStorageBuffer8BitAccess": 4449, + "StoragePushConstant8": 4450, + "DenormPreserve": 4464, + "DenormFlushToZero": 4465, + "SignedZeroInfNanPreserve": 4466, + "RoundingModeRTE": 4467, + "RoundingModeRTZ": 4468, + "RayQueryProvisionalKHR": 4471, + "RayTraversalPrimitiveCullingProvisionalKHR": 4478, + "Float16ImageAMD": 5008, + "ImageGatherBiasLodAMD": 5009, + "FragmentMaskAMD": 5010, + "StencilExportEXT": 5013, + "ImageReadWriteLodAMD": 5015, + "Int64ImageEXT": 5016, + "ShaderClockKHR": 5055, + "SampleMaskOverrideCoverageNV": 5249, + "GeometryShaderPassthroughNV": 5251, + "ShaderViewportIndexLayerEXT": 5254, + "ShaderViewportIndexLayerNV": 5254, + "ShaderViewportMaskNV": 5255, + "ShaderStereoViewNV": 5259, + "PerViewAttributesNV": 5260, + "FragmentFullyCoveredEXT": 5265, + "MeshShadingNV": 5266, + "ImageFootprintNV": 5282, + "FragmentBarycentricNV": 5284, + "ComputeDerivativeGroupQuadsNV": 5288, + "FragmentDensityEXT": 5291, + "ShadingRateNV": 5291, + "GroupNonUniformPartitionedNV": 5297, + "ShaderNonUniform": 5301, + "ShaderNonUniformEXT": 5301, + "RuntimeDescriptorArray": 5302, + "RuntimeDescriptorArrayEXT": 5302, + "InputAttachmentArrayDynamicIndexing": 5303, + "InputAttachmentArrayDynamicIndexingEXT": 5303, + "UniformTexelBufferArrayDynamicIndexing": 5304, + "UniformTexelBufferArrayDynamicIndexingEXT": 5304, + "StorageTexelBufferArrayDynamicIndexing": 5305, + "StorageTexelBufferArrayDynamicIndexingEXT": 5305, + "UniformBufferArrayNonUniformIndexing": 5306, + "UniformBufferArrayNonUniformIndexingEXT": 5306, + "SampledImageArrayNonUniformIndexing": 5307, + "SampledImageArrayNonUniformIndexingEXT": 5307, + "StorageBufferArrayNonUniformIndexing": 5308, + "StorageBufferArrayNonUniformIndexingEXT": 5308, + "StorageImageArrayNonUniformIndexing": 5309, + "StorageImageArrayNonUniformIndexingEXT": 5309, + "InputAttachmentArrayNonUniformIndexing": 5310, + "InputAttachmentArrayNonUniformIndexingEXT": 5310, + "UniformTexelBufferArrayNonUniformIndexing": 5311, + "UniformTexelBufferArrayNonUniformIndexingEXT": 5311, + "StorageTexelBufferArrayNonUniformIndexing": 5312, + "StorageTexelBufferArrayNonUniformIndexingEXT": 5312, + "RayTracingNV": 5340, + "VulkanMemoryModel": 5345, + "VulkanMemoryModelKHR": 5345, + "VulkanMemoryModelDeviceScope": 5346, + "VulkanMemoryModelDeviceScopeKHR": 5346, + "PhysicalStorageBufferAddresses": 5347, + "PhysicalStorageBufferAddressesEXT": 5347, + "ComputeDerivativeGroupLinearNV": 5350, + "RayTracingProvisionalKHR": 5353, + "CooperativeMatrixNV": 5357, + "FragmentShaderSampleInterlockEXT": 5363, + "FragmentShaderShadingRateInterlockEXT": 5372, + "ShaderSMBuiltinsNV": 5373, + "FragmentShaderPixelInterlockEXT": 5378, + "DemoteToHelperInvocationEXT": 5379, + "SubgroupShuffleINTEL": 5568, + "SubgroupBufferBlockIOINTEL": 5569, + "SubgroupImageBlockIOINTEL": 5570, + "SubgroupImageMediaBlockIOINTEL": 5579, + "IntegerFunctions2INTEL": 5584, + "FunctionPointersINTEL": 5603, + "IndirectReferencesINTEL": 5604, + "SubgroupAvcMotionEstimationINTEL": 5696, + "SubgroupAvcMotionEstimationIntraINTEL": 5697, + "SubgroupAvcMotionEstimationChromaINTEL": 5698, + "FPGAMemoryAttributesINTEL": 5824, + "UnstructuredLoopControlsINTEL": 5886, + "FPGALoopControlsINTEL": 5888, + "KernelAttributesINTEL": 5892, + "FPGAKernelAttributesINTEL": 5897, + "BlockingPipesINTEL": 5945, + "FPGARegINTEL": 5948, + "AtomicFloat32AddEXT": 6033, + "AtomicFloat64AddEXT": 6034 + } + }, + { + "Name": "RayFlags", + "Type": "Bit", + "Values": + { + "OpaqueKHR": 0, + "NoOpaqueKHR": 1, + "TerminateOnFirstHitKHR": 2, + "SkipClosestHitShaderKHR": 3, + "CullBackFacingTrianglesKHR": 4, + "CullFrontFacingTrianglesKHR": 5, + "CullOpaqueKHR": 6, + "CullNoOpaqueKHR": 7, + "SkipTrianglesKHR": 8, + "SkipAABBsKHR": 9 + } + }, + { + "Name": "RayQueryIntersection", + "Type": "Value", + "Values": + { + "RayQueryCandidateIntersectionKHR": 0, + "RayQueryCommittedIntersectionKHR": 1 + } + }, + { + "Name": "RayQueryCommittedIntersectionType", + "Type": "Value", + "Values": + { + "RayQueryCommittedIntersectionNoneKHR": 0, + "RayQueryCommittedIntersectionTriangleKHR": 1, + "RayQueryCommittedIntersectionGeneratedKHR": 2 + } + }, + { + "Name": "RayQueryCandidateIntersectionType", + "Type": "Value", + "Values": + { + "RayQueryCandidateIntersectionTriangleKHR": 0, + "RayQueryCandidateIntersectionAABBKHR": 1 + } + }, + { + "Name": "FragmentShadingRate", + "Type": "Bit", + "Values": + { + "Vertical2Pixels": 0, + "Vertical4Pixels": 1, + "Horizontal2Pixels": 2, + "Horizontal4Pixels": 3 + } + }, + { + "Name": "Op", + "Type": "Value", + "Values": + { + "OpNop": 0, + "OpUndef": 1, + "OpSourceContinued": 2, + "OpSource": 3, + "OpSourceExtension": 4, + "OpName": 5, + "OpMemberName": 6, + "OpString": 7, + "OpLine": 8, + "OpExtension": 10, + "OpExtInstImport": 11, + "OpExtInst": 12, + "OpMemoryModel": 14, + "OpEntryPoint": 15, + "OpExecutionMode": 16, + "OpCapability": 17, + "OpTypeVoid": 19, + "OpTypeBool": 20, + "OpTypeInt": 21, + "OpTypeFloat": 22, + "OpTypeVector": 23, + "OpTypeMatrix": 24, + "OpTypeImage": 25, + "OpTypeSampler": 26, + "OpTypeSampledImage": 27, + "OpTypeArray": 28, + "OpTypeRuntimeArray": 29, + "OpTypeStruct": 30, + "OpTypeOpaque": 31, + "OpTypePointer": 32, + "OpTypeFunction": 33, + "OpTypeEvent": 34, + "OpTypeDeviceEvent": 35, + "OpTypeReserveId": 36, + "OpTypeQueue": 37, + "OpTypePipe": 38, + "OpTypeForwardPointer": 39, + "OpConstantTrue": 41, + "OpConstantFalse": 42, + "OpConstant": 43, + "OpConstantComposite": 44, + "OpConstantSampler": 45, + "OpConstantNull": 46, + "OpSpecConstantTrue": 48, + "OpSpecConstantFalse": 49, + "OpSpecConstant": 50, + "OpSpecConstantComposite": 51, + "OpSpecConstantOp": 52, + "OpFunction": 54, + "OpFunctionParameter": 55, + "OpFunctionEnd": 56, + "OpFunctionCall": 57, + "OpVariable": 59, + "OpImageTexelPointer": 60, + "OpLoad": 61, + "OpStore": 62, + "OpCopyMemory": 63, + "OpCopyMemorySized": 64, + "OpAccessChain": 65, + "OpInBoundsAccessChain": 66, + "OpPtrAccessChain": 67, + "OpArrayLength": 68, + "OpGenericPtrMemSemantics": 69, + "OpInBoundsPtrAccessChain": 70, + "OpDecorate": 71, + "OpMemberDecorate": 72, + "OpDecorationGroup": 73, + "OpGroupDecorate": 74, + "OpGroupMemberDecorate": 75, + "OpVectorExtractDynamic": 77, + "OpVectorInsertDynamic": 78, + "OpVectorShuffle": 79, + "OpCompositeConstruct": 80, + "OpCompositeExtract": 81, + "OpCompositeInsert": 82, + "OpCopyObject": 83, + "OpTranspose": 84, + "OpSampledImage": 86, + "OpImageSampleImplicitLod": 87, + "OpImageSampleExplicitLod": 88, + "OpImageSampleDrefImplicitLod": 89, + "OpImageSampleDrefExplicitLod": 90, + "OpImageSampleProjImplicitLod": 91, + "OpImageSampleProjExplicitLod": 92, + "OpImageSampleProjDrefImplicitLod": 93, + "OpImageSampleProjDrefExplicitLod": 94, + "OpImageFetch": 95, + "OpImageGather": 96, + "OpImageDrefGather": 97, + "OpImageRead": 98, + "OpImageWrite": 99, + "OpImage": 100, + "OpImageQueryFormat": 101, + "OpImageQueryOrder": 102, + "OpImageQuerySizeLod": 103, + "OpImageQuerySize": 104, + "OpImageQueryLod": 105, + "OpImageQueryLevels": 106, + "OpImageQuerySamples": 107, + "OpConvertFToU": 109, + "OpConvertFToS": 110, + "OpConvertSToF": 111, + "OpConvertUToF": 112, + "OpUConvert": 113, + "OpSConvert": 114, + "OpFConvert": 115, + "OpQuantizeToF16": 116, + "OpConvertPtrToU": 117, + "OpSatConvertSToU": 118, + "OpSatConvertUToS": 119, + "OpConvertUToPtr": 120, + "OpPtrCastToGeneric": 121, + "OpGenericCastToPtr": 122, + "OpGenericCastToPtrExplicit": 123, + "OpBitcast": 124, + "OpSNegate": 126, + "OpFNegate": 127, + "OpIAdd": 128, + "OpFAdd": 129, + "OpISub": 130, + "OpFSub": 131, + "OpIMul": 132, + "OpFMul": 133, + "OpUDiv": 134, + "OpSDiv": 135, + "OpFDiv": 136, + "OpUMod": 137, + "OpSRem": 138, + "OpSMod": 139, + "OpFRem": 140, + "OpFMod": 141, + "OpVectorTimesScalar": 142, + "OpMatrixTimesScalar": 143, + "OpVectorTimesMatrix": 144, + "OpMatrixTimesVector": 145, + "OpMatrixTimesMatrix": 146, + "OpOuterProduct": 147, + "OpDot": 148, + "OpIAddCarry": 149, + "OpISubBorrow": 150, + "OpUMulExtended": 151, + "OpSMulExtended": 152, + "OpAny": 154, + "OpAll": 155, + "OpIsNan": 156, + "OpIsInf": 157, + "OpIsFinite": 158, + "OpIsNormal": 159, + "OpSignBitSet": 160, + "OpLessOrGreater": 161, + "OpOrdered": 162, + "OpUnordered": 163, + "OpLogicalEqual": 164, + "OpLogicalNotEqual": 165, + "OpLogicalOr": 166, + "OpLogicalAnd": 167, + "OpLogicalNot": 168, + "OpSelect": 169, + "OpIEqual": 170, + "OpINotEqual": 171, + "OpUGreaterThan": 172, + "OpSGreaterThan": 173, + "OpUGreaterThanEqual": 174, + "OpSGreaterThanEqual": 175, + "OpULessThan": 176, + "OpSLessThan": 177, + "OpULessThanEqual": 178, + "OpSLessThanEqual": 179, + "OpFOrdEqual": 180, + "OpFUnordEqual": 181, + "OpFOrdNotEqual": 182, + "OpFUnordNotEqual": 183, + "OpFOrdLessThan": 184, + "OpFUnordLessThan": 185, + "OpFOrdGreaterThan": 186, + "OpFUnordGreaterThan": 187, + "OpFOrdLessThanEqual": 188, + "OpFUnordLessThanEqual": 189, + "OpFOrdGreaterThanEqual": 190, + "OpFUnordGreaterThanEqual": 191, + "OpShiftRightLogical": 194, + "OpShiftRightArithmetic": 195, + "OpShiftLeftLogical": 196, + "OpBitwiseOr": 197, + "OpBitwiseXor": 198, + "OpBitwiseAnd": 199, + "OpNot": 200, + "OpBitFieldInsert": 201, + "OpBitFieldSExtract": 202, + "OpBitFieldUExtract": 203, + "OpBitReverse": 204, + "OpBitCount": 205, + "OpDPdx": 207, + "OpDPdy": 208, + "OpFwidth": 209, + "OpDPdxFine": 210, + "OpDPdyFine": 211, + "OpFwidthFine": 212, + "OpDPdxCoarse": 213, + "OpDPdyCoarse": 214, + "OpFwidthCoarse": 215, + "OpEmitVertex": 218, + "OpEndPrimitive": 219, + "OpEmitStreamVertex": 220, + "OpEndStreamPrimitive": 221, + "OpControlBarrier": 224, + "OpMemoryBarrier": 225, + "OpAtomicLoad": 227, + "OpAtomicStore": 228, + "OpAtomicExchange": 229, + "OpAtomicCompareExchange": 230, + "OpAtomicCompareExchangeWeak": 231, + "OpAtomicIIncrement": 232, + "OpAtomicIDecrement": 233, + "OpAtomicIAdd": 234, + "OpAtomicISub": 235, + "OpAtomicSMin": 236, + "OpAtomicUMin": 237, + "OpAtomicSMax": 238, + "OpAtomicUMax": 239, + "OpAtomicAnd": 240, + "OpAtomicOr": 241, + "OpAtomicXor": 242, + "OpPhi": 245, + "OpLoopMerge": 246, + "OpSelectionMerge": 247, + "OpLabel": 248, + "OpBranch": 249, + "OpBranchConditional": 250, + "OpSwitch": 251, + "OpKill": 252, + "OpReturn": 253, + "OpReturnValue": 254, + "OpUnreachable": 255, + "OpLifetimeStart": 256, + "OpLifetimeStop": 257, + "OpGroupAsyncCopy": 259, + "OpGroupWaitEvents": 260, + "OpGroupAll": 261, + "OpGroupAny": 262, + "OpGroupBroadcast": 263, + "OpGroupIAdd": 264, + "OpGroupFAdd": 265, + "OpGroupFMin": 266, + "OpGroupUMin": 267, + "OpGroupSMin": 268, + "OpGroupFMax": 269, + "OpGroupUMax": 270, + "OpGroupSMax": 271, + "OpReadPipe": 274, + "OpWritePipe": 275, + "OpReservedReadPipe": 276, + "OpReservedWritePipe": 277, + "OpReserveReadPipePackets": 278, + "OpReserveWritePipePackets": 279, + "OpCommitReadPipe": 280, + "OpCommitWritePipe": 281, + "OpIsValidReserveId": 282, + "OpGetNumPipePackets": 283, + "OpGetMaxPipePackets": 284, + "OpGroupReserveReadPipePackets": 285, + "OpGroupReserveWritePipePackets": 286, + "OpGroupCommitReadPipe": 287, + "OpGroupCommitWritePipe": 288, + "OpEnqueueMarker": 291, + "OpEnqueueKernel": 292, + "OpGetKernelNDrangeSubGroupCount": 293, + "OpGetKernelNDrangeMaxSubGroupSize": 294, + "OpGetKernelWorkGroupSize": 295, + "OpGetKernelPreferredWorkGroupSizeMultiple": 296, + "OpRetainEvent": 297, + "OpReleaseEvent": 298, + "OpCreateUserEvent": 299, + "OpIsValidEvent": 300, + "OpSetUserEventStatus": 301, + "OpCaptureEventProfilingInfo": 302, + "OpGetDefaultQueue": 303, + "OpBuildNDRange": 304, + "OpImageSparseSampleImplicitLod": 305, + "OpImageSparseSampleExplicitLod": 306, + "OpImageSparseSampleDrefImplicitLod": 307, + "OpImageSparseSampleDrefExplicitLod": 308, + "OpImageSparseSampleProjImplicitLod": 309, + "OpImageSparseSampleProjExplicitLod": 310, + "OpImageSparseSampleProjDrefImplicitLod": 311, + "OpImageSparseSampleProjDrefExplicitLod": 312, + "OpImageSparseFetch": 313, + "OpImageSparseGather": 314, + "OpImageSparseDrefGather": 315, + "OpImageSparseTexelsResident": 316, + "OpNoLine": 317, + "OpAtomicFlagTestAndSet": 318, + "OpAtomicFlagClear": 319, + "OpImageSparseRead": 320, + "OpSizeOf": 321, + "OpTypePipeStorage": 322, + "OpConstantPipeStorage": 323, + "OpCreatePipeFromPipeStorage": 324, + "OpGetKernelLocalSizeForSubgroupCount": 325, + "OpGetKernelMaxNumSubgroups": 326, + "OpTypeNamedBarrier": 327, + "OpNamedBarrierInitialize": 328, + "OpMemoryNamedBarrier": 329, + "OpModuleProcessed": 330, + "OpExecutionModeId": 331, + "OpDecorateId": 332, + "OpGroupNonUniformElect": 333, + "OpGroupNonUniformAll": 334, + "OpGroupNonUniformAny": 335, + "OpGroupNonUniformAllEqual": 336, + "OpGroupNonUniformBroadcast": 337, + "OpGroupNonUniformBroadcastFirst": 338, + "OpGroupNonUniformBallot": 339, + "OpGroupNonUniformInverseBallot": 340, + "OpGroupNonUniformBallotBitExtract": 341, + "OpGroupNonUniformBallotBitCount": 342, + "OpGroupNonUniformBallotFindLSB": 343, + "OpGroupNonUniformBallotFindMSB": 344, + "OpGroupNonUniformShuffle": 345, + "OpGroupNonUniformShuffleXor": 346, + "OpGroupNonUniformShuffleUp": 347, + "OpGroupNonUniformShuffleDown": 348, + "OpGroupNonUniformIAdd": 349, + "OpGroupNonUniformFAdd": 350, + "OpGroupNonUniformIMul": 351, + "OpGroupNonUniformFMul": 352, + "OpGroupNonUniformSMin": 353, + "OpGroupNonUniformUMin": 354, + "OpGroupNonUniformFMin": 355, + "OpGroupNonUniformSMax": 356, + "OpGroupNonUniformUMax": 357, + "OpGroupNonUniformFMax": 358, + "OpGroupNonUniformBitwiseAnd": 359, + "OpGroupNonUniformBitwiseOr": 360, + "OpGroupNonUniformBitwiseXor": 361, + "OpGroupNonUniformLogicalAnd": 362, + "OpGroupNonUniformLogicalOr": 363, + "OpGroupNonUniformLogicalXor": 364, + "OpGroupNonUniformQuadBroadcast": 365, + "OpGroupNonUniformQuadSwap": 366, + "OpCopyLogical": 400, + "OpPtrEqual": 401, + "OpPtrNotEqual": 402, + "OpPtrDiff": 403, + "OpTerminateInvocation": 4416, + "OpSubgroupBallotKHR": 4421, + "OpSubgroupFirstInvocationKHR": 4422, + "OpSubgroupAllKHR": 4428, + "OpSubgroupAnyKHR": 4429, + "OpSubgroupAllEqualKHR": 4430, + "OpSubgroupReadInvocationKHR": 4432, + "OpTypeRayQueryProvisionalKHR": 4472, + "OpRayQueryInitializeKHR": 4473, + "OpRayQueryTerminateKHR": 4474, + "OpRayQueryGenerateIntersectionKHR": 4475, + "OpRayQueryConfirmIntersectionKHR": 4476, + "OpRayQueryProceedKHR": 4477, + "OpRayQueryGetIntersectionTypeKHR": 4479, + "OpGroupIAddNonUniformAMD": 5000, + "OpGroupFAddNonUniformAMD": 5001, + "OpGroupFMinNonUniformAMD": 5002, + "OpGroupUMinNonUniformAMD": 5003, + "OpGroupSMinNonUniformAMD": 5004, + "OpGroupFMaxNonUniformAMD": 5005, + "OpGroupUMaxNonUniformAMD": 5006, + "OpGroupSMaxNonUniformAMD": 5007, + "OpFragmentMaskFetchAMD": 5011, + "OpFragmentFetchAMD": 5012, + "OpReadClockKHR": 5056, + "OpImageSampleFootprintNV": 5283, + "OpGroupNonUniformPartitionNV": 5296, + "OpWritePackedPrimitiveIndices4x8NV": 5299, + "OpReportIntersectionKHR": 5334, + "OpReportIntersectionNV": 5334, + "OpIgnoreIntersectionKHR": 5335, + "OpIgnoreIntersectionNV": 5335, + "OpTerminateRayKHR": 5336, + "OpTerminateRayNV": 5336, + "OpTraceNV": 5337, + "OpTraceRayKHR": 5337, + "OpTypeAccelerationStructureKHR": 5341, + "OpTypeAccelerationStructureNV": 5341, + "OpExecuteCallableKHR": 5344, + "OpExecuteCallableNV": 5344, + "OpTypeCooperativeMatrixNV": 5358, + "OpCooperativeMatrixLoadNV": 5359, + "OpCooperativeMatrixStoreNV": 5360, + "OpCooperativeMatrixMulAddNV": 5361, + "OpCooperativeMatrixLengthNV": 5362, + "OpBeginInvocationInterlockEXT": 5364, + "OpEndInvocationInterlockEXT": 5365, + "OpDemoteToHelperInvocationEXT": 5380, + "OpIsHelperInvocationEXT": 5381, + "OpSubgroupShuffleINTEL": 5571, + "OpSubgroupShuffleDownINTEL": 5572, + "OpSubgroupShuffleUpINTEL": 5573, + "OpSubgroupShuffleXorINTEL": 5574, + "OpSubgroupBlockReadINTEL": 5575, + "OpSubgroupBlockWriteINTEL": 5576, + "OpSubgroupImageBlockReadINTEL": 5577, + "OpSubgroupImageBlockWriteINTEL": 5578, + "OpSubgroupImageMediaBlockReadINTEL": 5580, + "OpSubgroupImageMediaBlockWriteINTEL": 5581, + "OpUCountLeadingZerosINTEL": 5585, + "OpUCountTrailingZerosINTEL": 5586, + "OpAbsISubINTEL": 5587, + "OpAbsUSubINTEL": 5588, + "OpIAddSatINTEL": 5589, + "OpUAddSatINTEL": 5590, + "OpIAverageINTEL": 5591, + "OpUAverageINTEL": 5592, + "OpIAverageRoundedINTEL": 5593, + "OpUAverageRoundedINTEL": 5594, + "OpISubSatINTEL": 5595, + "OpUSubSatINTEL": 5596, + "OpIMul32x16INTEL": 5597, + "OpUMul32x16INTEL": 5598, + "OpFunctionPointerINTEL": 5600, + "OpFunctionPointerCallINTEL": 5601, + "OpDecorateString": 5632, + "OpDecorateStringGOOGLE": 5632, + "OpMemberDecorateString": 5633, + "OpMemberDecorateStringGOOGLE": 5633, + "OpVmeImageINTEL": 5699, + "OpTypeVmeImageINTEL": 5700, + "OpTypeAvcImePayloadINTEL": 5701, + "OpTypeAvcRefPayloadINTEL": 5702, + "OpTypeAvcSicPayloadINTEL": 5703, + "OpTypeAvcMcePayloadINTEL": 5704, + "OpTypeAvcMceResultINTEL": 5705, + "OpTypeAvcImeResultINTEL": 5706, + "OpTypeAvcImeResultSingleReferenceStreamoutINTEL": 5707, + "OpTypeAvcImeResultDualReferenceStreamoutINTEL": 5708, + "OpTypeAvcImeSingleReferenceStreaminINTEL": 5709, + "OpTypeAvcImeDualReferenceStreaminINTEL": 5710, + "OpTypeAvcRefResultINTEL": 5711, + "OpTypeAvcSicResultINTEL": 5712, + "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL": 5713, + "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL": 5714, + "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL": 5715, + "OpSubgroupAvcMceSetInterShapePenaltyINTEL": 5716, + "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL": 5717, + "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL": 5718, + "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL": 5719, + "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL": 5720, + "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL": 5721, + "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL": 5722, + "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL": 5723, + "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL": 5724, + "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL": 5725, + "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL": 5726, + "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL": 5727, + "OpSubgroupAvcMceSetAcOnlyHaarINTEL": 5728, + "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL": 5729, + "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL": 5730, + "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL": 5731, + "OpSubgroupAvcMceConvertToImePayloadINTEL": 5732, + "OpSubgroupAvcMceConvertToImeResultINTEL": 5733, + "OpSubgroupAvcMceConvertToRefPayloadINTEL": 5734, + "OpSubgroupAvcMceConvertToRefResultINTEL": 5735, + "OpSubgroupAvcMceConvertToSicPayloadINTEL": 5736, + "OpSubgroupAvcMceConvertToSicResultINTEL": 5737, + "OpSubgroupAvcMceGetMotionVectorsINTEL": 5738, + "OpSubgroupAvcMceGetInterDistortionsINTEL": 5739, + "OpSubgroupAvcMceGetBestInterDistortionsINTEL": 5740, + "OpSubgroupAvcMceGetInterMajorShapeINTEL": 5741, + "OpSubgroupAvcMceGetInterMinorShapeINTEL": 5742, + "OpSubgroupAvcMceGetInterDirectionsINTEL": 5743, + "OpSubgroupAvcMceGetInterMotionVectorCountINTEL": 5744, + "OpSubgroupAvcMceGetInterReferenceIdsINTEL": 5745, + "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL": 5746, + "OpSubgroupAvcImeInitializeINTEL": 5747, + "OpSubgroupAvcImeSetSingleReferenceINTEL": 5748, + "OpSubgroupAvcImeSetDualReferenceINTEL": 5749, + "OpSubgroupAvcImeRefWindowSizeINTEL": 5750, + "OpSubgroupAvcImeAdjustRefOffsetINTEL": 5751, + "OpSubgroupAvcImeConvertToMcePayloadINTEL": 5752, + "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL": 5753, + "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL": 5754, + "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL": 5755, + "OpSubgroupAvcImeSetWeightedSadINTEL": 5756, + "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL": 5757, + "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL": 5758, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL": 5759, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL": 5760, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL": 5761, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL": 5762, + "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL": 5763, + "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL": 5764, + "OpSubgroupAvcImeConvertToMceResultINTEL": 5765, + "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL": 5766, + "OpSubgroupAvcImeGetDualReferenceStreaminINTEL": 5767, + "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL": 5768, + "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL": 5769, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL": 5770, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL": 5771, + "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL": 5772, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL": 5773, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL": 5774, + "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL": 5775, + "OpSubgroupAvcImeGetBorderReachedINTEL": 5776, + "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL": 5777, + "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL": 5778, + "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL": 5779, + "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL": 5780, + "OpSubgroupAvcFmeInitializeINTEL": 5781, + "OpSubgroupAvcBmeInitializeINTEL": 5782, + "OpSubgroupAvcRefConvertToMcePayloadINTEL": 5783, + "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL": 5784, + "OpSubgroupAvcRefSetBilinearFilterEnableINTEL": 5785, + "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL": 5786, + "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL": 5787, + "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL": 5788, + "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL": 5789, + "OpSubgroupAvcRefConvertToMceResultINTEL": 5790, + "OpSubgroupAvcSicInitializeINTEL": 5791, + "OpSubgroupAvcSicConfigureSkcINTEL": 5792, + "OpSubgroupAvcSicConfigureIpeLumaINTEL": 5793, + "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL": 5794, + "OpSubgroupAvcSicGetMotionVectorMaskINTEL": 5795, + "OpSubgroupAvcSicConvertToMcePayloadINTEL": 5796, + "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL": 5797, + "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL": 5798, + "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL": 5799, + "OpSubgroupAvcSicSetBilinearFilterEnableINTEL": 5800, + "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL": 5801, + "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL": 5802, + "OpSubgroupAvcSicEvaluateIpeINTEL": 5803, + "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL": 5804, + "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL": 5805, + "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL": 5806, + "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL": 5807, + "OpSubgroupAvcSicConvertToMceResultINTEL": 5808, + "OpSubgroupAvcSicGetIpeLumaShapeINTEL": 5809, + "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL": 5810, + "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL": 5811, + "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL": 5812, + "OpSubgroupAvcSicGetIpeChromaModeINTEL": 5813, + "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL": 5814, + "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL": 5815, + "OpSubgroupAvcSicGetInterRawSadsINTEL": 5816, + "OpLoopControlINTEL": 5887, + "OpReadPipeBlockingINTEL": 5946, + "OpWritePipeBlockingINTEL": 5947, + "OpFPGARegINTEL": 5949, + "OpRayQueryGetRayTMinKHR": 6016, + "OpRayQueryGetRayFlagsKHR": 6017, + "OpRayQueryGetIntersectionTKHR": 6018, + "OpRayQueryGetIntersectionInstanceCustomIndexKHR": 6019, + "OpRayQueryGetIntersectionInstanceIdKHR": 6020, + "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR": 6021, + "OpRayQueryGetIntersectionGeometryIndexKHR": 6022, + "OpRayQueryGetIntersectionPrimitiveIndexKHR": 6023, + "OpRayQueryGetIntersectionBarycentricsKHR": 6024, + "OpRayQueryGetIntersectionFrontFaceKHR": 6025, + "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR": 6026, + "OpRayQueryGetIntersectionObjectRayDirectionKHR": 6027, + "OpRayQueryGetIntersectionObjectRayOriginKHR": 6028, + "OpRayQueryGetWorldRayDirectionKHR": 6029, + "OpRayQueryGetWorldRayOriginKHR": 6030, + "OpRayQueryGetIntersectionObjectToWorldKHR": 6031, + "OpRayQueryGetIntersectionWorldToObjectKHR": 6032, + "OpAtomicFAddEXT": 6035 + } + } + ] + } +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua new file mode 100644 index 0000000..d97af41 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.lua @@ -0,0 +1,1572 @@ +-- Copyright (c) 2014-2020 The Khronos Group Inc. +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and/or associated documentation files (the "Materials"), +-- to deal in the Materials without restriction, including without limitation +-- the rights to use, copy, modify, merge, publish, distribute, sublicense, +-- and/or sell copies of the Materials, and to permit persons to whom the +-- Materials are furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Materials. +-- +-- MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +-- STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +-- HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +-- +-- THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +-- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +-- FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +-- IN THE MATERIALS. + +-- This header is automatically generated by the same tool that creates +-- the Binary Section of the SPIR-V specification. + +-- Enumeration tokens for SPIR-V, in various styles: +-- C, C++, C++11, JSON, Lua, Python, C#, D +-- +-- - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +-- - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +-- - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +-- - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +-- - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +-- - C# will use enum classes in the Specification class located in the "Spv" namespace, +-- e.g.: Spv.Specification.SourceLanguage.GLSL +-- - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +-- +-- Some tokens act like mask values, which can be OR'd together, +-- while others are mutually exclusive. The mask-like ones have +-- "Mask" in their name, and a parallel enum that has the shift +-- amount (1 << x) for each corresponding enumerant. + +spv = { + MagicNumber = 0x07230203, + Version = 0x00010500, + Revision = 4, + OpCodeMask = 0xffff, + WordCountShift = 16, + + SourceLanguage = { + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, + }, + + ExecutionModel = { + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, + }, + + AddressingModel = { + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, + }, + + MemoryModel = { + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, + }, + + ExecutionMode = { + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + StencilRefReplacingEXT = 5027, + OutputLinesNV = 5269, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearNV = 5290, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, + }, + + StorageClass = { + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + CodeSectionINTEL = 5605, + }, + + Dim = { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, + }, + + SamplerAddressingMode = { + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, + }, + + SamplerFilterMode = { + Nearest = 0, + Linear = 1, + }, + + ImageFormat = { + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, + }, + + ImageChannelOrder = { + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, + }, + + ImageChannelDataType = { + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, + }, + + ImageOperandsShift = { + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, + }, + + ImageOperandsMask = { + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, + }, + + FPFastMathModeShift = { + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, + }, + + FPFastMathModeMask = { + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, + }, + + FPRoundingMode = { + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, + }, + + LinkageType = { + Export = 0, + Import = 1, + }, + + AccessQualifier = { + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, + }, + + FunctionParameterAttribute = { + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, + }, + + Decoration = { + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + ReferencedIndirectlyINTEL = 5602, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, + }, + + BuiltIn = { + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordNV = 5286, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTKHR = 5332, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, + }, + + SelectionControlShift = { + Flatten = 0, + DontFlatten = 1, + }, + + SelectionControlMask = { + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, + }, + + LoopControlShift = { + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, + }, + + LoopControlMask = { + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, + }, + + FunctionControlShift = { + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, + }, + + FunctionControlMask = { + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, + }, + + MemorySemanticsShift = { + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, + }, + + MemorySemanticsMask = { + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, + }, + + MemoryAccessShift = { + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, + }, + + MemoryAccessMask = { + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, + }, + + Scope = { + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, + }, + + GroupOperation = { + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, + }, + + KernelEnqueueFlags = { + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, + }, + + KernelProfilingInfoShift = { + CmdExecTime = 0, + }, + + KernelProfilingInfoMask = { + MaskNone = 0, + CmdExecTime = 0x00000001, + }, + + Capability = { + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayTraversalPrimitiveCullingProvisionalKHR = 4478, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingNV = 5340, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocationEXT = 5379, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + FPGAMemoryAttributesINTEL = 5824, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, + }, + + RayFlagsShift = { + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, + }, + + RayFlagsMask = { + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, + }, + + RayQueryIntersection = { + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, + }, + + RayQueryCommittedIntersectionType = { + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, + }, + + RayQueryCandidateIntersectionType = { + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, + }, + + FragmentShadingRateShift = { + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, + }, + + FragmentShadingRateMask = { + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, + }, + + Op = { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpTerminateInvocation = 4416, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpLoopControlINTEL = 5887, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + }, + +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py new file mode 100644 index 0000000..869b7f3 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spirv.py @@ -0,0 +1,1572 @@ +# Copyright (c) 2014-2020 The Khronos Group Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and/or associated documentation files (the "Materials"), +# to deal in the Materials without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Materials, and to permit persons to whom the +# Materials are furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +# STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +# HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +# IN THE MATERIALS. + +# This header is automatically generated by the same tool that creates +# the Binary Section of the SPIR-V specification. + +# Enumeration tokens for SPIR-V, in various styles: +# C, C++, C++11, JSON, Lua, Python, C#, D +# +# - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +# - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +# - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +# - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +# - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +# - C# will use enum classes in the Specification class located in the "Spv" namespace, +# e.g.: Spv.Specification.SourceLanguage.GLSL +# - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +# +# Some tokens act like mask values, which can be OR'd together, +# while others are mutually exclusive. The mask-like ones have +# "Mask" in their name, and a parallel enum that has the shift +# amount (1 << x) for each corresponding enumerant. + +spv = { + 'MagicNumber' : 0x07230203, + 'Version' : 0x00010500, + 'Revision' : 4, + 'OpCodeMask' : 0xffff, + 'WordCountShift' : 16, + + 'SourceLanguage' : { + 'Unknown' : 0, + 'ESSL' : 1, + 'GLSL' : 2, + 'OpenCL_C' : 3, + 'OpenCL_CPP' : 4, + 'HLSL' : 5, + }, + + 'ExecutionModel' : { + 'Vertex' : 0, + 'TessellationControl' : 1, + 'TessellationEvaluation' : 2, + 'Geometry' : 3, + 'Fragment' : 4, + 'GLCompute' : 5, + 'Kernel' : 6, + 'TaskNV' : 5267, + 'MeshNV' : 5268, + 'RayGenerationKHR' : 5313, + 'RayGenerationNV' : 5313, + 'IntersectionKHR' : 5314, + 'IntersectionNV' : 5314, + 'AnyHitKHR' : 5315, + 'AnyHitNV' : 5315, + 'ClosestHitKHR' : 5316, + 'ClosestHitNV' : 5316, + 'MissKHR' : 5317, + 'MissNV' : 5317, + 'CallableKHR' : 5318, + 'CallableNV' : 5318, + }, + + 'AddressingModel' : { + 'Logical' : 0, + 'Physical32' : 1, + 'Physical64' : 2, + 'PhysicalStorageBuffer64' : 5348, + 'PhysicalStorageBuffer64EXT' : 5348, + }, + + 'MemoryModel' : { + 'Simple' : 0, + 'GLSL450' : 1, + 'OpenCL' : 2, + 'Vulkan' : 3, + 'VulkanKHR' : 3, + }, + + 'ExecutionMode' : { + 'Invocations' : 0, + 'SpacingEqual' : 1, + 'SpacingFractionalEven' : 2, + 'SpacingFractionalOdd' : 3, + 'VertexOrderCw' : 4, + 'VertexOrderCcw' : 5, + 'PixelCenterInteger' : 6, + 'OriginUpperLeft' : 7, + 'OriginLowerLeft' : 8, + 'EarlyFragmentTests' : 9, + 'PointMode' : 10, + 'Xfb' : 11, + 'DepthReplacing' : 12, + 'DepthGreater' : 14, + 'DepthLess' : 15, + 'DepthUnchanged' : 16, + 'LocalSize' : 17, + 'LocalSizeHint' : 18, + 'InputPoints' : 19, + 'InputLines' : 20, + 'InputLinesAdjacency' : 21, + 'Triangles' : 22, + 'InputTrianglesAdjacency' : 23, + 'Quads' : 24, + 'Isolines' : 25, + 'OutputVertices' : 26, + 'OutputPoints' : 27, + 'OutputLineStrip' : 28, + 'OutputTriangleStrip' : 29, + 'VecTypeHint' : 30, + 'ContractionOff' : 31, + 'Initializer' : 33, + 'Finalizer' : 34, + 'SubgroupSize' : 35, + 'SubgroupsPerWorkgroup' : 36, + 'SubgroupsPerWorkgroupId' : 37, + 'LocalSizeId' : 38, + 'LocalSizeHintId' : 39, + 'PostDepthCoverage' : 4446, + 'DenormPreserve' : 4459, + 'DenormFlushToZero' : 4460, + 'SignedZeroInfNanPreserve' : 4461, + 'RoundingModeRTE' : 4462, + 'RoundingModeRTZ' : 4463, + 'StencilRefReplacingEXT' : 5027, + 'OutputLinesNV' : 5269, + 'OutputPrimitivesNV' : 5270, + 'DerivativeGroupQuadsNV' : 5289, + 'DerivativeGroupLinearNV' : 5290, + 'OutputTrianglesNV' : 5298, + 'PixelInterlockOrderedEXT' : 5366, + 'PixelInterlockUnorderedEXT' : 5367, + 'SampleInterlockOrderedEXT' : 5368, + 'SampleInterlockUnorderedEXT' : 5369, + 'ShadingRateInterlockOrderedEXT' : 5370, + 'ShadingRateInterlockUnorderedEXT' : 5371, + 'MaxWorkgroupSizeINTEL' : 5893, + 'MaxWorkDimINTEL' : 5894, + 'NoGlobalOffsetINTEL' : 5895, + 'NumSIMDWorkitemsINTEL' : 5896, + }, + + 'StorageClass' : { + 'UniformConstant' : 0, + 'Input' : 1, + 'Uniform' : 2, + 'Output' : 3, + 'Workgroup' : 4, + 'CrossWorkgroup' : 5, + 'Private' : 6, + 'Function' : 7, + 'Generic' : 8, + 'PushConstant' : 9, + 'AtomicCounter' : 10, + 'Image' : 11, + 'StorageBuffer' : 12, + 'CallableDataKHR' : 5328, + 'CallableDataNV' : 5328, + 'IncomingCallableDataKHR' : 5329, + 'IncomingCallableDataNV' : 5329, + 'RayPayloadKHR' : 5338, + 'RayPayloadNV' : 5338, + 'HitAttributeKHR' : 5339, + 'HitAttributeNV' : 5339, + 'IncomingRayPayloadKHR' : 5342, + 'IncomingRayPayloadNV' : 5342, + 'ShaderRecordBufferKHR' : 5343, + 'ShaderRecordBufferNV' : 5343, + 'PhysicalStorageBuffer' : 5349, + 'PhysicalStorageBufferEXT' : 5349, + 'CodeSectionINTEL' : 5605, + }, + + 'Dim' : { + 'Dim1D' : 0, + 'Dim2D' : 1, + 'Dim3D' : 2, + 'Cube' : 3, + 'Rect' : 4, + 'Buffer' : 5, + 'SubpassData' : 6, + }, + + 'SamplerAddressingMode' : { + 'None' : 0, + 'ClampToEdge' : 1, + 'Clamp' : 2, + 'Repeat' : 3, + 'RepeatMirrored' : 4, + }, + + 'SamplerFilterMode' : { + 'Nearest' : 0, + 'Linear' : 1, + }, + + 'ImageFormat' : { + 'Unknown' : 0, + 'Rgba32f' : 1, + 'Rgba16f' : 2, + 'R32f' : 3, + 'Rgba8' : 4, + 'Rgba8Snorm' : 5, + 'Rg32f' : 6, + 'Rg16f' : 7, + 'R11fG11fB10f' : 8, + 'R16f' : 9, + 'Rgba16' : 10, + 'Rgb10A2' : 11, + 'Rg16' : 12, + 'Rg8' : 13, + 'R16' : 14, + 'R8' : 15, + 'Rgba16Snorm' : 16, + 'Rg16Snorm' : 17, + 'Rg8Snorm' : 18, + 'R16Snorm' : 19, + 'R8Snorm' : 20, + 'Rgba32i' : 21, + 'Rgba16i' : 22, + 'Rgba8i' : 23, + 'R32i' : 24, + 'Rg32i' : 25, + 'Rg16i' : 26, + 'Rg8i' : 27, + 'R16i' : 28, + 'R8i' : 29, + 'Rgba32ui' : 30, + 'Rgba16ui' : 31, + 'Rgba8ui' : 32, + 'R32ui' : 33, + 'Rgb10a2ui' : 34, + 'Rg32ui' : 35, + 'Rg16ui' : 36, + 'Rg8ui' : 37, + 'R16ui' : 38, + 'R8ui' : 39, + 'R64ui' : 40, + 'R64i' : 41, + }, + + 'ImageChannelOrder' : { + 'R' : 0, + 'A' : 1, + 'RG' : 2, + 'RA' : 3, + 'RGB' : 4, + 'RGBA' : 5, + 'BGRA' : 6, + 'ARGB' : 7, + 'Intensity' : 8, + 'Luminance' : 9, + 'Rx' : 10, + 'RGx' : 11, + 'RGBx' : 12, + 'Depth' : 13, + 'DepthStencil' : 14, + 'sRGB' : 15, + 'sRGBx' : 16, + 'sRGBA' : 17, + 'sBGRA' : 18, + 'ABGR' : 19, + }, + + 'ImageChannelDataType' : { + 'SnormInt8' : 0, + 'SnormInt16' : 1, + 'UnormInt8' : 2, + 'UnormInt16' : 3, + 'UnormShort565' : 4, + 'UnormShort555' : 5, + 'UnormInt101010' : 6, + 'SignedInt8' : 7, + 'SignedInt16' : 8, + 'SignedInt32' : 9, + 'UnsignedInt8' : 10, + 'UnsignedInt16' : 11, + 'UnsignedInt32' : 12, + 'HalfFloat' : 13, + 'Float' : 14, + 'UnormInt24' : 15, + 'UnormInt101010_2' : 16, + }, + + 'ImageOperandsShift' : { + 'Bias' : 0, + 'Lod' : 1, + 'Grad' : 2, + 'ConstOffset' : 3, + 'Offset' : 4, + 'ConstOffsets' : 5, + 'Sample' : 6, + 'MinLod' : 7, + 'MakeTexelAvailable' : 8, + 'MakeTexelAvailableKHR' : 8, + 'MakeTexelVisible' : 9, + 'MakeTexelVisibleKHR' : 9, + 'NonPrivateTexel' : 10, + 'NonPrivateTexelKHR' : 10, + 'VolatileTexel' : 11, + 'VolatileTexelKHR' : 11, + 'SignExtend' : 12, + 'ZeroExtend' : 13, + }, + + 'ImageOperandsMask' : { + 'MaskNone' : 0, + 'Bias' : 0x00000001, + 'Lod' : 0x00000002, + 'Grad' : 0x00000004, + 'ConstOffset' : 0x00000008, + 'Offset' : 0x00000010, + 'ConstOffsets' : 0x00000020, + 'Sample' : 0x00000040, + 'MinLod' : 0x00000080, + 'MakeTexelAvailable' : 0x00000100, + 'MakeTexelAvailableKHR' : 0x00000100, + 'MakeTexelVisible' : 0x00000200, + 'MakeTexelVisibleKHR' : 0x00000200, + 'NonPrivateTexel' : 0x00000400, + 'NonPrivateTexelKHR' : 0x00000400, + 'VolatileTexel' : 0x00000800, + 'VolatileTexelKHR' : 0x00000800, + 'SignExtend' : 0x00001000, + 'ZeroExtend' : 0x00002000, + }, + + 'FPFastMathModeShift' : { + 'NotNaN' : 0, + 'NotInf' : 1, + 'NSZ' : 2, + 'AllowRecip' : 3, + 'Fast' : 4, + }, + + 'FPFastMathModeMask' : { + 'MaskNone' : 0, + 'NotNaN' : 0x00000001, + 'NotInf' : 0x00000002, + 'NSZ' : 0x00000004, + 'AllowRecip' : 0x00000008, + 'Fast' : 0x00000010, + }, + + 'FPRoundingMode' : { + 'RTE' : 0, + 'RTZ' : 1, + 'RTP' : 2, + 'RTN' : 3, + }, + + 'LinkageType' : { + 'Export' : 0, + 'Import' : 1, + }, + + 'AccessQualifier' : { + 'ReadOnly' : 0, + 'WriteOnly' : 1, + 'ReadWrite' : 2, + }, + + 'FunctionParameterAttribute' : { + 'Zext' : 0, + 'Sext' : 1, + 'ByVal' : 2, + 'Sret' : 3, + 'NoAlias' : 4, + 'NoCapture' : 5, + 'NoWrite' : 6, + 'NoReadWrite' : 7, + }, + + 'Decoration' : { + 'RelaxedPrecision' : 0, + 'SpecId' : 1, + 'Block' : 2, + 'BufferBlock' : 3, + 'RowMajor' : 4, + 'ColMajor' : 5, + 'ArrayStride' : 6, + 'MatrixStride' : 7, + 'GLSLShared' : 8, + 'GLSLPacked' : 9, + 'CPacked' : 10, + 'BuiltIn' : 11, + 'NoPerspective' : 13, + 'Flat' : 14, + 'Patch' : 15, + 'Centroid' : 16, + 'Sample' : 17, + 'Invariant' : 18, + 'Restrict' : 19, + 'Aliased' : 20, + 'Volatile' : 21, + 'Constant' : 22, + 'Coherent' : 23, + 'NonWritable' : 24, + 'NonReadable' : 25, + 'Uniform' : 26, + 'UniformId' : 27, + 'SaturatedConversion' : 28, + 'Stream' : 29, + 'Location' : 30, + 'Component' : 31, + 'Index' : 32, + 'Binding' : 33, + 'DescriptorSet' : 34, + 'Offset' : 35, + 'XfbBuffer' : 36, + 'XfbStride' : 37, + 'FuncParamAttr' : 38, + 'FPRoundingMode' : 39, + 'FPFastMathMode' : 40, + 'LinkageAttributes' : 41, + 'NoContraction' : 42, + 'InputAttachmentIndex' : 43, + 'Alignment' : 44, + 'MaxByteOffset' : 45, + 'AlignmentId' : 46, + 'MaxByteOffsetId' : 47, + 'NoSignedWrap' : 4469, + 'NoUnsignedWrap' : 4470, + 'ExplicitInterpAMD' : 4999, + 'OverrideCoverageNV' : 5248, + 'PassthroughNV' : 5250, + 'ViewportRelativeNV' : 5252, + 'SecondaryViewportRelativeNV' : 5256, + 'PerPrimitiveNV' : 5271, + 'PerViewNV' : 5272, + 'PerTaskNV' : 5273, + 'PerVertexNV' : 5285, + 'NonUniform' : 5300, + 'NonUniformEXT' : 5300, + 'RestrictPointer' : 5355, + 'RestrictPointerEXT' : 5355, + 'AliasedPointer' : 5356, + 'AliasedPointerEXT' : 5356, + 'ReferencedIndirectlyINTEL' : 5602, + 'CounterBuffer' : 5634, + 'HlslCounterBufferGOOGLE' : 5634, + 'HlslSemanticGOOGLE' : 5635, + 'UserSemantic' : 5635, + 'UserTypeGOOGLE' : 5636, + 'RegisterINTEL' : 5825, + 'MemoryINTEL' : 5826, + 'NumbanksINTEL' : 5827, + 'BankwidthINTEL' : 5828, + 'MaxPrivateCopiesINTEL' : 5829, + 'SinglepumpINTEL' : 5830, + 'DoublepumpINTEL' : 5831, + 'MaxReplicatesINTEL' : 5832, + 'SimpleDualPortINTEL' : 5833, + 'MergeINTEL' : 5834, + 'BankBitsINTEL' : 5835, + 'ForcePow2DepthINTEL' : 5836, + }, + + 'BuiltIn' : { + 'Position' : 0, + 'PointSize' : 1, + 'ClipDistance' : 3, + 'CullDistance' : 4, + 'VertexId' : 5, + 'InstanceId' : 6, + 'PrimitiveId' : 7, + 'InvocationId' : 8, + 'Layer' : 9, + 'ViewportIndex' : 10, + 'TessLevelOuter' : 11, + 'TessLevelInner' : 12, + 'TessCoord' : 13, + 'PatchVertices' : 14, + 'FragCoord' : 15, + 'PointCoord' : 16, + 'FrontFacing' : 17, + 'SampleId' : 18, + 'SamplePosition' : 19, + 'SampleMask' : 20, + 'FragDepth' : 22, + 'HelperInvocation' : 23, + 'NumWorkgroups' : 24, + 'WorkgroupSize' : 25, + 'WorkgroupId' : 26, + 'LocalInvocationId' : 27, + 'GlobalInvocationId' : 28, + 'LocalInvocationIndex' : 29, + 'WorkDim' : 30, + 'GlobalSize' : 31, + 'EnqueuedWorkgroupSize' : 32, + 'GlobalOffset' : 33, + 'GlobalLinearId' : 34, + 'SubgroupSize' : 36, + 'SubgroupMaxSize' : 37, + 'NumSubgroups' : 38, + 'NumEnqueuedSubgroups' : 39, + 'SubgroupId' : 40, + 'SubgroupLocalInvocationId' : 41, + 'VertexIndex' : 42, + 'InstanceIndex' : 43, + 'SubgroupEqMask' : 4416, + 'SubgroupEqMaskKHR' : 4416, + 'SubgroupGeMask' : 4417, + 'SubgroupGeMaskKHR' : 4417, + 'SubgroupGtMask' : 4418, + 'SubgroupGtMaskKHR' : 4418, + 'SubgroupLeMask' : 4419, + 'SubgroupLeMaskKHR' : 4419, + 'SubgroupLtMask' : 4420, + 'SubgroupLtMaskKHR' : 4420, + 'BaseVertex' : 4424, + 'BaseInstance' : 4425, + 'DrawIndex' : 4426, + 'PrimitiveShadingRateKHR' : 4432, + 'DeviceIndex' : 4438, + 'ViewIndex' : 4440, + 'ShadingRateKHR' : 4444, + 'BaryCoordNoPerspAMD' : 4992, + 'BaryCoordNoPerspCentroidAMD' : 4993, + 'BaryCoordNoPerspSampleAMD' : 4994, + 'BaryCoordSmoothAMD' : 4995, + 'BaryCoordSmoothCentroidAMD' : 4996, + 'BaryCoordSmoothSampleAMD' : 4997, + 'BaryCoordPullModelAMD' : 4998, + 'FragStencilRefEXT' : 5014, + 'ViewportMaskNV' : 5253, + 'SecondaryPositionNV' : 5257, + 'SecondaryViewportMaskNV' : 5258, + 'PositionPerViewNV' : 5261, + 'ViewportMaskPerViewNV' : 5262, + 'FullyCoveredEXT' : 5264, + 'TaskCountNV' : 5274, + 'PrimitiveCountNV' : 5275, + 'PrimitiveIndicesNV' : 5276, + 'ClipDistancePerViewNV' : 5277, + 'CullDistancePerViewNV' : 5278, + 'LayerPerViewNV' : 5279, + 'MeshViewCountNV' : 5280, + 'MeshViewIndicesNV' : 5281, + 'BaryCoordNV' : 5286, + 'BaryCoordNoPerspNV' : 5287, + 'FragSizeEXT' : 5292, + 'FragmentSizeNV' : 5292, + 'FragInvocationCountEXT' : 5293, + 'InvocationsPerPixelNV' : 5293, + 'LaunchIdKHR' : 5319, + 'LaunchIdNV' : 5319, + 'LaunchSizeKHR' : 5320, + 'LaunchSizeNV' : 5320, + 'WorldRayOriginKHR' : 5321, + 'WorldRayOriginNV' : 5321, + 'WorldRayDirectionKHR' : 5322, + 'WorldRayDirectionNV' : 5322, + 'ObjectRayOriginKHR' : 5323, + 'ObjectRayOriginNV' : 5323, + 'ObjectRayDirectionKHR' : 5324, + 'ObjectRayDirectionNV' : 5324, + 'RayTminKHR' : 5325, + 'RayTminNV' : 5325, + 'RayTmaxKHR' : 5326, + 'RayTmaxNV' : 5326, + 'InstanceCustomIndexKHR' : 5327, + 'InstanceCustomIndexNV' : 5327, + 'ObjectToWorldKHR' : 5330, + 'ObjectToWorldNV' : 5330, + 'WorldToObjectKHR' : 5331, + 'WorldToObjectNV' : 5331, + 'HitTKHR' : 5332, + 'HitTNV' : 5332, + 'HitKindKHR' : 5333, + 'HitKindNV' : 5333, + 'IncomingRayFlagsKHR' : 5351, + 'IncomingRayFlagsNV' : 5351, + 'RayGeometryIndexKHR' : 5352, + 'WarpsPerSMNV' : 5374, + 'SMCountNV' : 5375, + 'WarpIDNV' : 5376, + 'SMIDNV' : 5377, + }, + + 'SelectionControlShift' : { + 'Flatten' : 0, + 'DontFlatten' : 1, + }, + + 'SelectionControlMask' : { + 'MaskNone' : 0, + 'Flatten' : 0x00000001, + 'DontFlatten' : 0x00000002, + }, + + 'LoopControlShift' : { + 'Unroll' : 0, + 'DontUnroll' : 1, + 'DependencyInfinite' : 2, + 'DependencyLength' : 3, + 'MinIterations' : 4, + 'MaxIterations' : 5, + 'IterationMultiple' : 6, + 'PeelCount' : 7, + 'PartialCount' : 8, + 'InitiationIntervalINTEL' : 16, + 'MaxConcurrencyINTEL' : 17, + 'DependencyArrayINTEL' : 18, + 'PipelineEnableINTEL' : 19, + 'LoopCoalesceINTEL' : 20, + 'MaxInterleavingINTEL' : 21, + 'SpeculatedIterationsINTEL' : 22, + }, + + 'LoopControlMask' : { + 'MaskNone' : 0, + 'Unroll' : 0x00000001, + 'DontUnroll' : 0x00000002, + 'DependencyInfinite' : 0x00000004, + 'DependencyLength' : 0x00000008, + 'MinIterations' : 0x00000010, + 'MaxIterations' : 0x00000020, + 'IterationMultiple' : 0x00000040, + 'PeelCount' : 0x00000080, + 'PartialCount' : 0x00000100, + 'InitiationIntervalINTEL' : 0x00010000, + 'MaxConcurrencyINTEL' : 0x00020000, + 'DependencyArrayINTEL' : 0x00040000, + 'PipelineEnableINTEL' : 0x00080000, + 'LoopCoalesceINTEL' : 0x00100000, + 'MaxInterleavingINTEL' : 0x00200000, + 'SpeculatedIterationsINTEL' : 0x00400000, + }, + + 'FunctionControlShift' : { + 'Inline' : 0, + 'DontInline' : 1, + 'Pure' : 2, + 'Const' : 3, + }, + + 'FunctionControlMask' : { + 'MaskNone' : 0, + 'Inline' : 0x00000001, + 'DontInline' : 0x00000002, + 'Pure' : 0x00000004, + 'Const' : 0x00000008, + }, + + 'MemorySemanticsShift' : { + 'Acquire' : 1, + 'Release' : 2, + 'AcquireRelease' : 3, + 'SequentiallyConsistent' : 4, + 'UniformMemory' : 6, + 'SubgroupMemory' : 7, + 'WorkgroupMemory' : 8, + 'CrossWorkgroupMemory' : 9, + 'AtomicCounterMemory' : 10, + 'ImageMemory' : 11, + 'OutputMemory' : 12, + 'OutputMemoryKHR' : 12, + 'MakeAvailable' : 13, + 'MakeAvailableKHR' : 13, + 'MakeVisible' : 14, + 'MakeVisibleKHR' : 14, + 'Volatile' : 15, + }, + + 'MemorySemanticsMask' : { + 'MaskNone' : 0, + 'Acquire' : 0x00000002, + 'Release' : 0x00000004, + 'AcquireRelease' : 0x00000008, + 'SequentiallyConsistent' : 0x00000010, + 'UniformMemory' : 0x00000040, + 'SubgroupMemory' : 0x00000080, + 'WorkgroupMemory' : 0x00000100, + 'CrossWorkgroupMemory' : 0x00000200, + 'AtomicCounterMemory' : 0x00000400, + 'ImageMemory' : 0x00000800, + 'OutputMemory' : 0x00001000, + 'OutputMemoryKHR' : 0x00001000, + 'MakeAvailable' : 0x00002000, + 'MakeAvailableKHR' : 0x00002000, + 'MakeVisible' : 0x00004000, + 'MakeVisibleKHR' : 0x00004000, + 'Volatile' : 0x00008000, + }, + + 'MemoryAccessShift' : { + 'Volatile' : 0, + 'Aligned' : 1, + 'Nontemporal' : 2, + 'MakePointerAvailable' : 3, + 'MakePointerAvailableKHR' : 3, + 'MakePointerVisible' : 4, + 'MakePointerVisibleKHR' : 4, + 'NonPrivatePointer' : 5, + 'NonPrivatePointerKHR' : 5, + }, + + 'MemoryAccessMask' : { + 'MaskNone' : 0, + 'Volatile' : 0x00000001, + 'Aligned' : 0x00000002, + 'Nontemporal' : 0x00000004, + 'MakePointerAvailable' : 0x00000008, + 'MakePointerAvailableKHR' : 0x00000008, + 'MakePointerVisible' : 0x00000010, + 'MakePointerVisibleKHR' : 0x00000010, + 'NonPrivatePointer' : 0x00000020, + 'NonPrivatePointerKHR' : 0x00000020, + }, + + 'Scope' : { + 'CrossDevice' : 0, + 'Device' : 1, + 'Workgroup' : 2, + 'Subgroup' : 3, + 'Invocation' : 4, + 'QueueFamily' : 5, + 'QueueFamilyKHR' : 5, + 'ShaderCallKHR' : 6, + }, + + 'GroupOperation' : { + 'Reduce' : 0, + 'InclusiveScan' : 1, + 'ExclusiveScan' : 2, + 'ClusteredReduce' : 3, + 'PartitionedReduceNV' : 6, + 'PartitionedInclusiveScanNV' : 7, + 'PartitionedExclusiveScanNV' : 8, + }, + + 'KernelEnqueueFlags' : { + 'NoWait' : 0, + 'WaitKernel' : 1, + 'WaitWorkGroup' : 2, + }, + + 'KernelProfilingInfoShift' : { + 'CmdExecTime' : 0, + }, + + 'KernelProfilingInfoMask' : { + 'MaskNone' : 0, + 'CmdExecTime' : 0x00000001, + }, + + 'Capability' : { + 'Matrix' : 0, + 'Shader' : 1, + 'Geometry' : 2, + 'Tessellation' : 3, + 'Addresses' : 4, + 'Linkage' : 5, + 'Kernel' : 6, + 'Vector16' : 7, + 'Float16Buffer' : 8, + 'Float16' : 9, + 'Float64' : 10, + 'Int64' : 11, + 'Int64Atomics' : 12, + 'ImageBasic' : 13, + 'ImageReadWrite' : 14, + 'ImageMipmap' : 15, + 'Pipes' : 17, + 'Groups' : 18, + 'DeviceEnqueue' : 19, + 'LiteralSampler' : 20, + 'AtomicStorage' : 21, + 'Int16' : 22, + 'TessellationPointSize' : 23, + 'GeometryPointSize' : 24, + 'ImageGatherExtended' : 25, + 'StorageImageMultisample' : 27, + 'UniformBufferArrayDynamicIndexing' : 28, + 'SampledImageArrayDynamicIndexing' : 29, + 'StorageBufferArrayDynamicIndexing' : 30, + 'StorageImageArrayDynamicIndexing' : 31, + 'ClipDistance' : 32, + 'CullDistance' : 33, + 'ImageCubeArray' : 34, + 'SampleRateShading' : 35, + 'ImageRect' : 36, + 'SampledRect' : 37, + 'GenericPointer' : 38, + 'Int8' : 39, + 'InputAttachment' : 40, + 'SparseResidency' : 41, + 'MinLod' : 42, + 'Sampled1D' : 43, + 'Image1D' : 44, + 'SampledCubeArray' : 45, + 'SampledBuffer' : 46, + 'ImageBuffer' : 47, + 'ImageMSArray' : 48, + 'StorageImageExtendedFormats' : 49, + 'ImageQuery' : 50, + 'DerivativeControl' : 51, + 'InterpolationFunction' : 52, + 'TransformFeedback' : 53, + 'GeometryStreams' : 54, + 'StorageImageReadWithoutFormat' : 55, + 'StorageImageWriteWithoutFormat' : 56, + 'MultiViewport' : 57, + 'SubgroupDispatch' : 58, + 'NamedBarrier' : 59, + 'PipeStorage' : 60, + 'GroupNonUniform' : 61, + 'GroupNonUniformVote' : 62, + 'GroupNonUniformArithmetic' : 63, + 'GroupNonUniformBallot' : 64, + 'GroupNonUniformShuffle' : 65, + 'GroupNonUniformShuffleRelative' : 66, + 'GroupNonUniformClustered' : 67, + 'GroupNonUniformQuad' : 68, + 'ShaderLayer' : 69, + 'ShaderViewportIndex' : 70, + 'FragmentShadingRateKHR' : 4422, + 'SubgroupBallotKHR' : 4423, + 'DrawParameters' : 4427, + 'SubgroupVoteKHR' : 4431, + 'StorageBuffer16BitAccess' : 4433, + 'StorageUniformBufferBlock16' : 4433, + 'StorageUniform16' : 4434, + 'UniformAndStorageBuffer16BitAccess' : 4434, + 'StoragePushConstant16' : 4435, + 'StorageInputOutput16' : 4436, + 'DeviceGroup' : 4437, + 'MultiView' : 4439, + 'VariablePointersStorageBuffer' : 4441, + 'VariablePointers' : 4442, + 'AtomicStorageOps' : 4445, + 'SampleMaskPostDepthCoverage' : 4447, + 'StorageBuffer8BitAccess' : 4448, + 'UniformAndStorageBuffer8BitAccess' : 4449, + 'StoragePushConstant8' : 4450, + 'DenormPreserve' : 4464, + 'DenormFlushToZero' : 4465, + 'SignedZeroInfNanPreserve' : 4466, + 'RoundingModeRTE' : 4467, + 'RoundingModeRTZ' : 4468, + 'RayQueryProvisionalKHR' : 4471, + 'RayTraversalPrimitiveCullingProvisionalKHR' : 4478, + 'Float16ImageAMD' : 5008, + 'ImageGatherBiasLodAMD' : 5009, + 'FragmentMaskAMD' : 5010, + 'StencilExportEXT' : 5013, + 'ImageReadWriteLodAMD' : 5015, + 'Int64ImageEXT' : 5016, + 'ShaderClockKHR' : 5055, + 'SampleMaskOverrideCoverageNV' : 5249, + 'GeometryShaderPassthroughNV' : 5251, + 'ShaderViewportIndexLayerEXT' : 5254, + 'ShaderViewportIndexLayerNV' : 5254, + 'ShaderViewportMaskNV' : 5255, + 'ShaderStereoViewNV' : 5259, + 'PerViewAttributesNV' : 5260, + 'FragmentFullyCoveredEXT' : 5265, + 'MeshShadingNV' : 5266, + 'ImageFootprintNV' : 5282, + 'FragmentBarycentricNV' : 5284, + 'ComputeDerivativeGroupQuadsNV' : 5288, + 'FragmentDensityEXT' : 5291, + 'ShadingRateNV' : 5291, + 'GroupNonUniformPartitionedNV' : 5297, + 'ShaderNonUniform' : 5301, + 'ShaderNonUniformEXT' : 5301, + 'RuntimeDescriptorArray' : 5302, + 'RuntimeDescriptorArrayEXT' : 5302, + 'InputAttachmentArrayDynamicIndexing' : 5303, + 'InputAttachmentArrayDynamicIndexingEXT' : 5303, + 'UniformTexelBufferArrayDynamicIndexing' : 5304, + 'UniformTexelBufferArrayDynamicIndexingEXT' : 5304, + 'StorageTexelBufferArrayDynamicIndexing' : 5305, + 'StorageTexelBufferArrayDynamicIndexingEXT' : 5305, + 'UniformBufferArrayNonUniformIndexing' : 5306, + 'UniformBufferArrayNonUniformIndexingEXT' : 5306, + 'SampledImageArrayNonUniformIndexing' : 5307, + 'SampledImageArrayNonUniformIndexingEXT' : 5307, + 'StorageBufferArrayNonUniformIndexing' : 5308, + 'StorageBufferArrayNonUniformIndexingEXT' : 5308, + 'StorageImageArrayNonUniformIndexing' : 5309, + 'StorageImageArrayNonUniformIndexingEXT' : 5309, + 'InputAttachmentArrayNonUniformIndexing' : 5310, + 'InputAttachmentArrayNonUniformIndexingEXT' : 5310, + 'UniformTexelBufferArrayNonUniformIndexing' : 5311, + 'UniformTexelBufferArrayNonUniformIndexingEXT' : 5311, + 'StorageTexelBufferArrayNonUniformIndexing' : 5312, + 'StorageTexelBufferArrayNonUniformIndexingEXT' : 5312, + 'RayTracingNV' : 5340, + 'VulkanMemoryModel' : 5345, + 'VulkanMemoryModelKHR' : 5345, + 'VulkanMemoryModelDeviceScope' : 5346, + 'VulkanMemoryModelDeviceScopeKHR' : 5346, + 'PhysicalStorageBufferAddresses' : 5347, + 'PhysicalStorageBufferAddressesEXT' : 5347, + 'ComputeDerivativeGroupLinearNV' : 5350, + 'RayTracingProvisionalKHR' : 5353, + 'CooperativeMatrixNV' : 5357, + 'FragmentShaderSampleInterlockEXT' : 5363, + 'FragmentShaderShadingRateInterlockEXT' : 5372, + 'ShaderSMBuiltinsNV' : 5373, + 'FragmentShaderPixelInterlockEXT' : 5378, + 'DemoteToHelperInvocationEXT' : 5379, + 'SubgroupShuffleINTEL' : 5568, + 'SubgroupBufferBlockIOINTEL' : 5569, + 'SubgroupImageBlockIOINTEL' : 5570, + 'SubgroupImageMediaBlockIOINTEL' : 5579, + 'IntegerFunctions2INTEL' : 5584, + 'FunctionPointersINTEL' : 5603, + 'IndirectReferencesINTEL' : 5604, + 'SubgroupAvcMotionEstimationINTEL' : 5696, + 'SubgroupAvcMotionEstimationIntraINTEL' : 5697, + 'SubgroupAvcMotionEstimationChromaINTEL' : 5698, + 'FPGAMemoryAttributesINTEL' : 5824, + 'UnstructuredLoopControlsINTEL' : 5886, + 'FPGALoopControlsINTEL' : 5888, + 'KernelAttributesINTEL' : 5892, + 'FPGAKernelAttributesINTEL' : 5897, + 'BlockingPipesINTEL' : 5945, + 'FPGARegINTEL' : 5948, + 'AtomicFloat32AddEXT' : 6033, + 'AtomicFloat64AddEXT' : 6034, + }, + + 'RayFlagsShift' : { + 'OpaqueKHR' : 0, + 'NoOpaqueKHR' : 1, + 'TerminateOnFirstHitKHR' : 2, + 'SkipClosestHitShaderKHR' : 3, + 'CullBackFacingTrianglesKHR' : 4, + 'CullFrontFacingTrianglesKHR' : 5, + 'CullOpaqueKHR' : 6, + 'CullNoOpaqueKHR' : 7, + 'SkipTrianglesKHR' : 8, + 'SkipAABBsKHR' : 9, + }, + + 'RayFlagsMask' : { + 'MaskNone' : 0, + 'OpaqueKHR' : 0x00000001, + 'NoOpaqueKHR' : 0x00000002, + 'TerminateOnFirstHitKHR' : 0x00000004, + 'SkipClosestHitShaderKHR' : 0x00000008, + 'CullBackFacingTrianglesKHR' : 0x00000010, + 'CullFrontFacingTrianglesKHR' : 0x00000020, + 'CullOpaqueKHR' : 0x00000040, + 'CullNoOpaqueKHR' : 0x00000080, + 'SkipTrianglesKHR' : 0x00000100, + 'SkipAABBsKHR' : 0x00000200, + }, + + 'RayQueryIntersection' : { + 'RayQueryCandidateIntersectionKHR' : 0, + 'RayQueryCommittedIntersectionKHR' : 1, + }, + + 'RayQueryCommittedIntersectionType' : { + 'RayQueryCommittedIntersectionNoneKHR' : 0, + 'RayQueryCommittedIntersectionTriangleKHR' : 1, + 'RayQueryCommittedIntersectionGeneratedKHR' : 2, + }, + + 'RayQueryCandidateIntersectionType' : { + 'RayQueryCandidateIntersectionTriangleKHR' : 0, + 'RayQueryCandidateIntersectionAABBKHR' : 1, + }, + + 'FragmentShadingRateShift' : { + 'Vertical2Pixels' : 0, + 'Vertical4Pixels' : 1, + 'Horizontal2Pixels' : 2, + 'Horizontal4Pixels' : 3, + }, + + 'FragmentShadingRateMask' : { + 'MaskNone' : 0, + 'Vertical2Pixels' : 0x00000001, + 'Vertical4Pixels' : 0x00000002, + 'Horizontal2Pixels' : 0x00000004, + 'Horizontal4Pixels' : 0x00000008, + }, + + 'Op' : { + 'OpNop' : 0, + 'OpUndef' : 1, + 'OpSourceContinued' : 2, + 'OpSource' : 3, + 'OpSourceExtension' : 4, + 'OpName' : 5, + 'OpMemberName' : 6, + 'OpString' : 7, + 'OpLine' : 8, + 'OpExtension' : 10, + 'OpExtInstImport' : 11, + 'OpExtInst' : 12, + 'OpMemoryModel' : 14, + 'OpEntryPoint' : 15, + 'OpExecutionMode' : 16, + 'OpCapability' : 17, + 'OpTypeVoid' : 19, + 'OpTypeBool' : 20, + 'OpTypeInt' : 21, + 'OpTypeFloat' : 22, + 'OpTypeVector' : 23, + 'OpTypeMatrix' : 24, + 'OpTypeImage' : 25, + 'OpTypeSampler' : 26, + 'OpTypeSampledImage' : 27, + 'OpTypeArray' : 28, + 'OpTypeRuntimeArray' : 29, + 'OpTypeStruct' : 30, + 'OpTypeOpaque' : 31, + 'OpTypePointer' : 32, + 'OpTypeFunction' : 33, + 'OpTypeEvent' : 34, + 'OpTypeDeviceEvent' : 35, + 'OpTypeReserveId' : 36, + 'OpTypeQueue' : 37, + 'OpTypePipe' : 38, + 'OpTypeForwardPointer' : 39, + 'OpConstantTrue' : 41, + 'OpConstantFalse' : 42, + 'OpConstant' : 43, + 'OpConstantComposite' : 44, + 'OpConstantSampler' : 45, + 'OpConstantNull' : 46, + 'OpSpecConstantTrue' : 48, + 'OpSpecConstantFalse' : 49, + 'OpSpecConstant' : 50, + 'OpSpecConstantComposite' : 51, + 'OpSpecConstantOp' : 52, + 'OpFunction' : 54, + 'OpFunctionParameter' : 55, + 'OpFunctionEnd' : 56, + 'OpFunctionCall' : 57, + 'OpVariable' : 59, + 'OpImageTexelPointer' : 60, + 'OpLoad' : 61, + 'OpStore' : 62, + 'OpCopyMemory' : 63, + 'OpCopyMemorySized' : 64, + 'OpAccessChain' : 65, + 'OpInBoundsAccessChain' : 66, + 'OpPtrAccessChain' : 67, + 'OpArrayLength' : 68, + 'OpGenericPtrMemSemantics' : 69, + 'OpInBoundsPtrAccessChain' : 70, + 'OpDecorate' : 71, + 'OpMemberDecorate' : 72, + 'OpDecorationGroup' : 73, + 'OpGroupDecorate' : 74, + 'OpGroupMemberDecorate' : 75, + 'OpVectorExtractDynamic' : 77, + 'OpVectorInsertDynamic' : 78, + 'OpVectorShuffle' : 79, + 'OpCompositeConstruct' : 80, + 'OpCompositeExtract' : 81, + 'OpCompositeInsert' : 82, + 'OpCopyObject' : 83, + 'OpTranspose' : 84, + 'OpSampledImage' : 86, + 'OpImageSampleImplicitLod' : 87, + 'OpImageSampleExplicitLod' : 88, + 'OpImageSampleDrefImplicitLod' : 89, + 'OpImageSampleDrefExplicitLod' : 90, + 'OpImageSampleProjImplicitLod' : 91, + 'OpImageSampleProjExplicitLod' : 92, + 'OpImageSampleProjDrefImplicitLod' : 93, + 'OpImageSampleProjDrefExplicitLod' : 94, + 'OpImageFetch' : 95, + 'OpImageGather' : 96, + 'OpImageDrefGather' : 97, + 'OpImageRead' : 98, + 'OpImageWrite' : 99, + 'OpImage' : 100, + 'OpImageQueryFormat' : 101, + 'OpImageQueryOrder' : 102, + 'OpImageQuerySizeLod' : 103, + 'OpImageQuerySize' : 104, + 'OpImageQueryLod' : 105, + 'OpImageQueryLevels' : 106, + 'OpImageQuerySamples' : 107, + 'OpConvertFToU' : 109, + 'OpConvertFToS' : 110, + 'OpConvertSToF' : 111, + 'OpConvertUToF' : 112, + 'OpUConvert' : 113, + 'OpSConvert' : 114, + 'OpFConvert' : 115, + 'OpQuantizeToF16' : 116, + 'OpConvertPtrToU' : 117, + 'OpSatConvertSToU' : 118, + 'OpSatConvertUToS' : 119, + 'OpConvertUToPtr' : 120, + 'OpPtrCastToGeneric' : 121, + 'OpGenericCastToPtr' : 122, + 'OpGenericCastToPtrExplicit' : 123, + 'OpBitcast' : 124, + 'OpSNegate' : 126, + 'OpFNegate' : 127, + 'OpIAdd' : 128, + 'OpFAdd' : 129, + 'OpISub' : 130, + 'OpFSub' : 131, + 'OpIMul' : 132, + 'OpFMul' : 133, + 'OpUDiv' : 134, + 'OpSDiv' : 135, + 'OpFDiv' : 136, + 'OpUMod' : 137, + 'OpSRem' : 138, + 'OpSMod' : 139, + 'OpFRem' : 140, + 'OpFMod' : 141, + 'OpVectorTimesScalar' : 142, + 'OpMatrixTimesScalar' : 143, + 'OpVectorTimesMatrix' : 144, + 'OpMatrixTimesVector' : 145, + 'OpMatrixTimesMatrix' : 146, + 'OpOuterProduct' : 147, + 'OpDot' : 148, + 'OpIAddCarry' : 149, + 'OpISubBorrow' : 150, + 'OpUMulExtended' : 151, + 'OpSMulExtended' : 152, + 'OpAny' : 154, + 'OpAll' : 155, + 'OpIsNan' : 156, + 'OpIsInf' : 157, + 'OpIsFinite' : 158, + 'OpIsNormal' : 159, + 'OpSignBitSet' : 160, + 'OpLessOrGreater' : 161, + 'OpOrdered' : 162, + 'OpUnordered' : 163, + 'OpLogicalEqual' : 164, + 'OpLogicalNotEqual' : 165, + 'OpLogicalOr' : 166, + 'OpLogicalAnd' : 167, + 'OpLogicalNot' : 168, + 'OpSelect' : 169, + 'OpIEqual' : 170, + 'OpINotEqual' : 171, + 'OpUGreaterThan' : 172, + 'OpSGreaterThan' : 173, + 'OpUGreaterThanEqual' : 174, + 'OpSGreaterThanEqual' : 175, + 'OpULessThan' : 176, + 'OpSLessThan' : 177, + 'OpULessThanEqual' : 178, + 'OpSLessThanEqual' : 179, + 'OpFOrdEqual' : 180, + 'OpFUnordEqual' : 181, + 'OpFOrdNotEqual' : 182, + 'OpFUnordNotEqual' : 183, + 'OpFOrdLessThan' : 184, + 'OpFUnordLessThan' : 185, + 'OpFOrdGreaterThan' : 186, + 'OpFUnordGreaterThan' : 187, + 'OpFOrdLessThanEqual' : 188, + 'OpFUnordLessThanEqual' : 189, + 'OpFOrdGreaterThanEqual' : 190, + 'OpFUnordGreaterThanEqual' : 191, + 'OpShiftRightLogical' : 194, + 'OpShiftRightArithmetic' : 195, + 'OpShiftLeftLogical' : 196, + 'OpBitwiseOr' : 197, + 'OpBitwiseXor' : 198, + 'OpBitwiseAnd' : 199, + 'OpNot' : 200, + 'OpBitFieldInsert' : 201, + 'OpBitFieldSExtract' : 202, + 'OpBitFieldUExtract' : 203, + 'OpBitReverse' : 204, + 'OpBitCount' : 205, + 'OpDPdx' : 207, + 'OpDPdy' : 208, + 'OpFwidth' : 209, + 'OpDPdxFine' : 210, + 'OpDPdyFine' : 211, + 'OpFwidthFine' : 212, + 'OpDPdxCoarse' : 213, + 'OpDPdyCoarse' : 214, + 'OpFwidthCoarse' : 215, + 'OpEmitVertex' : 218, + 'OpEndPrimitive' : 219, + 'OpEmitStreamVertex' : 220, + 'OpEndStreamPrimitive' : 221, + 'OpControlBarrier' : 224, + 'OpMemoryBarrier' : 225, + 'OpAtomicLoad' : 227, + 'OpAtomicStore' : 228, + 'OpAtomicExchange' : 229, + 'OpAtomicCompareExchange' : 230, + 'OpAtomicCompareExchangeWeak' : 231, + 'OpAtomicIIncrement' : 232, + 'OpAtomicIDecrement' : 233, + 'OpAtomicIAdd' : 234, + 'OpAtomicISub' : 235, + 'OpAtomicSMin' : 236, + 'OpAtomicUMin' : 237, + 'OpAtomicSMax' : 238, + 'OpAtomicUMax' : 239, + 'OpAtomicAnd' : 240, + 'OpAtomicOr' : 241, + 'OpAtomicXor' : 242, + 'OpPhi' : 245, + 'OpLoopMerge' : 246, + 'OpSelectionMerge' : 247, + 'OpLabel' : 248, + 'OpBranch' : 249, + 'OpBranchConditional' : 250, + 'OpSwitch' : 251, + 'OpKill' : 252, + 'OpReturn' : 253, + 'OpReturnValue' : 254, + 'OpUnreachable' : 255, + 'OpLifetimeStart' : 256, + 'OpLifetimeStop' : 257, + 'OpGroupAsyncCopy' : 259, + 'OpGroupWaitEvents' : 260, + 'OpGroupAll' : 261, + 'OpGroupAny' : 262, + 'OpGroupBroadcast' : 263, + 'OpGroupIAdd' : 264, + 'OpGroupFAdd' : 265, + 'OpGroupFMin' : 266, + 'OpGroupUMin' : 267, + 'OpGroupSMin' : 268, + 'OpGroupFMax' : 269, + 'OpGroupUMax' : 270, + 'OpGroupSMax' : 271, + 'OpReadPipe' : 274, + 'OpWritePipe' : 275, + 'OpReservedReadPipe' : 276, + 'OpReservedWritePipe' : 277, + 'OpReserveReadPipePackets' : 278, + 'OpReserveWritePipePackets' : 279, + 'OpCommitReadPipe' : 280, + 'OpCommitWritePipe' : 281, + 'OpIsValidReserveId' : 282, + 'OpGetNumPipePackets' : 283, + 'OpGetMaxPipePackets' : 284, + 'OpGroupReserveReadPipePackets' : 285, + 'OpGroupReserveWritePipePackets' : 286, + 'OpGroupCommitReadPipe' : 287, + 'OpGroupCommitWritePipe' : 288, + 'OpEnqueueMarker' : 291, + 'OpEnqueueKernel' : 292, + 'OpGetKernelNDrangeSubGroupCount' : 293, + 'OpGetKernelNDrangeMaxSubGroupSize' : 294, + 'OpGetKernelWorkGroupSize' : 295, + 'OpGetKernelPreferredWorkGroupSizeMultiple' : 296, + 'OpRetainEvent' : 297, + 'OpReleaseEvent' : 298, + 'OpCreateUserEvent' : 299, + 'OpIsValidEvent' : 300, + 'OpSetUserEventStatus' : 301, + 'OpCaptureEventProfilingInfo' : 302, + 'OpGetDefaultQueue' : 303, + 'OpBuildNDRange' : 304, + 'OpImageSparseSampleImplicitLod' : 305, + 'OpImageSparseSampleExplicitLod' : 306, + 'OpImageSparseSampleDrefImplicitLod' : 307, + 'OpImageSparseSampleDrefExplicitLod' : 308, + 'OpImageSparseSampleProjImplicitLod' : 309, + 'OpImageSparseSampleProjExplicitLod' : 310, + 'OpImageSparseSampleProjDrefImplicitLod' : 311, + 'OpImageSparseSampleProjDrefExplicitLod' : 312, + 'OpImageSparseFetch' : 313, + 'OpImageSparseGather' : 314, + 'OpImageSparseDrefGather' : 315, + 'OpImageSparseTexelsResident' : 316, + 'OpNoLine' : 317, + 'OpAtomicFlagTestAndSet' : 318, + 'OpAtomicFlagClear' : 319, + 'OpImageSparseRead' : 320, + 'OpSizeOf' : 321, + 'OpTypePipeStorage' : 322, + 'OpConstantPipeStorage' : 323, + 'OpCreatePipeFromPipeStorage' : 324, + 'OpGetKernelLocalSizeForSubgroupCount' : 325, + 'OpGetKernelMaxNumSubgroups' : 326, + 'OpTypeNamedBarrier' : 327, + 'OpNamedBarrierInitialize' : 328, + 'OpMemoryNamedBarrier' : 329, + 'OpModuleProcessed' : 330, + 'OpExecutionModeId' : 331, + 'OpDecorateId' : 332, + 'OpGroupNonUniformElect' : 333, + 'OpGroupNonUniformAll' : 334, + 'OpGroupNonUniformAny' : 335, + 'OpGroupNonUniformAllEqual' : 336, + 'OpGroupNonUniformBroadcast' : 337, + 'OpGroupNonUniformBroadcastFirst' : 338, + 'OpGroupNonUniformBallot' : 339, + 'OpGroupNonUniformInverseBallot' : 340, + 'OpGroupNonUniformBallotBitExtract' : 341, + 'OpGroupNonUniformBallotBitCount' : 342, + 'OpGroupNonUniformBallotFindLSB' : 343, + 'OpGroupNonUniformBallotFindMSB' : 344, + 'OpGroupNonUniformShuffle' : 345, + 'OpGroupNonUniformShuffleXor' : 346, + 'OpGroupNonUniformShuffleUp' : 347, + 'OpGroupNonUniformShuffleDown' : 348, + 'OpGroupNonUniformIAdd' : 349, + 'OpGroupNonUniformFAdd' : 350, + 'OpGroupNonUniformIMul' : 351, + 'OpGroupNonUniformFMul' : 352, + 'OpGroupNonUniformSMin' : 353, + 'OpGroupNonUniformUMin' : 354, + 'OpGroupNonUniformFMin' : 355, + 'OpGroupNonUniformSMax' : 356, + 'OpGroupNonUniformUMax' : 357, + 'OpGroupNonUniformFMax' : 358, + 'OpGroupNonUniformBitwiseAnd' : 359, + 'OpGroupNonUniformBitwiseOr' : 360, + 'OpGroupNonUniformBitwiseXor' : 361, + 'OpGroupNonUniformLogicalAnd' : 362, + 'OpGroupNonUniformLogicalOr' : 363, + 'OpGroupNonUniformLogicalXor' : 364, + 'OpGroupNonUniformQuadBroadcast' : 365, + 'OpGroupNonUniformQuadSwap' : 366, + 'OpCopyLogical' : 400, + 'OpPtrEqual' : 401, + 'OpPtrNotEqual' : 402, + 'OpPtrDiff' : 403, + 'OpTerminateInvocation' : 4416, + 'OpSubgroupBallotKHR' : 4421, + 'OpSubgroupFirstInvocationKHR' : 4422, + 'OpSubgroupAllKHR' : 4428, + 'OpSubgroupAnyKHR' : 4429, + 'OpSubgroupAllEqualKHR' : 4430, + 'OpSubgroupReadInvocationKHR' : 4432, + 'OpTypeRayQueryProvisionalKHR' : 4472, + 'OpRayQueryInitializeKHR' : 4473, + 'OpRayQueryTerminateKHR' : 4474, + 'OpRayQueryGenerateIntersectionKHR' : 4475, + 'OpRayQueryConfirmIntersectionKHR' : 4476, + 'OpRayQueryProceedKHR' : 4477, + 'OpRayQueryGetIntersectionTypeKHR' : 4479, + 'OpGroupIAddNonUniformAMD' : 5000, + 'OpGroupFAddNonUniformAMD' : 5001, + 'OpGroupFMinNonUniformAMD' : 5002, + 'OpGroupUMinNonUniformAMD' : 5003, + 'OpGroupSMinNonUniformAMD' : 5004, + 'OpGroupFMaxNonUniformAMD' : 5005, + 'OpGroupUMaxNonUniformAMD' : 5006, + 'OpGroupSMaxNonUniformAMD' : 5007, + 'OpFragmentMaskFetchAMD' : 5011, + 'OpFragmentFetchAMD' : 5012, + 'OpReadClockKHR' : 5056, + 'OpImageSampleFootprintNV' : 5283, + 'OpGroupNonUniformPartitionNV' : 5296, + 'OpWritePackedPrimitiveIndices4x8NV' : 5299, + 'OpReportIntersectionKHR' : 5334, + 'OpReportIntersectionNV' : 5334, + 'OpIgnoreIntersectionKHR' : 5335, + 'OpIgnoreIntersectionNV' : 5335, + 'OpTerminateRayKHR' : 5336, + 'OpTerminateRayNV' : 5336, + 'OpTraceNV' : 5337, + 'OpTraceRayKHR' : 5337, + 'OpTypeAccelerationStructureKHR' : 5341, + 'OpTypeAccelerationStructureNV' : 5341, + 'OpExecuteCallableKHR' : 5344, + 'OpExecuteCallableNV' : 5344, + 'OpTypeCooperativeMatrixNV' : 5358, + 'OpCooperativeMatrixLoadNV' : 5359, + 'OpCooperativeMatrixStoreNV' : 5360, + 'OpCooperativeMatrixMulAddNV' : 5361, + 'OpCooperativeMatrixLengthNV' : 5362, + 'OpBeginInvocationInterlockEXT' : 5364, + 'OpEndInvocationInterlockEXT' : 5365, + 'OpDemoteToHelperInvocationEXT' : 5380, + 'OpIsHelperInvocationEXT' : 5381, + 'OpSubgroupShuffleINTEL' : 5571, + 'OpSubgroupShuffleDownINTEL' : 5572, + 'OpSubgroupShuffleUpINTEL' : 5573, + 'OpSubgroupShuffleXorINTEL' : 5574, + 'OpSubgroupBlockReadINTEL' : 5575, + 'OpSubgroupBlockWriteINTEL' : 5576, + 'OpSubgroupImageBlockReadINTEL' : 5577, + 'OpSubgroupImageBlockWriteINTEL' : 5578, + 'OpSubgroupImageMediaBlockReadINTEL' : 5580, + 'OpSubgroupImageMediaBlockWriteINTEL' : 5581, + 'OpUCountLeadingZerosINTEL' : 5585, + 'OpUCountTrailingZerosINTEL' : 5586, + 'OpAbsISubINTEL' : 5587, + 'OpAbsUSubINTEL' : 5588, + 'OpIAddSatINTEL' : 5589, + 'OpUAddSatINTEL' : 5590, + 'OpIAverageINTEL' : 5591, + 'OpUAverageINTEL' : 5592, + 'OpIAverageRoundedINTEL' : 5593, + 'OpUAverageRoundedINTEL' : 5594, + 'OpISubSatINTEL' : 5595, + 'OpUSubSatINTEL' : 5596, + 'OpIMul32x16INTEL' : 5597, + 'OpUMul32x16INTEL' : 5598, + 'OpFunctionPointerINTEL' : 5600, + 'OpFunctionPointerCallINTEL' : 5601, + 'OpDecorateString' : 5632, + 'OpDecorateStringGOOGLE' : 5632, + 'OpMemberDecorateString' : 5633, + 'OpMemberDecorateStringGOOGLE' : 5633, + 'OpVmeImageINTEL' : 5699, + 'OpTypeVmeImageINTEL' : 5700, + 'OpTypeAvcImePayloadINTEL' : 5701, + 'OpTypeAvcRefPayloadINTEL' : 5702, + 'OpTypeAvcSicPayloadINTEL' : 5703, + 'OpTypeAvcMcePayloadINTEL' : 5704, + 'OpTypeAvcMceResultINTEL' : 5705, + 'OpTypeAvcImeResultINTEL' : 5706, + 'OpTypeAvcImeResultSingleReferenceStreamoutINTEL' : 5707, + 'OpTypeAvcImeResultDualReferenceStreamoutINTEL' : 5708, + 'OpTypeAvcImeSingleReferenceStreaminINTEL' : 5709, + 'OpTypeAvcImeDualReferenceStreaminINTEL' : 5710, + 'OpTypeAvcRefResultINTEL' : 5711, + 'OpTypeAvcSicResultINTEL' : 5712, + 'OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL' : 5713, + 'OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL' : 5714, + 'OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL' : 5715, + 'OpSubgroupAvcMceSetInterShapePenaltyINTEL' : 5716, + 'OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL' : 5717, + 'OpSubgroupAvcMceSetInterDirectionPenaltyINTEL' : 5718, + 'OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL' : 5719, + 'OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL' : 5720, + 'OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL' : 5721, + 'OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL' : 5722, + 'OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL' : 5723, + 'OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL' : 5724, + 'OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL' : 5725, + 'OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL' : 5726, + 'OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL' : 5727, + 'OpSubgroupAvcMceSetAcOnlyHaarINTEL' : 5728, + 'OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL' : 5729, + 'OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL' : 5730, + 'OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL' : 5731, + 'OpSubgroupAvcMceConvertToImePayloadINTEL' : 5732, + 'OpSubgroupAvcMceConvertToImeResultINTEL' : 5733, + 'OpSubgroupAvcMceConvertToRefPayloadINTEL' : 5734, + 'OpSubgroupAvcMceConvertToRefResultINTEL' : 5735, + 'OpSubgroupAvcMceConvertToSicPayloadINTEL' : 5736, + 'OpSubgroupAvcMceConvertToSicResultINTEL' : 5737, + 'OpSubgroupAvcMceGetMotionVectorsINTEL' : 5738, + 'OpSubgroupAvcMceGetInterDistortionsINTEL' : 5739, + 'OpSubgroupAvcMceGetBestInterDistortionsINTEL' : 5740, + 'OpSubgroupAvcMceGetInterMajorShapeINTEL' : 5741, + 'OpSubgroupAvcMceGetInterMinorShapeINTEL' : 5742, + 'OpSubgroupAvcMceGetInterDirectionsINTEL' : 5743, + 'OpSubgroupAvcMceGetInterMotionVectorCountINTEL' : 5744, + 'OpSubgroupAvcMceGetInterReferenceIdsINTEL' : 5745, + 'OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL' : 5746, + 'OpSubgroupAvcImeInitializeINTEL' : 5747, + 'OpSubgroupAvcImeSetSingleReferenceINTEL' : 5748, + 'OpSubgroupAvcImeSetDualReferenceINTEL' : 5749, + 'OpSubgroupAvcImeRefWindowSizeINTEL' : 5750, + 'OpSubgroupAvcImeAdjustRefOffsetINTEL' : 5751, + 'OpSubgroupAvcImeConvertToMcePayloadINTEL' : 5752, + 'OpSubgroupAvcImeSetMaxMotionVectorCountINTEL' : 5753, + 'OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL' : 5754, + 'OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL' : 5755, + 'OpSubgroupAvcImeSetWeightedSadINTEL' : 5756, + 'OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL' : 5757, + 'OpSubgroupAvcImeEvaluateWithDualReferenceINTEL' : 5758, + 'OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL' : 5759, + 'OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL' : 5760, + 'OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL' : 5761, + 'OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL' : 5762, + 'OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL' : 5763, + 'OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL' : 5764, + 'OpSubgroupAvcImeConvertToMceResultINTEL' : 5765, + 'OpSubgroupAvcImeGetSingleReferenceStreaminINTEL' : 5766, + 'OpSubgroupAvcImeGetDualReferenceStreaminINTEL' : 5767, + 'OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL' : 5768, + 'OpSubgroupAvcImeStripDualReferenceStreamoutINTEL' : 5769, + 'OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL' : 5770, + 'OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL' : 5771, + 'OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL' : 5772, + 'OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL' : 5773, + 'OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL' : 5774, + 'OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL' : 5775, + 'OpSubgroupAvcImeGetBorderReachedINTEL' : 5776, + 'OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL' : 5777, + 'OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL' : 5778, + 'OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL' : 5779, + 'OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL' : 5780, + 'OpSubgroupAvcFmeInitializeINTEL' : 5781, + 'OpSubgroupAvcBmeInitializeINTEL' : 5782, + 'OpSubgroupAvcRefConvertToMcePayloadINTEL' : 5783, + 'OpSubgroupAvcRefSetBidirectionalMixDisableINTEL' : 5784, + 'OpSubgroupAvcRefSetBilinearFilterEnableINTEL' : 5785, + 'OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL' : 5786, + 'OpSubgroupAvcRefEvaluateWithDualReferenceINTEL' : 5787, + 'OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL' : 5788, + 'OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL' : 5789, + 'OpSubgroupAvcRefConvertToMceResultINTEL' : 5790, + 'OpSubgroupAvcSicInitializeINTEL' : 5791, + 'OpSubgroupAvcSicConfigureSkcINTEL' : 5792, + 'OpSubgroupAvcSicConfigureIpeLumaINTEL' : 5793, + 'OpSubgroupAvcSicConfigureIpeLumaChromaINTEL' : 5794, + 'OpSubgroupAvcSicGetMotionVectorMaskINTEL' : 5795, + 'OpSubgroupAvcSicConvertToMcePayloadINTEL' : 5796, + 'OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL' : 5797, + 'OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL' : 5798, + 'OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL' : 5799, + 'OpSubgroupAvcSicSetBilinearFilterEnableINTEL' : 5800, + 'OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL' : 5801, + 'OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL' : 5802, + 'OpSubgroupAvcSicEvaluateIpeINTEL' : 5803, + 'OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL' : 5804, + 'OpSubgroupAvcSicEvaluateWithDualReferenceINTEL' : 5805, + 'OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL' : 5806, + 'OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL' : 5807, + 'OpSubgroupAvcSicConvertToMceResultINTEL' : 5808, + 'OpSubgroupAvcSicGetIpeLumaShapeINTEL' : 5809, + 'OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL' : 5810, + 'OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL' : 5811, + 'OpSubgroupAvcSicGetPackedIpeLumaModesINTEL' : 5812, + 'OpSubgroupAvcSicGetIpeChromaModeINTEL' : 5813, + 'OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL' : 5814, + 'OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL' : 5815, + 'OpSubgroupAvcSicGetInterRawSadsINTEL' : 5816, + 'OpLoopControlINTEL' : 5887, + 'OpReadPipeBlockingINTEL' : 5946, + 'OpWritePipeBlockingINTEL' : 5947, + 'OpFPGARegINTEL' : 5949, + 'OpRayQueryGetRayTMinKHR' : 6016, + 'OpRayQueryGetRayFlagsKHR' : 6017, + 'OpRayQueryGetIntersectionTKHR' : 6018, + 'OpRayQueryGetIntersectionInstanceCustomIndexKHR' : 6019, + 'OpRayQueryGetIntersectionInstanceIdKHR' : 6020, + 'OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR' : 6021, + 'OpRayQueryGetIntersectionGeometryIndexKHR' : 6022, + 'OpRayQueryGetIntersectionPrimitiveIndexKHR' : 6023, + 'OpRayQueryGetIntersectionBarycentricsKHR' : 6024, + 'OpRayQueryGetIntersectionFrontFaceKHR' : 6025, + 'OpRayQueryGetIntersectionCandidateAABBOpaqueKHR' : 6026, + 'OpRayQueryGetIntersectionObjectRayDirectionKHR' : 6027, + 'OpRayQueryGetIntersectionObjectRayOriginKHR' : 6028, + 'OpRayQueryGetWorldRayDirectionKHR' : 6029, + 'OpRayQueryGetWorldRayOriginKHR' : 6030, + 'OpRayQueryGetIntersectionObjectToWorldKHR' : 6031, + 'OpRayQueryGetIntersectionWorldToObjectKHR' : 6032, + 'OpAtomicFAddEXT' : 6035, + }, + +} + diff --git a/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d new file mode 100644 index 0000000..714e77f --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/include/spirv/unified1/spv.d @@ -0,0 +1,1624 @@ +/+ + + Copyright (c) 2014-2020 The Khronos Group Inc. + + + + Permission is hereby granted, free of charge, to any person obtaining a copy + + of this software and/or associated documentation files (the "Materials"), + + to deal in the Materials without restriction, including without limitation + + the rights to use, copy, modify, merge, publish, distribute, sublicense, + + and/or sell copies of the Materials, and to permit persons to whom the + + Materials are furnished to do so, subject to the following conditions: + + + + The above copyright notice and this permission notice shall be included in + + all copies or substantial portions of the Materials. + + + + MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS + + STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND + + HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ + + + + THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + + FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS + + IN THE MATERIALS. + +/ + +/+ + + This header is automatically generated by the same tool that creates + + the Binary Section of the SPIR-V specification. + +/ + +/+ + + Enumeration tokens for SPIR-V, in various styles: + + C, C++, C++11, JSON, Lua, Python, C#, D + + + + - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL + + - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL + + - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL + + - Lua will use tables, e.g.: spv.SourceLanguage.GLSL + + - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] + + - C# will use enum classes in the Specification class located in the "Spv" namespace, + + e.g.: Spv.Specification.SourceLanguage.GLSL + + - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL + + + + Some tokens act like mask values, which can be OR'd together, + + while others are mutually exclusive. The mask-like ones have + + "Mask" in their name, and a parallel enum that has the shift + + amount (1 << x) for each corresponding enumerant. + +/ + +module spv; + +enum uint MagicNumber = 0x07230203; +enum uint Version = 0x00010500; +enum uint Revision = 4; +enum uint OpCodeMask = 0xffff; +enum uint WordCountShift = 16; + +enum SourceLanguage : uint +{ + Unknown = 0, + ESSL = 1, + GLSL = 2, + OpenCL_C = 3, + OpenCL_CPP = 4, + HLSL = 5, +} + +enum ExecutionModel : uint +{ + Vertex = 0, + TessellationControl = 1, + TessellationEvaluation = 2, + Geometry = 3, + Fragment = 4, + GLCompute = 5, + Kernel = 6, + TaskNV = 5267, + MeshNV = 5268, + RayGenerationKHR = 5313, + RayGenerationNV = 5313, + IntersectionKHR = 5314, + IntersectionNV = 5314, + AnyHitKHR = 5315, + AnyHitNV = 5315, + ClosestHitKHR = 5316, + ClosestHitNV = 5316, + MissKHR = 5317, + MissNV = 5317, + CallableKHR = 5318, + CallableNV = 5318, +} + +enum AddressingModel : uint +{ + Logical = 0, + Physical32 = 1, + Physical64 = 2, + PhysicalStorageBuffer64 = 5348, + PhysicalStorageBuffer64EXT = 5348, +} + +enum MemoryModel : uint +{ + Simple = 0, + GLSL450 = 1, + OpenCL = 2, + Vulkan = 3, + VulkanKHR = 3, +} + +enum ExecutionMode : uint +{ + Invocations = 0, + SpacingEqual = 1, + SpacingFractionalEven = 2, + SpacingFractionalOdd = 3, + VertexOrderCw = 4, + VertexOrderCcw = 5, + PixelCenterInteger = 6, + OriginUpperLeft = 7, + OriginLowerLeft = 8, + EarlyFragmentTests = 9, + PointMode = 10, + Xfb = 11, + DepthReplacing = 12, + DepthGreater = 14, + DepthLess = 15, + DepthUnchanged = 16, + LocalSize = 17, + LocalSizeHint = 18, + InputPoints = 19, + InputLines = 20, + InputLinesAdjacency = 21, + Triangles = 22, + InputTrianglesAdjacency = 23, + Quads = 24, + Isolines = 25, + OutputVertices = 26, + OutputPoints = 27, + OutputLineStrip = 28, + OutputTriangleStrip = 29, + VecTypeHint = 30, + ContractionOff = 31, + Initializer = 33, + Finalizer = 34, + SubgroupSize = 35, + SubgroupsPerWorkgroup = 36, + SubgroupsPerWorkgroupId = 37, + LocalSizeId = 38, + LocalSizeHintId = 39, + PostDepthCoverage = 4446, + DenormPreserve = 4459, + DenormFlushToZero = 4460, + SignedZeroInfNanPreserve = 4461, + RoundingModeRTE = 4462, + RoundingModeRTZ = 4463, + StencilRefReplacingEXT = 5027, + OutputLinesNV = 5269, + OutputPrimitivesNV = 5270, + DerivativeGroupQuadsNV = 5289, + DerivativeGroupLinearNV = 5290, + OutputTrianglesNV = 5298, + PixelInterlockOrderedEXT = 5366, + PixelInterlockUnorderedEXT = 5367, + SampleInterlockOrderedEXT = 5368, + SampleInterlockUnorderedEXT = 5369, + ShadingRateInterlockOrderedEXT = 5370, + ShadingRateInterlockUnorderedEXT = 5371, + MaxWorkgroupSizeINTEL = 5893, + MaxWorkDimINTEL = 5894, + NoGlobalOffsetINTEL = 5895, + NumSIMDWorkitemsINTEL = 5896, +} + +enum StorageClass : uint +{ + UniformConstant = 0, + Input = 1, + Uniform = 2, + Output = 3, + Workgroup = 4, + CrossWorkgroup = 5, + Private = 6, + Function = 7, + Generic = 8, + PushConstant = 9, + AtomicCounter = 10, + Image = 11, + StorageBuffer = 12, + CallableDataKHR = 5328, + CallableDataNV = 5328, + IncomingCallableDataKHR = 5329, + IncomingCallableDataNV = 5329, + RayPayloadKHR = 5338, + RayPayloadNV = 5338, + HitAttributeKHR = 5339, + HitAttributeNV = 5339, + IncomingRayPayloadKHR = 5342, + IncomingRayPayloadNV = 5342, + ShaderRecordBufferKHR = 5343, + ShaderRecordBufferNV = 5343, + PhysicalStorageBuffer = 5349, + PhysicalStorageBufferEXT = 5349, + CodeSectionINTEL = 5605, +} + +enum Dim : uint +{ + _1D = 0, + _2D = 1, + _3D = 2, + Cube = 3, + Rect = 4, + Buffer = 5, + SubpassData = 6, +} + +enum SamplerAddressingMode : uint +{ + None = 0, + ClampToEdge = 1, + Clamp = 2, + Repeat = 3, + RepeatMirrored = 4, +} + +enum SamplerFilterMode : uint +{ + Nearest = 0, + Linear = 1, +} + +enum ImageFormat : uint +{ + Unknown = 0, + Rgba32f = 1, + Rgba16f = 2, + R32f = 3, + Rgba8 = 4, + Rgba8Snorm = 5, + Rg32f = 6, + Rg16f = 7, + R11fG11fB10f = 8, + R16f = 9, + Rgba16 = 10, + Rgb10A2 = 11, + Rg16 = 12, + Rg8 = 13, + R16 = 14, + R8 = 15, + Rgba16Snorm = 16, + Rg16Snorm = 17, + Rg8Snorm = 18, + R16Snorm = 19, + R8Snorm = 20, + Rgba32i = 21, + Rgba16i = 22, + Rgba8i = 23, + R32i = 24, + Rg32i = 25, + Rg16i = 26, + Rg8i = 27, + R16i = 28, + R8i = 29, + Rgba32ui = 30, + Rgba16ui = 31, + Rgba8ui = 32, + R32ui = 33, + Rgb10a2ui = 34, + Rg32ui = 35, + Rg16ui = 36, + Rg8ui = 37, + R16ui = 38, + R8ui = 39, + R64ui = 40, + R64i = 41, +} + +enum ImageChannelOrder : uint +{ + R = 0, + A = 1, + RG = 2, + RA = 3, + RGB = 4, + RGBA = 5, + BGRA = 6, + ARGB = 7, + Intensity = 8, + Luminance = 9, + Rx = 10, + RGx = 11, + RGBx = 12, + Depth = 13, + DepthStencil = 14, + sRGB = 15, + sRGBx = 16, + sRGBA = 17, + sBGRA = 18, + ABGR = 19, +} + +enum ImageChannelDataType : uint +{ + SnormInt8 = 0, + SnormInt16 = 1, + UnormInt8 = 2, + UnormInt16 = 3, + UnormShort565 = 4, + UnormShort555 = 5, + UnormInt101010 = 6, + SignedInt8 = 7, + SignedInt16 = 8, + SignedInt32 = 9, + UnsignedInt8 = 10, + UnsignedInt16 = 11, + UnsignedInt32 = 12, + HalfFloat = 13, + Float = 14, + UnormInt24 = 15, + UnormInt101010_2 = 16, +} + +enum ImageOperandsShift : uint +{ + Bias = 0, + Lod = 1, + Grad = 2, + ConstOffset = 3, + Offset = 4, + ConstOffsets = 5, + Sample = 6, + MinLod = 7, + MakeTexelAvailable = 8, + MakeTexelAvailableKHR = 8, + MakeTexelVisible = 9, + MakeTexelVisibleKHR = 9, + NonPrivateTexel = 10, + NonPrivateTexelKHR = 10, + VolatileTexel = 11, + VolatileTexelKHR = 11, + SignExtend = 12, + ZeroExtend = 13, +} + +enum ImageOperandsMask : uint +{ + MaskNone = 0, + Bias = 0x00000001, + Lod = 0x00000002, + Grad = 0x00000004, + ConstOffset = 0x00000008, + Offset = 0x00000010, + ConstOffsets = 0x00000020, + Sample = 0x00000040, + MinLod = 0x00000080, + MakeTexelAvailable = 0x00000100, + MakeTexelAvailableKHR = 0x00000100, + MakeTexelVisible = 0x00000200, + MakeTexelVisibleKHR = 0x00000200, + NonPrivateTexel = 0x00000400, + NonPrivateTexelKHR = 0x00000400, + VolatileTexel = 0x00000800, + VolatileTexelKHR = 0x00000800, + SignExtend = 0x00001000, + ZeroExtend = 0x00002000, +} + +enum FPFastMathModeShift : uint +{ + NotNaN = 0, + NotInf = 1, + NSZ = 2, + AllowRecip = 3, + Fast = 4, +} + +enum FPFastMathModeMask : uint +{ + MaskNone = 0, + NotNaN = 0x00000001, + NotInf = 0x00000002, + NSZ = 0x00000004, + AllowRecip = 0x00000008, + Fast = 0x00000010, +} + +enum FPRoundingMode : uint +{ + RTE = 0, + RTZ = 1, + RTP = 2, + RTN = 3, +} + +enum LinkageType : uint +{ + Export = 0, + Import = 1, +} + +enum AccessQualifier : uint +{ + ReadOnly = 0, + WriteOnly = 1, + ReadWrite = 2, +} + +enum FunctionParameterAttribute : uint +{ + Zext = 0, + Sext = 1, + ByVal = 2, + Sret = 3, + NoAlias = 4, + NoCapture = 5, + NoWrite = 6, + NoReadWrite = 7, +} + +enum Decoration : uint +{ + RelaxedPrecision = 0, + SpecId = 1, + Block = 2, + BufferBlock = 3, + RowMajor = 4, + ColMajor = 5, + ArrayStride = 6, + MatrixStride = 7, + GLSLShared = 8, + GLSLPacked = 9, + CPacked = 10, + BuiltIn = 11, + NoPerspective = 13, + Flat = 14, + Patch = 15, + Centroid = 16, + Sample = 17, + Invariant = 18, + Restrict = 19, + Aliased = 20, + Volatile = 21, + Constant = 22, + Coherent = 23, + NonWritable = 24, + NonReadable = 25, + Uniform = 26, + UniformId = 27, + SaturatedConversion = 28, + Stream = 29, + Location = 30, + Component = 31, + Index = 32, + Binding = 33, + DescriptorSet = 34, + Offset = 35, + XfbBuffer = 36, + XfbStride = 37, + FuncParamAttr = 38, + FPRoundingMode = 39, + FPFastMathMode = 40, + LinkageAttributes = 41, + NoContraction = 42, + InputAttachmentIndex = 43, + Alignment = 44, + MaxByteOffset = 45, + AlignmentId = 46, + MaxByteOffsetId = 47, + NoSignedWrap = 4469, + NoUnsignedWrap = 4470, + ExplicitInterpAMD = 4999, + OverrideCoverageNV = 5248, + PassthroughNV = 5250, + ViewportRelativeNV = 5252, + SecondaryViewportRelativeNV = 5256, + PerPrimitiveNV = 5271, + PerViewNV = 5272, + PerTaskNV = 5273, + PerVertexNV = 5285, + NonUniform = 5300, + NonUniformEXT = 5300, + RestrictPointer = 5355, + RestrictPointerEXT = 5355, + AliasedPointer = 5356, + AliasedPointerEXT = 5356, + ReferencedIndirectlyINTEL = 5602, + CounterBuffer = 5634, + HlslCounterBufferGOOGLE = 5634, + HlslSemanticGOOGLE = 5635, + UserSemantic = 5635, + UserTypeGOOGLE = 5636, + RegisterINTEL = 5825, + MemoryINTEL = 5826, + NumbanksINTEL = 5827, + BankwidthINTEL = 5828, + MaxPrivateCopiesINTEL = 5829, + SinglepumpINTEL = 5830, + DoublepumpINTEL = 5831, + MaxReplicatesINTEL = 5832, + SimpleDualPortINTEL = 5833, + MergeINTEL = 5834, + BankBitsINTEL = 5835, + ForcePow2DepthINTEL = 5836, +} + +enum BuiltIn : uint +{ + Position = 0, + PointSize = 1, + ClipDistance = 3, + CullDistance = 4, + VertexId = 5, + InstanceId = 6, + PrimitiveId = 7, + InvocationId = 8, + Layer = 9, + ViewportIndex = 10, + TessLevelOuter = 11, + TessLevelInner = 12, + TessCoord = 13, + PatchVertices = 14, + FragCoord = 15, + PointCoord = 16, + FrontFacing = 17, + SampleId = 18, + SamplePosition = 19, + SampleMask = 20, + FragDepth = 22, + HelperInvocation = 23, + NumWorkgroups = 24, + WorkgroupSize = 25, + WorkgroupId = 26, + LocalInvocationId = 27, + GlobalInvocationId = 28, + LocalInvocationIndex = 29, + WorkDim = 30, + GlobalSize = 31, + EnqueuedWorkgroupSize = 32, + GlobalOffset = 33, + GlobalLinearId = 34, + SubgroupSize = 36, + SubgroupMaxSize = 37, + NumSubgroups = 38, + NumEnqueuedSubgroups = 39, + SubgroupId = 40, + SubgroupLocalInvocationId = 41, + VertexIndex = 42, + InstanceIndex = 43, + SubgroupEqMask = 4416, + SubgroupEqMaskKHR = 4416, + SubgroupGeMask = 4417, + SubgroupGeMaskKHR = 4417, + SubgroupGtMask = 4418, + SubgroupGtMaskKHR = 4418, + SubgroupLeMask = 4419, + SubgroupLeMaskKHR = 4419, + SubgroupLtMask = 4420, + SubgroupLtMaskKHR = 4420, + BaseVertex = 4424, + BaseInstance = 4425, + DrawIndex = 4426, + PrimitiveShadingRateKHR = 4432, + DeviceIndex = 4438, + ViewIndex = 4440, + ShadingRateKHR = 4444, + BaryCoordNoPerspAMD = 4992, + BaryCoordNoPerspCentroidAMD = 4993, + BaryCoordNoPerspSampleAMD = 4994, + BaryCoordSmoothAMD = 4995, + BaryCoordSmoothCentroidAMD = 4996, + BaryCoordSmoothSampleAMD = 4997, + BaryCoordPullModelAMD = 4998, + FragStencilRefEXT = 5014, + ViewportMaskNV = 5253, + SecondaryPositionNV = 5257, + SecondaryViewportMaskNV = 5258, + PositionPerViewNV = 5261, + ViewportMaskPerViewNV = 5262, + FullyCoveredEXT = 5264, + TaskCountNV = 5274, + PrimitiveCountNV = 5275, + PrimitiveIndicesNV = 5276, + ClipDistancePerViewNV = 5277, + CullDistancePerViewNV = 5278, + LayerPerViewNV = 5279, + MeshViewCountNV = 5280, + MeshViewIndicesNV = 5281, + BaryCoordNV = 5286, + BaryCoordNoPerspNV = 5287, + FragSizeEXT = 5292, + FragmentSizeNV = 5292, + FragInvocationCountEXT = 5293, + InvocationsPerPixelNV = 5293, + LaunchIdKHR = 5319, + LaunchIdNV = 5319, + LaunchSizeKHR = 5320, + LaunchSizeNV = 5320, + WorldRayOriginKHR = 5321, + WorldRayOriginNV = 5321, + WorldRayDirectionKHR = 5322, + WorldRayDirectionNV = 5322, + ObjectRayOriginKHR = 5323, + ObjectRayOriginNV = 5323, + ObjectRayDirectionKHR = 5324, + ObjectRayDirectionNV = 5324, + RayTminKHR = 5325, + RayTminNV = 5325, + RayTmaxKHR = 5326, + RayTmaxNV = 5326, + InstanceCustomIndexKHR = 5327, + InstanceCustomIndexNV = 5327, + ObjectToWorldKHR = 5330, + ObjectToWorldNV = 5330, + WorldToObjectKHR = 5331, + WorldToObjectNV = 5331, + HitTKHR = 5332, + HitTNV = 5332, + HitKindKHR = 5333, + HitKindNV = 5333, + IncomingRayFlagsKHR = 5351, + IncomingRayFlagsNV = 5351, + RayGeometryIndexKHR = 5352, + WarpsPerSMNV = 5374, + SMCountNV = 5375, + WarpIDNV = 5376, + SMIDNV = 5377, +} + +enum SelectionControlShift : uint +{ + Flatten = 0, + DontFlatten = 1, +} + +enum SelectionControlMask : uint +{ + MaskNone = 0, + Flatten = 0x00000001, + DontFlatten = 0x00000002, +} + +enum LoopControlShift : uint +{ + Unroll = 0, + DontUnroll = 1, + DependencyInfinite = 2, + DependencyLength = 3, + MinIterations = 4, + MaxIterations = 5, + IterationMultiple = 6, + PeelCount = 7, + PartialCount = 8, + InitiationIntervalINTEL = 16, + MaxConcurrencyINTEL = 17, + DependencyArrayINTEL = 18, + PipelineEnableINTEL = 19, + LoopCoalesceINTEL = 20, + MaxInterleavingINTEL = 21, + SpeculatedIterationsINTEL = 22, +} + +enum LoopControlMask : uint +{ + MaskNone = 0, + Unroll = 0x00000001, + DontUnroll = 0x00000002, + DependencyInfinite = 0x00000004, + DependencyLength = 0x00000008, + MinIterations = 0x00000010, + MaxIterations = 0x00000020, + IterationMultiple = 0x00000040, + PeelCount = 0x00000080, + PartialCount = 0x00000100, + InitiationIntervalINTEL = 0x00010000, + MaxConcurrencyINTEL = 0x00020000, + DependencyArrayINTEL = 0x00040000, + PipelineEnableINTEL = 0x00080000, + LoopCoalesceINTEL = 0x00100000, + MaxInterleavingINTEL = 0x00200000, + SpeculatedIterationsINTEL = 0x00400000, +} + +enum FunctionControlShift : uint +{ + Inline = 0, + DontInline = 1, + Pure = 2, + Const = 3, +} + +enum FunctionControlMask : uint +{ + MaskNone = 0, + Inline = 0x00000001, + DontInline = 0x00000002, + Pure = 0x00000004, + Const = 0x00000008, +} + +enum MemorySemanticsShift : uint +{ + Acquire = 1, + Release = 2, + AcquireRelease = 3, + SequentiallyConsistent = 4, + UniformMemory = 6, + SubgroupMemory = 7, + WorkgroupMemory = 8, + CrossWorkgroupMemory = 9, + AtomicCounterMemory = 10, + ImageMemory = 11, + OutputMemory = 12, + OutputMemoryKHR = 12, + MakeAvailable = 13, + MakeAvailableKHR = 13, + MakeVisible = 14, + MakeVisibleKHR = 14, + Volatile = 15, +} + +enum MemorySemanticsMask : uint +{ + MaskNone = 0, + Acquire = 0x00000002, + Release = 0x00000004, + AcquireRelease = 0x00000008, + SequentiallyConsistent = 0x00000010, + UniformMemory = 0x00000040, + SubgroupMemory = 0x00000080, + WorkgroupMemory = 0x00000100, + CrossWorkgroupMemory = 0x00000200, + AtomicCounterMemory = 0x00000400, + ImageMemory = 0x00000800, + OutputMemory = 0x00001000, + OutputMemoryKHR = 0x00001000, + MakeAvailable = 0x00002000, + MakeAvailableKHR = 0x00002000, + MakeVisible = 0x00004000, + MakeVisibleKHR = 0x00004000, + Volatile = 0x00008000, +} + +enum MemoryAccessShift : uint +{ + Volatile = 0, + Aligned = 1, + Nontemporal = 2, + MakePointerAvailable = 3, + MakePointerAvailableKHR = 3, + MakePointerVisible = 4, + MakePointerVisibleKHR = 4, + NonPrivatePointer = 5, + NonPrivatePointerKHR = 5, +} + +enum MemoryAccessMask : uint +{ + MaskNone = 0, + Volatile = 0x00000001, + Aligned = 0x00000002, + Nontemporal = 0x00000004, + MakePointerAvailable = 0x00000008, + MakePointerAvailableKHR = 0x00000008, + MakePointerVisible = 0x00000010, + MakePointerVisibleKHR = 0x00000010, + NonPrivatePointer = 0x00000020, + NonPrivatePointerKHR = 0x00000020, +} + +enum Scope : uint +{ + CrossDevice = 0, + Device = 1, + Workgroup = 2, + Subgroup = 3, + Invocation = 4, + QueueFamily = 5, + QueueFamilyKHR = 5, + ShaderCallKHR = 6, +} + +enum GroupOperation : uint +{ + Reduce = 0, + InclusiveScan = 1, + ExclusiveScan = 2, + ClusteredReduce = 3, + PartitionedReduceNV = 6, + PartitionedInclusiveScanNV = 7, + PartitionedExclusiveScanNV = 8, +} + +enum KernelEnqueueFlags : uint +{ + NoWait = 0, + WaitKernel = 1, + WaitWorkGroup = 2, +} + +enum KernelProfilingInfoShift : uint +{ + CmdExecTime = 0, +} + +enum KernelProfilingInfoMask : uint +{ + MaskNone = 0, + CmdExecTime = 0x00000001, +} + +enum Capability : uint +{ + Matrix = 0, + Shader = 1, + Geometry = 2, + Tessellation = 3, + Addresses = 4, + Linkage = 5, + Kernel = 6, + Vector16 = 7, + Float16Buffer = 8, + Float16 = 9, + Float64 = 10, + Int64 = 11, + Int64Atomics = 12, + ImageBasic = 13, + ImageReadWrite = 14, + ImageMipmap = 15, + Pipes = 17, + Groups = 18, + DeviceEnqueue = 19, + LiteralSampler = 20, + AtomicStorage = 21, + Int16 = 22, + TessellationPointSize = 23, + GeometryPointSize = 24, + ImageGatherExtended = 25, + StorageImageMultisample = 27, + UniformBufferArrayDynamicIndexing = 28, + SampledImageArrayDynamicIndexing = 29, + StorageBufferArrayDynamicIndexing = 30, + StorageImageArrayDynamicIndexing = 31, + ClipDistance = 32, + CullDistance = 33, + ImageCubeArray = 34, + SampleRateShading = 35, + ImageRect = 36, + SampledRect = 37, + GenericPointer = 38, + Int8 = 39, + InputAttachment = 40, + SparseResidency = 41, + MinLod = 42, + Sampled1D = 43, + Image1D = 44, + SampledCubeArray = 45, + SampledBuffer = 46, + ImageBuffer = 47, + ImageMSArray = 48, + StorageImageExtendedFormats = 49, + ImageQuery = 50, + DerivativeControl = 51, + InterpolationFunction = 52, + TransformFeedback = 53, + GeometryStreams = 54, + StorageImageReadWithoutFormat = 55, + StorageImageWriteWithoutFormat = 56, + MultiViewport = 57, + SubgroupDispatch = 58, + NamedBarrier = 59, + PipeStorage = 60, + GroupNonUniform = 61, + GroupNonUniformVote = 62, + GroupNonUniformArithmetic = 63, + GroupNonUniformBallot = 64, + GroupNonUniformShuffle = 65, + GroupNonUniformShuffleRelative = 66, + GroupNonUniformClustered = 67, + GroupNonUniformQuad = 68, + ShaderLayer = 69, + ShaderViewportIndex = 70, + FragmentShadingRateKHR = 4422, + SubgroupBallotKHR = 4423, + DrawParameters = 4427, + SubgroupVoteKHR = 4431, + StorageBuffer16BitAccess = 4433, + StorageUniformBufferBlock16 = 4433, + StorageUniform16 = 4434, + UniformAndStorageBuffer16BitAccess = 4434, + StoragePushConstant16 = 4435, + StorageInputOutput16 = 4436, + DeviceGroup = 4437, + MultiView = 4439, + VariablePointersStorageBuffer = 4441, + VariablePointers = 4442, + AtomicStorageOps = 4445, + SampleMaskPostDepthCoverage = 4447, + StorageBuffer8BitAccess = 4448, + UniformAndStorageBuffer8BitAccess = 4449, + StoragePushConstant8 = 4450, + DenormPreserve = 4464, + DenormFlushToZero = 4465, + SignedZeroInfNanPreserve = 4466, + RoundingModeRTE = 4467, + RoundingModeRTZ = 4468, + RayQueryProvisionalKHR = 4471, + RayTraversalPrimitiveCullingProvisionalKHR = 4478, + Float16ImageAMD = 5008, + ImageGatherBiasLodAMD = 5009, + FragmentMaskAMD = 5010, + StencilExportEXT = 5013, + ImageReadWriteLodAMD = 5015, + Int64ImageEXT = 5016, + ShaderClockKHR = 5055, + SampleMaskOverrideCoverageNV = 5249, + GeometryShaderPassthroughNV = 5251, + ShaderViewportIndexLayerEXT = 5254, + ShaderViewportIndexLayerNV = 5254, + ShaderViewportMaskNV = 5255, + ShaderStereoViewNV = 5259, + PerViewAttributesNV = 5260, + FragmentFullyCoveredEXT = 5265, + MeshShadingNV = 5266, + ImageFootprintNV = 5282, + FragmentBarycentricNV = 5284, + ComputeDerivativeGroupQuadsNV = 5288, + FragmentDensityEXT = 5291, + ShadingRateNV = 5291, + GroupNonUniformPartitionedNV = 5297, + ShaderNonUniform = 5301, + ShaderNonUniformEXT = 5301, + RuntimeDescriptorArray = 5302, + RuntimeDescriptorArrayEXT = 5302, + InputAttachmentArrayDynamicIndexing = 5303, + InputAttachmentArrayDynamicIndexingEXT = 5303, + UniformTexelBufferArrayDynamicIndexing = 5304, + UniformTexelBufferArrayDynamicIndexingEXT = 5304, + StorageTexelBufferArrayDynamicIndexing = 5305, + StorageTexelBufferArrayDynamicIndexingEXT = 5305, + UniformBufferArrayNonUniformIndexing = 5306, + UniformBufferArrayNonUniformIndexingEXT = 5306, + SampledImageArrayNonUniformIndexing = 5307, + SampledImageArrayNonUniformIndexingEXT = 5307, + StorageBufferArrayNonUniformIndexing = 5308, + StorageBufferArrayNonUniformIndexingEXT = 5308, + StorageImageArrayNonUniformIndexing = 5309, + StorageImageArrayNonUniformIndexingEXT = 5309, + InputAttachmentArrayNonUniformIndexing = 5310, + InputAttachmentArrayNonUniformIndexingEXT = 5310, + UniformTexelBufferArrayNonUniformIndexing = 5311, + UniformTexelBufferArrayNonUniformIndexingEXT = 5311, + StorageTexelBufferArrayNonUniformIndexing = 5312, + StorageTexelBufferArrayNonUniformIndexingEXT = 5312, + RayTracingNV = 5340, + VulkanMemoryModel = 5345, + VulkanMemoryModelKHR = 5345, + VulkanMemoryModelDeviceScope = 5346, + VulkanMemoryModelDeviceScopeKHR = 5346, + PhysicalStorageBufferAddresses = 5347, + PhysicalStorageBufferAddressesEXT = 5347, + ComputeDerivativeGroupLinearNV = 5350, + RayTracingProvisionalKHR = 5353, + CooperativeMatrixNV = 5357, + FragmentShaderSampleInterlockEXT = 5363, + FragmentShaderShadingRateInterlockEXT = 5372, + ShaderSMBuiltinsNV = 5373, + FragmentShaderPixelInterlockEXT = 5378, + DemoteToHelperInvocationEXT = 5379, + SubgroupShuffleINTEL = 5568, + SubgroupBufferBlockIOINTEL = 5569, + SubgroupImageBlockIOINTEL = 5570, + SubgroupImageMediaBlockIOINTEL = 5579, + IntegerFunctions2INTEL = 5584, + FunctionPointersINTEL = 5603, + IndirectReferencesINTEL = 5604, + SubgroupAvcMotionEstimationINTEL = 5696, + SubgroupAvcMotionEstimationIntraINTEL = 5697, + SubgroupAvcMotionEstimationChromaINTEL = 5698, + FPGAMemoryAttributesINTEL = 5824, + UnstructuredLoopControlsINTEL = 5886, + FPGALoopControlsINTEL = 5888, + KernelAttributesINTEL = 5892, + FPGAKernelAttributesINTEL = 5897, + BlockingPipesINTEL = 5945, + FPGARegINTEL = 5948, + AtomicFloat32AddEXT = 6033, + AtomicFloat64AddEXT = 6034, +} + +enum RayFlagsShift : uint +{ + OpaqueKHR = 0, + NoOpaqueKHR = 1, + TerminateOnFirstHitKHR = 2, + SkipClosestHitShaderKHR = 3, + CullBackFacingTrianglesKHR = 4, + CullFrontFacingTrianglesKHR = 5, + CullOpaqueKHR = 6, + CullNoOpaqueKHR = 7, + SkipTrianglesKHR = 8, + SkipAABBsKHR = 9, +} + +enum RayFlagsMask : uint +{ + MaskNone = 0, + OpaqueKHR = 0x00000001, + NoOpaqueKHR = 0x00000002, + TerminateOnFirstHitKHR = 0x00000004, + SkipClosestHitShaderKHR = 0x00000008, + CullBackFacingTrianglesKHR = 0x00000010, + CullFrontFacingTrianglesKHR = 0x00000020, + CullOpaqueKHR = 0x00000040, + CullNoOpaqueKHR = 0x00000080, + SkipTrianglesKHR = 0x00000100, + SkipAABBsKHR = 0x00000200, +} + +enum RayQueryIntersection : uint +{ + RayQueryCandidateIntersectionKHR = 0, + RayQueryCommittedIntersectionKHR = 1, +} + +enum RayQueryCommittedIntersectionType : uint +{ + RayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionGeneratedKHR = 2, +} + +enum RayQueryCandidateIntersectionType : uint +{ + RayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionAABBKHR = 1, +} + +enum FragmentShadingRateShift : uint +{ + Vertical2Pixels = 0, + Vertical4Pixels = 1, + Horizontal2Pixels = 2, + Horizontal4Pixels = 3, +} + +enum FragmentShadingRateMask : uint +{ + MaskNone = 0, + Vertical2Pixels = 0x00000001, + Vertical4Pixels = 0x00000002, + Horizontal2Pixels = 0x00000004, + Horizontal4Pixels = 0x00000008, +} + +enum Op : uint +{ + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpTerminateInvocation = 4416, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpSubgroupReadInvocationKHR = 4432, + OpTypeRayQueryProvisionalKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpImageSampleFootprintNV = 5283, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionKHR = 5335, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayKHR = 5336, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceRayKHR = 5337, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableKHR = 5344, + OpExecuteCallableNV = 5344, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpLoopControlINTEL = 5887, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, +} + + diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/CMakeLists.txt b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/CMakeLists.txt new file mode 100644 index 0000000..c624151 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 2.8) + +set(CMAKE_INSTALL_PREFIX "install" CACHE STRING "prefix" FORCE) + +project(buildSpvHeaders) + +set(SOURCES + main.cpp + jsonToSpirv.cpp + header.cpp + jsoncpp/dist/jsoncpp.cpp) + +set(HEADERS + jsonToSpirv.h + header.h + jsoncpp/dist/json/json.h) + +if(CMAKE_COMPILER_IS_GNUCXX) + add_definitions(-std=c++11) +elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_definitions(-std=c++11) +endif() + +add_executable(buildSpvHeaders ${SOURCES} ${HEADERS}) + +install(TARGETS buildSpvHeaders RUNTIME DESTINATION bin) diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/generate_language_headers.py b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/generate_language_headers.py new file mode 100755 index 0000000..c56780c --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/generate_language_headers.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-2020 Google LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and/or associated documentation files (the +# "Materials"), to deal in the Materials without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Materials, and to +# permit persons to whom the Materials are furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Materials. +# +# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +# KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +# SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +# https://www.khronos.org/registry/ +# +# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + +"""Generates a C language headers from a SPIR-V JSON grammar file""" + +import errno +import json +import os.path +import re + +DEFAULT_COPYRIGHT="""Copyright (c) 2020 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT + https://www.khronos.org/registry/ + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +""".split('\n') + +def make_path_to_file(f): + """Makes all ancestor directories to the given file, if they + don't yet exist. + + Arguments: + f: The file whose ancestor directories are to be created. + """ + dir = os.path.dirname(os.path.abspath(f)) + try: + os.makedirs(dir) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(dir): + pass + else: + raise + +class ExtInstGrammar: + """The grammar for an extended instruction set""" + + def __init__(self, name, copyright, instructions, operand_kinds, version = None, revision = None): + self.name = name + self.copyright = copyright + self.instructions = instructions + self.operand_kinds = operand_kinds + self.version = version + self.revision = revision + + +class LangGenerator: + """A language-specific generator""" + + def __init__(self): + self.upper_case_initial = re.compile('^[A-Z]') + pass + + def comment_prefix(self): + return "" + + def namespace_prefix(self): + return "" + + def uses_guards(self): + return False + + def cpp_guard_preamble(self): + return "" + + def cpp_guard_postamble(self): + return "" + + def enum_value(self, prefix, name, value): + if self.upper_case_initial.match(name): + use_name = name + else: + use_name = '_' + name + + return " {}{} = {},".format(prefix, use_name, value) + + def generate(self, grammar): + """Returns a string that is the language-specific header for the given grammar""" + + parts = [] + if grammar.copyright: + parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright]) + parts.append('') + + guard = 'SPIRV_UNIFIED1_{}_H_'.format(grammar.name) + if self.uses_guards: + parts.append('#ifndef {}'.format(guard)) + parts.append('#define {}'.format(guard)) + parts.append('') + + parts.append(self.cpp_guard_preamble()) + + if grammar.version: + parts.append(self.const_definition(grammar.name, 'Version', grammar.version)) + + if grammar.revision is not None: + parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision)) + + parts.append('') + + if grammar.instructions: + parts.append(self.enum_prefix(grammar.name, 'Instructions')) + for inst in grammar.instructions: + parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode'])) + parts.append(self.enum_end(grammar.name, 'Instructions')) + parts.append('') + + if grammar.operand_kinds: + for kind in grammar.operand_kinds: + parts.append(self.enum_prefix(grammar.name, kind['kind'])) + for e in kind['enumerants']: + parts.append(self.enum_value(grammar.name, e['enumerant'], e['value'])) + parts.append(self.enum_end(grammar.name, kind['kind'])) + parts.append('') + + parts.append(self.cpp_guard_postamble()) + + if self.uses_guards: + parts.append('#endif // {}'.format(guard)) + + # Ensre the file ends in an end of line + parts.append('') + + return '\n'.join(parts) + + +class CLikeGenerator(LangGenerator): + def uses_guards(self): + return True + + def comment_prefix(self): + return "// " + + def const_definition(self, prefix, var, value): + # Use an anonymous enum. Don't use a static const int variable because + # that can bloat binary size. + return 'enum {0}{1}{2}{3} = {4},{1}{2}{3}_BitWidthPadding = 0x7fffffff{5};'.format( + '{', '\n ', prefix, var, value, '\n}') + + def enum_prefix(self, prefix, name): + return 'enum {}{} {}'.format(prefix, name, '{') + + def enum_end(self, prefix, enum): + return ' {}{}Max = 0x7fffffff\n{};\n'.format(prefix, enum, '}') + + def cpp_guard_preamble(self): + return '#ifdef __cplusplus\nextern "C" {\n#endif\n' + + def cpp_guard_postamble(self): + return '#ifdef __cplusplus\n}\n#endif\n' + + +class CGenerator(CLikeGenerator): + pass + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar') + + parser.add_argument('--extinst-name', + type=str, required=True, + help='The name to use in tokens') + parser.add_argument('--extinst-grammar', metavar='', + type=str, required=True, + help='input JSON grammar file for extended instruction set') + parser.add_argument('--extinst-output-base', metavar='', + type=str, required=True, + help='Basename of the language-specific output file.') + args = parser.parse_args() + + with open(args.extinst_grammar) as json_file: + grammar_json = json.loads(json_file.read()) + if 'copyright' in grammar_json: + copyright = grammar_json['copyright'] + else: + copyright = DEFAULT_COPYRIGHT + if 'version' in grammar_json: + version = grammar_json['version'] + else: + version = 0 + if 'operand_kinds' in grammar_json: + operand_kinds = grammar_json['operand_kinds'] + else: + operand_kinds = [] + + grammar = ExtInstGrammar(name = args.extinst_name, + copyright = copyright, + instructions = grammar_json['instructions'], + operand_kinds = operand_kinds, + version = version, + revision = grammar_json['revision']) + make_path_to_file(args.extinst_output_base) + with open(args.extinst_output_base + '.h', 'w') as f: + f.write(CGenerator().generate(grammar)) + + +if __name__ == '__main__': + main() diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeExtinstHeaders.py b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeExtinstHeaders.py new file mode 100755 index 0000000..9359747 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeExtinstHeaders.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +"""Generate C headers for certain extended instruction sets""" + +import subprocess +import os + +# Assume we are running from the tools/buildHeaders directory +os.chdir('../../include/spirv/unified1') + +def mk_extinst(name, grammar_file): + """Generate one C header from a grammar""" + script = '../../../tools/buildHeaders/bin/generate_language_headers.py' + subprocess.check_call(['python3', + script, + '--extinst-name=' + name, + '--extinst-grammar=' + grammar_file, + '--extinst-output-base=' + name]) + subprocess.check_call(['dos2unix', name + '.h']) + + +mk_extinst('DebugInfo', 'extinst.debuginfo.grammar.json') +mk_extinst('OpenCLDebugInfo100', 'extinst.opencl.debuginfo.100.grammar.json') +mk_extinst('AMD_gcn_shader', 'extinst.spv-amd-gcn-shader.grammar.json') +mk_extinst('AMD_shader_ballot', 'extinst.spv-amd-shader-ballot.grammar.json') +mk_extinst('AMD_shader_explicit_vertex_parameter', 'extinst.spv-amd-shader-explicit-vertex-parameter.grammar.json') +mk_extinst('AMD_shader_trinary_minmax', 'extinst.spv-amd-shader-trinary-minmax.grammar.json') +mk_extinst('NonSemanticDebugPrintf', 'extinst.nonsemantic.debugprintf.grammar.json') +mk_extinst('NonSemanticClspvReflection', 'extinst.nonsemantic.clspvreflection.grammar.json') diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeHeaders b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeHeaders new file mode 100755 index 0000000..0ca0b2f --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/bin/makeHeaders @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +python3 bin/makeExtinstHeaders.py + +cd ../../include/spirv/unified1 +../../../tools/buildHeaders/build/install/bin/buildSpvHeaders -H spirv.core.grammar.json +dos2unix spirv.* SpirV.* spv.* diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/header.cpp b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/header.cpp new file mode 100644 index 0000000..926905e --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/header.cpp @@ -0,0 +1,835 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// +// Print headers for SPIR-V in several languages. +// +// To change the header information, change the C++-built database in doc.*. +// +// Then, use "spriv -h " - e.g, spriv.{h,hpp,lua,py,etc}: +// replace the auto-generated header, or "spirv -H" to generate all +// supported language headers to predefined names in the current directory. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jsoncpp/dist/json/json.h" + +#include "header.h" +#include "jsonToSpirv.h" + +// snprintf and _snprintf are not quite the same, but close enough +// for our use. +#ifdef _MSC_VER +#pragma warning(disable:4996) +#define snprintf _snprintf +#endif + +// This file converts SPIR-V definitions to an internal JSON +// representation, and then generates language specific +// data from that single internal form. + +// Initially, the internal form is created from C++ data, +// though this can be changed to a JSON master in time. + +namespace { + class TPrinter { + protected: + TPrinter(); + + static const int DocMagicNumber = 0x07230203; + static const int DocVersion = 0x00010500; + static const int DocRevision = 4; + #define DocRevisionString "4" + static const std::string DocCopyright; + static const std::string DocComment1; + static const std::string DocComment2; + + enum enumStyle_t { + enumNoMask, + enumCount, + enumShift, + enumMask, + enumHex, + }; + + static std::string styleStr(enumStyle_t s) { + return s == enumShift ? "Shift" : + s == enumMask ? "Mask" : ""; + } + + friend std::ostream& operator<<(std::ostream&, const TPrinter&); + + virtual void printAll(std::ostream&) const; + virtual void printComments(std::ostream&) const; + virtual void printPrologue(std::ostream&) const { } + virtual void printDefs(std::ostream&) const; + virtual void printEpilogue(std::ostream&) const { } + virtual void printMeta(std::ostream&) const; + virtual void printTypes(std::ostream&) const { } + virtual void printHasResultType(std::ostream&) const { }; + + virtual std::string escapeComment(const std::string& s) const; + + // Default printComments() uses these comment strings + virtual std::string commentBeg() const { return ""; } + virtual std::string commentEnd(bool isLast) const { return ""; } + virtual std::string commentBOL() const { return ""; } + virtual std::string commentEOL(bool isLast) const { return ""; } + + typedef std::pair valpair_t; + + // for printing enum values + virtual std::string enumBeg(const std::string&, enumStyle_t) const { return ""; } + virtual std::string enumEnd(const std::string&, enumStyle_t, bool isLast = false) const { + return ""; + } + virtual std::string enumFmt(const std::string&, const valpair_t&, + enumStyle_t, bool isLast = false) const { + return ""; + } + virtual std::string maxEnumFmt(const std::string&, const valpair_t&, + enumStyle_t) const { + return ""; + } + + virtual std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast = false) const { + return ""; + } + + std::vector getSortedVals(const Json::Value&) const; + + virtual std::string indent(int count = 1) const { + return std::string(count * 4, ' '); // default indent level = 4 + } + + static std::string fmtNum(const char* fmt, unsigned val) { + char buff[16]; // ample for 8 hex digits + 0x + snprintf(buff, sizeof(buff), fmt, val); + buff[sizeof(buff)-1] = '\0'; // MSVC doesn't promise null termination + return buff; + } + + static std::string fmtStyleVal(unsigned v, enumStyle_t style); + + // If the enum value name would start with a sigit, prepend the enum name. + // E.g, "3D" -> "Dim3D". + static std::string prependIfDigit(const std::string& ename, const std::string& vname) { + return (std::isdigit(vname[0]) ? ename : std::string("")) + vname; + } + + void addComment(Json::Value& node, const std::string& str); + + Json::Value spvRoot; // JSON SPIR-V data + }; + + // Format value as mask or value + std::string TPrinter::fmtStyleVal(unsigned v, enumStyle_t style) + { + switch (style) { + case enumMask: + return fmtNum("0x%08x", 1< + TPrinter::getSortedVals(const Json::Value& p) const + { + std::vector values; + + for (auto e = p.begin(); e != p.end(); ++e) + values.push_back(valpair_t(e->asUInt(), e.name())); + + // Use a stable sort because we might have aliases, e.g. + // SubgropuBallot (might be in future core) vs. SubgroupBallotKHR. + std::stable_sort(values.begin(), values.end()); + + return values; + } + + // Escape comment characters if needed + std::string TPrinter::escapeComment(const std::string& s) const { return s; } + + // Format comments in language specific way + void TPrinter::printComments(std::ostream& out) const + { + const int commentCount = spvRoot["spv"]["meta"]["Comment"].size(); + int commentNum = 0; + + for (const auto& comment : spvRoot["spv"]["meta"]["Comment"]) { + out << commentBeg(); + + for (int line = 0; line < int(comment.size()); ++line) + out << commentBOL() << escapeComment(comment[line].asString()) << + commentEOL((line+1) == comment.size()) << std::endl; + + out << commentEnd(++commentNum == commentCount) << std::endl; + } + } + + // Format header metadata + void TPrinter::printMeta(std::ostream& out) const + { + const Json::Value& meta = spvRoot["spv"]["meta"]; + + const auto print = [&](const char* name, const char* fmt, bool isLast) { + out << fmtConstInt(meta[name].asUInt(), name, fmt, isLast); + }; + + print("MagicNumber", "0x%08lx", false); + print("Version", "0x%08lx", false); + print("Revision", "%d", false); + print("OpCodeMask", "0x%04x", false); + print("WordCountShift", "%d", true); + } + + // Format value definitions in language specific way + void TPrinter::printDefs(std::ostream& out) const + { + const Json::Value& enums = spvRoot["spv"]["enum"]; + + for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { + const bool isMask = (*opClass)["Type"].asString() == "Bit"; + const auto opName = (*opClass)["Name"].asString(); + const auto opPrefix = opName == "Op" ? "" : opName; + + for (enumStyle_t style = (isMask ? enumShift : enumCount); + style <= (isMask ? enumMask : enumCount); style = enumStyle_t(int(style)+1)) { + + out << enumBeg(opName, style); + + if (style == enumMask) + out << enumFmt(opPrefix, valpair_t(0, "MaskNone"), enumNoMask); + + const auto sorted = getSortedVals((*opClass)["Values"]); + + std::string maxEnum = maxEnumFmt(opName, valpair_t(0x7FFFFFFF, "Max"), enumHex); + + bool printMax = (style != enumMask && maxEnum.size() > 0); + + for (const auto& v : sorted) + out << enumFmt(opPrefix, v, style, !printMax && v.second == sorted.back().second); + + if (printMax) + out << maxEnum; + + auto nextOpClass = opClass; + out << enumEnd(opName, style, ++nextOpClass == enums.end()); + } + } + } + + void TPrinter::printAll(std::ostream& out) const + { + printComments(out); + printPrologue(out); + printTypes(out); + printMeta(out); + printDefs(out); + printHasResultType(out); + printEpilogue(out); + } + + // Stream entire header to output + std::ostream& operator<<(std::ostream& out, const TPrinter &p) + { + p.printAll(out); + return out; + } + + // JSON printer. Rather than use the default printer, we supply our own so + // we can control the printing order within various containers. + class TPrinterJSON final : public TPrinter { + private: + void printPrologue(std::ostream& out) const override { out << "{\n" + indent() + "\"spv\":\n" + indent() + "{\n"; } + void printEpilogue(std::ostream& out) const override { out << indent() + "}\n}\n"; } + + std::string escapeComment(const std::string& s) const override { + std::string newStr; + for (auto c : s) { + if (c == '"') { + newStr += '\\'; + newStr += c; + } else { + newStr += c; + } + } + return newStr; + } + + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override { + return indent(3) + '"' + name + "\": " + fmtNum("%d", val) + (isLast ? "\n" : ",\n"); + } + + void printMeta(std::ostream& out) const override + { + out << indent(2) + "\"meta\":\n" + indent(2) + "{\n"; + printComments(out); + TPrinter::printMeta(out); + out << indent(2) + "},\n"; + } + + std::string commentBeg() const override { return indent(4) + "[\n"; } + std::string commentEnd(bool isLast) const override { return indent(4) + (isLast ? "]" : "],"); } + std::string commentBOL() const override { return indent(5) + '"'; } + std::string commentEOL(bool isLast) const override { return (isLast ? "\"" : "\","); } + + void printComments(std::ostream& out) const override + { + out << indent(3) + "\"Comment\":\n" + indent(3) + "[\n"; + TPrinter::printComments(out); + out << indent(3) + "],\n"; + } + + void printDefs(std::ostream& out) const override + { + out << indent(2) + "\"enum\":\n" + indent(2) + "[\n"; + TPrinter::printDefs(out); + out << indent(2) + "]\n"; + } + + void printAll(std::ostream& out) const override + { + printPrologue(out); + printMeta(out); + printDefs(out); + printEpilogue(out); + } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + if (style == enumMask) + return ""; + return indent(3) + "{\n" + + indent(4) + "\"Name\": \"" + s + "\",\n" + + indent(4) + "\"Type\": " + (style == enumShift ? "\"Bit\"" : "\"Value\"") + ",\n" + + indent(4) + "\"Values\":\n" + + indent(4) + "{\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + if (style == enumMask) + return ""; + return indent(4) + "}\n" + + indent(3) + "}" + (isLast ? "" : ",") + "\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + if (style == enumMask || style == enumNoMask) + return ""; + return indent(5) + '"' + prependIfDigit(s, v.second) + "\": " + fmtNum("%d", v.first) + + (isLast ? "\n" : ",\n"); + } + }; + + // base for C and C++ + class TPrinterCBase : public TPrinter { + protected: + virtual void printPrologue(std::ostream& out) const override { + out << "#ifndef spirv_" << headerGuardSuffix() << std::endl + << "#define spirv_" << headerGuardSuffix() << std::endl + << std::endl; + } + + void printMeta(std::ostream& out) const override { + out << "#define SPV_VERSION 0x" << std::hex << DocVersion << std::dec << "\n"; + out << "#define SPV_REVISION " << DocRevision << "\n"; + out << "\n"; + + return TPrinter::printMeta(out); + } + + virtual void printEpilogue(std::ostream& out) const override { + out << "#endif" << std::endl; + } + + virtual void printTypes(std::ostream& out) const override { + out << "typedef unsigned int " << pre() << "Id;\n\n"; + } + + virtual std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override + { + return std::string("static const unsigned int ") + pre() + name + + " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); + } + + virtual std::string pre() const { return ""; } // C name prefix + virtual std::string headerGuardSuffix() const = 0; + + virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return pre() + name; } + + virtual void printHasResultType(std::ostream& out) const + { + const Json::Value& enums = spvRoot["spv"]["enum"]; + + std::set seenValues; + + for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { + const auto opName = (*opClass)["Name"].asString(); + if (opName != "Op") { + continue; + } + + out << "#ifdef SPV_ENABLE_UTILITY_CODE" << std::endl; + out << "inline void " << pre() << "HasResultAndType(" << pre() << opName << " opcode, bool *hasResult, bool *hasResultType) {" << std::endl; + out << " *hasResult = *hasResultType = false;" << std::endl; + out << " switch (opcode) {" << std::endl; + out << " default: /* unknown opcode */ break;" << std::endl; + + for (auto& inst : spv::InstructionDesc) { + + // Filter out duplicate enum values, which would break the switch statement. + // These are probably just extension enums promoted to core. + if (seenValues.find(inst.value) != seenValues.end()) { + continue; + } + seenValues.insert(inst.value); + + std::string name = inst.name; + out << " case " << fmtEnumUse("Op", name) << ": *hasResult = " << (inst.hasResult() ? "true" : "false") << "; *hasResultType = " << (inst.hasType() ? "true" : "false") << "; break;" << std::endl; + } + + out << " }" << std::endl; + out << "}" << std::endl; + out << "#endif /* SPV_ENABLE_UTILITY_CODE */" << std::endl << std::endl; + } + } + }; + + // C printer + class TPrinterC final : public TPrinterCBase { + private: + std::string commentBeg() const override { return "/*\n"; } + std::string commentEnd(bool isLast) const override { return "*/\n"; } + std::string commentBOL() const override { return "** "; } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return std::string("typedef enum ") + pre() + s + styleStr(style) + "_ {\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return "} " + pre() + s + styleStr(style) + ";\n\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent() + pre() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string maxEnumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style) const override { + return enumFmt(s, v, style, true); + } + + std::string pre() const override { return "Spv"; } // C name prefix + std::string headerGuardSuffix() const override { return "H"; } + }; + + // C++ printer + class TPrinterCPP : public TPrinterCBase { + private: + void printPrologue(std::ostream& out) const override { + TPrinterCBase::printPrologue(out); + out << "namespace spv {\n\n"; + } + + void printEpilogue(std::ostream& out) const override { + const Json::Value& enums = spvRoot["spv"]["enum"]; + + // Create overloaded operator| for mask types + out << "// Overload operator| for mask bit combining\n\n"; + + for (auto opClass = enums.begin(); opClass != enums.end(); ++opClass) { + const bool isMask = (*opClass)["Type"].asString() == "Bit"; + const auto opName = (*opClass)["Name"].asString(); + + if (isMask) { + const auto typeName = opName + styleStr(enumMask); + + out << "inline " + typeName + " operator|(" + typeName + " a, " + typeName + " b) { return " + + typeName + "(unsigned(a) | unsigned(b)); }\n"; + } + } + + out << "\n} // end namespace spv\n\n"; + out << "#endif // #ifndef spirv_" << headerGuardSuffix() << std::endl; + } + + std::string commentBOL() const override { return "// "; } + + + virtual std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return std::string("enum ") + s + styleStr(style) + " {\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return "};\n\n"; + } + + virtual std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent() + s + v.second + styleStr(style) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + virtual std::string maxEnumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style) const override { + return enumFmt(s, v, style, true); + } + + // The C++ and C++11 headers define types with the same name. So they + // should use the same header guard. + std::string headerGuardSuffix() const override { return "HPP"; } + + std::string operators; + }; + + // C++11 printer (uses enum classes) + class TPrinterCPP11 final : public TPrinterCPP { + private: + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return std::string("enum class ") + s + styleStr(style) + " : unsigned {\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent() + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string maxEnumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style) const override { + return enumFmt(s, v, style, true); + } + + // Add type prefix for scoped enum + virtual std::string fmtEnumUse(const std::string& opPrefix, const std::string& name) const { return opPrefix + "::" + name; } + + std::string headerGuardSuffix() const override { return "HPP"; } + }; + + // LUA printer + class TPrinterLua final : public TPrinter { + private: + void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } + + void printEpilogue(std::ostream& out) const override { out << "}\n"; } + + std::string commentBOL() const override { return "-- "; } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return indent() + s + styleStr(style) + " = {\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return indent() + "},\n\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent(2) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + virtual std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override + { + return indent() + name + " = " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); + } + }; + + // Python printer + class TPrinterPython final : public TPrinter { + private: + void printPrologue(std::ostream& out) const override { out << "spv = {\n"; } + + void printEpilogue(std::ostream& out) const override { out << "}\n"; } + + std::string commentBOL() const override { return "# "; } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return indent() + "'" + s + styleStr(style) + "'" + " : {\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return indent() + "},\n\n"; + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent(2) + "'" + prependIfDigit(s, v.second) + "'" + " : " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override + { + return indent() + "'" + name + "'" + " : " + fmtNum(fmt, val) + (isLast ? ",\n\n" : ",\n"); + } + }; + + // C# printer + class TPrinterCSharp final : public TPrinter { + private: + std::string commentBOL() const override { return "// "; } + + void printPrologue(std::ostream& out) const override { + out << "namespace Spv\n{\n\n"; + out << indent() << "public static class Specification\n"; + out << indent() << "{\n"; + } + + void printEpilogue(std::ostream& out) const override { + out << indent() << "}\n"; + out << "}\n"; + } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return indent(2) + "public enum " + s + styleStr(style) + "\n" + indent(2) + "{\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return indent(2) + "}" + + (isLast ? "\n" : "\n\n"); + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent(3) + prependIfDigit(s, v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override { + return indent(2) + std::string("public const uint ") + name + + " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); + } + }; + + // D printer + class TPrinterD final : public TPrinter { + private: + std::string commentBeg() const override { return "/+\n"; } + std::string commentBOL() const override { return " + "; } + std::string commentEnd(bool isLast) const override { return " +/\n"; } + + void printPrologue(std::ostream& out) const override { + out << "module spv;\n\n"; + } + + void printEpilogue(std::ostream& out) const override { + } + + std::string enumBeg(const std::string& s, enumStyle_t style) const override { + return "enum " + s + styleStr(style) + " : uint\n{\n"; + } + + std::string enumEnd(const std::string& s, enumStyle_t style, bool isLast) const override { + return std::string("}\n\n"); + } + + std::string enumFmt(const std::string& s, const valpair_t& v, + enumStyle_t style, bool isLast) const override { + return indent() + prependIfDigit("_", v.second) + " = " + fmtStyleVal(v.first, style) + ",\n"; + } + + std::string fmtConstInt(unsigned val, const std::string& name, + const char* fmt, bool isLast) const override { + return std::string("enum uint ") + name + + " = " + fmtNum(fmt, val) + (isLast ? ";\n\n" : ";\n"); + } + }; + +} // namespace + +namespace spv { + void PrintAllHeaders() + { + // TODO: Once MSVC 2012 is no longer a factor, use brace initializers here + std::vector> langInfo; + + langInfo.push_back(std::make_pair(ELangC, "spirv.h")); + langInfo.push_back(std::make_pair(ELangCPP, "spirv.hpp")); + langInfo.push_back(std::make_pair(ELangCPP11, "spirv.hpp11")); + langInfo.push_back(std::make_pair(ELangJSON, "spirv.json")); + langInfo.push_back(std::make_pair(ELangLua, "spirv.lua")); + langInfo.push_back(std::make_pair(ELangPython, "spirv.py")); + langInfo.push_back(std::make_pair(ELangCSharp, "spirv.cs")); + langInfo.push_back(std::make_pair(ELangD, "spv.d")); + + for (const auto& lang : langInfo) { + std::ofstream out(lang.second, std::ios::out); + + if ((out.rdstate() & std::ifstream::failbit)) { + std::cerr << "Unable to open file: " << lang.second << std::endl; + } else { + PrintHeader(lang.first, out); + } + } + } + + // Print header for given language to given output stream + void PrintHeader(TLanguage lang, std::ostream& out) + { + typedef std::unique_ptr TPrinterPtr; + TPrinterPtr p; + + switch (lang) { + case ELangC: p = TPrinterPtr(new TPrinterC); break; + case ELangCPP: p = TPrinterPtr(new TPrinterCPP); break; + case ELangCPP11: p = TPrinterPtr(new TPrinterCPP11); break; + case ELangJSON: p = TPrinterPtr(new TPrinterJSON); break; + case ELangLua: p = TPrinterPtr(new TPrinterLua); break; + case ELangPython: p = TPrinterPtr(new TPrinterPython); break; + case ELangCSharp: p = TPrinterPtr(new TPrinterCSharp); break; + case ELangD: p = TPrinterPtr(new TPrinterD); break; + case ELangAll: PrintAllHeaders(); break; + default: + std::cerr << "Unknown language." << std::endl; + return; + } + + // Print the data in the requested format + if (p) + out << *p << std::endl; + + // object is auto-deleted + } + +} // namespace spv diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/header.h b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/header.h new file mode 100644 index 0000000..9c34b21 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/header.h @@ -0,0 +1,54 @@ +// Copyright (c) 2014-2019 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +// +// Print headers for SPIR-V in several languages. +// + +#pragma once +#ifndef header +#define header + +#include + +namespace spv { + // Languages supported + enum TLanguage { + ELangC, // C + ELangCPP, // C++03 + ELangCPP11, // C++11 + ELangJSON, // JSON + ELangLua, // Lua + ELangPython, // Python + ELangCSharp, // CSharp + ELangD, // D + + ELangAll, // print headers in all languages to files + }; + + // Generate header for requested language + void PrintHeader(TLanguage, std::ostream&); +} // namespace spv + +#endif // header diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.cpp b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.cpp new file mode 100644 index 0000000..2118678 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.cpp @@ -0,0 +1,495 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jsoncpp/dist/json/json.h" + +#include "jsonToSpirv.h" + +namespace spv { + +// The set of objects that hold all the instruction/operand +// parameterization information. +InstructionValues InstructionDesc; + +// The ordered list (in printing order) of printing classes +// (specification subsections). +PrintingClasses InstructionPrintingClasses; + +// Note: There is no entry for OperandOpcode. Use InstructionDesc instead. +EnumDefinition OperandClassParams[OperandOpcode]; +EnumValues SourceLanguageParams; +EnumValues ExecutionModelParams; +EnumValues AddressingParams; +EnumValues MemoryParams; +EnumValues ExecutionModeParams; +EnumValues StorageParams; +EnumValues SamplerAddressingModeParams; +EnumValues SamplerFilterModeParams; +EnumValues ImageFormatParams; +EnumValues ImageChannelOrderParams; +EnumValues ImageChannelDataTypeParams; +EnumValues ImageOperandsParams; +EnumValues FPFastMathParams; +EnumValues FPRoundingModeParams; +EnumValues LinkageTypeParams; +EnumValues DecorationParams; +EnumValues BuiltInParams; +EnumValues DimensionalityParams; +EnumValues FuncParamAttrParams; +EnumValues AccessQualifierParams; +EnumValues GroupOperationParams; +EnumValues LoopControlParams; +EnumValues SelectionControlParams; +EnumValues FunctionControlParams; +EnumValues MemorySemanticsParams; +EnumValues MemoryAccessParams; +EnumValues ScopeParams; +EnumValues KernelEnqueueFlagsParams; +EnumValues KernelProfilingInfoParams; +EnumValues CapabilityParams; +EnumValues RayFlagsParams; +EnumValues RayQueryIntersectionParams; +EnumValues RayQueryCommittedIntersectionTypeParams; +EnumValues RayQueryCandidateIntersectionTypeParams; +EnumValues FragmentShadingRateParams; + +std::pair ReadFile(const std::string& path) +{ + std::ifstream fstream(path, std::ios::in); + if (fstream) { + std::string contents; + fstream.seekg(0, std::ios::end); + contents.reserve((unsigned int)fstream.tellg()); + fstream.seekg(0, std::ios::beg); + contents.assign((std::istreambuf_iterator(fstream)), + std::istreambuf_iterator()); + return std::make_pair(true, contents); + } + return std::make_pair(false, ""); +} + +struct ClassOptionality { + OperandClass type; + bool optional; +}; + +// Converts the |operandKind| and |quantifier| pair used to describe operands +// in the JSON grammar to OperandClass and optionality used in this repo. +ClassOptionality ToOperandClassAndOptionality(const std::string& operandKind, const std::string& quantifier) +{ + assert(quantifier.empty() || quantifier == "?" || quantifier == "*"); + + if (operandKind == "IdRef") { + if (quantifier.empty()) + return {OperandId, false}; + else if (quantifier == "?") + return {OperandId, true}; + else + return {OperandVariableIds, false}; + } else if (operandKind == "LiteralInteger") { + if (quantifier.empty()) + return {OperandLiteralNumber, false}; + if (quantifier == "?") + return {OperandOptionalLiteral, true}; + else + return {OperandVariableLiterals, false}; + } else if (operandKind == "LiteralString") { + if (quantifier.empty()) + return {OperandLiteralString, false}; + else if (quantifier == "?") + return {OperandLiteralString, true}; + else { + return {OperandOptionalLiteralStrings, false}; + } + } else if (operandKind == "PairLiteralIntegerIdRef") { + // Used by OpSwitch in the grammar + return {OperandVariableLiteralId, false}; + } else if (operandKind == "PairIdRefLiteralInteger") { + // Used by OpGroupMemberDecorate in the grammar + return {OperandVariableIdLiteral, false}; + } else if (operandKind == "PairIdRefIdRef") { + // Used by OpPhi in the grammar + return {OperandVariableIds, false}; + } else { + OperandClass type = OperandNone; + if (operandKind == "IdMemorySemantics" || operandKind == "MemorySemantics") { + type = OperandMemorySemantics; + } else if (operandKind == "IdScope" || operandKind == "Scope") { + type = OperandScope; + } else if (operandKind == "LiteralExtInstInteger") { + type = OperandLiteralNumber; + } else if (operandKind == "LiteralSpecConstantOpInteger") { + type = OperandLiteralNumber; + } else if (operandKind == "LiteralContextDependentNumber") { + type = OperandAnySizeLiteralNumber; + } else if (operandKind == "SourceLanguage") { + type = OperandSource; + } else if (operandKind == "ExecutionModel") { + type = OperandExecutionModel; + } else if (operandKind == "AddressingModel") { + type = OperandAddressing; + } else if (operandKind == "MemoryModel") { + type = OperandMemory; + } else if (operandKind == "ExecutionMode") { + type = OperandExecutionMode; + } else if (operandKind == "StorageClass") { + type = OperandStorage; + } else if (operandKind == "Dim") { + type = OperandDimensionality; + } else if (operandKind == "SamplerAddressingMode") { + type = OperandSamplerAddressingMode; + } else if (operandKind == "SamplerFilterMode") { + type = OperandSamplerFilterMode; + } else if (operandKind == "ImageFormat") { + type = OperandSamplerImageFormat; + } else if (operandKind == "ImageChannelOrder") { + type = OperandImageChannelOrder; + } else if (operandKind == "ImageChannelDataType") { + type = OperandImageChannelDataType; + } else if (operandKind == "FPRoundingMode") { + type = OperandFPRoundingMode; + } else if (operandKind == "LinkageType") { + type = OperandLinkageType; + } else if (operandKind == "AccessQualifier") { + type = OperandAccessQualifier; + } else if (operandKind == "FunctionParameterAttribute") { + type = OperandFuncParamAttr; + } else if (operandKind == "Decoration") { + type = OperandDecoration; + } else if (operandKind == "BuiltIn") { + type = OperandBuiltIn; + } else if (operandKind == "GroupOperation") { + type = OperandGroupOperation; + } else if (operandKind == "KernelEnqueueFlags") { + type = OperandKernelEnqueueFlags; + } else if (operandKind == "KernelProfilingInfo") { + type = OperandKernelProfilingInfo; + } else if (operandKind == "Capability") { + type = OperandCapability; + } else if (operandKind == "ImageOperands") { + type = OperandImageOperands; + } else if (operandKind == "FPFastMathMode") { + type = OperandFPFastMath; + } else if (operandKind == "SelectionControl") { + type = OperandSelect; + } else if (operandKind == "LoopControl") { + type = OperandLoop; + } else if (operandKind == "FunctionControl") { + type = OperandFunction; + } else if (operandKind == "MemoryAccess") { + type = OperandMemoryOperands; + } else if (operandKind == "RayFlags") { + type = OperandRayFlags; + } else if (operandKind == "RayQueryIntersection") { + type = OperandRayQueryIntersection; + } else if (operandKind == "RayQueryCommittedIntersectionType") { + type = OperandRayQueryCommittedIntersectionType; + } else if (operandKind == "RayQueryCandidateIntersectionType") { + type = OperandRayQueryCandidateIntersectionType; + } else if (operandKind == "FragmentShadingRate") { + type = OperandFragmentShadingRate; + } + + if (type == OperandNone) { + std::cerr << "Unhandled operand kind found: " << operandKind << std::endl; + exit(1); + } + return {type, !quantifier.empty()}; + } +} + +bool IsTypeOrResultId(const std::string& str, bool* isType, bool* isResult) +{ + if (str == "IdResultType") + return *isType = true; + if (str == "IdResult") + return *isResult = true; + return false; +} + +// Given a number string, returns the position of the only bits set in the number. +// So it requires the number is a power of two. +unsigned int NumberStringToBit(const std::string& str) +{ + char* parseEnd; + unsigned int value = (unsigned int)std::strtol(str.c_str(), &parseEnd, 16); + assert(!(value & (value - 1)) && "input number is not a power of 2"); + unsigned int bit = 0; + for (; value; value >>= 1) ++bit; + return bit; +} + +void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders) +{ + // only do this once. + static bool initialized = false; + if (initialized) + return; + initialized = true; + + // Read the JSON grammar file. + bool fileReadOk = false; + std::string content; + std::tie(fileReadOk, content) = ReadFile(jsonPath); + if (!fileReadOk) { + std::cerr << "Failed to read JSON grammar file: " + << jsonPath << std::endl; + exit(1); + } + + // Decode the JSON grammar file. + Json::Reader reader; + Json::Value root; + if (!reader.parse(content, root)) { + std::cerr << "Failed to parse JSON grammar:\n" + << reader.getFormattedErrorMessages(); + exit(1); + } + + // Layouts for all instructions. + + // A lambda for returning capabilities from a JSON object as strings. + const auto getCaps = [](const Json::Value& object) { + EnumCaps result; + const auto& caps = object["capabilities"]; + if (!caps.empty()) { + assert(caps.isArray()); + for (const auto& cap : caps) { + result.emplace_back(cap.asString()); + } + } + return result; + }; + + // A lambda for returning extensions from a JSON object as strings. + const auto getExts = [](const Json::Value& object) { + Extensions result; + const auto& exts = object["extensions"]; + if (!exts.empty()) { + assert(exts.isArray()); + for (const auto& ext : exts) { + result.emplace_back(ext.asString()); + } + } + return result; + }; + + // set up the printing classes + std::unordered_set tags; // short-lived local for error checking below + const Json::Value printingClasses = root["instruction_printing_class"]; + for (const auto& printingClass : printingClasses) { + if (printingClass["tag"].asString().size() > 0) + tags.insert(printingClass["tag"].asString()); // just for error checking + else + std::cerr << "Error: each instruction_printing_class requires a non-empty \"tag\"" << std::endl; + if (buildingHeaders || printingClass["tag"].asString() != "@exclude") { + InstructionPrintingClasses.push_back({printingClass["tag"].asString(), + printingClass["heading"].asString()}); + } + } + + // process the instructions + const Json::Value insts = root["instructions"]; + for (const auto& inst : insts) { + const auto printingClass = inst["class"].asString(); + if (printingClass.size() == 0) { + std::cerr << "Error: " << inst["opname"].asString() + << " requires a non-empty printing \"class\" tag" << std::endl; + } + if (!buildingHeaders && printingClass == "@exclude") + continue; + if (tags.find(printingClass) == tags.end()) { + std::cerr << "Error: " << inst["opname"].asString() + << " requires a \"class\" declared as a \"tag\" in \"instruction printing_class\"" + << std::endl; + } + const auto opcode = inst["opcode"].asUInt(); + const std::string name = inst["opname"].asString(); + EnumCaps caps = getCaps(inst); + std::string version = inst["version"].asString(); + std::string lastVersion = inst["lastVersion"].asString(); + Extensions exts = getExts(inst); + OperandParameters operands; + bool defResultId = false; + bool defTypeId = false; + for (const auto& operand : inst["operands"]) { + const std::string kind = operand["kind"].asString(); + const std::string quantifier = operand.get("quantifier", "").asString(); + const std::string doc = operand.get("name", "").asString(); + if (!IsTypeOrResultId(kind, &defTypeId, &defResultId)) { + const auto p = ToOperandClassAndOptionality(kind, quantifier); + operands.push(p.type, doc, p.optional); + } + } + InstructionDesc.emplace_back( + std::move(EnumValue(opcode, name, + std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), + std::move(operands))), + printingClass, defTypeId, defResultId); + } + + // Specific additional context-dependent operands + + // Populate dest with EnumValue objects constructed from source. + const auto populateEnumValues = [&getCaps,&getExts](EnumValues* dest, const Json::Value& source, bool bitEnum) { + // A lambda for determining the numeric value to be used for a given + // enumerant in JSON form, and whether that value is a 0 in a bitfield. + auto getValue = [&bitEnum](const Json::Value& enumerant) { + std::pair result{0u,false}; + if (!bitEnum) { + result.first = enumerant["value"].asUInt(); + } else { + const unsigned int bit = NumberStringToBit(enumerant["value"].asString()); + if (bit == 0) + result.second = true; + else + result.first = bit - 1; // This is the *shift* amount. + } + return result; + }; + + for (const auto& enumerant : source["enumerants"]) { + unsigned value; + bool skip_zero_in_bitfield; + std::tie(value, skip_zero_in_bitfield) = getValue(enumerant); + if (skip_zero_in_bitfield) + continue; + EnumCaps caps(getCaps(enumerant)); + std::string version = enumerant["version"].asString(); + std::string lastVersion = enumerant["lastVersion"].asString(); + Extensions exts(getExts(enumerant)); + OperandParameters params; + const Json::Value& paramsJson = enumerant["parameters"]; + if (!paramsJson.empty()) { // This enumerant has parameters. + assert(paramsJson.isArray()); + for (const auto& param : paramsJson) { + const std::string kind = param["kind"].asString(); + const std::string doc = param.get("name", "").asString(); + const auto p = ToOperandClassAndOptionality(kind, ""); // All parameters are required! + params.push(p.type, doc); + } + } + dest->emplace_back( + value, enumerant["enumerant"].asString(), + std::move(caps), std::move(version), std::move(lastVersion), std::move(exts), std::move(params)); + } + }; + + const auto establishOperandClass = [&populateEnumValues]( + const std::string& enumName, spv::OperandClass operandClass, + spv::EnumValues* enumValues, const Json::Value& operandEnum, const std::string& category) { + assert(category == "BitEnum" || category == "ValueEnum"); + bool bitEnum = (category == "BitEnum"); + populateEnumValues(enumValues, operandEnum, bitEnum); + OperandClassParams[operandClass].set(enumName, enumValues, bitEnum); + }; + + const Json::Value operandEnums = root["operand_kinds"]; + for (const auto& operandEnum : operandEnums) { + const std::string enumName = operandEnum["kind"].asString(); + const std::string category = operandEnum["category"].asString(); + if (enumName == "SourceLanguage") { + establishOperandClass(enumName, OperandSource, &SourceLanguageParams, operandEnum, category); + } else if (enumName == "Decoration") { + establishOperandClass(enumName, OperandDecoration, &DecorationParams, operandEnum, category); + } else if (enumName == "ExecutionMode") { + establishOperandClass(enumName, OperandExecutionMode, &ExecutionModeParams, operandEnum, category); + } else if (enumName == "Capability") { + establishOperandClass(enumName, OperandCapability, &CapabilityParams, operandEnum, category); + } else if (enumName == "AddressingModel") { + establishOperandClass(enumName, OperandAddressing, &AddressingParams, operandEnum, category); + } else if (enumName == "MemoryModel") { + establishOperandClass(enumName, OperandMemory, &MemoryParams, operandEnum, category); + } else if (enumName == "MemorySemantics") { + establishOperandClass(enumName, OperandMemorySemantics, &MemorySemanticsParams, operandEnum, category); + } else if (enumName == "ExecutionModel") { + establishOperandClass(enumName, OperandExecutionModel, &ExecutionModelParams, operandEnum, category); + } else if (enumName == "StorageClass") { + establishOperandClass(enumName, OperandStorage, &StorageParams, operandEnum, category); + } else if (enumName == "SamplerAddressingMode") { + establishOperandClass(enumName, OperandSamplerAddressingMode, &SamplerAddressingModeParams, operandEnum, category); + } else if (enumName == "SamplerFilterMode") { + establishOperandClass(enumName, OperandSamplerFilterMode, &SamplerFilterModeParams, operandEnum, category); + } else if (enumName == "ImageFormat") { + establishOperandClass(enumName, OperandSamplerImageFormat, &ImageFormatParams, operandEnum, category); + } else if (enumName == "ImageChannelOrder") { + establishOperandClass(enumName, OperandImageChannelOrder, &ImageChannelOrderParams, operandEnum, category); + } else if (enumName == "ImageChannelDataType") { + establishOperandClass(enumName, OperandImageChannelDataType, &ImageChannelDataTypeParams, operandEnum, category); + } else if (enumName == "ImageOperands") { + establishOperandClass(enumName, OperandImageOperands, &ImageOperandsParams, operandEnum, category); + } else if (enumName == "FPFastMathMode") { + establishOperandClass(enumName, OperandFPFastMath, &FPFastMathParams, operandEnum, category); + } else if (enumName == "FPRoundingMode") { + establishOperandClass(enumName, OperandFPRoundingMode, &FPRoundingModeParams, operandEnum, category); + } else if (enumName == "LinkageType") { + establishOperandClass(enumName, OperandLinkageType, &LinkageTypeParams, operandEnum, category); + } else if (enumName == "FunctionParameterAttribute") { + establishOperandClass(enumName, OperandFuncParamAttr, &FuncParamAttrParams, operandEnum, category); + } else if (enumName == "AccessQualifier") { + establishOperandClass(enumName, OperandAccessQualifier, &AccessQualifierParams, operandEnum, category); + } else if (enumName == "BuiltIn") { + establishOperandClass(enumName, OperandBuiltIn, &BuiltInParams, operandEnum, category); + } else if (enumName == "SelectionControl") { + establishOperandClass(enumName, OperandSelect, &SelectionControlParams, operandEnum, category); + } else if (enumName == "LoopControl") { + establishOperandClass(enumName, OperandLoop, &LoopControlParams, operandEnum, category); + } else if (enumName == "FunctionControl") { + establishOperandClass(enumName, OperandFunction, &FunctionControlParams, operandEnum, category); + } else if (enumName == "Dim") { + establishOperandClass(enumName, OperandDimensionality, &DimensionalityParams, operandEnum, category); + } else if (enumName == "MemoryAccess") { + establishOperandClass(enumName, OperandMemoryOperands, &MemoryAccessParams, operandEnum, category); + } else if (enumName == "Scope") { + establishOperandClass(enumName, OperandScope, &ScopeParams, operandEnum, category); + } else if (enumName == "GroupOperation") { + establishOperandClass(enumName, OperandGroupOperation, &GroupOperationParams, operandEnum, category); + } else if (enumName == "KernelEnqueueFlags") { + establishOperandClass(enumName, OperandKernelEnqueueFlags, &KernelEnqueueFlagsParams, operandEnum, category); + } else if (enumName == "KernelProfilingInfo") { + establishOperandClass(enumName, OperandKernelProfilingInfo, &KernelProfilingInfoParams, operandEnum, category); + } else if (enumName == "RayFlags") { + establishOperandClass(enumName, OperandRayFlags, &RayFlagsParams, operandEnum, category); + } else if (enumName == "RayQueryIntersection") { + establishOperandClass(enumName, OperandRayQueryIntersection, &RayQueryIntersectionParams, operandEnum, category); + } else if (enumName == "RayQueryCommittedIntersectionType") { + establishOperandClass(enumName, OperandRayQueryCommittedIntersectionType, &RayQueryCommittedIntersectionTypeParams, operandEnum, category); + } else if (enumName == "RayQueryCandidateIntersectionType") { + establishOperandClass(enumName, OperandRayQueryCandidateIntersectionType, &RayQueryCandidateIntersectionTypeParams, operandEnum, category); + } else if (enumName == "FragmentShadingRate") { + establishOperandClass(enumName, OperandFragmentShadingRate, &FragmentShadingRateParams, operandEnum, category); + } + } +} + +}; // end namespace spv diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.h b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.h new file mode 100644 index 0000000..72c7cde --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/jsonToSpirv.h @@ -0,0 +1,294 @@ +// Copyright (c) 2014-2020 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#pragma once +#ifndef JSON_TO_SPIRV +#define JSON_TO_SPIRV + +#include +#include +#include +#include + +namespace spv { + + // Reads the file in the given |path|. Returns true and the contents of the +// file on success; otherwise, returns false and an empty string. +std::pair ReadFile(const std::string& path); + +// Fill in all the parameters +void jsonToSpirv(const std::string& jsonPath, bool buildingHeaders); + +// For parameterizing operands. +enum OperandClass { + OperandNone, + OperandId, + OperandVariableIds, + OperandOptionalLiteral, + OperandOptionalLiteralString, + OperandOptionalLiteralStrings, + OperandVariableLiterals, + OperandVariableIdLiteral, + OperandVariableLiteralId, + OperandAnySizeLiteralNumber, + OperandLiteralNumber, + OperandLiteralString, + OperandSource, + OperandExecutionModel, + OperandAddressing, + OperandMemory, + OperandExecutionMode, + OperandStorage, + OperandDimensionality, + OperandSamplerAddressingMode, + OperandSamplerFilterMode, + OperandSamplerImageFormat, + OperandImageChannelOrder, + OperandImageChannelDataType, + OperandImageOperands, + OperandFPFastMath, + OperandFPRoundingMode, + OperandLinkageType, + OperandAccessQualifier, + OperandFuncParamAttr, + OperandDecoration, + OperandBuiltIn, + OperandSelect, + OperandLoop, + OperandFunction, + OperandMemorySemantics, + OperandMemoryOperands, + OperandScope, + OperandGroupOperation, + OperandKernelEnqueueFlags, + OperandKernelProfilingInfo, + OperandCapability, + OperandRayFlags, + OperandRayQueryIntersection, + OperandRayQueryCommittedIntersectionType, + OperandRayQueryCandidateIntersectionType, + OperandFragmentShadingRate, + + OperandOpcode, + + OperandCount +}; + +// For direct representation of the JSON grammar "instruction_printing_class". +struct PrintingClass { + std::string tag; + std::string heading; +}; +using PrintingClasses = std::vector; + +// Any specific enum can have a set of capabilities that allow it: +typedef std::vector EnumCaps; + +// A set of extensions. +typedef std::vector Extensions; + +// Parameterize a set of operands with their OperandClass(es) and descriptions. +class OperandParameters { +public: + OperandParameters() { } + void push(OperandClass oc, const std::string& d, bool opt = false) + { + opClass.push_back(oc); + desc.push_back(d); + optional.push_back(opt); + } + void setOptional(); + OperandClass getClass(int op) const { return opClass[op]; } + const char* getDesc(int op) const { return desc[op].c_str(); } + bool isOptional(int op) const { return optional[op]; } + int getNum() const { return (int)opClass.size(); } + +protected: + std::vector opClass; + std::vector desc; + std::vector optional; +}; + +// An ordered sequence of EValue. We'll preserve the order found in the +// JSON file. You can look up a value by enum or by name. If there are +// duplicate values, then take the first. We assume names are unique. +// The EValue must have an unsigned |value| field and a string |name| field. +template +class EnumValuesContainer { +public: + using ContainerType = std::vector; + using iterator = typename ContainerType::iterator; + using const_iterator = typename ContainerType::const_iterator; + + EnumValuesContainer() {} + + // Constructs an EValue in place as a new element at the end of the + // sequence. + template + void emplace_back(Args&&... args) { + values.emplace_back(std::forward(args)...); + } + + // Returns the first EValue in the sequence with the given value. + // More than one EValue might have the same value. + EValue& operator[](unsigned value) { + auto where = std::find_if(begin(), end(), [&value](const EValue& e) { + return value == e.value; + }); + assert((where != end()) && "Could not find enum in the enum list"); + return *where; + } + // gets *all* entries for the value, including the first one + void gatherAliases(unsigned value, std::vector& aliases) { + std::for_each(begin(), end(), [&](EValue& e) { + if (value == e.value) + aliases.push_back(&e);}); + } + // Returns the EValue with the given name. We assume uniqueness + // by name. + EValue& at(std::string name) { + auto where = std::find_if(begin(), end(), [&name](const EValue& e) { + return name == e.name; + }); + assert((where != end()) && "Could not find name in the enum list"); + return *where; + } + + iterator begin() { return values.begin(); } + iterator end() { return values.end(); } + +private: + ContainerType values; +}; + +// A single enumerant value. Corresponds to a row in an enumeration table +// in the spec. +class EnumValue { +public: + EnumValue() : value(0), desc(nullptr) {} + EnumValue(unsigned int the_value, const std::string& the_name, EnumCaps&& the_caps, + const std::string& the_firstVersion, const std::string& the_lastVersion, + Extensions&& the_extensions, OperandParameters&& the_operands) : + value(the_value), name(the_name), capabilities(std::move(the_caps)), + firstVersion(std::move(the_firstVersion)), lastVersion(std::move(the_lastVersion)), + extensions(std::move(the_extensions)), operands(std::move(the_operands)), desc(nullptr) { } + + // For ValueEnum, the value from the JSON file. + // For BitEnum, the index of the bit position represented by this mask. + // (That is, what you shift 1 by to get the mask.) + unsigned value; + std::string name; + EnumCaps capabilities; + std::string firstVersion; + std::string lastVersion; + // A feature only be enabled by certain extensions. + // An empty list means the feature does not require an extension. + // Normally, only Capability enums are enabled by extension. In turn, + // other enums and instructions are enabled by those capabilities. + Extensions extensions; + OperandParameters operands; + const char* desc; +}; + +using EnumValues = EnumValuesContainer; + +// Parameterize a set of enumerants that form an enum +class EnumDefinition { +public: + EnumDefinition() : + desc(0), bitmask(false), enumValues(nullptr) { } + void set(const std::string& enumName, EnumValues* enumValuesArg, bool mask = false) + { + codeName = enumName; + bitmask = mask; + enumValues = enumValuesArg; + } + // Returns the first EnumValue in the sequence with the given value. + // More than one EnumValue might have the same value. Only valid + // if enumValues has been populated. + EnumValue& operator[](unsigned value) { + assert(enumValues != nullptr); + return (*enumValues)[value]; + } + // Returns the name of the first EnumValue with the given value. + // Assumes enumValues has been populated. + const char* getName(unsigned value) { + return (*this)[value].name.c_str(); + } + + using iterator = EnumValues::iterator; + iterator begin() { return enumValues->begin(); } + iterator end() { return enumValues->end(); } + + std::string codeName; // name to use when declaring headers for code + const char* desc; + bool bitmask; // true if these enumerants combine into a bitmask + EnumValues* enumValues; // parameters for each individual enumerant +}; + +// Parameterize an instruction's logical format, including its known set of operands, +// per OperandParameters above. +class InstructionValue : public EnumValue { +public: + InstructionValue(EnumValue&& e, const std::string& printClass, bool has_type, bool has_result) + : EnumValue(std::move(e)), + printingClass(printClass), + opDesc("TBD"), + typePresent(has_type), + resultPresent(has_result), + alias(this) { } + InstructionValue(const InstructionValue& v) + { + *this = v; + alias = this; + } + + bool hasResult() const { return resultPresent != 0; } + bool hasType() const { return typePresent != 0; } + void setAlias(const InstructionValue& a) { alias = &a; } + const InstructionValue& getAlias() const { return *alias; } + bool isAlias() const { return alias != this; } + + std::string printingClass; + const char* opDesc; + +protected: + int typePresent : 1; + int resultPresent : 1; + const InstructionValue* alias; // correct only after discovering the aliases; otherwise points to this +}; + +using InstructionValues = EnumValuesContainer; + +// Parameterization info for all instructions. +extern InstructionValues InstructionDesc; +extern PrintingClasses InstructionPrintingClasses; + +// These hold definitions of the enumerants used for operands. +// This is indexed by OperandClass, but not including OperandOpcode. +extern EnumDefinition OperandClassParams[]; + +}; // end namespace spv + +#endif // JSON_TO_SPIRV diff --git a/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/main.cpp b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/main.cpp new file mode 100644 index 0000000..7e5f7f8 --- /dev/null +++ b/third_party/spirv-tools/external/spirv-headers/tools/buildHeaders/main.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2014-2019 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +//#include +#include +#include + +#include "jsonToSpirv.h" +#include "header.h" + +// Command-line options +enum TOptions { + EOptionNone = 0x000, + EOptionPrintHeader = 0x008, +}; + +std::string jsonPath; +int Options; +spv::TLanguage Language; + +void Usage() +{ + printf("Usage: spirv option [file]\n" + "\n" + " -h print header for given language to stdout, from one of:\n" + " C - C99 header\n" + " C++ - C++03 or greater header (also accepts C++03)\n" + " C++11 - C++11 or greater header\n" + " JSON - JSON format data\n" + " Lua - Lua module\n" + " Python - Python module (also accepts Py)\n" + " C# - C# module (also accepts CSharp)\n" + " D - D module\n" + " -H print header in all supported languages to files in current directory\n" + ); +} + +std::string tolower_s(std::string s) +{ + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + return s; +} + +bool ProcessArguments(int argc, char* argv[]) +{ + argc--; + argv++; + for (; argc >= 1; argc--, argv++) { + if (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'H': + Options |= EOptionPrintHeader; + Language = spv::ELangAll; + break; + case 'h': { + if (argc < 2) + return false; + + Options |= EOptionPrintHeader; + const std::string language(tolower_s(argv[1])); + + if (language == "c") { + Language = spv::ELangC; + } else if (language == "c++" || language == "c++03") { + Language = spv::ELangCPP; + } else if (language == "c++11") { + Language = spv::ELangCPP11; + } else if (language == "json") { + Language = spv::ELangJSON; + } else if (language == "lua") { + Language = spv::ELangLua; + } else if (language == "python" || language == "py") { + Language = spv::ELangPython; + } else if (language == "c#" || language == "csharp") { + Language = spv::ELangCSharp; + } else if (language == "d") { + Language = spv::ELangD; + } else + return false; + + return true; + } + default: + return false; + } + } else { + jsonPath = std::string(argv[0]); + } + } + + return true; +} + +int main(int argc, char* argv[]) +{ + if (argc < 2 || ! ProcessArguments(argc, argv)) { + Usage(); + return 1; + } + + spv::jsonToSpirv(jsonPath, (Options & EOptionPrintHeader) != 0); + if (Options & EOptionPrintHeader) + spv::PrintHeader(Language, std::cout); + + return 0; +} diff --git a/third_party/spirv-tools/filament-specific-changes.patch b/third_party/spirv-tools/filament-specific-changes.patch new file mode 100644 index 0000000..e1c6d58 --- /dev/null +++ b/third_party/spirv-tools/filament-specific-changes.patch @@ -0,0 +1,215 @@ +diff --git a/third_party/spirv-tools/CMakeLists.txt b/third_party/spirv-tools/CMakeLists.txt +index 6ed56a81..2392dfd8 100755 +--- a/third_party/spirv-tools/CMakeLists.txt ++++ b/third_party/spirv-tools/CMakeLists.txt +@@ -24,8 +24,19 @@ if (POLICY CMP0054) + endif() + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + ++# Filament specific changes ++if (APPLE) ++ set(CMAKE_MACOSX_RPATH ON) ++endif (APPLE) ++set(SPIRV_SKIP_EXECUTABLES_OPTION ON) ++set(SPIRV_SKIP_TESTS_OPTION ON) ++set(SKIP_SPIRV_TOOLS_INSTALL ON) ++# End Filament specific changes ++ + project(spirv-tools) +-enable_testing() ++# Filament specific changes ++# enable_testing() ++# End Filament specific changes + set(SPIRV_TOOLS "SPIRV-Tools") + + include(GNUInstallDirs) +@@ -68,7 +79,9 @@ if ("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE "Debug") + endif() + +-option(SKIP_SPIRV_TOOLS_INSTALL "Skip installation" ${SKIP_SPIRV_TOOLS_INSTALL}) ++# Filament specific changes ++# option(SKIP_SPIRV_TOOLS_INSTALL "Skip installation" ${SKIP_SPIRV_TOOLS_INSTALL}) ++# End Filament specific changes + if(NOT ${SKIP_SPIRV_TOOLS_INSTALL}) + set(ENABLE_SPIRV_TOOLS_INSTALL ON) + endif() +@@ -231,11 +244,13 @@ if(ENABLE_SPIRV_TOOLS_INSTALL) + endif() + + # Defaults to OFF if the user didn't set it. ++# Filament specific changes + option(SPIRV_SKIP_EXECUTABLES + "Skip building the executable and tests along with the library" +- ${SPIRV_SKIP_EXECUTABLES}) ++ ${SPIRV_SKIP_EXECUTABLES_OPTION}) + option(SPIRV_SKIP_TESTS +- "Skip building tests along with the library" ${SPIRV_SKIP_TESTS}) ++ "Skip building tests along with the library" ${SPIRV_SKIP_TESTS_OPTION}) ++# End Filament specific changes + if ("${SPIRV_SKIP_EXECUTABLES}") + set(SPIRV_SKIP_TESTS ON) + endif() +@@ -279,8 +294,10 @@ endif() + add_subdirectory(source) + add_subdirectory(tools) + +-add_subdirectory(test) +-add_subdirectory(examples) ++# Filament specific changes ++#add_subdirectory(test) ++#add_subdirectory(examples) ++# End Filament specific changes + + if(ENABLE_SPIRV_TOOLS_INSTALL) + install( +@@ -305,28 +322,30 @@ set(SPIRV_SHARED_LIBRARIES "-lSPIRV-Tools-shared") + + # Build pkg-config file + # Use a first-class target so it's regenerated when relevant files are updated. +-add_custom_target(spirv-tools-pkg-config ALL +- COMMAND ${CMAKE_COMMAND} +- -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES +- -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in +- -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc +- -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} +- -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} +- -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} +- -DSPIRV_LIBRARIES=${SPIRV_LIBRARIES} +- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake +- DEPENDS "CHANGES" "cmake/SPIRV-Tools.pc.in" "cmake/write_pkg_config.cmake") +-add_custom_target(spirv-tools-shared-pkg-config ALL +- COMMAND ${CMAKE_COMMAND} +- -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES +- -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in +- -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc +- -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} +- -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} +- -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} +- -DSPIRV_SHARED_LIBRARIES=${SPIRV_SHARED_LIBRARIES} +- -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake +- DEPENDS "CHANGES" "cmake/SPIRV-Tools-shared.pc.in" "cmake/write_pkg_config.cmake") ++# Filament specific changes ++# add_custom_target(spirv-tools-pkg-config ALL ++# COMMAND ${CMAKE_COMMAND} ++# -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES ++# -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools.pc.in ++# -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools.pc ++# -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} ++# -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} ++# -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} ++# -DSPIRV_LIBRARIES=${SPIRV_LIBRARIES} ++# -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake ++# DEPENDS "CHANGES" "cmake/SPIRV-Tools.pc.in" "cmake/write_pkg_config.cmake") ++# add_custom_target(spirv-tools-shared-pkg-config ALL ++# COMMAND ${CMAKE_COMMAND} ++# -DCHANGES_FILE=${CMAKE_CURRENT_SOURCE_DIR}/CHANGES ++# -DTEMPLATE_FILE=${CMAKE_CURRENT_SOURCE_DIR}/cmake/SPIRV-Tools-shared.pc.in ++# -DOUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/SPIRV-Tools-shared.pc ++# -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} ++# -DCMAKE_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR} ++# -DCMAKE_INSTALL_INCLUDEDIR=${CMAKE_INSTALL_INCLUDEDIR} ++# -DSPIRV_SHARED_LIBRARIES=${SPIRV_SHARED_LIBRARIES} ++# -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/write_pkg_config.cmake ++# DEPENDS "CHANGES" "cmake/SPIRV-Tools-shared.pc.in" "cmake/write_pkg_config.cmake") ++# End Filament specific changes + + # Install pkg-config file + if (ENABLE_SPIRV_TOOLS_INSTALL) +@@ -201,7 +201,11 @@ if(NOT COMMAND find_host_program) + endif() + + # Tests require Python3 +-find_host_package(PythonInterp 3 REQUIRED) ++if(CMAKE_VERSION VERSION_LESS "3.12" OR ${CMAKE_SYSTEM_NAME} MATCHES "Windows") ++ find_host_package(PythonInterp 3 REQUIRED) ++else() ++ find_package(Python3 COMPONENTS Interpreter) ++endif() + + # Check for symbol exports on Linux. + # At the moment, this check will fail on the OSX build machines for the Android NDK. +diff --git a/third_party/spirv-tools/external/CMakeLists.txt b/third_party/spirv-tools/external/CMakeLists.txt +index 179a4012f..152cfc421 100644 +--- a/third_party/spirv-tools/external/CMakeLists.txt ++++ b/third_party/spirv-tools/external/CMakeLists.txt +@@ -45,8 +45,10 @@ if (IS_DIRECTORY ${SPIRV_HEADER_DIR}) + # Do this so enclosing projects can use SPIRV-Headers_SOURCE_DIR to find + # headers to include. + if (NOT DEFINED SPIRV-Headers_SOURCE_DIR) +- set(SPIRV_HEADERS_SKIP_INSTALL ON) +- set(SPIRV_HEADERS_SKIP_EXAMPLES ON) ++# Filament specific changes ++ #set(SPIRV_HEADERS_SKIP_INSTALL ON) ++ #set(SPIRV_HEADERS_SKIP_EXAMPLES ON) ++# End Filament specific changes + add_subdirectory(${SPIRV_HEADER_DIR}) + endif() + else() +diff --git a/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt b/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt +index eb4694786..d3940532d 100644 +--- a/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt ++++ b/third_party/spirv-tools/external/spirv-headers/CMakeLists.txt +@@ -49,11 +49,11 @@ add_custom_target(install-headers + COMMAND cmake -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/include/spirv + $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/include/spirv) + +-option(SPIRV_HEADERS_SKIP_EXAMPLES "Skip building examples" +- ${SPIRV_HEADERS_SKIP_EXAMPLES}) ++# Filament specific changes ++option(SPIRV_HEADERS_SKIP_EXAMPLES "Skip building examples" ON) + +-option(SPIRV_HEADERS_SKIP_INSTALL "Skip install" +- ${SPIRV_HEADERS_SKIP_INSTALL}) ++option(SPIRV_HEADERS_SKIP_INSTALL "Skip install" ON) ++# End Filament specific changes + + if(NOT ${SPIRV_HEADERS_SKIP_EXAMPLES}) + set(SPIRV_HEADERS_ENABLE_EXAMPLES ON) +diff --git a/third_party/spirv-tools/source/CMakeLists.txt b/third_party/spirv-tools/source/CMakeLists.txt +index 65087f2c..38101ab2 100644 +--- a/third_party/spirv-tools/source/CMakeLists.txt ++++ b/third_party/spirv-tools/source/CMakeLists.txt +@@ -364,13 +364,15 @@ endfunction() + + # Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and + # unlike the other SPIRV_TOOLS target, defaults to hidden symbol visibility. +-add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES}) +-spirv_tools_default_target_options(${SPIRV_TOOLS}-shared) +-set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES CXX_VISIBILITY_PRESET hidden) +-target_compile_definitions(${SPIRV_TOOLS}-shared +- PRIVATE SPIRV_TOOLS_IMPLEMENTATION +- PUBLIC SPIRV_TOOLS_SHAREDLIB +-) ++# Filament specific changes ++# add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES}) ++# spirv_tools_default_target_options(${SPIRV_TOOLS}-shared) ++# set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES CXX_VISIBILITY_PRESET hidden) ++# target_compile_definitions(${SPIRV_TOOLS}-shared ++# PRIVATE SPIRV_TOOLS_IMPLEMENTATION ++# PUBLIC SPIRV_TOOLS_SHAREDLIB ++# ) ++# End Filament specific changes + + if(SPIRV_TOOLS_BUILD_STATIC) + add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES}) +@@ -386,11 +388,17 @@ if(SPIRV_TOOLS_BUILD_STATIC) + add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static) + endif() + +- set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared) ++# Filament specific changes ++ # set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared) ++ set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static) ++# End Filament specific changes + else() + add_library(${SPIRV_TOOLS} ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_SOURCES}) + spirv_tools_default_target_options(${SPIRV_TOOLS}) +- set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared) ++# Filament specific changes ++ # set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared) ++ set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}) ++# End Filament specific changes + endif() + + if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") diff --git a/third_party/spirv-tools/include/spirv-tools/instrument.hpp b/third_party/spirv-tools/include/spirv-tools/instrument.hpp new file mode 100644 index 0000000..9c01cb6 --- /dev/null +++ b/third_party/spirv-tools/include/spirv-tools/instrument.hpp @@ -0,0 +1,251 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_ +#define INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_ + +// Shader Instrumentation Interface +// +// This file provides an external interface for applications that wish to +// communicate with shaders instrumented by passes created by: +// +// CreateInstBindlessCheckPass +// CreateInstBuffAddrCheckPass +// CreateInstDebugPrintfPass +// +// More detailed documentation of these routines can be found in optimizer.hpp + +namespace spvtools { + +// Stream Output Buffer Offsets +// +// The following values provide offsets into the output buffer struct +// generated by InstrumentPass::GenDebugStreamWrite. This method is utilized +// by InstBindlessCheckPass, InstBuffAddrCheckPass, and InstDebugPrintfPass. +// +// The first member of the debug output buffer contains the next available word +// in the data stream to be written. Shaders will atomically read and update +// this value so as not to overwrite each others records. This value must be +// initialized to zero +static const int kDebugOutputSizeOffset = 0; + +// The second member of the output buffer is the start of the stream of records +// written by the instrumented shaders. Each record represents a validation +// error. The format of the records is documented below. +static const int kDebugOutputDataOffset = 1; + +// Common Stream Record Offsets +// +// The following are offsets to fields which are common to all records written +// to the output stream. +// +// Each record first contains the size of the record in 32-bit words, including +// the size word. +static const int kInstCommonOutSize = 0; + +// This is the shader id passed by the layer when the instrumentation pass is +// created. +static const int kInstCommonOutShaderId = 1; + +// This is the ordinal position of the instruction within the SPIR-V shader +// which generated the validation error. +static const int kInstCommonOutInstructionIdx = 2; + +// This is the stage which generated the validation error. This word is used +// to determine the contents of the next two words in the record. +// 0:Vert, 1:TessCtrl, 2:TessEval, 3:Geom, 4:Frag, 5:Compute +static const int kInstCommonOutStageIdx = 3; +static const int kInstCommonOutCnt = 4; + +// Stage-specific Stream Record Offsets +// +// Each stage will contain different values in the next set of words of the +// record used to identify which instantiation of the shader generated the +// validation error. +// +// Vertex Shader Output Record Offsets +static const int kInstVertOutVertexIndex = kInstCommonOutCnt; +static const int kInstVertOutInstanceIndex = kInstCommonOutCnt + 1; +static const int kInstVertOutUnused = kInstCommonOutCnt + 2; + +// Frag Shader Output Record Offsets +static const int kInstFragOutFragCoordX = kInstCommonOutCnt; +static const int kInstFragOutFragCoordY = kInstCommonOutCnt + 1; +static const int kInstFragOutUnused = kInstCommonOutCnt + 2; + +// Compute Shader Output Record Offsets +static const int kInstCompOutGlobalInvocationIdX = kInstCommonOutCnt; +static const int kInstCompOutGlobalInvocationIdY = kInstCommonOutCnt + 1; +static const int kInstCompOutGlobalInvocationIdZ = kInstCommonOutCnt + 2; + +// Tessellation Control Shader Output Record Offsets +static const int kInstTessCtlOutInvocationId = kInstCommonOutCnt; +static const int kInstTessCtlOutPrimitiveId = kInstCommonOutCnt + 1; +static const int kInstTessCtlOutUnused = kInstCommonOutCnt + 2; + +// Tessellation Eval Shader Output Record Offsets +static const int kInstTessEvalOutPrimitiveId = kInstCommonOutCnt; +static const int kInstTessEvalOutTessCoordU = kInstCommonOutCnt + 1; +static const int kInstTessEvalOutTessCoordV = kInstCommonOutCnt + 2; + +// Geometry Shader Output Record Offsets +static const int kInstGeomOutPrimitiveId = kInstCommonOutCnt; +static const int kInstGeomOutInvocationId = kInstCommonOutCnt + 1; +static const int kInstGeomOutUnused = kInstCommonOutCnt + 2; + +// Ray Tracing Shader Output Record Offsets +static const int kInstRayTracingOutLaunchIdX = kInstCommonOutCnt; +static const int kInstRayTracingOutLaunchIdY = kInstCommonOutCnt + 1; +static const int kInstRayTracingOutLaunchIdZ = kInstCommonOutCnt + 2; + +// Mesh Shader Output Record Offsets +static const int kInstMeshOutGlobalInvocationIdX = kInstCommonOutCnt; +static const int kInstMeshOutGlobalInvocationIdY = kInstCommonOutCnt + 1; +static const int kInstMeshOutGlobalInvocationIdZ = kInstCommonOutCnt + 2; + +// Task Shader Output Record Offsets +static const int kInstTaskOutGlobalInvocationIdX = kInstCommonOutCnt; +static const int kInstTaskOutGlobalInvocationIdY = kInstCommonOutCnt + 1; +static const int kInstTaskOutGlobalInvocationIdZ = kInstCommonOutCnt + 2; + +// Size of Common and Stage-specific Members +static const int kInstStageOutCnt = kInstCommonOutCnt + 3; + +// Validation Error Code Offset +// +// This identifies the validation error. It also helps to identify +// how many words follow in the record and their meaning. +static const int kInstValidationOutError = kInstStageOutCnt; + +// Validation-specific Output Record Offsets +// +// Each different validation will generate a potentially different +// number of words at the end of the record giving more specifics +// about the validation error. +// +// A bindless bounds error will output the index and the bound. +static const int kInstBindlessBoundsOutDescIndex = kInstStageOutCnt + 1; +static const int kInstBindlessBoundsOutDescBound = kInstStageOutCnt + 2; +static const int kInstBindlessBoundsOutUnused = kInstStageOutCnt + 3; +static const int kInstBindlessBoundsOutCnt = kInstStageOutCnt + 4; + +// A descriptor uninitialized error will output the index. +static const int kInstBindlessUninitOutDescIndex = kInstStageOutCnt + 1; +static const int kInstBindlessUninitOutUnused = kInstStageOutCnt + 2; +static const int kInstBindlessUninitOutUnused2 = kInstStageOutCnt + 3; +static const int kInstBindlessUninitOutCnt = kInstStageOutCnt + 4; + +// A buffer out-of-bounds error will output the descriptor +// index, the buffer offset and the buffer size +static const int kInstBindlessBuffOOBOutDescIndex = kInstStageOutCnt + 1; +static const int kInstBindlessBuffOOBOutBuffOff = kInstStageOutCnt + 2; +static const int kInstBindlessBuffOOBOutBuffSize = kInstStageOutCnt + 3; +static const int kInstBindlessBuffOOBOutCnt = kInstStageOutCnt + 4; + +// A buffer address unalloc error will output the 64-bit pointer in +// two 32-bit pieces, lower bits first. +static const int kInstBuffAddrUnallocOutDescPtrLo = kInstStageOutCnt + 1; +static const int kInstBuffAddrUnallocOutDescPtrHi = kInstStageOutCnt + 2; +static const int kInstBuffAddrUnallocOutCnt = kInstStageOutCnt + 3; + +// Maximum Output Record Member Count +static const int kInstMaxOutCnt = kInstStageOutCnt + 4; + +// Validation Error Codes +// +// These are the possible validation error codes. +static const int kInstErrorBindlessBounds = 0; +static const int kInstErrorBindlessUninit = 1; +static const int kInstErrorBuffAddrUnallocRef = 2; +static const int kInstErrorBindlessBuffOOB = 3; + +// Direct Input Buffer Offsets +// +// The following values provide member offsets into the input buffers +// consumed by InstrumentPass::GenDebugDirectRead(). This method is utilized +// by InstBindlessCheckPass. +// +// The only object in an input buffer is a runtime array of unsigned +// integers. Each validation will have its own formatting of this array. +static const int kDebugInputDataOffset = 0; + +// Debug Buffer Bindings +// +// These are the bindings for the different buffers which are +// read or written by the instrumentation passes. +// +// This is the output buffer written by InstBindlessCheckPass, +// InstBuffAddrCheckPass, and possibly other future validations. +static const int kDebugOutputBindingStream = 0; + +// The binding for the input buffer read by InstBindlessCheckPass. +static const int kDebugInputBindingBindless = 1; + +// The binding for the input buffer read by InstBuffAddrCheckPass. +static const int kDebugInputBindingBuffAddr = 2; + +// This is the output buffer written by InstDebugPrintfPass. +static const int kDebugOutputPrintfStream = 3; + +// Bindless Validation Input Buffer Format +// +// An input buffer for bindless validation consists of a single array of +// unsigned integers we will call Data[]. This array is formatted as follows. +// +// At offset kDebugInputBindlessInitOffset in Data[] is a single uint which +// gives an offset to the start of the bindless initialization data. More +// specifically, if the following value is zero, we know that the descriptor at +// (set = s, binding = b, index = i) is not initialized; if the value is +// non-zero, and the descriptor points to a buffer, the value is the length of +// the buffer in bytes and can be used to check for out-of-bounds buffer +// references: +// Data[ i + Data[ b + Data[ s + Data[ kDebugInputBindlessInitOffset ] ] ] ] +static const int kDebugInputBindlessInitOffset = 0; + +// At offset kDebugInputBindlessOffsetLengths is some number of uints which +// provide the bindless length data. More specifically, the number of +// descriptors at (set=s, binding=b) is: +// Data[ Data[ s + kDebugInputBindlessOffsetLengths ] + b ] +static const int kDebugInputBindlessOffsetLengths = 1; + +// Buffer Device Address Input Buffer Format +// +// An input buffer for buffer device address validation consists of a single +// array of unsigned 64-bit integers we will call Data[]. This array is +// formatted as follows: +// +// At offset kDebugInputBuffAddrPtrOffset is a list of sorted valid buffer +// addresses. The list is terminated with the address 0xffffffffffffffff. +// If 0x0 is not a valid buffer address, this address is inserted at the +// start of the list. +// +static const int kDebugInputBuffAddrPtrOffset = 1; +// +// At offset kDebugInputBuffAddrLengthOffset in Data[] is a single uint64 which +// gives an offset to the start of the buffer length data. More +// specifically, for a buffer whose pointer is located at input buffer offset +// i, the length is located at: +// +// Data[ i - kDebugInputBuffAddrPtrOffset +// + Data[ kDebugInputBuffAddrLengthOffset ] ] +// +// The length associated with the 0xffffffffffffffff address is zero. If +// not a valid buffer, the length associated with the 0x0 address is zero. +static const int kDebugInputBuffAddrLengthOffset = 0; + +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_INSTRUMENT_HPP_ diff --git a/third_party/spirv-tools/include/spirv-tools/libspirv.h b/third_party/spirv-tools/include/spirv-tools/libspirv.h new file mode 100644 index 0000000..a0114c3 --- /dev/null +++ b/third_party/spirv-tools/include/spirv-tools/libspirv.h @@ -0,0 +1,858 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_LIBSPIRV_H_ +#define INCLUDE_SPIRV_TOOLS_LIBSPIRV_H_ + +#ifdef __cplusplus +extern "C" { +#else +#include +#endif + +#include +#include + +#if defined(SPIRV_TOOLS_SHAREDLIB) +#if defined(_WIN32) +#if defined(SPIRV_TOOLS_IMPLEMENTATION) +#define SPIRV_TOOLS_EXPORT __declspec(dllexport) +#else +#define SPIRV_TOOLS_EXPORT __declspec(dllimport) +#endif +#else +#if defined(SPIRV_TOOLS_IMPLEMENTATION) +#define SPIRV_TOOLS_EXPORT __attribute__((visibility("default"))) +#else +#define SPIRV_TOOLS_EXPORT +#endif +#endif +#else +#define SPIRV_TOOLS_EXPORT +#endif + +// Helpers + +#define SPV_BIT(shift) (1 << (shift)) + +#define SPV_FORCE_16_BIT_ENUM(name) SPV_FORCE_16BIT_##name = 0x7fff +#define SPV_FORCE_32_BIT_ENUM(name) SPV_FORCE_32BIT_##name = 0x7fffffff + +// Enumerations + +typedef enum spv_result_t { + SPV_SUCCESS = 0, + SPV_UNSUPPORTED = 1, + SPV_END_OF_STREAM = 2, + SPV_WARNING = 3, + SPV_FAILED_MATCH = 4, + SPV_REQUESTED_TERMINATION = 5, // Success, but signals early termination. + SPV_ERROR_INTERNAL = -1, + SPV_ERROR_OUT_OF_MEMORY = -2, + SPV_ERROR_INVALID_POINTER = -3, + SPV_ERROR_INVALID_BINARY = -4, + SPV_ERROR_INVALID_TEXT = -5, + SPV_ERROR_INVALID_TABLE = -6, + SPV_ERROR_INVALID_VALUE = -7, + SPV_ERROR_INVALID_DIAGNOSTIC = -8, + SPV_ERROR_INVALID_LOOKUP = -9, + SPV_ERROR_INVALID_ID = -10, + SPV_ERROR_INVALID_CFG = -11, + SPV_ERROR_INVALID_LAYOUT = -12, + SPV_ERROR_INVALID_CAPABILITY = -13, + SPV_ERROR_INVALID_DATA = -14, // Indicates data rules validation failure. + SPV_ERROR_MISSING_EXTENSION = -15, + SPV_ERROR_WRONG_VERSION = -16, // Indicates wrong SPIR-V version + SPV_FORCE_32_BIT_ENUM(spv_result_t) +} spv_result_t; + +// Severity levels of messages communicated to the consumer. +typedef enum spv_message_level_t { + SPV_MSG_FATAL, // Unrecoverable error due to environment. + // Will exit the program immediately. E.g., + // out of memory. + SPV_MSG_INTERNAL_ERROR, // Unrecoverable error due to SPIRV-Tools + // internals. + // Will exit the program immediately. E.g., + // unimplemented feature. + SPV_MSG_ERROR, // Normal error due to user input. + SPV_MSG_WARNING, // Warning information. + SPV_MSG_INFO, // General information. + SPV_MSG_DEBUG, // Debug information. +} spv_message_level_t; + +typedef enum spv_endianness_t { + SPV_ENDIANNESS_LITTLE, + SPV_ENDIANNESS_BIG, + SPV_FORCE_32_BIT_ENUM(spv_endianness_t) +} spv_endianness_t; + +// The kinds of operands that an instruction may have. +// +// Some operand types are "concrete". The binary parser uses a concrete +// operand type to describe an operand of a parsed instruction. +// +// The assembler uses all operand types. In addition to determining what +// kind of value an operand may be, non-concrete operand types capture the +// fact that an operand might be optional (may be absent, or present exactly +// once), or might occur zero or more times. +// +// Sometimes we also need to be able to express the fact that an operand +// is a member of an optional tuple of values. In that case the first member +// would be optional, and the subsequent members would be required. +typedef enum spv_operand_type_t { + // A sentinel value. + SPV_OPERAND_TYPE_NONE = 0, + + // Set 1: Operands that are IDs. + SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_TYPE_ID, + SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, // SPIR-V Sec 3.25 + SPV_OPERAND_TYPE_SCOPE_ID, // SPIR-V Sec 3.27 + + // Set 2: Operands that are literal numbers. + SPV_OPERAND_TYPE_LITERAL_INTEGER, // Always unsigned 32-bits. + // The Instruction argument to OpExtInst. It's an unsigned 32-bit literal + // number indicating which instruction to use from an extended instruction + // set. + SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + // The Opcode argument to OpSpecConstantOp. It determines the operation + // to be performed on constant operands to compute a specialization constant + // result. + SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, + // A literal number whose format and size are determined by a previous operand + // in the same instruction. It's a signed integer, an unsigned integer, or a + // floating point number. It also has a specified bit width. The width + // may be larger than 32, which would require such a typed literal value to + // occupy multiple SPIR-V words. + SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + + // Set 3: The literal string operand type. + SPV_OPERAND_TYPE_LITERAL_STRING, + + // Set 4: Operands that are a single word enumerated value. + SPV_OPERAND_TYPE_SOURCE_LANGUAGE, // SPIR-V Sec 3.2 + SPV_OPERAND_TYPE_EXECUTION_MODEL, // SPIR-V Sec 3.3 + SPV_OPERAND_TYPE_ADDRESSING_MODEL, // SPIR-V Sec 3.4 + SPV_OPERAND_TYPE_MEMORY_MODEL, // SPIR-V Sec 3.5 + SPV_OPERAND_TYPE_EXECUTION_MODE, // SPIR-V Sec 3.6 + SPV_OPERAND_TYPE_STORAGE_CLASS, // SPIR-V Sec 3.7 + SPV_OPERAND_TYPE_DIMENSIONALITY, // SPIR-V Sec 3.8 + SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE, // SPIR-V Sec 3.9 + SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE, // SPIR-V Sec 3.10 + SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT, // SPIR-V Sec 3.11 + SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER, // SPIR-V Sec 3.12 + SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE, // SPIR-V Sec 3.13 + SPV_OPERAND_TYPE_FP_ROUNDING_MODE, // SPIR-V Sec 3.16 + SPV_OPERAND_TYPE_LINKAGE_TYPE, // SPIR-V Sec 3.17 + SPV_OPERAND_TYPE_ACCESS_QUALIFIER, // SPIR-V Sec 3.18 + SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE, // SPIR-V Sec 3.19 + SPV_OPERAND_TYPE_DECORATION, // SPIR-V Sec 3.20 + SPV_OPERAND_TYPE_BUILT_IN, // SPIR-V Sec 3.21 + SPV_OPERAND_TYPE_GROUP_OPERATION, // SPIR-V Sec 3.28 + SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS, // SPIR-V Sec 3.29 + SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO, // SPIR-V Sec 3.30 + SPV_OPERAND_TYPE_CAPABILITY, // SPIR-V Sec 3.31 + SPV_OPERAND_TYPE_RAY_FLAGS, // SPIR-V Sec 3.RF + SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION, // SPIR-V Sec 3.RQIntersection + SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCommitted + SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE, // SPIR-V Sec + // 3.RQCandidate + + // Set 5: Operands that are a single word bitmask. + // Sometimes a set bit indicates the instruction requires still more operands. + SPV_OPERAND_TYPE_IMAGE, // SPIR-V Sec 3.14 + SPV_OPERAND_TYPE_FP_FAST_MATH_MODE, // SPIR-V Sec 3.15 + SPV_OPERAND_TYPE_SELECTION_CONTROL, // SPIR-V Sec 3.22 + SPV_OPERAND_TYPE_LOOP_CONTROL, // SPIR-V Sec 3.23 + SPV_OPERAND_TYPE_FUNCTION_CONTROL, // SPIR-V Sec 3.24 + SPV_OPERAND_TYPE_MEMORY_ACCESS, // SPIR-V Sec 3.26 + SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE, // SPIR-V Sec 3.FSR + +// The remaining operand types are only used internally by the assembler. +// There are two categories: +// Optional : expands to 0 or 1 operand, like ? in regular expressions. +// Variable : expands to 0, 1 or many operands or pairs of operands. +// This is similar to * in regular expressions. + +// NOTE: These FIRST_* and LAST_* enum values are DEPRECATED. +// The concept of "optional" and "variable" operand types are only intended +// for use as an implementation detail of parsing SPIR-V, either in text or +// binary form. Instead of using enum ranges, use characteristic function +// spvOperandIsConcrete. +// The use of enum value ranges in a public API makes it difficult to insert +// new values into a range without also breaking binary compatibility. +// +// Macros for defining bounds on optional and variable operand types. +// Any variable operand type is also optional. +// TODO(dneto): Remove SPV_OPERAND_TYPE_FIRST_* and SPV_OPERAND_TYPE_LAST_* +#define FIRST_OPTIONAL(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_OPTIONAL_TYPE = ENUM +#define FIRST_VARIABLE(ENUM) ENUM, SPV_OPERAND_TYPE_FIRST_VARIABLE_TYPE = ENUM +#define LAST_VARIABLE(ENUM) \ + ENUM, SPV_OPERAND_TYPE_LAST_VARIABLE_TYPE = ENUM, \ + SPV_OPERAND_TYPE_LAST_OPTIONAL_TYPE = ENUM + + // An optional operand represents zero or one logical operands. + // In an instruction definition, this may only appear at the end of the + // operand types. + FIRST_OPTIONAL(SPV_OPERAND_TYPE_OPTIONAL_ID), + // An optional image operand type. + SPV_OPERAND_TYPE_OPTIONAL_IMAGE, + // An optional memory access type. + SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, + // An optional literal integer. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, + // An optional literal number, which may be either integer or floating point. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER, + // Like SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, but optional, and integral. + SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER, + // An optional literal string. + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING, + // An optional access qualifier + SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER, + // An optional context-independent value, or CIV. CIVs are tokens that we can + // assemble regardless of where they occur -- literals, IDs, immediate + // integers, etc. + SPV_OPERAND_TYPE_OPTIONAL_CIV, + + // A variable operand represents zero or more logical operands. + // In an instruction definition, this may only appear at the end of the + // operand types. + FIRST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID), + SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER, + // A sequence of zero or more pairs of (typed literal integer, Id). + // Expands to zero or more: + // (SPV_OPERAND_TYPE_TYPED_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID) + // where the literal number must always be an integer of some sort. + SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID, + // A sequence of zero or more pairs of (Id, Literal integer) + LAST_VARIABLE(SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER), + + // The following are concrete enum types from the DebugInfo extended + // instruction set. + SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS, // DebugInfo Sec 3.2. A mask. + SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // DebugInfo Sec 3.3 + SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE, // DebugInfo Sec 3.4 + SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER, // DebugInfo Sec 3.5 + SPV_OPERAND_TYPE_DEBUG_OPERATION, // DebugInfo Sec 3.6 + + // The following are concrete enum types from the OpenCL.DebugInfo.100 + // extended instruction set. + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS, // Sec 3.2. A Mask + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING, // Sec 3.3 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE, // Sec 3.4 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER, // Sec 3.5 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, // Sec 3.6 + SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY, // Sec 3.7 + + // This is a sentinel value, and does not represent an operand type. + // It should come last. + SPV_OPERAND_TYPE_NUM_OPERAND_TYPES, + + SPV_FORCE_32_BIT_ENUM(spv_operand_type_t) +} spv_operand_type_t; + +// Returns true if the given type is concrete. +bool spvOperandIsConcrete(spv_operand_type_t type); + +// Returns true if the given type is concrete and also a mask. +bool spvOperandIsConcreteMask(spv_operand_type_t type); + +typedef enum spv_ext_inst_type_t { + SPV_EXT_INST_TYPE_NONE = 0, + SPV_EXT_INST_TYPE_GLSL_STD_450, + SPV_EXT_INST_TYPE_OPENCL_STD, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX, + SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, + SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, + SPV_EXT_INST_TYPE_DEBUGINFO, + SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + + // Multiple distinct extended instruction set types could return this + // value, if they are prefixed with NonSemantic. and are otherwise + // unrecognised + SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN, + + SPV_FORCE_32_BIT_ENUM(spv_ext_inst_type_t) +} spv_ext_inst_type_t; + +// This determines at a high level the kind of a binary-encoded literal +// number, but not the bit width. +// In principle, these could probably be folded into new entries in +// spv_operand_type_t. But then we'd have some special case differences +// between the assembler and disassembler. +typedef enum spv_number_kind_t { + SPV_NUMBER_NONE = 0, // The default for value initialization. + SPV_NUMBER_UNSIGNED_INT, + SPV_NUMBER_SIGNED_INT, + SPV_NUMBER_FLOATING, +} spv_number_kind_t; + +typedef enum spv_text_to_binary_options_t { + SPV_TEXT_TO_BINARY_OPTION_NONE = SPV_BIT(0), + // Numeric IDs in the binary will have the same values as in the source. + // Non-numeric IDs are allocated by filling in the gaps, starting with 1 + // and going up. + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS = SPV_BIT(1), + SPV_FORCE_32_BIT_ENUM(spv_text_to_binary_options_t) +} spv_text_to_binary_options_t; + +typedef enum spv_binary_to_text_options_t { + SPV_BINARY_TO_TEXT_OPTION_NONE = SPV_BIT(0), + SPV_BINARY_TO_TEXT_OPTION_PRINT = SPV_BIT(1), + SPV_BINARY_TO_TEXT_OPTION_COLOR = SPV_BIT(2), + SPV_BINARY_TO_TEXT_OPTION_INDENT = SPV_BIT(3), + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET = SPV_BIT(4), + // Do not output the module header as leading comments in the assembly. + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER = SPV_BIT(5), + // Use friendly names where possible. The heuristic may expand over + // time, but will use common names for scalar types, and debug names from + // OpName instructions. + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES = SPV_BIT(6), + // Add some comments to the generated assembly + SPV_BINARY_TO_TEXT_OPTION_COMMENT = SPV_BIT(7), + SPV_FORCE_32_BIT_ENUM(spv_binary_to_text_options_t) +} spv_binary_to_text_options_t; + +// Constants + +// The default id bound is to the minimum value for the id limit +// in the spir-v specification under the section "Universal Limits". +const uint32_t kDefaultMaxIdBound = 0x3FFFFF; + +// Structures + +// Information about an operand parsed from a binary SPIR-V module. +// Note that the values are not included. You still need access to the binary +// to extract the values. +typedef struct spv_parsed_operand_t { + // Location of the operand, in words from the start of the instruction. + uint16_t offset; + // Number of words occupied by this operand. + uint16_t num_words; + // The "concrete" operand type. See the definition of spv_operand_type_t + // for details. + spv_operand_type_t type; + // If type is a literal number type, then number_kind says whether it's + // a signed integer, an unsigned integer, or a floating point number. + spv_number_kind_t number_kind; + // The number of bits for a literal number type. + uint32_t number_bit_width; +} spv_parsed_operand_t; + +// An instruction parsed from a binary SPIR-V module. +typedef struct spv_parsed_instruction_t { + // An array of words for this instruction, in native endianness. + const uint32_t* words; + // The number of words in this instruction. + uint16_t num_words; + uint16_t opcode; + // The extended instruction type, if opcode is OpExtInst. Otherwise + // this is the "none" value. + spv_ext_inst_type_t ext_inst_type; + // The type id, or 0 if this instruction doesn't have one. + uint32_t type_id; + // The result id, or 0 if this instruction doesn't have one. + uint32_t result_id; + // The array of parsed operands. + const spv_parsed_operand_t* operands; + uint16_t num_operands; +} spv_parsed_instruction_t; + +typedef struct spv_const_binary_t { + const uint32_t* code; + const size_t wordCount; +} spv_const_binary_t; + +typedef struct spv_binary_t { + uint32_t* code; + size_t wordCount; +} spv_binary_t; + +typedef struct spv_text_t { + const char* str; + size_t length; +} spv_text_t; + +typedef struct spv_position_t { + size_t line; + size_t column; + size_t index; +} spv_position_t; + +typedef struct spv_diagnostic_t { + spv_position_t position; + char* error; + bool isTextSource; +} spv_diagnostic_t; + +// Opaque struct containing the context used to operate on a SPIR-V module. +// Its object is used by various translation API functions. +typedef struct spv_context_t spv_context_t; + +typedef struct spv_validator_options_t spv_validator_options_t; + +typedef struct spv_optimizer_options_t spv_optimizer_options_t; + +typedef struct spv_reducer_options_t spv_reducer_options_t; + +typedef struct spv_fuzzer_options_t spv_fuzzer_options_t; + +// Type Definitions + +typedef spv_const_binary_t* spv_const_binary; +typedef spv_binary_t* spv_binary; +typedef spv_text_t* spv_text; +typedef spv_position_t* spv_position; +typedef spv_diagnostic_t* spv_diagnostic; +typedef const spv_context_t* spv_const_context; +typedef spv_context_t* spv_context; +typedef spv_validator_options_t* spv_validator_options; +typedef const spv_validator_options_t* spv_const_validator_options; +typedef spv_optimizer_options_t* spv_optimizer_options; +typedef const spv_optimizer_options_t* spv_const_optimizer_options; +typedef spv_reducer_options_t* spv_reducer_options; +typedef const spv_reducer_options_t* spv_const_reducer_options; +typedef spv_fuzzer_options_t* spv_fuzzer_options; +typedef const spv_fuzzer_options_t* spv_const_fuzzer_options; + +// Platform API + +// Returns the SPIRV-Tools software version as a null-terminated string. +// The contents of the underlying storage is valid for the remainder of +// the process. +SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionString(void); +// Returns a null-terminated string containing the name of the project, +// the software version string, and commit details. +// The contents of the underlying storage is valid for the remainder of +// the process. +SPIRV_TOOLS_EXPORT const char* spvSoftwareVersionDetailsString(void); + +// Certain target environments impose additional restrictions on SPIR-V, so it's +// often necessary to specify which one applies. SPV_ENV_UNIVERSAL_* implies an +// environment-agnostic SPIR-V. +// +// When an API method needs to derive a SPIR-V version from a target environment +// (from the spv_context object), the method will choose the highest version of +// SPIR-V supported by the target environment. Examples: +// SPV_ENV_VULKAN_1_0 -> SPIR-V 1.0 +// SPV_ENV_VULKAN_1_1 -> SPIR-V 1.3 +// SPV_ENV_VULKAN_1_1_SPIRV_1_4 -> SPIR-V 1.4 +// SPV_ENV_VULKAN_1_2 -> SPIR-V 1.5 +// Consult the description of API entry points for specific rules. +typedef enum { + SPV_ENV_UNIVERSAL_1_0, // SPIR-V 1.0 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_0, // Vulkan 1.0 latest revision. + SPV_ENV_UNIVERSAL_1_1, // SPIR-V 1.1 latest revision, no other restrictions. + SPV_ENV_OPENCL_2_1, // OpenCL Full Profile 2.1 latest revision. + SPV_ENV_OPENCL_2_2, // OpenCL Full Profile 2.2 latest revision. + SPV_ENV_OPENGL_4_0, // OpenGL 4.0 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_1, // OpenGL 4.1 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_2, // OpenGL 4.2 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_OPENGL_4_3, // OpenGL 4.3 plus GL_ARB_gl_spirv, latest revisions. + // There is no variant for OpenGL 4.4. + SPV_ENV_OPENGL_4_5, // OpenGL 4.5 plus GL_ARB_gl_spirv, latest revisions. + SPV_ENV_UNIVERSAL_1_2, // SPIR-V 1.2, latest revision, no other restrictions. + SPV_ENV_OPENCL_1_2, // OpenCL Full Profile 1.2 plus cl_khr_il_program, + // latest revision. + SPV_ENV_OPENCL_EMBEDDED_1_2, // OpenCL Embedded Profile 1.2 plus + // cl_khr_il_program, latest revision. + SPV_ENV_OPENCL_2_0, // OpenCL Full Profile 2.0 plus cl_khr_il_program, + // latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_0, // OpenCL Embedded Profile 2.0 plus + // cl_khr_il_program, latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_1, // OpenCL Embedded Profile 2.1 latest revision. + SPV_ENV_OPENCL_EMBEDDED_2_2, // OpenCL Embedded Profile 2.2 latest revision. + SPV_ENV_UNIVERSAL_1_3, // SPIR-V 1.3 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_1, // Vulkan 1.1 latest revision. + SPV_ENV_WEBGPU_0, // Work in progress WebGPU 1.0. + SPV_ENV_UNIVERSAL_1_4, // SPIR-V 1.4 latest revision, no other restrictions. + + // Vulkan 1.1 with VK_KHR_spirv_1_4, i.e. SPIR-V 1.4 binary. + SPV_ENV_VULKAN_1_1_SPIRV_1_4, + + SPV_ENV_UNIVERSAL_1_5, // SPIR-V 1.5 latest revision, no other restrictions. + SPV_ENV_VULKAN_1_2, // Vulkan 1.2 latest revision. +} spv_target_env; + +// SPIR-V Validator can be parameterized with the following Universal Limits. +typedef enum { + spv_validator_limit_max_struct_members, + spv_validator_limit_max_struct_depth, + spv_validator_limit_max_local_variables, + spv_validator_limit_max_global_variables, + spv_validator_limit_max_switch_branches, + spv_validator_limit_max_function_args, + spv_validator_limit_max_control_flow_nesting_depth, + spv_validator_limit_max_access_chain_indexes, + spv_validator_limit_max_id_bound, +} spv_validator_limit; + +// Returns a string describing the given SPIR-V target environment. +SPIRV_TOOLS_EXPORT const char* spvTargetEnvDescription(spv_target_env env); + +// Parses s into *env and returns true if successful. If unparsable, returns +// false and sets *env to SPV_ENV_UNIVERSAL_1_0. +SPIRV_TOOLS_EXPORT bool spvParseTargetEnv(const char* s, spv_target_env* env); + +// Determines the target env value with the least features but which enables +// the given Vulkan and SPIR-V versions. If such a target is supported, returns +// true and writes the value to |env|, otherwise returns false. +// +// The Vulkan version is given as an unsigned 32-bit number as specified in +// Vulkan section "29.2.1 Version Numbers": the major version number appears +// in bits 22 to 21, and the minor version is in bits 12 to 21. The SPIR-V +// version is given in the SPIR-V version header word: major version in bits +// 16 to 23, and minor version in bits 8 to 15. +SPIRV_TOOLS_EXPORT bool spvParseVulkanEnv(uint32_t vulkan_ver, + uint32_t spirv_ver, + spv_target_env* env); + +// Creates a context object for most of the SPIRV-Tools API. +// Returns null if env is invalid. +// +// See specific API calls for how the target environment is interpeted +// (particularly assembly and validation). +SPIRV_TOOLS_EXPORT spv_context spvContextCreate(spv_target_env env); + +// Destroys the given context object. +SPIRV_TOOLS_EXPORT void spvContextDestroy(spv_context context); + +// Creates a Validator options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// spvValidatorOptionsDestroy. +SPIRV_TOOLS_EXPORT spv_validator_options spvValidatorOptionsCreate(void); + +// Destroys the given Validator options object. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsDestroy( + spv_validator_options options); + +// Records the maximum Universal Limit that is considered valid in the given +// Validator options object. argument must be a valid options object. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniversalLimit( + spv_validator_options options, spv_validator_limit limit_type, + uint32_t limit); + +// Record whether or not the validator should relax the rules on types for +// stores to structs. When relaxed, it will allow a type mismatch as long as +// the types are structs with the same layout. Two structs have the same layout +// if +// +// 1) the members of the structs are either the same type or are structs with +// same layout, and +// +// 2) the decorations that affect the memory layout are identical for both +// types. Other decorations are not relevant. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxStoreStruct( + spv_validator_options options, bool val); + +// Records whether or not the validator should relax the rules on pointer usage +// in logical addressing mode. +// +// When relaxed, it will allow the following usage cases of pointers: +// 1) OpVariable allocating an object whose type is a pointer type +// 2) OpReturnValue returning a pointer value +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxLogicalPointer( + spv_validator_options options, bool val); + +// Records whether or not the validator should relax the rules because it is +// expected that the optimizations will make the code legal. +// +// When relaxed, it will allow the following: +// 1) It will allow relaxed logical pointers. Setting this option will also +// set that option. +// 2) Pointers that are pass as parameters to function calls do not have to +// match the storage class of the formal parameter. +// 3) Pointers that are actaul parameters on function calls do not have to point +// to the same type pointed as the formal parameter. The types just need to +// logically match. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetBeforeHlslLegalization( + spv_validator_options options, bool val); + +// Records whether the validator should use "relaxed" block layout rules. +// Relaxed layout rules are described by Vulkan extension +// VK_KHR_relaxed_block_layout, and they affect uniform blocks, storage blocks, +// and push constants. +// +// This is enabled by default when targeting Vulkan 1.1 or later. +// Relaxed layout is more permissive than the default rules in Vulkan 1.0. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetRelaxBlockLayout( + spv_validator_options options, bool val); + +// Records whether the validator should use standard block layout rules for +// uniform blocks. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout( + spv_validator_options options, bool val); + +// Records whether the validator should use "scalar" block layout rules. +// Scalar layout rules are more permissive than relaxed block layout. +// +// See Vulkan extnesion VK_EXT_scalar_block_layout. The scalar alignment is +// defined as follows: +// - scalar alignment of a scalar is the scalar size +// - scalar alignment of a vector is the scalar alignment of its component +// - scalar alignment of a matrix is the scalar alignment of its component +// - scalar alignment of an array is the scalar alignment of its element +// - scalar alignment of a struct is the max scalar alignment among its +// members +// +// For a struct in Uniform, StorageClass, or PushConstant: +// - a member Offset must be a multiple of the member's scalar alignment +// - ArrayStride or MatrixStride must be a multiple of the array or matrix +// scalar alignment +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetScalarBlockLayout( + spv_validator_options options, bool val); + +// Records whether or not the validator should skip validating standard +// uniform/storage block layout. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout( + spv_validator_options options, bool val); + +// Creates an optimizer options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// |spvOptimizerOptionsDestroy|. +SPIRV_TOOLS_EXPORT spv_optimizer_options spvOptimizerOptionsCreate(void); + +// Destroys the given optimizer options object. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsDestroy( + spv_optimizer_options options); + +// Records whether or not the optimizer should run the validator before +// optimizing. If |val| is true, the validator will be run. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetRunValidator( + spv_optimizer_options options, bool val); + +// Records the validator options that should be passed to the validator if it is +// run. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetValidatorOptions( + spv_optimizer_options options, spv_validator_options val); + +// Records the maximum possible value for the id bound. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetMaxIdBound( + spv_optimizer_options options, uint32_t val); + +// Records whether all bindings within the module should be preserved. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveBindings( + spv_optimizer_options options, bool val); + +// Records whether all specialization constants within the module +// should be preserved. +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveSpecConstants( + spv_optimizer_options options, bool val); + +// Creates a reducer options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// |spvReducerOptionsDestroy|. +SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate(); + +// Destroys the given reducer options object. +SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options); + +// Sets the maximum number of reduction steps that should run before the reducer +// gives up. +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit( + spv_reducer_options options, uint32_t step_limit); + +// Sets the fail-on-validation-error option; if true, the reducer will return +// kStateInvalid if a reduction step yields a state that fails SPIR-V +// validation. Otherwise, an invalid state is treated as uninteresting and the +// reduction backtracks and continues. +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError( + spv_reducer_options options, bool fail_on_validation_error); + +// Sets the function that the reducer should target. If set to zero the reducer +// will target all functions as well as parts of the module that lie outside +// functions. Otherwise the reducer will restrict reduction to the function +// with result id |target_function|, which is required to exist. +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetTargetFunction( + spv_reducer_options options, uint32_t target_function); + +// Creates a fuzzer options object with default options. Returns a valid +// options object. The object remains valid until it is passed into +// |spvFuzzerOptionsDestroy|. +SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate(); + +// Destroys the given fuzzer options object. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options); + +// Enables running the validator after every transformation is applied during +// a replay. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation( + spv_fuzzer_options options); + +// Sets the seed with which the random number generator used by the fuzzer +// should be initialized. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( + spv_fuzzer_options options, uint32_t seed); + +// Sets the range of transformations that should be applied during replay: 0 +// means all transformations, +N means the first N transformations, -N means all +// except the final N transformations. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange( + spv_fuzzer_options options, int32_t replay_range); + +// Sets the maximum number of steps that the shrinker should take before giving +// up. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( + spv_fuzzer_options options, uint32_t shrinker_step_limit); + +// Enables running the validator after every pass is applied during a fuzzing +// run. +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation( + spv_fuzzer_options options); + +// Enables all fuzzer passes during a fuzzing run (instead of a random subset +// of passes). +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableAllPasses( + spv_fuzzer_options options); + +// Encodes the given SPIR-V assembly text to its binary representation. The +// length parameter specifies the number of bytes for text. Encoded binary will +// be stored into *binary. Any error will be written into *diagnostic if +// diagnostic is non-null, otherwise the context's message consumer will be +// used. The generated binary is independent of the context and may outlive it. +// The SPIR-V binary version is set to the highest version of SPIR-V supported +// by the context's target environment. +SPIRV_TOOLS_EXPORT spv_result_t spvTextToBinary(const spv_const_context context, + const char* text, + const size_t length, + spv_binary* binary, + spv_diagnostic* diagnostic); + +// Encodes the given SPIR-V assembly text to its binary representation. Same as +// spvTextToBinary but with options. The options parameter is a bit field of +// spv_text_to_binary_options_t. +SPIRV_TOOLS_EXPORT spv_result_t spvTextToBinaryWithOptions( + const spv_const_context context, const char* text, const size_t length, + const uint32_t options, spv_binary* binary, spv_diagnostic* diagnostic); + +// Frees an allocated text stream. This is a no-op if the text parameter +// is a null pointer. +SPIRV_TOOLS_EXPORT void spvTextDestroy(spv_text text); + +// Decodes the given SPIR-V binary representation to its assembly text. The +// word_count parameter specifies the number of words for binary. The options +// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will +// be stored into *text. Any error will be written into *diagnostic if +// diagnostic is non-null, otherwise the context's message consumer will be +// used. +SPIRV_TOOLS_EXPORT spv_result_t spvBinaryToText(const spv_const_context context, + const uint32_t* binary, + const size_t word_count, + const uint32_t options, + spv_text* text, + spv_diagnostic* diagnostic); + +// Frees a binary stream from memory. This is a no-op if binary is a null +// pointer. +SPIRV_TOOLS_EXPORT void spvBinaryDestroy(spv_binary binary); + +// Validates a SPIR-V binary for correctness. Any errors will be written into +// *diagnostic if diagnostic is non-null, otherwise the context's message +// consumer will be used. +// +// Validate for SPIR-V spec rules for the SPIR-V version named in the +// binary's header (at word offset 1). Additionally, if the context target +// environment is a client API (such as Vulkan 1.1), then validate for that +// client API version, to the extent that it is verifiable from data in the +// binary itself. +SPIRV_TOOLS_EXPORT spv_result_t spvValidate(const spv_const_context context, + const spv_const_binary binary, + spv_diagnostic* diagnostic); + +// Validates a SPIR-V binary for correctness. Uses the provided Validator +// options. Any errors will be written into *diagnostic if diagnostic is +// non-null, otherwise the context's message consumer will be used. +// +// Validate for SPIR-V spec rules for the SPIR-V version named in the +// binary's header (at word offset 1). Additionally, if the context target +// environment is a client API (such as Vulkan 1.1), then validate for that +// client API version, to the extent that it is verifiable from data in the +// binary itself, or in the validator options. +SPIRV_TOOLS_EXPORT spv_result_t spvValidateWithOptions( + const spv_const_context context, const spv_const_validator_options options, + const spv_const_binary binary, spv_diagnostic* diagnostic); + +// Validates a raw SPIR-V binary for correctness. Any errors will be written +// into *diagnostic if diagnostic is non-null, otherwise the context's message +// consumer will be used. +SPIRV_TOOLS_EXPORT spv_result_t +spvValidateBinary(const spv_const_context context, const uint32_t* words, + const size_t num_words, spv_diagnostic* diagnostic); + +// Creates a diagnostic object. The position parameter specifies the location in +// the text/binary stream. The message parameter, copied into the diagnostic +// object, contains the error message to display. +SPIRV_TOOLS_EXPORT spv_diagnostic +spvDiagnosticCreate(const spv_position position, const char* message); + +// Destroys a diagnostic object. This is a no-op if diagnostic is a null +// pointer. +SPIRV_TOOLS_EXPORT void spvDiagnosticDestroy(spv_diagnostic diagnostic); + +// Prints the diagnostic to stderr. +SPIRV_TOOLS_EXPORT spv_result_t +spvDiagnosticPrint(const spv_diagnostic diagnostic); + +// Gets the name of an instruction, without the "Op" prefix. +SPIRV_TOOLS_EXPORT const char* spvOpcodeString(const uint32_t opcode); + +// The binary parser interface. + +// A pointer to a function that accepts a parsed SPIR-V header. +// The integer arguments are the 32-bit words from the header, as specified +// in SPIR-V 1.0 Section 2.3 Table 1. +// The function should return SPV_SUCCESS if parsing should continue. +typedef spv_result_t (*spv_parsed_header_fn_t)( + void* user_data, spv_endianness_t endian, uint32_t magic, uint32_t version, + uint32_t generator, uint32_t id_bound, uint32_t reserved); + +// A pointer to a function that accepts a parsed SPIR-V instruction. +// The parsed_instruction value is transient: it may be overwritten +// or released immediately after the function has returned. That also +// applies to the words array member of the parsed instruction. The +// function should return SPV_SUCCESS if and only if parsing should +// continue. +typedef spv_result_t (*spv_parsed_instruction_fn_t)( + void* user_data, const spv_parsed_instruction_t* parsed_instruction); + +// Parses a SPIR-V binary, specified as counted sequence of 32-bit words. +// Parsing feedback is provided via two callbacks provided as function +// pointers. Each callback function pointer can be a null pointer, in +// which case it is never called. Otherwise, in a valid parse the +// parsed-header callback is called once, and then the parsed-instruction +// callback once for each instruction in the stream. The user_data parameter +// is supplied as context to the callbacks. Returns SPV_SUCCESS on successful +// parse where the callbacks always return SPV_SUCCESS. For an invalid parse, +// returns a status code other than SPV_SUCCESS, and if diagnostic is non-null +// also emits a diagnostic. If diagnostic is null the context's message consumer +// will be used to emit any errors. If a callback returns anything other than +// SPV_SUCCESS, then that status code is returned, no further callbacks are +// issued, and no additional diagnostics are emitted. +SPIRV_TOOLS_EXPORT spv_result_t spvBinaryParse( + const spv_const_context context, void* user_data, const uint32_t* words, + const size_t num_words, spv_parsed_header_fn_t parse_header, + spv_parsed_instruction_fn_t parse_instruction, spv_diagnostic* diagnostic); + +#ifdef __cplusplus +} +#endif + +#endif // INCLUDE_SPIRV_TOOLS_LIBSPIRV_H_ diff --git a/third_party/spirv-tools/include/spirv-tools/libspirv.hpp b/third_party/spirv-tools/include/spirv-tools/libspirv.hpp new file mode 100644 index 0000000..2018e46 --- /dev/null +++ b/third_party/spirv-tools/include/spirv-tools/libspirv.hpp @@ -0,0 +1,356 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ +#define INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ + +#include +#include +#include +#include + +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Message consumer. The C strings for source and message are only alive for the +// specific invocation. +using MessageConsumer = std::function; + +// C++ RAII wrapper around the C context object spv_context. +class Context { + public: + // Constructs a context targeting the given environment |env|. + // + // See specific API calls for how the target environment is interpeted + // (particularly assembly and validation). + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + explicit Context(spv_target_env env); + + // Enables move constructor/assignment operations. + Context(Context&& other); + Context& operator=(Context&& other); + + // Disables copy constructor/assignment operations. + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + // Destructs this instance. + ~Context(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns the underlying spv_context. + spv_context& CContext(); + const spv_context& CContext() const; + + private: + spv_context context_; +}; + +// A RAII wrapper around a validator options object. +class ValidatorOptions { + public: + ValidatorOptions() : options_(spvValidatorOptionsCreate()) {} + ~ValidatorOptions() { spvValidatorOptionsDestroy(options_); } + // Allow implicit conversion to the underlying object. + operator spv_validator_options() const { return options_; } + + // Sets a limit. + void SetUniversalLimit(spv_validator_limit limit_type, uint32_t limit) { + spvValidatorOptionsSetUniversalLimit(options_, limit_type, limit); + } + + void SetRelaxStructStore(bool val) { + spvValidatorOptionsSetRelaxStoreStruct(options_, val); + } + + // Enables VK_KHR_relaxed_block_layout when validating standard + // uniform/storage buffer/push-constant layout. If true, disables + // scalar block layout rules. + void SetRelaxBlockLayout(bool val) { + spvValidatorOptionsSetRelaxBlockLayout(options_, val); + } + + // Enables VK_KHR_uniform_buffer_standard_layout when validating standard + // uniform layout. If true, disables scalar block layout rules. + void SetUniformBufferStandardLayout(bool val) { + spvValidatorOptionsSetUniformBufferStandardLayout(options_, val); + } + + // Enables VK_EXT_scalar_block_layout when validating standard + // uniform/storage buffer/push-constant layout. If true, disables + // relaxed block layout rules. + void SetScalarBlockLayout(bool val) { + spvValidatorOptionsSetScalarBlockLayout(options_, val); + } + + // Skips validating standard uniform/storage buffer/push-constant layout. + void SetSkipBlockLayout(bool val) { + spvValidatorOptionsSetSkipBlockLayout(options_, val); + } + + // Records whether or not the validator should relax the rules on pointer + // usage in logical addressing mode. + // + // When relaxed, it will allow the following usage cases of pointers: + // 1) OpVariable allocating an object whose type is a pointer type + // 2) OpReturnValue returning a pointer value + void SetRelaxLogicalPointer(bool val) { + spvValidatorOptionsSetRelaxLogicalPointer(options_, val); + } + + // Records whether or not the validator should relax the rules because it is + // expected that the optimizations will make the code legal. + // + // When relaxed, it will allow the following: + // 1) It will allow relaxed logical pointers. Setting this option will also + // set that option. + // 2) Pointers that are pass as parameters to function calls do not have to + // match the storage class of the formal parameter. + // 3) Pointers that are actaul parameters on function calls do not have to + // point to the same type pointed as the formal parameter. The types just + // need to logically match. + void SetBeforeHlslLegalization(bool val) { + spvValidatorOptionsSetBeforeHlslLegalization(options_, val); + } + + private: + spv_validator_options options_; +}; + +// A C++ wrapper around an optimization options object. +class OptimizerOptions { + public: + OptimizerOptions() : options_(spvOptimizerOptionsCreate()) {} + ~OptimizerOptions() { spvOptimizerOptionsDestroy(options_); } + + // Allow implicit conversion to the underlying object. + operator spv_optimizer_options() const { return options_; } + + // Records whether or not the optimizer should run the validator before + // optimizing. If |run| is true, the validator will be run. + void set_run_validator(bool run) { + spvOptimizerOptionsSetRunValidator(options_, run); + } + + // Records the validator options that should be passed to the validator if it + // is run. + void set_validator_options(const ValidatorOptions& val_options) { + spvOptimizerOptionsSetValidatorOptions(options_, val_options); + } + + // Records the maximum possible value for the id bound. + void set_max_id_bound(uint32_t new_bound) { + spvOptimizerOptionsSetMaxIdBound(options_, new_bound); + } + + // Records whether all bindings within the module should be preserved. + void set_preserve_bindings(bool preserve_bindings) { + spvOptimizerOptionsSetPreserveBindings(options_, preserve_bindings); + } + + // Records whether all specialization constants within the module + // should be preserved. + void set_preserve_spec_constants(bool preserve_spec_constants) { + spvOptimizerOptionsSetPreserveSpecConstants(options_, + preserve_spec_constants); + } + + private: + spv_optimizer_options options_; +}; + +// A C++ wrapper around a reducer options object. +class ReducerOptions { + public: + ReducerOptions() : options_(spvReducerOptionsCreate()) {} + ~ReducerOptions() { spvReducerOptionsDestroy(options_); } + + // Allow implicit conversion to the underlying object. + operator spv_reducer_options() const { // NOLINT(google-explicit-constructor) + return options_; + } + + // See spvReducerOptionsSetStepLimit. + void set_step_limit(uint32_t step_limit) { + spvReducerOptionsSetStepLimit(options_, step_limit); + } + + // See spvReducerOptionsSetFailOnValidationError. + void set_fail_on_validation_error(bool fail_on_validation_error) { + spvReducerOptionsSetFailOnValidationError(options_, + fail_on_validation_error); + } + + // See spvReducerOptionsSetTargetFunction. + void set_target_function(uint32_t target_function) { + spvReducerOptionsSetTargetFunction(options_, target_function); + } + + private: + spv_reducer_options options_; +}; + +// A C++ wrapper around a fuzzer options object. +class FuzzerOptions { + public: + FuzzerOptions() : options_(spvFuzzerOptionsCreate()) {} + ~FuzzerOptions() { spvFuzzerOptionsDestroy(options_); } + + // Allow implicit conversion to the underlying object. + operator spv_fuzzer_options() const { // NOLINT(google-explicit-constructor) + return options_; + } + + // See spvFuzzerOptionsEnableReplayValidation. + void enable_replay_validation() { + spvFuzzerOptionsEnableReplayValidation(options_); + } + + // See spvFuzzerOptionsSetRandomSeed. + void set_random_seed(uint32_t seed) { + spvFuzzerOptionsSetRandomSeed(options_, seed); + } + + // See spvFuzzerOptionsSetReplayRange. + void set_replay_range(int32_t replay_range) { + spvFuzzerOptionsSetReplayRange(options_, replay_range); + } + + // See spvFuzzerOptionsSetShrinkerStepLimit. + void set_shrinker_step_limit(uint32_t shrinker_step_limit) { + spvFuzzerOptionsSetShrinkerStepLimit(options_, shrinker_step_limit); + } + + // See spvFuzzerOptionsEnableFuzzerPassValidation. + void enable_fuzzer_pass_validation() { + spvFuzzerOptionsEnableFuzzerPassValidation(options_); + } + + // See spvFuzzerOptionsEnableAllPasses. + void enable_all_passes() { spvFuzzerOptionsEnableAllPasses(options_); } + + private: + spv_fuzzer_options options_; +}; + +// C++ interface for SPIRV-Tools functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides methods for assembling, disassembling, and validating. +// +// Instances of this class provide basic thread-safety guarantee. +class SpirvTools { + public: + enum { + // Default assembling option used by assemble(): + kDefaultAssembleOption = SPV_TEXT_TO_BINARY_OPTION_NONE, + + // Default disassembling option used by Disassemble(): + // * Avoid prefix comments from decoding the SPIR-V module header, and + // * Use friendly names for variables. + kDefaultDisassembleOption = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES + }; + + // Constructs an instance targeting the given environment |env|. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + explicit SpirvTools(spv_target_env env); + + // Disables copy/move constructor/assignment operations. + SpirvTools(const SpirvTools&) = delete; + SpirvTools(SpirvTools&&) = delete; + SpirvTools& operator=(const SpirvTools&) = delete; + SpirvTools& operator=(SpirvTools&&) = delete; + + // Destructs this instance. + ~SpirvTools(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Assembles the given assembly |text| and writes the result to |binary|. + // Returns true on successful assembling. |binary| will be kept untouched if + // assembling is unsuccessful. + // The SPIR-V binary version is set to the highest version of SPIR-V supported + // by the target environment with which this SpirvTools object was created. + bool Assemble(const std::string& text, std::vector* binary, + uint32_t options = kDefaultAssembleOption) const; + // |text_size| specifies the number of bytes in |text|. A terminating null + // character is not required to present in |text| as long as |text| is valid. + // The SPIR-V binary version is set to the highest version of SPIR-V supported + // by the target environment with which this SpirvTools object was created. + bool Assemble(const char* text, size_t text_size, + std::vector* binary, + uint32_t options = kDefaultAssembleOption) const; + + // Disassembles the given SPIR-V |binary| with the given |options| and writes + // the assembly to |text|. Returns true on successful disassembling. |text| + // will be kept untouched if diassembling is unsuccessful. + bool Disassemble(const std::vector& binary, std::string* text, + uint32_t options = kDefaultDisassembleOption) const; + // |binary_size| specifies the number of words in |binary|. + bool Disassemble(const uint32_t* binary, size_t binary_size, + std::string* text, + uint32_t options = kDefaultDisassembleOption) const; + + // Validates the given SPIR-V |binary|. Returns true if no issues are found. + // Otherwise, returns false and communicates issues via the message consumer + // registered. + // Validates for SPIR-V spec rules for the SPIR-V version named in the + // binary's header (at word offset 1). Additionally, if the target + // environment is a client API (such as Vulkan 1.1), then validate for that + // client API version, to the extent that it is verifiable from data in the + // binary itself. + bool Validate(const std::vector& binary) const; + // Like the previous overload, but provides the binary as a pointer and size: + // |binary_size| specifies the number of words in |binary|. + // Validates for SPIR-V spec rules for the SPIR-V version named in the + // binary's header (at word offset 1). Additionally, if the target + // environment is a client API (such as Vulkan 1.1), then validate for that + // client API version, to the extent that it is verifiable from data in the + // binary itself. + bool Validate(const uint32_t* binary, size_t binary_size) const; + // Like the previous overload, but takes an options object. + // Validates for SPIR-V spec rules for the SPIR-V version named in the + // binary's header (at word offset 1). Additionally, if the target + // environment is a client API (such as Vulkan 1.1), then validate for that + // client API version, to the extent that it is verifiable from data in the + // binary itself, or in the validator options. + bool Validate(const uint32_t* binary, size_t binary_size, + spv_validator_options options) const; + + // Was this object successfully constructed. + bool IsValid() const; + + private: + struct Impl; // Opaque struct for holding the data fields used by this class. + std::unique_ptr impl_; // Unique pointer to implementation data. +}; + +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_LIBSPIRV_HPP_ diff --git a/third_party/spirv-tools/include/spirv-tools/linker.hpp b/third_party/spirv-tools/include/spirv-tools/linker.hpp new file mode 100644 index 0000000..d2f3e72 --- /dev/null +++ b/third_party/spirv-tools/include/spirv-tools/linker.hpp @@ -0,0 +1,97 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_LINKER_HPP_ +#define INCLUDE_SPIRV_TOOLS_LINKER_HPP_ + +#include + +#include +#include + +#include "libspirv.hpp" + +namespace spvtools { + +class LinkerOptions { + public: + LinkerOptions() + : create_library_(false), + verify_ids_(false), + allow_partial_linkage_(false) {} + + // Returns whether a library or an executable should be produced by the + // linking phase. + // + // All exported symbols are kept when creating a library, whereas they will + // be removed when creating an executable. + // The returned value will be true if creating a library, and false if + // creating an executable. + bool GetCreateLibrary() const { return create_library_; } + + // Sets whether a library or an executable should be produced. + void SetCreateLibrary(bool create_library) { + create_library_ = create_library; + } + + // Returns whether to verify the uniqueness of the unique ids in the merged + // context. + bool GetVerifyIds() const { return verify_ids_; } + + // Sets whether to verify the uniqueness of the unique ids in the merged + // context. + void SetVerifyIds(bool verify_ids) { verify_ids_ = verify_ids; } + + // Returns whether to allow for imported symbols to have no corresponding + // exported symbols + bool GetAllowPartialLinkage() const { return allow_partial_linkage_; } + + // Sets whether to allow for imported symbols to have no corresponding + // exported symbols + void SetAllowPartialLinkage(bool allow_partial_linkage) { + allow_partial_linkage_ = allow_partial_linkage; + } + + private: + bool create_library_; + bool verify_ids_; + bool allow_partial_linkage_; +}; + +// Links one or more SPIR-V modules into a new SPIR-V module. That is, combine +// several SPIR-V modules into one, resolving link dependencies between them. +// +// At least one binary has to be provided in |binaries|. Those binaries do not +// have to be valid, but they should be at least parseable. +// The functions can fail due to the following: +// * The given context was not initialised using `spvContextCreate()`; +// * No input modules were given; +// * One or more of those modules were not parseable; +// * The input modules used different addressing or memory models; +// * The ID or global variable number limit were exceeded; +// * Some entry points were defined multiple times; +// * Some imported symbols did not have an exported counterpart; +// * Possibly other reasons. +spv_result_t Link(const Context& context, + const std::vector>& binaries, + std::vector* linked_binary, + const LinkerOptions& options = LinkerOptions()); +spv_result_t Link(const Context& context, const uint32_t* const* binaries, + const size_t* binary_sizes, size_t num_binaries, + std::vector* linked_binary, + const LinkerOptions& options = LinkerOptions()); + +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_LINKER_HPP_ diff --git a/third_party/spirv-tools/include/spirv-tools/optimizer.hpp b/third_party/spirv-tools/include/spirv-tools/optimizer.hpp new file mode 100644 index 0000000..f12774d --- /dev/null +++ b/third_party/spirv-tools/include/spirv-tools/optimizer.hpp @@ -0,0 +1,880 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ +#define INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ + +#include +#include +#include +#include +#include + +#include "libspirv.hpp" + +namespace spvtools { + +namespace opt { +class Pass; +} + +// C++ interface for SPIR-V optimization functionalities. It wraps the context +// (including target environment and the corresponding SPIR-V grammar) and +// provides methods for registering optimization passes and optimizing. +// +// Instances of this class provides basic thread-safety guarantee. +class Optimizer { + public: + // The token for an optimization pass. It is returned via one of the + // Create*Pass() standalone functions at the end of this header file and + // consumed by the RegisterPass() method. Tokens are one-time objects that + // only support move; copying is not allowed. + struct PassToken { + struct Impl; // Opaque struct for holding inernal data. + + PassToken(std::unique_ptr); + + // Tokens for built-in passes should be created using Create*Pass functions + // below; for out-of-tree passes, use this constructor instead. + // Note that this API isn't guaranteed to be stable and may change without + // preserving source or binary compatibility in the future. + PassToken(std::unique_ptr&& pass); + + // Tokens can only be moved. Copying is disabled. + PassToken(const PassToken&) = delete; + PassToken(PassToken&&); + PassToken& operator=(const PassToken&) = delete; + PassToken& operator=(PassToken&&); + + ~PassToken(); + + std::unique_ptr impl_; // Unique pointer to internal data. + }; + + // Constructs an instance with the given target |env|, which is used to decode + // the binaries to be optimized later. + // + // The instance will have an empty message consumer, which ignores all + // messages from the library. Use SetMessageConsumer() to supply a consumer + // if messages are of concern. + // + // For collections of passes that are meant to transform the input into + // another execution environment, then the source environment should be + // supplied. e.g. for VulkanToWebGPUPasses the environment should be + // SPV_ENV_VULKAN_1_1 not SPV_ENV_WEBGPU_0. + explicit Optimizer(spv_target_env env); + + // Disables copy/move constructor/assignment operations. + Optimizer(const Optimizer&) = delete; + Optimizer(Optimizer&&) = delete; + Optimizer& operator=(const Optimizer&) = delete; + Optimizer& operator=(Optimizer&&) = delete; + + // Destructs this instance. + ~Optimizer(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns a reference to the registered message consumer. + const MessageConsumer& consumer() const; + + // Registers the given |pass| to this optimizer. Passes will be run in the + // exact order of registration. The token passed in will be consumed by this + // method. + Optimizer& RegisterPass(PassToken&& pass); + + // Registers passes that attempt to improve performance of generated code. + // This sequence of passes is subject to constant review and will change + // from time to time. + Optimizer& RegisterPerformancePasses(); + + // Registers passes that attempt to improve the size of generated code. + // This sequence of passes is subject to constant review and will change + // from time to time. + Optimizer& RegisterSizePasses(); + + // Registers passes that have been prescribed for converting from Vulkan to + // WebGPU. This sequence of passes is subject to constant review and will + // change from time to time. + Optimizer& RegisterVulkanToWebGPUPasses(); + + // Registers passes that have been prescribed for converting from WebGPU to + // Vulkan. This sequence of passes is subject to constant review and will + // change from time to time. + Optimizer& RegisterWebGPUToVulkanPasses(); + + // Registers passes that attempt to legalize the generated code. + // + // Note: this recipe is specially designed for legalizing SPIR-V. It should be + // used by compilers after translating HLSL source code literally. It should + // *not* be used by general workloads for performance or size improvement. + // + // This sequence of passes is subject to constant review and will change + // from time to time. + Optimizer& RegisterLegalizationPasses(); + + // Register passes specified in the list of |flags|. Each flag must be a + // string of a form accepted by Optimizer::FlagHasValidForm(). + // + // If the list of flags contains an invalid entry, it returns false and an + // error message is emitted to the MessageConsumer object (use + // Optimizer::SetMessageConsumer to define a message consumer, if needed). + // + // If all the passes are registered successfully, it returns true. + bool RegisterPassesFromFlags(const std::vector& flags); + + // Registers the optimization pass associated with |flag|. This only accepts + // |flag| values of the form "--pass_name[=pass_args]". If no such pass + // exists, it returns false. Otherwise, the pass is registered and it returns + // true. + // + // The following flags have special meaning: + // + // -O: Registers all performance optimization passes + // (Optimizer::RegisterPerformancePasses) + // + // -Os: Registers all size optimization passes + // (Optimizer::RegisterSizePasses). + // + // --legalize-hlsl: Registers all passes that legalize SPIR-V generated by an + // HLSL front-end. + bool RegisterPassFromFlag(const std::string& flag); + + // Validates that |flag| has a valid format. Strings accepted: + // + // --pass_name[=pass_args] + // -O + // -Os + // + // If |flag| takes one of the forms above, it returns true. Otherwise, it + // returns false. + bool FlagHasValidForm(const std::string& flag) const; + + // Allows changing, after creation time, the target environment to be + // optimized for and validated. Should be called before calling Run(). + void SetTargetEnv(const spv_target_env env); + + // Optimizes the given SPIR-V module |original_binary| and writes the + // optimized binary into |optimized_binary|. The optimized binary uses + // the same SPIR-V version as the original binary. + // + // Returns true on successful optimization, whether or not the module is + // modified. Returns false if |original_binary| fails to validate or if errors + // occur when processing |original_binary| using any of the registered passes. + // In that case, no further passes are executed and the contents in + // |optimized_binary| may be invalid. + // + // By default, the binary is validated before any transforms are performed, + // and optionally after each transform. Validation uses SPIR-V spec rules + // for the SPIR-V version named in the binary's header (at word offset 1). + // Additionally, if the target environment is a client API (such as + // Vulkan 1.1), then validate for that client API version, to the extent + // that it is verifiable from data in the binary itself. + // + // It's allowed to alias |original_binary| to the start of |optimized_binary|. + bool Run(const uint32_t* original_binary, size_t original_binary_size, + std::vector* optimized_binary) const; + + // DEPRECATED: Same as above, except passes |options| to the validator when + // trying to validate the binary. If |skip_validation| is true, then the + // caller is guaranteeing that |original_binary| is valid, and the validator + // will not be run. The |max_id_bound| is the limit on the max id in the + // module. + bool Run(const uint32_t* original_binary, const size_t original_binary_size, + std::vector* optimized_binary, + const ValidatorOptions& options, bool skip_validation) const; + + // Same as above, except it takes an options object. See the documentation + // for |OptimizerOptions| to see which options can be set. + // + // By default, the binary is validated before any transforms are performed, + // and optionally after each transform. Validation uses SPIR-V spec rules + // for the SPIR-V version named in the binary's header (at word offset 1). + // Additionally, if the target environment is a client API (such as + // Vulkan 1.1), then validate for that client API version, to the extent + // that it is verifiable from data in the binary itself, or from the + // validator options set on the optimizer options. + bool Run(const uint32_t* original_binary, const size_t original_binary_size, + std::vector* optimized_binary, + const spv_optimizer_options opt_options) const; + + // Returns a vector of strings with all the pass names added to this + // optimizer's pass manager. These strings are valid until the associated + // pass manager is destroyed. + std::vector GetPassNames() const; + + // Sets the option to print the disassembly before each pass and after the + // last pass. If |out| is null, then no output is generated. Otherwise, + // output is sent to the |out| output stream. + Optimizer& SetPrintAll(std::ostream* out); + + // Sets the option to print the resource utilization of each pass. If |out| + // is null, then no output is generated. Otherwise, output is sent to the + // |out| output stream. + Optimizer& SetTimeReport(std::ostream* out); + + // Sets the option to validate the module after each pass. + Optimizer& SetValidateAfterAll(bool validate); + + private: + struct Impl; // Opaque struct for holding internal data. + std::unique_ptr impl_; // Unique pointer to internal data. +}; + +// Creates a null pass. +// A null pass does nothing to the SPIR-V module to be optimized. +Optimizer::PassToken CreateNullPass(); + +// Creates a strip-atomic-counter-memory pass. +// A strip-atomic-counter-memory pass removes all usages of the +// AtomicCounterMemory bit in Memory Semantics bitmasks. This bit is a no-op in +// Vulkan, so isn't needed in that env. And the related capability is not +// allowed in WebGPU, so it is not allowed in that env. +Optimizer::PassToken CreateStripAtomicCounterMemoryPass(); + +// Creates a strip-debug-info pass. +// A strip-debug-info pass removes all debug instructions (as documented in +// Section 3.32.2 of the SPIR-V spec) of the SPIR-V module to be optimized. +Optimizer::PassToken CreateStripDebugInfoPass(); + +// Creates a strip-reflect-info pass. +// A strip-reflect-info pass removes all reflections instructions. +// For now, this is limited to removing decorations defined in +// SPV_GOOGLE_hlsl_functionality1. The coverage may expand in +// the future. +Optimizer::PassToken CreateStripReflectInfoPass(); + +// Creates an eliminate-dead-functions pass. +// An eliminate-dead-functions pass will remove all functions that are not in +// the call trees rooted at entry points and exported functions. These +// functions are not needed because they will never be called. +Optimizer::PassToken CreateEliminateDeadFunctionsPass(); + +// Creates an eliminate-dead-members pass. +// An eliminate-dead-members pass will remove all unused members of structures. +// This will not affect the data layout of the remaining members. +Optimizer::PassToken CreateEliminateDeadMembersPass(); + +// Creates a set-spec-constant-default-value pass from a mapping from spec-ids +// to the default values in the form of string. +// A set-spec-constant-default-value pass sets the default values for the +// spec constants that have SpecId decorations (i.e., those defined by +// OpSpecConstant{|True|False} instructions). +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map& id_value_map); + +// Creates a set-spec-constant-default-value pass from a mapping from spec-ids +// to the default values in the form of bit pattern. +// A set-spec-constant-default-value pass sets the default values for the +// spec constants that have SpecId decorations (i.e., those defined by +// OpSpecConstant{|True|False} instructions). +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map>& id_value_map); + +// Creates a flatten-decoration pass. +// A flatten-decoration pass replaces grouped decorations with equivalent +// ungrouped decorations. That is, it replaces each OpDecorationGroup +// instruction and associated OpGroupDecorate and OpGroupMemberDecorate +// instructions with equivalent OpDecorate and OpMemberDecorate instructions. +// The pass does not attempt to preserve debug information for instructions +// it removes. +Optimizer::PassToken CreateFlattenDecorationPass(); + +// Creates a freeze-spec-constant-value pass. +// A freeze-spec-constant pass specializes the value of spec constants to +// their default values. This pass only processes the spec constants that have +// SpecId decorations (defined by OpSpecConstant, OpSpecConstantTrue, or +// OpSpecConstantFalse instructions) and replaces them with their normal +// counterparts (OpConstant, OpConstantTrue, or OpConstantFalse). The +// corresponding SpecId annotation instructions will also be removed. This +// pass does not fold the newly added normal constants and does not process +// other spec constants defined by OpSpecConstantComposite or +// OpSpecConstantOp. +Optimizer::PassToken CreateFreezeSpecConstantValuePass(); + +// Creates a fold-spec-constant-op-and-composite pass. +// A fold-spec-constant-op-and-composite pass folds spec constants defined by +// OpSpecConstantOp or OpSpecConstantComposite instruction, to normal Constants +// defined by OpConstantTrue, OpConstantFalse, OpConstant, OpConstantNull, or +// OpConstantComposite instructions. Note that spec constants defined with +// OpSpecConstant, OpSpecConstantTrue, or OpSpecConstantFalse instructions are +// not handled, as these instructions indicate their value are not determined +// and can be changed in future. A spec constant is foldable if all of its +// value(s) can be determined from the module. E.g., an integer spec constant +// defined with OpSpecConstantOp instruction can be folded if its value won't +// change later. This pass will replace the original OpSpecContantOp instruction +// with an OpConstant instruction. When folding composite spec constants, +// new instructions may be inserted to define the components of the composite +// constant first, then the original spec constants will be replaced by +// OpConstantComposite instructions. +// +// There are some operations not supported yet: +// OpSConvert, OpFConvert, OpQuantizeToF16 and +// all the operations under Kernel capability. +// TODO(qining): Add support for the operations listed above. +Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass(); + +// Creates a unify-constant pass. +// A unify-constant pass de-duplicates the constants. Constants with the exact +// same value and identical form will be unified and only one constant will +// be kept for each unique pair of type and value. +// There are several cases not handled by this pass: +// 1) Constants defined by OpConstantNull instructions (null constants) and +// constants defined by OpConstantFalse, OpConstant or OpConstantComposite +// with value 0 (zero-valued normal constants) are not considered equivalent. +// So null constants won't be used to replace zero-valued normal constants, +// vice versa. +// 2) Whenever there are decorations to the constant's result id id, the +// constant won't be handled, which means, it won't be used to replace any +// other constants, neither can other constants replace it. +// 3) NaN in float point format with different bit patterns are not unified. +Optimizer::PassToken CreateUnifyConstantPass(); + +// Creates a eliminate-dead-constant pass. +// A eliminate-dead-constant pass removes dead constants, including normal +// contants defined by OpConstant, OpConstantComposite, OpConstantTrue, or +// OpConstantFalse and spec constants defined by OpSpecConstant, +// OpSpecConstantComposite, OpSpecConstantTrue, OpSpecConstantFalse or +// OpSpecConstantOp. +Optimizer::PassToken CreateEliminateDeadConstantPass(); + +// Creates a strength-reduction pass. +// A strength-reduction pass will look for opportunities to replace an +// instruction with an equivalent and less expensive one. For example, +// multiplying by a power of 2 can be replaced by a bit shift. +Optimizer::PassToken CreateStrengthReductionPass(); + +// Creates a block merge pass. +// This pass searches for blocks with a single Branch to a block with no +// other predecessors and merges the blocks into a single block. Continue +// blocks and Merge blocks are not candidates for the second block. +// +// The pass is most useful after Dead Branch Elimination, which can leave +// such sequences of blocks. Merging them makes subsequent passes more +// effective, such as single block local store-load elimination. +// +// While this pass reduces the number of occurrences of this sequence, at +// this time it does not guarantee all such sequences are eliminated. +// +// Presence of phi instructions can inhibit this optimization. Handling +// these is left for future improvements. +Optimizer::PassToken CreateBlockMergePass(); + +// Creates an exhaustive inline pass. +// An exhaustive inline pass attempts to exhaustively inline all function +// calls in all functions in an entry point call tree. The intent is to enable, +// albeit through brute force, analysis and optimization across function +// calls by subsequent optimization passes. As the inlining is exhaustive, +// there is no attempt to optimize for size or runtime performance. Functions +// that are not in the call tree of an entry point are not changed. +Optimizer::PassToken CreateInlineExhaustivePass(); + +// Creates an opaque inline pass. +// An opaque inline pass inlines all function calls in all functions in all +// entry point call trees where the called function contains an opaque type +// in either its parameter types or return type. An opaque type is currently +// defined as Image, Sampler or SampledImage. The intent is to enable, albeit +// through brute force, analysis and optimization across these function calls +// by subsequent passes in order to remove the storing of opaque types which is +// not legal in Vulkan. Functions that are not in the call tree of an entry +// point are not changed. +Optimizer::PassToken CreateInlineOpaquePass(); + +// Creates a single-block local variable load/store elimination pass. +// For every entry point function, do single block memory optimization of +// function variables referenced only with non-access-chain loads and stores. +// For each targeted variable load, if previous store to that variable in the +// block, replace the load's result id with the value id of the store. +// If previous load within the block, replace the current load's result id +// with the previous load's result id. In either case, delete the current +// load. Finally, check if any remaining stores are useless, and delete store +// and variable if possible. +// +// The presence of access chain references and function calls can inhibit +// the above optimization. +// +// Only modules with relaxed logical addressing (see opt/instruction.h) are +// currently processed. +// +// This pass is most effective if preceeded by Inlining and +// LocalAccessChainConvert. This pass will reduce the work needed to be done +// by LocalSingleStoreElim and LocalMultiStoreElim. +// +// Only functions in the call tree of an entry point are processed. +Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass(); + +// Create dead branch elimination pass. +// For each entry point function, this pass will look for SelectionMerge +// BranchConditionals with constant condition and convert to a Branch to +// the indicated label. It will delete resulting dead blocks. +// +// For all phi functions in merge block, replace all uses with the id +// corresponding to the living predecessor. +// +// Note that some branches and blocks may be left to avoid creating invalid +// control flow. Improving this is left to future work. +// +// This pass is most effective when preceeded by passes which eliminate +// local loads and stores, effectively propagating constant values where +// possible. +Optimizer::PassToken CreateDeadBranchElimPass(); + +// Creates an SSA local variable load/store elimination pass. +// For every entry point function, eliminate all loads and stores of function +// scope variables only referenced with non-access-chain loads and stores. +// Eliminate the variables as well. +// +// The presence of access chain references and function calls can inhibit +// the above optimization. +// +// Only shader modules with relaxed logical addressing (see opt/instruction.h) +// are currently processed. Currently modules with any extensions enabled are +// not processed. This is left for future work. +// +// This pass is most effective if preceeded by Inlining and +// LocalAccessChainConvert. LocalSingleStoreElim and LocalSingleBlockElim +// will reduce the work that this pass has to do. +Optimizer::PassToken CreateLocalMultiStoreElimPass(); + +// Creates a local access chain conversion pass. +// A local access chain conversion pass identifies all function scope +// variables which are accessed only with loads, stores and access chains +// with constant indices. It then converts all loads and stores of such +// variables into equivalent sequences of loads, stores, extracts and inserts. +// +// This pass only processes entry point functions. It currently only converts +// non-nested, non-ptr access chains. It does not process modules with +// non-32-bit integer types present. Optional memory access options on loads +// and stores are ignored as we are only processing function scope variables. +// +// This pass unifies access to these variables to a single mode and simplifies +// subsequent analysis and elimination of these variables along with their +// loads and stores allowing values to propagate to their points of use where +// possible. +Optimizer::PassToken CreateLocalAccessChainConvertPass(); + +// Creates a local single store elimination pass. +// For each entry point function, this pass eliminates loads and stores for +// function scope variable that are stored to only once, where possible. Only +// whole variable loads and stores are eliminated; access-chain references are +// not optimized. Replace all loads of such variables with the value that is +// stored and eliminate any resulting dead code. +// +// Currently, the presence of access chains and function calls can inhibit this +// pass, however the Inlining and LocalAccessChainConvert passes can make it +// more effective. In additional, many non-load/store memory operations are +// not supported and will prohibit optimization of a function. Support of +// these operations are future work. +// +// Only shader modules with relaxed logical addressing (see opt/instruction.h) +// are currently processed. +// +// This pass will reduce the work needed to be done by LocalSingleBlockElim +// and LocalMultiStoreElim and can improve the effectiveness of other passes +// such as DeadBranchElimination which depend on values for their analysis. +Optimizer::PassToken CreateLocalSingleStoreElimPass(); + +// Creates an insert/extract elimination pass. +// This pass processes each entry point function in the module, searching for +// extracts on a sequence of inserts. It further searches the sequence for an +// insert with indices identical to the extract. If such an insert can be +// found before hitting a conflicting insert, the extract's result id is +// replaced with the id of the values from the insert. +// +// Besides removing extracts this pass enables subsequent dead code elimination +// passes to delete the inserts. This pass performs best after access chains are +// converted to inserts and extracts and local loads and stores are eliminated. +Optimizer::PassToken CreateInsertExtractElimPass(); + +// Creates a dead insert elimination pass. +// This pass processes each entry point function in the module, searching for +// unreferenced inserts into composite types. These are most often unused +// stores to vector components. They are unused because they are never +// referenced, or because there is another insert to the same component between +// the insert and the reference. After removing the inserts, dead code +// elimination is attempted on the inserted values. +// +// This pass performs best after access chains are converted to inserts and +// extracts and local loads and stores are eliminated. While executing this +// pass can be advantageous on its own, it is also advantageous to execute +// this pass after CreateInsertExtractPass() as it will remove any unused +// inserts created by that pass. +Optimizer::PassToken CreateDeadInsertElimPass(); + +// Create aggressive dead code elimination pass +// This pass eliminates unused code from the module. In addition, +// it detects and eliminates code which may have spurious uses but which do +// not contribute to the output of the function. The most common cause of +// such code sequences is summations in loops whose result is no longer used +// due to dead code elimination. This optimization has additional compile +// time cost over standard dead code elimination. +// +// This pass only processes entry point functions. It also only processes +// shaders with relaxed logical addressing (see opt/instruction.h). It +// currently will not process functions with function calls. Unreachable +// functions are deleted. +// +// This pass will be made more effective by first running passes that remove +// dead control flow and inlines function calls. +// +// This pass can be especially useful after running Local Access Chain +// Conversion, which tends to cause cycles of dead code to be left after +// Store/Load elimination passes are completed. These cycles cannot be +// eliminated with standard dead code elimination. +Optimizer::PassToken CreateAggressiveDCEPass(); + +// Creates an empty pass. +// This is deprecated and will be removed. +// TODO(jaebaek): remove this pass after handling glslang's broken unit tests. +// https://github.com/KhronosGroup/glslang/pull/2440 +Optimizer::PassToken CreatePropagateLineInfoPass(); + +// Creates an empty pass. +// This is deprecated and will be removed. +// TODO(jaebaek): remove this pass after handling glslang's broken unit tests. +// https://github.com/KhronosGroup/glslang/pull/2440 +Optimizer::PassToken CreateRedundantLineInfoElimPass(); + +// Creates a compact ids pass. +// The pass remaps result ids to a compact and gapless range starting from %1. +Optimizer::PassToken CreateCompactIdsPass(); + +// Creates a remove duplicate pass. +// This pass removes various duplicates: +// * duplicate capabilities; +// * duplicate extended instruction imports; +// * duplicate types; +// * duplicate decorations. +Optimizer::PassToken CreateRemoveDuplicatesPass(); + +// Creates a CFG cleanup pass. +// This pass removes cruft from the control flow graph of functions that are +// reachable from entry points and exported functions. It currently includes the +// following functionality: +// +// - Removal of unreachable basic blocks. +Optimizer::PassToken CreateCFGCleanupPass(); + +// Create dead variable elimination pass. +// This pass will delete module scope variables, along with their decorations, +// that are not referenced. +Optimizer::PassToken CreateDeadVariableEliminationPass(); + +// create merge return pass. +// changes functions that have multiple return statements so they have a single +// return statement. +// +// for structured control flow it is assumed that the only unreachable blocks in +// the function are trivial merge and continue blocks. +// +// a trivial merge block contains the label and an opunreachable instructions, +// nothing else. a trivial continue block contain a label and an opbranch to +// the header, nothing else. +// +// these conditions are guaranteed to be met after running dead-branch +// elimination. +Optimizer::PassToken CreateMergeReturnPass(); + +// Create value numbering pass. +// This pass will look for instructions in the same basic block that compute the +// same value, and remove the redundant ones. +Optimizer::PassToken CreateLocalRedundancyEliminationPass(); + +// Create LICM pass. +// This pass will look for invariant instructions inside loops and hoist them to +// the loops preheader. +Optimizer::PassToken CreateLoopInvariantCodeMotionPass(); + +// Creates a loop fission pass. +// This pass will split all top level loops whose register pressure exceedes the +// given |threshold|. +Optimizer::PassToken CreateLoopFissionPass(size_t threshold); + +// Creates a loop fusion pass. +// This pass will look for adjacent loops that are compatible and legal to be +// fused. The fuse all such loops as long as the register usage for the fused +// loop stays under the threshold defined by |max_registers_per_loop|. +Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop); + +// Creates a loop peeling pass. +// This pass will look for conditions inside a loop that are true or false only +// for the N first or last iteration. For loop with such condition, those N +// iterations of the loop will be executed outside of the main loop. +// To limit code size explosion, the loop peeling can only happen if the code +// size growth for each loop is under |code_growth_threshold|. +Optimizer::PassToken CreateLoopPeelingPass(); + +// Creates a loop unswitch pass. +// This pass will look for loop independent branch conditions and move the +// condition out of the loop and version the loop based on the taken branch. +// Works best after LICM and local multi store elimination pass. +Optimizer::PassToken CreateLoopUnswitchPass(); + +// Create global value numbering pass. +// This pass will look for instructions where the same value is computed on all +// paths leading to the instruction. Those instructions are deleted. +Optimizer::PassToken CreateRedundancyEliminationPass(); + +// Create scalar replacement pass. +// This pass replaces composite function scope variables with variables for each +// element if those elements are accessed individually. The parameter is a +// limit on the number of members in the composite variable that the pass will +// consider replacing. +Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit = 100); + +// Create a private to local pass. +// This pass looks for variables delcared in the private storage class that are +// used in only one function. Those variables are moved to the function storage +// class in the function that they are used. +Optimizer::PassToken CreatePrivateToLocalPass(); + +// Creates a conditional constant propagation (CCP) pass. +// This pass implements the SSA-CCP algorithm in +// +// Constant propagation with conditional branches, +// Wegman and Zadeck, ACM TOPLAS 13(2):181-210. +// +// Constant values in expressions and conditional jumps are folded and +// simplified. This may reduce code size by removing never executed jump targets +// and computations with constant operands. +Optimizer::PassToken CreateCCPPass(); + +// Creates a workaround driver bugs pass. This pass attempts to work around +// a known driver bug (issue #1209) by identifying the bad code sequences and +// rewriting them. +// +// Current workaround: Avoid OpUnreachable instructions in loops. +Optimizer::PassToken CreateWorkaround1209Pass(); + +// Creates a pass that converts if-then-else like assignments into OpSelect. +Optimizer::PassToken CreateIfConversionPass(); + +// Creates a pass that will replace instructions that are not valid for the +// current shader stage by constants. Has no effect on non-shader modules. +Optimizer::PassToken CreateReplaceInvalidOpcodePass(); + +// Creates a pass that simplifies instructions using the instruction folder. +Optimizer::PassToken CreateSimplificationPass(); + +// Create loop unroller pass. +// Creates a pass to unroll loops which have the "Unroll" loop control +// mask set. The loops must meet a specific criteria in order to be unrolled +// safely this criteria is checked before doing the unroll by the +// LoopUtils::CanPerformUnroll method. Any loop that does not meet the criteria +// won't be unrolled. See CanPerformUnroll LoopUtils.h for more information. +Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor = 0); + +// Create the SSA rewrite pass. +// This pass converts load/store operations on function local variables into +// operations on SSA IDs. This allows SSA optimizers to act on these variables. +// Only variables that are local to the function and of supported types are +// processed (see IsSSATargetVar for details). +Optimizer::PassToken CreateSSARewritePass(); + +// Create pass to convert relaxed precision instructions to half precision. +// This pass converts as many relaxed float32 arithmetic operations to half as +// possible. It converts any float32 operands to half if needed. It converts +// any resulting half precision values back to float32 as needed. No variables +// are changed. No image operations are changed. +// +// Best if run after function scope store/load and composite operation +// eliminations are run. Also best if followed by instruction simplification, +// redundancy elimination and DCE. +Optimizer::PassToken CreateConvertRelaxedToHalfPass(); + +// Create relax float ops pass. +// This pass decorates all float32 result instructions with RelaxedPrecision +// if not already so decorated. +Optimizer::PassToken CreateRelaxFloatOpsPass(); + +// Create copy propagate arrays pass. +// This pass looks to copy propagate memory references for arrays. It looks +// for specific code patterns to recognize array copies. +Optimizer::PassToken CreateCopyPropagateArraysPass(); + +// Create a vector dce pass. +// This pass looks for components of vectors that are unused, and removes them +// from the vector. Note this would still leave around lots of dead code that +// a pass of ADCE will be able to remove. +Optimizer::PassToken CreateVectorDCEPass(); + +// Create a pass to reduce the size of loads. +// This pass looks for loads of structures where only a few of its members are +// used. It replaces the loads feeding an OpExtract with an OpAccessChain and +// a load of the specific elements. +Optimizer::PassToken CreateReduceLoadSizePass(); + +// Create a pass to combine chained access chains. +// This pass looks for access chains fed by other access chains and combines +// them into a single instruction where possible. +Optimizer::PassToken CreateCombineAccessChainsPass(); + +// Create a pass to instrument bindless descriptor checking +// This pass instruments all bindless references to check that descriptor +// array indices are inbounds, and if the descriptor indexing extension is +// enabled, that the descriptor has been initialized. If the reference is +// invalid, a record is written to the debug output buffer (if space allows) +// and a null value is returned. This pass is designed to support bindless +// validation in the Vulkan validation layers. +// +// TODO(greg-lunarg): Add support for buffer references. Currently only does +// checking for image references. +// +// Dead code elimination should be run after this pass as the original, +// potentially invalid code is not removed and could cause undefined behavior, +// including crashes. It may also be beneficial to run Simplification +// (ie Constant Propagation), DeadBranchElim and BlockMerge after this pass to +// optimize instrument code involving the testing of compile-time constants. +// It is also generally recommended that this pass (and all +// instrumentation passes) be run after any legalization and optimization +// passes. This will give better analysis for the instrumentation and avoid +// potentially de-optimizing the instrument code, for example, inlining +// the debug record output function throughout the module. +// +// The instrumentation will read and write buffers in debug +// descriptor set |desc_set|. It will write |shader_id| in each output record +// to identify the shader module which generated the record. +// |input_length_enable| controls instrumentation of runtime descriptor array +// references, and |input_init_enable| controls instrumentation of descriptor +// initialization checking, both of which require input buffer support. +Optimizer::PassToken CreateInstBindlessCheckPass( + uint32_t desc_set, uint32_t shader_id, bool input_length_enable = false, + bool input_init_enable = false, bool input_buff_oob_enable = false); + +// Create a pass to instrument physical buffer address checking +// This pass instruments all physical buffer address references to check that +// all referenced bytes fall in a valid buffer. If the reference is +// invalid, a record is written to the debug output buffer (if space allows) +// and a null value is returned. This pass is designed to support buffer +// address validation in the Vulkan validation layers. +// +// Dead code elimination should be run after this pass as the original, +// potentially invalid code is not removed and could cause undefined behavior, +// including crashes. Instruction simplification would likely also be +// beneficial. It is also generally recommended that this pass (and all +// instrumentation passes) be run after any legalization and optimization +// passes. This will give better analysis for the instrumentation and avoid +// potentially de-optimizing the instrument code, for example, inlining +// the debug record output function throughout the module. +// +// The instrumentation will read and write buffers in debug +// descriptor set |desc_set|. It will write |shader_id| in each output record +// to identify the shader module which generated the record. +Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, + uint32_t shader_id); + +// Create a pass to instrument OpDebugPrintf instructions. +// This pass replaces all OpDebugPrintf instructions with instructions to write +// a record containing the string id and the all specified values into a special +// printf output buffer (if space allows). This pass is designed to support +// the printf validation in the Vulkan validation layers. +// +// The instrumentation will write buffers in debug descriptor set |desc_set|. +// It will write |shader_id| in each output record to identify the shader +// module which generated the record. +Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set, + uint32_t shader_id); + +// Create a pass to upgrade to the VulkanKHR memory model. +// This pass upgrades the Logical GLSL450 memory model to Logical VulkanKHR. +// Additionally, it modifies memory, image, atomic and barrier operations to +// conform to that model's requirements. +Optimizer::PassToken CreateUpgradeMemoryModelPass(); + +// Create a pass to do code sinking. Code sinking is a transformation +// where an instruction is moved into a more deeply nested construct. +Optimizer::PassToken CreateCodeSinkingPass(); + +// Create a pass to adds initializers for OpVariable calls that require them +// in WebGPU. Currently this pass naively initializes variables that are +// missing an initializer with a null value. In the future it may initialize +// variables to the first value stored in them, if that is a constant. +Optimizer::PassToken CreateGenerateWebGPUInitializersPass(); + +// Create a pass to fix incorrect storage classes. In order to make code +// generation simpler, DXC may generate code where the storage classes do not +// match up correctly. This pass will fix the errors that it can. +Optimizer::PassToken CreateFixStorageClassPass(); + +// Create a pass to legalize OpVectorShuffle operands going into WebGPU. WebGPU +// forbids using 0xFFFFFFFF, which indicates an undefined result, so this pass +// converts those literals to 0. +Optimizer::PassToken CreateLegalizeVectorShufflePass(); + +// Create a pass to decompose initialized variables into a seperate variable +// declaration and an initial store. +Optimizer::PassToken CreateDecomposeInitializedVariablesPass(); + +// Create a pass to attempt to split up invalid unreachable merge-blocks and +// continue-targets to legalize for WebGPU. +Optimizer::PassToken CreateSplitInvalidUnreachablePass(); + +// Creates a graphics robust access pass. +// +// This pass injects code to clamp indexed accesses to buffers and internal +// arrays, providing guarantees satisfying Vulkan's robustBufferAccess rules. +// +// TODO(dneto): Clamps coordinates and sample index for pointer calculations +// into storage images (OpImageTexelPointer). For an cube array image, it +// assumes the maximum layer count times 6 is at most 0xffffffff. +// +// NOTE: This pass will fail with a message if: +// - The module is not a Shader module. +// - The module declares VariablePointers, VariablePointersStorageBuffer, or +// RuntimeDescriptorArrayEXT capabilities. +// - The module uses an addressing model other than Logical +// - Access chain indices are wider than 64 bits. +// - Access chain index for a struct is not an OpConstant integer or is out +// of range. (The module is already invalid if that is the case.) +// - TODO(dneto): The OpImageTexelPointer coordinate component is not 32-bits +// wide. +// +// NOTE: Access chain indices are always treated as signed integers. So +// if an array has a fixed size of more than 2^31 elements, then elements +// from 2^31 and above are never accessible with a 32-bit index, +// signed or unsigned. For this case, this pass will clamp the index +// between 0 and at 2^31-1, inclusive. +// Similarly, if an array has more then 2^15 element and is accessed with +// a 16-bit index, then elements from 2^15 and above are not accessible. +// In this case, the pass will clamp the index between 0 and 2^15-1 +// inclusive. +Optimizer::PassToken CreateGraphicsRobustAccessPass(); + +// Create descriptor scalar replacement pass. +// This pass replaces every array variable |desc| that has a DescriptorSet and +// Binding decorations with a new variable for each element of the array. +// Suppose |desc| was bound at binding |b|. Then the variable corresponding to +// |desc[i]| will have binding |b+i|. The descriptor set will be the same. It +// is assumed that no other variable already has a binding that will used by one +// of the new variables. If not, the pass will generate invalid Spir-V. All +// accesses to |desc| must be OpAccessChain instructions with a literal index +// for the first index. +Optimizer::PassToken CreateDescriptorScalarReplacementPass(); + +// Create a pass to replace each OpKill instruction with a function call to a +// function that has a single OpKill. Also replace each OpTerminateInvocation +// instruction with a function call to a function that has a single +// OpTerminateInvocation. This allows more code to be inlined. +Optimizer::PassToken CreateWrapOpKillPass(); + +// Replaces the extensions VK_AMD_shader_ballot,VK_AMD_gcn_shader, and +// VK_AMD_shader_trinary_minmax with equivalent code using core instructions and +// capabilities. +Optimizer::PassToken CreateAmdExtToKhrPass(); + +} // namespace spvtools + +#endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/third_party/spirv-tools/kokoro/android/build.sh b/third_party/spirv-tools/kokoro/android/build.sh new file mode 100644 index 0000000..c05c139 --- /dev/null +++ b/third_party/spirv-tools/kokoro/android/build.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Android Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +BUILD_ROOT=$PWD +SRC=$PWD/github/SPIRV-Tools +TARGET_ARCH="armeabi-v7a with NEON" +export ANDROID_NDK=/opt/android-ndk-r15c + +# Get NINJA. +wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip +unzip -q ninja-linux.zip +export PATH="$PWD:$PATH" +git clone --depth=1 https://github.com/taka-no-me/android-cmake.git android-cmake +export TOOLCHAIN_PATH=$PWD/android-cmake/android.toolchain.cmake + + +cd $SRC +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 + +mkdir build && cd $SRC/build + +# Invoke the build. +BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} +echo $(date): Starting build... +cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 -DCMAKE_BUILD_TYPE=Release -DANDROID_NATIVE_API_LEVEL=android-14 -DANDROID_ABI="armeabi-v7a with NEON" -DSPIRV_SKIP_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN_PATH -GNinja -DANDROID_NDK=$ANDROID_NDK .. + +echo $(date): Build everything... +ninja +echo $(date): Build completed. diff --git a/third_party/spirv-tools/kokoro/android/continuous.cfg b/third_party/spirv-tools/kokoro/android/continuous.cfg new file mode 100644 index 0000000..3bdb17a --- /dev/null +++ b/third_party/spirv-tools/kokoro/android/continuous.cfg @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +# +build_file: "SPIRV-Tools/kokoro/android/build.sh" diff --git a/third_party/spirv-tools/kokoro/android/presubmit.cfg b/third_party/spirv-tools/kokoro/android/presubmit.cfg new file mode 100644 index 0000000..21589cc --- /dev/null +++ b/third_party/spirv-tools/kokoro/android/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/android/build.sh" diff --git a/third_party/spirv-tools/kokoro/check-format/build.sh b/third_party/spirv-tools/kokoro/check-format/build.sh new file mode 100644 index 0000000..0c4b8d1 --- /dev/null +++ b/third_party/spirv-tools/kokoro/check-format/build.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Android Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +BUILD_ROOT=$PWD +SRC=$PWD/github/SPIRV-Tools + +# Get clang-format-5.0.0. +# Once kokoro upgrades the Ubuntu VMs, we can use 'apt-get install clang-format' +curl -L http://releases.llvm.org/5.0.0/clang+llvm-5.0.0-linux-x86_64-ubuntu14.04.tar.xz -o clang-llvm.tar.xz +tar xf clang-llvm.tar.xz +export PATH=$PWD/clang+llvm-5.0.0-linux-x86_64-ubuntu14.04/bin:$PATH + +cd $SRC +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 +curl -L http://llvm.org/svn/llvm-project/cfe/trunk/tools/clang-format/clang-format-diff.py -o utils/clang-format-diff.py; + +echo $(date): Check formatting... +./utils/check_code_format.sh; +echo $(date): check completed. diff --git a/third_party/spirv-tools/kokoro/check-format/presubmit_check_format.cfg b/third_party/spirv-tools/kokoro/check-format/presubmit_check_format.cfg new file mode 100644 index 0000000..1993289 --- /dev/null +++ b/third_party/spirv-tools/kokoro/check-format/presubmit_check_format.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/check-format/build.sh" diff --git a/third_party/spirv-tools/kokoro/img/linux.png b/third_party/spirv-tools/kokoro/img/linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ff066d979e1393d1f8d57198025c9a79aa4999c0 GIT binary patch literal 17369 zcmeI4c~}$I7Qlmm3L;1?h$0xGA{9a=8zgB6B4JZD0R5e(Z&;u2?WAuKVNWU0t1Q{-p~I-xp*t>b67% zhkb`MWM*Z3>oD~Y3$bn=rLXUqmg?tWq^}Q|=74JYf*9X?%^+vs38a+Eo5B-OyznLJ%JVyeE1pcoj^6aI2_2+UfJjUwNLN`*d}1`FXSU zMdlXkYj?KqjIOuOv(AfNzh~X1ja>Tz3YnZfbGQGzsON=|+iO1UnY8lD)hlPE&5{;e zD*tfLmOZb-KHhV!%)ET>7hN6?GM`V`8W!c~=208o?PFqT_0xm~tDmsF;mAGhNrq3Cc|clK~a>kQtL^{*B5_d@TMoT zyDraTwkmmLq)Gbtb06A`6I)oAN14; zHyv!+?{GQnk9`r|~X>YbpaDVjFB)|C8!Q)MSo0p{>@5(Owly7;&x$Cf#qN}#Rt|D-M z!u)D!?d_cS5`PzI>yiWUFHI}9zD%xu%&w-yy-}&yt1@iG?)DkW8~h%G-Z?jA`>Vu^ zi(Zdr`|O{x=v8w1xqy;|$xm9YhaO*X6TAjhO?$Z=K0YQj^=abkghxRr4U<4a_1RyQ#go& zcvuM%Q{v+!3Qmd}N#mD;+^d@@B%-E^a;Y21LmiM95fDuDl*(Zu)7ga#QK?j-E8CgM zWYK9%Cn61?GARI?LIubGl|uzMG=SK9k$A?)9akl_!i;QA67teu}mqJN{DK|pg@|abR&_} zf%>oBdBw~614$IU>=25S6i`N?Is=rUjD%3XjVw_frx}_MqQG%*JSzQe`kD1Xf5BDi4!N<9OOtHgFIi=YV=u z5gh_RNGwsG4j*wTXrqCQ5BP)mpc3XGwytCV`C)`nSsZ}Hp)nnir3nB!PP9EyJ_4jd zu_$$r51LCD6xIDg8N%R555(NEJT@N~4hIGM~LW%0V z;UR%w1(H#PPNtE3Yj*!kFlKOTpCV4oL-Oh@6bm^_7;uGXR5n>ar9)%}8>W(3Y>-N( zi5RYc0DxI8Od6M>-t&mjz(~}dG_8YU=*|FZgzB{u5Ww-1D3l;F0YAQGJYr-#kc)r; z0zw*Dz!12S8B7L?%yw~gC5z~6fWZQ2bXOO;W%Qo{uGLd@fdSA}1b}3w z0Qtj!nE)9CX#kl9i>NNHBAP3U#_V&gWB!)wAh{T+`(T_-pFWy@Cd3eK-U{U_R_8Vq z1zw$vP&i?5>p*Tb6Aa!|wgKq9QKGo_RRZLKCR!uhUfr8?+k>J zX8*;B|3WGhVA5Yp1QS`%ARuCqnE;IJYykw3*)W?;772v_8-%Gewky<^=s%wbZG{Gy z9BO}s{%{GaA-(CPYrkvr{f99$Xu3i{iX zl&gNY;viKRsR*E^`XNX7TWtfYM%zC^T}|T>HT~HvI?II(AkW~))oZJ(N88T-H@3QZ zwC#ZDT!~o8L(XK)P*Ke~R_bR!?c+%(^11@^T$oHca=f9N(H6nt6gVz|FGij%6zUU! zflN$egH{Y_IR9OSI*MLHZL)M;EHuZNwiS78LEe}s{jW@Kopk-@(BFA8Fr&ZIE6Nuh z1O)_J#BpKsL1}SZC?MD(jtiR)N{iz{0l^k=T-bb2S{xS&2)2mh!sdh0;nXA zAlM>~3!4v0i{nB8!4`2`*nCi092W`*wus}x=7ZAWxKKc_MI0A4ACwlyg#v;t;<&K+ zptLwH6cB6?$A!%YrNwcffMAO_E^Iz1EshHX1Y5*$Ve>(0aa<@M*dmS#n-5Bh<3a(! z7I9qId{9~(7YYcrh>6R1;7iD`1o`H1GV&E;=J?2Jw zgTq3ns1=dyo}~7!t+jVbUzA?opF%m;^@{c6MHjE>S^8@h@@?Z%b`8|oJTWx(mXpdv zzU9z8O3&nJcV=ZCiy_Y{ea5w=*%*9X*;Z`-iBmSc_z%0L6uT26yRElJ85ORC^~hFZ zZAA`(0H#l25F5*dERHzY0b=kd0x?Rh zXA1Lgn#}w8Qsr*TV#x7GLg@_q`(4ooice2Jm$0!m*QVz?p!u$+y=BqJ%PWR0-xPRd zU3$Yz-W|)@iK{POe5hY|?ewrMw_GL76JKTI?q)SP&b(+!SP)0;_E#>mUvSh~Phxb> zr4cCDa>D+l$tuXL_TiB`thHmp9Rp|0tPHvUQ!aW=n#MZ2l%;5C+i$h>?ERg~L)JW{ zo5qYj`Zy8+O5siKiySL9jp(wO6Mp`bS%cewvJKCFUz6uyz0oXI+VCjp zlk#6p*4oX>88OGCb<(M-+N=J1y-f;M1ACcG_0IcMK@JXX6DN_b{9r*B7L2$Y$p}3u zZ{|ZH&OhjF5A`xa!p=M))t7Y8K6@QM!6xePj@aF2y>jPwyl8yc^ujI2arJ>3BfIAY zhHhz}LAEzWb!CpWFT9|N*!InduUJ)nxlFL``>#n061d+HZZ_B0=_D!l=T_fxkc6@A;wf(k4ZU=N426bmPoB#Mx^m*4vYdg8!>*M`T zK3zEWRBQdPZGpgoqN^si?J7^yAikpgyY9+O@XzBT&2T+TxZLx4Kra#m7z(3>O5a-B16V`2acG z`#a~{`JQuc=AT)gmh$X4??7(|g2t(ml^M)unElt&o%tKuYr4*Sc$t#3Ef6$jqW#wm z+R@+-L36@LO%|P{o{wrNLllNnc|?@WU}Dq|B%5n9VcJ51=H(Fuq*2cQ@k9%sN8)n+ zLaAD)HYE@`GP%@DWR|9Aw55gGSe!ps;VrYFOaKExV?3KdZ?vE`Io}Z%Wo-K}%;z~I zbfKIdXHUq>Qm63}C^Ny6Mnwy>2!ilpVxti09I;3m$rA|?DJ+bI5urecpokC^33>e& zU*XMIGBci!W+;>T(=q?a`8t|5p)hQ)Ny^Bx$HnrfVp`7U+Y`C2{`)eRT#1a9es&B+*oK*4BuWSm=Y(rr zKBi)`-mx@X3ln<6Kp1HYn3$=FTJ6d`?0OA`VN+Y^#1aNU zKhj}6Et=&f0?r^TRIyo0B$hC@5Zd>|g7#ArgnbYzS`$oxn& z$Q)ES2Ng+YGp#8U0zsV0Au-exPUbHk6hjoPVOm-mi;9I{3_v+F2D5{3jK+Rd6i^Gq z!L?{UWj0{6f;3!H21yV7B3nZfa7y%|0<_q$0Tr8Fn;uu6LazIq8LHEZ(D=BTUeZTaF z#(!gI!2Pk%sb-Qn>o7f_v9JEOrWhv7W2ur!3)7*?fzD<6*KwL<5=DbYA5G{<$Ejq( z%of7F=j8lHo6dENxlSp&k7MtmnAU!tDYW)2LEtiY(CEOjT%P@I_Qsm;Lm09t@exg94$5mP*A^89a1&s7D%UBlKBHlG(u)dtXYJ`*QdUofy_J zdn89-qTkV3D&Q;KQAy`0^T5D7tH7>@mBB@JZH3?27`UU~IWQQGO9IkhwK-g@h(G~{ z3#7qnbGTR$fdUQ}NQ2epaIqo+1spDr2CL2CVnqZBI9wnNR-41ciU<^NxIh}LHiwH9 z5h&nrfizfc4i_sTP{82=X|UQHE>=XKfWrmSV6{11tcXAXhYO^^YIC?)5rF~@7f6HE z=5Vnh0tFl{kOr&G;bKJu3OHOK4OW}O#fk_NaJWDktTu;>6%i=laDg;fZ4MVJB2d8L z0%@??94=Nwpn$^#(qOeYT&##d0f!5u!D@53SP_8&4i`v+)#h-qA_4^*E|3PR{X|^e z1F!!PM&_MAEAw)nX^Bq`^I{-Ro1CGBpwgKTw0aE${rr&m?1G?TAq0J&3qfct1Wlo; zmwu20K~KG_QpRa)9WT}7==~P>pIf!m^Jt{cJoS}*eq(uOCnIe)7KK%W`Dp@^zb>0M z@snI=0>5d-jK6r!?rpBhs@NbM-TCLSAyrTyS>e&yXi#=$r0XKmjdfK`xGw|`}Bo2q{uJc)ZN`YsX^j&{p4q~`**$N4F%21XiwF)(=9v# zlIrgq@5}yc=QAZg>uS@c&fJ<4gfxA=*UO{onpdjkurTQr)%Fw0=TrT3ubw$KX^l!1 zaPZrNrKc5N%TLkb_mTQ%rxdkc%c(AUM-{O6lRceh{F)ofCw5%m_aIw#k1{u{*(NIT zTX^fIfHBtfH>)nT;_Y7fZdd$+YE!ge%cdeDa-lRt+%!D<*nJvS}!iEyE%dS z;Sbj@HB~3&NuD}v-eBG_>Yz&7GJnonONw@Mn|oF%Lq43aHtkS+b%;J9;c{f-l<-e| z7yZe-{&-!+iSi{r`*)7*X#6SSwF`}hb3NX4Z`z^`!i;oJoDQ2ZyUhK^z8I-`{_F42 z>*HUm7dN??Hgr8NIaacM=eCzO8(T3g#9kgwkJEii2Q_cY<=&qSF! zB^B=DpVPmD9IitFw>5r*zth;y5X$wpD zSNhzSVO_a<;}-+uXl2@zn z{o+OX*H{1G)*P{Cb7gn&x~`co^aaG%9%#Naxg{_3zV74N&fBS(XTF+-mqo|ae*n1P_WlzcCk?1)VQm7OIZGGQ{ zJAPlDu3sCvFP-?OcNP5ky@Nd|@ftzlRb}3P;9^bJimMgEjiuh@9hJu(7S&IhR`lTd za<3W6a;w_v0r|9rdp#&`_Hr|4KS%uX&aIQg_)G7ri=sydV}a?R6A4Kw^7Qf4DL39W i*G&F$T*|439#GI-UY#j*jF0_YU{zv@@=$zk`Tqdo!tUBpZR00s`Rd>nw1 zHJ$(vRD~xb8WNS$q=+^>07kXRSb!;ANA?B)S%^soBWaj{m5im}nqcSPqYMNB`RZBp;|r05(bDk2n0c_KuG{36!UpPe-;mfgj`UF3`a`A>{Nq6C*^XDMq_}HAE4Eza3P6A!UcI;9*;xz;1Fhw0XA_o zgpbWhmmdX2AbMP9z_l8d#V?$!%`gPB*_J@vN9VZGb=`q9L?=6vBG&}#xKIGd?aK&7 zx@~kBdbRb|P=t%Av2;vhAV@o?PdiI{=n*U|lN^dqU!ynRs-DSt#*;>JzkDwcM6vFq+%Nt{@aPz|ZBy zzOS7h6p^a5`gGVJ$J5~yjH}b6$hbX?c0IfEN*bzFYxU%9VRA^u?W@_&%9?A^NDX0t zH3$}|kdqAoIF3p&F@%Zu0ue_5ffz>s!XQV?OU5_|0`pM>NfyJP#0pWVv^VeNTA@WU zEbE1IZ7VIP79rjD+!iJjsUTDgbCP+1Vh#ia!W^+cq~eG%P!cG@uw*_c?h0h*tXH6D zJx*3WSZ&wGG8U8!CPFbtm@JZT_=t!ciwG5QV3AP8353Y8B_k3T69#sL=)+B~P!Tvm z=BwE*kI8Jd*0y-;?PpuN6l&aBqja#Iz$_~;nBBDmyNg#9-z&57vAWF!^Vq+qC=+cmbXhX19xd%l|ZuP&lK_Lu*QxwEatXDee@ zwe4L=WtPVaDOrWdiU3=yAGwM3YU{}wTmM8$HC1O=`v>ws@*p2V$)nTMUQIQfNpEVe zrkeIxzNx_ta$aXNO0%7nzG_56!L{KF149z5rdmBb)o_%_ff*K9|*{C zS#79_J`HE~XK1JBG_*~X-GjBYg4$Zi$9D1;1GoDpL$Bi6y$k)37d<2TBfV05F+nIm z=tTw>Js*me!9@W=FEY63`B1bBE(#EOk-u>mg+)M2yTUYIt`Iml;{b5XQU#KL!v z9sIP`cuW$|dgMtl{%Q(jY(2K>y8QOf^4mngmb%!RZ;Wz16FXMnxBzy%f7jz8@!1QX zNb1b5%cj;{REH0Nj(I*XGWmt3|YP!z-LyiT=Oci`(O_8Js`JBc{XT`+fa*VAh!6BfxE4?$QA* z&6CTAmRGLV?esq3`9^6&?P;&~7M=E-GREw_B(_Q``SI=R7xMi^KlA zh9`$gPtErpAQ;x-arx4f`{5f!1or5z-}Y17y&(_Qn)kdk_+qfz!7vG{t)oOyuk?5O z9dwafVe%l5`%Gbl|b~tcH&b=_S{ewl5{9Tr>$3@Z{BCZWxvPTk&I8D3*9&HHj?rnmD;k%LO!|E%G|j;Oyr z`qWY2`HtZ5YyOD$BF7~Sx#ho>?1H-5Z$f$H@4 zP}+s@_2>3YIS5{uZ9E=PUdJx3fWC?_3&!YXUK#S$+7Yqp>ggvr(Wc|#H!^-0#Z}B< zDc#o*`jK-`^@f9wujd@&-!B_9uw_o3V3X66N14@C*-jf8d_K6>^pW}M+4a23cVx~3 zN2q{2tk~>7_s-a>Bb*AhE)E@0QRn{iJFh(c_FXcqe2?E97r*8j?<0SxVSHWS2#tex zhv>p4ufnivPE9Ej5;F7d)cz^z>7$byujb!MV>>VKeym!)wRC>Lz48-o(_Qw3Oyn+I zw=DeQ<1Oo}L$)=xR!^Rz9TDjW3ilT@`ft79UucXEM#%3G<9F1gFY;2MWb11>xK z@V*4j-@cYdrG$6zH?^je9^j-G!`-vLac9eTMuck&B~Ih!9eooE}@URgV1?x0`ImoGFHtxa`1`R(}S1(MZ81F;8( z)}>#2F=Nti-`r3R-KqcZ$GO43WvR~}UG?6uFKgmvd!JRbpJ>C1n{J&e&pwaGrFbVE zXhMj|t@kRTb&0`$Zwh>(+cCXySKeJOub6A`*(c*iHo1 literal 0 HcmV?d00001 diff --git a/third_party/spirv-tools/kokoro/linux-clang-asan/build.sh b/third_party/spirv-tools/kokoro/linux-clang-asan/build.sh new file mode 100644 index 0000000..8f86e6e --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-asan/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh ASAN clang diff --git a/third_party/spirv-tools/kokoro/linux-clang-asan/continuous.cfg b/third_party/spirv-tools/kokoro/linux-clang-asan/continuous.cfg new file mode 100644 index 0000000..3a98fc7 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-asan/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-asan/build.sh" diff --git a/third_party/spirv-tools/kokoro/linux-clang-asan/presubmit.cfg b/third_party/spirv-tools/kokoro/linux-clang-asan/presubmit.cfg new file mode 100644 index 0000000..ceac44b --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-asan/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-asan/build.sh" diff --git a/third_party/spirv-tools/kokoro/linux-clang-debug/build.sh b/third_party/spirv-tools/kokoro/linux-clang-debug/build.sh new file mode 100644 index 0000000..11b2968 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-debug/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG clang diff --git a/third_party/spirv-tools/kokoro/linux-clang-debug/continuous.cfg b/third_party/spirv-tools/kokoro/linux-clang-debug/continuous.cfg new file mode 100644 index 0000000..3350f3b --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-debug/continuous.cfg @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-debug/build.sh" + +action { + define_artifacts { + regex: "install.tgz" + } +} diff --git a/third_party/spirv-tools/kokoro/linux-clang-debug/presubmit.cfg b/third_party/spirv-tools/kokoro/linux-clang-debug/presubmit.cfg new file mode 100644 index 0000000..5011b44 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-debug/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-debug/build.sh" diff --git a/third_party/spirv-tools/kokoro/linux-clang-release-bazel/build.sh b/third_party/spirv-tools/kokoro/linux-clang-release-bazel/build.sh new file mode 100644 index 0000000..05a9bbb --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-release-bazel/build.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +CC=clang +CXX=clang++ +SRC=$PWD/github/SPIRV-Tools + +cd $SRC +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 + +gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-linux-x86_64 . +chmod +x bazel-0.29.1-linux-x86_64 + +echo $(date): Build everything... +./bazel-0.29.1-linux-x86_64 build :all +echo $(date): Build completed. + +echo $(date): Starting bazel test... +./bazel-0.29.1-linux-x86_64 test :all +echo $(date): Bazel test completed. diff --git a/third_party/spirv-tools/kokoro/linux-clang-release-bazel/continuous.cfg b/third_party/spirv-tools/kokoro/linux-clang-release-bazel/continuous.cfg new file mode 100644 index 0000000..8b33a24 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-release-bazel/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-release-bazel/build.sh" diff --git a/third_party/spirv-tools/kokoro/linux-clang-release-bazel/presubmit.cfg b/third_party/spirv-tools/kokoro/linux-clang-release-bazel/presubmit.cfg new file mode 100644 index 0000000..c7a2e6f --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-release-bazel/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-release-bazel/build.sh" diff --git a/third_party/spirv-tools/kokoro/linux-clang-release/build.sh b/third_party/spirv-tools/kokoro/linux-clang-release/build.sh new file mode 100644 index 0000000..4764331 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-release/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE clang diff --git a/third_party/spirv-tools/kokoro/linux-clang-release/continuous.cfg b/third_party/spirv-tools/kokoro/linux-clang-release/continuous.cfg new file mode 100644 index 0000000..8b075c6 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-release/continuous.cfg @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-release/build.sh" + +action { + define_artifacts { + regex: "install.tgz" + } +} diff --git a/third_party/spirv-tools/kokoro/linux-clang-release/presubmit.cfg b/third_party/spirv-tools/kokoro/linux-clang-release/presubmit.cfg new file mode 100644 index 0000000..b7b9b55 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-clang-release/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-clang-release/build.sh" diff --git a/third_party/spirv-tools/kokoro/linux-gcc-debug/build.sh b/third_party/spirv-tools/kokoro/linux-gcc-debug/build.sh new file mode 100644 index 0000000..3ef1e25 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-gcc-debug/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG gcc diff --git a/third_party/spirv-tools/kokoro/linux-gcc-debug/continuous.cfg b/third_party/spirv-tools/kokoro/linux-gcc-debug/continuous.cfg new file mode 100644 index 0000000..d9579d5 --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-gcc-debug/continuous.cfg @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-gcc-debug/build.sh" + +action { + define_artifacts { + regex: "install.tgz" + } +} diff --git a/third_party/spirv-tools/kokoro/linux-gcc-debug/presubmit.cfg b/third_party/spirv-tools/kokoro/linux-gcc-debug/presubmit.cfg new file mode 100644 index 0000000..2d9fe5c --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-gcc-debug/presubmit.cfg @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-gcc-debug/build.sh" + diff --git a/third_party/spirv-tools/kokoro/linux-gcc-release/build.sh b/third_party/spirv-tools/kokoro/linux-gcc-release/build.sh new file mode 100644 index 0000000..3e97d8d --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-gcc-release/build.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc diff --git a/third_party/spirv-tools/kokoro/linux-gcc-release/continuous.cfg b/third_party/spirv-tools/kokoro/linux-gcc-release/continuous.cfg new file mode 100644 index 0000000..ead07bf --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-gcc-release/continuous.cfg @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/linux-gcc-release/build.sh" + +action { + define_artifacts { + regex: "install.tgz" + } +} diff --git a/third_party/spirv-tools/kokoro/linux-gcc-release/presubmit.cfg b/third_party/spirv-tools/kokoro/linux-gcc-release/presubmit.cfg new file mode 100644 index 0000000..c249a5a --- /dev/null +++ b/third_party/spirv-tools/kokoro/linux-gcc-release/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/linux-gcc-release/build.sh" diff --git a/third_party/spirv-tools/kokoro/macos-clang-debug/build.sh b/third_party/spirv-tools/kokoro/macos-clang-debug/build.sh new file mode 100644 index 0000000..8d9a062 --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-debug/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# MacOS Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/macos/build.sh Debug + diff --git a/third_party/spirv-tools/kokoro/macos-clang-debug/continuous.cfg b/third_party/spirv-tools/kokoro/macos-clang-debug/continuous.cfg new file mode 100644 index 0000000..f5f274a --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-debug/continuous.cfg @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/macos-clang-debug/build.sh" + +action { + define_artifacts { + regex: "install.tgz" + } +} diff --git a/third_party/spirv-tools/kokoro/macos-clang-debug/presubmit.cfg b/third_party/spirv-tools/kokoro/macos-clang-debug/presubmit.cfg new file mode 100644 index 0000000..1d2f60d --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-debug/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/macos-clang-debug/build.sh" diff --git a/third_party/spirv-tools/kokoro/macos-clang-release-bazel/build.sh b/third_party/spirv-tools/kokoro/macos-clang-release-bazel/build.sh new file mode 100644 index 0000000..d2a516f --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-release-bazel/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +CC=clang +CXX=clang++ +SRC=$PWD/github/SPIRV-Tools + +cd $SRC +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 + +# Get bazel 0.29.1. +gsutil cp gs://bazel/0.29.1/release/bazel-0.29.1-darwin-x86_64 . +chmod +x bazel-0.29.1-darwin-x86_64 + +echo $(date): Build everything... +./bazel-0.29.1-darwin-x86_64 build :all +echo $(date): Build completed. + +echo $(date): Starting bazel test... +./bazel-0.29.1-darwin-x86_64 test :all +echo $(date): Bazel test completed. diff --git a/third_party/spirv-tools/kokoro/macos-clang-release-bazel/continuous.cfg b/third_party/spirv-tools/kokoro/macos-clang-release-bazel/continuous.cfg new file mode 100644 index 0000000..9765489 --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-release-bazel/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/macos-clang-release-bazel/build.sh" diff --git a/third_party/spirv-tools/kokoro/macos-clang-release-bazel/presubmit.cfg b/third_party/spirv-tools/kokoro/macos-clang-release-bazel/presubmit.cfg new file mode 100644 index 0000000..3b13602 --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-release-bazel/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/macos-clang-release-bazel/build.sh" diff --git a/third_party/spirv-tools/kokoro/macos-clang-release/build.sh b/third_party/spirv-tools/kokoro/macos-clang-release/build.sh new file mode 100644 index 0000000..ccc8b16 --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-release/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# MacOS Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +SCRIPT_DIR=`dirname "$BASH_SOURCE"` +source $SCRIPT_DIR/../scripts/macos/build.sh RelWithDebInfo + diff --git a/third_party/spirv-tools/kokoro/macos-clang-release/continuous.cfg b/third_party/spirv-tools/kokoro/macos-clang-release/continuous.cfg new file mode 100644 index 0000000..710185e --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-release/continuous.cfg @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/macos-clang-release/build.sh" + +action { + define_artifacts { + regex: "install.tgz" + } +} diff --git a/third_party/spirv-tools/kokoro/macos-clang-release/presubmit.cfg b/third_party/spirv-tools/kokoro/macos-clang-release/presubmit.cfg new file mode 100644 index 0000000..dbaa266 --- /dev/null +++ b/third_party/spirv-tools/kokoro/macos-clang-release/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/macos-clang-release/build.sh" diff --git a/third_party/spirv-tools/kokoro/ndk-build/build.sh b/third_party/spirv-tools/kokoro/ndk-build/build.sh new file mode 100644 index 0000000..f1f167d --- /dev/null +++ b/third_party/spirv-tools/kokoro/ndk-build/build.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +BUILD_ROOT=$PWD +SRC=$PWD/github/SPIRV-Tools + +# Get NINJA. +wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip +unzip -q ninja-linux.zip +export PATH="$PWD:$PATH" + +# NDK Path +export ANDROID_NDK=/opt/android-ndk-r15c + +# Get the dependencies. +cd $SRC +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 + +mkdir build && cd $SRC/build +mkdir libs +mkdir app + +# Invoke the build. +BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} +echo $(date): Starting ndk-build ... +$ANDROID_NDK/ndk-build \ + -C $SRC/android_test \ + NDK_PROJECT_PATH=. \ + NDK_LIBS_OUT=./libs \ + NDK_APP_OUT=./app \ + -j8 + +echo $(date): ndk-build completed. + diff --git a/third_party/spirv-tools/kokoro/ndk-build/continuous.cfg b/third_party/spirv-tools/kokoro/ndk-build/continuous.cfg new file mode 100644 index 0000000..b908a48 --- /dev/null +++ b/third_party/spirv-tools/kokoro/ndk-build/continuous.cfg @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +# +build_file: "SPIRV-Tools/kokoro/ndk-build/build.sh" diff --git a/third_party/spirv-tools/kokoro/ndk-build/presubmit.cfg b/third_party/spirv-tools/kokoro/ndk-build/presubmit.cfg new file mode 100644 index 0000000..3c1be4b --- /dev/null +++ b/third_party/spirv-tools/kokoro/ndk-build/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/ndk-build/build.sh" diff --git a/third_party/spirv-tools/kokoro/scripts/linux/build.sh b/third_party/spirv-tools/kokoro/scripts/linux/build.sh new file mode 100644 index 0000000..347f353 --- /dev/null +++ b/third_party/spirv-tools/kokoro/scripts/linux/build.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Linux Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +BUILD_ROOT=$PWD +SRC=$PWD/github/SPIRV-Tools +CONFIG=$1 +COMPILER=$2 + +SKIP_TESTS="False" +BUILD_TYPE="Debug" + +CMAKE_C_CXX_COMPILER="" +if [ $COMPILER = "clang" ] +then + PATH=/usr/lib/llvm-3.8/bin:$PATH + CMAKE_C_CXX_COMPILER="-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++" +fi + +# Possible configurations are: +# ASAN, COVERAGE, RELEASE, DEBUG, DEBUG_EXCEPTION, RELEASE_MINGW + +if [ $CONFIG = "RELEASE" ] || [ $CONFIG = "RELEASE_MINGW" ] +then + BUILD_TYPE="RelWithDebInfo" +fi + +ADDITIONAL_CMAKE_FLAGS="" +if [ $CONFIG = "ASAN" ] +then + ADDITIONAL_CMAKE_FLAGS="-DSPIRV_USE_SANITIZER=address,bounds,null" + [ $COMPILER = "clang" ] || { echo "$CONFIG requires clang"; exit 1; } +elif [ $CONFIG = "COVERAGE" ] +then + ADDITIONAL_CMAKE_FLAGS="-DENABLE_CODE_COVERAGE=ON" + SKIP_TESTS="True" +elif [ $CONFIG = "DEBUG_EXCEPTION" ] +then + ADDITIONAL_CMAKE_FLAGS="-DDISABLE_EXCEPTIONS=ON -DDISABLE_RTTI=ON" +elif [ $CONFIG = "RELEASE_MINGW" ] +then + ADDITIONAL_CMAKE_FLAGS="-Dgtest_disable_pthreads=ON -DCMAKE_TOOLCHAIN_FILE=$SRC/cmake/linux-mingw-toolchain.cmake" + SKIP_TESTS="True" +fi + +# Get NINJA. +wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip +unzip -q ninja-linux.zip +export PATH="$PWD:$PATH" + +cd $SRC +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 +git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf + +mkdir build && cd $SRC/build + +# Invoke the build. +BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} +echo $(date): Starting build... +cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/python3 -GNinja -DCMAKE_INSTALL_PREFIX=$KOKORO_ARTIFACTS_DIR/install -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DRE2_BUILD_TESTING=OFF -DSPIRV_BUILD_FUZZER=ON $ADDITIONAL_CMAKE_FLAGS $CMAKE_C_CXX_COMPILER .. + +echo $(date): Build everything... +ninja +echo $(date): Build completed. + +if [ $CONFIG = "COVERAGE" ] +then + echo $(date): Check coverage... + ninja report-coverage + echo $(date): Check coverage completed. +fi + +echo $(date): Starting ctest... +if [ $SKIP_TESTS = "False" ] +then + ctest -j4 --output-on-failure --timeout 300 +fi +echo $(date): ctest completed. + +# Package the build. +ninja install +cd $KOKORO_ARTIFACTS_DIR +tar czf install.tgz install diff --git a/third_party/spirv-tools/kokoro/scripts/macos/build.sh b/third_party/spirv-tools/kokoro/scripts/macos/build.sh new file mode 100644 index 0000000..44c9a41 --- /dev/null +++ b/third_party/spirv-tools/kokoro/scripts/macos/build.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# MacOS Build Script. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +BUILD_ROOT=$PWD +SRC=$PWD/github/SPIRV-Tools +BUILD_TYPE=$1 + +# Get NINJA. +wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip +unzip -q ninja-mac.zip +chmod +x ninja +export PATH="$PWD:$PATH" + +cd $SRC +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 +git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf + +mkdir build && cd $SRC/build + +# Invoke the build. +BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} +echo $(date): Starting build... +# We need Python 3. At the moment python3.7 is the newest Python on Kokoro. +cmake \ + -GNinja \ + -DCMAKE_INSTALL_PREFIX=$KOKORO_ARTIFACTS_DIR/install \ + -DPYTHON_EXECUTABLE:FILEPATH=/usr/local/bin/python3.7 \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ + -DSPIRV_BUILD_FUZZER=ON \ + .. + +echo $(date): Build everything... +ninja +echo $(date): Build completed. + +echo $(date): Starting ctest... +ctest -j4 --output-on-failure --timeout 300 +echo $(date): ctest completed. + +# Package the build. +ninja install +cd $KOKORO_ARTIFACTS_DIR +tar czf install.tgz install + diff --git a/third_party/spirv-tools/kokoro/scripts/windows/build.bat b/third_party/spirv-tools/kokoro/scripts/windows/build.bat new file mode 100644 index 0000000..fa7a71a --- /dev/null +++ b/third_party/spirv-tools/kokoro/scripts/windows/build.bat @@ -0,0 +1,109 @@ +:: Copyright (c) 2018 Google LLC. +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +:: +:: Windows Build Script. + +@echo on + +set BUILD_ROOT=%cd% +set SRC=%cd%\github\SPIRV-Tools +set BUILD_TYPE=%1 +set VS_VERSION=%2 + +:: Force usage of python 3.6 +set PATH=C:\python36;%PATH% + +cd %SRC% +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 +git clone --depth=1 --branch v3.13.0 https://github.com/protocolbuffers/protobuf external/protobuf + +:: ######################################### +:: set up msvc build env +:: ######################################### +if %VS_VERSION% == 2017 ( + call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 + echo "Using VS 2017..." +) else if %VS_VERSION% == 2015 ( + call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 + echo "Using VS 2015..." +) else if %VS_VERSION% == 2013 ( + call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x64 + echo "Using VS 2013..." +) + +cd %SRC% +mkdir build +cd build + +:: ######################################### +:: Start building. +:: ######################################### +echo "Starting build... %DATE% %TIME%" +if "%KOKORO_GITHUB_COMMIT%." == "." ( + set BUILD_SHA=%KOKORO_GITHUB_PULL_REQUEST_COMMIT% +) else ( + set BUILD_SHA=%KOKORO_GITHUB_COMMIT% +) + +set CMAKE_FLAGS=-DCMAKE_INSTALL_PREFIX=%KOKORO_ARTIFACTS_DIR%\install -GNinja -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DRE2_BUILD_TESTING=OFF -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe + +:: Skip building tests for VS2013 +if %VS_VERSION% == 2013 ( + set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_SKIP_TESTS=ON +) + +:: Skip building spirv-fuzz for VS2013; it relies on protobufs which VS2013 cannot handle. +if %VS_VERSION% NEQ 2013 ( + set CMAKE_FLAGS=%CMAKE_FLAGS% -DSPIRV_BUILD_FUZZER=ON +) + +cmake %CMAKE_FLAGS% .. + +if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% + +echo "Build everything... %DATE% %TIME%" +ninja +if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% +echo "Build Completed %DATE% %TIME%" + +:: This lets us use !ERRORLEVEL! inside an IF ... () and get the actual error at that point. +setlocal ENABLEDELAYEDEXPANSION + +:: ################################################ +:: Run the tests (We no longer run tests on VS2013) +:: ################################################ +echo "Running Tests... %DATE% %TIME%" +if %VS_VERSION% NEQ 2013 ( + ctest -C %BUILD_TYPE% --output-on-failure --timeout 300 + if !ERRORLEVEL! NEQ 0 exit /b !ERRORLEVEL! +) +echo "Tests Completed %DATE% %TIME%" + +:: ################################################ +:: Install and package. +:: ################################################ +ninja install +cd %KOKORO_ARTIFACTS_DIR% +zip -r install.zip install + +:: Clean up some directories. +rm -rf %SRC%\build +rm -rf %SRC%\external + +exit /b 0 + diff --git a/third_party/spirv-tools/kokoro/shaderc-smoketest/build.sh b/third_party/spirv-tools/kokoro/shaderc-smoketest/build.sh new file mode 100644 index 0000000..0856c9b --- /dev/null +++ b/third_party/spirv-tools/kokoro/shaderc-smoketest/build.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Fail on any error. +set -e +# Display commands being run. +set -x + +BUILD_ROOT=$PWD +GITHUB_DIR=$BUILD_ROOT/github + +SKIP_TESTS="False" +BUILD_TYPE="Release" + +# Get NINJA. +wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip +unzip -q ninja-linux.zip +export PATH="$PWD:$PATH" + +# Get shaderc. +cd $GITHUB_DIR +git clone https://github.com/google/shaderc.git +SHADERC_DIR=$GITHUB_DIR/shaderc +cd $SHADERC_DIR/third_party + +# Get shaderc dependencies. Link the appropriate SPIRV-Tools. +git clone https://github.com/google/googletest.git +git clone https://github.com/KhronosGroup/glslang.git +ln -s $GITHUB_DIR/SPIRV-Tools spirv-tools +git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-headers +git clone https://github.com/google/re2 +git clone https://github.com/google/effcee + +cd $SHADERC_DIR +mkdir build +cd $SHADERC_DIR/build + +# Invoke the build. +BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT} +echo $(date): Starting build... +cmake -GNinja -DRE2_BUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=$BUILD_TYPE .. + +echo $(date): Build glslang... +ninja glslangValidator + +echo $(date): Build everything... +ninja +echo $(date): Build completed. + +echo $(date): Check Shaderc for copyright notices... +ninja check-copyright + +echo $(date): Starting ctest... +if [ $SKIP_TESTS = "False" ] +then + ctest --output-on-failure -j4 +fi +echo $(date): ctest completed. + diff --git a/third_party/spirv-tools/kokoro/shaderc-smoketest/continuous.cfg b/third_party/spirv-tools/kokoro/shaderc-smoketest/continuous.cfg new file mode 100644 index 0000000..ee151ae --- /dev/null +++ b/third_party/spirv-tools/kokoro/shaderc-smoketest/continuous.cfg @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/shaderc-smoketest/build.sh" + diff --git a/third_party/spirv-tools/kokoro/shaderc-smoketest/presubmit.cfg b/third_party/spirv-tools/kokoro/shaderc-smoketest/presubmit.cfg new file mode 100644 index 0000000..4f2ed21 --- /dev/null +++ b/third_party/spirv-tools/kokoro/shaderc-smoketest/presubmit.cfg @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/shaderc-smoketest/build.sh" + diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2013-release/build.bat b/third_party/spirv-tools/kokoro/windows-msvc-2013-release/build.bat new file mode 100644 index 0000000..e77172a --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2013-release/build.bat @@ -0,0 +1,24 @@ +:: Copyright (c) 2018 Google LLC. +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +:: +:: Windows Build Script. + +@echo on + +:: Find out the directory of the common build script. +set SCRIPT_DIR=%~dp0 + +:: Call with correct parameter +call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2013 + diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2013-release/continuous.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2013-release/continuous.cfg new file mode 100644 index 0000000..5dfcba6 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2013-release/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2013-release/build.bat" diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2013-release/presubmit.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2013-release/presubmit.cfg new file mode 100644 index 0000000..7d3b238 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2013-release/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2013-release/build.bat" diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/build.bat b/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/build.bat new file mode 100644 index 0000000..2f721af --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/build.bat @@ -0,0 +1,60 @@ +:: Copyright (c) 2019 Google LLC. +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +:: +:: Windows Build Script. + +@echo on + +set SRC=%cd%\github\SPIRV-Tools + +:: Force usage of python 3.6 +set PATH=C:\python36;%PATH% + +:: Get dependencies +cd %SRC% +git clone --depth=1 https://github.com/KhronosGroup/SPIRV-Headers external/spirv-headers +git clone https://github.com/google/googletest external/googletest +cd external && cd googletest && git reset --hard 1fb1bb23bb8418dc73a5a9a82bbed31dc610fec7 && cd .. && cd .. +git clone --depth=1 https://github.com/google/effcee external/effcee +git clone --depth=1 https://github.com/google/re2 external/re2 + +:: REM Install Bazel. +wget -q https://github.com/bazelbuild/bazel/releases/download/0.29.1/bazel-0.29.1-windows-x86_64.zip +unzip -q bazel-0.29.1-windows-x86_64.zip + +:: Set up MSVC +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 +set BAZEL_VS=C:\Program Files (x86)\Microsoft Visual Studio 14.0 +set BAZEL_VC=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC +set BAZEL_SH=c:\tools\msys64\usr\bin\bash.exe +set BAZEL_PYTHON=c:\tools\python2\python.exe + +:: ######################################### +:: Start building. +:: ######################################### +echo "Build everything... %DATE% %TIME%" +bazel.exe build :all +if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% +echo "Build Completed %DATE% %TIME%" + +:: ############## +:: Run the tests +:: ############## +echo "Running Tests... %DATE% %TIME%" +bazel.exe test :all +if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% +echo "Tests Completed %DATE% %TIME%" + +exit /b 0 + diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/continuous.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/continuous.cfg new file mode 100644 index 0000000..f72cf05 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2015-release-bazel/build.bat" diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg new file mode 100644 index 0000000..148972c --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2015-release-bazel/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2019 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2015-release-bazel/build.bat" diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2015-release/build.bat b/third_party/spirv-tools/kokoro/windows-msvc-2015-release/build.bat new file mode 100644 index 0000000..c0e4bd3 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2015-release/build.bat @@ -0,0 +1,24 @@ +:: Copyright (c) 2018 Google LLC. +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +:: +:: Windows Build Script. + +@echo on + +:: Find out the directory of the common build script. +set SCRIPT_DIR=%~dp0 + +:: Call with correct parameter +call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2015 + diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2015-release/continuous.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2015-release/continuous.cfg new file mode 100644 index 0000000..3e47e52 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2015-release/continuous.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2015-release/build.bat" diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2015-release/presubmit.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2015-release/presubmit.cfg new file mode 100644 index 0000000..85a1625 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2015-release/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2015-release/build.bat" diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/build.bat b/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/build.bat new file mode 100644 index 0000000..25783a9 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/build.bat @@ -0,0 +1,23 @@ +:: Copyright (c) 2018 Google LLC. +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +:: +:: Windows Build Script. + +@echo on + +:: Find out the directory of the common build script. +set SCRIPT_DIR=%~dp0 + +:: Call with correct parameter +call %SCRIPT_DIR%\..\scripts\windows\build.bat Debug 2017 diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/continuous.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/continuous.cfg new file mode 100644 index 0000000..25c5e11 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/continuous.cfg @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-debug/build.bat" + +action { + define_artifacts { + regex: "install.zip" + } +} diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/presubmit.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/presubmit.cfg new file mode 100644 index 0000000..a7a553a --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2017-debug/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-debug/build.bat" diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2017-release/build.bat b/third_party/spirv-tools/kokoro/windows-msvc-2017-release/build.bat new file mode 100644 index 0000000..899fcbc --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2017-release/build.bat @@ -0,0 +1,24 @@ +:: Copyright (c) 2018 Google LLC. +:: +:: Licensed under the Apache License, Version 2.0 (the "License"); +:: you may not use this file except in compliance with the License. +:: You may obtain a copy of the License at +:: +:: http://www.apache.org/licenses/LICENSE-2.0 +:: +:: Unless required by applicable law or agreed to in writing, software +:: distributed under the License is distributed on an "AS IS" BASIS, +:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +:: See the License for the specific language governing permissions and +:: limitations under the License. +:: +:: Windows Build Script. + +@echo on + +:: Find out the directory of the common build script. +set SCRIPT_DIR=%~dp0 + +:: Call with correct parameter +call %SCRIPT_DIR%\..\scripts\windows\build.bat RelWithDebInfo 2017 + diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2017-release/continuous.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2017-release/continuous.cfg new file mode 100644 index 0000000..a9ac6ec --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2017-release/continuous.cfg @@ -0,0 +1,22 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Continuous build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-release/build.bat" + +action { + define_artifacts { + regex: "install.zip" + } +} diff --git a/third_party/spirv-tools/kokoro/windows-msvc-2017-release/presubmit.cfg b/third_party/spirv-tools/kokoro/windows-msvc-2017-release/presubmit.cfg new file mode 100644 index 0000000..5efd429 --- /dev/null +++ b/third_party/spirv-tools/kokoro/windows-msvc-2017-release/presubmit.cfg @@ -0,0 +1,16 @@ +# Copyright (c) 2018 Google LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Presubmit build configuration. +build_file: "SPIRV-Tools/kokoro/windows-msvc-2017-release/build.bat" diff --git a/third_party/spirv-tools/source/CMakeLists.txt b/third_party/spirv-tools/source/CMakeLists.txt new file mode 100644 index 0000000..38101ab --- /dev/null +++ b/third_party/spirv-tools/source/CMakeLists.txt @@ -0,0 +1,434 @@ +# Copyright (c) 2015-2016 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(GRAMMAR_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_grammar_tables.py") +set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_syntax.py") +set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py") +set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py") + +# For now, assume the DebugInfo grammar file is in the current directory. +# It might migrate to SPIRV-Headers. +set(DEBUGINFO_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.debuginfo.grammar.json") +set(CLDEBUGINFO100_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.opencl.debuginfo.100.grammar.json") + +# macro() definitions are used in the following because we need to append .inc +# file paths into some global lists (*_CPP_DEPENDS). And those global lists are +# later used by set_source_files_properties() calls. +# function() definitions are not suitable because they create new scopes. +macro(spvtools_core_tables CONFIG_VERSION) + set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(GRAMMAR_INSTS_INC_FILE "${spirv-tools_BINARY_DIR}/core.insts-${CONFIG_VERSION}.inc") + set(GRAMMAR_KINDS_INC_FILE "${spirv-tools_BINARY_DIR}/operand.kinds-${CONFIG_VERSION}.inc") + add_custom_command(OUTPUT ${GRAMMAR_INSTS_INC_FILE} ${GRAMMAR_KINDS_INC_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + --spirv-core-grammar=${GRAMMAR_JSON_FILE} + --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE} + --core-insts-output=${GRAMMAR_INSTS_INC_FILE} + --operand-kinds-output=${GRAMMAR_KINDS_INC_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} + ${GRAMMAR_JSON_FILE} + ${DEBUGINFO_GRAMMAR_JSON_FILE} + ${CLDEBUGINFO100_GRAMMAR_JSON_FILE} + COMMENT "Generate info tables for SPIR-V v${CONFIG_VERSION} core instructions and operands.") + list(APPEND OPCODE_CPP_DEPENDS ${GRAMMAR_INSTS_INC_FILE}) + list(APPEND OPERAND_CPP_DEPENDS ${GRAMMAR_KINDS_INC_FILE}) +endmacro(spvtools_core_tables) + +macro(spvtools_enum_string_mapping CONFIG_VERSION) + set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(GRAMMAR_EXTENSION_ENUM_INC_FILE "${spirv-tools_BINARY_DIR}/extension_enum.inc") + set(GRAMMAR_ENUM_STRING_MAPPING_INC_FILE "${spirv-tools_BINARY_DIR}/enum_string_mapping.inc") + add_custom_command(OUTPUT ${GRAMMAR_EXTENSION_ENUM_INC_FILE} + ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + --spirv-core-grammar=${GRAMMAR_JSON_FILE} + --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-cldebuginfo100-grammar=${CLDEBUGINFO100_GRAMMAR_JSON_FILE} + --extension-enum-output=${GRAMMAR_EXTENSION_ENUM_INC_FILE} + --enum-string-mapping-output=${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} + ${GRAMMAR_JSON_FILE} + ${DEBUGINFO_GRAMMAR_JSON_FILE} + ${CLDEBUGINFO100_GRAMMAR_JSON_FILE} + COMMENT "Generate enum-string mapping for SPIR-V v${CONFIG_VERSION}.") + list(APPEND EXTENSION_H_DEPENDS ${GRAMMAR_EXTENSION_ENUM_INC_FILE}) + list(APPEND ENUM_STRING_MAPPING_CPP_DEPENDS ${GRAMMAR_ENUM_STRING_MAPPING_INC_FILE}) +endmacro(spvtools_enum_string_mapping) + +macro(spvtools_vimsyntax CONFIG_VERSION CLVERSION) + set(GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(GLSL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.glsl.std.450.grammar.json") + set(OPENCL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.opencl.std.100.grammar.json") + set(VIMSYNTAX_FILE "${spirv-tools_BINARY_DIR}/spvasm.vim") + add_custom_command(OUTPUT ${VIMSYNTAX_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${VIMSYNTAX_PROCESSING_SCRIPT} + --spirv-core-grammar=${GRAMMAR_JSON_FILE} + --extinst-debuginfo-grammar=${DEBUGINFO_GRAMMAR_JSON_FILE} + --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} + --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE} + >${VIMSYNTAX_FILE} + DEPENDS ${VIMSYNTAX_PROCESSING_SCRIPT} ${GRAMMAR_JSON_FILE} + ${GLSL_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} ${DEBUGINFO_GRAMMAR_JSON_FILE} + COMMENT "Generate spvasm.vim: Vim syntax file for SPIR-V assembly.") +endmacro(spvtools_vimsyntax) + +macro(spvtools_glsl_tables CONFIG_VERSION) + set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(GLSL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.glsl.std.450.grammar.json") + set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/glsl.std.450.insts.inc") + add_custom_command(OUTPUT ${GRAMMAR_INC_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + --extinst-glsl-grammar=${GLSL_GRAMMAR_JSON_FILE} + --glsl-insts-output=${GRAMMAR_INC_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${GLSL_GRAMMAR_JSON_FILE} + COMMENT "Generate info tables for GLSL extended instructions and operands v${CONFIG_VERSION}.") + list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE}) +endmacro(spvtools_glsl_tables) + +macro(spvtools_opencl_tables CONFIG_VERSION) + set(CORE_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/spirv.core.grammar.json") + set(OPENCL_GRAMMAR_JSON_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/${CONFIG_VERSION}/extinst.opencl.std.100.grammar.json") + set(GRAMMAR_INC_FILE "${spirv-tools_BINARY_DIR}/opencl.std.insts.inc") + add_custom_command(OUTPUT ${GRAMMAR_INC_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + --extinst-opencl-grammar=${OPENCL_GRAMMAR_JSON_FILE} + --opencl-insts-output=${GRAMMAR_INC_FILE} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${CORE_GRAMMAR_JSON_FILE} ${OPENCL_GRAMMAR_JSON_FILE} + COMMENT "Generate info tables for OpenCL extended instructions and operands v${CONFIG_VERSION}.") + list(APPEND EXTINST_CPP_DEPENDS ${GRAMMAR_INC_FILE}) +endmacro(spvtools_opencl_tables) + +macro(spvtools_vendor_tables VENDOR_TABLE SHORT_NAME OPERAND_KIND_PREFIX) + set(INSTS_FILE "${spirv-tools_BINARY_DIR}/${VENDOR_TABLE}.insts.inc") + set(GRAMMAR_FILE "${SPIRV_HEADER_INCLUDE_DIR}/spirv/unified1/extinst.${VENDOR_TABLE}.grammar.json") + add_custom_command(OUTPUT ${INSTS_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${GRAMMAR_PROCESSING_SCRIPT} + --extinst-vendor-grammar=${GRAMMAR_FILE} + --vendor-insts-output=${INSTS_FILE} + --vendor-operand-kind-prefix=${OPERAND_KIND_PREFIX} + DEPENDS ${GRAMMAR_PROCESSING_SCRIPT} ${GRAMMAR_FILE} + COMMENT "Generate extended instruction tables for ${VENDOR_TABLE}.") + add_custom_target(spv-tools-${SHORT_NAME} DEPENDS ${INSTS_FILE}) + set_property(TARGET spv-tools-${SHORT_NAME} PROPERTY FOLDER "SPIRV-Tools build") + list(APPEND EXTINST_CPP_DEPENDS spv-tools-${SHORT_NAME}) +endmacro(spvtools_vendor_tables) + +macro(spvtools_extinst_lang_headers NAME GRAMMAR_FILE) + set(OUT_H ${spirv-tools_BINARY_DIR}/${NAME}.h) + add_custom_command(OUTPUT ${OUT_H} + COMMAND ${PYTHON_EXECUTABLE} ${LANG_HEADER_PROCESSING_SCRIPT} + --extinst-grammar=${GRAMMAR_FILE} + --extinst-output-path=${OUT_H} + DEPENDS ${LANG_HEADER_PROCESSING_SCRIPT} ${GRAMMAR_FILE} + COMMENT "Generate language specific header for ${NAME}.") + add_custom_target(spirv-tools-header-${NAME} DEPENDS ${OUT_H}) + set_property(TARGET spirv-tools-header-${NAME} PROPERTY FOLDER "SPIRV-Tools build") + list(APPEND EXTINST_CPP_DEPENDS spirv-tools-header-${NAME}) +endmacro(spvtools_extinst_lang_headers) + +spvtools_core_tables("unified1") +spvtools_enum_string_mapping("unified1") +spvtools_opencl_tables("unified1") +spvtools_glsl_tables("unified1") +spvtools_vendor_tables("spv-amd-shader-explicit-vertex-parameter" "spv-amd-sevp" "") +spvtools_vendor_tables("spv-amd-shader-trinary-minmax" "spv-amd-stm" "") +spvtools_vendor_tables("spv-amd-gcn-shader" "spv-amd-gs" "") +spvtools_vendor_tables("spv-amd-shader-ballot" "spv-amd-sb" "") +spvtools_vendor_tables("debuginfo" "debuginfo" "") +spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") +spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "") +spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) +spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) + +spvtools_vimsyntax("unified1" "1.0") +add_custom_target(spirv-tools-vimsyntax DEPENDS ${VIMSYNTAX_FILE}) +set_property(TARGET spirv-tools-vimsyntax PROPERTY FOLDER "SPIRV-Tools utilities") + +# Extract the list of known generators from the SPIR-V XML registry file. +set(GENERATOR_INC_FILE ${spirv-tools_BINARY_DIR}/generators.inc) +set(SPIRV_XML_REGISTRY_FILE ${SPIRV_HEADER_INCLUDE_DIR}/spirv/spir-v.xml) +add_custom_command(OUTPUT ${GENERATOR_INC_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${XML_REGISTRY_PROCESSING_SCRIPT} + --xml=${SPIRV_XML_REGISTRY_FILE} + --generator-output=${GENERATOR_INC_FILE} + DEPENDS ${XML_REGISTRY_PROCESSING_SCRIPT} ${SPIRV_XML_REGISTRY_FILE} + COMMENT "Generate tables based on the SPIR-V XML registry.") +list(APPEND OPCODE_CPP_DEPENDS ${GENERATOR_INC_FILE}) + +# The following .cpp files include the above generated .inc files. +# Add those .inc files as their dependencies. +# +# We need to wrap the .inc files with a custom target to avoid problems when +# multiple targets depend on the same custom command. +add_custom_target(core_tables + DEPENDS ${OPCODE_CPP_DEPENDS} ${OPERAND_CPP_DEPENDS}) +add_custom_target(enum_string_mapping + DEPENDS ${EXTENSION_H_DEPENDS} ${ENUM_STRING_MAPPING_CPP_DEPENDS}) +add_custom_target(extinst_tables + DEPENDS ${EXTINST_CPP_DEPENDS}) + +set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/extensions.h + PROPERTIES HEADER_FILE_ONLY TRUE) + +set(SPIRV_TOOLS_BUILD_VERSION_INC + ${spirv-tools_BINARY_DIR}/build-version.inc) +set(SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR + ${spirv-tools_SOURCE_DIR}/utils/update_build_version.py) +set(SPIRV_TOOLS_CHANGES_FILE + ${spirv-tools_SOURCE_DIR}/CHANGES) +add_custom_command(OUTPUT ${SPIRV_TOOLS_BUILD_VERSION_INC} + COMMAND ${PYTHON_EXECUTABLE} + ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} + ${spirv-tools_SOURCE_DIR} ${SPIRV_TOOLS_BUILD_VERSION_INC} + DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC_GENERATOR} + ${SPIRV_TOOLS_CHANGES_FILE} + COMMENT "Update build-version.inc in the SPIRV-Tools build directory (if necessary).") +# Convenience target for standalone generation of the build-version.inc file. +# This is not required for any dependence chain. +add_custom_target(spirv-tools-build-version + DEPENDS ${SPIRV_TOOLS_BUILD_VERSION_INC}) +set_property(TARGET spirv-tools-build-version PROPERTY FOLDER "SPIRV-Tools build") + +list(APPEND PCH_DEPENDS ${ENUM_STRING_MAPPING_CPP_DEPENDS} ${OPCODE_CPP_DEPENDS} ${OPERAND_CPP_DEPENDS} ${EXTENSION_H_DEPENDS} ${EXTINST_CPP_DEPENDS} ${SPIRV_TOOLS_BUILD_VERSION_INC}) +set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/pch_source.cpp + PROPERTIES OBJECT_DEPENDS "${PCH_DEPENDS}") + +add_subdirectory(opt) +add_subdirectory(reduce) +add_subdirectory(fuzz) +add_subdirectory(link) + +set(SPIRV_SOURCES + ${spirv-tools_SOURCE_DIR}/include/spirv-tools/libspirv.h + + ${CMAKE_CURRENT_SOURCE_DIR}/util/bitutils.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/hex_float.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/make_unique.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/small_vector.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.h + ${CMAKE_CURRENT_SOURCE_DIR}/util/timer.h + ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.h + ${CMAKE_CURRENT_SOURCE_DIR}/binary.h + ${CMAKE_CURRENT_SOURCE_DIR}/cfa.h + ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.h + ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.h + ${CMAKE_CURRENT_SOURCE_DIR}/enum_set.h + ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.h + ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.h + ${CMAKE_CURRENT_SOURCE_DIR}/extensions.h + ${CMAKE_CURRENT_SOURCE_DIR}/instruction.h + ${CMAKE_CURRENT_SOURCE_DIR}/latest_version_glsl_std_450_header.h + ${CMAKE_CURRENT_SOURCE_DIR}/latest_version_opencl_std_header.h + ${CMAKE_CURRENT_SOURCE_DIR}/latest_version_spirv_header.h + ${CMAKE_CURRENT_SOURCE_DIR}/macro.h + ${CMAKE_CURRENT_SOURCE_DIR}/name_mapper.h + ${CMAKE_CURRENT_SOURCE_DIR}/opcode.h + ${CMAKE_CURRENT_SOURCE_DIR}/operand.h + ${CMAKE_CURRENT_SOURCE_DIR}/parsed_operand.h + ${CMAKE_CURRENT_SOURCE_DIR}/print.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_constant.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_definition.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_fuzzer_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.h + ${CMAKE_CURRENT_SOURCE_DIR}/table.h + ${CMAKE_CURRENT_SOURCE_DIR}/text.h + ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.h + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate.h + + ${CMAKE_CURRENT_SOURCE_DIR}/util/bit_vector.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/parse_number.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util/string_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/assembly_grammar.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/binary.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/diagnostic.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/disassemble.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/enum_string_mapping.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ext_inst.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/extensions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/libspirv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/name_mapper.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/opcode.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/operand.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/parsed_operand.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/print.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_endian.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_fuzzer_options.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_optimizer_options.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reducer_options.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_target_env.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_validator_options.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/table.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/text.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/text_handler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_adjacency.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_annotation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_arithmetics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_atomics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_barriers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_bitwise.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_builtins.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_capability.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_cfg.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_composites.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_constants.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_conversion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_debug.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_decorations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_derivatives.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_extensions.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_execution_limitations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_function.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_id.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_image.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_interfaces.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_instruction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_layout.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_literals.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_logicals.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_memory_semantics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_misc.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_mode_setting.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_non_uniform.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_primitives.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_scopes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_small_type_uses.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validate_type.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/decoration.h + ${CMAKE_CURRENT_SOURCE_DIR}/val/basic_block.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/construct.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/function.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/instruction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/val/validation_state.cpp) + +if (${SPIRV_TIMER_ENABLED}) + set(SPIRV_SOURCES + ${SPIRV_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/util/timer.cpp) +endif() + +# The software_version.cpp file includes build-version.inc. +# Rebuild the software_version.cpp object file if it is older than +# build-version.inc or whenever build-version.inc itself is out of +# date. In the latter case, rebuild build-version.inc first. +# CMake is not smart enough to detect this dependency automatically. +# Without this, the dependency detection system for #included files +# does not kick in on a clean build for the following reason: The +# build will fail early because it doesn't know how to build the +# missing source file build-version.inc. That occurs before the +# preprocessor is run on software_version.cpp to detect the +# #include dependency. +set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/software_version.cpp + PROPERTIES OBJECT_DEPENDS "${SPIRV_TOOLS_BUILD_VERSION_INC}") + +spvtools_pch(SPIRV_SOURCES pch_source) + +# spirv_tools_default_target_options() sets the target options that are common +# for all ${SPIRV_TOOLS} targets. +function(spirv_tools_default_target_options target) + spvtools_default_compile_options(${target}) + target_include_directories(${target} + PUBLIC + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} + PRIVATE ${SPIRV_HEADER_INCLUDE_DIR} + ) + set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools libraries") + spvtools_check_symbol_exports(${target}) + add_dependencies(${target} core_tables enum_string_mapping extinst_tables) +endfunction() + +# Always build ${SPIRV_TOOLS}-shared. This is expected distro packages, and +# unlike the other SPIRV_TOOLS target, defaults to hidden symbol visibility. +# Filament specific changes +# add_library(${SPIRV_TOOLS}-shared SHARED ${SPIRV_SOURCES}) +# spirv_tools_default_target_options(${SPIRV_TOOLS}-shared) +# set_target_properties(${SPIRV_TOOLS}-shared PROPERTIES CXX_VISIBILITY_PRESET hidden) +# target_compile_definitions(${SPIRV_TOOLS}-shared +# PRIVATE SPIRV_TOOLS_IMPLEMENTATION +# PUBLIC SPIRV_TOOLS_SHAREDLIB +# ) +# End Filament specific changes + +if(SPIRV_TOOLS_BUILD_STATIC) + add_library(${SPIRV_TOOLS}-static STATIC ${SPIRV_SOURCES}) + spirv_tools_default_target_options(${SPIRV_TOOLS}-static) + # The static target does not have the '-static' suffix. + set_target_properties(${SPIRV_TOOLS}-static PROPERTIES OUTPUT_NAME "${SPIRV_TOOLS}") + + # Create the "${SPIRV_TOOLS}" target as an alias to either "${SPIRV_TOOLS}-static" + # or "${SPIRV_TOOLS}-shared" depending on the value of BUILD_SHARED_LIBS. + if(BUILD_SHARED_LIBS) + add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-shared) + else() + add_library(${SPIRV_TOOLS} ALIAS ${SPIRV_TOOLS}-static) + endif() + +# Filament specific changes + # set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static ${SPIRV_TOOLS}-shared) + set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}-static) +# End Filament specific changes +else() + add_library(${SPIRV_TOOLS} ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_SOURCES}) + spirv_tools_default_target_options(${SPIRV_TOOLS}) +# Filament specific changes + # set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS} ${SPIRV_TOOLS}-shared) + set(SPIRV_TOOLS_TARGETS ${SPIRV_TOOLS}) +# End Filament specific changes +endif() + +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + find_library(LIBRT rt) + if(LIBRT) + foreach(target ${SPIRV_TOOLS_TARGETS}) + target_link_libraries(${target} ${LIBRT}) + endforeach() + endif() +endif() + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS ${SPIRV_TOOLS_TARGETS} EXPORT ${SPIRV_TOOLS}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake) + + spvtools_config_package_dir(${SPIRV_TOOLS} PACKAGE_DIR) + install(EXPORT ${SPIRV_TOOLS}Targets FILE ${SPIRV_TOOLS}Target.cmake DESTINATION ${PACKAGE_DIR}) + + # Special config file for root library compared to other libs. + file(WRITE ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake + "include(\${CMAKE_CURRENT_LIST_DIR}/${SPIRV_TOOLS}Target.cmake)\n" + "set(${SPIRV_TOOLS}_LIBRARIES ${SPIRV_TOOLS})\n" + "get_target_property(${SPIRV_TOOLS}_INCLUDE_DIRS ${SPIRV_TOOLS} INTERFACE_INCLUDE_DIRECTORIES)\n") + install(FILES ${CMAKE_BINARY_DIR}/${SPIRV_TOOLS}Config.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) +endif() diff --git a/third_party/spirv-tools/source/assembly_grammar.cpp b/third_party/spirv-tools/source/assembly_grammar.cpp new file mode 100644 index 0000000..79f18ee --- /dev/null +++ b/third_party/spirv-tools/source/assembly_grammar.cpp @@ -0,0 +1,264 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/assembly_grammar.h" + +#include +#include +#include + +#include "source/ext_inst.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/table.h" + +namespace spvtools { +namespace { + +/// @brief Parses a mask expression string for the given operand type. +/// +/// A mask expression is a sequence of one or more terms separated by '|', +/// where each term a named enum value for the given type. No whitespace +/// is permitted. +/// +/// On success, the value is written to pValue. +/// +/// @param[in] operandTable operand lookup table +/// @param[in] type of the operand +/// @param[in] textValue word of text to be parsed +/// @param[out] pValue where the resulting value is written +/// +/// @return result code +spv_result_t spvTextParseMaskOperand(spv_target_env env, + const spv_operand_table operandTable, + const spv_operand_type_t type, + const char* textValue, uint32_t* pValue) { + if (textValue == nullptr) return SPV_ERROR_INVALID_TEXT; + size_t text_length = strlen(textValue); + if (text_length == 0) return SPV_ERROR_INVALID_TEXT; + const char* text_end = textValue + text_length; + + // We only support mask expressions in ASCII, so the separator value is a + // char. + const char separator = '|'; + + // Accumulate the result by interpreting one word at a time, scanning + // from left to right. + uint32_t value = 0; + const char* begin = textValue; // The left end of the current word. + const char* end = nullptr; // One character past the end of the current word. + do { + end = std::find(begin, text_end, separator); + + spv_operand_desc entry = nullptr; + if (spvOperandTableNameLookup(env, operandTable, type, begin, end - begin, + &entry)) { + return SPV_ERROR_INVALID_TEXT; + } + value |= entry->value; + + // Advance to the next word by skipping over the separator. + begin = end + 1; + } while (end != text_end); + + *pValue = value; + return SPV_SUCCESS; +} + +// Associates an opcode with its name. +struct SpecConstantOpcodeEntry { + SpvOp opcode; + const char* name; +}; + +// All the opcodes allowed as the operation for OpSpecConstantOp. +// The name does not have the usual "Op" prefix. For example opcode SpvOpIAdd +// is associated with the name "IAdd". +// +// clang-format off +#define CASE(NAME) { SpvOp##NAME, #NAME } +const SpecConstantOpcodeEntry kOpSpecConstantOpcodes[] = { + // Conversion + CASE(SConvert), + CASE(FConvert), + CASE(ConvertFToS), + CASE(ConvertSToF), + CASE(ConvertFToU), + CASE(ConvertUToF), + CASE(UConvert), + CASE(ConvertPtrToU), + CASE(ConvertUToPtr), + CASE(GenericCastToPtr), + CASE(PtrCastToGeneric), + CASE(Bitcast), + CASE(QuantizeToF16), + // Arithmetic + CASE(SNegate), + CASE(Not), + CASE(IAdd), + CASE(ISub), + CASE(IMul), + CASE(UDiv), + CASE(SDiv), + CASE(UMod), + CASE(SRem), + CASE(SMod), + CASE(ShiftRightLogical), + CASE(ShiftRightArithmetic), + CASE(ShiftLeftLogical), + CASE(BitwiseOr), + CASE(BitwiseAnd), + CASE(BitwiseXor), + CASE(FNegate), + CASE(FAdd), + CASE(FSub), + CASE(FMul), + CASE(FDiv), + CASE(FRem), + CASE(FMod), + // Composite + CASE(VectorShuffle), + CASE(CompositeExtract), + CASE(CompositeInsert), + // Logical + CASE(LogicalOr), + CASE(LogicalAnd), + CASE(LogicalNot), + CASE(LogicalEqual), + CASE(LogicalNotEqual), + CASE(Select), + // Comparison + CASE(IEqual), + CASE(INotEqual), + CASE(ULessThan), + CASE(SLessThan), + CASE(UGreaterThan), + CASE(SGreaterThan), + CASE(ULessThanEqual), + CASE(SLessThanEqual), + CASE(UGreaterThanEqual), + CASE(SGreaterThanEqual), + // Memory + CASE(AccessChain), + CASE(InBoundsAccessChain), + CASE(PtrAccessChain), + CASE(InBoundsPtrAccessChain), + CASE(CooperativeMatrixLengthNV) +}; + +// The 60 is determined by counting the opcodes listed in the spec. +static_assert(60 == sizeof(kOpSpecConstantOpcodes)/sizeof(kOpSpecConstantOpcodes[0]), + "OpSpecConstantOp opcode table is incomplete"); +#undef CASE +// clang-format on + +const size_t kNumOpSpecConstantOpcodes = + sizeof(kOpSpecConstantOpcodes) / sizeof(kOpSpecConstantOpcodes[0]); + +} // namespace + +bool AssemblyGrammar::isValid() const { + return operandTable_ && opcodeTable_ && extInstTable_; +} + +CapabilitySet AssemblyGrammar::filterCapsAgainstTargetEnv( + const SpvCapability* cap_array, uint32_t count) const { + CapabilitySet cap_set; + for (uint32_t i = 0; i < count; ++i) { + spv_operand_desc cap_desc = {}; + if (SPV_SUCCESS == lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, + static_cast(cap_array[i]), + &cap_desc)) { + // spvOperandTableValueLookup() filters capabilities internally + // according to the current target environment by itself. So we + // should be safe to add this capability if the lookup succeeds. + cap_set.Add(cap_array[i]); + } + } + return cap_set; +} + +spv_result_t AssemblyGrammar::lookupOpcode(const char* name, + spv_opcode_desc* desc) const { + return spvOpcodeTableNameLookup(target_env_, opcodeTable_, name, desc); +} + +spv_result_t AssemblyGrammar::lookupOpcode(SpvOp opcode, + spv_opcode_desc* desc) const { + return spvOpcodeTableValueLookup(target_env_, opcodeTable_, opcode, desc); +} + +spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, + const char* name, size_t name_len, + spv_operand_desc* desc) const { + return spvOperandTableNameLookup(target_env_, operandTable_, type, name, + name_len, desc); +} + +spv_result_t AssemblyGrammar::lookupOperand(spv_operand_type_t type, + uint32_t operand, + spv_operand_desc* desc) const { + return spvOperandTableValueLookup(target_env_, operandTable_, type, operand, + desc); +} + +spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(const char* name, + SpvOp* opcode) const { + const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes; + const auto* found = + std::find_if(kOpSpecConstantOpcodes, last, + [name](const SpecConstantOpcodeEntry& entry) { + return 0 == strcmp(name, entry.name); + }); + if (found == last) return SPV_ERROR_INVALID_LOOKUP; + *opcode = found->opcode; + return SPV_SUCCESS; +} + +spv_result_t AssemblyGrammar::lookupSpecConstantOpcode(SpvOp opcode) const { + const auto* last = kOpSpecConstantOpcodes + kNumOpSpecConstantOpcodes; + const auto* found = + std::find_if(kOpSpecConstantOpcodes, last, + [opcode](const SpecConstantOpcodeEntry& entry) { + return opcode == entry.opcode; + }); + if (found == last) return SPV_ERROR_INVALID_LOOKUP; + return SPV_SUCCESS; +} + +spv_result_t AssemblyGrammar::parseMaskOperand(const spv_operand_type_t type, + const char* textValue, + uint32_t* pValue) const { + return spvTextParseMaskOperand(target_env_, operandTable_, type, textValue, + pValue); +} +spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, + const char* textValue, + spv_ext_inst_desc* extInst) const { + return spvExtInstTableNameLookup(extInstTable_, type, textValue, extInst); +} + +spv_result_t AssemblyGrammar::lookupExtInst(spv_ext_inst_type_t type, + uint32_t firstWord, + spv_ext_inst_desc* extInst) const { + return spvExtInstTableValueLookup(extInstTable_, type, firstWord, extInst); +} + +void AssemblyGrammar::pushOperandTypesForMask( + const spv_operand_type_t type, const uint32_t mask, + spv_operand_pattern_t* pattern) const { + spvPushOperandTypesForMask(target_env_, operandTable_, type, mask, pattern); +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/assembly_grammar.h b/third_party/spirv-tools/source/assembly_grammar.h new file mode 100644 index 0000000..17c2bd3 --- /dev/null +++ b/third_party/spirv-tools/source/assembly_grammar.h @@ -0,0 +1,138 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_ASSEMBLY_GRAMMAR_H_ +#define SOURCE_ASSEMBLY_GRAMMAR_H_ + +#include "source/enum_set.h" +#include "source/latest_version_spirv_header.h" +#include "source/operand.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Encapsulates the grammar to use for SPIR-V assembly. +// Contains methods to query for valid instructions and operands. +class AssemblyGrammar { + public: + explicit AssemblyGrammar(const spv_const_context context) + : target_env_(context->target_env), + operandTable_(context->operand_table), + opcodeTable_(context->opcode_table), + extInstTable_(context->ext_inst_table) {} + + // Returns true if the internal tables have been initialized with valid data. + bool isValid() const; + + // Returns the SPIR-V target environment. + spv_target_env target_env() const { return target_env_; } + + // Removes capabilities not available in the current target environment and + // returns the rest. + CapabilitySet filterCapsAgainstTargetEnv(const SpvCapability* cap_array, + uint32_t count) const; + + // Fills in the desc parameter with the information about the opcode + // of the given name. Returns SPV_SUCCESS if the opcode was found, and + // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist. + spv_result_t lookupOpcode(const char* name, spv_opcode_desc* desc) const; + + // Fills in the desc parameter with the information about the opcode + // of the valid. Returns SPV_SUCCESS if the opcode was found, and + // SPV_ERROR_INVALID_LOOKUP if the opcode does not exist. + spv_result_t lookupOpcode(SpvOp opcode, spv_opcode_desc* desc) const; + + // Fills in the desc parameter with the information about the given + // operand. Returns SPV_SUCCESS if the operand was found, and + // SPV_ERROR_INVALID_LOOKUP otherwise. + spv_result_t lookupOperand(spv_operand_type_t type, const char* name, + size_t name_len, spv_operand_desc* desc) const; + + // Fills in the desc parameter with the information about the given + // operand. Returns SPV_SUCCESS if the operand was found, and + // SPV_ERROR_INVALID_LOOKUP otherwise. + spv_result_t lookupOperand(spv_operand_type_t type, uint32_t operand, + spv_operand_desc* desc) const; + + // Finds operand entry in the grammar table and returns its name. + // Returns "Unknown" if not found. + const char* lookupOperandName(spv_operand_type_t type, + uint32_t operand) const { + spv_operand_desc desc = nullptr; + if (lookupOperand(type, operand, &desc) != SPV_SUCCESS || !desc) { + return "Unknown"; + } + return desc->name; + } + + // Finds the opcode for the given OpSpecConstantOp opcode name. The name + // should not have the "Op" prefix. For example, "IAdd" corresponds to + // the integer add opcode for OpSpecConstantOp. On success, returns + // SPV_SUCCESS and sends the discovered operation code through the opcode + // parameter. On failure, returns SPV_ERROR_INVALID_LOOKUP. + spv_result_t lookupSpecConstantOpcode(const char* name, SpvOp* opcode) const; + + // Returns SPV_SUCCESS if the given opcode is valid as the opcode operand + // to OpSpecConstantOp. + spv_result_t lookupSpecConstantOpcode(SpvOp opcode) const; + + // Parses a mask expression string for the given operand type. + // + // A mask expression is a sequence of one or more terms separated by '|', + // where each term is a named enum value for a given type. No whitespace + // is permitted. + // + // On success, the value is written to pValue, and SPV_SUCCESS is returned. + // The operand type is defined by the type parameter, and the text to be + // parsed is defined by the textValue parameter. + spv_result_t parseMaskOperand(const spv_operand_type_t type, + const char* textValue, uint32_t* pValue) const; + + // Writes the extended operand with the given type and text to the *extInst + // parameter. + // Returns SPV_SUCCESS if the value could be found. + spv_result_t lookupExtInst(spv_ext_inst_type_t type, const char* textValue, + spv_ext_inst_desc* extInst) const; + + // Writes the extended operand with the given type and first encoded word + // to the *extInst parameter. + // Returns SPV_SUCCESS if the value could be found. + spv_result_t lookupExtInst(spv_ext_inst_type_t type, uint32_t firstWord, + spv_ext_inst_desc* extInst) const; + + // Inserts the operands expected after the given typed mask onto the end + // of the given pattern. + // + // Each set bit in the mask represents zero or more operand types that + // should be appended onto the pattern. Operands for a less significant + // bit must always match before operands for a more significant bit, so + // the operands for a less significant bit must appear closer to the end + // of the pattern stack. + // + // If a set bit is unknown, then we assume it has no operands. + void pushOperandTypesForMask(const spv_operand_type_t type, + const uint32_t mask, + spv_operand_pattern_t* pattern) const; + + private: + const spv_target_env target_env_; + const spv_operand_table operandTable_; + const spv_opcode_table opcodeTable_; + const spv_ext_inst_table extInstTable_; +}; + +} // namespace spvtools + +#endif // SOURCE_ASSEMBLY_GRAMMAR_H_ diff --git a/third_party/spirv-tools/source/binary.cpp b/third_party/spirv-tools/source/binary.cpp new file mode 100644 index 0000000..75a997d --- /dev/null +++ b/third_party/spirv-tools/source/binary.cpp @@ -0,0 +1,829 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/binary.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/diagnostic.h" +#include "source/ext_inst.h" +#include "source/latest_version_spirv_header.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "source/spirv_endian.h" + +spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, + const spv_endianness_t endian, + spv_header_t* pHeader) { + if (!binary->code) return SPV_ERROR_INVALID_BINARY; + if (binary->wordCount < SPV_INDEX_INSTRUCTION) + return SPV_ERROR_INVALID_BINARY; + if (!pHeader) return SPV_ERROR_INVALID_POINTER; + + // TODO: Validation checking? + pHeader->magic = spvFixWord(binary->code[SPV_INDEX_MAGIC_NUMBER], endian); + pHeader->version = spvFixWord(binary->code[SPV_INDEX_VERSION_NUMBER], endian); + // Per 2.3.1 version's high and low bytes are 0 + if ((pHeader->version & 0x000000ff) || pHeader->version & 0xff000000) + return SPV_ERROR_INVALID_BINARY; + // Minimum version was 1.0 and max version is defined by SPV_VERSION. + if (pHeader->version < SPV_SPIRV_VERSION_WORD(1, 0) || + pHeader->version > SPV_VERSION) + return SPV_ERROR_INVALID_BINARY; + + pHeader->generator = + spvFixWord(binary->code[SPV_INDEX_GENERATOR_NUMBER], endian); + pHeader->bound = spvFixWord(binary->code[SPV_INDEX_BOUND], endian); + pHeader->schema = spvFixWord(binary->code[SPV_INDEX_SCHEMA], endian); + pHeader->instructions = &binary->code[SPV_INDEX_INSTRUCTION]; + + return SPV_SUCCESS; +} + +namespace { + +// A SPIR-V binary parser. A parser instance communicates detailed parse +// results via callbacks. +class Parser { + public: + // The user_data value is provided to the callbacks as context. + Parser(const spv_const_context context, void* user_data, + spv_parsed_header_fn_t parsed_header_fn, + spv_parsed_instruction_fn_t parsed_instruction_fn) + : grammar_(context), + consumer_(context->consumer), + user_data_(user_data), + parsed_header_fn_(parsed_header_fn), + parsed_instruction_fn_(parsed_instruction_fn) {} + + // Parses the specified binary SPIR-V module, issuing callbacks on a parsed + // header and for each parsed instruction. Returns SPV_SUCCESS on success. + // Otherwise returns an error code and issues a diagnostic. + spv_result_t parse(const uint32_t* words, size_t num_words, + spv_diagnostic* diagnostic); + + private: + // All remaining methods work on the current module parse state. + + // Like the parse method, but works on the current module parse state. + spv_result_t parseModule(); + + // Parses an instruction at the current position of the binary. Assumes + // the header has been parsed, the endian has been set, and the word index is + // still in range. Advances the parsing position past the instruction, and + // updates other parsing state for the current module. + // On success, returns SPV_SUCCESS and issues the parsed-instruction callback. + // On failure, returns an error code and issues a diagnostic. + spv_result_t parseInstruction(); + + // Parses an instruction operand with the given type, for an instruction + // starting at inst_offset words into the SPIR-V binary. + // If the SPIR-V binary is the same endianness as the host, then the + // endian_converted_inst_words parameter is ignored. Otherwise, this method + // appends the words for this operand, converted to host native endianness, + // to the end of endian_converted_inst_words. This method also updates the + // expected_operands parameter, and the scalar members of the inst parameter. + // On success, returns SPV_SUCCESS, advances past the operand, and pushes a + // new entry on to the operands vector. Otherwise returns an error code and + // issues a diagnostic. + spv_result_t parseOperand(size_t inst_offset, spv_parsed_instruction_t* inst, + const spv_operand_type_t type, + std::vector* endian_converted_inst_words, + std::vector* operands, + spv_operand_pattern_t* expected_operands); + + // Records the numeric type for an operand according to the type information + // associated with the given non-zero type Id. This can fail if the type Id + // is not a type Id, or if the type Id does not reference a scalar numeric + // type. On success, return SPV_SUCCESS and populates the num_words, + // number_kind, and number_bit_width fields of parsed_operand. + spv_result_t setNumericTypeInfoForType(spv_parsed_operand_t* parsed_operand, + uint32_t type_id); + + // Records the number type for an instruction at the given offset, if that + // instruction generates a type. For types that aren't scalar numbers, + // record something with number kind SPV_NUMBER_NONE. + void recordNumberType(size_t inst_offset, + const spv_parsed_instruction_t* inst); + + // Returns a diagnostic stream object initialized with current position in + // the input stream, and for the given error code. Any data written to the + // returned object will be propagated to the current parse's diagnostic + // object. + spvtools::DiagnosticStream diagnostic(spv_result_t error) { + return spvtools::DiagnosticStream({0, 0, _.instruction_count}, consumer_, + "", error); + } + + // Returns a diagnostic stream object with the default parse error code. + spvtools::DiagnosticStream diagnostic() { + // The default failure for parsing is invalid binary. + return diagnostic(SPV_ERROR_INVALID_BINARY); + } + + // Issues a diagnostic describing an exhaustion of input condition when + // trying to decode an instruction operand, and returns + // SPV_ERROR_INVALID_BINARY. + spv_result_t exhaustedInputDiagnostic(size_t inst_offset, SpvOp opcode, + spv_operand_type_t type) { + return diagnostic() << "End of input reached while decoding Op" + << spvOpcodeString(opcode) << " starting at word " + << inst_offset + << ((_.word_index < _.num_words) ? ": truncated " + : ": missing ") + << spvOperandTypeStr(type) << " operand at word offset " + << _.word_index - inst_offset << "."; + } + + // Returns the endian-corrected word at the current position. + uint32_t peek() const { return peekAt(_.word_index); } + + // Returns the endian-corrected word at the given position. + uint32_t peekAt(size_t index) const { + assert(index < _.num_words); + return spvFixWord(_.words[index], _.endian); + } + + // Data members + + const spvtools::AssemblyGrammar grammar_; // SPIR-V syntax utility. + const spvtools::MessageConsumer& consumer_; // Message consumer callback. + void* const user_data_; // Context for the callbacks + const spv_parsed_header_fn_t parsed_header_fn_; // Parsed header callback + const spv_parsed_instruction_fn_t + parsed_instruction_fn_; // Parsed instruction callback + + // Describes the format of a typed literal number. + struct NumberType { + spv_number_kind_t type; + uint32_t bit_width; + }; + + // The state used to parse a single SPIR-V binary module. + struct State { + State(const uint32_t* words_arg, size_t num_words_arg, + spv_diagnostic* diagnostic_arg) + : words(words_arg), + num_words(num_words_arg), + diagnostic(diagnostic_arg), + word_index(0), + instruction_count(0), + endian(), + requires_endian_conversion(false) { + // Temporary storage for parser state within a single instruction. + // Most instructions require fewer than 25 words or operands. + operands.reserve(25); + endian_converted_words.reserve(25); + expected_operands.reserve(25); + } + State() : State(0, 0, nullptr) {} + const uint32_t* words; // Words in the binary SPIR-V module. + size_t num_words; // Number of words in the module. + spv_diagnostic* diagnostic; // Where diagnostics go. + size_t word_index; // The current position in words. + size_t instruction_count; // The count of processed instructions + spv_endianness_t endian; // The endianness of the binary. + // Is the SPIR-V binary in a different endiannes from the host native + // endianness? + bool requires_endian_conversion; + + // Maps a result ID to its type ID. By convention: + // - a result ID that is a type definition maps to itself. + // - a result ID without a type maps to 0. (E.g. for OpLabel) + std::unordered_map id_to_type_id; + // Maps a type ID to its number type description. + std::unordered_map type_id_to_number_type_info; + // Maps an ExtInstImport id to the extended instruction type. + std::unordered_map + import_id_to_ext_inst_type; + + // Used by parseOperand + std::vector operands; + std::vector endian_converted_words; + spv_operand_pattern_t expected_operands; + } _; +}; + +spv_result_t Parser::parse(const uint32_t* words, size_t num_words, + spv_diagnostic* diagnostic_arg) { + _ = State(words, num_words, diagnostic_arg); + + const spv_result_t result = parseModule(); + + // Clear the module state. The tables might be big. + _ = State(); + + return result; +} + +spv_result_t Parser::parseModule() { + if (!_.words) return diagnostic() << "Missing module."; + + if (_.num_words < SPV_INDEX_INSTRUCTION) + return diagnostic() << "Module has incomplete header: only " << _.num_words + << " words instead of " << SPV_INDEX_INSTRUCTION; + + // Check the magic number and detect the module's endianness. + spv_const_binary_t binary{_.words, _.num_words}; + if (spvBinaryEndianness(&binary, &_.endian)) { + return diagnostic() << "Invalid SPIR-V magic number '" << std::hex + << _.words[0] << "'."; + } + _.requires_endian_conversion = !spvIsHostEndian(_.endian); + + // Process the header. + spv_header_t header; + if (spvBinaryHeaderGet(&binary, _.endian, &header)) { + // It turns out there is no way to trigger this error since the only + // failure cases are already handled above, with better messages. + return diagnostic(SPV_ERROR_INTERNAL) + << "Internal error: unhandled header parse failure"; + } + if (parsed_header_fn_) { + if (auto error = parsed_header_fn_(user_data_, _.endian, header.magic, + header.version, header.generator, + header.bound, header.schema)) { + return error; + } + } + + // Process the instructions. + _.word_index = SPV_INDEX_INSTRUCTION; + while (_.word_index < _.num_words) + if (auto error = parseInstruction()) return error; + + // Running off the end should already have been reported earlier. + assert(_.word_index == _.num_words); + + return SPV_SUCCESS; +} + +spv_result_t Parser::parseInstruction() { + _.instruction_count++; + + // The zero values for all members except for opcode are the + // correct initial values. + spv_parsed_instruction_t inst = {}; + + const uint32_t first_word = peek(); + + // If the module's endianness is different from the host native endianness, + // then converted_words contains the the endian-translated words in the + // instruction. + _.endian_converted_words.clear(); + _.endian_converted_words.push_back(first_word); + + // After a successful parse of the instruction, the inst.operands member + // will point to this vector's storage. + _.operands.clear(); + + assert(_.word_index < _.num_words); + // Decompose and check the first word. + uint16_t inst_word_count = 0; + spvOpcodeSplit(first_word, &inst_word_count, &inst.opcode); + if (inst_word_count < 1) { + return diagnostic() << "Invalid instruction word count: " + << inst_word_count; + } + spv_opcode_desc opcode_desc; + if (grammar_.lookupOpcode(static_cast(inst.opcode), &opcode_desc)) + return diagnostic() << "Invalid opcode: " << inst.opcode; + + // Advance past the opcode word. But remember the of the start + // of the instruction. + const size_t inst_offset = _.word_index; + _.word_index++; + + // Maintains the ordered list of expected operand types. + // For many instructions we only need the {numTypes, operandTypes} + // entries in opcode_desc. However, sometimes we need to modify + // the list as we parse the operands. This occurs when an operand + // has its own logical operands (such as the LocalSize operand for + // ExecutionMode), or for extended instructions that may have their + // own operands depending on the selected extended instruction. + _.expected_operands.clear(); + for (auto i = 0; i < opcode_desc->numTypes; i++) + _.expected_operands.push_back( + opcode_desc->operandTypes[opcode_desc->numTypes - i - 1]); + + while (_.word_index < inst_offset + inst_word_count) { + const uint16_t inst_word_index = uint16_t(_.word_index - inst_offset); + if (_.expected_operands.empty()) { + return diagnostic() << "Invalid instruction Op" << opcode_desc->name + << " starting at word " << inst_offset + << ": expected no more operands after " + << inst_word_index + << " words, but stated word count is " + << inst_word_count << "."; + } + + spv_operand_type_t type = + spvTakeFirstMatchableOperand(&_.expected_operands); + + if (auto error = + parseOperand(inst_offset, &inst, type, &_.endian_converted_words, + &_.operands, &_.expected_operands)) { + return error; + } + } + + if (!_.expected_operands.empty() && + !spvOperandIsOptional(_.expected_operands.back())) { + return diagnostic() << "End of input reached while decoding Op" + << opcode_desc->name << " starting at word " + << inst_offset << ": expected more operands after " + << inst_word_count << " words."; + } + + if ((inst_offset + inst_word_count) != _.word_index) { + return diagnostic() << "Invalid word count: Op" << opcode_desc->name + << " starting at word " << inst_offset + << " says it has " << inst_word_count + << " words, but found " << _.word_index - inst_offset + << " words instead."; + } + + // Check the computed length of the endian-converted words vector against + // the declared number of words in the instruction. If endian conversion + // is required, then they should match. If no endian conversion was + // performed, then the vector only contains the initial opcode/word-count + // word. + assert(!_.requires_endian_conversion || + (inst_word_count == _.endian_converted_words.size())); + assert(_.requires_endian_conversion || + (_.endian_converted_words.size() == 1)); + + recordNumberType(inst_offset, &inst); + + if (_.requires_endian_conversion) { + // We must wait until here to set this pointer, because the vector might + // have been be resized while we accumulated its elements. + inst.words = _.endian_converted_words.data(); + } else { + // If no conversion is required, then just point to the underlying binary. + // This saves time and space. + inst.words = _.words + inst_offset; + } + inst.num_words = inst_word_count; + + // We must wait until here to set this pointer, because the vector might + // have been be resized while we accumulated its elements. + inst.operands = _.operands.data(); + inst.num_operands = uint16_t(_.operands.size()); + + // Issue the callback. The callee should know that all the storage in inst + // is transient, and will disappear immediately afterward. + if (parsed_instruction_fn_) { + if (auto error = parsed_instruction_fn_(user_data_, &inst)) return error; + } + + return SPV_SUCCESS; +} + +spv_result_t Parser::parseOperand(size_t inst_offset, + spv_parsed_instruction_t* inst, + const spv_operand_type_t type, + std::vector* words, + std::vector* operands, + spv_operand_pattern_t* expected_operands) { + const SpvOp opcode = static_cast(inst->opcode); + // We'll fill in this result as we go along. + spv_parsed_operand_t parsed_operand; + parsed_operand.offset = uint16_t(_.word_index - inst_offset); + // Most operands occupy one word. This might be be adjusted later. + parsed_operand.num_words = 1; + // The type argument is the one used by the grammar to parse the instruction. + // But it can exposes internal parser details such as whether an operand is + // optional or actually represents a variable-length sequence of operands. + // The resulting type should be adjusted to avoid those internal details. + // In most cases, the resulting operand type is the same as the grammar type. + parsed_operand.type = type; + + // Assume non-numeric values. This will be updated for literal numbers. + parsed_operand.number_kind = SPV_NUMBER_NONE; + parsed_operand.number_bit_width = 0; + + if (_.word_index >= _.num_words) + return exhaustedInputDiagnostic(inst_offset, opcode, type); + + const uint32_t word = peek(); + + // Do the words in this operand have to be converted to native endianness? + // True for all but literal strings. + bool convert_operand_endianness = true; + + switch (type) { + case SPV_OPERAND_TYPE_TYPE_ID: + if (!word) + return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Type Id is 0"; + inst->type_id = word; + break; + + case SPV_OPERAND_TYPE_RESULT_ID: + if (!word) + return diagnostic(SPV_ERROR_INVALID_ID) << "Error: Result Id is 0"; + inst->result_id = word; + // Save the result ID to type ID mapping. + // In the grammar, type ID always appears before result ID. + if (_.id_to_type_id.find(inst->result_id) != _.id_to_type_id.end()) + return diagnostic(SPV_ERROR_INVALID_ID) + << "Id " << inst->result_id << " is defined more than once"; + // Record it. + // A regular value maps to its type. Some instructions (e.g. OpLabel) + // have no type Id, and will map to 0. The result Id for a + // type-generating instruction (e.g. OpTypeInt) maps to itself. + _.id_to_type_id[inst->result_id] = + spvOpcodeGeneratesType(opcode) ? inst->result_id : inst->type_id; + break; + + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_OPTIONAL_ID: + if (!word) return diagnostic(SPV_ERROR_INVALID_ID) << "Id is 0"; + parsed_operand.type = SPV_OPERAND_TYPE_ID; + + if (opcode == SpvOpExtInst && parsed_operand.offset == 3) { + // The current word is the extended instruction set Id. + // Set the extended instruction set type for the current instruction. + auto ext_inst_type_iter = _.import_id_to_ext_inst_type.find(word); + if (ext_inst_type_iter == _.import_id_to_ext_inst_type.end()) { + return diagnostic(SPV_ERROR_INVALID_ID) + << "OpExtInst set Id " << word + << " does not reference an OpExtInstImport result Id"; + } + inst->ext_inst_type = ext_inst_type_iter->second; + } + break; + + case SPV_OPERAND_TYPE_SCOPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + // Check for trivially invalid values. The operand descriptions already + // have the word "ID" in them. + if (!word) return diagnostic() << spvOperandTypeStr(type) << " is 0"; + break; + + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { + assert(SpvOpExtInst == opcode); + assert(inst->ext_inst_type != SPV_EXT_INST_TYPE_NONE); + spv_ext_inst_desc ext_inst; + if (grammar_.lookupExtInst(inst->ext_inst_type, word, &ext_inst) == + SPV_SUCCESS) { + // if we know about this ext inst, push the expected operands + spvPushOperandTypes(ext_inst->operandTypes, expected_operands); + } else { + // if we don't know this extended instruction and the set isn't + // non-semantic, we cannot process further + if (!spvExtInstIsNonSemantic(inst->ext_inst_type)) { + return diagnostic() + << "Invalid extended instruction number: " << word; + } else { + // for non-semantic instruction sets, we know the form of all such + // extended instructions contains a series of IDs as parameters + expected_operands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); + } + } + } break; + + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { + assert(SpvOpSpecConstantOp == opcode); + if (grammar_.lookupSpecConstantOpcode(SpvOp(word))) { + return diagnostic() + << "Invalid " << spvOperandTypeStr(type) << ": " << word; + } + spv_opcode_desc opcode_entry = nullptr; + if (grammar_.lookupOpcode(SpvOp(word), &opcode_entry)) { + return diagnostic(SPV_ERROR_INTERNAL) + << "OpSpecConstant opcode table out of sync"; + } + // OpSpecConstant opcodes must have a type and result. We've already + // processed them, so skip them when preparing to parse the other + // operants for the opcode. + assert(opcode_entry->hasType); + assert(opcode_entry->hasResult); + assert(opcode_entry->numTypes >= 2); + spvPushOperandTypes(opcode_entry->operandTypes + 2, expected_operands); + } break; + + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + // These are regular single-word literal integer operands. + // Post-parsing validation should check the range of the parsed value. + parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_INTEGER; + // It turns out they are always unsigned integers! + parsed_operand.number_kind = SPV_NUMBER_UNSIGNED_INT; + parsed_operand.number_bit_width = 32; + break; + + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + parsed_operand.type = SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER; + if (opcode == SpvOpSwitch) { + // The literal operands have the same type as the value + // referenced by the selector Id. + const uint32_t selector_id = peekAt(inst_offset + 1); + const auto type_id_iter = _.id_to_type_id.find(selector_id); + if (type_id_iter == _.id_to_type_id.end() || + type_id_iter->second == 0) { + return diagnostic() << "Invalid OpSwitch: selector id " << selector_id + << " has no type"; + } + uint32_t type_id = type_id_iter->second; + + if (selector_id == type_id) { + // Recall that by convention, a result ID that is a type definition + // maps to itself. + return diagnostic() << "Invalid OpSwitch: selector id " << selector_id + << " is a type, not a value"; + } + if (auto error = setNumericTypeInfoForType(&parsed_operand, type_id)) + return error; + if (parsed_operand.number_kind != SPV_NUMBER_UNSIGNED_INT && + parsed_operand.number_kind != SPV_NUMBER_SIGNED_INT) { + return diagnostic() << "Invalid OpSwitch: selector id " << selector_id + << " is not a scalar integer"; + } + } else { + assert(opcode == SpvOpConstant || opcode == SpvOpSpecConstant); + // The literal number type is determined by the type Id for the + // constant. + assert(inst->type_id); + if (auto error = + setNumericTypeInfoForType(&parsed_operand, inst->type_id)) + return error; + } + break; + + case SPV_OPERAND_TYPE_LITERAL_STRING: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { + convert_operand_endianness = false; + const char* string = + reinterpret_cast(_.words + _.word_index); + // Compute the length of the string, but make sure we don't run off the + // end of the input. + const size_t remaining_input_bytes = + sizeof(uint32_t) * (_.num_words - _.word_index); + const size_t string_num_content_bytes = + spv_strnlen_s(string, remaining_input_bytes); + // If there was no terminating null byte, then that's an end-of-input + // error. + if (string_num_content_bytes == remaining_input_bytes) + return exhaustedInputDiagnostic(inst_offset, opcode, type); + // Account for null in the word length, so add 1 for null, then add 3 to + // make sure we round up. The following is equivalent to: + // (string_num_content_bytes + 1 + 3) / 4 + const size_t string_num_words = string_num_content_bytes / 4 + 1; + // Make sure we can record the word count without overflow. + // + // This error can't currently be triggered because of validity + // checks elsewhere. + if (string_num_words > std::numeric_limits::max()) { + return diagnostic() << "Literal string is longer than " + << std::numeric_limits::max() + << " words: " << string_num_words << " words long"; + } + parsed_operand.num_words = uint16_t(string_num_words); + parsed_operand.type = SPV_OPERAND_TYPE_LITERAL_STRING; + + if (SpvOpExtInstImport == opcode) { + // Record the extended instruction type for the ID for this import. + // There is only one string literal argument to OpExtInstImport, + // so it's sufficient to guard this just on the opcode. + const spv_ext_inst_type_t ext_inst_type = + spvExtInstImportTypeGet(string); + if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { + return diagnostic() + << "Invalid extended instruction import '" << string << "'"; + } + // We must have parsed a valid result ID. It's a condition + // of the grammar, and we only accept non-zero result Ids. + assert(inst->result_id); + _.import_id_to_ext_inst_type[inst->result_id] = ext_inst_type; + } + } break; + + case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + case SPV_OPERAND_TYPE_MEMORY_MODEL: + case SPV_OPERAND_TYPE_EXECUTION_MODE: + case SPV_OPERAND_TYPE_STORAGE_CLASS: + case SPV_OPERAND_TYPE_DIMENSIONALITY: + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + case SPV_OPERAND_TYPE_DECORATION: + case SPV_OPERAND_TYPE_BUILT_IN: + case SPV_OPERAND_TYPE_GROUP_OPERATION: + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: { + // A single word that is a plain enum value. + + // Map an optional operand type to its corresponding concrete type. + if (type == SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER) + parsed_operand.type = SPV_OPERAND_TYPE_ACCESS_QUALIFIER; + + spv_operand_desc entry; + if (grammar_.lookupOperand(type, word, &entry)) { + return diagnostic() + << "Invalid " << spvOperandTypeStr(parsed_operand.type) + << " operand: " << word; + } + // Prepare to accept operands to this operand, if needed. + spvPushOperandTypes(entry->operandTypes, expected_operands); + } break; + + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: { + // This operand is a mask. + + // Map an optional operand type to its corresponding concrete type. + if (type == SPV_OPERAND_TYPE_OPTIONAL_IMAGE) + parsed_operand.type = SPV_OPERAND_TYPE_IMAGE; + else if (type == SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS) + parsed_operand.type = SPV_OPERAND_TYPE_MEMORY_ACCESS; + + // Check validity of set mask bits. Also prepare for operands for those + // masks if they have any. To get operand order correct, scan from + // MSB to LSB since we can only prepend operands to a pattern. + // The only case in the grammar where you have more than one mask bit + // having an operand is for image operands. See SPIR-V 3.14 Image + // Operands. + uint32_t remaining_word = word; + for (uint32_t mask = (1u << 31); remaining_word; mask >>= 1) { + if (remaining_word & mask) { + spv_operand_desc entry; + if (grammar_.lookupOperand(type, mask, &entry)) { + return diagnostic() + << "Invalid " << spvOperandTypeStr(parsed_operand.type) + << " operand: " << word << " has invalid mask component " + << mask; + } + remaining_word ^= mask; + spvPushOperandTypes(entry->operandTypes, expected_operands); + } + } + if (word == 0) { + // An all-zeroes mask *might* also be valid. + spv_operand_desc entry; + if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) { + // Prepare for its operands, if any. + spvPushOperandTypes(entry->operandTypes, expected_operands); + } + } + } break; + default: + return diagnostic() << "Internal error: Unhandled operand type: " << type; + } + + assert(spvOperandIsConcrete(parsed_operand.type)); + + operands->push_back(parsed_operand); + + const size_t index_after_operand = _.word_index + parsed_operand.num_words; + + // Avoid buffer overrun for the cases where the operand has more than one + // word, and where it isn't a string. (Those other cases have already been + // handled earlier.) For example, this error can occur for a multi-word + // argument to OpConstant, or a multi-word case literal operand for OpSwitch. + if (_.num_words < index_after_operand) + return exhaustedInputDiagnostic(inst_offset, opcode, type); + + if (_.requires_endian_conversion) { + // Copy instruction words. Translate to native endianness as needed. + if (convert_operand_endianness) { + const spv_endianness_t endianness = _.endian; + std::transform(_.words + _.word_index, _.words + index_after_operand, + std::back_inserter(*words), + [endianness](const uint32_t raw_word) { + return spvFixWord(raw_word, endianness); + }); + } else { + words->insert(words->end(), _.words + _.word_index, + _.words + index_after_operand); + } + } + + // Advance past the operand. + _.word_index = index_after_operand; + + return SPV_SUCCESS; +} + +spv_result_t Parser::setNumericTypeInfoForType( + spv_parsed_operand_t* parsed_operand, uint32_t type_id) { + assert(type_id != 0); + auto type_info_iter = _.type_id_to_number_type_info.find(type_id); + if (type_info_iter == _.type_id_to_number_type_info.end()) { + return diagnostic() << "Type Id " << type_id << " is not a type"; + } + const NumberType& info = type_info_iter->second; + if (info.type == SPV_NUMBER_NONE) { + // This is a valid type, but for something other than a scalar number. + return diagnostic() << "Type Id " << type_id + << " is not a scalar numeric type"; + } + + parsed_operand->number_kind = info.type; + parsed_operand->number_bit_width = info.bit_width; + // Round up the word count. + parsed_operand->num_words = static_cast((info.bit_width + 31) / 32); + return SPV_SUCCESS; +} + +void Parser::recordNumberType(size_t inst_offset, + const spv_parsed_instruction_t* inst) { + const SpvOp opcode = static_cast(inst->opcode); + if (spvOpcodeGeneratesType(opcode)) { + NumberType info = {SPV_NUMBER_NONE, 0}; + if (SpvOpTypeInt == opcode) { + const bool is_signed = peekAt(inst_offset + 3) != 0; + info.type = is_signed ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT; + info.bit_width = peekAt(inst_offset + 2); + } else if (SpvOpTypeFloat == opcode) { + info.type = SPV_NUMBER_FLOATING; + info.bit_width = peekAt(inst_offset + 2); + } + // The *result* Id of a type generating instruction is the type Id. + _.type_id_to_number_type_info[inst->result_id] = info; + } +} + +} // anonymous namespace + +spv_result_t spvBinaryParse(const spv_const_context context, void* user_data, + const uint32_t* code, const size_t num_words, + spv_parsed_header_fn_t parsed_header, + spv_parsed_instruction_fn_t parsed_instruction, + spv_diagnostic* diagnostic) { + spv_context_t hijack_context = *context; + if (diagnostic) { + *diagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, diagnostic); + } + Parser parser(&hijack_context, user_data, parsed_header, parsed_instruction); + return parser.parse(code, num_words, diagnostic); +} + +// TODO(dneto): This probably belongs in text.cpp since that's the only place +// that a spv_binary_t value is created. +void spvBinaryDestroy(spv_binary binary) { + if (binary) { + if (binary->code) delete[] binary->code; + delete binary; + } +} + +size_t spv_strnlen_s(const char* str, size_t strsz) { + if (!str) return 0; + for (size_t i = 0; i < strsz; i++) { + if (!str[i]) return i; + } + return strsz; +} diff --git a/third_party/spirv-tools/source/binary.h b/third_party/spirv-tools/source/binary.h new file mode 100644 index 0000000..66d24c7 --- /dev/null +++ b/third_party/spirv-tools/source/binary.h @@ -0,0 +1,36 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_BINARY_H_ +#define SOURCE_BINARY_H_ + +#include "source/spirv_definition.h" +#include "spirv-tools/libspirv.h" + +// Functions + +// Grabs the header from the SPIR-V module given in the binary parameter. The +// endian parameter specifies the endianness of the binary module. On success, +// returns SPV_SUCCESS and writes the parsed header into *header. +spv_result_t spvBinaryHeaderGet(const spv_const_binary binary, + const spv_endianness_t endian, + spv_header_t* header); + +// Returns the number of non-null characters in str before the first null +// character, or strsz if there is no null character. Examines at most the +// first strsz characters in str. Returns 0 if str is nullptr. This is a +// replacement for C11's strnlen_s which might not exist in all environments. +size_t spv_strnlen_s(const char* str, size_t strsz); + +#endif // SOURCE_BINARY_H_ diff --git a/third_party/spirv-tools/source/cfa.h b/third_party/spirv-tools/source/cfa.h new file mode 100644 index 0000000..0d09014 --- /dev/null +++ b/third_party/spirv-tools/source/cfa.h @@ -0,0 +1,347 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_CFA_H_ +#define SOURCE_CFA_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spvtools { + +// Control Flow Analysis of control flow graphs of basic block nodes |BB|. +template +class CFA { + using bb_ptr = BB*; + using cbb_ptr = const BB*; + using bb_iter = typename std::vector::const_iterator; + using get_blocks_func = std::function*(const BB*)>; + + struct block_info { + cbb_ptr block; ///< pointer to the block + bb_iter iter; ///< Iterator to the current child node being processed + }; + + /// Returns true if a block with @p id is found in the @p work_list vector + /// + /// @param[in] work_list Set of blocks visited in the the depth first + /// traversal + /// of the CFG + /// @param[in] id The ID of the block being checked + /// + /// @return true if the edge work_list.back().block->id() => id is a back-edge + static bool FindInWorkList(const std::vector& work_list, + uint32_t id); + + public: + /// @brief Depth first traversal starting from the \p entry BasicBlock + /// + /// This function performs a depth first traversal from the \p entry + /// BasicBlock and calls the pre/postorder functions when it needs to process + /// the node in pre order, post order. It also calls the backedge function + /// when a back edge is encountered. + /// + /// @param[in] entry The root BasicBlock of a CFG + /// @param[in] successor_func A function which will return a pointer to the + /// successor nodes + /// @param[in] preorder A function that will be called for every block in a + /// CFG following preorder traversal semantics + /// @param[in] postorder A function that will be called for every block in a + /// CFG following postorder traversal semantics + /// @param[in] backedge A function that will be called when a backedge is + /// encountered during a traversal + /// NOTE: The @p successor_func and predecessor_func each return a pointer to + /// a + /// collection such that iterators to that collection remain valid for the + /// lifetime of the algorithm. + static void DepthFirstTraversal( + const BB* entry, get_blocks_func successor_func, + std::function preorder, + std::function postorder, + std::function backedge); + + /// @brief Calculates dominator edges for a set of blocks + /// + /// Computes dominators using the algorithm of Cooper, Harvey, and Kennedy + /// "A Simple, Fast Dominance Algorithm", 2001. + /// + /// The algorithm assumes there is a unique root node (a node without + /// predecessors), and it is therefore at the end of the postorder vector. + /// + /// This function calculates the dominator edges for a set of blocks in the + /// CFG. + /// Uses the dominator algorithm by Cooper et al. + /// + /// @param[in] postorder A vector of blocks in post order traversal + /// order + /// in a CFG + /// @param[in] predecessor_func Function used to get the predecessor nodes of + /// a + /// block + /// + /// @return the dominator tree of the graph, as a vector of pairs of nodes. + /// The first node in the pair is a node in the graph. The second node in the + /// pair is its immediate dominator in the sense of Cooper et.al., where a + /// block + /// without predecessors (such as the root node) is its own immediate + /// dominator. + static std::vector> CalculateDominators( + const std::vector& postorder, get_blocks_func predecessor_func); + + // Computes a minimal set of root nodes required to traverse, in the forward + // direction, the CFG represented by the given vector of blocks, and successor + // and predecessor functions. When considering adding two nodes, each having + // predecessors, favour using the one that appears earlier on the input blocks + // list. + static std::vector TraversalRoots(const std::vector& blocks, + get_blocks_func succ_func, + get_blocks_func pred_func); + + static void ComputeAugmentedCFG( + std::vector& ordered_blocks, BB* pseudo_entry_block, + BB* pseudo_exit_block, + std::unordered_map>* augmented_successors_map, + std::unordered_map>* + augmented_predecessors_map, + get_blocks_func succ_func, get_blocks_func pred_func); +}; + +template +bool CFA::FindInWorkList(const std::vector& work_list, + uint32_t id) { + for (const auto& b : work_list) { + if (b.block->id() == id) return true; + } + return false; +} + +template +void CFA::DepthFirstTraversal( + const BB* entry, get_blocks_func successor_func, + std::function preorder, + std::function postorder, + std::function backedge) { + std::unordered_set processed; + + /// NOTE: work_list is the sequence of nodes from the root node to the node + /// being processed in the traversal + std::vector work_list; + work_list.reserve(10); + + work_list.push_back({entry, std::begin(*successor_func(entry))}); + preorder(entry); + processed.insert(entry->id()); + + while (!work_list.empty()) { + block_info& top = work_list.back(); + if (top.iter == end(*successor_func(top.block))) { + postorder(top.block); + work_list.pop_back(); + } else { + BB* child = *top.iter; + top.iter++; + if (FindInWorkList(work_list, child->id())) { + backedge(top.block, child); + } + if (processed.count(child->id()) == 0) { + preorder(child); + work_list.emplace_back( + block_info{child, std::begin(*successor_func(child))}); + processed.insert(child->id()); + } + } + } +} + +template +std::vector> CFA::CalculateDominators( + const std::vector& postorder, get_blocks_func predecessor_func) { + struct block_detail { + size_t dominator; ///< The index of blocks's dominator in post order array + size_t postorder_index; ///< The index of the block in the post order array + }; + const size_t undefined_dom = postorder.size(); + + std::unordered_map idoms; + for (size_t i = 0; i < postorder.size(); i++) { + idoms[postorder[i]] = {undefined_dom, i}; + } + idoms[postorder.back()].dominator = idoms[postorder.back()].postorder_index; + + bool changed = true; + while (changed) { + changed = false; + for (auto b = postorder.rbegin() + 1; b != postorder.rend(); ++b) { + const std::vector& predecessors = *predecessor_func(*b); + // Find the first processed/reachable predecessor that is reachable + // in the forward traversal. + auto res = std::find_if(std::begin(predecessors), std::end(predecessors), + [&idoms, undefined_dom](BB* pred) { + return idoms.count(pred) && + idoms[pred].dominator != undefined_dom; + }); + if (res == end(predecessors)) continue; + const BB* idom = *res; + size_t idom_idx = idoms[idom].postorder_index; + + // all other predecessors + for (const auto* p : predecessors) { + if (idom == p) continue; + // Only consider nodes reachable in the forward traversal. + // Otherwise the intersection doesn't make sense and will never + // terminate. + if (!idoms.count(p)) continue; + if (idoms[p].dominator != undefined_dom) { + size_t finger1 = idoms[p].postorder_index; + size_t finger2 = idom_idx; + while (finger1 != finger2) { + while (finger1 < finger2) { + finger1 = idoms[postorder[finger1]].dominator; + } + while (finger2 < finger1) { + finger2 = idoms[postorder[finger2]].dominator; + } + } + idom_idx = finger1; + } + } + if (idoms[*b].dominator != idom_idx) { + idoms[*b].dominator = idom_idx; + changed = true; + } + } + } + + std::vector> out; + for (auto idom : idoms) { + // NOTE: performing a const cast for convenient usage with + // UpdateImmediateDominators + out.push_back({const_cast(std::get<0>(idom)), + const_cast(postorder[std::get<1>(idom).dominator])}); + } + + // Sort by postorder index to generate a deterministic ordering of edges. + std::sort( + out.begin(), out.end(), + [&idoms](const std::pair& lhs, + const std::pair& rhs) { + assert(lhs.first); + assert(lhs.second); + assert(rhs.first); + assert(rhs.second); + auto lhs_indices = std::make_pair(idoms[lhs.first].postorder_index, + idoms[lhs.second].postorder_index); + auto rhs_indices = std::make_pair(idoms[rhs.first].postorder_index, + idoms[rhs.second].postorder_index); + return lhs_indices < rhs_indices; + }); + return out; +} + +template +std::vector CFA::TraversalRoots(const std::vector& blocks, + get_blocks_func succ_func, + get_blocks_func pred_func) { + // The set of nodes which have been visited from any of the roots so far. + std::unordered_set visited; + + auto mark_visited = [&visited](const BB* b) { visited.insert(b); }; + auto ignore_block = [](const BB*) {}; + auto ignore_blocks = [](const BB*, const BB*) {}; + + auto traverse_from_root = [&mark_visited, &succ_func, &ignore_block, + &ignore_blocks](const BB* entry) { + DepthFirstTraversal(entry, succ_func, mark_visited, ignore_block, + ignore_blocks); + }; + + std::vector result; + + // First collect nodes without predecessors. + for (auto block : blocks) { + if (pred_func(block)->empty()) { + assert(visited.count(block) == 0 && "Malformed graph!"); + result.push_back(block); + traverse_from_root(block); + } + } + + // Now collect other stranded nodes. These must be in unreachable cycles. + for (auto block : blocks) { + if (visited.count(block) == 0) { + result.push_back(block); + traverse_from_root(block); + } + } + + return result; +} + +template +void CFA::ComputeAugmentedCFG( + std::vector& ordered_blocks, BB* pseudo_entry_block, + BB* pseudo_exit_block, + std::unordered_map>* augmented_successors_map, + std::unordered_map>* augmented_predecessors_map, + get_blocks_func succ_func, get_blocks_func pred_func) { + // Compute the successors of the pseudo-entry block, and + // the predecessors of the pseudo exit block. + auto sources = TraversalRoots(ordered_blocks, succ_func, pred_func); + + // For the predecessor traversals, reverse the order of blocks. This + // will affect the post-dominance calculation as follows: + // - Suppose you have blocks A and B, with A appearing before B in + // the list of blocks. + // - Also, A branches only to B, and B branches only to A. + // - We want to compute A as dominating B, and B as post-dominating B. + // By using reversed blocks for predecessor traversal roots discovery, + // we'll add an edge from B to the pseudo-exit node, rather than from A. + // All this is needed to correctly process the dominance/post-dominance + // constraint when A is a loop header that points to itself as its + // own continue target, and B is the latch block for the loop. + std::vector reversed_blocks(ordered_blocks.rbegin(), + ordered_blocks.rend()); + auto sinks = TraversalRoots(reversed_blocks, pred_func, succ_func); + + // Wire up the pseudo entry block. + (*augmented_successors_map)[pseudo_entry_block] = sources; + for (auto block : sources) { + auto& augmented_preds = (*augmented_predecessors_map)[block]; + const auto preds = pred_func(block); + augmented_preds.reserve(1 + preds->size()); + augmented_preds.push_back(pseudo_entry_block); + augmented_preds.insert(augmented_preds.end(), preds->begin(), preds->end()); + } + + // Wire up the pseudo exit block. + (*augmented_predecessors_map)[pseudo_exit_block] = sinks; + for (auto block : sinks) { + auto& augmented_succ = (*augmented_successors_map)[block]; + const auto succ = succ_func(block); + augmented_succ.reserve(1 + succ->size()); + augmented_succ.push_back(pseudo_exit_block); + augmented_succ.insert(augmented_succ.end(), succ->begin(), succ->end()); + } +} + +} // namespace spvtools + +#endif // SOURCE_CFA_H_ diff --git a/third_party/spirv-tools/source/diagnostic.cpp b/third_party/spirv-tools/source/diagnostic.cpp new file mode 100644 index 0000000..edc27c8 --- /dev/null +++ b/third_party/spirv-tools/source/diagnostic.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/diagnostic.h" + +#include +#include +#include +#include +#include + +#include "source/table.h" + +// Diagnostic API + +spv_diagnostic spvDiagnosticCreate(const spv_position position, + const char* message) { + spv_diagnostic diagnostic = new spv_diagnostic_t; + if (!diagnostic) return nullptr; + size_t length = strlen(message) + 1; + diagnostic->error = new char[length]; + if (!diagnostic->error) { + delete diagnostic; + return nullptr; + } + diagnostic->position = *position; + diagnostic->isTextSource = false; + memset(diagnostic->error, 0, length); + strncpy(diagnostic->error, message, length); + return diagnostic; +} + +void spvDiagnosticDestroy(spv_diagnostic diagnostic) { + if (!diagnostic) return; + delete[] diagnostic->error; + delete diagnostic; +} + +spv_result_t spvDiagnosticPrint(const spv_diagnostic diagnostic) { + if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC; + + if (diagnostic->isTextSource) { + // NOTE: This is a text position + // NOTE: add 1 to the line as editors start at line 1, we are counting new + // line characters to start at line 0 + std::cerr << "error: " << diagnostic->position.line + 1 << ": " + << diagnostic->position.column + 1 << ": " << diagnostic->error + << "\n"; + return SPV_SUCCESS; + } + + // NOTE: Assume this is a binary position + std::cerr << "error: "; + if (diagnostic->position.index > 0) + std::cerr << diagnostic->position.index << ": "; + std::cerr << diagnostic->error << "\n"; + return SPV_SUCCESS; +} + +namespace spvtools { + +DiagnosticStream::DiagnosticStream(DiagnosticStream&& other) + : stream_(), + position_(other.position_), + consumer_(other.consumer_), + disassembled_instruction_(std::move(other.disassembled_instruction_)), + error_(other.error_) { + // Prevent the other object from emitting output during destruction. + other.error_ = SPV_FAILED_MATCH; + // Some platforms are missing support for std::ostringstream functionality, + // including: move constructor, swap method. Either would have been a + // better choice than copying the string. + stream_ << other.stream_.str(); +} + +DiagnosticStream::~DiagnosticStream() { + if (error_ != SPV_FAILED_MATCH && consumer_ != nullptr) { + auto level = SPV_MSG_ERROR; + switch (error_) { + case SPV_SUCCESS: + case SPV_REQUESTED_TERMINATION: // Essentially success. + level = SPV_MSG_INFO; + break; + case SPV_WARNING: + level = SPV_MSG_WARNING; + break; + case SPV_UNSUPPORTED: + case SPV_ERROR_INTERNAL: + case SPV_ERROR_INVALID_TABLE: + level = SPV_MSG_INTERNAL_ERROR; + break; + case SPV_ERROR_OUT_OF_MEMORY: + level = SPV_MSG_FATAL; + break; + default: + break; + } + if (disassembled_instruction_.size() > 0) + stream_ << std::endl << " " << disassembled_instruction_ << std::endl; + + consumer_(level, "input", position_, stream_.str().c_str()); + } +} + +void UseDiagnosticAsMessageConsumer(spv_context context, + spv_diagnostic* diagnostic) { + assert(diagnostic && *diagnostic == nullptr); + + auto create_diagnostic = [diagnostic](spv_message_level_t, const char*, + const spv_position_t& position, + const char* message) { + auto p = position; + spvDiagnosticDestroy(*diagnostic); // Avoid memory leak. + *diagnostic = spvDiagnosticCreate(&p, message); + }; + SetContextMessageConsumer(context, std::move(create_diagnostic)); +} + +std::string spvResultToString(spv_result_t res) { + std::string out; + switch (res) { + case SPV_SUCCESS: + out = "SPV_SUCCESS"; + break; + case SPV_UNSUPPORTED: + out = "SPV_UNSUPPORTED"; + break; + case SPV_END_OF_STREAM: + out = "SPV_END_OF_STREAM"; + break; + case SPV_WARNING: + out = "SPV_WARNING"; + break; + case SPV_FAILED_MATCH: + out = "SPV_FAILED_MATCH"; + break; + case SPV_REQUESTED_TERMINATION: + out = "SPV_REQUESTED_TERMINATION"; + break; + case SPV_ERROR_INTERNAL: + out = "SPV_ERROR_INTERNAL"; + break; + case SPV_ERROR_OUT_OF_MEMORY: + out = "SPV_ERROR_OUT_OF_MEMORY"; + break; + case SPV_ERROR_INVALID_POINTER: + out = "SPV_ERROR_INVALID_POINTER"; + break; + case SPV_ERROR_INVALID_BINARY: + out = "SPV_ERROR_INVALID_BINARY"; + break; + case SPV_ERROR_INVALID_TEXT: + out = "SPV_ERROR_INVALID_TEXT"; + break; + case SPV_ERROR_INVALID_TABLE: + out = "SPV_ERROR_INVALID_TABLE"; + break; + case SPV_ERROR_INVALID_VALUE: + out = "SPV_ERROR_INVALID_VALUE"; + break; + case SPV_ERROR_INVALID_DIAGNOSTIC: + out = "SPV_ERROR_INVALID_DIAGNOSTIC"; + break; + case SPV_ERROR_INVALID_LOOKUP: + out = "SPV_ERROR_INVALID_LOOKUP"; + break; + case SPV_ERROR_INVALID_ID: + out = "SPV_ERROR_INVALID_ID"; + break; + case SPV_ERROR_INVALID_CFG: + out = "SPV_ERROR_INVALID_CFG"; + break; + case SPV_ERROR_INVALID_LAYOUT: + out = "SPV_ERROR_INVALID_LAYOUT"; + break; + default: + out = "Unknown Error"; + } + return out; +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/diagnostic.h b/third_party/spirv-tools/source/diagnostic.h new file mode 100644 index 0000000..22df961 --- /dev/null +++ b/third_party/spirv-tools/source/diagnostic.h @@ -0,0 +1,79 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_DIAGNOSTIC_H_ +#define SOURCE_DIAGNOSTIC_H_ + +#include +#include + +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { + +// A DiagnosticStream remembers the current position of the input and an error +// code, and captures diagnostic messages via the left-shift operator. +// If the error code is not SPV_FAILED_MATCH, then captured messages are +// emitted during the destructor. +class DiagnosticStream { + public: + DiagnosticStream(spv_position_t position, const MessageConsumer& consumer, + const std::string& disassembled_instruction, + spv_result_t error) + : position_(position), + consumer_(consumer), + disassembled_instruction_(disassembled_instruction), + error_(error) {} + + // Creates a DiagnosticStream from an expiring DiagnosticStream. + // The new object takes the contents of the other, and prevents the + // other from emitting anything during destruction. + DiagnosticStream(DiagnosticStream&& other); + + // Destroys a DiagnosticStream. + // If its status code is something other than SPV_FAILED_MATCH + // then emit the accumulated message to the consumer. + ~DiagnosticStream(); + + // Adds the given value to the diagnostic message to be written. + template + DiagnosticStream& operator<<(const T& val) { + stream_ << val; + return *this; + } + + // Conversion operator to spv_result, returning the error code. + operator spv_result_t() { return error_; } + + private: + std::ostringstream stream_; + spv_position_t position_; + MessageConsumer consumer_; // Message consumer callback. + std::string disassembled_instruction_; + spv_result_t error_; +}; + +// Changes the MessageConsumer in |context| to one that updates |diagnostic| +// with the last message received. +// +// This function expects that |diagnostic| is not nullptr and its content is a +// nullptr. +void UseDiagnosticAsMessageConsumer(spv_context context, + spv_diagnostic* diagnostic); + +std::string spvResultToString(spv_result_t res); + +} // namespace spvtools + +#endif // SOURCE_DIAGNOSTIC_H_ diff --git a/third_party/spirv-tools/source/disassemble.cpp b/third_party/spirv-tools/source/disassemble.cpp new file mode 100644 index 0000000..e763251 --- /dev/null +++ b/third_party/spirv-tools/source/disassemble.cpp @@ -0,0 +1,533 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains a disassembler: It converts a SPIR-V binary +// to text. + +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/disassemble.h" +#include "source/ext_inst.h" +#include "source/name_mapper.h" +#include "source/opcode.h" +#include "source/parsed_operand.h" +#include "source/print.h" +#include "source/spirv_constant.h" +#include "source/spirv_endian.h" +#include "source/util/hex_float.h" +#include "source/util/make_unique.h" +#include "spirv-tools/libspirv.h" + +namespace { + +// A Disassembler instance converts a SPIR-V binary to its assembly +// representation. +class Disassembler { + public: + Disassembler(const spvtools::AssemblyGrammar& grammar, uint32_t options, + spvtools::NameMapper name_mapper) + : grammar_(grammar), + print_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_PRINT, options)), + color_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COLOR, options)), + indent_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_INDENT, options) + ? kStandardIndent + : 0), + comment_(spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_COMMENT, options)), + text_(), + out_(print_ ? out_stream() : out_stream(text_)), + stream_(out_.get()), + header_(!spvIsInBitfield(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER, options)), + show_byte_offset_(spvIsInBitfield( + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET, options)), + byte_offset_(0), + name_mapper_(std::move(name_mapper)) {} + + // Emits the assembly header for the module, and sets up internal state + // so subsequent callbacks can handle the cases where the entire module + // is either big-endian or little-endian. + spv_result_t HandleHeader(spv_endianness_t endian, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema); + // Emits the assembly text for the given instruction. + spv_result_t HandleInstruction(const spv_parsed_instruction_t& inst); + + // If not printing, populates text_result with the accumulated text. + // Returns SPV_SUCCESS on success. + spv_result_t SaveTextResult(spv_text* text_result) const; + + private: + enum { kStandardIndent = 15 }; + + using out_stream = spvtools::out_stream; + + // Emits an operand for the given instruction, where the instruction + // is at offset words from the start of the binary. + void EmitOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index); + + // Emits a mask expression for the given mask word of the specified type. + void EmitMaskOperand(const spv_operand_type_t type, const uint32_t word); + + // Resets the output color, if color is turned on. + void ResetColor() { + if (color_) out_.get() << spvtools::clr::reset{print_}; + } + // Sets the output to grey, if color is turned on. + void SetGrey() { + if (color_) out_.get() << spvtools::clr::grey{print_}; + } + // Sets the output to blue, if color is turned on. + void SetBlue() { + if (color_) out_.get() << spvtools::clr::blue{print_}; + } + // Sets the output to yellow, if color is turned on. + void SetYellow() { + if (color_) out_.get() << spvtools::clr::yellow{print_}; + } + // Sets the output to red, if color is turned on. + void SetRed() { + if (color_) out_.get() << spvtools::clr::red{print_}; + } + // Sets the output to green, if color is turned on. + void SetGreen() { + if (color_) out_.get() << spvtools::clr::green{print_}; + } + + const spvtools::AssemblyGrammar& grammar_; + const bool print_; // Should we also print to the standard output stream? + const bool color_; // Should we print in colour? + const int indent_; // How much to indent. 0 means don't indent + const int comment_; // Should we comment the source + spv_endianness_t endian_; // The detected endianness of the binary. + std::stringstream text_; // Captures the text, if not printing. + out_stream out_; // The Output stream. Either to text_ or standard output. + std::ostream& stream_; // The output std::stream. + const bool header_; // Should we output header as the leading comment? + const bool show_byte_offset_; // Should we print byte offset, in hex? + size_t byte_offset_; // The number of bytes processed so far. + spvtools::NameMapper name_mapper_; + bool inserted_decoration_space_ = false; + bool inserted_debug_space_ = false; + bool inserted_type_space_ = false; +}; + +spv_result_t Disassembler::HandleHeader(spv_endianness_t endian, + uint32_t version, uint32_t generator, + uint32_t id_bound, uint32_t schema) { + endian_ = endian; + + if (header_) { + const char* generator_tool = + spvGeneratorStr(SPV_GENERATOR_TOOL_PART(generator)); + stream_ << "; SPIR-V\n" + << "; Version: " << SPV_SPIRV_VERSION_MAJOR_PART(version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(version) << "\n" + << "; Generator: " << generator_tool; + // For unknown tools, print the numeric tool value. + if (0 == strcmp("Unknown", generator_tool)) { + stream_ << "(" << SPV_GENERATOR_TOOL_PART(generator) << ")"; + } + // Print the miscellaneous part of the generator word on the same + // line as the tool name. + stream_ << "; " << SPV_GENERATOR_MISC_PART(generator) << "\n" + << "; Bound: " << id_bound << "\n" + << "; Schema: " << schema << "\n"; + } + + byte_offset_ = SPV_INDEX_INSTRUCTION * sizeof(uint32_t); + + return SPV_SUCCESS; +} + +spv_result_t Disassembler::HandleInstruction( + const spv_parsed_instruction_t& inst) { + auto opcode = static_cast(inst.opcode); + if (comment_ && opcode == SpvOpFunction) { + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Function " << name_mapper_(inst.result_id) << std::endl; + } + if (comment_ && !inserted_decoration_space_ && + spvOpcodeIsDecoration(opcode)) { + inserted_decoration_space_ = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Annotations" << std::endl; + } + if (comment_ && !inserted_debug_space_ && spvOpcodeIsDebug(opcode)) { + inserted_debug_space_ = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Debug Information" << std::endl; + } + if (comment_ && !inserted_type_space_ && spvOpcodeGeneratesType(opcode)) { + inserted_type_space_ = true; + stream_ << std::endl; + stream_ << std::string(indent_, ' '); + stream_ << "; Types, variables and constants" << std::endl; + } + + if (inst.result_id) { + SetBlue(); + const std::string id_name = name_mapper_(inst.result_id); + if (indent_) + stream_ << std::setw(std::max(0, indent_ - 3 - int(id_name.size()))); + stream_ << "%" << id_name; + ResetColor(); + stream_ << " = "; + } else { + stream_ << std::string(indent_, ' '); + } + + stream_ << "Op" << spvOpcodeString(opcode); + + for (uint16_t i = 0; i < inst.num_operands; i++) { + const spv_operand_type_t type = inst.operands[i].type; + assert(type != SPV_OPERAND_TYPE_NONE); + if (type == SPV_OPERAND_TYPE_RESULT_ID) continue; + stream_ << " "; + EmitOperand(inst, i); + } + + if (comment_ && opcode == SpvOpName) { + const spv_parsed_operand_t& operand = inst.operands[0]; + const uint32_t word = inst.words[operand.offset]; + stream_ << " ; id %" << word; + } + + if (show_byte_offset_) { + SetGrey(); + auto saved_flags = stream_.flags(); + auto saved_fill = stream_.fill(); + stream_ << " ; 0x" << std::setw(8) << std::hex << std::setfill('0') + << byte_offset_; + stream_.flags(saved_flags); + stream_.fill(saved_fill); + ResetColor(); + } + + byte_offset_ += inst.num_words * sizeof(uint32_t); + + stream_ << "\n"; + return SPV_SUCCESS; +} + +void Disassembler::EmitOperand(const spv_parsed_instruction_t& inst, + const uint16_t operand_index) { + assert(operand_index < inst.num_operands); + const spv_parsed_operand_t& operand = inst.operands[operand_index]; + const uint32_t word = inst.words[operand.offset]; + switch (operand.type) { + case SPV_OPERAND_TYPE_RESULT_ID: + assert(false && " is not supposed to be handled here"); + SetBlue(); + stream_ << "%" << name_mapper_(word); + break; + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + SetYellow(); + stream_ << "%" << name_mapper_(word); + break; + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { + spv_ext_inst_desc ext_inst; + SetRed(); + if (grammar_.lookupExtInst(inst.ext_inst_type, word, &ext_inst) == + SPV_SUCCESS) { + stream_ << ext_inst->name; + } else { + if (!spvExtInstIsNonSemantic(inst.ext_inst_type)) { + assert(false && "should have caught this earlier"); + } else { + // for non-semantic instruction sets we can just print the number + stream_ << word; + } + } + } break; + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { + spv_opcode_desc opcode_desc; + if (grammar_.lookupOpcode(SpvOp(word), &opcode_desc)) + assert(false && "should have caught this earlier"); + SetRed(); + stream_ << opcode_desc->name; + } break; + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { + SetRed(); + spvtools::EmitNumericLiteral(&stream_, inst, operand); + ResetColor(); + } break; + case SPV_OPERAND_TYPE_LITERAL_STRING: { + stream_ << "\""; + SetGreen(); + // Strings are always little-endian, and null-terminated. + // Write out the characters, escaping as needed, and without copying + // the entire string. + auto c_str = reinterpret_cast(inst.words + operand.offset); + for (auto p = c_str; *p; ++p) { + if (*p == '"' || *p == '\\') stream_ << '\\'; + stream_ << *p; + } + ResetColor(); + stream_ << '"'; + } break; + case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + case SPV_OPERAND_TYPE_MEMORY_MODEL: + case SPV_OPERAND_TYPE_EXECUTION_MODE: + case SPV_OPERAND_TYPE_STORAGE_CLASS: + case SPV_OPERAND_TYPE_DIMENSIONALITY: + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + case SPV_OPERAND_TYPE_DECORATION: + case SPV_OPERAND_TYPE_BUILT_IN: + case SPV_OPERAND_TYPE_GROUP_OPERATION: + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: { + spv_operand_desc entry; + if (grammar_.lookupOperand(operand.type, word, &entry)) + assert(false && "should have caught this earlier"); + stream_ << entry->name; + } break; + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + EmitMaskOperand(operand.type, word); + break; + default: + assert(false && "unhandled or invalid case"); + } + ResetColor(); +} + +void Disassembler::EmitMaskOperand(const spv_operand_type_t type, + const uint32_t word) { + // Scan the mask from least significant bit to most significant bit. For each + // set bit, emit the name of that bit. Separate multiple names with '|'. + uint32_t remaining_word = word; + uint32_t mask; + int num_emitted = 0; + for (mask = 1; remaining_word; mask <<= 1) { + if (remaining_word & mask) { + remaining_word ^= mask; + spv_operand_desc entry; + if (grammar_.lookupOperand(type, mask, &entry)) + assert(false && "should have caught this earlier"); + if (num_emitted) stream_ << "|"; + stream_ << entry->name; + num_emitted++; + } + } + if (!num_emitted) { + // An operand value of 0 was provided, so represent it by the name + // of the 0 value. In many cases, that's "None". + spv_operand_desc entry; + if (SPV_SUCCESS == grammar_.lookupOperand(type, 0, &entry)) + stream_ << entry->name; + } +} + +spv_result_t Disassembler::SaveTextResult(spv_text* text_result) const { + if (!print_) { + size_t length = text_.str().size(); + char* str = new char[length + 1]; + if (!str) return SPV_ERROR_OUT_OF_MEMORY; + strncpy(str, text_.str().c_str(), length + 1); + spv_text text = new spv_text_t(); + if (!text) { + delete[] str; + return SPV_ERROR_OUT_OF_MEMORY; + } + text->str = str; + text->length = length; + *text_result = text; + } + return SPV_SUCCESS; +} + +spv_result_t DisassembleHeader(void* user_data, spv_endianness_t endian, + uint32_t /* magic */, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema) { + assert(user_data); + auto disassembler = static_cast(user_data); + return disassembler->HandleHeader(endian, version, generator, id_bound, + schema); +} + +spv_result_t DisassembleInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto disassembler = static_cast(user_data); + return disassembler->HandleInstruction(*parsed_instruction); +} + +// Simple wrapper class to provide extra data necessary for targeted +// instruction disassembly. +class WrappedDisassembler { + public: + WrappedDisassembler(Disassembler* dis, const uint32_t* binary, size_t wc) + : disassembler_(dis), inst_binary_(binary), word_count_(wc) {} + + Disassembler* disassembler() { return disassembler_; } + const uint32_t* inst_binary() const { return inst_binary_; } + size_t word_count() const { return word_count_; } + + private: + Disassembler* disassembler_; + const uint32_t* inst_binary_; + const size_t word_count_; +}; + +spv_result_t DisassembleTargetHeader(void* user_data, spv_endianness_t endian, + uint32_t /* magic */, uint32_t version, + uint32_t generator, uint32_t id_bound, + uint32_t schema) { + assert(user_data); + auto wrapped = static_cast(user_data); + return wrapped->disassembler()->HandleHeader(endian, version, generator, + id_bound, schema); +} + +spv_result_t DisassembleTargetInstruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + assert(user_data); + auto wrapped = static_cast(user_data); + // Check if this is the instruction we want to disassemble. + if (wrapped->word_count() == parsed_instruction->num_words && + std::equal(wrapped->inst_binary(), + wrapped->inst_binary() + wrapped->word_count(), + parsed_instruction->words)) { + // Found the target instruction. Disassemble it and signal that we should + // stop searching so we don't output the same instruction again. + if (auto error = + wrapped->disassembler()->HandleInstruction(*parsed_instruction)) + return error; + return SPV_REQUESTED_TERMINATION; + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t spvBinaryToText(const spv_const_context context, + const uint32_t* code, const size_t wordCount, + const uint32_t options, spv_text* pText, + spv_diagnostic* pDiagnostic) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + const spvtools::AssemblyGrammar grammar(&hijack_context); + if (!grammar.isValid()) return SPV_ERROR_INVALID_TABLE; + + // Generate friendly names for Ids if requested. + std::unique_ptr friendly_mapper; + spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper(); + if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { + friendly_mapper = spvtools::MakeUnique( + &hijack_context, code, wordCount); + name_mapper = friendly_mapper->GetNameMapper(); + } + + // Now disassemble! + Disassembler disassembler(grammar, options, name_mapper); + if (auto error = spvBinaryParse(&hijack_context, &disassembler, code, + wordCount, DisassembleHeader, + DisassembleInstruction, pDiagnostic)) { + return error; + } + + return disassembler.SaveTextResult(pText); +} + +std::string spvtools::spvInstructionBinaryToText(const spv_target_env env, + const uint32_t* instCode, + const size_t instWordCount, + const uint32_t* code, + const size_t wordCount, + const uint32_t options) { + spv_context context = spvContextCreate(env); + const spvtools::AssemblyGrammar grammar(context); + if (!grammar.isValid()) { + spvContextDestroy(context); + return ""; + } + + // Generate friendly names for Ids if requested. + std::unique_ptr friendly_mapper; + spvtools::NameMapper name_mapper = spvtools::GetTrivialNameMapper(); + if (options & SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) { + friendly_mapper = spvtools::MakeUnique( + context, code, wordCount); + name_mapper = friendly_mapper->GetNameMapper(); + } + + // Now disassemble! + Disassembler disassembler(grammar, options, name_mapper); + WrappedDisassembler wrapped(&disassembler, instCode, instWordCount); + spvBinaryParse(context, &wrapped, code, wordCount, DisassembleTargetHeader, + DisassembleTargetInstruction, nullptr); + + spv_text text = nullptr; + std::string output; + if (disassembler.SaveTextResult(&text) == SPV_SUCCESS) { + output.assign(text->str, text->str + text->length); + // Drop trailing newline characters. + while (!output.empty() && output.back() == '\n') output.pop_back(); + } + spvTextDestroy(text); + spvContextDestroy(context); + + return output; +} diff --git a/third_party/spirv-tools/source/disassemble.h b/third_party/spirv-tools/source/disassemble.h new file mode 100644 index 0000000..ac35742 --- /dev/null +++ b/third_party/spirv-tools/source/disassemble.h @@ -0,0 +1,38 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_DISASSEMBLE_H_ +#define SOURCE_DISASSEMBLE_H_ + +#include + +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Decodes the given SPIR-V instruction binary representation to its assembly +// text. The context is inferred from the provided module binary. The options +// parameter is a bit field of spv_binary_to_text_options_t. Decoded text will +// be stored into *text. Any error will be written into *diagnostic if +// diagnostic is non-null. +std::string spvInstructionBinaryToText(const spv_target_env env, + const uint32_t* inst_binary, + const size_t inst_word_count, + const uint32_t* binary, + const size_t word_count, + const uint32_t options); + +} // namespace spvtools + +#endif // SOURCE_DISASSEMBLE_H_ diff --git a/third_party/spirv-tools/source/enum_set.h b/third_party/spirv-tools/source/enum_set.h new file mode 100644 index 0000000..d4d31e3 --- /dev/null +++ b/third_party/spirv-tools/source/enum_set.h @@ -0,0 +1,208 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_ENUM_SET_H_ +#define SOURCE_ENUM_SET_H_ + +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "source/util/make_unique.h" + +namespace spvtools { + +// A set of values of a 32-bit enum type. +// It is fast and compact for the common case, where enum values +// are at most 63. But it can represent enums with larger values, +// as may appear in extensions. +template +class EnumSet { + private: + // The ForEach method will call the functor on enum values in + // enum value order (lowest to highest). To make that easier, use + // an ordered set for the overflow values. + using OverflowSetType = std::set; + + public: + // Construct an empty set. + EnumSet() {} + // Construct an set with just the given enum value. + explicit EnumSet(EnumType c) { Add(c); } + // Construct an set from an initializer list of enum values. + EnumSet(std::initializer_list cs) { + for (auto c : cs) Add(c); + } + EnumSet(uint32_t count, const EnumType* ptr) { + for (uint32_t i = 0; i < count; ++i) Add(ptr[i]); + } + // Copy constructor. + EnumSet(const EnumSet& other) { *this = other; } + // Move constructor. The moved-from set is emptied. + EnumSet(EnumSet&& other) { + mask_ = other.mask_; + overflow_ = std::move(other.overflow_); + other.mask_ = 0; + other.overflow_.reset(nullptr); + } + // Assignment operator. + EnumSet& operator=(const EnumSet& other) { + if (&other != this) { + mask_ = other.mask_; + overflow_.reset(other.overflow_ ? new OverflowSetType(*other.overflow_) + : nullptr); + } + return *this; + } + + friend bool operator==(const EnumSet& a, const EnumSet& b) { + if (a.mask_ != b.mask_) { + return false; + } + + if (a.overflow_ == nullptr && b.overflow_ == nullptr) { + return true; + } + + if (a.overflow_ == nullptr || b.overflow_ == nullptr) { + return false; + } + + return *a.overflow_ == *b.overflow_; + } + + friend bool operator!=(const EnumSet& a, const EnumSet& b) { + return !(a == b); + } + + // Adds the given enum value to the set. This has no effect if the + // enum value is already in the set. + void Add(EnumType c) { AddWord(ToWord(c)); } + + // Removes the given enum value from the set. This has no effect if the + // enum value is not in the set. + void Remove(EnumType c) { RemoveWord(ToWord(c)); } + + // Returns true if this enum value is in the set. + bool Contains(EnumType c) const { return ContainsWord(ToWord(c)); } + + // Applies f to each enum in the set, in order from smallest enum + // value to largest. + void ForEach(std::function f) const { + for (uint32_t i = 0; i < 64; ++i) { + if (mask_ & AsMask(i)) f(static_cast(i)); + } + if (overflow_) { + for (uint32_t c : *overflow_) f(static_cast(c)); + } + } + + // Returns true if the set is empty. + bool IsEmpty() const { + if (mask_) return false; + if (overflow_ && !overflow_->empty()) return false; + return true; + } + + // Returns true if the set contains ANY of the elements of |in_set|, + // or if |in_set| is empty. + bool HasAnyOf(const EnumSet& in_set) const { + if (in_set.IsEmpty()) return true; + + if (mask_ & in_set.mask_) return true; + + if (!overflow_ || !in_set.overflow_) return false; + + for (uint32_t item : *in_set.overflow_) { + if (overflow_->find(item) != overflow_->end()) return true; + } + + return false; + } + + private: + // Adds the given enum value (as a 32-bit word) to the set. This has no + // effect if the enum value is already in the set. + void AddWord(uint32_t word) { + if (auto new_bits = AsMask(word)) { + mask_ |= new_bits; + } else { + Overflow().insert(word); + } + } + + // Removes the given enum value (as a 32-bit word) from the set. This has no + // effect if the enum value is not in the set. + void RemoveWord(uint32_t word) { + if (auto new_bits = AsMask(word)) { + mask_ &= ~new_bits; + } else { + auto itr = Overflow().find(word); + if (itr != Overflow().end()) Overflow().erase(itr); + } + } + + // Returns true if the enum represented as a 32-bit word is in the set. + bool ContainsWord(uint32_t word) const { + // We shouldn't call Overflow() since this is a const method. + if (auto bits = AsMask(word)) { + return (mask_ & bits) != 0; + } else if (auto overflow = overflow_.get()) { + return overflow->find(word) != overflow->end(); + } + // The word is large, but the set doesn't have large members, so + // it doesn't have an overflow set. + return false; + } + + // Returns the enum value as a uint32_t. + uint32_t ToWord(EnumType value) const { + static_assert(sizeof(EnumType) <= sizeof(uint32_t), + "EnumType must statically castable to uint32_t"); + return static_cast(value); + } + + // Determines whether the given enum value can be represented + // as a bit in a uint64_t mask. If so, then returns that mask bit. + // Otherwise, returns 0. + uint64_t AsMask(uint32_t word) const { + if (word > 63) return 0; + return uint64_t(1) << word; + } + + // Ensures that overflow_set_ references a set. A new empty set is + // allocated if one doesn't exist yet. Returns overflow_set_. + OverflowSetType& Overflow() { + if (overflow_.get() == nullptr) { + overflow_ = MakeUnique(); + } + return *overflow_; + } + + // Enums with values up to 63 are stored as bits in this mask. + uint64_t mask_ = 0; + // Enums with values larger than 63 are stored in this set. + // This set should normally be empty or very small. + std::unique_ptr overflow_ = {}; +}; + +// A set of SpvCapability, optimized for small capability values. +using CapabilitySet = EnumSet; + +} // namespace spvtools + +#endif // SOURCE_ENUM_SET_H_ diff --git a/third_party/spirv-tools/source/enum_string_mapping.cpp b/third_party/spirv-tools/source/enum_string_mapping.cpp new file mode 100644 index 0000000..32361a0 --- /dev/null +++ b/third_party/spirv-tools/source/enum_string_mapping.cpp @@ -0,0 +1,29 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/enum_string_mapping.h" + +#include +#include +#include +#include +#include + +#include "source/extensions.h" + +namespace spvtools { + +#include "enum_string_mapping.inc" + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/enum_string_mapping.h b/third_party/spirv-tools/source/enum_string_mapping.h new file mode 100644 index 0000000..af8f56b --- /dev/null +++ b/third_party/spirv-tools/source/enum_string_mapping.h @@ -0,0 +1,36 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_ENUM_STRING_MAPPING_H_ +#define SOURCE_ENUM_STRING_MAPPING_H_ + +#include + +#include "source/extensions.h" +#include "source/latest_version_spirv_header.h" + +namespace spvtools { + +// Finds Extension enum corresponding to |str|. Returns false if not found. +bool GetExtensionFromString(const char* str, Extension* extension); + +// Returns text string corresponding to |extension|. +const char* ExtensionToString(Extension extension); + +// Returns text string corresponding to |capability|. +const char* CapabilityToString(SpvCapability capability); + +} // namespace spvtools + +#endif // SOURCE_ENUM_STRING_MAPPING_H_ diff --git a/third_party/spirv-tools/source/ext_inst.cpp b/third_party/spirv-tools/source/ext_inst.cpp new file mode 100644 index 0000000..3471ebe --- /dev/null +++ b/third_party/spirv-tools/source/ext_inst.cpp @@ -0,0 +1,199 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/ext_inst.h" + +#include + +// DebugInfo extended instruction set. +// See https://www.khronos.org/registry/spir-v/specs/1.0/DebugInfo.html +// TODO(dneto): DebugInfo.h should probably move to SPIRV-Headers. +#include "DebugInfo.h" + +#include "source/latest_version_glsl_std_450_header.h" +#include "source/latest_version_opencl_std_header.h" +#include "source/macro.h" +#include "source/spirv_definition.h" + +#include "debuginfo.insts.inc" +#include "glsl.std.450.insts.inc" +#include "nonsemantic.clspvreflection.insts.inc" +#include "opencl.debuginfo.100.insts.inc" +#include "opencl.std.insts.inc" + +#include "spirv-tools/libspirv.h" +#include "spv-amd-gcn-shader.insts.inc" +#include "spv-amd-shader-ballot.insts.inc" +#include "spv-amd-shader-explicit-vertex-parameter.insts.inc" +#include "spv-amd-shader-trinary-minmax.insts.inc" + +static const spv_ext_inst_group_t kGroups_1_0[] = { + {SPV_EXT_INST_TYPE_GLSL_STD_450, ARRAY_SIZE(glsl_entries), glsl_entries}, + {SPV_EXT_INST_TYPE_OPENCL_STD, ARRAY_SIZE(opencl_entries), opencl_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER, + ARRAY_SIZE(spv_amd_shader_explicit_vertex_parameter_entries), + spv_amd_shader_explicit_vertex_parameter_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX, + ARRAY_SIZE(spv_amd_shader_trinary_minmax_entries), + spv_amd_shader_trinary_minmax_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER, + ARRAY_SIZE(spv_amd_gcn_shader_entries), spv_amd_gcn_shader_entries}, + {SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT, + ARRAY_SIZE(spv_amd_shader_ballot_entries), spv_amd_shader_ballot_entries}, + {SPV_EXT_INST_TYPE_DEBUGINFO, ARRAY_SIZE(debuginfo_entries), + debuginfo_entries}, + {SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + ARRAY_SIZE(opencl_debuginfo_100_entries), opencl_debuginfo_100_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, + ARRAY_SIZE(nonsemantic_clspvreflection_entries), + nonsemantic_clspvreflection_entries}, +}; + +static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0), + kGroups_1_0}; + +spv_result_t spvExtInstTableGet(spv_ext_inst_table* pExtInstTable, + spv_target_env env) { + if (!pExtInstTable) return SPV_ERROR_INVALID_POINTER; + + switch (env) { + // The extended instruction sets are all version 1.0 so far. + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_WEBGPU_0: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + *pExtInstTable = &kTable_1_0; + return SPV_SUCCESS; + default: + return SPV_ERROR_INVALID_TABLE; + } +} + +spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { + // The names are specified by the respective extension instruction + // specifications. + if (!strcmp("GLSL.std.450", name)) { + return SPV_EXT_INST_TYPE_GLSL_STD_450; + } + if (!strcmp("OpenCL.std", name)) { + return SPV_EXT_INST_TYPE_OPENCL_STD; + } + if (!strcmp("SPV_AMD_shader_explicit_vertex_parameter", name)) { + return SPV_EXT_INST_TYPE_SPV_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER; + } + if (!strcmp("SPV_AMD_shader_trinary_minmax", name)) { + return SPV_EXT_INST_TYPE_SPV_AMD_SHADER_TRINARY_MINMAX; + } + if (!strcmp("SPV_AMD_gcn_shader", name)) { + return SPV_EXT_INST_TYPE_SPV_AMD_GCN_SHADER; + } + if (!strcmp("SPV_AMD_shader_ballot", name)) { + return SPV_EXT_INST_TYPE_SPV_AMD_SHADER_BALLOT; + } + if (!strcmp("DebugInfo", name)) { + return SPV_EXT_INST_TYPE_DEBUGINFO; + } + if (!strcmp("OpenCL.DebugInfo.100", name)) { + return SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100; + } + if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION; + } + // ensure to add any known non-semantic extended instruction sets + // above this point, and update spvExtInstIsNonSemantic() + if (!strncmp("NonSemantic.", name, 12)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN; + } + return SPV_EXT_INST_TYPE_NONE; +} + +bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { + if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { + return true; + } + return false; +} + +bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type) { + if (type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + type == SPV_EXT_INST_TYPE_DEBUGINFO) { + return true; + } + return false; +} + +spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, + const spv_ext_inst_type_t type, + const char* name, + spv_ext_inst_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!pEntry) return SPV_ERROR_INVALID_POINTER; + + for (uint32_t groupIndex = 0; groupIndex < table->count; groupIndex++) { + const auto& group = table->groups[groupIndex]; + if (type != group.type) continue; + for (uint32_t index = 0; index < group.count; index++) { + const auto& entry = group.entries[index]; + if (!strcmp(name, entry.name)) { + *pEntry = &entry; + return SPV_SUCCESS; + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, + const spv_ext_inst_type_t type, + const uint32_t value, + spv_ext_inst_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!pEntry) return SPV_ERROR_INVALID_POINTER; + + for (uint32_t groupIndex = 0; groupIndex < table->count; groupIndex++) { + const auto& group = table->groups[groupIndex]; + if (type != group.type) continue; + for (uint32_t index = 0; index < group.count; index++) { + const auto& entry = group.entries[index]; + if (value == entry.ext_inst) { + *pEntry = &entry; + return SPV_SUCCESS; + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} diff --git a/third_party/spirv-tools/source/ext_inst.h b/third_party/spirv-tools/source/ext_inst.h new file mode 100644 index 0000000..aff6e30 --- /dev/null +++ b/third_party/spirv-tools/source/ext_inst.h @@ -0,0 +1,46 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_EXT_INST_H_ +#define SOURCE_EXT_INST_H_ + +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +// Gets the type of the extended instruction set with the specified name. +spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name); + +// Returns true if the extended instruction set is non-semantic +bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type); + +// Returns true if the extended instruction set is debug info +bool spvExtInstIsDebugInfo(const spv_ext_inst_type_t type); + +// Finds the named extented instruction of the given type in the given extended +// instruction table. On success, returns SPV_SUCCESS and writes a handle of +// the instruction entry into *entry. +spv_result_t spvExtInstTableNameLookup(const spv_ext_inst_table table, + const spv_ext_inst_type_t type, + const char* name, + spv_ext_inst_desc* entry); + +// Finds the extented instruction of the given type in the given extended +// instruction table by value. On success, returns SPV_SUCCESS and writes a +// handle of the instruction entry into *entry. +spv_result_t spvExtInstTableValueLookup(const spv_ext_inst_table table, + const spv_ext_inst_type_t type, + const uint32_t value, + spv_ext_inst_desc* pEntry); + +#endif // SOURCE_EXT_INST_H_ diff --git a/third_party/spirv-tools/source/extensions.cpp b/third_party/spirv-tools/source/extensions.cpp new file mode 100644 index 0000000..a94db27 --- /dev/null +++ b/third_party/spirv-tools/source/extensions.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/extensions.h" + +#include +#include +#include + +#include "source/enum_string_mapping.h" + +namespace spvtools { + +std::string GetExtensionString(const spv_parsed_instruction_t* inst) { + if (inst->opcode != SpvOpExtension) return "ERROR_not_op_extension"; + + assert(inst->num_operands == 1); + + const auto& operand = inst->operands[0]; + assert(operand.type == SPV_OPERAND_TYPE_LITERAL_STRING); + assert(inst->num_words > operand.offset); + + return reinterpret_cast(inst->words + operand.offset); +} + +std::string ExtensionSetToString(const ExtensionSet& extensions) { + std::stringstream ss; + extensions.ForEach( + [&ss](Extension ext) { ss << ExtensionToString(ext) << " "; }); + return ss.str(); +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/extensions.h b/third_party/spirv-tools/source/extensions.h new file mode 100644 index 0000000..8023444 --- /dev/null +++ b/third_party/spirv-tools/source/extensions.h @@ -0,0 +1,40 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_EXTENSIONS_H_ +#define SOURCE_EXTENSIONS_H_ + +#include + +#include "source/enum_set.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// The known SPIR-V extensions. +enum Extension { +#include "extension_enum.inc" +}; + +using ExtensionSet = EnumSet; + +// Returns literal string operand of OpExtension instruction. +std::string GetExtensionString(const spv_parsed_instruction_t* inst); + +// Returns text string listing |extensions| separated by whitespace. +std::string ExtensionSetToString(const ExtensionSet& extensions); + +} // namespace spvtools + +#endif // SOURCE_EXTENSIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/CMakeLists.txt b/third_party/spirv-tools/source/fuzz/CMakeLists.txt new file mode 100644 index 0000000..d3aa9f1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/CMakeLists.txt @@ -0,0 +1,473 @@ +# Copyright (c) 2019 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(SPIRV_BUILD_FUZZER) + + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/protobufs) + + set(PROTOBUF_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/protobufs/spvtoolsfuzz.proto) + + set( + SPIRV_FUZZ_PROTOC_COMMAND + "protobuf::protoc" + CACHE + STRING + "The command to invoke the protobuf compiler (protoc). By default it is the protobufs::protoc CMake target. It should be overridden when cross-compiling, such as for Android.") + + add_custom_command( + OUTPUT protobufs/spvtoolsfuzz.pb.cc protobufs/spvtoolsfuzz.pb.h + COMMAND "${SPIRV_FUZZ_PROTOC_COMMAND}" + -I=${CMAKE_CURRENT_SOURCE_DIR}/protobufs + --cpp_out=protobufs + ${PROTOBUF_SOURCE} + DEPENDS ${PROTOBUF_SOURCE} + COMMENT "Generate protobuf sources from proto definition file." + ) + + set(SPIRV_TOOLS_FUZZ_SOURCES + added_function_reducer.h + call_graph.h + comparator_deep_blocks_first.h + counter_overflow_id_source.h + data_descriptor.h + equivalence_relation.h + fact_manager/constant_uniform_facts.h + fact_manager/data_synonym_and_id_equation_facts.h + fact_manager/dead_block_facts.h + fact_manager/fact_manager.h + fact_manager/irrelevant_value_facts.h + fact_manager/livesafe_function_facts.h + force_render_red.h + fuzzer.h + fuzzer_context.h + fuzzer_pass.h + fuzzer_pass_add_access_chains.h + fuzzer_pass_add_bit_instruction_synonyms.h + fuzzer_pass_add_composite_extract.h + fuzzer_pass_add_composite_inserts.h + fuzzer_pass_add_composite_types.h + fuzzer_pass_add_copy_memory.h + fuzzer_pass_add_dead_blocks.h + fuzzer_pass_add_dead_breaks.h + fuzzer_pass_add_dead_continues.h + fuzzer_pass_add_equation_instructions.h + fuzzer_pass_add_function_calls.h + fuzzer_pass_add_global_variables.h + fuzzer_pass_add_image_sample_unused_components.h + fuzzer_pass_add_loads.h + fuzzer_pass_add_local_variables.h + fuzzer_pass_add_loop_preheaders.h + fuzzer_pass_add_loops_to_create_int_constant_synonyms.h + fuzzer_pass_add_no_contraction_decorations.h + fuzzer_pass_add_opphi_synonyms.h + fuzzer_pass_add_parameters.h + fuzzer_pass_add_relaxed_decorations.h + fuzzer_pass_add_stores.h + fuzzer_pass_add_synonyms.h + fuzzer_pass_add_vector_shuffle_instructions.h + fuzzer_pass_adjust_branch_weights.h + fuzzer_pass_adjust_function_controls.h + fuzzer_pass_adjust_loop_controls.h + fuzzer_pass_adjust_memory_operands_masks.h + fuzzer_pass_adjust_selection_controls.h + fuzzer_pass_apply_id_synonyms.h + fuzzer_pass_construct_composites.h + fuzzer_pass_copy_objects.h + fuzzer_pass_donate_modules.h + fuzzer_pass_duplicate_regions_with_selections.h + fuzzer_pass_expand_vector_reductions.h + fuzzer_pass_flatten_conditional_branches.h + fuzzer_pass_inline_functions.h + fuzzer_pass_invert_comparison_operators.h + fuzzer_pass_interchange_signedness_of_integer_operands.h + fuzzer_pass_interchange_zero_like_constants.h + fuzzer_pass_make_vector_operations_dynamic.h + fuzzer_pass_merge_blocks.h + fuzzer_pass_merge_function_returns.h + fuzzer_pass_mutate_pointers.h + fuzzer_pass_obfuscate_constants.h + fuzzer_pass_outline_functions.h + fuzzer_pass_permute_blocks.h + fuzzer_pass_permute_function_parameters.h + fuzzer_pass_permute_instructions.h + fuzzer_pass_permute_phi_operands.h + fuzzer_pass_propagate_instructions_down.h + fuzzer_pass_propagate_instructions_up.h + fuzzer_pass_push_ids_through_variables.h + fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h + fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h + fuzzer_pass_replace_copy_memories_with_loads_stores.h + fuzzer_pass_replace_copy_objects_with_stores_loads.h + fuzzer_pass_replace_irrelevant_ids.h + fuzzer_pass_replace_linear_algebra_instructions.h + fuzzer_pass_replace_loads_stores_with_copy_memories.h + fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h + fuzzer_pass_replace_opselects_with_conditional_branches.h + fuzzer_pass_replace_parameter_with_global.h + fuzzer_pass_replace_params_with_struct.h + fuzzer_pass_split_blocks.h + fuzzer_pass_swap_commutable_operands.h + fuzzer_pass_swap_conditional_branch_operands.h + fuzzer_pass_toggle_access_chain_instruction.h + fuzzer_pass_wrap_regions_in_selections.h + fuzzer_util.h + id_use_descriptor.h + instruction_descriptor.h + instruction_message.h + overflow_id_source.h + pass_management/repeated_pass_instances.h + pass_management/repeated_pass_manager.h + pass_management/repeated_pass_manager_looped_with_recommendations.h + pass_management/repeated_pass_manager_random_with_recommendations.h + pass_management/repeated_pass_manager_simple.h + pass_management/repeated_pass_recommender.h + pass_management/repeated_pass_recommender_standard.h + protobufs/spirvfuzz_protobufs.h + pseudo_random_generator.h + random_generator.h + replayer.h + shrinker.h + transformation.h + transformation_access_chain.h + transformation_add_bit_instruction_synonym.h + transformation_add_constant_boolean.h + transformation_add_constant_composite.h + transformation_add_constant_null.h + transformation_add_constant_scalar.h + transformation_add_copy_memory.h + transformation_add_dead_block.h + transformation_add_dead_break.h + transformation_add_dead_continue.h + transformation_add_early_terminator_wrapper.h + transformation_add_function.h + transformation_add_global_undef.h + transformation_add_global_variable.h + transformation_add_image_sample_unused_components.h + transformation_add_local_variable.h + transformation_add_loop_preheader.h + transformation_add_loop_to_create_int_constant_synonym.h + transformation_add_no_contraction_decoration.h + transformation_add_opphi_synonym.h + transformation_add_parameter.h + transformation_add_relaxed_decoration.h + transformation_add_spec_constant_op.h + transformation_add_synonym.h + transformation_add_type_array.h + transformation_add_type_boolean.h + transformation_add_type_float.h + transformation_add_type_function.h + transformation_add_type_int.h + transformation_add_type_matrix.h + transformation_add_type_pointer.h + transformation_add_type_struct.h + transformation_add_type_vector.h + transformation_adjust_branch_weights.h + transformation_composite_construct.h + transformation_composite_extract.h + transformation_composite_insert.h + transformation_compute_data_synonym_fact_closure.h + transformation_context.h + transformation_duplicate_region_with_selection.h + transformation_equation_instruction.h + transformation_expand_vector_reduction.h + transformation_flatten_conditional_branch.h + transformation_function_call.h + transformation_inline_function.h + transformation_invert_comparison_operator.h + transformation_load.h + transformation_make_vector_operation_dynamic.h + transformation_merge_blocks.h + transformation_merge_function_returns.h + transformation_move_block_down.h + transformation_move_instruction_down.h + transformation_mutate_pointer.h + transformation_outline_function.h + transformation_permute_function_parameters.h + transformation_permute_phi_operands.h + transformation_propagate_instruction_down.h + transformation_propagate_instruction_up.h + transformation_push_id_through_variable.h + transformation_record_synonymous_constants.h + transformation_replace_add_sub_mul_with_carrying_extended.h + transformation_replace_boolean_constant_with_constant_binary.h + transformation_replace_branch_from_dead_block_with_exit.h + transformation_replace_constant_with_uniform.h + transformation_replace_copy_memory_with_load_store.h + transformation_replace_copy_object_with_store_load.h + transformation_replace_id_with_synonym.h + transformation_replace_irrelevant_id.h + transformation_replace_linear_algebra_instruction.h + transformation_replace_load_store_with_copy_memory.h + transformation_replace_opphi_id_from_dead_predecessor.h + transformation_replace_opselect_with_conditional_branch.h + transformation_replace_parameter_with_global.h + transformation_replace_params_with_struct.h + transformation_set_function_control.h + transformation_set_loop_control.h + transformation_set_memory_operands_mask.h + transformation_set_selection_control.h + transformation_split_block.h + transformation_store.h + transformation_swap_commutable_operands.h + transformation_swap_conditional_branch_operands.h + transformation_toggle_access_chain_instruction.h + transformation_vector_shuffle.h + transformation_wrap_early_terminator_in_function.h + transformation_wrap_region_in_selection.h + uniform_buffer_element_descriptor.h + ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.h + + added_function_reducer.cpp + call_graph.cpp + counter_overflow_id_source.cpp + data_descriptor.cpp + fact_manager/constant_uniform_facts.cpp + fact_manager/data_synonym_and_id_equation_facts.cpp + fact_manager/dead_block_facts.cpp + fact_manager/fact_manager.cpp + fact_manager/irrelevant_value_facts.cpp + fact_manager/livesafe_function_facts.cpp + force_render_red.cpp + fuzzer.cpp + fuzzer_context.cpp + fuzzer_pass.cpp + fuzzer_pass_add_access_chains.cpp + fuzzer_pass_add_bit_instruction_synonyms.cpp + fuzzer_pass_add_composite_extract.cpp + fuzzer_pass_add_composite_inserts.cpp + fuzzer_pass_add_composite_types.cpp + fuzzer_pass_add_copy_memory.cpp + fuzzer_pass_add_dead_blocks.cpp + fuzzer_pass_add_dead_breaks.cpp + fuzzer_pass_add_dead_continues.cpp + fuzzer_pass_add_equation_instructions.cpp + fuzzer_pass_add_function_calls.cpp + fuzzer_pass_add_global_variables.cpp + fuzzer_pass_add_image_sample_unused_components.cpp + fuzzer_pass_add_loads.cpp + fuzzer_pass_add_local_variables.cpp + fuzzer_pass_add_loop_preheaders.cpp + fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp + fuzzer_pass_add_no_contraction_decorations.cpp + fuzzer_pass_add_opphi_synonyms.cpp + fuzzer_pass_add_parameters.cpp + fuzzer_pass_add_relaxed_decorations.cpp + fuzzer_pass_add_stores.cpp + fuzzer_pass_add_synonyms.cpp + fuzzer_pass_add_vector_shuffle_instructions.cpp + fuzzer_pass_adjust_branch_weights.cpp + fuzzer_pass_adjust_function_controls.cpp + fuzzer_pass_adjust_loop_controls.cpp + fuzzer_pass_adjust_memory_operands_masks.cpp + fuzzer_pass_adjust_selection_controls.cpp + fuzzer_pass_apply_id_synonyms.cpp + fuzzer_pass_construct_composites.cpp + fuzzer_pass_copy_objects.cpp + fuzzer_pass_donate_modules.cpp + fuzzer_pass_duplicate_regions_with_selections.cpp + fuzzer_pass_expand_vector_reductions.cpp + fuzzer_pass_flatten_conditional_branches.cpp + fuzzer_pass_inline_functions.cpp + fuzzer_pass_invert_comparison_operators.cpp + fuzzer_pass_interchange_signedness_of_integer_operands.cpp + fuzzer_pass_interchange_zero_like_constants.cpp + fuzzer_pass_make_vector_operations_dynamic.cpp + fuzzer_pass_merge_blocks.cpp + fuzzer_pass_merge_function_returns.cpp + fuzzer_pass_mutate_pointers.cpp + fuzzer_pass_obfuscate_constants.cpp + fuzzer_pass_outline_functions.cpp + fuzzer_pass_permute_blocks.cpp + fuzzer_pass_permute_function_parameters.cpp + fuzzer_pass_permute_instructions.cpp + fuzzer_pass_permute_phi_operands.cpp + fuzzer_pass_propagate_instructions_down.cpp + fuzzer_pass_propagate_instructions_up.cpp + fuzzer_pass_push_ids_through_variables.cpp + fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp + fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp + fuzzer_pass_replace_copy_memories_with_loads_stores.cpp + fuzzer_pass_replace_copy_objects_with_stores_loads.cpp + fuzzer_pass_replace_irrelevant_ids.cpp + fuzzer_pass_replace_linear_algebra_instructions.cpp + fuzzer_pass_replace_loads_stores_with_copy_memories.cpp + fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp + fuzzer_pass_replace_opselects_with_conditional_branches.cpp + fuzzer_pass_replace_parameter_with_global.cpp + fuzzer_pass_replace_params_with_struct.cpp + fuzzer_pass_split_blocks.cpp + fuzzer_pass_swap_commutable_operands.cpp + fuzzer_pass_swap_conditional_branch_operands.cpp + fuzzer_pass_toggle_access_chain_instruction.cpp + fuzzer_pass_wrap_regions_in_selections.cpp + fuzzer_util.cpp + id_use_descriptor.cpp + instruction_descriptor.cpp + instruction_message.cpp + overflow_id_source.cpp + pass_management/repeated_pass_manager.cpp + pass_management/repeated_pass_manager_looped_with_recommendations.cpp + pass_management/repeated_pass_manager_random_with_recommendations.cpp + pass_management/repeated_pass_manager_simple.cpp + pass_management/repeated_pass_recommender.cpp + pass_management/repeated_pass_recommender_standard.cpp + pseudo_random_generator.cpp + random_generator.cpp + replayer.cpp + shrinker.cpp + transformation.cpp + transformation_access_chain.cpp + transformation_add_bit_instruction_synonym.cpp + transformation_add_constant_boolean.cpp + transformation_add_constant_composite.cpp + transformation_add_constant_null.cpp + transformation_add_constant_scalar.cpp + transformation_add_copy_memory.cpp + transformation_add_dead_block.cpp + transformation_add_dead_break.cpp + transformation_add_dead_continue.cpp + transformation_add_early_terminator_wrapper.cpp + transformation_add_function.cpp + transformation_add_global_undef.cpp + transformation_add_global_variable.cpp + transformation_add_image_sample_unused_components.cpp + transformation_add_local_variable.cpp + transformation_add_loop_preheader.cpp + transformation_add_loop_to_create_int_constant_synonym.cpp + transformation_add_no_contraction_decoration.cpp + transformation_add_opphi_synonym.cpp + transformation_add_parameter.cpp + transformation_add_relaxed_decoration.cpp + transformation_add_spec_constant_op.cpp + transformation_add_synonym.cpp + transformation_add_type_array.cpp + transformation_add_type_boolean.cpp + transformation_add_type_float.cpp + transformation_add_type_function.cpp + transformation_add_type_int.cpp + transformation_add_type_matrix.cpp + transformation_add_type_pointer.cpp + transformation_add_type_struct.cpp + transformation_add_type_vector.cpp + transformation_adjust_branch_weights.cpp + transformation_composite_construct.cpp + transformation_composite_extract.cpp + transformation_composite_insert.cpp + transformation_compute_data_synonym_fact_closure.cpp + transformation_context.cpp + transformation_duplicate_region_with_selection.cpp + transformation_equation_instruction.cpp + transformation_expand_vector_reduction.cpp + transformation_flatten_conditional_branch.cpp + transformation_function_call.cpp + transformation_inline_function.cpp + transformation_invert_comparison_operator.cpp + transformation_load.cpp + transformation_make_vector_operation_dynamic.cpp + transformation_merge_blocks.cpp + transformation_merge_function_returns.cpp + transformation_move_block_down.cpp + transformation_move_instruction_down.cpp + transformation_mutate_pointer.cpp + transformation_outline_function.cpp + transformation_permute_function_parameters.cpp + transformation_permute_phi_operands.cpp + transformation_propagate_instruction_down.cpp + transformation_propagate_instruction_up.cpp + transformation_push_id_through_variable.cpp + transformation_record_synonymous_constants.cpp + transformation_replace_add_sub_mul_with_carrying_extended.cpp + transformation_replace_boolean_constant_with_constant_binary.cpp + transformation_replace_branch_from_dead_block_with_exit.cpp + transformation_replace_constant_with_uniform.cpp + transformation_replace_copy_memory_with_load_store.cpp + transformation_replace_copy_object_with_store_load.cpp + transformation_replace_id_with_synonym.cpp + transformation_replace_irrelevant_id.cpp + transformation_replace_linear_algebra_instruction.cpp + transformation_replace_load_store_with_copy_memory.cpp + transformation_replace_opphi_id_from_dead_predecessor.cpp + transformation_replace_opselect_with_conditional_branch.cpp + transformation_replace_parameter_with_global.cpp + transformation_replace_params_with_struct.cpp + transformation_set_function_control.cpp + transformation_set_loop_control.cpp + transformation_set_memory_operands_mask.cpp + transformation_set_selection_control.cpp + transformation_split_block.cpp + transformation_store.cpp + transformation_swap_commutable_operands.cpp + transformation_swap_conditional_branch_operands.cpp + transformation_toggle_access_chain_instruction.cpp + transformation_vector_shuffle.cpp + transformation_wrap_early_terminator_in_function.cpp + transformation_wrap_region_in_selection.cpp + uniform_buffer_element_descriptor.cpp + ${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc + ) + + if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) + endif() + + spvtools_pch(SPIRV_TOOLS_FUZZ_SOURCES pch_source_fuzz) + + add_library(SPIRV-Tools-fuzz ${SPIRV_TOOLS_FUZZ_SOURCES}) + + spvtools_default_compile_options(SPIRV-Tools-fuzz) + + # Compilation of the auto-generated protobuf source file will yield warnings, + # which we have no control over and thus wish to ignore. + if(${COMPILER_IS_LIKE_GNU}) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS -w) + endif() + if(MSVC) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/protobufs/spvtoolsfuzz.pb.cc PROPERTIES COMPILE_FLAGS /w) + endif() + + target_include_directories(SPIRV-Tools-fuzz + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} + PRIVATE ${CMAKE_BINARY_DIR}) + + # The fuzzer reuses a lot of functionality from the SPIRV-Tools library. + target_link_libraries(SPIRV-Tools-fuzz + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY} + PUBLIC SPIRV-Tools-opt + PUBLIC SPIRV-Tools-reduce + PUBLIC protobuf::libprotobuf) + + set_property(TARGET SPIRV-Tools-fuzz PROPERTY FOLDER "SPIRV-Tools libraries") + spvtools_check_symbol_exports(SPIRV-Tools-fuzz) + + if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-fuzz EXPORT SPIRV-Tools-fuzzTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake) + + spvtools_config_package_dir(SPIRV-Tools-fuzz PACKAGE_DIR) + install(EXPORT SPIRV-Tools-fuzzTargets FILE SPIRV-Tools-fuzzTarget.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-fuzz) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-fuzzConfig.cmake DESTINATION ${PACKAGE_DIR}) + endif(ENABLE_SPIRV_TOOLS_INSTALL) + +endif(SPIRV_BUILD_FUZZER) diff --git a/third_party/spirv-tools/source/fuzz/added_function_reducer.cpp b/third_party/spirv-tools/source/fuzz/added_function_reducer.cpp new file mode 100644 index 0000000..e7cb027 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/added_function_reducer.cpp @@ -0,0 +1,302 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/added_function_reducer.h" + +#include "source/fuzz/instruction_message.h" +#include "source/fuzz/replayer.h" +#include "source/fuzz/transformation_add_function.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/reduce/reducer.h" + +namespace spvtools { +namespace fuzz { + +AddedFunctionReducer::AddedFunctionReducer( + spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + uint32_t index_of_add_function_transformation, + const Shrinker::InterestingnessFunction& shrinker_interestingness_function, + bool validate_during_replay, spv_validator_options validator_options, + uint32_t shrinker_step_limit, uint32_t num_existing_shrink_attempts) + : target_env_(target_env), + consumer_(std::move(consumer)), + binary_in_(binary_in), + initial_facts_(initial_facts), + transformation_sequence_in_(transformation_sequence_in), + index_of_add_function_transformation_( + index_of_add_function_transformation), + shrinker_interestingness_function_(shrinker_interestingness_function), + validate_during_replay_(validate_during_replay), + validator_options_(validator_options), + shrinker_step_limit_(shrinker_step_limit), + num_existing_shrink_attempts_(num_existing_shrink_attempts), + num_reducer_interestingness_function_invocations_(0) {} + +AddedFunctionReducer::~AddedFunctionReducer() = default; + +AddedFunctionReducer::AddedFunctionReducerResult AddedFunctionReducer::Run() { + // Replay all transformations before the AddFunction transformation, then + // add the raw function associated with the AddFunction transformation. + std::vector binary_to_reduce; + std::unordered_set irrelevant_pointee_global_variables; + ReplayPrefixAndAddFunction(&binary_to_reduce, + &irrelevant_pointee_global_variables); + + // Set up spirv-reduce to use our very specific interestingness function. + reduce::Reducer reducer(target_env_); + reducer.SetMessageConsumer(consumer_); + reducer.AddDefaultReductionPasses(); + reducer.SetInterestingnessFunction( + [this, &irrelevant_pointee_global_variables]( + const std::vector& binary_under_reduction, + uint32_t /*unused*/) { + return InterestingnessFunctionForReducingAddedFunction( + binary_under_reduction, irrelevant_pointee_global_variables); + }); + + // Instruct spirv-reduce to only target the function with the id associated + // with the AddFunction transformation that we care about. + spvtools::ReducerOptions reducer_options; + reducer_options.set_target_function(GetAddedFunctionId()); + // Bound the number of reduction steps that spirv-reduce can make according + // to the overall shrinker step limit and the number of shrink attempts that + // have already been tried. + assert(shrinker_step_limit_ > num_existing_shrink_attempts_ && + "The added function reducer should not have been invoked."); + reducer_options.set_step_limit(shrinker_step_limit_ - + num_existing_shrink_attempts_); + + // Run spirv-reduce. + std::vector reduced_binary; + auto reducer_result = + reducer.Run(std::move(binary_to_reduce), &reduced_binary, reducer_options, + validator_options_); + if (reducer_result != reduce::Reducer::kComplete && + reducer_result != reduce::Reducer::kReachedStepLimit) { + return {AddedFunctionReducerResultStatus::kReductionFailed, + std::vector(), protobufs::TransformationSequence(), 0}; + } + + // Provide the outer shrinker with an adapted sequence of transformations in + // which the AddFunction transformation of interest has been simplified to use + // the version of the added function that appears in |reduced_binary|. + std::vector binary_out; + protobufs::TransformationSequence transformation_sequence_out; + ReplayAdaptedTransformations(reduced_binary, &binary_out, + &transformation_sequence_out); + // We subtract 1 from |num_reducer_interestingness_function_invocations_| to + // account for the fact that spirv-reduce invokes its interestingness test + // once before reduction commences in order to check that the initial module + // is interesting. + assert(num_reducer_interestingness_function_invocations_ > 0 && + "At a minimum spirv-reduce should have invoked its interestingness " + "test once."); + return {AddedFunctionReducerResultStatus::kComplete, std::move(binary_out), + std::move(transformation_sequence_out), + num_reducer_interestingness_function_invocations_ - 1}; +} + +bool AddedFunctionReducer::InterestingnessFunctionForReducingAddedFunction( + const std::vector& binary_under_reduction, + const std::unordered_set& irrelevant_pointee_global_variables) { + uint32_t counter_for_shrinker_interestingness_function = + num_existing_shrink_attempts_ + + num_reducer_interestingness_function_invocations_; + num_reducer_interestingness_function_invocations_++; + + // The reduced version of the added function must be limited to accessing + // global variables appearing in |irrelevant_pointee_global_variables|. This + // is to guard against the possibility of spirv-reduce changing a reference + // to an irrelevant global to a reference to a regular global variable, which + // could cause the added function to change the semantics of the original + // module. + auto ir_context = + BuildModule(target_env_, consumer_, binary_under_reduction.data(), + binary_under_reduction.size()); + assert(ir_context != nullptr && "The binary should be parsable."); + for (auto& type_or_value : ir_context->module()->types_values()) { + if (type_or_value.opcode() != SpvOpVariable) { + continue; + } + if (irrelevant_pointee_global_variables.count(type_or_value.result_id())) { + continue; + } + if (!ir_context->get_def_use_mgr()->WhileEachUse( + &type_or_value, + [this, &ir_context](opt::Instruction* user, + uint32_t /*unused*/) -> bool { + auto block = ir_context->get_instr_block(user); + if (block != nullptr && + block->GetParent()->result_id() == GetAddedFunctionId()) { + return false; + } + return true; + })) { + return false; + } + } + + // For the binary to be deemed interesting, it must be possible to + // successfully apply all the transformations, with the transformation at + // index |index_of_add_function_transformation_| simplified to use the version + // of the added function from |binary_under_reduction|. + // + // This might not be the case: spirv-reduce might have removed a chunk of the + // added function on which future transformations depend. + // + // This is an optimization: the assumption is that having already shrunk the + // transformation sequence down to minimal form, all transformations have a + // role to play, and it's almost certainly a waste of time to invoke the + // shrinker's interestingness function if we have eliminated transformations + // that the shrinker previously tried to -- but could not -- eliminate. + std::vector binary_out; + protobufs::TransformationSequence modified_transformations; + ReplayAdaptedTransformations(binary_under_reduction, &binary_out, + &modified_transformations); + if (transformation_sequence_in_.transformation_size() != + modified_transformations.transformation_size()) { + return false; + } + + // The resulting binary must be deemed interesting according to the shrinker's + // interestingness function. + return shrinker_interestingness_function_( + binary_out, counter_for_shrinker_interestingness_function); +} + +void AddedFunctionReducer::ReplayPrefixAndAddFunction( + std::vector* binary_out, + std::unordered_set* irrelevant_pointee_global_variables) const { + assert(transformation_sequence_in_ + .transformation(index_of_add_function_transformation_) + .has_add_function() && + "A TransformationAddFunction is required at the given index."); + + auto replay_result = Replayer(target_env_, consumer_, binary_in_, + initial_facts_, transformation_sequence_in_, + index_of_add_function_transformation_, + validate_during_replay_, validator_options_) + .Run(); + assert(replay_result.status == Replayer::ReplayerResultStatus::kComplete && + "Replay should succeed"); + assert(static_cast( + replay_result.applied_transformations.transformation_size()) == + index_of_add_function_transformation_ && + "All requested transformations should have applied."); + + auto* ir_context = replay_result.transformed_module.get(); + + for (auto& type_or_value : ir_context->module()->types_values()) { + if (type_or_value.opcode() != SpvOpVariable) { + continue; + } + if (replay_result.transformation_context->GetFactManager() + ->PointeeValueIsIrrelevant(type_or_value.result_id())) { + irrelevant_pointee_global_variables->insert(type_or_value.result_id()); + } + } + + // Add the function associated with the transformation at + // |index_of_add_function_transformation| to the module. By construction this + // should succeed. + const protobufs::TransformationAddFunction& + transformation_add_function_message = + transformation_sequence_in_ + .transformation(index_of_add_function_transformation_) + .add_function(); + bool success = TransformationAddFunction(transformation_add_function_message) + .TryToAddFunction(ir_context); + (void)success; // Keep release mode compilers happy. + assert(success && "Addition of the function should have succeeded."); + + // Get the binary representation of the module with this function added. + ir_context->module()->ToBinary(binary_out, false); +} + +void AddedFunctionReducer::ReplayAdaptedTransformations( + const std::vector& binary_under_reduction, + std::vector* binary_out, + protobufs::TransformationSequence* transformation_sequence_out) const { + assert(index_of_add_function_transformation_ < + static_cast( + transformation_sequence_in_.transformation_size()) && + "The relevant add function transformation must be present."); + std::unique_ptr ir_context_under_reduction = + BuildModule(target_env_, consumer_, binary_under_reduction.data(), + binary_under_reduction.size()); + assert(ir_context_under_reduction && "Error building module."); + + protobufs::TransformationSequence modified_transformations; + for (uint32_t i = 0; + i < + static_cast(transformation_sequence_in_.transformation_size()); + i++) { + if (i == index_of_add_function_transformation_) { + protobufs::TransformationAddFunction modified_add_function = + transformation_sequence_in_ + .transformation(index_of_add_function_transformation_) + .add_function(); + assert(GetAddedFunctionId() == + modified_add_function.instruction(0).result_id() && + "Unexpected result id for added function."); + modified_add_function.clear_instruction(); + for (auto& function : *ir_context_under_reduction->module()) { + if (function.result_id() != GetAddedFunctionId()) { + continue; + } + function.ForEachInst( + [&modified_add_function](const opt::Instruction* instruction) { + *modified_add_function.add_instruction() = + MakeInstructionMessage(instruction); + }); + } + assert(modified_add_function.instruction_size() > 0 && + "Some instructions for the added function should remain."); + *modified_transformations.add_transformation()->mutable_add_function() = + modified_add_function; + } else { + *modified_transformations.add_transformation() = + transformation_sequence_in_.transformation(i); + } + } + assert( + transformation_sequence_in_.transformation_size() == + modified_transformations.transformation_size() && + "The original and modified transformations should have the same size."); + auto replay_result = Replayer(target_env_, consumer_, binary_in_, + initial_facts_, modified_transformations, + modified_transformations.transformation_size(), + validate_during_replay_, validator_options_) + .Run(); + assert(replay_result.status == Replayer::ReplayerResultStatus::kComplete && + "Replay should succeed."); + replay_result.transformed_module->module()->ToBinary(binary_out, false); + *transformation_sequence_out = + std::move(replay_result.applied_transformations); +} + +uint32_t AddedFunctionReducer::GetAddedFunctionId() const { + return transformation_sequence_in_ + .transformation(index_of_add_function_transformation_) + .add_function() + .instruction(0) + .result_id(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/added_function_reducer.h b/third_party/spirv-tools/source/fuzz/added_function_reducer.h new file mode 100644 index 0000000..3efd268 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/added_function_reducer.h @@ -0,0 +1,193 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ +#define SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/shrinker.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// An auxiliary class used by Shrinker, this class takes care of using +// spirv-reduce to reduce the body of a function encoded in an AddFunction +// transformation, in case a smaller, simpler function can be added instead. +class AddedFunctionReducer { + public: + // Possible statuses that can result from running the shrinker. + enum class AddedFunctionReducerResultStatus { + kComplete, + kReductionFailed, + }; + + struct AddedFunctionReducerResult { + AddedFunctionReducerResultStatus status; + std::vector transformed_binary; + protobufs::TransformationSequence applied_transformations; + uint32_t num_reduction_attempts; + }; + + AddedFunctionReducer( + spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + uint32_t index_of_add_function_transformation, + const Shrinker::InterestingnessFunction& + shrinker_interestingness_function, + bool validate_during_replay, spv_validator_options validator_options, + uint32_t shrinker_step_limit, uint32_t num_existing_shrink_attempts); + + // Disables copy/move constructor/assignment operations. + AddedFunctionReducer(const AddedFunctionReducer&) = delete; + AddedFunctionReducer(AddedFunctionReducer&&) = delete; + AddedFunctionReducer& operator=(const AddedFunctionReducer&) = delete; + AddedFunctionReducer& operator=(AddedFunctionReducer&&) = delete; + + ~AddedFunctionReducer(); + + // Invokes spirv-reduce on the function in the AddFunction transformation + // identified by |index_of_add_function_transformation|. Returns a sequence + // of transformations identical to |transformation_sequence_in|, except that + // the AddFunction transformation at |index_of_add_function_transformation| + // might have been simplified. The binary associated with applying the + // resulting sequence of transformations to |binary_in| is also returned, as + // well as the number of reduction steps that spirv-reduce made. + // + // On failure, an empty transformation sequence and binary are returned, + // with a placeholder value of 0 for the number of reduction attempts. + AddedFunctionReducerResult Run(); + + private: + // Yields, via |binary_out|, the binary obtained by applying transformations + // [0, |index_of_added_function_| - 1] from |transformations_in_| to + // |binary_in_|, and then adding the raw function encoded in + // |transformations_in_[index_of_added_function_]| (without adapting that + // function to make it livesafe). This function has |added_function_id_| as + // its result id. + // + // The ids associated with all global variables in |binary_out| that had the + // "irrelevant pointee value" fact are also returned via + // |irrelevant_pointee_global_variables|. + // + // The point of this function is that spirv-reduce can subsequently be applied + // to function |added_function_id_| in |binary_out|. By construction, + // |added_function_id_| should originally manipulate globals for which + // "irrelevant pointee value" facts hold. The set + // |irrelevant_pointee_global_variables| can be used to force spirv-reduce + // to preserve this, to avoid the reduced function ending up manipulating + // other global variables of the SPIR-V module, potentially changing their + // value and thus changing the semantics of the module. + void ReplayPrefixAndAddFunction( + std::vector* binary_out, + std::unordered_set* irrelevant_pointee_global_variables) const; + + // This is the interestingness function that will be used by spirv-reduce + // when shrinking the added function. + // + // For |binary_under_reduction| to be deemed interesting, the following + // conditions must hold: + // - The function with id |added_function_id_| in |binary_under_reduction| + // must only reference global variables in + // |irrelevant_pointee_global_variables|. This avoids the reduced function + // changing the semantics of the original SPIR-V module. + // - It must be possible to successfully replay the transformations in + // |transformation_sequence_in_|, adapted so that the function added by the + // transformation at |index_of_add_function_transformation_| is replaced by + // the function with id |added_function_id_| in |binary_under_reduction|, + // to |binary_in| (starting with initial facts |initial_facts_|). + // - All the transformations in this sequence must be successfully applied + // during replay. + // - The resulting binary must be interesting according to + // |shrinker_interestingness_function_|. + bool InterestingnessFunctionForReducingAddedFunction( + const std::vector& binary_under_reduction, + const std::unordered_set& irrelevant_pointee_global_variables); + + // Starting with |binary_in_| and |initial_facts_|, the transformations in + // |transformation_sequence_in_| are replayed. However, the transformation + // at index |index_of_add_function_transformation_| of + // |transformation_sequence_in_| -- which is guaranteed to be an AddFunction + // transformation -- is adapted so that the function to be added is replaced + // with the function in |binary_under_reduction| with id |added_function_id_|. + // + // The binary resulting from this replay is returned via |binary_out|, and the + // adapted transformation sequence via |transformation_sequence_out|. + void ReplayAdaptedTransformations( + const std::vector& binary_under_reduction, + std::vector* binary_out, + protobufs::TransformationSequence* transformation_sequence_out) const; + + // Returns the id of the function to be added by the AddFunction + // transformation at + // |transformation_sequence_in_[index_of_add_function_transformation_]|. + uint32_t GetAddedFunctionId() const; + + // Target environment. + const spv_target_env target_env_; + + // Message consumer. + MessageConsumer consumer_; + + // The initial binary to which transformations are applied -- i.e., the + // binary to which spirv-fuzz originally applied transformations. + const std::vector& binary_in_; + + // Initial facts about |binary_in_|. + const protobufs::FactSequence& initial_facts_; + + // A set of transformations that can be successfully applied to |binary_in_|. + const protobufs::TransformationSequence& transformation_sequence_in_; + + // An index into |transformation_sequence_in_| referring to an AddFunction + // transformation. This is the transformation to be simplified using + // spirv-reduce. + const uint32_t index_of_add_function_transformation_; + + // The interestingness function that has been provided to guide the + // overall shrinking process. The AddFunction transformation being simplified + // by this class should still -- when applied in conjunction with the other + // transformations in |transformation_sequence_in_| -- lead to a binary that + // is deemed interesting by this function. + const Shrinker::InterestingnessFunction& shrinker_interestingness_function_; + + // Determines whether to check for validity during the replaying of + // transformations. + const bool validate_during_replay_; + + // Options to control validation. + spv_validator_options validator_options_; + + // The step limit associated with the overall shrinking process. + const uint32_t shrinker_step_limit_; + + // The number of shrink attempts that had been applied prior to invoking this + // AddedFunctionReducer instance. + const uint32_t num_existing_shrink_attempts_; + + // Tracks the number of attempts that spirv-reduce has invoked its + // interestingness function, which it does once at the start of reduction, + // and then once more each time it makes a reduction step. + uint32_t num_reducer_interestingness_function_invocations_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_ADDED_FUNCTION_REDUCER_H_ diff --git a/third_party/spirv-tools/source/fuzz/call_graph.cpp b/third_party/spirv-tools/source/fuzz/call_graph.cpp new file mode 100644 index 0000000..c52bc34 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/call_graph.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/call_graph.h" + +#include + +namespace spvtools { +namespace fuzz { + +CallGraph::CallGraph(opt::IRContext* context) { + // Initialize function in-degree, call graph edges and corresponding maximum + // loop nesting depth to 0, empty and 0 respectively. + for (auto& function : *context->module()) { + function_in_degree_[function.result_id()] = 0; + call_graph_edges_[function.result_id()] = std::set(); + function_max_loop_nesting_depth_[function.result_id()] = 0; + } + + // Record the maximum loop nesting depth for each edge, by keeping a map from + // pairs of function ids, where (A, B) represents a function call from A to B, + // to the corresponding maximum depth. + std::map, uint32_t> call_to_max_depth; + + // Compute |function_in_degree_|, |call_graph_edges_| and |call_to_max_depth|. + BuildGraphAndGetDepthOfFunctionCalls(context, &call_to_max_depth); + + // Compute |functions_in_topological_order_|. + ComputeTopologicalOrderOfFunctions(); + + // Compute |function_max_loop_nesting_depth_|. + ComputeInterproceduralFunctionCallDepths(call_to_max_depth); +} + +void CallGraph::BuildGraphAndGetDepthOfFunctionCalls( + opt::IRContext* context, + std::map, uint32_t>* call_to_max_depth) { + // Consider every function. + for (auto& function : *context->module()) { + // Avoid considering the same callee of this function multiple times by + // recording known callees. + std::set known_callees; + // Consider every function call instruction in every block. + for (auto& block : function) { + for (auto& instruction : block) { + if (instruction.opcode() != SpvOpFunctionCall) { + continue; + } + // Get the id of the function being called. + uint32_t callee = instruction.GetSingleWordInOperand(0); + + // Get the loop nesting depth of this function call. + uint32_t loop_nesting_depth = + context->GetStructuredCFGAnalysis()->LoopNestingDepth(block.id()); + // If inside a loop header, consider the function call nested inside the + // loop headed by the block. + if (block.IsLoopHeader()) { + loop_nesting_depth++; + } + + // Update the map if we have not seen this pair (caller, callee) + // before or if this function call is from a greater depth. + if (!known_callees.count(callee) || + call_to_max_depth->at({function.result_id(), callee}) < + loop_nesting_depth) { + call_to_max_depth->insert( + {{function.result_id(), callee}, loop_nesting_depth}); + } + + if (known_callees.count(callee)) { + // We have already considered a call to this function - ignore it. + continue; + } + // Increase the callee's in-degree and add an edge to the call graph. + function_in_degree_[callee]++; + call_graph_edges_[function.result_id()].insert(callee); + // Mark the callee as 'known'. + known_callees.insert(callee); + } + } + } +} + +void CallGraph::ComputeTopologicalOrderOfFunctions() { + // This is an implementation of Kahn’s algorithm for topological sorting. + + // Initialise |functions_in_topological_order_|. + functions_in_topological_order_.clear(); + + // Get a copy of the initial in-degrees of all functions. The algorithm + // involves decrementing these values, hence why we work on a copy. + std::map function_in_degree = GetFunctionInDegree(); + + // Populate a queue with all those function ids with in-degree zero. + std::queue queue; + for (auto& entry : function_in_degree) { + if (entry.second == 0) { + queue.push(entry.first); + } + } + + // Pop ids from the queue, adding them to the sorted order and decreasing the + // in-degrees of their successors. A successor who's in-degree becomes zero + // gets added to the queue. + while (!queue.empty()) { + auto next = queue.front(); + queue.pop(); + functions_in_topological_order_.push_back(next); + for (auto successor : GetDirectCallees(next)) { + assert(function_in_degree.at(successor) > 0 && + "The in-degree cannot be zero if the function is a successor."); + function_in_degree[successor] = function_in_degree.at(successor) - 1; + if (function_in_degree.at(successor) == 0) { + queue.push(successor); + } + } + } + + assert(functions_in_topological_order_.size() == function_in_degree.size() && + "Every function should appear in the sort."); + + return; +} + +void CallGraph::ComputeInterproceduralFunctionCallDepths( + const std::map, uint32_t>& + call_to_max_depth) { + // Find the maximum loop nesting depth that each function can be + // called from, by considering them in topological order. + for (uint32_t function_id : functions_in_topological_order_) { + const auto& callees = call_graph_edges_[function_id]; + + // For each callee, update its maximum loop nesting depth, if a call from + // |function_id| increases it. + for (uint32_t callee : callees) { + uint32_t max_depth_from_this_function = + function_max_loop_nesting_depth_[function_id] + + call_to_max_depth.at({function_id, callee}); + if (function_max_loop_nesting_depth_[callee] < + max_depth_from_this_function) { + function_max_loop_nesting_depth_[callee] = max_depth_from_this_function; + } + } + } +} + +void CallGraph::PushDirectCallees(uint32_t function_id, + std::queue* queue) const { + for (auto callee : GetDirectCallees(function_id)) { + queue->push(callee); + } +} + +std::set CallGraph::GetIndirectCallees(uint32_t function_id) const { + std::set result; + std::queue queue; + PushDirectCallees(function_id, &queue); + + while (!queue.empty()) { + auto next = queue.front(); + queue.pop(); + if (result.count(next)) { + continue; + } + result.insert(next); + PushDirectCallees(next, &queue); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/call_graph.h b/third_party/spirv-tools/source/fuzz/call_graph.h new file mode 100644 index 0000000..840b1f1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/call_graph.h @@ -0,0 +1,110 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_CALL_GRAPH_H_ +#define SOURCE_FUZZ_CALL_GRAPH_H_ + +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Represents the acyclic call graph of a SPIR-V module. +// The module is assumed to be recursion-free, so there are no cycles in the +// graph. This class is immutable, so it will need to be recomputed if the +// module changes. +class CallGraph { + public: + // Creates a call graph corresponding to the given SPIR-V module. + explicit CallGraph(opt::IRContext* context); + + // Returns a mapping from each function to its number of distinct callers. + const std::map& GetFunctionInDegree() const { + return function_in_degree_; + } + + // Returns the ids of the functions that |function_id| directly invokes. + const std::set& GetDirectCallees(uint32_t function_id) const { + return call_graph_edges_.at(function_id); + } + + // Returns the ids of the functions that |function_id| directly or indirectly + // invokes. + std::set GetIndirectCallees(uint32_t function_id) const; + + // Returns the ids of all the functions in the graph in a topological order, + // in relation to the function calls, which are assumed to be recursion-free. + const std::vector& GetFunctionsInTopologicalOrder() const { + return functions_in_topological_order_; + } + + // Returns the maximum loop nesting depth from which |function_id| can be + // called. This is computed inter-procedurally (i.e. if main calls A from + // depth 2 and A calls B from depth 1, the result will be 3 for A). + // This is a static analysis, so it's not necessarily true that the depth + // returned can actually be reached at runtime. + uint32_t GetMaxCallNestingDepth(uint32_t function_id) const { + return function_max_loop_nesting_depth_.at(function_id); + } + + private: + // Computes |call_graph_edges_| and |function_in_degree_|. For each pair (A, + // B) of functions such that there is at least a function call from A to B, + // adds, to |call_to_max_depth|, a mapping from (A, B) to the maximum loop + // nesting depth (within A) of any such function call. + void BuildGraphAndGetDepthOfFunctionCalls( + opt::IRContext* context, + std::map, uint32_t>* call_to_max_depth); + + // Computes a topological order of the functions in the graph, writing the + // result to |functions_in_topological_order_|. Assumes that the function + // calls are recursion-free and that |function_in_degree_| has been computed. + void ComputeTopologicalOrderOfFunctions(); + + // Computes |function_max_loop_nesting_depth_| so that each function is mapped + // to the maximum loop nesting depth from which it can be called, as described + // by the comment to GetMaxCallNestingDepth. Assumes that |call_graph_edges_| + // and |functions_in_topological_order_| have been computed, and that + // |call_to_max_depth| contains a mapping for each edge in the graph. + void ComputeInterproceduralFunctionCallDepths( + const std::map, uint32_t>& + call_to_max_depth); + + // Pushes the direct callees of |function_id| on to |queue|. + void PushDirectCallees(uint32_t function_id, + std::queue* queue) const; + + // Maps each function id to the ids of its immediate callees. + std::map> call_graph_edges_; + + // For each function id, stores the number of distinct functions that call + // the function. + std::map function_in_degree_; + + // Stores the ids of the functions in a topological order, + // in relation to the function calls, which are assumed to be recursion-free. + std::vector functions_in_topological_order_; + + // For each function id, stores the maximum loop nesting depth that the + // function can be called from. + std::map function_max_loop_nesting_depth_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_CALL_GRAPH_H_ diff --git a/third_party/spirv-tools/source/fuzz/comparator_deep_blocks_first.h b/third_party/spirv-tools/source/fuzz/comparator_deep_blocks_first.h new file mode 100644 index 0000000..be63d1a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/comparator_deep_blocks_first.h @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_COMPARATOR_BLOCKS_DEEP_FIRST_H_ +#define SOURCE_FUZZ_COMPARATOR_BLOCKS_DEEP_FIRST_H_ + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Comparator for blocks, comparing them based on how deep they are nested +// inside selection or loop constructs. Deeper blocks are considered less than +// ones that are not as deep. The blocks are required to be in the same +// function. +class ComparatorDeepBlocksFirst { + public: + explicit ComparatorDeepBlocksFirst(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + + bool operator()(uint32_t bb1, uint32_t bb2) const { + return this->operator()(fuzzerutil::MaybeFindBlock(ir_context_, bb1), + fuzzerutil::MaybeFindBlock(ir_context_, bb2)); + } + + bool operator()(const opt::BasicBlock* bb1, opt::BasicBlock* bb2) const { + assert(bb1 && bb2 && "The blocks must exist."); + assert(bb1->GetParent() == bb2->GetParent() && + "The blocks must be in the same functions."); + return ir_context_->GetStructuredCFGAnalysis()->NestingDepth(bb1->id()) > + ir_context_->GetStructuredCFGAnalysis()->NestingDepth(bb2->id()); + } + + private: + opt::IRContext* ir_context_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_COMPARATOR_BLOCKS_DEEP_FIRST_H_ diff --git a/third_party/spirv-tools/source/fuzz/counter_overflow_id_source.cpp b/third_party/spirv-tools/source/fuzz/counter_overflow_id_source.cpp new file mode 100644 index 0000000..0c21734 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/counter_overflow_id_source.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/counter_overflow_id_source.h" + +namespace spvtools { +namespace fuzz { + +CounterOverflowIdSource::CounterOverflowIdSource(uint32_t first_available_id) + : next_available_id_(first_available_id), issued_ids_() {} + +bool CounterOverflowIdSource::HasOverflowIds() const { return true; } + +uint32_t CounterOverflowIdSource::GetNextOverflowId() { + issued_ids_.insert(next_available_id_); + return next_available_id_++; +} + +const std::unordered_set& +CounterOverflowIdSource::GetIssuedOverflowIds() const { + return issued_ids_; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/counter_overflow_id_source.h b/third_party/spirv-tools/source/fuzz/counter_overflow_id_source.h new file mode 100644 index 0000000..852bbd0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/counter_overflow_id_source.h @@ -0,0 +1,49 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_COUNTER_OVERFLOW_ID_SOURCE_H_ +#define SOURCE_FUZZ_COUNTER_OVERFLOW_ID_SOURCE_H_ + +#include "source/fuzz/overflow_id_source.h" + +namespace spvtools { +namespace fuzz { + +// A source of overflow ids that uses a counter to provide successive ids from +// a given starting value. +class CounterOverflowIdSource : public OverflowIdSource { + public: + // |first_available_id| is the starting value for the counter. + explicit CounterOverflowIdSource(uint32_t first_available_id); + + // Always returns true. + bool HasOverflowIds() const override; + + // Returns the current counter value and increments the counter. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) We should + // account for the case where the maximum allowed id is reached. + uint32_t GetNextOverflowId() override; + + const std::unordered_set& GetIssuedOverflowIds() const override; + + private: + uint32_t next_available_id_; + + std::unordered_set issued_ids_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_OVERFLOW_ID_SOURCE_COUNTER_H_ diff --git a/third_party/spirv-tools/source/fuzz/data_descriptor.cpp b/third_party/spirv-tools/source/fuzz/data_descriptor.cpp new file mode 100644 index 0000000..c467363 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/data_descriptor.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/data_descriptor.h" + +#include + +namespace spvtools { +namespace fuzz { + +protobufs::DataDescriptor MakeDataDescriptor( + uint32_t object, const std::vector& indices) { + protobufs::DataDescriptor result; + result.set_object(object); + for (auto index : indices) { + result.add_index(index); + } + return result; +} + +size_t DataDescriptorHash::operator()( + const protobufs::DataDescriptor* data_descriptor) const { + std::u32string hash; + hash.push_back(data_descriptor->object()); + for (auto an_index : data_descriptor->index()) { + hash.push_back(an_index); + } + return std::hash()(hash); +} + +bool DataDescriptorEquals::operator()( + const protobufs::DataDescriptor* first, + const protobufs::DataDescriptor* second) const { + return first->object() == second->object() && + first->index().size() == second->index().size() && + std::equal(first->index().begin(), first->index().end(), + second->index().begin()); +} + +std::ostream& operator<<(std::ostream& out, + const protobufs::DataDescriptor& data_descriptor) { + out << data_descriptor.object(); + out << "["; + bool first = true; + for (auto index : data_descriptor.index()) { + if (first) { + first = false; + } else { + out << ", "; + } + out << index; + } + out << "]"; + return out; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/data_descriptor.h b/third_party/spirv-tools/source/fuzz/data_descriptor.h new file mode 100644 index 0000000..f4b8e9c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/data_descriptor.h @@ -0,0 +1,48 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_DATA_DESCRIPTOR_H_ +#define SOURCE_FUZZ_DATA_DESCRIPTOR_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" + +namespace spvtools { +namespace fuzz { + +// Factory method to create a data descriptor message from an object id and a +// list of indices. +protobufs::DataDescriptor MakeDataDescriptor( + uint32_t object, const std::vector& indices); + +// Hash function for data descriptors. +struct DataDescriptorHash { + size_t operator()(const protobufs::DataDescriptor* data_descriptor) const; +}; + +// Equality function for data descriptors. +struct DataDescriptorEquals { + bool operator()(const protobufs::DataDescriptor* first, + const protobufs::DataDescriptor* second) const; +}; + +std::ostream& operator<<(std::ostream& out, + const protobufs::DataDescriptor& data_descriptor); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_DATA_DESCRIPTOR_H_ diff --git a/third_party/spirv-tools/source/fuzz/equivalence_relation.h b/third_party/spirv-tools/source/fuzz/equivalence_relation.h new file mode 100644 index 0000000..a01eac0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/equivalence_relation.h @@ -0,0 +1,247 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_EQUIVALENCE_RELATION_H_ +#define SOURCE_FUZZ_EQUIVALENCE_RELATION_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +// A class for representing an equivalence relation on objects of type |T|, +// which should be a value type. The type |T| is required to have a copy +// constructor, and |PointerHashT| and |PointerEqualsT| must be functors +// providing hashing and equality testing functionality for pointers to objects +// of type |T|. +// +// A disjoint-set (a.k.a. union-find or merge-find) data structure is used to +// represent the equivalence relation. Path compression is used. Union by +// rank/size is not used. +// +// Each disjoint set is represented as a tree, rooted at the representative +// of the set. +// +// Getting the representative of a value simply requires chasing parent pointers +// from the value until you reach the root. +// +// Checking equivalence of two elements requires checking that the +// representatives are equal. +// +// Traversing the tree rooted at a value's representative visits the value's +// equivalence class. +// +// |PointerHashT| and |PointerEqualsT| are used to define *equality* between +// values, and otherwise are *not* used to define the equivalence relation +// (except that equal values are equivalent). The equivalence relation is +// constructed by repeatedly adding pairs of (typically non-equal) values that +// are deemed to be equivalent. +// +// For example in an equivalence relation on integers, 1 and 5 might be added +// as equivalent, so that IsEquivalent(1, 5) holds, because they represent +// IDs in a SPIR-V binary that are known to contain the same value at run time, +// but clearly 1 != 5. Since 1 and 1 are equal, IsEquivalent(1, 1) will also +// hold. +// +// Each unique (up to equality) value added to the relation is copied into +// |owned_values_|, so there is one canonical memory address per unique value. +// Uniqueness is ensured by storing (and checking) a set of pointers to these +// values in |value_set_|, which uses |PointerHashT| and |PointerEqualsT|. +// +// |parent_| and |children_| encode the equivalence relation, i.e., the trees. +template +class EquivalenceRelation { + public: + // Requires that |value1| and |value2| are already registered in the + // equivalence relation. Merges the equivalence classes associated with + // |value1| and |value2|. + void MakeEquivalent(const T& value1, const T& value2) { + assert(Exists(value1) && + "Precondition: value1 must already be registered."); + assert(Exists(value2) && + "Precondition: value2 must already be registered."); + + // Look up canonical pointers to each of the values in the value pool. + const T* value1_ptr = *value_set_.find(&value1); + const T* value2_ptr = *value_set_.find(&value2); + + // If the values turn out to be identical, they are already in the same + // equivalence class so there is nothing to do. + if (value1_ptr == value2_ptr) { + return; + } + + // Find the representative for each value's equivalence class, and if they + // are not already in the same class, make one the parent of the other. + const T* representative1 = Find(value1_ptr); + const T* representative2 = Find(value2_ptr); + assert(representative1 && "Representatives should never be null."); + assert(representative2 && "Representatives should never be null."); + if (representative1 != representative2) { + parent_[representative1] = representative2; + children_[representative2].push_back(representative1); + } + } + + // Requires that |value| is not known to the equivalence relation. Registers + // it in its own equivalence class and returns a pointer to the equivalence + // class representative. + const T* Register(const T& value) { + assert(!Exists(value)); + + // This relies on T having a copy constructor. + auto unique_pointer_to_value = MakeUnique(value); + auto pointer_to_value = unique_pointer_to_value.get(); + owned_values_.push_back(std::move(unique_pointer_to_value)); + value_set_.insert(pointer_to_value); + + // Initially say that the value is its own parent and that it has no + // children. + assert(pointer_to_value && "Representatives should never be null."); + parent_[pointer_to_value] = pointer_to_value; + children_[pointer_to_value] = std::vector(); + + return pointer_to_value; + } + + // Returns exactly one representative per equivalence class. + std::vector GetEquivalenceClassRepresentatives() const { + std::vector result; + for (auto& value : owned_values_) { + if (parent_[value.get()] == value.get()) { + result.push_back(value.get()); + } + } + return result; + } + + // Returns pointers to all values in the equivalence class of |value|, which + // must already be part of the equivalence relation. + std::vector GetEquivalenceClass(const T& value) const { + assert(Exists(value)); + + std::vector result; + + // Traverse the tree of values rooted at the representative of the + // equivalence class to which |value| belongs, and collect up all the values + // that are encountered. This constitutes the whole equivalence class. + std::vector stack; + stack.push_back(Find(*value_set_.find(&value))); + while (!stack.empty()) { + const T* item = stack.back(); + result.push_back(item); + stack.pop_back(); + for (auto child : children_[item]) { + stack.push_back(child); + } + } + return result; + } + + // Returns true if and only if |value1| and |value2| are in the same + // equivalence class. Both values must already be known to the equivalence + // relation. + bool IsEquivalent(const T& value1, const T& value2) const { + return Find(&value1) == Find(&value2); + } + + // Returns all values known to be part of the equivalence relation. + std::vector GetAllKnownValues() const { + std::vector result; + for (auto& value : owned_values_) { + result.push_back(value.get()); + } + return result; + } + + // Returns true if and only if |value| is known to be part of the equivalence + // relation. + bool Exists(const T& value) const { + return value_set_.find(&value) != value_set_.end(); + } + + // Returns the representative of the equivalence class of |value|, which must + // already be known to the equivalence relation. This is the 'Find' operation + // in a classic union-find data structure. + const T* Find(const T* value) const { + assert(Exists(*value)); + + // Get the canonical pointer to the value from the value pool. + const T* known_value = *value_set_.find(value); + assert(parent_[known_value] && "Every known value should have a parent."); + + // Compute the result by chasing parents until we find a value that is its + // own parent. + const T* result = known_value; + while (parent_[result] != result) { + result = parent_[result]; + } + assert(result && "Representatives should never be null."); + + // At this point, |result| is the representative of the equivalence class. + // Now perform the 'path compression' optimization by doing another pass up + // the parent chain, setting the parent of each node to be the + // representative, and rewriting children correspondingly. + const T* current = known_value; + while (parent_[current] != result) { + const T* next = parent_[current]; + parent_[current] = result; + children_[result].push_back(current); + auto child_iterator = + std::find(children_[next].begin(), children_[next].end(), current); + assert(child_iterator != children_[next].end() && + "'next' is the parent of 'current', so 'current' should be a " + "child of 'next'"); + children_[next].erase(child_iterator); + current = next; + } + return result; + } + + private: + // Maps every value to a parent. The representative of an equivalence class + // is its own parent. A value's representative can be found by walking its + // chain of ancestors. + // + // Mutable because the intuitively const method, 'Find', performs path + // compression. + mutable std::unordered_map parent_; + + // Stores the children of each value. This allows the equivalence class of + // a value to be calculated by traversing all descendents of the class's + // representative. + // + // Mutable because the intuitively const method, 'Find', performs path + // compression. + mutable std::unordered_map> children_; + + // The values known to the equivalence relation are allocated in + // |owned_values_|, and |value_pool_| provides (via |PointerHashT| and + // |PointerEqualsT|) a means for mapping a value of interest to a pointer + // into an equivalent value in |owned_values_|. + std::unordered_set value_set_; + std::vector> owned_values_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_EQUIVALENCE_RELATION_H_ diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.cpp b/third_party/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.cpp new file mode 100644 index 0000000..a629c0d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.cpp @@ -0,0 +1,235 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/constant_uniform_facts.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +ConstantUniformFacts::ConstantUniformFacts(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +uint32_t ConstantUniformFacts::GetConstantId( + const protobufs::FactConstantUniform& constant_uniform_fact, + uint32_t type_id) const { + auto type = ir_context_->get_type_mgr()->GetType(type_id); + assert(type != nullptr && "Unknown type id."); + const opt::analysis::Constant* known_constant; + if (type->AsInteger()) { + opt::analysis::IntConstant candidate_constant( + type->AsInteger(), GetConstantWords(constant_uniform_fact)); + known_constant = + ir_context_->get_constant_mgr()->FindConstant(&candidate_constant); + } else { + assert( + type->AsFloat() && + "Uniform constant facts are only supported for int and float types."); + opt::analysis::FloatConstant candidate_constant( + type->AsFloat(), GetConstantWords(constant_uniform_fact)); + known_constant = + ir_context_->get_constant_mgr()->FindConstant(&candidate_constant); + } + if (!known_constant) { + return 0; + } + return ir_context_->get_constant_mgr()->FindDeclaredConstant(known_constant, + type_id); +} + +std::vector ConstantUniformFacts::GetConstantWords( + const protobufs::FactConstantUniform& constant_uniform_fact) { + std::vector result; + for (auto constant_word : constant_uniform_fact.constant_word()) { + result.push_back(constant_word); + } + return result; +} + +bool ConstantUniformFacts::DataMatches( + const opt::Instruction& constant_instruction, + const protobufs::FactConstantUniform& constant_uniform_fact) { + assert(constant_instruction.opcode() == SpvOpConstant); + std::vector data_in_constant; + for (uint32_t i = 0; i < constant_instruction.NumInOperands(); i++) { + data_in_constant.push_back(constant_instruction.GetSingleWordInOperand(i)); + } + return data_in_constant == GetConstantWords(constant_uniform_fact); +} + +std::vector +ConstantUniformFacts::GetConstantsAvailableFromUniformsForType( + uint32_t type_id) const { + std::vector result; + std::set already_seen; + for (auto& fact_and_type_id : facts_and_type_ids_) { + if (fact_and_type_id.second != type_id) { + continue; + } + if (auto constant_id = GetConstantId(fact_and_type_id.first, type_id)) { + if (already_seen.find(constant_id) == already_seen.end()) { + result.push_back(constant_id); + already_seen.insert(constant_id); + } + } + } + return result; +} + +std::vector +ConstantUniformFacts::GetUniformDescriptorsForConstant( + uint32_t constant_id) const { + std::vector result; + auto constant_inst = ir_context_->get_def_use_mgr()->GetDef(constant_id); + assert(constant_inst->opcode() == SpvOpConstant && + "The given id must be that of a constant"); + auto type_id = constant_inst->type_id(); + for (auto& fact_and_type_id : facts_and_type_ids_) { + if (fact_and_type_id.second != type_id) { + continue; + } + if (DataMatches(*constant_inst, fact_and_type_id.first)) { + result.emplace_back( + fact_and_type_id.first.uniform_buffer_element_descriptor()); + } + } + return result; +} + +uint32_t ConstantUniformFacts::GetConstantFromUniformDescriptor( + const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const { + // Consider each fact. + for (auto& fact_and_type : facts_and_type_ids_) { + // Check whether the uniform descriptor associated with the fact matches + // |uniform_descriptor|. + if (UniformBufferElementDescriptorEquals()( + &uniform_descriptor, + &fact_and_type.first.uniform_buffer_element_descriptor())) { + return GetConstantId(fact_and_type.first, fact_and_type.second); + } + } + // No fact associated with the given uniform descriptor was found. + return 0; +} + +std::vector +ConstantUniformFacts::GetTypesForWhichUniformValuesAreKnown() const { + std::vector result; + for (auto& fact_and_type : facts_and_type_ids_) { + if (std::find(result.begin(), result.end(), fact_and_type.second) == + result.end()) { + result.push_back(fact_and_type.second); + } + } + return result; +} + +bool ConstantUniformFacts::FloatingPointValueIsSuitable( + const protobufs::FactConstantUniform& fact, uint32_t width) { + const uint32_t kFloatWidth = 32; + const uint32_t kDoubleWidth = 64; + if (width != kFloatWidth && width != kDoubleWidth) { + // Only 32- and 64-bit floating-point types are handled. + return false; + } + std::vector words = GetConstantWords(fact); + if (width == 32) { + float value; + memcpy(&value, words.data(), sizeof(float)); + if (!std::isfinite(value)) { + return false; + } + } else { + double value; + memcpy(&value, words.data(), sizeof(double)); + if (!std::isfinite(value)) { + return false; + } + } + return true; +} + +bool ConstantUniformFacts::MaybeAddFact( + const protobufs::FactConstantUniform& fact) { + // Try to find a unique instruction that declares a variable such that the + // variable is decorated with the descriptor set and binding associated with + // the constant uniform fact. + opt::Instruction* uniform_variable = FindUniformVariable( + fact.uniform_buffer_element_descriptor(), ir_context_, true); + + if (!uniform_variable) { + return false; + } + + assert(SpvOpVariable == uniform_variable->opcode()); + assert(SpvStorageClassUniform == uniform_variable->GetSingleWordInOperand(0)); + + auto should_be_uniform_pointer_type = + ir_context_->get_type_mgr()->GetType(uniform_variable->type_id()); + if (!should_be_uniform_pointer_type->AsPointer()) { + return false; + } + if (should_be_uniform_pointer_type->AsPointer()->storage_class() != + SpvStorageClassUniform) { + return false; + } + auto should_be_uniform_pointer_instruction = + ir_context_->get_def_use_mgr()->GetDef(uniform_variable->type_id()); + auto composite_type = + should_be_uniform_pointer_instruction->GetSingleWordInOperand(1); + + auto final_element_type_id = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, composite_type, + fact.uniform_buffer_element_descriptor().index()); + if (!final_element_type_id) { + return false; + } + auto final_element_type = + ir_context_->get_type_mgr()->GetType(final_element_type_id); + assert(final_element_type && + "There should be a type corresponding to this id."); + + if (!(final_element_type->AsFloat() || final_element_type->AsInteger())) { + return false; + } + auto width = final_element_type->AsFloat() + ? final_element_type->AsFloat()->width() + : final_element_type->AsInteger()->width(); + + if (final_element_type->AsFloat() && + !FloatingPointValueIsSuitable(fact, width)) { + return false; + } + + auto required_words = (width + 32 - 1) / 32; + if (static_cast(fact.constant_word().size()) != required_words) { + return false; + } + facts_and_type_ids_.emplace_back( + std::pair( + fact, final_element_type_id)); + return true; +} + +const std::vector>& +ConstantUniformFacts::GetConstantUniformFactsAndTypes() const { + return facts_and_type_ids_; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.h b/third_party/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.h new file mode 100644 index 0000000..41d253e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/constant_uniform_facts.h @@ -0,0 +1,90 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// The purpose of this class is to group the fields and data used to represent +// facts about uniform constants. +class ConstantUniformFacts { + public: + explicit ConstantUniformFacts(opt::IRContext* ir_context); + + // See method in FactManager which delegates to this method. + bool MaybeAddFact(const protobufs::FactConstantUniform& fact); + + // See method in FactManager which delegates to this method. + std::vector GetConstantsAvailableFromUniformsForType( + uint32_t type_id) const; + + // See method in FactManager which delegates to this method. + std::vector + GetUniformDescriptorsForConstant(uint32_t constant_id) const; + + // See method in FactManager which delegates to this method. + uint32_t GetConstantFromUniformDescriptor( + const protobufs::UniformBufferElementDescriptor& uniform_descriptor) + const; + + // See method in FactManager which delegates to this method. + std::vector GetTypesForWhichUniformValuesAreKnown() const; + + // See method in FactManager which delegates to this method. + const std::vector>& + GetConstantUniformFactsAndTypes() const; + + private: + // Returns true if and only if the words associated with + // |constant_instruction| exactly match the words for the constant associated + // with |constant_uniform_fact|. + static bool DataMatches( + const opt::Instruction& constant_instruction, + const protobufs::FactConstantUniform& constant_uniform_fact); + + // Yields the constant words associated with |constant_uniform_fact|. + static std::vector GetConstantWords( + const protobufs::FactConstantUniform& constant_uniform_fact); + + // Yields the id of a constant of type |type_id| whose data matches the + // constant data in |constant_uniform_fact|, or 0 if no such constant is + // declared. + uint32_t GetConstantId( + const protobufs::FactConstantUniform& constant_uniform_fact, + uint32_t type_id) const; + + // Checks that the width of a floating-point constant is supported, and that + // the constant is finite. + static bool FloatingPointValueIsSuitable( + const protobufs::FactConstantUniform& fact, uint32_t width); + + std::vector> + facts_and_type_ids_; + + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_CONSTANT_UNIFORM_FACTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp b/third_party/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp new file mode 100644 index 0000000..a2c1f2c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.cpp @@ -0,0 +1,932 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +size_t DataSynonymAndIdEquationFacts::OperationHash::operator()( + const Operation& operation) const { + std::u32string hash; + hash.push_back(operation.opcode); + for (auto operand : operation.operands) { + hash.push_back(static_cast(DataDescriptorHash()(operand))); + } + return std::hash()(hash); +} + +bool DataSynonymAndIdEquationFacts::OperationEquals::operator()( + const Operation& first, const Operation& second) const { + // Equal operations require... + // + // Equal opcodes. + if (first.opcode != second.opcode) { + return false; + } + // Matching operand counts. + if (first.operands.size() != second.operands.size()) { + return false; + } + // Equal operands. + for (uint32_t i = 0; i < first.operands.size(); i++) { + if (!DataDescriptorEquals()(first.operands[i], second.operands[i])) { + return false; + } + } + return true; +} + +DataSynonymAndIdEquationFacts::DataSynonymAndIdEquationFacts( + opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +bool DataSynonymAndIdEquationFacts::MaybeAddFact( + const protobufs::FactDataSynonym& fact, + const DeadBlockFacts& dead_block_facts, + const IrrelevantValueFacts& irrelevant_value_facts) { + if (irrelevant_value_facts.IdIsIrrelevant(fact.data1().object(), + dead_block_facts) || + irrelevant_value_facts.IdIsIrrelevant(fact.data2().object(), + dead_block_facts)) { + // Irrelevant ids cannot be synonymous with other ids. + return false; + } + + // Add the fact, including all facts relating sub-components of the data + // descriptors that are involved. + AddDataSynonymFactRecursive(fact.data1(), fact.data2()); + return true; +} + +bool DataSynonymAndIdEquationFacts::MaybeAddFact( + const protobufs::FactIdEquation& fact, + const DeadBlockFacts& dead_block_facts, + const IrrelevantValueFacts& irrelevant_value_facts) { + if (irrelevant_value_facts.IdIsIrrelevant(fact.lhs_id(), dead_block_facts)) { + // Irrelevant ids cannot participate in IdEquation facts. + return false; + } + + for (auto id : fact.rhs_id()) { + if (irrelevant_value_facts.IdIsIrrelevant(id, dead_block_facts)) { + // Irrelevant ids cannot participate in IdEquation facts. + return false; + } + } + + protobufs::DataDescriptor lhs_dd = MakeDataDescriptor(fact.lhs_id(), {}); + + // Register the LHS in the equivalence relation if needed. + RegisterDataDescriptor(lhs_dd); + + // Get equivalence class representatives for all ids used on the RHS of the + // equation. + std::vector rhs_dds; + for (auto rhs_id : fact.rhs_id()) { + // Register a data descriptor based on this id in the equivalence relation + // if needed, and then record the equivalence class representative. + rhs_dds.push_back(RegisterDataDescriptor(MakeDataDescriptor(rhs_id, {}))); + } + + // Now add the fact. + AddEquationFactRecursive(lhs_dd, static_cast(fact.opcode()), rhs_dds); + return true; +} + +DataSynonymAndIdEquationFacts::OperationSet +DataSynonymAndIdEquationFacts::GetEquations( + const protobufs::DataDescriptor* lhs) const { + auto existing = id_equations_.find(lhs); + if (existing == id_equations_.end()) { + return OperationSet(); + } + return existing->second; +} + +void DataSynonymAndIdEquationFacts::AddEquationFactRecursive( + const protobufs::DataDescriptor& lhs_dd, SpvOp opcode, + const std::vector& rhs_dds) { + assert(synonymous_.Exists(lhs_dd) && + "The LHS must be known to the equivalence relation."); + for (auto rhs_dd : rhs_dds) { + // Keep release compilers happy. + (void)(rhs_dd); + assert(synonymous_.Exists(*rhs_dd) && + "The RHS operands must be known to the equivalence relation."); + } + + auto lhs_dd_representative = synonymous_.Find(&lhs_dd); + + if (id_equations_.count(lhs_dd_representative) == 0) { + // We have not seen an equation with this LHS before, so associate the LHS + // with an initially empty set. + id_equations_.insert({lhs_dd_representative, OperationSet()}); + } + + { + auto existing_equations = id_equations_.find(lhs_dd_representative); + assert(existing_equations != id_equations_.end() && + "A set of operations should be present, even if empty."); + + Operation new_operation = {opcode, rhs_dds}; + if (existing_equations->second.count(new_operation)) { + // This equation is known, so there is nothing further to be done. + return; + } + // Add the equation to the set of known equations. + existing_equations->second.insert(new_operation); + } + + // Now try to work out corollaries implied by the new equation and existing + // facts. + switch (opcode) { + case SpvOpConvertSToF: + case SpvOpConvertUToF: + ComputeConversionDataSynonymFacts(*rhs_dds[0]); + break; + case SpvOpBitcast: { + assert(DataDescriptorsAreWellFormedAndComparable(lhs_dd, *rhs_dds[0]) && + "Operands of OpBitcast equation fact must have compatible types"); + if (!synonymous_.IsEquivalent(lhs_dd, *rhs_dds[0])) { + AddDataSynonymFactRecursive(lhs_dd, *rhs_dds[0]); + } + } break; + case SpvOpIAdd: { + // Equation form: "a = b + c" + for (const auto& equation : GetEquations(rhs_dds[0])) { + if (equation.opcode == SpvOpISub) { + // Equation form: "a = (d - e) + c" + if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) { + // Equation form: "a = (d - c) + c" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]); + } + } + } + for (const auto& equation : GetEquations(rhs_dds[1])) { + if (equation.opcode == SpvOpISub) { + // Equation form: "a = b + (d - e)" + if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) { + // Equation form: "a = b + (d - b)" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]); + } + } + } + break; + } + case SpvOpISub: { + // Equation form: "a = b - c" + for (const auto& equation : GetEquations(rhs_dds[0])) { + if (equation.opcode == SpvOpIAdd) { + // Equation form: "a = (d + e) - c" + if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) { + // Equation form: "a = (c + e) - c" + // We can thus infer "a = e" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1]); + } + if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[1])) { + // Equation form: "a = (d + c) - c" + // We can thus infer "a = d" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]); + } + } + + if (equation.opcode == SpvOpISub) { + // Equation form: "a = (d - e) - c" + if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[1])) { + // Equation form: "a = (c - e) - c" + // We can thus infer "a = -e" + AddEquationFactRecursive(lhs_dd, SpvOpSNegate, + {equation.operands[1]}); + } + } + } + + for (const auto& equation : GetEquations(rhs_dds[1])) { + if (equation.opcode == SpvOpIAdd) { + // Equation form: "a = b - (d + e)" + if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) { + // Equation form: "a = b - (b + e)" + // We can thus infer "a = -e" + AddEquationFactRecursive(lhs_dd, SpvOpSNegate, + {equation.operands[1]}); + } + if (synonymous_.IsEquivalent(*equation.operands[1], *rhs_dds[0])) { + // Equation form: "a = b - (d + b)" + // We can thus infer "a = -d" + AddEquationFactRecursive(lhs_dd, SpvOpSNegate, + {equation.operands[0]}); + } + } + if (equation.opcode == SpvOpISub) { + // Equation form: "a = b - (d - e)" + if (synonymous_.IsEquivalent(*equation.operands[0], *rhs_dds[0])) { + // Equation form: "a = b - (b - e)" + // We can thus infer "a = e" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[1]); + } + } + } + break; + } + case SpvOpLogicalNot: + case SpvOpSNegate: { + // Equation form: "a = !b" or "a = -b" + for (const auto& equation : GetEquations(rhs_dds[0])) { + if (equation.opcode == opcode) { + // Equation form: "a = !!b" or "a = -(-b)" + // We can thus infer "a = b" + AddDataSynonymFactRecursive(lhs_dd, *equation.operands[0]); + } + } + break; + } + default: + break; + } +} + +void DataSynonymAndIdEquationFacts::AddDataSynonymFactRecursive( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) { + assert((!ObjectStillExists(dd1) || !ObjectStillExists(dd2) || + DataDescriptorsAreWellFormedAndComparable(dd1, dd2)) && + "Mismatched data descriptors."); + + // Record that the data descriptors provided in the fact are equivalent. + MakeEquivalent(dd1, dd2); + assert(synonymous_.Find(&dd1) == synonymous_.Find(&dd2) && + "|dd1| and |dd2| must have a single representative"); + + // Compute various corollary facts. + + // |dd1| and |dd2| belong to the same equivalence class so it doesn't matter + // which one we use here. + ComputeConversionDataSynonymFacts(dd1); + + ComputeCompositeDataSynonymFacts(dd1, dd2); +} + +void DataSynonymAndIdEquationFacts::ComputeConversionDataSynonymFacts( + const protobufs::DataDescriptor& dd) { + assert(synonymous_.Exists(dd) && + "|dd| should've been registered in the equivalence relation"); + + if (!ObjectStillExists(dd)) { + // The object is gone from the module, so we cannot proceed. + return; + } + + const auto* type = + ir_context_->get_type_mgr()->GetType(fuzzerutil::WalkCompositeTypeIndices( + ir_context_, fuzzerutil::GetTypeId(ir_context_, dd.object()), + dd.index())); + assert(type && "Data descriptor has invalid type"); + + if ((type->AsVector() && type->AsVector()->element_type()->AsInteger()) || + type->AsInteger()) { + // If there exist equation facts of the form |%a = opcode %representative| + // and |%b = opcode %representative| where |opcode| is either OpConvertSToF + // or OpConvertUToF, then |a| and |b| are synonymous. + std::vector convert_s_to_f_lhs; + std::vector convert_u_to_f_lhs; + + for (const auto& fact : id_equations_) { + auto equivalence_class = synonymous_.GetEquivalenceClass(*fact.first); + auto dd_it = + std::find_if(equivalence_class.begin(), equivalence_class.end(), + [this](const protobufs::DataDescriptor* a) { + return ObjectStillExists(*a); + }); + if (dd_it == equivalence_class.end()) { + // Skip |equivalence_class| if it has no valid ids. + continue; + } + + for (const auto& equation : fact.second) { + if (synonymous_.IsEquivalent(*equation.operands[0], dd)) { + if (equation.opcode == SpvOpConvertSToF) { + convert_s_to_f_lhs.push_back(*dd_it); + } else if (equation.opcode == SpvOpConvertUToF) { + convert_u_to_f_lhs.push_back(*dd_it); + } + } + } + } + + // We use pointers in the initializer list here since otherwise we would + // copy memory from these vectors. + for (const auto* synonyms : {&convert_s_to_f_lhs, &convert_u_to_f_lhs}) { + for (const auto* synonym_a : *synonyms) { + for (const auto* synonym_b : *synonyms) { + // DataDescriptorsAreWellFormedAndComparable will be called in the + // AddDataSynonymFactRecursive method. + if (!synonymous_.IsEquivalent(*synonym_a, *synonym_b)) { + // |synonym_a| and |synonym_b| have compatible types - they are + // synonymous. + AddDataSynonymFactRecursive(*synonym_a, *synonym_b); + } + } + } + } + } +} + +void DataSynonymAndIdEquationFacts::ComputeCompositeDataSynonymFacts( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) { + // Check whether this is a synonym about composite objects. If it is, + // we can recursively add synonym facts about their associated sub-components. + + // Get the type of the object referred to by the first data descriptor in the + // synonym fact. + uint32_t type_id = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, + ir_context_->get_def_use_mgr()->GetDef(dd1.object())->type_id(), + dd1.index()); + auto type = ir_context_->get_type_mgr()->GetType(type_id); + auto type_instruction = ir_context_->get_def_use_mgr()->GetDef(type_id); + assert(type != nullptr && + "Invalid data synonym fact: one side has an unknown type."); + + // Check whether the type is composite, recording the number of elements + // associated with the composite if so. + uint32_t num_composite_elements; + if (type->AsArray()) { + num_composite_elements = + fuzzerutil::GetArraySize(*type_instruction, ir_context_); + } else if (type->AsMatrix()) { + num_composite_elements = type->AsMatrix()->element_count(); + } else if (type->AsStruct()) { + num_composite_elements = + fuzzerutil::GetNumberOfStructMembers(*type_instruction); + } else if (type->AsVector()) { + num_composite_elements = type->AsVector()->element_count(); + } else { + // The type is not a composite, so return. + return; + } + + // If the fact has the form: + // obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n] + // then for each composite index i, we add a fact of the form: + // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i] + // + // However, to avoid adding a large number of synonym facts e.g. in the case + // of arrays, we bound the number of composite elements to which this is + // applied. Nevertheless, we always add a synonym fact for the final + // components, as this may be an interesting edge case. + + // The bound on the number of indices of the composite pair to note as being + // synonymous. + const uint32_t kCompositeElementBound = 10; + + for (uint32_t i = 0; i < num_composite_elements;) { + std::vector extended_indices1 = + fuzzerutil::RepeatedFieldToVector(dd1.index()); + extended_indices1.push_back(i); + std::vector extended_indices2 = + fuzzerutil::RepeatedFieldToVector(dd2.index()); + extended_indices2.push_back(i); + AddDataSynonymFactRecursive( + MakeDataDescriptor(dd1.object(), extended_indices1), + MakeDataDescriptor(dd2.object(), extended_indices2)); + + if (i < kCompositeElementBound - 1 || i == num_composite_elements - 1) { + // We have not reached the bound yet, or have already skipped ahead to the + // last element, so increment the loop counter as standard. + i++; + } else { + // We have reached the bound, so skip ahead to the last element. + assert(i == kCompositeElementBound - 1); + i = num_composite_elements - 1; + } + } +} + +void DataSynonymAndIdEquationFacts::ComputeClosureOfFacts( + uint32_t maximum_equivalence_class_size) { + // Suppose that obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n] are distinct + // data descriptors that describe objects of the same composite type, and that + // the composite type is comprised of k components. + // + // For example, if m is a mat4x4 and v a vec4, we might consider: + // m[2]: describes the 2nd column of m, a vec4 + // v[]: describes all of v, a vec4 + // + // Suppose that we know, for every 0 <= i < k, that the fact: + // obj_1[a_1, ..., a_m, i] == obj_2[b_1, ..., b_n, i] + // holds - i.e. that the children of the two data descriptors are synonymous. + // + // Then we can conclude that: + // obj_1[a_1, ..., a_m] == obj_2[b_1, ..., b_n] + // holds. + // + // For instance, if we have the facts: + // m[2, 0] == v[0] + // m[2, 1] == v[1] + // m[2, 2] == v[2] + // m[2, 3] == v[3] + // then we can conclude that: + // m[2] == v. + // + // This method repeatedly searches the equivalence relation of data + // descriptors, deducing and adding such facts, until a pass over the + // relation leads to no further facts being deduced. + + // The method relies on working with pairs of data descriptors, and in + // particular being able to hash and compare such pairs. + + using DataDescriptorPair = + std::pair; + + struct DataDescriptorPairHash { + std::size_t operator()(const DataDescriptorPair& pair) const { + return DataDescriptorHash()(&pair.first) ^ + DataDescriptorHash()(&pair.second); + } + }; + + struct DataDescriptorPairEquals { + bool operator()(const DataDescriptorPair& first, + const DataDescriptorPair& second) const { + return (DataDescriptorEquals()(&first.first, &second.first) && + DataDescriptorEquals()(&first.second, &second.second)) || + (DataDescriptorEquals()(&first.first, &second.second) && + DataDescriptorEquals()(&first.second, &second.first)); + } + }; + + // This map records, for a given pair of composite data descriptors of the + // same type, all the indices at which the data descriptors are known to be + // synonymous. A pair is a key to this map only if we have observed that + // the pair are synonymous at *some* index, but not at *all* indices. + // Once we find that a pair of data descriptors are equivalent at all indices + // we record the fact that they are synonymous and remove them from the map. + // + // Using the m and v example from above, initially the pair (m[2], v) would + // not be a key to the map. If we find that m[2, 2] == v[2] holds, we would + // add an entry: + // (m[2], v) -> [false, false, true, false] + // to record that they are synonymous at index 2. If we then find that + // m[2, 0] == v[0] holds, we would update this entry to: + // (m[2], v) -> [true, false, true, false] + // If we then find that m[2, 3] == v[3] holds, we would update this entry to: + // (m[2], v) -> [true, false, true, true] + // Finally, if we then find that m[2, 1] == v[1] holds, which would make the + // boolean vector true at every index, we would add the fact: + // m[2] == v + // to the equivalence relation and remove (m[2], v) from the map. + std::unordered_map, + DataDescriptorPairHash, DataDescriptorPairEquals> + candidate_composite_synonyms; + + // We keep looking for new facts until we perform a complete pass over the + // equivalence relation without finding any new facts. + while (closure_computation_required_) { + // We have not found any new facts yet during this pass; we set this to + // 'true' if we do find a new fact. + closure_computation_required_ = false; + + // Consider each class in the equivalence relation. + for (auto representative : + synonymous_.GetEquivalenceClassRepresentatives()) { + auto equivalence_class = synonymous_.GetEquivalenceClass(*representative); + + if (equivalence_class.size() > maximum_equivalence_class_size) { + // This equivalence class is larger than the maximum size we are willing + // to consider, so we skip it. This potentially leads to missed fact + // deductions, but avoids excessive runtime for closure computation. + continue; + } + + // Consider every data descriptor in the equivalence class. + for (auto dd1_it = equivalence_class.begin(); + dd1_it != equivalence_class.end(); ++dd1_it) { + // If this data descriptor has no indices then it does not have the form + // obj_1[a_1, ..., a_m, i], so move on. + auto dd1 = *dd1_it; + if (dd1->index_size() == 0) { + continue; + } + + // Consider every other data descriptor later in the equivalence class + // (due to symmetry, there is no need to compare with previous data + // descriptors). + auto dd2_it = dd1_it; + for (++dd2_it; dd2_it != equivalence_class.end(); ++dd2_it) { + auto dd2 = *dd2_it; + // If this data descriptor has no indices then it does not have the + // form obj_2[b_1, ..., b_n, i], so move on. + if (dd2->index_size() == 0) { + continue; + } + + // At this point we know that: + // - |dd1| has the form obj_1[a_1, ..., a_m, i] + // - |dd2| has the form obj_2[b_1, ..., b_n, j] + assert(dd1->index_size() > 0 && dd2->index_size() > 0 && + "Control should not reach here if either data descriptor has " + "no indices."); + + // We are only interested if i == j. + if (dd1->index(dd1->index_size() - 1) != + dd2->index(dd2->index_size() - 1)) { + continue; + } + + const uint32_t common_final_index = dd1->index(dd1->index_size() - 1); + + // Make data descriptors |dd1_prefix| and |dd2_prefix| for + // obj_1[a_1, ..., a_m] + // and + // obj_2[b_1, ..., b_n] + // These are the two data descriptors we might be getting closer to + // deducing as being synonymous, due to knowing that they are + // synonymous when extended by a particular index. + protobufs::DataDescriptor dd1_prefix; + dd1_prefix.set_object(dd1->object()); + for (uint32_t i = 0; i < static_cast(dd1->index_size() - 1); + i++) { + dd1_prefix.add_index(dd1->index(i)); + } + protobufs::DataDescriptor dd2_prefix; + dd2_prefix.set_object(dd2->object()); + for (uint32_t i = 0; i < static_cast(dd2->index_size() - 1); + i++) { + dd2_prefix.add_index(dd2->index(i)); + } + assert(!DataDescriptorEquals()(&dd1_prefix, &dd2_prefix) && + "By construction these prefixes should be different."); + + // If we already know that these prefixes are synonymous, move on. + if (synonymous_.Exists(dd1_prefix) && + synonymous_.Exists(dd2_prefix) && + synonymous_.IsEquivalent(dd1_prefix, dd2_prefix)) { + continue; + } + if (!ObjectStillExists(*dd1) || !ObjectStillExists(*dd2)) { + // The objects are not both available in the module, so we cannot + // investigate the types of the associated data descriptors; we need + // to move on. + continue; + } + // Get the type of obj_1 + auto dd1_root_type_id = + fuzzerutil::GetTypeId(ir_context_, dd1->object()); + // Use this type, together with a_1, ..., a_m, to get the type of + // obj_1[a_1, ..., a_m]. + auto dd1_prefix_type = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, dd1_root_type_id, dd1_prefix.index()); + + // Similarly, get the type of obj_2 and use it to get the type of + // obj_2[b_1, ..., b_n]. + auto dd2_root_type_id = + fuzzerutil::GetTypeId(ir_context_, dd2->object()); + auto dd2_prefix_type = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, dd2_root_type_id, dd2_prefix.index()); + + // If the types of dd1_prefix and dd2_prefix are not the same, they + // cannot be synonymous. + if (dd1_prefix_type != dd2_prefix_type) { + continue; + } + + // At this point, we know we have synonymous data descriptors of the + // form: + // obj_1[a_1, ..., a_m, i] + // obj_2[b_1, ..., b_n, i] + // with the same last_index i, such that: + // obj_1[a_1, ..., a_m] + // and + // obj_2[b_1, ..., b_n] + // have the same type. + + // Work out how many components there are in the (common) commposite + // type associated with obj_1[a_1, ..., a_m] and obj_2[b_1, ..., b_n]. + // This depends on whether the composite type is array, matrix, struct + // or vector. + uint32_t num_components_in_composite; + auto composite_type = + ir_context_->get_type_mgr()->GetType(dd1_prefix_type); + auto composite_type_instruction = + ir_context_->get_def_use_mgr()->GetDef(dd1_prefix_type); + if (composite_type->AsArray()) { + num_components_in_composite = fuzzerutil::GetArraySize( + *composite_type_instruction, ir_context_); + if (num_components_in_composite == 0) { + // This indicates that the array has an unknown size, in which + // case we cannot be sure we have matched all of its elements with + // synonymous elements of another array. + continue; + } + } else if (composite_type->AsMatrix()) { + num_components_in_composite = + composite_type->AsMatrix()->element_count(); + } else if (composite_type->AsStruct()) { + num_components_in_composite = fuzzerutil::GetNumberOfStructMembers( + *composite_type_instruction); + } else { + assert(composite_type->AsVector()); + num_components_in_composite = + composite_type->AsVector()->element_count(); + } + + // We are one step closer to being able to say that |dd1_prefix| and + // |dd2_prefix| are synonymous. + DataDescriptorPair candidate_composite_synonym(dd1_prefix, + dd2_prefix); + + // We look up what we already know about this pair. + auto existing_entry = + candidate_composite_synonyms.find(candidate_composite_synonym); + + if (existing_entry == candidate_composite_synonyms.end()) { + // If this is the first time we have seen the pair, we make a vector + // of size |num_components_in_composite| that is 'true' at the + // common final index associated with |dd1| and |dd2|, and 'false' + // everywhere else, and register this vector as being associated + // with the pair. + std::vector entry; + for (uint32_t i = 0; i < num_components_in_composite; i++) { + entry.push_back(i == common_final_index); + } + candidate_composite_synonyms[candidate_composite_synonym] = entry; + existing_entry = + candidate_composite_synonyms.find(candidate_composite_synonym); + } else { + // We have seen this pair of data descriptors before, and we now + // know that they are synonymous at one further index, so we + // update the entry to record that. + existing_entry->second[common_final_index] = true; + } + assert(existing_entry != candidate_composite_synonyms.end()); + + // Check whether |dd1_prefix| and |dd2_prefix| are now known to match + // at every sub-component. + bool all_components_match = true; + for (uint32_t i = 0; i < num_components_in_composite; i++) { + if (!existing_entry->second[i]) { + all_components_match = false; + break; + } + } + if (all_components_match) { + // The two prefixes match on all sub-components, so we know that + // they are synonymous. We add this fact *non-recursively*, as we + // have deduced that |dd1_prefix| and |dd2_prefix| are synonymous + // by observing that all their sub-components are already + // synonymous. + assert(DataDescriptorsAreWellFormedAndComparable(dd1_prefix, + dd2_prefix)); + MakeEquivalent(dd1_prefix, dd2_prefix); + // Now that we know this pair of data descriptors are synonymous, + // there is no point recording how close they are to being + // synonymous. + candidate_composite_synonyms.erase(candidate_composite_synonym); + } + } + } + } + } +} + +void DataSynonymAndIdEquationFacts::MakeEquivalent( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) { + // Register the data descriptors if they are not already known to the + // equivalence relation. + RegisterDataDescriptor(dd1); + RegisterDataDescriptor(dd2); + + if (synonymous_.IsEquivalent(dd1, dd2)) { + // The data descriptors are already known to be equivalent, so there is + // nothing to do. + return; + } + + // We must make the data descriptors equivalent, and also make sure any + // equation facts known about their representatives are merged. + + // Record the original equivalence class representatives of the data + // descriptors. + auto dd1_original_representative = synonymous_.Find(&dd1); + auto dd2_original_representative = synonymous_.Find(&dd2); + + // Make the data descriptors equivalent. + synonymous_.MakeEquivalent(dd1, dd2); + // As we have updated the equivalence relation, we might be able to deduce + // more facts by performing a closure computation, so we record that such a + // computation is required. + closure_computation_required_ = true; + + // At this point, exactly one of |dd1_original_representative| and + // |dd2_original_representative| will be the representative of the combined + // equivalence class. We work out which one of them is still the class + // representative and which one is no longer the class representative. + + auto still_representative = synonymous_.Find(dd1_original_representative) == + dd1_original_representative + ? dd1_original_representative + : dd2_original_representative; + auto no_longer_representative = + still_representative == dd1_original_representative + ? dd2_original_representative + : dd1_original_representative; + + assert(no_longer_representative != still_representative && + "The current and former representatives cannot be the same."); + + // We now need to add all equations about |no_longer_representative| to the + // set of equations known about |still_representative|. + + // Get the equations associated with |no_longer_representative|. + auto no_longer_representative_id_equations = + id_equations_.find(no_longer_representative); + if (no_longer_representative_id_equations != id_equations_.end()) { + // There are some equations to transfer. There might not yet be any + // equations about |still_representative|; create an empty set of equations + // if this is the case. + if (!id_equations_.count(still_representative)) { + id_equations_.insert({still_representative, OperationSet()}); + } + auto still_representative_id_equations = + id_equations_.find(still_representative); + assert(still_representative_id_equations != id_equations_.end() && + "At this point there must be a set of equations."); + // Add all the equations known about |no_longer_representative| to the set + // of equations known about |still_representative|. + still_representative_id_equations->second.insert( + no_longer_representative_id_equations->second.begin(), + no_longer_representative_id_equations->second.end()); + } + // Delete the no longer-relevant equations about |no_longer_representative|. + id_equations_.erase(no_longer_representative); +} + +const protobufs::DataDescriptor* +DataSynonymAndIdEquationFacts::RegisterDataDescriptor( + const protobufs::DataDescriptor& dd) { + return synonymous_.Exists(dd) ? synonymous_.Find(&dd) + : synonymous_.Register(dd); +} + +bool DataSynonymAndIdEquationFacts::DataDescriptorsAreWellFormedAndComparable( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) const { + if (!ObjectStillExists(dd1) || !ObjectStillExists(dd2)) { + // We trivially return true if one or other of the objects associated with + // the data descriptors is gone. + return true; + } + + auto end_type_id_1 = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, fuzzerutil::GetTypeId(ir_context_, dd1.object()), + dd1.index()); + auto end_type_id_2 = fuzzerutil::WalkCompositeTypeIndices( + ir_context_, fuzzerutil::GetTypeId(ir_context_, dd2.object()), + dd2.index()); + // The end types of the data descriptors must exist. + if (end_type_id_1 == 0 || end_type_id_2 == 0) { + return false; + } + // Neither end type is allowed to be void. + if (ir_context_->get_def_use_mgr()->GetDef(end_type_id_1)->opcode() == + SpvOpTypeVoid || + ir_context_->get_def_use_mgr()->GetDef(end_type_id_2)->opcode() == + SpvOpTypeVoid) { + return false; + } + // If the end types are the same, the data descriptors are comparable. + if (end_type_id_1 == end_type_id_2) { + return true; + } + // Otherwise they are only comparable if they are integer scalars or integer + // vectors that differ only in signedness. + + // Get both types. + const auto* type_a = ir_context_->get_type_mgr()->GetType(end_type_id_1); + const auto* type_b = ir_context_->get_type_mgr()->GetType(end_type_id_2); + assert(type_a && type_b && "Data descriptors have invalid type(s)"); + + // If both types are numerical or vectors of numerical components, then they + // are compatible if they have the same number of components and the same bit + // count per component. + + if (type_a->AsVector() && type_b->AsVector()) { + const auto* vector_a = type_a->AsVector(); + const auto* vector_b = type_b->AsVector(); + + if (vector_a->element_count() != vector_b->element_count() || + vector_a->element_type()->AsBool() || + vector_b->element_type()->AsBool()) { + // The case where both vectors have boolean elements and the same number + // of components is handled by the direct equality check earlier. + // You can't have multiple identical boolean vector types. + return false; + } + + type_a = vector_a->element_type(); + type_b = vector_b->element_type(); + } + + auto get_bit_count_for_numeric_type = + [](const opt::analysis::Type& type) -> uint32_t { + if (const auto* integer = type.AsInteger()) { + return integer->width(); + } else if (const auto* floating = type.AsFloat()) { + return floating->width(); + } else { + assert(false && "|type| must be a numerical type"); + return 0; + } + }; + + // Checks that both |type_a| and |type_b| are either numerical or vectors of + // numerical components and have the same number of bits. + return (type_a->AsInteger() || type_a->AsFloat()) && + (type_b->AsInteger() || type_b->AsFloat()) && + (get_bit_count_for_numeric_type(*type_a) == + get_bit_count_for_numeric_type(*type_b)); +} + +std::vector +DataSynonymAndIdEquationFacts::GetSynonymsForId(uint32_t id) const { + return GetSynonymsForDataDescriptor(MakeDataDescriptor(id, {})); +} + +std::vector +DataSynonymAndIdEquationFacts::GetSynonymsForDataDescriptor( + const protobufs::DataDescriptor& data_descriptor) const { + std::vector result; + if (synonymous_.Exists(data_descriptor)) { + for (auto dd : synonymous_.GetEquivalenceClass(data_descriptor)) { + // There may be data descriptors in the equivalence class whose base + // objects have been removed from the module. We do not expose these + // data descriptors to clients of the fact manager. + if (ObjectStillExists(*dd)) { + result.push_back(dd); + } + } + } + return result; +} + +std::vector +DataSynonymAndIdEquationFacts::GetIdsForWhichSynonymsAreKnown() const { + std::vector result; + for (auto& data_descriptor : synonymous_.GetAllKnownValues()) { + // We skip any data descriptors whose base objects no longer exist in the + // module, and we restrict attention to data descriptors for plain ids, + // which have no indices. + if (ObjectStillExists(*data_descriptor) && + data_descriptor->index().empty()) { + result.push_back(data_descriptor->object()); + } + } + return result; +} + +std::vector +DataSynonymAndIdEquationFacts::GetAllKnownSynonyms() const { + std::vector result; + for (const auto* dd : synonymous_.GetAllKnownValues()) { + if (ObjectStillExists(*dd)) { + result.push_back(dd); + } + } + return result; +} + +bool DataSynonymAndIdEquationFacts::IsSynonymous( + const protobufs::DataDescriptor& data_descriptor1, + const protobufs::DataDescriptor& data_descriptor2) const { + return synonymous_.Exists(data_descriptor1) && + synonymous_.Exists(data_descriptor2) && + synonymous_.IsEquivalent(data_descriptor1, data_descriptor2); +} + +bool DataSynonymAndIdEquationFacts::ObjectStillExists( + const protobufs::DataDescriptor& dd) const { + return ir_context_->get_def_use_mgr()->GetDef(dd.object()) != nullptr; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h b/third_party/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h new file mode 100644 index 0000000..6652f30 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h @@ -0,0 +1,185 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_ + +#include +#include + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/equivalence_relation.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// Forward reference to the DeadBlockFacts class. +class DeadBlockFacts; +// Forward reference to the IrrelevantValueFacts class. +class IrrelevantValueFacts; + +// The purpose of this class is to group the fields and data used to represent +// facts about data synonyms and id equations. +class DataSynonymAndIdEquationFacts { + public: + explicit DataSynonymAndIdEquationFacts(opt::IRContext* ir_context); + + // See method in FactManager which delegates to this method. Returns true if + // neither |fact.data1()| nor |fact.data2()| contain an + // irrelevant id. Otherwise, returns false. |dead_block_facts| and + // |irrelevant_value_facts| are passed for consistency checks. + bool MaybeAddFact(const protobufs::FactDataSynonym& fact, + const DeadBlockFacts& dead_block_facts, + const IrrelevantValueFacts& irrelevant_value_facts); + + // See method in FactManager which delegates to this method. Returns true if + // neither |fact.lhs_id()| nor any of |fact.rhs_id()| is irrelevant. Returns + // false otherwise. |dead_block_facts| and |irrelevant_value_facts| are passed + // for consistency checks. + bool MaybeAddFact(const protobufs::FactIdEquation& fact, + const DeadBlockFacts& dead_block_facts, + const IrrelevantValueFacts& irrelevant_value_facts); + + // See method in FactManager which delegates to this method. + std::vector GetSynonymsForId( + uint32_t id) const; + + // See method in FactManager which delegates to this method. + std::vector GetSynonymsForDataDescriptor( + const protobufs::DataDescriptor& data_descriptor) const; + + // See method in FactManager which delegates to this method. + std::vector GetIdsForWhichSynonymsAreKnown() const; + + // See method in FactManager which delegates to this method. + std::vector GetAllKnownSynonyms() const; + + // See method in FactManager which delegates to this method. + bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1, + const protobufs::DataDescriptor& data_descriptor2) const; + + // See method in FactManager which delegates to this method. + void ComputeClosureOfFacts(uint32_t maximum_equivalence_class_size); + + private: + // This helper struct represents the right hand side of an equation as an + // operator applied to a number of data descriptor operands. + struct Operation { + SpvOp opcode; + std::vector operands; + }; + + // Hashing for operations, to allow deterministic unordered sets. + struct OperationHash { + size_t operator()(const Operation& operation) const; + }; + + // Equality for operations, to allow deterministic unordered sets. + struct OperationEquals { + bool operator()(const Operation& first, const Operation& second) const; + }; + + using OperationSet = + std::unordered_set; + + // Adds the synonym |dd1| = |dd2| to the set of managed facts, and recurses + // into sub-components of the data descriptors, if they are composites, to + // record that their components are pairwise-synonymous. + void AddDataSynonymFactRecursive(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2); + + // Computes various corollary facts from the data descriptor |dd| if members + // of its equivalence class participate in equation facts with OpConvert* + // opcodes. The descriptor should be registered in the equivalence relation. + void ComputeConversionDataSynonymFacts(const protobufs::DataDescriptor& dd); + + // Recurses into sub-components of the data descriptors, if they are + // composites, to record that their components are pairwise-synonymous. + void ComputeCompositeDataSynonymFacts(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2); + + // Records the fact that |dd1| and |dd2| are equivalent, and merges the sets + // of equations that are known about them. + void MakeEquivalent(const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2); + + // Registers a data descriptor in the equivalence relation if it hasn't been + // registered yet, and returns its representative. + const protobufs::DataDescriptor* RegisterDataDescriptor( + const protobufs::DataDescriptor& dd); + + // Trivially returns true if either |dd1| or |dd2|'s objects are not present + // in the module. + // + // Otherwise, returns true if and only if |dd1| and |dd2| are valid data + // descriptors whose associated data have compatible types. Two types are + // compatible if: + // - they are the same + // - they both are numerical or vectors of numerical components with the same + // number of components and the same bit count per component + bool DataDescriptorsAreWellFormedAndComparable( + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) const; + + OperationSet GetEquations(const protobufs::DataDescriptor* lhs) const; + + // Requires that |lhs_dd| and every element of |rhs_dds| is present in the + // |synonymous_| equivalence relation, but is not necessarily its own + // representative. Records the fact that the equation + // "|lhs_dd| |opcode| |rhs_dds_non_canonical|" holds, and adds any + // corollaries, in the form of data synonym or equation facts, that follow + // from this and other known facts. + void AddEquationFactRecursive( + const protobufs::DataDescriptor& lhs_dd, SpvOp opcode, + const std::vector& rhs_dds); + + // Returns true if and only if |dd.object()| still exists in the module. + bool ObjectStillExists(const protobufs::DataDescriptor& dd) const; + + // The data descriptors that are known to be synonymous with one another are + // captured by this equivalence relation. + EquivalenceRelation + synonymous_; + + // When a new synonym fact is added, it may be possible to deduce further + // synonym facts by computing a closure of all known facts. However, this is + // an expensive operation, so it should be performed sparingly and only there + // is some chance of new facts being deduced. This boolean tracks whether a + // closure computation is required - i.e., whether a new fact has been added + // since the last time such a computation was performed. + bool closure_computation_required_ = false; + + // Represents a set of equations on data descriptors as a map indexed by + // left-hand-side, mapping a left-hand-side to a set of operations, each of + // which (together with the left-hand-side) defines an equation. + // + // All data descriptors occurring in equations are required to be present in + // the |synonymous_| equivalence relation, and to be their own representatives + // in that relation. + std::unordered_map + id_equations_; + + // Pointer to the SPIR-V module we store facts about. + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_DATA_SYNONYM_AND_ID_EQUATION_FACTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/dead_block_facts.cpp b/third_party/spirv-tools/source/fuzz/fact_manager/dead_block_facts.cpp new file mode 100644 index 0000000..f3e0ef8 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/dead_block_facts.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/dead_block_facts.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +DeadBlockFacts::DeadBlockFacts(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +bool DeadBlockFacts::MaybeAddFact(const protobufs::FactBlockIsDead& fact) { + if (!fuzzerutil::MaybeFindBlock(ir_context_, fact.block_id())) { + return false; + } + + dead_block_ids_.insert(fact.block_id()); + return true; +} + +bool DeadBlockFacts::BlockIsDead(uint32_t block_id) const { + return dead_block_ids_.count(block_id) != 0; +} + +const std::unordered_set& DeadBlockFacts::GetDeadBlocks() const { + return dead_block_ids_; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/dead_block_facts.h b/third_party/spirv-tools/source/fuzz/fact_manager/dead_block_facts.h new file mode 100644 index 0000000..8ac5c3c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/dead_block_facts.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_DEAD_BLOCK_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_DEAD_BLOCK_FACTS_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// The purpose of this class is to group the fields and data used to represent +// facts about data blocks. +class DeadBlockFacts { + public: + explicit DeadBlockFacts(opt::IRContext* ir_context); + + // Marks |fact.block_id()| as being dead. Returns true if |fact.block_id()| + // represents a result id of some OpLabel instruction in |ir_context_|. + // Returns false otherwise. + bool MaybeAddFact(const protobufs::FactBlockIsDead& fact); + + // See method in FactManager which delegates to this method. + bool BlockIsDead(uint32_t block_id) const; + + // Returns a set of all the block ids that have been declared dead. + const std::unordered_set& GetDeadBlocks() const; + + private: + std::unordered_set dead_block_ids_; + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_DEAD_BLOCK_FACTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/fact_manager.cpp b/third_party/spirv-tools/source/fuzz/fact_manager/fact_manager.cpp new file mode 100644 index 0000000..40c0865 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/fact_manager.cpp @@ -0,0 +1,279 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fact_manager.h" + +#include +#include + +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace { + +std::string ToString(const protobufs::FactConstantUniform& fact) { + std::stringstream stream; + stream << "(" << fact.uniform_buffer_element_descriptor().descriptor_set() + << ", " << fact.uniform_buffer_element_descriptor().binding() << ")["; + + bool first = true; + for (auto index : fact.uniform_buffer_element_descriptor().index()) { + if (first) { + first = false; + } else { + stream << ", "; + } + stream << index; + } + + stream << "] == ["; + + first = true; + for (auto constant_word : fact.constant_word()) { + if (first) { + first = false; + } else { + stream << ", "; + } + stream << constant_word; + } + + stream << "]"; + return stream.str(); +} + +std::string ToString(const protobufs::FactDataSynonym& fact) { + std::stringstream stream; + stream << fact.data1() << " = " << fact.data2(); + return stream.str(); +} + +std::string ToString(const protobufs::FactIdEquation& fact) { + std::stringstream stream; + stream << fact.lhs_id(); + stream << " " << static_cast(fact.opcode()); + for (auto rhs_id : fact.rhs_id()) { + stream << " " << rhs_id; + } + return stream.str(); +} + +std::string ToString(const protobufs::Fact& fact) { + switch (fact.fact_case()) { + case protobufs::Fact::kConstantUniformFact: + return ToString(fact.constant_uniform_fact()); + case protobufs::Fact::kDataSynonymFact: + return ToString(fact.data_synonym_fact()); + case protobufs::Fact::kIdEquationFact: + return ToString(fact.id_equation_fact()); + default: + assert(false && "Stringification not supported for this fact."); + return ""; + } +} + +} // namespace + +FactManager::FactManager(opt::IRContext* ir_context) + : constant_uniform_facts_(ir_context), + data_synonym_and_id_equation_facts_(ir_context), + dead_block_facts_(ir_context), + livesafe_function_facts_(ir_context), + irrelevant_value_facts_(ir_context) {} + +void FactManager::AddInitialFacts(const MessageConsumer& message_consumer, + const protobufs::FactSequence& facts) { + for (auto& fact : facts.fact()) { + if (!MaybeAddFact(fact)) { + auto message = "Invalid fact " + ToString(fact) + " ignored."; + message_consumer(SPV_MSG_WARNING, nullptr, {}, message.c_str()); + } + } +} + +bool FactManager::MaybeAddFact(const fuzz::protobufs::Fact& fact) { + switch (fact.fact_case()) { + case protobufs::Fact::kBlockIsDeadFact: + return dead_block_facts_.MaybeAddFact(fact.block_is_dead_fact()); + case protobufs::Fact::kConstantUniformFact: + return constant_uniform_facts_.MaybeAddFact(fact.constant_uniform_fact()); + case protobufs::Fact::kDataSynonymFact: + return data_synonym_and_id_equation_facts_.MaybeAddFact( + fact.data_synonym_fact(), dead_block_facts_, irrelevant_value_facts_); + case protobufs::Fact::kFunctionIsLivesafeFact: + return livesafe_function_facts_.MaybeAddFact( + fact.function_is_livesafe_fact()); + case protobufs::Fact::kIdEquationFact: + return data_synonym_and_id_equation_facts_.MaybeAddFact( + fact.id_equation_fact(), dead_block_facts_, irrelevant_value_facts_); + case protobufs::Fact::kIdIsIrrelevant: + return irrelevant_value_facts_.MaybeAddFact( + fact.id_is_irrelevant(), data_synonym_and_id_equation_facts_); + case protobufs::Fact::kPointeeValueIsIrrelevantFact: + return irrelevant_value_facts_.MaybeAddFact( + fact.pointee_value_is_irrelevant_fact(), + data_synonym_and_id_equation_facts_); + case protobufs::Fact::FACT_NOT_SET: + assert(false && "The fact must be set"); + return false; + } + + assert(false && "Unreachable"); + return false; +} + +void FactManager::AddFactDataSynonym(const protobufs::DataDescriptor& data1, + const protobufs::DataDescriptor& data2) { + protobufs::FactDataSynonym fact; + *fact.mutable_data1() = data1; + *fact.mutable_data2() = data2; + auto success = data_synonym_and_id_equation_facts_.MaybeAddFact( + fact, dead_block_facts_, irrelevant_value_facts_); + (void)success; // Keep compilers happy in release mode. + assert(success && "Unable to create DataSynonym fact"); +} + +std::vector FactManager::GetConstantsAvailableFromUniformsForType( + uint32_t type_id) const { + return constant_uniform_facts_.GetConstantsAvailableFromUniformsForType( + type_id); +} + +std::vector +FactManager::GetUniformDescriptorsForConstant(uint32_t constant_id) const { + return constant_uniform_facts_.GetUniformDescriptorsForConstant(constant_id); +} + +uint32_t FactManager::GetConstantFromUniformDescriptor( + const protobufs::UniformBufferElementDescriptor& uniform_descriptor) const { + return constant_uniform_facts_.GetConstantFromUniformDescriptor( + uniform_descriptor); +} + +std::vector FactManager::GetTypesForWhichUniformValuesAreKnown() + const { + return constant_uniform_facts_.GetTypesForWhichUniformValuesAreKnown(); +} + +const std::vector>& +FactManager::GetConstantUniformFactsAndTypes() const { + return constant_uniform_facts_.GetConstantUniformFactsAndTypes(); +} + +std::vector FactManager::GetIdsForWhichSynonymsAreKnown() const { + return data_synonym_and_id_equation_facts_.GetIdsForWhichSynonymsAreKnown(); +} + +std::vector FactManager::GetAllSynonyms() + const { + return data_synonym_and_id_equation_facts_.GetAllKnownSynonyms(); +} + +std::vector +FactManager::GetSynonymsForDataDescriptor( + const protobufs::DataDescriptor& data_descriptor) const { + return data_synonym_and_id_equation_facts_.GetSynonymsForDataDescriptor( + data_descriptor); +} + +std::vector FactManager::GetSynonymsForId( + uint32_t id) const { + return data_synonym_and_id_equation_facts_.GetSynonymsForId(id); +} + +bool FactManager::IsSynonymous( + const protobufs::DataDescriptor& data_descriptor1, + const protobufs::DataDescriptor& data_descriptor2) const { + return data_synonym_and_id_equation_facts_.IsSynonymous(data_descriptor1, + data_descriptor2); +} + +bool FactManager::BlockIsDead(uint32_t block_id) const { + return dead_block_facts_.BlockIsDead(block_id); +} + +void FactManager::AddFactBlockIsDead(uint32_t block_id) { + protobufs::FactBlockIsDead fact; + fact.set_block_id(block_id); + auto success = dead_block_facts_.MaybeAddFact(fact); + (void)success; // Keep compilers happy in release mode. + assert(success && "|block_id| is invalid"); +} + +bool FactManager::FunctionIsLivesafe(uint32_t function_id) const { + return livesafe_function_facts_.FunctionIsLivesafe(function_id); +} + +void FactManager::AddFactFunctionIsLivesafe(uint32_t function_id) { + protobufs::FactFunctionIsLivesafe fact; + fact.set_function_id(function_id); + auto success = livesafe_function_facts_.MaybeAddFact(fact); + (void)success; // Keep compilers happy in release mode. + assert(success && "|function_id| is invalid"); +} + +bool FactManager::PointeeValueIsIrrelevant(uint32_t pointer_id) const { + return irrelevant_value_facts_.PointeeValueIsIrrelevant(pointer_id); +} + +bool FactManager::IdIsIrrelevant(uint32_t result_id) const { + return irrelevant_value_facts_.IdIsIrrelevant(result_id, dead_block_facts_); +} + +std::unordered_set FactManager::GetIrrelevantIds() const { + return irrelevant_value_facts_.GetIrrelevantIds(dead_block_facts_); +} + +void FactManager::AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id) { + protobufs::FactPointeeValueIsIrrelevant fact; + fact.set_pointer_id(pointer_id); + auto success = irrelevant_value_facts_.MaybeAddFact( + fact, data_synonym_and_id_equation_facts_); + (void)success; // Keep compilers happy in release mode. + assert(success && "|pointer_id| is invalid"); +} + +void FactManager::AddFactIdIsIrrelevant(uint32_t result_id) { + protobufs::FactIdIsIrrelevant fact; + fact.set_result_id(result_id); + auto success = irrelevant_value_facts_.MaybeAddFact( + fact, data_synonym_and_id_equation_facts_); + (void)success; // Keep compilers happy in release mode. + assert(success && "|result_id| is invalid"); +} + +void FactManager::AddFactIdEquation(uint32_t lhs_id, SpvOp opcode, + const std::vector& rhs_id) { + protobufs::FactIdEquation fact; + fact.set_lhs_id(lhs_id); + fact.set_opcode(opcode); + for (auto an_rhs_id : rhs_id) { + fact.add_rhs_id(an_rhs_id); + } + auto success = data_synonym_and_id_equation_facts_.MaybeAddFact( + fact, dead_block_facts_, irrelevant_value_facts_); + (void)success; // Keep compilers happy in release mode. + assert(success && "Can't create IdIsIrrelevant fact"); +} + +void FactManager::ComputeClosureOfFacts( + uint32_t maximum_equivalence_class_size) { + data_synonym_and_id_equation_facts_.ComputeClosureOfFacts( + maximum_equivalence_class_size); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/fact_manager.h b/third_party/spirv-tools/source/fuzz/fact_manager/fact_manager.h new file mode 100644 index 0000000..5cf5b18 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/fact_manager.h @@ -0,0 +1,225 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_FACT_MANAGER_H_ +#define SOURCE_FUZZ_FACT_MANAGER_FACT_MANAGER_H_ + +#include +#include +#include + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fact_manager/constant_uniform_facts.h" +#include "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h" +#include "source/fuzz/fact_manager/dead_block_facts.h" +#include "source/fuzz/fact_manager/irrelevant_value_facts.h" +#include "source/fuzz/fact_manager/livesafe_function_facts.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/constants.h" + +namespace spvtools { +namespace fuzz { + +// Keeps track of facts about the module being transformed on which the fuzzing +// process can depend. Some initial facts can be provided, for example about +// guarantees on the values of inputs to SPIR-V entry points. Transformations +// may then rely on these facts, can add further facts that they establish. +// Facts are intended to be simple properties that either cannot be deduced from +// the module (such as properties that are guaranteed to hold for entry point +// inputs), or that are established by transformations, likely to be useful for +// future transformations, and not completely trivial to deduce straight from +// the module. +class FactManager { + public: + explicit FactManager(opt::IRContext* ir_context); + + // Adds all the facts from |facts|, checking them for validity with respect to + // |ir_context_|. Warnings about invalid facts are communicated via + // |message_consumer|; such facts are otherwise ignored. + void AddInitialFacts(const MessageConsumer& message_consumer, + const protobufs::FactSequence& facts); + + // Checks the fact for validity with respect to |ir_context_|. Returns false, + // with no side effects, if the fact is invalid. Otherwise adds |fact| to the + // fact manager. + bool MaybeAddFact(const protobufs::Fact& fact); + + // Record the fact that |data1| and |data2| are synonymous. Neither |data1| + // nor |data2| may contain an irrelevant id. + void AddFactDataSynonym(const protobufs::DataDescriptor& data1, + const protobufs::DataDescriptor& data2); + + // Records the fact that |block_id| is dead. |block_id| must be a result id + // of some OpLabel instruction in the |ir_context_|. + void AddFactBlockIsDead(uint32_t block_id); + + // Records the fact that |function_id| is livesafe. |function_id| must be a + // result id of some non-entry-point function in the module. + void AddFactFunctionIsLivesafe(uint32_t function_id); + + // Records the fact that the value of the pointee associated with |pointer_id| + // is irrelevant: it does not affect the observable behaviour of the module. + // |pointer_id| must exist in the module and actually be a pointer. + void AddFactValueOfPointeeIsIrrelevant(uint32_t pointer_id); + + // Records a fact that the |result_id| is irrelevant (i.e. it doesn't affect + // the semantics of the module). + // |result_id| must exist in the module and it may not be a pointer. + void AddFactIdIsIrrelevant(uint32_t result_id); + + // Records the fact that |lhs_id| is defined by the equation: + // + // |lhs_id| = |opcode| |rhs_id[0]| ... |rhs_id[N-1]| + // + // Neither |lhs_id| nor any of |rhs_id| may be irrelevant. + void AddFactIdEquation(uint32_t lhs_id, SpvOp opcode, + const std::vector& rhs_id); + + // Inspects all known facts and adds corollary facts; e.g. if we know that + // a.x == b.x and a.y == b.y, where a and b have vec2 type, we can record + // that a == b holds. + // + // This method is expensive, and should only be called (by applying a + // transformation) at the start of a fuzzer pass that depends on data + // synonym facts, rather than calling it every time a new data synonym fact + // is added. + // + // The parameter |maximum_equivalence_class_size| specifies the size beyond + // which equivalence classes should not be mined for new facts, to avoid + // excessively-long closure computations. + void ComputeClosureOfFacts(uint32_t maximum_equivalence_class_size); + + // The fact manager is responsible for managing a few distinct categories of + // facts. In principle there could be different fact managers for each kind + // of fact, but in practice providing one 'go to' place for facts is + // convenient. To keep some separation, the public methods of the fact + // manager should be grouped according to the kind of fact to which they + // relate. + + //============================== + // Querying facts about uniform constants + + // Provides the distinct type ids for which at least one "constant == + // uniform element" fact is known. + std::vector GetTypesForWhichUniformValuesAreKnown() const; + + // Provides distinct constant ids with type |type_id| for which at least one + // "constant == uniform element" fact is known. If multiple identically- + // valued constants are relevant, only one will appear in the sequence. + std::vector GetConstantsAvailableFromUniformsForType( + uint32_t type_id) const; + + // Provides details of all uniform elements that are known to be equal to the + // constant associated with |constant_id| in |ir_context_|. + std::vector + GetUniformDescriptorsForConstant(uint32_t constant_id) const; + + // Returns the id of a constant whose value is known to match that of + // |uniform_descriptor|, and whose type matches the type of the uniform + // element. If multiple such constant is exist, the one that is returned + // is arbitrary. Returns 0 if no such constant id exists. + uint32_t GetConstantFromUniformDescriptor( + const protobufs::UniformBufferElementDescriptor& uniform_descriptor) + const; + + // Returns all "constant == uniform element" facts known to the fact + // manager, pairing each fact with id of the type that is associated with + // both the constant and the uniform element. + const std::vector>& + GetConstantUniformFactsAndTypes() const; + + // End of uniform constant facts + //============================== + + //============================== + // Querying facts about id synonyms + + // Returns every id for which a fact of the form "this id is synonymous with + // this piece of data" is known. + std::vector GetIdsForWhichSynonymsAreKnown() const; + + // Returns a vector of all data descriptors that participate in DataSynonym + // facts. All descriptors are guaranteed to exist in the |ir_context_|. + std::vector GetAllSynonyms() const; + + // Returns the equivalence class of all known synonyms of |id|, or an empty + // set if no synonyms are known. + std::vector GetSynonymsForId( + uint32_t id) const; + + // Returns the equivalence class of all known synonyms of |data_descriptor|, + // or empty if no synonyms are known. + std::vector GetSynonymsForDataDescriptor( + const protobufs::DataDescriptor& data_descriptor) const; + + // Returns true if and ony if |data_descriptor1| and |data_descriptor2| are + // known to be synonymous. + bool IsSynonymous(const protobufs::DataDescriptor& data_descriptor1, + const protobufs::DataDescriptor& data_descriptor2) const; + + // End of id synonym facts + //============================== + + //============================== + // Querying facts about dead blocks + + // Returns true if and ony if |block_id| is the id of a block known to be + // dynamically unreachable. + bool BlockIsDead(uint32_t block_id) const; + + // End of dead block facts + //============================== + + //============================== + // Querying facts about livesafe function + + // Returns true if and ony if |function_id| is the id of a function known + // to be livesafe. + bool FunctionIsLivesafe(uint32_t function_id) const; + + // End of dead livesafe function facts + //============================== + + //============================== + // Querying facts about irrelevant values + + // Returns true if and ony if the value of the pointee associated with + // |pointer_id| is irrelevant. + bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; + + // Returns true if there exists a fact that the |result_id| is irrelevant or + // if |result_id| is declared in a block that has been declared dead. + bool IdIsIrrelevant(uint32_t result_id) const; + + // Returns a set of all the ids which have been declared irrelevant, or which + // have been declared inside a dead block. + std::unordered_set GetIrrelevantIds() const; + + // End of irrelevant value facts + //============================== + + private: + // Keep these in alphabetical order. + fact_manager::ConstantUniformFacts constant_uniform_facts_; + fact_manager::DataSynonymAndIdEquationFacts + data_synonym_and_id_equation_facts_; + fact_manager::DeadBlockFacts dead_block_facts_; + fact_manager::LivesafeFunctionFacts livesafe_function_facts_; + fact_manager::IrrelevantValueFacts irrelevant_value_facts_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_FACT_MANAGER_H_ diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.cpp b/third_party/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.cpp new file mode 100644 index 0000000..07836ad --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/irrelevant_value_facts.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fact_manager/data_synonym_and_id_equation_facts.h" +#include "source/fuzz/fact_manager/dead_block_facts.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +IrrelevantValueFacts::IrrelevantValueFacts(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +bool IrrelevantValueFacts::MaybeAddFact( + const protobufs::FactPointeeValueIsIrrelevant& fact, + const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts) { + const auto* inst = ir_context_->get_def_use_mgr()->GetDef(fact.pointer_id()); + if (!inst || !inst->type_id()) { + // The id must exist in the module and have type id. + return false; + } + + if (!ir_context_->get_type_mgr()->GetType(inst->type_id())->AsPointer()) { + // The id must be a pointer. + return false; + } + + if (!data_synonym_and_id_equation_facts.GetSynonymsForId(fact.pointer_id()) + .empty()) { + // Irrelevant id cannot participate in DataSynonym facts. + return false; + } + + pointers_to_irrelevant_pointees_ids_.insert(fact.pointer_id()); + return true; +} + +bool IrrelevantValueFacts::MaybeAddFact( + const protobufs::FactIdIsIrrelevant& fact, + const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts) { + const auto* inst = ir_context_->get_def_use_mgr()->GetDef(fact.result_id()); + if (!inst || !inst->type_id()) { + // The id must exist in the module and have type id. + return false; + } + + if (ir_context_->get_type_mgr()->GetType(inst->type_id())->AsPointer()) { + // The id may not be a pointer. + return false; + } + + if (!data_synonym_and_id_equation_facts.GetSynonymsForId(fact.result_id()) + .empty()) { + // Irrelevant id cannot participate in DataSynonym facts. + return false; + } + + irrelevant_ids_.insert(fact.result_id()); + return true; +} + +bool IrrelevantValueFacts::PointeeValueIsIrrelevant(uint32_t pointer_id) const { + return pointers_to_irrelevant_pointees_ids_.count(pointer_id) != 0; +} + +bool IrrelevantValueFacts::IdIsIrrelevant( + uint32_t result_id, const DeadBlockFacts& dead_block_facts) const { + // The id is irrelevant if it has been declared irrelevant. + if (irrelevant_ids_.count(result_id)) { + return true; + } + + // The id must have a non-pointer type to be irrelevant. + auto def = ir_context_->get_def_use_mgr()->GetDef(result_id); + if (!def) { + return false; + } + auto type = ir_context_->get_type_mgr()->GetType(def->type_id()); + if (!type || type->AsPointer()) { + return false; + } + + // The id is irrelevant if it is in a dead block. + return ir_context_->get_instr_block(result_id) && + dead_block_facts.BlockIsDead( + ir_context_->get_instr_block(result_id)->id()); +} + +std::unordered_set IrrelevantValueFacts::GetIrrelevantIds( + const DeadBlockFacts& dead_block_facts) const { + // Get all the ids that have been declared irrelevant. + auto irrelevant_ids = irrelevant_ids_; + + // Get all the non-pointer ids declared in dead blocks that have a type. + for (uint32_t block_id : dead_block_facts.GetDeadBlocks()) { + auto block = fuzzerutil::MaybeFindBlock(ir_context_, block_id); + // It is possible and allowed for the block not to exist, e.g. it could have + // been merged with another block. + if (!block) { + continue; + } + block->ForEachInst([this, &irrelevant_ids](opt::Instruction* inst) { + // The instruction must have a result id and a type, and it must not be a + // pointer. + if (inst->HasResultId() && inst->type_id() && + !ir_context_->get_type_mgr()->GetType(inst->type_id())->AsPointer()) { + irrelevant_ids.emplace(inst->result_id()); + } + }); + } + + return irrelevant_ids; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.h b/third_party/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.h new file mode 100644 index 0000000..9faddc0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/irrelevant_value_facts.h @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// Forward reference to the DataSynonymAndIdEquationFacts class. +class DataSynonymAndIdEquationFacts; +// Forward reference to the DeadBlockFacts class. +class DeadBlockFacts; + +// The purpose of this class is to group the fields and data used to represent +// facts about various irrelevant values in the module. +class IrrelevantValueFacts { + public: + explicit IrrelevantValueFacts(opt::IRContext* ir_context); + + // See method in FactManager which delegates to this method. Returns true if + // |fact.pointer_id()| is a result id of pointer type in the |ir_context_| and + // |fact.pointer_id()| does not participate in DataSynonym facts. Returns + // false otherwise. |data_synonym_and_id_equation_facts| and |context| are + // passed for consistency checks. + bool MaybeAddFact( + const protobufs::FactPointeeValueIsIrrelevant& fact, + const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts); + + // See method in FactManager which delegates to this method. Returns true if + // |fact.result_id()| is a result id of non-pointer type in the |ir_context_| + // and |fact.result_id()| does not participate in DataSynonym facts. Returns + // false otherwise. |data_synonym_and_id_equation_facts| and |context| are + // passed for consistency checks. + bool MaybeAddFact( + const protobufs::FactIdIsIrrelevant& fact, + const DataSynonymAndIdEquationFacts& data_synonym_and_id_equation_facts); + + // See method in FactManager which delegates to this method. + bool PointeeValueIsIrrelevant(uint32_t pointer_id) const; + + // See method in FactManager which delegates to this method. + // |dead_block_facts| and |context| are passed to check whether |result_id| is + // declared inside a dead block, in which case it is irrelevant. + bool IdIsIrrelevant(uint32_t result_id, + const DeadBlockFacts& dead_block_facts) const; + + // See method in FactManager which delegates to this method. + // |dead_block_facts| and |context| are passed to also add all the ids + // declared in dead blocks to the set of irrelevant ids. + std::unordered_set GetIrrelevantIds( + const DeadBlockFacts& dead_block_facts) const; + + private: + std::unordered_set pointers_to_irrelevant_pointees_ids_; + std::unordered_set irrelevant_ids_; + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_IRRELEVANT_VALUE_FACTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.cpp b/third_party/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.cpp new file mode 100644 index 0000000..553ac57 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/livesafe_function_facts.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +LivesafeFunctionFacts::LivesafeFunctionFacts(opt::IRContext* ir_context) + : ir_context_(ir_context) {} + +bool LivesafeFunctionFacts::MaybeAddFact( + const protobufs::FactFunctionIsLivesafe& fact) { + if (!fuzzerutil::FindFunction(ir_context_, fact.function_id())) { + return false; + } + + if (fuzzerutil::FunctionIsEntryPoint(ir_context_, fact.function_id())) { + return false; + } + + livesafe_function_ids_.insert(fact.function_id()); + return true; +} + +bool LivesafeFunctionFacts::FunctionIsLivesafe(uint32_t function_id) const { + return livesafe_function_ids_.count(function_id) != 0; +} + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.h b/third_party/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.h new file mode 100644 index 0000000..2156d64 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fact_manager/livesafe_function_facts.h @@ -0,0 +1,50 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_ +#define SOURCE_FUZZ_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { +namespace fact_manager { + +// The purpose of this class is to group the fields and data used to represent +// facts about livesafe functions. +class LivesafeFunctionFacts { + public: + explicit LivesafeFunctionFacts(opt::IRContext* ir_context); + + // See method in FactManager which delegates to this method. Returns true if + // |fact.function_id()| is a result id of some non-entry-point function in + // |ir_context_|. Returns false otherwise. + bool MaybeAddFact(const protobufs::FactFunctionIsLivesafe& fact); + + // See method in FactManager which delegates to this method. + bool FunctionIsLivesafe(uint32_t function_id) const; + + private: + std::unordered_set livesafe_function_ids_; + opt::IRContext* ir_context_; +}; + +} // namespace fact_manager +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FACT_MANAGER_LIVESAFE_FUNCTION_FACTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/force_render_red.cpp b/third_party/spirv-tools/source/fuzz/force_render_red.cpp new file mode 100644 index 0000000..ed60bd0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/force_render_red.cpp @@ -0,0 +1,373 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/force_render_red.h" + +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" +#include "tools/util/cli_consumer.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +// Helper method to find the fragment shader entry point, complaining if there +// is no shader or if there is no fragment entry point. +opt::Function* FindFragmentShaderEntryPoint(opt::IRContext* ir_context, + MessageConsumer message_consumer) { + // Check that this is a fragment shader + bool found_capability_shader = false; + for (auto& capability : ir_context->capabilities()) { + assert(capability.opcode() == SpvOpCapability); + if (capability.GetSingleWordInOperand(0) == SpvCapabilityShader) { + found_capability_shader = true; + break; + } + } + if (!found_capability_shader) { + message_consumer( + SPV_MSG_ERROR, nullptr, {}, + "Forcing of red rendering requires the Shader capability."); + return nullptr; + } + + opt::Instruction* fragment_entry_point = nullptr; + for (auto& entry_point : ir_context->module()->entry_points()) { + if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelFragment) { + fragment_entry_point = &entry_point; + break; + } + } + if (fragment_entry_point == nullptr) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Forcing of red rendering requires an entry point with " + "the Fragment execution model."); + return nullptr; + } + + for (auto& function : *ir_context->module()) { + if (function.result_id() == + fragment_entry_point->GetSingleWordInOperand(1)) { + return &function; + } + } + assert( + false && + "A valid module must have a function associate with each entry point."); + return nullptr; +} + +// Helper method to check that there is a single vec4 output variable and get a +// pointer to it. +opt::Instruction* FindVec4OutputVariable(opt::IRContext* ir_context, + MessageConsumer message_consumer) { + opt::Instruction* output_variable = nullptr; + for (auto& inst : ir_context->types_values()) { + if (inst.opcode() == SpvOpVariable && + inst.GetSingleWordInOperand(0) == SpvStorageClassOutput) { + if (output_variable != nullptr) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Only one output variable can be handled at present; " + "found multiple."); + return nullptr; + } + output_variable = &inst; + // Do not break, as we want to check for multiple output variables. + } + } + if (output_variable == nullptr) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "No output variable to which to write red was found."); + return nullptr; + } + + auto output_variable_base_type = ir_context->get_type_mgr() + ->GetType(output_variable->type_id()) + ->AsPointer() + ->pointee_type() + ->AsVector(); + if (!output_variable_base_type || + output_variable_base_type->element_count() != 4 || + !output_variable_base_type->element_type()->AsFloat()) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "The output variable must have type vec4."); + return nullptr; + } + + return output_variable; +} + +// Helper to get the ids of float constants 0.0 and 1.0, creating them if +// necessary. +std::pair FindOrCreateFloatZeroAndOne( + opt::IRContext* ir_context, opt::analysis::Float* float_type) { + float one = 1.0; + uint32_t one_as_uint; + memcpy(&one_as_uint, &one, sizeof(float)); + std::vector zero_bytes = {0}; + std::vector one_bytes = {one_as_uint}; + auto constant_zero = ir_context->get_constant_mgr()->RegisterConstant( + MakeUnique(float_type, zero_bytes)); + auto constant_one = ir_context->get_constant_mgr()->RegisterConstant( + MakeUnique(float_type, one_bytes)); + auto constant_zero_id = ir_context->get_constant_mgr() + ->GetDefiningInstruction(constant_zero) + ->result_id(); + auto constant_one_id = ir_context->get_constant_mgr() + ->GetDefiningInstruction(constant_one) + ->result_id(); + return std::pair(constant_zero_id, constant_one_id); +} + +std::unique_ptr +MakeConstantUniformReplacement(opt::IRContext* ir_context, + const FactManager& fact_manager, + uint32_t constant_id, + uint32_t greater_than_instruction, + uint32_t in_operand_index) { + return MakeUnique( + MakeIdUseDescriptor(constant_id, + MakeInstructionDescriptor(greater_than_instruction, + SpvOpFOrdGreaterThan, 0), + in_operand_index), + fact_manager.GetUniformDescriptorsForConstant(constant_id)[0], + ir_context->TakeNextId(), ir_context->TakeNextId()); +} + +} // namespace + +bool ForceRenderRed( + const spv_target_env& target_env, spv_validator_options validator_options, + const std::vector& binary_in, + const spvtools::fuzz::protobufs::FactSequence& initial_facts, + std::vector* binary_out) { + auto message_consumer = spvtools::utils::CLIMessageConsumer; + spvtools::SpirvTools tools(target_env); + if (!tools.IsValid()) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return false; + } + + // Initial binary should be valid. + if (!tools.Validate(&binary_in[0], binary_in.size(), validator_options)) { + message_consumer(SPV_MSG_ERROR, nullptr, {}, + "Initial binary is invalid; stopping."); + return false; + } + + // Build the module from the input binary. + std::unique_ptr ir_context = BuildModule( + target_env, message_consumer, binary_in.data(), binary_in.size()); + assert(ir_context); + + // Set up a fact manager with any given initial facts. + TransformationContext transformation_context( + MakeUnique(ir_context.get()), validator_options); + for (auto& fact : initial_facts.fact()) { + transformation_context.GetFactManager()->MaybeAddFact(fact); + } + + auto entry_point_function = + FindFragmentShaderEntryPoint(ir_context.get(), message_consumer); + auto output_variable = + FindVec4OutputVariable(ir_context.get(), message_consumer); + if (entry_point_function == nullptr || output_variable == nullptr) { + return false; + } + + opt::analysis::Float temp_float_type(32); + opt::analysis::Float* float_type = ir_context->get_type_mgr() + ->GetRegisteredType(&temp_float_type) + ->AsFloat(); + std::pair zero_one_float_ids = + FindOrCreateFloatZeroAndOne(ir_context.get(), float_type); + + // Make the new exit block + auto new_exit_block_id = ir_context->TakeNextId(); + { + auto label = MakeUnique(ir_context.get(), SpvOpLabel, 0, + new_exit_block_id, + opt::Instruction::OperandList()); + auto new_exit_block = MakeUnique(std::move(label)); + new_exit_block->AddInstruction(MakeUnique( + ir_context.get(), SpvOpReturn, 0, 0, opt::Instruction::OperandList())); + new_exit_block->SetParent(entry_point_function); + entry_point_function->AddBasicBlock(std::move(new_exit_block)); + } + + // Make the new entry block + { + auto label = MakeUnique(ir_context.get(), SpvOpLabel, 0, + ir_context->TakeNextId(), + opt::Instruction::OperandList()); + auto new_entry_block = MakeUnique(std::move(label)); + + // Make an instruction to construct vec4(1.0, 0.0, 0.0, 1.0), representing + // the colour red. + opt::Operand zero_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.first}}; + opt::Operand one_float = {SPV_OPERAND_TYPE_ID, {zero_one_float_ids.second}}; + opt::Instruction::OperandList op_composite_construct_operands = { + one_float, zero_float, zero_float, one_float}; + auto temp_vec4 = opt::analysis::Vector(float_type, 4); + auto vec4_id = ir_context->get_type_mgr()->GetId(&temp_vec4); + auto red = MakeUnique( + ir_context.get(), SpvOpCompositeConstruct, vec4_id, + ir_context->TakeNextId(), op_composite_construct_operands); + auto red_id = red->result_id(); + new_entry_block->AddInstruction(std::move(red)); + + // Make an instruction to store red into the output color. + opt::Operand variable_to_store_into = {SPV_OPERAND_TYPE_ID, + {output_variable->result_id()}}; + opt::Operand value_to_be_stored = {SPV_OPERAND_TYPE_ID, {red_id}}; + opt::Instruction::OperandList op_store_operands = {variable_to_store_into, + value_to_be_stored}; + new_entry_block->AddInstruction(MakeUnique( + ir_context.get(), SpvOpStore, 0, 0, op_store_operands)); + + // We are going to attempt to construct 'false' as an expression of the form + // 'literal1 > literal2'. If we succeed, we will later replace each literal + // with a uniform of the same value - we can only do that replacement once + // we have added the entry block to the module. + std::unique_ptr + first_greater_then_operand_replacement = nullptr; + std::unique_ptr + second_greater_then_operand_replacement = nullptr; + uint32_t id_guaranteed_to_be_false = 0; + + opt::analysis::Bool temp_bool_type; + opt::analysis::Bool* registered_bool_type = + ir_context->get_type_mgr() + ->GetRegisteredType(&temp_bool_type) + ->AsBool(); + + auto float_type_id = ir_context->get_type_mgr()->GetId(float_type); + auto types_for_which_uniforms_are_known = + transformation_context.GetFactManager() + ->GetTypesForWhichUniformValuesAreKnown(); + + // Check whether we have any float uniforms. + if (std::find(types_for_which_uniforms_are_known.begin(), + types_for_which_uniforms_are_known.end(), + float_type_id) != types_for_which_uniforms_are_known.end()) { + // We have at least one float uniform; let's see whether we have at least + // two. + auto available_constants = + transformation_context.GetFactManager() + ->GetConstantsAvailableFromUniformsForType(float_type_id); + if (available_constants.size() > 1) { + // Grab the float constants associated with the first two known float + // uniforms. + auto first_constant = + ir_context->get_constant_mgr() + ->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef( + available_constants[0])) + ->AsFloatConstant(); + auto second_constant = + ir_context->get_constant_mgr() + ->GetConstantFromInst(ir_context->get_def_use_mgr()->GetDef( + available_constants[1])) + ->AsFloatConstant(); + + // Now work out which of the two constants is larger than the other. + uint32_t larger_constant_index = 0; + uint32_t smaller_constant_index = 0; + if (first_constant->GetFloat() > second_constant->GetFloat()) { + larger_constant_index = 0; + smaller_constant_index = 1; + } else if (first_constant->GetFloat() < second_constant->GetFloat()) { + larger_constant_index = 1; + smaller_constant_index = 0; + } + + // Only proceed with these constants if they have turned out to be + // distinct. + if (larger_constant_index != smaller_constant_index) { + // We are in a position to create 'false' as 'literal1 > literal2', so + // reserve an id for this computation; this id will end up being + // guaranteed to be 'false'. + id_guaranteed_to_be_false = ir_context->TakeNextId(); + + auto smaller_constant = available_constants[smaller_constant_index]; + auto larger_constant = available_constants[larger_constant_index]; + + opt::Instruction::OperandList greater_than_operands = { + {SPV_OPERAND_TYPE_ID, {smaller_constant}}, + {SPV_OPERAND_TYPE_ID, {larger_constant}}}; + new_entry_block->AddInstruction(MakeUnique( + ir_context.get(), SpvOpFOrdGreaterThan, + ir_context->get_type_mgr()->GetId(registered_bool_type), + id_guaranteed_to_be_false, greater_than_operands)); + + first_greater_then_operand_replacement = + MakeConstantUniformReplacement( + ir_context.get(), *transformation_context.GetFactManager(), + smaller_constant, id_guaranteed_to_be_false, 0); + second_greater_then_operand_replacement = + MakeConstantUniformReplacement( + ir_context.get(), *transformation_context.GetFactManager(), + larger_constant, id_guaranteed_to_be_false, 1); + } + } + } + + if (id_guaranteed_to_be_false == 0) { + auto constant_false = ir_context->get_constant_mgr()->RegisterConstant( + MakeUnique(registered_bool_type, false)); + id_guaranteed_to_be_false = ir_context->get_constant_mgr() + ->GetDefiningInstruction(constant_false) + ->result_id(); + } + + opt::Operand false_condition = {SPV_OPERAND_TYPE_ID, + {id_guaranteed_to_be_false}}; + opt::Operand then_block = {SPV_OPERAND_TYPE_ID, + {entry_point_function->entry()->id()}}; + opt::Operand else_block = {SPV_OPERAND_TYPE_ID, {new_exit_block_id}}; + opt::Instruction::OperandList op_branch_conditional_operands = { + false_condition, then_block, else_block}; + new_entry_block->AddInstruction( + MakeUnique(ir_context.get(), SpvOpBranchConditional, + 0, 0, op_branch_conditional_operands)); + + entry_point_function->InsertBasicBlockBefore( + std::move(new_entry_block), entry_point_function->entry().get()); + + for (auto& replacement : {first_greater_then_operand_replacement.get(), + second_greater_then_operand_replacement.get()}) { + if (replacement) { + assert(replacement->IsApplicable(ir_context.get(), + transformation_context)); + replacement->Apply(ir_context.get(), &transformation_context); + } + } + } + + // Write out the module as a binary. + ir_context->module()->ToBinary(binary_out, false); + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/force_render_red.h b/third_party/spirv-tools/source/fuzz/force_render_red.h new file mode 100644 index 0000000..b51c72b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/force_render_red.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FORCE_RENDER_RED_H_ +#define SOURCE_FORCE_RENDER_RED_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Requires |binary_in| to be a valid SPIR-V module with Shader capability, +// containing an entry point with the Fragment execution model, and a single +// output variable of type vec4. +// +// Turns the body of this entry point into effectively: +// +// output_variable = vec4(1.0, 0.0, 0.0, 1.0); +// if (false) { +// original_body +// } +// +// If suitable facts about values of uniforms are available, the 'false' will +// instead become: 'u > v', where 'u' and 'v' are pieces of uniform data for +// which it is known that 'u < v' holds. +bool ForceRenderRed( + const spv_target_env& target_env, spv_validator_options validator_options, + const std::vector& binary_in, + const spvtools::fuzz::protobufs::FactSequence& initial_facts, + std::vector* binary_out); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FORCE_RENDER_RED_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer.cpp b/third_party/spirv-tools/source/fuzz/fuzzer.cpp new file mode 100644 index 0000000..40da497 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer.cpp @@ -0,0 +1,409 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer.h" + +#include +#include +#include +#include + +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass_add_access_chains.h" +#include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_composite_extract.h" +#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" +#include "source/fuzz/fuzzer_pass_add_composite_types.h" +#include "source/fuzz/fuzzer_pass_add_copy_memory.h" +#include "source/fuzz/fuzzer_pass_add_dead_blocks.h" +#include "source/fuzz/fuzzer_pass_add_dead_breaks.h" +#include "source/fuzz/fuzzer_pass_add_dead_continues.h" +#include "source/fuzz/fuzzer_pass_add_equation_instructions.h" +#include "source/fuzz/fuzzer_pass_add_function_calls.h" +#include "source/fuzz/fuzzer_pass_add_global_variables.h" +#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h" +#include "source/fuzz/fuzzer_pass_add_loads.h" +#include "source/fuzz/fuzzer_pass_add_local_variables.h" +#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h" +#include "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" +#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_parameters.h" +#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h" +#include "source/fuzz/fuzzer_pass_add_stores.h" +#include "source/fuzz/fuzzer_pass_add_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h" +#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h" +#include "source/fuzz/fuzzer_pass_adjust_function_controls.h" +#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h" +#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h" +#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h" +#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h" +#include "source/fuzz/fuzzer_pass_construct_composites.h" +#include "source/fuzz/fuzzer_pass_copy_objects.h" +#include "source/fuzz/fuzzer_pass_donate_modules.h" +#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h" +#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h" +#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h" +#include "source/fuzz/fuzzer_pass_inline_functions.h" +#include "source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h" +#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h" +#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h" +#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h" +#include "source/fuzz/fuzzer_pass_merge_blocks.h" +#include "source/fuzz/fuzzer_pass_merge_function_returns.h" +#include "source/fuzz/fuzzer_pass_mutate_pointers.h" +#include "source/fuzz/fuzzer_pass_obfuscate_constants.h" +#include "source/fuzz/fuzzer_pass_outline_functions.h" +#include "source/fuzz/fuzzer_pass_permute_blocks.h" +#include "source/fuzz/fuzzer_pass_permute_function_parameters.h" +#include "source/fuzz/fuzzer_pass_permute_instructions.h" +#include "source/fuzz/fuzzer_pass_permute_phi_operands.h" +#include "source/fuzz/fuzzer_pass_propagate_instructions_down.h" +#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h" +#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h" +#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h" +#include "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h" +#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h" +#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h" +#include "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h" +#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h" +#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h" +#include "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h" +#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h" +#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h" +#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h" +#include "source/fuzz/fuzzer_pass_split_blocks.h" +#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" +#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" +#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" +#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" +#include "source/fuzz/pass_management/repeated_pass_manager.h" +#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h" +#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h" +#include "source/fuzz/pass_management/repeated_pass_manager_simple.h" +#include "source/fuzz/pass_management/repeated_pass_recommender_standard.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/build_module.h" +#include "source/spirv_fuzzer_options.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +namespace { +const uint32_t kIdBoundGap = 100; + +} // namespace + +Fuzzer::Fuzzer(spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const std::vector& donor_suppliers, + std::unique_ptr random_generator, + bool enable_all_passes, + RepeatedPassStrategy repeated_pass_strategy, + bool validate_after_each_fuzzer_pass, + spv_validator_options validator_options) + : target_env_(target_env), + consumer_(std::move(consumer)), + binary_in_(binary_in), + initial_facts_(initial_facts), + donor_suppliers_(donor_suppliers), + random_generator_(std::move(random_generator)), + enable_all_passes_(enable_all_passes), + repeated_pass_strategy_(repeated_pass_strategy), + validate_after_each_fuzzer_pass_(validate_after_each_fuzzer_pass), + validator_options_(validator_options), + num_repeated_passes_applied_(0), + ir_context_(nullptr), + fuzzer_context_(nullptr), + transformation_context_(nullptr), + transformation_sequence_out_() {} + +Fuzzer::~Fuzzer() = default; + +template +void Fuzzer::MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass, + RepeatedPassInstances* pass_instances, + Args&&... extra_args) { + if (enable_all_passes_ || + fuzzer_context_->ChoosePercentage(percentage_chance_of_adding_pass)) { + pass_instances->SetPass(MakeUnique( + ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), + &transformation_sequence_out_, std::forward(extra_args)...)); + } +} + +template +void Fuzzer::MaybeAddFinalPass(std::vector>* passes, + Args&&... extra_args) { + if (enable_all_passes_ || fuzzer_context_->ChooseEven()) { + passes->push_back(MakeUnique( + ir_context_.get(), transformation_context_.get(), fuzzer_context_.get(), + &transformation_sequence_out_, std::forward(extra_args)...)); + } +} + +bool Fuzzer::ApplyPassAndCheckValidity(FuzzerPass* pass) const { + pass->Apply(); + return !validate_after_each_fuzzer_pass_ || + fuzzerutil::IsValidAndWellFormed(ir_context_.get(), validator_options_, + consumer_); +} + +Fuzzer::FuzzerResult Fuzzer::Run() { + // Check compatibility between the library version being linked with and the + // header files being used. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + assert(ir_context_ == nullptr && fuzzer_context_ == nullptr && + transformation_context_ == nullptr && + transformation_sequence_out_.transformation_size() == 0 && + "'Run' must not be invoked more than once."); + + spvtools::SpirvTools tools(target_env_); + tools.SetMessageConsumer(consumer_); + if (!tools.IsValid()) { + consumer_(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return {Fuzzer::FuzzerResultStatus::kFailedToCreateSpirvToolsInterface, + std::vector(), protobufs::TransformationSequence()}; + } + + // Initial binary should be valid. + if (!tools.Validate(&binary_in_[0], binary_in_.size(), validator_options_)) { + consumer_(SPV_MSG_ERROR, nullptr, {}, + "Initial binary is invalid; stopping."); + return {Fuzzer::FuzzerResultStatus::kInitialBinaryInvalid, + std::vector(), protobufs::TransformationSequence()}; + } + + // Build the module from the input binary. + ir_context_ = + BuildModule(target_env_, consumer_, binary_in_.data(), binary_in_.size()); + assert(ir_context_); + + // The fuzzer will introduce new ids into the module. The module's id bound + // gives the smallest id that can be used for this purpose. We add an offset + // to this so that there is a sizeable gap between the ids used in the + // original module and the ids used for fuzzing, as a readability aid. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the + // case where the maximum id bound is reached. + auto minimum_fresh_id = ir_context_->module()->id_bound() + kIdBoundGap; + fuzzer_context_ = + MakeUnique(random_generator_.get(), minimum_fresh_id); + + transformation_context_ = MakeUnique( + MakeUnique(ir_context_.get()), validator_options_); + transformation_context_->GetFactManager()->AddInitialFacts(consumer_, + initial_facts_); + + RepeatedPassInstances pass_instances{}; + + // The following passes are likely to be very useful: many other passes + // introduce synonyms, irrelevant ids and constants that these passes can work + // with. We thus enable them with high probability. + MaybeAddRepeatedPass(90, &pass_instances); + MaybeAddRepeatedPass(90, &pass_instances); + MaybeAddRepeatedPass(90, &pass_instances); + + do { + // Each call to MaybeAddRepeatedPass randomly decides whether the given pass + // should be enabled, and adds an instance of the pass to |pass_instances| + // if it is enabled. + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances, + donor_suppliers_); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass(&pass_instances); + MaybeAddRepeatedPass( + &pass_instances); + MaybeAddRepeatedPass(&pass_instances); + // There is a theoretical possibility that no pass instances were created + // until now; loop again if so. + } while (pass_instances.GetPasses().empty()); + + RepeatedPassRecommenderStandard pass_recommender(&pass_instances, + fuzzer_context_.get()); + + std::unique_ptr repeated_pass_manager = nullptr; + switch (repeated_pass_strategy_) { + case RepeatedPassStrategy::kSimple: + repeated_pass_manager = MakeUnique( + fuzzer_context_.get(), &pass_instances); + break; + case RepeatedPassStrategy::kLoopedWithRecommendations: + repeated_pass_manager = + MakeUnique( + fuzzer_context_.get(), &pass_instances, &pass_recommender); + break; + case RepeatedPassStrategy::kRandomWithRecommendations: + repeated_pass_manager = + MakeUnique( + fuzzer_context_.get(), &pass_instances, &pass_recommender); + break; + } + + do { + if (!ApplyPassAndCheckValidity( + repeated_pass_manager->ChoosePass(transformation_sequence_out_))) { + return {Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule, + std::vector(), protobufs::TransformationSequence()}; + } + } while (ShouldContinueFuzzing()); + + // Now apply some passes that it does not make sense to apply repeatedly, + // as they do not unlock other passes. + std::vector> final_passes; + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass( + &final_passes); + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass(&final_passes); + MaybeAddFinalPass(&final_passes); + for (auto& pass : final_passes) { + if (!ApplyPassAndCheckValidity(pass.get())) { + return {Fuzzer::FuzzerResultStatus::kFuzzerPassLedToInvalidModule, + std::vector(), protobufs::TransformationSequence()}; + } + } + // Encode the module as a binary. + std::vector binary_out; + ir_context_->module()->ToBinary(&binary_out, false); + + return {Fuzzer::FuzzerResultStatus::kComplete, std::move(binary_out), + std::move(transformation_sequence_out_)}; +} + +bool Fuzzer::ShouldContinueFuzzing() { + // There's a risk that fuzzing could get stuck, if none of the enabled fuzzer + // passes are able to apply any transformations. To guard against this we + // count the number of times some repeated pass has been applied and ensure + // that fuzzing stops if the number of repeated passes hits the limit on the + // number of transformations that can be applied. + assert( + num_repeated_passes_applied_ <= + fuzzer_context_->GetTransformationLimit() && + "The number of repeated passes applied must not exceed its upper limit."); + if (ir_context_->module()->id_bound() >= fuzzer_context_->GetIdBoundLimit()) { + return false; + } + if (num_repeated_passes_applied_ == + fuzzer_context_->GetTransformationLimit()) { + // Stop because fuzzing has got stuck. + return false; + } + auto transformations_applied_so_far = + static_cast(transformation_sequence_out_.transformation_size()); + if (transformations_applied_so_far >= + fuzzer_context_->GetTransformationLimit()) { + // Stop because we have reached the transformation limit. + return false; + } + // If we have applied T transformations so far, and the limit on the number of + // transformations to apply is L (where T < L), the chance that we will + // continue fuzzing is: + // + // 1 - T/(2*L) + // + // That is, the chance of continuing decreases as more transformations are + // applied. Using 2*L instead of L increases the number of transformations + // that are applied on average. + auto chance_of_continuing = static_cast( + 100.0 * (1.0 - (static_cast(transformations_applied_so_far) / + (2.0 * static_cast( + fuzzer_context_->GetTransformationLimit()))))); + if (!fuzzer_context_->ChoosePercentage(chance_of_continuing)) { + // We have probabilistically decided to stop. + return false; + } + // Continue fuzzing! + num_repeated_passes_applied_++; + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer.h b/third_party/spirv-tools/source/fuzz/fuzzer.h new file mode 100644 index 0000000..774457f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer.h @@ -0,0 +1,187 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_H_ +#define SOURCE_FUZZ_FUZZER_H_ + +#include +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pass_management/repeated_pass_instances.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/random_generator.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Transforms a SPIR-V module into a semantically equivalent SPIR-V module by +// running a number of randomized fuzzer passes. +class Fuzzer { + public: + // Possible statuses that can result from running the fuzzer. + enum class FuzzerResultStatus { + kComplete, + kFailedToCreateSpirvToolsInterface, + kFuzzerPassLedToInvalidModule, + kInitialBinaryInvalid, + }; + + struct FuzzerResult { + FuzzerResultStatus status; + std::vector transformed_binary; + protobufs::TransformationSequence applied_transformations; + }; + + // Each field of this enum corresponds to an available repeated pass + // strategy, and is used to decide which kind of RepeatedPassManager object + // to create. + enum class RepeatedPassStrategy { + kSimple, + kRandomWithRecommendations, + kLoopedWithRecommendations + }; + + Fuzzer(spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const std::vector& donor_suppliers, + std::unique_ptr random_generator, + bool enable_all_passes, RepeatedPassStrategy repeated_pass_strategy, + bool validate_after_each_fuzzer_pass, + spv_validator_options validator_options); + + // Disables copy/move constructor/assignment operations. + Fuzzer(const Fuzzer&) = delete; + Fuzzer(Fuzzer&&) = delete; + Fuzzer& operator=(const Fuzzer&) = delete; + Fuzzer& operator=(Fuzzer&&) = delete; + + ~Fuzzer(); + + // Transforms |binary_in_| by running a number of randomized fuzzer passes. + // Initial facts about the input binary and the context in which it will + // execute are provided via |initial_facts_|. A source of donor modules to be + // used by transformations is provided via |donor_suppliers_|. On success, + // returns a successful result status together with the transformed binary and + // the sequence of transformations that were applied. Otherwise, returns an + // appropriate result status together with an empty binary and empty + // transformation sequence. + FuzzerResult Run(); + + private: + // A convenience method to add a repeated fuzzer pass to |pass_instances| with + // probability |percentage_chance_of_adding_pass|%, or with probability 100% + // if |enable_all_passes_| is true. + // + // All fuzzer passes take members |ir_context_|, |transformation_context_|, + // |fuzzer_context_| and |transformation_sequence_out_| as parameters. Extra + // arguments can be provided via |extra_args|. + template + void MaybeAddRepeatedPass(uint32_t percentage_chance_of_adding_pass, + RepeatedPassInstances* pass_instances, + Args&&... extra_args); + + // The same as the above, with |percentage_chance_of_adding_pass| == 50%. + template + void MaybeAddRepeatedPass(RepeatedPassInstances* pass_instances, + Args&&... extra_args) { + MaybeAddRepeatedPass(50, pass_instances, + std::forward(extra_args)...); + } + + // A convenience method to add a final fuzzer pass to |passes| with + // probability 50%, or with probability 100% if |enable_all_passes_| is true. + // + // All fuzzer passes take members |ir_context_|, |transformation_context_|, + // |fuzzer_context_| and |transformation_sequence_out_| as parameters. Extra + // arguments can be provided via |extra_args|. + template + void MaybeAddFinalPass(std::vector>* passes, + Args&&... extra_args); + + // Decides whether to apply more repeated passes. The probability decreases as + // the number of transformations that have been applied increases. + bool ShouldContinueFuzzing(); + + // Applies |pass|, which must be a pass constructed with |ir_context|. + // If |validate_after_each_fuzzer_pass_| is not set, true is always returned. + // Otherwise, true is returned if and only if |ir_context| passes validation, + // every block has its enclosing function as its parent, and every + // instruction has a distinct unique id. + bool ApplyPassAndCheckValidity(FuzzerPass* pass) const; + + // Target environment. + const spv_target_env target_env_; + + // Message consumer that will be invoked once for each message communicated + // from the library. + MessageConsumer consumer_; + + // The initial binary to which fuzzing should be applied. + const std::vector& binary_in_; + + // Initial facts known to hold in advance of applying any transformations. + const protobufs::FactSequence& initial_facts_; + + // A source of modules whose contents can be donated into the module being + // fuzzed. + const std::vector& donor_suppliers_; + + // Random number generator to control decision making during fuzzing. + std::unique_ptr random_generator_; + + // Determines whether all passes should be enabled, vs. having passes be + // probabilistically enabled. + bool enable_all_passes_; + + // Controls which type of RepeatedPassManager object to create. + RepeatedPassStrategy repeated_pass_strategy_; + + // Determines whether the validator should be invoked after every fuzzer pass. + bool validate_after_each_fuzzer_pass_; + + // Options to control validation. + spv_validator_options validator_options_; + + // The number of repeated fuzzer passes that have been applied is kept track + // of, in order to enforce a hard limit on the number of times such passes + // can be applied. + uint32_t num_repeated_passes_applied_; + + // Intermediate representation for the module being fuzzed, which gets + // mutated as fuzzing proceeds. + std::unique_ptr ir_context_; + + // Provides probabilities that control the fuzzing process. + std::unique_ptr fuzzer_context_; + + // Contextual information that is required in order to apply transformations. + std::unique_ptr transformation_context_; + + // The sequence of transformations that have been applied during fuzzing. It + // is initially empty and grows as fuzzer passes are applied. + protobufs::TransformationSequence transformation_sequence_out_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_context.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_context.cpp new file mode 100644 index 0000000..47bf4e2 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_context.cpp @@ -0,0 +1,407 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_context.h" + +#include + +namespace spvtools { +namespace fuzz { + +namespace { + +// Limits to help control the overall fuzzing process and rein in individual +// fuzzer passes. +const uint32_t kIdBoundLimit = 50000; +const uint32_t kTransformationLimit = 2000; + +// Default pairs of probabilities for applying various +// transformations. All values are percentages. Keep them in alphabetical order. +const std::pair + kChanceOfAcceptingRepeatedPassRecommendation = {50, 80}; +const std::pair kChanceOfAddingAccessChain = {5, 50}; +const std::pair kChanceOfAddingAnotherPassToPassLoop = {50, + 90}; +const std::pair kChanceOfAddingAnotherStructField = {20, + 90}; +const std::pair kChanceOfAddingArrayOrStructType = {20, 90}; +const std::pair kChanceOfAddingBitInstructionSynonym = {5, + 20}; +const std::pair + kChanceOfAddingBothBranchesWhenReplacingOpSelect = {40, 60}; +const std::pair kChanceOfAddingCompositeExtract = {20, 50}; +const std::pair kChanceOfAddingCompositeInsert = {20, 50}; +const std::pair kChanceOfAddingCopyMemory = {20, 50}; +const std::pair kChanceOfAddingDeadBlock = {20, 90}; +const std::pair kChanceOfAddingDeadBreak = {5, 80}; +const std::pair kChanceOfAddingDeadContinue = {5, 80}; +const std::pair kChanceOfAddingEquationInstruction = {5, + 90}; +const std::pair kChanceOfAddingGlobalVariable = {20, 90}; +const std::pair kChanceOfAddingImageSampleUnusedComponents = + {20, 90}; +const std::pair kChanceOfAddingLoad = {5, 50}; +const std::pair kChanceOfAddingLocalVariable = {20, 90}; +const std::pair kChanceOfAddingLoopPreheader = {20, 90}; +const std::pair kChanceOfAddingMatrixType = {20, 70}; +const std::pair kChanceOfAddingNoContractionDecoration = { + 5, 70}; +const std::pair kChanceOfAddingOpPhiSynonym = {5, 70}; +const std::pair kChanceOfAddingParameters = {5, 70}; +const std::pair kChanceOfAddingRelaxedDecoration = {20, 90}; +const std::pair kChanceOfAddingStore = {5, 50}; +const std::pair kChanceOfAddingSynonyms = {20, 50}; +const std::pair + kChanceOfAddingTrueBranchWhenReplacingOpSelect = {40, 60}; +const std::pair kChanceOfAddingVectorType = {20, 70}; +const std::pair kChanceOfAddingVectorShuffle = {20, 70}; +const std::pair kChanceOfAdjustingBranchWeights = {20, 90}; +const std::pair kChanceOfAdjustingFunctionControl = {20, + 70}; +const std::pair kChanceOfAdjustingLoopControl = {20, 90}; +const std::pair kChanceOfAdjustingMemoryOperandsMask = {20, + 90}; +const std::pair kChanceOfAdjustingSelectionControl = {20, + 90}; +const std::pair kChanceOfCallingFunction = {1, 10}; +const std::pair kChanceOfChoosingStructTypeVsArrayType = { + 20, 80}; +const std::pair kChanceOfChoosingWorkgroupStorageClass = { + 50, 50}; +const std::pair kChanceOfConstructingComposite = {20, 50}; +const std::pair kChanceOfCopyingObject = {20, 50}; +const std::pair kChanceOfCreatingIntSynonymsUsingLoops = { + 5, 10}; +const std::pair kChanceOfDonatingAdditionalModule = {5, 50}; +const std::pair kChanceOfDuplicatingRegionWithSelection = { + 20, 50}; +const std::pair kChanceOfExpandingVectorReduction = {20, + 90}; +const std::pair kChanceOfFlatteningConditionalBranch = {45, + 95}; +const std::pair kChanceOfGoingDeeperToExtractComposite = { + 30, 70}; +const std::pair kChanceOfGoingDeeperToInsertInComposite = { + 30, 70}; +const std::pair kChanceOfGoingDeeperWhenMakingAccessChain = + {50, 95}; +const std::pair + kChanceOfHavingTwoBlocksInLoopToCreateIntSynonym = {50, 80}; +const std::pair kChanceOfInliningFunction = {10, 90}; +const std::pair kChanceOfInterchangingZeroLikeConstants = { + 10, 90}; +const std::pair + kChanceOfInterchangingSignednessOfIntegerOperands = {10, 90}; +const std::pair kChanceOfInvertingComparisonOperators = { + 20, 50}; +const std::pair kChanceOfMakingDonorLivesafe = {40, 60}; +const std::pair kChanceOfMakingVectorOperationDynamic = { + 20, 90}; +const std::pair kChanceOfMergingBlocks = {20, 95}; +const std::pair kChanceOfMergingFunctionReturns = {20, 90}; +const std::pair kChanceOfMovingBlockDown = {20, 50}; +const std::pair kChanceOfMutatingPointer = {20, 90}; +const std::pair kChanceOfObfuscatingConstant = {10, 90}; +const std::pair kChanceOfOutliningFunction = {10, 90}; +const std::pair kChanceOfPermutingInstructions = {20, 70}; +const std::pair kChanceOfPermutingParameters = {30, 90}; +const std::pair kChanceOfPermutingPhiOperands = {30, 90}; +const std::pair kChanceOfPropagatingInstructionsDown = {20, + 70}; +const std::pair kChanceOfPropagatingInstructionsUp = {20, + 70}; +const std::pair kChanceOfPushingIdThroughVariable = {5, 50}; +const std::pair + kChanceOfReplacingAddSubMulWithCarryingExtended = {20, 70}; +const std::pair + kChanceOfReplacingBranchFromDeadBlockWithExit = {10, 65}; +const std::pair kChanceOfReplacingCopyMemoryWithLoadStore = + {20, 90}; +const std::pair kChanceOfReplacingCopyObjectWithStoreLoad = + {20, 90}; +const std::pair kChanceOfReplacingIdWithSynonym = {10, 90}; +const std::pair kChanceOfReplacingIrrelevantId = {35, 95}; +const std::pair + kChanceOfReplacingLinearAlgebraInstructions = {10, 90}; +const std::pair kChanceOfReplacingLoadStoreWithCopyMemory = + {20, 90}; +const std::pair + kChanceOfReplacingOpPhiIdFromDeadPredecessor = {20, 90}; +const std::pair + kChanceOfReplacingOpSelectWithConditionalBranch = {20, 90}; +const std::pair kChanceOfReplacingParametersWithGlobals = { + 30, 70}; +const std::pair kChanceOfReplacingParametersWithStruct = { + 20, 40}; +const std::pair kChanceOfSplittingBlock = {40, 95}; +const std::pair kChanceOfSwappingConditionalBranchOperands = + {10, 70}; +const std::pair kChanceOfTogglingAccessChainInstruction = { + 20, 90}; +const std::pair kChanceOfWrappingRegionInSelection = {70, + 90}; + +// Default limits for various quantities that are chosen during fuzzing. +// Keep them in alphabetical order. +const uint32_t kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure = 1000; +const uint32_t kDefaultMaxLoopControlPartialCount = 100; +const uint32_t kDefaultMaxLoopControlPeelCount = 100; +const uint32_t kDefaultMaxLoopLimit = 20; +const uint32_t kDefaultMaxNewArraySizeLimit = 100; +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3424): +// think whether there is a better limit on the maximum number of parameters. +const uint32_t kDefaultMaxNumberOfFunctionParameters = 128; +const uint32_t kDefaultMaxNumberOfNewParameters = 15; +const uint32_t kGetDefaultMaxNumberOfParametersReplacedWithStruct = 5; + +// Default functions for controlling how deep to go during recursive +// generation/transformation. Keep them in alphabetical order. + +const std::function + kDefaultGoDeeperInConstantObfuscation = + [](uint32_t current_depth, RandomGenerator* random_generator) -> bool { + double chance = 1.0 / std::pow(3.0, static_cast(current_depth + 1)); + return random_generator->RandomDouble() < chance; +}; + +} // namespace + +FuzzerContext::FuzzerContext(RandomGenerator* random_generator, + uint32_t min_fresh_id) + : random_generator_(random_generator), + next_fresh_id_(min_fresh_id), + max_equivalence_class_size_for_data_synonym_fact_closure_( + kDefaultMaxEquivalenceClassSizeForDataSynonymFactClosure), + max_loop_control_partial_count_(kDefaultMaxLoopControlPartialCount), + max_loop_control_peel_count_(kDefaultMaxLoopControlPeelCount), + max_loop_limit_(kDefaultMaxLoopLimit), + max_new_array_size_limit_(kDefaultMaxNewArraySizeLimit), + max_number_of_function_parameters_(kDefaultMaxNumberOfFunctionParameters), + max_number_of_new_parameters_(kDefaultMaxNumberOfNewParameters), + max_number_of_parameters_replaced_with_struct_( + kGetDefaultMaxNumberOfParametersReplacedWithStruct), + go_deeper_in_constant_obfuscation_( + kDefaultGoDeeperInConstantObfuscation) { + chance_of_accepting_repeated_pass_recommendation_ = + ChooseBetweenMinAndMax(kChanceOfAcceptingRepeatedPassRecommendation); + chance_of_adding_access_chain_ = + ChooseBetweenMinAndMax(kChanceOfAddingAccessChain); + chance_of_adding_another_pass_to_pass_loop_ = + ChooseBetweenMinAndMax(kChanceOfAddingAnotherPassToPassLoop); + chance_of_adding_another_struct_field_ = + ChooseBetweenMinAndMax(kChanceOfAddingAnotherStructField); + chance_of_adding_array_or_struct_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingArrayOrStructType); + chance_of_adding_bit_instruction_synonym_ = + ChooseBetweenMinAndMax(kChanceOfAddingBitInstructionSynonym); + chance_of_adding_both_branches_when_replacing_opselect_ = + ChooseBetweenMinAndMax(kChanceOfAddingBothBranchesWhenReplacingOpSelect); + chance_of_adding_composite_extract_ = + ChooseBetweenMinAndMax(kChanceOfAddingCompositeExtract); + chance_of_adding_composite_insert_ = + ChooseBetweenMinAndMax(kChanceOfAddingCompositeInsert); + chance_of_adding_copy_memory_ = + ChooseBetweenMinAndMax(kChanceOfAddingCopyMemory); + chance_of_adding_dead_block_ = + ChooseBetweenMinAndMax(kChanceOfAddingDeadBlock); + chance_of_adding_dead_break_ = + ChooseBetweenMinAndMax(kChanceOfAddingDeadBreak); + chance_of_adding_dead_continue_ = + ChooseBetweenMinAndMax(kChanceOfAddingDeadContinue); + chance_of_adding_equation_instruction_ = + ChooseBetweenMinAndMax(kChanceOfAddingEquationInstruction); + chance_of_adding_global_variable_ = + ChooseBetweenMinAndMax(kChanceOfAddingGlobalVariable); + chance_of_adding_load_ = ChooseBetweenMinAndMax(kChanceOfAddingLoad); + chance_of_adding_loop_preheader_ = + ChooseBetweenMinAndMax(kChanceOfAddingLoopPreheader); + chance_of_adding_image_sample_unused_components_ = + ChooseBetweenMinAndMax(kChanceOfAddingImageSampleUnusedComponents); + chance_of_adding_local_variable_ = + ChooseBetweenMinAndMax(kChanceOfAddingLocalVariable); + chance_of_adding_matrix_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingMatrixType); + chance_of_adding_no_contraction_decoration_ = + ChooseBetweenMinAndMax(kChanceOfAddingNoContractionDecoration); + chance_of_adding_opphi_synonym_ = + ChooseBetweenMinAndMax(kChanceOfAddingOpPhiSynonym); + chance_of_adding_parameters = + ChooseBetweenMinAndMax(kChanceOfAddingParameters); + chance_of_adding_relaxed_decoration_ = + ChooseBetweenMinAndMax(kChanceOfAddingRelaxedDecoration); + chance_of_adding_store_ = ChooseBetweenMinAndMax(kChanceOfAddingStore); + chance_of_adding_true_branch_when_replacing_opselect_ = + ChooseBetweenMinAndMax(kChanceOfAddingTrueBranchWhenReplacingOpSelect); + chance_of_adding_vector_shuffle_ = + ChooseBetweenMinAndMax(kChanceOfAddingVectorShuffle); + chance_of_adding_vector_type_ = + ChooseBetweenMinAndMax(kChanceOfAddingVectorType); + chance_of_adjusting_branch_weights_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingBranchWeights); + chance_of_adjusting_function_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingFunctionControl); + chance_of_adding_synonyms_ = ChooseBetweenMinAndMax(kChanceOfAddingSynonyms); + chance_of_adjusting_loop_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingLoopControl); + chance_of_adjusting_memory_operands_mask_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingMemoryOperandsMask); + chance_of_adjusting_selection_control_ = + ChooseBetweenMinAndMax(kChanceOfAdjustingSelectionControl); + chance_of_calling_function_ = + ChooseBetweenMinAndMax(kChanceOfCallingFunction); + chance_of_choosing_struct_type_vs_array_type_ = + ChooseBetweenMinAndMax(kChanceOfChoosingStructTypeVsArrayType); + chance_of_choosing_workgroup_storage_class_ = + ChooseBetweenMinAndMax(kChanceOfChoosingWorkgroupStorageClass); + chance_of_constructing_composite_ = + ChooseBetweenMinAndMax(kChanceOfConstructingComposite); + chance_of_copying_object_ = ChooseBetweenMinAndMax(kChanceOfCopyingObject); + chance_of_creating_int_synonyms_using_loops_ = + ChooseBetweenMinAndMax(kChanceOfCreatingIntSynonymsUsingLoops); + chance_of_donating_additional_module_ = + ChooseBetweenMinAndMax(kChanceOfDonatingAdditionalModule); + chance_of_duplicating_region_with_selection_ = + ChooseBetweenMinAndMax(kChanceOfDuplicatingRegionWithSelection); + chance_of_expanding_vector_reduction_ = + ChooseBetweenMinAndMax(kChanceOfExpandingVectorReduction); + chance_of_flattening_conditional_branch_ = + ChooseBetweenMinAndMax(kChanceOfFlatteningConditionalBranch); + chance_of_going_deeper_to_extract_composite_ = + ChooseBetweenMinAndMax(kChanceOfGoingDeeperToExtractComposite); + chance_of_going_deeper_to_insert_in_composite_ = + ChooseBetweenMinAndMax(kChanceOfGoingDeeperToInsertInComposite); + chance_of_going_deeper_when_making_access_chain_ = + ChooseBetweenMinAndMax(kChanceOfGoingDeeperWhenMakingAccessChain); + chance_of_having_two_blocks_in_loop_to_create_int_synonym_ = + ChooseBetweenMinAndMax(kChanceOfHavingTwoBlocksInLoopToCreateIntSynonym); + chance_of_inlining_function_ = + ChooseBetweenMinAndMax(kChanceOfInliningFunction); + chance_of_interchanging_signedness_of_integer_operands_ = + ChooseBetweenMinAndMax(kChanceOfInterchangingSignednessOfIntegerOperands); + chance_of_interchanging_zero_like_constants_ = + ChooseBetweenMinAndMax(kChanceOfInterchangingZeroLikeConstants); + chance_of_inverting_comparison_operators_ = + ChooseBetweenMinAndMax(kChanceOfInvertingComparisonOperators); + chance_of_making_donor_livesafe_ = + ChooseBetweenMinAndMax(kChanceOfMakingDonorLivesafe); + chance_of_making_vector_operation_dynamic_ = + ChooseBetweenMinAndMax(kChanceOfMakingVectorOperationDynamic); + chance_of_merging_blocks_ = ChooseBetweenMinAndMax(kChanceOfMergingBlocks); + chance_of_merging_function_returns_ = + ChooseBetweenMinAndMax(kChanceOfMergingFunctionReturns); + chance_of_moving_block_down_ = + ChooseBetweenMinAndMax(kChanceOfMovingBlockDown); + chance_of_mutating_pointer_ = + ChooseBetweenMinAndMax(kChanceOfMutatingPointer); + chance_of_obfuscating_constant_ = + ChooseBetweenMinAndMax(kChanceOfObfuscatingConstant); + chance_of_outlining_function_ = + ChooseBetweenMinAndMax(kChanceOfOutliningFunction); + chance_of_permuting_instructions_ = + ChooseBetweenMinAndMax(kChanceOfPermutingInstructions); + chance_of_permuting_parameters_ = + ChooseBetweenMinAndMax(kChanceOfPermutingParameters); + chance_of_permuting_phi_operands_ = + ChooseBetweenMinAndMax(kChanceOfPermutingPhiOperands); + chance_of_propagating_instructions_down_ = + ChooseBetweenMinAndMax(kChanceOfPropagatingInstructionsDown); + chance_of_propagating_instructions_up_ = + ChooseBetweenMinAndMax(kChanceOfPropagatingInstructionsUp); + chance_of_pushing_id_through_variable_ = + ChooseBetweenMinAndMax(kChanceOfPushingIdThroughVariable); + chance_of_replacing_add_sub_mul_with_carrying_extended_ = + ChooseBetweenMinAndMax(kChanceOfReplacingAddSubMulWithCarryingExtended); + chance_of_replacing_branch_from_dead_block_with_exit_ = + ChooseBetweenMinAndMax(kChanceOfReplacingBranchFromDeadBlockWithExit); + chance_of_replacing_copy_memory_with_load_store_ = + ChooseBetweenMinAndMax(kChanceOfReplacingCopyMemoryWithLoadStore); + chance_of_replacing_copyobject_with_store_load_ = + ChooseBetweenMinAndMax(kChanceOfReplacingCopyObjectWithStoreLoad); + chance_of_replacing_id_with_synonym_ = + ChooseBetweenMinAndMax(kChanceOfReplacingIdWithSynonym); + chance_of_replacing_irrelevant_id_ = + ChooseBetweenMinAndMax(kChanceOfReplacingIrrelevantId); + chance_of_replacing_linear_algebra_instructions_ = + ChooseBetweenMinAndMax(kChanceOfReplacingLinearAlgebraInstructions); + chance_of_replacing_load_store_with_copy_memory_ = + ChooseBetweenMinAndMax(kChanceOfReplacingLoadStoreWithCopyMemory); + chance_of_replacing_opphi_id_from_dead_predecessor_ = + ChooseBetweenMinAndMax(kChanceOfReplacingOpPhiIdFromDeadPredecessor); + chance_of_replacing_opselect_with_conditional_branch_ = + ChooseBetweenMinAndMax(kChanceOfReplacingOpSelectWithConditionalBranch); + chance_of_replacing_parameters_with_globals_ = + ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithGlobals); + chance_of_replacing_parameters_with_struct_ = + ChooseBetweenMinAndMax(kChanceOfReplacingParametersWithStruct); + chance_of_splitting_block_ = ChooseBetweenMinAndMax(kChanceOfSplittingBlock); + chance_of_swapping_conditional_branch_operands_ = + ChooseBetweenMinAndMax(kChanceOfSwappingConditionalBranchOperands); + chance_of_toggling_access_chain_instruction_ = + ChooseBetweenMinAndMax(kChanceOfTogglingAccessChainInstruction); + chance_of_wrapping_region_in_selection_ = + ChooseBetweenMinAndMax(kChanceOfWrappingRegionInSelection); +} + +FuzzerContext::~FuzzerContext() = default; + +uint32_t FuzzerContext::GetFreshId() { return next_fresh_id_++; } + +std::vector FuzzerContext::GetFreshIds(const uint32_t count) { + std::vector fresh_ids(count); + + for (uint32_t& fresh_id : fresh_ids) { + fresh_id = next_fresh_id_++; + } + + return fresh_ids; +} + +bool FuzzerContext::ChooseEven() { return random_generator_->RandomBool(); } + +bool FuzzerContext::ChoosePercentage(uint32_t percentage_chance) { + assert(percentage_chance <= 100); + return random_generator_->RandomPercentage() < percentage_chance; +} + +uint32_t FuzzerContext::ChooseBetweenMinAndMax( + const std::pair& min_max) { + assert(min_max.first <= min_max.second); + return min_max.first + + random_generator_->RandomUint32(min_max.second - min_max.first + 1); +} + +protobufs::TransformationAddSynonym::SynonymType +FuzzerContext::GetRandomSynonymType() { + // value_count method is guaranteed to return a value greater than 0. + auto result_index = ChooseBetweenMinAndMax( + {0, static_cast( + protobufs::TransformationAddSynonym::SynonymType_descriptor() + ->value_count() - + 1)}); + auto result = protobufs::TransformationAddSynonym::SynonymType_descriptor() + ->value(result_index) + ->number(); + assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(result) && + "|result| is not a value of SynonymType"); + return static_cast(result); +} + +uint32_t FuzzerContext::GetIdBoundLimit() const { return kIdBoundLimit; } + +uint32_t FuzzerContext::GetTransformationLimit() const { + return kTransformationLimit; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_context.h b/third_party/spirv-tools/source/fuzz/fuzzer_context.h new file mode 100644 index 0000000..8c51041 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_context.h @@ -0,0 +1,564 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_CONTEXT_H_ +#define SOURCE_FUZZ_FUZZER_CONTEXT_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/random_generator.h" +#include "source/opt/function.h" + +namespace spvtools { +namespace fuzz { + +// Encapsulates all parameters that control the fuzzing process, such as the +// source of randomness and the probabilities with which transformations are +// applied. +class FuzzerContext { + public: + // Constructs a fuzzer context with a given random generator and the minimum + // value that can be used for fresh ids. + FuzzerContext(RandomGenerator* random_generator, uint32_t min_fresh_id); + + ~FuzzerContext(); + + // Returns a random boolean. + bool ChooseEven(); + + // Returns true if and only if a randomly-chosen integer in the range [0, 100] + // is less than |percentage_chance|. + bool ChoosePercentage(uint32_t percentage_chance); + + // Returns a random index into |sequence|, which is expected to have a 'size' + // method, and which must be non-empty. Typically 'HasSizeMethod' will be an + // std::vector. + template + uint32_t RandomIndex(const HasSizeMethod& sequence) const { + assert(sequence.size() > 0); + return random_generator_->RandomUint32( + static_cast(sequence.size())); + } + + // Selects a random index into |sequence|, removes the element at that index + // and returns it. + template + T RemoveAtRandomIndex(std::vector* sequence) const { + uint32_t index = RandomIndex(*sequence); + T result = sequence->at(index); + sequence->erase(sequence->begin() + index); + return result; + } + + // Randomly shuffles a |sequence| between |lo| and |hi| indices inclusively. + // |lo| and |hi| must be valid indices to the |sequence| + template + void Shuffle(std::vector* sequence, size_t lo, size_t hi) const { + auto& array = *sequence; + + if (array.empty()) { + return; + } + + assert(lo <= hi && hi < array.size() && "lo and/or hi indices are invalid"); + + // i > lo to account for potential infinite loop when lo == 0 + for (size_t i = hi; i > lo; --i) { + auto index = + random_generator_->RandomUint32(static_cast(i - lo + 1)); + + if (lo + index != i) { + // Introduce std::swap to the scope but don't use it + // directly since there might be a better overload + using std::swap; + swap(array[lo + index], array[i]); + } + } + } + + // Ramdomly shuffles a |sequence| + template + void Shuffle(std::vector* sequence) const { + if (!sequence->empty()) { + Shuffle(sequence, 0, sequence->size() - 1); + } + } + + // Yields an id that is guaranteed not to be used in the module being fuzzed, + // or to have been issued before. + uint32_t GetFreshId(); + + // Returns a vector of |count| fresh ids. + std::vector GetFreshIds(const uint32_t count); + + // A suggested limit on the id bound for the module being fuzzed. This is + // useful for deciding when to stop the overall fuzzing process. Furthermore, + // fuzzer passes that run the risk of spiralling out of control can + // periodically check this limit and terminate early if it has been reached. + uint32_t GetIdBoundLimit() const; + + // A suggested limit on the number of transformations that should be applied. + // Also useful to control the overall fuzzing process and rein in individual + // fuzzer passes. + uint32_t GetTransformationLimit() const; + + // Probabilities associated with applying various transformations. + // Keep them in alphabetical order. + uint32_t GetChanceOfAcceptingRepeatedPassRecommendation() const { + return chance_of_accepting_repeated_pass_recommendation_; + } + uint32_t GetChanceOfAddingAccessChain() const { + return chance_of_adding_access_chain_; + } + uint32_t GetChanceOfAddingAnotherPassToPassLoop() const { + return chance_of_adding_another_pass_to_pass_loop_; + } + uint32_t GetChanceOfAddingAnotherStructField() const { + return chance_of_adding_another_struct_field_; + } + uint32_t GetChanceOfAddingArrayOrStructType() const { + return chance_of_adding_array_or_struct_type_; + } + uint32_t GetChanceOfAddingBitInstructionSynonym() const { + return chance_of_adding_bit_instruction_synonym_; + } + uint32_t GetChanceOfAddingBothBranchesWhenReplacingOpSelect() const { + return chance_of_adding_both_branches_when_replacing_opselect_; + } + uint32_t GetChanceOfAddingCompositeExtract() const { + return chance_of_adding_composite_extract_; + } + uint32_t GetChanceOfAddingCompositeInsert() const { + return chance_of_adding_composite_insert_; + } + uint32_t GetChanceOfAddingCopyMemory() const { + return chance_of_adding_copy_memory_; + } + uint32_t GetChanceOfAddingDeadBlock() const { + return chance_of_adding_dead_block_; + } + uint32_t GetChanceOfAddingDeadBreak() const { + return chance_of_adding_dead_break_; + } + uint32_t GetChanceOfAddingDeadContinue() const { + return chance_of_adding_dead_continue_; + } + uint32_t GetChanceOfAddingEquationInstruction() const { + return chance_of_adding_equation_instruction_; + } + uint32_t GetChanceOfAddingGlobalVariable() const { + return chance_of_adding_global_variable_; + } + uint32_t GetChanceOfAddingImageSampleUnusedComponents() const { + return chance_of_adding_image_sample_unused_components_; + } + uint32_t GetChanceOfAddingLoad() const { return chance_of_adding_load_; } + uint32_t GetChanceOfAddingLocalVariable() const { + return chance_of_adding_local_variable_; + } + uint32_t GetChanceOfAddingLoopPreheader() const { + return chance_of_adding_loop_preheader_; + } + uint32_t GetChanceOfAddingMatrixType() const { + return chance_of_adding_matrix_type_; + } + uint32_t GetChanceOfAddingNoContractionDecoration() const { + return chance_of_adding_no_contraction_decoration_; + } + uint32_t GetChanceOfAddingOpPhiSynonym() const { + return chance_of_adding_opphi_synonym_; + } + uint32_t GetChanceOfAddingParameters() const { + return chance_of_adding_parameters; + } + uint32_t GetChanceOfAddingRelaxedDecoration() const { + return chance_of_adding_relaxed_decoration_; + } + uint32_t GetChanceOfAddingStore() const { return chance_of_adding_store_; } + uint32_t GetChanceOfAddingSynonyms() const { + return chance_of_adding_synonyms_; + } + uint32_t GetChanceOfAddingTrueBranchWhenReplacingOpSelect() const { + return chance_of_adding_true_branch_when_replacing_opselect_; + } + uint32_t GetChanceOfAddingVectorShuffle() const { + return chance_of_adding_vector_shuffle_; + } + uint32_t GetChanceOfAddingVectorType() const { + return chance_of_adding_vector_type_; + } + uint32_t GetChanceOfAdjustingBranchWeights() const { + return chance_of_adjusting_branch_weights_; + } + uint32_t GetChanceOfAdjustingFunctionControl() const { + return chance_of_adjusting_function_control_; + } + uint32_t GetChanceOfAdjustingLoopControl() const { + return chance_of_adjusting_loop_control_; + } + uint32_t GetChanceOfAdjustingMemoryOperandsMask() const { + return chance_of_adjusting_memory_operands_mask_; + } + uint32_t GetChanceOfAdjustingSelectionControl() const { + return chance_of_adjusting_selection_control_; + } + uint32_t GetChanceOfCallingFunction() const { + return chance_of_calling_function_; + } + uint32_t GetChanceOfChoosingStructTypeVsArrayType() const { + return chance_of_choosing_struct_type_vs_array_type_; + } + uint32_t GetChanceOfChoosingWorkgroupStorageClass() const { + return chance_of_choosing_workgroup_storage_class_; + } + uint32_t GetChanceOfConstructingComposite() const { + return chance_of_constructing_composite_; + } + uint32_t GetChanceOfCopyingObject() const { + return chance_of_copying_object_; + } + uint32_t GetChanceOfCreatingIntSynonymsUsingLoops() const { + return chance_of_creating_int_synonyms_using_loops_; + } + uint32_t GetChanceOfDonatingAdditionalModule() const { + return chance_of_donating_additional_module_; + } + uint32_t GetChanceOfDuplicatingRegionWithSelection() const { + return chance_of_duplicating_region_with_selection_; + } + uint32_t GetChanceOfExpandingVectorReduction() const { + return chance_of_expanding_vector_reduction_; + } + uint32_t GetChanceOfFlatteningConditionalBranch() const { + return chance_of_flattening_conditional_branch_; + } + uint32_t GetChanceOfGoingDeeperToExtractComposite() const { + return chance_of_going_deeper_to_extract_composite_; + } + uint32_t GetChanceOfGoingDeeperToInsertInComposite() const { + return chance_of_going_deeper_to_insert_in_composite_; + } + uint32_t GetChanceOfGoingDeeperWhenMakingAccessChain() const { + return chance_of_going_deeper_when_making_access_chain_; + } + uint32_t GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym() const { + return chance_of_having_two_blocks_in_loop_to_create_int_synonym_; + } + uint32_t GetChanceOfInliningFunction() const { + return chance_of_inlining_function_; + } + uint32_t GetChanceOfInterchangingSignednessOfIntegerOperands() const { + return chance_of_interchanging_signedness_of_integer_operands_; + } + uint32_t GetChanceOfInterchangingZeroLikeConstants() const { + return chance_of_interchanging_zero_like_constants_; + } + uint32_t GetChanceOfInvertingComparisonOperators() const { + return chance_of_inverting_comparison_operators_; + } + uint32_t ChanceOfMakingDonorLivesafe() const { + return chance_of_making_donor_livesafe_; + } + uint32_t GetChanceOfMakingVectorOperationDynamic() const { + return chance_of_making_vector_operation_dynamic_; + } + uint32_t GetChanceOfMergingBlocks() const { + return chance_of_merging_blocks_; + } + uint32_t GetChanceOfMergingFunctionReturns() const { + return chance_of_merging_function_returns_; + } + uint32_t GetChanceOfMovingBlockDown() const { + return chance_of_moving_block_down_; + } + uint32_t GetChanceOfMutatingPointer() const { + return chance_of_mutating_pointer_; + } + uint32_t GetChanceOfObfuscatingConstant() const { + return chance_of_obfuscating_constant_; + } + uint32_t GetChanceOfOutliningFunction() const { + return chance_of_outlining_function_; + } + uint32_t GetChanceOfPermutingInstructions() const { + return chance_of_permuting_instructions_; + } + uint32_t GetChanceOfPermutingParameters() const { + return chance_of_permuting_parameters_; + } + uint32_t GetChanceOfPermutingPhiOperands() const { + return chance_of_permuting_phi_operands_; + } + uint32_t GetChanceOfPropagatingInstructionsDown() const { + return chance_of_propagating_instructions_down_; + } + uint32_t GetChanceOfPropagatingInstructionsUp() const { + return chance_of_propagating_instructions_up_; + } + uint32_t GetChanceOfPushingIdThroughVariable() const { + return chance_of_pushing_id_through_variable_; + } + uint32_t GetChanceOfReplacingAddSubMulWithCarryingExtended() const { + return chance_of_replacing_add_sub_mul_with_carrying_extended_; + } + uint32_t GetChanceOfReplacingBranchFromDeadBlockWithExit() const { + return chance_of_replacing_branch_from_dead_block_with_exit_; + } + uint32_t GetChanceOfReplacingCopyMemoryWithLoadStore() const { + return chance_of_replacing_copy_memory_with_load_store_; + } + uint32_t GetChanceOfReplacingCopyObjectWithStoreLoad() const { + return chance_of_replacing_copyobject_with_store_load_; + } + uint32_t GetChanceOfReplacingIdWithSynonym() const { + return chance_of_replacing_id_with_synonym_; + } + uint32_t GetChanceOfReplacingIrrelevantId() const { + return chance_of_replacing_irrelevant_id_; + } + uint32_t GetChanceOfReplacingLinearAlgebraInstructions() const { + return chance_of_replacing_linear_algebra_instructions_; + } + uint32_t GetChanceOfReplacingLoadStoreWithCopyMemory() const { + return chance_of_replacing_load_store_with_copy_memory_; + } + uint32_t GetChanceOfReplacingOpPhiIdFromDeadPredecessor() const { + return chance_of_replacing_opphi_id_from_dead_predecessor_; + } + uint32_t GetChanceOfReplacingOpselectWithConditionalBranch() const { + return chance_of_replacing_opselect_with_conditional_branch_; + } + uint32_t GetChanceOfReplacingParametersWithGlobals() const { + return chance_of_replacing_parameters_with_globals_; + } + uint32_t GetChanceOfReplacingParametersWithStruct() const { + return chance_of_replacing_parameters_with_struct_; + } + uint32_t GetChanceOfSplittingBlock() const { + return chance_of_splitting_block_; + } + uint32_t GetChanceOfSwappingConditionalBranchOperands() const { + return chance_of_swapping_conditional_branch_operands_; + } + uint32_t GetChanceOfTogglingAccessChainInstruction() const { + return chance_of_toggling_access_chain_instruction_; + } + uint32_t GetChanceOfWrappingRegionInSelection() const { + return chance_of_wrapping_region_in_selection_; + } + + // Other functions to control transformations. Keep them in alphabetical + // order. + uint32_t GetMaximumEquivalenceClassSizeForDataSynonymFactClosure() const { + return max_equivalence_class_size_for_data_synonym_fact_closure_; + } + uint32_t GetMaximumNumberOfFunctionParameters() const { + return max_number_of_function_parameters_; + } + uint32_t GetMaximumNumberOfParametersReplacedWithStruct() const { + return max_number_of_parameters_replaced_with_struct_; + } + std::pair GetRandomBranchWeights() { + std::pair branch_weights = {0, 0}; + + while (branch_weights.first == 0 && branch_weights.second == 0) { + // Using INT32_MAX to do not overflow UINT32_MAX when the branch weights + // are added together. + branch_weights.first = random_generator_->RandomUint32(INT32_MAX); + branch_weights.second = random_generator_->RandomUint32(INT32_MAX); + } + + return branch_weights; + } + std::vector GetRandomComponentsForVectorShuffle( + uint32_t max_component_index) { + // Component count must be in range [2, 4]. + std::vector components(random_generator_->RandomUint32(2) + 2); + + for (uint32_t& component : components) { + component = random_generator_->RandomUint32(max_component_index); + } + + return components; + } + uint32_t GetRandomCompositeExtractIndex(uint32_t number_of_members) { + assert(number_of_members > 0 && "Composite object must have some members"); + return ChooseBetweenMinAndMax({0, number_of_members - 1}); + } + uint32_t GetRandomIndexForAccessChain(uint32_t composite_size_bound) { + return random_generator_->RandomUint32(composite_size_bound); + } + uint32_t GetRandomIndexForCompositeInsert(uint32_t number_of_components) { + return random_generator_->RandomUint32(number_of_components); + } + int64_t GetRandomValueForStepConstantInLoop() { + return random_generator_->RandomUint64(UINT64_MAX); + } + uint32_t GetRandomLoopControlPartialCount() { + return random_generator_->RandomUint32(max_loop_control_partial_count_); + } + uint32_t GetRandomLoopControlPeelCount() { + return random_generator_->RandomUint32(max_loop_control_peel_count_); + } + uint32_t GetRandomLoopLimit() { + return random_generator_->RandomUint32(max_loop_limit_); + } + uint32_t GetRandomNumberOfLoopIterations(uint32_t max_num_iterations) { + return ChooseBetweenMinAndMax({1, max_num_iterations}); + } + uint32_t GetRandomNumberOfNewParameters(uint32_t num_of_params) { + assert(num_of_params < GetMaximumNumberOfFunctionParameters()); + return ChooseBetweenMinAndMax( + {1, std::min(max_number_of_new_parameters_, + GetMaximumNumberOfFunctionParameters() - num_of_params)}); + } + uint32_t GetRandomNumberOfParametersReplacedWithStruct(uint32_t num_params) { + assert(num_params != 0 && "A function must have parameters to replace"); + return ChooseBetweenMinAndMax( + {1, std::min(num_params, + GetMaximumNumberOfParametersReplacedWithStruct())}); + } + uint32_t GetRandomSizeForNewArray() { + // Ensure that the array size is non-zero. + return random_generator_->RandomUint32(max_new_array_size_limit_ - 1) + 1; + } + protobufs::TransformationAddSynonym::SynonymType GetRandomSynonymType(); + uint32_t GetRandomUnusedComponentCountForImageSample( + uint32_t max_unused_component_count) { + // Ensure that the number of unused components is non-zero. + return random_generator_->RandomUint32(max_unused_component_count) + 1; + } + bool GoDeeperInConstantObfuscation(uint32_t depth) { + return go_deeper_in_constant_obfuscation_(depth, random_generator_); + } + + private: + // The source of randomness. + RandomGenerator* random_generator_; + // The next fresh id to be issued. + uint32_t next_fresh_id_; + + // Probabilities associated with applying various transformations. + // Keep them in alphabetical order. + uint32_t chance_of_accepting_repeated_pass_recommendation_; + uint32_t chance_of_adding_access_chain_; + uint32_t chance_of_adding_another_pass_to_pass_loop_; + uint32_t chance_of_adding_another_struct_field_; + uint32_t chance_of_adding_array_or_struct_type_; + uint32_t chance_of_adding_bit_instruction_synonym_; + uint32_t chance_of_adding_both_branches_when_replacing_opselect_; + uint32_t chance_of_adding_composite_extract_; + uint32_t chance_of_adding_composite_insert_; + uint32_t chance_of_adding_copy_memory_; + uint32_t chance_of_adding_dead_block_; + uint32_t chance_of_adding_dead_break_; + uint32_t chance_of_adding_dead_continue_; + uint32_t chance_of_adding_equation_instruction_; + uint32_t chance_of_adding_global_variable_; + uint32_t chance_of_adding_image_sample_unused_components_; + uint32_t chance_of_adding_load_; + uint32_t chance_of_adding_local_variable_; + uint32_t chance_of_adding_loop_preheader_; + uint32_t chance_of_adding_matrix_type_; + uint32_t chance_of_adding_no_contraction_decoration_; + uint32_t chance_of_adding_opphi_synonym_; + uint32_t chance_of_adding_parameters; + uint32_t chance_of_adding_relaxed_decoration_; + uint32_t chance_of_adding_store_; + uint32_t chance_of_adding_synonyms_; + uint32_t chance_of_adding_true_branch_when_replacing_opselect_; + uint32_t chance_of_adding_vector_shuffle_; + uint32_t chance_of_adding_vector_type_; + uint32_t chance_of_adjusting_branch_weights_; + uint32_t chance_of_adjusting_function_control_; + uint32_t chance_of_adjusting_loop_control_; + uint32_t chance_of_adjusting_memory_operands_mask_; + uint32_t chance_of_adjusting_selection_control_; + uint32_t chance_of_calling_function_; + uint32_t chance_of_choosing_struct_type_vs_array_type_; + uint32_t chance_of_choosing_workgroup_storage_class_; + uint32_t chance_of_constructing_composite_; + uint32_t chance_of_copying_object_; + uint32_t chance_of_creating_int_synonyms_using_loops_; + uint32_t chance_of_donating_additional_module_; + uint32_t chance_of_duplicating_region_with_selection_; + uint32_t chance_of_expanding_vector_reduction_; + uint32_t chance_of_flattening_conditional_branch_; + uint32_t chance_of_going_deeper_to_extract_composite_; + uint32_t chance_of_going_deeper_to_insert_in_composite_; + uint32_t chance_of_going_deeper_when_making_access_chain_; + uint32_t chance_of_having_two_blocks_in_loop_to_create_int_synonym_; + uint32_t chance_of_inlining_function_; + uint32_t chance_of_interchanging_signedness_of_integer_operands_; + uint32_t chance_of_interchanging_zero_like_constants_; + uint32_t chance_of_inverting_comparison_operators_; + uint32_t chance_of_making_donor_livesafe_; + uint32_t chance_of_making_vector_operation_dynamic_; + uint32_t chance_of_merging_blocks_; + uint32_t chance_of_merging_function_returns_; + uint32_t chance_of_moving_block_down_; + uint32_t chance_of_mutating_pointer_; + uint32_t chance_of_obfuscating_constant_; + uint32_t chance_of_outlining_function_; + uint32_t chance_of_permuting_instructions_; + uint32_t chance_of_permuting_parameters_; + uint32_t chance_of_permuting_phi_operands_; + uint32_t chance_of_propagating_instructions_down_; + uint32_t chance_of_propagating_instructions_up_; + uint32_t chance_of_pushing_id_through_variable_; + uint32_t chance_of_replacing_add_sub_mul_with_carrying_extended_; + uint32_t chance_of_replacing_branch_from_dead_block_with_exit_; + uint32_t chance_of_replacing_copy_memory_with_load_store_; + uint32_t chance_of_replacing_copyobject_with_store_load_; + uint32_t chance_of_replacing_id_with_synonym_; + uint32_t chance_of_replacing_irrelevant_id_; + uint32_t chance_of_replacing_linear_algebra_instructions_; + uint32_t chance_of_replacing_load_store_with_copy_memory_; + uint32_t chance_of_replacing_opphi_id_from_dead_predecessor_; + uint32_t chance_of_replacing_opselect_with_conditional_branch_; + uint32_t chance_of_replacing_parameters_with_globals_; + uint32_t chance_of_replacing_parameters_with_struct_; + uint32_t chance_of_splitting_block_; + uint32_t chance_of_swapping_conditional_branch_operands_; + uint32_t chance_of_toggling_access_chain_instruction_; + uint32_t chance_of_wrapping_region_in_selection_; + + // Limits associated with various quantities for which random values are + // chosen during fuzzing. + // Keep them in alphabetical order. + uint32_t max_equivalence_class_size_for_data_synonym_fact_closure_; + uint32_t max_loop_control_partial_count_; + uint32_t max_loop_control_peel_count_; + uint32_t max_loop_limit_; + uint32_t max_new_array_size_limit_; + uint32_t max_number_of_function_parameters_; + uint32_t max_number_of_new_parameters_; + uint32_t max_number_of_parameters_replaced_with_struct_; + + // Functions to determine with what probability to go deeper when generating + // or mutating constructs recursively. + const std::function& + go_deeper_in_constant_obfuscation_; + + // Requires |min_max.first| <= |min_max.second|, and returns a value in the + // range [ |min_max.first|, |min_max.second| ] + uint32_t ChooseBetweenMinAndMax(const std::pair& min_max); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_CONTEXT_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass.cpp new file mode 100644 index 0000000..486f18f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass.cpp @@ -0,0 +1,754 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_composite.h" +#include "source/fuzz/transformation_add_constant_null.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_global_undef.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_add_loop_preheader.h" +#include "source/fuzz/transformation_add_type_boolean.h" +#include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_function.h" +#include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_matrix.h" +#include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" +#include "source/fuzz/transformation_add_type_vector.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPass::FuzzerPass(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : ir_context_(ir_context), + transformation_context_(transformation_context), + fuzzer_context_(fuzzer_context), + transformations_(transformations) {} + +FuzzerPass::~FuzzerPass() = default; + +std::vector FuzzerPass::FindAvailableInstructions( + opt::Function* function, opt::BasicBlock* block, + const opt::BasicBlock::iterator& inst_it, + std::function + instruction_is_relevant) const { + // TODO(afd) The following is (relatively) simple, but may end up being + // prohibitively inefficient, as it walks the whole dominator tree for + // every instruction that is considered. + + std::vector result; + // Consider all global declarations + for (auto& global : GetIRContext()->module()->types_values()) { + if (instruction_is_relevant(GetIRContext(), &global)) { + result.push_back(&global); + } + } + + // Consider all function parameters + function->ForEachParam( + [this, &instruction_is_relevant, &result](opt::Instruction* param) { + if (instruction_is_relevant(GetIRContext(), param)) { + result.push_back(param); + } + }); + + // Consider all previous instructions in this block + for (auto prev_inst_it = block->begin(); prev_inst_it != inst_it; + ++prev_inst_it) { + if (instruction_is_relevant(GetIRContext(), &*prev_inst_it)) { + result.push_back(&*prev_inst_it); + } + } + + // Walk the dominator tree to consider all instructions from dominating + // blocks + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function); + for (auto next_dominator = dominator_analysis->ImmediateDominator(block); + next_dominator != nullptr; + next_dominator = + dominator_analysis->ImmediateDominator(next_dominator)) { + for (auto& dominating_inst : *next_dominator) { + if (instruction_is_relevant(GetIRContext(), &dominating_inst)) { + result.push_back(&dominating_inst); + } + } + } + return result; +} + +void FuzzerPass::ForEachInstructionWithInstructionDescriptor( + opt::Function* function, + std::function< + void(opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor)> + action) { + // Consider only reachable blocks. We do this in a separate loop to avoid + // recomputing the dominator analysis every time |action| changes the + // module. + std::vector reachable_blocks; + + const auto* dominator_analysis = + GetIRContext()->GetDominatorAnalysis(function); + for (auto& block : *function) { + if (dominator_analysis->IsReachable(&block)) { + reachable_blocks.push_back(&block); + } + } + + for (auto* block : reachable_blocks) { + // We now consider every instruction in the block, randomly deciding + // whether to apply a transformation before it. + + // In order for transformations to insert new instructions, they need to + // be able to identify the instruction to insert before. We describe an + // instruction via its opcode, 'opc', a base instruction 'base' that has a + // result id, and the number of instructions with opcode 'opc' that we + // should skip when searching from 'base' for the desired instruction. + // (An instruction that has a result id is represented by its own opcode, + // itself as 'base', and a skip-count of 0.) + std::vector> base_opcode_skip_triples; + + // The initial base instruction is the block label. + uint32_t base = block->id(); + + // Counts the number of times we have seen each opcode since we reset the + // base instruction. + std::map skip_count; + + // Consider every instruction in the block. The label is excluded: it is + // only necessary to consider it as a base in case the first instruction + // in the block does not have a result id. + for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) { + if (inst_it->HasResultId()) { + // In the case that the instruction has a result id, we use the + // instruction as its own base, and clear the skip counts we have + // collected. + base = inst_it->result_id(); + skip_count.clear(); + } + const SpvOp opcode = inst_it->opcode(); + + // Invoke the provided function, which might apply a transformation. + action(block, inst_it, + MakeInstructionDescriptor( + base, opcode, + skip_count.count(opcode) ? skip_count.at(opcode) : 0)); + + if (!inst_it->HasResultId()) { + skip_count[opcode] = + skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1; + } + } + } +} + +void FuzzerPass::ForEachInstructionWithInstructionDescriptor( + std::function< + void(opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor)> + action) { + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + ForEachInstructionWithInstructionDescriptor( + &function, + [&action, &function]( + opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + action(&function, block, inst_it, instruction_descriptor); + }); + } +} + +uint32_t FuzzerPass::FindOrCreateBoolType() { + if (auto existing_id = fuzzerutil::MaybeGetBoolType(GetIRContext())) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeBoolean(result)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateIntegerType(uint32_t width, bool is_signed) { + opt::analysis::Integer int_type(width, is_signed); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&int_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeInt(result, width, is_signed)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateFloatType(uint32_t width) { + opt::analysis::Float float_type(width); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&float_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeFloat(result, width)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateFunctionType( + uint32_t return_type_id, const std::vector& argument_id) { + // FindFunctionType has a sigle argument for OpTypeFunction operands + // so we will have to copy them all in this vector + std::vector type_ids(argument_id.size() + 1); + type_ids[0] = return_type_id; + std::copy(argument_id.begin(), argument_id.end(), type_ids.begin() + 1); + + // Check if type exists + auto existing_id = fuzzerutil::FindFunctionType(GetIRContext(), type_ids); + if (existing_id) { + return existing_id; + } + + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeFunction(result, return_type_id, argument_id)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateVectorType(uint32_t component_type_id, + uint32_t component_count) { + assert(component_count >= 2 && component_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + opt::analysis::Type* component_type = + GetIRContext()->get_type_mgr()->GetType(component_type_id); + assert(component_type && "Precondition: the component type must exist."); + opt::analysis::Vector vector_type(component_type, component_count); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&vector_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeVector(result, component_type_id, component_count)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateMatrixType(uint32_t column_count, + uint32_t row_count) { + assert(column_count >= 2 && column_count <= 4 && + "Precondition: column count must be in range [2, 4]."); + assert(row_count >= 2 && row_count <= 4 && + "Precondition: row count must be in range [2, 4]."); + uint32_t column_type_id = + FindOrCreateVectorType(FindOrCreateFloatType(32), row_count); + opt::analysis::Type* column_type = + GetIRContext()->get_type_mgr()->GetType(column_type_id); + opt::analysis::Matrix matrix_type(column_type, column_count); + auto existing_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeMatrix(result, column_type_id, column_count)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateStructType( + const std::vector& component_type_ids) { + if (auto existing_id = + fuzzerutil::MaybeGetStructType(GetIRContext(), component_type_ids)) { + return existing_id; + } + auto new_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeStruct(new_id, component_type_ids)); + return new_id; +} + +uint32_t FuzzerPass::FindOrCreatePointerType(uint32_t base_type_id, + SpvStorageClass storage_class) { + // We do not use the type manager here, due to problems related to isomorphic + // but distinct structs not being regarded as different. + auto existing_id = fuzzerutil::MaybeGetPointerType( + GetIRContext(), base_type_id, storage_class); + if (existing_id) { + return existing_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypePointer(result, storage_class, base_type_id)); + return result; +} + +uint32_t FuzzerPass::FindOrCreatePointerToIntegerType( + uint32_t width, bool is_signed, SpvStorageClass storage_class) { + return FindOrCreatePointerType(FindOrCreateIntegerType(width, is_signed), + storage_class); +} + +uint32_t FuzzerPass::FindOrCreateIntegerConstant( + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant) { + auto int_type_id = FindOrCreateIntegerType(width, is_signed); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), words, int_type_id, + is_irrelevant)) { + return constant_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantScalar(result, int_type_id, + words, is_irrelevant)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateFloatConstant( + const std::vector& words, uint32_t width, bool is_irrelevant) { + auto float_type_id = FindOrCreateFloatType(width); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), words, float_type_id, + is_irrelevant)) { + return constant_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantScalar(result, float_type_id, + words, is_irrelevant)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateBoolConstant(bool value, bool is_irrelevant) { + auto bool_type_id = FindOrCreateBoolType(); + if (auto constant_id = fuzzerutil::MaybeGetScalarConstant( + GetIRContext(), *GetTransformationContext(), {value ? 1u : 0u}, + bool_type_id, is_irrelevant)) { + return constant_id; + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddConstantBoolean(result, value, is_irrelevant)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateConstant(const std::vector& words, + uint32_t type_id, + bool is_irrelevant) { + assert(type_id && "Constant's type id can't be 0."); + + const auto* type = GetIRContext()->get_type_mgr()->GetType(type_id); + assert(type && "Type does not exist."); + + if (type->AsBool()) { + assert(words.size() == 1); + return FindOrCreateBoolConstant(words[0], is_irrelevant); + } else if (const auto* integer = type->AsInteger()) { + return FindOrCreateIntegerConstant(words, integer->width(), + integer->IsSigned(), is_irrelevant); + } else if (const auto* floating = type->AsFloat()) { + return FindOrCreateFloatConstant(words, floating->width(), is_irrelevant); + } + + // This assertion will fail in debug build but not in release build + // so we return 0 to make compiler happy. + assert(false && "Constant type is not supported"); + return 0; +} + +uint32_t FuzzerPass::FindOrCreateCompositeConstant( + const std::vector& component_ids, uint32_t type_id, + bool is_irrelevant) { + if (auto existing_constant = fuzzerutil::MaybeGetCompositeConstant( + GetIRContext(), *GetTransformationContext(), component_ids, type_id, + is_irrelevant)) { + return existing_constant; + } + uint32_t result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantComposite( + result, type_id, component_ids, is_irrelevant)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateGlobalUndef(uint32_t type_id) { + for (auto& inst : GetIRContext()->types_values()) { + if (inst.opcode() == SpvOpUndef && inst.type_id() == type_id) { + return inst.result_id(); + } + } + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddGlobalUndef(result, type_id)); + return result; +} + +uint32_t FuzzerPass::FindOrCreateNullConstant(uint32_t type_id) { + // Find existing declaration + opt::analysis::NullConstant null_constant( + GetIRContext()->get_type_mgr()->GetType(type_id)); + auto existing_constant = + GetIRContext()->get_constant_mgr()->FindConstant(&null_constant); + + // Return if found + if (existing_constant) { + return GetIRContext() + ->get_constant_mgr() + ->GetDefiningInstruction(existing_constant) + ->result_id(); + } + + // Create new if not found + auto result = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantNull(result, type_id)); + return result; +} + +std::pair, std::map>> +FuzzerPass::GetAvailableBasicTypesAndPointers( + SpvStorageClass storage_class) const { + // Records all of the basic types available in the module. + std::set basic_types; + + // For each basic type, records all the associated pointer types that target + // the basic type and that have |storage_class| as their storage class. + std::map> basic_type_to_pointers; + + for (auto& inst : GetIRContext()->types_values()) { + // For each basic type that we come across, record type, and the fact that + // we cannot yet have seen any pointers that use the basic type as its + // pointee type. + // + // For pointer types with basic pointee types, associate the pointer type + // with the basic type. + switch (inst.opcode()) { + case SpvOpTypeBool: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeVector: + // These are all basic types. + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + break; + case SpvOpTypeArray: + // An array type is basic if its base type is basic. + if (basic_types.count(inst.GetSingleWordInOperand(0))) { + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + } + break; + case SpvOpTypeStruct: { + // A struct type is basic if it does not have the Block/BufferBlock + // decoration, and if all of its members are basic. + if (!fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(), + inst.result_id())) { + bool all_members_are_basic_types = true; + for (uint32_t i = 0; i < inst.NumInOperands(); i++) { + if (!basic_types.count(inst.GetSingleWordInOperand(i))) { + all_members_are_basic_types = false; + break; + } + } + if (all_members_are_basic_types) { + basic_types.insert(inst.result_id()); + basic_type_to_pointers.insert({inst.result_id(), {}}); + } + } + break; + } + case SpvOpTypePointer: { + // We are interested in the pointer if its pointee type is basic and it + // has the right storage class. + auto pointee_type = inst.GetSingleWordInOperand(1); + if (inst.GetSingleWordInOperand(0) == storage_class && + basic_types.count(pointee_type)) { + // The pointer has the desired storage class, and its pointee type is + // a basic type, so we are interested in it. Associate it with its + // basic type. + basic_type_to_pointers.at(pointee_type).push_back(inst.result_id()); + } + break; + } + default: + break; + } + } + return {{basic_types.begin(), basic_types.end()}, basic_type_to_pointers}; +} + +uint32_t FuzzerPass::FindOrCreateZeroConstant( + uint32_t scalar_or_composite_type_id, bool is_irrelevant) { + auto type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(scalar_or_composite_type_id); + assert(type_instruction && "The type instruction must exist."); + switch (type_instruction->opcode()) { + case SpvOpTypeBool: + return FindOrCreateBoolConstant(false, is_irrelevant); + case SpvOpTypeFloat: { + auto width = type_instruction->GetSingleWordInOperand(0); + auto num_words = (width + 32 - 1) / 32; + return FindOrCreateFloatConstant(std::vector(num_words, 0), + width, is_irrelevant); + } + case SpvOpTypeInt: { + auto width = type_instruction->GetSingleWordInOperand(0); + auto num_words = (width + 32 - 1) / 32; + return FindOrCreateIntegerConstant( + std::vector(num_words, 0), width, + type_instruction->GetSingleWordInOperand(1), is_irrelevant); + } + case SpvOpTypeArray: { + auto component_type_id = type_instruction->GetSingleWordInOperand(0); + auto num_components = + fuzzerutil::GetArraySize(*type_instruction, GetIRContext()); + return FindOrCreateCompositeConstant( + std::vector( + num_components, + FindOrCreateZeroConstant(component_type_id, is_irrelevant)), + scalar_or_composite_type_id, is_irrelevant); + } + case SpvOpTypeMatrix: + case SpvOpTypeVector: { + auto component_type_id = type_instruction->GetSingleWordInOperand(0); + auto num_components = type_instruction->GetSingleWordInOperand(1); + return FindOrCreateCompositeConstant( + std::vector( + num_components, + FindOrCreateZeroConstant(component_type_id, is_irrelevant)), + scalar_or_composite_type_id, is_irrelevant); + } + case SpvOpTypeStruct: { + assert(!fuzzerutil::HasBlockOrBufferBlockDecoration( + GetIRContext(), scalar_or_composite_type_id) && + "We do not construct constants of struct types decorated with " + "Block or BufferBlock."); + std::vector field_zero_ids; + for (uint32_t index = 0; index < type_instruction->NumInOperands(); + index++) { + field_zero_ids.push_back(FindOrCreateZeroConstant( + type_instruction->GetSingleWordInOperand(index), is_irrelevant)); + } + return FindOrCreateCompositeConstant( + field_zero_ids, scalar_or_composite_type_id, is_irrelevant); + } + default: + assert(false && "Unknown type."); + return 0; + } +} + +void FuzzerPass::MaybeAddUseToReplace( + opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id, + std::vector>* + uses_to_replace) { + // Only consider this use if it is in a block + if (!GetIRContext()->get_instr_block(use_inst)) { + return; + } + + // Get the index of the operand restricted to input operands. + uint32_t in_operand_index = + fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index); + auto id_use_descriptor = + MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, in_operand_index); + uses_to_replace->emplace_back( + std::make_pair(id_use_descriptor, replacement_id)); +} + +opt::BasicBlock* FuzzerPass::GetOrCreateSimpleLoopPreheader( + uint32_t header_id) { + auto header_block = fuzzerutil::MaybeFindBlock(GetIRContext(), header_id); + + assert(header_block && header_block->IsLoopHeader() && + "|header_id| should be the label id of a loop header"); + + auto predecessors = GetIRContext()->cfg()->preds(header_id); + + assert(predecessors.size() >= 2 && + "The block |header_id| should be reachable."); + + auto function = header_block->GetParent(); + + if (predecessors.size() == 2) { + // The header has a single out-of-loop predecessor, which could be a + // preheader. + + opt::BasicBlock* maybe_preheader; + + if (GetIRContext()->GetDominatorAnalysis(function)->Dominates( + header_id, predecessors[0])) { + // The first predecessor is the back-edge block, because the header + // dominates it, so the second one is out of the loop. + maybe_preheader = &*function->FindBlock(predecessors[1]); + } else { + // The first predecessor is out of the loop. + maybe_preheader = &*function->FindBlock(predecessors[0]); + } + + // |maybe_preheader| is a preheader if it branches unconditionally to + // the header. We also require it not to be a loop header. + if (maybe_preheader->terminator()->opcode() == SpvOpBranch && + !maybe_preheader->IsLoopHeader()) { + return maybe_preheader; + } + } + + // We need to add a preheader. + + // Get a fresh id for the preheader. + uint32_t preheader_id = GetFuzzerContext()->GetFreshId(); + + // Get a fresh id for each OpPhi instruction, if there is more than one + // out-of-loop predecessor. + std::vector phi_ids; + if (predecessors.size() > 2) { + header_block->ForEachPhiInst( + [this, &phi_ids](opt::Instruction* /* unused */) { + phi_ids.push_back(GetFuzzerContext()->GetFreshId()); + }); + } + + // Add the preheader. + ApplyTransformation( + TransformationAddLoopPreheader(header_id, preheader_id, phi_ids)); + + // Make the newly-created preheader the new entry block. + return &*function->FindBlock(preheader_id); +} + +opt::BasicBlock* FuzzerPass::SplitBlockAfterOpPhiOrOpVariable( + uint32_t block_id) { + auto block = fuzzerutil::MaybeFindBlock(GetIRContext(), block_id); + assert(block && "|block_id| must be a block label"); + assert(!block->IsLoopHeader() && "|block_id| cannot be a loop header"); + + // Find the first non-OpPhi and non-OpVariable instruction. + auto non_phi_or_var_inst = &*block->begin(); + while (non_phi_or_var_inst->opcode() == SpvOpPhi || + non_phi_or_var_inst->opcode() == SpvOpVariable) { + non_phi_or_var_inst = non_phi_or_var_inst->NextNode(); + } + + // Split the block. + uint32_t new_block_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), non_phi_or_var_inst), + new_block_id)); + + // We need to return the newly-created block. + return &*block->GetParent()->FindBlock(new_block_id); +} + +uint32_t FuzzerPass::FindOrCreateLocalVariable( + uint32_t pointer_type_id, uint32_t function_id, + bool pointee_value_is_irrelevant) { + auto pointer_type = GetIRContext()->get_type_mgr()->GetType(pointer_type_id); + // No unused variables in release mode. + (void)pointer_type; + assert(pointer_type && pointer_type->AsPointer() && + pointer_type->AsPointer()->storage_class() == + SpvStorageClassFunction && + "The pointer_type_id must refer to a defined pointer type with " + "storage class Function"); + auto function = fuzzerutil::FindFunction(GetIRContext(), function_id); + assert(function && "The function must be defined."); + + // First we try to find a suitable existing variable. + // All of the local variable declarations are located in the first block. + for (auto& instruction : *function->begin()) { + if (instruction.opcode() != SpvOpVariable) { + continue; + } + // The existing OpVariable must have type |pointer_type_id|. + if (instruction.type_id() != pointer_type_id) { + continue; + } + // Check if the found variable is marked with PointeeValueIsIrrelevant + // according to |pointee_value_is_irrelevant|. + if (GetTransformationContext()->GetFactManager()->PointeeValueIsIrrelevant( + instruction.result_id()) != pointee_value_is_irrelevant) { + continue; + } + return instruction.result_id(); + } + + // No such variable was found. Apply a transformation to get one. + uint32_t pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), pointer_type_id); + uint32_t result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddLocalVariable( + result_id, pointer_type_id, function_id, + FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant), + pointee_value_is_irrelevant)); + return result_id; +} + +uint32_t FuzzerPass::FindOrCreateGlobalVariable( + uint32_t pointer_type_id, bool pointee_value_is_irrelevant) { + auto pointer_type = GetIRContext()->get_type_mgr()->GetType(pointer_type_id); + // No unused variables in release mode. + (void)pointer_type; + assert( + pointer_type && pointer_type->AsPointer() && + (pointer_type->AsPointer()->storage_class() == SpvStorageClassPrivate || + pointer_type->AsPointer()->storage_class() == + SpvStorageClassWorkgroup) && + "The pointer_type_id must refer to a defined pointer type with storage " + "class Private or Workgroup"); + + // First we try to find a suitable existing variable. + for (auto& instruction : GetIRContext()->module()->types_values()) { + if (instruction.opcode() != SpvOpVariable) { + continue; + } + // The existing OpVariable must have type |pointer_type_id|. + if (instruction.type_id() != pointer_type_id) { + continue; + } + // Check if the found variable is marked with PointeeValueIsIrrelevant + // according to |pointee_value_is_irrelevant|. + if (GetTransformationContext()->GetFactManager()->PointeeValueIsIrrelevant( + instruction.result_id()) != pointee_value_is_irrelevant) { + continue; + } + return instruction.result_id(); + } + + // No such variable was found. Apply a transformation to get one. + uint32_t pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), pointer_type_id); + auto storage_class = fuzzerutil::GetStorageClassFromPointerType( + GetIRContext(), pointer_type_id); + uint32_t result_id = GetFuzzerContext()->GetFreshId(); + + // A variable with storage class Workgroup shouldn't have an initializer. + if (storage_class == SpvStorageClassWorkgroup) { + ApplyTransformation(TransformationAddGlobalVariable( + result_id, pointer_type_id, SpvStorageClassWorkgroup, 0, + pointee_value_is_irrelevant)); + } else { + ApplyTransformation(TransformationAddGlobalVariable( + result_id, pointer_type_id, SpvStorageClassPrivate, + FindOrCreateZeroConstant(pointee_type_id, pointee_value_is_irrelevant), + pointee_value_is_irrelevant)); + } + return result_id; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass.h new file mode 100644 index 0000000..da3f239 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass.h @@ -0,0 +1,353 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_H_ + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Interface for applying a pass of transformations to a module. +class FuzzerPass { + public: + FuzzerPass(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + virtual ~FuzzerPass(); + + // Applies the pass to the module |ir_context_|, assuming and updating + // information from |transformation_context_|, and using |fuzzer_context_| to + // guide the process. Appends to |transformations_| all transformations that + // were applied during the pass. + virtual void Apply() = 0; + + protected: + opt::IRContext* GetIRContext() const { return ir_context_; } + + TransformationContext* GetTransformationContext() const { + return transformation_context_; + } + + FuzzerContext* GetFuzzerContext() const { return fuzzer_context_; } + + protobufs::TransformationSequence* GetTransformations() const { + return transformations_; + } + + // Returns all instructions that are *available* at |inst_it|, which is + // required to be inside block |block| of function |function| - that is, all + // instructions at global scope and all instructions that strictly dominate + // |inst_it|. + // + // Filters said instructions to return only those that satisfy the + // |instruction_is_relevant| predicate. This, for instance, could ignore all + // instructions that have a particular decoration. + std::vector FindAvailableInstructions( + opt::Function* function, opt::BasicBlock* block, + const opt::BasicBlock::iterator& inst_it, + std::function + instruction_is_relevant) const; + + // A helper method that iterates through each instruction in each reachable + // block of |function|, at all times tracking an instruction descriptor that + // allows the latest instruction to be located even if it has no result id. + // + // The code to manipulate the instruction descriptor is a bit fiddly. The + // point of this method is to avoiding having to duplicate it in multiple + // transformation passes. + // + // The function |action| is invoked for each instruction |inst_it| in block + // |block| of function |function| that is encountered. The + // |instruction_descriptor| parameter to the function object allows |inst_it| + // to be identified. + // + // In most intended use cases, the job of |action| is to randomly decide + // whether to try to apply some transformation, and then - if selected - to + // attempt to apply it. + void ForEachInstructionWithInstructionDescriptor( + opt::Function* function, + std::function< + void(opt::BasicBlock* block, opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor)> + action); + + // Applies the above overload of ForEachInstructionWithInstructionDescriptor + // to every function in the module, so that |action| is applied to an + // |instruction_descriptor| for every instruction, |inst_it|, of every |block| + // in every |function|. + void ForEachInstructionWithInstructionDescriptor( + std::function< + void(opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor)> + action); + + // A generic helper for applying a transformation that should be applicable + // by construction, and adding it to the sequence of applied transformations. + void ApplyTransformation(const Transformation& transformation) { + assert(transformation.IsApplicable(GetIRContext(), + *GetTransformationContext()) && + "Transformation should be applicable by construction."); + transformation.Apply(GetIRContext(), GetTransformationContext()); + protobufs::Transformation transformation_message = + transformation.ToMessage(); + assert(transformation_message.transformation_case() != + protobufs::Transformation::TRANSFORMATION_NOT_SET && + "Bad transformation."); + *GetTransformations()->add_transformation() = transformation_message; + } + + // A generic helper for applying a transformation only if it is applicable. + // If it is applicable, the transformation is applied and then added to the + // sequence of applied transformations and the function returns true. + // Otherwise, the function returns false. + bool MaybeApplyTransformation(const Transformation& transformation) { + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); + protobufs::Transformation transformation_message = + transformation.ToMessage(); + assert(transformation_message.transformation_case() != + protobufs::Transformation::TRANSFORMATION_NOT_SET && + "Bad transformation."); + *GetTransformations()->add_transformation() = transformation_message; + return true; + } + return false; + } + + // Returns the id of an OpTypeBool instruction. If such an instruction does + // not exist, a transformation is applied to add it. + uint32_t FindOrCreateBoolType(); + + // Returns the id of an OpTypeInt instruction, with width and signedness + // specified by |width| and |is_signed|, respectively. If such an instruction + // does not exist, a transformation is applied to add it. + uint32_t FindOrCreateIntegerType(uint32_t width, bool is_signed); + + // Returns the id of an OpTypeFloat instruction, with width specified by + // |width|. If such an instruction does not exist, a transformation is + // applied to add it. + uint32_t FindOrCreateFloatType(uint32_t width); + + // Returns the id of an OpTypeFunction % %<...argument_id> + // instruction. If such an instruction doesn't exist, a transformation + // is applied to create a new one. + uint32_t FindOrCreateFunctionType(uint32_t return_type_id, + const std::vector& argument_id); + + // Returns the id of an OpTypeVector instruction, with |component_type_id| + // (which must already exist) as its base type, and |component_count| + // elements (which must be in the range [2, 4]). If such an instruction does + // not exist, a transformation is applied to add it. + uint32_t FindOrCreateVectorType(uint32_t component_type_id, + uint32_t component_count); + + // Returns the id of an OpTypeMatrix instruction, with |column_count| columns + // and |row_count| rows (each of which must be in the range [2, 4]). If the + // float and vector types required to build this matrix type or the matrix + // type itself do not exist, transformations are applied to add them. + uint32_t FindOrCreateMatrixType(uint32_t column_count, uint32_t row_count); + + // Returns the id of an OpTypeStruct instruction with |component_type_ids| as + // type ids for struct's components. If no such a struct type exists, + // transformations are applied to add it. |component_type_ids| may not contain + // a result id of an OpTypeFunction. + uint32_t FindOrCreateStructType( + const std::vector& component_type_ids); + + // Returns the id of a pointer type with base type |base_type_id| (which must + // already exist) and storage class |storage_class|. A transformation is + // applied to add the pointer if it does not already exist. + uint32_t FindOrCreatePointerType(uint32_t base_type_id, + SpvStorageClass storage_class); + + // Returns the id of an OpTypePointer instruction, with a integer base + // type of width and signedness specified by |width| and |is_signed|, + // respectively. If the pointer type or required integer base type do not + // exist, transformations are applied to add them. + uint32_t FindOrCreatePointerToIntegerType(uint32_t width, bool is_signed, + SpvStorageClass storage_class); + + // Returns the id of an OpConstant instruction, with a integer type of + // width and signedness specified by |width| and |is_signed|, respectively, + // with |words| as its value. If either the required integer type or the + // constant do not exist, transformations are applied to add them. + // The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateIntegerConstant(const std::vector& words, + uint32_t width, bool is_signed, + bool is_irrelevant); + + // Returns the id of an OpConstant instruction, with a floating-point + // type of width specified by |width|, with |words| as its value. If either + // the required floating-point type or the constant do not exist, + // transformations are applied to add them. The returned id either + // participates in IdIsIrrelevant fact or not, depending on the + // |is_irrelevant| parameter. + uint32_t FindOrCreateFloatConstant(const std::vector& words, + uint32_t width, bool is_irrelevant); + + // Returns the id of an OpConstantTrue or OpConstantFalse instruction, + // according to |value|. If either the required instruction or the bool + // type do not exist, transformations are applied to add them. + // The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateBoolConstant(bool value, bool is_irrelevant); + + // Returns the id of an OpConstant instruction of type with |type_id| + // that consists of |words|. If that instruction doesn't exist, + // transformations are applied to add it. |type_id| must be a valid + // result id of either scalar or boolean OpType* instruction that exists + // in the module. The returned id either participates in IdIsIrrelevant fact + // or not, depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateConstant(const std::vector& words, + uint32_t type_id, bool is_irrelevant); + + // Returns the id of an OpConstantComposite instruction of type with |type_id| + // that consists of |component_ids|. If that instruction doesn't exist, + // transformations are applied to add it. |type_id| must be a valid + // result id of an OpType* instruction that represents a composite type + // (i.e. a vector, matrix, struct or array). + // The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + uint32_t FindOrCreateCompositeConstant( + const std::vector& component_ids, uint32_t type_id, + bool is_irrelevant); + + // Returns the result id of an instruction of the form: + // %id = OpUndef %|type_id| + // If no such instruction exists, a transformation is applied to add it. + uint32_t FindOrCreateGlobalUndef(uint32_t type_id); + + // Returns the id of an OpNullConstant instruction of type |type_id|. If + // that instruction doesn't exist, it is added through a transformation. + // |type_id| must be a valid result id of an OpType* instruction that exists + // in the module. + uint32_t FindOrCreateNullConstant(uint32_t type_id); + + // Define a *basic type* to be an integer, boolean or floating-point type, + // or a matrix, vector, struct or fixed-size array built from basic types. In + // particular, a basic type cannot contain an opaque type (such as an image), + // or a runtime-sized array. + // + // Yields a pair, (basic_type_ids, basic_type_ids_to_pointers), such that: + // - basic_type_ids captures every basic type declared in the module. + // - basic_type_ids_to_pointers maps every such basic type to the sequence + // of all pointer types that have storage class |storage_class| and the + // given basic type as their pointee type. The sequence may be empty for + // some basic types if no pointers to those types are defined for the given + // storage class, and the sequence will have multiple elements if there are + // repeated pointer declarations for the same basic type and storage class. + std::pair, std::map>> + GetAvailableBasicTypesAndPointers(SpvStorageClass storage_class) const; + + // Given a type id, |scalar_or_composite_type_id|, which must correspond to + // some scalar or composite type, returns the result id of an instruction + // defining a constant of the given type that is zero or false at everywhere. + // If such an instruction does not yet exist, transformations are applied to + // add it. The returned id either participates in IdIsIrrelevant fact or not, + // depending on the |is_irrelevant| parameter. + // + // Examples: + // --------------+------------------------------- + // TYPE | RESULT is id corresponding to + // --------------+------------------------------- + // bool | false + // --------------+------------------------------- + // bvec4 | (false, false, false, false) + // --------------+------------------------------- + // float | 0.0 + // --------------+------------------------------- + // vec2 | (0.0, 0.0) + // --------------+------------------------------- + // int[3] | [0, 0, 0] + // --------------+------------------------------- + // struct S { | + // int i; | S(0, false, (0u, 0u)) + // bool b; | + // uint2 u; | + // } | + // --------------+------------------------------- + uint32_t FindOrCreateZeroConstant(uint32_t scalar_or_composite_type_id, + bool is_irrelevant); + + // Adds a pair (id_use_descriptor, |replacement_id|) to the vector + // |uses_to_replace|, where id_use_descriptor is the id use descriptor + // representing the usage of an id in the |use_inst| instruction, at operand + // index |use_index|, only if the instruction is in a basic block. + // If the instruction is not in a basic block, it does nothing. + void MaybeAddUseToReplace( + opt::Instruction* use_inst, uint32_t use_index, uint32_t replacement_id, + std::vector>* + uses_to_replace); + + // Returns the preheader of the loop with header |header_id|, which satisfies + // all of the following conditions: + // - It is the only out-of-loop predecessor of the header + // - It unconditionally branches to the header + // - It is not a loop header itself + // If such preheader does not exist, a new one is added and returned. + // Requires |header_id| to be the label id of a loop header block that is + // reachable in the CFG (and thus has at least 2 predecessors). + opt::BasicBlock* GetOrCreateSimpleLoopPreheader(uint32_t header_id); + + // Returns the second block in the pair obtained by splitting |block_id| just + // after the last OpPhi or OpVariable instruction in it. Assumes that the + // block is not a loop header. + opt::BasicBlock* SplitBlockAfterOpPhiOrOpVariable(uint32_t block_id); + + // Returns the id of an available local variable (storage class Function) with + // the fact PointeeValueIsIrrelevant set according to + // |pointee_value_is_irrelevant|. If there is no such variable, it creates one + // in the |function| adding a zero initializer constant that is irrelevant. + // The new variable has the fact PointeeValueIsIrrelevant set according to + // |pointee_value_is_irrelevant|. The function returns the id of the created + // variable. + uint32_t FindOrCreateLocalVariable(uint32_t pointer_type_id, + uint32_t function_id, + bool pointee_value_is_irrelevant); + + // Returns the id of an available global variable (storage class Private or + // Workgroup) with the fact PointeeValueIsIrrelevant set according to + // |pointee_value_is_irrelevant|. If there is no such variable, it creates + // one, adding a zero initializer constant that is irrelevant. The new + // variable has the fact PointeeValueIsIrrelevant set according to + // |pointee_value_is_irrelevant|. The function returns the id of the created + // variable. + uint32_t FindOrCreateGlobalVariable(uint32_t pointer_type_id, + bool pointee_value_is_irrelevant); + + private: + opt::IRContext* ir_context_; + TransformationContext* transformation_context_; + FuzzerContext* fuzzer_context_; + protobufs::TransformationSequence* transformations_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp new file mode 100644 index 0000000..11155f2 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.cpp @@ -0,0 +1,192 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_access_chains.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_access_chain.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddAccessChains::FuzzerPassAddAccessChains( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddAccessChains::~FuzzerPassAddAccessChains() = default; + +void FuzzerPassAddAccessChains::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(inst_it->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Check whether it is legitimate to insert an access chain + // instruction before this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpAccessChain, + inst_it)) { + return; + } + + // Randomly decide whether to try inserting a load here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAccessChain())) { + return; + } + + // Get all of the pointers that are currently in scope, excluding + // explicitly null and undefined pointers. + std::vector relevant_pointer_instructions = + FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + // A pointer needs both a result and type id. + return false; + } + switch (instruction->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + // Do not allow making an access chain from a null or + // undefined pointer. (We can eliminate these cases + // before actually checking that the instruction is a + // pointer.) + return false; + default: + break; + } + // If the instruction has pointer type, we can legitimately + // make an access chain from it. + return context->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == SpvOpTypePointer; + }); + + // At this point, |relevant_instructions| contains all the pointers + // we might think of making an access chain from. + if (relevant_pointer_instructions.empty()) { + return; + } + + auto chosen_pointer = + relevant_pointer_instructions[GetFuzzerContext()->RandomIndex( + relevant_pointer_instructions)]; + std::vector index_ids; + + // Each index accessing a non-struct composite will be clamped, thus + // needing a pair of fresh ids + std::vector> fresh_ids_for_clamping; + + auto pointer_type = GetIRContext()->get_def_use_mgr()->GetDef( + chosen_pointer->type_id()); + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + while (true) { + auto subobject_type = + GetIRContext()->get_def_use_mgr()->GetDef(subobject_type_id); + if (!spvOpcodeIsComposite(subobject_type->opcode())) { + break; + } + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfGoingDeeperWhenMakingAccessChain())) { + break; + } + uint32_t bound; + switch (subobject_type->opcode()) { + case SpvOpTypeArray: + bound = fuzzerutil::GetArraySize(*subobject_type, GetIRContext()); + break; + case SpvOpTypeMatrix: + case SpvOpTypeVector: + bound = subobject_type->GetSingleWordInOperand(1); + break; + case SpvOpTypeStruct: + bound = fuzzerutil::GetNumberOfStructMembers(*subobject_type); + break; + default: + assert(false && "Not a composite type opcode."); + // Set the bound to a value in order to keep release compilers + // happy. + bound = 0; + break; + } + if (bound == 0) { + // It is possible for a composite type to legitimately have zero + // sub-components, at least in the case of a struct, which + // can have no fields. + break; + } + + uint32_t index_value = + GetFuzzerContext()->GetRandomIndexForAccessChain(bound); + + switch (subobject_type->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeMatrix: + case SpvOpTypeVector: { + // The index will be clamped + + bool is_signed = GetFuzzerContext()->ChooseEven(); + + // Make the constant ready for clamping. We need: + // - an OpTypeBool to be present in the module + // - an OpConstant with the same type as the index and value + // the maximum value for an index + // - a new pair of fresh ids for the clamping instructions + FindOrCreateBoolType(); + FindOrCreateIntegerConstant({bound - 1}, 32, is_signed, false); + std::pair fresh_pair_of_ids = { + GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId()}; + fresh_ids_for_clamping.emplace_back(fresh_pair_of_ids); + + index_ids.push_back(FindOrCreateIntegerConstant( + {index_value}, 32, is_signed, false)); + subobject_type_id = subobject_type->GetSingleWordInOperand(0); + + } break; + case SpvOpTypeStruct: + index_ids.push_back(FindOrCreateIntegerConstant( + {index_value}, 32, GetFuzzerContext()->ChooseEven(), false)); + subobject_type_id = + subobject_type->GetSingleWordInOperand(index_value); + break; + default: + assert(false && "Not a composite type opcode."); + } + } + // The transformation we are about to create will only apply if a + // pointer suitable for the access chain's result type exists, so we + // create one if it does not. + FindOrCreatePointerType(subobject_type_id, + static_cast( + pointer_type->GetSingleWordInOperand(0))); + // Apply the transformation to add an access chain. + ApplyTransformation(TransformationAccessChain( + GetFuzzerContext()->GetFreshId(), chosen_pointer->result_id(), + index_ids, instruction_descriptor, fresh_ids_for_clamping)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h new file mode 100644 index 0000000..8649296 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_access_chains.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds access chains based on pointers available in +// the module. Other passes can use these access chains, e.g. by loading from +// them. +class FuzzerPassAddAccessChains : public FuzzerPass { + public: + FuzzerPassAddAccessChains(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddAccessChains(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_ACCESS_CHAINS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp new file mode 100644 index 0000000..7b9ac4e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_bit_instruction_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddBitInstructionSynonyms::FuzzerPassAddBitInstructionSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddBitInstructionSynonyms::~FuzzerPassAddBitInstructionSynonyms() = + default; + +void FuzzerPassAddBitInstructionSynonyms::Apply() { + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + // This fuzzer pass can add a *lot* of ids. We bail out early if we hit + // the recommended id limit. + if (GetIRContext()->module()->id_bound() >= + GetFuzzerContext()->GetIdBoundLimit()) { + return; + } + + // Randomly decides whether the transformation will be applied. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingBitInstructionSynonym())) { + continue; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): + // Right now we only support certain operations. When this issue is + // addressed the following conditional can use the function + // |spvOpcodeIsBit|. + if (instruction.opcode() != SpvOpBitwiseOr && + instruction.opcode() != SpvOpBitwiseXor && + instruction.opcode() != SpvOpBitwiseAnd && + instruction.opcode() != SpvOpNot) { + continue; + } + + // Right now, only integer operands are supported. + if (GetIRContext() + ->get_type_mgr() + ->GetType(instruction.type_id()) + ->AsVector()) { + continue; + } + + // Make sure all bit indexes are defined as 32-bit unsigned integers. + uint32_t width = GetIRContext() + ->get_type_mgr() + ->GetType(instruction.type_id()) + ->AsInteger() + ->width(); + for (uint32_t i = 0; i < width; i++) { + FindOrCreateIntegerConstant({i}, 32, false, false); + } + + // Applies the add bit instruction synonym transformation. + ApplyTransformation(TransformationAddBitInstructionSynonym( + instruction.result_id(), + GetFuzzerContext()->GetFreshIds( + TransformationAddBitInstructionSynonym::GetRequiredFreshIdCount( + GetIRContext(), &instruction)))); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h new file mode 100644 index 0000000..0194425 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_BIT_INSTRUCTION_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_BIT_INSTRUCTION_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass adds synonyms for bit instructions. It iterates over the +// module instructions, checks if they are bit instructions and randomly applies +// the transformation. +class FuzzerPassAddBitInstructionSynonyms : public FuzzerPass { + public: + FuzzerPassAddBitInstructionSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddBitInstructionSynonyms(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_BIT_INSTRUCTION_SYNONYMS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp new file mode 100644 index 0000000..132a49d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.cpp @@ -0,0 +1,167 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_composite_extract.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_composite_extract.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCompositeExtract::FuzzerPassAddCompositeExtract( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddCompositeExtract::~FuzzerPassAddCompositeExtract() = default; + +void FuzzerPassAddCompositeExtract::Apply() { + std::vector composite_synonyms; + for (const auto* dd : + GetTransformationContext()->GetFactManager()->GetAllSynonyms()) { + // |dd| must describe a component of a composite. + if (!dd->index().empty()) { + composite_synonyms.push_back(dd); + } + } + + // We don't want to invalidate the module every time we apply this + // transformation since rebuilding DominatorAnalysis can be expensive, so we + // collect up the transformations we wish to apply and apply them all later. + std::vector transformations; + + ForEachInstructionWithInstructionDescriptor( + [this, &composite_synonyms, &transformations]( + opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, + inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingCompositeExtract())) { + return; + } + + auto available_composites = FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* ir_context, opt::Instruction* inst) { + return inst->type_id() && inst->result_id() && + fuzzerutil::IsCompositeType( + ir_context->get_type_mgr()->GetType(inst->type_id())); + }); + + std::vector available_synonyms; + for (const auto* dd : composite_synonyms) { + if (fuzzerutil::IdIsAvailableBeforeInstruction( + GetIRContext(), &*inst_it, dd->object())) { + available_synonyms.push_back(dd); + } + } + + if (available_synonyms.empty() && available_composites.empty()) { + return; + } + + uint32_t composite_id = 0; + std::vector indices; + + if (available_synonyms.empty() || (!available_composites.empty() && + GetFuzzerContext()->ChooseEven())) { + const auto* inst = + available_composites[GetFuzzerContext()->RandomIndex( + available_composites)]; + composite_id = inst->result_id(); + + auto type_id = inst->type_id(); + do { + uint32_t number_of_members = 0; + + const auto* type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(type_id); + assert(type_inst && "Composite instruction has invalid type id"); + + switch (type_inst->opcode()) { + case SpvOpTypeArray: + number_of_members = + fuzzerutil::GetArraySize(*type_inst, GetIRContext()); + break; + case SpvOpTypeVector: + case SpvOpTypeMatrix: + number_of_members = type_inst->GetSingleWordInOperand(1); + break; + case SpvOpTypeStruct: + number_of_members = type_inst->NumInOperands(); + break; + default: + assert(false && "|type_inst| is not a composite"); + return; + } + + if (number_of_members == 0) { + return; + } + + indices.push_back( + GetFuzzerContext()->GetRandomCompositeExtractIndex( + number_of_members)); + + switch (type_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + type_id = type_inst->GetSingleWordInOperand(0); + break; + case SpvOpTypeStruct: + type_id = type_inst->GetSingleWordInOperand(indices.back()); + break; + default: + assert(false && "|type_inst| is not a composite"); + return; + } + } while (fuzzerutil::IsCompositeType( + GetIRContext()->get_type_mgr()->GetType(type_id)) && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfGoingDeeperToExtractComposite())); + } else { + const auto* dd = available_synonyms[GetFuzzerContext()->RandomIndex( + available_synonyms)]; + + composite_id = dd->object(); + indices.assign(dd->index().begin(), dd->index().end()); + } + + assert(composite_id != 0 && !indices.empty() && + "Composite object should have been chosen correctly"); + + transformations.emplace_back(instruction_descriptor, + GetFuzzerContext()->GetFreshId(), + composite_id, indices); + }); + + for (const auto& transformation : transformations) { + ApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h new file mode 100644 index 0000000..8bcb825 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_extract.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides whether to add OpCompositeExtract before some instruction +// in the module. +class FuzzerPassAddCompositeExtract : public FuzzerPass { + public: + FuzzerPassAddCompositeExtract( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddCompositeExtract() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_EXTRACT_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp new file mode 100644 index 0000000..e58c754 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.cpp @@ -0,0 +1,234 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/transformation_composite_insert.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCompositeInserts::FuzzerPassAddCompositeInserts( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddCompositeInserts::~FuzzerPassAddCompositeInserts() = default; + +void FuzzerPassAddCompositeInserts::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(instruction_iterator->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Randomly decide whether to try adding an OpCompositeInsert + // instruction. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingCompositeInsert())) { + return; + } + + // It must be possible to insert an OpCompositeInsert instruction + // before |instruction_iterator|. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpCompositeInsert, instruction_iterator)) { + return; + } + + // Look for available values that have composite type. + std::vector available_composites = + FindAvailableInstructions( + function, block, instruction_iterator, + [instruction_descriptor]( + opt::IRContext* ir_context, + opt::Instruction* instruction) -> bool { + // |instruction| must be a supported instruction of composite + // type. + if (!TransformationCompositeInsert:: + IsCompositeInstructionSupported(ir_context, + instruction)) { + return false; + } + + auto instruction_type = ir_context->get_type_mgr()->GetType( + instruction->type_id()); + + // No components of the composite can have type + // OpTypeRuntimeArray. + if (ContainsRuntimeArray(*instruction_type)) { + return false; + } + + // No components of the composite can be pointers. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3658): + // Structs can have components of pointer type. + // FindOrCreateZeroConstant cannot be called on a + // pointer. We ignore pointers for now. Consider adding + // support for pointer types. + if (ContainsPointer(*instruction_type)) { + return false; + } + + return true; + }); + + // If there are no available values, then return. + if (available_composites.empty()) { + return; + } + + // Choose randomly one available composite value. + auto available_composite = + available_composites[GetFuzzerContext()->RandomIndex( + available_composites)]; + + // Take a random component of the chosen composite value. If the chosen + // component is itself a composite, then randomly decide whether to take + // its component and repeat. + uint32_t current_node_type_id = available_composite->type_id(); + std::vector path_to_replaced; + while (true) { + auto current_node_type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(current_node_type_id); + uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex( + *current_node_type_inst, GetIRContext()); + + // If the composite is empty, then end the iteration. + if (num_of_components == 0) { + break; + } + uint32_t one_selected_index = + GetFuzzerContext()->GetRandomIndexForCompositeInsert( + num_of_components); + + // Construct a final index by appending the current index. + path_to_replaced.push_back(one_selected_index); + current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + GetIRContext(), current_node_type_id, one_selected_index); + + // If the component is not a composite then end the iteration. + if (!fuzzerutil::IsCompositeType( + GetIRContext()->get_type_mgr()->GetType( + current_node_type_id))) { + break; + } + + // If the component is a composite, but we decide not to go deeper, + // then end the iteration. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfGoingDeeperToInsertInComposite())) { + break; + } + } + + // Look for available objects that have the type id + // |current_node_type_id| and can be inserted. + std::vector available_objects = + FindAvailableInstructions( + function, block, instruction_iterator, + [instruction_descriptor, current_node_type_id]( + opt::IRContext* /*unused*/, + opt::Instruction* instruction) -> bool { + if (instruction->result_id() == 0 || + instruction->type_id() == 0) { + return false; + } + if (instruction->type_id() != current_node_type_id) { + return false; + } + return true; + }); + + // If there are no objects of the specific type available, check if + // FindOrCreateZeroConstant can be called and create a zero constant of + // this type. + uint32_t available_object_id; + if (available_objects.empty()) { + if (!fuzzerutil::CanCreateConstant(GetIRContext(), + current_node_type_id)) { + return; + } + available_object_id = + FindOrCreateZeroConstant(current_node_type_id, false); + } else { + available_object_id = + available_objects[GetFuzzerContext()->RandomIndex( + available_objects)] + ->result_id(); + } + auto new_result_id = GetFuzzerContext()->GetFreshId(); + + // Insert an OpCompositeInsert instruction which copies + // |available_composite| and in the copy inserts the object + // of type |available_object_id| at index |index_to_replace|. + ApplyTransformation(TransformationCompositeInsert( + instruction_descriptor, new_result_id, + available_composite->result_id(), available_object_id, + path_to_replaced)); + }); +} + +bool FuzzerPassAddCompositeInserts::ContainsPointer( + const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kPointer: + return true; + case opt::analysis::Type::kArray: + return ContainsPointer(*type.AsArray()->element_type()); + case opt::analysis::Type::kMatrix: + return ContainsPointer(*type.AsMatrix()->element_type()); + case opt::analysis::Type::kVector: + return ContainsPointer(*type.AsVector()->element_type()); + case opt::analysis::Type::kStruct: + return std::any_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return ContainsPointer(*element_type); + }); + default: + return false; + } +} + +bool FuzzerPassAddCompositeInserts::ContainsRuntimeArray( + const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kRuntimeArray: + return true; + case opt::analysis::Type::kStruct: + // If any component of a struct is of type OpTypeRuntimeArray, return + // true. + return std::any_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return ContainsRuntimeArray(*element_type); + }); + default: + return false; + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h new file mode 100644 index 0000000..c4f5103 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_inserts.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds new OpCompositeInsert instructions to +// available values that have the composite type. +class FuzzerPassAddCompositeInserts : public FuzzerPass { + public: + FuzzerPassAddCompositeInserts( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddCompositeInserts(); + void Apply() override; + + // Checks if any component of a composite is a pointer. + static bool ContainsPointer(const opt::analysis::Type& type); + + // Checks if any component of a composite has type OpTypeRuntimeArray. + static bool ContainsRuntimeArray(const opt::analysis::Type& type); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_INSERTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp new file mode 100644 index 0000000..c4d8d1c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_composite_types.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_type_array.h" +#include "source/fuzz/transformation_add_type_struct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCompositeTypes::FuzzerPassAddCompositeTypes( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddCompositeTypes::~FuzzerPassAddCompositeTypes() = default; + +void FuzzerPassAddCompositeTypes::Apply() { + MaybeAddMissingVectorTypes(); + MaybeAddMissingMatrixTypes(); + + // Randomly interleave between adding struct and array composite types + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingArrayOrStructType())) { + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfChoosingStructTypeVsArrayType())) { + AddNewStructType(); + } else { + AddNewArrayType(); + } + } +} + +void FuzzerPassAddCompositeTypes::MaybeAddMissingVectorTypes() { + // Functions to lazily supply scalar base types on demand if we decide to + // create vectors with the relevant base types. + std::function bool_type_supplier = [this]() -> uint32_t { + return FindOrCreateBoolType(); + }; + std::function float_type_supplier = [this]() -> uint32_t { + return FindOrCreateFloatType(32); + }; + std::function int_type_supplier = [this]() -> uint32_t { + return FindOrCreateIntegerType(32, true); + }; + std::function uint_type_supplier = [this]() -> uint32_t { + return FindOrCreateIntegerType(32, false); + }; + + // Consider each of the base types with which we can make vectors. + for (auto& base_type_supplier : {bool_type_supplier, float_type_supplier, + int_type_supplier, uint_type_supplier}) { + // Consider each valid vector size. + for (uint32_t size = 2; size <= 4; size++) { + // Randomly decide whether to create (if it does not already exist) a + // vector with this size and base type. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingVectorType())) { + FindOrCreateVectorType(base_type_supplier(), size); + } + } + } +} + +void FuzzerPassAddCompositeTypes::MaybeAddMissingMatrixTypes() { + // Consider every valid matrix dimension. + for (uint32_t columns = 2; columns <= 4; columns++) { + for (uint32_t rows = 2; rows <= 4; rows++) { + // Randomly decide whether to create (if it does not already exist) a + // matrix with these dimensions. As matrices can only have floating-point + // base type, we do not need to consider multiple base types as in the + // case for vectors. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingMatrixType())) { + FindOrCreateMatrixType(columns, rows); + } + } + } +} + +void FuzzerPassAddCompositeTypes::AddNewArrayType() { + ApplyTransformation(TransformationAddTypeArray( + GetFuzzerContext()->GetFreshId(), ChooseScalarOrCompositeType(), + FindOrCreateIntegerConstant( + {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, false))); +} + +void FuzzerPassAddCompositeTypes::AddNewStructType() { + std::vector field_type_ids; + do { + field_type_ids.push_back(ChooseScalarOrCompositeType()); + } while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingAnotherStructField())); + ApplyTransformation(TransformationAddTypeStruct( + GetFuzzerContext()->GetFreshId(), field_type_ids)); +} + +uint32_t FuzzerPassAddCompositeTypes::ChooseScalarOrCompositeType() { + // Gather up all the possibly-relevant types. + std::vector candidates; + for (auto& inst : GetIRContext()->types_values()) { + switch (inst.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeBool: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeVector: + candidates.push_back(inst.result_id()); + break; + case SpvOpTypeStruct: { + if (!fuzzerutil::MembersHaveBuiltInDecoration(GetIRContext(), + inst.result_id())) { + candidates.push_back(inst.result_id()); + } + } break; + default: + break; + } + } + assert(!candidates.empty() && + "This function should only be called if there is at least one scalar " + "or composite type available."); + // Return one of these types at random. + return candidates[GetFuzzerContext()->RandomIndex(candidates)]; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h new file mode 100644 index 0000000..87bc0ff --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_composite_types.h @@ -0,0 +1,61 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds missing vector and matrix types, and new +// array and struct types, to the module. +class FuzzerPassAddCompositeTypes : public FuzzerPass { + public: + FuzzerPassAddCompositeTypes( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddCompositeTypes(); + + void Apply() override; + + private: + // Creates an array of a random size with a random existing base type and adds + // it to the module. + void AddNewArrayType(); + + // Creates a struct with fields of random existing types and adds it to the + // module. + void AddNewStructType(); + + // For each vector type not already present in the module, randomly decides + // whether to add it to the module. + void MaybeAddMissingVectorTypes(); + + // For each matrix type not already present in the module, randomly decides + // whether to add it to the module. + void MaybeAddMissingMatrixTypes(); + + // Returns the id of a scalar or composite type declared in the module, + // chosen randomly. + uint32_t ChooseScalarOrCompositeType(); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COMPOSITE_TYPES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp new file mode 100644 index 0000000..d98619c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_copy_memory.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_copy_memory.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddCopyMemory::FuzzerPassAddCopyMemory( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddCopyMemory::~FuzzerPassAddCopyMemory() = default; + +void FuzzerPassAddCopyMemory::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + // Check that we can insert an OpCopyMemory before this instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory, + inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingCopyMemory())) { + return; + } + + // Get all instructions available before |inst_it| according to the + // domination rules. + auto instructions = FindAvailableInstructions( + function, block, inst_it, + TransformationAddCopyMemory::IsInstructionSupported); + + if (instructions.empty()) { + return; + } + + const auto* inst = + instructions[GetFuzzerContext()->RandomIndex(instructions)]; + + // Decide whether to create global or local variable. + auto storage_class = GetFuzzerContext()->ChooseEven() + ? SpvStorageClassPrivate + : SpvStorageClassFunction; + + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), inst->type_id()); + + // Create a pointer type with |storage_class| if needed. + FindOrCreatePointerType(pointee_type_id, storage_class); + + ApplyTransformation(TransformationAddCopyMemory( + instruction_descriptor, GetFuzzerContext()->GetFreshId(), + inst->result_id(), storage_class, + FindOrCreateZeroConstant(pointee_type_id, false))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h new file mode 100644 index 0000000..321e4a1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_copy_memory.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides whether to add OpCopyMemory before some instruction in the +// module. +class FuzzerPassAddCopyMemory : public FuzzerPass { + public: + FuzzerPassAddCopyMemory(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddCopyMemory() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_COPY_MEMORY_INSTRUCTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp new file mode 100644 index 0000000..84ed1fb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_dead_blocks.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_dead_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddDeadBlocks::FuzzerPassAddDeadBlocks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddDeadBlocks::~FuzzerPassAddDeadBlocks() = default; + +void FuzzerPassAddDeadBlocks::Apply() { + // We iterate over all blocks in the module collecting up those at which we + // might add a branch to a new dead block. We then loop over all such + // candidates and actually apply transformations. This separation is to + // avoid modifying the module as we traverse it. + std::vector candidate_transformations; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingDeadBlock())) { + continue; + } + + // Make sure the module contains a boolean constant equal to + // |condition_value|. + bool condition_value = GetFuzzerContext()->ChooseEven(); + FindOrCreateBoolConstant(condition_value, false); + + // We speculatively create a transformation, and then apply it (below) if + // it turns out to be applicable. This avoids duplicating the logic for + // applicability checking. + // + // It means that fresh ids for transformations that turn out not to be + // applicable end up being unused. + candidate_transformations.emplace_back(TransformationAddDeadBlock( + GetFuzzerContext()->GetFreshId(), block.id(), condition_value)); + } + } + // Apply all those transformations that are in fact applicable. + for (auto& transformation : candidate_transformations) { + MaybeApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h new file mode 100644 index 0000000..d78f088 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_blocks.h @@ -0,0 +1,40 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass to add dynamically unreachable blocks to the module. Future +// passes can then manipulate such blocks. +class FuzzerPassAddDeadBlocks : public FuzzerPass { + public: + FuzzerPassAddDeadBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddDeadBlocks(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BLOCKS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp new file mode 100644 index 0000000..e726c63 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_dead_breaks.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_dead_break.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddDeadBreaks::FuzzerPassAddDeadBreaks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddDeadBreaks::~FuzzerPassAddDeadBreaks() = default; + +void FuzzerPassAddDeadBreaks::Apply() { + // We first collect up lots of possibly-applicable transformations. + std::vector candidate_transformations; + // We consider each function separately. + for (auto& function : *GetIRContext()->module()) { + // For a given function, we find all the merge blocks in that function. + std::vector merge_blocks; + for (auto& block : function) { + auto maybe_merge_id = block.MergeBlockIdIfAny(); + if (maybe_merge_id) { + auto merge_block = + fuzzerutil::MaybeFindBlock(GetIRContext(), maybe_merge_id); + + assert(merge_block && "Merge block can't be null"); + + merge_blocks.push_back(merge_block); + } + } + // We rather aggressively consider the possibility of adding a break from + // every block in the function to every merge block. Many of these will be + // inapplicable as they would be illegal. That's OK - we later discard the + // ones that turn out to be no good. + for (auto& block : function) { + for (auto* merge_block : merge_blocks) { + // Populate this vector with ids that are available at the branch point + // of this basic block. We will use these ids to update OpPhi + // instructions later. + std::vector phi_ids; + + // Determine how we need to adjust OpPhi instructions' operands + // for this transformation to be valid. + // + // If |block| has a branch to |merge_block|, the latter must have all of + // its OpPhi instructions set up correctly - we don't need to adjust + // anything. + if (!block.IsSuccessor(merge_block)) { + merge_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) { + // Add an additional operand for OpPhi instruction. Use a constant + // if possible, and an undef otherwise. + if (fuzzerutil::CanCreateConstant(GetIRContext(), phi->type_id())) { + // We mark the constant as irrelevant so that we can replace it + // with a more interesting value later. + phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true)); + } else { + phi_ids.push_back(FindOrCreateGlobalUndef(phi->type_id())); + } + }); + } + + // Make sure the module has a required boolean constant to be used in + // OpBranchConditional instruction. + auto break_condition = GetFuzzerContext()->ChooseEven(); + FindOrCreateBoolConstant(break_condition, false); + + auto candidate_transformation = TransformationAddDeadBreak( + block.id(), merge_block->id(), break_condition, std::move(phi_ids)); + if (candidate_transformation.IsApplicable( + GetIRContext(), *GetTransformationContext())) { + // Only consider a transformation as a candidate if it is applicable. + candidate_transformations.push_back( + std::move(candidate_transformation)); + } + } + } + } + + // Go through the candidate transformations that were accumulated, + // probabilistically deciding whether to consider each one further and + // applying the still-applicable ones that are considered further. + // + // We iterate through the candidate transformations in a random order by + // repeatedly removing a random candidate transformation from the sequence + // until no candidate transformations remain. This is done because + // transformations can potentially disable one another, so that iterating + // through them in order would lead to a higher probability of + // transformations appearing early in the sequence being applied compared + // with later transformations. + while (!candidate_transformations.empty()) { + // Choose a random index into the sequence of remaining candidate + // transformations. + auto index = GetFuzzerContext()->RandomIndex(candidate_transformations); + // Remove the transformation at the chosen index from the sequence. + auto transformation = std::move(candidate_transformations[index]); + candidate_transformations.erase(candidate_transformations.begin() + index); + // Probabilistically decide whether to try to apply it vs. ignore it, in the + // case that it is applicable. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingDeadBreak())) { + MaybeApplyTransformation(transformation); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h new file mode 100644 index 0000000..c379eed --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_breaks.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for adding dead break edges to the module. +class FuzzerPassAddDeadBreaks : public FuzzerPass { + public: + FuzzerPassAddDeadBreaks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddDeadBreaks(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_BREAKS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp new file mode 100644 index 0000000..24617ae --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_dead_continues.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_dead_continue.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddDeadContinues::FuzzerPassAddDeadContinues( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddDeadContinues::~FuzzerPassAddDeadContinues() = default; + +void FuzzerPassAddDeadContinues::Apply() { + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // Get the label id of the continue target of the innermost loop. + auto continue_block_id = + block.IsLoopHeader() + ? block.ContinueBlockId() + : GetIRContext()->GetStructuredCFGAnalysis()->LoopContinueBlock( + block.id()); + + // This transformation is not applicable if current block is not inside a + // loop. + if (continue_block_id == 0) { + continue; + } + + auto* continue_block = + fuzzerutil::MaybeFindBlock(GetIRContext(), continue_block_id); + assert(continue_block && "Continue block is null"); + + // Analyze return type of each OpPhi instruction in the continue target + // and provide an id for the transformation if needed. + std::vector phi_ids; + // Check whether current block has an edge to the continue target. + // If this is the case, we don't need to do anything. + if (!block.IsSuccessor(continue_block)) { + continue_block->ForEachPhiInst([this, &phi_ids](opt::Instruction* phi) { + // Add an additional operand for OpPhi instruction. Use a constant + // if possible, and an undef otherwise. + if (fuzzerutil::CanCreateConstant(GetIRContext(), phi->type_id())) { + // We mark the constant as irrelevant so that we can replace it with + // a more interesting value later. + phi_ids.push_back(FindOrCreateZeroConstant(phi->type_id(), true)); + } else { + phi_ids.push_back(FindOrCreateGlobalUndef(phi->type_id())); + } + }); + } + + // Make sure the module contains a boolean constant equal to + // |condition_value|. + bool condition_value = GetFuzzerContext()->ChooseEven(); + FindOrCreateBoolConstant(condition_value, false); + + // Make a transformation to add a dead continue from this node; if the + // node turns out to be inappropriate (e.g. by not being in a loop) the + // precondition for the transformation will fail and it will be ignored. + auto candidate_transformation = TransformationAddDeadContinue( + block.id(), condition_value, std::move(phi_ids)); + // Probabilistically decide whether to apply the transformation in the + // case that it is applicable. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingDeadContinue())) { + MaybeApplyTransformation(candidate_transformation); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h new file mode 100644 index 0000000..b2acb93 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_dead_continues.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for adding dead continue edges to the module. +class FuzzerPassAddDeadContinues : public FuzzerPass { + public: + FuzzerPassAddDeadContinues( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddDeadContinues(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_DEAD_CONTINUES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp new file mode 100644 index 0000000..6376c9f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.cpp @@ -0,0 +1,414 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_equation_instructions.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_equation_instruction.h" + +namespace spvtools { +namespace fuzz { +namespace { + +bool IsBitWidthSupported(opt::IRContext* ir_context, uint32_t bit_width) { + switch (bit_width) { + case 32: + return true; + case 64: + return ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityFloat64) && + ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt64); + case 16: + return ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityFloat16) && + ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt16); + default: + return false; + } +} + +} // namespace + +FuzzerPassAddEquationInstructions::FuzzerPassAddEquationInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddEquationInstructions::~FuzzerPassAddEquationInstructions() = + default; + +void FuzzerPassAddEquationInstructions::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingEquationInstruction())) { + return; + } + + // Check that it is OK to add an equation instruction before the given + // instruction in principle - e.g. check that this does not lead to + // inserting before an OpVariable or OpPhi instruction. We use OpIAdd + // as an example opcode for this check, to be representative of *some* + // opcode that defines an equation, even though we may choose a + // different opcode below. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) { + return; + } + + // Get all available instructions with result ids and types that are not + // OpUndef. + std::vector available_instructions = + FindAvailableInstructions( + function, block, inst_it, + [this](opt::IRContext* /*unused*/, + opt::Instruction* instruction) -> bool { + return instruction->result_id() && instruction->type_id() && + instruction->opcode() != SpvOpUndef && + !GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()); + }); + + // Try the opcodes for which we know how to make ids at random until + // something works. + std::vector candidate_opcodes = { + SpvOpIAdd, SpvOpISub, SpvOpLogicalNot, SpvOpSNegate, + SpvOpConvertUToF, SpvOpConvertSToF, SpvOpBitcast}; + do { + auto opcode = + GetFuzzerContext()->RemoveAtRandomIndex(&candidate_opcodes); + switch (opcode) { + case SpvOpConvertSToF: + case SpvOpConvertUToF: { + std::vector candidate_instructions; + for (const auto* inst : + GetIntegerInstructions(available_instructions)) { + const auto* type = + GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + assert(type && "|inst| has invalid type"); + + if (const auto* vector_type = type->AsVector()) { + type = vector_type->element_type(); + } + + if (IsBitWidthSupported(GetIRContext(), + type->AsInteger()->width())) { + candidate_instructions.push_back(inst); + } + } + + if (candidate_instructions.empty()) { + break; + } + + const auto* operand = + candidate_instructions[GetFuzzerContext()->RandomIndex( + candidate_instructions)]; + + const auto* type = + GetIRContext()->get_type_mgr()->GetType(operand->type_id()); + assert(type && "Operand has invalid type"); + + // Make sure a result type exists in the module. + if (const auto* vector = type->AsVector()) { + // We store element count in a separate variable since the + // call FindOrCreate* functions below might invalidate + // |vector| pointer. + const auto element_count = vector->element_count(); + + FindOrCreateVectorType( + FindOrCreateFloatType( + vector->element_type()->AsInteger()->width()), + element_count); + } else { + FindOrCreateFloatType(type->AsInteger()->width()); + } + + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {operand->result_id()}, instruction_descriptor)); + return; + } + case SpvOpBitcast: { + const auto candidate_instructions = + GetNumericalInstructions(available_instructions); + + if (!candidate_instructions.empty()) { + const auto* operand_inst = + candidate_instructions[GetFuzzerContext()->RandomIndex( + candidate_instructions)]; + const auto* operand_type = + GetIRContext()->get_type_mgr()->GetType( + operand_inst->type_id()); + assert(operand_type && "Operand instruction has invalid type"); + + // Make sure a result type exists in the module. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539): + // The only constraint on the types of OpBitcast's parameters + // is that they must have the same number of bits. Consider + // improving the code below to support this in full. + if (const auto* vector = operand_type->AsVector()) { + // We store element count in a separate variable since the + // call FindOrCreate* functions below might invalidate + // |vector| pointer. + const auto element_count = vector->element_count(); + + uint32_t element_type_id; + if (const auto* int_type = + vector->element_type()->AsInteger()) { + element_type_id = FindOrCreateFloatType(int_type->width()); + } else { + assert(vector->element_type()->AsFloat() && + "Vector must have numerical elements"); + element_type_id = FindOrCreateIntegerType( + vector->element_type()->AsFloat()->width(), + GetFuzzerContext()->ChooseEven()); + } + + FindOrCreateVectorType(element_type_id, element_count); + } else if (const auto* int_type = operand_type->AsInteger()) { + FindOrCreateFloatType(int_type->width()); + } else { + assert(operand_type->AsFloat() && + "Operand is not a scalar of numerical type"); + FindOrCreateIntegerType(operand_type->AsFloat()->width(), + GetFuzzerContext()->ChooseEven()); + } + + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {operand_inst->result_id()}, instruction_descriptor)); + return; + } + } break; + case SpvOpIAdd: + case SpvOpISub: { + // Instructions of integer (scalar or vector) result type are + // suitable for these opcodes. + auto integer_instructions = + GetIntegerInstructions(available_instructions); + if (!integer_instructions.empty()) { + // There is at least one such instruction, so pick one at random + // for the LHS of an equation. + auto lhs = integer_instructions.at( + GetFuzzerContext()->RandomIndex(integer_instructions)); + + // For the RHS, we can use any instruction with an integer + // scalar/vector result type of the same number of components + // and the same bit-width for the underlying integer type. + + // Work out the element count and bit-width. + auto lhs_type = + GetIRContext()->get_type_mgr()->GetType(lhs->type_id()); + uint32_t lhs_element_count; + uint32_t lhs_bit_width; + if (lhs_type->AsVector()) { + lhs_element_count = lhs_type->AsVector()->element_count(); + lhs_bit_width = lhs_type->AsVector() + ->element_type() + ->AsInteger() + ->width(); + } else { + lhs_element_count = 1; + lhs_bit_width = lhs_type->AsInteger()->width(); + } + + // Get all the instructions that match on element count and + // bit-width. + auto candidate_rhs_instructions = RestrictToElementBitWidth( + RestrictToVectorWidth(integer_instructions, + lhs_element_count), + lhs_bit_width); + + // Choose a RHS instruction at random; there is guaranteed to + // be at least one choice as the LHS will be available. + auto rhs = candidate_rhs_instructions.at( + GetFuzzerContext()->RandomIndex( + candidate_rhs_instructions)); + + // Add the equation instruction. + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {lhs->result_id(), rhs->result_id()}, + instruction_descriptor)); + return; + } + break; + } + case SpvOpLogicalNot: { + // Choose any available instruction of boolean scalar/vector + // result type and equate its negation with a fresh id. + auto boolean_instructions = + GetBooleanInstructions(available_instructions); + if (!boolean_instructions.empty()) { + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {boolean_instructions + .at(GetFuzzerContext()->RandomIndex( + boolean_instructions)) + ->result_id()}, + instruction_descriptor)); + return; + } + break; + } + case SpvOpSNegate: { + // Similar to OpLogicalNot, but for signed integer negation. + auto integer_instructions = + GetIntegerInstructions(available_instructions); + if (!integer_instructions.empty()) { + ApplyTransformation(TransformationEquationInstruction( + GetFuzzerContext()->GetFreshId(), opcode, + {integer_instructions + .at(GetFuzzerContext()->RandomIndex( + integer_instructions)) + ->result_id()}, + instruction_descriptor)); + return; + } + break; + } + default: + assert(false && "Unexpected opcode."); + break; + } + } while (!candidate_opcodes.empty()); + // Reaching here means that we did not manage to apply any + // transformation at this point of the module. + }); +} + +std::vector +FuzzerPassAddEquationInstructions::GetIntegerInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsInteger() || + (type->AsVector() && type->AsVector()->element_type()->AsInteger())) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::GetFloatInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsFloat() || + (type->AsVector() && type->AsVector()->element_type()->AsFloat())) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::GetBooleanInstructions( + const std::vector& instructions) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsBool() || + (type->AsVector() && type->AsVector()->element_type()->AsBool())) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::RestrictToVectorWidth( + const std::vector& instructions, + uint32_t vector_width) const { + std::vector result; + for (auto& inst : instructions) { + auto type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + // Get the vector width of |inst|, which is 1 if |inst| is a scalar and is + // otherwise derived from its vector type. + uint32_t other_vector_width = + type->AsVector() ? type->AsVector()->element_count() : 1; + // Keep |inst| if the vector widths match. + if (vector_width == other_vector_width) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::RestrictToElementBitWidth( + const std::vector& instructions, + uint32_t bit_width) const { + std::vector result; + for (auto& inst : instructions) { + const opt::analysis::Type* type = + GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsVector()) { + type = type->AsVector()->element_type(); + } + assert((type->AsInteger() || type->AsFloat()) && + "Precondition: all input instructions must " + "have integer or float scalar or vector type."); + if ((type->AsInteger() && type->AsInteger()->width() == bit_width) || + (type->AsFloat() && type->AsFloat()->width() == bit_width)) { + result.push_back(inst); + } + } + return result; +} + +std::vector +FuzzerPassAddEquationInstructions::GetNumericalInstructions( + const std::vector& instructions) const { + std::vector result; + + for (auto* inst : instructions) { + const auto* type = GetIRContext()->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction has invalid type"); + + if (const auto* vector_type = type->AsVector()) { + type = vector_type->element_type(); + } + + if (!type->AsInteger() && !type->AsFloat()) { + // Only numerical scalars or vectors of numerical components are + // supported. + continue; + } + + if (!IsBitWidthSupported(GetIRContext(), type->AsInteger() + ? type->AsInteger()->width() + : type->AsFloat()->width())) { + continue; + } + + result.push_back(inst); + } + + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h new file mode 100644 index 0000000..9ce581e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_equation_instructions.h @@ -0,0 +1,80 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that sprinkles instructions through the module that define +// equations using various arithmetic and logical operators. +class FuzzerPassAddEquationInstructions : public FuzzerPass { + public: + FuzzerPassAddEquationInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddEquationInstructions(); + + void Apply() override; + + private: + // Yields those instructions in |instructions| that have integer scalar or + // vector result type. + std::vector GetIntegerInstructions( + const std::vector& instructions) const; + + // Returns only instructions, that have either a scalar floating-point or a + // vector type. + std::vector GetFloatInstructions( + const std::vector& instructions) const; + + // Yields those instructions in |instructions| that have boolean scalar or + // vector result type. + std::vector GetBooleanInstructions( + const std::vector& instructions) const; + + // Yields those instructions in |instructions| that have a scalar numerical or + // a vector of numerical components type. Only 16, 32 and 64-bit numericals + // are supported if both OpTypeInt and OpTypeFloat instructions can be created + // with the specified width (e.g. for 16-bit types both Float16 and Int16 + // capabilities must be present). + std::vector GetNumericalInstructions( + const std::vector& instructions) const; + + // Requires that |instructions| are scalars or vectors of some type. Returns + // only those instructions whose width is |width|. If |width| is 1 this means + // the scalars. + std::vector RestrictToVectorWidth( + const std::vector& instructions, + uint32_t vector_width) const; + + // Requires that |instructions| are integer or float scalars or vectors. + // Returns only those instructions for which the bit-width of the underlying + // integer or floating-point type is |bit_width|. + std::vector RestrictToElementBitWidth( + const std::vector& instructions, + uint32_t bit_width) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_EQUATION_INSTRUCTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp new file mode 100644 index 0000000..7400557 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.cpp @@ -0,0 +1,203 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_function_calls.h" + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_function_call.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddFunctionCalls::FuzzerPassAddFunctionCalls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddFunctionCalls::~FuzzerPassAddFunctionCalls() = default; + +void FuzzerPassAddFunctionCalls::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + // Check whether it is legitimate to insert a function call before the + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall, + inst_it)) { + return; + } + + // Randomly decide whether to try inserting a function call here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfCallingFunction())) { + return; + } + + // Compute the module's call graph - we don't cache it since it may + // change each time we apply a transformation. If this proves to be + // a bottleneck the call graph data structure could be made updatable. + CallGraph call_graph(GetIRContext()); + + // Gather all the non-entry point functions different from this + // function. It is important to ignore entry points as a function + // cannot be an entry point and the target of an OpFunctionCall + // instruction. We ignore this function to avoid direct recursion. + std::vector candidate_functions; + for (auto& other_function : *GetIRContext()->module()) { + if (&other_function != function && + !fuzzerutil::FunctionIsEntryPoint(GetIRContext(), + other_function.result_id())) { + candidate_functions.push_back(&other_function); + } + } + + // Choose a function to call, at random, by considering candidate + // functions until a suitable one is found. + opt::Function* chosen_function = nullptr; + while (!candidate_functions.empty()) { + opt::Function* candidate_function = + GetFuzzerContext()->RemoveAtRandomIndex(&candidate_functions); + if (!GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id()) && + !GetTransformationContext()->GetFactManager()->FunctionIsLivesafe( + candidate_function->result_id())) { + // Unless in a dead block, only livesafe functions can be invoked + continue; + } + if (call_graph.GetIndirectCallees(candidate_function->result_id()) + .count(function->result_id())) { + // Calling this function could lead to indirect recursion + continue; + } + chosen_function = candidate_function; + break; + } + + if (!chosen_function) { + // No suitable function was found to call. (This can happen, for + // instance, if the current function is the only function in the + // module.) + return; + } + + ApplyTransformation(TransformationFunctionCall( + GetFuzzerContext()->GetFreshId(), chosen_function->result_id(), + ChooseFunctionCallArguments(*chosen_function, function, block, + inst_it), + instruction_descriptor)); + }); +} + +std::vector FuzzerPassAddFunctionCalls::ChooseFunctionCallArguments( + const opt::Function& callee, opt::Function* caller_function, + opt::BasicBlock* caller_block, + const opt::BasicBlock::iterator& caller_inst_it) { + auto available_pointers = FindAvailableInstructions( + caller_function, caller_block, caller_inst_it, + [this, caller_block](opt::IRContext* /*unused*/, opt::Instruction* inst) { + if (inst->opcode() != SpvOpVariable || + inst->opcode() != SpvOpFunctionParameter) { + // Function parameters and variables are the only + // kinds of pointer that can be used as actual + // parameters. + return false; + } + + return GetTransformationContext()->GetFactManager()->BlockIsDead( + caller_block->id()) || + GetTransformationContext() + ->GetFactManager() + ->PointeeValueIsIrrelevant(inst->result_id()); + }); + + std::unordered_map> type_id_to_result_id; + for (const auto* inst : available_pointers) { + type_id_to_result_id[inst->type_id()].push_back(inst->result_id()); + } + + std::vector result; + for (const auto* param : + fuzzerutil::GetParameters(GetIRContext(), callee.result_id())) { + const auto* param_type = + GetIRContext()->get_type_mgr()->GetType(param->type_id()); + assert(param_type && "Parameter has invalid type"); + + if (!param_type->AsPointer()) { + if (fuzzerutil::CanCreateConstant(GetIRContext(), param->type_id())) { + // We mark the constant as irrelevant so that we can replace it with a + // more interesting value later. + result.push_back(FindOrCreateZeroConstant(param->type_id(), true)); + } else { + result.push_back(FindOrCreateGlobalUndef(param->type_id())); + } + continue; + } + + if (type_id_to_result_id.count(param->type_id())) { + // Use an existing pointer if there are any. + const auto& candidates = type_id_to_result_id[param->type_id()]; + result.push_back(candidates[GetFuzzerContext()->RandomIndex(candidates)]); + continue; + } + + // Make a new variable, at function or global scope depending on the storage + // class of the pointer. + + // Get a fresh id for the new variable. + uint32_t fresh_variable_id = GetFuzzerContext()->GetFreshId(); + + // The id of this variable is what we pass as the parameter to + // the call. + result.push_back(fresh_variable_id); + type_id_to_result_id[param->type_id()].push_back(fresh_variable_id); + + // Now bring the variable into existence. + auto storage_class = param_type->AsPointer()->storage_class(); + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), param->type_id()); + if (storage_class == SpvStorageClassFunction) { + // Add a new zero-initialized local variable to the current + // function, noting that its pointee value is irrelevant. + ApplyTransformation(TransformationAddLocalVariable( + fresh_variable_id, param->type_id(), caller_function->result_id(), + FindOrCreateZeroConstant(pointee_type_id, false), true)); + } else { + assert((storage_class == SpvStorageClassPrivate || + storage_class == SpvStorageClassWorkgroup) && + "Only Function, Private and Workgroup storage classes are " + "supported at present."); + // Add a new global variable to the module, zero-initializing it if + // it has Private storage class, and noting that its pointee value is + // irrelevant. + ApplyTransformation(TransformationAddGlobalVariable( + fresh_variable_id, param->type_id(), storage_class, + storage_class == SpvStorageClassPrivate + ? FindOrCreateZeroConstant(pointee_type_id, false) + : 0, + true)); + } + } + + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h new file mode 100644 index 0000000..4ed8791 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_function_calls.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds calls at random to (a) livesafe functions, from +// anywhere, and (b) any functions, from dead blocks. +class FuzzerPassAddFunctionCalls : public FuzzerPass { + public: + FuzzerPassAddFunctionCalls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddFunctionCalls(); + + void Apply() override; + + private: + // Randomly chooses suitable arguments to invoke |callee| right before + // instruction |caller_inst_it| of block |caller_block| in |caller_function|, + // based on both existing available instructions and the addition of new + // instructions to the module. + std::vector ChooseFunctionCallArguments( + const opt::Function& callee, opt::Function* caller_function, + opt::BasicBlock* caller_block, + const opt::BasicBlock::iterator& caller_inst_it); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_FUNCTION_CALLS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp new file mode 100644 index 0000000..9a45a37 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_global_variables.h" + +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_type_pointer.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddGlobalVariables::FuzzerPassAddGlobalVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddGlobalVariables::~FuzzerPassAddGlobalVariables() = default; + +void FuzzerPassAddGlobalVariables::Apply() { + SpvStorageClass variable_storage_class = SpvStorageClassPrivate; + for (auto& entry_point : GetIRContext()->module()->entry_points()) { + // If the execution model of some entry point is GLCompute, + // then the variable storage class may be Workgroup. + if (entry_point.GetSingleWordInOperand(0) == SpvExecutionModelGLCompute) { + variable_storage_class = + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfChoosingWorkgroupStorageClass()) + ? SpvStorageClassWorkgroup + : SpvStorageClassPrivate; + break; + } + } + + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(variable_storage_class); + + // These are the basic types that are available to this fuzzer pass. + auto& basic_types = basic_type_ids_and_pointers.first; + + // These are the pointers to those basic types that are *initially* available + // to the fuzzer pass. The fuzzer pass might add pointer types in cases where + // none are available for a given basic type. + auto& basic_type_to_pointers = basic_type_ids_and_pointers.second; + + // Probabilistically keep adding global variables. + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingGlobalVariable())) { + // Choose a random basic type; the new variable's type will be a pointer to + // this basic type. + uint32_t basic_type = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; + uint32_t pointer_type_id; + std::vector& available_pointers_to_basic_type = + basic_type_to_pointers.at(basic_type); + // Determine whether there is at least one pointer to this basic type. + if (available_pointers_to_basic_type.empty()) { + // There is not. Make one, to use here, and add it to the available + // pointers for the basic type so that future variables can potentially + // use it. + pointer_type_id = GetFuzzerContext()->GetFreshId(); + available_pointers_to_basic_type.push_back(pointer_type_id); + ApplyTransformation(TransformationAddTypePointer( + pointer_type_id, variable_storage_class, basic_type)); + } else { + // There is - grab one. + pointer_type_id = + available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_basic_type)]; + } + + ApplyTransformation(TransformationAddGlobalVariable( + GetFuzzerContext()->GetFreshId(), pointer_type_id, + variable_storage_class, + variable_storage_class == SpvStorageClassPrivate + ? FindOrCreateZeroConstant(basic_type, false) + : 0, + true)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h new file mode 100644 index 0000000..a907d36 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_global_variables.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds global variables, with Private storage class, +// to the module. +class FuzzerPassAddGlobalVariables : public FuzzerPass { + public: + FuzzerPassAddGlobalVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddGlobalVariables(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_GLOBAL_VARIABLES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp new file mode 100644 index 0000000..3095bb6 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_image_sample_unused_components.h" +#include "source/fuzz/transformation_composite_construct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddImageSampleUnusedComponents:: + FuzzerPassAddImageSampleUnusedComponents( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddImageSampleUnusedComponents:: + ~FuzzerPassAddImageSampleUnusedComponents() = default; + +void FuzzerPassAddImageSampleUnusedComponents::Apply() { + // SPIR-V module to help understand the transformation. + // + // OpCapability Shader + // %1 = OpExtInstImport "GLSL.std.450" + // OpMemoryModel Logical GLSL450 + // OpEntryPoint Fragment %15 "main" %12 %14 + // OpExecutionMode %15 OriginUpperLeft + // + // ; Decorations + // OpDecorate %12 Location 0 ; Input color variable location + // OpDecorate %13 DescriptorSet 0 ; Image coordinate variable + // descriptor set OpDecorate %13 Binding 0 ; Image coordinate + // variable binding OpDecorate %14 Location 0 ; Fragment color + // variable location + // + // ; Types + // %2 = OpTypeVoid + // %3 = OpTypeFunction %2 + // %4 = OpTypeFloat 32 + // %5 = OpTypeVector %4 2 + // %6 = OpTypeVector %4 4 + // %7 = OpTypeImage %4 2D 0 0 0 1 Rgba32f + // %8 = OpTypeSampledImage %7 + // %9 = OpTypePointer Input %5 + // %10 = OpTypePointer UniformConstant %8 + // %11 = OpTypePointer Output %6 + // + // ; Variables + // %12 = OpVariable %9 Input ; Input image coordinate variable + // %13 = OpVariable %10 UniformConstant ; Image variable + // %14 = OpVariable %11 Output ; Fragment color variable + // + // ; main function + // %15 = OpFunction %2 None %3 + // %16 = OpLabel + // %17 = OpLoad %5 %12 + // %18 = OpLoad %8 %13 + // %19 = OpImageSampleImplicitLod %6 %18 %17 + // OpStore %14 %19 + // OpReturn + // OpFunctionEnd + + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // |instruction| %19 = OpImageSampleImplicitLod %6 %18 %17 + if (!spvOpcodeIsImageSample(instruction->opcode())) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingImageSampleUnusedComponents())) { + return; + } + + // Gets image sample coordinate information. + // |coordinate_instruction| %17 = OpLoad %5 %12 + uint32_t coordinate_id = instruction->GetSingleWordInOperand(1); + auto coordinate_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(coordinate_id); + auto coordinate_type = GetIRContext()->get_type_mgr()->GetType( + coordinate_instruction->type_id()); + + // If the coordinate is a 4-dimensional vector, then no unused components + // may be added. + if (coordinate_type->AsVector() && + coordinate_type->AsVector()->element_count() == 4) { + return; + } + + // If the coordinate is a scalar, then at most 3 unused components may be + // added. If the coordinate is a vector, then the maximum number of unused + // components depends on the vector size. + // For the sample module, the coordinate type instruction is %5 = + // OpTypeVector %4 2, thus |max_unused_component_count| = 4 - 2 = 2. + uint32_t max_unused_component_count = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? 3 + : 4 - coordinate_type->AsVector()->element_count(); + + // |unused_component_count| may be 1 or 2. + uint32_t unused_component_count = + GetFuzzerContext()->GetRandomUnusedComponentCountForImageSample( + max_unused_component_count); + + // Gets a type for the zero-unused components. + uint32_t zero_constant_type_id; + switch (unused_component_count) { + case 1: + // If the coordinate is an integer or float, then the unused components + // type is the same as the coordinate. If the coordinate is a vector, + // then the unused components type is the same as the vector components + // type. + zero_constant_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? coordinate_instruction->type_id() + : GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()); + break; + case 2: + case 3: + // If the coordinate is an integer or float, then the unused components + // type is the same as the coordinate. If the coordinate is a vector, + // then the unused components type is the same as the coordinate + // components type. + // |zero_constant_type_id| %5 = OpTypeVector %4 2 + zero_constant_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? FindOrCreateVectorType(coordinate_instruction->type_id(), + unused_component_count) + : FindOrCreateVectorType( + GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()), + unused_component_count); + break; + default: + assert(false && "Should be unreachable."); + zero_constant_type_id = 0; + break; + } + + // Gets |coordinate_type| again because the module may have changed due to + // the use of FindOrCreateVectorType above. + coordinate_type = GetIRContext()->get_type_mgr()->GetType( + coordinate_instruction->type_id()); + + // If the new vector type with unused components does not exist, then create + // it. |coordinate_with_unused_components_type_id| %6 = OpTypeVector %4 4 + uint32_t coordinate_with_unused_components_type_id = + coordinate_type->AsInteger() || coordinate_type->AsFloat() + ? FindOrCreateVectorType(coordinate_instruction->type_id(), + 1 + unused_component_count) + : FindOrCreateVectorType( + GetIRContext()->get_type_mgr()->GetId( + coordinate_type->AsVector()->element_type()), + coordinate_type->AsVector()->element_count() + + unused_component_count); + + // Inserts an OpCompositeConstruct instruction which + // represents the coordinate with unused components. + // |coordinate_with_unused_components_id| + // %22 = OpCompositeConstruct %6 %17 %21 + uint32_t coordinate_with_unused_components_id = + GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationCompositeConstruct( + coordinate_with_unused_components_type_id, + {coordinate_instruction->result_id(), + // FindOrCreateZeroConstant + // %20 = OpConstant %4 0 + // %21 = OpConstantComposite %5 %20 %20 + FindOrCreateZeroConstant(zero_constant_type_id, true)}, + MakeInstructionDescriptor(GetIRContext(), instruction), + coordinate_with_unused_components_id)); + + // Tries to add unused components to the image sample coordinate. + // %19 = OpImageSampleImplicitLod %6 %18 %22 + ApplyTransformation(TransformationAddImageSampleUnusedComponents( + coordinate_with_unused_components_id, + MakeInstructionDescriptor(GetIRContext(), instruction))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h new file mode 100644 index 0000000..26374c3 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_image_sample_unused_components.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for image sample instructions in the module and +// randomly applies the transformation to add unused components to the image +// sample coordinate. +class FuzzerPassAddImageSampleUnusedComponents : public FuzzerPass { + public: + FuzzerPassAddImageSampleUnusedComponents( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddImageSampleUnusedComponents(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp new file mode 100644 index 0000000..256255b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_loads.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_load.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddLoads::FuzzerPassAddLoads( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddLoads::~FuzzerPassAddLoads() = default; + +void FuzzerPassAddLoads::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(inst_it->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Check whether it is legitimate to insert a load before this + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) { + return; + } + + // Randomly decide whether to try inserting a load here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingLoad())) { + return; + } + + std::vector relevant_instructions = + FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + switch (instruction->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + // Do not allow loading from a null or undefined pointer; + // this might be OK if the block is dead, but for now we + // conservatively avoid it. + return false; + default: + break; + } + return context->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == SpvOpTypePointer; + }); + + // At this point, |relevant_instructions| contains all the pointers + // we might think of loading from. + if (relevant_instructions.empty()) { + return; + } + + // Choose a pointer at random, and create and apply a loading + // transformation based on it. + ApplyTransformation(TransformationLoad( + GetFuzzerContext()->GetFreshId(), + relevant_instructions[GetFuzzerContext()->RandomIndex( + relevant_instructions)] + ->result_id(), + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h new file mode 100644 index 0000000..c4d5b27 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loads.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds stores, at random, from pointers in the module. +class FuzzerPassAddLoads : public FuzzerPass { + public: + FuzzerPassAddLoads(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddLoads(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOADS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp new file mode 100644 index 0000000..ef8b5d0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_local_variables.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_add_type_pointer.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddLocalVariables::FuzzerPassAddLocalVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddLocalVariables::~FuzzerPassAddLocalVariables() = default; + +void FuzzerPassAddLocalVariables::Apply() { + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(SpvStorageClassFunction); + + // These are the basic types that are available to this fuzzer pass. + auto& basic_types = basic_type_ids_and_pointers.first; + + // These are the pointers to those basic types that are *initially* available + // to the fuzzer pass. The fuzzer pass might add pointer types in cases where + // none are available for a given basic type. + auto& basic_type_to_pointers = basic_type_ids_and_pointers.second; + + // Consider every function in the module. + for (auto& function : *GetIRContext()->module()) { + // Probabilistically keep adding random variables to this function. + while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingLocalVariable())) { + // Choose a random basic type; the new variable's type will be a pointer + // to this basic type. + uint32_t basic_type = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; + uint32_t pointer_type; + std::vector& available_pointers_to_basic_type = + basic_type_to_pointers.at(basic_type); + // Determine whether there is at least one pointer to this basic type. + if (available_pointers_to_basic_type.empty()) { + // There is not. Make one, to use here, and add it to the available + // pointers for the basic type so that future variables can potentially + // use it. + pointer_type = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypePointer( + pointer_type, SpvStorageClassFunction, basic_type)); + available_pointers_to_basic_type.push_back(pointer_type); + } else { + // There is - grab one. + pointer_type = + available_pointers_to_basic_type[GetFuzzerContext()->RandomIndex( + available_pointers_to_basic_type)]; + } + ApplyTransformation(TransformationAddLocalVariable( + GetFuzzerContext()->GetFreshId(), pointer_type, function.result_id(), + FindOrCreateZeroConstant(basic_type, false), true)); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h new file mode 100644 index 0000000..08d26d8 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_local_variables.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that randomly adds local variables, with Function storage class, +// to the module. +class FuzzerPassAddLocalVariables : public FuzzerPass { + public: + FuzzerPassAddLocalVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddLocalVariables(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOCAL_VARIABLES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp new file mode 100644 index 0000000..bdc3151 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_loop_preheader.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddLoopPreheaders::FuzzerPassAddLoopPreheaders( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddLoopPreheaders::~FuzzerPassAddLoopPreheaders() = default; + +void FuzzerPassAddLoopPreheaders::Apply() { + for (auto& function : *GetIRContext()->module()) { + // Keep track of all the loop headers we want to add a preheader to. + std::vector loop_header_ids_to_consider; + for (auto& block : function) { + // We only care about loop headers. + if (!block.IsLoopHeader()) { + continue; + } + + // Randomly decide whether to consider this header. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingLoopPreheader())) { + continue; + } + + // We exclude loop headers with just one predecessor (the back-edge block) + // because they are unreachable. + if (GetIRContext()->cfg()->preds(block.id()).size() < 2) { + continue; + } + + loop_header_ids_to_consider.push_back(block.id()); + } + + for (uint32_t header_id : loop_header_ids_to_consider) { + // If not already present, add a preheader which is not also a loop + // header. + GetOrCreateSimpleLoopPreheader(header_id); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h new file mode 100644 index 0000000..a835056 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loop_preheaders.h @@ -0,0 +1,43 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that randomly adds simple loop preheaders to loops that do not +// have one. A simple loop preheader is a block that: +// - is the only out-of-loop predecessor of the header +// - branches unconditionally to the header +// - is not a loop header itself +class FuzzerPassAddLoopPreheaders : public FuzzerPass { + public: + FuzzerPassAddLoopPreheaders( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddLoopPreheaders(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOOP_PREHEADERS_H diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp new file mode 100644 index 0000000..31a5779 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.cpp @@ -0,0 +1,258 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h" + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h" + +namespace spvtools { +namespace fuzz { +namespace { +uint32_t kMaxNestingDepth = 4; +} // namespace + +FuzzerPassAddLoopsToCreateIntConstantSynonyms:: + FuzzerPassAddLoopsToCreateIntConstantSynonyms( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddLoopsToCreateIntConstantSynonyms:: + ~FuzzerPassAddLoopsToCreateIntConstantSynonyms() = default; + +void FuzzerPassAddLoopsToCreateIntConstantSynonyms::Apply() { + std::vector constants; + + // Choose the constants for which to create synonyms. + for (auto constant_def : GetIRContext()->GetConstants()) { + // Randomly decide whether to consider this constant. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfCreatingIntSynonymsUsingLoops())) { + continue; + } + + auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant( + constant_def->result_id()); + + // We do not consider irrelevant constants + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + constant_def->result_id())) { + continue; + } + + // We only consider integer constants (scalar or vector). + if (!constant->AsIntConstant() && + !(constant->AsVectorConstant() && + constant->AsVectorConstant()->component_type()->AsInteger())) { + continue; + } + + constants.push_back(constant_def->result_id()); + } + + std::vector blocks; + + // Get a list of all the blocks before which we can add a loop creating a new + // synonym. We cannot apply the transformation while iterating over the + // module, because we are going to add new blocks. + for (auto& function : *GetIRContext()->module()) { + // Consider all non-dead blocks reachable from the first block of the + // function. + GetIRContext()->cfg()->ForEachBlockInPostOrder( + &*function.begin(), [this, &blocks](opt::BasicBlock* block) { + if (!GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id())) { + blocks.push_back(block->id()); + } + }); + } + + // Make sure that the module has an OpTypeBool instruction, and 32-bit signed + // integer constants 0 and 1, adding them if necessary. + FindOrCreateBoolType(); + FindOrCreateIntegerConstant({0}, 32, true, false); + FindOrCreateIntegerConstant({1}, 32, true, false); + + // Compute the call graph. We can use this for any further computation, since + // we are not adding or removing functions or function calls. + auto call_graph = CallGraph(GetIRContext()); + + // Consider each constant and each block. + for (uint32_t constant_id : constants) { + // Choose one of the blocks. + uint32_t block_id = blocks[GetFuzzerContext()->RandomIndex(blocks)]; + + // Adjust the block so that the transformation can be applied. + auto block = GetIRContext()->get_instr_block(block_id); + + // If the block is a loop header, add a simple preheader. We can do this + // because we have excluded all the non-reachable headers. + if (block->IsLoopHeader()) { + block = GetOrCreateSimpleLoopPreheader(block->id()); + block_id = block->id(); + } + + assert(!block->IsLoopHeader() && + "The block cannot be a loop header at this point."); + + // If the block is a merge block, a continue block or it does not have + // exactly 1 predecessor, split it after any OpPhi or OpVariable + // instructions. + if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(block->id()) || + GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock( + block->id()) || + GetIRContext()->cfg()->preds(block->id()).size() != 1) { + block = SplitBlockAfterOpPhiOrOpVariable(block->id()); + block_id = block->id(); + } + + // Randomly decide the values for the number of iterations and the step + // value, and compute the initial value accordingly. + + // The maximum number of iterations depends on the maximum possible loop + // nesting depth of the block, computed interprocedurally, i.e. also + // considering the possibility that the enclosing function is called inside + // a loop. It is: + // - 1 if the nesting depth is >= kMaxNestingDepth + // - 2^(kMaxNestingDepth - nesting_depth) otherwise + uint32_t max_nesting_depth = + call_graph.GetMaxCallNestingDepth(block->GetParent()->result_id()) + + GetIRContext()->GetStructuredCFGAnalysis()->LoopNestingDepth( + block->id()); + uint32_t num_iterations = + max_nesting_depth >= kMaxNestingDepth + ? 1 + : GetFuzzerContext()->GetRandomNumberOfLoopIterations( + 1u << (kMaxNestingDepth - max_nesting_depth)); + + // Find or create the corresponding constant containing the number of + // iterations. + uint32_t num_iterations_id = + FindOrCreateIntegerConstant({num_iterations}, 32, true, false); + + // Find the other constants. + // We use 64-bit values and then use the bits that we need. We find the + // step value (S) randomly and then compute the initial value (I) using + // the equation I = C + S*N. + uint32_t initial_value_id = 0; + uint32_t step_value_id = 0; + + // Get the content of the existing constant. + const auto constant = + GetIRContext()->get_constant_mgr()->FindDeclaredConstant(constant_id); + const auto constant_type_id = + GetIRContext()->get_def_use_mgr()->GetDef(constant_id)->type_id(); + + if (constant->AsIntConstant()) { + // The constant is a scalar integer. + + std::tie(initial_value_id, step_value_id) = + FindSuitableStepAndInitialValueConstants( + constant->GetZeroExtendedValue(), + constant->type()->AsInteger()->width(), + constant->type()->AsInteger()->IsSigned(), num_iterations); + } else { + // The constant is a vector of integers. + assert(constant->AsVectorConstant() && + constant->AsVectorConstant()->component_type()->AsInteger() && + "If the program got here, the constant should be a vector of " + "integers."); + + // Find a constant for each component of the initial value and the step + // values. + std::vector initial_value_component_ids; + std::vector step_value_component_ids; + + // Get the value, width and signedness of the components. + std::vector component_values; + for (auto component : constant->AsVectorConstant()->GetComponents()) { + component_values.push_back(component->GetZeroExtendedValue()); + } + uint32_t bit_width = + constant->AsVectorConstant()->component_type()->AsInteger()->width(); + uint32_t is_signed = constant->AsVectorConstant() + ->component_type() + ->AsInteger() + ->IsSigned(); + + for (uint64_t component_val : component_values) { + uint32_t initial_val_id; + uint32_t step_val_id; + std::tie(initial_val_id, step_val_id) = + FindSuitableStepAndInitialValueConstants(component_val, bit_width, + is_signed, num_iterations); + initial_value_component_ids.push_back(initial_val_id); + step_value_component_ids.push_back(step_val_id); + } + + // Find or create the vector constants. + initial_value_id = FindOrCreateCompositeConstant( + initial_value_component_ids, constant_type_id, false); + step_value_id = FindOrCreateCompositeConstant(step_value_component_ids, + constant_type_id, false); + } + + assert(initial_value_id && step_value_id && + "|initial_value_id| and |step_value_id| should have been defined."); + + // Randomly decide whether to have two blocks (or just one) in the new + // loop. + uint32_t additional_block_id = + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfHavingTwoBlocksInLoopToCreateIntSynonym()) + ? GetFuzzerContext()->GetFreshId() + : 0; + + // Add the loop and create the synonym. + ApplyTransformation(TransformationAddLoopToCreateIntConstantSynonym( + constant_id, initial_value_id, step_value_id, num_iterations_id, + block_id, GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(), + additional_block_id)); + } +} + +std::pair FuzzerPassAddLoopsToCreateIntConstantSynonyms:: + FindSuitableStepAndInitialValueConstants(uint64_t constant_val, + uint32_t bit_width, bool is_signed, + uint32_t num_iterations) { + // Choose the step value randomly and compute the initial value accordingly. + // The result of |initial_value| could overflow, but this is OK, since + // the transformation takes overflows into consideration (the equation still + // holds as long as the last |bit_width| bits of C and of (I-S*N) match). + uint64_t step_value = + GetFuzzerContext()->GetRandomValueForStepConstantInLoop(); + uint64_t initial_value = constant_val + step_value * num_iterations; + + uint32_t initial_val_id = FindOrCreateIntegerConstant( + fuzzerutil::IntToWords(initial_value, bit_width, is_signed), bit_width, + is_signed, false); + + uint32_t step_val_id = FindOrCreateIntegerConstant( + fuzzerutil::IntToWords(step_value, bit_width, is_signed), bit_width, + is_signed, false); + + return {initial_val_id, step_val_id}; +} + +} // namespace fuzz +} // namespace spvtools \ No newline at end of file diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h new file mode 100644 index 0000000..ee98c4e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that adds synonyms for integer, scalar or vector, constants, by +// adding loops that compute the same value by subtracting a value S from an +// initial value I, and for N times, so that C = I - S*N. +class FuzzerPassAddLoopsToCreateIntConstantSynonyms : public FuzzerPass { + public: + FuzzerPassAddLoopsToCreateIntConstantSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddLoopsToCreateIntConstantSynonyms(); + + void Apply() override; + + private: + // Returns a pair (initial_val_id, step_val_id) such that both ids are + // integer scalar constants of the same type as the scalar integer constant + // identified by the given |constant_val|, |bit_width| and signedness, and + // such that, if I is the value of initial_val_id, S is the value of + // step_val_id and C is the value of the constant, the equation (C = I - S * + // num_iterations) holds, (only considering the last |bit_width| bits of each + // constant). + std::pair FindSuitableStepAndInitialValueConstants( + uint64_t constant_val, uint32_t bit_width, bool is_signed, + uint32_t num_iterations); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_LOOPS_TO_CREATE_INT_CONSTANT_SYNONYMS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp new file mode 100644 index 0000000..09627d0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_no_contraction_decorations.h" + +#include "source/fuzz/transformation_add_no_contraction_decoration.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddNoContractionDecorations::FuzzerPassAddNoContractionDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddNoContractionDecorations:: + ~FuzzerPassAddNoContractionDecorations() = default; + +void FuzzerPassAddNoContractionDecorations::Apply() { + // Consider every instruction in every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& inst : block) { + // Restrict attention to arithmetic instructions (as defined in the + // SPIR-V specification). + if (TransformationAddNoContractionDecoration::IsArithmetic( + inst.opcode())) { + // Randomly choose whether to apply the NoContraction decoration to + // this arithmetic instruction. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingNoContractionDecoration())) { + TransformationAddNoContractionDecoration transformation( + inst.result_id()); + ApplyTransformation(transformation); + } + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h new file mode 100644 index 0000000..c0c1e09 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_no_contraction_decorations.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that applies the NoContraction decoration to arithmetic instructions. +class FuzzerPassAddNoContractionDecorations : public FuzzerPass { + public: + FuzzerPassAddNoContractionDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddNoContractionDecorations() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_NO_CONTRACTION_DECORATIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp new file mode 100644 index 0000000..2b339ca --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.cpp @@ -0,0 +1,312 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_add_opphi_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddOpPhiSynonyms::FuzzerPassAddOpPhiSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddOpPhiSynonyms::~FuzzerPassAddOpPhiSynonyms() = default; + +void FuzzerPassAddOpPhiSynonyms::Apply() { + // Get a list of synonymous ids with the same type that can be used in the + // same OpPhi instruction. + auto equivalence_classes = GetIdEquivalenceClasses(); + + // Make a list of references, to avoid copying sets unnecessarily. + std::vector*> equivalence_class_pointers; + for (auto& set : equivalence_classes) { + equivalence_class_pointers.push_back(&set); + } + + // Keep a list of transformations to apply at the end. + std::vector transformations_to_apply; + + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // Randomly decide whether to consider this block. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingOpPhiSynonym())) { + continue; + } + + // The block must not be dead. + if (GetTransformationContext()->GetFactManager()->BlockIsDead( + block.id())) { + continue; + } + + // The block must have at least one predecessor. + size_t num_preds = GetIRContext()->cfg()->preds(block.id()).size(); + if (num_preds == 0) { + continue; + } + + std::set* chosen_equivalence_class = nullptr; + + if (num_preds > 1) { + // If the block has more than one predecessor, prioritise sets with at + // least 2 ids available at some predecessor. + chosen_equivalence_class = MaybeFindSuitableEquivalenceClassRandomly( + equivalence_class_pointers, block.id(), 2); + } + + // If a set was not already chosen, choose one with at least one available + // id. + if (!chosen_equivalence_class) { + chosen_equivalence_class = MaybeFindSuitableEquivalenceClassRandomly( + equivalence_class_pointers, block.id(), 1); + } + + // If no suitable set was found, we cannot apply the transformation to + // this block. + if (!chosen_equivalence_class) { + continue; + } + + // Initialise the map from predecessor labels to ids. + std::map preds_to_ids; + + // Keep track of the ids used and of the id of a predecessor with at least + // two ids to choose from. This is to ensure that, if possible, at least + // two distinct ids will be used. + std::set ids_chosen; + uint32_t pred_with_alternatives = 0; + + // Choose an id for each predecessor. + for (uint32_t pred_id : GetIRContext()->cfg()->preds(block.id())) { + auto suitable_ids = GetSuitableIds(*chosen_equivalence_class, pred_id); + assert(!suitable_ids.empty() && + "We must be able to find at least one suitable id because the " + "equivalence class was chosen among suitable ones."); + + // If this predecessor has more than one id to choose from and it is the + // first one of this kind that we found, remember its id. + if (suitable_ids.size() > 1 && !pred_with_alternatives) { + pred_with_alternatives = pred_id; + } + + uint32_t chosen_id = + suitable_ids[GetFuzzerContext()->RandomIndex(suitable_ids)]; + + // Add this id to the set of ids chosen. + ids_chosen.emplace(chosen_id); + + // Add the pair (predecessor, chosen id) to the map. + preds_to_ids[pred_id] = chosen_id; + } + + // If: + // - the block has more than one predecessor + // - at least one predecessor has more than one alternative + // - the same id has been chosen by all the predecessors + // then choose another one for the predecessor with more than one + // alternative. + if (num_preds > 1 && pred_with_alternatives != 0 && + ids_chosen.size() == 1) { + auto suitable_ids = + GetSuitableIds(*chosen_equivalence_class, pred_with_alternatives); + uint32_t chosen_id = + GetFuzzerContext()->RemoveAtRandomIndex(&suitable_ids); + if (chosen_id == preds_to_ids[pred_with_alternatives]) { + chosen_id = GetFuzzerContext()->RemoveAtRandomIndex(&suitable_ids); + } + + preds_to_ids[pred_with_alternatives] = chosen_id; + } + + // Add the transformation to the list of transformations to apply. + transformations_to_apply.emplace_back(block.id(), preds_to_ids, + GetFuzzerContext()->GetFreshId()); + } + } + + // Apply the transformations. + for (const auto& transformation : transformations_to_apply) { + ApplyTransformation(transformation); + } +} + +std::vector> +FuzzerPassAddOpPhiSynonyms::GetIdEquivalenceClasses() { + std::vector> id_equivalence_classes; + + // Keep track of all the ids that have already be assigned to a class. + std::set already_in_a_class; + + for (const auto& pair : GetIRContext()->get_def_use_mgr()->id_to_defs()) { + // Exclude ids that have already been assigned to a class. + if (already_in_a_class.count(pair.first)) { + continue; + } + + // Exclude irrelevant ids. + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + pair.first)) { + continue; + } + + // Exclude ids having a type that is not allowed by the transformation. + if (!TransformationAddOpPhiSynonym::CheckTypeIsAllowed( + GetIRContext(), pair.second->type_id())) { + continue; + } + + // Exclude OpFunction and OpUndef instructions, because: + // - OpFunction does not yield a value; + // - OpUndef yields an undefined value at each use, so it should never be a + // synonym of another id. + if (pair.second->opcode() == SpvOpFunction || + pair.second->opcode() == SpvOpUndef) { + continue; + } + + // We need a new equivalence class for this id. + std::set new_equivalence_class; + + // Add this id to the class. + new_equivalence_class.emplace(pair.first); + already_in_a_class.emplace(pair.first); + + // Add all the synonyms with the same type to this class. + for (auto synonym : + GetTransformationContext()->GetFactManager()->GetSynonymsForId( + pair.first)) { + // The synonym must be a plain id - it cannot be an indexed access into a + // composite. + if (synonym->index_size() > 0) { + continue; + } + + // The synonym must not be irrelevant. + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + synonym->object())) { + continue; + } + + auto synonym_def = + GetIRContext()->get_def_use_mgr()->GetDef(synonym->object()); + // The synonym must exist and have the same type as the id we are + // considering. + if (!synonym_def || synonym_def->type_id() != pair.second->type_id()) { + continue; + } + + // We can add this synonym to the new equivalence class. + new_equivalence_class.emplace(synonym->object()); + already_in_a_class.emplace(synonym->object()); + } + + // Add the new equivalence class to the list of equivalence classes. + id_equivalence_classes.emplace_back(std::move(new_equivalence_class)); + } + + return id_equivalence_classes; +} + +bool FuzzerPassAddOpPhiSynonyms::EquivalenceClassIsSuitableForBlock( + const std::set& equivalence_class, uint32_t block_id, + uint32_t distinct_ids_required) { + bool at_least_one_id_for_each_pred = true; + + // Keep a set of the suitable ids found. + std::set suitable_ids_found; + + // Loop through all the predecessors of the block. + for (auto pred_id : GetIRContext()->cfg()->preds(block_id)) { + // Find the last instruction in the predecessor block. + auto last_instruction = + GetIRContext()->get_instr_block(pred_id)->terminator(); + + // Initially assume that there is not a suitable id for this predecessor. + bool at_least_one_suitable_id_found = false; + for (uint32_t id : equivalence_class) { + if (fuzzerutil::IdIsAvailableBeforeInstruction(GetIRContext(), + last_instruction, id)) { + // We have found a suitable id. + at_least_one_suitable_id_found = true; + suitable_ids_found.emplace(id); + + // If we have already found enough distinct suitable ids, we don't need + // to check the remaining ones for this predecessor. + if (suitable_ids_found.size() >= distinct_ids_required) { + break; + } + } + } + // If no suitable id was found for this predecessor, this equivalence class + // is not suitable and we don't need to check the other predecessors. + if (!at_least_one_suitable_id_found) { + at_least_one_id_for_each_pred = false; + break; + } + } + + // The equivalence class is suitable if at least one suitable id was found for + // each predecessor and we have found at least |distinct_ids_required| + // distinct suitable ids in general. + return at_least_one_id_for_each_pred && + suitable_ids_found.size() >= distinct_ids_required; +} + +std::vector FuzzerPassAddOpPhiSynonyms::GetSuitableIds( + const std::set& ids, uint32_t pred_id) { + // Initialise an empty vector of suitable ids. + std::vector suitable_ids; + + // Get the predecessor block. + auto predecessor = fuzzerutil::MaybeFindBlock(GetIRContext(), pred_id); + + // Loop through the ids to find the suitable ones. + for (uint32_t id : ids) { + if (fuzzerutil::IdIsAvailableBeforeInstruction( + GetIRContext(), predecessor->terminator(), id)) { + suitable_ids.push_back(id); + } + } + + return suitable_ids; +} + +std::set* +FuzzerPassAddOpPhiSynonyms::MaybeFindSuitableEquivalenceClassRandomly( + const std::vector*>& candidates, uint32_t block_id, + uint32_t distinct_ids_required) { + auto remaining_candidates = candidates; + while (!remaining_candidates.empty()) { + // Choose one set randomly and return it if it is suitable. + auto chosen = + GetFuzzerContext()->RemoveAtRandomIndex(&remaining_candidates); + if (EquivalenceClassIsSuitableForBlock(*chosen, block_id, + distinct_ids_required)) { + return chosen; + } + } + + // No suitable sets were found. + return nullptr; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h new file mode 100644 index 0000000..6c7d752 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_opphi_synonyms.h @@ -0,0 +1,73 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_OPPHI_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_OPPHI_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass to add OpPhi instructions which can take the values of ids that +// have been marked as synonymous. This instruction will itself be marked as +// synonymous with the others. +class FuzzerPassAddOpPhiSynonyms : public FuzzerPass { + public: + FuzzerPassAddOpPhiSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddOpPhiSynonyms() override; + + void Apply() override; + + // Computes the equivalence classes for the non-pointer and non-irrelevant ids + // in the module, where two ids are considered equivalent iff they have been + // declared synonymous and they have the same type. + std::vector> GetIdEquivalenceClasses(); + + // Returns true iff |equivalence_class| contains at least + // |distinct_ids_required| ids so that all of these ids are available at the + // end of at least one predecessor of the block with label |block_id|. + // Assumes that the block has at least one predecessor. + bool EquivalenceClassIsSuitableForBlock( + const std::set& equivalence_class, uint32_t block_id, + uint32_t distinct_ids_required); + + // Returns a vector with the ids that are available to use at the end of the + // block with id |pred_id|, selected among the given |ids|. Assumes that + // |pred_id| is the label of a block and all ids in |ids| exist in the module. + std::vector GetSuitableIds(const std::set& ids, + uint32_t pred_id); + + private: + // Randomly chooses one of the equivalence classes in |candidates|, so that it + // satisfies all of the following conditions: + // - For each of the predecessors of the |block_id| block, there is at least + // one id in the chosen equivalence class that is available at the end of + // it. + // - There are at least |distinct_ids_required| ids available at the end of + // some predecessor. + // Returns nullptr if no equivalence class in |candidates| satisfies the + // requirements. + std::set* MaybeFindSuitableEquivalenceClassRandomly( + const std::vector*>& candidates, uint32_t block_id, + uint32_t distinct_ids_required); +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_OPPHI_SYNONYMS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp new file mode 100644 index 0000000..35532e9 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.cpp @@ -0,0 +1,145 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_parameters.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_parameter.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddParameters::FuzzerPassAddParameters( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddParameters::~FuzzerPassAddParameters() = default; + +void FuzzerPassAddParameters::Apply() { + // Compute type candidates for the new parameter. + std::vector type_candidates; + for (const auto& type_inst : GetIRContext()->module()->GetTypes()) { + if (TransformationAddParameter::IsParameterTypeSupported( + GetIRContext(), type_inst->result_id())) { + type_candidates.push_back(type_inst->result_id()); + } + } + + if (type_candidates.empty()) { + // The module contains no suitable types to use in new parameters. + return; + } + + // Iterate over all functions in the module. + for (const auto& function : *GetIRContext()->module()) { + // Skip all entry-point functions - we don't want to change those. + if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), + function.result_id())) { + continue; + } + + if (GetNumberOfParameters(function) >= + GetFuzzerContext()->GetMaximumNumberOfFunctionParameters()) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingParameters())) { + continue; + } + + auto num_new_parameters = + GetFuzzerContext()->GetRandomNumberOfNewParameters( + GetNumberOfParameters(function)); + + for (uint32_t i = 0; i < num_new_parameters; ++i) { + auto current_type_id = + type_candidates[GetFuzzerContext()->RandomIndex(type_candidates)]; + auto current_type = + GetIRContext()->get_type_mgr()->GetType(current_type_id); + std::map call_parameter_ids; + + // Consider the case when a pointer type was selected. + if (current_type->kind() == opt::analysis::Type::kPointer) { + auto storage_class = fuzzerutil::GetStorageClassFromPointerType( + GetIRContext(), current_type_id); + switch (storage_class) { + case SpvStorageClassFunction: { + // In every caller find or create a local variable that has the + // selected type. + for (auto* instr : + fuzzerutil::GetCallers(GetIRContext(), function.result_id())) { + auto block = GetIRContext()->get_instr_block(instr); + auto function_id = block->GetParent()->result_id(); + uint32_t variable_id = + FindOrCreateLocalVariable(current_type_id, function_id, true); + call_parameter_ids[instr->result_id()] = variable_id; + } + } break; + case SpvStorageClassPrivate: + case SpvStorageClassWorkgroup: { + // If there exists at least one caller, find or create a global + // variable that has the selected type. + std::vector callers = + fuzzerutil::GetCallers(GetIRContext(), function.result_id()); + if (!callers.empty()) { + uint32_t variable_id = + FindOrCreateGlobalVariable(current_type_id, true); + for (auto* instr : callers) { + call_parameter_ids[instr->result_id()] = variable_id; + } + } + } break; + default: + break; + } + } else { + // If there exists at least one caller, find or create a zero constant + // that has the selected type. + std::vector callers = + fuzzerutil::GetCallers(GetIRContext(), function.result_id()); + if (!callers.empty()) { + uint32_t constant_id = + FindOrCreateZeroConstant(current_type_id, true); + for (auto* instr : + fuzzerutil::GetCallers(GetIRContext(), function.result_id())) { + call_parameter_ids[instr->result_id()] = constant_id; + } + } + } + + ApplyTransformation(TransformationAddParameter( + function.result_id(), GetFuzzerContext()->GetFreshId(), + current_type_id, std::move(call_parameter_ids), + GetFuzzerContext()->GetFreshId())); + } + } +} + +uint32_t FuzzerPassAddParameters::GetNumberOfParameters( + const opt::Function& function) const { + const auto* type = GetIRContext()->get_type_mgr()->GetType( + function.DefInst().GetSingleWordInOperand(1)); + assert(type && type->AsFunction()); + + return static_cast(type->AsFunction()->param_types().size()); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h new file mode 100644 index 0000000..f1261ae --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_parameters.h @@ -0,0 +1,47 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides for each non-entry-point function in the module whether to +// add new parameters to it. If so, randomly determines the number of parameters +// to add, their type and creates constants used to initialize them. +class FuzzerPassAddParameters : public FuzzerPass { + public: + FuzzerPassAddParameters(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddParameters() override; + + void Apply() override; + + private: + // Returns number of parameters of |function|. + uint32_t GetNumberOfParameters(const opt::Function& function) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_PARAMETERS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp new file mode 100644 index 0000000..a2497df --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h" + +#include "source/fuzz/transformation_add_relaxed_decoration.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddRelaxedDecorations::FuzzerPassAddRelaxedDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddRelaxedDecorations::~FuzzerPassAddRelaxedDecorations() = default; + +void FuzzerPassAddRelaxedDecorations::Apply() { + // Consider every instruction in every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& inst : block) { + // Randomly choose whether to apply the RelaxedPrecision decoration + // to this instruction. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingRelaxedDecoration())) { + TransformationAddRelaxedDecoration transformation(inst.result_id()); + // Restrict attention to numeric instructions (returning 32-bit + // floats or ints according to SPIR-V documentation) in dead blocks. + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + transformation.Apply(GetIRContext(), GetTransformationContext()); + *GetTransformations()->add_transformation() = + transformation.ToMessage(); + } + } + } + } + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h new file mode 100644 index 0000000..897b821 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_relaxed_decorations.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that applies the Relaxed decoration to numeric instructions. +class FuzzerPassAddRelaxedDecorations : public FuzzerPass { + public: + FuzzerPassAddRelaxedDecorations( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddRelaxedDecorations() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_RELAXED_DECORATIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp new file mode 100644 index 0000000..46efc64 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.cpp @@ -0,0 +1,132 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_stores.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_store.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddStores::FuzzerPassAddStores( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddStores::~FuzzerPassAddStores() = default; + +void FuzzerPassAddStores::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(inst_it->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Check whether it is legitimate to insert a store before this + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, + inst_it)) { + return; + } + + // Randomly decide whether to try inserting a store here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingStore())) { + return; + } + + // Look for pointers we might consider storing to. + std::vector relevant_pointers = + FindAvailableInstructions( + function, block, inst_it, + [this, block](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + auto type_inst = context->get_def_use_mgr()->GetDef( + instruction->type_id()); + if (type_inst->opcode() != SpvOpTypePointer) { + // Not a pointer. + return false; + } + if (instruction->IsReadOnlyPointer()) { + // Read only: cannot store to it. + return false; + } + switch (instruction->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + // Do not allow storing to a null or undefined pointer; + // this might be OK if the block is dead, but for now we + // conservatively avoid it. + return false; + default: + break; + } + return GetTransformationContext() + ->GetFactManager() + ->BlockIsDead(block->id()) || + GetTransformationContext() + ->GetFactManager() + ->PointeeValueIsIrrelevant( + instruction->result_id()); + }); + + // At this point, |relevant_pointers| contains all the pointers we might + // think of storing to. + if (relevant_pointers.empty()) { + return; + } + + auto pointer = relevant_pointers[GetFuzzerContext()->RandomIndex( + relevant_pointers)]; + + std::vector relevant_values = + FindAvailableInstructions( + function, block, inst_it, + [pointer](opt::IRContext* context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + return instruction->type_id() == + context->get_def_use_mgr() + ->GetDef(pointer->type_id()) + ->GetSingleWordInOperand(1); + }); + + if (relevant_values.empty()) { + return; + } + + // Choose a value at random, and create and apply a storing + // transformation based on it and the pointer. + ApplyTransformation(TransformationStore( + pointer->result_id(), + relevant_values[GetFuzzerContext()->RandomIndex(relevant_values)] + ->result_id(), + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h new file mode 100644 index 0000000..55ec67f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_stores.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that adds stores, at random, through pointers in the module, +// either (a) from dead blocks, or (b) through pointers whose pointee values +// are known not to affect the module's overall behaviour. +class FuzzerPassAddStores : public FuzzerPass { + public: + FuzzerPassAddStores(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddStores(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_STORES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp new file mode 100644 index 0000000..2fa0700 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_synonyms.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddSynonyms::FuzzerPassAddSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddSynonyms::~FuzzerPassAddSynonyms() = default; + +void FuzzerPassAddSynonyms::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id())) { + // Don't create synonyms in dead blocks. + return; + } + + // Skip |inst_it| if we can't insert anything above it. OpIAdd is just + // a representative of some instruction that might be produced by the + // transformation. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, inst_it)) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingSynonyms())) { + return; + } + + auto synonym_type = GetFuzzerContext()->GetRandomSynonymType(); + + // Select all instructions that can be used to create a synonym to. + auto available_instructions = FindAvailableInstructions( + function, block, inst_it, + [synonym_type, this](opt::IRContext* ir_context, + opt::Instruction* inst) { + // Check that we can create a synonym to |inst| as described by + // the |synonym_type| and insert it before |inst_it|. + return TransformationAddSynonym::IsInstructionValid( + ir_context, *GetTransformationContext(), inst, synonym_type); + }); + + if (available_instructions.empty()) { + return; + } + + const auto* existing_synonym = + available_instructions[GetFuzzerContext()->RandomIndex( + available_instructions)]; + + // Make sure the module contains all instructions required to apply the + // transformation. + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + // Create a zero constant to be used as an operand of the synonymous + // instruction. + FindOrCreateZeroConstant(existing_synonym->type_id(), false); + break; + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: { + const auto* existing_synonym_type = + GetIRContext()->get_type_mgr()->GetType( + existing_synonym->type_id()); + assert(existing_synonym_type && "Instruction has invalid type"); + + if (const auto* vector = existing_synonym_type->AsVector()) { + auto element_type_id = + GetIRContext()->get_type_mgr()->GetId(vector->element_type()); + assert(element_type_id && "Vector's element type is invalid"); + + auto one_word = vector->element_type()->AsFloat() + ? fuzzerutil::FloatToWord(1) + : 1u; + FindOrCreateCompositeConstant( + std::vector( + vector->element_count(), + FindOrCreateConstant({one_word}, element_type_id, false)), + existing_synonym->type_id(), false); + } else { + FindOrCreateConstant( + {existing_synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) + : 1u}, + existing_synonym->type_id(), false); + } + } break; + default: + // This assertion will fail if some SynonymType is missing from the + // switch statement. + assert( + !TransformationAddSynonym::IsAdditionalConstantRequired( + synonym_type) && + "|synonym_type| requires an additional constant to be present " + "in the module"); + break; + } + + ApplyTransformation(TransformationAddSynonym( + existing_synonym->result_id(), synonym_type, + GetFuzzerContext()->GetFreshId(), instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h new file mode 100644 index 0000000..dcfb938 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_synonyms.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Sprinkles instructions through the module that produce ids, synonymous to +// some other instructions. +class FuzzerPassAddSynonyms : public FuzzerPass { + public: + FuzzerPassAddSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddSynonyms() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_SYNONYMS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp new file mode 100644 index 0000000..453448b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.cpp @@ -0,0 +1,145 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_vector_shuffle.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAddVectorShuffleInstructions::FuzzerPassAddVectorShuffleInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAddVectorShuffleInstructions:: + ~FuzzerPassAddVectorShuffleInstructions() = default; + +void FuzzerPassAddVectorShuffleInstructions::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(instruction_iterator->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Randomly decide whether to try adding an OpVectorShuffle instruction. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAddingVectorShuffle())) { + return; + } + + // It must be valid to insert an OpVectorShuffle instruction + // before |instruction_iterator|. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpVectorShuffle, instruction_iterator)) { + return; + } + + // Looks for vectors that we might consider to use as OpVectorShuffle + // operands. + std::vector vector_instructions = + FindAvailableInstructions( + function, block, instruction_iterator, + [this, instruction_descriptor]( + opt::IRContext* ir_context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + + if (!ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsVector()) { + return false; + } + + if (!GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, + *GetTransformationContext(), + instruction)) { + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be + // able to produce a synonym out of the id. + return false; + } + + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, + FindInstruction(instruction_descriptor, ir_context), + instruction->result_id()); + }); + + // If there are no vector instructions, then return. + if (vector_instructions.empty()) { + return; + } + + auto vector_1_instruction = + vector_instructions[GetFuzzerContext()->RandomIndex( + vector_instructions)]; + auto vector_1_type = GetIRContext() + ->get_type_mgr() + ->GetType(vector_1_instruction->type_id()) + ->AsVector(); + + auto vector_2_instruction = + GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions); + auto vector_2_type = GetIRContext() + ->get_type_mgr() + ->GetType(vector_2_instruction->type_id()) + ->AsVector(); + + // |vector_1| and |vector_2| must have the same element type as each + // other. The loop is guaranteed to terminate because each iteration + // removes on possible choice for |vector_2|, and there is at least one + // choice that will cause the loop to exit - namely |vector_1|. + while (vector_1_type->element_type() != vector_2_type->element_type()) { + vector_2_instruction = + GetFuzzerContext()->RemoveAtRandomIndex(&vector_instructions); + vector_2_type = GetIRContext() + ->get_type_mgr() + ->GetType(vector_2_instruction->type_id()) + ->AsVector(); + } + + // Gets components and creates the appropriate result vector type. + std::vector components = + GetFuzzerContext()->GetRandomComponentsForVectorShuffle( + vector_1_type->element_count() + + vector_2_type->element_count()); + FindOrCreateVectorType(GetIRContext()->get_type_mgr()->GetId( + vector_1_type->element_type()), + static_cast(components.size())); + + // Applies the vector shuffle transformation. + ApplyTransformation(TransformationVectorShuffle( + instruction_descriptor, GetFuzzerContext()->GetFreshId(), + vector_1_instruction->result_id(), + vector_2_instruction->result_id(), components)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h new file mode 100644 index 0000000..99b9f24 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Adds OpVectorShuffle instructions to the module. +class FuzzerPassAddVectorShuffleInstructions : public FuzzerPass { + public: + FuzzerPassAddVectorShuffleInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAddVectorShuffleInstructions(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADD_VECTOR_SHUFFLE_INSTRUCTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp new file mode 100644 index 0000000..1d6d434 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.cpp @@ -0,0 +1,48 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_branch_weights.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_adjust_branch_weights.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustBranchWeights::FuzzerPassAdjustBranchWeights( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAdjustBranchWeights::~FuzzerPassAdjustBranchWeights() = default; + +void FuzzerPassAdjustBranchWeights::Apply() { + // For all OpBranchConditional instructions, + // randomly applies the transformation. + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + if (instruction->opcode() == SpvOpBranchConditional && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingBranchWeights())) { + ApplyTransformation(TransformationAdjustBranchWeights( + MakeInstructionDescriptor(GetIRContext(), instruction), + GetFuzzerContext()->GetRandomBranchWeights())); + } + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h new file mode 100644 index 0000000..5b2b33f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_branch_weights.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for branch conditional instructions +// and randomly chooses which of these instructions will have their weights +// adjusted. +class FuzzerPassAdjustBranchWeights : public FuzzerPass { + public: + FuzzerPassAdjustBranchWeights( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAdjustBranchWeights(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_BRANCH_WEIGHTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp new file mode 100644 index 0000000..aa62d2f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_function_controls.h" + +#include "source/fuzz/transformation_set_function_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustFunctionControls::FuzzerPassAdjustFunctionControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAdjustFunctionControls::~FuzzerPassAdjustFunctionControls() = default; + +void FuzzerPassAdjustFunctionControls::Apply() { + // Consider every function in the module. + for (auto& function : *GetIRContext()->module()) { + // Randomly decide whether to adjust this function's controls. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingFunctionControl())) { + // Grab the function control mask for the function in its present form. + uint32_t existing_function_control_mask = + function.DefInst().GetSingleWordInOperand(0); + + // For the new mask, we first randomly select one of three basic masks: + // None, Inline or DontInline. These are always valid (and are mutually + // exclusive). + std::vector basic_function_control_masks = { + SpvFunctionControlMaskNone, SpvFunctionControlInlineMask, + SpvFunctionControlDontInlineMask}; + uint32_t new_function_control_mask = + basic_function_control_masks[GetFuzzerContext()->RandomIndex( + basic_function_control_masks)]; + + // We now consider the Pure and Const mask bits. If these are already + // set on the function then it's OK to keep them, but also interesting + // to consider dropping them, so we decide randomly in each case. + for (auto mask_bit : + {SpvFunctionControlPureMask, SpvFunctionControlConstMask}) { + if ((existing_function_control_mask & mask_bit) && + GetFuzzerContext()->ChooseEven()) { + new_function_control_mask |= mask_bit; + } + } + + // Create and add a transformation. + TransformationSetFunctionControl transformation( + function.DefInst().result_id(), new_function_control_mask); + ApplyTransformation(transformation); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h new file mode 100644 index 0000000..5fdbe43 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_function_controls.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that adjusts the function controls on OpFunction instructions. +class FuzzerPassAdjustFunctionControls : public FuzzerPass { + public: + FuzzerPassAdjustFunctionControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAdjustFunctionControls() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_FUNCTION_CONTROLS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp new file mode 100644 index 0000000..f7addff --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_loop_controls.h" + +#include "source/fuzz/transformation_set_loop_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustLoopControls::FuzzerPassAdjustLoopControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAdjustLoopControls::~FuzzerPassAdjustLoopControls() = default; + +void FuzzerPassAdjustLoopControls::Apply() { + // Consider every merge instruction in the module (via looking through all + // functions and blocks). + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + if (auto merge_inst = block.GetMergeInst()) { + // Ignore the instruction if it is not a loop merge. + if (merge_inst->opcode() != SpvOpLoopMerge) { + continue; + } + + // Decide randomly whether to adjust this loop merge. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingLoopControl())) { + continue; + } + + uint32_t existing_mask = merge_inst->GetSingleWordOperand( + TransformationSetLoopControl::kLoopControlMaskInOperandIndex); + + // First, set the new mask to one of None, Unroll or DontUnroll. + std::vector basic_masks = {SpvLoopControlMaskNone, + SpvLoopControlUnrollMask, + SpvLoopControlDontUnrollMask}; + uint32_t new_mask = + basic_masks[GetFuzzerContext()->RandomIndex(basic_masks)]; + + // For the loop controls that depend on guarantees about what the loop + // does, check which of these were present in the existing mask and + // randomly decide whether to keep them. They are just hints, so + // removing them should not change the semantics of the module. + for (auto mask_bit : + {SpvLoopControlDependencyInfiniteMask, + SpvLoopControlDependencyLengthMask, + SpvLoopControlMinIterationsMask, SpvLoopControlMaxIterationsMask, + SpvLoopControlIterationMultipleMask}) { + if ((existing_mask & mask_bit) && GetFuzzerContext()->ChooseEven()) { + // The mask bits we are considering are not available in all SPIR-V + // versions. However, we only include a mask bit if it was present + // in the original loop control mask, and we work under the + // assumption that we are transforming a valid module, thus we don't + // need to actually check whether the SPIR-V version being used + // supports these loop control mask bits. + new_mask |= mask_bit; + } + } + + // We use 0 for peel count and partial count in the case that we choose + // not to set these controls. + uint32_t peel_count = 0; + uint32_t partial_count = 0; + + // PeelCount and PartialCount are not compatible with DontUnroll, so + // we check whether DontUnroll is set. + if (!(new_mask & SpvLoopControlDontUnrollMask)) { + // If PeelCount is supported by this SPIR-V version, randomly choose + // whether to set it. If it was set in the original mask and is not + // selected for setting here, that amounts to dropping it. + if (TransformationSetLoopControl::PeelCountIsSupported( + GetIRContext()) && + GetFuzzerContext()->ChooseEven()) { + new_mask |= SpvLoopControlPeelCountMask; + // The peel count is chosen randomly - if PeelCount was already set + // this will overwrite whatever peel count was previously used. + peel_count = GetFuzzerContext()->GetRandomLoopControlPeelCount(); + } + // Similar, but for PartialCount. + if (TransformationSetLoopControl::PartialCountIsSupported( + GetIRContext()) && + GetFuzzerContext()->ChooseEven()) { + new_mask |= SpvLoopControlPartialCountMask; + partial_count = + GetFuzzerContext()->GetRandomLoopControlPartialCount(); + } + } + + // Apply the transformation and add it to the output transformation + // sequence. + TransformationSetLoopControl transformation(block.id(), new_mask, + peel_count, partial_count); + ApplyTransformation(transformation); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h new file mode 100644 index 0000000..f133b2d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_loop_controls.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that adjusts the loop controls on OpLoopMerge instructions. +class FuzzerPassAdjustLoopControls : public FuzzerPass { + public: + FuzzerPassAdjustLoopControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAdjustLoopControls() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_LOOP_CONTROLS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp new file mode 100644 index 0000000..68f0ca7 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h" + +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_set_memory_operands_mask.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustMemoryOperandsMasks::FuzzerPassAdjustMemoryOperandsMasks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAdjustMemoryOperandsMasks::~FuzzerPassAdjustMemoryOperandsMasks() = + default; + +void FuzzerPassAdjustMemoryOperandsMasks::Apply() { + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // Consider every instruction in this block, using an explicit iterator so + // that when we find an instruction of interest we can search backwards to + // create an id descriptor for it. + for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) { + if (!TransformationSetMemoryOperandsMask::IsMemoryAccess(*inst_it)) { + // We are only interested in memory access instructions. + continue; + } + + std::vector indices_of_available_masks_to_adjust; + // All memory instructions have at least one memory operands mask. + indices_of_available_masks_to_adjust.push_back(0); + // From SPIR-V 1.4 onwards, OpCopyMemory and OpCopyMemorySized have a + // second mask. + switch (inst_it->opcode()) { + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + if (TransformationSetMemoryOperandsMask:: + MultipleMemoryOperandMasksAreSupported(GetIRContext())) { + indices_of_available_masks_to_adjust.push_back(1); + } + break; + default: + break; + } + + // Consider the available masks + for (auto mask_index : indices_of_available_masks_to_adjust) { + // Randomly decide whether to adjust this mask. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAdjustingMemoryOperandsMask())) { + continue; + } + // Get the existing mask, using None if there was no mask present at + // all. + auto existing_mask_in_operand_index = + TransformationSetMemoryOperandsMask::GetInOperandIndexForMask( + *inst_it, mask_index); + auto existing_mask = + existing_mask_in_operand_index < inst_it->NumInOperands() + ? inst_it->GetSingleWordInOperand( + existing_mask_in_operand_index) + : static_cast(SpvMemoryAccessMaskNone); + + // There are two things we can do to a mask: + // - add Volatile if not already present + // - toggle Nontemporal + // The following ensures that we do at least one of these + bool add_volatile = !(existing_mask & SpvMemoryAccessVolatileMask) && + GetFuzzerContext()->ChooseEven(); + bool toggle_nontemporal = + !add_volatile || GetFuzzerContext()->ChooseEven(); + + // These bitwise operations use '|' to add Volatile if desired, and + // '^' to toggle Nontemporal if desired. + uint32_t new_mask = + (existing_mask | (add_volatile ? SpvMemoryAccessVolatileMask + : SpvMemoryAccessMaskNone)) ^ + (toggle_nontemporal ? SpvMemoryAccessNontemporalMask + : SpvMemoryAccessMaskNone); + + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(block, inst_it), new_mask, mask_index); + ApplyTransformation(transformation); + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h new file mode 100644 index 0000000..699dcb5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_memory_operands_masks.h @@ -0,0 +1,40 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass to adjust the memory operand masks in memory access +// instructions. +class FuzzerPassAdjustMemoryOperandsMasks : public FuzzerPass { + public: + FuzzerPassAdjustMemoryOperandsMasks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAdjustMemoryOperandsMasks(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_MEMORY_OPERANDS_MASKS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp new file mode 100644 index 0000000..83b1854 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.cpp @@ -0,0 +1,73 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_adjust_selection_controls.h" + +#include "source/fuzz/transformation_set_selection_control.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassAdjustSelectionControls::FuzzerPassAdjustSelectionControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassAdjustSelectionControls::~FuzzerPassAdjustSelectionControls() = + default; + +void FuzzerPassAdjustSelectionControls::Apply() { + // Consider every merge instruction in the module (via looking through all + // functions and blocks). + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + if (auto merge_inst = block.GetMergeInst()) { + // Ignore the instruction if it is not a selection merge. + if (merge_inst->opcode() != SpvOpSelectionMerge) { + continue; + } + + // Choose randomly whether to change the selection control for this + // instruction. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfAdjustingSelectionControl())) { + continue; + } + + // The choices to change the selection control to are the set of valid + // controls, minus the current control. + std::vector choices; + for (auto control : + {SpvSelectionControlMaskNone, SpvSelectionControlFlattenMask, + SpvSelectionControlDontFlattenMask}) { + if (control == merge_inst->GetSingleWordOperand(1)) { + continue; + } + choices.push_back(control); + } + + // Apply the transformation and add it to the output transformation + // sequence. + TransformationSetSelectionControl transformation( + block.id(), choices[GetFuzzerContext()->RandomIndex(choices)]); + ApplyTransformation(transformation); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h new file mode 100644 index 0000000..910b40d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_adjust_selection_controls.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that adjusts the selection controls on OpSelectionMerge instructions. +class FuzzerPassAdjustSelectionControls : public FuzzerPass { + public: + FuzzerPassAdjustSelectionControls( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassAdjustSelectionControls() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_ADJUST_SELECTION_CONTROLS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp new file mode 100644 index 0000000..38553d2 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.cpp @@ -0,0 +1,207 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_composite_extract.h" +#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassApplyIdSynonyms::FuzzerPassApplyIdSynonyms( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassApplyIdSynonyms::~FuzzerPassApplyIdSynonyms() = default; + +void FuzzerPassApplyIdSynonyms::Apply() { + // Compute a closure of data synonym facts, to enrich the pool of synonyms + // that are available. + ApplyTransformation(TransformationComputeDataSynonymFactClosure( + GetFuzzerContext() + ->GetMaximumEquivalenceClassSizeForDataSynonymFactClosure())); + + for (auto id_with_known_synonyms : GetTransformationContext() + ->GetFactManager() + ->GetIdsForWhichSynonymsAreKnown()) { + // Gather up all uses of |id_with_known_synonym| as a regular id, and + // subsequently iterate over these uses. We use this separation because, + // when considering a given use, we might apply a transformation that will + // invalidate the def-use manager. + std::vector> uses; + GetIRContext()->get_def_use_mgr()->ForEachUse( + id_with_known_synonyms, + [&uses](opt::Instruction* use_inst, uint32_t use_index) -> void { + // We only gather up regular id uses; e.g. we do not include a use of + // the id as the scope for an atomic operation. + if (use_inst->GetOperand(use_index).type == SPV_OPERAND_TYPE_ID) { + uses.emplace_back( + std::pair(use_inst, use_index)); + } + }); + + for (const auto& use : uses) { + auto use_inst = use.first; + auto use_index = use.second; + auto block_containing_use = GetIRContext()->get_instr_block(use_inst); + // The use might not be in a block; e.g. it could be a decoration. + if (!block_containing_use) { + continue; + } + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingIdWithSynonym())) { + continue; + } + // |use_index| is the absolute index of the operand. We require + // the index of the operand restricted to input operands only. + uint32_t use_in_operand_index = + fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index); + if (!fuzzerutil::IdUseCanBeReplaced(GetIRContext(), + *GetTransformationContext(), use_inst, + use_in_operand_index)) { + continue; + } + + std::vector synonyms_to_try; + for (const auto* data_descriptor : + GetTransformationContext()->GetFactManager()->GetSynonymsForId( + id_with_known_synonyms)) { + protobufs::DataDescriptor descriptor_for_this_id = + MakeDataDescriptor(id_with_known_synonyms, {}); + if (DataDescriptorEquals()(data_descriptor, &descriptor_for_this_id)) { + // Exclude the fact that the id is synonymous with itself. + continue; + } + + if (DataDescriptorsHaveCompatibleTypes( + use_inst->opcode(), use_in_operand_index, + descriptor_for_this_id, *data_descriptor)) { + synonyms_to_try.push_back(data_descriptor); + } + } + while (!synonyms_to_try.empty()) { + auto synonym_to_try = + GetFuzzerContext()->RemoveAtRandomIndex(&synonyms_to_try); + + // If the synonym's |index_size| is zero, the synonym represents an id. + // Otherwise it represents some element of a composite structure, in + // which case we need to be able to add an extract instruction to get + // that element out. + if (synonym_to_try->index_size() > 0 && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, + use_inst) && + use_inst->opcode() != SpvOpPhi) { + // We cannot insert an extract before this instruction, so this + // synonym is no good. + continue; + } + + if (!fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst, + use_in_operand_index, + synonym_to_try->object())) { + continue; + } + + // We either replace the use with an id known to be synonymous (when + // the synonym's |index_size| is 0), or an id that will hold the result + // of extracting a synonym from a composite (when the synonym's + // |index_size| is > 0). + uint32_t id_with_which_to_replace_use; + if (synonym_to_try->index_size() == 0) { + id_with_which_to_replace_use = synonym_to_try->object(); + } else { + id_with_which_to_replace_use = GetFuzzerContext()->GetFreshId(); + opt::Instruction* instruction_to_insert_before = nullptr; + + if (use_inst->opcode() != SpvOpPhi) { + instruction_to_insert_before = use_inst; + } else { + auto parent_block_id = + use_inst->GetSingleWordInOperand(use_in_operand_index + 1); + auto parent_block_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(parent_block_id); + auto parent_block = + GetIRContext()->get_instr_block(parent_block_instruction); + + instruction_to_insert_before = parent_block->GetMergeInst() + ? parent_block->GetMergeInst() + : parent_block->terminator(); + } + + if (GetTransformationContext()->GetFactManager()->BlockIsDead( + GetIRContext() + ->get_instr_block(instruction_to_insert_before) + ->id())) { + // We cannot create a synonym via a composite extraction in a dead + // block, as the resulting id is irrelevant. + continue; + } + + assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + synonym_to_try->object()) && + "Irrelevant ids can't participate in DataSynonym facts"); + ApplyTransformation(TransformationCompositeExtract( + MakeInstructionDescriptor(GetIRContext(), + instruction_to_insert_before), + id_with_which_to_replace_use, synonym_to_try->object(), + fuzzerutil::RepeatedFieldToVector(synonym_to_try->index()))); + assert(GetTransformationContext()->GetFactManager()->IsSynonymous( + MakeDataDescriptor(id_with_which_to_replace_use, {}), + *synonym_to_try) && + "The extracted id must be synonymous with the component from " + "which it was extracted."); + } + + ApplyTransformation(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, + use_in_operand_index), + id_with_which_to_replace_use)); + break; + } + } + } +} + +bool FuzzerPassApplyIdSynonyms::DataDescriptorsHaveCompatibleTypes( + SpvOp opcode, uint32_t use_in_operand_index, + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2) { + auto base_object_type_id_1 = + fuzzerutil::GetTypeId(GetIRContext(), dd1.object()); + auto base_object_type_id_2 = + fuzzerutil::GetTypeId(GetIRContext(), dd2.object()); + assert(base_object_type_id_1 && base_object_type_id_2 && + "Data descriptors are invalid"); + + auto type_id_1 = fuzzerutil::WalkCompositeTypeIndices( + GetIRContext(), base_object_type_id_1, dd1.index()); + auto type_id_2 = fuzzerutil::WalkCompositeTypeIndices( + GetIRContext(), base_object_type_id_2, dd2.index()); + assert(type_id_1 && type_id_2 && "Data descriptors have invalid types"); + + return TransformationReplaceIdWithSynonym::TypesAreCompatible( + GetIRContext(), opcode, use_in_operand_index, type_id_1, type_id_2); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h new file mode 100644 index 0000000..5deac10 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_apply_id_synonyms.h @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_H_ + +#include "source/fuzz/fuzzer_pass.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// A pass that replaces ids with other ids, or accesses into structures, that +// are known to hold the same values. +class FuzzerPassApplyIdSynonyms : public FuzzerPass { + public: + FuzzerPassApplyIdSynonyms(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassApplyIdSynonyms() override; + + void Apply() override; + + private: + // Returns true if uses of |dd1| can be replaced with |dd2| and vice-versa + // with respect to the type. Concretely, returns true if |dd1| and |dd2| have + // the same type or both |dd1| and |dd2| are either a numerical or a vector + // type of integral components with possibly different signedness. + bool DataDescriptorsHaveCompatibleTypes(SpvOp opcode, + uint32_t use_in_operand_index, + const protobufs::DataDescriptor& dd1, + const protobufs::DataDescriptor& dd2); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_APPLY_ID_SYNONYMS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp new file mode 100644 index 0000000..584fa1a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.cpp @@ -0,0 +1,388 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_construct_composites.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_composite_construct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassConstructComposites::FuzzerPassConstructComposites( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassConstructComposites::~FuzzerPassConstructComposites() = default; + +void FuzzerPassConstructComposites::Apply() { + // Gather up the ids of all composite types, but skip block-/buffer + // block-decorated struct types. + std::vector composite_type_ids; + for (auto& inst : GetIRContext()->types_values()) { + if (fuzzerutil::IsCompositeType( + GetIRContext()->get_type_mgr()->GetType(inst.result_id())) && + !fuzzerutil::HasBlockOrBufferBlockDecoration(GetIRContext(), + inst.result_id())) { + composite_type_ids.push_back(inst.result_id()); + } + } + + ForEachInstructionWithInstructionDescriptor( + [this, &composite_type_ids]( + opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + // Check whether it is legitimate to insert a composite construction + // before the instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpCompositeConstruct, inst_it)) { + return; + } + + // Randomly decide whether to try inserting an object copy here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfConstructingComposite())) { + return; + } + + // For each instruction that is available at this program point (i.e. an + // instruction that is global or whose definition strictly dominates the + // program point) and suitable for making a synonym of, associate it + // with the id of its result type. + TypeIdToInstructions type_id_to_available_instructions; + auto available_instructions = FindAvailableInstructions( + function, block, inst_it, + [this](opt::IRContext* ir_context, opt::Instruction* inst) { + if (!inst->result_id() || !inst->type_id()) { + return false; + } + + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be able + // to produce a synonym out of the id. + return GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(inst->result_id()) || + fuzzerutil::CanMakeSynonymOf( + ir_context, *GetTransformationContext(), inst); + }); + for (auto instruction : available_instructions) { + RecordAvailableInstruction(instruction, + &type_id_to_available_instructions); + } + + // At this point, |composite_type_ids| captures all the composite types + // we could try to create, while |type_id_to_available_instructions| + // captures all the available result ids we might use, organized by + // type. + + // Now we try to find a composite that we can construct. We might not + // manage, if there is a paucity of available ingredients in the module + // (e.g. if our only available composite was a boolean vector and we had + // no instructions generating boolean result types available). + // + // If we succeed, |chosen_composite_type| will end up being non-zero, + // and |constructor_arguments| will end up giving us result ids suitable + // for constructing a composite of that type. Otherwise these variables + // will remain 0 and null respectively. + uint32_t chosen_composite_type = 0; + std::vector constructor_arguments; + + // Initially, all composite type ids are available for us to try. Keep + // trying until we run out of options. + auto composites_to_try_constructing = composite_type_ids; + while (!composites_to_try_constructing.empty()) { + // Remove a composite type from the composite types left for us to + // try. + auto next_composite_to_try_constructing = + GetFuzzerContext()->RemoveAtRandomIndex( + &composites_to_try_constructing); + + // Now try to construct a composite of this type, using an appropriate + // helper method depending on the kind of composite type. + auto composite_type_inst = GetIRContext()->get_def_use_mgr()->GetDef( + next_composite_to_try_constructing); + switch (composite_type_inst->opcode()) { + case SpvOpTypeArray: + constructor_arguments = FindComponentsToConstructArray( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeMatrix: + constructor_arguments = FindComponentsToConstructMatrix( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeStruct: + constructor_arguments = FindComponentsToConstructStruct( + *composite_type_inst, type_id_to_available_instructions); + break; + case SpvOpTypeVector: + constructor_arguments = FindComponentsToConstructVector( + *composite_type_inst, type_id_to_available_instructions); + break; + default: + assert(false && + "The space of possible composite types should be covered " + "by the above cases."); + break; + } + if (!constructor_arguments.empty()) { + // We succeeded! Note the composite type we finally settled on, and + // exit from the loop. + chosen_composite_type = next_composite_to_try_constructing; + break; + } + } + + if (!chosen_composite_type) { + // We did not manage to make a composite; return 0 to indicate that no + // instructions were added. + assert(constructor_arguments.empty()); + return; + } + assert(!constructor_arguments.empty()); + + // Make and apply a transformation. + ApplyTransformation(TransformationCompositeConstruct( + chosen_composite_type, constructor_arguments, + instruction_descriptor, GetFuzzerContext()->GetFreshId())); + }); +} + +void FuzzerPassConstructComposites::RecordAvailableInstruction( + opt::Instruction* inst, + TypeIdToInstructions* type_id_to_available_instructions) { + if (type_id_to_available_instructions->count(inst->type_id()) == 0) { + (*type_id_to_available_instructions)[inst->type_id()] = {}; + } + type_id_to_available_instructions->at(inst->type_id()).push_back(inst); +} + +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructArray( + const opt::Instruction& array_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions) { + assert(array_type_instruction.opcode() == SpvOpTypeArray && + "Precondition: instruction must be an array type."); + + // Get the element type for the array. + auto element_type_id = array_type_instruction.GetSingleWordInOperand(0); + + // Get all instructions at our disposal that compute something of this element + // type. + auto available_instructions = + type_id_to_available_instructions.find(element_type_id); + + if (available_instructions == type_id_to_available_instructions.cend()) { + // If there are not any instructions available that compute the element type + // of the array then we are not in a position to construct a composite with + // this array type. + return {}; + } + + uint32_t array_length = + GetIRContext() + ->get_def_use_mgr() + ->GetDef(array_type_instruction.GetSingleWordInOperand(1)) + ->GetSingleWordInOperand(0); + + std::vector result; + for (uint32_t index = 0; index < array_length; index++) { + result.push_back(available_instructions + ->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)] + ->result_id()); + } + return result; +} + +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructMatrix( + const opt::Instruction& matrix_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions) { + assert(matrix_type_instruction.opcode() == SpvOpTypeMatrix && + "Precondition: instruction must be a matrix type."); + + // Get the element type for the matrix. + auto element_type_id = matrix_type_instruction.GetSingleWordInOperand(0); + + // Get all instructions at our disposal that compute something of this element + // type. + auto available_instructions = + type_id_to_available_instructions.find(element_type_id); + + if (available_instructions == type_id_to_available_instructions.cend()) { + // If there are not any instructions available that compute the element type + // of the matrix then we are not in a position to construct a composite with + // this matrix type. + return {}; + } + std::vector result; + for (uint32_t index = 0; + index < matrix_type_instruction.GetSingleWordInOperand(1); index++) { + result.push_back(available_instructions + ->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)] + ->result_id()); + } + return result; +} + +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructStruct( + const opt::Instruction& struct_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions) { + assert(struct_type_instruction.opcode() == SpvOpTypeStruct && + "Precondition: instruction must be a struct type."); + std::vector result; + // Consider the type of each field of the struct. + for (uint32_t in_operand_index = 0; + in_operand_index < struct_type_instruction.NumInOperands(); + in_operand_index++) { + auto element_type_id = + struct_type_instruction.GetSingleWordInOperand(in_operand_index); + // Find the instructions at our disposal that compute something of the field + // type. + auto available_instructions = + type_id_to_available_instructions.find(element_type_id); + if (available_instructions == type_id_to_available_instructions.cend()) { + // If there are no such instructions, we cannot construct a composite of + // this struct type. + return {}; + } + result.push_back(available_instructions + ->second[GetFuzzerContext()->RandomIndex( + available_instructions->second)] + ->result_id()); + } + return result; +} + +std::vector +FuzzerPassConstructComposites::FindComponentsToConstructVector( + const opt::Instruction& vector_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions) { + assert(vector_type_instruction.opcode() == SpvOpTypeVector && + "Precondition: instruction must be a vector type."); + + // Get details of the type underlying the vector, and the width of the vector, + // for convenience. + auto element_type_id = vector_type_instruction.GetSingleWordInOperand(0); + auto element_type = GetIRContext()->get_type_mgr()->GetType(element_type_id); + auto element_count = vector_type_instruction.GetSingleWordInOperand(1); + + // Collect a mapping, from type id to width, for scalar/vector types that are + // smaller in width than |vector_type|, but that have the same underlying + // type. For example, if |vector_type| is vec4, the mapping will be: + // { float -> 1, vec2 -> 2, vec3 -> 3 } + // The mapping will have missing entries if some of these types do not exist. + + std::map smaller_vector_type_id_to_width; + // Add the underlying type. This id must exist, in order for |vector_type| to + // exist. + smaller_vector_type_id_to_width[element_type_id] = 1; + + // Now add every vector type with width at least 2, and less than the width of + // |vector_type|. + for (uint32_t width = 2; width < element_count; width++) { + opt::analysis::Vector smaller_vector_type(element_type, width); + auto smaller_vector_type_id = + GetIRContext()->get_type_mgr()->GetId(&smaller_vector_type); + // We might find that there is no declared type of this smaller width. + // For example, a module can declare vec4 without having declared vec2 or + // vec3. + if (smaller_vector_type_id) { + smaller_vector_type_id_to_width[smaller_vector_type_id] = width; + } + } + + // Now we know the types that are available to us, we set about populating a + // vector of the right length. We do this by deciding, with no order in mind, + // which instructions we will use to populate the vector, and subsequently + // randomly choosing an order. This is to avoid biasing construction of + // vectors with smaller vectors to the left and scalars to the right. That is + // a concern because, e.g. in the case of populating a vec4, if we populate + // the constructor instructions left-to-right, we can always choose a vec3 to + // construct the first three elements, but can only choose a vec3 to construct + // the last three elements if we chose a float to construct the first element + // (otherwise there will not be space left for a vec3). + + uint32_t vector_slots_used = 0; + // The instructions we will use to construct the vector, in no particular + // order at this stage. + std::vector instructions_to_use; + + while (vector_slots_used < element_count) { + std::vector instructions_to_choose_from; + for (auto& entry : smaller_vector_type_id_to_width) { + if (entry.second > + std::min(element_count - 1, element_count - vector_slots_used)) { + continue; + } + auto available_instructions = + type_id_to_available_instructions.find(entry.first); + if (available_instructions == type_id_to_available_instructions.cend()) { + continue; + } + instructions_to_choose_from.insert(instructions_to_choose_from.end(), + available_instructions->second.begin(), + available_instructions->second.end()); + } + if (instructions_to_choose_from.empty()) { + // We may get unlucky and find that there are not any instructions to + // choose from. In this case we give up constructing a composite of this + // vector type. It might be that we could construct the composite in + // another manner, so we could opt to retry a few times here, but it is + // simpler to just give up on the basis that this will not happen + // frequently. + return {}; + } + auto instruction_to_use = + instructions_to_choose_from[GetFuzzerContext()->RandomIndex( + instructions_to_choose_from)]; + instructions_to_use.push_back(instruction_to_use); + auto chosen_type = + GetIRContext()->get_type_mgr()->GetType(instruction_to_use->type_id()); + if (chosen_type->AsVector()) { + assert(chosen_type->AsVector()->element_type() == element_type); + assert(chosen_type->AsVector()->element_count() < element_count); + assert(chosen_type->AsVector()->element_count() <= + element_count - vector_slots_used); + vector_slots_used += chosen_type->AsVector()->element_count(); + } else { + assert(chosen_type == element_type); + vector_slots_used += 1; + } + } + assert(vector_slots_used == element_count); + + std::vector result; + std::vector operands; + while (!instructions_to_use.empty()) { + auto index = GetFuzzerContext()->RandomIndex(instructions_to_use); + result.push_back(instructions_to_use[index]->result_id()); + instructions_to_use.erase(instructions_to_use.begin() + index); + } + assert(result.size() > 1); + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h new file mode 100644 index 0000000..c140bde --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_construct_composites.h @@ -0,0 +1,80 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ + +#include +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for constructing composite objects from smaller objects. +class FuzzerPassConstructComposites : public FuzzerPass { + public: + FuzzerPassConstructComposites( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassConstructComposites(); + + void Apply() override; + + private: + // Used to map a type id to relevant instructions whose result type matches + // the type id. + typedef std::map> + TypeIdToInstructions; + + // Considers all instructions that are available at |inst| - instructions + // whose results could be packed into a composite - and updates + // |type_id_to_available_instructions| so that each such instruction is + // associated with its the id of its result type. + void RecordAvailableInstruction( + opt::Instruction* inst, + TypeIdToInstructions* type_id_to_available_instructions); + + // Requires that |array_type_instruction| has opcode OpTypeArray. + // Attempts to find suitable instruction result ids from the values of + // |type_id_to_available_instructions| that would allow a composite of type + // |array_type_instruction| to be constructed. Returns said ids if they can + // be found and an empty vector otherwise. + std::vector FindComponentsToConstructArray( + const opt::Instruction& array_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to FindComponentsToConstructArray, but for matrices. + std::vector FindComponentsToConstructMatrix( + const opt::Instruction& matrix_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to FindComponentsToConstructArray, but for structs. + std::vector FindComponentsToConstructStruct( + const opt::Instruction& struct_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions); + + // Similar to FindComponentsToConstructArray, but for vectors. + std::vector FindComponentsToConstructVector( + const opt::Instruction& vector_type_instruction, + const TypeIdToInstructions& type_id_to_available_instructions); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_CONSTRUCT_COMPOSITES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp new file mode 100644 index 0000000..9f7bbd6 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_copy_objects.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_add_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassCopyObjects::FuzzerPassCopyObjects( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassCopyObjects::~FuzzerPassCopyObjects() = default; + +void FuzzerPassCopyObjects::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(inst_it->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + if (GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id())) { + // Don't create synonyms in dead blocks. + return; + } + + // Check whether it is legitimate to insert a copy before this + // instruction. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyObject, + inst_it)) { + return; + } + + // Randomly decide whether to try inserting an object copy here. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfCopyingObject())) { + return; + } + + const auto relevant_instructions = FindAvailableInstructions( + function, block, inst_it, + [this](opt::IRContext* ir_context, opt::Instruction* inst) { + return TransformationAddSynonym::IsInstructionValid( + ir_context, *GetTransformationContext(), inst, + protobufs::TransformationAddSynonym::COPY_OBJECT); + }); + + // At this point, |relevant_instructions| contains all the instructions + // we might think of copying. + if (relevant_instructions.empty()) { + return; + } + + // Choose a copyable instruction at random, and create and apply an + // object copying transformation based on it. + ApplyTransformation(TransformationAddSynonym( + relevant_instructions[GetFuzzerContext()->RandomIndex( + relevant_instructions)] + ->result_id(), + protobufs::TransformationAddSynonym::COPY_OBJECT, + GetFuzzerContext()->GetFreshId(), instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h new file mode 100644 index 0000000..8de382e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_copy_objects.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for adding adding copies of objects to the module. +class FuzzerPassCopyObjects : public FuzzerPass { + public: + FuzzerPassCopyObjects(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassCopyObjects(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_COPY_OBJECTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp new file mode 100644 index 0000000..4cd4804 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.cpp @@ -0,0 +1,1213 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_donate_modules.h" + +#include +#include +#include + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/instruction_message.h" +#include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_composite.h" +#include "source/fuzz/transformation_add_constant_null.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_function.h" +#include "source/fuzz/transformation_add_global_undef.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_spec_constant_op.h" +#include "source/fuzz/transformation_add_type_array.h" +#include "source/fuzz/transformation_add_type_boolean.h" +#include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_function.h" +#include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_matrix.h" +#include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" +#include "source/fuzz/transformation_add_type_vector.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassDonateModules::FuzzerPassDonateModules( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + const std::vector& donor_suppliers) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations), + donor_suppliers_(donor_suppliers) {} + +FuzzerPassDonateModules::~FuzzerPassDonateModules() = default; + +void FuzzerPassDonateModules::Apply() { + // If there are no donor suppliers, this fuzzer pass is a no-op. + if (donor_suppliers_.empty()) { + return; + } + + // Donate at least one module, and probabilistically decide when to stop + // donating modules. + do { + // Choose a donor supplier at random, and get the module that it provides. + std::unique_ptr donor_ir_context = donor_suppliers_.at( + GetFuzzerContext()->RandomIndex(donor_suppliers_))(); + assert(donor_ir_context != nullptr && "Supplying of donor failed"); + assert( + fuzzerutil::IsValid(donor_ir_context.get(), + GetTransformationContext()->GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer) && + "The donor module must be valid"); + // Donate the supplied module. + // + // Randomly decide whether to make the module livesafe (see + // FactFunctionIsLivesafe); doing so allows it to be used for live code + // injection but restricts its behaviour to allow this, and means that its + // functions cannot be transformed as if they were arbitrary dead code. + bool make_livesafe = GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->ChanceOfMakingDonorLivesafe()); + DonateSingleModule(donor_ir_context.get(), make_livesafe); + } while (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfDonatingAdditionalModule())); +} + +void FuzzerPassDonateModules::DonateSingleModule( + opt::IRContext* donor_ir_context, bool make_livesafe) { + // Check that the donated module has capabilities, supported by the recipient + // module. + for (const auto& capability_inst : donor_ir_context->capabilities()) { + auto capability = + static_cast(capability_inst.GetSingleWordInOperand(0)); + if (!GetIRContext()->get_feature_mgr()->HasCapability(capability)) { + return; + } + } + + // The ids used by the donor module may very well clash with ids defined in + // the recipient module. Furthermore, some instructions defined in the donor + // module will be equivalent to instructions defined in the recipient module, + // and it is not always legal to re-declare equivalent instructions. For + // example, OpTypeVoid cannot be declared twice. + // + // To handle this, we maintain a mapping from an id used in the donor module + // to the corresponding id that will be used by the donated code when it + // appears in the recipient module. + // + // This mapping is populated in two ways: + // (1) by mapping a donor instruction's result id to the id of some equivalent + // existing instruction in the recipient (e.g. this has to be done for + // OpTypeVoid) + // (2) by mapping a donor instruction's result id to a freshly chosen id that + // is guaranteed to be different from any id already used by the recipient + // (or from any id already chosen to handle a previous donor id) + std::map original_id_to_donated_id; + + HandleExternalInstructionImports(donor_ir_context, + &original_id_to_donated_id); + HandleTypesAndValues(donor_ir_context, &original_id_to_donated_id); + HandleFunctions(donor_ir_context, &original_id_to_donated_id, make_livesafe); + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3115) Handle some + // kinds of decoration. +} + +SpvStorageClass FuzzerPassDonateModules::AdaptStorageClass( + SpvStorageClass donor_storage_class) { + switch (donor_storage_class) { + case SpvStorageClassFunction: + case SpvStorageClassPrivate: + case SpvStorageClassWorkgroup: + // We leave these alone + return donor_storage_class; + case SpvStorageClassInput: + case SpvStorageClassOutput: + case SpvStorageClassUniform: + case SpvStorageClassUniformConstant: + case SpvStorageClassPushConstant: + case SpvStorageClassImage: + case SpvStorageClassStorageBuffer: + // We change these to Private + return SpvStorageClassPrivate; + default: + // Handle other cases on demand. + assert(false && "Currently unsupported storage class."); + return SpvStorageClassMax; + } +} + +void FuzzerPassDonateModules::HandleExternalInstructionImports( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id) { + // Consider every external instruction set import in the donor module. + for (auto& donor_import : donor_ir_context->module()->ext_inst_imports()) { + const auto& donor_import_name_words = donor_import.GetInOperand(0).words; + // Look for an identical import in the recipient module. + for (auto& existing_import : GetIRContext()->module()->ext_inst_imports()) { + const auto& existing_import_name_words = + existing_import.GetInOperand(0).words; + if (donor_import_name_words == existing_import_name_words) { + // A matching import has found. Map the result id for the donor import + // to the id of the existing import, so that when donor instructions + // rely on the import they will be rewritten to use the existing import. + original_id_to_donated_id->insert( + {donor_import.result_id(), existing_import.result_id()}); + break; + } + } + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3116): At present + // we do not handle donation of instruction imports, i.e. we do not allow + // the donor to import instruction sets that the recipient did not already + // import. It might be a good idea to allow this, but it requires some + // thought. + assert(original_id_to_donated_id->count(donor_import.result_id()) && + "Donation of imports is not yet supported."); + } +} + +void FuzzerPassDonateModules::HandleTypesAndValues( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id) { + // Consider every type/global/constant/undef in the module. + for (auto& type_or_value : donor_ir_context->module()->types_values()) { + HandleTypeOrValue(type_or_value, original_id_to_donated_id); + } +} + +void FuzzerPassDonateModules::HandleTypeOrValue( + const opt::Instruction& type_or_value, + std::map* original_id_to_donated_id) { + // The type/value instruction generates a result id, and we need to associate + // the donor's result id with a new result id. That new result id will either + // be the id of some existing instruction, or a fresh id. This variable + // captures it. + uint32_t new_result_id; + + // Decide how to handle each kind of instruction on a case-by-case basis. + // + // Because the donor module is required to be valid, when we encounter a + // type comprised of component types (e.g. an aggregate or pointer), we know + // that its component types will have been considered previously, and that + // |original_id_to_donated_id| will already contain an entry for them. + switch (type_or_value.opcode()) { + case SpvOpTypeImage: + case SpvOpTypeSampledImage: + case SpvOpTypeSampler: + // We do not donate types and variables that relate to images and + // samplers, so we skip these types and subsequently skip anything that + // depends on them. + return; + case SpvOpTypeVoid: { + // Void has to exist already in order for us to have an entry point. + // Get the existing id of void. + opt::analysis::Void void_type; + new_result_id = GetIRContext()->get_type_mgr()->GetId(&void_type); + assert(new_result_id && + "The module being transformed will always have 'void' type " + "declared."); + } break; + case SpvOpTypeBool: { + // Bool cannot be declared multiple times, so use its existing id if + // present, or add a declaration of Bool with a fresh id if not. + opt::analysis::Bool bool_type; + auto bool_type_id = GetIRContext()->get_type_mgr()->GetId(&bool_type); + if (bool_type_id) { + new_result_id = bool_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeBoolean(new_result_id)); + } + } break; + case SpvOpTypeInt: { + // Int cannot be declared multiple times with the same width and + // signedness, so check whether an existing identical Int type is + // present and use its id if so. Otherwise add a declaration of the + // Int type used by the donor, with a fresh id. + const uint32_t width = type_or_value.GetSingleWordInOperand(0); + const bool is_signed = + static_cast(type_or_value.GetSingleWordInOperand(1)); + opt::analysis::Integer int_type(width, is_signed); + auto int_type_id = GetIRContext()->get_type_mgr()->GetId(&int_type); + if (int_type_id) { + new_result_id = int_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeInt(new_result_id, width, is_signed)); + } + } break; + case SpvOpTypeFloat: { + // Similar to SpvOpTypeInt. + const uint32_t width = type_or_value.GetSingleWordInOperand(0); + opt::analysis::Float float_type(width); + auto float_type_id = GetIRContext()->get_type_mgr()->GetId(&float_type); + if (float_type_id) { + new_result_id = float_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeFloat(new_result_id, width)); + } + } break; + case SpvOpTypeVector: { + // It is not legal to have two Vector type declarations with identical + // element types and element counts, so check whether an existing + // identical Vector type is present and use its id if so. Otherwise add + // a declaration of the Vector type used by the donor, with a fresh id. + + // When considering the vector's component type id, we look up the id + // use in the donor to find the id to which this has been remapped. + uint32_t component_type_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)); + auto component_type = + GetIRContext()->get_type_mgr()->GetType(component_type_id); + assert(component_type && "The base type should be registered."); + auto component_count = type_or_value.GetSingleWordInOperand(1); + opt::analysis::Vector vector_type(component_type, component_count); + auto vector_type_id = GetIRContext()->get_type_mgr()->GetId(&vector_type); + if (vector_type_id) { + new_result_id = vector_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeVector( + new_result_id, component_type_id, component_count)); + } + } break; + case SpvOpTypeMatrix: { + // Similar to SpvOpTypeVector. + uint32_t column_type_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)); + auto column_type = + GetIRContext()->get_type_mgr()->GetType(column_type_id); + assert(column_type && column_type->AsVector() && + "The column type should be a registered vector type."); + auto column_count = type_or_value.GetSingleWordInOperand(1); + opt::analysis::Matrix matrix_type(column_type, column_count); + auto matrix_type_id = GetIRContext()->get_type_mgr()->GetId(&matrix_type); + if (matrix_type_id) { + new_result_id = matrix_type_id; + } else { + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeMatrix( + new_result_id, column_type_id, column_count)); + } + + } break; + case SpvOpTypeArray: { + // It is OK to have multiple structurally identical array types, so + // we go ahead and add a remapped version of the type declared by the + // donor. + uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate the component type of this array type, so we + // cannot donate the array type. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeArray( + new_result_id, original_id_to_donated_id->at(component_type_id), + original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(1)))); + } break; + case SpvOpTypeRuntimeArray: { + // A runtime array is allowed as the final member of an SSBO. During + // donation we turn runtime arrays into fixed-size arrays. For dead + // code donations this is OK because the array is never indexed into at + // runtime, so it does not matter what its size is. For live-safe code, + // all accesses are made in-bounds, so this is also OK. + // + // The special OpArrayLength instruction, which works on runtime arrays, + // is rewritten to yield the fixed length that is used for the array. + + uint32_t component_type_id = type_or_value.GetSingleWordInOperand(0); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate the component type of this runtime array type, so + // we cannot donate it as a fixed-size array. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypeArray( + new_result_id, original_id_to_donated_id->at(component_type_id), + FindOrCreateIntegerConstant( + {GetFuzzerContext()->GetRandomSizeForNewArray()}, 32, false, + false))); + } break; + case SpvOpTypeStruct: { + // Similar to SpvOpTypeArray. + std::vector member_type_ids; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + auto component_type_id = type_or_value.GetSingleWordInOperand(i); + if (!original_id_to_donated_id->count(component_type_id)) { + // We did not donate every member type for this struct type, so we + // cannot donate the struct type. + return; + } + member_type_ids.push_back( + original_id_to_donated_id->at(component_type_id)); + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation( + TransformationAddTypeStruct(new_result_id, member_type_ids)); + } break; + case SpvOpTypePointer: { + // Similar to SpvOpTypeArray. + uint32_t pointee_type_id = type_or_value.GetSingleWordInOperand(1); + if (!original_id_to_donated_id->count(pointee_type_id)) { + // We did not donate the pointee type for this pointer type, so we + // cannot donate the pointer type. + return; + } + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddTypePointer( + new_result_id, + AdaptStorageClass(static_cast( + type_or_value.GetSingleWordInOperand(0))), + original_id_to_donated_id->at(pointee_type_id))); + } break; + case SpvOpTypeFunction: { + // It is not OK to have multiple function types that use identical ids + // for their return and parameter types. We thus go through all + // existing function types to look for a match. We do not use the + // type manager here because we want to regard two function types that + // are structurally identical but that differ with respect to the + // actual ids used for pointer types as different. + // + // Example: + // + // %1 = OpTypeVoid + // %2 = OpTypeInt 32 0 + // %3 = OpTypePointer Function %2 + // %4 = OpTypePointer Function %2 + // %5 = OpTypeFunction %1 %3 + // %6 = OpTypeFunction %1 %4 + // + // We regard %5 and %6 as distinct function types here, even though + // they both have the form "uint32* -> void" + + std::vector return_and_parameter_types; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + uint32_t return_or_parameter_type = + type_or_value.GetSingleWordInOperand(i); + if (!original_id_to_donated_id->count(return_or_parameter_type)) { + // We did not donate every return/parameter type for this function + // type, so we cannot donate the function type. + return; + } + return_and_parameter_types.push_back( + original_id_to_donated_id->at(return_or_parameter_type)); + } + uint32_t existing_function_id = fuzzerutil::FindFunctionType( + GetIRContext(), return_and_parameter_types); + if (existing_function_id) { + new_result_id = existing_function_id; + } else { + // No match was found, so add a remapped version of the function type + // to the module, with a fresh id. + new_result_id = GetFuzzerContext()->GetFreshId(); + std::vector argument_type_ids; + for (uint32_t i = 1; i < type_or_value.NumInOperands(); i++) { + argument_type_ids.push_back(original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(i))); + } + ApplyTransformation(TransformationAddTypeFunction( + new_result_id, + original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(0)), + argument_type_ids)); + } + } break; + case SpvOpSpecConstantOp: { + new_result_id = GetFuzzerContext()->GetFreshId(); + auto type_id = original_id_to_donated_id->at(type_or_value.type_id()); + auto opcode = static_cast(type_or_value.GetSingleWordInOperand(0)); + + // Make sure we take into account |original_id_to_donated_id| when + // computing operands for OpSpecConstantOp. + opt::Instruction::OperandList operands; + for (uint32_t i = 1; i < type_or_value.NumInOperands(); ++i) { + const auto& operand = type_or_value.GetInOperand(i); + auto data = + operand.type == SPV_OPERAND_TYPE_ID + ? opt::Operand::OperandData{original_id_to_donated_id->at( + operand.words[0])} + : operand.words; + + operands.push_back({operand.type, std::move(data)}); + } + + ApplyTransformation(TransformationAddSpecConstantOp( + new_result_id, type_id, opcode, std::move(operands))); + } break; + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + case SpvOpConstantTrue: + case SpvOpConstantFalse: { + // It is OK to have duplicate definitions of True and False, so add + // these to the module, using a remapped Bool type. + new_result_id = GetFuzzerContext()->GetFreshId(); + auto value = type_or_value.opcode() == SpvOpConstantTrue || + type_or_value.opcode() == SpvOpSpecConstantTrue; + ApplyTransformation( + TransformationAddConstantBoolean(new_result_id, value, false)); + } break; + case SpvOpSpecConstant: + case SpvOpConstant: { + // It is OK to have duplicate constant definitions, so add this to the + // module using a remapped result type. + new_result_id = GetFuzzerContext()->GetFreshId(); + std::vector data_words; + type_or_value.ForEachInOperand([&data_words](const uint32_t* in_operand) { + data_words.push_back(*in_operand); + }); + ApplyTransformation(TransformationAddConstantScalar( + new_result_id, original_id_to_donated_id->at(type_or_value.type_id()), + data_words, false)); + } break; + case SpvOpSpecConstantComposite: + case SpvOpConstantComposite: { + assert(original_id_to_donated_id->count(type_or_value.type_id()) && + "Composite types for which it is possible to create a constant " + "should have been donated."); + + // It is OK to have duplicate constant composite definitions, so add + // this to the module using remapped versions of all consituent ids and + // the result type. + new_result_id = GetFuzzerContext()->GetFreshId(); + std::vector constituent_ids; + type_or_value.ForEachInId([&constituent_ids, &original_id_to_donated_id]( + const uint32_t* constituent_id) { + assert(original_id_to_donated_id->count(*constituent_id) && + "The constants used to construct this composite should " + "have been donated."); + constituent_ids.push_back( + original_id_to_donated_id->at(*constituent_id)); + }); + ApplyTransformation(TransformationAddConstantComposite( + new_result_id, original_id_to_donated_id->at(type_or_value.type_id()), + constituent_ids, false)); + } break; + case SpvOpConstantNull: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the type associated with this null constant, so + // we cannot donate the null constant. + return; + } + + // It is fine to have multiple OpConstantNull instructions of the same + // type, so we just add this to the recipient module. + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddConstantNull( + new_result_id, + original_id_to_donated_id->at(type_or_value.type_id()))); + } break; + case SpvOpVariable: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the pointer type associated with this variable, + // so we cannot donate the variable. + return; + } + + // This is a global variable that could have one of various storage + // classes. However, we change all global variable pointer storage + // classes (such as Uniform, Input and Output) to private when donating + // pointer types, with the exception of the Workgroup storage class. + // + // Thus this variable's pointer type is guaranteed to have storage class + // Private or Workgroup. + // + // We add a global variable with either Private or Workgroup storage + // class, using remapped versions of the result type and initializer ids + // for the global variable in the donor. + // + // We regard the added variable as having an irrelevant value. This + // means that future passes can add stores to the variable in any + // way they wish, and pass them as pointer parameters to functions + // without worrying about whether their data might get modified. + new_result_id = GetFuzzerContext()->GetFreshId(); + uint32_t remapped_pointer_type = + original_id_to_donated_id->at(type_or_value.type_id()); + uint32_t initializer_id; + SpvStorageClass storage_class = + static_cast(type_or_value.GetSingleWordInOperand( + 0)) == SpvStorageClassWorkgroup + ? SpvStorageClassWorkgroup + : SpvStorageClassPrivate; + if (type_or_value.NumInOperands() == 1) { + // The variable did not have an initializer. Initialize it to zero + // if it has Private storage class (to limit problems associated with + // uninitialized data), and leave it uninitialized if it has Workgroup + // storage class (as Workgroup variables cannot have initializers). + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3275): we + // could initialize Workgroup variables at the start of an entry + // point, and should do so if their uninitialized nature proves + // problematic. + initializer_id = storage_class == SpvStorageClassWorkgroup + ? 0 + : FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), remapped_pointer_type), + false); + } else { + // The variable already had an initializer; use its remapped id. + initializer_id = original_id_to_donated_id->at( + type_or_value.GetSingleWordInOperand(1)); + } + ApplyTransformation( + TransformationAddGlobalVariable(new_result_id, remapped_pointer_type, + storage_class, initializer_id, true)); + } break; + case SpvOpUndef: { + if (!original_id_to_donated_id->count(type_or_value.type_id())) { + // We did not donate the type associated with this undef, so we cannot + // donate the undef. + return; + } + + // It is fine to have multiple Undef instructions of the same type, so + // we just add this to the recipient module. + new_result_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationAddGlobalUndef( + new_result_id, + original_id_to_donated_id->at(type_or_value.type_id()))); + } break; + default: { + assert(0 && "Unknown type/value."); + new_result_id = 0; + } break; + } + + // Update the id mapping to associate the instruction's result id with its + // corresponding id in the recipient. + original_id_to_donated_id->insert({type_or_value.result_id(), new_result_id}); +} + +void FuzzerPassDonateModules::HandleFunctions( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + bool make_livesafe) { + // Get the ids of functions in the donor module, topologically sorted + // according to the donor's call graph. + auto topological_order = + CallGraph(donor_ir_context).GetFunctionsInTopologicalOrder(); + + // Donate the functions in reverse topological order. This ensures that a + // function gets donated before any function that depends on it. This allows + // donation of the functions to be separated into a number of transformations, + // each adding one function, such that every prefix of transformations leaves + // the module valid. + for (auto function_id = topological_order.rbegin(); + function_id != topological_order.rend(); ++function_id) { + // Find the function to be donated. + opt::Function* function_to_donate = nullptr; + for (auto& function : *donor_ir_context->module()) { + if (function.result_id() == *function_id) { + function_to_donate = &function; + break; + } + } + assert(function_to_donate && "Function to be donated was not found."); + + if (!original_id_to_donated_id->count( + function_to_donate->DefInst().GetSingleWordInOperand(1))) { + // We were not able to donate this function's type, so we cannot donate + // the function. + continue; + } + + // We will collect up protobuf messages representing the donor function's + // instructions here, and use them to create an AddFunction transformation. + std::vector donated_instructions; + + // This set tracks the ids of those instructions for which donation was + // completely skipped: neither the instruction nor a substitute for it was + // donated. + std::set skipped_instructions; + + // Consider every instruction of the donor function. + function_to_donate->ForEachInst( + [this, &donated_instructions, donor_ir_context, + &original_id_to_donated_id, + &skipped_instructions](const opt::Instruction* instruction) { + if (instruction->opcode() == SpvOpArrayLength) { + // We treat OpArrayLength specially. + HandleOpArrayLength(*instruction, original_id_to_donated_id, + &donated_instructions); + } else if (!CanDonateInstruction(donor_ir_context, *instruction, + *original_id_to_donated_id, + skipped_instructions)) { + // This is an instruction that we cannot directly donate. + HandleDifficultInstruction(*instruction, original_id_to_donated_id, + &donated_instructions, + &skipped_instructions); + } else { + PrepareInstructionForDonation(*instruction, donor_ir_context, + original_id_to_donated_id, + &donated_instructions); + } + }); + + // If |make_livesafe| is true, try to add the function in a livesafe manner. + // Otherwise (if |make_lifesafe| is false or an attempt to make the function + // livesafe has failed), add the function in a non-livesafe manner. + if (!make_livesafe || + !MaybeAddLivesafeFunction(*function_to_donate, donor_ir_context, + *original_id_to_donated_id, + donated_instructions)) { + ApplyTransformation(TransformationAddFunction(donated_instructions)); + } + } +} + +bool FuzzerPassDonateModules::CanDonateInstruction( + opt::IRContext* donor_ir_context, const opt::Instruction& instruction, + const std::map& original_id_to_donated_id, + const std::set& skipped_instructions) const { + if (instruction.type_id() && + !original_id_to_donated_id.count(instruction.type_id())) { + // We could not donate the result type of this instruction, so we cannot + // donate the instruction. + return false; + } + + // Now consider instructions we specifically want to skip because we do not + // yet support them. + switch (instruction.opcode()) { + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + // We conservatively ignore all atomic instructions at present. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3276): Consider + // being less conservative here. + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageFetch: + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImageRead: + case SpvOpImageWrite: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + case SpvOpImageSparseFetch: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + case SpvOpImageSparseRead: + case SpvOpImageSampleFootprintNV: + case SpvOpImage: + case SpvOpImageQueryFormat: + case SpvOpImageQueryLevels: + case SpvOpImageQueryLod: + case SpvOpImageQueryOrder: + case SpvOpImageQuerySamples: + case SpvOpImageQuerySize: + case SpvOpImageQuerySizeLod: + case SpvOpSampledImage: + // We ignore all instructions related to accessing images, since we do not + // donate images. + return false; + case SpvOpLoad: + switch (donor_ir_context->get_def_use_mgr() + ->GetDef(instruction.type_id()) + ->opcode()) { + case SpvOpTypeImage: + case SpvOpTypeSampledImage: + case SpvOpTypeSampler: + // Again, we ignore instructions that relate to accessing images. + return false; + default: + break; + } + default: + break; + } + + // Examine each id input operand to the instruction. If it turns out that we + // have skipped any of these operands then we cannot donate the instruction. + bool result = true; + instruction.WhileEachInId( + [donor_ir_context, &original_id_to_donated_id, &result, + &skipped_instructions](const uint32_t* in_id) -> bool { + if (!original_id_to_donated_id.count(*in_id)) { + // We do not have a mapped result id for this id operand. That either + // means that it is a forward reference (which is OK), that we skipped + // the instruction that generated it (which is not OK), or that it is + // the id of a function or global value that we did not donate (which + // is not OK). We check for the latter two cases. + if (skipped_instructions.count(*in_id) || + // A function or global value does not have an associated basic + // block. + !donor_ir_context->get_instr_block(*in_id)) { + result = false; + return false; + } + } + return true; + }); + return result; +} + +bool FuzzerPassDonateModules::IsBasicType( + const opt::Instruction& instruction) const { + switch (instruction.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeBool: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeMatrix: + case SpvOpTypeStruct: + case SpvOpTypeVector: + return true; + default: + return false; + } +} + +void FuzzerPassDonateModules::HandleOpArrayLength( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) const { + assert(instruction.opcode() == SpvOpArrayLength && + "Precondition: instruction must be OpArrayLength."); + uint32_t donated_variable_id = + original_id_to_donated_id->at(instruction.GetSingleWordInOperand(0)); + auto donated_variable_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(donated_variable_id); + auto pointer_to_struct_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + donated_variable_instruction->type_id()); + assert(pointer_to_struct_instruction->opcode() == SpvOpTypePointer && + "Type of variable must be pointer."); + auto donated_struct_type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + pointer_to_struct_instruction->GetSingleWordInOperand(1)); + assert(donated_struct_type_instruction->opcode() == SpvOpTypeStruct && + "Pointee type of pointer used by OpArrayLength must be struct."); + assert(donated_struct_type_instruction->NumInOperands() == + instruction.GetSingleWordInOperand(1) + 1 && + "OpArrayLength must refer to the final member of the given " + "struct."); + uint32_t fixed_size_array_type_id = + donated_struct_type_instruction->GetSingleWordInOperand( + donated_struct_type_instruction->NumInOperands() - 1); + auto fixed_size_array_type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef(fixed_size_array_type_id); + assert(fixed_size_array_type_instruction->opcode() == SpvOpTypeArray && + "The donated array type must be fixed-size."); + auto array_size_id = + fixed_size_array_type_instruction->GetSingleWordInOperand(1); + + if (instruction.result_id() && + !original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + donated_instructions->push_back(MakeInstructionMessage( + SpvOpCopyObject, original_id_to_donated_id->at(instruction.type_id()), + original_id_to_donated_id->at(instruction.result_id()), + opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {array_size_id}}}))); +} + +void FuzzerPassDonateModules::HandleDifficultInstruction( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions, + std::set* skipped_instructions) { + if (!instruction.result_id()) { + // It does not generate a result id, so it can be ignored. + return; + } + if (!original_id_to_donated_id->count(instruction.type_id())) { + // We cannot handle this instruction's result type, so we need to skip it + // all together. + skipped_instructions->insert(instruction.result_id()); + return; + } + + // We now attempt to replace the instruction with an OpCopyObject. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3278): We could do + // something more refined here - we could check which operands to the + // instruction could not be donated and replace those operands with + // references to other ids (such as constants), so that we still get an + // instruction with the opcode and easy-to-handle operands of the donor + // instruction. + auto remapped_type_id = original_id_to_donated_id->at(instruction.type_id()); + if (!IsBasicType( + *GetIRContext()->get_def_use_mgr()->GetDef(remapped_type_id))) { + // The instruction has a non-basic result type, so we cannot replace it with + // an object copy of a constant. We thus skip it completely. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3279): We could + // instead look for an available id of the right type and generate an + // OpCopyObject of that id. + skipped_instructions->insert(instruction.result_id()); + return; + } + + // We are going to add an OpCopyObject instruction. Add a mapping for the + // result id of the original instruction if does not already exist (it may + // exist in the case that it has been forward-referenced). + if (!original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + // We find or add a zero constant to the receiving module for the type in + // question, and add an OpCopyObject instruction that copies this zero. + // + // We mark the constant as irrelevant so that we can replace it with a + // more interesting value later. + auto zero_constant = FindOrCreateZeroConstant(remapped_type_id, true); + donated_instructions->push_back(MakeInstructionMessage( + SpvOpCopyObject, remapped_type_id, + original_id_to_donated_id->at(instruction.result_id()), + opt::Instruction::OperandList({{SPV_OPERAND_TYPE_ID, {zero_constant}}}))); +} + +void FuzzerPassDonateModules::PrepareInstructionForDonation( + const opt::Instruction& instruction, opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) { + // Get the instruction's input operands into donation-ready form, + // remapping any id uses in the process. + opt::Instruction::OperandList input_operands; + + // Consider each input operand in turn. + for (uint32_t in_operand_index = 0; + in_operand_index < instruction.NumInOperands(); in_operand_index++) { + std::vector operand_data; + const opt::Operand& in_operand = instruction.GetInOperand(in_operand_index); + // Check whether this operand is an id. + if (spvIsIdType(in_operand.type)) { + // This is an id operand - it consists of a single word of data, + // which needs to be remapped so that it is replaced with the + // donated form of the id. + auto operand_id = in_operand.words[0]; + if (!original_id_to_donated_id->count(operand_id)) { + // This is a forward reference. We will choose a corresponding + // donor id for the referenced id and update the mapping to + // reflect it. + + // Keep release compilers happy because |donor_ir_context| is only used + // in this assertion. + (void)(donor_ir_context); + assert((donor_ir_context->get_def_use_mgr() + ->GetDef(operand_id) + ->opcode() == SpvOpLabel || + instruction.opcode() == SpvOpPhi) && + "Unsupported forward reference."); + original_id_to_donated_id->insert( + {operand_id, GetFuzzerContext()->GetFreshId()}); + } + operand_data.push_back(original_id_to_donated_id->at(operand_id)); + } else { + // For non-id operands, we just add each of the data words. + for (auto word : in_operand.words) { + operand_data.push_back(word); + } + } + input_operands.push_back({in_operand.type, operand_data}); + } + + if (instruction.opcode() == SpvOpVariable && + instruction.NumInOperands() == 1) { + // This is an uninitialized local variable. Initialize it to zero. + input_operands.push_back( + {SPV_OPERAND_TYPE_ID, + {FindOrCreateZeroConstant( + fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), + original_id_to_donated_id->at(instruction.type_id())), + false)}}); + } + + if (instruction.result_id() && + !original_id_to_donated_id->count(instruction.result_id())) { + original_id_to_donated_id->insert( + {instruction.result_id(), GetFuzzerContext()->GetFreshId()}); + } + + // Remap the result type and result id (if present) of the + // instruction, and turn it into a protobuf message. + donated_instructions->push_back(MakeInstructionMessage( + instruction.opcode(), + instruction.type_id() + ? original_id_to_donated_id->at(instruction.type_id()) + : 0, + instruction.result_id() + ? original_id_to_donated_id->at(instruction.result_id()) + : 0, + input_operands)); +} + +bool FuzzerPassDonateModules::CreateLoopLimiterInfo( + opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header, + const std::map& original_id_to_donated_id, + protobufs::LoopLimiterInfo* out) { + assert(loop_header.IsLoopHeader() && "|loop_header| is not a loop header"); + + // Grab the loop header's id, mapped to its donated value. + out->set_loop_header_id(original_id_to_donated_id.at(loop_header.id())); + + // Get fresh ids that will be used to load the loop limiter, increment + // it, compare it with the loop limit, and an id for a new block that + // will contain the loop's original terminator. + out->set_load_id(GetFuzzerContext()->GetFreshId()); + out->set_increment_id(GetFuzzerContext()->GetFreshId()); + out->set_compare_id(GetFuzzerContext()->GetFreshId()); + out->set_logical_op_id(GetFuzzerContext()->GetFreshId()); + + // We are creating a branch from the back-edge block to the merge block. Thus, + // if merge block has any OpPhi instructions, we might need to adjust + // them. + + // Note that the loop might have an unreachable back-edge block. This means + // that the loop can't iterate, so we don't need to adjust anything. + const auto back_edge_block_id = TransformationAddFunction::GetBackEdgeBlockId( + donor_ir_context, loop_header.id()); + if (!back_edge_block_id) { + return true; + } + + auto* back_edge_block = donor_ir_context->cfg()->block(back_edge_block_id); + assert(back_edge_block && "|back_edge_block_id| is invalid"); + + const auto* merge_block = + donor_ir_context->cfg()->block(loop_header.MergeBlockId()); + assert(merge_block && "Loop header has invalid merge block id"); + + // We don't need to adjust anything if there is already a branch from + // the back-edge block to the merge block. + if (back_edge_block->IsSuccessor(merge_block)) { + return true; + } + + // Adjust OpPhi instructions in the |merge_block|. + for (const auto& inst : *merge_block) { + if (inst.opcode() != SpvOpPhi) { + break; + } + + // There is no simple way to ensure that a chosen operand for the OpPhi + // instruction will never cause any problems (e.g. if we choose an + // integer id, it might have a zero value when we branch from the back + // edge block. This might cause a division by 0 later in the function.). + // Thus, we ignore possible problems and proceed as follows: + // - if any of the existing OpPhi operands dominates the back-edge + // block - use it + // - if OpPhi has a basic type (see IsBasicType method) - create + // a zero constant + // - otherwise, we can't add a livesafe function. + uint32_t suitable_operand_id = 0; + for (uint32_t i = 0; i < inst.NumInOperands(); i += 2) { + auto dependency_inst_id = inst.GetSingleWordInOperand(i); + + if (fuzzerutil::IdIsAvailableBeforeInstruction( + donor_ir_context, back_edge_block->terminator(), + dependency_inst_id)) { + suitable_operand_id = original_id_to_donated_id.at(dependency_inst_id); + break; + } + } + + if (suitable_operand_id == 0 && + IsBasicType( + *donor_ir_context->get_def_use_mgr()->GetDef(inst.type_id()))) { + // We mark this constant as irrelevant so that we can replace it + // with more interesting value later. + suitable_operand_id = FindOrCreateZeroConstant( + original_id_to_donated_id.at(inst.type_id()), true); + } + + if (suitable_operand_id == 0) { + return false; + } + + out->add_phi_id(suitable_operand_id); + } + + return true; +} + +bool FuzzerPassDonateModules::MaybeAddLivesafeFunction( + const opt::Function& function_to_donate, opt::IRContext* donor_ir_context, + const std::map& original_id_to_donated_id, + const std::vector& donated_instructions) { + // Various types and constants must be in place for a function to be made + // live-safe. Add them if not already present. + FindOrCreateBoolType(); // Needed for comparisons + FindOrCreatePointerToIntegerType( + 32, false, SpvStorageClassFunction); // Needed for adding loop limiters + FindOrCreateIntegerConstant({0}, 32, false, + false); // Needed for initializing loop limiters + FindOrCreateIntegerConstant({1}, 32, false, + false); // Needed for incrementing loop limiters + + // Get a fresh id for the variable that will be used as a loop limiter. + const uint32_t loop_limiter_variable_id = GetFuzzerContext()->GetFreshId(); + // Choose a random loop limit, and add the required constant to the + // module if not already there. + const uint32_t loop_limit = FindOrCreateIntegerConstant( + {GetFuzzerContext()->GetRandomLoopLimit()}, 32, false, false); + + // Consider every loop header in the function to donate, and create a + // structure capturing the ids to be used for manipulating the loop + // limiter each time the loop is iterated. + std::vector loop_limiters; + for (auto& block : function_to_donate) { + if (block.IsLoopHeader()) { + protobufs::LoopLimiterInfo loop_limiter; + + if (!CreateLoopLimiterInfo(donor_ir_context, block, + original_id_to_donated_id, &loop_limiter)) { + return false; + } + + loop_limiters.emplace_back(std::move(loop_limiter)); + } + } + + // Consider every access chain in the function to donate, and create a + // structure containing the ids necessary to clamp the access chain + // indices to be in-bounds. + std::vector access_chain_clamping_info; + for (auto& block : function_to_donate) { + for (auto& inst : block) { + switch (inst.opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: { + protobufs::AccessChainClampingInfo clamping_info; + clamping_info.set_access_chain_id( + original_id_to_donated_id.at(inst.result_id())); + + auto base_object = donor_ir_context->get_def_use_mgr()->GetDef( + inst.GetSingleWordInOperand(0)); + assert(base_object && "The base object must exist."); + auto pointer_type = donor_ir_context->get_def_use_mgr()->GetDef( + base_object->type_id()); + assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer && + "The base object must have pointer type."); + + auto should_be_composite_type = + donor_ir_context->get_def_use_mgr()->GetDef( + pointer_type->GetSingleWordInOperand(1)); + + // Walk the access chain, creating fresh ids to facilitate + // clamping each index. For simplicity we do this for every + // index, even though constant indices will not end up being + // clamped. + for (uint32_t index = 1; index < inst.NumInOperands(); index++) { + auto compare_and_select_ids = + clamping_info.add_compare_and_select_ids(); + compare_and_select_ids->set_first(GetFuzzerContext()->GetFreshId()); + compare_and_select_ids->set_second( + GetFuzzerContext()->GetFreshId()); + + // Get the bound for the component being indexed into. + uint32_t bound; + if (should_be_composite_type->opcode() == SpvOpTypeRuntimeArray) { + // The donor is indexing into a runtime array. We do not + // donate runtime arrays. Instead, we donate a corresponding + // fixed-size array for every runtime array. We should thus + // find that donor composite type's result id maps to a fixed- + // size array. + auto fixed_size_array_type = + GetIRContext()->get_def_use_mgr()->GetDef( + original_id_to_donated_id.at( + should_be_composite_type->result_id())); + assert(fixed_size_array_type->opcode() == SpvOpTypeArray && + "A runtime array type in the donor should have been " + "replaced by a fixed-sized array in the recipient."); + // The size of this fixed-size array is a suitable bound. + bound = fuzzerutil::GetBoundForCompositeIndex( + *fixed_size_array_type, GetIRContext()); + } else { + bound = fuzzerutil::GetBoundForCompositeIndex( + *should_be_composite_type, donor_ir_context); + } + const uint32_t index_id = inst.GetSingleWordInOperand(index); + auto index_inst = + donor_ir_context->get_def_use_mgr()->GetDef(index_id); + auto index_type_inst = donor_ir_context->get_def_use_mgr()->GetDef( + index_inst->type_id()); + assert(index_type_inst->opcode() == SpvOpTypeInt); + opt::analysis::Integer* index_int_type = + donor_ir_context->get_type_mgr() + ->GetType(index_type_inst->result_id()) + ->AsInteger(); + if (index_inst->opcode() != SpvOpConstant) { + // We will have to clamp this index, so we need a constant + // whose value is one less than the bound, to compare + // against and to use as the clamped value. + FindOrCreateIntegerConstant({bound - 1}, 32, + index_int_type->IsSigned(), false); + } + should_be_composite_type = + TransformationAddFunction::FollowCompositeIndex( + donor_ir_context, *should_be_composite_type, index_id); + } + access_chain_clamping_info.push_back(clamping_info); + break; + } + default: + break; + } + } + } + + // If |function_to_donate| has non-void return type and contains an + // OpKill/OpUnreachable instruction, then a value is needed in order to turn + // these into instructions of the form OpReturnValue %value_id. + uint32_t kill_unreachable_return_value_id = 0; + auto function_return_type_inst = + donor_ir_context->get_def_use_mgr()->GetDef(function_to_donate.type_id()); + if (function_return_type_inst->opcode() != SpvOpTypeVoid && + fuzzerutil::FunctionContainsOpKillOrUnreachable(function_to_donate)) { + kill_unreachable_return_value_id = FindOrCreateZeroConstant( + original_id_to_donated_id.at(function_return_type_inst->result_id()), + false); + } + + // Add the function in a livesafe manner. + ApplyTransformation(TransformationAddFunction( + donated_instructions, loop_limiter_variable_id, loop_limit, loop_limiters, + kill_unreachable_return_value_id, access_chain_clamping_info)); + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h new file mode 100644 index 0000000..0424cec --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_donate_modules.h @@ -0,0 +1,164 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that randomly adds code from other SPIR-V modules to the module +// being transformed. +class FuzzerPassDonateModules : public FuzzerPass { + public: + FuzzerPassDonateModules( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations, + const std::vector& donor_suppliers); + + ~FuzzerPassDonateModules(); + + void Apply() override; + + // Donates the global declarations and functions of |donor_ir_context| into + // the fuzzer pass's IR context. |make_livesafe| dictates whether the + // functions of the donated module will be made livesafe (see + // FactFunctionIsLivesafe). + void DonateSingleModule(opt::IRContext* donor_ir_context, bool make_livesafe); + + private: + // Adapts a storage class coming from a donor module so that it will work + // in a recipient module, e.g. by changing Uniform to Private. + static SpvStorageClass AdaptStorageClass(SpvStorageClass donor_storage_class); + + // Identifies all external instruction set imports in |donor_ir_context| and + // populates |original_id_to_donated_id| with a mapping from the donor's id + // for such an import to a corresponding import in the recipient. Aborts if + // no such corresponding import is available. + void HandleExternalInstructionImports( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id); + + // Considers all types, globals, constants and undefs in |donor_ir_context|. + // For each instruction, uses |original_to_donated_id| to map its result id to + // either (1) the id of an existing identical instruction in the recipient, or + // (2) to a fresh id, in which case the instruction is also added to the + // recipient (with any operand ids that it uses being remapped via + // |original_id_to_donated_id|). + void HandleTypesAndValues( + opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id); + + // Helper method for HandleTypesAndValues, to handle a single type/value. + void HandleTypeOrValue( + const opt::Instruction& type_or_value, + std::map* original_id_to_donated_id); + + // Assumes that |donor_ir_context| does not exhibit recursion. Considers the + // functions in |donor_ir_context|'s call graph in a reverse-topologically- + // sorted order (leaves-to-root), adding each function to the recipient + // module, rewritten to use fresh ids and using |original_id_to_donated_id| to + // remap ids. The |make_livesafe| argument captures whether the functions in + // the module are required to be made livesafe before being added to the + // recipient. + void HandleFunctions(opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + bool make_livesafe); + + // During donation we will have to ignore some instructions, e.g. because they + // use opcodes that we cannot support or because they reference the ids of + // instructions that have not been donated. This function encapsulates the + // logic for deciding which whether instruction |instruction| from + // |donor_ir_context| can be donated. + bool CanDonateInstruction( + opt::IRContext* donor_ir_context, const opt::Instruction& instruction, + const std::map& original_id_to_donated_id, + const std::set& skipped_instructions) const; + + // We treat the OpArrayLength instruction specially. In the donor shader this + // instruction yields the length of a runtime array that is the final member + // of a struct. During donation, we will have converted the runtime array + // type, and the associated struct field, into a fixed-size array. + // + // Instead of donating this instruction, we turn it into an OpCopyObject + // instruction that copies the size of the fixed-size array. + void HandleOpArrayLength( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions) const; + + // The instruction |instruction| is required to be an instruction that cannot + // be easily donated, either because it uses an unsupported opcode, has an + // unsupported result type, or uses id operands that could not be donated. + // + // If |instruction| generates a result id, the function attempts to add a + // substitute for |instruction| to |donated_instructions| that has the correct + // result type. If this cannot be done, the instruction's result id is added + // to |skipped_instructions|. The mapping from donor ids to recipient ids is + // managed by |original_id_to_donated_id|. + void HandleDifficultInstruction( + const opt::Instruction& instruction, + std::map* original_id_to_donated_id, + std::vector* donated_instructions, + std::set* skipped_instructions); + + // Adds an instruction based in |instruction| to |donated_instructions| in a + // form ready for donation. The original instruction comes from + // |donor_ir_context|, and |original_id_to_donated_id| maps ids from + // |donor_ir_context| to corresponding ids in the recipient module. + void PrepareInstructionForDonation( + const opt::Instruction& instruction, opt::IRContext* donor_ir_context, + std::map* original_id_to_donated_id, + std::vector* donated_instructions); + + // Tries to create a protobufs::LoopLimiterInfo given a loop header basic + // block. Returns true if successful and outputs loop limiter into the |out| + // variable. Otherwise, returns false. |out| contains an undefined value when + // this function returns false. + bool CreateLoopLimiterInfo( + opt::IRContext* donor_ir_context, const opt::BasicBlock& loop_header, + const std::map& original_id_to_donated_id, + protobufs::LoopLimiterInfo* out); + + // Requires that |donated_instructions| represents a prepared version of the + // instructions of |function_to_donate| (which comes from |donor_ir_context|) + // ready for donation, and |original_id_to_donated_id| maps ids from + // |donor_ir_context| to their corresponding ids in the recipient module. + // + // Attempts to add a livesafe version of the function, based on + // |donated_instructions|, to the recipient module. Returns true if the + // donation was successful, false otherwise. + bool MaybeAddLivesafeFunction( + const opt::Function& function_to_donate, opt::IRContext* donor_ir_context, + const std::map& original_id_to_donated_id, + const std::vector& donated_instructions); + + // Returns true if and only if |instruction| is a scalar, vector, matrix, + // array or struct; i.e. it is not an opaque type. + bool IsBasicType(const opt::Instruction& instruction) const; + + // Functions that supply SPIR-V modules + std::vector donor_suppliers_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_DONATE_MODULES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp new file mode 100644 index 0000000..1651a9d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_duplicate_region_with_selection.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassDuplicateRegionsWithSelections:: + FuzzerPassDuplicateRegionsWithSelections( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassDuplicateRegionsWithSelections:: + ~FuzzerPassDuplicateRegionsWithSelections() = default; + +void FuzzerPassDuplicateRegionsWithSelections::Apply() { + // Iterate over all of the functions in the module. + for (auto& function : *GetIRContext()->module()) { + // Randomly decide whether to apply the transformation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfDuplicatingRegionWithSelection())) { + continue; + } + std::vector candidate_entry_blocks; + for (auto& block : function) { + // We don't consider the first block to be the entry block, since it + // could contain OpVariable instructions that would require additional + // operations to be reassigned. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3778): + // Consider extending this fuzzer pass to allow the first block to be + // used in duplication. + if (&block == &*function.begin()) { + continue; + } + candidate_entry_blocks.push_back(&block); + } + if (candidate_entry_blocks.empty()) { + continue; + } + // Randomly choose the entry block. + auto entry_block = candidate_entry_blocks[GetFuzzerContext()->RandomIndex( + candidate_entry_blocks)]; + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(&function); + auto postdominator_analysis = + GetIRContext()->GetPostDominatorAnalysis(&function); + std::vector candidate_exit_blocks; + for (auto postdominates_entry_block = entry_block; + postdominates_entry_block != nullptr; + postdominates_entry_block = postdominator_analysis->ImmediateDominator( + postdominates_entry_block)) { + // The candidate exit block must be dominated by the entry block and the + // entry block must be post-dominated by the candidate exit block. Ignore + // the block if it heads a selection construct or a loop construct. + if (dominator_analysis->Dominates(entry_block, + postdominates_entry_block) && + !postdominates_entry_block->GetMergeInst()) { + candidate_exit_blocks.push_back(postdominates_entry_block); + } + } + if (candidate_exit_blocks.empty()) { + continue; + } + // Randomly choose the exit block. + auto exit_block = candidate_exit_blocks[GetFuzzerContext()->RandomIndex( + candidate_exit_blocks)]; + + auto region_blocks = + TransformationDuplicateRegionWithSelection::GetRegionBlocks( + GetIRContext(), entry_block, exit_block); + + // Construct |original_label_to_duplicate_label| by iterating over all + // blocks in the region. Construct |original_id_to_duplicate_id| and + // |original_id_to_phi_id| by iterating over all instructions in each block. + std::map original_label_to_duplicate_label; + std::map original_id_to_duplicate_id; + std::map original_id_to_phi_id; + for (auto& block : region_blocks) { + original_label_to_duplicate_label[block->id()] = + GetFuzzerContext()->GetFreshId(); + for (auto& instr : *block) { + if (instr.result_id()) { + original_id_to_duplicate_id[instr.result_id()] = + GetFuzzerContext()->GetFreshId(); + auto final_instruction = &*exit_block->tail(); + // &*exit_block->tail() is the final instruction of the region. + // The instruction is available at the end of the region if and only + // if it is available before this final instruction or it is the final + // instruction. + if ((&instr == final_instruction || + fuzzerutil::IdIsAvailableBeforeInstruction( + GetIRContext(), final_instruction, instr.result_id()))) { + original_id_to_phi_id[instr.result_id()] = + GetFuzzerContext()->GetFreshId(); + } + } + } + } + // Randomly decide between value "true" or "false" for a bool constant. + // Make sure the transformation has access to a bool constant to be used + // while creating conditional construct. + auto condition_id = + FindOrCreateBoolConstant(GetFuzzerContext()->ChooseEven(), true); + + TransformationDuplicateRegionWithSelection transformation = + TransformationDuplicateRegionWithSelection( + GetFuzzerContext()->GetFreshId(), condition_id, + GetFuzzerContext()->GetFreshId(), entry_block->id(), + exit_block->id(), original_label_to_duplicate_label, + original_id_to_duplicate_id, original_id_to_phi_id); + MaybeApplyTransformation(transformation); + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h new file mode 100644 index 0000000..3fae698 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_DUPLICATE_REGIONS_WITH_SELECTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_DUPLICATE_REGIONS_WITH_SELECTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that iterates over the whole module. For each function it +// finds a single-entry, single-exit region with its constructs and their merge +// blocks either completely within or completely outside the region. It +// duplicates this region using the corresponding transformation. +class FuzzerPassDuplicateRegionsWithSelections : public FuzzerPass { + public: + FuzzerPassDuplicateRegionsWithSelections( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassDuplicateRegionsWithSelections() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_DUPLICATE_REGIONS_WITH_SELECTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp new file mode 100644 index 0000000..1416fe0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_expand_vector_reduction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassExpandVectorReductions::FuzzerPassExpandVectorReductions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassExpandVectorReductions::~FuzzerPassExpandVectorReductions() = default; + +void FuzzerPassExpandVectorReductions::Apply() { + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + // Randomly decides whether the transformation will be applied. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfExpandingVectorReduction())) { + continue; + } + + // |instruction| must be OpAny or OpAll. + if (instruction.opcode() != SpvOpAny && + instruction.opcode() != SpvOpAll) { + continue; + } + + // It must be able to make a synonym of |instruction|. + if (!fuzzerutil::CanMakeSynonymOf( + GetIRContext(), *GetTransformationContext(), &instruction)) { + continue; + } + + // Applies the expand vector reduction transformation. + ApplyTransformation(TransformationExpandVectorReduction( + instruction.result_id(), + GetFuzzerContext()->GetFreshIds( + TransformationExpandVectorReduction::GetRequiredFreshIdCount( + GetIRContext(), &instruction)))); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h new file mode 100644 index 0000000..ae3238b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_expand_vector_reductions.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass adds synonyms for the OpAny and OpAll instructions. It +// iterates over the module, checks if there are any OpAny or OpAll applicable +// instructions and randomly applies the expand vector reduction transformation. +class FuzzerPassExpandVectorReductions : public FuzzerPass { + public: + FuzzerPassExpandVectorReductions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassExpandVectorReductions(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_EXPAND_VECTOR_REDUCTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp new file mode 100644 index 0000000..1e21aa5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.cpp @@ -0,0 +1,251 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h" + +#include "source/fuzz/comparator_deep_blocks_first.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_flatten_conditional_branch.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that randomly selects conditional branches to flatten and +// flattens them, if possible. +FuzzerPassFlattenConditionalBranches::FuzzerPassFlattenConditionalBranches( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassFlattenConditionalBranches::~FuzzerPassFlattenConditionalBranches() = + default; + +void FuzzerPassFlattenConditionalBranches::Apply() { + for (auto& function : *GetIRContext()->module()) { + // Get all the selection headers that we want to flatten. We need to collect + // all of them first, because, since we are changing the structure of the + // module, it's not safe to modify them while iterating. + std::vector selection_headers; + + for (auto& block : function) { + // Randomly decide whether to consider this block. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfFlatteningConditionalBranch())) { + continue; + } + + // Only consider this block if it is the header of a conditional, with a + // non-irrelevant condition. + if (block.GetMergeInst() && + block.GetMergeInst()->opcode() == SpvOpSelectionMerge && + block.terminator()->opcode() == SpvOpBranchConditional && + !GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + block.terminator()->GetSingleWordInOperand(0))) { + selection_headers.emplace_back(&block); + } + } + + // Sort the headers so that those that are more deeply nested are considered + // first, possibly enabling outer conditionals to be flattened. + std::sort(selection_headers.begin(), selection_headers.end(), + ComparatorDeepBlocksFirst(GetIRContext())); + + // Apply the transformation to the headers which can be flattened. + for (auto header : selection_headers) { + // Make a set to keep track of the instructions that need fresh ids. + std::set instructions_that_need_ids; + + // Do not consider this header if the conditional cannot be flattened. + if (!TransformationFlattenConditionalBranch:: + GetProblematicInstructionsIfConditionalCanBeFlattened( + GetIRContext(), header, *GetTransformationContext(), + &instructions_that_need_ids)) { + continue; + } + + uint32_t convergence_block_id = + TransformationFlattenConditionalBranch::FindConvergenceBlock( + GetIRContext(), *header); + + // If the SPIR-V version is restricted so that OpSelect can only work on + // scalar, pointer and vector types then we cannot apply this + // transformation to a header whose convergence block features OpPhi + // instructions on different types, as we cannot convert such instructions + // to OpSelect instructions. + if (TransformationFlattenConditionalBranch:: + OpSelectArgumentsAreRestricted(GetIRContext())) { + if (!GetIRContext() + ->cfg() + ->block(convergence_block_id) + ->WhileEachPhiInst( + [this](opt::Instruction* phi_instruction) -> bool { + switch (GetIRContext() + ->get_def_use_mgr() + ->GetDef(phi_instruction->type_id()) + ->opcode()) { + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypePointer: + case SpvOpTypeVector: + return true; + default: + return false; + } + })) { + // An OpPhi is performed on a type not supported by OpSelect; we + // cannot flatten this selection. + continue; + } + } + + // If the construct's convergence block features OpPhi instructions with + // vector result types then we may be *forced*, by the SPIR-V version, to + // turn these into component-wise OpSelect instructions, or we might wish + // to do so anyway. The following booleans capture whether we will opt + // to use a component-wise select even if we don't have to. + bool use_component_wise_2d_select_even_if_optional = + GetFuzzerContext()->ChooseEven(); + bool use_component_wise_3d_select_even_if_optional = + GetFuzzerContext()->ChooseEven(); + bool use_component_wise_4d_select_even_if_optional = + GetFuzzerContext()->ChooseEven(); + + // If we do need to perform any component-wise selections, we will need a + // fresh id for a boolean vector representing the selection's condition + // repeated N times, where N is the vector dimension. + uint32_t fresh_id_for_bvec2_selector = 0; + uint32_t fresh_id_for_bvec3_selector = 0; + uint32_t fresh_id_for_bvec4_selector = 0; + + GetIRContext() + ->cfg() + ->block(convergence_block_id) + ->ForEachPhiInst([this, &fresh_id_for_bvec2_selector, + &fresh_id_for_bvec3_selector, + &fresh_id_for_bvec4_selector, + use_component_wise_2d_select_even_if_optional, + use_component_wise_3d_select_even_if_optional, + use_component_wise_4d_select_even_if_optional]( + opt::Instruction* phi_instruction) { + opt::Instruction* type_instruction = + GetIRContext()->get_def_use_mgr()->GetDef( + phi_instruction->type_id()); + switch (type_instruction->opcode()) { + case SpvOpTypeVector: { + uint32_t dimension = + type_instruction->GetSingleWordInOperand(1); + switch (dimension) { + case 2: + PrepareForOpPhiOnVectors( + dimension, + use_component_wise_2d_select_even_if_optional, + &fresh_id_for_bvec2_selector); + break; + case 3: + PrepareForOpPhiOnVectors( + dimension, + use_component_wise_3d_select_even_if_optional, + &fresh_id_for_bvec3_selector); + break; + case 4: + PrepareForOpPhiOnVectors( + dimension, + use_component_wise_4d_select_even_if_optional, + &fresh_id_for_bvec4_selector); + break; + default: + assert(false && "Invalid vector dimension."); + } + break; + } + default: + break; + } + }); + + // Some instructions will require to be enclosed inside conditionals + // because they have side effects (for example, loads and stores). Some of + // this have no result id, so we require instruction descriptors to + // identify them. Each of them is associated with the necessary ids for it + // via a SideEffectWrapperInfo message. + std::vector wrappers_info; + + for (auto instruction : instructions_that_need_ids) { + protobufs::SideEffectWrapperInfo wrapper_info; + *wrapper_info.mutable_instruction() = + MakeInstructionDescriptor(GetIRContext(), instruction); + wrapper_info.set_merge_block_id(GetFuzzerContext()->GetFreshId()); + wrapper_info.set_execute_block_id(GetFuzzerContext()->GetFreshId()); + + // If the instruction has a non-void result id, we need to define more + // fresh ids and provide an id of the suitable type whose value can be + // copied in order to create a placeholder id. + if (TransformationFlattenConditionalBranch::InstructionNeedsPlaceholder( + GetIRContext(), *instruction)) { + wrapper_info.set_actual_result_id(GetFuzzerContext()->GetFreshId()); + wrapper_info.set_alternative_block_id( + GetFuzzerContext()->GetFreshId()); + wrapper_info.set_placeholder_result_id( + GetFuzzerContext()->GetFreshId()); + + // The id will be a zero constant if the type allows it, and an + // OpUndef otherwise. We want to avoid using OpUndef, if possible, to + // avoid undefined behaviour in the module as much as possible. + if (fuzzerutil::CanCreateConstant(GetIRContext(), + instruction->type_id())) { + wrapper_info.set_value_to_copy_id( + FindOrCreateZeroConstant(instruction->type_id(), true)); + } else { + wrapper_info.set_value_to_copy_id( + FindOrCreateGlobalUndef(instruction->type_id())); + } + } + + wrappers_info.push_back(std::move(wrapper_info)); + } + + // Apply the transformation, evenly choosing whether to lay out the true + // branch or the false branch first. + ApplyTransformation(TransformationFlattenConditionalBranch( + header->id(), GetFuzzerContext()->ChooseEven(), + fresh_id_for_bvec2_selector, fresh_id_for_bvec3_selector, + fresh_id_for_bvec4_selector, wrappers_info)); + } + } +} + +void FuzzerPassFlattenConditionalBranches::PrepareForOpPhiOnVectors( + uint32_t vector_dimension, bool use_vector_select_if_optional, + uint32_t* fresh_id_for_bvec_selector) { + if (*fresh_id_for_bvec_selector != 0) { + // We already have a fresh id for a component-wise OpSelect of this + // dimension + return; + } + if (TransformationFlattenConditionalBranch::OpSelectArgumentsAreRestricted( + GetIRContext()) || + use_vector_select_if_optional) { + // We either have to, or have chosen to, perform a component-wise select, so + // we ensure that the right boolean vector type is available, and grab a + // fresh id. + FindOrCreateVectorType(FindOrCreateBoolType(), vector_dimension); + *fresh_id_for_bvec_selector = GetFuzzerContext()->GetFreshId(); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h new file mode 100644 index 0000000..76f7782 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_flatten_conditional_branches.h @@ -0,0 +1,46 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_FLATTEN_CONDITIONAL_BRANCHES_H +#define SOURCE_FUZZ_FUZZER_PASS_FLATTEN_CONDITIONAL_BRANCHES_H + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +class FuzzerPassFlattenConditionalBranches : public FuzzerPass { + public: + FuzzerPassFlattenConditionalBranches( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassFlattenConditionalBranches() override; + + void Apply() override; + + private: + // If the SPIR-V version requires vector OpSelects to be component-wise, or + // if |use_vector_select_if_optional| holds, |fresh_id_for_bvec_selector| is + // populated with a fresh id if it is currently zero, and a + // |vector_dimension|-dimensional boolean vector type is added to the module + // if not already present. + void PrepareForOpPhiOnVectors(uint32_t vector_dimension, + bool use_vector_select_if_optional, + uint32_t* fresh_id_for_bvec_selector); +}; +} // namespace fuzz +} // namespace spvtools +#endif // SOURCE_FUZZ_FUZZER_PASS_FLATTEN_CONDITIONAL_BRANCHES_H diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp new file mode 100644 index 0000000..90160d8 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_inline_functions.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_inline_function.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassInlineFunctions::FuzzerPassInlineFunctions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassInlineFunctions::~FuzzerPassInlineFunctions() = default; + +void FuzzerPassInlineFunctions::Apply() { + // |function_call_instructions| are the instructions that will be inlined. + // First, they will be collected and then do the inlining in another loop. + // This avoids changing the module while it is being inspected. + std::vector function_call_instructions; + + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfInliningFunction())) { + continue; + } + + // |instruction| must be suitable for inlining. + if (!TransformationInlineFunction::IsSuitableForInlining( + GetIRContext(), &instruction)) { + continue; + } + + function_call_instructions.push_back(&instruction); + } + } + } + + // Once the function calls have been collected, it's time to actually create + // and apply the inlining transformations. + for (auto& function_call_instruction : function_call_instructions) { + // If |function_call_instruction| is not the penultimate instruction in its + // block or its block termination instruction is not OpBranch, then try to + // split |function_call_block| such that the conditions are met. + auto* function_call_block = + GetIRContext()->get_instr_block(function_call_instruction); + if ((function_call_instruction != &*--function_call_block->tail() || + function_call_block->terminator()->opcode() != SpvOpBranch) && + !MaybeApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), + function_call_instruction->NextNode()), + GetFuzzerContext()->GetFreshId()))) { + continue; + } + + auto* called_function = fuzzerutil::FindFunction( + GetIRContext(), function_call_instruction->GetSingleWordInOperand(0)); + + // Mapping the called function instructions. + std::map result_id_map; + for (auto& called_function_block : *called_function) { + // The called function entry block label will not be inlined. + if (&called_function_block != &*called_function->entry()) { + result_id_map[called_function_block.GetLabelInst()->result_id()] = + GetFuzzerContext()->GetFreshId(); + } + + for (auto& instruction_to_inline : called_function_block) { + // The instructions are mapped to fresh ids. + if (instruction_to_inline.HasResultId()) { + result_id_map[instruction_to_inline.result_id()] = + GetFuzzerContext()->GetFreshId(); + } + } + } + + // Applies the inline function transformation. + ApplyTransformation(TransformationInlineFunction( + function_call_instruction->result_id(), result_id_map)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h new file mode 100644 index 0000000..37295d1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_inline_functions.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_INLINE_FUNCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INLINE_FUNCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Looks for OpFunctionCall instructions and randomly decides which ones to +// inline. If the instructions of the called function are going to be inlined, +// then a mapping, between their result ids and suitable ids, is done. +class FuzzerPassInlineFunctions : public FuzzerPass { + public: + FuzzerPassInlineFunctions(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassInlineFunctions() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_INLINE_FUNCTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp new file mode 100644 index 0000000..0e40b49 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fuzzer_pass_interchange_signedness_of_integer_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/transformation_record_synonymous_constants.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassInterchangeSignednessOfIntegerOperands:: + FuzzerPassInterchangeSignednessOfIntegerOperands( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassInterchangeSignednessOfIntegerOperands:: + ~FuzzerPassInterchangeSignednessOfIntegerOperands() = default; + +void FuzzerPassInterchangeSignednessOfIntegerOperands::Apply() { + // Make vector keeping track of all the uses we want to replace. + // This is a vector of pairs, where the first element is an id use descriptor + // identifying the use of a constant id and the second is the id that should + // be used to replace it. + std::vector> uses_to_replace; + + for (auto constant : GetIRContext()->GetConstants()) { + uint32_t constant_id = constant->result_id(); + + // We want to record the synonymity of an integer constant with another + // constant with opposite signedness, and this can only be done if they are + // not irrelevant. + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + constant_id)) { + continue; + } + + uint32_t toggled_id = + FindOrCreateToggledIntegerConstant(constant->result_id()); + if (!toggled_id) { + // Not an integer constant + continue; + } + + assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + toggled_id) && + "FindOrCreateToggledConstant can't produce an irrelevant id"); + + // Record synonymous constants + ApplyTransformation( + TransformationRecordSynonymousConstants(constant_id, toggled_id)); + + // Find all the uses of the constant and, for each, probabilistically + // decide whether to replace it. + GetIRContext()->get_def_use_mgr()->ForEachUse( + constant_id, + [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst, + uint32_t use_index) -> void { + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfInterchangingSignednessOfIntegerOperands())) { + MaybeAddUseToReplace(use_inst, use_index, toggled_id, + &uses_to_replace); + } + }); + } + + // Replace the ids if it is allowed. + for (auto use_to_replace : uses_to_replace) { + MaybeApplyTransformation(TransformationReplaceIdWithSynonym( + use_to_replace.first, use_to_replace.second)); + } +} + +uint32_t FuzzerPassInterchangeSignednessOfIntegerOperands:: + FindOrCreateToggledIntegerConstant(uint32_t id) { + // |id| must not be a specialization constant because we do not know the value + // of specialization constants. + if (opt::IsSpecConstantInst( + GetIRContext()->get_def_use_mgr()->GetDef(id)->opcode())) { + return 0; + } + + auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant(id); + + // This pass only toggles integer constants. + if (!constant->AsIntConstant() && + (!constant->AsVectorConstant() || + !constant->AsVectorConstant()->component_type()->AsInteger())) { + return 0; + } + + if (auto integer = constant->AsIntConstant()) { + auto type = integer->type()->AsInteger(); + + // Find or create and return the toggled constant. + return FindOrCreateIntegerConstant(std::vector(integer->words()), + type->width(), !type->IsSigned(), false); + } + + // The constant is an integer vector. + + // Find the component type. + auto component_type = + constant->AsVectorConstant()->component_type()->AsInteger(); + + // Find or create the toggled component type. + uint32_t toggled_component_type = FindOrCreateIntegerType( + component_type->width(), !component_type->IsSigned()); + + // Get the information about the toggled components. We need to extract this + // information now because the analyses might be invalidated, which would make + // the constant and component_type variables invalid. + std::vector> component_words; + + for (auto component : constant->AsVectorConstant()->GetComponents()) { + component_words.push_back(component->AsIntConstant()->words()); + } + uint32_t width = component_type->width(); + bool is_signed = !component_type->IsSigned(); + + std::vector toggled_components; + + // Find or create the toggled components. + for (auto words : component_words) { + toggled_components.push_back( + FindOrCreateIntegerConstant(words, width, is_signed, false)); + } + + // Find or create the required toggled vector type. + uint32_t toggled_type = FindOrCreateVectorType( + toggled_component_type, (uint32_t)toggled_components.size()); + + // Find or create and return the toggled vector constant. + return FindOrCreateCompositeConstant(toggled_components, toggled_type, false); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h new file mode 100644 index 0000000..06882f4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_signedness_of_integer_operands.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that: +// - Finds all the integer constant (scalar and vector) definitions in the +// module and adds the definitions of the integer with the same data words but +// opposite signedness. If the synonym is already in the module, it does not +// add a new one. +// - For each use of an integer constant where its signedness does not matter, +// decides whether to change it to the id of the toggled constant. +class FuzzerPassInterchangeSignednessOfIntegerOperands : public FuzzerPass { + public: + FuzzerPassInterchangeSignednessOfIntegerOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassInterchangeSignednessOfIntegerOperands() override; + + void Apply() override; + + private: + // Given the id of an integer constant (scalar or vector), it finds or creates + // the corresponding toggled constant (the integer with the same data words + // but opposite signedness). Returns the id of the toggled instruction if the + // constant is an integer scalar or vector, 0 otherwise. + uint32_t FindOrCreateToggledIntegerConstant(uint32_t id); +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_SIGNEDNESS_OF_INTEGER_OPERANDS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp new file mode 100644 index 0000000..20575e1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_interchange_zero_like_constants.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/transformation_record_synonymous_constants.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +namespace spvtools { +namespace fuzz { +FuzzerPassInterchangeZeroLikeConstants::FuzzerPassInterchangeZeroLikeConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassInterchangeZeroLikeConstants:: + ~FuzzerPassInterchangeZeroLikeConstants() = default; + +uint32_t FuzzerPassInterchangeZeroLikeConstants::FindOrCreateToggledConstant( + opt::Instruction* declaration) { + // |declaration| must not be a specialization constant because we do not know + // the value of specialization constants. + if (opt::IsSpecConstantInst(declaration->opcode())) { + return 0; + } + + auto constant = GetIRContext()->get_constant_mgr()->FindDeclaredConstant( + declaration->result_id()); + + // This pass only toggles zero-like constants + if (!constant->IsZero()) { + return 0; + } + + if (constant->AsScalarConstant()) { + return FindOrCreateNullConstant(declaration->type_id()); + } else if (constant->AsNullConstant()) { + // Add declaration of equivalent scalar constant + auto kind = constant->type()->kind(); + if (kind == opt::analysis::Type::kBool || + kind == opt::analysis::Type::kInteger || + kind == opt::analysis::Type::kFloat) { + return FindOrCreateZeroConstant(declaration->type_id(), false); + } + } + + return 0; +} + +void FuzzerPassInterchangeZeroLikeConstants::Apply() { + // Make vector keeping track of all the uses we want to replace. + // This is a vector of pairs, where the first element is an id use descriptor + // identifying the use of a constant id and the second is the id that should + // be used to replace it. + std::vector> uses_to_replace; + + for (auto constant : GetIRContext()->GetConstants()) { + uint32_t constant_id = constant->result_id(); + if (GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + constant_id)) { + continue; + } + + uint32_t toggled_id = FindOrCreateToggledConstant(constant); + if (!toggled_id) { + // Not a zero-like constant + continue; + } + + assert(!GetTransformationContext()->GetFactManager()->IdIsIrrelevant( + toggled_id) && + "FindOrCreateToggledConstant can't produce an irrelevant id"); + + // Record synonymous constants + ApplyTransformation( + TransformationRecordSynonymousConstants(constant_id, toggled_id)); + + // Find all the uses of the constant and, for each, probabilistically + // decide whether to replace it. + GetIRContext()->get_def_use_mgr()->ForEachUse( + constant_id, + [this, toggled_id, &uses_to_replace](opt::Instruction* use_inst, + uint32_t use_index) -> void { + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfInterchangingZeroLikeConstants())) { + MaybeAddUseToReplace(use_inst, use_index, toggled_id, + &uses_to_replace); + } + }); + } + + // Replace the ids if it is allowed. + for (auto use_to_replace : uses_to_replace) { + MaybeApplyTransformation(TransformationReplaceIdWithSynonym( + use_to_replace.first, use_to_replace.second)); + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h new file mode 100644 index 0000000..ef0f765 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_interchange_zero_like_constants.h @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A pass that: +// - Finds all the zero-like constant definitions in the module and adds the +// definitions of the corresponding synonym, recording the fact that they +// are synonymous. If the synonym is already in the module, it does not +// add a new one. +// - For each use of a zero-like constant, decides whether to change it to the +// id of the toggled constant. +class FuzzerPassInterchangeZeroLikeConstants : public FuzzerPass { + public: + FuzzerPassInterchangeZeroLikeConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassInterchangeZeroLikeConstants() override; + + void Apply() override; + + private: + // Given the declaration of a zero-like constant, it finds or creates the + // corresponding toggled constant (a scalar constant of value 0 becomes a + // null constant of the same type and vice versa). + // Returns the id of the toggled instruction if the constant is zero-like, + // 0 otherwise. + uint32_t FindOrCreateToggledConstant(opt::Instruction* declaration); +}; + +} // namespace fuzz +} // namespace spvtools +#endif // SOURCE_FUZZ_FUZZER_PASS_INTERCHANGE_ZERO_LIKE_CONSTANTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp new file mode 100644 index 0000000..de4ff1d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_invert_comparison_operator.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassInvertComparisonOperators::FuzzerPassInvertComparisonOperators( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassInvertComparisonOperators::~FuzzerPassInvertComparisonOperators() = + default; + +void FuzzerPassInvertComparisonOperators::Apply() { + GetIRContext()->module()->ForEachInst([this](const opt::Instruction* inst) { + if (!TransformationInvertComparisonOperator::IsInversionSupported( + inst->opcode())) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfInvertingComparisonOperators())) { + return; + } + + ApplyTransformation(TransformationInvertComparisonOperator( + inst->result_id(), GetFuzzerContext()->GetFreshId())); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h new file mode 100644 index 0000000..9c80bbb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_invert_comparison_operators.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over different comparison operators in the module (>=, <, > etc.) +// and randomly decides whether to invert each one or not. +class FuzzerPassInvertComparisonOperators : public FuzzerPass { + public: + FuzzerPassInvertComparisonOperators( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassInvertComparisonOperators() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_INVERT_COMPARISON_OPERATORS_H_ \ No newline at end of file diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp new file mode 100644 index 0000000..f4f2a80 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_make_vector_operation_dynamic.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMakeVectorOperationsDynamic::FuzzerPassMakeVectorOperationsDynamic( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassMakeVectorOperationsDynamic:: + ~FuzzerPassMakeVectorOperationsDynamic() = default; + +void FuzzerPassMakeVectorOperationsDynamic::Apply() { + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + // Randomly decide whether to try applying the transformation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfMakingVectorOperationDynamic())) { + continue; + } + + // |instruction| must be a vector operation. + if (!TransformationMakeVectorOperationDynamic::IsVectorOperation( + GetIRContext(), &instruction)) { + continue; + } + + // Make sure |instruction| has only one indexing operand. + assert(instruction.NumInOperands() == + (instruction.opcode() == SpvOpCompositeExtract ? 2 : 3) && + "FuzzerPassMakeVectorOperationsDynamic: the composite " + "instruction must have " + "only one indexing operand."); + + // Applies the make vector operation dynamic transformation. + ApplyTransformation(TransformationMakeVectorOperationDynamic( + instruction.result_id(), + FindOrCreateIntegerConstant( + {instruction.GetSingleWordInOperand( + instruction.opcode() == SpvOpCompositeExtract ? 1 : 2)}, + 32, GetFuzzerContext()->ChooseEven(), false))); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h new file mode 100644 index 0000000..dd51cde --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Looks for OpCompositeExtract/Insert instructions on vectors, and replaces +// them with OpVectorExtract/InsertDynamic. +class FuzzerPassMakeVectorOperationsDynamic : public FuzzerPass { + public: + FuzzerPassMakeVectorOperationsDynamic( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassMakeVectorOperationsDynamic() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MAKE_VECTOR_OPERATIONS_DYNAMIC_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp new file mode 100644 index 0000000..e66fc44 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_merge_blocks.h" + +#include + +#include "source/fuzz/transformation_merge_blocks.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMergeBlocks::FuzzerPassMergeBlocks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassMergeBlocks::~FuzzerPassMergeBlocks() = default; + +void FuzzerPassMergeBlocks::Apply() { + // First we populate a sequence of transformations that we might consider + // applying. + std::vector potential_transformations; + // We do this by considering every block of every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // We probabilistically decide to ignore some blocks. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMergingBlocks())) { + continue; + } + // For other blocks, we add a transformation to merge the block into its + // predecessor if that transformation would be applicable. + TransformationMergeBlocks transformation(block.id()); + if (transformation.IsApplicable(GetIRContext(), + *GetTransformationContext())) { + potential_transformations.push_back(transformation); + } + } + } + + while (!potential_transformations.empty()) { + auto transformation = + GetFuzzerContext()->RemoveAtRandomIndex(&potential_transformations); + MaybeApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h new file mode 100644 index 0000000..1a6c2c2 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_blocks.h @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for merging blocks in the module. +class FuzzerPassMergeBlocks : public FuzzerPass { + public: + FuzzerPassMergeBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassMergeBlocks(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MERGE_BLOCKS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp new file mode 100644 index 0000000..fc9c74d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.cpp @@ -0,0 +1,337 @@ +// Copyright (c) 2020 Stefano Milizia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_merge_function_returns.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_early_terminator_wrapper.h" +#include "source/fuzz/transformation_merge_function_returns.h" +#include "source/fuzz/transformation_wrap_early_terminator_in_function.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMergeFunctionReturns::FuzzerPassMergeFunctionReturns( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassMergeFunctionReturns::~FuzzerPassMergeFunctionReturns() = default; + +void FuzzerPassMergeFunctionReturns::Apply() { + // The pass might add new functions to the module (due to wrapping early + // terminator instructions in function calls), so we record the functions that + // are currently present and then iterate over them. + std::vector functions; + for (auto& function : *GetIRContext()->module()) { + functions.emplace_back(&function); + } + + for (auto* function : functions) { + // Randomly decide whether to consider this function. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMergingFunctionReturns())) { + continue; + } + + // We skip wrappers for early terminators, since this fuzzer pass introduces + // such wrappers to eliminate early terminators. + if (IsEarlyTerminatorWrapper(*function)) { + continue; + } + + // Only consider functions that have early returns. + if (!function->HasEarlyReturn()) { + continue; + } + + // Wrap early terminators in function calls. + ForEachInstructionWithInstructionDescriptor( + function, + [this, function]( + opt::BasicBlock* /*unused*/, opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + const SpvOp opcode = inst_it->opcode(); + switch (opcode) { + case SpvOpKill: + case SpvOpUnreachable: + case SpvOpTerminateInvocation: { + // This is an early termination instruction - we need to wrap it + // so that it becomes a return. + if (TransformationWrapEarlyTerminatorInFunction:: + MaybeGetWrapperFunction(GetIRContext(), opcode) == + nullptr) { + // We don't have a suitable wrapper function, so create one. + ApplyTransformation(TransformationAddEarlyTerminatorWrapper( + GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), opcode)); + } + // If the function has non-void return type then we need a + // suitable value to use in an OpReturnValue instruction. + opt::Instruction* function_return_type = + GetIRContext()->get_def_use_mgr()->GetDef( + function->type_id()); + uint32_t returned_value_id; + if (function_return_type->opcode() == SpvOpTypeVoid) { + // No value is needed. + returned_value_id = 0; + } else if (fuzzerutil::CanCreateConstant( + GetIRContext(), + function_return_type->result_id())) { + // We favour returning an irrelevant zero. + returned_value_id = FindOrCreateZeroConstant( + function_return_type->result_id(), true); + } else { + // It's not possible to use an irrelevant zero, so we use an + // OpUndef instead. + returned_value_id = + FindOrCreateGlobalUndef(function_return_type->result_id()); + } + // Wrap the early termination instruction in a function call. + ApplyTransformation(TransformationWrapEarlyTerminatorInFunction( + GetFuzzerContext()->GetFreshId(), instruction_descriptor, + returned_value_id)); + break; + } + default: + break; + } + }); + + // Get the return blocks. + auto return_blocks = fuzzerutil::GetReachableReturnBlocks( + GetIRContext(), function->result_id()); + + // Only go ahead if there is more than one reachable return block. + if (return_blocks.size() <= 1) { + continue; + } + + // Make sure that OpConstantTrue and OpConstantFalse are in the module. + FindOrCreateBoolConstant(true, false); + FindOrCreateBoolConstant(false, false); + + // Collect the ids available after the entry block of the function. + auto ids_available_after_entry_block = + GetTypesToIdsAvailableAfterEntryBlock(function); + + // If the entry block does not branch unconditionally to another block, + // split it. + if (function->entry()->terminator()->opcode() != SpvOpBranch) { + SplitBlockAfterOpPhiOrOpVariable(function->entry()->id()); + } + + // Collect the merge blocks of the function whose corresponding loops + // contain return blocks. + auto merge_blocks = GetMergeBlocksOfLoopsContainingBlocks(return_blocks); + + // Split the merge blocks, if they contain instructions different from + // OpLabel, OpPhi and OpBranch. Collect the new ids of merge blocks. + std::vector actual_merge_blocks; + for (uint32_t merge_block : merge_blocks) { + opt::BasicBlock* block = GetIRContext()->get_instr_block(merge_block); + + // We don't need to split blocks that are already suitable (they only + // contain OpLabel, OpPhi or OpBranch instructions). + if (GetIRContext() + ->get_instr_block(merge_block) + ->WhileEachInst([](opt::Instruction* inst) { + return inst->opcode() == SpvOpLabel || + inst->opcode() == SpvOpPhi || + inst->opcode() == SpvOpBranch; + })) { + actual_merge_blocks.emplace_back(merge_block); + continue; + } + + // If the merge block is also a loop header, we need to add a preheader, + // which will be the new merge block. + if (block->IsLoopHeader()) { + actual_merge_blocks.emplace_back( + GetOrCreateSimpleLoopPreheader(merge_block)->id()); + continue; + } + + // If the merge block is not a loop header, we must split it after the + // last OpPhi instruction. The merge block will be the first of the pair + // of blocks obtained after splitting, and it keeps the original id. + SplitBlockAfterOpPhiOrOpVariable(merge_block); + actual_merge_blocks.emplace_back(merge_block); + } + + // Get the ids needed by the transformation. + uint32_t outer_header_id = GetFuzzerContext()->GetFreshId(); + uint32_t outer_return_id = GetFuzzerContext()->GetFreshId(); + + bool function_is_void = + GetIRContext()->get_type_mgr()->GetType(function->type_id())->AsVoid(); + + // We only need a return value if the function is not void. + uint32_t return_val_id = + function_is_void ? 0 : GetFuzzerContext()->GetFreshId(); + + // We only need a placeholder for the return value if the function is not + // void and there is at least one relevant merge block. + uint32_t returnable_val_id = 0; + if (!function_is_void && !actual_merge_blocks.empty()) { + // If there is an id of the suitable type, choose one at random. + if (ids_available_after_entry_block.count(function->type_id())) { + const auto& candidates = + ids_available_after_entry_block[function->type_id()]; + returnable_val_id = + candidates[GetFuzzerContext()->RandomIndex(candidates)]; + } else { + // If there is no id, add a global OpUndef. + uint32_t suitable_id = FindOrCreateGlobalUndef(function->type_id()); + // Add the new id to the map of available ids. + ids_available_after_entry_block.emplace( + function->type_id(), std::vector({suitable_id})); + returnable_val_id = suitable_id; + } + } + + // Collect all the ids needed for merge blocks. + auto merge_blocks_info = GetInfoNeededForMergeBlocks( + actual_merge_blocks, &ids_available_after_entry_block); + + // Apply the transformation if it is applicable (it could be inapplicable if + // adding new predecessors to merge blocks breaks dominance rules). + MaybeApplyTransformation(TransformationMergeFunctionReturns( + function->result_id(), outer_header_id, outer_return_id, return_val_id, + returnable_val_id, merge_blocks_info)); + } +} + +std::map> +FuzzerPassMergeFunctionReturns::GetTypesToIdsAvailableAfterEntryBlock( + opt::Function* function) const { + std::map> result; + // Consider all global declarations + for (auto& global : GetIRContext()->module()->types_values()) { + if (global.HasResultId() && global.type_id()) { + if (!result.count(global.type_id())) { + result.emplace(global.type_id(), std::vector()); + } + result[global.type_id()].emplace_back(global.result_id()); + } + } + + // Consider all function parameters + function->ForEachParam([&result](opt::Instruction* param) { + if (param->HasResultId() && param->type_id()) { + if (!result.count(param->type_id())) { + result.emplace(param->type_id(), std::vector()); + } + + result[param->type_id()].emplace_back(param->result_id()); + } + }); + + // Consider all the instructions in the entry block. + for (auto& inst : *function->entry()) { + if (inst.HasResultId() && inst.type_id()) { + if (!result.count(inst.type_id())) { + result.emplace(inst.type_id(), std::vector()); + } + result[inst.type_id()].emplace_back(inst.result_id()); + } + } + + return result; +} + +std::set +FuzzerPassMergeFunctionReturns::GetMergeBlocksOfLoopsContainingBlocks( + const std::set& blocks) const { + std::set result; + for (uint32_t block : blocks) { + uint32_t merge_block = + GetIRContext()->GetStructuredCFGAnalysis()->LoopMergeBlock(block); + + while (merge_block != 0 && !result.count(merge_block)) { + // Add a new entry. + result.emplace(merge_block); + + // Walk up the loop tree. + block = merge_block; + merge_block = GetIRContext()->GetStructuredCFGAnalysis()->LoopMergeBlock( + merge_block); + } + } + + return result; +} + +std::vector +FuzzerPassMergeFunctionReturns::GetInfoNeededForMergeBlocks( + const std::vector& merge_blocks, + std::map>* + ids_available_after_entry_block) { + std::vector result; + for (uint32_t merge_block : merge_blocks) { + protobufs::ReturnMergingInfo info; + info.set_merge_block_id(merge_block); + info.set_is_returning_id(this->GetFuzzerContext()->GetFreshId()); + info.set_maybe_return_val_id(this->GetFuzzerContext()->GetFreshId()); + + // Add all the ids needed for the OpPhi instructions. + this->GetIRContext() + ->get_instr_block(merge_block) + ->ForEachPhiInst([this, &info, &ids_available_after_entry_block]( + opt::Instruction* phi_inst) { + protobufs::UInt32Pair entry; + entry.set_first(phi_inst->result_id()); + + // If there is an id of the suitable type, choose one at random. + if (ids_available_after_entry_block->count(phi_inst->type_id())) { + auto& candidates = + ids_available_after_entry_block->at(phi_inst->type_id()); + entry.set_second( + candidates[this->GetFuzzerContext()->RandomIndex(candidates)]); + } else { + // If there is no id, add a global OpUndef. + uint32_t suitable_id = + this->FindOrCreateGlobalUndef(phi_inst->type_id()); + // Add the new id to the map of available ids. + ids_available_after_entry_block->emplace( + phi_inst->type_id(), std::vector({suitable_id})); + entry.set_second(suitable_id); + } + + // Add the entry to the list. + *info.add_opphi_to_suitable_id() = entry; + }); + + result.emplace_back(info); + } + + return result; +} + +bool FuzzerPassMergeFunctionReturns::IsEarlyTerminatorWrapper( + const opt::Function& function) const { + for (SpvOp opcode : {SpvOpKill, SpvOpUnreachable, SpvOpTerminateInvocation}) { + if (TransformationWrapEarlyTerminatorInFunction::MaybeGetWrapperFunction( + GetIRContext(), opcode) == &function) { + return true; + } + } + return false; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h new file mode 100644 index 0000000..3b5a668 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_merge_function_returns.h @@ -0,0 +1,69 @@ +// Copyright (c) 2020 Stefano Milizia +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MERGE_FUNCTION_RETURNS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MERGE_FUNCTION_RETURNS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for changing functions in the module so that they don't have an +// early return. When handling a function the pass first eliminates early +// terminator instructions, such as OpKill, by wrapping them in functions and +// replacing them with a function call followed by a return. The return +// instructions that arise are then modified so that the function does not have +// early returns. +class FuzzerPassMergeFunctionReturns : public FuzzerPass { + public: + FuzzerPassMergeFunctionReturns( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassMergeFunctionReturns(); + + void Apply() override; + + private: + // Returns a map from type ids to a list of ids with that type and which are + // available at the end of the entry block of |function|. + std::map> + GetTypesToIdsAvailableAfterEntryBlock(opt::Function* function) const; + + // Returns the set of all the loop merge blocks whose corresponding loops + // contain at least one of the blocks in |blocks|. + std::set GetMergeBlocksOfLoopsContainingBlocks( + const std::set& blocks) const; + + // Returns a list of ReturnMergingInfo messages, containing the information + // needed by the transformation for each of the relevant merge blocks. + // If a new id is created (because |ids_available_after_entry_block| does not + // have an entry for the corresponding type), a new entry is added to + // |ids_available_after_entry_block|, mapping its type to a singleton set + // containing it. + std::vector GetInfoNeededForMergeBlocks( + const std::vector& merge_blocks, + std::map>* + ids_available_after_entry_block); + + // Returns true if and only if |function| is a wrapper for an early terminator + // instruction such as OpKill. + bool IsEarlyTerminatorWrapper(const opt::Function& function) const; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MERGE_FUNCTION_RETURNS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp new file mode 100644 index 0000000..89f5f5c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_mutate_pointers.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_mutate_pointer.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassMutatePointers::FuzzerPassMutatePointers( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassMutatePointers::~FuzzerPassMutatePointers() = default; + +void FuzzerPassMutatePointers::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMutatingPointer())) { + return; + } + + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, inst_it)) { + return; + } + + auto available_pointers = FindAvailableInstructions( + function, block, inst_it, + [](opt::IRContext* ir_context, opt::Instruction* inst) { + return TransformationMutatePointer::IsValidPointerInstruction( + ir_context, *inst); + }); + + if (available_pointers.empty()) { + return; + } + + const auto* pointer_inst = + available_pointers[GetFuzzerContext()->RandomIndex( + available_pointers)]; + + // Make sure there is an irrelevant constant in the module. + FindOrCreateZeroConstant(fuzzerutil::GetPointeeTypeIdFromPointerType( + GetIRContext(), pointer_inst->type_id()), + true); + + ApplyTransformation(TransformationMutatePointer( + pointer_inst->result_id(), GetFuzzerContext()->GetFreshId(), + instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h new file mode 100644 index 0000000..f77523e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_mutate_pointers.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_MUTATE_POINTERS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_MUTATE_POINTERS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly mutates the value of each pointer instruction in the module. +class FuzzerPassMutatePointers : public FuzzerPass { + public: + FuzzerPassMutatePointers(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassMutatePointers() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_MUTATE_POINTERS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp new file mode 100644 index 0000000..d87662e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.cpp @@ -0,0 +1,523 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_obfuscate_constants.h" + +#include +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassObfuscateConstants::FuzzerPassObfuscateConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassObfuscateConstants::~FuzzerPassObfuscateConstants() = default; + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + const std::vector& greater_than_opcodes, + const std::vector& less_than_opcodes, uint32_t constant_id_1, + uint32_t constant_id_2, bool first_constant_is_larger) { + auto bool_constant_opcode = GetIRContext() + ->get_def_use_mgr() + ->GetDef(bool_constant_use.id_of_interest()) + ->opcode(); + assert((bool_constant_opcode == SpvOpConstantFalse || + bool_constant_opcode == SpvOpConstantTrue) && + "Precondition: this must be a usage of a boolean constant."); + + // Pick an opcode at random. First randomly decide whether to generate + // a 'greater than' or 'less than' kind of opcode, and then select a + // random opcode from the resulting subset. + SpvOp comparison_opcode; + if (GetFuzzerContext()->ChooseEven()) { + comparison_opcode = greater_than_opcodes[GetFuzzerContext()->RandomIndex( + greater_than_opcodes)]; + } else { + comparison_opcode = + less_than_opcodes[GetFuzzerContext()->RandomIndex(less_than_opcodes)]; + } + + // We now need to decide how to order constant_id_1 and constant_id_2 such + // that 'constant_id_1 comparison_opcode constant_id_2' evaluates to the + // boolean constant. + const bool is_greater_than_opcode = + std::find(greater_than_opcodes.begin(), greater_than_opcodes.end(), + comparison_opcode) != greater_than_opcodes.end(); + uint32_t lhs_id; + uint32_t rhs_id; + if ((bool_constant_opcode == SpvOpConstantTrue && + first_constant_is_larger == is_greater_than_opcode) || + (bool_constant_opcode == SpvOpConstantFalse && + first_constant_is_larger != is_greater_than_opcode)) { + lhs_id = constant_id_1; + rhs_id = constant_id_2; + } else { + lhs_id = constant_id_2; + rhs_id = constant_id_1; + } + + // We can now make a transformation that will replace |bool_constant_use| + // with an expression of the form (written using infix notation): + // |lhs_id| |comparison_opcode| |rhs_id| + auto transformation = TransformationReplaceBooleanConstantWithConstantBinary( + bool_constant_use, lhs_id, rhs_id, comparison_opcode, + GetFuzzerContext()->GetFreshId()); + // The transformation should be applicable by construction. + assert( + transformation.IsApplicable(GetIRContext(), *GetTransformationContext())); + + // Applying this transformation yields a pointer to the new instruction that + // computes the result of the binary expression. + auto binary_operator_instruction = transformation.ApplyWithResult( + GetIRContext(), GetTransformationContext()); + + // Add this transformation to the sequence of transformations that have been + // applied. + *GetTransformations()->add_transformation() = transformation.ToMessage(); + + // Having made a binary expression, there may now be opportunities to further + // obfuscate the constants used as the LHS and RHS of the expression (e.g. by + // replacing them with loads from known uniforms). + // + // We thus consider operands 0 and 1 (LHS and RHS in turn). + for (uint32_t index : {0u, 1u}) { + // We randomly decide, based on the current depth of obfuscation, whether + // to further obfuscate this operand. + if (GetFuzzerContext()->GoDeeperInConstantObfuscation(depth)) { + auto in_operand_use = MakeIdUseDescriptor( + binary_operator_instruction->GetSingleWordInOperand(index), + MakeInstructionDescriptor(binary_operator_instruction->result_id(), + binary_operator_instruction->opcode(), 0), + index); + ObfuscateConstant(depth + 1, in_operand_use); + } + } +} + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstantViaFloatConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t float_constant_id_1, uint32_t float_constant_id_2) { + auto float_constant_1 = GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(float_constant_id_1) + ->AsFloatConstant(); + auto float_constant_2 = GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(float_constant_id_2) + ->AsFloatConstant(); + assert(float_constant_1->words() != float_constant_2->words() && + "The constants should not be identical."); + assert(std::isfinite(float_constant_1->GetValueAsDouble()) && + "The constants must be finite numbers."); + assert(std::isfinite(float_constant_2->GetValueAsDouble()) && + "The constants must be finite numbers."); + bool first_constant_is_larger; + assert(float_constant_1->type()->AsFloat()->width() == + float_constant_2->type()->AsFloat()->width() && + "First and second floating-point constants must have the same width."); + if (float_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + float_constant_1->GetFloat() > float_constant_2->GetFloat(); + } else { + assert(float_constant_1->type()->AsFloat()->width() == 64 && + "Supported floating-point widths are 32 and 64."); + first_constant_is_larger = + float_constant_1->GetDouble() > float_constant_2->GetDouble(); + } + std::vector greater_than_opcodes{ + SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan, + SpvOpFUnordGreaterThanEqual}; + std::vector less_than_opcodes{ + SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan, + SpvOpFUnordGreaterThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + float_constant_id_1, float_constant_id_2, first_constant_is_larger); +} + +void FuzzerPassObfuscateConstants:: + ObfuscateBoolConstantViaSignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2) { + auto signed_int_constant_1 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(signed_int_constant_id_1) + ->AsIntConstant(); + auto signed_int_constant_2 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(signed_int_constant_id_2) + ->AsIntConstant(); + assert(signed_int_constant_1->words() != signed_int_constant_2->words() && + "The constants should not be identical."); + bool first_constant_is_larger; + assert(signed_int_constant_1->type()->AsInteger()->width() == + signed_int_constant_2->type()->AsInteger()->width() && + "First and second floating-point constants must have the same width."); + assert(signed_int_constant_1->type()->AsInteger()->IsSigned()); + assert(signed_int_constant_2->type()->AsInteger()->IsSigned()); + if (signed_int_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + signed_int_constant_1->GetS32() > signed_int_constant_2->GetS32(); + } else { + assert(signed_int_constant_1->type()->AsFloat()->width() == 64 && + "Supported integer widths are 32 and 64."); + first_constant_is_larger = + signed_int_constant_1->GetS64() > signed_int_constant_2->GetS64(); + } + std::vector greater_than_opcodes{SpvOpSGreaterThan, + SpvOpSGreaterThanEqual}; + std::vector less_than_opcodes{SpvOpSLessThan, SpvOpSLessThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + signed_int_constant_id_1, signed_int_constant_id_2, + first_constant_is_larger); +} + +void FuzzerPassObfuscateConstants:: + ObfuscateBoolConstantViaUnsignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t unsigned_int_constant_id_1, + uint32_t unsigned_int_constant_id_2) { + auto unsigned_int_constant_1 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(unsigned_int_constant_id_1) + ->AsIntConstant(); + auto unsigned_int_constant_2 = + GetIRContext() + ->get_constant_mgr() + ->FindDeclaredConstant(unsigned_int_constant_id_2) + ->AsIntConstant(); + assert(unsigned_int_constant_1->words() != unsigned_int_constant_2->words() && + "The constants should not be identical."); + bool first_constant_is_larger; + assert(unsigned_int_constant_1->type()->AsInteger()->width() == + unsigned_int_constant_2->type()->AsInteger()->width() && + "First and second floating-point constants must have the same width."); + assert(!unsigned_int_constant_1->type()->AsInteger()->IsSigned()); + assert(!unsigned_int_constant_2->type()->AsInteger()->IsSigned()); + if (unsigned_int_constant_1->type()->AsFloat()->width() == 32) { + first_constant_is_larger = + unsigned_int_constant_1->GetU32() > unsigned_int_constant_2->GetU32(); + } else { + assert(unsigned_int_constant_1->type()->AsFloat()->width() == 64 && + "Supported integer widths are 32 and 64."); + first_constant_is_larger = + unsigned_int_constant_1->GetU64() > unsigned_int_constant_2->GetU64(); + } + std::vector greater_than_opcodes{SpvOpUGreaterThan, + SpvOpUGreaterThanEqual}; + std::vector less_than_opcodes{SpvOpULessThan, SpvOpULessThanEqual}; + + ObfuscateBoolConstantViaConstantPair( + depth, bool_constant_use, greater_than_opcodes, less_than_opcodes, + unsigned_int_constant_id_1, unsigned_int_constant_id_2, + first_constant_is_larger); +} + +std::vector> +FuzzerPassObfuscateConstants::GetConstantWordsFromUniformsForType( + uint32_t type_id) { + assert(type_id && "Type id can't be 0"); + std::vector> result; + + for (const auto& facts_and_types : GetTransformationContext() + ->GetFactManager() + ->GetConstantUniformFactsAndTypes()) { + if (facts_and_types.second != type_id) { + continue; + } + + std::vector words(facts_and_types.first.constant_word().begin(), + facts_and_types.first.constant_word().end()); + if (std::find(result.begin(), result.end(), words) == result.end()) { + result.push_back(std::move(words)); + } + } + + return result; +} + +void FuzzerPassObfuscateConstants::ObfuscateBoolConstant( + uint32_t depth, const protobufs::IdUseDescriptor& constant_use) { + // We want to replace the boolean constant use with a binary expression over + // scalar constants, but only if we can then potentially replace the constants + // with uniforms of the same value. + + auto available_types_with_uniforms = + GetTransformationContext() + ->GetFactManager() + ->GetTypesForWhichUniformValuesAreKnown(); + if (available_types_with_uniforms.empty()) { + // Do not try to obfuscate if we do not have access to any uniform + // elements with known values. + return; + } + auto chosen_type_id = + available_types_with_uniforms[GetFuzzerContext()->RandomIndex( + available_types_with_uniforms)]; + auto available_constant_words = + GetConstantWordsFromUniformsForType(chosen_type_id); + if (available_constant_words.size() == 1) { + // TODO(afd): for now we only obfuscate a boolean if there are at least + // two constants available from uniforms, so that we can do a + // comparison between them. It would be good to be able to do the + // obfuscation even if there is only one such constant, if there is + // also another regular constant available. + return; + } + + assert(!available_constant_words.empty() && + "There exists a fact but no constants - impossible"); + + // We know we have at least two known-to-be-constant uniforms of the chosen + // type. Pick one of them at random. + auto constant_index_1 = + GetFuzzerContext()->RandomIndex(available_constant_words); + uint32_t constant_index_2; + + // Now choose another one distinct from the first one. + do { + constant_index_2 = + GetFuzzerContext()->RandomIndex(available_constant_words); + } while (constant_index_1 == constant_index_2); + + auto constant_id_1 = FindOrCreateConstant( + available_constant_words[constant_index_1], chosen_type_id, false); + auto constant_id_2 = FindOrCreateConstant( + available_constant_words[constant_index_2], chosen_type_id, false); + + assert(constant_id_1 != 0 && constant_id_2 != 0 && + "We should not find an available constant with an id of 0."); + + // Now perform the obfuscation, according to whether the type of the constants + // is float, signed int, or unsigned int. + auto chosen_type = GetIRContext()->get_type_mgr()->GetType(chosen_type_id); + if (chosen_type->AsFloat()) { + ObfuscateBoolConstantViaFloatConstantPair(depth, constant_use, + constant_id_1, constant_id_2); + } else { + assert(chosen_type->AsInteger() && + "We should only have uniform facts about ints and floats."); + if (chosen_type->AsInteger()->IsSigned()) { + ObfuscateBoolConstantViaSignedIntConstantPair( + depth, constant_use, constant_id_1, constant_id_2); + } else { + ObfuscateBoolConstantViaUnsignedIntConstantPair( + depth, constant_use, constant_id_1, constant_id_2); + } + } +} + +void FuzzerPassObfuscateConstants::ObfuscateScalarConstant( + uint32_t /*depth*/, const protobufs::IdUseDescriptor& constant_use) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2670): consider + // additional ways to obfuscate scalar constants. + + // Check whether we know that any uniforms are guaranteed to be equal to the + // scalar constant associated with |constant_use|. + auto uniform_descriptors = + GetTransformationContext() + ->GetFactManager() + ->GetUniformDescriptorsForConstant(constant_use.id_of_interest()); + if (uniform_descriptors.empty()) { + // No relevant uniforms, so do not obfuscate. + return; + } + + // Choose a random available uniform known to be equal to the constant. + const auto& uniform_descriptor = + uniform_descriptors[GetFuzzerContext()->RandomIndex(uniform_descriptors)]; + + // Make sure the module has OpConstant instructions for each index used to + // access a uniform. + for (auto index : uniform_descriptor.index()) { + FindOrCreateIntegerConstant({index}, 32, true, false); + } + + // Make sure the module has OpTypePointer that points to the element type of + // the uniform. + const auto* uniform_variable_instr = + FindUniformVariable(uniform_descriptor, GetIRContext(), true); + assert(uniform_variable_instr && + "Uniform variable does not exist or not unique."); + + const auto* uniform_variable_type_intr = + GetIRContext()->get_def_use_mgr()->GetDef( + uniform_variable_instr->type_id()); + assert(uniform_variable_type_intr && "Uniform variable has invalid type"); + + auto element_type_id = fuzzerutil::WalkCompositeTypeIndices( + GetIRContext(), uniform_variable_type_intr->GetSingleWordInOperand(1), + uniform_descriptor.index()); + assert(element_type_id && "Type of uniform variable is invalid"); + + FindOrCreatePointerType(element_type_id, SpvStorageClassUniform); + + // Create, apply and record a transformation to replace the constant use with + // the result of a load from the chosen uniform. + ApplyTransformation(TransformationReplaceConstantWithUniform( + constant_use, uniform_descriptor, GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId())); +} + +void FuzzerPassObfuscateConstants::ObfuscateConstant( + uint32_t depth, const protobufs::IdUseDescriptor& constant_use) { + switch (GetIRContext() + ->get_def_use_mgr() + ->GetDef(constant_use.id_of_interest()) + ->opcode()) { + case SpvOpConstantTrue: + case SpvOpConstantFalse: + ObfuscateBoolConstant(depth, constant_use); + break; + case SpvOpConstant: + ObfuscateScalarConstant(depth, constant_use); + break; + default: + assert(false && "The opcode should be one of the above."); + break; + } +} + +void FuzzerPassObfuscateConstants::MaybeAddConstantIdUse( + const opt::Instruction& inst, uint32_t in_operand_index, + uint32_t base_instruction_result_id, + const std::map& skipped_opcode_count, + std::vector* constant_uses) { + if (inst.GetInOperand(in_operand_index).type != SPV_OPERAND_TYPE_ID) { + // The operand is not an id, so it cannot be a constant id. + return; + } + auto operand_id = inst.GetSingleWordInOperand(in_operand_index); + auto operand_definition = + GetIRContext()->get_def_use_mgr()->GetDef(operand_id); + switch (operand_definition->opcode()) { + case SpvOpConstantFalse: + case SpvOpConstantTrue: + case SpvOpConstant: { + // The operand is a constant id, so make an id use descriptor and record + // it. + protobufs::IdUseDescriptor id_use_descriptor; + id_use_descriptor.set_id_of_interest(operand_id); + id_use_descriptor.mutable_enclosing_instruction() + ->set_target_instruction_opcode(inst.opcode()); + id_use_descriptor.mutable_enclosing_instruction() + ->set_base_instruction_result_id(base_instruction_result_id); + id_use_descriptor.mutable_enclosing_instruction() + ->set_num_opcodes_to_ignore( + skipped_opcode_count.find(inst.opcode()) == + skipped_opcode_count.end() + ? 0 + : skipped_opcode_count.at(inst.opcode())); + id_use_descriptor.set_in_operand_index(in_operand_index); + constant_uses->push_back(id_use_descriptor); + } break; + default: + break; + } +} + +void FuzzerPassObfuscateConstants::Apply() { + // First, gather up all the constant uses available in the module, by going + // through each block in each function. + std::vector constant_uses; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // For each constant use we encounter we are going to make an id use + // descriptor. An id use is described with respect to a base instruction; + // if there are instructions at the start of the block without result ids, + // the base instruction will have to be the block's label. + uint32_t base_instruction_result_id = block.id(); + + // An id use descriptor also records how many instructions of a particular + // opcode need to be skipped in order to find the instruction of interest + // from the base instruction. We maintain a mapping that records a skip + // count for each relevant opcode. + std::map skipped_opcode_count; + + // Go through each instruction in the block. + for (auto& inst : block) { + if (inst.HasResultId()) { + // The instruction has a result id, so can be used as the base + // instruction from now on, until another instruction with a result id + // is encountered. + base_instruction_result_id = inst.result_id(); + // Opcode skip counts were with respect to the previous base + // instruction and are now irrelevant. + skipped_opcode_count.clear(); + } + + // The instruction must not be an OpVariable, the only id that an + // OpVariable uses is an initializer id, which has to remain + // constant. + if (inst.opcode() != SpvOpVariable) { + // Consider each operand of the instruction, and add a constant id + // use for the operand if relevant. + for (uint32_t in_operand_index = 0; + in_operand_index < inst.NumInOperands(); in_operand_index++) { + MaybeAddConstantIdUse(inst, in_operand_index, + base_instruction_result_id, + skipped_opcode_count, &constant_uses); + } + } + + if (!inst.HasResultId()) { + // The instruction has no result id, so in order to identify future id + // uses for instructions with this opcode from the existing base + // instruction, we need to increase the skip count for this opcode. + skipped_opcode_count[inst.opcode()] = + skipped_opcode_count.find(inst.opcode()) == + skipped_opcode_count.end() + ? 1 + : skipped_opcode_count[inst.opcode()] + 1; + } + } + } + } + + // Go through the constant uses in a random order by repeatedly pulling out a + // constant use at a random index. + while (!constant_uses.empty()) { + auto index = GetFuzzerContext()->RandomIndex(constant_uses); + auto constant_use = std::move(constant_uses[index]); + constant_uses.erase(constant_uses.begin() + index); + // Decide probabilistically whether to skip or obfuscate this constant use. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfObfuscatingConstant())) { + continue; + } + ObfuscateConstant(0, constant_use); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h new file mode 100644 index 0000000..d48b37f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_obfuscate_constants.h @@ -0,0 +1,112 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for turning uses of constants into more complex forms. +// Examples include replacing 'true' with '42 < 52', and replacing '42' with +// 'a.b.c' if 'a.b.c' is known to hold the value '42'. +class FuzzerPassObfuscateConstants : public FuzzerPass { + public: + FuzzerPassObfuscateConstants( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassObfuscateConstants() override; + + void Apply() override; + + private: + // Applies 0 or more transformations to potentially obfuscate the constant + // use represented by |constant_use|. The |depth| parameter controls how + // deeply obfuscation can recurse. + void ObfuscateConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // This method will try to turn |constant_use|, required to be a use of a + // boolean constant, into a binary expression on scalar constants, which may + // themselves be recursively obfuscated. + void ObfuscateBoolConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // This method will try to turn |constant_use|, required to be a use of a + // scalar constant, into the value loaded from a uniform known to have the + // same value as the constant (if one exists). + void ObfuscateScalarConstant(uint32_t depth, + const protobufs::IdUseDescriptor& constant_use); + + // Applies a transformation to replace the boolean constant usage represented + // by |bool_constant_use| with a binary expression involving + // |float_constant_id_1| and |float_constant_id_2|, which must not be equal + // to one another. Possibly further obfuscates the uses of these float + // constants. The |depth| parameter controls how deeply obfuscation can + // recurse. + void ObfuscateBoolConstantViaFloatConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t float_constant_id_1, uint32_t float_constant_id_2); + + // Similar to the above, but for signed int constants. + void ObfuscateBoolConstantViaSignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t signed_int_constant_id_1, uint32_t signed_int_constant_id_2); + + // Similar to the above, but for unsigned int constants. + void ObfuscateBoolConstantViaUnsignedIntConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + uint32_t unsigned_int_constant_id_1, uint32_t unsigned_int_constant_id_2); + + // A helper method to capture the common parts of the above methods. + // The method is used to obfuscate the boolean constant usage represented by + // |bool_constant_use| by replacing it with '|constant_id_1| OP + // |constant_id_2|', where 'OP' is chosen from either |greater_than_opcodes| + // or |less_than_opcodes|. + // + // The two constant ids must not represent the same value, and thus + // |greater_than_opcodes| may include 'greater than or equal' opcodes + // (similar for |less_than_opcodes|). + void ObfuscateBoolConstantViaConstantPair( + uint32_t depth, const protobufs::IdUseDescriptor& bool_constant_use, + const std::vector& greater_than_opcodes, + const std::vector& less_than_opcodes, uint32_t constant_id_1, + uint32_t constant_id_2, bool first_constant_is_larger); + + // A helper method to determine whether input operand |in_operand_index| of + // |inst| is the id of a constant, and add an id use descriptor to + // |candidate_constant_uses| if so. The other parameters are used for id use + // descriptor construction. + void MaybeAddConstantIdUse( + const opt::Instruction& inst, uint32_t in_operand_index, + uint32_t base_instruction_result_id, + const std::map& skipped_opcode_count, + std::vector* constant_uses); + + // Returns a vector of unique words that denote constants. Every such constant + // is used in |FactConstantUniform| and has type with id equal to |type_id|. + std::vector> GetConstantWordsFromUniformsForType( + uint32_t type_id); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_OBFUSCATE_CONSTANTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp new file mode 100644 index 0000000..4210125 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.cpp @@ -0,0 +1,196 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_outline_functions.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_outline_function.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassOutlineFunctions::FuzzerPassOutlineFunctions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassOutlineFunctions::~FuzzerPassOutlineFunctions() = default; + +void FuzzerPassOutlineFunctions::Apply() { + std::vector original_functions; + for (auto& function : *GetIRContext()->module()) { + original_functions.push_back(&function); + } + for (auto& function : original_functions) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfOutliningFunction())) { + continue; + } + std::vector blocks; + for (auto& block : *function) { + blocks.push_back(&block); + } + auto entry_block = MaybeGetEntryBlockSuitableForOutlining( + blocks[GetFuzzerContext()->RandomIndex(blocks)]); + + if (!entry_block) { + // The chosen block is not suitable to be the entry block of a region that + // will be outlined. + continue; + } + + auto dominator_analysis = GetIRContext()->GetDominatorAnalysis(function); + auto postdominator_analysis = + GetIRContext()->GetPostDominatorAnalysis(function); + std::vector candidate_exit_blocks; + for (auto postdominates_entry_block = entry_block; + postdominates_entry_block != nullptr; + postdominates_entry_block = postdominator_analysis->ImmediateDominator( + postdominates_entry_block)) { + // Consider the block if it is dominated by the entry block, ignore it if + // it is a continue target. + if (dominator_analysis->Dominates(entry_block, + postdominates_entry_block) && + !GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock( + postdominates_entry_block->id())) { + candidate_exit_blocks.push_back(postdominates_entry_block); + } + } + if (candidate_exit_blocks.empty()) { + continue; + } + auto exit_block = MaybeGetExitBlockSuitableForOutlining( + candidate_exit_blocks[GetFuzzerContext()->RandomIndex( + candidate_exit_blocks)]); + + if (!exit_block) { + // The block chosen is not suitable + continue; + } + + auto region_blocks = TransformationOutlineFunction::GetRegionBlocks( + GetIRContext(), entry_block, exit_block); + std::map input_id_to_fresh_id; + for (auto id : TransformationOutlineFunction::GetRegionInputIds( + GetIRContext(), region_blocks, exit_block)) { + input_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId(); + } + std::map output_id_to_fresh_id; + for (auto id : TransformationOutlineFunction::GetRegionOutputIds( + GetIRContext(), region_blocks, exit_block)) { + output_id_to_fresh_id[id] = GetFuzzerContext()->GetFreshId(); + } + TransformationOutlineFunction transformation( + entry_block->id(), exit_block->id(), + /*new_function_struct_return_type_id*/ + GetFuzzerContext()->GetFreshId(), + /*new_function_type_id*/ GetFuzzerContext()->GetFreshId(), + /*new_function_id*/ GetFuzzerContext()->GetFreshId(), + /*new_function_region_entry_block*/ + GetFuzzerContext()->GetFreshId(), + /*new_caller_result_id*/ GetFuzzerContext()->GetFreshId(), + /*new_callee_result_id*/ GetFuzzerContext()->GetFreshId(), + /*input_id_to_fresh_id*/ input_id_to_fresh_id, + /*output_id_to_fresh_id*/ output_id_to_fresh_id); + MaybeApplyTransformation(transformation); + } +} + +opt::BasicBlock* +FuzzerPassOutlineFunctions::MaybeGetEntryBlockSuitableForOutlining( + opt::BasicBlock* entry_block) { + // If the entry block is a loop header, we need to get or create its + // preheader and make it the entry block, if possible. + if (entry_block->IsLoopHeader()) { + auto predecessors = + GetIRContext()->cfg()->preds(entry_block->GetLabel()->result_id()); + + if (predecessors.size() < 2) { + // The header only has one predecessor (the back-edge block) and thus + // it is unreachable. The block cannot be adjusted to be suitable for + // outlining. + return nullptr; + } + + // Get or create a suitable preheader and make it become the entry block. + entry_block = + GetOrCreateSimpleLoopPreheader(entry_block->GetLabel()->result_id()); + } + + assert(!entry_block->IsLoopHeader() && + "The entry block cannot be a loop header at this point."); + + // If the entry block starts with OpPhi or OpVariable, try to split it. + if (entry_block->begin()->opcode() == SpvOpPhi || + entry_block->begin()->opcode() == SpvOpVariable) { + // Find the first non-OpPhi and non-OpVariable instruction. + auto non_phi_or_var_inst = &*entry_block->begin(); + while (non_phi_or_var_inst->opcode() == SpvOpPhi || + non_phi_or_var_inst->opcode() == SpvOpVariable) { + non_phi_or_var_inst = non_phi_or_var_inst->NextNode(); + } + + // Split the block. + uint32_t new_block_id = GetFuzzerContext()->GetFreshId(); + ApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), non_phi_or_var_inst), + new_block_id)); + + // The new entry block is the newly-created block. + entry_block = &*entry_block->GetParent()->FindBlock(new_block_id); + } + + return entry_block; +} + +opt::BasicBlock* +FuzzerPassOutlineFunctions::MaybeGetExitBlockSuitableForOutlining( + opt::BasicBlock* exit_block) { + // The exit block must not be a continue target. + assert(!GetIRContext()->GetStructuredCFGAnalysis()->IsContinueBlock( + exit_block->id()) && + "A candidate exit block cannot be a continue target."); + + // If the exit block is a merge block, try to split it and return the second + // block in the pair as the exit block. + if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock( + exit_block->id())) { + uint32_t new_block_id = GetFuzzerContext()->GetFreshId(); + + // Find the first non-OpPhi instruction, after which to split. + auto split_before = &*exit_block->begin(); + while (split_before->opcode() == SpvOpPhi) { + split_before = split_before->NextNode(); + } + + if (!MaybeApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), split_before), + new_block_id))) { + return nullptr; + } + + return &*exit_block->GetParent()->FindBlock(new_block_id); + } + + return exit_block; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h new file mode 100644 index 0000000..02022aa --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_outline_functions.h @@ -0,0 +1,65 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for outlining single-entry single-exit regions of a control +// flow graph into their own functions. +class FuzzerPassOutlineFunctions : public FuzzerPass { + public: + FuzzerPassOutlineFunctions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassOutlineFunctions(); + + void Apply() override; + + // Returns a block suitable to be an entry block for a region that can be + // outlined, i.e. a block that is not a loop header and that does not start + // with OpPhi or OpVariable. In particular, it returns: + // - |entry_block| if it is suitable + // - otherwise, a block found by: + // - looking for or creating a new preheader, if |entry_block| is a loop + // header + // - splitting the candidate entry block, if it starts with OpPhi or + // OpVariable. + // Returns nullptr if a suitable block cannot be found following the + // instructions above. + opt::BasicBlock* MaybeGetEntryBlockSuitableForOutlining( + opt::BasicBlock* entry_block); + + // Returns: + // - |exit_block| if it is not a merge block + // - the second block obtained by splitting |exit_block|, if |exit_block| is a + // merge block. + // Assumes that |exit_block| is not a continue target. + // The block returned by this function should be suitable to be the exit block + // of a region that can be outlined. + // Returns nullptr if |exit_block| is a merge block and it cannot be split. + opt::BasicBlock* MaybeGetExitBlockSuitableForOutlining( + opt::BasicBlock* exit_block); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_OUTLINE_FUNCTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp new file mode 100644 index 0000000..24c16fb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_blocks.h" + +#include "source/fuzz/transformation_move_block_down.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteBlocks::FuzzerPassPermuteBlocks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassPermuteBlocks::~FuzzerPassPermuteBlocks() = default; + +void FuzzerPassPermuteBlocks::Apply() { + // For now we do something very simple: we randomly decide whether to move a + // block, and for each block that we do move, we push it down as far as we + // legally can. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2635): it would be + // nice to randomly sample from the set of legal block permutations and then + // encode the chosen permutation via a series of move-block-down + // transformations. This should be possible but will require some thought. + + for (auto& function : *GetIRContext()->module()) { + std::vector block_ids; + // Collect all block ids for the function before messing with block + // ordering. + for (auto& block : function) { + block_ids.push_back(block.id()); + } + // Now consider each block id. We consider block ids in reverse, because + // e.g. in code generated from the following: + // + // if (...) { + // A + // B + // } else { + // C + // } + // + // block A cannot be moved down, but B has freedom to move and that movement + // would provide more freedom for A to move. + for (auto id = block_ids.rbegin(); id != block_ids.rend(); ++id) { + // Randomly decide whether to ignore the block id. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfMovingBlockDown())) { + continue; + } + // Keep pushing the block down, until pushing down fails. + // The loop is guaranteed to terminate because a block cannot be pushed + // down indefinitely. + while (true) { + TransformationMoveBlockDown transformation(*id); + if (!MaybeApplyTransformation(transformation)) { + break; + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h new file mode 100644 index 0000000..e5a672c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_blocks.h @@ -0,0 +1,40 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for shuffling the blocks of the module in a validity-preserving +// manner. +class FuzzerPassPermuteBlocks : public FuzzerPass { + public: + FuzzerPassPermuteBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassPermuteBlocks() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_BLOCKS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp new file mode 100644 index 0000000..de6b03f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_function_parameters.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_permute_function_parameters.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteFunctionParameters::FuzzerPassPermuteFunctionParameters( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassPermuteFunctionParameters::~FuzzerPassPermuteFunctionParameters() = + default; + +void FuzzerPassPermuteFunctionParameters::Apply() { + for (const auto& function : *GetIRContext()->module()) { + uint32_t function_id = function.result_id(); + + // Skip the function if it is an entry point + if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), function_id)) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingParameters())) { + continue; + } + + // Compute permutation for parameters + auto* function_type = + fuzzerutil::GetFunctionType(GetIRContext(), &function); + assert(function_type && "Function type is null"); + + // Don't take return type into account + uint32_t arg_size = function_type->NumInOperands() - 1; + + // Create a vector, fill it with [0, n-1] values and shuffle it + std::vector permutation(arg_size); + std::iota(permutation.begin(), permutation.end(), 0); + GetFuzzerContext()->Shuffle(&permutation); + + // Apply our transformation + ApplyTransformation(TransformationPermuteFunctionParameters( + function_id, GetFuzzerContext()->GetFreshId(), permutation)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h new file mode 100644 index 0000000..bc1031c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_function_parameters.h @@ -0,0 +1,45 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that, given a non-entry-point function taking n parameters +// and a permutation of the set [0, n - 1]: +// 1. Introduces a new function type that is the same as the original +// function's type but with the order of arguments permuted +// (only add this if it doesn't already exist) +// 2. Changes the type of the function to this type +// 3. Adjusts all calls to the function so that their arguments are permuted +class FuzzerPassPermuteFunctionParameters : public FuzzerPass { + public: + FuzzerPassPermuteFunctionParameters( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassPermuteFunctionParameters() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_FUNCTION_PARAMETERS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp new file mode 100644 index 0000000..6867053 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_instructions.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_move_instruction_down.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermuteInstructions::FuzzerPassPermuteInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassPermuteInstructions::~FuzzerPassPermuteInstructions() = default; + +void FuzzerPassPermuteInstructions::Apply() { + // We are iterating over all instructions in all basic blocks. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // We need to collect all instructions in the block into a separate vector + // since application of the transformation below might invalidate + // iterators. + std::vector instructions; + for (auto& instruction : block) { + instructions.push_back(&instruction); + } + + // We consider all instructions in reverse to increase the possible number + // of applied transformations. + for (auto it = instructions.rbegin(); it != instructions.rend(); ++it) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingInstructions())) { + continue; + } + + while (MaybeApplyTransformation(TransformationMoveInstructionDown( + MakeInstructionDescriptor(GetIRContext(), *it)))) { + // Apply the transformation as many times as possible. + } + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h new file mode 100644 index 0000000..e02ddfa --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_instructions.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Permutes instructions in every block of all while preserving the module's +// semantics. +class FuzzerPassPermuteInstructions : public FuzzerPass { + public: + FuzzerPassPermuteInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassPermuteInstructions() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_INSTRUCTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp new file mode 100644 index 0000000..c379c53 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_permute_phi_operands.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_permute_phi_operands.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPermutePhiOperands::FuzzerPassPermutePhiOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassPermutePhiOperands::~FuzzerPassPermutePhiOperands() = default; + +void FuzzerPassPermutePhiOperands::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& /*unused*/) { + const auto& inst = *inst_it; + + if (inst.opcode() != SpvOpPhi) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPermutingPhiOperands())) { + return; + } + + // Create a vector of indices for each pair of operands in the |inst|. + // OpPhi always has an even number of operands. + std::vector permutation(inst.NumInOperands() / 2); + std::iota(permutation.begin(), permutation.end(), 0); + GetFuzzerContext()->Shuffle(&permutation); + + ApplyTransformation(TransformationPermutePhiOperands( + inst.result_id(), std::move(permutation))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h new file mode 100644 index 0000000..974c2c1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_permute_phi_operands.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over all instructions in the module and randomly decides for each +// OpPhi instruction whether to permute its operands. +class FuzzerPassPermutePhiOperands : public FuzzerPass { + public: + FuzzerPassPermutePhiOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassPermutePhiOperands() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PERMUTE_PHI_OPERANDS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp new file mode 100644 index 0000000..7a115ae --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_propagate_instructions_down.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/transformation_propagate_instruction_down.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPropagateInstructionsDown::FuzzerPassPropagateInstructionsDown( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassPropagateInstructionsDown::~FuzzerPassPropagateInstructionsDown() = + default; + +void FuzzerPassPropagateInstructionsDown::Apply() { + for (const auto& function : *GetIRContext()->module()) { + std::vector reachable_blocks; + for (const auto& block : function) { + if (GetIRContext()->GetDominatorAnalysis(&function)->IsReachable( + &block)) { + reachable_blocks.push_back(&block); + } + } + + for (const auto* block : reachable_blocks) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPropagatingInstructionsDown())) { + continue; + } + + if (TransformationPropagateInstructionDown::IsApplicableToBlock( + GetIRContext(), block->id())) { + // Record fresh ids for every successor of the |block| that we can + // propagate an instruction into. + std::map fresh_ids; + for (auto id : + TransformationPropagateInstructionDown::GetAcceptableSuccessors( + GetIRContext(), block->id())) { + fresh_ids[id] = GetFuzzerContext()->GetFreshId(); + } + + ApplyTransformation(TransformationPropagateInstructionDown( + block->id(), GetFuzzerContext()->GetFreshId(), fresh_ids)); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h new file mode 100644 index 0000000..536bf00 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_down.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_DOWN_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_DOWN_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly propagates instructions from some block into the block's successors. +class FuzzerPassPropagateInstructionsDown : public FuzzerPass { + public: + FuzzerPassPropagateInstructionsDown( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassPropagateInstructionsDown() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_DOWN_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp new file mode 100644 index 0000000..16ec680 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_propagate_instruction_up.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPropagateInstructionsUp::FuzzerPassPropagateInstructionsUp( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassPropagateInstructionsUp::~FuzzerPassPropagateInstructionsUp() = + default; + +void FuzzerPassPropagateInstructionsUp::Apply() { + for (const auto& function : *GetIRContext()->module()) { + for (const auto& block : function) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPropagatingInstructionsUp())) { + continue; + } + + if (TransformationPropagateInstructionUp::IsApplicableToBlock( + GetIRContext(), block.id())) { + std::map fresh_ids; + for (auto id : GetIRContext()->cfg()->preds(block.id())) { + auto& fresh_id = fresh_ids[id]; + + if (!fresh_id) { + // Create a fresh id if it hasn't been created yet. |fresh_id| will + // be default-initialized to 0 in this case. + fresh_id = GetFuzzerContext()->GetFreshId(); + } + } + ApplyTransformation( + TransformationPropagateInstructionUp(block.id(), fresh_ids)); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h new file mode 100644 index 0000000..d915b31 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_propagate_instructions_up.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Decides whether to propagate instructions from some block into its +// predecessors. +class FuzzerPassPropagateInstructionsUp : public FuzzerPass { + public: + FuzzerPassPropagateInstructionsUp( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassPropagateInstructionsUp() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PROPAGATE_INSTRUCTIONS_UP_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp new file mode 100644 index 0000000..8d9acaa --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.cpp @@ -0,0 +1,154 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_push_id_through_variable.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassPushIdsThroughVariables::FuzzerPassPushIdsThroughVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassPushIdsThroughVariables::~FuzzerPassPushIdsThroughVariables() = + default; + +void FuzzerPassPushIdsThroughVariables::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* function, opt::BasicBlock* block, + opt::BasicBlock::iterator instruction_iterator, + const protobufs::InstructionDescriptor& instruction_descriptor) + -> void { + assert(instruction_iterator->opcode() == + instruction_descriptor.target_instruction_opcode() && + "The opcode of the instruction we might insert before must be " + "the same as the opcode in the descriptor for the instruction"); + + // Randomly decide whether to try pushing an id through a variable. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfPushingIdThroughVariable())) { + return; + } + + // The block containing the instruction we are going to insert before + // must be reachable. + if (!fuzzerutil::BlockIsReachableInItsFunction(GetIRContext(), block)) { + return; + } + + // It must be valid to insert OpStore and OpLoad instructions + // before the instruction to insert before. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpStore, instruction_iterator) || + !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpLoad, instruction_iterator)) { + return; + } + + // Randomly decides whether a global or local variable will be added. + auto variable_storage_class = GetFuzzerContext()->ChooseEven() + ? SpvStorageClassPrivate + : SpvStorageClassFunction; + + // Gets the available basic and pointer types. + auto basic_type_ids_and_pointers = + GetAvailableBasicTypesAndPointers(variable_storage_class); + auto& basic_types = basic_type_ids_and_pointers.first; + uint32_t basic_type_id = + basic_types[GetFuzzerContext()->RandomIndex(basic_types)]; + + // Looks for ids that we might wish to consider pushing through a + // variable. + std::vector value_instructions = + FindAvailableInstructions( + function, block, instruction_iterator, + [this, basic_type_id, instruction_descriptor]( + opt::IRContext* ir_context, + opt::Instruction* instruction) -> bool { + if (!instruction->result_id() || !instruction->type_id()) { + return false; + } + + if (instruction->type_id() != basic_type_id) { + return false; + } + + // If the id is irrelevant, we can use it since it will not + // participate in DataSynonym fact. Otherwise, we should be + // able to produce a synonym out of the id. + if (!GetTransformationContext() + ->GetFactManager() + ->IdIsIrrelevant(instruction->result_id()) && + !fuzzerutil::CanMakeSynonymOf(ir_context, + *GetTransformationContext(), + instruction)) { + return false; + } + + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, + FindInstruction(instruction_descriptor, ir_context), + instruction->result_id()); + }); + + if (value_instructions.empty()) { + return; + } + + // If the pointer type does not exist, then create it. + FindOrCreatePointerType(basic_type_id, variable_storage_class); + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // type support here is limited by the FindOrCreateZeroConstant + // function. + const auto* type_inst = + GetIRContext()->get_def_use_mgr()->GetDef(basic_type_id); + assert(type_inst); + switch (type_inst->opcode()) { + case SpvOpTypeBool: + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeArray: + case SpvOpTypeMatrix: + case SpvOpTypeVector: + case SpvOpTypeStruct: + break; + default: + return; + } + + // Create a constant to initialize the variable from. This might update + // module's id bound so it must be done before any fresh ids are + // computed. + auto initializer_id = FindOrCreateZeroConstant(basic_type_id, false); + + // Applies the push id through variable transformation. + ApplyTransformation(TransformationPushIdThroughVariable( + value_instructions[GetFuzzerContext()->RandomIndex( + value_instructions)] + ->result_id(), + GetFuzzerContext()->GetFreshId(), GetFuzzerContext()->GetFreshId(), + variable_storage_class, initializer_id, instruction_descriptor)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h new file mode 100644 index 0000000..3ad5404 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_push_ids_through_variables.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Adds instructions to the module that store existing ids into fresh variables +// and immediately load from said variables into new ids, thus creating synonyms +// between the existing and fresh ids. +class FuzzerPassPushIdsThroughVariables : public FuzzerPass { + public: + FuzzerPassPushIdsThroughVariables( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassPushIdsThroughVariables() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_PUSH_IDS_THROUGH_VARIABLES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp new file mode 100644 index 0000000..139dc6e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h" + +namespace spvtools { +namespace fuzz { + +namespace { +const uint32_t kArithmeticInstructionIndexLeftInOperand = 0; +} // namespace + +FuzzerPassReplaceAddsSubsMulsWithCarryingExtended:: + FuzzerPassReplaceAddsSubsMulsWithCarryingExtended( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceAddsSubsMulsWithCarryingExtended:: + ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() = default; + +void FuzzerPassReplaceAddsSubsMulsWithCarryingExtended::Apply() { + std::vector instructions_for_transformation; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + // Randomly decide whether to apply the transformation. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingAddSubMulWithCarryingExtended())) { + continue; + } + + // Check if the transformation can be applied to this instruction. + if (!TransformationReplaceAddSubMulWithCarryingExtended:: + IsInstructionSuitable(GetIRContext(), instruction)) { + continue; + } + instructions_for_transformation.push_back(instruction); + } + } + } + for (auto& instruction : instructions_for_transformation) { + // Get the operand type id. We know that both operands have the same + // type. + uint32_t operand_type_id = + GetIRContext() + ->get_def_use_mgr() + ->GetDef(instruction.GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)) + ->type_id(); + + // Ensure the required struct type exists. The struct type is based on + // the operand type. + FindOrCreateStructType({operand_type_id, operand_type_id}); + + ApplyTransformation(TransformationReplaceAddSubMulWithCarryingExtended( + GetFuzzerContext()->GetFreshId(), instruction.result_id())); + } +} +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h new file mode 100644 index 0000000..dd39e6b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that replaces instructions OpIAdd, OpISub, OpIMul with pairs of +// instructions. The first one (OpIAddCarry, OpISubBorrow, OpUMulExtended, +// OpSMulExtended) computes the result into a struct. The second one extracts +// the appropriate component from the struct to yield the original result. +class FuzzerPassReplaceAddsSubsMulsWithCarryingExtended : public FuzzerPass { + public: + FuzzerPassReplaceAddsSubsMulsWithCarryingExtended( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceAddsSubsMulsWithCarryingExtended() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_ADDS_SUBS_MULS_WITH_CARRYING_EXTENDED_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp new file mode 100644 index 0000000..e6bebea --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h" + +#include +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceBranchesFromDeadBlocksWithExits:: + FuzzerPassReplaceBranchesFromDeadBlocksWithExits( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceBranchesFromDeadBlocksWithExits:: + ~FuzzerPassReplaceBranchesFromDeadBlocksWithExits() = default; + +void FuzzerPassReplaceBranchesFromDeadBlocksWithExits::Apply() { + // OpKill can only be used as a terminator in a function that is guaranteed + // to be executed with the Fragment execution model. We conservatively only + // allow OpKill if every entry point in the module has the Fragment execution + // model. + auto fragment_execution_model_guaranteed = + std::all_of(GetIRContext()->module()->entry_points().begin(), + GetIRContext()->module()->entry_points().end(), + [](const opt::Instruction& entry_point) -> bool { + return entry_point.GetSingleWordInOperand(0) == + SpvExecutionModelFragment; + }); + + // Transformations of this type can disable one another. To avoid ordering + // bias, we therefore build a set of candidate transformations to apply, and + // subsequently apply them in a random order, skipping any that cease to be + // applicable. + std::vector + candidate_transformations; + + // Consider every block in every function. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // Probabilistically decide whether to skip this block. + if (GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingBranchFromDeadBlockWithExit())) { + continue; + } + // Check whether the block is suitable for having its terminator replaced. + if (!TransformationReplaceBranchFromDeadBlockWithExit::BlockIsSuitable( + GetIRContext(), *GetTransformationContext(), block)) { + continue; + } + // We can always use OpUnreachable to replace a block's terminator. + // Whether we can use OpKill depends on the execution model, and which of + // OpReturn and OpReturnValue we can use depends on the return type of the + // enclosing function. + std::vector opcodes = {SpvOpUnreachable}; + if (fragment_execution_model_guaranteed) { + opcodes.emplace_back(SpvOpKill); + } + auto function_return_type = + GetIRContext()->get_type_mgr()->GetType(function.type_id()); + if (function_return_type->AsVoid()) { + opcodes.emplace_back(SpvOpReturn); + } else if (fuzzerutil::CanCreateConstant(GetIRContext(), + function.type_id())) { + // For simplicity we only allow OpReturnValue if the function return + // type is a type for which we can create a constant. This allows us a + // zero of the given type as a default return value. + opcodes.emplace_back(SpvOpReturnValue); + } + // Choose one of the available terminator opcodes at random and create a + // candidate transformation. + auto opcode = opcodes[GetFuzzerContext()->RandomIndex(opcodes)]; + candidate_transformations.emplace_back( + TransformationReplaceBranchFromDeadBlockWithExit( + block.id(), opcode, + opcode == SpvOpReturnValue + ? FindOrCreateZeroConstant(function.type_id(), true) + : 0)); + } + } + + // Process the candidate transformations in a random order. + while (!candidate_transformations.empty()) { + // Transformations of this type can disable one another. For example, + // suppose we have dead blocks A, B, C, D arranged as follows: + // + // A | + // / \ | + // B C | + // \ / | + // D | + // + // Here we can replace the terminator of either B or C with an early exit, + // because D has two predecessors. But if we replace the terminator of B, + // say, we get: + // + // A | + // / \ | + // B C | + // / | + // D | + // + // and now it is no longer OK to replace the terminator of C as D only has + // one predecessor and we do not want to make D unreachable in the control + // flow graph. + MaybeApplyTransformation( + GetFuzzerContext()->RemoveAtRandomIndex(&candidate_transformations)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h new file mode 100644 index 0000000..62164b3 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_BRANCHES_FROM_DEAD_BLOCKS_WITH_EXITS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_BRANCHES_FROM_DEAD_BLOCKS_WITH_EXITS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Fuzzer pass that, under the right conditions, replaces branch instructions +// from dead blocks with non-branching "exit" terminators, such as OpKill and +// OpReturn. +class FuzzerPassReplaceBranchesFromDeadBlocksWithExits : public FuzzerPass { + public: + FuzzerPassReplaceBranchesFromDeadBlocksWithExits( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceBranchesFromDeadBlocksWithExits() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_BRANCHES_FROM_DEAD_BLOCKS_WITH_EXITS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp new file mode 100644 index 0000000..6847146 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceCopyMemoriesWithLoadsStores:: + FuzzerPassReplaceCopyMemoriesWithLoadsStores( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceCopyMemoriesWithLoadsStores:: + ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() = default; + +void FuzzerPassReplaceCopyMemoriesWithLoadsStores::Apply() { + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // Randomly decide whether to replace the OpCopyMemory. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingCopyMemoryWithLoadStore())) { + return; + } + + // The instruction must be OpCopyMemory. + if (instruction->opcode() != SpvOpCopyMemory) { + return; + } + + // Apply the transformation replacing OpCopyMemory with OpLoad and OpStore. + ApplyTransformation(TransformationReplaceCopyMemoryWithLoadStore( + GetFuzzerContext()->GetFreshId(), + MakeInstructionDescriptor(GetIRContext(), instruction))); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h new file mode 100644 index 0000000..9c24ac7 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Replaces instructions OpCopyMemory with loading the source variable to +// an intermediate value and storing this value into the target variable of +// the original OpCopyMemory instruction. +class FuzzerPassReplaceCopyMemoriesWithLoadsStores : public FuzzerPass { + public: + FuzzerPassReplaceCopyMemoriesWithLoadsStores( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceCopyMemoriesWithLoadsStores() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_MEMORIES_WITH_LOADS_STORES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp new file mode 100644 index 0000000..e372924 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceCopyObjectsWithStoresLoads:: + FuzzerPassReplaceCopyObjectsWithStoresLoads( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceCopyObjectsWithStoresLoads:: + ~FuzzerPassReplaceCopyObjectsWithStoresLoads() = default; + +void FuzzerPassReplaceCopyObjectsWithStoresLoads::Apply() { + GetIRContext()->module()->ForEachInst([this](opt::Instruction* instruction) { + // Randomly decide whether to replace OpCopyObject. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingCopyObjectWithStoreLoad())) { + return; + } + // The instruction must be OpCopyObject. + if (instruction->opcode() != SpvOpCopyObject) { + return; + } + // The opcode of the type_id instruction cannot be a OpTypePointer, + // because we cannot define a pointer to pointer. + if (GetIRContext() + ->get_def_use_mgr() + ->GetDef(instruction->type_id()) + ->opcode() == SpvOpTypePointer) { + return; + } + // It must be valid to insert OpStore and OpLoad instructions + // before the instruction OpCopyObject. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, + instruction) || + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, instruction)) { + return; + } + + // Randomly decides whether a global or local variable will be added. + auto variable_storage_class = GetFuzzerContext()->ChooseEven() + ? SpvStorageClassPrivate + : SpvStorageClassFunction; + + // Find or create a constant to initialize the variable from. The type of + // |instruction| must be such that the function FindOrCreateConstant can be + // called. + if (!fuzzerutil::CanCreateConstant(GetIRContext(), + instruction->type_id())) { + return; + } + auto variable_initializer_id = + FindOrCreateZeroConstant(instruction->type_id(), false); + + // Make sure that pointer type is defined. + FindOrCreatePointerType(instruction->type_id(), variable_storage_class); + // Apply the transformation replacing OpCopyObject with Store and Load. + ApplyTransformation(TransformationReplaceCopyObjectWithStoreLoad( + instruction->result_id(), GetFuzzerContext()->GetFreshId(), + variable_storage_class, variable_initializer_id)); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h new file mode 100644 index 0000000..ae03a45 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Replaces instructions OpCopyObject with storing into a new variable +// and immediately loading this variable to |result_id| of the +// original OpCopyObject instruction. +class FuzzerPassReplaceCopyObjectsWithStoresLoads : public FuzzerPass { + public: + FuzzerPassReplaceCopyObjectsWithStoresLoads( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceCopyObjectsWithStoresLoads() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_COPY_OBJECTS_WITH_STORES_LOADS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp new file mode 100644 index 0000000..432addb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/transformation_replace_irrelevant_id.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that, for every use of an id that has been recorded as +// irrelevant, randomly decides whether to replace it with another id of the +// same type. +FuzzerPassReplaceIrrelevantIds::FuzzerPassReplaceIrrelevantIds( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceIrrelevantIds::~FuzzerPassReplaceIrrelevantIds() = default; + +void FuzzerPassReplaceIrrelevantIds::Apply() { + // Keep track of the irrelevant ids. This includes all the ids that are + // irrelevant according to the fact manager and that are still present in the + // module (some of them may have been removed by previously-run + // transformations). + std::vector irrelevant_ids; + + // Keep a map from the type ids of irrelevant ids to all the ids with that + // type. + std::unordered_map> types_to_ids; + + // Find all the irrelevant ids that still exist in the module and all the + // types for which irrelevant ids exist. + for (auto id : + GetTransformationContext()->GetFactManager()->GetIrrelevantIds()) { + // Check that the id still exists in the module. + auto declaration = GetIRContext()->get_def_use_mgr()->GetDef(id); + if (!declaration) { + continue; + } + + irrelevant_ids.push_back(id); + + // If the type of this id has not been seen before, add a mapping from this + // type id to an empty list in |types_to_ids|. The list will be filled later + // on. + if (types_to_ids.count(declaration->type_id()) == 0) { + types_to_ids.insert({declaration->type_id(), {}}); + } + } + + // If no irrelevant ids were found, return. + if (irrelevant_ids.empty()) { + return; + } + + // For every type for which we have at least one irrelevant id, record all ids + // in the module which have that type. Skip ids of OpFunction instructions as + // we cannot use these as replacements. + for (const auto& pair : GetIRContext()->get_def_use_mgr()->id_to_defs()) { + uint32_t type_id = pair.second->type_id(); + if (pair.second->opcode() != SpvOpFunction && type_id && + types_to_ids.count(type_id)) { + types_to_ids[type_id].push_back(pair.first); + } + } + + // Keep a list of all the transformations to perform. We avoid applying the + // transformations while traversing the uses since applying the transformation + // invalidates all analyses, and we want to avoid invalidating and recomputing + // them every time. + std::vector transformations_to_apply; + + // Loop through all the uses of irrelevant ids, check that the id can be + // replaced and randomly decide whether to apply the transformation. + for (auto irrelevant_id : irrelevant_ids) { + uint32_t type_id = + GetIRContext()->get_def_use_mgr()->GetDef(irrelevant_id)->type_id(); + + GetIRContext()->get_def_use_mgr()->ForEachUse( + irrelevant_id, [this, &irrelevant_id, &type_id, &types_to_ids, + &transformations_to_apply](opt::Instruction* use_inst, + uint32_t use_index) { + // Randomly decide whether to consider this use. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingIrrelevantId())) { + return; + } + + // The id must be used as an input operand. + if (use_index < use_inst->NumOperands() - use_inst->NumInOperands()) { + // The id is used as an output operand, so we cannot replace this + // usage. + return; + } + + // Get the input operand index for this use, from the absolute operand + // index. + uint32_t in_index = + fuzzerutil::InOperandIndexFromOperandIndex(*use_inst, use_index); + + // Only go ahead if this id use can be replaced in principle. + if (!fuzzerutil::IdUseCanBeReplaced(GetIRContext(), + *GetTransformationContext(), + use_inst, in_index)) { + return; + } + + // Find out which ids could be used to replace this use. + std::vector available_replacement_ids; + + for (auto replacement_id : types_to_ids[type_id]) { + // It would be pointless to replace an id with itself. + if (replacement_id == irrelevant_id) { + continue; + } + + // We cannot replace a variable initializer with a non-constant. + if (TransformationReplaceIrrelevantId:: + AttemptsToReplaceVariableInitializerWithNonConstant( + *use_inst, *GetIRContext()->get_def_use_mgr()->GetDef( + replacement_id))) { + continue; + } + + // Only consider this replacement if the use point is within a basic + // block and the id is available at the use point. + // + // There might be opportunities for replacing a non-block use of an + // irrelevant id - such as the initializer of a global variable - + // with another id, but it would require some care (e.g. to ensure + // that the replacement id is defined earlier) and does not seem + // worth doing. + if (GetIRContext()->get_instr_block(use_inst) && + fuzzerutil::IdIsAvailableAtUse(GetIRContext(), use_inst, + in_index, replacement_id)) { + available_replacement_ids.push_back(replacement_id); + } + } + + // Only go ahead if there is at least one id with which this use can + // be replaced. + if (available_replacement_ids.empty()) { + return; + } + + // Choose the replacement id randomly. + uint32_t replacement_id = + available_replacement_ids[GetFuzzerContext()->RandomIndex( + available_replacement_ids)]; + + // Add this replacement to the list of transformations to apply. + transformations_to_apply.emplace_back( + TransformationReplaceIrrelevantId( + MakeIdUseDescriptorFromUse(GetIRContext(), use_inst, + in_index), + replacement_id)); + }); + } + + // Apply all the transformations. + for (const auto& transformation : transformations_to_apply) { + ApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h new file mode 100644 index 0000000..ab3f01d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_irrelevant_ids.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_IRRELEVANT_IDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_IRRELEVANT_IDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that, for every use of an irrelevant id, checks if it is +// possible to replace it with other ids of the same type and randomly decides +// whether to do it. +class FuzzerPassReplaceIrrelevantIds : public FuzzerPass { + public: + FuzzerPassReplaceIrrelevantIds( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceIrrelevantIds(); + + void Apply() override; +}; +} // namespace fuzz +} // namespace spvtools +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_IRRELEVANT_IDS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp new file mode 100644 index 0000000..c3e6578 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_linear_algebra_instruction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceLinearAlgebraInstructions:: + FuzzerPassReplaceLinearAlgebraInstructions( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceLinearAlgebraInstructions:: + ~FuzzerPassReplaceLinearAlgebraInstructions() = default; + +void FuzzerPassReplaceLinearAlgebraInstructions::Apply() { + // For each instruction, checks whether it is a linear algebra instruction. In + // this case, the transformation is randomly applied. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + for (auto& instruction : block) { + if (!spvOpcodeIsLinearAlgebra(instruction.opcode())) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingLinearAlgebraInstructions())) { + continue; + } + + ApplyTransformation(TransformationReplaceLinearAlgebraInstruction( + GetFuzzerContext()->GetFreshIds( + TransformationReplaceLinearAlgebraInstruction:: + GetRequiredFreshIdCount(GetIRContext(), &instruction)), + MakeInstructionDescriptor(GetIRContext(), &instruction))); + } + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h new file mode 100644 index 0000000..2c6126c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass replaces linear algebra instructions with its mathematical +// definition. +class FuzzerPassReplaceLinearAlgebraInstructions : public FuzzerPass { + public: + FuzzerPassReplaceLinearAlgebraInstructions( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceLinearAlgebraInstructions(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_LINEAR_ALGEBRA_INSTRUCTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp new file mode 100644 index 0000000..7690ac4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceLoadsStoresWithCopyMemories:: + FuzzerPassReplaceLoadsStoresWithCopyMemories( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceLoadsStoresWithCopyMemories:: + ~FuzzerPassReplaceLoadsStoresWithCopyMemories() = default; + +void FuzzerPassReplaceLoadsStoresWithCopyMemories::Apply() { + // We look for matching pairs of instructions OpLoad and + // OpStore within the same block. Potential instructions OpLoad to be matched + // are stored in a hash map. If we encounter instructions that write to memory + // or instructions of memory barriers that could operate on variables within + // unsafe storage classes we need to erase the hash map to avoid unsafe + // operations. + + // A vector of matching OpLoad and OpStore instructions. + std::vector> + op_load_store_pairs; + + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // A hash map storing potential OpLoad instructions. + std::unordered_map current_op_loads; + for (auto& instruction : block) { + // Add a potential OpLoad instruction. + if (instruction.opcode() == SpvOpLoad) { + current_op_loads[instruction.result_id()] = &instruction; + } else if (instruction.opcode() == SpvOpStore) { + if (current_op_loads.find(instruction.GetSingleWordOperand(1)) != + current_op_loads.end()) { + // We have found the matching OpLoad instruction to the current + // OpStore instruction. + op_load_store_pairs.push_back(std::make_pair( + current_op_loads[instruction.GetSingleWordOperand(1)], + &instruction)); + } + } + if (TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode( + instruction.opcode())) { + current_op_loads.clear(); + } else if (TransformationReplaceLoadStoreWithCopyMemory:: + IsMemoryBarrierOpCode(instruction.opcode())) { + for (auto it = current_op_loads.begin(); + it != current_op_loads.end();) { + // Get the storage class. + opt::Instruction* source_id = + GetIRContext()->get_def_use_mgr()->GetDef( + it->second->GetSingleWordOperand(2)); + SpvStorageClass storage_class = + fuzzerutil::GetStorageClassFromPointerType( + GetIRContext(), source_id->type_id()); + if (!TransformationReplaceLoadStoreWithCopyMemory:: + IsStorageClassSafeAcrossMemoryBarriers(storage_class)) { + it = current_op_loads.erase(it); + } else { + it++; + } + } + } + } + } + } + for (auto instr_pair : op_load_store_pairs) { + // Randomly decide to apply the transformation for the + // potential pairs. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingLoadStoreWithCopyMemory())) { + ApplyTransformation(TransformationReplaceLoadStoreWithCopyMemory( + MakeInstructionDescriptor(GetIRContext(), instr_pair.first), + MakeInstructionDescriptor(GetIRContext(), instr_pair.second))); + } + } +} // namespace fuzz +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h new file mode 100644 index 0000000..67871db --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass that takes pairs of instruction descriptors to OpLoad and +// OpStore that have the same intermediate value and in each pair replaces the +// OpStore with an equivalent OpCopyMemory. +class FuzzerPassReplaceLoadsStoresWithCopyMemories : public FuzzerPass { + public: + FuzzerPassReplaceLoadsStoresWithCopyMemories( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceLoadsStoresWithCopyMemories() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_LOADS_STORES_WITH_COPY_MEMORIES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp new file mode 100644 index 0000000..433cf74 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h" + +#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceOpPhiIdsFromDeadPredecessors:: + FuzzerPassReplaceOpPhiIdsFromDeadPredecessors( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceOpPhiIdsFromDeadPredecessors:: + ~FuzzerPassReplaceOpPhiIdsFromDeadPredecessors() = default; + +void FuzzerPassReplaceOpPhiIdsFromDeadPredecessors::Apply() { + // Keep a vector of the transformations to apply. + std::vector transformations; + + // Loop through the reachable blocks in the module. + for (auto& function : *GetIRContext()->module()) { + GetIRContext()->cfg()->ForEachBlockInPostOrder( + &*function.begin(), + [this, &function, &transformations](opt::BasicBlock* block) { + // Only consider dead blocks. + if (!GetTransformationContext()->GetFactManager()->BlockIsDead( + block->id())) { + return; + } + + // Find all the uses of the label id of the block inside OpPhi + // instructions. + GetIRContext()->get_def_use_mgr()->ForEachUse( + block->id(), [this, &function, block, &transformations]( + opt::Instruction* instruction, uint32_t) { + // Only consider OpPhi instructions. + if (instruction->opcode() != SpvOpPhi) { + return; + } + + // Randomly decide whether to consider this use. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingOpPhiIdFromDeadPredecessor())) { + return; + } + + // Get the current id corresponding to the predecessor. + uint32_t current_id = 0; + for (uint32_t i = 1; i < instruction->NumInOperands(); i += 2) { + if (instruction->GetSingleWordInOperand(i) == block->id()) { + // The corresponding id is at the index of the block - 1. + current_id = instruction->GetSingleWordInOperand(i - 1); + break; + } + } + assert( + current_id != 0 && + "The predecessor - and corresponding id - should always be " + "found."); + + uint32_t type_id = instruction->type_id(); + + // Find all the suitable instructions to replace the id. + const auto& candidates = FindAvailableInstructions( + &function, block, block->end(), + [type_id, current_id](opt::IRContext* /* unused */, + opt::Instruction* candidate) -> bool { + // Only consider instructions with a result id different + // from the currently-used one, and with the right type. + return candidate->HasResultId() && + candidate->type_id() == type_id && + candidate->result_id() != current_id; + }); + + // If there is no possible replacement, we cannot apply any + // transformation. + if (candidates.empty()) { + return; + } + + // Choose one of the candidates. + uint32_t replacement_id = + candidates[GetFuzzerContext()->RandomIndex(candidates)] + ->result_id(); + + // Add a new transformation to the list of transformations to + // apply. + transformations.emplace_back( + TransformationReplaceOpPhiIdFromDeadPredecessor( + instruction->result_id(), block->id(), replacement_id)); + }); + }); + } + + // Apply all the transformations. + for (const auto& transformation : transformations) { + ApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h new file mode 100644 index 0000000..972c5f9 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Replaces id operands in OpPhi instructions with other available ids of the +// right type, where the corresponding predecessor is dead. +class FuzzerPassReplaceOpPhiIdsFromDeadPredecessors : public FuzzerPass { + public: + FuzzerPassReplaceOpPhiIdsFromDeadPredecessors( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceOpPhiIdsFromDeadPredecessors(); + + void Apply() override; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPPHI_IDS_FROM_DEAD_PREDECESSORS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp new file mode 100644 index 0000000..c3db0ef --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceOpSelectsWithConditionalBranches:: + FuzzerPassReplaceOpSelectsWithConditionalBranches( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceOpSelectsWithConditionalBranches:: + ~FuzzerPassReplaceOpSelectsWithConditionalBranches() = default; + +void FuzzerPassReplaceOpSelectsWithConditionalBranches::Apply() { + // Keep track of the instructions that we want to replace. We need to collect + // them in a vector, since it's not safe to modify the module while iterating + // over it. + std::vector replaceable_opselect_instruction_ids; + + // Loop over all the instructions in the module. + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + // We cannot split loop headers, so we don't need to consider instructions + // in loop headers that are also merge blocks (since they would need to be + // split). + if (block.IsLoopHeader() && + GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock( + block.id())) { + continue; + } + + for (auto& instruction : block) { + // We only care about OpSelect instructions. + if (instruction.opcode() != SpvOpSelect) { + continue; + } + + // Randomly choose whether to consider this instruction for replacement. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfReplacingOpselectWithConditionalBranch())) { + continue; + } + + // If the selector does not have scalar boolean type (i.e., it is a + // boolean vector) then ignore this OpSelect. + if (GetIRContext() + ->get_def_use_mgr() + ->GetDef(fuzzerutil::GetTypeId( + GetIRContext(), instruction.GetSingleWordInOperand(0))) + ->opcode() != SpvOpTypeBool) { + continue; + } + + // If the block is a loop header and we need to split it, the + // transformation cannot be applied because loop headers cannot be + // split. We can break out of this loop because the transformation can + // only be applied to at most the first instruction in a loop header. + if (block.IsLoopHeader() && InstructionNeedsSplitBefore(&instruction)) { + break; + } + + // If the instruction separates an OpSampledImage from its use, the + // block cannot be split around it and the instruction cannot be + // replaced. + if (fuzzerutil:: + SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse( + &block, &instruction)) { + continue; + } + + // We can apply the transformation to this instruction. + replaceable_opselect_instruction_ids.push_back(instruction.result_id()); + } + } + } + + // Apply the transformations, splitting the blocks containing the + // instructions, if necessary. + for (uint32_t instruction_id : replaceable_opselect_instruction_ids) { + auto instruction = + GetIRContext()->get_def_use_mgr()->GetDef(instruction_id); + + // If the instruction requires the block containing it to be split before + // it, split the block. + if (InstructionNeedsSplitBefore(instruction)) { + ApplyTransformation(TransformationSplitBlock( + MakeInstructionDescriptor(GetIRContext(), instruction), + GetFuzzerContext()->GetFreshId())); + } + + // Decide whether to have two branches or just one. + bool two_branches = GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingBothBranchesWhenReplacingOpSelect()); + + // If there will be only one branch, decide whether it will be the true + // branch or the false branch. + bool true_branch_id_zero = + !two_branches && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfAddingTrueBranchWhenReplacingOpSelect()); + bool false_branch_id_zero = !two_branches && !true_branch_id_zero; + + uint32_t true_branch_id = + true_branch_id_zero ? 0 : GetFuzzerContext()->GetFreshId(); + uint32_t false_branch_id = + false_branch_id_zero ? 0 : GetFuzzerContext()->GetFreshId(); + + ApplyTransformation(TransformationReplaceOpSelectWithConditionalBranch( + instruction_id, true_branch_id, false_branch_id)); + } +} + +bool FuzzerPassReplaceOpSelectsWithConditionalBranches:: + InstructionNeedsSplitBefore(opt::Instruction* instruction) { + assert(instruction && instruction->opcode() == SpvOpSelect && + "The instruction must be OpSelect."); + + auto block = GetIRContext()->get_instr_block(instruction); + assert(block && "The instruction must be contained in a block."); + + // We need to split the block if the instruction is not the first in its + // block. + if (instruction->unique_id() != block->begin()->unique_id()) { + return true; + } + + // We need to split the block if it is a merge block. + if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) { + return true; + } + + // We need to split the block if it has more than one predecessor. + if (GetIRContext()->cfg()->preds(block->id()).size() != 1) { + return true; + } + + // We need to split the block if its predecessor is a header or it does not + // branch unconditionally to the block. + auto predecessor = GetIRContext()->get_instr_block( + GetIRContext()->cfg()->preds(block->id())[0]); + return predecessor->MergeBlockIdIfAny() || + predecessor->terminator()->opcode() != SpvOpBranch; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h new file mode 100644 index 0000000..04c6cc6 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h @@ -0,0 +1,53 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPSELECTS_WITH_CONDITIONAL_BRANCHES_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPSELECTS_WITH_CONDITIONAL_BRANCHES_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass to replace OpSelect instructions (where the condition is a +// scalar boolean) with conditional branches and OpPhi instructions. +class FuzzerPassReplaceOpSelectsWithConditionalBranches : public FuzzerPass { + public: + FuzzerPassReplaceOpSelectsWithConditionalBranches( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceOpSelectsWithConditionalBranches() override; + + void Apply() override; + + private: + // Returns true if any of the following holds: + // - the instruction is not the first in its block + // - the block containing it is a merge block + // - the block does not have a unique predecessor + // - the predecessor of the block is the header of a construct + // - the predecessor does not branch unconditionally to the block + // If this function returns true, the block must be split before the + // instruction for TransformationReplaceOpSelectWithConditionalBranch to be + // applicable. + // Assumes that the instruction is OpSelect. + bool InstructionNeedsSplitBefore(opt::Instruction* instruction); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_OPSELECTS_WITH_CONDITIONAL_BRANCHES_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp new file mode 100644 index 0000000..6b3a63b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_parameter_with_global.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceParameterWithGlobal::FuzzerPassReplaceParameterWithGlobal( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceParameterWithGlobal::~FuzzerPassReplaceParameterWithGlobal() = + default; + +void FuzzerPassReplaceParameterWithGlobal::Apply() { + for (const auto& function : *GetIRContext()->module()) { + if (fuzzerutil::FunctionIsEntryPoint(GetIRContext(), + function.result_id())) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingParametersWithGlobals())) { + continue; + } + + auto params = + fuzzerutil::GetParameters(GetIRContext(), function.result_id()); + + // Make sure at least one parameter can be replaced. Also checks that the + // function has at least one parameter. + if (std::none_of(params.begin(), params.end(), + [this](const opt::Instruction* param) { + return TransformationReplaceParameterWithGlobal:: + IsParameterTypeSupported(GetIRContext(), + param->type_id()); + })) { + continue; + } + + // Select id of a parameter to replace. + const opt::Instruction* replaced_param; + uint32_t param_type_id; + do { + replaced_param = GetFuzzerContext()->RemoveAtRandomIndex(¶ms); + param_type_id = replaced_param->type_id(); + assert(param_type_id && "Parameter has invalid type"); + } while ( + !TransformationReplaceParameterWithGlobal::IsParameterTypeSupported( + GetIRContext(), param_type_id)); + + assert(replaced_param && "Unable to find a parameter to replace"); + + // Make sure type id for the global variable exists in the module. + FindOrCreatePointerType(replaced_param->type_id(), SpvStorageClassPrivate); + + // Make sure initializer for the global variable exists in the module. + FindOrCreateZeroConstant(replaced_param->type_id(), false); + + ApplyTransformation(TransformationReplaceParameterWithGlobal( + GetFuzzerContext()->GetFreshId(), replaced_param->result_id(), + GetFuzzerContext()->GetFreshId())); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h new file mode 100644 index 0000000..25011bd --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_parameter_with_global.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over all non-entry-point functions in the module and randomly +// replaces a parameter with a global variable. +class FuzzerPassReplaceParameterWithGlobal : public FuzzerPass { + public: + FuzzerPassReplaceParameterWithGlobal( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceParameterWithGlobal() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMETER_WITH_GLOBAL_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp new file mode 100644 index 0000000..0e0610f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h" + +#include +#include + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_replace_params_with_struct.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassReplaceParamsWithStruct::FuzzerPassReplaceParamsWithStruct( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassReplaceParamsWithStruct::~FuzzerPassReplaceParamsWithStruct() = + default; + +void FuzzerPassReplaceParamsWithStruct::Apply() { + for (const auto& function : *GetIRContext()->module()) { + auto params = + fuzzerutil::GetParameters(GetIRContext(), function.result_id()); + + if (params.empty() || fuzzerutil::FunctionIsEntryPoint( + GetIRContext(), function.result_id())) { + continue; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfReplacingParametersWithStruct())) { + continue; + } + + std::vector parameter_index(params.size()); + std::iota(parameter_index.begin(), parameter_index.end(), 0); + + // Remove the indices of unsupported parameters. + auto new_end = + std::remove_if(parameter_index.begin(), parameter_index.end(), + [this, ¶ms](uint32_t index) { + return !TransformationReplaceParamsWithStruct:: + IsParameterTypeSupported(GetIRContext(), + params[index]->type_id()); + }); + + // std::remove_if changes the vector so that removed elements are placed at + // the end (i.e. [new_end, parameter_index.end()) is a range of removed + // elements). However, the size of the vector is not changed so we need to + // change that explicitly by popping those elements from the vector. + parameter_index.erase(new_end, parameter_index.end()); + + if (parameter_index.empty()) { + continue; + } + + // Select |num_replaced_params| parameters at random. We shuffle the vector + // of indices for randomization and shrink it to select first + // |num_replaced_params| parameters. + auto num_replaced_params = std::min( + parameter_index.size(), + GetFuzzerContext()->GetRandomNumberOfParametersReplacedWithStruct( + static_cast(params.size()))); + + GetFuzzerContext()->Shuffle(¶meter_index); + parameter_index.resize(num_replaced_params); + + // Make sure OpTypeStruct exists in the module. + std::vector component_type_ids; + for (auto index : parameter_index) { + component_type_ids.push_back(params[index]->type_id()); + } + + FindOrCreateStructType(component_type_ids); + + // Map parameters' indices to parameters' ids. + std::vector parameter_id; + for (auto index : parameter_index) { + parameter_id.push_back(params[index]->result_id()); + } + + std::map caller_id_to_fresh_id; + for (const auto* inst : + fuzzerutil::GetCallers(GetIRContext(), function.result_id())) { + caller_id_to_fresh_id[inst->result_id()] = + GetFuzzerContext()->GetFreshId(); + } + + ApplyTransformation(TransformationReplaceParamsWithStruct( + parameter_id, GetFuzzerContext()->GetFreshId(), + GetFuzzerContext()->GetFreshId(), caller_id_to_fresh_id)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h new file mode 100644 index 0000000..ed1aa6b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_replace_params_with_struct.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_ +#define SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Iterates over all functions in the module and randomly decides for each one +// whether to replace a subset of its parameters with a struct value. +class FuzzerPassReplaceParamsWithStruct : public FuzzerPass { + public: + FuzzerPassReplaceParamsWithStruct( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassReplaceParamsWithStruct() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_REPLACE_PARAMS_WITH_STRUCT_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp new file mode 100644 index 0000000..481cd96 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_split_blocks.h" + +#include + +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_split_block.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSplitBlocks::FuzzerPassSplitBlocks( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassSplitBlocks::~FuzzerPassSplitBlocks() = default; + +void FuzzerPassSplitBlocks::Apply() { + // Gather up pointers to all the blocks in the module. We are then able to + // iterate over these pointers and split the blocks to which they point; + // we cannot safely split blocks while we iterate through the module. + std::vector blocks; + for (auto& function : *GetIRContext()->module()) { + for (auto& block : function) { + blocks.push_back(&block); + } + } + + // Now go through all the block pointers that were gathered. + for (auto& block : blocks) { + // Probabilistically decide whether to try to split this block. + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfSplittingBlock())) { + // We are not going to try to split this block. + continue; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2964): consider + // taking a simpler approach to identifying the instruction before which + // to split a block. + + // We are going to try to split this block. We now need to choose where + // to split it. We describe the instruction before which we would like to + // split a block via an InstructionDescriptor, details of which are + // commented in the protobufs definition file. + std::vector instruction_descriptors; + + // The initial base instruction is the block label. + uint32_t base = block->id(); + + // Counts the number of times we have seen each opcode since we reset the + // base instruction. + std::map skip_count; + + // Consider every instruction in the block. The label is excluded: it is + // only necessary to consider it as a base in case the first instruction + // in the block does not have a result id. + for (auto& inst : *block) { + if (inst.HasResultId()) { + // In the case that the instruction has a result id, we use the + // instruction as its own base, and clear the skip counts we have + // collected. + base = inst.result_id(); + skip_count.clear(); + } + const SpvOp opcode = inst.opcode(); + instruction_descriptors.emplace_back(MakeInstructionDescriptor( + base, opcode, skip_count.count(opcode) ? skip_count.at(opcode) : 0)); + if (!inst.HasResultId()) { + skip_count[opcode] = + skip_count.count(opcode) ? skip_count.at(opcode) + 1 : 1; + } + } + // Having identified all the places we might be able to split the block, + // we choose one of them. + auto transformation = TransformationSplitBlock( + instruction_descriptors[GetFuzzerContext()->RandomIndex( + instruction_descriptors)], + GetFuzzerContext()->GetFreshId()); + // If the position we have chosen turns out to be a valid place to split + // the block, we apply the split. Otherwise the block just doesn't get + // split. + MaybeApplyTransformation(transformation); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h new file mode 100644 index 0000000..0ece48a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_split_blocks.h @@ -0,0 +1,40 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_SPLIT_BLOCKS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SPLIT_BLOCKS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// A fuzzer pass for splitting blocks in the module, to create more blocks; this +// can be very useful for giving other passes a chance to apply. +class FuzzerPassSplitBlocks : public FuzzerPass { + public: + FuzzerPassSplitBlocks(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassSplitBlocks() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SPLIT_BLOCKS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp new file mode 100644 index 0000000..321e8ef --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_swap_commutable_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_swap_commutable_operands.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSwapCommutableOperands::FuzzerPassSwapCommutableOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassSwapCommutableOperands::~FuzzerPassSwapCommutableOperands() = default; + +void FuzzerPassSwapCommutableOperands::Apply() { + auto context = GetIRContext(); + // Iterates over the module's instructions and checks whether it is + // commutative. In this case, the transformation is probabilistically applied. + context->module()->ForEachInst( + [this, context](opt::Instruction* instruction) { + if (spvOpcodeIsCommutativeBinaryOperator(instruction->opcode()) && + GetFuzzerContext()->ChooseEven()) { + auto instructionDescriptor = + MakeInstructionDescriptor(context, instruction); + auto transformation = + TransformationSwapCommutableOperands(instructionDescriptor); + ApplyTransformation(transformation); + } + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h new file mode 100644 index 0000000..74d937d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_commutable_operands.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for all commutative instructions in the module, +// probabilistically choosing which of these instructions will have its input +// operands swapped. +class FuzzerPassSwapCommutableOperands : public FuzzerPass { + public: + FuzzerPassSwapCommutableOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassSwapCommutableOperands(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_COMMUTABLE_OPERANDS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp new file mode 100644 index 0000000..9433a61 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_swap_conditional_branch_operands.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassSwapBranchConditionalOperands:: + FuzzerPassSwapBranchConditionalOperands( + opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassSwapBranchConditionalOperands:: + ~FuzzerPassSwapBranchConditionalOperands() = default; + +void FuzzerPassSwapBranchConditionalOperands::Apply() { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& instruction_descriptor) { + const auto& inst = *inst_it; + + if (inst.opcode() != SpvOpBranchConditional) { + return; + } + + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext() + ->GetChanceOfSwappingConditionalBranchOperands())) { + return; + } + + ApplyTransformation(TransformationSwapConditionalBranchOperands( + instruction_descriptor, GetFuzzerContext()->GetFreshId())); + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h new file mode 100644 index 0000000..f84f3ba --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly decides for each OpBranchConditional instruction in the module +// whether to swap its operands or not. +class FuzzerPassSwapBranchConditionalOperands : public FuzzerPass { + public: + FuzzerPassSwapBranchConditionalOperands( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassSwapBranchConditionalOperands() override; + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_SWAP_BRANCH_CONDITIONAL_OPERANDS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp new file mode 100644 index 0000000..4f26cba --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassToggleAccessChainInstruction::FuzzerPassToggleAccessChainInstruction( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassToggleAccessChainInstruction:: + ~FuzzerPassToggleAccessChainInstruction() = default; + +void FuzzerPassToggleAccessChainInstruction::Apply() { + auto context = GetIRContext(); + // Iterates over the module's instructions and checks whether it is + // OpAccessChain or OpInBoundsAccessChain. In this case, the transformation is + // probabilistically applied. + context->module()->ForEachInst([this, + context](opt::Instruction* instruction) { + SpvOp opcode = instruction->opcode(); + if ((opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) && + GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfTogglingAccessChainInstruction())) { + auto instructionDescriptor = + MakeInstructionDescriptor(context, instruction); + auto transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ApplyTransformation(transformation); + } + }); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h new file mode 100644 index 0000000..d77c7cb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_toggle_access_chain_instruction.h @@ -0,0 +1,40 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ +#define SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// This fuzzer pass searches for all access chain instructions in the module, +// probabilistically choosing which of these instructions will be toggled. +class FuzzerPassToggleAccessChainInstruction : public FuzzerPass { + public: + FuzzerPassToggleAccessChainInstruction( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassToggleAccessChainInstruction(); + + void Apply() override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp new file mode 100644 index 0000000..e6cdca4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.cpp @@ -0,0 +1,140 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_split_block.h" +#include "source/fuzz/transformation_wrap_region_in_selection.h" + +namespace spvtools { +namespace fuzz { + +FuzzerPassWrapRegionsInSelections::FuzzerPassWrapRegionsInSelections( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + +FuzzerPassWrapRegionsInSelections::~FuzzerPassWrapRegionsInSelections() = + default; + +void FuzzerPassWrapRegionsInSelections::Apply() { + for (auto& function : *GetIRContext()->module()) { + if (!GetFuzzerContext()->ChoosePercentage( + GetFuzzerContext()->GetChanceOfWrappingRegionInSelection())) { + continue; + } + + // It is easier to select an element at random from a vector than from an + // instruction list. + std::vector header_block_candidates; + for (auto& block : function) { + header_block_candidates.push_back(&block); + } + + if (header_block_candidates.empty()) { + continue; + } + + // Try to get a header block candidate that will increase the chances of the + // transformation being applicable. + auto* header_block_candidate = MaybeGetHeaderBlockCandidate( + header_block_candidates[GetFuzzerContext()->RandomIndex( + header_block_candidates)]); + if (!header_block_candidate) { + continue; + } + + std::vector merge_block_candidates; + for (auto& block : function) { + if (GetIRContext()->GetDominatorAnalysis(&function)->StrictlyDominates( + header_block_candidate, &block) && + GetIRContext() + ->GetPostDominatorAnalysis(&function) + ->StrictlyDominates(&block, header_block_candidate)) { + merge_block_candidates.push_back(&block); + } + } + + if (merge_block_candidates.empty()) { + continue; + } + + // Try to get a merge block candidate that will increase the chances of the + // transformation being applicable. + auto* merge_block_candidate = MaybeGetMergeBlockCandidate( + merge_block_candidates[GetFuzzerContext()->RandomIndex( + merge_block_candidates)]); + if (!merge_block_candidate) { + continue; + } + + if (!TransformationWrapRegionInSelection::IsApplicableToBlockRange( + GetIRContext(), header_block_candidate->id(), + merge_block_candidate->id())) { + continue; + } + + // This boolean constant will be used as a condition for the + // OpBranchConditional instruction. We mark it as irrelevant to be able to + // replace it with a more interesting value later. + auto branch_condition = GetFuzzerContext()->ChooseEven(); + FindOrCreateBoolConstant(branch_condition, true); + + ApplyTransformation(TransformationWrapRegionInSelection( + header_block_candidate->id(), merge_block_candidate->id(), + branch_condition)); + } +} + +opt::BasicBlock* +FuzzerPassWrapRegionsInSelections::MaybeGetHeaderBlockCandidate( + opt::BasicBlock* header_block_candidate) { + // Try to create a preheader if |header_block_candidate| is a loop header. + if (header_block_candidate->IsLoopHeader()) { + // GetOrCreateSimpleLoopPreheader only supports reachable blocks. + return GetIRContext()->cfg()->preds(header_block_candidate->id()).size() == + 1 + ? nullptr + : GetOrCreateSimpleLoopPreheader(header_block_candidate->id()); + } + + // Try to split |header_block_candidate| if it's already a header block. + if (header_block_candidate->GetMergeInst()) { + SplitBlockAfterOpPhiOrOpVariable(header_block_candidate->id()); + } + + return header_block_candidate; +} + +opt::BasicBlock* FuzzerPassWrapRegionsInSelections::MaybeGetMergeBlockCandidate( + opt::BasicBlock* merge_block_candidate) { + // If |merge_block_candidate| is a merge block of some construct, try to split + // it and return a newly created block. + if (GetIRContext()->GetStructuredCFGAnalysis()->IsMergeBlock( + merge_block_candidate->id())) { + // We can't split a merge block if it's also a loop header. + return merge_block_candidate->IsLoopHeader() + ? nullptr + : SplitBlockAfterOpPhiOrOpVariable(merge_block_candidate->id()); + } + + return merge_block_candidate; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h new file mode 100644 index 0000000..eb28d20 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_pass_wrap_regions_in_selections.h @@ -0,0 +1,55 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_PASS_WRAP_REGIONS_IN_SELECTIONS_H_ +#define SOURCE_FUZZ_FUZZER_PASS_WRAP_REGIONS_IN_SELECTIONS_H_ + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Randomly wraps a region of blocks in every function into a selection +// construct. +class FuzzerPassWrapRegionsInSelections : public FuzzerPass { + public: + FuzzerPassWrapRegionsInSelections( + opt::IRContext* ir_context, TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations); + + ~FuzzerPassWrapRegionsInSelections() override; + + void Apply() override; + + private: + // Tries to adjust |header_block_candidate| such that + // TransformationWrapRegionInSelection has higher chances of being + // applied. In particular, tries to split |header_block_candidate| if it's + // already a header block of some other construct. + opt::BasicBlock* MaybeGetHeaderBlockCandidate( + opt::BasicBlock* header_block_candidate); + + // Tries to adjust |merge_block_candidate| such that + // TransformationWrapRegionInSelection has higher chances of being + // applied. In particular, tries to split |merge_block_candidate| if it's + // already a merge block of some other construct. + opt::BasicBlock* MaybeGetMergeBlockCandidate( + opt::BasicBlock* merge_block_candidate); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_PASS_WRAP_REGIONS_IN_SELECTIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_util.cpp b/third_party/spirv-tools/source/fuzz/fuzzer_util.cpp new file mode 100644 index 0000000..8c14db4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_util.cpp @@ -0,0 +1,1832 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_util.h" + +#include +#include + +#include "source/opt/build_module.h" + +namespace spvtools { +namespace fuzz { + +namespace fuzzerutil { +namespace { + +uint32_t MaybeGetOpConstant(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, + uint32_t type_id, bool is_irrelevant) { + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == SpvOpConstant && inst.type_id() == type_id && + inst.GetInOperand(0).words == words && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant) { + return inst.result_id(); + } + } + + return 0; +} + +} // namespace + +const spvtools::MessageConsumer kSilentMessageConsumer = + [](spv_message_level_t, const char*, const spv_position_t&, + const char*) -> void {}; + +bool IsFreshId(opt::IRContext* context, uint32_t id) { + return !context->get_def_use_mgr()->GetDef(id); +} + +void UpdateModuleIdBound(opt::IRContext* context, uint32_t id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2541) consider the + // case where the maximum id bound is reached. + context->module()->SetIdBound( + std::max(context->module()->id_bound(), id + 1)); +} + +opt::BasicBlock* MaybeFindBlock(opt::IRContext* context, + uint32_t maybe_block_id) { + auto inst = context->get_def_use_mgr()->GetDef(maybe_block_id); + if (inst == nullptr) { + // No instruction defining this id was found. + return nullptr; + } + if (inst->opcode() != SpvOpLabel) { + // The instruction defining the id is not a label, so it cannot be a block + // id. + return nullptr; + } + return context->cfg()->block(maybe_block_id); +} + +bool PhiIdsOkForNewEdge( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + const google::protobuf::RepeatedField& phi_ids) { + if (bb_from->IsSuccessor(bb_to)) { + // There is already an edge from |from_block| to |to_block|, so there is + // no need to extend OpPhi instructions. Do not allow phi ids to be + // present. This might turn out to be too strict; perhaps it would be OK + // just to ignore the ids in this case. + return phi_ids.empty(); + } + // The edge would add a previously non-existent edge from |from_block| to + // |to_block|, so we go through the given phi ids and check that they exactly + // match the OpPhi instructions in |to_block|. + uint32_t phi_index = 0; + // An explicit loop, rather than applying a lambda to each OpPhi in |bb_to|, + // makes sense here because we need to increment |phi_index| for each OpPhi + // instruction. + for (auto& inst : *bb_to) { + if (inst.opcode() != SpvOpPhi) { + // The OpPhi instructions all occur at the start of the block; if we find + // a non-OpPhi then we have seen them all. + break; + } + if (phi_index == static_cast(phi_ids.size())) { + // Not enough phi ids have been provided to account for the OpPhi + // instructions. + return false; + } + // Look for an instruction defining the next phi id. + opt::Instruction* phi_extension = + context->get_def_use_mgr()->GetDef(phi_ids[phi_index]); + if (!phi_extension) { + // The id given to extend this OpPhi does not exist. + return false; + } + if (phi_extension->type_id() != inst.type_id()) { + // The instruction given to extend this OpPhi either does not have a type + // or its type does not match that of the OpPhi. + return false; + } + + if (context->get_instr_block(phi_extension)) { + // The instruction defining the phi id has an associated block (i.e., it + // is not a global value). Check whether its definition dominates the + // exit of |from_block|. + auto dominator_analysis = + context->GetDominatorAnalysis(bb_from->GetParent()); + if (!dominator_analysis->Dominates(phi_extension, + bb_from->terminator())) { + // The given id is no good as its definition does not dominate the exit + // of |from_block| + return false; + } + } + phi_index++; + } + // We allow some of the ids provided for extending OpPhi instructions to be + // unused. Their presence does no harm, and requiring a perfect match may + // make transformations less likely to cleanly apply. + return true; +} + +void AddUnreachableEdgeAndUpdateOpPhis( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + uint32_t bool_id, + const google::protobuf::RepeatedField& phi_ids) { + assert(PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) && + "Precondition on phi_ids is not satisfied"); + assert(bb_from->terminator()->opcode() == SpvOpBranch && + "Precondition on terminator of bb_from is not satisfied"); + + // Get the id of the boolean constant to be used as the condition. + auto condition_inst = context->get_def_use_mgr()->GetDef(bool_id); + assert(condition_inst && + (condition_inst->opcode() == SpvOpConstantTrue || + condition_inst->opcode() == SpvOpConstantFalse) && + "|bool_id| is invalid"); + + auto condition_value = condition_inst->opcode() == SpvOpConstantTrue; + + const bool from_to_edge_already_exists = bb_from->IsSuccessor(bb_to); + auto successor = bb_from->terminator()->GetSingleWordInOperand(0); + + // Add the dead branch, by turning OpBranch into OpBranchConditional, and + // ordering the targets depending on whether the given boolean corresponds to + // true or false. + bb_from->terminator()->SetOpcode(SpvOpBranchConditional); + bb_from->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {bool_id}}, + {SPV_OPERAND_TYPE_ID, {condition_value ? successor : bb_to->id()}}, + {SPV_OPERAND_TYPE_ID, {condition_value ? bb_to->id() : successor}}}); + + // Update OpPhi instructions in the target block if this branch adds a + // previously non-existent edge from source to target. + if (!from_to_edge_already_exists) { + uint32_t phi_index = 0; + for (auto& inst : *bb_to) { + if (inst.opcode() != SpvOpPhi) { + break; + } + assert(phi_index < static_cast(phi_ids.size()) && + "There should be at least one phi id per OpPhi instruction."); + inst.AddOperand({SPV_OPERAND_TYPE_ID, {phi_ids[phi_index]}}); + inst.AddOperand({SPV_OPERAND_TYPE_ID, {bb_from->id()}}); + phi_index++; + } + } +} + +bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id, + uint32_t loop_header_id) { + auto block = context->cfg()->block(block_id); + auto loop_header = context->cfg()->block(loop_header_id); + + // |block| and |loop_header| must be defined, |loop_header| must be in fact + // loop header and |block| must branch to it. + if (!(block && loop_header && loop_header->IsLoopHeader() && + block->IsSuccessor(loop_header))) { + return false; + } + + // |block_id| must be reachable and be dominated by |loop_header|. + opt::DominatorAnalysis* dominator_analysis = + context->GetDominatorAnalysis(loop_header->GetParent()); + return dominator_analysis->IsReachable(block_id) && + dominator_analysis->Dominates(loop_header_id, block_id); +} + +bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, + uint32_t maybe_loop_header_id) { + // We deem a block to be part of a loop's continue construct if the loop's + // continue target dominates the block. + auto containing_construct_block = context->cfg()->block(maybe_loop_header_id); + if (containing_construct_block->IsLoopHeader()) { + auto continue_target = containing_construct_block->ContinueBlockId(); + if (context->GetDominatorAnalysis(containing_construct_block->GetParent()) + ->Dominates(continue_target, block_id)) { + return true; + } + } + return false; +} + +opt::BasicBlock::iterator GetIteratorForInstruction( + opt::BasicBlock* block, const opt::Instruction* inst) { + for (auto inst_it = block->begin(); inst_it != block->end(); ++inst_it) { + if (inst == &*inst_it) { + return inst_it; + } + } + return block->end(); +} + +bool BlockIsReachableInItsFunction(opt::IRContext* context, + opt::BasicBlock* bb) { + auto enclosing_function = bb->GetParent(); + return context->GetDominatorAnalysis(enclosing_function) + ->Dominates(enclosing_function->entry().get(), bb); +} + +bool CanInsertOpcodeBeforeInstruction( + SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block) { + if (instruction_in_block->PreviousNode() && + (instruction_in_block->PreviousNode()->opcode() == SpvOpLoopMerge || + instruction_in_block->PreviousNode()->opcode() == SpvOpSelectionMerge)) { + // We cannot insert directly after a merge instruction. + return false; + } + if (opcode != SpvOpVariable && + instruction_in_block->opcode() == SpvOpVariable) { + // We cannot insert a non-OpVariable instruction directly before a + // variable; variables in a function must be contiguous in the entry block. + return false; + } + // We cannot insert a non-OpPhi instruction directly before an OpPhi, because + // OpPhi instructions need to be contiguous at the start of a block. + return opcode == SpvOpPhi || instruction_in_block->opcode() != SpvOpPhi; +} + +bool CanMakeSynonymOf(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* inst) { + if (inst->opcode() == SpvOpSampledImage) { + // The SPIR-V data rules say that only very specific instructions may + // may consume the result id of an OpSampledImage, and this excludes the + // instructions that are used for making synonyms. + return false; + } + if (!inst->HasResultId()) { + // We can only make a synonym of an instruction that generates an id. + return false; + } + if (transformation_context.GetFactManager()->IdIsIrrelevant( + inst->result_id())) { + // An irrelevant id can't be a synonym of anything. + return false; + } + if (!inst->type_id()) { + // We can only make a synonym of an instruction that has a type. + return false; + } + auto type_inst = ir_context->get_def_use_mgr()->GetDef(inst->type_id()); + if (type_inst->opcode() == SpvOpTypeVoid) { + // We only make synonyms of instructions that define objects, and an object + // cannot have void type. + return false; + } + if (type_inst->opcode() == SpvOpTypePointer) { + switch (inst->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + // We disallow making synonyms of null or undefined pointers. This is + // to provide the property that if the original shader exhibited no bad + // pointer accesses, the transformed shader will not either. + return false; + default: + break; + } + } + + // We do not make synonyms of objects that have decorations: if the synonym is + // not decorated analogously, using the original object vs. its synonymous + // form may not be equivalent. + return ir_context->get_decoration_mgr() + ->GetDecorationsFor(inst->result_id(), true) + .empty(); +} + +bool IsCompositeType(const opt::analysis::Type* type) { + return type && (type->AsArray() || type->AsMatrix() || type->AsStruct() || + type->AsVector()); +} + +std::vector RepeatedFieldToVector( + const google::protobuf::RepeatedField& repeated_field) { + std::vector result; + for (auto i : repeated_field) { + result.push_back(i); + } + return result; +} + +uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context, + uint32_t base_object_type_id, + uint32_t index) { + auto should_be_composite_type = + context->get_def_use_mgr()->GetDef(base_object_type_id); + assert(should_be_composite_type && "The type should exist."); + switch (should_be_composite_type->opcode()) { + case SpvOpTypeArray: { + auto array_length = GetArraySize(*should_be_composite_type, context); + if (array_length == 0 || index >= array_length) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(0); + } + case SpvOpTypeMatrix: + case SpvOpTypeVector: { + auto count = should_be_composite_type->GetSingleWordInOperand(1); + if (index >= count) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(0); + } + case SpvOpTypeStruct: { + if (index >= GetNumberOfStructMembers(*should_be_composite_type)) { + return 0; + } + return should_be_composite_type->GetSingleWordInOperand(index); + } + default: + return 0; + } +} + +uint32_t WalkCompositeTypeIndices( + opt::IRContext* context, uint32_t base_object_type_id, + const google::protobuf::RepeatedField& indices) { + uint32_t sub_object_type_id = base_object_type_id; + for (auto index : indices) { + sub_object_type_id = + WalkOneCompositeTypeIndex(context, sub_object_type_id, index); + if (!sub_object_type_id) { + return 0; + } + } + return sub_object_type_id; +} + +uint32_t GetNumberOfStructMembers( + const opt::Instruction& struct_type_instruction) { + assert(struct_type_instruction.opcode() == SpvOpTypeStruct && + "An OpTypeStruct instruction is required here."); + return struct_type_instruction.NumInOperands(); +} + +uint32_t GetArraySize(const opt::Instruction& array_type_instruction, + opt::IRContext* context) { + auto array_length_constant = + context->get_constant_mgr() + ->GetConstantFromInst(context->get_def_use_mgr()->GetDef( + array_type_instruction.GetSingleWordInOperand(1))) + ->AsIntConstant(); + if (array_length_constant->words().size() != 1) { + return 0; + } + return array_length_constant->GetU32(); +} + +uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, + opt::IRContext* ir_context) { + switch (composite_type_inst.opcode()) { + case SpvOpTypeArray: + return fuzzerutil::GetArraySize(composite_type_inst, ir_context); + case SpvOpTypeMatrix: + case SpvOpTypeVector: + return composite_type_inst.GetSingleWordInOperand(1); + case SpvOpTypeStruct: { + return fuzzerutil::GetNumberOfStructMembers(composite_type_inst); + } + case SpvOpTypeRuntimeArray: + assert(false && + "GetBoundForCompositeIndex should not be invoked with an " + "OpTypeRuntimeArray, which does not have a static bound."); + return 0; + default: + assert(false && "Unknown composite type."); + return 0; + } +} + +bool IsValid(const opt::IRContext* context, + spv_validator_options validator_options, + MessageConsumer consumer) { + std::vector binary; + context->module()->ToBinary(&binary, false); + SpirvTools tools(context->grammar().target_env()); + tools.SetMessageConsumer(consumer); + return tools.Validate(binary.data(), binary.size(), validator_options); +} + +bool IsValidAndWellFormed(const opt::IRContext* ir_context, + spv_validator_options validator_options, + MessageConsumer consumer) { + if (!IsValid(ir_context, validator_options, consumer)) { + // Expression to dump |ir_context| to /data/temp/shader.spv: + // DumpShader(ir_context, "/data/temp/shader.spv") + consumer(SPV_MSG_INFO, nullptr, {}, + "Module is invalid (set a breakpoint to inspect)."); + return false; + } + // Check that all blocks in the module have appropriate parent functions. + for (auto& function : *ir_context->module()) { + for (auto& block : function) { + if (block.GetParent() == nullptr) { + std::stringstream ss; + ss << "Block " << block.id() << " has no parent; its parent should be " + << function.result_id() << " (set a breakpoint to inspect)."; + consumer(SPV_MSG_INFO, nullptr, {}, ss.str().c_str()); + return false; + } + if (block.GetParent() != &function) { + std::stringstream ss; + ss << "Block " << block.id() << " should have parent " + << function.result_id() << " but instead has parent " + << block.GetParent() << " (set a breakpoint to inspect)."; + consumer(SPV_MSG_INFO, nullptr, {}, ss.str().c_str()); + return false; + } + } + } + + // Check that all instructions have distinct unique ids. We map each unique + // id to the first instruction it is observed to be associated with so that + // if we encounter a duplicate we have access to the previous instruction - + // this is a useful aid to debugging. + std::unordered_map unique_ids; + bool found_duplicate = false; + ir_context->module()->ForEachInst([&consumer, &found_duplicate, + &unique_ids](opt::Instruction* inst) { + if (unique_ids.count(inst->unique_id()) != 0) { + consumer(SPV_MSG_INFO, nullptr, {}, + "Two instructions have the same unique id (set a breakpoint to " + "inspect)."); + found_duplicate = true; + } + unique_ids.insert({inst->unique_id(), inst}); + }); + return !found_duplicate; +} + +std::unique_ptr CloneIRContext(opt::IRContext* context) { + std::vector binary; + context->module()->ToBinary(&binary, false); + return BuildModule(context->grammar().target_env(), nullptr, binary.data(), + binary.size()); +} + +bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id) { + auto type = ir_context->get_type_mgr()->GetType(id); + return type && !type->AsFunction(); +} + +bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id) { + bool result = false; + ir_context->get_def_use_mgr()->WhileEachUse( + block_id, + [&result](const opt::Instruction* use_instruction, + uint32_t /*unused*/) -> bool { + switch (use_instruction->opcode()) { + case SpvOpLoopMerge: + case SpvOpSelectionMerge: + result = true; + return false; + default: + return true; + } + }); + return result; +} + +uint32_t GetLoopFromMergeBlock(opt::IRContext* ir_context, + uint32_t merge_block_id) { + uint32_t result = 0; + ir_context->get_def_use_mgr()->WhileEachUse( + merge_block_id, + [ir_context, &result](opt::Instruction* use_instruction, + uint32_t use_index) -> bool { + switch (use_instruction->opcode()) { + case SpvOpLoopMerge: + // The merge block operand is the first operand in OpLoopMerge. + if (use_index == 0) { + result = ir_context->get_instr_block(use_instruction)->id(); + return false; + } + return true; + default: + return true; + } + }); + return result; +} + +uint32_t FindFunctionType(opt::IRContext* ir_context, + const std::vector& type_ids) { + // Look through the existing types for a match. + for (auto& type_or_value : ir_context->types_values()) { + if (type_or_value.opcode() != SpvOpTypeFunction) { + // We are only interested in function types. + continue; + } + if (type_or_value.NumInOperands() != type_ids.size()) { + // Not a match: different numbers of arguments. + continue; + } + // Check whether the return type and argument types match. + bool input_operands_match = true; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + if (type_ids[i] != type_or_value.GetSingleWordInOperand(i)) { + input_operands_match = false; + break; + } + } + if (input_operands_match) { + // Everything matches. + return type_or_value.result_id(); + } + } + // No match was found. + return 0; +} + +opt::Instruction* GetFunctionType(opt::IRContext* context, + const opt::Function* function) { + uint32_t type_id = function->DefInst().GetSingleWordInOperand(1); + return context->get_def_use_mgr()->GetDef(type_id); +} + +opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id) { + for (auto& function : *ir_context->module()) { + if (function.result_id() == function_id) { + return &function; + } + } + return nullptr; +} + +bool FunctionContainsOpKillOrUnreachable(const opt::Function& function) { + for (auto& block : function) { + if (block.terminator()->opcode() == SpvOpKill || + block.terminator()->opcode() == SpvOpUnreachable) { + return true; + } + } + return false; +} + +bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id) { + for (auto& entry_point : context->module()->entry_points()) { + if (entry_point.GetSingleWordInOperand(1) == function_id) { + return true; + } + } + return false; +} + +bool IdIsAvailableAtUse(opt::IRContext* context, + opt::Instruction* use_instruction, + uint32_t use_input_operand_index, uint32_t id) { + assert(context->get_instr_block(use_instruction) && + "|use_instruction| must be in a basic block"); + + auto defining_instruction = context->get_def_use_mgr()->GetDef(id); + auto enclosing_function = + context->get_instr_block(use_instruction)->GetParent(); + // If the id a function parameter, it needs to be associated with the + // function containing the use. + if (defining_instruction->opcode() == SpvOpFunctionParameter) { + return InstructionIsFunctionParameter(defining_instruction, + enclosing_function); + } + if (!context->get_instr_block(id)) { + // The id must be at global scope. + return true; + } + if (defining_instruction == use_instruction) { + // It is not OK for a definition to use itself. + return false; + } + auto dominator_analysis = context->GetDominatorAnalysis(enclosing_function); + if (!dominator_analysis->IsReachable( + context->get_instr_block(use_instruction)) || + !dominator_analysis->IsReachable(context->get_instr_block(id))) { + // Skip unreachable blocks. + return false; + } + if (use_instruction->opcode() == SpvOpPhi) { + // In the case where the use is an operand to OpPhi, it is actually the + // *parent* block associated with the operand that must be dominated by + // the synonym. + auto parent_block = + use_instruction->GetSingleWordInOperand(use_input_operand_index + 1); + return dominator_analysis->Dominates( + context->get_instr_block(defining_instruction)->id(), parent_block); + } + return dominator_analysis->Dominates(defining_instruction, use_instruction); +} + +bool IdIsAvailableBeforeInstruction(opt::IRContext* context, + opt::Instruction* instruction, + uint32_t id) { + assert(context->get_instr_block(instruction) && + "|instruction| must be in a basic block"); + + auto id_definition = context->get_def_use_mgr()->GetDef(id); + auto function_enclosing_instruction = + context->get_instr_block(instruction)->GetParent(); + // If the id a function parameter, it needs to be associated with the + // function containing the instruction. + if (id_definition->opcode() == SpvOpFunctionParameter) { + return InstructionIsFunctionParameter(id_definition, + function_enclosing_instruction); + } + if (!context->get_instr_block(id)) { + // The id is at global scope. + return true; + } + if (id_definition == instruction) { + // The instruction is not available right before its own definition. + return false; + } + const auto* dominator_analysis = + context->GetDominatorAnalysis(function_enclosing_instruction); + if (dominator_analysis->IsReachable(context->get_instr_block(instruction)) && + dominator_analysis->IsReachable(context->get_instr_block(id)) && + dominator_analysis->Dominates(id_definition, instruction)) { + // The id's definition dominates the instruction, and both the definition + // and the instruction are in reachable blocks, thus the id is available at + // the instruction. + return true; + } + if (id_definition->opcode() == SpvOpVariable && + function_enclosing_instruction == + context->get_instr_block(id)->GetParent()) { + assert(!dominator_analysis->IsReachable( + context->get_instr_block(instruction)) && + "If the instruction were in a reachable block we should already " + "have returned true."); + // The id is a variable and it is in the same function as |instruction|. + // This is OK despite |instruction| being unreachable. + return true; + } + return false; +} + +bool InstructionIsFunctionParameter(opt::Instruction* instruction, + opt::Function* function) { + if (instruction->opcode() != SpvOpFunctionParameter) { + return false; + } + bool found_parameter = false; + function->ForEachParam( + [instruction, &found_parameter](opt::Instruction* param) { + if (param == instruction) { + found_parameter = true; + } + }); + return found_parameter; +} + +uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id) { + const auto* inst = context->get_def_use_mgr()->GetDef(result_id); + assert(inst && "|result_id| is invalid"); + return inst->type_id(); +} + +uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst) { + assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer && + "Precondition: |pointer_type_inst| must be OpTypePointer."); + return pointer_type_inst->GetSingleWordInOperand(1); +} + +uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id) { + return GetPointeeTypeIdFromPointerType( + context->get_def_use_mgr()->GetDef(pointer_type_id)); +} + +SpvStorageClass GetStorageClassFromPointerType( + opt::Instruction* pointer_type_inst) { + assert(pointer_type_inst && pointer_type_inst->opcode() == SpvOpTypePointer && + "Precondition: |pointer_type_inst| must be OpTypePointer."); + return static_cast( + pointer_type_inst->GetSingleWordInOperand(0)); +} + +SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id) { + return GetStorageClassFromPointerType( + context->get_def_use_mgr()->GetDef(pointer_type_id)); +} + +uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, + SpvStorageClass storage_class) { + for (auto& inst : context->types_values()) { + switch (inst.opcode()) { + case SpvOpTypePointer: + if (inst.GetSingleWordInOperand(0) == storage_class && + inst.GetSingleWordInOperand(1) == pointee_type_id) { + return inst.result_id(); + } + break; + default: + break; + } + } + return 0; +} + +uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, + uint32_t absolute_index) { + // Subtract the number of non-input operands from the index + return absolute_index - inst.NumOperands() + inst.NumInOperands(); +} + +bool IsNullConstantSupported(const opt::analysis::Type& type) { + return type.AsBool() || type.AsInteger() || type.AsFloat() || + type.AsMatrix() || type.AsVector() || type.AsArray() || + type.AsStruct() || type.AsPointer() || type.AsEvent() || + type.AsDeviceEvent() || type.AsReserveId() || type.AsQueue(); +} + +bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( + const opt::IRContext* ir_context) { + // TODO(afd): We capture the universal environments for which this requirement + // holds. The check should be refined on demand for other target + // environments. + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + return false; + default: + return true; + } +} + +void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id) { + if (GlobalVariablesMustBeDeclaredInEntryPointInterfaces(context)) { + // Conservatively add this global to the interface of every entry point in + // the module. This means that the global is available for other + // transformations to use. + // + // A downside of this is that the global will be in the interface even if it + // ends up never being used. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3111) revisit + // this if a more thorough approach to entry point interfaces is taken. + for (auto& entry_point : context->module()->entry_points()) { + entry_point.AddOperand({SPV_OPERAND_TYPE_ID, {id}}); + } + } +} + +void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, SpvStorageClass storage_class, + uint32_t initializer_id) { + // Check various preconditions. + assert(result_id != 0 && "Result id can't be 0"); + + assert((storage_class == SpvStorageClassPrivate || + storage_class == SpvStorageClassWorkgroup) && + "Variable's storage class must be either Private or Workgroup"); + + auto* type_inst = context->get_def_use_mgr()->GetDef(type_id); + (void)type_inst; // Variable becomes unused in release mode. + assert(type_inst && type_inst->opcode() == SpvOpTypePointer && + GetStorageClassFromPointerType(type_inst) == storage_class && + "Variable's type is invalid"); + + if (storage_class == SpvStorageClassWorkgroup) { + assert(initializer_id == 0); + } + + if (initializer_id != 0) { + const auto* constant_inst = + context->get_def_use_mgr()->GetDef(initializer_id); + (void)constant_inst; // Variable becomes unused in release mode. + assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) && + GetPointeeTypeIdFromPointerType(type_inst) == + constant_inst->type_id() && + "Initializer is invalid"); + } + + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_STORAGE_CLASS, {static_cast(storage_class)}}}; + + if (initializer_id) { + operands.push_back({SPV_OPERAND_TYPE_ID, {initializer_id}}); + } + + context->module()->AddGlobalValue(MakeUnique( + context, SpvOpVariable, type_id, result_id, std::move(operands))); + + AddVariableIdToEntryPointInterfaces(context, result_id); + UpdateModuleIdBound(context, result_id); +} + +void AddLocalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, uint32_t function_id, + uint32_t initializer_id) { + // Check various preconditions. + assert(result_id != 0 && "Result id can't be 0"); + + auto* type_inst = context->get_def_use_mgr()->GetDef(type_id); + (void)type_inst; // Variable becomes unused in release mode. + assert(type_inst && type_inst->opcode() == SpvOpTypePointer && + GetStorageClassFromPointerType(type_inst) == SpvStorageClassFunction && + "Variable's type is invalid"); + + const auto* constant_inst = + context->get_def_use_mgr()->GetDef(initializer_id); + (void)constant_inst; // Variable becomes unused in release mode. + assert(constant_inst && spvOpcodeIsConstant(constant_inst->opcode()) && + GetPointeeTypeIdFromPointerType(type_inst) == + constant_inst->type_id() && + "Initializer is invalid"); + + auto* function = FindFunction(context, function_id); + assert(function && "Function id is invalid"); + + function->begin()->begin()->InsertBefore(MakeUnique( + context, SpvOpVariable, type_id, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}, + {SPV_OPERAND_TYPE_ID, {initializer_id}}})); + + UpdateModuleIdBound(context, result_id); +} + +bool HasDuplicates(const std::vector& arr) { + return std::unordered_set(arr.begin(), arr.end()).size() != + arr.size(); +} + +bool IsPermutationOfRange(const std::vector& arr, uint32_t lo, + uint32_t hi) { + if (arr.empty()) { + return lo > hi; + } + + if (HasDuplicates(arr)) { + return false; + } + + auto min_max = std::minmax_element(arr.begin(), arr.end()); + return arr.size() == hi - lo + 1 && *min_max.first == lo && + *min_max.second == hi; +} + +std::vector GetParameters(opt::IRContext* ir_context, + uint32_t function_id) { + auto* function = FindFunction(ir_context, function_id); + assert(function && "|function_id| is invalid"); + + std::vector result; + function->ForEachParam( + [&result](opt::Instruction* inst) { result.push_back(inst); }); + + return result; +} + +void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id) { + auto* function = GetFunctionFromParameterId(ir_context, parameter_id); + assert(function && "|parameter_id| is invalid"); + assert(!FunctionIsEntryPoint(ir_context, function->result_id()) && + "Can't remove parameter from an entry point function"); + + function->RemoveParameter(parameter_id); + + // We've just removed parameters from the function and cleared their memory. + // Make sure analyses have no dangling pointers. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +std::vector GetCallers(opt::IRContext* ir_context, + uint32_t function_id) { + assert(FindFunction(ir_context, function_id) && + "|function_id| is not a result id of a function"); + + std::vector result; + ir_context->get_def_use_mgr()->ForEachUser( + function_id, [&result, function_id](opt::Instruction* inst) { + if (inst->opcode() == SpvOpFunctionCall && + inst->GetSingleWordInOperand(0) == function_id) { + result.push_back(inst); + } + }); + + return result; +} + +opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context, + uint32_t param_id) { + auto* param_inst = ir_context->get_def_use_mgr()->GetDef(param_id); + assert(param_inst && "Parameter id is invalid"); + + for (auto& function : *ir_context->module()) { + if (InstructionIsFunctionParameter(param_inst, &function)) { + return &function; + } + } + + return nullptr; +} + +uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id, + uint32_t new_function_type_result_id, + uint32_t return_type_id, + const std::vector& parameter_type_ids) { + // Check some initial constraints. + assert(ir_context->get_type_mgr()->GetType(return_type_id) && + "Return type is invalid"); + for (auto id : parameter_type_ids) { + const auto* type = ir_context->get_type_mgr()->GetType(id); + (void)type; // Make compilers happy in release mode. + // Parameters can't be OpTypeVoid. + assert(type && !type->AsVoid() && "Parameter has invalid type"); + } + + auto* function = FindFunction(ir_context, function_id); + assert(function && "|function_id| is invalid"); + + auto* old_function_type = GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + std::vector operand_ids = {return_type_id}; + operand_ids.insert(operand_ids.end(), parameter_type_ids.begin(), + parameter_type_ids.end()); + + // A trivial case - we change nothing. + if (FindFunctionType(ir_context, operand_ids) == + old_function_type->result_id()) { + return old_function_type->result_id(); + } + + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1 && + FindFunctionType(ir_context, operand_ids) == 0) { + // We can change |old_function_type| only if it's used once in the module + // and we are certain we won't create a duplicate as a result of the change. + + // Update |old_function_type| in-place. + opt::Instruction::OperandList operands; + for (auto id : operand_ids) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + old_function_type->SetInOperands(std::move(operands)); + + // |operands| may depend on result ids defined below the |old_function_type| + // in the module. + old_function_type->RemoveFromList(); + ir_context->AddType(std::unique_ptr(old_function_type)); + return old_function_type->result_id(); + } else { + // We can't modify the |old_function_type| so we have to either use an + // existing one or create a new one. + auto type_id = FindOrCreateFunctionType( + ir_context, new_function_type_result_id, operand_ids); + assert(type_id != old_function_type->result_id() && + "We should've handled this case above"); + + function->DefInst().SetInOperand(1, {type_id}); + + // DefUseManager hasn't been updated yet, so if the following condition is + // true, then |old_function_type| will have no users when this function + // returns. We might as well remove it. + if (ir_context->get_def_use_mgr()->NumUsers(old_function_type) == 1) { + ir_context->KillInst(old_function_type); + } + + return type_id; + } +} + +void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& type_ids) { + assert(result_id != 0 && "Result id can't be 0"); + assert(!type_ids.empty() && + "OpTypeFunction always has at least one operand - function's return " + "type"); + assert(IsNonFunctionTypeId(ir_context, type_ids[0]) && + "Return type must not be a function"); + + for (size_t i = 1; i < type_ids.size(); ++i) { + const auto* param_type = ir_context->get_type_mgr()->GetType(type_ids[i]); + (void)param_type; // Make compiler happy in release mode. + assert(param_type && !param_type->AsVoid() && !param_type->AsFunction() && + "Function parameter can't have a function or void type"); + } + + opt::Instruction::OperandList operands; + operands.reserve(type_ids.size()); + for (auto id : type_ids) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + ir_context->AddType(MakeUnique( + ir_context, SpvOpTypeFunction, 0, result_id, std::move(operands))); + + UpdateModuleIdBound(ir_context, result_id); +} + +uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context, + uint32_t result_id, + const std::vector& type_ids) { + if (auto existing_id = FindFunctionType(ir_context, type_ids)) { + return existing_id; + } + AddFunctionType(ir_context, result_id, type_ids); + return result_id; +} + +uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, + bool is_signed) { + opt::analysis::Integer type(width, is_signed); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width) { + opt::analysis::Float type(width); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetBoolType(opt::IRContext* ir_context) { + opt::analysis::Bool type; + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetVectorType(opt::IRContext* ir_context, + uint32_t component_type_id, + uint32_t element_count) { + const auto* component_type = + ir_context->get_type_mgr()->GetType(component_type_id); + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(element_count >= 2 && element_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + opt::analysis::Vector type(component_type, element_count); + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetStructType(opt::IRContext* ir_context, + const std::vector& component_type_ids) { + for (auto& type_or_value : ir_context->types_values()) { + if (type_or_value.opcode() != SpvOpTypeStruct || + type_or_value.NumInOperands() != + static_cast(component_type_ids.size())) { + continue; + } + bool all_components_match = true; + for (uint32_t i = 0; i < component_type_ids.size(); i++) { + if (type_or_value.GetSingleWordInOperand(i) != component_type_ids[i]) { + all_components_match = false; + break; + } + } + if (all_components_match) { + return type_or_value.result_id(); + } + } + return 0; +} + +uint32_t MaybeGetVoidType(opt::IRContext* ir_context) { + opt::analysis::Void type; + return ir_context->get_type_mgr()->GetId(&type); +} + +uint32_t MaybeGetZeroConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + uint32_t scalar_or_composite_type_id, bool is_irrelevant) { + const auto* type_inst = + ir_context->get_def_use_mgr()->GetDef(scalar_or_composite_type_id); + assert(type_inst && "|scalar_or_composite_type_id| is invalid"); + + switch (type_inst->opcode()) { + case SpvOpTypeBool: + return MaybeGetBoolConstant(ir_context, transformation_context, false, + is_irrelevant); + case SpvOpTypeFloat: + case SpvOpTypeInt: { + const auto width = type_inst->GetSingleWordInOperand(0); + std::vector words = {0}; + if (width > 32) { + words.push_back(0); + } + + return MaybeGetScalarConstant(ir_context, transformation_context, words, + scalar_or_composite_type_id, is_irrelevant); + } + case SpvOpTypeStruct: { + std::vector component_ids; + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + const auto component_type_id = type_inst->GetSingleWordInOperand(i); + + auto component_id = + MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, is_irrelevant); + + if (component_id == 0 && is_irrelevant) { + // Irrelevant constants can use either relevant or irrelevant + // constituents. + component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, false); + } + + if (component_id == 0) { + return 0; + } + + component_ids.push_back(component_id); + } + + return MaybeGetCompositeConstant( + ir_context, transformation_context, component_ids, + scalar_or_composite_type_id, is_irrelevant); + } + case SpvOpTypeMatrix: + case SpvOpTypeVector: { + const auto component_type_id = type_inst->GetSingleWordInOperand(0); + + auto component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, is_irrelevant); + + if (component_id == 0 && is_irrelevant) { + // Irrelevant constants can use either relevant or irrelevant + // constituents. + component_id = MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, false); + } + + if (component_id == 0) { + return 0; + } + + const auto component_count = type_inst->GetSingleWordInOperand(1); + return MaybeGetCompositeConstant( + ir_context, transformation_context, + std::vector(component_count, component_id), + scalar_or_composite_type_id, is_irrelevant); + } + case SpvOpTypeArray: { + const auto component_type_id = type_inst->GetSingleWordInOperand(0); + + auto component_id = MaybeGetZeroConstant( + ir_context, transformation_context, component_type_id, is_irrelevant); + + if (component_id == 0 && is_irrelevant) { + // Irrelevant constants can use either relevant or irrelevant + // constituents. + component_id = MaybeGetZeroConstant(ir_context, transformation_context, + component_type_id, false); + } + + if (component_id == 0) { + return 0; + } + + return MaybeGetCompositeConstant( + ir_context, transformation_context, + std::vector(GetArraySize(*type_inst, ir_context), + component_id), + scalar_or_composite_type_id, is_irrelevant); + } + default: + assert(false && "Type is not supported"); + return 0; + } +} + +bool CanCreateConstant(opt::IRContext* ir_context, uint32_t type_id) { + opt::Instruction* type_instr = ir_context->get_def_use_mgr()->GetDef(type_id); + assert(type_instr != nullptr && "The type must exist."); + assert(spvOpcodeGeneratesType(type_instr->opcode()) && + "A type-generating opcode was expected."); + switch (type_instr->opcode()) { + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeMatrix: + case SpvOpTypeVector: + return true; + case SpvOpTypeArray: + return CanCreateConstant(ir_context, + type_instr->GetSingleWordInOperand(0)); + case SpvOpTypeStruct: + if (HasBlockOrBufferBlockDecoration(ir_context, type_id)) { + return false; + } + for (uint32_t index = 0; index < type_instr->NumInOperands(); index++) { + if (!CanCreateConstant(ir_context, + type_instr->GetSingleWordInOperand(index))) { + return false; + } + } + return true; + default: + return false; + } +} + +uint32_t MaybeGetScalarConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t scalar_type_id, + bool is_irrelevant) { + const auto* type = ir_context->get_type_mgr()->GetType(scalar_type_id); + assert(type && "|scalar_type_id| is invalid"); + + if (const auto* int_type = type->AsInteger()) { + return MaybeGetIntegerConstant(ir_context, transformation_context, words, + int_type->width(), int_type->IsSigned(), + is_irrelevant); + } else if (const auto* float_type = type->AsFloat()) { + return MaybeGetFloatConstant(ir_context, transformation_context, words, + float_type->width(), is_irrelevant); + } else { + assert(type->AsBool() && words.size() == 1 && + "|scalar_type_id| doesn't represent a scalar type"); + return MaybeGetBoolConstant(ir_context, transformation_context, words[0], + is_irrelevant); + } +} + +uint32_t MaybeGetCompositeConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& component_ids, uint32_t composite_type_id, + bool is_irrelevant) { + const auto* type = ir_context->get_type_mgr()->GetType(composite_type_id); + (void)type; // Make compilers happy in release mode. + assert(IsCompositeType(type) && "|composite_type_id| is invalid"); + + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == SpvOpConstantComposite && + inst.type_id() == composite_type_id && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant && + inst.NumInOperands() == component_ids.size()) { + bool is_match = true; + + for (uint32_t i = 0; i < inst.NumInOperands(); ++i) { + if (inst.GetSingleWordInOperand(i) != component_ids[i]) { + is_match = false; + break; + } + } + + if (is_match) { + return inst.result_id(); + } + } + } + + return 0; +} + +uint32_t MaybeGetIntegerConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant) { + if (auto type_id = MaybeGetIntegerType(ir_context, width, is_signed)) { + return MaybeGetOpConstant(ir_context, transformation_context, words, + type_id, is_irrelevant); + } + + return 0; +} + +uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context, + uint32_t value, + uint32_t int_type_id) { + auto int_type_inst = ir_context->get_def_use_mgr()->GetDef(int_type_id); + + assert(int_type_inst && "The given type id must exist."); + + auto int_type = ir_context->get_type_mgr() + ->GetType(int_type_inst->result_id()) + ->AsInteger(); + + assert(int_type && int_type->width() == 32 && + "The given type id must correspond to an 32-bit integer type."); + + opt::analysis::IntConstant constant(int_type, {value}); + + // Check that the constant exists in the module. + if (!ir_context->get_constant_mgr()->FindConstant(&constant)) { + return 0; + } + + return ir_context->get_constant_mgr() + ->GetDefiningInstruction(&constant) + ->result_id(); +} + +uint32_t MaybeGetFloatConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_irrelevant) { + if (auto type_id = MaybeGetFloatType(ir_context, width)) { + return MaybeGetOpConstant(ir_context, transformation_context, words, + type_id, is_irrelevant); + } + + return 0; +} + +uint32_t MaybeGetBoolConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, bool value, + bool is_irrelevant) { + if (auto type_id = MaybeGetBoolType(ir_context)) { + for (const auto& inst : ir_context->types_values()) { + if (inst.opcode() == (value ? SpvOpConstantTrue : SpvOpConstantFalse) && + inst.type_id() == type_id && + transformation_context.GetFactManager()->IdIsIrrelevant( + inst.result_id()) == is_irrelevant) { + return inst.result_id(); + } + } + } + + return 0; +} + +void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed) { + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeInt, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {is_signed ? 1u : 0u}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width) { + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeFloat, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {width}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, uint32_t element_count) { + const auto* component_type = + ir_context->get_type_mgr()->GetType(component_type_id); + (void)component_type; // Make compiler happy in release mode. + assert(component_type && + (component_type->AsInteger() || component_type->AsFloat() || + component_type->AsBool()) && + "|component_type_id| is invalid"); + assert(element_count >= 2 && element_count <= 4 && + "Precondition: component count must be in range [2, 4]."); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeVector, 0, result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {component_type_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_count}}})); + + UpdateModuleIdBound(ir_context, result_id); +} + +void AddStructType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids) { + opt::Instruction::OperandList operands; + operands.reserve(component_type_ids.size()); + + for (auto type_id : component_type_ids) { + const auto* type = ir_context->get_type_mgr()->GetType(type_id); + (void)type; // Make compiler happy in release mode. + assert(type && !type->AsFunction() && "Component's type id is invalid"); + + if (type->AsStruct()) { + // From the spec for the BuiltIn decoration: + // - When applied to a structure-type member, that structure type cannot + // be contained as a member of another structure type. + assert(!MembersHaveBuiltInDecoration(ir_context, type_id) && + "A member struct has BuiltIn members"); + } + + operands.push_back({SPV_OPERAND_TYPE_ID, {type_id}}); + } + + ir_context->AddType(MakeUnique( + ir_context, SpvOpTypeStruct, 0, result_id, std::move(operands))); + + UpdateModuleIdBound(ir_context, result_id); +} + +std::vector IntToWords(uint64_t value, uint32_t width, + bool is_signed) { + assert(width <= 64 && "The bit width should not be more than 64 bits"); + + // Sign-extend or zero-extend the last |width| bits of |value|, depending on + // |is_signed|. + if (is_signed) { + // Sign-extend by shifting left and then shifting right, interpreting the + // integer as signed. + value = static_cast(value << (64 - width)) >> (64 - width); + } else { + // Zero-extend by shifting left and then shifting right, interpreting the + // integer as unsigned. + value = (value << (64 - width)) >> (64 - width); + } + + std::vector result; + result.push_back(static_cast(value)); + if (width > 32) { + result.push_back(static_cast(value >> 32)); + } + return result; +} + +bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id, + uint32_t type2_id) { + if (type1_id == type2_id) { + return true; + } + + auto type1 = ir_context->get_type_mgr()->GetType(type1_id); + auto type2 = ir_context->get_type_mgr()->GetType(type2_id); + + // Integer scalar types must have the same width + if (type1->AsInteger() && type2->AsInteger()) { + return type1->AsInteger()->width() == type2->AsInteger()->width(); + } + + // Integer vector types must have the same number of components and their + // component types must be integers with the same width. + if (type1->AsVector() && type2->AsVector()) { + auto component_type1 = type1->AsVector()->element_type()->AsInteger(); + auto component_type2 = type2->AsVector()->element_type()->AsInteger(); + + // Only check the component count and width if they are integer. + if (component_type1 && component_type2) { + return type1->AsVector()->element_count() == + type2->AsVector()->element_count() && + component_type1->width() == component_type2->width(); + } + } + + // In all other cases, the types cannot be considered equal. + return false; +} + +std::map RepeatedUInt32PairToMap( + const google::protobuf::RepeatedPtrField& data) { + std::map result; + + for (const auto& entry : data) { + result[entry.first()] = entry.second(); + } + + return result; +} + +google::protobuf::RepeatedPtrField +MapToRepeatedUInt32Pair(const std::map& data) { + google::protobuf::RepeatedPtrField result; + + for (const auto& entry : data) { + protobufs::UInt32Pair pair; + pair.set_first(entry.first); + pair.set_second(entry.second); + *result.Add() = std::move(pair); + } + + return result; +} + +opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context, + uint32_t block_id, + SpvOp opcode) { + // CFG::block uses std::map::at which throws an exception when |block_id| is + // invalid. The error message is unhelpful, though. Thus, we test that + // |block_id| is valid here. + const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id); + (void)label_inst; // Make compilers happy in release mode. + assert(label_inst && label_inst->opcode() == SpvOpLabel && + "|block_id| is invalid"); + + auto* block = ir_context->cfg()->block(block_id); + auto it = block->rbegin(); + assert(it != block->rend() && "Basic block can't be empty"); + + if (block->GetMergeInst()) { + ++it; + assert(it != block->rend() && + "|block| must have at least two instructions:" + "terminator and a merge instruction"); + } + + return CanInsertOpcodeBeforeInstruction(opcode, &*it) ? &*it : nullptr; +} + +bool IdUseCanBeReplaced(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* use_instruction, + uint32_t use_in_operand_index) { + if (spvOpcodeIsAccessChain(use_instruction->opcode()) && + use_in_operand_index > 0) { + // A replacement for an irrelevant index in OpAccessChain must be clamped + // first. + if (transformation_context.GetFactManager()->IdIsIrrelevant( + use_instruction->GetSingleWordInOperand(use_in_operand_index))) { + return false; + } + + // This is an access chain index. If the (sub-)object being accessed by the + // given index has struct type then we cannot replace the use, as it needs + // to be an OpConstant. + + // Get the top-level composite type that is being accessed. + auto object_being_accessed = ir_context->get_def_use_mgr()->GetDef( + use_instruction->GetSingleWordInOperand(0)); + auto pointer_type = + ir_context->get_type_mgr()->GetType(object_being_accessed->type_id()); + assert(pointer_type->AsPointer()); + auto composite_type_being_accessed = + pointer_type->AsPointer()->pointee_type(); + + // Now walk the access chain, tracking the type of each sub-object of the + // composite that is traversed, until the index of interest is reached. + for (uint32_t index_in_operand = 1; index_in_operand < use_in_operand_index; + index_in_operand++) { + // For vectors, matrices and arrays, getting the type of the sub-object is + // trivial. For the struct case, the sub-object type is field-sensitive, + // and depends on the constant index that is used. + if (composite_type_being_accessed->AsVector()) { + composite_type_being_accessed = + composite_type_being_accessed->AsVector()->element_type(); + } else if (composite_type_being_accessed->AsMatrix()) { + composite_type_being_accessed = + composite_type_being_accessed->AsMatrix()->element_type(); + } else if (composite_type_being_accessed->AsArray()) { + composite_type_being_accessed = + composite_type_being_accessed->AsArray()->element_type(); + } else if (composite_type_being_accessed->AsRuntimeArray()) { + composite_type_being_accessed = + composite_type_being_accessed->AsRuntimeArray()->element_type(); + } else { + assert(composite_type_being_accessed->AsStruct()); + auto constant_index_instruction = ir_context->get_def_use_mgr()->GetDef( + use_instruction->GetSingleWordInOperand(index_in_operand)); + assert(constant_index_instruction->opcode() == SpvOpConstant); + uint32_t member_index = + constant_index_instruction->GetSingleWordInOperand(0); + composite_type_being_accessed = + composite_type_being_accessed->AsStruct() + ->element_types()[member_index]; + } + } + + // We have found the composite type being accessed by the index we are + // considering replacing. If it is a struct, then we cannot do the + // replacement as struct indices must be constants. + if (composite_type_being_accessed->AsStruct()) { + return false; + } + } + + if (use_instruction->opcode() == SpvOpFunctionCall && + use_in_operand_index > 0) { + // This is a function call argument. It is not allowed to have pointer + // type. + + // Get the definition of the function being called. + auto function = ir_context->get_def_use_mgr()->GetDef( + use_instruction->GetSingleWordInOperand(0)); + // From the function definition, get the function type. + auto function_type = ir_context->get_def_use_mgr()->GetDef( + function->GetSingleWordInOperand(1)); + // OpTypeFunction's 0-th input operand is the function return type, and the + // function argument types follow. Because the arguments to OpFunctionCall + // start from input operand 1, we can use |use_in_operand_index| to get the + // type associated with this function argument. + auto parameter_type = ir_context->get_type_mgr()->GetType( + function_type->GetSingleWordInOperand(use_in_operand_index)); + if (parameter_type->AsPointer()) { + return false; + } + } + + if (use_instruction->opcode() == SpvOpImageTexelPointer && + use_in_operand_index == 2) { + // The OpImageTexelPointer instruction has a Sample parameter that in some + // situations must be an id for the value 0. To guard against disrupting + // that requirement, we do not replace this argument to that instruction. + return false; + } + + return true; +} + +bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context, + uint32_t struct_type_id) { + const auto* type_inst = ir_context->get_def_use_mgr()->GetDef(struct_type_id); + assert(type_inst && type_inst->opcode() == SpvOpTypeStruct && + "|struct_type_id| is not a result id of an OpTypeStruct"); + + uint32_t builtin_count = 0; + ir_context->get_def_use_mgr()->ForEachUser( + type_inst, + [struct_type_id, &builtin_count](const opt::Instruction* user) { + if (user->opcode() == SpvOpMemberDecorate && + user->GetSingleWordInOperand(0) == struct_type_id && + static_cast(user->GetSingleWordInOperand(2)) == + SpvDecorationBuiltIn) { + ++builtin_count; + } + }); + + assert((builtin_count == 0 || builtin_count == type_inst->NumInOperands()) && + "The module is invalid: either none or all of the members of " + "|struct_type_id| may be builtin"); + + return builtin_count != 0; +} + +bool HasBlockOrBufferBlockDecoration(opt::IRContext* ir_context, uint32_t id) { + for (auto decoration : {SpvDecorationBlock, SpvDecorationBufferBlock}) { + if (!ir_context->get_decoration_mgr()->WhileEachDecoration( + id, decoration, [](const opt::Instruction & /*unused*/) -> bool { + return false; + })) { + return true; + } + } + return false; +} + +bool SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse( + opt::BasicBlock* block_to_split, opt::Instruction* split_before) { + std::set sampled_image_result_ids; + bool before_split = true; + + // Check all the instructions in the block to split. + for (auto& instruction : *block_to_split) { + if (&instruction == &*split_before) { + before_split = false; + } + if (before_split) { + // If the instruction comes before the split and its opcode is + // OpSampledImage, record its result id. + if (instruction.opcode() == SpvOpSampledImage) { + sampled_image_result_ids.insert(instruction.result_id()); + } + } else { + // If the instruction comes after the split, check if ids + // corresponding to OpSampledImage instructions defined before the split + // are used, and return true if they are. + if (!instruction.WhileEachInId( + [&sampled_image_result_ids](uint32_t* id) -> bool { + return !sampled_image_result_ids.count(*id); + })) { + return true; + } + } + } + + // No usage that would be separated from the definition has been found. + return false; +} + +bool InstructionHasNoSideEffects(const opt::Instruction& instruction) { + switch (instruction.opcode()) { + case SpvOpUndef: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpArrayLength: + case SpvOpVectorExtractDynamic: + case SpvOpVectorInsertDynamic: + case SpvOpVectorShuffle: + case SpvOpCompositeConstruct: + case SpvOpCompositeExtract: + case SpvOpCompositeInsert: + case SpvOpCopyObject: + case SpvOpTranspose: + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpUConvert: + case SpvOpSConvert: + case SpvOpFConvert: + case SpvOpQuantizeToF16: + case SpvOpSatConvertSToU: + case SpvOpSatConvertUToS: + case SpvOpBitcast: + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpAny: + case SpvOpAll: + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpLogicalNot: + case SpvOpSelect: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitFieldInsert: + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: + case SpvOpBitReverse: + case SpvOpBitCount: + case SpvOpCopyLogical: + case SpvOpPhi: + case SpvOpPtrEqual: + case SpvOpPtrNotEqual: + return true; + default: + return false; + } +} + +std::set GetReachableReturnBlocks(opt::IRContext* ir_context, + uint32_t function_id) { + auto function = ir_context->GetFunction(function_id); + assert(function && "The function |function_id| must exist."); + + std::set result; + + ir_context->cfg()->ForEachBlockInPostOrder(function->entry().get(), + [&result](opt::BasicBlock* block) { + if (block->IsReturn()) { + result.emplace(block->id()); + } + }); + + return result; +} + +} // namespace fuzzerutil +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/fuzzer_util.h b/third_party/spirv-tools/source/fuzz/fuzzer_util.h new file mode 100644 index 0000000..4e6ec36 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/fuzzer_util.h @@ -0,0 +1,595 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_FUZZER_UTIL_H_ +#define SOURCE_FUZZ_FUZZER_UTIL_H_ + +#include +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Provides types and global utility methods for use by the fuzzer +namespace fuzzerutil { + +// A silent message consumer. +extern const spvtools::MessageConsumer kSilentMessageConsumer; + +// Function type that produces a SPIR-V module. +using ModuleSupplier = std::function()>; + +// Returns true if and only if the module does not define the given id. +bool IsFreshId(opt::IRContext* context, uint32_t id); + +// Updates the module's id bound if needed so that it is large enough to +// account for the given id. +void UpdateModuleIdBound(opt::IRContext* context, uint32_t id); + +// Return the block with id |maybe_block_id| if it exists, and nullptr +// otherwise. +opt::BasicBlock* MaybeFindBlock(opt::IRContext* context, + uint32_t maybe_block_id); + +// When adding an edge from |bb_from| to |bb_to| (which are assumed to be blocks +// in the same function), it is important to supply |bb_to| with ids that can be +// used to augment OpPhi instructions in the case that there is not already such +// an edge. This function returns true if and only if the ids provided in +// |phi_ids| suffice for this purpose, +bool PhiIdsOkForNewEdge( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + const google::protobuf::RepeatedField& phi_ids); + +// Requires that |bool_id| is a valid result id of either OpConstantTrue or +// OpConstantFalse, that PhiIdsOkForNewEdge(context, bb_from, bb_to, phi_ids) +// holds, and that bb_from ends with "OpBranch %some_block". Turns OpBranch +// into "OpBranchConditional |condition_value| ...", such that control will +// branch to %some_block, with |bb_to| being the unreachable alternative. +// Updates OpPhi instructions in |bb_to| using |phi_ids| so that the new edge is +// valid. |condition_value| above is equal to |true| if |bool_id| is a result id +// of an OpConstantTrue instruction. +void AddUnreachableEdgeAndUpdateOpPhis( + opt::IRContext* context, opt::BasicBlock* bb_from, opt::BasicBlock* bb_to, + uint32_t bool_id, + const google::protobuf::RepeatedField& phi_ids); + +// Returns true if and only if |loop_header_id| is a loop header and +// |block_id| is a reachable block branching to and dominated by +// |loop_header_id|. +bool BlockIsBackEdge(opt::IRContext* context, uint32_t block_id, + uint32_t loop_header_id); + +// Returns true if and only if |maybe_loop_header_id| is a loop header and +// |block_id| is in the continue construct of the associated loop. +bool BlockIsInLoopContinueConstruct(opt::IRContext* context, uint32_t block_id, + uint32_t maybe_loop_header_id); + +// If |block| contains |inst|, an iterator for |inst| is returned. +// Otherwise |block|->end() is returned. +opt::BasicBlock::iterator GetIteratorForInstruction( + opt::BasicBlock* block, const opt::Instruction* inst); + +// Returns true if and only if there is a path to |bb| from the entry block of +// the function that contains |bb|. +bool BlockIsReachableInItsFunction(opt::IRContext* context, + opt::BasicBlock* bb); + +// Determines whether it is OK to insert an instruction with opcode |opcode| +// before |instruction_in_block|. +bool CanInsertOpcodeBeforeInstruction( + SpvOp opcode, const opt::BasicBlock::iterator& instruction_in_block); + +// Determines whether it is OK to make a synonym of |inst|. +// |transformation_context| is used to verify that the result id of |inst| +// does not participate in IdIsIrrelevant fact. +bool CanMakeSynonymOf(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* inst); + +// Determines whether the given type is a composite; that is: an array, matrix, +// struct or vector. +bool IsCompositeType(const opt::analysis::Type* type); + +// Returns a vector containing the same elements as |repeated_field|. +std::vector RepeatedFieldToVector( + const google::protobuf::RepeatedField& repeated_field); + +// Given a type id, |base_object_type_id|, returns 0 if the type is not a +// composite type or if |index| is too large to be used as an index into the +// composite. Otherwise returns the type id of the type associated with the +// composite's index. +// +// Example: if |base_object_type_id| is 10, and we have: +// +// %10 = OpTypeStruct %3 %4 %5 +// +// then 3 will be returned if |index| is 0, 5 if |index| is 2, and 0 if index +// is 3 or larger. +uint32_t WalkOneCompositeTypeIndex(opt::IRContext* context, + uint32_t base_object_type_id, + uint32_t index); + +// Given a type id, |base_object_type_id|, checks that the given sequence of +// |indices| is suitable for indexing into this type. Returns the id of the +// type of the final sub-object reached via the indices if they are valid, and +// 0 otherwise. +uint32_t WalkCompositeTypeIndices( + opt::IRContext* context, uint32_t base_object_type_id, + const google::protobuf::RepeatedField& indices); + +// Returns the number of members associated with |struct_type_instruction|, +// which must be an OpStructType instruction. +uint32_t GetNumberOfStructMembers( + const opt::Instruction& struct_type_instruction); + +// Returns the constant size of the array associated with +// |array_type_instruction|, which must be an OpArrayType instruction. Returns +// 0 if there is not a static size. +uint32_t GetArraySize(const opt::Instruction& array_type_instruction, + opt::IRContext* context); + +// Returns the bound for indexing into a composite of type +// |composite_type_inst|, i.e. the number of fields of a struct, the size of an +// array, the number of components of a vector, or the number of columns of a +// matrix. |composite_type_inst| must be the type of a composite. +uint32_t GetBoundForCompositeIndex(const opt::Instruction& composite_type_inst, + opt::IRContext* ir_context); + +// Returns true if and only if |context| is valid, according to the validator +// instantiated with |validator_options|. |consumer| is used for error +// reporting. +bool IsValid(const opt::IRContext* context, + spv_validator_options validator_options, MessageConsumer consumer); + +// Returns true if and only if IsValid(|context|, |validator_options|) holds, +// and furthermore every basic block in |context| has its enclosing function as +// its parent, and every instruction in |context| has a distinct unique id. +// |consumer| is used for error reporting. +bool IsValidAndWellFormed(const opt::IRContext* context, + spv_validator_options validator_options, + MessageConsumer consumer); + +// Returns a clone of |context|, by writing |context| to a binary and then +// parsing it again. +std::unique_ptr CloneIRContext(opt::IRContext* context); + +// Returns true if and only if |id| is the id of a type that is not a function +// type. +bool IsNonFunctionTypeId(opt::IRContext* ir_context, uint32_t id); + +// Returns true if and only if |block_id| is a merge block or continue target +bool IsMergeOrContinue(opt::IRContext* ir_context, uint32_t block_id); + +// Returns the id of the header of the loop corresponding to the given loop +// merge block. Returns 0 if |merge_block_id| is not a loop merge block. +uint32_t GetLoopFromMergeBlock(opt::IRContext* ir_context, + uint32_t merge_block_id); + +// Returns the result id of an instruction of the form: +// %id = OpTypeFunction |type_ids| +// or 0 if no such instruction exists. +uint32_t FindFunctionType(opt::IRContext* ir_context, + const std::vector& type_ids); + +// Returns a type instruction (OpTypeFunction) for |function|. +// Returns |nullptr| if type is not found. +opt::Instruction* GetFunctionType(opt::IRContext* context, + const opt::Function* function); + +// Returns the function with result id |function_id|, or |nullptr| if no such +// function exists. +opt::Function* FindFunction(opt::IRContext* ir_context, uint32_t function_id); + +// Returns true if |function| has a block that the termination instruction is +// OpKill or OpUnreachable. +bool FunctionContainsOpKillOrUnreachable(const opt::Function& function); + +// Returns |true| if one of entry points has function id |function_id|. +bool FunctionIsEntryPoint(opt::IRContext* context, uint32_t function_id); + +// Checks whether |id| is available (according to dominance rules) at the use +// point defined by input operand |use_input_operand_index| of +// |use_instruction|. |use_instruction| must be a in some basic block. +bool IdIsAvailableAtUse(opt::IRContext* context, + opt::Instruction* use_instruction, + uint32_t use_input_operand_index, uint32_t id); + +// Checks whether |id| is available (according to dominance rules) at the +// program point directly before |instruction|. |instruction| must be in some +// basic block. +bool IdIsAvailableBeforeInstruction(opt::IRContext* context, + opt::Instruction* instruction, uint32_t id); + +// Returns true if and only if |instruction| is an OpFunctionParameter +// associated with |function|. +bool InstructionIsFunctionParameter(opt::Instruction* instruction, + opt::Function* function); + +// Returns the type id of the instruction defined by |result_id|, or 0 if there +// is no such result id. +uint32_t GetTypeId(opt::IRContext* context, uint32_t result_id); + +// Given |pointer_type_inst|, which must be an OpTypePointer instruction, +// returns the id of the associated pointee type. +uint32_t GetPointeeTypeIdFromPointerType(opt::Instruction* pointer_type_inst); + +// Given |pointer_type_id|, which must be the id of a pointer type, returns the +// id of the associated pointee type. +uint32_t GetPointeeTypeIdFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id); + +// Given |pointer_type_inst|, which must be an OpTypePointer instruction, +// returns the associated storage class. +SpvStorageClass GetStorageClassFromPointerType( + opt::Instruction* pointer_type_inst); + +// Given |pointer_type_id|, which must be the id of a pointer type, returns the +// associated storage class. +SpvStorageClass GetStorageClassFromPointerType(opt::IRContext* context, + uint32_t pointer_type_id); + +// Returns the id of a pointer with pointee type |pointee_type_id| and storage +// class |storage_class|, if it exists, and 0 otherwise. +uint32_t MaybeGetPointerType(opt::IRContext* context, uint32_t pointee_type_id, + SpvStorageClass storage_class); + +// Given an instruction |inst| and an operand absolute index |absolute_index|, +// returns the index of the operand restricted to the input operands. +uint32_t InOperandIndexFromOperandIndex(const opt::Instruction& inst, + uint32_t absolute_index); + +// Returns true if and only if |type| is one of the types for which it is legal +// to have an OpConstantNull value. +bool IsNullConstantSupported(const opt::analysis::Type& type); + +// Returns true if and only if the SPIR-V version being used requires that +// global variables accessed in the static call graph of an entry point need +// to be listed in that entry point's interface. +bool GlobalVariablesMustBeDeclaredInEntryPointInterfaces( + const opt::IRContext* context); + +// Adds |id| into the interface of every entry point of the shader. +// Does nothing if SPIR-V doesn't require global variables, that are accessed +// from an entry point function, to be listed in that function's interface. +void AddVariableIdToEntryPointInterfaces(opt::IRContext* context, uint32_t id); + +// Adds a global variable with storage class |storage_class| to the module, with +// type |type_id| and either no initializer or |initializer_id| as an +// initializer, depending on whether |initializer_id| is 0. The global variable +// has result id |result_id|. Updates module's id bound to accommodate for +// |result_id|. +// +// - |type_id| must be the id of a pointer type with the same storage class as +// |storage_class|. +// - |storage_class| must be Private or Workgroup. +// - |initializer_id| must be 0 if |storage_class| is Workgroup, and otherwise +// may either be 0 or the id of a constant whose type is the pointee type of +// |type_id|. +void AddGlobalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, SpvStorageClass storage_class, + uint32_t initializer_id); + +// Adds an instruction to the start of |function_id|, of the form: +// |result_id| = OpVariable |type_id| Function |initializer_id|. +// Updates module's id bound to accommodate for |result_id|. +// +// - |type_id| must be the id of a pointer type with Function storage class. +// - |initializer_id| must be the id of a constant with the same type as the +// pointer's pointee type. +// - |function_id| must be the id of a function. +void AddLocalVariable(opt::IRContext* context, uint32_t result_id, + uint32_t type_id, uint32_t function_id, + uint32_t initializer_id); + +// Returns true if the vector |arr| has duplicates. +bool HasDuplicates(const std::vector& arr); + +// Checks that the given vector |arr| contains a permutation of a range +// [lo, hi]. That being said, all elements in the range are present without +// duplicates. If |arr| is empty, returns true iff |lo > hi|. +bool IsPermutationOfRange(const std::vector& arr, uint32_t lo, + uint32_t hi); + +// Returns OpFunctionParameter instructions corresponding to the function +// with result id |function_id|. +std::vector GetParameters(opt::IRContext* ir_context, + uint32_t function_id); + +// Removes an OpFunctionParameter instruction with result id |parameter_id| +// from the its function. Parameter's function must not be an entry-point +// function. The function must have a parameter with result id |parameter_id|. +// +// Prefer using this function to opt::Function::RemoveParameter since +// this function also guarantees that |ir_context| has no invalid pointers +// to the removed parameter. +void RemoveParameter(opt::IRContext* ir_context, uint32_t parameter_id); + +// Returns all OpFunctionCall instructions that call a function with result id +// |function_id|. +std::vector GetCallers(opt::IRContext* ir_context, + uint32_t function_id); + +// Returns a function that contains OpFunctionParameter instruction with result +// id |param_id|. Returns nullptr if the module has no such function. +opt::Function* GetFunctionFromParameterId(opt::IRContext* ir_context, + uint32_t param_id); + +// Changes the type of function |function_id| so that its return type is +// |return_type_id| and its parameters' types are |parameter_type_ids|. If a +// suitable function type already exists in the module, it is used, otherwise +// |new_function_type_result_id| is used as the result id of a suitable new +// function type instruction. If the old type of the function doesn't have any +// more users, it is removed from the module. Returns the result id of the +// OpTypeFunction instruction that is used as a type of the function with +// |function_id|. +// +// CAUTION: When the old type of the function is removed from the module, its +// memory is deallocated. Be sure not to use any pointers to the old +// type when this function returns. +uint32_t UpdateFunctionType(opt::IRContext* ir_context, uint32_t function_id, + uint32_t new_function_type_result_id, + uint32_t return_type_id, + const std::vector& parameter_type_ids); + +// Creates new OpTypeFunction instruction in the module. |type_ids| may not be +// empty. It may not contain result ids of OpTypeFunction instructions. +// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|. +// |result_id| may not equal to 0. Updates module's id bound to accommodate for +// |result_id|. +void AddFunctionType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& type_ids); + +// Returns a result id of an OpTypeFunction instruction in the module. Creates a +// new instruction if required and returns |result_id|. type_ids| may not be +// empty. It may not contain result ids of OpTypeFunction instructions. +// |type_ids[i]| may not be a result id of OpTypeVoid instruction for |i >= 1|. +// |result_id| must not be equal to 0. Updates module's id bound to accommodate +// for |result_id|. +uint32_t FindOrCreateFunctionType(opt::IRContext* ir_context, + uint32_t result_id, + const std::vector& type_ids); + +// Returns a result id of an OpTypeInt instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetIntegerType(opt::IRContext* ir_context, uint32_t width, + bool is_signed); + +// Returns a result id of an OpTypeFloat instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetFloatType(opt::IRContext* ir_context, uint32_t width); + +// Returns a result id of an OpTypeBool instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetBoolType(opt::IRContext* ir_context); + +// Returns a result id of an OpTypeVector instruction if present. Returns 0 +// otherwise. |component_type_id| must be a valid result id of an OpTypeInt, +// OpTypeFloat or OpTypeBool instruction in the module. |element_count| must be +// in the range [2, 4]. +uint32_t MaybeGetVectorType(opt::IRContext* ir_context, + uint32_t component_type_id, uint32_t element_count); + +// Returns a result id of an OpTypeStruct instruction whose field types exactly +// match |component_type_ids| if such an instruction is present. Returns 0 +// otherwise. |component_type_ids| may not contain a result id of an +// OpTypeFunction. +uint32_t MaybeGetStructType(opt::IRContext* ir_context, + const std::vector& component_type_ids); + +// Returns a result id of an OpTypeVoid instruction if present. Returns 0 +// otherwise. +uint32_t MaybeGetVoidType(opt::IRContext* ir_context); + +// Recursive definition is the following: +// - if |scalar_or_composite_type_id| is a result id of a scalar type - returns +// a result id of the following constants (depending on the type): int -> 0, +// float -> 0.0, bool -> false. +// - otherwise, returns a result id of an OpConstantComposite instruction. +// Every component of the composite constant is looked up by calling this +// function with the type id of that component. +// Returns 0 if no such instruction is present in the module. +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetZeroConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + uint32_t scalar_or_composite_type_id, bool is_irrelevant); + +// Returns true if it is possible to create an OpConstant or an +// OpConstantComposite instruction of type |type_id|. That is, returns true if +// the type associated with |type_id| and all its constituents are either scalar +// or composite. +bool CanCreateConstant(opt::IRContext* ir_context, uint32_t type_id); + +// Returns the result id of an OpConstant instruction. |scalar_type_id| must be +// a result id of a scalar type (i.e. int, float or bool). Returns 0 if no such +// instruction is present in the module. The returned id either participates in +// IdIsIrrelevant fact or not, depending on the |is_irrelevant| parameter. +uint32_t MaybeGetScalarConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t scalar_type_id, + bool is_irrelevant); + +// Returns the result id of an OpConstantComposite instruction. +// |composite_type_id| must be a result id of a composite type (i.e. vector, +// matrix, struct or array). Returns 0 if no such instruction is present in the +// module. The returned id either participates in IdIsIrrelevant fact or not, +// depending on the |is_irrelevant| parameter. +uint32_t MaybeGetCompositeConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& component_ids, uint32_t composite_type_id, + bool is_irrelevant); + +// Returns the result id of an OpConstant instruction of integral type. +// Returns 0 if no such instruction or type is present in the module. +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetIntegerConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_signed, + bool is_irrelevant); + +// Returns the id of a 32-bit integer constant in the module with type +// |int_type_id| and value |value|, or 0 if no such constant exists in the +// module. |int_type_id| must exist in the module and it must correspond to a +// 32-bit integer type. +uint32_t MaybeGetIntegerConstantFromValueAndType(opt::IRContext* ir_context, + uint32_t value, + uint32_t int_type_id); + +// Returns the result id of an OpConstant instruction of floating-point type. +// Returns 0 if no such instruction or type is present in the module. +// The returned id either participates in IdIsIrrelevant fact or not, depending +// on the |is_irrelevant| parameter. +uint32_t MaybeGetFloatConstant( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const std::vector& words, uint32_t width, bool is_irrelevant); + +// Returns the id of a boolean constant with value |value| if it exists in the +// module, or 0 otherwise. The returned id either participates in IdIsIrrelevant +// fact or not, depending on the |is_irrelevant| parameter. +uint32_t MaybeGetBoolConstant( + opt::IRContext* context, + const TransformationContext& transformation_context, bool value, + bool is_irrelevant); + +// Creates a new OpTypeInt instruction in the module. Updates module's id bound +// to accommodate for |result_id|. +void AddIntegerType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width, bool is_signed); + +// Creates a new OpTypeFloat instruction in the module. Updates module's id +// bound to accommodate for |result_id|. +void AddFloatType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t width); + +// Creates a new OpTypeVector instruction in the module. |component_type_id| +// must be a valid result id of an OpTypeInt, OpTypeFloat or OpTypeBool +// instruction in the module. |element_count| must be in the range [2, 4]. +// Updates module's id bound to accommodate for |result_id|. +void AddVectorType(opt::IRContext* ir_context, uint32_t result_id, + uint32_t component_type_id, uint32_t element_count); + +// Creates a new OpTypeStruct instruction in the module. Updates module's id +// bound to accommodate for |result_id|. |component_type_ids| may not contain +// a result id of an OpTypeFunction. if |component_type_ids| contains a result +// of an OpTypeStruct instruction, that struct may not have BuiltIn members. +void AddStructType(opt::IRContext* ir_context, uint32_t result_id, + const std::vector& component_type_ids); + +// Returns a vector of words representing the integer |value|, only considering +// the last |width| bits. The last |width| bits are sign-extended if the value +// is signed, zero-extended if it is unsigned. +// |width| must be <= 64. +// If |width| <= 32, returns a vector containing one value. If |width| > 64, +// returns a vector containing two values, with the first one representing the +// lower-order word of the value and the second one representing the +// higher-order word. +std::vector IntToWords(uint64_t value, uint32_t width, + bool is_signed); + +// Returns a bit pattern that represents a floating-point |value|. +inline uint32_t FloatToWord(float value) { + uint32_t result; + memcpy(&result, &value, sizeof(uint32_t)); + return result; +} + +// Returns true if any of the following is true: +// - |type1_id| and |type2_id| are the same id +// - |type1_id| and |type2_id| refer to integer scalar or vector types, only +// differing by their signedness. +bool TypesAreEqualUpToSign(opt::IRContext* ir_context, uint32_t type1_id, + uint32_t type2_id); + +// Converts repeated field of UInt32Pair to a map. If two or more equal values +// of |UInt32Pair::first()| are available in |data|, the last value of +// |UInt32Pair::second()| is used. +std::map RepeatedUInt32PairToMap( + const google::protobuf::RepeatedPtrField& data); + +// Converts a map into a repeated field of UInt32Pair. +google::protobuf::RepeatedPtrField +MapToRepeatedUInt32Pair(const std::map& data); + +// Returns the last instruction in |block_id| before which an instruction with +// opcode |opcode| can be inserted, or nullptr if there is no such instruction. +opt::Instruction* GetLastInsertBeforeInstruction(opt::IRContext* ir_context, + uint32_t block_id, + SpvOp opcode); + +// Checks whether various conditions hold related to the acceptability of +// replacing the id use at |use_in_operand_index| of |use_instruction| with a +// synonym or another id of appropriate type if the original id is irrelevant. +// In particular, this checks that: +// - If id use is an index of an irrelevant id (|use_in_operand_index > 0|) +// in OpAccessChain - it can't be replaced. +// - The id use is not an index into a struct field in an OpAccessChain - such +// indices must be constants, so it is dangerous to replace them. +// - The id use is not a pointer function call argument, on which there are +// restrictions that make replacement problematic. +// - The id use is not the Sample parameter of an OpImageTexelPointer +// instruction, as this must satisfy particular requirements. +bool IdUseCanBeReplaced(opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* use_instruction, + uint32_t use_in_operand_index); + +// Requires that |struct_type_id| is the id of a struct type, and (as per the +// SPIR-V spec) that either all or none of the members of |struct_type_id| have +// the BuiltIn decoration. Returns true if and only if all members have the +// BuiltIn decoration. +bool MembersHaveBuiltInDecoration(opt::IRContext* ir_context, + uint32_t struct_type_id); + +// Returns true if and only if |id| is decorated with either Block or +// BufferBlock. Even though these decorations are only allowed on struct types, +// for convenience |id| can be any result id so that it is possible to call this +// method on something that *might* be a struct type. +bool HasBlockOrBufferBlockDecoration(opt::IRContext* ir_context, uint32_t id); + +// Returns true iff splitting block |block_to_split| just before the instruction +// |split_before| would separate an OpSampledImage instruction from its usage. +bool SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse( + opt::BasicBlock* block_to_split, opt::Instruction* split_before); + +// Returns true if the instruction given has no side effects. +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3758): Add any +// missing instructions to the list. In particular, GLSL extended instructions +// (called using OpExtInst) have not been considered. +bool InstructionHasNoSideEffects(const opt::Instruction& instruction); + +// Returns a set of the ids of all the return blocks that are reachable from +// the entry block of |function_id|. +// Assumes that the function exists in the module. +std::set GetReachableReturnBlocks(opt::IRContext* ir_context, + uint32_t function_id); + +} // namespace fuzzerutil +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_FUZZER_UTIL_H_ diff --git a/third_party/spirv-tools/source/fuzz/id_use_descriptor.cpp b/third_party/spirv-tools/source/fuzz/id_use_descriptor.cpp new file mode 100644 index 0000000..4e10146 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/id_use_descriptor.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/id_use_descriptor.h" + +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +opt::Instruction* FindInstructionContainingUse( + const protobufs::IdUseDescriptor& id_use_descriptor, + opt::IRContext* context) { + auto result = + FindInstruction(id_use_descriptor.enclosing_instruction(), context); + if (!result) { + return nullptr; + } + if (id_use_descriptor.in_operand_index() >= result->NumInOperands()) { + return nullptr; + } + if (result->GetSingleWordInOperand(id_use_descriptor.in_operand_index()) != + id_use_descriptor.id_of_interest()) { + return nullptr; + } + return result; +} + +protobufs::IdUseDescriptor MakeIdUseDescriptor( + uint32_t id_of_interest, + const protobufs::InstructionDescriptor& enclosing_instruction, + uint32_t in_operand_index) { + protobufs::IdUseDescriptor result; + result.set_id_of_interest(id_of_interest); + *result.mutable_enclosing_instruction() = enclosing_instruction; + result.set_in_operand_index(in_operand_index); + return result; +} + +protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse( + opt::IRContext* context, opt::Instruction* inst, + uint32_t in_operand_index) { + const auto& in_operand = inst->GetInOperand(in_operand_index); + assert(spvIsInIdType(in_operand.type)); + return MakeIdUseDescriptor(in_operand.words[0], + MakeInstructionDescriptor(context, inst), + in_operand_index); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/id_use_descriptor.h b/third_party/spirv-tools/source/fuzz/id_use_descriptor.h new file mode 100644 index 0000000..d18bb66 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/id_use_descriptor.h @@ -0,0 +1,47 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ +#define SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Looks for an instruction in |context| that contains a use +// identified by |id_use_descriptor|. +// Returns |nullptr| if no such instruction can be found. +opt::Instruction* FindInstructionContainingUse( + const protobufs::IdUseDescriptor& id_use_descriptor, + opt::IRContext* context); + +// Creates an IdUseDescriptor protobuf message from the given components. +// See the protobuf definition for details of what these components mean. +protobufs::IdUseDescriptor MakeIdUseDescriptor( + uint32_t id_of_interest, + const protobufs::InstructionDescriptor& enclosing_instruction, + uint32_t in_operand_index); + +// Given an id use, represented by the instruction |inst| that uses the id, and +// the input operand index |in_operand_index| associated with the usage, returns +// an IdUseDescriptor that represents the use. +protobufs::IdUseDescriptor MakeIdUseDescriptorFromUse( + opt::IRContext* context, opt::Instruction* inst, uint32_t in_operand_index); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_ID_USE_DESCRIPTOR_H_ diff --git a/third_party/spirv-tools/source/fuzz/instruction_descriptor.cpp b/third_party/spirv-tools/source/fuzz/instruction_descriptor.cpp new file mode 100644 index 0000000..c0cc5e5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/instruction_descriptor.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +opt::Instruction* FindInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor, + spvtools::opt::IRContext* context) { + for (auto& function : *context->module()) { + for (auto& block : function) { + bool found_base = + block.id() == instruction_descriptor.base_instruction_result_id(); + uint32_t num_ignored = 0; + for (auto& instruction : block) { + if (instruction.HasResultId() && + instruction.result_id() == + instruction_descriptor.base_instruction_result_id()) { + assert(!found_base && + "It should not be possible to find the base instruction " + "multiple times."); + found_base = true; + assert(num_ignored == 0 && + "The skipped instruction count should only be incremented " + "after the instruction base has been found."); + } + if (found_base && + instruction.opcode() == + instruction_descriptor.target_instruction_opcode()) { + if (num_ignored == instruction_descriptor.num_opcodes_to_ignore()) { + return &instruction; + } + num_ignored++; + } + } + if (found_base) { + // We found the base instruction, but did not find the target + // instruction in the same block. + return nullptr; + } + } + } + return nullptr; +} + +protobufs::InstructionDescriptor MakeInstructionDescriptor( + uint32_t base_instruction_result_id, SpvOp target_instruction_opcode, + uint32_t num_opcodes_to_ignore) { + protobufs::InstructionDescriptor result; + result.set_base_instruction_result_id(base_instruction_result_id); + result.set_target_instruction_opcode(target_instruction_opcode); + result.set_num_opcodes_to_ignore(num_opcodes_to_ignore); + return result; +} + +protobufs::InstructionDescriptor MakeInstructionDescriptor( + const opt::BasicBlock& block, + const opt::BasicBlock::const_iterator& inst_it) { + const SpvOp opcode = + inst_it->opcode(); // The opcode of the instruction being described. + uint32_t skip_count = 0; // The number of these opcodes we have skipped when + // searching backwards. + + // Consider instructions in the block in reverse order, starting from + // |inst_it|. + for (opt::BasicBlock::const_iterator backwards_iterator = inst_it;; + --backwards_iterator) { + if (backwards_iterator->HasResultId()) { + // As soon as we find an instruction with a result id, we can return a + // descriptor for |inst_it|. + return MakeInstructionDescriptor(backwards_iterator->result_id(), opcode, + skip_count); + } + if (backwards_iterator != inst_it && + backwards_iterator->opcode() == opcode) { + // We are skipping over an instruction with the same opcode as |inst_it|; + // we increase our skip count to reflect this. + skip_count++; + } + if (backwards_iterator == block.begin()) { + // We exit the loop when we reach the start of the block, but only after + // we have processed the first instruction in the block. + break; + } + } + // We did not find an instruction inside the block with a result id, so we use + // the block's label's id. + return MakeInstructionDescriptor(block.id(), opcode, skip_count); +} + +protobufs::InstructionDescriptor MakeInstructionDescriptor( + opt::IRContext* context, opt::Instruction* inst) { + auto block = context->get_instr_block(inst); + uint32_t base_instruction_result_id = block->id(); + uint32_t num_opcodes_to_ignore = 0; + for (auto& inst_in_block : *block) { + if (inst_in_block.HasResultId()) { + base_instruction_result_id = inst_in_block.result_id(); + num_opcodes_to_ignore = 0; + } + if (&inst_in_block == inst) { + return MakeInstructionDescriptor(base_instruction_result_id, + inst->opcode(), num_opcodes_to_ignore); + } + if (inst_in_block.opcode() == inst->opcode()) { + num_opcodes_to_ignore++; + } + } + assert(false && "No matching instruction was found."); + return protobufs::InstructionDescriptor(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/instruction_descriptor.h b/third_party/spirv-tools/source/fuzz/instruction_descriptor.h new file mode 100644 index 0000000..2ccd15a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/instruction_descriptor.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_ +#define SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Looks for an instruction in |context| corresponding to |descriptor|. +// Returns |nullptr| if no such instruction can be found. +opt::Instruction* FindInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor, + opt::IRContext* context); + +// Creates an InstructionDescriptor protobuf message from the given +// components. See the protobuf definition for details of what these +// components mean. +protobufs::InstructionDescriptor MakeInstructionDescriptor( + uint32_t base_instruction_result_id, SpvOp target_instruction_opcode, + uint32_t num_opcodes_to_ignore); + +// Returns an instruction descriptor that describing the instruction at +// |inst_it|, which must be inside |block|. The descriptor will be with +// respect to the first instruction at or before |inst_it| that has a result +// id. +protobufs::InstructionDescriptor MakeInstructionDescriptor( + const opt::BasicBlock& block, + const opt::BasicBlock::const_iterator& inst_it); + +// Returns an InstructionDescriptor that describes the given instruction |inst|. +protobufs::InstructionDescriptor MakeInstructionDescriptor( + opt::IRContext* context, opt::Instruction* inst); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_INSTRUCTION_DESCRIPTOR_H_ diff --git a/third_party/spirv-tools/source/fuzz/instruction_message.cpp b/third_party/spirv-tools/source/fuzz/instruction_message.cpp new file mode 100644 index 0000000..9503932 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/instruction_message.cpp @@ -0,0 +1,80 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/instruction_message.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +protobufs::Instruction MakeInstructionMessage( + SpvOp opcode, uint32_t result_type_id, uint32_t result_id, + const opt::Instruction::OperandList& input_operands) { + protobufs::Instruction result; + result.set_opcode(opcode); + result.set_result_type_id(result_type_id); + result.set_result_id(result_id); + for (auto& operand : input_operands) { + auto operand_message = result.add_input_operand(); + operand_message->set_operand_type(static_cast(operand.type)); + for (auto operand_word : operand.words) { + operand_message->add_operand_data(operand_word); + } + } + return result; +} + +protobufs::Instruction MakeInstructionMessage( + const opt::Instruction* instruction) { + opt::Instruction::OperandList input_operands; + for (uint32_t input_operand_index = 0; + input_operand_index < instruction->NumInOperands(); + input_operand_index++) { + input_operands.push_back(instruction->GetInOperand(input_operand_index)); + } + return MakeInstructionMessage(instruction->opcode(), instruction->type_id(), + instruction->result_id(), input_operands); +} + +std::unique_ptr InstructionFromMessage( + opt::IRContext* ir_context, + const protobufs::Instruction& instruction_message) { + // First, update the module's id bound with respect to the new instruction, + // if it has a result id. + if (instruction_message.result_id()) { + fuzzerutil::UpdateModuleIdBound(ir_context, + instruction_message.result_id()); + } + // Now create a sequence of input operands from the input operand data in the + // protobuf message. + opt::Instruction::OperandList in_operands; + for (auto& operand_message : instruction_message.input_operand()) { + opt::Operand::OperandData operand_data; + for (auto& word : operand_message.operand_data()) { + operand_data.push_back(word); + } + in_operands.push_back( + {static_cast(operand_message.operand_type()), + operand_data}); + } + // Create and return the instruction. + return MakeUnique( + ir_context, static_cast(instruction_message.opcode()), + instruction_message.result_type_id(), instruction_message.result_id(), + in_operands); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/instruction_message.h b/third_party/spirv-tools/source/fuzz/instruction_message.h new file mode 100644 index 0000000..fcbb4c7 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/instruction_message.h @@ -0,0 +1,47 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_ +#define SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Creates an Instruction protobuf message from its component parts. +protobufs::Instruction MakeInstructionMessage( + SpvOp opcode, uint32_t result_type_id, uint32_t result_id, + const opt::Instruction::OperandList& input_operands); + +// Creates an Instruction protobuf message from a parsed instruction. +protobufs::Instruction MakeInstructionMessage( + const opt::Instruction* instruction); + +// Creates and returns an opt::Instruction from protobuf message +// |instruction_message|, relative to |ir_context|. In the process, the module +// id bound associated with |ir_context| is updated to be at least as large as +// the result id (if any) associated with the new instruction. +std::unique_ptr InstructionFromMessage( + opt::IRContext* ir_context, + const protobufs::Instruction& instruction_message); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_INSTRUCTION_MESSAGE_H_ diff --git a/third_party/spirv-tools/source/fuzz/overflow_id_source.cpp b/third_party/spirv-tools/source/fuzz/overflow_id_source.cpp new file mode 100644 index 0000000..d900455 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/overflow_id_source.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/overflow_id_source.h" + +namespace spvtools { +namespace fuzz { + +OverflowIdSource::~OverflowIdSource() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/overflow_id_source.h b/third_party/spirv-tools/source/fuzz/overflow_id_source.h new file mode 100644 index 0000000..cbf399a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/overflow_id_source.h @@ -0,0 +1,111 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_ +#define SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_ + +#include +#include + +namespace spvtools { +namespace fuzz { + +// An implementation of this interface can be used to provide fresh ids on +// demand when applying a transformation. +// +// During fuzzing this should never be required: a fuzzer pass should determine +// all the fresh ids it requires to apply a transformation. +// +// However, during shrinking we can have the situation where, after removing +// an early transformation, a later transformation needs more ids. +// +// As an example, suppose a SPIR-V function originally has this form: +// +// main() { +// stmt1; +// stmt2; +// stmt3; +// stmt4; +// } +// +// Now suppose two *outlining* transformations are applied. The first +// transformation, T1, outlines "stmt1; stmt2;" into a function foo, giving us: +// +// foo() { +// stmt1; +// stmt2; +// } +// +// main() { +// foo(); +// stmt3; +// stmt4; +// } +// +// The second transformation, T2, outlines "foo(); stmt3;" from main into a +// function bar, giving us: +// +// foo() { +// stmt1; +// stmt2; +// } +// +// bar() { +// foo(); +// stmt3; +// } +// +// main() { +// bar(); +// stmt4; +// } +// +// Suppose that T2 used a set of fresh ids, FRESH, in order to perform its +// outlining. +// +// Now suppose that during shrinking we remove T1, but still want to apply T2. +// The fresh ids used by T2 - FRESH - are sufficient to outline "foo(); stmt3;". +// However, because we did not apply T1, "foo();" does not exist and instead the +// task of T2 is to outline "stmt1; stmt2; stmt3;". The set FRESH contains +// *some* of the fresh ids required to do this (those for "stmt3;"), but not all +// of them (those for "stmt1; stmt2;" are missing). +// +// A source of overflow ids can be used to allow the shrinker to proceed +// nevertheless. +// +// It is desirable to use overflow ids only when needed. In our worked example, +// T2 should still use the ids from FRESH when handling "stmt3;", because later +// transformations might refer to those ids and will become inapplicable if +// overflow ids are used instead. +class OverflowIdSource { + public: + virtual ~OverflowIdSource(); + + // Returns true if and only if this source is capable of providing overflow + // ids. + virtual bool HasOverflowIds() const = 0; + + // Precondition: HasOverflowIds() must hold. Returns the next available + // overflow id. + virtual uint32_t GetNextOverflowId() = 0; + + // Returns the set of overflow ids from this source that have been previously + // issued via calls to GetNextOverflowId(). + virtual const std::unordered_set& GetIssuedOverflowIds() const = 0; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_OVERFLOW_ID_SOURCE_H_ diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h new file mode 100644 index 0000000..80ac087 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_instances.h @@ -0,0 +1,187 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_ +#define SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_ + +#include "source/fuzz/fuzzer_pass_add_access_chains.h" +#include "source/fuzz/fuzzer_pass_add_bit_instruction_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_composite_extract.h" +#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" +#include "source/fuzz/fuzzer_pass_add_composite_types.h" +#include "source/fuzz/fuzzer_pass_add_copy_memory.h" +#include "source/fuzz/fuzzer_pass_add_dead_blocks.h" +#include "source/fuzz/fuzzer_pass_add_dead_breaks.h" +#include "source/fuzz/fuzzer_pass_add_dead_continues.h" +#include "source/fuzz/fuzzer_pass_add_equation_instructions.h" +#include "source/fuzz/fuzzer_pass_add_function_calls.h" +#include "source/fuzz/fuzzer_pass_add_global_variables.h" +#include "source/fuzz/fuzzer_pass_add_image_sample_unused_components.h" +#include "source/fuzz/fuzzer_pass_add_loads.h" +#include "source/fuzz/fuzzer_pass_add_local_variables.h" +#include "source/fuzz/fuzzer_pass_add_loop_preheaders.h" +#include "source/fuzz/fuzzer_pass_add_loops_to_create_int_constant_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_parameters.h" +#include "source/fuzz/fuzzer_pass_add_relaxed_decorations.h" +#include "source/fuzz/fuzzer_pass_add_stores.h" +#include "source/fuzz/fuzzer_pass_add_synonyms.h" +#include "source/fuzz/fuzzer_pass_add_vector_shuffle_instructions.h" +#include "source/fuzz/fuzzer_pass_apply_id_synonyms.h" +#include "source/fuzz/fuzzer_pass_construct_composites.h" +#include "source/fuzz/fuzzer_pass_copy_objects.h" +#include "source/fuzz/fuzzer_pass_donate_modules.h" +#include "source/fuzz/fuzzer_pass_duplicate_regions_with_selections.h" +#include "source/fuzz/fuzzer_pass_expand_vector_reductions.h" +#include "source/fuzz/fuzzer_pass_flatten_conditional_branches.h" +#include "source/fuzz/fuzzer_pass_inline_functions.h" +#include "source/fuzz/fuzzer_pass_invert_comparison_operators.h" +#include "source/fuzz/fuzzer_pass_make_vector_operations_dynamic.h" +#include "source/fuzz/fuzzer_pass_merge_blocks.h" +#include "source/fuzz/fuzzer_pass_merge_function_returns.h" +#include "source/fuzz/fuzzer_pass_mutate_pointers.h" +#include "source/fuzz/fuzzer_pass_obfuscate_constants.h" +#include "source/fuzz/fuzzer_pass_outline_functions.h" +#include "source/fuzz/fuzzer_pass_permute_blocks.h" +#include "source/fuzz/fuzzer_pass_permute_function_parameters.h" +#include "source/fuzz/fuzzer_pass_permute_instructions.h" +#include "source/fuzz/fuzzer_pass_propagate_instructions_down.h" +#include "source/fuzz/fuzzer_pass_propagate_instructions_up.h" +#include "source/fuzz/fuzzer_pass_push_ids_through_variables.h" +#include "source/fuzz/fuzzer_pass_replace_adds_subs_muls_with_carrying_extended.h" +#include "source/fuzz/fuzzer_pass_replace_branches_from_dead_blocks_with_exits.h" +#include "source/fuzz/fuzzer_pass_replace_copy_memories_with_loads_stores.h" +#include "source/fuzz/fuzzer_pass_replace_copy_objects_with_stores_loads.h" +#include "source/fuzz/fuzzer_pass_replace_irrelevant_ids.h" +#include "source/fuzz/fuzzer_pass_replace_linear_algebra_instructions.h" +#include "source/fuzz/fuzzer_pass_replace_loads_stores_with_copy_memories.h" +#include "source/fuzz/fuzzer_pass_replace_opphi_ids_from_dead_predecessors.h" +#include "source/fuzz/fuzzer_pass_replace_opselects_with_conditional_branches.h" +#include "source/fuzz/fuzzer_pass_replace_parameter_with_global.h" +#include "source/fuzz/fuzzer_pass_replace_params_with_struct.h" +#include "source/fuzz/fuzzer_pass_split_blocks.h" +#include "source/fuzz/fuzzer_pass_swap_conditional_branch_operands.h" +#include "source/fuzz/fuzzer_pass_wrap_regions_in_selections.h" + +namespace spvtools { +namespace fuzz { + +// This class has a distinct member for each repeated fuzzer pass (i.e., a +// fuzzer pass that it makes sense to run multiple times). If a member is null +// then we do not have an instance of that fuzzer pass, i.e. it is disabled. +// The class also provides access to the set of passes that are enabled. +class RepeatedPassInstances { +// This macro should be invoked below for every repeated fuzzer pass. If a +// repeated fuzzer pass is called FuzzerPassFoo then the macro invocation: +// +// REPEATED_PASS_INSTANCE(Foo); +// +// should be used. This adds a private member of type FuzzerPassFoo*, and +// provides the following public methods: +// +// // Requires that SetPass has not been called previously with FuzzerPassFoo. +// // Adds |pass| to the set of known pass instances. +// void SetPass(std::unique_ptr pass); +// +// // Returns a pointer to a pass instance of type FuzzerPassFoo that was +// // previously registered via SetPass(), or nullptr if no such instance was +// // registered +// FuzzerPassFoo* GetFoo(); +#define REPEATED_PASS_INSTANCE(NAME) \ + public: \ + FuzzerPass##NAME* Get##NAME() const { return NAME##_; } \ + void SetPass(std::unique_ptr pass) { \ + assert(NAME##_ == nullptr && "Attempt to set pass multiple times."); \ + NAME##_ = pass.get(); \ + passes_.push_back(std::move(pass)); \ + } \ + \ + private: \ + FuzzerPass##NAME* NAME##_ = nullptr + + REPEATED_PASS_INSTANCE(AddAccessChains); + REPEATED_PASS_INSTANCE(AddBitInstructionSynonyms); + REPEATED_PASS_INSTANCE(AddCompositeExtract); + REPEATED_PASS_INSTANCE(AddCompositeInserts); + REPEATED_PASS_INSTANCE(AddCompositeTypes); + REPEATED_PASS_INSTANCE(AddCopyMemory); + REPEATED_PASS_INSTANCE(AddDeadBlocks); + REPEATED_PASS_INSTANCE(AddDeadBreaks); + REPEATED_PASS_INSTANCE(AddDeadContinues); + REPEATED_PASS_INSTANCE(AddEquationInstructions); + REPEATED_PASS_INSTANCE(AddFunctionCalls); + REPEATED_PASS_INSTANCE(AddGlobalVariables); + REPEATED_PASS_INSTANCE(AddImageSampleUnusedComponents); + REPEATED_PASS_INSTANCE(AddLoads); + REPEATED_PASS_INSTANCE(AddLocalVariables); + REPEATED_PASS_INSTANCE(AddLoopPreheaders); + REPEATED_PASS_INSTANCE(AddLoopsToCreateIntConstantSynonyms); + REPEATED_PASS_INSTANCE(AddOpPhiSynonyms); + REPEATED_PASS_INSTANCE(AddParameters); + REPEATED_PASS_INSTANCE(AddRelaxedDecorations); + REPEATED_PASS_INSTANCE(AddStores); + REPEATED_PASS_INSTANCE(AddSynonyms); + REPEATED_PASS_INSTANCE(AddVectorShuffleInstructions); + REPEATED_PASS_INSTANCE(ApplyIdSynonyms); + REPEATED_PASS_INSTANCE(ConstructComposites); + REPEATED_PASS_INSTANCE(CopyObjects); + REPEATED_PASS_INSTANCE(DonateModules); + REPEATED_PASS_INSTANCE(DuplicateRegionsWithSelections); + REPEATED_PASS_INSTANCE(ExpandVectorReductions); + REPEATED_PASS_INSTANCE(FlattenConditionalBranches); + REPEATED_PASS_INSTANCE(InlineFunctions); + REPEATED_PASS_INSTANCE(InvertComparisonOperators); + REPEATED_PASS_INSTANCE(MakeVectorOperationsDynamic); + REPEATED_PASS_INSTANCE(MergeBlocks); + REPEATED_PASS_INSTANCE(MergeFunctionReturns); + REPEATED_PASS_INSTANCE(MutatePointers); + REPEATED_PASS_INSTANCE(ObfuscateConstants); + REPEATED_PASS_INSTANCE(OutlineFunctions); + REPEATED_PASS_INSTANCE(PermuteBlocks); + REPEATED_PASS_INSTANCE(PermuteFunctionParameters); + REPEATED_PASS_INSTANCE(PermuteInstructions); + REPEATED_PASS_INSTANCE(PropagateInstructionsDown); + REPEATED_PASS_INSTANCE(PropagateInstructionsUp); + REPEATED_PASS_INSTANCE(PushIdsThroughVariables); + REPEATED_PASS_INSTANCE(ReplaceAddsSubsMulsWithCarryingExtended); + REPEATED_PASS_INSTANCE(ReplaceBranchesFromDeadBlocksWithExits); + REPEATED_PASS_INSTANCE(ReplaceCopyMemoriesWithLoadsStores); + REPEATED_PASS_INSTANCE(ReplaceCopyObjectsWithStoresLoads); + REPEATED_PASS_INSTANCE(ReplaceLoadsStoresWithCopyMemories); + REPEATED_PASS_INSTANCE(ReplaceIrrelevantIds); + REPEATED_PASS_INSTANCE(ReplaceOpPhiIdsFromDeadPredecessors); + REPEATED_PASS_INSTANCE(ReplaceOpSelectsWithConditionalBranches); + REPEATED_PASS_INSTANCE(ReplaceParameterWithGlobal); + REPEATED_PASS_INSTANCE(ReplaceLinearAlgebraInstructions); + REPEATED_PASS_INSTANCE(ReplaceParamsWithStruct); + REPEATED_PASS_INSTANCE(SplitBlocks); + REPEATED_PASS_INSTANCE(SwapBranchConditionalOperands); + REPEATED_PASS_INSTANCE(WrapRegionsInSelections); +#undef REPEATED_PASS_INSTANCE + + public: + // Yields the sequence of fuzzer pass instances that have been registered. + const std::vector>& GetPasses() const { + return passes_; + } + + private: + // The distinct fuzzer pass instances that have been registered via SetPass(). + std::vector> passes_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_INSTANCES_H_ diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.cpp b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.cpp new file mode 100644 index 0000000..032f264 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_manager.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassManager::RepeatedPassManager(FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances) + : fuzzer_context_(fuzzer_context), pass_instances_(pass_instances) {} + +RepeatedPassManager::~RepeatedPassManager() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.h b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.h new file mode 100644 index 0000000..1c23179 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_ +#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_ + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass.h" +#include "source/fuzz/pass_management/repeated_pass_instances.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" + +namespace spvtools { +namespace fuzz { + +// An interface to encapsulate the manner in which the sequence of repeated +// passes that are applied during fuzzing is chosen. An implementation of this +// interface could, for example, keep track of the history of passes that have +// been run and bias the selection of future passes according to this history. +class RepeatedPassManager { + public: + RepeatedPassManager(FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances); + + virtual ~RepeatedPassManager(); + + // Returns the fuzzer pass instance that should be run next. The + // transformations that have been applied so far are provided via + // |applied_transformations| and can be used to influence the decision. + virtual FuzzerPass* ChoosePass( + const protobufs::TransformationSequence& applied_transformations) = 0; + + protected: + FuzzerContext* GetFuzzerContext() { return fuzzer_context_; } + + RepeatedPassInstances* GetPassInstances() { return pass_instances_; } + + private: + // Provided in order to allow the pass manager to make random decisions. + FuzzerContext* fuzzer_context_; + + // The repeated fuzzer passes that are enabled. + RepeatedPassInstances* pass_instances_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_H_ diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp new file mode 100644 index 0000000..e91ba35 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassManagerLoopedWithRecommendations:: + RepeatedPassManagerLoopedWithRecommendations( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender) + : RepeatedPassManager(fuzzer_context, pass_instances), + num_transformations_applied_before_last_pass_choice_(0), + next_pass_index_(0) { + auto& passes = GetPassInstances()->GetPasses(); + do { + FuzzerPass* current_pass = + passes[GetFuzzerContext()->RandomIndex(passes)].get(); + pass_loop_.push_back(current_pass); + for (auto future_pass : + pass_recommender->GetFuturePassRecommendations(*current_pass)) { + recommended_pass_indices_.insert( + static_cast(pass_loop_.size())); + pass_loop_.push_back(future_pass); + } + } while (fuzzer_context->ChoosePercentage( + fuzzer_context->GetChanceOfAddingAnotherPassToPassLoop())); +} + +RepeatedPassManagerLoopedWithRecommendations:: + ~RepeatedPassManagerLoopedWithRecommendations() = default; + +FuzzerPass* RepeatedPassManagerLoopedWithRecommendations::ChoosePass( + const protobufs::TransformationSequence& applied_transformations) { + assert((next_pass_index_ > 0 || + recommended_pass_indices_.count(next_pass_index_) == 0) && + "The first pass in the loop should not be a recommendation."); + assert(static_cast(applied_transformations.transformation_size()) >= + num_transformations_applied_before_last_pass_choice_ && + "The number of applied transformations should not decrease."); + if (num_transformations_applied_before_last_pass_choice_ == + static_cast(applied_transformations.transformation_size())) { + // The last pass that was applied did not lead to any new transformations. + // We thus do not want to apply recommendations based on it, so we skip on + // to the next non-recommended pass. + while (recommended_pass_indices_.count(next_pass_index_)) { + next_pass_index_ = + (next_pass_index_ + 1) % static_cast(pass_loop_.size()); + } + } + auto result = pass_loop_[next_pass_index_]; + next_pass_index_ = + (next_pass_index_ + 1) % static_cast(pass_loop_.size()); + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h new file mode 100644 index 0000000..42ce38e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_looped_with_recommendations.h @@ -0,0 +1,69 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_ +#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_ + +#include + +#include "source/fuzz/pass_management/repeated_pass_manager.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" + +namespace spvtools { +namespace fuzz { + +// On construction, this pass manager creates a sequence of fuzzer passes which +// is not changed thereafter. Passes from this sequence are served up in round +// robin fashion each time ChoosePass is invoked - i.e., the sequence is a "pass +// loop". +// +// The pass loop is constructed by repeatedly: +// - Randomly adding an enabled pass +// - Adding all recommended follow-on passes for this pass +// and probabilistically terminating this process. +class RepeatedPassManagerLoopedWithRecommendations + : public RepeatedPassManager { + public: + RepeatedPassManagerLoopedWithRecommendations( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender); + + ~RepeatedPassManagerLoopedWithRecommendations() override; + + FuzzerPass* ChoosePass(const protobufs::TransformationSequence& + applied_transformations) override; + + private: + // The loop of fuzzer passes to be applied, populated on construction. + std::vector pass_loop_; + + // A set of indices into |pass_loop_| recording which passes are in the loop + // because they are recommended based on previous passes in the loop. This + // allows these recommended passes to be skipped if the passes they are + // meant to amplify had no effect. + std::unordered_set recommended_pass_indices_; + + // Used to detect when chosen passes have had no effect, so that their + // associated recommendations are skipped. + uint32_t num_transformations_applied_before_last_pass_choice_; + + // An index into |pass_loop_| specifying which pass should be served up next + // time ChoosePass is invoked. + uint32_t next_pass_index_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_LOOPED_WITH_RECOMMENDATIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp new file mode 100644 index 0000000..6d77f25 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassManagerRandomWithRecommendations:: + RepeatedPassManagerRandomWithRecommendations( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender) + : RepeatedPassManager(fuzzer_context, pass_instances), + pass_recommender_(pass_recommender), + num_transformations_applied_before_last_pass_choice_(0), + last_pass_choice_(nullptr) {} + +RepeatedPassManagerRandomWithRecommendations:: + ~RepeatedPassManagerRandomWithRecommendations() = default; + +FuzzerPass* RepeatedPassManagerRandomWithRecommendations::ChoosePass( + const protobufs::TransformationSequence& applied_transformations) { + assert(static_cast(applied_transformations.transformation_size()) >= + num_transformations_applied_before_last_pass_choice_ && + "The number of applied transformations should not decrease."); + if (last_pass_choice_ != nullptr && + static_cast(applied_transformations.transformation_size()) > + num_transformations_applied_before_last_pass_choice_) { + // The last pass had some effect, so we make future recommendations based on + // it. + for (auto future_pass : + pass_recommender_->GetFuturePassRecommendations(*last_pass_choice_)) { + recommended_passes_.push_back(future_pass); + } + } + + FuzzerPass* result; + if (recommended_passes_.empty() || GetFuzzerContext()->ChooseEven()) { + auto& passes = GetPassInstances()->GetPasses(); + result = passes[GetFuzzerContext()->RandomIndex(passes)].get(); + } else { + result = recommended_passes_.front(); + recommended_passes_.pop_front(); + } + assert(result != nullptr && "A pass must have been chosen."); + last_pass_choice_ = result; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h new file mode 100644 index 0000000..5dbd455 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_random_with_recommendations.h @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_ +#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_ + +#include + +#include "source/fuzz/pass_management/repeated_pass_manager.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" + +namespace spvtools { +namespace fuzz { + +// This repeated pass manager uses a pass recommender to recommend future passes +// each time a fuzzer pass is run. It keeps a queue of recommended passes. +// +// Each time a fuzzer pass is requested, the manager either selects an enabled +// fuzzer pass at random, or selects the pass at the front of the recommendation +// queue, removing it from the queue. The decision of which of these pass +// selection methods to use is made randomly each time ChoosePass is called. +// +// Either way, recommended follow-on passes for the chosen pass are added to +// the recommendation queue. +class RepeatedPassManagerRandomWithRecommendations + : public RepeatedPassManager { + public: + RepeatedPassManagerRandomWithRecommendations( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances, + RepeatedPassRecommender* pass_recommender); + + ~RepeatedPassManagerRandomWithRecommendations() override; + + FuzzerPass* ChoosePass(const protobufs::TransformationSequence& + applied_transformations) override; + + private: + // The queue of passes that have been recommended based on previously-chosen + // passes. + std::deque recommended_passes_; + + // Used to recommend future passes. + RepeatedPassRecommender* pass_recommender_; + + // Used to detect when chosen passes have had no effect, so that their + // associated recommendations are skipped. + uint32_t num_transformations_applied_before_last_pass_choice_; + + // The fuzzer pass returned last time ChoosePass() was called; nullptr if + // ChoosePass() has not yet been called. + FuzzerPass* last_pass_choice_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_RANDOM_WITH_RECOMMENDATIONS_H_ diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.cpp b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.cpp new file mode 100644 index 0000000..c593ffe --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_manager_simple.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassManagerSimple::RepeatedPassManagerSimple( + FuzzerContext* fuzzer_context, RepeatedPassInstances* pass_instances) + : RepeatedPassManager(fuzzer_context, pass_instances) {} + +RepeatedPassManagerSimple::~RepeatedPassManagerSimple() = default; + +FuzzerPass* RepeatedPassManagerSimple::ChoosePass( + const protobufs::TransformationSequence& /*unused*/) { + auto& passes = GetPassInstances()->GetPasses(); + return passes[GetFuzzerContext()->RandomIndex(passes)].get(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.h b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.h new file mode 100644 index 0000000..a4cc652 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_manager_simple.h @@ -0,0 +1,39 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_ +#define SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_ + +#include "source/fuzz/pass_management/repeated_pass_manager.h" + +namespace spvtools { +namespace fuzz { + +// Selects the next pass to run uniformly at random from the enabled repeated +// passes. Recommendations are not used. +class RepeatedPassManagerSimple : public RepeatedPassManager { + public: + RepeatedPassManagerSimple(FuzzerContext* fuzzer_context, + RepeatedPassInstances* pass_instances); + + ~RepeatedPassManagerSimple() override; + + FuzzerPass* ChoosePass(const protobufs::TransformationSequence& + applied_transformations) override; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_MANAGER_SIMPLE_H_ diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.cpp b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.cpp new file mode 100644 index 0000000..c7789dc --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_recommender.h" + +namespace spvtools { +namespace fuzz { + +RepeatedPassRecommender::~RepeatedPassRecommender() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.h b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.h new file mode 100644 index 0000000..a6b1338 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_ +#define SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_ + +#include + +#include "source/fuzz/fuzzer_pass.h" + +namespace spvtools { +namespace fuzz { + +// Interface for influencing interactions between repeated fuzzer passes, by +// allowing hints as to which passes are recommended to be run after one +// another. +class RepeatedPassRecommender { + public: + virtual ~RepeatedPassRecommender(); + + // Given a reference to a repeated pass, |pass|, returns a sequence of + // repeated pass instances that might be worth running soon after having + // run |pass|. + virtual std::vector GetFuturePassRecommendations( + const FuzzerPass& pass) = 0; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_H_ diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp new file mode 100644 index 0000000..a933848 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.cpp @@ -0,0 +1,377 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pass_management/repeated_pass_recommender_standard.h" + +#include + +namespace spvtools { +namespace fuzz { + +RepeatedPassRecommenderStandard::RepeatedPassRecommenderStandard( + RepeatedPassInstances* pass_instances, FuzzerContext* fuzzer_context) + : pass_instances_(pass_instances), fuzzer_context_(fuzzer_context) {} + +RepeatedPassRecommenderStandard::~RepeatedPassRecommenderStandard() = default; + +std::vector +RepeatedPassRecommenderStandard::GetFuturePassRecommendations( + const FuzzerPass& pass) { + if (&pass == pass_instances_->GetAddAccessChains()) { + // - Adding access chains means there is more scope for loading and storing + // - It could be worth making more access chains from the recently-added + // access chains + return RandomOrderAndNonNull({pass_instances_->GetAddLoads(), + pass_instances_->GetAddStores(), + pass_instances_->GetAddAccessChains()}); + } + if (&pass == pass_instances_->GetAddBitInstructionSynonyms()) { + // - Adding bit instruction synonyms creates opportunities to apply synonyms + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetAddCompositeExtract()) { + // - This transformation can introduce synonyms to the fact manager. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetAddCompositeInserts()) { + // - Having added inserts we will have more vectors, so there is scope for + // vector shuffling + // - Adding inserts creates synonyms, which we should try to use + // - Vector inserts can be made dynamic + return RandomOrderAndNonNull( + {pass_instances_->GetAddVectorShuffleInstructions(), + pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetMakeVectorOperationsDynamic()}); + } + if (&pass == pass_instances_->GetAddCompositeTypes()) { + // - More composite types gives more scope for constructing composites + return RandomOrderAndNonNull({pass_instances_->GetConstructComposites()}); + } + if (&pass == pass_instances_->GetAddCopyMemory()) { + // - Recently-added copy memories could be replace with load-store pairs + return RandomOrderAndNonNull( + {pass_instances_->GetReplaceCopyMemoriesWithLoadsStores()}); + } + if (&pass == pass_instances_->GetAddDeadBlocks()) { + // - Dead blocks are great for adding function calls + // - Dead blocks are also great for adding loads and stores + // - The guard associated with a dead block can be obfuscated + // - Branches from dead blocks may be replaced with exits + return RandomOrderAndNonNull( + {pass_instances_->GetAddFunctionCalls(), pass_instances_->GetAddLoads(), + pass_instances_->GetAddStores(), + pass_instances_->GetObfuscateConstants(), + pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()}); + } + if (&pass == pass_instances_->GetAddDeadBreaks()) { + // - The guard of the dead break is a good candidate for obfuscation + return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants()}); + } + if (&pass == pass_instances_->GetAddDeadContinues()) { + // - The guard of the dead continue is a good candidate for obfuscation + return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants()}); + } + if (&pass == pass_instances_->GetAddEquationInstructions()) { + // - Equation instructions can create synonyms, which we can apply + // - Equation instructions collaborate with one another to make synonyms, so + // having added some it is worth adding more + return RandomOrderAndNonNull( + {pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetAddEquationInstructions()}); + } + if (&pass == pass_instances_->GetAddFunctionCalls()) { + // - Called functions can be inlined + // - Irrelevant ids are created, so they can be replaced + return RandomOrderAndNonNull({pass_instances_->GetInlineFunctions(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetAddGlobalVariables()) { + // - New globals provide new possibilities for making access chains + // - We can load from and store to new globals + return RandomOrderAndNonNull({pass_instances_->GetAddAccessChains(), + pass_instances_->GetAddLoads(), + pass_instances_->GetAddStores()}); + } + if (&pass == pass_instances_->GetAddImageSampleUnusedComponents()) { + // - This introduces an unused component whose id is irrelevant and can be + // replaced + return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetAddLoads()) { + // - Loads might end up with corresponding stores, so that pairs can be + // replaced with memory copies + return RandomOrderAndNonNull( + {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()}); + } + if (&pass == pass_instances_->GetAddLocalVariables()) { + // - New locals provide new possibilities for making access chains + // - We can load from and store to new locals + return RandomOrderAndNonNull({pass_instances_->GetAddAccessChains(), + pass_instances_->GetAddLoads(), + pass_instances_->GetAddStores()}); + } + if (&pass == pass_instances_->GetAddLoopPreheaders()) { + // - The loop preheader provides more scope for duplicating regions and + // outlining functions. + return RandomOrderAndNonNull( + {pass_instances_->GetDuplicateRegionsWithSelections(), + pass_instances_->GetOutlineFunctions(), + pass_instances_->GetWrapRegionsInSelections()}); + } + if (&pass == pass_instances_->GetAddLoopsToCreateIntConstantSynonyms()) { + // - New synonyms can be applied + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetAddOpPhiSynonyms()) { + // - New synonyms can be applied + // - If OpPhi synonyms are introduced for blocks with dead predecessors, the + // values consumed from dead predecessors can be replaced + return RandomOrderAndNonNull( + {pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceOpPhiIdsFromDeadPredecessors()}); + } + if (&pass == pass_instances_->GetAddParameters()) { + // - We might be able to create interesting synonyms of new parameters. + // - This introduces irrelevant ids, which can be replaced + return RandomOrderAndNonNull({pass_instances_->GetAddSynonyms(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetAddRelaxedDecorations()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetAddStores()) { + // - Stores might end up with corresponding loads, so that pairs can be + // replaced with memory copies + return RandomOrderAndNonNull( + {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()}); + } + if (&pass == pass_instances_->GetAddSynonyms()) { + // - New synonyms can be applied + // - Synonym instructions use constants, which can be obfuscated + // - Synonym instructions use irrelevant ids, which can be replaced + // - Synonym instructions introduce addition/subtraction, which can be + // replaced with carrying/extended versions + return RandomOrderAndNonNull( + {pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetObfuscateConstants(), + pass_instances_->GetReplaceAddsSubsMulsWithCarryingExtended(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetAddVectorShuffleInstructions()) { + // - Vector shuffles create synonyms that can be applied + // - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806) Extract + // from composites. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetApplyIdSynonyms()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetConstructComposites()) { + // - TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3806): Extract + // from composites. + return RandomOrderAndNonNull({}); + } + if (&pass == pass_instances_->GetCopyObjects()) { + // - Object copies create synonyms that can be applied + // - OpCopyObject can be replaced with a store/load pair + return RandomOrderAndNonNull( + {pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceCopyObjectsWithStoresLoads()}); + } + if (&pass == pass_instances_->GetDonateModules()) { + // - New functions in the module can be called + // - Donated dead functions produce irrelevant ids, which can be replaced + // - Donated functions are good candidates for having their returns merged + // - Donated dead functions may allow branches to be replaced with exits + return RandomOrderAndNonNull( + {pass_instances_->GetAddFunctionCalls(), + pass_instances_->GetReplaceIrrelevantIds(), + pass_instances_->GetMergeFunctionReturns(), + pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()}); + } + if (&pass == pass_instances_->GetDuplicateRegionsWithSelections()) { + // - Parts of duplicated regions can be outlined + return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()}); + } + if (&pass == pass_instances_->GetExpandVectorReductions()) { + // - Adding OpAny and OpAll synonyms creates opportunities to apply synonyms + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetFlattenConditionalBranches()) { + // - Parts of flattened selections can be outlined + // - The flattening transformation introduces constants and irrelevant ids + // for enclosing hard-to-flatten operations; these can be obfuscated or + // replaced + return RandomOrderAndNonNull({pass_instances_->GetObfuscateConstants(), + pass_instances_->GetOutlineFunctions(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetInlineFunctions()) { + // - Parts of inlined functions can be outlined again + return RandomOrderAndNonNull({pass_instances_->GetOutlineFunctions()}); + } + if (&pass == pass_instances_->GetInvertComparisonOperators()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetMakeVectorOperationsDynamic()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetMergeBlocks()) { + // - Having merged some blocks it may be interesting to split them in a + // different way + return RandomOrderAndNonNull({pass_instances_->GetSplitBlocks()}); + } + if (&pass == pass_instances_->GetMergeFunctionReturns()) { + // - Functions without early returns are more likely to be able to be + // inlined. + return RandomOrderAndNonNull({pass_instances_->GetInlineFunctions()}); + } + if (&pass == pass_instances_->GetMutatePointers()) { + // - This creates irrelevant ids, which can be replaced + return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetObfuscateConstants()) { + // - No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetOutlineFunctions()) { + // - This creates more functions, which can be called + // - Inlining the function for the region that was outlined might also be + // fruitful; it will be inlined in a different form + return RandomOrderAndNonNull({pass_instances_->GetAddFunctionCalls(), + pass_instances_->GetInlineFunctions()}); + } + if (&pass == pass_instances_->GetPermuteBlocks()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetPermuteFunctionParameters()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetPermuteInstructions()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetPropagateInstructionsDown()) { + // - This fuzzer pass might create new synonyms that can later be applied. + // - This fuzzer pass might create irrelevant ids that can later be + // replaced. + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms(), + pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetPropagateInstructionsUp()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetPushIdsThroughVariables()) { + // - This pass creates synonyms, so it is worth applying them + return RandomOrderAndNonNull({pass_instances_->GetApplyIdSynonyms()}); + } + if (&pass == pass_instances_->GetReplaceAddsSubsMulsWithCarryingExtended()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceBranchesFromDeadBlocksWithExits()) { + // - Changing a branch to OpReturnValue introduces an irrelevant id, which + // can be replaced + return RandomOrderAndNonNull({pass_instances_->GetReplaceIrrelevantIds()}); + } + if (&pass == pass_instances_->GetReplaceCopyMemoriesWithLoadsStores()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceCopyObjectsWithStoresLoads()) { + // - We may end up with load/store pairs that could be used to create memory + // copies + return RandomOrderAndNonNull( + {pass_instances_->GetReplaceLoadsStoresWithCopyMemories()}); + } + if (&pass == pass_instances_->GetReplaceIrrelevantIds()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceLinearAlgebraInstructions()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceLoadsStoresWithCopyMemories()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceOpPhiIdsFromDeadPredecessors()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceOpSelectsWithConditionalBranches()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceParameterWithGlobal()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetReplaceParamsWithStruct()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetSplitBlocks()) { + // - More blocks means more chances for adding dead breaks/continues, and + // for adding dead blocks + return RandomOrderAndNonNull({pass_instances_->GetAddDeadBreaks(), + pass_instances_->GetAddDeadContinues(), + pass_instances_->GetAddDeadBlocks()}); + } + if (&pass == pass_instances_->GetSwapBranchConditionalOperands()) { + // No obvious follow-on passes + return {}; + } + if (&pass == pass_instances_->GetWrapRegionsInSelections()) { + // - This pass uses an irrelevant boolean constant - we can replace it with + // something more interesting. + // - We can obfuscate that very constant as well. + // - We can flatten created selection construct. + return RandomOrderAndNonNull( + {pass_instances_->GetObfuscateConstants(), + pass_instances_->GetReplaceIrrelevantIds(), + pass_instances_->GetFlattenConditionalBranches()}); + } + assert(false && "Unreachable: every fuzzer pass should be dealt with."); + return {}; +} + +std::vector RepeatedPassRecommenderStandard::RandomOrderAndNonNull( + const std::vector& passes) { + std::vector indices(passes.size()); + std::iota(indices.begin(), indices.end(), 0); + std::vector result; + while (!indices.empty()) { + FuzzerPass* maybe_pass = + passes[fuzzer_context_->RemoveAtRandomIndex(&indices)]; + if (maybe_pass != nullptr && + fuzzer_context_->ChoosePercentage( + fuzzer_context_ + ->GetChanceOfAcceptingRepeatedPassRecommendation())) { + result.push_back(maybe_pass); + } + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.h b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.h new file mode 100644 index 0000000..293b8e0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pass_management/repeated_pass_recommender_standard.h @@ -0,0 +1,50 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_ +#define SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_ + +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/pass_management/repeated_pass_instances.h" +#include "source/fuzz/pass_management/repeated_pass_recommender.h" + +namespace spvtools { +namespace fuzz { + +// A manually-crafter recommender of repeated passes, designed based on +// knowledge of how the various fuzzer passes work and speculation as to how +// they might interact in interesting ways. +class RepeatedPassRecommenderStandard : public RepeatedPassRecommender { + public: + RepeatedPassRecommenderStandard(RepeatedPassInstances* pass_instances, + FuzzerContext* fuzzer_context); + + ~RepeatedPassRecommenderStandard(); + + std::vector GetFuturePassRecommendations( + const FuzzerPass& pass) override; + + private: + std::vector RandomOrderAndNonNull( + const std::vector& passes); + + RepeatedPassInstances* pass_instances_; + + FuzzerContext* fuzzer_context_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPEATED_PASS_RECOMMENDER_STANDARD_H_ diff --git a/third_party/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h b/third_party/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h new file mode 100644 index 0000000..eb8cb14 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/protobufs/spirvfuzz_protobufs.h @@ -0,0 +1,54 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ +#define SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ + +// This header file serves to act as a barrier between the protobuf header +// files and files that include them. It uses compiler pragmas to disable +// diagnostics, in order to ignore warnings generated during the processing +// of these header files without having to compromise on freedom from warnings +// in the rest of the project. + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wshadow" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4244) +#endif + +// The following should be the only place in the project where protobuf files +// are directly included. This is so that they can be compiled in a manner +// where warnings are ignored. + +#include "google/protobuf/util/json_util.h" +#include "google/protobuf/util/message_differencer.h" +#include "source/fuzz/protobufs/spvtoolsfuzz.pb.h" + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // SOURCE_FUZZ_SPIRVFUZZ_PROTOBUFS_H_ diff --git a/third_party/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto b/third_party/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto new file mode 100644 index 0000000..770a2dd --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/protobufs/spvtoolsfuzz.proto @@ -0,0 +1,2342 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file is specifically named spvtools_fuzz.proto so that the string +// 'spvtools_fuzz' appears in the names of global-scope symbols that protoc +// generates when targeting C++. This is to reduce the potential for name +// clashes with other globally-scoped symbols. + +syntax = "proto3"; + +package spvtools.fuzz.protobufs; + +message UInt32Pair { + + // A pair of uint32s; useful for defining mappings. + + uint32 first = 1; + + uint32 second = 2; + +} + +message InstructionDescriptor { + + // Describes an instruction in some block of a function with respect to a + // base instruction. + + // The id of an instruction after which the instruction being described is + // believed to be located. It might be the using instruction itself. + uint32 base_instruction_result_id = 1; + + // The opcode for the instruction being described. + uint32 target_instruction_opcode = 2; + + // The number of matching opcodes to skip over when searching from the base + // instruction to the instruction being described. + uint32 num_opcodes_to_ignore = 3; + +} + +message IdUseDescriptor { + + // Describes a use of an id as an input operand to an instruction in some + // block of a function. + + // Example: + // - id_of_interest = 42 + // - enclosing_instruction = ( + // base_instruction_result_id = 50, + // target_instruction_opcode = OpStore + // num_opcodes_to_ignore = 7 + // ) + // - in_operand_index = 1 + // represents a use of id 42 as input operand 1 to an OpStore instruction, + // such that the OpStore instruction can be found in the same basic block as + // the instruction with result id 50, and in particular is the 8th OpStore + // instruction found from instruction 50 onwards (i.e. 7 OpStore + // instructions are skipped). + + // An id that we would like to be able to find a use of. + uint32 id_of_interest = 1; + + // The input operand index at which the use is expected. + InstructionDescriptor enclosing_instruction = 2; + + uint32 in_operand_index = 3; + +} + +message DataDescriptor { + + // Represents a data element that can be accessed from an id, by walking the + // type hierarchy via a sequence of 0 or more indices. + // + // Very similar to a UniformBufferElementDescriptor, except that a + // DataDescriptor is rooted at the id of a scalar or composite. + + // The object being accessed - a scalar or composite + uint32 object = 1; + + // 0 or more indices, used to index into a composite object + repeated uint32 index = 2; + +} + +message UniformBufferElementDescriptor { + + // Represents a data element inside a uniform buffer. The element is + // specified via (a) the result id of a uniform variable in which the element + // is contained, and (b) a series of indices that need to be followed to get + // to the element (via fields and array/vector indices). + // + // Example: suppose there is a uniform variable with descriptor set 7 and + // binding 9, and that the uniform variable has the following type (using + // GLSL-like syntax): + // + // struct S { + // float f; + // vec3 g; + // int4 h[10]; + // }; + // + // Then: + // - (7, 9, [0]) describes the 'f' field. + // - (7, 9, [1,1]) describes the y component of the 'g' field. + // - (7, 9, [2,7,3]) describes the w component of element 7 of the 'h' field + + // The descriptor set and binding associated with a uniform variable. + uint32 descriptor_set = 1; + uint32 binding = 2; + + // An ordered sequence of indices through composite structures in the + // uniform buffer. + repeated uint32 index = 3; + +} + +message InstructionOperand { + + // Represents an operand to a SPIR-V instruction. + + // The type of the operand. + uint32 operand_type = 1; + + // The data associated with the operand. For most operands (e.g. ids, + // storage classes and literals) this will be a single word. + repeated uint32 operand_data = 2; + +} + +message Instruction { + + // Represents a SPIR-V instruction. + + // The instruction's opcode (e.g. OpLabel). + uint32 opcode = 1; + + // The id of the instruction's result type; 0 if there is no result type. + uint32 result_type_id = 2; + + // The id of the instruction's result; 0 if there is no result. + uint32 result_id = 3; + + // Zero or more input operands. + repeated InstructionOperand input_operand = 4; + +} + +message FactSequence { + repeated Fact fact = 1; +} + +message Fact { + oneof fact { + // Order the fact options by numeric id (rather than alphabetically). + FactConstantUniform constant_uniform_fact = 1; + FactDataSynonym data_synonym_fact = 2; + FactBlockIsDead block_is_dead_fact = 3; + FactFunctionIsLivesafe function_is_livesafe_fact = 4; + FactPointeeValueIsIrrelevant pointee_value_is_irrelevant_fact = 5; + FactIdEquation id_equation_fact = 6; + FactIdIsIrrelevant id_is_irrelevant = 7; + } +} + +// Keep fact message types in alphabetical order: + +message FactBlockIsDead { + + // Records the fact that a block is guaranteed to be dynamically unreachable. + // This is useful because it informs the fuzzer that rather arbitrary changes + // can be made to this block. + + uint32 block_id = 1; + +} + +message FactConstantUniform { + + // Records the fact that a uniform buffer element is guaranteed to be equal + // to a particular constant value. spirv-fuzz can use such guarantees to + // obfuscate code, e.g. to manufacture an expression that will (due to the + // guarantee) evaluate to a particular value at runtime but in a manner that + // cannot be predicted at compile-time. + + // An element of a uniform buffer + UniformBufferElementDescriptor uniform_buffer_element_descriptor = 1; + + // The words of the associated constant + repeated uint32 constant_word = 2; + +} + +message FactDataSynonym { + + // Records the fact that the data held in two data descriptors are guaranteed + // to be equal. spirv-fuzz can use this to replace uses of one piece of data + // with a known-to-be-equal piece of data. + + // Data descriptors guaranteed to hold identical data. + DataDescriptor data1 = 1; + + DataDescriptor data2 = 2; + +} + +message FactFunctionIsLivesafe { + + // Records the fact that a function is guaranteed to be "livesafe", meaning + // that it will not make out-of-bounds accesses, does not contain reachable + // OpKill or OpUnreachable instructions, does not contain loops that will + // execute for large numbers of iterations, and only invokes other livesafe + // functions. + + uint32 function_id = 1; + +} + +message FactIdEquation { + + // Records the fact that the equation: + // + // lhs_id = opcode rhs_id[0] rhs_id[1] ... rhs_id[N-1] + // + // holds; e.g. that the equation: + // + // %12 = OpIAdd %13 %14 + // + // holds in the case where lhs_id is 12, rhs_id is [13, 14], and the opcode is + // OpIAdd. + + // The left-hand-side of the equation. + uint32 lhs_id = 1; + + // A SPIR-V opcode, from a restricted set of instructions for which equation + // facts make sense. + uint32 opcode = 2; + + // The operands to the right-hand-side of the equation. + repeated uint32 rhs_id = 3; + +} + +message FactIdIsIrrelevant { + + // Records a fact that |result_id| is irrelevant (i.e. it's usage doesn't + // change the semantics of the module). This implies that a use of this id + // can later be replaced with some other id of the same type, or the + // definition of |result_id| can be changed so that it yields a different value. + + // An irrelevant id. + uint32 result_id = 1; + +} + +message FactPointeeValueIsIrrelevant { + + // Records the fact that value of the data pointed to by a pointer id does + // not influence the observable behaviour of the module. This means that + // arbitrary stores can be made through the pointer, and that nothing can be + // guaranteed about the values that are loaded via the pointer. + + // A result id of pointer type + uint32 pointer_id = 1; + +} + +message AccessChainClampingInfo { + + // When making a function livesafe it is necessary to clamp the indices that + // occur as operands to access chain instructions so that they are guaranteed + // to be in bounds. This message type allows an access chain instruction to + // have an associated sequence of ids that are reserved for comparing an + // access chain index with a bound (e.g. an array size), and selecting + // between the access chain index (if it is within bounds) and the bound (if + // it is not). + // + // This allows turning an instruction of the form: + // + // %result = OpAccessChain %type %object ... %index ... + // + // into: + // + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + // %result = OpAccessChain %type %object ... %t2 ... + + // The result id of an OpAccessChain or OpInBoundsAccessChain instruction. + uint32 access_chain_id = 1; + + // A series of pairs of fresh ids, one per access chain index, for the results + // of a compare instruction and a select instruction, serving the roles of %t1 + // and %t2 in the above example. + repeated UInt32Pair compare_and_select_ids = 2; + +} + +message SideEffectWrapperInfo { + // When flattening a conditional branch, it is necessary to enclose + // instructions that have side effects inside conditionals, so that + // they are only executed if the condition holds. Otherwise, there + // might be unintended changes in memory, or crashes that would not + // originally happen. + // For example, the instruction %id = OpLoad %type %ptr, found in + // the true branch of the conditional, will be enclosed in a new + // conditional (assuming that the block containing it can be split + // around it) as follows: + // + // [previous instructions in the block] + // OpSelectionMerge %merge_block_id None + // OpBranchConditional %cond %execute_block_id %alternative_block_id + // %execute_block_id = OpLabel + // %actual_result_id = OpLoad %type %ptr + // OpBranch %merge_block_id + // %alternative_block_id = OpLabel + // %placeholder_result_id = OpCopyObject %type %value_to_copy_id + // OpBranch %merge_block_id + // %merge_block_id = OpLabel + // %id = OpPhi %type %actual_result_id %execute_block_id %placeholder_result_id %alternative_block_id + // [following instructions from the original block] + // + // If the instruction does not have a result id, this is simplified. + // For example, OpStore %ptr %value, found in the true branch of a + // conditional, is enclosed as follows: + // + // [previous instructions in the block] + // OpSelectionMerge %merge_block None + // OpBranchConditional %cond %execute_block_id %merge_block_id + // %execute_block_id = OpLabel + // OpStore %ptr %value + // OpBranch %merge_block_id + // %merge_block_id = OpLabel + // [following instructions from the original block] + // + // The same happens if the instruction is found in the false branch + // of the conditional being flattened, except that the label ids in + // the OpBranchConditional are swapped. + + + // An instruction descriptor for identifying the instruction to be + // enclosed inside a conditional. An instruction descriptor is + // necessary because the instruction might not have a result id. + InstructionDescriptor instruction = 1; + + // A fresh id for the new merge block. + uint32 merge_block_id = 2; + + // A fresh id for the new block where the actual instruction is + // executed. + uint32 execute_block_id = 3; + + // The following fields are only needed if the original instruction has a + // result id. They can be set to 0 if not needed. + + // A fresh id for the result id of the instruction (the original + // one is used by the OpPhi instruction). + uint32 actual_result_id = 4; + + // A fresh id for the new block where the placeholder instruction + // is placed. + uint32 alternative_block_id = 5; + + // A fresh id for the placeholder instruction. + uint32 placeholder_result_id = 6; + + // An id present in the module, available to use at this point in + // the program and with the same type as the original instruction, + // that can be used to create a placeholder OpCopyObject + // instruction. + uint32 value_to_copy_id = 7; +} + +message ReturnMergingInfo { + // TransformationMergeFunctionReturns needs to modify each merge block of + // loops containing return instructions, by: + // - adding instructions to decide whether the function is returning + // - adding instructions to pass on the return value of the function, + // if it is returning + // - changing the branch instruction (which must be an unconditional branch) + // to a conditional branch that, if the function is returning, branches to + // the merge block of the innermost loop that contains this merge block + // (which can be the new merge block introduced by the transformation). + // + // One such merge block of the form: + // %block = OpLabel + // %phi1 = OpPhi %type1 %val1_1 %pred1 %val1_2 %pred2 + // %phi2 = OpPhi %type2 %val2_1 %pred1 %val2_2 %pred2 + // OpBranch %next + // + // is transformed into: + // %block = OpLabel + // %is_returning_id = OpPhi %bool %false %pred1 %false %pred2 %true %ret_bb1 %is_bb2_returning %mer_bb2 + // %maybe_return_val_id = OpPhi %return_type %any_returnable_val %pred1 %any_returnable_val %pred2 + // %ret_val1 %ret_bb1 %ret_val2 %mer_bb2 + // %phi1 = OpPhi %type1 %val1_1 %pred1 %val1_2 %pred2 + // %any_suitable_id_1 %ret_bb1 %any_suitable_id_1 %mer_bb2 + // %phi2 = OpPhi %type2 %val2_1 %pred1 %val2_2 %pred2 + // %any_suitable_id_1 %ret_bb1 %any_suitable_id_1 %mer_bb2 + // OpBranchConditional %is_returning_id %innermost_loop_merge %next + // + // where %ret_bb1 is a block that originally contains a return instruction and %mer_bb2 is the merge block of an inner + // loop, from where the function might be returning. + // + // Note that the block is required to only have OpLabel, OpPhi or OpBranch instructions. + + // The id of the merge block that needs to be modified. + uint32 merge_block_id = 1; + + // A fresh id for a boolean OpPhi whose value will be true iff the function + // is returning. This will be used to decide whether to break out of the loop + // or to use the original branch of the function. This value will also be + // used by the merge block of the enclosing loop (if there is one) if the + // function is returning from this block. + uint32 is_returning_id = 2; + + // A fresh id that will get the value being returned, if the function is + // returning. If the function return type is void, this is ignored. + uint32 maybe_return_val_id = 3; + + // A mapping from each existing OpPhi id to a suitable id of the same type + // available to use before the instruction. + repeated UInt32Pair opphi_to_suitable_id = 4; +} + +message LoopLimiterInfo { + + // Structure capturing the information required to manipulate a loop limiter + // at a loop header. + + // The header for the loop. + uint32 loop_header_id = 1; + + // A fresh id into which the loop limiter's current value can be loaded. + uint32 load_id = 2; + + // A fresh id that can be used to increment the loaded value by 1. + uint32 increment_id = 3; + + // A fresh id that can be used to compare the loaded value with the loop + // limit. + uint32 compare_id = 4; + + // A fresh id that can be used to compute the conjunction or disjunction of + // an original loop exit condition with |compare_id|, if the loop's back edge + // block can conditionally exit the loop. + uint32 logical_op_id = 5; + + // A sequence of ids suitable for extending OpPhi instructions of the loop + // merge block if it did not previously have an incoming edge from the loop + // back edge block. + repeated uint32 phi_id = 6; + +} + +message TransformationSequence { + repeated Transformation transformation = 1; +} + +message Transformation { + oneof transformation { + // Order the transformation options by numeric id (rather than + // alphabetically). + TransformationMoveBlockDown move_block_down = 1; + TransformationSplitBlock split_block = 2; + TransformationAddConstantBoolean add_constant_boolean = 3; + TransformationAddConstantScalar add_constant_scalar = 4; + TransformationAddTypeBoolean add_type_boolean = 5; + TransformationAddTypeFloat add_type_float = 6; + TransformationAddTypeInt add_type_int = 7; + TransformationAddDeadBreak add_dead_break = 8; + TransformationReplaceBooleanConstantWithConstantBinary + replace_boolean_constant_with_constant_binary = 9; + TransformationAddTypePointer add_type_pointer = 10; + TransformationReplaceConstantWithUniform replace_constant_with_uniform = 11; + TransformationAddDeadContinue add_dead_continue = 12; + TransformationReplaceIdWithSynonym replace_id_with_synonym = 13; + TransformationSetSelectionControl set_selection_control = 14; + TransformationCompositeConstruct composite_construct = 15; + TransformationSetLoopControl set_loop_control = 16; + TransformationSetFunctionControl set_function_control = 17; + TransformationAddNoContractionDecoration add_no_contraction_decoration = 18; + TransformationSetMemoryOperandsMask set_memory_operands_mask = 19; + TransformationCompositeExtract composite_extract = 20; + TransformationVectorShuffle vector_shuffle = 21; + TransformationOutlineFunction outline_function = 22; + TransformationMergeBlocks merge_blocks = 23; + TransformationAddTypeVector add_type_vector = 24; + TransformationAddTypeArray add_type_array = 25; + TransformationAddTypeMatrix add_type_matrix = 26; + TransformationAddTypeStruct add_type_struct = 27; + TransformationAddTypeFunction add_type_function = 28; + TransformationAddConstantComposite add_constant_composite = 29; + TransformationAddGlobalVariable add_global_variable = 30; + TransformationAddGlobalUndef add_global_undef = 31; + TransformationAddFunction add_function = 32; + TransformationAddDeadBlock add_dead_block = 33; + TransformationAddLocalVariable add_local_variable = 34; + TransformationLoad load = 35; + TransformationStore store = 36; + TransformationFunctionCall function_call = 37; + TransformationAccessChain access_chain = 38; + TransformationEquationInstruction equation_instruction = 39; + TransformationSwapCommutableOperands swap_commutable_operands = 40; + TransformationPermuteFunctionParameters permute_function_parameters = 41; + TransformationToggleAccessChainInstruction toggle_access_chain_instruction = 42; + TransformationAddConstantNull add_constant_null = 43; + TransformationComputeDataSynonymFactClosure compute_data_synonym_fact_closure = 44; + TransformationAdjustBranchWeights adjust_branch_weights = 45; + TransformationPushIdThroughVariable push_id_through_variable = 46; + TransformationAddSpecConstantOp add_spec_constant_op = 47; + TransformationReplaceLinearAlgebraInstruction replace_linear_algebra_instruction = 48; + TransformationSwapConditionalBranchOperands swap_conditional_branch_operands = 49; + TransformationPermutePhiOperands permute_phi_operands = 50; + TransformationAddParameter add_parameter = 51; + TransformationAddCopyMemory add_copy_memory = 52; + TransformationInvertComparisonOperator invert_comparison_operator = 53; + TransformationAddImageSampleUnusedComponents add_image_sample_unused_components = 54; + TransformationReplaceParameterWithGlobal replace_parameter_with_global = 55; + TransformationRecordSynonymousConstants record_synonymous_constants = 56; + TransformationAddSynonym add_synonym = 57; + TransformationAddRelaxedDecoration add_relaxed_decoration = 58; + TransformationReplaceParamsWithStruct replace_params_with_struct = 59; + TransformationReplaceCopyObjectWithStoreLoad replace_copy_object_with_store_load = 60; + TransformationReplaceCopyMemoryWithLoadStore replace_copy_memory_with_load_store = 61; + TransformationReplaceLoadStoreWithCopyMemory replace_load_store_with_copy_memory = 62; + TransformationAddLoopPreheader add_loop_preheader = 63; + TransformationMoveInstructionDown move_instruction_down = 64; + TransformationMakeVectorOperationDynamic make_vector_operation_dynamic = 65; + TransformationReplaceAddSubMulWithCarryingExtended replace_add_sub_mul_with_carrying_extended = 66; + TransformationPropagateInstructionUp propagate_instruction_up = 67; + TransformationCompositeInsert composite_insert = 68; + TransformationInlineFunction inline_function = 69; + TransformationAddOpPhiSynonym add_opphi_synonym = 70; + TransformationMutatePointer mutate_pointer = 71; + TransformationReplaceIrrelevantId replace_irrelevant_id = 72; + TransformationReplaceOpPhiIdFromDeadPredecessor replace_opphi_id_from_dead_predecessor = 73; + TransformationReplaceOpSelectWithConditionalBranch replace_opselect_with_conditional_branch = 74; + TransformationDuplicateRegionWithSelection duplicate_region_with_selection = 75; + TransformationFlattenConditionalBranch flatten_conditional_branch = 76; + TransformationAddBitInstructionSynonym add_bit_instruction_synonym = 77; + TransformationAddLoopToCreateIntConstantSynonym add_loop_to_create_int_constant_synonym = 78; + TransformationWrapRegionInSelection wrap_region_in_selection = 79; + TransformationAddEarlyTerminatorWrapper add_early_terminator_wrapper = 80; + TransformationPropagateInstructionDown propagate_instruction_down = 81; + TransformationReplaceBranchFromDeadBlockWithExit replace_branch_from_dead_block_with_exit = 82; + TransformationWrapEarlyTerminatorInFunction wrap_early_terminator_in_function = 83; + TransformationMergeFunctionReturns merge_function_returns = 84; + TransformationExpandVectorReduction expand_vector_reduction = 85; + // Add additional option using the next available number. + } +} + +// Keep transformation message types in alphabetical order: + +message TransformationAccessChain { + + // Adds an access chain instruction based on a given pointer and indices. + + // When accessing a struct, the corresponding indices must be 32-bit integer constants. + // For any other composite, the indices can be any 32-bit integer, and the transformation + // adds two instructions for each such index to clamp it to the bound, as follows: + // + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + + // Result id for the access chain + uint32 fresh_id = 1; + + // The pointer from which the access chain starts + uint32 pointer_id = 2; + + // Zero or more access chain indices + repeated uint32 index_id = 3; + + // A descriptor for an instruction in a block before which the new + // OpAccessChain instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + + // Additional fresh ids, required to clamp index variables. A pair is needed + // for each access to a non-struct composite. + repeated UInt32Pair fresh_ids_for_clamping = 5; + +} + +message TransformationAddBitInstructionSynonym { + + // A transformation that adds synonyms for bit instructions by evaluating + // each bit with the corresponding operation. There is a SPIR-V code example in the + // header file of the transformation class that can help understand the transformation. + + // This transformation is only applicable if the described instruction has one of the following opcodes. + // Supported: + // OpBitwiseOr + // OpBitwiseXor + // OpBitwiseAnd + // OpNot + // To be supported in the future: + // OpShiftRightLogical + // OpShiftRightArithmetic + // OpShiftLeftLogical + // OpBitReverse + // OpBitCount + + // The bit instruction result id. + uint32 instruction_result_id = 1; + + // The fresh ids required to apply the transformation. + repeated uint32 fresh_ids = 2; + +} + +message TransformationAddConstantBoolean { + + // Supports adding the constants true and false to a module, which may be + // necessary in order to enable other transformations if they are not present. + // Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true. + + uint32 fresh_id = 1; + bool is_true = 2; + + // If the constant should be marked as irrelevant. + bool is_irrelevant = 3; + +} + +message TransformationAddConstantComposite { + + // Adds a constant of the given composite type to the module. + // Also, creates an IdIsIrrelevant fact about |fresh_id| if + // |is_irrelevant| is true. + + // Fresh id for the composite + uint32 fresh_id = 1; + + // A composite type id + uint32 type_id = 2; + + // Constituent ids for the composite + repeated uint32 constituent_id = 3; + + // If the constant should be marked as irrelevant. + bool is_irrelevant = 4; + +} + +message TransformationAddConstantNull { + + // Adds a null constant. + + // Id for the constant + uint32 fresh_id = 1; + + // Type of the constant + uint32 type_id = 2; + +} + +message TransformationAddConstantScalar { + + // Adds a constant of the given scalar type. + // Also, creates an IdIsIrrelevant fact about + // |fresh_id| if |is_irrelevant| is true. + + // Id for the constant + uint32 fresh_id = 1; + + // Id for the scalar type of the constant + uint32 type_id = 2; + + // Value of the constant + repeated uint32 word = 3; + + // If the constant should be marked as irrelevant. + bool is_irrelevant = 4; + +} + +message TransformationAddCopyMemory { + + // Adds an OpCopyMemory instruction into the module. + // Creates either a global or a local variable (based on + // |storage_class| field) to copy the target into. + + // OpCopyMemory will be inserted before this instruction. + InstructionDescriptor instruction_descriptor = 1; + + // Fresh id to copy memory into. + uint32 fresh_id = 2; + + // Source to copy memory from. + uint32 source_id = 3; + + // Storage class for the target variable. Can be either Function or Private. + uint32 storage_class = 4; + + // Result id for the variable's initializer operand. Its type must be equal to + // variable's pointee type. + uint32 initializer_id = 5; + +} + +message TransformationAddDeadBlock { + + // Adds a new block to the module that is statically reachable from an + // existing block, but dynamically unreachable. + + // Fresh id for the dead block + uint32 fresh_id = 1; + + // Id of an existing block terminated with OpBranch, such that this OpBranch + // can be replaced with an OpBranchConditional to its exiting successor or + // the dead block + uint32 existing_block = 2; + + // Determines whether the condition associated with the OpBranchConditional + // is true or false + bool condition_value = 3; + +} + +message TransformationAddDeadBreak { + + // A transformation that turns a basic block that unconditionally branches to + // its successor into a block that potentially breaks out of a structured + // control flow construct, but in such a manner that the break cannot actually + // be taken. + + // The block to break from + uint32 from_block = 1; + + // The merge block to break to + uint32 to_block = 2; + + // Determines whether the break condition is true or false + bool break_condition_value = 3; + + // A sequence of ids suitable for extending OpPhi instructions as a result of + // the new break edge + repeated uint32 phi_id = 4; + +} + +message TransformationAddDeadContinue { + + // A transformation that turns a basic block appearing in a loop and that + // unconditionally branches to its successor into a block that potentially + // branches to the continue target of the loop, but in such a manner that the + // continue branch cannot actually be taken. + + // The block to continue from + uint32 from_block = 1; + + // Determines whether the continue condition is true or false + bool continue_condition_value = 2; + + // A sequence of ids suitable for extending OpPhi instructions as a result of + // the new break edge + repeated uint32 phi_id = 3; + +} + +message TransformationAddEarlyTerminatorWrapper { + + // Adds a function to the module containing a single block with a single non- + // label instruction that is either OpKill, OpUnreachable, or + // OpTerminateInvocation. The purpose of this is to allow such instructions + // to be subsequently replaced with wrapper functions, which can then enable + // transformations (such as inlining) that are hard in the direct presence + // of these instructions. + + // Fresh id for the function. + uint32 function_fresh_id = 1; + + // Fresh id for the single basic block in the function. + uint32 label_fresh_id = 2; + + // One of OpKill, OpUnreachable, OpTerminateInvocation. If additional early + // termination instructions are added to SPIR-V they should also be handled + // here. + uint32 opcode = 3; + +} + +message TransformationAddFunction { + + // Adds a SPIR-V function to the module. + + // The series of instructions that comprise the function. + repeated Instruction instruction = 1; + + // True if and only if the given function should be made livesafe (see + // FactFunctionIsLivesafe for definition). + bool is_livesafe = 2; + + // Fresh id for a new variable that will serve as a "loop limiter" for the + // function; only relevant if |is_livesafe| holds. + uint32 loop_limiter_variable_id = 3; + + // Id of an existing unsigned integer constant providing the maximum value + // that the loop limiter can reach before the loop is broken from; only + // relevant if |is_livesafe| holds. + uint32 loop_limit_constant_id = 4; + + // Fresh ids for each loop in the function that allow the loop limiter to be + // manipulated; only relevant if |is_livesafe| holds. + repeated LoopLimiterInfo loop_limiter_info = 5; + + // Id of an existing global value with the same return type as the function + // that can be used to replace OpKill and OpReachable instructions with + // ReturnValue instructions. Ignored if the function has void return type. + // Only relevant if |is_livesafe| holds. + uint32 kill_unreachable_return_value_id = 6; + + // A mapping (represented as a sequence) from every access chain result id in + // the function to the ids required to clamp its indices to ensure they are in + // bounds; only relevant if |is_livesafe| holds. + repeated AccessChainClampingInfo access_chain_clamping_info = 7; + +} + +message TransformationAddGlobalUndef { + + // Adds an undefined value of a given type to the module at global scope. + + // Fresh id for the undefined value + uint32 fresh_id = 1; + + // The type of the undefined value + uint32 type_id = 2; + +} + +message TransformationAddGlobalVariable { + + // Adds a global variable of the given type to the module, with Private or + // Workgroup storage class, and optionally (for the Private case) with an + // initializer. + + // Fresh id for the global variable + uint32 fresh_id = 1; + + // The type of the global variable + uint32 type_id = 2; + + uint32 storage_class = 3; + + // Initial value of the variable + uint32 initializer_id = 4; + + // True if and only if the behaviour of the module should not depend on the + // value of the variable, in which case stores to the variable can be + // performed in an arbitrary fashion. + bool value_is_irrelevant = 5; + +} + +message TransformationAddImageSampleUnusedComponents { + + // A transformation that adds unused components to an image sample coordinate. + + // An vector id with the original coordinate and the unused components. + uint32 coordinate_with_unused_components_id = 1; + + // A descriptor for an image sample instruction. + InstructionDescriptor instruction_descriptor = 2; + +} + +message TransformationAddLocalVariable { + + // Adds a local variable of the given type (which must be a pointer with + // Function storage class) to the given function, initialized to the given + // id. + + // Fresh id for the local variable + uint32 fresh_id = 1; + + // The type of the local variable + uint32 type_id = 2; + + // The id of the function to which the local variable should be added + uint32 function_id = 3; + + // Initial value of the variable + uint32 initializer_id = 4; + + // True if and only if the behaviour of the module should not depend on the + // value of the variable, in which case stores to the variable can be + // performed in an arbitrary fashion. + bool value_is_irrelevant = 5; + +} + +message TransformationAddLoopPreheader { + + // A transformation that adds a loop preheader block before the given loop header. + + // The id of the loop header block + uint32 loop_header_block = 1; + + // A fresh id for the preheader block + uint32 fresh_id = 2; + + // Fresh ids for splitting the OpPhi instructions in the header. + // A new OpPhi instruction in the preheader is needed for each OpPhi instruction in the header, + // if the header has more than one predecessor outside of the loop. + // This allows turning instructions of the form: + // + // %loop_header_block = OpLabel + // %id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id %val3 %backedge_block_id + // + // into: + // %fresh_id = OpLabel + // %phi_id1 = OpPhi %type %val1 %pred1_id %val2 %pred2_id + // OpBranch %header_id + // %loop_header_block = OpLabel + // %id1 = OpPhi %type %phi_id1 %fresh_id %val3 %backedge_block_id + repeated uint32 phi_id = 3; + +} + +message TransformationAddLoopToCreateIntConstantSynonym { + // A transformation that uses a loop to create a synonym for an integer + // constant C (scalar or vector) using an initial value I, a step value S and + // a number of iterations N such that C = I - N * S. For each iteration, S is + // subtracted from the total. + // The loop can be made up of one or two blocks, and it is inserted before a + // block with a single predecessor. In the one-block case, it is of the form: + // + // %loop_id = OpLabel + // %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id + // %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id + // %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id + // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1 + // %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id + // OpLoopMerge %block_after_loop_id %loop_id None + // OpBranchConditional %cond_id %loop_id %block_after_loop_id + // + // A new OpPhi instruction is then added to %block_after_loop_id, as follows: + // + // %block_after_loop_id = OpLabel + // %syn_id = OpPhi %type_of_I %eventual_syn_id %loop_id + // + // This can be translated, assuming that N > 0, to: + // int syn = I; + // for (int ctr = 0; ctr < N; ctr++) syn = syn - S; + // + // All existing OpPhi instructions in %block_after_loop_id are also updated + // to reflect the fact that its predecessor is now %loop_id. + + // The following are existing ids. + + // The id of the integer constant C that we want a synonym of. + uint32 constant_id = 1; + + // The id of the initial value integer constant I. + uint32 initial_val_id = 2; + + // The id of the step value integer constant S. + uint32 step_val_id = 3; + + // The id of the integer scalar constant, its value being the number of + // iterations N. + uint32 num_iterations_id = 4; + + // The label id of the block before which the loop must be inserted. + uint32 block_after_loop_id = 5; + + + // The following are fresh ids. + + // A fresh id for the synonym. + uint32 syn_id = 6; + + // A fresh id for the label of the loop, + uint32 loop_id = 7; + + // A fresh id for the counter. + uint32 ctr_id = 8; + + // A fresh id taking the value I - S * ctr at the ctr-th iteration. + uint32 temp_id = 9; + + // A fresh id taking the value I - S * (ctr + 1) at the ctr-th iteration, and + // thus I - S * N at the last iteration. + uint32 eventual_syn_id = 10; + + // A fresh id for the incremented counter. + uint32 incremented_ctr_id = 11; + + // A fresh id for the loop condition. + uint32 cond_id = 12; + + // The instructions in the loop can also be laid out in two basic blocks, as follows: + // + // %loop_id = OpLabel + // %ctr_id = OpPhi %int %int_0 %pred %incremented_ctr_id %loop_id + // %temp_id = OpPhi %type_of_I %I %pred %eventual_syn_id %loop_id + // OpLoopMerge %block_after_loop_id %additional_block_id None + // OpBranch %additional_block_id + // + // %additional_block_id = OpLabel + // %eventual_syn_id = OpISub %type_of_I %temp_id %step_val_id + // %incremented_ctr_id = OpIAdd %int %ctr_id %int_1 + // %cond_id = OpSLessThan %bool %incremented_ctr_id %num_iterations_id + // OpBranchConditional %cond_id %loop_id %block_after_loop_id + + // A fresh id for the additional block. If this is 0, it means that only one + // block is to be created. + uint32 additional_block_id = 13; +} + +message TransformationAddNoContractionDecoration { + + // Applies OpDecorate NoContraction to the given result id + + // Result id to be decorated + uint32 result_id = 1; + +} + +message TransformationAddOpPhiSynonym { + + // Adds an OpPhi instruction at the start of a block with n predecessors (pred_1, pred_2, ..., pred_n) + // and n related ids (id_1, id_2, ..., id_n) which are pairwise synonymous. + // The instruction will be of the form: + // %fresh_id = OpPhi %type %id_1 %pred_1 %id_2 %pred_2 ... %id_n %pred_n + // and fresh_id will be recorded as being synonymous with all the other ids. + + // Label id of the block + uint32 block_id = 1; + + // Pairs (pred_i, id_i) + repeated UInt32Pair pred_to_id = 2; + + // Fresh id for the new instruction + uint32 fresh_id = 3; +} + +message TransformationAddParameter { + + // Adds a new parameter into the function. + + // Result id of the function to add parameters to. + uint32 function_id = 1; + + // Fresh id for a new parameter. + uint32 parameter_fresh_id = 2; + + // Type id for a new parameter. + uint32 parameter_type_id = 3; + + // A map that maps from the OpFunctionCall id to the id that will be passed as the new + // parameter at that call site. It must have the same type as that of the new parameter. + repeated UInt32Pair call_parameter_ids = 4; + + // A fresh id for a new function type. This might not be used + // if a required function type already exists or if we can change + // the old function type. + uint32 function_type_fresh_id = 5; + +} + +message TransformationAddRelaxedDecoration { + + // Applies OpDecorate RelaxedPrecision to the given result id + + // Result id to be decorated + uint32 result_id = 1; + +} + +message TransformationAddSpecConstantOp { + + // Adds OpSpecConstantOp into the module. + + // Result id for the new instruction. + uint32 fresh_id = 1; + + // Type id for the new instruction. + uint32 type_id = 2; + + // Opcode operand of the OpSpecConstantOp instruction. + uint32 opcode = 3; + + // Operands of the |opcode| instruction. + repeated InstructionOperand operand = 4; + +} + +message TransformationAddSynonym { + + // Adds a |synonymous_instruction| before |insert_before| instruction with + // and creates a fact that |result_id| and the result id of |synonymous_instruction| + // are synonymous. + + // Result id of the first synonym. + uint32 result_id = 1; + + // Type of the synonym to apply. Some types might produce instructions + // with commutative operands. Such types do not specify the order of the + // operands since we have a special transformation to swap commutable operands. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3499): + // Consider adding more types here. + enum SynonymType { + // New synonym is derived by adding zero to the |result_id|. + ADD_ZERO = 0; + + // New synonym is derived by subtracting zero from the |result_id|. + SUB_ZERO = 1; + + // New synonym is derived by multiplying |result_id| by one. + MUL_ONE = 2; + + // New synonym is derived by applying OpCopyObject instruction to |result_id|. + COPY_OBJECT = 3; + + // New synonym is derived by applying OpLogicalOr to |result_id| with the second + // operand being 'false'. + LOGICAL_OR = 4; + + // New synonym is derived by applying OpLogicalAnd to |result_id| with the second + // operand being 'true'. + LOGICAL_AND = 5; + } + + // Type of the synonym to create. See SynonymType for more details. + SynonymType synonym_type = 2; + + // Fresh result id for a created synonym. + uint32 synonym_fresh_id = 3; + + // An instruction to insert a new synonym before. + InstructionDescriptor insert_before = 4; + +} + +message TransformationAddTypeArray { + + // Adds an array type of the given element type and size to the module + + // Fresh id for the array type + uint32 fresh_id = 1; + + // The array's element type + uint32 element_type_id = 2; + + // The array's size + uint32 size_id = 3; + +} + +message TransformationAddTypeBoolean { + + // Adds OpTypeBool to the module + + // Id to be used for the type + uint32 fresh_id = 1; + +} + +message TransformationAddTypeFloat { + + // Adds OpTypeFloat to the module with the given width + + // Id to be used for the type + uint32 fresh_id = 1; + + // Floating-point width + uint32 width = 2; + +} + +message TransformationAddTypeFunction { + + // Adds a function type to the module + + // Fresh id for the function type + uint32 fresh_id = 1; + + // The function's return type + uint32 return_type_id = 2; + + // The function's argument types + repeated uint32 argument_type_id = 3; + +} + +message TransformationAddTypeInt { + + // Adds OpTypeInt to the module with the given width and signedness + + // Id to be used for the type + uint32 fresh_id = 1; + + // Integer width + uint32 width = 2; + + // True if and only if this is a signed type + bool is_signed = 3; + +} + +message TransformationAddTypeMatrix { + + // Adds a matrix type to the module + + // Fresh id for the matrix type + uint32 fresh_id = 1; + + // The matrix's column type, which must be a floating-point vector (as per + // the "data rules" in the SPIR-V specification). + uint32 column_type_id = 2; + + // The matrix's column count + uint32 column_count = 3; + +} + +message TransformationAddTypePointer { + + // Adds OpTypePointer to the module, with the given storage class and base + // type + + // Id to be used for the type + uint32 fresh_id = 1; + + // Pointer storage class + uint32 storage_class = 2; + + // Id of the base type for the pointer + uint32 base_type_id = 3; + +} + +message TransformationAddTypeStruct { + + // Adds a struct type to the module + + // Fresh id for the struct type + uint32 fresh_id = 1; + + // The struct's member types + repeated uint32 member_type_id = 3; + +} + +message TransformationAddTypeVector { + + // Adds a vector type to the module + + // Fresh id for the vector type + uint32 fresh_id = 1; + + // The vector's component type + uint32 component_type_id = 2; + + // The vector's component count + uint32 component_count = 3; + +} + +message TransformationAdjustBranchWeights { + + // A transformation that adjusts the branch weights + // of a branch conditional instruction. + + // A descriptor for a branch conditional instruction. + InstructionDescriptor instruction_descriptor = 1; + + // Branch weights of a branch conditional instruction. + UInt32Pair branch_weights = 2; + +} + +message TransformationCompositeConstruct { + + // A transformation that introduces an OpCompositeConstruct instruction to + // make a composite object. + + // Id of the type of the composite that is to be constructed + uint32 composite_type_id = 1; + + // Ids of the objects that will form the components of the composite + repeated uint32 component = 2; + + // A descriptor for an instruction in a block before which the new + // OpCompositeConstruct instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 3; + + // A fresh id for the composite object + uint32 fresh_id = 4; + +} + +message TransformationCompositeExtract { + + // A transformation that adds an instruction to extract an element from a + // composite. + + // A descriptor for an instruction in a block before which the new + // OpCompositeExtract instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 1; + + // Result id for the extract operation. + uint32 fresh_id = 2; + + // Id of the composite from which data is to be extracted. + uint32 composite_id = 3; + + // Indices that indicate which part of the composite should be extracted. + repeated uint32 index = 4; + +} + +message TransformationCompositeInsert { + + // A transformation that adds an instruction OpCompositeInsert which creates + // a new composite from an existing composite, with an element inserted. + + // A descriptor for an instruction before which the new instruction + // OpCompositeInsert should be inserted. + InstructionDescriptor instruction_to_insert_before = 1; + + // Result id of the inserted OpCompositeInsert instruction. + uint32 fresh_id = 2; + + // Id of the composite used as the basis for the insertion. + uint32 composite_id = 3; + + // Id of the object to be inserted. + uint32 object_id = 4; + + // Indices that indicate which part of the composite should be inserted into. + repeated uint32 index = 5; + +} + +message TransformationComputeDataSynonymFactClosure { + + // A transformation that impacts the fact manager only, forcing a computation + // of the closure of data synonym facts, so that e.g. if the components of + // vectors v and w are known to be pairwise synonymous, it is deduced that v + // and w are themselves synonymous. + + // When searching equivalence classes for implied facts, equivalence classes + // larger than this size will be skipped. + uint32 maximum_equivalence_class_size = 1; + +} + +message TransformationDuplicateRegionWithSelection { + + // A transformation that inserts a conditional statement with a boolean expression + // of arbitrary value and duplicates a given single-entry, single-exit region, so + // that it is present in each conditional branch and will be executed regardless + // of which branch will be taken. + + // Fresh id for a label of the new entry block. + uint32 new_entry_fresh_id = 1; + + // Id for a boolean expression. + uint32 condition_id = 2; + + // Fresh id for a label of the merge block of the conditional. + uint32 merge_label_fresh_id = 3; + + // Block id of the entry block of the original region. + uint32 entry_block_id = 4; + + // Block id of the exit block of the original region. + uint32 exit_block_id = 5; + + // Map that maps from a label in the original region to the corresponding label + // in the duplicated region. + repeated UInt32Pair original_label_to_duplicate_label = 6; + + // Map that maps from a result id in the original region to the corresponding + // result id in the duplicated region. + repeated UInt32Pair original_id_to_duplicate_id = 7; + + // Map that maps from a result id in the original region to the result id of the + // corresponding OpPhi instruction. + repeated UInt32Pair original_id_to_phi_id = 8; +} + +message TransformationEquationInstruction { + + // A transformation that adds an instruction to the module that defines an + // equation between its result id and input operand ids, such that the + // equation is guaranteed to hold at any program point where all ids involved + // are available (i.e. at any program point dominated by the instruction). + + // The result id of the new instruction + uint32 fresh_id = 1; + + // The instruction's opcode + uint32 opcode = 2; + + // The input operands to the instruction + repeated uint32 in_operand_id = 3; + + // A descriptor for an instruction in a block before which the new + // instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + +} + +message TransformationExpandVectorReduction { + + // A transformation that adds synonyms for OpAny and OpAll instructions by + // evaluating each vector component with the corresponding logical operation. + // There is a SPIR-V code example in the header file of the transformation + // class that can help understand the transformation. + + // The OpAny or OpAll instruction result id. + uint32 instruction_result_id = 1; + + // The fresh ids required to apply the transformation. + repeated uint32 fresh_ids = 2; + +} + +message TransformationFlattenConditionalBranch { + + // A transformation that takes a selection construct with a header + // containing an OpBranchConditional instruction and flattens it. + // For example, something of the form: + // + // %1 = OpLabel + // [header instructions] + // OpSelectionMerge %4 None + // OpBranchConditional %cond %2 %3 + // %2 = OpLabel + // [true branch instructions] + // OpBranch %4 + // %3 = OpLabel + // [false branch instructions] + // OpBranch %4 + // %4 = OpLabel + // ... + // + // becomes: + // + // %1 = OpLabel + // [header instructions] + // OpBranch %2 + // %2 = OpLabel + // [true branch instructions] + // OpBranch %3 + // %3 = OpLabel + // [false branch instructions] + // OpBranch %4 + // %4 = OpLabel + // ... + // + // If all of the instructions in the true or false branches have + // no side effects, this is semantics-preserving. + // Side-effecting instructions will instead be enclosed by smaller + // conditionals. For more details, look at the definition for the + // SideEffectWrapperInfo message. + // + // Nested conditionals or loops are not supported. The false branch + // could also be executed before the true branch, depending on the + // |true_branch_first| field. + + // The label id of the header block + uint32 header_block_id = 1; + + // A boolean field deciding the order in which the original branches + // will be laid out: the true branch will be laid out first iff this + // field is true. + bool true_branch_first = 2; + + // If the convergence block contains an OpPhi with bvec2 result type, it may + // be necessary to introduce a bvec2 with the selection construct's condition + // in both components in order to turn the OpPhi into an OpSelect. This + // this field provides a fresh id for an OpCompositeConstruct instruction for + // this purpose. It should be set to 0 if no such instruction is required. + uint32 fresh_id_for_bvec2_selector = 3; + + // The same as |fresh_id_for_bvec2_selector| but for the bvec3 case. + uint32 fresh_id_for_bvec3_selector = 4; + + // The same as |fresh_id_for_bvec2_selector| but for the bvec4 case. + uint32 fresh_id_for_bvec4_selector = 5; + + // A list of instructions with side effects, which must be enclosed + // inside smaller conditionals before flattening the main one, and + // the corresponding fresh ids and module ids needed. + repeated SideEffectWrapperInfo side_effect_wrapper_info = 6; +} + +message TransformationFunctionCall { + + // A transformation that introduces an OpFunctionCall instruction. The call + // must not make the module's call graph cyclic. Beyond that, if the call + // is in a dead block it can be to any function with arbitrary suitably-typed + // arguments; otherwise it must be to a livesafe function, with injected + // variables as pointer arguments and arbitrary non-pointer arguments. + + // A fresh id for the result of the call + uint32 fresh_id = 1; + + // Id of the function to be called + uint32 callee_id = 2; + + // Ids for arguments to the function + repeated uint32 argument_id = 3; + + // A descriptor for an instruction in a block before which the new + // OpFunctionCall instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 4; + +} + +message TransformationInlineFunction { + + // This transformation inlines a function by mapping the function instructions to fresh ids. + + // Result id of the function call instruction. + uint32 function_call_id = 1; + + // For each result id defined by the called function, + // this map provides an associated fresh id that can + // be used in the inlined version of the function call. + repeated UInt32Pair result_id_map = 2; + +} + +message TransformationInvertComparisonOperator { + + // For some instruction with result id |operator_id| that + // represents a binary comparison operator (e.g. <, >, <=), this transformation + // will replace that instruction's result id with |fresh_id|, + // invert the opcode (< will become >=) and insert OpLogicalNot + // instruction with result id |operator_id| below. + + // Result id of the instruction to invert. + uint32 operator_id = 1; + + // Fresh id that will be used by the operator after the inversion. + uint32 fresh_id = 2; + +} + +message TransformationLoad { + + // Transformation that adds an OpLoad instruction from a pointer into an id. + + // The result of the load instruction + uint32 fresh_id = 1; + + // The pointer to be loaded from + uint32 pointer_id = 2; + + // A descriptor for an instruction in a block before which the new OpLoad + // instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 3; + +} + +message TransformationMakeVectorOperationDynamic { + + // A transformation that replaces the OpCompositeExtract and OpCompositeInsert + // instructions with the OpVectorExtractDynamic and OpVectorInsertDynamic instructions. + + // The composite instruction result id. + uint32 instruction_result_id = 1; + + // The OpCompositeExtract/Insert instructions accept integer literals as indices to the composite object. + // However, the OpVectorInsert/ExtractDynamic instructions require its single index to be an integer instruction. + // This is the result id of the integer instruction. + uint32 constant_index_id = 2; + +} + +message TransformationMergeBlocks { + + // A transformation that merges a block with its predecessor. + + // The id of the block that is to be merged with its predecessor; the merged + // block will have the *predecessor's* id. + uint32 block_id = 1; + +} + +message TransformationMergeFunctionReturns { + + // A transformation that modifies a function so that it does not return early, + // so it only has one return statement (ignoring unreachable blocks). + // + // The function is enclosed inside an outer loop, that is only executed once, + // and whose merge block is the new return block of the function. + // + // Each return instruction is replaced by: + // OpBranch %innermost_loop_merge + // where %innermost_loop_merge is the innermost loop containing the return + // instruction. + // + // Each merge block whose associated loop contains return instructions is + // changed so that it branches to the merge block of the loop containing it, + // as explained in the comments to the ReturnMergingInfo message. + // + // The new return block (the merge block of the new outer loop) will be of + // the following form (if the return type is not void): + // %outer_return_id = OpLabel + // %return_val_id = OpPhi %return_type %val1 %block_1 %val2 %block_2 ... + // OpReturnValue %return_val_id + // where %block_k is either a return block that, in the original function, is + // outside of any loops, or the merge block of a loop that contains return + // instructions and is not, originally, nested inside another loop, and + // %block_k is the corresponding return value. + // If the function has void type, there will be no OpPhi instruction and the + // last instruction will be OpReturn. + + // The id of the function to which the transformation is being applied. + uint32 function_id = 1; + + // A fresh id for the header of the new outer loop. + uint32 outer_header_id = 2; + + // A fresh id for the new return block of the function, + // i.e. the merge block of the new outer loop. + uint32 outer_return_id = 3; + + // A fresh id for the value that will be returned. + // This is ignored if the function has void return type. + uint32 return_val_id = 4; + + // An existing id of the same type as the return value, which is + // available to use at the end of the entry block. + // This is ignored if the function has void return type or if no + // loops in the function contain a return instruction. + // If the function is not void, the transformation will add an + // OpPhi instruction to each merge block whose associated loop + // contains at least a return instruction. The value associated + // with existing predecessors from which the function cannot be + // returning will be this id, used as a placeholder. + uint32 any_returnable_val_id = 5; + + // The information needed to modify the merge blocks of + // loops containing return instructions. + repeated ReturnMergingInfo return_merging_info = 6; +} + +message TransformationMoveBlockDown { + + // A transformation that moves a basic block to be one position lower in + // program order. + + // The id of the block to move down. + uint32 block_id = 1; +} + +message TransformationMoveInstructionDown { + + // Swaps |instruction| with the next instruction in the block. + + // The instruction to move down. + InstructionDescriptor instruction = 1; + +} + +message TransformationMutatePointer { + + // Backs up value of the pointer, writes into the pointer and + // restores the original value. + + // Result id of the pointer instruction to mutate. + uint32 pointer_id = 1; + + // Fresh id for the OpLoad instruction. + uint32 fresh_id = 2; + + // Instruction to insert backup, mutation and restoration code before. + InstructionDescriptor insert_before = 3; + +} + +message TransformationOutlineFunction { + + // A transformation that outlines a single-entry single-exit region of a + // control flow graph into a separate function, and replaces the region with + // a call to that function. + + // Id of the entry block of the single-entry single-exit region to be outlined + uint32 entry_block = 1; + + // Id of the exit block of the single-entry single-exit region to be outlined + uint32 exit_block = 2; + + // Id of a struct that will store the return values of the new function + uint32 new_function_struct_return_type_id = 3; + + // A fresh id for the type of the outlined function + uint32 new_function_type_id = 4; + + // A fresh id for the outlined function itself + uint32 new_function_id = 5; + + // A fresh id to represent the block in the outlined function that represents + // the first block of the outlined region. + uint32 new_function_region_entry_block = 6; + + // A fresh id for the result of the OpFunctionCall instruction that will call + // the outlined function + uint32 new_caller_result_id = 7; + + // A fresh id to capture the return value of the outlined function - the + // argument to OpReturn + uint32 new_callee_result_id = 8; + + // Ids defined outside the region and used inside the region will become + // parameters to the outlined function. This is a mapping from used ids to + // fresh parameter ids. + repeated UInt32Pair input_id_to_fresh_id = 9; + + // Ids defined inside the region and used outside the region will become + // fresh ids defined by the outlined function, which get copied into the + // function's struct return value and then copied into their destination ids + // by the caller. This is a mapping from original ids to corresponding fresh + // ids. + repeated UInt32Pair output_id_to_fresh_id = 10; + +} + +message TransformationPermuteFunctionParameters { + + // A transformation that, given a non-entry-point function taking n + // parameters and a permutation of the set [0, n-1]: + // - Introduces a new function type that is the same as the original + // function's type but with the order of arguments permuted + // (only if it doesn't already exist) + // - Changes the type of the function to this type + // - Adjusts all calls to the function so that their arguments are permuted + + // Function, whose parameters will be permuted + uint32 function_id = 1; + + // Fresh id for a new type of the function. This might not be used + // if a required function type already exists or if we can change + // the old function type. + uint32 function_type_fresh_id = 2; + + // An array of size |n|, where |n| is a number of arguments to a function + // with |function_id|. For each i: 0 <= permutation[i] < n. + // + // i-th element of this array contains a position for an i-th + // function's argument (i.e. i-th argument will be permutation[i]-th + // after running this transformation) + repeated uint32 permutation = 3; + +} + +message TransformationPermutePhiOperands { + + // Permutes operands of some OpPhi instruction. + + // Result id of the instruction to apply the transformation to. + uint32 result_id = 1; + + // A sequence of numbers in the range [0, n/2 - 1] where |n| is the number + // of operands of the OpPhi instruction with |result_id|. + repeated uint32 permutation = 2; + +} + +message TransformationPropagateInstructionDown { + + // Propagates an instruction from |block_id| into its successors. + // Concretely, the transformation clones the propagated instruction + // into some of the successors of |block_id| and removes the original + // instruction. Additionally, an OpPhi instruction may be added to make sure + // that the transformation can be applied in various scenarios. + // + // Note that the instruction might not be propagated down into every successor + // of |block_id| since it might make the module invalid. + + // Id of the block to propagate an instruction from. The decision on what + // instruction to propagate is made based on whether the instruction interacts + // with memory, whether that instruction is used in its block etc (see the + // transformation class for more details). + uint32 block_id = 1; + + // A fresh id for an OpPhi instruction. This might not be used by the + // transformation since an OpPhi instruction is created only if needed + // (e.g. an instruction is propagated into divergent blocks). + uint32 phi_fresh_id = 2; + + // A map from the id of some successor of the |block_id| to the fresh id. + // The map contains a fresh id for at least every successor of the |block_id|. + // Every fresh id in the map corresponds to the result id of the clone, + // propagated into the corresponding successor block. This transformation + // might use overflow ids if they are available and this field doesn't account + // for every successor of |block_id|. + repeated UInt32Pair successor_id_to_fresh_id = 3; + +} + +message TransformationPropagateInstructionUp { + + // Propagates an instruction in the block into the block's predecessors. + // Concretely, this transformation clones some particular instruction from + // the |block_id| into every block's predecessor and replaces the original + // instruction with OpPhi. Take a look at the transformation class to learn + // more about how we choose what instruction to propagate. + + // Id of the block to propagate an instruction from. + uint32 block_id = 1; + + // A map from the id of some predecessor of the |block_id| to the fresh id. + // The map contains a fresh id for at least every predecessor of the |block_id|. + // The instruction is propagated by creating a number of clones - one clone for + // each predecessor. Fresh ids from this field are used as result ids of cloned + // instructions. + repeated UInt32Pair predecessor_id_to_fresh_id = 2; + +} + +message TransformationPushIdThroughVariable { + + // A transformation that makes |value_synonym_id| and |value_id| to be + // synonymous by storing |value_id| into |variable_id| and + // loading |variable_id| to |value_synonym_id|. + + // The value to be stored. + uint32 value_id = 1; + + // A fresh id for the result of the load instruction. + uint32 value_synonym_id = 2; + + // A fresh id for the variable to be stored to. + uint32 variable_id = 3; + + // Constant to initialize the variable from. + uint32 initializer_id = 4; + + // The variable storage class (global or local). + uint32 variable_storage_class = 5; + + // A descriptor for an instruction which the new OpStore + // and OpLoad instructions might be inserted before. + InstructionDescriptor instruction_descriptor = 6; + +} + +message TransformationRecordSynonymousConstants { + + // A transformation that, given the IDs to two synonymous constants, + // records the fact that they are synonymous. The module is not changed. + // Two constants are synonymous if: + // - they have the same type (ignoring the presence of integer sign) + // - they have the same opcode (one of OpConstant, OpConstantTrue, + // OpConstantFalse, OpConstantNull) + // - they have the same value + // If the types are the same, OpConstantNull is equivalent to + // OpConstantFalse or OpConstant with value zero. + + // The id of a constant + uint32 constant1_id = 1; + + // The id of the synonym + uint32 constant2_id = 2; + +} + +message TransformationReplaceAddSubMulWithCarryingExtended { + + // Replaces OpIAdd with OpIAddCarry, OpISub with OpISubBorrow, OpIMul + // with OpUMulExtended or OpSMulExtended (depending on the signedness + // of the operands) and stores the result into a |struct_fresh_id|. + // In the original instruction the result type id and the type ids of + // the operands must be the same. Then the transformation extracts + // the first element of the result into the original |result_id|. + // This value is the same as the result of the original instruction. + + // The fresh id of the intermediate result. + uint32 struct_fresh_id = 1; + + // The result id of the original instruction. + uint32 result_id = 2; + +} + +message TransformationReplaceBranchFromDeadBlockWithExit { + + // Given a dead block that ends with OpBranch, replaces OpBranch with an + // "exit" instruction; one of OpReturn/OpReturnValue, OpKill (in a fragment + // shader) or OpUnreachable. + + // The dead block whose terminator is to be replaced. + uint32 block_id = 1; + + // The opcode of the new terminator. + uint32 opcode = 2; + + // Ignored unless opcode is OpReturnValue, in which case this field provides + // a suitable result id to be returned. + uint32 return_value_id = 3; + +} + +message TransformationReplaceParameterWithGlobal { + + // Removes parameter with result id |parameter_id| from its function + // and creates a global variable to pass its value to the function instead. + + // Fresh id for a new function type. This might not be used if a required + // function type already exists or if we can change the old function type. + uint32 function_type_fresh_id = 2; + + // Result id of the OpFunctionParameter instruction to remove. + uint32 parameter_id = 3; + + // Fresh id of a global variable used to pass parameter's value to the function. + uint32 global_variable_fresh_id = 4; + +} + +message TransformationReplaceBooleanConstantWithConstantBinary { + + // A transformation to capture replacing a use of a boolean constant with + // binary operation on two constant values + + // A descriptor for the boolean constant id we would like to replace + IdUseDescriptor id_use_descriptor = 1; + + // Id for the constant to be used on the LHS of the comparision + uint32 lhs_id = 2; + + // Id for the constant to be used on the RHS of the comparision + uint32 rhs_id = 3; + + // Opcode for binary operator + uint32 opcode = 4; + + // Id that will store the result of the binary operation instruction + uint32 fresh_id_for_binary_operation = 5; + +} + +message TransformationReplaceConstantWithUniform { + + // Replaces a use of a constant id with the result of a load from an + // element of uniform buffer known to hold the same value as the constant + + // A descriptor for the id we would like to replace + IdUseDescriptor id_use_descriptor = 1; + + // Uniform descriptor to identify which uniform value to choose + UniformBufferElementDescriptor uniform_descriptor = 2; + + // Id that will store the result of an access chain + uint32 fresh_id_for_access_chain = 3; + + // Id that will store the result of a load + uint32 fresh_id_for_load = 4; + +} + +message TransformationReplaceCopyMemoryWithLoadStore { + + // A transformation that replaces instructions OpCopyMemory with loading + // the source variable to an intermediate value and storing this value into the + // target variable of the original OpCopyMemory instruction. + + // The intermediate value. + uint32 fresh_id = 1; + + // The instruction descriptor to OpCopyMemory. It is necessary, because + // OpCopyMemory doesn't have a result id. + InstructionDescriptor copy_memory_instruction_descriptor = 2; +} + +message TransformationReplaceCopyObjectWithStoreLoad { + + // A transformation that replaces instruction OpCopyObject with + // storing into a new variable and immediately loading from this + // variable to |result_id| of the original OpCopyObject instruction. + + // The result id of initial OpCopyObject instruction + uint32 copy_object_result_id = 1; + + // A fresh id for the variable to be stored to. + uint32 fresh_variable_id = 2; + + // The variable storage class (Function or Private). + uint32 variable_storage_class = 3; + + // Constant to initialize the variable with. + uint32 variable_initializer_id = 4; +} + +message TransformationReplaceIdWithSynonym { + + // Replaces a use of an id with an id that is known to be synonymous, e.g. + // because it was obtained via applying OpCopyObject + + // The id use that is to be replaced + IdUseDescriptor id_use_descriptor = 1; + + // The synonymous id + uint32 synonymous_id = 2; + +} + +message TransformationReplaceIrrelevantId { + + // Replaces an irrelevant id with another id of the same type. + + // The id use that is to be replaced + IdUseDescriptor id_use_descriptor = 1; + + // The replacement id + uint32 replacement_id = 2; +} + +message TransformationReplaceLinearAlgebraInstruction { + + // Replaces a linear algebra instruction with its + // mathematical definition. + + // The fresh ids needed to apply the transformation. + repeated uint32 fresh_ids = 1; + + // A descriptor for a linear algebra instruction. + InstructionDescriptor instruction_descriptor = 2; + +} + +message TransformationReplaceLoadStoreWithCopyMemory { + // A transformation that takes a pair of instruction descriptors + // to OpLoad and OpStore that have the same intermediate value + // and replaces the OpStore with an equivalent OpCopyMemory. + + // The instruction descriptor to OpLoad + InstructionDescriptor load_instruction_descriptor = 1; + + // The instruction descriptor to OpStore + InstructionDescriptor store_instruction_descriptor = 2; +} + +message TransformationReplaceOpPhiIdFromDeadPredecessor { + + // Replaces one of the ids used by an OpPhi instruction, when + // the corresponding predecessor is dead, with any available id + // of the correct type. + + // The result id of the OpPhi instruction. + uint32 opphi_id = 1; + + // The label id of one of the predecessors of the block containing + // the OpPhi instruction, corresponding to the id that we want to + // replace. + uint32 pred_label_id = 2; + + // The id that, after the transformation, will be associated with + // the given predecessor. + uint32 replacement_id = 3; + +} + +message TransformationReplaceOpSelectWithConditionalBranch { + + // A transformation that takes an OpSelect instruction with a + // scalar boolean condition and replaces it with a conditional + // branch and an OpPhi instruction. + // The OpSelect instruction must be the first instruction in its + // block, which must have a unique predecessor. The block will + // become the merge block of a new construct, while its predecessor + // will become the header. + // Given the original OpSelect instruction: + // %id = OpSelect %type %cond %then %else + // The branching instruction of the header will be: + // OpBranchConditional %cond %true_block_id %false_block_id + // and the OpSelect instruction will be turned into: + // %id = OpPhi %type %then %true_block_id %else %false_block_id + // At most one of |true_block_id| and |false_block_id| can be zero. In + // that case, there will be no such block and all references to it + // will be replaced by %merge_block (where %merge_block is the + // block containing the OpSelect instruction). + + // The result id of the OpSelect instruction. + uint32 select_id = 1; + + // A fresh id for the new block that the predecessor of the block + // containing |select_id| will branch to if the condition holds. + uint32 true_block_id = 2; + + // A fresh id for the new block that the predecessor of the block + // containing |select_id| will branch to if the condition does not + // hold. + uint32 false_block_id = 3; +} + +message TransformationReplaceParamsWithStruct { + + // Replaces parameters of the function with a struct containing + // values of those parameters. + + // Result ids of parameters to replace. + repeated uint32 parameter_id = 1; + + // Fresh id for a new function type. This might be unused if the required type + // already exists in the module or if we can change the old type. + uint32 fresh_function_type_id = 2; + + // Fresh id for a new struct function parameter to be used as a replacement. + uint32 fresh_parameter_id = 3; + + // Fresh ids for struct objects containing values of replaced parameters. + // This field contains a fresh id for at least every result id of a relevant + // OpFunctionCall instruction. + repeated UInt32Pair caller_id_to_fresh_composite_id = 4; + +} + +message TransformationSetFunctionControl { + + // A transformation that sets the function control operand of an OpFunction + // instruction. + + // The result id of an OpFunction instruction + uint32 function_id = 1; + + // The value to which the 'function control' operand should be set. + uint32 function_control = 2; + +} + +message TransformationSetLoopControl { + + // A transformation that sets the loop control operand of an OpLoopMerge + // instruction. + + // The id of a basic block that should contain OpLoopMerge + uint32 block_id = 1; + + // The value to which the 'loop control' operand should be set. + // This must be a legal loop control mask. + uint32 loop_control = 2; + + // Provides a peel count value for the loop. Used if and only if the + // PeelCount bit is set. Must be zero if the PeelCount bit is not set (can + // still be zero if this bit is set). + uint32 peel_count = 3; + + // Provides a partial count value for the loop. Used if and only if the + // PartialCount bit is set. Must be zero if the PartialCount bit is not set + // (can still be zero if this bit is set). + uint32 partial_count = 4; + +} + +message TransformationSetMemoryOperandsMask { + + // A transformation that sets the memory operands mask of a memory access + // instruction. + + // A descriptor for a memory access instruction, e.g. an OpLoad + InstructionDescriptor memory_access_instruction = 1; + + // A mask of memory operands to be applied to the instruction. It must be the + // same as the original mask, except that Volatile can be added, and + // Nontemporal can be added or removed. + uint32 memory_operands_mask = 2; + + // Some memory access instructions allow more than one mask to be specified; + // this field indicates which mask should be set + uint32 memory_operands_mask_index = 3; + +} + +message TransformationSetSelectionControl { + + // A transformation that sets the selection control operand of an + // OpSelectionMerge instruction. + + // The id of a basic block that should contain OpSelectionMerge + uint32 block_id = 1; + + // The value to which the 'selection control' operand should be set. + // Although technically 'selection control' is a literal mask that can be + // some combination of 'None', 'Flatten' and 'DontFlatten', the combination + // 'Flatten | DontFlatten' does not make sense and is not allowed here. + uint32 selection_control = 2; + +} + +message TransformationSplitBlock { + + // A transformation that splits a basic block into two basic blocks + + // A descriptor for an instruction such that the block containing the + // described instruction should be split right before the instruction. + InstructionDescriptor instruction_to_split_before = 1; + + // An id that must not yet be used by the module to which this transformation + // is applied. Rather than having the transformation choose a suitable id on + // application, we require the id to be given upfront in order to facilitate + // reducing fuzzed shaders by removing transformations. The reason is that + // future transformations may refer to the fresh id introduced by this + // transformation, and if we end up changing what that id is, due to removing + // earlier transformations, it may inhibit later transformations from + // applying. + uint32 fresh_id = 2; + +} + +message TransformationStore { + + // Transformation that adds an OpStore instruction of an id to a pointer. + + // The pointer to be stored to + uint32 pointer_id = 1; + + // The value to be stored + uint32 value_id = 2; + + // A descriptor for an instruction in a block before which the new OpStore + // instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 3; + +} + +message TransformationSwapCommutableOperands { + + // A transformation that swaps the operands of a commutative instruction. + + // A descriptor for a commutative instruction + InstructionDescriptor instruction_descriptor = 1; + +} + +message TransformationSwapConditionalBranchOperands { + + // Swaps label ids in OpBranchConditional instruction. + // Additionally, inverts the guard and swaps branch weights + // if present. + + // Descriptor of the instruction to swap operands of. + InstructionDescriptor instruction_descriptor = 1; + + // Fresh result id for the OpLogicalNot instruction, used + // to invert the guard. + uint32 fresh_id = 2; + +} + +message TransformationToggleAccessChainInstruction { + + // A transformation that toggles an access chain instruction. + + // A descriptor for an access chain instruction + InstructionDescriptor instruction_descriptor = 1; + +} + +message TransformationVectorShuffle { + + // A transformation that adds a vector shuffle instruction. + + // A descriptor for an instruction in a block before which the new + // OpVectorShuffle instruction should be inserted + InstructionDescriptor instruction_to_insert_before = 1; + + // Result id for the shuffle operation. + uint32 fresh_id = 2; + + // Id of the first vector operand. + uint32 vector1 = 3; + + // Id of the second vector operand. + uint32 vector2 = 4; + + // Indices that indicate which components of the input vectors should be used. + repeated uint32 component = 5; + +} + +message TransformationWrapEarlyTerminatorInFunction { + + // Replaces an early terminator - OpKill, OpReachable or OpTerminateInvocation + // - with a call to a wrapper function for the terminator. + + // A fresh id for a new OpFunctionCall instruction. + uint32 fresh_id = 1; + + // A descriptor for an OpKill, OpUnreachable or OpTerminateInvocation + // instruction. + InstructionDescriptor early_terminator_instruction = 2; + + // An id with the same type as the enclosing function's return type that is + // available at the early terminator. This is used to change the terminator + // to OpReturnValue. Ignored if the enclosing function has void return type, + // in which case OpReturn can be used as the new terminator. + uint32 returned_value_id = 3; + +} + +message TransformationWrapRegionInSelection { + + // Transforms a single-entry-single-exit region R into + // if (|branch_condition|) { R } else { R } + // The entry block for R becomes a selection header and + // the exit block - a selection merge. + // + // Note that the region R is not duplicated. Thus, the effect of + // this transformation can be represented as follows: + // entry + // entry / \ + // | \ / + // R --> R + // | | + // exit exit + + // This behaviour is different from TransformationDuplicateRegionWithSelection + // that copies the blocks in R. + + // The entry block for the region R. + uint32 region_entry_block_id = 1; + + // The exit block for the region R. + uint32 region_exit_block_id = 2; + + // Boolean value for the condition expression. + bool branch_condition = 3; + +} diff --git a/third_party/spirv-tools/source/fuzz/pseudo_random_generator.cpp b/third_party/spirv-tools/source/fuzz/pseudo_random_generator.cpp new file mode 100644 index 0000000..51f0538 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pseudo_random_generator.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/pseudo_random_generator.h" + +#include + +namespace spvtools { +namespace fuzz { + +PseudoRandomGenerator::PseudoRandomGenerator(uint32_t seed) : mt_(seed) {} + +PseudoRandomGenerator::~PseudoRandomGenerator() = default; + +uint32_t PseudoRandomGenerator::RandomUint32(uint32_t bound) { + assert(bound > 0 && "Bound must be positive"); + return std::uniform_int_distribution(0, bound - 1)(mt_); +} + +uint64_t PseudoRandomGenerator::RandomUint64(uint64_t bound) { + assert(bound > 0 && "Bound must be positive"); + return std::uniform_int_distribution(0, bound - 1)(mt_); +} + +bool PseudoRandomGenerator::RandomBool() { + return static_cast(std::uniform_int_distribution<>(0, 1)(mt_)); +} + +uint32_t PseudoRandomGenerator::RandomPercentage() { + // We use 101 because we want a result in the closed interval [0, 100], and + // RandomUint32 is not inclusive of its bound. + return RandomUint32(101); +} + +double PseudoRandomGenerator::RandomDouble() { + return std::uniform_real_distribution(0.0, 1.0)(mt_); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/pseudo_random_generator.h b/third_party/spirv-tools/source/fuzz/pseudo_random_generator.h new file mode 100644 index 0000000..7c19833 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/pseudo_random_generator.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ +#define SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ + +#include + +#include "source/fuzz/random_generator.h" + +namespace spvtools { +namespace fuzz { + +// Generates random data from a pseudo-random number generator. +class PseudoRandomGenerator : public RandomGenerator { + public: + explicit PseudoRandomGenerator(uint32_t seed); + + ~PseudoRandomGenerator() override; + + uint32_t RandomUint32(uint32_t bound) override; + + uint64_t RandomUint64(uint64_t bound) override; + + uint32_t RandomPercentage() override; + + bool RandomBool() override; + + double RandomDouble() override; + + private: + std::mt19937 mt_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_PSEUDO_RANDOM_GENERATOR_H_ diff --git a/third_party/spirv-tools/source/fuzz/random_generator.cpp b/third_party/spirv-tools/source/fuzz/random_generator.cpp new file mode 100644 index 0000000..9ec4845 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/random_generator.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/random_generator.h" + +namespace spvtools { +namespace fuzz { + +RandomGenerator::RandomGenerator() = default; + +RandomGenerator::~RandomGenerator() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/random_generator.h b/third_party/spirv-tools/source/fuzz/random_generator.h new file mode 100644 index 0000000..8c1baaa --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/random_generator.h @@ -0,0 +1,48 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_RANDOM_GENERATOR_H_ +#define SOURCE_FUZZ_RANDOM_GENERATOR_H_ + +#include + +namespace spvtools { +namespace fuzz { + +class RandomGenerator { + public: + RandomGenerator(); + + virtual ~RandomGenerator(); + + // Returns a value in the half-open interval [0, bound). + virtual uint32_t RandomUint32(uint32_t bound) = 0; + + // Returns a value in the half-open interval [0, bound). + virtual uint64_t RandomUint64(uint64_t bound) = 0; + + // Returns a value in the closed interval [0, 100]. + virtual uint32_t RandomPercentage() = 0; + + // Returns a boolean. + virtual bool RandomBool() = 0; + + // Returns a double in the closed interval [0, 1] + virtual double RandomDouble() = 0; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_RANDOM_GENERATOR_H_ diff --git a/third_party/spirv-tools/source/fuzz/replayer.cpp b/third_party/spirv-tools/source/fuzz/replayer.cpp new file mode 100644 index 0000000..5b3468e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/replayer.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/replayer.h" + +#include +#include +#include + +#include "source/fuzz/counter_overflow_id_source.h" +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/build_module.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +Replayer::Replayer( + spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + uint32_t num_transformations_to_apply, bool validate_during_replay, + spv_validator_options validator_options) + : target_env_(target_env), + consumer_(std::move(consumer)), + binary_in_(binary_in), + initial_facts_(initial_facts), + transformation_sequence_in_(transformation_sequence_in), + num_transformations_to_apply_(num_transformations_to_apply), + validate_during_replay_(validate_during_replay), + validator_options_(validator_options) {} + +Replayer::~Replayer() = default; + +Replayer::ReplayerResult Replayer::Run() { + // Check compatibility between the library version being linked with and the + // header files being used. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (num_transformations_to_apply_ > + static_cast( + transformation_sequence_in_.transformation_size())) { + consumer_(SPV_MSG_ERROR, nullptr, {}, + "The number of transformations to be replayed must not " + "exceed the size of the transformation sequence."); + return {Replayer::ReplayerResultStatus::kTooManyTransformationsRequested, + nullptr, nullptr, protobufs::TransformationSequence()}; + } + + spvtools::SpirvTools tools(target_env_); + if (!tools.IsValid()) { + consumer_(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return {Replayer::ReplayerResultStatus::kFailedToCreateSpirvToolsInterface, + nullptr, nullptr, protobufs::TransformationSequence()}; + } + + // Initial binary should be valid. + if (!tools.Validate(&binary_in_[0], binary_in_.size(), validator_options_)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is invalid; stopping."); + return {Replayer::ReplayerResultStatus::kInitialBinaryInvalid, nullptr, + nullptr, protobufs::TransformationSequence()}; + } + + // Build the module from the input binary. + std::unique_ptr ir_context = + BuildModule(target_env_, consumer_, binary_in_.data(), binary_in_.size()); + assert(ir_context); + + // For replay validation, we track the last valid SPIR-V binary that was + // observed. Initially this is the input binary. + std::vector last_valid_binary; + if (validate_during_replay_) { + last_valid_binary = binary_in_; + } + + // We find the smallest id that is (a) not in use by the original module, and + // (b) not used by any transformation in the sequence to be replayed. This + // serves as a starting id from which to issue overflow ids if they are + // required during replay. + uint32_t first_overflow_id = ir_context->module()->id_bound(); + for (auto& transformation : transformation_sequence_in_.transformation()) { + auto fresh_ids = Transformation::FromMessage(transformation)->GetFreshIds(); + if (!fresh_ids.empty()) { + first_overflow_id = + std::max(first_overflow_id, + *std::max_element(fresh_ids.begin(), fresh_ids.end()) + 1); + } + } + + std::unique_ptr transformation_context = + MakeUnique( + MakeUnique(ir_context.get()), validator_options_, + MakeUnique(first_overflow_id)); + transformation_context->GetFactManager()->AddInitialFacts(consumer_, + initial_facts_); + + // We track the largest id bound observed, to ensure that it only increases + // as transformations are applied. + uint32_t max_observed_id_bound = ir_context->module()->id_bound(); + (void)(max_observed_id_bound); // Keep release-mode compilers happy. + + protobufs::TransformationSequence transformation_sequence_out; + + // Consider the transformation proto messages in turn. + uint32_t counter = 0; + for (auto& message : transformation_sequence_in_.transformation()) { + if (counter >= num_transformations_to_apply_) { + break; + } + counter++; + + auto transformation = Transformation::FromMessage(message); + + // Check whether the transformation can be applied. + if (transformation->IsApplicable(ir_context.get(), + *transformation_context)) { + // The transformation is applicable, so apply it, and copy it to the + // sequence of transformations that were applied. + transformation->Apply(ir_context.get(), transformation_context.get()); + *transformation_sequence_out.add_transformation() = message; + + assert(ir_context->module()->id_bound() >= max_observed_id_bound && + "The module's id bound should only increase due to applying " + "transformations."); + max_observed_id_bound = ir_context->module()->id_bound(); + + if (validate_during_replay_) { + std::vector binary_to_validate; + ir_context->module()->ToBinary(&binary_to_validate, false); + + // Check whether the latest transformation led to a valid binary. + if (!tools.Validate(&binary_to_validate[0], binary_to_validate.size(), + validator_options_)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Binary became invalid during replay (set a " + "breakpoint to inspect); stopping."); + return {Replayer::ReplayerResultStatus::kReplayValidationFailure, + nullptr, nullptr, protobufs::TransformationSequence()}; + } + + // The binary was valid, so it becomes the latest valid binary. + last_valid_binary = std::move(binary_to_validate); + } + } + } + + return {Replayer::ReplayerResultStatus::kComplete, std::move(ir_context), + std::move(transformation_context), + std::move(transformation_sequence_out)}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/replayer.h b/third_party/spirv-tools/source/fuzz/replayer.h new file mode 100644 index 0000000..6730bd1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/replayer.h @@ -0,0 +1,105 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_REPLAYER_H_ +#define SOURCE_FUZZ_REPLAYER_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Transforms a SPIR-V module into a semantically equivalent SPIR-V module by +// applying a series of pre-defined transformations. +class Replayer { + public: + // Possible statuses that can result from running the replayer. + enum class ReplayerResultStatus { + kComplete, + kFailedToCreateSpirvToolsInterface, + kInitialBinaryInvalid, + kReplayValidationFailure, + kTooManyTransformationsRequested, + }; + + struct ReplayerResult { + ReplayerResultStatus status; + std::unique_ptr transformed_module; + std::unique_ptr transformation_context; + protobufs::TransformationSequence applied_transformations; + }; + + Replayer(spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + uint32_t num_transformations_to_apply, bool validate_during_replay, + spv_validator_options validator_options); + + // Disables copy/move constructor/assignment operations. + Replayer(const Replayer&) = delete; + Replayer(Replayer&&) = delete; + Replayer& operator=(const Replayer&) = delete; + Replayer& operator=(Replayer&&) = delete; + + ~Replayer(); + + // Attempts to apply the first |num_transformations_to_apply_| transformations + // from |transformation_sequence_in_| to |binary_in_|. Initial facts about + // the input binary and the context in which it will execute are provided via + // |initial_facts_|. + // + // On success, returns a successful result status together with the + // transformations that were applied, the IR for the transformed module, and + // the transformation context that arises from applying these transformations. + // Otherwise, returns an appropriate result status, an empty transformation + // sequence, and null pointers for the IR context and transformation context. + ReplayerResult Run(); + + private: + // Target environment. + const spv_target_env target_env_; + + // Message consumer. + MessageConsumer consumer_; + + // The binary to which transformations are to be applied. + const std::vector& binary_in_; + + // Initial facts known to hold in advance of applying any transformations. + const protobufs::FactSequence& initial_facts_; + + // The transformations to be replayed. + const protobufs::TransformationSequence& transformation_sequence_in_; + + // The number of transformations that should be replayed. + const uint32_t num_transformations_to_apply_; + + // Controls whether the validator should be run after every replay step. + const bool validate_during_replay_; + + // Options to control validation + spv_validator_options validator_options_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_REPLAYER_H_ diff --git a/third_party/spirv-tools/source/fuzz/shrinker.cpp b/third_party/spirv-tools/source/fuzz/shrinker.cpp new file mode 100644 index 0000000..c1e2514 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/shrinker.cpp @@ -0,0 +1,316 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/shrinker.h" + +#include + +#include "source/fuzz/added_function_reducer.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/replayer.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "source/spirv_fuzzer_options.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +// A helper to get the size of a protobuf transformation sequence in a less +// verbose manner. +uint32_t NumRemainingTransformations( + const protobufs::TransformationSequence& transformation_sequence) { + return static_cast(transformation_sequence.transformation_size()); +} + +// A helper to return a transformation sequence identical to |transformations|, +// except that a chunk of size |chunk_size| starting from |chunk_index| x +// |chunk_size| is removed (or as many transformations as available if the whole +// chunk is not). +protobufs::TransformationSequence RemoveChunk( + const protobufs::TransformationSequence& transformations, + uint32_t chunk_index, uint32_t chunk_size) { + uint32_t lower = chunk_index * chunk_size; + uint32_t upper = std::min((chunk_index + 1) * chunk_size, + NumRemainingTransformations(transformations)); + assert(lower < upper); + assert(upper <= NumRemainingTransformations(transformations)); + protobufs::TransformationSequence result; + for (uint32_t j = 0; j < NumRemainingTransformations(transformations); j++) { + if (j >= lower && j < upper) { + continue; + } + protobufs::Transformation transformation = + transformations.transformation()[j]; + *result.mutable_transformation()->Add() = transformation; + } + return result; +} + +} // namespace + +Shrinker::Shrinker( + spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + const InterestingnessFunction& interestingness_function, + uint32_t step_limit, bool validate_during_replay, + spv_validator_options validator_options) + : target_env_(target_env), + consumer_(std::move(consumer)), + binary_in_(binary_in), + initial_facts_(initial_facts), + transformation_sequence_in_(transformation_sequence_in), + interestingness_function_(interestingness_function), + step_limit_(step_limit), + validate_during_replay_(validate_during_replay), + validator_options_(validator_options) {} + +Shrinker::~Shrinker() = default; + +Shrinker::ShrinkerResult Shrinker::Run() { + // Check compatibility between the library version being linked with and the + // header files being used. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + SpirvTools tools(target_env_); + if (!tools.IsValid()) { + consumer_(SPV_MSG_ERROR, nullptr, {}, + "Failed to create SPIRV-Tools interface; stopping."); + return {Shrinker::ShrinkerResultStatus::kFailedToCreateSpirvToolsInterface, + std::vector(), protobufs::TransformationSequence()}; + } + + // Initial binary should be valid. + if (!tools.Validate(&binary_in_[0], binary_in_.size(), validator_options_)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is invalid; stopping."); + return {Shrinker::ShrinkerResultStatus::kInitialBinaryInvalid, + std::vector(), protobufs::TransformationSequence()}; + } + + // Run a replay of the initial transformation sequence to check that it + // succeeds. + auto initial_replay_result = + Replayer(target_env_, consumer_, binary_in_, initial_facts_, + transformation_sequence_in_, + static_cast( + transformation_sequence_in_.transformation_size()), + validate_during_replay_, validator_options_) + .Run(); + if (initial_replay_result.status != + Replayer::ReplayerResultStatus::kComplete) { + return {ShrinkerResultStatus::kReplayFailed, std::vector(), + protobufs::TransformationSequence()}; + } + // Get the binary that results from running these transformations, and the + // subsequence of the initial transformations that actually apply (in + // principle this could be a strict subsequence). + std::vector current_best_binary; + initial_replay_result.transformed_module->module()->ToBinary( + ¤t_best_binary, false); + protobufs::TransformationSequence current_best_transformations = + std::move(initial_replay_result.applied_transformations); + + // Check that the binary produced by applying the initial transformations is + // indeed interesting. + if (!interestingness_function_(current_best_binary, 0)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is not interesting; stopping."); + return {ShrinkerResultStatus::kInitialBinaryNotInteresting, + std::vector(), protobufs::TransformationSequence()}; + } + + uint32_t attempt = 0; // Keeps track of the number of shrink attempts that + // have been tried, whether successful or not. + + uint32_t chunk_size = + std::max(1u, NumRemainingTransformations(current_best_transformations) / + 2); // The number of contiguous transformations that the + // shrinker will try to remove in one go; starts + // high and decreases during the shrinking process. + + // Keep shrinking until we: + // - reach the step limit, + // - run out of transformations to remove, or + // - cannot make the chunk size any smaller. + while (attempt < step_limit_ && + !current_best_transformations.transformation().empty() && + chunk_size > 0) { + bool progress_this_round = + false; // Used to decide whether to make the chunk size with which we + // remove transformations smaller. If we managed to remove at + // least one chunk of transformations at a particular chunk + // size, we set this flag so that we do not yet decrease the + // chunk size. + + assert(chunk_size <= + NumRemainingTransformations(current_best_transformations) && + "Chunk size should never exceed the number of transformations that " + "remain."); + + // The number of chunks is the ceiling of (#remaining_transformations / + // chunk_size). + const uint32_t num_chunks = + (NumRemainingTransformations(current_best_transformations) + + chunk_size - 1) / + chunk_size; + assert(num_chunks >= 1 && "There should be at least one chunk."); + assert(num_chunks * chunk_size >= + NumRemainingTransformations(current_best_transformations) && + "All transformations should be in some chunk."); + + // We go through the transformations in reverse, in chunks of size + // |chunk_size|, using |chunk_index| to track which chunk to try removing + // next. The loop exits early if we reach the shrinking step limit. + for (int chunk_index = num_chunks - 1; + attempt < step_limit_ && chunk_index >= 0; chunk_index--) { + // Remove a chunk of transformations according to the current index and + // chunk size. + auto transformations_with_chunk_removed = + RemoveChunk(current_best_transformations, + static_cast(chunk_index), chunk_size); + + // Replay the smaller sequence of transformations to get a next binary and + // transformation sequence. Note that the transformations arising from + // replay might be even smaller than the transformations with the chunk + // removed, because removing those transformations might make further + // transformations inapplicable. + auto replay_result = + Replayer( + target_env_, consumer_, binary_in_, initial_facts_, + transformations_with_chunk_removed, + static_cast( + transformations_with_chunk_removed.transformation_size()), + validate_during_replay_, validator_options_) + .Run(); + if (replay_result.status != Replayer::ReplayerResultStatus::kComplete) { + // Replay should not fail; if it does, we need to abort shrinking. + return {ShrinkerResultStatus::kReplayFailed, std::vector(), + protobufs::TransformationSequence()}; + } + + assert( + NumRemainingTransformations(replay_result.applied_transformations) >= + chunk_index * chunk_size && + "Removing this chunk of transformations should not have an effect " + "on earlier chunks."); + + std::vector transformed_binary; + replay_result.transformed_module->module()->ToBinary(&transformed_binary, + false); + if (interestingness_function_(transformed_binary, attempt)) { + // If the binary arising from the smaller transformation sequence is + // interesting, this becomes our current best binary and transformation + // sequence. + current_best_binary = std::move(transformed_binary); + current_best_transformations = + std::move(replay_result.applied_transformations); + progress_this_round = true; + } + // Either way, this was a shrink attempt, so increment our count of shrink + // attempts. + attempt++; + } + if (!progress_this_round) { + // If we didn't manage to remove any chunks at this chunk size, try a + // smaller chunk size. + chunk_size /= 2; + } + // Decrease the chunk size until it becomes no larger than the number of + // remaining transformations. + while (chunk_size > + NumRemainingTransformations(current_best_transformations)) { + chunk_size /= 2; + } + } + + // We now use spirv-reduce to minimise the functions associated with any + // AddFunction transformations that remain. + // + // Consider every remaining transformation. + for (uint32_t transformation_index = 0; + attempt < step_limit_ && + transformation_index < + static_cast( + current_best_transformations.transformation_size()); + transformation_index++) { + // Skip all transformations apart from TransformationAddFunction. + if (!current_best_transformations.transformation(transformation_index) + .has_add_function()) { + continue; + } + // Invoke spirv-reduce on the function encoded in this AddFunction + // transformation. The details of this are rather involved, and so are + // encapsulated in a separate class. + auto added_function_reducer_result = + AddedFunctionReducer(target_env_, consumer_, binary_in_, initial_facts_, + current_best_transformations, transformation_index, + interestingness_function_, validate_during_replay_, + validator_options_, step_limit_, attempt) + .Run(); + // Reducing the added function should succeed. If it doesn't, we report + // a shrinking error. + if (added_function_reducer_result.status != + AddedFunctionReducer::AddedFunctionReducerResultStatus::kComplete) { + return {ShrinkerResultStatus::kAddedFunctionReductionFailed, + std::vector(), protobufs::TransformationSequence()}; + } + assert(current_best_transformations.transformation_size() == + added_function_reducer_result.applied_transformations + .transformation_size() && + "The number of transformations should not have changed."); + current_best_binary = + std::move(added_function_reducer_result.transformed_binary); + current_best_transformations = + std::move(added_function_reducer_result.applied_transformations); + // The added function reducer reports how many reduction attempts + // spirv-reduce took when reducing the function. We regard each of these + // as a shrinker attempt. + attempt += added_function_reducer_result.num_reduction_attempts; + } + + // Indicate whether shrinking completed or was truncated due to reaching the + // step limit. + // + // Either way, the output from the shrinker is the best binary we saw, and the + // transformations that led to it. + assert(attempt <= step_limit_); + if (attempt == step_limit_) { + std::stringstream strstream; + strstream << "Shrinking did not complete; step limit " << step_limit_ + << " was reached."; + consumer_(SPV_MSG_WARNING, nullptr, {}, strstream.str().c_str()); + return {Shrinker::ShrinkerResultStatus::kStepLimitReached, + std::move(current_best_binary), + std::move(current_best_transformations)}; + } + return {Shrinker::ShrinkerResultStatus::kComplete, + std::move(current_best_binary), + std::move(current_best_transformations)}; +} + +uint32_t Shrinker::GetIdBound(const std::vector& binary) const { + // Build the module from the input binary. + std::unique_ptr ir_context = + BuildModule(target_env_, consumer_, binary.data(), binary.size()); + assert(ir_context && "Error building module."); + return ir_context->module()->id_bound(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/shrinker.h b/third_party/spirv-tools/source/fuzz/shrinker.h new file mode 100644 index 0000000..b9015d6 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/shrinker.h @@ -0,0 +1,128 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_SHRINKER_H_ +#define SOURCE_FUZZ_SHRINKER_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Shrinks a sequence of transformations that lead to an interesting SPIR-V +// binary to yield a smaller sequence of transformations that still produce an +// interesting binary. +class Shrinker { + public: + // Possible statuses that can result from running the shrinker. + enum class ShrinkerResultStatus { + kComplete, + kFailedToCreateSpirvToolsInterface, + kInitialBinaryInvalid, + kInitialBinaryNotInteresting, + kReplayFailed, + kStepLimitReached, + kAddedFunctionReductionFailed, + }; + + struct ShrinkerResult { + ShrinkerResultStatus status; + std::vector transformed_binary; + protobufs::TransformationSequence applied_transformations; + }; + + // The type for a function that will take a binary, |binary|, and return true + // if and only if the binary is deemed interesting. (The function also takes + // an integer argument, |counter|, that will be incremented each time the + // function is called; this is for debugging purposes). + // + // The notion of "interesting" depends on what properties of the binary or + // tools that process the binary we are trying to maintain during shrinking. + using InterestingnessFunction = std::function& binary, uint32_t counter)>; + + Shrinker(spv_target_env target_env, MessageConsumer consumer, + const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + const InterestingnessFunction& interestingness_function, + uint32_t step_limit, bool validate_during_replay, + spv_validator_options validator_options); + + // Disables copy/move constructor/assignment operations. + Shrinker(const Shrinker&) = delete; + Shrinker(Shrinker&&) = delete; + Shrinker& operator=(const Shrinker&) = delete; + Shrinker& operator=(Shrinker&&) = delete; + + ~Shrinker(); + + // Requires that when |transformation_sequence_in_| is applied to |binary_in_| + // with initial facts |initial_facts_|, the resulting binary is interesting + // according to |interestingness_function_|. + // + // If shrinking succeeded -- possibly terminating early due to reaching the + // shrinker's step limit -- an associated result status is returned together + // with a subsequence of |transformation_sequence_in_| that, when applied + // to |binary_in_| with initial facts |initial_facts_|, produces a binary + // that is also interesting according to |interestingness_function_|; this + // binary is also returned. + // + // If shrinking failed for some reason, an appropriate result status is + // returned together with an empty binary and empty transformation sequence. + ShrinkerResult Run(); + + private: + // Returns the id bound for the given SPIR-V binary, which is assumed to be + // valid. + uint32_t GetIdBound(const std::vector& binary) const; + + // Target environment. + const spv_target_env target_env_; + + // Message consumer that will be invoked once for each message communicated + // from the library. + MessageConsumer consumer_; + + // The binary to which transformations are to be applied. + const std::vector& binary_in_; + + // Initial facts known to hold in advance of applying any transformations. + const protobufs::FactSequence& initial_facts_; + + // The series of transformations to be shrunk. + const protobufs::TransformationSequence& transformation_sequence_in_; + + // Function that decides whether a given module is interesting. + const InterestingnessFunction& interestingness_function_; + + // Step limit to decide when to terminate shrinking early. + const uint32_t step_limit_; + + // Determines whether to check for validity during the replaying of + // transformations. + const bool validate_during_replay_; + + // Options to control validation. + spv_validator_options validator_options_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_SHRINKER_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation.cpp b/third_party/spirv-tools/source/fuzz/transformation.cpp new file mode 100644 index 0000000..ebbc393 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation.cpp @@ -0,0 +1,399 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_access_chain.h" +#include "source/fuzz/transformation_add_bit_instruction_synonym.h" +#include "source/fuzz/transformation_add_constant_boolean.h" +#include "source/fuzz/transformation_add_constant_composite.h" +#include "source/fuzz/transformation_add_constant_null.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_copy_memory.h" +#include "source/fuzz/transformation_add_dead_block.h" +#include "source/fuzz/transformation_add_dead_break.h" +#include "source/fuzz/transformation_add_dead_continue.h" +#include "source/fuzz/transformation_add_early_terminator_wrapper.h" +#include "source/fuzz/transformation_add_function.h" +#include "source/fuzz/transformation_add_global_undef.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_image_sample_unused_components.h" +#include "source/fuzz/transformation_add_local_variable.h" +#include "source/fuzz/transformation_add_loop_preheader.h" +#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h" +#include "source/fuzz/transformation_add_no_contraction_decoration.h" +#include "source/fuzz/transformation_add_opphi_synonym.h" +#include "source/fuzz/transformation_add_parameter.h" +#include "source/fuzz/transformation_add_relaxed_decoration.h" +#include "source/fuzz/transformation_add_spec_constant_op.h" +#include "source/fuzz/transformation_add_synonym.h" +#include "source/fuzz/transformation_add_type_array.h" +#include "source/fuzz/transformation_add_type_boolean.h" +#include "source/fuzz/transformation_add_type_float.h" +#include "source/fuzz/transformation_add_type_function.h" +#include "source/fuzz/transformation_add_type_int.h" +#include "source/fuzz/transformation_add_type_matrix.h" +#include "source/fuzz/transformation_add_type_pointer.h" +#include "source/fuzz/transformation_add_type_struct.h" +#include "source/fuzz/transformation_add_type_vector.h" +#include "source/fuzz/transformation_adjust_branch_weights.h" +#include "source/fuzz/transformation_composite_construct.h" +#include "source/fuzz/transformation_composite_extract.h" +#include "source/fuzz/transformation_composite_insert.h" +#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h" +#include "source/fuzz/transformation_duplicate_region_with_selection.h" +#include "source/fuzz/transformation_equation_instruction.h" +#include "source/fuzz/transformation_expand_vector_reduction.h" +#include "source/fuzz/transformation_flatten_conditional_branch.h" +#include "source/fuzz/transformation_function_call.h" +#include "source/fuzz/transformation_inline_function.h" +#include "source/fuzz/transformation_invert_comparison_operator.h" +#include "source/fuzz/transformation_load.h" +#include "source/fuzz/transformation_make_vector_operation_dynamic.h" +#include "source/fuzz/transformation_merge_blocks.h" +#include "source/fuzz/transformation_merge_function_returns.h" +#include "source/fuzz/transformation_move_block_down.h" +#include "source/fuzz/transformation_move_instruction_down.h" +#include "source/fuzz/transformation_mutate_pointer.h" +#include "source/fuzz/transformation_outline_function.h" +#include "source/fuzz/transformation_permute_function_parameters.h" +#include "source/fuzz/transformation_permute_phi_operands.h" +#include "source/fuzz/transformation_propagate_instruction_down.h" +#include "source/fuzz/transformation_propagate_instruction_up.h" +#include "source/fuzz/transformation_push_id_through_variable.h" +#include "source/fuzz/transformation_record_synonymous_constants.h" +#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h" +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" +#include "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h" +#include "source/fuzz/transformation_replace_constant_with_uniform.h" +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" +#include "source/fuzz/transformation_replace_irrelevant_id.h" +#include "source/fuzz/transformation_replace_linear_algebra_instruction.h" +#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h" +#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h" +#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h" +#include "source/fuzz/transformation_replace_parameter_with_global.h" +#include "source/fuzz/transformation_replace_params_with_struct.h" +#include "source/fuzz/transformation_set_function_control.h" +#include "source/fuzz/transformation_set_loop_control.h" +#include "source/fuzz/transformation_set_memory_operands_mask.h" +#include "source/fuzz/transformation_set_selection_control.h" +#include "source/fuzz/transformation_split_block.h" +#include "source/fuzz/transformation_store.h" +#include "source/fuzz/transformation_swap_commutable_operands.h" +#include "source/fuzz/transformation_swap_conditional_branch_operands.h" +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" +#include "source/fuzz/transformation_vector_shuffle.h" +#include "source/fuzz/transformation_wrap_early_terminator_in_function.h" +#include "source/fuzz/transformation_wrap_region_in_selection.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +Transformation::~Transformation() = default; + +std::unique_ptr Transformation::FromMessage( + const protobufs::Transformation& message) { + switch (message.transformation_case()) { + case protobufs::Transformation::TransformationCase::kAccessChain: + return MakeUnique(message.access_chain()); + case protobufs::Transformation::TransformationCase:: + kAddBitInstructionSynonym: + return MakeUnique( + message.add_bit_instruction_synonym()); + case protobufs::Transformation::TransformationCase::kAddConstantBoolean: + return MakeUnique( + message.add_constant_boolean()); + case protobufs::Transformation::TransformationCase::kAddConstantComposite: + return MakeUnique( + message.add_constant_composite()); + case protobufs::Transformation::TransformationCase::kAddConstantNull: + return MakeUnique( + message.add_constant_null()); + case protobufs::Transformation::TransformationCase::kAddConstantScalar: + return MakeUnique( + message.add_constant_scalar()); + case protobufs::Transformation::TransformationCase::kAddCopyMemory: + return MakeUnique(message.add_copy_memory()); + case protobufs::Transformation::TransformationCase::kAddDeadBlock: + return MakeUnique(message.add_dead_block()); + case protobufs::Transformation::TransformationCase::kAddDeadBreak: + return MakeUnique(message.add_dead_break()); + case protobufs::Transformation::TransformationCase::kAddDeadContinue: + return MakeUnique( + message.add_dead_continue()); + case protobufs::Transformation::TransformationCase:: + kAddEarlyTerminatorWrapper: + return MakeUnique( + message.add_early_terminator_wrapper()); + case protobufs::Transformation::TransformationCase::kAddFunction: + return MakeUnique(message.add_function()); + case protobufs::Transformation::TransformationCase::kAddGlobalUndef: + return MakeUnique( + message.add_global_undef()); + case protobufs::Transformation::TransformationCase::kAddGlobalVariable: + return MakeUnique( + message.add_global_variable()); + case protobufs::Transformation::TransformationCase:: + kAddImageSampleUnusedComponents: + return MakeUnique( + message.add_image_sample_unused_components()); + case protobufs::Transformation::TransformationCase::kAddLocalVariable: + return MakeUnique( + message.add_local_variable()); + case protobufs::Transformation::TransformationCase::kAddLoopPreheader: + return MakeUnique( + message.add_loop_preheader()); + case protobufs::Transformation::TransformationCase:: + kAddLoopToCreateIntConstantSynonym: + return MakeUnique( + message.add_loop_to_create_int_constant_synonym()); + case protobufs::Transformation::TransformationCase:: + kAddNoContractionDecoration: + return MakeUnique( + message.add_no_contraction_decoration()); + case protobufs::Transformation::TransformationCase::kAddOpphiSynonym: + return MakeUnique( + message.add_opphi_synonym()); + case protobufs::Transformation::TransformationCase::kAddParameter: + return MakeUnique(message.add_parameter()); + case protobufs::Transformation::TransformationCase::kAddRelaxedDecoration: + return MakeUnique( + message.add_relaxed_decoration()); + case protobufs::Transformation::TransformationCase::kAddSpecConstantOp: + return MakeUnique( + message.add_spec_constant_op()); + case protobufs::Transformation::TransformationCase::kAddSynonym: + return MakeUnique(message.add_synonym()); + case protobufs::Transformation::TransformationCase::kAddTypeArray: + return MakeUnique(message.add_type_array()); + case protobufs::Transformation::TransformationCase::kAddTypeBoolean: + return MakeUnique( + message.add_type_boolean()); + case protobufs::Transformation::TransformationCase::kAddTypeFloat: + return MakeUnique(message.add_type_float()); + case protobufs::Transformation::TransformationCase::kAddTypeFunction: + return MakeUnique( + message.add_type_function()); + case protobufs::Transformation::TransformationCase::kAddTypeInt: + return MakeUnique(message.add_type_int()); + case protobufs::Transformation::TransformationCase::kAddTypeMatrix: + return MakeUnique(message.add_type_matrix()); + case protobufs::Transformation::TransformationCase::kAddTypePointer: + return MakeUnique( + message.add_type_pointer()); + case protobufs::Transformation::TransformationCase::kAddTypeStruct: + return MakeUnique(message.add_type_struct()); + case protobufs::Transformation::TransformationCase::kAddTypeVector: + return MakeUnique(message.add_type_vector()); + case protobufs::Transformation::TransformationCase::kAdjustBranchWeights: + return MakeUnique( + message.adjust_branch_weights()); + case protobufs::Transformation::TransformationCase::kCompositeConstruct: + return MakeUnique( + message.composite_construct()); + case protobufs::Transformation::TransformationCase::kCompositeExtract: + return MakeUnique( + message.composite_extract()); + case protobufs::Transformation::TransformationCase::kCompositeInsert: + return MakeUnique( + message.composite_insert()); + case protobufs::Transformation::TransformationCase:: + kComputeDataSynonymFactClosure: + return MakeUnique( + message.compute_data_synonym_fact_closure()); + case protobufs::Transformation::TransformationCase:: + kDuplicateRegionWithSelection: + return MakeUnique( + message.duplicate_region_with_selection()); + case protobufs::Transformation::TransformationCase::kEquationInstruction: + return MakeUnique( + message.equation_instruction()); + case protobufs::Transformation::TransformationCase::kExpandVectorReduction: + return MakeUnique( + message.expand_vector_reduction()); + case protobufs::Transformation::TransformationCase:: + kFlattenConditionalBranch: + return MakeUnique( + message.flatten_conditional_branch()); + case protobufs::Transformation::TransformationCase::kFunctionCall: + return MakeUnique(message.function_call()); + case protobufs::Transformation::TransformationCase::kInlineFunction: + return MakeUnique( + message.inline_function()); + case protobufs::Transformation::TransformationCase:: + kInvertComparisonOperator: + return MakeUnique( + message.invert_comparison_operator()); + case protobufs::Transformation::TransformationCase::kLoad: + return MakeUnique(message.load()); + case protobufs::Transformation::TransformationCase:: + kMakeVectorOperationDynamic: + return MakeUnique( + message.make_vector_operation_dynamic()); + case protobufs::Transformation::TransformationCase::kMergeBlocks: + return MakeUnique(message.merge_blocks()); + case protobufs::Transformation::TransformationCase::kMergeFunctionReturns: + return MakeUnique( + message.merge_function_returns()); + case protobufs::Transformation::TransformationCase::kMoveBlockDown: + return MakeUnique(message.move_block_down()); + case protobufs::Transformation::TransformationCase::kMoveInstructionDown: + return MakeUnique( + message.move_instruction_down()); + case protobufs::Transformation::TransformationCase::kMutatePointer: + return MakeUnique(message.mutate_pointer()); + case protobufs::Transformation::TransformationCase::kOutlineFunction: + return MakeUnique( + message.outline_function()); + case protobufs::Transformation::TransformationCase:: + kPermuteFunctionParameters: + return MakeUnique( + message.permute_function_parameters()); + case protobufs::Transformation::TransformationCase::kPermutePhiOperands: + return MakeUnique( + message.permute_phi_operands()); + case protobufs::Transformation::TransformationCase:: + kPropagateInstructionDown: + return MakeUnique( + message.propagate_instruction_down()); + case protobufs::Transformation::TransformationCase::kPropagateInstructionUp: + return MakeUnique( + message.propagate_instruction_up()); + case protobufs::Transformation::TransformationCase::kPushIdThroughVariable: + return MakeUnique( + message.push_id_through_variable()); + case protobufs::Transformation::TransformationCase:: + kRecordSynonymousConstants: + return MakeUnique( + message.record_synonymous_constants()); + case protobufs::Transformation::TransformationCase:: + kReplaceAddSubMulWithCarryingExtended: + return MakeUnique( + message.replace_add_sub_mul_with_carrying_extended()); + case protobufs::Transformation::TransformationCase:: + kReplaceBooleanConstantWithConstantBinary: + return MakeUnique( + message.replace_boolean_constant_with_constant_binary()); + case protobufs::Transformation::TransformationCase:: + kReplaceBranchFromDeadBlockWithExit: + return MakeUnique( + message.replace_branch_from_dead_block_with_exit()); + case protobufs::Transformation::TransformationCase:: + kReplaceConstantWithUniform: + return MakeUnique( + message.replace_constant_with_uniform()); + case protobufs::Transformation::TransformationCase:: + kReplaceCopyMemoryWithLoadStore: + return MakeUnique( + message.replace_copy_memory_with_load_store()); + case protobufs::Transformation::TransformationCase:: + kReplaceCopyObjectWithStoreLoad: + return MakeUnique( + message.replace_copy_object_with_store_load()); + case protobufs::Transformation::TransformationCase::kReplaceIdWithSynonym: + return MakeUnique( + message.replace_id_with_synonym()); + case protobufs::Transformation::TransformationCase::kReplaceIrrelevantId: + return MakeUnique( + message.replace_irrelevant_id()); + case protobufs::Transformation::TransformationCase:: + kReplaceLinearAlgebraInstruction: + return MakeUnique( + message.replace_linear_algebra_instruction()); + case protobufs::Transformation::TransformationCase:: + kReplaceLoadStoreWithCopyMemory: + return MakeUnique( + message.replace_load_store_with_copy_memory()); + case protobufs::Transformation::TransformationCase:: + kReplaceOpselectWithConditionalBranch: + return MakeUnique( + message.replace_opselect_with_conditional_branch()); + case protobufs::Transformation::TransformationCase:: + kReplaceParameterWithGlobal: + return MakeUnique( + message.replace_parameter_with_global()); + case protobufs::Transformation::TransformationCase:: + kReplaceParamsWithStruct: + return MakeUnique( + message.replace_params_with_struct()); + case protobufs::Transformation::TransformationCase:: + kReplaceOpphiIdFromDeadPredecessor: + return MakeUnique( + message.replace_opphi_id_from_dead_predecessor()); + case protobufs::Transformation::TransformationCase::kSetFunctionControl: + return MakeUnique( + message.set_function_control()); + case protobufs::Transformation::TransformationCase::kSetLoopControl: + return MakeUnique( + message.set_loop_control()); + case protobufs::Transformation::TransformationCase::kSetMemoryOperandsMask: + return MakeUnique( + message.set_memory_operands_mask()); + case protobufs::Transformation::TransformationCase::kSetSelectionControl: + return MakeUnique( + message.set_selection_control()); + case protobufs::Transformation::TransformationCase::kSplitBlock: + return MakeUnique(message.split_block()); + case protobufs::Transformation::TransformationCase::kStore: + return MakeUnique(message.store()); + case protobufs::Transformation::TransformationCase::kSwapCommutableOperands: + return MakeUnique( + message.swap_commutable_operands()); + case protobufs::Transformation::TransformationCase:: + kSwapConditionalBranchOperands: + return MakeUnique( + message.swap_conditional_branch_operands()); + case protobufs::Transformation::TransformationCase:: + kToggleAccessChainInstruction: + return MakeUnique( + message.toggle_access_chain_instruction()); + case protobufs::Transformation::TransformationCase::kVectorShuffle: + return MakeUnique(message.vector_shuffle()); + case protobufs::Transformation::TransformationCase:: + kWrapEarlyTerminatorInFunction: + return MakeUnique( + message.wrap_early_terminator_in_function()); + case protobufs::Transformation::TransformationCase::kWrapRegionInSelection: + return MakeUnique( + message.wrap_region_in_selection()); + case protobufs::Transformation::TRANSFORMATION_NOT_SET: + assert(false && "An unset transformation was encountered."); + return nullptr; + } + assert(false && "Should be unreachable as all cases must be handled above."); + return nullptr; +} + +bool Transformation::CheckIdIsFreshAndNotUsedByThisTransformation( + uint32_t id, opt::IRContext* ir_context, + std::set* ids_used_by_this_transformation) { + if (!fuzzerutil::IsFreshId(ir_context, id)) { + return false; + } + if (ids_used_by_this_transformation->count(id) != 0) { + return false; + } + ids_used_by_this_transformation->insert(id); + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation.h b/third_party/spirv-tools/source/fuzz/transformation.h new file mode 100644 index 0000000..8d618e8 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation.h @@ -0,0 +1,108 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_H_ + +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Rules for transformations +// ------------------------- +// +// - Immutability: a transformation must be immutable. +// - Ability to copy and serialize: to ensure that a copy of a transformation, +// possibly saved out to disk and read back again, is indistinguishable +// from the original transformation, thus a transformation must depend +// only on well-defined pieces of state, such as instruction ids. It must +// not rely on state such as pointers to instructions and blocks. +// - Determinism: the effect of a transformation on a module be a deterministic +// function of the module and the transformation. Any randomization should +// be applied before creating the transformation, not during its +// application. +// - Well-defined and precondition: the 'IsApplicable' method should only +// return true if the transformation can be cleanly applied to the given +// module, to mutate it into a valid and semantically-equivalent module, as +// long as the module is initially valid. +// - Ability to test precondition on any valid module: 'IsApplicable' should be +// designed so that it is safe to ask whether a transformation is +// applicable to an arbitrary valid module. For example, if a +// transformation involves a block id, 'IsApplicable' should check whether +// the module indeed has a block with that id, and return false if not. It +// must not assume that there is such a block. +// - Documented precondition: while the implementation of 'IsApplicable' should +// should codify the precondition, the method should be commented in the +// header file for a transformation with a precise English description of +// the precondition. +// - Documented effect: while the implementation of 'Apply' should codify the +// effect of the transformation, the method should be commented in the +// header file for a transformation with a precise English description of +// the effect. + +class Transformation { + public: + virtual ~Transformation(); + + // Factory method to obtain a transformation object from the protobuf + // representation of a transformation given by |message|. + static std::unique_ptr FromMessage( + const protobufs::Transformation& message); + + // A precondition that determines whether the transformation can be cleanly + // applied in a semantics-preserving manner to the SPIR-V module given by + // |ir_context|, in the presence of facts and other contextual information + // captured by |transformation_context|. + // + // Preconditions for individual transformations must be documented in the + // associated header file using precise English. The transformation context + // provides access to facts about the module that are known to be true, on + // which the precondition may depend. + virtual bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const = 0; + + // Requires that IsApplicable(ir_context, *transformation_context) holds. + // Applies the transformation, mutating |ir_context| and possibly updating + // |transformation_context| with new facts established by the transformation. + virtual void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const = 0; + + // Returns the set of fresh ids that appear in the transformation's protobuf + // message. + virtual std::unordered_set GetFreshIds() const = 0; + + // Turns the transformation into a protobuf message for serialization. + virtual protobufs::Transformation ToMessage() const = 0; + + // Helper that returns true if and only if (a) |id| is a fresh id for the + // module, and (b) |id| is not in |ids_used_by_this_transformation|, a set of + // ids already known to be in use by a transformation. This is useful when + // checking id freshness for a transformation that uses many ids, all of which + // must be distinct. + static bool CheckIdIsFreshAndNotUsedByThisTransformation( + uint32_t id, opt::IRContext* ir_context, + std::set* ids_used_by_this_transformation); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_access_chain.cpp b/third_party/spirv-tools/source/fuzz/transformation_access_chain.cpp new file mode 100644 index 0000000..daf73d6 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_access_chain.cpp @@ -0,0 +1,420 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_access_chain.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAccessChain::TransformationAccessChain( + const spvtools::fuzz::protobufs::TransformationAccessChain& message) + : message_(message) {} + +TransformationAccessChain::TransformationAccessChain( + uint32_t fresh_id, uint32_t pointer_id, + const std::vector& index_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before, + const std::vector>& fresh_ids_for_clamping) { + message_.set_fresh_id(fresh_id); + message_.set_pointer_id(pointer_id); + for (auto id : index_id) { + message_.add_index_id(id); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + for (auto clamping_ids_pair : fresh_ids_for_clamping) { + protobufs::UInt32Pair pair; + pair.set_first(clamping_ids_pair.first); + pair.set_second(clamping_ids_pair.second); + *message_.add_fresh_ids_for_clamping() = pair; + } +} + +bool TransformationAccessChain::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Keep track of the fresh ids used to make sure that they are distinct. + std::set fresh_ids_used; + + // The result id must be fresh. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.fresh_id(), ir_context, &fresh_ids_used)) { + return false; + } + // The pointer id must exist and have a type. + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + // The type must indeed be a pointer. + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); + if (pointer_type->opcode() != SpvOpTypePointer) { + return false; + } + + // The described instruction to insert before must exist and be a suitable + // point where an OpAccessChain instruction could be inserted. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!instruction_to_insert_before) { + return false; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpAccessChain, instruction_to_insert_before)) { + return false; + } + + // Do not allow making an access chain from a null or undefined pointer, as + // we do not want to allow accessing such pointers. This might be acceptable + // in dead blocks, but we conservatively avoid it. + switch (pointer->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + assert( + false && + "Access chains should not be created from null/undefined pointers"); + return false; + default: + break; + } + + // The pointer on which the access chain is to be based needs to be available + // (according to dominance rules) at the insertion point. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.pointer_id())) { + return false; + } + + // We now need to use the given indices to walk the type structure of the + // base type of the pointer, making sure that (a) the indices correspond to + // integers, and (b) these integer values are in-bounds. + + // Start from the base type of the pointer. + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + + int id_pairs_used = 0; + + // Consider the given index ids in turn. + for (auto index_id : message_.index_id()) { + // The index value will correspond to the value of the index if the object + // is a struct, otherwise the value 0 will be used. + uint32_t index_value; + + // Check whether the object is a struct. + if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() == + SpvOpTypeStruct) { + // It is a struct: we need to retrieve the integer value. + + bool successful; + std::tie(successful, index_value) = + GetIndexValue(ir_context, index_id, subobject_type_id); + + if (!successful) { + return false; + } + } else { + // It is not a struct: the index will need clamping. + + if (message_.fresh_ids_for_clamping().size() <= id_pairs_used) { + // We don't have enough ids + return false; + } + + // Get two new ids to use and update the amount used. + protobufs::UInt32Pair fresh_ids = + message_.fresh_ids_for_clamping()[id_pairs_used++]; + + // Valid ids need to have been given + if (fresh_ids.first() == 0 || fresh_ids.second() == 0) { + return false; + } + + // Check that the ids are actually fresh and not already used by this + // transformation. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + fresh_ids.first(), ir_context, &fresh_ids_used) || + !CheckIdIsFreshAndNotUsedByThisTransformation( + fresh_ids.second(), ir_context, &fresh_ids_used)) { + return false; + } + + if (!ValidIndexToComposite(ir_context, index_id, subobject_type_id)) { + return false; + } + + // Perform the clamping using the fresh ids at our disposal. + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); + + uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( + *ir_context->get_def_use_mgr()->GetDef(subobject_type_id), + ir_context); + + // The module must have an integer constant of value bound-1 of the same + // type as the index. + if (!fuzzerutil::MaybeGetIntegerConstantFromValueAndType( + ir_context, bound - 1, index_instruction->type_id())) { + return false; + } + + // The module must have the definition of bool type to make a comparison. + if (!fuzzerutil::MaybeGetBoolType(ir_context)) { + return false; + } + + // The index is not necessarily a constant, so we may not know its value. + // We can use index 0 because the components of a non-struct composite + // all have the same type, and index 0 is always in bounds. + index_value = 0; + } + + // Try to walk down the type using this index. This will yield 0 if the + // type is not a composite or the index is out of bounds, and the id of + // the next type otherwise. + subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + ir_context, subobject_type_id, index_value); + if (!subobject_type_id) { + // Either the type was not a composite (so that too many indices were + // provided), or the index was out of bounds. + return false; + } + } + // At this point, |subobject_type_id| is the type of the value targeted by + // the new access chain. The result type of the access chain should be a + // pointer to this type, with the same storage class as for the original + // pointer. Such a pointer type needs to exist in the module. + // + // We do not use the type manager to look up this type, due to problems + // associated with pointers to isomorphic structs being regarded as the same. + return fuzzerutil::MaybeGetPointerType( + ir_context, subobject_type_id, + static_cast( + pointer_type->GetSingleWordInOperand(0))) != 0; +} + +void TransformationAccessChain::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // The operands to the access chain are the pointer followed by the indices. + // The result type of the access chain is determined by where the indices + // lead. We thus push the pointer to a sequence of operands, and then follow + // the indices, pushing each to the operand list and tracking the type + // obtained by following it. Ultimately this yields the type of the + // component reached by following all the indices, and the result type is + // a pointer to this component type. + opt::Instruction::OperandList operands; + + // Add the pointer id itself. + operands.push_back({SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}); + + // Start walking the indices, starting with the pointer's base type. + auto pointer_type = ir_context->get_def_use_mgr()->GetDef( + ir_context->get_def_use_mgr()->GetDef(message_.pointer_id())->type_id()); + uint32_t subobject_type_id = pointer_type->GetSingleWordInOperand(1); + + uint32_t id_pairs_used = 0; + + // Go through the index ids in turn. + for (auto index_id : message_.index_id()) { + uint32_t index_value; + + // Actual id to be used in the instruction: the original id + // or the clamped one. + uint32_t new_index_id; + + // Check whether the object is a struct. + if (ir_context->get_def_use_mgr()->GetDef(subobject_type_id)->opcode() == + SpvOpTypeStruct) { + // It is a struct: we need to retrieve the integer value. + + index_value = + GetIndexValue(ir_context, index_id, subobject_type_id).second; + + new_index_id = index_id; + + } else { + // It is not a struct: the index will need clamping. + + // Get two new ids to use and update the amount used. + protobufs::UInt32Pair fresh_ids = + message_.fresh_ids_for_clamping()[id_pairs_used++]; + + // Perform the clamping using the fresh ids at our disposal. + // The module will not be changed if |add_clamping_instructions| is not + // set. + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); + + uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( + *ir_context->get_def_use_mgr()->GetDef(subobject_type_id), + ir_context); + + auto bound_minus_one_id = + fuzzerutil::MaybeGetIntegerConstantFromValueAndType( + ir_context, bound - 1, index_instruction->type_id()); + + assert(bound_minus_one_id && + "A constant of value bound - 1 and the same type as the index " + "must exist as a precondition."); + + uint32_t bool_type_id = fuzzerutil::MaybeGetBoolType(ir_context); + + assert(bool_type_id && + "An OpTypeBool instruction must exist as a precondition."); + + auto int_type_inst = + ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id()); + + // Clamp the integer and add the corresponding instructions in the module + // if |add_clamping_instructions| is set. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + + // Compare the index with the bound via an instruction of the form: + // %fresh_ids.first = OpULessThanEqual %bool %int_id %bound_minus_one. + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.first()); + instruction_to_insert_before->InsertBefore(MakeUnique( + ir_context, SpvOpULessThanEqual, bool_type_id, fresh_ids.first(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + + // Select the index if in-bounds, otherwise one less than the bound: + // %fresh_ids.second = OpSelect %int_type %fresh_ids.first %int_id + // %bound_minus_one + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_ids.second()); + instruction_to_insert_before->InsertBefore(MakeUnique( + ir_context, SpvOpSelect, int_type_inst->result_id(), + fresh_ids.second(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {fresh_ids.first()}}, + {SPV_OPERAND_TYPE_ID, {index_instruction->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + + new_index_id = fresh_ids.second(); + + index_value = 0; + } + + // Add the correct index id to the operands. + operands.push_back({SPV_OPERAND_TYPE_ID, {new_index_id}}); + + // Walk to the next type in the composite object using this index. + subobject_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + ir_context, subobject_type_id, index_value); + } + // The access chain's result type is a pointer to the composite component + // that was reached after following all indices. The storage class is that + // of the original pointer. + uint32_t result_type = fuzzerutil::MaybeGetPointerType( + ir_context, subobject_type_id, + static_cast(pointer_type->GetSingleWordInOperand(0))); + + // Add the access chain instruction to the module, and update the module's + // id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpAccessChain, result_type, message_.fresh_id(), + operands)); + + // Conservatively invalidate all analyses. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // If the base pointer's pointee value was irrelevant, the same is true of + // the pointee value of the result of this access chain. + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + message_.pointer_id())) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAccessChain::ToMessage() const { + protobufs::Transformation result; + *result.mutable_access_chain() = message_; + return result; +} + +std::pair TransformationAccessChain::GetIndexValue( + opt::IRContext* ir_context, uint32_t index_id, + uint32_t object_type_id) const { + if (!ValidIndexToComposite(ir_context, index_id, object_type_id)) { + return {false, 0}; + } + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); + + uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( + *ir_context->get_def_use_mgr()->GetDef(object_type_id), ir_context); + + // The index must be a constant + if (!spvOpcodeIsConstant(index_instruction->opcode())) { + return {false, 0}; + } + + // The index must be in bounds. + uint32_t value = index_instruction->GetSingleWordInOperand(0); + + if (value >= bound) { + return {false, 0}; + } + + return {true, value}; +} + +bool TransformationAccessChain::ValidIndexToComposite( + opt::IRContext* ir_context, uint32_t index_id, uint32_t object_type_id) { + auto object_type_def = ir_context->get_def_use_mgr()->GetDef(object_type_id); + // The object being indexed must be a composite. + if (!spvOpcodeIsComposite(object_type_def->opcode())) { + return false; + } + + // Get the defining instruction of the index. + auto index_instruction = ir_context->get_def_use_mgr()->GetDef(index_id); + if (!index_instruction) { + return false; + } + + // The index type must be 32-bit integer. + auto index_type = + ir_context->get_def_use_mgr()->GetDef(index_instruction->type_id()); + if (index_type->opcode() != SpvOpTypeInt || + index_type->GetSingleWordInOperand(0) != 32) { + return false; + } + + // If the object being traversed is a struct, the id must correspond to an + // in-bound constant. + if (object_type_def->opcode() == SpvOpTypeStruct) { + if (!spvOpcodeIsConstant(index_instruction->opcode())) { + return false; + } + } + return true; +} + +std::unordered_set TransformationAccessChain::GetFreshIds() const { + std::unordered_set result = {message_.fresh_id()}; + for (auto& fresh_ids_for_clamping : message_.fresh_ids_for_clamping()) { + result.insert(fresh_ids_for_clamping.first()); + result.insert(fresh_ids_for_clamping.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_access_chain.h b/third_party/spirv-tools/source/fuzz/transformation_access_chain.h new file mode 100644 index 0000000..02cdc32 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_access_chain.h @@ -0,0 +1,106 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAccessChain : public Transformation { + public: + explicit TransformationAccessChain( + const protobufs::TransformationAccessChain& message); + + TransformationAccessChain( + uint32_t fresh_id, uint32_t pointer_id, + const std::vector& index_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before, + const std::vector>& fresh_ids_for_clamping = + {}); + + // - |message_.fresh_id| must be fresh. + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is legitimate to insert an OpAccessChain instruction. + // - |message_.pointer_id| must be a result id with pointer type that is + // available (according to dominance rules) at the insertion point. + // - The pointer must not be OpConstantNull or OpUndef. + // - |message_.index_id| must be a sequence of ids of 32-bit integers + // such that it is possible to walk the pointee type of + // |message_.pointer_id| using these indices. + // - All indices used to access a struct must be OpConstant. + // - The indices used to index non-struct composites will be clamped to be + // in bound. Enough fresh ids must be given in + // |message_.fresh_id_for_clamping| to perform clamping (2 for + // each index accessing a non-struct). This requires the bool type and + // a constant of value (bound - 1) to be declared in the module. + // - If type t is the final type reached by walking these indices, the module + // must include an instruction "OpTypePointer SC %t" where SC is the storage + // class associated with |message_.pointer_id|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction of the form: + // |message_.fresh_id| = OpAccessChain %ptr |message_.index_id| + // where %ptr is the result if of an instruction declaring a pointer to the + // type reached by walking the pointee type of |message_.pointer_id| using + // the indices in |message_.index_id|, and with the same storage class as + // |message_.pointer_id|. + // + // For each of the indices traversing non-struct composites, two clamping + // instructions are added using ids in |message_.fresh_id_for_clamping|. + // + // If the fact manager in |transformation_context| reports that + // |message_.pointer_id| has an irrelevant pointee value, then the fact that + // |message_.fresh_id| (the result of the access chain) also has an irrelevant + // pointee value is also recorded. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns {false, 0} in each of the following cases: + // - |index_id| does not correspond to a 32-bit integer constant + // - the object being indexed is not a composite type + // - the constant at |index_id| is out of bounds. + // Otherwise, returns {true, value}, where value is the value of the constant + // at |index_id|. + std::pair GetIndexValue(opt::IRContext* ir_context, + uint32_t index_id, + uint32_t object_type_id) const; + + // Returns true if |index_id| corresponds, in the given context, to a 32-bit + // integer which can be used to index an object of the type specified by + // |object_type_id|. Returns false otherwise. + static bool ValidIndexToComposite(opt::IRContext* ir_context, + uint32_t index_id, uint32_t object_type_id); + + protobufs::TransformationAccessChain message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ACCESS_CHAIN_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp new file mode 100644 index 0000000..6cdfdfb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.cpp @@ -0,0 +1,253 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_bit_instruction_synonym.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym( + const spvtools::fuzz::protobufs::TransformationAddBitInstructionSynonym& + message) + : message_(message) {} + +TransformationAddBitInstructionSynonym::TransformationAddBitInstructionSynonym( + const uint32_t instruction_result_id, + const std::vector& fresh_ids) { + message_.set_instruction_result_id(instruction_result_id); + *message_.mutable_fresh_ids() = + google::protobuf::RepeatedField( + fresh_ids.begin(), fresh_ids.end()); +} + +bool TransformationAddBitInstructionSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): + // Right now we only support certain operations. When this issue is addressed + // the following conditional can use the function |spvOpcodeIsBit|. + // |instruction| must be defined and must be a supported bit instruction. + if (!instruction || (instruction->opcode() != SpvOpBitwiseOr && + instruction->opcode() != SpvOpBitwiseXor && + instruction->opcode() != SpvOpBitwiseAnd && + instruction->opcode() != SpvOpNot)) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3792): + // Right now, only integer operands are supported. + if (ir_context->get_type_mgr()->GetType(instruction->type_id())->AsVector()) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3791): + // This condition could be relaxed if the index exists as another integer + // type. + // All bit indexes must be defined as 32-bit unsigned integers. + uint32_t width = ir_context->get_type_mgr() + ->GetType(instruction->type_id()) + ->AsInteger() + ->width(); + for (uint32_t i = 0; i < width; i++) { + if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context, + {i}, 32, false, false)) { + return false; + } + } + + // |message_.fresh_ids.size| must have the exact number of fresh ids required + // to apply the transformation. + if (static_cast(message_.fresh_ids().size()) != + GetRequiredFreshIdCount(ir_context, instruction)) { + return false; + } + + // All ids in |message_.fresh_ids| must be fresh. + for (uint32_t fresh_id : message_.fresh_ids()) { + if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) { + return false; + } + } + + return true; +} + +void TransformationAddBitInstructionSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto bit_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + + // Use an appropriate helper function to add the new instruction and new + // synonym fact. The helper function should take care of invalidating + // analyses before adding facts. + switch (bit_instruction->opcode()) { + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + AddOpBitwiseOrOpNotSynonym(ir_context, transformation_context, + bit_instruction); + break; + default: + assert(false && "Should be unreachable."); + break; + } +} + +protobufs::Transformation TransformationAddBitInstructionSynonym::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_bit_instruction_synonym() = message_; + return result; +} + +uint32_t TransformationAddBitInstructionSynonym::GetRequiredFreshIdCount( + opt::IRContext* ir_context, opt::Instruction* bit_instruction) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3557): + // Right now, only certain operations are supported. + switch (bit_instruction->opcode()) { + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + return (2 + bit_instruction->NumInOperands()) * + ir_context->get_type_mgr() + ->GetType(bit_instruction->type_id()) + ->AsInteger() + ->width() - + 1; + default: + assert(false && "Unsupported bit instruction."); + return 0; + } +} + +void TransformationAddBitInstructionSynonym::AddOpBitwiseOrOpNotSynonym( + opt::IRContext* ir_context, TransformationContext* transformation_context, + opt::Instruction* bit_instruction) const { + // Fresh id iterator. + auto fresh_id = message_.fresh_ids().begin(); + + // |width| is the bit width of operands (8, 16, 32 or 64). + const uint32_t width = ir_context->get_type_mgr() + ->GetType(bit_instruction->type_id()) + ->AsInteger() + ->width(); + + // |count| is the number of bits to be extracted and inserted at a time. + const uint32_t count = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {1}, 32, false, false); + + // |extracted_bit_instructions| is the collection of OpBiwise* or OpNot + // instructions that evaluate the extracted bits. Those ids will be used to + // insert the result bits. + std::vector extracted_bit_instructions(width); + + for (uint32_t i = 0; i < width; i++) { + // |offset| is the current bit index. + uint32_t offset = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {i}, 32, false, false); + + // |bit_extract_ids| are the two extracted bits from the operands. + opt::Instruction::OperandList bit_extract_ids; + + // Extracts the i-th bit from operands. + for (auto operand = bit_instruction->begin() + 2; + operand != bit_instruction->end(); operand++) { + auto bit_extract = + opt::Instruction(ir_context, SpvOpBitFieldUExtract, + bit_instruction->type_id(), *fresh_id++, + {{SPV_OPERAND_TYPE_ID, operand->words}, + {SPV_OPERAND_TYPE_ID, {offset}}, + {SPV_OPERAND_TYPE_ID, {count}}}); + bit_instruction->InsertBefore(MakeUnique(bit_extract)); + fuzzerutil::UpdateModuleIdBound(ir_context, bit_extract.result_id()); + bit_extract_ids.push_back( + {SPV_OPERAND_TYPE_ID, {bit_extract.result_id()}}); + } + + // Applies |bit_instruction| to the extracted bits. + auto extracted_bit_instruction = opt::Instruction( + ir_context, bit_instruction->opcode(), bit_instruction->type_id(), + *fresh_id++, bit_extract_ids); + bit_instruction->InsertBefore( + MakeUnique(extracted_bit_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, + extracted_bit_instruction.result_id()); + extracted_bit_instructions[i] = extracted_bit_instruction.result_id(); + } + + // The first two ids in |extracted_bit_instructions| are used to insert the + // first two bits of the result. + uint32_t offset = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {1}, 32, false, false); + auto bit_insert = opt::Instruction( + ir_context, SpvOpBitFieldInsert, bit_instruction->type_id(), *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[0]}}, + {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[1]}}, + {SPV_OPERAND_TYPE_ID, {offset}}, + {SPV_OPERAND_TYPE_ID, {count}}}); + bit_instruction->InsertBefore(MakeUnique(bit_insert)); + fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id()); + + // Inserts the remaining bits. + for (uint32_t i = 2; i < width; i++) { + offset = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {i}, 32, false, false); + bit_insert = opt::Instruction( + ir_context, SpvOpBitFieldInsert, bit_instruction->type_id(), + *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {bit_insert.result_id()}}, + {SPV_OPERAND_TYPE_ID, {extracted_bit_instructions[i]}}, + {SPV_OPERAND_TYPE_ID, {offset}}, + {SPV_OPERAND_TYPE_ID, {count}}}); + bit_instruction->InsertBefore(MakeUnique(bit_insert)); + fuzzerutil::UpdateModuleIdBound(ir_context, bit_insert.result_id()); + } + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // We only add a synonym fact if the bit instruction is not irrelevant, and if + // the new result id we would make it synonymous with is not irrelevant. (It + // could be irrelevant if we are in a dead block.) + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + bit_instruction->result_id()) && + !transformation_context->GetFactManager()->IdIsIrrelevant( + bit_insert.result_id())) { + // Adds the fact that the last |bit_insert| instruction is synonymous of + // |bit_instruction|. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(bit_insert.result_id(), {}), + MakeDataDescriptor(bit_instruction->result_id(), {})); + } +} + +std::unordered_set +TransformationAddBitInstructionSynonym::GetFreshIds() const { + std::unordered_set result; + for (auto id : message_.fresh_ids()) { + result.insert(id); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.h b/third_party/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.h new file mode 100644 index 0000000..ed1a0af --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_bit_instruction_synonym.h @@ -0,0 +1,143 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_BIT_INSTRUCTION_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_BIT_INSTRUCTION_SYNONYM_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// clang-format off +// SPIR-V code to help understand the transformation. +// +// ---------------------------------------------------------------------------------------------------------------- +// | Reference shader | Variant shader | +// ---------------------------------------------------------------------------------------------------------------- +// | OpCapability Shader | OpCapability Shader | +// | OpCapability Int8 | OpCapability Int8 | +// | %1 = OpExtInstImport "GLSL.std.450" | %1 = OpExtInstImport "GLSL.std.450" | +// | OpMemoryModel Logical GLSL450 | OpMemoryModel Logical GLSL450 | +// | OpEntryPoint Vertex %7 "main" | OpEntryPoint Vertex %7 "main" | +// | | | +// | ; Types | ; Types | +// | %2 = OpTypeInt 8 0 | %2 = OpTypeInt 8 0 | +// | %3 = OpTypeVoid | %3 = OpTypeVoid | +// | %4 = OpTypeFunction %3 | %4 = OpTypeFunction %3 | +// | | | +// | ; Constants | ; Constants | +// | %5 = OpConstant %2 0 | %5 = OpConstant %2 0 | +// | %6 = OpConstant %2 1 | %6 = OpConstant %2 1 | +// | | %10 = OpConstant %2 2 | +// | ; main function | %11 = OpConstant %2 3 | +// | %7 = OpFunction %3 None %4 | %12 = OpConstant %2 4 | +// | %8 = OpLabel | %13 = OpConstant %2 5 | +// | %9 = OpBitwiseOr %2 %5 %6 ; bit instruction | %14 = OpConstant %2 6 | +// | OpReturn | %15 = OpConstant %2 7 | +// | OpFunctionEnd | | +// | | ; main function | +// | | %7 = OpFunction %3 None %4 | +// | | %8 = OpLabel | +// | | | +// | | %16 = OpBitFieldUExtract %2 %5 %5 %6 ; extracts bit 0 from %5 | +// | | %17 = OpBitFieldUExtract %2 %6 %5 %6 ; extracts bit 0 from %6 | +// | | %18 = OpBitwiseOr %2 %16 %17 | +// | | | +// | | %19 = OpBitFieldUExtract %2 %5 %6 %6 ; extracts bit 1 from %5 | +// | | %20 = OpBitFieldUExtract %2 %6 %6 %6 ; extracts bit 1 from %6 | +// | | %21 = OpBitwiseOr %2 %19 %20 | +// | | | +// | | %22 = OpBitFieldUExtract %2 %5 %10 %6 ; extracts bit 2 from %5 | +// | | %23 = OpBitFieldUExtract %2 %6 %10 %6 ; extracts bit 2 from %6 | +// | | %24 = OpBitwiseOr %2 %22 %23 | +// | | | +// | | %25 = OpBitFieldUExtract %2 %5 %11 %6 ; extracts bit 3 from %5 | +// | | %26 = OpBitFieldUExtract %2 %6 %11 %6 ; extracts bit 3 from %6 | +// | | %27 = OpBitwiseOr %2 %25 %26 | +// | | | +// | | %28 = OpBitFieldUExtract %2 %5 %12 %6 ; extracts bit 4 from %5 | +// | | %29 = OpBitFieldUExtract %2 %6 %12 %6 ; extracts bit 4 from %6 | +// | | %30 = OpBitwiseOr %2 %28 %29 | +// | | | +// | | %31 = OpBitFieldUExtract %2 %5 %13 %6 ; extracts bit 5 from %5 | +// | | %32 = OpBitFieldUExtract %2 %6 %13 %6 ; extracts bit 5 from %6 | +// | | %33 = OpBitwiseOr %2 %31 %32 | +// | | | +// | | %34 = OpBitFieldUExtract %2 %5 %14 %6 ; extracts bit 6 from %5 | +// | | %35 = OpBitFieldUExtract %2 %6 %14 %6 ; extracts bit 6 from %6 | +// | | %36 = OpBitwiseOr %2 %34 %35 | +// | | | +// | | %37 = OpBitFieldUExtract %2 %5 %15 %6 ; extracts bit 7 from %5 | +// | | %38 = OpBitFieldUExtract %2 %6 %15 %6 ; extracts bit 7 from %6 | +// | | %39 = OpBitwiseOr %2 %37 %38 | +// | | | +// | | %40 = OpBitFieldInsert %2 %18 %21 %6 %6 ; inserts bit 1 | +// | | %41 = OpBitFieldInsert %2 %40 %24 %10 %6 ; inserts bit 2 | +// | | %42 = OpBitFieldInsert %2 %41 %27 %11 %6 ; inserts bit 3 | +// | | %43 = OpBitFieldInsert %2 %42 %30 %12 %6 ; inserts bit 4 | +// | | %44 = OpBitFieldInsert %2 %43 %33 %13 %6 ; inserts bit 5 | +// | | %45 = OpBitFieldInsert %2 %44 %36 %14 %6 ; inserts bit 6 | +// | | %46 = OpBitFieldInsert %2 %45 %39 %15 %6 ; inserts bit 7 | +// | | %9 = OpBitwiseOr %2 %5 %6 ; bit instruction | +// | | OpReturn | +// | | OpFunctionEnd | +// ---------------------------------------------------------------------------------------------------------------- +// +// After the transformation, %9 and %46 will be synonymous. +// clang-format on +class TransformationAddBitInstructionSynonym : public Transformation { + public: + explicit TransformationAddBitInstructionSynonym( + const protobufs::TransformationAddBitInstructionSynonym& message); + + TransformationAddBitInstructionSynonym( + const uint32_t instruction_result_id, + const std::vector& fresh_ids); + + // - |message_.instruction_result_id| must be a bit instruction. + // - |message_.fresh_ids| must be fresh ids needed to apply the + // transformation. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a bit instruction synonym. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the number of fresh ids required to apply the transformation. + static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context, + opt::Instruction* bit_instruction); + + private: + protobufs::TransformationAddBitInstructionSynonym message_; + + // Adds OpBitwise* or OpNot synonym. + void AddOpBitwiseOrOpNotSynonym(opt::IRContext* ir_context, + TransformationContext* transformation_context, + opt::Instruction* bitwise_instruction) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_BIT_INSTRUCTION_SYNONYM_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp new file mode 100644 index 0000000..937fdbc --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_boolean.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_boolean.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantBoolean::TransformationAddConstantBoolean( + const protobufs::TransformationAddConstantBoolean& message) + : message_(message) {} + +TransformationAddConstantBoolean::TransformationAddConstantBoolean( + uint32_t fresh_id, bool is_true, bool is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_is_true(is_true); + message_.set_is_irrelevant(is_irrelevant); +} + +bool TransformationAddConstantBoolean::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + return fuzzerutil::MaybeGetBoolType(ir_context) != 0 && + fuzzerutil::IsFreshId(ir_context, message_.fresh_id()); +} + +void TransformationAddConstantBoolean::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Add the boolean constant to the module, ensuring the module's id bound is + // high enough. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + ir_context->module()->AddGlobalValue( + message_.is_true() ? SpvOpConstantTrue : SpvOpConstantFalse, + message_.fresh_id(), fuzzerutil::MaybeGetBoolType(ir_context)); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddConstantBoolean::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_constant_boolean() = message_; + return result; +} + +std::unordered_set TransformationAddConstantBoolean::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_boolean.h b/third_party/spirv-tools/source/fuzz/transformation_add_constant_boolean.h new file mode 100644 index 0000000..d1d04ef --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_boolean.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantBoolean : public Transformation { + public: + explicit TransformationAddConstantBoolean( + const protobufs::TransformationAddConstantBoolean& message); + + TransformationAddConstantBoolean(uint32_t fresh_id, bool is_true, + bool is_irrelevant); + + // - |message_.fresh_id| must not be used by the module. + // - The module must already contain OpTypeBool. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Adds OpConstantTrue (OpConstantFalse) to the module with id + // |message_.fresh_id| if |message_.is_true| holds (does not hold). + // - Also, creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| + // is true. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantBoolean message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_BOOLEAN_CONSTANT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp new file mode 100644 index 0000000..0260321 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_composite.cpp @@ -0,0 +1,151 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_composite.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantComposite::TransformationAddConstantComposite( + const spvtools::fuzz::protobufs::TransformationAddConstantComposite& + message) + : message_(message) {} + +TransformationAddConstantComposite::TransformationAddConstantComposite( + uint32_t fresh_id, uint32_t type_id, + const std::vector& constituent_ids, bool is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_is_irrelevant(is_irrelevant); + for (auto constituent_id : constituent_ids) { + message_.add_constituent_id(constituent_id); + } +} + +bool TransformationAddConstantComposite::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that the given id is fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // Check that the composite type id is an instruction id. + auto composite_type_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.type_id()); + if (!composite_type_instruction) { + return false; + } + // Gather up the operands for the composite constant, in the process checking + // whether the given type really defines a composite and - in the case of a + // struct - whether its decorations are OK. + std::vector constituent_type_ids; + switch (composite_type_instruction->opcode()) { + case SpvOpTypeArray: + for (uint32_t index = 0; + index < + fuzzerutil::GetArraySize(*composite_type_instruction, ir_context); + index++) { + constituent_type_ids.push_back( + composite_type_instruction->GetSingleWordInOperand(0)); + } + break; + case SpvOpTypeMatrix: + case SpvOpTypeVector: + for (uint32_t index = 0; + index < composite_type_instruction->GetSingleWordInOperand(1); + index++) { + constituent_type_ids.push_back( + composite_type_instruction->GetSingleWordInOperand(0)); + } + break; + case SpvOpTypeStruct: + // We do not create constants of structs decorated with Block nor + // BufferBlock. The SPIR-V spec does not explicitly disallow this, but it + // seems like a strange thing to do, so we disallow it to avoid triggering + // low priorty edge case issues related to it. + if (fuzzerutil::HasBlockOrBufferBlockDecoration( + ir_context, composite_type_instruction->result_id())) { + return false; + } + composite_type_instruction->ForEachInOperand( + [&constituent_type_ids](const uint32_t* member_type_id) { + constituent_type_ids.push_back(*member_type_id); + }); + break; + default: + // Not a composite type. + return false; + } + + // Check that the number of provided operands matches the number of + // constituents required by the type. + if (constituent_type_ids.size() != + static_cast(message_.constituent_id().size())) { + return false; + } + + // Check that every provided operand refers to an instruction of the + // corresponding constituent type. + for (uint32_t index = 0; index < constituent_type_ids.size(); index++) { + auto constituent_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.constituent_id(index)); + if (!constituent_instruction) { + return false; + } + if (constituent_instruction->type_id() != constituent_type_ids.at(index)) { + return false; + } + } + return true; +} + +void TransformationAddConstantComposite::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + opt::Instruction::OperandList in_operands; + for (auto constituent_id : message_.constituent_id()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {constituent_id}}); + } + ir_context->module()->AddGlobalValue(MakeUnique( + ir_context, SpvOpConstantComposite, message_.type_id(), + message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddConstantComposite::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_constant_composite() = message_; + return result; +} + +std::unordered_set TransformationAddConstantComposite::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_composite.h b/third_party/spirv-tools/source/fuzz/transformation_add_constant_composite.h new file mode 100644 index 0000000..9e9222d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_composite.h @@ -0,0 +1,66 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantComposite : public Transformation { + public: + explicit TransformationAddConstantComposite( + const protobufs::TransformationAddConstantComposite& message); + + TransformationAddConstantComposite( + uint32_t fresh_id, uint32_t type_id, + const std::vector& constituent_ids, bool is_irrelevant); + + // - |message_.fresh_id| must be a fresh id + // - |message_.type_id| must be the id of a composite type + // - |message_.constituent_id| must refer to ids that match the constituent + // types of this composite type + // - If |message_.type_id| is a struct type, it must not have the Block or + // BufferBlock decoration + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Adds an OpConstantComposite instruction defining a constant of type + // |message_.type_id|, using |message_.constituent_id| as constituents, with + // result id |message_.fresh_id|. + // - Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is + // true. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantComposite message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_COMPOSITE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.cpp new file mode 100644 index 0000000..3c66ab1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_null.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantNull::TransformationAddConstantNull( + const spvtools::fuzz::protobufs::TransformationAddConstantNull& message) + : message_(message) {} + +TransformationAddConstantNull::TransformationAddConstantNull(uint32_t fresh_id, + uint32_t type_id) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); +} + +bool TransformationAddConstantNull::IsApplicable( + opt::IRContext* context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(context, message_.fresh_id())) { + return false; + } + auto type = context->get_type_mgr()->GetType(message_.type_id()); + // The type must exist. + if (!type) { + return false; + } + // The type must be one of the types for which null constants are allowed, + // according to the SPIR-V spec. + return fuzzerutil::IsNullConstantSupported(*type); +} + +void TransformationAddConstantNull::Apply( + opt::IRContext* context, TransformationContext* /*unused*/) const { + context->module()->AddGlobalValue(MakeUnique( + context, SpvOpConstantNull, message_.type_id(), message_.fresh_id(), + opt::Instruction::OperandList())); + fuzzerutil::UpdateModuleIdBound(context, message_.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddConstantNull::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_constant_null() = message_; + return result; +} + +std::unordered_set TransformationAddConstantNull::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.h b/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.h new file mode 100644 index 0000000..bd08b1d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_null.h @@ -0,0 +1,56 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantNull : public Transformation { + public: + explicit TransformationAddConstantNull( + const protobufs::TransformationAddConstantNull& message); + + TransformationAddConstantNull(uint32_t fresh_id, uint32_t type_id); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a type for which it is acceptable + // to create a null constant + bool IsApplicable( + opt::IRContext* context, + const TransformationContext& transformation_context) const override; + + // Adds an OpConstantNull instruction to the module, with |message_.type_id| + // as its type. The instruction has result id |message_.fresh_id|. + void Apply(opt::IRContext* context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantNull message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_NULL_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp new file mode 100644 index 0000000..9a6642a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_scalar.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_scalar.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddConstantScalar::TransformationAddConstantScalar( + const spvtools::fuzz::protobufs::TransformationAddConstantScalar& message) + : message_(message) {} + +TransformationAddConstantScalar::TransformationAddConstantScalar( + uint32_t fresh_id, uint32_t type_id, const std::vector& words, + bool is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_is_irrelevant(is_irrelevant); + for (auto word : words) { + message_.add_word(word); + } +} + +bool TransformationAddConstantScalar::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id needs to be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The type id for the scalar must exist and be a type. + auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); + if (!type) { + return false; + } + uint32_t width; + if (type->AsFloat()) { + width = type->AsFloat()->width(); + } else if (type->AsInteger()) { + width = type->AsInteger()->width(); + } else { + return false; + } + // The number of words is the integer floor of the width. + auto words = (width + 32 - 1) / 32; + + // The number of words provided by the transformation needs to match the + // width of the type. + return static_cast(message_.word().size()) == words; +} + +void TransformationAddConstantScalar::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + ir_context->module()->AddGlobalValue(MakeUnique( + ir_context, SpvOpConstant, message_.type_id(), message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_LITERAL_INTEGER, + std::vector(message_.word().begin(), + message_.word().end())}}))); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + if (message_.is_irrelevant()) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddConstantScalar::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_constant_scalar() = message_; + return result; +} + +std::unordered_set TransformationAddConstantScalar::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_constant_scalar.h b/third_party/spirv-tools/source/fuzz/transformation_add_constant_scalar.h new file mode 100644 index 0000000..3f23907 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_constant_scalar.h @@ -0,0 +1,61 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_SCALAR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_SCALAR_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddConstantScalar : public Transformation { + public: + explicit TransformationAddConstantScalar( + const protobufs::TransformationAddConstantScalar& message); + + TransformationAddConstantScalar(uint32_t fresh_id, uint32_t type_id, + const std::vector& words, + bool is_irrelevant); + + // - |message_.fresh_id| must not be used by the module + // - |message_.type_id| must be the id of a floating-point or integer type + // - The size of |message_.word| must be compatible with the width of this + // type + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a new OpConstant instruction with the given type and words. + // Creates an IdIsIrrelevant fact about |fresh_id| if |is_irrelevant| is true. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddConstantScalar message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_CONSTANT_SCALAR_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp new file mode 100644 index 0000000..44bb9c5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_copy_memory.cpp @@ -0,0 +1,210 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_copy_memory.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddCopyMemory::TransformationAddCopyMemory( + const protobufs::TransformationAddCopyMemory& message) + : message_(message) {} + +TransformationAddCopyMemory::TransformationAddCopyMemory( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class, + uint32_t initializer_id) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; + message_.set_fresh_id(fresh_id); + message_.set_source_id(source_id); + message_.set_storage_class(storage_class); + message_.set_initializer_id(initializer_id); +} + +bool TransformationAddCopyMemory::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that target id is fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Check that instruction descriptor is valid. This also checks that + // |message_.instruction_descriptor| is not a global instruction. + auto* inst = FindInstruction(message_.instruction_descriptor(), ir_context); + if (!inst) { + return false; + } + + // Check that we can insert OpCopyMemory before |instruction_descriptor|. + auto iter = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(inst), inst); + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCopyMemory, iter)) { + return false; + } + + // Check that source instruction exists and is valid. + auto* source_inst = + ir_context->get_def_use_mgr()->GetDef(message_.source_id()); + if (!source_inst || !IsInstructionSupported(ir_context, source_inst)) { + return false; + } + + // |storage_class| is either Function or Private. + if (message_.storage_class() != SpvStorageClassFunction && + message_.storage_class() != SpvStorageClassPrivate) { + return false; + } + + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, source_inst->type_id()); + + // OpTypePointer with |message_.storage_class| exists. + if (!fuzzerutil::MaybeGetPointerType( + ir_context, pointee_type_id, + static_cast(message_.storage_class()))) { + return false; + } + + // Check that |initializer_id| exists and has valid type. + const auto* initializer_inst = + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); + if (!initializer_inst || initializer_inst->type_id() != pointee_type_id) { + return false; + } + + // Check that domination rules are satisfied. + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, inst, + message_.source_id()); +} + +void TransformationAddCopyMemory::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Insert OpCopyMemory before |instruction_descriptor|. + auto* insert_before_inst = + FindInstruction(message_.instruction_descriptor(), ir_context); + assert(insert_before_inst); + + auto insert_before_iter = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(insert_before_inst), insert_before_inst); + + insert_before_iter.InsertBefore(MakeUnique( + ir_context, SpvOpCopyMemory, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.source_id()}}})); + + // Add global or local variable to copy memory into. + auto storage_class = static_cast(message_.storage_class()); + auto type_id = fuzzerutil::MaybeGetPointerType( + ir_context, + fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.source_id())), + storage_class); + + if (storage_class == SpvStorageClassPrivate) { + fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_id(), type_id, + storage_class, message_.initializer_id()); + } else { + assert(storage_class == SpvStorageClassFunction && + "Storage class can be either Private or Function"); + fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), type_id, + ir_context->get_instr_block(insert_before_inst) + ->GetParent() + ->result_id(), + message_.initializer_id()); + } + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // Even though the copy memory instruction will - at least temporarily - lead + // to the destination and source pointers referring to identical values, this + // fact is not guaranteed to hold throughout execution of the SPIR-V code + // since the source pointer could be over-written. We thus assume nothing + // about the destination pointer, and record this fact so that the destination + // pointer can be used freely by other fuzzer passes. + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); +} + +protobufs::Transformation TransformationAddCopyMemory::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_copy_memory() = message_; + return result; +} + +bool TransformationAddCopyMemory::IsInstructionSupported( + opt::IRContext* ir_context, opt::Instruction* inst) { + if (!inst->result_id() || !inst->type_id() || + inst->opcode() == SpvOpConstantNull || inst->opcode() == SpvOpUndef) { + return false; + } + + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction must have a valid type"); + + if (!type->AsPointer()) { + return false; + } + + // We do not support copying memory from a pointer to a block-/buffer + // block-decorated struct. + auto pointee_type_inst = ir_context->get_def_use_mgr() + ->GetDef(inst->type_id()) + ->GetSingleWordInOperand(1); + if (fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, + pointee_type_inst)) { + return false; + } + + return CanUsePointeeWithCopyMemory(*type->AsPointer()->pointee_type()); +} + +bool TransformationAddCopyMemory::CanUsePointeeWithCopyMemory( + const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kBool: + case opt::analysis::Type::kInteger: + case opt::analysis::Type::kFloat: + case opt::analysis::Type::kArray: + return true; + case opt::analysis::Type::kVector: + return CanUsePointeeWithCopyMemory(*type.AsVector()->element_type()); + case opt::analysis::Type::kMatrix: + return CanUsePointeeWithCopyMemory(*type.AsMatrix()->element_type()); + case opt::analysis::Type::kStruct: + return std::all_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element) { + return CanUsePointeeWithCopyMemory(*element); + }); + default: + return false; + } +} + +std::unordered_set TransformationAddCopyMemory::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_copy_memory.h b/third_party/spirv-tools/source/fuzz/transformation_add_copy_memory.h new file mode 100644 index 0000000..cc42f1e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_copy_memory.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddCopyMemory : public Transformation { + public: + explicit TransformationAddCopyMemory( + const protobufs::TransformationAddCopyMemory& message); + + TransformationAddCopyMemory( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id, uint32_t source_id, SpvStorageClass storage_class, + uint32_t initializer_id); + + // - |instruction_descriptor| must point to a valid instruction in the module. + // - it should be possible to insert OpCopyMemory before + // |instruction_descriptor| (i.e. the module remains valid after the + // insertion). + // - |source_id| must be a result id for some valid instruction in the module. + // - |fresh_id| must be a fresh id to copy memory into. + // - type of |source_id| must be OpTypePointer where pointee can be used with + // OpCopyMemory. + // - If the pointee type of |source_id| is a struct type, it must not have the + // Block or BufferBlock decoration. + // - |storage_class| must be either Private or Function. + // - type ids of instructions with result ids |source_id| and |initialize_id| + // must be the same. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A global or local variable with id |target_id| and |storage_class| class is + // created. An 'OpCopyMemory %fresh_id %source_id' instruction is inserted + // before the |instruction_descriptor|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if we can copy memory from |instruction| using OpCopyMemory. + static bool IsInstructionSupported(opt::IRContext* ir_context, + opt::Instruction* inst); + + private: + // Returns whether the type, pointed to by some OpTypePointer, can be used + // with OpCopyMemory instruction. + static bool CanUsePointeeWithCopyMemory(const opt::analysis::Type& type); + + protobufs::TransformationAddCopyMemory message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_COPY_MEMORY_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_dead_block.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_dead_block.cpp new file mode 100644 index 0000000..3d77a80 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_dead_block.cpp @@ -0,0 +1,197 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_block.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddDeadBlock::TransformationAddDeadBlock( + const spvtools::fuzz::protobufs::TransformationAddDeadBlock& message) + : message_(message) {} + +TransformationAddDeadBlock::TransformationAddDeadBlock(uint32_t fresh_id, + uint32_t existing_block, + bool condition_value) { + message_.set_fresh_id(fresh_id); + message_.set_existing_block(existing_block); + message_.set_condition_value(condition_value); +} + +bool TransformationAddDeadBlock::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The new block's id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // First, we check that a constant with the same value as + // |message_.condition_value| is present. + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.condition_value(), false)) { + // The required constant is not present, so the transformation cannot be + // applied. + return false; + } + + // The existing block must indeed exist. + auto existing_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.existing_block()); + if (!existing_block) { + return false; + } + + // It must not head a loop. + if (existing_block->IsLoopHeader()) { + return false; + } + + // It must end with OpBranch. + if (existing_block->terminator()->opcode() != SpvOpBranch) { + return false; + } + + // Its successor must not be a merge block nor continue target. + auto successor_block_id = + existing_block->terminator()->GetSingleWordInOperand(0); + if (fuzzerutil::IsMergeOrContinue(ir_context, successor_block_id)) { + return false; + } + + // The successor must not be a loop header (i.e., |message_.existing_block| + // must not be a back-edge block. + if (ir_context->cfg()->block(successor_block_id)->IsLoopHeader()) { + return false; + } + + // |existing_block| must be reachable. + opt::DominatorAnalysis* dominator_analysis = + ir_context->GetDominatorAnalysis(existing_block->GetParent()); + if (!dominator_analysis->IsReachable(existing_block->id())) { + return false; + } + + assert(existing_block->id() != successor_block_id && + "|existing_block| must be different from |successor_block_id|"); + + // Even though we know |successor_block_id| is not a merge block, it might + // still have multiple predecessors because divergent control flow is allowed + // to converge early (before the merge block). In this case, when we create + // the selection construct, its header |existing_block| will not dominate the + // merge block |successor_block_id|, which is invalid. Thus, |existing_block| + // must dominate |successor_block_id|. + if (!dominator_analysis->Dominates(existing_block->id(), + successor_block_id)) { + return false; + } + + return true; +} + +void TransformationAddDeadBlock::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Update the module id bound so that it is at least the id of the new block. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Get the existing block and its successor. + auto existing_block = ir_context->cfg()->block(message_.existing_block()); + auto successor_block_id = + existing_block->terminator()->GetSingleWordInOperand(0); + + // Get the id of the boolean value that will be used as the branch condition. + auto bool_id = fuzzerutil::MaybeGetBoolConstant( + ir_context, *transformation_context, message_.condition_value(), false); + + // Make a new block that unconditionally branches to the original successor + // block. + auto enclosing_function = existing_block->GetParent(); + std::unique_ptr new_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.fresh_id(), + opt::Instruction::OperandList())); + new_block->AddInstruction(MakeUnique( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {successor_block_id}}}))); + + // Turn the original block into a selection merge, with its original successor + // as the merge block. + existing_block->terminator()->InsertBefore(MakeUnique( + ir_context, SpvOpSelectionMerge, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {successor_block_id}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {SpvSelectionControlMaskNone}}}))); + + // Change the original block's terminator to be a conditional branch on the + // given boolean, with the original successor and the new successor as branch + // targets, and such that at runtime control will always transfer to the + // original successor. + existing_block->terminator()->SetOpcode(SpvOpBranchConditional); + existing_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {bool_id}}, + {SPV_OPERAND_TYPE_ID, + {message_.condition_value() ? successor_block_id + : message_.fresh_id()}}, + {SPV_OPERAND_TYPE_ID, + {message_.condition_value() ? message_.fresh_id() + : successor_block_id}}}); + + // Add the new block to the enclosing function. + new_block->SetParent(enclosing_function); + enclosing_function->InsertBasicBlockAfter(std::move(new_block), + existing_block); + + // Fix up OpPhi instructions in the successor block, so that the values they + // yield when control has transferred from the new block are the same as if + // control had transferred from |message_.existing_block|. This is guaranteed + // to be valid since |message_.existing_block| dominates the new block by + // construction. Other transformations can change these phi operands to more + // interesting values. + ir_context->cfg() + ->block(successor_block_id) + ->ForEachPhiInst([this](opt::Instruction* phi_inst) { + // Copy the operand that provides the phi value for the first of any + // existing predecessors. + opt::Operand copy_of_existing_operand = phi_inst->GetInOperand(0); + // Use this as the value associated with the new predecessor. + phi_inst->AddOperand(std::move(copy_of_existing_operand)); + phi_inst->AddOperand({SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}); + }); + + // Do not rely on any existing analysis results since the control flow graph + // of the module has changed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Record the fact that the new block is dead. + transformation_context->GetFactManager()->AddFactBlockIsDead( + message_.fresh_id()); +} + +protobufs::Transformation TransformationAddDeadBlock::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_dead_block() = message_; + return result; +} + +std::unordered_set TransformationAddDeadBlock::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_dead_block.h b/third_party/spirv-tools/source/fuzz/transformation_add_dead_block.h new file mode 100644 index 0000000..50af6b0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_dead_block.h @@ -0,0 +1,67 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddDeadBlock : public Transformation { + public: + explicit TransformationAddDeadBlock( + const protobufs::TransformationAddDeadBlock& message); + + TransformationAddDeadBlock(uint32_t fresh_id, uint32_t existing_block, + bool condition_value); + + // - |message_.fresh_id| must be a fresh id + // - A constant with the same value as |message_.condition_value| must be + // available + // - |message_.existing_block| must be a block that is not a loop header, + // and that ends with OpBranch to a block that is not a merge block nor + // continue target - this is because the successor will become the merge + // block of a selection construct headed at |message_.existing_block| + // - |message_.existing_block| must not be a back-edge block, since in this + // case the newly-added block would lead to another back-edge to the + // associated loop header + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Changes the OpBranch from |message_.existing_block| to its successor 's' + // to an OpBranchConditional to either 's' or a new block, + // |message_.fresh_id|, which itself unconditionally branches to 's'. The + // conditional branch uses |message.condition_value| as its condition, and is + // arranged so that control will pass to 's' at runtime. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddDeadBlock message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BLOCK_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_dead_break.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_dead_break.cpp new file mode 100644 index 0000000..bc938d4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_dead_break.cpp @@ -0,0 +1,220 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_break.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" +#include "source/opt/struct_cfg_analysis.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddDeadBreak::TransformationAddDeadBreak( + const spvtools::fuzz::protobufs::TransformationAddDeadBreak& message) + : message_(message) {} + +TransformationAddDeadBreak::TransformationAddDeadBreak( + uint32_t from_block, uint32_t to_block, bool break_condition_value, + std::vector phi_id) { + message_.set_from_block(from_block); + message_.set_to_block(to_block); + message_.set_break_condition_value(break_condition_value); + for (auto id : phi_id) { + message_.add_phi_id(id); + } +} + +bool TransformationAddDeadBreak::AddingBreakRespectsStructuredControlFlow( + opt::IRContext* ir_context, opt::BasicBlock* bb_from) const { + // Look at the structured control flow associated with |from_block| and + // check whether it is contained in an appropriate construct with merge id + // |to_block| such that a break from |from_block| to |to_block| is legal. + + // There are three legal cases to consider: + // (1) |from_block| is a loop header and |to_block| is its merge + // (2) |from_block| is a non-header node of a construct, and |to_block| + // is the merge for that construct + // (3) |from_block| is a non-header node of a selection construct, and + // |to_block| is the merge for the innermost loop containing + // |from_block| + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2653) It may be + // possible to be more aggressive in breaking from switch constructs. + // + // The reason we need to distinguish between cases (1) and (2) is that the + // structured CFG analysis does not deem a header to be part of the construct + // that it heads. + + // Consider case (1) + if (bb_from->IsLoopHeader()) { + // Case (1) holds if |to_block| is the merge block for the loop; + // otherwise no case holds + return bb_from->MergeBlockId() == message_.to_block(); + } + + // Both cases (2) and (3) require that |from_block| is inside some + // structured control flow construct. + + auto containing_construct = + ir_context->GetStructuredCFGAnalysis()->ContainingConstruct( + message_.from_block()); + if (!containing_construct) { + // |from_block| is not in a construct from which we can break. + return false; + } + + // Consider case (2) + if (message_.to_block() == + ir_context->cfg()->block(containing_construct)->MergeBlockId()) { + // This looks like an instance of case (2). + // However, the structured CFG analysis regards the continue construct of a + // loop as part of the loop, but it is not legal to jump from a loop's + // continue construct to the loop's merge (except from the back-edge block), + // so we need to check for this case. + return !fuzzerutil::BlockIsInLoopContinueConstruct( + ir_context, message_.from_block(), containing_construct) || + fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(), + containing_construct); + } + + // Case (3) holds if and only if |to_block| is the merge block for this + // innermost loop that contains |from_block| + auto containing_loop_header = + ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + message_.from_block()); + if (containing_loop_header && + message_.to_block() == + ir_context->cfg()->block(containing_loop_header)->MergeBlockId()) { + return !fuzzerutil::BlockIsInLoopContinueConstruct( + ir_context, message_.from_block(), containing_loop_header) || + fuzzerutil::BlockIsBackEdge(ir_context, message_.from_block(), + containing_loop_header); + } + return false; +} + +bool TransformationAddDeadBreak::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // First, we check that a constant with the same value as + // |message_.break_condition_value| is present. + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.break_condition_value(), + false)) { + // The required constant is not present, so the transformation cannot be + // applied. + return false; + } + + // Check that |message_.from_block| and |message_.to_block| really are block + // ids + opt::BasicBlock* bb_from = + fuzzerutil::MaybeFindBlock(ir_context, message_.from_block()); + if (bb_from == nullptr) { + return false; + } + opt::BasicBlock* bb_to = + fuzzerutil::MaybeFindBlock(ir_context, message_.to_block()); + if (bb_to == nullptr) { + return false; + } + + if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, bb_to)) { + // If the target of the break is unreachable, we conservatively do not + // allow adding a dead break, to avoid the compilations that arise due to + // the lack of sensible dominance information for unreachable blocks. + return false; + } + + // Check that |message_.from_block| ends with an unconditional branch. + if (bb_from->terminator()->opcode() != SpvOpBranch) { + // The block associated with the id does not end with an unconditional + // branch. + return false; + } + + assert(bb_from != nullptr && + "We should have found a block if this line of code is reached."); + assert( + bb_from->id() == message_.from_block() && + "The id of the block we found should match the source id for the break."); + assert(bb_to != nullptr && + "We should have found a block if this line of code is reached."); + assert( + bb_to->id() == message_.to_block() && + "The id of the block we found should match the target id for the break."); + + // Check whether the data passed to extend OpPhi instructions is appropriate. + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from, bb_to, + message_.phi_id())) { + return false; + } + + // Check that adding the break would respect the rules of structured + // control flow. + if (!AddingBreakRespectsStructuredControlFlow(ir_context, bb_from)) { + return false; + } + + // Adding the dead break is only valid if SPIR-V rules related to dominance + // hold. Rather than checking these rules explicitly, we defer to the + // validator. We make a clone of the module, apply the transformation to the + // clone, and check whether the transformed clone is valid. + // + // In principle some of the above checks could be removed, with more reliance + // being places on the validator. This should be revisited if we are sure + // the validator is complete with respect to checking structured control flow + // rules. + auto cloned_context = fuzzerutil::CloneIRContext(ir_context); + ApplyImpl(cloned_context.get(), transformation_context); + return fuzzerutil::IsValid(cloned_context.get(), + transformation_context.GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer); +} + +void TransformationAddDeadBreak::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + ApplyImpl(ir_context, *transformation_context); + // Invalidate all analyses + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddDeadBreak::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_dead_break() = message_; + return result; +} + +void TransformationAddDeadBreak::ApplyImpl( + spvtools::opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( + ir_context, ir_context->cfg()->block(message_.from_block()), + ir_context->cfg()->block(message_.to_block()), + fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.break_condition_value(), false), + message_.phi_id()); +} + +std::unordered_set TransformationAddDeadBreak::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_dead_break.h b/third_party/spirv-tools/source/fuzz/transformation_add_dead_break.h new file mode 100644 index 0000000..afb8dc7 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_dead_break.h @@ -0,0 +1,89 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BREAK_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BREAK_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddDeadBreak : public Transformation { + public: + explicit TransformationAddDeadBreak( + const protobufs::TransformationAddDeadBreak& message); + + TransformationAddDeadBreak(uint32_t from_block, uint32_t to_block, + bool break_condition_value, + std::vector phi_id); + + // - |message_.from_block| must be the id of a block a in the given module. + // - |message_.to_block| must be the id of a block b in the given module. + // - if |message_.break_condition_value| holds (does not hold) then + // OpConstantTrue (OpConstantFalse) must be present in the module + // - |message_.phi_ids| must be a list of ids that are all available at + // |message_.from_block| + // - a and b must be in the same function. + // - b must be a merge block. + // - a must end with an unconditional branch to some block c. + // - replacing this branch with a conditional branch to b or c, with + // the boolean constant associated with |message_.break_condition_value| as + // the condition, and the ids in |message_.phi_ids| used to extend + // any OpPhi instructions at b as a result of the edge from a, must + // maintain validity of the module. + // In particular, the new branch must not lead to violations of the rule + // that a use must be dominated by its definition. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the terminator of a with a conditional branch to b or c. + // The boolean constant associated with |message_.break_condition_value| is + // used as the condition, and the order of b and c is arranged such that + // control is guaranteed to jump to c. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns true if and only if adding an edge from |bb_from| to + // |message_.to_block| respects structured control flow. + bool AddingBreakRespectsStructuredControlFlow(opt::IRContext* ir_context, + opt::BasicBlock* bb_from) const; + + // Used by 'Apply' to actually apply the transformation to the module of + // interest, and by 'IsApplicable' to do a dry-run of the transformation on a + // cloned module, in order to check that the transformation leads to a valid + // module. This is only invoked by 'IsApplicable' after certain basic + // applicability checks have been made, ensuring that the invocation of this + // method is legal. + void ApplyImpl(opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; + + protobufs::TransformationAddDeadBreak message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_BREAK_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp new file mode 100644 index 0000000..18b3c39 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_dead_continue.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_continue.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddDeadContinue::TransformationAddDeadContinue( + const spvtools::fuzz::protobufs::TransformationAddDeadContinue& message) + : message_(message) {} + +TransformationAddDeadContinue::TransformationAddDeadContinue( + uint32_t from_block, bool continue_condition_value, + std::vector phi_id) { + message_.set_from_block(from_block); + message_.set_continue_condition_value(continue_condition_value); + for (auto id : phi_id) { + message_.add_phi_id(id); + } +} + +bool TransformationAddDeadContinue::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // First, we check that a constant with the same value as + // |message_.continue_condition_value| is present. + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.continue_condition_value(), + false)) { + // The required constant is not present, so the transformation cannot be + // applied. + return false; + } + + // Check that |message_.from_block| really is a block id. + opt::BasicBlock* bb_from = + fuzzerutil::MaybeFindBlock(ir_context, message_.from_block()); + if (bb_from == nullptr) { + return false; + } + + // Check that |message_.from_block| ends with an unconditional branch. + if (bb_from->terminator()->opcode() != SpvOpBranch) { + // The block associated with the id does not end with an unconditional + // branch. + return false; + } + + assert(bb_from != nullptr && + "We should have found a block if this line of code is reached."); + assert( + bb_from->id() == message_.from_block() && + "The id of the block we found should match the source id for the break."); + + // Get the header for the innermost loop containing |message_.from_block|. + // Because the structured CFG analysis does not regard a loop header as part + // of the loop it heads, we check first whether bb_from is a loop header + // before using the structured CFG analysis. + auto loop_header = + bb_from->IsLoopHeader() + ? message_.from_block() + : ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + message_.from_block()); + if (!loop_header) { + return false; + } + + auto continue_block = + ir_context->cfg()->block(loop_header)->ContinueBlockId(); + + if (!fuzzerutil::BlockIsReachableInItsFunction( + ir_context, ir_context->cfg()->block(continue_block))) { + // If the loop's continue block is unreachable, we conservatively do not + // allow adding a dead continue, to avoid the compilations that arise due to + // the lack of sensible dominance information for unreachable blocks. + return false; + } + + if (fuzzerutil::BlockIsInLoopContinueConstruct( + ir_context, message_.from_block(), loop_header)) { + // We cannot jump to the continue target from the continue construct. + return false; + } + + if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(continue_block)) { + // A branch straight to the continue target that is also a merge block might + // break the property that a construct header must dominate its merge block + // (if the merge block is reachable). + return false; + } + + // Check whether the data passed to extend OpPhi instructions is appropriate. + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, bb_from, + ir_context->cfg()->block(continue_block), + message_.phi_id())) { + return false; + } + + // Adding the dead break is only valid if SPIR-V rules related to dominance + // hold. Rather than checking these rules explicitly, we defer to the + // validator. We make a clone of the module, apply the transformation to the + // clone, and check whether the transformed clone is valid. + // + // In principle some of the above checks could be removed, with more reliance + // being placed on the validator. This should be revisited if we are sure + // the validator is complete with respect to checking structured control flow + // rules. + auto cloned_context = fuzzerutil::CloneIRContext(ir_context); + ApplyImpl(cloned_context.get(), transformation_context); + return fuzzerutil::IsValid(cloned_context.get(), + transformation_context.GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer); +} + +void TransformationAddDeadContinue::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + ApplyImpl(ir_context, *transformation_context); + // Invalidate all analyses + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddDeadContinue::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_dead_continue() = message_; + return result; +} + +void TransformationAddDeadContinue::ApplyImpl( + spvtools::opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto bb_from = ir_context->cfg()->block(message_.from_block()); + auto continue_block = + bb_from->IsLoopHeader() + ? bb_from->ContinueBlockId() + : ir_context->GetStructuredCFGAnalysis()->LoopContinueBlock( + message_.from_block()); + assert(continue_block && "message_.from_block must be in a loop."); + fuzzerutil::AddUnreachableEdgeAndUpdateOpPhis( + ir_context, bb_from, ir_context->cfg()->block(continue_block), + fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.continue_condition_value(), + false), + message_.phi_id()); +} + +std::unordered_set TransformationAddDeadContinue::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_dead_continue.h b/third_party/spirv-tools/source/fuzz/transformation_add_dead_continue.h new file mode 100644 index 0000000..27527e7 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_dead_continue.h @@ -0,0 +1,86 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_CONTINUE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_CONTINUE_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddDeadContinue : public Transformation { + public: + explicit TransformationAddDeadContinue( + const protobufs::TransformationAddDeadContinue& message); + + TransformationAddDeadContinue(uint32_t from_block, + bool continue_condition_value, + std::vector phi_id); + + // - |message_.from_block| must be the id of a block a in the given module. + // - a must be contained in a loop with continue target b + // - The continue target b must be dominated by the head of the loop in which + // it is contained + // - b must not be the merge block of a selection construct + // - if |message_.continue_condition_value| holds (does not hold) then + // OpConstantTrue (OpConstantFalse) must be present in the module + // - |message_.phi_ids| must be a list of ids that are all available at + // |message_.from_block| + // - a must end with an unconditional branch to some block c. + // - replacing this branch with a conditional branch to b or c, with + // the boolean constant associated with |message_.continue_condition_value| + // as the condition, and the ids in |message_.phi_ids| used to extend any + // OpPhi instructions at b as a result of the edge from a, must maintain + // validity of the module. + // In particular, adding an edge from somewhere in the loop to the continue + // target must not prevent uses of ids in the continue target from being + // dominated by the definitions of those ids. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the terminator of a with a conditional branch to b or c. + // The boolean constant associated with |message_.continue_condition_value| is + // used as the condition, and the order of b and c is arranged such that + // control is guaranteed to jump to c. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Used by 'Apply' to actually apply the transformation to the module of + // interest, and by 'IsApplicable' to do a dry-run of the transformation on a + // cloned module, in order to check that the transformation leads to a valid + // module. This is only invoked by 'IsApplicable' after certain basic + // applicability checks have been made, ensuring that the invocation of this + // method is legal. + void ApplyImpl(opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; + + protobufs::TransformationAddDeadContinue message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_DEAD_CONTINUE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.cpp new file mode 100644 index 0000000..0aa1214 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_early_terminator_wrapper.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddEarlyTerminatorWrapper:: + TransformationAddEarlyTerminatorWrapper( + const spvtools::fuzz::protobufs:: + TransformationAddEarlyTerminatorWrapper& message) + : message_(message) {} + +TransformationAddEarlyTerminatorWrapper:: + TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id, + uint32_t label_fresh_id, + SpvOp opcode) { + message_.set_function_fresh_id(function_fresh_id); + message_.set_label_fresh_id(label_fresh_id); + message_.set_opcode(opcode); +} + +bool TransformationAddEarlyTerminatorWrapper::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + assert((message_.opcode() == SpvOpKill || + message_.opcode() == SpvOpUnreachable || + message_.opcode() == SpvOpTerminateInvocation) && + "Invalid opcode."); + + if (!fuzzerutil::IsFreshId(ir_context, message_.function_fresh_id())) { + return false; + } + if (!fuzzerutil::IsFreshId(ir_context, message_.label_fresh_id())) { + return false; + } + if (message_.function_fresh_id() == message_.label_fresh_id()) { + return false; + } + uint32_t void_type_id = fuzzerutil::MaybeGetVoidType(ir_context); + if (!void_type_id) { + return false; + } + return fuzzerutil::FindFunctionType(ir_context, {void_type_id}); +} + +void TransformationAddEarlyTerminatorWrapper::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.function_fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.label_fresh_id()); + + // Create a basic block of the form: + // %label_fresh_id = OpLabel + // OpKill|Unreachable|TerminateInvocation + auto basic_block = MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.label_fresh_id(), + opt::Instruction::OperandList())); + basic_block->AddInstruction(MakeUnique( + ir_context, static_cast(message_.opcode()), 0, 0, + opt::Instruction::OperandList())); + + // Create a zero-argument void function. + auto void_type_id = fuzzerutil::MaybeGetVoidType(ir_context); + auto function = MakeUnique(MakeUnique( + ir_context, SpvOpFunction, void_type_id, message_.function_fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, + {fuzzerutil::FindFunctionType(ir_context, {void_type_id})}}}))); + + // Add the basic block to the function as the sole block, and add the function + // to the module. + basic_block->SetParent(function.get()); + function->AddBasicBlock(std::move(basic_block)); + function->SetFunctionEnd(MakeUnique( + ir_context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList())); + ir_context->module()->AddFunction(std::move(function)); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +std::unordered_set +TransformationAddEarlyTerminatorWrapper::GetFreshIds() const { + return std::unordered_set( + {message_.function_fresh_id(), message_.label_fresh_id()}); +} + +protobufs::Transformation TransformationAddEarlyTerminatorWrapper::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_early_terminator_wrapper() = message_; + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.h b/third_party/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.h new file mode 100644 index 0000000..273037e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_early_terminator_wrapper.h @@ -0,0 +1,63 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_add_early_terminator_wrapper_H_ +#define SOURCE_FUZZ_TRANSFORMATION_add_early_terminator_wrapper_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddEarlyTerminatorWrapper : public Transformation { + public: + explicit TransformationAddEarlyTerminatorWrapper( + const protobufs::TransformationAddEarlyTerminatorWrapper& message); + + TransformationAddEarlyTerminatorWrapper(uint32_t function_fresh_id, + uint32_t label_fresh_id, + SpvOp opcode); + + // - |message_.function_fresh_id| and |message_.label_fresh_id| must be fresh + // and distinct. + // - OpTypeVoid must be declared in the module. + // - The module must contain a type for a zero-argument void function. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a function to the module of the form: + // + // |message_.function_fresh_id| = OpFunction %void None %zero_args_return_void + // |message_.label_fresh_id| = OpLabel + // |message_.opcode| + // OpFunctionEnd + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddEarlyTerminatorWrapper message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_add_early_terminator_wrapper_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_function.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_function.cpp new file mode 100644 index 0000000..214f741 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_function.cpp @@ -0,0 +1,960 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_function.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_message.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddFunction::TransformationAddFunction( + const spvtools::fuzz::protobufs::TransformationAddFunction& message) + : message_(message) {} + +TransformationAddFunction::TransformationAddFunction( + const std::vector& instructions) { + for (auto& instruction : instructions) { + *message_.add_instruction() = instruction; + } + message_.set_is_livesafe(false); +} + +TransformationAddFunction::TransformationAddFunction( + const std::vector& instructions, + uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id, + const std::vector& loop_limiters, + uint32_t kill_unreachable_return_value_id, + const std::vector& + access_chain_clampers) { + for (auto& instruction : instructions) { + *message_.add_instruction() = instruction; + } + message_.set_is_livesafe(true); + message_.set_loop_limiter_variable_id(loop_limiter_variable_id); + message_.set_loop_limit_constant_id(loop_limit_constant_id); + for (auto& loop_limiter : loop_limiters) { + *message_.add_loop_limiter_info() = loop_limiter; + } + message_.set_kill_unreachable_return_value_id( + kill_unreachable_return_value_id); + for (auto& access_clamper : access_chain_clampers) { + *message_.add_access_chain_clamping_info() = access_clamper; + } +} + +bool TransformationAddFunction::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // This transformation may use a lot of ids, all of which need to be fresh + // and distinct. This set tracks them. + std::set ids_used_by_this_transformation; + + // Ensure that all result ids in the new function are fresh and distinct. + for (auto& instruction : message_.instruction()) { + if (instruction.result_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + instruction.result_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + } + } + + if (message_.is_livesafe()) { + // Ensure that all ids provided for making the function livesafe are fresh + // and distinct. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.loop_limiter_variable_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + for (auto& loop_limiter_info : message_.loop_limiter_info()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.load_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.increment_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.compare_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + loop_limiter_info.logical_op_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + } + for (auto& access_chain_clamping_info : + message_.access_chain_clamping_info()) { + for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.first(), ir_context, &ids_used_by_this_transformation)) { + return false; + } + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + } + } + + // Because checking all the conditions for a function to be valid is a big + // job that the SPIR-V validator can already do, a "try it and see" approach + // is taken here. + + // We first clone the current module, so that we can try adding the new + // function without risking wrecking |ir_context|. + auto cloned_module = fuzzerutil::CloneIRContext(ir_context); + + // We try to add a function to the cloned module, which may fail if + // |message_.instruction| is not sufficiently well-formed. + if (!TryToAddFunction(cloned_module.get())) { + return false; + } + + // Check whether the cloned module is still valid after adding the function. + // If it is not, the transformation is not applicable. + if (!fuzzerutil::IsValid(cloned_module.get(), + transformation_context.GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer)) { + return false; + } + + if (message_.is_livesafe()) { + if (!TryToMakeFunctionLivesafe(cloned_module.get(), + transformation_context)) { + return false; + } + // After making the function livesafe, we check validity of the module + // again. This is because the turning of OpKill, OpUnreachable and OpReturn + // instructions into branches changes control flow graph reachability, which + // has the potential to make the module invalid when it was otherwise valid. + // It is simpler to rely on the validator to guard against this than to + // consider all scenarios when making a function livesafe. + if (!fuzzerutil::IsValid(cloned_module.get(), + transformation_context.GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer)) { + return false; + } + } + return true; +} + +void TransformationAddFunction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Add the function to the module. As the transformation is applicable, this + // should succeed. + bool success = TryToAddFunction(ir_context); + assert(success && "The function should be successfully added."); + (void)(success); // Keep release builds happy (otherwise they may complain + // that |success| is not used). + + if (message_.is_livesafe()) { + // Make the function livesafe, which also should succeed. + success = TryToMakeFunctionLivesafe(ir_context, *transformation_context); + assert(success && "It should be possible to make the function livesafe."); + (void)(success); // Keep release builds happy. + } + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + assert(message_.instruction(0).opcode() == SpvOpFunction && + "The first instruction of an 'add function' transformation must be " + "OpFunction."); + + if (message_.is_livesafe()) { + // Inform the fact manager that the function is livesafe. + transformation_context->GetFactManager()->AddFactFunctionIsLivesafe( + message_.instruction(0).result_id()); + } else { + // Inform the fact manager that all blocks in the function are dead. + for (auto& inst : message_.instruction()) { + if (inst.opcode() == SpvOpLabel) { + transformation_context->GetFactManager()->AddFactBlockIsDead( + inst.result_id()); + } + } + } + + // Record the fact that all pointer parameters and variables declared in the + // function should be regarded as having irrelevant values. This allows other + // passes to store arbitrarily to such variables, and to pass them freely as + // parameters to other functions knowing that it is OK if they get + // over-written. + for (auto& instruction : message_.instruction()) { + switch (instruction.opcode()) { + case SpvOpFunctionParameter: + if (ir_context->get_def_use_mgr() + ->GetDef(instruction.result_type_id()) + ->opcode() == SpvOpTypePointer) { + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id()); + } + break; + case SpvOpVariable: + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(instruction.result_id()); + break; + default: + break; + } + } +} + +protobufs::Transformation TransformationAddFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_function() = message_; + return result; +} + +bool TransformationAddFunction::TryToAddFunction( + opt::IRContext* ir_context) const { + // This function returns false if |message_.instruction| was not well-formed + // enough to actually create a function and add it to |ir_context|. + + // A function must have at least some instructions. + if (message_.instruction().empty()) { + return false; + } + + // A function must start with OpFunction. + auto function_begin = message_.instruction(0); + if (function_begin.opcode() != SpvOpFunction) { + return false; + } + + // Make a function, headed by the OpFunction instruction. + std::unique_ptr new_function = MakeUnique( + InstructionFromMessage(ir_context, function_begin)); + + // Keeps track of which instruction protobuf message we are currently + // considering. + uint32_t instruction_index = 1; + const auto num_instructions = + static_cast(message_.instruction().size()); + + // Iterate through all function parameter instructions, adding parameters to + // the new function. + while (instruction_index < num_instructions && + message_.instruction(instruction_index).opcode() == + SpvOpFunctionParameter) { + new_function->AddParameter(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + instruction_index++; + } + + // After the parameters, there needs to be a label. + if (instruction_index == num_instructions || + message_.instruction(instruction_index).opcode() != SpvOpLabel) { + return false; + } + + // Iterate through the instructions block by block until the end of the + // function is reached. + while (instruction_index < num_instructions && + message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) { + // Invariant: we should always be at a label instruction at this point. + assert(message_.instruction(instruction_index).opcode() == SpvOpLabel); + + // Make a basic block using the label instruction, with the new function + // as its parent. + std::unique_ptr block = + MakeUnique(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + block->SetParent(new_function.get()); + + // Consider successive instructions until we hit another label or the end + // of the function, adding each such instruction to the block. + instruction_index++; + while (instruction_index < num_instructions && + message_.instruction(instruction_index).opcode() != + SpvOpFunctionEnd && + message_.instruction(instruction_index).opcode() != SpvOpLabel) { + block->AddInstruction(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + instruction_index++; + } + // Add the block to the new function. + new_function->AddBasicBlock(std::move(block)); + } + // Having considered all the blocks, we should be at the last instruction and + // it needs to be OpFunctionEnd. + if (instruction_index != num_instructions - 1 || + message_.instruction(instruction_index).opcode() != SpvOpFunctionEnd) { + return false; + } + // Set the function's final instruction, add the function to the module and + // report success. + new_function->SetFunctionEnd(InstructionFromMessage( + ir_context, message_.instruction(instruction_index))); + ir_context->AddFunction(std::move(new_function)); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + return true; +} + +bool TransformationAddFunction::TryToMakeFunctionLivesafe( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + assert(message_.is_livesafe() && "Precondition: is_livesafe must hold."); + + // Get a pointer to the added function. + opt::Function* added_function = nullptr; + for (auto& function : *ir_context->module()) { + if (function.result_id() == message_.instruction(0).result_id()) { + added_function = &function; + break; + } + } + assert(added_function && "The added function should have been found."); + + if (!TryToAddLoopLimiters(ir_context, added_function)) { + // Adding loop limiters did not work; bail out. + return false; + } + + // Consider all the instructions in the function, and: + // - attempt to replace OpKill and OpUnreachable with return instructions + // - attempt to clamp access chains to be within bounds + // - check that OpFunctionCall instructions are only to livesafe functions + for (auto& block : *added_function) { + for (auto& inst : block) { + switch (inst.opcode()) { + case SpvOpKill: + case SpvOpUnreachable: + if (!TryToTurnKillOrUnreachableIntoReturn(ir_context, added_function, + &inst)) { + return false; + } + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + if (!TryToClampAccessChainIndices(ir_context, &inst)) { + return false; + } + break; + case SpvOpFunctionCall: + // A livesafe function my only call other livesafe functions. + if (!transformation_context.GetFactManager()->FunctionIsLivesafe( + inst.GetSingleWordInOperand(0))) { + return false; + } + default: + break; + } + } + } + return true; +} + +uint32_t TransformationAddFunction::GetBackEdgeBlockId( + opt::IRContext* ir_context, uint32_t loop_header_block_id) { + const auto* loop_header_block = + ir_context->cfg()->block(loop_header_block_id); + assert(loop_header_block && "|loop_header_block_id| is invalid"); + + for (auto pred : ir_context->cfg()->preds(loop_header_block_id)) { + if (ir_context->GetDominatorAnalysis(loop_header_block->GetParent()) + ->Dominates(loop_header_block_id, pred)) { + return pred; + } + } + + return 0; +} + +bool TransformationAddFunction::TryToAddLoopLimiters( + opt::IRContext* ir_context, opt::Function* added_function) const { + // Collect up all the loop headers so that we can subsequently add loop + // limiting logic. + std::vector loop_headers; + for (auto& block : *added_function) { + if (block.IsLoopHeader()) { + loop_headers.push_back(&block); + } + } + + if (loop_headers.empty()) { + // There are no loops, so no need to add any loop limiters. + return true; + } + + // Check that the module contains appropriate ingredients for declaring and + // manipulating a loop limiter. + + auto loop_limit_constant_id_instr = + ir_context->get_def_use_mgr()->GetDef(message_.loop_limit_constant_id()); + if (!loop_limit_constant_id_instr || + loop_limit_constant_id_instr->opcode() != SpvOpConstant) { + // The loop limit constant id instruction must exist and have an + // appropriate opcode. + return false; + } + + auto loop_limit_type = ir_context->get_def_use_mgr()->GetDef( + loop_limit_constant_id_instr->type_id()); + if (loop_limit_type->opcode() != SpvOpTypeInt || + loop_limit_type->GetSingleWordInOperand(0) != 32) { + // The type of the loop limit constant must be 32-bit integer. It + // doesn't actually matter whether the integer is signed or not. + return false; + } + + // Find the id of the "unsigned int" type. + opt::analysis::Integer unsigned_int_type(32, false); + uint32_t unsigned_int_type_id = + ir_context->get_type_mgr()->GetId(&unsigned_int_type); + if (!unsigned_int_type_id) { + // Unsigned int is not available; we need this type in order to add loop + // limiters. + return false; + } + auto registered_unsigned_int_type = + ir_context->get_type_mgr()->GetRegisteredType(&unsigned_int_type); + + // Look for 0 of type unsigned int. + opt::analysis::IntConstant zero(registered_unsigned_int_type->AsInteger(), + {0}); + auto registered_zero = ir_context->get_constant_mgr()->FindConstant(&zero); + if (!registered_zero) { + // We need 0 in order to be able to initialize loop limiters. + return false; + } + uint32_t zero_id = ir_context->get_constant_mgr() + ->GetDefiningInstruction(registered_zero) + ->result_id(); + + // Look for 1 of type unsigned int. + opt::analysis::IntConstant one(registered_unsigned_int_type->AsInteger(), + {1}); + auto registered_one = ir_context->get_constant_mgr()->FindConstant(&one); + if (!registered_one) { + // We need 1 in order to be able to increment loop limiters. + return false; + } + uint32_t one_id = ir_context->get_constant_mgr() + ->GetDefiningInstruction(registered_one) + ->result_id(); + + // Look for pointer-to-unsigned int type. + opt::analysis::Pointer pointer_to_unsigned_int_type( + registered_unsigned_int_type, SpvStorageClassFunction); + uint32_t pointer_to_unsigned_int_type_id = + ir_context->get_type_mgr()->GetId(&pointer_to_unsigned_int_type); + if (!pointer_to_unsigned_int_type_id) { + // We need pointer-to-unsigned int in order to declare the loop limiter + // variable. + return false; + } + + // Look for bool type. + opt::analysis::Bool bool_type; + uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type); + if (!bool_type_id) { + // We need bool in order to compare the loop limiter's value with the loop + // limit constant. + return false; + } + + // Declare the loop limiter variable at the start of the function's entry + // block, via an instruction of the form: + // %loop_limiter_var = SpvOpVariable %ptr_to_uint Function %zero + added_function->begin()->begin()->InsertBefore(MakeUnique( + ir_context, SpvOpVariable, pointer_to_unsigned_int_type_id, + message_.loop_limiter_variable_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}, + {SPV_OPERAND_TYPE_ID, {zero_id}}}))); + // Update the module's id bound since we have added the loop limiter + // variable id. + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.loop_limiter_variable_id()); + + // Consider each loop in turn. + for (auto loop_header : loop_headers) { + // Look for the loop's back-edge block. This is a predecessor of the loop + // header that is dominated by the loop header. + const auto back_edge_block_id = + GetBackEdgeBlockId(ir_context, loop_header->id()); + if (!back_edge_block_id) { + // The loop's back-edge block must be unreachable. This means that the + // loop cannot iterate, so there is no need to make it lifesafe; we can + // move on from this loop. + continue; + } + + // If the loop's merge block is unreachable, then there are no constraints + // on where the merge block appears in relation to the blocks of the loop. + // This means we need to be careful when adding a branch from the back-edge + // block to the merge block: the branch might make the loop merge reachable, + // and it might then be dominated by the loop header and possibly by other + // blocks in the loop. Since a block needs to appear before those blocks it + // strictly dominates, this could make the module invalid. To avoid this + // problem we bail out in the case where the loop header does not dominate + // the loop merge. + if (!ir_context->GetDominatorAnalysis(added_function) + ->Dominates(loop_header->id(), loop_header->MergeBlockId())) { + return false; + } + + // Go through the sequence of loop limiter infos and find the one + // corresponding to this loop. + bool found = false; + protobufs::LoopLimiterInfo loop_limiter_info; + for (auto& info : message_.loop_limiter_info()) { + if (info.loop_header_id() == loop_header->id()) { + loop_limiter_info = info; + found = true; + break; + } + } + if (!found) { + // We don't have loop limiter info for this loop header. + return false; + } + + // The back-edge block either has the form: + // + // (1) + // + // %l = OpLabel + // ... instructions ... + // OpBranch %loop_header + // + // (2) + // + // %l = OpLabel + // ... instructions ... + // OpBranchConditional %c %loop_header %loop_merge + // + // (3) + // + // %l = OpLabel + // ... instructions ... + // OpBranchConditional %c %loop_merge %loop_header + // + // We turn these into the following: + // + // (1) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // OpBranchConditional %t3 %loop_merge %loop_header + // + // (2) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpULessThan %bool %t1 %loop_limit + // %t4 = OpLogicalAnd %bool %c %t3 + // OpBranchConditional %t4 %loop_header %loop_merge + // + // (3) + // + // %l = OpLabel + // ... instructions ... + // %t1 = OpLoad %uint32 %loop_limiter + // %t2 = OpIAdd %uint32 %t1 %one + // OpStore %loop_limiter %t2 + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // %t4 = OpLogicalOr %bool %c %t3 + // OpBranchConditional %t4 %loop_merge %loop_header + + auto back_edge_block = ir_context->cfg()->block(back_edge_block_id); + auto back_edge_block_terminator = back_edge_block->terminator(); + bool compare_using_greater_than_equal; + if (back_edge_block_terminator->opcode() == SpvOpBranch) { + compare_using_greater_than_equal = true; + } else { + assert(back_edge_block_terminator->opcode() == SpvOpBranchConditional); + assert(((back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->id() && + back_edge_block_terminator->GetSingleWordInOperand(2) == + loop_header->MergeBlockId()) || + (back_edge_block_terminator->GetSingleWordInOperand(2) == + loop_header->id() && + back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->MergeBlockId())) && + "A back edge edge block must branch to" + " either the loop header or merge"); + compare_using_greater_than_equal = + back_edge_block_terminator->GetSingleWordInOperand(1) == + loop_header->MergeBlockId(); + } + + std::vector> new_instructions; + + // Add a load from the loop limiter variable, of the form: + // %t1 = OpLoad %uint32 %loop_limiter + new_instructions.push_back(MakeUnique( + ir_context, SpvOpLoad, unsigned_int_type_id, + loop_limiter_info.load_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}}))); + + // Increment the loaded value: + // %t2 = OpIAdd %uint32 %t1 %one + new_instructions.push_back(MakeUnique( + ir_context, SpvOpIAdd, unsigned_int_type_id, + loop_limiter_info.increment_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}}, + {SPV_OPERAND_TYPE_ID, {one_id}}}))); + + // Store the incremented value back to the loop limiter variable: + // OpStore %loop_limiter %t2 + new_instructions.push_back(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.loop_limiter_variable_id()}}, + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.increment_id()}}}))); + + // Compare the loaded value with the loop limit; either: + // %t3 = OpUGreaterThanEqual %bool %t1 %loop_limit + // or + // %t3 = OpULessThan %bool %t1 %loop_limit + new_instructions.push_back(MakeUnique( + ir_context, + compare_using_greater_than_equal ? SpvOpUGreaterThanEqual + : SpvOpULessThan, + bool_type_id, loop_limiter_info.compare_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.load_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.loop_limit_constant_id()}}}))); + + if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) { + new_instructions.push_back(MakeUnique( + ir_context, + compare_using_greater_than_equal ? SpvOpLogicalOr : SpvOpLogicalAnd, + bool_type_id, loop_limiter_info.logical_op_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, + {back_edge_block_terminator->GetSingleWordInOperand(0)}}, + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}}))); + } + + // Add the new instructions at the end of the back edge block, before the + // terminator and any loop merge instruction (as the back edge block can + // be the loop header). + if (back_edge_block->GetLoopMergeInst()) { + back_edge_block->GetLoopMergeInst()->InsertBefore( + std::move(new_instructions)); + } else { + back_edge_block_terminator->InsertBefore(std::move(new_instructions)); + } + + if (back_edge_block_terminator->opcode() == SpvOpBranchConditional) { + back_edge_block_terminator->SetInOperand( + 0, {loop_limiter_info.logical_op_id()}); + } else { + assert(back_edge_block_terminator->opcode() == SpvOpBranch && + "Back-edge terminator must be OpBranch or OpBranchConditional"); + + // Check that, if the merge block starts with OpPhi instructions, suitable + // ids have been provided to give these instructions a value corresponding + // to the new incoming edge from the back edge block. + auto merge_block = ir_context->cfg()->block(loop_header->MergeBlockId()); + if (!fuzzerutil::PhiIdsOkForNewEdge(ir_context, back_edge_block, + merge_block, + loop_limiter_info.phi_id())) { + return false; + } + + // Augment OpPhi instructions at the loop merge with the given ids. + uint32_t phi_index = 0; + for (auto& inst : *merge_block) { + if (inst.opcode() != SpvOpPhi) { + break; + } + assert(phi_index < + static_cast(loop_limiter_info.phi_id().size()) && + "There should be at least one phi id per OpPhi instruction."); + inst.AddOperand( + {SPV_OPERAND_TYPE_ID, {loop_limiter_info.phi_id(phi_index)}}); + inst.AddOperand({SPV_OPERAND_TYPE_ID, {back_edge_block_id}}); + phi_index++; + } + + // Add the new edge, by changing OpBranch to OpBranchConditional. + back_edge_block_terminator->SetOpcode(SpvOpBranchConditional); + back_edge_block_terminator->SetInOperands(opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {loop_limiter_info.compare_id()}}, + {SPV_OPERAND_TYPE_ID, {loop_header->MergeBlockId()}}, + {SPV_OPERAND_TYPE_ID, {loop_header->id()}}})); + } + + // Update the module's id bound with respect to the various ids that + // have been used for loop limiter manipulation. + fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.load_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + loop_limiter_info.increment_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, loop_limiter_info.compare_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + loop_limiter_info.logical_op_id()); + } + return true; +} + +bool TransformationAddFunction::TryToTurnKillOrUnreachableIntoReturn( + opt::IRContext* ir_context, opt::Function* added_function, + opt::Instruction* kill_or_unreachable_inst) const { + assert((kill_or_unreachable_inst->opcode() == SpvOpKill || + kill_or_unreachable_inst->opcode() == SpvOpUnreachable) && + "Precondition: instruction must be OpKill or OpUnreachable."); + + // Get the function's return type. + auto function_return_type_inst = + ir_context->get_def_use_mgr()->GetDef(added_function->type_id()); + + if (function_return_type_inst->opcode() == SpvOpTypeVoid) { + // The function has void return type, so change this instruction to + // OpReturn. + kill_or_unreachable_inst->SetOpcode(SpvOpReturn); + } else { + // The function has non-void return type, so change this instruction + // to OpReturnValue, using the value id provided with the + // transformation. + + // We first check that the id, %id, provided with the transformation + // specifically to turn OpKill and OpUnreachable instructions into + // OpReturnValue %id has the same type as the function's return type. + if (ir_context->get_def_use_mgr() + ->GetDef(message_.kill_unreachable_return_value_id()) + ->type_id() != function_return_type_inst->result_id()) { + return false; + } + kill_or_unreachable_inst->SetOpcode(SpvOpReturnValue); + kill_or_unreachable_inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {message_.kill_unreachable_return_value_id()}}}); + } + return true; +} + +bool TransformationAddFunction::TryToClampAccessChainIndices( + opt::IRContext* ir_context, opt::Instruction* access_chain_inst) const { + assert((access_chain_inst->opcode() == SpvOpAccessChain || + access_chain_inst->opcode() == SpvOpInBoundsAccessChain) && + "Precondition: instruction must be OpAccessChain or " + "OpInBoundsAccessChain."); + + // Find the AccessChainClampingInfo associated with this access chain. + const protobufs::AccessChainClampingInfo* access_chain_clamping_info = + nullptr; + for (auto& clamping_info : message_.access_chain_clamping_info()) { + if (clamping_info.access_chain_id() == access_chain_inst->result_id()) { + access_chain_clamping_info = &clamping_info; + break; + } + } + if (!access_chain_clamping_info) { + // No access chain clamping information was found; the function cannot be + // made livesafe. + return false; + } + + // Check that there is a (compare_id, select_id) pair for every + // index associated with the instruction. + if (static_cast( + access_chain_clamping_info->compare_and_select_ids().size()) != + access_chain_inst->NumInOperands() - 1) { + return false; + } + + // Walk the access chain, clamping each index to be within bounds if it is + // not a constant. + auto base_object = ir_context->get_def_use_mgr()->GetDef( + access_chain_inst->GetSingleWordInOperand(0)); + assert(base_object && "The base object must exist."); + auto pointer_type = + ir_context->get_def_use_mgr()->GetDef(base_object->type_id()); + assert(pointer_type && pointer_type->opcode() == SpvOpTypePointer && + "The base object must have pointer type."); + auto should_be_composite_type = ir_context->get_def_use_mgr()->GetDef( + pointer_type->GetSingleWordInOperand(1)); + + // Consider each index input operand in turn (operand 0 is the base object). + for (uint32_t index = 1; index < access_chain_inst->NumInOperands(); + index++) { + // We are going to turn: + // + // %result = OpAccessChain %type %object ... %index ... + // + // into: + // + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + // %result = OpAccessChain %type %object ... %t2 ... + // + // ... unless %index is already a constant. + + // Get the bound for the composite being indexed into; e.g. the number of + // columns of matrix or the size of an array. + uint32_t bound = fuzzerutil::GetBoundForCompositeIndex( + *should_be_composite_type, ir_context); + + // Get the instruction associated with the index and figure out its integer + // type. + const uint32_t index_id = access_chain_inst->GetSingleWordInOperand(index); + auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id); + auto index_type_inst = + ir_context->get_def_use_mgr()->GetDef(index_inst->type_id()); + assert(index_type_inst->opcode() == SpvOpTypeInt); + assert(index_type_inst->GetSingleWordInOperand(0) == 32); + opt::analysis::Integer* index_int_type = + ir_context->get_type_mgr() + ->GetType(index_type_inst->result_id()) + ->AsInteger(); + + if (index_inst->opcode() != SpvOpConstant || + index_inst->GetSingleWordInOperand(0) >= bound) { + // The index is either non-constant or an out-of-bounds constant, so we + // need to clamp it. + assert(should_be_composite_type->opcode() != SpvOpTypeStruct && + "Access chain indices into structures are required to be " + "constants."); + opt::analysis::IntConstant bound_minus_one(index_int_type, {bound - 1}); + if (!ir_context->get_constant_mgr()->FindConstant(&bound_minus_one)) { + // We do not have an integer constant whose value is |bound| -1. + return false; + } + + opt::analysis::Bool bool_type; + uint32_t bool_type_id = ir_context->get_type_mgr()->GetId(&bool_type); + if (!bool_type_id) { + // Bool type is not declared; we cannot do a comparison. + return false; + } + + uint32_t bound_minus_one_id = + ir_context->get_constant_mgr() + ->GetDefiningInstruction(&bound_minus_one) + ->result_id(); + + uint32_t compare_id = + access_chain_clamping_info->compare_and_select_ids(index - 1).first(); + uint32_t select_id = + access_chain_clamping_info->compare_and_select_ids(index - 1) + .second(); + std::vector> new_instructions; + + // Compare the index with the bound via an instruction of the form: + // %t1 = OpULessThanEqual %bool %index %bound_minus_one + new_instructions.push_back(MakeUnique( + ir_context, SpvOpULessThanEqual, bool_type_id, compare_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {index_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + + // Select the index if in-bounds, otherwise one less than the bound: + // %t2 = OpSelect %int_type %t1 %index %bound_minus_one + new_instructions.push_back(MakeUnique( + ir_context, SpvOpSelect, index_type_inst->result_id(), select_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {compare_id}}, + {SPV_OPERAND_TYPE_ID, {index_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {bound_minus_one_id}}}))); + + // Add the new instructions before the access chain + access_chain_inst->InsertBefore(std::move(new_instructions)); + + // Replace %index with %t2. + access_chain_inst->SetInOperand(index, {select_id}); + fuzzerutil::UpdateModuleIdBound(ir_context, compare_id); + fuzzerutil::UpdateModuleIdBound(ir_context, select_id); + } + should_be_composite_type = + FollowCompositeIndex(ir_context, *should_be_composite_type, index_id); + } + return true; +} + +opt::Instruction* TransformationAddFunction::FollowCompositeIndex( + opt::IRContext* ir_context, const opt::Instruction& composite_type_inst, + uint32_t index_id) { + uint32_t sub_object_type_id; + switch (composite_type_inst.opcode()) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0); + break; + case SpvOpTypeMatrix: + case SpvOpTypeVector: + sub_object_type_id = composite_type_inst.GetSingleWordInOperand(0); + break; + case SpvOpTypeStruct: { + auto index_inst = ir_context->get_def_use_mgr()->GetDef(index_id); + assert(index_inst->opcode() == SpvOpConstant); + assert(ir_context->get_def_use_mgr() + ->GetDef(index_inst->type_id()) + ->opcode() == SpvOpTypeInt); + assert(ir_context->get_def_use_mgr() + ->GetDef(index_inst->type_id()) + ->GetSingleWordInOperand(0) == 32); + uint32_t index_value = index_inst->GetSingleWordInOperand(0); + sub_object_type_id = + composite_type_inst.GetSingleWordInOperand(index_value); + break; + } + default: + assert(false && "Unknown composite type."); + sub_object_type_id = 0; + break; + } + assert(sub_object_type_id && "No sub-object found."); + return ir_context->get_def_use_mgr()->GetDef(sub_object_type_id); +} + +std::unordered_set TransformationAddFunction::GetFreshIds() const { + std::unordered_set result; + for (auto& instruction : message_.instruction()) { + result.insert(instruction.result_id()); + } + if (message_.is_livesafe()) { + result.insert(message_.loop_limiter_variable_id()); + for (auto& loop_limiter_info : message_.loop_limiter_info()) { + result.insert(loop_limiter_info.load_id()); + result.insert(loop_limiter_info.increment_id()); + result.insert(loop_limiter_info.compare_id()); + result.insert(loop_limiter_info.logical_op_id()); + } + for (auto& access_chain_clamping_info : + message_.access_chain_clamping_info()) { + for (auto& pair : access_chain_clamping_info.compare_and_select_ids()) { + result.insert(pair.first()); + result.insert(pair.second()); + } + } + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_function.h b/third_party/spirv-tools/source/fuzz/transformation_add_function.h new file mode 100644 index 0000000..e5381d1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_function.h @@ -0,0 +1,128 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddFunction : public Transformation { + public: + explicit TransformationAddFunction( + const protobufs::TransformationAddFunction& message); + + // Creates a transformation to add a non live-safe function. + explicit TransformationAddFunction( + const std::vector& instructions); + + // Creates a transformation to add a live-safe function. + TransformationAddFunction( + const std::vector& instructions, + uint32_t loop_limiter_variable_id, uint32_t loop_limit_constant_id, + const std::vector& loop_limiters, + uint32_t kill_unreachable_return_value_id, + const std::vector& + access_chain_clampers); + + // - |message_.instruction| must correspond to a sufficiently well-formed + // sequence of instructions that a function can be created from them + // - If |message_.is_livesafe| holds then |message_| must contain suitable + // ingredients to make the function livesafe, and the function must only + // invoke other livesafe functions + // - Adding the created function to the module must lead to a valid module. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds the function defined by |message_.instruction| to the module, making + // it livesafe if |message_.is_livesafe| holds. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Helper method that, given composite type |composite_type_inst|, returns the + // type of the sub-object at index |index_id|, which is required to be in- + // bounds. + static opt::Instruction* FollowCompositeIndex( + opt::IRContext* ir_context, const opt::Instruction& composite_type_inst, + uint32_t index_id); + + // Returns id of the back-edge block, given the corresponding + // |loop_header_block_id|. |loop_header_block_id| must be the id of a loop + // header block. Returns 0 if the loop has no back-edge block. + static uint32_t GetBackEdgeBlockId(opt::IRContext* ir_context, + uint32_t loop_header_block_id); + + // Attempts to create a function from the series of instructions in + // |message_.instruction| and add it to |ir_context|. + // + // Returns false if adding the function is not possible due to the messages + // not respecting the basic structure of a function, e.g. if there is no + // OpFunction instruction or no blocks; in this case |ir_context| is left in + // an indeterminate state. + // + // Otherwise returns true. Whether |ir_context| is valid after addition of + // the function depends on the contents of |message_.instruction|. + // + // Intended usage: + // - Perform a dry run of this method on a clone of a module, and use + // the validator to check whether the resulting module is valid. Working + // on a clone means it does not matter if the function fails to be cleanly + // added, or leads to an invalid module. + // - If the dry run succeeds, run the method on the real module of interest, + // to add the function. + bool TryToAddFunction(opt::IRContext* ir_context) const; + + private: + // Should only be called if |message_.is_livesafe| holds. Attempts to make + // the function livesafe (see FactFunctionIsLivesafe for a definition). + // Returns false if this is not possible, due to |message_| or |ir_context| + // not containing sufficient ingredients (such as types and fresh ids) to add + // the instrumentation necessary to make the function livesafe. + bool TryToMakeFunctionLivesafe( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; + + // A helper for TryToMakeFunctionLivesafe that tries to add loop-limiting + // logic. + bool TryToAddLoopLimiters(opt::IRContext* ir_context, + opt::Function* added_function) const; + + // A helper for TryToMakeFunctionLivesafe that tries to replace OpKill and + // OpUnreachable instructions into return instructions. + bool TryToTurnKillOrUnreachableIntoReturn( + opt::IRContext* ir_context, opt::Function* added_function, + opt::Instruction* kill_or_unreachable_inst) const; + + // A helper for TryToMakeFunctionLivesafe that tries to clamp access chain + // indices so that they are guaranteed to be in-bounds. + bool TryToClampAccessChainIndices(opt::IRContext* ir_context, + opt::Instruction* access_chain_inst) const; + + protobufs::TransformationAddFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_FUNCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.cpp new file mode 100644 index 0000000..7a90b82 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_undef.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddGlobalUndef::TransformationAddGlobalUndef( + const spvtools::fuzz::protobufs::TransformationAddGlobalUndef& message) + : message_(message) {} + +TransformationAddGlobalUndef::TransformationAddGlobalUndef(uint32_t fresh_id, + uint32_t type_id) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); +} + +bool TransformationAddGlobalUndef::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); + // The type must exist, and must not be a function type. + return type && !type->AsFunction(); +} + +void TransformationAddGlobalUndef::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ir_context->module()->AddGlobalValue(MakeUnique( + ir_context, SpvOpUndef, message_.type_id(), message_.fresh_id(), + opt::Instruction::OperandList())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddGlobalUndef::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_global_undef() = message_; + return result; +} + +std::unordered_set TransformationAddGlobalUndef::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.h b/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.h new file mode 100644 index 0000000..717dc9a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_global_undef.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddGlobalUndef : public Transformation { + public: + explicit TransformationAddGlobalUndef( + const protobufs::TransformationAddGlobalUndef& message); + + TransformationAddGlobalUndef(uint32_t fresh_id, uint32_t type_id); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a non-function type + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpUndef instruction to the module, with |message_.type_id| as its + // type. The instruction has result id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddGlobalUndef message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_UNDEF_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_global_variable.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_global_variable.cpp new file mode 100644 index 0000000..dd04e48 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_global_variable.cpp @@ -0,0 +1,124 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_variable.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddGlobalVariable::TransformationAddGlobalVariable( + const spvtools::fuzz::protobufs::TransformationAddGlobalVariable& message) + : message_(message) {} + +TransformationAddGlobalVariable::TransformationAddGlobalVariable( + uint32_t fresh_id, uint32_t type_id, SpvStorageClass storage_class, + uint32_t initializer_id, bool value_is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_storage_class(storage_class); + message_.set_initializer_id(initializer_id); + message_.set_value_is_irrelevant(value_is_irrelevant); +} + +bool TransformationAddGlobalVariable::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // The storage class must be Private or Workgroup. + auto storage_class = static_cast(message_.storage_class()); + switch (storage_class) { + case SpvStorageClassPrivate: + case SpvStorageClassWorkgroup: + break; + default: + assert(false && "Unsupported storage class."); + return false; + } + // The type id must correspond to a type. + auto type = ir_context->get_type_mgr()->GetType(message_.type_id()); + if (!type) { + return false; + } + // That type must be a pointer type ... + auto pointer_type = type->AsPointer(); + if (!pointer_type) { + return false; + } + // ... with the right storage class. + if (pointer_type->storage_class() != storage_class) { + return false; + } + if (message_.initializer_id()) { + // An initializer is not allowed if the storage class is Workgroup. + if (storage_class == SpvStorageClassWorkgroup) { + assert(false && + "By construction this transformation should not have an " + "initializer when Workgroup storage class is used."); + return false; + } + // The initializer id must be the id of a constant. Check this with the + // constant manager. + auto constant_id = ir_context->get_constant_mgr()->GetConstantsFromIds( + {message_.initializer_id()}); + if (constant_id.empty()) { + return false; + } + assert(constant_id.size() == 1 && + "We asked for the constant associated with a single id; we should " + "get a single constant."); + // The type of the constant must match the pointee type of the pointer. + if (pointer_type->pointee_type() != constant_id[0]->type()) { + return false; + } + } + return true; +} + +void TransformationAddGlobalVariable::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::AddGlobalVariable( + ir_context, message_.fresh_id(), message_.type_id(), + static_cast(message_.storage_class()), + message_.initializer_id()); + + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + if (message_.value_is_irrelevant()) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddGlobalVariable::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_global_variable() = message_; + return result; +} + +std::unordered_set TransformationAddGlobalVariable::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_global_variable.h b/third_party/spirv-tools/source/fuzz/transformation_add_global_variable.h new file mode 100644 index 0000000..8d46edb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_global_variable.h @@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddGlobalVariable : public Transformation { + public: + explicit TransformationAddGlobalVariable( + const protobufs::TransformationAddGlobalVariable& message); + + TransformationAddGlobalVariable(uint32_t fresh_id, uint32_t type_id, + SpvStorageClass storage_class, + uint32_t initializer_id, + bool value_is_irrelevant); + + // - |message_.fresh_id| must be fresh + // - |message_.type_id| must be the id of a pointer type with the same storage + // class as |message_.storage_class| + // - |message_.storage_class| must be Private or Workgroup + // - |message_.initializer_id| must be 0 if |message_.storage_class| is + // Workgroup, and otherwise may either be 0 or the id of a constant whose + // type is the pointee type of |message_.type_id| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a global variable with storage class |message_.storage_class| to the + // module, with type |message_.type_id| and either no initializer or + // |message_.initializer_id| as an initializer, depending on whether + // |message_.initializer_id| is 0. The global variable has result id + // |message_.fresh_id|. + // + // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the + // fact manager in |transformation_context|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddGlobalVariable message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_GLOBAL_VARIABLE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp new file mode 100644 index 0000000..ab48f0b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_image_sample_unused_components.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddImageSampleUnusedComponents:: + TransformationAddImageSampleUnusedComponents( + const spvtools::fuzz::protobufs:: + TransformationAddImageSampleUnusedComponents& message) + : message_(message) {} + +TransformationAddImageSampleUnusedComponents:: + TransformationAddImageSampleUnusedComponents( + uint32_t coordinate_with_unused_components_id, + const protobufs::InstructionDescriptor& instruction_descriptor) { + message_.set_coordinate_with_unused_components_id( + coordinate_with_unused_components_id); + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationAddImageSampleUnusedComponents::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto image_sample_instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + + // The image sample instruction must be defined. + if (image_sample_instruction == nullptr) { + return false; + } + + // The instruction must be an image sample instruction. + if (!spvOpcodeIsImageSample(image_sample_instruction->opcode())) { + return false; + } + + uint32_t coordinate_id = image_sample_instruction->GetSingleWordInOperand(1); + auto coordinate_instruction = + ir_context->get_def_use_mgr()->GetDef(coordinate_id); + auto coordinate_type = + ir_context->get_type_mgr()->GetType(coordinate_instruction->type_id()); + + // It must be possible to add unused components. + if (coordinate_type->AsVector() && + coordinate_type->AsVector()->element_count() == 4) { + return false; + } + + auto coordinate_with_unused_components_instruction = + ir_context->get_def_use_mgr()->GetDef( + message_.coordinate_with_unused_components_id()); + + // The coordinate with unused components instruction must be defined. + if (coordinate_with_unused_components_instruction == nullptr) { + return false; + } + + // It must be an OpCompositeConstruct instruction such that it can be checked + // that the original components are present. + if (coordinate_with_unused_components_instruction->opcode() != + SpvOpCompositeConstruct) { + return false; + } + + // The first constituent must be the original coordinate. + if (coordinate_with_unused_components_instruction->GetSingleWordInOperand( + 0) != coordinate_id) { + return false; + } + + auto coordinate_with_unused_components_type = + ir_context->get_type_mgr()->GetType( + coordinate_with_unused_components_instruction->type_id()); + + // |coordinate_with_unused_components_type| must be a vector. + if (!coordinate_with_unused_components_type->AsVector()) { + return false; + } + + return true; +} + +void TransformationAddImageSampleUnusedComponents::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Sets the coordinate operand. + auto image_sample_instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + image_sample_instruction->SetInOperand( + 1, {message_.coordinate_with_unused_components_id()}); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationAddImageSampleUnusedComponents::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_image_sample_unused_components() = message_; + return result; +} + +std::unordered_set +TransformationAddImageSampleUnusedComponents::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h b/third_party/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h new file mode 100644 index 0000000..7486c76 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_image_sample_unused_components.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddImageSampleUnusedComponents : public Transformation { + public: + explicit TransformationAddImageSampleUnusedComponents( + const protobufs::TransformationAddImageSampleUnusedComponents& message); + + TransformationAddImageSampleUnusedComponents( + uint32_t coordinate_with_unused_components_id, + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |coordinate_with_unused_components_id| must identify a vector such that + // the first components match the components of the image sample coordinate. + // - |message_.instruction_descriptor| must identify an image sample + // instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Add unused components to an image sample coordinate by replacing the + // coordinate with |coordinate_with_unused_components_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddImageSampleUnusedComponents message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_IMAGE_SAMPLE_UNUSED_COMPONENTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_local_variable.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_local_variable.cpp new file mode 100644 index 0000000..0a7a3da --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_local_variable.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_local_variable.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddLocalVariable::TransformationAddLocalVariable( + const spvtools::fuzz::protobufs::TransformationAddLocalVariable& message) + : message_(message) {} + +TransformationAddLocalVariable::TransformationAddLocalVariable( + uint32_t fresh_id, uint32_t type_id, uint32_t function_id, + uint32_t initializer_id, bool value_is_irrelevant) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_function_id(function_id); + message_.set_initializer_id(initializer_id); + message_.set_value_is_irrelevant(value_is_irrelevant); +} + +bool TransformationAddLocalVariable::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The provided id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The pointer type id must indeed correspond to a pointer, and it must have + // function storage class. + auto type_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.type_id()); + if (!type_instruction || type_instruction->opcode() != SpvOpTypePointer || + type_instruction->GetSingleWordInOperand(0) != SpvStorageClassFunction) { + return false; + } + // The initializer must... + auto initializer_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); + // ... exist, ... + if (!initializer_instruction) { + return false; + } + // ... be a constant, ... + if (!spvOpcodeIsConstant(initializer_instruction->opcode())) { + return false; + } + // ... and have the same type as the pointee type. + if (initializer_instruction->type_id() != + type_instruction->GetSingleWordInOperand(1)) { + return false; + } + // The function to which the local variable is to be added must exist. + return fuzzerutil::FindFunction(ir_context, message_.function_id()); +} + +void TransformationAddLocalVariable::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::AddLocalVariable(ir_context, message_.fresh_id(), + message_.type_id(), message_.function_id(), + message_.initializer_id()); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + if (message_.value_is_irrelevant()) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationAddLocalVariable::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_local_variable() = message_; + return result; +} + +std::unordered_set TransformationAddLocalVariable::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_local_variable.h b/third_party/spirv-tools/source/fuzz/transformation_add_local_variable.h new file mode 100644 index 0000000..963079f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_local_variable.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddLocalVariable : public Transformation { + public: + explicit TransformationAddLocalVariable( + const protobufs::TransformationAddLocalVariable& message); + + TransformationAddLocalVariable(uint32_t fresh_id, uint32_t type_id, + uint32_t function_id, uint32_t initializer_id, + bool value_is_irrelevant); + + // - |message_.fresh_id| must not be used by the module + // - |message_.type_id| must be the id of a pointer type with Function + // storage class + // - |message_.initializer_id| must be the id of a constant with the same + // type as the pointer's pointee type + // - |message_.function_id| must be the id of a function + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction to the start of |message_.function_id|, of the form: + // |message_.fresh_id| = OpVariable |message_.type_id| Function + // |message_.initializer_id| + // If |message_.value_is_irrelevant| holds, adds a corresponding fact to the + // fact manager in |transformation_context|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddLocalVariable message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOCAL_VARIABLE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp new file mode 100644 index 0000000..3d50fa9 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_loop_preheader.cpp @@ -0,0 +1,233 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_add_loop_preheader.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { +TransformationAddLoopPreheader::TransformationAddLoopPreheader( + const protobufs::TransformationAddLoopPreheader& message) + : message_(message) {} + +TransformationAddLoopPreheader::TransformationAddLoopPreheader( + uint32_t loop_header_block, uint32_t fresh_id, + std::vector phi_id) { + message_.set_loop_header_block(loop_header_block); + message_.set_fresh_id(fresh_id); + for (auto id : phi_id) { + message_.add_phi_id(id); + } +} + +bool TransformationAddLoopPreheader::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& /* unused */) const { + // |message_.loop_header_block()| must be the id of a loop header block. + opt::BasicBlock* loop_header_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block()); + if (!loop_header_block || !loop_header_block->IsLoopHeader()) { + return false; + } + + // The id for the preheader must actually be fresh. + std::set used_ids; + if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.fresh_id(), + ir_context, &used_ids)) { + return false; + } + + size_t num_predecessors = + ir_context->cfg()->preds(message_.loop_header_block()).size(); + + // The block must have at least 2 predecessors (the back-edge block and + // another predecessor outside of the loop) + if (num_predecessors < 2) { + return false; + } + + // If the block only has one predecessor outside of the loop (and thus 2 in + // total), then no additional fresh ids are necessary. + if (num_predecessors == 2) { + return true; + } + + // Count the number of OpPhi instructions. + int32_t num_phi_insts = 0; + loop_header_block->ForEachPhiInst( + [&num_phi_insts](opt::Instruction* /* unused */) { num_phi_insts++; }); + + // There must be enough fresh ids for the OpPhi instructions. + if (num_phi_insts > message_.phi_id_size()) { + return false; + } + + // Check that the needed ids are fresh and distinct. + for (int32_t i = 0; i < num_phi_insts; i++) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation(message_.phi_id(i), + ir_context, &used_ids)) { + return false; + } + } + + return true; +} + +void TransformationAddLoopPreheader::Apply( + opt::IRContext* ir_context, + TransformationContext* /* transformation_context */) const { + // Find the loop header. + opt::BasicBlock* loop_header = + fuzzerutil::MaybeFindBlock(ir_context, message_.loop_header_block()); + + auto dominator_analysis = + ir_context->GetDominatorAnalysis(loop_header->GetParent()); + + uint32_t back_edge_block_id = 0; + + // Update the branching instructions of the out-of-loop predecessors of the + // header. Set |back_edge_block_id| to be the id of the back-edge block. + ir_context->get_def_use_mgr()->ForEachUse( + loop_header->id(), + [this, &ir_context, &dominator_analysis, &loop_header, + &back_edge_block_id](opt::Instruction* use_inst, uint32_t use_index) { + if (dominator_analysis->Dominates(loop_header->GetLabelInst(), + use_inst)) { + // If |use_inst| is a branch instruction dominated by the header, the + // block containing it is the back-edge block. + if (use_inst->IsBranch()) { + assert(back_edge_block_id == 0 && + "There should only be one back-edge block"); + back_edge_block_id = ir_context->get_instr_block(use_inst)->id(); + } + // References to the header inside the loop should not be updated + return; + } + + // If |use_inst| is not a branch or merge instruction, it should not be + // changed. + if (!use_inst->IsBranch() && + use_inst->opcode() != SpvOpSelectionMerge && + use_inst->opcode() != SpvOpLoopMerge) { + return; + } + + // Update the reference. + use_inst->SetOperand(use_index, {message_.fresh_id()}); + }); + + assert(back_edge_block_id && "The back-edge block should have been found"); + + // Make a new block for the preheader. + std::unique_ptr preheader = MakeUnique( + std::unique_ptr(new opt::Instruction( + ir_context, SpvOpLabel, 0, message_.fresh_id(), {}))); + + uint32_t phi_ids_used = 0; + + // Update the OpPhi instructions and, if there is more than one out-of-loop + // predecessor, add necessary OpPhi instructions so the preheader. + loop_header->ForEachPhiInst([this, &ir_context, &preheader, + &back_edge_block_id, + &phi_ids_used](opt::Instruction* phi_inst) { + // The loop header must have at least 2 incoming edges (the back edge, and + // at least one from outside the loop). + assert(phi_inst->NumInOperands() >= 4); + + if (phi_inst->NumInOperands() == 4) { + // There is just one out-of-loop predecessor, so no additional + // instructions in the preheader are necessary. The reference to the + // original out-of-loop predecessor needs to be updated so that it refers + // to the preheader. + uint32_t index_of_out_of_loop_pred_id = + phi_inst->GetInOperand(1).words[0] == back_edge_block_id ? 3 : 1; + phi_inst->SetInOperand(index_of_out_of_loop_pred_id, {preheader->id()}); + } else { + // There is more than one out-of-loop predecessor, so an OpPhi instruction + // needs to be added to the preheader, and its value will depend on all + // the current out-of-loop predecessors of the header. + + // Get the operand list and the value corresponding to the back-edge + // block. + std::vector preheader_in_operands; + uint32_t back_edge_val = 0; + + for (uint32_t i = 0; i < phi_inst->NumInOperands(); i += 2) { + // Only add operands if they don't refer to the back-edge block. + if (phi_inst->GetInOperand(i + 1).words[0] == back_edge_block_id) { + back_edge_val = phi_inst->GetInOperand(i).words[0]; + } else { + preheader_in_operands.push_back(std::move(phi_inst->GetInOperand(i))); + preheader_in_operands.push_back( + std::move(phi_inst->GetInOperand(i + 1))); + } + } + + // Add the new instruction to the preheader. + uint32_t fresh_phi_id = message_.phi_id(phi_ids_used++); + + // Update id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_phi_id); + + preheader->AddInstruction(std::unique_ptr( + new opt::Instruction(ir_context, SpvOpPhi, phi_inst->type_id(), + fresh_phi_id, preheader_in_operands))); + + // Update the OpPhi instruction in the header so that it refers to the + // back edge block and the preheader as the predecessors, and it uses the + // newly-defined OpPhi in the preheader for the corresponding value. + phi_inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {fresh_phi_id}}, + {SPV_OPERAND_TYPE_ID, {preheader->id()}}, + {SPV_OPERAND_TYPE_ID, {back_edge_val}}, + {SPV_OPERAND_TYPE_ID, {back_edge_block_id}}}); + } + }); + + // Update id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Add an unconditional branch from the preheader to the header. + preheader->AddInstruction( + std::unique_ptr(new opt::Instruction( + ir_context, SpvOpBranch, 0, 0, + std::initializer_list{opt::Operand( + spv_operand_type_t::SPV_OPERAND_TYPE_ID, {loop_header->id()})}))); + + // Insert the preheader in the module. + loop_header->GetParent()->InsertBasicBlockBefore(std::move(preheader), + loop_header); + + // Invalidate analyses because the structure of the program changed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationAddLoopPreheader::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_loop_preheader() = message_; + return result; +} + +std::unordered_set TransformationAddLoopPreheader::GetFreshIds() + const { + std::unordered_set result = {message_.fresh_id()}; + for (auto id : message_.phi_id()) { + result.insert(id); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_loop_preheader.h b/third_party/spirv-tools/source/fuzz/transformation_add_loop_preheader.h new file mode 100644 index 0000000..05448f3 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_loop_preheader.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H +#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddLoopPreheader : public Transformation { + public: + explicit TransformationAddLoopPreheader( + const protobufs::TransformationAddLoopPreheader& message); + + TransformationAddLoopPreheader(uint32_t loop_header_block, uint32_t fresh_id, + std::vector phi_id); + + // - |message_.loop_header_block| must be the id of a loop header block in + // the given module. + // - |message_.fresh_id| must be an available id. + // - |message_.phi_ids| must be a list of available ids. + // It can be empty if the loop header only has one predecessor outside of + // the loop. Otherwise, it must contain at least as many ids as OpPhi + // instructions in the loop header block. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a preheader block as the unique out-of-loop predecessor of the given + // loop header block. All of the existing out-of-loop predecessors of the + // header are changed so that they branch to the preheader instead. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddLoopPreheader message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_PREHEADER_H diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp new file mode 100644 index 0000000..45d3fc8 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.cpp @@ -0,0 +1,453 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h" +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +namespace { +uint32_t kMaxNumOfIterations = 32; +} + +TransformationAddLoopToCreateIntConstantSynonym:: + TransformationAddLoopToCreateIntConstantSynonym( + const protobufs::TransformationAddLoopToCreateIntConstantSynonym& + message) + : message_(message) {} + +TransformationAddLoopToCreateIntConstantSynonym:: + TransformationAddLoopToCreateIntConstantSynonym( + uint32_t constant_id, uint32_t initial_val_id, uint32_t step_val_id, + uint32_t num_iterations_id, uint32_t block_after_loop_id, + uint32_t syn_id, uint32_t loop_id, uint32_t ctr_id, uint32_t temp_id, + uint32_t eventual_syn_id, uint32_t incremented_ctr_id, uint32_t cond_id, + uint32_t additional_block_id) { + message_.set_constant_id(constant_id); + message_.set_initial_val_id(initial_val_id); + message_.set_step_val_id(step_val_id); + message_.set_num_iterations_id(num_iterations_id); + message_.set_block_after_loop_id(block_after_loop_id); + message_.set_syn_id(syn_id); + message_.set_loop_id(loop_id); + message_.set_ctr_id(ctr_id); + message_.set_temp_id(temp_id); + message_.set_eventual_syn_id(eventual_syn_id); + message_.set_incremented_ctr_id(incremented_ctr_id); + message_.set_cond_id(cond_id); + message_.set_additional_block_id(additional_block_id); +} + +bool TransformationAddLoopToCreateIntConstantSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that |message_.constant_id|, |message_.initial_val_id| and + // |message_.step_val_id| are existing constants, and that their values are + // not irrelevant. + auto constant = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.constant_id()); + auto initial_val = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.initial_val_id()); + auto step_val = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.step_val_id()); + + if (!constant || !initial_val || !step_val) { + return false; + } + if (transformation_context.GetFactManager()->IdIsIrrelevant( + message_.constant_id()) || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.initial_val_id()) || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.step_val_id())) { + return false; + } + + // Check that the type of |constant| is integer scalar or vector with integer + // components. + if (!constant->AsIntConstant() && + (!constant->AsVectorConstant() || + !constant->type()->AsVector()->element_type()->AsInteger())) { + return false; + } + + // Check that the component bit width of |constant| is <= 64. + // Consider the width of the constant if it is an integer, of a single + // component if it is a vector. + uint32_t bit_width = + constant->AsIntConstant() + ? constant->type()->AsInteger()->width() + : constant->type()->AsVector()->element_type()->AsInteger()->width(); + if (bit_width > 64) { + return false; + } + + auto constant_def = + ir_context->get_def_use_mgr()->GetDef(message_.constant_id()); + auto initial_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.initial_val_id()); + auto step_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.step_val_id()); + + // Check that |constant|, |initial_val| and |step_val| have the same type, + // with possibly different signedness. + if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, constant_def->type_id(), + initial_val_def->type_id()) || + !fuzzerutil::TypesAreEqualUpToSign(ir_context, constant_def->type_id(), + step_val_def->type_id())) { + return false; + } + + // |message_.num_iterations_id| must be a non-irrelevant integer constant with + // bit width 32. + auto num_iterations = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.num_iterations_id()); + + if (!num_iterations || !num_iterations->AsIntConstant() || + num_iterations->type()->AsInteger()->width() != 32 || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.num_iterations_id())) { + return false; + } + + // Check that the number of iterations is > 0 and <= 32. + uint32_t num_iterations_value = + num_iterations->AsIntConstant()->GetU32BitValue(); + + if (num_iterations_value == 0 || num_iterations_value > kMaxNumOfIterations) { + return false; + } + + // Check that the module contains 32-bit signed integer scalar constants of + // value 0 and 1. + if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context, + {0}, 32, true, false)) { + return false; + } + + if (!fuzzerutil::MaybeGetIntegerConstant(ir_context, transformation_context, + {1}, 32, true, false)) { + return false; + } + + // Check that the module contains the Bool type. + if (!fuzzerutil::MaybeGetBoolType(ir_context)) { + return false; + } + + // Check that the equation C = I - S * N is satisfied. + + // Collect the components in vectors (if the constants are scalars, these + // vectors will contain the constants themselves). + std::vector c_components; + std::vector i_components; + std::vector s_components; + if (constant->AsIntConstant()) { + c_components.emplace_back(constant); + i_components.emplace_back(initial_val); + s_components.emplace_back(step_val); + } else { + // It is a vector: get all the components. + c_components = constant->AsVectorConstant()->GetComponents(); + i_components = initial_val->AsVectorConstant()->GetComponents(); + s_components = step_val->AsVectorConstant()->GetComponents(); + } + + // Check the value of the components satisfy the equation. + for (uint32_t i = 0; i < c_components.size(); i++) { + // Use 64-bits integers to be able to handle constants of any width <= 64. + uint64_t c_value = c_components[i]->AsIntConstant()->GetZeroExtendedValue(); + uint64_t i_value = i_components[i]->AsIntConstant()->GetZeroExtendedValue(); + uint64_t s_value = s_components[i]->AsIntConstant()->GetZeroExtendedValue(); + + uint64_t result = i_value - s_value * num_iterations_value; + + // Use bit shifts to ignore the first bits in excess (if there are any). By + // shifting left, we discard the first |64 - bit_width| bits. By shifting + // right, we move the bits back to their correct position. + result = (result << (64 - bit_width)) >> (64 - bit_width); + + if (c_value != result) { + return false; + } + } + + // Check that |message_.block_after_loop_id| is the label of a block. + auto block = + fuzzerutil::MaybeFindBlock(ir_context, message_.block_after_loop_id()); + + // Check that the block exists and has a single predecessor. + if (!block || ir_context->cfg()->preds(block->id()).size() != 1) { + return false; + } + + // Check that the block is not dead. If it is then the new loop would be + // dead and the data it computes would be irrelevant, so we would not be able + // to make a synonym. + if (transformation_context.GetFactManager()->BlockIsDead(block->id())) { + return false; + } + + // Check that the block is not a merge block. + if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) { + return false; + } + + // Check that the block is not a continue block. + if (ir_context->GetStructuredCFGAnalysis()->IsContinueBlock(block->id())) { + return false; + } + + // Check that the block is not a loop header. + if (block->IsLoopHeader()) { + return false; + } + + // Check all the fresh ids. + std::set fresh_ids_used; + for (uint32_t id : {message_.syn_id(), message_.loop_id(), message_.ctr_id(), + message_.temp_id(), message_.eventual_syn_id(), + message_.incremented_ctr_id(), message_.cond_id()}) { + if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context, + &fresh_ids_used)) { + return false; + } + } + + // Check the additional block id if it is non-zero. + return !message_.additional_block_id() || + CheckIdIsFreshAndNotUsedByThisTransformation( + message_.additional_block_id(), ir_context, &fresh_ids_used); +} + +void TransformationAddLoopToCreateIntConstantSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Find 32-bit signed integer constants 0 and 1. + uint32_t const_0_id = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {0}, 32, true, false); + auto const_0_def = ir_context->get_def_use_mgr()->GetDef(const_0_id); + uint32_t const_1_id = fuzzerutil::MaybeGetIntegerConstant( + ir_context, *transformation_context, {1}, 32, true, false); + + // Retrieve the instruction defining the initial value constant. + auto initial_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.initial_val_id()); + + // Retrieve the block before which we want to insert the loop. + auto block_after_loop = + ir_context->get_instr_block(message_.block_after_loop_id()); + + // Find the predecessor of the block. + uint32_t pred_id = + ir_context->cfg()->preds(message_.block_after_loop_id())[0]; + + // Get the id for the last block in the new loop. It will be + // |message_.additional_block_id| if this is non_zero, |message_.loop_id| + // otherwise. + uint32_t last_loop_block_id = message_.additional_block_id() + ? message_.additional_block_id() + : message_.loop_id(); + + // Create the loop header block. + std::unique_ptr loop_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.loop_id(), + opt::Instruction::OperandList{})); + + // Add OpPhi instructions to retrieve the current value of the counter and of + // the temporary variable that will be decreased at each operation. + loop_block->AddInstruction(MakeUnique( + ir_context, SpvOpPhi, const_0_def->type_id(), message_.ctr_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {const_0_id}}, + {SPV_OPERAND_TYPE_ID, {pred_id}}, + {SPV_OPERAND_TYPE_ID, {message_.incremented_ctr_id()}}, + {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}})); + + loop_block->AddInstruction(MakeUnique( + ir_context, SpvOpPhi, initial_val_def->type_id(), message_.temp_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.initial_val_id()}}, + {SPV_OPERAND_TYPE_ID, {pred_id}}, + {SPV_OPERAND_TYPE_ID, {message_.eventual_syn_id()}}, + {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}})); + + // Collect the other instructions in a list. These will be added to an + // additional block if |message_.additional_block_id| is defined, to the loop + // header otherwise. + std::vector> other_instructions; + + // Add an instruction to subtract the step value from the temporary value. + // The value of this id will converge to the constant in the last iteration. + other_instructions.push_back(MakeUnique( + ir_context, SpvOpISub, initial_val_def->type_id(), + message_.eventual_syn_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.temp_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.step_val_id()}}})); + + // Add an instruction to increment the counter. + other_instructions.push_back(MakeUnique( + ir_context, SpvOpIAdd, const_0_def->type_id(), + message_.incremented_ctr_id(), + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {message_.ctr_id()}}, + {SPV_OPERAND_TYPE_ID, {const_1_id}}})); + + // Add an instruction to decide whether the condition holds. + other_instructions.push_back(MakeUnique( + ir_context, SpvOpSLessThan, fuzzerutil::MaybeGetBoolType(ir_context), + message_.cond_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.incremented_ctr_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.num_iterations_id()}}})); + + // Define the OpLoopMerge instruction for the loop header. The merge block is + // the existing block, the continue block is the last block in the loop + // (either the loop itself or the additional block). + std::unique_ptr merge_inst = MakeUnique( + ir_context, SpvOpLoopMerge, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.block_after_loop_id()}}, + {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}); + + // Define a conditional branch instruction, branching to the loop header if + // the condition holds, and to the existing block otherwise. This instruction + // will be added to the last block in the loop. + std::unique_ptr conditional_branch = + MakeUnique( + ir_context, SpvOpBranchConditional, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.cond_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.loop_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.block_after_loop_id()}}}); + + if (message_.additional_block_id()) { + // If an id for the additional block is specified, create an additional + // block, containing the instructions in the list and a branching + // instruction. + + std::unique_ptr additional_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.additional_block_id(), + opt::Instruction::OperandList{})); + + for (auto& instruction : other_instructions) { + additional_block->AddInstruction(std::move(instruction)); + } + + additional_block->AddInstruction(std::move(conditional_branch)); + + // Add the merge instruction to the header. + loop_block->AddInstruction(std::move(merge_inst)); + + // Add an unconditional branch from the header to the additional block. + loop_block->AddInstruction(MakeUnique( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.additional_block_id()}}})); + + // Insert the two loop blocks before the existing block. + block_after_loop->GetParent()->InsertBasicBlockBefore(std::move(loop_block), + block_after_loop); + block_after_loop->GetParent()->InsertBasicBlockBefore( + std::move(additional_block), block_after_loop); + } else { + // If no id for an additional block is specified, the loop will only be made + // up of one block, so we need to add all the instructions to it. + + for (auto& instruction : other_instructions) { + loop_block->AddInstruction(std::move(instruction)); + } + + // Add the merge and conditional branch instructions. + loop_block->AddInstruction(std::move(merge_inst)); + loop_block->AddInstruction(std::move(conditional_branch)); + + // Insert the header before the existing block. + block_after_loop->GetParent()->InsertBasicBlockBefore(std::move(loop_block), + block_after_loop); + } + + // Update the branching instructions leading to this block. + ir_context->get_def_use_mgr()->ForEachUse( + message_.block_after_loop_id(), + [this](opt::Instruction* instruction, uint32_t operand_index) { + assert(instruction->opcode() != SpvOpLoopMerge && + instruction->opcode() != SpvOpSelectionMerge && + "The block should not be referenced by OpLoopMerge or " + "OpSelectionMerge, by construction."); + // Replace all uses of the label inside branch instructions. + if (instruction->opcode() == SpvOpBranch || + instruction->opcode() == SpvOpBranchConditional || + instruction->opcode() == SpvOpSwitch) { + instruction->SetOperand(operand_index, {message_.loop_id()}); + } + }); + + // Update all the OpPhi instructions in the block after the loop: its + // predecessor is now the last block in the loop. + block_after_loop->ForEachPhiInst( + [last_loop_block_id](opt::Instruction* phi_inst) { + // Since the block only had one predecessor, the id of the predecessor + // is input operand 1. + phi_inst->SetInOperand(1, {last_loop_block_id}); + }); + + // Add a new OpPhi instruction at the beginning of the block after the loop, + // defining the synonym of the constant. The type id will be the same as + // |message_.initial_value_id|, since this is the value that is decremented in + // the loop. + block_after_loop->begin()->InsertBefore(MakeUnique( + ir_context, SpvOpPhi, initial_val_def->type_id(), message_.syn_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.eventual_syn_id()}}, + {SPV_OPERAND_TYPE_ID, {last_loop_block_id}}})); + + // Update the module id bound with all the fresh ids used. + for (uint32_t id : {message_.syn_id(), message_.loop_id(), message_.ctr_id(), + message_.temp_id(), message_.eventual_syn_id(), + message_.incremented_ctr_id(), message_.cond_id(), + message_.cond_id(), message_.additional_block_id()}) { + fuzzerutil::UpdateModuleIdBound(ir_context, id); + } + + // Since we changed the structure of the module, we need to invalidate all the + // analyses. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Record that |message_.syn_id| is synonymous with |message_.constant_id|. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.syn_id(), {}), + MakeDataDescriptor(message_.constant_id(), {})); +} + +protobufs::Transformation +TransformationAddLoopToCreateIntConstantSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_loop_to_create_int_constant_synonym() = message_; + return result; +} + +std::unordered_set +TransformationAddLoopToCreateIntConstantSynonym::GetFreshIds() const { + return {message_.syn_id(), message_.loop_id(), + message_.ctr_id(), message_.temp_id(), + message_.eventual_syn_id(), message_.incremented_ctr_id(), + message_.cond_id(), message_.additional_block_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h b/third_party/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h new file mode 100644 index 0000000..67c3bcd --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h @@ -0,0 +1,72 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { +class TransformationAddLoopToCreateIntConstantSynonym : public Transformation { + public: + explicit TransformationAddLoopToCreateIntConstantSynonym( + const protobufs::TransformationAddLoopToCreateIntConstantSynonym& + message); + + TransformationAddLoopToCreateIntConstantSynonym( + uint32_t constant_id, uint32_t initial_val_id, uint32_t step_val_id, + uint32_t num_iterations_id, uint32_t block_after_loop_id, uint32_t syn_id, + uint32_t loop_id, uint32_t ctr_id, uint32_t temp_id, + uint32_t eventual_syn_id, uint32_t incremented_ctr_id, uint32_t cond_id, + uint32_t additional_block_id); + + // - |message_.constant_id|, |message_.initial_value_id|, + // |message_.step_val_id| are integer constants (scalar or vectors) with the + // same type (with possibly different signedness, but same bit width, which + // must be <= 64). Let their value be C, I, S respectively. + // - |message_.num_iterations_id| is a 32-bit integer scalar constant, with + // value N > 0 and N <= 32. + // - The module contains 32-bit signed integer scalar constants of values 0 + // and 1. + // - The module contains the boolean type. + // - C = I - S * N + // - |message_.block_after_loop_id| is the label of a block which has a single + // predecessor and which is not a merge block, a continue block or a loop + // header. + // - |message_.block_after_loop_id| must not be a dead block. + // - |message_.additional_block_id| is either 0 or a valid fresh id, distinct + // from the other fresh ids. + // - All of the other parameters are valid fresh ids. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a loop to the module, defining a synonym of an integer (scalar or + // vector) constant. This id is marked as synonym with the original constant. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddLoopToCreateIntConstantSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_LOOP_TO_CREATE_INT_CONSTANT_SYNONYM_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp new file mode 100644 index 0000000..29a871d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_no_contraction_decoration.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddNoContractionDecoration:: + TransformationAddNoContractionDecoration( + const spvtools::fuzz::protobufs:: + TransformationAddNoContractionDecoration& message) + : message_(message) {} + +TransformationAddNoContractionDecoration:: + TransformationAddNoContractionDecoration(uint32_t result_id) { + message_.set_result_id(result_id); +} + +bool TransformationAddNoContractionDecoration::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.result_id| must be the id of an instruction. + auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!instr) { + return false; + } + // The instruction must be arithmetic. + return IsArithmetic(instr->opcode()); +} + +void TransformationAddNoContractionDecoration::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Add a NoContraction decoration targeting |message_.result_id|. + ir_context->get_decoration_mgr()->AddDecoration(message_.result_id(), + SpvDecorationNoContraction); +} + +protobufs::Transformation TransformationAddNoContractionDecoration::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_no_contraction_decoration() = message_; + return result; +} + +bool TransformationAddNoContractionDecoration::IsArithmetic(uint32_t opcode) { + switch (opcode) { + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpAny: + case SpvOpAll: + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpLogicalNot: + return true; + default: + return false; + } +} + +std::unordered_set +TransformationAddNoContractionDecoration::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h b/third_party/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h new file mode 100644 index 0000000..f5a34e8 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_no_contraction_decoration.h @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddNoContractionDecoration : public Transformation { + public: + explicit TransformationAddNoContractionDecoration( + const protobufs::TransformationAddNoContractionDecoration& message); + + explicit TransformationAddNoContractionDecoration(uint32_t fresh_id); + + // - |message_.result_id| must be the result id of an arithmetic instruction, + // as defined by the SPIR-V specification. + // - It does not matter whether this instruction is already annotated with the + // NoContraction decoration. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a decoration of the form: + // 'OpDecoration |message_.result_id| NoContraction' + // to the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |opcode| is the opcode of an arithmetic + // instruction, as defined by the SPIR-V specification. + static bool IsArithmetic(uint32_t opcode); + + private: + protobufs::TransformationAddNoContractionDecoration message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_NO_CONTRACTION_DECORATION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_opphi_synonym.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_opphi_synonym.cpp new file mode 100644 index 0000000..227c433 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_opphi_synonym.cpp @@ -0,0 +1,204 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_opphi_synonym.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym( + const protobufs::TransformationAddOpPhiSynonym& message) + : message_(message) {} + +TransformationAddOpPhiSynonym::TransformationAddOpPhiSynonym( + uint32_t block_id, const std::map& preds_to_ids, + uint32_t fresh_id) { + message_.set_block_id(block_id); + *message_.mutable_pred_to_id() = + fuzzerutil::MapToRepeatedUInt32Pair(preds_to_ids); + message_.set_fresh_id(fresh_id); +} + +bool TransformationAddOpPhiSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that |message_.block_id| is a block label id, and that it is not + // dead. + auto block = fuzzerutil::MaybeFindBlock(ir_context, message_.block_id()); + if (!block || + transformation_context.GetFactManager()->BlockIsDead(block->id())) { + return false; + } + + // Check that |message_.fresh_id| is actually fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Check that |message_.pred_to_id| contains a mapping for all of the block's + // predecessors. + std::vector predecessors = ir_context->cfg()->preds(block->id()); + + // There must be at least one predecessor. + if (predecessors.empty()) { + return false; + } + + std::map preds_to_ids = + fuzzerutil::RepeatedUInt32PairToMap(message_.pred_to_id()); + + // There must not be repeated key values in |message_.pred_to_id|. + if (preds_to_ids.size() != static_cast(message_.pred_to_id_size())) { + return false; + } + + // Check that each predecessor has a corresponding mapping and all of the + // corresponding ids exist. + for (uint32_t pred : predecessors) { + if (preds_to_ids.count(pred) == 0) { + return false; + } + + // Check that the id exists in the module. + if (!ir_context->get_def_use_mgr()->GetDef(preds_to_ids[pred])) { + return false; + } + } + + // Get the first id and its type (which should be the same as all the other + // ones) and check that the transformation supports this type. + uint32_t first_id = preds_to_ids[predecessors[0]]; + uint32_t type_id = ir_context->get_def_use_mgr()->GetDef(first_id)->type_id(); + if (!CheckTypeIsAllowed(ir_context, type_id)) { + return false; + } + + // Check that the ids corresponding to predecessors are all synonymous, have + // the same type and are available to use at the end of the predecessor. + for (uint32_t pred : predecessors) { + auto id = preds_to_ids[pred]; + + // Check that the id has the same type as the other ones. + if (ir_context->get_def_use_mgr()->GetDef(id)->type_id() != type_id) { + return false; + } + + // Check that the id is synonymous with the others by checking that it is + // synonymous with the first one (or it is the same id). + if (id != first_id && + !transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(id, {}), MakeDataDescriptor(first_id, {}))) { + return false; + } + + // Check that the id is available at the end of the corresponding + // predecessor block. + + auto pred_block = ir_context->get_instr_block(pred); + + // We should always be able to find the predecessor block, since it is in + // the predecessors list of |block|. + assert(pred_block && "Could not find one of the predecessor blocks."); + + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, pred_block->terminator(), id)) { + return false; + } + } + + return true; +} + +void TransformationAddOpPhiSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Get the type id from one of the ids. + uint32_t first_id = message_.pred_to_id(0).second(); + uint32_t type_id = ir_context->get_def_use_mgr()->GetDef(first_id)->type_id(); + + // Define the operand list. + opt::Instruction::OperandList operand_list; + + // For each predecessor, add the corresponding operands. + for (auto& pair : message_.pred_to_id()) { + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {pair.second()}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {pair.first()}}); + } + + // Add a new OpPhi instructions at the beginning of the block. + ir_context->get_instr_block(message_.block_id()) + ->begin() + .InsertBefore(MakeUnique(ir_context, SpvOpPhi, type_id, + message_.fresh_id(), + std::move(operand_list))); + + // Update the module id bound. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Invalidate all analyses, since we added an instruction to the module. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // Record the fact that the new id is synonym with the other ones by declaring + // that it is a synonym of the first one. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.fresh_id(), {}), + MakeDataDescriptor(first_id, {})); +} + +protobufs::Transformation TransformationAddOpPhiSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_opphi_synonym() = message_; + return result; +} + +bool TransformationAddOpPhiSynonym::CheckTypeIsAllowed( + opt::IRContext* ir_context, uint32_t type_id) { + auto type = ir_context->get_type_mgr()->GetType(type_id); + if (!type) { + return false; + } + + // We allow the following types: Bool, Integer, Float, Vector, Matrix, Array, + // Struct. + if (type->AsBool() || type->AsInteger() || type->AsFloat() || + type->AsVector() || type->AsMatrix() || type->AsArray() || + type->AsStruct()) { + return true; + } + + // We allow pointer types if the VariablePointers capability is enabled and + // the pointer has the correct storage class (Workgroup or StorageBuffer). + if (type->AsPointer()) { + auto storage_class = type->AsPointer()->storage_class(); + return ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers) && + (storage_class == SpvStorageClassWorkgroup || + storage_class == SpvStorageClassStorageBuffer); + } + + // We do not allow other types. + return false; +} + +std::unordered_set TransformationAddOpPhiSynonym::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_opphi_synonym.h b/third_party/spirv-tools/source/fuzz/transformation_add_opphi_synonym.h new file mode 100644 index 0000000..3b68abe --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_opphi_synonym.h @@ -0,0 +1,75 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_OPPHI_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_OPPHI_SYNONYM_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { +class TransformationAddOpPhiSynonym : public Transformation { + public: + explicit TransformationAddOpPhiSynonym( + const protobufs::TransformationAddOpPhiSynonym& message); + + TransformationAddOpPhiSynonym( + uint32_t block_id, const std::map& preds_to_ids, + uint32_t fresh_id); + + // - |message_.block_id| is the label of a block with at least one + // predecessor. + // - |message_.block_id| must not be a dead block. + // - |message_.pred_to_id| contains a mapping from each of the predecessors of + // the block to an id that is available at the end of the predecessor. + // - All the ids corresponding to a predecessor in |message_.pred_to_id|: + // - have been recorded as synonymous and all have the same type. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3726): if a + // predecessor is a dead block, any id of the right type could be used, + // even if it is not synonym with the others. + // - have one of the following types: Bool, Integer, Float, Vector, Matrix, + // Array, Struct. Pointer types are also allowed if the VariablePointers + // capability is enabled and the storage class is Workgroup or + // StorageBuffer. + // - |message_.fresh_id| is a fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Given a block with n predecessors, with n >= 1, and n corresponding + // synonymous ids of the same type, each available to use at the end of the + // corresponding predecessor, adds an OpPhi instruction at the beginning of + // the block of the form: + // %fresh_id = OpPhi %type %id_1 %pred_1 %id_2 %pred_2 ... %id_n %pred_n + // This instruction is then marked as synonymous with the ids. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + // Returns true if |type_id| is the id of a type in the module, which is one + // of the following: Bool, Integer, Float, Vector, Matrix, Array, Struct. + // Pointer types are also allowed if the VariablePointers capability is + // enabled and the storage class is Workgroup or StorageBuffer. + static bool CheckTypeIsAllowed(opt::IRContext* ir_context, uint32_t type_id); + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddOpPhiSynonym message_; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_OPPHI_SYNONYM_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_parameter.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_parameter.cpp new file mode 100644 index 0000000..9ed0bfb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_parameter.cpp @@ -0,0 +1,225 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_parameter.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddParameter::TransformationAddParameter( + const protobufs::TransformationAddParameter& message) + : message_(message) {} + +TransformationAddParameter::TransformationAddParameter( + uint32_t function_id, uint32_t parameter_fresh_id, + uint32_t parameter_type_id, std::map call_parameter_ids, + uint32_t function_type_fresh_id) { + message_.set_function_id(function_id); + message_.set_parameter_fresh_id(parameter_fresh_id); + message_.set_parameter_type_id(parameter_type_id); + *message_.mutable_call_parameter_ids() = + fuzzerutil::MapToRepeatedUInt32Pair(call_parameter_ids); + message_.set_function_type_fresh_id(function_type_fresh_id); +} + +bool TransformationAddParameter::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that function exists. + const auto* function = + fuzzerutil::FindFunction(ir_context, message_.function_id()); + if (!function || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // The type must be supported. + if (ir_context->get_def_use_mgr()->GetDef(message_.parameter_type_id()) == + nullptr) { + return false; + } + if (!IsParameterTypeSupported(ir_context, message_.parameter_type_id())) { + return false; + } + + // Iterate over all callers. + std::map call_parameter_ids_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids()); + for (auto* instr : + fuzzerutil::GetCallers(ir_context, message_.function_id())) { + uint32_t caller_id = instr->result_id(); + + // If there is no entry for this caller, return false. + if (call_parameter_ids_map.find(caller_id) == + call_parameter_ids_map.end()) { + return false; + } + uint32_t value_id = call_parameter_ids_map[caller_id]; + + auto value_instr = ir_context->get_def_use_mgr()->GetDef(value_id); + if (!value_instr) { + return false; + } + // If the id of the value of the map is not available before the caller, + // return false. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, instr, + value_id)) { + return false; + } + + // The type of the value must be defined. + uint32_t value_type_id = fuzzerutil::GetTypeId(ir_context, value_id); + if (!value_type_id) { + return false; + } + + // Type of every value of the map must be the same for all callers. + if (message_.parameter_type_id() != value_type_id) { + return false; + } + } + return fuzzerutil::IsFreshId(ir_context, message_.parameter_fresh_id()) && + fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) && + message_.parameter_fresh_id() != message_.function_type_fresh_id(); +} + +void TransformationAddParameter::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Find the function that will be transformed. + auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); + assert(function && "Can't find the function"); + + std::map call_parameter_ids_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.call_parameter_ids()); + + uint32_t new_parameter_type_id = message_.parameter_type_id(); + auto new_parameter_type = + ir_context->get_type_mgr()->GetType(new_parameter_type_id); + assert(new_parameter_type && "New parameter has invalid type."); + + // Add new parameters to the function. + function->AddParameter(MakeUnique( + ir_context, SpvOpFunctionParameter, new_parameter_type_id, + message_.parameter_fresh_id(), opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.parameter_fresh_id()); + + // Fix all OpFunctionCall instructions. + for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) { + inst->AddOperand( + {SPV_OPERAND_TYPE_ID, {call_parameter_ids_map[inst->result_id()]}}); + } + + // Update function's type. + { + // We use a separate scope here since |old_function_type| might become a + // dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + const auto* old_function_type = + fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function must have a valid type"); + + std::vector parameter_type_ids; + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + parameter_type_ids.push_back( + old_function_type->GetSingleWordInOperand(i)); + } + + parameter_type_ids.push_back(new_parameter_type_id); + + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.function_type_fresh_id(), + old_function_type->GetSingleWordInOperand(0), parameter_type_ids); + } + + auto new_parameter_kind = new_parameter_type->kind(); + + // Make sure our changes are analyzed. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // If the |new_parameter_type_id| is not a pointer type, mark id as + // irrelevant so that we can replace its use with some other id. If the + // |new_parameter_type_id| is a pointer type, we cannot mark it with + // IdIsIrrelevant, because this pointer might be replaced by a pointer from + // original shader. This would change the semantics of the module. In the case + // of a pointer type we mark it with PointeeValueIsIrrelevant. + if (new_parameter_kind != opt::analysis::Type::kPointer) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + message_.parameter_fresh_id()); + } else { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.parameter_fresh_id()); + } +} + +protobufs::Transformation TransformationAddParameter::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_parameter() = message_; + return result; +} + +bool TransformationAddParameter::IsParameterTypeSupported( + opt::IRContext* ir_context, uint32_t type_id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Think about other type instructions we can add here. + opt::Instruction* type_inst = ir_context->get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeMatrix: + case SpvOpTypeVector: + return true; + case SpvOpTypeArray: + return IsParameterTypeSupported(ir_context, + type_inst->GetSingleWordInOperand(0)); + case SpvOpTypeStruct: + if (fuzzerutil::HasBlockOrBufferBlockDecoration(ir_context, type_id)) { + return false; + } + for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) { + if (!IsParameterTypeSupported(ir_context, + type_inst->GetSingleWordInOperand(i))) { + return false; + } + } + return true; + case SpvOpTypePointer: { + SpvStorageClass storage_class = + static_cast(type_inst->GetSingleWordInOperand(0)); + switch (storage_class) { + case SpvStorageClassPrivate: + case SpvStorageClassFunction: + case SpvStorageClassWorkgroup: { + return IsParameterTypeSupported(ir_context, + type_inst->GetSingleWordInOperand(1)); + } + default: + return false; + } + } + default: + return false; + } +} + +std::unordered_set TransformationAddParameter::GetFreshIds() const { + return {message_.parameter_fresh_id(), message_.function_type_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_parameter.h b/third_party/spirv-tools/source/fuzz/transformation_add_parameter.h new file mode 100644 index 0000000..a33521d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_parameter.h @@ -0,0 +1,75 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddParameter : public Transformation { + public: + explicit TransformationAddParameter( + const protobufs::TransformationAddParameter& message); + + TransformationAddParameter(uint32_t function_id, uint32_t parameter_fresh_id, + uint32_t parameter_type_id, + std::map call_parameter_ids, + uint32_t function_type_fresh_id); + + // - |function_id| must be a valid result id of some non-entry-point function + // in the module. + // - |parameter_type_id| is a type id of the new parameter. The type must be + // supported by this transformation as specified by IsParameterTypeSupported + // function. + // - |call_parameter_id| must map from every id of an OpFunctionCall + // instruction of this function to the id that will be passed as the new + // parameter at that call site. There could be no callers, therefore this + // map can be empty. + // - |parameter_fresh_id| and |function_type_fresh_id| are fresh ids and are + // not equal. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Creates a new OpFunctionParameter instruction with result id + // |parameter_fresh_id| for the function with |function_id|. + // - Adjusts function's type to include a new parameter. + // - Adds an argument to every caller of the function to account for the added + // parameter. The argument is the value in |call_parameter_id| map. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if the type of the parameter is supported by this + // transformation. + static bool IsParameterTypeSupported(opt::IRContext* ir_context, + uint32_t type_id); + + private: + protobufs::TransformationAddParameter message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_PARAMETER_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp new file mode 100644 index 0000000..7b51305 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.cpp @@ -0,0 +1,151 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_relaxed_decoration.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration( + const spvtools::fuzz::protobufs::TransformationAddRelaxedDecoration& + message) + : message_(message) {} + +TransformationAddRelaxedDecoration::TransformationAddRelaxedDecoration( + uint32_t result_id) { + message_.set_result_id(result_id); +} + +bool TransformationAddRelaxedDecoration::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |message_.result_id| must be the id of an instruction. + auto instr = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!instr) { + return false; + } + opt::BasicBlock* cur_block = ir_context->get_instr_block(instr); + // The instruction must have a block. + if (cur_block == nullptr) { + return false; + } + // |cur_block| must be a dead block. + if (!(transformation_context.GetFactManager()->BlockIsDead( + cur_block->id()))) { + return false; + } + // The instruction must be numeric. + return IsNumeric(instr->opcode()); +} + +void TransformationAddRelaxedDecoration::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Add a RelaxedPrecision decoration targeting |message_.result_id|. + ir_context->get_decoration_mgr()->AddDecoration( + message_.result_id(), SpvDecorationRelaxedPrecision); +} + +protobufs::Transformation TransformationAddRelaxedDecoration::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_add_relaxed_decoration() = message_; + return result; +} + +bool TransformationAddRelaxedDecoration::IsNumeric(uint32_t opcode) { + switch (opcode) { + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpUConvert: + case SpvOpSConvert: + case SpvOpFConvert: + case SpvOpConvertPtrToU: + case SpvOpSatConvertSToU: + case SpvOpSatConvertUToS: + case SpvOpVectorExtractDynamic: + case SpvOpVectorInsertDynamic: + case SpvOpVectorShuffle: + case SpvOpTranspose: + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitFieldInsert: + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: + case SpvOpBitReverse: + case SpvOpBitCount: + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + return true; + default: + return false; + } +} + +std::unordered_set TransformationAddRelaxedDecoration::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools \ No newline at end of file diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h b/third_party/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h new file mode 100644 index 0000000..3f8bf3e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_relaxed_decoration.h @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_RELAXED_DECORATION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_RELAXED_DECORATION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddRelaxedDecoration : public Transformation { + public: + explicit TransformationAddRelaxedDecoration( + const protobufs::TransformationAddRelaxedDecoration& message); + + explicit TransformationAddRelaxedDecoration(uint32_t fresh_id); + + // - |message_.result_id| must be the result id of an instruction, which is + // located in a dead block and Relaxed decoration can be applied. + // - It does not matter whether this instruction is already annotated with the + // Relaxed decoration. + bool IsApplicable( + + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds a decoration of the form: + // 'OpDecoration |message_.result_id| RelaxedPrecision' + // to the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |opcode| is the opcode of an instruction + // that operates on 32-bit integers and 32-bit floats + // as defined by the SPIR-V specification. + static bool IsNumeric(uint32_t opcode); + + private: + protobufs::TransformationAddRelaxedDecoration message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_RELAXED_DECORATION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_spec_constant_op.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_spec_constant_op.cpp new file mode 100644 index 0000000..19c5e85 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_spec_constant_op.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_spec_constant_op.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddSpecConstantOp::TransformationAddSpecConstantOp( + spvtools::fuzz::protobufs::TransformationAddSpecConstantOp message) + : message_(std::move(message)) {} + +TransformationAddSpecConstantOp::TransformationAddSpecConstantOp( + uint32_t fresh_id, uint32_t type_id, SpvOp opcode, + const opt::Instruction::OperandList& operands) { + message_.set_fresh_id(fresh_id); + message_.set_type_id(type_id); + message_.set_opcode(opcode); + for (const auto& operand : operands) { + auto* op = message_.add_operand(); + op->set_operand_type(operand.type); + for (auto word : operand.words) { + op->add_operand_data(word); + } + } +} + +bool TransformationAddSpecConstantOp::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto clone = fuzzerutil::CloneIRContext(ir_context); + ApplyImpl(clone.get()); + return fuzzerutil::IsValid(clone.get(), + transformation_context.GetValidatorOptions(), + fuzzerutil::kSilentMessageConsumer); +} + +void TransformationAddSpecConstantOp::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ApplyImpl(ir_context); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +void TransformationAddSpecConstantOp::ApplyImpl( + opt::IRContext* ir_context) const { + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {message_.opcode()}}}; + + for (const auto& operand : message_.operand()) { + std::vector words(operand.operand_data().begin(), + operand.operand_data().end()); + operands.push_back({static_cast(operand.operand_type()), + std::move(words)}); + } + + ir_context->AddGlobalValue(MakeUnique( + ir_context, SpvOpSpecConstantOp, message_.type_id(), message_.fresh_id(), + std::move(operands))); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); +} + +protobufs::Transformation TransformationAddSpecConstantOp::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_spec_constant_op() = message_; + return result; +} + +std::unordered_set TransformationAddSpecConstantOp::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_spec_constant_op.h b/third_party/spirv-tools/source/fuzz/transformation_add_spec_constant_op.h new file mode 100644 index 0000000..29851fd --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_spec_constant_op.h @@ -0,0 +1,62 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddSpecConstantOp : public Transformation { + public: + explicit TransformationAddSpecConstantOp( + protobufs::TransformationAddSpecConstantOp message); + + TransformationAddSpecConstantOp( + uint32_t fresh_id, uint32_t type_id, SpvOp opcode, + const opt::Instruction::OperandList& operands); + + // - |fresh_id| is a fresh result id in the module. + // - |type_id| is a valid result id of some OpType* instruction in the + // module. It is also a valid type id with respect to |opcode|. + // - |opcode| is one of the opcodes supported by OpSpecConstantOp. + // - |operands| are valid with respect to |opcode| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // |%fresh_id = OpSpecConstantOp %type_id opcode operands...| is added to the + // module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + void ApplyImpl(opt::IRContext* ir_context) const; + + protobufs::TransformationAddSpecConstantOp message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SPEC_CONSTANT_OP_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_synonym.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_synonym.cpp new file mode 100644 index 0000000..a516916 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_synonym.cpp @@ -0,0 +1,328 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_synonym.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddSynonym::TransformationAddSynonym( + protobufs::TransformationAddSynonym message) + : message_(std::move(message)) {} + +TransformationAddSynonym::TransformationAddSynonym( + uint32_t result_id, + protobufs::TransformationAddSynonym::SynonymType synonym_type, + uint32_t synonym_fresh_id, + const protobufs::InstructionDescriptor& insert_before) { + message_.set_result_id(result_id); + message_.set_synonym_type(synonym_type); + message_.set_synonym_fresh_id(synonym_fresh_id); + *message_.mutable_insert_before() = insert_before; +} + +bool TransformationAddSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + assert(protobufs::TransformationAddSynonym::SynonymType_IsValid( + message_.synonym_type()) && + "Synonym type is invalid"); + + // |synonym_fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) { + return false; + } + + // Check that |message_.result_id| is valid. + auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!synonym) { + return false; + } + + // Check that we can apply |synonym_type| to |result_id|. + if (!IsInstructionValid(ir_context, transformation_context, synonym, + message_.synonym_type())) { + return false; + } + + // Check that |insert_before| is valid. + auto* insert_before_inst = + FindInstruction(message_.insert_before(), ir_context); + if (!insert_before_inst) { + return false; + } + + const auto* insert_before_inst_block = + ir_context->get_instr_block(insert_before_inst); + assert(insert_before_inst_block && + "|insert_before_inst| must be in some block"); + + if (transformation_context.GetFactManager()->BlockIsDead( + insert_before_inst_block->id())) { + // We don't create synonyms in dead blocks. + return false; + } + + // Check that we can insert |message._synonymous_instruction| before + // |message_.insert_before| instruction. We use OpIAdd to represent some + // instruction that can produce a synonym. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpIAdd, + insert_before_inst)) { + return false; + } + + // A constant instruction must be present in the module if required. + if (IsAdditionalConstantRequired(message_.synonym_type()) && + MaybeGetConstantId(ir_context, transformation_context) == 0) { + return false; + } + + // Domination rules must be satisfied. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before_inst, message_.result_id()); +} + +void TransformationAddSynonym::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Add a synonymous instruction. + FindInstruction(message_.insert_before(), ir_context) + ->InsertBefore( + MakeSynonymousInstruction(ir_context, *transformation_context)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id()); + + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // Propagate PointeeValueIsIrrelevant fact. + const auto* new_synonym_type = ir_context->get_type_mgr()->GetType( + fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id())); + assert(new_synonym_type && "New synonym should have a valid type"); + + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + message_.result_id()) && + new_synonym_type->AsPointer()) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.synonym_fresh_id()); + } + + // Mark two ids as synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.result_id(), {}), + MakeDataDescriptor(message_.synonym_fresh_id(), {})); +} + +protobufs::Transformation TransformationAddSynonym::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_synonym() = message_; + return result; +} + +bool TransformationAddSynonym::IsInstructionValid( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, opt::Instruction* inst, + protobufs::TransformationAddSynonym::SynonymType synonym_type) { + // Instruction must have a result id, type id. We skip OpUndef and + // OpConstantNull. + if (!inst || !inst->result_id() || !inst->type_id() || + inst->opcode() == SpvOpUndef || inst->opcode() == SpvOpConstantNull) { + return false; + } + + if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) { + return false; + } + + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::MUL_ONE: { + // The instruction must be either scalar or vector of integers or floats. + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction's result id is invalid"); + + if (const auto* vector = type->AsVector()) { + return vector->element_type()->AsInteger() || + vector->element_type()->AsFloat(); + } + + return type->AsInteger() || type->AsFloat(); + } + case protobufs::TransformationAddSynonym::COPY_OBJECT: + // All checks for OpCopyObject are handled by + // fuzzerutil::CanMakeSynonymOf. + return true; + case protobufs::TransformationAddSynonym::LOGICAL_AND: + case protobufs::TransformationAddSynonym::LOGICAL_OR: { + // The instruction must be either a scalar or a vector of booleans. + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "Instruction's result id is invalid"); + return (type->AsVector() && type->AsVector()->element_type()->AsBool()) || + type->AsBool(); + } + default: + assert(false && "Synonym type is not supported"); + return false; + } +} + +std::unique_ptr +TransformationAddSynonym::MakeSynonymousInstruction( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto synonym_type_id = + fuzzerutil::GetTypeId(ir_context, message_.result_id()); + assert(synonym_type_id && "Synonym has invalid type id"); + + switch (message_.synonym_type()) { + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::ADD_ZERO: { + const auto* synonym_type = + ir_context->get_type_mgr()->GetType(synonym_type_id); + assert(synonym_type && "Synonym has invalid type"); + + // Compute instruction's opcode based on the type of the operand. + // We have already checked that the operand is either a scalar or a vector + // of either integers or floats. + auto is_integral = + (synonym_type->AsVector() && + synonym_type->AsVector()->element_type()->AsInteger()) || + synonym_type->AsInteger(); + auto opcode = SpvOpNop; + switch (message_.synonym_type()) { + case protobufs::TransformationAddSynonym::SUB_ZERO: + opcode = is_integral ? SpvOpISub : SpvOpFSub; + break; + case protobufs::TransformationAddSynonym::MUL_ONE: + opcode = is_integral ? SpvOpIMul : SpvOpFMul; + break; + case protobufs::TransformationAddSynonym::ADD_ZERO: + opcode = is_integral ? SpvOpIAdd : SpvOpFAdd; + break; + default: + assert(false && "Unreachable"); + break; + } + + return MakeUnique( + ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, + {SPV_OPERAND_TYPE_ID, + {MaybeGetConstantId(ir_context, transformation_context)}}}); + } + case protobufs::TransformationAddSynonym::COPY_OBJECT: + return MakeUnique( + ir_context, SpvOpCopyObject, synonym_type_id, + message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}}); + case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::LOGICAL_AND: { + auto opcode = message_.synonym_type() == + protobufs::TransformationAddSynonym::LOGICAL_OR + ? SpvOpLogicalOr + : SpvOpLogicalAnd; + return MakeUnique( + ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.result_id()}}, + {SPV_OPERAND_TYPE_ID, + {MaybeGetConstantId(ir_context, transformation_context)}}}); + } + default: + assert(false && "Unhandled synonym type"); + return nullptr; + } +} + +uint32_t TransformationAddSynonym::MaybeGetConstantId( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + assert(IsAdditionalConstantRequired(message_.synonym_type()) && + "Synonym type doesn't require an additional constant"); + + auto synonym_type_id = + fuzzerutil::GetTypeId(ir_context, message_.result_id()); + assert(synonym_type_id && "Synonym has invalid type id"); + + switch (message_.synonym_type()) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + return fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, synonym_type_id, false); + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: { + auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id); + assert(synonym_type && "Synonym has invalid type"); + + if (const auto* vector = synonym_type->AsVector()) { + auto element_type_id = + ir_context->get_type_mgr()->GetId(vector->element_type()); + assert(element_type_id && "Vector's element type is invalid"); + + auto one_word = + vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u; + if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, {one_word}, element_type_id, + false)) { + return fuzzerutil::MaybeGetCompositeConstant( + ir_context, transformation_context, + std::vector(vector->element_count(), scalar_one_id), + synonym_type_id, false); + } + + return 0; + } else { + return fuzzerutil::MaybeGetScalarConstant( + ir_context, transformation_context, + {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u}, + synonym_type_id, false); + } + } + default: + // The assertion at the beginning of the function will fail in the debug + // mode. + return 0; + } +} + +bool TransformationAddSynonym::IsAdditionalConstantRequired( + protobufs::TransformationAddSynonym::SynonymType synonym_type) { + switch (synonym_type) { + case protobufs::TransformationAddSynonym::ADD_ZERO: + case protobufs::TransformationAddSynonym::SUB_ZERO: + case protobufs::TransformationAddSynonym::LOGICAL_OR: + case protobufs::TransformationAddSynonym::MUL_ONE: + case protobufs::TransformationAddSynonym::LOGICAL_AND: + return true; + default: + return false; + } +} + +std::unordered_set TransformationAddSynonym::GetFreshIds() const { + return {message_.synonym_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_synonym.h b/third_party/spirv-tools/source/fuzz/transformation_add_synonym.h new file mode 100644 index 0000000..33c4353 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_synonym.h @@ -0,0 +1,94 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddSynonym : public Transformation { + public: + explicit TransformationAddSynonym( + protobufs::TransformationAddSynonym message); + + TransformationAddSynonym( + uint32_t result_id, + protobufs::TransformationAddSynonym::SynonymType synonym_type, + uint32_t synonym_fresh_id, + const protobufs::InstructionDescriptor& insert_before); + + // - |result_id| must be a valid result id of some instruction in the module. + // - |result_id| may not be an irrelevant id. + // - |synonym_type| is a type of the synonymous instruction that will be + // created. + // - |synonym_fresh_id| is a fresh id. + // - |insert_before| must be a valid instruction descriptor and we must be + // able to insert a new synonymous instruction before |insert_before|. + // - |result_id| must be available before |insert_before|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Creates a new synonymous instruction according to the |synonym_type| with + // result id |synonym_fresh_id|. + // Inserts that instruction before |insert_before| and creates a fact + // that the |synonym_fresh_id| and the |result_id| are synonymous. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if we can create a synonym of |inst| according to the + // |synonym_type|. + static bool IsInstructionValid( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::Instruction* inst, + protobufs::TransformationAddSynonym::SynonymType synonym_type); + + // Returns true if |synonym_type| requires an additional constant instruction + // to be present in the module. + static bool IsAdditionalConstantRequired( + protobufs::TransformationAddSynonym::SynonymType synonym_type); + + private: + // Returns a new instruction which is synonymous to |message_.result_id|. + std::unique_ptr MakeSynonymousInstruction( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; + + // Returns a result id of a constant instruction that is required to be + // present in some synonym types (e.g. returns a result id of a zero constant + // for ADD_ZERO synonym type). Returns 0 if no such instruction is present in + // the module. This method should only be called when + // IsAdditionalConstantRequired returns true. + uint32_t MaybeGetConstantId( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const; + + protobufs::TransformationAddSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_SYNONYM_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_array.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_array.cpp new file mode 100644 index 0000000..c9f6a87 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_array.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_array.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeArray::TransformationAddTypeArray( + const spvtools::fuzz::protobufs::TransformationAddTypeArray& message) + : message_(message) {} + +TransformationAddTypeArray::TransformationAddTypeArray(uint32_t fresh_id, + uint32_t element_type_id, + uint32_t size_id) { + message_.set_fresh_id(fresh_id); + message_.set_element_type_id(element_type_id); + message_.set_size_id(size_id); +} + +bool TransformationAddTypeArray::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + auto element_type = + ir_context->get_type_mgr()->GetType(message_.element_type_id()); + if (!element_type || element_type->AsFunction()) { + // The element type id either does not refer to a type, or refers to a + // function type; both are illegal. + return false; + } + auto constant = + ir_context->get_constant_mgr()->GetConstantsFromIds({message_.size_id()}); + if (constant.empty()) { + // The size id does not refer to a constant. + return false; + } + assert(constant.size() == 1 && + "Only one constant id was provided, so only one constant should have " + "been returned"); + + auto int_constant = constant[0]->AsIntConstant(); + if (!int_constant) { + // The size constant is not an integer. + return false; + } + // We require that the size constant be a 32-bit value that is positive when + // interpreted as being signed. + return int_constant->words().size() == 1 && int_constant->GetS32() >= 1; +} + +void TransformationAddTypeArray::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.element_type_id()}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.size_id()}}); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeArray, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeArray::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_array() = message_; + return result; +} + +std::unordered_set TransformationAddTypeArray::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_array.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_array.h new file mode 100644 index 0000000..ebefc23 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_array.h @@ -0,0 +1,59 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeArray : public Transformation { + public: + explicit TransformationAddTypeArray( + const protobufs::TransformationAddTypeArray& message); + + TransformationAddTypeArray(uint32_t fresh_id, uint32_t element_type_id, + uint32_t size_id); + + // - |message_.fresh_id| must be fresh + // - |message_.element_type_id| must be the id of a non-function type + // - |message_.size_id| must be the id of a 32-bit integer constant that is + // positive when interpreted as signed. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeArray instruction to the module, with element type given by + // |message_.element_type_id| and size given by |message_.size_id|. The + // result id of the instruction is |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeArray message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_ARRAY_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_boolean.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_boolean.cpp new file mode 100644 index 0000000..ebbfabc --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_boolean.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_boolean.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeBoolean::TransformationAddTypeBoolean( + const spvtools::fuzz::protobufs::TransformationAddTypeBoolean& message) + : message_(message) {} + +TransformationAddTypeBoolean::TransformationAddTypeBoolean(uint32_t fresh_id) { + message_.set_fresh_id(fresh_id); +} + +bool TransformationAddTypeBoolean::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Applicable if there is no bool type already declared in the module. + opt::analysis::Bool bool_type; + return ir_context->get_type_mgr()->GetId(&bool_type) == 0; +} + +void TransformationAddTypeBoolean::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction::OperandList empty_operands; + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeBool, 0, message_.fresh_id(), empty_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeBoolean::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_boolean() = message_; + return result; +} + +std::unordered_set TransformationAddTypeBoolean::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_boolean.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_boolean.h new file mode 100644 index 0000000..25c9272 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_boolean.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeBoolean : public Transformation { + public: + explicit TransformationAddTypeBoolean( + const protobufs::TransformationAddTypeBoolean& message); + + explicit TransformationAddTypeBoolean(uint32_t fresh_id); + + // - |message_.fresh_id| must not be used by the module. + // - The module must not yet declare OpTypeBoolean + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds OpTypeBoolean with |message_.fresh_id| as result id. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeBoolean message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_BOOLEAN_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_float.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_float.cpp new file mode 100644 index 0000000..da3f3e4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_float.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_float.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeFloat::TransformationAddTypeFloat(uint32_t fresh_id, + uint32_t width) { + message_.set_fresh_id(fresh_id); + message_.set_width(width); +} + +TransformationAddTypeFloat::TransformationAddTypeFloat( + const spvtools::fuzz::protobufs::TransformationAddTypeFloat& message) + : message_(message) {} + +bool TransformationAddTypeFloat::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Checks float type width capabilities. + switch (message_.width()) { + case 16: + // The Float16 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityFloat16)) { + return false; + } + break; + case 32: + // No capabilities needed. + break; + case 64: + // The Float64 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityFloat64)) { + return false; + } + break; + default: + assert(false && "Unexpected float type width"); + return false; + } + + // Applicable if there is no float type with this width already declared in + // the module. + return fuzzerutil::MaybeGetFloatType(ir_context, message_.width()) == 0; +} + +void TransformationAddTypeFloat::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + fuzzerutil::AddFloatType(ir_context, message_.fresh_id(), message_.width()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeFloat::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_float() = message_; + return result; +} + +std::unordered_set TransformationAddTypeFloat::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_float.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_float.h new file mode 100644 index 0000000..30cd0fc --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_float.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeFloat : public Transformation { + public: + explicit TransformationAddTypeFloat( + const protobufs::TransformationAddTypeFloat& message); + + TransformationAddTypeFloat(uint32_t fresh_id, uint32_t width); + + // - |message_.fresh_id| must not be used by the module + // - The module must not contain an OpTypeFloat instruction with width + // |message_.width| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeFloat instruction to the module with the given width + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeFloat message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FLOAT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_function.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_function.cpp new file mode 100644 index 0000000..b6cddfc --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_function.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_function.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeFunction::TransformationAddTypeFunction( + const spvtools::fuzz::protobufs::TransformationAddTypeFunction& message) + : message_(message) {} + +TransformationAddTypeFunction::TransformationAddTypeFunction( + uint32_t fresh_id, uint32_t return_type_id, + const std::vector& argument_type_ids) { + message_.set_fresh_id(fresh_id); + message_.set_return_type_id(return_type_id); + for (auto id : argument_type_ids) { + message_.add_argument_type_id(id); + } +} + +bool TransformationAddTypeFunction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The return and argument types must be type ids but not not be function + // type ids. + if (!fuzzerutil::IsNonFunctionTypeId(ir_context, message_.return_type_id())) { + return false; + } + for (auto argument_type_id : message_.argument_type_id()) { + if (!fuzzerutil::IsNonFunctionTypeId(ir_context, argument_type_id)) { + return false; + } + } + // Check whether there is already an OpTypeFunction definition that uses + // exactly the same return and argument type ids. (Note that the type manager + // does not allow us to check this, as it does not distinguish between + // function types with different but isomorphic pointer argument types.) + std::vector type_ids = {message_.return_type_id()}; + type_ids.insert(type_ids.end(), message_.argument_type_id().begin(), + message_.argument_type_id().end()); + return fuzzerutil::FindFunctionType(ir_context, type_ids) == 0; +} + +void TransformationAddTypeFunction::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + std::vector type_ids = {message_.return_type_id()}; + type_ids.insert(type_ids.end(), message_.argument_type_id().begin(), + message_.argument_type_id().end()); + + fuzzerutil::AddFunctionType(ir_context, message_.fresh_id(), type_ids); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_function() = message_; + return result; +} + +std::unordered_set TransformationAddTypeFunction::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_function.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_function.h new file mode 100644 index 0000000..59ded2b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_function.h @@ -0,0 +1,63 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeFunction : public Transformation { + public: + explicit TransformationAddTypeFunction( + const protobufs::TransformationAddTypeFunction& message); + + TransformationAddTypeFunction(uint32_t fresh_id, uint32_t return_type_id, + const std::vector& argument_type_ids); + + // - |message_.fresh_id| must not be used by the module + // - |message_.return_type_id| and each element of |message_.argument_type_id| + // must be the ids of non-function types + // - The module must not contain an OpTypeFunction instruction defining a + // function type with the signature provided by the given return and + // argument types + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeFunction instruction to the module, with signature given by + // |message_.return_type_id| and |message_.argument_type_id|. The result id + // for the instruction is |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_FUNCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_int.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_int.cpp new file mode 100644 index 0000000..253ea15 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_int.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_int.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeInt::TransformationAddTypeInt( + const spvtools::fuzz::protobufs::TransformationAddTypeInt& message) + : message_(message) {} + +TransformationAddTypeInt::TransformationAddTypeInt(uint32_t fresh_id, + uint32_t width, + bool is_signed) { + message_.set_fresh_id(fresh_id); + message_.set_width(width); + message_.set_is_signed(is_signed); +} + +bool TransformationAddTypeInt::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // Checks integer type width capabilities. + switch (message_.width()) { + case 8: + // The Int8 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt8)) { + return false; + } + break; + case 16: + // The Int16 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt16)) { + return false; + } + break; + case 32: + // No capabilities needed. + break; + case 64: + // The Int64 capability must be present. + if (!ir_context->get_feature_mgr()->HasCapability(SpvCapabilityInt64)) { + return false; + } + break; + default: + assert(false && "Unexpected integer type width"); + return false; + } + + // Applicable if there is no int type with this width and signedness already + // declared in the module. + return fuzzerutil::MaybeGetIntegerType(ir_context, message_.width(), + message_.is_signed()) == 0; +} + +void TransformationAddTypeInt::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + fuzzerutil::AddIntegerType(ir_context, message_.fresh_id(), message_.width(), + message_.is_signed()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeInt::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_int() = message_; + return result; +} + +std::unordered_set TransformationAddTypeInt::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_int.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_int.h new file mode 100644 index 0000000..20c90ca --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_int.h @@ -0,0 +1,56 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeInt : public Transformation { + public: + explicit TransformationAddTypeInt( + const protobufs::TransformationAddTypeInt& message); + + TransformationAddTypeInt(uint32_t fresh_id, uint32_t width, bool is_signed); + + // - |message_.fresh_id| must not be used by the module + // - The module must not contain an OpTypeInt instruction with width + // |message_.width| and signedness |message.is_signed| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeInt instruction to the module with the given width and + // signedness. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeInt message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_INT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp new file mode 100644 index 0000000..cecebb4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_matrix.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_matrix.h" + +#include "fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeMatrix::TransformationAddTypeMatrix( + const spvtools::fuzz::protobufs::TransformationAddTypeMatrix& message) + : message_(message) {} + +TransformationAddTypeMatrix::TransformationAddTypeMatrix( + uint32_t fresh_id, uint32_t column_type_id, uint32_t column_count) { + message_.set_fresh_id(fresh_id); + message_.set_column_type_id(column_type_id); + message_.set_column_count(column_count); +} + +bool TransformationAddTypeMatrix::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The column type must be a floating-point vector. + auto column_type = + ir_context->get_type_mgr()->GetType(message_.column_type_id()); + if (!column_type) { + return false; + } + return column_type->AsVector() && + column_type->AsVector()->element_type()->AsFloat(); +} + +void TransformationAddTypeMatrix::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.column_type_id()}}); + in_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.column_count()}}); + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeMatrix, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeMatrix::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_matrix() = message_; + return result; +} + +std::unordered_set TransformationAddTypeMatrix::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_matrix.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_matrix.h new file mode 100644 index 0000000..f1d4165 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_matrix.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeMatrix : public Transformation { + public: + explicit TransformationAddTypeMatrix( + const protobufs::TransformationAddTypeMatrix& message); + + TransformationAddTypeMatrix(uint32_t fresh_id, uint32_t column_type_id, + uint32_t column_count); + + // - |message_.fresh_id| must be a fresh id + // - |message_.column_type_id| must be the id of a floating-point vector type + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeMatrix instruction to the module, with column type + // |message_.column_type_id| and |message_.column_count| columns, with result + // id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeMatrix message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_MATRIX_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp new file mode 100644 index 0000000..f74768d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_pointer.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_pointer.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypePointer::TransformationAddTypePointer( + const spvtools::fuzz::protobufs::TransformationAddTypePointer& message) + : message_(message) {} + +TransformationAddTypePointer::TransformationAddTypePointer( + uint32_t fresh_id, SpvStorageClass storage_class, uint32_t base_type_id) { + message_.set_fresh_id(fresh_id); + message_.set_storage_class(storage_class); + message_.set_base_type_id(base_type_id); +} + +bool TransformationAddTypePointer::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The base type must be known. + return ir_context->get_type_mgr()->GetType(message_.base_type_id()) != + nullptr; +} + +void TransformationAddTypePointer::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Add the pointer type. + opt::Instruction::OperandList in_operands = { + {SPV_OPERAND_TYPE_STORAGE_CLASS, {message_.storage_class()}}, + {SPV_OPERAND_TYPE_ID, {message_.base_type_id()}}}; + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypePointer, 0, message_.fresh_id(), in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypePointer::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_pointer() = message_; + return result; +} + +std::unordered_set TransformationAddTypePointer::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_pointer.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_pointer.h new file mode 100644 index 0000000..3f686e9 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_pointer.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypePointer : public Transformation { + public: + explicit TransformationAddTypePointer( + const protobufs::TransformationAddTypePointer& message); + + TransformationAddTypePointer(uint32_t fresh_id, SpvStorageClass storage_class, + uint32_t base_type_id); + + // - |message_.fresh_id| must not be used by the module + // - |message_.base_type_id| must be the result id of an OpType[...] + // instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypePointer instruction with the given storage class and base + // type to the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypePointer message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_POINTER_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_struct.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_struct.cpp new file mode 100644 index 0000000..b20ffb0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_struct.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_struct.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeStruct::TransformationAddTypeStruct( + const spvtools::fuzz::protobufs::TransformationAddTypeStruct& message) + : message_(message) {} + +TransformationAddTypeStruct::TransformationAddTypeStruct( + uint32_t fresh_id, const std::vector& member_type_ids) { + message_.set_fresh_id(fresh_id); + for (auto member_type_id : member_type_ids) { + message_.add_member_type_id(member_type_id); + } +} + +bool TransformationAddTypeStruct::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // A fresh id is required. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + for (auto member_type : message_.member_type_id()) { + auto type = ir_context->get_type_mgr()->GetType(member_type); + if (!type || type->AsFunction()) { + // The member type id either does not refer to a type, or refers to a + // function type; both are illegal. + return false; + } + + // From the spec for the BuiltIn decoration: + // - When applied to a structure-type member, that structure type cannot + // be contained as a member of another structure type. + if (type->AsStruct() && + fuzzerutil::MembersHaveBuiltInDecoration(ir_context, member_type)) { + return false; + } + } + return true; +} + +void TransformationAddTypeStruct::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + fuzzerutil::AddStructType( + ir_context, message_.fresh_id(), + std::vector(message_.member_type_id().begin(), + message_.member_type_id().end())); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeStruct::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_struct() = message_; + return result; +} + +std::unordered_set TransformationAddTypeStruct::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_struct.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_struct.h new file mode 100644 index 0000000..94be42a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_struct.h @@ -0,0 +1,61 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeStruct : public Transformation { + public: + explicit TransformationAddTypeStruct( + const protobufs::TransformationAddTypeStruct& message); + + TransformationAddTypeStruct(uint32_t fresh_id, + const std::vector& component_type_ids); + + // - |message_.fresh_id| must be a fresh id + // - |message_.member_type_id| must be a sequence of non-function type ids + // - |message_.member_type_id| may not contain a result id of an OpTypeStruct + // instruction with BuiltIn members (i.e. members of the struct are + // decorated via OpMemberDecorate with BuiltIn decoration). + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeStruct instruction whose field types are given by + // |message_.member_type_id|, with result id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeStruct message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_STRUCT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_vector.cpp b/third_party/spirv-tools/source/fuzz/transformation_add_type_vector.cpp new file mode 100644 index 0000000..a3b0010 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_vector.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_vector.h" + +#include "fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationAddTypeVector::TransformationAddTypeVector( + const spvtools::fuzz::protobufs::TransformationAddTypeVector& message) + : message_(message) {} + +TransformationAddTypeVector::TransformationAddTypeVector( + uint32_t fresh_id, uint32_t component_type_id, uint32_t component_count) { + message_.set_fresh_id(fresh_id); + message_.set_component_type_id(component_type_id); + message_.set_component_count(component_count); +} + +bool TransformationAddTypeVector::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + auto component_type = + ir_context->get_type_mgr()->GetType(message_.component_type_id()); + if (!component_type) { + return false; + } + return component_type->AsBool() || component_type->AsFloat() || + component_type->AsInteger(); +} + +void TransformationAddTypeVector::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + fuzzerutil::AddVectorType(ir_context, message_.fresh_id(), + message_.component_type_id(), + message_.component_count()); + // We have added an instruction to the module, so need to be careful about the + // validity of existing analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationAddTypeVector::ToMessage() const { + protobufs::Transformation result; + *result.mutable_add_type_vector() = message_; + return result; +} + +std::unordered_set TransformationAddTypeVector::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_add_type_vector.h b/third_party/spirv-tools/source/fuzz/transformation_add_type_vector.h new file mode 100644 index 0000000..c25d565 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_add_type_vector.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAddTypeVector : public Transformation { + public: + explicit TransformationAddTypeVector( + const protobufs::TransformationAddTypeVector& message); + + TransformationAddTypeVector(uint32_t fresh_id, uint32_t component_type_id, + uint32_t component_count); + + // - |message_.fresh_id| must be a fresh id + // - |message_.component_type_id| must be the id of a scalar type + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpTypeVector instruction to the module, with component type + // |message_.component_type_id| and |message_.component_count| components, + // with result id |message_.fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAddTypeVector message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADD_TYPE_VECTOR_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_adjust_branch_weights.cpp b/third_party/spirv-tools/source/fuzz/transformation_adjust_branch_weights.cpp new file mode 100644 index 0000000..8b74ed3 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_adjust_branch_weights.cpp @@ -0,0 +1,102 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_adjust_branch_weights.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +const uint32_t kBranchWeightForTrueLabelIndex = 3; +const uint32_t kBranchWeightForFalseLabelIndex = 4; + +} // namespace + +TransformationAdjustBranchWeights::TransformationAdjustBranchWeights( + const spvtools::fuzz::protobufs::TransformationAdjustBranchWeights& message) + : message_(message) {} + +TransformationAdjustBranchWeights::TransformationAdjustBranchWeights( + const protobufs::InstructionDescriptor& instruction_descriptor, + const std::pair& branch_weights) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; + message_.mutable_branch_weights()->set_first(branch_weights.first); + message_.mutable_branch_weights()->set_second(branch_weights.second); +} + +bool TransformationAdjustBranchWeights::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (instruction == nullptr) { + return false; + } + + SpvOp opcode = static_cast( + message_.instruction_descriptor().target_instruction_opcode()); + + assert(instruction->opcode() == opcode && + "The located instruction must have the same opcode as in the " + "descriptor."); + + // Must be an OpBranchConditional instruction. + if (opcode != SpvOpBranchConditional) { + return false; + } + + assert((message_.branch_weights().first() != 0 || + message_.branch_weights().second() != 0) && + "At least one weight must be non-zero."); + + assert(message_.branch_weights().first() <= + UINT32_MAX - message_.branch_weights().second() && + "The sum of the two weights must not be greater than UINT32_MAX."); + + return true; +} + +void TransformationAdjustBranchWeights::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (instruction->HasBranchWeights()) { + instruction->SetOperand(kBranchWeightForTrueLabelIndex, + {message_.branch_weights().first()}); + instruction->SetOperand(kBranchWeightForFalseLabelIndex, + {message_.branch_weights().second()}); + } else { + instruction->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, + {message_.branch_weights().first()}}); + instruction->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, + {message_.branch_weights().second()}}); + } +} + +protobufs::Transformation TransformationAdjustBranchWeights::ToMessage() const { + protobufs::Transformation result; + *result.mutable_adjust_branch_weights() = message_; + return result; +} + +std::unordered_set TransformationAdjustBranchWeights::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_adjust_branch_weights.h b/third_party/spirv-tools/source/fuzz/transformation_adjust_branch_weights.h new file mode 100644 index 0000000..4d451a5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_adjust_branch_weights.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_ADJUST_BRANCH_WEIGHTS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_ADJUST_BRANCH_WEIGHTS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationAdjustBranchWeights : public Transformation { + public: + explicit TransformationAdjustBranchWeights( + const protobufs::TransformationAdjustBranchWeights& message); + + TransformationAdjustBranchWeights( + const protobufs::InstructionDescriptor& instruction_descriptor, + const std::pair& branch_weights); + + // - |message_.instruction_descriptor| must identify an existing + // branch conditional instruction + // - At least one of |branch_weights| must be non-zero and + // the two weights must not overflow a 32-bit unsigned integer when added + // together + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adjust the branch weights of a branch conditional instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationAdjustBranchWeights message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_ADJUST_BRANCH_WEIGHTS_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_construct.cpp b/third_party/spirv-tools/source/fuzz/transformation_composite_construct.cpp new file mode 100644 index 0000000..f6711f5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_construct.cpp @@ -0,0 +1,323 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_composite_construct.h" + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/opt/instruction.h" + +namespace spvtools { +namespace fuzz { + +TransformationCompositeConstruct::TransformationCompositeConstruct( + const protobufs::TransformationCompositeConstruct& message) + : message_(message) {} + +TransformationCompositeConstruct::TransformationCompositeConstruct( + uint32_t composite_type_id, std::vector component, + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id) { + message_.set_composite_type_id(composite_type_id); + for (auto a_component : component) { + message_.add_component(a_component); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + message_.set_fresh_id(fresh_id); +} + +bool TransformationCompositeConstruct::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + // We require the id for the composite constructor to be unused. + return false; + } + + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!insert_before) { + // The instruction before which the composite should be inserted was not + // found. + return false; + } + + auto composite_type = + ir_context->get_type_mgr()->GetType(message_.composite_type_id()); + + if (!fuzzerutil::IsCompositeType(composite_type)) { + // The type must actually be a composite. + return false; + } + + // If the type is an array, matrix, struct or vector, the components need to + // be suitable for constructing something of that type. + if (composite_type->AsArray() && + !ComponentsForArrayConstructionAreOK(ir_context, + *composite_type->AsArray())) { + return false; + } + if (composite_type->AsMatrix() && + !ComponentsForMatrixConstructionAreOK(ir_context, + *composite_type->AsMatrix())) { + return false; + } + if (composite_type->AsStruct() && + !ComponentsForStructConstructionAreOK(ir_context, + *composite_type->AsStruct())) { + return false; + } + if (composite_type->AsVector() && + !ComponentsForVectorConstructionAreOK(ir_context, + *composite_type->AsVector())) { + return false; + } + + // Now check whether every component being used to initialize the composite is + // available at the desired program point. + for (auto component : message_.component()) { + auto* inst = ir_context->get_def_use_mgr()->GetDef(component); + if (!inst) { + return false; + } + + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + component)) { + return false; + } + } + + return true; +} + +void TransformationCompositeConstruct::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Use the base and offset information from the transformation to determine + // where in the module a new instruction should be inserted. + auto insert_before_inst = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + auto destination_block = ir_context->get_instr_block(insert_before_inst); + auto insert_before = fuzzerutil::GetIteratorForInstruction( + destination_block, insert_before_inst); + + // Prepare the input operands for an OpCompositeConstruct instruction. + opt::Instruction::OperandList in_operands; + for (auto& component_id : message_.component()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {component_id}}); + } + + // Insert an OpCompositeConstruct instruction. + insert_before.InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, message_.composite_type_id(), + message_.fresh_id(), in_operands)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + AddDataSynonymFacts(ir_context, transformation_context); +} + +bool TransformationCompositeConstruct::ComponentsForArrayConstructionAreOK( + opt::IRContext* ir_context, const opt::analysis::Array& array_type) const { + if (array_type.length_info().words[0] != + opt::analysis::Array::LengthInfo::kConstant) { + // We only handle constant-sized arrays. + return false; + } + if (array_type.length_info().words.size() != 2) { + // We only handle the case where the array size can be captured in a single + // word. + return false; + } + // Get the array size. + auto array_size = array_type.length_info().words[1]; + if (static_cast(message_.component().size()) != array_size) { + // The number of components must match the array size. + return false; + } + // Check that each component is the result id of an instruction whose type is + // the array's element type. + for (auto component_id : message_.component()) { + auto inst = ir_context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != array_type.element_type()) { + // The component's type does not match the array's element type. + return false; + } + } + return true; +} + +bool TransformationCompositeConstruct::ComponentsForMatrixConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Matrix& matrix_type) const { + if (static_cast(message_.component().size()) != + matrix_type.element_count()) { + // The number of components must match the number of columns of the matrix. + return false; + } + // Check that each component is the result id of an instruction whose type is + // the matrix's column type. + for (auto component_id : message_.component()) { + auto inst = ir_context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != matrix_type.element_type()) { + // The component's type does not match the matrix's column type. + return false; + } + } + return true; +} + +bool TransformationCompositeConstruct::ComponentsForStructConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Struct& struct_type) const { + if (static_cast(message_.component().size()) != + struct_type.element_types().size()) { + // The number of components must match the number of fields of the struct. + return false; + } + // Check that each component is the result id of an instruction those type + // matches the associated field type. + for (uint32_t field_index = 0; + field_index < struct_type.element_types().size(); field_index++) { + auto inst = ir_context->get_def_use_mgr()->GetDef( + message_.component()[field_index]); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type != struct_type.element_types()[field_index]) { + // The component's type does not match the corresponding field type. + return false; + } + } + return true; +} + +bool TransformationCompositeConstruct::ComponentsForVectorConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Vector& vector_type) const { + uint32_t base_element_count = 0; + auto element_type = vector_type.element_type(); + for (auto& component_id : message_.component()) { + auto inst = ir_context->get_def_use_mgr()->GetDef(component_id); + if (inst == nullptr || !inst->type_id()) { + // The component does not correspond to an instruction with a result + // type. + return false; + } + auto component_type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(component_type); + if (component_type == element_type) { + base_element_count++; + } else if (component_type->AsVector() && + component_type->AsVector()->element_type() == element_type) { + base_element_count += component_type->AsVector()->element_count(); + } else { + // The component was not appropriate; e.g. no type corresponding to the + // given id was found, or the type that was found was not compatible + // with the vector being constructed. + return false; + } + } + // The number of components provided (when vector components are flattened + // out) needs to match the length of the vector being constructed. + return base_element_count == vector_type.element_count(); +} + +protobufs::Transformation TransformationCompositeConstruct::ToMessage() const { + protobufs::Transformation result; + *result.mutable_composite_construct() = message_; + return result; +} + +std::unordered_set TransformationCompositeConstruct::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +void TransformationCompositeConstruct::AddDataSynonymFacts( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // If the result id of the composite we are constructing is irrelevant (e.g. + // because it is in a dead block) then we do not make any synonyms. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + return; + } + + // Inform the fact manager that we now have new synonyms: every component of + // the composite is synonymous with the id used to construct that component + // (so long as it is legitimate to create a synonym from that id), except in + // the case of a vector where a single vector id can span multiple components. + auto composite_type = + ir_context->get_type_mgr()->GetType(message_.composite_type_id()); + uint32_t index = 0; + for (auto component : message_.component()) { + if (!fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + ir_context->get_def_use_mgr()->GetDef(component))) { + index++; + continue; + } + auto component_type = ir_context->get_type_mgr()->GetType( + ir_context->get_def_use_mgr()->GetDef(component)->type_id()); + if (composite_type->AsVector() && component_type->AsVector()) { + // The case where the composite being constructed is a vector and the + // component provided for construction is also a vector is special. It + // requires adding a synonym fact relating each element of the sub-vector + // to the corresponding element of the composite being constructed. + assert(component_type->AsVector()->element_type() == + composite_type->AsVector()->element_type()); + assert(component_type->AsVector()->element_count() < + composite_type->AsVector()->element_count()); + for (uint32_t subvector_index = 0; + subvector_index < component_type->AsVector()->element_count(); + subvector_index++) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(component, {subvector_index}), + MakeDataDescriptor(message_.fresh_id(), {index})); + index++; + } + } else { + // The other cases are simple: the component is made directly synonymous + // with the element of the composite being constructed. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(component, {}), + MakeDataDescriptor(message_.fresh_id(), {index})); + index++; + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_construct.h b/third_party/spirv-tools/source/fuzz/transformation_composite_construct.h new file mode 100644 index 0000000..3a3e43c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_construct.h @@ -0,0 +1,104 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationCompositeConstruct : public Transformation { + public: + explicit TransformationCompositeConstruct( + const protobufs::TransformationCompositeConstruct& message); + + TransformationCompositeConstruct( + uint32_t composite_type_id, std::vector component, + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id); + + // - |message_.fresh_id| must not be used by the module. + // - |message_.composite_type_id| must be the id of a composite type + // - The elements of |message_.component| must be result ids that are + // suitable for constructing an element of the given composite type, in + // order + // - The elements of |message_.component| must not be the target of any + // decorations. + // - |message_.base_instruction_id| must be the result id of an instruction + // 'base' in some block 'blk'. + // - 'blk' must contain an instruction 'inst' located |message_.offset| + // instructions after 'base' (if |message_.offset| = 0 then 'inst' = + // 'base'). + // - It must be legal to insert an OpCompositeConstruct instruction directly + // before 'inst'. + // - Each element of |message_.component| must be available directly before + // 'inst'. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts a new OpCompositeConstruct instruction, with id + // |message_.fresh_id|, directly before the instruction identified by + // |message_.base_instruction_id| and |message_.offset|. The instruction + // creates a composite of type |message_.composite_type_id| using the ids of + // |message_.component|. + // + // Synonym facts are added between the elements of the resulting composite + // and the components used to construct it, as long as the associated ids + // support synonym creation. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Helper to decide whether the components of the transformation are suitable + // for constructing an array of the given type. + bool ComponentsForArrayConstructionAreOK( + opt::IRContext* ir_context, const opt::analysis::Array& array_type) const; + + // Similar, but for matrices. + bool ComponentsForMatrixConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Matrix& matrix_type) const; + + // Similar, but for structs. + bool ComponentsForStructConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Struct& struct_type) const; + + // Similar, but for vectors. + bool ComponentsForVectorConstructionAreOK( + opt::IRContext* ir_context, + const opt::analysis::Vector& vector_type) const; + + // Helper method for adding data synonym facts when applying the + // transformation to |ir_context| and |transformation_context|. + void AddDataSynonymFacts(opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + protobufs::TransformationCompositeConstruct message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_CONSTRUCT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_extract.cpp b/third_party/spirv-tools/source/fuzz/transformation_composite_extract.cpp new file mode 100644 index 0000000..2aff02f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_extract.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_composite_extract.h" + +#include + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationCompositeExtract::TransformationCompositeExtract( + const spvtools::fuzz::protobufs::TransformationCompositeExtract& message) + : message_(message) {} + +TransformationCompositeExtract::TransformationCompositeExtract( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t composite_id, + const std::vector& index) { + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + message_.set_fresh_id(fresh_id); + message_.set_composite_id(composite_id); + for (auto an_index : index) { + message_.add_index(an_index); + } +} + +bool TransformationCompositeExtract::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!instruction_to_insert_before) { + return false; + } + auto composite_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()); + if (!composite_instruction) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.composite_id())) { + return false; + } + + auto composite_type = + ir_context->get_type_mgr()->GetType(composite_instruction->type_id()); + if (!fuzzerutil::IsCompositeType(composite_type)) { + return false; + } + + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpCompositeExtract, instruction_to_insert_before)) { + return false; + } + + return fuzzerutil::WalkCompositeTypeIndices(ir_context, + composite_instruction->type_id(), + message_.index()) != 0; +} + +void TransformationCompositeExtract::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + opt::Instruction::OperandList extract_operands; + extract_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}}); + for (auto an_index : message_.index()) { + extract_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {an_index}}); + } + auto composite_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()); + auto extracted_type = fuzzerutil::WalkCompositeTypeIndices( + ir_context, composite_instruction->type_id(), message_.index()); + + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, extracted_type, + message_.fresh_id(), extract_operands)); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + AddDataSynonymFacts(ir_context, transformation_context); +} + +protobufs::Transformation TransformationCompositeExtract::ToMessage() const { + protobufs::Transformation result; + *result.mutable_composite_extract() = message_; + return result; +} + +std::unordered_set TransformationCompositeExtract::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +void TransformationCompositeExtract::AddDataSynonymFacts( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Don't add synonyms if the composite being extracted from is not suitable, + // or if the result id into which we are extracting is irrelevant. + if (!fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + ir_context->get_def_use_mgr()->GetDef(message_.composite_id())) || + transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + return; + } + + // Add the fact that the id storing the extracted element is synonymous with + // the index into the structure. + std::vector indices(message_.index().begin(), + message_.index().end()); + auto data_descriptor_for_extracted_element = + MakeDataDescriptor(message_.composite_id(), indices); + auto data_descriptor_for_result_id = + MakeDataDescriptor(message_.fresh_id(), {}); + transformation_context->GetFactManager()->AddFactDataSynonym( + data_descriptor_for_extracted_element, data_descriptor_for_result_id); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_extract.h b/third_party/spirv-tools/source/fuzz/transformation_composite_extract.h new file mode 100644 index 0000000..0f5348a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_extract.h @@ -0,0 +1,76 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationCompositeExtract : public Transformation { + public: + explicit TransformationCompositeExtract( + const protobufs::TransformationCompositeExtract& message); + + TransformationCompositeExtract( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t composite_id, + const std::vector& index); + + // - |message_.fresh_id| must be available + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is valid to place an OpCompositeExtract + // - |message_.composite_id| must be the id of an instruction that defines + // a composite object, and this id must be available at the instruction + // identified by |message_.instruction_to_insert_before| + // - |message_.index| must be a suitable set of indices for + // |message_.composite_id|, i.e. it must be possible to follow this chain + // of indices to reach a sub-object of |message_.composite_id| + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an OpCompositeConstruct instruction before the instruction identified + // by |message_.instruction_to_insert_before|, that extracts from + // |message_.composite_id| via indices |message_.index| into + // |message_.fresh_id|. + // + // Adds a synonym fact associating |message_.fresh_id| with the relevant + // element of |message_.composite_id|, as long as these ids support synonym + // creation. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Helper method for adding data synonym facts when applying the + // transformation to |ir_context| and |transformation_context|. + void AddDataSynonymFacts(opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + protobufs::TransformationCompositeExtract message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_EXTRACT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_insert.cpp b/third_party/spirv-tools/source/fuzz/transformation_composite_insert.cpp new file mode 100644 index 0000000..cc68141 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_insert.cpp @@ -0,0 +1,241 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_composite_insert.h" + +#include "source/fuzz/fuzzer_pass_add_composite_inserts.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationCompositeInsert::TransformationCompositeInsert( + const spvtools::fuzz::protobufs::TransformationCompositeInsert& message) + : message_(message) {} + +TransformationCompositeInsert::TransformationCompositeInsert( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t composite_id, uint32_t object_id, + const std::vector& index) { + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + message_.set_fresh_id(fresh_id); + message_.set_composite_id(composite_id); + message_.set_object_id(object_id); + for (auto an_index : index) { + message_.add_index(an_index); + } +} + +bool TransformationCompositeInsert::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // |message_.composite_id| must refer to an existing composite value. + auto composite = + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()); + + if (!IsCompositeInstructionSupported(ir_context, composite)) { + return false; + } + + // The indices in |message_.index| must be suitable for indexing into + // |composite->type_id()|. + auto component_to_be_replaced_type_id = fuzzerutil::WalkCompositeTypeIndices( + ir_context, composite->type_id(), message_.index()); + if (component_to_be_replaced_type_id == 0) { + return false; + } + + // The instruction having the id of |message_.object_id| must be defined. + auto object_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.object_id()); + if (object_instruction == nullptr || object_instruction->type_id() == 0) { + return false; + } + + // We ignore pointers for now. + auto object_instruction_type = + ir_context->get_type_mgr()->GetType(object_instruction->type_id()); + if (object_instruction_type->AsPointer() != nullptr) { + return false; + } + + // The type id of the object having |message_.object_id| and the type id of + // the component of the composite at index |message_.index| must be the same. + if (component_to_be_replaced_type_id != object_instruction->type_id()) { + return false; + } + + // |message_.instruction_to_insert_before| must be a defined instruction. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (instruction_to_insert_before == nullptr) { + return false; + } + + // |message_.composite_id| and |message_.object_id| must be available before + // the |message_.instruction_to_insert_before|. + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.composite_id())) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.object_id())) { + return false; + } + + // It must be possible to insert an OpCompositeInsert before this + // instruction. + return fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpCompositeInsert, instruction_to_insert_before); +} + +void TransformationCompositeInsert::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // |message_.struct_fresh_id| must be fresh. + assert(fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && + "|message_.fresh_id| must be fresh"); + + std::vector index = + fuzzerutil::RepeatedFieldToVector(message_.index()); + opt::Instruction::OperandList in_operands; + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.object_id()}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {message_.composite_id()}}); + for (auto i : index) { + in_operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}); + } + auto composite_type_id = + fuzzerutil::GetTypeId(ir_context, message_.composite_id()); + + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeInsert, composite_type_id, + message_.fresh_id(), std::move(in_operands))); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // We have modified the module so most analyzes are now invalid. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Add data synonym facts that arise from the insertion. + AddDataSynonymFacts(ir_context, transformation_context); +} + +protobufs::Transformation TransformationCompositeInsert::ToMessage() const { + protobufs::Transformation result; + *result.mutable_composite_insert() = message_; + return result; +} + +bool TransformationCompositeInsert::IsCompositeInstructionSupported( + opt::IRContext* ir_context, opt::Instruction* instruction) { + if (instruction == nullptr) { + return false; + } + if (instruction->result_id() == 0 || instruction->type_id() == 0) { + return false; + } + auto composite_type = + ir_context->get_type_mgr()->GetType(instruction->type_id()); + if (!fuzzerutil::IsCompositeType(composite_type)) { + return false; + } + + // Empty composites are not supported. + auto instruction_type_inst = + ir_context->get_def_use_mgr()->GetDef(instruction->type_id()); + if (fuzzerutil::GetBoundForCompositeIndex(*instruction_type_inst, + ir_context) == 0) { + return false; + } + return true; +} + +std::unordered_set TransformationCompositeInsert::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +void TransformationCompositeInsert::AddDataSynonymFacts( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // If the result id arising from the insertion is irrelevant then do not add + // any data synonym facts. (The result id can be irrelevant if the insertion + // occurs in a dead block.) + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + return; + } + + // So long as the |message_.composite_id| is suitable for participating in + // synonyms, every every element of the insertion result except for at the + // index being inserted into is synonymous with the corresponding element of + // |message_.composite_id|. In that case, for every index that is a prefix of + // |index|, the components different from the one that contains the inserted + // object are synonymous with corresponding elements in the original + // composite. + uint32_t current_node_type_id = + fuzzerutil::GetTypeId(ir_context, message_.composite_id()); + std::vector current_index; + + std::vector index = + fuzzerutil::RepeatedFieldToVector(message_.index()); + + for (uint32_t current_level : index) { + auto current_node_type_inst = + ir_context->get_def_use_mgr()->GetDef(current_node_type_id); + uint32_t index_to_skip = current_level; + uint32_t num_of_components = fuzzerutil::GetBoundForCompositeIndex( + *current_node_type_inst, ir_context); + + // Update the current_node_type_id. + current_node_type_id = fuzzerutil::WalkOneCompositeTypeIndex( + ir_context, current_node_type_id, index_to_skip); + + for (uint32_t i = 0; i < num_of_components; i++) { + if (i == index_to_skip) { + continue; + } + current_index.push_back(i); + if (fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + ir_context->get_def_use_mgr()->GetDef(message_.composite_id()))) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.fresh_id(), current_index), + MakeDataDescriptor(message_.composite_id(), current_index)); + } + current_index.pop_back(); + } + // Store the prefix of the |index|. + current_index.push_back(current_level); + } + // If the object being inserted supports synonym creation then it is + // synonymous with the result of the insert instruction at the given index. + if (fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + ir_context->get_def_use_mgr()->GetDef(message_.object_id()))) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.object_id(), {}), + MakeDataDescriptor(message_.fresh_id(), index)); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_composite_insert.h b/third_party/spirv-tools/source/fuzz/transformation_composite_insert.h new file mode 100644 index 0000000..f229014 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_composite_insert.h @@ -0,0 +1,79 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_INSERT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_INSERT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationCompositeInsert : public Transformation { + public: + explicit TransformationCompositeInsert( + const protobufs::TransformationCompositeInsert& message); + + TransformationCompositeInsert( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t composite_id, uint32_t object_id, + const std::vector& index); + + // - |message_.fresh_id| must be fresh. + // - |message_.composite_id| must refer to an existing composite value. + // - |message_.index| must refer to a correct index in the composite. + // - The type id of the object and the type id of the component of the + // composite at index |message_.index| must be the same. + // - |message_.instruction_to_insert_before| must refer to a defined + // instruction. + // - It must be possible to insert OpCompositeInsert before + // |instruction_to_insert_before|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction OpCompositeInsert before + // |instruction_to_insert_before|, which creates a new composite from + // |composite_id| by inserting |object_id| at the specified |index|. + // Synonyms are created between those components which are identical in the + // original and the modified composite and between the inserted object and its + // copy in the modified composite. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Checks if |instruction| is a instruction of a composite type supported by + // this transformation. + static bool IsCompositeInstructionSupported(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + // Helper method for adding data synonym facts when applying the + // transformation to |ir_context| and |transformation_context|. + void AddDataSynonymFacts(opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + protobufs::TransformationCompositeInsert message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_COMPOSITE_INSERT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp b/third_party/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp new file mode 100644 index 0000000..f727052 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h" + +namespace spvtools { +namespace fuzz { + +TransformationComputeDataSynonymFactClosure:: + TransformationComputeDataSynonymFactClosure( + const spvtools::fuzz::protobufs:: + TransformationComputeDataSynonymFactClosure& message) + : message_(message) {} + +TransformationComputeDataSynonymFactClosure:: + TransformationComputeDataSynonymFactClosure( + uint32_t maximum_equivalence_class_size) { + message_.set_maximum_equivalence_class_size(maximum_equivalence_class_size); +} + +bool TransformationComputeDataSynonymFactClosure::IsApplicable( + opt::IRContext* /*unused*/, const TransformationContext& /*unused*/) const { + return true; +} + +void TransformationComputeDataSynonymFactClosure::Apply( + opt::IRContext* /*unused*/, + TransformationContext* transformation_context) const { + transformation_context->GetFactManager()->ComputeClosureOfFacts( + message_.maximum_equivalence_class_size()); +} + +protobufs::Transformation +TransformationComputeDataSynonymFactClosure::ToMessage() const { + protobufs::Transformation result; + *result.mutable_compute_data_synonym_fact_closure() = message_; + return result; +} + +std::unordered_set +TransformationComputeDataSynonymFactClosure::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h b/third_party/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h new file mode 100644 index 0000000..dedabe2 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_compute_data_synonym_fact_closure.h @@ -0,0 +1,55 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationComputeDataSynonymFactClosure : public Transformation { + public: + explicit TransformationComputeDataSynonymFactClosure( + const protobufs::TransformationComputeDataSynonymFactClosure& message); + + explicit TransformationComputeDataSynonymFactClosure( + uint32_t maximum_equivalence_class_size); + + // This transformation is trivially applicable. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Forces the fact manager to compute a closure of data synonym facts, so that + // facts implied by existing facts are deduced. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationComputeDataSynonymFactClosure message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_COMPUTE_DATA_SYNONYM_FACT_CLOSURE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_context.cpp b/third_party/spirv-tools/source/fuzz/transformation_context.cpp new file mode 100644 index 0000000..d008ba9 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_context.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_context.h" + +#include + +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { +namespace { + +// An overflow id source that should never be used: its methods assert false. +// This is the right id source for use during fuzzing, when overflow ids should +// never be required. +class NullOverflowIdSource : public OverflowIdSource { + bool HasOverflowIds() const override { + assert(false && "Bad attempt to query whether overflow ids are available."); + return false; + } + + uint32_t GetNextOverflowId() override { + assert(false && "Bad attempt to request an overflow id."); + return 0; + } + + const std::unordered_set& GetIssuedOverflowIds() const override { + assert(false && "Operation not supported."); + return placeholder_; + } + + private: + std::unordered_set placeholder_; +}; + +} // namespace + +TransformationContext::TransformationContext( + std::unique_ptr fact_manager, + spv_validator_options validator_options) + : fact_manager_(std::move(fact_manager)), + validator_options_(validator_options), + overflow_id_source_(MakeUnique()) {} + +TransformationContext::TransformationContext( + std::unique_ptr fact_manager, + spv_validator_options validator_options, + std::unique_ptr overflow_id_source) + : fact_manager_(std::move(fact_manager)), + validator_options_(validator_options), + overflow_id_source_(std::move(overflow_id_source)) {} + +TransformationContext::~TransformationContext() = default; + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_context.h b/third_party/spirv-tools/source/fuzz/transformation_context.h new file mode 100644 index 0000000..3edcb63 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_context.h @@ -0,0 +1,74 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ + +#include + +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/overflow_id_source.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace fuzz { + +// Encapsulates all information that is required to inform how to apply a +// transformation to a module. +class TransformationContext { + public: + // Constructs a transformation context with a given fact manager and validator + // options. Overflow ids are not available from a transformation context + // constructed in this way. + TransformationContext(std::unique_ptr, + spv_validator_options validator_options); + + // Constructs a transformation context with a given fact manager, validator + // options and overflow id source. + TransformationContext(std::unique_ptr, + spv_validator_options validator_options, + std::unique_ptr overflow_id_source); + + ~TransformationContext(); + + FactManager* GetFactManager() { return fact_manager_.get(); } + + const FactManager* GetFactManager() const { return fact_manager_.get(); } + + OverflowIdSource* GetOverflowIdSource() { return overflow_id_source_.get(); } + + const OverflowIdSource* GetOverflowIdSource() const { + return overflow_id_source_.get(); + } + + spv_validator_options GetValidatorOptions() const { + return validator_options_; + } + + private: + // Manages facts that inform whether transformations can be applied, and that + // are produced by applying transformations. + std::unique_ptr fact_manager_; + + // Options to control validation when deciding whether transformations can be + // applied. + spv_validator_options validator_options_; + + std::unique_ptr overflow_id_source_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_CONTEXT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp b/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp new file mode 100644 index 0000000..07758cd --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.cpp @@ -0,0 +1,682 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_duplicate_region_with_selection.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationDuplicateRegionWithSelection:: + TransformationDuplicateRegionWithSelection( + const spvtools::fuzz::protobufs:: + TransformationDuplicateRegionWithSelection& message) + : message_(message) {} + +TransformationDuplicateRegionWithSelection:: + TransformationDuplicateRegionWithSelection( + uint32_t new_entry_fresh_id, uint32_t condition_id, + uint32_t merge_label_fresh_id, uint32_t entry_block_id, + uint32_t exit_block_id, + const std::map& original_label_to_duplicate_label, + const std::map& original_id_to_duplicate_id, + const std::map& original_id_to_phi_id) { + message_.set_new_entry_fresh_id(new_entry_fresh_id); + message_.set_condition_id(condition_id); + message_.set_merge_label_fresh_id(merge_label_fresh_id); + message_.set_entry_block_id(entry_block_id); + message_.set_exit_block_id(exit_block_id); + *message_.mutable_original_label_to_duplicate_label() = + fuzzerutil::MapToRepeatedUInt32Pair(original_label_to_duplicate_label); + *message_.mutable_original_id_to_duplicate_id() = + fuzzerutil::MapToRepeatedUInt32Pair(original_id_to_duplicate_id); + *message_.mutable_original_id_to_phi_id() = + fuzzerutil::MapToRepeatedUInt32Pair(original_id_to_phi_id); +} + +bool TransformationDuplicateRegionWithSelection::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Instruction with the id |condition_id| must exist and must be of a bool + // type. + auto bool_instr = + ir_context->get_def_use_mgr()->GetDef(message_.condition_id()); + if (bool_instr == nullptr || !bool_instr->type_id()) { + return false; + } + if (!ir_context->get_type_mgr()->GetType(bool_instr->type_id())->AsBool()) { + return false; + } + + // The |new_entry_fresh_id| must be fresh and distinct. + std::set ids_used_by_this_transformation; + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_entry_fresh_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + // The |merge_label_fresh_id| must be fresh and distinct. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.merge_label_fresh_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + // The entry and exit block ids must refer to blocks. + for (auto block_id : {message_.entry_block_id(), message_.exit_block_id()}) { + auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id); + if (!block_label || block_label->opcode() != SpvOpLabel) { + return false; + } + } + auto entry_block = ir_context->cfg()->block(message_.entry_block_id()); + auto exit_block = ir_context->cfg()->block(message_.exit_block_id()); + + // The |entry_block| and the |exit_block| must be in the same function. + if (entry_block->GetParent() != exit_block->GetParent()) { + return false; + } + + // The |entry_block| must dominate the |exit_block|. + auto dominator_analysis = + ir_context->GetDominatorAnalysis(entry_block->GetParent()); + if (!dominator_analysis->Dominates(entry_block, exit_block)) { + return false; + } + + // The |exit_block| must post-dominate the |entry_block|. + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(entry_block->GetParent()); + if (!postdominator_analysis->Dominates(exit_block, entry_block)) { + return false; + } + + auto enclosing_function = entry_block->GetParent(); + + // |entry_block| cannot be the first block of the |enclosing_function|. + if (&*enclosing_function->begin() == entry_block) { + return false; + } + + // To make the process of resolving OpPhi instructions easier, we require that + // the entry block has only one predecessor. + auto entry_block_preds = ir_context->cfg()->preds(entry_block->id()); + std::sort(entry_block_preds.begin(), entry_block_preds.end()); + entry_block_preds.erase( + std::unique(entry_block_preds.begin(), entry_block_preds.end()), + entry_block_preds.end()); + if (entry_block_preds.size() > 1) { + return false; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785): + // The following code has been copied from TransformationOutlineFunction. + // Consider refactoring to avoid duplication. + auto region_set = GetRegionBlocks(ir_context, entry_block, exit_block); + + // Check whether |region_set| really is a single-entry single-exit region, and + // also check whether structured control flow constructs and their merge + // and continue constructs are either wholly in or wholly out of the region - + // e.g. avoid the situation where the region contains the head of a loop but + // not the loop's continue construct. + // + // This is achieved by going through every block in the |enclosing_function| + for (auto& block : *enclosing_function) { + if (&block == exit_block) { + // It is not OK for the exit block to head a loop construct or a + // conditional construct. + if (block.GetMergeInst()) { + return false; + } + continue; + } + if (region_set.count(&block) != 0) { + // The block is in the region and is not the region's exit block. Let's + // see whether all of the block's successors are in the region. If they + // are not, the region is not single-entry single-exit. + bool all_successors_in_region = true; + block.WhileEachSuccessorLabel([&all_successors_in_region, ir_context, + ®ion_set](uint32_t successor) -> bool { + if (region_set.count(ir_context->cfg()->block(successor)) == 0) { + all_successors_in_region = false; + return false; + } + return true; + }); + if (!all_successors_in_region) { + return false; + } + } + + if (auto merge = block.GetMergeInst()) { + // The block is a loop or selection header. The header and its + // associated merge block must be both in the region or both be + // outside the region. + auto merge_block = + ir_context->cfg()->block(merge->GetSingleWordOperand(0)); + if (region_set.count(&block) != region_set.count(merge_block)) { + return false; + } + } + + if (auto loop_merge = block.GetLoopMergeInst()) { + // The continue target of a loop must be within the region if and only if + // the header of the loop is. + auto continue_target = + ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1)); + // The continue target is a single-entry, single-exit region. Therefore, + // if the continue target is the exit block, the region might not contain + // the loop header. However, we would like to exclude this situation, + // since it would be impossible for the modified exit block to branch to + // the new selection merge block. In this scenario the exit block is + // required to branch to the loop header. + if (region_set.count(&block) != region_set.count(continue_target)) { + return false; + } + } + } + + // Get the maps from the protobuf. + std::map original_label_to_duplicate_label = + fuzzerutil::RepeatedUInt32PairToMap( + message_.original_label_to_duplicate_label()); + + std::map original_id_to_duplicate_id = + fuzzerutil::RepeatedUInt32PairToMap( + message_.original_id_to_duplicate_id()); + + std::map original_id_to_phi_id = + fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id()); + + for (auto block : region_set) { + // The label of every block in the region must be present in the map + // |original_label_to_duplicate_label|, unless overflow ids are present. + if (original_label_to_duplicate_label.count(block->id()) == 0) { + if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } else { + auto duplicate_label = original_label_to_duplicate_label[block->id()]; + // Each id assigned to labels in the region must be distinct and fresh. + if (!duplicate_label || + !CheckIdIsFreshAndNotUsedByThisTransformation( + duplicate_label, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + for (auto instr : *block) { + if (!instr.HasResultId()) { + continue; + } + // Every instruction with a result id in the region must be present in the + // map |original_id_to_duplicate_id|, unless overflow ids are present. + if (original_id_to_duplicate_id.count(instr.result_id()) == 0) { + if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } else { + auto duplicate_id = original_id_to_duplicate_id[instr.result_id()]; + // Id assigned to this result id in the region must be distinct and + // fresh. + if (!duplicate_id || + !CheckIdIsFreshAndNotUsedByThisTransformation( + duplicate_id, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + if (&instr == &*exit_block->tail() || + fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, &*exit_block->tail(), instr.result_id())) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3787): + // Consider not adding OpPhi instructions for the pointers and + // sampled images which are unused after the region, so that the + // transformation could be still applicable. + + // Using pointers with OpPhi requires capability VariablePointers. + if (ir_context->get_def_use_mgr()->GetDef(instr.type_id())->opcode() == + SpvOpTypePointer && + !ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers)) { + return false; + } + + // OpTypeSampledImage cannot be the result type of an OpPhi instruction. + if (ir_context->get_def_use_mgr()->GetDef(instr.type_id())->opcode() == + SpvOpTypeSampledImage) { + return false; + } + + // Every instruction with a result id available at the end of the region + // must be present in the map |original_id_to_phi_id|, unless overflow + // ids are present. + if (original_id_to_phi_id.count(instr.result_id()) == 0) { + if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } else { + auto phi_id = original_id_to_phi_id[instr.result_id()]; + // Id assigned to this result id in the region must be distinct and + // fresh. + if (!phi_id || + !CheckIdIsFreshAndNotUsedByThisTransformation( + phi_id, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + } + } + } + return true; +} + +void TransformationDuplicateRegionWithSelection::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_entry_fresh_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.merge_label_fresh_id()); + + // Create the new entry block containing the main conditional instruction. Set + // its parent to the parent of the original entry block, since it is located + // in the same function. + std::unique_ptr new_entry_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.new_entry_fresh_id(), + opt::Instruction::OperandList())); + auto entry_block = ir_context->cfg()->block(message_.entry_block_id()); + auto enclosing_function = entry_block->GetParent(); + auto exit_block = ir_context->cfg()->block(message_.exit_block_id()); + + // Get the blocks contained in the region. + std::set region_blocks = + GetRegionBlocks(ir_context, entry_block, exit_block); + + // Construct the merge block. + std::unique_ptr merge_block = + MakeUnique(MakeUnique(opt::Instruction( + ir_context, SpvOpLabel, 0, message_.merge_label_fresh_id(), + opt::Instruction::OperandList()))); + + // Get the maps from the protobuf. + std::map original_label_to_duplicate_label = + fuzzerutil::RepeatedUInt32PairToMap( + message_.original_label_to_duplicate_label()); + + std::map original_id_to_duplicate_id = + fuzzerutil::RepeatedUInt32PairToMap( + message_.original_id_to_duplicate_id()); + + std::map original_id_to_phi_id = + fuzzerutil::RepeatedUInt32PairToMap(message_.original_id_to_phi_id()); + + // Use oveflow ids to fill in any required ids that are missing from these + // maps. + for (auto block : region_blocks) { + if (original_label_to_duplicate_label.count(block->id()) == 0) { + original_label_to_duplicate_label.insert( + {block->id(), + transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); + } + for (auto instr : *block) { + if (!instr.HasResultId()) { + continue; + } + if (original_id_to_duplicate_id.count(instr.result_id()) == 0) { + original_id_to_duplicate_id.insert( + {instr.result_id(), transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()}); + } + if (&instr == &*exit_block->tail() || + fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, &*exit_block->tail(), instr.result_id())) { + if (original_id_to_phi_id.count(instr.result_id()) == 0) { + original_id_to_phi_id.insert( + {instr.result_id(), transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()}); + } + } + } + } + + // Before adding duplicate blocks, we need to update the OpPhi instructions in + // the successors of the |exit_block|. We know that the execution of the + // transformed region will end in |merge_block|. Hence, we need to change all + // occurrences of the label id of the |exit_block| to the label id of the + // |merge_block|. + exit_block->ForEachSuccessorLabel([this, ir_context](uint32_t label_id) { + auto block = ir_context->cfg()->block(label_id); + for (auto& instr : *block) { + if (instr.opcode() == SpvOpPhi) { + instr.ForEachId([this](uint32_t* id) { + if (*id == message_.exit_block_id()) { + *id = message_.merge_label_fresh_id(); + } + }); + } + } + }); + + // Get vector of predecessors id of |entry_block|. Remove any duplicate + // values. + auto entry_block_preds = ir_context->cfg()->preds(entry_block->id()); + std::sort(entry_block_preds.begin(), entry_block_preds.end()); + entry_block_preds.erase( + unique(entry_block_preds.begin(), entry_block_preds.end()), + entry_block_preds.end()); + // We know that |entry_block| has only one predecessor, since the region is + // single-entry, single-exit and its constructs and their merge blocks must be + // either wholly within or wholly outside of the region. + assert(entry_block_preds.size() == 1 && + "The entry of the region to be duplicated can have only one " + "predecessor."); + uint32_t entry_block_pred_id = + ir_context->get_instr_block(entry_block_preds[0])->id(); + // Update all the OpPhi instructions in the |entry_block|. Change every + // occurrence of |entry_block_pred_id| to the id of |new_entry|, because we + // will insert |new_entry| before |entry_block|. + for (auto& instr : *entry_block) { + if (instr.opcode() == SpvOpPhi) { + instr.ForEachId([this, entry_block_pred_id](uint32_t* id) { + if (*id == entry_block_pred_id) { + *id = message_.new_entry_fresh_id(); + } + }); + } + } + + // Duplication of blocks will invalidate iterators. Store all the blocks from + // the enclosing function. + std::vector blocks; + for (auto& block : *enclosing_function) { + blocks.push_back(&block); + } + + opt::BasicBlock* previous_block = nullptr; + opt::BasicBlock* duplicated_exit_block = nullptr; + // Iterate over all blocks of the function to duplicate blocks of the original + // region and their instructions. + for (auto& block : blocks) { + // The block must be contained in the region. + if (region_blocks.count(block) == 0) { + continue; + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, original_label_to_duplicate_label[block->id()]); + + std::unique_ptr duplicated_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, + original_label_to_duplicate_label[block->id()], + opt::Instruction::OperandList())); + + for (auto& instr : *block) { + // Case where an instruction is the terminator of the exit block is + // handled separately. + if (block == exit_block && instr.IsBlockTerminator()) { + switch (instr.opcode()) { + case SpvOpBranch: + case SpvOpBranchConditional: + case SpvOpReturn: + case SpvOpReturnValue: + case SpvOpUnreachable: + case SpvOpKill: + continue; + default: + assert(false && + "Unexpected terminator for |exit_block| of the region."); + } + } + // Duplicate the instruction. + auto cloned_instr = instr.Clone(ir_context); + duplicated_block->AddInstruction( + std::unique_ptr(cloned_instr)); + + fuzzerutil::UpdateModuleIdBound( + ir_context, original_id_to_duplicate_id[instr.result_id()]); + + // If an id from the original region was used in this instruction, + // replace it with the value from |original_id_to_duplicate_id|. + // If a label from the original region was used in this instruction, + // replace it with the value from |original_label_to_duplicate_label|. + cloned_instr->ForEachId( + [original_id_to_duplicate_id, + original_label_to_duplicate_label](uint32_t* op) { + if (original_id_to_duplicate_id.count(*op) != 0) { + *op = original_id_to_duplicate_id.at(*op); + } + if (original_label_to_duplicate_label.count(*op) != 0) { + *op = original_label_to_duplicate_label.at(*op); + } + }); + } + + // If the block is the first duplicated block, insert it after the exit + // block of the original region. Otherwise, insert it after the preceding + // one. + auto duplicated_block_ptr = duplicated_block.get(); + if (previous_block) { + enclosing_function->InsertBasicBlockAfter(std::move(duplicated_block), + previous_block); + } else { + enclosing_function->InsertBasicBlockAfter(std::move(duplicated_block), + exit_block); + } + previous_block = duplicated_block_ptr; + if (block == exit_block) { + // After execution of the loop, this variable stores a pointer to the last + // duplicated block. + duplicated_exit_block = duplicated_block_ptr; + } + } + + for (auto& block : region_blocks) { + for (auto& instr : *block) { + if (instr.result_id() != 0 && + (&instr == &*exit_block->tail() || + fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, &*exit_block->tail(), instr.result_id()))) { + // Add the OpPhi instruction for every result id that is + // available at the end of the region (the last instruction + // of the |exit_block|) + merge_block->AddInstruction(MakeUnique( + ir_context, SpvOpPhi, instr.type_id(), + original_id_to_phi_id[instr.result_id()], + opt::Instruction::OperandList({ + {SPV_OPERAND_TYPE_ID, {instr.result_id()}}, + {SPV_OPERAND_TYPE_ID, {exit_block->id()}}, + {SPV_OPERAND_TYPE_ID, + {original_id_to_duplicate_id[instr.result_id()]}}, + {SPV_OPERAND_TYPE_ID, {duplicated_exit_block->id()}}, + }))); + + fuzzerutil::UpdateModuleIdBound( + ir_context, original_id_to_phi_id[instr.result_id()]); + + // If the instruction has been remapped by an OpPhi, look + // for all its uses outside of the region and outside of the + // merge block (to not overwrite just added instructions in + // the merge block) and replace the original instruction id + // with the id of the corresponding OpPhi instruction. + ir_context->get_def_use_mgr()->ForEachUse( + &instr, + [ir_context, &instr, region_blocks, original_id_to_phi_id, + &merge_block](opt::Instruction* user, uint32_t operand_index) { + auto user_block = ir_context->get_instr_block(user); + if ((region_blocks.find(user_block) != region_blocks.end()) || + user_block == merge_block.get()) { + return; + } + user->SetOperand(operand_index, + {original_id_to_phi_id.at(instr.result_id())}); + }); + } + } + } + + // Construct a conditional instruction in the |new_entry_block|. + // If the condition is true, the execution proceeds in the + // |entry_block| of the original region. If the condition is + // false, the execution proceeds in the first block of the + // duplicated region. + new_entry_block->AddInstruction(MakeUnique( + ir_context, SpvOpSelectionMerge, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {SpvSelectionControlMaskNone}}}))); + + new_entry_block->AddInstruction(MakeUnique( + ir_context, SpvOpBranchConditional, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.condition_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.entry_block_id()}}, + {SPV_OPERAND_TYPE_ID, + {original_label_to_duplicate_label[message_.entry_block_id()]}}}))); + + // Move the terminator of |exit_block| to the end of + // |merge_block|. + auto exit_block_terminator = exit_block->terminator(); + auto cloned_instr = exit_block_terminator->Clone(ir_context); + merge_block->AddInstruction(std::unique_ptr(cloned_instr)); + ir_context->KillInst(exit_block_terminator); + + // Add OpBranch instruction to the merge block at the end of + // |exit_block| and at the end of |duplicated_exit_block|, so that + // the execution proceeds in the |merge_block|. + opt::Instruction merge_branch_instr = opt::Instruction( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.merge_label_fresh_id()}}})); + exit_block->AddInstruction(MakeUnique(merge_branch_instr)); + duplicated_exit_block->AddInstruction( + std::unique_ptr(merge_branch_instr.Clone(ir_context))); + + // Execution needs to start in the |new_entry_block|. Change all + // the uses of |entry_block_label_instr| outside of the original + // region to |message_.new_entry_fresh_id|. + auto entry_block_label_instr = + ir_context->get_def_use_mgr()->GetDef(message_.entry_block_id()); + ir_context->get_def_use_mgr()->ForEachUse( + entry_block_label_instr, + [this, ir_context, region_blocks](opt::Instruction* user, + uint32_t operand_index) { + auto user_block = ir_context->get_instr_block(user); + if ((region_blocks.count(user_block) != 0)) { + return; + } + switch (user->opcode()) { + case SpvOpSwitch: + case SpvOpBranch: + case SpvOpBranchConditional: + case SpvOpLoopMerge: + case SpvOpSelectionMerge: { + user->SetOperand(operand_index, {message_.new_entry_fresh_id()}); + } break; + case SpvOpName: + break; + default: + assert(false && + "The label id cannot be used by instructions " + "other than " + "OpSwitch, OpBranch, OpBranchConditional, " + "OpLoopMerge, " + "OpSelectionMerge"); + } + }); + + opt::Instruction* merge_block_terminator = merge_block->terminator(); + switch (merge_block_terminator->opcode()) { + case SpvOpReturnValue: + case SpvOpBranchConditional: { + uint32_t operand = merge_block_terminator->GetSingleWordInOperand(0); + if (original_id_to_phi_id.count(operand)) { + merge_block_terminator->SetInOperand( + 0, {original_id_to_phi_id.at(operand)}); + } + break; + } + default: + break; + } + + // Insert the merge block after the |duplicated_exit_block| (the + // last duplicated block). + enclosing_function->InsertBasicBlockAfter(std::move(merge_block), + duplicated_exit_block); + + // Insert the |new_entry_block| before the entry block of the + // original region. + enclosing_function->InsertBasicBlockBefore(std::move(new_entry_block), + entry_block); + + // Since we have changed the module, most of the analysis are now + // invalid. We can invalidate analyses now after all of the blocks + // have been registered. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3785): +// The following method has been copied from +// TransformationOutlineFunction. Consider refactoring to avoid +// duplication. +std::set +TransformationDuplicateRegionWithSelection::GetRegionBlocks( + opt::IRContext* ir_context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block) { + auto enclosing_function = entry_block->GetParent(); + auto dominator_analysis = + ir_context->GetDominatorAnalysis(enclosing_function); + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(enclosing_function); + + // A block belongs to a region between the entry block and the exit + // block if and only if it is dominated by the entry block and + // post-dominated by the exit block. + std::set result; + for (auto& block : *enclosing_function) { + if (dominator_analysis->Dominates(entry_block, &block) && + postdominator_analysis->Dominates(exit_block, &block)) { + result.insert(&block); + } + } + return result; +} + +protobufs::Transformation +TransformationDuplicateRegionWithSelection::ToMessage() const { + protobufs::Transformation result; + *result.mutable_duplicate_region_with_selection() = message_; + return result; +} + +std::unordered_set +TransformationDuplicateRegionWithSelection::GetFreshIds() const { + std::unordered_set result = {message_.new_entry_fresh_id(), + message_.merge_label_fresh_id()}; + for (auto& pair : message_.original_label_to_duplicate_label()) { + result.insert(pair.second()); + } + for (auto& pair : message_.original_id_to_duplicate_id()) { + result.insert(pair.second()); + } + for (auto& pair : message_.original_id_to_phi_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.h b/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.h new file mode 100644 index 0000000..d6f0ad9 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_duplicate_region_with_selection.h @@ -0,0 +1,80 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_DUPLICATE_REGION_WITH_SELECTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_DUPLICATE_REGION_WITH_SELECTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationDuplicateRegionWithSelection : public Transformation { + public: + explicit TransformationDuplicateRegionWithSelection( + const protobufs::TransformationDuplicateRegionWithSelection& message); + + explicit TransformationDuplicateRegionWithSelection( + uint32_t new_entry_fresh_id, uint32_t condition_id, + uint32_t merge_label_fresh_id, uint32_t entry_block_id, + uint32_t exit_block_id, + const std::map& original_label_to_duplicate_label, + const std::map& original_id_to_duplicate_id, + const std::map& original_id_to_phi_id); + + // - |new_entry_fresh_id|, |merge_label_fresh_id| must be fresh and distinct. + // - |condition_id| must refer to a valid instruction of boolean type. + // - |entry_block_id| and |exit_block_id| must refer to valid blocks and they + // must form a single-entry, single-exit region. Its constructs and their + // merge blocks must be either wholly within or wholly outside of the + // region. + // - |original_label_to_duplicate_label| must contain at least a key for every + // block in the original region. + // - |original_id_to_duplicate_id| must contain at least a key for every + // result id in the original region. + // - |original_id_to_phi_id| must contain at least a key for every result id + // available at the end of the original region. + // - In each of these three maps, each value must be a distinct, fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A transformation that inserts a conditional statement with a boolean + // expression of arbitrary value and duplicates a given single-entry, + // single-exit region, so that it is present in each conditional branch and + // will be executed regardless of which branch will be taken. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + // Returns the set of blocks dominated by |entry_block| and post-dominated + // by |exit_block|. + static std::set GetRegionBlocks( + opt::IRContext* ir_context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block); + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationDuplicateRegionWithSelection message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_DUPLICATE_REGION_WITH_SELECTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_equation_instruction.cpp b/third_party/spirv-tools/source/fuzz/transformation_equation_instruction.cpp new file mode 100644 index 0000000..32e8351 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_equation_instruction.cpp @@ -0,0 +1,296 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_equation_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationEquationInstruction::TransformationEquationInstruction( + const spvtools::fuzz::protobufs::TransformationEquationInstruction& message) + : message_(message) {} + +TransformationEquationInstruction::TransformationEquationInstruction( + uint32_t fresh_id, SpvOp opcode, const std::vector& in_operand_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_opcode(opcode); + for (auto id : in_operand_id) { + message_.add_in_operand_id(id); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationEquationInstruction::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // The instruction to insert before must exist. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!insert_before) { + return false; + } + // The input ids must all exist, not be OpUndef, not be irrelevant, and be + // available before this instruction. + for (auto id : message_.in_operand_id()) { + auto inst = ir_context->get_def_use_mgr()->GetDef(id); + if (!inst) { + return false; + } + if (inst->opcode() == SpvOpUndef) { + return false; + } + if (transformation_context.GetFactManager()->IdIsIrrelevant(id)) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + id)) { + return false; + } + } + + return MaybeGetResultTypeId(ir_context) != 0; +} + +void TransformationEquationInstruction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + opt::Instruction::OperandList in_operands; + std::vector rhs_id; + for (auto id : message_.in_operand_id()) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + rhs_id.push_back(id); + } + + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, static_cast(message_.opcode()), + MaybeGetResultTypeId(ir_context), message_.fresh_id(), + std::move(in_operands))); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Add an equation fact as long as the result id is not irrelevant (it could + // be if we are inserting into a dead block). + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + transformation_context->GetFactManager()->AddFactIdEquation( + message_.fresh_id(), static_cast(message_.opcode()), rhs_id); + } +} + +protobufs::Transformation TransformationEquationInstruction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_equation_instruction() = message_; + return result; +} + +uint32_t TransformationEquationInstruction::MaybeGetResultTypeId( + opt::IRContext* ir_context) const { + auto opcode = static_cast(message_.opcode()); + switch (opcode) { + case SpvOpConvertUToF: + case SpvOpConvertSToF: { + if (message_.in_operand_id_size() != 1) { + return 0; + } + + const auto* type = ir_context->get_type_mgr()->GetType( + fuzzerutil::GetTypeId(ir_context, message_.in_operand_id(0))); + if (!type) { + return 0; + } + + if (const auto* vector = type->AsVector()) { + if (!vector->element_type()->AsInteger()) { + return 0; + } + + if (auto element_type_id = fuzzerutil::MaybeGetFloatType( + ir_context, vector->element_type()->AsInteger()->width())) { + return fuzzerutil::MaybeGetVectorType(ir_context, element_type_id, + vector->element_count()); + } + + return 0; + } else { + if (!type->AsInteger()) { + return 0; + } + + return fuzzerutil::MaybeGetFloatType(ir_context, + type->AsInteger()->width()); + } + } + case SpvOpBitcast: { + if (message_.in_operand_id_size() != 1) { + return 0; + } + + const auto* operand_inst = + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst) { + return 0; + } + + const auto* operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!operand_type) { + return 0; + } + + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3539): + // The only constraint on the types of OpBitcast's parameters is that + // they must have the same number of bits. Consider improving the code + // below to support this in full. + if (const auto* vector = operand_type->AsVector()) { + uint32_t component_type_id; + if (const auto* int_type = vector->element_type()->AsInteger()) { + component_type_id = + fuzzerutil::MaybeGetFloatType(ir_context, int_type->width()); + } else if (const auto* float_type = vector->element_type()->AsFloat()) { + component_type_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), true); + if (component_type_id == 0 || + fuzzerutil::MaybeGetVectorType(ir_context, component_type_id, + vector->element_count()) == 0) { + component_type_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), false); + } + } else { + assert(false && "Only vectors of numerical components are supported"); + return 0; + } + + if (component_type_id == 0) { + return 0; + } + + return fuzzerutil::MaybeGetVectorType(ir_context, component_type_id, + vector->element_count()); + } else if (const auto* int_type = operand_type->AsInteger()) { + return fuzzerutil::MaybeGetFloatType(ir_context, int_type->width()); + } else if (const auto* float_type = operand_type->AsFloat()) { + if (auto existing_id = fuzzerutil::MaybeGetIntegerType( + ir_context, float_type->width(), true)) { + return existing_id; + } + + return fuzzerutil::MaybeGetIntegerType(ir_context, float_type->width(), + false); + } else { + assert(false && + "Operand is not a scalar or a vector of numerical type"); + return 0; + } + } + case SpvOpIAdd: + case SpvOpISub: { + if (message_.in_operand_id_size() != 2) { + return 0; + } + uint32_t first_operand_width = 0; + uint32_t first_operand_type_id = 0; + for (uint32_t index = 0; index < 2; index++) { + auto operand_inst = ir_context->get_def_use_mgr()->GetDef( + message_.in_operand_id(index)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsInteger() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsInteger()))) { + return 0; + } + uint32_t operand_width = + operand_type->AsInteger() + ? 1 + : operand_type->AsVector()->element_count(); + if (index == 0) { + first_operand_width = operand_width; + first_operand_type_id = operand_inst->type_id(); + } else { + assert(first_operand_width != 0 && + "The first operand should have been processed."); + if (operand_width != first_operand_width) { + return 0; + } + } + } + assert(first_operand_type_id != 0 && + "A type must have been found for the first operand."); + return first_operand_type_id; + } + case SpvOpLogicalNot: { + if (message_.in_operand_id().size() != 1) { + return 0; + } + auto operand_inst = + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsBool() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsBool()))) { + return 0; + } + return operand_inst->type_id(); + } + case SpvOpSNegate: { + if (message_.in_operand_id().size() != 1) { + return 0; + } + auto operand_inst = + ir_context->get_def_use_mgr()->GetDef(message_.in_operand_id(0)); + if (!operand_inst || !operand_inst->type_id()) { + return 0; + } + auto operand_type = + ir_context->get_type_mgr()->GetType(operand_inst->type_id()); + if (!(operand_type->AsInteger() || + (operand_type->AsVector() && + operand_type->AsVector()->element_type()->AsInteger()))) { + return 0; + } + return operand_inst->type_id(); + } + default: + assert(false && "Inappropriate opcode for equation instruction."); + return 0; + } +} + +std::unordered_set TransformationEquationInstruction::GetFreshIds() + const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_equation_instruction.h b/third_party/spirv-tools/source/fuzz/transformation_equation_instruction.h new file mode 100644 index 0000000..4afc85f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_equation_instruction.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationEquationInstruction : public Transformation { + public: + explicit TransformationEquationInstruction( + const protobufs::TransformationEquationInstruction& message); + + TransformationEquationInstruction( + uint32_t fresh_id, SpvOp opcode, + const std::vector& in_operand_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh. + // - |message_.instruction_to_insert_before| must identify an instruction + // before which an equation instruction can legitimately be inserted. + // - Each id in |message_.in_operand_id| must exist, not be an OpUndef, and + // be available before |message_.instruction_to_insert_before|. + // - |message_.opcode| must be an opcode for which we know how to handle + // equations, the types of the ids in |message_.in_operand_id| must be + // suitable for use with this opcode, and the module must contain an + // appropriate result type id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction to the module, right before + // |message_.instruction_to_insert_before|, of the form: + // + // |message_.fresh_id| = |message_.opcode| %type |message_.in_operand_ids| + // + // where %type is a type id that already exists in the module and that is + // compatible with the opcode and input operands. + // + // The fact manager is also updated to inform it of this equation fact. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns type id for the equation instruction. Returns 0 if result type does + // not exist. + uint32_t MaybeGetResultTypeId(opt::IRContext* ir_context) const; + + protobufs::TransformationEquationInstruction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_EQUATION_INSTRUCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp b/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp new file mode 100644 index 0000000..640aea2 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_expand_vector_reduction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationExpandVectorReduction::TransformationExpandVectorReduction( + const spvtools::fuzz::protobufs::TransformationExpandVectorReduction& + message) + : message_(message) {} + +TransformationExpandVectorReduction::TransformationExpandVectorReduction( + const uint32_t instruction_result_id, + const std::vector& fresh_ids) { + message_.set_instruction_result_id(instruction_result_id); + *message_.mutable_fresh_ids() = + google::protobuf::RepeatedField( + fresh_ids.begin(), fresh_ids.end()); +} + +bool TransformationExpandVectorReduction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto* instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + + // |instruction| must be defined. + if (!instruction) { + return false; + } + + // |instruction| must be OpAny or OpAll. + if (instruction->opcode() != SpvOpAny && instruction->opcode() != SpvOpAll) { + return false; + } + + // |message_.fresh_ids.size| must have the exact number of fresh ids required + // to apply the transformation. + if (static_cast(message_.fresh_ids().size()) != + GetRequiredFreshIdCount(ir_context, instruction)) { + return false; + } + + std::set ids_used_by_this_transformation; + for (uint32_t fresh_id : message_.fresh_ids()) { + // All ids in |message_.fresh_ids| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) { + return false; + } + + // All fresh ids need to be distinct. + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + fresh_id, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + + return true; +} + +void TransformationExpandVectorReduction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto* instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + auto* vector = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t vector_component_count = ir_context->get_type_mgr() + ->GetType(vector->type_id()) + ->AsVector() + ->element_count(); + + // Fresh id iterator. + auto fresh_id = message_.fresh_ids().begin(); + + // |vector_components| are the ids of the extracted components from |vector|. + std::vector vector_components; + + for (uint32_t i = 0; i < vector_component_count; i++) { + // Extracts the i-th |vector| component. + auto vector_component = opt::Instruction( + ir_context, SpvOpCompositeExtract, instruction->type_id(), *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {vector->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}); + instruction->InsertBefore(MakeUnique(vector_component)); + fuzzerutil::UpdateModuleIdBound(ir_context, vector_component.result_id()); + vector_components.push_back(vector_component.result_id()); + } + + // The first two |vector| components are used in the first logical operation. + auto logical_instruction = opt::Instruction( + ir_context, + instruction->opcode() == SpvOpAny ? SpvOpLogicalOr : SpvOpLogicalAnd, + instruction->type_id(), *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {vector_components[0]}}, + {SPV_OPERAND_TYPE_ID, {vector_components[1]}}}); + instruction->InsertBefore(MakeUnique(logical_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, logical_instruction.result_id()); + + // Evaluates the remaining components. + for (uint32_t i = 2; i < vector_components.size(); i++) { + logical_instruction = opt::Instruction( + ir_context, logical_instruction.opcode(), instruction->type_id(), + *fresh_id++, + {{SPV_OPERAND_TYPE_ID, {vector_components[i]}}, + {SPV_OPERAND_TYPE_ID, {logical_instruction.result_id()}}}); + instruction->InsertBefore( + MakeUnique(logical_instruction)); + fuzzerutil::UpdateModuleIdBound(ir_context, + logical_instruction.result_id()); + } + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // If it's possible to make a synonym of |instruction|, then add the fact that + // the last |logical_instruction| is a synonym of |instruction|. + if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, + instruction)) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(logical_instruction.result_id(), {}), + MakeDataDescriptor(instruction->result_id(), {})); + } +} + +protobufs::Transformation TransformationExpandVectorReduction::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_expand_vector_reduction() = message_; + return result; +} + +uint32_t TransformationExpandVectorReduction::GetRequiredFreshIdCount( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // For each vector component, 1 OpCompositeExtract and 1 OpLogical* (except + // for the first component) instructions will be inserted. + return 2 * ir_context->get_type_mgr() + ->GetType(ir_context->get_def_use_mgr() + ->GetDef(instruction->GetSingleWordInOperand(0)) + ->type_id()) + ->AsVector() + ->element_count() - + 1; +} + +std::unordered_set TransformationExpandVectorReduction::GetFreshIds() + const { + std::unordered_set result; + for (auto id : message_.fresh_ids()) { + result.insert(id); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.h b/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.h new file mode 100644 index 0000000..e4cc953 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_expand_vector_reduction.h @@ -0,0 +1,105 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// clang-format off +// SPIR-V code to help understand the transformation. +// +// ------------------------------------------------------------------------------- +// | Reference shader | Variant shader | +// ------------------------------------------------------------------------------- +// | OpCapability Shader | OpCapability Shader | +// | %1 = OpExtInstImport "GLSL.std.450" | %1 = OpExtInstImport "GLSL.std.450" | +// | OpMemoryModel Logical GLSL450 | OpMemoryModel Logical GLSL450 | +// | OpEntryPoint Vertex %9 "main" | OpEntryPoint Vertex %9 "main" | +// | | | +// | ; Types | ; Types | +// | %2 = OpTypeBool | %2 = OpTypeBool | +// | %3 = OpTypeVector %2 2 | %3 = OpTypeVector %2 2 | +// | %4 = OpTypeVoid | %4 = OpTypeVoid | +// | %5 = OpTypeFunction %4 | %5 = OpTypeFunction %4 | +// | | | +// | ; Constants | ; Constants | +// | %6 = OpConstantTrue %2 | %6 = OpConstantTrue %2 | +// | %7 = OpConstantFalse %2 | %7 = OpConstantFalse %2 | +// | %8 = OpConstantComposite %3 %6 %7 | %8 = OpConstantComposite %3 %6 %7 | +// | | | +// | ; main function | ; main function | +// | %9 = OpFunction %4 None %5 | %9 = OpFunction %4 None %5 | +// | %10 = OpLabel | %10 = OpLabel | +// | %11 = OpAny %2 %8 | | +// | %12 = OpAll %2 %8 | ; Add OpAny synonym | +// | OpReturn | %13 = OpCompositeExtract %2 %8 0 | +// | OpFunctionEnd | %14 = OpCompositeExtract %2 %8 1 | +// | | %15 = OpLogicalOr %2 %13 %14 | +// | | %11 = OpAny %2 %8 | +// | | | +// | | ; Add OpAll synonym | +// | | %16 = OpCompositeExtract %2 %8 0 | +// | | %17 = OpCompositeExtract %2 %8 1 | +// | | %18 = OpLogicalAnd %2 %16 %17 | +// | | %12 = OpAll %2 %8 | +// | | OpReturn | +// | | OpFunctionEnd | +// ------------------------------------------------------------------------------- +// +// %11 and %15 are synonymous +// %12 and %18 are synonymous +// clang-format on +class TransformationExpandVectorReduction : public Transformation { + public: + explicit TransformationExpandVectorReduction( + const protobufs::TransformationExpandVectorReduction& message); + + TransformationExpandVectorReduction(const uint32_t instruction_result_id, + const std::vector& fresh_ids); + + // - |message_.instruction_result_id| must be OpAny or OpAll. + // - |message_.fresh_ids| must be fresh ids needed to apply the + // transformation. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds synonyms for OpAny and OpAll instructions by evaluating each vector + // component with the corresponding logical operation. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the number of fresh ids required to apply the transformation. + static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + protobufs::TransformationExpandVectorReduction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_EXPAND_VECTOR_REDUCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.cpp b/third_party/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.cpp new file mode 100644 index 0000000..dec933c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.cpp @@ -0,0 +1,1020 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_flatten_conditional_branch.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch( + const protobufs::TransformationFlattenConditionalBranch& message) + : message_(message) {} + +TransformationFlattenConditionalBranch::TransformationFlattenConditionalBranch( + uint32_t header_block_id, bool true_branch_first, + uint32_t fresh_id_for_bvec2_selector, uint32_t fresh_id_for_bvec3_selector, + uint32_t fresh_id_for_bvec4_selector, + const std::vector& + side_effect_wrappers_info) { + message_.set_header_block_id(header_block_id); + message_.set_true_branch_first(true_branch_first); + message_.set_fresh_id_for_bvec2_selector(fresh_id_for_bvec2_selector); + message_.set_fresh_id_for_bvec3_selector(fresh_id_for_bvec3_selector); + message_.set_fresh_id_for_bvec4_selector(fresh_id_for_bvec4_selector); + for (auto const& side_effect_wrapper_info : side_effect_wrappers_info) { + *message_.add_side_effect_wrapper_info() = side_effect_wrapper_info; + } +} + +bool TransformationFlattenConditionalBranch::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto header_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.header_block_id()); + + // The block must have been found and it must be a selection header. + if (!header_block || !header_block->GetMergeInst() || + header_block->GetMergeInst()->opcode() != SpvOpSelectionMerge) { + return false; + } + + // The header block must end with an OpBranchConditional instruction. + if (header_block->terminator()->opcode() != SpvOpBranchConditional) { + return false; + } + + // The branch condition cannot be irrelevant: we will make reference to it + // multiple times and we need to be guaranteed that these references will + // yield the same result; if they are replaced by other ids that will not + // work. + if (transformation_context.GetFactManager()->IdIsIrrelevant( + header_block->terminator()->GetSingleWordInOperand(0))) { + return false; + } + + std::set used_fresh_ids; + + // If ids have been provided to be used as vector guards for OpSelect + // instructions then they must be fresh. + for (uint32_t fresh_id_for_bvec_selector : + {message_.fresh_id_for_bvec2_selector(), + message_.fresh_id_for_bvec3_selector(), + message_.fresh_id_for_bvec4_selector()}) { + if (fresh_id_for_bvec_selector != 0) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + fresh_id_for_bvec_selector, ir_context, &used_fresh_ids)) { + return false; + } + } + } + + // Use a set to keep track of the instructions that require fresh ids. + std::set instructions_that_need_ids; + + // Check that, if there are enough ids, the conditional can be flattened and, + // if so, add all the problematic instructions that need to be enclosed inside + // conditionals to |instructions_that_need_ids|. + if (!GetProblematicInstructionsIfConditionalCanBeFlattened( + ir_context, header_block, transformation_context, + &instructions_that_need_ids)) { + return false; + } + + // Get the mapping from instructions to the fresh ids needed to enclose them + // inside conditionals. + auto insts_to_wrapper_info = GetInstructionsToWrapperInfo(ir_context); + + { + // Check the ids in the map. + for (const auto& inst_to_info : insts_to_wrapper_info) { + // Check the fresh ids needed for all of the instructions that need to be + // enclosed inside a conditional. + for (uint32_t id : {inst_to_info.second.merge_block_id(), + inst_to_info.second.execute_block_id()}) { + if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation( + id, ir_context, &used_fresh_ids)) { + return false; + } + } + + // Check the other ids needed, if the instruction needs a placeholder. + if (InstructionNeedsPlaceholder(ir_context, *inst_to_info.first)) { + // Check the fresh ids. + for (uint32_t id : {inst_to_info.second.actual_result_id(), + inst_to_info.second.alternative_block_id(), + inst_to_info.second.placeholder_result_id()}) { + if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation( + id, ir_context, &used_fresh_ids)) { + return false; + } + } + + // Check that the placeholder value id exists, has the right type and is + // available to use at this point. + auto value_def = ir_context->get_def_use_mgr()->GetDef( + inst_to_info.second.value_to_copy_id()); + if (!value_def || + value_def->type_id() != inst_to_info.first->type_id() || + !fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, inst_to_info.first, + inst_to_info.second.value_to_copy_id())) { + return false; + } + } + } + } + + // If some instructions that require ids are not in the map, the + // transformation needs overflow ids to be applicable. + for (auto instruction : instructions_that_need_ids) { + if (insts_to_wrapper_info.count(instruction) == 0 && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } + + if (OpSelectArgumentsAreRestricted(ir_context)) { + // OpPhi instructions at the convergence block for the selection are handled + // by turning them into OpSelect instructions. As the SPIR-V version in use + // has restrictions on the arguments that OpSelect can take, we must check + // that any OpPhi instructions are compatible with these restrictions. + uint32_t convergence_block_id = + FindConvergenceBlock(ir_context, *header_block); + // Consider every OpPhi instruction at the convergence block. + if (!ir_context->cfg() + ->block(convergence_block_id) + ->WhileEachPhiInst([this, + ir_context](opt::Instruction* inst) -> bool { + // Decide whether the OpPhi can be handled based on its result + // type. + opt::Instruction* phi_result_type = + ir_context->get_def_use_mgr()->GetDef(inst->type_id()); + switch (phi_result_type->opcode()) { + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypePointer: + // Fine: OpSelect can work directly on scalar and pointer + // types. + return true; + case SpvOpTypeVector: { + // In its restricted form, OpSelect can only select between + // vectors if the condition of the select is a boolean + // boolean vector. We thus require the appropriate boolean + // vector type to be present. + uint32_t bool_type_id = + fuzzerutil::MaybeGetBoolType(ir_context); + if (!bool_type_id) { + return false; + } + + uint32_t dimension = + phi_result_type->GetSingleWordInOperand(1); + if (fuzzerutil::MaybeGetVectorType(ir_context, bool_type_id, + dimension) == 0) { + // The required boolean vector type is not present. + return false; + } + // The transformation needs to be equipped with a fresh id + // in which to store the vectorized version of the selection + // construct's condition. + switch (dimension) { + case 2: + return message_.fresh_id_for_bvec2_selector() != 0; + case 3: + return message_.fresh_id_for_bvec3_selector() != 0; + default: + assert(dimension == 4 && "Invalid vector dimension."); + return message_.fresh_id_for_bvec4_selector() != 0; + } + } + default: + return false; + } + })) { + return false; + } + } + + // All checks were passed. + return true; +} + +void TransformationFlattenConditionalBranch::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // branch = 1 corresponds to the true branch, branch = 2 corresponds to the + // false branch. If the true branch is to be laid out first, we need to visit + // the false branch first, because each branch is moved to right after the + // header while it is visited. + std::vector branches = {2, 1}; + if (!message_.true_branch_first()) { + // Similarly, we need to visit the true branch first, if we want it to be + // laid out after the false branch. + branches = {1, 2}; + } + + auto header_block = ir_context->cfg()->block(message_.header_block_id()); + + // Get the ids of the starting blocks of the first and last branches to be + // laid out. The first branch is the true branch iff + // |message_.true_branch_first| is true. + auto branch_instruction = header_block->terminator(); + uint32_t first_block_first_branch_id = + branch_instruction->GetSingleWordInOperand(branches[1]); + uint32_t first_block_last_branch_id = + branch_instruction->GetSingleWordInOperand(branches[0]); + + uint32_t convergence_block_id = + FindConvergenceBlock(ir_context, *header_block); + + // If the OpBranchConditional instruction in the header branches to the same + // block for both values of the condition, this is the convergence block (the + // flow does not actually diverge) and the OpPhi instructions in it are still + // valid, so we do not need to make any changes. + if (first_block_first_branch_id != first_block_last_branch_id) { + RewriteOpPhiInstructionsAtConvergenceBlock( + *header_block, convergence_block_id, ir_context); + } + + // Get the mapping from instructions to fresh ids. + auto insts_to_info = GetInstructionsToWrapperInfo(ir_context); + + // Get a reference to the last block in the first branch that will be laid out + // (this depends on |message_.true_branch_first|). The last block is the block + // in the branch just before flow converges (it might not exist). + opt::BasicBlock* last_block_first_branch = nullptr; + + // Keep track of blocks and ids for which we should later add dead block and + // irrelevant id facts. We wait until we have finished applying the + // transformation before adding these facts, so that the fact manager has + // access to the fully up-to-date module. + std::vector dead_blocks; + std::vector irrelevant_ids; + + // Adjust the conditional branches by enclosing problematic instructions + // within conditionals and get references to the last block in each branch. + for (uint32_t branch : branches) { + auto current_block = header_block; + // Get the id of the first block in this branch. + uint32_t next_block_id = branch_instruction->GetSingleWordInOperand(branch); + + // Consider all blocks in the branch until the convergence block is reached. + while (next_block_id != convergence_block_id) { + // Move the next block to right after the current one. + current_block->GetParent()->MoveBasicBlockToAfter(next_block_id, + current_block); + + // Move forward in the branch. + current_block = ir_context->cfg()->block(next_block_id); + + // Find all the instructions in the current block which need to be + // enclosed inside conditionals. + std::vector problematic_instructions; + + current_block->ForEachInst( + [&problematic_instructions](opt::Instruction* instruction) { + if (instruction->opcode() != SpvOpLabel && + instruction->opcode() != SpvOpBranch && + !fuzzerutil::InstructionHasNoSideEffects(*instruction)) { + problematic_instructions.push_back(instruction); + } + }); + + uint32_t condition_id = + header_block->terminator()->GetSingleWordInOperand(0); + + // Enclose all of the problematic instructions in conditionals, with the + // same condition as the selection construct being flattened. + for (auto instruction : problematic_instructions) { + // Get the info needed by this instruction to wrap it inside a + // conditional. + protobufs::SideEffectWrapperInfo wrapper_info; + + if (insts_to_info.count(instruction) != 0) { + // Get the fresh ids from the map, if present. + wrapper_info = insts_to_info[instruction]; + } else { + // If we could not get it from the map, use overflow ids. We don't + // need to set |wrapper_info.instruction|, as it will not be used. + wrapper_info.set_merge_block_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + wrapper_info.set_execute_block_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + + if (InstructionNeedsPlaceholder(ir_context, *instruction)) { + // Ge the fresh ids from the overflow ids. + wrapper_info.set_actual_result_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + wrapper_info.set_alternative_block_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + wrapper_info.set_placeholder_result_id( + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()); + + // Try to find a zero constant. It does not matter whether it is + // relevant or irrelevant. + for (bool is_irrelevant : {true, false}) { + wrapper_info.set_value_to_copy_id( + fuzzerutil::MaybeGetZeroConstant( + ir_context, *transformation_context, + instruction->type_id(), is_irrelevant)); + if (wrapper_info.value_to_copy_id()) { + break; + } + } + } + } + + // Enclose the instruction in a conditional and get the merge block + // generated by this operation (this is where all the following + // instructions will be). + current_block = EncloseInstructionInConditional( + ir_context, *transformation_context, current_block, instruction, + wrapper_info, condition_id, branch == 1, &dead_blocks, + &irrelevant_ids); + } + + next_block_id = current_block->terminator()->GetSingleWordInOperand(0); + + // If the next block is the convergence block and this the branch that + // will be laid out right after the header, record this as the last block + // in the first branch. + if (next_block_id == convergence_block_id && branch == branches[1]) { + last_block_first_branch = current_block; + } + } + } + + // The current header should unconditionally branch to the starting block in + // the first branch to be laid out, if such a branch exists (i.e. the header + // does not branch directly to the convergence block), and to the starting + // block in the last branch to be laid out otherwise. + uint32_t after_header = first_block_first_branch_id != convergence_block_id + ? first_block_first_branch_id + : first_block_last_branch_id; + + // Kill the merge instruction and the branch instruction in the current + // header. + auto merge_inst = header_block->GetMergeInst(); + ir_context->KillInst(branch_instruction); + ir_context->KillInst(merge_inst); + + // Add a new, unconditional, branch instruction from the current header to + // |after_header|. + header_block->AddInstruction(MakeUnique( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {after_header}}})); + + // If the first branch to be laid out exists, change the branch instruction so + // that the last block in such branch unconditionally branches to the first + // block in the other branch (or the convergence block if there is no other + // branch) and change the OpPhi instructions in the last branch accordingly + // (the predecessor changed). + if (last_block_first_branch) { + last_block_first_branch->terminator()->SetInOperand( + 0, {first_block_last_branch_id}); + + // Change the OpPhi instructions of the last branch (if there is another + // branch) so that the predecessor is now the last block of the first + // branch. The block must have a single predecessor, so the operand + // specifying the predecessor is always in the same position. + if (first_block_last_branch_id != convergence_block_id) { + ir_context->get_instr_block(first_block_last_branch_id) + ->ForEachPhiInst( + [&last_block_first_branch](opt::Instruction* phi_inst) { + // The operand specifying the predecessor is the second input + // operand. + phi_inst->SetInOperand(1, {last_block_first_branch->id()}); + }); + } + } + + // Invalidate all analyses + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Now that we have finished adding blocks and ids to the module and + // invalidated existing analyses, update the fact manager regarding dead + // blocks and irrelevant ids. + for (auto dead_block : dead_blocks) { + transformation_context->GetFactManager()->AddFactBlockIsDead(dead_block); + } + for (auto irrelevant_id : irrelevant_ids) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant( + irrelevant_id); + } +} + +protobufs::Transformation TransformationFlattenConditionalBranch::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_flatten_conditional_branch() = message_; + return result; +} + +bool TransformationFlattenConditionalBranch:: + GetProblematicInstructionsIfConditionalCanBeFlattened( + opt::IRContext* ir_context, opt::BasicBlock* header, + const TransformationContext& transformation_context, + std::set* instructions_that_need_ids) { + uint32_t merge_block_id = header->MergeBlockIdIfAny(); + assert(merge_block_id && + header->GetMergeInst()->opcode() == SpvOpSelectionMerge && + header->terminator()->opcode() == SpvOpBranchConditional && + "|header| must be the header of a conditional."); + + auto enclosing_function = header->GetParent(); + auto dominator_analysis = + ir_context->GetDominatorAnalysis(enclosing_function); + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(enclosing_function); + + // |header| must be reachable. + if (!dominator_analysis->IsReachable(header)) { + return false; + } + + // Check that the header and the merge block describe a single-entry, + // single-exit region. + if (!dominator_analysis->Dominates(header->id(), merge_block_id) || + !postdominator_analysis->Dominates(merge_block_id, header->id())) { + return false; + } + + // Traverse the CFG starting from the header and check that, for all the + // blocks that can be reached by the header before the flow converges: + // - they don't contain merge, barrier or OpSampledImage instructions + // - they branch unconditionally to another block + // Add any side-effecting instruction, requiring fresh ids, to + // |instructions_that_need_ids| + std::queue to_check; + header->ForEachSuccessorLabel( + [&to_check](uint32_t label) { to_check.push(label); }); + + auto* structured_cfg = ir_context->GetStructuredCFGAnalysis(); + while (!to_check.empty()) { + uint32_t block_id = to_check.front(); + to_check.pop(); + + if (structured_cfg->ContainingConstruct(block_id) != header->id() && + block_id != merge_block_id) { + // This block can be reached from the |header| but doesn't belong to its + // selection construct. This might be a continue target of some loop - + // we can't flatten the |header|. + return false; + } + + // If the block post-dominates the header, this is where flow converges, and + // we don't need to check this branch any further, because the + // transformation will only change the part of the graph where flow is + // divergent. + if (postdominator_analysis->Dominates(block_id, header->id())) { + continue; + } + + if (!transformation_context.GetFactManager()->BlockIsDead(header->id()) && + transformation_context.GetFactManager()->BlockIsDead(block_id)) { + // The |header| is not dead but the |block_id| is. Since |block_id| + // doesn't postdominate the |header|, CFG hasn't converged yet. Thus, we + // don't flatten the construct to prevent |block_id| from becoming + // executable. + return false; + } + + auto block = ir_context->cfg()->block(block_id); + + // The block must not have a merge instruction, because inner constructs are + // not allowed. + if (block->GetMergeInst()) { + return false; + } + + // The terminator instruction for the block must be OpBranch. + if (block->terminator()->opcode() != SpvOpBranch) { + return false; + } + + // Check all of the instructions in the block. + bool all_instructions_compatible = + block->WhileEachInst([ir_context, instructions_that_need_ids]( + opt::Instruction* instruction) { + // We can ignore OpLabel instructions. + if (instruction->opcode() == SpvOpLabel) { + return true; + } + + // If the instruction is a branch, it must be an unconditional branch. + if (instruction->IsBranch()) { + return instruction->opcode() == SpvOpBranch; + } + + // We cannot go ahead if we encounter an instruction that cannot be + // handled. + if (!InstructionCanBeHandled(ir_context, *instruction)) { + return false; + } + + // If the instruction has side effects, add it to the + // |instructions_that_need_ids| set. + if (!fuzzerutil::InstructionHasNoSideEffects(*instruction)) { + instructions_that_need_ids->emplace(instruction); + } + + return true; + }); + + if (!all_instructions_compatible) { + return false; + } + + // Add the successor of this block to the list of blocks that need to be + // checked. + to_check.push(block->terminator()->GetSingleWordInOperand(0)); + } + + // All the blocks are compatible with the transformation and this is indeed a + // single-entry, single-exit region. + return true; +} + +bool TransformationFlattenConditionalBranch::InstructionNeedsPlaceholder( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + assert(!fuzzerutil::InstructionHasNoSideEffects(instruction) && + InstructionCanBeHandled(ir_context, instruction) && + "The instruction must have side effects and it must be possible to " + "enclose it inside a conditional."); + + if (instruction.HasResultId()) { + // We need a placeholder iff the type is not Void. + auto type = ir_context->get_type_mgr()->GetType(instruction.type_id()); + return type && !type->AsVoid(); + } + + return false; +} + +std::unordered_map +TransformationFlattenConditionalBranch::GetInstructionsToWrapperInfo( + opt::IRContext* ir_context) const { + std::unordered_map + instructions_to_ids; + for (const auto& wrapper_info : message_.side_effect_wrapper_info()) { + auto instruction = FindInstruction(wrapper_info.instruction(), ir_context); + if (instruction) { + instructions_to_ids.emplace(instruction, wrapper_info); + } + } + + return instructions_to_ids; +} + +opt::BasicBlock* +TransformationFlattenConditionalBranch::EncloseInstructionInConditional( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, opt::BasicBlock* block, + opt::Instruction* instruction, + const protobufs::SideEffectWrapperInfo& wrapper_info, uint32_t condition_id, + bool exec_if_cond_true, std::vector* dead_blocks, + std::vector* irrelevant_ids) { + // Get the next instruction (it will be useful for splitting). + auto next_instruction = instruction->NextNode(); + + // Update the module id bound. + for (uint32_t id : + {wrapper_info.merge_block_id(), wrapper_info.execute_block_id()}) { + fuzzerutil::UpdateModuleIdBound(ir_context, id); + } + + // Create the block where the instruction is executed by splitting the + // original block. + auto execute_block = block->SplitBasicBlock( + ir_context, wrapper_info.execute_block_id(), + fuzzerutil::GetIteratorForInstruction(block, instruction)); + + // Create the merge block for the conditional that we are about to create by + // splitting execute_block (this will leave |instruction| as the only + // instruction in |execute_block|). + auto merge_block = execute_block->SplitBasicBlock( + ir_context, wrapper_info.merge_block_id(), + fuzzerutil::GetIteratorForInstruction(execute_block, next_instruction)); + + // Propagate the fact that the block is dead to the newly-created blocks. + if (transformation_context.GetFactManager()->BlockIsDead(block->id())) { + dead_blocks->emplace_back(execute_block->id()); + dead_blocks->emplace_back(merge_block->id()); + } + + // Initially, consider the merge block as the alternative block to branch to + // if the instruction should not be executed. + auto alternative_block = merge_block; + + // Add an unconditional branch from |execute_block| to |merge_block|. + execute_block->AddInstruction(MakeUnique( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {merge_block->id()}}})); + + // If the instruction requires a placeholder, it means that it has a result id + // and its result needs to be able to be used later on, and we need to: + // - add an additional block |ids.alternative_block_id| where a placeholder + // result id (using fresh id |ids.placeholder_result_id|) is obtained either + // by using OpCopyObject and copying |ids.value_to_copy_id| or, if such id + // was not given and a suitable constant was not found, by using OpUndef. + // - mark |ids.placeholder_result_id| as irrelevant + // - change the result id of the instruction to a fresh id + // (|ids.actual_result_id|). + // - add an OpPhi instruction, which will have the original result id of the + // instruction, in the merge block. + if (InstructionNeedsPlaceholder(ir_context, *instruction)) { + // Update the module id bound with the additional ids. + for (uint32_t id : + {wrapper_info.actual_result_id(), wrapper_info.alternative_block_id(), + wrapper_info.placeholder_result_id()}) { + fuzzerutil::UpdateModuleIdBound(ir_context, id); + } + + // Create a new block using |fresh_ids.alternative_block_id| for its label. + auto alternative_block_temp = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, wrapper_info.alternative_block_id(), + opt::Instruction::OperandList{})); + + // Keep the original result id of the instruction in a variable. + uint32_t original_result_id = instruction->result_id(); + + // Set the result id of the instruction to be |ids.actual_result_id|. + instruction->SetResultId(wrapper_info.actual_result_id()); + + // Add a placeholder instruction, with the same type as the original + // instruction and id |ids.placeholder_result_id|, to the new block. + if (wrapper_info.value_to_copy_id()) { + // If there is an available id to copy from, the placeholder instruction + // will be %placeholder_result_id = OpCopyObject %type %value_to_copy_id + alternative_block_temp->AddInstruction(MakeUnique( + ir_context, SpvOpCopyObject, instruction->type_id(), + wrapper_info.placeholder_result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {wrapper_info.value_to_copy_id()}}})); + } else { + // If there is no such id, use an OpUndef instruction. + alternative_block_temp->AddInstruction(MakeUnique( + ir_context, SpvOpUndef, instruction->type_id(), + wrapper_info.placeholder_result_id(), + opt::Instruction::OperandList{})); + } + + // Mark |ids.placeholder_result_id| as irrelevant. + irrelevant_ids->emplace_back(wrapper_info.placeholder_result_id()); + + // Add an unconditional branch from the new block to the merge block. + alternative_block_temp->AddInstruction(MakeUnique( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {merge_block->id()}}})); + + // Insert the block before the merge block. + alternative_block = block->GetParent()->InsertBasicBlockBefore( + std::move(alternative_block_temp), merge_block); + + // Using the original instruction result id, add an OpPhi instruction to the + // merge block, which will either take the value of the result of the + // instruction or the placeholder value defined in the alternative block. + merge_block->begin().InsertBefore(MakeUnique( + ir_context, SpvOpPhi, instruction->type_id(), original_result_id, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {instruction->result_id()}}, + {SPV_OPERAND_TYPE_ID, {execute_block->id()}}, + {SPV_OPERAND_TYPE_ID, {wrapper_info.placeholder_result_id()}}, + {SPV_OPERAND_TYPE_ID, {alternative_block->id()}}})); + + // Propagate the fact that the block is dead to the new block. + if (transformation_context.GetFactManager()->BlockIsDead(block->id())) { + dead_blocks->emplace_back(alternative_block->id()); + } + } + + // Depending on whether the instruction should be executed in the if branch or + // in the else branch, get the corresponding ids. + auto if_block_id = (exec_if_cond_true ? execute_block : alternative_block) + ->GetLabel() + ->result_id(); + auto else_block_id = (exec_if_cond_true ? alternative_block : execute_block) + ->GetLabel() + ->result_id(); + + // Add an OpSelectionMerge instruction to the block. + block->AddInstruction(MakeUnique( + ir_context, SpvOpSelectionMerge, 0, 0, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {merge_block->id()}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {SpvSelectionControlMaskNone}}})); + + // Add an OpBranchConditional, to the block, using |condition_id| as the + // condition and branching to |if_block_id| if the condition is true and to + // |else_block_id| if the condition is false. + block->AddInstruction(MakeUnique( + ir_context, SpvOpBranchConditional, 0, 0, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {condition_id}}, + {SPV_OPERAND_TYPE_ID, {if_block_id}}, + {SPV_OPERAND_TYPE_ID, {else_block_id}}})); + + return merge_block; +} + +bool TransformationFlattenConditionalBranch::InstructionCanBeHandled( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + // We can handle all instructions with no side effects. + if (fuzzerutil::InstructionHasNoSideEffects(instruction)) { + return true; + } + + // We cannot handle barrier instructions, while we should be able to handle + // all other instructions by enclosing them inside a conditional. + if (instruction.opcode() == SpvOpControlBarrier || + instruction.opcode() == SpvOpMemoryBarrier || + instruction.opcode() == SpvOpNamedBarrierInitialize || + instruction.opcode() == SpvOpMemoryNamedBarrier || + instruction.opcode() == SpvOpTypeNamedBarrier) { + return false; + } + + // We cannot handle OpSampledImage instructions, as they need to be in the + // same block as their use. + if (instruction.opcode() == SpvOpSampledImage) { + return false; + } + + // We cannot handle a sampled image load, because we re-work loads using + // conditional branches and OpPhi instructions, and the result type of OpPhi + // cannot be OpTypeSampledImage. + if (instruction.opcode() == SpvOpLoad && + ir_context->get_def_use_mgr()->GetDef(instruction.type_id())->opcode() == + SpvOpTypeSampledImage) { + return false; + } + + // We cannot handle instructions with an id which return a void type, if the + // result id is used in the module (e.g. a function call to a function that + // returns nothing). + if (instruction.HasResultId()) { + auto type = ir_context->get_type_mgr()->GetType(instruction.type_id()); + assert(type && "The type should be found in the module"); + + if (type->AsVoid() && + !ir_context->get_def_use_mgr()->WhileEachUse( + instruction.result_id(), + [](opt::Instruction* use_inst, uint32_t use_index) { + // Return false if the id is used as an input operand. + return use_index < + use_inst->NumOperands() - use_inst->NumInOperands(); + })) { + return false; + } + } + + return true; +} + +std::unordered_set +TransformationFlattenConditionalBranch::GetFreshIds() const { + std::unordered_set result = { + message_.fresh_id_for_bvec2_selector(), + message_.fresh_id_for_bvec3_selector(), + message_.fresh_id_for_bvec4_selector()}; + for (auto& side_effect_wrapper_info : message_.side_effect_wrapper_info()) { + result.insert(side_effect_wrapper_info.merge_block_id()); + result.insert(side_effect_wrapper_info.execute_block_id()); + result.insert(side_effect_wrapper_info.actual_result_id()); + result.insert(side_effect_wrapper_info.alternative_block_id()); + result.insert(side_effect_wrapper_info.placeholder_result_id()); + } + return result; +} + +uint32_t TransformationFlattenConditionalBranch::FindConvergenceBlock( + opt::IRContext* ir_context, const opt::BasicBlock& header_block) { + uint32_t result = header_block.terminator()->GetSingleWordInOperand(1); + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(header_block.GetParent()); + while (!postdominator_analysis->Dominates(result, header_block.id())) { + auto current_block = ir_context->get_instr_block(result); + // If the transformation is applicable, the terminator is OpBranch. + result = current_block->terminator()->GetSingleWordInOperand(0); + } + return result; +} + +bool TransformationFlattenConditionalBranch::OpSelectArgumentsAreRestricted( + opt::IRContext* ir_context) { + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: { + return true; + } + default: + return false; + } +} + +void TransformationFlattenConditionalBranch::AddBooleanVectorConstructorToBlock( + uint32_t fresh_id, uint32_t dimension, + const opt::Operand& branch_condition_operand, opt::IRContext* ir_context, + opt::BasicBlock* block) { + opt::Instruction::OperandList in_operands; + for (uint32_t i = 0; i < dimension; i++) { + in_operands.emplace_back(branch_condition_operand); + } + block->begin()->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, + fuzzerutil::MaybeGetVectorType( + ir_context, fuzzerutil::MaybeGetBoolType(ir_context), dimension), + fresh_id, in_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_id); +} + +void TransformationFlattenConditionalBranch:: + RewriteOpPhiInstructionsAtConvergenceBlock( + const opt::BasicBlock& header_block, uint32_t convergence_block_id, + opt::IRContext* ir_context) const { + const opt::Instruction& branch_instruction = *header_block.terminator(); + + const opt::Operand& branch_condition_operand = + branch_instruction.GetInOperand(0); + + // If we encounter OpPhi instructions on vector types then we may need to + // introduce vector versions of the selection construct's condition to use + // in corresponding OpSelect instructions. These booleans track whether we + // need to introduce such boolean vectors. + bool require_2d_boolean_vector = false; + bool require_3d_boolean_vector = false; + bool require_4d_boolean_vector = false; + + // Consider every OpPhi instruction at the convergence block. + opt::BasicBlock* convergence_block = + ir_context->get_instr_block(convergence_block_id); + convergence_block->ForEachPhiInst( + [this, &branch_condition_operand, branch_instruction, + convergence_block_id, &header_block, ir_context, + &require_2d_boolean_vector, &require_3d_boolean_vector, + &require_4d_boolean_vector](opt::Instruction* phi_inst) { + assert(phi_inst->NumInOperands() == 4 && + "We are going to replace an OpPhi with an OpSelect. This " + "only makes sense if the block has two distinct " + "predecessors."); + // We are going to replace the OpPhi with an OpSelect. By default, + // the condition for the OpSelect will be the branch condition's + // operand. However, if the OpPhi has vector result type we may need + // to use a boolean vector as the condition instead. + opt::Operand selector_operand = branch_condition_operand; + opt::Instruction* type_inst = + ir_context->get_def_use_mgr()->GetDef(phi_inst->type_id()); + if (type_inst->opcode() == SpvOpTypeVector) { + uint32_t dimension = type_inst->GetSingleWordInOperand(1); + switch (dimension) { + case 2: + // The OpPhi's result type is a 2D vector. If a fresh id for a + // bvec2 selector was provided then we should use it as the + // OpSelect's condition, and note the fact that we will need to + // add an instruction to bring this bvec2 into existence. + if (message_.fresh_id_for_bvec2_selector() != 0) { + selector_operand = {SPV_OPERAND_TYPE_ID, + {message_.fresh_id_for_bvec2_selector()}}; + require_2d_boolean_vector = true; + } + break; + case 3: + // Similar to the 2D case. + if (message_.fresh_id_for_bvec3_selector() != 0) { + selector_operand = {SPV_OPERAND_TYPE_ID, + {message_.fresh_id_for_bvec3_selector()}}; + require_3d_boolean_vector = true; + } + break; + case 4: + // Similar to the 2D case. + if (message_.fresh_id_for_bvec4_selector() != 0) { + selector_operand = {SPV_OPERAND_TYPE_ID, + {message_.fresh_id_for_bvec4_selector()}}; + require_4d_boolean_vector = true; + } + break; + default: + assert(dimension == 4 && "Invalid vector dimension."); + break; + } + } + std::vector operands; + operands.emplace_back(selector_operand); + + uint32_t branch_instruction_true_block_id = + branch_instruction.GetSingleWordInOperand(1); + uint32_t branch_instruction_false_block_id = + branch_instruction.GetSingleWordInOperand(2); + + // The OpPhi takes values from two distinct predecessors. One + // predecessor is associated with the "true" path of the conditional + // we are flattening, the other with the "false" path, but these + // predecessors can appear in either order as operands to the OpPhi + // instruction. We determine in which order the OpPhi inputs should + // appear as OpSelect arguments by first checking whether the + // convergence block is a direct successor of the selection header, and + // otherwise checking dominance of the true and false immediate + // successors of the header block. + if (branch_instruction_true_block_id == convergence_block_id) { + // The branch instruction's true block is the convergence block. This + // means that the OpPhi's value associated with the branch + // instruction's block should the "true" result of the OpSelect. + assert(branch_instruction_false_block_id != convergence_block_id && + "Control should not reach here if both branches target the " + "convergence block."); + if (phi_inst->GetSingleWordInOperand(1) == + message_.header_block_id()) { + operands.emplace_back(phi_inst->GetInOperand(0)); + operands.emplace_back(phi_inst->GetInOperand(2)); + } else { + assert(phi_inst->GetSingleWordInOperand(3) == + message_.header_block_id() && + "Since the convergence block has the header block as one of " + "two predecessors, if it is not handled by the first pair " + "of operands of this OpPhi instruction it should be handled " + "by the second pair."); + operands.emplace_back(phi_inst->GetInOperand(2)); + operands.emplace_back(phi_inst->GetInOperand(0)); + } + } else if (branch_instruction_false_block_id == convergence_block_id) { + // The branch instruction's false block is the convergence block. This + // means that the OpPhi's value associated with the branch + // instruction's block should the "false" result of the OpSelect. + if (phi_inst->GetSingleWordInOperand(1) == + message_.header_block_id()) { + operands.emplace_back(phi_inst->GetInOperand(2)); + operands.emplace_back(phi_inst->GetInOperand(0)); + } else { + assert(phi_inst->GetSingleWordInOperand(3) == + message_.header_block_id() && + "Since the convergence block has the header block as one of " + "two predecessors, if it is not handled by the first pair " + "of operands of this OpPhi instruction it should be handled " + "by the second pair."); + operands.emplace_back(phi_inst->GetInOperand(0)); + operands.emplace_back(phi_inst->GetInOperand(2)); + } + } else if (ir_context->GetDominatorAnalysis(header_block.GetParent()) + ->Dominates(branch_instruction_true_block_id, + phi_inst->GetSingleWordInOperand(1))) { + // The "true" branch of the conditional is handled first in the + // OpPhi's operands; we thus provide operands to OpSelect in the same + // order that they appear in the OpPhi. + operands.emplace_back(phi_inst->GetInOperand(0)); + operands.emplace_back(phi_inst->GetInOperand(2)); + } else { + // The "false" branch of the conditional is handled first in the + // OpPhi's operands; we thus provide operands to OpSelect in reverse + // of the order that they appear in the OpPhi. + operands.emplace_back(phi_inst->GetInOperand(2)); + operands.emplace_back(phi_inst->GetInOperand(0)); + } + phi_inst->SetOpcode(SpvOpSelect); + phi_inst->SetInOperands(std::move(operands)); + }); + + // Add boolean vector instructions to the start of the block as required. + if (require_2d_boolean_vector) { + AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec2_selector(), + 2, branch_condition_operand, ir_context, + convergence_block); + } + if (require_3d_boolean_vector) { + AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec3_selector(), + 3, branch_condition_operand, ir_context, + convergence_block); + } + if (require_4d_boolean_vector) { + AddBooleanVectorConstructorToBlock(message_.fresh_id_for_bvec4_selector(), + 4, branch_condition_operand, ir_context, + convergence_block); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.h b/third_party/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.h new file mode 100644 index 0000000..e8cb414 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_flatten_conditional_branch.h @@ -0,0 +1,157 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_ +#define SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationFlattenConditionalBranch : public Transformation { + public: + explicit TransformationFlattenConditionalBranch( + const protobufs::TransformationFlattenConditionalBranch& message); + + TransformationFlattenConditionalBranch( + uint32_t header_block_id, bool true_branch_first, + uint32_t fresh_id_for_bvec2_selector, + uint32_t fresh_id_for_bvec3_selector, + uint32_t fresh_id_for_bvec4_selector, + const std::vector& + side_effect_wrappers_info); + + // - |message_.header_block_id| must be the label id of a reachable selection + // header, which ends with an OpBranchConditional instruction. + // - The condition of the OpBranchConditional instruction must not be an + // irrelevant id. + // - The header block and the merge block must describe a single-entry, + // single-exit region. + // - The region must not contain barrier or OpSampledImage instructions. + // - The region must not contain selection or loop constructs. + // - For each instruction that requires additional fresh ids, then: + // - if the instruction is mapped to the required ids for enclosing it by + // |message_.side_effect_wrapper_info|, these must be valid (the + // fresh ids must be non-zero, fresh and distinct); + // - if there is no such mapping, the transformation context must have + // overflow ids available. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Flattens the selection construct with header |message_.header_block_id|, + // changing any OpPhi in the block where the flow converges to OpSelect and + // enclosing any instruction with side effects in conditionals so that + // they are only executed when they should. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if the conditional headed by |header| can be flattened, + // according to the conditions of the IsApplicable method, assuming that + // enough fresh ids would be provided. In this case, it fills the + // |instructions_that_need_ids| set with all the instructions that would + // require fresh ids. + // Returns false otherwise. + // Assumes that |header| is the header of a conditional, so its last two + // instructions are OpSelectionMerge and OpBranchConditional. + static bool GetProblematicInstructionsIfConditionalCanBeFlattened( + opt::IRContext* ir_context, opt::BasicBlock* header, + const TransformationContext& transformation_context, + std::set* instructions_that_need_ids); + + // Returns true iff the given instruction needs a placeholder to be enclosed + // inside a conditional. So, it returns: + // - true if the instruction has a non-void result id, + // - false if the instruction does not have a result id or has a void result + // id. + // Assumes that the instruction has side effects, requiring it to be enclosed + // inside a conditional, and that it can be enclosed inside a conditional + // keeping the module valid. Assumes that, if the instruction has a void + // result type, its result id is not used in the module. + static bool InstructionNeedsPlaceholder(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + // Returns true if and only if the SPIR-V version is such that the arguments + // to OpSelect are restricted to only scalars, pointers (if the appropriate + // capability is enabled) and component-wise vectors. + static bool OpSelectArgumentsAreRestricted(opt::IRContext* ir_context); + + // Find the first block where flow converges (it is not necessarily the merge + // block) by walking the true branch until reaching a block that post- + // dominates the header. + // This is necessary because a potential common set of blocks at the end of + // the construct should not be duplicated. + static uint32_t FindConvergenceBlock(opt::IRContext* ir_context, + const opt::BasicBlock& header_block); + + private: + // Returns an unordered_map mapping instructions to the info required to + // enclose them inside a conditional. It maps the instructions to the + // corresponding entry in |message_.side_effect_wrapper_info|. + std::unordered_map + GetInstructionsToWrapperInfo(opt::IRContext* ir_context) const; + + // Splits the given block, adding a new selection construct so that the given + // instruction is only executed if the boolean value of |condition_id| matches + // the value of |exec_if_cond_true|. + // Assumes that all parameters are consistent. + // 2 fresh ids are required if the instruction does not have a result id (the + // first two ids in |wrapper_info| must be valid fresh ids), 5 otherwise. + // Returns the merge block created. + // + // |dead_blocks| and |irrelevant_ids| are used to record the ids of blocks + // and instructions for which dead block and irrelevant id facts should + // ultimately be created. + static opt::BasicBlock* EncloseInstructionInConditional( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + opt::BasicBlock* block, opt::Instruction* instruction, + const protobufs::SideEffectWrapperInfo& wrapper_info, + uint32_t condition_id, bool exec_if_cond_true, + std::vector* dead_blocks, + std::vector* irrelevant_ids); + + // Turns every OpPhi instruction of |convergence_block| -- the convergence + // block for |header_block| (both in |ir_context|) into an OpSelect + // instruction. + void RewriteOpPhiInstructionsAtConvergenceBlock( + const opt::BasicBlock& header_block, uint32_t convergence_block_id, + opt::IRContext* ir_context) const; + + // Adds an OpCompositeExtract instruction to the start of |block| in + // |ir_context|, with result id given by |fresh_id|. The instruction will + // make a |dimension|-dimensional boolean vector with + // |branch_condition_operand| at every component. + static void AddBooleanVectorConstructorToBlock( + uint32_t fresh_id, uint32_t dimension, + const opt::Operand& branch_condition_operand, opt::IRContext* ir_context, + opt::BasicBlock* block); + + // Returns true if the given instruction either has no side effects or it can + // be handled by being enclosed in a conditional. + static bool InstructionCanBeHandled(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + protobufs::TransformationFlattenConditionalBranch message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_FLATTEN_CONDITIONAL_BRANCH_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_function_call.cpp b/third_party/spirv-tools/source/fuzz/transformation_function_call.cpp new file mode 100644 index 0000000..ec95c32 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_function_call.cpp @@ -0,0 +1,193 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_function_call.h" + +#include "source/fuzz/call_graph.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationFunctionCall::TransformationFunctionCall( + const spvtools::fuzz::protobufs::TransformationFunctionCall& message) + : message_(message) {} + +TransformationFunctionCall::TransformationFunctionCall( + uint32_t fresh_id, uint32_t callee_id, + const std::vector& argument_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_callee_id(callee_id); + for (auto argument : argument_id) { + message_.add_argument_id(argument); + } + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationFunctionCall::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The result id must be fresh + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // The function must exist + auto callee_inst = + ir_context->get_def_use_mgr()->GetDef(message_.callee_id()); + if (!callee_inst || callee_inst->opcode() != SpvOpFunction) { + return false; + } + + // The function must not be an entry point + if (fuzzerutil::FunctionIsEntryPoint(ir_context, message_.callee_id())) { + return false; + } + + auto callee_type_inst = ir_context->get_def_use_mgr()->GetDef( + callee_inst->GetSingleWordInOperand(1)); + assert(callee_type_inst->opcode() == SpvOpTypeFunction && + "Bad function type."); + + // The number of expected function arguments must match the number of given + // arguments. The number of expected arguments is one less than the function + // type's number of input operands, as one operand is for the return type. + if (callee_type_inst->NumInOperands() - 1 != + static_cast(message_.argument_id().size())) { + return false; + } + + // The instruction descriptor must refer to a position where it is valid to + // insert the call + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!insert_before) { + return false; + } + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpFunctionCall, + insert_before)) { + return false; + } + + auto block = ir_context->get_instr_block(insert_before); + auto enclosing_function = block->GetParent(); + + // If the block is not dead, the function must be livesafe + bool block_is_dead = + transformation_context.GetFactManager()->BlockIsDead(block->id()); + if (!block_is_dead && + !transformation_context.GetFactManager()->FunctionIsLivesafe( + message_.callee_id())) { + return false; + } + + // The ids must all match and have the right types and satisfy rules on + // pointers. If the block is not dead, pointers must be arbitrary. + for (uint32_t arg_index = 0; + arg_index < static_cast(message_.argument_id().size()); + arg_index++) { + opt::Instruction* arg_inst = + ir_context->get_def_use_mgr()->GetDef(message_.argument_id(arg_index)); + if (!arg_inst) { + // The given argument does not correspond to an instruction. + return false; + } + if (!arg_inst->type_id()) { + // The given argument does not have a type; it is thus not suitable. + } + if (arg_inst->type_id() != + callee_type_inst->GetSingleWordInOperand(arg_index + 1)) { + // Argument type mismatch. + return false; + } + opt::Instruction* arg_type_inst = + ir_context->get_def_use_mgr()->GetDef(arg_inst->type_id()); + if (arg_type_inst->opcode() == SpvOpTypePointer) { + switch (arg_inst->opcode()) { + case SpvOpFunctionParameter: + case SpvOpVariable: + // These are OK + break; + default: + // Other pointer ids cannot be passed as parameters + return false; + } + if (!block_is_dead && + !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + arg_inst->result_id())) { + // This is not a dead block, so pointer parameters passed to the called + // function might really have their contents modified. We thus require + // such pointers to be to arbitrary-valued variables, which this is not. + return false; + } + } + + // The argument id needs to be available (according to dominance rules) at + // the point where the call will occur. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + arg_inst->result_id())) { + return false; + } + } + + // Introducing the call must not lead to recursion. + if (message_.callee_id() == enclosing_function->result_id()) { + // This would be direct recursion. + return false; + } + // Ensure the call would not lead to indirect recursion. + return !CallGraph(ir_context) + .GetIndirectCallees(message_.callee_id()) + .count(block->GetParent()->result_id()); +} + +void TransformationFunctionCall::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Update the module's bound to reflect the fresh id for the result of the + // function call. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // Get the return type of the function being called. + uint32_t return_type = + ir_context->get_def_use_mgr()->GetDef(message_.callee_id())->type_id(); + // Populate the operands to the call instruction, with the function id and the + // arguments. + opt::Instruction::OperandList operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {message_.callee_id()}}); + for (auto arg : message_.argument_id()) { + operands.push_back({SPV_OPERAND_TYPE_ID, {arg}}); + } + // Insert the function call before the instruction specified in the message. + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpFunctionCall, return_type, message_.fresh_id(), + operands)); + // Invalidate all analyses since we have changed the module. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationFunctionCall::ToMessage() const { + protobufs::Transformation result; + *result.mutable_function_call() = message_; + return result; +} + +std::unordered_set TransformationFunctionCall::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_function_call.h b/third_party/spirv-tools/source/fuzz/transformation_function_call.h new file mode 100644 index 0000000..e220d83 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_function_call.h @@ -0,0 +1,69 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationFunctionCall : public Transformation { + public: + explicit TransformationFunctionCall( + const protobufs::TransformationFunctionCall& message); + + TransformationFunctionCall( + uint32_t fresh_id, uint32_t callee_id, + const std::vector& argument_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh + // - |message_.instruction_to_insert_before| must identify an instruction + // before which an OpFunctionCall can be legitimately inserted + // - |message_.function_id| must be the id of a function, and calling the + // function before the identified instruction must not introduce recursion + // - |message_.arg_id| must provide suitable arguments for the function call + // (they must have the right types and be available according to dominance + // rules) + // - If the insertion point is not in a dead block then |message_function_id| + // must refer to a livesafe function, and every pointer argument in + // |message_.arg_id| must refer to an arbitrary-valued variable + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction of the form: + // |fresh_id| = OpFunctionCall %type |callee_id| |arg_id...| + // before |instruction_to_insert_before|, where %type is the return type of + // |callee_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationFunctionCall message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_FUNCTION_CALL_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_inline_function.cpp b/third_party/spirv-tools/source/fuzz/transformation_inline_function.cpp new file mode 100644 index 0000000..f58b123 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_inline_function.cpp @@ -0,0 +1,365 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_inline_function.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationInlineFunction::TransformationInlineFunction( + const spvtools::fuzz::protobufs::TransformationInlineFunction& message) + : message_(message) {} + +TransformationInlineFunction::TransformationInlineFunction( + uint32_t function_call_id, + const std::map& result_id_map) { + message_.set_function_call_id(function_call_id); + *message_.mutable_result_id_map() = + fuzzerutil::MapToRepeatedUInt32Pair(result_id_map); +} + +bool TransformationInlineFunction::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The values in the |message_.result_id_map| must be all fresh and all + // distinct. + const auto result_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.result_id_map()); + std::set ids_used_by_this_transformation; + for (auto& pair : result_id_map) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second, ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + + // |function_call_instruction| must be suitable for inlining. + auto* function_call_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.function_call_id()); + if (!IsSuitableForInlining(ir_context, function_call_instruction)) { + return false; + } + + // |function_call_instruction| must be the penultimate instruction in its + // block and its block termination instruction must be an OpBranch. This + // avoids the case where the penultimate instruction is an OpLoopMerge, which + // would make the back-edge block not branch to the loop header. + auto* function_call_instruction_block = + ir_context->get_instr_block(function_call_instruction); + if (function_call_instruction != + &*--function_call_instruction_block->tail() || + function_call_instruction_block->terminator()->opcode() != SpvOpBranch) { + return false; + } + + auto* called_function = fuzzerutil::FindFunction( + ir_context, function_call_instruction->GetSingleWordInOperand(0)); + for (auto& block : *called_function) { + // Since the entry block label will not be inlined, only the remaining + // labels must have a corresponding value in the map. + if (&block != &*called_function->entry() && + !result_id_map.count(block.id()) && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + + // |result_id_map| must have an entry for every result id in the called + // function. + for (auto& instruction : block) { + // If |instruction| has result id, then it must have a mapped id in + // |result_id_map|. + if (instruction.HasResultId() && + !result_id_map.count(instruction.result_id()) && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } + } + + // |result_id_map| must not contain an entry for any parameter of the function + // that is being inlined. + bool found_entry_for_parameter = false; + called_function->ForEachParam( + [&result_id_map, &found_entry_for_parameter](opt::Instruction* param) { + if (result_id_map.count(param->result_id())) { + found_entry_for_parameter = true; + } + }); + return !found_entry_for_parameter; +} + +void TransformationInlineFunction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto* function_call_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.function_call_id()); + auto* caller_function = + ir_context->get_instr_block(function_call_instruction)->GetParent(); + auto* called_function = fuzzerutil::FindFunction( + ir_context, function_call_instruction->GetSingleWordInOperand(0)); + std::map result_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.result_id_map()); + + // If there are gaps in the result id map, fill them using overflow ids. + for (auto& block : *called_function) { + if (&block != &*called_function->entry() && + !result_id_map.count(block.id())) { + result_id_map.insert( + {block.id(), + transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); + } + for (auto& instruction : block) { + // If |instruction| has result id, then it must have a mapped id in + // |result_id_map|. + if (instruction.HasResultId() && + !result_id_map.count(instruction.result_id())) { + result_id_map.insert({instruction.result_id(), + transformation_context->GetOverflowIdSource() + ->GetNextOverflowId()}); + } + } + } + + auto* successor_block = ir_context->cfg()->block( + ir_context->get_instr_block(function_call_instruction) + ->terminator() + ->GetSingleWordInOperand(0)); + + // Inline the |called_function| entry block. + for (auto& entry_block_instruction : *called_function->entry()) { + opt::Instruction* inlined_instruction; + + if (entry_block_instruction.opcode() == SpvOpVariable) { + // All OpVariable instructions in a function must be in the first block + // in the function. + inlined_instruction = caller_function->begin()->begin()->InsertBefore( + std::unique_ptr( + entry_block_instruction.Clone(ir_context))); + } else { + inlined_instruction = function_call_instruction->InsertBefore( + std::unique_ptr( + entry_block_instruction.Clone(ir_context))); + } + + AdaptInlinedInstruction(result_id_map, ir_context, inlined_instruction); + } + + // If the function call's successor block contains OpPhi instructions that + // refer to the block containing the call then these will need to be rewritten + // to instead refer to the block associated with "returning" from the inlined + // function, as this block will be the predecessor of what used to be the + // function call's successor block. We look out for this block. + uint32_t new_return_block_id = 0; + + // Inline the |called_function| non-entry blocks. + for (auto& block : *called_function) { + if (&block == &*called_function->entry()) { + continue; + } + + // Check whether this is the function's return block. Take note if it is, + // so that OpPhi instructions in the successor of the original function call + // block can be re-written. + if (block.terminator()->IsReturn()) { + assert(new_return_block_id == 0 && + "There should be only one return block."); + new_return_block_id = result_id_map.at(block.id()); + } + + auto* cloned_block = block.Clone(ir_context); + cloned_block = caller_function->InsertBasicBlockBefore( + std::unique_ptr(cloned_block), successor_block); + cloned_block->SetParent(caller_function); + cloned_block->GetLabel()->SetResultId(result_id_map.at(cloned_block->id())); + fuzzerutil::UpdateModuleIdBound(ir_context, cloned_block->id()); + + for (auto& inlined_instruction : *cloned_block) { + AdaptInlinedInstruction(result_id_map, ir_context, &inlined_instruction); + } + } + + opt::BasicBlock* block_containing_function_call = + ir_context->get_instr_block(function_call_instruction); + + assert(((new_return_block_id == 0) == + called_function->entry()->terminator()->IsReturn()) && + "We should have found a return block unless the function being " + "inlined returns in its first block."); + if (new_return_block_id != 0) { + // Rewrite any OpPhi instructions in the successor block so that they refer + // to the new return block instead of the block that originally contained + // the function call. + ir_context->get_def_use_mgr()->ForEachUse( + block_containing_function_call->id(), + [ir_context, new_return_block_id, successor_block]( + opt::Instruction* use_instruction, uint32_t operand_index) { + if (use_instruction->opcode() == SpvOpPhi && + ir_context->get_instr_block(use_instruction) == successor_block) { + use_instruction->SetOperand(operand_index, {new_return_block_id}); + } + }); + } + + // Removes the function call instruction and its block termination instruction + // from |caller_function|. + ir_context->KillInst(block_containing_function_call->terminator()); + ir_context->KillInst(function_call_instruction); + + // Since the SPIR-V module has changed, no analyses must be validated. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationInlineFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_inline_function() = message_; + return result; +} + +bool TransformationInlineFunction::IsSuitableForInlining( + opt::IRContext* ir_context, opt::Instruction* function_call_instruction) { + // |function_call_instruction| must be defined and must be an OpFunctionCall + // instruction. + if (!function_call_instruction || + function_call_instruction->opcode() != SpvOpFunctionCall) { + return false; + } + + // If |function_call_instruction| return type is void, then + // |function_call_instruction| must not have uses. + if (ir_context->get_type_mgr() + ->GetType(function_call_instruction->type_id()) + ->AsVoid() && + ir_context->get_def_use_mgr()->NumUses(function_call_instruction) != 0) { + return false; + } + + // |called_function| must not have an early return. + auto called_function = fuzzerutil::FindFunction( + ir_context, function_call_instruction->GetSingleWordInOperand(0)); + if (called_function->HasEarlyReturn()) { + return false; + } + + // |called_function| must not use OpKill or OpUnreachable. + if (fuzzerutil::FunctionContainsOpKillOrUnreachable(*called_function)) { + return false; + } + + return true; +} + +void TransformationInlineFunction::AdaptInlinedInstruction( + const std::map& result_id_map, + opt::IRContext* ir_context, + opt::Instruction* instruction_to_be_inlined) const { + auto* function_call_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.function_call_id()); + auto* called_function = fuzzerutil::FindFunction( + ir_context, function_call_instruction->GetSingleWordInOperand(0)); + + const auto* function_call_block = + ir_context->get_instr_block(function_call_instruction); + assert(function_call_block && "OpFunctionCall must belong to some block"); + + // Replaces the operand ids with their mapped result ids. + instruction_to_be_inlined->ForEachInId( + [called_function, function_call_instruction, &result_id_map, + function_call_block](uint32_t* id) { + // We are not inlining the entry block of the |called_function|. + // + // We must check this condition first since we can't use the fresh id + // from |result_id_map| even if it has one. This is because that fresh + // id will never be added to the module since entry blocks are not + // inlined. + if (*id == called_function->entry()->id()) { + *id = function_call_block->id(); + return; + } + + // If |id| is mapped, then set it to its mapped value. + if (result_id_map.count(*id)) { + *id = result_id_map.at(*id); + return; + } + + uint32_t parameter_index = 0; + called_function->ForEachParam( + [id, function_call_instruction, + ¶meter_index](opt::Instruction* parameter_instruction) { + // If the id is a function parameter, then set it to the + // parameter value passed in the function call instruction. + if (*id == parameter_instruction->result_id()) { + // We do + 1 because the first in-operand for OpFunctionCall is + // the function id that is being called. + *id = function_call_instruction->GetSingleWordInOperand( + parameter_index + 1); + } + parameter_index++; + }); + }); + + // If |instruction_to_be_inlined| has result id, then set it to its mapped + // value. + if (instruction_to_be_inlined->HasResultId()) { + assert(result_id_map.count(instruction_to_be_inlined->result_id()) && + "Result id must be mapped to a fresh id."); + instruction_to_be_inlined->SetResultId( + result_id_map.at(instruction_to_be_inlined->result_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, + instruction_to_be_inlined->result_id()); + } + + // The return instruction will be changed into an OpBranch to the basic + // block that follows the block containing the function call. + if (spvOpcodeIsReturn(instruction_to_be_inlined->opcode())) { + uint32_t successor_block_id = + ir_context->get_instr_block(function_call_instruction) + ->terminator() + ->GetSingleWordInOperand(0); + switch (instruction_to_be_inlined->opcode()) { + case SpvOpReturn: + instruction_to_be_inlined->AddOperand( + {SPV_OPERAND_TYPE_ID, {successor_block_id}}); + break; + case SpvOpReturnValue: { + instruction_to_be_inlined->InsertBefore(MakeUnique( + ir_context, SpvOpCopyObject, function_call_instruction->type_id(), + function_call_instruction->result_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, + {instruction_to_be_inlined->GetSingleWordOperand(0)}}}))); + instruction_to_be_inlined->SetInOperand(0, {successor_block_id}); + break; + } + default: + break; + } + instruction_to_be_inlined->SetOpcode(SpvOpBranch); + } +} + +std::unordered_set TransformationInlineFunction::GetFreshIds() const { + std::unordered_set result; + for (auto& pair : message_.result_id_map()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_inline_function.h b/third_party/spirv-tools/source/fuzz/transformation_inline_function.h new file mode 100644 index 0000000..8105d92 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_inline_function.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_INLINE_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_INLINE_FUNCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationInlineFunction : public Transformation { + public: + explicit TransformationInlineFunction( + const protobufs::TransformationInlineFunction& message); + + TransformationInlineFunction( + uint32_t function_call_id, + const std::map& result_id_map); + + // - |message_.result_id_map| must map the instructions of the called function + // to fresh ids, unless overflow ids are available. + // - |message_.function_call_id| must be an OpFunctionCall instruction. + // It must not have an early return and must not use OpUnreachable or + // OpKill. This is to guard against making the module invalid when the + // caller is inside a continue construct. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3735): + // Allow functions that use OpKill or OpUnreachable to be inlined if the + // function call is not part of a continue construct. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the OpFunctionCall instruction, identified by + // |message_.function_call_id|, with a copy of the function's body. + // |message_.result_id_map| is used to provide fresh ids for duplicate + // instructions. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if |function_call_instruction| is defined, is an + // OpFunctionCall instruction, has no uses if its return type is void, has no + // early returns and has no uses of OpKill or OpUnreachable. + static bool IsSuitableForInlining( + opt::IRContext* ir_context, opt::Instruction* function_call_instruction); + + private: + protobufs::TransformationInlineFunction message_; + + // Inline |instruction_to_be_inlined| by setting its ids to the corresponding + // ids in |result_id_map|. + void AdaptInlinedInstruction( + const std::map& result_id_map, + opt::IRContext* ir_context, opt::Instruction* instruction) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_INLINE_FUNCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp b/third_party/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp new file mode 100644 index 0000000..ed7358f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_invert_comparison_operator.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_invert_comparison_operator.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationInvertComparisonOperator::TransformationInvertComparisonOperator( + protobufs::TransformationInvertComparisonOperator message) + : message_(std::move(message)) {} + +TransformationInvertComparisonOperator::TransformationInvertComparisonOperator( + uint32_t operator_id, uint32_t fresh_id) { + message_.set_operator_id(operator_id); + message_.set_fresh_id(fresh_id); +} + +bool TransformationInvertComparisonOperator::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.operator_id| must be valid and inversion must be supported for + // it. + auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id()); + if (!inst || !IsInversionSupported(inst->opcode())) { + return false; + } + + // Check that we can insert negation instruction. + auto* block = ir_context->get_instr_block(inst); + assert(block && "Instruction must have a basic block"); + + auto iter = fuzzerutil::GetIteratorForInstruction(block, inst); + ++iter; + assert(iter != block->end() && "Instruction can't be the last in the block"); + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) && + "Can't insert negation after comparison operator"); + + // |message_.fresh_id| must be fresh. + return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()); +} + +void TransformationInvertComparisonOperator::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.operator_id()); + assert(inst && "Result id of an operator is invalid"); + + // Insert negation after |inst|. + auto iter = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(inst), inst); + ++iter; + + iter.InsertBefore(MakeUnique( + ir_context, SpvOpLogicalNot, inst->type_id(), inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})); + + // Change the result id of the original operator to |fresh_id|. + inst->SetResultId(message_.fresh_id()); + + // Invert the operator. + inst->SetOpcode(InvertOpcode(inst->opcode())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +bool TransformationInvertComparisonOperator::IsInversionSupported( + SpvOp opcode) { + switch (opcode) { + case SpvOpSGreaterThan: + case SpvOpSGreaterThanEqual: + case SpvOpSLessThan: + case SpvOpSLessThanEqual: + case SpvOpUGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpULessThan: + case SpvOpULessThanEqual: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + return true; + default: + return false; + } +} + +SpvOp TransformationInvertComparisonOperator::InvertOpcode(SpvOp opcode) { + assert(IsInversionSupported(opcode) && "Inversion must be supported"); + + switch (opcode) { + case SpvOpSGreaterThan: + return SpvOpSLessThanEqual; + case SpvOpSGreaterThanEqual: + return SpvOpSLessThan; + case SpvOpSLessThan: + return SpvOpSGreaterThanEqual; + case SpvOpSLessThanEqual: + return SpvOpSGreaterThan; + case SpvOpUGreaterThan: + return SpvOpULessThanEqual; + case SpvOpUGreaterThanEqual: + return SpvOpULessThan; + case SpvOpULessThan: + return SpvOpUGreaterThanEqual; + case SpvOpULessThanEqual: + return SpvOpUGreaterThan; + case SpvOpIEqual: + return SpvOpINotEqual; + case SpvOpINotEqual: + return SpvOpIEqual; + case SpvOpFOrdEqual: + return SpvOpFUnordNotEqual; + case SpvOpFUnordEqual: + return SpvOpFOrdNotEqual; + case SpvOpFOrdNotEqual: + return SpvOpFUnordEqual; + case SpvOpFUnordNotEqual: + return SpvOpFOrdEqual; + case SpvOpFOrdLessThan: + return SpvOpFUnordGreaterThanEqual; + case SpvOpFUnordLessThan: + return SpvOpFOrdGreaterThanEqual; + case SpvOpFOrdLessThanEqual: + return SpvOpFUnordGreaterThan; + case SpvOpFUnordLessThanEqual: + return SpvOpFOrdGreaterThan; + case SpvOpFOrdGreaterThan: + return SpvOpFUnordLessThanEqual; + case SpvOpFUnordGreaterThan: + return SpvOpFOrdLessThanEqual; + case SpvOpFOrdGreaterThanEqual: + return SpvOpFUnordLessThan; + case SpvOpFUnordGreaterThanEqual: + return SpvOpFOrdLessThan; + default: + // The program will fail in the debug mode because of the assertion + // at the beginning of the function. + return SpvOpNop; + } +} + +protobufs::Transformation TransformationInvertComparisonOperator::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_invert_comparison_operator() = message_; + return result; +} + +std::unordered_set +TransformationInvertComparisonOperator::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h b/third_party/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h new file mode 100644 index 0000000..f00f62b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_invert_comparison_operator.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationInvertComparisonOperator : public Transformation { + public: + explicit TransformationInvertComparisonOperator( + protobufs::TransformationInvertComparisonOperator message); + + TransformationInvertComparisonOperator(uint32_t operator_id, + uint32_t fresh_id); + + // - |operator_id| should be a result id of some instruction for which + // IsInversionSupported returns true. + // - |fresh_id| must be a fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inverts the opcode of the instruction with result id |operator_id| (e.g >= + // becomes <) and inserts OpLogicalNot instruction after |operator_id|. Also, + // changes the result id of OpLogicalNot to |operator_id| and the result id of + // the inverted operator to |fresh_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if |opcode| is supported by this transformation. + static bool IsInversionSupported(SpvOp opcode); + + private: + // Returns an inverted |opcode| (e.g. < becomes >=, == becomes != etc.) + static SpvOp InvertOpcode(SpvOp opcode); + + protobufs::TransformationInvertComparisonOperator message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_INVERT_COMPARISON_OPERATOR_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_load.cpp b/third_party/spirv-tools/source/fuzz/transformation_load.cpp new file mode 100644 index 0000000..f8b3513 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_load.cpp @@ -0,0 +1,106 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_load.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationLoad::TransformationLoad( + const spvtools::fuzz::protobufs::TransformationLoad& message) + : message_(message) {} + +TransformationLoad::TransformationLoad( + uint32_t fresh_id, uint32_t pointer_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_fresh_id(fresh_id); + message_.set_pointer_id(pointer_id); + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationLoad::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The result id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // The pointer must exist and have a type. + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + // The type must indeed be a pointer type. + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); + assert(pointer_type && "Type id must be defined."); + if (pointer_type->opcode() != SpvOpTypePointer) { + return false; + } + // We do not want to allow loading from null or undefined pointers, as it is + // not clear how punishing the consequences of doing so are from a semantics + // point of view. + switch (pointer->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + return false; + default: + break; + } + + // Determine which instruction we should be inserting before. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + // It must exist, ... + if (!insert_before) { + return false; + } + // ... and it must be legitimate to insert a store before it. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, insert_before)) { + return false; + } + + // The pointer needs to be available at the insertion point. + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + message_.pointer_id()); +} + +void TransformationLoad::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + uint32_t result_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpLoad, result_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}}))); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationLoad::ToMessage() const { + protobufs::Transformation result; + *result.mutable_load() = message_; + return result; +} + +std::unordered_set TransformationLoad::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_load.h b/third_party/spirv-tools/source/fuzz/transformation_load.h new file mode 100644 index 0000000..683bba5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_load.h @@ -0,0 +1,63 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ +#define SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationLoad : public Transformation { + public: + explicit TransformationLoad(const protobufs::TransformationLoad& message); + + TransformationLoad( + uint32_t fresh_id, uint32_t pointer_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.fresh_id| must be fresh + // - |message_.pointer_id| must be the id of a pointer + // - The pointer must not be OpConstantNull or OpUndef + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is valid to insert an OpLoad, and where + // |message_.pointer_id| is available (according to dominance rules) + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction of the form: + // |message_.fresh_id| = OpLoad %type |message_.pointer_id| + // before the instruction identified by + // |message_.instruction_to_insert_before|, where %type is the pointer's + // pointee type. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationLoad message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_LOAD_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp b/third_party/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp new file mode 100644 index 0000000..d6d5140 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_make_vector_operation_dynamic.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationMakeVectorOperationDynamic:: + TransformationMakeVectorOperationDynamic( + const spvtools::fuzz::protobufs:: + TransformationMakeVectorOperationDynamic& message) + : message_(message) {} + +TransformationMakeVectorOperationDynamic:: + TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id, + uint32_t constant_index_id) { + message_.set_instruction_result_id(instruction_result_id); + message_.set_constant_index_id(constant_index_id); +} + +bool TransformationMakeVectorOperationDynamic::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |instruction| must be a vector operation. + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + if (!IsVectorOperation(ir_context, instruction)) { + return false; + } + + // |constant_index_instruction| must be defined as an integer instruction. + auto constant_index_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.constant_index_id()); + if (!constant_index_instruction || !constant_index_instruction->type_id() || + !ir_context->get_type_mgr() + ->GetType(constant_index_instruction->type_id()) + ->AsInteger()) { + return false; + } + + return true; +} + +void TransformationMakeVectorOperationDynamic::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.instruction_result_id()); + + // The OpVectorInsertDynamic instruction has the vector and component operands + // in reverse order in relation to the OpCompositeInsert corresponding + // operands. + if (instruction->opcode() == SpvOpCompositeInsert) { + std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1)); + } + + // Sets the literal operand to the equivalent constant. + instruction->SetInOperand( + instruction->opcode() == SpvOpCompositeExtract ? 1 : 2, + {message_.constant_index_id()}); + + // Sets the |instruction| opcode to the corresponding vector dynamic opcode. + instruction->SetOpcode(instruction->opcode() == SpvOpCompositeExtract + ? SpvOpVectorExtractDynamic + : SpvOpVectorInsertDynamic); +} + +protobufs::Transformation TransformationMakeVectorOperationDynamic::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_make_vector_operation_dynamic() = message_; + return result; +} + +bool TransformationMakeVectorOperationDynamic::IsVectorOperation( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // |instruction| must be defined and must be an OpCompositeExtract/Insert + // instruction. + if (!instruction || (instruction->opcode() != SpvOpCompositeExtract && + instruction->opcode() != SpvOpCompositeInsert)) { + return false; + } + + // The composite must be a vector. + auto composite_instruction = + ir_context->get_def_use_mgr()->GetDef(instruction->GetSingleWordInOperand( + instruction->opcode() == SpvOpCompositeExtract ? 0 : 1)); + if (!ir_context->get_type_mgr() + ->GetType(composite_instruction->type_id()) + ->AsVector()) { + return false; + } + + return true; +} + +std::unordered_set +TransformationMakeVectorOperationDynamic::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h b/third_party/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h new file mode 100644 index 0000000..d1765c5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_make_vector_operation_dynamic.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMakeVectorOperationDynamic : public Transformation { + public: + explicit TransformationMakeVectorOperationDynamic( + const protobufs::TransformationMakeVectorOperationDynamic& message); + + TransformationMakeVectorOperationDynamic(uint32_t instruction_result_id, + uint32_t constant_index_id); + + // - |message_.instruction_result_id| must be the result id of an + // OpCompositeExtract/Insert instruction such that the composite operand is a + // vector. + // - |message_.constant_index_id| must be the result id of an integer + // instruction such that its value equals the indexing literal of the + // OpCompositeExtract/Insert instruction. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the OpCompositeExtract and OpCompositeInsert instructions with the + // OpVectorExtractDynamic and OpVectorInsertDynamic instructions. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Checks |instruction| is defined, is an OpCompositeExtract/Insert + // instruction and the composite operand is a vector. + static bool IsVectorOperation(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + protobufs::TransformationMakeVectorOperationDynamic message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MAKE_VECTOR_OPERATION_DYNAMIC_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_merge_blocks.cpp b/third_party/spirv-tools/source/fuzz/transformation_merge_blocks.cpp new file mode 100644 index 0000000..2a9e90c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_merge_blocks.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_merge_blocks.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/opt/block_merge_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationMergeBlocks::TransformationMergeBlocks( + const spvtools::fuzz::protobufs::TransformationMergeBlocks& message) + : message_(message) {} + +TransformationMergeBlocks::TransformationMergeBlocks(uint32_t block_id) { + message_.set_block_id(block_id); +} + +bool TransformationMergeBlocks::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto second_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.block_id()); + // The given block must exist. + if (!second_block) { + return false; + } + // The block must have just one predecessor. + auto predecessors = ir_context->cfg()->preds(second_block->id()); + if (predecessors.size() != 1) { + return false; + } + auto first_block = ir_context->cfg()->block(predecessors.at(0)); + + return opt::blockmergeutil::CanMergeWithSuccessor(ir_context, first_block); +} + +void TransformationMergeBlocks::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + auto second_block = + fuzzerutil::MaybeFindBlock(ir_context, message_.block_id()); + auto first_block = ir_context->cfg()->block( + ir_context->cfg()->preds(second_block->id()).at(0)); + + auto function = first_block->GetParent(); + // We need an iterator pointing to the predecessor, hence the loop. + for (auto bi = function->begin(); bi != function->end(); ++bi) { + if (bi->id() == first_block->id()) { + assert(opt::blockmergeutil::CanMergeWithSuccessor(ir_context, &*bi) && + "Because 'Apply' should only be invoked if 'IsApplicable' holds, " + "it must be possible to merge |bi| with its successor."); + opt::blockmergeutil::MergeWithSuccessor(ir_context, function, bi); + // Invalidate all analyses, since we have changed the module + // significantly. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + return; + } + } + assert(false && + "Control should not reach here - we should always find the desired " + "block"); +} + +protobufs::Transformation TransformationMergeBlocks::ToMessage() const { + protobufs::Transformation result; + *result.mutable_merge_blocks() = message_; + return result; +} + +std::unordered_set TransformationMergeBlocks::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_merge_blocks.h b/third_party/spirv-tools/source/fuzz/transformation_merge_blocks.h new file mode 100644 index 0000000..d9a0ca0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_merge_blocks.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMergeBlocks : public Transformation { + public: + explicit TransformationMergeBlocks( + const protobufs::TransformationMergeBlocks& message); + + TransformationMergeBlocks(uint32_t block_id); + + // - |message_.block_id| must be the id of a block, b + // - b must have a single predecessor, a + // - b must be the sole successor of a + // - Replacing a with the merge of a and b (and removing b) must lead to a + // valid module + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // The contents of b are merged into a, and a's terminator is replaced with + // the terminator of b. Block b is removed from the module. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationMergeBlocks message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MERGE_BLOCKS_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_merge_function_returns.cpp b/third_party/spirv-tools/source/fuzz/transformation_merge_function_returns.cpp new file mode 100644 index 0000000..90578a2 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_merge_function_returns.cpp @@ -0,0 +1,831 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_merge_function_returns.h" + +#include "source/fuzz/comparator_deep_blocks_first.h" +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationMergeFunctionReturns::TransformationMergeFunctionReturns( + const protobufs::TransformationMergeFunctionReturns& message) + : message_(message) {} + +TransformationMergeFunctionReturns::TransformationMergeFunctionReturns( + uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id, + uint32_t return_val_id, uint32_t any_returnable_val_id, + const std::vector& returns_merging_info) { + message_.set_function_id(function_id); + message_.set_outer_header_id(outer_header_id); + message_.set_outer_return_id(outer_return_id); + message_.set_return_val_id(return_val_id); + message_.set_any_returnable_val_id(any_returnable_val_id); + for (const auto& return_merging_info : returns_merging_info) { + *message_.add_return_merging_info() = return_merging_info; + } +} + +bool TransformationMergeFunctionReturns::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto function = ir_context->GetFunction(message_.function_id()); + // The function must exist. + if (!function) { + return false; + } + + // The entry block must end in an unconditional branch. + if (function->entry()->terminator()->opcode() != SpvOpBranch) { + return false; + } + + // The module must contain an OpConstantTrue instruction. + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + true, false)) { + return false; + } + + // The module must contain an OpConstantFalse instruction. + if (!fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + false, false)) { + return false; + } + + // Check that the fresh ids provided are fresh and distinct. + std::set used_fresh_ids; + for (uint32_t id : {message_.outer_header_id(), message_.outer_return_id()}) { + if (!id || !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context, + &used_fresh_ids)) { + return false; + } + } + + // Check the additional fresh id required if the function is not void. + auto function_type = ir_context->get_type_mgr()->GetType(function->type_id()); + assert(function_type && "The function type should always exist."); + + if (!function_type->AsVoid() && + (!message_.return_val_id() || + !CheckIdIsFreshAndNotUsedByThisTransformation( + message_.return_val_id(), ir_context, &used_fresh_ids))) { + return false; + } + + // Get a map from the types for which ids are available at the end of the + // entry block to one of the ids with that type. We compute this here to avoid + // potentially doing it multiple times later on. + auto types_to_available_ids = + GetTypesToIdAvailableAfterEntryBlock(ir_context); + + // Get the reachable return blocks. + auto return_blocks = + fuzzerutil::GetReachableReturnBlocks(ir_context, message_.function_id()); + + // Map each merge block of loops containing reachable return blocks to the + // corresponding returning predecessors (all the blocks that, at the end of + // the transformation, will branch to the merge block because the function is + // returning). + std::map> merge_blocks_to_returning_preds; + for (uint32_t block : return_blocks) { + uint32_t merge_block = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(block); + + while (merge_block != 0) { + // If we have seen this merge block before, update the corresponding set + // and break out of the loop. + if (merge_blocks_to_returning_preds.count(merge_block)) { + merge_blocks_to_returning_preds[merge_block].emplace(block); + break; + } + + // If we have not seen this merge block before, add a new entry and walk + // up the loop tree. + merge_blocks_to_returning_preds.emplace(merge_block, + std::set({block})); + + // Walk up the loop tree. + block = merge_block; + merge_block = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(merge_block); + } + } + + // Instructions in the relevant merge blocks must be restricted to OpLabel, + // OpPhi and OpBranch. + for (const auto& merge_block_entry : merge_blocks_to_returning_preds) { + uint32_t merge_block = merge_block_entry.first; + bool all_instructions_allowed = + ir_context->get_instr_block(merge_block) + ->WhileEachInst([](opt::Instruction* inst) { + return inst->opcode() == SpvOpLabel || + inst->opcode() == SpvOpPhi || + inst->opcode() == SpvOpBranch; + }); + if (!all_instructions_allowed) { + return false; + } + } + + auto merge_blocks_to_info = GetMappingOfMergeBlocksToInfo(); + + // For each relevant merge block, check that the correct ids are available. + for (const auto& merge_block_entry : merge_blocks_to_returning_preds) { + if (!CheckThatTheCorrectIdsAreGivenForMergeBlock( + merge_block_entry.first, merge_blocks_to_info, + types_to_available_ids, function_type->AsVoid(), ir_context, + transformation_context, &used_fresh_ids)) { + return false; + } + } + + // If the function has a non-void return type, and there are merge loops which + // contain return instructions, we need to check that either: + // - |message_.any_returnable_val_id| exists. In this case, it must have the + // same type as the return type of the function and be available at the end + // of the entry block. + // - a suitable id, available at the end of the entry block can be found in + // the module. + if (!function_type->AsVoid() && !merge_blocks_to_returning_preds.empty()) { + auto returnable_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.any_returnable_val_id()); + if (!returnable_val_def) { + // Check if a suitable id can be found in the module. + if (types_to_available_ids.count(function->type_id()) == 0) { + return false; + } + } else if (returnable_val_def->type_id() != function->type_id()) { + return false; + } else if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, function->entry()->terminator(), + message_.any_returnable_val_id())) { + // The id must be available at the end of the entry block. + return false; + } + } + + // Check that adding new predecessors to the relevant merge blocks does not + // render any instructions invalid (each id definition must still dominate + // each of its uses). + if (!CheckDefinitionsStillDominateUsesAfterAddingNewPredecessors( + ir_context, function, merge_blocks_to_returning_preds)) { + return false; + } + + return true; +} + +void TransformationMergeFunctionReturns::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto function = ir_context->GetFunction(message_.function_id()); + auto function_type = ir_context->get_type_mgr()->GetType(function->type_id()); + + // Get a map from the types for which ids are available at the end of the + // entry block to one of the ids with that type. We compute this here to avoid + // potentially doing it multiple times later on. + auto types_to_available_ids = + GetTypesToIdAvailableAfterEntryBlock(ir_context); + + uint32_t bool_type = fuzzerutil::MaybeGetBoolType(ir_context); + + uint32_t constant_true = fuzzerutil::MaybeGetBoolConstant( + ir_context, *transformation_context, true, false); + + uint32_t constant_false = fuzzerutil::MaybeGetBoolConstant( + ir_context, *transformation_context, false, false); + + // Get the reachable return blocks. + auto return_blocks = + fuzzerutil::GetReachableReturnBlocks(ir_context, message_.function_id()); + + // Keep a map from the relevant merge blocks to a mapping from each of the + // returning predecessors to the corresponding pair (return value, + // boolean specifying whether the function is returning). Returning + // predecessors are blocks in the loop (not further nested inside loops), + // which either return or are merge blocks of nested loops containing return + // instructions. + std::map>> + merge_blocks_to_returning_predecessors; + + // Initialise the map, mapping each relevant merge block to an empty map. + for (uint32_t ret_block_id : return_blocks) { + uint32_t merge_block_id = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(ret_block_id); + + while (merge_block_id != 0 && + !merge_blocks_to_returning_predecessors.count(merge_block_id)) { + merge_blocks_to_returning_predecessors.emplace( + merge_block_id, std::map>()); + merge_block_id = ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock( + merge_block_id); + } + } + + // Get a reference to an instruction with the same type id as the function's + // return type, if the type of the function is not void and ther are loops + // containing return instructions. + uint32_t returnable_val_id = 0; + if (!function_type->AsVoid() && + !merge_blocks_to_returning_predecessors.empty()) { + // If |message.any_returnable_val_id| can be found in the module, use it. + // Otherwise, use another suitable id found in the module. + auto returnable_val_def = + ir_context->get_def_use_mgr()->GetDef(message_.any_returnable_val_id()); + returnable_val_id = returnable_val_def + ? returnable_val_def->result_id() + : types_to_available_ids[function->type_id()]; + } + + // Keep a map from all the new predecessors of the merge block of the new + // outer loop, to the related return value ids. + std::map outer_merge_predecessors; + + // Adjust the return blocks and add the related information to the map or + // |outer_merge_predecessors| set. + for (uint32_t ret_block_id : return_blocks) { + auto ret_block = ir_context->get_instr_block(ret_block_id); + + // Get the return value id (if the function is not void). + uint32_t ret_val_id = + function_type->AsVoid() + ? 0 + : ret_block->terminator()->GetSingleWordInOperand(0); + + uint32_t merge_block_id = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(ret_block_id); + + // Add a new entry to the map corresponding to the merge block of the + // innermost enclosing loop (or that of the new outer loop if there is no + // enclosing loop). + if (merge_block_id != 0) { + merge_blocks_to_returning_predecessors[merge_block_id].emplace( + ret_block_id, + std::pair(ret_val_id, constant_true)); + } else { + // If there is no enclosing loop, the block will branch to the merge block + // of the new outer loop. + merge_block_id = message_.outer_return_id(); + outer_merge_predecessors.emplace(ret_block_id, ret_val_id); + } + + // Replace the return instruction with an unconditional branch. + ret_block->terminator()->SetOpcode(SpvOpBranch); + ret_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {merge_block_id}}}); + } + + // Get a list of all the relevant merge blocks. + std::vector merge_blocks; + merge_blocks.reserve(merge_blocks_to_returning_predecessors.size()); + for (const auto& entry : merge_blocks_to_returning_predecessors) { + merge_blocks.emplace_back(entry.first); + } + + // Sort the list so that deeper merge blocks come first. + // We need to consider deeper merge blocks first so that, when a merge block + // is considered, all the merge blocks enclosed by the corresponding loop have + // already been considered and, thus, the mapping from this merge block to the + // returning predecessors is complete. + std::sort(merge_blocks.begin(), merge_blocks.end(), + ComparatorDeepBlocksFirst(ir_context)); + + auto merge_blocks_to_info = GetMappingOfMergeBlocksToInfo(); + + // Adjust the merge blocks and add the related information to the map or + // |outer_merge_predecessors| set. + for (uint32_t merge_block_id : merge_blocks) { + // Get the info corresponding to |merge_block| from the map, if a + // corresponding entry exists. Otherwise use overflow ids and find suitable + // ids in the module. + protobufs::ReturnMergingInfo* info = + merge_blocks_to_info.count(merge_block_id) + ? &merge_blocks_to_info[merge_block_id] + : nullptr; + + uint32_t is_returning_id = + info ? info->is_returning_id() + : transformation_context->GetOverflowIdSource() + ->GetNextOverflowId(); + + uint32_t maybe_return_val_id = 0; + if (!function_type->AsVoid()) { + maybe_return_val_id = info ? info->maybe_return_val_id() + : transformation_context->GetOverflowIdSource() + ->GetNextOverflowId(); + } + + // Map from existing OpPhi to overflow ids. If there is no mapping, get an + // empty map. + auto phi_to_id = info ? fuzzerutil::RepeatedUInt32PairToMap( + *merge_blocks_to_info[merge_block_id] + .mutable_opphi_to_suitable_id()) + : std::map(); + + // Get a reference to the info related to the returning predecessors. + const auto& returning_preds = + merge_blocks_to_returning_predecessors[merge_block_id]; + + // Get a set of the original predecessors. + auto preds_list = ir_context->cfg()->preds(merge_block_id); + auto preds = std::set(preds_list.begin(), preds_list.end()); + + auto merge_block = ir_context->get_instr_block(merge_block_id); + + // Adjust the existing OpPhi instructions. + merge_block->ForEachPhiInst( + [&preds, &returning_preds, &phi_to_id, + &types_to_available_ids](opt::Instruction* inst) { + // We need a placeholder value id. If |phi_to_id| contains a mapping + // for this instruction, we use the given id, otherwise a suitable id + // for the instruction's type from |types_to_available_ids|. + uint32_t placeholder_val_id = + phi_to_id.count(inst->result_id()) + ? phi_to_id[inst->result_id()] + : types_to_available_ids[inst->type_id()]; + assert(placeholder_val_id && + "We should always be able to find a suitable if the " + "transformation is applicable."); + + // Add a pair of operands (placeholder id, new predecessor) for each + // new predecessor of the merge block. + for (const auto& entry : returning_preds) { + // A returning predecessor may already be a predecessor of the + // block. In that case, we should not add new operands. + // Each entry is in the form (predecessor, {return val, is + // returning}). + if (!preds.count(entry.first)) { + inst->AddOperand({SPV_OPERAND_TYPE_ID, {placeholder_val_id}}); + inst->AddOperand({SPV_OPERAND_TYPE_ID, {entry.first}}); + } + } + }); + + // If the function is not void, add a new OpPhi instructions to collect the + // return value from the returning predecessors. + if (!function_type->AsVoid()) { + opt::Instruction::OperandList operand_list; + + // Add two operands (return value, predecessor) for each returning + // predecessor. + for (auto entry : returning_preds) { + // Each entry is in the form (predecessor, {return value, + // is returning}). + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.second.first}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.first}}); + } + + // Add two operands for each original predecessor from which the function + // does not return. + for (uint32_t original_pred : preds) { + // Only add operands if the function cannot be returning from this + // block. + if (returning_preds.count(original_pred)) { + continue; + } + + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {returnable_val_id}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {original_pred}}); + } + + // Insert the instruction. + merge_block->begin()->InsertBefore(MakeUnique( + ir_context, SpvOpPhi, function->type_id(), maybe_return_val_id, + std::move(operand_list))); + + fuzzerutil::UpdateModuleIdBound(ir_context, maybe_return_val_id); + } + + // Add an OpPhi instruction deciding whether the function is returning. + { + opt::Instruction::OperandList operand_list; + + // Add two operands (return value, is returning) for each returning + // predecessor. + for (auto entry : returning_preds) { + // Each entry is in the form (predecessor, {return value, + // is returning}). + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.second.second}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.first}}); + } + + // Add two operands for each original predecessor from which the function + // does not return. + for (uint32_t original_pred : preds) { + // Only add operands if the function cannot be returning from this + // block. + if (returning_preds.count(original_pred)) { + continue; + } + + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {constant_false}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {original_pred}}); + } + + // Insert the instruction. + merge_block->begin()->InsertBefore(MakeUnique( + ir_context, SpvOpPhi, bool_type, is_returning_id, + std::move(operand_list))); + + fuzzerutil::UpdateModuleIdBound(ir_context, is_returning_id); + } + + // Change the branching instruction of the block. + assert(merge_block->terminator()->opcode() == SpvOpBranch && + "Each block should branch unconditionally to the next."); + + // Add a new entry to the map corresponding to the merge block of the + // innermost enclosing loop (or that of the new outer loop if there is no + // enclosing loop). + uint32_t enclosing_merge = + ir_context->GetStructuredCFGAnalysis()->LoopMergeBlock(merge_block_id); + if (enclosing_merge == 0) { + enclosing_merge = message_.outer_return_id(); + outer_merge_predecessors.emplace(merge_block_id, maybe_return_val_id); + } else { + merge_blocks_to_returning_predecessors[enclosing_merge].emplace( + merge_block_id, + std::pair(maybe_return_val_id, is_returning_id)); + } + + // Get the current successor. + uint32_t original_succ = + merge_block->terminator()->GetSingleWordInOperand(0); + // Leave the instruction as it is if the block already branches to the merge + // block of the enclosing loop. + if (original_succ == enclosing_merge) { + continue; + } + + // The block should branch to |enclosing_merge| if |is_returning_id| is + // true, to |original_succ| otherwise. + merge_block->terminator()->SetOpcode(SpvOpBranchConditional); + merge_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {is_returning_id}}, + {SPV_OPERAND_TYPE_ID, {enclosing_merge}}, + {SPV_OPERAND_TYPE_ID, {original_succ}}}); + } + + assert(function->entry()->terminator()->opcode() == SpvOpBranch && + "The entry block should branch unconditionally to another block."); + uint32_t block_after_entry = + function->entry()->terminator()->GetSingleWordInOperand(0); + + // Create the header for the new outer loop. + auto outer_loop_header = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.outer_header_id(), + opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_header_id()); + + // Add the instruction: OpLoopMerge %outer_return_id %outer_header_id None + // The header is the continue block of the outer loop. + outer_loop_header->AddInstruction(MakeUnique( + ir_context, SpvOpLoopMerge, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.outer_return_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); + + // Add conditional branch: + // OpBranchConditional %true %block_after_entry %outer_header_id + // This will always branch to %block_after_entry, but it also creates a back + // edge for the loop (which is never traversed). + outer_loop_header->AddInstruction(MakeUnique( + ir_context, SpvOpBranchConditional, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {constant_true}}, + {SPV_OPERAND_TYPE_ID, {block_after_entry}}, + {SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}})); + + // Insert the header right after the entry block. + function->InsertBasicBlockAfter(std::move(outer_loop_header), + function->entry().get()); + + // Update the branching instruction of the entry block. + function->entry()->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {message_.outer_header_id()}}}); + + // If the entry block is referenced in an OpPhi instruction, the header for + // the new loop should be referenced instead. + ir_context->get_def_use_mgr()->ForEachUse( + function->entry()->id(), + [this](opt::Instruction* use_instruction, uint32_t use_operand_index) { + if (use_instruction->opcode() == SpvOpPhi) { + use_instruction->SetOperand(use_operand_index, + {message_.outer_header_id()}); + } + }); + + // Create the merge block for the loop (and return block for the function). + auto outer_return_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.outer_return_id(), + opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.outer_return_id()); + + // If the function is not void, insert an instruction to collect the return + // value from the predecessors and an OpReturnValue instruction. + if (!function_type->AsVoid()) { + opt::Instruction::OperandList operand_list; + + // Add two operands (return value, predecessor) for each predecessor. + for (auto entry : outer_merge_predecessors) { + // Each entry is in the form (predecessor, return value). + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.second}}); + operand_list.emplace_back( + opt::Operand{SPV_OPERAND_TYPE_ID, {entry.first}}); + } + + // Insert the OpPhi instruction. + outer_return_block->AddInstruction(MakeUnique( + ir_context, SpvOpPhi, function->type_id(), message_.return_val_id(), + std::move(operand_list))); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.return_val_id()); + + // Insert the OpReturnValue instruction. + outer_return_block->AddInstruction(MakeUnique( + ir_context, SpvOpReturnValue, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.return_val_id()}}})); + } else { + // Insert an OpReturn instruction (the function is void). + outer_return_block->AddInstruction(MakeUnique( + ir_context, SpvOpReturn, 0, 0, opt::Instruction::OperandList{})); + } + + // Insert the new return block at the end of the function. + outer_return_block->SetParent(function); + function->AddBasicBlock(std::move(outer_return_block)); + + // All analyses must be invalidated because the structure of the module was + // changed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +std::unordered_set TransformationMergeFunctionReturns::GetFreshIds() + const { + std::unordered_set result; + result.emplace(message_.outer_header_id()); + result.emplace(message_.outer_return_id()); + // |message_.return_val_info| can be 0 if the function is void. + if (message_.return_val_id()) { + result.emplace(message_.return_val_id()); + } + + for (auto merging_info : message_.return_merging_info()) { + result.emplace(merging_info.is_returning_id()); + // |maybe_return_val_id| can be 0 if the function is void. + if (merging_info.maybe_return_val_id()) { + result.emplace(merging_info.maybe_return_val_id()); + } + } + + return result; +} + +protobufs::Transformation TransformationMergeFunctionReturns::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_merge_function_returns() = message_; + return result; +} + +std::map +TransformationMergeFunctionReturns::GetMappingOfMergeBlocksToInfo() const { + std::map result; + for (const auto& info : message_.return_merging_info()) { + result.emplace(info.merge_block_id(), info); + } + return result; +} + +std::map +TransformationMergeFunctionReturns::GetTypesToIdAvailableAfterEntryBlock( + opt::IRContext* ir_context) const { + std::map result; + // Consider all global declarations + for (auto& global : ir_context->module()->types_values()) { + if (global.HasResultId() && global.type_id()) { + result.emplace(global.type_id(), global.result_id()); + } + } + + auto function = ir_context->GetFunction(message_.function_id()); + assert(function && "The function must exist."); + + // Consider all function parameters + function->ForEachParam([&result](opt::Instruction* param) { + if (param->HasResultId() && param->type_id()) { + result.emplace(param->type_id(), param->result_id()); + } + }); + + // Consider all the instructions in the entry block. + for (auto& inst : *function->entry()) { + if (inst.HasResultId() && inst.type_id()) { + result.emplace(inst.type_id(), inst.result_id()); + } + } + + return result; +} + +bool TransformationMergeFunctionReturns:: + CheckDefinitionsStillDominateUsesAfterAddingNewPredecessors( + opt::IRContext* ir_context, const opt::Function* function, + const std::map>& + merge_blocks_to_new_predecessors) { + for (const auto& merge_block_entry : merge_blocks_to_new_predecessors) { + uint32_t merge_block = merge_block_entry.first; + const auto& returning_preds = merge_block_entry.second; + + // Find a list of blocks in which there might be problematic definitions. + // These are all the blocks that dominate the merge block but do not + // dominate all of the new predecessors. + std::vector problematic_blocks; + + auto dominator_analysis = ir_context->GetDominatorAnalysis(function); + + // Start from the immediate dominator of the merge block. + auto current_block = dominator_analysis->ImmediateDominator(merge_block); + assert(current_block && + "Each merge block should have at least one dominator."); + + for (uint32_t pred : returning_preds) { + while (!dominator_analysis->Dominates(current_block->id(), pred)) { + // The current block does not dominate all of the new predecessor + // blocks, so it might be problematic. + problematic_blocks.emplace_back(current_block); + + // Walk up the dominator tree. + current_block = dominator_analysis->ImmediateDominator(current_block); + assert(current_block && + "We should be able to find a dominator for all the blocks, " + "since they must all be dominated at least by the header."); + } + } + + // Identify the loop header corresponding to the merge block. + uint32_t loop_header = + fuzzerutil::GetLoopFromMergeBlock(ir_context, merge_block); + + // For all the ids defined in blocks inside |problematic_blocks|, check that + // all their uses are either: + // - inside the loop (or in the loop header). If this is the case, the path + // from the definition to the use does not go through the merge block, so + // adding new predecessor to it is not a problem. + // - inside an OpPhi instruction in the merge block. If this is the case, + // the definition does not need to dominate the merge block. + for (auto block : problematic_blocks) { + assert((block->id() == loop_header || + ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + block->id()) == loop_header) && + "The problematic blocks should all be inside the loop (also " + "considering the header)."); + bool dominance_rules_maintained = + block->WhileEachInst([ir_context, loop_header, + merge_block](opt::Instruction* instruction) { + // Instruction without a result id do not cause any problems. + if (!instruction->HasResultId()) { + return true; + } + + // Check that all the uses of the id are inside the loop. + return ir_context->get_def_use_mgr()->WhileEachUse( + instruction->result_id(), + [ir_context, loop_header, merge_block]( + opt::Instruction* inst_use, uint32_t /* unused */) { + uint32_t block_use = + ir_context->get_instr_block(inst_use)->id(); + + // The usage is OK if it is inside the loop (including the + // header). + if (block_use == loop_header || + ir_context->GetStructuredCFGAnalysis()->ContainingLoop( + block_use)) { + return true; + } + + // The usage is OK if it is inside an OpPhi instruction in the + // merge block. + return block_use == merge_block && + inst_use->opcode() == SpvOpPhi; + }); + }); + + // If not all instructions in the block satisfy the requirement, the + // transformation is not applicable. + if (!dominance_rules_maintained) { + return false; + } + } + } + + return true; +} + +bool TransformationMergeFunctionReturns:: + CheckThatTheCorrectIdsAreGivenForMergeBlock( + uint32_t merge_block, + const std::map& + merge_blocks_to_info, + const std::map& types_to_available_id, + bool function_is_void, opt::IRContext* ir_context, + const TransformationContext& transformation_context, + std::set* used_fresh_ids) { + // A map from OpPhi ids to ids of the same type available at the beginning + // of the merge block. + std::map phi_to_id; + + if (merge_blocks_to_info.count(merge_block) > 0) { + // If the map contains an entry for the merge block, check that the fresh + // ids are fresh and distinct. + auto info = merge_blocks_to_info.at(merge_block); + if (!info.is_returning_id() || + !CheckIdIsFreshAndNotUsedByThisTransformation( + info.is_returning_id(), ir_context, used_fresh_ids)) { + return false; + } + + if (!function_is_void && + (!info.maybe_return_val_id() || + !CheckIdIsFreshAndNotUsedByThisTransformation( + info.maybe_return_val_id(), ir_context, used_fresh_ids))) { + return false; + } + + // Get the mapping from OpPhis to suitable ids. + phi_to_id = fuzzerutil::RepeatedUInt32PairToMap( + *info.mutable_opphi_to_suitable_id()); + } else { + // If the map does not contain an entry for the merge block, check that + // overflow ids are available. + if (!transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } + + // For each OpPhi instruction, check that a suitable placeholder id is + // available. + bool suitable_info_for_phi = + ir_context->get_instr_block(merge_block) + ->WhileEachPhiInst([ir_context, &phi_to_id, + &types_to_available_id](opt::Instruction* inst) { + if (phi_to_id.count(inst->result_id()) > 0) { + // If there exists a mapping for this instruction and the + // placeholder id exists in the module, check that it has the + // correct type and it is available before the instruction. + auto placeholder_def = ir_context->get_def_use_mgr()->GetDef( + phi_to_id[inst->result_id()]); + if (placeholder_def) { + if (inst->type_id() != placeholder_def->type_id()) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, inst, placeholder_def->result_id())) { + return false; + } + + return true; + } + } + + // If there is no mapping, check if there is a suitable id + // available at the end of the entry block. + return types_to_available_id.count(inst->type_id()) > 0; + }); + + if (!suitable_info_for_phi) { + return false; + } + + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_merge_function_returns.h b/third_party/spirv-tools/source/fuzz/transformation_merge_function_returns.h new file mode 100644 index 0000000..4b29936 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_merge_function_returns.h @@ -0,0 +1,115 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MERGE_FUNCTION_RETURNS_ +#define SOURCE_FUZZ_TRANSFORMATION_MERGE_FUNCTION_RETURNS_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { +class TransformationMergeFunctionReturns : public Transformation { + public: + explicit TransformationMergeFunctionReturns( + const protobufs::TransformationMergeFunctionReturns& message); + + TransformationMergeFunctionReturns( + uint32_t function_id, uint32_t outer_header_id, uint32_t outer_return_id, + uint32_t return_val_id, uint32_t any_returnable_val_id, + const std::vector& returns_merging_info); + + // - |message_.function_id| is the id of a function. + // - The entry block of |message_.function_id| branches unconditionally to + // another block. + // - |message_.any_returnable_val_id| is an id whose type is the same as the + // return type of the function and which is available at the end of the + // entry block. If this id is not found in the module, the transformation + // will try to find a suitable one. + // If the function is void, or no loops in the function contain return + // statements, this id will be ignored. + // - Merge blocks of reachable loops that contain return statements only + // consist of OpLabel, OpPhi or OpBranch instructions. + // - The model contains OpConstantTrue and OpConstantFalse instructions. + // - For all merge blocks of reachable loops that contain return statements, + // either: + // - a mapping is provided in |message_.return_merging_info|, all of the + // corresponding fresh ids are valid and, for each OpPhi instruction in + // the block, there is a mapping to an available id of the same type in + // |opphi_to_suitable_id| or a suitable id, available at the end of the + // entry block, can be found in the module. + // - there is no mapping, but overflow ids are available and, for every + // OpPhi instruction in the merge blocks that need to be modified, a + // suitable id, available at the end of the entry block, can be found. + // - The addition of new predecessors to the relevant merge blocks does not + // cause any id use to be invalid (i.e. every id must dominate all its uses + // even after the transformation has added new branches). + // - All of the fresh ids that are provided and needed by the transformation + // are valid. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Changes the function so that there is only one reachable return + // instruction. The function is enclosed by an outer loop, whose merge block + // is the new return block. All existing return statements are replaced by + // branch instructions to the merge block of the loop enclosing them, and + // OpPhi instructions are used to keep track of the return value and of + // whether the function is returning. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns a map from merge block ids to the corresponding info in + // |message_.return_merging_info|. + std::map + GetMappingOfMergeBlocksToInfo() const; + + // Returns a map from type ids to an id with that type and which is available + // at the end of the entry block of |message_.function_id|. + // Assumes that the function exists. + std::map GetTypesToIdAvailableAfterEntryBlock( + opt::IRContext* ir_context) const; + + // Returns true if adding new predecessors to the given loop merge blocks + // does not render any instructions invalid (each id definition must still + // dominate all of its uses). The loop merge blocks and corresponding new + // predecessors to consider are given in |merge_blocks_to_new_predecessors|. + // All of the new predecessors are assumed to be inside the loop associated + // with the corresponding loop merge block. + static bool CheckDefinitionsStillDominateUsesAfterAddingNewPredecessors( + opt::IRContext* ir_context, const opt::Function* function, + const std::map>& + merge_blocks_to_new_predecessors); + + // Returns true if the required ids for |merge_block| are provided in the + // |merge_blocks_to_info| map, or if ids of the suitable type can be found. + static bool CheckThatTheCorrectIdsAreGivenForMergeBlock( + uint32_t merge_block, + const std::map& + merge_blocks_to_info, + const std::map& types_to_available_id, + bool function_is_void, opt::IRContext* ir_context, + const TransformationContext& transformation_context, + std::set* used_fresh_ids); + + protobufs::TransformationMergeFunctionReturns message_; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MERGE_FUNCTION_RETURNS_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_move_block_down.cpp b/third_party/spirv-tools/source/fuzz/transformation_move_block_down.cpp new file mode 100644 index 0000000..c5ed433 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_move_block_down.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_move_block_down.h" + +#include "source/opt/basic_block.h" + +namespace spvtools { +namespace fuzz { + +TransformationMoveBlockDown::TransformationMoveBlockDown( + const spvtools::fuzz::protobufs::TransformationMoveBlockDown& message) + : message_(message) {} + +TransformationMoveBlockDown::TransformationMoveBlockDown(uint32_t id) { + message_.set_block_id(id); +} + +bool TransformationMoveBlockDown::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Go through every block in every function, looking for a block whose id + // matches that of the block we want to consider moving down. + for (auto& function : *ir_context->module()) { + for (auto block_it = function.begin(); block_it != function.end(); + ++block_it) { + if (block_it->id() == message_.block_id()) { + // We have found a match. + if (block_it == function.begin()) { + // The block is the first one appearing in the function. We are not + // allowed to move this block down. + return false; + } + // Record the block we would like to consider moving down. + opt::BasicBlock* block_matching_id = &*block_it; + if (!ir_context->GetDominatorAnalysis(&function)->IsReachable( + block_matching_id)) { + // The block is not reachable. We are not allowed to move it down. + return false; + } + // Now see whether there is some block following that block in program + // order. + ++block_it; + if (block_it == function.end()) { + // There is no such block; i.e., the block we are considering moving + // is the last one in the function. The transformation thus does not + // apply. + return false; + } + opt::BasicBlock* next_block_in_program_order = &*block_it; + // We can move the block of interest down if and only if it does not + // dominate the block that comes next. + return !ir_context->GetDominatorAnalysis(&function)->Dominates( + block_matching_id, next_block_in_program_order); + } + } + } + + // We did not find a matching block, so the transformation is not applicable: + // there is no relevant block to move. + return false; +} + +void TransformationMoveBlockDown::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Go through every block in every function, looking for a block whose id + // matches that of the block we want to move down. + for (auto& function : *ir_context->module()) { + for (auto block_it = function.begin(); block_it != function.end(); + ++block_it) { + if (block_it->id() == message_.block_id()) { + ++block_it; + assert(block_it != function.end() && + "To be able to move a block down, it needs to have a " + "program-order successor."); + function.MoveBasicBlockToAfter(message_.block_id(), &*block_it); + // For performance, it is vital to keep the dominator analysis valid + // (which due to https://github.com/KhronosGroup/SPIRV-Tools/issues/2889 + // requires keeping the CFG analysis valid). + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisDefUse | + opt::IRContext::Analysis::kAnalysisCFG | + opt::IRContext::Analysis::kAnalysisDominatorAnalysis); + + return; + } + } + } + assert(false && "No block was found to move down."); +} + +protobufs::Transformation TransformationMoveBlockDown::ToMessage() const { + protobufs::Transformation result; + *result.mutable_move_block_down() = message_; + return result; +} + +std::unordered_set TransformationMoveBlockDown::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_move_block_down.h b/third_party/spirv-tools/source/fuzz/transformation_move_block_down.h new file mode 100644 index 0000000..82f2599 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_move_block_down.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMoveBlockDown : public Transformation { + public: + explicit TransformationMoveBlockDown( + const protobufs::TransformationMoveBlockDown& message); + + explicit TransformationMoveBlockDown(uint32_t id); + + // - |message_.block_id| must be the id of a block b in the given module. + // - b must not be the first nor last block appearing, in program order, + // in a function. + // - b must not dominate the block that follows it in program order. + // - b must be reachable. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // The block with id |message_.block_id| is moved down; i.e. the program order + // between it and the block that follows it is swapped. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationMoveBlockDown message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MOVE_BLOCK_DOWN_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp b/third_party/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp new file mode 100644 index 0000000..dec0578 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_move_instruction_down.cpp @@ -0,0 +1,734 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_move_instruction_down.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "spirv/unified1/GLSL.std.450.h" + +namespace spvtools { +namespace fuzz { +namespace { + +const char* const kExtensionSetName = "GLSL.std.450"; + +std::string GetExtensionSet(opt::IRContext* ir_context, + const opt::Instruction& op_ext_inst) { + assert(op_ext_inst.opcode() == SpvOpExtInst && "Wrong opcode"); + + const auto* ext_inst_import = ir_context->get_def_use_mgr()->GetDef( + op_ext_inst.GetSingleWordInOperand(0)); + assert(ext_inst_import && "Extension set is not imported"); + + return ext_inst_import->GetInOperand(0).AsString(); +} + +} // namespace + +TransformationMoveInstructionDown::TransformationMoveInstructionDown( + const protobufs::TransformationMoveInstructionDown& message) + : message_(message) {} + +TransformationMoveInstructionDown::TransformationMoveInstructionDown( + const protobufs::InstructionDescriptor& instruction) { + *message_.mutable_instruction() = instruction; +} + +bool TransformationMoveInstructionDown::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |instruction| must be valid. + auto* inst = FindInstruction(message_.instruction(), ir_context); + if (!inst) { + return false; + } + + // Instruction's opcode must be supported by this transformation. + if (!IsInstructionSupported(ir_context, *inst)) { + return false; + } + + auto* inst_block = ir_context->get_instr_block(inst); + assert(inst_block && + "Global instructions and function parameters are not supported"); + + auto inst_it = fuzzerutil::GetIteratorForInstruction(inst_block, inst); + assert(inst_it != inst_block->end() && + "Can't get an iterator for the instruction"); + + // |instruction| can't be the last instruction in the block. + auto successor_it = ++inst_it; + if (successor_it == inst_block->end()) { + return false; + } + + // We don't risk swapping a memory instruction with an unsupported one. + if (!IsSimpleInstruction(ir_context, *inst) && + !IsInstructionSupported(ir_context, *successor_it)) { + return false; + } + + // It must be safe to swap the instructions without changing the semantics of + // the module. + if (IsInstructionSupported(ir_context, *successor_it) && + !CanSafelySwapInstructions(ir_context, *inst, *successor_it, + *transformation_context.GetFactManager())) { + return false; + } + + // Check that we can insert |instruction| after |inst_it|. + auto successors_successor_it = ++inst_it; + if (successors_successor_it == inst_block->end() || + !fuzzerutil::CanInsertOpcodeBeforeInstruction(inst->opcode(), + successors_successor_it)) { + return false; + } + + // Check that |instruction|'s successor doesn't depend on the |instruction|. + if (inst->result_id()) { + for (uint32_t i = 0; i < successor_it->NumInOperands(); ++i) { + const auto& operand = successor_it->GetInOperand(i); + if (spvIsInIdType(operand.type) && + operand.words[0] == inst->result_id()) { + return false; + } + } + } + + return true; +} + +void TransformationMoveInstructionDown::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = FindInstruction(message_.instruction(), ir_context); + assert(inst && + "The instruction should've been validated in the IsApplicable"); + + auto inst_it = fuzzerutil::GetIteratorForInstruction( + ir_context->get_instr_block(inst), inst); + + // Move the instruction down in the block. + inst->InsertAfter(&*++inst_it); + + ir_context->InvalidateAnalyses(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationMoveInstructionDown::ToMessage() const { + protobufs::Transformation result; + *result.mutable_move_instruction_down() = message_; + return result; +} + +bool TransformationMoveInstructionDown::IsInstructionSupported( + opt::IRContext* ir_context, const opt::Instruction& inst) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605): + // Add support for more instructions here. + return IsSimpleInstruction(ir_context, inst) || + IsMemoryInstruction(ir_context, inst) || IsBarrierInstruction(inst); +} + +bool TransformationMoveInstructionDown::IsSimpleInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + switch (inst.opcode()) { + case SpvOpNop: + case SpvOpUndef: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + // OpAccessChain and OpInBoundsAccessChain are considered simple + // instructions since they result in a pointer to the object in memory, + // not the object itself. + case SpvOpVectorExtractDynamic: + case SpvOpVectorInsertDynamic: + case SpvOpVectorShuffle: + case SpvOpCompositeConstruct: + case SpvOpCompositeExtract: + case SpvOpCompositeInsert: + case SpvOpCopyObject: + case SpvOpTranspose: + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpUConvert: + case SpvOpSConvert: + case SpvOpFConvert: + case SpvOpQuantizeToF16: + case SpvOpSatConvertSToU: + case SpvOpSatConvertUToS: + case SpvOpBitcast: + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpAny: + case SpvOpAll: + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpLogicalNot: + case SpvOpSelect: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitFieldInsert: + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: + case SpvOpBitReverse: + case SpvOpBitCount: + case SpvOpCopyLogical: + return true; + case SpvOpExtInst: { + const auto* ext_inst_import = + ir_context->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0)); + + if (ext_inst_import->GetInOperand(0).AsString() != kExtensionSetName) { + return false; + } + + switch (static_cast(inst.GetSingleWordInOperand(1))) { + case GLSLstd450Round: + case GLSLstd450RoundEven: + case GLSLstd450Trunc: + case GLSLstd450FAbs: + case GLSLstd450SAbs: + case GLSLstd450FSign: + case GLSLstd450SSign: + case GLSLstd450Floor: + case GLSLstd450Ceil: + case GLSLstd450Fract: + case GLSLstd450Radians: + case GLSLstd450Degrees: + case GLSLstd450Sin: + case GLSLstd450Cos: + case GLSLstd450Tan: + case GLSLstd450Asin: + case GLSLstd450Acos: + case GLSLstd450Atan: + case GLSLstd450Sinh: + case GLSLstd450Cosh: + case GLSLstd450Tanh: + case GLSLstd450Asinh: + case GLSLstd450Acosh: + case GLSLstd450Atanh: + case GLSLstd450Atan2: + case GLSLstd450Pow: + case GLSLstd450Exp: + case GLSLstd450Log: + case GLSLstd450Exp2: + case GLSLstd450Log2: + case GLSLstd450Sqrt: + case GLSLstd450InverseSqrt: + case GLSLstd450Determinant: + case GLSLstd450MatrixInverse: + case GLSLstd450ModfStruct: + case GLSLstd450FMin: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450FMax: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450FClamp: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FMix: + case GLSLstd450IMix: + case GLSLstd450Step: + case GLSLstd450SmoothStep: + case GLSLstd450Fma: + case GLSLstd450FrexpStruct: + case GLSLstd450Ldexp: + case GLSLstd450PackSnorm4x8: + case GLSLstd450PackUnorm4x8: + case GLSLstd450PackSnorm2x16: + case GLSLstd450PackUnorm2x16: + case GLSLstd450PackHalf2x16: + case GLSLstd450PackDouble2x32: + case GLSLstd450UnpackSnorm2x16: + case GLSLstd450UnpackUnorm2x16: + case GLSLstd450UnpackHalf2x16: + case GLSLstd450UnpackSnorm4x8: + case GLSLstd450UnpackUnorm4x8: + case GLSLstd450UnpackDouble2x32: + case GLSLstd450Length: + case GLSLstd450Distance: + case GLSLstd450Cross: + case GLSLstd450Normalize: + case GLSLstd450FaceForward: + case GLSLstd450Reflect: + case GLSLstd450Refract: + case GLSLstd450FindILsb: + case GLSLstd450FindSMsb: + case GLSLstd450FindUMsb: + case GLSLstd450NMin: + case GLSLstd450NMax: + case GLSLstd450NClamp: + return true; + default: + return false; + } + } + default: + return false; + } +} + +bool TransformationMoveInstructionDown::IsMemoryReadInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + switch (inst.opcode()) { + // Some simple instructions. + case SpvOpLoad: + case SpvOpCopyMemory: + // Image instructions. + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageFetch: + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImageRead: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + case SpvOpImageSparseFetch: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + case SpvOpImageSparseRead: + // Atomic instructions. + case SpvOpAtomicLoad: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFlagTestAndSet: + return true; + // Extensions. + case SpvOpExtInst: { + if (GetExtensionSet(ir_context, inst) != kExtensionSetName) { + return false; + } + + switch (static_cast(inst.GetSingleWordInOperand(1))) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtOffset: + case GLSLstd450InterpolateAtSample: + return true; + default: + return false; + } + } + default: + return false; + } +} + +uint32_t TransformationMoveInstructionDown::GetMemoryReadTarget( + opt::IRContext* ir_context, const opt::Instruction& inst) { + (void)ir_context; // |ir_context| is only used in assertions. + assert(IsMemoryReadInstruction(ir_context, inst) && + "|inst| is not a memory read instruction"); + + switch (inst.opcode()) { + // Simple instructions. + case SpvOpLoad: + // Image instructions. + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageFetch: + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImageRead: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + case SpvOpImageSparseFetch: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + case SpvOpImageSparseRead: + // Atomic instructions. + case SpvOpAtomicLoad: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFlagTestAndSet: + return inst.GetSingleWordInOperand(0); + case SpvOpCopyMemory: + return inst.GetSingleWordInOperand(1); + case SpvOpExtInst: { + assert(GetExtensionSet(ir_context, inst) == kExtensionSetName && + "Extension set is not supported"); + + switch (static_cast(inst.GetSingleWordInOperand(1))) { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtOffset: + case GLSLstd450InterpolateAtSample: + return inst.GetSingleWordInOperand(2); + default: + // This assertion will fail if not all memory read extension + // instructions are handled in the switch. + assert(false && "Not all memory opcodes are handled"); + return 0; + } + } + default: + // This assertion will fail if not all memory read opcodes are handled in + // the switch. + assert(false && "Not all memory opcodes are handled"); + return 0; + } +} + +bool TransformationMoveInstructionDown::IsMemoryWriteInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + switch (inst.opcode()) { + // Simple Instructions. + case SpvOpStore: + case SpvOpCopyMemory: + // Image instructions. + case SpvOpImageWrite: + // Atomic instructions. + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFlagTestAndSet: + case SpvOpAtomicFlagClear: + return true; + // Extensions. + case SpvOpExtInst: { + if (GetExtensionSet(ir_context, inst) != kExtensionSetName) { + return false; + } + + auto extension = static_cast(inst.GetSingleWordInOperand(1)); + return extension == GLSLstd450Modf || extension == GLSLstd450Frexp; + } + default: + return false; + } +} + +uint32_t TransformationMoveInstructionDown::GetMemoryWriteTarget( + opt::IRContext* ir_context, const opt::Instruction& inst) { + (void)ir_context; // |ir_context| is only used in assertions. + assert(IsMemoryWriteInstruction(ir_context, inst) && + "|inst| is not a memory write instruction"); + + switch (inst.opcode()) { + case SpvOpStore: + case SpvOpCopyMemory: + case SpvOpImageWrite: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFlagTestAndSet: + case SpvOpAtomicFlagClear: + return inst.GetSingleWordInOperand(0); + case SpvOpExtInst: { + assert(GetExtensionSet(ir_context, inst) == kExtensionSetName && + "Extension set is not supported"); + + switch (static_cast(inst.GetSingleWordInOperand(1))) { + case GLSLstd450Modf: + case GLSLstd450Frexp: + return inst.GetSingleWordInOperand(3); + default: + // This assertion will fail if not all memory write extension + // instructions are handled in the switch. + assert(false && "Not all opcodes are handled"); + return 0; + } + } + default: + // This assertion will fail if not all memory write opcodes are handled in + // the switch. + assert(false && "Not all opcodes are handled"); + return 0; + } +} + +bool TransformationMoveInstructionDown::IsMemoryInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + return IsMemoryReadInstruction(ir_context, inst) || + IsMemoryWriteInstruction(ir_context, inst); +} + +bool TransformationMoveInstructionDown::IsBarrierInstruction( + const opt::Instruction& inst) { + switch (inst.opcode()) { + case SpvOpMemoryBarrier: + case SpvOpControlBarrier: + case SpvOpMemoryNamedBarrier: + return true; + default: + return false; + } +} + +bool TransformationMoveInstructionDown::CanSafelySwapInstructions( + opt::IRContext* ir_context, const opt::Instruction& a, + const opt::Instruction& b, const FactManager& fact_manager) { + assert(IsInstructionSupported(ir_context, a) && + IsInstructionSupported(ir_context, b) && + "Both opcodes must be supported"); + + // One of opcodes is simple - we can swap them without any side-effects. + if (IsSimpleInstruction(ir_context, a) || + IsSimpleInstruction(ir_context, b)) { + return true; + } + + // Both parameters are either memory instruction or barriers. + + // One of the opcodes is a barrier - can't swap them. + if (IsBarrierInstruction(a) || IsBarrierInstruction(b)) { + return false; + } + + // Both parameters are memory instructions. + + // Both parameters only read from memory - it's OK to swap them. + if (!IsMemoryWriteInstruction(ir_context, a) && + !IsMemoryWriteInstruction(ir_context, b)) { + return true; + } + + // At least one of parameters is a memory read instruction. + + // In theory, we can swap two memory instructions, one of which reads + // from the memory, if the read target (the pointer the memory is read from) + // and the write target (the memory is written into): + // - point to different memory regions + // - point to the same region with irrelevant value + // - point to the same region and the region is not used anymore. + // + // However, we can't currently determine if two pointers point to two + // different memory regions. That being said, if two pointers are not + // synonymous, they still might point to the same memory region. For example: + // %1 = OpVariable ... + // %2 = OpAccessChain %1 0 + // %3 = OpAccessChain %1 0 + // In this pseudo-code, %2 and %3 are not synonymous but point to the same + // memory location. This implies that we can't determine if some memory + // location is not used in the block. + // + // With this in mind, consider two cases (we will build a table for each one): + // - one instruction only reads from memory, the other one only writes to it. + // S - both point to the same memory region. + // D - both point to different memory regions. + // 0, 1, 2 - neither, one of or both of the memory regions are irrelevant. + // |-| - can't swap; |+| - can swap. + // | 0 | 1 | 2 | + // S : - + + + // D : + + + + // - both instructions write to memory. Notation is the same. + // | 0 | 1 | 2 | + // S : * + + + // D : + + + + // * - we can swap two instructions that write into the same non-irrelevant + // memory region if the written value is the same. + // + // Note that we can't always distinguish between S and D. Also note that + // in case of S, if one of the instructions is marked with + // PointeeValueIsIrrelevant, then the pointee of the other one is irrelevant + // as well even if the instruction is not marked with that fact. + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3723): + // This procedure can be improved when we can determine if two pointers point + // to different memory regions. + + // From now on we will denote an instruction that: + // - only reads from memory - R + // - only writes into memory - W + // - reads and writes - RW + // + // Both |a| and |b| can be either W or RW at this point. Additionally, at most + // one of them can be R. The procedure below checks all possible combinations + // of R, W and RW according to the tables above. We conservatively assume that + // both |a| and |b| point to the same memory region. + + auto memory_is_irrelevant = [ir_context, &fact_manager](uint32_t id) { + const auto* inst = ir_context->get_def_use_mgr()->GetDef(id); + if (!inst->type_id()) { + return false; + } + + const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id()); + assert(type && "|id| has invalid type"); + + if (!type->AsPointer()) { + return false; + } + + return fact_manager.PointeeValueIsIrrelevant(id); + }; + + if (IsMemoryWriteInstruction(ir_context, a) && + IsMemoryWriteInstruction(ir_context, b) && + (memory_is_irrelevant(GetMemoryWriteTarget(ir_context, a)) || + memory_is_irrelevant(GetMemoryWriteTarget(ir_context, b)))) { + // We ignore the case when the written value is the same. This is because + // the written value might not be equal to any of the instruction's + // operands. + return true; + } + + if (IsMemoryReadInstruction(ir_context, a) && + IsMemoryWriteInstruction(ir_context, b) && + !memory_is_irrelevant(GetMemoryReadTarget(ir_context, a)) && + !memory_is_irrelevant(GetMemoryWriteTarget(ir_context, b))) { + return false; + } + + if (IsMemoryWriteInstruction(ir_context, a) && + IsMemoryReadInstruction(ir_context, b) && + !memory_is_irrelevant(GetMemoryWriteTarget(ir_context, a)) && + !memory_is_irrelevant(GetMemoryReadTarget(ir_context, b))) { + return false; + } + + return IsMemoryReadInstruction(ir_context, a) || + IsMemoryReadInstruction(ir_context, b); +} + +std::unordered_set TransformationMoveInstructionDown::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_move_instruction_down.h b/third_party/spirv-tools/source/fuzz/transformation_move_instruction_down.h new file mode 100644 index 0000000..8585225 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_move_instruction_down.h @@ -0,0 +1,107 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMoveInstructionDown : public Transformation { + public: + explicit TransformationMoveInstructionDown( + const protobufs::TransformationMoveInstructionDown& message); + + explicit TransformationMoveInstructionDown( + const protobufs::InstructionDescriptor& instruction); + + // - |instruction| should be a descriptor of a valid instruction in the module + // - |instruction|'s opcode should be supported by this transformation + // - neither |instruction| nor its successor may be the last instruction in + // the block + // - |instruction|'s successor may not be dependent on the |instruction| + // - it should be possible to insert |instruction|'s opcode after its + // successor + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Swaps |instruction| with its successor. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns true if the |inst| is supported by this transformation. + static bool IsInstructionSupported(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| represents a "simple" instruction. That is, it + // neither reads from nor writes to the memory and is not a barrier. + static bool IsSimpleInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| reads from memory. + static bool IsMemoryReadInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns id being used by |inst| to read from. |inst| must be a memory read + // instruction (see IsMemoryReadInstruction). Returned id is not guaranteed to + // have pointer type. + static uint32_t GetMemoryReadTarget(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| that writes to the memory. + static bool IsMemoryWriteInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns id being used by |inst| to write into. |inst| must be a memory + // write instruction (see IsMemoryWriteInstruction). Returned id is not + // guaranteed to have pointer type. + static uint32_t GetMemoryWriteTarget(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| either reads from or writes to the memory + // (see IsMemoryReadInstruction and IsMemoryWriteInstruction accordingly). + static bool IsMemoryInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + // Returns true if |inst| is a barrier instruction. + static bool IsBarrierInstruction(const opt::Instruction& inst); + + // Returns true if it is possible to swap |a| and |b| without changing the + // module's semantics. |a| and |b| are required to be supported instructions + // (see IsInstructionSupported). In particular, if either |a| or |b| are + // memory or barrier instructions, some checks are used to only say that they + // can be swapped if the swap is definitely semantics-preserving. + static bool CanSafelySwapInstructions(opt::IRContext* ir_context, + const opt::Instruction& a, + const opt::Instruction& b, + const FactManager& fact_manager); + + protobufs::TransformationMoveInstructionDown message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MOVE_INSTRUCTION_DOWN_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_mutate_pointer.cpp b/third_party/spirv-tools/source/fuzz/transformation_mutate_pointer.cpp new file mode 100644 index 0000000..fefedbd --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_mutate_pointer.cpp @@ -0,0 +1,171 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_mutate_pointer.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationMutatePointer::TransformationMutatePointer( + const protobufs::TransformationMutatePointer& message) + : message_(message) {} + +TransformationMutatePointer::TransformationMutatePointer( + uint32_t pointer_id, uint32_t fresh_id, + const protobufs::InstructionDescriptor& insert_before) { + message_.set_pointer_id(pointer_id); + message_.set_fresh_id(fresh_id); + *message_.mutable_insert_before() = insert_before; +} + +bool TransformationMutatePointer::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that |fresh_id| is fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + auto* insert_before_inst = + FindInstruction(message_.insert_before(), ir_context); + + // Check that |insert_before| is a valid instruction descriptor. + if (!insert_before_inst) { + return false; + } + + // Check that it is possible to insert OpLoad and OpStore before + // |insert_before_inst|. We are only using OpLoad here since the result does + // not depend on the opcode. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, + insert_before_inst)) { + return false; + } + + const auto* pointer_inst = + ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); + + // Check that |pointer_id| is a result id of a valid pointer instruction. + if (!pointer_inst || !IsValidPointerInstruction(ir_context, *pointer_inst)) { + return false; + } + + // Check that the module contains an irrelevant constant that will be used to + // mutate |pointer_inst|. The constant is irrelevant so that the latter + // transformation can change its value to something more interesting. + auto constant_id = fuzzerutil::MaybeGetZeroConstant( + ir_context, transformation_context, + fuzzerutil::GetPointeeTypeIdFromPointerType(ir_context, + pointer_inst->type_id()), + true); + if (!constant_id) { + return false; + } + + assert(fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before_inst, constant_id) && + "Global constant instruction is not available before " + "|insert_before_inst|"); + + // Check that |pointer_inst| is available before |insert_before_inst|. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, insert_before_inst, pointer_inst->result_id()); +} + +void TransformationMutatePointer::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto* insert_before_inst = + FindInstruction(message_.insert_before(), ir_context); + assert(insert_before_inst && "|insert_before| descriptor is invalid"); + + auto pointee_type_id = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, fuzzerutil::GetTypeId(ir_context, message_.pointer_id())); + + // Back up the original value. + insert_before_inst->InsertBefore(MakeUnique( + ir_context, SpvOpLoad, pointee_type_id, message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}})); + + // Insert a new value. + insert_before_inst->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, + {fuzzerutil::MaybeGetZeroConstant( + ir_context, *transformation_context, pointee_type_id, true)}}})); + + // Restore the original value. + insert_before_inst->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}})); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Make sure analyses represent the correct state of the module. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationMutatePointer::ToMessage() const { + protobufs::Transformation result; + *result.mutable_mutate_pointer() = message_; + return result; +} + +bool TransformationMutatePointer::IsValidPointerInstruction( + opt::IRContext* ir_context, const opt::Instruction& inst) { + // |inst| must have both result id and type id and it may not cause undefined + // behaviour. + if (!inst.result_id() || !inst.type_id() || inst.opcode() == SpvOpUndef || + inst.opcode() == SpvOpConstantNull) { + return false; + } + + opt::Instruction* type_inst = + ir_context->get_def_use_mgr()->GetDef(inst.type_id()); + assert(type_inst != nullptr && "|inst| has invalid type id"); + + // |inst| must be a pointer. + if (type_inst->opcode() != SpvOpTypePointer) { + return false; + } + + // |inst| must have a supported storage class. + switch (static_cast(type_inst->GetSingleWordInOperand(0))) { + case SpvStorageClassFunction: + case SpvStorageClassPrivate: + case SpvStorageClassWorkgroup: + break; + default: + return false; + } + + // |inst|'s pointee must consist of scalars and/or composites. + return fuzzerutil::CanCreateConstant(ir_context, + type_inst->GetSingleWordInOperand(1)); +} + +std::unordered_set TransformationMutatePointer::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_mutate_pointer.h b/third_party/spirv-tools/source/fuzz/transformation_mutate_pointer.h new file mode 100644 index 0000000..b9f0965 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_mutate_pointer.h @@ -0,0 +1,79 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_MUTATE_POINTER_H_ +#define SOURCE_FUZZ_TRANSFORMATION_MUTATE_POINTER_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationMutatePointer : public Transformation { + public: + explicit TransformationMutatePointer( + const protobufs::TransformationMutatePointer& message); + + explicit TransformationMutatePointer( + uint32_t pointer_id, uint32_t fresh_id, + const protobufs::InstructionDescriptor& insert_before); + + // - |fresh_id| must be fresh. + // - |insert_before| must be a valid instruction descriptor of some + // instruction in the module. + // - It should be possible to insert OpLoad and OpStore before + // |insert_before|. + // - |pointer_id| must be a result id of some instruction in the module. + // - Instruction with result id |pointer_id| must be valid (see + // IsValidPointerInstruction method). + // - There must exist an irrelevant constant in the module. Type of the + // constant must be equal to the type of the |pointer_id|'s pointee. + // - |pointer_id| must be available (according to the dominance rules) before + // |insert_before|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts the following instructions before |insert_before|: + // %fresh_id = OpLoad %pointee_type_id %pointer_id + // OpStore %pointer_id %constant_id + // OpStore %pointer_id %fresh_id + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if |inst| valid pointer according to the following: + // - |inst| has result id and type id. + // - |inst| is neither OpUndef nor OpConstantNull. + // - |inst| has a pointer type. + // - |inst|'s storage class is either Private, Function or Workgroup. + // - |inst|'s pointee type and all its constituents are either scalar or + // composite. + static bool IsValidPointerInstruction(opt::IRContext* ir_context, + const opt::Instruction& inst); + + private: + protobufs::TransformationMutatePointer message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_MUTATE_POINTER_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_outline_function.cpp b/third_party/spirv-tools/source/fuzz/transformation_outline_function.cpp new file mode 100644 index 0000000..26f9e0b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_outline_function.cpp @@ -0,0 +1,1015 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_outline_function.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationOutlineFunction::TransformationOutlineFunction( + const spvtools::fuzz::protobufs::TransformationOutlineFunction& message) + : message_(message) {} + +TransformationOutlineFunction::TransformationOutlineFunction( + uint32_t entry_block, uint32_t exit_block, + uint32_t new_function_struct_return_type_id, uint32_t new_function_type_id, + uint32_t new_function_id, uint32_t new_function_region_entry_block, + uint32_t new_caller_result_id, uint32_t new_callee_result_id, + const std::map& input_id_to_fresh_id, + const std::map& output_id_to_fresh_id) { + message_.set_entry_block(entry_block); + message_.set_exit_block(exit_block); + message_.set_new_function_struct_return_type_id( + new_function_struct_return_type_id); + message_.set_new_function_type_id(new_function_type_id); + message_.set_new_function_id(new_function_id); + message_.set_new_function_region_entry_block(new_function_region_entry_block); + message_.set_new_caller_result_id(new_caller_result_id); + message_.set_new_callee_result_id(new_callee_result_id); + *message_.mutable_input_id_to_fresh_id() = + fuzzerutil::MapToRepeatedUInt32Pair(input_id_to_fresh_id); + *message_.mutable_output_id_to_fresh_id() = + fuzzerutil::MapToRepeatedUInt32Pair(output_id_to_fresh_id); +} + +bool TransformationOutlineFunction::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + std::set ids_used_by_this_transformation; + + // The various new ids used by the transformation must be fresh and distinct. + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_struct_return_type_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_type_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_function_region_entry_block(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_caller_result_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + message_.new_callee_result_id(), ir_context, + &ids_used_by_this_transformation)) { + return false; + } + + for (auto& pair : message_.input_id_to_fresh_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + + for (auto& pair : message_.output_id_to_fresh_id()) { + if (!CheckIdIsFreshAndNotUsedByThisTransformation( + pair.second(), ir_context, &ids_used_by_this_transformation)) { + return false; + } + } + + // The entry and exit block ids must indeed refer to blocks. + for (auto block_id : {message_.entry_block(), message_.exit_block()}) { + auto block_label = ir_context->get_def_use_mgr()->GetDef(block_id); + if (!block_label || block_label->opcode() != SpvOpLabel) { + return false; + } + } + + auto entry_block = ir_context->cfg()->block(message_.entry_block()); + auto exit_block = ir_context->cfg()->block(message_.exit_block()); + + // The entry block cannot start with OpVariable - this would mean that + // outlining would remove a variable from the function containing the region + // being outlined. + if (entry_block->begin()->opcode() == SpvOpVariable) { + return false; + } + + // For simplicity, we do not allow the entry block to be a loop header. + if (entry_block->GetLoopMergeInst()) { + return false; + } + + // For simplicity, we do not allow the exit block to be a merge block or + // continue target. + if (fuzzerutil::IsMergeOrContinue(ir_context, exit_block->id())) { + return false; + } + + // The entry block cannot start with OpPhi. This is to keep the + // transformation logic simple. (Another transformation to split the OpPhis + // from a block could be applied to avoid this scenario.) + if (entry_block->begin()->opcode() == SpvOpPhi) { + return false; + } + + // The block must be in the same function. + if (entry_block->GetParent() != exit_block->GetParent()) { + return false; + } + + // The entry block must dominate the exit block. + auto dominator_analysis = + ir_context->GetDominatorAnalysis(entry_block->GetParent()); + if (!dominator_analysis->Dominates(entry_block, exit_block)) { + return false; + } + + // The exit block must post-dominate the entry block. + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(entry_block->GetParent()); + if (!postdominator_analysis->Dominates(exit_block, entry_block)) { + return false; + } + + // Find all the blocks dominated by |message_.entry_block| and post-dominated + // by |message_.exit_block|. + auto region_set = GetRegionBlocks( + ir_context, + entry_block = ir_context->cfg()->block(message_.entry_block()), + exit_block = ir_context->cfg()->block(message_.exit_block())); + + // Check whether |region_set| really is a single-entry single-exit region, and + // also check whether structured control flow constructs and their merge + // and continue constructs are either wholly in or wholly out of the region - + // e.g. avoid the situation where the region contains the head of a loop but + // not the loop's continue construct. + // + // This is achieved by going through every block in the function that contains + // the region. + for (auto& block : *entry_block->GetParent()) { + if (&block == exit_block) { + // It is OK (and typically expected) for the exit block of the region to + // have successors outside the region. + // + // It is also OK for the exit block to head a selection construct: the + // block containing the call to the outlined function will end up heading + // this construct if outlining takes place. However, it is not OK for + // the exit block to head a loop construct. + if (block.GetLoopMergeInst()) { + return false; + } + continue; + } + + if (region_set.count(&block) != 0) { + // The block is in the region and is not the region's exit block. Let's + // see whether all of the block's successors are in the region. If they + // are not, the region is not single-entry single-exit. + bool all_successors_in_region = true; + block.WhileEachSuccessorLabel([&all_successors_in_region, ir_context, + ®ion_set](uint32_t successor) -> bool { + if (region_set.count(ir_context->cfg()->block(successor)) == 0) { + all_successors_in_region = false; + return false; + } + return true; + }); + if (!all_successors_in_region) { + return false; + } + } + + if (auto merge = block.GetMergeInst()) { + // The block is a loop or selection header -- the header and its + // associated merge block had better both be in the region or both be + // outside the region. + auto merge_block = + ir_context->cfg()->block(merge->GetSingleWordOperand(0)); + if (region_set.count(&block) != region_set.count(merge_block)) { + return false; + } + } + + if (auto loop_merge = block.GetLoopMergeInst()) { + // Similar to the above, but for the continue target of a loop. + auto continue_target = + ir_context->cfg()->block(loop_merge->GetSingleWordOperand(1)); + if (continue_target != exit_block && + region_set.count(&block) != region_set.count(continue_target)) { + return false; + } + } + } + + // For each region input id, i.e. every id defined outside the region but + // used inside the region, ... + auto input_id_to_fresh_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id()); + for (auto id : GetRegionInputIds(ir_context, region_set, exit_block)) { + // There needs to be a corresponding fresh id to be used as a function + // parameter, or overflow ids need to be available. + if (input_id_to_fresh_id_map.count(id) == 0 && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + // Furthermore, if the input id has pointer type it must be an OpVariable + // or OpFunctionParameter. + auto input_id_inst = ir_context->get_def_use_mgr()->GetDef(id); + if (ir_context->get_def_use_mgr() + ->GetDef(input_id_inst->type_id()) + ->opcode() == SpvOpTypePointer) { + switch (input_id_inst->opcode()) { + case SpvOpFunctionParameter: + case SpvOpVariable: + // These are OK. + break; + default: + // Anything else is not OK. + return false; + } + } + } + + // For each region output id -- i.e. every id defined inside the region but + // used outside the region, ... + auto output_id_to_fresh_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id()); + for (auto id : GetRegionOutputIds(ir_context, region_set, exit_block)) { + if ( + // ... there needs to be a corresponding fresh id that can hold the + // value for this id computed in the outlined function (or overflow ids + // must be available), and ... + (output_id_to_fresh_id_map.count(id) == 0 && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) + // ... the output id must not have pointer type (to avoid creating a + // struct with pointer members to pass data out of the outlined + // function) + || ir_context->get_def_use_mgr() + ->GetDef(fuzzerutil::GetTypeId(ir_context, id)) + ->opcode() == SpvOpTypePointer) { + return false; + } + } + + return true; +} + +void TransformationOutlineFunction::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // The entry block for the region before outlining. + auto original_region_entry_block = + ir_context->cfg()->block(message_.entry_block()); + + // The exit block for the region before outlining. + auto original_region_exit_block = + ir_context->cfg()->block(message_.exit_block()); + + // The single-entry single-exit region defined by |message_.entry_block| and + // |message_.exit_block|. + std::set region_blocks = GetRegionBlocks( + ir_context, original_region_entry_block, original_region_exit_block); + + // Input and output ids for the region being outlined. + std::vector region_input_ids = + GetRegionInputIds(ir_context, region_blocks, original_region_exit_block); + std::vector region_output_ids = + GetRegionOutputIds(ir_context, region_blocks, original_region_exit_block); + + // Maps from input and output ids to fresh ids. + auto input_id_to_fresh_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.input_id_to_fresh_id()); + auto output_id_to_fresh_id_map = + fuzzerutil::RepeatedUInt32PairToMap(message_.output_id_to_fresh_id()); + + // Use overflow ids to augment these maps at any locations where fresh ids are + // required but not provided. + for (uint32_t id : region_input_ids) { + if (input_id_to_fresh_id_map.count(id) == 0) { + input_id_to_fresh_id_map.insert( + {id, + transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); + } + } + for (uint32_t id : region_output_ids) { + if (output_id_to_fresh_id_map.count(id) == 0) { + output_id_to_fresh_id_map.insert( + {id, + transformation_context->GetOverflowIdSource()->GetNextOverflowId()}); + } + } + + UpdateModuleIdBoundForFreshIds(ir_context, input_id_to_fresh_id_map, + output_id_to_fresh_id_map); + + // Construct a map that associates each output id with its type id. + std::map output_id_to_type_id; + for (uint32_t output_id : region_output_ids) { + output_id_to_type_id[output_id] = + ir_context->get_def_use_mgr()->GetDef(output_id)->type_id(); + } + + // The region will be collapsed to a single block that calls a function + // containing the outlined region. This block needs to end with whatever + // the exit block of the region ended with before outlining. We thus clone + // the terminator of the region's exit block, and the merge instruction for + // the block if there is one, so that we can append them to the end of the + // collapsed block later. + std::unique_ptr cloned_exit_block_terminator = + std::unique_ptr( + original_region_exit_block->terminator()->Clone(ir_context)); + std::unique_ptr cloned_exit_block_merge = + original_region_exit_block->GetMergeInst() + ? std::unique_ptr( + original_region_exit_block->GetMergeInst()->Clone(ir_context)) + : nullptr; + + // Make a function prototype for the outlined function, which involves + // figuring out its required type. + std::unique_ptr outlined_function = PrepareFunctionPrototype( + region_input_ids, region_output_ids, input_id_to_fresh_id_map, ir_context, + transformation_context); + + // Adapt the region to be outlined so that its input ids are replaced with the + // ids of the outlined function's input parameters, and so that output ids + // are similarly remapped. + RemapInputAndOutputIdsInRegion( + ir_context, *original_region_exit_block, region_blocks, region_input_ids, + region_output_ids, input_id_to_fresh_id_map, output_id_to_fresh_id_map); + + // Fill out the body of the outlined function according to the region that is + // being outlined. + PopulateOutlinedFunction( + *original_region_entry_block, *original_region_exit_block, region_blocks, + region_output_ids, output_id_to_type_id, output_id_to_fresh_id_map, + ir_context, outlined_function.get()); + + // Collapse the region that has been outlined into a function down to a single + // block that calls said function. + ShrinkOriginalRegion( + ir_context, region_blocks, region_input_ids, region_output_ids, + output_id_to_type_id, outlined_function->type_id(), + std::move(cloned_exit_block_merge), + std::move(cloned_exit_block_terminator), original_region_entry_block); + + // Add the outlined function to the module. + const auto* outlined_function_ptr = outlined_function.get(); + ir_context->module()->AddFunction(std::move(outlined_function)); + + // Major surgery has been conducted on the module, so invalidate all analyses. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // If the original function was livesafe, the new function should also be + // livesafe. + if (transformation_context->GetFactManager()->FunctionIsLivesafe( + original_region_entry_block->GetParent()->result_id())) { + transformation_context->GetFactManager()->AddFactFunctionIsLivesafe( + message_.new_function_id()); + } + + // Record the fact that all blocks in the outlined region are dead if the + // first block is dead. + if (transformation_context->GetFactManager()->BlockIsDead( + original_region_entry_block->id())) { + transformation_context->GetFactManager()->AddFactBlockIsDead( + outlined_function_ptr->entry()->id()); + } +} + +protobufs::Transformation TransformationOutlineFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_outline_function() = message_; + return result; +} + +std::vector TransformationOutlineFunction::GetRegionInputIds( + opt::IRContext* ir_context, const std::set& region_set, + opt::BasicBlock* region_exit_block) { + std::vector result; + + auto enclosing_function = region_exit_block->GetParent(); + + // Consider each parameter of the function containing the region. + enclosing_function->ForEachParam( + [ir_context, ®ion_set, &result](opt::Instruction* function_parameter) { + // Consider every use of the parameter. + ir_context->get_def_use_mgr()->WhileEachUse( + function_parameter, + [ir_context, function_parameter, ®ion_set, &result]( + opt::Instruction* use, uint32_t /*unused*/) { + // Get the block, if any, in which the parameter is used. + auto use_block = ir_context->get_instr_block(use); + // If the use is in a block that lies within the region, the + // parameter is an input id for the region. + if (use_block && region_set.count(use_block) != 0) { + result.push_back(function_parameter->result_id()); + return false; + } + return true; + }); + }); + + // Consider all definitions in the function that might turn out to be input + // ids. + for (auto& block : *enclosing_function) { + std::vector candidate_input_ids_for_block; + if (region_set.count(&block) == 0) { + // All instructions in blocks outside the region are candidate's for + // generating input ids. + for (auto& inst : block) { + candidate_input_ids_for_block.push_back(&inst); + } + } else { + // Blocks in the region cannot generate input ids. + continue; + } + + // Consider each candidate input id to check whether it is used in the + // region. + for (auto& inst : candidate_input_ids_for_block) { + ir_context->get_def_use_mgr()->WhileEachUse( + inst, + [ir_context, &inst, region_exit_block, ®ion_set, &result]( + opt::Instruction* use, uint32_t /*unused*/) -> bool { + // Find the block in which this id use occurs, recording the id as + // an input id if the block is outside the region, with some + // exceptions detailed below. + auto use_block = ir_context->get_instr_block(use); + + if (!use_block) { + // There might be no containing block, e.g. if the use is in a + // decoration. + return true; + } + + if (region_set.count(use_block) == 0) { + // The use is not in the region: this does not make it an input + // id. + return true; + } + + if (use_block == region_exit_block && use->IsBlockTerminator()) { + // We do not regard uses in the exit block terminator as input + // ids, as this terminator does not get outlined. + return true; + } + + result.push_back(inst->result_id()); + return false; + }); + } + } + return result; +} + +std::vector TransformationOutlineFunction::GetRegionOutputIds( + opt::IRContext* ir_context, const std::set& region_set, + opt::BasicBlock* region_exit_block) { + std::vector result; + + // Consider each block in the function containing the region. + for (auto& block : *region_exit_block->GetParent()) { + if (region_set.count(&block) == 0) { + // Skip blocks that are not in the region. + continue; + } + // Consider each use of each instruction defined in the block. + for (auto& inst : block) { + ir_context->get_def_use_mgr()->WhileEachUse( + &inst, + [®ion_set, ir_context, &inst, region_exit_block, &result]( + opt::Instruction* use, uint32_t /*unused*/) -> bool { + // Find the block in which this id use occurs, recording the id as + // an output id if the block is outside the region, with some + // exceptions detailed below. + auto use_block = ir_context->get_instr_block(use); + + if (!use_block) { + // There might be no containing block, e.g. if the use is in a + // decoration. + return true; + } + + if (region_set.count(use_block) != 0) { + // The use is in the region. + if (use_block != region_exit_block || !use->IsBlockTerminator()) { + // Furthermore, the use is not in the terminator of the region's + // exit block. + return true; + } + } + + result.push_back(inst.result_id()); + return false; + }); + } + } + return result; +} + +std::set TransformationOutlineFunction::GetRegionBlocks( + opt::IRContext* ir_context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block) { + auto enclosing_function = entry_block->GetParent(); + auto dominator_analysis = + ir_context->GetDominatorAnalysis(enclosing_function); + auto postdominator_analysis = + ir_context->GetPostDominatorAnalysis(enclosing_function); + + std::set result; + for (auto& block : *enclosing_function) { + if (dominator_analysis->Dominates(entry_block, &block) && + postdominator_analysis->Dominates(exit_block, &block)) { + result.insert(&block); + } + } + return result; +} + +std::unique_ptr +TransformationOutlineFunction::PrepareFunctionPrototype( + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& input_id_to_fresh_id_map, + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + uint32_t return_type_id = 0; + uint32_t function_type_id = 0; + + // First, try to find an existing function type that is suitable. This is + // only possible if the region generates no output ids; if it generates output + // ids we are going to make a new struct for those, and since that struct does + // not exist there cannot already be a function type with this struct as its + // return type. + if (region_output_ids.empty()) { + std::vector return_and_parameter_types; + opt::analysis::Void void_type; + return_type_id = ir_context->get_type_mgr()->GetId(&void_type); + return_and_parameter_types.push_back(return_type_id); + for (auto id : region_input_ids) { + return_and_parameter_types.push_back( + ir_context->get_def_use_mgr()->GetDef(id)->type_id()); + } + function_type_id = + fuzzerutil::FindFunctionType(ir_context, return_and_parameter_types); + } + + // If no existing function type was found, we need to create one. + if (function_type_id == 0) { + assert( + ((return_type_id == 0) == !region_output_ids.empty()) && + "We should only have set the return type if there are no output ids."); + // If the region generates output ids, we need to make a struct with one + // field per output id. + if (!region_output_ids.empty()) { + opt::Instruction::OperandList struct_member_types; + for (uint32_t output_id : region_output_ids) { + auto output_id_type = + ir_context->get_def_use_mgr()->GetDef(output_id)->type_id(); + if (ir_context->get_def_use_mgr()->GetDef(output_id_type)->opcode() == + SpvOpTypeVoid) { + // We cannot add a void field to a struct. We instead use OpUndef to + // handle void output ids. + continue; + } + struct_member_types.push_back({SPV_OPERAND_TYPE_ID, {output_id_type}}); + } + // Add a new struct type to the module. + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeStruct, 0, + message_.new_function_struct_return_type_id(), + std::move(struct_member_types))); + // The return type for the function is the newly-created struct. + return_type_id = message_.new_function_struct_return_type_id(); + } + assert( + return_type_id != 0 && + "We should either have a void return type, or have created a struct."); + + // The region's input ids dictate the parameter types to the function. + opt::Instruction::OperandList function_type_operands; + function_type_operands.push_back({SPV_OPERAND_TYPE_ID, {return_type_id}}); + for (auto id : region_input_ids) { + function_type_operands.push_back( + {SPV_OPERAND_TYPE_ID, + {ir_context->get_def_use_mgr()->GetDef(id)->type_id()}}); + } + // Add a new function type to the module, and record that this is the type + // id for the new function. + ir_context->module()->AddType(MakeUnique( + ir_context, SpvOpTypeFunction, 0, message_.new_function_type_id(), + function_type_operands)); + function_type_id = message_.new_function_type_id(); + } + + // Create a new function with |message_.new_function_id| as the function id, + // and the return type and function type prepared above. + std::unique_ptr outlined_function = + MakeUnique(MakeUnique( + ir_context, SpvOpFunction, return_type_id, message_.new_function_id(), + opt::Instruction::OperandList( + {{spv_operand_type_t ::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvFunctionControlMaskNone}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {function_type_id}}}))); + + // Add one parameter to the function for each input id, using the fresh ids + // provided in |input_id_to_fresh_id_map|, or overflow ids if needed. + for (auto id : region_input_ids) { + uint32_t fresh_id = input_id_to_fresh_id_map.at(id); + outlined_function->AddParameter(MakeUnique( + ir_context, SpvOpFunctionParameter, + ir_context->get_def_use_mgr()->GetDef(id)->type_id(), fresh_id, + opt::Instruction::OperandList())); + + // Analyse the use of the new parameter instruction. + outlined_function->ForEachParam( + [fresh_id, ir_context](opt::Instruction* inst) { + if (inst->result_id() == fresh_id) { + ir_context->AnalyzeDefUse(inst); + } + }); + + // If the input id is an irrelevant-valued variable, the same should be true + // of the corresponding parameter. + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + id)) { + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(input_id_to_fresh_id_map.at(id)); + } + } + + return outlined_function; +} + +void TransformationOutlineFunction::UpdateModuleIdBoundForFreshIds( + opt::IRContext* ir_context, + const std::map& input_id_to_fresh_id_map, + const std::map& output_id_to_fresh_id_map) const { + // Enlarge the module's id bound as needed to accommodate the various fresh + // ids associated with the transformation. + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.new_function_struct_return_type_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_type_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_function_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.new_function_region_entry_block()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_caller_result_id()); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.new_callee_result_id()); + + for (auto& entry : input_id_to_fresh_id_map) { + fuzzerutil::UpdateModuleIdBound(ir_context, entry.second); + } + + for (auto& entry : output_id_to_fresh_id_map) { + fuzzerutil::UpdateModuleIdBound(ir_context, entry.second); + } +} + +void TransformationOutlineFunction::RemapInputAndOutputIdsInRegion( + opt::IRContext* ir_context, + const opt::BasicBlock& original_region_exit_block, + const std::set& region_blocks, + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& input_id_to_fresh_id_map, + const std::map& output_id_to_fresh_id_map) const { + // Change all uses of input ids inside the region to the corresponding fresh + // ids that will ultimately be parameters of the outlined function. + // This is done by considering each region input id in turn. + for (uint32_t id : region_input_ids) { + // We then consider each use of the input id. + ir_context->get_def_use_mgr()->ForEachUse( + id, [ir_context, id, &input_id_to_fresh_id_map, region_blocks]( + opt::Instruction* use, uint32_t operand_index) { + // Find the block in which this use of the input id occurs. + opt::BasicBlock* use_block = ir_context->get_instr_block(use); + // We want to rewrite the use id if its block occurs in the outlined + // region. + if (region_blocks.count(use_block) != 0) { + // Rewrite this use of the input id. + use->SetOperand(operand_index, {input_id_to_fresh_id_map.at(id)}); + } + }); + } + + // Change each definition of a region output id to define the corresponding + // fresh ids that will store intermediate value for the output ids. Also + // change all uses of the output id located in the outlined region. + // This is done by considering each region output id in turn. + for (uint32_t id : region_output_ids) { + // First consider each use of the output id and update the relevant uses. + ir_context->get_def_use_mgr()->ForEachUse( + id, [ir_context, &original_region_exit_block, id, + &output_id_to_fresh_id_map, + region_blocks](opt::Instruction* use, uint32_t operand_index) { + // Find the block in which this use of the output id occurs. + auto use_block = ir_context->get_instr_block(use); + // We want to rewrite the use id if its block occurs in the outlined + // region, with one exception: the terminator of the exit block of + // the region is going to remain in the original function, so if the + // use appears in such a terminator instruction we leave it alone. + if ( + // The block is in the region ... + region_blocks.count(use_block) != 0 && + // ... and the use is not in the terminator instruction of the + // region's exit block. + !(use_block == &original_region_exit_block && + use->IsBlockTerminator())) { + // Rewrite this use of the output id. + use->SetOperand(operand_index, {output_id_to_fresh_id_map.at(id)}); + } + }); + + // Now change the instruction that defines the output id so that it instead + // defines the corresponding fresh id. We do this after changing all the + // uses so that the definition of the original id is still registered when + // we analyse its uses. + ir_context->get_def_use_mgr()->GetDef(id)->SetResultId( + output_id_to_fresh_id_map.at(id)); + } +} + +void TransformationOutlineFunction::PopulateOutlinedFunction( + const opt::BasicBlock& original_region_entry_block, + const opt::BasicBlock& original_region_exit_block, + const std::set& region_blocks, + const std::vector& region_output_ids, + const std::map& output_id_to_type_id, + const std::map& output_id_to_fresh_id_map, + opt::IRContext* ir_context, opt::Function* outlined_function) const { + // When we create the exit block for the outlined region, we use this pointer + // to track of it so that we can manipulate it later. + opt::BasicBlock* outlined_region_exit_block = nullptr; + + // The region entry block in the new function is identical to the entry block + // of the region being outlined, except that it has + // |message_.new_function_region_entry_block| as its id. + std::unique_ptr outlined_region_entry_block = + MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, message_.new_function_region_entry_block(), + opt::Instruction::OperandList())); + outlined_region_entry_block->SetParent(outlined_function); + + if (&original_region_entry_block == &original_region_exit_block) { + outlined_region_exit_block = outlined_region_entry_block.get(); + } + + for (auto& inst : original_region_entry_block) { + outlined_region_entry_block->AddInstruction( + std::unique_ptr(inst.Clone(ir_context))); + } + outlined_function->AddBasicBlock(std::move(outlined_region_entry_block)); + + // We now go through the single-entry single-exit region defined by the entry + // and exit blocks, adding clones of all blocks to the new function. + + // Consider every block in the enclosing function. + auto enclosing_function = original_region_entry_block.GetParent(); + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + // Skip the region's entry block - we already dealt with it above. + if (region_blocks.count(&*block_it) == 0 || + &*block_it == &original_region_entry_block) { + ++block_it; + continue; + } + // Clone the block so that it can be added to the new function. + auto cloned_block = + std::unique_ptr(block_it->Clone(ir_context)); + + // If this is the region's exit block, then the cloned block is the outlined + // region's exit block. + if (&*block_it == &original_region_exit_block) { + assert(outlined_region_exit_block == nullptr && + "We should not yet have encountered the exit block."); + outlined_region_exit_block = cloned_block.get(); + } + + cloned_block->SetParent(outlined_function); + + // Redirect any OpPhi operands whose predecessors are the original region + // entry block to become the new function entry block. + cloned_block->ForEachPhiInst([this](opt::Instruction* phi_inst) { + for (uint32_t predecessor_index = 1; + predecessor_index < phi_inst->NumInOperands(); + predecessor_index += 2) { + if (phi_inst->GetSingleWordInOperand(predecessor_index) == + message_.entry_block()) { + phi_inst->SetInOperand(predecessor_index, + {message_.new_function_region_entry_block()}); + } + } + }); + + outlined_function->AddBasicBlock(std::move(cloned_block)); + block_it = block_it.Erase(); + } + assert(outlined_region_exit_block != nullptr && + "We should have encountered the region's exit block when iterating " + "through the function"); + + // We now need to adapt the exit block for the region - in the new function - + // so that it ends with a return. + + // We first eliminate the merge instruction (if any) and the terminator for + // the cloned exit block. + for (auto inst_it = outlined_region_exit_block->begin(); + inst_it != outlined_region_exit_block->end();) { + if (inst_it->opcode() == SpvOpLoopMerge || + inst_it->opcode() == SpvOpSelectionMerge) { + inst_it = inst_it.Erase(); + } else if (inst_it->IsBlockTerminator()) { + inst_it = inst_it.Erase(); + } else { + ++inst_it; + } + } + + // We now add either OpReturn or OpReturnValue as the cloned exit block's + // terminator. + if (region_output_ids.empty()) { + // The case where there are no region output ids is simple: we just add + // OpReturn. + outlined_region_exit_block->AddInstruction(MakeUnique( + ir_context, SpvOpReturn, 0, 0, opt::Instruction::OperandList())); + } else { + // In the case where there are output ids, we add an OpCompositeConstruct + // instruction to pack all the non-void output values into a struct, and + // then an OpReturnValue instruction to return this struct. + opt::Instruction::OperandList struct_member_operands; + for (uint32_t id : region_output_ids) { + if (ir_context->get_def_use_mgr() + ->GetDef(output_id_to_type_id.at(id)) + ->opcode() != SpvOpTypeVoid) { + struct_member_operands.push_back( + {SPV_OPERAND_TYPE_ID, {output_id_to_fresh_id_map.at(id)}}); + } + } + outlined_region_exit_block->AddInstruction(MakeUnique( + ir_context, SpvOpCompositeConstruct, + message_.new_function_struct_return_type_id(), + message_.new_callee_result_id(), struct_member_operands)); + outlined_region_exit_block->AddInstruction(MakeUnique( + ir_context, SpvOpReturnValue, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.new_callee_result_id()}}}))); + } + + outlined_function->SetFunctionEnd(MakeUnique( + ir_context, SpvOpFunctionEnd, 0, 0, opt::Instruction::OperandList())); +} + +void TransformationOutlineFunction::ShrinkOriginalRegion( + opt::IRContext* ir_context, const std::set& region_blocks, + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& output_id_to_type_id, + uint32_t return_type_id, + std::unique_ptr cloned_exit_block_merge, + std::unique_ptr cloned_exit_block_terminator, + opt::BasicBlock* original_region_entry_block) const { + // Erase all blocks from the original function that are in the outlined + // region, except for the region's entry block. + // + // In the process, identify all references to the exit block of the region, + // as merge blocks, continue targets, or OpPhi predecessors, and rewrite them + // to refer to the region entry block (the single block to which we are + // shrinking the region). + auto enclosing_function = original_region_entry_block->GetParent(); + for (auto block_it = enclosing_function->begin(); + block_it != enclosing_function->end();) { + if (&*block_it == original_region_entry_block) { + ++block_it; + } else if (region_blocks.count(&*block_it) == 0) { + // The block is not in the region. Check whether it has the last block + // of the region as an OpPhi predecessor, and if so change the + // predecessor to be the first block of the region (i.e. the block + // containing the call to what was outlined). + assert(block_it->MergeBlockIdIfAny() != message_.exit_block() && + "Outlined region must not end with a merge block"); + assert(block_it->ContinueBlockIdIfAny() != message_.exit_block() && + "Outlined region must not end with a continue target"); + block_it->ForEachPhiInst([this](opt::Instruction* phi_inst) { + for (uint32_t predecessor_index = 1; + predecessor_index < phi_inst->NumInOperands(); + predecessor_index += 2) { + if (phi_inst->GetSingleWordInOperand(predecessor_index) == + message_.exit_block()) { + phi_inst->SetInOperand(predecessor_index, {message_.entry_block()}); + } + } + }); + ++block_it; + } else { + // The block is in the region and is not the region's entry block: kill + // it. + block_it = block_it.Erase(); + } + } + + // Now erase all instructions from the region's entry block, as they have + // been outlined. + for (auto inst_it = original_region_entry_block->begin(); + inst_it != original_region_entry_block->end();) { + inst_it = inst_it.Erase(); + } + + // Now we add a call to the outlined function to the region's entry block. + opt::Instruction::OperandList function_call_operands; + function_call_operands.push_back( + {SPV_OPERAND_TYPE_ID, {message_.new_function_id()}}); + // The function parameters are the region input ids. + for (auto input_id : region_input_ids) { + function_call_operands.push_back({SPV_OPERAND_TYPE_ID, {input_id}}); + } + + original_region_entry_block->AddInstruction(MakeUnique( + ir_context, SpvOpFunctionCall, return_type_id, + message_.new_caller_result_id(), function_call_operands)); + + // If there are output ids, the function call will return a struct. For each + // output id, we add an extract operation to pull the appropriate struct + // member out into an output id. The exception is for output ids with void + // type. There are no struct entries for these, so we use an OpUndef of void + // type instead. + uint32_t struct_member_index = 0; + for (uint32_t output_id : region_output_ids) { + uint32_t output_type_id = output_id_to_type_id.at(output_id); + if (ir_context->get_def_use_mgr()->GetDef(output_type_id)->opcode() == + SpvOpTypeVoid) { + original_region_entry_block->AddInstruction(MakeUnique( + ir_context, SpvOpUndef, output_type_id, output_id, + opt::Instruction::OperandList())); + // struct_member_index is not incremented since there was no struct member + // associated with this void-typed output id. + } else { + original_region_entry_block->AddInstruction(MakeUnique( + ir_context, SpvOpCompositeExtract, output_type_id, output_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.new_caller_result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {struct_member_index}}}))); + struct_member_index++; + } + } + + // Finally, we terminate the block with the merge instruction (if any) that + // used to belong to the region's exit block, and the terminator that used + // to belong to the region's exit block. + if (cloned_exit_block_merge != nullptr) { + original_region_entry_block->AddInstruction( + std::move(cloned_exit_block_merge)); + } + original_region_entry_block->AddInstruction( + std::move(cloned_exit_block_terminator)); +} + +std::unordered_set TransformationOutlineFunction::GetFreshIds() + const { + std::unordered_set result = { + message_.new_function_struct_return_type_id(), + message_.new_function_type_id(), + message_.new_function_id(), + message_.new_function_region_entry_block(), + message_.new_caller_result_id(), + message_.new_callee_result_id()}; + for (auto& pair : message_.input_id_to_fresh_id()) { + result.insert(pair.second()); + } + for (auto& pair : message_.output_id_to_fresh_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_outline_function.h b/third_party/spirv-tools/source/fuzz/transformation_outline_function.h new file mode 100644 index 0000000..36c0daf --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_outline_function.h @@ -0,0 +1,229 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_ + +#include +#include +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationOutlineFunction : public Transformation { + public: + explicit TransformationOutlineFunction( + const protobufs::TransformationOutlineFunction& message); + + TransformationOutlineFunction( + uint32_t entry_block, uint32_t exit_block, + uint32_t new_function_struct_return_type_id, + uint32_t new_function_type_id, uint32_t new_function_id, + uint32_t new_function_region_entry_block, uint32_t new_caller_result_id, + uint32_t new_callee_result_id, + const std::map& input_id_to_fresh_id, + const std::map& output_id_to_fresh_id); + + // - All the fresh ids occurring in the transformation must be distinct and + // fresh + // - |message_.entry_block| and |message_.exit_block| must form a single-entry + // single-exit control flow graph region + // - |message_.entry_block| must not start with OpVariable + // - |message_.entry_block| must not be a loop header + // - |message_.exit_block| must not be a merge block or the continue target + // of a loop + // - A structured control flow construct must lie either completely within the + // region or completely outside it + // - |message.entry_block| must not start with OpPhi; this is to keep the + // transformation simple - another transformation should be used to split + // a desired entry block that starts with OpPhi if needed + // - |message_.input_id_to_fresh_id| must contain an entry for every id + // defined outside the region but used in the region + // - |message_.output_id_to_fresh_id| must contain an entry for every id + // defined in the region but used outside the region + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - A new function with id |message_.new_function_id| is added to the module. + // - If the region generates output ids, the return type of this function is + // a new struct type with one field per output id, and with type id + // |message_.new_function_struct_return_type|, otherwise the function return + // types is void and |message_.new_function_struct_return_type| is not used. + // - If the region generates input ids, the new function has one parameter per + // input id. Fresh ids for these parameters are provided by + // |message_.input_id_to_fresh_id|. + // - Unless the type required for the new function is already known, + // |message_.new_function_type_id| is used as the type id for a new function + // type, and the new function uses this type. + // - The new function starts with a placeholder block with id + // |message_.new_function_first_block|, which jumps straight to a successor + // block, to avoid violating rules on what the first block in a function may + // look like. + // - The outlined region is replaced with a single block, with the same id + // as |message_.entry_block|, and which calls the new function, passing the + // region's input ids as parameters. The result is stored in + // |message_.new_caller_result_id|, which has type + // |message_.new_function_struct_return_type| (unless there are + // no output ids, in which case the return type is void). The components + // of this returned struct are then copied out into the region's output ids. + // The block ends with the merge instruction (if any) and terminator of + // |message_.exit_block|. + // - The body of the new function is identical to the outlined region, except + // that (a) the region's entry block has id + // |message_.new_function_region_entry_block|, (b) input id uses are + // replaced with parameter accesses, (c) and definitions of output ids are + // replaced with definitions of corresponding fresh ids provided by + // |message_.output_id_to_fresh_id|, and (d) the block of the function + // ends by returning a composite of type + // |message_.new_function_struct_return_type| comprised of all the fresh + // output ids (unless the return type is void, in which case no value is + // returned. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the set of blocks dominated by |entry_block| and post-dominated + // by |exit_block|. + static std::set GetRegionBlocks( + opt::IRContext* ir_context, opt::BasicBlock* entry_block, + opt::BasicBlock* exit_block); + + // Yields ids that are used in |region_set| and that are either parameters + // to the function containing |region_set|, or are defined by blocks of this + // function that are outside |region_set|. + // + // Special cases: OpPhi instructions in |region_entry_block| and the + // terminator of |region_exit_block| do not get outlined, therefore + // - id uses in OpPhi instructions in |region_entry_block| are ignored + // - id uses in the terminator instruction of |region_exit_block| are ignored + static std::vector GetRegionInputIds( + opt::IRContext* ir_context, const std::set& region_set, + opt::BasicBlock* region_exit_block); + + // Yields all ids that are defined in |region_set| and used outside + // |region_set|. + // + // Special cases: for similar reasons as for |GetRegionInputIds|, + // - ids defined in the region and used in the terminator of + // |region_exit_block| count as output ids + static std::vector GetRegionOutputIds( + opt::IRContext* ir_context, const std::set& region_set, + opt::BasicBlock* region_exit_block); + + private: + // Ensures that the module's id bound is at least the maximum of any fresh id + // associated with the transformation. + void UpdateModuleIdBoundForFreshIds( + opt::IRContext* ir_context, + const std::map& input_id_to_fresh_id_map, + const std::map& output_id_to_fresh_id_map) const; + + // Uses |input_id_to_fresh_id_map| and |output_id_to_fresh_id_map| to convert, + // in the region to be outlined, all the input ids in |region_input_ids| and + // the output ids in |region_output_ids| to their fresh counterparts. + // Parameters |region_blocks| provides access to the blocks that must be + // modified, and |original_region_exit_block| allows for some special cases + // where ids should not be remapped. + void RemapInputAndOutputIdsInRegion( + opt::IRContext* ir_context, + const opt::BasicBlock& original_region_exit_block, + const std::set& region_blocks, + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& input_id_to_fresh_id_map, + const std::map& output_id_to_fresh_id_map) const; + + // Produce a Function object that has the right function type and parameter + // declarations. The function argument types and parameter ids are dictated + // by |region_input_ids| and |input_id_to_fresh_id_map|. The function return + // type is dictated by |region_output_ids|. + // + // A new struct type to represent the function return type, and a new function + // type for the function, will be added to the module (unless suitable types + // are already present). + // + // Facts about the function containing the outlined region that are relevant + // to the new function are propagated via the vact manager in + // |transformation_context|. + std::unique_ptr PrepareFunctionPrototype( + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& input_id_to_fresh_id_map, + opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + // Creates the body of the outlined function by cloning blocks from the + // original region, given by |region_blocks|, adapting the cloned version + // of |original_region_exit_block| so that it returns something appropriate, + // and patching up branches to |original_region_entry_block| to refer to its + // clone. Parameters |region_output_ids| and |output_id_to_fresh_id_map| are + // used to determine what the function should return. Parameter + // |output_id_to_type_id| provides the type of each output id. + // + // The |transformation_context| argument allow facts about blocks being + // outlined, e.g. whether they are dead blocks, to be asserted about blocks + // that get created during outlining. + void PopulateOutlinedFunction( + const opt::BasicBlock& original_region_entry_block, + const opt::BasicBlock& original_region_exit_block, + const std::set& region_blocks, + const std::vector& region_output_ids, + const std::map& output_id_to_type_id, + const std::map& output_id_to_fresh_id_map, + opt::IRContext* ir_context, opt::Function* outlined_function) const; + + // Shrinks the outlined region, given by |region_blocks|, down to the single + // block |original_region_entry_block|. This block is itself shrunk to just + // contain: + // - any OpPhi instructions that were originally present + // - a call to the outlined function, with parameters provided by + // |region_input_ids| + // - instructions to route components of the call's return value into + // |region_output_ids| + // - The merge instruction (if any) and terminator of the original region's + // exit block, given by |cloned_exit_block_merge| and + // |cloned_exit_block_terminator| + // Parameters |output_id_to_type_id| and |return_type_id| provide the + // provide types for the region's output ids, and the return type of the + // outlined function: as the module is in an inconsistent state when this + // function is called, this information cannot be gotten from the def-use + // manager. + void ShrinkOriginalRegion( + opt::IRContext* ir_context, + const std::set& region_blocks, + const std::vector& region_input_ids, + const std::vector& region_output_ids, + const std::map& output_id_to_type_id, + uint32_t return_type_id, + std::unique_ptr cloned_exit_block_merge, + std::unique_ptr cloned_exit_block_terminator, + opt::BasicBlock* original_region_entry_block) const; + + protobufs::TransformationOutlineFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_OUTLINE_FUNCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp b/third_party/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp new file mode 100644 index 0000000..a954cc1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_permute_function_parameters.cpp @@ -0,0 +1,170 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_permute_function_parameters.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationPermuteFunctionParameters:: + TransformationPermuteFunctionParameters( + const spvtools::fuzz::protobufs:: + TransformationPermuteFunctionParameters& message) + : message_(message) {} + +TransformationPermuteFunctionParameters:: + TransformationPermuteFunctionParameters( + uint32_t function_id, uint32_t function_type_fresh_id, + const std::vector& permutation) { + message_.set_function_id(function_id); + message_.set_function_type_fresh_id(function_type_fresh_id); + + for (auto index : permutation) { + message_.add_permutation(index); + } +} + +bool TransformationPermuteFunctionParameters::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that function exists + const auto* function = + fuzzerutil::FindFunction(ir_context, message_.function_id()); + if (!function || function->DefInst().opcode() != SpvOpFunction || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // Check that permutation has valid indices + const auto* function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(function_type && "Function type is null"); + + std::vector permutation(message_.permutation().begin(), + message_.permutation().end()); + + // Don't take return type into account + auto arg_size = function_type->NumInOperands() - 1; + + // |permutation| vector should be equal to the number of arguments + if (static_cast(permutation.size()) != arg_size) { + return false; + } + + // Check that permutation doesn't have duplicated values. + assert(!fuzzerutil::HasDuplicates(permutation) && + "Permutation has duplicates"); + + // Check that elements in permutation are in range [0, arg_size - 1]. + // + // We must check whether the permutation is empty first because in that case + // |arg_size - 1| will produce |std::numeric_limits::max()| since + // it's an unsigned integer. + if (!permutation.empty() && + !fuzzerutil::IsPermutationOfRange(permutation, 0, arg_size - 1)) { + return false; + } + + return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()); +} + +void TransformationPermuteFunctionParameters::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Find the function that will be transformed + auto* function = fuzzerutil::FindFunction(ir_context, message_.function_id()); + assert(function && "Can't find the function"); + + // Adjust OpFunctionParameter instructions + + // Collect ids and types from OpFunctionParameter instructions + std::vector param_id, param_type; + function->ForEachParam( + [¶m_id, ¶m_type](const opt::Instruction* param) { + param_id.push_back(param->result_id()); + param_type.push_back(param->type_id()); + }); + + // Permute parameters' ids and types + std::vector permuted_param_id, permuted_param_type; + for (auto index : message_.permutation()) { + permuted_param_id.push_back(param_id[index]); + permuted_param_type.push_back(param_type[index]); + } + + // Set OpFunctionParameter instructions to point to new parameters + size_t i = 0; + function->ForEachParam( + [&i, &permuted_param_id, &permuted_param_type](opt::Instruction* param) { + param->SetResultType(permuted_param_type[i]); + param->SetResultId(permuted_param_id[i]); + ++i; + }); + + // Fix all OpFunctionCall instructions + for (auto* call : fuzzerutil::GetCallers(ir_context, function->result_id())) { + opt::Instruction::OperandList call_operands = { + call->GetInOperand(0) // Function id + }; + + for (auto index : message_.permutation()) { + // Take function id into account + call_operands.push_back(call->GetInOperand(index + 1)); + } + + call->SetInOperands(std::move(call_operands)); + } + + // Update function type. + { + // We use a separate scope here since |old_function_type_inst| might become + // a dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + auto* old_function_type_inst = + fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type_inst && "Function must have a valid type"); + + std::vector parameter_type_ids; + for (auto index : message_.permutation()) { + // +1 since the first operand to OpTypeFunction is a return type. + parameter_type_ids.push_back( + old_function_type_inst->GetSingleWordInOperand(index + 1)); + } + + // Change function's type. + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.function_type_fresh_id(), + old_function_type_inst->GetSingleWordInOperand(0), parameter_type_ids); + } + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationPermuteFunctionParameters::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_permute_function_parameters() = message_; + return result; +} + +std::unordered_set +TransformationPermuteFunctionParameters::GetFreshIds() const { + return {message_.function_type_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_permute_function_parameters.h b/third_party/spirv-tools/source/fuzz/transformation_permute_function_parameters.h new file mode 100644 index 0000000..38de8b1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_permute_function_parameters.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPermuteFunctionParameters : public Transformation { + public: + explicit TransformationPermuteFunctionParameters( + const protobufs::TransformationPermuteFunctionParameters& message); + + TransformationPermuteFunctionParameters( + uint32_t function_id, uint32_t function_type_fresh_id, + const std::vector& permutation); + + // - |function_id| is a valid non-entry-point OpFunction instruction + // - |function_type_fresh_id| is a fresh id. + // New type is valid if: + // - it has the same number of operands as the old one + // - function's result type is the same as the old one + // - function's arguments are permuted according to |permutation| vector + // - |permutation| is a set of [0..(n - 1)], where n is a number of arguments + // to the function + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - OpFunction instruction with |result_id == function_id| is changed. + // Its arguments are permuted according to the |permutation| vector + // - Adjusts function's type to accommodate for permuted parameters. + // - Calls to the function are adjusted accordingly + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationPermuteFunctionParameters message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_FUNCTION_PARAMETERS_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp b/third_party/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp new file mode 100644 index 0000000..ebd3c86 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_permute_phi_operands.cpp @@ -0,0 +1,100 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_permute_phi_operands.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationPermutePhiOperands::TransformationPermutePhiOperands( + const spvtools::fuzz::protobufs::TransformationPermutePhiOperands& message) + : message_(message) {} + +TransformationPermutePhiOperands::TransformationPermutePhiOperands( + uint32_t result_id, const std::vector& permutation) { + message_.set_result_id(result_id); + + for (auto index : permutation) { + message_.add_permutation(index); + } +} + +bool TransformationPermutePhiOperands::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that |message_.result_id| is valid. + const auto* inst = + ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (!inst || inst->opcode() != SpvOpPhi) { + return false; + } + + // Check that |message_.permutation| has expected size. + auto expected_permutation_size = inst->NumInOperands() / 2; + if (static_cast(message_.permutation().size()) != + expected_permutation_size) { + return false; + } + + // Check that |message_.permutation| has elements in range + // [0, expected_permutation_size - 1]. + std::vector permutation(message_.permutation().begin(), + message_.permutation().end()); + assert(!fuzzerutil::HasDuplicates(permutation) && + "Permutation has duplicates"); + + // We must check whether the permutation is empty first because in that case + // |expected_permutation_size - 1| will produce + // |std::numeric_limits::max()| since it's an unsigned integer. + return permutation.empty() || + fuzzerutil::IsPermutationOfRange(permutation, 0, + expected_permutation_size - 1); +} + +void TransformationPermutePhiOperands::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + assert(inst); + + opt::Instruction::OperandList permuted_operands; + permuted_operands.reserve(inst->NumInOperands()); + + for (auto index : message_.permutation()) { + permuted_operands.push_back(std::move(inst->GetInOperand(2 * index))); + permuted_operands.push_back(std::move(inst->GetInOperand(2 * index + 1))); + } + + inst->SetInOperands(std::move(permuted_operands)); + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationPermutePhiOperands::ToMessage() const { + protobufs::Transformation result; + *result.mutable_permute_phi_operands() = message_; + return result; +} + +std::unordered_set TransformationPermutePhiOperands::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_permute_phi_operands.h b/third_party/spirv-tools/source/fuzz/transformation_permute_phi_operands.h new file mode 100644 index 0000000..8198b70 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_permute_phi_operands.h @@ -0,0 +1,58 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPermutePhiOperands : public Transformation { + public: + explicit TransformationPermutePhiOperands( + const protobufs::TransformationPermutePhiOperands& message); + + TransformationPermutePhiOperands(uint32_t result_id, + const std::vector& permutation); + + // - |result_id| must be a valid id of some OpPhi instruction in the module. + // - |permutation| must contain elements in the range [0, n/2 - 1] where |n| + // is a number of operands to the instruction with |result_id|. All elements + // must be unique (i.e. |permutation.size() == n / 2|). + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Permutes operands of the OpPhi instruction with |result_id| according to + // the elements in |permutation|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationPermutePhiOperands message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PERMUTE_PHI_OPERANDS_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_down.cpp b/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_down.cpp new file mode 100644 index 0000000..ba22e39 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_down.cpp @@ -0,0 +1,592 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_propagate_instruction_down.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationPropagateInstructionDown::TransformationPropagateInstructionDown( + const protobufs::TransformationPropagateInstructionDown& message) + : message_(message) {} + +TransformationPropagateInstructionDown::TransformationPropagateInstructionDown( + uint32_t block_id, uint32_t phi_fresh_id, + const std::map& successor_id_to_fresh_id) { + message_.set_block_id(block_id); + message_.set_phi_fresh_id(phi_fresh_id); + *message_.mutable_successor_id_to_fresh_id() = + fuzzerutil::MapToRepeatedUInt32Pair(successor_id_to_fresh_id); +} + +bool TransformationPropagateInstructionDown::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that we can apply this transformation to the |block_id|. + if (!IsApplicableToBlock(ir_context, message_.block_id())) { + return false; + } + + const auto successor_id_to_fresh_id = + fuzzerutil::RepeatedUInt32PairToMap(message_.successor_id_to_fresh_id()); + + for (auto id : GetAcceptableSuccessors(ir_context, message_.block_id())) { + // Each successor must have a fresh id in the |successor_id_to_fresh_id| + // map, unless overflow ids are available. + if (!successor_id_to_fresh_id.count(id) && + !transformation_context.GetOverflowIdSource()->HasOverflowIds()) { + return false; + } + } + + std::vector maybe_fresh_ids = {message_.phi_fresh_id()}; + maybe_fresh_ids.reserve(successor_id_to_fresh_id.size()); + for (const auto& entry : successor_id_to_fresh_id) { + maybe_fresh_ids.push_back(entry.second); + } + + // All ids must be unique and fresh. + return !fuzzerutil::HasDuplicates(maybe_fresh_ids) && + std::all_of(maybe_fresh_ids.begin(), maybe_fresh_ids.end(), + [ir_context](uint32_t id) { + return fuzzerutil::IsFreshId(ir_context, id); + }); +} + +void TransformationPropagateInstructionDown::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Get instruction to propagate down. There must be one. + auto* inst_to_propagate = + GetInstructionToPropagate(ir_context, message_.block_id()); + assert(inst_to_propagate && "There must be an instruction to propagate"); + + auto successor_id_to_fresh_id = + fuzzerutil::RepeatedUInt32PairToMap(message_.successor_id_to_fresh_id()); + std::vector created_inst_ids; + auto successor_ids = GetAcceptableSuccessors(ir_context, message_.block_id()); + + // Clone |inst_to_propagate| into every successor. + for (auto successor_id : successor_ids) { + std::unique_ptr clone( + inst_to_propagate->Clone(ir_context)); + + uint32_t new_result_id; + if (successor_id_to_fresh_id.count(successor_id)) { + new_result_id = successor_id_to_fresh_id.at(successor_id); + } else { + assert(transformation_context->GetOverflowIdSource()->HasOverflowIds() && + "Overflow ids must be available"); + new_result_id = + transformation_context->GetOverflowIdSource()->GetNextOverflowId(); + successor_id_to_fresh_id[successor_id] = new_result_id; + } + + clone->SetResultId(new_result_id); + fuzzerutil::UpdateModuleIdBound(ir_context, new_result_id); + + auto* insert_before_inst = GetFirstInsertBeforeInstruction( + ir_context, successor_id, clone->opcode()); + assert(insert_before_inst && "Can't insert into one of the successors"); + + insert_before_inst->InsertBefore(std::move(clone)); + created_inst_ids.push_back(new_result_id); + } + + // Add an OpPhi instruction into the module if possible. + if (auto merge_block_id = GetOpPhiBlockId( + ir_context, message_.block_id(), *inst_to_propagate, successor_ids)) { + opt::Instruction::OperandList in_operands; + std::unordered_set visited_predecessors; + for (auto predecessor_id : ir_context->cfg()->preds(merge_block_id)) { + if (visited_predecessors.count(predecessor_id)) { + // Merge block might have multiple identical predecessors. + continue; + } + + visited_predecessors.insert(predecessor_id); + + const auto* dominator_analysis = ir_context->GetDominatorAnalysis( + ir_context->cfg()->block(message_.block_id())->GetParent()); + + // Find the successor of |source_block| that dominates the predecessor of + // the merge block |predecessor_id|. + auto it = std::find_if( + successor_ids.begin(), successor_ids.end(), + [predecessor_id, dominator_analysis](uint32_t successor_id) { + return dominator_analysis->Dominates(successor_id, predecessor_id); + }); + + // OpPhi requires a single operand pair for every predecessor of the + // OpPhi's block. + assert(it != successor_ids.end() && "Unable to insert OpPhi"); + + in_operands.push_back( + {SPV_OPERAND_TYPE_ID, {successor_id_to_fresh_id.at(*it)}}); + in_operands.push_back({SPV_OPERAND_TYPE_ID, {predecessor_id}}); + } + + ir_context->cfg() + ->block(merge_block_id) + ->begin() + ->InsertBefore(MakeUnique( + ir_context, SpvOpPhi, inst_to_propagate->type_id(), + message_.phi_fresh_id(), std::move(in_operands))); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.phi_fresh_id()); + created_inst_ids.push_back(message_.phi_fresh_id()); + } + + // Make sure analyses are updated when we adjust users of |inst_to_propagate|. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // Copy decorations from the original instructions to its propagated copies. + for (auto id : created_inst_ids) { + ir_context->get_decoration_mgr()->CloneDecorations( + inst_to_propagate->result_id(), id); + } + + // Remove all decorations from the original instruction. + ir_context->get_decoration_mgr()->RemoveDecorationsFrom( + inst_to_propagate->result_id()); + + // Update every use of the |inst_to_propagate| with a result id of some of the + // newly created instructions. + ir_context->get_def_use_mgr()->ForEachUse( + inst_to_propagate, [ir_context, &created_inst_ids]( + opt::Instruction* user, uint32_t operand_index) { + assert(ir_context->get_instr_block(user) && + "All decorations should have already been adjusted"); + + auto in_operand_index = + fuzzerutil::InOperandIndexFromOperandIndex(*user, operand_index); + for (auto id : created_inst_ids) { + if (fuzzerutil::IdIsAvailableAtUse(ir_context, user, in_operand_index, + id)) { + user->SetInOperand(in_operand_index, {id}); + return; + } + } + + // Every user of |inst_to_propagate| must be updated since we will + // remove that instruction from the module. + assert(false && "Every user of |inst_to_propagate| must be updated"); + }); + + // Add synonyms about newly created instructions. + assert(inst_to_propagate->HasResultId() && + "Result id is required to add facts"); + if (transformation_context->GetFactManager()->IdIsIrrelevant( + inst_to_propagate->result_id())) { + for (auto id : created_inst_ids) { + transformation_context->GetFactManager()->AddFactIdIsIrrelevant(id); + } + } else { + std::vector non_irrelevant_ids; + for (auto id : created_inst_ids) { + // |id| can be irrelevant implicitly (e.g. if we propagate it into a dead + // block). + if (!transformation_context->GetFactManager()->IdIsIrrelevant(id)) { + non_irrelevant_ids.push_back(id); + } + } + + if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant( + inst_to_propagate->result_id())) { + for (auto id : non_irrelevant_ids) { + transformation_context->GetFactManager() + ->AddFactValueOfPointeeIsIrrelevant(id); + } + } + + for (auto id : non_irrelevant_ids) { + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(id, {}), + MakeDataDescriptor(non_irrelevant_ids[0], {})); + } + } + + // Remove the propagated instruction from the module. + ir_context->KillInst(inst_to_propagate); + + // We've adjusted all users - make sure these changes are analyzed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationPropagateInstructionDown::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_propagate_instruction_down() = message_; + return result; +} + +bool TransformationPropagateInstructionDown::IsOpcodeSupported(SpvOp opcode) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605): + // We only support "simple" instructions that don't work with memory. + // We should extend this so that we support the ones that modify the memory + // too. + switch (opcode) { + case SpvOpUndef: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpArrayLength: + case SpvOpVectorExtractDynamic: + case SpvOpVectorInsertDynamic: + case SpvOpVectorShuffle: + case SpvOpCompositeConstruct: + case SpvOpCompositeExtract: + case SpvOpCompositeInsert: + case SpvOpCopyObject: + case SpvOpTranspose: + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpUConvert: + case SpvOpSConvert: + case SpvOpFConvert: + case SpvOpQuantizeToF16: + case SpvOpSatConvertSToU: + case SpvOpSatConvertUToS: + case SpvOpBitcast: + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpAny: + case SpvOpAll: + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpLogicalNot: + case SpvOpSelect: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitFieldInsert: + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: + case SpvOpBitReverse: + case SpvOpBitCount: + case SpvOpCopyLogical: + case SpvOpPtrEqual: + case SpvOpPtrNotEqual: + return true; + default: + return false; + } +} + +opt::Instruction* +TransformationPropagateInstructionDown::GetInstructionToPropagate( + opt::IRContext* ir_context, uint32_t block_id) { + auto* block = ir_context->cfg()->block(block_id); + assert(block && "|block_id| is invalid"); + + for (auto it = block->rbegin(); it != block->rend(); ++it) { + if (!it->result_id() || !it->type_id() || + !IsOpcodeSupported(it->opcode())) { + continue; + } + + auto all_users_from_different_blocks = + ir_context->get_def_use_mgr()->WhileEachUser( + &*it, [ir_context, block](opt::Instruction* user) { + return ir_context->get_instr_block(user) != block; + }); + + if (!all_users_from_different_blocks) { + // We can't propagate an instruction if it's used in the same block. + continue; + } + + return &*it; + } + + return nullptr; +} + +bool TransformationPropagateInstructionDown::IsApplicableToBlock( + opt::IRContext* ir_context, uint32_t block_id) { + // Check that |block_id| is valid. + const auto* block = fuzzerutil::MaybeFindBlock(ir_context, block_id); + if (!block) { + return false; + } + + const auto* dominator_analysis = + ir_context->GetDominatorAnalysis(block->GetParent()); + + // |block| must be reachable. + if (!dominator_analysis->IsReachable(block)) { + return false; + } + + // The block must have an instruction to propagate. + const auto* inst_to_propagate = + GetInstructionToPropagate(ir_context, block_id); + if (!inst_to_propagate) { + return false; + } + + // Check that |block| has successors. + auto successor_ids = GetAcceptableSuccessors(ir_context, block_id); + if (successor_ids.empty()) { + return false; + } + + // Check that |successor_block| doesn't have any OpPhi instructions that + // use |inst|. + for (auto successor_id : successor_ids) { + for (const auto& maybe_phi_inst : *ir_context->cfg()->block(successor_id)) { + if (maybe_phi_inst.opcode() != SpvOpPhi) { + // OpPhis can be intermixed with OpLine and OpNoLine. + continue; + } + + for (uint32_t i = 0; i < maybe_phi_inst.NumInOperands(); i += 2) { + if (maybe_phi_inst.GetSingleWordInOperand(i) == + inst_to_propagate->result_id()) { + return false; + } + } + } + } + + // Get the result id of the block we will insert OpPhi instruction into. + // This is either 0 or a result id of some merge block in the function. + auto phi_block_id = + GetOpPhiBlockId(ir_context, block_id, *inst_to_propagate, successor_ids); + + // Make sure we can adjust all users of the propagated instruction. + return ir_context->get_def_use_mgr()->WhileEachUse( + inst_to_propagate, + [ir_context, &successor_ids, dominator_analysis, phi_block_id]( + opt::Instruction* user, uint32_t index) { + const auto* user_block = ir_context->get_instr_block(user); + + if (!user_block) { + // |user| might be a global instruction (e.g. OpDecorate). + return true; + } + + // Check that at least one of the ids in |successor_ids| or a + // |phi_block_id| dominates |user|'s block (or its predecessor if the + // user is an OpPhi). We can't use fuzzerutil::IdIsAvailableAtUse since + // the id in question hasn't yet been created in the module. + auto block_id_to_dominate = user->opcode() == SpvOpPhi + ? user->GetSingleWordOperand(index + 1) + : user_block->id(); + + if (phi_block_id != 0 && + dominator_analysis->Dominates(phi_block_id, block_id_to_dominate)) { + return true; + } + + return std::any_of( + successor_ids.begin(), successor_ids.end(), + [dominator_analysis, block_id_to_dominate](uint32_t id) { + return dominator_analysis->Dominates(id, block_id_to_dominate); + }); + }); +} + +opt::Instruction* +TransformationPropagateInstructionDown::GetFirstInsertBeforeInstruction( + opt::IRContext* ir_context, uint32_t block_id, SpvOp opcode) { + auto* block = ir_context->cfg()->block(block_id); + + auto it = block->begin(); + + while (it != block->end() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(opcode, it)) { + ++it; + } + + return it == block->end() ? nullptr : &*it; +} + +std::unordered_set +TransformationPropagateInstructionDown::GetAcceptableSuccessors( + opt::IRContext* ir_context, uint32_t block_id) { + const auto* block = ir_context->cfg()->block(block_id); + assert(block && "|block_id| is invalid"); + + const auto* inst = GetInstructionToPropagate(ir_context, block_id); + assert(inst && "The block must have an instruction to propagate"); + + std::unordered_set result; + block->ForEachSuccessorLabel([ir_context, &result, + inst](uint32_t successor_id) { + if (result.count(successor_id)) { + return; + } + + auto* successor_block = ir_context->cfg()->block(successor_id); + + // We can't propagate |inst| into |successor_block| if the latter is not + // dominated by the |inst|'s dependencies. + if (!inst->WhileEachInId([ir_context, successor_block](const uint32_t* id) { + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, &*successor_block->begin(), *id); + })) { + return; + } + + // We don't propagate any "special" instructions (e.g. OpSelectionMerge + // etc), thus, insertion point must always exist if the module is valid. + assert(GetFirstInsertBeforeInstruction(ir_context, successor_id, + inst->opcode()) && + "There must exist an insertion point."); + + result.insert(successor_id); + }); + + return result; +} + +uint32_t TransformationPropagateInstructionDown::GetOpPhiBlockId( + opt::IRContext* ir_context, uint32_t block_id, + const opt::Instruction& inst_to_propagate, + const std::unordered_set& successor_ids) { + const auto* block = ir_context->cfg()->block(block_id); + + // |block_id| must belong to some construct. + auto merge_block_id = + block->GetMergeInst() + ? block->GetMergeInst()->GetSingleWordInOperand(0) + : ir_context->GetStructuredCFGAnalysis()->MergeBlock(block_id); + if (!merge_block_id) { + return 0; + } + + const auto* dominator_analysis = + ir_context->GetDominatorAnalysis(block->GetParent()); + + // Check that |merge_block_id| is reachable in the CFG and |block_id| + // dominates |merge_block_id|. + if (!dominator_analysis->IsReachable(merge_block_id) || + !dominator_analysis->Dominates(block_id, merge_block_id)) { + return 0; + } + + // We can't insert an OpPhi into |merge_block_id| if it's an acceptable + // successor of |block_id|. + if (successor_ids.count(merge_block_id)) { + return 0; + } + + // All predecessors of the merge block must be dominated by at least one + // successor of the |block_id|. + assert(!ir_context->cfg()->preds(merge_block_id).empty() && + "Merge block must be reachable"); + for (auto predecessor_id : ir_context->cfg()->preds(merge_block_id)) { + if (std::none_of( + successor_ids.begin(), successor_ids.end(), + [dominator_analysis, predecessor_id](uint32_t successor_id) { + return dominator_analysis->Dominates(successor_id, + predecessor_id); + })) { + return 0; + } + } + + const auto* propagate_type = + ir_context->get_type_mgr()->GetType(inst_to_propagate.type_id()); + assert(propagate_type && "|inst_to_propagate| must have a valid type"); + + // VariablePointers capability implicitly declares + // VariablePointersStorageBuffer. We need those capabilities since otherwise + // OpPhi instructions cannot have operands of pointer types. + if (propagate_type->AsPointer() && + !ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)) { + return 0; + } + + return merge_block_id; +} + +std::unordered_set +TransformationPropagateInstructionDown::GetFreshIds() const { + std::unordered_set result = {message_.phi_fresh_id()}; + for (const auto& pair : message_.successor_id_to_fresh_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_down.h b/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_down.h new file mode 100644 index 0000000..7eca1ad --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_down.h @@ -0,0 +1,184 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_DOWN_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_DOWN_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPropagateInstructionDown : public Transformation { + public: + explicit TransformationPropagateInstructionDown( + const protobufs::TransformationPropagateInstructionDown& message); + + TransformationPropagateInstructionDown( + uint32_t block_id, uint32_t phi_fresh_id, + const std::map& successor_id_to_fresh_id); + + // - It should be possible to apply this transformation to |block_id| (see + // IsApplicableToBlock method). + // - Every acceptable successor of |block_id| (see GetAcceptableSuccessors + // method) must have an entry in the |successor_id_to_fresh_id| map unless + // overflow ids are available. + // - All values in |successor_id_to_fresh_id| and |phi_fresh_id| must be + // unique and fresh. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Adds a clone of the propagated instruction into every acceptable + // successor of |block_id|. + // - Removes the original instruction. + // - Creates an OpPhi instruction if possible, that tries to group created + // clones. + // - If the original instruction's id was irrelevant - marks created + // instructions as irrelevant. Otherwise, marks the created instructions as + // synonymous to each other if possible (i.e. skips instructions, copied + // into dead blocks). + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if this transformation can be applied to the block with id + // |block_id|. Concretely, returns true iff: + // - |block_id| is a result id of some reachable basic block in the module. + // - the block has an instruction to propagate (see + // GetInstructionToPropagate method). + // - the block has at least one acceptable successor (see + // GetAcceptableSuccessors method). + // - none of the acceptable successors have OpPhi instructions that use the + // original instruction. + // - it is possible to replace every use of the original instruction with some + // of the propagated instructions (or an OpPhi if we can create it - see + // GetOpPhiBlockId method). + static bool IsApplicableToBlock(opt::IRContext* ir_context, + uint32_t block_id); + + // Returns ids of successors of |block_id|, that can be used to propagate an + // instruction into. Concretely, a successor block is acceptable if all + // dependencies of the propagated instruction dominate it. Note that this + // implies that an acceptable successor must be reachable in the CFG. + // For example: + // %1 = OpLabel + // OpSelectionMerge %2 None + // OpBranchConditional %cond %2 %3 + // %3 = OpLabel + // %4 = OpUndef %int + // %5 = OpCopyObject %int %4 + // OpBranch %2 + // %2 = OpLabel + // ... + // In this example, %2 is not an acceptable successor of %3 since one of the + // dependencies (%4) of the propagated instruction (%5) does not dominate it. + static std::unordered_set GetAcceptableSuccessors( + opt::IRContext* ir_context, uint32_t block_id); + + std::unordered_set GetFreshIds() const override; + + private: + // Returns the last possible instruction in the |block_id| that satisfies the + // following properties: + // - has result id + // - has type id + // - has supported opcode (see IsOpcodeSupported method) + // - has no users in its basic block. + // Returns nullptr if no such an instruction exists. For example: + // %1 = OpLabel + // %2 = OpUndef %int + // %3 = OpUndef %int + // OpStore %var %3 + // OpBranch %some_block + // In this example: + // - We cannot propagate OpBranch nor OpStore since they both have unsupported + // opcodes and have neither result ids nor type ids. + // - We cannot propagate %3 either since it is used by OpStore. + // - We can propagate %2 since it satisfies all our conditions. + // The basic idea behind this method it to make sure that the returned + // instruction will not break domination rules in its original block when + // propagated. + static opt::Instruction* GetInstructionToPropagate(opt::IRContext* ir_context, + uint32_t block_id); + + // Returns true if |opcode| is supported by this transformation. + static bool IsOpcodeSupported(SpvOp opcode); + + // Returns the first instruction in the |block| that allows us to insert + // |opcode| above itself. Returns nullptr is no such instruction exists. + static opt::Instruction* GetFirstInsertBeforeInstruction( + opt::IRContext* ir_context, uint32_t block_id, SpvOp opcode); + + // Returns a result id of a basic block, where an OpPhi instruction can be + // inserted. Returns nullptr if it's not possible to create an OpPhi. The + // created OpPhi instruction groups all the propagated clones of the original + // instruction. |block_id| is a result id of the block we propagate the + // instruction from. |successor_ids| contains result ids of the successors we + // propagate the instruction into. Concretely, returns a non-null value if: + // - |block_id| is in some construct. + // - The merge block of that construct is reachable. + // - |block_id| dominates that merge block. + // - That merge block may not be an acceptable successor of |block_id|. + // - There must be at least one |block_id|'s acceptable successor for every + // predecessor of the merge block, dominating that predecessor. + // - We can't create an OpPhi if the module has neither VariablePointers nor + // VariablePointersStorageBuffer capabilities. + // A simple example of when we can insert an OpPhi instruction is: + // - This snippet of code: + // %1 = OpLabel + // %2 = OpUndef %int + // OpSelectionMerge %5 None + // OpBranchConditional %cond %3 %4 + // %3 = OpLabel + // OpBranch %5 + // %4 = OpLabel + // OpBranch %5 + // %5 = OpLabel + // ... + // will be transformed into the following one (if %2 is propagated): + // %1 = OpLabel + // OpSelectionMerge %5 None + // OpBranchConditional %cond %3 %4 + // %3 = OpLabel + // %6 = OpUndef %int + // OpBranch %5 + // %4 = OpLabel + // %7 = OpUndef %int + // OpBranch %5 + // %5 = OpLabel + // %8 = OpPhi %int %6 %3 %7 %4 + // ... + // The fact that we introduce an OpPhi allows us to increase the applicability + // of the transformation. Concretely, we wouldn't be able to apply it in the + // example above if %2 were used in %5. Some more complicated examples can be + // found in unit tests. + static uint32_t GetOpPhiBlockId( + opt::IRContext* ir_context, uint32_t block_id, + const opt::Instruction& inst_to_propagate, + const std::unordered_set& successor_ids); + + protobufs::TransformationPropagateInstructionDown message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_DOWN_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp b/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp new file mode 100644 index 0000000..a2cacf4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_up.cpp @@ -0,0 +1,417 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_propagate_instruction_up.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { +namespace { + +uint32_t GetResultIdFromLabelId(const opt::Instruction& phi_inst, + uint32_t label_id) { + assert(phi_inst.opcode() == SpvOpPhi && "|phi_inst| is not an OpPhi"); + + for (uint32_t i = 1; i < phi_inst.NumInOperands(); i += 2) { + if (phi_inst.GetSingleWordInOperand(i) == label_id) { + return phi_inst.GetSingleWordInOperand(i - 1); + } + } + + return 0; +} + +bool ContainsPointers(const opt::analysis::Type& type) { + switch (type.kind()) { + case opt::analysis::Type::kPointer: + return true; + case opt::analysis::Type::kStruct: + return std::any_of(type.AsStruct()->element_types().begin(), + type.AsStruct()->element_types().end(), + [](const opt::analysis::Type* element_type) { + return ContainsPointers(*element_type); + }); + default: + return false; + } +} + +bool HasValidDependencies(opt::IRContext* ir_context, opt::Instruction* inst) { + const auto* inst_block = ir_context->get_instr_block(inst); + assert(inst_block && + "This function shouldn't be applied to global instructions or function" + "parameters"); + + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + const auto& operand = inst->GetInOperand(i); + if (operand.type != SPV_OPERAND_TYPE_ID) { + // Consider only operands. + continue; + } + + auto* dependency = ir_context->get_def_use_mgr()->GetDef(operand.words[0]); + assert(dependency && "Operand has invalid id"); + + if (ir_context->get_instr_block(dependency) == inst_block && + dependency->opcode() != SpvOpPhi) { + // |dependency| is "valid" if it's an OpPhi from the same basic block or + // an instruction from a different basic block. + return false; + } + } + + return true; +} + +} // namespace + +TransformationPropagateInstructionUp::TransformationPropagateInstructionUp( + const protobufs::TransformationPropagateInstructionUp& message) + : message_(message) {} + +TransformationPropagateInstructionUp::TransformationPropagateInstructionUp( + uint32_t block_id, + const std::map& predecessor_id_to_fresh_id) { + message_.set_block_id(block_id); + *message_.mutable_predecessor_id_to_fresh_id() = + fuzzerutil::MapToRepeatedUInt32Pair(predecessor_id_to_fresh_id); +} + +bool TransformationPropagateInstructionUp::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // Check that we can apply this transformation to the |block_id|. + if (!IsApplicableToBlock(ir_context, message_.block_id())) { + return false; + } + + const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap( + message_.predecessor_id_to_fresh_id()); + for (auto id : ir_context->cfg()->preds(message_.block_id())) { + // Each predecessor must have a fresh id in the |predecessor_id_to_fresh_id| + // map. + if (!predecessor_id_to_fresh_id.count(id)) { + return false; + } + } + + std::vector maybe_fresh_ids; + maybe_fresh_ids.reserve(predecessor_id_to_fresh_id.size()); + for (const auto& entry : predecessor_id_to_fresh_id) { + maybe_fresh_ids.push_back(entry.second); + } + + // All ids must be unique and fresh. + return !fuzzerutil::HasDuplicates(maybe_fresh_ids) && + std::all_of(maybe_fresh_ids.begin(), maybe_fresh_ids.end(), + [ir_context](uint32_t id) { + return fuzzerutil::IsFreshId(ir_context, id); + }); +} + +void TransformationPropagateInstructionUp::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* inst = GetInstructionToPropagate(ir_context, message_.block_id()); + assert(inst && + "The block must have at least one supported instruction to propagate"); + assert(inst->result_id() && inst->type_id() && + "|inst| must have a result id and a type id"); + + opt::Instruction::OperandList op_phi_operands; + const auto predecessor_id_to_fresh_id = fuzzerutil::RepeatedUInt32PairToMap( + message_.predecessor_id_to_fresh_id()); + std::unordered_set visited_predecessors; + for (auto predecessor_id : ir_context->cfg()->preds(message_.block_id())) { + // A block can have multiple identical predecessors. + if (visited_predecessors.count(predecessor_id)) { + continue; + } + + visited_predecessors.insert(predecessor_id); + + auto new_result_id = predecessor_id_to_fresh_id.at(predecessor_id); + + // Compute InOperands for the OpPhi instruction to be inserted later. + op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {new_result_id}}); + op_phi_operands.push_back({SPV_OPERAND_TYPE_ID, {predecessor_id}}); + + // Create a clone of the |inst| to be inserted into the |predecessor_id|. + std::unique_ptr clone(inst->Clone(ir_context)); + clone->SetResultId(new_result_id); + + fuzzerutil::UpdateModuleIdBound(ir_context, new_result_id); + + // Adjust |clone|'s operands to account for possible dependencies on OpPhi + // instructions from the same basic block. + for (uint32_t i = 0; i < clone->NumInOperands(); ++i) { + auto& operand = clone->GetInOperand(i); + if (operand.type != SPV_OPERAND_TYPE_ID) { + // Consider only ids. + continue; + } + + const auto* dependency_inst = + ir_context->get_def_use_mgr()->GetDef(operand.words[0]); + assert(dependency_inst && "|clone| depends on an invalid id"); + + if (ir_context->get_instr_block(dependency_inst->result_id()) != + ir_context->cfg()->block(message_.block_id())) { + // We don't need to adjust anything if |dependency_inst| is from a + // different block, a global instruction or a function parameter. + continue; + } + + assert(dependency_inst->opcode() == SpvOpPhi && + "Propagated instruction can depend only on OpPhis from the same " + "basic block or instructions from different basic blocks"); + + auto new_id = GetResultIdFromLabelId(*dependency_inst, predecessor_id); + assert(new_id && "OpPhi instruction is missing a predecessor"); + operand.words[0] = new_id; + } + + auto* insert_before_inst = fuzzerutil::GetLastInsertBeforeInstruction( + ir_context, predecessor_id, clone->opcode()); + assert(insert_before_inst && "Can't insert |clone| into |predecessor_id"); + + insert_before_inst->InsertBefore(std::move(clone)); + } + + // Insert an OpPhi instruction into the basic block of |inst|. + ir_context->get_instr_block(inst)->begin()->InsertBefore( + MakeUnique(ir_context, SpvOpPhi, inst->type_id(), + inst->result_id(), + std::move(op_phi_operands))); + + // Remove |inst| from the basic block. + ir_context->KillInst(inst); + + // We have changed the module so most analyzes are now invalid. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationPropagateInstructionUp::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_propagate_instruction_up() = message_; + return result; +} + +bool TransformationPropagateInstructionUp::IsOpcodeSupported(SpvOp opcode) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3605): + // We only support "simple" instructions that don't work with memory. + // We should extend this so that we support the ones that modify the memory + // too. + switch (opcode) { + case SpvOpUndef: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpArrayLength: + case SpvOpVectorExtractDynamic: + case SpvOpVectorInsertDynamic: + case SpvOpVectorShuffle: + case SpvOpCompositeConstruct: + case SpvOpCompositeExtract: + case SpvOpCompositeInsert: + case SpvOpCopyObject: + case SpvOpTranspose: + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpUConvert: + case SpvOpSConvert: + case SpvOpFConvert: + case SpvOpQuantizeToF16: + case SpvOpSatConvertSToU: + case SpvOpSatConvertUToS: + case SpvOpBitcast: + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpAny: + case SpvOpAll: + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpLogicalNot: + case SpvOpSelect: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitFieldInsert: + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: + case SpvOpBitReverse: + case SpvOpBitCount: + case SpvOpCopyLogical: + case SpvOpPtrEqual: + case SpvOpPtrNotEqual: + return true; + default: + return false; + } +} + +opt::Instruction* +TransformationPropagateInstructionUp::GetInstructionToPropagate( + opt::IRContext* ir_context, uint32_t block_id) { + auto* block = ir_context->cfg()->block(block_id); + assert(block && "|block_id| is invalid"); + + for (auto& inst : *block) { + // We look for the first instruction in the block that satisfies the + // following rules: + // - it's not an OpPhi + // - it must be supported by this transformation + // - it may depend only on instructions from different basic blocks or on + // OpPhi instructions from the same basic block. + if (inst.opcode() == SpvOpPhi || !IsOpcodeSupported(inst.opcode()) || + !inst.type_id() || !inst.result_id()) { + continue; + } + + const auto* inst_type = ir_context->get_type_mgr()->GetType(inst.type_id()); + assert(inst_type && "|inst| has invalid type"); + + if (inst_type->AsSampledImage()) { + // OpTypeSampledImage cannot be used as an argument to OpPhi instructions, + // thus we cannot support this type. + continue; + } + + if (!ir_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer) && + ContainsPointers(*inst_type)) { + // OpPhi supports pointer operands only with VariablePointers or + // VariablePointersStorageBuffer capabilities. + // + // Note that VariablePointers capability implicitly declares + // VariablePointersStorageBuffer capability. + continue; + } + + if (!HasValidDependencies(ir_context, &inst)) { + continue; + } + + return &inst; + } + + return nullptr; +} + +bool TransformationPropagateInstructionUp::IsApplicableToBlock( + opt::IRContext* ir_context, uint32_t block_id) { + // Check that |block_id| is valid. + const auto* label_inst = ir_context->get_def_use_mgr()->GetDef(block_id); + if (!label_inst || label_inst->opcode() != SpvOpLabel) { + return false; + } + + // Check that |block| has predecessors. + const auto& predecessors = ir_context->cfg()->preds(block_id); + if (predecessors.empty()) { + return false; + } + + // The block must contain an instruction to propagate. + const auto* inst_to_propagate = + GetInstructionToPropagate(ir_context, block_id); + if (!inst_to_propagate) { + return false; + } + + // We should be able to insert |inst_to_propagate| into every predecessor of + // |block|. + return std::all_of(predecessors.begin(), predecessors.end(), + [ir_context, inst_to_propagate](uint32_t predecessor_id) { + return fuzzerutil::GetLastInsertBeforeInstruction( + ir_context, predecessor_id, + inst_to_propagate->opcode()) != nullptr; + }); +} + +std::unordered_set TransformationPropagateInstructionUp::GetFreshIds() + const { + std::unordered_set result; + for (auto& pair : message_.predecessor_id_to_fresh_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h b/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h new file mode 100644 index 0000000..6354094 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_propagate_instruction_up.h @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPropagateInstructionUp : public Transformation { + public: + explicit TransformationPropagateInstructionUp( + const protobufs::TransformationPropagateInstructionUp& message); + + TransformationPropagateInstructionUp( + uint32_t block_id, + const std::map& predecessor_id_to_fresh_id); + + // - |block_id| must be a valid result id of some OpLabel instruction. + // - |block_id| must have at least one predecessor + // - |block_id| must contain an instruction that can be propagated using this + // transformation + // - the instruction can be propagated if: + // - it's not an OpPhi + // - it is supported by this transformation + // - it depends only on instructions from different basic blocks or on + // OpPhi instructions from the same basic block + // - it should be possible to insert the propagated instruction at the end of + // each |block_id|'s predecessor + // - |predecessor_id_to_fresh_id| must have an entry for at least every + // predecessor of |block_id| + // - each value in the |predecessor_id_to_fresh_id| map must be a fresh id + // - all fresh ids in the |predecessor_id_to_fresh_id| must be unique + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts a copy of the propagated instruction into each |block_id|'s + // predecessor. Replaces the original instruction with an OpPhi referring + // inserted copies. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if this transformation can be applied to the block with id + // |block_id|. Concretely, returns true iff: + // - |block_id| is a valid id of some block in the module + // - |block_id| has predecessors + // - |block_id| contains an instruction that can be propagated + // - it is possible to insert the propagated instruction into every + // |block_id|'s predecessor + static bool IsApplicableToBlock(opt::IRContext* ir_context, + uint32_t block_id); + + private: + // Returns the instruction that will be propagated into the predecessors of + // the |block_id|. Returns nullptr if no such an instruction exists. + static opt::Instruction* GetInstructionToPropagate(opt::IRContext* ir_context, + uint32_t block_id); + + // Returns true if |opcode| is supported by this transformation. + static bool IsOpcodeSupported(SpvOp opcode); + + protobufs::TransformationPropagateInstructionUp message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PROPAGATE_INSTRUCTION_UP_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp b/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp new file mode 100644 index 0000000..cdc40aa --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.cpp @@ -0,0 +1,173 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_push_id_through_variable.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationPushIdThroughVariable::TransformationPushIdThroughVariable( + const spvtools::fuzz::protobufs::TransformationPushIdThroughVariable& + message) + : message_(message) {} + +TransformationPushIdThroughVariable::TransformationPushIdThroughVariable( + uint32_t value_id, uint32_t value_synonym_id, uint32_t variable_id, + uint32_t variable_storage_class, uint32_t initializer_id, + const protobufs::InstructionDescriptor& instruction_descriptor) { + message_.set_value_id(value_id); + message_.set_value_synonym_id(value_synonym_id); + message_.set_variable_id(variable_id); + message_.set_variable_storage_class(variable_storage_class); + message_.set_initializer_id(initializer_id); + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationPushIdThroughVariable::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.value_synonym_id| and |message_.variable_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.value_synonym_id()) || + !fuzzerutil::IsFreshId(ir_context, message_.variable_id())) { + return false; + } + + // The instruction to insert before must be defined. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (!instruction_to_insert_before) { + return false; + } + + // It must be valid to insert the OpStore and OpLoad instruction before it. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpStore, instruction_to_insert_before) || + !fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpLoad, instruction_to_insert_before)) { + return false; + } + + // The instruction to insert before must belong to a reachable block. + auto basic_block = ir_context->get_instr_block(instruction_to_insert_before); + if (!fuzzerutil::BlockIsReachableInItsFunction(ir_context, basic_block)) { + return false; + } + + // The value instruction must be defined and have a type. + auto value_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.value_id()); + if (!value_instruction || !value_instruction->type_id()) { + return false; + } + + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, value_instruction->type_id(), + static_cast(message_.variable_storage_class())); + if (!pointer_type_id) { + return false; + } + + // |message_.variable_storage_class| must be private or function. + assert((message_.variable_storage_class() == SpvStorageClassPrivate || + message_.variable_storage_class() == SpvStorageClassFunction) && + "The variable storage class must be private or function."); + + // Check that initializer is valid. + const auto* constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.initializer_id()); + if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) || + value_instruction->type_id() != constant_inst->type_id()) { + return false; + } + + // |message_.value_id| must be available at the insertion point. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, instruction_to_insert_before, message_.value_id()); +} + +void TransformationPushIdThroughVariable::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto value_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.value_id()); + + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, value_instruction->type_id(), + static_cast(message_.variable_storage_class())); + assert(pointer_type_id && "The required pointer type must be available."); + + // Adds whether a global or local variable. + if (message_.variable_storage_class() == SpvStorageClassPrivate) { + fuzzerutil::AddGlobalVariable(ir_context, message_.variable_id(), + pointer_type_id, SpvStorageClassPrivate, + message_.initializer_id()); + } else { + auto function_id = ir_context + ->get_instr_block(FindInstruction( + message_.instruction_descriptor(), ir_context)) + ->GetParent() + ->result_id(); + fuzzerutil::AddLocalVariable(ir_context, message_.variable_id(), + pointer_type_id, function_id, + message_.initializer_id()); + } + + // First, insert the OpLoad instruction before |instruction_descriptor| and + // then insert the OpStore instruction before the OpLoad instruction. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.value_synonym_id()); + FindInstruction(message_.instruction_descriptor(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpLoad, value_instruction->type_id(), + message_.value_synonym_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}}))) + ->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.variable_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + // We should be able to create a synonym of |value_id| if it's not irrelevant. + if (fuzzerutil::CanMakeSynonymOf(ir_context, *transformation_context, + value_instruction) && + !transformation_context->GetFactManager()->IdIsIrrelevant( + message_.value_synonym_id())) { + // Adds the fact that |message_.value_synonym_id| + // and |message_.value_id| are synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.value_synonym_id(), {}), + MakeDataDescriptor(message_.value_id(), {})); + } +} + +protobufs::Transformation TransformationPushIdThroughVariable::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_push_id_through_variable() = message_; + return result; +} + +std::unordered_set TransformationPushIdThroughVariable::GetFreshIds() + const { + return {message_.value_synonym_id(), message_.variable_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.h b/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.h new file mode 100644 index 0000000..d055825 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_push_id_through_variable.h @@ -0,0 +1,70 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationPushIdThroughVariable : public Transformation { + public: + explicit TransformationPushIdThroughVariable( + const protobufs::TransformationPushIdThroughVariable& message); + + TransformationPushIdThroughVariable( + uint32_t value_id, uint32_t value_synonym_fresh_id, + uint32_t variable_fresh_id, uint32_t variable_storage_class, + uint32_t initializer_id, + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.value_id| must be an instruction result id that has the same + // type as the pointee type of |message_.pointer_id| + // - |message_.value_synonym_id| must be fresh + // - |message_.variable_id| must be fresh + // - |message_.variable_storage_class| must be either StorageClassPrivate or + // StorageClassFunction + // - |message_.initializer_id| must be a result id of some constant in the + // module. Its type must be equal to the pointee type of the variable that + // will be created. + // - |message_.instruction_descriptor| must identify an instruction + // which it is valid to insert the OpStore and OpLoad instructions before it + // and must be belongs to a reachable block. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Stores |value_id| to |variable_id|, loads |variable_id| to + // |value_synonym_id|. Adds the fact that |value_synonym_id| and |value_id| + // are synonymous if |value_id| and |value_synonym_id| are not irrelevant. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationPushIdThroughVariable message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_PUSH_ID_THROUGH_VARIABLE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp b/third_party/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp new file mode 100644 index 0000000..30ea94b --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_record_synonymous_constants.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_record_synonymous_constants.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationRecordSynonymousConstants:: + TransformationRecordSynonymousConstants( + const protobufs::TransformationRecordSynonymousConstants& message) + : message_(message) {} + +TransformationRecordSynonymousConstants:: + TransformationRecordSynonymousConstants(uint32_t constant1_id, + uint32_t constant2_id) { + message_.set_constant1_id(constant1_id); + message_.set_constant2_id(constant2_id); +} + +bool TransformationRecordSynonymousConstants::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The ids must be different + if (message_.constant1_id() == message_.constant2_id()) { + return false; + } + + if (transformation_context.GetFactManager()->IdIsIrrelevant( + message_.constant1_id()) || + transformation_context.GetFactManager()->IdIsIrrelevant( + message_.constant2_id())) { + return false; + } + + return AreEquivalentConstants(ir_context, message_.constant1_id(), + message_.constant2_id()); +} + +void TransformationRecordSynonymousConstants::Apply( + opt::IRContext* /*unused*/, + TransformationContext* transformation_context) const { + // Add the fact to the fact manager + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.constant1_id(), {}), + MakeDataDescriptor(message_.constant2_id(), {})); +} + +protobufs::Transformation TransformationRecordSynonymousConstants::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_record_synonymous_constants() = message_; + return result; +} + +bool TransformationRecordSynonymousConstants::AreEquivalentConstants( + opt::IRContext* ir_context, uint32_t constant_id1, uint32_t constant_id2) { + const auto* def_1 = ir_context->get_def_use_mgr()->GetDef(constant_id1); + const auto* def_2 = ir_context->get_def_use_mgr()->GetDef(constant_id2); + + // Check that the definitions exist + if (!def_1 || !def_2) { + // We don't use an assertion since otherwise the shrinker fails. + return false; + } + + auto constant1 = ir_context->get_constant_mgr()->GetConstantFromInst(def_1); + auto constant2 = ir_context->get_constant_mgr()->GetConstantFromInst(def_2); + + // The ids must refer to constants. + if (!constant1 || !constant2) { + return false; + } + + // The types must be compatible. + if (!fuzzerutil::TypesAreEqualUpToSign(ir_context, def_1->type_id(), + def_2->type_id())) { + return false; + } + + // If either constant is null, the other is equivalent iff it is zero-like + if (constant1->AsNullConstant()) { + return constant2->IsZero(); + } + + if (constant2->AsNullConstant()) { + return constant1->IsZero(); + } + + // If the constants are scalar, they are equal iff their words are the same + if (auto scalar1 = constant1->AsScalarConstant()) { + // Either both or neither constant is scalar since we've already checked + // that their types are compatible. + assert(constant2->AsScalarConstant() && "Both constants must be scalar"); + return scalar1->words() == constant2->AsScalarConstant()->words(); + } + + // The only remaining possibility is that the constants are composite + assert(constant1->AsCompositeConstant() && constant2->AsCompositeConstant() && + "Equivalence of constants can only be checked with scalar, composite " + "or null constants."); + + // Since the types match, we already know that the number of components is + // the same. We check that the input operands of the definitions are all + // constants and that they are pairwise equivalent. + for (uint32_t i = 0; i < def_1->NumInOperands(); i++) { + if (!AreEquivalentConstants(ir_context, def_1->GetSingleWordInOperand(i), + def_2->GetSingleWordInOperand(i))) { + return false; + } + } + + // If we get here, all the components are equivalent + return true; +} + +std::unordered_set +TransformationRecordSynonymousConstants::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h b/third_party/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h new file mode 100644 index 0000000..4376c87 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_record_synonymous_constants.h @@ -0,0 +1,73 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H +#define SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS_H + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationRecordSynonymousConstants : public Transformation { + public: + explicit TransformationRecordSynonymousConstants( + const protobufs::TransformationRecordSynonymousConstants& message); + + TransformationRecordSynonymousConstants(uint32_t constant1_id, + uint32_t constant2_id); + + // - |message_.constant_id| and |message_.synonym_id| are distinct ids + // of constants + // - |message_.constant_id| and |message_.synonym_id| refer to constants + // that are equivalent. + // Constants are equivalent if at least one of the following holds: + // - they are equal (i.e. they have the same type ids and equal values) + // - both of them represent zero-like values of compatible types + // - they are composite constants with compatible types and their + // components are pairwise equivalent + // Two types are compatible if at least one of the following holds: + // - they have the same id + // - they are integer scalar types with the same width + // - they are integer vectors and their components have the same width + // (this is always the case if the components are equivalent) + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds the fact that |message_.constant_id| and |message_.synonym_id| + // are synonyms to the fact manager. The module is not changed. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationRecordSynonymousConstants message_; + + // Returns true if the two given constants are equivalent + // (the description of IsApplicable specifies the conditions they must satisfy + // to be considered equivalent) + static bool AreEquivalentConstants(opt::IRContext* ir_context, + uint32_t constant_id1, + uint32_t constant_id2); +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_RECORD_SYNONYMOUS_CONSTANTS diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp new file mode 100644 index 0000000..a257515 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.cpp @@ -0,0 +1,237 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +namespace { +const uint32_t kOpCompositeExtractIndexLowOrderBits = 0; +const uint32_t kArithmeticInstructionIndexLeftInOperand = 0; +const uint32_t kArithmeticInstructionIndexRightInOperand = 1; +} // namespace + +TransformationReplaceAddSubMulWithCarryingExtended:: + TransformationReplaceAddSubMulWithCarryingExtended( + const spvtools::fuzz::protobufs:: + TransformationReplaceAddSubMulWithCarryingExtended& message) + : message_(message) {} + +TransformationReplaceAddSubMulWithCarryingExtended:: + TransformationReplaceAddSubMulWithCarryingExtended(uint32_t struct_fresh_id, + uint32_t result_id) { + message_.set_struct_fresh_id(struct_fresh_id); + message_.set_result_id(result_id); +} + +bool TransformationReplaceAddSubMulWithCarryingExtended::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.struct_fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id())) { + return false; + } + + // |message_.result_id| must refer to a suitable OpIAdd, OpISub or OpIMul + // instruction. The instruction must be defined. + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + if (instruction == nullptr) { + return false; + } + if (!TransformationReplaceAddSubMulWithCarryingExtended:: + IsInstructionSuitable(ir_context, *instruction)) { + return false; + } + + // The struct type for holding the intermediate result must exist in the + // module. The struct type is based on the operand type. + uint32_t operand_type_id = ir_context->get_def_use_mgr() + ->GetDef(instruction->GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)) + ->type_id(); + + uint32_t struct_type_id = fuzzerutil::MaybeGetStructType( + ir_context, {operand_type_id, operand_type_id}); + if (struct_type_id == 0) { + return false; + } + return true; +} + +void TransformationReplaceAddSubMulWithCarryingExtended::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // |message_.struct_fresh_id| must be fresh. + assert(fuzzerutil::IsFreshId(ir_context, message_.struct_fresh_id()) && + "|message_.struct_fresh_id| must be fresh"); + + // Get the signedness of an operand if it is an int or the signedness of a + // component if it is a vector. + auto type_id = + ir_context->get_def_use_mgr()->GetDef(message_.result_id())->type_id(); + auto type = ir_context->get_type_mgr()->GetType(type_id); + bool operand_is_signed; + if (type->kind() == opt::analysis::Type::kVector) { + auto operand_type = type->AsVector()->element_type(); + operand_is_signed = operand_type->AsInteger()->IsSigned(); + } else { + operand_is_signed = type->AsInteger()->IsSigned(); + } + + auto original_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.result_id()); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.struct_fresh_id()); + + // Determine the opcode of the new instruction that computes the result into a + // struct. + SpvOp new_instruction_opcode; + + switch (original_instruction->opcode()) { + case SpvOpIAdd: + new_instruction_opcode = SpvOpIAddCarry; + break; + case SpvOpISub: + new_instruction_opcode = SpvOpISubBorrow; + break; + case SpvOpIMul: + if (!operand_is_signed) { + new_instruction_opcode = SpvOpUMulExtended; + } else { + new_instruction_opcode = SpvOpSMulExtended; + } + break; + default: + assert(false && "The instruction has an unsupported opcode."); + return; + } + // Get the type of struct type id holding the intermediate result based on the + // operand type. + uint32_t operand_type_id = + ir_context->get_def_use_mgr() + ->GetDef(original_instruction->GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)) + ->type_id(); + + uint32_t struct_type_id = fuzzerutil::MaybeGetStructType( + ir_context, {operand_type_id, operand_type_id}); + // Avoid unused variables in release mode. + (void)struct_type_id; + assert(struct_type_id && "The struct type must exist in the module."); + + // Insert the new instruction that computes the result into a struct before + // the |original_instruction|. + original_instruction->InsertBefore(MakeUnique( + ir_context, new_instruction_opcode, struct_type_id, + message_.struct_fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, + {original_instruction->GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)}}, + {SPV_OPERAND_TYPE_ID, + {original_instruction->GetSingleWordInOperand( + kArithmeticInstructionIndexRightInOperand)}}}))); + + // Insert the OpCompositeExtract after the added instruction. This instruction + // takes the first component of the struct which represents low-order bits of + // the operation. This is the original result. + original_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, original_instruction->type_id(), + message_.result_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.struct_fresh_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {kOpCompositeExtractIndexLowOrderBits}}}))); + + // Remove the original instruction. + ir_context->KillInst(original_instruction); + + // We have modified the module so most analyzes are now invalid. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +bool TransformationReplaceAddSubMulWithCarryingExtended::IsInstructionSuitable( + opt::IRContext* ir_context, const opt::Instruction& instruction) { + auto instruction_opcode = instruction.opcode(); + + // Only instructions OpIAdd, OpISub, OpIMul are supported. + switch (instruction_opcode) { + case SpvOpIAdd: + case SpvOpISub: + case SpvOpIMul: + break; + default: + return false; + } + uint32_t operand_1_type_id = + ir_context->get_def_use_mgr() + ->GetDef(instruction.GetSingleWordInOperand( + kArithmeticInstructionIndexLeftInOperand)) + ->type_id(); + + uint32_t operand_2_type_id = + ir_context->get_def_use_mgr() + ->GetDef(instruction.GetSingleWordInOperand( + kArithmeticInstructionIndexRightInOperand)) + ->type_id(); + + uint32_t result_type_id = instruction.type_id(); + + // Both type ids of the operands and the result type ids must be equal. + if (operand_1_type_id != operand_2_type_id) { + return false; + } + if (operand_2_type_id != result_type_id) { + return false; + } + + // In case of OpIAdd and OpISub, the type must be unsigned. + auto type = ir_context->get_type_mgr()->GetType(instruction.type_id()); + + switch (instruction_opcode) { + case SpvOpIAdd: + case SpvOpISub: { + // In case of OpIAdd and OpISub if the operand is a vector, the component + // type must be unsigned. Otherwise (if the operand is an int), the + // operand must be unsigned. + bool operand_is_signed = + type->AsVector() + ? type->AsVector()->element_type()->AsInteger()->IsSigned() + : type->AsInteger()->IsSigned(); + if (operand_is_signed) { + return false; + } + } break; + default: + break; + } + return true; +} + +protobufs::Transformation +TransformationReplaceAddSubMulWithCarryingExtended::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_add_sub_mul_with_carrying_extended() = message_; + return result; +} + +std::unordered_set +TransformationReplaceAddSubMulWithCarryingExtended::GetFreshIds() const { + return {message_.struct_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h b/third_party/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h new file mode 100644 index 0000000..243542c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h @@ -0,0 +1,71 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceAddSubMulWithCarryingExtended + : public Transformation { + public: + explicit TransformationReplaceAddSubMulWithCarryingExtended( + const protobufs::TransformationReplaceAddSubMulWithCarryingExtended& + message); + + explicit TransformationReplaceAddSubMulWithCarryingExtended( + uint32_t struct_fresh_id, uint32_t result_id); + + // - |message_.struct_fresh_id| must be fresh. + // - |message_.result_id| must refer to an OpIAdd or OpISub or OpIMul + // instruction. In this instruction the result type id and the type ids of + // the operands must be the same. + // - The type of struct holding the intermediate result must exists in the + // module. + // - For OpIAdd, OpISub both operands must be unsigned. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A transformation that replaces instructions OpIAdd, OpISub, OpIMul with + // pairs of instructions. The first one (OpIAddCarry, OpISubBorrow, + // OpUMulExtended, OpSMulExtended) computes the result into a struct. The + // second one extracts the appropriate component from the struct to yield the + // original result. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Checks if an OpIAdd, OpISub or OpIMul instruction can be used by the + // transformation. + bool static IsInstructionSuitable(opt::IRContext* ir_context, + const opt::Instruction& instruction); + + private: + protobufs::TransformationReplaceAddSubMulWithCarryingExtended message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_ADD_SUB_MUL_WITH_CARRYING_EXTENDED_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp new file mode 100644 index 0000000..b458b56 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.cpp @@ -0,0 +1,327 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +// Given floating-point values |lhs| and |rhs|, and a floating-point binary +// operator |binop|, returns true if it is certain that 'lhs binop rhs' +// evaluates to |required_value|. +template +bool float_binop_evaluates_to(T lhs, T rhs, SpvOp binop, bool required_value) { + // Infinity and NaN values are conservatively treated as out of scope. + if (!std::isfinite(lhs) || !std::isfinite(rhs)) { + return false; + } + bool binop_result; + // The following captures the binary operators that spirv-fuzz can actually + // generate when turning a boolean constant into a binary expression. + switch (binop) { + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + binop_result = (lhs >= rhs); + break; + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + binop_result = (lhs > rhs); + break; + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + binop_result = (lhs <= rhs); + break; + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + binop_result = (lhs < rhs); + break; + default: + return false; + } + return binop_result == required_value; +} + +// Analogous to 'float_binop_evaluates_to', but for signed int values. +template +bool signed_int_binop_evaluates_to(T lhs, T rhs, SpvOp binop, + bool required_value) { + bool binop_result; + switch (binop) { + case SpvOpSGreaterThanEqual: + binop_result = (lhs >= rhs); + break; + case SpvOpSGreaterThan: + binop_result = (lhs > rhs); + break; + case SpvOpSLessThanEqual: + binop_result = (lhs <= rhs); + break; + case SpvOpSLessThan: + binop_result = (lhs < rhs); + break; + default: + return false; + } + return binop_result == required_value; +} + +// Analogous to 'float_binop_evaluates_to', but for unsigned int values. +template +bool unsigned_int_binop_evaluates_to(T lhs, T rhs, SpvOp binop, + bool required_value) { + bool binop_result; + switch (binop) { + case SpvOpUGreaterThanEqual: + binop_result = (lhs >= rhs); + break; + case SpvOpUGreaterThan: + binop_result = (lhs > rhs); + break; + case SpvOpULessThanEqual: + binop_result = (lhs <= rhs); + break; + case SpvOpULessThan: + binop_result = (lhs < rhs); + break; + default: + return false; + } + return binop_result == required_value; +} + +} // namespace + +TransformationReplaceBooleanConstantWithConstantBinary:: + TransformationReplaceBooleanConstantWithConstantBinary( + const spvtools::fuzz::protobufs:: + TransformationReplaceBooleanConstantWithConstantBinary& message) + : message_(message) {} + +TransformationReplaceBooleanConstantWithConstantBinary:: + TransformationReplaceBooleanConstantWithConstantBinary( + const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id, + uint32_t rhs_id, SpvOp comparison_opcode, + uint32_t fresh_id_for_binary_operation) { + *message_.mutable_id_use_descriptor() = id_use_descriptor; + message_.set_lhs_id(lhs_id); + message_.set_rhs_id(rhs_id); + message_.set_opcode(comparison_opcode); + message_.set_fresh_id_for_binary_operation(fresh_id_for_binary_operation); +} + +bool TransformationReplaceBooleanConstantWithConstantBinary::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The id for the binary result must be fresh + if (!fuzzerutil::IsFreshId(ir_context, + message_.fresh_id_for_binary_operation())) { + return false; + } + + // The used id must be for a boolean constant + auto boolean_constant = ir_context->get_def_use_mgr()->GetDef( + message_.id_use_descriptor().id_of_interest()); + if (!boolean_constant) { + return false; + } + if (!(boolean_constant->opcode() == SpvOpConstantFalse || + boolean_constant->opcode() == SpvOpConstantTrue)) { + return false; + } + + // The left-hand-side id must correspond to a constant instruction. + auto lhs_constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.lhs_id()); + if (!lhs_constant_inst) { + return false; + } + if (lhs_constant_inst->opcode() != SpvOpConstant) { + return false; + } + + // The right-hand-side id must correspond to a constant instruction. + auto rhs_constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.rhs_id()); + if (!rhs_constant_inst) { + return false; + } + if (rhs_constant_inst->opcode() != SpvOpConstant) { + return false; + } + + // The left- and right-hand side instructions must have the same type. + if (lhs_constant_inst->type_id() != rhs_constant_inst->type_id()) { + return false; + } + + // The expression 'LHS opcode RHS' must evaluate to the boolean constant. + auto lhs_constant = + ir_context->get_constant_mgr()->FindDeclaredConstant(message_.lhs_id()); + auto rhs_constant = + ir_context->get_constant_mgr()->FindDeclaredConstant(message_.rhs_id()); + bool expected_result = (boolean_constant->opcode() == SpvOpConstantTrue); + + const auto binary_opcode = static_cast(message_.opcode()); + + // We consider the floating point, signed and unsigned integer cases + // separately. In each case the logic is very similar. + if (lhs_constant->AsFloatConstant()) { + assert(rhs_constant->AsFloatConstant() && + "Both constants should be of the same type."); + if (lhs_constant->type()->AsFloat()->width() == 32) { + if (!float_binop_evaluates_to(lhs_constant->GetFloat(), + rhs_constant->GetFloat(), binary_opcode, + expected_result)) { + return false; + } + } else { + assert(lhs_constant->type()->AsFloat()->width() == 64); + if (!float_binop_evaluates_to(lhs_constant->GetDouble(), + rhs_constant->GetDouble(), binary_opcode, + expected_result)) { + return false; + } + } + } else { + assert(lhs_constant->AsIntConstant() && "Constants should be in or float."); + assert(rhs_constant->AsIntConstant() && + "Both constants should be of the same type."); + if (lhs_constant->type()->AsInteger()->IsSigned()) { + if (lhs_constant->type()->AsInteger()->width() == 32) { + if (!signed_int_binop_evaluates_to(lhs_constant->GetS32(), + rhs_constant->GetS32(), + binary_opcode, expected_result)) { + return false; + } + } else { + assert(lhs_constant->type()->AsInteger()->width() == 64); + if (!signed_int_binop_evaluates_to(lhs_constant->GetS64(), + rhs_constant->GetS64(), + binary_opcode, expected_result)) { + return false; + } + } + } else { + if (lhs_constant->type()->AsInteger()->width() == 32) { + if (!unsigned_int_binop_evaluates_to(lhs_constant->GetU32(), + rhs_constant->GetU32(), + binary_opcode, expected_result)) { + return false; + } + } else { + assert(lhs_constant->type()->AsInteger()->width() == 64); + if (!unsigned_int_binop_evaluates_to(lhs_constant->GetU64(), + rhs_constant->GetU64(), + binary_opcode, expected_result)) { + return false; + } + } + } + } + + // The id use descriptor must identify some instruction + auto instruction = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (instruction == nullptr) { + return false; + } + + // The instruction must not be an OpVariable, because (a) we cannot insert + // a binary operator before an OpVariable, but in any case (b) the + // constant we would be replacing is the initializer constant of the + // OpVariable, and this cannot be the result of a binary operation. + if (instruction->opcode() == SpvOpVariable) { + return false; + } + + return true; +} + +void TransformationReplaceBooleanConstantWithConstantBinary::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + ApplyWithResult(ir_context, transformation_context); +} + +opt::Instruction* +TransformationReplaceBooleanConstantWithConstantBinary::ApplyWithResult( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::analysis::Bool bool_type; + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_ID, {message_.lhs_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.rhs_id()}}}; + auto binary_instruction = MakeUnique( + ir_context, static_cast(message_.opcode()), + ir_context->get_type_mgr()->GetId(&bool_type), + message_.fresh_id_for_binary_operation(), operands); + opt::Instruction* result = binary_instruction.get(); + auto instruction_containing_constant_use = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + auto instruction_before_which_to_insert = instruction_containing_constant_use; + + // If |instruction_before_which_to_insert| is an OpPhi instruction, + // then |binary_instruction| will be inserted into the parent block associated + // with the OpPhi variable operand. + if (instruction_containing_constant_use->opcode() == SpvOpPhi) { + instruction_before_which_to_insert = + ir_context->cfg() + ->block(instruction_containing_constant_use->GetSingleWordInOperand( + message_.id_use_descriptor().in_operand_index() + 1)) + ->terminator(); + } + + // We want to insert the new instruction before the instruction that contains + // the use of the boolean, but we need to go backwards one more instruction if + // the using instruction is preceded by a merge instruction. + { + opt::Instruction* previous_node = + instruction_before_which_to_insert->PreviousNode(); + if (previous_node && (previous_node->opcode() == SpvOpLoopMerge || + previous_node->opcode() == SpvOpSelectionMerge)) { + instruction_before_which_to_insert = previous_node; + } + } + + instruction_before_which_to_insert->InsertBefore( + std::move(binary_instruction)); + instruction_containing_constant_use->SetInOperand( + message_.id_use_descriptor().in_operand_index(), + {message_.fresh_id_for_binary_operation()}); + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.fresh_id_for_binary_operation()); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + return result; +} + +protobufs::Transformation +TransformationReplaceBooleanConstantWithConstantBinary::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_boolean_constant_with_constant_binary() = message_; + return result; +} + +std::unordered_set +TransformationReplaceBooleanConstantWithConstantBinary::GetFreshIds() const { + return {message_.fresh_id_for_binary_operation()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h b/third_party/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h new file mode 100644 index 0000000..a0ece7f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceBooleanConstantWithConstantBinary + : public Transformation { + public: + explicit TransformationReplaceBooleanConstantWithConstantBinary( + const protobufs::TransformationReplaceBooleanConstantWithConstantBinary& + message); + + TransformationReplaceBooleanConstantWithConstantBinary( + const protobufs::IdUseDescriptor& id_use_descriptor, uint32_t lhs_id, + uint32_t rhs_id, SpvOp comparison_opcode, + uint32_t fresh_id_for_binary_operation); + + // - |message_.fresh_id_for_binary_operation| must not already be used by the + // module. + // - |message_.id_use_descriptor| must identify a use of a boolean constant c. + // - |message_.lhs_id| and |message.rhs_id| must be the ids of constant + // instructions with the same type + // - |message_.opcode| must be suitable for applying to |message.lhs_id| and + // |message_.rhs_id|, and the result must evaluate to the boolean constant + // c. + // - The boolean constant usage must not be an argument to OpPhi, because in + // this case it is not legal to insert a binary operator instruction right + // before the OpPhi. + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/2902): consider + // replacing a boolean in an OpPhi by adding a binary operator instruction + // to the parent block for the OpPhi. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // A new instruction is added before the boolean constant usage that computes + // the result of applying |message_.opcode| to |message_.lhs_id| and + // |message_.rhs_id| is added, with result id + // |message_.fresh_id_for_binary_operation|. The boolean constant usage is + // replaced with this result id. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + // The same as Apply, except that the newly-added binary instruction is + // returned. + opt::Instruction* ApplyWithResult( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceBooleanConstantWithConstantBinary message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_BOOLEAN_CONSTANT_WITH_CONSTANT_BINARY_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp new file mode 100644 index 0000000..e809012 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceBranchFromDeadBlockWithExit:: + TransformationReplaceBranchFromDeadBlockWithExit( + const spvtools::fuzz::protobufs:: + TransformationReplaceBranchFromDeadBlockWithExit& message) + : message_(message) {} + +TransformationReplaceBranchFromDeadBlockWithExit:: + TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id, + SpvOp opcode, + uint32_t return_value_id) { + message_.set_block_id(block_id); + message_.set_opcode(opcode); + message_.set_return_value_id(return_value_id); +} + +bool TransformationReplaceBranchFromDeadBlockWithExit::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The block whose terminator is to be changed must exist. + auto block = ir_context->get_instr_block(message_.block_id()); + if (!block) { + return false; + } + if (!BlockIsSuitable(ir_context, transformation_context, *block)) { + return false; + } + auto function_return_type_id = block->GetParent()->type_id(); + switch (message_.opcode()) { + case SpvOpKill: + for (auto& entry_point : ir_context->module()->entry_points()) { + if (entry_point.GetSingleWordInOperand(0) != + SpvExecutionModelFragment) { + // OpKill is only allowed in a fragment shader. This is a + // conservative check: if the module contains a non-fragment entry + // point then adding an OpKill might lead to OpKill being used in a + // non-fragment shader. + return false; + } + } + break; + case SpvOpReturn: + if (ir_context->get_def_use_mgr() + ->GetDef(function_return_type_id) + ->opcode() != SpvOpTypeVoid) { + // OpReturn is only allowed in a function with void return type. + return false; + } + break; + case SpvOpReturnValue: { + // If the terminator is to be changed to OpReturnValue, with + // |message_.return_value_id| being the value that will be returned, then + // |message_.return_value_id| must have a compatible type and be available + // at the block terminator. + auto return_value = + ir_context->get_def_use_mgr()->GetDef(message_.return_value_id()); + if (!return_value || return_value->type_id() != function_return_type_id) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, block->terminator(), message_.return_value_id())) { + return false; + } + break; + } + default: + assert(message_.opcode() == SpvOpUnreachable && + "Invalid early exit opcode."); + break; + } + return true; +} + +void TransformationReplaceBranchFromDeadBlockWithExit::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // If the successor block has OpPhi instructions then arguments related to + // |message_.block_id| need to be removed from these instruction. + auto block = ir_context->get_instr_block(message_.block_id()); + assert(block->terminator()->opcode() == SpvOpBranch && + "Precondition: the block must end with OpBranch."); + auto successor = ir_context->get_instr_block( + block->terminator()->GetSingleWordInOperand(0)); + successor->ForEachPhiInst([block](opt::Instruction* phi_inst) { + opt::Instruction::OperandList new_phi_in_operands; + for (uint32_t i = 0; i < phi_inst->NumInOperands(); i += 2) { + if (phi_inst->GetSingleWordInOperand(i + 1) == block->id()) { + continue; + } + new_phi_in_operands.emplace_back(phi_inst->GetInOperand(i)); + new_phi_in_operands.emplace_back(phi_inst->GetInOperand(i + 1)); + } + assert(new_phi_in_operands.size() == phi_inst->NumInOperands() - 2); + phi_inst->SetInOperands(std::move(new_phi_in_operands)); + }); + + // Rewrite the terminator of |message_.block_id|. + opt::Instruction::OperandList new_terminator_in_operands; + if (message_.opcode() == SpvOpReturnValue) { + new_terminator_in_operands.push_back( + {SPV_OPERAND_TYPE_ID, {message_.return_value_id()}}); + } + auto terminator = block->terminator(); + terminator->SetOpcode(static_cast(message_.opcode())); + terminator->SetInOperands(std::move(new_terminator_in_operands)); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +std::unordered_set +TransformationReplaceBranchFromDeadBlockWithExit::GetFreshIds() const { + return std::unordered_set(); +} + +protobufs::Transformation +TransformationReplaceBranchFromDeadBlockWithExit::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_branch_from_dead_block_with_exit() = message_; + return result; +} + +bool TransformationReplaceBranchFromDeadBlockWithExit::BlockIsSuitable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const opt::BasicBlock& block) { + // The block must be dead. + if (!transformation_context.GetFactManager()->BlockIsDead(block.id())) { + return false; + } + // The block's terminator must be OpBranch. + if (block.terminator()->opcode() != SpvOpBranch) { + return false; + } + if (ir_context->GetStructuredCFGAnalysis()->IsInContinueConstruct( + block.id())) { + // Early exits from continue constructs are not allowed as they would break + // the SPIR-V structured control flow rules. + return false; + } + // We only allow changing OpBranch to an early terminator if the target of the + // OpBranch has at least one other predecessor. + auto successor = ir_context->get_instr_block( + block.terminator()->GetSingleWordInOperand(0)); + if (ir_context->cfg()->preds(successor->id()).size() < 2) { + return false; + } + return true; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h b/third_party/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h new file mode 100644 index 0000000..e1418c9 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h @@ -0,0 +1,84 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_BRANCH_FROM_DEAD_BLOCK_WITH_EXIT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_BRANCH_FROM_DEAD_BLOCK_WITH_EXIT_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceBranchFromDeadBlockWithExit : public Transformation { + public: + explicit TransformationReplaceBranchFromDeadBlockWithExit( + const protobufs::TransformationReplaceBranchFromDeadBlockWithExit& + message); + + TransformationReplaceBranchFromDeadBlockWithExit(uint32_t block_id, + SpvOp opcode, + uint32_t return_value_id); + + // - |message_.block_id| must be the id of a dead block that is not part of + // a continue construct + // - |message_.block_id| must end with OpBranch + // - The successor of |message_.block_id| must have at least one other + // predecessor + // - |message_.opcode()| must be one of OpKill, OpReturn, OpReturnValue and + // OpUnreachable + // - |message_.opcode()| can only be OpKill the module's entry points all + // have Fragment execution mode + // - |message_.opcode()| can only be OpReturn if the return type of the + // function containing the block is void + // - If |message_.opcode()| is OpReturnValue then |message_.return_value_id| + // must be an id that is available at the block terminator and that matches + // the return type of the enclosing function + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Changes the terminator of |message_.block_id| to have opcode + // |message_.opcode|, additionally with input operand + // |message_.return_value_id| in the case that |message_.opcode| is + // OpReturnValue. + // + // If |message_.block_id|'s successor starts with OpPhi instructions these are + // updated so that they no longer refer to |message_.block_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |block| meets the criteria for having its + // terminator replaced with an early exit (see IsApplicable for details of the + // criteria.) + static bool BlockIsSuitable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context, + const opt::BasicBlock& block); + + private: + protobufs::TransformationReplaceBranchFromDeadBlockWithExit message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_BRANCH_FROM_DEAD_BLOCK_WITH_EXIT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp new file mode 100644 index 0000000..95932bf --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.cpp @@ -0,0 +1,294 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_constant_with_uniform.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceConstantWithUniform:: + TransformationReplaceConstantWithUniform( + const spvtools::fuzz::protobufs:: + TransformationReplaceConstantWithUniform& message) + : message_(message) {} + +TransformationReplaceConstantWithUniform:: + TransformationReplaceConstantWithUniform( + protobufs::IdUseDescriptor id_use, + protobufs::UniformBufferElementDescriptor uniform_descriptor, + uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load) { + *message_.mutable_id_use_descriptor() = std::move(id_use); + *message_.mutable_uniform_descriptor() = std::move(uniform_descriptor); + message_.set_fresh_id_for_access_chain(fresh_id_for_access_chain); + message_.set_fresh_id_for_load(fresh_id_for_load); +} + +std::unique_ptr +TransformationReplaceConstantWithUniform::MakeAccessChainInstruction( + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const { + // The input operands for the access chain. + opt::Instruction::OperandList operands_for_access_chain; + + opt::Instruction* uniform_variable = + FindUniformVariable(message_.uniform_descriptor(), ir_context, false); + + // The first input operand is the id of the uniform variable. + operands_for_access_chain.push_back( + {SPV_OPERAND_TYPE_ID, {uniform_variable->result_id()}}); + + // The other input operands are the ids of the constants used to index into + // the uniform. The uniform buffer descriptor specifies a series of literals; + // for each we find the id of the instruction that defines it, and add these + // instruction ids as operands. + opt::analysis::Integer int_type(32, true); + auto registered_int_type = + ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); + auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type); + for (auto index : message_.uniform_descriptor().index()) { + opt::analysis::IntConstant int_constant(registered_int_type, {index}); + auto constant_id = ir_context->get_constant_mgr()->FindDeclaredConstant( + &int_constant, int_type_id); + operands_for_access_chain.push_back({SPV_OPERAND_TYPE_ID, {constant_id}}); + } + + // The type id for the access chain is a uniform pointer with base type + // matching the given constant id type. + auto type_and_pointer_type = + ir_context->get_type_mgr()->GetTypeAndPointerType(constant_type_id, + SpvStorageClassUniform); + assert(type_and_pointer_type.first != nullptr); + assert(type_and_pointer_type.second != nullptr); + auto pointer_to_uniform_constant_type_id = + ir_context->get_type_mgr()->GetId(type_and_pointer_type.second.get()); + + return MakeUnique( + ir_context, SpvOpAccessChain, pointer_to_uniform_constant_type_id, + message_.fresh_id_for_access_chain(), operands_for_access_chain); +} + +std::unique_ptr +TransformationReplaceConstantWithUniform::MakeLoadInstruction( + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const { + opt::Instruction::OperandList operands_for_load = { + {SPV_OPERAND_TYPE_ID, {message_.fresh_id_for_access_chain()}}}; + return MakeUnique(ir_context, SpvOpLoad, constant_type_id, + message_.fresh_id_for_load(), + operands_for_load); +} + +opt::Instruction* +TransformationReplaceConstantWithUniform::GetInsertBeforeInstruction( + opt::IRContext* ir_context) const { + auto* result = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (!result) { + return nullptr; + } + + // The use might be in an OpPhi instruction. + if (result->opcode() == SpvOpPhi) { + // OpPhi instructions must be the first instructions in a block. Thus, we + // can't insert above the OpPhi instruction. Given the predecessor block + // that corresponds to the id use, get the last instruction in that block + // above which we can insert OpAccessChain and OpLoad. + return fuzzerutil::GetLastInsertBeforeInstruction( + ir_context, + result->GetSingleWordInOperand( + message_.id_use_descriptor().in_operand_index() + 1), + SpvOpLoad); + } + + // The only operand that we could've replaced in the OpBranchConditional is + // the condition id. But that operand has a boolean type and uniform variables + // can't store booleans (see the spec on OpTypeBool). Thus, |result| can't be + // an OpBranchConditional. + assert(result->opcode() != SpvOpBranchConditional && + "OpBranchConditional has no operands to replace"); + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, result) && + "We should be able to insert OpLoad and OpAccessChain at this point"); + return result; +} + +bool TransformationReplaceConstantWithUniform::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The following is really an invariant of the transformation rather than + // merely a requirement of the precondition. We check it here since we cannot + // check it in the message_ constructor. + assert(message_.fresh_id_for_access_chain() != message_.fresh_id_for_load() && + "Fresh ids for access chain and load result cannot be the same."); + + // The ids for the access chain and load instructions must both be fresh. + if (!fuzzerutil::IsFreshId(ir_context, + message_.fresh_id_for_access_chain())) { + return false; + } + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id_for_load())) { + return false; + } + + // The id specified in the id use descriptor must be that of a declared scalar + // constant. + auto declared_constant = ir_context->get_constant_mgr()->FindDeclaredConstant( + message_.id_use_descriptor().id_of_interest()); + if (!declared_constant) { + return false; + } + if (!declared_constant->AsScalarConstant()) { + return false; + } + + // The fact manager needs to believe that the uniform data element described + // by the uniform buffer element descriptor will hold a scalar value. + auto constant_id_associated_with_uniform = + transformation_context.GetFactManager()->GetConstantFromUniformDescriptor( + message_.uniform_descriptor()); + if (!constant_id_associated_with_uniform) { + return false; + } + auto constant_associated_with_uniform = + ir_context->get_constant_mgr()->FindDeclaredConstant( + constant_id_associated_with_uniform); + assert(constant_associated_with_uniform && + "The constant should be present in the module."); + if (!constant_associated_with_uniform->AsScalarConstant()) { + return false; + } + + // The types and values of the scalar value held in the id specified by the id + // use descriptor and in the uniform data element specified by the uniform + // buffer element descriptor need to match on both type and value. + if (!declared_constant->type()->IsSame( + constant_associated_with_uniform->type())) { + return false; + } + if (declared_constant->AsScalarConstant()->words() != + constant_associated_with_uniform->AsScalarConstant()->words()) { + return false; + } + + // The id use descriptor must identify some instruction with respect to the + // module. + auto instruction_using_constant = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (!instruction_using_constant) { + return false; + } + + // The use must not be a variable initializer; these are required to be + // constants, so it would be illegal to replace one with a uniform access. + if (instruction_using_constant->opcode() == SpvOpVariable) { + return false; + } + + // The module needs to have a uniform pointer type suitable for indexing into + // the uniform variable, i.e. matching the type of the constant we wish to + // replace with a uniform. + opt::analysis::Pointer pointer_to_type_of_constant(declared_constant->type(), + SpvStorageClassUniform); + if (!ir_context->get_type_mgr()->GetId(&pointer_to_type_of_constant)) { + return false; + } + + // In order to index into the uniform, the module has got to contain the int32 + // type, plus an OpConstant for each of the indices of interest. + opt::analysis::Integer int_type(32, true); + if (!ir_context->get_type_mgr()->GetId(&int_type)) { + return false; + } + auto registered_int_type = + ir_context->get_type_mgr()->GetRegisteredType(&int_type)->AsInteger(); + auto int_type_id = ir_context->get_type_mgr()->GetId(&int_type); + for (auto index : message_.uniform_descriptor().index()) { + opt::analysis::IntConstant int_constant(registered_int_type, {index}); + if (!ir_context->get_constant_mgr()->FindDeclaredConstant(&int_constant, + int_type_id)) { + return false; + } + } + + // Once all checks are completed, we should be able to safely insert + // OpAccessChain and OpLoad into the module. + assert(GetInsertBeforeInstruction(ir_context) && + "There must exist an instruction that we can use to insert " + "OpAccessChain and OpLoad above"); + + return true; +} + +void TransformationReplaceConstantWithUniform::Apply( + spvtools::opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + // Get the instruction that contains the id use we wish to replace. + auto* instruction_containing_constant_use = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + assert(instruction_containing_constant_use && + "Precondition requires that the id use can be found."); + assert(instruction_containing_constant_use->GetSingleWordInOperand( + message_.id_use_descriptor().in_operand_index()) == + message_.id_use_descriptor().id_of_interest() && + "Does not appear to be a usage of the desired id."); + + // The id of the type for the constant whose use we wish to replace. + auto constant_type_id = + ir_context->get_def_use_mgr() + ->GetDef(message_.id_use_descriptor().id_of_interest()) + ->type_id(); + + // Get an instruction that will be used to insert OpAccessChain and OpLoad. + auto* insert_before_inst = GetInsertBeforeInstruction(ir_context); + assert(insert_before_inst && + "There must exist an insertion point for OpAccessChain and OpLoad"); + + // Add an access chain instruction to target the uniform element. + insert_before_inst->InsertBefore( + MakeAccessChainInstruction(ir_context, constant_type_id)); + + // Add a load from this access chain. + insert_before_inst->InsertBefore( + MakeLoadInstruction(ir_context, constant_type_id)); + + // Adjust the instruction containing the usage of the constant so that this + // usage refers instead to the result of the load. + instruction_containing_constant_use->SetInOperand( + message_.id_use_descriptor().in_operand_index(), + {message_.fresh_id_for_load()}); + + // Update the module id bound to reflect the new instructions. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id_for_load()); + fuzzerutil::UpdateModuleIdBound(ir_context, + message_.fresh_id_for_access_chain()); + + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationReplaceConstantWithUniform::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_constant_with_uniform() = message_; + return result; +} + +std::unordered_set +TransformationReplaceConstantWithUniform::GetFreshIds() const { + return {message_.fresh_id_for_access_chain(), message_.fresh_id_for_load()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h b/third_party/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h new file mode 100644 index 0000000..9e09748 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_constant_with_uniform.h @@ -0,0 +1,99 @@ +#include + +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ + +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceConstantWithUniform : public Transformation { + public: + explicit TransformationReplaceConstantWithUniform( + const protobufs::TransformationReplaceConstantWithUniform& message); + + TransformationReplaceConstantWithUniform( + protobufs::IdUseDescriptor id_use, + protobufs::UniformBufferElementDescriptor uniform_descriptor, + uint32_t fresh_id_for_access_chain, uint32_t fresh_id_for_load); + + // - |message_.fresh_id_for_access_chain| and |message_.fresh_id_for_load| + // must be distinct fresh ids. + // - |message_.uniform_descriptor| specifies a result id and a list of integer + // literal indices. + // As an example, suppose |message_.uniform_descriptor| is (18, [0, 1, 0]) + // It is required that: + // - the result id (18 in our example) is the id of some uniform variable + // - the module contains an integer constant instruction corresponding to + // each of the literal indices; in our example there must thus be + // OpConstant instructions %A and %B say for each of 0 and 1 + // - it is legitimate to index into the uniform variable using the + // sequence of indices; in our example this means indexing into %18 + // using the sequence %A %B %A + // - the module contains a uniform pointer type corresponding to the type + // of the uniform data element obtained by following these indices + // - |message_.id_use_descriptor| identifies the use of some id %C. It is + // required that: + // - this use does indeed exist in the module + // - %C is an OpConstant + // - According to the fact manager, the uniform data element specified by + // |message_.uniform_descriptor| holds a value with the same type and + // value as %C + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Introduces two new instructions: + // - An access chain targeting the uniform data element specified by + // |message_.uniform_descriptor|, with result id + // |message_.fresh_id_for_access_chain| + // - A load from this access chain, with id |message_.fresh_id_for_load| + // - Replaces the id use specified by |message_.id_use_descriptor| with + // |message_.fresh_id_for_load| + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Helper method to create an access chain for the uniform element associated + // with the transformation. + std::unique_ptr MakeAccessChainInstruction( + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const; + + // Helper to create a load instruction. + std::unique_ptr MakeLoadInstruction( + spvtools::opt::IRContext* ir_context, uint32_t constant_type_id) const; + + // OpAccessChain and OpLoad will be inserted above the instruction returned + // by this function. Returns nullptr if no such instruction is present. + opt::Instruction* GetInsertBeforeInstruction( + opt::IRContext* ir_context) const; + + protobufs::TransformationReplaceConstantWithUniform message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_CONSTANT_WITH_UNIFORM_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp new file mode 100644 index 0000000..936b054 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.cpp @@ -0,0 +1,132 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceCopyMemoryWithLoadStore:: + TransformationReplaceCopyMemoryWithLoadStore( + const spvtools::fuzz::protobufs:: + TransformationReplaceCopyMemoryWithLoadStore& message) + : message_(message) {} + +TransformationReplaceCopyMemoryWithLoadStore:: + TransformationReplaceCopyMemoryWithLoadStore( + uint32_t fresh_id, const protobufs::InstructionDescriptor& + copy_memory_instruction_descriptor) { + message_.set_fresh_id(fresh_id); + *message_.mutable_copy_memory_instruction_descriptor() = + copy_memory_instruction_descriptor; +} + +bool TransformationReplaceCopyMemoryWithLoadStore::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.fresh_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The instruction to be replaced must be defined and have opcode + // OpCopyMemory. + auto copy_memory_instruction = FindInstruction( + message_.copy_memory_instruction_descriptor(), ir_context); + if (!copy_memory_instruction || + copy_memory_instruction->opcode() != SpvOpCopyMemory) { + return false; + } + return true; +} + +void TransformationReplaceCopyMemoryWithLoadStore::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto copy_memory_instruction = FindInstruction( + message_.copy_memory_instruction_descriptor(), ir_context); + // |copy_memory_instruction| must be defined. + assert(copy_memory_instruction && + copy_memory_instruction->opcode() == SpvOpCopyMemory && + "The required OpCopyMemory instruction must be defined."); + + // Integrity check: Both operands must be pointers. + + // Get types of ids used as a source and target of |copy_memory_instruction|. + auto target = ir_context->get_def_use_mgr()->GetDef( + copy_memory_instruction->GetSingleWordInOperand(0)); + auto source = ir_context->get_def_use_mgr()->GetDef( + copy_memory_instruction->GetSingleWordInOperand(1)); + auto target_type_opcode = + ir_context->get_def_use_mgr()->GetDef(target->type_id())->opcode(); + auto source_type_opcode = + ir_context->get_def_use_mgr()->GetDef(source->type_id())->opcode(); + + // Keep release-mode compilers happy. (No unused variables.) + (void)target; + (void)source; + (void)target_type_opcode; + (void)source_type_opcode; + + assert(target_type_opcode == SpvOpTypePointer && + source_type_opcode == SpvOpTypePointer && + "Operands must be of type OpTypePointer"); + + // Integrity check: |source| and |target| must point to the same type. + uint32_t target_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, target->type_id()); + uint32_t source_pointee_type = fuzzerutil::GetPointeeTypeIdFromPointerType( + ir_context, source->type_id()); + + // Keep release-mode compilers happy. (No unused variables.) + (void)target_pointee_type; + (void)source_pointee_type; + + assert(target_pointee_type == source_pointee_type && + "Operands must have the same type to which they point to."); + + // First, insert the OpStore instruction before the OpCopyMemory instruction + // and then insert the OpLoad instruction before the OpStore instruction. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + FindInstruction(message_.copy_memory_instruction_descriptor(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {target->result_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.fresh_id()}}}))) + ->InsertBefore(MakeUnique( + ir_context, SpvOpLoad, target_pointee_type, message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {source->result_id()}}}))); + + // Remove the OpCopyMemory instruction. + ir_context->KillInst(copy_memory_instruction); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceCopyMemoryWithLoadStore::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_copy_memory_with_load_store() = message_; + return result; +} + +std::unordered_set +TransformationReplaceCopyMemoryWithLoadStore::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h b/third_party/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h new file mode 100644 index 0000000..67d349f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_copy_memory_with_load_store.h @@ -0,0 +1,59 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceCopyMemoryWithLoadStore : public Transformation { + public: + explicit TransformationReplaceCopyMemoryWithLoadStore( + const protobufs::TransformationReplaceCopyMemoryWithLoadStore& message); + + TransformationReplaceCopyMemoryWithLoadStore( + uint32_t fresh_id, const protobufs::InstructionDescriptor& + copy_memory_instruction_descriptor); + + // - |message_.fresh_id| must be fresh. + // - |message_.copy_memory_instruction_descriptor| must refer to an + // OpCopyMemory instruction. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces instruction OpCopyMemory with loading the source variable to an + // intermediate value and storing this value into the target variable of the + // original OpCopyMemory instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceCopyMemoryWithLoadStore message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_MEMORY_WITH_LOAD_STORE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp new file mode 100644 index 0000000..54c99d5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.cpp @@ -0,0 +1,156 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceCopyObjectWithStoreLoad:: + TransformationReplaceCopyObjectWithStoreLoad( + const spvtools::fuzz::protobufs:: + TransformationReplaceCopyObjectWithStoreLoad& message) + : message_(message) {} + +TransformationReplaceCopyObjectWithStoreLoad:: + TransformationReplaceCopyObjectWithStoreLoad( + uint32_t copy_object_result_id, uint32_t fresh_variable_id, + uint32_t variable_storage_class, uint32_t variable_initializer_id) { + message_.set_copy_object_result_id(copy_object_result_id); + message_.set_fresh_variable_id(fresh_variable_id); + message_.set_variable_storage_class(variable_storage_class); + message_.set_variable_initializer_id(variable_initializer_id); +} + +bool TransformationReplaceCopyObjectWithStoreLoad::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.fresh_variable_id| must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_variable_id())) { + return false; + } + auto copy_object_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id()); + + // This must be a defined OpCopyObject instruction. + if (!copy_object_instruction || + copy_object_instruction->opcode() != SpvOpCopyObject) { + return false; + } + + // The opcode of the type_id instruction cannot be a OpTypePointer, + // because we cannot define a pointer to pointer. + if (ir_context->get_def_use_mgr() + ->GetDef(copy_object_instruction->type_id()) + ->opcode() == SpvOpTypePointer) { + return false; + } + + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, copy_object_instruction->type_id(), + static_cast(message_.variable_storage_class())); + if (!pointer_type_id) { + return false; + } + + // Check that initializer is valid. + const auto* constant_inst = + ir_context->get_def_use_mgr()->GetDef(message_.variable_initializer_id()); + if (!constant_inst || !spvOpcodeIsConstant(constant_inst->opcode()) || + copy_object_instruction->type_id() != constant_inst->type_id()) { + return false; + } + // |message_.variable_storage_class| must be Private or Function. + return message_.variable_storage_class() == SpvStorageClassPrivate || + message_.variable_storage_class() == SpvStorageClassFunction; +} + +void TransformationReplaceCopyObjectWithStoreLoad::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto copy_object_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.copy_object_result_id()); + // |copy_object_instruction| must be defined. + assert(copy_object_instruction && + copy_object_instruction->opcode() == SpvOpCopyObject && + "The required OpCopyObject instruction must be defined."); + // Get id used as a source by the OpCopyObject instruction. + uint32_t src_operand = copy_object_instruction->GetSingleWordInOperand(0); + // A pointer type instruction pointing to the value type must be defined. + auto pointer_type_id = fuzzerutil::MaybeGetPointerType( + ir_context, copy_object_instruction->type_id(), + static_cast(message_.variable_storage_class())); + assert(pointer_type_id && "The required pointer type must be available."); + + // Adds a global or local variable (according to the storage class). + if (message_.variable_storage_class() == SpvStorageClassPrivate) { + fuzzerutil::AddGlobalVariable(ir_context, message_.fresh_variable_id(), + pointer_type_id, SpvStorageClassPrivate, + message_.variable_initializer_id()); + } else { + auto function_id = ir_context->get_instr_block(copy_object_instruction) + ->GetParent() + ->result_id(); + fuzzerutil::AddLocalVariable(ir_context, message_.fresh_variable_id(), + pointer_type_id, function_id, + message_.variable_initializer_id()); + } + + // First, insert the OpLoad instruction before the OpCopyObject instruction + // and then insert the OpStore instruction before the OpLoad instruction. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_variable_id()); + copy_object_instruction + ->InsertBefore(MakeUnique( + ir_context, SpvOpLoad, copy_object_instruction->type_id(), + message_.copy_object_result_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}}))) + ->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.fresh_variable_id()}}, + {SPV_OPERAND_TYPE_ID, {src_operand}}}))); + // Remove the CopyObject instruction. + ir_context->KillInst(copy_object_instruction); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + if (!transformation_context->GetFactManager()->IdIsIrrelevant( + message_.copy_object_result_id()) && + !transformation_context->GetFactManager()->IdIsIrrelevant(src_operand)) { + // Adds the fact that |message_.copy_object_result_id| + // and src_operand (id used by OpCopyObject) are synonymous. + transformation_context->GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(message_.copy_object_result_id(), {}), + MakeDataDescriptor(src_operand, {})); + } +} + +protobufs::Transformation +TransformationReplaceCopyObjectWithStoreLoad::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_copy_object_with_store_load() = message_; + return result; +} + +std::unordered_set +TransformationReplaceCopyObjectWithStoreLoad::GetFreshIds() const { + return {message_.fresh_variable_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h b/third_party/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h new file mode 100644 index 0000000..a90905c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_copy_object_with_store_load.h @@ -0,0 +1,65 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceCopyObjectWithStoreLoad : public Transformation { + public: + explicit TransformationReplaceCopyObjectWithStoreLoad( + const protobufs::TransformationReplaceCopyObjectWithStoreLoad& message); + + TransformationReplaceCopyObjectWithStoreLoad( + uint32_t copy_object_result_id, uint32_t fresh_variable_id, + uint32_t variable_storage_class, uint32_t variable_initializer_id); + + // - |message_.copy_object_result_id| must be a result id of an OpCopyObject + // instruction. + // - |message_.fresh_variable_id| must be a fresh id given to variable used by + // OpStore. + // - |message_.variable_storage_class| must be either StorageClassPrivate or + // StorageClassFunction. + // - |message_.initializer_id| must be a result id of some constant in the + // module. Its type must be equal to the pointee type of the variable that + // will be created. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces instruction OpCopyObject with storing into a new variable and + // immediately loading from this variable to |result_id| of the original + // OpCopyObject instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceCopyObjectWithStoreLoad message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_COPY_OBJECT_WITH_STORE_LOAD_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp new file mode 100644 index 0000000..24e079f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +#include + +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym( + const spvtools::fuzz::protobufs::TransformationReplaceIdWithSynonym& + message) + : message_(message) {} + +TransformationReplaceIdWithSynonym::TransformationReplaceIdWithSynonym( + protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id) { + *message_.mutable_id_use_descriptor() = std::move(id_use_descriptor); + message_.set_synonymous_id(synonymous_id); +} + +bool TransformationReplaceIdWithSynonym::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto id_of_interest = message_.id_use_descriptor().id_of_interest(); + + // Does the fact manager know about the synonym? + auto data_descriptor_for_synonymous_id = + MakeDataDescriptor(message_.synonymous_id(), {}); + if (!transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(id_of_interest, {}), + data_descriptor_for_synonymous_id)) { + return false; + } + + // Does the id use descriptor in the transformation identify an instruction? + auto use_instruction = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (!use_instruction) { + return false; + } + + uint32_t type_id_of_interest = + ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id(); + uint32_t type_id_synonym = ir_context->get_def_use_mgr() + ->GetDef(message_.synonymous_id()) + ->type_id(); + + // If the id of interest and the synonym are scalar or vector integer + // constants with different signedness, their use can only be swapped if the + // instruction is agnostic to the signedness of the operand. + if (!TypesAreCompatible(ir_context, use_instruction->opcode(), + message_.id_use_descriptor().in_operand_index(), + type_id_of_interest, type_id_synonym)) { + return false; + } + + // Is the use suitable for being replaced in principle? + if (!fuzzerutil::IdUseCanBeReplaced( + ir_context, transformation_context, use_instruction, + message_.id_use_descriptor().in_operand_index())) { + return false; + } + + // The transformation is applicable if the synonymous id is available at the + // use point. + return fuzzerutil::IdIsAvailableAtUse( + ir_context, use_instruction, + message_.id_use_descriptor().in_operand_index(), + message_.synonymous_id()); +} + +void TransformationReplaceIdWithSynonym::Apply( + spvtools::opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + auto instruction_to_change = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + instruction_to_change->SetInOperand( + message_.id_use_descriptor().in_operand_index(), + {message_.synonymous_id()}); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationReplaceIdWithSynonym::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_id_with_synonym() = message_; + return result; +} + +// TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3582): Add all +// opcodes that are agnostic to signedness of operands to function. +// This is not exhaustive yet. +bool TransformationReplaceIdWithSynonym::IsAgnosticToSignednessOfOperand( + SpvOp opcode, uint32_t use_in_operand_index) { + switch (opcode) { + case SpvOpSNegate: + case SpvOpNot: + case SpvOpIAdd: + case SpvOpISub: + case SpvOpIMul: + case SpvOpSDiv: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + return true; + case SpvOpAccessChain: + // The signedness of indices does not matter. + return use_in_operand_index > 0; + default: + // Conservatively assume that the id cannot be swapped in other + // instructions. + return false; + } +} + +bool TransformationReplaceIdWithSynonym::TypesAreCompatible( + opt::IRContext* ir_context, SpvOp opcode, uint32_t use_in_operand_index, + uint32_t type_id_1, uint32_t type_id_2) { + assert(ir_context->get_type_mgr()->GetType(type_id_1) && + ir_context->get_type_mgr()->GetType(type_id_2) && + "Type ids are invalid"); + + return type_id_1 == type_id_2 || + (IsAgnosticToSignednessOfOperand(opcode, use_in_operand_index) && + fuzzerutil::TypesAreEqualUpToSign(ir_context, type_id_1, type_id_2)); +} + +std::unordered_set TransformationReplaceIdWithSynonym::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h b/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h new file mode 100644 index 0000000..3101710 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_id_with_synonym.h @@ -0,0 +1,77 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceIdWithSynonym : public Transformation { + public: + explicit TransformationReplaceIdWithSynonym( + const protobufs::TransformationReplaceIdWithSynonym& message); + + TransformationReplaceIdWithSynonym( + protobufs::IdUseDescriptor id_use_descriptor, uint32_t synonymous_id); + + // - The fact manager must know that the id identified by + // |message_.id_use_descriptor| is synonomous with |message_.synonymous_id|. + // - Replacing the id in |message_.id_use_descriptor| by + // |message_.synonymous_id| must respect SPIR-V's rules about uses being + // dominated by their definitions. + // - The id use must be replaceable in principle. See + // fuzzerutil::IdUseCanBeReplaced for details. + // - |fresh_id_for_temporary| must be 0. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the use identified by |message_.id_use_descriptor| with the + // synonymous id identified by |message_.synonymous_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if |type_id_1| and |type_id_2| represent compatible types + // given the context of the instruction with |opcode| (i.e. we can replace + // an operand of |opcode| of the first type with an id of the second type + // and vice-versa). + static bool TypesAreCompatible(opt::IRContext* ir_context, SpvOp opcode, + uint32_t use_in_operand_index, + uint32_t type_id_1, uint32_t type_id_2); + + private: + // Returns true if the instruction with opcode |opcode| does not change its + // behaviour depending on the signedness of the operand at + // |use_in_operand_index|. + // Assumes that the operand must be the id of an integer scalar or vector. + static bool IsAgnosticToSignednessOfOperand(SpvOp opcode, + uint32_t use_in_operand_index); + + protobufs::TransformationReplaceIdWithSynonym message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_ID_WITH_SYNONYM_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.cpp new file mode 100644 index 0000000..27f56eb --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_irrelevant_id.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId( + const protobufs::TransformationReplaceIrrelevantId& message) + : message_(message) {} + +TransformationReplaceIrrelevantId::TransformationReplaceIrrelevantId( + const protobufs::IdUseDescriptor& id_use_descriptor, + uint32_t replacement_id) { + *message_.mutable_id_use_descriptor() = id_use_descriptor; + message_.set_replacement_id(replacement_id); +} + +bool TransformationReplaceIrrelevantId::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + auto id_of_interest = message_.id_use_descriptor().id_of_interest(); + + // The id must be irrelevant. + if (!transformation_context.GetFactManager()->IdIsIrrelevant( + id_of_interest)) { + return false; + } + + // Find the instruction containing the id use, which must exist. + auto use_instruction = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + if (!use_instruction) { + return false; + } + + // Check that the replacement id exists and retrieve its definition. + auto replacement_id_def = + ir_context->get_def_use_mgr()->GetDef(message_.replacement_id()); + if (!replacement_id_def) { + return false; + } + + // The type of the id of interest and of the replacement id must be the same. + uint32_t type_id_of_interest = + ir_context->get_def_use_mgr()->GetDef(id_of_interest)->type_id(); + uint32_t type_replacement_id = replacement_id_def->type_id(); + if (type_id_of_interest != type_replacement_id) { + return false; + } + + // The replacement id must not be the result of an OpFunction instruction. + if (replacement_id_def->opcode() == SpvOpFunction) { + return false; + } + + // Consistency check: an irrelevant id cannot be a pointer. + assert( + !ir_context->get_type_mgr()->GetType(type_id_of_interest)->AsPointer() && + "An irrelevant id cannot be a pointer"); + + uint32_t use_in_operand_index = + message_.id_use_descriptor().in_operand_index(); + + // The id use must be replaceable with any other id of the same type. + if (!fuzzerutil::IdUseCanBeReplaced(ir_context, transformation_context, + use_instruction, use_in_operand_index)) { + return false; + } + + if (AttemptsToReplaceVariableInitializerWithNonConstant( + *use_instruction, *replacement_id_def)) { + return false; + } + + // The id must be available to use at the use point. + return fuzzerutil::IdIsAvailableAtUse( + ir_context, use_instruction, + message_.id_use_descriptor().in_operand_index(), + message_.replacement_id()); +} + +void TransformationReplaceIrrelevantId::Apply( + opt::IRContext* ir_context, + TransformationContext* /* transformation_context */) const { + // Find the instruction. + auto instruction_to_change = + FindInstructionContainingUse(message_.id_use_descriptor(), ir_context); + + // Replace the instruction. + instruction_to_change->SetInOperand( + message_.id_use_descriptor().in_operand_index(), + {message_.replacement_id()}); + + // Invalidate the analyses, since the usage of ids has been changed. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationReplaceIrrelevantId::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_irrelevant_id() = message_; + return result; +} + +std::unordered_set TransformationReplaceIrrelevantId::GetFreshIds() + const { + return std::unordered_set(); +} + +bool TransformationReplaceIrrelevantId:: + AttemptsToReplaceVariableInitializerWithNonConstant( + const opt::Instruction& use_instruction, + const opt::Instruction& replacement_for_use) { + return use_instruction.opcode() == SpvOpVariable && + !spvOpcodeIsConstant(replacement_for_use.opcode()); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.h b/third_party/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.h new file mode 100644 index 0000000..35b1987 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_irrelevant_id.h @@ -0,0 +1,68 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_IRRELEVANT_ID_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_IRRELEVANT_ID_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceIrrelevantId : public Transformation { + public: + explicit TransformationReplaceIrrelevantId( + const protobufs::TransformationReplaceIrrelevantId& message); + + TransformationReplaceIrrelevantId( + const protobufs::IdUseDescriptor& id_use_descriptor, + uint32_t replacement_id); + + // - The id of interest in |message_.id_use_descriptor| is irrelevant + // according to the fact manager. + // - The types of the original id and of the replacement ids are the same. + // - The replacement must not be the result id of an OpFunction instruction. + // - |message_.replacement_id| is available to use at the enclosing + // instruction of |message_.id_use_descriptor|. + // - The original id is in principle replaceable with any other id of the same + // type. See fuzzerutil::IdUseCanBeReplaced for details. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the use of an irrelevant id identified by + // |message_.id_use_descriptor| with the id |message_.replacement_id|, which + // has the same type as the id of interest. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if and only if |use_instruction| is OpVariable and + // |replacement_for_use| is not a constant instruction - i.e., if it would be + // illegal to replace the variable's initializer with the given instruction. + static bool AttemptsToReplaceVariableInitializerWithNonConstant( + const opt::Instruction& use_instruction, + const opt::Instruction& replacement_for_use); + + private: + protobufs::TransformationReplaceIrrelevantId message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_IRRELEVANT_ID_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp new file mode 100644 index 0000000..fc73a26 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.cpp @@ -0,0 +1,1041 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_linear_algebra_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceLinearAlgebraInstruction:: + TransformationReplaceLinearAlgebraInstruction( + const spvtools::fuzz::protobufs:: + TransformationReplaceLinearAlgebraInstruction& message) + : message_(message) {} + +TransformationReplaceLinearAlgebraInstruction:: + TransformationReplaceLinearAlgebraInstruction( + const std::vector& fresh_ids, + const protobufs::InstructionDescriptor& instruction_descriptor) { + for (auto fresh_id : fresh_ids) { + message_.add_fresh_ids(fresh_id); + } + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationReplaceLinearAlgebraInstruction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + + // It must be a linear algebra instruction. + if (!spvOpcodeIsLinearAlgebra(instruction->opcode())) { + return false; + } + + // |message_.fresh_ids.size| must be the exact number of fresh ids needed to + // apply the transformation. + if (static_cast(message_.fresh_ids().size()) != + GetRequiredFreshIdCount(ir_context, instruction)) { + return false; + } + + // All ids in |message_.fresh_ids| must be fresh. + for (uint32_t fresh_id : message_.fresh_ids()) { + if (!fuzzerutil::IsFreshId(ir_context, fresh_id)) { + return false; + } + } + + return true; +} + +void TransformationReplaceLinearAlgebraInstruction::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto linear_algebra_instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + + switch (linear_algebra_instruction->opcode()) { + case SpvOpTranspose: + ReplaceOpTranspose(ir_context, linear_algebra_instruction); + break; + case SpvOpVectorTimesScalar: + ReplaceOpVectorTimesScalar(ir_context, linear_algebra_instruction); + break; + case SpvOpMatrixTimesScalar: + ReplaceOpMatrixTimesScalar(ir_context, linear_algebra_instruction); + break; + case SpvOpVectorTimesMatrix: + ReplaceOpVectorTimesMatrix(ir_context, linear_algebra_instruction); + break; + case SpvOpMatrixTimesVector: + ReplaceOpMatrixTimesVector(ir_context, linear_algebra_instruction); + break; + case SpvOpMatrixTimesMatrix: + ReplaceOpMatrixTimesMatrix(ir_context, linear_algebra_instruction); + break; + case SpvOpOuterProduct: + ReplaceOpOuterProduct(ir_context, linear_algebra_instruction); + break; + case SpvOpDot: + ReplaceOpDot(ir_context, linear_algebra_instruction); + break; + default: + assert(false && "Should be unreachable."); + break; + } + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceLinearAlgebraInstruction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_linear_algebra_instruction() = message_; + return result; +} + +uint32_t TransformationReplaceLinearAlgebraInstruction::GetRequiredFreshIdCount( + opt::IRContext* ir_context, opt::Instruction* instruction) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3354): + // Right now we only support certain operations. + switch (instruction->opcode()) { + case SpvOpTranspose: { + // For each matrix row, |2 * matrix_column_count| OpCompositeExtract and 1 + // OpCompositeConstruct will be inserted. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + uint32_t matrix_row_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type() + ->AsVector() + ->element_count(); + return matrix_row_count * (2 * matrix_column_count + 1); + } + case SpvOpVectorTimesScalar: + // For each vector component, 1 OpCompositeExtract and 1 OpFMul will be + // inserted. + return 2 * + ir_context->get_type_mgr() + ->GetType(ir_context->get_def_use_mgr() + ->GetDef(instruction->GetSingleWordInOperand(0)) + ->type_id()) + ->AsVector() + ->element_count(); + case SpvOpMatrixTimesScalar: { + // For each matrix column, |1 + column.size| OpCompositeExtract, + // |column.size| OpFMul and 1 OpCompositeConstruct instructions will be + // inserted. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + auto matrix_type = + ir_context->get_type_mgr()->GetType(matrix_instruction->type_id()); + return 2 * matrix_type->AsMatrix()->element_count() * + (1 + matrix_type->AsMatrix() + ->element_type() + ->AsVector() + ->element_count()); + } + case SpvOpVectorTimesMatrix: { + // For each vector component, 1 OpCompositeExtract instruction will be + // inserted. For each matrix column, |1 + vector_component_count| + // OpCompositeExtract, |vector_component_count| OpFMul and + // |vector_component_count - 1| OpFAdd instructions will be inserted. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(1)); + uint32_t vector_component_count = + ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_count(); + uint32_t matrix_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + return vector_component_count * (3 * matrix_column_count + 1); + } + case SpvOpMatrixTimesVector: { + // For each matrix column, |1 + matrix_row_count| OpCompositeExtract + // will be inserted. For each matrix row, |matrix_column_count| OpFMul and + // |matrix_column_count - 1| OpFAdd instructions will be inserted. For + // each vector component, 1 OpCompositeExtract instruction will be + // inserted. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + uint32_t matrix_row_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type() + ->AsVector() + ->element_count(); + return 3 * matrix_column_count * matrix_row_count + + 2 * matrix_column_count - matrix_row_count; + } + case SpvOpMatrixTimesMatrix: { + // For each matrix 2 column, 1 OpCompositeExtract, 1 OpCompositeConstruct, + // |3 * matrix_1_row_count * matrix_1_column_count| OpCompositeExtract, + // |matrix_1_row_count * matrix_1_column_count| OpFMul, + // |matrix_1_row_count * (matrix_1_column_count - 1)| OpFAdd instructions + // will be inserted. + auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + uint32_t matrix_1_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_count(); + uint32_t matrix_1_row_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_type() + ->AsVector() + ->element_count(); + + auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(1)); + uint32_t matrix_2_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_count(); + return matrix_2_column_count * + (2 + matrix_1_row_count * (5 * matrix_1_column_count - 1)); + } + case SpvOpOuterProduct: { + // For each |vector_2| component, |vector_1_component_count + 1| + // OpCompositeExtract, |vector_1_component_count| OpFMul and 1 + // OpCompositeConstruct instructions will be inserted. + auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(1)); + uint32_t vector_1_component_count = + ir_context->get_type_mgr() + ->GetType(vector_1_instruction->type_id()) + ->AsVector() + ->element_count(); + uint32_t vector_2_component_count = + ir_context->get_type_mgr() + ->GetType(vector_2_instruction->type_id()) + ->AsVector() + ->element_count(); + return 2 * vector_2_component_count * (vector_1_component_count + 1); + } + case SpvOpDot: + // For each pair of vector components, 2 OpCompositeExtract and 1 OpFMul + // will be inserted. The first two OpFMul instructions will result the + // first OpFAdd instruction to be inserted. For each remaining OpFMul, 1 + // OpFAdd will be inserted. The last OpFAdd instruction is got by changing + // the OpDot instruction. + return 4 * ir_context->get_type_mgr() + ->GetType( + ir_context->get_def_use_mgr() + ->GetDef(instruction->GetSingleWordInOperand(0)) + ->type_id()) + ->AsVector() + ->element_count() - + 2; + default: + assert(false && "Unsupported linear algebra instruction."); + return 0; + } +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpTranspose( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets OpTranspose instruction information. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + auto matrix_column_component_type = + matrix_column_type->AsVector()->element_type(); + uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count(); + auto resulting_matrix_column_type = + ir_context->get_type_mgr() + ->GetType(linear_algebra_instruction->type_id()) + ->AsMatrix() + ->element_type(); + + uint32_t fresh_id_index = 0; + std::vector result_column_ids(matrix_row_count); + for (uint32_t i = 0; i < matrix_row_count; i++) { + std::vector column_component_ids(matrix_column_count); + for (uint32_t j = 0; j < matrix_column_count; j++) { + // Extracts the matrix column. + uint32_t matrix_column_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_column_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Extracts the matrix column component. + column_component_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_component_type), + column_component_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_column_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + // Inserts the resulting matrix column. + opt::Instruction::OperandList in_operands; + for (auto& column_component_id : column_component_ids) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}}); + } + result_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, + ir_context->get_type_mgr()->GetId(resulting_matrix_column_type), + result_column_ids[i], opt::Instruction::OperandList(in_operands))); + } + + // The OpTranspose instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]}); + for (uint32_t i = 1; i < result_column_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesScalar( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets OpVectorTimesScalar in operands. + auto vector = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + auto scalar = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + + uint32_t vector_component_count = ir_context->get_type_mgr() + ->GetType(vector->type_id()) + ->AsVector() + ->element_count(); + std::vector float_multiplication_ids(vector_component_count); + uint32_t fresh_id_index = 0; + + for (uint32_t i = 0; i < vector_component_count; i++) { + // Extracts |vector| component. + uint32_t vector_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, vector_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, scalar->type_id(), vector_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Multiplies the |vector| component with the |scalar|. + uint32_t float_multiplication_id = message_.fresh_ids(fresh_id_index++); + float_multiplication_ids[i] = float_multiplication_id; + fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, scalar->type_id(), float_multiplication_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_extract_id}}, + {SPV_OPERAND_TYPE_ID, {scalar->result_id()}}}))); + } + + // The OpVectorTimesScalar instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]}); + for (uint32_t i = 2; i < float_multiplication_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}}); + } +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesScalar( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets OpMatrixTimesScalar in operands. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + auto scalar_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + + // Gets matrix information. + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + uint32_t matrix_column_size = matrix_column_type->AsVector()->element_count(); + + std::vector composite_construct_ids(matrix_column_count); + uint32_t fresh_id_index = 0; + + for (uint32_t i = 0; i < matrix_column_count; i++) { + // Extracts |matrix| column. + uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, matrix_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector float_multiplication_ids(matrix_column_size); + + for (uint32_t j = 0; j < matrix_column_size; j++) { + // Extracts |column| component. + uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, column_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, scalar_instruction->type_id(), + column_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Multiplies the |column| component with the |scalar|. + float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[j]); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, scalar_instruction->type_id(), + float_multiplication_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {column_extract_id}}, + {SPV_OPERAND_TYPE_ID, {scalar_instruction->result_id()}}}))); + } + + // Constructs a new column multiplied by |scalar|. + opt::Instruction::OperandList composite_construct_in_operands; + for (uint32_t& float_multiplication_id : float_multiplication_ids) { + composite_construct_in_operands.push_back( + {SPV_OPERAND_TYPE_ID, {float_multiplication_id}}); + } + composite_construct_ids[i] = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, composite_construct_ids[i]); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, + ir_context->get_type_mgr()->GetId(matrix_column_type), + composite_construct_ids[i], composite_construct_in_operands)); + } + + // The OpMatrixTimesScalar instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {composite_construct_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {composite_construct_ids[1]}); + for (uint32_t i = 2; i < composite_construct_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {composite_construct_ids[i]}}); + } +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpVectorTimesMatrix( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets vector information. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t vector_component_count = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_count(); + auto vector_component_type = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_type(); + + // Extracts vector components. + uint32_t fresh_id_index = 0; + std::vector vector_component_ids(vector_component_count); + for (uint32_t i = 0; i < vector_component_count; i++) { + vector_component_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + vector_component_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + // Gets matrix information. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + + std::vector result_component_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + // Extracts matrix column. + uint32_t matrix_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector float_multiplication_ids(vector_component_count); + for (uint32_t j = 0; j < vector_component_count; j++) { + // Extracts column component. + uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + column_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_extract_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Multiplies corresponding vector and column components. + float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_multiplication_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {column_extract_id}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}}))); + } + + result_component_ids[i] = float_add_ids.back(); + } + + // The OpVectorTimesMatrix instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]}); + for (uint32_t i = 2; i < result_component_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesVector( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets matrix information. + auto matrix_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t matrix_column_count = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_column_type = ir_context->get_type_mgr() + ->GetType(matrix_instruction->type_id()) + ->AsMatrix() + ->element_type(); + uint32_t matrix_row_count = matrix_column_type->AsVector()->element_count(); + + // Extracts matrix columns. + uint32_t fresh_id_index = 0; + std::vector matrix_column_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + matrix_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_column_type), + matrix_column_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + // Gets vector information. + auto vector_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + auto vector_component_type = ir_context->get_type_mgr() + ->GetType(vector_instruction->type_id()) + ->AsVector() + ->element_type(); + + // Extracts vector components. + std::vector vector_component_ids(matrix_column_count); + for (uint32_t i = 0; i < matrix_column_count; i++) { + vector_component_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + vector_component_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + } + + std::vector result_component_ids(matrix_row_count); + for (uint32_t i = 0; i < matrix_row_count; i++) { + std::vector float_multiplication_ids(matrix_column_count); + for (uint32_t j = 0; j < matrix_column_count; j++) { + // Extracts column component. + uint32_t column_extract_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_component_type), + column_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_column_ids[j]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Multiplies corresponding vector and column components. + float_multiplication_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_multiplication_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {column_extract_id}}, + {SPV_OPERAND_TYPE_ID, {vector_component_ids[j]}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t j = 2; j < float_multiplication_ids.size(); j++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(vector_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[j]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[j - 2]}}}))); + } + + result_component_ids[i] = float_add_ids.back(); + } + + // The OpMatrixTimesVector instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_component_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_component_ids[1]}); + for (uint32_t i = 2; i < result_component_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_component_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpMatrixTimesMatrix( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets matrix 1 information. + auto matrix_1_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t matrix_1_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_1_column_type = ir_context->get_type_mgr() + ->GetType(matrix_1_instruction->type_id()) + ->AsMatrix() + ->element_type(); + auto matrix_1_column_component_type = + matrix_1_column_type->AsVector()->element_type(); + uint32_t matrix_1_row_count = + matrix_1_column_type->AsVector()->element_count(); + + // Gets matrix 2 information. + auto matrix_2_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + uint32_t matrix_2_column_count = + ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_count(); + auto matrix_2_column_type = ir_context->get_type_mgr() + ->GetType(matrix_2_instruction->type_id()) + ->AsMatrix() + ->element_type(); + + uint32_t fresh_id_index = 0; + std::vector result_column_ids(matrix_2_column_count); + for (uint32_t i = 0; i < matrix_2_column_count; i++) { + // Extracts matrix 2 column. + uint32_t matrix_2_column_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_2_column_type), + matrix_2_column_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_2_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector column_component_ids(matrix_1_row_count); + for (uint32_t j = 0; j < matrix_1_row_count; j++) { + std::vector float_multiplication_ids(matrix_1_column_count); + for (uint32_t k = 0; k < matrix_1_column_count; k++) { + // Extracts matrix 1 column. + uint32_t matrix_1_column_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_type), + matrix_1_column_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}}))); + + // Extracts matrix 1 column component. + uint32_t matrix_1_column_component_id = + message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + matrix_1_column_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_column_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Extracts matrix 2 column component. + uint32_t matrix_2_column_component_id = + message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + matrix_2_column_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_2_column_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {k}}}))); + + // Multiplies corresponding matrix 1 and matrix 2 column components. + float_multiplication_ids[k] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_multiplication_ids[k], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {matrix_1_column_component_id}}, + {SPV_OPERAND_TYPE_ID, {matrix_2_column_component_id}}}))); + } + + // Adds the multiplication results. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + for (uint32_t k = 2; k < float_multiplication_ids.size(); k++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, + ir_context->get_type_mgr()->GetId(matrix_1_column_component_type), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[k]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[k - 2]}}}))); + } + + column_component_ids[j] = float_add_ids.back(); + } + + // Inserts the resulting matrix column. + opt::Instruction::OperandList in_operands; + for (auto& column_component_id : column_component_ids) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}}); + } + result_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, + ir_context->get_type_mgr()->GetId(matrix_1_column_type), + result_column_ids[i], opt::Instruction::OperandList(in_operands))); + } + + // The OpMatrixTimesMatrix instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]}); + for (uint32_t i = 2; i < result_column_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpOuterProduct( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets vector 1 information. + auto vector_1_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + uint32_t vector_1_component_count = + ir_context->get_type_mgr() + ->GetType(vector_1_instruction->type_id()) + ->AsVector() + ->element_count(); + auto vector_1_component_type = ir_context->get_type_mgr() + ->GetType(vector_1_instruction->type_id()) + ->AsVector() + ->element_type(); + + // Gets vector 2 information. + auto vector_2_instruction = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + uint32_t vector_2_component_count = + ir_context->get_type_mgr() + ->GetType(vector_2_instruction->type_id()) + ->AsVector() + ->element_count(); + + uint32_t fresh_id_index = 0; + std::vector result_column_ids(vector_2_component_count); + for (uint32_t i = 0; i < vector_2_component_count; i++) { + // Extracts |vector_2| component. + uint32_t vector_2_component_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_1_component_type), + vector_2_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_2_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + std::vector column_component_ids(vector_1_component_count); + for (uint32_t j = 0; j < vector_1_component_count; j++) { + // Extracts |vector_1| component. + uint32_t vector_1_component_id = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + ir_context->get_type_mgr()->GetId(vector_1_component_type), + vector_1_component_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_1_instruction->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {j}}}))); + + // Multiplies |vector_1| and |vector_2| components. + column_component_ids[j] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, + ir_context->get_type_mgr()->GetId(vector_1_component_type), + column_component_ids[j], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_2_component_id}}, + {SPV_OPERAND_TYPE_ID, {vector_1_component_id}}}))); + } + + // Inserts the resulting matrix column. + opt::Instruction::OperandList in_operands; + for (auto& column_component_id : column_component_ids) { + in_operands.push_back({SPV_OPERAND_TYPE_ID, {column_component_id}}); + } + result_column_ids[i] = message_.fresh_ids(fresh_id_index++); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, vector_1_instruction->type_id(), + result_column_ids[i], in_operands)); + } + + // The OpOuterProduct instruction is changed to an OpCompositeConstruct + // instruction. + linear_algebra_instruction->SetOpcode(SpvOpCompositeConstruct); + linear_algebra_instruction->SetInOperand(0, {result_column_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {result_column_ids[1]}); + for (uint32_t i = 2; i < result_column_ids.size(); i++) { + linear_algebra_instruction->AddOperand( + {SPV_OPERAND_TYPE_ID, {result_column_ids[i]}}); + } + + fuzzerutil::UpdateModuleIdBound( + ir_context, message_.fresh_ids(message_.fresh_ids().size() - 1)); +} + +void TransformationReplaceLinearAlgebraInstruction::ReplaceOpDot( + opt::IRContext* ir_context, + opt::Instruction* linear_algebra_instruction) const { + // Gets OpDot in operands. + auto vector_1 = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(0)); + auto vector_2 = ir_context->get_def_use_mgr()->GetDef( + linear_algebra_instruction->GetSingleWordInOperand(1)); + + uint32_t vectors_component_count = ir_context->get_type_mgr() + ->GetType(vector_1->type_id()) + ->AsVector() + ->element_count(); + std::vector float_multiplication_ids(vectors_component_count); + uint32_t fresh_id_index = 0; + + for (uint32_t i = 0; i < vectors_component_count; i++) { + // Extracts |vector_1| component. + uint32_t vector_1_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, vector_1_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + linear_algebra_instruction->type_id(), vector_1_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_1->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Extracts |vector_2| component. + uint32_t vector_2_extract_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, vector_2_extract_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, + linear_algebra_instruction->type_id(), vector_2_extract_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_2->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}}))); + + // Multiplies the pair of components. + float_multiplication_ids[i] = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, float_multiplication_ids[i]); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFMul, linear_algebra_instruction->type_id(), + float_multiplication_ids[i], + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {vector_1_extract_id}}, + {SPV_OPERAND_TYPE_ID, {vector_2_extract_id}}}))); + } + + // If the vector has 2 components, then there will be 2 float multiplication + // instructions. + if (vectors_component_count == 2) { + linear_algebra_instruction->SetOpcode(SpvOpFAdd); + linear_algebra_instruction->SetInOperand(0, {float_multiplication_ids[0]}); + linear_algebra_instruction->SetInOperand(1, {float_multiplication_ids[1]}); + } else { + // The first OpFAdd instruction has as operands the first two OpFMul + // instructions. + std::vector float_add_ids; + uint32_t float_add_id = message_.fresh_ids(fresh_id_index++); + float_add_ids.push_back(float_add_id); + fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, linear_algebra_instruction->type_id(), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[0]}}, + {SPV_OPERAND_TYPE_ID, {float_multiplication_ids[1]}}}))); + + // The remaining OpFAdd instructions has as operands an OpFMul and an OpFAdd + // instruction. + for (uint32_t i = 2; i < float_multiplication_ids.size() - 1; i++) { + float_add_id = message_.fresh_ids(fresh_id_index++); + fuzzerutil::UpdateModuleIdBound(ir_context, float_add_id); + float_add_ids.push_back(float_add_id); + linear_algebra_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpFAdd, linear_algebra_instruction->type_id(), + float_add_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {float_multiplication_ids[i]}}, + {SPV_OPERAND_TYPE_ID, {float_add_ids[i - 2]}}}))); + } + + // The last OpFAdd instruction is got by changing some of the OpDot + // instruction attributes. + linear_algebra_instruction->SetOpcode(SpvOpFAdd); + linear_algebra_instruction->SetInOperand( + 0, {float_multiplication_ids[float_multiplication_ids.size() - 1]}); + linear_algebra_instruction->SetInOperand( + 1, {float_add_ids[float_add_ids.size() - 1]}); + } +} + +std::unordered_set +TransformationReplaceLinearAlgebraInstruction::GetFreshIds() const { + std::unordered_set result; + for (auto id : message_.fresh_ids()) { + result.insert(id); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h b/third_party/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h new file mode 100644 index 0000000..45f4aa6 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_linear_algebra_instruction.h @@ -0,0 +1,93 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceLinearAlgebraInstruction : public Transformation { + public: + explicit TransformationReplaceLinearAlgebraInstruction( + const protobufs::TransformationReplaceLinearAlgebraInstruction& message); + + TransformationReplaceLinearAlgebraInstruction( + const std::vector& fresh_ids, + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.fresh_ids| must be fresh ids needed to apply the + // transformation. + // - |message_.instruction_descriptor| must be a linear algebra instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces a linear algebra instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns the number of ids needed to apply the transformation. + static uint32_t GetRequiredFreshIdCount(opt::IRContext* ir_context, + opt::Instruction* instruction); + + private: + protobufs::TransformationReplaceLinearAlgebraInstruction message_; + + // Replaces an OpTranspose instruction. + void ReplaceOpTranspose(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpVectorTimesScalar instruction. + void ReplaceOpVectorTimesScalar(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpMatrixTimesScalar instruction. + void ReplaceOpMatrixTimesScalar(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpVectorTimesMatrix instruction. + void ReplaceOpVectorTimesMatrix(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpMatrixTimesVector instruction. + void ReplaceOpMatrixTimesVector(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpMatrixTimesMatrix instruction. + void ReplaceOpMatrixTimesMatrix(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpOuterProduct instruction. + void ReplaceOpOuterProduct(opt::IRContext* ir_context, + opt::Instruction* instruction) const; + + // Replaces an OpDot instruction. + void ReplaceOpDot(opt::IRContext* ir_context, + opt::Instruction* instruction) const; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_LINEAR_ALGEBRA_INSTRUCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp new file mode 100644 index 0000000..6067fca --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.cpp @@ -0,0 +1,189 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "transformation_replace_load_store_with_copy_memory.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/opcode.h" + +namespace spvtools { +namespace fuzz { + +namespace { +const uint32_t kOpStoreOperandIndexTargetVariable = 0; +const uint32_t kOpStoreOperandIndexIntermediateIdToWrite = 1; +const uint32_t kOpLoadOperandIndexSourceVariable = 2; +} // namespace + +TransformationReplaceLoadStoreWithCopyMemory:: + TransformationReplaceLoadStoreWithCopyMemory( + const spvtools::fuzz::protobufs:: + TransformationReplaceLoadStoreWithCopyMemory& message) + : message_(message) {} + +TransformationReplaceLoadStoreWithCopyMemory:: + TransformationReplaceLoadStoreWithCopyMemory( + const protobufs::InstructionDescriptor& load_instruction_descriptor, + const protobufs::InstructionDescriptor& store_instruction_descriptor) { + *message_.mutable_load_instruction_descriptor() = load_instruction_descriptor; + *message_.mutable_store_instruction_descriptor() = + store_instruction_descriptor; +} +bool TransformationReplaceLoadStoreWithCopyMemory::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // This transformation is only applicable to the pair of OpLoad and OpStore + // instructions. + + // The OpLoad instruction must be defined. + auto load_instruction = + FindInstruction(message_.load_instruction_descriptor(), ir_context); + if (!load_instruction || load_instruction->opcode() != SpvOpLoad) { + return false; + } + + // The OpStore instruction must be defined. + auto store_instruction = + FindInstruction(message_.store_instruction_descriptor(), ir_context); + if (!store_instruction || store_instruction->opcode() != SpvOpStore) { + return false; + } + + // Intermediate values of the OpLoad and the OpStore must match. + if (load_instruction->result_id() != + store_instruction->GetSingleWordOperand( + kOpStoreOperandIndexIntermediateIdToWrite)) { + return false; + } + + // Get storage class of the variable pointed by the source operand in OpLoad. + opt::Instruction* source_id = ir_context->get_def_use_mgr()->GetDef( + load_instruction->GetSingleWordOperand(2)); + SpvStorageClass storage_class = fuzzerutil::GetStorageClassFromPointerType( + ir_context, source_id->type_id()); + + // Iterate over all instructions between |load_instruction| and + // |store_instruction|. + for (auto it = load_instruction; it != store_instruction; + it = it->NextNode()) { + //|load_instruction| and |store_instruction| are not in the same block. + if (it == nullptr) { + return false; + } + + // We need to make sure that the value pointed to by the source of the + // OpLoad hasn't changed by the time we see the matching OpStore + // instruction. + if (IsMemoryWritingOpCode(it->opcode())) { + return false; + } else if (IsMemoryBarrierOpCode(it->opcode()) && + !IsStorageClassSafeAcrossMemoryBarriers(storage_class)) { + return false; + } + } + return true; +} + +void TransformationReplaceLoadStoreWithCopyMemory::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // OpLoad and OpStore instructions must be defined. + auto load_instruction = + FindInstruction(message_.load_instruction_descriptor(), ir_context); + assert(load_instruction && load_instruction->opcode() == SpvOpLoad && + "The required OpLoad instruction must be defined."); + auto store_instruction = + FindInstruction(message_.store_instruction_descriptor(), ir_context); + assert(store_instruction && store_instruction->opcode() == SpvOpStore && + "The required OpStore instruction must be defined."); + + // Intermediate values of the OpLoad and the OpStore must match. + assert(load_instruction->result_id() == + store_instruction->GetSingleWordOperand( + kOpStoreOperandIndexIntermediateIdToWrite) && + "OpLoad and OpStore must refer to the same value."); + + // Get the ids of the source operand of the OpLoad and the target operand of + // the OpStore. + uint32_t source_variable_id = + load_instruction->GetSingleWordOperand(kOpLoadOperandIndexSourceVariable); + uint32_t target_variable_id = store_instruction->GetSingleWordOperand( + kOpStoreOperandIndexTargetVariable); + + // Insert the OpCopyMemory instruction before the OpStore instruction. + store_instruction->InsertBefore(MakeUnique( + ir_context, SpvOpCopyMemory, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {target_variable_id}}, + {SPV_OPERAND_TYPE_ID, {source_variable_id}}}))); + + // Remove the OpStore instruction. + ir_context->KillInst(store_instruction); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryWritingOpCode( + SpvOp op_code) { + if (spvOpcodeIsAtomicOp(op_code)) { + return op_code != SpvOpAtomicLoad; + } + switch (op_code) { + case SpvOpStore: + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + return true; + default: + return false; + } +} + +bool TransformationReplaceLoadStoreWithCopyMemory::IsMemoryBarrierOpCode( + SpvOp op_code) { + switch (op_code) { + case SpvOpMemoryBarrier: + case SpvOpMemoryNamedBarrier: + return true; + default: + return false; + } +} + +bool TransformationReplaceLoadStoreWithCopyMemory:: + IsStorageClassSafeAcrossMemoryBarriers(SpvStorageClass storage_class) { + switch (storage_class) { + case SpvStorageClassUniformConstant: + case SpvStorageClassInput: + case SpvStorageClassUniform: + case SpvStorageClassPrivate: + case SpvStorageClassFunction: + return true; + default: + return false; + } +} + +protobufs::Transformation +TransformationReplaceLoadStoreWithCopyMemory::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_load_store_with_copy_memory() = message_; + return result; +} + +std::unordered_set +TransformationReplaceLoadStoreWithCopyMemory::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h b/third_party/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h new file mode 100644 index 0000000..4dd728e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_load_store_with_copy_memory.h @@ -0,0 +1,78 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceLoadStoreWithCopyMemory : public Transformation { + public: + explicit TransformationReplaceLoadStoreWithCopyMemory( + const protobufs::TransformationReplaceLoadStoreWithCopyMemory& message); + + TransformationReplaceLoadStoreWithCopyMemory( + const protobufs::InstructionDescriptor& load_instruction_descriptor, + const protobufs::InstructionDescriptor& store_instruction_descriptor); + + // - |message_.load_instruction_descriptor| must identify an OpLoad + // instruction. + // - |message_.store_instruction_descriptor| must identify an OpStore + // instruction. + // - The OpStore must write the intermediate value loaded by the OpLoad. + // - The OpLoad and the OpStore must not have certain instruction in between + // (checked by IsMemoryWritingOpCode(), IsMemoryBarrierOpCode(), + // IsStorageClassSafeAcrossMemoryBarriers()). + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Takes a pair of instruction descriptors to OpLoad and OpStore that have the + // same intermediate value and replaces the OpStore with an equivalent + // OpCopyMemory. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + // Checks if the instruction that has an |op_code| might write to + // the source operand of the OpLoad instruction. + static bool IsMemoryWritingOpCode(SpvOp op_code); + + // Checks if the instruction that has an |op_code| is a memory barrier that + // could interfere with the source operand of the OpLoad instruction + static bool IsMemoryBarrierOpCode(SpvOp op_code); + + // Checks if the |storage_class| of the source operand of the OpLoad + // instruction implies that this variable cannot change (due to other threads) + // across memory barriers. + static bool IsStorageClassSafeAcrossMemoryBarriers( + SpvStorageClass storage_class); + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceLoadStoreWithCopyMemory message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_LOAD_STORE_WITH_COPY_MEMORY_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp new file mode 100644 index 0000000..f13af7e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceOpPhiIdFromDeadPredecessor:: + TransformationReplaceOpPhiIdFromDeadPredecessor( + const protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor& + message) + : message_(message) {} + +TransformationReplaceOpPhiIdFromDeadPredecessor:: + TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id, + uint32_t pred_label_id, + uint32_t replacement_id) { + message_.set_opphi_id(opphi_id); + message_.set_pred_label_id(pred_label_id); + message_.set_replacement_id(replacement_id); +} + +bool TransformationReplaceOpPhiIdFromDeadPredecessor::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // |opphi_id| must be the id of an OpPhi instruction. + auto opphi_def = ir_context->get_def_use_mgr()->GetDef(message_.opphi_id()); + if (!opphi_def || opphi_def->opcode() != SpvOpPhi) { + return false; + } + + // |pred_label_id| must be the label id of a dead block. + auto pred_block = ir_context->get_instr_block(message_.pred_label_id()); + if (!pred_block || pred_block->id() != message_.pred_label_id() || + !transformation_context.GetFactManager()->BlockIsDead(pred_block->id())) { + return false; + } + + // |pred_label_id| must be one of the predecessors of the block containing the + // OpPhi instruction. + bool found = false; + for (auto pred : + ir_context->cfg()->preds(ir_context->get_instr_block(opphi_def)->id())) { + if (pred == message_.pred_label_id()) { + found = true; + break; + } + } + + if (!found) { + return false; + } + + // |replacement_id| must have the same type id as the OpPhi instruction. + auto replacement_def = + ir_context->get_def_use_mgr()->GetDef(message_.replacement_id()); + + if (!replacement_def || replacement_def->type_id() != opphi_def->type_id()) { + return false; + } + + // The replacement id must be available at the end of the predecessor. + return fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, pred_block->terminator(), replacement_def->result_id()); +} + +void TransformationReplaceOpPhiIdFromDeadPredecessor::Apply( + opt::IRContext* ir_context, + TransformationContext* /* transformation_context */) const { + // Get the OpPhi instruction. + auto opphi_def = ir_context->get_def_use_mgr()->GetDef(message_.opphi_id()); + + // Find the index corresponding to the operand being replaced and replace it, + // by looping through the odd-indexed input operands and finding + // |pred_label_id|. The index that we are interested in is the one before + // that. + for (uint32_t i = 1; i < opphi_def->NumInOperands(); i += 2) { + if (opphi_def->GetSingleWordInOperand(i) == message_.pred_label_id()) { + // The operand to be replaced is at index i-1. + opphi_def->SetInOperand(i - 1, {message_.replacement_id()}); + } + } + + // Invalidate the analyses because we have altered the usages of ids. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceOpPhiIdFromDeadPredecessor::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_opphi_id_from_dead_predecessor() = message_; + return result; +} + +std::unordered_set +TransformationReplaceOpPhiIdFromDeadPredecessor::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h b/third_party/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h new file mode 100644 index 0000000..d26b6b0 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h @@ -0,0 +1,58 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_ + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceOpPhiIdFromDeadPredecessor : public Transformation { + public: + explicit TransformationReplaceOpPhiIdFromDeadPredecessor( + const protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor& + message); + + TransformationReplaceOpPhiIdFromDeadPredecessor(uint32_t opphi_id, + uint32_t pred_label_id, + uint32_t replacement_id); + + // - |message_.opphi_id| is the id of an OpPhi instruction. + // - |message_.pred_label_id| is the label id of one of the predecessors of + // the block containing the OpPhi instruction. + // - The predecessor has been recorded as dead. + // - |message_.replacement_id| is the id of an instruction with the same type + // as the OpPhi instruction, available at the end of the predecessor. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the id corresponding to predecessor |message_.pred_label_id|, in + // the OpPhi instruction |message_.opphi_id|, with |message_.replacement_id|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceOpPhiIdFromDeadPredecessor message_; +}; +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPPHI_ID_FROM_DEAD_PREDECESSOR_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp new file mode 100644 index 0000000..7160d4d --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.cpp @@ -0,0 +1,209 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { +TransformationReplaceOpSelectWithConditionalBranch:: + TransformationReplaceOpSelectWithConditionalBranch( + const spvtools::fuzz::protobufs:: + TransformationReplaceOpSelectWithConditionalBranch& message) + : message_(message) {} + +TransformationReplaceOpSelectWithConditionalBranch:: + TransformationReplaceOpSelectWithConditionalBranch( + uint32_t select_id, uint32_t true_block_id, uint32_t false_block_id) { + message_.set_select_id(select_id); + message_.set_true_block_id(true_block_id); + message_.set_false_block_id(false_block_id); +} + +bool TransformationReplaceOpSelectWithConditionalBranch::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& /* unused */) const { + assert((message_.true_block_id() || message_.false_block_id()) && + "At least one of the ids must be non-zero."); + + // Check that the non-zero ids are fresh. + std::set used_ids; + for (uint32_t id : {message_.true_block_id(), message_.false_block_id()}) { + if (id && !CheckIdIsFreshAndNotUsedByThisTransformation(id, ir_context, + &used_ids)) { + return false; + } + } + + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.select_id()); + + // The instruction must exist and it must be an OpSelect instruction. + if (!instruction || instruction->opcode() != SpvOpSelect) { + return false; + } + + // Check that the condition is a scalar boolean. + auto condition = ir_context->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + assert(condition && "The condition should always exist in a valid module."); + + auto condition_type = + ir_context->get_type_mgr()->GetType(condition->type_id()); + if (!condition_type->AsBool()) { + return false; + } + + auto block = ir_context->get_instr_block(instruction); + assert(block && "The block containing the instruction must be found"); + + // The instruction must be the first in its block. + if (instruction->unique_id() != block->begin()->unique_id()) { + return false; + } + + // The block must not be a merge block. + if (ir_context->GetStructuredCFGAnalysis()->IsMergeBlock(block->id())) { + return false; + } + + // The block must have exactly one predecessor. + auto predecessors = ir_context->cfg()->preds(block->id()); + if (predecessors.size() != 1) { + return false; + } + + uint32_t pred_id = predecessors[0]; + auto predecessor = ir_context->get_instr_block(pred_id); + + // The predecessor must not be the header of a construct and it must end with + // OpBranch. + if (predecessor->GetMergeInst() != nullptr || + predecessor->terminator()->opcode() != SpvOpBranch) { + return false; + } + + return true; +} + +void TransformationReplaceOpSelectWithConditionalBranch::Apply( + opt::IRContext* ir_context, TransformationContext* /* unused */) const { + auto instruction = + ir_context->get_def_use_mgr()->GetDef(message_.select_id()); + + auto block = ir_context->get_instr_block(instruction); + + auto predecessor = + ir_context->get_instr_block(ir_context->cfg()->preds(block->id())[0]); + + // Create a new block for each non-zero id in {|message_.true_branch_id|, + // |message_.false_branch_id|}. Make each newly-created block branch + // unconditionally to the instruction block. + for (uint32_t id : {message_.true_block_id(), message_.false_block_id()}) { + if (id) { + fuzzerutil::UpdateModuleIdBound(ir_context, id); + + // Create the new block. + auto new_block = MakeUnique(MakeUnique( + ir_context, SpvOpLabel, 0, id, opt::Instruction::OperandList{})); + + // Add an unconditional branch from the new block to the instruction + // block. + new_block->AddInstruction(MakeUnique( + ir_context, SpvOpBranch, 0, 0, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}}})); + + // Insert the new block right after the predecessor of the instruction + // block. + block->GetParent()->InsertBasicBlockBefore(std::move(new_block), block); + } + } + + // Delete the OpBranch instruction from the predecessor. + ir_context->KillInst(predecessor->terminator()); + + // Add an OpSelectionMerge instruction to the predecessor block, where the + // merge block is the instruction block. + predecessor->AddInstruction(MakeUnique( + ir_context, SpvOpSelectionMerge, 0, 0, + opt::Instruction::OperandList{{SPV_OPERAND_TYPE_ID, {block->id()}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {SpvSelectionControlMaskNone}}})); + + // |if_block| will be the true block, if it has been created, the instruction + // block otherwise. + uint32_t if_block = + message_.true_block_id() ? message_.true_block_id() : block->id(); + + // |else_block| will be the false block, if it has been created, the + // instruction block otherwise. + uint32_t else_block = + message_.false_block_id() ? message_.false_block_id() : block->id(); + + assert(if_block != else_block && + "|if_block| and |else_block| should always be different, if the " + "transformation is applicable."); + + // Add a conditional branching instruction to the predecessor, branching to + // |if_block| if the condition is true and to |if_false| otherwise. + predecessor->AddInstruction(MakeUnique( + ir_context, SpvOpBranchConditional, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {instruction->GetSingleWordInOperand(0)}}, + {SPV_OPERAND_TYPE_ID, {if_block}}, + {SPV_OPERAND_TYPE_ID, {else_block}}})); + + // |if_pred| will be the true block, if it has been created, the existing + // predecessor otherwise. + uint32_t if_pred = + message_.true_block_id() ? message_.true_block_id() : predecessor->id(); + + // |else_pred| will be the false block, if it has been created, the existing + // predecessor otherwise. + uint32_t else_pred = + message_.false_block_id() ? message_.false_block_id() : predecessor->id(); + + // Replace the OpSelect instruction in the merge block with an OpPhi. + // This: OpSelect %type %cond %if %else + // will become: OpPhi %type %if %if_pred %else %else_pred + instruction->SetOpcode(SpvOpPhi); + std::vector operands; + + operands.emplace_back(instruction->GetInOperand(1)); + operands.emplace_back(opt::Operand{SPV_OPERAND_TYPE_ID, {if_pred}}); + + operands.emplace_back(instruction->GetInOperand(2)); + operands.emplace_back(opt::Operand{SPV_OPERAND_TYPE_ID, {else_pred}}); + + instruction->SetInOperands(std::move(operands)); + + // Invalidate all analyses, since the structure of the module was changed. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation +TransformationReplaceOpSelectWithConditionalBranch::ToMessage() const { + protobufs::Transformation result; + *result.mutable_replace_opselect_with_conditional_branch() = message_; + return result; +} + +std::unordered_set +TransformationReplaceOpSelectWithConditionalBranch::GetFreshIds() const { + return {message_.true_block_id(), message_.false_block_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.h b/third_party/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.h new file mode 100644 index 0000000..8ee5c7f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_opselect_with_conditional_branch.h @@ -0,0 +1,63 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPSELECT_WITH_CONDITIONAL_BRANCH_H +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPSELECT_WITH_CONDITIONAL_BRANCH_H + +#include "source/fuzz/transformation.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceOpSelectWithConditionalBranch + : public Transformation { + public: + explicit TransformationReplaceOpSelectWithConditionalBranch( + const protobufs::TransformationReplaceOpSelectWithConditionalBranch& + message); + + TransformationReplaceOpSelectWithConditionalBranch(uint32_t select_id, + uint32_t true_block_id, + uint32_t false_block_id); + + // - |message_.select_id| is the result id of an OpSelect instruction. + // - The condition of the OpSelect must be a scalar boolean. + // - The OpSelect instruction is the first instruction in its block. + // - The block containing the instruction is not a merge block, and it has a + // single predecessor, which is not a header and whose last instruction is + // OpBranch. + // - Each of |message_.true_block_id| and |message_.false_block_id| is either + // 0 or a valid fresh id, and at most one of them is 0. They must be + // distinct. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the OpSelect instruction with id |message_.select_id| with a + // conditional branch and an OpPhi instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationReplaceOpSelectWithConditionalBranch message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_OPSELECT_WITH_CONDITIONAL_BRANCH_H diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp new file mode 100644 index 0000000..cdf7645 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.cpp @@ -0,0 +1,211 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_parameter_with_global.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceParameterWithGlobal:: + TransformationReplaceParameterWithGlobal( + const protobufs::TransformationReplaceParameterWithGlobal& message) + : message_(message) {} + +TransformationReplaceParameterWithGlobal:: + TransformationReplaceParameterWithGlobal( + uint32_t function_type_fresh_id, uint32_t parameter_id, + uint32_t global_variable_fresh_id) { + message_.set_function_type_fresh_id(function_type_fresh_id); + message_.set_parameter_id(parameter_id); + message_.set_global_variable_fresh_id(global_variable_fresh_id); +} + +bool TransformationReplaceParameterWithGlobal::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that |parameter_id| is valid. + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id()); + if (!param_inst || param_inst->opcode() != SpvOpFunctionParameter) { + return false; + } + + // Check that function exists and is not an entry point. + const auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id()); + if (!function || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // We already know that the function has at least one parameter - + // |parameter_id|. + + // Check that replaced parameter has valid type. + if (!IsParameterTypeSupported(ir_context, param_inst->type_id())) { + return false; + } + + // Check that initializer for the global variable exists in the module. + if (fuzzerutil::MaybeGetZeroConstant(ir_context, transformation_context, + param_inst->type_id(), false) == 0) { + return false; + } + + // Check that pointer type for the global variable exists in the module. + if (!fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(), + SpvStorageClassPrivate)) { + return false; + } + + return fuzzerutil::IsFreshId(ir_context, message_.function_type_fresh_id()) && + fuzzerutil::IsFreshId(ir_context, + message_.global_variable_fresh_id()) && + message_.function_type_fresh_id() != + message_.global_variable_fresh_id(); +} + +void TransformationReplaceParameterWithGlobal::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id()); + assert(param_inst && "Parameter must exist"); + + // Create global variable to store parameter's value. + fuzzerutil::AddGlobalVariable( + ir_context, message_.global_variable_fresh_id(), + fuzzerutil::MaybeGetPointerType(ir_context, param_inst->type_id(), + SpvStorageClassPrivate), + SpvStorageClassPrivate, + fuzzerutil::MaybeGetZeroConstant(ir_context, *transformation_context, + param_inst->type_id(), false)); + + auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id()); + assert(function && "Function must exist"); + + // Insert an OpLoad instruction right after OpVariable instructions. + auto it = function->begin()->begin(); + while (it != function->begin()->end() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it)) { + ++it; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLoad, it) && + "Can't insert OpLoad or OpCopyMemory into the first basic block of " + "the function"); + + it.InsertBefore(MakeUnique( + ir_context, SpvOpLoad, param_inst->type_id(), param_inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}})); + + // Calculate the index of the replaced parameter (we need to know this to + // remove operands from the OpFunctionCall). + auto params = fuzzerutil::GetParameters(ir_context, function->result_id()); + auto parameter_index = static_cast(params.size()); + for (uint32_t i = 0, n = static_cast(params.size()); i < n; ++i) { + if (params[i]->result_id() == message_.parameter_id()) { + parameter_index = i; + break; + } + } + + assert(parameter_index != params.size() && + "Parameter must exist in the function"); + + // Update all relevant OpFunctionCall instructions. + for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) { + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, inst) && + "Can't insert OpStore right before the function call"); + + // Insert an OpStore before the OpFunctionCall. +1 since the first + // operand of OpFunctionCall is an id of the function. + inst->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.global_variable_fresh_id()}}, + {SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand(parameter_index + 1)}}})); + + // +1 since the first operand of OpFunctionCall is an id of the + // function. + inst->RemoveInOperand(parameter_index + 1); + } + + // Remove the parameter from the function. + fuzzerutil::RemoveParameter(ir_context, message_.parameter_id()); + + // Update function's type. + { + // We use a separate scope here since |old_function_type| might become a + // dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + // +1 and -1 since the first operand is the return type id. + std::vector parameter_type_ids; + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + if (i - 1 != parameter_index) { + parameter_type_ids.push_back( + old_function_type->GetSingleWordInOperand(i)); + } + } + + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.function_type_fresh_id(), + old_function_type->GetSingleWordInOperand(0), parameter_type_ids); + } + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // Mark the pointee of the global variable storing the parameter's value as + // irrelevant if replaced parameter is irrelevant. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.parameter_id())) { + transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + message_.global_variable_fresh_id()); + } +} + +protobufs::Transformation TransformationReplaceParameterWithGlobal::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_parameter_with_global() = message_; + return result; +} + +bool TransformationReplaceParameterWithGlobal::IsParameterTypeSupported( + opt::IRContext* ir_context, uint32_t param_type_id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Think about other type instructions we can add here. + return fuzzerutil::CanCreateConstant(ir_context, param_type_id); +} + +std::unordered_set +TransformationReplaceParameterWithGlobal::GetFreshIds() const { + return {message_.function_type_fresh_id(), + message_.global_variable_fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h b/third_party/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h new file mode 100644 index 0000000..c2d5f8f --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_parameter_with_global.h @@ -0,0 +1,70 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceParameterWithGlobal : public Transformation { + public: + explicit TransformationReplaceParameterWithGlobal( + const protobufs::TransformationReplaceParameterWithGlobal& message); + + TransformationReplaceParameterWithGlobal(uint32_t function_type_fresh_id, + uint32_t parameter_id, + uint32_t global_variable_fresh_id); + + // - |function_type_fresh_id| is a fresh id. + // - |parameter_id| is the result id of the parameter to replace. + // - |global_variable_fresh_id| is a fresh id. + // - |function_type_fresh_id| is not equal to |global_variable_fresh_id|. + // - the function that contains |parameter_id| may not be an entry-point + // function. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Removes parameter with result id |parameter_id| from its function + // - Adds a global variable to store the value for the parameter + // - Add an OpStore instruction before each function call to + // store parameter's value into the variable + // - Adds OpLoad at the beginning of the function to load the + // value from the variable into the old parameter's id + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if the type of the parameter is supported by this + // transformation. + static bool IsParameterTypeSupported(opt::IRContext* ir_context, + uint32_t param_type_id); + + private: + protobufs::TransformationReplaceParameterWithGlobal message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMETER_WITH_GLOBAL_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp b/third_party/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp new file mode 100644 index 0000000..0a135e5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_params_with_struct.cpp @@ -0,0 +1,316 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_params_with_struct.h" + +#include + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( + const protobufs::TransformationReplaceParamsWithStruct& message) + : message_(message) {} + +TransformationReplaceParamsWithStruct::TransformationReplaceParamsWithStruct( + const std::vector& parameter_id, uint32_t fresh_function_type_id, + uint32_t fresh_parameter_id, + const std::map& caller_id_to_fresh_composite_id) { + message_.set_fresh_function_type_id(fresh_function_type_id); + message_.set_fresh_parameter_id(fresh_parameter_id); + + for (auto id : parameter_id) { + message_.add_parameter_id(id); + } + + *message_.mutable_caller_id_to_fresh_composite_id() = + fuzzerutil::MapToRepeatedUInt32Pair(caller_id_to_fresh_composite_id); +} + +bool TransformationReplaceParamsWithStruct::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + std::vector parameter_id(message_.parameter_id().begin(), + message_.parameter_id().end()); + + // Check that |parameter_id| is neither empty nor it has duplicates. + if (parameter_id.empty() || fuzzerutil::HasDuplicates(parameter_id)) { + return false; + } + + // All ids must correspond to valid parameters of the same function. + // The function can't be an entry-point function. + + // fuzzerutil::GetFunctionFromParameterId requires a valid id. + if (!ir_context->get_def_use_mgr()->GetDef(parameter_id[0])) { + return false; + } + + const auto* function = + fuzzerutil::GetFunctionFromParameterId(ir_context, parameter_id[0]); + if (!function || + fuzzerutil::FunctionIsEntryPoint(ir_context, function->result_id())) { + return false; + } + + // Compute all ids of the function's parameters. + std::unordered_set all_parameter_ids; + for (const auto* param : + fuzzerutil::GetParameters(ir_context, function->result_id())) { + all_parameter_ids.insert(param->result_id()); + } + + // Check that all elements in |parameter_id| are valid. + for (auto id : parameter_id) { + // fuzzerutil::GetFunctionFromParameterId requires a valid id. + if (!ir_context->get_def_use_mgr()->GetDef(id)) { + return false; + } + + // Check that |id| is a result id of one of the |function|'s parameters. + if (!all_parameter_ids.count(id)) { + return false; + } + + // Check that the parameter with result id |id| has supported type. + if (!IsParameterTypeSupported(ir_context, + fuzzerutil::GetTypeId(ir_context, id))) { + return false; + } + } + + // We already know that the function has at least |parameter_id.size()| + // parameters. + + // Check that a relevant OpTypeStruct exists in the module. + if (!MaybeGetRequiredStructType(ir_context)) { + return false; + } + + const auto caller_id_to_fresh_composite_id = + fuzzerutil::RepeatedUInt32PairToMap( + message_.caller_id_to_fresh_composite_id()); + + // Check that |callee_id_to_fresh_composite_id| is valid. + for (const auto* inst : + fuzzerutil::GetCallers(ir_context, function->result_id())) { + // Check that the callee is present in the map. It's ok if the map contains + // more ids that there are callees (those ids will not be used). + if (!caller_id_to_fresh_composite_id.count(inst->result_id())) { + return false; + } + } + + // Check that all fresh ids are unique and fresh. + std::vector fresh_ids = {message_.fresh_function_type_id(), + message_.fresh_parameter_id()}; + + for (const auto& entry : caller_id_to_fresh_composite_id) { + fresh_ids.push_back(entry.second); + } + + return !fuzzerutil::HasDuplicates(fresh_ids) && + std::all_of(fresh_ids.begin(), fresh_ids.end(), + [ir_context](uint32_t id) { + return fuzzerutil::IsFreshId(ir_context, id); + }); +} + +void TransformationReplaceParamsWithStruct::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id(0)); + assert(function && + "All parameters' ids should've been checked in the IsApplicable"); + + // Get a type id of the OpTypeStruct used as a type id of the new parameter. + auto struct_type_id = MaybeGetRequiredStructType(ir_context); + assert(struct_type_id && + "IsApplicable should've guaranteed that this value isn't equal to 0"); + + // Add new parameter to the function. + function->AddParameter(MakeUnique( + ir_context, SpvOpFunctionParameter, struct_type_id, + message_.fresh_parameter_id(), opt::Instruction::OperandList())); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_parameter_id()); + + // Compute indices of replaced parameters. This will be used to adjust + // OpFunctionCall instructions and create OpCompositeConstruct instructions at + // every call site. + const auto indices_of_replaced_params = + ComputeIndicesOfReplacedParameters(ir_context); + + const auto caller_id_to_fresh_composite_id = + fuzzerutil::RepeatedUInt32PairToMap( + message_.caller_id_to_fresh_composite_id()); + + // Update all function calls. + for (auto* inst : fuzzerutil::GetCallers(ir_context, function->result_id())) { + // Create a list of operands for the OpCompositeConstruct instruction. + opt::Instruction::OperandList composite_components; + for (auto index : indices_of_replaced_params) { + // +1 since the first in operand to OpFunctionCall is the result id of + // the function. + composite_components.emplace_back( + std::move(inst->GetInOperand(index + 1))); + } + + // Remove arguments from the function call. We do it in a separate loop + // and in decreasing order to make sure we have removed correct operands. + for (auto index : std::set>( + indices_of_replaced_params.begin(), + indices_of_replaced_params.end())) { + // +1 since the first in operand to OpFunctionCall is the result id of + // the function. + inst->RemoveInOperand(index + 1); + } + + // Insert OpCompositeConstruct before the function call. + auto fresh_composite_id = + caller_id_to_fresh_composite_id.at(inst->result_id()); + inst->InsertBefore(MakeUnique( + ir_context, SpvOpCompositeConstruct, struct_type_id, fresh_composite_id, + std::move(composite_components))); + + // Add a new operand to the OpFunctionCall instruction. + inst->AddOperand({SPV_OPERAND_TYPE_ID, {fresh_composite_id}}); + fuzzerutil::UpdateModuleIdBound(ir_context, fresh_composite_id); + } + + // Insert OpCompositeExtract instructions into the entry point block of the + // function and remove replaced parameters. + for (int i = 0; i < message_.parameter_id_size(); ++i) { + const auto* param_inst = + ir_context->get_def_use_mgr()->GetDef(message_.parameter_id(i)); + assert(param_inst && "Parameter id is invalid"); + + // Skip all OpVariable instructions. + auto iter = function->begin()->begin(); + while (iter != function->begin()->end() && + !fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, + iter)) { + ++iter; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpCompositeExtract, + iter) && + "Can't extract parameter's value from the structure"); + + // Insert OpCompositeExtract instructions to unpack parameters' values from + // the struct type. + iter.InsertBefore(MakeUnique( + ir_context, SpvOpCompositeExtract, param_inst->type_id(), + param_inst->result_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.fresh_parameter_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {static_cast(i)}}})); + + fuzzerutil::RemoveParameter(ir_context, param_inst->result_id()); + } + + // Update function's type. + { + // We use a separate scope here since |old_function_type| might become a + // dangling pointer after the call to the fuzzerutil::UpdateFunctionType. + + auto* old_function_type = fuzzerutil::GetFunctionType(ir_context, function); + assert(old_function_type && "Function has invalid type"); + + // +1 since the first in operand to OpTypeFunction is the result type id + // of the function. + std::vector parameter_type_ids; + for (uint32_t i = 1; i < old_function_type->NumInOperands(); ++i) { + if (std::find(indices_of_replaced_params.begin(), + indices_of_replaced_params.end(), + i - 1) == indices_of_replaced_params.end()) { + parameter_type_ids.push_back( + old_function_type->GetSingleWordInOperand(i)); + } + } + + parameter_type_ids.push_back(struct_type_id); + + fuzzerutil::UpdateFunctionType( + ir_context, function->result_id(), message_.fresh_function_type_id(), + old_function_type->GetSingleWordInOperand(0), parameter_type_ids); + } + + // Make sure our changes are analyzed + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation TransformationReplaceParamsWithStruct::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_replace_params_with_struct() = message_; + return result; +} + +bool TransformationReplaceParamsWithStruct::IsParameterTypeSupported( + opt::IRContext* ir_context, uint32_t param_type_id) { + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3403): + // Consider adding support for more types of parameters. + return fuzzerutil::CanCreateConstant(ir_context, param_type_id); +} + +uint32_t TransformationReplaceParamsWithStruct::MaybeGetRequiredStructType( + opt::IRContext* ir_context) const { + std::vector component_type_ids; + for (auto id : message_.parameter_id()) { + component_type_ids.push_back(fuzzerutil::GetTypeId(ir_context, id)); + } + + return fuzzerutil::MaybeGetStructType(ir_context, component_type_ids); +} + +std::vector +TransformationReplaceParamsWithStruct::ComputeIndicesOfReplacedParameters( + opt::IRContext* ir_context) const { + assert(!message_.parameter_id().empty() && + "There must be at least one parameter to replace"); + + const auto* function = fuzzerutil::GetFunctionFromParameterId( + ir_context, message_.parameter_id(0)); + assert(function && "|parameter_id|s are invalid"); + + std::vector result; + + auto params = fuzzerutil::GetParameters(ir_context, function->result_id()); + for (auto id : message_.parameter_id()) { + auto it = std::find_if(params.begin(), params.end(), + [id](const opt::Instruction* param) { + return param->result_id() == id; + }); + assert(it != params.end() && "Parameter's id is invalid"); + result.push_back(static_cast(it - params.begin())); + } + + return result; +} + +std::unordered_set +TransformationReplaceParamsWithStruct::GetFreshIds() const { + std::unordered_set result = {message_.fresh_function_type_id(), + message_.fresh_parameter_id()}; + for (auto& pair : message_.caller_id_to_fresh_composite_id()) { + result.insert(pair.second()); + } + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h b/third_party/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h new file mode 100644 index 0000000..afa6b14 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_replace_params_with_struct.h @@ -0,0 +1,91 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ +#define SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationReplaceParamsWithStruct : public Transformation { + public: + explicit TransformationReplaceParamsWithStruct( + const protobufs::TransformationReplaceParamsWithStruct& message); + + TransformationReplaceParamsWithStruct( + const std::vector& parameter_id, + uint32_t fresh_function_type_id, uint32_t fresh_parameter_id, + const std::map& caller_id_to_fresh_composite_id); + + // - Each element of |parameter_id| is a valid result id of some + // OpFunctionParameter instruction. All parameter ids must correspond to + // parameters of the same function. That function may not be an entry-point + // function. + // - Types of all parameters must be supported by this transformation (see + // IsParameterTypeSupported method). + // - |parameter_id| may not be empty or contain duplicates. + // - There must exist an OpTypeStruct instruction containing types of all + // replaced parameters. Type of the i'th component of the struct is equal + // to the type of the instruction with result id |parameter_id[i]|. + // - |caller_id_to_fresh_composite_id| should contain a key for at least every + // result id of an OpFunctionCall instruction that calls the function. + // - |fresh_function_type_id|, |fresh_parameter_id|, + // |caller_id_to_fresh_composite_id| are all fresh and unique ids. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Creates a new function parameter with result id |fresh_parameter_id|. + // Parameter's type is OpTypeStruct with each components type equal to the + // type of the replaced parameter. + // - OpCompositeConstruct with result id from |fresh_composite_id| is inserted + // before each OpFunctionCall instruction. + // - OpCompositeExtract with result id equal to the result id of the replaced + // parameter is created in the function. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if parameter's type is supported by this transformation. + static bool IsParameterTypeSupported(opt::IRContext* ir_context, + uint32_t param_type_id); + + private: + // Returns a result id of the OpTypeStruct instruction required by this + // transformation (see docs on the IsApplicable method to learn more). + uint32_t MaybeGetRequiredStructType(opt::IRContext* ir_context) const; + + // Returns a vector of indices of parameters to replace. Concretely, i'th + // element is the index of the parameter with result id |parameter_id[i]| in + // its function. + std::vector ComputeIndicesOfReplacedParameters( + opt::IRContext* ir_context) const; + + protobufs::TransformationReplaceParamsWithStruct message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_REPLACE_PARAMS_WITH_STRUCT_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_set_function_control.cpp b/third_party/spirv-tools/source/fuzz/transformation_set_function_control.cpp new file mode 100644 index 0000000..8ab9b8c --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_set_function_control.cpp @@ -0,0 +1,105 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_function_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetFunctionControl::TransformationSetFunctionControl( + const spvtools::fuzz::protobufs::TransformationSetFunctionControl& message) + : message_(message) {} + +TransformationSetFunctionControl::TransformationSetFunctionControl( + uint32_t function_id, uint32_t function_control) { + message_.set_function_id(function_id); + message_.set_function_control(function_control); +} + +bool TransformationSetFunctionControl::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + opt::Instruction* function_def_instruction = + FindFunctionDefInstruction(ir_context); + if (!function_def_instruction) { + // The given function id does not correspond to any function. + return false; + } + uint32_t existing_function_control_mask = + function_def_instruction->GetSingleWordInOperand(0); + + // Check (via an assertion) that function control mask doesn't have any bad + // bits set. + uint32_t acceptable_function_control_bits = + SpvFunctionControlInlineMask | SpvFunctionControlDontInlineMask | + SpvFunctionControlPureMask | SpvFunctionControlConstMask; + // The following is to keep release-mode compilers happy as this variable is + // only used in an assertion. + (void)(acceptable_function_control_bits); + assert(!(message_.function_control() & ~acceptable_function_control_bits) && + "Nonsensical loop control bits were found."); + + // Check (via an assertion) that function control mask does not have both + // Inline and DontInline bits set. + assert(!((message_.function_control() & SpvFunctionControlInlineMask) && + (message_.function_control() & SpvFunctionControlDontInlineMask)) && + "It is not OK to set both the 'Inline' and 'DontInline' bits of a " + "function control mask"); + + // Check that Const and Pure are only present if they were present on the + // original function + for (auto mask_bit : + {SpvFunctionControlPureMask, SpvFunctionControlConstMask}) { + if ((message_.function_control() & mask_bit) && + !(existing_function_control_mask & mask_bit)) { + return false; + } + } + + return true; +} + +void TransformationSetFunctionControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + opt::Instruction* function_def_instruction = + FindFunctionDefInstruction(ir_context); + function_def_instruction->SetInOperand(0, {message_.function_control()}); +} + +protobufs::Transformation TransformationSetFunctionControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_function_control() = message_; + return result; +} + +opt::Instruction* TransformationSetFunctionControl ::FindFunctionDefInstruction( + opt::IRContext* ir_context) const { + // Look through all functions for a function whose defining instruction's + // result id matches |message_.function_id|, returning the defining + // instruction if found. + for (auto& function : *ir_context->module()) { + if (function.DefInst().result_id() == message_.function_id()) { + return &function.DefInst(); + } + } + // A nullptr result indicates that no match was found. + return nullptr; +} + +std::unordered_set TransformationSetFunctionControl::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_set_function_control.h b/third_party/spirv-tools/source/fuzz/transformation_set_function_control.h new file mode 100644 index 0000000..2952cc6 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_set_function_control.h @@ -0,0 +1,63 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetFunctionControl : public Transformation { + public: + explicit TransformationSetFunctionControl( + const protobufs::TransformationSetFunctionControl& message); + + TransformationSetFunctionControl(uint32_t function_id, + uint32_t function_control); + + // - |message_.function_id| must be the result id of an OpFunction + // instruction. + // - |message_.function_control| must be a function control mask that sets + // at most one of 'Inline' or 'DontInline', and that may not contain 'Pure' + // (respectively 'Const') unless the existing function control mask contains + // 'Pure' (respectively 'Const'). + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // The function control operand of instruction |message_.function_id| is + // over-written with |message_.function_control|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + opt::Instruction* FindFunctionDefInstruction( + opt::IRContext* ir_context) const; + + protobufs::TransformationSetFunctionControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_FUNCTION_CONTROL_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_set_loop_control.cpp b/third_party/spirv-tools/source/fuzz/transformation_set_loop_control.cpp new file mode 100644 index 0000000..b2180d8 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_set_loop_control.cpp @@ -0,0 +1,221 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_loop_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetLoopControl::TransformationSetLoopControl( + const spvtools::fuzz::protobufs::TransformationSetLoopControl& message) + : message_(message) {} + +TransformationSetLoopControl::TransformationSetLoopControl( + uint32_t block_id, uint32_t loop_control, uint32_t peel_count, + uint32_t partial_count) { + message_.set_block_id(block_id); + message_.set_loop_control(loop_control); + message_.set_peel_count(peel_count); + message_.set_partial_count(partial_count); +} + +bool TransformationSetLoopControl::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // |message_.block_id| must identify a block that ends with OpLoopMerge. + auto block = ir_context->get_instr_block(message_.block_id()); + if (!block) { + return false; + } + auto merge_inst = block->GetMergeInst(); + if (!merge_inst || merge_inst->opcode() != SpvOpLoopMerge) { + return false; + } + + // We assert that the transformation does not try to set any meaningless bits + // of the loop control mask. + uint32_t all_loop_control_mask_bits_set = + SpvLoopControlUnrollMask | SpvLoopControlDontUnrollMask | + SpvLoopControlDependencyInfiniteMask | + SpvLoopControlDependencyLengthMask | SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | SpvLoopControlIterationMultipleMask | + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask; + + // The variable is only used in an assertion; the following keeps release-mode + // compilers happy. + (void)(all_loop_control_mask_bits_set); + + // No additional bits should be set. + assert(!(message_.loop_control() & ~all_loop_control_mask_bits_set)); + + // Grab the loop control mask currently associated with the OpLoopMerge + // instruction. + auto existing_loop_control_mask = + merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex); + + // Check that there is no attempt to set one of the loop controls that + // requires guarantees to hold. + for (SpvLoopControlMask mask : + {SpvLoopControlDependencyInfiniteMask, + SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask, + SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) { + // We have a problem if this loop control bit was not set in the original + // loop control mask but is set by the transformation. + if (LoopControlBitIsAddedByTransformation(mask, + existing_loop_control_mask)) { + return false; + } + } + + if ((message_.loop_control() & + (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask)) && + !(PeelCountIsSupported(ir_context) && + PartialCountIsSupported(ir_context))) { + // At least one of PeelCount or PartialCount is used, but the SPIR-V version + // in question does not support these loop controls. + return false; + } + + if (message_.peel_count() > 0 && + !(message_.loop_control() & SpvLoopControlPeelCountMask)) { + // Peel count provided, but peel count mask bit not set. + return false; + } + + if (message_.partial_count() > 0 && + !(message_.loop_control() & SpvLoopControlPartialCountMask)) { + // Partial count provided, but partial count mask bit not set. + return false; + } + + // We must not set both 'don't unroll' and one of 'peel count' or 'partial + // count'. + return !((message_.loop_control() & SpvLoopControlDontUnrollMask) && + (message_.loop_control() & + (SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask))); +} + +void TransformationSetLoopControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + // Grab the loop merge instruction and its associated loop control mask. + auto merge_inst = + ir_context->get_instr_block(message_.block_id())->GetMergeInst(); + auto existing_loop_control_mask = + merge_inst->GetSingleWordInOperand(kLoopControlMaskInOperandIndex); + + // We are going to replace the OpLoopMerge's operands with this list. + opt::Instruction::OperandList new_operands; + // We add the existing merge block and continue target ids. + new_operands.push_back(merge_inst->GetInOperand(0)); + new_operands.push_back(merge_inst->GetInOperand(1)); + // We use the loop control mask from the transformation. + new_operands.push_back( + {SPV_OPERAND_TYPE_LOOP_CONTROL, {message_.loop_control()}}); + + // It remains to determine what literals to provide, in association with + // the new loop control mask. + // + // For the loop controls that require guarantees to hold about the number + // of loop iterations, we need to keep, from the original OpLoopMerge, any + // literals associated with loop control bits that are still set. + + uint32_t literal_index = 0; // Indexes into the literals from the original + // instruction. + for (SpvLoopControlMask mask : + {SpvLoopControlDependencyLengthMask, SpvLoopControlMinIterationsMask, + SpvLoopControlMaxIterationsMask, SpvLoopControlIterationMultipleMask}) { + // Check whether the bit was set in the original loop control mask. + if (existing_loop_control_mask & mask) { + // Check whether the bit is set in the new loop control mask. + if (message_.loop_control() & mask) { + // Add the associated literal to our sequence of replacement operands. + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {merge_inst->GetSingleWordInOperand( + kLoopControlFirstLiteralInOperandIndex + literal_index)}}); + } + // Increment our index into the original loop control mask's literals, + // whether or not the bit was set in the new mask. + literal_index++; + } + } + + // If PeelCount is set in the new mask, |message_.peel_count| provides the + // associated peel count. + if (message_.loop_control() & SpvLoopControlPeelCountMask) { + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.peel_count()}}); + } + + // Similar, but for PartialCount. + if (message_.loop_control() & SpvLoopControlPartialCountMask) { + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {message_.partial_count()}}); + } + + // Replace the input operands of the OpLoopMerge with the new operands we have + // accumulated. + merge_inst->SetInOperands(std::move(new_operands)); +} + +protobufs::Transformation TransformationSetLoopControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_loop_control() = message_; + return result; +} + +bool TransformationSetLoopControl::LoopControlBitIsAddedByTransformation( + SpvLoopControlMask loop_control_single_bit_mask, + uint32_t existing_loop_control_mask) const { + return !(loop_control_single_bit_mask & existing_loop_control_mask) && + (loop_control_single_bit_mask & message_.loop_control()); +} + +bool TransformationSetLoopControl::PartialCountIsSupported( + opt::IRContext* ir_context) { + // TODO(afd): We capture the universal environments for which this loop + // control is definitely not supported. The check should be refined on + // demand for other target environments. + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + return false; + default: + return true; + } +} + +bool TransformationSetLoopControl::PeelCountIsSupported( + opt::IRContext* ir_context) { + // TODO(afd): We capture the universal environments for which this loop + // control is definitely not supported. The check should be refined on + // demand for other target environments. + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + return false; + default: + return true; + } +} + +std::unordered_set TransformationSetLoopControl::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_set_loop_control.h b/third_party/spirv-tools/source/fuzz/transformation_set_loop_control.h new file mode 100644 index 0000000..c3480b1 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_set_loop_control.h @@ -0,0 +1,83 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetLoopControl : public Transformation { + public: + const static uint32_t kLoopControlMaskInOperandIndex = 2; + const static uint32_t kLoopControlFirstLiteralInOperandIndex = 3; + + explicit TransformationSetLoopControl( + const protobufs::TransformationSetLoopControl& message); + + TransformationSetLoopControl(uint32_t block_id, uint32_t loop_control, + uint32_t peel_count, uint32_t partial_count); + + // - |message_.block_id| must be a block containing an OpLoopMerge + // instruction. + // - |message_.loop_control| must be a legal loop control mask that + // only uses controls available in the SPIR-V version associated with + // |ir_context|, and must not add loop controls that are only valid in the + // presence of guarantees about what the loop does (e.g. MinIterations). + // - |message_.peel_count| (respectively |message_.partial_count|) must be + // zero PeelCount (respectively PartialCount) is set in + // |message_.loop_control|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - The loop control operand of the OpLoopMergeInstruction in + // |message_.block_id| is overwritten with |message_.loop_control|. + // - The literals associated with the loop control are updated to reflect any + // controls with associated literals that have been removed (e.g. + // MinIterations), and any that have been added (PeelCount and/or + // PartialCount). + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Does the version of SPIR-V being used support the PartialCount loop + // control? + static bool PartialCountIsSupported(opt::IRContext* ir_context); + + // Does the version of SPIR-V being used support the PeelCount loop control? + static bool PeelCountIsSupported(opt::IRContext* ir_context); + + private: + // Returns true if and only if |loop_single_bit_mask| is *not* set in + // |existing_loop_control| but *is* set in |message_.loop_control|. + bool LoopControlBitIsAddedByTransformation( + SpvLoopControlMask loop_control_single_bit_mask, + uint32_t existing_loop_control_mask) const; + + protobufs::TransformationSetLoopControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_LOOP_CONTROL_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.cpp b/third_party/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.cpp new file mode 100644 index 0000000..deb207a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.cpp @@ -0,0 +1,228 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_memory_operands_mask.h" + +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +namespace { + +const uint32_t kOpLoadMemoryOperandsMaskIndex = 1; +const uint32_t kOpStoreMemoryOperandsMaskIndex = 2; +const uint32_t kOpCopyMemoryFirstMemoryOperandsMaskIndex = 2; +const uint32_t kOpCopyMemorySizedFirstMemoryOperandsMaskIndex = 3; + +} // namespace + +TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask( + const spvtools::fuzz::protobufs::TransformationSetMemoryOperandsMask& + message) + : message_(message) {} + +TransformationSetMemoryOperandsMask::TransformationSetMemoryOperandsMask( + const protobufs::InstructionDescriptor& memory_access_instruction, + uint32_t memory_operands_mask, uint32_t memory_operands_mask_index) { + *message_.mutable_memory_access_instruction() = memory_access_instruction; + message_.set_memory_operands_mask(memory_operands_mask); + message_.set_memory_operands_mask_index(memory_operands_mask_index); +} + +bool TransformationSetMemoryOperandsMask::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (message_.memory_operands_mask_index() != 0) { + // The following conditions should never be violated, even if + // transformations end up being replayed in a different way to the manner in + // which they were applied during fuzzing, hence why these are assertions + // rather than applicability checks. + assert(message_.memory_operands_mask_index() == 1); + assert(message_.memory_access_instruction().target_instruction_opcode() == + SpvOpCopyMemory || + message_.memory_access_instruction().target_instruction_opcode() == + SpvOpCopyMemorySized); + assert(MultipleMemoryOperandMasksAreSupported(ir_context)); + } + + auto instruction = + FindInstruction(message_.memory_access_instruction(), ir_context); + if (!instruction) { + return false; + } + if (!IsMemoryAccess(*instruction)) { + return false; + } + + auto original_mask_in_operand_index = GetInOperandIndexForMask( + *instruction, message_.memory_operands_mask_index()); + assert(original_mask_in_operand_index != 0 && + "The given mask index is not valid."); + uint32_t original_mask = + original_mask_in_operand_index < instruction->NumInOperands() + ? instruction->GetSingleWordInOperand(original_mask_in_operand_index) + : static_cast(SpvMemoryAccessMaskNone); + uint32_t new_mask = message_.memory_operands_mask(); + + // Volatile must not be removed + if ((original_mask & SpvMemoryAccessVolatileMask) && + !(new_mask & SpvMemoryAccessVolatileMask)) { + return false; + } + + // Nontemporal can be added or removed, and no other flag is allowed to + // change. We do this by checking that the masks are equal once we set + // their Volatile and Nontemporal flags to the same value (this works + // because valid manipulation of Volatile is checked above, and the manner + // in which Nontemporal is manipulated does not matter). + return (original_mask | SpvMemoryAccessVolatileMask | + SpvMemoryAccessNontemporalMask) == + (new_mask | SpvMemoryAccessVolatileMask | + SpvMemoryAccessNontemporalMask); +} + +void TransformationSetMemoryOperandsMask::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + FindInstruction(message_.memory_access_instruction(), ir_context); + auto original_mask_in_operand_index = GetInOperandIndexForMask( + *instruction, message_.memory_operands_mask_index()); + // Either add a new operand, if no mask operand was already present, or + // replace an existing mask operand. + if (original_mask_in_operand_index >= instruction->NumInOperands()) { + // Add first memory operand if it's missing. + if (message_.memory_operands_mask_index() == 1 && + GetInOperandIndexForMask(*instruction, 0) >= + instruction->NumInOperands()) { + instruction->AddOperand( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}}); + } + + instruction->AddOperand( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, {message_.memory_operands_mask()}}); + + } else { + instruction->SetInOperand(original_mask_in_operand_index, + {message_.memory_operands_mask()}); + } +} + +protobufs::Transformation TransformationSetMemoryOperandsMask::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_set_memory_operands_mask() = message_; + return result; +} + +bool TransformationSetMemoryOperandsMask::IsMemoryAccess( + const opt::Instruction& instruction) { + switch (instruction.opcode()) { + case SpvOpLoad: + case SpvOpStore: + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + return true; + default: + return false; + } +} + +uint32_t TransformationSetMemoryOperandsMask::GetInOperandIndexForMask( + const opt::Instruction& instruction, uint32_t mask_index) { + // Get the input operand index associated with the first memory operands mask + // for the instruction. + uint32_t first_mask_in_operand_index = 0; + switch (instruction.opcode()) { + case SpvOpLoad: + first_mask_in_operand_index = kOpLoadMemoryOperandsMaskIndex; + break; + case SpvOpStore: + first_mask_in_operand_index = kOpStoreMemoryOperandsMaskIndex; + break; + case SpvOpCopyMemory: + first_mask_in_operand_index = kOpCopyMemoryFirstMemoryOperandsMaskIndex; + break; + case SpvOpCopyMemorySized: + first_mask_in_operand_index = + kOpCopyMemorySizedFirstMemoryOperandsMaskIndex; + break; + default: + assert(false && "Unknown memory instruction."); + break; + } + // If we are looking for the input operand index of the first mask, return it. + // This will also return a correct value if the operand is missing. + if (mask_index == 0) { + return first_mask_in_operand_index; + } + assert(mask_index == 1 && "Memory operands mask index must be 0 or 1."); + + // Memory mask operands are optional. Thus, if the second operand exists, + // its index will be >= |first_mask_in_operand_index + 1|. We can reason as + // follows to separate the cases where the index of the second operand is + // equal to |first_mask_in_operand_index + 1|: + // - If the first memory operand doesn't exist, its value is equal to None. + // This means that it doesn't have additional operands following it and the + // condition in the if statement below will be satisfied. + // - If the first memory operand exists and has no additional memory operands + // following it, the condition in the if statement below will be satisfied + // and we will return the correct value from the function. + if (first_mask_in_operand_index + 1 >= instruction.NumInOperands()) { + return first_mask_in_operand_index + 1; + } + + // We are looking for the input operand index of the second mask. This is a + // little complicated because, depending on the contents of the first mask, + // there may be some input operands separating the two masks. + uint32_t first_mask = + instruction.GetSingleWordInOperand(first_mask_in_operand_index); + + // Consider each bit that might have an associated extra input operand, and + // count how many there are expected to be. + uint32_t first_mask_extra_operand_count = 0; + for (auto mask_bit : + {SpvMemoryAccessAlignedMask, SpvMemoryAccessMakePointerAvailableMask, + SpvMemoryAccessMakePointerAvailableKHRMask, + SpvMemoryAccessMakePointerVisibleMask, + SpvMemoryAccessMakePointerVisibleKHRMask}) { + if (first_mask & mask_bit) { + first_mask_extra_operand_count++; + } + } + return first_mask_in_operand_index + first_mask_extra_operand_count + 1; +} + +bool TransformationSetMemoryOperandsMask:: + MultipleMemoryOperandMasksAreSupported(opt::IRContext* ir_context) { + // TODO(afd): We capture the universal environments for which this loop + // control is definitely not supported. The check should be refined on + // demand for other target environments. + switch (ir_context->grammar().target_env()) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + return false; + default: + return true; + } +} + +std::unordered_set TransformationSetMemoryOperandsMask::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.h b/third_party/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.h new file mode 100644 index 0000000..7357b1a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_set_memory_operands_mask.h @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetMemoryOperandsMask : public Transformation { + public: + explicit TransformationSetMemoryOperandsMask( + const protobufs::TransformationSetMemoryOperandsMask& message); + + TransformationSetMemoryOperandsMask( + const protobufs::InstructionDescriptor& memory_access_instruction, + uint32_t memory_operands_mask, uint32_t memory_operands_mask_index); + + // - |message_.memory_access_instruction| must describe a memory access + // instruction. + // - |message_.memory_operands_mask_index| must be suitable for this memory + // access instruction, e.g. it must be 0 in the case of OpLoad, and may be + // 1 in the case of OpCopyMemory if the SPIR-V version is 1.4 or higher. + // - |message_.memory_operands_mask| must be identical to the original memory + // operands mask, except that Volatile may be added, and Nontemporal may be + // toggled. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Replaces the operands mask identified by + // |message_.memory_operands_mask_index| in the instruction described by + // |message_.memory_access_instruction| with |message_.memory_operands_mask|, + // creating an input operand for the mask if no such operand was present. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + // Helper function that determines whether |instruction| is a memory + // instruction (e.g. OpLoad). + static bool IsMemoryAccess(const opt::Instruction& instruction); + + // Does the version of SPIR-V being used support multiple memory operand + // masks on relevant memory access instructions? + static bool MultipleMemoryOperandMasksAreSupported( + opt::IRContext* ir_context); + + // Helper function to get the input operand index associated with mask number + // |mask_index|. This is a bit tricky if there are multiple masks, because the + // index associated with the second mask depends on whether the first mask + // includes any flags such as Aligned that have corresponding operands. + static uint32_t GetInOperandIndexForMask(const opt::Instruction& instruction, + uint32_t mask_index); + + private: + protobufs::TransformationSetMemoryOperandsMask message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_MEMORY_OPERANDS_MASK_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_set_selection_control.cpp b/third_party/spirv-tools/source/fuzz/transformation_set_selection_control.cpp new file mode 100644 index 0000000..625187e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_set_selection_control.cpp @@ -0,0 +1,65 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_selection_control.h" + +namespace spvtools { +namespace fuzz { + +TransformationSetSelectionControl::TransformationSetSelectionControl( + const spvtools::fuzz::protobufs::TransformationSetSelectionControl& message) + : message_(message) {} + +TransformationSetSelectionControl::TransformationSetSelectionControl( + uint32_t block_id, uint32_t selection_control) { + message_.set_block_id(block_id); + message_.set_selection_control(selection_control); +} + +bool TransformationSetSelectionControl::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + assert((message_.selection_control() == SpvSelectionControlMaskNone || + message_.selection_control() == SpvSelectionControlFlattenMask || + message_.selection_control() == SpvSelectionControlDontFlattenMask) && + "Selection control should never be set to something other than " + "'None', 'Flatten' or 'DontFlatten'"); + if (auto block = ir_context->get_instr_block(message_.block_id())) { + if (auto merge_inst = block->GetMergeInst()) { + return merge_inst->opcode() == SpvOpSelectionMerge; + } + } + // Either the block did not exit, or did not end with OpSelectionMerge. + return false; +} + +void TransformationSetSelectionControl::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + ir_context->get_instr_block(message_.block_id()) + ->GetMergeInst() + ->SetInOperand(1, {message_.selection_control()}); +} + +protobufs::Transformation TransformationSetSelectionControl::ToMessage() const { + protobufs::Transformation result; + *result.mutable_set_selection_control() = message_; + return result; +} + +std::unordered_set TransformationSetSelectionControl::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_set_selection_control.h b/third_party/spirv-tools/source/fuzz/transformation_set_selection_control.h new file mode 100644 index 0000000..56b5885 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_set_selection_control.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSetSelectionControl : public Transformation { + public: + explicit TransformationSetSelectionControl( + const protobufs::TransformationSetSelectionControl& message); + + TransformationSetSelectionControl(uint32_t block_id, + uint32_t selection_control); + + // - |message_.block_id| must be a block containing an OpSelectionMerge + // instruction. + // - |message_.selection_control| must be one of None, Flatten or + // DontFlatten. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - The selection control operand of the OpSelectionMergeInstruction in + // |message_.block_id| is overwritten with |message_.selection_control|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSetSelectionControl message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SET_SELECTION_CONTROL_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_split_block.cpp b/third_party/spirv-tools/source/fuzz/transformation_split_block.cpp new file mode 100644 index 0000000..b383c40 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_split_block.cpp @@ -0,0 +1,151 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_split_block.h" + +#include + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +TransformationSplitBlock::TransformationSplitBlock( + const spvtools::fuzz::protobufs::TransformationSplitBlock& message) + : message_(message) {} + +TransformationSplitBlock::TransformationSplitBlock( + const protobufs::InstructionDescriptor& instruction_to_split_before, + uint32_t fresh_id) { + *message_.mutable_instruction_to_split_before() = instruction_to_split_before; + message_.set_fresh_id(fresh_id); +} + +bool TransformationSplitBlock::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + // We require the id for the new block to be unused. + return false; + } + auto instruction_to_split_before = + FindInstruction(message_.instruction_to_split_before(), ir_context); + if (!instruction_to_split_before) { + // The instruction describing the block we should split does not exist. + return false; + } + auto block_to_split = + ir_context->get_instr_block(instruction_to_split_before); + assert(block_to_split && + "We should not have managed to find the " + "instruction if it was not contained in a block."); + + if (block_to_split->IsLoopHeader()) { + // We cannot split a loop header block: back-edges would become invalid. + return false; + } + + auto split_before = fuzzerutil::GetIteratorForInstruction( + block_to_split, instruction_to_split_before); + assert(split_before != block_to_split->end() && + "At this point we know the" + " block split point exists."); + + if (split_before->PreviousNode() && + split_before->PreviousNode()->opcode() == SpvOpSelectionMerge) { + // We cannot split directly after a selection merge: this would separate + // the merge from its associated branch or switch operation. + return false; + } + if (split_before->opcode() == SpvOpVariable) { + // We cannot split directly after a variable; variables in a function + // must be contiguous in the entry block. + return false; + } + // We cannot split before an OpPhi unless the OpPhi has exactly one + // associated incoming edge. + if (split_before->opcode() == SpvOpPhi && + split_before->NumInOperands() != 2) { + return false; + } + + // Splitting the block must not separate the definition of an OpSampledImage + // from its use: the SPIR-V data rules require them to be in the same block. + return !fuzzerutil:: + SplittingBeforeInstructionSeparatesOpSampledImageDefinitionFromUse( + block_to_split, instruction_to_split_before); +} + +void TransformationSplitBlock::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + opt::Instruction* instruction_to_split_before = + FindInstruction(message_.instruction_to_split_before(), ir_context); + opt::BasicBlock* block_to_split = + ir_context->get_instr_block(instruction_to_split_before); + auto split_before = fuzzerutil::GetIteratorForInstruction( + block_to_split, instruction_to_split_before); + assert(split_before != block_to_split->end() && + "If the transformation is applicable, we should have an " + "instruction to split on."); + + // We need to make sure the module's id bound is large enough to add the + // fresh id. + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + // Split the block. + auto new_bb = block_to_split->SplitBasicBlock(ir_context, message_.fresh_id(), + split_before); + // The split does not automatically add a branch between the two parts of + // the original block, so we add one. + block_to_split->AddInstruction(MakeUnique( + ir_context, SpvOpBranch, 0, 0, + std::initializer_list{opt::Operand( + spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})})); + // If we split before OpPhi instructions, we need to update their + // predecessor operand so that the block they used to be inside is now the + // predecessor. + new_bb->ForEachPhiInst([block_to_split](opt::Instruction* phi_inst) { + assert( + phi_inst->NumInOperands() == 2 && + "Precondition: a block can only be split before an OpPhi if the block" + "has exactly one predecessor."); + phi_inst->SetInOperand(1, {block_to_split->id()}); + }); + + // Invalidate all analyses + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // If the block being split was dead, the new block arising from the split is + // also dead. + if (transformation_context->GetFactManager()->BlockIsDead( + block_to_split->id())) { + transformation_context->GetFactManager()->AddFactBlockIsDead( + message_.fresh_id()); + } +} + +protobufs::Transformation TransformationSplitBlock::ToMessage() const { + protobufs::Transformation result; + *result.mutable_split_block() = message_; + return result; +} + +std::unordered_set TransformationSplitBlock::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_split_block.h b/third_party/spirv-tools/source/fuzz/transformation_split_block.h new file mode 100644 index 0000000..27bf6f8 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_split_block.h @@ -0,0 +1,67 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSplitBlock : public Transformation { + public: + explicit TransformationSplitBlock( + const protobufs::TransformationSplitBlock& message); + + TransformationSplitBlock( + const protobufs::InstructionDescriptor& instruction_to_split_before, + uint32_t fresh_id); + + // - |message_.base_instruction_id| must be the result id of an instruction + // 'base' in some block 'blk'. + // - 'blk' must contain an instruction 'inst' located |message_.offset| + // instructions after 'base' (if |message_.offset| = 0 then 'inst' = + // 'base'). + // - Splitting 'blk' at 'inst', so that all instructions from 'inst' onwards + // appear in a new block that 'blk' directly jumps to must be valid. + // - |message_.fresh_id| must not be used by the module. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - A new block with label |message_.fresh_id| is inserted right after 'blk' + // in program order. + // - All instructions of 'blk' from 'inst' onwards are moved into the new + // block. + // - 'blk' is made to jump unconditionally to the new block. + // - If 'blk' was dead, the new block is also dead. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSplitBlock message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SPLIT_BLOCK_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_store.cpp b/third_party/spirv-tools/source/fuzz/transformation_store.cpp new file mode 100644 index 0000000..460ca01 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_store.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_store.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationStore::TransformationStore( + const spvtools::fuzz::protobufs::TransformationStore& message) + : message_(message) {} + +TransformationStore::TransformationStore( + uint32_t pointer_id, uint32_t value_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before) { + message_.set_pointer_id(pointer_id); + message_.set_value_id(value_id); + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; +} + +bool TransformationStore::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // The pointer must exist and have a type. + auto pointer = ir_context->get_def_use_mgr()->GetDef(message_.pointer_id()); + if (!pointer || !pointer->type_id()) { + return false; + } + + // The pointer type must indeed be a pointer. + auto pointer_type = ir_context->get_def_use_mgr()->GetDef(pointer->type_id()); + assert(pointer_type && "Type id must be defined."); + if (pointer_type->opcode() != SpvOpTypePointer) { + return false; + } + + // The pointer must not be read only. + if (pointer->IsReadOnlyPointer()) { + return false; + } + + // We do not want to allow storing to null or undefined pointers. + switch (pointer->opcode()) { + case SpvOpConstantNull: + case SpvOpUndef: + return false; + default: + break; + } + + // Determine which instruction we should be inserting before. + auto insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + // It must exist, ... + if (!insert_before) { + return false; + } + // ... and it must be legitimate to insert a store before it. + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpStore, + insert_before)) { + return false; + } + + // The block we are inserting into needs to be dead, or else the pointee type + // of the pointer we are storing to needs to be irrelevant (otherwise the + // store could impact on the observable behaviour of the module). + if (!transformation_context.GetFactManager()->BlockIsDead( + ir_context->get_instr_block(insert_before)->id()) && + !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + message_.pointer_id())) { + return false; + } + + // The value being stored needs to exist and have a type. + auto value = ir_context->get_def_use_mgr()->GetDef(message_.value_id()); + if (!value || !value->type_id()) { + return false; + } + + // The type of the value must match the pointee type. + if (pointer_type->GetSingleWordInOperand(1) != value->type_id()) { + return false; + } + + // The pointer needs to be available at the insertion point. + if (!fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + message_.pointer_id())) { + return false; + } + + // The value needs to be available at the insertion point. + return fuzzerutil::IdIsAvailableBeforeInstruction(ir_context, insert_before, + message_.value_id()); +} + +void TransformationStore::Apply(opt::IRContext* ir_context, + TransformationContext* /*unused*/) const { + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpStore, 0, 0, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {message_.pointer_id()}}, + {SPV_OPERAND_TYPE_ID, {message_.value_id()}}}))); + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationStore::ToMessage() const { + protobufs::Transformation result; + *result.mutable_store() = message_; + return result; +} + +std::unordered_set TransformationStore::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_store.h b/third_party/spirv-tools/source/fuzz/transformation_store.h new file mode 100644 index 0000000..7052048 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_store.h @@ -0,0 +1,67 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_STORE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_STORE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationStore : public Transformation { + public: + explicit TransformationStore(const protobufs::TransformationStore& message); + + TransformationStore( + uint32_t pointer_id, uint32_t value_id, + const protobufs::InstructionDescriptor& instruction_to_insert_before); + + // - |message_.pointer_id| must be the id of a pointer + // - The pointer type must not have read-only storage class + // - The pointer must not be OpConstantNull or OpUndef + // - |message_.value_id| must be an instruction result id that has the same + // type as the pointee type of |message_.pointer_id| + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is valid to insert an OpStore, and where both + // |message_.pointer_id| and |message_.value_id| are available (according + // to dominance rules) + // - Either the insertion point must be in a dead block, or it must be known + // that the pointee value of |message_.pointer_id| is irrelevant + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Adds an instruction of the form: + // OpStore |pointer_id| |value_id| + // before the instruction identified by + // |message_.instruction_to_insert_before|. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationStore message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_STORE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp b/third_party/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp new file mode 100644 index 0000000..b8bdb79 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_swap_commutable_operands.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_commutable_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( + const spvtools::fuzz::protobufs::TransformationSwapCommutableOperands& + message) + : message_(message) {} + +TransformationSwapCommutableOperands::TransformationSwapCommutableOperands( + const protobufs::InstructionDescriptor& instruction_descriptor) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationSwapCommutableOperands::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (instruction == nullptr) return false; + + SpvOp opcode = static_cast( + message_.instruction_descriptor().target_instruction_opcode()); + assert(instruction->opcode() == opcode && + "The located instruction must have the same opcode as in the " + "descriptor."); + return spvOpcodeIsCommutativeBinaryOperator(opcode); +} + +void TransformationSwapCommutableOperands::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + // By design, the instructions defined to be commutative have exactly two + // input parameters. + std::swap(instruction->GetInOperand(0), instruction->GetInOperand(1)); +} + +protobufs::Transformation TransformationSwapCommutableOperands::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_swap_commutable_operands() = message_; + return result; +} + +std::unordered_set TransformationSwapCommutableOperands::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_swap_commutable_operands.h b/third_party/spirv-tools/source/fuzz/transformation_swap_commutable_operands.h new file mode 100644 index 0000000..c291c3e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_swap_commutable_operands.h @@ -0,0 +1,55 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSwapCommutableOperands : public Transformation { + public: + explicit TransformationSwapCommutableOperands( + const protobufs::TransformationSwapCommutableOperands& message); + + TransformationSwapCommutableOperands( + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.instruction_descriptor| must identify an existing + // commutative instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Swaps the commutable operands. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSwapCommutableOperands message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_COMMUTABLE_OPERANDS_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp b/third_party/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp new file mode 100644 index 0000000..866fee6 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_conditional_branch_operands.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationSwapConditionalBranchOperands:: + TransformationSwapConditionalBranchOperands( + const spvtools::fuzz::protobufs:: + TransformationSwapConditionalBranchOperands& message) + : message_(message) {} + +TransformationSwapConditionalBranchOperands:: + TransformationSwapConditionalBranchOperands( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; + message_.set_fresh_id(fresh_id); +} + +bool TransformationSwapConditionalBranchOperands::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + const auto* inst = + FindInstruction(message_.instruction_descriptor(), ir_context); + return fuzzerutil::IsFreshId(ir_context, message_.fresh_id()) && inst && + inst->opcode() == SpvOpBranchConditional; +} + +void TransformationSwapConditionalBranchOperands::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto* branch_inst = + FindInstruction(message_.instruction_descriptor(), ir_context); + assert(branch_inst); + + auto* block = ir_context->get_instr_block(branch_inst); + assert(block); + + // Compute the last instruction in the |block| that allows us to insert + // OpLogicalNot above it. + auto iter = fuzzerutil::GetIteratorForInstruction(block, branch_inst); + if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter)) { + // There might be a merge instruction before OpBranchConditional. + --iter; + } + + assert(fuzzerutil::CanInsertOpcodeBeforeInstruction(SpvOpLogicalNot, iter) && + "We should now be able to insert SpvOpLogicalNot before |iter|"); + + // Get the instruction whose result is used as a condition for + // OpBranchConditional. + const auto* condition_inst = ir_context->get_def_use_mgr()->GetDef( + branch_inst->GetSingleWordInOperand(0)); + assert(condition_inst); + + // We are swapping the labels in OpBranchConditional. This means that we must + // invert the guard as well. We are using OpLogicalNot for that purpose here. + iter.InsertBefore(MakeUnique( + ir_context, SpvOpLogicalNot, condition_inst->type_id(), + message_.fresh_id(), + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {condition_inst->result_id()}}})); + + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + + // Update OpBranchConditional condition operand. + branch_inst->GetInOperand(0).words[0] = message_.fresh_id(); + + // Swap label operands. + std::swap(branch_inst->GetInOperand(1), branch_inst->GetInOperand(2)); + + // Additionally, swap branch weights if present. + if (branch_inst->NumInOperands() > 3) { + std::swap(branch_inst->GetInOperand(3), branch_inst->GetInOperand(4)); + } + + // Make sure the changes are analyzed. + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +protobufs::Transformation +TransformationSwapConditionalBranchOperands::ToMessage() const { + protobufs::Transformation result; + *result.mutable_swap_conditional_branch_operands() = message_; + return result; +} + +std::unordered_set +TransformationSwapConditionalBranchOperands::GetFreshIds() const { + return {message_.fresh_id()}; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h b/third_party/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h new file mode 100644 index 0000000..022c54a --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_swap_conditional_branch_operands.h @@ -0,0 +1,60 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_ +#define SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationSwapConditionalBranchOperands : public Transformation { + public: + explicit TransformationSwapConditionalBranchOperands( + const protobufs::TransformationSwapConditionalBranchOperands& message); + + TransformationSwapConditionalBranchOperands( + const protobufs::InstructionDescriptor& instruction_descriptor, + uint32_t fresh_id); + + // - |message_.instruction_descriptor| must be a valid descriptor of some + // OpBranchConditional instruction in the module. + // - |message_.fresh_id| must be a fresh id. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts |%fresh_id = OpLogicalNot %bool_type_id %cond_id| before + // |OpBranchConditional %cond_id %branch_a %branch_b [%weight_a %weight_b]|. + // Replaces %cond_id with %fresh_id and swaps %branch_* and %weight_* + // operands. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationSwapConditionalBranchOperands message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_SWAP_CONDITIONAL_BRANCH_OPERANDS_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp b/third_party/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp new file mode 100644 index 0000000..1952a34 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationToggleAccessChainInstruction:: + TransformationToggleAccessChainInstruction( + const spvtools::fuzz::protobufs:: + TransformationToggleAccessChainInstruction& message) + : message_(message) {} + +TransformationToggleAccessChainInstruction:: + TransformationToggleAccessChainInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor) { + *message_.mutable_instruction_descriptor() = instruction_descriptor; +} + +bool TransformationToggleAccessChainInstruction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + if (instruction == nullptr) { + return false; + } + + SpvOp opcode = static_cast( + message_.instruction_descriptor().target_instruction_opcode()); + + assert(instruction->opcode() == opcode && + "The located instruction must have the same opcode as in the " + "descriptor."); + + if (opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain) { + return true; + } + + return false; +} + +void TransformationToggleAccessChainInstruction::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + auto instruction = + FindInstruction(message_.instruction_descriptor(), ir_context); + SpvOp opcode = instruction->opcode(); + + if (opcode == SpvOpAccessChain) { + instruction->SetOpcode(SpvOpInBoundsAccessChain); + } else { + assert(opcode == SpvOpInBoundsAccessChain && + "The located instruction must be an OpInBoundsAccessChain " + "instruction."); + instruction->SetOpcode(SpvOpAccessChain); + } +} + +protobufs::Transformation +TransformationToggleAccessChainInstruction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_toggle_access_chain_instruction() = message_; + return result; +} + +std::unordered_set +TransformationToggleAccessChainInstruction::GetFreshIds() const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.h b/third_party/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.h new file mode 100644 index 0000000..977b0d7 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_toggle_access_chain_instruction.h @@ -0,0 +1,55 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationToggleAccessChainInstruction : public Transformation { + public: + explicit TransformationToggleAccessChainInstruction( + const protobufs::TransformationToggleAccessChainInstruction& message); + + TransformationToggleAccessChainInstruction( + const protobufs::InstructionDescriptor& instruction_descriptor); + + // - |message_.instruction_descriptor| must identify an existing + // access chain instruction + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Toggles the access chain instruction. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + protobufs::TransformationToggleAccessChainInstruction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_TOGGLE_ACCESS_CHAIN_INSTRUCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp b/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp new file mode 100644 index 0000000..05af18e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.cpp @@ -0,0 +1,233 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_vector_shuffle.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" + +namespace spvtools { +namespace fuzz { + +TransformationVectorShuffle::TransformationVectorShuffle( + const spvtools::fuzz::protobufs::TransformationVectorShuffle& message) + : message_(message) {} + +TransformationVectorShuffle::TransformationVectorShuffle( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t vector1, uint32_t vector2, + const std::vector& component) { + *message_.mutable_instruction_to_insert_before() = + instruction_to_insert_before; + message_.set_fresh_id(fresh_id); + message_.set_vector1(vector1); + message_.set_vector2(vector2); + for (auto a_component : component) { + message_.add_component(a_component); + } +} + +bool TransformationVectorShuffle::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The fresh id must not already be in use. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + // The instruction before which the shuffle will be inserted must exist. + auto instruction_to_insert_before = + FindInstruction(message_.instruction_to_insert_before(), ir_context); + if (!instruction_to_insert_before) { + return false; + } + // The first vector must be an instruction with a type id + auto vector1_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.vector1()); + if (!vector1_instruction || !vector1_instruction->type_id()) { + return false; + } + // The second vector must be an instruction with a type id + auto vector2_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.vector2()); + if (!vector2_instruction || !vector2_instruction->type_id()) { + return false; + } + auto vector1_type = + ir_context->get_type_mgr()->GetType(vector1_instruction->type_id()); + // The first vector instruction's type must actually be a vector type. + if (!vector1_type->AsVector()) { + return false; + } + auto vector2_type = + ir_context->get_type_mgr()->GetType(vector2_instruction->type_id()); + // The second vector instruction's type must actually be a vector type. + if (!vector2_type->AsVector()) { + return false; + } + // The element types of the vectors must be the same. + if (vector1_type->AsVector()->element_type() != + vector2_type->AsVector()->element_type()) { + return false; + } + uint32_t combined_size = vector1_type->AsVector()->element_count() + + vector2_type->AsVector()->element_count(); + for (auto a_compoment : message_.component()) { + // 0xFFFFFFFF is used to represent an undefined component. Unless + // undefined, a component must be less than the combined size of the + // vectors. + if (a_compoment != 0xFFFFFFFF && a_compoment >= combined_size) { + return false; + } + } + // The module must already declare an appropriate type in which to store the + // result of the shuffle. + if (!GetResultTypeId(ir_context, *vector1_type->AsVector()->element_type())) { + return false; + } + // Each of the vectors used in the shuffle must be available at the insertion + // point. + for (auto used_instruction : {vector1_instruction, vector2_instruction}) { + if (auto block = ir_context->get_instr_block(used_instruction)) { + if (!ir_context->GetDominatorAnalysis(block->GetParent()) + ->Dominates(used_instruction, instruction_to_insert_before)) { + return false; + } + } + } + + // It must be legitimate to insert an OpVectorShuffle before the identified + // instruction. + return fuzzerutil::CanInsertOpcodeBeforeInstruction( + SpvOpVectorShuffle, instruction_to_insert_before); +} + +void TransformationVectorShuffle::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // Make input operands for a shuffle instruction - these comprise the two + // vectors being shuffled, followed by the integer literal components. + opt::Instruction::OperandList shuffle_operands = { + {SPV_OPERAND_TYPE_ID, {message_.vector1()}}, + {SPV_OPERAND_TYPE_ID, {message_.vector2()}}}; + for (auto a_component : message_.component()) { + shuffle_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {a_component}}); + } + + uint32_t result_type_id = GetResultTypeId( + ir_context, + *GetVectorType(ir_context, message_.vector1())->element_type()); + + // Add a shuffle instruction right before the instruction identified by + // |message_.instruction_to_insert_before|. + FindInstruction(message_.instruction_to_insert_before(), ir_context) + ->InsertBefore(MakeUnique( + ir_context, SpvOpVectorShuffle, result_type_id, message_.fresh_id(), + shuffle_operands)); + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + ir_context->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + AddDataSynonymFacts(ir_context, transformation_context); +} + +protobufs::Transformation TransformationVectorShuffle::ToMessage() const { + protobufs::Transformation result; + *result.mutable_vector_shuffle() = message_; + return result; +} + +uint32_t TransformationVectorShuffle::GetResultTypeId( + opt::IRContext* ir_context, const opt::analysis::Type& element_type) const { + opt::analysis::Vector result_type( + &element_type, static_cast(message_.component_size())); + return ir_context->get_type_mgr()->GetId(&result_type); +} + +opt::analysis::Vector* TransformationVectorShuffle::GetVectorType( + opt::IRContext* ir_context, uint32_t id_of_vector) { + return ir_context->get_type_mgr() + ->GetType(ir_context->get_def_use_mgr()->GetDef(id_of_vector)->type_id()) + ->AsVector(); +} + +std::unordered_set TransformationVectorShuffle::GetFreshIds() const { + return {message_.fresh_id()}; +} + +void TransformationVectorShuffle::AddDataSynonymFacts( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + // If the new instruction is irrelevant (because it is in a dead block), it + // cannot participate in any DataSynonym fact. + if (transformation_context->GetFactManager()->IdIsIrrelevant( + message_.fresh_id())) { + return; + } + + // Add synonym facts relating the defined elements of the shuffle result to + // the vector components that they come from. + for (uint32_t component_index = 0; + component_index < static_cast(message_.component_size()); + component_index++) { + uint32_t component = message_.component(component_index); + if (component == 0xFFFFFFFF) { + // This component is undefined, we do not introduce a synonym. + continue; + } + // This describes the element of the result vector associated with + // |component_index|. + protobufs::DataDescriptor descriptor_for_result_component = + MakeDataDescriptor(message_.fresh_id(), {component_index}); + + protobufs::DataDescriptor descriptor_for_source_component; + + // Get a data descriptor for the component of the input vector to which + // |component| refers. + if (component < + GetVectorType(ir_context, message_.vector1())->element_count()) { + // Check that the first vector can participate in data synonym facts. + if (!fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + ir_context->get_def_use_mgr()->GetDef(message_.vector1()))) { + continue; + } + descriptor_for_source_component = + MakeDataDescriptor(message_.vector1(), {component}); + } else { + // Check that the second vector can participate in data synonym facts. + if (!fuzzerutil::CanMakeSynonymOf( + ir_context, *transformation_context, + ir_context->get_def_use_mgr()->GetDef(message_.vector2()))) { + continue; + } + auto index_into_vector_2 = + component - + GetVectorType(ir_context, message_.vector1())->element_count(); + assert( + index_into_vector_2 < + GetVectorType(ir_context, message_.vector2())->element_count() && + "Vector shuffle index is out of bounds."); + descriptor_for_source_component = + MakeDataDescriptor(message_.vector2(), {index_into_vector_2}); + } + + // Add a fact relating this input vector component with the associated + // result component. + transformation_context->GetFactManager()->AddFactDataSynonym( + descriptor_for_result_component, descriptor_for_source_component); + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.h b/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.h new file mode 100644 index 0000000..cf08a62 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_vector_shuffle.h @@ -0,0 +1,95 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_ +#define SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace fuzz { + +class TransformationVectorShuffle : public Transformation { + public: + explicit TransformationVectorShuffle( + const protobufs::TransformationVectorShuffle& message); + + TransformationVectorShuffle( + const protobufs::InstructionDescriptor& instruction_to_insert_before, + uint32_t fresh_id, uint32_t vector1, uint32_t vector2, + const std::vector& component); + + // - |message_.fresh_id| must not be in use + // - |message_.instruction_to_insert_before| must identify an instruction + // before which it is legitimate to insert an OpVectorShuffle + // - |message_.vector1| and |message_.vector2| must be instructions of vector + // type, and the element types of these vectors must be the same + // - Each element of |message_.component| must either be 0xFFFFFFFF + // (representing an undefined component), or must be less than the combined + // sizes of the input vectors + // - The module must already contain a vector type with the same element type + // as |message_.vector1| and |message_.vector2|, and with the size of + // |message_component| as its element count + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // Inserts an OpVectorShuffle instruction before + // |message_.instruction_to_insert_before|, shuffles vectors + // |message_.vector1| and |message_.vector2| using the indices provided by + // |message_.component|, into |message_.fresh_id|. + // + // If |message_.fresh_id| is irrelevant (e.g. due to being in a dead block) + // of if one of |message_.vector1| or |message_.vector2| is irrelevant and the + // shuffle reads components from the irrelevant vector then no synonym facts + // are added. + // + // Otherwise, a fact is added recording that element of |message_.fresh_id| is + // synonymous with the element of |message_.vector1| or |message_.vector2| + // from which it came (with undefined components being ignored). + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + private: + // Returns a type id that already exists in |ir_context| suitable for + // representing the result of the shuffle, where |element_type| is known to + // be the common element type of the vectors to which the shuffle is being + // applied. Returns 0 if no such id exists. + uint32_t GetResultTypeId(opt::IRContext* ir_context, + const opt::analysis::Type& element_type) const; + + // Returns the type associated with |id_of_vector| in |ir_context|. + static opt::analysis::Vector* GetVectorType(opt::IRContext* ir_context, + uint32_t id_of_vector); + + // Helper method for adding data synonym facts when applying the + // transformation to |ir_context| and |transformation_context|. + void AddDataSynonymFacts(opt::IRContext* ir_context, + TransformationContext* transformation_context) const; + + protobufs::TransformationVectorShuffle message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_VECTOR_SHUFFLE_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.cpp b/third_party/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.cpp new file mode 100644 index 0000000..4c436f5 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_early_terminator_in_function.h" + +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace fuzz { + +TransformationWrapEarlyTerminatorInFunction:: + TransformationWrapEarlyTerminatorInFunction( + const spvtools::fuzz::protobufs:: + TransformationWrapEarlyTerminatorInFunction& message) + : message_(message) {} + +TransformationWrapEarlyTerminatorInFunction:: + TransformationWrapEarlyTerminatorInFunction( + uint32_t fresh_id, + const protobufs::InstructionDescriptor& early_terminator_instruction, + uint32_t returned_value_id) { + message_.set_fresh_id(fresh_id); + *message_.mutable_early_terminator_instruction() = + early_terminator_instruction; + message_.set_returned_value_id(returned_value_id); +} + +bool TransformationWrapEarlyTerminatorInFunction::IsApplicable( + opt::IRContext* ir_context, const TransformationContext& /*unused*/) const { + // The given id must be fresh. + if (!fuzzerutil::IsFreshId(ir_context, message_.fresh_id())) { + return false; + } + + // |message_.early_terminator_instruction| must identify an instruction, and + // the instruction must indeed be an early terminator. + auto early_terminator = + FindInstruction(message_.early_terminator_instruction(), ir_context); + if (!early_terminator) { + return false; + } + switch (early_terminator->opcode()) { + case SpvOpKill: + case SpvOpUnreachable: + case SpvOpTerminateInvocation: + break; + default: + return false; + } + // A wrapper function for the early terminator must exist. + auto wrapper_function = + MaybeGetWrapperFunction(ir_context, early_terminator->opcode()); + if (wrapper_function == nullptr) { + return false; + } + auto enclosing_function = + ir_context->get_instr_block(early_terminator)->GetParent(); + // The wrapper function cannot be the function containing the instruction we + // would like to wrap. + if (wrapper_function->result_id() == enclosing_function->result_id()) { + return false; + } + if (!ir_context->get_type_mgr() + ->GetType(enclosing_function->type_id()) + ->AsVoid()) { + // The enclosing function has non-void return type. We thus need to make + // sure that |message_.returned_value_instruction| provides a suitable + // result id to use in an OpReturnValue instruction. + auto returned_value_instruction = + ir_context->get_def_use_mgr()->GetDef(message_.returned_value_id()); + if (!returned_value_instruction || !returned_value_instruction->type_id() || + returned_value_instruction->type_id() != + enclosing_function->type_id()) { + return false; + } + if (!fuzzerutil::IdIsAvailableBeforeInstruction( + ir_context, early_terminator, message_.returned_value_id())) { + return false; + } + } + return true; +} + +void TransformationWrapEarlyTerminatorInFunction::Apply( + opt::IRContext* ir_context, TransformationContext* /*unused*/) const { + fuzzerutil::UpdateModuleIdBound(ir_context, message_.fresh_id()); + auto early_terminator = + FindInstruction(message_.early_terminator_instruction(), ir_context); + auto enclosing_block = ir_context->get_instr_block(early_terminator); + auto enclosing_function = enclosing_block->GetParent(); + + // We would like to add an OpFunctionCall before the block's terminator + // instruction, and then change the block's terminator to OpReturn or + // OpReturnValue. + + // We get an iterator to the instruction we would like to insert the function + // call before. It will be an iterator to the final instruction in the block + // unless the block is a merge block in which case it will be to the + // penultimate instruction (because we cannot insert an OpFunctionCall after + // a merge instruction). + auto iterator = enclosing_block->tail(); + if (enclosing_block->MergeBlockIdIfAny()) { + --iterator; + } + + auto wrapper_function = + MaybeGetWrapperFunction(ir_context, early_terminator->opcode()); + + iterator->InsertBefore(MakeUnique( + ir_context, SpvOpFunctionCall, wrapper_function->type_id(), + message_.fresh_id(), + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_ID, {wrapper_function->result_id()}}}))); + + opt::Instruction::OperandList new_in_operands; + if (!ir_context->get_type_mgr() + ->GetType(enclosing_function->type_id()) + ->AsVoid()) { + new_in_operands.push_back( + {SPV_OPERAND_TYPE_ID, {message_.returned_value_id()}}); + early_terminator->SetOpcode(SpvOpReturnValue); + } else { + early_terminator->SetOpcode(SpvOpReturn); + } + early_terminator->SetInOperands(std::move(new_in_operands)); + + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +std::unordered_set +TransformationWrapEarlyTerminatorInFunction::GetFreshIds() const { + return std::unordered_set({message_.fresh_id()}); +} + +protobufs::Transformation +TransformationWrapEarlyTerminatorInFunction::ToMessage() const { + protobufs::Transformation result; + *result.mutable_wrap_early_terminator_in_function() = message_; + return result; +} + +opt::Function* +TransformationWrapEarlyTerminatorInFunction::MaybeGetWrapperFunction( + opt::IRContext* ir_context, SpvOp early_terminator_opcode) { + assert((early_terminator_opcode == SpvOpKill || + early_terminator_opcode == SpvOpUnreachable || + early_terminator_opcode == SpvOpTerminateInvocation) && + "Invalid opcode."); + auto void_type_id = fuzzerutil::MaybeGetVoidType(ir_context); + if (!void_type_id) { + return nullptr; + } + auto void_function_type_id = + fuzzerutil::FindFunctionType(ir_context, {void_type_id}); + if (!void_function_type_id) { + return nullptr; + } + for (auto& function : *ir_context->module()) { + if (function.DefInst().GetSingleWordInOperand(1) != void_function_type_id) { + continue; + } + if (function.begin()->begin()->opcode() == early_terminator_opcode) { + return &function; + } + } + return nullptr; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.h b/third_party/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.h new file mode 100644 index 0000000..00151d4 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_wrap_early_terminator_in_function.h @@ -0,0 +1,73 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationWrapEarlyTerminatorInFunction : public Transformation { + public: + explicit TransformationWrapEarlyTerminatorInFunction( + const protobufs::TransformationWrapEarlyTerminatorInFunction& message); + + TransformationWrapEarlyTerminatorInFunction( + uint32_t fresh_id, + const protobufs::InstructionDescriptor& early_terminator_instruction, + uint32_t returned_value_id); + + // - |message_.fresh_id| must be fresh. + // - |message_.early_terminator_instruction| must identify an early terminator + // instruction, i.e. an instruction with opcode OpKill, OpUnreachable or + // OpTerminateInvocation. + // - A suitable wrapper function for the early terminator must exist, and it + // must be distinct from the function containing + // |message_.early_terminator_instruction|. + // - If the enclosing function has non-void return type then + // |message_.returned_value_instruction| must be the id of an instruction of + // the return type that is available at the point of the early terminator + // instruction. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // An OpFunctionCall instruction to an appropriate wrapper function is + // inserted before |message_.early_terminator_instruction|, and + // |message_.early_terminator_instruction| is replaced with either OpReturn + // or OpReturnValue |message_.returned_value_instruction| depending on whether + // the enclosing function's return type is void. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + std::unordered_set GetFreshIds() const override; + + protobufs::Transformation ToMessage() const override; + + static opt::Function* MaybeGetWrapperFunction(opt::IRContext* ir_context, + SpvOp early_terminator_opcode); + + private: + protobufs::TransformationWrapEarlyTerminatorInFunction message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_EARLY_TERMINATOR_IN_FUNCTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.cpp b/third_party/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.cpp new file mode 100644 index 0000000..9924e36 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.cpp @@ -0,0 +1,166 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_region_in_selection.h" + +#include "source/fuzz/fuzzer_util.h" + +namespace spvtools { +namespace fuzz { + +TransformationWrapRegionInSelection::TransformationWrapRegionInSelection( + const protobufs::TransformationWrapRegionInSelection& message) + : message_(message) {} + +TransformationWrapRegionInSelection::TransformationWrapRegionInSelection( + uint32_t region_entry_block_id, uint32_t region_exit_block_id, + bool branch_condition) { + message_.set_region_entry_block_id(region_entry_block_id); + message_.set_region_exit_block_id(region_exit_block_id); + message_.set_branch_condition(branch_condition); +} + +bool TransformationWrapRegionInSelection::IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const { + // Check that it is possible to outline a region of blocks without breaking + // domination and structured control flow rules. + if (!IsApplicableToBlockRange(ir_context, message_.region_entry_block_id(), + message_.region_exit_block_id())) { + return false; + } + + // There must exist an irrelevant boolean constant to be used as a condition + // in the OpBranchConditional instruction. + return fuzzerutil::MaybeGetBoolConstant(ir_context, transformation_context, + message_.branch_condition(), + true) != 0; +} + +void TransformationWrapRegionInSelection::Apply( + opt::IRContext* ir_context, + TransformationContext* transformation_context) const { + auto* new_header_block = + ir_context->cfg()->block(message_.region_entry_block_id()); + assert(new_header_block->terminator()->opcode() == SpvOpBranch && + "This condition should have been checked in the IsApplicable"); + + const auto successor_id = + new_header_block->terminator()->GetSingleWordInOperand(0); + + // Change |entry_block|'s terminator to |OpBranchConditional|. + new_header_block->terminator()->SetOpcode(SpvOpBranchConditional); + new_header_block->terminator()->SetInOperands( + {{SPV_OPERAND_TYPE_ID, + {fuzzerutil::MaybeGetBoolConstant(ir_context, *transformation_context, + message_.branch_condition(), true)}}, + {SPV_OPERAND_TYPE_ID, {successor_id}}, + {SPV_OPERAND_TYPE_ID, {successor_id}}}); + + // Insert OpSelectionMerge before the terminator. + new_header_block->terminator()->InsertBefore(MakeUnique( + ir_context, SpvOpSelectionMerge, 0, 0, + opt::Instruction::OperandList{ + {SPV_OPERAND_TYPE_ID, {message_.region_exit_block_id()}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, + {SpvSelectionControlMaskNone}}})); + + // We've change the module so we must invalidate analyses. + ir_context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); +} + +protobufs::Transformation TransformationWrapRegionInSelection::ToMessage() + const { + protobufs::Transformation result; + *result.mutable_wrap_region_in_selection() = message_; + return result; +} + +bool TransformationWrapRegionInSelection::IsApplicableToBlockRange( + opt::IRContext* ir_context, uint32_t header_block_candidate_id, + uint32_t merge_block_candidate_id) { + // Check that |header_block_candidate_id| and |merge_block_candidate_id| are + // valid. + const auto* header_block_candidate = + fuzzerutil::MaybeFindBlock(ir_context, header_block_candidate_id); + if (!header_block_candidate) { + return false; + } + + const auto* merge_block_candidate = + fuzzerutil::MaybeFindBlock(ir_context, merge_block_candidate_id); + if (!merge_block_candidate) { + return false; + } + + // |header_block_candidate| and |merge_block_candidate| must be from the same + // function. + if (header_block_candidate->GetParent() != + merge_block_candidate->GetParent()) { + return false; + } + + const auto* dominator_analysis = + ir_context->GetDominatorAnalysis(header_block_candidate->GetParent()); + const auto* postdominator_analysis = + ir_context->GetPostDominatorAnalysis(header_block_candidate->GetParent()); + + if (!dominator_analysis->StrictlyDominates(header_block_candidate, + merge_block_candidate) || + !postdominator_analysis->StrictlyDominates(merge_block_candidate, + header_block_candidate)) { + return false; + } + + // |header_block_candidate| can't be a header since we are about to make it + // one. + if (header_block_candidate->GetMergeInst()) { + return false; + } + + // |header_block_candidate| must have an OpBranch terminator. + if (header_block_candidate->terminator()->opcode() != SpvOpBranch) { + return false; + } + + // Every header block must have a unique merge block. Thus, + // |merge_block_candidate| can't be a merge block of some other header. + auto* structured_cfg = ir_context->GetStructuredCFGAnalysis(); + if (structured_cfg->IsMergeBlock(merge_block_candidate_id)) { + return false; + } + + // |header_block_candidate|'s containing construct must also contain + // |merge_block_candidate|. + // + // ContainingConstruct will return the id of a loop header for a block in the + // loop's continue construct. Thus, we must also check the case when one of + // the candidates is in continue construct and the other one is not. + if (structured_cfg->ContainingConstruct(header_block_candidate_id) != + structured_cfg->ContainingConstruct(merge_block_candidate_id) || + structured_cfg->IsInContinueConstruct(header_block_candidate_id) != + structured_cfg->IsInContinueConstruct(merge_block_candidate_id)) { + return false; + } + + return true; +} + +std::unordered_set TransformationWrapRegionInSelection::GetFreshIds() + const { + return std::unordered_set(); +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.h b/third_party/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.h new file mode 100644 index 0000000..57f4f64 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/transformation_wrap_region_in_selection.h @@ -0,0 +1,88 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_TRANSFORMATION_WRAP_REGION_IN_SELECTION_H_ +#define SOURCE_FUZZ_TRANSFORMATION_WRAP_REGION_IN_SELECTION_H_ + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +class TransformationWrapRegionInSelection : public Transformation { + public: + explicit TransformationWrapRegionInSelection( + const protobufs::TransformationWrapRegionInSelection& message); + + TransformationWrapRegionInSelection(uint32_t region_entry_block_id, + uint32_t region_exit_block_id, + bool branch_condition); + + // - It should be possible to apply this transformation to a + // single-exit-single-entry region of blocks dominated by + // |region_entry_block_id| and postdominated by |region_exit_block_id| + // (see IsApplicableToBlockRange method for further details). + // + // TODO(https://github.com/KhronosGroup/SPIRV-Tools/issues/3828): + // Consider applying this transformation to non-single-entry-single-exit + // regions of blocks. + // - There must exist an irrelevant boolean constant with value + // |branch_condition|. + bool IsApplicable( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) const override; + + // - Transforms |region_entry_block_id| into a selection header with both + // branches pointing to the block's successor. + // - |branch_condition| is used as a condition in the header's + // OpBranchConditional instruction. + // - Transforms |region_exit_block_id| into a merge block of the selection's + // header. + void Apply(opt::IRContext* ir_context, + TransformationContext* transformation_context) const override; + + protobufs::Transformation ToMessage() const override; + + // Returns true if it's possible to apply this transformation to the + // single-exit-single-entry region of blocks starting with + // |header_block_candidate_id| and ending with |merge_block_candidate_id|. + // Concretely: + // - Both |header_block_candidate_id| and |merge_block_candidate_id| must be + // result ids of some blocks in the module. + // - Both blocks must belong to the same function. + // - |header_block_candidate_id| must strictly dominate + // |merge_block_candidate_id| and |merge_block_candidate_id| must strictly + // postdominate |header_block_candidate_id|. + // - |header_block_candidate_id| can't be a header block of any construct. + // - |header_block_candidate_id|'s terminator must be an OpBranch. + // - |merge_block_candidate_id| can't be a merge block of any other construct. + // - Both |header_block_candidate_id| and |merge_block_candidate_id| must be + // inside the same construct if any. + static bool IsApplicableToBlockRange(opt::IRContext* ir_context, + uint32_t header_block_candidate_id, + uint32_t merge_block_candidate_id); + + std::unordered_set GetFreshIds() const override; + + private: + protobufs::TransformationWrapRegionInSelection message_; +}; + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_TRANSFORMATION_WRAP_REGION_IN_SELECTION_H_ diff --git a/third_party/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp b/third_party/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp new file mode 100644 index 0000000..90fd85e --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/uniform_buffer_element_descriptor.h" + +#include + +namespace spvtools { +namespace fuzz { + +protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor( + uint32_t descriptor_set, uint32_t binding, + std::vector&& indices) { + protobufs::UniformBufferElementDescriptor result; + result.set_descriptor_set(descriptor_set); + result.set_binding(binding); + for (auto index : indices) { + result.add_index(index); + } + return result; +} + +bool UniformBufferElementDescriptorEquals::operator()( + const protobufs::UniformBufferElementDescriptor* first, + const protobufs::UniformBufferElementDescriptor* second) const { + return first->descriptor_set() == second->descriptor_set() && + first->binding() == second->binding() && + first->index().size() == second->index().size() && + std::equal(first->index().begin(), first->index().end(), + second->index().begin()); +} + +opt::Instruction* FindUniformVariable( + const protobufs::UniformBufferElementDescriptor& + uniform_buffer_element_descriptor, + opt::IRContext* context, bool check_unique) { + opt::Instruction* result = nullptr; + + for (auto& inst : context->types_values()) { + // Consider all global variables with uniform storage class. + if (inst.opcode() != SpvOpVariable) { + continue; + } + if (inst.GetSingleWordInOperand(0) != SpvStorageClassUniform) { + continue; + } + + // Determine whether the variable is decorated with a descriptor set + // matching that in |uniform_buffer_element|. + bool descriptor_set_matches = false; + context->get_decoration_mgr()->ForEachDecoration( + inst.result_id(), SpvDecorationDescriptorSet, + [&descriptor_set_matches, &uniform_buffer_element_descriptor]( + const opt::Instruction& decoration_inst) { + const uint32_t kDescriptorSetOperandIndex = 2; + if (decoration_inst.GetSingleWordInOperand( + kDescriptorSetOperandIndex) == + uniform_buffer_element_descriptor.descriptor_set()) { + descriptor_set_matches = true; + } + }); + if (!descriptor_set_matches) { + // Descriptor set does not match. + continue; + } + + // Determine whether the variable is decorated with a binding matching that + // in |uniform_buffer_element|. + bool binding_matches = false; + context->get_decoration_mgr()->ForEachDecoration( + inst.result_id(), SpvDecorationBinding, + [&binding_matches, &uniform_buffer_element_descriptor]( + const opt::Instruction& decoration_inst) { + const uint32_t kBindingOperandIndex = 2; + if (decoration_inst.GetSingleWordInOperand(kBindingOperandIndex) == + uniform_buffer_element_descriptor.binding()) { + binding_matches = true; + } + }); + if (!binding_matches) { + // Binding does not match. + continue; + } + + // This instruction is a uniform variable with the right descriptor set and + // binding. + if (!check_unique) { + // If we aren't checking uniqueness, return it. + return &inst; + } + + if (result) { + // More than one uniform variable is decorated with the given descriptor + // set and binding. This means the fact is ambiguous. + return nullptr; + } + result = &inst; + } + + // We get here either if no match was found, or if |check_unique| holds and + // exactly one match was found. + assert(result == nullptr || check_unique); + return result; +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h b/third_party/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h new file mode 100644 index 0000000..f5d7320 --- /dev/null +++ b/third_party/spirv-tools/source/fuzz/uniform_buffer_element_descriptor.h @@ -0,0 +1,52 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_ +#define SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace fuzz { + +// Factory method to create a uniform buffer element descriptor message from +// descriptor set and binding ids and a list of indices. +protobufs::UniformBufferElementDescriptor MakeUniformBufferElementDescriptor( + uint32_t descriptor_set, uint32_t binding, std::vector&& indices); + +// Equality function for uniform buffer element descriptors. +struct UniformBufferElementDescriptorEquals { + bool operator()( + const protobufs::UniformBufferElementDescriptor* first, + const protobufs::UniformBufferElementDescriptor* second) const; +}; + +// Returns a pointer to an OpVariable in |context| that is decorated with the +// descriptor set and binding associated with |uniform_buffer_element|. Returns +// nullptr if no such variable exists. If multiple such variables exist, a +// pointer to an arbitrary one of the associated instructions is returned if +// |check_unique| is false, and nullptr is returned if |check_unique| is true. +opt::Instruction* FindUniformVariable( + const protobufs::UniformBufferElementDescriptor& + uniform_buffer_element_descriptor, + opt::IRContext* context, bool check_unique); + +} // namespace fuzz +} // namespace spvtools + +#endif // SOURCE_FUZZ_UNIFORM_BUFFER_ELEMENT_DESCRIPTOR_H_ diff --git a/third_party/spirv-tools/source/instruction.h b/third_party/spirv-tools/source/instruction.h new file mode 100644 index 0000000..9e7dccd --- /dev/null +++ b/third_party/spirv-tools/source/instruction.h @@ -0,0 +1,49 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_INSTRUCTION_H_ +#define SOURCE_INSTRUCTION_H_ + +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "spirv-tools/libspirv.h" + +// Describes an instruction. +struct spv_instruction_t { + // Normally, both opcode and extInstType contain valid data. + // However, when the assembler parses ! as the first word in + // an instruction and opcode and extInstType are invalid. + SpvOp opcode; + spv_ext_inst_type_t extInstType; + + // The Id of the result type, if this instruction has one. Zero otherwise. + uint32_t resultTypeId; + + // The instruction, as a sequence of 32-bit words. + // For a regular instruction the opcode and word count are combined + // in words[0], as described in the SPIR-V spec. + // Otherwise, the first token was !, and that number appears + // in words[0]. Subsequent elements are the result of parsing + // tokens in the alternate parsing mode as described in syntax.md. + std::vector words; +}; + +// Appends a word to an instruction, without checking for overflow. +inline void spvInstructionAddWord(spv_instruction_t* inst, uint32_t value) { + inst->words.push_back(value); +} + +#endif // SOURCE_INSTRUCTION_H_ diff --git a/third_party/spirv-tools/source/latest_version_glsl_std_450_header.h b/third_party/spirv-tools/source/latest_version_glsl_std_450_header.h new file mode 100644 index 0000000..bed1f25 --- /dev/null +++ b/third_party/spirv-tools/source/latest_version_glsl_std_450_header.h @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LATEST_VERSION_GLSL_STD_450_HEADER_H_ +#define SOURCE_LATEST_VERSION_GLSL_STD_450_HEADER_H_ + +#include "spirv/unified1/GLSL.std.450.h" + +#endif // SOURCE_LATEST_VERSION_GLSL_STD_450_HEADER_H_ diff --git a/third_party/spirv-tools/source/latest_version_opencl_std_header.h b/third_party/spirv-tools/source/latest_version_opencl_std_header.h new file mode 100644 index 0000000..90ff9c0 --- /dev/null +++ b/third_party/spirv-tools/source/latest_version_opencl_std_header.h @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LATEST_VERSION_OPENCL_STD_HEADER_H_ +#define SOURCE_LATEST_VERSION_OPENCL_STD_HEADER_H_ + +#include "spirv/unified1/OpenCL.std.h" + +#endif // SOURCE_LATEST_VERSION_OPENCL_STD_HEADER_H_ diff --git a/third_party/spirv-tools/source/latest_version_spirv_header.h b/third_party/spirv-tools/source/latest_version_spirv_header.h new file mode 100644 index 0000000..e4f28e4 --- /dev/null +++ b/third_party/spirv-tools/source/latest_version_spirv_header.h @@ -0,0 +1,20 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_LATEST_VERSION_SPIRV_HEADER_H_ +#define SOURCE_LATEST_VERSION_SPIRV_HEADER_H_ + +#include "spirv/unified1/spirv.h" + +#endif // SOURCE_LATEST_VERSION_SPIRV_HEADER_H_ diff --git a/third_party/spirv-tools/source/libspirv.cpp b/third_party/spirv-tools/source/libspirv.cpp new file mode 100644 index 0000000..a1ed11d --- /dev/null +++ b/third_party/spirv-tools/source/libspirv.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/libspirv.hpp" + +#include + +#include +#include +#include + +#include "source/table.h" + +namespace spvtools { + +Context::Context(spv_target_env env) : context_(spvContextCreate(env)) {} + +Context::Context(Context&& other) : context_(other.context_) { + other.context_ = nullptr; +} + +Context& Context::operator=(Context&& other) { + spvContextDestroy(context_); + context_ = other.context_; + other.context_ = nullptr; + + return *this; +} + +Context::~Context() { spvContextDestroy(context_); } + +void Context::SetMessageConsumer(MessageConsumer consumer) { + SetContextMessageConsumer(context_, std::move(consumer)); +} + +spv_context& Context::CContext() { return context_; } + +const spv_context& Context::CContext() const { return context_; } + +// Structs for holding the data members for SpvTools. +struct SpirvTools::Impl { + explicit Impl(spv_target_env env) : context(spvContextCreate(env)) { + // The default consumer in spv_context_t is a null consumer, which provides + // equivalent functionality (from the user's perspective) as a real consumer + // does nothing. + } + ~Impl() { spvContextDestroy(context); } + + spv_context context; // C interface context object. +}; + +SpirvTools::SpirvTools(spv_target_env env) : impl_(new Impl(env)) {} + +SpirvTools::~SpirvTools() {} + +void SpirvTools::SetMessageConsumer(MessageConsumer consumer) { + SetContextMessageConsumer(impl_->context, std::move(consumer)); +} + +bool SpirvTools::Assemble(const std::string& text, + std::vector* binary, + uint32_t options) const { + return Assemble(text.data(), text.size(), binary, options); +} + +bool SpirvTools::Assemble(const char* text, const size_t text_size, + std::vector* binary, + uint32_t options) const { + spv_binary spvbinary = nullptr; + spv_result_t status = spvTextToBinaryWithOptions( + impl_->context, text, text_size, options, &spvbinary, nullptr); + if (status == SPV_SUCCESS) { + binary->assign(spvbinary->code, spvbinary->code + spvbinary->wordCount); + } + spvBinaryDestroy(spvbinary); + return status == SPV_SUCCESS; +} + +bool SpirvTools::Disassemble(const std::vector& binary, + std::string* text, uint32_t options) const { + return Disassemble(binary.data(), binary.size(), text, options); +} + +bool SpirvTools::Disassemble(const uint32_t* binary, const size_t binary_size, + std::string* text, uint32_t options) const { + spv_text spvtext = nullptr; + spv_result_t status = spvBinaryToText(impl_->context, binary, binary_size, + options, &spvtext, nullptr); + if (status == SPV_SUCCESS) { + text->assign(spvtext->str, spvtext->str + spvtext->length); + } + spvTextDestroy(spvtext); + return status == SPV_SUCCESS; +} + +bool SpirvTools::Validate(const std::vector& binary) const { + return Validate(binary.data(), binary.size()); +} + +bool SpirvTools::Validate(const uint32_t* binary, + const size_t binary_size) const { + return spvValidateBinary(impl_->context, binary, binary_size, nullptr) == + SPV_SUCCESS; +} + +bool SpirvTools::Validate(const uint32_t* binary, const size_t binary_size, + spv_validator_options options) const { + spv_const_binary_t the_binary{binary, binary_size}; + spv_diagnostic diagnostic = nullptr; + bool valid = spvValidateWithOptions(impl_->context, options, &the_binary, + &diagnostic) == SPV_SUCCESS; + if (!valid && impl_->context->consumer) { + impl_->context->consumer.operator()( + SPV_MSG_ERROR, nullptr, diagnostic->position, diagnostic->error); + } + spvDiagnosticDestroy(diagnostic); + return valid; +} + +bool SpirvTools::IsValid() const { return impl_->context != nullptr; } + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/link/CMakeLists.txt b/third_party/spirv-tools/source/link/CMakeLists.txt new file mode 100644 index 0000000..c8dd2f7 --- /dev/null +++ b/third_party/spirv-tools/source/link/CMakeLists.txt @@ -0,0 +1,46 @@ +# Copyright (c) 2017 Pierre Moreau + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +add_library(SPIRV-Tools-link ${SPIRV_TOOLS_LIBRARY_TYPE} + linker.cpp +) + +spvtools_default_compile_options(SPIRV-Tools-link) +target_include_directories(SPIRV-Tools-link + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the IR functionnalities from the optimizer +target_link_libraries(SPIRV-Tools-link + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-link PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-link) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-link EXPORT SPIRV-Tools-linkTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-link PACKAGE_DIR) + install(EXPORT SPIRV-Tools-linkTargets FILE SPIRV-Tools-linkTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-link) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-linkConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/third_party/spirv-tools/source/link/linker.cpp b/third_party/spirv-tools/source/link/linker.cpp new file mode 100644 index 0000000..da6f0a7 --- /dev/null +++ b/third_party/spirv-tools/source/link/linker.cpp @@ -0,0 +1,767 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/linker.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/diagnostic.h" +#include "source/opt/build_module.h" +#include "source/opt/compact_ids_pass.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/ir_loader.h" +#include "source/opt/pass_manager.h" +#include "source/opt/remove_duplicates_pass.h" +#include "source/opt/type_manager.h" +#include "source/spirv_target_env.h" +#include "source/util/make_unique.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace { + +using opt::Instruction; +using opt::IRContext; +using opt::Module; +using opt::PassManager; +using opt::RemoveDuplicatesPass; +using opt::analysis::DecorationManager; +using opt::analysis::DefUseManager; +using opt::analysis::Type; +using opt::analysis::TypeManager; + +// Stores various information about an imported or exported symbol. +struct LinkageSymbolInfo { + SpvId id; // ID of the symbol + SpvId type_id; // ID of the type of the symbol + std::string name; // unique name defining the symbol and used for matching + // imports and exports together + std::vector parameter_ids; // ID of the parameters of the symbol, if + // it is a function +}; +struct LinkageEntry { + LinkageSymbolInfo imported_symbol; + LinkageSymbolInfo exported_symbol; + + LinkageEntry(const LinkageSymbolInfo& import_info, + const LinkageSymbolInfo& export_info) + : imported_symbol(import_info), exported_symbol(export_info) {} +}; +using LinkageTable = std::vector; + +// Shifts the IDs used in each binary of |modules| so that they occupy a +// disjoint range from the other binaries, and compute the new ID bound which +// is returned in |max_id_bound|. +// +// Both |modules| and |max_id_bound| should not be null, and |modules| should +// not be empty either. Furthermore |modules| should not contain any null +// pointers. +spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, + std::vector* modules, + uint32_t* max_id_bound); + +// Generates the header for the linked module and returns it in |header|. +// +// |header| should not be null, |modules| should not be empty and pointers +// should be non-null. |max_id_bound| should be strictly greater than 0. +// +// TODO(pierremoreau): What to do when binaries use different versions of +// SPIR-V? For now, use the max of all versions found in +// the input modules. +spv_result_t GenerateHeader(const MessageConsumer& consumer, + const std::vector& modules, + uint32_t max_id_bound, opt::ModuleHeader* header); + +// Merge all the modules from |in_modules| into a single module owned by +// |linked_context|. +// +// |linked_context| should not be null. +spv_result_t MergeModules(const MessageConsumer& consumer, + const std::vector& in_modules, + const AssemblyGrammar& grammar, + IRContext* linked_context); + +// Compute all pairs of import and export and return it in |linkings_to_do|. +// +// |linkings_to_do should not be null. Built-in symbols will be ignored. +// +// TODO(pierremoreau): Linkage attributes applied by a group decoration are +// currently not handled. (You could have a group being +// applied to a single ID.) +// TODO(pierremoreau): What should be the proper behaviour with built-in +// symbols? +spv_result_t GetImportExportPairs(const MessageConsumer& consumer, + const opt::IRContext& linked_context, + const DefUseManager& def_use_manager, + const DecorationManager& decoration_manager, + bool allow_partial_linkage, + LinkageTable* linkings_to_do); + +// Checks that for each pair of import and export, the import and export have +// the same type as well as the same decorations. +// +// TODO(pierremoreau): Decorations on functions parameters are currently not +// checked. +spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer, + const LinkageTable& linkings_to_do, + opt::IRContext* context); + +// Remove linkage specific instructions, such as prototypes of imported +// functions, declarations of imported variables, import (and export if +// necessary) linkage attribtes. +// +// |linked_context| and |decoration_manager| should not be null, and the +// 'RemoveDuplicatePass' should be run first. +// +// TODO(pierremoreau): Linkage attributes applied by a group decoration are +// currently not handled. (You could have a group being +// applied to a single ID.) +spv_result_t RemoveLinkageSpecificInstructions( + const MessageConsumer& consumer, const LinkerOptions& options, + const LinkageTable& linkings_to_do, DecorationManager* decoration_manager, + opt::IRContext* linked_context); + +// Verify that the unique ids of each instruction in |linked_context| (i.e. the +// merged module) are truly unique. Does not check the validity of other ids +spv_result_t VerifyIds(const MessageConsumer& consumer, + opt::IRContext* linked_context); + +spv_result_t ShiftIdsInModules(const MessageConsumer& consumer, + std::vector* modules, + uint32_t* max_id_bound) { + spv_position_t position = {}; + + if (modules == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|modules| of ShiftIdsInModules should not be null."; + if (modules->empty()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|modules| of ShiftIdsInModules should not be empty."; + if (max_id_bound == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|max_id_bound| of ShiftIdsInModules should not be null."; + + uint32_t id_bound = modules->front()->IdBound() - 1u; + for (auto module_iter = modules->begin() + 1; module_iter != modules->end(); + ++module_iter) { + Module* module = *module_iter; + module->ForEachInst([&id_bound](Instruction* insn) { + insn->ForEachId([&id_bound](uint32_t* id) { *id += id_bound; }); + }); + id_bound += module->IdBound() - 1u; + if (id_bound > 0x3FFFFF) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID) + << "The limit of IDs, 4194303, was exceeded:" + << " " << id_bound << " is the current ID bound."; + + // Invalidate the DefUseManager + module->context()->InvalidateAnalyses(opt::IRContext::kAnalysisDefUse); + } + ++id_bound; + if (id_bound > 0x3FFFFF) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_ID) + << "The limit of IDs, 4194303, was exceeded:" + << " " << id_bound << " is the current ID bound."; + + *max_id_bound = id_bound; + + return SPV_SUCCESS; +} + +spv_result_t GenerateHeader(const MessageConsumer& consumer, + const std::vector& modules, + uint32_t max_id_bound, opt::ModuleHeader* header) { + spv_position_t position = {}; + + if (modules.empty()) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|modules| of GenerateHeader should not be empty."; + if (max_id_bound == 0u) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|max_id_bound| of GenerateHeader should not be null."; + + uint32_t version = 0u; + for (const auto& module : modules) + version = std::max(version, module->version()); + + header->magic_number = SpvMagicNumber; + header->version = version; + header->generator = 17u; + header->bound = max_id_bound; + header->reserved = 0u; + + return SPV_SUCCESS; +} + +spv_result_t MergeModules(const MessageConsumer& consumer, + const std::vector& input_modules, + const AssemblyGrammar& grammar, + IRContext* linked_context) { + spv_position_t position = {}; + + if (linked_context == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|linked_module| of MergeModules should not be null."; + Module* linked_module = linked_context->module(); + + if (input_modules.empty()) return SPV_SUCCESS; + + for (const auto& module : input_modules) + for (const auto& inst : module->capabilities()) + linked_module->AddCapability( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->extensions()) + linked_module->AddExtension( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->ext_inst_imports()) + linked_module->AddExtInstImport( + std::unique_ptr(inst.Clone(linked_context))); + + do { + const Instruction* memory_model_inst = input_modules[0]->GetMemoryModel(); + if (memory_model_inst == nullptr) break; + + uint32_t addressing_model = memory_model_inst->GetSingleWordOperand(0u); + uint32_t memory_model = memory_model_inst->GetSingleWordOperand(1u); + for (const auto& module : input_modules) { + memory_model_inst = module->GetMemoryModel(); + if (memory_model_inst == nullptr) continue; + + if (addressing_model != memory_model_inst->GetSingleWordOperand(0u)) { + spv_operand_desc initial_desc = nullptr, current_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + addressing_model, &initial_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_ADDRESSING_MODEL, + memory_model_inst->GetSingleWordOperand(0u), + ¤t_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting addressing models: " << initial_desc->name + << " vs " << current_desc->name << "."; + } + if (memory_model != memory_model_inst->GetSingleWordOperand(1u)) { + spv_operand_desc initial_desc = nullptr, current_desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, memory_model, + &initial_desc); + grammar.lookupOperand(SPV_OPERAND_TYPE_MEMORY_MODEL, + memory_model_inst->GetSingleWordOperand(1u), + ¤t_desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "Conflicting memory models: " << initial_desc->name << " vs " + << current_desc->name << "."; + } + } + + if (memory_model_inst != nullptr) + linked_module->SetMemoryModel(std::unique_ptr( + memory_model_inst->Clone(linked_context))); + } while (false); + + std::vector> entry_points; + for (const auto& module : input_modules) + for (const auto& inst : module->entry_points()) { + const uint32_t model = inst.GetSingleWordInOperand(0); + const char* const name = + reinterpret_cast(inst.GetInOperand(2).words.data()); + const auto i = std::find_if( + entry_points.begin(), entry_points.end(), + [model, name](const std::pair& v) { + return v.first == model && strcmp(name, v.second) == 0; + }); + if (i != entry_points.end()) { + spv_operand_desc desc = nullptr; + grammar.lookupOperand(SPV_OPERAND_TYPE_EXECUTION_MODEL, model, &desc); + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "The entry point \"" << name << "\", with execution model " + << desc->name << ", was already defined."; + } + linked_module->AddEntryPoint( + std::unique_ptr(inst.Clone(linked_context))); + entry_points.emplace_back(model, name); + } + + for (const auto& module : input_modules) + for (const auto& inst : module->execution_modes()) + linked_module->AddExecutionMode( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->debugs1()) + linked_module->AddDebug1Inst( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->debugs2()) + linked_module->AddDebug2Inst( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->debugs3()) + linked_module->AddDebug3Inst( + std::unique_ptr(inst.Clone(linked_context))); + + for (const auto& module : input_modules) + for (const auto& inst : module->ext_inst_debuginfo()) + linked_module->AddExtInstDebugInfo( + std::unique_ptr(inst.Clone(linked_context))); + + // If the generated module uses SPIR-V 1.1 or higher, add an + // OpModuleProcessed instruction about the linking step. + if (linked_module->version() >= 0x10100) { + const std::string processed_string("Linked by SPIR-V Tools Linker"); + const auto num_chars = processed_string.size(); + // Compute num words, accommodate the terminating null character. + const auto num_words = (num_chars + 1 + 3) / 4; + std::vector processed_words(num_words, 0u); + std::memcpy(processed_words.data(), processed_string.data(), num_chars); + linked_module->AddDebug3Inst(std::unique_ptr( + new Instruction(linked_context, SpvOpModuleProcessed, 0u, 0u, + {{SPV_OPERAND_TYPE_LITERAL_STRING, processed_words}}))); + } + + for (const auto& module : input_modules) + for (const auto& inst : module->annotations()) + linked_module->AddAnnotationInst( + std::unique_ptr(inst.Clone(linked_context))); + + // TODO(pierremoreau): Since the modules have not been validate, should we + // expect SpvStorageClassFunction variables outside + // functions? + uint32_t num_global_values = 0u; + for (const auto& module : input_modules) { + for (const auto& inst : module->types_values()) { + linked_module->AddType( + std::unique_ptr(inst.Clone(linked_context))); + num_global_values += inst.opcode() == SpvOpVariable; + } + } + if (num_global_values > 0xFFFF) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INTERNAL) + << "The limit of global values, 65535, was exceeded;" + << " " << num_global_values << " global values were found."; + + // Process functions and their basic blocks + for (const auto& module : input_modules) { + for (const auto& func : *module) { + std::unique_ptr cloned_func(func.Clone(linked_context)); + linked_module->AddFunction(std::move(cloned_func)); + } + } + + return SPV_SUCCESS; +} + +spv_result_t GetImportExportPairs(const MessageConsumer& consumer, + const opt::IRContext& linked_context, + const DefUseManager& def_use_manager, + const DecorationManager& decoration_manager, + bool allow_partial_linkage, + LinkageTable* linkings_to_do) { + spv_position_t position = {}; + + if (linkings_to_do == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|linkings_to_do| of GetImportExportPairs should not be empty."; + + std::vector imports; + std::unordered_map> exports; + + // Figure out the imports and exports + for (const auto& decoration : linked_context.annotations()) { + if (decoration.opcode() != SpvOpDecorate || + decoration.GetSingleWordInOperand(1u) != SpvDecorationLinkageAttributes) + continue; + + const SpvId id = decoration.GetSingleWordInOperand(0u); + // Ignore if the targeted symbol is a built-in + bool is_built_in = false; + for (const auto& id_decoration : + decoration_manager.GetDecorationsFor(id, false)) { + if (id_decoration->GetSingleWordInOperand(1u) == SpvDecorationBuiltIn) { + is_built_in = true; + break; + } + } + if (is_built_in) { + continue; + } + + const uint32_t type = decoration.GetSingleWordInOperand(3u); + + LinkageSymbolInfo symbol_info; + symbol_info.name = + reinterpret_cast(decoration.GetInOperand(2u).words.data()); + symbol_info.id = id; + symbol_info.type_id = 0u; + + // Retrieve the type of the current symbol. This information will be used + // when checking that the imported and exported symbols have the same + // types. + const Instruction* def_inst = def_use_manager.GetDef(id); + if (def_inst == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "ID " << id << " is never defined:\n"; + + if (def_inst->opcode() == SpvOpVariable) { + symbol_info.type_id = def_inst->type_id(); + } else if (def_inst->opcode() == SpvOpFunction) { + symbol_info.type_id = def_inst->GetSingleWordInOperand(1u); + + // range-based for loop calls begin()/end(), but never cbegin()/cend(), + // which will not work here. + for (auto func_iter = linked_context.module()->cbegin(); + func_iter != linked_context.module()->cend(); ++func_iter) { + if (func_iter->result_id() != id) continue; + func_iter->ForEachParam([&symbol_info](const Instruction* inst) { + symbol_info.parameter_ids.push_back(inst->result_id()); + }); + } + } else { + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Only global variables and functions can be decorated using" + << " LinkageAttributes; " << id << " is neither of them.\n"; + } + + if (type == SpvLinkageTypeImport) + imports.push_back(symbol_info); + else if (type == SpvLinkageTypeExport) + exports[symbol_info.name].push_back(symbol_info); + } + + // Find the import/export pairs + for (const auto& import : imports) { + std::vector possible_exports; + const auto& exp = exports.find(import.name); + if (exp != exports.end()) possible_exports = exp->second; + if (possible_exports.empty() && !allow_partial_linkage) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Unresolved external reference to \"" << import.name << "\"."; + else if (possible_exports.size() > 1u) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Too many external references, " << possible_exports.size() + << ", were found for \"" << import.name << "\"."; + + if (!possible_exports.empty()) + linkings_to_do->emplace_back(import, possible_exports.front()); + } + + return SPV_SUCCESS; +} + +spv_result_t CheckImportExportCompatibility(const MessageConsumer& consumer, + const LinkageTable& linkings_to_do, + opt::IRContext* context) { + spv_position_t position = {}; + + // Ensure the import and export types are the same. + const DecorationManager& decoration_manager = *context->get_decoration_mgr(); + const TypeManager& type_manager = *context->get_type_mgr(); + for (const auto& linking_entry : linkings_to_do) { + Type* imported_symbol_type = + type_manager.GetType(linking_entry.imported_symbol.type_id); + Type* exported_symbol_type = + type_manager.GetType(linking_entry.exported_symbol.type_id); + if (!(*imported_symbol_type == *exported_symbol_type)) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Type mismatch on symbol \"" + << linking_entry.imported_symbol.name + << "\" between imported variable/function %" + << linking_entry.imported_symbol.id + << " and exported variable/function %" + << linking_entry.exported_symbol.id << "."; + } + + // Ensure the import and export decorations are similar + for (const auto& linking_entry : linkings_to_do) { + if (!decoration_manager.HaveTheSameDecorations( + linking_entry.imported_symbol.id, linking_entry.exported_symbol.id)) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Decorations mismatch on symbol \"" + << linking_entry.imported_symbol.name + << "\" between imported variable/function %" + << linking_entry.imported_symbol.id + << " and exported variable/function %" + << linking_entry.exported_symbol.id << "."; + // TODO(pierremoreau): Decorations on function parameters should probably + // match, except for FuncParamAttr if I understand the + // spec correctly. + // TODO(pierremoreau): Decorations on the function return type should + // match, except for FuncParamAttr. + } + + return SPV_SUCCESS; +} + +spv_result_t RemoveLinkageSpecificInstructions( + const MessageConsumer& consumer, const LinkerOptions& options, + const LinkageTable& linkings_to_do, DecorationManager* decoration_manager, + opt::IRContext* linked_context) { + spv_position_t position = {}; + + if (decoration_manager == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|decoration_manager| of RemoveLinkageSpecificInstructions " + "should not be empty."; + if (linked_context == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_DATA) + << "|linked_module| of RemoveLinkageSpecificInstructions should not " + "be empty."; + + // TODO(pierremoreau): Remove FuncParamAttr decorations of imported + // functions' return type. + + // Remove prototypes of imported functions + for (const auto& linking_entry : linkings_to_do) { + for (auto func_iter = linked_context->module()->begin(); + func_iter != linked_context->module()->end();) { + if (func_iter->result_id() == linking_entry.imported_symbol.id) + func_iter = func_iter.Erase(); + else + ++func_iter; + } + } + + // Remove declarations of imported variables + for (const auto& linking_entry : linkings_to_do) { + auto next = linked_context->types_values_begin(); + for (auto inst = next; inst != linked_context->types_values_end(); + inst = next) { + ++next; + if (inst->result_id() == linking_entry.imported_symbol.id) { + linked_context->KillInst(&*inst); + } + } + } + + // If partial linkage is allowed, we need an efficient way to check whether + // an imported ID had a corresponding export symbol. As uses of the imported + // symbol have already been replaced by the exported symbol, use the exported + // symbol ID. + // TODO(pierremoreau): This will not work if the decoration is applied + // through a group, but the linker does not support that + // either. + std::unordered_set imports; + if (options.GetAllowPartialLinkage()) { + imports.reserve(linkings_to_do.size()); + for (const auto& linking_entry : linkings_to_do) + imports.emplace(linking_entry.exported_symbol.id); + } + + // Remove import linkage attributes + auto next = linked_context->annotation_begin(); + for (auto inst = next; inst != linked_context->annotation_end(); + inst = next) { + ++next; + // If this is an import annotation: + // * if we do not allow partial linkage, remove all import annotations; + // * otherwise, remove the annotation only if there was a corresponding + // export. + if (inst->opcode() == SpvOpDecorate && + inst->GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes && + inst->GetSingleWordOperand(3u) == SpvLinkageTypeImport && + (!options.GetAllowPartialLinkage() || + imports.find(inst->GetSingleWordOperand(0u)) != imports.end())) { + linked_context->KillInst(&*inst); + } + } + + // Remove export linkage attributes if making an executable + if (!options.GetCreateLibrary()) { + next = linked_context->annotation_begin(); + for (auto inst = next; inst != linked_context->annotation_end(); + inst = next) { + ++next; + if (inst->opcode() == SpvOpDecorate && + inst->GetSingleWordOperand(1u) == SpvDecorationLinkageAttributes && + inst->GetSingleWordOperand(3u) == SpvLinkageTypeExport) { + linked_context->KillInst(&*inst); + } + } + } + + // Remove Linkage capability if making an executable and partial linkage is + // not allowed + if (!options.GetCreateLibrary() && !options.GetAllowPartialLinkage()) { + for (auto& inst : linked_context->capabilities()) + if (inst.GetSingleWordInOperand(0u) == SpvCapabilityLinkage) { + linked_context->KillInst(&inst); + // The RemoveDuplicatesPass did remove duplicated capabilities, so we + // now there aren’t more SpvCapabilityLinkage further down. + break; + } + } + + return SPV_SUCCESS; +} + +spv_result_t VerifyIds(const MessageConsumer& consumer, + opt::IRContext* linked_context) { + std::unordered_set ids; + bool ok = true; + linked_context->module()->ForEachInst( + [&ids, &ok](const opt::Instruction* inst) { + ok &= ids.insert(inst->unique_id()).second; + }); + + if (!ok) { + consumer(SPV_MSG_INTERNAL_ERROR, "", {}, "Non-unique id in merged module"); + return SPV_ERROR_INVALID_ID; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t Link(const Context& context, + const std::vector>& binaries, + std::vector* linked_binary, + const LinkerOptions& options) { + std::vector binary_ptrs; + binary_ptrs.reserve(binaries.size()); + std::vector binary_sizes; + binary_sizes.reserve(binaries.size()); + + for (const auto& binary : binaries) { + binary_ptrs.push_back(binary.data()); + binary_sizes.push_back(binary.size()); + } + + return Link(context, binary_ptrs.data(), binary_sizes.data(), binaries.size(), + linked_binary, options); +} + +spv_result_t Link(const Context& context, const uint32_t* const* binaries, + const size_t* binary_sizes, size_t num_binaries, + std::vector* linked_binary, + const LinkerOptions& options) { + spv_position_t position = {}; + const spv_context& c_context = context.CContext(); + const MessageConsumer& consumer = c_context->consumer; + + linked_binary->clear(); + if (num_binaries == 0u) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "No modules were given."; + + std::vector> ir_contexts; + std::vector modules; + modules.reserve(num_binaries); + for (size_t i = 0u; i < num_binaries; ++i) { + const uint32_t schema = binaries[i][4u]; + if (schema != 0u) { + position.index = 4u; + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Schema is non-zero for module " << i << "."; + } + + std::unique_ptr ir_context = BuildModule( + c_context->target_env, consumer, binaries[i], binary_sizes[i]); + if (ir_context == nullptr) + return DiagnosticStream(position, consumer, "", SPV_ERROR_INVALID_BINARY) + << "Failed to build a module out of " << ir_contexts.size() << "."; + modules.push_back(ir_context->module()); + ir_contexts.push_back(std::move(ir_context)); + } + + // Phase 1: Shift the IDs used in each binary so that they occupy a disjoint + // range from the other binaries, and compute the new ID bound. + uint32_t max_id_bound = 0u; + spv_result_t res = ShiftIdsInModules(consumer, &modules, &max_id_bound); + if (res != SPV_SUCCESS) return res; + + // Phase 2: Generate the header + opt::ModuleHeader header; + res = GenerateHeader(consumer, modules, max_id_bound, &header); + if (res != SPV_SUCCESS) return res; + IRContext linked_context(c_context->target_env, consumer); + linked_context.module()->SetHeader(header); + + // Phase 3: Merge all the binaries into a single one. + AssemblyGrammar grammar(c_context); + res = MergeModules(consumer, modules, grammar, &linked_context); + if (res != SPV_SUCCESS) return res; + + if (options.GetVerifyIds()) { + res = VerifyIds(consumer, &linked_context); + if (res != SPV_SUCCESS) return res; + } + + // Phase 4: Find the import/export pairs + LinkageTable linkings_to_do; + res = GetImportExportPairs(consumer, linked_context, + *linked_context.get_def_use_mgr(), + *linked_context.get_decoration_mgr(), + options.GetAllowPartialLinkage(), &linkings_to_do); + if (res != SPV_SUCCESS) return res; + + // Phase 5: Ensure the import and export have the same types and decorations. + res = + CheckImportExportCompatibility(consumer, linkings_to_do, &linked_context); + if (res != SPV_SUCCESS) return res; + + // Phase 6: Remove duplicates + PassManager manager; + manager.SetMessageConsumer(consumer); + manager.AddPass(); + opt::Pass::Status pass_res = manager.Run(&linked_context); + if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; + + // Phase 7: Remove all names and decorations of import variables/functions + for (const auto& linking_entry : linkings_to_do) { + linked_context.KillNamesAndDecorates(linking_entry.imported_symbol.id); + for (const auto parameter_id : + linking_entry.imported_symbol.parameter_ids) { + linked_context.KillNamesAndDecorates(parameter_id); + } + } + + // Phase 8: Rematch import variables/functions to export variables/functions + for (const auto& linking_entry : linkings_to_do) { + linked_context.ReplaceAllUsesWith(linking_entry.imported_symbol.id, + linking_entry.exported_symbol.id); + } + + // Phase 9: Remove linkage specific instructions, such as import/export + // attributes, linkage capability, etc. if applicable + res = RemoveLinkageSpecificInstructions(consumer, options, linkings_to_do, + linked_context.get_decoration_mgr(), + &linked_context); + if (res != SPV_SUCCESS) return res; + + // Phase 10: Compact the IDs used in the module + manager.AddPass(); + pass_res = manager.Run(&linked_context); + if (pass_res == opt::Pass::Status::Failure) return SPV_ERROR_INVALID_DATA; + + // Phase 11: Output the module + linked_context.module()->ToBinary(linked_binary, true); + + return SPV_SUCCESS; +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/macro.h b/third_party/spirv-tools/source/macro.h new file mode 100644 index 0000000..7219ffe --- /dev/null +++ b/third_party/spirv-tools/source/macro.h @@ -0,0 +1,25 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_MACRO_H_ +#define SOURCE_MACRO_H_ + +// Evaluates to the number of elements of array A. +// +// If we could use constexpr, then we could make this a template function. +// If the source arrays were std::array, then we could have used +// std::array::size. +#define ARRAY_SIZE(A) (static_cast(sizeof(A) / sizeof(A[0]))) + +#endif // SOURCE_MACRO_H_ diff --git a/third_party/spirv-tools/source/name_mapper.cpp b/third_party/spirv-tools/source/name_mapper.cpp new file mode 100644 index 0000000..eb08f8f --- /dev/null +++ b/third_party/spirv-tools/source/name_mapper.cpp @@ -0,0 +1,332 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/name_mapper.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "spirv-tools/libspirv.h" + +#include "source/latest_version_spirv_header.h" +#include "source/parsed_operand.h" + +namespace spvtools { +namespace { + +// Converts a uint32_t to its string decimal representation. +std::string to_string(uint32_t id) { + // Use stringstream, since some versions of Android compilers lack + // std::to_string. + std::stringstream os; + os << id; + return os.str(); +} + +} // anonymous namespace + +NameMapper GetTrivialNameMapper() { return to_string; } + +FriendlyNameMapper::FriendlyNameMapper(const spv_const_context context, + const uint32_t* code, + const size_t wordCount) + : grammar_(AssemblyGrammar(context)) { + spv_diagnostic diag = nullptr; + // We don't care if the parse fails. + spvBinaryParse(context, this, code, wordCount, nullptr, + ParseInstructionForwarder, &diag); + spvDiagnosticDestroy(diag); +} + +std::string FriendlyNameMapper::NameForId(uint32_t id) { + auto iter = name_for_id_.find(id); + if (iter == name_for_id_.end()) { + // It must have been an invalid module, so just return a trivial mapping. + // We don't care about uniqueness. + return to_string(id); + } else { + return iter->second; + } +} + +std::string FriendlyNameMapper::Sanitize(const std::string& suggested_name) { + if (suggested_name.empty()) return "_"; + // Otherwise, replace invalid characters by '_'. + std::string result; + std::string valid = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "_0123456789"; + std::transform(suggested_name.begin(), suggested_name.end(), + std::back_inserter(result), [&valid](const char c) { + return (std::string::npos == valid.find(c)) ? '_' : c; + }); + return result; +} + +void FriendlyNameMapper::SaveName(uint32_t id, + const std::string& suggested_name) { + if (name_for_id_.find(id) != name_for_id_.end()) return; + + const std::string sanitized_suggested_name = Sanitize(suggested_name); + std::string name = sanitized_suggested_name; + auto inserted = used_names_.insert(name); + if (!inserted.second) { + const std::string base_name = sanitized_suggested_name + "_"; + for (uint32_t index = 0; !inserted.second; ++index) { + name = base_name + to_string(index); + inserted = used_names_.insert(name); + } + } + name_for_id_[id] = name; +} + +void FriendlyNameMapper::SaveBuiltInName(uint32_t target_id, + uint32_t built_in) { +#define GLCASE(name) \ + case SpvBuiltIn##name: \ + SaveName(target_id, "gl_" #name); \ + return; +#define GLCASE2(name, suggested) \ + case SpvBuiltIn##name: \ + SaveName(target_id, "gl_" #suggested); \ + return; +#define CASE(name) \ + case SpvBuiltIn##name: \ + SaveName(target_id, #name); \ + return; + switch (built_in) { + GLCASE(Position) + GLCASE(PointSize) + GLCASE(ClipDistance) + GLCASE(CullDistance) + GLCASE2(VertexId, VertexID) + GLCASE2(InstanceId, InstanceID) + GLCASE2(PrimitiveId, PrimitiveID) + GLCASE2(InvocationId, InvocationID) + GLCASE(Layer) + GLCASE(ViewportIndex) + GLCASE(TessLevelOuter) + GLCASE(TessLevelInner) + GLCASE(TessCoord) + GLCASE(PatchVertices) + GLCASE(FragCoord) + GLCASE(PointCoord) + GLCASE(FrontFacing) + GLCASE2(SampleId, SampleID) + GLCASE(SamplePosition) + GLCASE(SampleMask) + GLCASE(FragDepth) + GLCASE(HelperInvocation) + GLCASE2(NumWorkgroups, NumWorkGroups) + GLCASE2(WorkgroupSize, WorkGroupSize) + GLCASE2(WorkgroupId, WorkGroupID) + GLCASE2(LocalInvocationId, LocalInvocationID) + GLCASE2(GlobalInvocationId, GlobalInvocationID) + GLCASE(LocalInvocationIndex) + CASE(WorkDim) + CASE(GlobalSize) + CASE(EnqueuedWorkgroupSize) + CASE(GlobalOffset) + CASE(GlobalLinearId) + CASE(SubgroupSize) + CASE(SubgroupMaxSize) + CASE(NumSubgroups) + CASE(NumEnqueuedSubgroups) + CASE(SubgroupId) + CASE(SubgroupLocalInvocationId) + GLCASE(VertexIndex) + GLCASE(InstanceIndex) + GLCASE(BaseInstance) + CASE(SubgroupEqMaskKHR) + CASE(SubgroupGeMaskKHR) + CASE(SubgroupGtMaskKHR) + CASE(SubgroupLeMaskKHR) + CASE(SubgroupLtMaskKHR) + default: + break; + } +#undef GLCASE +#undef GLCASE2 +#undef CASE +} + +spv_result_t FriendlyNameMapper::ParseInstruction( + const spv_parsed_instruction_t& inst) { + const auto result_id = inst.result_id; + switch (inst.opcode) { + case SpvOpName: + SaveName(inst.words[1], reinterpret_cast(inst.words + 2)); + break; + case SpvOpDecorate: + // Decorations come after OpName. So OpName will take precedence over + // decorations. + // + // In theory, we should also handle OpGroupDecorate. But that's unlikely + // to occur. + if (inst.words[2] == SpvDecorationBuiltIn) { + assert(inst.num_words > 3); + SaveBuiltInName(inst.words[1], inst.words[3]); + } + break; + case SpvOpTypeVoid: + SaveName(result_id, "void"); + break; + case SpvOpTypeBool: + SaveName(result_id, "bool"); + break; + case SpvOpTypeInt: { + std::string signedness; + std::string root; + const auto bit_width = inst.words[2]; + switch (bit_width) { + case 8: + root = "char"; + break; + case 16: + root = "short"; + break; + case 32: + root = "int"; + break; + case 64: + root = "long"; + break; + default: + root = to_string(bit_width); + signedness = "i"; + break; + } + if (0 == inst.words[3]) signedness = "u"; + SaveName(result_id, signedness + root); + } break; + case SpvOpTypeFloat: { + const auto bit_width = inst.words[2]; + switch (bit_width) { + case 16: + SaveName(result_id, "half"); + break; + case 32: + SaveName(result_id, "float"); + break; + case 64: + SaveName(result_id, "double"); + break; + default: + SaveName(result_id, std::string("fp") + to_string(bit_width)); + break; + } + } break; + case SpvOpTypeVector: + SaveName(result_id, std::string("v") + to_string(inst.words[3]) + + NameForId(inst.words[2])); + break; + case SpvOpTypeMatrix: + SaveName(result_id, std::string("mat") + to_string(inst.words[3]) + + NameForId(inst.words[2])); + break; + case SpvOpTypeArray: + SaveName(result_id, std::string("_arr_") + NameForId(inst.words[2]) + + "_" + NameForId(inst.words[3])); + break; + case SpvOpTypeRuntimeArray: + SaveName(result_id, + std::string("_runtimearr_") + NameForId(inst.words[2])); + break; + case SpvOpTypePointer: + SaveName(result_id, std::string("_ptr_") + + NameForEnumOperand(SPV_OPERAND_TYPE_STORAGE_CLASS, + inst.words[2]) + + "_" + NameForId(inst.words[3])); + break; + case SpvOpTypePipe: + SaveName(result_id, + std::string("Pipe") + + NameForEnumOperand(SPV_OPERAND_TYPE_ACCESS_QUALIFIER, + inst.words[2])); + break; + case SpvOpTypeEvent: + SaveName(result_id, "Event"); + break; + case SpvOpTypeDeviceEvent: + SaveName(result_id, "DeviceEvent"); + break; + case SpvOpTypeReserveId: + SaveName(result_id, "ReserveId"); + break; + case SpvOpTypeQueue: + SaveName(result_id, "Queue"); + break; + case SpvOpTypeOpaque: + SaveName(result_id, + std::string("Opaque_") + + Sanitize(reinterpret_cast(inst.words + 2))); + break; + case SpvOpTypePipeStorage: + SaveName(result_id, "PipeStorage"); + break; + case SpvOpTypeNamedBarrier: + SaveName(result_id, "NamedBarrier"); + break; + case SpvOpTypeStruct: + // Structs are mapped rather simplisitically. Just indicate that they + // are a struct and then give the raw Id number. + SaveName(result_id, std::string("_struct_") + to_string(result_id)); + break; + case SpvOpConstantTrue: + SaveName(result_id, "true"); + break; + case SpvOpConstantFalse: + SaveName(result_id, "false"); + break; + case SpvOpConstant: { + std::ostringstream value; + EmitNumericLiteral(&value, inst, inst.operands[2]); + auto value_str = value.str(); + // Use 'n' to signify negative. Other invalid characters will be mapped + // to underscore. + for (auto& c : value_str) + if (c == '-') c = 'n'; + SaveName(result_id, NameForId(inst.type_id) + "_" + value_str); + } break; + default: + // If this instruction otherwise defines an Id, then save a mapping for + // it. This is needed to ensure uniqueness in there is an OpName with + // string something like "1" that might collide with this result_id. + // We should only do this if a name hasn't already been registered by some + // previous forward reference. + if (result_id && name_for_id_.find(result_id) == name_for_id_.end()) + SaveName(result_id, to_string(result_id)); + break; + } + return SPV_SUCCESS; +} + +std::string FriendlyNameMapper::NameForEnumOperand(spv_operand_type_t type, + uint32_t word) { + spv_operand_desc desc = nullptr; + if (SPV_SUCCESS == grammar_.lookupOperand(type, word, &desc)) { + return desc->name; + } else { + // Invalid input. Just give something. + return std::string("StorageClass") + to_string(word); + } +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/name_mapper.h b/third_party/spirv-tools/source/name_mapper.h new file mode 100644 index 0000000..6902141 --- /dev/null +++ b/third_party/spirv-tools/source/name_mapper.h @@ -0,0 +1,122 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_NAME_MAPPER_H_ +#define SOURCE_NAME_MAPPER_H_ + +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// A NameMapper maps SPIR-V Id values to names. Each name is valid to use in +// SPIR-V assembly. The mapping is one-to-one, i.e. no two Ids map to the same +// name. +using NameMapper = std::function; + +// Returns a NameMapper which always maps an Id to its decimal representation. +NameMapper GetTrivialNameMapper(); + +// A FriendlyNameMapper parses a module upon construction. If the parse is +// successful, then the NameForId method maps an Id to a friendly name +// while also satisfying the constraints on a NameMapper. +// +// The mapping is friendly in the following sense: +// - If an Id has a debug name (via OpName), then that will be used when +// possible. +// - Well known scalar types map to friendly names. For example, +// OpTypeVoid should be %void. Scalar types map to their names in OpenCL +// when +// there is a correspondence, and otherwise as follows: +// - unsigned integer type of n bits map to "u" followed by n +// - signed integer type of n bits map to "i" followed by n +// - floating point type of n bits map to "fp" followed by n +// - Vector type names map to "v" followed by the number of components, +// followed by the friendly name for the base type. +// - Matrix type names map to "mat" followed by the number of columns, +// followed by the friendly name for the base vector type. +// - Pointer types map to "_ptr_", then the name of the storage class, then the +// name for the pointee type. +// - Exotic types like event, pipe, opaque, queue, reserve-id map to their own +// human readable names. +// - A struct type maps to "_struct_" followed by the raw Id number. That's +// pretty simplistic, but workable. +// - A built-in variable maps to its GLSL variable name. +// - Numeric literals in OpConstant map to a human-friendly name. +class FriendlyNameMapper { + public: + // Construct a friendly name mapper, and determine friendly names for each + // defined Id in the specified module. The module is specified by the code + // wordCount, and should be parseable in the specified context. + FriendlyNameMapper(const spv_const_context context, const uint32_t* code, + const size_t wordCount); + + // Returns a NameMapper which maps ids to the friendly names parsed from the + // module provided to the constructor. + NameMapper GetNameMapper() { + return [this](uint32_t id) { return this->NameForId(id); }; + } + + // Returns the friendly name for the given id. If the module parsed during + // construction is valid, then the mapping satisfies the rules for a + // NameMapper. + std::string NameForId(uint32_t id); + + private: + // Transforms the given string so that it is acceptable as an Id name in + // assembly language. Two distinct inputs can map to the same output. + std::string Sanitize(const std::string& suggested_name); + + // Records a name for the given id. If this id already has a name, then + // this is a no-op. If the id doesn't have a name, use the given + // suggested_name if it hasn't already been taken, and otherwise generate + // a new (unused) name based on the suggested name. + void SaveName(uint32_t id, const std::string& suggested_name); + + // Records a built-in variable name for target_id. If target_id already + // has a name then this is a no-op. + void SaveBuiltInName(uint32_t target_id, uint32_t built_in); + + // Collects information from the given parsed instruction to populate + // name_for_id_. Returns SPV_SUCCESS; + spv_result_t ParseInstruction(const spv_parsed_instruction_t& inst); + + // Forwards a parsed-instruction callback from the binary parser into the + // FriendlyNameMapper hidden inside the user_data parameter. + static spv_result_t ParseInstructionForwarder( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + return reinterpret_cast(user_data)->ParseInstruction( + *parsed_instruction); + } + + // Returns the friendly name for an enumerant. + std::string NameForEnumOperand(spv_operand_type_t type, uint32_t word); + + // Maps an id to its friendly name. This will have an entry for each Id + // defined in the module. + std::unordered_map name_for_id_; + // The set of names that have a mapping in name_for_id_; + std::unordered_set used_names_; + // The assembly grammar for the current context. + const AssemblyGrammar grammar_; +}; + +} // namespace spvtools + +#endif // SOURCE_NAME_MAPPER_H_ diff --git a/third_party/spirv-tools/source/opcode.cpp b/third_party/spirv-tools/source/opcode.cpp new file mode 100644 index 0000000..8305bcf --- /dev/null +++ b/third_party/spirv-tools/source/opcode.cpp @@ -0,0 +1,750 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opcode.h" + +#include +#include + +#include +#include + +#include "source/instruction.h" +#include "source/macro.h" +#include "source/spirv_constant.h" +#include "source/spirv_endian.h" +#include "source/spirv_target_env.h" +#include "spirv-tools/libspirv.h" + +namespace { +struct OpcodeDescPtrLen { + const spv_opcode_desc_t* ptr; + uint32_t len; +}; + +#include "core.insts-unified1.inc" + +static const spv_opcode_table_t kOpcodeTable = {ARRAY_SIZE(kOpcodeTableEntries), + kOpcodeTableEntries}; + +// Represents a vendor tool entry in the SPIR-V XML Regsitry. +struct VendorTool { + uint32_t value; + const char* vendor; + const char* tool; // Might be empty string. + const char* vendor_tool; // Combiantion of vendor and tool. +}; + +const VendorTool vendor_tools[] = { +#include "generators.inc" +}; + +} // anonymous namespace + +// TODO(dneto): Move this to another file. It doesn't belong with opcode +// processing. +const char* spvGeneratorStr(uint32_t generator) { + auto where = std::find_if( + std::begin(vendor_tools), std::end(vendor_tools), + [generator](const VendorTool& vt) { return generator == vt.value; }); + if (where != std::end(vendor_tools)) return where->vendor_tool; + return "Unknown"; +} + +uint32_t spvOpcodeMake(uint16_t wordCount, SpvOp opcode) { + return ((uint32_t)opcode) | (((uint32_t)wordCount) << 16); +} + +void spvOpcodeSplit(const uint32_t word, uint16_t* pWordCount, + uint16_t* pOpcode) { + if (pWordCount) { + *pWordCount = (uint16_t)((0xffff0000 & word) >> 16); + } + if (pOpcode) { + *pOpcode = 0x0000ffff & word; + } +} + +spv_result_t spvOpcodeTableGet(spv_opcode_table* pInstTable, spv_target_env) { + if (!pInstTable) return SPV_ERROR_INVALID_POINTER; + + // Descriptions of each opcode. Each entry describes the format of the + // instruction that follows a particular opcode. + + *pInstTable = &kOpcodeTable; + return SPV_SUCCESS; +} + +spv_result_t spvOpcodeTableNameLookup(spv_target_env env, + const spv_opcode_table table, + const char* name, + spv_opcode_desc* pEntry) { + if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; + if (!table) return SPV_ERROR_INVALID_TABLE; + + // TODO: This lookup of the Opcode table is suboptimal! Binary sort would be + // preferable but the table requires sorting on the Opcode name, but it's + // static const initialized and matches the order of the spec. + const size_t nameLength = strlen(name); + const auto version = spvVersionForTargetEnv(env); + for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) { + const spv_opcode_desc_t& entry = table->entries[opcodeIndex]; + // We considers the current opcode as available as long as + // 1. The target environment satisfies the minimal requirement of the + // opcode; or + // 2. There is at least one extension enabling this opcode. + // + // Note that the second rule assumes the extension enabling this instruction + // is indeed requested in the SPIR-V code; checking that should be + // validator's work. + if (((version >= entry.minVersion && version <= entry.lastVersion) || + entry.numExtensions > 0u || entry.numCapabilities > 0u) && + nameLength == strlen(entry.name) && + !strncmp(name, entry.name, nameLength)) { + // NOTE: Found out Opcode! + *pEntry = &entry; + return SPV_SUCCESS; + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t spvOpcodeTableValueLookup(spv_target_env env, + const spv_opcode_table table, + const SpvOp opcode, + spv_opcode_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!pEntry) return SPV_ERROR_INVALID_POINTER; + + const auto beg = table->entries; + const auto end = table->entries + table->count; + + spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {}, + false, false, 0, nullptr, ~0u, ~0u}; + + auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { + return lhs.opcode < rhs.opcode; + }; + + // We need to loop here because there can exist multiple symbols for the same + // opcode value, and they can be introduced in different target environments, + // which means they can have different minimal version requirements. + // Assumes the underlying table is already sorted ascendingly according to + // opcode value. + const auto version = spvVersionForTargetEnv(env); + for (auto it = std::lower_bound(beg, end, needle, comp); + it != end && it->opcode == opcode; ++it) { + // We considers the current opcode as available as long as + // 1. The target environment satisfies the minimal requirement of the + // opcode; or + // 2. There is at least one extension enabling this opcode. + // + // Note that the second rule assumes the extension enabling this instruction + // is indeed requested in the SPIR-V code; checking that should be + // validator's work. + if ((version >= it->minVersion && version <= it->lastVersion) || + it->numExtensions > 0u || it->numCapabilities > 0u) { + *pEntry = it; + return SPV_SUCCESS; + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +void spvInstructionCopy(const uint32_t* words, const SpvOp opcode, + const uint16_t wordCount, const spv_endianness_t endian, + spv_instruction_t* pInst) { + pInst->opcode = opcode; + pInst->words.resize(wordCount); + for (uint16_t wordIndex = 0; wordIndex < wordCount; ++wordIndex) { + pInst->words[wordIndex] = spvFixWord(words[wordIndex], endian); + if (!wordIndex) { + uint16_t thisWordCount; + uint16_t thisOpcode; + spvOpcodeSplit(pInst->words[wordIndex], &thisWordCount, &thisOpcode); + assert(opcode == static_cast(thisOpcode) && + wordCount == thisWordCount && "Endianness failed!"); + } + } +} + +const char* spvOpcodeString(const uint32_t opcode) { + const auto beg = kOpcodeTableEntries; + const auto end = kOpcodeTableEntries + ARRAY_SIZE(kOpcodeTableEntries); + spv_opcode_desc_t needle = {"", static_cast(opcode), + 0, nullptr, + 0, {}, + false, false, + 0, nullptr, + ~0u, ~0u}; + auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) { + return lhs.opcode < rhs.opcode; + }; + auto it = std::lower_bound(beg, end, needle, comp); + if (it != end && it->opcode == opcode) { + return it->name; + } + + assert(0 && "Unreachable!"); + return "unknown"; +} + +int32_t spvOpcodeIsScalarType(const SpvOp opcode) { + switch (opcode) { + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeBool: + return true; + default: + return false; + } +} + +int32_t spvOpcodeIsSpecConstant(const SpvOp opcode) { + switch (opcode) { + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + case SpvOpSpecConstant: + case SpvOpSpecConstantComposite: + case SpvOpSpecConstantOp: + return true; + default: + return false; + } +} + +int32_t spvOpcodeIsConstant(const SpvOp opcode) { + switch (opcode) { + case SpvOpConstantTrue: + case SpvOpConstantFalse: + case SpvOpConstant: + case SpvOpConstantComposite: + case SpvOpConstantSampler: + case SpvOpConstantNull: + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + case SpvOpSpecConstant: + case SpvOpSpecConstantComposite: + case SpvOpSpecConstantOp: + return true; + default: + return false; + } +} + +bool spvOpcodeIsConstantOrUndef(const SpvOp opcode) { + return opcode == SpvOpUndef || spvOpcodeIsConstant(opcode); +} + +bool spvOpcodeIsScalarSpecConstant(const SpvOp opcode) { + switch (opcode) { + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + case SpvOpSpecConstant: + return true; + default: + return false; + } +} + +int32_t spvOpcodeIsComposite(const SpvOp opcode) { + switch (opcode) { + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeArray: + case SpvOpTypeStruct: + case SpvOpTypeCooperativeMatrixNV: + return true; + default: + return false; + } +} + +bool spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode) { + switch (opcode) { + case SpvOpVariable: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpFunctionParameter: + case SpvOpImageTexelPointer: + case SpvOpCopyObject: + case SpvOpSelect: + case SpvOpPhi: + case SpvOpFunctionCall: + case SpvOpPtrAccessChain: + case SpvOpLoad: + case SpvOpConstantNull: + return true; + default: + return false; + } +} + +int32_t spvOpcodeReturnsLogicalPointer(const SpvOp opcode) { + switch (opcode) { + case SpvOpVariable: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpFunctionParameter: + case SpvOpImageTexelPointer: + case SpvOpCopyObject: + return true; + default: + return false; + } +} + +int32_t spvOpcodeGeneratesType(SpvOp op) { + switch (op) { + case SpvOpTypeVoid: + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeImage: + case SpvOpTypeSampler: + case SpvOpTypeSampledImage: + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeStruct: + case SpvOpTypeOpaque: + case SpvOpTypePointer: + case SpvOpTypeFunction: + case SpvOpTypeEvent: + case SpvOpTypeDeviceEvent: + case SpvOpTypeReserveId: + case SpvOpTypeQueue: + case SpvOpTypePipe: + case SpvOpTypePipeStorage: + case SpvOpTypeNamedBarrier: + case SpvOpTypeAccelerationStructureNV: + case SpvOpTypeCooperativeMatrixNV: + // case SpvOpTypeAccelerationStructureKHR: covered by + // SpvOpTypeAccelerationStructureNV + case SpvOpTypeRayQueryProvisionalKHR: + return true; + default: + // In particular, OpTypeForwardPointer does not generate a type, + // but declares a storage class for a pointer type generated + // by a different instruction. + break; + } + return 0; +} + +bool spvOpcodeIsDecoration(const SpvOp opcode) { + switch (opcode) { + case SpvOpDecorate: + case SpvOpDecorateId: + case SpvOpMemberDecorate: + case SpvOpGroupDecorate: + case SpvOpGroupMemberDecorate: + case SpvOpDecorateStringGOOGLE: + case SpvOpMemberDecorateStringGOOGLE: + return true; + default: + break; + } + return false; +} + +bool spvOpcodeIsLoad(const SpvOp opcode) { + switch (opcode) { + case SpvOpLoad: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageFetch: + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImageRead: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseFetch: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + case SpvOpImageSparseRead: + return true; + default: + return false; + } +} + +bool spvOpcodeIsBranch(SpvOp opcode) { + switch (opcode) { + case SpvOpBranch: + case SpvOpBranchConditional: + case SpvOpSwitch: + return true; + default: + return false; + } +} + +bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode) { + switch (opcode) { + case SpvOpAtomicLoad: + case SpvOpAtomicExchange: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicFAddEXT: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFlagTestAndSet: + return true; + default: + return false; + } +} + +bool spvOpcodeIsAtomicOp(const SpvOp opcode) { + return (spvOpcodeIsAtomicWithLoad(opcode) || opcode == SpvOpAtomicStore || + opcode == SpvOpAtomicFlagClear); +} + +bool spvOpcodeIsReturn(SpvOp opcode) { + switch (opcode) { + case SpvOpReturn: + case SpvOpReturnValue: + return true; + default: + return false; + } +} + +bool spvOpcodeIsReturnOrAbort(SpvOp opcode) { + return spvOpcodeIsReturn(opcode) || opcode == SpvOpKill || + opcode == SpvOpUnreachable || opcode == SpvOpTerminateInvocation; +} + +bool spvOpcodeIsBlockTerminator(SpvOp opcode) { + return spvOpcodeIsBranch(opcode) || spvOpcodeIsReturnOrAbort(opcode); +} + +bool spvOpcodeIsBaseOpaqueType(SpvOp opcode) { + switch (opcode) { + case SpvOpTypeImage: + case SpvOpTypeSampler: + case SpvOpTypeSampledImage: + case SpvOpTypeOpaque: + case SpvOpTypeEvent: + case SpvOpTypeDeviceEvent: + case SpvOpTypeReserveId: + case SpvOpTypeQueue: + case SpvOpTypePipe: + case SpvOpTypeForwardPointer: + case SpvOpTypePipeStorage: + case SpvOpTypeNamedBarrier: + return true; + default: + return false; + } +} + +bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode) { + switch (opcode) { + case SpvOpGroupNonUniformElect: + case SpvOpGroupNonUniformAll: + case SpvOpGroupNonUniformAny: + case SpvOpGroupNonUniformAllEqual: + case SpvOpGroupNonUniformBroadcast: + case SpvOpGroupNonUniformBroadcastFirst: + case SpvOpGroupNonUniformBallot: + case SpvOpGroupNonUniformInverseBallot: + case SpvOpGroupNonUniformBallotBitExtract: + case SpvOpGroupNonUniformBallotBitCount: + case SpvOpGroupNonUniformBallotFindLSB: + case SpvOpGroupNonUniformBallotFindMSB: + case SpvOpGroupNonUniformShuffle: + case SpvOpGroupNonUniformShuffleXor: + case SpvOpGroupNonUniformShuffleUp: + case SpvOpGroupNonUniformShuffleDown: + case SpvOpGroupNonUniformIAdd: + case SpvOpGroupNonUniformFAdd: + case SpvOpGroupNonUniformIMul: + case SpvOpGroupNonUniformFMul: + case SpvOpGroupNonUniformSMin: + case SpvOpGroupNonUniformUMin: + case SpvOpGroupNonUniformFMin: + case SpvOpGroupNonUniformSMax: + case SpvOpGroupNonUniformUMax: + case SpvOpGroupNonUniformFMax: + case SpvOpGroupNonUniformBitwiseAnd: + case SpvOpGroupNonUniformBitwiseOr: + case SpvOpGroupNonUniformBitwiseXor: + case SpvOpGroupNonUniformLogicalAnd: + case SpvOpGroupNonUniformLogicalOr: + case SpvOpGroupNonUniformLogicalXor: + case SpvOpGroupNonUniformQuadBroadcast: + case SpvOpGroupNonUniformQuadSwap: + return true; + default: + return false; + } +} + +bool spvOpcodeIsScalarizable(SpvOp opcode) { + switch (opcode) { + case SpvOpPhi: + case SpvOpCopyObject: + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpUConvert: + case SpvOpSConvert: + case SpvOpFConvert: + case SpvOpQuantizeToF16: + case SpvOpVectorInsertDynamic: + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitFieldInsert: + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: + case SpvOpBitReverse: + case SpvOpBitCount: + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpLogicalNot: + case SpvOpSelect: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + return true; + default: + return false; + } +} + +bool spvOpcodeIsDebug(SpvOp opcode) { + switch (opcode) { + case SpvOpName: + case SpvOpMemberName: + case SpvOpSource: + case SpvOpSourceContinued: + case SpvOpSourceExtension: + case SpvOpString: + case SpvOpLine: + case SpvOpNoLine: + return true; + default: + return false; + } +} + +bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode) { + switch (opcode) { + case SpvOpPtrEqual: + case SpvOpPtrNotEqual: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpOrdered: + case SpvOpUnordered: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + return true; + default: + return false; + } +} + +bool spvOpcodeIsLinearAlgebra(SpvOp opcode) { + switch (opcode) { + case SpvOpTranspose: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + return true; + default: + return false; + } +} + +bool spvOpcodeIsImageSample(const SpvOp opcode) { + switch (opcode) { + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + return true; + default: + return false; + } +} + +std::vector spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode) { + switch (opcode) { + case SpvOpMemoryBarrier: + return {1}; + case SpvOpAtomicStore: + case SpvOpControlBarrier: + case SpvOpAtomicFlagClear: + case SpvOpMemoryNamedBarrier: + return {2}; + case SpvOpAtomicLoad: + case SpvOpAtomicExchange: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicFAddEXT: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFlagTestAndSet: + return {4}; + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + return {4, 5}; + default: + return {}; + } +} + +bool spvOpcodeIsAccessChain(SpvOp opcode) { + switch (opcode) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + return true; + default: + return false; + } +} + +bool spvOpcodeIsBit(SpvOp opcode) { + switch (opcode) { + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitReverse: + case SpvOpBitCount: + return true; + default: + return false; + } +} diff --git a/third_party/spirv-tools/source/opcode.h b/third_party/spirv-tools/source/opcode.h new file mode 100644 index 0000000..3702cb3 --- /dev/null +++ b/third_party/spirv-tools/source/opcode.h @@ -0,0 +1,153 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPCODE_H_ +#define SOURCE_OPCODE_H_ + +#include "source/instruction.h" +#include "source/latest_version_spirv_header.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +// Returns the name of a registered SPIR-V generator as a null-terminated +// string. If the generator is not known, then returns the string "Unknown". +// The generator parameter should be most significant 16-bits of the generator +// word in the SPIR-V module header. +// +// See the registry at https://www.khronos.org/registry/spir-v/api/spir-v.xml. +const char* spvGeneratorStr(uint32_t generator); + +// Combines word_count and opcode enumerant in single word. +uint32_t spvOpcodeMake(uint16_t word_count, SpvOp opcode); + +// Splits word into into two constituent parts: word_count and opcode. +void spvOpcodeSplit(const uint32_t word, uint16_t* word_count, + uint16_t* opcode); + +// Finds the named opcode in the given opcode table. On success, returns +// SPV_SUCCESS and writes a handle of the table entry into *entry. +spv_result_t spvOpcodeTableNameLookup(spv_target_env, + const spv_opcode_table table, + const char* name, spv_opcode_desc* entry); + +// Finds the opcode by enumerant in the given opcode table. On success, returns +// SPV_SUCCESS and writes a handle of the table entry into *entry. +spv_result_t spvOpcodeTableValueLookup(spv_target_env, + const spv_opcode_table table, + const SpvOp opcode, + spv_opcode_desc* entry); + +// Copies an instruction's word and fixes the endianness to host native. The +// source instruction's stream/opcode/endianness is in the words/opcode/endian +// parameter. The word_count parameter specifies the number of words to copy. +// Writes copied instruction into *inst. +void spvInstructionCopy(const uint32_t* words, const SpvOp opcode, + const uint16_t word_count, + const spv_endianness_t endian, spv_instruction_t* inst); + +// Determine if the given opcode is a scalar type. Returns zero if false, +// non-zero otherwise. +int32_t spvOpcodeIsScalarType(const SpvOp opcode); + +// Determines if the given opcode is a specialization constant. Returns zero if +// false, non-zero otherwise. +int32_t spvOpcodeIsSpecConstant(const SpvOp opcode); + +// Determines if the given opcode is a constant. Returns zero if false, non-zero +// otherwise. +int32_t spvOpcodeIsConstant(const SpvOp opcode); + +// Returns true if the given opcode is a constant or undef. +bool spvOpcodeIsConstantOrUndef(const SpvOp opcode); + +// Returns true if the given opcode is a scalar specialization constant. +bool spvOpcodeIsScalarSpecConstant(const SpvOp opcode); + +// Determines if the given opcode is a composite type. Returns zero if false, +// non-zero otherwise. +int32_t spvOpcodeIsComposite(const SpvOp opcode); + +// Determines if the given opcode results in a pointer when using the logical +// addressing model. Returns zero if false, non-zero otherwise. +int32_t spvOpcodeReturnsLogicalPointer(const SpvOp opcode); + +// Returns whether the given opcode could result in a pointer or a variable +// pointer when using the logical addressing model. +bool spvOpcodeReturnsLogicalVariablePointer(const SpvOp opcode); + +// Determines if the given opcode generates a type. Returns zero if false, +// non-zero otherwise. +int32_t spvOpcodeGeneratesType(SpvOp opcode); + +// Returns true if the opcode adds a decoration to an id. +bool spvOpcodeIsDecoration(const SpvOp opcode); + +// Returns true if the opcode is a load from memory into a result id. This +// function only considers core instructions. +bool spvOpcodeIsLoad(const SpvOp opcode); + +// Returns true if the opcode is an atomic operation that uses the original +// value. +bool spvOpcodeIsAtomicWithLoad(const SpvOp opcode); + +// Returns true if the opcode is an atomic operation. +bool spvOpcodeIsAtomicOp(const SpvOp opcode); + +// Returns true if the given opcode is a branch instruction. +bool spvOpcodeIsBranch(SpvOp opcode); + +// Returns true if the given opcode is a return instruction. +bool spvOpcodeIsReturn(SpvOp opcode); + +// Returns true if the given opcode is a return instruction or it aborts +// execution. +bool spvOpcodeIsReturnOrAbort(SpvOp opcode); + +// Returns true if the given opcode is a basic block terminator. +bool spvOpcodeIsBlockTerminator(SpvOp opcode); + +// Returns true if the given opcode always defines an opaque type. +bool spvOpcodeIsBaseOpaqueType(SpvOp opcode); + +// Returns true if the given opcode is a non-uniform group operation. +bool spvOpcodeIsNonUniformGroupOperation(SpvOp opcode); + +// Returns true if the opcode with vector inputs could be divided into a series +// of independent scalar operations that would give the same result. +bool spvOpcodeIsScalarizable(SpvOp opcode); + +// Returns true if the given opcode is a debug instruction. +bool spvOpcodeIsDebug(SpvOp opcode); + +// Returns true for opcodes that are binary operators, +// where the order of the operands is irrelevant. +bool spvOpcodeIsCommutativeBinaryOperator(SpvOp opcode); + +// Returns true for opcodes that represent linear algebra instructions. +bool spvOpcodeIsLinearAlgebra(SpvOp opcode); + +// Returns true for opcodes that represent image sample instructions. +bool spvOpcodeIsImageSample(SpvOp opcode); + +// Returns a vector containing the indices of the memory semantics +// operands for |opcode|. +std::vector spvOpcodeMemorySemanticsOperandIndices(SpvOp opcode); + +// Returns true for opcodes that represent access chain instructions. +bool spvOpcodeIsAccessChain(SpvOp opcode); + +// Returns true for opcodes that represent bit instructions. +bool spvOpcodeIsBit(SpvOp opcode); + +#endif // SOURCE_OPCODE_H_ diff --git a/third_party/spirv-tools/source/operand.cpp b/third_party/spirv-tools/source/operand.cpp new file mode 100644 index 0000000..d4b64a8 --- /dev/null +++ b/third_party/spirv-tools/source/operand.cpp @@ -0,0 +1,588 @@ +// Copyright (c) 2015-2020 The Khronos Group Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/operand.h" + +#include +#include + +#include + +#include "DebugInfo.h" +#include "OpenCLDebugInfo100.h" +#include "source/macro.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" + +// For now, assume unified1 contains up to SPIR-V 1.3 and no later +// SPIR-V version. +// TODO(dneto): Make one set of tables, but with version tags on a +// per-item basis. https://github.com/KhronosGroup/SPIRV-Tools/issues/1195 + +#include "operand.kinds-unified1.inc" +#include "spirv-tools/libspirv.h" + +static const spv_operand_table_t kOperandTable = { + ARRAY_SIZE(pygen_variable_OperandInfoTable), + pygen_variable_OperandInfoTable}; + +spv_result_t spvOperandTableGet(spv_operand_table* pOperandTable, + spv_target_env) { + if (!pOperandTable) return SPV_ERROR_INVALID_POINTER; + + *pOperandTable = &kOperandTable; + return SPV_SUCCESS; +} + +spv_result_t spvOperandTableNameLookup(spv_target_env env, + const spv_operand_table table, + const spv_operand_type_t type, + const char* name, + const size_t nameLength, + spv_operand_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!name || !pEntry) return SPV_ERROR_INVALID_POINTER; + + const auto version = spvVersionForTargetEnv(env); + for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) { + const auto& group = table->types[typeIndex]; + if (type != group.type) continue; + for (uint64_t index = 0; index < group.count; ++index) { + const auto& entry = group.entries[index]; + // We consider the current operand as available as long as + // 1. The target environment satisfies the minimal requirement of the + // operand; or + // 2. There is at least one extension enabling this operand; or + // 3. There is at least one capability enabling this operand. + // + // Note that the second rule assumes the extension enabling this operand + // is indeed requested in the SPIR-V code; checking that should be + // validator's work. + if (((version >= entry.minVersion && version <= entry.lastVersion) || + entry.numExtensions > 0u || entry.numCapabilities > 0u) && + nameLength == strlen(entry.name) && + !strncmp(entry.name, name, nameLength)) { + *pEntry = &entry; + return SPV_SUCCESS; + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +spv_result_t spvOperandTableValueLookup(spv_target_env env, + const spv_operand_table table, + const spv_operand_type_t type, + const uint32_t value, + spv_operand_desc* pEntry) { + if (!table) return SPV_ERROR_INVALID_TABLE; + if (!pEntry) return SPV_ERROR_INVALID_POINTER; + + spv_operand_desc_t needle = {"", value, 0, nullptr, 0, nullptr, {}, ~0u, ~0u}; + + auto comp = [](const spv_operand_desc_t& lhs, const spv_operand_desc_t& rhs) { + return lhs.value < rhs.value; + }; + + for (uint64_t typeIndex = 0; typeIndex < table->count; ++typeIndex) { + const auto& group = table->types[typeIndex]; + if (type != group.type) continue; + + const auto beg = group.entries; + const auto end = group.entries + group.count; + + // We need to loop here because there can exist multiple symbols for the + // same operand value, and they can be introduced in different target + // environments, which means they can have different minimal version + // requirements. For example, SubgroupEqMaskKHR can exist in any SPIR-V + // version as long as the SPV_KHR_shader_ballot extension is there; but + // starting from SPIR-V 1.3, SubgroupEqMask, which has the same numeric + // value as SubgroupEqMaskKHR, is available in core SPIR-V without extension + // requirements. + // Assumes the underlying table is already sorted ascendingly according to + // opcode value. + const auto version = spvVersionForTargetEnv(env); + for (auto it = std::lower_bound(beg, end, needle, comp); + it != end && it->value == value; ++it) { + // We consider the current operand as available as long as + // 1. The target environment satisfies the minimal requirement of the + // operand; or + // 2. There is at least one extension enabling this operand; or + // 3. There is at least one capability enabling this operand. + // + // Note that the second rule assumes the extension enabling this operand + // is indeed requested in the SPIR-V code; checking that should be + // validator's work. + if ((version >= it->minVersion && version <= it->lastVersion) || + it->numExtensions > 0u || it->numCapabilities > 0u) { + *pEntry = it; + return SPV_SUCCESS; + } + } + } + + return SPV_ERROR_INVALID_LOOKUP; +} + +const char* spvOperandTypeStr(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_OPTIONAL_ID: + return "ID"; + case SPV_OPERAND_TYPE_TYPE_ID: + return "type ID"; + case SPV_OPERAND_TYPE_RESULT_ID: + return "result ID"; + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: + return "literal number"; + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + return "possibly multi-word literal integer"; + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + return "possibly multi-word literal number"; + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: + return "extension instruction number"; + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: + return "OpSpecConstantOp opcode"; + case SPV_OPERAND_TYPE_LITERAL_STRING: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: + return "literal string"; + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: + return "source language"; + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + return "execution model"; + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + return "addressing model"; + case SPV_OPERAND_TYPE_MEMORY_MODEL: + return "memory model"; + case SPV_OPERAND_TYPE_EXECUTION_MODE: + return "execution mode"; + case SPV_OPERAND_TYPE_STORAGE_CLASS: + return "storage class"; + case SPV_OPERAND_TYPE_DIMENSIONALITY: + return "dimensionality"; + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + return "sampler addressing mode"; + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + return "sampler filter mode"; + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + return "image format"; + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + return "floating-point fast math mode"; + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + return "floating-point rounding mode"; + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + return "linkage type"; + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: + return "access qualifier"; + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + return "function parameter attribute"; + case SPV_OPERAND_TYPE_DECORATION: + return "decoration"; + case SPV_OPERAND_TYPE_BUILT_IN: + return "built-in"; + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + return "selection control"; + case SPV_OPERAND_TYPE_LOOP_CONTROL: + return "loop control"; + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + return "function control"; + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + return "memory semantics ID"; + case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + return "memory access"; + case SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE: + return "shading rate"; + case SPV_OPERAND_TYPE_SCOPE_ID: + return "scope ID"; + case SPV_OPERAND_TYPE_GROUP_OPERATION: + return "group operation"; + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + return "kernel enqeue flags"; + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + return "kernel profiling info"; + case SPV_OPERAND_TYPE_CAPABILITY: + return "capability"; + case SPV_OPERAND_TYPE_RAY_FLAGS: + return "ray flags"; + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + return "ray query intersection"; + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + return "ray query committed intersection type"; + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + return "ray query candidate intersection type"; + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + return "image"; + case SPV_OPERAND_TYPE_OPTIONAL_CIV: + return "context-insensitive value"; + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + return "debug info flags"; + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + return "debug base type encoding"; + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + return "debug composite type"; + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + return "debug type qualifier"; + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + return "debug operation"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + return "OpenCL.DebugInfo.100 debug info flags"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + return "OpenCL.DebugInfo.100 debug base type encoding"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + return "OpenCL.DebugInfo.100 debug composite type"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + return "OpenCL.DebugInfo.100 debug type qualifier"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + return "OpenCL.DebugInfo.100 debug operation"; + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + return "OpenCL.DebugInfo.100 debug imported entity"; + + // The next values are for values returned from an instruction, not actually + // an operand. So the specific strings don't matter. But let's add them + // for completeness and ease of testing. + case SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER: + return "image channel order"; + case SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE: + return "image channel data type"; + + case SPV_OPERAND_TYPE_NONE: + return "NONE"; + default: + break; + } + return "unknown"; +} + +void spvPushOperandTypes(const spv_operand_type_t* types, + spv_operand_pattern_t* pattern) { + const spv_operand_type_t* endTypes; + for (endTypes = types; *endTypes != SPV_OPERAND_TYPE_NONE; ++endTypes) { + } + + while (endTypes-- != types) { + pattern->push_back(*endTypes); + } +} + +void spvPushOperandTypesForMask(spv_target_env env, + const spv_operand_table operandTable, + const spv_operand_type_t type, + const uint32_t mask, + spv_operand_pattern_t* pattern) { + // Scan from highest bits to lowest bits because we will append in LIFO + // fashion, and we need the operands for lower order bits to be consumed first + for (uint32_t candidate_bit = (1u << 31u); candidate_bit; + candidate_bit >>= 1) { + if (candidate_bit & mask) { + spv_operand_desc entry = nullptr; + if (SPV_SUCCESS == spvOperandTableValueLookup(env, operandTable, type, + candidate_bit, &entry)) { + spvPushOperandTypes(entry->operandTypes, pattern); + } + } + } +} + +bool spvOperandIsConcrete(spv_operand_type_t type) { + if (spvIsIdType(type) || spvOperandIsConcreteMask(type)) { + return true; + } + switch (type) { + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_LITERAL_STRING: + case SPV_OPERAND_TYPE_SOURCE_LANGUAGE: + case SPV_OPERAND_TYPE_EXECUTION_MODEL: + case SPV_OPERAND_TYPE_ADDRESSING_MODEL: + case SPV_OPERAND_TYPE_MEMORY_MODEL: + case SPV_OPERAND_TYPE_EXECUTION_MODE: + case SPV_OPERAND_TYPE_STORAGE_CLASS: + case SPV_OPERAND_TYPE_DIMENSIONALITY: + case SPV_OPERAND_TYPE_SAMPLER_ADDRESSING_MODE: + case SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE: + case SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT: + case SPV_OPERAND_TYPE_IMAGE_CHANNEL_ORDER: + case SPV_OPERAND_TYPE_IMAGE_CHANNEL_DATA_TYPE: + case SPV_OPERAND_TYPE_FP_ROUNDING_MODE: + case SPV_OPERAND_TYPE_LINKAGE_TYPE: + case SPV_OPERAND_TYPE_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE: + case SPV_OPERAND_TYPE_DECORATION: + case SPV_OPERAND_TYPE_BUILT_IN: + case SPV_OPERAND_TYPE_GROUP_OPERATION: + case SPV_OPERAND_TYPE_KERNEL_ENQ_FLAGS: + case SPV_OPERAND_TYPE_KERNEL_PROFILING_INFO: + case SPV_OPERAND_TYPE_CAPABILITY: + case SPV_OPERAND_TYPE_RAY_FLAGS: + case SPV_OPERAND_TYPE_RAY_QUERY_INTERSECTION: + case SPV_OPERAND_TYPE_RAY_QUERY_COMMITTED_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_RAY_QUERY_CANDIDATE_INTERSECTION_TYPE: + case SPV_OPERAND_TYPE_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_BASE_TYPE_ATTRIBUTE_ENCODING: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_COMPOSITE_TYPE: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_TYPE_QUALIFIER: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_IMPORTED_ENTITY: + return true; + default: + break; + } + return false; +} + +bool spvOperandIsConcreteMask(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_FRAGMENT_SHADING_RATE: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: + return true; + default: + break; + } + return false; +} + +bool spvOperandIsOptional(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_OPTIONAL_ID: + case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: + case SPV_OPERAND_TYPE_OPTIONAL_ACCESS_QUALIFIER: + case SPV_OPERAND_TYPE_OPTIONAL_CIV: + return true; + default: + break; + } + // Any variable operand is also optional. + return spvOperandIsVariable(type); +} + +bool spvOperandIsVariable(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_VARIABLE_ID: + case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID: + case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER: + return true; + default: + break; + } + return false; +} + +bool spvExpandOperandSequenceOnce(spv_operand_type_t type, + spv_operand_pattern_t* pattern) { + switch (type) { + case SPV_OPERAND_TYPE_VARIABLE_ID: + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_ID); + return true; + case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER: + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER); + return true; + case SPV_OPERAND_TYPE_VARIABLE_LITERAL_INTEGER_ID: + // Represents Zero or more (Literal number, Id) pairs, + // where the literal number must be a scalar integer. + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_ID); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER); + return true; + case SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER: + // Represents Zero or more (Id, Literal number) pairs. + pattern->push_back(type); + pattern->push_back(SPV_OPERAND_TYPE_LITERAL_INTEGER); + pattern->push_back(SPV_OPERAND_TYPE_OPTIONAL_ID); + return true; + default: + break; + } + return false; +} + +spv_operand_type_t spvTakeFirstMatchableOperand( + spv_operand_pattern_t* pattern) { + assert(!pattern->empty()); + spv_operand_type_t result; + do { + result = pattern->back(); + pattern->pop_back(); + } while (spvExpandOperandSequenceOnce(result, pattern)); + return result; +} + +spv_operand_pattern_t spvAlternatePatternFollowingImmediate( + const spv_operand_pattern_t& pattern) { + auto it = + std::find(pattern.crbegin(), pattern.crend(), SPV_OPERAND_TYPE_RESULT_ID); + if (it != pattern.crend()) { + spv_operand_pattern_t alternatePattern(it - pattern.crbegin() + 2, + SPV_OPERAND_TYPE_OPTIONAL_CIV); + alternatePattern[1] = SPV_OPERAND_TYPE_RESULT_ID; + return alternatePattern; + } + + // No result-id found, so just expect CIVs. + return {SPV_OPERAND_TYPE_OPTIONAL_CIV}; +} + +bool spvIsIdType(spv_operand_type_t type) { + switch (type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + return true; + default: + return false; + } +} + +bool spvIsInIdType(spv_operand_type_t type) { + if (!spvIsIdType(type)) { + // If it is not an ID it cannot be an input ID. + return false; + } + switch (type) { + // Deny non-input IDs. + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + return false; + default: + return true; + } +} + +std::function spvOperandCanBeForwardDeclaredFunction( + SpvOp opcode) { + std::function out; + switch (opcode) { + case SpvOpExecutionMode: + case SpvOpExecutionModeId: + case SpvOpEntryPoint: + case SpvOpName: + case SpvOpMemberName: + case SpvOpSelectionMerge: + case SpvOpDecorate: + case SpvOpMemberDecorate: + case SpvOpDecorateId: + case SpvOpDecorateStringGOOGLE: + case SpvOpMemberDecorateStringGOOGLE: + case SpvOpTypeStruct: + case SpvOpBranch: + case SpvOpLoopMerge: + out = [](unsigned) { return true; }; + break; + case SpvOpGroupDecorate: + case SpvOpGroupMemberDecorate: + case SpvOpBranchConditional: + case SpvOpSwitch: + out = [](unsigned index) { return index != 0; }; + break; + + case SpvOpFunctionCall: + // The Function parameter. + out = [](unsigned index) { return index == 2; }; + break; + + case SpvOpPhi: + out = [](unsigned index) { return index > 1; }; + break; + + case SpvOpEnqueueKernel: + // The Invoke parameter. + out = [](unsigned index) { return index == 8; }; + break; + + case SpvOpGetKernelNDrangeSubGroupCount: + case SpvOpGetKernelNDrangeMaxSubGroupSize: + // The Invoke parameter. + out = [](unsigned index) { return index == 3; }; + break; + + case SpvOpGetKernelWorkGroupSize: + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: + // The Invoke parameter. + out = [](unsigned index) { return index == 2; }; + break; + case SpvOpTypeForwardPointer: + out = [](unsigned index) { return index == 0; }; + break; + case SpvOpTypeArray: + out = [](unsigned index) { return index == 1; }; + break; + default: + out = [](unsigned) { return false; }; + break; + } + return out; +} + +std::function spvDbgInfoExtOperandCanBeForwardDeclaredFunction( + spv_ext_inst_type_t ext_type, uint32_t key) { + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/532): Forward + // references for debug info instructions are still in discussion. We must + // update the following lines of code when we conclude the spec. + std::function out; + if (ext_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + switch (OpenCLDebugInfo100Instructions(key)) { + case OpenCLDebugInfo100DebugFunction: + out = [](unsigned index) { return index == 13; }; + break; + case OpenCLDebugInfo100DebugTypeComposite: + out = [](unsigned index) { return index >= 13; }; + break; + default: + out = [](unsigned) { return false; }; + break; + } + } else { + switch (DebugInfoInstructions(key)) { + case DebugInfoDebugFunction: + out = [](unsigned index) { return index == 13; }; + break; + case DebugInfoDebugTypeComposite: + out = [](unsigned index) { return index >= 12; }; + break; + default: + out = [](unsigned) { return false; }; + break; + } + } + return out; +} diff --git a/third_party/spirv-tools/source/operand.h b/third_party/spirv-tools/source/operand.h new file mode 100644 index 0000000..7c73c6f --- /dev/null +++ b/third_party/spirv-tools/source/operand.h @@ -0,0 +1,151 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPERAND_H_ +#define SOURCE_OPERAND_H_ + +#include +#include + +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +// A sequence of operand types. +// +// A SPIR-V parser uses an operand pattern to describe what is expected +// next on the input. +// +// As we parse an instruction in text or binary form from left to right, +// we pop and push at the end of the pattern vector. Symbols later in the +// pattern vector are matched against the input before symbols earlier in the +// pattern vector are matched. + +// Using a vector in this way reduces memory traffic, which is good for +// performance. +using spv_operand_pattern_t = std::vector; + +// Finds the named operand in the table. The type parameter specifies the +// operand's group. A handle of the operand table entry for this operand will +// be written into *entry. +spv_result_t spvOperandTableNameLookup(spv_target_env, + const spv_operand_table table, + const spv_operand_type_t type, + const char* name, + const size_t name_length, + spv_operand_desc* entry); + +// Finds the operand with value in the table. The type parameter specifies the +// operand's group. A handle of the operand table entry for this operand will +// be written into *entry. +spv_result_t spvOperandTableValueLookup(spv_target_env, + const spv_operand_table table, + const spv_operand_type_t type, + const uint32_t value, + spv_operand_desc* entry); + +// Gets the name string of the non-variable operand type. +const char* spvOperandTypeStr(spv_operand_type_t type); + +// Returns true if the given type is concrete. +bool spvOperandIsConcrete(spv_operand_type_t type); + +// Returns true if the given type is concrete and also a mask. +bool spvOperandIsConcreteMask(spv_operand_type_t type); + +// Returns true if an operand of the given type is optional. +bool spvOperandIsOptional(spv_operand_type_t type); + +// Returns true if an operand type represents zero or more logical operands. +// +// Note that a single logical operand may still be a variable number of words. +// For example, a literal string may be many words, but is just one logical +// operand. +bool spvOperandIsVariable(spv_operand_type_t type); + +// Append a list of operand types to the end of the pattern vector. +// The types parameter specifies the source array of types, ending with +// SPV_OPERAND_TYPE_NONE. +void spvPushOperandTypes(const spv_operand_type_t* types, + spv_operand_pattern_t* pattern); + +// Appends the operands expected after the given typed mask onto the +// end of the given pattern. +// +// Each set bit in the mask represents zero or more operand types that should +// be appended onto the pattern. Operands for a less significant bit always +// appear after operands for a more significant bit. +// +// If a set bit is unknown, then we assume it has no operands. +void spvPushOperandTypesForMask(spv_target_env, + const spv_operand_table operand_table, + const spv_operand_type_t mask_type, + const uint32_t mask, + spv_operand_pattern_t* pattern); + +// Expands an operand type representing zero or more logical operands, +// exactly once. +// +// If the given type represents potentially several logical operands, +// then prepend the given pattern with the first expansion of the logical +// operands, followed by original type. Otherwise, don't modify the pattern. +// +// For example, the SPV_OPERAND_TYPE_VARIABLE_ID represents zero or more +// IDs. In that case we would prepend the pattern with SPV_OPERAND_TYPE_ID +// followed by SPV_OPERAND_TYPE_VARIABLE_ID again. +// +// This also applies to zero or more tuples of logical operands. In that case +// we prepend pattern with for the members of the tuple, followed by the +// original type argument. The pattern must encode the fact that if any part +// of the tuple is present, then all tuple members should be. So the first +// member of the tuple must be optional, and the remaining members +// non-optional. +// +// Returns true if we modified the pattern. +bool spvExpandOperandSequenceOnce(spv_operand_type_t type, + spv_operand_pattern_t* pattern); + +// Expands the first element in the pattern until it is a matchable operand +// type, then pops it off the front and returns it. The pattern must not be +// empty. +// +// A matchable operand type is anything other than a zero-or-more-items +// operand type. +spv_operand_type_t spvTakeFirstMatchableOperand(spv_operand_pattern_t* pattern); + +// Calculates the corresponding post-immediate alternate pattern, which allows +// a limited set of operand types. +spv_operand_pattern_t spvAlternatePatternFollowingImmediate( + const spv_operand_pattern_t& pattern); + +// Is the operand an ID? +bool spvIsIdType(spv_operand_type_t type); + +// Is the operand an input ID? +bool spvIsInIdType(spv_operand_type_t type); + +// Takes the opcode of an instruction and returns +// a function object that will return true if the index +// of the operand can be forward declared. This function will +// used in the SSA validation stage of the pipeline +std::function spvOperandCanBeForwardDeclaredFunction( + SpvOp opcode); + +// Takes the instruction key of a debug info extension instruction +// and returns a function object that will return true if the index +// of the operand can be forward declared. This function will +// used in the SSA validation stage of the pipeline +std::function spvDbgInfoExtOperandCanBeForwardDeclaredFunction( + spv_ext_inst_type_t ext_type, uint32_t key); + +#endif // SOURCE_OPERAND_H_ diff --git a/third_party/spirv-tools/source/opt/CMakeLists.txt b/third_party/spirv-tools/source/opt/CMakeLists.txt new file mode 100644 index 0000000..f3ac590 --- /dev/null +++ b/third_party/spirv-tools/source/opt/CMakeLists.txt @@ -0,0 +1,265 @@ +# Copyright (c) 2016 Google Inc. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SPIRV_TOOLS_OPT_SOURCES + aggressive_dead_code_elim_pass.h + amd_ext_to_khr.h + basic_block.h + block_merge_pass.h + block_merge_util.h + build_module.h + ccp_pass.h + cfg_cleanup_pass.h + cfg.h + code_sink.h + combine_access_chains.h + compact_ids_pass.h + composite.h + const_folding_rules.h + constants.h + convert_to_half_pass.h + copy_prop_arrays.h + dead_branch_elim_pass.h + dead_insert_elim_pass.h + dead_variable_elimination.h + decompose_initialized_variables_pass.h + decoration_manager.h + debug_info_manager.h + def_use_manager.h + desc_sroa.h + dominator_analysis.h + dominator_tree.h + eliminate_dead_constant_pass.h + eliminate_dead_functions_pass.h + eliminate_dead_functions_util.h + eliminate_dead_members_pass.h + empty_pass.h + feature_manager.h + fix_storage_class.h + flatten_decoration_pass.h + fold.h + folding_rules.h + fold_spec_constant_op_and_composite_pass.h + freeze_spec_constant_value_pass.h + function.h + generate_webgpu_initializers_pass.h + graphics_robust_access_pass.h + if_conversion.h + inline_exhaustive_pass.h + inline_opaque_pass.h + inline_pass.h + inst_bindless_check_pass.h + inst_buff_addr_check_pass.h + inst_debug_printf_pass.h + instruction.h + instruction_list.h + instrument_pass.h + ir_builder.h + ir_context.h + ir_loader.h + licm_pass.h + local_access_chain_convert_pass.h + local_redundancy_elimination.h + local_single_block_elim_pass.h + local_single_store_elim_pass.h + log.h + loop_dependence.h + loop_descriptor.h + loop_fission.h + loop_fusion.h + loop_fusion_pass.h + loop_peeling.h + loop_unroller.h + loop_utils.h + loop_unswitch_pass.h + mem_pass.h + merge_return_pass.h + module.h + null_pass.h + passes.h + pass.h + pass_manager.h + private_to_local_pass.h + propagator.h + reduce_load_size.h + redundancy_elimination.h + reflect.h + register_pressure.h + relax_float_ops_pass.h + remove_duplicates_pass.h + replace_invalid_opc.h + scalar_analysis.h + scalar_analysis_nodes.h + scalar_replacement_pass.h + set_spec_constant_default_value_pass.h + simplification_pass.h + split_invalid_unreachable_pass.h + ssa_rewrite_pass.h + strength_reduction_pass.h + strip_atomic_counter_memory_pass.h + strip_debug_info_pass.h + strip_reflect_info_pass.h + struct_cfg_analysis.h + tree_iterator.h + type_manager.h + types.h + unify_const_pass.h + upgrade_memory_model.h + value_number_table.h + vector_dce.h + workaround1209.h + wrap_opkill.h + + aggressive_dead_code_elim_pass.cpp + amd_ext_to_khr.cpp + basic_block.cpp + block_merge_pass.cpp + block_merge_util.cpp + build_module.cpp + ccp_pass.cpp + cfg_cleanup_pass.cpp + cfg.cpp + code_sink.cpp + combine_access_chains.cpp + compact_ids_pass.cpp + composite.cpp + const_folding_rules.cpp + constants.cpp + convert_to_half_pass.cpp + copy_prop_arrays.cpp + dead_branch_elim_pass.cpp + dead_insert_elim_pass.cpp + dead_variable_elimination.cpp + decompose_initialized_variables_pass.cpp + decoration_manager.cpp + debug_info_manager.cpp + def_use_manager.cpp + desc_sroa.cpp + dominator_analysis.cpp + dominator_tree.cpp + eliminate_dead_constant_pass.cpp + eliminate_dead_functions_pass.cpp + eliminate_dead_functions_util.cpp + eliminate_dead_members_pass.cpp + feature_manager.cpp + fix_storage_class.cpp + flatten_decoration_pass.cpp + fold.cpp + folding_rules.cpp + fold_spec_constant_op_and_composite_pass.cpp + freeze_spec_constant_value_pass.cpp + function.cpp + graphics_robust_access_pass.cpp + generate_webgpu_initializers_pass.cpp + if_conversion.cpp + inline_exhaustive_pass.cpp + inline_opaque_pass.cpp + inline_pass.cpp + inst_bindless_check_pass.cpp + inst_buff_addr_check_pass.cpp + inst_debug_printf_pass.cpp + instruction.cpp + instruction_list.cpp + instrument_pass.cpp + ir_context.cpp + ir_loader.cpp + legalize_vector_shuffle_pass.cpp + licm_pass.cpp + local_access_chain_convert_pass.cpp + local_redundancy_elimination.cpp + local_single_block_elim_pass.cpp + local_single_store_elim_pass.cpp + loop_dependence.cpp + loop_dependence_helpers.cpp + loop_descriptor.cpp + loop_fission.cpp + loop_fusion.cpp + loop_fusion_pass.cpp + loop_peeling.cpp + loop_utils.cpp + loop_unroller.cpp + loop_unswitch_pass.cpp + mem_pass.cpp + merge_return_pass.cpp + module.cpp + optimizer.cpp + pass.cpp + pass_manager.cpp + private_to_local_pass.cpp + propagator.cpp + reduce_load_size.cpp + redundancy_elimination.cpp + register_pressure.cpp + relax_float_ops_pass.cpp + remove_duplicates_pass.cpp + replace_invalid_opc.cpp + scalar_analysis.cpp + scalar_analysis_simplification.cpp + scalar_replacement_pass.cpp + set_spec_constant_default_value_pass.cpp + simplification_pass.cpp + split_invalid_unreachable_pass.cpp + ssa_rewrite_pass.cpp + strength_reduction_pass.cpp + strip_atomic_counter_memory_pass.cpp + strip_debug_info_pass.cpp + strip_reflect_info_pass.cpp + struct_cfg_analysis.cpp + type_manager.cpp + types.cpp + unify_const_pass.cpp + upgrade_memory_model.cpp + value_number_table.cpp + vector_dce.cpp + workaround1209.cpp + wrap_opkill.cpp +) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) +endif() + +spvtools_pch(SPIRV_TOOLS_OPT_SOURCES pch_source_opt) + +add_library(SPIRV-Tools-opt ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_OPT_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-opt) +target_include_directories(SPIRV-Tools-opt + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# We need the assembling and disassembling functionalities in the main library. +target_link_libraries(SPIRV-Tools-opt + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY}) + +set_property(TARGET SPIRV-Tools-opt PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-opt) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-opt EXPORT SPIRV-Tools-optTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake) + + spvtools_config_package_dir(SPIRV-Tools-opt PACKAGE_DIR) + install(EXPORT SPIRV-Tools-optTargets FILE SPIRV-Tools-optTargets.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-opt) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-optConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp b/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp new file mode 100644 index 0000000..39d468f --- /dev/null +++ b/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.cpp @@ -0,0 +1,1001 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/aggressive_dead_code_elim_pass.h" + +#include +#include + +#include "source/cfa.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/opt/eliminate_dead_functions_util.h" +#include "source/opt/iterator.h" +#include "source/opt/reflect.h" +#include "source/spirv_constant.h" + +namespace spvtools { +namespace opt { + +namespace { + +const uint32_t kTypePointerStorageClassInIdx = 0; +const uint32_t kEntryPointFunctionIdInIdx = 1; +const uint32_t kSelectionMergeMergeBlockIdInIdx = 0; +const uint32_t kLoopMergeMergeBlockIdInIdx = 0; +const uint32_t kLoopMergeContinueBlockIdInIdx = 1; +const uint32_t kCopyMemoryTargetAddrInIdx = 0; +const uint32_t kCopyMemorySourceAddrInIdx = 1; +const uint32_t kDebugDeclareOperandVariableIndex = 5; +const uint32_t kGlobalVariableVariableIndex = 12; + +// Sorting functor to present annotation instructions in an easy-to-process +// order. The functor orders by opcode first and falls back on unique id +// ordering if both instructions have the same opcode. +// +// Desired priority: +// SpvOpGroupDecorate +// SpvOpGroupMemberDecorate +// SpvOpDecorate +// SpvOpMemberDecorate +// SpvOpDecorateId +// SpvOpDecorateStringGOOGLE +// SpvOpDecorationGroup +struct DecorationLess { + bool operator()(const Instruction* lhs, const Instruction* rhs) const { + assert(lhs && rhs); + SpvOp lhsOp = lhs->opcode(); + SpvOp rhsOp = rhs->opcode(); + if (lhsOp != rhsOp) { +#define PRIORITY_CASE(opcode) \ + if (lhsOp == opcode && rhsOp != opcode) return true; \ + if (rhsOp == opcode && lhsOp != opcode) return false; + // OpGroupDecorate and OpGroupMember decorate are highest priority to + // eliminate dead targets early and simplify subsequent checks. + PRIORITY_CASE(SpvOpGroupDecorate) + PRIORITY_CASE(SpvOpGroupMemberDecorate) + PRIORITY_CASE(SpvOpDecorate) + PRIORITY_CASE(SpvOpMemberDecorate) + PRIORITY_CASE(SpvOpDecorateId) + PRIORITY_CASE(SpvOpDecorateStringGOOGLE) + // OpDecorationGroup is lowest priority to ensure use/def chains remain + // usable for instructions that target this group. + PRIORITY_CASE(SpvOpDecorationGroup) +#undef PRIORITY_CASE + } + + // Fall back to maintain total ordering (compare unique ids). + return *lhs < *rhs; + } +}; + +} // namespace + +bool AggressiveDCEPass::IsVarOfStorage(uint32_t varId, uint32_t storageClass) { + if (varId == 0) return false; + const Instruction* varInst = get_def_use_mgr()->GetDef(varId); + const SpvOp op = varInst->opcode(); + if (op != SpvOpVariable) return false; + const uint32_t varTypeId = varInst->type_id(); + const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); + if (varTypeInst->opcode() != SpvOpTypePointer) return false; + return varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) == + storageClass; +} + +bool AggressiveDCEPass::IsLocalVar(uint32_t varId) { + if (IsVarOfStorage(varId, SpvStorageClassFunction)) { + return true; + } + if (!private_like_local_) { + return false; + } + + return IsVarOfStorage(varId, SpvStorageClassPrivate) || + IsVarOfStorage(varId, SpvStorageClassWorkgroup); +} + +void AggressiveDCEPass::AddStores(Function* func, uint32_t ptrId) { + get_def_use_mgr()->ForEachUser(ptrId, [this, ptrId, func](Instruction* user) { + // If the user is not a part of |func|, skip it. + BasicBlock* blk = context()->get_instr_block(user); + if (blk && blk->GetParent() != func) return; + + switch (user->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpCopyObject: + this->AddStores(func, user->result_id()); + break; + case SpvOpLoad: + break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + if (user->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx) == ptrId) { + AddToWorklist(user); + } + break; + // If default, assume it stores e.g. frexp, modf, function call + case SpvOpStore: + default: + AddToWorklist(user); + break; + } + }); +} + +bool AggressiveDCEPass::AllExtensionsSupported() const { + // If any extension not in allowlist, return false + for (auto& ei : get_module()->extensions()) { + const char* extName = + reinterpret_cast(&ei.GetInOperand(0).words[0]); + if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) + return false; + } + return true; +} + +bool AggressiveDCEPass::IsDead(Instruction* inst) { + if (IsLive(inst)) return false; + if ((inst->IsBranch() || inst->opcode() == SpvOpUnreachable) && + !IsStructuredHeader(context()->get_instr_block(inst), nullptr, nullptr, + nullptr)) + return false; + return true; +} + +bool AggressiveDCEPass::IsTargetDead(Instruction* inst) { + const uint32_t tId = inst->GetSingleWordInOperand(0); + Instruction* tInst = get_def_use_mgr()->GetDef(tId); + if (IsAnnotationInst(tInst->opcode())) { + // This must be a decoration group. We go through annotations in a specific + // order. So if this is not used by any group or group member decorates, it + // is dead. + assert(tInst->opcode() == SpvOpDecorationGroup); + bool dead = true; + get_def_use_mgr()->ForEachUser(tInst, [&dead](Instruction* user) { + if (user->opcode() == SpvOpGroupDecorate || + user->opcode() == SpvOpGroupMemberDecorate) + dead = false; + }); + return dead; + } + return IsDead(tInst); +} + +void AggressiveDCEPass::ProcessLoad(Function* func, uint32_t varId) { + // Only process locals + if (!IsLocalVar(varId)) return; + // Return if already processed + if (live_local_vars_.find(varId) != live_local_vars_.end()) return; + // Mark all stores to varId as live + AddStores(func, varId); + // Cache varId as processed + live_local_vars_.insert(varId); +} + +bool AggressiveDCEPass::IsStructuredHeader(BasicBlock* bp, + Instruction** mergeInst, + Instruction** branchInst, + uint32_t* mergeBlockId) { + if (!bp) return false; + Instruction* mi = bp->GetMergeInst(); + if (mi == nullptr) return false; + Instruction* bri = &*bp->tail(); + if (branchInst != nullptr) *branchInst = bri; + if (mergeInst != nullptr) *mergeInst = mi; + if (mergeBlockId != nullptr) *mergeBlockId = mi->GetSingleWordInOperand(0); + return true; +} + +void AggressiveDCEPass::ComputeBlock2HeaderMaps( + std::list& structuredOrder) { + block2headerBranch_.clear(); + header2nextHeaderBranch_.clear(); + branch2merge_.clear(); + structured_order_index_.clear(); + std::stack currentHeaderBranch; + currentHeaderBranch.push(nullptr); + uint32_t currentMergeBlockId = 0; + uint32_t index = 0; + for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); + ++bi, ++index) { + structured_order_index_[*bi] = index; + // If this block is the merge block of the current control construct, + // we are leaving the current construct so we must update state + if ((*bi)->id() == currentMergeBlockId) { + currentHeaderBranch.pop(); + Instruction* chb = currentHeaderBranch.top(); + if (chb != nullptr) + currentMergeBlockId = branch2merge_[chb]->GetSingleWordInOperand(0); + } + Instruction* mergeInst; + Instruction* branchInst; + uint32_t mergeBlockId; + bool is_header = + IsStructuredHeader(*bi, &mergeInst, &branchInst, &mergeBlockId); + // Map header block to next enclosing header. + if (is_header) header2nextHeaderBranch_[*bi] = currentHeaderBranch.top(); + // If this is a loop header, update state first so the block will map to + // itself. + if (is_header && mergeInst->opcode() == SpvOpLoopMerge) { + currentHeaderBranch.push(branchInst); + branch2merge_[branchInst] = mergeInst; + currentMergeBlockId = mergeBlockId; + } + // Map the block to the current construct. + block2headerBranch_[*bi] = currentHeaderBranch.top(); + // If this is an if header, update state so following blocks map to the if. + if (is_header && mergeInst->opcode() == SpvOpSelectionMerge) { + currentHeaderBranch.push(branchInst); + branch2merge_[branchInst] = mergeInst; + currentMergeBlockId = mergeBlockId; + } + } +} + +void AggressiveDCEPass::AddBranch(uint32_t labelId, BasicBlock* bp) { + std::unique_ptr newBranch( + new Instruction(context(), SpvOpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}})); + context()->AnalyzeDefUse(&*newBranch); + context()->set_instr_block(&*newBranch, bp); + bp->AddInstruction(std::move(newBranch)); +} + +void AggressiveDCEPass::AddBreaksAndContinuesToWorklist( + Instruction* mergeInst) { + assert(mergeInst->opcode() == SpvOpSelectionMerge || + mergeInst->opcode() == SpvOpLoopMerge); + + BasicBlock* header = context()->get_instr_block(mergeInst); + uint32_t headerIndex = structured_order_index_[header]; + const uint32_t mergeId = mergeInst->GetSingleWordInOperand(0); + BasicBlock* merge = context()->get_instr_block(mergeId); + uint32_t mergeIndex = structured_order_index_[merge]; + get_def_use_mgr()->ForEachUser( + mergeId, [headerIndex, mergeIndex, this](Instruction* user) { + if (!user->IsBranch()) return; + BasicBlock* block = context()->get_instr_block(user); + uint32_t index = structured_order_index_[block]; + if (headerIndex < index && index < mergeIndex) { + // This is a break from the loop. + AddToWorklist(user); + // Add branch's merge if there is one. + Instruction* userMerge = branch2merge_[user]; + if (userMerge != nullptr) AddToWorklist(userMerge); + } + }); + + if (mergeInst->opcode() != SpvOpLoopMerge) { + return; + } + + // For loops we need to find the continues as well. + const uint32_t contId = + mergeInst->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx); + get_def_use_mgr()->ForEachUser(contId, [&contId, this](Instruction* user) { + SpvOp op = user->opcode(); + if (op == SpvOpBranchConditional || op == SpvOpSwitch) { + // A conditional branch or switch can only be a continue if it does not + // have a merge instruction or its merge block is not the continue block. + Instruction* hdrMerge = branch2merge_[user]; + if (hdrMerge != nullptr && hdrMerge->opcode() == SpvOpSelectionMerge) { + uint32_t hdrMergeId = + hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); + if (hdrMergeId == contId) return; + // Need to mark merge instruction too + AddToWorklist(hdrMerge); + } + } else if (op == SpvOpBranch) { + // An unconditional branch can only be a continue if it is not + // branching to its own merge block. + BasicBlock* blk = context()->get_instr_block(user); + Instruction* hdrBranch = block2headerBranch_[blk]; + if (hdrBranch == nullptr) return; + Instruction* hdrMerge = branch2merge_[hdrBranch]; + if (hdrMerge->opcode() == SpvOpLoopMerge) return; + uint32_t hdrMergeId = + hdrMerge->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); + if (contId == hdrMergeId) return; + } else { + return; + } + AddToWorklist(user); + }); +} + +bool AggressiveDCEPass::AggressiveDCE(Function* func) { + // Mark function parameters as live. + AddToWorklist(&func->DefInst()); + func->ForEachParam( + [this](const Instruction* param) { + AddToWorklist(const_cast(param)); + }, + false); + + // Compute map from block to controlling conditional branch + std::list structuredOrder; + cfg()->ComputeStructuredOrder(func, &*func->begin(), &structuredOrder); + ComputeBlock2HeaderMaps(structuredOrder); + bool modified = false; + // Add instructions with external side effects to worklist. Also add branches + // EXCEPT those immediately contained in an "if" selection construct or a loop + // or continue construct. + // TODO(greg-lunarg): Handle Frexp, Modf more optimally + call_in_func_ = false; + func_is_entry_point_ = false; + private_stores_.clear(); + live_local_vars_.clear(); + // Stacks to keep track of when we are inside an if- or loop-construct. + // When immediately inside an if- or loop-construct, we do not initially + // mark branches live. All other branches must be marked live. + std::stack assume_branches_live; + std::stack currentMergeBlockId; + // Push sentinel values on stack for when outside of any control flow. + assume_branches_live.push(true); + currentMergeBlockId.push(0); + for (auto bi = structuredOrder.begin(); bi != structuredOrder.end(); ++bi) { + // If exiting if or loop, update stacks + if ((*bi)->id() == currentMergeBlockId.top()) { + assume_branches_live.pop(); + currentMergeBlockId.pop(); + } + for (auto ii = (*bi)->begin(); ii != (*bi)->end(); ++ii) { + SpvOp op = ii->opcode(); + switch (op) { + case SpvOpStore: { + uint32_t varId; + (void)GetPtr(&*ii, &varId); + // Mark stores as live if their variable is not function scope + // and is not private scope. Remember private stores for possible + // later inclusion. We cannot call IsLocalVar at this point because + // private_like_local_ has not been set yet. + if (IsVarOfStorage(varId, SpvStorageClassPrivate) || + IsVarOfStorage(varId, SpvStorageClassWorkgroup)) + private_stores_.push_back(&*ii); + else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) + AddToWorklist(&*ii); + } break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: { + uint32_t varId; + (void)GetPtr(ii->GetSingleWordInOperand(kCopyMemoryTargetAddrInIdx), + &varId); + if (IsVarOfStorage(varId, SpvStorageClassPrivate) || + IsVarOfStorage(varId, SpvStorageClassWorkgroup)) + private_stores_.push_back(&*ii); + else if (!IsVarOfStorage(varId, SpvStorageClassFunction)) + AddToWorklist(&*ii); + } break; + case SpvOpLoopMerge: { + assume_branches_live.push(false); + currentMergeBlockId.push( + ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx)); + } break; + case SpvOpSelectionMerge: { + assume_branches_live.push(false); + currentMergeBlockId.push( + ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx)); + } break; + case SpvOpSwitch: + case SpvOpBranch: + case SpvOpBranchConditional: + case SpvOpUnreachable: { + if (assume_branches_live.top()) { + AddToWorklist(&*ii); + } + } break; + default: { + // Function calls, atomics, function params, function returns, etc. + // TODO(greg-lunarg): function calls live only if write to non-local + if (!ii->IsOpcodeSafeToDelete()) { + AddToWorklist(&*ii); + } + // Remember function calls + if (op == SpvOpFunctionCall) call_in_func_ = true; + } break; + } + } + } + // See if current function is an entry point + for (auto& ei : get_module()->entry_points()) { + if (ei.GetSingleWordInOperand(kEntryPointFunctionIdInIdx) == + func->result_id()) { + func_is_entry_point_ = true; + break; + } + } + // If the current function is an entry point and has no function calls, + // we can optimize private variables as locals + private_like_local_ = func_is_entry_point_ && !call_in_func_; + // If privates are not like local, add their stores to worklist + if (!private_like_local_) + for (auto& ps : private_stores_) AddToWorklist(ps); + // Perform closure on live instruction set. + while (!worklist_.empty()) { + Instruction* liveInst = worklist_.front(); + // Add all operand instructions if not already live + liveInst->ForEachInId([&liveInst, this](const uint32_t* iid) { + Instruction* inInst = get_def_use_mgr()->GetDef(*iid); + // Do not add label if an operand of a branch. This is not needed + // as part of live code discovery and can create false live code, + // for example, the branch to a header of a loop. + if (inInst->opcode() == SpvOpLabel && liveInst->IsBranch()) return; + AddToWorklist(inInst); + }); + if (liveInst->type_id() != 0) { + AddToWorklist(get_def_use_mgr()->GetDef(liveInst->type_id())); + } + // If in a structured if or loop construct, add the controlling + // conditional branch and its merge. + BasicBlock* blk = context()->get_instr_block(liveInst); + Instruction* branchInst = block2headerBranch_[blk]; + if (branchInst != nullptr) { + AddToWorklist(branchInst); + Instruction* mergeInst = branch2merge_[branchInst]; + AddToWorklist(mergeInst); + } + // If the block is a header, add the next outermost controlling + // conditional branch and its merge. + Instruction* nextBranchInst = header2nextHeaderBranch_[blk]; + if (nextBranchInst != nullptr) { + AddToWorklist(nextBranchInst); + Instruction* mergeInst = branch2merge_[nextBranchInst]; + AddToWorklist(mergeInst); + } + // If local load, add all variable's stores if variable not already live + if (liveInst->opcode() == SpvOpLoad || liveInst->IsAtomicWithLoad()) { + uint32_t varId; + (void)GetPtr(liveInst, &varId); + if (varId != 0) { + ProcessLoad(func, varId); + } + // Process memory copies like loads + } else if (liveInst->opcode() == SpvOpCopyMemory || + liveInst->opcode() == SpvOpCopyMemorySized) { + uint32_t varId; + (void)GetPtr(liveInst->GetSingleWordInOperand(kCopyMemorySourceAddrInIdx), + &varId); + if (varId != 0) { + ProcessLoad(func, varId); + } + // If DebugDeclare, process as load of variable + } else if (liveInst->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugDeclare) { + uint32_t varId = + liveInst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + ProcessLoad(func, varId); + // If DebugValue with Deref, process as load of variable + } else if (liveInst->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugValue) { + uint32_t varId = context() + ->get_debug_info_mgr() + ->GetVariableIdOfDebugValueUsedForDeclare(liveInst); + if (varId != 0) ProcessLoad(func, varId); + // If merge, add other branches that are part of its control structure + } else if (liveInst->opcode() == SpvOpLoopMerge || + liveInst->opcode() == SpvOpSelectionMerge) { + AddBreaksAndContinuesToWorklist(liveInst); + // If function call, treat as if it loads from all pointer arguments + } else if (liveInst->opcode() == SpvOpFunctionCall) { + liveInst->ForEachInId([this, func](const uint32_t* iid) { + // Skip non-ptr args + if (!IsPtr(*iid)) return; + uint32_t varId; + (void)GetPtr(*iid, &varId); + ProcessLoad(func, varId); + }); + // If function parameter, treat as if it's result id is loaded from + } else if (liveInst->opcode() == SpvOpFunctionParameter) { + ProcessLoad(func, liveInst->result_id()); + // We treat an OpImageTexelPointer as a load of the pointer, and + // that value is manipulated to get the result. + } else if (liveInst->opcode() == SpvOpImageTexelPointer) { + uint32_t varId; + (void)GetPtr(liveInst, &varId); + if (varId != 0) { + ProcessLoad(func, varId); + } + } + + // Add OpDecorateId instructions that apply to this instruction to the work + // list. We use the decoration manager to look through the group + // decorations to get to the OpDecorate* instructions themselves. + auto decorations = + get_decoration_mgr()->GetDecorationsFor(liveInst->result_id(), false); + for (Instruction* dec : decorations) { + // We only care about OpDecorateId instructions because the are the only + // decorations that will reference an id that will have to be kept live + // because of that use. + if (dec->opcode() != SpvOpDecorateId) { + continue; + } + if (dec->GetSingleWordInOperand(1) == + SpvDecorationHlslCounterBufferGOOGLE) { + // These decorations should not force the use id to be live. It will be + // removed if either the target or the in operand are dead. + continue; + } + AddToWorklist(dec); + } + + // Add DebugScope and DebugInlinedAt for |liveInst| to the work list. + if (liveInst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { + auto* scope = get_def_use_mgr()->GetDef( + liveInst->GetDebugScope().GetLexicalScope()); + AddToWorklist(scope); + } + if (liveInst->GetDebugInlinedAt() != kNoInlinedAt) { + auto* inlined_at = + get_def_use_mgr()->GetDef(liveInst->GetDebugInlinedAt()); + AddToWorklist(inlined_at); + } + worklist_.pop(); + } + + // Kill dead instructions and remember dead blocks + for (auto bi = structuredOrder.begin(); bi != structuredOrder.end();) { + uint32_t mergeBlockId = 0; + (*bi)->ForEachInst([this, &modified, &mergeBlockId](Instruction* inst) { + if (!IsDead(inst)) return; + if (inst->opcode() == SpvOpLabel) return; + // If dead instruction is selection merge, remember merge block + // for new branch at end of block + if (inst->opcode() == SpvOpSelectionMerge || + inst->opcode() == SpvOpLoopMerge) + mergeBlockId = inst->GetSingleWordInOperand(0); + to_kill_.push_back(inst); + modified = true; + }); + // If a structured if or loop was deleted, add a branch to its merge + // block, and traverse to the merge block and continue processing there. + // We know the block still exists because the label is not deleted. + if (mergeBlockId != 0) { + AddBranch(mergeBlockId, *bi); + for (++bi; (*bi)->id() != mergeBlockId; ++bi) { + } + + auto merge_terminator = (*bi)->terminator(); + if (merge_terminator->opcode() == SpvOpUnreachable) { + // The merge was unreachable. This is undefined behaviour so just + // return (or return an undef). Then mark the new return as live. + auto func_ret_type_inst = get_def_use_mgr()->GetDef(func->type_id()); + if (func_ret_type_inst->opcode() == SpvOpTypeVoid) { + merge_terminator->SetOpcode(SpvOpReturn); + } else { + // Find an undef for the return value and make sure it gets kept by + // the pass. + auto undef_id = Type2Undef(func->type_id()); + auto undef = get_def_use_mgr()->GetDef(undef_id); + live_insts_.Set(undef->unique_id()); + merge_terminator->SetOpcode(SpvOpReturnValue); + merge_terminator->SetInOperands({{SPV_OPERAND_TYPE_ID, {undef_id}}}); + get_def_use_mgr()->AnalyzeInstUse(merge_terminator); + } + live_insts_.Set(merge_terminator->unique_id()); + } + } else { + ++bi; + } + } + + return modified; +} + +void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() { + // Keep all execution modes. + for (auto& exec : get_module()->execution_modes()) { + AddToWorklist(&exec); + } + // Keep all entry points. + for (auto& entry : get_module()->entry_points()) { + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // In SPIR-V 1.4 and later, entry points must list all global variables + // used. DCE can still remove non-input/output variables and update the + // interface list. Mark the entry point as live and inputs and outputs as + // live, but defer decisions all other interfaces. + live_insts_.Set(entry.unique_id()); + // The actual function is live always. + AddToWorklist( + get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(1u))); + for (uint32_t i = 3; i < entry.NumInOperands(); ++i) { + auto* var = get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); + auto storage_class = var->GetSingleWordInOperand(0u); + if (storage_class == SpvStorageClassInput || + storage_class == SpvStorageClassOutput) { + AddToWorklist(var); + } + } + } else { + AddToWorklist(&entry); + } + } + for (auto& anno : get_module()->annotations()) { + if (anno.opcode() == SpvOpDecorate) { + // Keep workgroup size. + if (anno.GetSingleWordInOperand(1u) == SpvDecorationBuiltIn && + anno.GetSingleWordInOperand(2u) == SpvBuiltInWorkgroupSize) { + AddToWorklist(&anno); + } + + if (context()->preserve_bindings()) { + // Keep all bindings. + if ((anno.GetSingleWordInOperand(1u) == SpvDecorationDescriptorSet) || + (anno.GetSingleWordInOperand(1u) == SpvDecorationBinding)) { + AddToWorklist(&anno); + } + } + + if (context()->preserve_spec_constants()) { + // Keep all specialization constant instructions + if (anno.GetSingleWordInOperand(1u) == SpvDecorationSpecId) { + AddToWorklist(&anno); + } + } + } + } + + // For each DebugInfo GlobalVariable keep all operands except the Variable. + // Later, if the variable is dead, we will set the operand to DebugInfoNone. + for (auto& dbg : get_module()->ext_inst_debuginfo()) { + if (dbg.GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugGlobalVariable) + continue; + dbg.ForEachInId([this](const uint32_t* iid) { + Instruction* inInst = get_def_use_mgr()->GetDef(*iid); + if (inInst->opcode() == SpvOpVariable) return; + AddToWorklist(inInst); + }); + } +} + +Pass::Status AggressiveDCEPass::ProcessImpl() { + // Current functionality assumes shader capability + // TODO(greg-lunarg): Handle additional capabilities + if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) + return Status::SuccessWithoutChange; + + // Current functionality assumes relaxed logical addressing (see + // instruction.h) + // TODO(greg-lunarg): Handle non-logical addressing + if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses)) + return Status::SuccessWithoutChange; + + // The variable pointer extension is no longer needed to use the capability, + // so we have to look for the capability. + if (context()->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)) + return Status::SuccessWithoutChange; + + // If any extensions in the module are not explicitly supported, + // return unmodified. + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; + + // Eliminate Dead functions. + bool modified = EliminateDeadFunctions(); + + InitializeModuleScopeLiveInstructions(); + + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { return AggressiveDCE(fp); }; + modified |= context()->ProcessEntryPointCallTree(pfn); + + // If the decoration manager is kept live then the context will try to keep it + // up to date. ADCE deals with group decorations by changing the operands in + // |OpGroupDecorate| instruction directly without informing the decoration + // manager. This can put it in an invalid state which will cause an error + // when the context tries to update it. To avoid this problem invalidate + // the decoration manager upfront. + // + // We kill it at now because it is used when processing the entry point + // functions. + context()->InvalidateAnalyses(IRContext::Analysis::kAnalysisDecorations); + + // Process module-level instructions. Now that all live instructions have + // been marked, it is safe to remove dead global values. + modified |= ProcessGlobalValues(); + + assert((to_kill_.empty() || modified) && + "A dead instruction was identified, but no change recorded."); + + // Kill all dead instructions. + for (auto inst : to_kill_) { + context()->KillInst(inst); + } + + // Cleanup all CFG including all unreachable blocks. + ProcessFunction cleanup = [this](Function* f) { return CFGCleanup(f); }; + modified |= context()->ProcessEntryPointCallTree(cleanup); + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool AggressiveDCEPass::EliminateDeadFunctions() { + // Identify live functions first. Those that are not live + // are dead. ADCE is disabled for non-shaders so we do not check for exported + // functions here. + std::unordered_set live_function_set; + ProcessFunction mark_live = [&live_function_set](Function* fp) { + live_function_set.insert(fp); + return false; + }; + context()->ProcessEntryPointCallTree(mark_live); + + bool modified = false; + for (auto funcIter = get_module()->begin(); + funcIter != get_module()->end();) { + if (live_function_set.count(&*funcIter) == 0) { + modified = true; + funcIter = + eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter); + } else { + ++funcIter; + } + } + + return modified; +} + +bool AggressiveDCEPass::ProcessGlobalValues() { + // Remove debug and annotation statements referencing dead instructions. + // This must be done before killing the instructions, otherwise there are + // dead objects in the def/use database. + bool modified = false; + Instruction* instruction = &*get_module()->debug2_begin(); + while (instruction) { + if (instruction->opcode() != SpvOpName) { + instruction = instruction->NextNode(); + continue; + } + + if (IsTargetDead(instruction)) { + instruction = context()->KillInst(instruction); + modified = true; + } else { + instruction = instruction->NextNode(); + } + } + + // This code removes all unnecessary decorations safely (see #1174). It also + // does so in a more efficient manner than deleting them only as the targets + // are deleted. + std::vector annotations; + for (auto& inst : get_module()->annotations()) annotations.push_back(&inst); + std::sort(annotations.begin(), annotations.end(), DecorationLess()); + for (auto annotation : annotations) { + switch (annotation->opcode()) { + case SpvOpDecorate: + case SpvOpMemberDecorate: + case SpvOpDecorateStringGOOGLE: + case SpvOpMemberDecorateStringGOOGLE: + if (IsTargetDead(annotation)) { + context()->KillInst(annotation); + modified = true; + } + break; + case SpvOpDecorateId: + if (IsTargetDead(annotation)) { + context()->KillInst(annotation); + modified = true; + } else { + if (annotation->GetSingleWordInOperand(1) == + SpvDecorationHlslCounterBufferGOOGLE) { + // HlslCounterBuffer will reference an id other than the target. + // If that id is dead, then the decoration can be removed as well. + uint32_t counter_buffer_id = annotation->GetSingleWordInOperand(2); + Instruction* counter_buffer_inst = + get_def_use_mgr()->GetDef(counter_buffer_id); + if (IsDead(counter_buffer_inst)) { + context()->KillInst(annotation); + modified = true; + } + } + } + break; + case SpvOpGroupDecorate: { + // Go through the targets of this group decorate. Remove each dead + // target. If all targets are dead, remove this decoration. + bool dead = true; + bool removed_operand = false; + for (uint32_t i = 1; i < annotation->NumOperands();) { + Instruction* opInst = + get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); + if (IsDead(opInst)) { + // Don't increment |i|. + annotation->RemoveOperand(i); + modified = true; + removed_operand = true; + } else { + i++; + dead = false; + } + } + if (dead) { + context()->KillInst(annotation); + modified = true; + } else if (removed_operand) { + context()->UpdateDefUse(annotation); + } + break; + } + case SpvOpGroupMemberDecorate: { + // Go through the targets of this group member decorate. Remove each + // dead target (and member index). If all targets are dead, remove this + // decoration. + bool dead = true; + bool removed_operand = false; + for (uint32_t i = 1; i < annotation->NumOperands();) { + Instruction* opInst = + get_def_use_mgr()->GetDef(annotation->GetSingleWordOperand(i)); + if (IsDead(opInst)) { + // Don't increment |i|. + annotation->RemoveOperand(i + 1); + annotation->RemoveOperand(i); + modified = true; + removed_operand = true; + } else { + i += 2; + dead = false; + } + } + if (dead) { + context()->KillInst(annotation); + modified = true; + } else if (removed_operand) { + context()->UpdateDefUse(annotation); + } + break; + } + case SpvOpDecorationGroup: + // By the time we hit decoration groups we've checked everything that + // can target them. So if they have no uses they must be dead. + if (get_def_use_mgr()->NumUsers(annotation) == 0) { + context()->KillInst(annotation); + modified = true; + } + break; + default: + assert(false); + break; + } + } + + for (auto& dbg : get_module()->ext_inst_debuginfo()) { + if (!IsDead(&dbg)) continue; + // Save GlobalVariable if its variable is live, otherwise null out variable + // index + if (dbg.GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugGlobalVariable) { + auto var_id = dbg.GetSingleWordOperand(kGlobalVariableVariableIndex); + Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); + if (!IsDead(var_inst)) continue; + context()->ForgetUses(&dbg); + dbg.SetOperand( + kGlobalVariableVariableIndex, + {context()->get_debug_info_mgr()->GetDebugInfoNone()->result_id()}); + context()->AnalyzeUses(&dbg); + continue; + } + to_kill_.push_back(&dbg); + modified = true; + } + + // Since ADCE is disabled for non-shaders, we don't check for export linkage + // attributes here. + for (auto& val : get_module()->types_values()) { + if (IsDead(&val)) { + // Save forwarded pointer if pointer is live since closure does not mark + // this live as it does not have a result id. This is a little too + // conservative since it is not known if the structure type that needed + // it is still live. TODO(greg-lunarg): Only save if needed. + if (val.opcode() == SpvOpTypeForwardPointer) { + uint32_t ptr_ty_id = val.GetSingleWordInOperand(0); + Instruction* ptr_ty_inst = get_def_use_mgr()->GetDef(ptr_ty_id); + if (!IsDead(ptr_ty_inst)) continue; + } + to_kill_.push_back(&val); + modified = true; + } + } + + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // Remove the dead interface variables from the entry point interface list. + for (auto& entry : get_module()->entry_points()) { + std::vector new_operands; + for (uint32_t i = 0; i < entry.NumInOperands(); ++i) { + if (i < 3) { + // Execution model, function id and name are always valid. + new_operands.push_back(entry.GetInOperand(i)); + } else { + auto* var = + get_def_use_mgr()->GetDef(entry.GetSingleWordInOperand(i)); + if (!IsDead(var)) { + new_operands.push_back(entry.GetInOperand(i)); + } + } + } + if (new_operands.size() != entry.NumInOperands()) { + entry.SetInOperands(std::move(new_operands)); + get_def_use_mgr()->UpdateDefUse(&entry); + } + } + } + + return modified; +} + +AggressiveDCEPass::AggressiveDCEPass() = default; + +Pass::Status AggressiveDCEPass::Process() { + // Initialize extensions allowlist + InitExtensions(); + return ProcessImpl(); +} + +void AggressiveDCEPass::InitExtensions() { + extensions_allowlist_.clear(); + extensions_allowlist_.insert({ + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + // SPV_KHR_variable_pointers + // Currently do not support extended pointer expressions + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", + "SPV_EXT_descriptor_indexing", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", + "SPV_EXT_fragment_invocation_density", + "SPV_EXT_physical_storage_buffer", + "SPV_KHR_terminate_invocation", + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h b/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h new file mode 100644 index 0000000..f02e729 --- /dev/null +++ b/third_party/spirv-tools/source/opt/aggressive_dead_code_elim_pass.h @@ -0,0 +1,197 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ +#define SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class AggressiveDCEPass : public MemPass { + using cbb_ptr = const BasicBlock*; + + public: + using GetBlocksFunction = + std::function*(const BasicBlock*)>; + + AggressiveDCEPass(); + const char* name() const override { return "eliminate-dead-code-aggressive"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Return true if |varId| is a variable of |storageClass|. |varId| must either + // be 0 or the result of an instruction. + bool IsVarOfStorage(uint32_t varId, uint32_t storageClass); + + // Return true if |varId| is variable of function storage class or is + // private variable and privates can be optimized like locals (see + // privates_like_local_). + bool IsLocalVar(uint32_t varId); + + // Return true if |inst| is marked live. + bool IsLive(const Instruction* inst) const { + return live_insts_.Get(inst->unique_id()); + } + + // Returns true if |inst| is dead. + bool IsDead(Instruction* inst); + + // Adds entry points, execution modes and workgroup size decorations to the + // worklist for processing with the first function. + void InitializeModuleScopeLiveInstructions(); + + // Add |inst| to worklist_ and live_insts_. + void AddToWorklist(Instruction* inst) { + if (!live_insts_.Set(inst->unique_id())) { + worklist_.push(inst); + } + } + + // Add all store instruction which use |ptrId|, directly or indirectly, + // to the live instruction worklist. + void AddStores(Function* func, uint32_t ptrId); + + // Initialize extensions allowlist + void InitExtensions(); + + // Return true if all extensions in this module are supported by this pass. + bool AllExtensionsSupported() const; + + // Returns true if the target of |inst| is dead. An instruction is dead if + // its result id is used in decoration or debug instructions only. |inst| is + // assumed to be OpName, OpMemberName or an annotation instruction. + bool IsTargetDead(Instruction* inst); + + // If |varId| is local, mark all stores of varId as live. + void ProcessLoad(Function* func, uint32_t varId); + + // If |bp| is structured header block, returns true and sets |mergeInst| to + // the merge instruction, |branchInst| to the branch and |mergeBlockId| to the + // merge block if they are not nullptr. Any of |mergeInst|, |branchInst| or + // |mergeBlockId| may be a null pointer. Returns false if |bp| is a null + // pointer. + bool IsStructuredHeader(BasicBlock* bp, Instruction** mergeInst, + Instruction** branchInst, uint32_t* mergeBlockId); + + // Initialize block2headerBranch_, header2nextHeaderBranch_, and + // branch2merge_ using |structuredOrder| to order blocks. + void ComputeBlock2HeaderMaps(std::list& structuredOrder); + + // Add branch to |labelId| to end of block |bp|. + void AddBranch(uint32_t labelId, BasicBlock* bp); + + // Add all break and continue branches in the construct associated with + // |mergeInst| to worklist if not already live + void AddBreaksAndContinuesToWorklist(Instruction* mergeInst); + + // Eliminates dead debug2 and annotation instructions. Marks dead globals for + // removal (e.g. types, constants and variables). + bool ProcessGlobalValues(); + + // Erases functions that are unreachable from the entry points of the module. + bool EliminateDeadFunctions(); + + // For function |func|, mark all Stores to non-function-scope variables + // and block terminating instructions as live. Recursively mark the values + // they use. When complete, mark any non-live instructions to be deleted. + // Returns true if the function has been modified. + // + // Note: This function does not delete useless control structures. All + // existing control structures will remain. This can leave not-insignificant + // sequences of ultimately useless code. + // TODO(): Remove useless control constructs. + bool AggressiveDCE(Function* func); + + Pass::Status ProcessImpl(); + + // True if current function has a call instruction contained in it + bool call_in_func_; + + // True if current function is an entry point + bool func_is_entry_point_; + + // True if current function is entry point and has no function calls. + bool private_like_local_; + + // Live Instruction Worklist. An instruction is added to this list + // if it might have a side effect, either directly or indirectly. + // If we don't know, then add it to this list. Instructions are + // removed from this list as the algorithm traces side effects, + // building up the live instructions set |live_insts_|. + std::queue worklist_; + + // Map from block to the branch instruction in the header of the most + // immediate controlling structured if or loop. A loop header block points + // to its own branch instruction. An if-selection block points to the branch + // of an enclosing construct's header, if one exists. + std::unordered_map block2headerBranch_; + + // Map from header block to the branch instruction in the header of the + // structured construct enclosing it. + // The liveness algorithm is designed to iteratively mark as live all + // structured constructs enclosing a live instruction. + std::unordered_map header2nextHeaderBranch_; + + // Maps basic block to their index in the structured order traversal. + std::unordered_map structured_order_index_; + + // Map from branch to its associated merge instruction, if any + std::unordered_map branch2merge_; + + // Store instructions to variables of private storage + std::vector private_stores_; + + // Live Instructions + utils::BitVector live_insts_; + + // Live Local Variables + std::unordered_set live_local_vars_; + + // List of instructions to delete. Deletion is delayed until debug and + // annotation instructions are processed. + std::vector to_kill_; + + // Extensions supported by this pass. + std::unordered_set extensions_allowlist_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_AGGRESSIVE_DEAD_CODE_ELIM_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/amd_ext_to_khr.cpp b/third_party/spirv-tools/source/opt/amd_ext_to_khr.cpp new file mode 100644 index 0000000..ccedc0b --- /dev/null +++ b/third_party/spirv-tools/source/opt/amd_ext_to_khr.cpp @@ -0,0 +1,972 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/amd_ext_to_khr.h" + +#include +#include + +#include "ir_builder.h" +#include "source/opt/ir_context.h" +#include "spv-amd-shader-ballot.insts.inc" +#include "type_manager.h" + +namespace spvtools { +namespace opt { + +namespace { + +enum AmdShaderBallotExtOpcodes { + AmdShaderBallotSwizzleInvocationsAMD = 1, + AmdShaderBallotSwizzleInvocationsMaskedAMD = 2, + AmdShaderBallotWriteInvocationAMD = 3, + AmdShaderBallotMbcntAMD = 4 +}; + +enum AmdShaderTrinaryMinMaxExtOpCodes { + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9 +}; + +enum AmdGcnShader { CubeFaceCoordAMD = 2, CubeFaceIndexAMD = 1, TimeAMD = 3 }; + +analysis::Type* GetUIntType(IRContext* ctx) { + analysis::Integer int_type(32, false); + return ctx->get_type_mgr()->GetRegisteredType(&int_type); +} + +// Returns a folding rule that replaces |op(a,b,c)| by |op(op(a,b),c)|, where +// |op| is either min or max. |opcode| is the binary opcode in the GLSLstd450 +// extended instruction set that corresponds to the trinary instruction being +// replaced. +template +bool ReplaceTrinaryMinMax(IRContext* ctx, Instruction* inst, + const std::vector&) { + uint32_t glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + if (glsl405_ext_inst_id == 0) { + ctx->AddExtInstImport("GLSL.std.450"); + glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + } + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t op1 = inst->GetSingleWordInOperand(2); + uint32_t op2 = inst->GetSingleWordInOperand(3); + uint32_t op3 = inst->GetSingleWordInOperand(4); + + Instruction* temp = ir_builder.AddNaryExtendedInstruction( + inst->type_id(), glsl405_ext_inst_id, opcode, {op1, op2}); + + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}}); + new_operands.push_back({SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(opcode)}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {temp->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {op3}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that replaces |mid(a,b,c)| by |clamp(a, min(b,c), +// max(b,c)|. The three parameters are the opcode that correspond to the min, +// max, and clamp operations for the type of the instruction being replaced. +template +bool ReplaceTrinaryMid(IRContext* ctx, Instruction* inst, + const std::vector&) { + uint32_t glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + if (glsl405_ext_inst_id == 0) { + ctx->AddExtInstImport("GLSL.std.450"); + glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + } + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t op1 = inst->GetSingleWordInOperand(2); + uint32_t op2 = inst->GetSingleWordInOperand(3); + uint32_t op3 = inst->GetSingleWordInOperand(4); + + Instruction* min = ir_builder.AddNaryExtendedInstruction( + inst->type_id(), glsl405_ext_inst_id, static_cast(min_opcode), + {op2, op3}); + Instruction* max = ir_builder.AddNaryExtendedInstruction( + inst->type_id(), glsl405_ext_inst_id, static_cast(max_opcode), + {op2, op3}); + + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {glsl405_ext_inst_id}}); + new_operands.push_back({SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(clamp_opcode)}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {op1}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {min->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {max->result_id()}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that will replace the opcode with |opcode| and add +// the capabilities required. The folding rule assumes it is folding an +// OpGroup*NonUniformAMD instruction from the SPV_AMD_shader_ballot extension. +template +bool ReplaceGroupNonuniformOperationOpCode( + IRContext* ctx, Instruction* inst, + const std::vector&) { + switch (new_opcode) { + case SpvOpGroupNonUniformIAdd: + case SpvOpGroupNonUniformFAdd: + case SpvOpGroupNonUniformUMin: + case SpvOpGroupNonUniformSMin: + case SpvOpGroupNonUniformFMin: + case SpvOpGroupNonUniformUMax: + case SpvOpGroupNonUniformSMax: + case SpvOpGroupNonUniformFMax: + break; + default: + assert( + false && + "Should be replacing with a group non uniform arithmetic operation."); + } + + switch (inst->opcode()) { + case SpvOpGroupIAddNonUniformAMD: + case SpvOpGroupFAddNonUniformAMD: + case SpvOpGroupUMinNonUniformAMD: + case SpvOpGroupSMinNonUniformAMD: + case SpvOpGroupFMinNonUniformAMD: + case SpvOpGroupUMaxNonUniformAMD: + case SpvOpGroupSMaxNonUniformAMD: + case SpvOpGroupFMaxNonUniformAMD: + break; + default: + assert(false && + "Should be replacing a group non uniform arithmetic operation."); + } + + ctx->AddCapability(SpvCapabilityGroupNonUniformArithmetic); + inst->SetOpcode(new_opcode); + return true; +} + +// Returns a folding rule that will replace the SwizzleInvocationsAMD extended +// instruction in the SPV_AMD_shader_ballot extension. +// +// The instruction +// +// %offset = OpConstantComposite %v3uint %x %y %z %w +// %result = OpExtInst %type %1 SwizzleInvocationsAMD %data %offset +// +// is replaced with +// +// potentially new constants and types +// +// clang-format off +// %uint_max = OpConstant %uint 0xFFFFFFFF +// %v4uint = OpTypeVector %uint 4 +// %ballot_value = OpConstantComposite %v4uint %uint_max %uint_max %uint_max %uint_max +// %null = OpConstantNull %type +// clang-format on +// +// and the following code in the function body +// +// clang-format off +// %id = OpLoad %uint %SubgroupLocalInvocationId +// %quad_idx = OpBitwiseAnd %uint %id %uint_3 +// %quad_ldr = OpBitwiseXor %uint %id %quad_idx +// %my_offset = OpVectorExtractDynamic %uint %offset %quad_idx +// %target_inv = OpIAdd %uint %quad_ldr %my_offset +// %is_active = OpGroupNonUniformBallotBitExtract %bool %uint_3 %ballot_value %target_inv +// %shuffle = OpGroupNonUniformShuffle %type %uint_3 %data %target_inv +// %result = OpSelect %type %is_active %shuffle %null +// clang-format on +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceSwizzleInvocations(IRContext* ctx, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = ctx->get_type_mgr(); + analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); + + ctx->AddExtension("SPV_KHR_shader_ballot"); + ctx->AddCapability(SpvCapabilityGroupNonUniformBallot); + ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t data_id = inst->GetSingleWordInOperand(2); + uint32_t offset_id = inst->GetSingleWordInOperand(3); + + // Get the subgroup invocation id. + uint32_t var_id = + ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId); + assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); + Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + Instruction* var_ptr_type = + ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1); + + Instruction* id = ir_builder.AddLoad(uint_type_id, var_id); + + uint32_t quad_mask = ir_builder.GetUintConstantId(3); + + // This gives the offset in the group of 4 of this invocation. + Instruction* quad_idx = ir_builder.AddBinaryOp(uint_type_id, SpvOpBitwiseAnd, + id->result_id(), quad_mask); + + // Get the invocation id of the first invocation in the group of 4. + Instruction* quad_ldr = ir_builder.AddBinaryOp( + uint_type_id, SpvOpBitwiseXor, id->result_id(), quad_idx->result_id()); + + // Get the offset of the target invocation from the offset vector. + Instruction* my_offset = + ir_builder.AddBinaryOp(uint_type_id, SpvOpVectorExtractDynamic, offset_id, + quad_idx->result_id()); + + // Determine the index of the invocation to read from. + Instruction* target_inv = ir_builder.AddBinaryOp( + uint_type_id, SpvOpIAdd, quad_ldr->result_id(), my_offset->result_id()); + + // Do the group operations + uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF); + uint32_t subgroup_scope = ir_builder.GetUintConstantId(SpvScopeSubgroup); + const auto* ballot_value_const = const_mgr->GetConstant( + type_mgr->GetUIntVectorType(4), + {uint_max_id, uint_max_id, uint_max_id, uint_max_id}); + Instruction* ballot_value = + const_mgr->GetDefiningInstruction(ballot_value_const); + Instruction* is_active = ir_builder.AddNaryOp( + type_mgr->GetBoolTypeId(), SpvOpGroupNonUniformBallotBitExtract, + {subgroup_scope, ballot_value->result_id(), target_inv->result_id()}); + Instruction* shuffle = + ir_builder.AddNaryOp(inst->type_id(), SpvOpGroupNonUniformShuffle, + {subgroup_scope, data_id, target_inv->result_id()}); + + // Create the null constant to use in the select. + const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), + std::vector()); + Instruction* null_inst = const_mgr->GetDefiningInstruction(null); + + // Build the select. + inst->SetOpcode(SpvOpSelect); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_active->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {shuffle->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {null_inst->result_id()}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that will replace the SwizzleInvocationsMaskedAMD +// extended instruction in the SPV_AMD_shader_ballot extension. +// +// The instruction +// +// %mask = OpConstantComposite %v3uint %uint_x %uint_y %uint_z +// %result = OpExtInst %uint %1 SwizzleInvocationsMaskedAMD %data %mask +// +// is replaced with +// +// potentially new constants and types +// +// clang-format off +// %uint_mask_extend = OpConstant %uint 0xFFFFFFE0 +// %uint_max = OpConstant %uint 0xFFFFFFFF +// %v4uint = OpTypeVector %uint 4 +// %ballot_value = OpConstantComposite %v4uint %uint_max %uint_max %uint_max %uint_max +// clang-format on +// +// and the following code in the function body +// +// clang-format off +// %id = OpLoad %uint %SubgroupLocalInvocationId +// %and_mask = OpBitwiseOr %uint %uint_x %uint_mask_extend +// %and = OpBitwiseAnd %uint %id %and_mask +// %or = OpBitwiseOr %uint %and %uint_y +// %target_inv = OpBitwiseXor %uint %or %uint_z +// %is_active = OpGroupNonUniformBallotBitExtract %bool %uint_3 %ballot_value %target_inv +// %shuffle = OpGroupNonUniformShuffle %type %uint_3 %data %target_inv +// %result = OpSelect %type %is_active %shuffle %uint_0 +// clang-format on +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceSwizzleInvocationsMasked( + IRContext* ctx, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = ctx->get_type_mgr(); + analysis::DefUseManager* def_use_mgr = ctx->get_def_use_mgr(); + analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); + + ctx->AddCapability(SpvCapabilityGroupNonUniformBallot); + ctx->AddCapability(SpvCapabilityGroupNonUniformShuffle); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + // Get the operands to inst, and the components of the mask + uint32_t data_id = inst->GetSingleWordInOperand(2); + + Instruction* mask_inst = def_use_mgr->GetDef(inst->GetSingleWordInOperand(3)); + assert(mask_inst->opcode() == SpvOpConstantComposite && + "The mask is suppose to be a vector constant."); + assert(mask_inst->NumInOperands() == 3 && + "The mask is suppose to have 3 components."); + + uint32_t uint_x = mask_inst->GetSingleWordInOperand(0); + uint32_t uint_y = mask_inst->GetSingleWordInOperand(1); + uint32_t uint_z = mask_inst->GetSingleWordInOperand(2); + + // Get the subgroup invocation id. + uint32_t var_id = + ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId); + ctx->AddExtension("SPV_KHR_shader_ballot"); + assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); + Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + Instruction* var_ptr_type = + ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + uint32_t uint_type_id = var_ptr_type->GetSingleWordInOperand(1); + + Instruction* id = ir_builder.AddLoad(uint_type_id, var_id); + + // Do the bitwise operations. + uint32_t mask_extended = ir_builder.GetUintConstantId(0xFFFFFFE0); + Instruction* and_mask = ir_builder.AddBinaryOp(uint_type_id, SpvOpBitwiseOr, + uint_x, mask_extended); + Instruction* and_result = ir_builder.AddBinaryOp( + uint_type_id, SpvOpBitwiseAnd, id->result_id(), and_mask->result_id()); + Instruction* or_result = ir_builder.AddBinaryOp( + uint_type_id, SpvOpBitwiseOr, and_result->result_id(), uint_y); + Instruction* target_inv = ir_builder.AddBinaryOp( + uint_type_id, SpvOpBitwiseXor, or_result->result_id(), uint_z); + + // Do the group operations + uint32_t uint_max_id = ir_builder.GetUintConstantId(0xFFFFFFFF); + uint32_t subgroup_scope = ir_builder.GetUintConstantId(SpvScopeSubgroup); + const auto* ballot_value_const = const_mgr->GetConstant( + type_mgr->GetUIntVectorType(4), + {uint_max_id, uint_max_id, uint_max_id, uint_max_id}); + Instruction* ballot_value = + const_mgr->GetDefiningInstruction(ballot_value_const); + Instruction* is_active = ir_builder.AddNaryOp( + type_mgr->GetBoolTypeId(), SpvOpGroupNonUniformBallotBitExtract, + {subgroup_scope, ballot_value->result_id(), target_inv->result_id()}); + Instruction* shuffle = + ir_builder.AddNaryOp(inst->type_id(), SpvOpGroupNonUniformShuffle, + {subgroup_scope, data_id, target_inv->result_id()}); + + // Create the null constant to use in the select. + const auto* null = const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), + std::vector()); + Instruction* null_inst = const_mgr->GetDefiningInstruction(null); + + // Build the select. + inst->SetOpcode(SpvOpSelect); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_active->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {shuffle->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {null_inst->result_id()}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that will replace the WriteInvocationAMD extended +// instruction in the SPV_AMD_shader_ballot extension. +// +// The instruction +// +// clang-format off +// %result = OpExtInst %type %1 WriteInvocationAMD %input_value %write_value %invocation_index +// clang-format on +// +// with +// +// %id = OpLoad %uint %SubgroupLocalInvocationId +// %cmp = OpIEqual %bool %id %invocation_index +// %result = OpSelect %type %cmp %write_value %input_value +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceWriteInvocation(IRContext* ctx, Instruction* inst, + const std::vector&) { + uint32_t var_id = + ctx->GetBuiltinInputVarId(SpvBuiltInSubgroupLocalInvocationId); + ctx->AddCapability(SpvCapabilitySubgroupBallotKHR); + ctx->AddExtension("SPV_KHR_shader_ballot"); + assert(var_id != 0 && "Could not get SubgroupLocalInvocationId variable."); + Instruction* var_inst = ctx->get_def_use_mgr()->GetDef(var_id); + Instruction* var_ptr_type = + ctx->get_def_use_mgr()->GetDef(var_inst->type_id()); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* t = + ir_builder.AddLoad(var_ptr_type->GetSingleWordInOperand(1), var_id); + analysis::Bool bool_type; + uint32_t bool_type_id = ctx->get_type_mgr()->GetTypeInstruction(&bool_type); + Instruction* cmp = + ir_builder.AddBinaryOp(bool_type_id, SpvOpIEqual, t->result_id(), + inst->GetSingleWordInOperand(4)); + + // Build a select. + inst->SetOpcode(SpvOpSelect); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {cmp->result_id()}}); + new_operands.push_back(inst->GetInOperand(3)); + new_operands.push_back(inst->GetInOperand(2)); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// Returns a folding rule that will replace the MbcntAMD extended instruction in +// the SPV_AMD_shader_ballot extension. +// +// The instruction +// +// %result = OpExtInst %uint %1 MbcntAMD %mask +// +// with +// +// Get SubgroupLtMask and convert the first 64-bits into a uint64_t because +// AMD's shader compiler expects a 64-bit integer mask. +// +// %var = OpLoad %v4uint %SubgroupLtMaskKHR +// %shuffle = OpVectorShuffle %v2uint %var %var 0 1 +// %cast = OpBitcast %ulong %shuffle +// +// Perform the mask and count the bits. +// +// %and = OpBitwiseAnd %ulong %cast %mask +// %result = OpBitCount %uint %and +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceMbcnt(IRContext* context, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = context->get_type_mgr(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + uint32_t var_id = context->GetBuiltinInputVarId(SpvBuiltInSubgroupLtMask); + assert(var_id != 0 && "Could not get SubgroupLtMask variable."); + context->AddCapability(SpvCapabilityGroupNonUniformBallot); + Instruction* var_inst = def_use_mgr->GetDef(var_id); + Instruction* var_ptr_type = def_use_mgr->GetDef(var_inst->type_id()); + Instruction* var_type = + def_use_mgr->GetDef(var_ptr_type->GetSingleWordInOperand(1)); + assert(var_type->opcode() == SpvOpTypeVector && + "Variable is suppose to be a vector of 4 ints"); + + // Get the type for the shuffle. + analysis::Vector temp_type(GetUIntType(context), 2); + const analysis::Type* shuffle_type = + context->get_type_mgr()->GetRegisteredType(&temp_type); + uint32_t shuffle_type_id = type_mgr->GetTypeInstruction(shuffle_type); + + uint32_t mask_id = inst->GetSingleWordInOperand(2); + Instruction* mask_inst = def_use_mgr->GetDef(mask_id); + + // Testing with amd's shader compiler shows that a 64-bit mask is expected. + assert(type_mgr->GetType(mask_inst->type_id())->AsInteger() != nullptr); + assert(type_mgr->GetType(mask_inst->type_id())->AsInteger()->width() == 64); + + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* load = ir_builder.AddLoad(var_type->result_id(), var_id); + Instruction* shuffle = ir_builder.AddVectorShuffle( + shuffle_type_id, load->result_id(), load->result_id(), {0, 1}); + Instruction* bitcast = ir_builder.AddUnaryOp( + mask_inst->type_id(), SpvOpBitcast, shuffle->result_id()); + Instruction* t = ir_builder.AddBinaryOp(mask_inst->type_id(), SpvOpBitwiseAnd, + bitcast->result_id(), mask_id); + + inst->SetOpcode(SpvOpBitCount); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {t->result_id()}}}); + context->UpdateDefUse(inst); + return true; +} + +// A folding rule that will replace the CubeFaceCoordAMD extended +// instruction in the SPV_AMD_gcn_shader_ballot. Returns true if the folding is +// successful. +// +// The instruction +// +// %result = OpExtInst %v2float %1 CubeFaceCoordAMD %input +// +// with +// +// %x = OpCompositeExtract %float %input 0 +// %y = OpCompositeExtract %float %input 1 +// %z = OpCompositeExtract %float %input 2 +// %nx = OpFNegate %float %x +// %ny = OpFNegate %float %y +// %nz = OpFNegate %float %z +// %ax = OpExtInst %float %n_1 FAbs %x +// %ay = OpExtInst %float %n_1 FAbs %y +// %az = OpExtInst %float %n_1 FAbs %z +// %amax_x_y = OpExtInst %float %n_1 FMax %ay %ax +// %amax = OpExtInst %float %n_1 FMax %az %amax_x_y +// %cubema = OpFMul %float %float_2 %amax +// %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y +// %not_is_z_max = OpLogicalNot %bool %is_z_max +// %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax +// %is_y_max = OpLogicalAnd %bool %not_is_z_max %y_gt_x +// %is_z_neg = OpFOrdLessThan %bool %z %float_0 +// %cubesc_case_1 = OpSelect %float %is_z_neg %nx %x +// %is_x_neg = OpFOrdLessThan %bool %x %float_0 +// %cubesc_case_2 = OpSelect %float %is_x_neg %z %nz +// %sel = OpSelect %float %is_y_max %x %cubesc_case_2 +// %cubesc = OpSelect %float %is_z_max %cubesc_case_1 %sel +// %is_y_neg = OpFOrdLessThan %bool %y %float_0 +// %cubetc_case_1 = OpSelect %float %is_y_neg %nz %z +// %cubetc = OpSelect %float %is_y_max %cubetc_case_1 %ny +// %cube = OpCompositeConstruct %v2float %cubesc %cubetc +// %denom = OpCompositeConstruct %v2float %cubema %cubema +// %div = OpFDiv %v2float %cube %denom +// %result = OpFAdd %v2float %div %const +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceCubeFaceCoord(IRContext* ctx, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = ctx->get_type_mgr(); + analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); + + uint32_t float_type_id = type_mgr->GetFloatTypeId(); + const analysis::Type* v2_float_type = type_mgr->GetFloatVectorType(2); + uint32_t v2_float_type_id = type_mgr->GetId(v2_float_type); + uint32_t bool_id = type_mgr->GetBoolTypeId(); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t input_id = inst->GetSingleWordInOperand(2); + uint32_t glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + if (glsl405_ext_inst_id == 0) { + ctx->AddExtInstImport("GLSL.std.450"); + glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + } + + // Get the constants that will be used. + uint32_t f0_const_id = const_mgr->GetFloatConst(0.0); + uint32_t f2_const_id = const_mgr->GetFloatConst(2.0); + uint32_t f0_5_const_id = const_mgr->GetFloatConst(0.5); + const analysis::Constant* vec_const = + const_mgr->GetConstant(v2_float_type, {f0_5_const_id, f0_5_const_id}); + uint32_t vec_const_id = + const_mgr->GetDefiningInstruction(vec_const)->result_id(); + + // Extract the input values. + Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); + Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1}); + Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2}); + + // Negate the input values. + Instruction* nx = + ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, x->result_id()); + Instruction* ny = + ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, y->result_id()); + Instruction* nz = + ir_builder.AddUnaryOp(float_type_id, SpvOpFNegate, z->result_id()); + + // Get the abolsute values of the inputs. + Instruction* ax = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()}); + Instruction* ay = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()}); + Instruction* az = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()}); + + // Find which values are negative. Used in later computations. + Instruction* is_z_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan, + z->result_id(), f0_const_id); + Instruction* is_y_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan, + y->result_id(), f0_const_id); + Instruction* is_x_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan, + x->result_id(), f0_const_id); + + // Compute cubema + Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, + {ax->result_id(), ay->result_id()}); + Instruction* amax = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, + {az->result_id(), amax_x_y->result_id()}); + Instruction* cubema = ir_builder.AddBinaryOp(float_type_id, SpvOpFMul, + f2_const_id, amax->result_id()); + + // Do the comparisons needed for computing cubesc and cubetc. + Instruction* is_z_max = + ir_builder.AddBinaryOp(bool_id, SpvOpFOrdGreaterThanEqual, + az->result_id(), amax_x_y->result_id()); + Instruction* not_is_z_max = + ir_builder.AddUnaryOp(bool_id, SpvOpLogicalNot, is_z_max->result_id()); + Instruction* y_gr_x = ir_builder.AddBinaryOp( + bool_id, SpvOpFOrdGreaterThanEqual, ay->result_id(), ax->result_id()); + Instruction* is_y_max = ir_builder.AddBinaryOp( + bool_id, SpvOpLogicalAnd, not_is_z_max->result_id(), y_gr_x->result_id()); + + // Select the correct value for cubesc. + Instruction* cubesc_case_1 = ir_builder.AddSelect( + float_type_id, is_z_neg->result_id(), nx->result_id(), x->result_id()); + Instruction* cubesc_case_2 = ir_builder.AddSelect( + float_type_id, is_x_neg->result_id(), z->result_id(), nz->result_id()); + Instruction* sel = + ir_builder.AddSelect(float_type_id, is_y_max->result_id(), x->result_id(), + cubesc_case_2->result_id()); + Instruction* cubesc = + ir_builder.AddSelect(float_type_id, is_z_max->result_id(), + cubesc_case_1->result_id(), sel->result_id()); + + // Select the correct value for cubetc. + Instruction* cubetc_case_1 = ir_builder.AddSelect( + float_type_id, is_y_neg->result_id(), nz->result_id(), z->result_id()); + Instruction* cubetc = + ir_builder.AddSelect(float_type_id, is_y_max->result_id(), + cubetc_case_1->result_id(), ny->result_id()); + + // Do the division + Instruction* cube = ir_builder.AddCompositeConstruct( + v2_float_type_id, {cubesc->result_id(), cubetc->result_id()}); + Instruction* denom = ir_builder.AddCompositeConstruct( + v2_float_type_id, {cubema->result_id(), cubema->result_id()}); + Instruction* div = ir_builder.AddBinaryOp( + v2_float_type_id, SpvOpFDiv, cube->result_id(), denom->result_id()); + + // Get the final result by adding 0.5 to |div|. + inst->SetOpcode(SpvOpFAdd); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {div->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {vec_const_id}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// A folding rule that will replace the CubeFaceIndexAMD extended +// instruction in the SPV_AMD_gcn_shader_ballot. Returns true if the folding +// is successful. +// +// The instruction +// +// %result = OpExtInst %float %1 CubeFaceIndexAMD %input +// +// with +// +// %x = OpCompositeExtract %float %input 0 +// %y = OpCompositeExtract %float %input 1 +// %z = OpCompositeExtract %float %input 2 +// %ax = OpExtInst %float %n_1 FAbs %x +// %ay = OpExtInst %float %n_1 FAbs %y +// %az = OpExtInst %float %n_1 FAbs %z +// %is_z_neg = OpFOrdLessThan %bool %z %float_0 +// %is_y_neg = OpFOrdLessThan %bool %y %float_0 +// %is_x_neg = OpFOrdLessThan %bool %x %float_0 +// %amax_x_y = OpExtInst %float %n_1 FMax %ax %ay +// %is_z_max = OpFOrdGreaterThanEqual %bool %az %amax_x_y +// %y_gt_x = OpFOrdGreaterThanEqual %bool %ay %ax +// %case_z = OpSelect %float %is_z_neg %float_5 %float4 +// %case_y = OpSelect %float %is_y_neg %float_3 %float2 +// %case_x = OpSelect %float %is_x_neg %float_1 %float0 +// %sel = OpSelect %float %y_gt_x %case_y %case_x +// %result = OpSelect %float %is_z_max %case_z %sel +// +// Also adding the capabilities and builtins that are needed. +bool ReplaceCubeFaceIndex(IRContext* ctx, Instruction* inst, + const std::vector&) { + analysis::TypeManager* type_mgr = ctx->get_type_mgr(); + analysis::ConstantManager* const_mgr = ctx->get_constant_mgr(); + + uint32_t float_type_id = type_mgr->GetFloatTypeId(); + uint32_t bool_id = type_mgr->GetBoolTypeId(); + + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + uint32_t input_id = inst->GetSingleWordInOperand(2); + uint32_t glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + if (glsl405_ext_inst_id == 0) { + ctx->AddExtInstImport("GLSL.std.450"); + glsl405_ext_inst_id = + ctx->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + } + + // Get the constants that will be used. + uint32_t f0_const_id = const_mgr->GetFloatConst(0.0); + uint32_t f1_const_id = const_mgr->GetFloatConst(1.0); + uint32_t f2_const_id = const_mgr->GetFloatConst(2.0); + uint32_t f3_const_id = const_mgr->GetFloatConst(3.0); + uint32_t f4_const_id = const_mgr->GetFloatConst(4.0); + uint32_t f5_const_id = const_mgr->GetFloatConst(5.0); + + // Extract the input values. + Instruction* x = ir_builder.AddCompositeExtract(float_type_id, input_id, {0}); + Instruction* y = ir_builder.AddCompositeExtract(float_type_id, input_id, {1}); + Instruction* z = ir_builder.AddCompositeExtract(float_type_id, input_id, {2}); + + // Get the absolute values of the inputs. + Instruction* ax = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {x->result_id()}); + Instruction* ay = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {y->result_id()}); + Instruction* az = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FAbs, {z->result_id()}); + + // Find which values are negative. Used in later computations. + Instruction* is_z_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan, + z->result_id(), f0_const_id); + Instruction* is_y_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan, + y->result_id(), f0_const_id); + Instruction* is_x_neg = ir_builder.AddBinaryOp(bool_id, SpvOpFOrdLessThan, + x->result_id(), f0_const_id); + + // Find the max value. + Instruction* amax_x_y = ir_builder.AddNaryExtendedInstruction( + float_type_id, glsl405_ext_inst_id, GLSLstd450FMax, + {ax->result_id(), ay->result_id()}); + Instruction* is_z_max = + ir_builder.AddBinaryOp(bool_id, SpvOpFOrdGreaterThanEqual, + az->result_id(), amax_x_y->result_id()); + Instruction* y_gr_x = ir_builder.AddBinaryOp( + bool_id, SpvOpFOrdGreaterThanEqual, ay->result_id(), ax->result_id()); + + // Get the value for each case. + Instruction* case_z = ir_builder.AddSelect( + float_type_id, is_z_neg->result_id(), f5_const_id, f4_const_id); + Instruction* case_y = ir_builder.AddSelect( + float_type_id, is_y_neg->result_id(), f3_const_id, f2_const_id); + Instruction* case_x = ir_builder.AddSelect( + float_type_id, is_x_neg->result_id(), f1_const_id, f0_const_id); + + // Select the correct case. + Instruction* sel = + ir_builder.AddSelect(float_type_id, y_gr_x->result_id(), + case_y->result_id(), case_x->result_id()); + + // Get the final result by adding 0.5 to |div|. + inst->SetOpcode(SpvOpSelect); + Instruction::OperandList new_operands; + new_operands.push_back({SPV_OPERAND_TYPE_ID, {is_z_max->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {case_z->result_id()}}); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {sel->result_id()}}); + + inst->SetInOperands(std::move(new_operands)); + ctx->UpdateDefUse(inst); + return true; +} + +// A folding rule that will replace the TimeAMD extended instruction in the +// SPV_AMD_gcn_shader_ballot. It returns true if the folding is successful. +// It returns False, otherwise. +// +// The instruction +// +// %result = OpExtInst %uint64 %1 TimeAMD +// +// with +// +// %result = OpReadClockKHR %uint64 %uint_3 +// +// NOTE: TimeAMD uses subgroup scope (it is not a real time clock). +bool ReplaceTimeAMD(IRContext* ctx, Instruction* inst, + const std::vector&) { + InstructionBuilder ir_builder( + ctx, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + ctx->AddExtension("SPV_KHR_shader_clock"); + ctx->AddCapability(SpvCapabilityShaderClockKHR); + + inst->SetOpcode(SpvOpReadClockKHR); + Instruction::OperandList args; + uint32_t subgroup_scope_id = ir_builder.GetUintConstantId(SpvScopeSubgroup); + args.push_back({SPV_OPERAND_TYPE_ID, {subgroup_scope_id}}); + inst->SetInOperands(std::move(args)); + ctx->UpdateDefUse(inst); + + return true; +} + +class AmdExtFoldingRules : public FoldingRules { + public: + explicit AmdExtFoldingRules(IRContext* ctx) : FoldingRules(ctx) {} + + protected: + virtual void AddFoldingRules() override { + rules_[SpvOpGroupIAddNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[SpvOpGroupFAddNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[SpvOpGroupUMinNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[SpvOpGroupSMinNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[SpvOpGroupFMinNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[SpvOpGroupUMaxNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[SpvOpGroupSMaxNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + rules_[SpvOpGroupFMaxNonUniformAMD].push_back( + ReplaceGroupNonuniformOperationOpCode); + + uint32_t extension_id = + context()->module()->GetExtInstImportId("SPV_AMD_shader_ballot"); + + if (extension_id != 0) { + ext_rules_[{extension_id, AmdShaderBallotSwizzleInvocationsAMD}] + .push_back(ReplaceSwizzleInvocations); + ext_rules_[{extension_id, AmdShaderBallotSwizzleInvocationsMaskedAMD}] + .push_back(ReplaceSwizzleInvocationsMasked); + ext_rules_[{extension_id, AmdShaderBallotWriteInvocationAMD}].push_back( + ReplaceWriteInvocation); + ext_rules_[{extension_id, AmdShaderBallotMbcntAMD}].push_back( + ReplaceMbcnt); + } + + extension_id = context()->module()->GetExtInstImportId( + "SPV_AMD_shader_trinary_minmax"); + + if (extension_id != 0) { + ext_rules_[{extension_id, FMin3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, UMin3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, SMin3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, FMax3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, UMax3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, SMax3AMD}].push_back( + ReplaceTrinaryMinMax); + ext_rules_[{extension_id, FMid3AMD}].push_back( + ReplaceTrinaryMid); + ext_rules_[{extension_id, UMid3AMD}].push_back( + ReplaceTrinaryMid); + ext_rules_[{extension_id, SMid3AMD}].push_back( + ReplaceTrinaryMid); + } + + extension_id = + context()->module()->GetExtInstImportId("SPV_AMD_gcn_shader"); + + if (extension_id != 0) { + ext_rules_[{extension_id, CubeFaceCoordAMD}].push_back( + ReplaceCubeFaceCoord); + ext_rules_[{extension_id, CubeFaceIndexAMD}].push_back( + ReplaceCubeFaceIndex); + ext_rules_[{extension_id, TimeAMD}].push_back(ReplaceTimeAMD); + } + } +}; + +class AmdExtConstFoldingRules : public ConstantFoldingRules { + public: + AmdExtConstFoldingRules(IRContext* ctx) : ConstantFoldingRules(ctx) {} + + protected: + virtual void AddFoldingRules() override {} +}; + +} // namespace + +Pass::Status AmdExtensionToKhrPass::Process() { + bool changed = false; + + // Traverse the body of the functions to replace instructions that require + // the extensions. + InstructionFolder folder( + context(), + std::unique_ptr(new AmdExtFoldingRules(context())), + MakeUnique(context())); + for (Function& func : *get_module()) { + func.ForEachInst([&changed, &folder](Instruction* inst) { + if (folder.FoldInstruction(inst)) { + changed = true; + } + }); + } + + // Now that instruction that require the extensions have been removed, we can + // remove the extension instructions. + std::set ext_to_remove = {"SPV_AMD_shader_ballot", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader"}; + + std::vector to_be_killed; + for (Instruction& inst : context()->module()->extensions()) { + if (inst.opcode() == SpvOpExtension) { + if (ext_to_remove.count(reinterpret_cast( + &(inst.GetInOperand(0).words[0]))) != 0) { + to_be_killed.push_back(&inst); + } + } + } + + for (Instruction& inst : context()->ext_inst_imports()) { + if (inst.opcode() == SpvOpExtInstImport) { + if (ext_to_remove.count(reinterpret_cast( + &(inst.GetInOperand(0).words[0]))) != 0) { + to_be_killed.push_back(&inst); + } + } + } + + for (Instruction* inst : to_be_killed) { + context()->KillInst(inst); + changed = true; + } + + // The replacements that take place use instructions that are missing before + // SPIR-V 1.3. If we changed something, we will have to make sure the version + // is at least SPIR-V 1.3 to make sure those instruction can be used. + if (changed) { + uint32_t version = get_module()->version(); + if (version < 0x00010300 /*1.3*/) { + get_module()->set_version(0x00010300); + } + } + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/amd_ext_to_khr.h b/third_party/spirv-tools/source/opt/amd_ext_to_khr.h new file mode 100644 index 0000000..fd3dab4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/amd_ext_to_khr.h @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_AMD_EXT_TO_KHR_H_ +#define SOURCE_OPT_AMD_EXT_TO_KHR_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Replaces the extensions VK_AMD_shader_ballot, VK_AMD_gcn_shader, and +// VK_AMD_shader_trinary_minmax with equivalant code using core instructions and +// capabilities. +class AmdExtensionToKhrPass : public Pass { + public: + const char* name() const override { return "amd-ext-to-khr"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_AMD_EXT_TO_KHR_H_ diff --git a/third_party/spirv-tools/source/opt/basic_block.cpp b/third_party/spirv-tools/source/opt/basic_block.cpp new file mode 100644 index 0000000..3608448 --- /dev/null +++ b/third_party/spirv-tools/source/opt/basic_block.cpp @@ -0,0 +1,286 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/basic_block.h" + +#include + +#include "source/opt/function.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/reflect.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kLoopMergeContinueBlockIdInIdx = 1; +const uint32_t kLoopMergeMergeBlockIdInIdx = 0; +const uint32_t kSelectionMergeMergeBlockIdInIdx = 0; + +} // namespace + +BasicBlock* BasicBlock::Clone(IRContext* context) const { + BasicBlock* clone = new BasicBlock( + std::unique_ptr(GetLabelInst()->Clone(context))); + for (const auto& inst : insts_) { + // Use the incoming context + clone->AddInstruction(std::unique_ptr(inst.Clone(context))); + } + + if (context->AreAnalysesValid( + IRContext::Analysis::kAnalysisInstrToBlockMapping)) { + for (auto& inst : *clone) { + context->set_instr_block(&inst, clone); + } + } + + return clone; +} + +const Instruction* BasicBlock::GetMergeInst() const { + const Instruction* result = nullptr; + // If it exists, the merge instruction immediately precedes the + // terminator. + auto iter = ctail(); + if (iter != cbegin()) { + --iter; + const auto opcode = iter->opcode(); + if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { + result = &*iter; + } + } + return result; +} + +Instruction* BasicBlock::GetMergeInst() { + Instruction* result = nullptr; + // If it exists, the merge instruction immediately precedes the + // terminator. + auto iter = tail(); + if (iter != begin()) { + --iter; + const auto opcode = iter->opcode(); + if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { + result = &*iter; + } + } + return result; +} + +const Instruction* BasicBlock::GetLoopMergeInst() const { + if (auto* merge = GetMergeInst()) { + if (merge->opcode() == SpvOpLoopMerge) { + return merge; + } + } + return nullptr; +} + +Instruction* BasicBlock::GetLoopMergeInst() { + if (auto* merge = GetMergeInst()) { + if (merge->opcode() == SpvOpLoopMerge) { + return merge; + } + } + return nullptr; +} + +void BasicBlock::KillAllInsts(bool killLabel) { + ForEachInst([killLabel](Instruction* ip) { + if (killLabel || ip->opcode() != SpvOpLabel) { + ip->context()->KillInst(ip); + } + }); +} + +void BasicBlock::ForEachSuccessorLabel( + const std::function& f) const { + WhileEachSuccessorLabel([f](const uint32_t l) { + f(l); + return true; + }); +} + +bool BasicBlock::WhileEachSuccessorLabel( + const std::function& f) const { + const auto br = &insts_.back(); + switch (br->opcode()) { + case SpvOpBranch: + return f(br->GetOperand(0).words[0]); + case SpvOpBranchConditional: + case SpvOpSwitch: { + bool is_first = true; + return br->WhileEachInId([&is_first, &f](const uint32_t* idp) { + if (!is_first) return f(*idp); + is_first = false; + return true; + }); + } + default: + return true; + } +} + +void BasicBlock::ForEachSuccessorLabel( + const std::function& f) { + auto br = &insts_.back(); + switch (br->opcode()) { + case SpvOpBranch: { + uint32_t tmp_id = br->GetOperand(0).words[0]; + f(&tmp_id); + if (tmp_id != br->GetOperand(0).words[0]) br->SetOperand(0, {tmp_id}); + } break; + case SpvOpBranchConditional: + case SpvOpSwitch: { + bool is_first = true; + br->ForEachInId([&is_first, &f](uint32_t* idp) { + if (!is_first) f(idp); + is_first = false; + }); + } break; + default: + break; + } +} + +bool BasicBlock::IsSuccessor(const BasicBlock* block) const { + uint32_t succId = block->id(); + bool isSuccessor = false; + ForEachSuccessorLabel([&isSuccessor, succId](const uint32_t label) { + if (label == succId) isSuccessor = true; + }); + return isSuccessor; +} + +void BasicBlock::ForMergeAndContinueLabel( + const std::function& f) { + auto ii = insts_.end(); + --ii; + if (ii == insts_.begin()) return; + --ii; + if (ii->opcode() == SpvOpSelectionMerge || ii->opcode() == SpvOpLoopMerge) { + ii->ForEachInId([&f](const uint32_t* idp) { f(*idp); }); + } +} + +uint32_t BasicBlock::MergeBlockIdIfAny() const { + auto merge_ii = cend(); + --merge_ii; + uint32_t mbid = 0; + if (merge_ii != cbegin()) { + --merge_ii; + if (merge_ii->opcode() == SpvOpLoopMerge) { + mbid = merge_ii->GetSingleWordInOperand(kLoopMergeMergeBlockIdInIdx); + } else if (merge_ii->opcode() == SpvOpSelectionMerge) { + mbid = merge_ii->GetSingleWordInOperand(kSelectionMergeMergeBlockIdInIdx); + } + } + + return mbid; +} + +uint32_t BasicBlock::MergeBlockId() const { + uint32_t mbid = MergeBlockIdIfAny(); + assert(mbid && "Expected block to have a corresponding merge block"); + return mbid; +} + +uint32_t BasicBlock::ContinueBlockIdIfAny() const { + auto merge_ii = cend(); + --merge_ii; + uint32_t cbid = 0; + if (merge_ii != cbegin()) { + --merge_ii; + if (merge_ii->opcode() == SpvOpLoopMerge) { + cbid = merge_ii->GetSingleWordInOperand(kLoopMergeContinueBlockIdInIdx); + } + } + return cbid; +} + +uint32_t BasicBlock::ContinueBlockId() const { + uint32_t cbid = ContinueBlockIdIfAny(); + assert(cbid && "Expected block to have a corresponding continue target"); + return cbid; +} + +std::ostream& operator<<(std::ostream& str, const BasicBlock& block) { + str << block.PrettyPrint(); + return str; +} + +void BasicBlock::Dump() const { + std::cerr << "Basic block #" << id() << "\n" << *this << "\n "; +} + +std::string BasicBlock::PrettyPrint(uint32_t options) const { + std::ostringstream str; + ForEachInst([&str, options](const Instruction* inst) { + str << inst->PrettyPrint(options); + if (!IsTerminatorInst(inst->opcode())) { + str << std::endl; + } + }); + return str.str(); +} + +BasicBlock* BasicBlock::SplitBasicBlock(IRContext* context, uint32_t label_id, + iterator iter) { + assert(!insts_.empty()); + + std::unique_ptr new_block_temp = + MakeUnique(MakeUnique( + context, SpvOpLabel, 0, label_id, std::initializer_list{})); + BasicBlock* new_block = new_block_temp.get(); + function_->InsertBasicBlockAfter(std::move(new_block_temp), this); + + new_block->insts_.Splice(new_block->end(), &insts_, iter, end()); + new_block->SetParent(GetParent()); + + context->AnalyzeDefUse(new_block->GetLabelInst()); + + // Update the phi nodes in the successor blocks to reference the new block id. + const_cast(new_block)->ForEachSuccessorLabel( + [new_block, this, context](const uint32_t label) { + BasicBlock* target_bb = context->get_instr_block(label); + target_bb->ForEachPhiInst( + [this, new_block, context](Instruction* phi_inst) { + bool changed = false; + for (uint32_t i = 1; i < phi_inst->NumInOperands(); i += 2) { + if (phi_inst->GetSingleWordInOperand(i) == this->id()) { + changed = true; + phi_inst->SetInOperand(i, {new_block->id()}); + } + } + + if (changed) { + context->UpdateDefUse(phi_inst); + } + }); + }); + + if (context->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { + context->set_instr_block(new_block->GetLabelInst(), new_block); + new_block->ForEachInst([new_block, context](Instruction* inst) { + context->set_instr_block(inst, new_block); + }); + } + + return new_block; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/basic_block.h b/third_party/spirv-tools/source/opt/basic_block.h new file mode 100644 index 0000000..6741a50 --- /dev/null +++ b/third_party/spirv-tools/source/opt/basic_block.h @@ -0,0 +1,342 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file defines the language constructs for representing a SPIR-V +// module in memory. + +#ifndef SOURCE_OPT_BASIC_BLOCK_H_ +#define SOURCE_OPT_BASIC_BLOCK_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/instruction_list.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +class Function; +class IRContext; + +// A SPIR-V basic block. +class BasicBlock { + public: + using iterator = InstructionList::iterator; + using const_iterator = InstructionList::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = + std::reverse_iterator; + + // Creates a basic block with the given starting |label|. + inline explicit BasicBlock(std::unique_ptr label); + + explicit BasicBlock(const BasicBlock& bb) = delete; + + // Creates a clone of the basic block in the given |context| + // + // The parent function will default to null and needs to be explicitly set by + // the user. + // + // If the inst-to-block map in |context| is valid, then the new instructions + // will be inserted into the map. + BasicBlock* Clone(IRContext*) const; + + // Sets the enclosing function for this basic block. + void SetParent(Function* function) { function_ = function; } + + // Return the enclosing function + inline Function* GetParent() const { return function_; } + + // Appends an instruction to this basic block. + inline void AddInstruction(std::unique_ptr i); + + // Appends all of block's instructions (except label) to this block + inline void AddInstructions(BasicBlock* bp); + + // The pointer to the label starting this basic block. + std::unique_ptr& GetLabel() { return label_; } + + // The label starting this basic block. + Instruction* GetLabelInst() { return label_.get(); } + const Instruction* GetLabelInst() const { return label_.get(); } + + // Returns the merge instruction in this basic block, if it exists. + // Otherwise return null. May be used whenever tail() can be used. + const Instruction* GetMergeInst() const; + Instruction* GetMergeInst(); + + // Returns the OpLoopMerge instruciton in this basic block, if it exists. + // Otherwise return null. May be used whenever tail() can be used. + const Instruction* GetLoopMergeInst() const; + Instruction* GetLoopMergeInst(); + + // Returns the id of the label at the top of this block + inline uint32_t id() const { return label_->result_id(); } + + iterator begin() { return insts_.begin(); } + iterator end() { return insts_.end(); } + const_iterator begin() const { return insts_.cbegin(); } + const_iterator end() const { return insts_.cend(); } + const_iterator cbegin() const { return insts_.cbegin(); } + const_iterator cend() const { return insts_.cend(); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(cend()); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(cbegin()); + } + const_reverse_iterator crbegin() const { + return const_reverse_iterator(cend()); + } + const_reverse_iterator crend() const { + return const_reverse_iterator(cbegin()); + } + + // Returns an iterator pointing to the last instruction. This may only + // be used if this block has an instruction other than the OpLabel + // that defines it. + iterator tail() { + assert(!insts_.empty()); + return --end(); + } + + // Returns a const iterator, but othewrise similar to tail(). + const_iterator ctail() const { + assert(!insts_.empty()); + return --insts_.cend(); + } + + // Returns true if the basic block has at least one successor. + inline bool hasSuccessor() const { return ctail()->IsBranch(); } + + // Runs the given function |f| on each instruction in this basic block, and + // optionally on the debug line instructions that might precede them. + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Runs the given function |f| on each instruction in this basic block, and + // optionally on the debug line instructions that might precede them. If |f| + // returns false, iteration is terminated and this function returns false. + inline bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + inline bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Runs the given function |f| on each Phi instruction in this basic block, + // and optionally on the debug line instructions that might precede them. + inline void ForEachPhiInst(const std::function& f, + bool run_on_debug_line_insts = false); + + // Runs the given function |f| on each Phi instruction in this basic block, + // and optionally on the debug line instructions that might precede them. If + // |f| returns false, iteration is terminated and this function return false. + inline bool WhileEachPhiInst(const std::function& f, + bool run_on_debug_line_insts = false); + + // Runs the given function |f| on each label id of each successor block + void ForEachSuccessorLabel( + const std::function& f) const; + + // Runs the given function |f| on each label id of each successor block. If + // |f| returns false, iteration is terminated and this function returns false. + bool WhileEachSuccessorLabel( + const std::function& f) const; + + // Runs the given function |f| on each label id of each successor block. + // Modifying the pointed value will change the branch taken by the basic + // block. It is the caller responsibility to update or invalidate the CFG. + void ForEachSuccessorLabel(const std::function& f); + + // Returns true if |block| is a direct successor of |this|. + bool IsSuccessor(const BasicBlock* block) const; + + // Runs the given function |f| on the merge and continue label, if any + void ForMergeAndContinueLabel(const std::function& f); + + // Returns true if this basic block has any Phi instructions. + bool HasPhiInstructions() { + return !WhileEachPhiInst([](Instruction*) { return false; }); + } + + // Return true if this block is a loop header block. + bool IsLoopHeader() const { return GetLoopMergeInst() != nullptr; } + + // Returns the ID of the merge block declared by a merge instruction in this + // block, if any. If none, returns zero. + uint32_t MergeBlockIdIfAny() const; + + // Returns MergeBlockIdIfAny() and asserts that it is non-zero. + uint32_t MergeBlockId() const; + + // Returns the ID of the continue block declared by a merge instruction in + // this block, if any. If none, returns zero. + uint32_t ContinueBlockIdIfAny() const; + + // Returns ContinueBlockIdIfAny() and asserts that it is non-zero. + uint32_t ContinueBlockId() const; + + // Returns the terminator instruction. Assumes the terminator exists. + Instruction* terminator() { return &*tail(); } + const Instruction* terminator() const { return &*ctail(); } + + // Returns true if this basic block exits this function and returns to its + // caller. + bool IsReturn() const { return ctail()->IsReturn(); } + + // Returns true if this basic block exits this function or aborts execution. + bool IsReturnOrAbort() const { return ctail()->IsReturnOrAbort(); } + + // Kill all instructions in this block. Whether or not to kill the label is + // indicated by |killLabel|. + void KillAllInsts(bool killLabel); + + // Splits this basic block into two. Returns a new basic block with label + // |label_id| containing the instructions from |iter| onwards. Instructions + // prior to |iter| remain in this basic block. The new block will be added + // to the function immediately after the original block. + BasicBlock* SplitBasicBlock(IRContext* context, uint32_t label_id, + iterator iter); + + // Pretty-prints this basic block into a std::string by printing every + // instruction in it. + // + // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER + // is always added to |options|. + std::string PrettyPrint(uint32_t options = 0u) const; + + // Dump this basic block on stderr. Useful when running interactive + // debuggers. + void Dump() const; + + private: + // The enclosing function. + Function* function_; + // The label starting this basic block. + std::unique_ptr label_; + // Instructions inside this basic block, but not the OpLabel. + InstructionList insts_; +}; + +// Pretty-prints |block| to |str|. Returns |str|. +std::ostream& operator<<(std::ostream& str, const BasicBlock& block); + +inline BasicBlock::BasicBlock(std::unique_ptr label) + : function_(nullptr), label_(std::move(label)) {} + +inline void BasicBlock::AddInstruction(std::unique_ptr i) { + insts_.push_back(std::move(i)); +} + +inline void BasicBlock::AddInstructions(BasicBlock* bp) { + auto bEnd = end(); + (void)bEnd.MoveBefore(&bp->insts_); +} + +inline bool BasicBlock::WhileEachInst( + const std::function& f, bool run_on_debug_line_insts) { + if (label_) { + if (!label_->WhileEachInst(f, run_on_debug_line_insts)) return false; + } + if (insts_.empty()) { + return true; + } + + Instruction* inst = &insts_.front(); + while (inst != nullptr) { + Instruction* next_instruction = inst->NextNode(); + if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false; + inst = next_instruction; + } + return true; +} + +inline bool BasicBlock::WhileEachInst( + const std::function& f, + bool run_on_debug_line_insts) const { + if (label_) { + if (!static_cast(label_.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) + return false; + } + for (const auto& inst : insts_) { + if (!static_cast(&inst)->WhileEachInst( + f, run_on_debug_line_insts)) + return false; + } + return true; +} + +inline void BasicBlock::ForEachInst(const std::function& f, + bool run_on_debug_line_insts) { + WhileEachInst( + [&f](Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +inline void BasicBlock::ForEachInst( + const std::function& f, + bool run_on_debug_line_insts) const { + WhileEachInst( + [&f](const Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +inline bool BasicBlock::WhileEachPhiInst( + const std::function& f, bool run_on_debug_line_insts) { + if (insts_.empty()) { + return true; + } + + Instruction* inst = &insts_.front(); + while (inst != nullptr) { + Instruction* next_instruction = inst->NextNode(); + if (inst->opcode() != SpvOpPhi) break; + if (!inst->WhileEachInst(f, run_on_debug_line_insts)) return false; + inst = next_instruction; + } + return true; +} + +inline void BasicBlock::ForEachPhiInst( + const std::function& f, bool run_on_debug_line_insts) { + WhileEachPhiInst( + [&f](Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_BASIC_BLOCK_H_ diff --git a/third_party/spirv-tools/source/opt/block_merge_pass.cpp b/third_party/spirv-tools/source/opt/block_merge_pass.cpp new file mode 100644 index 0000000..c7315ba --- /dev/null +++ b/third_party/spirv-tools/source/opt/block_merge_pass.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/block_merge_pass.h" + +#include + +#include "source/opt/block_merge_util.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +bool BlockMergePass::MergeBlocks(Function* func) { + bool modified = false; + for (auto bi = func->begin(); bi != func->end();) { + if (blockmergeutil::CanMergeWithSuccessor(context(), &*bi)) { + blockmergeutil::MergeWithSuccessor(context(), func, bi); + // Reprocess block. + modified = true; + } else { + ++bi; + } + } + return modified; +} + +Pass::Status BlockMergePass::Process() { + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { return MergeBlocks(fp); }; + bool modified = context()->ProcessEntryPointCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +BlockMergePass::BlockMergePass() = default; + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/block_merge_pass.h b/third_party/spirv-tools/source/opt/block_merge_pass.h new file mode 100644 index 0000000..aabf789 --- /dev/null +++ b/third_party/spirv-tools/source/opt/block_merge_pass.h @@ -0,0 +1,62 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_BLOCK_MERGE_PASS_H_ +#define SOURCE_OPT_BLOCK_MERGE_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class BlockMergePass : public Pass { + public: + BlockMergePass(); + const char* name() const override { return "merge-blocks"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + + // Search |func| for blocks which have a single Branch to a block + // with no other predecessors. Merge these blocks into a single block. + bool MergeBlocks(Function* func); + +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_BLOCK_MERGE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/block_merge_util.cpp b/third_party/spirv-tools/source/opt/block_merge_util.cpp new file mode 100644 index 0000000..14b5d36 --- /dev/null +++ b/third_party/spirv-tools/source/opt/block_merge_util.cpp @@ -0,0 +1,194 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "block_merge_util.h" + +namespace spvtools { +namespace opt { +namespace blockmergeutil { + +namespace { + +// Returns true if |block| contains a merge instruction. +bool IsHeader(BasicBlock* block) { return block->GetMergeInst() != nullptr; } + +// Returns true if |id| contains a merge instruction. +bool IsHeader(IRContext* context, uint32_t id) { + return IsHeader( + context->get_instr_block(context->get_def_use_mgr()->GetDef(id))); +} + +// Returns true if |id| is the merge target of a merge instruction. +bool IsMerge(IRContext* context, uint32_t id) { + return !context->get_def_use_mgr()->WhileEachUse(id, [](Instruction* user, + uint32_t index) { + SpvOp op = user->opcode(); + if ((op == SpvOpLoopMerge || op == SpvOpSelectionMerge) && index == 0u) { + return false; + } + return true; + }); +} + +// Returns true if |block| is the merge target of a merge instruction. +bool IsMerge(IRContext* context, BasicBlock* block) { + return IsMerge(context, block->id()); +} + +// Returns true if |id| is the continue target of a merge instruction. +bool IsContinue(IRContext* context, uint32_t id) { + return !context->get_def_use_mgr()->WhileEachUse( + id, [](Instruction* user, uint32_t index) { + SpvOp op = user->opcode(); + if (op == SpvOpLoopMerge && index == 1u) { + return false; + } + return true; + }); +} + +// Removes any OpPhi instructions in |block|, which should have exactly one +// predecessor, replacing uses of OpPhi ids with the ids associated with the +// predecessor. +void EliminateOpPhiInstructions(IRContext* context, BasicBlock* block) { + block->ForEachPhiInst([context](Instruction* phi) { + assert(2 == phi->NumInOperands() && + "A block can only have one predecessor for block merging to make " + "sense."); + context->ReplaceAllUsesWith(phi->result_id(), + phi->GetSingleWordInOperand(0)); + context->KillInst(phi); + }); +} + +} // Anonymous namespace + +bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block) { + // Find block with single successor which has no other predecessors. + auto ii = block->end(); + --ii; + Instruction* br = &*ii; + if (br->opcode() != SpvOpBranch) { + return false; + } + + const uint32_t lab_id = br->GetSingleWordInOperand(0); + if (context->cfg()->preds(lab_id).size() != 1) { + return false; + } + + bool pred_is_merge = IsMerge(context, block); + bool succ_is_merge = IsMerge(context, lab_id); + if (pred_is_merge && succ_is_merge) { + // Cannot merge two merges together. + return false; + } + + if (pred_is_merge && IsContinue(context, lab_id)) { + // Cannot merge a continue target with a merge block. + return false; + } + + // Don't bother trying to merge unreachable blocks. + if (auto dominators = context->GetDominatorAnalysis(block->GetParent())) { + if (!dominators->IsReachable(block)) return false; + } + + Instruction* merge_inst = block->GetMergeInst(); + const bool pred_is_header = IsHeader(block); + if (pred_is_header && lab_id != merge_inst->GetSingleWordInOperand(0u)) { + bool succ_is_header = IsHeader(context, lab_id); + if (pred_is_header && succ_is_header) { + // Cannot merge two headers together when the successor is not the merge + // block of the predecessor. + return false; + } + + // If this is a header block and the successor is not its merge, we must + // be careful about which blocks we are willing to merge together. + // OpLoopMerge must be followed by a conditional or unconditional branch. + // The merge must be a loop merge because a selection merge cannot be + // followed by an unconditional branch. + BasicBlock* succ_block = context->get_instr_block(lab_id); + SpvOp succ_term_op = succ_block->terminator()->opcode(); + assert(merge_inst->opcode() == SpvOpLoopMerge); + if (succ_term_op != SpvOpBranch && succ_term_op != SpvOpBranchConditional) { + return false; + } + } + return true; +} + +void MergeWithSuccessor(IRContext* context, Function* func, + Function::iterator bi) { + assert(CanMergeWithSuccessor(context, &*bi) && + "Precondition failure for MergeWithSuccessor: it must be legal to " + "merge the block and its successor."); + + auto ii = bi->end(); + --ii; + Instruction* br = &*ii; + const uint32_t lab_id = br->GetSingleWordInOperand(0); + Instruction* merge_inst = bi->GetMergeInst(); + bool pred_is_header = IsHeader(&*bi); + + // Merge blocks. + context->KillInst(br); + auto sbi = bi; + for (; sbi != func->end(); ++sbi) + if (sbi->id() == lab_id) break; + // If bi is sbi's only predecessor, it dominates sbi and thus + // sbi must follow bi in func's ordering. + assert(sbi != func->end()); + + // Update the inst-to-block mapping for the instructions in sbi. + for (auto& inst : *sbi) { + context->set_instr_block(&inst, &*bi); + } + + EliminateOpPhiInstructions(context, &*sbi); + + // Now actually move the instructions. + bi->AddInstructions(&*sbi); + + if (merge_inst) { + if (pred_is_header && lab_id == merge_inst->GetSingleWordInOperand(0u)) { + // Merging the header and merge blocks, so remove the structured control + // flow declaration. + context->KillInst(merge_inst); + } else { + // Move OpLine/OpNoLine information to merge_inst. This solves + // the validation error that OpLine is placed between OpLoopMerge + // and OpBranchConditional. + auto terminator = bi->terminator(); + auto& vec = terminator->dbg_line_insts(); + auto& new_vec = merge_inst->dbg_line_insts(); + new_vec.insert(new_vec.end(), vec.begin(), vec.end()); + terminator->clear_dbg_line_insts(); + + // Move the merge instruction to just before the terminator. + merge_inst->InsertBefore(terminator); + } + } + context->ReplaceAllUsesWith(lab_id, bi->id()); + context->KillInst(sbi->GetLabelInst()); + (void)sbi.Erase(); +} + +} // namespace blockmergeutil +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/block_merge_util.h b/third_party/spirv-tools/source/opt/block_merge_util.h new file mode 100644 index 0000000..e71e3d6 --- /dev/null +++ b/third_party/spirv-tools/source/opt/block_merge_util.h @@ -0,0 +1,44 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_BLOCK_MERGE_UTIL_H_ +#define SOURCE_OPT_BLOCK_MERGE_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functions for determining when it is safe to merge blocks, and for +// actually merging blocks, for use by various analyses and passes. +namespace blockmergeutil { + +// Returns true if and only if |block| has exactly one successor and merging +// this successor into |block| has no impact on the semantics or validity of the +// SPIR-V module. +bool CanMergeWithSuccessor(IRContext* context, BasicBlock* block); + +// Requires that |bi| has a successor that can be safely merged into |bi|, and +// performs the merge. +void MergeWithSuccessor(IRContext* context, Function* func, + Function::iterator bi); + +} // namespace blockmergeutil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_BLOCK_MERGE_UTIL_H_ diff --git a/third_party/spirv-tools/source/opt/build_module.cpp b/third_party/spirv-tools/source/opt/build_module.cpp new file mode 100644 index 0000000..fc76a3c --- /dev/null +++ b/third_party/spirv-tools/source/opt/build_module.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/build_module.h" + +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/ir_loader.h" +#include "source/table.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace { + +// Sets the module header for IrLoader. Meets the interface requirement of +// spvBinaryParse(). +spv_result_t SetSpvHeader(void* builder, spv_endianness_t, uint32_t magic, + uint32_t version, uint32_t generator, + uint32_t id_bound, uint32_t reserved) { + reinterpret_cast(builder)->SetModuleHeader( + magic, version, generator, id_bound, reserved); + return SPV_SUCCESS; +} + +// Processes a parsed instruction for IrLoader. Meets the interface requirement +// of spvBinaryParse(). +spv_result_t SetSpvInst(void* builder, const spv_parsed_instruction_t* inst) { + if (reinterpret_cast(builder)->AddInstruction(inst)) { + return SPV_SUCCESS; + } + return SPV_ERROR_INVALID_BINARY; +} + +} // namespace + +std::unique_ptr BuildModule(spv_target_env env, + MessageConsumer consumer, + const uint32_t* binary, + const size_t size) { + auto context = spvContextCreate(env); + SetContextMessageConsumer(context, consumer); + + auto irContext = MakeUnique(env, consumer); + opt::IrLoader loader(consumer, irContext->module()); + + spv_result_t status = spvBinaryParse(context, &loader, binary, size, + SetSpvHeader, SetSpvInst, nullptr); + loader.EndModule(); + + spvContextDestroy(context); + + return status == SPV_SUCCESS ? std::move(irContext) : nullptr; +} + +std::unique_ptr BuildModule(spv_target_env env, + MessageConsumer consumer, + const std::string& text, + uint32_t assemble_options) { + SpirvTools t(env); + t.SetMessageConsumer(consumer); + std::vector binary; + if (!t.Assemble(text, &binary, assemble_options)) return nullptr; + return BuildModule(env, consumer, binary.data(), binary.size()); +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/build_module.h b/third_party/spirv-tools/source/opt/build_module.h new file mode 100644 index 0000000..c9d1cf2 --- /dev/null +++ b/third_party/spirv-tools/source/opt/build_module.h @@ -0,0 +1,46 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_BUILD_MODULE_H_ +#define SOURCE_OPT_BUILD_MODULE_H_ + +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { + +// Builds an Module returns the owning IRContext from the given SPIR-V +// |binary|. |size| specifies number of words in |binary|. The |binary| will be +// decoded according to the given target |env|. Returns nullptr if errors occur +// and sends the errors to |consumer|. +std::unique_ptr BuildModule(spv_target_env env, + MessageConsumer consumer, + const uint32_t* binary, + size_t size); + +// Builds an Module and returns the owning IRContext from the given +// SPIR-V assembly |text|. The |text| will be encoded according to the given +// target |env|. Returns nullptr if errors occur and sends the errors to +// |consumer|. +std::unique_ptr BuildModule( + spv_target_env env, MessageConsumer consumer, const std::string& text, + uint32_t assemble_options = SpirvTools::kDefaultAssembleOption); + +} // namespace spvtools + +#endif // SOURCE_OPT_BUILD_MODULE_H_ diff --git a/third_party/spirv-tools/source/opt/ccp_pass.cpp b/third_party/spirv-tools/source/opt/ccp_pass.cpp new file mode 100644 index 0000000..d84f13f --- /dev/null +++ b/third_party/spirv-tools/source/opt/ccp_pass.cpp @@ -0,0 +1,343 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements conditional constant propagation as described in +// +// Constant propagation with conditional branches, +// Wegman and Zadeck, ACM TOPLAS 13(2):181-210. + +#include "source/opt/ccp_pass.h" + +#include +#include + +#include "source/opt/fold.h" +#include "source/opt/function.h" +#include "source/opt/module.h" +#include "source/opt/propagator.h" + +namespace spvtools { +namespace opt { + +namespace { + +// This SSA id is never defined nor referenced in the IR. It is a special ID +// which represents varying values. When an ID is found to have a varying +// value, its entry in the |values_| table maps to kVaryingSSAId. +const uint32_t kVaryingSSAId = std::numeric_limits::max(); + +} // namespace + +bool CCPPass::IsVaryingValue(uint32_t id) const { return id == kVaryingSSAId; } + +SSAPropagator::PropStatus CCPPass::MarkInstructionVarying(Instruction* instr) { + assert(instr->result_id() != 0 && + "Instructions with no result cannot be marked varying."); + values_[instr->result_id()] = kVaryingSSAId; + return SSAPropagator::kVarying; +} + +SSAPropagator::PropStatus CCPPass::VisitPhi(Instruction* phi) { + uint32_t meet_val_id = 0; + + // Implement the lattice meet operation. The result of this Phi instruction is + // interesting only if the meet operation over arguments coming through + // executable edges yields the same constant value. + for (uint32_t i = 2; i < phi->NumOperands(); i += 2) { + if (!propagator_->IsPhiArgExecutable(phi, i)) { + // Ignore arguments coming through non-executable edges. + continue; + } + uint32_t phi_arg_id = phi->GetSingleWordOperand(i); + auto it = values_.find(phi_arg_id); + if (it != values_.end()) { + // We found an argument with a constant value. Apply the meet operation + // with the previous arguments. + if (it->second == kVaryingSSAId) { + // The "constant" value is actually a placeholder for varying. Return + // varying for this phi. + return MarkInstructionVarying(phi); + } else if (meet_val_id == 0) { + // This is the first argument we find. Initialize the result to its + // constant value id. + meet_val_id = it->second; + } else if (it->second == meet_val_id) { + // The argument is the same constant value already computed. Continue + // looking. + continue; + } else { + // We either found a varying value, or another constant value different + // from the previous computed meet value. This Phi will never be + // constant. + return MarkInstructionVarying(phi); + } + } else { + // The incoming value has no recorded value and is therefore not + // interesting. A not interesting value joined with any other value is the + // other value. + continue; + } + } + + // If there are no incoming executable edges, the meet ID will still be 0. In + // that case, return not interesting to evaluate the Phi node again. + if (meet_val_id == 0) { + return SSAPropagator::kNotInteresting; + } + + // All the operands have the same constant value represented by |meet_val_id|. + // Set the Phi's result to that value and declare it interesting. + values_[phi->result_id()] = meet_val_id; + return SSAPropagator::kInteresting; +} + +SSAPropagator::PropStatus CCPPass::VisitAssignment(Instruction* instr) { + assert(instr->result_id() != 0 && + "Expecting an instruction that produces a result"); + + // If this is a copy operation, and the RHS is a known constant, assign its + // value to the LHS. + if (instr->opcode() == SpvOpCopyObject) { + uint32_t rhs_id = instr->GetSingleWordInOperand(0); + auto it = values_.find(rhs_id); + if (it != values_.end()) { + if (IsVaryingValue(it->second)) { + return MarkInstructionVarying(instr); + } else { + values_[instr->result_id()] = it->second; + return SSAPropagator::kInteresting; + } + } + return SSAPropagator::kNotInteresting; + } + + // Instructions with a RHS that cannot produce a constant are always varying. + if (!instr->IsFoldable()) { + return MarkInstructionVarying(instr); + } + + // See if the RHS of the assignment folds into a constant value. + auto map_func = [this](uint32_t id) { + auto it = values_.find(id); + if (it == values_.end() || IsVaryingValue(it->second)) { + return id; + } + return it->second; + }; + Instruction* folded_inst = + context()->get_instruction_folder().FoldInstructionToConstant(instr, + map_func); + + if (folded_inst != nullptr) { + // We do not want to change the body of the function by adding new + // instructions. When folding we can only generate new constants. + assert(folded_inst->IsConstant() && "CCP is only interested in constant."); + values_[instr->result_id()] = folded_inst->result_id(); + return SSAPropagator::kInteresting; + } + + // Conservatively mark this instruction as varying if any input id is varying. + if (!instr->WhileEachInId([this](uint32_t* op_id) { + auto iter = values_.find(*op_id); + if (iter != values_.end() && IsVaryingValue(iter->second)) return false; + return true; + })) { + return MarkInstructionVarying(instr); + } + + // If not, see if there is a least one unknown operand to the instruction. If + // so, we might be able to fold it later. + if (!instr->WhileEachInId([this](uint32_t* op_id) { + auto it = values_.find(*op_id); + if (it == values_.end()) return false; + return true; + })) { + return SSAPropagator::kNotInteresting; + } + + // Otherwise, we will never be able to fold this instruction, so mark it + // varying. + return MarkInstructionVarying(instr); +} + +SSAPropagator::PropStatus CCPPass::VisitBranch(Instruction* instr, + BasicBlock** dest_bb) const { + assert(instr->IsBranch() && "Expected a branch instruction."); + + *dest_bb = nullptr; + uint32_t dest_label = 0; + if (instr->opcode() == SpvOpBranch) { + // An unconditional jump always goes to its unique destination. + dest_label = instr->GetSingleWordInOperand(0); + } else if (instr->opcode() == SpvOpBranchConditional) { + // For a conditional branch, determine whether the predicate selector has a + // known value in |values_|. If it does, set the destination block + // according to the selector's boolean value. + uint32_t pred_id = instr->GetSingleWordOperand(0); + auto it = values_.find(pred_id); + if (it == values_.end() || IsVaryingValue(it->second)) { + // The predicate has an unknown value, either branch could be taken. + return SSAPropagator::kVarying; + } + + // Get the constant value for the predicate selector from the value table. + // Use it to decide which branch will be taken. + uint32_t pred_val_id = it->second; + const analysis::Constant* c = const_mgr_->FindDeclaredConstant(pred_val_id); + assert(c && "Expected to find a constant declaration for a known value."); + // Undef values should have returned as varying above. + assert(c->AsBoolConstant() || c->AsNullConstant()); + if (c->AsNullConstant()) { + dest_label = instr->GetSingleWordOperand(2u); + } else { + const analysis::BoolConstant* val = c->AsBoolConstant(); + dest_label = val->value() ? instr->GetSingleWordOperand(1) + : instr->GetSingleWordOperand(2); + } + } else { + // For an OpSwitch, extract the value taken by the switch selector and check + // which of the target literals it matches. The branch associated with that + // literal is the taken branch. + assert(instr->opcode() == SpvOpSwitch); + if (instr->GetOperand(0).words.size() != 1) { + // If the selector is wider than 32-bits, return varying. TODO(dnovillo): + // Add support for wider constants. + return SSAPropagator::kVarying; + } + uint32_t select_id = instr->GetSingleWordOperand(0); + auto it = values_.find(select_id); + if (it == values_.end() || IsVaryingValue(it->second)) { + // The selector has an unknown value, any of the branches could be taken. + return SSAPropagator::kVarying; + } + + // Get the constant value for the selector from the value table. Use it to + // decide which branch will be taken. + uint32_t select_val_id = it->second; + const analysis::Constant* c = + const_mgr_->FindDeclaredConstant(select_val_id); + assert(c && "Expected to find a constant declaration for a known value."); + // TODO: support 64-bit integer switches. + uint32_t constant_cond = 0; + if (const analysis::IntConstant* val = c->AsIntConstant()) { + constant_cond = val->words()[0]; + } else { + // Undef values should have returned varying above. + assert(c->AsNullConstant()); + constant_cond = 0; + } + + // Start assuming that the selector will take the default value; + dest_label = instr->GetSingleWordOperand(1); + for (uint32_t i = 2; i < instr->NumOperands(); i += 2) { + if (constant_cond == instr->GetSingleWordOperand(i)) { + dest_label = instr->GetSingleWordOperand(i + 1); + break; + } + } + } + + assert(dest_label && "Destination label should be set at this point."); + *dest_bb = context()->cfg()->block(dest_label); + return SSAPropagator::kInteresting; +} + +SSAPropagator::PropStatus CCPPass::VisitInstruction(Instruction* instr, + BasicBlock** dest_bb) { + *dest_bb = nullptr; + if (instr->opcode() == SpvOpPhi) { + return VisitPhi(instr); + } else if (instr->IsBranch()) { + return VisitBranch(instr, dest_bb); + } else if (instr->result_id()) { + return VisitAssignment(instr); + } + return SSAPropagator::kVarying; +} + +bool CCPPass::ReplaceValues() { + // Even if we make no changes to the function's IR, propagation may have + // created new constants. Even if those constants cannot be replaced in + // the IR, the constant definition itself is a change. To reflect this, + // we check whether the next ID to be given by the module is different than + // the original bound ID. If that happens, new instructions were added to the + // module during propagation. + // + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/3636 and + // https://github.com/KhronosGroup/SPIRV-Tools/issues/3991 for details. + bool changed_ir = (context()->module()->IdBound() > original_id_bound_); + + for (const auto& it : values_) { + uint32_t id = it.first; + uint32_t cst_id = it.second; + if (!IsVaryingValue(cst_id) && id != cst_id) { + context()->KillNamesAndDecorates(id); + changed_ir |= context()->ReplaceAllUsesWith(id, cst_id); + } + } + + return changed_ir; +} + +bool CCPPass::PropagateConstants(Function* fp) { + // Mark function parameters as varying. + fp->ForEachParam([this](const Instruction* inst) { + values_[inst->result_id()] = kVaryingSSAId; + }); + + const auto visit_fn = [this](Instruction* instr, BasicBlock** dest_bb) { + return VisitInstruction(instr, dest_bb); + }; + + propagator_ = + std::unique_ptr(new SSAPropagator(context(), visit_fn)); + + if (propagator_->Run(fp)) { + return ReplaceValues(); + } + + return false; +} + +void CCPPass::Initialize() { + const_mgr_ = context()->get_constant_mgr(); + + // Populate the constant table with values from constant declarations in the + // module. The values of each OpConstant declaration is the identity + // assignment (i.e., each constant is its own value). + for (const auto& inst : get_module()->types_values()) { + // Record compile time constant ids. Treat all other global values as + // varying. + if (inst.IsConstant()) { + values_[inst.result_id()] = inst.result_id(); + } else { + values_[inst.result_id()] = kVaryingSSAId; + } + } + + original_id_bound_ = context()->module()->IdBound(); +} + +Pass::Status CCPPass::Process() { + Initialize(); + + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { return PropagateConstants(fp); }; + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/ccp_pass.h b/third_party/spirv-tools/source/opt/ccp_pass.h new file mode 100644 index 0000000..fb20c78 --- /dev/null +++ b/third_party/spirv-tools/source/opt/ccp_pass.h @@ -0,0 +1,117 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CCP_PASS_H_ +#define SOURCE_OPT_CCP_PASS_H_ + +#include +#include + +#include "source/opt/constants.h" +#include "source/opt/function.h" +#include "source/opt/ir_context.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" +#include "source/opt/propagator.h" + +namespace spvtools { +namespace opt { + +class CCPPass : public MemPass { + public: + CCPPass() = default; + + const char* name() const override { return "ccp"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Initializes the pass. + void Initialize(); + + // Runs constant propagation on the given function |fp|. Returns true if any + // constants were propagated and the IR modified. + bool PropagateConstants(Function* fp); + + // Visits a single instruction |instr|. If the instruction is a conditional + // branch that always jumps to the same basic block, it sets the destination + // block in |dest_bb|. + SSAPropagator::PropStatus VisitInstruction(Instruction* instr, + BasicBlock** dest_bb); + + // Visits an OpPhi instruction |phi|. This applies the meet operator for the + // CCP lattice. Essentially, if all the operands in |phi| have the same + // constant value C, the result for |phi| gets assigned the value C. + SSAPropagator::PropStatus VisitPhi(Instruction* phi); + + // Visits an SSA assignment instruction |instr|. If the RHS of |instr| folds + // into a constant value C, then the LHS of |instr| is assigned the value C in + // |values_|. + SSAPropagator::PropStatus VisitAssignment(Instruction* instr); + + // Visits a branch instruction |instr|. If the branch is conditional + // (OpBranchConditional or OpSwitch), and the value of its selector is known, + // |dest_bb| will be set to the corresponding destination block. Unconditional + // branches always set |dest_bb| to the single destination block. + SSAPropagator::PropStatus VisitBranch(Instruction* instr, + BasicBlock** dest_bb) const; + + // Replaces all operands used in |fp| with the corresponding constant values + // in |values_|. Returns true if any operands were replaced, and false + // otherwise. + bool ReplaceValues(); + + // Marks |instr| as varying by registering a varying value for its result + // into the |values_| table. Returns SSAPropagator::kVarying. + SSAPropagator::PropStatus MarkInstructionVarying(Instruction* instr); + + // Returns true if |id| is the special SSA id that corresponds to a varying + // value. + bool IsVaryingValue(uint32_t id) const; + + // Constant manager for the parent IR context. Used to record new constants + // generated during propagation. + analysis::ConstantManager* const_mgr_; + + // Constant value table. Each entry in this map + // represents the compile-time constant value for |id| as declared by + // |const_decl_id|. Each |const_decl_id| in this table is an OpConstant + // declaration for the current module. + // + // Additionally, this table keeps track of SSA IDs with varying values. If an + // SSA ID is found to have a varying value, it will have an entry in this + // table that maps to the special SSA id kVaryingSSAId. These values are + // never replaced in the IR, they are used by CCP during propagation. + std::unordered_map values_; + + // Propagator engine used. + std::unique_ptr propagator_; + + // Value for the module's ID bound before running CCP. Used to detect whether + // propagation created new instructions. + uint32_t original_id_bound_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CCP_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/cfg.cpp b/third_party/spirv-tools/source/opt/cfg.cpp new file mode 100644 index 0000000..ac0fcc3 --- /dev/null +++ b/third_party/spirv-tools/source/opt/cfg.cpp @@ -0,0 +1,338 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/cfg.h" + +#include +#include + +#include "source/cfa.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { +namespace { + +using cbb_ptr = const opt::BasicBlock*; + +// Universal Limit of ResultID + 1 +const int kMaxResultId = 0x400000; + +} // namespace + +CFG::CFG(Module* module) + : module_(module), + pseudo_entry_block_(std::unique_ptr( + new Instruction(module->context(), SpvOpLabel, 0, 0, {}))), + pseudo_exit_block_(std::unique_ptr(new Instruction( + module->context(), SpvOpLabel, 0, kMaxResultId, {}))) { + for (auto& fn : *module) { + for (auto& blk : fn) { + RegisterBlock(&blk); + } + } +} + +void CFG::AddEdges(BasicBlock* blk) { + uint32_t blk_id = blk->id(); + // Force the creation of an entry, not all basic block have predecessors + // (such as the entry blocks and some unreachables). + label2preds_[blk_id]; + const auto* const_blk = blk; + const_blk->ForEachSuccessorLabel( + [blk_id, this](const uint32_t succ_id) { AddEdge(blk_id, succ_id); }); +} + +void CFG::RemoveNonExistingEdges(uint32_t blk_id) { + std::vector updated_pred_list; + for (uint32_t id : preds(blk_id)) { + const BasicBlock* pred_blk = block(id); + bool has_branch = false; + pred_blk->ForEachSuccessorLabel([&has_branch, blk_id](uint32_t succ) { + if (succ == blk_id) { + has_branch = true; + } + }); + if (has_branch) updated_pred_list.push_back(id); + } + + label2preds_.at(blk_id) = std::move(updated_pred_list); +} + +void CFG::ComputeStructuredOrder(Function* func, BasicBlock* root, + std::list* order) { + assert(module_->context()->get_feature_mgr()->HasCapability( + SpvCapabilityShader) && + "This only works on structured control flow"); + + // Compute structured successors and do DFS. + ComputeStructuredSuccessors(func); + auto ignore_block = [](cbb_ptr) {}; + auto ignore_edge = [](cbb_ptr, cbb_ptr) {}; + auto get_structured_successors = [this](const BasicBlock* b) { + return &(block2structured_succs_[b]); + }; + + // TODO(greg-lunarg): Get rid of const_cast by making moving const + // out of the cfa.h prototypes and into the invoking code. + auto post_order = [&](cbb_ptr b) { + order->push_front(const_cast(b)); + }; + CFA::DepthFirstTraversal(root, get_structured_successors, + ignore_block, post_order, ignore_edge); +} + +void CFG::ForEachBlockInPostOrder(BasicBlock* bb, + const std::function& f) { + std::vector po; + std::unordered_set seen; + ComputePostOrderTraversal(bb, &po, &seen); + + for (BasicBlock* current_bb : po) { + if (!IsPseudoExitBlock(current_bb) && !IsPseudoEntryBlock(current_bb)) { + f(current_bb); + } + } +} + +void CFG::ForEachBlockInReversePostOrder( + BasicBlock* bb, const std::function& f) { + WhileEachBlockInReversePostOrder(bb, [f](BasicBlock* b) { + f(b); + return true; + }); +} + +bool CFG::WhileEachBlockInReversePostOrder( + BasicBlock* bb, const std::function& f) { + std::vector po; + std::unordered_set seen; + ComputePostOrderTraversal(bb, &po, &seen); + + for (auto current_bb = po.rbegin(); current_bb != po.rend(); ++current_bb) { + if (!IsPseudoExitBlock(*current_bb) && !IsPseudoEntryBlock(*current_bb)) { + if (!f(*current_bb)) { + return false; + } + } + } + return true; +} + +void CFG::ComputeStructuredSuccessors(Function* func) { + block2structured_succs_.clear(); + for (auto& blk : *func) { + // If no predecessors in function, make successor to pseudo entry. + if (label2preds_[blk.id()].size() == 0) + block2structured_succs_[&pseudo_entry_block_].push_back(&blk); + + // If header, make merge block first successor and continue block second + // successor if there is one. + uint32_t mbid = blk.MergeBlockIdIfAny(); + if (mbid != 0) { + block2structured_succs_[&blk].push_back(block(mbid)); + uint32_t cbid = blk.ContinueBlockIdIfAny(); + if (cbid != 0) { + block2structured_succs_[&blk].push_back(block(cbid)); + } + } + + // Add true successors. + const auto& const_blk = blk; + const_blk.ForEachSuccessorLabel([&blk, this](const uint32_t sbid) { + block2structured_succs_[&blk].push_back(block(sbid)); + }); + } +} + +void CFG::ComputePostOrderTraversal(BasicBlock* bb, + std::vector* order, + std::unordered_set* seen) { + std::vector stack; + stack.push_back(bb); + while (!stack.empty()) { + bb = stack.back(); + seen->insert(bb); + static_cast(bb)->WhileEachSuccessorLabel( + [&seen, &stack, this](const uint32_t sbid) { + BasicBlock* succ_bb = id2block_[sbid]; + if (!seen->count(succ_bb)) { + stack.push_back(succ_bb); + return false; + } + return true; + }); + if (stack.back() == bb) { + order->push_back(bb); + stack.pop_back(); + } + } +} + +BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) { + assert(bb->GetLoopMergeInst() && "Expecting bb to be the header of a loop."); + + Function* fn = bb->GetParent(); + IRContext* context = module_->context(); + + // Get the new header id up front. If we are out of ids, then we cannot split + // the loop. + uint32_t new_header_id = context->TakeNextId(); + if (new_header_id == 0) { + return nullptr; + } + + // Find the insertion point for the new bb. + Function::iterator header_it = std::find_if( + fn->begin(), fn->end(), + [bb](BasicBlock& block_in_func) { return &block_in_func == bb; }); + assert(header_it != fn->end()); + + const std::vector& pred = preds(bb->id()); + // Find the back edge + BasicBlock* latch_block = nullptr; + Function::iterator latch_block_iter = header_it; + while (++latch_block_iter != fn->end()) { + // If blocks are in the proper order, then the only branch that appears + // after the header is the latch. + if (std::find(pred.begin(), pred.end(), latch_block_iter->id()) != + pred.end()) { + break; + } + } + assert(latch_block_iter != fn->end() && "Could not find the latch."); + latch_block = &*latch_block_iter; + + RemoveSuccessorEdges(bb); + + // Create the new header bb basic bb. + // Leave the phi instructions behind. + auto iter = bb->begin(); + while (iter->opcode() == SpvOpPhi) { + ++iter; + } + + BasicBlock* new_header = bb->SplitBasicBlock(context, new_header_id, iter); + context->AnalyzeDefUse(new_header->GetLabelInst()); + + // Update cfg + RegisterBlock(new_header); + + // Update bb mappings. + context->set_instr_block(new_header->GetLabelInst(), new_header); + new_header->ForEachInst([new_header, context](Instruction* inst) { + context->set_instr_block(inst, new_header); + }); + + // Adjust the OpPhi instructions as needed. + bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) { + std::vector preheader_phi_ops; + std::vector header_phi_ops; + + // Identify where the original inputs to original OpPhi belong: header or + // preheader. + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + uint32_t def_id = phi->GetSingleWordInOperand(i); + uint32_t branch_id = phi->GetSingleWordInOperand(i + 1); + if (branch_id == latch_block->id()) { + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {def_id}}); + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {branch_id}}); + } else { + preheader_phi_ops.push_back(def_id); + preheader_phi_ops.push_back(branch_id); + } + } + + // Create a phi instruction if and only if the preheader_phi_ops has more + // than one pair. + if (preheader_phi_ops.size() > 2) { + InstructionBuilder builder( + context, &*bb->begin(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops); + + // Add the OpPhi to the header bb. + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}}); + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}}); + } else { + // An OpPhi with a single entry is just a copy. In this case use the same + // instruction in the new header. + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}}); + header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}}); + } + + phi->RemoveFromList(); + std::unique_ptr phi_owner(phi); + phi->SetInOperands(std::move(header_phi_ops)); + new_header->begin()->InsertBefore(std::move(phi_owner)); + context->set_instr_block(phi, new_header); + context->AnalyzeUses(phi); + }); + + // Add a branch to the new header. + InstructionBuilder branch_builder( + context, bb, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + bb->AddInstruction( + MakeUnique(context, SpvOpBranch, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {new_header->id()}}})); + context->AnalyzeUses(bb->terminator()); + context->set_instr_block(bb->terminator(), bb); + label2preds_[new_header->id()].push_back(bb->id()); + + // Update the latch to branch to the new header. + latch_block->ForEachSuccessorLabel([bb, new_header_id](uint32_t* id) { + if (*id == bb->id()) { + *id = new_header_id; + } + }); + Instruction* latch_branch = latch_block->terminator(); + context->AnalyzeUses(latch_branch); + label2preds_[new_header->id()].push_back(latch_block->id()); + + auto& block_preds = label2preds_[bb->id()]; + auto latch_pos = + std::find(block_preds.begin(), block_preds.end(), latch_block->id()); + assert(latch_pos != block_preds.end() && "The cfg was invalid."); + block_preds.erase(latch_pos); + + // Update the loop descriptors + if (context->AreAnalysesValid(IRContext::kAnalysisLoopAnalysis)) { + LoopDescriptor* loop_desc = context->GetLoopDescriptor(bb->GetParent()); + Loop* loop = (*loop_desc)[bb->id()]; + + loop->AddBasicBlock(new_header_id); + loop->SetHeaderBlock(new_header); + loop_desc->SetBasicBlockToLoop(new_header_id, loop); + + loop->RemoveBasicBlock(bb->id()); + loop->SetPreHeaderBlock(bb); + + Loop* parent_loop = loop->GetParent(); + if (parent_loop != nullptr) { + parent_loop->AddBasicBlock(bb->id()); + loop_desc->SetBasicBlockToLoop(bb->id(), parent_loop); + } else { + loop_desc->SetBasicBlockToLoop(bb->id(), nullptr); + } + } + return new_header; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/cfg.h b/third_party/spirv-tools/source/opt/cfg.h new file mode 100644 index 0000000..f280682 --- /dev/null +++ b/third_party/spirv-tools/source/opt/cfg.h @@ -0,0 +1,181 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CFG_H_ +#define SOURCE_OPT_CFG_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" + +namespace spvtools { +namespace opt { + +class CFG { + public: + explicit CFG(Module* module); + + // Return the list of predecesors for basic block with label |blkid|. + // TODO(dnovillo): Move this to BasicBlock. + const std::vector& preds(uint32_t blk_id) const { + assert(label2preds_.count(blk_id)); + return label2preds_.at(blk_id); + } + + // Return a pointer to the basic block instance corresponding to the label + // |blk_id|. + BasicBlock* block(uint32_t blk_id) const { return id2block_.at(blk_id); } + + // Return the pseudo entry and exit blocks. + const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; } + BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; } + + const BasicBlock* pseudo_exit_block() const { return &pseudo_exit_block_; } + BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; } + + // Return true if |block_ptr| is the pseudo-entry block. + bool IsPseudoEntryBlock(BasicBlock* block_ptr) const { + return block_ptr == &pseudo_entry_block_; + } + + // Return true if |block_ptr| is the pseudo-exit block. + bool IsPseudoExitBlock(BasicBlock* block_ptr) const { + return block_ptr == &pseudo_exit_block_; + } + + // Compute structured block order into |order| for |func| starting at |root|. + // This order has the property that dominators come before all blocks they + // dominate, merge blocks come after all blocks that are in the control + // constructs of their header, and continue blocks come after all of the + // blocks in the body of their loop. + void ComputeStructuredOrder(Function* func, BasicBlock* root, + std::list* order); + + // Applies |f| to all blocks that can be reach from |bb| in post order. + void ForEachBlockInPostOrder(BasicBlock* bb, + const std::function& f); + + // Applies |f| to all blocks that can be reach from |bb| in reverse post + // order. + void ForEachBlockInReversePostOrder( + BasicBlock* bb, const std::function& f); + + // Applies |f| to all blocks that can be reach from |bb| in reverse post + // order. Return false if |f| return false on any basic block, and stops + // processing. + bool WhileEachBlockInReversePostOrder( + BasicBlock* bb, const std::function& f); + + // Registers |blk| as a basic block in the cfg, this also updates the + // predecessor lists of each successor of |blk|. |blk| must have a terminator + // instruction at the end of the block. + void RegisterBlock(BasicBlock* blk) { + assert(blk->begin() != blk->end() && + "Basic blocks must have a terminator before registering."); + assert(blk->tail()->IsBlockTerminator() && + "Basic blocks must have a terminator before registering."); + uint32_t blk_id = blk->id(); + id2block_[blk_id] = blk; + AddEdges(blk); + } + + // Removes from the CFG any mapping for the basic block id |blk_id|. + void ForgetBlock(const BasicBlock* blk) { + id2block_.erase(blk->id()); + label2preds_.erase(blk->id()); + RemoveSuccessorEdges(blk); + } + + void RemoveEdge(uint32_t pred_blk_id, uint32_t succ_blk_id) { + auto pred_it = label2preds_.find(succ_blk_id); + if (pred_it == label2preds_.end()) return; + auto& preds_list = pred_it->second; + auto it = std::find(preds_list.begin(), preds_list.end(), pred_blk_id); + if (it != preds_list.end()) preds_list.erase(it); + } + + // Registers |blk| to all of its successors. + void AddEdges(BasicBlock* blk); + + // Registers the basic block id |pred_blk_id| as being a predecessor of the + // basic block id |succ_blk_id|. + void AddEdge(uint32_t pred_blk_id, uint32_t succ_blk_id) { + label2preds_[succ_blk_id].push_back(pred_blk_id); + } + + // Removes any edges that no longer exist from the predecessor mapping for + // the basic block id |blk_id|. + void RemoveNonExistingEdges(uint32_t blk_id); + + // Remove all edges that leave |bb|. + void RemoveSuccessorEdges(const BasicBlock* bb) { + bb->ForEachSuccessorLabel( + [bb, this](uint32_t succ_id) { RemoveEdge(bb->id(), succ_id); }); + } + + // Divides |block| into two basic blocks. The first block will have the same + // id as |block| and will become a preheader for the loop. The other block + // is a new block that will be the new loop header. + // + // Returns a pointer to the new loop header. Returns |nullptr| if the new + // loop pointer could not be created. + BasicBlock* SplitLoopHeader(BasicBlock* bb); + + private: + // Compute structured successors for function |func|. A block's structured + // successors are the blocks it branches to together with its declared merge + // block and continue block if it has them. When order matters, the merge + // block and continue block always appear first. This assures correct depth + // first search in the presence of early returns and kills. If the successor + // vector contain duplicates of the merge or continue blocks, they are safely + // ignored by DFS. + void ComputeStructuredSuccessors(Function* func); + + // Computes the post-order traversal of the cfg starting at |bb| skipping + // nodes in |seen|. The order of the traversal is appended to |order|, and + // all nodes in the traversal are added to |seen|. + void ComputePostOrderTraversal(BasicBlock* bb, + std::vector* order, + std::unordered_set* seen); + + // Module for this CFG. + Module* module_; + + // Map from block to its structured successor blocks. See + // ComputeStructuredSuccessors() for definition. + std::unordered_map> + block2structured_succs_; + + // Extra block whose successors are all blocks with no predecessors + // in function. + BasicBlock pseudo_entry_block_; + + // Augmented CFG Exit Block. + BasicBlock pseudo_exit_block_; + + // Map from block's label id to its predecessor blocks ids + std::unordered_map> label2preds_; + + // Map from block's label id to block. + std::unordered_map id2block_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CFG_H_ diff --git a/third_party/spirv-tools/source/opt/cfg_cleanup_pass.cpp b/third_party/spirv-tools/source/opt/cfg_cleanup_pass.cpp new file mode 100644 index 0000000..6d48637 --- /dev/null +++ b/third_party/spirv-tools/source/opt/cfg_cleanup_pass.cpp @@ -0,0 +1,39 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements a pass to cleanup the CFG to remove superfluous +// constructs (e.g., unreachable basic blocks, empty control flow structures, +// etc) + +#include +#include + +#include "source/opt/cfg_cleanup_pass.h" + +#include "source/opt/function.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +Pass::Status CFGCleanupPass::Process() { + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { return CFGCleanup(fp); }; + bool modified = context()->ProcessReachableCallTree(pfn); + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/cfg_cleanup_pass.h b/third_party/spirv-tools/source/opt/cfg_cleanup_pass.h new file mode 100644 index 0000000..5095428 --- /dev/null +++ b/third_party/spirv-tools/source/opt/cfg_cleanup_pass.h @@ -0,0 +1,41 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CFG_CLEANUP_PASS_H_ +#define SOURCE_OPT_CFG_CLEANUP_PASS_H_ + +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +class CFGCleanupPass : public MemPass { + public: + CFGCleanupPass() = default; + + const char* name() const override { return "cfg-cleanup"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CFG_CLEANUP_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/code_sink.cpp b/third_party/spirv-tools/source/opt/code_sink.cpp new file mode 100644 index 0000000..e49029f --- /dev/null +++ b/third_party/spirv-tools/source/opt/code_sink.cpp @@ -0,0 +1,320 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "code_sink.h" + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +Pass::Status CodeSinkingPass::Process() { + bool modified = false; + for (Function& function : *get_module()) { + cfg()->ForEachBlockInPostOrder(function.entry().get(), + [&modified, this](BasicBlock* bb) { + if (SinkInstructionsInBB(bb)) { + modified = true; + } + }); + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool CodeSinkingPass::SinkInstructionsInBB(BasicBlock* bb) { + bool modified = false; + for (auto inst = bb->rbegin(); inst != bb->rend(); ++inst) { + if (SinkInstruction(&*inst)) { + inst = bb->rbegin(); + modified = true; + } + } + return modified; +} + +bool CodeSinkingPass::SinkInstruction(Instruction* inst) { + if (inst->opcode() != SpvOpLoad && inst->opcode() != SpvOpAccessChain) { + return false; + } + + if (ReferencesMutableMemory(inst)) { + return false; + } + + if (BasicBlock* target_bb = FindNewBasicBlockFor(inst)) { + Instruction* pos = &*target_bb->begin(); + while (pos->opcode() == SpvOpPhi) { + pos = pos->NextNode(); + } + + inst->InsertBefore(pos); + context()->set_instr_block(inst, target_bb); + return true; + } + return false; +} + +BasicBlock* CodeSinkingPass::FindNewBasicBlockFor(Instruction* inst) { + assert(inst->result_id() != 0 && "Instruction should have a result."); + BasicBlock* original_bb = context()->get_instr_block(inst); + BasicBlock* bb = original_bb; + + std::unordered_set bbs_with_uses; + get_def_use_mgr()->ForEachUse( + inst, [&bbs_with_uses, this](Instruction* use, uint32_t idx) { + if (use->opcode() != SpvOpPhi) { + BasicBlock* use_bb = context()->get_instr_block(use); + if (use_bb) { + bbs_with_uses.insert(use_bb->id()); + } + } else { + bbs_with_uses.insert(use->GetSingleWordOperand(idx + 1)); + } + }); + + while (true) { + // If |inst| is used in |bb|, then |inst| cannot be moved any further. + if (bbs_with_uses.count(bb->id())) { + break; + } + + // If |bb| has one successor (succ_bb), and |bb| is the only predecessor + // of succ_bb, then |inst| can be moved to succ_bb. If succ_bb, has move + // then one predecessor, then moving |inst| into succ_bb could cause it to + // be executed more often, so the search has to stop. + if (bb->terminator()->opcode() == SpvOpBranch) { + uint32_t succ_bb_id = bb->terminator()->GetSingleWordInOperand(0); + if (cfg()->preds(succ_bb_id).size() == 1) { + bb = context()->get_instr_block(succ_bb_id); + continue; + } else { + break; + } + } + + // The remaining checks need to know the merge node. If there is no merge + // instruction or an OpLoopMerge, then it is a break or continue. We could + // figure it out, but not worth doing it now. + Instruction* merge_inst = bb->GetMergeInst(); + if (merge_inst == nullptr || merge_inst->opcode() != SpvOpSelectionMerge) { + break; + } + + // Check all of the successors of |bb| it see which lead to a use of |inst| + // before reaching the merge node. + bool used_in_multiple_blocks = false; + uint32_t bb_used_in = 0; + bb->ForEachSuccessorLabel([this, bb, &bb_used_in, &used_in_multiple_blocks, + &bbs_with_uses](uint32_t* succ_bb_id) { + if (IntersectsPath(*succ_bb_id, bb->MergeBlockIdIfAny(), bbs_with_uses)) { + if (bb_used_in == 0) { + bb_used_in = *succ_bb_id; + } else { + used_in_multiple_blocks = true; + } + } + }); + + // If more than one successor, which is not the merge block, uses |inst| + // then we have to leave |inst| in bb because there is none of the + // successors dominate all uses of |inst|. + if (used_in_multiple_blocks) { + break; + } + + if (bb_used_in == 0) { + // If |inst| is not used before reaching the merge node, then we can move + // |inst| to the merge node. + bb = context()->get_instr_block(bb->MergeBlockIdIfAny()); + } else { + // If the only successor that leads to a used of |inst| has more than 1 + // predecessor, then moving |inst| could cause it to be executed more + // often, so we cannot move it. + if (cfg()->preds(bb_used_in).size() != 1) { + break; + } + + // If |inst| is used after the merge block, then |bb_used_in| does not + // dominate all of the uses. So we cannot move |inst| any further. + if (IntersectsPath(bb->MergeBlockIdIfAny(), original_bb->id(), + bbs_with_uses)) { + break; + } + + // Otherwise, |bb_used_in| dominates all uses, so move |inst| into that + // block. + bb = context()->get_instr_block(bb_used_in); + } + continue; + } + return (bb != original_bb ? bb : nullptr); +} + +bool CodeSinkingPass::ReferencesMutableMemory(Instruction* inst) { + if (!inst->IsLoad()) { + return false; + } + + Instruction* base_ptr = inst->GetBaseAddress(); + if (base_ptr->opcode() != SpvOpVariable) { + return true; + } + + if (base_ptr->IsReadOnlyPointer()) { + return false; + } + + if (HasUniformMemorySync()) { + return true; + } + + if (base_ptr->GetSingleWordInOperand(0) != SpvStorageClassUniform) { + return true; + } + + return HasPossibleStore(base_ptr); +} + +bool CodeSinkingPass::HasUniformMemorySync() { + if (checked_for_uniform_sync_) { + return has_uniform_sync_; + } + + bool has_sync = false; + get_module()->ForEachInst([this, &has_sync](Instruction* inst) { + switch (inst->opcode()) { + case SpvOpMemoryBarrier: { + uint32_t mem_semantics_id = inst->GetSingleWordInOperand(1); + if (IsSyncOnUniform(mem_semantics_id)) { + has_sync = true; + } + break; + } + case SpvOpControlBarrier: + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicFAddEXT: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFlagTestAndSet: + case SpvOpAtomicFlagClear: { + uint32_t mem_semantics_id = inst->GetSingleWordInOperand(2); + if (IsSyncOnUniform(mem_semantics_id)) { + has_sync = true; + } + break; + } + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + if (IsSyncOnUniform(inst->GetSingleWordInOperand(2)) || + IsSyncOnUniform(inst->GetSingleWordInOperand(3))) { + has_sync = true; + } + break; + default: + break; + } + }); + has_uniform_sync_ = has_sync; + return has_sync; +} + +bool CodeSinkingPass::IsSyncOnUniform(uint32_t mem_semantics_id) const { + const analysis::Constant* mem_semantics_const = + context()->get_constant_mgr()->FindDeclaredConstant(mem_semantics_id); + assert(mem_semantics_const != nullptr && + "Expecting memory semantics id to be a constant."); + assert(mem_semantics_const->AsIntConstant() && + "Memory semantics should be an integer."); + uint32_t mem_semantics_int = mem_semantics_const->GetU32(); + + // If it does not affect uniform memory, then it is does not apply to uniform + // memory. + if ((mem_semantics_int & SpvMemorySemanticsUniformMemoryMask) == 0) { + return false; + } + + // Check if there is an acquire or release. If so not, this it does not add + // any memory constraints. + return (mem_semantics_int & (SpvMemorySemanticsAcquireMask | + SpvMemorySemanticsAcquireReleaseMask | + SpvMemorySemanticsReleaseMask)) != 0; +} + +bool CodeSinkingPass::HasPossibleStore(Instruction* var_inst) { + assert(var_inst->opcode() == SpvOpVariable || + var_inst->opcode() == SpvOpAccessChain || + var_inst->opcode() == SpvOpPtrAccessChain); + + return get_def_use_mgr()->WhileEachUser(var_inst, [this](Instruction* use) { + switch (use->opcode()) { + case SpvOpStore: + return true; + case SpvOpAccessChain: + case SpvOpPtrAccessChain: + return HasPossibleStore(use); + default: + return false; + } + }); +} + +bool CodeSinkingPass::IntersectsPath(uint32_t start, uint32_t end, + const std::unordered_set& set) { + std::vector worklist; + worklist.push_back(start); + std::unordered_set already_done; + already_done.insert(start); + + while (!worklist.empty()) { + BasicBlock* bb = context()->get_instr_block(worklist.back()); + worklist.pop_back(); + + if (bb->id() == end) { + continue; + } + + if (set.count(bb->id())) { + return true; + } + + bb->ForEachSuccessorLabel([&already_done, &worklist](uint32_t* succ_bb_id) { + if (already_done.insert(*succ_bb_id).second) { + worklist.push_back(*succ_bb_id); + } + }); + } + return false; +} + +// namespace opt + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/code_sink.h b/third_party/spirv-tools/source/opt/code_sink.h new file mode 100644 index 0000000..d24df03 --- /dev/null +++ b/third_party/spirv-tools/source/opt/code_sink.h @@ -0,0 +1,107 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CODE_SINK_H_ +#define SOURCE_OPT_CODE_SINK_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass does code sinking for OpAccessChain and OpLoad on variables in +// uniform storage or in read only memory. Code sinking is a transformation +// where an instruction is moved into a more deeply nested construct. +// +// The goal is to move these instructions as close as possible to their uses +// without having to execute them more often or to replicate the instruction. +// Moving the instruction in this way can lead to shorter live ranges, which can +// lead to less register pressure. It can also cause instructions to be +// executed less often because they could be moved into one path of a selection +// construct. +// +// This optimization can cause register pressure to rise if the operands of the +// instructions go dead after the instructions being moved. That is why we only +// move certain OpLoad and OpAccessChain instructions. They generally have +// constants, loop induction variables, and global pointers as operands. The +// operands are live for a longer time in most cases. +class CodeSinkingPass : public Pass { + public: + const char* name() const override { return "code-sink"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Sinks the instructions in |bb| as much as possible. Returns true if + // something changes. + bool SinkInstructionsInBB(BasicBlock* bb); + + // Tries the sink |inst| as much as possible. Returns true if the instruction + // is moved. + bool SinkInstruction(Instruction* inst); + + // Returns the basic block in which to move |inst| to move is as close as + // possible to the uses of |inst| without increasing the number of times + // |inst| will be executed. Return |nullptr| if there is no need to move + // |inst|. + BasicBlock* FindNewBasicBlockFor(Instruction* inst); + + // Return true if |inst| reference memory and it is possible that the data in + // the memory changes at some point. + bool ReferencesMutableMemory(Instruction* inst); + + // Returns true if the module contains an instruction that has a memory + // semantics id as an operand, and the memory semantics enforces a + // synchronization of uniform memory. See section 3.25 of the SPIR-V + // specification. + bool HasUniformMemorySync(); + + // Returns true if there may be a store to the variable |var_inst|. + bool HasPossibleStore(Instruction* var_inst); + + // Returns true if one of the basic blocks in |set| exists on a path from the + // basic block |start| to |end|. + bool IntersectsPath(uint32_t start, uint32_t end, + const std::unordered_set& set); + + // Returns true if |mem_semantics_id| is the id of a constant that, when + // interpreted as a memory semantics mask enforces synchronization of uniform + // memory. See section 3.25 of the SPIR-V specification. + bool IsSyncOnUniform(uint32_t mem_semantics_id) const; + + // True if a check has for uniform storage has taken place. + bool checked_for_uniform_sync_; + + // Cache of whether or not the module has a memory sync on uniform storage. + // only valid if |check_for_uniform_sync_| is true. + bool has_uniform_sync_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CODE_SINK_H_ diff --git a/third_party/spirv-tools/source/opt/combine_access_chains.cpp b/third_party/spirv-tools/source/opt/combine_access_chains.cpp new file mode 100644 index 0000000..facfc24 --- /dev/null +++ b/third_party/spirv-tools/source/opt/combine_access_chains.cpp @@ -0,0 +1,290 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/combine_access_chains.h" + +#include + +#include "source/opt/constants.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status CombineAccessChains::Process() { + bool modified = false; + + for (auto& function : *get_module()) { + modified |= ProcessFunction(function); + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool CombineAccessChains::ProcessFunction(Function& function) { + bool modified = false; + + cfg()->ForEachBlockInReversePostOrder( + function.entry().get(), [&modified, this](BasicBlock* block) { + block->ForEachInst([&modified, this](Instruction* inst) { + switch (inst->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + modified |= CombineAccessChain(inst); + break; + default: + break; + } + }); + }); + + return modified; +} + +uint32_t CombineAccessChains::GetConstantValue( + const analysis::Constant* constant_inst) { + if (constant_inst->type()->AsInteger()->width() <= 32) { + if (constant_inst->type()->AsInteger()->IsSigned()) { + return static_cast(constant_inst->GetS32()); + } else { + return constant_inst->GetU32(); + } + } else { + assert(false); + return 0u; + } +} + +uint32_t CombineAccessChains::GetArrayStride(const Instruction* inst) { + uint32_t array_stride = 0; + context()->get_decoration_mgr()->WhileEachDecoration( + inst->type_id(), SpvDecorationArrayStride, + [&array_stride](const Instruction& decoration) { + assert(decoration.opcode() != SpvOpDecorateId); + if (decoration.opcode() == SpvOpDecorate) { + array_stride = decoration.GetSingleWordInOperand(1); + } else { + array_stride = decoration.GetSingleWordInOperand(2); + } + return false; + }); + return array_stride; +} + +const analysis::Type* CombineAccessChains::GetIndexedType(Instruction* inst) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + + Instruction* base_ptr = def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + const analysis::Type* type = type_mgr->GetType(base_ptr->type_id()); + assert(type->AsPointer()); + type = type->AsPointer()->pointee_type(); + std::vector element_indices; + uint32_t starting_index = 1; + if (IsPtrAccessChain(inst->opcode())) { + // Skip the first index of OpPtrAccessChain as it does not affect type + // resolution. + starting_index = 2; + } + for (uint32_t i = starting_index; i < inst->NumInOperands(); ++i) { + Instruction* index_inst = + def_use_mgr->GetDef(inst->GetSingleWordInOperand(i)); + const analysis::Constant* index_constant = + context()->get_constant_mgr()->GetConstantFromInst(index_inst); + if (index_constant) { + uint32_t index_value = GetConstantValue(index_constant); + element_indices.push_back(index_value); + } else { + // This index must not matter to resolve the type in valid SPIR-V. + element_indices.push_back(0); + } + } + type = type_mgr->GetMemberType(type, element_indices); + return type; +} + +bool CombineAccessChains::CombineIndices(Instruction* ptr_input, + Instruction* inst, + std::vector* new_operands) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::ConstantManager* constant_mgr = context()->get_constant_mgr(); + + Instruction* last_index_inst = def_use_mgr->GetDef( + ptr_input->GetSingleWordInOperand(ptr_input->NumInOperands() - 1)); + const analysis::Constant* last_index_constant = + constant_mgr->GetConstantFromInst(last_index_inst); + + Instruction* element_inst = + def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + const analysis::Constant* element_constant = + constant_mgr->GetConstantFromInst(element_inst); + + // Combine the last index of the AccessChain (|ptr_inst|) with the element + // operand of the PtrAccessChain (|inst|). + const bool combining_element_operands = + IsPtrAccessChain(inst->opcode()) && + IsPtrAccessChain(ptr_input->opcode()) && ptr_input->NumInOperands() == 2; + uint32_t new_value_id = 0; + const analysis::Type* type = GetIndexedType(ptr_input); + if (last_index_constant && element_constant) { + // Combine the constants. + uint32_t new_value = GetConstantValue(last_index_constant) + + GetConstantValue(element_constant); + const analysis::Constant* new_value_constant = + constant_mgr->GetConstant(last_index_constant->type(), {new_value}); + Instruction* new_value_inst = + constant_mgr->GetDefiningInstruction(new_value_constant); + new_value_id = new_value_inst->result_id(); + } else if (!type->AsStruct() || combining_element_operands) { + // Generate an addition of the two indices. + InstructionBuilder builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* addition = builder.AddIAdd(last_index_inst->type_id(), + last_index_inst->result_id(), + element_inst->result_id()); + new_value_id = addition->result_id(); + } else { + // Indexing into structs must be constant, so bail out here. + return false; + } + new_operands->push_back({SPV_OPERAND_TYPE_ID, {new_value_id}}); + return true; +} + +bool CombineAccessChains::CreateNewInputOperands( + Instruction* ptr_input, Instruction* inst, + std::vector* new_operands) { + // Start by copying all the input operands of the feeder access chain. + for (uint32_t i = 0; i != ptr_input->NumInOperands() - 1; ++i) { + new_operands->push_back(ptr_input->GetInOperand(i)); + } + + // Deal with the last index of the feeder access chain. + if (IsPtrAccessChain(inst->opcode())) { + // The last index of the feeder should be combined with the element operand + // of |inst|. + if (!CombineIndices(ptr_input, inst, new_operands)) return false; + } else { + // The indices aren't being combined so now add the last index operand of + // |ptr_input|. + new_operands->push_back( + ptr_input->GetInOperand(ptr_input->NumInOperands() - 1)); + } + + // Copy the remaining index operands. + uint32_t starting_index = IsPtrAccessChain(inst->opcode()) ? 2 : 1; + for (uint32_t i = starting_index; i < inst->NumInOperands(); ++i) { + new_operands->push_back(inst->GetInOperand(i)); + } + + return true; +} + +bool CombineAccessChains::CombineAccessChain(Instruction* inst) { + assert((inst->opcode() == SpvOpPtrAccessChain || + inst->opcode() == SpvOpAccessChain || + inst->opcode() == SpvOpInBoundsAccessChain || + inst->opcode() == SpvOpInBoundsPtrAccessChain) && + "Wrong opcode. Expected an access chain."); + + Instruction* ptr_input = + context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0)); + if (ptr_input->opcode() != SpvOpAccessChain && + ptr_input->opcode() != SpvOpInBoundsAccessChain && + ptr_input->opcode() != SpvOpPtrAccessChain && + ptr_input->opcode() != SpvOpInBoundsPtrAccessChain) { + return false; + } + + if (Has64BitIndices(inst) || Has64BitIndices(ptr_input)) return false; + + // Handles the following cases: + // 1. |ptr_input| is an index-less access chain. Replace the pointer + // in |inst| with |ptr_input|'s pointer. + // 2. |inst| is a index-less access chain. Change |inst| to an + // OpCopyObject. + // 3. |inst| is not a pointer access chain. + // |inst|'s indices are appended to |ptr_input|'s indices. + // 4. |ptr_input| is not pointer access chain. + // |inst| is a pointer access chain. + // |inst|'s element operand is combined with the last index in + // |ptr_input| to form a new operand. + // 5. |ptr_input| is a pointer access chain. + // Like the above scenario, |inst|'s element operand is combined + // with |ptr_input|'s last index. This results is either a + // combined element operand or combined regular index. + + // TODO(alan-baker): Support this properly. Requires analyzing the + // size/alignment of the type and converting the stride into an element + // index. + uint32_t array_stride = GetArrayStride(ptr_input); + if (array_stride != 0) return false; + + if (ptr_input->NumInOperands() == 1) { + // The input is effectively a no-op. + inst->SetInOperand(0, {ptr_input->GetSingleWordInOperand(0)}); + context()->AnalyzeUses(inst); + } else if (inst->NumInOperands() == 1) { + // |inst| is a no-op, change it to a copy. Instruction simplification will + // clean it up. + inst->SetOpcode(SpvOpCopyObject); + } else { + std::vector new_operands; + if (!CreateNewInputOperands(ptr_input, inst, &new_operands)) return false; + + // Update the instruction. + inst->SetOpcode(UpdateOpcode(inst->opcode(), ptr_input->opcode())); + inst->SetInOperands(std::move(new_operands)); + context()->AnalyzeUses(inst); + } + return true; +} + +SpvOp CombineAccessChains::UpdateOpcode(SpvOp base_opcode, SpvOp input_opcode) { + auto IsInBounds = [](SpvOp opcode) { + return opcode == SpvOpInBoundsPtrAccessChain || + opcode == SpvOpInBoundsAccessChain; + }; + + if (input_opcode == SpvOpInBoundsPtrAccessChain) { + if (!IsInBounds(base_opcode)) return SpvOpPtrAccessChain; + } else if (input_opcode == SpvOpInBoundsAccessChain) { + if (!IsInBounds(base_opcode)) return SpvOpAccessChain; + } + + return input_opcode; +} + +bool CombineAccessChains::IsPtrAccessChain(SpvOp opcode) { + return opcode == SpvOpPtrAccessChain || opcode == SpvOpInBoundsPtrAccessChain; +} + +bool CombineAccessChains::Has64BitIndices(Instruction* inst) { + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + Instruction* index_inst = + context()->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(i)); + const analysis::Type* index_type = + context()->get_type_mgr()->GetType(index_inst->type_id()); + if (!index_type->AsInteger() || index_type->AsInteger()->width() != 32) + return true; + } + return false; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/combine_access_chains.h b/third_party/spirv-tools/source/opt/combine_access_chains.h new file mode 100644 index 0000000..531209e --- /dev/null +++ b/third_party/spirv-tools/source/opt/combine_access_chains.h @@ -0,0 +1,83 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_COMBINE_ACCESS_CHAINS_H_ +#define SOURCE_OPT_COMBINE_ACCESS_CHAINS_H_ + +#include + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class CombineAccessChains : public Pass { + public: + const char* name() const override { return "combine-access-chains"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Combine access chains in |function|. Blocks are processed in reverse + // post-order. Returns true if the function is modified. + bool ProcessFunction(Function& function); + + // Combines an access chain (normal, in bounds or pointer) |inst| if its base + // pointer is another access chain. Returns true if the access chain was + // modified. + bool CombineAccessChain(Instruction* inst); + + // Returns the value of |constant_inst| as a uint32_t. + uint32_t GetConstantValue(const analysis::Constant* constant_inst); + + // Returns the array stride of |inst|'s type. + uint32_t GetArrayStride(const Instruction* inst); + + // Returns the type by resolving the index operands |inst|. |inst| must be an + // access chain instruction. + const analysis::Type* GetIndexedType(Instruction* inst); + + // Populates |new_operands| with the operands for the combined access chain. + // Returns false if the access chains cannot be combined. + bool CreateNewInputOperands(Instruction* ptr_input, Instruction* inst, + std::vector* new_operands); + + // Combines the last index of |ptr_input| with the element operand of |inst|. + // Adds the combined operand to |new_operands|. + bool CombineIndices(Instruction* ptr_input, Instruction* inst, + std::vector* new_operands); + + // Returns the opcode to use for the combined access chain. + SpvOp UpdateOpcode(SpvOp base_opcode, SpvOp input_opcode); + + // Returns true if |opcode| is a pointer access chain. + bool IsPtrAccessChain(SpvOp opcode); + + // Returns true if |inst| (an access chain) has 64-bit indices. + bool Has64BitIndices(Instruction* inst); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_COMBINE_ACCESS_CHAINS_H_ diff --git a/third_party/spirv-tools/source/opt/compact_ids_pass.cpp b/third_party/spirv-tools/source/opt/compact_ids_pass.cpp new file mode 100644 index 0000000..6709153 --- /dev/null +++ b/third_party/spirv-tools/source/opt/compact_ids_pass.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/compact_ids_pass.h" + +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +// Returns the remapped id of |id| from |result_id_mapping|. If the remapped +// id does not exist, adds a new one to |result_id_mapping| and returns it. +uint32_t GetRemappedId( + std::unordered_map* result_id_mapping, uint32_t id) { + auto it = result_id_mapping->find(id); + if (it == result_id_mapping->end()) { + const uint32_t new_id = + static_cast(result_id_mapping->size()) + 1; + const auto insertion_result = result_id_mapping->emplace(id, new_id); + it = insertion_result.first; + assert(insertion_result.second); + } + return it->second; +} + +} // namespace + +Pass::Status CompactIdsPass::Process() { + bool modified = false; + std::unordered_map result_id_mapping; + + context()->module()->ForEachInst( + [&result_id_mapping, &modified](Instruction* inst) { + auto operand = inst->begin(); + while (operand != inst->end()) { + const auto type = operand->type; + if (spvIsIdType(type)) { + assert(operand->words.size() == 1); + uint32_t& id = operand->words[0]; + uint32_t new_id = GetRemappedId(&result_id_mapping, id); + if (id != new_id) { + modified = true; + id = new_id; + // Update data cached in the instruction object. + if (type == SPV_OPERAND_TYPE_RESULT_ID) { + inst->SetResultId(id); + } else if (type == SPV_OPERAND_TYPE_TYPE_ID) { + inst->SetResultType(id); + } + } + } + ++operand; + } + + uint32_t scope_id = inst->GetDebugScope().GetLexicalScope(); + if (scope_id != kNoDebugScope) { + uint32_t new_id = GetRemappedId(&result_id_mapping, scope_id); + if (scope_id != new_id) { + inst->UpdateLexicalScope(new_id); + modified = true; + } + } + uint32_t inlinedat_id = inst->GetDebugInlinedAt(); + if (inlinedat_id != kNoInlinedAt) { + uint32_t new_id = GetRemappedId(&result_id_mapping, inlinedat_id); + if (inlinedat_id != new_id) { + inst->UpdateDebugInlinedAt(new_id); + modified = true; + } + } + }, + true); + + if (modified) + context()->module()->SetIdBound( + static_cast(result_id_mapping.size() + 1)); + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/compact_ids_pass.h b/third_party/spirv-tools/source/opt/compact_ids_pass.h new file mode 100644 index 0000000..d97ae0f --- /dev/null +++ b/third_party/spirv-tools/source/opt/compact_ids_pass.h @@ -0,0 +1,42 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_COMPACT_IDS_PASS_H_ +#define SOURCE_OPT_COMPACT_IDS_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class CompactIdsPass : public Pass { + public: + const char* name() const override { return "compact-ids"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_COMPACT_IDS_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/composite.cpp b/third_party/spirv-tools/source/opt/composite.cpp new file mode 100644 index 0000000..2b4dca2 --- /dev/null +++ b/third_party/spirv-tools/source/opt/composite.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/composite.h" + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" +#include "spirv/1.2/GLSL.std.450.h" + +namespace spvtools { +namespace opt { + +bool ExtInsMatch(const std::vector& extIndices, + const Instruction* insInst, const uint32_t extOffset) { + uint32_t numIndices = static_cast(extIndices.size()) - extOffset; + if (numIndices != insInst->NumInOperands() - 2) return false; + for (uint32_t i = 0; i < numIndices; ++i) + if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2)) + return false; + return true; +} + +bool ExtInsConflict(const std::vector& extIndices, + const Instruction* insInst, const uint32_t extOffset) { + if (extIndices.size() - extOffset == insInst->NumInOperands() - 2) + return false; + uint32_t extNumIndices = static_cast(extIndices.size()) - extOffset; + uint32_t insNumIndices = insInst->NumInOperands() - 2; + uint32_t numIndices = std::min(extNumIndices, insNumIndices); + for (uint32_t i = 0; i < numIndices; ++i) + if (extIndices[i + extOffset] != insInst->GetSingleWordInOperand(i + 2)) + return false; + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/composite.h b/third_party/spirv-tools/source/opt/composite.h new file mode 100644 index 0000000..3cc036e --- /dev/null +++ b/third_party/spirv-tools/source/opt/composite.h @@ -0,0 +1,51 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_COMPOSITE_H_ +#define SOURCE_OPT_COMPOSITE_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// Return true if the extract indices in |extIndices| starting at |extOffset| +// match indices of insert |insInst|. +bool ExtInsMatch(const std::vector& extIndices, + const Instruction* insInst, const uint32_t extOffset); + +// Return true if indices in |extIndices| starting at |extOffset| and +// indices of insert |insInst| conflict, specifically, if the insert +// changes bits specified by the extract, but changes either more bits +// or less bits than the extract specifies, meaning the exact value being +// inserted cannot be used to replace the extract. +bool ExtInsConflict(const std::vector& extIndices, + const Instruction* insInst, const uint32_t extOffset); + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_COMPOSITE_H_ diff --git a/third_party/spirv-tools/source/opt/const_folding_rules.cpp b/third_party/spirv-tools/source/opt/const_folding_rules.cpp new file mode 100644 index 0000000..d262a7e --- /dev/null +++ b/third_party/spirv-tools/source/opt/const_folding_rules.cpp @@ -0,0 +1,1277 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/const_folding_rules.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kExtractCompositeIdInIdx = 0; + +// Returns true if |type| is Float or a vector of Float. +bool HasFloatingPoint(const analysis::Type* type) { + if (type->AsFloat()) { + return true; + } else if (const analysis::Vector* vec_type = type->AsVector()) { + return vec_type->element_type()->AsFloat() != nullptr; + } + + return false; +} + +// Folds an OpcompositeExtract where input is a composite constant. +ConstantFoldingRule FoldExtractWithConstants() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + const analysis::Constant* c = constants[kExtractCompositeIdInIdx]; + if (c == nullptr) { + return nullptr; + } + + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + uint32_t element_index = inst->GetSingleWordInOperand(i); + if (c->AsNullConstant()) { + // Return Null for the return type. + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + return const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), {}); + } + + auto cc = c->AsCompositeConstant(); + assert(cc != nullptr); + auto components = cc->GetComponents(); + // Protect against invalid IR. Refuse to fold if the index is out + // of bounds. + if (element_index >= components.size()) return nullptr; + c = components[element_index]; + } + return c; + }; +} + +ConstantFoldingRule FoldVectorShuffleWithConstants() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == SpvOpVectorShuffle); + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* element_type = c1->type()->AsVector()->element_type(); + + std::vector c1_components; + if (const analysis::VectorConstant* vec_const = c1->AsVectorConstant()) { + c1_components = vec_const->GetComponents(); + } else { + assert(c1->AsNullConstant()); + const analysis::Constant* element = + const_mgr->GetConstant(element_type, {}); + c1_components.resize(c1->type()->AsVector()->element_count(), element); + } + std::vector c2_components; + if (const analysis::VectorConstant* vec_const = c2->AsVectorConstant()) { + c2_components = vec_const->GetComponents(); + } else { + assert(c2->AsNullConstant()); + const analysis::Constant* element = + const_mgr->GetConstant(element_type, {}); + c2_components.resize(c2->type()->AsVector()->element_count(), element); + } + + std::vector ids; + const uint32_t undef_literal_value = 0xffffffff; + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + uint32_t index = inst->GetSingleWordInOperand(i); + if (index == undef_literal_value) { + // Don't fold shuffle with undef literal value. + return nullptr; + } else if (index < c1_components.size()) { + Instruction* member_inst = + const_mgr->GetDefiningInstruction(c1_components[index]); + ids.push_back(member_inst->result_id()); + } else { + Instruction* member_inst = const_mgr->GetDefiningInstruction( + c2_components[index - c1_components.size()]); + ids.push_back(member_inst->result_id()); + } + } + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + return const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), ids); + }; +} + +ConstantFoldingRule FoldVectorTimesScalar() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + assert(inst->opcode() == SpvOpVectorTimesScalar); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + if (HasFloatingPoint(type_mgr->GetType(inst->type_id()))) { + return nullptr; + } + } + + const analysis::Constant* c1 = constants[0]; + const analysis::Constant* c2 = constants[1]; + + if (c1 && c1->IsZero()) { + return c1; + } + + if (c2 && c2->IsZero()) { + // Get or create the NullConstant for this type. + std::vector ids; + return const_mgr->GetConstant(type_mgr->GetType(inst->type_id()), ids); + } + + if (c1 == nullptr || c2 == nullptr) { + return nullptr; + } + + // Check result type. + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + assert(vector_type != nullptr); + const analysis::Type* element_type = vector_type->element_type(); + assert(element_type != nullptr); + const analysis::Float* float_type = element_type->AsFloat(); + assert(float_type != nullptr); + + // Check types of c1 and c2. + assert(c1->type()->AsVector() == vector_type); + assert(c1->type()->AsVector()->element_type() == element_type && + c2->type() == element_type); + + // Get a float vector that is the result of vector-times-scalar. + std::vector c1_components = + c1->GetVectorComponents(const_mgr); + std::vector ids; + if (float_type->width() == 32) { + float scalar = c2->GetFloat(); + for (uint32_t i = 0; i < c1_components.size(); ++i) { + utils::FloatProxy result(c1_components[i]->GetFloat() * scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else if (float_type->width() == 64) { + double scalar = c2->GetDouble(); + for (uint32_t i = 0; i < c1_components.size(); ++i) { + utils::FloatProxy result(c1_components[i]->GetDouble() * + scalar); + std::vector words = result.GetWords(); + const analysis::Constant* new_elem = + const_mgr->GetConstant(float_type, words); + ids.push_back(const_mgr->GetDefiningInstruction(new_elem)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } + return nullptr; + }; +} + +ConstantFoldingRule FoldCompositeWithConstants() { + // Folds an OpCompositeConstruct where all of the inputs are constants to a + // constant. A new constant is created if necessary. + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* new_type = type_mgr->GetType(inst->type_id()); + Instruction* type_inst = + context->get_def_use_mgr()->GetDef(inst->type_id()); + + std::vector ids; + for (uint32_t i = 0; i < constants.size(); ++i) { + const analysis::Constant* element_const = constants[i]; + if (element_const == nullptr) { + return nullptr; + } + + uint32_t component_type_id = 0; + if (type_inst->opcode() == SpvOpTypeStruct) { + component_type_id = type_inst->GetSingleWordInOperand(i); + } else if (type_inst->opcode() == SpvOpTypeArray) { + component_type_id = type_inst->GetSingleWordInOperand(0); + } + + uint32_t element_id = + const_mgr->FindDeclaredConstant(element_const, component_type_id); + if (element_id == 0) { + return nullptr; + } + ids.push_back(element_id); + } + return const_mgr->GetConstant(new_type, ids); + }; +} + +// The interface for a function that returns the result of applying a scalar +// floating-point binary operation on |a| and |b|. The type of the return value +// will be |type|. The input constants must also be of type |type|. +using UnaryScalarFoldingRule = std::function; + +// The interface for a function that returns the result of applying a scalar +// floating-point binary operation on |a| and |b|. The type of the return value +// will be |type|. The input constants must also be of type |type|. +using BinaryScalarFoldingRule = std::function; + +// Returns a |ConstantFoldingRule| that folds unary floating point scalar ops +// using |scalar_rule| and unary float point vectors ops by applying +// |scalar_rule| to the elements of the vector. The |ConstantFoldingRule| +// that is returned assumes that |constants| contains 1 entry. If they are +// not |nullptr|, then their type is either |Float| or |Integer| or a |Vector| +// whose element type is |Float| or |Integer|. +ConstantFoldingRule FoldFPUnaryOp(UnaryScalarFoldingRule scalar_rule) { + return [scalar_rule](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* result_type = type_mgr->GetType(inst->type_id()); + const analysis::Vector* vector_type = result_type->AsVector(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + const analysis::Constant* arg = + (inst->opcode() == SpvOpExtInst) ? constants[1] : constants[0]; + + if (arg == nullptr) { + return nullptr; + } + + if (vector_type != nullptr) { + std::vector a_components; + std::vector results_components; + + a_components = arg->GetVectorComponents(const_mgr); + + // Fold each component of the vector. + for (uint32_t i = 0; i < a_components.size(); ++i) { + results_components.push_back(scalar_rule(vector_type->element_type(), + a_components[i], const_mgr)); + if (results_components[i] == nullptr) { + return nullptr; + } + } + + // Build the constant object and return it. + std::vector ids; + for (const analysis::Constant* member : results_components) { + ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else { + return scalar_rule(result_type, arg, const_mgr); + } + }; +} + +// Returns the result of folding the constants in |constants| according the +// |scalar_rule|. If |result_type| is a vector, then |scalar_rule| is applied +// per component. +const analysis::Constant* FoldFPBinaryOp( + BinaryScalarFoldingRule scalar_rule, uint32_t result_type_id, + const std::vector& constants, + IRContext* context) { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* result_type = type_mgr->GetType(result_type_id); + const analysis::Vector* vector_type = result_type->AsVector(); + + if (constants[0] == nullptr || constants[1] == nullptr) { + return nullptr; + } + + if (vector_type != nullptr) { + std::vector a_components; + std::vector b_components; + std::vector results_components; + + a_components = constants[0]->GetVectorComponents(const_mgr); + b_components = constants[1]->GetVectorComponents(const_mgr); + + // Fold each component of the vector. + for (uint32_t i = 0; i < a_components.size(); ++i) { + results_components.push_back(scalar_rule(vector_type->element_type(), + a_components[i], b_components[i], + const_mgr)); + if (results_components[i] == nullptr) { + return nullptr; + } + } + + // Build the constant object and return it. + std::vector ids; + for (const analysis::Constant* member : results_components) { + ids.push_back(const_mgr->GetDefiningInstruction(member)->result_id()); + } + return const_mgr->GetConstant(vector_type, ids); + } else { + return scalar_rule(result_type, constants[0], constants[1], const_mgr); + } +} + +// Returns a |ConstantFoldingRule| that folds floating point scalars using +// |scalar_rule| and vectors of floating point by applying |scalar_rule| to the +// elements of the vector. The |ConstantFoldingRule| that is returned assumes +// that |constants| contains 2 entries. If they are not |nullptr|, then their +// type is either |Float| or a |Vector| whose element type is |Float|. +ConstantFoldingRule FoldFPBinaryOp(BinaryScalarFoldingRule scalar_rule) { + return [scalar_rule](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + if (inst->opcode() == SpvOpExtInst) { + return FoldFPBinaryOp(scalar_rule, inst->type_id(), + {constants[1], constants[2]}, context); + } + return FoldFPBinaryOp(scalar_rule, inst->type_id(), constants, context); + }; +} + +// This macro defines a |UnaryScalarFoldingRule| that performs float to +// integer conversion. +// TODO(greg-lunarg): Support for 64-bit integer types. +UnaryScalarFoldingRule FoldFToIOp() { + return [](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Integer* integer_type = result_type->AsInteger(); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + assert(integer_type != nullptr); + if (integer_type->width() != 32) return nullptr; + if (float_type->width() == 32) { + float fa = a->GetFloat(); + uint32_t result = integer_type->IsSigned() + ? static_cast(static_cast(fa)) + : static_cast(fa); + std::vector words = {result}; + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double fa = a->GetDouble(); + uint32_t result = integer_type->IsSigned() + ? static_cast(static_cast(fa)) + : static_cast(fa); + std::vector words = {result}; + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +// This function defines a |UnaryScalarFoldingRule| that performs integer to +// float conversion. +// TODO(greg-lunarg): Support for 64-bit integer types. +UnaryScalarFoldingRule FoldIToFOp() { + return [](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Integer* integer_type = a->type()->AsInteger(); + const analysis::Float* float_type = result_type->AsFloat(); + assert(float_type != nullptr); + assert(integer_type != nullptr); + if (integer_type->width() != 32) return nullptr; + uint32_t ua = a->GetU32(); + if (float_type->width() == 32) { + float result_val = integer_type->IsSigned() + ? static_cast(static_cast(ua)) + : static_cast(ua); + utils::FloatProxy result(result_val); + std::vector words = {result.data()}; + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double result_val = integer_type->IsSigned() + ? static_cast(static_cast(ua)) + : static_cast(ua); + utils::FloatProxy result(result_val); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +// This defines a |UnaryScalarFoldingRule| that performs |OpQuantizeToF16|. +UnaryScalarFoldingRule FoldQuantizeToF16Scalar() { + return [](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + if (float_type->width() != 32) { + return nullptr; + } + + float fa = a->GetFloat(); + utils::HexFloat> orignal(fa); + utils::HexFloat> quantized(0); + utils::HexFloat> result(0.0f); + orignal.castTo(quantized, utils::round_direction::kToZero); + quantized.castTo(result, utils::round_direction::kToZero); + std::vector words = {result.getBits()}; + return const_mgr->GetConstant(result_type, words); + }; +} + +// This macro defines a |BinaryScalarFoldingRule| that applies |op|. The +// operator |op| must work for both float and double, and use syntax "f1 op f2". +#define FOLD_FPARITH_OP(op) \ + [](const analysis::Type* result_type_in_macro, const analysis::Constant* a, \ + const analysis::Constant* b, \ + analysis::ConstantManager* const_mgr_in_macro) \ + -> const analysis::Constant* { \ + assert(result_type_in_macro != nullptr && a != nullptr && b != nullptr); \ + assert(result_type_in_macro == a->type() && \ + result_type_in_macro == b->type()); \ + const analysis::Float* float_type_in_macro = \ + result_type_in_macro->AsFloat(); \ + assert(float_type_in_macro != nullptr); \ + if (float_type_in_macro->width() == 32) { \ + float fa = a->GetFloat(); \ + float fb = b->GetFloat(); \ + utils::FloatProxy result_in_macro(fa op fb); \ + std::vector words_in_macro = result_in_macro.GetWords(); \ + return const_mgr_in_macro->GetConstant(result_type_in_macro, \ + words_in_macro); \ + } else if (float_type_in_macro->width() == 64) { \ + double fa = a->GetDouble(); \ + double fb = b->GetDouble(); \ + utils::FloatProxy result_in_macro(fa op fb); \ + std::vector words_in_macro = result_in_macro.GetWords(); \ + return const_mgr_in_macro->GetConstant(result_type_in_macro, \ + words_in_macro); \ + } \ + return nullptr; \ + } + +// Define the folding rule for conversion between floating point and integer +ConstantFoldingRule FoldFToI() { return FoldFPUnaryOp(FoldFToIOp()); } +ConstantFoldingRule FoldIToF() { return FoldFPUnaryOp(FoldIToFOp()); } +ConstantFoldingRule FoldQuantizeToF16() { + return FoldFPUnaryOp(FoldQuantizeToF16Scalar()); +} + +// Define the folding rules for subtraction, addition, multiplication, and +// division for floating point values. +ConstantFoldingRule FoldFSub() { return FoldFPBinaryOp(FOLD_FPARITH_OP(-)); } +ConstantFoldingRule FoldFAdd() { return FoldFPBinaryOp(FOLD_FPARITH_OP(+)); } +ConstantFoldingRule FoldFMul() { return FoldFPBinaryOp(FOLD_FPARITH_OP(*)); } +ConstantFoldingRule FoldFDiv() { return FoldFPBinaryOp(FOLD_FPARITH_OP(/)); } + +bool CompareFloatingPoint(bool op_result, bool op_unordered, + bool need_ordered) { + if (need_ordered) { + // operands are ordered and Operand 1 is |op| Operand 2 + return !op_unordered && op_result; + } else { + // operands are unordered or Operand 1 is |op| Operand 2 + return op_unordered || op_result; + } +} + +// This macro defines a |BinaryScalarFoldingRule| that applies |op|. The +// operator |op| must work for both float and double, and use syntax "f1 op f2". +#define FOLD_FPCMP_OP(op, ord) \ + [](const analysis::Type* result_type, const analysis::Constant* a, \ + const analysis::Constant* b, \ + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { \ + assert(result_type != nullptr && a != nullptr && b != nullptr); \ + assert(result_type->AsBool()); \ + assert(a->type() == b->type()); \ + const analysis::Float* float_type = a->type()->AsFloat(); \ + assert(float_type != nullptr); \ + if (float_type->width() == 32) { \ + float fa = a->GetFloat(); \ + float fb = b->GetFloat(); \ + bool result = CompareFloatingPoint( \ + fa op fb, std::isnan(fa) || std::isnan(fb), ord); \ + std::vector words = {uint32_t(result)}; \ + return const_mgr->GetConstant(result_type, words); \ + } else if (float_type->width() == 64) { \ + double fa = a->GetDouble(); \ + double fb = b->GetDouble(); \ + bool result = CompareFloatingPoint( \ + fa op fb, std::isnan(fa) || std::isnan(fb), ord); \ + std::vector words = {uint32_t(result)}; \ + return const_mgr->GetConstant(result_type, words); \ + } \ + return nullptr; \ + } + +// Define the folding rules for ordered and unordered comparison for floating +// point values. +ConstantFoldingRule FoldFOrdEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(==, true)); +} +ConstantFoldingRule FoldFUnordEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(==, false)); +} +ConstantFoldingRule FoldFOrdNotEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(!=, true)); +} +ConstantFoldingRule FoldFUnordNotEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(!=, false)); +} +ConstantFoldingRule FoldFOrdLessThan() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(<, true)); +} +ConstantFoldingRule FoldFUnordLessThan() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(<, false)); +} +ConstantFoldingRule FoldFOrdGreaterThan() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(>, true)); +} +ConstantFoldingRule FoldFUnordGreaterThan() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(>, false)); +} +ConstantFoldingRule FoldFOrdLessThanEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(<=, true)); +} +ConstantFoldingRule FoldFUnordLessThanEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(<=, false)); +} +ConstantFoldingRule FoldFOrdGreaterThanEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(>=, true)); +} +ConstantFoldingRule FoldFUnordGreaterThanEqual() { + return FoldFPBinaryOp(FOLD_FPCMP_OP(>=, false)); +} + +// Folds an OpDot where all of the inputs are constants to a +// constant. A new constant is created if necessary. +ConstantFoldingRule FoldOpDotWithConstants() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* new_type = type_mgr->GetType(inst->type_id()); + assert(new_type->AsFloat() && "OpDot should have a float return type."); + const analysis::Float* float_type = new_type->AsFloat(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + // If one of the operands is 0, then the result is 0. + bool has_zero_operand = false; + + for (int i = 0; i < 2; ++i) { + if (constants[i]) { + if (constants[i]->AsNullConstant() || + constants[i]->AsVectorConstant()->IsZero()) { + has_zero_operand = true; + break; + } + } + } + + if (has_zero_operand) { + if (float_type->width() == 32) { + utils::FloatProxy result(0.0f); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(float_type, words); + } + if (float_type->width() == 64) { + utils::FloatProxy result(0.0); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(float_type, words); + } + return nullptr; + } + + if (constants[0] == nullptr || constants[1] == nullptr) { + return nullptr; + } + + std::vector a_components; + std::vector b_components; + + a_components = constants[0]->GetVectorComponents(const_mgr); + b_components = constants[1]->GetVectorComponents(const_mgr); + + utils::FloatProxy result(0.0); + std::vector words = result.GetWords(); + const analysis::Constant* result_const = + const_mgr->GetConstant(float_type, words); + for (uint32_t i = 0; i < a_components.size() && result_const != nullptr; + ++i) { + if (a_components[i] == nullptr || b_components[i] == nullptr) { + return nullptr; + } + + const analysis::Constant* component = FOLD_FPARITH_OP(*)( + new_type, a_components[i], b_components[i], const_mgr); + if (component == nullptr) { + return nullptr; + } + result_const = + FOLD_FPARITH_OP(+)(new_type, result_const, component, const_mgr); + } + return result_const; + }; +} + +// This function defines a |UnaryScalarFoldingRule| that subtracts the constant +// from zero. +UnaryScalarFoldingRule FoldFNegateOp() { + return [](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + assert(result_type == a->type()); + const analysis::Float* float_type = result_type->AsFloat(); + assert(float_type != nullptr); + if (float_type->width() == 32) { + float fa = a->GetFloat(); + utils::FloatProxy result(-fa); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double da = a->GetDouble(); + utils::FloatProxy result(-da); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +ConstantFoldingRule FoldFNegate() { return FoldFPUnaryOp(FoldFNegateOp()); } + +ConstantFoldingRule FoldFClampFeedingCompare(uint32_t cmp_opcode) { + return [cmp_opcode](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + uint32_t non_const_idx = (constants[0] ? 1 : 0); + uint32_t operand_id = inst->GetSingleWordInOperand(non_const_idx); + Instruction* operand_inst = def_use_mgr->GetDef(operand_id); + + analysis::TypeManager* type_mgr = context->get_type_mgr(); + const analysis::Type* operand_type = + type_mgr->GetType(operand_inst->type_id()); + + if (!operand_type->AsFloat()) { + return nullptr; + } + + if (operand_type->AsFloat()->width() != 32 && + operand_type->AsFloat()->width() != 64) { + return nullptr; + } + + if (operand_inst->opcode() != SpvOpExtInst) { + return nullptr; + } + + if (operand_inst->GetSingleWordInOperand(1) != GLSLstd450FClamp) { + return nullptr; + } + + if (constants[1] == nullptr && constants[0] == nullptr) { + return nullptr; + } + + uint32_t max_id = operand_inst->GetSingleWordInOperand(4); + const analysis::Constant* max_const = + const_mgr->FindDeclaredConstant(max_id); + + uint32_t min_id = operand_inst->GetSingleWordInOperand(3); + const analysis::Constant* min_const = + const_mgr->FindDeclaredConstant(min_id); + + bool found_result = false; + bool result = false; + + switch (cmp_opcode) { + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + if (constants[0]) { + if (min_const) { + if (constants[0]->GetValueAsDouble() < + min_const->GetValueAsDouble()) { + found_result = true; + result = (cmp_opcode == SpvOpFOrdLessThan || + cmp_opcode == SpvOpFUnordLessThan); + } + } + if (max_const) { + if (constants[0]->GetValueAsDouble() >= + max_const->GetValueAsDouble()) { + found_result = true; + result = !(cmp_opcode == SpvOpFOrdLessThan || + cmp_opcode == SpvOpFUnordLessThan); + } + } + } + + if (constants[1]) { + if (max_const) { + if (max_const->GetValueAsDouble() < + constants[1]->GetValueAsDouble()) { + found_result = true; + result = (cmp_opcode == SpvOpFOrdLessThan || + cmp_opcode == SpvOpFUnordLessThan); + } + } + + if (min_const) { + if (min_const->GetValueAsDouble() >= + constants[1]->GetValueAsDouble()) { + found_result = true; + result = !(cmp_opcode == SpvOpFOrdLessThan || + cmp_opcode == SpvOpFUnordLessThan); + } + } + } + break; + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + if (constants[0]) { + if (min_const) { + if (constants[0]->GetValueAsDouble() <= + min_const->GetValueAsDouble()) { + found_result = true; + result = (cmp_opcode == SpvOpFOrdLessThanEqual || + cmp_opcode == SpvOpFUnordLessThanEqual); + } + } + if (max_const) { + if (constants[0]->GetValueAsDouble() > + max_const->GetValueAsDouble()) { + found_result = true; + result = !(cmp_opcode == SpvOpFOrdLessThanEqual || + cmp_opcode == SpvOpFUnordLessThanEqual); + } + } + } + + if (constants[1]) { + if (max_const) { + if (max_const->GetValueAsDouble() <= + constants[1]->GetValueAsDouble()) { + found_result = true; + result = (cmp_opcode == SpvOpFOrdLessThanEqual || + cmp_opcode == SpvOpFUnordLessThanEqual); + } + } + + if (min_const) { + if (min_const->GetValueAsDouble() > + constants[1]->GetValueAsDouble()) { + found_result = true; + result = !(cmp_opcode == SpvOpFOrdLessThanEqual || + cmp_opcode == SpvOpFUnordLessThanEqual); + } + } + } + break; + default: + return nullptr; + } + + if (!found_result) { + return nullptr; + } + + const analysis::Type* bool_type = + context->get_type_mgr()->GetType(inst->type_id()); + const analysis::Constant* result_const = + const_mgr->GetConstant(bool_type, {static_cast(result)}); + assert(result_const); + return result_const; + }; +} + +ConstantFoldingRule FoldFMix() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) + -> const analysis::Constant* { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + assert(inst->opcode() == SpvOpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + assert(inst->GetSingleWordInOperand(1) == GLSLstd450FMix && + "Expecting and FMix instruction."); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return nullptr; + } + + // Make sure all FMix operands are constants. + for (uint32_t i = 1; i < 4; i++) { + if (constants[i] == nullptr) { + return nullptr; + } + } + + const analysis::Constant* one; + bool is_vector = false; + const analysis::Type* result_type = constants[1]->type(); + const analysis::Type* base_type = result_type; + if (base_type->AsVector()) { + is_vector = true; + base_type = base_type->AsVector()->element_type(); + } + assert(base_type->AsFloat() != nullptr && + "FMix is suppose to act on floats or vectors of floats."); + + if (base_type->AsFloat()->width() == 32) { + one = const_mgr->GetConstant(base_type, + utils::FloatProxy(1.0f).GetWords()); + } else { + one = const_mgr->GetConstant(base_type, + utils::FloatProxy(1.0).GetWords()); + } + + if (is_vector) { + uint32_t one_id = const_mgr->GetDefiningInstruction(one)->result_id(); + one = + const_mgr->GetConstant(result_type, std::vector(4, one_id)); + } + + const analysis::Constant* temp1 = FoldFPBinaryOp( + FOLD_FPARITH_OP(-), inst->type_id(), {one, constants[3]}, context); + if (temp1 == nullptr) { + return nullptr; + } + + const analysis::Constant* temp2 = FoldFPBinaryOp( + FOLD_FPARITH_OP(*), inst->type_id(), {constants[1], temp1}, context); + if (temp2 == nullptr) { + return nullptr; + } + const analysis::Constant* temp3 = + FoldFPBinaryOp(FOLD_FPARITH_OP(*), inst->type_id(), + {constants[2], constants[3]}, context); + if (temp3 == nullptr) { + return nullptr; + } + return FoldFPBinaryOp(FOLD_FPARITH_OP(+), inst->type_id(), {temp2, temp3}, + context); + }; +} + +template +IntType FoldIClamp(IntType x, IntType min_val, IntType max_val) { + if (x < min_val) { + x = min_val; + } + if (x > max_val) { + x = max_val; + } + return x; +} + +const analysis::Constant* FoldMin(const analysis::Type* result_type, + const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager*) { + if (const analysis::Integer* int_type = result_type->AsInteger()) { + if (int_type->width() == 32) { + if (int_type->IsSigned()) { + int32_t va = a->GetS32(); + int32_t vb = b->GetS32(); + return (va < vb ? a : b); + } else { + uint32_t va = a->GetU32(); + uint32_t vb = b->GetU32(); + return (va < vb ? a : b); + } + } else if (int_type->width() == 64) { + if (int_type->IsSigned()) { + int64_t va = a->GetS64(); + int64_t vb = b->GetS64(); + return (va < vb ? a : b); + } else { + uint64_t va = a->GetU64(); + uint64_t vb = b->GetU64(); + return (va < vb ? a : b); + } + } + } else if (const analysis::Float* float_type = result_type->AsFloat()) { + if (float_type->width() == 32) { + float va = a->GetFloat(); + float vb = b->GetFloat(); + return (va < vb ? a : b); + } else if (float_type->width() == 64) { + double va = a->GetDouble(); + double vb = b->GetDouble(); + return (va < vb ? a : b); + } + } + return nullptr; +} + +const analysis::Constant* FoldMax(const analysis::Type* result_type, + const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager*) { + if (const analysis::Integer* int_type = result_type->AsInteger()) { + if (int_type->width() == 32) { + if (int_type->IsSigned()) { + int32_t va = a->GetS32(); + int32_t vb = b->GetS32(); + return (va > vb ? a : b); + } else { + uint32_t va = a->GetU32(); + uint32_t vb = b->GetU32(); + return (va > vb ? a : b); + } + } else if (int_type->width() == 64) { + if (int_type->IsSigned()) { + int64_t va = a->GetS64(); + int64_t vb = b->GetS64(); + return (va > vb ? a : b); + } else { + uint64_t va = a->GetU64(); + uint64_t vb = b->GetU64(); + return (va > vb ? a : b); + } + } + } else if (const analysis::Float* float_type = result_type->AsFloat()) { + if (float_type->width() == 32) { + float va = a->GetFloat(); + float vb = b->GetFloat(); + return (va > vb ? a : b); + } else if (float_type->width() == 64) { + double va = a->GetDouble(); + double vb = b->GetDouble(); + return (va > vb ? a : b); + } + } + return nullptr; +} + +// Fold an clamp instruction when all three operands are constant. +const analysis::Constant* FoldClamp1( + IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + // Make sure all Clamp operands are constants. + for (uint32_t i = 1; i < 3; i++) { + if (constants[i] == nullptr) { + return nullptr; + } + } + + const analysis::Constant* temp = FoldFPBinaryOp( + FoldMax, inst->type_id(), {constants[1], constants[2]}, context); + if (temp == nullptr) { + return nullptr; + } + return FoldFPBinaryOp(FoldMin, inst->type_id(), {temp, constants[3]}, + context); +} + +// Fold a clamp instruction when |x >= min_val|. +const analysis::Constant* FoldClamp2( + IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + const analysis::Constant* x = constants[1]; + const analysis::Constant* min_val = constants[2]; + + if (x == nullptr || min_val == nullptr) { + return nullptr; + } + + const analysis::Constant* temp = + FoldFPBinaryOp(FoldMax, inst->type_id(), {x, min_val}, context); + if (temp == min_val) { + // We can assume that |min_val| is less than |max_val|. Therefore, if the + // result of the max operation is |min_val|, we know the result of the min + // operation, even if |max_val| is not a constant. + return min_val; + } + return nullptr; +} + +// Fold a clamp instruction when |x >= max_val|. +const analysis::Constant* FoldClamp3( + IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpExtInst && + "Expecting an extended instruction."); + assert(inst->GetSingleWordInOperand(0) == + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + "Expecting a GLSLstd450 extended instruction."); + + const analysis::Constant* x = constants[1]; + const analysis::Constant* max_val = constants[3]; + + if (x == nullptr || max_val == nullptr) { + return nullptr; + } + + const analysis::Constant* temp = + FoldFPBinaryOp(FoldMin, inst->type_id(), {x, max_val}, context); + if (temp == max_val) { + // We can assume that |min_val| is less than |max_val|. Therefore, if the + // result of the max operation is |min_val|, we know the result of the min + // operation, even if |max_val| is not a constant. + return max_val; + } + return nullptr; +} + +UnaryScalarFoldingRule FoldFTranscendentalUnary(double (*fp)(double)) { + return + [fp](const analysis::Type* result_type, const analysis::Constant* a, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + assert(float_type == result_type->AsFloat()); + if (float_type->width() == 32) { + float fa = a->GetFloat(); + float res = static_cast(fp(fa)); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double fa = a->GetDouble(); + double res = fp(fa); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} + +BinaryScalarFoldingRule FoldFTranscendentalBinary(double (*fp)(double, + double)) { + return + [fp](const analysis::Type* result_type, const analysis::Constant* a, + const analysis::Constant* b, + analysis::ConstantManager* const_mgr) -> const analysis::Constant* { + assert(result_type != nullptr && a != nullptr); + const analysis::Float* float_type = a->type()->AsFloat(); + assert(float_type != nullptr); + assert(float_type == result_type->AsFloat()); + assert(float_type == b->type()->AsFloat()); + if (float_type->width() == 32) { + float fa = a->GetFloat(); + float fb = b->GetFloat(); + float res = static_cast(fp(fa, fb)); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } else if (float_type->width() == 64) { + double fa = a->GetDouble(); + double fb = b->GetDouble(); + double res = fp(fa, fb); + utils::FloatProxy result(res); + std::vector words = result.GetWords(); + return const_mgr->GetConstant(result_type, words); + } + return nullptr; + }; +} +} // namespace + +void ConstantFoldingRules::AddFoldingRules() { + // Add all folding rules to the list for the opcodes to which they apply. + // Note that the order in which rules are added to the list matters. If a rule + // applies to the instruction, the rest of the rules will not be attempted. + // Take that into consideration. + + rules_[SpvOpCompositeConstruct].push_back(FoldCompositeWithConstants()); + + rules_[SpvOpCompositeExtract].push_back(FoldExtractWithConstants()); + + rules_[SpvOpConvertFToS].push_back(FoldFToI()); + rules_[SpvOpConvertFToU].push_back(FoldFToI()); + rules_[SpvOpConvertSToF].push_back(FoldIToF()); + rules_[SpvOpConvertUToF].push_back(FoldIToF()); + + rules_[SpvOpDot].push_back(FoldOpDotWithConstants()); + rules_[SpvOpFAdd].push_back(FoldFAdd()); + rules_[SpvOpFDiv].push_back(FoldFDiv()); + rules_[SpvOpFMul].push_back(FoldFMul()); + rules_[SpvOpFSub].push_back(FoldFSub()); + + rules_[SpvOpFOrdEqual].push_back(FoldFOrdEqual()); + + rules_[SpvOpFUnordEqual].push_back(FoldFUnordEqual()); + + rules_[SpvOpFOrdNotEqual].push_back(FoldFOrdNotEqual()); + + rules_[SpvOpFUnordNotEqual].push_back(FoldFUnordNotEqual()); + + rules_[SpvOpFOrdLessThan].push_back(FoldFOrdLessThan()); + rules_[SpvOpFOrdLessThan].push_back( + FoldFClampFeedingCompare(SpvOpFOrdLessThan)); + + rules_[SpvOpFUnordLessThan].push_back(FoldFUnordLessThan()); + rules_[SpvOpFUnordLessThan].push_back( + FoldFClampFeedingCompare(SpvOpFUnordLessThan)); + + rules_[SpvOpFOrdGreaterThan].push_back(FoldFOrdGreaterThan()); + rules_[SpvOpFOrdGreaterThan].push_back( + FoldFClampFeedingCompare(SpvOpFOrdGreaterThan)); + + rules_[SpvOpFUnordGreaterThan].push_back(FoldFUnordGreaterThan()); + rules_[SpvOpFUnordGreaterThan].push_back( + FoldFClampFeedingCompare(SpvOpFUnordGreaterThan)); + + rules_[SpvOpFOrdLessThanEqual].push_back(FoldFOrdLessThanEqual()); + rules_[SpvOpFOrdLessThanEqual].push_back( + FoldFClampFeedingCompare(SpvOpFOrdLessThanEqual)); + + rules_[SpvOpFUnordLessThanEqual].push_back(FoldFUnordLessThanEqual()); + rules_[SpvOpFUnordLessThanEqual].push_back( + FoldFClampFeedingCompare(SpvOpFUnordLessThanEqual)); + + rules_[SpvOpFOrdGreaterThanEqual].push_back(FoldFOrdGreaterThanEqual()); + rules_[SpvOpFOrdGreaterThanEqual].push_back( + FoldFClampFeedingCompare(SpvOpFOrdGreaterThanEqual)); + + rules_[SpvOpFUnordGreaterThanEqual].push_back(FoldFUnordGreaterThanEqual()); + rules_[SpvOpFUnordGreaterThanEqual].push_back( + FoldFClampFeedingCompare(SpvOpFUnordGreaterThanEqual)); + + rules_[SpvOpVectorShuffle].push_back(FoldVectorShuffleWithConstants()); + rules_[SpvOpVectorTimesScalar].push_back(FoldVectorTimesScalar()); + + rules_[SpvOpFNegate].push_back(FoldFNegate()); + rules_[SpvOpQuantizeToF16].push_back(FoldQuantizeToF16()); + + // Add rules for GLSLstd450 + FeatureManager* feature_manager = context_->get_feature_mgr(); + uint32_t ext_inst_glslstd450_id = + feature_manager->GetExtInstImportId_GLSLstd450(); + if (ext_inst_glslstd450_id != 0) { + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMix}].push_back(FoldFMix()); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SMin}].push_back( + FoldFPBinaryOp(FoldMin)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UMin}].push_back( + FoldFPBinaryOp(FoldMin)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMin}].push_back( + FoldFPBinaryOp(FoldMin)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SMax}].push_back( + FoldFPBinaryOp(FoldMax)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UMax}].push_back( + FoldFPBinaryOp(FoldMax)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMax}].push_back( + FoldFPBinaryOp(FoldMax)); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back( + FoldClamp1); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back( + FoldClamp2); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450UClamp}].push_back( + FoldClamp3); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back( + FoldClamp1); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back( + FoldClamp2); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450SClamp}].push_back( + FoldClamp3); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back( + FoldClamp1); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back( + FoldClamp2); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FClamp}].push_back( + FoldClamp3); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sin}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::sin))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Cos}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::cos))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Tan}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::tan))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Asin}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::asin))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Acos}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::acos))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::atan))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::log))); + +#ifdef __ANDROID__ + // Android NDK r15c tageting ABI 15 doesn't have full support for C++11 + // (no std::exp2/log2). ::exp2 is available from C99 but ::log2 isn't + // available up until ABI 18 so we use a shim + auto log2_shim = [](double v) -> double { return log(v) / log(2.0); }; + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(::exp2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(log2_shim))); +#else + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Exp2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::exp2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Log2}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::log2))); +#endif + + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Sqrt}].push_back( + FoldFPUnaryOp(FoldFTranscendentalUnary(std::sqrt))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Atan2}].push_back( + FoldFPBinaryOp(FoldFTranscendentalBinary(std::atan2))); + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450Pow}].push_back( + FoldFPBinaryOp(FoldFTranscendentalBinary(std::pow))); + } +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/const_folding_rules.h b/third_party/spirv-tools/source/opt/const_folding_rules.h new file mode 100644 index 0000000..41ee2aa --- /dev/null +++ b/third_party/spirv-tools/source/opt/const_folding_rules.h @@ -0,0 +1,130 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONST_FOLDING_RULES_H_ +#define SOURCE_OPT_CONST_FOLDING_RULES_H_ + +#include +#include + +#include "source/opt/constants.h" + +namespace spvtools { +namespace opt { + +// Constant Folding Rules: +// +// The folding mechanism is built around the concept of a |ConstantFoldingRule|. +// A constant folding rule is a function that implements a method of simplifying +// an instruction to a constant. +// +// The inputs to a folding rule are: +// |inst| - the instruction to be simplified. +// |constants| - if an in-operands is an id of a constant, then the +// corresponding value in |constants| contains that +// constant value. Otherwise, the corresponding entry in +// |constants| is |nullptr|. +// +// A constant folding rule returns a pointer to an Constant if |inst| can be +// simplified using this rule. Otherwise, it returns |nullptr|. +// +// See const_folding_rules.cpp for examples on how to write a constant folding +// rule. +// +// Be sure to add new constant folding rules to the table of constant folding +// rules in the constructor for ConstantFoldingRules. The new rule should be +// added to the list for every opcode that it applies to. Note that earlier +// rules in the list are given priority. That is, if an earlier rule is able to +// fold an instruction, the later rules will not be attempted. + +using ConstantFoldingRule = std::function& constants)>; + +class ConstantFoldingRules { + protected: + // The |Key| and |Value| structs are used to by-pass a "decorated name length + // exceeded, name was truncated" warning on VS2013 and VS2015. + struct Key { + uint32_t instruction_set; + uint32_t opcode; + }; + + friend bool operator<(const Key& a, const Key& b) { + if (a.instruction_set < b.instruction_set) { + return true; + } + if (a.instruction_set > b.instruction_set) { + return false; + } + return a.opcode < b.opcode; + } + + struct Value { + std::vector value; + void push_back(ConstantFoldingRule rule) { value.push_back(rule); } + }; + + public: + ConstantFoldingRules(IRContext* ctx) : context_(ctx) {} + virtual ~ConstantFoldingRules() = default; + + // Returns true if there is at least 1 folding rule for |opcode|. + bool HasFoldingRule(const Instruction* inst) const { + return !GetRulesForInstruction(inst).empty(); + } + + // Returns true if there is at least 1 folding rule for |inst|. + const std::vector& GetRulesForInstruction( + const Instruction* inst) const { + if (inst->opcode() != SpvOpExtInst) { + auto it = rules_.find(inst->opcode()); + if (it != rules_.end()) { + return it->second.value; + } + } else { + uint32_t ext_inst_id = inst->GetSingleWordInOperand(0); + uint32_t ext_opcode = inst->GetSingleWordInOperand(1); + auto it = ext_rules_.find({ext_inst_id, ext_opcode}); + if (it != ext_rules_.end()) { + return it->second.value; + } + } + return empty_vector_; + } + + // Add the folding rules. + virtual void AddFoldingRules(); + + protected: + // |rules[opcode]| is the set of rules that can be applied to instructions + // with |opcode| as the opcode. + std::unordered_map rules_; + + // The folding rules for extended instructions. + std::map ext_rules_; + + private: + // The context that the instruction to be folded will be a part of. + IRContext* context_; + + // The empty set of rules to be used as the default return value in + // |GetRulesForInstruction|. + std::vector empty_vector_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONST_FOLDING_RULES_H_ diff --git a/third_party/spirv-tools/source/opt/constants.cpp b/third_party/spirv-tools/source/opt/constants.cpp new file mode 100644 index 0000000..cf24295 --- /dev/null +++ b/third_party/spirv-tools/source/opt/constants.cpp @@ -0,0 +1,428 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/constants.h" + +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +float Constant::GetFloat() const { + assert(type()->AsFloat() != nullptr && type()->AsFloat()->width() == 32); + + if (const FloatConstant* fc = AsFloatConstant()) { + return fc->GetFloatValue(); + } else { + assert(AsNullConstant() && "Must be a floating point constant."); + return 0.0f; + } +} + +double Constant::GetDouble() const { + assert(type()->AsFloat() != nullptr && type()->AsFloat()->width() == 64); + + if (const FloatConstant* fc = AsFloatConstant()) { + return fc->GetDoubleValue(); + } else { + assert(AsNullConstant() && "Must be a floating point constant."); + return 0.0; + } +} + +double Constant::GetValueAsDouble() const { + assert(type()->AsFloat() != nullptr); + if (type()->AsFloat()->width() == 32) { + return GetFloat(); + } else { + assert(type()->AsFloat()->width() == 64); + return GetDouble(); + } +} + +uint32_t Constant::GetU32() const { + assert(type()->AsInteger() != nullptr); + assert(type()->AsInteger()->width() == 32); + + if (const IntConstant* ic = AsIntConstant()) { + return ic->GetU32BitValue(); + } else { + assert(AsNullConstant() && "Must be an integer constant."); + return 0u; + } +} + +uint64_t Constant::GetU64() const { + assert(type()->AsInteger() != nullptr); + assert(type()->AsInteger()->width() == 64); + + if (const IntConstant* ic = AsIntConstant()) { + return ic->GetU64BitValue(); + } else { + assert(AsNullConstant() && "Must be an integer constant."); + return 0u; + } +} + +int32_t Constant::GetS32() const { + assert(type()->AsInteger() != nullptr); + assert(type()->AsInteger()->width() == 32); + + if (const IntConstant* ic = AsIntConstant()) { + return ic->GetS32BitValue(); + } else { + assert(AsNullConstant() && "Must be an integer constant."); + return 0; + } +} + +int64_t Constant::GetS64() const { + assert(type()->AsInteger() != nullptr); + assert(type()->AsInteger()->width() == 64); + + if (const IntConstant* ic = AsIntConstant()) { + return ic->GetS64BitValue(); + } else { + assert(AsNullConstant() && "Must be an integer constant."); + return 0; + } +} + +uint64_t Constant::GetZeroExtendedValue() const { + const auto* int_type = type()->AsInteger(); + assert(int_type != nullptr); + const auto width = int_type->width(); + assert(width <= 64); + + uint64_t value = 0; + if (const IntConstant* ic = AsIntConstant()) { + if (width <= 32) { + value = ic->GetU32BitValue(); + } else { + value = ic->GetU64BitValue(); + } + } else { + assert(AsNullConstant() && "Must be an integer constant."); + } + return value; +} + +int64_t Constant::GetSignExtendedValue() const { + const auto* int_type = type()->AsInteger(); + assert(int_type != nullptr); + const auto width = int_type->width(); + assert(width <= 64); + + int64_t value = 0; + if (const IntConstant* ic = AsIntConstant()) { + if (width <= 32) { + // Let the C++ compiler do the sign extension. + value = int64_t(ic->GetS32BitValue()); + } else { + value = ic->GetS64BitValue(); + } + } else { + assert(AsNullConstant() && "Must be an integer constant."); + } + return value; +} + +ConstantManager::ConstantManager(IRContext* ctx) : ctx_(ctx) { + // Populate the constant table with values from constant declarations in the + // module. The values of each OpConstant declaration is the identity + // assignment (i.e., each constant is its own value). + for (const auto& inst : ctx_->module()->GetConstants()) { + MapInst(inst); + } +} + +Type* ConstantManager::GetType(const Instruction* inst) const { + return context()->get_type_mgr()->GetType(inst->type_id()); +} + +std::vector ConstantManager::GetOperandConstants( + const Instruction* inst) const { + std::vector constants; + for (uint32_t i = 0; i < inst->NumInOperands(); i++) { + const Operand* operand = &inst->GetInOperand(i); + if (operand->type != SPV_OPERAND_TYPE_ID) { + constants.push_back(nullptr); + } else { + uint32_t id = operand->words[0]; + const analysis::Constant* constant = FindDeclaredConstant(id); + constants.push_back(constant); + } + } + return constants; +} + +uint32_t ConstantManager::FindDeclaredConstant(const Constant* c, + uint32_t type_id) const { + c = FindConstant(c); + if (c == nullptr) { + return 0; + } + + for (auto range = const_val_to_id_.equal_range(c); + range.first != range.second; ++range.first) { + Instruction* const_def = + context()->get_def_use_mgr()->GetDef(range.first->second); + if (type_id == 0 || const_def->type_id() == type_id) { + return range.first->second; + } + } + return 0; +} + +std::vector ConstantManager::GetConstantsFromIds( + const std::vector& ids) const { + std::vector constants; + for (uint32_t id : ids) { + if (const Constant* c = FindDeclaredConstant(id)) { + constants.push_back(c); + } else { + return {}; + } + } + return constants; +} + +Instruction* ConstantManager::BuildInstructionAndAddToModule( + const Constant* new_const, Module::inst_iterator* pos, uint32_t type_id) { + // TODO(1841): Handle id overflow. + uint32_t new_id = context()->TakeNextId(); + if (new_id == 0) { + return nullptr; + } + + auto new_inst = CreateInstruction(new_id, new_const, type_id); + if (!new_inst) { + return nullptr; + } + auto* new_inst_ptr = new_inst.get(); + *pos = pos->InsertBefore(std::move(new_inst)); + ++(*pos); + context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inst_ptr); + MapConstantToInst(new_const, new_inst_ptr); + return new_inst_ptr; +} + +Instruction* ConstantManager::GetDefiningInstruction( + const Constant* c, uint32_t type_id, Module::inst_iterator* pos) { + uint32_t decl_id = FindDeclaredConstant(c, type_id); + if (decl_id == 0) { + auto iter = context()->types_values_end(); + if (pos == nullptr) pos = &iter; + return BuildInstructionAndAddToModule(c, pos, type_id); + } else { + auto def = context()->get_def_use_mgr()->GetDef(decl_id); + assert(def != nullptr); + assert((type_id == 0 || def->type_id() == type_id) && + "This constant already has an instruction with a different type."); + return def; + } +} + +std::unique_ptr ConstantManager::CreateConstant( + const Type* type, const std::vector& literal_words_or_ids) const { + if (literal_words_or_ids.size() == 0) { + // Constant declared with OpConstantNull + return MakeUnique(type); + } else if (auto* bt = type->AsBool()) { + assert(literal_words_or_ids.size() == 1 && + "Bool constant should be declared with one operand"); + return MakeUnique(bt, literal_words_or_ids.front()); + } else if (auto* it = type->AsInteger()) { + return MakeUnique(it, literal_words_or_ids); + } else if (auto* ft = type->AsFloat()) { + return MakeUnique(ft, literal_words_or_ids); + } else if (auto* vt = type->AsVector()) { + auto components = GetConstantsFromIds(literal_words_or_ids); + if (components.empty()) return nullptr; + // All components of VectorConstant must be of type Bool, Integer or Float. + if (!std::all_of(components.begin(), components.end(), + [](const Constant* c) { + if (c->type()->AsBool() || c->type()->AsInteger() || + c->type()->AsFloat()) { + return true; + } else { + return false; + } + })) + return nullptr; + // All components of VectorConstant must be in the same type. + const auto* component_type = components.front()->type(); + if (!std::all_of(components.begin(), components.end(), + [&component_type](const Constant* c) { + if (c->type() == component_type) return true; + return false; + })) + return nullptr; + return MakeUnique(vt, components); + } else if (auto* mt = type->AsMatrix()) { + auto components = GetConstantsFromIds(literal_words_or_ids); + if (components.empty()) return nullptr; + return MakeUnique(mt, components); + } else if (auto* st = type->AsStruct()) { + auto components = GetConstantsFromIds(literal_words_or_ids); + if (components.empty()) return nullptr; + return MakeUnique(st, components); + } else if (auto* at = type->AsArray()) { + auto components = GetConstantsFromIds(literal_words_or_ids); + if (components.empty()) return nullptr; + return MakeUnique(at, components); + } else { + return nullptr; + } +} + +const Constant* ConstantManager::GetConstantFromInst(const Instruction* inst) { + std::vector literal_words_or_ids; + + // Collect the constant defining literals or component ids. + for (uint32_t i = 0; i < inst->NumInOperands(); i++) { + literal_words_or_ids.insert(literal_words_or_ids.end(), + inst->GetInOperand(i).words.begin(), + inst->GetInOperand(i).words.end()); + } + + switch (inst->opcode()) { + // OpConstant{True|False} have the value embedded in the opcode. So they + // are not handled by the for-loop above. Here we add the value explicitly. + case SpvOp::SpvOpConstantTrue: + literal_words_or_ids.push_back(true); + break; + case SpvOp::SpvOpConstantFalse: + literal_words_or_ids.push_back(false); + break; + case SpvOp::SpvOpConstantNull: + case SpvOp::SpvOpConstant: + case SpvOp::SpvOpConstantComposite: + case SpvOp::SpvOpSpecConstantComposite: + break; + default: + return nullptr; + } + + return GetConstant(GetType(inst), literal_words_or_ids); +} + +std::unique_ptr ConstantManager::CreateInstruction( + uint32_t id, const Constant* c, uint32_t type_id) const { + uint32_t type = + (type_id == 0) ? context()->get_type_mgr()->GetId(c->type()) : type_id; + if (c->AsNullConstant()) { + return MakeUnique(context(), SpvOp::SpvOpConstantNull, type, + id, std::initializer_list{}); + } else if (const BoolConstant* bc = c->AsBoolConstant()) { + return MakeUnique( + context(), + bc->value() ? SpvOp::SpvOpConstantTrue : SpvOp::SpvOpConstantFalse, + type, id, std::initializer_list{}); + } else if (const IntConstant* ic = c->AsIntConstant()) { + return MakeUnique( + context(), SpvOp::SpvOpConstant, type, id, + std::initializer_list{ + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + ic->words())}); + } else if (const FloatConstant* fc = c->AsFloatConstant()) { + return MakeUnique( + context(), SpvOp::SpvOpConstant, type, id, + std::initializer_list{ + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + fc->words())}); + } else if (const CompositeConstant* cc = c->AsCompositeConstant()) { + return CreateCompositeInstruction(id, cc, type_id); + } else { + return nullptr; + } +} + +std::unique_ptr ConstantManager::CreateCompositeInstruction( + uint32_t result_id, const CompositeConstant* cc, uint32_t type_id) const { + std::vector operands; + Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + uint32_t component_index = 0; + for (const Constant* component_const : cc->GetComponents()) { + uint32_t component_type_id = 0; + if (type_inst && type_inst->opcode() == SpvOpTypeStruct) { + component_type_id = type_inst->GetSingleWordInOperand(component_index); + } else if (type_inst && type_inst->opcode() == SpvOpTypeArray) { + component_type_id = type_inst->GetSingleWordInOperand(0); + } + uint32_t id = FindDeclaredConstant(component_const, component_type_id); + + if (id == 0) { + // Cannot get the id of the component constant, while all components + // should have been added to the module prior to the composite constant. + // Cannot create OpConstantComposite instruction in this case. + return nullptr; + } + operands.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_ID, + std::initializer_list{id}); + component_index++; + } + uint32_t type = + (type_id == 0) ? context()->get_type_mgr()->GetId(cc->type()) : type_id; + return MakeUnique(context(), SpvOp::SpvOpConstantComposite, type, + result_id, std::move(operands)); +} + +const Constant* ConstantManager::GetConstant( + const Type* type, const std::vector& literal_words_or_ids) { + auto cst = CreateConstant(type, literal_words_or_ids); + return cst ? RegisterConstant(std::move(cst)) : nullptr; +} + +uint32_t ConstantManager::GetFloatConst(float val) { + Type* float_type = context()->get_type_mgr()->GetFloatType(); + utils::FloatProxy v(val); + const Constant* c = GetConstant(float_type, v.GetWords()); + return GetDefiningInstruction(c)->result_id(); +} + +uint32_t ConstantManager::GetSIntConst(int32_t val) { + Type* sint_type = context()->get_type_mgr()->GetSIntType(); + const Constant* c = GetConstant(sint_type, {static_cast(val)}); + return GetDefiningInstruction(c)->result_id(); +} + +std::vector Constant::GetVectorComponents( + analysis::ConstantManager* const_mgr) const { + std::vector components; + const analysis::VectorConstant* a = this->AsVectorConstant(); + const analysis::Vector* vector_type = this->type()->AsVector(); + assert(vector_type != nullptr); + if (a != nullptr) { + for (uint32_t i = 0; i < vector_type->element_count(); ++i) { + components.push_back(a->GetComponents()[i]); + } + } else { + const analysis::Type* element_type = vector_type->element_type(); + const analysis::Constant* element_null_const = + const_mgr->GetConstant(element_type, {}); + for (uint32_t i = 0; i < vector_type->element_count(); ++i) { + components.push_back(element_null_const); + } + } + return components; +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/constants.h b/third_party/spirv-tools/source/opt/constants.h new file mode 100644 index 0000000..e17ae6b --- /dev/null +++ b/third_party/spirv-tools/source/opt/constants.h @@ -0,0 +1,706 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_CONSTANTS_H_ +#define SOURCE_OPT_CONSTANTS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/module.h" +#include "source/opt/type_manager.h" +#include "source/opt/types.h" +#include "source/util/hex_float.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +class IRContext; + +namespace analysis { + +// Class hierarchy to represent the normal constants defined through +// OpConstantTrue, OpConstantFalse, OpConstant, OpConstantNull and +// OpConstantComposite instructions. +// TODO(qining): Add class for constants defined with OpConstantSampler. +class Constant; +class ScalarConstant; +class IntConstant; +class FloatConstant; +class BoolConstant; +class CompositeConstant; +class StructConstant; +class VectorConstant; +class MatrixConstant; +class ArrayConstant; +class NullConstant; +class ConstantManager; + +// Abstract class for a SPIR-V constant. It has a bunch of As methods, +// which is used as a way to probe the actual +class Constant { + public: + Constant() = delete; + virtual ~Constant() {} + + // Make a deep copy of this constant. + virtual std::unique_ptr Copy() const = 0; + + // reflections + virtual ScalarConstant* AsScalarConstant() { return nullptr; } + virtual IntConstant* AsIntConstant() { return nullptr; } + virtual FloatConstant* AsFloatConstant() { return nullptr; } + virtual BoolConstant* AsBoolConstant() { return nullptr; } + virtual CompositeConstant* AsCompositeConstant() { return nullptr; } + virtual StructConstant* AsStructConstant() { return nullptr; } + virtual VectorConstant* AsVectorConstant() { return nullptr; } + virtual MatrixConstant* AsMatrixConstant() { return nullptr; } + virtual ArrayConstant* AsArrayConstant() { return nullptr; } + virtual NullConstant* AsNullConstant() { return nullptr; } + + virtual const ScalarConstant* AsScalarConstant() const { return nullptr; } + virtual const IntConstant* AsIntConstant() const { return nullptr; } + virtual const FloatConstant* AsFloatConstant() const { return nullptr; } + virtual const BoolConstant* AsBoolConstant() const { return nullptr; } + virtual const CompositeConstant* AsCompositeConstant() const { + return nullptr; + } + virtual const StructConstant* AsStructConstant() const { return nullptr; } + virtual const VectorConstant* AsVectorConstant() const { return nullptr; } + virtual const MatrixConstant* AsMatrixConstant() const { return nullptr; } + virtual const ArrayConstant* AsArrayConstant() const { return nullptr; } + virtual const NullConstant* AsNullConstant() const { return nullptr; } + + // Returns the float representation of the constant. Must be a 32 bit + // Float type. + float GetFloat() const; + + // Returns the double representation of the constant. Must be a 64 bit + // Float type. + double GetDouble() const; + + // Returns the double representation of the constant. Must be a 32-bit or + // 64-bit Float type. + double GetValueAsDouble() const; + + // Returns uint32_t representation of the constant. Must be a 32 bit + // Integer type. + uint32_t GetU32() const; + + // Returns uint64_t representation of the constant. Must be a 64 bit + // Integer type. + uint64_t GetU64() const; + + // Returns int32_t representation of the constant. Must be a 32 bit + // Integer type. + int32_t GetS32() const; + + // Returns int64_t representation of the constant. Must be a 64 bit + // Integer type. + int64_t GetS64() const; + + // Returns the zero-extended representation of an integer constant. Must + // be an integral constant of at most 64 bits. + uint64_t GetZeroExtendedValue() const; + + // Returns the sign-extended representation of an integer constant. Must + // be an integral constant of at most 64 bits. + int64_t GetSignExtendedValue() const; + + // Returns true if the constant is a zero or a composite containing 0s. + virtual bool IsZero() const { return false; } + + const Type* type() const { return type_; } + + // Returns an std::vector containing the elements of |constant|. The type of + // |constant| must be |Vector|. + std::vector GetVectorComponents( + ConstantManager* const_mgr) const; + + protected: + Constant(const Type* ty) : type_(ty) {} + + // The type of this constant. + const Type* type_; +}; + +// Abstract class for scalar type constants. +class ScalarConstant : public Constant { + public: + ScalarConstant() = delete; + ScalarConstant* AsScalarConstant() override { return this; } + const ScalarConstant* AsScalarConstant() const override { return this; } + + // Returns a const reference of the value of this constant in 32-bit words. + virtual const std::vector& words() const { return words_; } + + // Returns true if the value is zero. + bool IsZero() const override { + bool is_zero = true; + for (uint32_t v : words()) { + if (v != 0) { + is_zero = false; + break; + } + } + return is_zero; + } + + protected: + ScalarConstant(const Type* ty, const std::vector& w) + : Constant(ty), words_(w) {} + ScalarConstant(const Type* ty, std::vector&& w) + : Constant(ty), words_(std::move(w)) {} + std::vector words_; +}; + +// Integer type constant. +class IntConstant : public ScalarConstant { + public: + IntConstant(const Integer* ty, const std::vector& w) + : ScalarConstant(ty, w) {} + IntConstant(const Integer* ty, std::vector&& w) + : ScalarConstant(ty, std::move(w)) {} + + IntConstant* AsIntConstant() override { return this; } + const IntConstant* AsIntConstant() const override { return this; } + + int32_t GetS32BitValue() const { + // Relies on signed values smaller than 32-bit being sign extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 1); + return words()[0]; + } + + uint32_t GetU32BitValue() const { + // Relies on unsigned values smaller than 32-bit being zero extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 1); + return words()[0]; + } + + int64_t GetS64BitValue() const { + // Relies on unsigned values smaller than 64-bit being sign extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 2); + return static_cast(words()[1]) << 32 | + static_cast(words()[0]); + } + + uint64_t GetU64BitValue() const { + // Relies on unsigned values smaller than 64-bit being zero extended. See + // section 2.2.1 of the SPIR-V spec. + assert(words().size() == 2); + return static_cast(words()[1]) << 32 | + static_cast(words()[0]); + } + + // Make a copy of this IntConstant instance. + std::unique_ptr CopyIntConstant() const { + return MakeUnique(type_->AsInteger(), words_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyIntConstant().release()); + } +}; + +// Float type constant. +class FloatConstant : public ScalarConstant { + public: + FloatConstant(const Float* ty, const std::vector& w) + : ScalarConstant(ty, w) {} + FloatConstant(const Float* ty, std::vector&& w) + : ScalarConstant(ty, std::move(w)) {} + + FloatConstant* AsFloatConstant() override { return this; } + const FloatConstant* AsFloatConstant() const override { return this; } + + // Make a copy of this FloatConstant instance. + std::unique_ptr CopyFloatConstant() const { + return MakeUnique(type_->AsFloat(), words_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyFloatConstant().release()); + } + + // Returns the float value of |this|. The type of |this| must be |Float| with + // width of 32. + float GetFloatValue() const { + assert(type()->AsFloat()->width() == 32 && + "Not a 32-bit floating point value."); + utils::FloatProxy a(words()[0]); + return a.getAsFloat(); + } + + // Returns the double value of |this|. The type of |this| must be |Float| + // with width of 64. + double GetDoubleValue() const { + assert(type()->AsFloat()->width() == 64 && + "Not a 32-bit floating point value."); + uint64_t combined_words = words()[1]; + combined_words = combined_words << 32; + combined_words |= words()[0]; + utils::FloatProxy a(combined_words); + return a.getAsFloat(); + } +}; + +// Bool type constant. +class BoolConstant : public ScalarConstant { + public: + BoolConstant(const Bool* ty, bool v) + : ScalarConstant(ty, {static_cast(v)}), value_(v) {} + + BoolConstant* AsBoolConstant() override { return this; } + const BoolConstant* AsBoolConstant() const override { return this; } + + // Make a copy of this BoolConstant instance. + std::unique_ptr CopyBoolConstant() const { + return MakeUnique(type_->AsBool(), value_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyBoolConstant().release()); + } + + bool value() const { return value_; } + + private: + bool value_; +}; + +// Abstract class for composite constants. +class CompositeConstant : public Constant { + public: + CompositeConstant() = delete; + CompositeConstant* AsCompositeConstant() override { return this; } + const CompositeConstant* AsCompositeConstant() const override { return this; } + + // Returns a const reference of the components held in this composite + // constant. + virtual const std::vector& GetComponents() const { + return components_; + } + + bool IsZero() const override { + for (const Constant* c : GetComponents()) { + if (!c->IsZero()) { + return false; + } + } + return true; + } + + protected: + CompositeConstant(const Type* ty) : Constant(ty), components_() {} + CompositeConstant(const Type* ty, + const std::vector& components) + : Constant(ty), components_(components) {} + CompositeConstant(const Type* ty, std::vector&& components) + : Constant(ty), components_(std::move(components)) {} + std::vector components_; +}; + +// Struct type constant. +class StructConstant : public CompositeConstant { + public: + StructConstant(const Struct* ty) : CompositeConstant(ty) {} + StructConstant(const Struct* ty, + const std::vector& components) + : CompositeConstant(ty, components) {} + StructConstant(const Struct* ty, std::vector&& components) + : CompositeConstant(ty, std::move(components)) {} + + StructConstant* AsStructConstant() override { return this; } + const StructConstant* AsStructConstant() const override { return this; } + + // Make a copy of this StructConstant instance. + std::unique_ptr CopyStructConstant() const { + return MakeUnique(type_->AsStruct(), components_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyStructConstant().release()); + } +}; + +// Vector type constant. +class VectorConstant : public CompositeConstant { + public: + VectorConstant(const Vector* ty) + : CompositeConstant(ty), component_type_(ty->element_type()) {} + VectorConstant(const Vector* ty, + const std::vector& components) + : CompositeConstant(ty, components), + component_type_(ty->element_type()) {} + VectorConstant(const Vector* ty, std::vector&& components) + : CompositeConstant(ty, std::move(components)), + component_type_(ty->element_type()) {} + + VectorConstant* AsVectorConstant() override { return this; } + const VectorConstant* AsVectorConstant() const override { return this; } + + // Make a copy of this VectorConstant instance. + std::unique_ptr CopyVectorConstant() const { + auto another = MakeUnique(type_->AsVector()); + another->components_.insert(another->components_.end(), components_.begin(), + components_.end()); + return another; + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyVectorConstant().release()); + } + + const Type* component_type() const { return component_type_; } + + private: + const Type* component_type_; +}; + +// Matrix type constant. +class MatrixConstant : public CompositeConstant { + public: + MatrixConstant(const Matrix* ty) + : CompositeConstant(ty), component_type_(ty->element_type()) {} + MatrixConstant(const Matrix* ty, + const std::vector& components) + : CompositeConstant(ty, components), + component_type_(ty->element_type()) {} + MatrixConstant(const Vector* ty, std::vector&& components) + : CompositeConstant(ty, std::move(components)), + component_type_(ty->element_type()) {} + + MatrixConstant* AsMatrixConstant() override { return this; } + const MatrixConstant* AsMatrixConstant() const override { return this; } + + // Make a copy of this MatrixConstant instance. + std::unique_ptr CopyMatrixConstant() const { + auto another = MakeUnique(type_->AsMatrix()); + another->components_.insert(another->components_.end(), components_.begin(), + components_.end()); + return another; + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyMatrixConstant().release()); + } + + const Type* component_type() { return component_type_; } + + private: + const Type* component_type_; +}; + +// Array type constant. +class ArrayConstant : public CompositeConstant { + public: + ArrayConstant(const Array* ty) : CompositeConstant(ty) {} + ArrayConstant(const Array* ty, const std::vector& components) + : CompositeConstant(ty, components) {} + ArrayConstant(const Array* ty, std::vector&& components) + : CompositeConstant(ty, std::move(components)) {} + + ArrayConstant* AsArrayConstant() override { return this; } + const ArrayConstant* AsArrayConstant() const override { return this; } + + // Make a copy of this ArrayConstant instance. + std::unique_ptr CopyArrayConstant() const { + return MakeUnique(type_->AsArray(), components_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyArrayConstant().release()); + } +}; + +// Null type constant. +class NullConstant : public Constant { + public: + NullConstant(const Type* ty) : Constant(ty) {} + NullConstant* AsNullConstant() override { return this; } + const NullConstant* AsNullConstant() const override { return this; } + + // Make a copy of this NullConstant instance. + std::unique_ptr CopyNullConstant() const { + return MakeUnique(type_); + } + std::unique_ptr Copy() const override { + return std::unique_ptr(CopyNullConstant().release()); + } + bool IsZero() const override { return true; } +}; + +// Hash function for Constant instances. Use the structure of the constant as +// the key. +struct ConstantHash { + void add_pointer(std::u32string* h, const void* p) const { + uint64_t ptr_val = reinterpret_cast(p); + h->push_back(static_cast(ptr_val >> 32)); + h->push_back(static_cast(ptr_val)); + } + + size_t operator()(const Constant* const_val) const { + std::u32string h; + add_pointer(&h, const_val->type()); + if (const auto scalar = const_val->AsScalarConstant()) { + for (const auto& w : scalar->words()) { + h.push_back(w); + } + } else if (const auto composite = const_val->AsCompositeConstant()) { + for (const auto& c : composite->GetComponents()) { + add_pointer(&h, c); + } + } else if (const_val->AsNullConstant()) { + h.push_back(0); + } else { + assert( + false && + "Tried to compute the hash value of an invalid Constant instance."); + } + + return std::hash()(h); + } +}; + +// Equality comparison structure for two constants. +struct ConstantEqual { + bool operator()(const Constant* c1, const Constant* c2) const { + if (c1->type() != c2->type()) { + return false; + } + + if (const auto& s1 = c1->AsScalarConstant()) { + const auto& s2 = c2->AsScalarConstant(); + return s2 && s1->words() == s2->words(); + } else if (const auto& composite1 = c1->AsCompositeConstant()) { + const auto& composite2 = c2->AsCompositeConstant(); + return composite2 && + composite1->GetComponents() == composite2->GetComponents(); + } else if (c1->AsNullConstant()) { + return c2->AsNullConstant() != nullptr; + } else { + assert(false && "Tried to compare two invalid Constant instances."); + } + return false; + } +}; + +// This class represents a pool of constants. +class ConstantManager { + public: + ConstantManager(IRContext* ctx); + + IRContext* context() const { return ctx_; } + + // Gets or creates a unique Constant instance of type |type| and a vector of + // constant defining words |words|. If a Constant instance existed already in + // the constant pool, it returns a pointer to it. Otherwise, it creates one + // using CreateConstant. If a new Constant instance cannot be created, it + // returns nullptr. + const Constant* GetConstant( + const Type* type, const std::vector& literal_words_or_ids); + + template + const Constant* GetConstant(const Type* type, const C& literal_words_or_ids) { + return GetConstant(type, std::vector(literal_words_or_ids.begin(), + literal_words_or_ids.end())); + } + + // Gets or creates a Constant instance to hold the constant value of the given + // instruction. It returns a pointer to a Constant instance or nullptr if it + // could not create the constant. + const Constant* GetConstantFromInst(const Instruction* inst); + + // Gets or creates a constant defining instruction for the given Constant |c|. + // If |c| had already been defined, it returns a pointer to the existing + // declaration. Otherwise, it calls BuildInstructionAndAddToModule. If the + // optional |pos| is given, it will insert any newly created instructions at + // the given instruction iterator position. Otherwise, it inserts the new + // instruction at the end of the current module's types section. + // + // |type_id| is an optional argument for disambiguating equivalent types. If + // |type_id| is specified, the contant returned will have that type id. + Instruction* GetDefiningInstruction(const Constant* c, uint32_t type_id = 0, + Module::inst_iterator* pos = nullptr); + + // Creates a constant defining instruction for the given Constant instance + // and inserts the instruction at the position specified by the given + // instruction iterator. Returns a pointer to the created instruction if + // succeeded, otherwise returns a null pointer. The instruction iterator + // points to the same instruction before and after the insertion. This is the + // only method that actually manages id creation/assignment and instruction + // creation/insertion for a new Constant instance. + // + // |type_id| is an optional argument for disambiguating equivalent types. If + // |type_id| is specified, it is used as the type of the constant. Otherwise + // the type of the constant is derived by getting an id from the type manager + // for |c|. + Instruction* BuildInstructionAndAddToModule(const Constant* c, + Module::inst_iterator* pos, + uint32_t type_id = 0); + + // A helper function to get the result type of the given instruction. Returns + // nullptr if the instruction does not have a type id (type id is 0). + Type* GetType(const Instruction* inst) const; + + // A helper function to get the collected normal constant with the given id. + // Returns the pointer to the Constant instance in case it is found. + // Otherwise, it returns a null pointer. + const Constant* FindDeclaredConstant(uint32_t id) const { + auto iter = id_to_const_val_.find(id); + return (iter != id_to_const_val_.end()) ? iter->second : nullptr; + } + + // A helper function to get the id of a collected constant with the pointer + // to the Constant instance. Returns 0 in case the constant is not found. + uint32_t FindDeclaredConstant(const Constant* c, uint32_t type_id) const; + + // Returns the canonical constant that has the same structure and value as the + // given Constant |cst|. If none is found, it returns nullptr. + // + // TODO: Should be able to give a type id to disambiguate types with the same + // structure. + const Constant* FindConstant(const Constant* c) const { + auto it = const_pool_.find(c); + return (it != const_pool_.end()) ? *it : nullptr; + } + + // Registers a new constant |cst| in the constant pool. If the constant + // existed already, it returns a pointer to the previously existing Constant + // in the pool. Otherwise, it returns |cst|. + const Constant* RegisterConstant(std::unique_ptr cst) { + auto ret = const_pool_.insert(cst.get()); + if (ret.second) { + owned_constants_.emplace_back(std::move(cst)); + } + return *ret.first; + } + + // A helper function to get a vector of Constant instances with the specified + // ids. If it can not find the Constant instance for any one of the ids, + // it returns an empty vector. + std::vector GetConstantsFromIds( + const std::vector& ids) const; + + // Returns a vector of constants representing each in operand. If an operand + // is not constant its entry is nullptr. + std::vector GetOperandConstants( + const Instruction* inst) const; + + // Records a mapping between |inst| and the constant value generated by it. + // It returns true if a new Constant was successfully mapped, false if |inst| + // generates no constant values. + bool MapInst(Instruction* inst) { + if (auto cst = GetConstantFromInst(inst)) { + MapConstantToInst(cst, inst); + return true; + } + return false; + } + + void RemoveId(uint32_t id) { + auto it = id_to_const_val_.find(id); + if (it != id_to_const_val_.end()) { + const_val_to_id_.erase(it->second); + id_to_const_val_.erase(it); + } + } + + // Records a new mapping between |inst| and |const_value|. This updates the + // two mappings |id_to_const_val_| and |const_val_to_id_|. + void MapConstantToInst(const Constant* const_value, Instruction* inst) { + if (id_to_const_val_.insert({inst->result_id(), const_value}).second) { + const_val_to_id_.insert({const_value, inst->result_id()}); + } + } + + // Returns the id of a 32-bit floating point constant with value |val|. + uint32_t GetFloatConst(float val); + + // Returns the id of a 32-bit signed integer constant with value |val|. + uint32_t GetSIntConst(int32_t val); + + private: + // Creates a Constant instance with the given type and a vector of constant + // defining words. Returns a unique pointer to the created Constant instance + // if the Constant instance can be created successfully. To create scalar + // type constants, the vector should contain the constant value in 32 bit + // words and the given type must be of type Bool, Integer or Float. To create + // composite type constants, the vector should contain the component ids, and + // those component ids should have been recorded before as Normal Constants. + // And the given type must be of type Struct, Vector or Array. When creating + // VectorType Constant instance, the components must be scalars of the same + // type, either Bool, Integer or Float. If any of the rules above failed, the + // creation will fail and nullptr will be returned. If the vector is empty, + // a NullConstant instance will be created with the given type. + std::unique_ptr CreateConstant( + const Type* type, + const std::vector& literal_words_or_ids) const; + + // Creates an instruction with the given result id to declare a constant + // represented by the given Constant instance. Returns an unique pointer to + // the created instruction if the instruction can be created successfully. + // Otherwise, returns a null pointer. + // + // |type_id| is an optional argument for disambiguating equivalent types. If + // |type_id| is specified, it is used as the type of the constant. Otherwise + // the type of the constant is derived by getting an id from the type manager + // for |c|. + std::unique_ptr CreateInstruction(uint32_t result_id, + const Constant* c, + uint32_t type_id = 0) const; + + // Creates an OpConstantComposite instruction with the given result id and + // the CompositeConst instance which represents a composite constant. Returns + // an unique pointer to the created instruction if succeeded. Otherwise + // returns a null pointer. + // + // |type_id| is an optional argument for disambiguating equivalent types. If + // |type_id| is specified, it is used as the type of the constant. Otherwise + // the type of the constant is derived by getting an id from the type manager + // for |c|. + std::unique_ptr CreateCompositeInstruction( + uint32_t result_id, const CompositeConstant* cc, + uint32_t type_id = 0) const; + + // IR context that owns this constant manager. + IRContext* ctx_; + + // A mapping from the result ids of Normal Constants to their + // Constant instances. All Normal Constants in the module, either + // existing ones before optimization or the newly generated ones, should have + // their Constant instance stored and their result id registered in this map. + std::unordered_map id_to_const_val_; + + // A mapping from the Constant instance of Normal Constants to their + // result id in the module. This is a mirror map of |id_to_const_val_|. All + // Normal Constants that defining instructions in the module should have + // their Constant and their result id registered here. + std::multimap const_val_to_id_; + + // The constant pool. All created constants are registered here. + std::unordered_set const_pool_; + + // The constant that are owned by the constant manager. Every constant in + // |const_pool_| should be in |owned_constants_| as well. + std::vector> owned_constants_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_CONSTANTS_H_ diff --git a/third_party/spirv-tools/source/opt/convert_to_half_pass.cpp b/third_party/spirv-tools/source/opt/convert_to_half_pass.cpp new file mode 100644 index 0000000..5022e1b --- /dev/null +++ b/third_party/spirv-tools/source/opt/convert_to_half_pass.cpp @@ -0,0 +1,471 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "convert_to_half_pass.h" + +#include "source/opt/ir_builder.h" + +namespace { + +// Indices of operands in SPIR-V instructions +static const int kImageSampleDrefIdInIdx = 2; + +} // anonymous namespace + +namespace spvtools { +namespace opt { + +bool ConvertToHalfPass::IsArithmetic(Instruction* inst) { + return target_ops_core_.count(inst->opcode()) != 0 || + (inst->opcode() == SpvOpExtInst && + inst->GetSingleWordInOperand(0) == + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + target_ops_450_.count(inst->GetSingleWordInOperand(1)) != 0); +} + +bool ConvertToHalfPass::IsFloat(Instruction* inst, uint32_t width) { + uint32_t ty_id = inst->type_id(); + if (ty_id == 0) return false; + return Pass::IsFloat(ty_id, width); +} + +bool ConvertToHalfPass::IsDecoratedRelaxed(Instruction* inst) { + uint32_t r_id = inst->result_id(); + for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false)) + if (r_inst->opcode() == SpvOpDecorate && + r_inst->GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) + return true; + return false; +} + +bool ConvertToHalfPass::IsRelaxed(uint32_t id) { + return relaxed_ids_set_.count(id) > 0; +} + +void ConvertToHalfPass::AddRelaxed(uint32_t id) { relaxed_ids_set_.insert(id); } + +analysis::Type* ConvertToHalfPass::FloatScalarType(uint32_t width) { + analysis::Float float_ty(width); + return context()->get_type_mgr()->GetRegisteredType(&float_ty); +} + +analysis::Type* ConvertToHalfPass::FloatVectorType(uint32_t v_len, + uint32_t width) { + analysis::Type* reg_float_ty = FloatScalarType(width); + analysis::Vector vec_ty(reg_float_ty, v_len); + return context()->get_type_mgr()->GetRegisteredType(&vec_ty); +} + +analysis::Type* ConvertToHalfPass::FloatMatrixType(uint32_t v_cnt, + uint32_t vty_id, + uint32_t width) { + Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id); + uint32_t v_len = vty_inst->GetSingleWordInOperand(1); + analysis::Type* reg_vec_ty = FloatVectorType(v_len, width); + analysis::Matrix mat_ty(reg_vec_ty, v_cnt); + return context()->get_type_mgr()->GetRegisteredType(&mat_ty); +} + +uint32_t ConvertToHalfPass::EquivFloatTypeId(uint32_t ty_id, uint32_t width) { + analysis::Type* reg_equiv_ty; + Instruction* ty_inst = get_def_use_mgr()->GetDef(ty_id); + if (ty_inst->opcode() == SpvOpTypeMatrix) + reg_equiv_ty = FloatMatrixType(ty_inst->GetSingleWordInOperand(1), + ty_inst->GetSingleWordInOperand(0), width); + else if (ty_inst->opcode() == SpvOpTypeVector) + reg_equiv_ty = FloatVectorType(ty_inst->GetSingleWordInOperand(1), width); + else // SpvOpTypeFloat + reg_equiv_ty = FloatScalarType(width); + return context()->get_type_mgr()->GetTypeInstruction(reg_equiv_ty); +} + +void ConvertToHalfPass::GenConvert(uint32_t* val_idp, uint32_t width, + Instruction* inst) { + Instruction* val_inst = get_def_use_mgr()->GetDef(*val_idp); + uint32_t ty_id = val_inst->type_id(); + uint32_t nty_id = EquivFloatTypeId(ty_id, width); + if (nty_id == ty_id) return; + Instruction* cvt_inst; + InstructionBuilder builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + if (val_inst->opcode() == SpvOpUndef) + cvt_inst = builder.AddNullaryOp(nty_id, SpvOpUndef); + else + cvt_inst = builder.AddUnaryOp(nty_id, SpvOpFConvert, *val_idp); + *val_idp = cvt_inst->result_id(); +} + +bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) { + if (inst->opcode() != SpvOpFConvert) return false; + uint32_t mty_id = inst->type_id(); + Instruction* mty_inst = get_def_use_mgr()->GetDef(mty_id); + if (mty_inst->opcode() != SpvOpTypeMatrix) return false; + uint32_t vty_id = mty_inst->GetSingleWordInOperand(0); + uint32_t v_cnt = mty_inst->GetSingleWordInOperand(1); + Instruction* vty_inst = get_def_use_mgr()->GetDef(vty_id); + uint32_t cty_id = vty_inst->GetSingleWordInOperand(0); + Instruction* cty_inst = get_def_use_mgr()->GetDef(cty_id); + InstructionBuilder builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Convert each component vector, combine them with OpCompositeConstruct + // and replace original instruction. + uint32_t orig_width = (cty_inst->GetSingleWordInOperand(0) == 16) ? 32 : 16; + uint32_t orig_mat_id = inst->GetSingleWordInOperand(0); + uint32_t orig_vty_id = EquivFloatTypeId(vty_id, orig_width); + std::vector opnds = {}; + for (uint32_t vidx = 0; vidx < v_cnt; ++vidx) { + Instruction* ext_inst = builder.AddIdLiteralOp( + orig_vty_id, SpvOpCompositeExtract, orig_mat_id, vidx); + Instruction* cvt_inst = + builder.AddUnaryOp(vty_id, SpvOpFConvert, ext_inst->result_id()); + opnds.push_back({SPV_OPERAND_TYPE_ID, {cvt_inst->result_id()}}); + } + uint32_t mat_id = TakeNextId(); + std::unique_ptr mat_inst(new Instruction( + context(), SpvOpCompositeConstruct, mty_id, mat_id, opnds)); + (void)builder.AddInstruction(std::move(mat_inst)); + context()->ReplaceAllUsesWith(inst->result_id(), mat_id); + // Turn original instruction into copy so it is valid. + inst->SetOpcode(SpvOpCopyObject); + inst->SetResultType(EquivFloatTypeId(mty_id, orig_width)); + get_def_use_mgr()->AnalyzeInstUse(inst); + return true; +} + +void ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) { + context()->get_decoration_mgr()->RemoveDecorationsFrom( + id, [](const Instruction& dec) { + if (dec.opcode() == SpvOpDecorate && + dec.GetSingleWordInOperand(1u) == SpvDecorationRelaxedPrecision) + return true; + else + return false; + }); +} + +bool ConvertToHalfPass::GenHalfArith(Instruction* inst) { + bool modified = false; + // Convert all float32 based operands to float16 equivalent and change + // instruction type to float16 equivalent. + inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { + Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); + if (!IsFloat(op_inst, 32)) return; + GenConvert(idp, 16, inst); + modified = true; + }); + if (IsFloat(inst, 32)) { + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); + converted_ids_.insert(inst->result_id()); + modified = true; + } + if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); + return modified; +} + +bool ConvertToHalfPass::ProcessPhi(Instruction* inst) { + // Add float16 converts of any float32 operands and change type + // of phi to float16 equivalent. Operand converts need to be added to + // preceeding blocks. + uint32_t ocnt = 0; + uint32_t* prev_idp; + inst->ForEachInId([&ocnt, &prev_idp, this](uint32_t* idp) { + if (ocnt % 2 == 0) { + prev_idp = idp; + } else { + Instruction* val_inst = get_def_use_mgr()->GetDef(*prev_idp); + if (IsFloat(val_inst, 32)) { + BasicBlock* bp = context()->get_instr_block(*idp); + auto insert_before = bp->tail(); + if (insert_before != bp->begin()) { + --insert_before; + if (insert_before->opcode() != SpvOpSelectionMerge && + insert_before->opcode() != SpvOpLoopMerge) + ++insert_before; + } + GenConvert(prev_idp, 16, &*insert_before); + } + } + ++ocnt; + }); + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); + get_def_use_mgr()->AnalyzeInstUse(inst); + converted_ids_.insert(inst->result_id()); + return true; +} + +bool ConvertToHalfPass::ProcessConvert(Instruction* inst) { + // If float32 and relaxed, change to float16 convert + if (IsFloat(inst, 32) && IsRelaxed(inst->result_id())) { + inst->SetResultType(EquivFloatTypeId(inst->type_id(), 16)); + get_def_use_mgr()->AnalyzeInstUse(inst); + converted_ids_.insert(inst->result_id()); + } + // If operand and result types are the same, change FConvert to CopyObject to + // keep validator happy; simplification and DCE will clean it up + // One way this can happen is if an FConvert generated during this pass + // (likely by ProcessPhi) is later encountered here and its operand has been + // changed to half. + uint32_t val_id = inst->GetSingleWordInOperand(0); + Instruction* val_inst = get_def_use_mgr()->GetDef(val_id); + if (inst->type_id() == val_inst->type_id()) inst->SetOpcode(SpvOpCopyObject); + return true; // modified +} + +bool ConvertToHalfPass::ProcessImageRef(Instruction* inst) { + bool modified = false; + // If image reference, only need to convert dref args back to float32 + if (dref_image_ops_.count(inst->opcode()) != 0) { + uint32_t dref_id = inst->GetSingleWordInOperand(kImageSampleDrefIdInIdx); + if (converted_ids_.count(dref_id) > 0) { + GenConvert(&dref_id, 32, inst); + inst->SetInOperand(kImageSampleDrefIdInIdx, {dref_id}); + get_def_use_mgr()->AnalyzeInstUse(inst); + modified = true; + } + } + return modified; +} + +bool ConvertToHalfPass::ProcessDefault(Instruction* inst) { + bool modified = false; + // If non-relaxed instruction has changed operands, need to convert + // them back to float32 + inst->ForEachInId([&inst, &modified, this](uint32_t* idp) { + if (converted_ids_.count(*idp) == 0) return; + uint32_t old_id = *idp; + GenConvert(idp, 32, inst); + if (*idp != old_id) modified = true; + }); + if (modified) get_def_use_mgr()->AnalyzeInstUse(inst); + return modified; +} + +bool ConvertToHalfPass::GenHalfInst(Instruction* inst) { + bool modified = false; + // Remember id for later deletion of RelaxedPrecision decoration + bool inst_relaxed = IsRelaxed(inst->result_id()); + if (IsArithmetic(inst) && inst_relaxed) + modified = GenHalfArith(inst); + else if (inst->opcode() == SpvOpPhi && inst_relaxed) + modified = ProcessPhi(inst); + else if (inst->opcode() == SpvOpFConvert) + modified = ProcessConvert(inst); + else if (image_ops_.count(inst->opcode()) != 0) + modified = ProcessImageRef(inst); + else + modified = ProcessDefault(inst); + return modified; +} + +bool ConvertToHalfPass::CloseRelaxInst(Instruction* inst) { + if (inst->result_id() == 0) return false; + if (IsRelaxed(inst->result_id())) return false; + if (!IsFloat(inst, 32)) return false; + if (IsDecoratedRelaxed(inst)) { + AddRelaxed(inst->result_id()); + return true; + } + if (closure_ops_.count(inst->opcode()) == 0) return false; + // Can relax if all float operands are relaxed + bool relax = true; + inst->ForEachInId([&relax, this](uint32_t* idp) { + Instruction* op_inst = get_def_use_mgr()->GetDef(*idp); + if (!IsFloat(op_inst, 32)) return; + if (!IsRelaxed(*idp)) relax = false; + }); + if (relax) { + AddRelaxed(inst->result_id()); + return true; + } + // Can relax if all uses are relaxed + relax = true; + get_def_use_mgr()->ForEachUser(inst, [&relax, this](Instruction* uinst) { + if (uinst->result_id() == 0 || !IsFloat(uinst, 32) || + (!IsDecoratedRelaxed(uinst) && !IsRelaxed(uinst->result_id()))) { + relax = false; + return; + } + }); + if (relax) { + AddRelaxed(inst->result_id()); + return true; + } + return false; +} + +bool ConvertToHalfPass::ProcessFunction(Function* func) { + // Do a closure of Relaxed on composite and phi instructions + bool changed = true; + while (changed) { + changed = false; + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&changed, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + changed |= CloseRelaxInst(&*ii); + }); + } + // Do convert of relaxed instructions to half precision + bool modified = false; + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&modified, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + modified |= GenHalfInst(&*ii); + }); + // Replace invalid converts of matrix into equivalent vector extracts, + // converts and finally a composite construct + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&modified, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + modified |= MatConvertCleanup(&*ii); + }); + return modified; +} + +Pass::Status ConvertToHalfPass::ProcessImpl() { + Pass::ProcessFunction pfn = [this](Function* fp) { + return ProcessFunction(fp); + }; + bool modified = context()->ProcessEntryPointCallTree(pfn); + // If modified, make sure module has Float16 capability + if (modified) context()->AddCapability(SpvCapabilityFloat16); + // Remove all RelaxedPrecision decorations from instructions and globals + for (auto c_id : relaxed_ids_set_) RemoveRelaxedDecoration(c_id); + for (auto& val : get_module()->types_values()) { + uint32_t v_id = val.result_id(); + if (v_id != 0) RemoveRelaxedDecoration(v_id); + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Pass::Status ConvertToHalfPass::Process() { + Initialize(); + return ProcessImpl(); +} + +void ConvertToHalfPass::Initialize() { + target_ops_core_ = { + SpvOpVectorExtractDynamic, + SpvOpVectorInsertDynamic, + SpvOpVectorShuffle, + SpvOpCompositeConstruct, + SpvOpCompositeInsert, + SpvOpCompositeExtract, + SpvOpCopyObject, + SpvOpTranspose, + SpvOpConvertSToF, + SpvOpConvertUToF, + // SpvOpFConvert, + // SpvOpQuantizeToF16, + SpvOpFNegate, + SpvOpFAdd, + SpvOpFSub, + SpvOpFMul, + SpvOpFDiv, + SpvOpFMod, + SpvOpVectorTimesScalar, + SpvOpMatrixTimesScalar, + SpvOpVectorTimesMatrix, + SpvOpMatrixTimesVector, + SpvOpMatrixTimesMatrix, + SpvOpOuterProduct, + SpvOpDot, + SpvOpSelect, + SpvOpFOrdEqual, + SpvOpFUnordEqual, + SpvOpFOrdNotEqual, + SpvOpFUnordNotEqual, + SpvOpFOrdLessThan, + SpvOpFUnordLessThan, + SpvOpFOrdGreaterThan, + SpvOpFUnordGreaterThan, + SpvOpFOrdLessThanEqual, + SpvOpFUnordLessThanEqual, + SpvOpFOrdGreaterThanEqual, + SpvOpFUnordGreaterThanEqual, + }; + target_ops_450_ = { + GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, GLSLstd450FAbs, + GLSLstd450FSign, GLSLstd450Floor, GLSLstd450Ceil, GLSLstd450Fract, + GLSLstd450Radians, GLSLstd450Degrees, GLSLstd450Sin, GLSLstd450Cos, + GLSLstd450Tan, GLSLstd450Asin, GLSLstd450Acos, GLSLstd450Atan, + GLSLstd450Sinh, GLSLstd450Cosh, GLSLstd450Tanh, GLSLstd450Asinh, + GLSLstd450Acosh, GLSLstd450Atanh, GLSLstd450Atan2, GLSLstd450Pow, + GLSLstd450Exp, GLSLstd450Log, GLSLstd450Exp2, GLSLstd450Log2, + GLSLstd450Sqrt, GLSLstd450InverseSqrt, GLSLstd450Determinant, + GLSLstd450MatrixInverse, + // TODO(greg-lunarg): GLSLstd450ModfStruct, + GLSLstd450FMin, GLSLstd450FMax, GLSLstd450FClamp, GLSLstd450FMix, + GLSLstd450Step, GLSLstd450SmoothStep, GLSLstd450Fma, + // TODO(greg-lunarg): GLSLstd450FrexpStruct, + GLSLstd450Ldexp, GLSLstd450Length, GLSLstd450Distance, GLSLstd450Cross, + GLSLstd450Normalize, GLSLstd450FaceForward, GLSLstd450Reflect, + GLSLstd450Refract, GLSLstd450NMin, GLSLstd450NMax, GLSLstd450NClamp}; + image_ops_ = {SpvOpImageSampleImplicitLod, + SpvOpImageSampleExplicitLod, + SpvOpImageSampleDrefImplicitLod, + SpvOpImageSampleDrefExplicitLod, + SpvOpImageSampleProjImplicitLod, + SpvOpImageSampleProjExplicitLod, + SpvOpImageSampleProjDrefImplicitLod, + SpvOpImageSampleProjDrefExplicitLod, + SpvOpImageFetch, + SpvOpImageGather, + SpvOpImageDrefGather, + SpvOpImageRead, + SpvOpImageSparseSampleImplicitLod, + SpvOpImageSparseSampleExplicitLod, + SpvOpImageSparseSampleDrefImplicitLod, + SpvOpImageSparseSampleDrefExplicitLod, + SpvOpImageSparseSampleProjImplicitLod, + SpvOpImageSparseSampleProjExplicitLod, + SpvOpImageSparseSampleProjDrefImplicitLod, + SpvOpImageSparseSampleProjDrefExplicitLod, + SpvOpImageSparseFetch, + SpvOpImageSparseGather, + SpvOpImageSparseDrefGather, + SpvOpImageSparseTexelsResident, + SpvOpImageSparseRead}; + dref_image_ops_ = { + SpvOpImageSampleDrefImplicitLod, + SpvOpImageSampleDrefExplicitLod, + SpvOpImageSampleProjDrefImplicitLod, + SpvOpImageSampleProjDrefExplicitLod, + SpvOpImageDrefGather, + SpvOpImageSparseSampleDrefImplicitLod, + SpvOpImageSparseSampleDrefExplicitLod, + SpvOpImageSparseSampleProjDrefImplicitLod, + SpvOpImageSparseSampleProjDrefExplicitLod, + SpvOpImageSparseDrefGather, + }; + closure_ops_ = { + SpvOpVectorExtractDynamic, + SpvOpVectorInsertDynamic, + SpvOpVectorShuffle, + SpvOpCompositeConstruct, + SpvOpCompositeInsert, + SpvOpCompositeExtract, + SpvOpCopyObject, + SpvOpTranspose, + SpvOpPhi, + }; + relaxed_ids_set_.clear(); + converted_ids_.clear(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/convert_to_half_pass.h b/third_party/spirv-tools/source/opt/convert_to_half_pass.h new file mode 100644 index 0000000..143aebf --- /dev/null +++ b/third_party/spirv-tools/source/opt/convert_to_half_pass.h @@ -0,0 +1,148 @@ +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_ +#define LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_ + +#include "source/opt/ir_builder.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class ConvertToHalfPass : public Pass { + public: + ConvertToHalfPass() : Pass() {} + + ~ConvertToHalfPass() override = default; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + } + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "convert-to-half-pass"; } + + private: + // Return true if |inst| is an arithmetic, composite or phi op that can be + // of type float16 + bool IsArithmetic(Instruction* inst); + + // Return true if |inst| returns scalar, vector or matrix type with base + // float and |width| + bool IsFloat(Instruction* inst, uint32_t width); + + // Return true if |inst| is decorated with RelaxedPrecision + bool IsDecoratedRelaxed(Instruction* inst); + + // Return true if |id| has been added to the relaxed id set + bool IsRelaxed(uint32_t id); + + // Add |id| to the relaxed id set + void AddRelaxed(uint32_t id); + + // Return type id for float with |width| + analysis::Type* FloatScalarType(uint32_t width); + + // Return type id for vector of length |vlen| of float of |width| + analysis::Type* FloatVectorType(uint32_t v_len, uint32_t width); + + // Return type id for matrix of |v_cnt| vectors of length identical to + // |vty_id| of float of |width| + analysis::Type* FloatMatrixType(uint32_t v_cnt, uint32_t vty_id, + uint32_t width); + + // Return equivalent to float type |ty_id| with |width| + uint32_t EquivFloatTypeId(uint32_t ty_id, uint32_t width); + + // Append instructions to builder to convert value |*val_idp| to type + // |ty_id| but with |width|. Set |*val_idp| to the new id. + void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst); + + // Remove RelaxedPrecision decoration of |id|. + void RemoveRelaxedDecoration(uint32_t id); + + // Add |inst| to relaxed instruction set if warranted. Specifically, if + // it is float32 and either decorated relaxed or a composite or phi + // instruction where all operands are relaxed or all uses are relaxed. + bool CloseRelaxInst(Instruction* inst); + + // If |inst| is an arithmetic, phi, extract or convert instruction of float32 + // base type and decorated with RelaxedPrecision, change it to the equivalent + // float16 based type instruction. Specifically, insert instructions to + // convert all operands to float16 (if needed) and change its type to the + // equivalent float16 type. Otherwise, insert instructions to convert its + // operands back to their original types, if needed. + bool GenHalfInst(Instruction* inst); + + // Gen code for relaxed arithmetic |inst| + bool GenHalfArith(Instruction* inst); + + // Gen code for relaxed phi |inst| + bool ProcessPhi(Instruction* inst); + + // Gen code for relaxed convert |inst| + bool ProcessConvert(Instruction* inst); + + // Gen code for image reference |inst| + bool ProcessImageRef(Instruction* inst); + + // Process default non-relaxed |inst| + bool ProcessDefault(Instruction* inst); + + // If |inst| is an FConvert of a matrix type, decompose it to a series + // of vector extracts, converts and inserts into an Undef. These are + // generated by GenHalfInst because they are easier to manipulate, but are + // invalid so we need to clean them up. + bool MatConvertCleanup(Instruction* inst); + + // Call GenHalfInst on every instruction in |func|. + // If code is generated for an instruction, replace the instruction + // with the new instructions that are generated. + bool ProcessFunction(Function* func); + + Pass::Status ProcessImpl(); + + // Initialize state for converting to half + void Initialize(); + + // Set of core operations to be processed + std::unordered_set target_ops_core_; + + // Set of 450 extension operations to be processed + std::unordered_set target_ops_450_; + + // Set of sample operations + std::unordered_set image_ops_; + + // Set of dref sample operations + std::unordered_set dref_image_ops_; + + // Set of dref sample operations + std::unordered_set closure_ops_; + + // Set of ids of all relaxed instructions + std::unordered_set relaxed_ids_set_; + + // Ids of all converted instructions + std::unordered_set converted_ids_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_CONVERT_TO_HALF_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/copy_prop_arrays.cpp b/third_party/spirv-tools/source/opt/copy_prop_arrays.cpp new file mode 100644 index 0000000..67a97b6 --- /dev/null +++ b/third_party/spirv-tools/source/opt/copy_prop_arrays.cpp @@ -0,0 +1,859 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/copy_prop_arrays.h" + +#include + +#include "source/opt/ir_builder.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kLoadPointerInOperand = 0; +const uint32_t kStorePointerInOperand = 0; +const uint32_t kStoreObjectInOperand = 1; +const uint32_t kCompositeExtractObjectInOperand = 0; +const uint32_t kTypePointerStorageClassInIdx = 0; +const uint32_t kTypePointerPointeeInIdx = 1; + +bool IsOpenCL100DebugDeclareOrValue(Instruction* di) { + auto dbg_opcode = di->GetOpenCL100DebugOpcode(); + return dbg_opcode == OpenCLDebugInfo100DebugDeclare || + dbg_opcode == OpenCLDebugInfo100DebugValue; +} + +} // namespace + +Pass::Status CopyPropagateArrays::Process() { + bool modified = false; + for (Function& function : *get_module()) { + BasicBlock* entry_bb = &*function.begin(); + + for (auto var_inst = entry_bb->begin(); var_inst->opcode() == SpvOpVariable; + ++var_inst) { + if (!IsPointerToArrayType(var_inst->type_id())) { + continue; + } + + // Find the only store to the entire memory location, if it exists. + Instruction* store_inst = FindStoreInstruction(&*var_inst); + + if (!store_inst) { + continue; + } + + std::unique_ptr source_object = + FindSourceObjectIfPossible(&*var_inst, store_inst); + + if (source_object != nullptr) { + if (CanUpdateUses(&*var_inst, source_object->GetPointerTypeId(this))) { + modified = true; + PropagateObject(&*var_inst, source_object.get(), store_inst); + } + } + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +std::unique_ptr +CopyPropagateArrays::FindSourceObjectIfPossible(Instruction* var_inst, + Instruction* store_inst) { + assert(var_inst->opcode() == SpvOpVariable && "Expecting a variable."); + + // Check that the variable is a composite object where |store_inst| + // dominates all of its loads. + if (!store_inst) { + return nullptr; + } + + // Look at the loads to ensure they are dominated by the store. + if (!HasValidReferencesOnly(var_inst, store_inst)) { + return nullptr; + } + + // If so, look at the store to see if it is the copy of an object. + std::unique_ptr source = GetSourceObjectIfAny( + store_inst->GetSingleWordInOperand(kStoreObjectInOperand)); + + if (!source) { + return nullptr; + } + + // Ensure that |source| does not change between the point at which it is + // loaded, and the position in which |var_inst| is loaded. + // + // For now we will go with the easy to implement approach, and check that the + // entire variable (not just the specific component) is never written to. + + if (!HasNoStores(source->GetVariable())) { + return nullptr; + } + return source; +} + +Instruction* CopyPropagateArrays::FindStoreInstruction( + const Instruction* var_inst) const { + Instruction* store_inst = nullptr; + get_def_use_mgr()->WhileEachUser( + var_inst, [&store_inst, var_inst](Instruction* use) { + if (use->opcode() == SpvOpStore && + use->GetSingleWordInOperand(kStorePointerInOperand) == + var_inst->result_id()) { + if (store_inst == nullptr) { + store_inst = use; + } else { + store_inst = nullptr; + return false; + } + } + return true; + }); + return store_inst; +} + +void CopyPropagateArrays::PropagateObject(Instruction* var_inst, + MemoryObject* source, + Instruction* insertion_point) { + assert(var_inst->opcode() == SpvOpVariable && + "This function propagates variables."); + + Instruction* new_access_chain = BuildNewAccessChain(insertion_point, source); + context()->KillNamesAndDecorates(var_inst); + UpdateUses(var_inst, new_access_chain); +} + +Instruction* CopyPropagateArrays::BuildNewAccessChain( + Instruction* insertion_point, + CopyPropagateArrays::MemoryObject* source) const { + InstructionBuilder builder( + context(), insertion_point, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + if (source->AccessChain().size() == 0) { + return source->GetVariable(); + } + + return builder.AddAccessChain(source->GetPointerTypeId(this), + source->GetVariable()->result_id(), + source->AccessChain()); +} + +bool CopyPropagateArrays::HasNoStores(Instruction* ptr_inst) { + return get_def_use_mgr()->WhileEachUser(ptr_inst, [this](Instruction* use) { + if (use->opcode() == SpvOpLoad) { + return true; + } else if (use->opcode() == SpvOpAccessChain) { + return HasNoStores(use); + } else if (use->IsDecoration() || use->opcode() == SpvOpName) { + return true; + } else if (use->opcode() == SpvOpStore) { + return false; + } else if (use->opcode() == SpvOpImageTexelPointer) { + return true; + } + // Some other instruction. Be conservative. + return false; + }); +} + +bool CopyPropagateArrays::HasValidReferencesOnly(Instruction* ptr_inst, + Instruction* store_inst) { + BasicBlock* store_block = context()->get_instr_block(store_inst); + DominatorAnalysis* dominator_analysis = + context()->GetDominatorAnalysis(store_block->GetParent()); + + return get_def_use_mgr()->WhileEachUser( + ptr_inst, + [this, store_inst, dominator_analysis, ptr_inst](Instruction* use) { + if (use->opcode() == SpvOpLoad || + use->opcode() == SpvOpImageTexelPointer) { + // TODO: If there are many load in the same BB as |store_inst| the + // time to do the multiple traverses can add up. Consider collecting + // those loads and doing a single traversal. + return dominator_analysis->Dominates(store_inst, use); + } else if (use->opcode() == SpvOpAccessChain) { + return HasValidReferencesOnly(use, store_inst); + } else if (use->IsDecoration() || use->opcode() == SpvOpName) { + return true; + } else if (use->opcode() == SpvOpStore) { + // If we are storing to part of the object it is not an candidate. + return ptr_inst->opcode() == SpvOpVariable && + store_inst->GetSingleWordInOperand(kStorePointerInOperand) == + ptr_inst->result_id(); + } else if (IsOpenCL100DebugDeclareOrValue(use)) { + return true; + } + // Some other instruction. Be conservative. + return false; + }); +} + +std::unique_ptr +CopyPropagateArrays::GetSourceObjectIfAny(uint32_t result) { + Instruction* result_inst = context()->get_def_use_mgr()->GetDef(result); + + switch (result_inst->opcode()) { + case SpvOpLoad: + return BuildMemoryObjectFromLoad(result_inst); + case SpvOpCompositeExtract: + return BuildMemoryObjectFromExtract(result_inst); + case SpvOpCompositeConstruct: + return BuildMemoryObjectFromCompositeConstruct(result_inst); + case SpvOpCopyObject: + return GetSourceObjectIfAny(result_inst->GetSingleWordInOperand(0)); + case SpvOpCompositeInsert: + return BuildMemoryObjectFromInsert(result_inst); + default: + return nullptr; + } +} + +std::unique_ptr +CopyPropagateArrays::BuildMemoryObjectFromLoad(Instruction* load_inst) { + std::vector components_in_reverse; + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + Instruction* current_inst = def_use_mgr->GetDef( + load_inst->GetSingleWordInOperand(kLoadPointerInOperand)); + + // Build the access chain for the memory object by collecting the indices used + // in the OpAccessChain instructions. If we find a variable index, then + // return |nullptr| because we cannot know for sure which memory location is + // used. + // + // It is built in reverse order because the different |OpAccessChain| + // instructions are visited in reverse order from which they are applied. + while (current_inst->opcode() == SpvOpAccessChain) { + for (uint32_t i = current_inst->NumInOperands() - 1; i >= 1; --i) { + uint32_t element_index_id = current_inst->GetSingleWordInOperand(i); + components_in_reverse.push_back(element_index_id); + } + current_inst = def_use_mgr->GetDef(current_inst->GetSingleWordInOperand(0)); + } + + // If the address in the load is not constructed from an |OpVariable| + // instruction followed by a series of |OpAccessChain| instructions, then + // return |nullptr| because we cannot identify the owner or access chain + // exactly. + if (current_inst->opcode() != SpvOpVariable) { + return nullptr; + } + + // Build the memory object. Use |rbegin| and |rend| to put the access chain + // back in the correct order. + return std::unique_ptr( + new MemoryObject(current_inst, components_in_reverse.rbegin(), + components_in_reverse.rend())); +} + +std::unique_ptr +CopyPropagateArrays::BuildMemoryObjectFromExtract(Instruction* extract_inst) { + assert(extract_inst->opcode() == SpvOpCompositeExtract && + "Expecting an OpCompositeExtract instruction."); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + std::unique_ptr result = GetSourceObjectIfAny( + extract_inst->GetSingleWordInOperand(kCompositeExtractObjectInOperand)); + + if (result) { + analysis::Integer int_type(32, false); + const analysis::Type* uint32_type = + context()->get_type_mgr()->GetRegisteredType(&int_type); + + std::vector components; + // Convert the indices in the extract instruction to a series of ids that + // can be used by the |OpAccessChain| instruction. + for (uint32_t i = 1; i < extract_inst->NumInOperands(); ++i) { + uint32_t index = extract_inst->GetSingleWordInOperand(i); + const analysis::Constant* index_const = + const_mgr->GetConstant(uint32_type, {index}); + components.push_back( + const_mgr->GetDefiningInstruction(index_const)->result_id()); + } + result->GetMember(components); + return result; + } + return nullptr; +} + +std::unique_ptr +CopyPropagateArrays::BuildMemoryObjectFromCompositeConstruct( + Instruction* conststruct_inst) { + assert(conststruct_inst->opcode() == SpvOpCompositeConstruct && + "Expecting an OpCompositeConstruct instruction."); + + // If every operand in the instruction are part of the same memory object, and + // are being combined in the same order, then the result is the same as the + // parent. + + std::unique_ptr memory_object = + GetSourceObjectIfAny(conststruct_inst->GetSingleWordInOperand(0)); + + if (!memory_object) { + return nullptr; + } + + if (!memory_object->IsMember()) { + return nullptr; + } + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Constant* last_access = + const_mgr->FindDeclaredConstant(memory_object->AccessChain().back()); + if (!last_access || !last_access->type()->AsInteger()) { + return nullptr; + } + + if (last_access->GetU32() != 0) { + return nullptr; + } + + memory_object->GetParent(); + + if (memory_object->GetNumberOfMembers() != + conststruct_inst->NumInOperands()) { + return nullptr; + } + + for (uint32_t i = 1; i < conststruct_inst->NumInOperands(); ++i) { + std::unique_ptr member_object = + GetSourceObjectIfAny(conststruct_inst->GetSingleWordInOperand(i)); + + if (!member_object) { + return nullptr; + } + + if (!member_object->IsMember()) { + return nullptr; + } + + if (!memory_object->Contains(member_object.get())) { + return nullptr; + } + + last_access = + const_mgr->FindDeclaredConstant(member_object->AccessChain().back()); + if (!last_access || !last_access->type()->AsInteger()) { + return nullptr; + } + + if (last_access->GetU32() != i) { + return nullptr; + } + } + return memory_object; +} + +std::unique_ptr +CopyPropagateArrays::BuildMemoryObjectFromInsert(Instruction* insert_inst) { + assert(insert_inst->opcode() == SpvOpCompositeInsert && + "Expecting an OpCompositeInsert instruction."); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Type* result_type = type_mgr->GetType(insert_inst->type_id()); + + uint32_t number_of_elements = 0; + if (const analysis::Struct* struct_type = result_type->AsStruct()) { + number_of_elements = + static_cast(struct_type->element_types().size()); + } else if (const analysis::Array* array_type = result_type->AsArray()) { + const analysis::Constant* length_const = + const_mgr->FindDeclaredConstant(array_type->LengthId()); + number_of_elements = length_const->GetU32(); + } else if (const analysis::Vector* vector_type = result_type->AsVector()) { + number_of_elements = vector_type->element_count(); + } else if (const analysis::Matrix* matrix_type = result_type->AsMatrix()) { + number_of_elements = matrix_type->element_count(); + } + + if (number_of_elements == 0) { + return nullptr; + } + + if (insert_inst->NumInOperands() != 3) { + return nullptr; + } + + if (insert_inst->GetSingleWordInOperand(2) != number_of_elements - 1) { + return nullptr; + } + + std::unique_ptr memory_object = + GetSourceObjectIfAny(insert_inst->GetSingleWordInOperand(0)); + + if (!memory_object) { + return nullptr; + } + + if (!memory_object->IsMember()) { + return nullptr; + } + + const analysis::Constant* last_access = + const_mgr->FindDeclaredConstant(memory_object->AccessChain().back()); + if (!last_access || !last_access->type()->AsInteger()) { + return nullptr; + } + + if (last_access->GetU32() != number_of_elements - 1) { + return nullptr; + } + + memory_object->GetParent(); + + Instruction* current_insert = + def_use_mgr->GetDef(insert_inst->GetSingleWordInOperand(1)); + for (uint32_t i = number_of_elements - 1; i > 0; --i) { + if (current_insert->opcode() != SpvOpCompositeInsert) { + return nullptr; + } + + if (current_insert->NumInOperands() != 3) { + return nullptr; + } + + if (current_insert->GetSingleWordInOperand(2) != i - 1) { + return nullptr; + } + + std::unique_ptr current_memory_object = + GetSourceObjectIfAny(current_insert->GetSingleWordInOperand(0)); + + if (!current_memory_object) { + return nullptr; + } + + if (!current_memory_object->IsMember()) { + return nullptr; + } + + if (memory_object->AccessChain().size() + 1 != + current_memory_object->AccessChain().size()) { + return nullptr; + } + + if (!memory_object->Contains(current_memory_object.get())) { + return nullptr; + } + + const analysis::Constant* current_last_access = + const_mgr->FindDeclaredConstant( + current_memory_object->AccessChain().back()); + if (!current_last_access || !current_last_access->type()->AsInteger()) { + return nullptr; + } + + if (current_last_access->GetU32() != i - 1) { + return nullptr; + } + current_insert = + def_use_mgr->GetDef(current_insert->GetSingleWordInOperand(1)); + } + + return memory_object; +} + +bool CopyPropagateArrays::IsPointerToArrayType(uint32_t type_id) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Pointer* pointer_type = type_mgr->GetType(type_id)->AsPointer(); + if (pointer_type) { + return pointer_type->pointee_type()->kind() == analysis::Type::kArray || + pointer_type->pointee_type()->kind() == analysis::Type::kImage; + } + return false; +} + +bool CopyPropagateArrays::CanUpdateUses(Instruction* original_ptr_inst, + uint32_t type_id) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + analysis::Type* type = type_mgr->GetType(type_id); + if (type->AsRuntimeArray()) { + return false; + } + + if (!type->AsStruct() && !type->AsArray() && !type->AsPointer()) { + // If the type is not an aggregate, then the desired type must be the + // same as the current type. No work to do, and we can do that. + return true; + } + + return def_use_mgr->WhileEachUse(original_ptr_inst, [this, type_mgr, + const_mgr, + type](Instruction* use, + uint32_t) { + if (IsOpenCL100DebugDeclareOrValue(use)) return true; + + switch (use->opcode()) { + case SpvOpLoad: { + analysis::Pointer* pointer_type = type->AsPointer(); + uint32_t new_type_id = type_mgr->GetId(pointer_type->pointee_type()); + + if (new_type_id != use->type_id()) { + return CanUpdateUses(use, new_type_id); + } + return true; + } + case SpvOpAccessChain: { + analysis::Pointer* pointer_type = type->AsPointer(); + const analysis::Type* pointee_type = pointer_type->pointee_type(); + + std::vector access_chain; + for (uint32_t i = 1; i < use->NumInOperands(); ++i) { + const analysis::Constant* index_const = + const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i)); + if (index_const) { + access_chain.push_back(index_const->GetU32()); + } else { + // Variable index means the type is a type where every element + // is the same type. Use element 0 to get the type. + access_chain.push_back(0); + } + } + + const analysis::Type* new_pointee_type = + type_mgr->GetMemberType(pointee_type, access_chain); + analysis::Pointer pointerTy(new_pointee_type, + pointer_type->storage_class()); + uint32_t new_pointer_type_id = + context()->get_type_mgr()->GetTypeInstruction(&pointerTy); + if (new_pointer_type_id == 0) { + return false; + } + + if (new_pointer_type_id != use->type_id()) { + return CanUpdateUses(use, new_pointer_type_id); + } + return true; + } + case SpvOpCompositeExtract: { + std::vector access_chain; + for (uint32_t i = 1; i < use->NumInOperands(); ++i) { + access_chain.push_back(use->GetSingleWordInOperand(i)); + } + + const analysis::Type* new_type = + type_mgr->GetMemberType(type, access_chain); + uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type); + if (new_type_id == 0) { + return false; + } + + if (new_type_id != use->type_id()) { + return CanUpdateUses(use, new_type_id); + } + return true; + } + case SpvOpStore: + // If needed, we can create an element-by-element copy to change the + // type of the value being stored. This way we can always handled + // stores. + return true; + case SpvOpImageTexelPointer: + case SpvOpName: + return true; + default: + return use->IsDecoration(); + } + }); +} + +void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst, + Instruction* new_ptr_inst) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + std::vector > uses; + def_use_mgr->ForEachUse(original_ptr_inst, + [&uses](Instruction* use, uint32_t index) { + uses.push_back({use, index}); + }); + + for (auto pair : uses) { + Instruction* use = pair.first; + uint32_t index = pair.second; + + if (use->IsOpenCL100DebugInstr()) { + switch (use->GetOpenCL100DebugOpcode()) { + case OpenCLDebugInfo100DebugDeclare: { + if (new_ptr_inst->opcode() == SpvOpVariable || + new_ptr_inst->opcode() == SpvOpFunctionParameter) { + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + } else { + // Based on the spec, we cannot use a pointer other than OpVariable + // or OpFunctionParameter for DebugDeclare. We have to use + // DebugValue with Deref. + + context()->ForgetUses(use); + + // Change DebugDeclare to DebugValue. + use->SetOperand( + index - 2, + {static_cast(OpenCLDebugInfo100DebugValue)}); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + // Add Deref operation. + Instruction* dbg_expr = + def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1)); + auto* deref_expr_instr = + context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr); + use->SetOperand(index + 1, {deref_expr_instr->result_id()}); + + context()->AnalyzeUses(deref_expr_instr); + context()->AnalyzeUses(use); + } + break; + } + case OpenCLDebugInfo100DebugValue: + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + break; + default: + assert(false && "Don't know how to rewrite instruction"); + break; + } + continue; + } + + switch (use->opcode()) { + case SpvOpLoad: { + // Replace the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + // Update the type. + Instruction* pointer_type_inst = + def_use_mgr->GetDef(new_ptr_inst->type_id()); + uint32_t new_type_id = + pointer_type_inst->GetSingleWordInOperand(kTypePointerPointeeInIdx); + if (new_type_id != use->type_id()) { + use->SetResultType(new_type_id); + context()->AnalyzeUses(use); + UpdateUses(use, use); + } else { + context()->AnalyzeUses(use); + } + } break; + case SpvOpAccessChain: { + // Update the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + // Convert the ids on the OpAccessChain to indices that can be used to + // get the specific member. + std::vector access_chain; + for (uint32_t i = 1; i < use->NumInOperands(); ++i) { + const analysis::Constant* index_const = + const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i)); + if (index_const) { + access_chain.push_back(index_const->GetU32()); + } else { + // Variable index means the type is an type where every element + // is the same type. Use element 0 to get the type. + access_chain.push_back(0); + } + } + + Instruction* pointer_type_inst = + get_def_use_mgr()->GetDef(new_ptr_inst->type_id()); + + uint32_t new_pointee_type_id = GetMemberTypeId( + pointer_type_inst->GetSingleWordInOperand(kTypePointerPointeeInIdx), + access_chain); + + SpvStorageClass storage_class = static_cast( + pointer_type_inst->GetSingleWordInOperand( + kTypePointerStorageClassInIdx)); + + uint32_t new_pointer_type_id = + type_mgr->FindPointerToType(new_pointee_type_id, storage_class); + + if (new_pointer_type_id != use->type_id()) { + use->SetResultType(new_pointer_type_id); + context()->AnalyzeUses(use); + UpdateUses(use, use); + } else { + context()->AnalyzeUses(use); + } + } break; + case SpvOpCompositeExtract: { + // Update the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + + uint32_t new_type_id = new_ptr_inst->type_id(); + std::vector access_chain; + for (uint32_t i = 1; i < use->NumInOperands(); ++i) { + access_chain.push_back(use->GetSingleWordInOperand(i)); + } + + new_type_id = GetMemberTypeId(new_type_id, access_chain); + + if (new_type_id != use->type_id()) { + use->SetResultType(new_type_id); + context()->AnalyzeUses(use); + UpdateUses(use, use); + } else { + context()->AnalyzeUses(use); + } + } break; + case SpvOpStore: + // If the use is the pointer, then it is the single store to that + // variable. We do not want to replace it. Instead, it will become + // dead after all of the loads are removed, and ADCE will get rid of it. + // + // If the use is the object being stored, we will create a copy of the + // object turning it into the correct type. The copy is done by + // decomposing the object into the base type, which must be the same, + // and then rebuilding them. + if (index == 1) { + Instruction* target_pointer = def_use_mgr->GetDef( + use->GetSingleWordInOperand(kStorePointerInOperand)); + Instruction* pointer_type = + def_use_mgr->GetDef(target_pointer->type_id()); + uint32_t pointee_type_id = + pointer_type->GetSingleWordInOperand(kTypePointerPointeeInIdx); + uint32_t copy = GenerateCopy(original_ptr_inst, pointee_type_id, use); + + context()->ForgetUses(use); + use->SetInOperand(index, {copy}); + context()->AnalyzeUses(use); + } + break; + case SpvOpImageTexelPointer: + // We treat an OpImageTexelPointer as a load. The result type should + // always have the Image storage class, and should not need to be + // updated. + + // Replace the actual use. + context()->ForgetUses(use); + use->SetOperand(index, {new_ptr_inst->result_id()}); + context()->AnalyzeUses(use); + break; + default: + assert(false && "Don't know how to rewrite instruction"); + break; + } + } +} + +uint32_t CopyPropagateArrays::GetMemberTypeId( + uint32_t id, const std::vector& access_chain) const { + for (uint32_t element_index : access_chain) { + Instruction* type_inst = get_def_use_mgr()->GetDef(id); + switch (type_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeMatrix: + case SpvOpTypeVector: + id = type_inst->GetSingleWordInOperand(0); + break; + case SpvOpTypeStruct: + id = type_inst->GetSingleWordInOperand(element_index); + break; + default: + break; + } + assert(id != 0 && + "Tried to extract from an object where it cannot be done."); + } + return id; +} + +void CopyPropagateArrays::MemoryObject::GetMember( + const std::vector& access_chain) { + access_chain_.insert(access_chain_.end(), access_chain.begin(), + access_chain.end()); +} + +uint32_t CopyPropagateArrays::MemoryObject::GetNumberOfMembers() { + IRContext* context = variable_inst_->context(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + const analysis::Type* type = type_mgr->GetType(variable_inst_->type_id()); + type = type->AsPointer()->pointee_type(); + + std::vector access_indices = GetAccessIds(); + type = type_mgr->GetMemberType(type, access_indices); + + if (const analysis::Struct* struct_type = type->AsStruct()) { + return static_cast(struct_type->element_types().size()); + } else if (const analysis::Array* array_type = type->AsArray()) { + const analysis::Constant* length_const = + context->get_constant_mgr()->FindDeclaredConstant( + array_type->LengthId()); + assert(length_const->type()->AsInteger()); + return length_const->GetU32(); + } else if (const analysis::Vector* vector_type = type->AsVector()) { + return vector_type->element_count(); + } else if (const analysis::Matrix* matrix_type = type->AsMatrix()) { + return matrix_type->element_count(); + } else { + return 0; + } +} + +template +CopyPropagateArrays::MemoryObject::MemoryObject(Instruction* var_inst, + iterator begin, iterator end) + : variable_inst_(var_inst), access_chain_(begin, end) {} + +std::vector CopyPropagateArrays::MemoryObject::GetAccessIds() const { + analysis::ConstantManager* const_mgr = + variable_inst_->context()->get_constant_mgr(); + + std::vector access_indices; + for (uint32_t id : AccessChain()) { + const analysis::Constant* element_index_const = + const_mgr->FindDeclaredConstant(id); + if (!element_index_const) { + access_indices.push_back(0); + } else { + access_indices.push_back(element_index_const->GetU32()); + } + } + return access_indices; +} + +bool CopyPropagateArrays::MemoryObject::Contains( + CopyPropagateArrays::MemoryObject* other) { + if (this->GetVariable() != other->GetVariable()) { + return false; + } + + if (AccessChain().size() > other->AccessChain().size()) { + return false; + } + + for (uint32_t i = 0; i < AccessChain().size(); i++) { + if (AccessChain()[i] != other->AccessChain()[i]) { + return false; + } + } + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/copy_prop_arrays.h b/third_party/spirv-tools/source/opt/copy_prop_arrays.h new file mode 100644 index 0000000..f4314a7 --- /dev/null +++ b/third_party/spirv-tools/source/opt/copy_prop_arrays.h @@ -0,0 +1,235 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_COPY_PROP_ARRAYS_H_ +#define SOURCE_OPT_COPY_PROP_ARRAYS_H_ + +#include +#include + +#include "source/opt/mem_pass.h" + +namespace spvtools { +namespace opt { + +// This pass implements a simple array copy propagation. It does not do a full +// array data flow. It looks for simple cases that meet the following +// conditions: +// +// 1) The source must never be stored to. +// 2) The target must be stored to exactly once. +// 3) The store to the target must be a store to the entire array, and be a +// copy of the entire source. +// 4) All loads of the target must be dominated by the store. +// +// The hard part is keeping all of the types correct. We do not want to +// have to do too large a search to update everything, which may not be +// possible, do we give up if we see any instruction that might be hard to +// update. + +class CopyPropagateArrays : public MemPass { + public: + const char* name() const override { return "copy-propagate-arrays"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisCFG | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisDecorations | + IRContext::kAnalysisDominatorAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // The class used to identify a particular memory object. This memory object + // will be owned by a particular variable, meaning that the memory is part of + // that variable. It could be the entire variable or a member of the + // variable. + class MemoryObject { + public: + // Construction a memory object that is owned by |var_inst|. The iterator + // |begin| and |end| traverse a container of integers that identify which + // member of |var_inst| this memory object will represent. These integers + // are interpreted the same way they would be in an |OpAccessChain| + // instruction. + template + MemoryObject(Instruction* var_inst, iterator begin, iterator end); + + // Change |this| to now point to the member identified by |access_chain| + // (starting from the current member). The elements in |access_chain| are + // interpreted the same as the indices in the |OpAccessChain| + // instruction. + void GetMember(const std::vector& access_chain); + + // Change |this| to now represent the first enclosing object to which it + // belongs. (Remove the last element off the access_chain). It is invalid + // to call this function if |this| does not represent a member of its owner. + void GetParent() { + assert(IsMember()); + access_chain_.pop_back(); + } + + // Returns true if |this| represents a member of its owner, and not the + // entire variable. + bool IsMember() const { return !access_chain_.empty(); } + + // Returns the number of members in the object represented by |this|. If + // |this| does not represent a composite type, the return value will be 0. + uint32_t GetNumberOfMembers(); + + // Returns the owning variable that the memory object is contained in. + Instruction* GetVariable() const { return variable_inst_; } + + // Returns a vector of integers that can be used to access the specific + // member that |this| represents starting from the owning variable. These + // values are to be interpreted the same way the indices are in an + // |OpAccessChain| instruction. + const std::vector& AccessChain() const { return access_chain_; } + + // Returns the type id of the pointer type that can be used to point to this + // memory object. + uint32_t GetPointerTypeId(const CopyPropagateArrays* pass) const { + analysis::DefUseManager* def_use_mgr = + GetVariable()->context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = + GetVariable()->context()->get_type_mgr(); + + Instruction* var_pointer_inst = + def_use_mgr->GetDef(GetVariable()->type_id()); + + uint32_t member_type_id = pass->GetMemberTypeId( + var_pointer_inst->GetSingleWordInOperand(1), GetAccessIds()); + + uint32_t member_pointer_type_id = type_mgr->FindPointerToType( + member_type_id, static_cast( + var_pointer_inst->GetSingleWordInOperand(0))); + return member_pointer_type_id; + } + + // Returns the storage class of the memory object. + SpvStorageClass GetStorageClass() const { + analysis::TypeManager* type_mgr = + GetVariable()->context()->get_type_mgr(); + const analysis::Pointer* pointer_type = + type_mgr->GetType(GetVariable()->type_id())->AsPointer(); + return pointer_type->storage_class(); + } + + // Returns true if |other| represents memory that is contains inside of the + // memory represented by |this|. + bool Contains(MemoryObject* other); + + private: + // The variable that owns this memory object. + Instruction* variable_inst_; + + // The access chain to reach the particular member the memory object + // represents. It should be interpreted the same way the indices in an + // |OpAccessChain| are interpreted. + std::vector access_chain_; + std::vector GetAccessIds() const; + }; + + // Returns the memory object being stored to |var_inst| in the store + // instruction |store_inst|, if one exists, that can be used in place of + // |var_inst| in all of the loads of |var_inst|. This code is conservative + // and only identifies very simple cases. If no such memory object can be + // found, the return value is |nullptr|. + std::unique_ptr FindSourceObjectIfPossible( + Instruction* var_inst, Instruction* store_inst); + + // Replaces all loads of |var_inst| with a load from |source| instead. + // |insertion_pos| is a position where it is possible to construct the + // address of |source| and also dominates all of the loads of |var_inst|. + void PropagateObject(Instruction* var_inst, MemoryObject* source, + Instruction* insertion_pos); + + // Returns true if all of the references to |ptr_inst| can be rewritten and + // are dominated by |store_inst|. + bool HasValidReferencesOnly(Instruction* ptr_inst, Instruction* store_inst); + + // Returns a memory object that at one time was equivalent to the value in + // |result|. If no such memory object exists, the return value is |nullptr|. + std::unique_ptr GetSourceObjectIfAny(uint32_t result); + + // Returns the memory object that is loaded by |load_inst|. If a memory + // object cannot be identified, the return value is |nullptr|. The opcode of + // |load_inst| must be |OpLoad|. + std::unique_ptr BuildMemoryObjectFromLoad( + Instruction* load_inst); + + // Returns the memory object that at some point was equivalent to the result + // of |extract_inst|. If a memory object cannot be identified, the return + // value is |nullptr|. The opcode of |extract_inst| must be + // |OpCompositeExtract|. + std::unique_ptr BuildMemoryObjectFromExtract( + Instruction* extract_inst); + + // Returns the memory object that at some point was equivalent to the result + // of |construct_inst|. If a memory object cannot be identified, the return + // value is |nullptr|. The opcode of |constuct_inst| must be + // |OpCompositeConstruct|. + std::unique_ptr BuildMemoryObjectFromCompositeConstruct( + Instruction* conststruct_inst); + + // Returns the memory object that at some point was equivalent to the result + // of |insert_inst|. If a memory object cannot be identified, the return + // value is |nullptr\. The opcode of |insert_inst| must be + // |OpCompositeInsert|. This function looks for a series of + // |OpCompositeInsert| instructions that insert the elements one at a time in + // order from beginning to end. + std::unique_ptr BuildMemoryObjectFromInsert( + Instruction* insert_inst); + + // Return true if |type_id| is a pointer type whose pointee type is an array. + bool IsPointerToArrayType(uint32_t type_id); + + // Returns true of there are not stores using |ptr_inst| or something derived + // from it. + bool HasNoStores(Instruction* ptr_inst); + + // Creates an |OpAccessChain| instruction whose result is a pointer the memory + // represented by |source|. The new instruction will be placed before + // |insertion_point|. |insertion_point| must be part of a function. Returns + // the new instruction. + Instruction* BuildNewAccessChain(Instruction* insertion_point, + MemoryObject* source) const; + + // Rewrites all uses of |original_ptr| to use |new_pointer_inst| updating + // types of other instructions as needed. This function should not be called + // if |CanUpdateUses(original_ptr_inst, new_pointer_inst->type_id())| returns + // false. + void UpdateUses(Instruction* original_ptr_inst, + Instruction* new_pointer_inst); + + // Return true if |UpdateUses| is able to change all of the uses of + // |original_ptr_inst| to |type_id| and still have valid code. + bool CanUpdateUses(Instruction* original_ptr_inst, uint32_t type_id); + + // Returns a store to |var_inst| that writes to the entire variable, and is + // the only store that does so. Note it does not look through OpAccessChain + // instruction, so partial stores are not considered. + Instruction* FindStoreInstruction(const Instruction* var_inst) const; + + // Return the type id of the member of the type |id| access using + // |access_chain|. The elements of |access_chain| are to be interpreted the + // same way the indexes are used in an |OpCompositeExtract| instruction. + uint32_t GetMemberTypeId(uint32_t id, + const std::vector& access_chain) const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_COPY_PROP_ARRAYS_H_ diff --git a/third_party/spirv-tools/source/opt/dead_branch_elim_pass.cpp b/third_party/spirv-tools/source/opt/dead_branch_elim_pass.cpp new file mode 100644 index 0000000..0054f57 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dead_branch_elim_pass.cpp @@ -0,0 +1,658 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dead_branch_elim_pass.h" + +#include +#include +#include + +#include "source/cfa.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" +#include "source/opt/struct_cfg_analysis.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +namespace { + +const uint32_t kBranchCondTrueLabIdInIdx = 1; +const uint32_t kBranchCondFalseLabIdInIdx = 2; + +} // anonymous namespace + +bool DeadBranchElimPass::GetConstCondition(uint32_t condId, bool* condVal) { + bool condIsConst; + Instruction* cInst = get_def_use_mgr()->GetDef(condId); + switch (cInst->opcode()) { + case SpvOpConstantNull: + case SpvOpConstantFalse: { + *condVal = false; + condIsConst = true; + } break; + case SpvOpConstantTrue: { + *condVal = true; + condIsConst = true; + } break; + case SpvOpLogicalNot: { + bool negVal; + condIsConst = + GetConstCondition(cInst->GetSingleWordInOperand(0), &negVal); + if (condIsConst) *condVal = !negVal; + } break; + default: { condIsConst = false; } break; + } + return condIsConst; +} + +bool DeadBranchElimPass::GetConstInteger(uint32_t selId, uint32_t* selVal) { + Instruction* sInst = get_def_use_mgr()->GetDef(selId); + uint32_t typeId = sInst->type_id(); + Instruction* typeInst = get_def_use_mgr()->GetDef(typeId); + if (!typeInst || (typeInst->opcode() != SpvOpTypeInt)) return false; + // TODO(greg-lunarg): Support non-32 bit ints + if (typeInst->GetSingleWordInOperand(0) != 32) return false; + if (sInst->opcode() == SpvOpConstant) { + *selVal = sInst->GetSingleWordInOperand(0); + return true; + } else if (sInst->opcode() == SpvOpConstantNull) { + *selVal = 0; + return true; + } + return false; +} + +void DeadBranchElimPass::AddBranch(uint32_t labelId, BasicBlock* bp) { + assert(get_def_use_mgr()->GetDef(labelId) != nullptr); + std::unique_ptr newBranch( + new Instruction(context(), SpvOpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {labelId}}})); + context()->AnalyzeDefUse(&*newBranch); + context()->set_instr_block(&*newBranch, bp); + bp->AddInstruction(std::move(newBranch)); +} + +BasicBlock* DeadBranchElimPass::GetParentBlock(uint32_t id) { + return context()->get_instr_block(get_def_use_mgr()->GetDef(id)); +} + +bool DeadBranchElimPass::MarkLiveBlocks( + Function* func, std::unordered_set* live_blocks) { + std::vector> conditions_to_simplify; + std::unordered_set blocks_with_backedge; + std::vector stack; + stack.push_back(&*func->begin()); + bool modified = false; + while (!stack.empty()) { + BasicBlock* block = stack.back(); + stack.pop_back(); + + // Live blocks doubles as visited set. + if (!live_blocks->insert(block).second) continue; + + uint32_t cont_id = block->ContinueBlockIdIfAny(); + if (cont_id != 0) { + AddBlocksWithBackEdge(cont_id, block->id(), block->MergeBlockIdIfAny(), + &blocks_with_backedge); + } + + Instruction* terminator = block->terminator(); + uint32_t live_lab_id = 0; + // Check if the terminator has a single valid successor. + if (terminator->opcode() == SpvOpBranchConditional) { + bool condVal; + if (GetConstCondition(terminator->GetSingleWordInOperand(0u), &condVal)) { + live_lab_id = terminator->GetSingleWordInOperand( + condVal ? kBranchCondTrueLabIdInIdx : kBranchCondFalseLabIdInIdx); + } + } else if (terminator->opcode() == SpvOpSwitch) { + uint32_t sel_val; + if (GetConstInteger(terminator->GetSingleWordInOperand(0u), &sel_val)) { + // Search switch operands for selector value, set live_lab_id to + // corresponding label, use default if not found. + uint32_t icnt = 0; + uint32_t case_val; + terminator->WhileEachInOperand( + [&icnt, &case_val, &sel_val, &live_lab_id](const uint32_t* idp) { + if (icnt == 1) { + // Start with default label. + live_lab_id = *idp; + } else if (icnt > 1) { + if (icnt % 2 == 0) { + case_val = *idp; + } else { + if (case_val == sel_val) { + live_lab_id = *idp; + return false; + } + } + } + ++icnt; + return true; + }); + } + } + + // Don't simplify back edges unless it becomes a branch to the header. Every + // loop must have exactly one back edge to the loop header, so we cannot + // remove it. + bool simplify = false; + if (live_lab_id != 0) { + if (!blocks_with_backedge.count(block)) { + // This is not a back edge. + simplify = true; + } else { + const auto& struct_cfg_analysis = context()->GetStructuredCFGAnalysis(); + uint32_t header_id = struct_cfg_analysis->ContainingLoop(block->id()); + if (live_lab_id == header_id) { + // The new branch will be a branch to the header. + simplify = true; + } + } + } + + if (simplify) { + conditions_to_simplify.push_back({block, live_lab_id}); + stack.push_back(GetParentBlock(live_lab_id)); + } else { + // All successors are live. + const auto* const_block = block; + const_block->ForEachSuccessorLabel([&stack, this](const uint32_t label) { + stack.push_back(GetParentBlock(label)); + }); + } + } + + // Traverse |conditions_to_simplify| in reverse order. This is done so that + // we simplify nested constructs before simplifying the constructs that + // contain them. + for (auto b = conditions_to_simplify.rbegin(); + b != conditions_to_simplify.rend(); ++b) { + modified |= SimplifyBranch(b->first, b->second); + } + + return modified; +} + +bool DeadBranchElimPass::SimplifyBranch(BasicBlock* block, + uint32_t live_lab_id) { + Instruction* merge_inst = block->GetMergeInst(); + Instruction* terminator = block->terminator(); + if (merge_inst && merge_inst->opcode() == SpvOpSelectionMerge) { + if (merge_inst->NextNode()->opcode() == SpvOpSwitch && + SwitchHasNestedBreak(block->id())) { + if (terminator->NumInOperands() == 2) { + // We cannot remove the branch, and it already has a single case, so no + // work to do. + return false; + } + // We have to keep the switch because it has a nest break, so we + // remove all cases except for the live one. + Instruction::OperandList new_operands; + new_operands.push_back(terminator->GetInOperand(0)); + new_operands.push_back({SPV_OPERAND_TYPE_ID, {live_lab_id}}); + terminator->SetInOperands(move(new_operands)); + context()->UpdateDefUse(terminator); + } else { + // Check if the merge instruction is still needed because of a + // non-nested break from the construct. Move the merge instruction if + // it is still needed. + StructuredCFGAnalysis* cfg_analysis = + context()->GetStructuredCFGAnalysis(); + Instruction* first_break = FindFirstExitFromSelectionMerge( + live_lab_id, merge_inst->GetSingleWordInOperand(0), + cfg_analysis->LoopMergeBlock(live_lab_id), + cfg_analysis->LoopContinueBlock(live_lab_id), + cfg_analysis->SwitchMergeBlock(live_lab_id)); + + AddBranch(live_lab_id, block); + context()->KillInst(terminator); + if (first_break == nullptr) { + context()->KillInst(merge_inst); + } else { + merge_inst->RemoveFromList(); + first_break->InsertBefore(std::unique_ptr(merge_inst)); + context()->set_instr_block(merge_inst, + context()->get_instr_block(first_break)); + } + } + } else { + AddBranch(live_lab_id, block); + context()->KillInst(terminator); + } + return true; +} + +void DeadBranchElimPass::MarkUnreachableStructuredTargets( + const std::unordered_set& live_blocks, + std::unordered_set* unreachable_merges, + std::unordered_map* unreachable_continues) { + for (auto block : live_blocks) { + if (auto merge_id = block->MergeBlockIdIfAny()) { + BasicBlock* merge_block = GetParentBlock(merge_id); + if (!live_blocks.count(merge_block)) { + unreachable_merges->insert(merge_block); + } + if (auto cont_id = block->ContinueBlockIdIfAny()) { + BasicBlock* cont_block = GetParentBlock(cont_id); + if (!live_blocks.count(cont_block)) { + (*unreachable_continues)[cont_block] = block; + } + } + } + } +} + +bool DeadBranchElimPass::FixPhiNodesInLiveBlocks( + Function* func, const std::unordered_set& live_blocks, + const std::unordered_map& unreachable_continues) { + bool modified = false; + for (auto& block : *func) { + if (live_blocks.count(&block)) { + for (auto iter = block.begin(); iter != block.end();) { + if (iter->opcode() != SpvOpPhi) { + break; + } + + bool changed = false; + bool backedge_added = false; + Instruction* inst = &*iter; + std::vector operands; + // Build a complete set of operands (not just input operands). Start + // with type and result id operands. + operands.push_back(inst->GetOperand(0u)); + operands.push_back(inst->GetOperand(1u)); + // Iterate through the incoming labels and determine which to keep + // and/or modify. If there in an unreachable continue block, there will + // be an edge from that block to the header. We need to keep it to + // maintain the structured control flow. If the header has more that 2 + // incoming edges, then the OpPhi must have an entry for that edge. + // However, if there is only one other incoming edge, the OpPhi can be + // eliminated. + for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) { + BasicBlock* inc = GetParentBlock(inst->GetSingleWordInOperand(i)); + auto cont_iter = unreachable_continues.find(inc); + if (cont_iter != unreachable_continues.end() && + cont_iter->second == &block && inst->NumInOperands() > 4) { + if (get_def_use_mgr() + ->GetDef(inst->GetSingleWordInOperand(i - 1)) + ->opcode() == SpvOpUndef) { + // Already undef incoming value, no change necessary. + operands.push_back(inst->GetInOperand(i - 1)); + operands.push_back(inst->GetInOperand(i)); + backedge_added = true; + } else { + // Replace incoming value with undef if this phi exists in the + // loop header. Otherwise, this edge is not live since the + // unreachable continue block will be replaced with an + // unconditional branch to the header only. + operands.emplace_back( + SPV_OPERAND_TYPE_ID, + std::initializer_list{Type2Undef(inst->type_id())}); + operands.push_back(inst->GetInOperand(i)); + changed = true; + backedge_added = true; + } + } else if (live_blocks.count(inc) && inc->IsSuccessor(&block)) { + // Keep live incoming edge. + operands.push_back(inst->GetInOperand(i - 1)); + operands.push_back(inst->GetInOperand(i)); + } else { + // Remove incoming edge. + changed = true; + } + } + + if (changed) { + modified = true; + uint32_t continue_id = block.ContinueBlockIdIfAny(); + if (!backedge_added && continue_id != 0 && + unreachable_continues.count(GetParentBlock(continue_id)) && + operands.size() > 4) { + // Changed the backedge to branch from the continue block instead + // of a successor of the continue block. Add an entry to the phi to + // provide an undef for the continue block. Since the successor of + // the continue must also be unreachable (dominated by the continue + // block), any entry for the original backedge has been removed + // from the phi operands. + operands.emplace_back( + SPV_OPERAND_TYPE_ID, + std::initializer_list{Type2Undef(inst->type_id())}); + operands.emplace_back(SPV_OPERAND_TYPE_ID, + std::initializer_list{continue_id}); + } + + // Either replace the phi with a single value or rebuild the phi out + // of |operands|. + // + // We always have type and result id operands. So this phi has a + // single source if there are two more operands beyond those. + if (operands.size() == 4) { + // First input data operands is at index 2. + uint32_t replId = operands[2u].words[0]; + context()->ReplaceAllUsesWith(inst->result_id(), replId); + iter = context()->KillInst(&*inst); + } else { + // We've rewritten the operands, so first instruct the def/use + // manager to forget uses in the phi before we replace them. After + // replacing operands update the def/use manager by re-analyzing + // the used ids in this phi. + get_def_use_mgr()->EraseUseRecordsOfOperandIds(inst); + inst->ReplaceOperands(operands); + get_def_use_mgr()->AnalyzeInstUse(inst); + ++iter; + } + } else { + ++iter; + } + } + } + } + + return modified; +} + +bool DeadBranchElimPass::EraseDeadBlocks( + Function* func, const std::unordered_set& live_blocks, + const std::unordered_set& unreachable_merges, + const std::unordered_map& unreachable_continues) { + bool modified = false; + for (auto ebi = func->begin(); ebi != func->end();) { + if (unreachable_continues.count(&*ebi)) { + uint32_t cont_id = unreachable_continues.find(&*ebi)->second->id(); + if (ebi->begin() != ebi->tail() || + ebi->terminator()->opcode() != SpvOpBranch || + ebi->terminator()->GetSingleWordInOperand(0u) != cont_id) { + // Make unreachable, but leave the label. + KillAllInsts(&*ebi, false); + // Add unconditional branch to header. + assert(unreachable_continues.count(&*ebi)); + ebi->AddInstruction(MakeUnique( + context(), SpvOpBranch, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {cont_id}}})); + get_def_use_mgr()->AnalyzeInstUse(&*ebi->tail()); + context()->set_instr_block(&*ebi->tail(), &*ebi); + modified = true; + } + ++ebi; + } else if (unreachable_merges.count(&*ebi)) { + if (ebi->begin() != ebi->tail() || + ebi->terminator()->opcode() != SpvOpUnreachable) { + // Make unreachable, but leave the label. + KillAllInsts(&*ebi, false); + // Add unreachable terminator. + ebi->AddInstruction( + MakeUnique(context(), SpvOpUnreachable, 0, 0, + std::initializer_list{})); + context()->AnalyzeUses(ebi->terminator()); + context()->set_instr_block(ebi->terminator(), &*ebi); + modified = true; + } + ++ebi; + } else if (!live_blocks.count(&*ebi)) { + // Kill this block. + KillAllInsts(&*ebi); + ebi = ebi.Erase(); + modified = true; + } else { + ++ebi; + } + } + + return modified; +} + +bool DeadBranchElimPass::EliminateDeadBranches(Function* func) { + bool modified = false; + std::unordered_set live_blocks; + modified |= MarkLiveBlocks(func, &live_blocks); + + std::unordered_set unreachable_merges; + std::unordered_map unreachable_continues; + MarkUnreachableStructuredTargets(live_blocks, &unreachable_merges, + &unreachable_continues); + modified |= FixPhiNodesInLiveBlocks(func, live_blocks, unreachable_continues); + modified |= EraseDeadBlocks(func, live_blocks, unreachable_merges, + unreachable_continues); + + return modified; +} + +void DeadBranchElimPass::FixBlockOrder() { + context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis); + // Reorders blocks according to DFS of dominator tree. + ProcessFunction reorder_dominators = [this](Function* function) { + DominatorAnalysis* dominators = context()->GetDominatorAnalysis(function); + std::vector blocks; + for (auto iter = dominators->GetDomTree().begin(); + iter != dominators->GetDomTree().end(); ++iter) { + if (iter->id() != 0) { + blocks.push_back(iter->bb_); + } + } + for (uint32_t i = 1; i < blocks.size(); ++i) { + function->MoveBasicBlockToAfter(blocks[i]->id(), blocks[i - 1]); + } + return true; + }; + + // Reorders blocks according to structured order. + ProcessFunction reorder_structured = [this](Function* function) { + std::list order; + context()->cfg()->ComputeStructuredOrder(function, &*function->begin(), + &order); + std::vector blocks; + for (auto block : order) { + blocks.push_back(block); + } + for (uint32_t i = 1; i < blocks.size(); ++i) { + function->MoveBasicBlockToAfter(blocks[i]->id(), blocks[i - 1]); + } + return true; + }; + + // Structured order is more intuitive so use it where possible. + if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) { + context()->ProcessReachableCallTree(reorder_structured); + } else { + context()->ProcessReachableCallTree(reorder_dominators); + } +} + +Pass::Status DeadBranchElimPass::Process() { + // Do not process if module contains OpGroupDecorate. Additional + // support required in KillNamesAndDecorates(). + // TODO(greg-lunarg): Add support for OpGroupDecorate + for (auto& ai : get_module()->annotations()) + if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange; + // Process all entry point functions + ProcessFunction pfn = [this](Function* fp) { + return EliminateDeadBranches(fp); + }; + bool modified = context()->ProcessReachableCallTree(pfn); + if (modified) FixBlockOrder(); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Instruction* DeadBranchElimPass::FindFirstExitFromSelectionMerge( + uint32_t start_block_id, uint32_t merge_block_id, uint32_t loop_merge_id, + uint32_t loop_continue_id, uint32_t switch_merge_id) { + // To find the "first" exit, we follow branches looking for a conditional + // branch that is not in a nested construct and is not the header of a new + // construct. We follow the control flow from |start_block_id| to find the + // first one. + + while (start_block_id != merge_block_id && start_block_id != loop_merge_id && + start_block_id != loop_continue_id) { + BasicBlock* start_block = context()->get_instr_block(start_block_id); + Instruction* branch = start_block->terminator(); + uint32_t next_block_id = 0; + switch (branch->opcode()) { + case SpvOpBranchConditional: + next_block_id = start_block->MergeBlockIdIfAny(); + if (next_block_id == 0) { + // If a possible target is the |loop_merge_id| or |loop_continue_id|, + // which are not the current merge node, then we continue the search + // with the other target. + for (uint32_t i = 1; i < 3; i++) { + if (branch->GetSingleWordInOperand(i) == loop_merge_id && + loop_merge_id != merge_block_id) { + next_block_id = branch->GetSingleWordInOperand(3 - i); + break; + } + if (branch->GetSingleWordInOperand(i) == loop_continue_id && + loop_continue_id != merge_block_id) { + next_block_id = branch->GetSingleWordInOperand(3 - i); + break; + } + if (branch->GetSingleWordInOperand(i) == switch_merge_id && + switch_merge_id != merge_block_id) { + next_block_id = branch->GetSingleWordInOperand(3 - i); + break; + } + } + + if (next_block_id == 0) { + return branch; + } + } + break; + case SpvOpSwitch: + next_block_id = start_block->MergeBlockIdIfAny(); + if (next_block_id == 0) { + // A switch with no merge instructions can have at most 5 targets: + // a. |merge_block_id| + // b. |loop_merge_id| + // c. |loop_continue_id| + // d. |switch_merge_id| + // e. 1 block inside the current region. + // + // Note that because this is a switch, |merge_block_id| must equal + // |switch_merge_id|. + // + // This leads to a number of cases of what to do. + // + // 1. Does not jump to a block inside of the current construct. In + // this case, there is not conditional break, so we should return + // |nullptr|. + // + // 2. Jumps to |merge_block_id| and a block inside the current + // construct. In this case, this branch conditionally break to the + // end of the current construct, so return the current branch. + // + // 3. Otherwise, this branch may break, but not to the current merge + // block. So we continue with the block that is inside the loop. + bool found_break = false; + for (uint32_t i = 1; i < branch->NumInOperands(); i += 2) { + uint32_t target = branch->GetSingleWordInOperand(i); + if (target == merge_block_id) { + found_break = true; + } else if (target != loop_merge_id && target != loop_continue_id) { + next_block_id = branch->GetSingleWordInOperand(i); + } + } + + if (next_block_id == 0) { + // Case 1. + return nullptr; + } + + if (found_break) { + // Case 2. + return branch; + } + + // The fall through is case 3. + } + break; + case SpvOpBranch: + // Need to check if this is the header of a loop nested in the + // selection construct. + next_block_id = start_block->MergeBlockIdIfAny(); + if (next_block_id == 0) { + next_block_id = branch->GetSingleWordInOperand(0); + } + break; + default: + return nullptr; + } + start_block_id = next_block_id; + } + return nullptr; +} + +void DeadBranchElimPass::AddBlocksWithBackEdge( + uint32_t cont_id, uint32_t header_id, uint32_t merge_id, + std::unordered_set* blocks_with_back_edges) { + std::unordered_set visited; + visited.insert(cont_id); + visited.insert(header_id); + visited.insert(merge_id); + + std::vector work_list; + work_list.push_back(cont_id); + + while (!work_list.empty()) { + uint32_t bb_id = work_list.back(); + work_list.pop_back(); + + BasicBlock* bb = context()->get_instr_block(bb_id); + + bool has_back_edge = false; + bb->ForEachSuccessorLabel([header_id, &visited, &work_list, + &has_back_edge](uint32_t* succ_label_id) { + if (visited.insert(*succ_label_id).second) { + work_list.push_back(*succ_label_id); + } + if (*succ_label_id == header_id) { + has_back_edge = true; + } + }); + + if (has_back_edge) { + blocks_with_back_edges->insert(bb); + } + } +} + +bool DeadBranchElimPass::SwitchHasNestedBreak(uint32_t switch_header_id) { + std::vector block_in_construct; + BasicBlock* start_block = context()->get_instr_block(switch_header_id); + uint32_t merge_block_id = start_block->MergeBlockIdIfAny(); + + StructuredCFGAnalysis* cfg_analysis = context()->GetStructuredCFGAnalysis(); + return !get_def_use_mgr()->WhileEachUser( + merge_block_id, + [this, cfg_analysis, switch_header_id](Instruction* inst) { + if (!inst->IsBranch()) { + return true; + } + + BasicBlock* bb = context()->get_instr_block(inst); + if (bb->id() == switch_header_id) { + return true; + } + return (cfg_analysis->ContainingConstruct(inst) == switch_header_id && + bb->GetMergeInst() == nullptr); + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/dead_branch_elim_pass.h b/third_party/spirv-tools/source/opt/dead_branch_elim_pass.h new file mode 100644 index 0000000..7841bc4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dead_branch_elim_pass.h @@ -0,0 +1,176 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEAD_BRANCH_ELIM_PASS_H_ +#define SOURCE_OPT_DEAD_BRANCH_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class DeadBranchElimPass : public MemPass { + using cbb_ptr = const BasicBlock*; + + public: + DeadBranchElimPass() = default; + + const char* name() const override { return "eliminate-dead-branches"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // If |condId| is boolean constant, return conditional value in |condVal| and + // return true, otherwise return false. + bool GetConstCondition(uint32_t condId, bool* condVal); + + // If |valId| is a 32-bit integer constant, return value via |value| and + // return true, otherwise return false. + bool GetConstInteger(uint32_t valId, uint32_t* value); + + // Add branch to |labelId| to end of block |bp|. + void AddBranch(uint32_t labelId, BasicBlock* bp); + + // For function |func|, look for BranchConditionals with constant condition + // and convert to a Branch to the indicated label. Delete resulting dead + // blocks. Note some such branches and blocks may be left to avoid creating + // invalid control flow. + // TODO(greg-lunarg): Remove remaining constant conditional branches and dead + // blocks. + bool EliminateDeadBranches(Function* func); + + // Returns the basic block containing |id|. + // Note: this pass only requires correct instruction block mappings for the + // input. This pass does not preserve the block mapping, so it is not kept + // up-to-date during processing. + BasicBlock* GetParentBlock(uint32_t id); + + // Marks live blocks reachable from the entry of |func|. Simplifies constant + // branches and switches as it proceeds, to limit the number of live blocks. + // It is careful not to eliminate backedges even if they are dead, but the + // header is live. Likewise, unreachable merge blocks named in live merge + // instruction must be retained (though they may be clobbered). + bool MarkLiveBlocks(Function* func, + std::unordered_set* live_blocks); + + // Checks for unreachable merge and continue blocks with live headers; those + // blocks must be retained. Continues are tracked separately so that a live + // phi can be updated to take an undef value from any of its predecessors + // that are unreachable continues. + // + // |unreachable_continues| maps the id of an unreachable continue target to + // the header block that declares it. + void MarkUnreachableStructuredTargets( + const std::unordered_set& live_blocks, + std::unordered_set* unreachable_merges, + std::unordered_map* unreachable_continues); + + // Fix phis in reachable blocks so that only live (or unremovable) incoming + // edges are present. If the block now only has a single live incoming edge, + // remove the phi and replace its uses with its data input. If the single + // remaining incoming edge is from the phi itself, the the phi is in an + // unreachable single block loop. Either the block is dead and will be + // removed, or it's reachable from an unreachable continue target. In the + // latter case that continue target block will be collapsed into a block that + // only branches back to its header and we'll eliminate the block with the + // phi. + // + // |unreachable_continues| maps continue targets that cannot be reached to + // merge instruction that declares them. + bool FixPhiNodesInLiveBlocks( + Function* func, const std::unordered_set& live_blocks, + const std::unordered_map& + unreachable_continues); + + // Erases dead blocks. Any block captured in |unreachable_merges| or + // |unreachable_continues| is a dead block that is required to remain due to + // a live merge instruction in the corresponding header. These blocks will + // have their instructions clobbered and will become a label and terminator. + // Unreachable merge blocks are terminated by OpUnreachable, while + // unreachable continue blocks are terminated by an unconditional branch to + // the header. Otherwise, blocks are dead if not explicitly captured in + // |live_blocks| and are totally removed. + // + // |unreachable_continues| maps continue targets that cannot be reached to + // corresponding header block that declares them. + bool EraseDeadBlocks( + Function* func, const std::unordered_set& live_blocks, + const std::unordered_set& unreachable_merges, + const std::unordered_map& + unreachable_continues); + + // Reorders blocks in reachable functions so that they satisfy dominator + // block ordering rules. + void FixBlockOrder(); + + // Return the first branch instruction that is a conditional branch to + // |merge_block_id|. Returns |nullptr| if no such branch exists. If there are + // multiple such branches, the first one is the one that would be executed + // first when running the code. That is, the one that dominates all of the + // others. + // + // |start_block_id| must be a block whose innermost containing merge construct + // has |merge_block_id| as the merge block. + // + // |loop_merge_id| and |loop_continue_id| are the merge and continue block ids + // of the innermost loop containing |start_block_id|. + Instruction* FindFirstExitFromSelectionMerge(uint32_t start_block_id, + uint32_t merge_block_id, + uint32_t loop_merge_id, + uint32_t loop_continue_id, + uint32_t switch_merge_id); + + // Adds to |blocks_with_back_edges| all of the blocks on the path from the + // basic block |cont_id| to |header_id| and |merge_id|. The intention is that + // |cond_id| is a the continue target of a loop, |header_id| is the header of + // the loop, and |merge_id| is the merge block of the loop. + void AddBlocksWithBackEdge( + uint32_t cont_id, uint32_t header_id, uint32_t merge_id, + std::unordered_set* blocks_with_back_edges); + + // Returns true if there is a brach to the merge node of the selection + // construct |switch_header_id| that is inside a nested selection construct or + // in the header of the nested selection construct. + bool SwitchHasNestedBreak(uint32_t switch_header_id); + + // Return true of the terminator of |block| is successfully replaced with a + // branch to |live_lab_id|. The merge instruction is deleted or moved as + // needed to maintain structured control flow. Assumes that the + // StructuredCFGAnalysis is valid for the constructs containing |block|. + bool SimplifyBranch(BasicBlock* block, uint32_t live_lab_id); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEAD_BRANCH_ELIM_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/dead_insert_elim_pass.cpp b/third_party/spirv-tools/source/opt/dead_insert_elim_pass.cpp new file mode 100644 index 0000000..46f4f12 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dead_insert_elim_pass.cpp @@ -0,0 +1,264 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dead_insert_elim_pass.h" + +#include "source/opt/composite.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" +#include "spirv/1.2/GLSL.std.450.h" + +namespace spvtools { +namespace opt { + +namespace { + +const uint32_t kTypeVectorCountInIdx = 1; +const uint32_t kTypeMatrixCountInIdx = 1; +const uint32_t kTypeArrayLengthIdInIdx = 1; +const uint32_t kTypeIntWidthInIdx = 0; +const uint32_t kConstantValueInIdx = 0; +const uint32_t kInsertObjectIdInIdx = 0; +const uint32_t kInsertCompositeIdInIdx = 1; + +} // anonymous namespace + +uint32_t DeadInsertElimPass::NumComponents(Instruction* typeInst) { + switch (typeInst->opcode()) { + case SpvOpTypeVector: { + return typeInst->GetSingleWordInOperand(kTypeVectorCountInIdx); + } break; + case SpvOpTypeMatrix: { + return typeInst->GetSingleWordInOperand(kTypeMatrixCountInIdx); + } break; + case SpvOpTypeArray: { + uint32_t lenId = + typeInst->GetSingleWordInOperand(kTypeArrayLengthIdInIdx); + Instruction* lenInst = get_def_use_mgr()->GetDef(lenId); + if (lenInst->opcode() != SpvOpConstant) return 0; + uint32_t lenTypeId = lenInst->type_id(); + Instruction* lenTypeInst = get_def_use_mgr()->GetDef(lenTypeId); + // TODO(greg-lunarg): Support non-32-bit array length + if (lenTypeInst->GetSingleWordInOperand(kTypeIntWidthInIdx) != 32) + return 0; + return lenInst->GetSingleWordInOperand(kConstantValueInIdx); + } break; + case SpvOpTypeStruct: { + return typeInst->NumInOperands(); + } break; + default: { return 0; } break; + } +} + +void DeadInsertElimPass::MarkInsertChain( + Instruction* insertChain, std::vector* pExtIndices, + uint32_t extOffset, std::unordered_set* visited_phis) { + // Not currently optimizing array inserts. + Instruction* typeInst = get_def_use_mgr()->GetDef(insertChain->type_id()); + if (typeInst->opcode() == SpvOpTypeArray) return; + // Insert chains are only composed of inserts and phis + if (insertChain->opcode() != SpvOpCompositeInsert && + insertChain->opcode() != SpvOpPhi) + return; + // If extract indices are empty, mark all subcomponents if type + // is constant length. + if (pExtIndices == nullptr) { + uint32_t cnum = NumComponents(typeInst); + if (cnum > 0) { + std::vector extIndices; + for (uint32_t i = 0; i < cnum; i++) { + extIndices.clear(); + extIndices.push_back(i); + std::unordered_set sub_visited_phis; + MarkInsertChain(insertChain, &extIndices, 0, &sub_visited_phis); + } + return; + } + } + Instruction* insInst = insertChain; + while (insInst->opcode() == SpvOpCompositeInsert) { + // If no extract indices, mark insert and inserted object (which might + // also be an insert chain) and continue up the chain though the input + // composite. + // + // Note: We mark inserted objects in this function (rather than in + // EliminateDeadInsertsOnePass) because in some cases, we can do it + // more accurately here. + if (pExtIndices == nullptr) { + liveInserts_.insert(insInst->result_id()); + uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx); + std::unordered_set obj_visited_phis; + MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0, + &obj_visited_phis); + // If extract indices match insert, we are done. Mark insert and + // inserted object. + } else if (ExtInsMatch(*pExtIndices, insInst, extOffset)) { + liveInserts_.insert(insInst->result_id()); + uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx); + std::unordered_set obj_visited_phis; + MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0, + &obj_visited_phis); + break; + // If non-matching intersection, mark insert + } else if (ExtInsConflict(*pExtIndices, insInst, extOffset)) { + liveInserts_.insert(insInst->result_id()); + // If more extract indices than insert, we are done. Use remaining + // extract indices to mark inserted object. + uint32_t numInsertIndices = insInst->NumInOperands() - 2; + if (pExtIndices->size() - extOffset > numInsertIndices) { + uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx); + std::unordered_set obj_visited_phis; + MarkInsertChain(get_def_use_mgr()->GetDef(objId), pExtIndices, + extOffset + numInsertIndices, &obj_visited_phis); + break; + // If fewer extract indices than insert, also mark inserted object and + // continue up chain. + } else { + uint32_t objId = insInst->GetSingleWordInOperand(kInsertObjectIdInIdx); + std::unordered_set obj_visited_phis; + MarkInsertChain(get_def_use_mgr()->GetDef(objId), nullptr, 0, + &obj_visited_phis); + } + } + // Get next insert in chain + const uint32_t compId = + insInst->GetSingleWordInOperand(kInsertCompositeIdInIdx); + insInst = get_def_use_mgr()->GetDef(compId); + } + // If insert chain ended with phi, do recursive call on each operand + if (insInst->opcode() != SpvOpPhi) return; + // Mark phi visited to prevent potential infinite loop. If phi is already + // visited, return to avoid infinite loop. + if (visited_phis->count(insInst->result_id()) != 0) return; + visited_phis->insert(insInst->result_id()); + + // Phis may have duplicate inputs values for different edges, prune incoming + // ids lists before recursing. + std::vector ids; + for (uint32_t i = 0; i < insInst->NumInOperands(); i += 2) { + ids.push_back(insInst->GetSingleWordInOperand(i)); + } + std::sort(ids.begin(), ids.end()); + auto new_end = std::unique(ids.begin(), ids.end()); + for (auto id_iter = ids.begin(); id_iter != new_end; ++id_iter) { + Instruction* pi = get_def_use_mgr()->GetDef(*id_iter); + MarkInsertChain(pi, pExtIndices, extOffset, visited_phis); + } +} + +bool DeadInsertElimPass::EliminateDeadInserts(Function* func) { + bool modified = false; + bool lastmodified = true; + // Each pass can delete dead instructions, thus potentially revealing + // new dead insertions ie insertions with no uses. + while (lastmodified) { + lastmodified = EliminateDeadInsertsOnePass(func); + modified |= lastmodified; + } + return modified; +} + +bool DeadInsertElimPass::EliminateDeadInsertsOnePass(Function* func) { + bool modified = false; + liveInserts_.clear(); + visitedPhis_.clear(); + // Mark all live inserts + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + // Only process Inserts and composite Phis + SpvOp op = ii->opcode(); + Instruction* typeInst = get_def_use_mgr()->GetDef(ii->type_id()); + if (op != SpvOpCompositeInsert && + (op != SpvOpPhi || !spvOpcodeIsComposite(typeInst->opcode()))) + continue; + // The marking algorithm can be expensive for large arrays and the + // efficacy of eliminating dead inserts into arrays is questionable. + // Skip optimizing array inserts for now. Just mark them live. + // TODO(greg-lunarg): Eliminate dead array inserts + if (op == SpvOpCompositeInsert) { + if (typeInst->opcode() == SpvOpTypeArray) { + liveInserts_.insert(ii->result_id()); + continue; + } + } + const uint32_t id = ii->result_id(); + get_def_use_mgr()->ForEachUser(id, [&ii, this](Instruction* user) { + if (user->IsOpenCL100DebugInstr()) return; + switch (user->opcode()) { + case SpvOpCompositeInsert: + case SpvOpPhi: + // Use by insert or phi does not initiate marking + break; + case SpvOpCompositeExtract: { + // Capture extract indices + std::vector extIndices; + uint32_t icnt = 0; + user->ForEachInOperand([&icnt, &extIndices](const uint32_t* idp) { + if (icnt > 0) extIndices.push_back(*idp); + ++icnt; + }); + // Mark all inserts in chain that intersect with extract + std::unordered_set visited_phis; + MarkInsertChain(&*ii, &extIndices, 0, &visited_phis); + } break; + default: { + // Mark inserts in chain for all components + MarkInsertChain(&*ii, nullptr, 0, nullptr); + } break; + } + }); + } + } + // Find and disconnect dead inserts + std::vector dead_instructions; + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + if (ii->opcode() != SpvOpCompositeInsert) continue; + const uint32_t id = ii->result_id(); + if (liveInserts_.find(id) != liveInserts_.end()) continue; + const uint32_t replId = + ii->GetSingleWordInOperand(kInsertCompositeIdInIdx); + (void)context()->ReplaceAllUsesWith(id, replId); + dead_instructions.push_back(&*ii); + modified = true; + } + } + // DCE dead inserts + while (!dead_instructions.empty()) { + Instruction* inst = dead_instructions.back(); + dead_instructions.pop_back(); + DCEInst(inst, [&dead_instructions](Instruction* other_inst) { + auto i = std::find(dead_instructions.begin(), dead_instructions.end(), + other_inst); + if (i != dead_instructions.end()) { + dead_instructions.erase(i); + } + }); + } + return modified; +} + +Pass::Status DeadInsertElimPass::Process() { + // Process all entry point functions. + ProcessFunction pfn = [this](Function* fp) { + return EliminateDeadInserts(fp); + }; + bool modified = context()->ProcessEntryPointCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/dead_insert_elim_pass.h b/third_party/spirv-tools/source/opt/dead_insert_elim_pass.h new file mode 100644 index 0000000..01f12bb --- /dev/null +++ b/third_party/spirv-tools/source/opt/dead_insert_elim_pass.h @@ -0,0 +1,90 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEAD_INSERT_ELIM_PASS_H_ +#define SOURCE_OPT_DEAD_INSERT_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class DeadInsertElimPass : public MemPass { + public: + DeadInsertElimPass() = default; + + const char* name() const override { return "eliminate-dead-inserts"; } + Status Process() override; + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Return the number of subcomponents in the composite type |typeId|. + // Return 0 if not a composite type or number of components is not a + // 32-bit constant. + uint32_t NumComponents(Instruction* typeInst); + + // Mark all inserts in instruction chain ending at |insertChain| with + // indices that intersect with extract indices |extIndices| starting with + // index at |extOffset|. Chains are composed solely of Inserts and Phis. + // Mark all inserts in chain if |extIndices| is nullptr. + void MarkInsertChain(Instruction* insertChain, + std::vector* extIndices, uint32_t extOffset, + std::unordered_set* visited_phis); + + // Perform EliminateDeadInsertsOnePass(|func|) until no modification is + // made. Return true if modified. + bool EliminateDeadInserts(Function* func); + + // DCE all dead struct, matrix and vector inserts in |func|. An insert is + // dead if the value it inserts is never used. Replace any reference to the + // insert with its original composite. Return true if modified. Dead inserts + // in dependence cycles are not currently eliminated. Dead inserts into + // arrays are not currently eliminated. + bool EliminateDeadInsertsOnePass(Function* func); + + // Return true if all extensions in this module are allowed by this pass. + bool AllExtensionsSupported() const; + + // Live inserts + std::unordered_set liveInserts_; + + // Visited phis as insert chain is traversed; used to avoid infinite loop + std::unordered_map visitedPhis_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEAD_INSERT_ELIM_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/dead_variable_elimination.cpp b/third_party/spirv-tools/source/opt/dead_variable_elimination.cpp new file mode 100644 index 0000000..2837106 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dead_variable_elimination.cpp @@ -0,0 +1,111 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dead_variable_elimination.h" + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { + +// This optimization removes global variables that are not needed because they +// are definitely not accessed. +Pass::Status DeadVariableElimination::Process() { + // The algorithm will compute the reference count for every global variable. + // Anything with a reference count of 0 will then be deleted. For variables + // that might have references that are not explicit in this context, we use + // the value kMustKeep as the reference count. + std::vector ids_to_remove; + + // Get the reference count for all of the global OpVariable instructions. + for (auto& inst : context()->types_values()) { + if (inst.opcode() != SpvOp::SpvOpVariable) { + continue; + } + + size_t count = 0; + uint32_t result_id = inst.result_id(); + + // Check the linkage. If it is exported, it could be reference somewhere + // else, so we must keep the variable around. + get_decoration_mgr()->ForEachDecoration( + result_id, SpvDecorationLinkageAttributes, + [&count](const Instruction& linkage_instruction) { + uint32_t last_operand = linkage_instruction.NumOperands() - 1; + if (linkage_instruction.GetSingleWordOperand(last_operand) == + SpvLinkageTypeExport) { + count = kMustKeep; + } + }); + + if (count != kMustKeep) { + // If we don't have to keep the instruction for other reasons, then look + // at the uses and count the number of real references. + count = 0; + get_def_use_mgr()->ForEachUser(result_id, [&count](Instruction* user) { + if (!IsAnnotationInst(user->opcode()) && user->opcode() != SpvOpName) { + ++count; + } + }); + } + reference_count_[result_id] = count; + if (count == 0) { + ids_to_remove.push_back(result_id); + } + } + + // Remove all of the variables that have a reference count of 0. + bool modified = false; + if (!ids_to_remove.empty()) { + modified = true; + for (auto result_id : ids_to_remove) { + DeleteVariable(result_id); + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void DeadVariableElimination::DeleteVariable(uint32_t result_id) { + Instruction* inst = get_def_use_mgr()->GetDef(result_id); + assert(inst->opcode() == SpvOpVariable && + "Should not be trying to delete anything other than an OpVariable."); + + // Look for an initializer that references another variable. We need to know + // if that variable can be deleted after the reference is removed. + if (inst->NumOperands() == 4) { + Instruction* initializer = + get_def_use_mgr()->GetDef(inst->GetSingleWordOperand(3)); + + // TODO: Handle OpSpecConstantOP which might be defined in terms of other + // variables. Will probably require a unified dead code pass that does all + // instruction types. (Issue 906) + if (initializer->opcode() == SpvOpVariable) { + uint32_t initializer_id = initializer->result_id(); + size_t& count = reference_count_[initializer_id]; + if (count != kMustKeep) { + --count; + } + + if (count == 0) { + DeleteVariable(initializer_id); + } + } + } + context()->KillDef(result_id); +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/dead_variable_elimination.h b/third_party/spirv-tools/source/opt/dead_variable_elimination.h new file mode 100644 index 0000000..5dde71b --- /dev/null +++ b/third_party/spirv-tools/source/opt/dead_variable_elimination.h @@ -0,0 +1,56 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEAD_VARIABLE_ELIMINATION_H_ +#define SOURCE_OPT_DEAD_VARIABLE_ELIMINATION_H_ + +#include +#include + +#include "source/opt/decoration_manager.h" +#include "source/opt/mem_pass.h" + +namespace spvtools { +namespace opt { + +class DeadVariableElimination : public MemPass { + public: + const char* name() const override { return "eliminate-dead-variables"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Deletes the OpVariable instruction who result id is |result_id|. + void DeleteVariable(uint32_t result_id); + + // Keeps track of the number of references of an id. Once that value is 0, it + // is safe to remove the corresponding instruction. + // + // Note that the special value kMustKeep is used to indicate that the + // instruction cannot be deleted for reasons other that is being explicitly + // referenced. + std::unordered_map reference_count_; + + // Special value used to indicate that an id cannot be safely deleted. + enum { kMustKeep = INT_MAX }; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEAD_VARIABLE_ELIMINATION_H_ diff --git a/third_party/spirv-tools/source/opt/debug_info_manager.cpp b/third_party/spirv-tools/source/opt/debug_info_manager.cpp new file mode 100644 index 0000000..87ef655 --- /dev/null +++ b/third_party/spirv-tools/source/opt/debug_info_manager.cpp @@ -0,0 +1,862 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/debug_info_manager.h" + +#include + +#include "source/opt/ir_context.h" + +// Constants for OpenCL.DebugInfo.100 extension instructions. + +static const uint32_t kOpLineOperandLineIndex = 1; +static const uint32_t kLineOperandIndexDebugFunction = 7; +static const uint32_t kLineOperandIndexDebugLexicalBlock = 5; +static const uint32_t kDebugFunctionOperandFunctionIndex = 13; +static const uint32_t kDebugFunctionOperandParentIndex = 9; +static const uint32_t kDebugTypeCompositeOperandParentIndex = 9; +static const uint32_t kDebugLexicalBlockOperandParentIndex = 7; +static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6; +static const uint32_t kDebugExpressOperandOperationIndex = 4; +static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4; +static const uint32_t kDebugDeclareOperandVariableIndex = 5; +static const uint32_t kDebugValueOperandLocalVariableIndex = 4; +static const uint32_t kDebugValueOperandExpressionIndex = 6; +static const uint32_t kDebugValueOperandIndexesIndex = 7; +static const uint32_t kDebugOperationOperandOperationIndex = 4; +static const uint32_t kOpVariableOperandStorageClassIndex = 2; +static const uint32_t kDebugLocalVariableOperandParentIndex = 9; +static const uint32_t kExtInstInstructionInIdx = 1; +static const uint32_t kDebugGlobalVariableOperandFlagsIndex = 12; +static const uint32_t kDebugLocalVariableOperandFlagsIndex = 10; + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +void SetInlinedOperand(Instruction* dbg_inlined_at, uint32_t inlined_operand) { + assert(dbg_inlined_at); + assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugInlinedAt); + if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) { + dbg_inlined_at->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inlined_operand}}); + } else { + dbg_inlined_at->SetOperand(kDebugInlinedAtOperandInlinedIndex, + {inlined_operand}); + } +} + +uint32_t GetInlinedOperand(Instruction* dbg_inlined_at) { + assert(dbg_inlined_at); + assert(dbg_inlined_at->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugInlinedAt); + if (dbg_inlined_at->NumOperands() <= kDebugInlinedAtOperandInlinedIndex) + return kNoInlinedAt; + return dbg_inlined_at->GetSingleWordOperand( + kDebugInlinedAtOperandInlinedIndex); +} + +bool IsEmptyDebugExpression(Instruction* instr) { + return instr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugExpression && + instr->NumOperands() == kDebugExpressOperandOperationIndex; +} + +} // namespace + +DebugInfoManager::DebugInfoManager(IRContext* c) : context_(c) { + AnalyzeDebugInsts(*c->module()); +} + +Instruction* DebugInfoManager::GetDbgInst(uint32_t id) { + auto dbg_inst_it = id_to_dbg_inst_.find(id); + return dbg_inst_it == id_to_dbg_inst_.end() ? nullptr : dbg_inst_it->second; +} + +void DebugInfoManager::RegisterDbgInst(Instruction* inst) { + assert( + inst->NumInOperands() != 0 && + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == + inst->GetInOperand(0).words[0] && + "Given instruction is not a debug instruction"); + id_to_dbg_inst_[inst->result_id()] = inst; +} + +void DebugInfoManager::RegisterDbgFunction(Instruction* inst) { + assert(inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction && + "inst is not a DebugFunction"); + auto fn_id = inst->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); + // Do not register function that has been optimized away + auto fn_inst = GetDbgInst(fn_id); + if (fn_inst != nullptr) { + assert(GetDbgInst(fn_id)->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugInfoNone); + return; + } + assert( + fn_id_to_dbg_fn_.find(fn_id) == fn_id_to_dbg_fn_.end() && + "Register DebugFunction for a function that already has DebugFunction"); + fn_id_to_dbg_fn_[fn_id] = inst; +} + +void DebugInfoManager::RegisterDbgDeclare(uint32_t var_id, + Instruction* dbg_declare) { + assert(dbg_declare->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugDeclare || + dbg_declare->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugValue); + auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_id); + if (dbg_decl_itr == var_id_to_dbg_decl_.end()) { + var_id_to_dbg_decl_[var_id] = {dbg_declare}; + } else { + dbg_decl_itr->second.insert(dbg_declare); + } +} + +uint32_t DebugInfoManager::CreateDebugInlinedAt(const Instruction* line, + const DebugScope& scope) { + if (context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo() == + 0) + return kNoInlinedAt; + + uint32_t line_number = 0; + if (line == nullptr) { + auto* lexical_scope_inst = GetDbgInst(scope.GetLexicalScope()); + if (lexical_scope_inst == nullptr) return kNoInlinedAt; + OpenCLDebugInfo100Instructions debug_opcode = + lexical_scope_inst->GetOpenCL100DebugOpcode(); + switch (debug_opcode) { + case OpenCLDebugInfo100DebugFunction: + line_number = lexical_scope_inst->GetSingleWordOperand( + kLineOperandIndexDebugFunction); + break; + case OpenCLDebugInfo100DebugLexicalBlock: + line_number = lexical_scope_inst->GetSingleWordOperand( + kLineOperandIndexDebugLexicalBlock); + break; + case OpenCLDebugInfo100DebugTypeComposite: + case OpenCLDebugInfo100DebugCompilationUnit: + assert(false && + "DebugTypeComposite and DebugCompilationUnit are lexical " + "scopes, but we inline functions into a function or a block " + "of a function, not into a struct/class or a global scope."); + break; + default: + assert(false && + "Unreachable. a debug extension instruction for a " + "lexical scope must be DebugFunction, DebugTypeComposite, " + "DebugLexicalBlock, or DebugCompilationUnit."); + break; + } + } else { + line_number = line->GetSingleWordOperand(kOpLineOperandLineIndex); + } + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr inlined_at(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugInlinedAt)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetLexicalScope()}}, + })); + // |scope| already has DebugInlinedAt. We put the existing DebugInlinedAt + // into the Inlined operand of this new DebugInlinedAt. + if (scope.GetInlinedAt() != kNoInlinedAt) { + inlined_at->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {scope.GetInlinedAt()}}); + } + RegisterDbgInst(inlined_at.get()); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(inlined_at.get()); + context()->module()->AddExtInstDebugInfo(std::move(inlined_at)); + return result_id; +} + +DebugScope DebugInfoManager::BuildDebugScope( + const DebugScope& callee_instr_scope, + DebugInlinedAtContext* inlined_at_ctx) { + return DebugScope(callee_instr_scope.GetLexicalScope(), + BuildDebugInlinedAtChain(callee_instr_scope.GetInlinedAt(), + inlined_at_ctx)); +} + +uint32_t DebugInfoManager::BuildDebugInlinedAtChain( + uint32_t callee_inlined_at, DebugInlinedAtContext* inlined_at_ctx) { + if (inlined_at_ctx->GetScopeOfCallInstruction().GetLexicalScope() == + kNoDebugScope) + return kNoInlinedAt; + + // Reuse the already generated DebugInlinedAt chain if exists. + uint32_t already_generated_chain_head_id = + inlined_at_ctx->GetDebugInlinedAtChain(callee_inlined_at); + if (already_generated_chain_head_id != kNoInlinedAt) { + return already_generated_chain_head_id; + } + + const uint32_t new_dbg_inlined_at_id = + CreateDebugInlinedAt(inlined_at_ctx->GetLineOfCallInstruction(), + inlined_at_ctx->GetScopeOfCallInstruction()); + if (new_dbg_inlined_at_id == kNoInlinedAt) return kNoInlinedAt; + + if (callee_inlined_at == kNoInlinedAt) { + inlined_at_ctx->SetDebugInlinedAtChain(kNoInlinedAt, new_dbg_inlined_at_id); + return new_dbg_inlined_at_id; + } + + uint32_t chain_head_id = kNoInlinedAt; + uint32_t chain_iter_id = callee_inlined_at; + Instruction* last_inlined_at_in_chain = nullptr; + do { + Instruction* new_inlined_at_in_chain = CloneDebugInlinedAt( + chain_iter_id, /* insert_before */ last_inlined_at_in_chain); + assert(new_inlined_at_in_chain != nullptr); + + // Set DebugInlinedAt of the new scope as the head of the chain. + if (chain_head_id == kNoInlinedAt) + chain_head_id = new_inlined_at_in_chain->result_id(); + + // Previous DebugInlinedAt of the chain must point to the new + // DebugInlinedAt as its Inlined operand to build a recursive + // chain. + if (last_inlined_at_in_chain != nullptr) { + SetInlinedOperand(last_inlined_at_in_chain, + new_inlined_at_in_chain->result_id()); + } + last_inlined_at_in_chain = new_inlined_at_in_chain; + + chain_iter_id = GetInlinedOperand(new_inlined_at_in_chain); + } while (chain_iter_id != kNoInlinedAt); + + // Put |new_dbg_inlined_at_id| into the end of the chain. + SetInlinedOperand(last_inlined_at_in_chain, new_dbg_inlined_at_id); + + // Keep the new chain information that will be reused it. + inlined_at_ctx->SetDebugInlinedAtChain(callee_inlined_at, chain_head_id); + return chain_head_id; +} + +Instruction* DebugInfoManager::GetDebugOperationWithDeref() { + if (deref_operation_ != nullptr) return deref_operation_; + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr deref_operation(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {SPV_OPERAND_TYPE_ID, + {context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugOperation)}}, + {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION, + {static_cast(OpenCLDebugInfo100Deref)}}, + })); + + // Add to the front of |ext_inst_debuginfo_|. + deref_operation_ = + context()->module()->ext_inst_debuginfo_begin()->InsertBefore( + std::move(deref_operation)); + + RegisterDbgInst(deref_operation_); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_); + return deref_operation_; +} + +Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) { + assert(dbg_expr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugExpression); + std::unique_ptr deref_expr(dbg_expr->Clone(context())); + deref_expr->SetResultId(context()->TakeNextId()); + deref_expr->InsertOperand( + kDebugExpressOperandOperationIndex, + {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}}); + auto* deref_expr_instr = + context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr)); + AnalyzeDebugInst(deref_expr_instr); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr); + return deref_expr_instr; +} + +Instruction* DebugInfoManager::GetDebugInfoNone() { + if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_; + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr dbg_info_none_inst(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugInfoNone)}}, + })); + + // Add to the front of |ext_inst_debuginfo_|. + debug_info_none_inst_ = + context()->module()->ext_inst_debuginfo_begin()->InsertBefore( + std::move(dbg_info_none_inst)); + + RegisterDbgInst(debug_info_none_inst_); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(debug_info_none_inst_); + return debug_info_none_inst_; +} + +Instruction* DebugInfoManager::GetEmptyDebugExpression() { + if (empty_debug_expr_inst_ != nullptr) return empty_debug_expr_inst_; + + uint32_t result_id = context()->TakeNextId(); + std::unique_ptr empty_debug_expr(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugExpression)}}, + })); + + // Add to the front of |ext_inst_debuginfo_|. + empty_debug_expr_inst_ = + context()->module()->ext_inst_debuginfo_begin()->InsertBefore( + std::move(empty_debug_expr)); + + RegisterDbgInst(empty_debug_expr_inst_); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(empty_debug_expr_inst_); + return empty_debug_expr_inst_; +} + +Instruction* DebugInfoManager::GetDebugInlinedAt(uint32_t dbg_inlined_at_id) { + auto* inlined_at = GetDbgInst(dbg_inlined_at_id); + if (inlined_at == nullptr) return nullptr; + if (inlined_at->GetOpenCL100DebugOpcode() != + OpenCLDebugInfo100DebugInlinedAt) { + return nullptr; + } + return inlined_at; +} + +Instruction* DebugInfoManager::CloneDebugInlinedAt(uint32_t clone_inlined_at_id, + Instruction* insert_before) { + auto* inlined_at = GetDebugInlinedAt(clone_inlined_at_id); + if (inlined_at == nullptr) return nullptr; + std::unique_ptr new_inlined_at(inlined_at->Clone(context())); + new_inlined_at->SetResultId(context()->TakeNextId()); + RegisterDbgInst(new_inlined_at.get()); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(new_inlined_at.get()); + if (insert_before != nullptr) + return insert_before->InsertBefore(std::move(new_inlined_at)); + return context()->module()->ext_inst_debuginfo_end()->InsertBefore( + std::move(new_inlined_at)); +} + +bool DebugInfoManager::IsVariableDebugDeclared(uint32_t variable_id) { + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + return dbg_decl_itr != var_id_to_dbg_decl_.end(); +} + +bool DebugInfoManager::KillDebugDeclares(uint32_t variable_id) { + bool modified = false; + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { + // We intentionally copy the list of DebugDeclare instructions because + // context()->KillInst(dbg_decl) will update |var_id_to_dbg_decl_|. If we + // directly use |dbg_decl_itr->second|, it accesses a dangling pointer. + auto copy_dbg_decls = dbg_decl_itr->second; + + for (auto* dbg_decl : copy_dbg_decls) { + context()->KillInst(dbg_decl); + modified = true; + } + var_id_to_dbg_decl_.erase(dbg_decl_itr); + } + return modified; +} + +uint32_t DebugInfoManager::GetParentScope(uint32_t child_scope) { + auto dbg_scope_itr = id_to_dbg_inst_.find(child_scope); + assert(dbg_scope_itr != id_to_dbg_inst_.end()); + OpenCLDebugInfo100Instructions debug_opcode = + dbg_scope_itr->second->GetOpenCL100DebugOpcode(); + uint32_t parent_scope = kNoDebugScope; + switch (debug_opcode) { + case OpenCLDebugInfo100DebugFunction: + parent_scope = dbg_scope_itr->second->GetSingleWordOperand( + kDebugFunctionOperandParentIndex); + break; + case OpenCLDebugInfo100DebugLexicalBlock: + parent_scope = dbg_scope_itr->second->GetSingleWordOperand( + kDebugLexicalBlockOperandParentIndex); + break; + case OpenCLDebugInfo100DebugTypeComposite: + parent_scope = dbg_scope_itr->second->GetSingleWordOperand( + kDebugTypeCompositeOperandParentIndex); + break; + case OpenCLDebugInfo100DebugCompilationUnit: + // DebugCompilationUnit does not have a parent scope. + break; + default: + assert(false && + "Unreachable. A debug scope instruction must be " + "DebugFunction, DebugTypeComposite, DebugLexicalBlock, " + "or DebugCompilationUnit."); + break; + } + return parent_scope; +} + +bool DebugInfoManager::IsAncestorOfScope(uint32_t scope, uint32_t ancestor) { + uint32_t ancestor_scope_itr = scope; + while (ancestor_scope_itr != kNoDebugScope) { + if (ancestor == ancestor_scope_itr) return true; + ancestor_scope_itr = GetParentScope(ancestor_scope_itr); + } + return false; +} + +bool DebugInfoManager::IsDeclareVisibleToInstr(Instruction* dbg_declare, + Instruction* scope) { + assert(dbg_declare != nullptr); + assert(scope != nullptr); + + std::vector scope_ids; + if (scope->opcode() == SpvOpPhi) { + scope_ids.push_back(scope->GetDebugScope().GetLexicalScope()); + for (uint32_t i = 0; i < scope->NumInOperands(); i += 2) { + auto* value = context()->get_def_use_mgr()->GetDef( + scope->GetSingleWordInOperand(i)); + if (value != nullptr) + scope_ids.push_back(value->GetDebugScope().GetLexicalScope()); + } + } else { + scope_ids.push_back(scope->GetDebugScope().GetLexicalScope()); + } + + uint32_t dbg_local_var_id = + dbg_declare->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex); + auto dbg_local_var_itr = id_to_dbg_inst_.find(dbg_local_var_id); + assert(dbg_local_var_itr != id_to_dbg_inst_.end()); + uint32_t decl_scope_id = dbg_local_var_itr->second->GetSingleWordOperand( + kDebugLocalVariableOperandParentIndex); + + // If the scope of DebugDeclare is an ancestor scope of the instruction's + // scope, the local variable is visible to the instruction. + for (uint32_t scope_id : scope_ids) { + if (scope_id != kNoDebugScope && + IsAncestorOfScope(scope_id, decl_scope_id)) { + return true; + } + } + return false; +} + +Instruction* DebugInfoManager::AddDebugValueWithIndex( + uint32_t dbg_local_var_id, uint32_t value_id, uint32_t expr_id, + uint32_t index_id, Instruction* insert_before) { + uint32_t result_id = context()->TakeNextId(); + if (!result_id) return nullptr; + std::unique_ptr new_dbg_value(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + result_id, + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugValue)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {dbg_local_var_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {value_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {expr_id == 0 ? GetEmptyDebugExpression()->result_id() : expr_id}}, + })); + if (index_id) { + new_dbg_value->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {index_id}}); + } + + Instruction* added_dbg_value = + insert_before->InsertBefore(std::move(new_dbg_value)); + AnalyzeDebugInst(added_dbg_value); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_value); + if (context()->AreAnalysesValid( + IRContext::Analysis::kAnalysisInstrToBlockMapping)) { + auto insert_blk = context()->get_instr_block(insert_before); + context()->set_instr_block(added_dbg_value, insert_blk); + } + return added_dbg_value; +} + +bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible( + Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id, + Instruction* insert_pos, + std::unordered_set* invisible_decls) { + assert(scope_and_line != nullptr); + + auto dbg_decl_itr = var_id_to_dbg_decl_.find(variable_id); + if (dbg_decl_itr == var_id_to_dbg_decl_.end()) return false; + + bool modified = false; + for (auto* dbg_decl_or_val : dbg_decl_itr->second) { + if (!IsDeclareVisibleToInstr(dbg_decl_or_val, scope_and_line)) { + if (invisible_decls) invisible_decls->insert(dbg_decl_or_val); + continue; + } + + // Avoid inserting the new DebugValue between OpPhi or OpVariable + // instructions. + Instruction* insert_before = insert_pos->NextNode(); + while (insert_before->opcode() == SpvOpPhi || + insert_before->opcode() == SpvOpVariable) { + insert_before = insert_before->NextNode(); + } + + uint32_t index_id = 0; + if (dbg_decl_or_val->NumOperands() > kDebugValueOperandIndexesIndex) { + index_id = + dbg_decl_or_val->GetSingleWordOperand(kDebugValueOperandIndexesIndex); + } + + Instruction* added_dbg_value = + AddDebugValueWithIndex(dbg_decl_or_val->GetSingleWordOperand( + kDebugValueOperandLocalVariableIndex), + value_id, 0, index_id, insert_before); + assert(added_dbg_value != nullptr); + added_dbg_value->UpdateDebugInfoFrom(scope_and_line); + AnalyzeDebugInst(added_dbg_value); + modified = true; + } + return modified; +} + +bool DebugInfoManager::AddDebugValueForDecl(Instruction* dbg_decl, + uint32_t value_id) { + if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return false; + + std::unique_ptr dbg_val(dbg_decl->Clone(context())); + dbg_val->SetResultId(context()->TakeNextId()); + dbg_val->SetInOperand(kExtInstInstructionInIdx, + {OpenCLDebugInfo100DebugValue}); + dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id}); + dbg_val->SetOperand(kDebugValueOperandExpressionIndex, + {GetEmptyDebugExpression()->result_id()}); + + auto* added_dbg_val = dbg_decl->InsertBefore(std::move(dbg_val)); + AnalyzeDebugInst(added_dbg_val); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_val); + if (context()->AreAnalysesValid( + IRContext::Analysis::kAnalysisInstrToBlockMapping)) { + auto insert_blk = context()->get_instr_block(dbg_decl); + context()->set_instr_block(added_dbg_val, insert_blk); + } + return true; +} + +uint32_t DebugInfoManager::GetVariableIdOfDebugValueUsedForDeclare( + Instruction* inst) { + if (inst->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugValue) return 0; + + auto* expr = + GetDbgInst(inst->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); + if (expr == nullptr) return 0; + if (expr->NumOperands() != kDebugExpressOperandOperationIndex + 1) return 0; + + auto* operation = GetDbgInst( + expr->GetSingleWordOperand(kDebugExpressOperandOperationIndex)); + if (operation == nullptr) return 0; + if (operation->GetSingleWordOperand(kDebugOperationOperandOperationIndex) != + OpenCLDebugInfo100Deref) { + return 0; + } + + uint32_t var_id = + inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + if (!context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) { + assert(false && + "Checking a DebugValue can be used for declare needs DefUseManager"); + return 0; + } + + auto* var = context()->get_def_use_mgr()->GetDef(var_id); + if (var->opcode() == SpvOpVariable && + SpvStorageClass(var->GetSingleWordOperand( + kOpVariableOperandStorageClassIndex)) == SpvStorageClassFunction) { + return var_id; + } + return 0; +} + +bool DebugInfoManager::IsDebugDeclare(Instruction* instr) { + if (!instr->IsOpenCL100DebugInstr()) return false; + return instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || + GetVariableIdOfDebugValueUsedForDeclare(instr) != 0; +} + +void DebugInfoManager::ReplaceAllUsesInDebugScopeWithPredicate( + uint32_t before, uint32_t after, + const std::function& predicate) { + auto scope_id_to_users_itr = scope_id_to_users_.find(before); + if (scope_id_to_users_itr != scope_id_to_users_.end()) { + for (Instruction* inst : scope_id_to_users_itr->second) { + if (predicate(inst)) inst->UpdateLexicalScope(after); + } + scope_id_to_users_[after] = scope_id_to_users_itr->second; + scope_id_to_users_.erase(scope_id_to_users_itr); + } + auto inlinedat_id_to_users_itr = inlinedat_id_to_users_.find(before); + if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { + for (Instruction* inst : inlinedat_id_to_users_itr->second) { + if (predicate(inst)) inst->UpdateDebugInlinedAt(after); + } + inlinedat_id_to_users_[after] = inlinedat_id_to_users_itr->second; + inlinedat_id_to_users_.erase(inlinedat_id_to_users_itr); + } +} + +void DebugInfoManager::ClearDebugScopeAndInlinedAtUses(Instruction* inst) { + auto scope_id_to_users_itr = scope_id_to_users_.find(inst->result_id()); + if (scope_id_to_users_itr != scope_id_to_users_.end()) { + scope_id_to_users_.erase(scope_id_to_users_itr); + } + auto inlinedat_id_to_users_itr = + inlinedat_id_to_users_.find(inst->result_id()); + if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { + inlinedat_id_to_users_.erase(inlinedat_id_to_users_itr); + } +} + +void DebugInfoManager::AnalyzeDebugInst(Instruction* inst) { + if (inst->GetDebugScope().GetLexicalScope() != kNoDebugScope) { + auto& users = scope_id_to_users_[inst->GetDebugScope().GetLexicalScope()]; + users.insert(inst); + } + if (inst->GetDebugInlinedAt() != kNoInlinedAt) { + auto& users = inlinedat_id_to_users_[inst->GetDebugInlinedAt()]; + users.insert(inst); + } + + if (!inst->IsOpenCL100DebugInstr()) return; + + RegisterDbgInst(inst); + + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + assert(GetDebugFunction(inst->GetSingleWordOperand( + kDebugFunctionOperandFunctionIndex)) == nullptr && + "Two DebugFunction instruction exists for a single OpFunction."); + RegisterDbgFunction(inst); + } + + if (deref_operation_ == nullptr && + inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation && + inst->GetSingleWordOperand(kDebugOperationOperandOperationIndex) == + OpenCLDebugInfo100Deref) { + deref_operation_ = inst; + } + + if (debug_info_none_inst_ == nullptr && + inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) { + debug_info_none_inst_ = inst; + } + + if (empty_debug_expr_inst_ == nullptr && IsEmptyDebugExpression(inst)) { + empty_debug_expr_inst_ = inst; + } + + if (inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + uint32_t var_id = + inst->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + RegisterDbgDeclare(var_id, inst); + } + + if (uint32_t var_id = GetVariableIdOfDebugValueUsedForDeclare(inst)) { + RegisterDbgDeclare(var_id, inst); + } +} + +void DebugInfoManager::ConvertDebugGlobalToLocalVariable( + Instruction* dbg_global_var, Instruction* local_var) { + if (dbg_global_var->GetOpenCL100DebugOpcode() != + OpenCLDebugInfo100DebugGlobalVariable) { + return; + } + assert(local_var->opcode() == SpvOpVariable || + local_var->opcode() == SpvOpFunctionParameter); + + // Convert |dbg_global_var| to DebugLocalVariable + dbg_global_var->SetInOperand(kExtInstInstructionInIdx, + {OpenCLDebugInfo100DebugLocalVariable}); + auto flags = dbg_global_var->GetSingleWordOperand( + kDebugGlobalVariableOperandFlagsIndex); + for (uint32_t i = dbg_global_var->NumInOperands() - 1; + i >= kDebugLocalVariableOperandFlagsIndex; --i) { + dbg_global_var->RemoveOperand(i); + } + dbg_global_var->SetOperand(kDebugLocalVariableOperandFlagsIndex, {flags}); + context()->ForgetUses(dbg_global_var); + context()->AnalyzeUses(dbg_global_var); + + // Create a DebugDeclare + std::unique_ptr new_dbg_decl(new Instruction( + context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(), + context()->TakeNextId(), + { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {context() + ->get_feature_mgr() + ->GetExtInstImportId_OpenCL100DebugInfo()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {static_cast(OpenCLDebugInfo100DebugDeclare)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {dbg_global_var->result_id()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {local_var->result_id()}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {GetEmptyDebugExpression()->result_id()}}, + })); + auto* added_dbg_decl = + local_var->NextNode()->InsertBefore(std::move(new_dbg_decl)); + if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse)) + context()->get_def_use_mgr()->AnalyzeInstDefUse(added_dbg_decl); + if (context()->AreAnalysesValid( + IRContext::Analysis::kAnalysisInstrToBlockMapping)) { + auto insert_blk = context()->get_instr_block(local_var); + context()->set_instr_block(added_dbg_decl, insert_blk); + } +} + +void DebugInfoManager::AnalyzeDebugInsts(Module& module) { + deref_operation_ = nullptr; + debug_info_none_inst_ = nullptr; + empty_debug_expr_inst_ = nullptr; + module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); }); + + // Move |empty_debug_expr_inst_| to the beginning of the debug instruction + // list. + if (empty_debug_expr_inst_ != nullptr && + empty_debug_expr_inst_->PreviousNode() != nullptr && + empty_debug_expr_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { + empty_debug_expr_inst_->InsertBefore( + &*context()->module()->ext_inst_debuginfo_begin()); + } + + // Move |debug_info_none_inst_| to the beginning of the debug instruction + // list. + if (debug_info_none_inst_ != nullptr && + debug_info_none_inst_->PreviousNode() != nullptr && + debug_info_none_inst_->PreviousNode()->IsOpenCL100DebugInstr()) { + debug_info_none_inst_->InsertBefore( + &*context()->module()->ext_inst_debuginfo_begin()); + } +} + +void DebugInfoManager::ClearDebugInfo(Instruction* instr) { + auto scope_id_to_users_itr = + scope_id_to_users_.find(instr->GetDebugScope().GetLexicalScope()); + if (scope_id_to_users_itr != scope_id_to_users_.end()) { + scope_id_to_users_itr->second.erase(instr); + } + auto inlinedat_id_to_users_itr = + inlinedat_id_to_users_.find(instr->GetDebugInlinedAt()); + if (inlinedat_id_to_users_itr != inlinedat_id_to_users_.end()) { + inlinedat_id_to_users_itr->second.erase(instr); + } + + if (instr == nullptr || !instr->IsOpenCL100DebugInstr()) { + return; + } + + id_to_dbg_inst_.erase(instr->result_id()); + + if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugFunction) { + auto fn_id = + instr->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex); + fn_id_to_dbg_fn_.erase(fn_id); + } + + if (instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || + instr->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + auto var_or_value_id = + instr->GetSingleWordOperand(kDebugDeclareOperandVariableIndex); + auto dbg_decl_itr = var_id_to_dbg_decl_.find(var_or_value_id); + if (dbg_decl_itr != var_id_to_dbg_decl_.end()) { + dbg_decl_itr->second.erase(instr); + } + } + + if (deref_operation_ == instr) { + deref_operation_ = nullptr; + for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); + dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); + ++dbg_instr_itr) { + if (instr != &*dbg_instr_itr && + dbg_instr_itr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugOperation && + dbg_instr_itr->GetSingleWordOperand( + kDebugOperationOperandOperationIndex) == + OpenCLDebugInfo100Deref) { + deref_operation_ = &*dbg_instr_itr; + break; + } + } + } + + if (debug_info_none_inst_ == instr) { + debug_info_none_inst_ = nullptr; + for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); + dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); + ++dbg_instr_itr) { + if (instr != &*dbg_instr_itr && + dbg_instr_itr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugInfoNone) { + debug_info_none_inst_ = &*dbg_instr_itr; + break; + } + } + } + + if (empty_debug_expr_inst_ == instr) { + empty_debug_expr_inst_ = nullptr; + for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin(); + dbg_instr_itr != context()->module()->ext_inst_debuginfo_end(); + ++dbg_instr_itr) { + if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) { + empty_debug_expr_inst_ = &*dbg_instr_itr; + break; + } + } + } +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/debug_info_manager.h b/third_party/spirv-tools/source/opt/debug_info_manager.h new file mode 100644 index 0000000..630be4f --- /dev/null +++ b/third_party/spirv-tools/source/opt/debug_info_manager.h @@ -0,0 +1,273 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEBUG_INFO_MANAGER_H_ +#define SOURCE_OPT_DEBUG_INFO_MANAGER_H_ + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +// When an instruction of a callee function is inlined to its caller function, +// we need the line and the scope information of the function call instruction +// to generate DebugInlinedAt. This class keeps the data. For multiple inlining +// of a single instruction, we have to create multiple DebugInlinedAt +// instructions as a chain. This class keeps the information of the generated +// DebugInlinedAt chains to reduce the number of chains. +class DebugInlinedAtContext { + public: + explicit DebugInlinedAtContext(Instruction* call_inst) + : call_inst_line_(call_inst->dbg_line_inst()), + call_inst_scope_(call_inst->GetDebugScope()) {} + + const Instruction* GetLineOfCallInstruction() { return call_inst_line_; } + const DebugScope& GetScopeOfCallInstruction() { return call_inst_scope_; } + // Puts the DebugInlinedAt chain that is generated for the callee instruction + // whose DebugInlinedAt of DebugScope is |callee_instr_inlined_at| into + // |callee_inlined_at2chain_|. + void SetDebugInlinedAtChain(uint32_t callee_instr_inlined_at, + uint32_t chain_head_id) { + callee_inlined_at2chain_[callee_instr_inlined_at] = chain_head_id; + } + // Gets the DebugInlinedAt chain from |callee_inlined_at2chain_|. + uint32_t GetDebugInlinedAtChain(uint32_t callee_instr_inlined_at) { + auto chain_itr = callee_inlined_at2chain_.find(callee_instr_inlined_at); + if (chain_itr != callee_inlined_at2chain_.end()) return chain_itr->second; + return kNoInlinedAt; + } + + private: + // The line information of the function call instruction that will be + // replaced by the callee function. + const Instruction* call_inst_line_; + + // The scope information of the function call instruction that will be + // replaced by the callee function. + const DebugScope call_inst_scope_; + + // Map from DebugInlinedAt ids of callee to head ids of new generated + // DebugInlinedAt chain. + std::unordered_map callee_inlined_at2chain_; +}; + +// A class for analyzing, managing, and creating OpenCL.DebugInfo.100 extension +// instructions. +class DebugInfoManager { + public: + // Constructs a debug information manager from the given |context|. + DebugInfoManager(IRContext* context); + + DebugInfoManager(const DebugInfoManager&) = delete; + DebugInfoManager(DebugInfoManager&&) = delete; + DebugInfoManager& operator=(const DebugInfoManager&) = delete; + DebugInfoManager& operator=(DebugInfoManager&&) = delete; + + friend bool operator==(const DebugInfoManager&, const DebugInfoManager&); + friend bool operator!=(const DebugInfoManager& lhs, + const DebugInfoManager& rhs) { + return !(lhs == rhs); + } + + // Analyzes OpenCL.DebugInfo.100 instruction |dbg_inst|. + void AnalyzeDebugInst(Instruction* dbg_inst); + + // Creates new DebugInlinedAt and returns its id. Its line operand is the + // line number of |line| if |line| is not nullptr. Otherwise, its line operand + // is the line number of lexical scope of |scope|. Its Scope and Inlined + // operands are Scope and Inlined of |scope|. + uint32_t CreateDebugInlinedAt(const Instruction* line, + const DebugScope& scope); + + // Clones DebugExpress instruction |dbg_expr| and add Deref Operation + // in the front of the Operation list of |dbg_expr|. + Instruction* DerefDebugExpression(Instruction* dbg_expr); + + // Returns a DebugInfoNone instruction. + Instruction* GetDebugInfoNone(); + + // Returns DebugInlinedAt whose id is |dbg_inlined_at_id|. If it does not + // exist or it is not a DebugInlinedAt instruction, return nullptr. + Instruction* GetDebugInlinedAt(uint32_t dbg_inlined_at_id); + + // Returns DebugFunction whose Function operand is |fn_id|. If it does not + // exist, return nullptr. + Instruction* GetDebugFunction(uint32_t fn_id) { + auto dbg_fn_it = fn_id_to_dbg_fn_.find(fn_id); + return dbg_fn_it == fn_id_to_dbg_fn_.end() ? nullptr : dbg_fn_it->second; + } + + // Clones DebugInlinedAt whose id is |clone_inlined_at_id|. If + // |clone_inlined_at_id| is not an id of DebugInlinedAt, returns nullptr. + // If |insert_before| is given, inserts the new DebugInlinedAt before it. + // Otherwise, inserts the new DebugInlinedAt into the debug instruction + // section of the module. + Instruction* CloneDebugInlinedAt(uint32_t clone_inlined_at_id, + Instruction* insert_before = nullptr); + + // Returns the debug scope corresponding to an inlining instruction in the + // scope |callee_instr_scope| into |inlined_at_ctx|. Generates all new + // debug instructions needed to represent the scope. + DebugScope BuildDebugScope(const DebugScope& callee_instr_scope, + DebugInlinedAtContext* inlined_at_ctx); + + // Returns DebugInlinedAt corresponding to inlining an instruction, which + // was inlined at |callee_inlined_at|, into |inlined_at_ctx|. Generates all + // new debug instructions needed to represent the DebugInlinedAt. + uint32_t BuildDebugInlinedAtChain(uint32_t callee_inlined_at, + DebugInlinedAtContext* inlined_at_ctx); + + // Returns true if there is a debug declaration instruction whose + // 'Local Variable' operand is |variable_id|. + bool IsVariableDebugDeclared(uint32_t variable_id); + + // Kills all debug declaration instructions with Deref whose 'Local Variable' + // operand is |variable_id|. Returns whether it kills an instruction or not. + bool KillDebugDeclares(uint32_t variable_id); + + // Generates a DebugValue instruction with value |value_id| for every local + // variable that is in the scope of |scope_and_line| and whose memory is + // |variable_id| and inserts it after the instruction |insert_pos|. + // Returns whether a DebugValue is added or not. |invisible_decls| returns + // DebugDeclares invisible to |scope_and_line|. + bool AddDebugValueIfVarDeclIsVisible( + Instruction* scope_and_line, uint32_t variable_id, uint32_t value_id, + Instruction* insert_pos, + std::unordered_set* invisible_decls); + + // Generates a DebugValue instruction with |dbg_local_var_id|, |value_id|, + // |expr_id|, |index_id| operands and inserts it before |insert_before|. + Instruction* AddDebugValueWithIndex(uint32_t dbg_local_var_id, + uint32_t value_id, uint32_t expr_id, + uint32_t index_id, + Instruction* insert_before); + + // Adds DebugValue for DebugDeclare |dbg_decl|. The new DebugValue has the + // same line, scope, and operands but it uses |value_id| for value. Returns + // weather it succeeds or not. + bool AddDebugValueForDecl(Instruction* dbg_decl, uint32_t value_id); + + // Erases |instr| from data structures of this class. + void ClearDebugInfo(Instruction* instr); + + // Returns the id of Value operand if |inst| is DebugValue who has Deref + // operation and its Value operand is a result id of OpVariable with + // Function storage class. Otherwise, returns 0. + uint32_t GetVariableIdOfDebugValueUsedForDeclare(Instruction* inst); + + // Converts DebugGlobalVariable |dbg_global_var| to a DebugLocalVariable and + // creates a DebugDeclare mapping the new DebugLocalVariable to |local_var|. + void ConvertDebugGlobalToLocalVariable(Instruction* dbg_global_var, + Instruction* local_var); + + // Returns true if |instr| is a debug declaration instruction. + bool IsDebugDeclare(Instruction* instr); + + // Replace all uses of |before| id that is an operand of a DebugScope with + // |after| id if those uses (instruction) return true for |predicate|. + void ReplaceAllUsesInDebugScopeWithPredicate( + uint32_t before, uint32_t after, + const std::function& predicate); + + // Removes uses of DebugScope |inst| from |scope_id_to_users_| or uses of + // DebugInlinedAt |inst| from |inlinedat_id_to_users_|. + void ClearDebugScopeAndInlinedAtUses(Instruction* inst); + + private: + IRContext* context() { return context_; } + + // Analyzes OpenCL.DebugInfo.100 instructions in the given |module| and + // populates data structures in this class. + void AnalyzeDebugInsts(Module& module); + + // Returns the debug instruction whose id is |id|. Returns |nullptr| if one + // does not exists. + Instruction* GetDbgInst(uint32_t id); + + // Returns a DebugOperation instruction with OpCode Deref. + Instruction* GetDebugOperationWithDeref(); + + // Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of + // |inst| as a key. + void RegisterDbgInst(Instruction* inst); + + // Register the DebugFunction instruction |inst|. The function referenced + // in |inst| must not already be registered. + void RegisterDbgFunction(Instruction* inst); + + // Register the DebugDeclare or DebugValue with Deref operation + // |dbg_declare| into |var_id_to_dbg_decl_| using OpVariable id + // |var_id| as a key. + void RegisterDbgDeclare(uint32_t var_id, Instruction* dbg_declare); + + // Returns a DebugExpression instruction without Operation operands. + Instruction* GetEmptyDebugExpression(); + + // Returns true if a scope |ancestor| is |scope| or an ancestor scope + // of |scope|. + bool IsAncestorOfScope(uint32_t scope, uint32_t ancestor); + + // Returns true if the declaration of a local variable |dbg_declare| + // is visible in the scope of an instruction |instr_scope_id|. + bool IsDeclareVisibleToInstr(Instruction* dbg_declare, Instruction* scope); + + // Returns the parent scope of the scope |child_scope|. + uint32_t GetParentScope(uint32_t child_scope); + + IRContext* context_; + + // Mapping from ids of OpenCL.DebugInfo.100 extension instructions + // to their Instruction instances. + std::unordered_map id_to_dbg_inst_; + + // Mapping from function's ids to DebugFunction instructions whose + // operand is the function. + std::unordered_map fn_id_to_dbg_fn_; + + // Mapping from variable or value ids to DebugDeclare or DebugValue + // instructions whose operand is the variable or value. + std::unordered_map> + var_id_to_dbg_decl_; + + // Mapping from DebugScope ids to users. + std::unordered_map> + scope_id_to_users_; + + // Mapping from DebugInlinedAt ids to users. + std::unordered_map> + inlinedat_id_to_users_; + + // DebugOperation whose OpCode is OpenCLDebugInfo100Deref. + Instruction* deref_operation_; + + // DebugInfoNone instruction. We need only a single DebugInfoNone. + // To reuse the existing one, we keep it using this member variable. + Instruction* debug_info_none_inst_; + + // DebugExpression instruction without Operation operands. We need only + // a single DebugExpression without Operation operands. To reuse the + // existing one, we keep it using this member variable. + Instruction* empty_debug_expr_inst_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEBUG_INFO_MANAGER_H_ diff --git a/third_party/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp b/third_party/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp new file mode 100644 index 0000000..875bf7e --- /dev/null +++ b/third_party/spirv-tools/source/opt/decompose_initialized_variables_pass.cpp @@ -0,0 +1,112 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/decompose_initialized_variables_pass.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +using inst_iterator = InstructionList::iterator; + +namespace { + +bool HasInitializer(Instruction* inst) { + if (inst->opcode() != SpvOpVariable) return false; + if (inst->NumOperands() < 4) return false; + + return true; +} + +} // namespace + +Pass::Status DecomposeInitializedVariablesPass::Process() { + auto* module = context()->module(); + std::unordered_set changed; + + std::vector> global_stores; + for (auto iter = module->types_values_begin(); + iter != module->types_values_end(); ++iter) { + Instruction* inst = &(*iter); + if (!HasInitializer(inst)) continue; + + auto var_id = inst->result_id(); + auto val_id = inst->GetOperand(3).words[0]; + global_stores.push_back(std::make_tuple(var_id, val_id)); + iter->RemoveOperand(3); + changed.insert(&*iter); + } + + std::unordered_set entry_ids; + for (auto entry = module->entry_points().begin(); + entry != module->entry_points().end(); ++entry) { + entry_ids.insert(entry->GetSingleWordInOperand(1)); + } + + for (auto func = module->begin(); func != module->end(); ++func) { + std::vector function_stores; + auto first_block = func->entry().get(); + inst_iterator insert_point = first_block->begin(); + for (auto iter = first_block->begin(); + iter != first_block->end() && iter->opcode() == SpvOpVariable; + ++iter) { + // For valid SPIRV-V, there is guaranteed to be at least one instruction + // after the OpVariable instructions. + insert_point = (*iter).NextNode(); + Instruction* inst = &(*iter); + if (!HasInitializer(inst)) continue; + + auto var_id = inst->result_id(); + auto val_id = inst->GetOperand(3).words[0]; + Instruction* store_inst = new Instruction( + context(), SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}}); + function_stores.push_back(store_inst); + iter->RemoveOperand(3); + changed.insert(&*iter); + } + + if (entry_ids.find(func->result_id()) != entry_ids.end()) { + for (auto store_ids : global_stores) { + uint32_t var_id; + uint32_t val_id; + std::tie(var_id, val_id) = store_ids; + auto* store_inst = new Instruction( + context(), SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {var_id}}, {SPV_OPERAND_TYPE_ID, {val_id}}}); + context()->set_instr_block(store_inst, &*first_block); + first_block->AddInstruction(std::unique_ptr(store_inst)); + store_inst->InsertBefore(&*insert_point); + changed.insert(store_inst); + } + } + + for (auto store = function_stores.begin(); store != function_stores.end(); + ++store) { + context()->set_instr_block(*store, first_block); + (*store)->InsertBefore(&*insert_point); + changed.insert(*store); + } + } + + auto* def_use_mgr = get_def_use_mgr(); + for (auto* inst : changed) def_use_mgr->UpdateDefUse(inst); + + return !changed.empty() ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/decompose_initialized_variables_pass.h b/third_party/spirv-tools/source/opt/decompose_initialized_variables_pass.h new file mode 100644 index 0000000..c0bd35e --- /dev/null +++ b/third_party/spirv-tools/source/opt/decompose_initialized_variables_pass.h @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ +#define SOURCE_OPT_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Converts variable declartions with initializers into seperate declaration and +// assignment statements. This is done due to known issues with some Vulkan +// implementations' handling of initialized variables. +// +// Only decomposes variables with storage classes that are valid in Vulkan +// execution environments; Output, Private, and Function. +// Currently only Function is implemented. +class DecomposeInitializedVariablesPass : public Pass { + public: + const char* name() const override { + return "decompose-initialized-variables"; + } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DECOMPOSE_INITALIZED_VAIRABLES_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/decoration_manager.cpp b/third_party/spirv-tools/source/opt/decoration_manager.cpp new file mode 100644 index 0000000..a10c992 --- /dev/null +++ b/third_party/spirv-tools/source/opt/decoration_manager.cpp @@ -0,0 +1,618 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/decoration_manager.h" + +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" + +namespace { +using InstructionVector = std::vector; +using DecorationSet = std::set; + +// Returns true if |a| is a subet of |b|. +bool IsSubset(const DecorationSet& a, const DecorationSet& b) { + auto it1 = a.begin(); + auto it2 = b.begin(); + + while (it1 != a.end()) { + if (it2 == b.end() || *it1 < *it2) { + // |*it1| is in |a|, but not in |b|. + return false; + } + if (*it1 == *it2) { + // Found the element move to the next one. + it1++; + it2++; + } else /* *it1 > *it2 */ { + // Did not find |*it1| yet, check the next element in |b|. + it2++; + } + } + return true; +} +} // namespace + +namespace spvtools { +namespace opt { +namespace analysis { + +void DecorationManager::RemoveDecorationsFrom( + uint32_t id, std::function pred) { + const auto ids_iter = id_to_decoration_insts_.find(id); + if (ids_iter == id_to_decoration_insts_.end()) { + return; + } + + TargetData& decorations_info = ids_iter->second; + auto context = module_->context(); + std::vector insts_to_kill; + const bool is_group = !decorations_info.decorate_insts.empty(); + + // Schedule all direct decorations for removal if instructed as such by + // |pred|. + for (Instruction* inst : decorations_info.direct_decorations) + if (pred(*inst)) insts_to_kill.push_back(inst); + + // For all groups being directly applied to |id|, remove |id| (and the + // literal if |inst| is an OpGroupMemberDecorate) from the instruction + // applying the group. + std::unordered_set indirect_decorations_to_remove; + for (Instruction* inst : decorations_info.indirect_decorations) { + assert(inst->opcode() == SpvOpGroupDecorate || + inst->opcode() == SpvOpGroupMemberDecorate); + + std::vector group_decorations_to_keep; + const uint32_t group_id = inst->GetSingleWordInOperand(0u); + const auto group_iter = id_to_decoration_insts_.find(group_id); + assert(group_iter != id_to_decoration_insts_.end() && + "Unknown decoration group"); + const auto& group_decorations = group_iter->second.direct_decorations; + for (Instruction* decoration : group_decorations) { + if (!pred(*decoration)) group_decorations_to_keep.push_back(decoration); + } + + // If all decorations should be kept, then we can keep |id| part of the + // group. However, if the group itself has no decorations, we should remove + // the id from the group. This is needed to make |KillNameAndDecorate| work + // correctly when a decoration group has no decorations. + if (group_decorations_to_keep.size() == group_decorations.size() && + group_decorations.size() != 0) { + continue; + } + + // Otherwise, remove |id| from the targets of |group_id| + const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u; + bool was_modified = false; + for (uint32_t i = 1u; i < inst->NumInOperands();) { + if (inst->GetSingleWordInOperand(i) != id) { + i += stride; + continue; + } + + const uint32_t last_operand_index = inst->NumInOperands() - stride; + if (i < last_operand_index) + inst->GetInOperand(i) = inst->GetInOperand(last_operand_index); + // Remove the associated literal, if it exists. + if (stride == 2u) { + if (i < last_operand_index) + inst->GetInOperand(i + 1u) = + inst->GetInOperand(last_operand_index + 1u); + inst->RemoveInOperand(last_operand_index + 1u); + } + inst->RemoveInOperand(last_operand_index); + was_modified = true; + } + + // If the instruction has no targets left, remove the instruction + // altogether. + if (inst->NumInOperands() == 1u) { + indirect_decorations_to_remove.emplace(inst); + insts_to_kill.push_back(inst); + } else if (was_modified) { + context->ForgetUses(inst); + indirect_decorations_to_remove.emplace(inst); + context->AnalyzeUses(inst); + } + + // If only some of the decorations should be kept, clone them and apply + // them directly to |id|. + if (!group_decorations_to_keep.empty()) { + for (Instruction* decoration : group_decorations_to_keep) { + // simply clone decoration and change |group_id| to |id| + std::unique_ptr new_inst( + decoration->Clone(module_->context())); + new_inst->SetInOperand(0, {id}); + module_->AddAnnotationInst(std::move(new_inst)); + auto decoration_iter = --module_->annotation_end(); + context->AnalyzeUses(&*decoration_iter); + } + } + } + + auto& indirect_decorations = decorations_info.indirect_decorations; + indirect_decorations.erase( + std::remove_if( + indirect_decorations.begin(), indirect_decorations.end(), + [&indirect_decorations_to_remove](const Instruction* inst) { + return indirect_decorations_to_remove.count(inst); + }), + indirect_decorations.end()); + + for (Instruction* inst : insts_to_kill) context->KillInst(inst); + insts_to_kill.clear(); + + // Schedule all instructions applying the group for removal if this group no + // longer applies decorations, either directly or indirectly. + if (is_group && decorations_info.direct_decorations.empty() && + decorations_info.indirect_decorations.empty()) { + for (Instruction* inst : decorations_info.decorate_insts) + insts_to_kill.push_back(inst); + } + for (Instruction* inst : insts_to_kill) context->KillInst(inst); + + if (decorations_info.direct_decorations.empty() && + decorations_info.indirect_decorations.empty() && + decorations_info.decorate_insts.empty()) { + id_to_decoration_insts_.erase(ids_iter); + } +} + +std::vector DecorationManager::GetDecorationsFor( + uint32_t id, bool include_linkage) { + return InternalGetDecorationsFor(id, include_linkage); +} + +std::vector DecorationManager::GetDecorationsFor( + uint32_t id, bool include_linkage) const { + return const_cast(this) + ->InternalGetDecorationsFor(id, include_linkage); +} + +bool DecorationManager::HaveTheSameDecorations(uint32_t id1, + uint32_t id2) const { + const InstructionVector decorations_for1 = GetDecorationsFor(id1, false); + const InstructionVector decorations_for2 = GetDecorationsFor(id2, false); + + // This function splits the decoration instructions into different sets, + // based on their opcode; only OpDecorate, OpDecorateId, + // OpDecorateStringGOOGLE, and OpMemberDecorate are considered, the other + // opcodes are ignored. + const auto fillDecorationSets = + [](const InstructionVector& decoration_list, DecorationSet* decorate_set, + DecorationSet* decorate_id_set, DecorationSet* decorate_string_set, + DecorationSet* member_decorate_set) { + for (const Instruction* inst : decoration_list) { + std::u32string decoration_payload; + // Ignore the opcode and the target as we do not want them to be + // compared. + for (uint32_t i = 1u; i < inst->NumInOperands(); ++i) { + for (uint32_t word : inst->GetInOperand(i).words) { + decoration_payload.push_back(word); + } + } + + switch (inst->opcode()) { + case SpvOpDecorate: + decorate_set->emplace(std::move(decoration_payload)); + break; + case SpvOpMemberDecorate: + member_decorate_set->emplace(std::move(decoration_payload)); + break; + case SpvOpDecorateId: + decorate_id_set->emplace(std::move(decoration_payload)); + break; + case SpvOpDecorateStringGOOGLE: + decorate_string_set->emplace(std::move(decoration_payload)); + break; + default: + break; + } + } + }; + + DecorationSet decorate_set_for1; + DecorationSet decorate_id_set_for1; + DecorationSet decorate_string_set_for1; + DecorationSet member_decorate_set_for1; + fillDecorationSets(decorations_for1, &decorate_set_for1, + &decorate_id_set_for1, &decorate_string_set_for1, + &member_decorate_set_for1); + + DecorationSet decorate_set_for2; + DecorationSet decorate_id_set_for2; + DecorationSet decorate_string_set_for2; + DecorationSet member_decorate_set_for2; + fillDecorationSets(decorations_for2, &decorate_set_for2, + &decorate_id_set_for2, &decorate_string_set_for2, + &member_decorate_set_for2); + + const bool result = decorate_set_for1 == decorate_set_for2 && + decorate_id_set_for1 == decorate_id_set_for2 && + member_decorate_set_for1 == member_decorate_set_for2 && + // Compare string sets last in case the strings are long. + decorate_string_set_for1 == decorate_string_set_for2; + return result; +} + +bool DecorationManager::HaveSubsetOfDecorations(uint32_t id1, + uint32_t id2) const { + const InstructionVector decorations_for1 = GetDecorationsFor(id1, false); + const InstructionVector decorations_for2 = GetDecorationsFor(id2, false); + + // This function splits the decoration instructions into different sets, + // based on their opcode; only OpDecorate, OpDecorateId, + // OpDecorateStringGOOGLE, and OpMemberDecorate are considered, the other + // opcodes are ignored. + const auto fillDecorationSets = + [](const InstructionVector& decoration_list, DecorationSet* decorate_set, + DecorationSet* decorate_id_set, DecorationSet* decorate_string_set, + DecorationSet* member_decorate_set) { + for (const Instruction* inst : decoration_list) { + std::u32string decoration_payload; + // Ignore the opcode and the target as we do not want them to be + // compared. + for (uint32_t i = 1u; i < inst->NumInOperands(); ++i) { + for (uint32_t word : inst->GetInOperand(i).words) { + decoration_payload.push_back(word); + } + } + + switch (inst->opcode()) { + case SpvOpDecorate: + decorate_set->emplace(std::move(decoration_payload)); + break; + case SpvOpMemberDecorate: + member_decorate_set->emplace(std::move(decoration_payload)); + break; + case SpvOpDecorateId: + decorate_id_set->emplace(std::move(decoration_payload)); + break; + case SpvOpDecorateStringGOOGLE: + decorate_string_set->emplace(std::move(decoration_payload)); + break; + default: + break; + } + } + }; + + DecorationSet decorate_set_for1; + DecorationSet decorate_id_set_for1; + DecorationSet decorate_string_set_for1; + DecorationSet member_decorate_set_for1; + fillDecorationSets(decorations_for1, &decorate_set_for1, + &decorate_id_set_for1, &decorate_string_set_for1, + &member_decorate_set_for1); + + DecorationSet decorate_set_for2; + DecorationSet decorate_id_set_for2; + DecorationSet decorate_string_set_for2; + DecorationSet member_decorate_set_for2; + fillDecorationSets(decorations_for2, &decorate_set_for2, + &decorate_id_set_for2, &decorate_string_set_for2, + &member_decorate_set_for2); + + const bool result = + IsSubset(decorate_set_for1, decorate_set_for2) && + IsSubset(decorate_id_set_for1, decorate_id_set_for2) && + IsSubset(member_decorate_set_for1, member_decorate_set_for2) && + // Compare string sets last in case the strings are long. + IsSubset(decorate_string_set_for1, decorate_string_set_for2); + return result; +} + +// TODO(pierremoreau): If OpDecorateId is referencing an OpConstant, one could +// check that the constants are the same rather than just +// looking at the constant ID. +bool DecorationManager::AreDecorationsTheSame(const Instruction* inst1, + const Instruction* inst2, + bool ignore_target) const { + switch (inst1->opcode()) { + case SpvOpDecorate: + case SpvOpMemberDecorate: + case SpvOpDecorateId: + case SpvOpDecorateStringGOOGLE: + break; + default: + return false; + } + + if (inst1->opcode() != inst2->opcode() || + inst1->NumInOperands() != inst2->NumInOperands()) + return false; + + for (uint32_t i = ignore_target ? 1u : 0u; i < inst1->NumInOperands(); ++i) + if (inst1->GetInOperand(i) != inst2->GetInOperand(i)) return false; + + return true; +} + +void DecorationManager::AnalyzeDecorations() { + if (!module_) return; + + // For each group and instruction, collect all their decoration instructions. + for (Instruction& inst : module_->annotations()) { + AddDecoration(&inst); + } +} + +void DecorationManager::AddDecoration(Instruction* inst) { + switch (inst->opcode()) { + case SpvOpDecorate: + case SpvOpDecorateId: + case SpvOpDecorateStringGOOGLE: + case SpvOpMemberDecorate: { + const auto target_id = inst->GetSingleWordInOperand(0u); + id_to_decoration_insts_[target_id].direct_decorations.push_back(inst); + break; + } + case SpvOpGroupDecorate: + case SpvOpGroupMemberDecorate: { + const uint32_t start = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u; + const uint32_t stride = start; + for (uint32_t i = start; i < inst->NumInOperands(); i += stride) { + const auto target_id = inst->GetSingleWordInOperand(i); + TargetData& target_data = id_to_decoration_insts_[target_id]; + target_data.indirect_decorations.push_back(inst); + } + const auto target_id = inst->GetSingleWordInOperand(0u); + id_to_decoration_insts_[target_id].decorate_insts.push_back(inst); + break; + } + default: + break; + } +} + +void DecorationManager::AddDecoration(SpvOp opcode, + std::vector opnds) { + IRContext* ctx = module_->context(); + std::unique_ptr newDecoOp( + new Instruction(ctx, opcode, 0, 0, opnds)); + ctx->AddAnnotationInst(std::move(newDecoOp)); +} + +void DecorationManager::AddDecoration(uint32_t inst_id, uint32_t decoration) { + AddDecoration( + SpvOpDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}}); +} + +void DecorationManager::AddDecorationVal(uint32_t inst_id, uint32_t decoration, + uint32_t decoration_value) { + AddDecoration( + SpvOpDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {decoration_value}}}); +} + +void DecorationManager::AddMemberDecoration(uint32_t inst_id, uint32_t member, + uint32_t decoration, + uint32_t decoration_value) { + AddDecoration( + SpvOpMemberDecorate, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {inst_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {member}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {decoration_value}}}); +} + +template +std::vector DecorationManager::InternalGetDecorationsFor( + uint32_t id, bool include_linkage) { + std::vector decorations; + + const auto ids_iter = id_to_decoration_insts_.find(id); + // |id| has no decorations + if (ids_iter == id_to_decoration_insts_.end()) return decorations; + + const TargetData& target_data = ids_iter->second; + + const auto process_direct_decorations = + [include_linkage, + &decorations](const std::vector& direct_decorations) { + for (Instruction* inst : direct_decorations) { + const bool is_linkage = inst->opcode() == SpvOpDecorate && + inst->GetSingleWordInOperand(1u) == + SpvDecorationLinkageAttributes; + if (include_linkage || !is_linkage) decorations.push_back(inst); + } + }; + + // Process |id|'s decorations. + process_direct_decorations(ids_iter->second.direct_decorations); + + // Process the decorations of all groups applied to |id|. + for (const Instruction* inst : target_data.indirect_decorations) { + const uint32_t group_id = inst->GetSingleWordInOperand(0u); + const auto group_iter = id_to_decoration_insts_.find(group_id); + assert(group_iter != id_to_decoration_insts_.end() && "Unknown group ID"); + process_direct_decorations(group_iter->second.direct_decorations); + } + + return decorations; +} + +bool DecorationManager::WhileEachDecoration( + uint32_t id, uint32_t decoration, + std::function f) { + for (const Instruction* inst : GetDecorationsFor(id, true)) { + switch (inst->opcode()) { + case SpvOpMemberDecorate: + if (inst->GetSingleWordInOperand(2) == decoration) { + if (!f(*inst)) return false; + } + break; + case SpvOpDecorate: + case SpvOpDecorateId: + case SpvOpDecorateStringGOOGLE: + if (inst->GetSingleWordInOperand(1) == decoration) { + if (!f(*inst)) return false; + } + break; + default: + assert(false && "Unexpected decoration instruction"); + } + } + return true; +} + +void DecorationManager::ForEachDecoration( + uint32_t id, uint32_t decoration, + std::function f) { + WhileEachDecoration(id, decoration, [&f](const Instruction& inst) { + f(inst); + return true; + }); +} + +void DecorationManager::CloneDecorations(uint32_t from, uint32_t to) { + const auto decoration_list = id_to_decoration_insts_.find(from); + if (decoration_list == id_to_decoration_insts_.end()) return; + auto context = module_->context(); + for (Instruction* inst : decoration_list->second.direct_decorations) { + // simply clone decoration and change |target-id| to |to| + std::unique_ptr new_inst(inst->Clone(module_->context())); + new_inst->SetInOperand(0, {to}); + module_->AddAnnotationInst(std::move(new_inst)); + auto decoration_iter = --module_->annotation_end(); + context->AnalyzeUses(&*decoration_iter); + } + // We need to copy the list of instructions as ForgetUses and AnalyzeUses are + // going to modify it. + std::vector indirect_decorations = + decoration_list->second.indirect_decorations; + for (Instruction* inst : indirect_decorations) { + switch (inst->opcode()) { + case SpvOpGroupDecorate: + context->ForgetUses(inst); + // add |to| to list of decorated id's + inst->AddOperand( + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to})); + context->AnalyzeUses(inst); + break; + case SpvOpGroupMemberDecorate: { + context->ForgetUses(inst); + // for each (id == from), add (to, literal) as operands + const uint32_t num_operands = inst->NumOperands(); + for (uint32_t i = 1; i < num_operands; i += 2) { + Operand op = inst->GetOperand(i); + if (op.words[0] == from) { // add new pair of operands: (to, literal) + inst->AddOperand( + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {to})); + op = inst->GetOperand(i + 1); + inst->AddOperand(std::move(op)); + } + } + context->AnalyzeUses(inst); + break; + } + default: + assert(false && "Unexpected decoration instruction"); + } + } +} + +void DecorationManager::CloneDecorations( + uint32_t from, uint32_t to, + const std::vector& decorations_to_copy) { + const auto decoration_list = id_to_decoration_insts_.find(from); + if (decoration_list == id_to_decoration_insts_.end()) return; + auto context = module_->context(); + for (Instruction* inst : decoration_list->second.direct_decorations) { + if (std::find(decorations_to_copy.begin(), decorations_to_copy.end(), + inst->GetSingleWordInOperand(1)) == + decorations_to_copy.end()) { + continue; + } + + // Clone decoration and change |target-id| to |to|. + std::unique_ptr new_inst(inst->Clone(module_->context())); + new_inst->SetInOperand(0, {to}); + module_->AddAnnotationInst(std::move(new_inst)); + auto decoration_iter = --module_->annotation_end(); + context->AnalyzeUses(&*decoration_iter); + } + + // We need to copy the list of instructions as ForgetUses and AnalyzeUses are + // going to modify it. + std::vector indirect_decorations = + decoration_list->second.indirect_decorations; + for (Instruction* inst : indirect_decorations) { + switch (inst->opcode()) { + case SpvOpGroupDecorate: + CloneDecorations(inst->GetSingleWordInOperand(0), to, + decorations_to_copy); + break; + case SpvOpGroupMemberDecorate: { + assert(false && "The source id is not suppose to be a type."); + break; + } + default: + assert(false && "Unexpected decoration instruction"); + } + } +} + +void DecorationManager::RemoveDecoration(Instruction* inst) { + const auto remove_from_container = [inst](std::vector& v) { + v.erase(std::remove(v.begin(), v.end(), inst), v.end()); + }; + + switch (inst->opcode()) { + case SpvOpDecorate: + case SpvOpDecorateId: + case SpvOpDecorateStringGOOGLE: + case SpvOpMemberDecorate: { + const auto target_id = inst->GetSingleWordInOperand(0u); + auto const iter = id_to_decoration_insts_.find(target_id); + if (iter == id_to_decoration_insts_.end()) return; + remove_from_container(iter->second.direct_decorations); + } break; + case SpvOpGroupDecorate: + case SpvOpGroupMemberDecorate: { + const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u; + for (uint32_t i = 1u; i < inst->NumInOperands(); i += stride) { + const auto target_id = inst->GetSingleWordInOperand(i); + auto const iter = id_to_decoration_insts_.find(target_id); + if (iter == id_to_decoration_insts_.end()) continue; + remove_from_container(iter->second.indirect_decorations); + } + const auto group_id = inst->GetSingleWordInOperand(0u); + auto const iter = id_to_decoration_insts_.find(group_id); + if (iter == id_to_decoration_insts_.end()) return; + remove_from_container(iter->second.decorate_insts); + } break; + default: + break; + } +} + +bool operator==(const DecorationManager& lhs, const DecorationManager& rhs) { + return lhs.id_to_decoration_insts_ == rhs.id_to_decoration_insts_; +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/decoration_manager.h b/third_party/spirv-tools/source/opt/decoration_manager.h new file mode 100644 index 0000000..01244f2 --- /dev/null +++ b/third_party/spirv-tools/source/opt/decoration_manager.h @@ -0,0 +1,198 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DECORATION_MANAGER_H_ +#define SOURCE_OPT_DECORATION_MANAGER_H_ + +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +// A class for analyzing and managing decorations in an Module. +class DecorationManager { + public: + // Constructs a decoration manager from the given |module| + explicit DecorationManager(Module* module) : module_(module) { + AnalyzeDecorations(); + } + DecorationManager() = delete; + + // Changes all of the decorations (direct and through groups) where |pred| is + // true and that apply to |id| so that they no longer apply to |id|. + // + // If |id| is part of a group, it will be removed from the group if it + // does not use all of the group's decorations, or, if there are no + // decorations that apply to the group. + // + // If decoration groups become empty, the |OpGroupDecorate| and + // |OpGroupMemberDecorate| instructions will be killed. + // + // Decoration instructions that apply directly to |id| will be killed. + // + // If |id| is a decoration group and all of the group's decorations are + // removed, then the |OpGroupDecorate| and + // |OpGroupMemberDecorate| for the group will be killed, but not the defining + // |OpDecorationGroup| instruction. + void RemoveDecorationsFrom(uint32_t id, + std::function pred = + [](const Instruction&) { return true; }); + + // Removes all decorations from the result id of |inst|. + // + // NOTE: This is only meant to be called from ir_context, as only metadata + // will be removed, and no actual instruction. + void RemoveDecoration(Instruction* inst); + + // Returns a vector of all decorations affecting |id|. If a group is applied + // to |id|, the decorations of that group are returned rather than the group + // decoration instruction. If |include_linkage| is not set, linkage + // decorations won't be returned. + std::vector GetDecorationsFor(uint32_t id, + bool include_linkage); + std::vector GetDecorationsFor(uint32_t id, + bool include_linkage) const; + // Returns whether two IDs have the same decorations. Two SpvOpGroupDecorate + // instructions that apply the same decorations but to different IDs, still + // count as being the same. + bool HaveTheSameDecorations(uint32_t id1, uint32_t id2) const; + + // Returns whether two IDs have the same decorations. Two SpvOpGroupDecorate + // instructions that apply the same decorations but to different IDs, still + // count as being the same. + bool HaveSubsetOfDecorations(uint32_t id1, uint32_t id2) const; + + // Returns whether the two decorations instructions are the same and are + // applying the same decorations; unless |ignore_target| is false, the targets + // to which they are applied to does not matter, except for the member part. + // + // This is only valid for OpDecorate, OpMemberDecorate and OpDecorateId; it + // will return false for other opcodes. + bool AreDecorationsTheSame(const Instruction* inst1, const Instruction* inst2, + bool ignore_target) const; + + // |f| is run on each decoration instruction for |id| with decoration + // |decoration|. Processed are all decorations which target |id| either + // directly or indirectly by Decoration Groups. + void ForEachDecoration(uint32_t id, uint32_t decoration, + std::function f); + + // |f| is run on each decoration instruction for |id| with decoration + // |decoration|. Processes all decoration which target |id| either directly or + // indirectly through decoration groups. If |f| returns false, iteration is + // terminated and this function returns false. + bool WhileEachDecoration(uint32_t id, uint32_t decoration, + std::function f); + + // Clone all decorations from one id |from|. + // The cloned decorations are assigned to the given id |to| and are + // added to the module. The purpose is to decorate cloned instructions. + // This function does not check if the id |to| is already decorated. + void CloneDecorations(uint32_t from, uint32_t to); + + // Same as above, but only clone the decoration if the decoration operand is + // in |decorations_to_copy|. This function has the extra restriction that + // |from| and |to| must not be an object, not a type. + void CloneDecorations(uint32_t from, uint32_t to, + const std::vector& decorations_to_copy); + + // Informs the decoration manager of a new decoration that it needs to track. + void AddDecoration(Instruction* inst); + + // Add decoration with |opcode| and operands |opnds|. + void AddDecoration(SpvOp opcode, const std::vector opnds); + + // Add |decoration| of |inst_id| to module. + void AddDecoration(uint32_t inst_id, uint32_t decoration); + + // Add |decoration, decoration_value| of |inst_id| to module. + void AddDecorationVal(uint32_t inst_id, uint32_t decoration, + uint32_t decoration_value); + + // Add |decoration, decoration_value| of |inst_id, member| to module. + void AddMemberDecoration(uint32_t member, uint32_t inst_id, + uint32_t decoration, uint32_t decoration_value); + + friend bool operator==(const DecorationManager&, const DecorationManager&); + friend bool operator!=(const DecorationManager& lhs, + const DecorationManager& rhs) { + return !(lhs == rhs); + } + + private: + // Analyzes the defs and uses in the given |module| and populates data + // structures in this class. Does nothing if |module| is nullptr. + void AnalyzeDecorations(); + + template + std::vector InternalGetDecorationsFor(uint32_t id, bool include_linkage); + + // Tracks decoration information of an ID. + struct TargetData { + std::vector direct_decorations; // All decorate + // instructions applied + // to the tracked ID. + std::vector indirect_decorations; // All instructions + // applying a group to + // the tracked ID. + std::vector decorate_insts; // All decorate instructions + // applying the decorations + // of the tracked ID to + // targets. + // It is empty if the + // tracked ID is not a + // group. + }; + + friend bool operator==(const TargetData& lhs, const TargetData& rhs) { + if (!std::is_permutation(lhs.direct_decorations.begin(), + lhs.direct_decorations.end(), + rhs.direct_decorations.begin())) { + return false; + } + if (!std::is_permutation(lhs.indirect_decorations.begin(), + lhs.indirect_decorations.end(), + rhs.indirect_decorations.begin())) { + return false; + } + if (!std::is_permutation(lhs.decorate_insts.begin(), + lhs.decorate_insts.end(), + rhs.decorate_insts.begin())) { + return false; + } + return true; + } + + // Mapping from ids to the instructions applying a decoration to those ids. + // In other words, for each id you get all decoration instructions + // referencing that id, be it directly (SpvOpDecorate, SpvOpMemberDecorate + // and SpvOpDecorateId), or indirectly (SpvOpGroupDecorate, + // SpvOpMemberGroupDecorate). + std::unordered_map id_to_decoration_insts_; + // The enclosing module. + Module* module_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DECORATION_MANAGER_H_ diff --git a/third_party/spirv-tools/source/opt/def_use_manager.cpp b/third_party/spirv-tools/source/opt/def_use_manager.cpp new file mode 100644 index 0000000..0ec98ca --- /dev/null +++ b/third_party/spirv-tools/source/opt/def_use_manager.cpp @@ -0,0 +1,299 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/def_use_manager.h" + +#include + +#include "source/opt/log.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +void DefUseManager::AnalyzeInstDef(Instruction* inst) { + const uint32_t def_id = inst->result_id(); + if (def_id != 0) { + auto iter = id_to_def_.find(def_id); + if (iter != id_to_def_.end()) { + // Clear the original instruction that defining the same result id of the + // new instruction. + ClearInst(iter->second); + } + id_to_def_[def_id] = inst; + } else { + ClearInst(inst); + } +} + +void DefUseManager::AnalyzeInstUse(Instruction* inst) { + // Create entry for the given instruction. Note that the instruction may + // not have any in-operands. In such cases, we still need a entry for those + // instructions so this manager knows it has seen the instruction later. + auto* used_ids = &inst_to_used_ids_[inst]; + if (used_ids->size()) { + EraseUseRecordsOfOperandIds(inst); + used_ids = &inst_to_used_ids_[inst]; + } + used_ids->clear(); // It might have existed before. + + for (uint32_t i = 0; i < inst->NumOperands(); ++i) { + switch (inst->GetOperand(i).type) { + // For any id type but result id type + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: { + uint32_t use_id = inst->GetSingleWordOperand(i); + Instruction* def = GetDef(use_id); + assert(def && "Definition is not registered."); + id_to_users_.insert(UserEntry(def, inst)); + used_ids->push_back(use_id); + } break; + default: + break; + } + } +} + +void DefUseManager::AnalyzeInstDefUse(Instruction* inst) { + AnalyzeInstDef(inst); + AnalyzeInstUse(inst); +} + +void DefUseManager::UpdateDefUse(Instruction* inst) { + const uint32_t def_id = inst->result_id(); + if (def_id != 0) { + auto iter = id_to_def_.find(def_id); + if (iter == id_to_def_.end()) { + AnalyzeInstDef(inst); + } + } + AnalyzeInstUse(inst); +} + +Instruction* DefUseManager::GetDef(uint32_t id) { + auto iter = id_to_def_.find(id); + if (iter == id_to_def_.end()) return nullptr; + return iter->second; +} + +const Instruction* DefUseManager::GetDef(uint32_t id) const { + const auto iter = id_to_def_.find(id); + if (iter == id_to_def_.end()) return nullptr; + return iter->second; +} + +DefUseManager::IdToUsersMap::const_iterator DefUseManager::UsersBegin( + const Instruction* def) const { + return id_to_users_.lower_bound( + UserEntry(const_cast(def), nullptr)); +} + +bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, + const IdToUsersMap::const_iterator& cached_end, + const Instruction* inst) const { + return (iter != cached_end && iter->first == inst); +} + +bool DefUseManager::UsersNotEnd(const IdToUsersMap::const_iterator& iter, + const Instruction* inst) const { + return UsersNotEnd(iter, id_to_users_.end(), inst); +} + +bool DefUseManager::WhileEachUser( + const Instruction* def, const std::function& f) const { + // Ensure that |def| has been registered. + assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) && + "Definition is not registered."); + if (!def->HasResultId()) return true; + + auto end = id_to_users_.end(); + for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { + if (!f(iter->second)) return false; + } + return true; +} + +bool DefUseManager::WhileEachUser( + uint32_t id, const std::function& f) const { + return WhileEachUser(GetDef(id), f); +} + +void DefUseManager::ForEachUser( + const Instruction* def, const std::function& f) const { + WhileEachUser(def, [&f](Instruction* user) { + f(user); + return true; + }); +} + +void DefUseManager::ForEachUser( + uint32_t id, const std::function& f) const { + ForEachUser(GetDef(id), f); +} + +bool DefUseManager::WhileEachUse( + const Instruction* def, + const std::function& f) const { + // Ensure that |def| has been registered. + assert(def && (!def->HasResultId() || def == GetDef(def->result_id())) && + "Definition is not registered."); + if (!def->HasResultId()) return true; + + auto end = id_to_users_.end(); + for (auto iter = UsersBegin(def); UsersNotEnd(iter, end, def); ++iter) { + Instruction* user = iter->second; + for (uint32_t idx = 0; idx != user->NumOperands(); ++idx) { + const Operand& op = user->GetOperand(idx); + if (op.type != SPV_OPERAND_TYPE_RESULT_ID && spvIsIdType(op.type)) { + if (def->result_id() == op.words[0]) { + if (!f(user, idx)) return false; + } + } + } + } + return true; +} + +bool DefUseManager::WhileEachUse( + uint32_t id, const std::function& f) const { + return WhileEachUse(GetDef(id), f); +} + +void DefUseManager::ForEachUse( + const Instruction* def, + const std::function& f) const { + WhileEachUse(def, [&f](Instruction* user, uint32_t index) { + f(user, index); + return true; + }); +} + +void DefUseManager::ForEachUse( + uint32_t id, const std::function& f) const { + ForEachUse(GetDef(id), f); +} + +uint32_t DefUseManager::NumUsers(const Instruction* def) const { + uint32_t count = 0; + ForEachUser(def, [&count](Instruction*) { ++count; }); + return count; +} + +uint32_t DefUseManager::NumUsers(uint32_t id) const { + return NumUsers(GetDef(id)); +} + +uint32_t DefUseManager::NumUses(const Instruction* def) const { + uint32_t count = 0; + ForEachUse(def, [&count](Instruction*, uint32_t) { ++count; }); + return count; +} + +uint32_t DefUseManager::NumUses(uint32_t id) const { + return NumUses(GetDef(id)); +} + +std::vector DefUseManager::GetAnnotations(uint32_t id) const { + std::vector annos; + const Instruction* def = GetDef(id); + if (!def) return annos; + + ForEachUser(def, [&annos](Instruction* user) { + if (IsAnnotationInst(user->opcode())) { + annos.push_back(user); + } + }); + return annos; +} + +void DefUseManager::AnalyzeDefUse(Module* module) { + if (!module) return; + // Analyze all the defs before any uses to catch forward references. + module->ForEachInst( + std::bind(&DefUseManager::AnalyzeInstDef, this, std::placeholders::_1)); + module->ForEachInst( + std::bind(&DefUseManager::AnalyzeInstUse, this, std::placeholders::_1)); +} + +void DefUseManager::ClearInst(Instruction* inst) { + auto iter = inst_to_used_ids_.find(inst); + if (iter != inst_to_used_ids_.end()) { + EraseUseRecordsOfOperandIds(inst); + if (inst->result_id() != 0) { + // Remove all uses of this inst. + auto users_begin = UsersBegin(inst); + auto end = id_to_users_.end(); + auto new_end = users_begin; + for (; UsersNotEnd(new_end, end, inst); ++new_end) { + } + id_to_users_.erase(users_begin, new_end); + id_to_def_.erase(inst->result_id()); + } + } +} + +void DefUseManager::EraseUseRecordsOfOperandIds(const Instruction* inst) { + // Go through all ids used by this instruction, remove this instruction's + // uses of them. + auto iter = inst_to_used_ids_.find(inst); + if (iter != inst_to_used_ids_.end()) { + for (auto use_id : iter->second) { + id_to_users_.erase( + UserEntry(GetDef(use_id), const_cast(inst))); + } + inst_to_used_ids_.erase(inst); + } +} + +bool operator==(const DefUseManager& lhs, const DefUseManager& rhs) { + if (lhs.id_to_def_ != rhs.id_to_def_) { + return false; + } + + if (lhs.id_to_users_ != rhs.id_to_users_) { + for (auto p : lhs.id_to_users_) { + if (rhs.id_to_users_.count(p) == 0) { + return false; + } + } + for (auto p : rhs.id_to_users_) { + if (lhs.id_to_users_.count(p) == 0) { + return false; + } + } + return false; + } + + if (lhs.inst_to_used_ids_ != rhs.inst_to_used_ids_) { + for (auto p : lhs.inst_to_used_ids_) { + if (rhs.inst_to_used_ids_.count(p.first) == 0) { + return false; + } + } + for (auto p : rhs.inst_to_used_ids_) { + if (lhs.inst_to_used_ids_.count(p.first) == 0) { + return false; + } + } + return false; + } + return true; +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/def_use_manager.h b/third_party/spirv-tools/source/opt/def_use_manager.h new file mode 100644 index 0000000..0499e82 --- /dev/null +++ b/third_party/spirv-tools/source/opt/def_use_manager.h @@ -0,0 +1,256 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DEF_USE_MANAGER_H_ +#define SOURCE_OPT_DEF_USE_MANAGER_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { +namespace analysis { + +// Class for representing a use of id. Note that: +// * Result type id is a use. +// * Ids referenced in OpSectionMerge & OpLoopMerge are considered as use. +// * Ids referenced in OpPhi's in operands are considered as use. +struct Use { + Instruction* inst; // Instruction using the id. + uint32_t operand_index; // logical operand index of the id use. This can be + // the index of result type id. +}; + +inline bool operator==(const Use& lhs, const Use& rhs) { + return lhs.inst == rhs.inst && lhs.operand_index == rhs.operand_index; +} + +inline bool operator!=(const Use& lhs, const Use& rhs) { return !(lhs == rhs); } + +inline bool operator<(const Use& lhs, const Use& rhs) { + if (lhs.inst < rhs.inst) return true; + if (lhs.inst > rhs.inst) return false; + return lhs.operand_index < rhs.operand_index; +} + +// Definition and user pair. +// +// The first element of the pair is the definition. +// The second element of the pair is the user. +// +// Definition should never be null. User can be null, however, such an entry +// should be used only for searching (e.g. all users of a particular definition) +// and never stored in a container. +using UserEntry = std::pair; + +// Orders UserEntry for use in associative containers (i.e. less than ordering). +// +// The definition of an UserEntry is treated as the major key and the users as +// the minor key so that all the users of a particular definition are +// consecutive in a container. +// +// A null user always compares less than a real user. This is done to provide +// easy values to search for the beginning of the users of a particular +// definition (i.e. using {def, nullptr}). +struct UserEntryLess { + bool operator()(const UserEntry& lhs, const UserEntry& rhs) const { + // If lhs.first and rhs.first are both null, fall through to checking the + // second entries. + if (!lhs.first && rhs.first) return true; + if (lhs.first && !rhs.first) return false; + + // If neither definition is null, then compare unique ids. + if (lhs.first && rhs.first) { + if (lhs.first->unique_id() < rhs.first->unique_id()) return true; + if (rhs.first->unique_id() < lhs.first->unique_id()) return false; + } + + // Return false on equality. + if (!lhs.second && !rhs.second) return false; + if (!lhs.second) return true; + if (!rhs.second) return false; + + // If neither user is null then compare unique ids. + return lhs.second->unique_id() < rhs.second->unique_id(); + } +}; + +// A class for analyzing and managing defs and uses in an Module. +class DefUseManager { + public: + using IdToDefMap = std::unordered_map; + using IdToUsersMap = std::set; + + // Constructs a def-use manager from the given |module|. All internal messages + // will be communicated to the outside via the given message |consumer|. This + // instance only keeps a reference to the |consumer|, so the |consumer| should + // outlive this instance. + DefUseManager(Module* module) { AnalyzeDefUse(module); } + + DefUseManager(const DefUseManager&) = delete; + DefUseManager(DefUseManager&&) = delete; + DefUseManager& operator=(const DefUseManager&) = delete; + DefUseManager& operator=(DefUseManager&&) = delete; + + // Analyzes the defs in the given |inst|. + void AnalyzeInstDef(Instruction* inst); + + // Analyzes the uses in the given |inst|. + // + // All operands of |inst| must be analyzed as defs. + void AnalyzeInstUse(Instruction* inst); + + // Analyzes the defs and uses in the given |inst|. + void AnalyzeInstDefUse(Instruction* inst); + + // Returns the def instruction for the given |id|. If there is no instruction + // defining |id|, returns nullptr. + Instruction* GetDef(uint32_t id); + const Instruction* GetDef(uint32_t id) const; + + // Runs the given function |f| on each unique user instruction of |def| (or + // |id|). + // + // If one instruction uses |def| in multiple operands, that instruction will + // only be visited once. + // + // |def| (or |id|) must be registered as a definition. + void ForEachUser(const Instruction* def, + const std::function& f) const; + void ForEachUser(uint32_t id, + const std::function& f) const; + + // Runs the given function |f| on each unique user instruction of |def| (or + // |id|). If |f| returns false, iteration is terminated and this function + // returns false. + // + // If one instruction uses |def| in multiple operands, that instruction will + // be only be visited once. + // + // |def| (or |id|) must be registered as a definition. + bool WhileEachUser(const Instruction* def, + const std::function& f) const; + bool WhileEachUser(uint32_t id, + const std::function& f) const; + + // Runs the given function |f| on each unique use of |def| (or + // |id|). + // + // If one instruction uses |def| in multiple operands, each operand will be + // visited separately. + // + // |def| (or |id|) must be registered as a definition. + void ForEachUse( + const Instruction* def, + const std::function& f) const; + void ForEachUse( + uint32_t id, + const std::function& f) const; + + // Runs the given function |f| on each unique use of |def| (or + // |id|). If |f| returns false, iteration is terminated and this function + // returns false. + // + // If one instruction uses |def| in multiple operands, each operand will be + // visited separately. + // + // |def| (or |id|) must be registered as a definition. + bool WhileEachUse( + const Instruction* def, + const std::function& f) const; + bool WhileEachUse( + uint32_t id, + const std::function& f) const; + + // Returns the number of users of |def| (or |id|). + uint32_t NumUsers(const Instruction* def) const; + uint32_t NumUsers(uint32_t id) const; + + // Returns the number of uses of |def| (or |id|). + uint32_t NumUses(const Instruction* def) const; + uint32_t NumUses(uint32_t id) const; + + // Returns the annotation instrunctions which are a direct use of the given + // |id|. This means when the decorations are applied through decoration + // group(s), this function will just return the OpGroupDecorate + // instrcution(s) which refer to the given id as an operand. The OpDecorate + // instructions which decorate the decoration group will not be returned. + std::vector GetAnnotations(uint32_t id) const; + + // Returns the map from ids to their def instructions. + const IdToDefMap& id_to_defs() const { return id_to_def_; } + // Returns the map from instructions to their users. + const IdToUsersMap& id_to_users() const { return id_to_users_; } + + // Clear the internal def-use record of the given instruction |inst|. This + // method will update the use information of the operand ids of |inst|. The + // record: |inst| uses an |id|, will be removed from the use records of |id|. + // If |inst| defines an result id, the use record of this result id will also + // be removed. Does nothing if |inst| was not analyzed before. + void ClearInst(Instruction* inst); + + // Erases the records that a given instruction uses its operand ids. + void EraseUseRecordsOfOperandIds(const Instruction* inst); + + friend bool operator==(const DefUseManager&, const DefUseManager&); + friend bool operator!=(const DefUseManager& lhs, const DefUseManager& rhs) { + return !(lhs == rhs); + } + + // If |inst| has not already been analysed, then analyses its defintion and + // uses. + void UpdateDefUse(Instruction* inst); + + private: + using InstToUsedIdsMap = + std::unordered_map>; + + // Returns the first location that {|def|, nullptr} could be inserted into the + // users map without violating ordering. + IdToUsersMap::const_iterator UsersBegin(const Instruction* def) const; + + // Returns true if |iter| has not reached the end of |def|'s users. + // + // In the first version |iter| is compared against the end of the map for + // validity before other checks. In the second version, |iter| is compared + // against |cached_end| for validity before other checks. This allows caching + // the map's end which is a performance improvement on some platforms. + bool UsersNotEnd(const IdToUsersMap::const_iterator& iter, + const Instruction* def) const; + bool UsersNotEnd(const IdToUsersMap::const_iterator& iter, + const IdToUsersMap::const_iterator& cached_end, + const Instruction* def) const; + + // Analyzes the defs and uses in the given |module| and populates data + // structures in this class. Does nothing if |module| is nullptr. + void AnalyzeDefUse(Module* module); + + IdToDefMap id_to_def_; // Mapping from ids to their definitions + IdToUsersMap id_to_users_; // Mapping from ids to their users + // Mapping from instructions to the ids used in the instruction. + InstToUsedIdsMap inst_to_used_ids_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DEF_USE_MANAGER_H_ diff --git a/third_party/spirv-tools/source/opt/desc_sroa.cpp b/third_party/spirv-tools/source/opt/desc_sroa.cpp new file mode 100644 index 0000000..b68549a --- /dev/null +++ b/third_party/spirv-tools/source/opt/desc_sroa.cpp @@ -0,0 +1,448 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/desc_sroa.h" + +#include "source/util/string_utils.h" + +namespace spvtools { +namespace opt { + +Pass::Status DescriptorScalarReplacement::Process() { + bool modified = false; + + std::vector vars_to_kill; + + for (Instruction& var : context()->types_values()) { + if (IsCandidate(&var)) { + modified = true; + if (!ReplaceCandidate(&var)) { + return Status::Failure; + } + vars_to_kill.push_back(&var); + } + } + + for (Instruction* var : vars_to_kill) { + context()->KillInst(var); + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool DescriptorScalarReplacement::IsCandidate(Instruction* var) { + if (var->opcode() != SpvOpVariable) { + return false; + } + + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = + context()->get_def_use_mgr()->GetDef(ptr_type_id); + if (ptr_type_inst->opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t var_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* var_type_inst = + context()->get_def_use_mgr()->GetDef(var_type_id); + if (var_type_inst->opcode() != SpvOpTypeArray && + var_type_inst->opcode() != SpvOpTypeStruct) { + return false; + } + + // All structures with descriptor assignments must be replaced by variables, + // one for each of their members - with the exceptions of buffers. + // Buffers are represented as structures, but we shouldn't replace a buffer + // with its elements. All buffers have offset decorations for members of their + // structure types. + bool has_offset_decoration = false; + context()->get_decoration_mgr()->ForEachDecoration( + var_type_inst->result_id(), SpvDecorationOffset, + [&has_offset_decoration](const Instruction&) { + has_offset_decoration = true; + }); + if (has_offset_decoration) { + return false; + } + + bool has_desc_set_decoration = false; + context()->get_decoration_mgr()->ForEachDecoration( + var->result_id(), SpvDecorationDescriptorSet, + [&has_desc_set_decoration](const Instruction&) { + has_desc_set_decoration = true; + }); + if (!has_desc_set_decoration) { + return false; + } + + bool has_binding_decoration = false; + context()->get_decoration_mgr()->ForEachDecoration( + var->result_id(), SpvDecorationBinding, + [&has_binding_decoration](const Instruction&) { + has_binding_decoration = true; + }); + if (!has_binding_decoration) { + return false; + } + + return true; +} + +bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) { + std::vector access_chain_work_list; + std::vector load_work_list; + bool failed = !get_def_use_mgr()->WhileEachUser( + var->result_id(), + [this, &access_chain_work_list, &load_work_list](Instruction* use) { + if (use->opcode() == SpvOpName) { + return true; + } + + if (use->IsDecoration()) { + return true; + } + + switch (use->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + access_chain_work_list.push_back(use); + return true; + case SpvOpLoad: + load_work_list.push_back(use); + return true; + default: + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", use); + return false; + } + return true; + }); + + if (failed) { + return false; + } + + for (Instruction* use : access_chain_work_list) { + if (!ReplaceAccessChain(var, use)) { + return false; + } + } + for (Instruction* use : load_work_list) { + if (!ReplaceLoadedValue(var, use)) { + return false; + } + } + return true; +} + +bool DescriptorScalarReplacement::ReplaceAccessChain(Instruction* var, + Instruction* use) { + if (use->NumInOperands() <= 1) { + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", use); + return false; + } + + uint32_t idx_id = use->GetSingleWordInOperand(1); + const analysis::Constant* idx_const = + context()->get_constant_mgr()->FindDeclaredConstant(idx_id); + if (idx_const == nullptr) { + context()->EmitErrorMessage("Variable cannot be replaced: invalid index", + use); + return false; + } + + uint32_t idx = idx_const->GetU32(); + uint32_t replacement_var = GetReplacementVariable(var, idx); + + if (use->NumInOperands() == 2) { + // We are not indexing into the replacement variable. We can replaces the + // access chain with the replacement varibale itself. + context()->ReplaceAllUsesWith(use->result_id(), replacement_var); + context()->KillInst(use); + return true; + } + + // We need to build a new access chain with the replacement variable as the + // base address. + Instruction::OperandList new_operands; + + // Same result id and result type. + new_operands.emplace_back(use->GetOperand(0)); + new_operands.emplace_back(use->GetOperand(1)); + + // Use the replacement variable as the base address. + new_operands.push_back({SPV_OPERAND_TYPE_ID, {replacement_var}}); + + // Drop the first index because it is consumed by the replacment, and copy the + // rest. + for (uint32_t i = 4; i < use->NumOperands(); i++) { + new_operands.emplace_back(use->GetOperand(i)); + } + + use->ReplaceOperands(new_operands); + context()->UpdateDefUse(use); + return true; +} + +uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var, + uint32_t idx) { + auto replacement_vars = replacement_variables_.find(var); + if (replacement_vars == replacement_variables_.end()) { + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == SpvOpTypePointer && + "Variable should be a pointer to an array or structure."); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id); + const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray; + const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct; + assert((is_array || is_struct) && + "Variable should be a pointer to an array or structure."); + + // For arrays, each array element should be replaced with a new replacement + // variable + if (is_array) { + uint32_t array_len_id = pointee_type_inst->GetSingleWordInOperand(1); + const analysis::Constant* array_len_const = + context()->get_constant_mgr()->FindDeclaredConstant(array_len_id); + assert(array_len_const != nullptr && "Array length must be a constant."); + uint32_t array_len = array_len_const->GetU32(); + + replacement_vars = replacement_variables_ + .insert({var, std::vector(array_len, 0)}) + .first; + } + // For structures, each member should be replaced with a new replacement + // variable + if (is_struct) { + const uint32_t num_members = pointee_type_inst->NumInOperands(); + replacement_vars = + replacement_variables_ + .insert({var, std::vector(num_members, 0)}) + .first; + } + } + + if (replacement_vars->second[idx] == 0) { + replacement_vars->second[idx] = CreateReplacementVariable(var, idx); + } + + return replacement_vars->second[idx]; +} + +uint32_t DescriptorScalarReplacement::CreateReplacementVariable( + Instruction* var, uint32_t idx) { + // The storage class for the new variable is the same as the original. + SpvStorageClass storage_class = + static_cast(var->GetSingleWordInOperand(0)); + + // The type for the new variable will be a pointer to type of the elements of + // the array. + uint32_t ptr_type_id = var->type_id(); + Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == SpvOpTypePointer && + "Variable should be a pointer to an array or structure."); + uint32_t pointee_type_id = ptr_type_inst->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = get_def_use_mgr()->GetDef(pointee_type_id); + const bool is_array = pointee_type_inst->opcode() == SpvOpTypeArray; + const bool is_struct = pointee_type_inst->opcode() == SpvOpTypeStruct; + assert((is_array || is_struct) && + "Variable should be a pointer to an array or structure."); + + uint32_t element_type_id = + is_array ? pointee_type_inst->GetSingleWordInOperand(0) + : pointee_type_inst->GetSingleWordInOperand(idx); + + uint32_t ptr_element_type_id = context()->get_type_mgr()->FindPointerToType( + element_type_id, storage_class); + + // Create the variable. + uint32_t id = TakeNextId(); + std::unique_ptr variable( + new Instruction(context(), SpvOpVariable, ptr_element_type_id, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(storage_class)}}})); + context()->AddGlobalValue(std::move(variable)); + + // Copy all of the decorations to the new variable. The only difference is + // the Binding decoration needs to be adjusted. + for (auto old_decoration : + get_decoration_mgr()->GetDecorationsFor(var->result_id(), true)) { + assert(old_decoration->opcode() == SpvOpDecorate); + std::unique_ptr new_decoration( + old_decoration->Clone(context())); + new_decoration->SetInOperand(0, {id}); + + uint32_t decoration = new_decoration->GetSingleWordInOperand(1u); + if (decoration == SpvDecorationBinding) { + uint32_t new_binding = new_decoration->GetSingleWordInOperand(2); + if (is_array) { + new_binding += idx * GetNumBindingsUsedByType(ptr_element_type_id); + } + if (is_struct) { + // The binding offset that should be added is the sum of binding numbers + // used by previous members of the current struct. + for (uint32_t i = 0; i < idx; ++i) { + new_binding += GetNumBindingsUsedByType( + pointee_type_inst->GetSingleWordInOperand(i)); + } + } + new_decoration->SetInOperand(2, {new_binding}); + } + context()->AddAnnotationInst(std::move(new_decoration)); + } + + // Create a new OpName for the replacement variable. + std::vector> names_to_add; + for (auto p : context()->GetNames(var->result_id())) { + Instruction* name_inst = p.second; + std::string name_str = utils::MakeString(name_inst->GetOperand(1).words); + if (is_array) { + name_str += "[" + utils::ToString(idx) + "]"; + } + if (is_struct) { + Instruction* member_name_inst = + context()->GetMemberName(pointee_type_inst->result_id(), idx); + name_str += "."; + if (member_name_inst) + name_str += utils::MakeString(member_name_inst->GetOperand(2).words); + else + // In case the member does not have a name assigned to it, use the + // member index. + name_str += utils::ToString(idx); + } + + std::unique_ptr new_name(new Instruction( + context(), SpvOpName, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {id}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, utils::MakeVector(name_str)}})); + Instruction* new_name_inst = new_name.get(); + get_def_use_mgr()->AnalyzeInstDefUse(new_name_inst); + names_to_add.push_back(std::move(new_name)); + } + + // We shouldn't add the new names when we are iterating over name ranges + // above. We can add all the new names now. + for (auto& new_name : names_to_add) + context()->AddDebug2Inst(std::move(new_name)); + + return id; +} + +uint32_t DescriptorScalarReplacement::GetNumBindingsUsedByType( + uint32_t type_id) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + + // If it's a pointer, look at the underlying type. + if (type_inst->opcode() == SpvOpTypePointer) { + type_id = type_inst->GetSingleWordInOperand(1); + type_inst = get_def_use_mgr()->GetDef(type_id); + } + + // Arrays consume N*M binding numbers where N is the array length, and M is + // the number of bindings used by each array element. + if (type_inst->opcode() == SpvOpTypeArray) { + uint32_t element_type_id = type_inst->GetSingleWordInOperand(0); + uint32_t length_id = type_inst->GetSingleWordInOperand(1); + const analysis::Constant* length_const = + context()->get_constant_mgr()->FindDeclaredConstant(length_id); + // OpTypeArray's length must always be a constant + assert(length_const != nullptr); + uint32_t num_elems = length_const->GetU32(); + return num_elems * GetNumBindingsUsedByType(element_type_id); + } + + // The number of bindings consumed by a structure is the sum of the bindings + // used by its members. + if (type_inst->opcode() == SpvOpTypeStruct) { + uint32_t sum = 0; + for (uint32_t i = 0; i < type_inst->NumInOperands(); i++) + sum += GetNumBindingsUsedByType(type_inst->GetSingleWordInOperand(i)); + return sum; + } + + // All other types are considered to take up 1 binding number. + return 1; +} + +bool DescriptorScalarReplacement::ReplaceLoadedValue(Instruction* var, + Instruction* value) { + // |var| is the global variable that has to be eliminated (OpVariable). + // |value| is the OpLoad instruction that has loaded |var|. + // The function expects all users of |value| to be OpCompositeExtract + // instructions. Otherwise the function returns false with an error message. + assert(value->opcode() == SpvOpLoad); + assert(value->GetSingleWordInOperand(0) == var->result_id()); + std::vector work_list; + bool failed = !get_def_use_mgr()->WhileEachUser( + value->result_id(), [this, &work_list](Instruction* use) { + if (use->opcode() != SpvOpCompositeExtract) { + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", use); + return false; + } + work_list.push_back(use); + return true; + }); + + if (failed) { + return false; + } + + for (Instruction* use : work_list) { + if (!ReplaceCompositeExtract(var, use)) { + return false; + } + } + + // All usages of the loaded value have been killed. We can kill the OpLoad. + context()->KillInst(value); + return true; +} + +bool DescriptorScalarReplacement::ReplaceCompositeExtract( + Instruction* var, Instruction* extract) { + assert(extract->opcode() == SpvOpCompositeExtract); + // We're currently only supporting extractions of one index at a time. If we + // need to, we can handle cases with multiple indexes in the future. + if (extract->NumInOperands() != 2) { + context()->EmitErrorMessage( + "Variable cannot be replaced: invalid instruction", extract); + return false; + } + + uint32_t replacement_var = + GetReplacementVariable(var, extract->GetSingleWordInOperand(1)); + + // The result type of the OpLoad is the same as the result type of the + // OpCompositeExtract. + uint32_t load_id = TakeNextId(); + std::unique_ptr load( + new Instruction(context(), SpvOpLoad, extract->type_id(), load_id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {replacement_var}}})); + Instruction* load_instr = load.get(); + get_def_use_mgr()->AnalyzeInstDefUse(load_instr); + context()->set_instr_block(load_instr, context()->get_instr_block(extract)); + extract->InsertBefore(std::move(load)); + context()->ReplaceAllUsesWith(extract->result_id(), load_id); + context()->KillInst(extract); + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/desc_sroa.h b/third_party/spirv-tools/source/opt/desc_sroa.h new file mode 100644 index 0000000..c3aa0ea --- /dev/null +++ b/third_party/spirv-tools/source/opt/desc_sroa.h @@ -0,0 +1,107 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DESC_SROA_H_ +#define SOURCE_OPT_DESC_SROA_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class DescriptorScalarReplacement : public Pass { + public: + DescriptorScalarReplacement() {} + + const char* name() const override { return "descriptor-scalar-replacement"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Returns true if |var| is an OpVariable instruction that represents a + // descriptor array. These are the variables that we want to replace. + bool IsCandidate(Instruction* var); + + // Replaces all references to |var| by new variables, one for each element of + // the array |var|. The binding for the new variables corresponding to + // element i will be the binding of |var| plus i. Returns true if successful. + bool ReplaceCandidate(Instruction* var); + + // Replaces the base address |var| in the OpAccessChain or + // OpInBoundsAccessChain instruction |use| by the variable that the access + // chain accesses. The first index in |use| must be an |OpConstant|. Returns + // |true| if successful. + bool ReplaceAccessChain(Instruction* var, Instruction* use); + + // Replaces the given compososite variable |var| loaded by OpLoad |value| with + // replacement variables, one for each component that's accessed in the + // shader. Assumes that |value| is only used by OpCompositeExtract + // instructions, one index at a time. Returns true on success, and false + // otherwise. + bool ReplaceLoadedValue(Instruction* var, Instruction* value); + + // Replaces the given OpCompositeExtract |extract| and all of its references + // with an OpLoad of a replacement variable. |var| is the variable with + // composite type whose value is being used by |extract|. Assumes that + // |extract| is extracting one index only. Returns true on success, and false + // otherwise. + bool ReplaceCompositeExtract(Instruction* var, Instruction* extract); + + // Returns the id of the variable that will be used to replace the |idx|th + // element of |var|. The variable is created if it has not already been + // created. + uint32_t GetReplacementVariable(Instruction* var, uint32_t idx); + + // Returns the id of a new variable that can be used to replace the |idx|th + // element of |var|. + uint32_t CreateReplacementVariable(Instruction* var, uint32_t idx); + + // Returns the number of bindings used by the given |type_id|. + // All types are considered to use 1 binding slot, except: + // 1- A pointer type consumes as many binding numbers as its pointee. + // 2- An array of size N consumes N*M binding numbers, where M is the number + // of bindings used by each array element. + // 3- The number of bindings consumed by a structure is the sum of the + // bindings used by its members. + uint32_t GetNumBindingsUsedByType(uint32_t type_id); + + // A map from an OpVariable instruction to the set of variables that will be + // used to replace it. The entry |replacement_variables_[var][i]| is the id of + // a variable that will be used in the place of the the ith element of the + // array |var|. If the entry is |0|, then the variable has not been + // created yet. + std::map> replacement_variables_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DESC_SROA_H_ diff --git a/third_party/spirv-tools/source/opt/dominator_analysis.cpp b/third_party/spirv-tools/source/opt/dominator_analysis.cpp new file mode 100644 index 0000000..b692d26 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dominator_analysis.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/dominator_analysis.h" + +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +BasicBlock* DominatorAnalysisBase::CommonDominator(BasicBlock* b1, + BasicBlock* b2) const { + if (!b1 || !b2) return nullptr; + + std::unordered_set seen; + BasicBlock* block = b1; + while (block && seen.insert(block).second) { + block = ImmediateDominator(block); + } + + block = b2; + while (block && !seen.count(block)) { + block = ImmediateDominator(block); + } + + return block; +} + +bool DominatorAnalysisBase::Dominates(Instruction* a, Instruction* b) const { + if (!a || !b) { + return false; + } + + if (a == b) { + return true; + } + + BasicBlock* bb_a = a->context()->get_instr_block(a); + BasicBlock* bb_b = b->context()->get_instr_block(b); + + if (bb_a != bb_b) { + return tree_.Dominates(bb_a, bb_b); + } + + const Instruction* current = a; + const Instruction* other = b; + + if (tree_.IsPostDominator()) { + std::swap(current, other); + } + + // We handle OpLabel instructions explicitly since they are not stored in the + // instruction list. + if (current->opcode() == SpvOpLabel) { + return true; + } + + while ((current = current->NextNode())) { + if (current == other) { + return true; + } + } + + return false; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/dominator_analysis.h b/third_party/spirv-tools/source/opt/dominator_analysis.h new file mode 100644 index 0000000..a94120a --- /dev/null +++ b/third_party/spirv-tools/source/opt/dominator_analysis.h @@ -0,0 +1,138 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DOMINATOR_ANALYSIS_H_ +#define SOURCE_OPT_DOMINATOR_ANALYSIS_H_ + +#include +#include + +#include "source/opt/dominator_tree.h" + +namespace spvtools { +namespace opt { + +// Interface to perform dominator or postdominator analysis on a given function. +class DominatorAnalysisBase { + public: + explicit DominatorAnalysisBase(bool is_post_dom) : tree_(is_post_dom) {} + + // Calculates the dominator (or postdominator) tree for given function |f|. + inline void InitializeTree(const CFG& cfg, const Function* f) { + tree_.InitializeTree(cfg, f); + } + + // Returns true if BasicBlock |a| dominates BasicBlock |b|. + inline bool Dominates(const BasicBlock* a, const BasicBlock* b) const { + if (!a || !b) return false; + return Dominates(a->id(), b->id()); + } + + // Returns true if BasicBlock |a| dominates BasicBlock |b|. Same as above only + // using the BasicBlock IDs. + inline bool Dominates(uint32_t a, uint32_t b) const { + return tree_.Dominates(a, b); + } + + // Returns true if instruction |a| dominates instruction |b|. + bool Dominates(Instruction* a, Instruction* b) const; + + // Returns true if BasicBlock |a| strictly dominates BasicBlock |b|. + inline bool StrictlyDominates(const BasicBlock* a, + const BasicBlock* b) const { + if (!a || !b) return false; + return StrictlyDominates(a->id(), b->id()); + } + + // Returns true if BasicBlock |a| strictly dominates BasicBlock |b|. Same as + // above only using the BasicBlock IDs. + inline bool StrictlyDominates(uint32_t a, uint32_t b) const { + return tree_.StrictlyDominates(a, b); + } + + // Returns the immediate dominator of |node| or returns nullptr if it is has + // no dominator. + inline BasicBlock* ImmediateDominator(const BasicBlock* node) const { + if (!node) return nullptr; + return tree_.ImmediateDominator(node); + } + + // Returns the immediate dominator of |node_id| or returns nullptr if it is + // has no dominator. Same as above but operates on IDs. + inline BasicBlock* ImmediateDominator(uint32_t node_id) const { + return tree_.ImmediateDominator(node_id); + } + + // Returns true if |node| is reachable from the entry. + inline bool IsReachable(const BasicBlock* node) const { + if (!node) return false; + return tree_.ReachableFromRoots(node->id()); + } + + // Returns true if |node_id| is reachable from the entry. + inline bool IsReachable(uint32_t node_id) const { + return tree_.ReachableFromRoots(node_id); + } + + // Dump the tree structure into the given |out| stream in the dot format. + inline void DumpAsDot(std::ostream& out) const { tree_.DumpTreeAsDot(out); } + + // Returns true if this is a postdomiator tree. + inline bool IsPostDominator() const { return tree_.IsPostDominator(); } + + // Returns the tree itself for manual operations, such as traversing the + // roots. + // For normal dominance relationships the methods above should be used. + inline DominatorTree& GetDomTree() { return tree_; } + inline const DominatorTree& GetDomTree() const { return tree_; } + + // Force the dominator tree to be removed + inline void ClearTree() { tree_.ClearTree(); } + + // Applies the std::function |func| to dominator tree nodes in dominator + // order. + void Visit(std::function func) { + tree_.Visit(func); + } + + // Applies the std::function |func| to dominator tree nodes in dominator + // order. + void Visit(std::function func) const { + tree_.Visit(func); + } + + // Returns the most immediate basic block that dominates both |b1| and |b2|. + // If there is no such basic block, nullptr is returned. + BasicBlock* CommonDominator(BasicBlock* b1, BasicBlock* b2) const; + + protected: + DominatorTree tree_; +}; + +// Derived class for normal dominator analysis. +class DominatorAnalysis : public DominatorAnalysisBase { + public: + DominatorAnalysis() : DominatorAnalysisBase(false) {} +}; + +// Derived class for postdominator analysis. +class PostDominatorAnalysis : public DominatorAnalysisBase { + public: + PostDominatorAnalysis() : DominatorAnalysisBase(true) {} +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DOMINATOR_ANALYSIS_H_ diff --git a/third_party/spirv-tools/source/opt/dominator_tree.cpp b/third_party/spirv-tools/source/opt/dominator_tree.cpp new file mode 100644 index 0000000..55287f4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dominator_tree.cpp @@ -0,0 +1,398 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/cfa.h" +#include "source/opt/dominator_tree.h" +#include "source/opt/ir_context.h" + +// Calculates the dominator or postdominator tree for a given function. +// 1 - Compute the successors and predecessors for each BasicBlock. We add a +// placeholder node for the start node or for postdominators the exit. This node +// will point to all entry or all exit nodes. +// 2 - Using the CFA::DepthFirstTraversal get a depth first postordered list of +// all BasicBlocks. Using the successors (or for postdominator, predecessors) +// calculated in step 1 to traverse the tree. +// 3 - Pass the list calculated in step 2 to the CFA::CalculateDominators using +// the predecessors list (or for postdominator, successors). This will give us a +// vector of BB pairs. Each BB and its immediate dominator. +// 4 - Using the list from 3 use those edges to build a tree of +// DominatorTreeNodes. Each node containing a link to the parent dominator and +// children which are dominated. +// 5 - Using the tree from 4, perform a depth first traversal to calculate the +// preorder and postorder index of each node. We use these indexes to compare +// nodes against each other for domination checks. + +namespace spvtools { +namespace opt { +namespace { + +// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform +// depth first search on generic BasicBlock types. Will call post and pre order +// user defined functions during traversal +// +// BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode +// SuccessorLambda - Lamdba matching the signature of 'const +// std::vector*(const BBType *A)'. Will return a vector of the nodes +// succeding BasicBlock A. +// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be +// called on each node traversed AFTER their children. +// PreLambda - Lamdba matching the signature of 'void (const BBType*)' will be +// called on each node traversed BEFORE their children. +template +static void DepthFirstSearch(const BBType* bb, SuccessorLambda successors, + PreLambda pre, PostLambda post) { + // Ignore backedge operation. + auto nop_backedge = [](const BBType*, const BBType*) {}; + CFA::DepthFirstTraversal(bb, successors, pre, post, nop_backedge); +} + +// Wrapper around CFA::DepthFirstTraversal to provide an interface to perform +// depth first search on generic BasicBlock types. This overload is for only +// performing user defined post order. +// +// BBType - BasicBlock type. Will either be BasicBlock or DominatorTreeNode +// SuccessorLambda - Lamdba matching the signature of 'const +// std::vector*(const BBType *A)'. Will return a vector of the nodes +// succeding BasicBlock A. +// PostLambda - Lamdba matching the signature of 'void (const BBType*)' will be +// called on each node traversed after their children. +template +static void DepthFirstSearchPostOrder(const BBType* bb, + SuccessorLambda successors, + PostLambda post) { + // Ignore preorder operation. + auto nop_preorder = [](const BBType*) {}; + DepthFirstSearch(bb, successors, nop_preorder, post); +} + +// Small type trait to get the function class type. +template +struct GetFunctionClass { + using FunctionType = Function; +}; + +// Helper class to compute predecessors and successors for each Basic Block in a +// function. Through GetPredFunctor and GetSuccessorFunctor it provides an +// interface to get the successor and predecessor lists for each basic +// block. This is required by the DepthFirstTraversal and ComputeDominator +// functions which take as parameter an std::function returning the successors +// and predecessors respectively. +// +// When computing the post-dominator tree, all edges are inverted. So successors +// returned by this class will be predecessors in the original CFG. +template +class BasicBlockSuccessorHelper { + // This should eventually become const BasicBlock. + using BasicBlock = BBType; + using Function = typename GetFunctionClass::FunctionType; + + using BasicBlockListTy = std::vector; + using BasicBlockMapTy = std::map; + + public: + // For compliance with the dominance tree computation, entry nodes are + // connected to a single placeholder node. + BasicBlockSuccessorHelper(Function& func, + const BasicBlock* placeholder_start_node, + bool post); + + // CFA::CalculateDominators requires std::vector. + using GetBlocksFunction = + std::function*(const BasicBlock*)>; + + // Returns the list of predecessor functions. + GetBlocksFunction GetPredFunctor() { + return [this](const BasicBlock* bb) { + BasicBlockListTy* v = &this->predecessors_[bb]; + return v; + }; + } + + // Returns a vector of the list of successor nodes from a given node. + GetBlocksFunction GetSuccessorFunctor() { + return [this](const BasicBlock* bb) { + BasicBlockListTy* v = &this->successors_[bb]; + return v; + }; + } + + private: + bool invert_graph_; + BasicBlockMapTy successors_; + BasicBlockMapTy predecessors_; + + // Build the successors and predecessors map for each basic blocks |f|. + // If |invert_graph_| is true, all edges are reversed (successors becomes + // predecessors and vice versa). + // For convenience, the start of the graph is |placeholder_start_node|. + // The dominator tree construction requires a unique entry node, which cannot + // be guaranteed for the postdominator graph. The |placeholder_start_node| BB + // is here to gather all entry nodes. + void CreateSuccessorMap(Function& f, + const BasicBlock* placeholder_start_node); +}; + +template +BasicBlockSuccessorHelper::BasicBlockSuccessorHelper( + Function& func, const BasicBlock* placeholder_start_node, bool invert) + : invert_graph_(invert) { + CreateSuccessorMap(func, placeholder_start_node); +} + +template +void BasicBlockSuccessorHelper::CreateSuccessorMap( + Function& f, const BasicBlock* placeholder_start_node) { + std::map id_to_BB_map; + auto GetSuccessorBasicBlock = [&f, &id_to_BB_map](uint32_t successor_id) { + BasicBlock*& Succ = id_to_BB_map[successor_id]; + if (!Succ) { + for (BasicBlock& BBIt : f) { + if (successor_id == BBIt.id()) { + Succ = &BBIt; + break; + } + } + } + return Succ; + }; + + if (invert_graph_) { + // For the post dominator tree, we see the inverted graph. + // successors_ in the inverted graph are the predecessors in the CFG. + // The tree construction requires 1 entry point, so we add a placeholder + // node that is connected to all function exiting basic blocks. An exiting + // basic block is a block with an OpKill, OpUnreachable, OpReturn, + // OpReturnValue, or OpTerminateInvocation as terminator instruction. + for (BasicBlock& bb : f) { + if (bb.hasSuccessor()) { + BasicBlockListTy& pred_list = predecessors_[&bb]; + const auto& const_bb = bb; + const_bb.ForEachSuccessorLabel( + [this, &pred_list, &bb, + &GetSuccessorBasicBlock](const uint32_t successor_id) { + BasicBlock* succ = GetSuccessorBasicBlock(successor_id); + // Inverted graph: our successors in the CFG + // are our predecessors in the inverted graph. + this->successors_[succ].push_back(&bb); + pred_list.push_back(succ); + }); + } else { + successors_[placeholder_start_node].push_back(&bb); + predecessors_[&bb].push_back( + const_cast(placeholder_start_node)); + } + } + } else { + successors_[placeholder_start_node].push_back(f.entry().get()); + predecessors_[f.entry().get()].push_back( + const_cast(placeholder_start_node)); + for (BasicBlock& bb : f) { + BasicBlockListTy& succ_list = successors_[&bb]; + + const auto& const_bb = bb; + const_bb.ForEachSuccessorLabel([&](const uint32_t successor_id) { + BasicBlock* succ = GetSuccessorBasicBlock(successor_id); + succ_list.push_back(succ); + predecessors_[succ].push_back(&bb); + }); + } + } +} + +} // namespace + +bool DominatorTree::StrictlyDominates(uint32_t a, uint32_t b) const { + if (a == b) return false; + return Dominates(a, b); +} + +bool DominatorTree::StrictlyDominates(const BasicBlock* a, + const BasicBlock* b) const { + return DominatorTree::StrictlyDominates(a->id(), b->id()); +} + +bool DominatorTree::StrictlyDominates(const DominatorTreeNode* a, + const DominatorTreeNode* b) const { + if (a == b) return false; + return Dominates(a, b); +} + +bool DominatorTree::Dominates(uint32_t a, uint32_t b) const { + // Check that both of the inputs are actual nodes. + const DominatorTreeNode* a_node = GetTreeNode(a); + const DominatorTreeNode* b_node = GetTreeNode(b); + if (!a_node || !b_node) return false; + + return Dominates(a_node, b_node); +} + +bool DominatorTree::Dominates(const DominatorTreeNode* a, + const DominatorTreeNode* b) const { + if (!a || !b) return false; + // Node A dominates node B if they are the same. + if (a == b) return true; + + return a->dfs_num_pre_ < b->dfs_num_pre_ && + a->dfs_num_post_ > b->dfs_num_post_; +} + +bool DominatorTree::Dominates(const BasicBlock* A, const BasicBlock* B) const { + return Dominates(A->id(), B->id()); +} + +BasicBlock* DominatorTree::ImmediateDominator(const BasicBlock* A) const { + return ImmediateDominator(A->id()); +} + +BasicBlock* DominatorTree::ImmediateDominator(uint32_t a) const { + // Check that A is a valid node in the tree. + auto a_itr = nodes_.find(a); + if (a_itr == nodes_.end()) return nullptr; + + const DominatorTreeNode* node = &a_itr->second; + + if (node->parent_ == nullptr) { + return nullptr; + } + + return node->parent_->bb_; +} + +DominatorTreeNode* DominatorTree::GetOrInsertNode(BasicBlock* bb) { + DominatorTreeNode* dtn = nullptr; + + std::map::iterator node_iter = + nodes_.find(bb->id()); + if (node_iter == nodes_.end()) { + dtn = &nodes_.emplace(std::make_pair(bb->id(), DominatorTreeNode{bb})) + .first->second; + } else { + dtn = &node_iter->second; + } + + return dtn; +} + +void DominatorTree::GetDominatorEdges( + const Function* f, const BasicBlock* placeholder_start_node, + std::vector>* edges) { + // Each time the depth first traversal calls the postorder callback + // std::function we push that node into the postorder vector to create our + // postorder list. + std::vector postorder; + auto postorder_function = [&](const BasicBlock* b) { + postorder.push_back(b); + }; + + // CFA::CalculateDominators requires std::vector + // BB are derived from F, so we need to const cast it at some point + // no modification is made on F. + BasicBlockSuccessorHelper helper{ + *const_cast(f), placeholder_start_node, postdominator_}; + + // The successor function tells DepthFirstTraversal how to move to successive + // nodes by providing an interface to get a list of successor nodes from any + // given node. + auto successor_functor = helper.GetSuccessorFunctor(); + + // The predecessor functor does the same as the successor functor + // but for all nodes preceding a given node. + auto predecessor_functor = helper.GetPredFunctor(); + + // If we're building a post dominator tree we traverse the tree in reverse + // using the predecessor function in place of the successor function and vice + // versa. + DepthFirstSearchPostOrder(placeholder_start_node, successor_functor, + postorder_function); + *edges = CFA::CalculateDominators(postorder, predecessor_functor); +} + +void DominatorTree::InitializeTree(const CFG& cfg, const Function* f) { + ClearTree(); + + // Skip over empty functions. + if (f->cbegin() == f->cend()) { + return; + } + + const BasicBlock* placeholder_start_node = + postdominator_ ? cfg.pseudo_exit_block() : cfg.pseudo_entry_block(); + + // Get the immediate dominator for each node. + std::vector> edges; + GetDominatorEdges(f, placeholder_start_node, &edges); + + // Transform the vector into the tree structure which we can use to + // efficiently query dominance. + for (auto edge : edges) { + DominatorTreeNode* first = GetOrInsertNode(edge.first); + + if (edge.first == edge.second) { + if (std::find(roots_.begin(), roots_.end(), first) == roots_.end()) + roots_.push_back(first); + continue; + } + + DominatorTreeNode* second = GetOrInsertNode(edge.second); + + first->parent_ = second; + second->children_.push_back(first); + } + ResetDFNumbering(); +} + +void DominatorTree::ResetDFNumbering() { + int index = 0; + auto preFunc = [&index](const DominatorTreeNode* node) { + const_cast(node)->dfs_num_pre_ = ++index; + }; + + auto postFunc = [&index](const DominatorTreeNode* node) { + const_cast(node)->dfs_num_post_ = ++index; + }; + + auto getSucc = [](const DominatorTreeNode* node) { return &node->children_; }; + + for (auto root : roots_) DepthFirstSearch(root, getSucc, preFunc, postFunc); +} + +void DominatorTree::DumpTreeAsDot(std::ostream& out_stream) const { + out_stream << "digraph {\n"; + Visit([&out_stream](const DominatorTreeNode* node) { + // Print the node. + if (node->bb_) { + out_stream << node->bb_->id() << "[label=\"" << node->bb_->id() + << "\"];\n"; + } + + // Print the arrow from the parent to this node. Entry nodes will not have + // parents so draw them as children from the placeholder node. + if (node->parent_) { + out_stream << node->parent_->bb_->id() << " -> " << node->bb_->id() + << ";\n"; + } + + // Return true to continue the traversal. + return true; + }); + out_stream << "}\n"; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/dominator_tree.h b/third_party/spirv-tools/source/opt/dominator_tree.h new file mode 100644 index 0000000..0024bc5 --- /dev/null +++ b/third_party/spirv-tools/source/opt/dominator_tree.h @@ -0,0 +1,305 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_DOMINATOR_TREE_H_ +#define SOURCE_OPT_DOMINATOR_TREE_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/tree_iterator.h" + +namespace spvtools { +namespace opt { +// This helper struct forms the nodes in the tree, with each node containing its +// children. It also contains two values, for the pre and post indexes in the +// tree which are used to compare two nodes. +struct DominatorTreeNode { + explicit DominatorTreeNode(BasicBlock* bb) + : bb_(bb), + parent_(nullptr), + children_({}), + dfs_num_pre_(-1), + dfs_num_post_(-1) {} + + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + + // depth first preorder iterator. + using df_iterator = TreeDFIterator; + using const_df_iterator = TreeDFIterator; + // depth first postorder iterator. + using post_iterator = PostOrderTreeDFIterator; + using const_post_iterator = PostOrderTreeDFIterator; + + iterator begin() { return children_.begin(); } + iterator end() { return children_.end(); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + const_iterator cbegin() const { return children_.begin(); } + const_iterator cend() const { return children_.end(); } + + // Depth first preorder iterator using this node as root. + df_iterator df_begin() { return df_iterator(this); } + df_iterator df_end() { return df_iterator(); } + const_df_iterator df_begin() const { return df_cbegin(); } + const_df_iterator df_end() const { return df_cend(); } + const_df_iterator df_cbegin() const { return const_df_iterator(this); } + const_df_iterator df_cend() const { return const_df_iterator(); } + + // Depth first postorder iterator using this node as root. + post_iterator post_begin() { return post_iterator::begin(this); } + post_iterator post_end() { return post_iterator::end(nullptr); } + const_post_iterator post_begin() const { return post_cbegin(); } + const_post_iterator post_end() const { return post_cend(); } + const_post_iterator post_cbegin() const { + return const_post_iterator::begin(this); + } + const_post_iterator post_cend() const { + return const_post_iterator::end(nullptr); + } + + inline uint32_t id() const { return bb_->id(); } + + BasicBlock* bb_; + DominatorTreeNode* parent_; + std::vector children_; + + // These indexes are used to compare two given nodes. A node is a child or + // grandchild of another node if its preorder index is greater than the + // first nodes preorder index AND if its postorder index is less than the + // first nodes postorder index. + int dfs_num_pre_; + int dfs_num_post_; +}; + +// A class representing a tree of BasicBlocks in a given function, where each +// node is dominated by its parent. +class DominatorTree { + public: + // Map OpLabel ids to dominator tree nodes + using DominatorTreeNodeMap = std::map; + using iterator = TreeDFIterator; + using const_iterator = TreeDFIterator; + using post_iterator = PostOrderTreeDFIterator; + using const_post_iterator = PostOrderTreeDFIterator; + + // List of DominatorTreeNode to define the list of roots + using DominatorTreeNodeList = std::vector; + using roots_iterator = DominatorTreeNodeList::iterator; + using roots_const_iterator = DominatorTreeNodeList::const_iterator; + + DominatorTree() : postdominator_(false) {} + explicit DominatorTree(bool post) : postdominator_(post) {} + + // Depth first iterators. + // Traverse the dominator tree in a depth first pre-order. + // The pseudo-block is ignored. + iterator begin() { return ++iterator(GetRoot()); } + iterator end() { return iterator(); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + const_iterator cbegin() const { return ++const_iterator(GetRoot()); } + const_iterator cend() const { return const_iterator(); } + + // Traverse the dominator tree in a depth first post-order. + // The pseudo-block is ignored. + post_iterator post_begin() { return post_iterator::begin(GetRoot()); } + post_iterator post_end() { return post_iterator::end(GetRoot()); } + const_post_iterator post_begin() const { return post_cbegin(); } + const_post_iterator post_end() const { return post_cend(); } + const_post_iterator post_cbegin() const { + return const_post_iterator::begin(GetRoot()); + } + const_post_iterator post_cend() const { + return const_post_iterator::end(GetRoot()); + } + + roots_iterator roots_begin() { return roots_.begin(); } + roots_iterator roots_end() { return roots_.end(); } + roots_const_iterator roots_begin() const { return roots_cbegin(); } + roots_const_iterator roots_end() const { return roots_cend(); } + roots_const_iterator roots_cbegin() const { return roots_.begin(); } + roots_const_iterator roots_cend() const { return roots_.end(); } + + // Get the unique root of the tree. + // It is guaranteed to work on a dominator tree. + // post-dominator might have a list. + DominatorTreeNode* GetRoot() { + assert(roots_.size() == 1); + return *roots_.begin(); + } + + const DominatorTreeNode* GetRoot() const { + assert(roots_.size() == 1); + return *roots_.begin(); + } + + const DominatorTreeNodeList& Roots() const { return roots_; } + + // Dumps the tree in the graphvis dot format into the |out_stream|. + void DumpTreeAsDot(std::ostream& out_stream) const; + + // Build the (post-)dominator tree for the given control flow graph + // |cfg| and the function |f|. |f| must exist in the |cfg|. Any + // existing data in the dominator tree will be overwritten + void InitializeTree(const CFG& cfg, const Function* f); + + // Check if the basic block |a| dominates the basic block |b|. + bool Dominates(const BasicBlock* a, const BasicBlock* b) const; + + // Check if the basic block id |a| dominates the basic block id |b|. + bool Dominates(uint32_t a, uint32_t b) const; + + // Check if the dominator tree node |a| dominates the dominator tree node |b|. + bool Dominates(const DominatorTreeNode* a, const DominatorTreeNode* b) const; + + // Check if the basic block |a| strictly dominates the basic block |b|. + bool StrictlyDominates(const BasicBlock* a, const BasicBlock* b) const; + + // Check if the basic block id |a| strictly dominates the basic block id |b|. + bool StrictlyDominates(uint32_t a, uint32_t b) const; + + // Check if the dominator tree node |a| strictly dominates the dominator tree + // node |b|. + bool StrictlyDominates(const DominatorTreeNode* a, + const DominatorTreeNode* b) const; + + // Returns the immediate dominator of basic block |a|. + BasicBlock* ImmediateDominator(const BasicBlock* A) const; + + // Returns the immediate dominator of basic block id |a|. + BasicBlock* ImmediateDominator(uint32_t a) const; + + // Returns true if the basic block |a| is reachable by this tree. A node would + // be unreachable if it cannot be reached by traversal from the start node or + // for a postdominator tree, cannot be reached from the exit nodes. + inline bool ReachableFromRoots(const BasicBlock* a) const { + if (!a) return false; + return ReachableFromRoots(a->id()); + } + + // Returns true if the basic block id |a| is reachable by this tree. + bool ReachableFromRoots(uint32_t a) const { + return GetTreeNode(a) != nullptr; + } + + // Returns true if this tree is a post dominator tree. + bool IsPostDominator() const { return postdominator_; } + + // Clean up the tree. + void ClearTree() { + nodes_.clear(); + roots_.clear(); + } + + // Applies the std::function |func| to all nodes in the dominator tree. + // Tree nodes are visited in a depth first pre-order. + bool Visit(std::function func) { + for (auto n : *this) { + if (!func(&n)) return false; + } + return true; + } + + // Applies the std::function |func| to all nodes in the dominator tree. + // Tree nodes are visited in a depth first pre-order. + bool Visit(std::function func) const { + for (auto n : *this) { + if (!func(&n)) return false; + } + return true; + } + + // Applies the std::function |func| to all nodes in the dominator tree from + // |node| downwards. The boolean return from |func| is used to determine + // whether or not the children should also be traversed. Tree nodes are + // visited in a depth first pre-order. + void VisitChildrenIf(std::function func, + iterator node) { + if (func(&*node)) { + for (auto n : *node) { + VisitChildrenIf(func, n->df_begin()); + } + } + } + + // Returns the DominatorTreeNode associated with the basic block |bb|. + // If the |bb| is unknown to the dominator tree, it returns null. + inline DominatorTreeNode* GetTreeNode(BasicBlock* bb) { + return GetTreeNode(bb->id()); + } + // Returns the DominatorTreeNode associated with the basic block |bb|. + // If the |bb| is unknown to the dominator tree, it returns null. + inline const DominatorTreeNode* GetTreeNode(BasicBlock* bb) const { + return GetTreeNode(bb->id()); + } + + // Returns the DominatorTreeNode associated with the basic block id |id|. + // If the id |id| is unknown to the dominator tree, it returns null. + inline DominatorTreeNode* GetTreeNode(uint32_t id) { + DominatorTreeNodeMap::iterator node_iter = nodes_.find(id); + if (node_iter == nodes_.end()) { + return nullptr; + } + return &node_iter->second; + } + // Returns the DominatorTreeNode associated with the basic block id |id|. + // If the id |id| is unknown to the dominator tree, it returns null. + inline const DominatorTreeNode* GetTreeNode(uint32_t id) const { + DominatorTreeNodeMap::const_iterator node_iter = nodes_.find(id); + if (node_iter == nodes_.end()) { + return nullptr; + } + return &node_iter->second; + } + + // Adds the basic block |bb| to the tree structure if it doesn't already + // exist. + DominatorTreeNode* GetOrInsertNode(BasicBlock* bb); + + // Recomputes the DF numbering of the tree. + void ResetDFNumbering(); + + private: + // Wrapper function which gets the list of pairs of each BasicBlocks to its + // immediately dominating BasicBlock and stores the result in the the edges + // parameter. + // + // The |edges| vector will contain the dominator tree as pairs of nodes. + // The first node in the pair is a node in the graph. The second node in the + // pair is its immediate dominator. + // The root of the tree has themself as immediate dominator. + void GetDominatorEdges( + const Function* f, const BasicBlock* dummy_start_node, + std::vector>* edges); + + // The roots of the tree. + std::vector roots_; + + // Pairs each basic block id to the tree node containing that basic block. + DominatorTreeNodeMap nodes_; + + // True if this is a post dominator tree. + bool postdominator_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_DOMINATOR_TREE_H_ diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_constant_pass.cpp b/third_party/spirv-tools/source/opt/eliminate_dead_constant_pass.cpp new file mode 100644 index 0000000..d368bd1 --- /dev/null +++ b/third_party/spirv-tools/source/opt/eliminate_dead_constant_pass.cpp @@ -0,0 +1,104 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_constant_pass.h" + +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { + +Pass::Status EliminateDeadConstantPass::Process() { + std::unordered_set working_list; + // Traverse all the instructions to get the initial set of dead constants as + // working list and count number of real uses for constants. Uses in + // annotation instructions do not count. + std::unordered_map use_counts; + std::vector constants = context()->GetConstants(); + for (auto* c : constants) { + uint32_t const_id = c->result_id(); + size_t count = 0; + context()->get_def_use_mgr()->ForEachUse( + const_id, [&count](Instruction* user, uint32_t index) { + (void)index; + SpvOp op = user->opcode(); + if (!(IsAnnotationInst(op) || IsDebug1Inst(op) || IsDebug2Inst(op) || + IsDebug3Inst(op))) { + ++count; + } + }); + use_counts[c] = count; + if (!count) { + working_list.insert(c); + } + } + + // Start from the constants with 0 uses, back trace through the def-use chain + // to find all dead constants. + std::unordered_set dead_consts; + while (!working_list.empty()) { + Instruction* inst = *working_list.begin(); + // Back propagate if the instruction contains IDs in its operands. + switch (inst->opcode()) { + case SpvOp::SpvOpConstantComposite: + case SpvOp::SpvOpSpecConstantComposite: + case SpvOp::SpvOpSpecConstantOp: + for (uint32_t i = 0; i < inst->NumInOperands(); i++) { + // SpecConstantOp instruction contains 'opcode' as its operand. Need + // to exclude such operands when decreasing uses. + if (inst->GetInOperand(i).type != SPV_OPERAND_TYPE_ID) { + continue; + } + uint32_t operand_id = inst->GetSingleWordInOperand(i); + Instruction* def_inst = + context()->get_def_use_mgr()->GetDef(operand_id); + // If the use_count does not have any count for the def_inst, + // def_inst must not be a constant, and should be ignored here. + if (!use_counts.count(def_inst)) { + continue; + } + // The number of uses should never be less then 0, so it can not be + // less than 1 before it decreases. + SPIRV_ASSERT(consumer(), use_counts[def_inst] > 0); + --use_counts[def_inst]; + if (!use_counts[def_inst]) { + working_list.insert(def_inst); + } + } + break; + default: + break; + } + dead_consts.insert(inst); + working_list.erase(inst); + } + + // Turn all dead instructions and uses of them to nop + for (auto* dc : dead_consts) { + context()->KillDef(dc->result_id()); + } + return dead_consts.empty() ? Status::SuccessWithoutChange + : Status::SuccessWithChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_constant_pass.h b/third_party/spirv-tools/source/opt/eliminate_dead_constant_pass.h new file mode 100644 index 0000000..01692db --- /dev/null +++ b/third_party/spirv-tools/source/opt/eliminate_dead_constant_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_CONSTANT_PASS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_CONSTANT_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class EliminateDeadConstantPass : public Pass { + public: + const char* name() const override { return "eliminate-dead-const"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_CONSTANT_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp b/third_party/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp new file mode 100644 index 0000000..a465521 --- /dev/null +++ b/third_party/spirv-tools/source/opt/eliminate_dead_functions_pass.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_functions_pass.h" +#include "source/opt/eliminate_dead_functions_util.h" + +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status EliminateDeadFunctionsPass::Process() { + // Identify live functions first. Those that are not live + // are dead. + std::unordered_set live_function_set; + ProcessFunction mark_live = [&live_function_set](Function* fp) { + live_function_set.insert(fp); + return false; + }; + context()->ProcessReachableCallTree(mark_live); + + bool modified = false; + for (auto funcIter = get_module()->begin(); + funcIter != get_module()->end();) { + if (live_function_set.count(&*funcIter) == 0) { + modified = true; + funcIter = + eliminatedeadfunctionsutil::EliminateFunction(context(), &funcIter); + } else { + ++funcIter; + } + } + + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_functions_pass.h b/third_party/spirv-tools/source/opt/eliminate_dead_functions_pass.h new file mode 100644 index 0000000..6ed5c42 --- /dev/null +++ b/third_party/spirv-tools/source/opt/eliminate_dead_functions_pass.h @@ -0,0 +1,44 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_PASS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_PASS_H_ + +#include "source/opt/def_use_manager.h" +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class EliminateDeadFunctionsPass : public MemPass { + public: + const char* name() const override { return "eliminate-dead-functions"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + void EliminateFunction(Function* func); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_functions_util.cpp b/third_party/spirv-tools/source/opt/eliminate_dead_functions_util.cpp new file mode 100644 index 0000000..6b5234b --- /dev/null +++ b/third_party/spirv-tools/source/opt/eliminate_dead_functions_util.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "eliminate_dead_functions_util.h" + +namespace spvtools { +namespace opt { + +namespace eliminatedeadfunctionsutil { + +Module::iterator EliminateFunction(IRContext* context, + Module::iterator* func_iter) { + bool first_func = *func_iter == context->module()->begin(); + bool seen_func_end = false; + (*func_iter) + ->ForEachInst( + [context, first_func, func_iter, &seen_func_end](Instruction* inst) { + if (inst->opcode() == SpvOpFunctionEnd) { + seen_func_end = true; + } + // Move non-semantic instructions to the previous function or + // global values if this is the first function. + if (seen_func_end && inst->opcode() == SpvOpExtInst) { + assert(inst->IsNonSemanticInstruction()); + std::unique_ptr clone(inst->Clone(context)); + context->ForgetUses(inst); + context->AnalyzeDefUse(clone.get()); + if (first_func) { + context->AddGlobalValue(std::move(clone)); + } else { + auto prev_func_iter = *func_iter; + --prev_func_iter; + prev_func_iter->AddNonSemanticInstruction(std::move(clone)); + } + inst->ToNop(); + } else { + context->KillNonSemanticInfo(inst); + context->KillInst(inst); + } + }, + true, true); + return func_iter->Erase(); +} + +} // namespace eliminatedeadfunctionsutil +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_functions_util.h b/third_party/spirv-tools/source/opt/eliminate_dead_functions_util.h new file mode 100644 index 0000000..9fcce95 --- /dev/null +++ b/third_party/spirv-tools/source/opt/eliminate_dead_functions_util.h @@ -0,0 +1,36 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// Provides functionality for eliminating functions that are not needed, for use +// by various analyses and passes. +namespace eliminatedeadfunctionsutil { + +// Removes all of the function's instructions, removes the function from the +// module, and returns the next iterator. +Module::iterator EliminateFunction(IRContext* context, + Module::iterator* func_iter); + +} // namespace eliminatedeadfunctionsutil +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_FUNCTIONS_UTIL_H_ diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.cpp b/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.cpp new file mode 100644 index 0000000..5b8f4ec --- /dev/null +++ b/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.cpp @@ -0,0 +1,691 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/eliminate_dead_members_pass.h" + +#include "ir_builder.h" +#include "source/opt/ir_context.h" + +namespace { +const uint32_t kRemovedMember = 0xFFFFFFFF; +const uint32_t kSpecConstOpOpcodeIdx = 0; +} + +namespace spvtools { +namespace opt { + +Pass::Status EliminateDeadMembersPass::Process() { + if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) + return Status::SuccessWithoutChange; + + FindLiveMembers(); + if (RemoveDeadMembers()) { + return Status::SuccessWithChange; + } + return Status::SuccessWithoutChange; +} + +void EliminateDeadMembersPass::FindLiveMembers() { + // Until we have implemented the rewritting of OpSpecConsantOp instructions, + // we have to mark them as fully used just to be safe. + for (auto& inst : get_module()->types_values()) { + if (inst.opcode() == SpvOpSpecConstantOp) { + switch (inst.GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) { + case SpvOpCompositeExtract: + MarkMembersAsLiveForExtract(&inst); + break; + case SpvOpCompositeInsert: + // Nothing specific to do. + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + assert(false && "Not implemented yet."); + break; + default: + break; + } + } else if (inst.opcode() == SpvOpVariable) { + switch (inst.GetSingleWordInOperand(0)) { + case SpvStorageClassInput: + case SpvStorageClassOutput: + MarkPointeeTypeAsFullUsed(inst.type_id()); + break; + default: + break; + } + } + } + + for (const Function& func : *get_module()) { + FindLiveMembers(func); + } +} + +void EliminateDeadMembersPass::FindLiveMembers(const Function& function) { + function.ForEachInst( + [this](const Instruction* inst) { FindLiveMembers(inst); }); +} + +void EliminateDeadMembersPass::FindLiveMembers(const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpStore: + MarkMembersAsLiveForStore(inst); + break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + MarkMembersAsLiveForCopyMemory(inst); + break; + case SpvOpCompositeExtract: + MarkMembersAsLiveForExtract(inst); + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + MarkMembersAsLiveForAccessChain(inst); + break; + case SpvOpReturnValue: + // This should be an issue only if we are returning from the entry point. + // However, for now I will keep it more conservative because functions are + // often inlined leaving only the entry points. + MarkOperandTypeAsFullyUsed(inst, 0); + break; + case SpvOpArrayLength: + MarkMembersAsLiveForArrayLength(inst); + break; + case SpvOpLoad: + case SpvOpCompositeInsert: + case SpvOpCompositeConstruct: + break; + default: + // This path is here for safety. All instructions that can reference + // structs in a function body should be handled above. However, this will + // keep the pass valid, but not optimal, as new instructions get added + // or if something was missed. + MarkStructOperandsAsFullyUsed(inst); + break; + } +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForStore( + const Instruction* inst) { + // We should only have to mark the members as live if the store is to + // memory that is read outside of the shader. Other passes can remove all + // store to memory that is not visible outside of the shader, so we do not + // complicate the code for now. + assert(inst->opcode() == SpvOpStore); + uint32_t object_id = inst->GetSingleWordInOperand(1); + Instruction* object_inst = context()->get_def_use_mgr()->GetDef(object_id); + uint32_t object_type_id = object_inst->type_id(); + MarkTypeAsFullyUsed(object_type_id); +} + +void EliminateDeadMembersPass::MarkTypeAsFullyUsed(uint32_t type_id) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + assert(type_inst != nullptr); + if (type_inst->opcode() != SpvOpTypeStruct) { + return; + } + + // Mark every member of the current struct as used. + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + used_members_[type_id].insert(i); + } + + // Mark any sub struct as fully used. + for (uint32_t i = 0; i < type_inst->NumInOperands(); ++i) { + MarkTypeAsFullyUsed(type_inst->GetSingleWordInOperand(i)); + } +} + +void EliminateDeadMembersPass::MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id) { + Instruction* ptr_type_inst = get_def_use_mgr()->GetDef(ptr_type_id); + assert(ptr_type_inst->opcode() == SpvOpTypePointer); + MarkTypeAsFullyUsed(ptr_type_inst->GetSingleWordInOperand(1)); +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForCopyMemory( + const Instruction* inst) { + uint32_t target_id = inst->GetSingleWordInOperand(0); + Instruction* target_inst = get_def_use_mgr()->GetDef(target_id); + uint32_t pointer_type_id = target_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + MarkTypeAsFullyUsed(type_id); +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForExtract( + const Instruction* inst) { + assert(inst->opcode() == SpvOpCompositeExtract || + (inst->opcode() == SpvOpSpecConstantOp && + inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) == + SpvOpCompositeExtract)); + + uint32_t first_operand = (inst->opcode() == SpvOpSpecConstantOp ? 1 : 0); + uint32_t composite_id = inst->GetSingleWordInOperand(first_operand); + Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); + uint32_t type_id = composite_inst->type_id(); + + for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + uint32_t member_idx = inst->GetSingleWordInOperand(i); + switch (type_inst->opcode()) { + case SpvOpTypeStruct: + used_members_[type_id].insert(member_idx); + type_id = type_inst->GetSingleWordInOperand(member_idx); + break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + } + } +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForAccessChain( + const Instruction* inst) { + assert(inst->opcode() == SpvOpAccessChain || + inst->opcode() == SpvOpInBoundsAccessChain || + inst->opcode() == SpvOpPtrAccessChain || + inst->opcode() == SpvOpInBoundsPtrAccessChain); + + uint32_t pointer_id = inst->GetSingleWordInOperand(0); + Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id); + uint32_t pointer_type_id = pointer_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + // For a pointer access chain, we need to skip the |element| index. It is not + // a reference to the member of a struct, and it does not change the type. + uint32_t i = (inst->opcode() == SpvOpAccessChain || + inst->opcode() == SpvOpInBoundsAccessChain + ? 1 + : 2); + for (; i < inst->NumInOperands(); ++i) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeStruct: { + const analysis::IntConstant* member_idx = + const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i)) + ->AsIntConstant(); + assert(member_idx); + if (member_idx->type()->AsInteger()->width() == 32) { + used_members_[type_id].insert(member_idx->GetU32()); + type_id = type_inst->GetSingleWordInOperand(member_idx->GetU32()); + } else { + used_members_[type_id].insert( + static_cast(member_idx->GetU64())); + type_id = type_inst->GetSingleWordInOperand( + static_cast(member_idx->GetU64())); + } + } break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + } + } +} + +void EliminateDeadMembersPass::MarkOperandTypeAsFullyUsed( + const Instruction* inst, uint32_t in_idx) { + uint32_t op_id = inst->GetSingleWordInOperand(in_idx); + Instruction* op_inst = get_def_use_mgr()->GetDef(op_id); + MarkTypeAsFullyUsed(op_inst->type_id()); +} + +void EliminateDeadMembersPass::MarkMembersAsLiveForArrayLength( + const Instruction* inst) { + assert(inst->opcode() == SpvOpArrayLength); + uint32_t object_id = inst->GetSingleWordInOperand(0); + Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); + uint32_t pointer_type_id = object_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + used_members_[type_id].insert(inst->GetSingleWordInOperand(1)); +} + +bool EliminateDeadMembersPass::RemoveDeadMembers() { + bool modified = false; + + // First update all of the OpTypeStruct instructions. + get_module()->ForEachInst([&modified, this](Instruction* inst) { + switch (inst->opcode()) { + case SpvOpTypeStruct: + modified |= UpdateOpTypeStruct(inst); + break; + default: + break; + } + }); + + // Now update all of the instructions that reference the OpTypeStructs. + get_module()->ForEachInst([&modified, this](Instruction* inst) { + switch (inst->opcode()) { + case SpvOpMemberName: + modified |= UpdateOpMemberNameOrDecorate(inst); + break; + case SpvOpMemberDecorate: + modified |= UpdateOpMemberNameOrDecorate(inst); + break; + case SpvOpGroupMemberDecorate: + modified |= UpdateOpGroupMemberDecorate(inst); + break; + case SpvOpSpecConstantComposite: + case SpvOpConstantComposite: + case SpvOpCompositeConstruct: + modified |= UpdateConstantComposite(inst); + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + modified |= UpdateAccessChain(inst); + break; + case SpvOpCompositeExtract: + modified |= UpdateCompsiteExtract(inst); + break; + case SpvOpCompositeInsert: + modified |= UpdateCompositeInsert(inst); + break; + case SpvOpArrayLength: + modified |= UpdateOpArrayLength(inst); + break; + case SpvOpSpecConstantOp: + switch (inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx)) { + case SpvOpCompositeExtract: + modified |= UpdateCompsiteExtract(inst); + break; + case SpvOpCompositeInsert: + modified |= UpdateCompositeInsert(inst); + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + assert(false && "Not implemented yet."); + break; + default: + break; + } + break; + default: + break; + } + }); + return modified; +} + +bool EliminateDeadMembersPass::UpdateOpTypeStruct(Instruction* inst) { + assert(inst->opcode() == SpvOpTypeStruct); + + const auto& live_members = used_members_[inst->result_id()]; + if (live_members.size() == inst->NumInOperands()) { + return false; + } + + Instruction::OperandList new_operands; + for (uint32_t idx : live_members) { + new_operands.emplace_back(inst->GetInOperand(idx)); + } + + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +bool EliminateDeadMembersPass::UpdateOpMemberNameOrDecorate(Instruction* inst) { + assert(inst->opcode() == SpvOpMemberName || + inst->opcode() == SpvOpMemberDecorate); + + uint32_t type_id = inst->GetSingleWordInOperand(0); + auto live_members = used_members_.find(type_id); + if (live_members == used_members_.end()) { + return false; + } + + uint32_t orig_member_idx = inst->GetSingleWordInOperand(1); + uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx); + + if (new_member_idx == kRemovedMember) { + context()->KillInst(inst); + return true; + } + + if (new_member_idx == orig_member_idx) { + return false; + } + + inst->SetInOperand(1, {new_member_idx}); + return true; +} + +bool EliminateDeadMembersPass::UpdateOpGroupMemberDecorate(Instruction* inst) { + assert(inst->opcode() == SpvOpGroupMemberDecorate); + + bool modified = false; + + Instruction::OperandList new_operands; + new_operands.emplace_back(inst->GetInOperand(0)); + for (uint32_t i = 1; i < inst->NumInOperands(); i += 2) { + uint32_t type_id = inst->GetSingleWordInOperand(i); + uint32_t member_idx = inst->GetSingleWordInOperand(i + 1); + uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); + + if (new_member_idx == kRemovedMember) { + modified = true; + continue; + } + + new_operands.emplace_back(inst->GetOperand(i)); + if (new_member_idx != member_idx) { + new_operands.emplace_back( + Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); + modified = true; + } else { + new_operands.emplace_back(inst->GetOperand(i + 1)); + } + } + + if (!modified) { + return false; + } + + if (new_operands.size() == 1) { + context()->KillInst(inst); + return true; + } + + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +bool EliminateDeadMembersPass::UpdateConstantComposite(Instruction* inst) { + assert(inst->opcode() == SpvOpSpecConstantComposite || + inst->opcode() == SpvOpConstantComposite || + inst->opcode() == SpvOpCompositeConstruct); + uint32_t type_id = inst->type_id(); + + bool modified = false; + Instruction::OperandList new_operands; + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + uint32_t new_idx = GetNewMemberIndex(type_id, i); + if (new_idx == kRemovedMember) { + modified = true; + } else { + new_operands.emplace_back(inst->GetInOperand(i)); + } + } + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return modified; +} + +bool EliminateDeadMembersPass::UpdateAccessChain(Instruction* inst) { + assert(inst->opcode() == SpvOpAccessChain || + inst->opcode() == SpvOpInBoundsAccessChain || + inst->opcode() == SpvOpPtrAccessChain || + inst->opcode() == SpvOpInBoundsPtrAccessChain); + + uint32_t pointer_id = inst->GetSingleWordInOperand(0); + Instruction* pointer_inst = get_def_use_mgr()->GetDef(pointer_id); + uint32_t pointer_type_id = pointer_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + Instruction::OperandList new_operands; + bool modified = false; + new_operands.emplace_back(inst->GetInOperand(0)); + + // For pointer access chains we want to copy the element operand. + if (inst->opcode() == SpvOpPtrAccessChain || + inst->opcode() == SpvOpInBoundsPtrAccessChain) { + new_operands.emplace_back(inst->GetInOperand(1)); + } + + for (uint32_t i = static_cast(new_operands.size()); + i < inst->NumInOperands(); ++i) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeStruct: { + const analysis::IntConstant* member_idx = + const_mgr->FindDeclaredConstant(inst->GetSingleWordInOperand(i)) + ->AsIntConstant(); + assert(member_idx); + uint32_t orig_member_idx; + if (member_idx->type()->AsInteger()->width() == 32) { + orig_member_idx = member_idx->GetU32(); + } else { + orig_member_idx = static_cast(member_idx->GetU64()); + } + uint32_t new_member_idx = GetNewMemberIndex(type_id, orig_member_idx); + assert(new_member_idx != kRemovedMember); + if (orig_member_idx != new_member_idx) { + InstructionBuilder ir_builder( + context(), inst, + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping); + uint32_t const_id = + ir_builder.GetUintConstant(new_member_idx)->result_id(); + new_operands.emplace_back(Operand({SPV_OPERAND_TYPE_ID, {const_id}})); + modified = true; + } else { + new_operands.emplace_back(inst->GetInOperand(i)); + } + // The type will have already been rewritten, so use the new member + // index. + type_id = type_inst->GetSingleWordInOperand(new_member_idx); + } break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + new_operands.emplace_back(inst->GetInOperand(i)); + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + break; + } + } + + if (!modified) { + return false; + } + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +uint32_t EliminateDeadMembersPass::GetNewMemberIndex(uint32_t type_id, + uint32_t member_idx) { + auto live_members = used_members_.find(type_id); + if (live_members == used_members_.end()) { + return member_idx; + } + + auto current_member = live_members->second.find(member_idx); + if (current_member == live_members->second.end()) { + return kRemovedMember; + } + + return static_cast( + std::distance(live_members->second.begin(), current_member)); +} + +bool EliminateDeadMembersPass::UpdateCompsiteExtract(Instruction* inst) { + assert(inst->opcode() == SpvOpCompositeExtract || + (inst->opcode() == SpvOpSpecConstantOp && + inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) == + SpvOpCompositeExtract)); + + uint32_t first_operand = 0; + if (inst->opcode() == SpvOpSpecConstantOp) { + first_operand = 1; + } + uint32_t object_id = inst->GetSingleWordInOperand(first_operand); + Instruction* object_inst = get_def_use_mgr()->GetDef(object_id); + uint32_t type_id = object_inst->type_id(); + + Instruction::OperandList new_operands; + bool modified = false; + for (uint32_t i = 0; i < first_operand + 1; i++) { + new_operands.emplace_back(inst->GetInOperand(i)); + } + for (uint32_t i = first_operand + 1; i < inst->NumInOperands(); ++i) { + uint32_t member_idx = inst->GetSingleWordInOperand(i); + uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); + assert(new_member_idx != kRemovedMember); + if (member_idx != new_member_idx) { + modified = true; + } + new_operands.emplace_back( + Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); + + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeStruct: + // The type will have already been rewriten, so use the new member + // index. + type_id = type_inst->GetSingleWordInOperand(new_member_idx); + break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + } + } + + if (!modified) { + return false; + } + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +bool EliminateDeadMembersPass::UpdateCompositeInsert(Instruction* inst) { + assert(inst->opcode() == SpvOpCompositeInsert || + (inst->opcode() == SpvOpSpecConstantOp && + inst->GetSingleWordInOperand(kSpecConstOpOpcodeIdx) == + SpvOpCompositeInsert)); + + uint32_t first_operand = 0; + if (inst->opcode() == SpvOpSpecConstantOp) { + first_operand = 1; + } + + uint32_t composite_id = inst->GetSingleWordInOperand(first_operand + 1); + Instruction* composite_inst = get_def_use_mgr()->GetDef(composite_id); + uint32_t type_id = composite_inst->type_id(); + + Instruction::OperandList new_operands; + bool modified = false; + + for (uint32_t i = 0; i < first_operand + 2; ++i) { + new_operands.emplace_back(inst->GetInOperand(i)); + } + for (uint32_t i = first_operand + 2; i < inst->NumInOperands(); ++i) { + uint32_t member_idx = inst->GetSingleWordInOperand(i); + uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); + if (new_member_idx == kRemovedMember) { + context()->KillInst(inst); + return true; + } + + if (member_idx != new_member_idx) { + modified = true; + } + new_operands.emplace_back( + Operand({SPV_OPERAND_TYPE_LITERAL_INTEGER, {new_member_idx}})); + + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeStruct: + // The type will have already been rewritten, so use the new member + // index. + type_id = type_inst->GetSingleWordInOperand(new_member_idx); + break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + type_id = type_inst->GetSingleWordInOperand(0); + break; + default: + assert(false); + } + } + + if (!modified) { + return false; + } + inst->SetInOperands(std::move(new_operands)); + context()->UpdateDefUse(inst); + return true; +} + +bool EliminateDeadMembersPass::UpdateOpArrayLength(Instruction* inst) { + uint32_t struct_id = inst->GetSingleWordInOperand(0); + Instruction* struct_inst = get_def_use_mgr()->GetDef(struct_id); + uint32_t pointer_type_id = struct_inst->type_id(); + Instruction* pointer_type_inst = get_def_use_mgr()->GetDef(pointer_type_id); + uint32_t type_id = pointer_type_inst->GetSingleWordInOperand(1); + + uint32_t member_idx = inst->GetSingleWordInOperand(1); + uint32_t new_member_idx = GetNewMemberIndex(type_id, member_idx); + assert(new_member_idx != kRemovedMember); + + if (member_idx == new_member_idx) { + return false; + } + + inst->SetInOperand(1, {new_member_idx}); + context()->UpdateDefUse(inst); + return true; +} + +void EliminateDeadMembersPass::MarkStructOperandsAsFullyUsed( + const Instruction* inst) { + if (inst->type_id() != 0) { + MarkTypeAsFullyUsed(inst->type_id()); + } + + inst->ForEachInId([this](const uint32_t* id) { + Instruction* instruction = get_def_use_mgr()->GetDef(*id); + if (instruction->type_id() != 0) { + MarkTypeAsFullyUsed(instruction->type_id()); + } + }); +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.h b/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.h new file mode 100644 index 0000000..4feaa55 --- /dev/null +++ b/third_party/spirv-tools/source/opt/eliminate_dead_members_pass.h @@ -0,0 +1,146 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ELIMINATE_DEAD_MEMBERS_PASS_H_ +#define SOURCE_OPT_ELIMINATE_DEAD_MEMBERS_PASS_H_ + +#include "source/opt/def_use_manager.h" +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// Remove unused members from structures. The remaining members will remain at +// the same offset. +class EliminateDeadMembersPass : public MemPass { + public: + const char* name() const override { return "eliminate-dead-members"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping; + } + + private: + // Populate |used_members_| with the member of structures that are live in the + // current context. + void FindLiveMembers(); + + // Add to |used_members_| the member of structures that are live in + // |function|. + void FindLiveMembers(const Function& function); + // Add to |used_members_| the member of structures that are live in |inst|. + void FindLiveMembers(const Instruction* inst); + + // Add to |used_members_| the members that are live in the |OpStore| + // instruction |inst|. + void MarkMembersAsLiveForStore(const Instruction* inst); + + // Add to |used_members_| the members that are live in the |OpCopyMemory*| + // instruction |inst|. + void MarkMembersAsLiveForCopyMemory(const Instruction* inst); + + // Add to |used_members_| the members that are live in the + // |OpCompositeExtract| instruction |inst|. + void MarkMembersAsLiveForExtract(const Instruction* inst); + + // Add to |used_members_| the members that are live in the |Op*AccessChain| + // instruction |inst|. + void MarkMembersAsLiveForAccessChain(const Instruction* inst); + + // Add the member referenced by the OpArrayLength instruction |inst| to + // |uses_members_|. + void MarkMembersAsLiveForArrayLength(const Instruction* inst); + + // Remove dead members from structs and updates any instructions that need to + // be updated as a consequence. Return true if something changed. + bool RemoveDeadMembers(); + + // Update |inst|, which must be an |OpMemberName| or |OpMemberDecorate| + // instruction, so it references the correct member after the struct is + // updated. Return true if something changed. + bool UpdateOpMemberNameOrDecorate(Instruction* inst); + + // Update |inst|, which must be an |OpGroupMemberDecorate| instruction, so it + // references the correct member after the struct is updated. Return true if + // something changed. + bool UpdateOpGroupMemberDecorate(Instruction* inst); + + // Update the |OpTypeStruct| instruction |inst| my removing the members that + // are not live. Return true if something changed. + bool UpdateOpTypeStruct(Instruction* inst); + + // Update the |OpConstantComposite| instruction |inst| to match the change + // made to the type that was being generated. Return true if something + // changed. + bool UpdateConstantComposite(Instruction* inst); + + // Update the |Op*AccessChain| instruction |inst| to reference the correct + // members. All members referenced in the access chain must be live. This + // function must be called after the |OpTypeStruct| instruction for the type + // has been updated. Return true if something changed. + bool UpdateAccessChain(Instruction* inst); + + // Update the |OpCompositeExtract| instruction |inst| to reference the correct + // members. All members referenced in the instruction must be live. This + // function must be called after the |OpTypeStruct| instruction for the type + // has been updated. Return true if something changed. + bool UpdateCompsiteExtract(Instruction* inst); + + // Update the |OpCompositeInsert| instruction |inst| to reference the correct + // members. If the member being inserted is not live, then |inst| is killed. + // This function must be called after the |OpTypeStruct| instruction for the + // type has been updated. Return true if something changed. + bool UpdateCompositeInsert(Instruction* inst); + + // Update the |OpArrayLength| instruction |inst| to reference the correct + // member. The member referenced in the instruction must be live. Return true + // if something changed. + bool UpdateOpArrayLength(Instruction* inst); + + // Add all of the members of type |type_id| and members of any subtypes to + // |used_members_|. + void MarkTypeAsFullyUsed(uint32_t type_id); + + // Add all of the members of the type of the operand |in_idx| in |inst| and + // members of any subtypes to |uses_members_|. + void MarkOperandTypeAsFullyUsed(const Instruction* inst, uint32_t in_idx); + + // Return the index of the member that use to be the |member_idx|th member of + // |type_id|. If the member has been removed, |kRemovedMember| is returned. + uint32_t GetNewMemberIndex(uint32_t type_id, uint32_t member_idx); + + // A map from a type id to a set of indices representing the members of the + // type that are used, and must be kept. + std::unordered_map> used_members_; + void MarkStructOperandsAsFullyUsed(const Instruction* inst); + void MarkPointeeTypeAsFullUsed(uint32_t ptr_type_id); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ELIMINATE_DEAD_MEMBERS_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/empty_pass.h b/third_party/spirv-tools/source/opt/empty_pass.h new file mode 100644 index 0000000..1fd2ae5 --- /dev/null +++ b/third_party/spirv-tools/source/opt/empty_pass.h @@ -0,0 +1,36 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_EMPTY_PASS_H_ +#define SOURCE_OPT_EMPTY_PASS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class EmptyPass : public Pass { + public: + EmptyPass() {} + + const char* name() const override { return "empty-pass"; } + + Status Process() override { return Status::SuccessWithoutChange; } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_EMPTY_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/feature_manager.cpp b/third_party/spirv-tools/source/opt/feature_manager.cpp new file mode 100644 index 0000000..ad70c1e --- /dev/null +++ b/third_party/spirv-tools/source/opt/feature_manager.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/feature_manager.h" + +#include +#include +#include + +#include "source/enum_string_mapping.h" + +namespace spvtools { +namespace opt { + +void FeatureManager::Analyze(Module* module) { + AddExtensions(module); + AddCapabilities(module); + AddExtInstImportIds(module); +} + +void FeatureManager::AddExtensions(Module* module) { + for (auto ext : module->extensions()) { + AddExtension(&ext); + } +} + +void FeatureManager::AddExtension(Instruction* ext) { + assert(ext->opcode() == SpvOpExtension && + "Expecting an extension instruction."); + + const std::string name = + reinterpret_cast(ext->GetInOperand(0u).words.data()); + Extension extension; + if (GetExtensionFromString(name.c_str(), &extension)) { + extensions_.Add(extension); + } +} + +void FeatureManager::RemoveExtension(Extension ext) { + if (!extensions_.Contains(ext)) return; + extensions_.Remove(ext); +} + +void FeatureManager::AddCapability(SpvCapability cap) { + if (capabilities_.Contains(cap)) return; + + capabilities_.Add(cap); + + spv_operand_desc desc = {}; + if (SPV_SUCCESS == + grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) { + CapabilitySet(desc->numCapabilities, desc->capabilities) + .ForEach([this](SpvCapability c) { AddCapability(c); }); + } +} + +void FeatureManager::RemoveCapability(SpvCapability cap) { + if (!capabilities_.Contains(cap)) return; + capabilities_.Remove(cap); +} + +void FeatureManager::AddCapabilities(Module* module) { + for (Instruction& inst : module->capabilities()) { + AddCapability(static_cast(inst.GetSingleWordInOperand(0))); + } +} + +void FeatureManager::AddExtInstImportIds(Module* module) { + extinst_importid_GLSLstd450_ = module->GetExtInstImportId("GLSL.std.450"); + extinst_importid_OpenCL100DebugInfo_ = + module->GetExtInstImportId("OpenCL.DebugInfo.100"); +} + +bool operator==(const FeatureManager& a, const FeatureManager& b) { + // We check that the addresses of the grammars are the same because they + // are large objects, and this is faster. It can be changed if needed as a + // later time. + if (&a.grammar_ != &b.grammar_) { + return false; + } + + if (a.capabilities_ != b.capabilities_) { + return false; + } + + if (a.extensions_ != b.extensions_) { + return false; + } + + if (a.extinst_importid_GLSLstd450_ != b.extinst_importid_GLSLstd450_) { + return false; + } + + if (a.extinst_importid_OpenCL100DebugInfo_ != + b.extinst_importid_OpenCL100DebugInfo_) { + return false; + } + + return true; +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/feature_manager.h b/third_party/spirv-tools/source/opt/feature_manager.h new file mode 100644 index 0000000..66d1cba --- /dev/null +++ b/third_party/spirv-tools/source/opt/feature_manager.h @@ -0,0 +1,100 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FEATURE_MANAGER_H_ +#define SOURCE_OPT_FEATURE_MANAGER_H_ + +#include "source/assembly_grammar.h" +#include "source/extensions.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// Tracks features enabled by a module. The IRContext has a FeatureManager. +class FeatureManager { + public: + explicit FeatureManager(const AssemblyGrammar& grammar) : grammar_(grammar) {} + + // Returns true if |ext| is an enabled extension in the module. + bool HasExtension(Extension ext) const { return extensions_.Contains(ext); } + + // Removes the given |extension| from the current FeatureManager. + void RemoveExtension(Extension extension); + + // Returns true if |cap| is an enabled capability in the module. + bool HasCapability(SpvCapability cap) const { + return capabilities_.Contains(cap); + } + + // Removes the given |capability| from the current FeatureManager. + void RemoveCapability(SpvCapability capability); + + // Analyzes |module| and records enabled extensions and capabilities. + void Analyze(Module* module); + + CapabilitySet* GetCapabilities() { return &capabilities_; } + const CapabilitySet* GetCapabilities() const { return &capabilities_; } + + uint32_t GetExtInstImportId_GLSLstd450() const { + return extinst_importid_GLSLstd450_; + } + + uint32_t GetExtInstImportId_OpenCL100DebugInfo() const { + return extinst_importid_OpenCL100DebugInfo_; + } + + friend bool operator==(const FeatureManager& a, const FeatureManager& b); + friend bool operator!=(const FeatureManager& a, const FeatureManager& b) { + return !(a == b); + } + + // Adds the given |capability| and all implied capabilities into the current + // FeatureManager. + void AddCapability(SpvCapability capability); + + // Add the extension |ext| to the feature manager. + void AddExtension(Instruction* ext); + + // Analyzes |module| and records imported external instruction sets. + void AddExtInstImportIds(Module* module); + + private: + // Analyzes |module| and records enabled extensions. + void AddExtensions(Module* module); + + // Analyzes |module| and records enabled capabilities. + void AddCapabilities(Module* module); + + // Auxiliary object for querying SPIR-V grammar facts. + const AssemblyGrammar& grammar_; + + // The enabled extensions. + ExtensionSet extensions_; + + // The enabled capabilities. + CapabilitySet capabilities_; + + // Common external instruction import ids, cached for performance. + uint32_t extinst_importid_GLSLstd450_ = 0; + + // Common OpenCL100DebugInfo external instruction import ids, cached + // for performance. + uint32_t extinst_importid_OpenCL100DebugInfo_ = 0; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FEATURE_MANAGER_H_ diff --git a/third_party/spirv-tools/source/opt/fix_storage_class.cpp b/third_party/spirv-tools/source/opt/fix_storage_class.cpp new file mode 100644 index 0000000..03da0d0 --- /dev/null +++ b/third_party/spirv-tools/source/opt/fix_storage_class.cpp @@ -0,0 +1,330 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "fix_storage_class.h" + +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status FixStorageClass::Process() { + bool modified = false; + + get_module()->ForEachInst([this, &modified](Instruction* inst) { + if (inst->opcode() == SpvOpVariable) { + std::set seen; + std::vector> uses; + get_def_use_mgr()->ForEachUse(inst, + [&uses](Instruction* use, uint32_t op_idx) { + uses.push_back({use, op_idx}); + }); + + for (auto& use : uses) { + modified |= PropagateStorageClass( + use.first, + static_cast(inst->GetSingleWordInOperand(0)), + &seen); + assert(seen.empty() && "Seen was not properly reset."); + modified |= + PropagateType(use.first, inst->type_id(), use.second, &seen); + assert(seen.empty() && "Seen was not properly reset."); + } + } + }); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool FixStorageClass::PropagateStorageClass(Instruction* inst, + SpvStorageClass storage_class, + std::set* seen) { + if (!IsPointerResultType(inst)) { + return false; + } + + if (IsPointerToStorageClass(inst, storage_class)) { + if (inst->opcode() == SpvOpPhi) { + if (!seen->insert(inst->result_id()).second) { + return false; + } + } + + bool modified = false; + std::vector uses; + get_def_use_mgr()->ForEachUser( + inst, [&uses](Instruction* use) { uses.push_back(use); }); + for (Instruction* use : uses) { + modified |= PropagateStorageClass(use, storage_class, seen); + } + + if (inst->opcode() == SpvOpPhi) { + seen->erase(inst->result_id()); + } + return modified; + } + + switch (inst->opcode()) { + case SpvOpAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpCopyObject: + case SpvOpPhi: + case SpvOpSelect: + FixInstructionStorageClass(inst, storage_class, seen); + return true; + case SpvOpFunctionCall: + // We cannot be sure of the actual connection between the storage class + // of the parameter and the storage class of the result, so we should not + // do anything. If the result type needs to be fixed, the function call + // should be inlined. + return false; + case SpvOpImageTexelPointer: + case SpvOpLoad: + case SpvOpStore: + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + case SpvOpVariable: + case SpvOpBitcast: + // Nothing to change for these opcode. The result type is the same + // regardless of the storage class of the operand. + return false; + default: + assert(false && + "Not expecting instruction to have a pointer result type."); + return false; + } +} + +void FixStorageClass::FixInstructionStorageClass(Instruction* inst, + SpvStorageClass storage_class, + std::set* seen) { + assert(IsPointerResultType(inst) && + "The result type of the instruction must be a pointer."); + + ChangeResultStorageClass(inst, storage_class); + + std::vector uses; + get_def_use_mgr()->ForEachUser( + inst, [&uses](Instruction* use) { uses.push_back(use); }); + for (Instruction* use : uses) { + PropagateStorageClass(use, storage_class, seen); + } +} + +void FixStorageClass::ChangeResultStorageClass( + Instruction* inst, SpvStorageClass storage_class) const { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + Instruction* result_type_inst = get_def_use_mgr()->GetDef(inst->type_id()); + assert(result_type_inst->opcode() == SpvOpTypePointer); + uint32_t pointee_type_id = result_type_inst->GetSingleWordInOperand(1); + uint32_t new_result_type_id = + type_mgr->FindPointerToType(pointee_type_id, storage_class); + inst->SetResultType(new_result_type_id); + context()->UpdateDefUse(inst); +} + +bool FixStorageClass::IsPointerResultType(Instruction* inst) { + if (inst->type_id() == 0) { + return false; + } + const analysis::Type* ret_type = + context()->get_type_mgr()->GetType(inst->type_id()); + return ret_type->AsPointer() != nullptr; +} + +bool FixStorageClass::IsPointerToStorageClass(Instruction* inst, + SpvStorageClass storage_class) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* pType = type_mgr->GetType(inst->type_id()); + const analysis::Pointer* result_type = pType->AsPointer(); + + if (result_type == nullptr) { + return false; + } + + return (result_type->storage_class() == storage_class); +} + +bool FixStorageClass::ChangeResultType(Instruction* inst, + uint32_t new_type_id) { + if (inst->type_id() == new_type_id) { + return false; + } + + context()->ForgetUses(inst); + inst->SetResultType(new_type_id); + context()->AnalyzeUses(inst); + return true; +} + +bool FixStorageClass::PropagateType(Instruction* inst, uint32_t type_id, + uint32_t op_idx, std::set* seen) { + assert(type_id != 0 && "Not given a valid type in PropagateType"); + bool modified = false; + + // If the type of operand |op_idx| forces the result type of |inst| to a + // particular type, then we want find that type. + uint32_t new_type_id = 0; + switch (inst->opcode()) { + case SpvOpAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpInBoundsPtrAccessChain: + if (op_idx == 2) { + new_type_id = WalkAccessChainType(inst, type_id); + } + break; + case SpvOpCopyObject: + new_type_id = type_id; + break; + case SpvOpPhi: + if (seen->insert(inst->result_id()).second) { + new_type_id = type_id; + } + break; + case SpvOpSelect: + if (op_idx > 2) { + new_type_id = type_id; + } + break; + case SpvOpFunctionCall: + // We cannot be sure of the actual connection between the type + // of the parameter and the type of the result, so we should not + // do anything. If the result type needs to be fixed, the function call + // should be inlined. + return false; + case SpvOpLoad: { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + new_type_id = type_inst->GetSingleWordInOperand(1); + break; + } + case SpvOpStore: { + uint32_t obj_id = inst->GetSingleWordInOperand(1); + Instruction* obj_inst = get_def_use_mgr()->GetDef(obj_id); + uint32_t obj_type_id = obj_inst->type_id(); + + uint32_t ptr_id = inst->GetSingleWordInOperand(0); + Instruction* ptr_inst = get_def_use_mgr()->GetDef(ptr_id); + uint32_t pointee_type_id = GetPointeeTypeId(ptr_inst); + + if (obj_type_id != pointee_type_id) { + uint32_t copy_id = GenerateCopy(obj_inst, pointee_type_id, inst); + inst->SetInOperand(1, {copy_id}); + context()->UpdateDefUse(inst); + } + } break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + // TODO: May need to expand the copy as we do with the stores. + break; + case SpvOpCompositeConstruct: + case SpvOpCompositeExtract: + case SpvOpCompositeInsert: + // TODO: DXC does not seem to generate code that will require changes to + // these opcode. The can be implemented when they come up. + break; + case SpvOpImageTexelPointer: + case SpvOpBitcast: + // Nothing to change for these opcode. The result type is the same + // regardless of the type of the operand. + return false; + default: + // I expect the remaining instructions to act on types that are guaranteed + // to be unique, so no change will be necessary. + break; + } + + // If the operand forces the result type, then make sure the result type + // matches, and update the uses of |inst|. We do not have to check the uses + // of |inst| in the result type is not forced because we are only looking for + // issue that come from mismatches between function formal and actual + // parameters after the function has been inlined. These parameters are + // pointers. Once the type no longer depends on the type of the parameter, + // then the types should have be correct. + if (new_type_id != 0) { + modified = ChangeResultType(inst, new_type_id); + + std::vector> uses; + get_def_use_mgr()->ForEachUse(inst, + [&uses](Instruction* use, uint32_t idx) { + uses.push_back({use, idx}); + }); + + for (auto& use : uses) { + PropagateType(use.first, new_type_id, use.second, seen); + } + + if (inst->opcode() == SpvOpPhi) { + seen->erase(inst->result_id()); + } + } + return modified; +} + +uint32_t FixStorageClass::WalkAccessChainType(Instruction* inst, uint32_t id) { + uint32_t start_idx = 0; + switch (inst->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + start_idx = 1; + break; + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + start_idx = 2; + break; + default: + assert(false); + break; + } + + Instruction* orig_type_inst = get_def_use_mgr()->GetDef(id); + assert(orig_type_inst->opcode() == SpvOpTypePointer); + id = orig_type_inst->GetSingleWordInOperand(1); + + for (uint32_t i = start_idx; i < inst->NumInOperands(); ++i) { + Instruction* type_inst = get_def_use_mgr()->GetDef(id); + switch (type_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeMatrix: + case SpvOpTypeVector: + id = type_inst->GetSingleWordInOperand(0); + break; + case SpvOpTypeStruct: { + const analysis::Constant* index_const = + context()->get_constant_mgr()->FindDeclaredConstant( + inst->GetSingleWordInOperand(i)); + uint32_t index = index_const->GetU32(); + id = type_inst->GetSingleWordInOperand(index); + break; + } + default: + break; + } + assert(id != 0 && + "Tried to extract from an object where it cannot be done."); + } + + return context()->get_type_mgr()->FindPointerToType( + id, + static_cast(orig_type_inst->GetSingleWordInOperand(0))); +} + +// namespace opt + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/fix_storage_class.h b/third_party/spirv-tools/source/opt/fix_storage_class.h new file mode 100644 index 0000000..e72e864 --- /dev/null +++ b/third_party/spirv-tools/source/opt/fix_storage_class.h @@ -0,0 +1,93 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FIX_STORAGE_CLASS_H_ +#define SOURCE_OPT_FIX_STORAGE_CLASS_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass tries to fix validation error due to a mismatch of storage classes +// in instructions. There is no guarantee that all such error will be fixed, +// and it is possible that in fixing these errors, it could lead to other +// errors. +class FixStorageClass : public Pass { + public: + const char* name() const override { return "fix-storage-class"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Changes the storage class of the result of |inst| to |storage_class| in + // appropriate, and propagates the change to the users of |inst| as well. + // Returns true of any changes were made. + // |seen| is used to track OpPhi instructions that should not be processed. + bool PropagateStorageClass(Instruction* inst, SpvStorageClass storage_class, + std::set* seen); + + // Changes the storage class of the result of |inst| to |storage_class|. + // Is it assumed that the result type of |inst| is a pointer type. + // Propagates the change to the users of |inst| as well. + // Returns true of any changes were made. + // |seen| is used to track OpPhi instructions that should not be processed by + // |PropagateStorageClass| + void FixInstructionStorageClass(Instruction* inst, + SpvStorageClass storage_class, + std::set* seen); + + // Changes the storage class of the result of |inst| to |storage_class|. The + // result type of |inst| must be a pointer. + void ChangeResultStorageClass(Instruction* inst, + SpvStorageClass storage_class) const; + + // Returns true if the result type of |inst| is a pointer. + bool IsPointerResultType(Instruction* inst); + + // Returns true if the result of |inst| is a pointer to storage class + // |storage_class|. + bool IsPointerToStorageClass(Instruction* inst, + SpvStorageClass storage_class); + + // Change |inst| to match that operand |op_idx| now has type |type_id|, and + // adjust any uses of |inst| accordingly. Returns true if the code changed. + bool PropagateType(Instruction* inst, uint32_t type_id, uint32_t op_idx, + std::set* seen); + + // Changes the result type of |inst| to |new_type_id|. + bool ChangeResultType(Instruction* inst, uint32_t new_type_id); + + // Returns the type id of the member of the type |id| that would be returned + // by following the indices of the access chain instruction |inst|. + uint32_t WalkAccessChainType(Instruction* inst, uint32_t id); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FIX_STORAGE_CLASS_H_ diff --git a/third_party/spirv-tools/source/opt/flatten_decoration_pass.cpp b/third_party/spirv-tools/source/opt/flatten_decoration_pass.cpp new file mode 100644 index 0000000..f4de911 --- /dev/null +++ b/third_party/spirv-tools/source/opt/flatten_decoration_pass.cpp @@ -0,0 +1,165 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/flatten_decoration_pass.h" + +#include +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +using Words = std::vector; +using OrderedUsesMap = std::unordered_map; + +Pass::Status FlattenDecorationPass::Process() { + bool modified = false; + + // The target Id of OpDecorationGroup instructions. + // We have to track this separately from its uses, in case it + // has no uses. + std::unordered_set group_ids; + // Maps a decoration group Id to its GroupDecorate targets, in order + // of appearance. + OrderedUsesMap normal_uses; + // Maps a decoration group Id to its GroupMemberDecorate targets and + // their indices, in of appearance. + OrderedUsesMap member_uses; + + auto annotations = context()->annotations(); + + // On the first pass, record each OpDecorationGroup with its ordered uses. + // Rely on unordered_map::operator[] to create its entries on first access. + for (const auto& inst : annotations) { + switch (inst.opcode()) { + case SpvOp::SpvOpDecorationGroup: + group_ids.insert(inst.result_id()); + break; + case SpvOp::SpvOpGroupDecorate: { + Words& words = normal_uses[inst.GetSingleWordInOperand(0)]; + for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) { + words.push_back(inst.GetSingleWordInOperand(i)); + } + } break; + case SpvOp::SpvOpGroupMemberDecorate: { + Words& words = member_uses[inst.GetSingleWordInOperand(0)]; + for (uint32_t i = 1; i < inst.NumInOperandWords(); i++) { + words.push_back(inst.GetSingleWordInOperand(i)); + } + } break; + default: + break; + } + } + + // On the second pass, replace OpDecorationGroup and its uses with + // equivalent normal and struct member uses. + auto inst_iter = annotations.begin(); + // We have to re-evaluate the end pointer + while (inst_iter != context()->annotations().end()) { + // Should we replace this instruction? + bool replace = false; + switch (inst_iter->opcode()) { + case SpvOp::SpvOpDecorationGroup: + case SpvOp::SpvOpGroupDecorate: + case SpvOp::SpvOpGroupMemberDecorate: + replace = true; + break; + case SpvOp::SpvOpDecorate: { + // If this decoration targets a group, then replace it + // by sets of normal and member decorations. + const uint32_t group = inst_iter->GetSingleWordOperand(0); + const auto normal_uses_iter = normal_uses.find(group); + if (normal_uses_iter != normal_uses.end()) { + for (auto target : normal_uses[group]) { + std::unique_ptr new_inst(inst_iter->Clone(context())); + new_inst->SetInOperand(0, Words{target}); + inst_iter = inst_iter.InsertBefore(std::move(new_inst)); + ++inst_iter; + replace = true; + } + } + const auto member_uses_iter = member_uses.find(group); + if (member_uses_iter != member_uses.end()) { + const Words& member_id_pairs = (*member_uses_iter).second; + // The collection is a sequence of pairs. + assert((member_id_pairs.size() % 2) == 0); + for (size_t i = 0; i < member_id_pairs.size(); i += 2) { + // Make an OpMemberDecorate instruction for each (target, member) + // pair. + const uint32_t target = member_id_pairs[i]; + const uint32_t member = member_id_pairs[i + 1]; + std::vector operands; + operands.push_back(Operand(SPV_OPERAND_TYPE_ID, {target})); + operands.push_back( + Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {member})); + auto decoration_operands_iter = inst_iter->begin(); + decoration_operands_iter++; // Skip the group target. + operands.insert(operands.end(), decoration_operands_iter, + inst_iter->end()); + std::unique_ptr new_inst(new Instruction( + context(), SpvOp::SpvOpMemberDecorate, 0, 0, operands)); + inst_iter = inst_iter.InsertBefore(std::move(new_inst)); + ++inst_iter; + replace = true; + } + } + // If this is an OpDecorate targeting the OpDecorationGroup itself, + // remove it even if that decoration group itself is not the target of + // any OpGroupDecorate or OpGroupMemberDecorate. + if (!replace && group_ids.count(group)) { + replace = true; + } + } break; + default: + break; + } + if (replace) { + inst_iter = inst_iter.Erase(); + modified = true; + } else { + // Handle the case of decorations unrelated to decoration groups. + ++inst_iter; + } + } + + // Remove OpName instructions which reference the removed group decorations. + // An OpDecorationGroup instruction might not have been used by an + // OpGroupDecorate or OpGroupMemberDecorate instruction. + if (!group_ids.empty()) { + for (auto debug_inst_iter = context()->debug2_begin(); + debug_inst_iter != context()->debug2_end();) { + if (debug_inst_iter->opcode() == SpvOp::SpvOpName) { + const uint32_t target = debug_inst_iter->GetSingleWordOperand(0); + if (group_ids.count(target)) { + debug_inst_iter = debug_inst_iter.Erase(); + modified = true; + } else { + ++debug_inst_iter; + } + } + } + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/flatten_decoration_pass.h b/third_party/spirv-tools/source/opt/flatten_decoration_pass.h new file mode 100644 index 0000000..6a34f5b --- /dev/null +++ b/third_party/spirv-tools/source/opt/flatten_decoration_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FLATTEN_DECORATION_PASS_H_ +#define SOURCE_OPT_FLATTEN_DECORATION_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class FlattenDecorationPass : public Pass { + public: + const char* name() const override { return "flatten-decorations"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FLATTEN_DECORATION_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/fold.cpp b/third_party/spirv-tools/source/opt/fold.cpp new file mode 100644 index 0000000..6550fb4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/fold.cpp @@ -0,0 +1,711 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/fold.h" + +#include +#include +#include + +#include "source/opt/const_folding_rules.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/folding_rules.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +#ifndef INT32_MIN +#define INT32_MIN (-2147483648) +#endif + +#ifndef INT32_MAX +#define INT32_MAX 2147483647 +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX 0xffffffff /* 4294967295U */ +#endif + +} // namespace + +uint32_t InstructionFolder::UnaryOperate(SpvOp opcode, uint32_t operand) const { + switch (opcode) { + // Arthimetics + case SpvOp::SpvOpSNegate: { + int32_t s_operand = static_cast(operand); + if (s_operand == std::numeric_limits::min()) { + return s_operand; + } + return -s_operand; + } + case SpvOp::SpvOpNot: + return ~operand; + case SpvOp::SpvOpLogicalNot: + return !static_cast(operand); + case SpvOp::SpvOpUConvert: + return operand; + case SpvOp::SpvOpSConvert: + return operand; + default: + assert(false && + "Unsupported unary operation for OpSpecConstantOp instruction"); + return 0u; + } +} + +uint32_t InstructionFolder::BinaryOperate(SpvOp opcode, uint32_t a, + uint32_t b) const { + switch (opcode) { + // Arthimetics + case SpvOp::SpvOpIAdd: + return a + b; + case SpvOp::SpvOpISub: + return a - b; + case SpvOp::SpvOpIMul: + return a * b; + case SpvOp::SpvOpUDiv: + if (b != 0) { + return a / b; + } else { + // Dividing by 0 is undefined, so we will just pick 0. + return 0; + } + case SpvOp::SpvOpSDiv: + if (b != 0u) { + return (static_cast(a)) / (static_cast(b)); + } else { + // Dividing by 0 is undefined, so we will just pick 0. + return 0; + } + case SpvOp::SpvOpSRem: { + // The sign of non-zero result comes from the first operand: a. This is + // guaranteed by C++11 rules for integer division operator. The division + // result is rounded toward zero, so the result of '%' has the sign of + // the first operand. + if (b != 0u) { + return static_cast(a) % static_cast(b); + } else { + // Remainder when dividing with 0 is undefined, so we will just pick 0. + return 0; + } + } + case SpvOp::SpvOpSMod: { + // The sign of non-zero result comes from the second operand: b + if (b != 0u) { + int32_t rem = BinaryOperate(SpvOp::SpvOpSRem, a, b); + int32_t b_prim = static_cast(b); + return (rem + b_prim) % b_prim; + } else { + // Mod with 0 is undefined, so we will just pick 0. + return 0; + } + } + case SpvOp::SpvOpUMod: + if (b != 0u) { + return (a % b); + } else { + // Mod with 0 is undefined, so we will just pick 0. + return 0; + } + + // Shifting + case SpvOp::SpvOpShiftRightLogical: + if (b >= 32) { + // This is undefined behaviour when |b| > 32. Choose 0 for consistency. + // When |b| == 32, doing the shift in C++ in undefined, but the result + // will be 0, so just return that value. + return 0; + } + return a >> b; + case SpvOp::SpvOpShiftRightArithmetic: + if (b > 32) { + // This is undefined behaviour. Choose 0 for consistency. + return 0; + } + if (b == 32) { + // Doing the shift in C++ is undefined, but the result is defined in the + // spir-v spec. Find that value another way. + if (static_cast(a) >= 0) { + return 0; + } else { + return static_cast(-1); + } + } + return (static_cast(a)) >> b; + case SpvOp::SpvOpShiftLeftLogical: + if (b >= 32) { + // This is undefined behaviour when |b| > 32. Choose 0 for consistency. + // When |b| == 32, doing the shift in C++ in undefined, but the result + // will be 0, so just return that value. + return 0; + } + return a << b; + + // Bitwise operations + case SpvOp::SpvOpBitwiseOr: + return a | b; + case SpvOp::SpvOpBitwiseAnd: + return a & b; + case SpvOp::SpvOpBitwiseXor: + return a ^ b; + + // Logical + case SpvOp::SpvOpLogicalEqual: + return (static_cast(a)) == (static_cast(b)); + case SpvOp::SpvOpLogicalNotEqual: + return (static_cast(a)) != (static_cast(b)); + case SpvOp::SpvOpLogicalOr: + return (static_cast(a)) || (static_cast(b)); + case SpvOp::SpvOpLogicalAnd: + return (static_cast(a)) && (static_cast(b)); + + // Comparison + case SpvOp::SpvOpIEqual: + return a == b; + case SpvOp::SpvOpINotEqual: + return a != b; + case SpvOp::SpvOpULessThan: + return a < b; + case SpvOp::SpvOpSLessThan: + return (static_cast(a)) < (static_cast(b)); + case SpvOp::SpvOpUGreaterThan: + return a > b; + case SpvOp::SpvOpSGreaterThan: + return (static_cast(a)) > (static_cast(b)); + case SpvOp::SpvOpULessThanEqual: + return a <= b; + case SpvOp::SpvOpSLessThanEqual: + return (static_cast(a)) <= (static_cast(b)); + case SpvOp::SpvOpUGreaterThanEqual: + return a >= b; + case SpvOp::SpvOpSGreaterThanEqual: + return (static_cast(a)) >= (static_cast(b)); + default: + assert(false && + "Unsupported binary operation for OpSpecConstantOp instruction"); + return 0u; + } +} + +uint32_t InstructionFolder::TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b, + uint32_t c) const { + switch (opcode) { + case SpvOp::SpvOpSelect: + return (static_cast(a)) ? b : c; + default: + assert(false && + "Unsupported ternary operation for OpSpecConstantOp instruction"); + return 0u; + } +} + +uint32_t InstructionFolder::OperateWords( + SpvOp opcode, const std::vector& operand_words) const { + switch (operand_words.size()) { + case 1: + return UnaryOperate(opcode, operand_words.front()); + case 2: + return BinaryOperate(opcode, operand_words.front(), operand_words.back()); + case 3: + return TernaryOperate(opcode, operand_words[0], operand_words[1], + operand_words[2]); + default: + assert(false && "Invalid number of operands"); + return 0; + } +} + +bool InstructionFolder::FoldInstructionInternal(Instruction* inst) const { + auto identity_map = [](uint32_t id) { return id; }; + Instruction* folded_inst = FoldInstructionToConstant(inst, identity_map); + if (folded_inst != nullptr) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {folded_inst->result_id()}}}); + return true; + } + + analysis::ConstantManager* const_manager = context_->get_constant_mgr(); + std::vector constants = + const_manager->GetOperandConstants(inst); + + for (const FoldingRule& rule : + GetFoldingRules().GetRulesForInstruction(inst)) { + if (rule(context_, inst, constants)) { + return true; + } + } + return false; +} + +// Returns the result of performing an operation on scalar constant operands. +// This function extracts the operand values as 32 bit words and returns the +// result in 32 bit word. Scalar constants with longer than 32-bit width are +// not accepted in this function. +uint32_t InstructionFolder::FoldScalars( + SpvOp opcode, + const std::vector& operands) const { + assert(IsFoldableOpcode(opcode) && + "Unhandled instruction opcode in FoldScalars"); + std::vector operand_values_in_raw_words; + for (const auto& operand : operands) { + if (const analysis::ScalarConstant* scalar = operand->AsScalarConstant()) { + const auto& scalar_words = scalar->words(); + assert(scalar_words.size() == 1 && + "Scalar constants with longer than 32-bit width are not allowed " + "in FoldScalars()"); + operand_values_in_raw_words.push_back(scalar_words.front()); + } else if (operand->AsNullConstant()) { + operand_values_in_raw_words.push_back(0u); + } else { + assert(false && + "FoldScalars() only accepts ScalarConst or NullConst type of " + "constant"); + } + } + return OperateWords(opcode, operand_values_in_raw_words); +} + +bool InstructionFolder::FoldBinaryIntegerOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const { + SpvOp opcode = inst->opcode(); + analysis::ConstantManager* const_manger = context_->get_constant_mgr(); + + uint32_t ids[2]; + const analysis::IntConstant* constants[2]; + for (uint32_t i = 0; i < 2; i++) { + const Operand* operand = &inst->GetInOperand(i); + if (operand->type != SPV_OPERAND_TYPE_ID) { + return false; + } + ids[i] = id_map(operand->words[0]); + const analysis::Constant* constant = + const_manger->FindDeclaredConstant(ids[i]); + constants[i] = (constant != nullptr ? constant->AsIntConstant() : nullptr); + } + + switch (opcode) { + // Arthimetics + case SpvOp::SpvOpIMul: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr && constants[i]->IsZero()) { + *result = 0; + return true; + } + } + break; + case SpvOp::SpvOpUDiv: + case SpvOp::SpvOpSDiv: + case SpvOp::SpvOpSRem: + case SpvOp::SpvOpSMod: + case SpvOp::SpvOpUMod: + // This changes undefined behaviour (ie divide by 0) into a 0. + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr && constants[i]->IsZero()) { + *result = 0; + return true; + } + } + break; + + // Shifting + case SpvOp::SpvOpShiftRightLogical: + case SpvOp::SpvOpShiftLeftLogical: + if (constants[1] != nullptr) { + // When shifting by a value larger than the size of the result, the + // result is undefined. We are setting the undefined behaviour to a + // result of 0. If the shift amount is the same as the size of the + // result, then the result is defined, and it 0. + uint32_t shift_amount = constants[1]->GetU32BitValue(); + if (shift_amount >= 32) { + *result = 0; + return true; + } + } + break; + + // Bitwise operations + case SpvOp::SpvOpBitwiseOr: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr) { + // TODO: Change the mask against a value based on the bit width of the + // instruction result type. This way we can handle say 16-bit values + // as well. + uint32_t mask = constants[i]->GetU32BitValue(); + if (mask == 0xFFFFFFFF) { + *result = 0xFFFFFFFF; + return true; + } + } + } + break; + case SpvOp::SpvOpBitwiseAnd: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr) { + if (constants[i]->IsZero()) { + *result = 0; + return true; + } + } + } + break; + + // Comparison + case SpvOp::SpvOpULessThan: + if (constants[0] != nullptr && + constants[0]->GetU32BitValue() == UINT32_MAX) { + *result = false; + return true; + } + if (constants[1] != nullptr && constants[1]->GetU32BitValue() == 0) { + *result = false; + return true; + } + break; + case SpvOp::SpvOpSLessThan: + if (constants[0] != nullptr && + constants[0]->GetS32BitValue() == INT32_MAX) { + *result = false; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetS32BitValue() == INT32_MIN) { + *result = false; + return true; + } + break; + case SpvOp::SpvOpUGreaterThan: + if (constants[0] != nullptr && constants[0]->IsZero()) { + *result = false; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetU32BitValue() == UINT32_MAX) { + *result = false; + return true; + } + break; + case SpvOp::SpvOpSGreaterThan: + if (constants[0] != nullptr && + constants[0]->GetS32BitValue() == INT32_MIN) { + *result = false; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetS32BitValue() == INT32_MAX) { + *result = false; + return true; + } + break; + case SpvOp::SpvOpULessThanEqual: + if (constants[0] != nullptr && constants[0]->IsZero()) { + *result = true; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetU32BitValue() == UINT32_MAX) { + *result = true; + return true; + } + break; + case SpvOp::SpvOpSLessThanEqual: + if (constants[0] != nullptr && + constants[0]->GetS32BitValue() == INT32_MIN) { + *result = true; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetS32BitValue() == INT32_MAX) { + *result = true; + return true; + } + break; + case SpvOp::SpvOpUGreaterThanEqual: + if (constants[0] != nullptr && + constants[0]->GetU32BitValue() == UINT32_MAX) { + *result = true; + return true; + } + if (constants[1] != nullptr && constants[1]->GetU32BitValue() == 0) { + *result = true; + return true; + } + break; + case SpvOp::SpvOpSGreaterThanEqual: + if (constants[0] != nullptr && + constants[0]->GetS32BitValue() == INT32_MAX) { + *result = true; + return true; + } + if (constants[1] != nullptr && + constants[1]->GetS32BitValue() == INT32_MIN) { + *result = true; + return true; + } + break; + default: + break; + } + return false; +} + +bool InstructionFolder::FoldBinaryBooleanOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const { + SpvOp opcode = inst->opcode(); + analysis::ConstantManager* const_manger = context_->get_constant_mgr(); + + uint32_t ids[2]; + const analysis::BoolConstant* constants[2]; + for (uint32_t i = 0; i < 2; i++) { + const Operand* operand = &inst->GetInOperand(i); + if (operand->type != SPV_OPERAND_TYPE_ID) { + return false; + } + ids[i] = id_map(operand->words[0]); + const analysis::Constant* constant = + const_manger->FindDeclaredConstant(ids[i]); + constants[i] = (constant != nullptr ? constant->AsBoolConstant() : nullptr); + } + + switch (opcode) { + // Logical + case SpvOp::SpvOpLogicalOr: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr) { + if (constants[i]->value()) { + *result = true; + return true; + } + } + } + break; + case SpvOp::SpvOpLogicalAnd: + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] != nullptr) { + if (!constants[i]->value()) { + *result = false; + return true; + } + } + } + break; + + default: + break; + } + return false; +} + +bool InstructionFolder::FoldIntegerOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const { + assert(IsFoldableOpcode(inst->opcode()) && + "Unhandled instruction opcode in FoldScalars"); + switch (inst->NumInOperands()) { + case 2: + return FoldBinaryIntegerOpToConstant(inst, id_map, result) || + FoldBinaryBooleanOpToConstant(inst, id_map, result); + default: + return false; + } +} + +std::vector InstructionFolder::FoldVectors( + SpvOp opcode, uint32_t num_dims, + const std::vector& operands) const { + assert(IsFoldableOpcode(opcode) && + "Unhandled instruction opcode in FoldVectors"); + std::vector result; + for (uint32_t d = 0; d < num_dims; d++) { + std::vector operand_values_for_one_dimension; + for (const auto& operand : operands) { + if (const analysis::VectorConstant* vector_operand = + operand->AsVectorConstant()) { + // Extract the raw value of the scalar component constants + // in 32-bit words here. The reason of not using FoldScalars() here + // is that we do not create temporary null constants as components + // when the vector operand is a NullConstant because Constant creation + // may need extra checks for the validity and that is not manageed in + // here. + if (const analysis::ScalarConstant* scalar_component = + vector_operand->GetComponents().at(d)->AsScalarConstant()) { + const auto& scalar_words = scalar_component->words(); + assert( + scalar_words.size() == 1 && + "Vector components with longer than 32-bit width are not allowed " + "in FoldVectors()"); + operand_values_for_one_dimension.push_back(scalar_words.front()); + } else if (operand->AsNullConstant()) { + operand_values_for_one_dimension.push_back(0u); + } else { + assert(false && + "VectorConst should only has ScalarConst or NullConst as " + "components"); + } + } else if (operand->AsNullConstant()) { + operand_values_for_one_dimension.push_back(0u); + } else { + assert(false && + "FoldVectors() only accepts VectorConst or NullConst type of " + "constant"); + } + } + result.push_back(OperateWords(opcode, operand_values_for_one_dimension)); + } + return result; +} + +bool InstructionFolder::IsFoldableOpcode(SpvOp opcode) const { + // NOTE: Extend to more opcodes as new cases are handled in the folder + // functions. + switch (opcode) { + case SpvOp::SpvOpBitwiseAnd: + case SpvOp::SpvOpBitwiseOr: + case SpvOp::SpvOpBitwiseXor: + case SpvOp::SpvOpIAdd: + case SpvOp::SpvOpIEqual: + case SpvOp::SpvOpIMul: + case SpvOp::SpvOpINotEqual: + case SpvOp::SpvOpISub: + case SpvOp::SpvOpLogicalAnd: + case SpvOp::SpvOpLogicalEqual: + case SpvOp::SpvOpLogicalNot: + case SpvOp::SpvOpLogicalNotEqual: + case SpvOp::SpvOpLogicalOr: + case SpvOp::SpvOpNot: + case SpvOp::SpvOpSDiv: + case SpvOp::SpvOpSelect: + case SpvOp::SpvOpSGreaterThan: + case SpvOp::SpvOpSGreaterThanEqual: + case SpvOp::SpvOpShiftLeftLogical: + case SpvOp::SpvOpShiftRightArithmetic: + case SpvOp::SpvOpShiftRightLogical: + case SpvOp::SpvOpSLessThan: + case SpvOp::SpvOpSLessThanEqual: + case SpvOp::SpvOpSMod: + case SpvOp::SpvOpSNegate: + case SpvOp::SpvOpSRem: + case SpvOp::SpvOpSConvert: + case SpvOp::SpvOpUConvert: + case SpvOp::SpvOpUDiv: + case SpvOp::SpvOpUGreaterThan: + case SpvOp::SpvOpUGreaterThanEqual: + case SpvOp::SpvOpULessThan: + case SpvOp::SpvOpULessThanEqual: + case SpvOp::SpvOpUMod: + return true; + default: + return false; + } +} + +bool InstructionFolder::IsFoldableConstant( + const analysis::Constant* cst) const { + // Currently supported constants are 32-bit values or null constants. + if (const analysis::ScalarConstant* scalar = cst->AsScalarConstant()) + return scalar->words().size() == 1; + else + return cst->AsNullConstant() != nullptr; +} + +Instruction* InstructionFolder::FoldInstructionToConstant( + Instruction* inst, std::function id_map) const { + analysis::ConstantManager* const_mgr = context_->get_constant_mgr(); + + if (!inst->IsFoldableByFoldScalar() && + !GetConstantFoldingRules().HasFoldingRule(inst)) { + return nullptr; + } + // Collect the values of the constant parameters. + std::vector constants; + bool missing_constants = false; + inst->ForEachInId([&constants, &missing_constants, const_mgr, + &id_map](uint32_t* op_id) { + uint32_t id = id_map(*op_id); + const analysis::Constant* const_op = const_mgr->FindDeclaredConstant(id); + if (!const_op) { + constants.push_back(nullptr); + missing_constants = true; + } else { + constants.push_back(const_op); + } + }); + + const analysis::Constant* folded_const = nullptr; + for (auto rule : GetConstantFoldingRules().GetRulesForInstruction(inst)) { + folded_const = rule(context_, inst, constants); + if (folded_const != nullptr) { + Instruction* const_inst = + const_mgr->GetDefiningInstruction(folded_const, inst->type_id()); + if (const_inst == nullptr) { + return nullptr; + } + assert(const_inst->type_id() == inst->type_id()); + // May be a new instruction that needs to be analysed. + context_->UpdateDefUse(const_inst); + return const_inst; + } + } + + uint32_t result_val = 0; + bool successful = false; + // If all parameters are constant, fold the instruction to a constant. + if (!missing_constants && inst->IsFoldableByFoldScalar()) { + result_val = FoldScalars(inst->opcode(), constants); + successful = true; + } + + if (!successful && inst->IsFoldableByFoldScalar()) { + successful = FoldIntegerOpToConstant(inst, id_map, &result_val); + } + + if (successful) { + const analysis::Constant* result_const = + const_mgr->GetConstant(const_mgr->GetType(inst), {result_val}); + Instruction* folded_inst = + const_mgr->GetDefiningInstruction(result_const, inst->type_id()); + return folded_inst; + } + return nullptr; +} + +bool InstructionFolder::IsFoldableType(Instruction* type_inst) const { + // Support 32-bit integers. + if (type_inst->opcode() == SpvOpTypeInt) { + return type_inst->GetSingleWordInOperand(0) == 32; + } + // Support booleans. + if (type_inst->opcode() == SpvOpTypeBool) { + return true; + } + // Nothing else yet. + return false; +} + +bool InstructionFolder::FoldInstruction(Instruction* inst) const { + bool modified = false; + Instruction* folded_inst(inst); + while (folded_inst->opcode() != SpvOpCopyObject && + FoldInstructionInternal(&*folded_inst)) { + modified = true; + } + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/fold.h b/third_party/spirv-tools/source/opt/fold.h new file mode 100644 index 0000000..9e7c470 --- /dev/null +++ b/third_party/spirv-tools/source/opt/fold.h @@ -0,0 +1,187 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FOLD_H_ +#define SOURCE_OPT_FOLD_H_ + +#include +#include + +#include "source/opt/const_folding_rules.h" +#include "source/opt/constants.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/folding_rules.h" + +namespace spvtools { +namespace opt { + +class InstructionFolder { + public: + explicit InstructionFolder(IRContext* context) + : context_(context), + const_folding_rules_(new ConstantFoldingRules(context)), + folding_rules_(new FoldingRules(context)) { + folding_rules_->AddFoldingRules(); + const_folding_rules_->AddFoldingRules(); + } + + explicit InstructionFolder( + IRContext* context, std::unique_ptr&& folding_rules, + std::unique_ptr&& constant_folding_rules) + : context_(context), + const_folding_rules_(std::move(constant_folding_rules)), + folding_rules_(std::move(folding_rules)) { + folding_rules_->AddFoldingRules(); + const_folding_rules_->AddFoldingRules(); + } + + // Returns the result of folding a scalar instruction with the given |opcode| + // and |operands|. Each entry in |operands| is a pointer to an + // analysis::Constant instance, which should've been created with the constant + // manager (See IRContext::get_constant_mgr). + // + // It is an error to call this function with an opcode that does not pass the + // IsFoldableOpcode test. If any error occurs during folding, the folder will + // fail with a call to assert. + uint32_t FoldScalars( + SpvOp opcode, + const std::vector& operands) const; + + // Returns the result of performing an operation with the given |opcode| over + // constant vectors with |num_dims| dimensions. Each entry in |operands| is a + // pointer to an analysis::Constant instance, which should've been created + // with the constant manager (See IRContext::get_constant_mgr). + // + // This function iterates through the given vector type constant operands and + // calculates the result for each element of the result vector to return. + // Vectors with longer than 32-bit scalar components are not accepted in this + // function. + // + // It is an error to call this function with an opcode that does not pass the + // IsFoldableOpcode test. If any error occurs during folding, the folder will + // fail with a call to assert. + std::vector FoldVectors( + SpvOp opcode, uint32_t num_dims, + const std::vector& operands) const; + + // Returns true if |opcode| represents an operation handled by FoldScalars or + // FoldVectors. + bool IsFoldableOpcode(SpvOp opcode) const; + + // Returns true if |cst| is supported by FoldScalars and FoldVectors. + bool IsFoldableConstant(const analysis::Constant* cst) const; + + // Returns true if |FoldInstructionToConstant| could fold an instruction whose + // result type is |type_inst|. + bool IsFoldableType(Instruction* type_inst) const; + + // Tries to fold |inst| to a single constant, when the input ids to |inst| + // have been substituted using |id_map|. Returns a pointer to the OpConstant* + // instruction if successful. If necessary, a new constant instruction is + // created and placed in the global values section. + // + // |id_map| is a function that takes one result id and returns another. It + // can be used for things like CCP where it is known that some ids contain a + // constant, but the instruction itself has not been updated yet. This can + // map those ids to the appropriate constants. + Instruction* FoldInstructionToConstant( + Instruction* inst, std::function id_map) const; + // Returns true if |inst| can be folded into a simpler instruction. + // If |inst| can be simplified, |inst| is overwritten with the simplified + // instruction reusing the same result id. + // + // If |inst| is simplified, it is possible that the resulting code in invalid + // because the instruction is in a bad location. Callers of this function + // have to handle the following cases: + // + // 1) An OpPhi becomes and OpCopyObject - If there are OpPhi instruction after + // |inst| in a basic block then this is invalid. The caller must fix this + // up. + bool FoldInstruction(Instruction* inst) const; + + // Return true if this opcode has a const folding rule associtated with it. + bool HasConstFoldingRule(const Instruction* inst) const { + return GetConstantFoldingRules().HasFoldingRule(inst); + } + + private: + // Returns a reference to the ConstnatFoldingRules instance. + const ConstantFoldingRules& GetConstantFoldingRules() const { + return *const_folding_rules_; + } + + // Returns a reference to the FoldingRules instance. + const FoldingRules& GetFoldingRules() const { return *folding_rules_; } + + // Returns the single-word result from performing the given unary operation on + // the operand value which is passed in as a 32-bit word. + uint32_t UnaryOperate(SpvOp opcode, uint32_t operand) const; + + // Returns the single-word result from performing the given binary operation + // on the operand values which are passed in as two 32-bit word. + uint32_t BinaryOperate(SpvOp opcode, uint32_t a, uint32_t b) const; + + // Returns the single-word result from performing the given ternary operation + // on the operand values which are passed in as three 32-bit word. + uint32_t TernaryOperate(SpvOp opcode, uint32_t a, uint32_t b, + uint32_t c) const; + + // Returns the single-word result from performing the given operation on the + // operand words. This only works with 32-bit operations and uses boolean + // convention that 0u is false, and anything else is boolean true. + // TODO(qining): Support operands other than 32-bit wide. + uint32_t OperateWords(SpvOp opcode, + const std::vector& operand_words) const; + + bool FoldInstructionInternal(Instruction* inst) const; + + // Returns true if |inst| is a binary operation that takes two integers as + // parameters and folds to a constant that can be represented as an unsigned + // 32-bit value when the ids have been replaced by |id_map|. If |inst| can be + // folded, the resulting value is returned in |*result|. Valid result types + // for the instruction are any integer (signed or unsigned) with 32-bits or + // less, or a boolean value. + bool FoldBinaryIntegerOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const; + + // Returns true if |inst| is a binary operation on two boolean values, and + // folds + // to a constant boolean value when the ids have been replaced using |id_map|. + // If |inst| can be folded, the result value is returned in |*result|. + bool FoldBinaryBooleanOpToConstant( + Instruction* inst, const std::function& id_map, + uint32_t* result) const; + + // Returns true if |inst| can be folded to an constant when the ids have been + // substituted using id_map. If it can, the value is returned in |result|. If + // not, |result| is unchanged. It is assumed that not all operands are + // constant. Those cases are handled by |FoldScalar|. + bool FoldIntegerOpToConstant(Instruction* inst, + const std::function& id_map, + uint32_t* result) const; + + IRContext* context_; + + // Folding rules used by |FoldInstructionToConstant| and |FoldInstruction|. + std::unique_ptr const_folding_rules_; + + // Folding rules used by |FoldInstruction|. + std::unique_ptr folding_rules_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FOLD_H_ diff --git a/third_party/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp b/third_party/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp new file mode 100644 index 0000000..8ab717e --- /dev/null +++ b/third_party/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.cpp @@ -0,0 +1,445 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/fold_spec_constant_op_and_composite_pass.h" + +#include +#include +#include + +#include "source/opt/constants.h" +#include "source/opt/fold.h" +#include "source/opt/ir_context.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +Pass::Status FoldSpecConstantOpAndCompositePass::Process() { + bool modified = false; + // Traverse through all the constant defining instructions. For Normal + // Constants whose values are determined and do not depend on OpUndef + // instructions, records their values in two internal maps: id_to_const_val_ + // and const_val_to_id_ so that we can use them to infer the value of Spec + // Constants later. + // For Spec Constants defined with OpSpecConstantComposite instructions, if + // all of their components are Normal Constants, they will be turned into + // Normal Constants too. For Spec Constants defined with OpSpecConstantOp + // instructions, we check if they only depends on Normal Constants and fold + // them when possible. The two maps for Normal Constants: id_to_const_val_ + // and const_val_to_id_ will be updated along the traversal so that the new + // Normal Constants generated from folding can be used to fold following Spec + // Constants. + // This algorithm depends on the SSA property of SPIR-V when + // defining constants. The dependent constants must be defined before the + // dependee constants. So a dependent Spec Constant must be defined and + // will be processed before its dependee Spec Constant. When we encounter + // the dependee Spec Constants, all its dependent constants must have been + // processed and all its dependent Spec Constants should have been folded if + // possible. + Module::inst_iterator next_inst = context()->types_values_begin(); + for (Module::inst_iterator inst_iter = next_inst; + // Need to re-evaluate the end iterator since we may modify the list of + // instructions in this section of the module as the process goes. + inst_iter != context()->types_values_end(); inst_iter = next_inst) { + ++next_inst; + Instruction* inst = &*inst_iter; + // Collect constant values of normal constants and process the + // OpSpecConstantOp and OpSpecConstantComposite instructions if possible. + // The constant values will be stored in analysis::Constant instances. + // OpConstantSampler instruction is not collected here because it cannot be + // used in OpSpecConstant{Composite|Op} instructions. + // TODO(qining): If the constant or its type has decoration, we may need + // to skip it. + if (context()->get_constant_mgr()->GetType(inst) && + !context()->get_constant_mgr()->GetType(inst)->decoration_empty()) + continue; + switch (SpvOp opcode = inst->opcode()) { + // Records the values of Normal Constants. + case SpvOp::SpvOpConstantTrue: + case SpvOp::SpvOpConstantFalse: + case SpvOp::SpvOpConstant: + case SpvOp::SpvOpConstantNull: + case SpvOp::SpvOpConstantComposite: + case SpvOp::SpvOpSpecConstantComposite: { + // A Constant instance will be created if the given instruction is a + // Normal Constant whose value(s) are fixed. Note that for a composite + // Spec Constant defined with OpSpecConstantComposite instruction, if + // all of its components are Normal Constants already, the Spec + // Constant will be turned in to a Normal Constant. In that case, a + // Constant instance should also be created successfully and recorded + // in the id_to_const_val_ and const_val_to_id_ mapps. + if (auto const_value = + context()->get_constant_mgr()->GetConstantFromInst(inst)) { + // Need to replace the OpSpecConstantComposite instruction with a + // corresponding OpConstantComposite instruction. + if (opcode == SpvOp::SpvOpSpecConstantComposite) { + inst->SetOpcode(SpvOp::SpvOpConstantComposite); + modified = true; + } + context()->get_constant_mgr()->MapConstantToInst(const_value, inst); + } + break; + } + // For a Spec Constants defined with OpSpecConstantOp instruction, check + // if it only depends on Normal Constants. If so, the Spec Constant will + // be folded. The original Spec Constant defining instruction will be + // replaced by Normal Constant defining instructions, and the new Normal + // Constants will be added to id_to_const_val_ and const_val_to_id_ so + // that we can use the new Normal Constants when folding following Spec + // Constants. + case SpvOp::SpvOpSpecConstantOp: + modified |= ProcessOpSpecConstantOp(&inst_iter); + break; + default: + break; + } + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp( + Module::inst_iterator* pos) { + Instruction* inst = &**pos; + Instruction* folded_inst = nullptr; + assert(inst->GetInOperand(0).type == + SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER && + "The first in-operand of OpSpecContantOp instruction must be of " + "SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type"); + + switch (static_cast(inst->GetSingleWordInOperand(0))) { + case SpvOp::SpvOpCompositeExtract: + case SpvOp::SpvOpVectorShuffle: + case SpvOp::SpvOpCompositeInsert: + case SpvOp::SpvOpQuantizeToF16: + folded_inst = FoldWithInstructionFolder(pos); + break; + default: + // TODO: This should use the instruction folder as well, but some folding + // rules are missing. + + // Component-wise operations. + folded_inst = DoComponentWiseOperation(pos); + break; + } + if (!folded_inst) return false; + + // Replace the original constant with the new folded constant, kill the + // original constant. + uint32_t new_id = folded_inst->result_id(); + uint32_t old_id = inst->result_id(); + context()->ReplaceAllUsesWith(old_id, new_id); + context()->KillDef(old_id); + return true; +} + +uint32_t FoldSpecConstantOpAndCompositePass::GetTypeComponent( + uint32_t typeId, uint32_t element) const { + Instruction* type = context()->get_def_use_mgr()->GetDef(typeId); + uint32_t subtype = type->GetTypeComponent(element); + assert(subtype != 0); + + return subtype; +} + +Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder( + Module::inst_iterator* inst_iter_ptr) { + // If one of operands to the instruction is not a + // constant, then we cannot fold this spec constant. + for (uint32_t i = 1; i < (*inst_iter_ptr)->NumInOperands(); i++) { + const Operand& operand = (*inst_iter_ptr)->GetInOperand(i); + if (operand.type != SPV_OPERAND_TYPE_ID && + operand.type != SPV_OPERAND_TYPE_OPTIONAL_ID) { + continue; + } + uint32_t id = operand.words[0]; + if (context()->get_constant_mgr()->FindDeclaredConstant(id) == nullptr) { + return nullptr; + } + } + + // All of the operands are constant. Construct a regular version of the + // instruction and pass it to the instruction folder. + std::unique_ptr inst((*inst_iter_ptr)->Clone(context())); + inst->SetOpcode( + static_cast((*inst_iter_ptr)->GetSingleWordInOperand(0))); + inst->RemoveOperand(2); + + // We want the current instruction to be replaced by an |OpConstant*| + // instruction in the same position. We need to keep track of which constants + // the instruction folder creates, so we can move them into the correct place. + auto last_type_value_iter = (context()->types_values_end()); + --last_type_value_iter; + Instruction* last_type_value = &*last_type_value_iter; + + auto identity_map = [](uint32_t id) { return id; }; + Instruction* new_const_inst = + context()->get_instruction_folder().FoldInstructionToConstant( + inst.get(), identity_map); + assert(new_const_inst != nullptr && + "Failed to fold instruction that must be folded."); + + // Get the instruction before |pos| to insert after. |pos| cannot be the + // first instruction in the list because its type has to come first. + Instruction* insert_pos = (*inst_iter_ptr)->PreviousNode(); + assert(insert_pos != nullptr && + "pos is the first instruction in the types and values."); + bool need_to_clone = true; + for (Instruction* i = last_type_value->NextNode(); i != nullptr; + i = last_type_value->NextNode()) { + if (i == new_const_inst) { + need_to_clone = false; + } + i->InsertAfter(insert_pos); + insert_pos = insert_pos->NextNode(); + } + + if (need_to_clone) { + new_const_inst = new_const_inst->Clone(context()); + new_const_inst->SetResultId(TakeNextId()); + new_const_inst->InsertAfter(insert_pos); + get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst); + } + return new_const_inst; +} + +Instruction* FoldSpecConstantOpAndCompositePass::DoVectorShuffle( + Module::inst_iterator* pos) { + Instruction* inst = &**pos; + analysis::Vector* result_vec_type = + context()->get_constant_mgr()->GetType(inst)->AsVector(); + assert(inst->NumInOperands() - 1 > 2 && + "OpSpecConstantOp DoVectorShuffle instruction requires more than 2 " + "operands (2 vector ids and at least one literal operand"); + assert(result_vec_type && + "The result of VectorShuffle must be of type vector"); + + // A temporary null constants that can be used as the components of the result + // vector. This is needed when any one of the vector operands are null + // constant. + const analysis::Constant* null_component_constants = nullptr; + + // Get a concatenated vector of scalar constants. The vector should be built + // with the components from the first and the second operand of VectorShuffle. + std::vector concatenated_components; + // Note that for OpSpecConstantOp, the second in-operand is the first id + // operand. The first in-operand is the spec opcode. + for (uint32_t i : {1, 2}) { + assert(inst->GetInOperand(i).type == SPV_OPERAND_TYPE_ID && + "The vector operand must have a SPV_OPERAND_TYPE_ID type"); + uint32_t operand_id = inst->GetSingleWordInOperand(i); + auto operand_const = + context()->get_constant_mgr()->FindDeclaredConstant(operand_id); + if (!operand_const) return nullptr; + const analysis::Type* operand_type = operand_const->type(); + assert(operand_type->AsVector() && + "The first two operand of VectorShuffle must be of vector type"); + if (auto vec_const = operand_const->AsVectorConstant()) { + // case 1: current operand is a non-null vector constant. + concatenated_components.insert(concatenated_components.end(), + vec_const->GetComponents().begin(), + vec_const->GetComponents().end()); + } else if (operand_const->AsNullConstant()) { + // case 2: current operand is a null vector constant. Create a temporary + // null scalar constant as the component. + if (!null_component_constants) { + const analysis::Type* component_type = + operand_type->AsVector()->element_type(); + null_component_constants = + context()->get_constant_mgr()->GetConstant(component_type, {}); + } + // Append the null scalar consts to the concatenated components + // vector. + concatenated_components.insert(concatenated_components.end(), + operand_type->AsVector()->element_count(), + null_component_constants); + } else { + // no other valid cases + return nullptr; + } + } + // Create null component constants if there are any. The component constants + // must be added to the module before the dependee composite constants to + // satisfy SSA def-use dominance. + if (null_component_constants) { + context()->get_constant_mgr()->BuildInstructionAndAddToModule( + null_component_constants, pos); + } + // Create the new vector constant with the selected components. + std::vector selected_components; + for (uint32_t i = 3; i < inst->NumInOperands(); i++) { + assert(inst->GetInOperand(i).type == SPV_OPERAND_TYPE_LITERAL_INTEGER && + "The literal operand must of type SPV_OPERAND_TYPE_LITERAL_INTEGER"); + uint32_t literal = inst->GetSingleWordInOperand(i); + assert(literal < concatenated_components.size() && + "Literal index out of bound of the concatenated vector"); + selected_components.push_back(concatenated_components[literal]); + } + auto new_vec_const = MakeUnique( + result_vec_type, selected_components); + auto reg_vec_const = + context()->get_constant_mgr()->RegisterConstant(std::move(new_vec_const)); + return context()->get_constant_mgr()->BuildInstructionAndAddToModule( + reg_vec_const, pos); +} + +namespace { +// A helper function to check the type for component wise operations. Returns +// true if the type: +// 1) is bool type; +// 2) is 32-bit int type; +// 3) is vector of bool type; +// 4) is vector of 32-bit integer type. +// Otherwise returns false. +bool IsValidTypeForComponentWiseOperation(const analysis::Type* type) { + if (type->AsBool()) { + return true; + } else if (auto* it = type->AsInteger()) { + if (it->width() == 32) return true; + } else if (auto* vt = type->AsVector()) { + if (vt->element_type()->AsBool()) { + return true; + } else if (auto* vit = vt->element_type()->AsInteger()) { + if (vit->width() == 32) return true; + } + } + return false; +} + +// Encodes the integer |value| of in a word vector format appropriate for +// representing this value as a operands for a constant definition. Performs +// zero-extension/sign-extension/truncation when needed, based on the signess of +// the given target type. +// +// Note: type |type| argument must be either Integer or Bool. +utils::SmallVector EncodeIntegerAsWords(const analysis::Type& type, + uint32_t value) { + const uint32_t all_ones = ~0; + uint32_t bit_width = 0; + uint32_t pad_value = 0; + bool result_type_signed = false; + if (auto* int_ty = type.AsInteger()) { + bit_width = int_ty->width(); + result_type_signed = int_ty->IsSigned(); + if (result_type_signed && static_cast(value) < 0) { + pad_value = all_ones; + } + } else if (type.AsBool()) { + bit_width = 1; + } else { + assert(false && "type must be Integer or Bool"); + } + + assert(bit_width > 0); + uint32_t first_word = value; + const uint32_t bits_per_word = 32; + + // Truncate first_word if the |type| has width less than uint32. + if (bit_width < bits_per_word) { + const uint32_t num_high_bits_to_mask = bits_per_word - bit_width; + const bool is_negative_after_truncation = + result_type_signed && + utils::IsBitAtPositionSet(first_word, bit_width - 1); + + if (is_negative_after_truncation) { + // Truncate and sign-extend |first_word|. No padding words will be + // added and |pad_value| can be left as-is. + first_word = utils::SetHighBits(first_word, num_high_bits_to_mask); + } else { + first_word = utils::ClearHighBits(first_word, num_high_bits_to_mask); + } + } + + utils::SmallVector words = {first_word}; + for (uint32_t current_bit = bits_per_word; current_bit < bit_width; + current_bit += bits_per_word) { + words.push_back(pad_value); + } + + return words; +} +} // namespace + +Instruction* FoldSpecConstantOpAndCompositePass::DoComponentWiseOperation( + Module::inst_iterator* pos) { + const Instruction* inst = &**pos; + const analysis::Type* result_type = + context()->get_constant_mgr()->GetType(inst); + SpvOp spec_opcode = static_cast(inst->GetSingleWordInOperand(0)); + // Check and collect operands. + std::vector operands; + + if (!std::all_of( + inst->cbegin(), inst->cend(), [&operands, this](const Operand& o) { + // skip the operands that is not an id. + if (o.type != spv_operand_type_t::SPV_OPERAND_TYPE_ID) return true; + uint32_t id = o.words.front(); + if (auto c = + context()->get_constant_mgr()->FindDeclaredConstant(id)) { + if (IsValidTypeForComponentWiseOperation(c->type())) { + operands.push_back(c); + return true; + } + } + return false; + })) + return nullptr; + + if (result_type->AsInteger() || result_type->AsBool()) { + // Scalar operation + const uint32_t result_val = + context()->get_instruction_folder().FoldScalars(spec_opcode, operands); + auto result_const = context()->get_constant_mgr()->GetConstant( + result_type, EncodeIntegerAsWords(*result_type, result_val)); + return context()->get_constant_mgr()->BuildInstructionAndAddToModule( + result_const, pos); + } else if (result_type->AsVector()) { + // Vector operation + const analysis::Type* element_type = + result_type->AsVector()->element_type(); + uint32_t num_dims = result_type->AsVector()->element_count(); + std::vector result_vec = + context()->get_instruction_folder().FoldVectors(spec_opcode, num_dims, + operands); + std::vector result_vector_components; + for (const uint32_t r : result_vec) { + if (auto rc = context()->get_constant_mgr()->GetConstant( + element_type, EncodeIntegerAsWords(*element_type, r))) { + result_vector_components.push_back(rc); + if (!context()->get_constant_mgr()->BuildInstructionAndAddToModule( + rc, pos)) { + assert(false && + "Failed to build and insert constant declaring instruction " + "for the given vector component constant"); + } + } else { + assert(false && "Failed to create constants with 32-bit word"); + } + } + auto new_vec_const = MakeUnique( + result_type->AsVector(), result_vector_components); + auto reg_vec_const = context()->get_constant_mgr()->RegisterConstant( + std::move(new_vec_const)); + return context()->get_constant_mgr()->BuildInstructionAndAddToModule( + reg_vec_const, pos); + } else { + // Cannot process invalid component wise operation. The result of component + // wise operation must be of integer or bool scalar or vector of + // integer/bool type. + return nullptr; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h b/third_party/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h new file mode 100644 index 0000000..361d3ca --- /dev/null +++ b/third_party/spirv-tools/source/opt/fold_spec_constant_op_and_composite_pass.h @@ -0,0 +1,82 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FOLD_SPEC_CONSTANT_OP_AND_COMPOSITE_PASS_H_ +#define SOURCE_OPT_FOLD_SPEC_CONSTANT_OP_AND_COMPOSITE_PASS_H_ + +#include +#include +#include + +#include "source/opt/constants.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class FoldSpecConstantOpAndCompositePass : public Pass { + public: + FoldSpecConstantOpAndCompositePass() = default; + + const char* name() const override { return "fold-spec-const-op-composite"; } + + // Iterates through the types-constants-globals section of the given module, + // finds the Spec Constants defined with OpSpecConstantOp and + // OpSpecConstantComposite instructions. If the result value of those spec + // constants can be folded, fold them to their corresponding normal constants. + Status Process() override; + + private: + // Processes the OpSpecConstantOp instruction pointed by the given + // instruction iterator, folds it to normal constants if possible. Returns + // true if the spec constant is folded to normal constants. New instructions + // will be inserted before the OpSpecConstantOp instruction pointed by the + // instruction iterator. The instruction iterator, which is passed by + // pointer, will still point to the original OpSpecConstantOp instruction. If + // folding is done successfully, the original OpSpecConstantOp instruction + // will be changed to Nop and new folded instruction will be inserted before + // it. + bool ProcessOpSpecConstantOp(Module::inst_iterator* pos); + + // Returns the result of folding the OpSpecConstantOp instruction + // |inst_iter_ptr| using the instruction folder. + Instruction* FoldWithInstructionFolder(Module::inst_iterator* inst_iter_ptr); + + // Try to fold the OpSpecConstantOp VectorShuffle instruction pointed by the + // given instruction iterator to a normal constant defining instruction. + // Returns the pointer to the new constant defining instruction if succeeded. + // Otherwise return nullptr. + Instruction* DoVectorShuffle(Module::inst_iterator* inst_iter_ptr); + + // Try to fold the OpSpecConstantOp instruction + // pointed by the given instruction iterator to a normal constant defining + // instruction. Returns the pointer to the new constant defining instruction + // if succeeded, otherwise return nullptr. + Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr); + + // Returns the |element|'th subtype of |type|. + // + // |type| must be a composite type. + uint32_t GetTypeComponent(uint32_t type, uint32_t element) const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FOLD_SPEC_CONSTANT_OP_AND_COMPOSITE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/folding_rules.cpp b/third_party/spirv-tools/source/opt/folding_rules.cpp new file mode 100644 index 0000000..010eec9 --- /dev/null +++ b/third_party/spirv-tools/source/opt/folding_rules.cpp @@ -0,0 +1,2537 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/folding_rules.h" + +#include +#include +#include + +#include "ir_builder.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kExtractCompositeIdInIdx = 0; +const uint32_t kInsertObjectIdInIdx = 0; +const uint32_t kInsertCompositeIdInIdx = 1; +const uint32_t kExtInstSetIdInIdx = 0; +const uint32_t kExtInstInstructionInIdx = 1; +const uint32_t kFMixXIdInIdx = 2; +const uint32_t kFMixYIdInIdx = 3; +const uint32_t kFMixAIdInIdx = 4; +const uint32_t kStoreObjectInIdx = 1; + +// Some image instructions may contain an "image operands" argument. +// Returns the operand index for the "image operands". +// Returns -1 if the instruction does not have image operands. +int32_t ImageOperandsMaskInOperandIndex(Instruction* inst) { + const auto opcode = inst->opcode(); + switch (opcode) { + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageFetch: + case SpvOpImageRead: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseFetch: + case SpvOpImageSparseRead: + return inst->NumOperands() > 4 ? 2 : -1; + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + return inst->NumOperands() > 5 ? 3 : -1; + case SpvOpImageWrite: + return inst->NumOperands() > 3 ? 3 : -1; + default: + return -1; + } +} + +// Returns the element width of |type|. +uint32_t ElementWidth(const analysis::Type* type) { + if (const analysis::Vector* vec_type = type->AsVector()) { + return ElementWidth(vec_type->element_type()); + } else if (const analysis::Float* float_type = type->AsFloat()) { + return float_type->width(); + } else { + assert(type->AsInteger()); + return type->AsInteger()->width(); + } +} + +// Returns true if |type| is Float or a vector of Float. +bool HasFloatingPoint(const analysis::Type* type) { + if (type->AsFloat()) { + return true; + } else if (const analysis::Vector* vec_type = type->AsVector()) { + return vec_type->element_type()->AsFloat() != nullptr; + } + + return false; +} + +// Returns false if |val| is NaN, infinite or subnormal. +template +bool IsValidResult(T val) { + int classified = std::fpclassify(val); + switch (classified) { + case FP_NAN: + case FP_INFINITE: + case FP_SUBNORMAL: + return false; + default: + return true; + } +} + +const analysis::Constant* ConstInput( + const std::vector& constants) { + return constants[0] ? constants[0] : constants[1]; +} + +Instruction* NonConstInput(IRContext* context, const analysis::Constant* c, + Instruction* inst) { + uint32_t in_op = c ? 1u : 0u; + return context->get_def_use_mgr()->GetDef( + inst->GetSingleWordInOperand(in_op)); +} + +// Returns the negation of |c|. |c| must be a 32 or 64 bit floating point +// constant. +uint32_t NegateFloatingPointConstant(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + assert(c); + assert(c->type()->AsFloat()); + uint32_t width = c->type()->AsFloat()->width(); + assert(width == 32 || width == 64); + std::vector words; + if (width == 64) { + utils::FloatProxy result(c->GetDouble() * -1.0); + words = result.GetWords(); + } else { + utils::FloatProxy result(c->GetFloat() * -1.0f); + words = result.GetWords(); + } + + const analysis::Constant* negated_const = + const_mgr->GetConstant(c->type(), std::move(words)); + return const_mgr->GetDefiningInstruction(negated_const)->result_id(); +} + +std::vector ExtractInts(uint64_t val) { + std::vector words; + words.push_back(static_cast(val)); + words.push_back(static_cast(val >> 32)); + return words; +} + +// Negates the integer constant |c|. Returns the id of the defining instruction. +uint32_t NegateIntegerConstant(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + assert(c); + assert(c->type()->AsInteger()); + uint32_t width = c->type()->AsInteger()->width(); + assert(width == 32 || width == 64); + std::vector words; + if (width == 64) { + uint64_t uval = static_cast(0 - c->GetU64()); + words = ExtractInts(uval); + } else { + words.push_back(static_cast(0 - c->GetU32())); + } + + const analysis::Constant* negated_const = + const_mgr->GetConstant(c->type(), std::move(words)); + return const_mgr->GetDefiningInstruction(negated_const)->result_id(); +} + +// Negates the vector constant |c|. Returns the id of the defining instruction. +uint32_t NegateVectorConstant(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + assert(const_mgr && c); + assert(c->type()->AsVector()); + if (c->AsNullConstant()) { + // 0.0 vs -0.0 shouldn't matter. + return const_mgr->GetDefiningInstruction(c)->result_id(); + } else { + const analysis::Type* component_type = + c->AsVectorConstant()->component_type(); + std::vector words; + for (auto& comp : c->AsVectorConstant()->GetComponents()) { + if (component_type->AsFloat()) { + words.push_back(NegateFloatingPointConstant(const_mgr, comp)); + } else { + assert(component_type->AsInteger()); + words.push_back(NegateIntegerConstant(const_mgr, comp)); + } + } + + const analysis::Constant* negated_const = + const_mgr->GetConstant(c->type(), std::move(words)); + return const_mgr->GetDefiningInstruction(negated_const)->result_id(); + } +} + +// Negates |c|. Returns the id of the defining instruction. +uint32_t NegateConstant(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + if (c->type()->AsVector()) { + return NegateVectorConstant(const_mgr, c); + } else if (c->type()->AsFloat()) { + return NegateFloatingPointConstant(const_mgr, c); + } else { + assert(c->type()->AsInteger()); + return NegateIntegerConstant(const_mgr, c); + } +} + +// Takes the reciprocal of |c|. |c|'s type must be Float or a vector of Float. +// Returns 0 if the reciprocal is NaN, infinite or subnormal. +uint32_t Reciprocal(analysis::ConstantManager* const_mgr, + const analysis::Constant* c) { + assert(const_mgr && c); + assert(c->type()->AsFloat()); + + uint32_t width = c->type()->AsFloat()->width(); + assert(width == 32 || width == 64); + std::vector words; + if (width == 64) { + spvtools::utils::FloatProxy result(1.0 / c->GetDouble()); + if (!IsValidResult(result.getAsFloat())) return 0; + words = result.GetWords(); + } else { + spvtools::utils::FloatProxy result(1.0f / c->GetFloat()); + if (!IsValidResult(result.getAsFloat())) return 0; + words = result.GetWords(); + } + + const analysis::Constant* negated_const = + const_mgr->GetConstant(c->type(), std::move(words)); + return const_mgr->GetDefiningInstruction(negated_const)->result_id(); +} + +// Replaces fdiv where second operand is constant with fmul. +FoldingRule ReciprocalFDiv() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFDiv); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + if (constants[1] != nullptr) { + uint32_t id = 0; + if (const analysis::VectorConstant* vector_const = + constants[1]->AsVectorConstant()) { + std::vector neg_ids; + for (auto& comp : vector_const->GetComponents()) { + id = Reciprocal(const_mgr, comp); + if (id == 0) return false; + neg_ids.push_back(id); + } + const analysis::Constant* negated_const = + const_mgr->GetConstant(constants[1]->type(), std::move(neg_ids)); + id = const_mgr->GetDefiningInstruction(negated_const)->result_id(); + } else if (constants[1]->AsFloatConstant()) { + id = Reciprocal(const_mgr, constants[1]); + if (id == 0) return false; + } else { + // Don't fold a null constant. + return false; + } + inst->SetOpcode(SpvOpFMul); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0u)}}, + {SPV_OPERAND_TYPE_ID, {id}}}); + return true; + } + + return false; + }; +} + +// Elides consecutive negate instructions. +FoldingRule MergeNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate); + (void)constants; + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + Instruction* op_inst = + context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); + if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (op_inst->opcode() == inst->opcode()) { + // Elide negates. + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op_inst->GetSingleWordInOperand(0u)}}}); + return true; + } + + return false; + }; +} + +// Merges negate into a mul or div operation if that operation contains a +// constant operand. +// Cases: +// -(x * 2) = x * -2 +// -(2 * x) = x * -2 +// -(x / 2) = x / -2 +// -(2 / x) = -2 / x +FoldingRule MergeNegateMulDivArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate); + (void)constants; + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + Instruction* op_inst = + context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); + if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed()) + return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + SpvOp opcode = op_inst->opcode(); + if (opcode == SpvOpFMul || opcode == SpvOpFDiv || opcode == SpvOpIMul || + opcode == SpvOpSDiv || opcode == SpvOpUDiv) { + std::vector op_constants = + const_mgr->GetOperandConstants(op_inst); + // Merge negate into mul or div if one operand is constant. + if (op_constants[0] || op_constants[1]) { + bool zero_is_variable = op_constants[0] == nullptr; + const analysis::Constant* c = ConstInput(op_constants); + uint32_t neg_id = NegateConstant(const_mgr, c); + uint32_t non_const_id = zero_is_variable + ? op_inst->GetSingleWordInOperand(0u) + : op_inst->GetSingleWordInOperand(1u); + // Change this instruction to a mul/div. + inst->SetOpcode(op_inst->opcode()); + if (opcode == SpvOpFDiv || opcode == SpvOpUDiv || opcode == SpvOpSDiv) { + uint32_t op0 = zero_is_variable ? non_const_id : neg_id; + uint32_t op1 = zero_is_variable ? neg_id : non_const_id; + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op0}}, {SPV_OPERAND_TYPE_ID, {op1}}}); + } else { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {non_const_id}}, + {SPV_OPERAND_TYPE_ID, {neg_id}}}); + } + return true; + } + } + + return false; + }; +} + +// Merges negate into a add or sub operation if that operation contains a +// constant operand. +// Cases: +// -(x + 2) = -2 - x +// -(2 + x) = -2 - x +// -(x - 2) = 2 - x +// -(2 - x) = x - 2 +FoldingRule MergeNegateAddSubArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFNegate || inst->opcode() == SpvOpSNegate); + (void)constants; + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + Instruction* op_inst = + context->get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); + if (HasFloatingPoint(type) && !op_inst->IsFloatingPointFoldingAllowed()) + return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + if (op_inst->opcode() == SpvOpFAdd || op_inst->opcode() == SpvOpFSub || + op_inst->opcode() == SpvOpIAdd || op_inst->opcode() == SpvOpISub) { + std::vector op_constants = + const_mgr->GetOperandConstants(op_inst); + if (op_constants[0] || op_constants[1]) { + bool zero_is_variable = op_constants[0] == nullptr; + bool is_add = (op_inst->opcode() == SpvOpFAdd) || + (op_inst->opcode() == SpvOpIAdd); + bool swap_operands = !is_add || zero_is_variable; + bool negate_const = is_add; + const analysis::Constant* c = ConstInput(op_constants); + uint32_t const_id = 0; + if (negate_const) { + const_id = NegateConstant(const_mgr, c); + } else { + const_id = zero_is_variable ? op_inst->GetSingleWordInOperand(1u) + : op_inst->GetSingleWordInOperand(0u); + } + + // Swap operands if necessary and make the instruction a subtraction. + uint32_t op0 = + zero_is_variable ? op_inst->GetSingleWordInOperand(0u) : const_id; + uint32_t op1 = + zero_is_variable ? const_id : op_inst->GetSingleWordInOperand(1u); + if (swap_operands) std::swap(op0, op1); + inst->SetOpcode(HasFloatingPoint(type) ? SpvOpFSub : SpvOpISub); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op0}}, {SPV_OPERAND_TYPE_ID, {op1}}}); + return true; + } + } + + return false; + }; +} + +// Returns true if |c| has a zero element. +bool HasZero(const analysis::Constant* c) { + if (c->AsNullConstant()) { + return true; + } + if (const analysis::VectorConstant* vec_const = c->AsVectorConstant()) { + for (auto& comp : vec_const->GetComponents()) + if (HasZero(comp)) return true; + } else { + assert(c->AsScalarConstant()); + return c->AsScalarConstant()->IsZero(); + } + + return false; +} + +// Performs |input1| |opcode| |input2| and returns the merged constant result +// id. Returns 0 if the result is not a valid value. The input types must be +// Float. +uint32_t PerformFloatingPointOperation(analysis::ConstantManager* const_mgr, + SpvOp opcode, + const analysis::Constant* input1, + const analysis::Constant* input2) { + const analysis::Type* type = input1->type(); + assert(type->AsFloat()); + uint32_t width = type->AsFloat()->width(); + assert(width == 32 || width == 64); + std::vector words; +#define FOLD_OP(op) \ + if (width == 64) { \ + utils::FloatProxy val = \ + input1->GetDouble() op input2->GetDouble(); \ + double dval = val.getAsFloat(); \ + if (!IsValidResult(dval)) return 0; \ + words = val.GetWords(); \ + } else { \ + utils::FloatProxy val = input1->GetFloat() op input2->GetFloat(); \ + float fval = val.getAsFloat(); \ + if (!IsValidResult(fval)) return 0; \ + words = val.GetWords(); \ + } + switch (opcode) { + case SpvOpFMul: + FOLD_OP(*); + break; + case SpvOpFDiv: + if (HasZero(input2)) return 0; + FOLD_OP(/); + break; + case SpvOpFAdd: + FOLD_OP(+); + break; + case SpvOpFSub: + FOLD_OP(-); + break; + default: + assert(false && "Unexpected operation"); + break; + } +#undef FOLD_OP + const analysis::Constant* merged_const = const_mgr->GetConstant(type, words); + return const_mgr->GetDefiningInstruction(merged_const)->result_id(); +} + +// Performs |input1| |opcode| |input2| and returns the merged constant result +// id. Returns 0 if the result is not a valid value. The input types must be +// Integers. +uint32_t PerformIntegerOperation(analysis::ConstantManager* const_mgr, + SpvOp opcode, const analysis::Constant* input1, + const analysis::Constant* input2) { + assert(input1->type()->AsInteger()); + const analysis::Integer* type = input1->type()->AsInteger(); + uint32_t width = type->AsInteger()->width(); + assert(width == 32 || width == 64); + std::vector words; +#define FOLD_OP(op) \ + if (width == 64) { \ + if (type->IsSigned()) { \ + int64_t val = input1->GetS64() op input2->GetS64(); \ + words = ExtractInts(static_cast(val)); \ + } else { \ + uint64_t val = input1->GetU64() op input2->GetU64(); \ + words = ExtractInts(val); \ + } \ + } else { \ + if (type->IsSigned()) { \ + int32_t val = input1->GetS32() op input2->GetS32(); \ + words.push_back(static_cast(val)); \ + } else { \ + uint32_t val = input1->GetU32() op input2->GetU32(); \ + words.push_back(val); \ + } \ + } + switch (opcode) { + case SpvOpIMul: + FOLD_OP(*); + break; + case SpvOpSDiv: + case SpvOpUDiv: + assert(false && "Should not merge integer division"); + break; + case SpvOpIAdd: + FOLD_OP(+); + break; + case SpvOpISub: + FOLD_OP(-); + break; + default: + assert(false && "Unexpected operation"); + break; + } +#undef FOLD_OP + const analysis::Constant* merged_const = const_mgr->GetConstant(type, words); + return const_mgr->GetDefiningInstruction(merged_const)->result_id(); +} + +// Performs |input1| |opcode| |input2| and returns the merged constant result +// id. Returns 0 if the result is not a valid value. The input types must be +// Integers, Floats or Vectors of such. +uint32_t PerformOperation(analysis::ConstantManager* const_mgr, SpvOp opcode, + const analysis::Constant* input1, + const analysis::Constant* input2) { + assert(input1 && input2); + const analysis::Type* type = input1->type(); + std::vector words; + if (const analysis::Vector* vector_type = type->AsVector()) { + const analysis::Type* ele_type = vector_type->element_type(); + for (uint32_t i = 0; i != vector_type->element_count(); ++i) { + uint32_t id = 0; + + const analysis::Constant* input1_comp = nullptr; + if (const analysis::VectorConstant* input1_vector = + input1->AsVectorConstant()) { + input1_comp = input1_vector->GetComponents()[i]; + } else { + assert(input1->AsNullConstant()); + input1_comp = const_mgr->GetConstant(ele_type, {}); + } + + const analysis::Constant* input2_comp = nullptr; + if (const analysis::VectorConstant* input2_vector = + input2->AsVectorConstant()) { + input2_comp = input2_vector->GetComponents()[i]; + } else { + assert(input2->AsNullConstant()); + input2_comp = const_mgr->GetConstant(ele_type, {}); + } + + if (ele_type->AsFloat()) { + id = PerformFloatingPointOperation(const_mgr, opcode, input1_comp, + input2_comp); + } else { + assert(ele_type->AsInteger()); + id = PerformIntegerOperation(const_mgr, opcode, input1_comp, + input2_comp); + } + if (id == 0) return 0; + words.push_back(id); + } + const analysis::Constant* merged_const = + const_mgr->GetConstant(type, words); + return const_mgr->GetDefiningInstruction(merged_const)->result_id(); + } else if (type->AsFloat()) { + return PerformFloatingPointOperation(const_mgr, opcode, input1, input2); + } else { + assert(type->AsInteger()); + return PerformIntegerOperation(const_mgr, opcode, input1, input2); + } +} + +// Merges consecutive multiplies where each contains one constant operand. +// Cases: +// 2 * (x * 2) = x * 4 +// 2 * (2 * x) = x * 4 +// (x * 2) * 2 = x * 4 +// (2 * x) * 2 = x * 4 +FoldingRule MergeMulMulArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFMul || inst->opcode() == SpvOpIMul); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (HasFloatingPoint(type) && !inst->IsFloatingPointFoldingAllowed()) + return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + // Determine the constant input and the variable input in |inst|. + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (HasFloatingPoint(type) && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == inst->opcode()) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + bool other_first_is_variable = other_constants[0] == nullptr; + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + if (merged_id == 0) return false; + + uint32_t non_const_id = other_first_is_variable + ? other_inst->GetSingleWordInOperand(0u) + : other_inst->GetSingleWordInOperand(1u); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {non_const_id}}, + {SPV_OPERAND_TYPE_ID, {merged_id}}}); + return true; + } + + return false; + }; +} + +// Merges divides into subsequent multiplies if each instruction contains one +// constant operand. Does not support integer operations. +// Cases: +// 2 * (x / 2) = x * 1 +// 2 * (2 / x) = 4 / x +// (x / 2) * 2 = x * 1 +// (2 / x) * 2 = 4 / x +// (y / x) * x = y +// x * (y / x) = y +FoldingRule MergeMulDivArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFMul); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + for (uint32_t i = 0; i < 2; i++) { + uint32_t op_id = inst->GetSingleWordInOperand(i); + Instruction* op_inst = def_use_mgr->GetDef(op_id); + if (op_inst->opcode() == SpvOpFDiv) { + if (op_inst->GetSingleWordInOperand(1) == + inst->GetSingleWordInOperand(1 - i)) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op_inst->GetSingleWordInOperand(0)}}}); + return true; + } + } + } + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; + + if (other_inst->opcode() == SpvOpFDiv) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2 || HasZero(const_input2)) return false; + + bool other_first_is_variable = other_constants[0] == nullptr; + // If the variable value is the second operand of the divide, multiply + // the constants together. Otherwise divide the constants. + uint32_t merged_id = PerformOperation( + const_mgr, + other_first_is_variable ? other_inst->opcode() : inst->opcode(), + const_input1, const_input2); + if (merged_id == 0) return false; + + uint32_t non_const_id = other_first_is_variable + ? other_inst->GetSingleWordInOperand(0u) + : other_inst->GetSingleWordInOperand(1u); + + // If the variable value is on the second operand of the div, then this + // operation is a div. Otherwise it should be a multiply. + inst->SetOpcode(other_first_is_variable ? inst->opcode() + : other_inst->opcode()); + if (other_first_is_variable) { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {non_const_id}}, + {SPV_OPERAND_TYPE_ID, {merged_id}}}); + } else { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {merged_id}}, + {SPV_OPERAND_TYPE_ID, {non_const_id}}}); + } + return true; + } + + return false; + }; +} + +// Merges multiply of constant and negation. +// Cases: +// (-x) * 2 = x * -2 +// 2 * (-x) = x * -2 +FoldingRule MergeMulNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFMul || inst->opcode() == SpvOpIMul); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == SpvOpFNegate || + other_inst->opcode() == SpvOpSNegate) { + uint32_t neg_id = NegateConstant(const_mgr, const_input1); + + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}, + {SPV_OPERAND_TYPE_ID, {neg_id}}}); + return true; + } + + return false; + }; +} + +// Merges consecutive divides if each instruction contains one constant operand. +// Does not support integer division. +// Cases: +// 2 / (x / 2) = 4 / x +// 4 / (2 / x) = 2 * x +// (4 / x) / 2 = 2 / x +// (x / 2) / 2 = x / 4 +FoldingRule MergeDivDivArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFDiv); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1 || HasZero(const_input1)) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; + + bool first_is_variable = constants[0] == nullptr; + if (other_inst->opcode() == inst->opcode()) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2 || HasZero(const_input2)) return false; + + bool other_first_is_variable = other_constants[0] == nullptr; + + SpvOp merge_op = inst->opcode(); + if (other_first_is_variable) { + // Constants magnify. + merge_op = SpvOpFMul; + } + + // This is an x / (*) case. Swap the inputs. Doesn't harm multiply + // because it is commutative. + if (first_is_variable) std::swap(const_input1, const_input2); + uint32_t merged_id = + PerformOperation(const_mgr, merge_op, const_input1, const_input2); + if (merged_id == 0) return false; + + uint32_t non_const_id = other_first_is_variable + ? other_inst->GetSingleWordInOperand(0u) + : other_inst->GetSingleWordInOperand(1u); + + SpvOp op = inst->opcode(); + if (!first_is_variable && !other_first_is_variable) { + // Effectively div of 1/x, so change to multiply. + op = SpvOpFMul; + } + + uint32_t op1 = merged_id; + uint32_t op2 = non_const_id; + if (first_is_variable && other_first_is_variable) std::swap(op1, op2); + inst->SetOpcode(op); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + + return false; + }; +} + +// Fold multiplies succeeded by divides where each instruction contains a +// constant operand. Does not support integer divide. +// Cases: +// 4 / (x * 2) = 2 / x +// 4 / (2 * x) = 2 / x +// (x * 4) / 2 = x * 2 +// (4 * x) / 2 = x * 2 +// (x * y) / x = y +// (y * x) / x = y +FoldingRule MergeDivMulArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFDiv); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + if (!inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + uint32_t op_id = inst->GetSingleWordInOperand(0); + Instruction* op_inst = def_use_mgr->GetDef(op_id); + + if (op_inst->opcode() == SpvOpFMul) { + for (uint32_t i = 0; i < 2; i++) { + if (op_inst->GetSingleWordInOperand(i) == + inst->GetSingleWordInOperand(1)) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, + {op_inst->GetSingleWordInOperand(1 - i)}}}); + return true; + } + } + } + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1 || HasZero(const_input1)) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (!other_inst->IsFloatingPointFoldingAllowed()) return false; + + bool first_is_variable = constants[0] == nullptr; + if (other_inst->opcode() == SpvOpFMul) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + bool other_first_is_variable = other_constants[0] == nullptr; + + // This is an x / (*) case. Swap the inputs. + if (first_is_variable) std::swap(const_input1, const_input2); + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + if (merged_id == 0) return false; + + uint32_t non_const_id = other_first_is_variable + ? other_inst->GetSingleWordInOperand(0u) + : other_inst->GetSingleWordInOperand(1u); + + uint32_t op1 = merged_id; + uint32_t op2 = non_const_id; + if (first_is_variable) std::swap(op1, op2); + + // Convert to multiply + if (first_is_variable) inst->SetOpcode(other_inst->opcode()); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + + return false; + }; +} + +// Fold divides of a constant and a negation. +// Cases: +// (-x) / 2 = x / -2 +// 2 / (-x) = 2 / -x +FoldingRule MergeDivNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFDiv || inst->opcode() == SpvOpSDiv || + inst->opcode() == SpvOpUDiv); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + bool first_is_variable = constants[0] == nullptr; + if (other_inst->opcode() == SpvOpFNegate || + other_inst->opcode() == SpvOpSNegate) { + uint32_t neg_id = NegateConstant(const_mgr, const_input1); + + if (first_is_variable) { + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}, + {SPV_OPERAND_TYPE_ID, {neg_id}}}); + } else { + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {neg_id}}, + {SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}}); + } + return true; + } + + return false; + }; +} + +// Folds addition of a constant and a negation. +// Cases: +// (-x) + 2 = 2 - x +// 2 + (-x) = 2 - x +FoldingRule MergeAddNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == SpvOpSNegate || + other_inst->opcode() == SpvOpFNegate) { + inst->SetOpcode(HasFloatingPoint(type) ? SpvOpFSub : SpvOpISub); + uint32_t const_id = constants[0] ? inst->GetSingleWordInOperand(0u) + : inst->GetSingleWordInOperand(1u); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {const_id}}, + {SPV_OPERAND_TYPE_ID, {other_inst->GetSingleWordInOperand(0u)}}}); + return true; + } + return false; + }; +} + +// Folds subtraction of a constant and a negation. +// Cases: +// (-x) - 2 = -2 - x +// 2 - (-x) = x + 2 +FoldingRule MergeSubNegateArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == SpvOpSNegate || + other_inst->opcode() == SpvOpFNegate) { + uint32_t op1 = 0; + uint32_t op2 = 0; + SpvOp opcode = inst->opcode(); + if (constants[0] != nullptr) { + op1 = other_inst->GetSingleWordInOperand(0u); + op2 = inst->GetSingleWordInOperand(0u); + opcode = HasFloatingPoint(type) ? SpvOpFAdd : SpvOpIAdd; + } else { + op1 = NegateConstant(const_mgr, const_input1); + op2 = other_inst->GetSingleWordInOperand(0u); + } + + inst->SetOpcode(opcode); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + return false; + }; +} + +// Folds addition of an addition where each operation has a constant operand. +// Cases: +// (x + 2) + 2 = x + 4 +// (2 + x) + 2 = x + 4 +// 2 + (x + 2) = x + 4 +// 2 + (2 + x) = x + 4 +FoldingRule MergeAddAddArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == SpvOpFAdd || + other_inst->opcode() == SpvOpIAdd) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + if (merged_id == 0) return false; + + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {non_const_input->result_id()}}, + {SPV_OPERAND_TYPE_ID, {merged_id}}}); + return true; + } + return false; + }; +} + +// Folds addition of a subtraction where each operation has a constant operand. +// Cases: +// (x - 2) + 2 = x + 0 +// (2 - x) + 2 = 4 - x +// 2 + (x - 2) = x + 0 +// 2 + (2 - x) = 4 - x +FoldingRule MergeAddSubArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == SpvOpFSub || + other_inst->opcode() == SpvOpISub) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + bool first_is_variable = other_constants[0] == nullptr; + SpvOp op = inst->opcode(); + uint32_t op1 = 0; + uint32_t op2 = 0; + if (first_is_variable) { + // Subtract constants. Non-constant operand is first. + op1 = other_inst->GetSingleWordInOperand(0u); + op2 = PerformOperation(const_mgr, other_inst->opcode(), const_input1, + const_input2); + } else { + // Add constants. Constant operand is first. Change the opcode. + op1 = PerformOperation(const_mgr, inst->opcode(), const_input1, + const_input2); + op2 = other_inst->GetSingleWordInOperand(1u); + op = other_inst->opcode(); + } + if (op1 == 0 || op2 == 0) return false; + + inst->SetOpcode(op); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + return false; + }; +} + +// Folds subtraction of an addition where each operand has a constant operand. +// Cases: +// (x + 2) - 2 = x + 0 +// (2 + x) - 2 = x + 0 +// 2 - (x + 2) = 0 - x +// 2 - (2 + x) = 0 - x +FoldingRule MergeSubAddArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == SpvOpFAdd || + other_inst->opcode() == SpvOpIAdd) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + + // If the first operand of the sub is not a constant, swap the constants + // so the subtraction has the correct operands. + if (constants[0] == nullptr) std::swap(const_input1, const_input2); + // Subtract the constants. + uint32_t merged_id = PerformOperation(const_mgr, inst->opcode(), + const_input1, const_input2); + SpvOp op = inst->opcode(); + uint32_t op1 = 0; + uint32_t op2 = 0; + if (constants[0] == nullptr) { + // Non-constant operand is first. Change the opcode. + op1 = non_const_input->result_id(); + op2 = merged_id; + op = other_inst->opcode(); + } else { + // Constant operand is first. + op1 = merged_id; + op2 = non_const_input->result_id(); + } + if (op1 == 0 || op2 == 0) return false; + + inst->SetOpcode(op); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + return false; + }; +} + +// Folds subtraction of a subtraction where each operand has a constant operand. +// Cases: +// (x - 2) - 2 = x - 4 +// (2 - x) - 2 = 0 - x +// 2 - (x - 2) = 4 - x +// 2 - (2 - x) = x + 0 +FoldingRule MergeSubSubArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFSub || inst->opcode() == SpvOpISub); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + const analysis::Constant* const_input1 = ConstInput(constants); + if (!const_input1) return false; + Instruction* other_inst = NonConstInput(context, constants[0], inst); + if (uses_float && !other_inst->IsFloatingPointFoldingAllowed()) + return false; + + if (other_inst->opcode() == SpvOpFSub || + other_inst->opcode() == SpvOpISub) { + std::vector other_constants = + const_mgr->GetOperandConstants(other_inst); + const analysis::Constant* const_input2 = ConstInput(other_constants); + if (!const_input2) return false; + + Instruction* non_const_input = + NonConstInput(context, other_constants[0], other_inst); + + // Merge the constants. + uint32_t merged_id = 0; + SpvOp merge_op = inst->opcode(); + if (other_constants[0] == nullptr) { + merge_op = uses_float ? SpvOpFAdd : SpvOpIAdd; + } else if (constants[0] == nullptr) { + std::swap(const_input1, const_input2); + } + merged_id = + PerformOperation(const_mgr, merge_op, const_input1, const_input2); + if (merged_id == 0) return false; + + SpvOp op = inst->opcode(); + if (constants[0] != nullptr && other_constants[0] != nullptr) { + // Change the operation. + op = uses_float ? SpvOpFAdd : SpvOpIAdd; + } + + uint32_t op1 = 0; + uint32_t op2 = 0; + if ((constants[0] == nullptr) ^ (other_constants[0] == nullptr)) { + op1 = merged_id; + op2 = non_const_input->result_id(); + } else { + op1 = non_const_input->result_id(); + op2 = merged_id; + } + + inst->SetOpcode(op); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}}); + return true; + } + return false; + }; +} + +// Helper function for MergeGenericAddSubArithmetic. If |addend| and +// subtrahend of |sub| is the same, merge to copy of minuend of |sub|. +bool MergeGenericAddendSub(uint32_t addend, uint32_t sub, Instruction* inst) { + IRContext* context = inst->context(); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + Instruction* sub_inst = def_use_mgr->GetDef(sub); + if (sub_inst->opcode() != SpvOpFSub && sub_inst->opcode() != SpvOpISub) + return false; + if (sub_inst->opcode() == SpvOpFSub && + !sub_inst->IsFloatingPointFoldingAllowed()) + return false; + if (addend != sub_inst->GetSingleWordInOperand(1)) return false; + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {sub_inst->GetSingleWordInOperand(0)}}}); + context->UpdateDefUse(inst); + return true; +} + +// Folds addition of a subtraction where the subtrahend is equal to the +// other addend. Return a copy of the minuend. Accepts generic (const and +// non-const) operands. +// Cases: +// (a - b) + b = a +// b + (a - b) = a +FoldingRule MergeGenericAddSubArithmetic() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + uint32_t width = ElementWidth(type); + if (width != 32 && width != 64) return false; + + uint32_t add_op0 = inst->GetSingleWordInOperand(0); + uint32_t add_op1 = inst->GetSingleWordInOperand(1); + if (MergeGenericAddendSub(add_op0, add_op1, inst)) return true; + return MergeGenericAddendSub(add_op1, add_op0, inst); + }; +} + +// Helper function for FactorAddMuls. If |factor0_0| is the same as |factor1_0|, +// generate |factor0_0| * (|factor0_1| + |factor1_1|). +bool FactorAddMulsOpnds(uint32_t factor0_0, uint32_t factor0_1, + uint32_t factor1_0, uint32_t factor1_1, + Instruction* inst) { + IRContext* context = inst->context(); + if (factor0_0 != factor1_0) return false; + InstructionBuilder ir_builder( + context, inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* new_add_inst = ir_builder.AddBinaryOp( + inst->type_id(), inst->opcode(), factor0_1, factor1_1); + inst->SetOpcode(inst->opcode() == SpvOpFAdd ? SpvOpFMul : SpvOpIMul); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {factor0_0}}, + {SPV_OPERAND_TYPE_ID, {new_add_inst->result_id()}}}); + context->UpdateDefUse(inst); + return true; +} + +// Perform the following factoring identity, handling all operand order +// combinations: (a * b) + (a * c) = a * (b + c) +FoldingRule FactorAddMuls() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpFAdd || inst->opcode() == SpvOpIAdd); + const analysis::Type* type = + context->get_type_mgr()->GetType(inst->type_id()); + bool uses_float = HasFloatingPoint(type); + if (uses_float && !inst->IsFloatingPointFoldingAllowed()) return false; + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t add_op0 = inst->GetSingleWordInOperand(0); + Instruction* add_op0_inst = def_use_mgr->GetDef(add_op0); + if (add_op0_inst->opcode() != SpvOpFMul && + add_op0_inst->opcode() != SpvOpIMul) + return false; + uint32_t add_op1 = inst->GetSingleWordInOperand(1); + Instruction* add_op1_inst = def_use_mgr->GetDef(add_op1); + if (add_op1_inst->opcode() != SpvOpFMul && + add_op1_inst->opcode() != SpvOpIMul) + return false; + + // Only perform this optimization if both of the muls only have one use. + // Otherwise this is a deoptimization in size and performance. + if (def_use_mgr->NumUses(add_op0_inst) > 1) return false; + if (def_use_mgr->NumUses(add_op1_inst) > 1) return false; + + if (add_op0_inst->opcode() == SpvOpFMul && + (!add_op0_inst->IsFloatingPointFoldingAllowed() || + !add_op1_inst->IsFloatingPointFoldingAllowed())) + return false; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + // Check if operand i in add_op0_inst matches operand j in add_op1_inst. + if (FactorAddMulsOpnds(add_op0_inst->GetSingleWordInOperand(i), + add_op0_inst->GetSingleWordInOperand(1 - i), + add_op1_inst->GetSingleWordInOperand(j), + add_op1_inst->GetSingleWordInOperand(1 - j), + inst)) + return true; + } + } + return false; + }; +} + +FoldingRule IntMultipleBy1() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpIMul && "Wrong opcode. Should be OpIMul."); + for (uint32_t i = 0; i < 2; i++) { + if (constants[i] == nullptr) { + continue; + } + const analysis::IntConstant* int_constant = constants[i]->AsIntConstant(); + if (int_constant) { + uint32_t width = ElementWidth(int_constant->type()); + if (width != 32 && width != 64) return false; + bool is_one = (width == 32) ? int_constant->GetU32BitValue() == 1u + : int_constant->GetU64BitValue() == 1ull; + if (is_one) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1 - i)}}}); + return true; + } + } + } + return false; + }; +} + +FoldingRule CompositeConstructFeedingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + // If the input to an OpCompositeExtract is an OpCompositeConstruct, + // then we can simply use the appropriate element in the construction. + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + // If there are no index operands, then this rule cannot do anything. + if (inst->NumInOperands() <= 1) { + return false; + } + + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != SpvOpCompositeConstruct) { + return false; + } + + std::vector operands; + analysis::Type* composite_type = type_mgr->GetType(cinst->type_id()); + if (composite_type->AsVector() == nullptr) { + // Get the element being extracted from the OpCompositeConstruct + // Since it is not a vector, it is simple to extract the single element. + uint32_t element_index = inst->GetSingleWordInOperand(1); + uint32_t element_id = cinst->GetSingleWordInOperand(element_index); + operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); + + // Add the remaining indices for extraction. + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, + {inst->GetSingleWordInOperand(i)}}); + } + + } else { + // With vectors we have to handle the case where it is concatenating + // vectors. + assert(inst->NumInOperands() == 2 && + "Expecting a vector of scalar values."); + + uint32_t element_index = inst->GetSingleWordInOperand(1); + for (uint32_t construct_index = 0; + construct_index < cinst->NumInOperands(); ++construct_index) { + uint32_t element_id = cinst->GetSingleWordInOperand(construct_index); + Instruction* element_def = def_use_mgr->GetDef(element_id); + analysis::Vector* element_type = + type_mgr->GetType(element_def->type_id())->AsVector(); + if (element_type) { + uint32_t vector_size = element_type->element_count(); + if (vector_size <= element_index) { + // The element we want comes after this vector. + element_index -= vector_size; + } else { + // We want an element of this vector. + operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); + operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {element_index}}); + break; + } + } else { + if (element_index == 0) { + // This is a scalar, and we this is the element we are extracting. + operands.push_back({SPV_OPERAND_TYPE_ID, {element_id}}); + break; + } else { + // Skip over this scalar value. + --element_index; + } + } + } + } + + // If there were no extra indices, then we have the final object. No need + // to extract even more. + if (operands.size() == 1) { + inst->SetOpcode(SpvOpCopyObject); + } + + inst->SetInOperands(std::move(operands)); + return true; + }; +} + +// If the OpCompositeConstruct is simply putting back together elements that +// where extracted from the same source, we can simply reuse the source. +// +// This is a common code pattern because of the way that scalar replacement +// works. +bool CompositeExtractFeedingConstruct( + IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpCompositeConstruct && + "Wrong opcode. Should be OpCompositeConstruct."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t original_id = 0; + + if (inst->NumInOperands() == 0) { + // The struct being constructed has no members. + return false; + } + + // Check each element to make sure they are: + // - extractions + // - extracting the same position they are inserting + // - all extract from the same id. + for (uint32_t i = 0; i < inst->NumInOperands(); ++i) { + const uint32_t element_id = inst->GetSingleWordInOperand(i); + Instruction* element_inst = def_use_mgr->GetDef(element_id); + + if (element_inst->opcode() != SpvOpCompositeExtract) { + return false; + } + + if (element_inst->NumInOperands() != 2) { + return false; + } + + if (element_inst->GetSingleWordInOperand(1) != i) { + return false; + } + + if (i == 0) { + original_id = + element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + } else if (original_id != + element_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx)) { + return false; + } + } + + // The last check it to see that the object being extracted from is the + // correct type. + Instruction* original_inst = def_use_mgr->GetDef(original_id); + if (original_inst->type_id() != inst->type_id()) { + return false; + } + + // Simplify by using the original object. + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {original_id}}}); + return true; +} + +FoldingRule InsertFeedingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != SpvOpCompositeInsert) { + return false; + } + + // Find the first position where the list of insert and extract indicies + // differ, if at all. + uint32_t i; + for (i = 1; i < inst->NumInOperands(); ++i) { + if (i + 1 >= cinst->NumInOperands()) { + break; + } + + if (inst->GetSingleWordInOperand(i) != + cinst->GetSingleWordInOperand(i + 1)) { + break; + } + } + + // We are extracting the element that was inserted. + if (i == inst->NumInOperands() && i + 1 == cinst->NumInOperands()) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, + {cinst->GetSingleWordInOperand(kInsertObjectIdInIdx)}}}); + return true; + } + + // Extracting the value that was inserted along with values for the base + // composite. Cannot do anything. + if (i == inst->NumInOperands()) { + return false; + } + + // Extracting an element of the value that was inserted. Extract from + // that value directly. + if (i + 1 == cinst->NumInOperands()) { + std::vector operands; + operands.push_back( + {SPV_OPERAND_TYPE_ID, + {cinst->GetSingleWordInOperand(kInsertObjectIdInIdx)}}); + for (; i < inst->NumInOperands(); ++i) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, + {inst->GetSingleWordInOperand(i)}}); + } + inst->SetInOperands(std::move(operands)); + return true; + } + + // Extracting a value that is disjoint from the element being inserted. + // Rewrite the extract to use the composite input to the insert. + std::vector operands; + operands.push_back( + {SPV_OPERAND_TYPE_ID, + {cinst->GetSingleWordInOperand(kInsertCompositeIdInIdx)}}); + for (i = 1; i < inst->NumInOperands(); ++i) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, + {inst->GetSingleWordInOperand(i)}}); + } + inst->SetInOperands(std::move(operands)); + return true; + }; +} + +// When a VectorShuffle is feeding an Extract, we can extract from one of the +// operands of the VectorShuffle. We just need to adjust the index in the +// extract instruction. +FoldingRule VectorShuffleFeedingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + uint32_t cid = inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* cinst = def_use_mgr->GetDef(cid); + + if (cinst->opcode() != SpvOpVectorShuffle) { + return false; + } + + // Find the size of the first vector operand of the VectorShuffle + Instruction* first_input = + def_use_mgr->GetDef(cinst->GetSingleWordInOperand(0)); + analysis::Type* first_input_type = + type_mgr->GetType(first_input->type_id()); + assert(first_input_type->AsVector() && + "Input to vector shuffle should be vectors."); + uint32_t first_input_size = first_input_type->AsVector()->element_count(); + + // Get index of the element the vector shuffle is placing in the position + // being extracted. + uint32_t new_index = + cinst->GetSingleWordInOperand(2 + inst->GetSingleWordInOperand(1)); + + // Extracting an undefined value so fold this extract into an undef. + const uint32_t undef_literal_value = 0xffffffff; + if (new_index == undef_literal_value) { + inst->SetOpcode(SpvOpUndef); + inst->SetInOperands({}); + return true; + } + + // Get the id of the of the vector the elemtent comes from, and update the + // index if needed. + uint32_t new_vector = 0; + if (new_index < first_input_size) { + new_vector = cinst->GetSingleWordInOperand(0); + } else { + new_vector = cinst->GetSingleWordInOperand(1); + new_index -= first_input_size; + } + + // Update the extract instruction. + inst->SetInOperand(kExtractCompositeIdInIdx, {new_vector}); + inst->SetInOperand(1, {new_index}); + return true; + }; +} + +// When an FMix with is feeding an Extract that extracts an element whose +// corresponding |a| in the FMix is 0 or 1, we can extract from one of the +// operands of the FMix. +FoldingRule FMixFeedingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + + uint32_t composite_id = + inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* composite_inst = def_use_mgr->GetDef(composite_id); + + if (composite_inst->opcode() != SpvOpExtInst) { + return false; + } + + uint32_t inst_set_id = + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (composite_inst->GetSingleWordInOperand(kExtInstSetIdInIdx) != + inst_set_id || + composite_inst->GetSingleWordInOperand(kExtInstInstructionInIdx) != + GLSLstd450FMix) { + return false; + } + + // Get the |a| for the FMix instruction. + uint32_t a_id = composite_inst->GetSingleWordInOperand(kFMixAIdInIdx); + std::unique_ptr a(inst->Clone(context)); + a->SetInOperand(kExtractCompositeIdInIdx, {a_id}); + context->get_instruction_folder().FoldInstruction(a.get()); + + if (a->opcode() != SpvOpCopyObject) { + return false; + } + + const analysis::Constant* a_const = + const_mgr->FindDeclaredConstant(a->GetSingleWordInOperand(0)); + + if (!a_const) { + return false; + } + + bool use_x = false; + + assert(a_const->type()->AsFloat()); + double element_value = a_const->GetValueAsDouble(); + if (element_value == 0.0) { + use_x = true; + } else if (element_value == 1.0) { + use_x = false; + } else { + return false; + } + + // Get the id of the of the vector the element comes from. + uint32_t new_vector = 0; + if (use_x) { + new_vector = composite_inst->GetSingleWordInOperand(kFMixXIdInIdx); + } else { + new_vector = composite_inst->GetSingleWordInOperand(kFMixYIdInIdx); + } + + // Update the extract instruction. + inst->SetInOperand(kExtractCompositeIdInIdx, {new_vector}); + return true; + }; +} + +FoldingRule RedundantPhi() { + // An OpPhi instruction where all values are the same or the result of the phi + // itself, can be replaced by the value itself. + return [](IRContext*, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpPhi && "Wrong opcode. Should be OpPhi."); + + uint32_t incoming_value = 0; + + for (uint32_t i = 0; i < inst->NumInOperands(); i += 2) { + uint32_t op_id = inst->GetSingleWordInOperand(i); + if (op_id == inst->result_id()) { + continue; + } + + if (incoming_value == 0) { + incoming_value = op_id; + } else if (op_id != incoming_value) { + // Found two possible value. Can't simplify. + return false; + } + } + + if (incoming_value == 0) { + // Code looks invalid. Don't do anything. + return false; + } + + // We have a single incoming value. Simplify using that value. + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {incoming_value}}}); + return true; + }; +} + +FoldingRule RedundantSelect() { + // An OpSelect instruction where both values are the same or the condition is + // constant can be replaced by one of the values + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpSelect && + "Wrong opcode. Should be OpSelect."); + assert(inst->NumInOperands() == 3); + assert(constants.size() == 3); + + uint32_t true_id = inst->GetSingleWordInOperand(1); + uint32_t false_id = inst->GetSingleWordInOperand(2); + + if (true_id == false_id) { + // Both results are the same, condition doesn't matter + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {true_id}}}); + return true; + } else if (constants[0]) { + const analysis::Type* type = constants[0]->type(); + if (type->AsBool()) { + // Scalar constant value, select the corresponding value. + inst->SetOpcode(SpvOpCopyObject); + if (constants[0]->AsNullConstant() || + !constants[0]->AsBoolConstant()->value()) { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {false_id}}}); + } else { + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {true_id}}}); + } + return true; + } else { + assert(type->AsVector()); + if (constants[0]->AsNullConstant()) { + // All values come from false id. + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {false_id}}}); + return true; + } else { + // Convert to a vector shuffle. + std::vector ops; + ops.push_back({SPV_OPERAND_TYPE_ID, {true_id}}); + ops.push_back({SPV_OPERAND_TYPE_ID, {false_id}}); + const analysis::VectorConstant* vector_const = + constants[0]->AsVectorConstant(); + uint32_t size = + static_cast(vector_const->GetComponents().size()); + for (uint32_t i = 0; i != size; ++i) { + const analysis::Constant* component = + vector_const->GetComponents()[i]; + if (component->AsNullConstant() || + !component->AsBoolConstant()->value()) { + // Selecting from the false vector which is the second input + // vector to the shuffle. Offset the index by |size|. + ops.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {i + size}}); + } else { + // Selecting from true vector which is the first input vector to + // the shuffle. + ops.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {i}}); + } + } + + inst->SetOpcode(SpvOpVectorShuffle); + inst->SetInOperands(std::move(ops)); + return true; + } + } + } + + return false; + }; +} + +enum class FloatConstantKind { Unknown, Zero, One }; + +FloatConstantKind getFloatConstantKind(const analysis::Constant* constant) { + if (constant == nullptr) { + return FloatConstantKind::Unknown; + } + + assert(HasFloatingPoint(constant->type()) && "Unexpected constant type"); + + if (constant->AsNullConstant()) { + return FloatConstantKind::Zero; + } else if (const analysis::VectorConstant* vc = + constant->AsVectorConstant()) { + const std::vector& components = + vc->GetComponents(); + assert(!components.empty()); + + FloatConstantKind kind = getFloatConstantKind(components[0]); + + for (size_t i = 1; i < components.size(); ++i) { + if (getFloatConstantKind(components[i]) != kind) { + return FloatConstantKind::Unknown; + } + } + + return kind; + } else if (const analysis::FloatConstant* fc = constant->AsFloatConstant()) { + if (fc->IsZero()) return FloatConstantKind::Zero; + + uint32_t width = fc->type()->AsFloat()->width(); + if (width != 32 && width != 64) return FloatConstantKind::Unknown; + + double value = (width == 64) ? fc->GetDoubleValue() : fc->GetFloatValue(); + + if (value == 0.0) { + return FloatConstantKind::Zero; + } else if (value == 1.0) { + return FloatConstantKind::One; + } else { + return FloatConstantKind::Unknown; + } + } else { + return FloatConstantKind::Unknown; + } +} + +FoldingRule RedundantFAdd() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFAdd && "Wrong opcode. Should be OpFAdd."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + FloatConstantKind kind1 = getFloatConstantKind(constants[1]); + + if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::Zero) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand( + kind0 == FloatConstantKind::Zero ? 1 : 0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFSub() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFSub && "Wrong opcode. Should be OpFSub."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + FloatConstantKind kind1 = getFloatConstantKind(constants[1]); + + if (kind0 == FloatConstantKind::Zero) { + inst->SetOpcode(SpvOpFNegate); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1)}}}); + return true; + } + + if (kind1 == FloatConstantKind::Zero) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFMul() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFMul && "Wrong opcode. Should be OpFMul."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + FloatConstantKind kind1 = getFloatConstantKind(constants[1]); + + if (kind0 == FloatConstantKind::Zero || kind1 == FloatConstantKind::Zero) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand( + kind0 == FloatConstantKind::Zero ? 0 : 1)}}}); + return true; + } + + if (kind0 == FloatConstantKind::One || kind1 == FloatConstantKind::One) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand( + kind0 == FloatConstantKind::One ? 1 : 0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFDiv() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpFDiv && "Wrong opcode. Should be OpFDiv."); + assert(constants.size() == 2); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + FloatConstantKind kind0 = getFloatConstantKind(constants[0]); + FloatConstantKind kind1 = getFloatConstantKind(constants[1]); + + if (kind0 == FloatConstantKind::Zero) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); + return true; + } + + if (kind1 == FloatConstantKind::One) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(0)}}}); + return true; + } + + return false; + }; +} + +FoldingRule RedundantFMix() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpExtInst && + "Wrong opcode. Should be OpExtInst."); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + uint32_t instSetId = + context->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (inst->GetSingleWordInOperand(kExtInstSetIdInIdx) == instSetId && + inst->GetSingleWordInOperand(kExtInstInstructionInIdx) == + GLSLstd450FMix) { + assert(constants.size() == 5); + + FloatConstantKind kind4 = getFloatConstantKind(constants[4]); + + if (kind4 == FloatConstantKind::Zero || kind4 == FloatConstantKind::One) { + inst->SetOpcode(SpvOpCopyObject); + inst->SetInOperands( + {{SPV_OPERAND_TYPE_ID, + {inst->GetSingleWordInOperand(kind4 == FloatConstantKind::Zero + ? kFMixXIdInIdx + : kFMixYIdInIdx)}}}); + return true; + } + } + + return false; + }; +} + +// This rule handles addition of zero for integers. +FoldingRule RedundantIAdd() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpIAdd && "Wrong opcode. Should be OpIAdd."); + + uint32_t operand = std::numeric_limits::max(); + const analysis::Type* operand_type = nullptr; + if (constants[0] && constants[0]->IsZero()) { + operand = inst->GetSingleWordInOperand(1); + operand_type = constants[0]->type(); + } else if (constants[1] && constants[1]->IsZero()) { + operand = inst->GetSingleWordInOperand(0); + operand_type = constants[1]->type(); + } + + if (operand != std::numeric_limits::max()) { + const analysis::Type* inst_type = + context->get_type_mgr()->GetType(inst->type_id()); + if (inst_type->IsSame(operand_type)) { + inst->SetOpcode(SpvOpCopyObject); + } else { + inst->SetOpcode(SpvOpBitcast); + } + inst->SetInOperands({{SPV_OPERAND_TYPE_ID, {operand}}}); + return true; + } + return false; + }; +} + +// This rule look for a dot with a constant vector containing a single 1 and +// the rest 0s. This is the same as doing an extract. +FoldingRule DotProductDoingExtract() { + return [](IRContext* context, Instruction* inst, + const std::vector& constants) { + assert(inst->opcode() == SpvOpDot && "Wrong opcode. Should be OpDot."); + + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + + if (!inst->IsFloatingPointFoldingAllowed()) { + return false; + } + + for (int i = 0; i < 2; ++i) { + if (!constants[i]) { + continue; + } + + const analysis::Vector* vector_type = constants[i]->type()->AsVector(); + assert(vector_type && "Inputs to OpDot must be vectors."); + const analysis::Float* element_type = + vector_type->element_type()->AsFloat(); + assert(element_type && "Inputs to OpDot must be vectors of floats."); + uint32_t element_width = element_type->width(); + if (element_width != 32 && element_width != 64) { + return false; + } + + std::vector components; + components = constants[i]->GetVectorComponents(const_mgr); + + const uint32_t kNotFound = std::numeric_limits::max(); + + uint32_t component_with_one = kNotFound; + bool all_others_zero = true; + for (uint32_t j = 0; j < components.size(); ++j) { + const analysis::Constant* element = components[j]; + double value = + (element_width == 32 ? element->GetFloat() : element->GetDouble()); + if (value == 0.0) { + continue; + } else if (value == 1.0) { + if (component_with_one == kNotFound) { + component_with_one = j; + } else { + component_with_one = kNotFound; + break; + } + } else { + all_others_zero = false; + break; + } + } + + if (!all_others_zero || component_with_one == kNotFound) { + continue; + } + + std::vector operands; + operands.push_back( + {SPV_OPERAND_TYPE_ID, {inst->GetSingleWordInOperand(1u - i)}}); + operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {component_with_one}}); + + inst->SetOpcode(SpvOpCompositeExtract); + inst->SetInOperands(std::move(operands)); + return true; + } + return false; + }; +} + +// If we are storing an undef, then we can remove the store. +// +// TODO: We can do something similar for OpImageWrite, but checking for volatile +// is complicated. Waiting to see if it is needed. +FoldingRule StoringUndef() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpStore && "Wrong opcode. Should be OpStore."); + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + + // If this is a volatile store, the store cannot be removed. + if (inst->NumInOperands() == 3) { + if (inst->GetSingleWordInOperand(2) & SpvMemoryAccessVolatileMask) { + return false; + } + } + + uint32_t object_id = inst->GetSingleWordInOperand(kStoreObjectInIdx); + Instruction* object_inst = def_use_mgr->GetDef(object_id); + if (object_inst->opcode() == SpvOpUndef) { + inst->ToNop(); + return true; + } + return false; + }; +} + +FoldingRule VectorShuffleFeedingShuffle() { + return [](IRContext* context, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpVectorShuffle && + "Wrong opcode. Should be OpVectorShuffle."); + + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context->get_type_mgr(); + + Instruction* feeding_shuffle_inst = + def_use_mgr->GetDef(inst->GetSingleWordInOperand(0)); + analysis::Vector* op0_type = + type_mgr->GetType(feeding_shuffle_inst->type_id())->AsVector(); + uint32_t op0_length = op0_type->element_count(); + + bool feeder_is_op0 = true; + if (feeding_shuffle_inst->opcode() != SpvOpVectorShuffle) { + feeding_shuffle_inst = + def_use_mgr->GetDef(inst->GetSingleWordInOperand(1)); + feeder_is_op0 = false; + } + + if (feeding_shuffle_inst->opcode() != SpvOpVectorShuffle) { + return false; + } + + Instruction* feeder2 = + def_use_mgr->GetDef(feeding_shuffle_inst->GetSingleWordInOperand(0)); + analysis::Vector* feeder_op0_type = + type_mgr->GetType(feeder2->type_id())->AsVector(); + uint32_t feeder_op0_length = feeder_op0_type->element_count(); + + uint32_t new_feeder_id = 0; + std::vector new_operands; + new_operands.resize( + 2, {SPV_OPERAND_TYPE_ID, {0}}); // Place holders for vector operands. + const uint32_t undef_literal = 0xffffffff; + for (uint32_t op = 2; op < inst->NumInOperands(); ++op) { + uint32_t component_index = inst->GetSingleWordInOperand(op); + + // Do not interpret the undefined value literal as coming from operand 1. + if (component_index != undef_literal && + feeder_is_op0 == (component_index < op0_length)) { + // This component comes from the feeding_shuffle_inst. Update + // |component_index| to be the index into the operand of the feeder. + + // Adjust component_index to get the index into the operands of the + // feeding_shuffle_inst. + if (component_index >= op0_length) { + component_index -= op0_length; + } + component_index = + feeding_shuffle_inst->GetSingleWordInOperand(component_index + 2); + + // Check if we are using a component from the first or second operand of + // the feeding instruction. + if (component_index < feeder_op0_length) { + if (new_feeder_id == 0) { + // First time through, save the id of the operand the element comes + // from. + new_feeder_id = feeding_shuffle_inst->GetSingleWordInOperand(0); + } else if (new_feeder_id != + feeding_shuffle_inst->GetSingleWordInOperand(0)) { + // We need both elements of the feeding_shuffle_inst, so we cannot + // fold. + return false; + } + } else { + if (new_feeder_id == 0) { + // First time through, save the id of the operand the element comes + // from. + new_feeder_id = feeding_shuffle_inst->GetSingleWordInOperand(1); + } else if (new_feeder_id != + feeding_shuffle_inst->GetSingleWordInOperand(1)) { + // We need both elements of the feeding_shuffle_inst, so we cannot + // fold. + return false; + } + component_index -= feeder_op0_length; + } + + if (!feeder_is_op0) { + component_index += op0_length; + } + } + new_operands.push_back( + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {component_index}}); + } + + if (new_feeder_id == 0) { + analysis::ConstantManager* const_mgr = context->get_constant_mgr(); + const analysis::Type* type = + type_mgr->GetType(feeding_shuffle_inst->type_id()); + const analysis::Constant* null_const = const_mgr->GetConstant(type, {}); + new_feeder_id = + const_mgr->GetDefiningInstruction(null_const, 0)->result_id(); + } + + if (feeder_is_op0) { + // If the size of the first vector operand changed then the indices + // referring to the second operand need to be adjusted. + Instruction* new_feeder_inst = def_use_mgr->GetDef(new_feeder_id); + analysis::Type* new_feeder_type = + type_mgr->GetType(new_feeder_inst->type_id()); + uint32_t new_op0_size = new_feeder_type->AsVector()->element_count(); + int32_t adjustment = op0_length - new_op0_size; + + if (adjustment != 0) { + for (uint32_t i = 2; i < new_operands.size(); i++) { + if (inst->GetSingleWordInOperand(i) >= op0_length) { + new_operands[i].words[0] -= adjustment; + } + } + } + + new_operands[0].words[0] = new_feeder_id; + new_operands[1] = inst->GetInOperand(1); + } else { + new_operands[1].words[0] = new_feeder_id; + new_operands[0] = inst->GetInOperand(0); + } + + inst->SetInOperands(std::move(new_operands)); + return true; + }; +} + +// Removes duplicate ids from the interface list of an OpEntryPoint +// instruction. +FoldingRule RemoveRedundantOperands() { + return [](IRContext*, Instruction* inst, + const std::vector&) { + assert(inst->opcode() == SpvOpEntryPoint && + "Wrong opcode. Should be OpEntryPoint."); + bool has_redundant_operand = false; + std::unordered_set seen_operands; + std::vector new_operands; + + new_operands.emplace_back(inst->GetOperand(0)); + new_operands.emplace_back(inst->GetOperand(1)); + new_operands.emplace_back(inst->GetOperand(2)); + for (uint32_t i = 3; i < inst->NumOperands(); ++i) { + if (seen_operands.insert(inst->GetSingleWordOperand(i)).second) { + new_operands.emplace_back(inst->GetOperand(i)); + } else { + has_redundant_operand = true; + } + } + + if (!has_redundant_operand) { + return false; + } + + inst->SetInOperands(std::move(new_operands)); + return true; + }; +} + +// If an image instruction's operand is a constant, updates the image operand +// flag from Offset to ConstOffset. +FoldingRule UpdateImageOperands() { + return [](IRContext*, Instruction* inst, + const std::vector& constants) { + const auto opcode = inst->opcode(); + (void)opcode; + assert((opcode == SpvOpImageSampleImplicitLod || + opcode == SpvOpImageSampleExplicitLod || + opcode == SpvOpImageSampleDrefImplicitLod || + opcode == SpvOpImageSampleDrefExplicitLod || + opcode == SpvOpImageSampleProjImplicitLod || + opcode == SpvOpImageSampleProjExplicitLod || + opcode == SpvOpImageSampleProjDrefImplicitLod || + opcode == SpvOpImageSampleProjDrefExplicitLod || + opcode == SpvOpImageFetch || opcode == SpvOpImageGather || + opcode == SpvOpImageDrefGather || opcode == SpvOpImageRead || + opcode == SpvOpImageWrite || + opcode == SpvOpImageSparseSampleImplicitLod || + opcode == SpvOpImageSparseSampleExplicitLod || + opcode == SpvOpImageSparseSampleDrefImplicitLod || + opcode == SpvOpImageSparseSampleDrefExplicitLod || + opcode == SpvOpImageSparseSampleProjImplicitLod || + opcode == SpvOpImageSparseSampleProjExplicitLod || + opcode == SpvOpImageSparseSampleProjDrefImplicitLod || + opcode == SpvOpImageSparseSampleProjDrefExplicitLod || + opcode == SpvOpImageSparseFetch || + opcode == SpvOpImageSparseGather || + opcode == SpvOpImageSparseDrefGather || + opcode == SpvOpImageSparseRead) && + "Wrong opcode. Should be an image instruction."); + + int32_t operand_index = ImageOperandsMaskInOperandIndex(inst); + if (operand_index >= 0) { + auto image_operands = inst->GetSingleWordInOperand(operand_index); + if (image_operands & SpvImageOperandsOffsetMask) { + uint32_t offset_operand_index = operand_index + 1; + if (image_operands & SpvImageOperandsBiasMask) offset_operand_index++; + if (image_operands & SpvImageOperandsLodMask) offset_operand_index++; + if (image_operands & SpvImageOperandsGradMask) + offset_operand_index += 2; + assert(((image_operands & SpvImageOperandsConstOffsetMask) == 0) && + "Offset and ConstOffset may not be used together"); + if (offset_operand_index < inst->NumOperands()) { + if (constants[offset_operand_index]) { + image_operands = image_operands | SpvImageOperandsConstOffsetMask; + image_operands = image_operands & ~SpvImageOperandsOffsetMask; + inst->SetInOperand(operand_index, {image_operands}); + return true; + } + } + } + } + + return false; + }; +} + +} // namespace + +void FoldingRules::AddFoldingRules() { + // Add all folding rules to the list for the opcodes to which they apply. + // Note that the order in which rules are added to the list matters. If a rule + // applies to the instruction, the rest of the rules will not be attempted. + // Take that into consideration. + rules_[SpvOpCompositeConstruct].push_back(CompositeExtractFeedingConstruct); + + rules_[SpvOpCompositeExtract].push_back(InsertFeedingExtract()); + rules_[SpvOpCompositeExtract].push_back(CompositeConstructFeedingExtract()); + rules_[SpvOpCompositeExtract].push_back(VectorShuffleFeedingExtract()); + rules_[SpvOpCompositeExtract].push_back(FMixFeedingExtract()); + + rules_[SpvOpDot].push_back(DotProductDoingExtract()); + + rules_[SpvOpEntryPoint].push_back(RemoveRedundantOperands()); + + rules_[SpvOpFAdd].push_back(RedundantFAdd()); + rules_[SpvOpFAdd].push_back(MergeAddNegateArithmetic()); + rules_[SpvOpFAdd].push_back(MergeAddAddArithmetic()); + rules_[SpvOpFAdd].push_back(MergeAddSubArithmetic()); + rules_[SpvOpFAdd].push_back(MergeGenericAddSubArithmetic()); + rules_[SpvOpFAdd].push_back(FactorAddMuls()); + + rules_[SpvOpFDiv].push_back(RedundantFDiv()); + rules_[SpvOpFDiv].push_back(ReciprocalFDiv()); + rules_[SpvOpFDiv].push_back(MergeDivDivArithmetic()); + rules_[SpvOpFDiv].push_back(MergeDivMulArithmetic()); + rules_[SpvOpFDiv].push_back(MergeDivNegateArithmetic()); + + rules_[SpvOpFMul].push_back(RedundantFMul()); + rules_[SpvOpFMul].push_back(MergeMulMulArithmetic()); + rules_[SpvOpFMul].push_back(MergeMulDivArithmetic()); + rules_[SpvOpFMul].push_back(MergeMulNegateArithmetic()); + + rules_[SpvOpFNegate].push_back(MergeNegateArithmetic()); + rules_[SpvOpFNegate].push_back(MergeNegateAddSubArithmetic()); + rules_[SpvOpFNegate].push_back(MergeNegateMulDivArithmetic()); + + rules_[SpvOpFSub].push_back(RedundantFSub()); + rules_[SpvOpFSub].push_back(MergeSubNegateArithmetic()); + rules_[SpvOpFSub].push_back(MergeSubAddArithmetic()); + rules_[SpvOpFSub].push_back(MergeSubSubArithmetic()); + + rules_[SpvOpIAdd].push_back(RedundantIAdd()); + rules_[SpvOpIAdd].push_back(MergeAddNegateArithmetic()); + rules_[SpvOpIAdd].push_back(MergeAddAddArithmetic()); + rules_[SpvOpIAdd].push_back(MergeAddSubArithmetic()); + rules_[SpvOpIAdd].push_back(MergeGenericAddSubArithmetic()); + rules_[SpvOpIAdd].push_back(FactorAddMuls()); + + rules_[SpvOpIMul].push_back(IntMultipleBy1()); + rules_[SpvOpIMul].push_back(MergeMulMulArithmetic()); + rules_[SpvOpIMul].push_back(MergeMulNegateArithmetic()); + + rules_[SpvOpISub].push_back(MergeSubNegateArithmetic()); + rules_[SpvOpISub].push_back(MergeSubAddArithmetic()); + rules_[SpvOpISub].push_back(MergeSubSubArithmetic()); + + rules_[SpvOpPhi].push_back(RedundantPhi()); + + rules_[SpvOpSDiv].push_back(MergeDivNegateArithmetic()); + + rules_[SpvOpSNegate].push_back(MergeNegateArithmetic()); + rules_[SpvOpSNegate].push_back(MergeNegateMulDivArithmetic()); + rules_[SpvOpSNegate].push_back(MergeNegateAddSubArithmetic()); + + rules_[SpvOpSelect].push_back(RedundantSelect()); + + rules_[SpvOpStore].push_back(StoringUndef()); + + rules_[SpvOpUDiv].push_back(MergeDivNegateArithmetic()); + + rules_[SpvOpVectorShuffle].push_back(VectorShuffleFeedingShuffle()); + + rules_[SpvOpImageSampleImplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSampleExplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSampleDrefImplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSampleDrefExplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSampleProjImplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSampleProjExplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSampleProjDrefImplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSampleProjDrefExplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageFetch].push_back(UpdateImageOperands()); + rules_[SpvOpImageGather].push_back(UpdateImageOperands()); + rules_[SpvOpImageDrefGather].push_back(UpdateImageOperands()); + rules_[SpvOpImageRead].push_back(UpdateImageOperands()); + rules_[SpvOpImageWrite].push_back(UpdateImageOperands()); + rules_[SpvOpImageSparseSampleImplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSparseSampleExplicitLod].push_back(UpdateImageOperands()); + rules_[SpvOpImageSparseSampleDrefImplicitLod].push_back( + UpdateImageOperands()); + rules_[SpvOpImageSparseSampleDrefExplicitLod].push_back( + UpdateImageOperands()); + rules_[SpvOpImageSparseSampleProjImplicitLod].push_back( + UpdateImageOperands()); + rules_[SpvOpImageSparseSampleProjExplicitLod].push_back( + UpdateImageOperands()); + rules_[SpvOpImageSparseSampleProjDrefImplicitLod].push_back( + UpdateImageOperands()); + rules_[SpvOpImageSparseSampleProjDrefExplicitLod].push_back( + UpdateImageOperands()); + rules_[SpvOpImageSparseFetch].push_back(UpdateImageOperands()); + rules_[SpvOpImageSparseGather].push_back(UpdateImageOperands()); + rules_[SpvOpImageSparseDrefGather].push_back(UpdateImageOperands()); + rules_[SpvOpImageSparseRead].push_back(UpdateImageOperands()); + + FeatureManager* feature_manager = context_->get_feature_mgr(); + // Add rules for GLSLstd450 + uint32_t ext_inst_glslstd450_id = + feature_manager->GetExtInstImportId_GLSLstd450(); + if (ext_inst_glslstd450_id != 0) { + ext_rules_[{ext_inst_glslstd450_id, GLSLstd450FMix}].push_back( + RedundantFMix()); + } +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/folding_rules.h b/third_party/spirv-tools/source/opt/folding_rules.h new file mode 100644 index 0000000..f1a8639 --- /dev/null +++ b/third_party/spirv-tools/source/opt/folding_rules.h @@ -0,0 +1,118 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FOLDING_RULES_H_ +#define SOURCE_OPT_FOLDING_RULES_H_ + +#include +#include +#include + +#include "source/opt/constants.h" + +namespace spvtools { +namespace opt { + +// Folding Rules: +// +// The folding mechanism is built around the concept of a |FoldingRule|. A +// folding rule is a function that implements a method of simplifying an +// instruction. +// +// The inputs to a folding rule are: +// |inst| - the instruction to be simplified. +// |constants| - if an in-operands is an id of a constant, then the +// corresponding value in |constants| contains that +// constant value. Otherwise, the corresponding entry in +// |constants| is |nullptr|. +// +// A folding rule returns true if |inst| can be simplified using this rule. If +// the instruction can be simplified, then |inst| is changed to the simplified +// instruction. Otherwise, |inst| remains the same. +// +// See folding_rules.cpp for examples on how to write a folding rule. It is +// important to note that if |inst| can be folded to the result of an +// instruction that feed it, then |inst| should be changed to an OpCopyObject +// that copies that id. +// +// Be sure to add new folding rules to the table of folding rules in the +// constructor for FoldingRules. The new rule should be added to the list for +// every opcode that it applies to. Note that earlier rules in the list are +// given priority. That is, if an earlier rule is able to fold an instruction, +// the later rules will not be attempted. + +using FoldingRule = std::function& constants)>; + +class FoldingRules { + public: + using FoldingRuleSet = std::vector; + + explicit FoldingRules(IRContext* ctx) : context_(ctx) {} + virtual ~FoldingRules() = default; + + const FoldingRuleSet& GetRulesForInstruction(Instruction* inst) const { + if (inst->opcode() != SpvOpExtInst) { + auto it = rules_.find(inst->opcode()); + if (it != rules_.end()) { + return it->second; + } + } else { + uint32_t ext_inst_id = inst->GetSingleWordInOperand(0); + uint32_t ext_opcode = inst->GetSingleWordInOperand(1); + auto it = ext_rules_.find({ext_inst_id, ext_opcode}); + if (it != ext_rules_.end()) { + return it->second; + } + } + return empty_vector_; + } + + IRContext* context() { return context_; } + + // Adds the folding rules for the object. + virtual void AddFoldingRules(); + + protected: + // The folding rules for core instructions. + std::unordered_map rules_; + + // The folding rules for extended instructions. + struct Key { + uint32_t instruction_set; + uint32_t opcode; + }; + + friend bool operator<(const Key& a, const Key& b) { + if (a.instruction_set < b.instruction_set) { + return true; + } + if (a.instruction_set > b.instruction_set) { + return false; + } + return a.opcode < b.opcode; + } + + std::map ext_rules_; + + private: + IRContext* context_; + FoldingRuleSet empty_vector_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FOLDING_RULES_H_ diff --git a/third_party/spirv-tools/source/opt/freeze_spec_constant_value_pass.cpp b/third_party/spirv-tools/source/opt/freeze_spec_constant_value_pass.cpp new file mode 100644 index 0000000..10e98fd --- /dev/null +++ b/third_party/spirv-tools/source/opt/freeze_spec_constant_value_pass.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/freeze_spec_constant_value_pass.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status FreezeSpecConstantValuePass::Process() { + bool modified = false; + auto ctx = context(); + ctx->module()->ForEachInst([&modified, ctx](Instruction* inst) { + switch (inst->opcode()) { + case SpvOp::SpvOpSpecConstant: + inst->SetOpcode(SpvOp::SpvOpConstant); + modified = true; + break; + case SpvOp::SpvOpSpecConstantTrue: + inst->SetOpcode(SpvOp::SpvOpConstantTrue); + modified = true; + break; + case SpvOp::SpvOpSpecConstantFalse: + inst->SetOpcode(SpvOp::SpvOpConstantFalse); + modified = true; + break; + case SpvOp::SpvOpDecorate: + if (inst->GetSingleWordInOperand(1) == + SpvDecoration::SpvDecorationSpecId) { + ctx->KillInst(inst); + modified = true; + } + break; + default: + break; + } + }); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/freeze_spec_constant_value_pass.h b/third_party/spirv-tools/source/opt/freeze_spec_constant_value_pass.h new file mode 100644 index 0000000..0663adf --- /dev/null +++ b/third_party/spirv-tools/source/opt/freeze_spec_constant_value_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FREEZE_SPEC_CONSTANT_VALUE_PASS_H_ +#define SOURCE_OPT_FREEZE_SPEC_CONSTANT_VALUE_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class FreezeSpecConstantValuePass : public Pass { + public: + const char* name() const override { return "freeze-spec-const"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FREEZE_SPEC_CONSTANT_VALUE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/function.cpp b/third_party/spirv-tools/source/opt/function.cpp new file mode 100644 index 0000000..52054ea --- /dev/null +++ b/third_party/spirv-tools/source/opt/function.cpp @@ -0,0 +1,275 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/function.h" + +#include +#include + +#include "function.h" +#include "ir_context.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +Function* Function::Clone(IRContext* ctx) const { + Function* clone = + new Function(std::unique_ptr(DefInst().Clone(ctx))); + clone->params_.reserve(params_.size()); + ForEachParam( + [clone, ctx](const Instruction* inst) { + clone->AddParameter(std::unique_ptr(inst->Clone(ctx))); + }, + true); + + for (const auto& i : debug_insts_in_header_) { + clone->AddDebugInstructionInHeader( + std::unique_ptr(i.Clone(ctx))); + } + + clone->blocks_.reserve(blocks_.size()); + for (const auto& b : blocks_) { + std::unique_ptr bb(b->Clone(ctx)); + bb->SetParent(clone); + clone->AddBasicBlock(std::move(bb)); + } + + clone->SetFunctionEnd(std::unique_ptr(EndInst()->Clone(ctx))); + + clone->non_semantic_.reserve(non_semantic_.size()); + for (auto& non_semantic : non_semantic_) { + clone->AddNonSemanticInstruction( + std::unique_ptr(non_semantic->Clone(ctx))); + } + return clone; +} + +void Function::ForEachInst(const std::function& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) { + WhileEachInst( + [&f](Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts, run_on_non_semantic_insts); +} + +void Function::ForEachInst(const std::function& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) const { + WhileEachInst( + [&f](const Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts, run_on_non_semantic_insts); +} + +bool Function::WhileEachInst(const std::function& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) { + if (def_inst_) { + if (!def_inst_->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + for (auto& param : params_) { + if (!param->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + if (!debug_insts_in_header_.empty()) { + Instruction* di = &debug_insts_in_header_.front(); + while (di != nullptr) { + Instruction* next_instruction = di->NextNode(); + if (!di->WhileEachInst(f, run_on_debug_line_insts)) return false; + di = next_instruction; + } + } + + for (auto& bb : blocks_) { + if (!bb->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + if (end_inst_) { + if (!end_inst_->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + if (run_on_non_semantic_insts) { + for (auto& non_semantic : non_semantic_) { + if (!non_semantic->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + } + + return true; +} + +bool Function::WhileEachInst(const std::function& f, + bool run_on_debug_line_insts, + bool run_on_non_semantic_insts) const { + if (def_inst_) { + if (!static_cast(def_inst_.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + for (const auto& param : params_) { + if (!static_cast(param.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + for (const auto& di : debug_insts_in_header_) { + if (!static_cast(&di)->WhileEachInst( + f, run_on_debug_line_insts)) + return false; + } + + for (const auto& bb : blocks_) { + if (!static_cast(bb.get())->WhileEachInst( + f, run_on_debug_line_insts)) { + return false; + } + } + + if (end_inst_) { + if (!static_cast(end_inst_.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + + if (run_on_non_semantic_insts) { + for (auto& non_semantic : non_semantic_) { + if (!static_cast(non_semantic.get()) + ->WhileEachInst(f, run_on_debug_line_insts)) { + return false; + } + } + } + + return true; +} + +void Function::ForEachParam(const std::function& f, + bool run_on_debug_line_insts) { + for (auto& param : params_) + static_cast(param.get()) + ->ForEachInst(f, run_on_debug_line_insts); +} + +void Function::ForEachParam(const std::function& f, + bool run_on_debug_line_insts) const { + for (const auto& param : params_) + static_cast(param.get()) + ->ForEachInst(f, run_on_debug_line_insts); +} + +void Function::ForEachDebugInstructionsInHeader( + const std::function& f) { + if (debug_insts_in_header_.empty()) return; + + Instruction* di = &debug_insts_in_header_.front(); + while (di != nullptr) { + Instruction* next_instruction = di->NextNode(); + di->ForEachInst(f); + di = next_instruction; + } +} + +BasicBlock* Function::InsertBasicBlockAfter( + std::unique_ptr&& new_block, BasicBlock* position) { + for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) { + if (&*bb_iter == position) { + new_block->SetParent(this); + ++bb_iter; + bb_iter = bb_iter.InsertBefore(std::move(new_block)); + return &*bb_iter; + } + } + assert(false && "Could not find insertion point."); + return nullptr; +} + +BasicBlock* Function::InsertBasicBlockBefore( + std::unique_ptr&& new_block, BasicBlock* position) { + for (auto bb_iter = begin(); bb_iter != end(); ++bb_iter) { + if (&*bb_iter == position) { + new_block->SetParent(this); + bb_iter = bb_iter.InsertBefore(std::move(new_block)); + return &*bb_iter; + } + } + assert(false && "Could not find insertion point."); + return nullptr; +} + +bool Function::HasEarlyReturn() const { + auto post_dominator_analysis = + blocks_.front()->GetLabel()->context()->GetPostDominatorAnalysis(this); + for (auto& block : blocks_) { + if (spvOpcodeIsReturn(block->tail()->opcode()) && + !post_dominator_analysis->Dominates(block.get(), entry().get())) { + return true; + } + } + return false; +} + +bool Function::IsRecursive() const { + IRContext* ctx = blocks_.front()->GetLabel()->context(); + IRContext::ProcessFunction mark_visited = [this](Function* fp) { + return fp == this; + }; + + // Process the call tree from all of the function called by |this|. If it get + // back to |this|, then we have a recursive function. + std::queue roots; + ctx->AddCalls(this, &roots); + return ctx->ProcessCallTreeFromRoots(mark_visited, &roots); +} + +std::ostream& operator<<(std::ostream& str, const Function& func) { + str << func.PrettyPrint(); + return str; +} + +void Function::Dump() const { + std::cerr << "Function #" << result_id() << "\n" << *this << "\n"; +} + +std::string Function::PrettyPrint(uint32_t options) const { + std::ostringstream str; + ForEachInst([&str, options](const Instruction* inst) { + str << inst->PrettyPrint(options); + if (inst->opcode() != SpvOpFunctionEnd) { + str << std::endl; + } + }); + return str.str(); +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/function.h b/third_party/spirv-tools/source/opt/function.h new file mode 100644 index 0000000..4b20dcb --- /dev/null +++ b/third_party/spirv-tools/source/opt/function.h @@ -0,0 +1,264 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_FUNCTION_H_ +#define SOURCE_OPT_FUNCTION_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +class CFG; +class IRContext; +class Module; + +// A SPIR-V function. +class Function { + public: + using iterator = UptrVectorIterator; + using const_iterator = UptrVectorIterator; + + // Creates a function instance declared by the given OpFunction instruction + // |def_inst|. + inline explicit Function(std::unique_ptr def_inst); + + explicit Function(const Function& f) = delete; + + // Creates a clone of the instruction in the given |context| + // + // The parent module will default to null and needs to be explicitly set by + // the user. + Function* Clone(IRContext*) const; + // The OpFunction instruction that begins the definition of this function. + Instruction& DefInst() { return *def_inst_; } + const Instruction& DefInst() const { return *def_inst_; } + + // Appends a parameter to this function. + inline void AddParameter(std::unique_ptr p); + // Appends a debug instruction in function header to this function. + inline void AddDebugInstructionInHeader(std::unique_ptr p); + // Appends a basic block to this function. + inline void AddBasicBlock(std::unique_ptr b); + // Appends a basic block to this function at the position |ip|. + inline void AddBasicBlock(std::unique_ptr b, iterator ip); + template + inline void AddBasicBlocks(T begin, T end, iterator ip); + + // Move basic block with |id| to the position after |ip|. Both have to be + // contained in this function. + inline void MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip); + + // Delete all basic blocks that contain no instructions. + inline void RemoveEmptyBlocks(); + + // Removes a parameter from the function with result id equal to |id|. + // Does nothing if the function doesn't have such a parameter. + inline void RemoveParameter(uint32_t id); + + // Saves the given function end instruction. + inline void SetFunctionEnd(std::unique_ptr end_inst); + + // Add a non-semantic instruction that succeeds this function in the module. + // These instructions are maintained in the order they are added. + inline void AddNonSemanticInstruction( + std::unique_ptr non_semantic); + + // Returns the given function end instruction. + inline Instruction* EndInst() { return end_inst_.get(); } + inline const Instruction* EndInst() const { return end_inst_.get(); } + + // Returns function's id + inline uint32_t result_id() const { return def_inst_->result_id(); } + + // Returns function's return type id + inline uint32_t type_id() const { return def_inst_->type_id(); } + + // Returns the function's control mask + inline uint32_t control_mask() const { return def_inst_->GetSingleWordInOperand(0); } + + // Returns the entry basic block for this function. + const std::unique_ptr& entry() const { return blocks_.front(); } + + // Returns the last basic block in this function. + BasicBlock* tail() { return blocks_.back().get(); } + const BasicBlock* tail() const { return blocks_.back().get(); } + + iterator begin() { return iterator(&blocks_, blocks_.begin()); } + iterator end() { return iterator(&blocks_, blocks_.end()); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + const_iterator cbegin() const { + return const_iterator(&blocks_, blocks_.cbegin()); + } + const_iterator cend() const { + return const_iterator(&blocks_, blocks_.cend()); + } + + // Returns an iterator to the basic block |id|. + iterator FindBlock(uint32_t bb_id) { + return std::find_if(begin(), end(), [bb_id](const BasicBlock& it_bb) { + return bb_id == it_bb.id(); + }); + } + + // Runs the given function |f| on instructions in this function, in order, + // and optionally on debug line instructions that might precede them and + // non-semantic instructions that succceed the function. + void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false); + void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false) const; + // Runs the given function |f| on instructions in this function, in order, + // and optionally on debug line instructions that might precede them and + // non-semantic instructions that succeed the function. If |f| returns + // false, iteration is terminated and this function returns false. + bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false); + bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false, + bool run_on_non_semantic_insts = false) const; + + // Runs the given function |f| on each parameter instruction in this function, + // in order, and optionally on debug line instructions that might precede + // them. + void ForEachParam(const std::function& f, + bool run_on_debug_line_insts = false) const; + void ForEachParam(const std::function& f, + bool run_on_debug_line_insts = false); + + // Runs the given function |f| on each debug instruction in this function's + // header in order. + void ForEachDebugInstructionsInHeader( + const std::function& f); + + BasicBlock* InsertBasicBlockAfter(std::unique_ptr&& new_block, + BasicBlock* position); + + BasicBlock* InsertBasicBlockBefore(std::unique_ptr&& new_block, + BasicBlock* position); + + // Returns true if the function has a return block other than the exit block. + bool HasEarlyReturn() const; + + // Returns true if the function calls itself either directly or indirectly. + bool IsRecursive() const; + + // Pretty-prints all the basic blocks in this function into a std::string. + // + // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER + // is always added to |options|. + std::string PrettyPrint(uint32_t options = 0u) const; + + // Dump this function on stderr. Useful when running interactive + // debuggers. + void Dump() const; + + private: + // The OpFunction instruction that begins the definition of this function. + std::unique_ptr def_inst_; + // All parameters to this function. + std::vector> params_; + // All debug instructions in this function's header. + InstructionList debug_insts_in_header_; + // All basic blocks inside this function in specification order + std::vector> blocks_; + // The OpFunctionEnd instruction. + std::unique_ptr end_inst_; + // Non-semantic instructions succeeded by this function. + std::vector> non_semantic_; +}; + +// Pretty-prints |func| to |str|. Returns |str|. +std::ostream& operator<<(std::ostream& str, const Function& func); + +inline Function::Function(std::unique_ptr def_inst) + : def_inst_(std::move(def_inst)), end_inst_() {} + +inline void Function::AddParameter(std::unique_ptr p) { + params_.emplace_back(std::move(p)); +} + +inline void Function::AddDebugInstructionInHeader( + std::unique_ptr p) { + debug_insts_in_header_.push_back(std::move(p)); +} + +inline void Function::AddBasicBlock(std::unique_ptr b) { + AddBasicBlock(std::move(b), end()); +} + +inline void Function::AddBasicBlock(std::unique_ptr b, + iterator ip) { + ip.InsertBefore(std::move(b)); +} + +template +inline void Function::AddBasicBlocks(T src_begin, T src_end, iterator ip) { + blocks_.insert(ip.Get(), std::make_move_iterator(src_begin), + std::make_move_iterator(src_end)); +} + +inline void Function::MoveBasicBlockToAfter(uint32_t id, BasicBlock* ip) { + std::unique_ptr block_to_move = std::move(*FindBlock(id).Get()); + blocks_.erase(std::find(std::begin(blocks_), std::end(blocks_), nullptr)); + + assert(block_to_move->GetParent() == ip->GetParent() && + "Both blocks have to be in the same function."); + + InsertBasicBlockAfter(std::move(block_to_move), ip); +} + +inline void Function::RemoveEmptyBlocks() { + auto first_empty = + std::remove_if(std::begin(blocks_), std::end(blocks_), + [](const std::unique_ptr& bb) -> bool { + return bb->GetLabelInst()->opcode() == SpvOpNop; + }); + blocks_.erase(first_empty, std::end(blocks_)); +} + +inline void Function::RemoveParameter(uint32_t id) { + params_.erase(std::remove_if(params_.begin(), params_.end(), + [id](const std::unique_ptr& param) { + return param->result_id() == id; + }), + params_.end()); +} + +inline void Function::SetFunctionEnd(std::unique_ptr end_inst) { + end_inst_ = std::move(end_inst); +} + +inline void Function::AddNonSemanticInstruction( + std::unique_ptr non_semantic) { + non_semantic_.emplace_back(std::move(non_semantic)); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_FUNCTION_H_ diff --git a/third_party/spirv-tools/source/opt/generate_webgpu_initializers_pass.cpp b/third_party/spirv-tools/source/opt/generate_webgpu_initializers_pass.cpp new file mode 100644 index 0000000..eaed3c2 --- /dev/null +++ b/third_party/spirv-tools/source/opt/generate_webgpu_initializers_pass.cpp @@ -0,0 +1,116 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/generate_webgpu_initializers_pass.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +using inst_iterator = InstructionList::iterator; + +namespace { + +bool NeedsWebGPUInitializer(Instruction* inst) { + if (inst->opcode() != SpvOpVariable) return false; + + auto storage_class = inst->GetSingleWordOperand(2); + if (storage_class != SpvStorageClassOutput && + storage_class != SpvStorageClassPrivate && + storage_class != SpvStorageClassFunction) { + return false; + } + + if (inst->NumOperands() > 3) return false; + + return true; +} + +} // namespace + +Pass::Status GenerateWebGPUInitializersPass::Process() { + auto* module = context()->module(); + bool changed = false; + + // Handle global/module scoped variables + for (auto iter = module->types_values_begin(); + iter != module->types_values_end(); ++iter) { + Instruction* inst = &(*iter); + + if (inst->opcode() == SpvOpConstantNull) { + null_constant_type_map_[inst->type_id()] = inst; + seen_null_constants_.insert(inst); + continue; + } + + if (!NeedsWebGPUInitializer(inst)) continue; + + changed = true; + + auto* constant_inst = GetNullConstantForVariable(inst); + if (!constant_inst) return Status::Failure; + + if (seen_null_constants_.find(constant_inst) == + seen_null_constants_.end()) { + constant_inst->InsertBefore(inst); + null_constant_type_map_[inst->type_id()] = inst; + seen_null_constants_.insert(inst); + } + AddNullInitializerToVariable(constant_inst, inst); + } + + // Handle local/function scoped variables + for (auto func = module->begin(); func != module->end(); ++func) { + auto block = func->entry().get(); + for (auto iter = block->begin(); + iter != block->end() && iter->opcode() == SpvOpVariable; ++iter) { + Instruction* inst = &(*iter); + if (!NeedsWebGPUInitializer(inst)) continue; + + changed = true; + auto* constant_inst = GetNullConstantForVariable(inst); + if (!constant_inst) return Status::Failure; + + AddNullInitializerToVariable(constant_inst, inst); + } + } + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Instruction* GenerateWebGPUInitializersPass::GetNullConstantForVariable( + Instruction* variable_inst) { + auto constant_mgr = context()->get_constant_mgr(); + auto* def_use_mgr = get_def_use_mgr(); + + auto* ptr_inst = def_use_mgr->GetDef(variable_inst->type_id()); + auto type_id = ptr_inst->GetInOperand(1).words[0]; + if (null_constant_type_map_.find(type_id) == null_constant_type_map_.end()) { + auto* constant_type = context()->get_type_mgr()->GetType(type_id); + auto* constant = constant_mgr->GetConstant(constant_type, {}); + return constant_mgr->GetDefiningInstruction(constant, type_id); + } else { + return null_constant_type_map_[type_id]; + } +} + +void GenerateWebGPUInitializersPass::AddNullInitializerToVariable( + Instruction* constant_inst, Instruction* variable_inst) { + auto constant_id = constant_inst->result_id(); + variable_inst->AddOperand(Operand(SPV_OPERAND_TYPE_ID, {constant_id})); + get_def_use_mgr()->AnalyzeInstUse(variable_inst); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/generate_webgpu_initializers_pass.h b/third_party/spirv-tools/source/opt/generate_webgpu_initializers_pass.h new file mode 100644 index 0000000..f95e84c --- /dev/null +++ b/third_party/spirv-tools/source/opt/generate_webgpu_initializers_pass.h @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_GENERATE_WEBGPU_INITIALIZERS_PASS_H_ +#define SOURCE_OPT_GENERATE_WEBGPU_INITIALIZERS_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Adds initializers to variables with storage classes Output, Private, and +// Function if they are missing. In the WebGPU environment these storage classes +// require that the variables are initialized. Currently they are initialized to +// NULL, though in the future some of them may be initialized to the first value +// that is stored in them, if that was a constant. +class GenerateWebGPUInitializersPass : public Pass { + public: + const char* name() const override { return "generate-webgpu-initializers"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } + + private: + using NullConstantTypeMap = std::unordered_map; + NullConstantTypeMap null_constant_type_map_; + std::unordered_set seen_null_constants_; + + Instruction* GetNullConstantForVariable(Instruction* variable_inst); + void AddNullInitializerToVariable(Instruction* constant_inst, + Instruction* variable_inst); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_GENERATE_WEBGPU_INITIALIZERS_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/graphics_robust_access_pass.cpp b/third_party/spirv-tools/source/opt/graphics_robust_access_pass.cpp new file mode 100644 index 0000000..46483e4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/graphics_robust_access_pass.cpp @@ -0,0 +1,1061 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This pass injects code in a graphics shader to implement guarantees +// satisfying Vulkan's robustBufferAcces rules. Robust access rules permit +// an out-of-bounds access to be redirected to an access of the same type +// (load, store, etc.) but within the same root object. +// +// We assume baseline functionality in Vulkan, i.e. the module uses +// logical addressing mode, without VK_KHR_variable_pointers. +// +// - Logical addressing mode implies: +// - Each root pointer (a pointer that exists other than by the +// execution of a shader instruction) is the result of an OpVariable. +// +// - Instructions that result in pointers are: +// OpVariable +// OpAccessChain +// OpInBoundsAccessChain +// OpFunctionParameter +// OpImageTexelPointer +// OpCopyObject +// +// - Instructions that use a pointer are: +// OpLoad +// OpStore +// OpAccessChain +// OpInBoundsAccessChain +// OpFunctionCall +// OpImageTexelPointer +// OpCopyMemory +// OpCopyObject +// all OpAtomic* instructions +// +// We classify pointer-users into: +// - Accesses: +// - OpLoad +// - OpStore +// - OpAtomic* +// - OpCopyMemory +// +// - Address calculations: +// - OpAccessChain +// - OpInBoundsAccessChain +// +// - Pass-through: +// - OpFunctionCall +// - OpFunctionParameter +// - OpCopyObject +// +// The strategy is: +// +// - Handle only logical addressing mode. In particular, don't handle a module +// if it uses one of the variable-pointers capabilities. +// +// - Don't handle modules using capability RuntimeDescriptorArrayEXT. So the +// only runtime arrays are those that are the last member in a +// Block-decorated struct. This allows us to feasibly/easily compute the +// length of the runtime array. See below. +// +// - The memory locations accessed by OpLoad, OpStore, OpCopyMemory, and +// OpAtomic* are determined by their pointer parameter or parameters. +// Pointers are always (correctly) typed and so the address and number of +// consecutive locations are fully determined by the pointer. +// +// - A pointer value orginates as one of few cases: +// +// - OpVariable for an interface object or an array of them: image, +// buffer (UBO or SSBO), sampler, sampled-image, push-constant, input +// variable, output variable. The execution environment is responsible for +// allocating the correct amount of storage for these, and for ensuring +// each resource bound to such a variable is big enough to contain the +// SPIR-V pointee type of the variable. +// +// - OpVariable for a non-interface object. These are variables in +// Workgroup, Private, and Function storage classes. The compiler ensures +// the underlying allocation is big enough to store the entire SPIR-V +// pointee type of the variable. +// +// - An OpFunctionParameter. This always maps to a pointer parameter to an +// OpFunctionCall. +// +// - In logical addressing mode, these are severely limited: +// "Any pointer operand to an OpFunctionCall must be: +// - a memory object declaration, or +// - a pointer to an element in an array that is a memory object +// declaration, where the element type is OpTypeSampler or OpTypeImage" +// +// - This has an important simplifying consequence: +// +// - When looking for a pointer to the structure containing a runtime +// array, you begin with a pointer to the runtime array and trace +// backward in the function. You never have to trace back beyond +// your function call boundary. So you can't take a partial access +// chain into an SSBO, then pass that pointer into a function. So +// we don't resort to using fat pointers to compute array length. +// We can trace back to a pointer to the containing structure, +// and use that in an OpArrayLength instruction. (The structure type +// gives us the member index of the runtime array.) +// +// - Otherwise, the pointer type fully encodes the range of valid +// addresses. In particular, the type of a pointer to an aggregate +// value fully encodes the range of indices when indexing into +// that aggregate. +// +// - The pointer is the result of an access chain instruction. We clamp +// indices contributing to address calculations. As noted above, the +// valid ranges are either bound by the length of a runtime array, or +// by the type of the base pointer. The length of a runtime array is +// the result of an OpArrayLength instruction acting on the pointer of +// the containing structure as noted above. +// +// - Access chain indices are always treated as signed, so: +// - Clamp the upper bound at the signed integer maximum. +// - Use SClamp for all clamping. +// +// - TODO(dneto): OpImageTexelPointer: +// - Clamp coordinate to the image size returned by OpImageQuerySize +// - If multi-sampled, clamp the sample index to the count returned by +// OpImageQuerySamples. +// - If not multi-sampled, set the sample index to 0. +// +// - Rely on the external validator to check that pointers are only +// used by the instructions as above. +// +// - Handles OpTypeRuntimeArray +// Track pointer back to original resource (pointer to struct), so we can +// query the runtime array size. +// + +#include "graphics_robust_access_pass.h" + +#include +#include +#include +#include +#include +#include + +#include "constants.h" +#include "def_use_manager.h" +#include "function.h" +#include "ir_context.h" +#include "module.h" +#include "pass.h" +#include "source/diagnostic.h" +#include "source/util/make_unique.h" +#include "spirv-tools/libspirv.h" +#include "spirv/unified1/GLSL.std.450.h" +#include "spirv/unified1/spirv.h" +#include "type_manager.h" +#include "types.h" + +namespace spvtools { +namespace opt { + +using opt::Instruction; +using opt::Operand; +using spvtools::MakeUnique; + +GraphicsRobustAccessPass::GraphicsRobustAccessPass() : module_status_() {} + +Pass::Status GraphicsRobustAccessPass::Process() { + module_status_ = PerModuleState(); + + ProcessCurrentModule(); + + auto result = module_status_.failed + ? Status::Failure + : (module_status_.modified ? Status::SuccessWithChange + : Status::SuccessWithoutChange); + + return result; +} + +spvtools::DiagnosticStream GraphicsRobustAccessPass::Fail() { + module_status_.failed = true; + // We don't really have a position, and we'll ignore the result. + return std::move( + spvtools::DiagnosticStream({}, consumer(), "", SPV_ERROR_INVALID_BINARY) + << name() << ": "); +} + +spv_result_t GraphicsRobustAccessPass::IsCompatibleModule() { + auto* feature_mgr = context()->get_feature_mgr(); + if (!feature_mgr->HasCapability(SpvCapabilityShader)) + return Fail() << "Can only process Shader modules"; + if (feature_mgr->HasCapability(SpvCapabilityVariablePointers)) + return Fail() << "Can't process modules with VariablePointers capability"; + if (feature_mgr->HasCapability(SpvCapabilityVariablePointersStorageBuffer)) + return Fail() << "Can't process modules with VariablePointersStorageBuffer " + "capability"; + if (feature_mgr->HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) { + // These have a RuntimeArray outside of Block-decorated struct. There + // is no way to compute the array length from within SPIR-V. + return Fail() << "Can't process modules with RuntimeDescriptorArrayEXT " + "capability"; + } + + { + auto* inst = context()->module()->GetMemoryModel(); + const auto addressing_model = inst->GetSingleWordOperand(0); + if (addressing_model != SpvAddressingModelLogical) + return Fail() << "Addressing model must be Logical. Found " + << inst->PrettyPrint(); + } + return SPV_SUCCESS; +} + +spv_result_t GraphicsRobustAccessPass::ProcessCurrentModule() { + auto err = IsCompatibleModule(); + if (err != SPV_SUCCESS) return err; + + ProcessFunction fn = [this](opt::Function* f) { return ProcessAFunction(f); }; + module_status_.modified |= context()->ProcessReachableCallTree(fn); + + // Need something here. It's the price we pay for easier failure paths. + return SPV_SUCCESS; +} + +bool GraphicsRobustAccessPass::ProcessAFunction(opt::Function* function) { + // Ensure that all pointers computed inside a function are within bounds. + // Find the access chains in this block before trying to modify them. + std::vector access_chains; + std::vector image_texel_pointers; + for (auto& block : *function) { + for (auto& inst : block) { + switch (inst.opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + access_chains.push_back(&inst); + break; + case SpvOpImageTexelPointer: + image_texel_pointers.push_back(&inst); + break; + default: + break; + } + } + } + for (auto* inst : access_chains) { + ClampIndicesForAccessChain(inst); + if (module_status_.failed) return module_status_.modified; + } + + for (auto* inst : image_texel_pointers) { + if (SPV_SUCCESS != ClampCoordinateForImageTexelPointer(inst)) break; + } + return module_status_.modified; +} + +void GraphicsRobustAccessPass::ClampIndicesForAccessChain( + Instruction* access_chain) { + Instruction& inst = *access_chain; + + auto* constant_mgr = context()->get_constant_mgr(); + auto* def_use_mgr = context()->get_def_use_mgr(); + auto* type_mgr = context()->get_type_mgr(); + const bool have_int64_cap = + context()->get_feature_mgr()->HasCapability(SpvCapabilityInt64); + + // Replaces one of the OpAccessChain index operands with a new value. + // Updates def-use analysis. + auto replace_index = [&inst, def_use_mgr](uint32_t operand_index, + Instruction* new_value) { + inst.SetOperand(operand_index, {new_value->result_id()}); + def_use_mgr->AnalyzeInstUse(&inst); + return SPV_SUCCESS; + }; + + // Replaces one of the OpAccesssChain index operands with a clamped value. + // Replace the operand at |operand_index| with the value computed from + // signed_clamp(%old_value, %min_value, %max_value). It also analyzes + // the new instruction and records that them module is modified. + // Assumes %min_value is signed-less-or-equal than %max_value. (All callees + // use 0 for %min_value). + auto clamp_index = [&inst, type_mgr, this, &replace_index]( + uint32_t operand_index, Instruction* old_value, + Instruction* min_value, Instruction* max_value) { + auto* clamp_inst = + MakeSClampInst(*type_mgr, old_value, min_value, max_value, &inst); + return replace_index(operand_index, clamp_inst); + }; + + // Ensures the specified index of access chain |inst| has a value that is + // at most |count| - 1. If the index is already a constant value less than + // |count| then no change is made. + auto clamp_to_literal_count = + [&inst, this, &constant_mgr, &type_mgr, have_int64_cap, &replace_index, + &clamp_index](uint32_t operand_index, uint64_t count) -> spv_result_t { + Instruction* index_inst = + this->GetDef(inst.GetSingleWordOperand(operand_index)); + const auto* index_type = + type_mgr->GetType(index_inst->type_id())->AsInteger(); + assert(index_type); + const auto index_width = index_type->width(); + + if (count <= 1) { + // Replace the index with 0. + return replace_index(operand_index, GetValueForType(0, index_type)); + } + + uint64_t maxval = count - 1; + + // Compute the bit width of a viable type to hold |maxval|. + // Look for a bit width, up to 64 bits wide, to fit maxval. + uint32_t maxval_width = index_width; + while ((maxval_width < 64) && (0 != (maxval >> maxval_width))) { + maxval_width *= 2; + } + // Determine the type for |maxval|. + uint32_t next_id = context()->module()->IdBound(); + analysis::Integer signed_type_for_query(maxval_width, true); + auto* maxval_type = + type_mgr->GetRegisteredType(&signed_type_for_query)->AsInteger(); + if (next_id != context()->module()->IdBound()) { + module_status_.modified = true; + } + // Access chain indices are treated as signed, so limit the maximum value + // of the index so it will always be positive for a signed clamp operation. + maxval = std::min(maxval, ((uint64_t(1) << (maxval_width - 1)) - 1)); + + if (index_width > 64) { + return this->Fail() << "Can't handle indices wider than 64 bits, found " + "constant index with " + << index_width << " bits as index number " + << operand_index << " of access chain " + << inst.PrettyPrint(); + } + + // Split into two cases: the current index is a constant, or not. + + // If the index is a constant then |index_constant| will not be a null + // pointer. (If index is an |OpConstantNull| then it |index_constant| will + // not be a null pointer.) Since access chain indices must be scalar + // integers, this can't be a spec constant. + if (auto* index_constant = constant_mgr->GetConstantFromInst(index_inst)) { + auto* int_index_constant = index_constant->AsIntConstant(); + int64_t value = 0; + // OpAccessChain indices are treated as signed. So get the signed + // constant value here. + if (index_width <= 32) { + value = int64_t(int_index_constant->GetS32BitValue()); + } else if (index_width <= 64) { + value = int_index_constant->GetS64BitValue(); + } + if (value < 0) { + return replace_index(operand_index, GetValueForType(0, index_type)); + } else if (uint64_t(value) <= maxval) { + // Nothing to do. + return SPV_SUCCESS; + } else { + // Replace with maxval. + assert(count > 0); // Already took care of this case above. + return replace_index(operand_index, + GetValueForType(maxval, maxval_type)); + } + } else { + // Generate a clamp instruction. + assert(maxval >= 1); + assert(index_width <= 64); // Otherwise, already returned above. + if (index_width >= 64 && !have_int64_cap) { + // An inconsistent module. + return Fail() << "Access chain index is wider than 64 bits, but Int64 " + "is not declared: " + << index_inst->PrettyPrint(); + } + // Widen the index value if necessary + if (maxval_width > index_width) { + // Find the wider type. We only need this case if a constant array + // bound is too big. + + // From how we calculated maxval_width, widening won't require adding + // the Int64 capability. + assert(have_int64_cap || maxval_width <= 32); + if (!have_int64_cap && maxval_width >= 64) { + // Be defensive, but this shouldn't happen. + return this->Fail() + << "Clamping index would require adding Int64 capability. " + << "Can't clamp 32-bit index " << operand_index + << " of access chain " << inst.PrettyPrint(); + } + index_inst = WidenInteger(index_type->IsSigned(), maxval_width, + index_inst, &inst); + } + + // Finally, clamp the index. + return clamp_index(operand_index, index_inst, + GetValueForType(0, maxval_type), + GetValueForType(maxval, maxval_type)); + } + return SPV_SUCCESS; + }; + + // Ensures the specified index of access chain |inst| has a value that is at + // most the value of |count_inst| minus 1, where |count_inst| is treated as an + // unsigned integer. This can log a failure. + auto clamp_to_count = [&inst, this, &constant_mgr, &clamp_to_literal_count, + &clamp_index, + &type_mgr](uint32_t operand_index, + Instruction* count_inst) -> spv_result_t { + Instruction* index_inst = + this->GetDef(inst.GetSingleWordOperand(operand_index)); + const auto* index_type = + type_mgr->GetType(index_inst->type_id())->AsInteger(); + const auto* count_type = + type_mgr->GetType(count_inst->type_id())->AsInteger(); + assert(index_type); + if (const auto* count_constant = + constant_mgr->GetConstantFromInst(count_inst)) { + uint64_t value = 0; + const auto width = count_constant->type()->AsInteger()->width(); + if (width <= 32) { + value = count_constant->AsIntConstant()->GetU32BitValue(); + } else if (width <= 64) { + value = count_constant->AsIntConstant()->GetU64BitValue(); + } else { + return this->Fail() << "Can't handle indices wider than 64 bits, found " + "constant index with " + << index_type->width() << "bits"; + } + return clamp_to_literal_count(operand_index, value); + } else { + // Widen them to the same width. + const auto index_width = index_type->width(); + const auto count_width = count_type->width(); + const auto target_width = std::max(index_width, count_width); + // UConvert requires the result type to have 0 signedness. So enforce + // that here. + auto* wider_type = index_width < count_width ? count_type : index_type; + if (index_type->width() < target_width) { + // Access chain indices are treated as signed integers. + index_inst = WidenInteger(true, target_width, index_inst, &inst); + } else if (count_type->width() < target_width) { + // Assume type sizes are treated as unsigned. + count_inst = WidenInteger(false, target_width, count_inst, &inst); + } + // Compute count - 1. + // It doesn't matter if 1 is signed or unsigned. + auto* one = GetValueForType(1, wider_type); + auto* count_minus_1 = InsertInst( + &inst, SpvOpISub, type_mgr->GetId(wider_type), TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {count_inst->result_id()}}, + {SPV_OPERAND_TYPE_ID, {one->result_id()}}}); + auto* zero = GetValueForType(0, wider_type); + // Make sure we clamp to an upper bound that is at most the signed max + // for the target type. + const uint64_t max_signed_value = + ((uint64_t(1) << (target_width - 1)) - 1); + // Use unsigned-min to ensure that the result is always non-negative. + // That ensures we satisfy the invariant for SClamp, where the "min" + // argument we give it (zero), is no larger than the third argument. + auto* upper_bound = + MakeUMinInst(*type_mgr, count_minus_1, + GetValueForType(max_signed_value, wider_type), &inst); + // Now clamp the index to this upper bound. + return clamp_index(operand_index, index_inst, zero, upper_bound); + } + return SPV_SUCCESS; + }; + + const Instruction* base_inst = GetDef(inst.GetSingleWordInOperand(0)); + const Instruction* base_type = GetDef(base_inst->type_id()); + Instruction* pointee_type = GetDef(base_type->GetSingleWordInOperand(1)); + + // Walk the indices from earliest to latest, replacing indices with a + // clamped value, and updating the pointee_type. The order matters for + // the case when we have to compute the length of a runtime array. In + // that the algorithm relies on the fact that that the earlier indices + // have already been clamped. + const uint32_t num_operands = inst.NumOperands(); + for (uint32_t idx = 3; !module_status_.failed && idx < num_operands; ++idx) { + const uint32_t index_id = inst.GetSingleWordOperand(idx); + Instruction* index_inst = GetDef(index_id); + + switch (pointee_type->opcode()) { + case SpvOpTypeMatrix: // Use column count + case SpvOpTypeVector: // Use component count + { + const uint32_t count = pointee_type->GetSingleWordOperand(2); + clamp_to_literal_count(idx, count); + pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); + } break; + + case SpvOpTypeArray: { + // The array length can be a spec constant, so go through the general + // case. + Instruction* array_len = GetDef(pointee_type->GetSingleWordOperand(2)); + clamp_to_count(idx, array_len); + pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); + } break; + + case SpvOpTypeStruct: { + // SPIR-V requires the index to be an OpConstant. + // We need to know the index literal value so we can compute the next + // pointee type. + if (index_inst->opcode() != SpvOpConstant || + !constant_mgr->GetConstantFromInst(index_inst) + ->type() + ->AsInteger()) { + Fail() << "Member index into struct is not a constant integer: " + << index_inst->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << "\nin access chain: " + << inst.PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + return; + } + const auto num_members = pointee_type->NumInOperands(); + const auto* index_constant = + constant_mgr->GetConstantFromInst(index_inst); + // Get the sign-extended value, since access index is always treated as + // signed. + const auto index_value = index_constant->GetSignExtendedValue(); + if (index_value < 0 || index_value >= num_members) { + Fail() << "Member index " << index_value + << " is out of bounds for struct type: " + << pointee_type->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << "\nin access chain: " + << inst.PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + return; + } + pointee_type = GetDef(pointee_type->GetSingleWordInOperand( + static_cast(index_value))); + // No need to clamp this index. We just checked that it's valid. + } break; + + case SpvOpTypeRuntimeArray: { + auto* array_len = MakeRuntimeArrayLengthInst(&inst, idx); + if (!array_len) { // We've already signaled an error. + return; + } + clamp_to_count(idx, array_len); + if (module_status_.failed) return; + pointee_type = GetDef(pointee_type->GetSingleWordOperand(1)); + } break; + + default: + Fail() << " Unhandled pointee type for access chain " + << pointee_type->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + } + } +} + +uint32_t GraphicsRobustAccessPass::GetGlslInsts() { + if (module_status_.glsl_insts_id == 0) { + // This string serves double-duty as raw data for a string and for a vector + // of 32-bit words + const char glsl[] = "GLSL.std.450\0\0\0\0"; + const size_t glsl_str_byte_len = 16; + // Use an existing import if we can. + for (auto& inst : context()->module()->ext_inst_imports()) { + const auto& name_words = inst.GetInOperand(0).words; + if (0 == std::strncmp(reinterpret_cast(name_words.data()), + glsl, glsl_str_byte_len)) { + module_status_.glsl_insts_id = inst.result_id(); + } + } + if (module_status_.glsl_insts_id == 0) { + // Make a new import instruction. + module_status_.glsl_insts_id = TakeNextId(); + std::vector words(glsl_str_byte_len / sizeof(uint32_t)); + std::memcpy(words.data(), glsl, glsl_str_byte_len); + auto import_inst = MakeUnique( + context(), SpvOpExtInstImport, 0, module_status_.glsl_insts_id, + std::initializer_list{ + Operand{SPV_OPERAND_TYPE_LITERAL_STRING, std::move(words)}}); + Instruction* inst = import_inst.get(); + context()->module()->AddExtInstImport(std::move(import_inst)); + module_status_.modified = true; + context()->AnalyzeDefUse(inst); + // Reanalyze the feature list, since we added an extended instruction + // set improt. + context()->get_feature_mgr()->Analyze(context()->module()); + } + } + return module_status_.glsl_insts_id; +} + +opt::Instruction* opt::GraphicsRobustAccessPass::GetValueForType( + uint64_t value, const analysis::Integer* type) { + auto* mgr = context()->get_constant_mgr(); + assert(type->width() <= 64); + std::vector words; + words.push_back(uint32_t(value)); + if (type->width() > 32) { + words.push_back(uint32_t(value >> 32u)); + } + const auto* constant = mgr->GetConstant(type, words); + return mgr->GetDefiningInstruction( + constant, context()->get_type_mgr()->GetTypeInstruction(type)); +} + +opt::Instruction* opt::GraphicsRobustAccessPass::WidenInteger( + bool sign_extend, uint32_t bit_width, Instruction* value, + Instruction* before_inst) { + analysis::Integer unsigned_type_for_query(bit_width, false); + auto* type_mgr = context()->get_type_mgr(); + auto* unsigned_type = type_mgr->GetRegisteredType(&unsigned_type_for_query); + auto type_id = context()->get_type_mgr()->GetId(unsigned_type); + auto conversion_id = TakeNextId(); + auto* conversion = InsertInst( + before_inst, (sign_extend ? SpvOpSConvert : SpvOpUConvert), type_id, + conversion_id, {{SPV_OPERAND_TYPE_ID, {value->result_id()}}}); + return conversion; +} + +Instruction* GraphicsRobustAccessPass::MakeUMinInst( + const analysis::TypeManager& tm, Instruction* x, Instruction* y, + Instruction* where) { + // Get IDs of instructions we'll be referencing. Evaluate them before calling + // the function so we force a deterministic ordering in case both of them need + // to take a new ID. + const uint32_t glsl_insts_id = GetGlslInsts(); + uint32_t smin_id = TakeNextId(); + const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width(); + const auto ywidth = tm.GetType(y->type_id())->AsInteger()->width(); + assert(xwidth == ywidth); + (void)xwidth; + (void)ywidth; + auto* smin_inst = InsertInst( + where, SpvOpExtInst, x->type_id(), smin_id, + { + {SPV_OPERAND_TYPE_ID, {glsl_insts_id}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450UMin}}, + {SPV_OPERAND_TYPE_ID, {x->result_id()}}, + {SPV_OPERAND_TYPE_ID, {y->result_id()}}, + }); + return smin_inst; +} + +Instruction* GraphicsRobustAccessPass::MakeSClampInst( + const analysis::TypeManager& tm, Instruction* x, Instruction* min, + Instruction* max, Instruction* where) { + // Get IDs of instructions we'll be referencing. Evaluate them before calling + // the function so we force a deterministic ordering in case both of them need + // to take a new ID. + const uint32_t glsl_insts_id = GetGlslInsts(); + uint32_t clamp_id = TakeNextId(); + const auto xwidth = tm.GetType(x->type_id())->AsInteger()->width(); + const auto minwidth = tm.GetType(min->type_id())->AsInteger()->width(); + const auto maxwidth = tm.GetType(max->type_id())->AsInteger()->width(); + assert(xwidth == minwidth); + assert(xwidth == maxwidth); + (void)xwidth; + (void)minwidth; + (void)maxwidth; + auto* clamp_inst = InsertInst( + where, SpvOpExtInst, x->type_id(), clamp_id, + { + {SPV_OPERAND_TYPE_ID, {glsl_insts_id}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {GLSLstd450SClamp}}, + {SPV_OPERAND_TYPE_ID, {x->result_id()}}, + {SPV_OPERAND_TYPE_ID, {min->result_id()}}, + {SPV_OPERAND_TYPE_ID, {max->result_id()}}, + }); + return clamp_inst; +} + +Instruction* GraphicsRobustAccessPass::MakeRuntimeArrayLengthInst( + Instruction* access_chain, uint32_t operand_index) { + // The Index parameter to the access chain at |operand_index| is indexing + // *into* the runtime-array. To get the number of elements in the runtime + // array we need a pointer to the Block-decorated struct that contains the + // runtime array. So conceptually we have to go 2 steps backward in the + // access chain. The two steps backward might forces us to traverse backward + // across multiple dominating instructions. + auto* type_mgr = context()->get_type_mgr(); + + // How many access chain indices do we have to unwind to find the pointer + // to the struct containing the runtime array? + uint32_t steps_remaining = 2; + // Find or create an instruction computing the pointer to the structure + // containing the runtime array. + // Walk backward through pointer address calculations until we either get + // to exactly the right base pointer, or to an access chain instruction + // that we can replicate but truncate to compute the address of the right + // struct. + Instruction* current_access_chain = access_chain; + Instruction* pointer_to_containing_struct = nullptr; + while (steps_remaining > 0) { + switch (current_access_chain->opcode()) { + case SpvOpCopyObject: + // Whoops. Walk right through this one. + current_access_chain = + GetDef(current_access_chain->GetSingleWordInOperand(0)); + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: { + const int first_index_operand = 3; + // How many indices in this access chain contribute to getting us + // to an element in the runtime array? + const auto num_contributing_indices = + current_access_chain == access_chain + ? operand_index - (first_index_operand - 1) + : current_access_chain->NumInOperands() - 1 /* skip the base */; + Instruction* base = + GetDef(current_access_chain->GetSingleWordInOperand(0)); + if (num_contributing_indices == steps_remaining) { + // The base pointer points to the structure. + pointer_to_containing_struct = base; + steps_remaining = 0; + break; + } else if (num_contributing_indices < steps_remaining) { + // Peel off the index and keep going backward. + steps_remaining -= num_contributing_indices; + current_access_chain = base; + } else { + // This access chain has more indices than needed. Generate a new + // access chain instruction, but truncating the list of indices. + const int base_operand = 2; + // We'll use the base pointer and the indices up to but not including + // the one indexing into the runtime array. + Instruction::OperandList ops; + // Use the base pointer + ops.push_back(current_access_chain->GetOperand(base_operand)); + const uint32_t num_indices_to_keep = + num_contributing_indices - steps_remaining - 1; + for (uint32_t i = 0; i <= num_indices_to_keep; i++) { + ops.push_back( + current_access_chain->GetOperand(first_index_operand + i)); + } + // Compute the type of the result of the new access chain. Start at + // the base and walk the indices in a forward direction. + auto* constant_mgr = context()->get_constant_mgr(); + std::vector indices_for_type; + for (uint32_t i = 0; i < ops.size() - 1; i++) { + uint32_t index_for_type_calculation = 0; + Instruction* index = + GetDef(current_access_chain->GetSingleWordOperand( + first_index_operand + i)); + if (auto* index_constant = + constant_mgr->GetConstantFromInst(index)) { + // We only need 32 bits. For the type calculation, it's sufficient + // to take the zero-extended value. It only matters for the struct + // case, and struct member indices are unsigned. + index_for_type_calculation = + uint32_t(index_constant->GetZeroExtendedValue()); + } else { + // Indexing into a variably-sized thing like an array. Use 0. + index_for_type_calculation = 0; + } + indices_for_type.push_back(index_for_type_calculation); + } + auto* base_ptr_type = type_mgr->GetType(base->type_id())->AsPointer(); + auto* base_pointee_type = base_ptr_type->pointee_type(); + auto* new_access_chain_result_pointee_type = + type_mgr->GetMemberType(base_pointee_type, indices_for_type); + const uint32_t new_access_chain_type_id = type_mgr->FindPointerToType( + type_mgr->GetId(new_access_chain_result_pointee_type), + base_ptr_type->storage_class()); + + // Create the instruction and insert it. + const auto new_access_chain_id = TakeNextId(); + auto* new_access_chain = + InsertInst(current_access_chain, current_access_chain->opcode(), + new_access_chain_type_id, new_access_chain_id, ops); + pointer_to_containing_struct = new_access_chain; + steps_remaining = 0; + break; + } + } break; + default: + Fail() << "Unhandled access chain in logical addressing mode passes " + "through " + << current_access_chain->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + return nullptr; + } + } + assert(pointer_to_containing_struct); + auto* pointee_type = + type_mgr->GetType(pointer_to_containing_struct->type_id()) + ->AsPointer() + ->pointee_type(); + + auto* struct_type = pointee_type->AsStruct(); + const uint32_t member_index_of_runtime_array = + uint32_t(struct_type->element_types().size() - 1); + // Create the length-of-array instruction before the original access chain, + // but after the generation of the pointer to the struct. + const auto array_len_id = TakeNextId(); + analysis::Integer uint_type_for_query(32, false); + auto* uint_type = type_mgr->GetRegisteredType(&uint_type_for_query); + auto* array_len = InsertInst( + access_chain, SpvOpArrayLength, type_mgr->GetId(uint_type), array_len_id, + {{SPV_OPERAND_TYPE_ID, {pointer_to_containing_struct->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {member_index_of_runtime_array}}}); + return array_len; +} + +spv_result_t GraphicsRobustAccessPass::ClampCoordinateForImageTexelPointer( + opt::Instruction* image_texel_pointer) { + // TODO(dneto): Write tests for this code. + // TODO(dneto): Use signed-clamp + (void)(image_texel_pointer); + return SPV_SUCCESS; + + // Do not compile this code until it is ready to be used. +#if 0 + // Example: + // %texel_ptr = OpImageTexelPointer %texel_ptr_type %image_ptr %coord + // %sample + // + // We want to clamp %coord components between vector-0 and the result + // of OpImageQuerySize acting on the underlying image. So insert: + // %image = OpLoad %image_type %image_ptr + // %query_size = OpImageQuerySize %query_size_type %image + // + // For a multi-sampled image, %sample is the sample index, and we need + // to clamp it between zero and the number of samples in the image. + // %sample_count = OpImageQuerySamples %uint %image + // %max_sample_index = OpISub %uint %sample_count %uint_1 + // For non-multi-sampled images, the sample index must be constant zero. + + auto* def_use_mgr = context()->get_def_use_mgr(); + auto* type_mgr = context()->get_type_mgr(); + auto* constant_mgr = context()->get_constant_mgr(); + + auto* image_ptr = GetDef(image_texel_pointer->GetSingleWordInOperand(0)); + auto* image_ptr_type = GetDef(image_ptr->type_id()); + auto image_type_id = image_ptr_type->GetSingleWordInOperand(1); + auto* image_type = GetDef(image_type_id); + auto* coord = GetDef(image_texel_pointer->GetSingleWordInOperand(1)); + auto* samples = GetDef(image_texel_pointer->GetSingleWordInOperand(2)); + + // We will modify the module, at least by adding image query instructions. + module_status_.modified = true; + + // Declare the ImageQuery capability if the module doesn't already have it. + auto* feature_mgr = context()->get_feature_mgr(); + if (!feature_mgr->HasCapability(SpvCapabilityImageQuery)) { + auto cap = MakeUnique( + context(), SpvOpCapability, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityImageQuery}}}); + def_use_mgr->AnalyzeInstDefUse(cap.get()); + context()->AddCapability(std::move(cap)); + feature_mgr->Analyze(context()->module()); + } + + // OpImageTexelPointer is used to translate a coordinate and sample index + // into an address for use with an atomic operation. That is, it may only + // used with what Vulkan calls a "storage image" + // (OpTypeImage parameter Sampled=2). + // Note: A storage image never has a level-of-detail associated with it. + + // Constraints on the sample id: + // - Only 2D images can be multi-sampled: OpTypeImage parameter MS=1 + // only if Dim=2D. + // - Non-multi-sampled images (OpTypeImage parameter MS=0) must use + // sample ID to a constant 0. + + // The coordinate is treated as unsigned, and should be clamped against the + // image "size", returned by OpImageQuerySize. (Note: OpImageQuerySizeLod + // is only usable with a sampled image, i.e. its image type has Sampled=1). + + // Determine the result type for the OpImageQuerySize. + // For non-arrayed images: + // non-Cube: + // - Always the same as the coordinate type + // Cube: + // - Use all but the last component of the coordinate (which is the face + // index from 0 to 5). + // For arrayed images (in Vulkan the Dim is 1D, 2D, or Cube): + // non-Cube: + // - A vector with the components in the coordinate, and one more for + // the layer index. + // Cube: + // - The same as the coordinate type: 3-element integer vector. + // - The third component from the size query is the layer count. + // - The third component in the texel pointer calculation is + // 6 * layer + face, where 0 <= face < 6. + // Cube: Use all but the last component of the coordinate (which is the face + // index from 0 to 5). + const auto dim = SpvDim(image_type->GetSingleWordInOperand(1)); + const bool arrayed = image_type->GetSingleWordInOperand(3) == 1; + const bool multisampled = image_type->GetSingleWordInOperand(4) != 0; + const auto query_num_components = [dim, arrayed, this]() -> int { + const int arrayness_bonus = arrayed ? 1 : 0; + int num_coords = 0; + switch (dim) { + case SpvDimBuffer: + case SpvDim1D: + num_coords = 1; + break; + case SpvDimCube: + // For cube, we need bounds for x, y, but not face. + case SpvDimRect: + case SpvDim2D: + num_coords = 2; + break; + case SpvDim3D: + num_coords = 3; + break; + case SpvDimSubpassData: + case SpvDimMax: + return Fail() << "Invalid image dimension for OpImageTexelPointer: " + << int(dim); + break; + } + return num_coords + arrayness_bonus; + }(); + const auto* coord_component_type = [type_mgr, coord]() { + const analysis::Type* coord_type = type_mgr->GetType(coord->type_id()); + if (auto* vector_type = coord_type->AsVector()) { + return vector_type->element_type()->AsInteger(); + } + return coord_type->AsInteger(); + }(); + // For now, only handle 32-bit case for coordinates. + if (!coord_component_type) { + return Fail() << " Coordinates for OpImageTexelPointer are not integral: " + << image_texel_pointer->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + } + if (coord_component_type->width() != 32) { + return Fail() << " Expected OpImageTexelPointer coordinate components to " + "be 32-bits wide. They are " + << coord_component_type->width() << " bits. " + << image_texel_pointer->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + } + const auto* query_size_type = + [type_mgr, coord_component_type, + query_num_components]() -> const analysis::Type* { + if (query_num_components == 1) return coord_component_type; + analysis::Vector proposed(coord_component_type, query_num_components); + return type_mgr->GetRegisteredType(&proposed); + }(); + + const uint32_t image_id = TakeNextId(); + auto* image = + InsertInst(image_texel_pointer, SpvOpLoad, image_type_id, image_id, + {{SPV_OPERAND_TYPE_ID, {image_ptr->result_id()}}}); + + const uint32_t query_size_id = TakeNextId(); + auto* query_size = + InsertInst(image_texel_pointer, SpvOpImageQuerySize, + type_mgr->GetTypeInstruction(query_size_type), query_size_id, + {{SPV_OPERAND_TYPE_ID, {image->result_id()}}}); + + auto* component_1 = constant_mgr->GetConstant(coord_component_type, {1}); + const uint32_t component_1_id = + constant_mgr->GetDefiningInstruction(component_1)->result_id(); + auto* component_0 = constant_mgr->GetConstant(coord_component_type, {0}); + const uint32_t component_0_id = + constant_mgr->GetDefiningInstruction(component_0)->result_id(); + + // If the image is a cube array, then the last component of the queried + // size is the layer count. In the query, we have to accomodate folding + // in the face index ranging from 0 through 5. The inclusive upper bound + // on the third coordinate therefore is multiplied by 6. + auto* query_size_including_faces = query_size; + if (arrayed && (dim == SpvDimCube)) { + // Multiply the last coordinate by 6. + auto* component_6 = constant_mgr->GetConstant(coord_component_type, {6}); + const uint32_t component_6_id = + constant_mgr->GetDefiningInstruction(component_6)->result_id(); + assert(query_num_components == 3); + auto* multiplicand = constant_mgr->GetConstant( + query_size_type, {component_1_id, component_1_id, component_6_id}); + auto* multiplicand_inst = + constant_mgr->GetDefiningInstruction(multiplicand); + const auto query_size_including_faces_id = TakeNextId(); + query_size_including_faces = InsertInst( + image_texel_pointer, SpvOpIMul, + type_mgr->GetTypeInstruction(query_size_type), + query_size_including_faces_id, + {{SPV_OPERAND_TYPE_ID, {query_size_including_faces->result_id()}}, + {SPV_OPERAND_TYPE_ID, {multiplicand_inst->result_id()}}}); + } + + // Make a coordinate-type with all 1 components. + auto* coordinate_1 = + query_num_components == 1 + ? component_1 + : constant_mgr->GetConstant( + query_size_type, + std::vector(query_num_components, component_1_id)); + // Make a coordinate-type with all 1 components. + auto* coordinate_0 = + query_num_components == 0 + ? component_0 + : constant_mgr->GetConstant( + query_size_type, + std::vector(query_num_components, component_0_id)); + + const uint32_t query_max_including_faces_id = TakeNextId(); + auto* query_max_including_faces = InsertInst( + image_texel_pointer, SpvOpISub, + type_mgr->GetTypeInstruction(query_size_type), + query_max_including_faces_id, + {{SPV_OPERAND_TYPE_ID, {query_size_including_faces->result_id()}}, + {SPV_OPERAND_TYPE_ID, + {constant_mgr->GetDefiningInstruction(coordinate_1)->result_id()}}}); + + // Clamp the coordinate + auto* clamp_coord = MakeSClampInst( + *type_mgr, coord, constant_mgr->GetDefiningInstruction(coordinate_0), + query_max_including_faces, image_texel_pointer); + image_texel_pointer->SetInOperand(1, {clamp_coord->result_id()}); + + // Clamp the sample index + if (multisampled) { + // Get the sample count via OpImageQuerySamples + const auto query_samples_id = TakeNextId(); + auto* query_samples = InsertInst( + image_texel_pointer, SpvOpImageQuerySamples, + constant_mgr->GetDefiningInstruction(component_0)->type_id(), + query_samples_id, {{SPV_OPERAND_TYPE_ID, {image->result_id()}}}); + + const auto max_samples_id = TakeNextId(); + auto* max_samples = InsertInst(image_texel_pointer, SpvOpImageQuerySamples, + query_samples->type_id(), max_samples_id, + {{SPV_OPERAND_TYPE_ID, {query_samples_id}}, + {SPV_OPERAND_TYPE_ID, {component_1_id}}}); + + auto* clamp_samples = MakeSClampInst( + *type_mgr, samples, constant_mgr->GetDefiningInstruction(coordinate_0), + max_samples, image_texel_pointer); + image_texel_pointer->SetInOperand(2, {clamp_samples->result_id()}); + + } else { + // Just replace it with 0. Don't even check what was there before. + image_texel_pointer->SetInOperand(2, {component_0_id}); + } + + def_use_mgr->AnalyzeInstUse(image_texel_pointer); + + return SPV_SUCCESS; +#endif +} + +opt::Instruction* GraphicsRobustAccessPass::InsertInst( + opt::Instruction* where_inst, SpvOp opcode, uint32_t type_id, + uint32_t result_id, const Instruction::OperandList& operands) { + module_status_.modified = true; + auto* result = where_inst->InsertBefore( + MakeUnique(context(), opcode, type_id, result_id, operands)); + context()->get_def_use_mgr()->AnalyzeInstDefUse(result); + auto* basic_block = context()->get_instr_block(where_inst); + context()->set_instr_block(result, basic_block); + return result; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/graphics_robust_access_pass.h b/third_party/spirv-tools/source/opt/graphics_robust_access_pass.h new file mode 100644 index 0000000..6fc692c --- /dev/null +++ b/third_party/spirv-tools/source/opt/graphics_robust_access_pass.h @@ -0,0 +1,156 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_GRAPHICS_ROBUST_ACCESS_PASS_H_ +#define SOURCE_OPT_GRAPHICS_ROBUST_ACCESS_PASS_H_ + +#include +#include + +#include "constants.h" +#include "def_use_manager.h" +#include "instruction.h" +#include "module.h" +#include "pass.h" +#include "source/diagnostic.h" +#include "type_manager.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class GraphicsRobustAccessPass : public Pass { + public: + GraphicsRobustAccessPass(); + const char* name() const override { return "graphics-robust-access"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes | + IRContext::kAnalysisIdToFuncMapping; + } + + private: + // Records failure for the current module, and returns a stream + // that can be used to provide user error information to the message + // consumer. + spvtools::DiagnosticStream Fail(); + + // Returns SPV_SUCCESS if this pass can correctly process the module, + // as far as we can tell from capabilities and the memory model. + // Otherwise logs a message and returns a failure code. + spv_result_t IsCompatibleModule(); + + // Transform the current module, if possible. Failure and modification + // status is recorded in the |_| member. On failure, error information is + // posted to the message consumer. The return value has no significance. + spv_result_t ProcessCurrentModule(); + + // Process the given function. Updates the state value |_|. Returns true + // if the module was modified. This can log a failure. + bool ProcessAFunction(opt::Function*); + + // Clamps indices in the OpAccessChain or OpInBoundsAccessChain instruction + // |access_chain|. Inserts instructions before the given instruction. Updates + // analyses and records that the module is modified. This can log a failure. + void ClampIndicesForAccessChain(Instruction* access_chain); + + // Returns the id of the instruction importing the "GLSL.std.450" extended + // instruction set. If it does not yet exist, the import instruction is + // created and inserted into the module, and updates |_.modified| and + // |_.glsl_insts_id|. + uint32_t GetGlslInsts(); + + // Returns an instruction which is constant with the given value of the given + // type. Ignores any value bits beyond the width of the type. + Instruction* GetValueForType(uint64_t value, const analysis::Integer* type); + + // Converts an integer value to an unsigned wider integer type, using either + // sign extension or zero extension. The new instruction is inserted + // immediately before |before_inst|, and is analyzed for definitions and uses. + // Returns the newly inserted instruction. Assumes the |value| is an integer + // scalar of a narrower type than |bit_width| bits. + Instruction* WidenInteger(bool sign_extend, uint32_t bit_width, + Instruction* value, Instruction* before_inst); + + // Returns a new instruction that invokes the UMin GLSL.std.450 extended + // instruction with the two given operands. That is, the result of the + // instruction is: + // - |x| if |x| is unsigned-less than |y| + // - |y| otherwise + // We assume that |x| and |y| are scalar integer types with the same + // width. The instruction is inserted before |where|. + opt::Instruction* MakeUMinInst(const analysis::TypeManager& tm, + Instruction* x, Instruction* y, + Instruction* where); + + // Returns a new instruction that invokes the SClamp GLSL.std.450 extended + // instruction with the three given operands. That is, the result of the + // instruction is: + // - |min| if |x| is signed-less than |min| + // - |max| if |x| is signed-more than |max| + // - |x| otherwise. + // We assume that |min| is signed-less-or-equal to |max|, and that the + // operands all have the same scalar integer type. The instruction is + // inserted before |where|. + opt::Instruction* MakeSClampInst(const analysis::TypeManager& tm, + Instruction* x, Instruction* min, + Instruction* max, Instruction* where); + + // Returns a new instruction which evaluates to the length the runtime array + // referenced by the access chain at the specfied index. The instruction is + // inserted before the access chain instruction. Returns a null pointer in + // some cases if assumptions are violated (rather than asserting out). + opt::Instruction* MakeRuntimeArrayLengthInst(Instruction* access_chain, + uint32_t operand_index); + + // Clamps the coordinate for an OpImageTexelPointer so it stays within + // the bounds of the size of the image. Updates analyses and records that + // the module is modified. Returns a status code to indicate success + // or failure. If assumptions are not met, returns an error status code + // and emits a diagnostic. + spv_result_t ClampCoordinateForImageTexelPointer( + opt::Instruction* image_texel_pointer); + + // Gets the instruction that defines the given id. + opt::Instruction* GetDef(uint32_t id) { + return context()->get_def_use_mgr()->GetDef(id); + } + + // Returns a new instruction inserted before |where_inst|, and created from + // the remaining arguments. Registers the definitions and uses of the new + // instruction and also records its block. + opt::Instruction* InsertInst(opt::Instruction* where_inst, SpvOp opcode, + uint32_t type_id, uint32_t result_id, + const Instruction::OperandList& operands); + + // State required for the current module. + struct PerModuleState { + // This pass modified the module. + bool modified = false; + // True if there is an error processing the current module, e.g. if + // preconditions are not met. + bool failed = false; + // The id of the GLSL.std.450 extended instruction set. Zero if it does + // not exist. + uint32_t glsl_insts_id = 0; + } module_status_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_GRAPHICS_ROBUST_ACCESS_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/if_conversion.cpp b/third_party/spirv-tools/source/opt/if_conversion.cpp new file mode 100644 index 0000000..4284069 --- /dev/null +++ b/third_party/spirv-tools/source/opt/if_conversion.cpp @@ -0,0 +1,286 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/if_conversion.h" + +#include +#include + +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +Pass::Status IfConversion::Process() { + if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) { + return Status::SuccessWithoutChange; + } + + const ValueNumberTable& vn_table = *context()->GetValueNumberTable(); + bool modified = false; + std::vector to_kill; + for (auto& func : *get_module()) { + DominatorAnalysis* dominators = context()->GetDominatorAnalysis(&func); + for (auto& block : func) { + // Check if it is possible for |block| to have phis that can be + // transformed. + BasicBlock* common = nullptr; + if (!CheckBlock(&block, dominators, &common)) continue; + + // Get an insertion point. + auto iter = block.begin(); + while (iter != block.end() && iter->opcode() == SpvOpPhi) { + ++iter; + } + + InstructionBuilder builder( + context(), &*iter, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + block.ForEachPhiInst([this, &builder, &modified, &common, &to_kill, + dominators, &block, &vn_table](Instruction* phi) { + // This phi is not compatible, but subsequent phis might be. + if (!CheckType(phi->type_id())) return; + + // We cannot transform cases where the phi is used by another phi in the + // same block due to instruction ordering restrictions. + // TODO(alan-baker): If all inappropriate uses could also be + // transformed, we could still remove this phi. + if (!CheckPhiUsers(phi, &block)) return; + + // Identify the incoming values associated with the true and false + // branches. If |then_block| dominates |inc0| or if the true edge + // branches straight to this block and |common| is |inc0|, then |inc0| + // is on the true branch. Otherwise the |inc1| is on the true branch. + BasicBlock* inc0 = GetIncomingBlock(phi, 0u); + Instruction* branch = common->terminator(); + uint32_t condition = branch->GetSingleWordInOperand(0u); + BasicBlock* then_block = GetBlock(branch->GetSingleWordInOperand(1u)); + Instruction* true_value = nullptr; + Instruction* false_value = nullptr; + if ((then_block == &block && inc0 == common) || + dominators->Dominates(then_block, inc0)) { + true_value = GetIncomingValue(phi, 0u); + false_value = GetIncomingValue(phi, 1u); + } else { + true_value = GetIncomingValue(phi, 1u); + false_value = GetIncomingValue(phi, 0u); + } + + BasicBlock* true_def_block = context()->get_instr_block(true_value); + BasicBlock* false_def_block = context()->get_instr_block(false_value); + + uint32_t true_vn = vn_table.GetValueNumber(true_value); + uint32_t false_vn = vn_table.GetValueNumber(false_value); + if (true_vn != 0 && true_vn == false_vn) { + Instruction* inst_to_use = nullptr; + + // Try to pick an instruction that is not in a side node. If we can't + // pick either the true for false branch as long as they can be + // legally moved. + if (!true_def_block || + dominators->Dominates(true_def_block, &block)) { + inst_to_use = true_value; + } else if (!false_def_block || + dominators->Dominates(false_def_block, &block)) { + inst_to_use = false_value; + } else if (CanHoistInstruction(true_value, common, dominators)) { + inst_to_use = true_value; + } else if (CanHoistInstruction(false_value, common, dominators)) { + inst_to_use = false_value; + } + + if (inst_to_use != nullptr) { + modified = true; + HoistInstruction(inst_to_use, common, dominators); + context()->KillNamesAndDecorates(phi); + context()->ReplaceAllUsesWith(phi->result_id(), + inst_to_use->result_id()); + } + return; + } + + // If either incoming value is defined in a block that does not dominate + // this phi, then we cannot eliminate the phi with a select. + // TODO(alan-baker): Perform code motion where it makes sense to enable + // the transform in this case. + if (true_def_block && !dominators->Dominates(true_def_block, &block)) + return; + + if (false_def_block && !dominators->Dominates(false_def_block, &block)) + return; + + analysis::Type* data_ty = + context()->get_type_mgr()->GetType(true_value->type_id()); + if (analysis::Vector* vec_data_ty = data_ty->AsVector()) { + condition = SplatCondition(vec_data_ty, condition, &builder); + } + + Instruction* select = builder.AddSelect(phi->type_id(), condition, + true_value->result_id(), + false_value->result_id()); + select->UpdateDebugInfoFrom(phi); + context()->ReplaceAllUsesWith(phi->result_id(), select->result_id()); + to_kill.push_back(phi); + modified = true; + + return; + }); + } + } + + for (auto inst : to_kill) { + context()->KillInst(inst); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool IfConversion::CheckBlock(BasicBlock* block, DominatorAnalysis* dominators, + BasicBlock** common) { + const std::vector& preds = cfg()->preds(block->id()); + + // TODO(alan-baker): Extend to more than two predecessors + if (preds.size() != 2) return false; + + BasicBlock* inc0 = context()->get_instr_block(preds[0]); + if (dominators->Dominates(block, inc0)) return false; + + BasicBlock* inc1 = context()->get_instr_block(preds[1]); + if (dominators->Dominates(block, inc1)) return false; + + // All phis will have the same common dominator, so cache the result + // for this block. If there is no common dominator, then we cannot transform + // any phi in this basic block. + *common = dominators->CommonDominator(inc0, inc1); + if (!*common || cfg()->IsPseudoEntryBlock(*common)) return false; + Instruction* branch = (*common)->terminator(); + if (branch->opcode() != SpvOpBranchConditional) return false; + auto merge = (*common)->GetMergeInst(); + if (!merge || merge->opcode() != SpvOpSelectionMerge) return false; + if ((*common)->MergeBlockIdIfAny() != block->id()) return false; + + return true; +} + +bool IfConversion::CheckPhiUsers(Instruction* phi, BasicBlock* block) { + return get_def_use_mgr()->WhileEachUser(phi, [block, + this](Instruction* user) { + if (user->opcode() == SpvOpPhi && context()->get_instr_block(user) == block) + return false; + return true; + }); +} + +uint32_t IfConversion::SplatCondition(analysis::Vector* vec_data_ty, + uint32_t cond, + InstructionBuilder* builder) { + // If the data inputs to OpSelect are vectors, the condition for + // OpSelect must be a boolean vector with the same number of + // components. So splat the condition for the branch into a vector + // type. + analysis::Bool bool_ty; + analysis::Vector bool_vec_ty(&bool_ty, vec_data_ty->element_count()); + uint32_t bool_vec_id = + context()->get_type_mgr()->GetTypeInstruction(&bool_vec_ty); + std::vector ids(vec_data_ty->element_count(), cond); + return builder->AddCompositeConstruct(bool_vec_id, ids)->result_id(); +} + +bool IfConversion::CheckType(uint32_t id) { + Instruction* type = get_def_use_mgr()->GetDef(id); + SpvOp op = type->opcode(); + if (spvOpcodeIsScalarType(op) || op == SpvOpTypePointer || + op == SpvOpTypeVector) + return true; + return false; +} + +BasicBlock* IfConversion::GetBlock(uint32_t id) { + return context()->get_instr_block(get_def_use_mgr()->GetDef(id)); +} + +BasicBlock* IfConversion::GetIncomingBlock(Instruction* phi, + uint32_t predecessor) { + uint32_t in_index = 2 * predecessor + 1; + return GetBlock(phi->GetSingleWordInOperand(in_index)); +} + +Instruction* IfConversion::GetIncomingValue(Instruction* phi, + uint32_t predecessor) { + uint32_t in_index = 2 * predecessor; + return get_def_use_mgr()->GetDef(phi->GetSingleWordInOperand(in_index)); +} + +void IfConversion::HoistInstruction(Instruction* inst, BasicBlock* target_block, + DominatorAnalysis* dominators) { + BasicBlock* inst_block = context()->get_instr_block(inst); + if (!inst_block) { + // This is in the header, and dominates everything. + return; + } + + if (dominators->Dominates(inst_block, target_block)) { + // Already in position. No work to do. + return; + } + + assert(inst->IsOpcodeCodeMotionSafe() && + "Trying to move an instruction that is not safe to move."); + + // First hoist all instructions it depends on. + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + inst->ForEachInId( + [this, target_block, def_use_mgr, dominators](uint32_t* id) { + Instruction* operand_inst = def_use_mgr->GetDef(*id); + HoistInstruction(operand_inst, target_block, dominators); + }); + + Instruction* insertion_pos = target_block->terminator(); + if ((insertion_pos)->PreviousNode()->opcode() == SpvOpSelectionMerge) { + insertion_pos = insertion_pos->PreviousNode(); + } + inst->RemoveFromList(); + insertion_pos->InsertBefore(std::unique_ptr(inst)); + context()->set_instr_block(inst, target_block); +} + +bool IfConversion::CanHoistInstruction(Instruction* inst, + BasicBlock* target_block, + DominatorAnalysis* dominators) { + BasicBlock* inst_block = context()->get_instr_block(inst); + if (!inst_block) { + // This is in the header, and dominates everything. + return true; + } + + if (dominators->Dominates(inst_block, target_block)) { + // Already in position. No work to do. + return true; + } + + if (!inst->IsOpcodeCodeMotionSafe()) { + return false; + } + + // Check all instruction |inst| depends on. + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + return inst->WhileEachInId( + [this, target_block, def_use_mgr, dominators](uint32_t* id) { + Instruction* operand_inst = def_use_mgr->GetDef(*id); + return CanHoistInstruction(operand_inst, target_block, dominators); + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/if_conversion.h b/third_party/spirv-tools/source/opt/if_conversion.h new file mode 100644 index 0000000..db84e70 --- /dev/null +++ b/third_party/spirv-tools/source/opt/if_conversion.h @@ -0,0 +1,89 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_IF_CONVERSION_H_ +#define SOURCE_OPT_IF_CONVERSION_H_ + +#include "source/opt/basic_block.h" +#include "source/opt/ir_builder.h" +#include "source/opt/pass.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class IfConversion : public Pass { + public: + const char* name() const override { return "if-conversion"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisCFG | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Returns true if |id| is a valid type for use with OpSelect. OpSelect only + // allows scalars, vectors and pointers as valid inputs. + bool CheckType(uint32_t id); + + // Returns the basic block containing |id|. + BasicBlock* GetBlock(uint32_t id); + + // Returns the basic block for the |predecessor|'th index predecessor of + // |phi|. + BasicBlock* GetIncomingBlock(Instruction* phi, uint32_t predecessor); + + // Returns the instruction defining the |predecessor|'th index of |phi|. + Instruction* GetIncomingValue(Instruction* phi, uint32_t predecessor); + + // Returns the id of a OpCompositeConstruct boolean vector. The composite has + // the same number of elements as |vec_data_ty| and each member is |cond|. + // |where| indicates the location in |block| to insert the composite + // construct. If necessary, this function will also construct the necessary + // type instructions for the boolean vector. + uint32_t SplatCondition(analysis::Vector* vec_data_ty, uint32_t cond, + InstructionBuilder* builder); + + // Returns true if none of |phi|'s users are in |block|. + bool CheckPhiUsers(Instruction* phi, BasicBlock* block); + + // Returns |false| if |block| is not appropriate to transform. Only + // transforms blocks with two predecessors. Neither incoming block can be + // dominated by |block|. Both predecessors must share a common dominator that + // is terminated by a conditional branch. + bool CheckBlock(BasicBlock* block, DominatorAnalysis* dominators, + BasicBlock** common); + + // Moves |inst| to |target_block| if it does not already dominate the block. + // Any instructions that |inst| depends on are move if necessary. It is + // assumed that |inst| can be hoisted to |target_block| as defined by + // |CanHoistInstruction|. |dominators| is the dominator analysis for the + // function that contains |target_block|. + void HoistInstruction(Instruction* inst, BasicBlock* target_block, + DominatorAnalysis* dominators); + + // Returns true if it is legal to move |inst| and the instructions it depends + // on to |target_block| if they do not already dominate |target_block|. + bool CanHoistInstruction(Instruction* inst, BasicBlock* target_block, + DominatorAnalysis* dominators); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_IF_CONVERSION_H_ diff --git a/third_party/spirv-tools/source/opt/inline_exhaustive_pass.cpp b/third_party/spirv-tools/source/opt/inline_exhaustive_pass.cpp new file mode 100644 index 0000000..24f4e73 --- /dev/null +++ b/third_party/spirv-tools/source/opt/inline_exhaustive_pass.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/inline_exhaustive_pass.h" + +#include + +namespace spvtools { +namespace opt { + +Pass::Status InlineExhaustivePass::InlineExhaustive(Function* func) { + bool modified = false; + // Using block iterators here because of block erasures and insertions. + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end();) { + if (IsInlinableFunctionCall(&*ii)) { + // Inline call. + std::vector> newBlocks; + std::vector> newVars; + if (!GenInlineCode(&newBlocks, &newVars, ii, bi)) { + return Status::Failure; + } + // If call block is replaced with more than one block, point + // succeeding phis at new last block. + if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks); + // Replace old calling block with new block(s). + + // We need to kill the name and decorations for the call, which + // will be deleted. Other instructions in the block will be moved to + // newBlocks. We don't need to do anything with those. + context()->KillNamesAndDecorates(&*ii); + + bi = bi.Erase(); + + for (auto& bb : newBlocks) { + bb->SetParent(func); + } + bi = bi.InsertBefore(&newBlocks); + // Insert new function variables. + if (newVars.size() > 0) + func->begin()->begin().InsertBefore(std::move(newVars)); + // Restart inlining at beginning of calling block. + ii = bi->begin(); + modified = true; + } else { + ++ii; + } + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +Pass::Status InlineExhaustivePass::ProcessImpl() { + Status status = Status::SuccessWithoutChange; + // Attempt exhaustive inlining on each entry point function in module + ProcessFunction pfn = [&status, this](Function* fp) { + status = CombineStatus(status, InlineExhaustive(fp)); + return false; + }; + context()->ProcessEntryPointCallTree(pfn); + return status; +} + +InlineExhaustivePass::InlineExhaustivePass() = default; + +Pass::Status InlineExhaustivePass::Process() { + InitializeInline(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/inline_exhaustive_pass.h b/third_party/spirv-tools/source/opt/inline_exhaustive_pass.h new file mode 100644 index 0000000..c2e8547 --- /dev/null +++ b/third_party/spirv-tools/source/opt/inline_exhaustive_pass.h @@ -0,0 +1,53 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INLINE_EXHAUSTIVE_PASS_H_ +#define SOURCE_OPT_INLINE_EXHAUSTIVE_PASS_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/inline_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class InlineExhaustivePass : public InlinePass { + public: + InlineExhaustivePass(); + Status Process() override; + + const char* name() const override { return "inline-entry-points-exhaustive"; } + + private: + // Exhaustively inline all function calls in func as well as in + // all code that is inlined into func. Returns the status. + Status InlineExhaustive(Function* func); + + void Initialize(); + Pass::Status ProcessImpl(); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INLINE_EXHAUSTIVE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/inline_opaque_pass.cpp b/third_party/spirv-tools/source/opt/inline_opaque_pass.cpp new file mode 100644 index 0000000..6ccaf90 --- /dev/null +++ b/third_party/spirv-tools/source/opt/inline_opaque_pass.cpp @@ -0,0 +1,120 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/inline_opaque_pass.h" + +#include + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kTypePointerTypeIdInIdx = 1; + +} // anonymous namespace + +bool InlineOpaquePass::IsOpaqueType(uint32_t typeId) { + const Instruction* typeInst = get_def_use_mgr()->GetDef(typeId); + switch (typeInst->opcode()) { + case SpvOpTypeSampler: + case SpvOpTypeImage: + case SpvOpTypeSampledImage: + return true; + case SpvOpTypePointer: + return IsOpaqueType( + typeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx)); + default: + break; + } + // TODO(greg-lunarg): Handle arrays containing opaque type + if (typeInst->opcode() != SpvOpTypeStruct) return false; + // Return true if any member is opaque + return !typeInst->WhileEachInId([this](const uint32_t* tid) { + if (IsOpaqueType(*tid)) return false; + return true; + }); +} + +bool InlineOpaquePass::HasOpaqueArgsOrReturn(const Instruction* callInst) { + // Check return type + if (IsOpaqueType(callInst->type_id())) return true; + // Check args + int icnt = 0; + return !callInst->WhileEachInId([&icnt, this](const uint32_t* iid) { + if (icnt > 0) { + const Instruction* argInst = get_def_use_mgr()->GetDef(*iid); + if (IsOpaqueType(argInst->type_id())) return false; + } + ++icnt; + return true; + }); +} + +Pass::Status InlineOpaquePass::InlineOpaque(Function* func) { + bool modified = false; + // Using block iterators here because of block erasures and insertions. + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end();) { + if (IsInlinableFunctionCall(&*ii) && HasOpaqueArgsOrReturn(&*ii)) { + // Inline call. + std::vector> newBlocks; + std::vector> newVars; + if (!GenInlineCode(&newBlocks, &newVars, ii, bi)) { + return Status::Failure; + } + + // If call block is replaced with more than one block, point + // succeeding phis at new last block. + if (newBlocks.size() > 1) UpdateSucceedingPhis(newBlocks); + // Replace old calling block with new block(s). + bi = bi.Erase(); + bi = bi.InsertBefore(&newBlocks); + // Insert new function variables. + if (newVars.size() > 0) + func->begin()->begin().InsertBefore(std::move(newVars)); + // Restart inlining at beginning of calling block. + ii = bi->begin(); + modified = true; + } else { + ++ii; + } + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void InlineOpaquePass::Initialize() { InitializeInline(); } + +Pass::Status InlineOpaquePass::ProcessImpl() { + Status status = Status::SuccessWithoutChange; + // Do opaque inlining on each function in entry point call tree + ProcessFunction pfn = [&status, this](Function* fp) { + status = CombineStatus(status, InlineOpaque(fp)); + return false; + }; + context()->ProcessEntryPointCallTree(pfn); + return status; +} + +InlineOpaquePass::InlineOpaquePass() = default; + +Pass::Status InlineOpaquePass::Process() { + Initialize(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/inline_opaque_pass.h b/third_party/spirv-tools/source/opt/inline_opaque_pass.h new file mode 100644 index 0000000..1e3081d --- /dev/null +++ b/third_party/spirv-tools/source/opt/inline_opaque_pass.h @@ -0,0 +1,60 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INLINE_OPAQUE_PASS_H_ +#define SOURCE_OPT_INLINE_OPAQUE_PASS_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/inline_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class InlineOpaquePass : public InlinePass { + public: + InlineOpaquePass(); + Status Process() override; + + const char* name() const override { return "inline-entry-points-opaque"; } + + private: + // Return true if |typeId| is or contains opaque type + bool IsOpaqueType(uint32_t typeId); + + // Return true if function call |callInst| has opaque argument or return type + bool HasOpaqueArgsOrReturn(const Instruction* callInst); + + // Inline all function calls in |func| that have opaque params or return + // type. Inline similarly all code that is inlined into func. Return true + // if func is modified. + Status InlineOpaque(Function* func); + + void Initialize(); + Pass::Status ProcessImpl(); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INLINE_OPAQUE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/inline_pass.cpp b/third_party/spirv-tools/source/opt/inline_pass.cpp new file mode 100644 index 0000000..eaf29aa --- /dev/null +++ b/third_party/spirv-tools/source/opt/inline_pass.cpp @@ -0,0 +1,796 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/inline_pass.h" + +#include +#include + +#include "source/cfa.h" +#include "source/opt/reflect.h" +#include "source/util/make_unique.h" + +// Indices of operands in SPIR-V instructions + +static const int kSpvFunctionCallFunctionId = 2; +static const int kSpvFunctionCallArgumentId = 3; +static const int kSpvReturnValueId = 0; + +namespace spvtools { +namespace opt { + +uint32_t InlinePass::AddPointerToType(uint32_t type_id, + SpvStorageClass storage_class) { + uint32_t resultId = context()->TakeNextId(); + if (resultId == 0) { + return resultId; + } + + std::unique_ptr type_inst( + new Instruction(context(), SpvOpTypePointer, 0, resultId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(storage_class)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}})); + context()->AddType(std::move(type_inst)); + analysis::Type* pointeeTy; + std::unique_ptr pointerTy; + std::tie(pointeeTy, pointerTy) = + context()->get_type_mgr()->GetTypeAndPointerType(type_id, + SpvStorageClassFunction); + context()->get_type_mgr()->RegisterType(resultId, *pointerTy); + return resultId; +} + +void InlinePass::AddBranch(uint32_t label_id, + std::unique_ptr* block_ptr) { + std::unique_ptr newBranch( + new Instruction(context(), SpvOpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}})); + (*block_ptr)->AddInstruction(std::move(newBranch)); +} + +void InlinePass::AddBranchCond(uint32_t cond_id, uint32_t true_id, + uint32_t false_id, + std::unique_ptr* block_ptr) { + std::unique_ptr newBranch( + new Instruction(context(), SpvOpBranchConditional, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}})); + (*block_ptr)->AddInstruction(std::move(newBranch)); +} + +void InlinePass::AddLoopMerge(uint32_t merge_id, uint32_t continue_id, + std::unique_ptr* block_ptr) { + std::unique_ptr newLoopMerge(new Instruction( + context(), SpvOpLoopMerge, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {0}}})); + (*block_ptr)->AddInstruction(std::move(newLoopMerge)); +} + +void InlinePass::AddStore(uint32_t ptr_id, uint32_t val_id, + std::unique_ptr* block_ptr, + const Instruction* line_inst, + const DebugScope& dbg_scope) { + std::unique_ptr newStore( + new Instruction(context(), SpvOpStore, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {val_id}}})); + if (line_inst != nullptr) { + newStore->dbg_line_insts().push_back(*line_inst); + } + newStore->SetDebugScope(dbg_scope); + (*block_ptr)->AddInstruction(std::move(newStore)); +} + +void InlinePass::AddLoad(uint32_t type_id, uint32_t resultId, uint32_t ptr_id, + std::unique_ptr* block_ptr, + const Instruction* line_inst, + const DebugScope& dbg_scope) { + std::unique_ptr newLoad( + new Instruction(context(), SpvOpLoad, type_id, resultId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ptr_id}}})); + if (line_inst != nullptr) { + newLoad->dbg_line_insts().push_back(*line_inst); + } + newLoad->SetDebugScope(dbg_scope); + (*block_ptr)->AddInstruction(std::move(newLoad)); +} + +std::unique_ptr InlinePass::NewLabel(uint32_t label_id) { + std::unique_ptr newLabel( + new Instruction(context(), SpvOpLabel, 0, label_id, {})); + return newLabel; +} + +uint32_t InlinePass::GetFalseId() { + if (false_id_ != 0) return false_id_; + false_id_ = get_module()->GetGlobalValue(SpvOpConstantFalse); + if (false_id_ != 0) return false_id_; + uint32_t boolId = get_module()->GetGlobalValue(SpvOpTypeBool); + if (boolId == 0) { + boolId = context()->TakeNextId(); + if (boolId == 0) { + return 0; + } + get_module()->AddGlobalValue(SpvOpTypeBool, boolId, 0); + } + false_id_ = context()->TakeNextId(); + if (false_id_ == 0) { + return 0; + } + get_module()->AddGlobalValue(SpvOpConstantFalse, false_id_, boolId); + return false_id_; +} + +void InlinePass::MapParams( + Function* calleeFn, BasicBlock::iterator call_inst_itr, + std::unordered_map* callee2caller) { + int param_idx = 0; + calleeFn->ForEachParam( + [&call_inst_itr, ¶m_idx, &callee2caller](const Instruction* cpi) { + const uint32_t pid = cpi->result_id(); + (*callee2caller)[pid] = call_inst_itr->GetSingleWordOperand( + kSpvFunctionCallArgumentId + param_idx); + ++param_idx; + }); +} + +bool InlinePass::CloneAndMapLocals( + Function* calleeFn, std::vector>* new_vars, + std::unordered_map* callee2caller, + analysis::DebugInlinedAtContext* inlined_at_ctx) { + auto callee_block_itr = calleeFn->begin(); + auto callee_var_itr = callee_block_itr->begin(); + while (callee_var_itr->opcode() == SpvOp::SpvOpVariable || + callee_var_itr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugDeclare) { + if (callee_var_itr->opcode() != SpvOp::SpvOpVariable) { + ++callee_var_itr; + continue; + } + + std::unique_ptr var_inst(callee_var_itr->Clone(context())); + uint32_t newId = context()->TakeNextId(); + if (newId == 0) { + return false; + } + get_decoration_mgr()->CloneDecorations(callee_var_itr->result_id(), newId); + var_inst->SetResultId(newId); + var_inst->UpdateDebugInlinedAt( + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + callee_var_itr->GetDebugInlinedAt(), inlined_at_ctx)); + (*callee2caller)[callee_var_itr->result_id()] = newId; + new_vars->push_back(std::move(var_inst)); + ++callee_var_itr; + } + return true; +} + +uint32_t InlinePass::CreateReturnVar( + Function* calleeFn, std::vector>* new_vars) { + uint32_t returnVarId = 0; + const uint32_t calleeTypeId = calleeFn->type_id(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + assert(type_mgr->GetType(calleeTypeId)->AsVoid() == nullptr && + "Cannot create a return variable of type void."); + // Find or create ptr to callee return type. + uint32_t returnVarTypeId = + type_mgr->FindPointerToType(calleeTypeId, SpvStorageClassFunction); + + if (returnVarTypeId == 0) { + returnVarTypeId = AddPointerToType(calleeTypeId, SpvStorageClassFunction); + if (returnVarTypeId == 0) { + return 0; + } + } + + // Add return var to new function scope variables. + returnVarId = context()->TakeNextId(); + if (returnVarId == 0) { + return 0; + } + + std::unique_ptr var_inst( + new Instruction(context(), SpvOpVariable, returnVarTypeId, returnVarId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS, + {SpvStorageClassFunction}}})); + new_vars->push_back(std::move(var_inst)); + get_decoration_mgr()->CloneDecorations(calleeFn->result_id(), returnVarId); + return returnVarId; +} + +bool InlinePass::IsSameBlockOp(const Instruction* inst) const { + return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage; +} + +bool InlinePass::CloneSameBlockOps( + std::unique_ptr* inst, + std::unordered_map* postCallSB, + std::unordered_map* preCallSB, + std::unique_ptr* block_ptr) { + return (*inst)->WhileEachInId([&postCallSB, &preCallSB, &block_ptr, + this](uint32_t* iid) { + const auto mapItr = (*postCallSB).find(*iid); + if (mapItr == (*postCallSB).end()) { + const auto mapItr2 = (*preCallSB).find(*iid); + if (mapItr2 != (*preCallSB).end()) { + // Clone pre-call same-block ops, map result id. + const Instruction* inInst = mapItr2->second; + std::unique_ptr sb_inst(inInst->Clone(context())); + if (!CloneSameBlockOps(&sb_inst, postCallSB, preCallSB, block_ptr)) { + return false; + } + + const uint32_t rid = sb_inst->result_id(); + const uint32_t nid = context()->TakeNextId(); + if (nid == 0) { + return false; + } + get_decoration_mgr()->CloneDecorations(rid, nid); + sb_inst->SetResultId(nid); + (*postCallSB)[rid] = nid; + *iid = nid; + (*block_ptr)->AddInstruction(std::move(sb_inst)); + } + } else { + // Reset same-block op operand. + *iid = mapItr->second; + } + return true; + }); +} + +void InlinePass::MoveInstsBeforeEntryBlock( + std::unordered_map* preCallSB, + BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr, + UptrVectorIterator call_block_itr) { + for (auto cii = call_block_itr->begin(); cii != call_inst_itr; + cii = call_block_itr->begin()) { + Instruction* inst = &*cii; + inst->RemoveFromList(); + std::unique_ptr cp_inst(inst); + // Remember same-block ops for possible regeneration. + if (IsSameBlockOp(&*cp_inst)) { + auto* sb_inst_ptr = cp_inst.get(); + (*preCallSB)[cp_inst->result_id()] = sb_inst_ptr; + } + new_blk_ptr->AddInstruction(std::move(cp_inst)); + } +} + +std::unique_ptr InlinePass::AddGuardBlock( + std::vector>* new_blocks, + std::unordered_map* callee2caller, + std::unique_ptr new_blk_ptr, uint32_t entry_blk_label_id) { + const auto guard_block_id = context()->TakeNextId(); + if (guard_block_id == 0) { + return nullptr; + } + AddBranch(guard_block_id, &new_blk_ptr); + new_blocks->push_back(std::move(new_blk_ptr)); + // Start the next block. + new_blk_ptr = MakeUnique(NewLabel(guard_block_id)); + // Reset the mapping of the callee's entry block to point to + // the guard block. Do this so we can fix up phis later on to + // satisfy dominance. + (*callee2caller)[entry_blk_label_id] = guard_block_id; + return new_blk_ptr; +} + +InstructionList::iterator InlinePass::AddStoresForVariableInitializers( + const std::unordered_map& callee2caller, + analysis::DebugInlinedAtContext* inlined_at_ctx, + std::unique_ptr* new_blk_ptr, + UptrVectorIterator callee_first_block_itr) { + auto callee_itr = callee_first_block_itr->begin(); + while (callee_itr->opcode() == SpvOp::SpvOpVariable || + callee_itr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugDeclare) { + if (callee_itr->opcode() == SpvOp::SpvOpVariable && + callee_itr->NumInOperands() == 2) { + assert(callee2caller.count(callee_itr->result_id()) && + "Expected the variable to have already been mapped."); + uint32_t new_var_id = callee2caller.at(callee_itr->result_id()); + + // The initializer must be a constant or global value. No mapped + // should be used. + uint32_t val_id = callee_itr->GetSingleWordInOperand(1); + AddStore(new_var_id, val_id, new_blk_ptr, callee_itr->dbg_line_inst(), + context()->get_debug_info_mgr()->BuildDebugScope( + callee_itr->GetDebugScope(), inlined_at_ctx)); + } + if (callee_itr->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugDeclare) { + InlineSingleInstruction( + callee2caller, new_blk_ptr->get(), &*callee_itr, + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + callee_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx)); + } + ++callee_itr; + } + return callee_itr; +} + +bool InlinePass::InlineSingleInstruction( + const std::unordered_map& callee2caller, + BasicBlock* new_blk_ptr, const Instruction* inst, uint32_t dbg_inlined_at) { + // If we have return, it must be at the end of the callee. We will handle + // it at the end. + if (inst->opcode() == SpvOpReturnValue || inst->opcode() == SpvOpReturn) + return true; + + // Copy callee instruction and remap all input Ids. + std::unique_ptr cp_inst(inst->Clone(context())); + cp_inst->ForEachInId([&callee2caller](uint32_t* iid) { + const auto mapItr = callee2caller.find(*iid); + if (mapItr != callee2caller.end()) { + *iid = mapItr->second; + } + }); + + // If result id is non-zero, remap it. + const uint32_t rid = cp_inst->result_id(); + if (rid != 0) { + const auto mapItr = callee2caller.find(rid); + if (mapItr == callee2caller.end()) { + return false; + } + uint32_t nid = mapItr->second; + cp_inst->SetResultId(nid); + get_decoration_mgr()->CloneDecorations(rid, nid); + } + + cp_inst->UpdateDebugInlinedAt(dbg_inlined_at); + new_blk_ptr->AddInstruction(std::move(cp_inst)); + return true; +} + +std::unique_ptr InlinePass::InlineReturn( + const std::unordered_map& callee2caller, + std::vector>* new_blocks, + std::unique_ptr new_blk_ptr, + analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn, + const Instruction* inst, uint32_t returnVarId) { + // Store return value to return variable. + if (inst->opcode() == SpvOpReturnValue) { + assert(returnVarId != 0); + uint32_t valId = inst->GetInOperand(kSpvReturnValueId).words[0]; + const auto mapItr = callee2caller.find(valId); + if (mapItr != callee2caller.end()) { + valId = mapItr->second; + } + AddStore(returnVarId, valId, &new_blk_ptr, inst->dbg_line_inst(), + context()->get_debug_info_mgr()->BuildDebugScope( + inst->GetDebugScope(), inlined_at_ctx)); + } + + uint32_t returnLabelId = 0; + for (auto callee_block_itr = calleeFn->begin(); + callee_block_itr != calleeFn->end(); ++callee_block_itr) { + if (callee_block_itr->tail()->opcode() == SpvOpUnreachable || + callee_block_itr->tail()->opcode() == SpvOpKill || + callee_block_itr->tail()->opcode() == SpvOpTerminateInvocation) { + returnLabelId = context()->TakeNextId(); + break; + } + } + if (returnLabelId == 0) return new_blk_ptr; + + if (inst->opcode() == SpvOpReturn || inst->opcode() == SpvOpReturnValue) + AddBranch(returnLabelId, &new_blk_ptr); + new_blocks->push_back(std::move(new_blk_ptr)); + return MakeUnique(NewLabel(returnLabelId)); +} + +bool InlinePass::InlineEntryBlock( + const std::unordered_map& callee2caller, + std::unique_ptr* new_blk_ptr, + UptrVectorIterator callee_first_block, + analysis::DebugInlinedAtContext* inlined_at_ctx) { + auto callee_inst_itr = AddStoresForVariableInitializers( + callee2caller, inlined_at_ctx, new_blk_ptr, callee_first_block); + + while (callee_inst_itr != callee_first_block->end()) { + if (!InlineSingleInstruction( + callee2caller, new_blk_ptr->get(), &*callee_inst_itr, + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + callee_inst_itr->GetDebugScope().GetInlinedAt(), + inlined_at_ctx))) { + return false; + } + ++callee_inst_itr; + } + return true; +} + +std::unique_ptr InlinePass::InlineBasicBlocks( + std::vector>* new_blocks, + const std::unordered_map& callee2caller, + std::unique_ptr new_blk_ptr, + analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn) { + auto callee_block_itr = calleeFn->begin(); + ++callee_block_itr; + + while (callee_block_itr != calleeFn->end()) { + new_blocks->push_back(std::move(new_blk_ptr)); + const auto mapItr = + callee2caller.find(callee_block_itr->GetLabelInst()->result_id()); + if (mapItr == callee2caller.end()) return nullptr; + new_blk_ptr = MakeUnique(NewLabel(mapItr->second)); + + auto tail_inst_itr = callee_block_itr->end(); + for (auto inst_itr = callee_block_itr->begin(); inst_itr != tail_inst_itr; + ++inst_itr) { + if (!InlineSingleInstruction( + callee2caller, new_blk_ptr.get(), &*inst_itr, + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + inst_itr->GetDebugScope().GetInlinedAt(), inlined_at_ctx))) { + return nullptr; + } + } + + ++callee_block_itr; + } + return new_blk_ptr; +} + +bool InlinePass::MoveCallerInstsAfterFunctionCall( + std::unordered_map* preCallSB, + std::unordered_map* postCallSB, + std::unique_ptr* new_blk_ptr, + BasicBlock::iterator call_inst_itr, bool multiBlocks) { + // Copy remaining instructions from caller block. + for (Instruction* inst = call_inst_itr->NextNode(); inst; + inst = call_inst_itr->NextNode()) { + inst->RemoveFromList(); + std::unique_ptr cp_inst(inst); + // If multiple blocks generated, regenerate any same-block + // instruction that has not been seen in this last block. + if (multiBlocks) { + if (!CloneSameBlockOps(&cp_inst, postCallSB, preCallSB, new_blk_ptr)) { + return false; + } + + // Remember same-block ops in this block. + if (IsSameBlockOp(&*cp_inst)) { + const uint32_t rid = cp_inst->result_id(); + (*postCallSB)[rid] = rid; + } + } + new_blk_ptr->get()->AddInstruction(std::move(cp_inst)); + } + + return true; +} + +void InlinePass::MoveLoopMergeInstToFirstBlock( + std::vector>* new_blocks) { + // Move the OpLoopMerge from the last block back to the first, where + // it belongs. + auto& first = new_blocks->front(); + auto& last = new_blocks->back(); + assert(first != last); + + // Insert a modified copy of the loop merge into the first block. + auto loop_merge_itr = last->tail(); + --loop_merge_itr; + assert(loop_merge_itr->opcode() == SpvOpLoopMerge); + std::unique_ptr cp_inst(loop_merge_itr->Clone(context())); + first->tail().InsertBefore(std::move(cp_inst)); + + // Remove the loop merge from the last block. + loop_merge_itr->RemoveFromList(); + delete &*loop_merge_itr; +} + +bool InlinePass::GenInlineCode( + std::vector>* new_blocks, + std::vector>* new_vars, + BasicBlock::iterator call_inst_itr, + UptrVectorIterator call_block_itr) { + // Map from all ids in the callee to their equivalent id in the caller + // as callee instructions are copied into caller. + std::unordered_map callee2caller; + // Pre-call same-block insts + std::unordered_map preCallSB; + // Post-call same-block op ids + std::unordered_map postCallSB; + + analysis::DebugInlinedAtContext inlined_at_ctx(&*call_inst_itr); + + // Invalidate the def-use chains. They are not kept up to date while + // inlining. However, certain calls try to keep them up-to-date if they are + // valid. These operations can fail. + context()->InvalidateAnalyses(IRContext::kAnalysisDefUse); + + // If the caller is a loop header and the callee has multiple blocks, then the + // normal inlining logic will place the OpLoopMerge in the last of several + // blocks in the loop. Instead, it should be placed at the end of the first + // block. We'll wait to move the OpLoopMerge until the end of the regular + // inlining logic, and only if necessary. + bool caller_is_loop_header = call_block_itr->GetLoopMergeInst() != nullptr; + + // Single-trip loop continue block + std::unique_ptr single_trip_loop_cont_blk; + + Function* calleeFn = id2function_[call_inst_itr->GetSingleWordOperand( + kSpvFunctionCallFunctionId)]; + + // Map parameters to actual arguments. + MapParams(calleeFn, call_inst_itr, &callee2caller); + + // Define caller local variables for all callee variables and create map to + // them. + if (!CloneAndMapLocals(calleeFn, new_vars, &callee2caller, &inlined_at_ctx)) { + return false; + } + + // First block needs to use label of original block + // but map callee label in case of phi reference. + uint32_t entry_blk_label_id = calleeFn->begin()->GetLabelInst()->result_id(); + callee2caller[entry_blk_label_id] = call_block_itr->id(); + std::unique_ptr new_blk_ptr = + MakeUnique(NewLabel(call_block_itr->id())); + + // Move instructions of original caller block up to call instruction. + MoveInstsBeforeEntryBlock(&preCallSB, new_blk_ptr.get(), call_inst_itr, + call_block_itr); + + if (caller_is_loop_header && + (*(calleeFn->begin())).GetMergeInst() != nullptr) { + // We can't place both the caller's merge instruction and + // another merge instruction in the same block. So split the + // calling block. Insert an unconditional branch to a new guard + // block. Later, once we know the ID of the last block, we + // will move the caller's OpLoopMerge from the last generated + // block into the first block. We also wait to avoid + // invalidating various iterators. + new_blk_ptr = AddGuardBlock(new_blocks, &callee2caller, + std::move(new_blk_ptr), entry_blk_label_id); + if (new_blk_ptr == nullptr) return false; + } + + // Create return var if needed. + const uint32_t calleeTypeId = calleeFn->type_id(); + uint32_t returnVarId = 0; + analysis::Type* calleeType = context()->get_type_mgr()->GetType(calleeTypeId); + if (calleeType->AsVoid() == nullptr) { + returnVarId = CreateReturnVar(calleeFn, new_vars); + if (returnVarId == 0) { + return false; + } + } + + calleeFn->WhileEachInst([&callee2caller, this](const Instruction* cpi) { + // Create set of callee result ids. Used to detect forward references + const uint32_t rid = cpi->result_id(); + if (rid != 0 && callee2caller.find(rid) == callee2caller.end()) { + const uint32_t nid = context()->TakeNextId(); + if (nid == 0) return false; + callee2caller[rid] = nid; + } + return true; + }); + + // Inline DebugClare instructions in the callee's header. + calleeFn->ForEachDebugInstructionsInHeader( + [&new_blk_ptr, &callee2caller, &inlined_at_ctx, this](Instruction* inst) { + InlineSingleInstruction( + callee2caller, new_blk_ptr.get(), inst, + context()->get_debug_info_mgr()->BuildDebugInlinedAtChain( + inst->GetDebugScope().GetInlinedAt(), &inlined_at_ctx)); + }); + + // Inline the entry block of the callee function. + if (!InlineEntryBlock(callee2caller, &new_blk_ptr, calleeFn->begin(), + &inlined_at_ctx)) { + return false; + } + + // Inline blocks of the callee function other than the entry block. + new_blk_ptr = + InlineBasicBlocks(new_blocks, callee2caller, std::move(new_blk_ptr), + &inlined_at_ctx, calleeFn); + if (new_blk_ptr == nullptr) return false; + + new_blk_ptr = InlineReturn(callee2caller, new_blocks, std::move(new_blk_ptr), + &inlined_at_ctx, calleeFn, + &*(calleeFn->tail()->tail()), returnVarId); + + // Load return value into result id of call, if it exists. + if (returnVarId != 0) { + const uint32_t resId = call_inst_itr->result_id(); + assert(resId != 0); + AddLoad(calleeTypeId, resId, returnVarId, &new_blk_ptr, + call_inst_itr->dbg_line_inst(), call_inst_itr->GetDebugScope()); + } else { + // Even though it is very unlikely, it is possible that the result id of + // the void-function call is used, so we need to generate an instruction + // with that result id. + std::unique_ptr undef_inst( + new Instruction(context(), SpvOpUndef, call_inst_itr->type_id(), + call_inst_itr->result_id(), {})); + context()->AddGlobalValue(std::move(undef_inst)); + } + + // Move instructions of original caller block after call instruction. + if (!MoveCallerInstsAfterFunctionCall(&preCallSB, &postCallSB, &new_blk_ptr, + call_inst_itr, + calleeFn->begin() != calleeFn->end())) + return false; + + // Finalize inline code. + new_blocks->push_back(std::move(new_blk_ptr)); + + if (caller_is_loop_header && (new_blocks->size() > 1)) + MoveLoopMergeInstToFirstBlock(new_blocks); + + // Update block map given replacement blocks. + for (auto& blk : *new_blocks) { + id2block_[blk->id()] = &*blk; + } + return true; +} + +bool InlinePass::IsInlinableFunctionCall(const Instruction* inst) { + if (inst->opcode() != SpvOp::SpvOpFunctionCall) return false; + const uint32_t calleeFnId = + inst->GetSingleWordOperand(kSpvFunctionCallFunctionId); + const auto ci = inlinable_.find(calleeFnId); + if (ci == inlinable_.cend()) return false; + + if (early_return_funcs_.find(calleeFnId) != early_return_funcs_.end()) { + // We rely on the merge-return pass to handle the early return case + // in advance. + std::string message = + "The function '" + id2function_[calleeFnId]->DefInst().PrettyPrint() + + "' could not be inlined because the return instruction " + "is not at the end of the function. This could be fixed by " + "running merge-return before inlining."; + consumer()(SPV_MSG_WARNING, "", {0, 0, 0}, message.c_str()); + return false; + } + + return true; +} + +void InlinePass::UpdateSucceedingPhis( + std::vector>& new_blocks) { + const auto firstBlk = new_blocks.begin(); + const auto lastBlk = new_blocks.end() - 1; + const uint32_t firstId = (*firstBlk)->id(); + const uint32_t lastId = (*lastBlk)->id(); + const BasicBlock& const_last_block = *lastBlk->get(); + const_last_block.ForEachSuccessorLabel( + [&firstId, &lastId, this](const uint32_t succ) { + BasicBlock* sbp = this->id2block_[succ]; + sbp->ForEachPhiInst([&firstId, &lastId](Instruction* phi) { + phi->ForEachInId([&firstId, &lastId](uint32_t* id) { + if (*id == firstId) *id = lastId; + }); + }); + }); +} + +bool InlinePass::HasNoReturnInLoop(Function* func) { + // If control not structured, do not do loop/return analysis + // TODO: Analyze returns in non-structured control flow + if (!context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) + return false; + const auto structured_analysis = context()->GetStructuredCFGAnalysis(); + // Search for returns in structured construct. + bool return_in_loop = false; + for (auto& blk : *func) { + auto terminal_ii = blk.cend(); + --terminal_ii; + if (spvOpcodeIsReturn(terminal_ii->opcode()) && + structured_analysis->ContainingLoop(blk.id()) != 0) { + return_in_loop = true; + break; + } + } + return !return_in_loop; +} + +void InlinePass::AnalyzeReturns(Function* func) { + // Analyze functions without a return in loop. + if (HasNoReturnInLoop(func)) { + no_return_in_loop_.insert(func->result_id()); + } + // Analyze functions with a return before its tail basic block. + for (auto& blk : *func) { + auto terminal_ii = blk.cend(); + --terminal_ii; + if (spvOpcodeIsReturn(terminal_ii->opcode()) && &blk != func->tail()) { + early_return_funcs_.insert(func->result_id()); + break; + } + } +} + +bool InlinePass::IsInlinableFunction(Function* func) { + // We can only inline a function if it has blocks. + if (func->cbegin() == func->cend()) return false; + + // Do not inline functions with DontInline flag. + if (func->control_mask() & SpvFunctionControlDontInlineMask) { + return false; + } + + // Do not inline functions with returns in loops. Currently early return + // functions are inlined by wrapping them in a one trip loop and implementing + // the returns as a branch to the loop's merge block. However, this can only + // done validly if the return was not in a loop in the original function. + // Also remember functions with multiple (early) returns. + AnalyzeReturns(func); + if (no_return_in_loop_.find(func->result_id()) == no_return_in_loop_.cend()) { + return false; + } + + if (func->IsRecursive()) { + return false; + } + + // Do not inline functions with an OpKill if they are called from a continue + // construct. If it is inlined into a continue construct it will generate + // invalid code. + bool func_is_called_from_continue = + funcs_called_from_continue_.count(func->result_id()) != 0; + + if (func_is_called_from_continue && ContainsKillOrTerminateInvocation(func)) { + return false; + } + + return true; +} + +bool InlinePass::ContainsKillOrTerminateInvocation(Function* func) const { + return !func->WhileEachInst([](Instruction* inst) { + const auto opcode = inst->opcode(); + return (opcode != SpvOpKill) && (opcode != SpvOpTerminateInvocation); + }); +} + +void InlinePass::InitializeInline() { + false_id_ = 0; + + // clear collections + id2function_.clear(); + id2block_.clear(); + inlinable_.clear(); + no_return_in_loop_.clear(); + early_return_funcs_.clear(); + funcs_called_from_continue_ = + context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue(); + + for (auto& fn : *get_module()) { + // Initialize function and block maps. + id2function_[fn.result_id()] = &fn; + for (auto& blk : fn) { + id2block_[blk.id()] = &blk; + } + // Compute inlinability + if (IsInlinableFunction(&fn)) inlinable_.insert(fn.result_id()); + } +} + +InlinePass::InlinePass() {} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/inline_pass.h b/third_party/spirv-tools/source/opt/inline_pass.h new file mode 100644 index 0000000..abe773a --- /dev/null +++ b/third_party/spirv-tools/source/opt/inline_pass.h @@ -0,0 +1,243 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INLINE_PASS_H_ +#define SOURCE_OPT_INLINE_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/debug_info_manager.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class InlinePass : public Pass { + using cbb_ptr = const BasicBlock*; + + public: + virtual ~InlinePass() = default; + + protected: + InlinePass(); + + // Add pointer to type to module and return resultId. Returns 0 if the type + // could not be created. + uint32_t AddPointerToType(uint32_t type_id, SpvStorageClass storage_class); + + // Add unconditional branch to labelId to end of block block_ptr. + void AddBranch(uint32_t labelId, std::unique_ptr* block_ptr); + + // Add conditional branch to end of block |block_ptr|. + void AddBranchCond(uint32_t cond_id, uint32_t true_id, uint32_t false_id, + std::unique_ptr* block_ptr); + + // Add unconditional branch to labelId to end of block block_ptr. + void AddLoopMerge(uint32_t merge_id, uint32_t continue_id, + std::unique_ptr* block_ptr); + + // Add store of valId to ptrId to end of block block_ptr. + void AddStore(uint32_t ptrId, uint32_t valId, + std::unique_ptr* block_ptr, + const Instruction* line_inst, const DebugScope& dbg_scope); + + // Add load of ptrId into resultId to end of block block_ptr. + void AddLoad(uint32_t typeId, uint32_t resultId, uint32_t ptrId, + std::unique_ptr* block_ptr, + const Instruction* line_inst, const DebugScope& dbg_scope); + + // Return new label. + std::unique_ptr NewLabel(uint32_t label_id); + + // Returns the id for the boolean false value. Looks in the module first + // and creates it if not found. Remembers it for future calls. Returns 0 if + // the value could not be created. + uint32_t GetFalseId(); + + // Map callee params to caller args + void MapParams(Function* calleeFn, BasicBlock::iterator call_inst_itr, + std::unordered_map* callee2caller); + + // Clone and map callee locals. Return true if successful. + bool CloneAndMapLocals(Function* calleeFn, + std::vector>* new_vars, + std::unordered_map* callee2caller, + analysis::DebugInlinedAtContext* inlined_at_ctx); + + // Create return variable for callee clone code. The return type of + // |calleeFn| must not be void. Returns the id of the return variable if + // created. Returns 0 if the return variable could not be created. + uint32_t CreateReturnVar(Function* calleeFn, + std::vector>* new_vars); + + // Return true if instruction must be in the same block that its result + // is used. + bool IsSameBlockOp(const Instruction* inst) const; + + // Clone operands which must be in same block as consumer instructions. + // Look in preCallSB for instructions that need cloning. Look in + // postCallSB for instructions already cloned. Add cloned instruction + // to postCallSB. + bool CloneSameBlockOps(std::unique_ptr* inst, + std::unordered_map* postCallSB, + std::unordered_map* preCallSB, + std::unique_ptr* block_ptr); + + // Return in new_blocks the result of inlining the call at call_inst_itr + // within its block at call_block_itr. The block at call_block_itr can + // just be replaced with the blocks in new_blocks. Any additional branches + // are avoided. Debug instructions are cloned along with their callee + // instructions. Early returns are replaced by a store to a local return + // variable and a branch to a (created) exit block where the local variable + // is returned. Formal parameters are trivially mapped to their actual + // parameters. Note that the first block in new_blocks retains the label + // of the original calling block. Also note that if an exit block is + // created, it is the last block of new_blocks. + // + // Also return in new_vars additional OpVariable instructions required by + // and to be inserted into the caller function after the block at + // call_block_itr is replaced with new_blocks. + // + // Returns true if successful. + bool GenInlineCode(std::vector>* new_blocks, + std::vector>* new_vars, + BasicBlock::iterator call_inst_itr, + UptrVectorIterator call_block_itr); + + // Return true if |inst| is a function call that can be inlined. + bool IsInlinableFunctionCall(const Instruction* inst); + + // Return true if |func| has no return in a loop. The current analysis + // requires structured control flow, so return false if control flow not + // structured ie. module is not a shader. + bool HasNoReturnInLoop(Function* func); + + // Find all functions with multiple returns and no returns in loops + void AnalyzeReturns(Function* func); + + // Return true if |func| is a function that can be inlined. + bool IsInlinableFunction(Function* func); + + // Returns true if |func| contains an OpKill or OpTerminateInvocation + // instruction. + bool ContainsKillOrTerminateInvocation(Function* func) const; + + // Update phis in succeeding blocks to point to new last block + void UpdateSucceedingPhis( + std::vector>& new_blocks); + + // Initialize state for optimization of |module| + void InitializeInline(); + + // Map from function's result id to function. + std::unordered_map id2function_; + + // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt + // CFG. It has functionality not present in CFG. Consolidate. + std::unordered_map id2block_; + + // Set of ids of functions with early return. + std::set early_return_funcs_; + + // Set of ids of functions with no returns in loop + std::set no_return_in_loop_; + + // Set of ids of inlinable functions + std::set inlinable_; + + // result id for OpConstantFalse + uint32_t false_id_; + + // Set of functions that are originally called directly or indirectly from a + // continue construct. + std::unordered_set funcs_called_from_continue_; + + private: + // Moves instructions of the caller function up to the call instruction + // to |new_blk_ptr|. + void MoveInstsBeforeEntryBlock( + std::unordered_map* preCallSB, + BasicBlock* new_blk_ptr, BasicBlock::iterator call_inst_itr, + UptrVectorIterator call_block_itr); + + // Returns a new guard block after adding a branch to the end of + // |new_blocks|. + std::unique_ptr AddGuardBlock( + std::vector>* new_blocks, + std::unordered_map* callee2caller, + std::unique_ptr new_blk_ptr, uint32_t entry_blk_label_id); + + // Add store instructions for initializers of variables. + InstructionList::iterator AddStoresForVariableInitializers( + const std::unordered_map& callee2caller, + analysis::DebugInlinedAtContext* inlined_at_ctx, + std::unique_ptr* new_blk_ptr, + UptrVectorIterator callee_block_itr); + + // Inlines a single instruction of the callee function. + bool InlineSingleInstruction( + const std::unordered_map& callee2caller, + BasicBlock* new_blk_ptr, const Instruction* inst, + uint32_t dbg_inlined_at); + + // Inlines the return instruction of the callee function. + std::unique_ptr InlineReturn( + const std::unordered_map& callee2caller, + std::vector>* new_blocks, + std::unique_ptr new_blk_ptr, + analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn, + const Instruction* inst, uint32_t returnVarId); + + // Inlines the entry block of the callee function. + bool InlineEntryBlock( + const std::unordered_map& callee2caller, + std::unique_ptr* new_blk_ptr, + UptrVectorIterator callee_first_block, + analysis::DebugInlinedAtContext* inlined_at_ctx); + + // Inlines basic blocks of the callee function other than the entry basic + // block. + std::unique_ptr InlineBasicBlocks( + std::vector>* new_blocks, + const std::unordered_map& callee2caller, + std::unique_ptr new_blk_ptr, + analysis::DebugInlinedAtContext* inlined_at_ctx, Function* calleeFn); + + // Moves instructions of the caller function after the call instruction + // to |new_blk_ptr|. + bool MoveCallerInstsAfterFunctionCall( + std::unordered_map* preCallSB, + std::unordered_map* postCallSB, + std::unique_ptr* new_blk_ptr, + BasicBlock::iterator call_inst_itr, bool multiBlocks); + + // Move the OpLoopMerge from the last block back to the first. + void MoveLoopMergeInstToFirstBlock( + std::vector>* new_blocks); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INLINE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/inst_bindless_check_pass.cpp b/third_party/spirv-tools/source/opt/inst_bindless_check_pass.cpp new file mode 100644 index 0000000..64d389c --- /dev/null +++ b/third_party/spirv-tools/source/opt/inst_bindless_check_pass.cpp @@ -0,0 +1,647 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "inst_bindless_check_pass.h" + +namespace { + +// Input Operand Indices +static const int kSpvImageSampleImageIdInIdx = 0; +static const int kSpvSampledImageImageIdInIdx = 0; +static const int kSpvSampledImageSamplerIdInIdx = 1; +static const int kSpvImageSampledImageIdInIdx = 0; +static const int kSpvLoadPtrIdInIdx = 0; +static const int kSpvAccessChainBaseIdInIdx = 0; +static const int kSpvAccessChainIndex0IdInIdx = 1; +static const int kSpvTypeArrayLengthIdInIdx = 1; +static const int kSpvConstantValueInIdx = 0; +static const int kSpvVariableStorageClassInIdx = 0; + +} // anonymous namespace + +// Avoid unused variable warning/error on Linux +#ifndef NDEBUG +#define USE_ASSERT(x) assert(x) +#else +#define USE_ASSERT(x) ((void)(x)) +#endif + +namespace spvtools { +namespace opt { + +uint32_t InstBindlessCheckPass::GenDebugReadLength( + uint32_t var_id, InstructionBuilder* builder) { + uint32_t desc_set_idx = + var2desc_set_[var_id] + kDebugInputBindlessOffsetLengths; + uint32_t desc_set_idx_id = builder->GetUintConstantId(desc_set_idx); + uint32_t binding_idx_id = builder->GetUintConstantId(var2binding_[var_id]); + return GenDebugDirectRead({desc_set_idx_id, binding_idx_id}, builder); +} + +uint32_t InstBindlessCheckPass::GenDebugReadInit(uint32_t var_id, + uint32_t desc_idx_id, + InstructionBuilder* builder) { + uint32_t binding_idx_id = builder->GetUintConstantId(var2binding_[var_id]); + uint32_t u_desc_idx_id = GenUintCastCode(desc_idx_id, builder); + // If desc index checking is not enabled, we know the offset of initialization + // entries is 1, so we can avoid loading this value and just add 1 to the + // descriptor set. + if (!desc_idx_enabled_) { + uint32_t desc_set_idx_id = + builder->GetUintConstantId(var2desc_set_[var_id] + 1); + return GenDebugDirectRead({desc_set_idx_id, binding_idx_id, u_desc_idx_id}, + builder); + } else { + uint32_t desc_set_base_id = + builder->GetUintConstantId(kDebugInputBindlessInitOffset); + uint32_t desc_set_idx_id = + builder->GetUintConstantId(var2desc_set_[var_id]); + return GenDebugDirectRead( + {desc_set_base_id, desc_set_idx_id, binding_idx_id, u_desc_idx_id}, + builder); + } +} + +uint32_t InstBindlessCheckPass::CloneOriginalReference( + ref_analysis* ref, InstructionBuilder* builder) { + // If original is image based, start by cloning descriptor load + uint32_t new_image_id = 0; + if (ref->desc_load_id != 0) { + Instruction* desc_load_inst = get_def_use_mgr()->GetDef(ref->desc_load_id); + Instruction* new_load_inst = builder->AddLoad( + desc_load_inst->type_id(), + desc_load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx)); + uid2offset_[new_load_inst->unique_id()] = + uid2offset_[desc_load_inst->unique_id()]; + uint32_t new_load_id = new_load_inst->result_id(); + get_decoration_mgr()->CloneDecorations(desc_load_inst->result_id(), + new_load_id); + new_image_id = new_load_id; + // Clone Image/SampledImage with new load, if needed + if (ref->image_id != 0) { + Instruction* image_inst = get_def_use_mgr()->GetDef(ref->image_id); + if (image_inst->opcode() == SpvOp::SpvOpSampledImage) { + Instruction* new_image_inst = builder->AddBinaryOp( + image_inst->type_id(), SpvOpSampledImage, new_load_id, + image_inst->GetSingleWordInOperand(kSpvSampledImageSamplerIdInIdx)); + uid2offset_[new_image_inst->unique_id()] = + uid2offset_[image_inst->unique_id()]; + new_image_id = new_image_inst->result_id(); + } else { + assert(image_inst->opcode() == SpvOp::SpvOpImage && + "expecting OpImage"); + Instruction* new_image_inst = + builder->AddUnaryOp(image_inst->type_id(), SpvOpImage, new_load_id); + uid2offset_[new_image_inst->unique_id()] = + uid2offset_[image_inst->unique_id()]; + new_image_id = new_image_inst->result_id(); + } + get_decoration_mgr()->CloneDecorations(ref->image_id, new_image_id); + } + } + // Clone original reference + std::unique_ptr new_ref_inst(ref->ref_inst->Clone(context())); + uint32_t ref_result_id = ref->ref_inst->result_id(); + uint32_t new_ref_id = 0; + if (ref_result_id != 0) { + new_ref_id = TakeNextId(); + new_ref_inst->SetResultId(new_ref_id); + } + // Update new ref with new image if created + if (new_image_id != 0) + new_ref_inst->SetInOperand(kSpvImageSampleImageIdInIdx, {new_image_id}); + // Register new reference and add to new block + Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst)); + uid2offset_[added_inst->unique_id()] = + uid2offset_[ref->ref_inst->unique_id()]; + if (new_ref_id != 0) + get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id); + return new_ref_id; +} + +uint32_t InstBindlessCheckPass::GetImageId(Instruction* inst) { + switch (inst->opcode()) { + case SpvOp::SpvOpImageSampleImplicitLod: + case SpvOp::SpvOpImageSampleExplicitLod: + case SpvOp::SpvOpImageSampleDrefImplicitLod: + case SpvOp::SpvOpImageSampleDrefExplicitLod: + case SpvOp::SpvOpImageSampleProjImplicitLod: + case SpvOp::SpvOpImageSampleProjExplicitLod: + case SpvOp::SpvOpImageSampleProjDrefImplicitLod: + case SpvOp::SpvOpImageSampleProjDrefExplicitLod: + case SpvOp::SpvOpImageGather: + case SpvOp::SpvOpImageDrefGather: + case SpvOp::SpvOpImageQueryLod: + case SpvOp::SpvOpImageSparseSampleImplicitLod: + case SpvOp::SpvOpImageSparseSampleExplicitLod: + case SpvOp::SpvOpImageSparseSampleDrefImplicitLod: + case SpvOp::SpvOpImageSparseSampleDrefExplicitLod: + case SpvOp::SpvOpImageSparseSampleProjImplicitLod: + case SpvOp::SpvOpImageSparseSampleProjExplicitLod: + case SpvOp::SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOp::SpvOpImageSparseSampleProjDrefExplicitLod: + case SpvOp::SpvOpImageSparseGather: + case SpvOp::SpvOpImageSparseDrefGather: + case SpvOp::SpvOpImageFetch: + case SpvOp::SpvOpImageRead: + case SpvOp::SpvOpImageQueryFormat: + case SpvOp::SpvOpImageQueryOrder: + case SpvOp::SpvOpImageQuerySizeLod: + case SpvOp::SpvOpImageQuerySize: + case SpvOp::SpvOpImageQueryLevels: + case SpvOp::SpvOpImageQuerySamples: + case SpvOp::SpvOpImageSparseFetch: + case SpvOp::SpvOpImageSparseRead: + case SpvOp::SpvOpImageWrite: + return inst->GetSingleWordInOperand(kSpvImageSampleImageIdInIdx); + default: + break; + } + return 0; +} + +Instruction* InstBindlessCheckPass::GetPointeeTypeInst(Instruction* ptr_inst) { + uint32_t pte_ty_id = GetPointeeTypeId(ptr_inst); + return get_def_use_mgr()->GetDef(pte_ty_id); +} + +bool InstBindlessCheckPass::AnalyzeDescriptorReference(Instruction* ref_inst, + ref_analysis* ref) { + ref->ref_inst = ref_inst; + if (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) { + ref->desc_load_id = 0; + ref->ptr_id = ref_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx); + Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref->ptr_id); + if (ptr_inst->opcode() != SpvOp::SpvOpAccessChain) return false; + ref->var_id = ptr_inst->GetSingleWordInOperand(kSpvAccessChainBaseIdInIdx); + Instruction* var_inst = get_def_use_mgr()->GetDef(ref->var_id); + if (var_inst->opcode() != SpvOp::SpvOpVariable) return false; + uint32_t storage_class = + var_inst->GetSingleWordInOperand(kSpvVariableStorageClassInIdx); + switch (storage_class) { + case SpvStorageClassUniform: + case SpvStorageClassUniformConstant: + case SpvStorageClassStorageBuffer: + break; + default: + return false; + break; + } + Instruction* desc_type_inst = GetPointeeTypeInst(var_inst); + switch (desc_type_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + // A load through a descriptor array will have at least 3 operands. We + // do not want to instrument loads of descriptors here which are part of + // an image-based reference. + if (ptr_inst->NumInOperands() < 3) return false; + ref->desc_idx_id = + ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx); + break; + default: + ref->desc_idx_id = 0; + break; + } + return true; + } + // Reference is not load or store. If not an image-based reference, return. + ref->image_id = GetImageId(ref_inst); + if (ref->image_id == 0) return false; + Instruction* image_inst = get_def_use_mgr()->GetDef(ref->image_id); + Instruction* desc_load_inst = nullptr; + if (image_inst->opcode() == SpvOp::SpvOpSampledImage) { + ref->desc_load_id = + image_inst->GetSingleWordInOperand(kSpvSampledImageImageIdInIdx); + desc_load_inst = get_def_use_mgr()->GetDef(ref->desc_load_id); + } else if (image_inst->opcode() == SpvOp::SpvOpImage) { + ref->desc_load_id = + image_inst->GetSingleWordInOperand(kSpvImageSampledImageIdInIdx); + desc_load_inst = get_def_use_mgr()->GetDef(ref->desc_load_id); + } else { + ref->desc_load_id = ref->image_id; + desc_load_inst = image_inst; + ref->image_id = 0; + } + if (desc_load_inst->opcode() != SpvOp::SpvOpLoad) { + // TODO(greg-lunarg): Handle additional possibilities? + return false; + } + ref->ptr_id = desc_load_inst->GetSingleWordInOperand(kSpvLoadPtrIdInIdx); + Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref->ptr_id); + if (ptr_inst->opcode() == SpvOp::SpvOpVariable) { + ref->desc_idx_id = 0; + ref->var_id = ref->ptr_id; + } else if (ptr_inst->opcode() == SpvOp::SpvOpAccessChain) { + if (ptr_inst->NumInOperands() != 2) { + assert(false && "unexpected bindless index number"); + return false; + } + ref->desc_idx_id = + ptr_inst->GetSingleWordInOperand(kSpvAccessChainIndex0IdInIdx); + ref->var_id = ptr_inst->GetSingleWordInOperand(kSpvAccessChainBaseIdInIdx); + Instruction* var_inst = get_def_use_mgr()->GetDef(ref->var_id); + if (var_inst->opcode() != SpvOpVariable) { + assert(false && "unexpected bindless base"); + return false; + } + } else { + // TODO(greg-lunarg): Handle additional possibilities? + return false; + } + return true; +} + +uint32_t InstBindlessCheckPass::FindStride(uint32_t ty_id, + uint32_t stride_deco) { + uint32_t stride = 0xdeadbeef; + bool found = !get_decoration_mgr()->WhileEachDecoration( + ty_id, stride_deco, [&stride](const Instruction& deco_inst) { + stride = deco_inst.GetSingleWordInOperand(2u); + return false; + }); + USE_ASSERT(found && "stride not found"); + return stride; +} + +uint32_t InstBindlessCheckPass::ByteSize(uint32_t ty_id) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + const analysis::Type* sz_ty = type_mgr->GetType(ty_id); + if (sz_ty->kind() == analysis::Type::kPointer) { + // Assuming PhysicalStorageBuffer pointer + return 8; + } + uint32_t size = 1; + if (sz_ty->kind() == analysis::Type::kMatrix) { + const analysis::Matrix* m_ty = sz_ty->AsMatrix(); + size = m_ty->element_count() * size; + uint32_t stride = FindStride(ty_id, SpvDecorationMatrixStride); + if (stride != 0) return size * stride; + sz_ty = m_ty->element_type(); + } + if (sz_ty->kind() == analysis::Type::kVector) { + const analysis::Vector* v_ty = sz_ty->AsVector(); + size = v_ty->element_count() * size; + sz_ty = v_ty->element_type(); + } + switch (sz_ty->kind()) { + case analysis::Type::kFloat: { + const analysis::Float* f_ty = sz_ty->AsFloat(); + size *= f_ty->width(); + } break; + case analysis::Type::kInteger: { + const analysis::Integer* i_ty = sz_ty->AsInteger(); + size *= i_ty->width(); + } break; + default: { assert(false && "unexpected type"); } break; + } + size /= 8; + return size; +} + +uint32_t InstBindlessCheckPass::GenLastByteIdx(ref_analysis* ref, + InstructionBuilder* builder) { + // Find outermost buffer type and its access chain index + Instruction* var_inst = get_def_use_mgr()->GetDef(ref->var_id); + Instruction* desc_ty_inst = GetPointeeTypeInst(var_inst); + uint32_t buff_ty_id; + uint32_t ac_in_idx = 1; + switch (desc_ty_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + buff_ty_id = desc_ty_inst->GetSingleWordInOperand(0); + ++ac_in_idx; + break; + default: + assert(desc_ty_inst->opcode() == SpvOpTypeStruct && + "unexpected descriptor type"); + buff_ty_id = desc_ty_inst->result_id(); + break; + } + // Process remaining access chain indices + Instruction* ac_inst = get_def_use_mgr()->GetDef(ref->ptr_id); + uint32_t curr_ty_id = buff_ty_id; + uint32_t sum_id = 0; + while (ac_in_idx < ac_inst->NumInOperands()) { + uint32_t curr_idx_id = ac_inst->GetSingleWordInOperand(ac_in_idx); + Instruction* curr_idx_inst = get_def_use_mgr()->GetDef(curr_idx_id); + Instruction* curr_ty_inst = get_def_use_mgr()->GetDef(curr_ty_id); + uint32_t curr_offset_id = 0; + switch (curr_ty_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeMatrix: { + // Get array/matrix stride and multiply by current index + uint32_t stride_deco = (curr_ty_inst->opcode() == SpvOpTypeMatrix) + ? SpvDecorationMatrixStride + : SpvDecorationArrayStride; + uint32_t arr_stride = FindStride(curr_ty_id, stride_deco); + uint32_t arr_stride_id = builder->GetUintConstantId(arr_stride); + uint32_t curr_idx_32b_id = Gen32BitCvtCode(curr_idx_id, builder); + Instruction* curr_offset_inst = builder->AddBinaryOp( + GetUintId(), SpvOpIMul, arr_stride_id, curr_idx_32b_id); + curr_offset_id = curr_offset_inst->result_id(); + // Get element type for next step + curr_ty_id = curr_ty_inst->GetSingleWordInOperand(0); + } break; + case SpvOpTypeVector: { + // Stride is size of component type + uint32_t comp_ty_id = curr_ty_inst->GetSingleWordInOperand(0u); + uint32_t vec_stride = ByteSize(comp_ty_id); + uint32_t vec_stride_id = builder->GetUintConstantId(vec_stride); + uint32_t curr_idx_32b_id = Gen32BitCvtCode(curr_idx_id, builder); + Instruction* curr_offset_inst = builder->AddBinaryOp( + GetUintId(), SpvOpIMul, vec_stride_id, curr_idx_32b_id); + curr_offset_id = curr_offset_inst->result_id(); + // Get element type for next step + curr_ty_id = comp_ty_id; + } break; + case SpvOpTypeStruct: { + // Get buffer byte offset for the referenced member + assert(curr_idx_inst->opcode() == SpvOpConstant && + "unexpected struct index"); + uint32_t member_idx = curr_idx_inst->GetSingleWordInOperand(0); + uint32_t member_offset = 0xdeadbeef; + bool found = !get_decoration_mgr()->WhileEachDecoration( + curr_ty_id, SpvDecorationOffset, + [&member_idx, &member_offset](const Instruction& deco_inst) { + if (deco_inst.GetSingleWordInOperand(1u) != member_idx) + return true; + member_offset = deco_inst.GetSingleWordInOperand(3u); + return false; + }); + USE_ASSERT(found && "member offset not found"); + curr_offset_id = builder->GetUintConstantId(member_offset); + // Get element type for next step + curr_ty_id = curr_ty_inst->GetSingleWordInOperand(member_idx); + } break; + default: { assert(false && "unexpected non-composite type"); } break; + } + if (sum_id == 0) + sum_id = curr_offset_id; + else { + Instruction* sum_inst = + builder->AddBinaryOp(GetUintId(), SpvOpIAdd, sum_id, curr_offset_id); + sum_id = sum_inst->result_id(); + } + ++ac_in_idx; + } + // Add in offset of last byte of referenced object + uint32_t bsize = ByteSize(curr_ty_id); + uint32_t last = bsize - 1; + uint32_t last_id = builder->GetUintConstantId(last); + Instruction* sum_inst = + builder->AddBinaryOp(GetUintId(), SpvOpIAdd, sum_id, last_id); + return sum_inst->result_id(); +} + +void InstBindlessCheckPass::GenCheckCode( + uint32_t check_id, uint32_t error_id, uint32_t offset_id, + uint32_t length_id, uint32_t stage_idx, ref_analysis* ref, + std::vector>* new_blocks) { + BasicBlock* back_blk_ptr = &*new_blocks->back(); + InstructionBuilder builder( + context(), back_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Gen conditional branch on check_id. Valid branch generates original + // reference. Invalid generates debug output and zero result (if needed). + uint32_t merge_blk_id = TakeNextId(); + uint32_t valid_blk_id = TakeNextId(); + uint32_t invalid_blk_id = TakeNextId(); + std::unique_ptr merge_label(NewLabel(merge_blk_id)); + std::unique_ptr valid_label(NewLabel(valid_blk_id)); + std::unique_ptr invalid_label(NewLabel(invalid_blk_id)); + (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id, + merge_blk_id, SpvSelectionControlMaskNone); + // Gen valid bounds branch + std::unique_ptr new_blk_ptr( + new BasicBlock(std::move(valid_label))); + builder.SetInsertPoint(&*new_blk_ptr); + uint32_t new_ref_id = CloneOriginalReference(ref, &builder); + (void)builder.AddBranch(merge_blk_id); + new_blocks->push_back(std::move(new_blk_ptr)); + // Gen invalid block + new_blk_ptr.reset(new BasicBlock(std::move(invalid_label))); + builder.SetInsertPoint(&*new_blk_ptr); + uint32_t u_index_id = GenUintCastCode(ref->desc_idx_id, &builder); + if (offset_id != 0) { + // Buffer OOB + uint32_t u_offset_id = GenUintCastCode(offset_id, &builder); + uint32_t u_length_id = GenUintCastCode(length_id, &builder); + GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx, + {error_id, u_index_id, u_offset_id, u_length_id}, + &builder); + } else if (buffer_bounds_enabled_) { + // Uninitialized Descriptor - Return additional unused zero so all error + // modes will use same debug stream write function + uint32_t u_length_id = GenUintCastCode(length_id, &builder); + GenDebugStreamWrite( + uid2offset_[ref->ref_inst->unique_id()], stage_idx, + {error_id, u_index_id, u_length_id, builder.GetUintConstantId(0)}, + &builder); + } else { + // Uninitialized Descriptor - Normal error return + uint32_t u_length_id = GenUintCastCode(length_id, &builder); + GenDebugStreamWrite(uid2offset_[ref->ref_inst->unique_id()], stage_idx, + {error_id, u_index_id, u_length_id}, &builder); + } + // Remember last invalid block id + uint32_t last_invalid_blk_id = new_blk_ptr->GetLabelInst()->result_id(); + // Gen zero for invalid reference + uint32_t ref_type_id = ref->ref_inst->type_id(); + (void)builder.AddBranch(merge_blk_id); + new_blocks->push_back(std::move(new_blk_ptr)); + // Gen merge block + new_blk_ptr.reset(new BasicBlock(std::move(merge_label))); + builder.SetInsertPoint(&*new_blk_ptr); + // Gen phi of new reference and zero, if necessary, and replace the + // result id of the original reference with that of the Phi. Kill original + // reference. + if (new_ref_id != 0) { + Instruction* phi_inst = builder.AddPhi( + ref_type_id, {new_ref_id, valid_blk_id, GetNullId(ref_type_id), + last_invalid_blk_id}); + context()->ReplaceAllUsesWith(ref->ref_inst->result_id(), + phi_inst->result_id()); + } + new_blocks->push_back(std::move(new_blk_ptr)); + context()->KillInst(ref->ref_inst); +} + +void InstBindlessCheckPass::GenDescIdxCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + // Look for reference through indexed descriptor. If found, analyze and + // save components. If not, return. + ref_analysis ref; + if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return; + Instruction* ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id); + if (ptr_inst->opcode() != SpvOp::SpvOpAccessChain) return; + // If index and bound both compile-time constants and index < bound, + // return without changing + Instruction* var_inst = get_def_use_mgr()->GetDef(ref.var_id); + Instruction* desc_type_inst = GetPointeeTypeInst(var_inst); + uint32_t length_id = 0; + if (desc_type_inst->opcode() == SpvOpTypeArray) { + length_id = + desc_type_inst->GetSingleWordInOperand(kSpvTypeArrayLengthIdInIdx); + Instruction* index_inst = get_def_use_mgr()->GetDef(ref.desc_idx_id); + Instruction* length_inst = get_def_use_mgr()->GetDef(length_id); + if (index_inst->opcode() == SpvOpConstant && + length_inst->opcode() == SpvOpConstant && + index_inst->GetSingleWordInOperand(kSpvConstantValueInIdx) < + length_inst->GetSingleWordInOperand(kSpvConstantValueInIdx)) + return; + } else if (!desc_idx_enabled_ || + desc_type_inst->opcode() != SpvOpTypeRuntimeArray) { + return; + } + // Move original block's preceding instructions into first new block + std::unique_ptr new_blk_ptr; + MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + new_blocks->push_back(std::move(new_blk_ptr)); + uint32_t error_id = builder.GetUintConstantId(kInstErrorBindlessBounds); + // If length id not yet set, descriptor array is runtime size so + // generate load of length from stage's debug input buffer. + if (length_id == 0) { + assert(desc_type_inst->opcode() == SpvOpTypeRuntimeArray && + "unexpected bindless type"); + length_id = GenDebugReadLength(ref.var_id, &builder); + } + // Generate full runtime bounds test code with true branch + // being full reference and false branch being debug output and zero + // for the referenced value. + uint32_t desc_idx_32b_id = Gen32BitCvtCode(ref.desc_idx_id, &builder); + uint32_t length_32b_id = Gen32BitCvtCode(length_id, &builder); + Instruction* ult_inst = builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, + desc_idx_32b_id, length_32b_id); + ref.desc_idx_id = desc_idx_32b_id; + GenCheckCode(ult_inst->result_id(), error_id, 0u, length_id, stage_idx, &ref, + new_blocks); + // Move original block's remaining code into remainder/merge block and add + // to new blocks + BasicBlock* back_blk_ptr = &*new_blocks->back(); + MovePostludeCode(ref_block_itr, back_blk_ptr); +} + +void InstBindlessCheckPass::GenDescInitCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + // Look for reference through descriptor. If not, return. + ref_analysis ref; + if (!AnalyzeDescriptorReference(&*ref_inst_itr, &ref)) return; + // Determine if we can only do initialization check + bool init_check = false; + if (ref.desc_load_id != 0 || !buffer_bounds_enabled_) { + init_check = true; + } else { + // For now, only do bounds check for non-aggregate types. Otherwise + // just do descriptor initialization check. + // TODO(greg-lunarg): Do bounds check for aggregate loads and stores + Instruction* ref_ptr_inst = get_def_use_mgr()->GetDef(ref.ptr_id); + Instruction* pte_type_inst = GetPointeeTypeInst(ref_ptr_inst); + uint32_t pte_type_op = pte_type_inst->opcode(); + if (pte_type_op == SpvOpTypeArray || pte_type_op == SpvOpTypeRuntimeArray || + pte_type_op == SpvOpTypeStruct) + init_check = true; + } + // If initialization check and not enabled, return + if (init_check && !desc_init_enabled_) return; + // Move original block's preceding instructions into first new block + std::unique_ptr new_blk_ptr; + MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + new_blocks->push_back(std::move(new_blk_ptr)); + // If initialization check, use reference value of zero. + // Else use the index of the last byte referenced. + uint32_t ref_id = init_check ? builder.GetUintConstantId(0u) + : GenLastByteIdx(&ref, &builder); + // Read initialization/bounds from debug input buffer. If index id not yet + // set, binding is single descriptor, so set index to constant 0. + if (ref.desc_idx_id == 0) ref.desc_idx_id = builder.GetUintConstantId(0u); + uint32_t init_id = GenDebugReadInit(ref.var_id, ref.desc_idx_id, &builder); + // Generate runtime initialization/bounds test code with true branch + // being full reference and false branch being debug output and zero + // for the referenced value. + Instruction* ult_inst = + builder.AddBinaryOp(GetBoolId(), SpvOpULessThan, ref_id, init_id); + uint32_t error = + init_check ? kInstErrorBindlessUninit : kInstErrorBindlessBuffOOB; + uint32_t error_id = builder.GetUintConstantId(error); + GenCheckCode(ult_inst->result_id(), error_id, init_check ? 0 : ref_id, + init_check ? builder.GetUintConstantId(0u) : init_id, stage_idx, + &ref, new_blocks); + // Move original block's remaining code into remainder/merge block and add + // to new blocks + BasicBlock* back_blk_ptr = &*new_blocks->back(); + MovePostludeCode(ref_block_itr, back_blk_ptr); +} + +void InstBindlessCheckPass::InitializeInstBindlessCheck() { + // Initialize base class + InitializeInstrument(); + // If runtime array length support enabled, create variable mappings. Length + // support is always enabled if descriptor init check is enabled. + if (desc_idx_enabled_ || buffer_bounds_enabled_) + for (auto& anno : get_module()->annotations()) + if (anno.opcode() == SpvOpDecorate) { + if (anno.GetSingleWordInOperand(1u) == SpvDecorationDescriptorSet) + var2desc_set_[anno.GetSingleWordInOperand(0u)] = + anno.GetSingleWordInOperand(2u); + else if (anno.GetSingleWordInOperand(1u) == SpvDecorationBinding) + var2binding_[anno.GetSingleWordInOperand(0u)] = + anno.GetSingleWordInOperand(2u); + } +} + +Pass::Status InstBindlessCheckPass::ProcessImpl() { + // Perform bindless bounds check on each entry point function in module + InstProcessFunction pfn = + [this](BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + return GenDescIdxCheckCode(ref_inst_itr, ref_block_itr, stage_idx, + new_blocks); + }; + bool modified = InstProcessEntryPointCallTree(pfn); + if (desc_init_enabled_ || buffer_bounds_enabled_) { + // Perform descriptor initialization check on each entry point function in + // module + pfn = [this](BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, + uint32_t stage_idx, + std::vector>* new_blocks) { + return GenDescInitCheckCode(ref_inst_itr, ref_block_itr, stage_idx, + new_blocks); + }; + modified |= InstProcessEntryPointCallTree(pfn); + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Pass::Status InstBindlessCheckPass::Process() { + InitializeInstBindlessCheck(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/inst_bindless_check_pass.h b/third_party/spirv-tools/source/opt/inst_bindless_check_pass.h new file mode 100644 index 0000000..50dfd95 --- /dev/null +++ b/third_party/spirv-tools/source/opt/inst_bindless_check_pass.h @@ -0,0 +1,198 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_INST_BINDLESS_CHECK_PASS_H_ +#define LIBSPIRV_OPT_INST_BINDLESS_CHECK_PASS_H_ + +#include "instrument_pass.h" + +namespace spvtools { +namespace opt { + +// This class/pass is designed to support the bindless (descriptor indexing) +// GPU-assisted validation layer of +// https://github.com/KhronosGroup/Vulkan-ValidationLayers. Its internal and +// external design may change as the layer evolves. +class InstBindlessCheckPass : public InstrumentPass { + public: + // Old interface to support testing pre-buffer-overrun capability + InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id, + bool desc_idx_enable, bool desc_init_enable) + : InstrumentPass(desc_set, shader_id, kInstValidationIdBindless, false), + desc_idx_enabled_(desc_idx_enable), + desc_init_enabled_(desc_init_enable), + buffer_bounds_enabled_(false) {} + + // New interface supporting buffer overrun checking + InstBindlessCheckPass(uint32_t desc_set, uint32_t shader_id, + bool desc_idx_enable, bool desc_init_enable, + bool buffer_bounds_enable) + : InstrumentPass( + desc_set, shader_id, kInstValidationIdBindless, + desc_idx_enable || desc_init_enable || buffer_bounds_enable), + desc_idx_enabled_(desc_idx_enable), + desc_init_enabled_(desc_init_enable), + buffer_bounds_enabled_(buffer_bounds_enable) {} + + ~InstBindlessCheckPass() override = default; + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "inst-bindless-check-pass"; } + + private: + // These functions do bindless checking instrumentation on a single + // instruction which references through a descriptor (ie references into an + // image or buffer). Refer to Vulkan API for further information on + // descriptors. GenDescIdxCheckCode checks that an index into a descriptor + // array (array of images or buffers) is in-bounds. GenDescInitCheckCode + // checks that the referenced descriptor has been initialized, if the + // SPV_EXT_descriptor_indexing extension is enabled, and initialized large + // enough to handle the reference, if RobustBufferAccess is disabled. + // + // The functions are designed to be passed to + // InstrumentPass::InstProcessEntryPointCallTree(), which applies the + // function to each instruction in a module and replaces the instruction + // if warranted. + // + // If |ref_inst_itr| is a bindless reference, return in |new_blocks| the + // result of instrumenting it with validation code within its block at + // |ref_block_itr|. The validation code first executes a check for the + // specific condition called for. If the check passes, it executes + // the remainder of the reference, otherwise writes a record to the debug + // output buffer stream including |function_idx, instruction_idx, stage_idx| + // and replaces the reference with the null value of the original type. The + // block at |ref_block_itr| can just be replaced with the blocks in + // |new_blocks|, which will contain at least two blocks. The last block will + // comprise all instructions following |ref_inst_itr|, + // preceded by a phi instruction. + // + // These instrumentation functions utilize GenDebugDirectRead() to read data + // from the debug input buffer, specifically the lengths of variable length + // descriptor arrays, and the initialization status of each descriptor. + // The format of the debug input buffer is documented in instrument.hpp. + // + // These instrumentation functions utilize GenDebugStreamWrite() to write its + // error records. The validation-specific part of the error record will + // have the format: + // + // Validation Error Code (=kInstErrorBindlessBounds) + // Descriptor Index + // Descriptor Array Size + // + // The Descriptor Index is the index which has been determined to be + // out-of-bounds. + // + // The Descriptor Array Size is the size of the descriptor array which was + // indexed. + void GenDescIdxCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks); + + void GenDescInitCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks); + + // Generate instructions into |builder| to read length of runtime descriptor + // array |var_id| from debug input buffer and return id of value. + uint32_t GenDebugReadLength(uint32_t var_id, InstructionBuilder* builder); + + // Generate instructions into |builder| to read initialization status of + // descriptor array |image_id| at |index_id| from debug input buffer and + // return id of value. + uint32_t GenDebugReadInit(uint32_t image_id, uint32_t index_id, + InstructionBuilder* builder); + + // Analysis data for descriptor reference components, generated by + // AnalyzeDescriptorReference. It is necessary and sufficient for further + // analysis and regeneration of the reference. + typedef struct ref_analysis { + uint32_t desc_load_id; + uint32_t image_id; + uint32_t load_id; + uint32_t ptr_id; + uint32_t var_id; + uint32_t desc_idx_id; + Instruction* ref_inst; + } ref_analysis; + + // Return size of type |ty_id| in bytes. + uint32_t ByteSize(uint32_t ty_id); + + // Return stride of type |ty_id| with decoration |stride_deco|. Return 0 + // if not found + uint32_t FindStride(uint32_t ty_id, uint32_t stride_deco); + + // Generate index of last byte referenced by buffer reference |ref| + uint32_t GenLastByteIdx(ref_analysis* ref, InstructionBuilder* builder); + + // Clone original original reference encapsulated by |ref| into |builder|. + // This may generate more than one instruction if neccessary. + uint32_t CloneOriginalReference(ref_analysis* ref, + InstructionBuilder* builder); + + // If |inst| references through an image, return the id of the image it + // references through. Else return 0. + uint32_t GetImageId(Instruction* inst); + + // Get pointee type inst of pointer value |ptr_inst|. + Instruction* GetPointeeTypeInst(Instruction* ptr_inst); + + // Analyze descriptor reference |ref_inst| and save components into |ref|. + // Return true if |ref_inst| is a descriptor reference, false otherwise. + bool AnalyzeDescriptorReference(Instruction* ref_inst, ref_analysis* ref); + + // Generate instrumentation code for generic test result |check_id|, starting + // with |builder| of block |new_blk_ptr|, adding new blocks to |new_blocks|. + // Generate conditional branch to a valid or invalid branch. Generate valid + // block which does original reference |ref|. Generate invalid block which + // writes debug error output utilizing |ref|, |error_id|, |length_id| and + // |stage_idx|. Generate merge block for valid and invalid branches. Kill + // original reference. + void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t offset_id, + uint32_t length_id, uint32_t stage_idx, ref_analysis* ref, + std::vector>* new_blocks); + + // Initialize state for instrumenting bindless checking + void InitializeInstBindlessCheck(); + + // Apply GenDescIdxCheckCode to every instruction in module. Then apply + // GenDescInitCheckCode to every instruction in module. + Pass::Status ProcessImpl(); + + // Enable instrumentation of runtime array length checking + bool desc_idx_enabled_; + + // Enable instrumentation of descriptor initialization checking + bool desc_init_enabled_; + + // Enable instrumentation of buffer overrun checking + bool buffer_bounds_enabled_; + + // Mapping from variable to descriptor set + std::unordered_map var2desc_set_; + + // Mapping from variable to binding + std::unordered_map var2binding_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_INST_BINDLESS_CHECK_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp b/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp new file mode 100644 index 0000000..fa6c2c6 --- /dev/null +++ b/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.cpp @@ -0,0 +1,441 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "inst_buff_addr_check_pass.h" + +namespace spvtools { +namespace opt { + +uint32_t InstBuffAddrCheckPass::CloneOriginalReference( + Instruction* ref_inst, InstructionBuilder* builder) { + // Clone original ref with new result id (if load) + assert( + (ref_inst->opcode() == SpvOpLoad || ref_inst->opcode() == SpvOpStore) && + "unexpected ref"); + std::unique_ptr new_ref_inst(ref_inst->Clone(context())); + uint32_t ref_result_id = ref_inst->result_id(); + uint32_t new_ref_id = 0; + if (ref_result_id != 0) { + new_ref_id = TakeNextId(); + new_ref_inst->SetResultId(new_ref_id); + } + // Register new reference and add to new block + Instruction* added_inst = builder->AddInstruction(std::move(new_ref_inst)); + uid2offset_[added_inst->unique_id()] = uid2offset_[ref_inst->unique_id()]; + if (new_ref_id != 0) + get_decoration_mgr()->CloneDecorations(ref_result_id, new_ref_id); + return new_ref_id; +} + +bool InstBuffAddrCheckPass::IsPhysicalBuffAddrReference(Instruction* ref_inst) { + if (ref_inst->opcode() != SpvOpLoad && ref_inst->opcode() != SpvOpStore) + return false; + uint32_t ptr_id = ref_inst->GetSingleWordInOperand(0); + analysis::DefUseManager* du_mgr = get_def_use_mgr(); + Instruction* ptr_inst = du_mgr->GetDef(ptr_id); + if (ptr_inst->opcode() != SpvOpAccessChain) return false; + uint32_t ptr_ty_id = ptr_inst->type_id(); + Instruction* ptr_ty_inst = du_mgr->GetDef(ptr_ty_id); + if (ptr_ty_inst->GetSingleWordInOperand(0) != + SpvStorageClassPhysicalStorageBufferEXT) + return false; + return true; +} + +// TODO(greg-lunarg): Refactor with InstBindlessCheckPass::GenCheckCode() ?? +void InstBuffAddrCheckPass::GenCheckCode( + uint32_t check_id, uint32_t error_id, uint32_t ref_uptr_id, + uint32_t stage_idx, Instruction* ref_inst, + std::vector>* new_blocks) { + BasicBlock* back_blk_ptr = &*new_blocks->back(); + InstructionBuilder builder( + context(), back_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Gen conditional branch on check_id. Valid branch generates original + // reference. Invalid generates debug output and zero result (if needed). + uint32_t merge_blk_id = TakeNextId(); + uint32_t valid_blk_id = TakeNextId(); + uint32_t invalid_blk_id = TakeNextId(); + std::unique_ptr merge_label(NewLabel(merge_blk_id)); + std::unique_ptr valid_label(NewLabel(valid_blk_id)); + std::unique_ptr invalid_label(NewLabel(invalid_blk_id)); + (void)builder.AddConditionalBranch(check_id, valid_blk_id, invalid_blk_id, + merge_blk_id, SpvSelectionControlMaskNone); + // Gen valid branch + std::unique_ptr new_blk_ptr( + new BasicBlock(std::move(valid_label))); + builder.SetInsertPoint(&*new_blk_ptr); + uint32_t new_ref_id = CloneOriginalReference(ref_inst, &builder); + (void)builder.AddBranch(merge_blk_id); + new_blocks->push_back(std::move(new_blk_ptr)); + // Gen invalid block + new_blk_ptr.reset(new BasicBlock(std::move(invalid_label))); + builder.SetInsertPoint(&*new_blk_ptr); + // Convert uptr from uint64 to 2 uint32 + Instruction* lo_uptr_inst = + builder.AddUnaryOp(GetUintId(), SpvOpUConvert, ref_uptr_id); + Instruction* rshift_uptr_inst = + builder.AddBinaryOp(GetUint64Id(), SpvOpShiftRightLogical, ref_uptr_id, + builder.GetUintConstantId(32)); + Instruction* hi_uptr_inst = builder.AddUnaryOp(GetUintId(), SpvOpUConvert, + rshift_uptr_inst->result_id()); + GenDebugStreamWrite( + uid2offset_[ref_inst->unique_id()], stage_idx, + {error_id, lo_uptr_inst->result_id(), hi_uptr_inst->result_id()}, + &builder); + // Gen zero for invalid load. If pointer type, need to convert uint64 + // zero to pointer; cannot create ConstantNull of pointer type. + uint32_t null_id = 0; + if (new_ref_id != 0) { + uint32_t ref_type_id = ref_inst->type_id(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* ref_type = type_mgr->GetType(ref_type_id); + if (ref_type->AsPointer() != nullptr) { + uint32_t null_u64_id = GetNullId(GetUint64Id()); + Instruction* null_ptr_inst = + builder.AddUnaryOp(ref_type_id, SpvOpConvertUToPtr, null_u64_id); + null_id = null_ptr_inst->result_id(); + } else { + null_id = GetNullId(ref_type_id); + } + } + (void)builder.AddBranch(merge_blk_id); + new_blocks->push_back(std::move(new_blk_ptr)); + // Gen merge block + new_blk_ptr.reset(new BasicBlock(std::move(merge_label))); + builder.SetInsertPoint(&*new_blk_ptr); + // Gen phi of new reference and zero, if necessary, and replace the + // result id of the original reference with that of the Phi. Kill original + // reference. + if (new_ref_id != 0) { + Instruction* phi_inst = + builder.AddPhi(ref_inst->type_id(), + {new_ref_id, valid_blk_id, null_id, invalid_blk_id}); + context()->ReplaceAllUsesWith(ref_inst->result_id(), phi_inst->result_id()); + } + new_blocks->push_back(std::move(new_blk_ptr)); + context()->KillInst(ref_inst); +} + +uint32_t InstBuffAddrCheckPass::GetTypeLength(uint32_t type_id) { + Instruction* type_inst = get_def_use_mgr()->GetDef(type_id); + switch (type_inst->opcode()) { + case SpvOpTypeFloat: + case SpvOpTypeInt: + return type_inst->GetSingleWordInOperand(0) / 8u; + case SpvOpTypeVector: + case SpvOpTypeMatrix: + return type_inst->GetSingleWordInOperand(1) * + GetTypeLength(type_inst->GetSingleWordInOperand(0)); + case SpvOpTypePointer: + assert(type_inst->GetSingleWordInOperand(0) == + SpvStorageClassPhysicalStorageBufferEXT && + "unexpected pointer type"); + return 8u; + default: + assert(false && "unexpected buffer reference type"); + return 0; + } +} + +void InstBuffAddrCheckPass::AddParam(uint32_t type_id, + std::vector* param_vec, + std::unique_ptr* input_func) { + uint32_t pid = TakeNextId(); + param_vec->push_back(pid); + std::unique_ptr param_inst(new Instruction( + get_module()->context(), SpvOpFunctionParameter, type_id, pid, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); + (*input_func)->AddParameter(std::move(param_inst)); +} + +uint32_t InstBuffAddrCheckPass::GetSearchAndTestFuncId() { + if (search_test_func_id_ == 0) { + // Generate function "bool search_and_test(uint64_t ref_ptr, uint32_t len)" + // which searches input buffer for buffer which most likely contains the + // pointer value |ref_ptr| and verifies that the entire reference of + // length |len| bytes is contained in the buffer. + search_test_func_id_ = TakeNextId(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + std::vector param_types = { + type_mgr->GetType(GetUint64Id()), type_mgr->GetType(GetUintId())}; + analysis::Function func_ty(type_mgr->GetType(GetBoolId()), param_types); + analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); + std::unique_ptr func_inst( + new Instruction(get_module()->context(), SpvOpFunction, GetBoolId(), + search_test_func_id_, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvFunctionControlMaskNone}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {type_mgr->GetTypeInstruction(reg_func_ty)}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); + std::unique_ptr input_func = + MakeUnique(std::move(func_inst)); + std::vector param_vec; + // Add ref_ptr and length parameters + AddParam(GetUint64Id(), ¶m_vec, &input_func); + AddParam(GetUintId(), ¶m_vec, &input_func); + // Empty first block. + uint32_t first_blk_id = TakeNextId(); + std::unique_ptr first_blk_label(NewLabel(first_blk_id)); + std::unique_ptr first_blk_ptr = + MakeUnique(std::move(first_blk_label)); + InstructionBuilder builder( + context(), &*first_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + uint32_t hdr_blk_id = TakeNextId(); + // Branch to search loop header + std::unique_ptr hdr_blk_label(NewLabel(hdr_blk_id)); + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpBranch, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {hdr_blk_id}}})); + first_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(first_blk_ptr)); + // Linear search loop header block + // TODO(greg-lunarg): Implement binary search + std::unique_ptr hdr_blk_ptr = + MakeUnique(std::move(hdr_blk_label)); + builder.SetInsertPoint(&*hdr_blk_ptr); + // Phi for search index. Starts with 1. + uint32_t cont_blk_id = TakeNextId(); + std::unique_ptr cont_blk_label(NewLabel(cont_blk_id)); + // Deal with def-use cycle caused by search loop index computation. + // Create Add and Phi instructions first, then do Def analysis on Add. + // Add Phi and Add instructions and do Use analysis later. + uint32_t idx_phi_id = TakeNextId(); + uint32_t idx_inc_id = TakeNextId(); + std::unique_ptr idx_inc_inst(new Instruction( + context(), SpvOpIAdd, GetUintId(), idx_inc_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_phi_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {builder.GetUintConstantId(1u)}}})); + std::unique_ptr idx_phi_inst(new Instruction( + context(), SpvOpPhi, GetUintId(), idx_phi_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {builder.GetUintConstantId(1u)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {first_blk_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {idx_inc_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cont_blk_id}}})); + get_def_use_mgr()->AnalyzeInstDef(&*idx_inc_inst); + // Add (previously created) search index phi + (void)builder.AddInstruction(std::move(idx_phi_inst)); + // LoopMerge + uint32_t bound_test_blk_id = TakeNextId(); + std::unique_ptr bound_test_blk_label( + NewLabel(bound_test_blk_id)); + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpLoopMerge, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {bound_test_blk_id}}, + {SPV_OPERAND_TYPE_ID, {cont_blk_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {SpvLoopControlMaskNone}}})); + // Branch to continue/work block + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpBranch, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {cont_blk_id}}})); + hdr_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(hdr_blk_ptr)); + // Continue/Work Block. Read next buffer pointer and break if greater + // than ref_ptr arg. + std::unique_ptr cont_blk_ptr = + MakeUnique(std::move(cont_blk_label)); + builder.SetInsertPoint(&*cont_blk_ptr); + // Add (previously created) search index increment now. + (void)builder.AddInstruction(std::move(idx_inc_inst)); + // Load next buffer address from debug input buffer + uint32_t ibuf_id = GetInputBufferId(); + uint32_t ibuf_ptr_id = GetInputBufferPtrId(); + Instruction* uptr_ac_inst = builder.AddTernaryOp( + ibuf_ptr_id, SpvOpAccessChain, ibuf_id, + builder.GetUintConstantId(kDebugInputDataOffset), idx_inc_id); + uint32_t ibuf_type_id = GetInputBufferTypeId(); + Instruction* uptr_load_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, uptr_ac_inst->result_id()); + // If loaded address greater than ref_ptr arg, break, else branch back to + // loop header + Instruction* uptr_test_inst = + builder.AddBinaryOp(GetBoolId(), SpvOpUGreaterThan, + uptr_load_inst->result_id(), param_vec[0]); + (void)builder.AddConditionalBranch(uptr_test_inst->result_id(), + bound_test_blk_id, hdr_blk_id, + kInvalidId, SpvSelectionControlMaskNone); + cont_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(cont_blk_ptr)); + // Bounds test block. Read length of selected buffer and test that + // all len arg bytes are in buffer. + std::unique_ptr bound_test_blk_ptr = + MakeUnique(std::move(bound_test_blk_label)); + builder.SetInsertPoint(&*bound_test_blk_ptr); + // Decrement index to point to previous/candidate buffer address + Instruction* cand_idx_inst = builder.AddBinaryOp( + GetUintId(), SpvOpISub, idx_inc_id, builder.GetUintConstantId(1u)); + // Load candidate buffer address + Instruction* cand_ac_inst = + builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, + builder.GetUintConstantId(kDebugInputDataOffset), + cand_idx_inst->result_id()); + Instruction* cand_load_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, cand_ac_inst->result_id()); + // Compute offset of ref_ptr from candidate buffer address + Instruction* offset_inst = builder.AddBinaryOp( + ibuf_type_id, SpvOpISub, param_vec[0], cand_load_inst->result_id()); + // Convert ref length to uint64 + Instruction* ref_len_64_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpUConvert, param_vec[1]); + // Add ref length to ref offset to compute end of reference + Instruction* ref_end_inst = + builder.AddBinaryOp(ibuf_type_id, SpvOpIAdd, offset_inst->result_id(), + ref_len_64_inst->result_id()); + // Load starting index of lengths in input buffer and convert to uint32 + Instruction* len_start_ac_inst = + builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, + builder.GetUintConstantId(kDebugInputDataOffset), + builder.GetUintConstantId(0u)); + Instruction* len_start_load_inst = builder.AddUnaryOp( + ibuf_type_id, SpvOpLoad, len_start_ac_inst->result_id()); + Instruction* len_start_32_inst = builder.AddUnaryOp( + GetUintId(), SpvOpUConvert, len_start_load_inst->result_id()); + // Decrement search index to get candidate buffer length index + Instruction* cand_len_idx_inst = + builder.AddBinaryOp(GetUintId(), SpvOpISub, cand_idx_inst->result_id(), + builder.GetUintConstantId(1u)); + // Add candidate length index to start index + Instruction* len_idx_inst = builder.AddBinaryOp( + GetUintId(), SpvOpIAdd, cand_len_idx_inst->result_id(), + len_start_32_inst->result_id()); + // Load candidate buffer length + Instruction* len_ac_inst = + builder.AddTernaryOp(ibuf_ptr_id, SpvOpAccessChain, ibuf_id, + builder.GetUintConstantId(kDebugInputDataOffset), + len_idx_inst->result_id()); + Instruction* len_load_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, len_ac_inst->result_id()); + // Test if reference end within candidate buffer length + Instruction* len_test_inst = builder.AddBinaryOp( + GetBoolId(), SpvOpULessThanEqual, ref_end_inst->result_id(), + len_load_inst->result_id()); + // Return test result + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpReturnValue, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {len_test_inst->result_id()}}})); + // Close block + bound_test_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(bound_test_blk_ptr)); + // Close function and add function to module + std::unique_ptr func_end_inst( + new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); + input_func->SetFunctionEnd(std::move(func_end_inst)); + context()->AddFunction(std::move(input_func)); + } + return search_test_func_id_; +} + +uint32_t InstBuffAddrCheckPass::GenSearchAndTest(Instruction* ref_inst, + InstructionBuilder* builder, + uint32_t* ref_uptr_id) { + // Enable Int64 if necessary + if (!get_feature_mgr()->HasCapability(SpvCapabilityInt64)) { + std::unique_ptr cap_int64_inst(new Instruction( + context(), SpvOpCapability, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityInt64}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*cap_int64_inst); + context()->AddCapability(std::move(cap_int64_inst)); + } + // Convert reference pointer to uint64 + uint32_t ref_ptr_id = ref_inst->GetSingleWordInOperand(0); + Instruction* ref_uptr_inst = + builder->AddUnaryOp(GetUint64Id(), SpvOpConvertPtrToU, ref_ptr_id); + *ref_uptr_id = ref_uptr_inst->result_id(); + // Compute reference length in bytes + analysis::DefUseManager* du_mgr = get_def_use_mgr(); + Instruction* ref_ptr_inst = du_mgr->GetDef(ref_ptr_id); + uint32_t ref_ptr_ty_id = ref_ptr_inst->type_id(); + Instruction* ref_ptr_ty_inst = du_mgr->GetDef(ref_ptr_ty_id); + uint32_t ref_len = GetTypeLength(ref_ptr_ty_inst->GetSingleWordInOperand(1)); + uint32_t ref_len_id = builder->GetUintConstantId(ref_len); + // Gen call to search and test function + const std::vector args = {GetSearchAndTestFuncId(), *ref_uptr_id, + ref_len_id}; + Instruction* call_inst = + builder->AddNaryOp(GetBoolId(), SpvOpFunctionCall, args); + uint32_t retval = call_inst->result_id(); + return retval; +} + +void InstBuffAddrCheckPass::GenBuffAddrCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + // Look for reference through indexed descriptor. If found, analyze and + // save components. If not, return. + Instruction* ref_inst = &*ref_inst_itr; + if (!IsPhysicalBuffAddrReference(ref_inst)) return; + // Move original block's preceding instructions into first new block + std::unique_ptr new_blk_ptr; + MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + new_blocks->push_back(std::move(new_blk_ptr)); + uint32_t error_id = builder.GetUintConstantId(kInstErrorBuffAddrUnallocRef); + // Generate code to do search and test if all bytes of reference + // are within a listed buffer. Return reference pointer converted to uint64. + uint32_t ref_uptr_id; + uint32_t valid_id = GenSearchAndTest(ref_inst, &builder, &ref_uptr_id); + // Generate test of search results with true branch + // being full reference and false branch being debug output and zero + // for the referenced value. + GenCheckCode(valid_id, error_id, ref_uptr_id, stage_idx, ref_inst, + new_blocks); + // Move original block's remaining code into remainder/merge block and add + // to new blocks + BasicBlock* back_blk_ptr = &*new_blocks->back(); + MovePostludeCode(ref_block_itr, back_blk_ptr); +} + +void InstBuffAddrCheckPass::InitInstBuffAddrCheck() { + // Initialize base class + InitializeInstrument(); + // Initialize class + search_test_func_id_ = 0; +} + +Pass::Status InstBuffAddrCheckPass::ProcessImpl() { + // Perform bindless bounds check on each entry point function in module + InstProcessFunction pfn = + [this](BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + return GenBuffAddrCheckCode(ref_inst_itr, ref_block_itr, stage_idx, + new_blocks); + }; + bool modified = InstProcessEntryPointCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Pass::Status InstBuffAddrCheckPass::Process() { + if (!get_feature_mgr()->HasCapability( + SpvCapabilityPhysicalStorageBufferAddressesEXT)) + return Status::SuccessWithoutChange; + InitInstBuffAddrCheck(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.h b/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.h new file mode 100644 index 0000000..ec7bb68 --- /dev/null +++ b/third_party/spirv-tools/source/opt/inst_buff_addr_check_pass.h @@ -0,0 +1,129 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_ +#define LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_ + +#include "instrument_pass.h" + +namespace spvtools { +namespace opt { + +// This class/pass is designed to support the GPU-assisted validation layer of +// the Buffer Device Address (BDA) extension in +// https://github.com/KhronosGroup/Vulkan-ValidationLayers. The internal and +// external design of this class may change as the layer evolves. +class InstBuffAddrCheckPass : public InstrumentPass { + public: + // Preferred interface + InstBuffAddrCheckPass(uint32_t desc_set, uint32_t shader_id) + : InstrumentPass(desc_set, shader_id, kInstValidationIdBuffAddr) {} + + ~InstBuffAddrCheckPass() override = default; + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "inst-bindless-check-pass"; } + + private: + // Return byte length of type |type_id|. Must be int, float, vector, matrix + // or physical pointer. + uint32_t GetTypeLength(uint32_t type_id); + + // Add |type_id| param to |input_func| and add id to |param_vec|. + void AddParam(uint32_t type_id, std::vector* param_vec, + std::unique_ptr* input_func); + + // Return id for search and test function. Generate it if not already gen'd. + uint32_t GetSearchAndTestFuncId(); + + // Generate code into |builder| to do search of the BDA debug input buffer + // for the buffer used by |ref_inst| and test that all bytes of reference + // are within the buffer. Returns id of boolean value which is true if + // search and test is successful, false otherwise. + uint32_t GenSearchAndTest(Instruction* ref_inst, InstructionBuilder* builder, + uint32_t* ref_uptr_id); + + // This function does checking instrumentation on a single + // instruction which references through a physical storage buffer address. + // GenBuffAddrCheckCode generates code that checks that all bytes that + // are referenced fall within a buffer that was queried via + // the Vulkan API call vkGetBufferDeviceAddressEXT(). + // + // The function is designed to be passed to + // InstrumentPass::InstProcessEntryPointCallTree(), which applies the + // function to each instruction in a module and replaces the instruction + // with instrumented code if warranted. + // + // If |ref_inst_itr| is a physical storage buffer reference, return in + // |new_blocks| the result of instrumenting it with validation code within + // its block at |ref_block_itr|. The validation code first executes a check + // for the specific condition called for. If the check passes, it executes + // the remainder of the reference, otherwise writes a record to the debug + // output buffer stream including |function_idx, instruction_idx, stage_idx| + // and replaces the reference with the null value of the original type. The + // block at |ref_block_itr| can just be replaced with the blocks in + // |new_blocks|, which will contain at least two blocks. The last block will + // comprise all instructions following |ref_inst_itr|, + // preceded by a phi instruction if needed. + // + // This instrumentation function utilizes GenDebugStreamWrite() to write its + // error records. The validation-specific part of the error record will + // have the format: + // + // Validation Error Code (=kInstErrorBuffAddr) + // Buffer Address (lowest 32 bits) + // Buffer Address (highest 32 bits) + // + void GenBuffAddrCheckCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks); + + // Return true if |ref_inst| is a physical buffer address reference, false + // otherwise. + bool IsPhysicalBuffAddrReference(Instruction* ref_inst); + + // Clone original reference |ref_inst| into |builder| and return id of result + uint32_t CloneOriginalReference(Instruction* ref_inst, + InstructionBuilder* builder); + + // Generate instrumentation code for boolean test result |check_id|, + // adding new blocks to |new_blocks|. Generate conditional branch to valid + // or invalid reference blocks. Generate valid reference block which does + // original reference |ref_inst|. Then generate invalid reference block which + // writes debug error output utilizing |ref_inst|, |error_id| and + // |stage_idx|. Generate merge block for valid and invalid reference blocks. + // Kill original reference. + void GenCheckCode(uint32_t check_id, uint32_t error_id, uint32_t length_id, + uint32_t stage_idx, Instruction* ref_inst, + std::vector>* new_blocks); + + // Initialize state for instrumenting physical buffer address checking + void InitInstBuffAddrCheck(); + + // Apply GenBuffAddrCheckCode to every instruction in module. + Pass::Status ProcessImpl(); + + // Id of search and test function, if already gen'd, else zero. + uint32_t search_test_func_id_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_INST_BUFFER_ADDRESS_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/inst_debug_printf_pass.cpp b/third_party/spirv-tools/source/opt/inst_debug_printf_pass.cpp new file mode 100644 index 0000000..c0e6bc3 --- /dev/null +++ b/third_party/spirv-tools/source/opt/inst_debug_printf_pass.cpp @@ -0,0 +1,266 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// Copyright (c) 2020 Valve Corporation +// Copyright (c) 2020 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "inst_debug_printf_pass.h" + +#include "spirv/unified1/NonSemanticDebugPrintf.h" + +namespace spvtools { +namespace opt { + +void InstDebugPrintfPass::GenOutputValues(Instruction* val_inst, + std::vector* val_ids, + InstructionBuilder* builder) { + uint32_t val_ty_id = val_inst->type_id(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* val_ty = type_mgr->GetType(val_ty_id); + switch (val_ty->kind()) { + case analysis::Type::kVector: { + analysis::Vector* v_ty = val_ty->AsVector(); + const analysis::Type* c_ty = v_ty->element_type(); + uint32_t c_ty_id = type_mgr->GetId(c_ty); + for (uint32_t c = 0; c < v_ty->element_count(); ++c) { + Instruction* c_inst = builder->AddIdLiteralOp( + c_ty_id, SpvOpCompositeExtract, val_inst->result_id(), c); + GenOutputValues(c_inst, val_ids, builder); + } + return; + } + case analysis::Type::kBool: { + // Select between uint32 zero or one + uint32_t zero_id = builder->GetUintConstantId(0); + uint32_t one_id = builder->GetUintConstantId(1); + Instruction* sel_inst = builder->AddTernaryOp( + GetUintId(), SpvOpSelect, val_inst->result_id(), one_id, zero_id); + val_ids->push_back(sel_inst->result_id()); + return; + } + case analysis::Type::kFloat: { + analysis::Float* f_ty = val_ty->AsFloat(); + switch (f_ty->width()) { + case 16: { + // Convert float16 to float32 and recurse + Instruction* f32_inst = builder->AddUnaryOp( + GetFloatId(), SpvOpFConvert, val_inst->result_id()); + GenOutputValues(f32_inst, val_ids, builder); + return; + } + case 64: { + // Bitcast float64 to uint64 and recurse + Instruction* ui64_inst = builder->AddUnaryOp( + GetUint64Id(), SpvOpBitcast, val_inst->result_id()); + GenOutputValues(ui64_inst, val_ids, builder); + return; + } + case 32: { + // Bitcase float32 to uint32 + Instruction* bc_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast, + val_inst->result_id()); + val_ids->push_back(bc_inst->result_id()); + return; + } + default: + assert(false && "unsupported float width"); + return; + } + } + case analysis::Type::kInteger: { + analysis::Integer* i_ty = val_ty->AsInteger(); + switch (i_ty->width()) { + case 64: { + Instruction* ui64_inst = val_inst; + if (i_ty->IsSigned()) { + // Bitcast sint64 to uint64 + ui64_inst = builder->AddUnaryOp(GetUint64Id(), SpvOpBitcast, + val_inst->result_id()); + } + // Break uint64 into 2x uint32 + Instruction* lo_ui64_inst = builder->AddUnaryOp( + GetUintId(), SpvOpUConvert, ui64_inst->result_id()); + Instruction* rshift_ui64_inst = builder->AddBinaryOp( + GetUint64Id(), SpvOpShiftRightLogical, ui64_inst->result_id(), + builder->GetUintConstantId(32)); + Instruction* hi_ui64_inst = builder->AddUnaryOp( + GetUintId(), SpvOpUConvert, rshift_ui64_inst->result_id()); + val_ids->push_back(lo_ui64_inst->result_id()); + val_ids->push_back(hi_ui64_inst->result_id()); + return; + } + case 8: { + Instruction* ui8_inst = val_inst; + if (i_ty->IsSigned()) { + // Bitcast sint8 to uint8 + ui8_inst = builder->AddUnaryOp(GetUint8Id(), SpvOpBitcast, + val_inst->result_id()); + } + // Convert uint8 to uint32 + Instruction* ui32_inst = builder->AddUnaryOp( + GetUintId(), SpvOpUConvert, ui8_inst->result_id()); + val_ids->push_back(ui32_inst->result_id()); + return; + } + case 32: { + Instruction* ui32_inst = val_inst; + if (i_ty->IsSigned()) { + // Bitcast sint32 to uint32 + ui32_inst = builder->AddUnaryOp(GetUintId(), SpvOpBitcast, + val_inst->result_id()); + } + // uint32 needs no further processing + val_ids->push_back(ui32_inst->result_id()); + return; + } + default: + // TODO(greg-lunarg): Support non-32-bit int + assert(false && "unsupported int width"); + return; + } + } + default: + assert(false && "unsupported type"); + return; + } +} + +void InstDebugPrintfPass::GenOutputCode( + Instruction* printf_inst, uint32_t stage_idx, + std::vector>* new_blocks) { + BasicBlock* back_blk_ptr = &*new_blocks->back(); + InstructionBuilder builder( + context(), back_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Gen debug printf record validation-specific values. The format string + // will have its id written. Vectors will need to be broken down into + // component values. float16 will need to be converted to float32. Pointer + // and uint64 will need to be converted to two uint32 values. float32 will + // need to be bitcast to uint32. int32 will need to be bitcast to uint32. + std::vector val_ids; + bool is_first_operand = false; + printf_inst->ForEachInId( + [&is_first_operand, &val_ids, &builder, this](const uint32_t* iid) { + // skip set operand + if (!is_first_operand) { + is_first_operand = true; + return; + } + Instruction* opnd_inst = get_def_use_mgr()->GetDef(*iid); + if (opnd_inst->opcode() == SpvOpString) { + uint32_t string_id_id = builder.GetUintConstantId(*iid); + val_ids.push_back(string_id_id); + } else { + GenOutputValues(opnd_inst, &val_ids, &builder); + } + }); + GenDebugStreamWrite(uid2offset_[printf_inst->unique_id()], stage_idx, val_ids, + &builder); + context()->KillInst(printf_inst); +} + +void InstDebugPrintfPass::GenDebugPrintfCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + // If not DebugPrintf OpExtInst, return. + Instruction* printf_inst = &*ref_inst_itr; + if (printf_inst->opcode() != SpvOpExtInst) return; + if (printf_inst->GetSingleWordInOperand(0) != ext_inst_printf_id_) return; + if (printf_inst->GetSingleWordInOperand(1) != + NonSemanticDebugPrintfDebugPrintf) + return; + // Initialize DefUse manager before dismantling module + (void)get_def_use_mgr(); + // Move original block's preceding instructions into first new block + std::unique_ptr new_blk_ptr; + MovePreludeCode(ref_inst_itr, ref_block_itr, &new_blk_ptr); + new_blocks->push_back(std::move(new_blk_ptr)); + // Generate instructions to output printf args to printf buffer + GenOutputCode(printf_inst, stage_idx, new_blocks); + // Caller expects at least two blocks with last block containing remaining + // code, so end block after instrumentation, create remainder block, and + // branch to it + uint32_t rem_blk_id = TakeNextId(); + std::unique_ptr rem_label(NewLabel(rem_blk_id)); + BasicBlock* back_blk_ptr = &*new_blocks->back(); + InstructionBuilder builder( + context(), back_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + (void)builder.AddBranch(rem_blk_id); + // Gen remainder block + new_blk_ptr.reset(new BasicBlock(std::move(rem_label))); + builder.SetInsertPoint(&*new_blk_ptr); + // Move original block's remaining code into remainder block and add + // to new blocks + MovePostludeCode(ref_block_itr, &*new_blk_ptr); + new_blocks->push_back(std::move(new_blk_ptr)); +} + +void InstDebugPrintfPass::InitializeInstDebugPrintf() { + // Initialize base class + InitializeInstrument(); +} + +Pass::Status InstDebugPrintfPass::ProcessImpl() { + // Perform printf instrumentation on each entry point function in module + InstProcessFunction pfn = + [this](BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, uint32_t stage_idx, + std::vector>* new_blocks) { + return GenDebugPrintfCode(ref_inst_itr, ref_block_itr, stage_idx, + new_blocks); + }; + (void)InstProcessEntryPointCallTree(pfn); + // Remove DebugPrintf OpExtInstImport instruction + Instruction* ext_inst_import_inst = + get_def_use_mgr()->GetDef(ext_inst_printf_id_); + context()->KillInst(ext_inst_import_inst); + // If no remaining non-semantic instruction sets, remove non-semantic debug + // info extension from module and feature manager + bool non_sem_set_seen = false; + for (auto c_itr = context()->module()->ext_inst_import_begin(); + c_itr != context()->module()->ext_inst_import_end(); ++c_itr) { + const char* set_name = + reinterpret_cast(&c_itr->GetInOperand(0).words[0]); + const char* non_sem_str = "NonSemantic."; + if (!strncmp(set_name, non_sem_str, strlen(non_sem_str))) { + non_sem_set_seen = true; + break; + } + } + if (!non_sem_set_seen) { + for (auto c_itr = context()->module()->extension_begin(); + c_itr != context()->module()->extension_end(); ++c_itr) { + const char* ext_name = + reinterpret_cast(&c_itr->GetInOperand(0).words[0]); + if (!strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + context()->KillInst(&*c_itr); + break; + } + } + context()->get_feature_mgr()->RemoveExtension(kSPV_KHR_non_semantic_info); + } + return Status::SuccessWithChange; +} + +Pass::Status InstDebugPrintfPass::Process() { + ext_inst_printf_id_ = + get_module()->GetExtInstImportId("NonSemantic.DebugPrintf"); + if (ext_inst_printf_id_ == 0) return Status::SuccessWithoutChange; + InitializeInstDebugPrintf(); + return ProcessImpl(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/inst_debug_printf_pass.h b/third_party/spirv-tools/source/opt/inst_debug_printf_pass.h new file mode 100644 index 0000000..70b0a72 --- /dev/null +++ b/third_party/spirv-tools/source/opt/inst_debug_printf_pass.h @@ -0,0 +1,95 @@ +// Copyright (c) 2020 The Khronos Group Inc. +// Copyright (c) 2020 Valve Corporation +// Copyright (c) 2020 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_ +#define LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_ + +#include "instrument_pass.h" + +namespace spvtools { +namespace opt { + +// This class/pass is designed to support the debug printf GPU-assisted layer +// of https://github.com/KhronosGroup/Vulkan-ValidationLayers. Its internal and +// external design may change as the layer evolves. +class InstDebugPrintfPass : public InstrumentPass { + public: + // For test harness only + InstDebugPrintfPass() : InstrumentPass(7, 23, kInstValidationIdDebugPrintf) {} + // For all other interfaces + InstDebugPrintfPass(uint32_t desc_set, uint32_t shader_id) + : InstrumentPass(desc_set, shader_id, kInstValidationIdDebugPrintf) {} + + ~InstDebugPrintfPass() override = default; + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "inst-printf-pass"; } + + private: + // Generate instructions for OpDebugPrintf. + // + // If |ref_inst_itr| is an OpDebugPrintf, return in |new_blocks| the result + // of replacing it with buffer write instructions within its block at + // |ref_block_itr|. The instructions write a record to the printf + // output buffer stream including |function_idx, instruction_idx, stage_idx| + // and removes the OpDebugPrintf. The block at |ref_block_itr| can just be + // replaced with the block in |new_blocks|. Besides the buffer writes, this + // block will comprise all instructions preceding and following + // |ref_inst_itr|. + // + // This function is designed to be passed to + // InstrumentPass::InstProcessEntryPointCallTree(), which applies the + // function to each instruction in a module and replaces the instruction + // if warranted. + // + // This instrumentation function utilizes GenDebugStreamWrite() to write its + // error records. The validation-specific part of the error record will + // consist of a uint32 which is the id of the format string plus a sequence + // of uint32s representing the values of the remaining operands of the + // DebugPrintf. + void GenDebugPrintfCode(BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, + uint32_t stage_idx, + std::vector>* new_blocks); + + // Generate a sequence of uint32 instructions in |builder| (if necessary) + // representing the value of |val_inst|, which must be a buffer pointer, a + // uint64, or a scalar or vector of type uint32, float32 or float16. Append + // the ids of all values to the end of |val_ids|. + void GenOutputValues(Instruction* val_inst, std::vector* val_ids, + InstructionBuilder* builder); + + // Generate instructions to write a record containing the operands of + // |printf_inst| arguments to printf buffer, adding new code to the end of + // the last block in |new_blocks|. Kill OpDebugPrintf instruction. + void GenOutputCode(Instruction* printf_inst, uint32_t stage_idx, + std::vector>* new_blocks); + + // Initialize state for instrumenting bindless checking + void InitializeInstDebugPrintf(); + + // Apply GenDebugPrintfCode to every instruction in module. + Pass::Status ProcessImpl(); + + uint32_t ext_inst_printf_id_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_INST_DEBUG_PRINTF_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/instruction.cpp b/third_party/spirv-tools/source/opt/instruction.cpp new file mode 100644 index 0000000..1054a20 --- /dev/null +++ b/third_party/spirv-tools/source/opt/instruction.cpp @@ -0,0 +1,965 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/instruction.h" + +#include + +#include "OpenCLDebugInfo100.h" +#include "source/disassemble.h" +#include "source/opt/fold.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { + +namespace { +// Indices used to get particular operands out of instructions using InOperand. +const uint32_t kTypeImageDimIndex = 1; +const uint32_t kLoadBaseIndex = 0; +const uint32_t kPointerTypeStorageClassIndex = 0; +const uint32_t kTypeImageSampledIndex = 5; + +// Constants for OpenCL.DebugInfo.100 extension instructions. +const uint32_t kExtInstSetIdInIdx = 0; +const uint32_t kExtInstInstructionInIdx = 1; +const uint32_t kDebugScopeNumWords = 7; +const uint32_t kDebugScopeNumWordsWithoutInlinedAt = 6; +const uint32_t kDebugNoScopeNumWords = 5; + +// Number of operands of an OpBranchConditional instruction +// with weights. +const uint32_t kOpBranchConditionalWithWeightsNumOperands = 5; +} // namespace + +Instruction::Instruction(IRContext* c) + : utils::IntrusiveNodeBase(), + context_(c), + opcode_(SpvOpNop), + has_type_id_(false), + has_result_id_(false), + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} + +Instruction::Instruction(IRContext* c, SpvOp op) + : utils::IntrusiveNodeBase(), + context_(c), + opcode_(op), + has_type_id_(false), + has_result_id_(false), + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} + +Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + std::vector&& dbg_line) + : context_(c), + opcode_(static_cast(inst.opcode)), + has_type_id_(inst.type_id != 0), + has_result_id_(inst.result_id != 0), + unique_id_(c->TakeNextUniqueId()), + dbg_line_insts_(std::move(dbg_line)), + dbg_scope_(kNoDebugScope, kNoInlinedAt) { + assert((!IsDebugLineInst(opcode_) || dbg_line.empty()) && + "Op(No)Line attaching to Op(No)Line found"); + for (uint32_t i = 0; i < inst.num_operands; ++i) { + const auto& current_payload = inst.operands[i]; + std::vector words( + inst.words + current_payload.offset, + inst.words + current_payload.offset + current_payload.num_words); + operands_.emplace_back(current_payload.type, std::move(words)); + } +} + +Instruction::Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + const DebugScope& dbg_scope) + : context_(c), + opcode_(static_cast(inst.opcode)), + has_type_id_(inst.type_id != 0), + has_result_id_(inst.result_id != 0), + unique_id_(c->TakeNextUniqueId()), + dbg_scope_(dbg_scope) { + for (uint32_t i = 0; i < inst.num_operands; ++i) { + const auto& current_payload = inst.operands[i]; + std::vector words( + inst.words + current_payload.offset, + inst.words + current_payload.offset + current_payload.num_words); + operands_.emplace_back(current_payload.type, std::move(words)); + } +} + +Instruction::Instruction(IRContext* c, SpvOp op, uint32_t ty_id, + uint32_t res_id, const OperandList& in_operands) + : utils::IntrusiveNodeBase(), + context_(c), + opcode_(op), + has_type_id_(ty_id != 0), + has_result_id_(res_id != 0), + unique_id_(c->TakeNextUniqueId()), + operands_(), + dbg_scope_(kNoDebugScope, kNoInlinedAt) { + if (has_type_id_) { + operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_TYPE_ID, + std::initializer_list{ty_id}); + } + if (has_result_id_) { + operands_.emplace_back(spv_operand_type_t::SPV_OPERAND_TYPE_RESULT_ID, + std::initializer_list{res_id}); + } + operands_.insert(operands_.end(), in_operands.begin(), in_operands.end()); +} + +Instruction::Instruction(Instruction&& that) + : utils::IntrusiveNodeBase(), + opcode_(that.opcode_), + has_type_id_(that.has_type_id_), + has_result_id_(that.has_result_id_), + unique_id_(that.unique_id_), + operands_(std::move(that.operands_)), + dbg_line_insts_(std::move(that.dbg_line_insts_)), + dbg_scope_(that.dbg_scope_) { + for (auto& i : dbg_line_insts_) { + i.dbg_scope_ = that.dbg_scope_; + } +} + +Instruction& Instruction::operator=(Instruction&& that) { + opcode_ = that.opcode_; + has_type_id_ = that.has_type_id_; + has_result_id_ = that.has_result_id_; + unique_id_ = that.unique_id_; + operands_ = std::move(that.operands_); + dbg_line_insts_ = std::move(that.dbg_line_insts_); + dbg_scope_ = that.dbg_scope_; + return *this; +} + +Instruction* Instruction::Clone(IRContext* c) const { + Instruction* clone = new Instruction(c); + clone->opcode_ = opcode_; + clone->has_type_id_ = has_type_id_; + clone->has_result_id_ = has_result_id_; + clone->unique_id_ = c->TakeNextUniqueId(); + clone->operands_ = operands_; + clone->dbg_line_insts_ = dbg_line_insts_; + clone->dbg_scope_ = dbg_scope_; + return clone; +} + +uint32_t Instruction::GetSingleWordOperand(uint32_t index) const { + const auto& words = GetOperand(index).words; + assert(words.size() == 1 && "expected the operand only taking one word"); + return words.front(); +} + +uint32_t Instruction::NumInOperandWords() const { + uint32_t size = 0; + for (uint32_t i = TypeResultIdCount(); i < operands_.size(); ++i) + size += static_cast(operands_[i].words.size()); + return size; +} + +bool Instruction::HasBranchWeights() const { + if (opcode_ == SpvOpBranchConditional && + NumOperands() == kOpBranchConditionalWithWeightsNumOperands) { + return true; + } + + return false; +} + +void Instruction::ToBinaryWithoutAttachedDebugInsts( + std::vector* binary) const { + const uint32_t num_words = 1 + NumOperandWords(); + binary->push_back((num_words << 16) | static_cast(opcode_)); + for (const auto& operand : operands_) { + binary->insert(binary->end(), operand.words.begin(), operand.words.end()); + } +} + +void Instruction::ReplaceOperands(const OperandList& new_operands) { + operands_.clear(); + operands_.insert(operands_.begin(), new_operands.begin(), new_operands.end()); +} + +bool Instruction::IsReadOnlyLoad() const { + if (IsLoad()) { + Instruction* address_def = GetBaseAddress(); + if (!address_def) { + return false; + } + + if (address_def->opcode() == SpvOpVariable) { + if (address_def->IsReadOnlyPointer()) { + return true; + } + } + + if (address_def->opcode() == SpvOpLoad) { + const analysis::Type* address_type = + context()->get_type_mgr()->GetType(address_def->type_id()); + if (address_type->AsSampledImage() != nullptr) { + const auto* image_type = + address_type->AsSampledImage()->image_type()->AsImage(); + if (image_type->sampled() == 1) { + return true; + } + } + } + } + return false; +} + +Instruction* Instruction::GetBaseAddress() const { + uint32_t base = GetSingleWordInOperand(kLoadBaseIndex); + Instruction* base_inst = context()->get_def_use_mgr()->GetDef(base); + bool done = false; + while (!done) { + switch (base_inst->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + case SpvOpImageTexelPointer: + case SpvOpCopyObject: + // All of these instructions have the base pointer use a base pointer + // in in-operand 0. + base = base_inst->GetSingleWordInOperand(0); + base_inst = context()->get_def_use_mgr()->GetDef(base); + break; + default: + done = true; + break; + } + } + return base_inst; +} + +bool Instruction::IsReadOnlyPointer() const { + if (context()->get_feature_mgr()->HasCapability(SpvCapabilityShader)) + return IsReadOnlyPointerShaders(); + else + return IsReadOnlyPointerKernel(); +} + +bool Instruction::IsVulkanStorageImage() const { + if (opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t storage_class = + GetSingleWordInOperand(kPointerTypeStorageClassIndex); + if (storage_class != SpvStorageClassUniformConstant) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != SpvOpTypeImage) { + return false; + } + + if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) { + return false; + } + + // Check if the image is sampled. If we do not know for sure that it is, + // then assume it is a storage image. + return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1; +} + +bool Instruction::IsVulkanSampledImage() const { + if (opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t storage_class = + GetSingleWordInOperand(kPointerTypeStorageClassIndex); + if (storage_class != SpvStorageClassUniformConstant) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != SpvOpTypeImage) { + return false; + } + + if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) == SpvDimBuffer) { + return false; + } + + // Check if the image is sampled. If we know for sure that it is, + // then return true. + return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) == 1; +} + +bool Instruction::IsVulkanStorageTexelBuffer() const { + if (opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t storage_class = + GetSingleWordInOperand(kPointerTypeStorageClassIndex); + if (storage_class != SpvStorageClassUniformConstant) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != SpvOpTypeImage) { + return false; + } + + if (base_type->GetSingleWordInOperand(kTypeImageDimIndex) != SpvDimBuffer) { + return false; + } + + // Check if the image is sampled. If we do not know for sure that it is, + // then assume it is a storage texel buffer. + return base_type->GetSingleWordInOperand(kTypeImageSampledIndex) != 1; +} + +bool Instruction::IsVulkanStorageBuffer() const { + // Is there a difference between a "Storage buffer" and a "dynamic storage + // buffer" in SPIR-V and do we care about the difference? + if (opcode() != SpvOpTypePointer) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != SpvOpTypeStruct) { + return false; + } + + uint32_t storage_class = + GetSingleWordInOperand(kPointerTypeStorageClassIndex); + if (storage_class == SpvStorageClassUniform) { + bool is_buffer_block = false; + context()->get_decoration_mgr()->ForEachDecoration( + base_type->result_id(), SpvDecorationBufferBlock, + [&is_buffer_block](const Instruction&) { is_buffer_block = true; }); + return is_buffer_block; + } else if (storage_class == SpvStorageClassStorageBuffer) { + bool is_block = false; + context()->get_decoration_mgr()->ForEachDecoration( + base_type->result_id(), SpvDecorationBlock, + [&is_block](const Instruction&) { is_block = true; }); + return is_block; + } + return false; +} + +bool Instruction::IsVulkanUniformBuffer() const { + if (opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t storage_class = + GetSingleWordInOperand(kPointerTypeStorageClassIndex); + if (storage_class != SpvStorageClassUniform) { + return false; + } + + Instruction* base_type = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(1)); + + // Unpack the optional layer of arraying. + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = context()->get_def_use_mgr()->GetDef( + base_type->GetSingleWordInOperand(0)); + } + + if (base_type->opcode() != SpvOpTypeStruct) { + return false; + } + + bool is_block = false; + context()->get_decoration_mgr()->ForEachDecoration( + base_type->result_id(), SpvDecorationBlock, + [&is_block](const Instruction&) { is_block = true; }); + return is_block; +} + +bool Instruction::IsReadOnlyPointerShaders() const { + if (type_id() == 0) { + return false; + } + + Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id()); + if (type_def->opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t storage_class = + type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex); + + switch (storage_class) { + case SpvStorageClassUniformConstant: + if (!type_def->IsVulkanStorageImage() && + !type_def->IsVulkanStorageTexelBuffer()) { + return true; + } + break; + case SpvStorageClassUniform: + if (!type_def->IsVulkanStorageBuffer()) { + return true; + } + break; + case SpvStorageClassPushConstant: + case SpvStorageClassInput: + return true; + default: + break; + } + + bool is_nonwritable = false; + context()->get_decoration_mgr()->ForEachDecoration( + result_id(), SpvDecorationNonWritable, + [&is_nonwritable](const Instruction&) { is_nonwritable = true; }); + return is_nonwritable; +} + +bool Instruction::IsReadOnlyPointerKernel() const { + if (type_id() == 0) { + return false; + } + + Instruction* type_def = context()->get_def_use_mgr()->GetDef(type_id()); + if (type_def->opcode() != SpvOpTypePointer) { + return false; + } + + uint32_t storage_class = + type_def->GetSingleWordInOperand(kPointerTypeStorageClassIndex); + + return storage_class == SpvStorageClassUniformConstant; +} + +uint32_t Instruction::GetTypeComponent(uint32_t element) const { + uint32_t subtype = 0; + switch (opcode()) { + case SpvOpTypeStruct: + subtype = GetSingleWordInOperand(element); + break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + // These types all have uniform subtypes. + subtype = GetSingleWordInOperand(0u); + break; + default: + break; + } + + return subtype; +} + +void Instruction::UpdateLexicalScope(uint32_t scope) { + dbg_scope_.SetLexicalScope(scope); + for (auto& i : dbg_line_insts_) { + i.dbg_scope_.SetLexicalScope(scope); + } + if (!IsDebugLineInst(opcode()) && + context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { + context()->get_debug_info_mgr()->AnalyzeDebugInst(this); + } +} + +void Instruction::UpdateDebugInlinedAt(uint32_t new_inlined_at) { + dbg_scope_.SetInlinedAt(new_inlined_at); + for (auto& i : dbg_line_insts_) { + i.dbg_scope_.SetInlinedAt(new_inlined_at); + } + if (!IsDebugLineInst(opcode()) && + context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { + context()->get_debug_info_mgr()->AnalyzeDebugInst(this); + } +} + +void Instruction::UpdateDebugInfoFrom(const Instruction* from) { + if (from == nullptr) return; + clear_dbg_line_insts(); + if (!from->dbg_line_insts().empty()) + dbg_line_insts().push_back(from->dbg_line_insts().back()); + SetDebugScope(from->GetDebugScope()); + if (!IsDebugLineInst(opcode()) && + context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) { + context()->get_debug_info_mgr()->AnalyzeDebugInst(this); + } +} + +Instruction* Instruction::InsertBefore(std::unique_ptr&& inst) { + inst.get()->InsertBefore(this); + return inst.release(); +} + +Instruction* Instruction::InsertBefore( + std::vector>&& list) { + Instruction* first_node = list.front().get(); + for (auto& inst : list) { + inst.release()->InsertBefore(this); + } + list.clear(); + return first_node; +} + +bool Instruction::IsValidBasePointer() const { + uint32_t tid = type_id(); + if (tid == 0) { + return false; + } + + Instruction* type = context()->get_def_use_mgr()->GetDef(tid); + if (type->opcode() != SpvOpTypePointer) { + return false; + } + + auto feature_mgr = context()->get_feature_mgr(); + if (feature_mgr->HasCapability(SpvCapabilityAddresses)) { + // TODO: The rules here could be more restrictive. + return true; + } + + if (opcode() == SpvOpVariable || opcode() == SpvOpFunctionParameter) { + return true; + } + + // With variable pointers, there are more valid base pointer objects. + // Variable pointers implicitly declares Variable pointers storage buffer. + SpvStorageClass storage_class = + static_cast(type->GetSingleWordInOperand(0)); + if ((feature_mgr->HasCapability(SpvCapabilityVariablePointersStorageBuffer) && + storage_class == SpvStorageClassStorageBuffer) || + (feature_mgr->HasCapability(SpvCapabilityVariablePointers) && + storage_class == SpvStorageClassWorkgroup)) { + switch (opcode()) { + case SpvOpPhi: + case SpvOpSelect: + case SpvOpFunctionCall: + case SpvOpConstantNull: + return true; + default: + break; + } + } + + uint32_t pointee_type_id = type->GetSingleWordInOperand(1); + Instruction* pointee_type_inst = + context()->get_def_use_mgr()->GetDef(pointee_type_id); + + if (pointee_type_inst->IsOpaqueType()) { + return true; + } + return false; +} + +OpenCLDebugInfo100Instructions Instruction::GetOpenCL100DebugOpcode() const { + if (opcode() != SpvOpExtInst) { + return OpenCLDebugInfo100InstructionsMax; + } + + if (!context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + return OpenCLDebugInfo100InstructionsMax; + } + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) != + context()->get_feature_mgr()->GetExtInstImportId_OpenCL100DebugInfo()) { + return OpenCLDebugInfo100InstructionsMax; + } + + return OpenCLDebugInfo100Instructions( + GetSingleWordInOperand(kExtInstInstructionInIdx)); +} + +bool Instruction::IsValidBaseImage() const { + uint32_t tid = type_id(); + if (tid == 0) { + return false; + } + + Instruction* type = context()->get_def_use_mgr()->GetDef(tid); + return (type->opcode() == SpvOpTypeImage || + type->opcode() == SpvOpTypeSampledImage); +} + +bool Instruction::IsOpaqueType() const { + if (opcode() == SpvOpTypeStruct) { + bool is_opaque = false; + ForEachInOperand([&is_opaque, this](const uint32_t* op_id) { + Instruction* type_inst = context()->get_def_use_mgr()->GetDef(*op_id); + is_opaque |= type_inst->IsOpaqueType(); + }); + return is_opaque; + } else if (opcode() == SpvOpTypeArray) { + uint32_t sub_type_id = GetSingleWordInOperand(0); + Instruction* sub_type_inst = + context()->get_def_use_mgr()->GetDef(sub_type_id); + return sub_type_inst->IsOpaqueType(); + } else { + return opcode() == SpvOpTypeRuntimeArray || + spvOpcodeIsBaseOpaqueType(opcode()); + } +} + +bool Instruction::IsFoldable() const { + return IsFoldableByFoldScalar() || + context()->get_instruction_folder().HasConstFoldingRule(this); +} + +bool Instruction::IsFoldableByFoldScalar() const { + const InstructionFolder& folder = context()->get_instruction_folder(); + if (!folder.IsFoldableOpcode(opcode())) { + return false; + } + + Instruction* type = context()->get_def_use_mgr()->GetDef(type_id()); + if (!folder.IsFoldableType(type)) { + return false; + } + + // Even if the type of the instruction is foldable, its operands may not be + // foldable (e.g., comparisons of 64bit types). Check that all operand types + // are foldable before accepting the instruction. + return WhileEachInOperand([&folder, this](const uint32_t* op_id) { + Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id); + Instruction* def_inst_type = + context()->get_def_use_mgr()->GetDef(def_inst->type_id()); + return folder.IsFoldableType(def_inst_type); + }); +} + +bool Instruction::IsFloatingPointFoldingAllowed() const { + // TODO: Add the rules for kernels. For now it will be pessimistic. + // For now, do not support capabilities introduced by SPV_KHR_float_controls. + if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader) || + context_->get_feature_mgr()->HasCapability(SpvCapabilityDenormPreserve) || + context_->get_feature_mgr()->HasCapability( + SpvCapabilityDenormFlushToZero) || + context_->get_feature_mgr()->HasCapability( + SpvCapabilitySignedZeroInfNanPreserve) || + context_->get_feature_mgr()->HasCapability( + SpvCapabilityRoundingModeRTZ) || + context_->get_feature_mgr()->HasCapability( + SpvCapabilityRoundingModeRTE)) { + return false; + } + + bool is_nocontract = false; + context_->get_decoration_mgr()->WhileEachDecoration( + result_id(), SpvDecorationNoContraction, + [&is_nocontract](const Instruction&) { + is_nocontract = true; + return false; + }); + return !is_nocontract; +} + +std::string Instruction::PrettyPrint(uint32_t options) const { + // Convert the module to binary. + std::vector module_binary; + context()->module()->ToBinary(&module_binary, /* skip_nop = */ false); + + // Convert the instruction to binary. This is used to identify the correct + // stream of words to output from the module. + std::vector inst_binary; + ToBinaryWithoutAttachedDebugInsts(&inst_binary); + + // Do not generate a header. + return spvInstructionBinaryToText( + context()->grammar().target_env(), inst_binary.data(), inst_binary.size(), + module_binary.data(), module_binary.size(), + options | SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); +} + +std::ostream& operator<<(std::ostream& str, const Instruction& inst) { + str << inst.PrettyPrint(); + return str; +} + +void Instruction::Dump() const { + std::cerr << "Instruction #" << unique_id() << "\n" << *this << "\n"; +} + +bool Instruction::IsOpcodeCodeMotionSafe() const { + switch (opcode_) { + case SpvOpNop: + case SpvOpUndef: + case SpvOpLoad: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpArrayLength: + case SpvOpVectorExtractDynamic: + case SpvOpVectorInsertDynamic: + case SpvOpVectorShuffle: + case SpvOpCompositeConstruct: + case SpvOpCompositeExtract: + case SpvOpCompositeInsert: + case SpvOpCopyObject: + case SpvOpTranspose: + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpUConvert: + case SpvOpSConvert: + case SpvOpFConvert: + case SpvOpQuantizeToF16: + case SpvOpBitcast: + case SpvOpSNegate: + case SpvOpFNegate: + case SpvOpIAdd: + case SpvOpFAdd: + case SpvOpISub: + case SpvOpFSub: + case SpvOpIMul: + case SpvOpFMul: + case SpvOpUDiv: + case SpvOpSDiv: + case SpvOpFDiv: + case SpvOpUMod: + case SpvOpSRem: + case SpvOpSMod: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpVectorTimesScalar: + case SpvOpMatrixTimesScalar: + case SpvOpVectorTimesMatrix: + case SpvOpMatrixTimesVector: + case SpvOpMatrixTimesMatrix: + case SpvOpOuterProduct: + case SpvOpDot: + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: + case SpvOpAny: + case SpvOpAll: + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: + case SpvOpLogicalNot: + case SpvOpSelect: + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: + case SpvOpBitFieldInsert: + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: + case SpvOpBitReverse: + case SpvOpBitCount: + case SpvOpSizeOf: + return true; + default: + return false; + } +} + +bool Instruction::IsScalarizable() const { + if (spvOpcodeIsScalarizable(opcode())) { + return true; + } + + if (opcode() == SpvOpExtInst) { + uint32_t instSetId = + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450(); + + if (GetSingleWordInOperand(kExtInstSetIdInIdx) == instSetId) { + switch (GetSingleWordInOperand(kExtInstInstructionInIdx)) { + case GLSLstd450Round: + case GLSLstd450RoundEven: + case GLSLstd450Trunc: + case GLSLstd450FAbs: + case GLSLstd450SAbs: + case GLSLstd450FSign: + case GLSLstd450SSign: + case GLSLstd450Floor: + case GLSLstd450Ceil: + case GLSLstd450Fract: + case GLSLstd450Radians: + case GLSLstd450Degrees: + case GLSLstd450Sin: + case GLSLstd450Cos: + case GLSLstd450Tan: + case GLSLstd450Asin: + case GLSLstd450Acos: + case GLSLstd450Atan: + case GLSLstd450Sinh: + case GLSLstd450Cosh: + case GLSLstd450Tanh: + case GLSLstd450Asinh: + case GLSLstd450Acosh: + case GLSLstd450Atanh: + case GLSLstd450Atan2: + case GLSLstd450Pow: + case GLSLstd450Exp: + case GLSLstd450Log: + case GLSLstd450Exp2: + case GLSLstd450Log2: + case GLSLstd450Sqrt: + case GLSLstd450InverseSqrt: + case GLSLstd450Modf: + case GLSLstd450FMin: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450FMax: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450FClamp: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FMix: + case GLSLstd450Step: + case GLSLstd450SmoothStep: + case GLSLstd450Fma: + case GLSLstd450Frexp: + case GLSLstd450Ldexp: + case GLSLstd450FindILsb: + case GLSLstd450FindSMsb: + case GLSLstd450FindUMsb: + case GLSLstd450NMin: + case GLSLstd450NMax: + case GLSLstd450NClamp: + return true; + default: + return false; + } + } + } + return false; +} + +bool Instruction::IsOpcodeSafeToDelete() const { + if (context()->IsCombinatorInstruction(this)) { + return true; + } + + switch (opcode()) { + case SpvOpDPdx: + case SpvOpDPdy: + case SpvOpFwidth: + case SpvOpDPdxFine: + case SpvOpDPdyFine: + case SpvOpFwidthFine: + case SpvOpDPdxCoarse: + case SpvOpDPdyCoarse: + case SpvOpFwidthCoarse: + case SpvOpImageQueryLod: + return true; + default: + return false; + } +} + +bool Instruction::IsNonSemanticInstruction() const { + if (!HasResultId()) return false; + if (opcode() != SpvOpExtInst) return false; + + auto import_inst = + context()->get_def_use_mgr()->GetDef(GetSingleWordInOperand(0)); + std::string import_name = import_inst->GetInOperand(0).AsString(); + return import_name.find("NonSemantic.") == 0; +} + +void DebugScope::ToBinary(uint32_t type_id, uint32_t result_id, + uint32_t ext_set, + std::vector* binary) const { + uint32_t num_words = kDebugScopeNumWords; + OpenCLDebugInfo100Instructions dbg_opcode = OpenCLDebugInfo100DebugScope; + if (GetLexicalScope() == kNoDebugScope) { + num_words = kDebugNoScopeNumWords; + dbg_opcode = OpenCLDebugInfo100DebugNoScope; + } else if (GetInlinedAt() == kNoInlinedAt) { + num_words = kDebugScopeNumWordsWithoutInlinedAt; + } + std::vector operands = { + (num_words << 16) | static_cast(SpvOpExtInst), + type_id, + result_id, + ext_set, + static_cast(dbg_opcode), + }; + binary->insert(binary->end(), operands.begin(), operands.end()); + if (GetLexicalScope() != kNoDebugScope) { + binary->push_back(GetLexicalScope()); + if (GetInlinedAt() != kNoInlinedAt) binary->push_back(GetInlinedAt()); + } +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/instruction.h b/third_party/spirv-tools/source/opt/instruction.h new file mode 100644 index 0000000..252e8cb --- /dev/null +++ b/third_party/spirv-tools/source/opt/instruction.h @@ -0,0 +1,867 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INSTRUCTION_H_ +#define SOURCE_OPT_INSTRUCTION_H_ + +#include +#include +#include +#include +#include +#include + +#include "OpenCLDebugInfo100.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/latest_version_spirv_header.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/opt/reflect.h" +#include "source/util/ilist_node.h" +#include "source/util/small_vector.h" +#include "spirv-tools/libspirv.h" + +const uint32_t kNoDebugScope = 0; +const uint32_t kNoInlinedAt = 0; + +namespace spvtools { +namespace opt { + +class Function; +class IRContext; +class Module; +class InstructionList; + +// Relaxed logical addressing: +// +// In the logical addressing model, pointers cannot be stored or loaded. This +// is a useful assumption because it simplifies the aliasing significantly. +// However, for the purpose of legalizing code generated from HLSL, we will have +// to allow storing and loading of pointers to opaque objects and runtime +// arrays. This relaxation of the rule still implies that function and private +// scope variables do not have any aliasing, so we can treat them as before. +// This will be call the relaxed logical addressing model. +// +// This relaxation of the rule will be allowed by |GetBaseAddress|, but it will +// enforce that no other pointers are stored or loaded. + +// About operand: +// +// In the SPIR-V specification, the term "operand" is used to mean any single +// SPIR-V word following the leading wordcount-opcode word. Here, the term +// "operand" is used to mean a *logical* operand. A logical operand may consist +// of multiple SPIR-V words, which together make up the same component. For +// example, a logical operand of a 64-bit integer needs two words to express. +// +// Further, we categorize logical operands into *in* and *out* operands. +// In operands are operands actually serve as input to operations, while out +// operands are operands that represent ids generated from operations (result +// type id or result id). For example, for "OpIAdd %rtype %rid %inop1 %inop2", +// "%inop1" and "%inop2" are in operands, while "%rtype" and "%rid" are out +// operands. + +// A *logical* operand to a SPIR-V instruction. It can be the type id, result +// id, or other additional operands carried in an instruction. +struct Operand { + using OperandData = utils::SmallVector; + Operand(spv_operand_type_t t, OperandData&& w) + : type(t), words(std::move(w)) {} + + Operand(spv_operand_type_t t, const OperandData& w) : type(t), words(w) {} + + spv_operand_type_t type; // Type of this logical operand. + OperandData words; // Binary segments of this logical operand. + + // Returns a string operand as a C-style string. + const char* AsCString() const { + assert(type == SPV_OPERAND_TYPE_LITERAL_STRING); + return reinterpret_cast(words.data()); + } + + // Returns a string operand as a std::string. + std::string AsString() const { return AsCString(); } + + // Returns a literal integer operand as a uint64_t + uint64_t AsLiteralUint64() const { + assert(type == SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER); + assert(1 <= words.size()); + assert(words.size() <= 2); + // Load the low word. + uint64_t result = uint64_t(words[0]); + if (words.size() > 1) { + result = result | (uint64_t(words[1]) << 32); + } + return result; + } + + friend bool operator==(const Operand& o1, const Operand& o2) { + return o1.type == o2.type && o1.words == o2.words; + } + + // TODO(antiagainst): create fields for literal number kind, width, etc. +}; + +inline bool operator!=(const Operand& o1, const Operand& o2) { + return !(o1 == o2); +} + +// This structure is used to represent a DebugScope instruction from +// the OpenCL.100.DebugInfo extened instruction set. Note that we can +// ignore the result id of DebugScope instruction because it is not +// used for anything. We do not keep it to reduce the size of +// structure. +// TODO: Let validator check that the result id is not used anywhere. +class DebugScope { + public: + DebugScope(uint32_t lexical_scope, uint32_t inlined_at) + : lexical_scope_(lexical_scope), inlined_at_(inlined_at) {} + + inline bool operator!=(const DebugScope& d) const { + return lexical_scope_ != d.lexical_scope_ || inlined_at_ != d.inlined_at_; + } + + // Accessor functions for |lexical_scope_|. + uint32_t GetLexicalScope() const { return lexical_scope_; } + void SetLexicalScope(uint32_t scope) { lexical_scope_ = scope; } + + // Accessor functions for |inlined_at_|. + uint32_t GetInlinedAt() const { return inlined_at_; } + void SetInlinedAt(uint32_t at) { inlined_at_ = at; } + + // Pushes the binary segments for this DebugScope instruction into + // the back of *|binary|. + void ToBinary(uint32_t type_id, uint32_t result_id, uint32_t ext_set, + std::vector* binary) const; + + private: + // The result id of the lexical scope in which this debug scope is + // contained. The value is kNoDebugScope if there is no scope. + uint32_t lexical_scope_; + + // The result id of DebugInlinedAt if instruction in this debug scope + // is inlined. The value is kNoInlinedAt if it is not inlined. + uint32_t inlined_at_; +}; + +// A SPIR-V instruction. It contains the opcode and any additional logical +// operand, including the result id (if any) and result type id (if any). It +// may also contain line-related debug instruction (OpLine, OpNoLine) directly +// appearing before this instruction. Note that the result id of an instruction +// should never change after the instruction being built. If the result id +// needs to change, the user should create a new instruction instead. +class Instruction : public utils::IntrusiveNodeBase { + public: + using OperandList = std::vector; + using iterator = OperandList::iterator; + using const_iterator = OperandList::const_iterator; + + // Creates a default OpNop instruction. + // This exists solely for containers that can't do without. Should be removed. + Instruction() + : utils::IntrusiveNodeBase(), + context_(nullptr), + opcode_(SpvOpNop), + has_type_id_(false), + has_result_id_(false), + unique_id_(0), + dbg_scope_(kNoDebugScope, kNoInlinedAt) {} + + // Creates a default OpNop instruction. + Instruction(IRContext*); + // Creates an instruction with the given opcode |op| and no additional logical + // operands. + Instruction(IRContext*, SpvOp); + // Creates an instruction using the given spv_parsed_instruction_t |inst|. All + // the data inside |inst| will be copied and owned in this instance. And keep + // record of line-related debug instructions |dbg_line| ahead of this + // instruction, if any. + Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + std::vector&& dbg_line = {}); + + Instruction(IRContext* c, const spv_parsed_instruction_t& inst, + const DebugScope& dbg_scope); + + // Creates an instruction with the given opcode |op|, type id: |ty_id|, + // result id: |res_id| and input operands: |in_operands|. + Instruction(IRContext* c, SpvOp op, uint32_t ty_id, uint32_t res_id, + const OperandList& in_operands); + + // TODO: I will want to remove these, but will first have to remove the use of + // std::vector. + Instruction(const Instruction&) = default; + Instruction& operator=(const Instruction&) = default; + + Instruction(Instruction&&); + Instruction& operator=(Instruction&&); + + virtual ~Instruction() = default; + + // Returns a newly allocated instruction that has the same operands, result, + // and type as |this|. The new instruction is not linked into any list. + // It is the responsibility of the caller to make sure that the storage is + // removed. It is the caller's responsibility to make sure that there is only + // one instruction for each result id. + Instruction* Clone(IRContext* c) const; + + IRContext* context() const { return context_; } + + SpvOp opcode() const { return opcode_; } + // Sets the opcode of this instruction to a specific opcode. Note this may + // invalidate the instruction. + // TODO(qining): Remove this function when instruction building and insertion + // is well implemented. + void SetOpcode(SpvOp op) { opcode_ = op; } + uint32_t type_id() const { + return has_type_id_ ? GetSingleWordOperand(0) : 0; + } + uint32_t result_id() const { + return has_result_id_ ? GetSingleWordOperand(has_type_id_ ? 1 : 0) : 0; + } + uint32_t unique_id() const { + assert(unique_id_ != 0); + return unique_id_; + } + // Returns the vector of line-related debug instructions attached to this + // instruction and the caller can directly modify them. + std::vector& dbg_line_insts() { return dbg_line_insts_; } + const std::vector& dbg_line_insts() const { + return dbg_line_insts_; + } + + const Instruction* dbg_line_inst() const { + return dbg_line_insts_.empty() ? nullptr : &dbg_line_insts_[0]; + } + + // Clear line-related debug instructions attached to this instruction. + void clear_dbg_line_insts() { dbg_line_insts_.clear(); } + + // Set line-related debug instructions. + void set_dbg_line_insts(const std::vector& lines) { + dbg_line_insts_ = lines; + } + + // Same semantics as in the base class except the list the InstructionList + // containing |pos| will now assume ownership of |this|. + // inline void MoveBefore(Instruction* pos); + // inline void InsertAfter(Instruction* pos); + + // Begin and end iterators for operands. + iterator begin() { return operands_.begin(); } + iterator end() { return operands_.end(); } + const_iterator begin() const { return operands_.cbegin(); } + const_iterator end() const { return operands_.cend(); } + // Const begin and end iterators for operands. + const_iterator cbegin() const { return operands_.cbegin(); } + const_iterator cend() const { return operands_.cend(); } + + // Gets the number of logical operands. + uint32_t NumOperands() const { + return static_cast(operands_.size()); + } + // Gets the number of SPIR-V words occupied by all logical operands. + uint32_t NumOperandWords() const { + return NumInOperandWords() + TypeResultIdCount(); + } + // Gets the |index|-th logical operand. + inline Operand& GetOperand(uint32_t index); + inline const Operand& GetOperand(uint32_t index) const; + // Adds |operand| to the list of operands of this instruction. + // It is the responsibility of the caller to make sure + // that the instruction remains valid. + inline void AddOperand(Operand&& operand); + // Gets the |index|-th logical operand as a single SPIR-V word. This method is + // not expected to be used with logical operands consisting of multiple SPIR-V + // words. + uint32_t GetSingleWordOperand(uint32_t index) const; + // Sets the |index|-th in-operand's data to the given |data|. + inline void SetInOperand(uint32_t index, Operand::OperandData&& data); + // Sets the |index|-th operand's data to the given |data|. + // This is for in-operands modification only, but with |index| expressed in + // terms of operand index rather than in-operand index. + inline void SetOperand(uint32_t index, Operand::OperandData&& data); + // Replace all of the in operands with those in |new_operands|. + inline void SetInOperands(OperandList&& new_operands); + // Sets the result type id. + inline void SetResultType(uint32_t ty_id); + // Sets the result id + inline void SetResultId(uint32_t res_id); + inline bool HasResultId() const { return has_result_id_; } + // Sets DebugScope. + inline void SetDebugScope(const DebugScope& scope); + inline const DebugScope& GetDebugScope() const { return dbg_scope_; } + // Updates DebugInlinedAt of DebugScope and OpLine. + void UpdateDebugInlinedAt(uint32_t new_inlined_at); + inline uint32_t GetDebugInlinedAt() const { + return dbg_scope_.GetInlinedAt(); + } + // Updates lexical scope of DebugScope and OpLine. + void UpdateLexicalScope(uint32_t scope); + // Updates OpLine and DebugScope based on the information of |from|. + void UpdateDebugInfoFrom(const Instruction* from); + // Remove the |index|-th operand + void RemoveOperand(uint32_t index) { + operands_.erase(operands_.begin() + index); + } + // Insert an operand before the |index|-th operand + void InsertOperand(uint32_t index, Operand&& operand) { + operands_.insert(operands_.begin() + index, operand); + } + + // The following methods are similar to the above, but are for in operands. + uint32_t NumInOperands() const { + return static_cast(operands_.size() - TypeResultIdCount()); + } + uint32_t NumInOperandWords() const; + Operand& GetInOperand(uint32_t index) { + return GetOperand(index + TypeResultIdCount()); + } + const Operand& GetInOperand(uint32_t index) const { + return GetOperand(index + TypeResultIdCount()); + } + uint32_t GetSingleWordInOperand(uint32_t index) const { + return GetSingleWordOperand(index + TypeResultIdCount()); + } + void RemoveInOperand(uint32_t index) { + operands_.erase(operands_.begin() + index + TypeResultIdCount()); + } + + // Returns true if this instruction is OpNop. + inline bool IsNop() const; + // Turns this instruction to OpNop. This does not clear out all preceding + // line-related debug instructions. + inline void ToNop(); + + // Runs the given function |f| on this instruction and optionally on the + // preceding debug line instructions. The function will always be run + // if this is itself a debug line instruction. + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Runs the given function |f| on this instruction and optionally on the + // preceding debug line instructions. The function will always be run + // if this is itself a debug line instruction. If |f| returns false, + // iteration is terminated and this function returns false. + inline bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + inline bool WhileEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Runs the given function |f| on all operand ids. + // + // |f| should not transform an ID into 0, as 0 is an invalid ID. + inline void ForEachId(const std::function& f); + inline void ForEachId(const std::function& f) const; + + // Runs the given function |f| on all "in" operand ids. + inline void ForEachInId(const std::function& f); + inline void ForEachInId(const std::function& f) const; + + // Runs the given function |f| on all "in" operand ids. If |f| returns false, + // iteration is terminated and this function returns false. + inline bool WhileEachInId(const std::function& f); + inline bool WhileEachInId( + const std::function& f) const; + + // Runs the given function |f| on all "in" operands. + inline void ForEachInOperand(const std::function& f); + inline void ForEachInOperand( + const std::function& f) const; + + // Runs the given function |f| on all "in" operands. If |f| returns false, + // iteration is terminated and this function return false. + inline bool WhileEachInOperand(const std::function& f); + inline bool WhileEachInOperand( + const std::function& f) const; + + // Returns true if it's an OpBranchConditional instruction + // with branch weights. + bool HasBranchWeights() const; + + // Returns true if any operands can be labels + inline bool HasLabels() const; + + // Pushes the binary segments for this instruction into the back of *|binary|. + void ToBinaryWithoutAttachedDebugInsts(std::vector* binary) const; + + // Replaces the operands to the instruction with |new_operands|. The caller + // is responsible for building a complete and valid list of operands for + // this instruction. + void ReplaceOperands(const OperandList& new_operands); + + // Returns true if the instruction annotates an id with a decoration. + inline bool IsDecoration() const; + + // Returns true if the instruction is known to be a load from read-only + // memory. + bool IsReadOnlyLoad() const; + + // Returns the instruction that gives the base address of an address + // calculation. The instruction must be a load, as defined by |IsLoad|, + // store, copy, or access chain instruction. In logical addressing mode, will + // return an OpVariable or OpFunctionParameter instruction. For relaxed + // logical addressing, it would also return a load of a pointer to an opaque + // object. For physical addressing mode, could return other types of + // instructions. + Instruction* GetBaseAddress() const; + + // Returns true if the instruction loads from memory or samples an image, and + // stores the result into an id. It considers only core instructions. + // Memory-to-memory instructions are not considered loads. + inline bool IsLoad() const; + + // Returns true if the instruction generates a pointer that is definitely + // read-only. This is determined by analysing the pointer type's storage + // class and decorations that target the pointer's id. It does not analyse + // other instructions that the pointer may be derived from. Thus if 'true' is + // returned, the pointer is definitely read-only, while if 'false' is returned + // it is possible that the pointer may actually be read-only if it is derived + // from another pointer that is decorated as read-only. + bool IsReadOnlyPointer() const; + + // The following functions check for the various descriptor types defined in + // the Vulkan specification section 13.1. + + // Returns true if the instruction defines a pointer type that points to a + // storage image. + bool IsVulkanStorageImage() const; + + // Returns true if the instruction defines a pointer type that points to a + // sampled image. + bool IsVulkanSampledImage() const; + + // Returns true if the instruction defines a pointer type that points to a + // storage texel buffer. + bool IsVulkanStorageTexelBuffer() const; + + // Returns true if the instruction defines a pointer type that points to a + // storage buffer. + bool IsVulkanStorageBuffer() const; + + // Returns true if the instruction defines a pointer type that points to a + // uniform buffer. + bool IsVulkanUniformBuffer() const; + + // Returns true if the instruction is an atom operation that uses original + // value. + inline bool IsAtomicWithLoad() const; + + // Returns true if the instruction is an atom operation. + inline bool IsAtomicOp() const; + + // Returns true if this instruction is a branch or switch instruction (either + // conditional or not). + bool IsBranch() const { return spvOpcodeIsBranch(opcode()); } + + // Returns true if this instruction causes the function to finish execution + // and return to its caller + bool IsReturn() const { return spvOpcodeIsReturn(opcode()); } + + // Returns true if this instruction exits this function or aborts execution. + bool IsReturnOrAbort() const { return spvOpcodeIsReturnOrAbort(opcode()); } + + // Returns the id for the |element|'th subtype. If the |this| is not a + // composite type, this function returns 0. + uint32_t GetTypeComponent(uint32_t element) const; + + // Returns true if this instruction is a basic block terminator. + bool IsBlockTerminator() const { + return spvOpcodeIsBlockTerminator(opcode()); + } + + // Returns true if |this| is an instruction that define an opaque type. Since + // runtime array have similar characteristics they are included as opaque + // types. + bool IsOpaqueType() const; + + // Returns true if |this| is an instruction which could be folded into a + // constant value. + bool IsFoldable() const; + + // Returns true if |this| is an instruction which could be folded into a + // constant value by |FoldScalar|. + bool IsFoldableByFoldScalar() const; + + // Returns true if we are allowed to fold or otherwise manipulate the + // instruction that defines |id| in the given context. This includes not + // handling NaN values. + bool IsFloatingPointFoldingAllowed() const; + + inline bool operator==(const Instruction&) const; + inline bool operator!=(const Instruction&) const; + inline bool operator<(const Instruction&) const; + + // Takes ownership of the instruction owned by |i| and inserts it immediately + // before |this|. Returns the inserted instruction. + Instruction* InsertBefore(std::unique_ptr&& i); + // Takes ownership of the instructions in |list| and inserts them in order + // immediately before |this|. Returns the first inserted instruction. + // Assumes the list is non-empty. + Instruction* InsertBefore(std::vector>&& list); + using utils::IntrusiveNodeBase::InsertBefore; + + // Returns true if |this| is an instruction defining a constant, but not a + // Spec constant. + inline bool IsConstant() const; + + // Returns true if |this| is an instruction with an opcode safe to move + bool IsOpcodeCodeMotionSafe() const; + + // Pretty-prints |inst|. + // + // Provides the disassembly of a specific instruction. Utilizes |inst|'s + // context to provide the correct interpretation of types, constants, etc. + // + // |options| are the disassembly options. SPV_BINARY_TO_TEXT_OPTION_NO_HEADER + // is always added to |options|. + std::string PrettyPrint(uint32_t options = 0u) const; + + // Returns true if the result can be a vector and the result of each component + // depends on the corresponding component of any vector inputs. + bool IsScalarizable() const; + + // Return true if the only effect of this instructions is the result. + bool IsOpcodeSafeToDelete() const; + + // Returns true if it is valid to use the result of |inst| as the base + // pointer for a load or store. In this case, valid is defined by the relaxed + // logical addressing rules when using logical addressing. Normal validation + // rules for physical addressing. + bool IsValidBasePointer() const; + + // Returns debug opcode of an OpenCL.100.DebugInfo instruction. If + // it is not an OpenCL.100.DebugInfo instruction, just returns + // OpenCLDebugInfo100InstructionsMax. + OpenCLDebugInfo100Instructions GetOpenCL100DebugOpcode() const; + + // Returns true if it is an OpenCL.DebugInfo.100 instruction. + bool IsOpenCL100DebugInstr() const { + return GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax; + } + + // Returns true if this instructions a non-semantic instruction. + bool IsNonSemanticInstruction() const; + + // Dump this instruction on stderr. Useful when running interactive + // debuggers. + void Dump() const; + + private: + // Returns the total count of result type id and result id. + uint32_t TypeResultIdCount() const { + if (has_type_id_ && has_result_id_) return 2; + if (has_type_id_ || has_result_id_) return 1; + return 0; + } + + // Returns true if the instruction generates a read-only pointer, with the + // same caveats documented in the comment for IsReadOnlyPointer. The first + // version assumes the module is a shader module. The second assumes a + // kernel. + bool IsReadOnlyPointerShaders() const; + bool IsReadOnlyPointerKernel() const; + + // Returns true if the result of |inst| can be used as the base image for an + // instruction that samples a image, reads an image, or writes to an image. + bool IsValidBaseImage() const; + + IRContext* context_; // IR Context + SpvOp opcode_; // Opcode + bool has_type_id_; // True if the instruction has a type id + bool has_result_id_; // True if the instruction has a result id + uint32_t unique_id_; // Unique instruction id + // All logical operands, including result type id and result id. + OperandList operands_; + // Opline and OpNoLine instructions preceding this instruction. Note that for + // Instructions representing OpLine or OpNonLine itself, this field should be + // empty. + std::vector dbg_line_insts_; + + // DebugScope that wraps this instruction. + DebugScope dbg_scope_; + + friend InstructionList; +}; + +// Pretty-prints |inst| to |str| and returns |str|. +// +// Provides the disassembly of a specific instruction. Utilizes |inst|'s context +// to provide the correct interpretation of types, constants, etc. +// +// Disassembly uses raw ids (not pretty printed names). +std::ostream& operator<<(std::ostream& str, const Instruction& inst); + +inline bool Instruction::operator==(const Instruction& other) const { + return unique_id() == other.unique_id(); +} + +inline bool Instruction::operator!=(const Instruction& other) const { + return !(*this == other); +} + +inline bool Instruction::operator<(const Instruction& other) const { + return unique_id() < other.unique_id(); +} + +inline Operand& Instruction::GetOperand(uint32_t index) { + assert(index < operands_.size() && "operand index out of bound"); + return operands_[index]; +} + +inline const Operand& Instruction::GetOperand(uint32_t index) const { + assert(index < operands_.size() && "operand index out of bound"); + return operands_[index]; +} + +inline void Instruction::AddOperand(Operand&& operand) { + operands_.push_back(std::move(operand)); +} + +inline void Instruction::SetInOperand(uint32_t index, + Operand::OperandData&& data) { + SetOperand(index + TypeResultIdCount(), std::move(data)); +} + +inline void Instruction::SetOperand(uint32_t index, + Operand::OperandData&& data) { + assert(index < operands_.size() && "operand index out of bound"); + assert(index >= TypeResultIdCount() && "operand is not a in-operand"); + operands_[index].words = std::move(data); +} + +inline void Instruction::SetInOperands(OperandList&& new_operands) { + // Remove the old in operands. + operands_.erase(operands_.begin() + TypeResultIdCount(), operands_.end()); + // Add the new in operands. + operands_.insert(operands_.end(), new_operands.begin(), new_operands.end()); +} + +inline void Instruction::SetResultId(uint32_t res_id) { + // TODO(dsinclair): Allow setting a result id if there wasn't one + // previously. Need to make room in the operands_ array to place the result, + // and update the has_result_id_ flag. + assert(has_result_id_); + + // TODO(dsinclair): Allow removing the result id. This needs to make sure, + // if there was a result id previously to remove it from the operands_ array + // and reset the has_result_id_ flag. + assert(res_id != 0); + + auto ridx = has_type_id_ ? 1 : 0; + operands_[ridx].words = {res_id}; +} + +inline void Instruction::SetDebugScope(const DebugScope& scope) { + dbg_scope_ = scope; + for (auto& i : dbg_line_insts_) { + i.dbg_scope_ = scope; + } +} + +inline void Instruction::SetResultType(uint32_t ty_id) { + // TODO(dsinclair): Allow setting a type id if there wasn't one + // previously. Need to make room in the operands_ array to place the result, + // and update the has_type_id_ flag. + assert(has_type_id_); + + // TODO(dsinclair): Allow removing the type id. This needs to make sure, + // if there was a type id previously to remove it from the operands_ array + // and reset the has_type_id_ flag. + assert(ty_id != 0); + + operands_.front().words = {ty_id}; +} + +inline bool Instruction::IsNop() const { + return opcode_ == SpvOpNop && !has_type_id_ && !has_result_id_ && + operands_.empty(); +} + +inline void Instruction::ToNop() { + opcode_ = SpvOpNop; + has_type_id_ = false; + has_result_id_ = false; + operands_.clear(); +} + +inline bool Instruction::WhileEachInst( + const std::function& f, bool run_on_debug_line_insts) { + if (run_on_debug_line_insts) { + for (auto& dbg_line : dbg_line_insts_) { + if (!f(&dbg_line)) return false; + } + } + return f(this); +} + +inline bool Instruction::WhileEachInst( + const std::function& f, + bool run_on_debug_line_insts) const { + if (run_on_debug_line_insts) { + for (auto& dbg_line : dbg_line_insts_) { + if (!f(&dbg_line)) return false; + } + } + return f(this); +} + +inline void Instruction::ForEachInst(const std::function& f, + bool run_on_debug_line_insts) { + WhileEachInst( + [&f](Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +inline void Instruction::ForEachInst( + const std::function& f, + bool run_on_debug_line_insts) const { + WhileEachInst( + [&f](const Instruction* inst) { + f(inst); + return true; + }, + run_on_debug_line_insts); +} + +inline void Instruction::ForEachId(const std::function& f) { + for (auto& operand : operands_) + if (spvIsIdType(operand.type)) f(&operand.words[0]); +} + +inline void Instruction::ForEachId( + const std::function& f) const { + for (const auto& operand : operands_) + if (spvIsIdType(operand.type)) f(&operand.words[0]); +} + +inline bool Instruction::WhileEachInId( + const std::function& f) { + for (auto& operand : operands_) { + if (spvIsInIdType(operand.type) && !f(&operand.words[0])) { + return false; + } + } + return true; +} + +inline bool Instruction::WhileEachInId( + const std::function& f) const { + for (const auto& operand : operands_) { + if (spvIsInIdType(operand.type) && !f(&operand.words[0])) { + return false; + } + } + return true; +} + +inline void Instruction::ForEachInId(const std::function& f) { + WhileEachInId([&f](uint32_t* id) { + f(id); + return true; + }); +} + +inline void Instruction::ForEachInId( + const std::function& f) const { + WhileEachInId([&f](const uint32_t* id) { + f(id); + return true; + }); +} + +inline bool Instruction::WhileEachInOperand( + const std::function& f) { + for (auto& operand : operands_) { + switch (operand.type) { + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + break; + default: + if (!f(&operand.words[0])) return false; + break; + } + } + return true; +} + +inline bool Instruction::WhileEachInOperand( + const std::function& f) const { + for (const auto& operand : operands_) { + switch (operand.type) { + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + break; + default: + if (!f(&operand.words[0])) return false; + break; + } + } + return true; +} + +inline void Instruction::ForEachInOperand( + const std::function& f) { + WhileEachInOperand([&f](uint32_t* operand) { + f(operand); + return true; + }); +} + +inline void Instruction::ForEachInOperand( + const std::function& f) const { + WhileEachInOperand([&f](const uint32_t* operand) { + f(operand); + return true; + }); +} + +inline bool Instruction::HasLabels() const { + switch (opcode_) { + case SpvOpSelectionMerge: + case SpvOpBranch: + case SpvOpLoopMerge: + case SpvOpBranchConditional: + case SpvOpSwitch: + case SpvOpPhi: + return true; + break; + default: + break; + } + return false; +} + +bool Instruction::IsDecoration() const { + return spvOpcodeIsDecoration(opcode()); +} + +bool Instruction::IsLoad() const { return spvOpcodeIsLoad(opcode()); } + +bool Instruction::IsAtomicWithLoad() const { + return spvOpcodeIsAtomicWithLoad(opcode()); +} + +bool Instruction::IsAtomicOp() const { return spvOpcodeIsAtomicOp(opcode()); } + +bool Instruction::IsConstant() const { + return IsCompileTimeConstantInst(opcode()); +} +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INSTRUCTION_H_ diff --git a/third_party/spirv-tools/source/opt/instruction_list.cpp b/third_party/spirv-tools/source/opt/instruction_list.cpp new file mode 100644 index 0000000..385a136 --- /dev/null +++ b/third_party/spirv-tools/source/opt/instruction_list.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/instruction_list.h" + +namespace spvtools { +namespace opt { + +InstructionList::iterator InstructionList::iterator::InsertBefore( + std::vector>&& list) { + Instruction* first_node = list.front().get(); + for (auto& i : list) { + i.release()->InsertBefore(node_); + } + list.clear(); + return iterator(first_node); +} + +InstructionList::iterator InstructionList::iterator::InsertBefore( + std::unique_ptr&& i) { + i.get()->InsertBefore(node_); + return iterator(i.release()); +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/instruction_list.h b/third_party/spirv-tools/source/opt/instruction_list.h new file mode 100644 index 0000000..417cbd7 --- /dev/null +++ b/third_party/spirv-tools/source/opt/instruction_list.h @@ -0,0 +1,140 @@ + +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_INSTRUCTION_LIST_H_ +#define SOURCE_OPT_INSTRUCTION_LIST_H_ + +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "source/operand.h" +#include "source/opt/instruction.h" +#include "source/util/ilist.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace opt { + +// This class is intended to be the container for Instructions. This container +// owns the instructions that are in it. When removing an Instruction from the +// list, the caller is assuming responsibility for deleting the storage. +// +// TODO: Because there are a number of other data structures that will want +// pointers to instruction, ownership should probably be moved to the module. +// Because of that I have not made the ownership passing in this class fully +// explicit. For example, RemoveFromList takes ownership from the list, but +// does not return an std::unique_ptr to signal that. When we fully decide on +// ownership, this will have to be fixed up one way or the other. +class InstructionList : public utils::IntrusiveList { + public: + InstructionList() = default; + InstructionList(InstructionList&& that) + : utils::IntrusiveList(std::move(that)) {} + InstructionList& operator=(InstructionList&& that) { + auto p = static_cast*>(this); + *p = std::move(that); + return *this; + } + + // Destroy this list and any instructions in the list. + inline virtual ~InstructionList(); + + class iterator : public utils::IntrusiveList::iterator { + public: + iterator(const utils::IntrusiveList::iterator& i) + : utils::IntrusiveList::iterator(i) {} + iterator(Instruction* i) : utils::IntrusiveList::iterator(i) {} + + iterator& operator++() { + utils::IntrusiveList::iterator::operator++(); + return *this; + } + + iterator& operator--() { + utils::IntrusiveList::iterator::operator--(); + return *this; + } + + // DEPRECATED: Please use MoveBefore with an InstructionList instead. + // + // Moves the nodes in |list| to the list that |this| points to. The + // positions of the nodes will be immediately before the element pointed to + // by the iterator. The return value will be an iterator pointing to the + // first of the newly inserted elements. Ownership of the elements in + // |list| is now passed on to |*this|. + iterator InsertBefore(std::vector>&& list); + + // The node |i| will be inserted immediately before |this|. The return value + // will be an iterator pointing to the newly inserted node. The owner of + // |*i| becomes |*this| + iterator InsertBefore(std::unique_ptr&& i); + + // Removes the node from the list, and deletes the storage. Returns a valid + // iterator to the next node. + iterator Erase() { + iterator_template next_node = *this; + ++next_node; + node_->RemoveFromList(); + delete node_; + return next_node; + } + }; + + iterator begin() { return utils::IntrusiveList::begin(); } + iterator end() { return utils::IntrusiveList::end(); } + const_iterator begin() const { + return utils::IntrusiveList::begin(); + } + const_iterator end() const { + return utils::IntrusiveList::end(); + } + + void push_back(std::unique_ptr&& inst) { + utils::IntrusiveList::push_back(inst.release()); + } + + // Same as in the base class, except it will delete the data as well. + inline void clear(); + + // Runs the given function |f| on the instructions in the list and optionally + // on the preceding debug line instructions. + inline void ForEachInst(const std::function& f, + bool run_on_debug_line_insts) { + auto next = begin(); + for (auto i = next; i != end(); i = next) { + ++next; + i->ForEachInst(f, run_on_debug_line_insts); + } + } +}; + +InstructionList::~InstructionList() { clear(); } + +void InstructionList::clear() { + while (!empty()) { + Instruction* inst = &front(); + inst->RemoveFromList(); + delete inst; + } +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_INSTRUCTION_LIST_H_ diff --git a/third_party/spirv-tools/source/opt/instrument_pass.cpp b/third_party/spirv-tools/source/opt/instrument_pass.cpp new file mode 100644 index 0000000..e7d9778 --- /dev/null +++ b/third_party/spirv-tools/source/opt/instrument_pass.cpp @@ -0,0 +1,1128 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "instrument_pass.h" + +#include "source/cfa.h" +#include "source/spirv_constant.h" + +namespace { + +// Common Parameter Positions +static const int kInstCommonParamInstIdx = 0; +static const int kInstCommonParamCnt = 1; + +// Indices of operands in SPIR-V instructions +static const int kEntryPointExecutionModelInIdx = 0; +static const int kEntryPointFunctionIdInIdx = 1; + +} // anonymous namespace + +namespace spvtools { +namespace opt { + +void InstrumentPass::MovePreludeCode( + BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, + std::unique_ptr* new_blk_ptr) { + same_block_pre_.clear(); + same_block_post_.clear(); + // Initialize new block. Reuse label from original block. + new_blk_ptr->reset(new BasicBlock(std::move(ref_block_itr->GetLabel()))); + // Move contents of original ref block up to ref instruction. + for (auto cii = ref_block_itr->begin(); cii != ref_inst_itr; + cii = ref_block_itr->begin()) { + Instruction* inst = &*cii; + inst->RemoveFromList(); + std::unique_ptr mv_ptr(inst); + // Remember same-block ops for possible regeneration. + if (IsSameBlockOp(&*mv_ptr)) { + auto* sb_inst_ptr = mv_ptr.get(); + same_block_pre_[mv_ptr->result_id()] = sb_inst_ptr; + } + (*new_blk_ptr)->AddInstruction(std::move(mv_ptr)); + } +} + +void InstrumentPass::MovePostludeCode( + UptrVectorIterator ref_block_itr, BasicBlock* new_blk_ptr) { + // new_blk_ptr->reset(new BasicBlock(NewLabel(ref_block_itr->id()))); + // Move contents of original ref block. + for (auto cii = ref_block_itr->begin(); cii != ref_block_itr->end(); + cii = ref_block_itr->begin()) { + Instruction* inst = &*cii; + inst->RemoveFromList(); + std::unique_ptr mv_inst(inst); + // Regenerate any same-block instruction that has not been seen in the + // current block. + if (same_block_pre_.size() > 0) { + CloneSameBlockOps(&mv_inst, &same_block_post_, &same_block_pre_, + new_blk_ptr); + // Remember same-block ops in this block. + if (IsSameBlockOp(&*mv_inst)) { + const uint32_t rid = mv_inst->result_id(); + same_block_post_[rid] = rid; + } + } + new_blk_ptr->AddInstruction(std::move(mv_inst)); + } +} + +std::unique_ptr InstrumentPass::NewLabel(uint32_t label_id) { + std::unique_ptr newLabel( + new Instruction(context(), SpvOpLabel, 0, label_id, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*newLabel); + return newLabel; +} + +uint32_t InstrumentPass::Gen32BitCvtCode(uint32_t val_id, + InstructionBuilder* builder) { + // Convert integer value to 32-bit if necessary + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + uint32_t val_ty_id = get_def_use_mgr()->GetDef(val_id)->type_id(); + analysis::Integer* val_ty = type_mgr->GetType(val_ty_id)->AsInteger(); + if (val_ty->width() == 32) return val_id; + bool is_signed = val_ty->IsSigned(); + analysis::Integer val_32b_ty(32, is_signed); + analysis::Type* val_32b_reg_ty = type_mgr->GetRegisteredType(&val_32b_ty); + uint32_t val_32b_reg_ty_id = type_mgr->GetId(val_32b_reg_ty); + if (is_signed) + return builder->AddUnaryOp(val_32b_reg_ty_id, SpvOpSConvert, val_id) + ->result_id(); + else + return builder->AddUnaryOp(val_32b_reg_ty_id, SpvOpUConvert, val_id) + ->result_id(); +} + +uint32_t InstrumentPass::GenUintCastCode(uint32_t val_id, + InstructionBuilder* builder) { + // Convert value to 32-bit if necessary + uint32_t val_32b_id = Gen32BitCvtCode(val_id, builder); + // Cast value to unsigned if necessary + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + uint32_t val_ty_id = get_def_use_mgr()->GetDef(val_32b_id)->type_id(); + analysis::Integer* val_ty = type_mgr->GetType(val_ty_id)->AsInteger(); + if (!val_ty->IsSigned()) return val_32b_id; + return builder->AddUnaryOp(GetUintId(), SpvOpBitcast, val_32b_id) + ->result_id(); +} + +void InstrumentPass::GenDebugOutputFieldCode(uint32_t base_offset_id, + uint32_t field_offset, + uint32_t field_value_id, + InstructionBuilder* builder) { + // Cast value to 32-bit unsigned if necessary + uint32_t val_id = GenUintCastCode(field_value_id, builder); + // Store value + Instruction* data_idx_inst = + builder->AddBinaryOp(GetUintId(), SpvOpIAdd, base_offset_id, + builder->GetUintConstantId(field_offset)); + uint32_t buf_id = GetOutputBufferId(); + uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); + Instruction* achain_inst = + builder->AddTernaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, + builder->GetUintConstantId(kDebugOutputDataOffset), + data_idx_inst->result_id()); + (void)builder->AddBinaryOp(0, SpvOpStore, achain_inst->result_id(), val_id); +} + +void InstrumentPass::GenCommonStreamWriteCode(uint32_t record_sz, + uint32_t inst_id, + uint32_t stage_idx, + uint32_t base_offset_id, + InstructionBuilder* builder) { + // Store record size + GenDebugOutputFieldCode(base_offset_id, kInstCommonOutSize, + builder->GetUintConstantId(record_sz), builder); + // Store Shader Id + GenDebugOutputFieldCode(base_offset_id, kInstCommonOutShaderId, + builder->GetUintConstantId(shader_id_), builder); + // Store Instruction Idx + GenDebugOutputFieldCode(base_offset_id, kInstCommonOutInstructionIdx, inst_id, + builder); + // Store Stage Idx + GenDebugOutputFieldCode(base_offset_id, kInstCommonOutStageIdx, + builder->GetUintConstantId(stage_idx), builder); +} + +void InstrumentPass::GenFragCoordEltDebugOutputCode( + uint32_t base_offset_id, uint32_t uint_frag_coord_id, uint32_t element, + InstructionBuilder* builder) { + Instruction* element_val_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, uint_frag_coord_id, element); + GenDebugOutputFieldCode(base_offset_id, kInstFragOutFragCoordX + element, + element_val_inst->result_id(), builder); +} + +uint32_t InstrumentPass::GenVarLoad(uint32_t var_id, + InstructionBuilder* builder) { + Instruction* var_inst = get_def_use_mgr()->GetDef(var_id); + uint32_t type_id = GetPointeeTypeId(var_inst); + Instruction* load_inst = builder->AddUnaryOp(type_id, SpvOpLoad, var_id); + return load_inst->result_id(); +} + +void InstrumentPass::GenBuiltinOutputCode(uint32_t builtin_id, + uint32_t builtin_off, + uint32_t base_offset_id, + InstructionBuilder* builder) { + // Load and store builtin + uint32_t load_id = GenVarLoad(builtin_id, builder); + GenDebugOutputFieldCode(base_offset_id, builtin_off, load_id, builder); +} + +void InstrumentPass::GenStageStreamWriteCode(uint32_t stage_idx, + uint32_t base_offset_id, + InstructionBuilder* builder) { + // TODO(greg-lunarg): Add support for all stages + switch (stage_idx) { + case SpvExecutionModelVertex: { + // Load and store VertexId and InstanceId + GenBuiltinOutputCode( + context()->GetBuiltinInputVarId(SpvBuiltInVertexIndex), + kInstVertOutVertexIndex, base_offset_id, builder); + GenBuiltinOutputCode( + context()->GetBuiltinInputVarId(SpvBuiltInInstanceIndex), + kInstVertOutInstanceIndex, base_offset_id, builder); + } break; + case SpvExecutionModelGLCompute: + case SpvExecutionModelTaskNV: + case SpvExecutionModelMeshNV: { + // Load and store GlobalInvocationId. + uint32_t load_id = GenVarLoad( + context()->GetBuiltinInputVarId(SpvBuiltInGlobalInvocationId), + builder); + Instruction* x_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, load_id, 0); + Instruction* y_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, load_id, 1); + Instruction* z_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, load_id, 2); + GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdX, + x_inst->result_id(), builder); + GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdY, + y_inst->result_id(), builder); + GenDebugOutputFieldCode(base_offset_id, kInstCompOutGlobalInvocationIdZ, + z_inst->result_id(), builder); + } break; + case SpvExecutionModelGeometry: { + // Load and store PrimitiveId and InvocationId. + GenBuiltinOutputCode( + context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), + kInstGeomOutPrimitiveId, base_offset_id, builder); + GenBuiltinOutputCode( + context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), + kInstGeomOutInvocationId, base_offset_id, builder); + } break; + case SpvExecutionModelTessellationControl: { + // Load and store InvocationId and PrimitiveId + GenBuiltinOutputCode( + context()->GetBuiltinInputVarId(SpvBuiltInInvocationId), + kInstTessCtlOutInvocationId, base_offset_id, builder); + GenBuiltinOutputCode( + context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), + kInstTessCtlOutPrimitiveId, base_offset_id, builder); + } break; + case SpvExecutionModelTessellationEvaluation: { + // Load and store PrimitiveId and TessCoord.uv + GenBuiltinOutputCode( + context()->GetBuiltinInputVarId(SpvBuiltInPrimitiveId), + kInstTessEvalOutPrimitiveId, base_offset_id, builder); + uint32_t load_id = GenVarLoad( + context()->GetBuiltinInputVarId(SpvBuiltInTessCoord), builder); + Instruction* uvec3_cast_inst = + builder->AddUnaryOp(GetVec3UintId(), SpvOpBitcast, load_id); + uint32_t uvec3_cast_id = uvec3_cast_inst->result_id(); + Instruction* u_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 0); + Instruction* v_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, uvec3_cast_id, 1); + GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordU, + u_inst->result_id(), builder); + GenDebugOutputFieldCode(base_offset_id, kInstTessEvalOutTessCoordV, + v_inst->result_id(), builder); + } break; + case SpvExecutionModelFragment: { + // Load FragCoord and convert to Uint + Instruction* frag_coord_inst = builder->AddUnaryOp( + GetVec4FloatId(), SpvOpLoad, + context()->GetBuiltinInputVarId(SpvBuiltInFragCoord)); + Instruction* uint_frag_coord_inst = builder->AddUnaryOp( + GetVec4UintId(), SpvOpBitcast, frag_coord_inst->result_id()); + for (uint32_t u = 0; u < 2u; ++u) + GenFragCoordEltDebugOutputCode( + base_offset_id, uint_frag_coord_inst->result_id(), u, builder); + } break; + case SpvExecutionModelRayGenerationNV: + case SpvExecutionModelIntersectionNV: + case SpvExecutionModelAnyHitNV: + case SpvExecutionModelClosestHitNV: + case SpvExecutionModelMissNV: + case SpvExecutionModelCallableNV: { + // Load and store LaunchIdNV. + uint32_t launch_id = GenVarLoad( + context()->GetBuiltinInputVarId(SpvBuiltInLaunchIdNV), builder); + Instruction* x_launch_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, launch_id, 0); + Instruction* y_launch_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, launch_id, 1); + Instruction* z_launch_inst = builder->AddIdLiteralOp( + GetUintId(), SpvOpCompositeExtract, launch_id, 2); + GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdX, + x_launch_inst->result_id(), builder); + GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdY, + y_launch_inst->result_id(), builder); + GenDebugOutputFieldCode(base_offset_id, kInstRayTracingOutLaunchIdZ, + z_launch_inst->result_id(), builder); + } break; + default: { assert(false && "unsupported stage"); } break; + } +} + +void InstrumentPass::GenDebugStreamWrite( + uint32_t instruction_idx, uint32_t stage_idx, + const std::vector& validation_ids, InstructionBuilder* builder) { + // Call debug output function. Pass func_idx, instruction_idx and + // validation ids as args. + uint32_t val_id_cnt = static_cast(validation_ids.size()); + uint32_t output_func_id = GetStreamWriteFunctionId(stage_idx, val_id_cnt); + std::vector args = {output_func_id, + builder->GetUintConstantId(instruction_idx)}; + (void)args.insert(args.end(), validation_ids.begin(), validation_ids.end()); + (void)builder->AddNaryOp(GetVoidId(), SpvOpFunctionCall, args); +} + +bool InstrumentPass::AllConstant(const std::vector& ids) { + for (auto& id : ids) { + Instruction* id_inst = context()->get_def_use_mgr()->GetDef(id); + if (!spvOpcodeIsConstant(id_inst->opcode())) return false; + } + return true; +} + +uint32_t InstrumentPass::GenDebugDirectRead( + const std::vector& offset_ids, InstructionBuilder* ref_builder) { + // Call debug input function. Pass func_idx and offset ids as args. + uint32_t off_id_cnt = static_cast(offset_ids.size()); + uint32_t input_func_id = GetDirectReadFunctionId(off_id_cnt); + std::vector args = {input_func_id}; + (void)args.insert(args.end(), offset_ids.begin(), offset_ids.end()); + // If optimizing direct reads and the call has already been generated, + // use its result + if (opt_direct_reads_) { + uint32_t res_id = call2id_[args]; + if (res_id != 0) return res_id; + } + // If the offsets are all constants, the call can be moved to the first block + // of the function where its result can be reused. One example where this is + // profitable is for uniform buffer references, of which there are often many. + InstructionBuilder builder(ref_builder->GetContext(), + &*ref_builder->GetInsertPoint(), + ref_builder->GetPreservedAnalysis()); + bool insert_in_first_block = opt_direct_reads_ && AllConstant(offset_ids); + if (insert_in_first_block) { + Instruction* insert_before = &*curr_func_->begin()->tail(); + builder.SetInsertPoint(insert_before); + } + uint32_t res_id = + builder.AddNaryOp(GetUintId(), SpvOpFunctionCall, args)->result_id(); + if (insert_in_first_block) call2id_[args] = res_id; + return res_id; +} + +bool InstrumentPass::IsSameBlockOp(const Instruction* inst) const { + return inst->opcode() == SpvOpSampledImage || inst->opcode() == SpvOpImage; +} + +void InstrumentPass::CloneSameBlockOps( + std::unique_ptr* inst, + std::unordered_map* same_blk_post, + std::unordered_map* same_blk_pre, + BasicBlock* block_ptr) { + bool changed = false; + (*inst)->ForEachInId([&same_blk_post, &same_blk_pre, &block_ptr, &changed, + this](uint32_t* iid) { + const auto map_itr = (*same_blk_post).find(*iid); + if (map_itr == (*same_blk_post).end()) { + const auto map_itr2 = (*same_blk_pre).find(*iid); + if (map_itr2 != (*same_blk_pre).end()) { + // Clone pre-call same-block ops, map result id. + const Instruction* in_inst = map_itr2->second; + std::unique_ptr sb_inst(in_inst->Clone(context())); + const uint32_t rid = sb_inst->result_id(); + const uint32_t nid = this->TakeNextId(); + get_decoration_mgr()->CloneDecorations(rid, nid); + sb_inst->SetResultId(nid); + get_def_use_mgr()->AnalyzeInstDefUse(&*sb_inst); + (*same_blk_post)[rid] = nid; + *iid = nid; + changed = true; + CloneSameBlockOps(&sb_inst, same_blk_post, same_blk_pre, block_ptr); + block_ptr->AddInstruction(std::move(sb_inst)); + } + } else { + // Reset same-block op operand if necessary + if (*iid != map_itr->second) { + *iid = map_itr->second; + changed = true; + } + } + }); + if (changed) get_def_use_mgr()->AnalyzeInstUse(&**inst); +} + +void InstrumentPass::UpdateSucceedingPhis( + std::vector>& new_blocks) { + const auto first_blk = new_blocks.begin(); + const auto last_blk = new_blocks.end() - 1; + const uint32_t first_id = (*first_blk)->id(); + const uint32_t last_id = (*last_blk)->id(); + const BasicBlock& const_last_block = *last_blk->get(); + const_last_block.ForEachSuccessorLabel( + [&first_id, &last_id, this](const uint32_t succ) { + BasicBlock* sbp = this->id2block_[succ]; + sbp->ForEachPhiInst([&first_id, &last_id, this](Instruction* phi) { + bool changed = false; + phi->ForEachInId([&first_id, &last_id, &changed](uint32_t* id) { + if (*id == first_id) { + *id = last_id; + changed = true; + } + }); + if (changed) get_def_use_mgr()->AnalyzeInstUse(phi); + }); + }); +} + +uint32_t InstrumentPass::GetOutputBufferPtrId() { + if (output_buffer_ptr_id_ == 0) { + output_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( + GetUintId(), SpvStorageClassStorageBuffer); + } + return output_buffer_ptr_id_; +} + +uint32_t InstrumentPass::GetInputBufferTypeId() { + return (validation_id_ == kInstValidationIdBuffAddr) ? GetUint64Id() + : GetUintId(); +} + +uint32_t InstrumentPass::GetInputBufferPtrId() { + if (input_buffer_ptr_id_ == 0) { + input_buffer_ptr_id_ = context()->get_type_mgr()->FindPointerToType( + GetInputBufferTypeId(), SpvStorageClassStorageBuffer); + } + return input_buffer_ptr_id_; +} + +uint32_t InstrumentPass::GetOutputBufferBinding() { + switch (validation_id_) { + case kInstValidationIdBindless: + return kDebugOutputBindingStream; + case kInstValidationIdBuffAddr: + return kDebugOutputBindingStream; + case kInstValidationIdDebugPrintf: + return kDebugOutputPrintfStream; + default: + assert(false && "unexpected validation id"); + } + return 0; +} + +uint32_t InstrumentPass::GetInputBufferBinding() { + switch (validation_id_) { + case kInstValidationIdBindless: + return kDebugInputBindingBindless; + case kInstValidationIdBuffAddr: + return kDebugInputBindingBuffAddr; + default: + assert(false && "unexpected validation id"); + } + return 0; +} + +analysis::Type* InstrumentPass::GetUintXRuntimeArrayType( + uint32_t width, analysis::Type** rarr_ty) { + if (*rarr_ty == nullptr) { + analysis::DecorationManager* deco_mgr = get_decoration_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint_ty(width, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::RuntimeArray uint_rarr_ty_tmp(reg_uint_ty); + *rarr_ty = type_mgr->GetRegisteredType(&uint_rarr_ty_tmp); + uint32_t uint_arr_ty_id = type_mgr->GetTypeInstruction(*rarr_ty); + // By the Vulkan spec, a pre-existing RuntimeArray of uint must be part of + // a block, and will therefore be decorated with an ArrayStride. Therefore + // the undecorated type returned here will not be pre-existing and can + // safely be decorated. Since this type is now decorated, it is out of + // sync with the TypeManager and therefore the TypeManager must be + // invalidated after this pass. + assert(context()->get_def_use_mgr()->NumUses(uint_arr_ty_id) == 0 && + "used RuntimeArray type returned"); + deco_mgr->AddDecorationVal(uint_arr_ty_id, SpvDecorationArrayStride, + width / 8u); + } + return *rarr_ty; +} + +analysis::Type* InstrumentPass::GetUintRuntimeArrayType(uint32_t width) { + analysis::Type** rarr_ty = + (width == 64) ? &uint64_rarr_ty_ : &uint32_rarr_ty_; + return GetUintXRuntimeArrayType(width, rarr_ty); +} + +void InstrumentPass::AddStorageBufferExt() { + if (storage_buffer_ext_defined_) return; + if (!get_feature_mgr()->HasExtension(kSPV_KHR_storage_buffer_storage_class)) { + context()->AddExtension("SPV_KHR_storage_buffer_storage_class"); + } + storage_buffer_ext_defined_ = true; +} + +// Return id for output buffer +uint32_t InstrumentPass::GetOutputBufferId() { + if (output_buffer_id_ == 0) { + // If not created yet, create one + analysis::DecorationManager* deco_mgr = get_decoration_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(32); + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Struct buf_ty({reg_uint_ty, reg_uint_rarr_ty}); + analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); + uint32_t obufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); + // By the Vulkan spec, a pre-existing struct containing a RuntimeArray + // must be a block, and will therefore be decorated with Block. Therefore + // the undecorated type returned here will not be pre-existing and can + // safely be decorated. Since this type is now decorated, it is out of + // sync with the TypeManager and therefore the TypeManager must be + // invalidated after this pass. + assert(context()->get_def_use_mgr()->NumUses(obufTyId) == 0 && + "used struct type returned"); + deco_mgr->AddDecoration(obufTyId, SpvDecorationBlock); + deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputSizeOffset, + SpvDecorationOffset, 0); + deco_mgr->AddMemberDecoration(obufTyId, kDebugOutputDataOffset, + SpvDecorationOffset, 4); + uint32_t obufTyPtrId_ = + type_mgr->FindPointerToType(obufTyId, SpvStorageClassStorageBuffer); + output_buffer_id_ = TakeNextId(); + std::unique_ptr newVarOp(new Instruction( + context(), SpvOpVariable, obufTyPtrId_, output_buffer_id_, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvStorageClassStorageBuffer}}})); + context()->AddGlobalValue(std::move(newVarOp)); + deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationDescriptorSet, + desc_set_); + deco_mgr->AddDecorationVal(output_buffer_id_, SpvDecorationBinding, + GetOutputBufferBinding()); + AddStorageBufferExt(); + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // Add the new buffer to all entry points. + for (auto& entry : get_module()->entry_points()) { + entry.AddOperand({SPV_OPERAND_TYPE_ID, {output_buffer_id_}}); + context()->AnalyzeUses(&entry); + } + } + } + return output_buffer_id_; +} + +uint32_t InstrumentPass::GetInputBufferId() { + if (input_buffer_id_ == 0) { + // If not created yet, create one + analysis::DecorationManager* deco_mgr = get_decoration_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + uint32_t width = (validation_id_ == kInstValidationIdBuffAddr) ? 64u : 32u; + analysis::Type* reg_uint_rarr_ty = GetUintRuntimeArrayType(width); + analysis::Struct buf_ty({reg_uint_rarr_ty}); + analysis::Type* reg_buf_ty = type_mgr->GetRegisteredType(&buf_ty); + uint32_t ibufTyId = type_mgr->GetTypeInstruction(reg_buf_ty); + // By the Vulkan spec, a pre-existing struct containing a RuntimeArray + // must be a block, and will therefore be decorated with Block. Therefore + // the undecorated type returned here will not be pre-existing and can + // safely be decorated. Since this type is now decorated, it is out of + // sync with the TypeManager and therefore the TypeManager must be + // invalidated after this pass. + assert(context()->get_def_use_mgr()->NumUses(ibufTyId) == 0 && + "used struct type returned"); + deco_mgr->AddDecoration(ibufTyId, SpvDecorationBlock); + deco_mgr->AddMemberDecoration(ibufTyId, 0, SpvDecorationOffset, 0); + uint32_t ibufTyPtrId_ = + type_mgr->FindPointerToType(ibufTyId, SpvStorageClassStorageBuffer); + input_buffer_id_ = TakeNextId(); + std::unique_ptr newVarOp(new Instruction( + context(), SpvOpVariable, ibufTyPtrId_, input_buffer_id_, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvStorageClassStorageBuffer}}})); + context()->AddGlobalValue(std::move(newVarOp)); + deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationDescriptorSet, + desc_set_); + deco_mgr->AddDecorationVal(input_buffer_id_, SpvDecorationBinding, + GetInputBufferBinding()); + AddStorageBufferExt(); + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // Add the new buffer to all entry points. + for (auto& entry : get_module()->entry_points()) { + entry.AddOperand({SPV_OPERAND_TYPE_ID, {input_buffer_id_}}); + context()->AnalyzeUses(&entry); + } + } + } + return input_buffer_id_; +} + +uint32_t InstrumentPass::GetFloatId() { + if (float_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + float_id_ = type_mgr->GetTypeInstruction(reg_float_ty); + } + return float_id_; +} + +uint32_t InstrumentPass::GetVec4FloatId() { + if (v4float_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v4float_ty(reg_float_ty, 4); + analysis::Type* reg_v4float_ty = type_mgr->GetRegisteredType(&v4float_ty); + v4float_id_ = type_mgr->GetTypeInstruction(reg_v4float_ty); + } + return v4float_id_; +} + +uint32_t InstrumentPass::GetUintId() { + if (uint_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + uint_id_ = type_mgr->GetTypeInstruction(reg_uint_ty); + } + return uint_id_; +} + +uint32_t InstrumentPass::GetUint64Id() { + if (uint64_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint64_ty(64, false); + analysis::Type* reg_uint64_ty = type_mgr->GetRegisteredType(&uint64_ty); + uint64_id_ = type_mgr->GetTypeInstruction(reg_uint64_ty); + } + return uint64_id_; +} + +uint32_t InstrumentPass::GetUint8Id() { + if (uint8_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint8_ty(8, false); + analysis::Type* reg_uint8_ty = type_mgr->GetRegisteredType(&uint8_ty); + uint8_id_ = type_mgr->GetTypeInstruction(reg_uint8_ty); + } + return uint8_id_; +} + +uint32_t InstrumentPass::GetVecUintId(uint32_t len) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Vector v_uint_ty(reg_uint_ty, len); + analysis::Type* reg_v_uint_ty = type_mgr->GetRegisteredType(&v_uint_ty); + uint32_t v_uint_id = type_mgr->GetTypeInstruction(reg_v_uint_ty); + return v_uint_id; +} + +uint32_t InstrumentPass::GetVec4UintId() { + if (v4uint_id_ == 0) v4uint_id_ = GetVecUintId(4u); + return v4uint_id_; +} + +uint32_t InstrumentPass::GetVec3UintId() { + if (v3uint_id_ == 0) v3uint_id_ = GetVecUintId(3u); + return v3uint_id_; +} + +uint32_t InstrumentPass::GetBoolId() { + if (bool_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Bool bool_ty; + analysis::Type* reg_bool_ty = type_mgr->GetRegisteredType(&bool_ty); + bool_id_ = type_mgr->GetTypeInstruction(reg_bool_ty); + } + return bool_id_; +} + +uint32_t InstrumentPass::GetVoidId() { + if (void_id_ == 0) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_ty; + analysis::Type* reg_void_ty = type_mgr->GetRegisteredType(&void_ty); + void_id_ = type_mgr->GetTypeInstruction(reg_void_ty); + } + return void_id_; +} + +uint32_t InstrumentPass::GetStreamWriteFunctionId(uint32_t stage_idx, + uint32_t val_spec_param_cnt) { + // Total param count is common params plus validation-specific + // params + uint32_t param_cnt = kInstCommonParamCnt + val_spec_param_cnt; + if (param2output_func_id_[param_cnt] == 0) { + // Create function + param2output_func_id_[param_cnt] = TakeNextId(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + std::vector param_types; + for (uint32_t c = 0; c < param_cnt; ++c) + param_types.push_back(type_mgr->GetType(GetUintId())); + analysis::Function func_ty(type_mgr->GetType(GetVoidId()), param_types); + analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); + std::unique_ptr func_inst( + new Instruction(get_module()->context(), SpvOpFunction, GetVoidId(), + param2output_func_id_[param_cnt], + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvFunctionControlMaskNone}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {type_mgr->GetTypeInstruction(reg_func_ty)}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); + std::unique_ptr output_func = + MakeUnique(std::move(func_inst)); + // Add parameters + std::vector param_vec; + for (uint32_t c = 0; c < param_cnt; ++c) { + uint32_t pid = TakeNextId(); + param_vec.push_back(pid); + std::unique_ptr param_inst( + new Instruction(get_module()->context(), SpvOpFunctionParameter, + GetUintId(), pid, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); + output_func->AddParameter(std::move(param_inst)); + } + // Create first block + uint32_t test_blk_id = TakeNextId(); + std::unique_ptr test_label(NewLabel(test_blk_id)); + std::unique_ptr new_blk_ptr = + MakeUnique(std::move(test_label)); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Gen test if debug output buffer size will not be exceeded. + uint32_t val_spec_offset = kInstStageOutCnt; + uint32_t obuf_record_sz = val_spec_offset + val_spec_param_cnt; + uint32_t buf_id = GetOutputBufferId(); + uint32_t buf_uint_ptr_id = GetOutputBufferPtrId(); + Instruction* obuf_curr_sz_ac_inst = + builder.AddBinaryOp(buf_uint_ptr_id, SpvOpAccessChain, buf_id, + builder.GetUintConstantId(kDebugOutputSizeOffset)); + // Fetch the current debug buffer written size atomically, adding the + // size of the record to be written. + uint32_t obuf_record_sz_id = builder.GetUintConstantId(obuf_record_sz); + uint32_t mask_none_id = builder.GetUintConstantId(SpvMemoryAccessMaskNone); + uint32_t scope_invok_id = builder.GetUintConstantId(SpvScopeInvocation); + Instruction* obuf_curr_sz_inst = builder.AddQuadOp( + GetUintId(), SpvOpAtomicIAdd, obuf_curr_sz_ac_inst->result_id(), + scope_invok_id, mask_none_id, obuf_record_sz_id); + uint32_t obuf_curr_sz_id = obuf_curr_sz_inst->result_id(); + // Compute new written size + Instruction* obuf_new_sz_inst = + builder.AddBinaryOp(GetUintId(), SpvOpIAdd, obuf_curr_sz_id, + builder.GetUintConstantId(obuf_record_sz)); + // Fetch the data bound + Instruction* obuf_bnd_inst = + builder.AddIdLiteralOp(GetUintId(), SpvOpArrayLength, + GetOutputBufferId(), kDebugOutputDataOffset); + // Test that new written size is less than or equal to debug output + // data bound + Instruction* obuf_safe_inst = builder.AddBinaryOp( + GetBoolId(), SpvOpULessThanEqual, obuf_new_sz_inst->result_id(), + obuf_bnd_inst->result_id()); + uint32_t merge_blk_id = TakeNextId(); + uint32_t write_blk_id = TakeNextId(); + std::unique_ptr merge_label(NewLabel(merge_blk_id)); + std::unique_ptr write_label(NewLabel(write_blk_id)); + (void)builder.AddConditionalBranch(obuf_safe_inst->result_id(), + write_blk_id, merge_blk_id, merge_blk_id, + SpvSelectionControlMaskNone); + // Close safety test block and gen write block + new_blk_ptr->SetParent(&*output_func); + output_func->AddBasicBlock(std::move(new_blk_ptr)); + new_blk_ptr = MakeUnique(std::move(write_label)); + builder.SetInsertPoint(&*new_blk_ptr); + // Generate common and stage-specific debug record members + GenCommonStreamWriteCode(obuf_record_sz, param_vec[kInstCommonParamInstIdx], + stage_idx, obuf_curr_sz_id, &builder); + GenStageStreamWriteCode(stage_idx, obuf_curr_sz_id, &builder); + // Gen writes of validation specific data + for (uint32_t i = 0; i < val_spec_param_cnt; ++i) { + GenDebugOutputFieldCode(obuf_curr_sz_id, val_spec_offset + i, + param_vec[kInstCommonParamCnt + i], &builder); + } + // Close write block and gen merge block + (void)builder.AddBranch(merge_blk_id); + new_blk_ptr->SetParent(&*output_func); + output_func->AddBasicBlock(std::move(new_blk_ptr)); + new_blk_ptr = MakeUnique(std::move(merge_label)); + builder.SetInsertPoint(&*new_blk_ptr); + // Close merge block and function and add function to module + (void)builder.AddNullaryOp(0, SpvOpReturn); + new_blk_ptr->SetParent(&*output_func); + output_func->AddBasicBlock(std::move(new_blk_ptr)); + std::unique_ptr func_end_inst( + new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); + output_func->SetFunctionEnd(std::move(func_end_inst)); + context()->AddFunction(std::move(output_func)); + } + return param2output_func_id_[param_cnt]; +} + +uint32_t InstrumentPass::GetDirectReadFunctionId(uint32_t param_cnt) { + uint32_t func_id = param2input_func_id_[param_cnt]; + if (func_id != 0) return func_id; + // Create input function for param_cnt. + func_id = TakeNextId(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + std::vector param_types; + for (uint32_t c = 0; c < param_cnt; ++c) + param_types.push_back(type_mgr->GetType(GetUintId())); + uint32_t ibuf_type_id = GetInputBufferTypeId(); + analysis::Function func_ty(type_mgr->GetType(ibuf_type_id), param_types); + analysis::Type* reg_func_ty = type_mgr->GetRegisteredType(&func_ty); + std::unique_ptr func_inst(new Instruction( + get_module()->context(), SpvOpFunction, ibuf_type_id, func_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvFunctionControlMaskNone}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {type_mgr->GetTypeInstruction(reg_func_ty)}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_inst); + std::unique_ptr input_func = + MakeUnique(std::move(func_inst)); + // Add parameters + std::vector param_vec; + for (uint32_t c = 0; c < param_cnt; ++c) { + uint32_t pid = TakeNextId(); + param_vec.push_back(pid); + std::unique_ptr param_inst(new Instruction( + get_module()->context(), SpvOpFunctionParameter, GetUintId(), pid, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*param_inst); + input_func->AddParameter(std::move(param_inst)); + } + // Create block + uint32_t blk_id = TakeNextId(); + std::unique_ptr blk_label(NewLabel(blk_id)); + std::unique_ptr new_blk_ptr = + MakeUnique(std::move(blk_label)); + InstructionBuilder builder( + context(), &*new_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // For each offset parameter, generate new offset with parameter, adding last + // loaded value if it exists, and load value from input buffer at new offset. + // Return last loaded value. + uint32_t buf_id = GetInputBufferId(); + uint32_t buf_ptr_id = GetInputBufferPtrId(); + uint32_t last_value_id = 0; + for (uint32_t p = 0; p < param_cnt; ++p) { + uint32_t offset_id; + if (p == 0) { + offset_id = param_vec[0]; + } else { + if (ibuf_type_id != GetUintId()) { + Instruction* ucvt_inst = + builder.AddUnaryOp(GetUintId(), SpvOpUConvert, last_value_id); + last_value_id = ucvt_inst->result_id(); + } + Instruction* offset_inst = builder.AddBinaryOp( + GetUintId(), SpvOpIAdd, last_value_id, param_vec[p]); + offset_id = offset_inst->result_id(); + } + Instruction* ac_inst = builder.AddTernaryOp( + buf_ptr_id, SpvOpAccessChain, buf_id, + builder.GetUintConstantId(kDebugInputDataOffset), offset_id); + Instruction* load_inst = + builder.AddUnaryOp(ibuf_type_id, SpvOpLoad, ac_inst->result_id()); + last_value_id = load_inst->result_id(); + } + (void)builder.AddInstruction(MakeUnique( + context(), SpvOpReturnValue, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {last_value_id}}})); + // Close block and function and add function to module + new_blk_ptr->SetParent(&*input_func); + input_func->AddBasicBlock(std::move(new_blk_ptr)); + std::unique_ptr func_end_inst( + new Instruction(get_module()->context(), SpvOpFunctionEnd, 0, 0, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*func_end_inst); + input_func->SetFunctionEnd(std::move(func_end_inst)); + context()->AddFunction(std::move(input_func)); + param2input_func_id_[param_cnt] = func_id; + return func_id; +} + +void InstrumentPass::SplitBlock( + BasicBlock::iterator inst_itr, UptrVectorIterator block_itr, + std::vector>* new_blocks) { + // Make sure def/use analysis is done before we start moving instructions + // out of function + (void)get_def_use_mgr(); + // Move original block's preceding instructions into first new block + std::unique_ptr first_blk_ptr; + MovePreludeCode(inst_itr, block_itr, &first_blk_ptr); + InstructionBuilder builder( + context(), &*first_blk_ptr, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + uint32_t split_blk_id = TakeNextId(); + std::unique_ptr split_label(NewLabel(split_blk_id)); + (void)builder.AddBranch(split_blk_id); + new_blocks->push_back(std::move(first_blk_ptr)); + // Move remaining instructions into split block and add to new blocks + std::unique_ptr split_blk_ptr( + new BasicBlock(std::move(split_label))); + MovePostludeCode(block_itr, &*split_blk_ptr); + new_blocks->push_back(std::move(split_blk_ptr)); +} + +bool InstrumentPass::InstrumentFunction(Function* func, uint32_t stage_idx, + InstProcessFunction& pfn) { + curr_func_ = func; + call2id_.clear(); + bool first_block_split = false; + bool modified = false; + // Apply instrumentation function to each instruction. + // Using block iterators here because of block erasures and insertions. + std::vector> new_blks; + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end();) { + // Split all executable instructions out of first block into a following + // block. This will allow function calls to be inserted into the first + // block without interfering with the instrumentation algorithm. + if (opt_direct_reads_ && !first_block_split) { + if (ii->opcode() != SpvOpVariable) { + SplitBlock(ii, bi, &new_blks); + first_block_split = true; + } + } else { + pfn(ii, bi, stage_idx, &new_blks); + } + // If no new code, continue + if (new_blks.size() == 0) { + ++ii; + continue; + } + // Add new blocks to label id map + for (auto& blk : new_blks) id2block_[blk->id()] = &*blk; + // If there are new blocks we know there will always be two or + // more, so update succeeding phis with label of new last block. + size_t newBlocksSize = new_blks.size(); + assert(newBlocksSize > 1); + UpdateSucceedingPhis(new_blks); + // Replace original block with new block(s) + bi = bi.Erase(); + for (auto& bb : new_blks) { + bb->SetParent(func); + } + bi = bi.InsertBefore(&new_blks); + // Reset block iterator to last new block + for (size_t i = 0; i < newBlocksSize - 1; i++) ++bi; + modified = true; + // Restart instrumenting at beginning of last new block, + // but skip over any new phi or copy instruction. + ii = bi->begin(); + if (ii->opcode() == SpvOpPhi || ii->opcode() == SpvOpCopyObject) ++ii; + new_blks.clear(); + } + } + return modified; +} + +bool InstrumentPass::InstProcessCallTreeFromRoots(InstProcessFunction& pfn, + std::queue* roots, + uint32_t stage_idx) { + bool modified = false; + std::unordered_set done; + // Don't process input and output functions + for (auto& ifn : param2input_func_id_) done.insert(ifn.second); + for (auto& ofn : param2output_func_id_) done.insert(ofn.second); + // Process all functions from roots + while (!roots->empty()) { + const uint32_t fi = roots->front(); + roots->pop(); + if (done.insert(fi).second) { + Function* fn = id2function_.at(fi); + // Add calls first so we don't add new output function + context()->AddCalls(fn, roots); + modified = InstrumentFunction(fn, stage_idx, pfn) || modified; + } + } + return modified; +} + +bool InstrumentPass::InstProcessEntryPointCallTree(InstProcessFunction& pfn) { + // Make sure all entry points have the same execution model. Do not + // instrument if they do not. + // TODO(greg-lunarg): Handle mixed stages. Technically, a shader module + // can contain entry points with different execution models, although + // such modules will likely be rare as GLSL and HLSL are geared toward + // one model per module. In such cases we will need + // to clone any functions which are in the call trees of entrypoints + // with differing execution models. + uint32_t ecnt = 0; + uint32_t stage = SpvExecutionModelMax; + for (auto& e : get_module()->entry_points()) { + if (ecnt == 0) + stage = e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx); + else if (e.GetSingleWordInOperand(kEntryPointExecutionModelInIdx) != + stage) { + if (consumer()) { + std::string message = "Mixed stage shader module not supported"; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } + return false; + } + ++ecnt; + } + // Check for supported stages + if (stage != SpvExecutionModelVertex && stage != SpvExecutionModelFragment && + stage != SpvExecutionModelGeometry && + stage != SpvExecutionModelGLCompute && + stage != SpvExecutionModelTessellationControl && + stage != SpvExecutionModelTessellationEvaluation && + stage != SpvExecutionModelTaskNV && stage != SpvExecutionModelMeshNV && + stage != SpvExecutionModelRayGenerationNV && + stage != SpvExecutionModelIntersectionNV && + stage != SpvExecutionModelAnyHitNV && + stage != SpvExecutionModelClosestHitNV && + stage != SpvExecutionModelMissNV && + stage != SpvExecutionModelCallableNV) { + if (consumer()) { + std::string message = "Stage not supported by instrumentation"; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } + return false; + } + // Add together the roots of all entry points + std::queue roots; + for (auto& e : get_module()->entry_points()) { + roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + } + bool modified = InstProcessCallTreeFromRoots(pfn, &roots, stage); + return modified; +} + +void InstrumentPass::InitializeInstrument() { + output_buffer_id_ = 0; + output_buffer_ptr_id_ = 0; + input_buffer_ptr_id_ = 0; + input_buffer_id_ = 0; + float_id_ = 0; + v4float_id_ = 0; + uint_id_ = 0; + uint64_id_ = 0; + uint8_id_ = 0; + v4uint_id_ = 0; + v3uint_id_ = 0; + bool_id_ = 0; + void_id_ = 0; + storage_buffer_ext_defined_ = false; + uint32_rarr_ty_ = nullptr; + uint64_rarr_ty_ = nullptr; + + // clear collections + id2function_.clear(); + id2block_.clear(); + + // clear maps + param2input_func_id_.clear(); + param2output_func_id_.clear(); + + // Initialize function and block maps. + for (auto& fn : *get_module()) { + id2function_[fn.result_id()] = &fn; + for (auto& blk : fn) { + id2block_[blk.id()] = &blk; + } + } + + // Remember original instruction offsets + uint32_t module_offset = 0; + Module* module = get_module(); + for (auto& i : context()->capabilities()) { + (void)i; + ++module_offset; + } + for (auto& i : module->extensions()) { + (void)i; + ++module_offset; + } + for (auto& i : module->ext_inst_imports()) { + (void)i; + ++module_offset; + } + ++module_offset; // memory_model + for (auto& i : module->entry_points()) { + (void)i; + ++module_offset; + } + for (auto& i : module->execution_modes()) { + (void)i; + ++module_offset; + } + for (auto& i : module->debugs1()) { + (void)i; + ++module_offset; + } + for (auto& i : module->debugs2()) { + (void)i; + ++module_offset; + } + for (auto& i : module->debugs3()) { + (void)i; + ++module_offset; + } + for (auto& i : module->ext_inst_debuginfo()) { + (void)i; + ++module_offset; + } + for (auto& i : module->annotations()) { + (void)i; + ++module_offset; + } + for (auto& i : module->types_values()) { + module_offset += 1; + module_offset += static_cast(i.dbg_line_insts().size()); + } + + auto curr_fn = get_module()->begin(); + for (; curr_fn != get_module()->end(); ++curr_fn) { + // Count function instruction + module_offset += 1; + curr_fn->ForEachParam( + [&module_offset](const Instruction*) { module_offset += 1; }, true); + for (auto& blk : *curr_fn) { + // Count label + module_offset += 1; + for (auto& inst : blk) { + module_offset += static_cast(inst.dbg_line_insts().size()); + uid2offset_[inst.unique_id()] = module_offset; + module_offset += 1; + } + } + // Count function end instruction + module_offset += 1; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/instrument_pass.h b/third_party/spirv-tools/source/opt/instrument_pass.h new file mode 100644 index 0000000..12b939d --- /dev/null +++ b/third_party/spirv-tools/source/opt/instrument_pass.h @@ -0,0 +1,475 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// Copyright (c) 2018 Valve Corporation +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_INSTRUMENT_PASS_H_ +#define LIBSPIRV_OPT_INSTRUMENT_PASS_H_ + +#include +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/opt/pass.h" +#include "spirv-tools/instrument.hpp" + +// This is a base class to assist in the creation of passes which instrument +// shader modules. More specifically, passes which replace instructions with a +// larger and more capable set of instructions. Commonly, these new +// instructions will add testing of operands and execute different +// instructions depending on the outcome, including outputting of debug +// information into a buffer created especially for that purpose. +// +// This class contains helper functions to create an InstProcessFunction, +// which is the heart of any derived class implementing a specific +// instrumentation pass. It takes an instruction as an argument, decides +// if it should be instrumented, and generates code to replace it. This class +// also supplies function InstProcessEntryPointCallTree which applies the +// InstProcessFunction to every reachable instruction in a module and replaces +// the instruction with new instructions if generated. +// +// Chief among the helper functions are output code generation functions, +// used to generate code in the shader which writes data to output buffers +// associated with that validation. Currently one such function, +// GenDebugStreamWrite, exists. Other such functions may be added in the +// future. Each is accompanied by documentation describing the format of +// its output buffer. +// +// A validation pass may read or write multiple buffers. All such buffers +// are located in a single debug descriptor set whose index is passed at the +// creation of the instrumentation pass. The bindings of the buffers used by +// a validation pass are permanantly assigned and fixed and documented by +// the kDebugOutput* static consts. + +namespace spvtools { +namespace opt { + +// Validation Ids +// These are used to identify the general validation being done and map to +// its output buffers. +static const uint32_t kInstValidationIdBindless = 0; +static const uint32_t kInstValidationIdBuffAddr = 1; +static const uint32_t kInstValidationIdDebugPrintf = 2; + +class InstrumentPass : public Pass { + using cbb_ptr = const BasicBlock*; + + public: + using InstProcessFunction = + std::function, + uint32_t, std::vector>*)>; + + ~InstrumentPass() override = default; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisDecorations | + IRContext::kAnalysisCombinators | IRContext::kAnalysisNameMap | + IRContext::kAnalysisBuiltinVarId | IRContext::kAnalysisConstants; + } + + protected: + // Create instrumentation pass for |validation_id| which utilizes descriptor + // set |desc_set| for debug input and output buffers and writes |shader_id| + // into debug output records. |opt_direct_reads| indicates that the pass + // will see direct input buffer reads and should prepare to optimize them. + InstrumentPass(uint32_t desc_set, uint32_t shader_id, uint32_t validation_id, + bool opt_direct_reads = false) + : Pass(), + desc_set_(desc_set), + shader_id_(shader_id), + validation_id_(validation_id), + opt_direct_reads_(opt_direct_reads) {} + + // Initialize state for instrumentation of module. + void InitializeInstrument(); + + // Call |pfn| on all instructions in all functions in the call tree of the + // entry points in |module|. If code is generated for an instruction, replace + // the instruction's block with the new blocks that are generated. Continue + // processing at the top of the last new block. + bool InstProcessEntryPointCallTree(InstProcessFunction& pfn); + + // Move all code in |ref_block_itr| preceding the instruction |ref_inst_itr| + // to be instrumented into block |new_blk_ptr|. + void MovePreludeCode(BasicBlock::iterator ref_inst_itr, + UptrVectorIterator ref_block_itr, + std::unique_ptr* new_blk_ptr); + + // Move all code in |ref_block_itr| succeeding the instruction |ref_inst_itr| + // to be instrumented into block |new_blk_ptr|. + void MovePostludeCode(UptrVectorIterator ref_block_itr, + BasicBlock* new_blk_ptr); + + // Generate instructions in |builder| which will atomically fetch and + // increment the size of the debug output buffer stream of the current + // validation and write a record to the end of the stream, if enough space + // in the buffer remains. The record will contain the index of the function + // and instruction within that function |func_idx, instruction_idx| which + // generated the record. It will also contain additional information to + // identify the instance of the shader, depending on the stage |stage_idx| + // of the shader. Finally, the record will contain validation-specific + // data contained in |validation_ids| which will identify the validation + // error as well as the values involved in the error. + // + // The output buffer binding written to by the code generated by the function + // is determined by the validation id specified when each specific + // instrumentation pass is created. + // + // The output buffer is a sequence of 32-bit values with the following + // format (where all elements are unsigned 32-bit unless otherwise noted): + // + // Size + // Record0 + // Record1 + // Record2 + // ... + // + // Size is the number of 32-bit values that have been written or + // attempted to be written to the output buffer, excluding the Size. It is + // initialized to 0. If the size of attempts to write the buffer exceeds + // the actual size of the buffer, it is possible that this field can exceed + // the actual size of the buffer. + // + // Each Record* is a variable-length sequence of 32-bit values with the + // following format defined using static const offsets in the .cpp file: + // + // Record Size + // Shader ID + // Instruction Index + // Stage + // Stage-specific Word 0 + // Stage-specific Word 1 + // ... + // Validation Error Code + // Validation-specific Word 0 + // Validation-specific Word 1 + // Validation-specific Word 2 + // ... + // + // Each record consists of three subsections: members common across all + // validation, members specific to the stage, and members specific to a + // validation. + // + // The Record Size is the number of 32-bit words in the record, including + // the Record Size word. + // + // Shader ID is a value that identifies which shader has generated the + // validation error. It is passed when the instrumentation pass is created. + // + // The Instruction Index is the position of the instruction within the + // SPIR-V file which is in error. + // + // The Stage is the pipeline stage which has generated the error as defined + // by the SpvExecutionModel_ enumeration. This is used to interpret the + // following Stage-specific words. + // + // The Stage-specific Words identify which invocation of the shader generated + // the error. Every stage will write a fixed number of words. Vertex shaders + // will write the Vertex and Instance ID. Fragment shaders will write + // FragCoord.xy. Compute shaders will write the GlobalInvocation ID. + // The tesselation eval shader will write the Primitive ID and TessCoords.uv. + // The tesselation control shader and geometry shader will write the + // Primitive ID and Invocation ID. + // + // The Validation Error Code specifies the exact error which has occurred. + // These are enumerated with the kInstError* static consts. This allows + // multiple validation layers to use the same, single output buffer. + // + // The Validation-specific Words are a validation-specific number of 32-bit + // words which give further information on the validation error that + // occurred. These are documented further in each file containing the + // validation-specific class which derives from this base class. + // + // Because the code that is generated checks against the size of the buffer + // before writing, the size of the debug out buffer can be used by the + // validation layer to control the number of error records that are written. + void GenDebugStreamWrite(uint32_t instruction_idx, uint32_t stage_idx, + const std::vector& validation_ids, + InstructionBuilder* builder); + + // Return true if all instructions in |ids| are constants or spec constants. + bool AllConstant(const std::vector& ids); + + // Generate in |builder| instructions to read the unsigned integer from the + // input buffer specified by the offsets in |offset_ids|. Given offsets + // o0, o1, ... oN, and input buffer ibuf, return the id for the value: + // + // ibuf[...ibuf[ibuf[o0]+o1]...+oN] + // + // The binding and the format of the input buffer is determined by each + // specific validation, which is specified at the creation of the pass. + uint32_t GenDebugDirectRead(const std::vector& offset_ids, + InstructionBuilder* builder); + + // Generate code to convert integer |value_id| to 32bit, if needed. Return + // an id to the 32bit equivalent. + uint32_t Gen32BitCvtCode(uint32_t value_id, InstructionBuilder* builder); + + // Generate code to cast integer |value_id| to 32bit unsigned, if needed. + // Return an id to the Uint equivalent. + uint32_t GenUintCastCode(uint32_t value_id, InstructionBuilder* builder); + + // Return new label. + std::unique_ptr NewLabel(uint32_t label_id); + + // Return id for 32-bit unsigned type + uint32_t GetUintId(); + + // Return id for 64-bit unsigned type + uint32_t GetUint64Id(); + + // Return id for 8-bit unsigned type + uint32_t GetUint8Id(); + + // Return id for 32-bit unsigned type + uint32_t GetBoolId(); + + // Return id for void type + uint32_t GetVoidId(); + + // Return pointer to type for runtime array of uint + analysis::Type* GetUintXRuntimeArrayType(uint32_t width, + analysis::Type** rarr_ty); + + // Return pointer to type for runtime array of uint + analysis::Type* GetUintRuntimeArrayType(uint32_t width); + + // Return id for buffer uint type + uint32_t GetOutputBufferPtrId(); + + // Return id for buffer uint type + uint32_t GetInputBufferTypeId(); + + // Return id for buffer uint type + uint32_t GetInputBufferPtrId(); + + // Return binding for output buffer for current validation. + uint32_t GetOutputBufferBinding(); + + // Return binding for input buffer for current validation. + uint32_t GetInputBufferBinding(); + + // Add storage buffer extension if needed + void AddStorageBufferExt(); + + // Return id for debug output buffer + uint32_t GetOutputBufferId(); + + // Return id for debug input buffer + uint32_t GetInputBufferId(); + + // Return id for 32-bit float type + uint32_t GetFloatId(); + + // Return id for v4float type + uint32_t GetVec4FloatId(); + + // Return id for uint vector type of |length| + uint32_t GetVecUintId(uint32_t length); + + // Return id for v4uint type + uint32_t GetVec4UintId(); + + // Return id for v3uint type + uint32_t GetVec3UintId(); + + // Return id for output function. Define if it doesn't exist with + // |val_spec_param_cnt| validation-specific uint32 parameters. + uint32_t GetStreamWriteFunctionId(uint32_t stage_idx, + uint32_t val_spec_param_cnt); + + // Return id for input function taking |param_cnt| uint32 parameters. Define + // if it doesn't exist. + uint32_t GetDirectReadFunctionId(uint32_t param_cnt); + + // Split block |block_itr| into two new blocks where the second block + // contains |inst_itr| and place in |new_blocks|. + void SplitBlock(BasicBlock::iterator inst_itr, + UptrVectorIterator block_itr, + std::vector>* new_blocks); + + // Apply instrumentation function |pfn| to every instruction in |func|. + // If code is generated for an instruction, replace the instruction's + // block with the new blocks that are generated. Continue processing at the + // top of the last new block. + bool InstrumentFunction(Function* func, uint32_t stage_idx, + InstProcessFunction& pfn); + + // Call |pfn| on all functions in the call tree of the function + // ids in |roots|. + bool InstProcessCallTreeFromRoots(InstProcessFunction& pfn, + std::queue* roots, + uint32_t stage_idx); + + // Gen code into |builder| to write |field_value_id| into debug output + // buffer at |base_offset_id| + |field_offset|. + void GenDebugOutputFieldCode(uint32_t base_offset_id, uint32_t field_offset, + uint32_t field_value_id, + InstructionBuilder* builder); + + // Generate instructions into |builder| which will write the members + // of the debug output record common for all stages and validations at + // |base_off|. + void GenCommonStreamWriteCode(uint32_t record_sz, uint32_t instruction_idx, + uint32_t stage_idx, uint32_t base_off, + InstructionBuilder* builder); + + // Generate instructions into |builder| which will write + // |uint_frag_coord_id| at |component| of the record at |base_offset_id| of + // the debug output buffer . + void GenFragCoordEltDebugOutputCode(uint32_t base_offset_id, + uint32_t uint_frag_coord_id, + uint32_t component, + InstructionBuilder* builder); + + // Generate instructions into |builder| which will load |var_id| and return + // its result id. + uint32_t GenVarLoad(uint32_t var_id, InstructionBuilder* builder); + + // Generate instructions into |builder| which will load the uint |builtin_id| + // and write it into the debug output buffer at |base_off| + |builtin_off|. + void GenBuiltinOutputCode(uint32_t builtin_id, uint32_t builtin_off, + uint32_t base_off, InstructionBuilder* builder); + + // Generate instructions into |builder| which will write the |stage_idx|- + // specific members of the debug output stream at |base_off|. + void GenStageStreamWriteCode(uint32_t stage_idx, uint32_t base_off, + InstructionBuilder* builder); + + // Return true if instruction must be in the same block that its result + // is used. + bool IsSameBlockOp(const Instruction* inst) const; + + // Clone operands which must be in same block as consumer instructions. + // Look in same_blk_pre for instructions that need cloning. Look in + // same_blk_post for instructions already cloned. Add cloned instruction + // to same_blk_post. + void CloneSameBlockOps( + std::unique_ptr* inst, + std::unordered_map* same_blk_post, + std::unordered_map* same_blk_pre, + BasicBlock* block_ptr); + + // Update phis in succeeding blocks to point to new last block + void UpdateSucceedingPhis( + std::vector>& new_blocks); + + // Debug descriptor set index + uint32_t desc_set_; + + // Shader module ID written into output record + uint32_t shader_id_; + + // Map from function id to function pointer. + std::unordered_map id2function_; + + // Map from block's label id to block. TODO(dnovillo): This is superfluous wrt + // CFG. It has functionality not present in CFG. Consolidate. + std::unordered_map id2block_; + + // Map from instruction's unique id to offset in original file. + std::unordered_map uid2offset_; + + // result id for OpConstantFalse + uint32_t validation_id_; + + // id for output buffer variable + uint32_t output_buffer_id_; + + // ptr type id for output buffer element + uint32_t output_buffer_ptr_id_; + + // ptr type id for input buffer element + uint32_t input_buffer_ptr_id_; + + // id for debug output function + std::unordered_map param2output_func_id_; + + // ids for debug input functions + std::unordered_map param2input_func_id_; + + // id for input buffer variable + uint32_t input_buffer_id_; + + // id for 32-bit float type + uint32_t float_id_; + + // id for v4float type + uint32_t v4float_id_; + + // id for v4uint type + uint32_t v4uint_id_; + + // id for v3uint type + uint32_t v3uint_id_; + + // id for 32-bit unsigned type + uint32_t uint_id_; + + // id for 64-bit unsigned type + uint32_t uint64_id_; + + // id for 8-bit unsigned type + uint32_t uint8_id_; + + // id for bool type + uint32_t bool_id_; + + // id for void type + uint32_t void_id_; + + // boolean to remember storage buffer extension + bool storage_buffer_ext_defined_; + + // runtime array of uint type + analysis::Type* uint64_rarr_ty_; + + // runtime array of uint type + analysis::Type* uint32_rarr_ty_; + + // Pre-instrumentation same-block insts + std::unordered_map same_block_pre_; + + // Post-instrumentation same-block op ids + std::unordered_map same_block_post_; + + // Map function calls to result id. Clear for every function. + // This is for debug input reads with constant arguments that + // have been generated into the first block of the function. + // This mechanism is used to avoid multiple identical debug + // input buffer reads. + struct vector_hash_ { + std::size_t operator()(const std::vector& v) const { + std::size_t hash = v.size(); + for (auto& u : v) { + hash ^= u + 0x9e3779b9 + (hash << 11) + (hash >> 21); + } + return hash; + } + }; + std::unordered_map, uint32_t, vector_hash_> call2id_; + + // Function currently being instrumented + Function* curr_func_; + + // Optimize direct debug input buffer reads. Specifically, move all such + // reads with constant args to first block and reuse them. + bool opt_direct_reads_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_INSTRUMENT_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/ir_builder.h b/third_party/spirv-tools/source/opt/ir_builder.h new file mode 100644 index 0000000..fe5feff --- /dev/null +++ b/third_party/spirv-tools/source/opt/ir_builder.h @@ -0,0 +1,628 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_IR_BUILDER_H_ +#define SOURCE_OPT_IR_BUILDER_H_ + +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/constants.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +// In SPIR-V, ids are encoded as uint16_t, this id is guaranteed to be always +// invalid. +const uint32_t kInvalidId = std::numeric_limits::max(); + +// Helper class to abstract instruction construction and insertion. +// The instruction builder can preserve the following analyses (specified via +// the constructors): +// - Def-use analysis +// - Instruction to block analysis +class InstructionBuilder { + public: + using InsertionPointTy = BasicBlock::iterator; + + // Creates an InstructionBuilder, all new instructions will be inserted before + // the instruction |insert_before|. + InstructionBuilder( + IRContext* context, Instruction* insert_before, + IRContext::Analysis preserved_analyses = IRContext::kAnalysisNone) + : InstructionBuilder(context, context->get_instr_block(insert_before), + InsertionPointTy(insert_before), + preserved_analyses) {} + + // Creates an InstructionBuilder, all new instructions will be inserted at the + // end of the basic block |parent_block|. + InstructionBuilder( + IRContext* context, BasicBlock* parent_block, + IRContext::Analysis preserved_analyses = IRContext::kAnalysisNone) + : InstructionBuilder(context, parent_block, parent_block->end(), + preserved_analyses) {} + + Instruction* AddNullaryOp(uint32_t type_id, SpvOp opcode) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr new_inst( + new Instruction(GetContext(), opcode, type_id, result_id, {})); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddUnaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newUnOp(new Instruction( + GetContext(), opcode, type_id, result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}})); + return AddInstruction(std::move(newUnOp)); + } + + Instruction* AddBinaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, + uint32_t operand2) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newBinOp(new Instruction( + GetContext(), opcode, type_id, opcode == SpvOpStore ? 0 : result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}})); + return AddInstruction(std::move(newBinOp)); + } + + Instruction* AddTernaryOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, + uint32_t operand2, uint32_t operand3) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newTernOp(new Instruction( + GetContext(), opcode, type_id, result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}})); + return AddInstruction(std::move(newTernOp)); + } + + Instruction* AddQuadOp(uint32_t type_id, SpvOp opcode, uint32_t operand1, + uint32_t operand2, uint32_t operand3, + uint32_t operand4) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newQuadOp(new Instruction( + GetContext(), opcode, type_id, result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand1}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand2}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand3}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {operand4}}})); + return AddInstruction(std::move(newQuadOp)); + } + + Instruction* AddIdLiteralOp(uint32_t type_id, SpvOp opcode, uint32_t id, + uint32_t uliteral) { + uint32_t result_id = 0; + if (type_id != 0) { + result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + } + std::unique_ptr newBinOp(new Instruction( + GetContext(), opcode, type_id, result_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {uliteral}}})); + return AddInstruction(std::move(newBinOp)); + } + + // Creates an N-ary instruction of |opcode|. + // |typid| must be the id of the instruction's type. + // |operands| must be a sequence of operand ids. + // Use |result| for the result id if non-zero. + Instruction* AddNaryOp(uint32_t type_id, SpvOp opcode, + const std::vector& operands, + uint32_t result = 0) { + std::vector ops; + for (size_t i = 0; i < operands.size(); i++) { + ops.push_back({SPV_OPERAND_TYPE_ID, {operands[i]}}); + } + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst(new Instruction( + GetContext(), opcode, type_id, + result != 0 ? result : GetContext()->TakeNextId(), ops)); + return AddInstruction(std::move(new_inst)); + } + + // Creates a new selection merge instruction. + // The id |merge_id| is the merge basic block id. + Instruction* AddSelectionMerge( + uint32_t merge_id, + uint32_t selection_control = SpvSelectionControlMaskNone) { + std::unique_ptr new_branch_merge(new Instruction( + GetContext(), SpvOpSelectionMerge, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_SELECTION_CONTROL, + {selection_control}}})); + return AddInstruction(std::move(new_branch_merge)); + } + + // Creates a new loop merge instruction. + // The id |merge_id| is the basic block id of the merge block. + // |continue_id| is the id of the continue block. + // |loop_control| are the loop control flags to be added to the instruction. + Instruction* AddLoopMerge(uint32_t merge_id, uint32_t continue_id, + uint32_t loop_control = SpvLoopControlMaskNone) { + std::unique_ptr new_branch_merge(new Instruction( + GetContext(), SpvOpLoopMerge, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {merge_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {continue_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_LOOP_CONTROL, {loop_control}}})); + return AddInstruction(std::move(new_branch_merge)); + } + + // Creates a new branch instruction to |label_id|. + // Note that the user must make sure the final basic block is + // well formed. + Instruction* AddBranch(uint32_t label_id) { + std::unique_ptr new_branch(new Instruction( + GetContext(), SpvOpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {label_id}}})); + return AddInstruction(std::move(new_branch)); + } + + // Creates a new conditional instruction and the associated selection merge + // instruction if requested. + // The id |cond_id| is the id of the condition instruction, must be of + // type bool. + // The id |true_id| is the id of the basic block to branch to if the condition + // is true. + // The id |false_id| is the id of the basic block to branch to if the + // condition is false. + // The id |merge_id| is the id of the merge basic block for the selection + // merge instruction. If |merge_id| equals kInvalidId then no selection merge + // instruction will be created. + // The value |selection_control| is the selection control flag for the + // selection merge instruction. + // Note that the user must make sure the final basic block is + // well formed. + Instruction* AddConditionalBranch( + uint32_t cond_id, uint32_t true_id, uint32_t false_id, + uint32_t merge_id = kInvalidId, + uint32_t selection_control = SpvSelectionControlMaskNone) { + if (merge_id != kInvalidId) { + AddSelectionMerge(merge_id, selection_control); + } + std::unique_ptr new_branch(new Instruction( + GetContext(), SpvOpBranchConditional, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {cond_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {true_id}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {false_id}}})); + return AddInstruction(std::move(new_branch)); + } + + // Creates a new switch instruction and the associated selection merge + // instruction if requested. + // The id |selector_id| is the id of the selector instruction, must be of + // type int. + // The id |default_id| is the id of the default basic block to branch to. + // The vector |targets| is the pair of literal/branch id. + // The id |merge_id| is the id of the merge basic block for the selection + // merge instruction. If |merge_id| equals kInvalidId then no selection merge + // instruction will be created. + // The value |selection_control| is the selection control flag for the + // selection merge instruction. + // Note that the user must make sure the final basic block is + // well formed. + Instruction* AddSwitch( + uint32_t selector_id, uint32_t default_id, + const std::vector>& targets, + uint32_t merge_id = kInvalidId, + uint32_t selection_control = SpvSelectionControlMaskNone) { + if (merge_id != kInvalidId) { + AddSelectionMerge(merge_id, selection_control); + } + std::vector operands; + operands.emplace_back( + Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {selector_id}}); + operands.emplace_back( + Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {default_id}}); + for (auto& target : targets) { + operands.emplace_back( + Operand{spv_operand_type_t::SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER, + target.first}); + operands.emplace_back( + Operand{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {target.second}}); + } + std::unique_ptr new_switch( + new Instruction(GetContext(), SpvOpSwitch, 0, 0, operands)); + return AddInstruction(std::move(new_switch)); + } + + // Creates a phi instruction. + // The id |type| must be the id of the phi instruction's type. + // The vector |incomings| must be a sequence of pairs of . + Instruction* AddPhi(uint32_t type, const std::vector& incomings, + uint32_t result = 0) { + assert(incomings.size() % 2 == 0 && "A sequence of pairs is expected"); + return AddNaryOp(type, SpvOpPhi, incomings, result); + } + + // Creates an addition instruction. + // The id |type| must be the id of the instruction's type, must be the same as + // |op1| and |op2| types. + // The id |op1| is the left hand side of the operation. + // The id |op2| is the right hand side of the operation. + Instruction* AddIAdd(uint32_t type, uint32_t op1, uint32_t op2) { + // TODO(1841): Handle id overflow. + std::unique_ptr inst(new Instruction( + GetContext(), SpvOpIAdd, type, GetContext()->TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); + return AddInstruction(std::move(inst)); + } + + // Creates a less than instruction for unsigned integer. + // The id |op1| is the left hand side of the operation. + // The id |op2| is the right hand side of the operation. + // It is assumed that |op1| and |op2| have the same underlying type. + Instruction* AddULessThan(uint32_t op1, uint32_t op2) { + analysis::Bool bool_type; + uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); + // TODO(1841): Handle id overflow. + std::unique_ptr inst(new Instruction( + GetContext(), SpvOpULessThan, type, GetContext()->TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); + return AddInstruction(std::move(inst)); + } + + // Creates a less than instruction for signed integer. + // The id |op1| is the left hand side of the operation. + // The id |op2| is the right hand side of the operation. + // It is assumed that |op1| and |op2| have the same underlying type. + Instruction* AddSLessThan(uint32_t op1, uint32_t op2) { + analysis::Bool bool_type; + uint32_t type = GetContext()->get_type_mgr()->GetId(&bool_type); + // TODO(1841): Handle id overflow. + std::unique_ptr inst(new Instruction( + GetContext(), SpvOpSLessThan, type, GetContext()->TakeNextId(), + {{SPV_OPERAND_TYPE_ID, {op1}}, {SPV_OPERAND_TYPE_ID, {op2}}})); + return AddInstruction(std::move(inst)); + } + + // Creates an OpILessThan or OpULessThen instruction depending on the sign of + // |op1|. The id |op1| is the left hand side of the operation. The id |op2| is + // the right hand side of the operation. It is assumed that |op1| and |op2| + // have the same underlying type. + Instruction* AddLessThan(uint32_t op1, uint32_t op2) { + Instruction* op1_insn = context_->get_def_use_mgr()->GetDef(op1); + analysis::Type* type = + GetContext()->get_type_mgr()->GetType(op1_insn->type_id()); + analysis::Integer* int_type = type->AsInteger(); + assert(int_type && "Operand is not of int type"); + + if (int_type->IsSigned()) + return AddSLessThan(op1, op2); + else + return AddULessThan(op1, op2); + } + + // Creates a select instruction. + // |type| must match the types of |true_value| and |false_value|. It is up to + // the caller to ensure that |cond| is a correct type (bool or vector of + // bool) for |type|. + Instruction* AddSelect(uint32_t type, uint32_t cond, uint32_t true_value, + uint32_t false_value) { + // TODO(1841): Handle id overflow. + std::unique_ptr select(new Instruction( + GetContext(), SpvOpSelect, type, GetContext()->TakeNextId(), + std::initializer_list{{SPV_OPERAND_TYPE_ID, {cond}}, + {SPV_OPERAND_TYPE_ID, {true_value}}, + {SPV_OPERAND_TYPE_ID, {false_value}}})); + return AddInstruction(std::move(select)); + } + + // Adds a signed int32 constant to the binary. + // The |value| parameter is the constant value to be added. + Instruction* GetSintConstant(int32_t value) { + return GetIntConstant(value, true); + } + + // Create a composite construct. + // |type| should be a composite type and the number of elements it has should + // match the size od |ids|. + Instruction* AddCompositeConstruct(uint32_t type, + const std::vector& ids) { + std::vector ops; + for (auto id : ids) { + ops.emplace_back(SPV_OPERAND_TYPE_ID, + std::initializer_list{id}); + } + // TODO(1841): Handle id overflow. + std::unique_ptr construct( + new Instruction(GetContext(), SpvOpCompositeConstruct, type, + GetContext()->TakeNextId(), ops)); + return AddInstruction(std::move(construct)); + } + // Adds an unsigned int32 constant to the binary. + // The |value| parameter is the constant value to be added. + Instruction* GetUintConstant(uint32_t value) { + return GetIntConstant(value, false); + } + + uint32_t GetUintConstantId(uint32_t value) { + Instruction* uint_inst = GetUintConstant(value); + return uint_inst->result_id(); + } + + // Adds either a signed or unsigned 32 bit integer constant to the binary + // depedning on the |sign|. If |sign| is true then the value is added as a + // signed constant otherwise as an unsigned constant. If |sign| is false the + // value must not be a negative number. + template + Instruction* GetIntConstant(T value, bool sign) { + // Assert that we are not trying to store a negative number in an unsigned + // type. + if (!sign) + assert(value >= 0 && + "Trying to add a signed integer with an unsigned type!"); + + analysis::Integer int_type{32, sign}; + + // Get or create the integer type. This rebuilds the type and manages the + // memory for the rebuilt type. + uint32_t type_id = + GetContext()->get_type_mgr()->GetTypeInstruction(&int_type); + + // Get the memory managed type so that it is safe to be stored by + // GetConstant. + analysis::Type* rebuilt_type = + GetContext()->get_type_mgr()->GetType(type_id); + + // Even if the value is negative we need to pass the bit pattern as a + // uint32_t to GetConstant. + uint32_t word = value; + + // Create the constant value. + const analysis::Constant* constant = + GetContext()->get_constant_mgr()->GetConstant(rebuilt_type, {word}); + + // Create the OpConstant instruction using the type and the value. + return GetContext()->get_constant_mgr()->GetDefiningInstruction(constant); + } + + Instruction* AddCompositeExtract(uint32_t type, uint32_t id_of_composite, + const std::vector& index_list) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {id_of_composite}}); + + for (uint32_t index : index_list) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}); + } + + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst( + new Instruction(GetContext(), SpvOpCompositeExtract, type, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + + // Creates an unreachable instruction. + Instruction* AddUnreachable() { + std::unique_ptr select( + new Instruction(GetContext(), SpvOpUnreachable, 0, 0, + std::initializer_list{})); + return AddInstruction(std::move(select)); + } + + Instruction* AddAccessChain(uint32_t type_id, uint32_t base_ptr_id, + std::vector ids) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); + + for (uint32_t index_id : ids) { + operands.push_back({SPV_OPERAND_TYPE_ID, {index_id}}); + } + + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst( + new Instruction(GetContext(), SpvOpAccessChain, type_id, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddLoad(uint32_t type_id, uint32_t base_ptr_id) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {base_ptr_id}}); + + // TODO(1841): Handle id overflow. + std::unique_ptr new_inst( + new Instruction(GetContext(), SpvOpLoad, type_id, + GetContext()->TakeNextId(), operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddStore(uint32_t ptr_id, uint32_t obj_id) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {ptr_id}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {obj_id}}); + + std::unique_ptr new_inst( + new Instruction(GetContext(), SpvOpStore, 0, 0, operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddFunctionCall(uint32_t result_type, uint32_t function, + const std::vector& parameters) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {function}}); + for (uint32_t id : parameters) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + std::unique_ptr new_inst(new Instruction( + GetContext(), SpvOpFunctionCall, result_type, result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddVectorShuffle(uint32_t result_type, uint32_t vec1, + uint32_t vec2, + const std::vector& components) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {vec1}}); + operands.push_back({SPV_OPERAND_TYPE_ID, {vec2}}); + for (uint32_t id : components) { + operands.push_back({SPV_OPERAND_TYPE_LITERAL_INTEGER, {id}}); + } + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + + std::unique_ptr new_inst(new Instruction( + GetContext(), SpvOpVectorShuffle, result_type, result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + + Instruction* AddNaryExtendedInstruction( + uint32_t result_type, uint32_t set, uint32_t instruction, + const std::vector& ext_operands) { + std::vector operands; + operands.push_back({SPV_OPERAND_TYPE_ID, {set}}); + operands.push_back( + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, {instruction}}); + for (uint32_t id : ext_operands) { + operands.push_back({SPV_OPERAND_TYPE_ID, {id}}); + } + + uint32_t result_id = GetContext()->TakeNextId(); + if (result_id == 0) { + return nullptr; + } + + std::unique_ptr new_inst(new Instruction( + GetContext(), SpvOpExtInst, result_type, result_id, operands)); + return AddInstruction(std::move(new_inst)); + } + + // Inserts the new instruction before the insertion point. + Instruction* AddInstruction(std::unique_ptr&& insn) { + Instruction* insn_ptr = &*insert_before_.InsertBefore(std::move(insn)); + UpdateInstrToBlockMapping(insn_ptr); + UpdateDefUseMgr(insn_ptr); + return insn_ptr; + } + + // Returns the insertion point iterator. + InsertionPointTy GetInsertPoint() { return insert_before_; } + + // Change the insertion point to insert before the instruction + // |insert_before|. + void SetInsertPoint(Instruction* insert_before) { + parent_ = context_->get_instr_block(insert_before); + insert_before_ = InsertionPointTy(insert_before); + } + + // Change the insertion point to insert at the end of the basic block + // |parent_block|. + void SetInsertPoint(BasicBlock* parent_block) { + parent_ = parent_block; + insert_before_ = parent_block->end(); + } + + // Returns the context which instructions are constructed for. + IRContext* GetContext() const { return context_; } + + // Returns the set of preserved analyses. + inline IRContext::Analysis GetPreservedAnalysis() const { + return preserved_analyses_; + } + + private: + InstructionBuilder(IRContext* context, BasicBlock* parent, + InsertionPointTy insert_before, + IRContext::Analysis preserved_analyses) + : context_(context), + parent_(parent), + insert_before_(insert_before), + preserved_analyses_(preserved_analyses) { + assert(!(preserved_analyses_ & ~(IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping))); + } + + // Returns true if the users requested to update |analysis|. + inline bool IsAnalysisUpdateRequested(IRContext::Analysis analysis) const { + if (!GetContext()->AreAnalysesValid(analysis)) { + // Do not try to update something that is not built. + return false; + } + return preserved_analyses_ & analysis; + } + + // Updates the def/use manager if the user requested it. If an update was not + // requested, this function does nothing. + inline void UpdateDefUseMgr(Instruction* insn) { + if (IsAnalysisUpdateRequested(IRContext::kAnalysisDefUse)) + GetContext()->get_def_use_mgr()->AnalyzeInstDefUse(insn); + } + + // Updates the instruction to block analysis if the user requested it. If + // an update was not requested, this function does nothing. + inline void UpdateInstrToBlockMapping(Instruction* insn) { + if (IsAnalysisUpdateRequested(IRContext::kAnalysisInstrToBlockMapping) && + parent_) + GetContext()->set_instr_block(insn, parent_); + } + + IRContext* context_; + BasicBlock* parent_; + InsertionPointTy insert_before_; + const IRContext::Analysis preserved_analyses_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_IR_BUILDER_H_ diff --git a/third_party/spirv-tools/source/opt/ir_context.cpp b/third_party/spirv-tools/source/opt/ir_context.cpp new file mode 100644 index 0000000..3e610d7 --- /dev/null +++ b/third_party/spirv-tools/source/opt/ir_context.cpp @@ -0,0 +1,1042 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/ir_context.h" + +#include + +#include "OpenCLDebugInfo100.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/opt/log.h" +#include "source/opt/mem_pass.h" +#include "source/opt/reflect.h" + +namespace { + +static const int kSpvDecorateTargetIdInIdx = 0; +static const int kSpvDecorateDecorationInIdx = 1; +static const int kSpvDecorateBuiltinInIdx = 2; +static const int kEntryPointInterfaceInIdx = 3; +static const int kEntryPointFunctionIdInIdx = 1; + +// Constants for OpenCL.DebugInfo.100 extension instructions. +static const uint32_t kDebugFunctionOperandFunctionIndex = 13; +static const uint32_t kDebugGlobalVariableOperandVariableIndex = 11; + +} // anonymous namespace + +namespace spvtools { +namespace opt { + +void IRContext::BuildInvalidAnalyses(IRContext::Analysis set) { + if (set & kAnalysisDefUse) { + BuildDefUseManager(); + } + if (set & kAnalysisInstrToBlockMapping) { + BuildInstrToBlockMapping(); + } + if (set & kAnalysisDecorations) { + BuildDecorationManager(); + } + if (set & kAnalysisCFG) { + BuildCFG(); + } + if (set & kAnalysisDominatorAnalysis) { + ResetDominatorAnalysis(); + } + if (set & kAnalysisLoopAnalysis) { + ResetLoopAnalysis(); + } + if (set & kAnalysisBuiltinVarId) { + ResetBuiltinAnalysis(); + } + if (set & kAnalysisNameMap) { + BuildIdToNameMap(); + } + if (set & kAnalysisScalarEvolution) { + BuildScalarEvolutionAnalysis(); + } + if (set & kAnalysisRegisterPressure) { + BuildRegPressureAnalysis(); + } + if (set & kAnalysisValueNumberTable) { + BuildValueNumberTable(); + } + if (set & kAnalysisStructuredCFG) { + BuildStructuredCFGAnalysis(); + } + if (set & kAnalysisIdToFuncMapping) { + BuildIdToFuncMapping(); + } + if (set & kAnalysisConstants) { + BuildConstantManager(); + } + if (set & kAnalysisTypes) { + BuildTypeManager(); + } + if (set & kAnalysisDebugInfo) { + BuildDebugInfoManager(); + } +} + +void IRContext::InvalidateAnalysesExceptFor( + IRContext::Analysis preserved_analyses) { + uint32_t analyses_to_invalidate = valid_analyses_ & (~preserved_analyses); + InvalidateAnalyses(static_cast(analyses_to_invalidate)); +} + +void IRContext::InvalidateAnalyses(IRContext::Analysis analyses_to_invalidate) { + // The ConstantManager and DebugInfoManager contain Type pointers. If the + // TypeManager goes away, the ConstantManager and DebugInfoManager have to + // go away. + if (analyses_to_invalidate & kAnalysisTypes) { + analyses_to_invalidate |= kAnalysisConstants; + analyses_to_invalidate |= kAnalysisDebugInfo; + } + + // The dominator analysis hold the psuedo entry and exit nodes from the CFG. + // Also if the CFG change the dominators many changed as well, so the + // dominator analysis should be invalidated as well. + if (analyses_to_invalidate & kAnalysisCFG) { + analyses_to_invalidate |= kAnalysisDominatorAnalysis; + } + + if (analyses_to_invalidate & kAnalysisDefUse) { + def_use_mgr_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisInstrToBlockMapping) { + instr_to_block_.clear(); + } + if (analyses_to_invalidate & kAnalysisDecorations) { + decoration_mgr_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisCombinators) { + combinator_ops_.clear(); + } + if (analyses_to_invalidate & kAnalysisBuiltinVarId) { + builtin_var_id_map_.clear(); + } + if (analyses_to_invalidate & kAnalysisCFG) { + cfg_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisDominatorAnalysis) { + dominator_trees_.clear(); + post_dominator_trees_.clear(); + } + if (analyses_to_invalidate & kAnalysisNameMap) { + id_to_name_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisValueNumberTable) { + vn_table_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisStructuredCFG) { + struct_cfg_analysis_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisIdToFuncMapping) { + id_to_func_.clear(); + } + if (analyses_to_invalidate & kAnalysisConstants) { + constant_mgr_.reset(nullptr); + } + if (analyses_to_invalidate & kAnalysisTypes) { + type_mgr_.reset(nullptr); + } + + if (analyses_to_invalidate & kAnalysisDebugInfo) { + debug_info_mgr_.reset(nullptr); + } + + valid_analyses_ = Analysis(valid_analyses_ & ~analyses_to_invalidate); +} + +Instruction* IRContext::KillInst(Instruction* inst) { + if (!inst) { + return nullptr; + } + + KillNamesAndDecorates(inst); + + KillOperandFromDebugInstructions(inst); + + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->ClearInst(inst); + } + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { + instr_to_block_.erase(inst); + } + if (AreAnalysesValid(kAnalysisDecorations)) { + if (inst->IsDecoration()) { + decoration_mgr_->RemoveDecoration(inst); + } + } + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ClearDebugScopeAndInlinedAtUses(inst); + get_debug_info_mgr()->ClearDebugInfo(inst); + } + if (type_mgr_ && IsTypeInst(inst->opcode())) { + type_mgr_->RemoveId(inst->result_id()); + } + if (constant_mgr_ && IsConstantInst(inst->opcode())) { + constant_mgr_->RemoveId(inst->result_id()); + } + if (inst->opcode() == SpvOpCapability || inst->opcode() == SpvOpExtension) { + // We reset the feature manager, instead of updating it, because it is just + // as much work. We would have to remove all capabilities implied by this + // capability that are not also implied by the remaining OpCapability + // instructions. We could update extensions, but we will see if it is + // needed. + ResetFeatureManager(); + } + + RemoveFromIdToName(inst); + + Instruction* next_instruction = nullptr; + if (inst->IsInAList()) { + next_instruction = inst->NextNode(); + inst->RemoveFromList(); + delete inst; + } else { + // Needed for instructions that are not part of a list like OpLabels, + // OpFunction, OpFunctionEnd, etc.. + inst->ToNop(); + } + return next_instruction; +} + +void IRContext::KillNonSemanticInfo(Instruction* inst) { + if (!inst->HasResultId()) return; + std::vector work_list; + std::vector to_kill; + std::unordered_set seen; + work_list.push_back(inst); + + while (!work_list.empty()) { + auto* i = work_list.back(); + work_list.pop_back(); + get_def_use_mgr()->ForEachUser( + i, [&work_list, &to_kill, &seen](Instruction* user) { + if (user->IsNonSemanticInstruction() && seen.insert(user).second) { + work_list.push_back(user); + to_kill.push_back(user); + } + }); + } + + for (auto* dead : to_kill) { + KillInst(dead); + } +} + +bool IRContext::KillDef(uint32_t id) { + Instruction* def = get_def_use_mgr()->GetDef(id); + if (def != nullptr) { + KillInst(def); + return true; + } + return false; +} + +bool IRContext::ReplaceAllUsesWith(uint32_t before, uint32_t after) { + return ReplaceAllUsesWithPredicate(before, after, + [](Instruction*) { return true; }); +} + +bool IRContext::ReplaceAllUsesWithPredicate( + uint32_t before, uint32_t after, + const std::function& predicate) { + if (before == after) return false; + + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ReplaceAllUsesInDebugScopeWithPredicate(before, after, + predicate); + } + + // Ensure that |after| has been registered as def. + assert(get_def_use_mgr()->GetDef(after) && + "'after' is not a registered def."); + + std::vector> uses_to_update; + get_def_use_mgr()->ForEachUse( + before, [&predicate, &uses_to_update](Instruction* user, uint32_t index) { + if (predicate(user)) { + uses_to_update.emplace_back(user, index); + } + }); + + Instruction* prev = nullptr; + for (auto p : uses_to_update) { + Instruction* user = p.first; + uint32_t index = p.second; + if (prev == nullptr || prev != user) { + ForgetUses(user); + prev = user; + } + const uint32_t type_result_id_count = + (user->result_id() != 0) + (user->type_id() != 0); + + if (index < type_result_id_count) { + // Update the type_id. Note that result id is immutable so it should + // never be updated. + if (user->type_id() != 0 && index == 0) { + user->SetResultType(after); + } else if (user->type_id() == 0) { + SPIRV_ASSERT(consumer_, false, + "Result type id considered as use while the instruction " + "doesn't have a result type id."); + (void)consumer_; // Makes the compiler happy for release build. + } else { + SPIRV_ASSERT(consumer_, false, + "Trying setting the immutable result id."); + } + } else { + // Update an in-operand. + uint32_t in_operand_pos = index - type_result_id_count; + // Make the modification in the instruction. + user->SetInOperand(in_operand_pos, {after}); + } + AnalyzeUses(user); + } + return true; +} + +bool IRContext::IsConsistent() { +#ifndef SPIRV_CHECK_CONTEXT + return true; +#else + if (AreAnalysesValid(kAnalysisDefUse)) { + analysis::DefUseManager new_def_use(module()); + if (*get_def_use_mgr() != new_def_use) { + return false; + } + } + + if (AreAnalysesValid(kAnalysisIdToFuncMapping)) { + for (auto& fn : *module_) { + if (id_to_func_[fn.result_id()] != &fn) { + return false; + } + } + } + + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { + for (auto& func : *module()) { + for (auto& block : func) { + if (!block.WhileEachInst([this, &block](Instruction* inst) { + if (get_instr_block(inst) != &block) { + return false; + } + return true; + })) + return false; + } + } + } + + if (!CheckCFG()) { + return false; + } + + if (AreAnalysesValid(kAnalysisDecorations)) { + analysis::DecorationManager* dec_mgr = get_decoration_mgr(); + analysis::DecorationManager current(module()); + + if (*dec_mgr != current) { + return false; + } + } + + if (feature_mgr_ != nullptr) { + FeatureManager current(grammar_); + current.Analyze(module()); + + if (current != *feature_mgr_) { + return false; + } + } + return true; +#endif +} + +void IRContext::ForgetUses(Instruction* inst) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->EraseUseRecordsOfOperandIds(inst); + } + if (AreAnalysesValid(kAnalysisDecorations)) { + if (inst->IsDecoration()) { + get_decoration_mgr()->RemoveDecoration(inst); + } + } + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->ClearDebugInfo(inst); + } + RemoveFromIdToName(inst); +} + +void IRContext::AnalyzeUses(Instruction* inst) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstUse(inst); + } + if (AreAnalysesValid(kAnalysisDecorations)) { + if (inst->IsDecoration()) { + get_decoration_mgr()->AddDecoration(inst); + } + } + if (AreAnalysesValid(kAnalysisDebugInfo)) { + get_debug_info_mgr()->AnalyzeDebugInst(inst); + } + if (id_to_name_ && + (inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) { + id_to_name_->insert({inst->GetSingleWordInOperand(0), inst}); + } +} + +void IRContext::KillNamesAndDecorates(uint32_t id) { + analysis::DecorationManager* dec_mgr = get_decoration_mgr(); + dec_mgr->RemoveDecorationsFrom(id); + + std::vector name_to_kill; + for (auto name : GetNames(id)) { + name_to_kill.push_back(name.second); + } + for (Instruction* name_inst : name_to_kill) { + KillInst(name_inst); + } +} + +void IRContext::KillNamesAndDecorates(Instruction* inst) { + const uint32_t rId = inst->result_id(); + if (rId == 0) return; + KillNamesAndDecorates(rId); +} + +void IRContext::KillOperandFromDebugInstructions(Instruction* inst) { + const auto opcode = inst->opcode(); + const uint32_t id = inst->result_id(); + // Kill id of OpFunction from DebugFunction. + if (opcode == SpvOpFunction) { + for (auto it = module()->ext_inst_debuginfo_begin(); + it != module()->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100DebugFunction) + continue; + auto& operand = it->GetOperand(kDebugFunctionOperandFunctionIndex); + if (operand.words[0] == id) { + operand.words[0] = + get_debug_info_mgr()->GetDebugInfoNone()->result_id(); + get_def_use_mgr()->AnalyzeInstUse(&*it); + } + } + } + // Kill id of OpVariable for global variable from DebugGlobalVariable. + if (opcode == SpvOpVariable || IsConstantInst(opcode)) { + for (auto it = module()->ext_inst_debuginfo_begin(); + it != module()->ext_inst_debuginfo_end(); ++it) { + if (it->GetOpenCL100DebugOpcode() != + OpenCLDebugInfo100DebugGlobalVariable) + continue; + auto& operand = it->GetOperand(kDebugGlobalVariableOperandVariableIndex); + if (operand.words[0] == id) { + operand.words[0] = + get_debug_info_mgr()->GetDebugInfoNone()->result_id(); + get_def_use_mgr()->AnalyzeInstUse(&*it); + } + } + } +} + +void IRContext::AddCombinatorsForCapability(uint32_t capability) { + if (capability == SpvCapabilityShader) { + combinator_ops_[0].insert({SpvOpNop, + SpvOpUndef, + SpvOpConstant, + SpvOpConstantTrue, + SpvOpConstantFalse, + SpvOpConstantComposite, + SpvOpConstantSampler, + SpvOpConstantNull, + SpvOpTypeVoid, + SpvOpTypeBool, + SpvOpTypeInt, + SpvOpTypeFloat, + SpvOpTypeVector, + SpvOpTypeMatrix, + SpvOpTypeImage, + SpvOpTypeSampler, + SpvOpTypeSampledImage, + SpvOpTypeAccelerationStructureNV, + SpvOpTypeAccelerationStructureKHR, + SpvOpTypeRayQueryProvisionalKHR, + SpvOpTypeArray, + SpvOpTypeRuntimeArray, + SpvOpTypeStruct, + SpvOpTypeOpaque, + SpvOpTypePointer, + SpvOpTypeFunction, + SpvOpTypeEvent, + SpvOpTypeDeviceEvent, + SpvOpTypeReserveId, + SpvOpTypeQueue, + SpvOpTypePipe, + SpvOpTypeForwardPointer, + SpvOpVariable, + SpvOpImageTexelPointer, + SpvOpLoad, + SpvOpAccessChain, + SpvOpInBoundsAccessChain, + SpvOpArrayLength, + SpvOpVectorExtractDynamic, + SpvOpVectorInsertDynamic, + SpvOpVectorShuffle, + SpvOpCompositeConstruct, + SpvOpCompositeExtract, + SpvOpCompositeInsert, + SpvOpCopyObject, + SpvOpTranspose, + SpvOpSampledImage, + SpvOpImageSampleImplicitLod, + SpvOpImageSampleExplicitLod, + SpvOpImageSampleDrefImplicitLod, + SpvOpImageSampleDrefExplicitLod, + SpvOpImageSampleProjImplicitLod, + SpvOpImageSampleProjExplicitLod, + SpvOpImageSampleProjDrefImplicitLod, + SpvOpImageSampleProjDrefExplicitLod, + SpvOpImageFetch, + SpvOpImageGather, + SpvOpImageDrefGather, + SpvOpImageRead, + SpvOpImage, + SpvOpImageQueryFormat, + SpvOpImageQueryOrder, + SpvOpImageQuerySizeLod, + SpvOpImageQuerySize, + SpvOpImageQueryLevels, + SpvOpImageQuerySamples, + SpvOpConvertFToU, + SpvOpConvertFToS, + SpvOpConvertSToF, + SpvOpConvertUToF, + SpvOpUConvert, + SpvOpSConvert, + SpvOpFConvert, + SpvOpQuantizeToF16, + SpvOpBitcast, + SpvOpSNegate, + SpvOpFNegate, + SpvOpIAdd, + SpvOpFAdd, + SpvOpISub, + SpvOpFSub, + SpvOpIMul, + SpvOpFMul, + SpvOpUDiv, + SpvOpSDiv, + SpvOpFDiv, + SpvOpUMod, + SpvOpSRem, + SpvOpSMod, + SpvOpFRem, + SpvOpFMod, + SpvOpVectorTimesScalar, + SpvOpMatrixTimesScalar, + SpvOpVectorTimesMatrix, + SpvOpMatrixTimesVector, + SpvOpMatrixTimesMatrix, + SpvOpOuterProduct, + SpvOpDot, + SpvOpIAddCarry, + SpvOpISubBorrow, + SpvOpUMulExtended, + SpvOpSMulExtended, + SpvOpAny, + SpvOpAll, + SpvOpIsNan, + SpvOpIsInf, + SpvOpLogicalEqual, + SpvOpLogicalNotEqual, + SpvOpLogicalOr, + SpvOpLogicalAnd, + SpvOpLogicalNot, + SpvOpSelect, + SpvOpIEqual, + SpvOpINotEqual, + SpvOpUGreaterThan, + SpvOpSGreaterThan, + SpvOpUGreaterThanEqual, + SpvOpSGreaterThanEqual, + SpvOpULessThan, + SpvOpSLessThan, + SpvOpULessThanEqual, + SpvOpSLessThanEqual, + SpvOpFOrdEqual, + SpvOpFUnordEqual, + SpvOpFOrdNotEqual, + SpvOpFUnordNotEqual, + SpvOpFOrdLessThan, + SpvOpFUnordLessThan, + SpvOpFOrdGreaterThan, + SpvOpFUnordGreaterThan, + SpvOpFOrdLessThanEqual, + SpvOpFUnordLessThanEqual, + SpvOpFOrdGreaterThanEqual, + SpvOpFUnordGreaterThanEqual, + SpvOpShiftRightLogical, + SpvOpShiftRightArithmetic, + SpvOpShiftLeftLogical, + SpvOpBitwiseOr, + SpvOpBitwiseXor, + SpvOpBitwiseAnd, + SpvOpNot, + SpvOpBitFieldInsert, + SpvOpBitFieldSExtract, + SpvOpBitFieldUExtract, + SpvOpBitReverse, + SpvOpBitCount, + SpvOpPhi, + SpvOpImageSparseSampleImplicitLod, + SpvOpImageSparseSampleExplicitLod, + SpvOpImageSparseSampleDrefImplicitLod, + SpvOpImageSparseSampleDrefExplicitLod, + SpvOpImageSparseSampleProjImplicitLod, + SpvOpImageSparseSampleProjExplicitLod, + SpvOpImageSparseSampleProjDrefImplicitLod, + SpvOpImageSparseSampleProjDrefExplicitLod, + SpvOpImageSparseFetch, + SpvOpImageSparseGather, + SpvOpImageSparseDrefGather, + SpvOpImageSparseTexelsResident, + SpvOpImageSparseRead, + SpvOpSizeOf}); + } +} + +void IRContext::AddCombinatorsForExtension(Instruction* extension) { + assert(extension->opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const char* extension_name = + reinterpret_cast(&extension->GetInOperand(0).words[0]); + if (!strcmp(extension_name, "GLSL.std.450")) { + combinator_ops_[extension->result_id()] = {GLSLstd450Round, + GLSLstd450RoundEven, + GLSLstd450Trunc, + GLSLstd450FAbs, + GLSLstd450SAbs, + GLSLstd450FSign, + GLSLstd450SSign, + GLSLstd450Floor, + GLSLstd450Ceil, + GLSLstd450Fract, + GLSLstd450Radians, + GLSLstd450Degrees, + GLSLstd450Sin, + GLSLstd450Cos, + GLSLstd450Tan, + GLSLstd450Asin, + GLSLstd450Acos, + GLSLstd450Atan, + GLSLstd450Sinh, + GLSLstd450Cosh, + GLSLstd450Tanh, + GLSLstd450Asinh, + GLSLstd450Acosh, + GLSLstd450Atanh, + GLSLstd450Atan2, + GLSLstd450Pow, + GLSLstd450Exp, + GLSLstd450Log, + GLSLstd450Exp2, + GLSLstd450Log2, + GLSLstd450Sqrt, + GLSLstd450InverseSqrt, + GLSLstd450Determinant, + GLSLstd450MatrixInverse, + GLSLstd450ModfStruct, + GLSLstd450FMin, + GLSLstd450UMin, + GLSLstd450SMin, + GLSLstd450FMax, + GLSLstd450UMax, + GLSLstd450SMax, + GLSLstd450FClamp, + GLSLstd450UClamp, + GLSLstd450SClamp, + GLSLstd450FMix, + GLSLstd450IMix, + GLSLstd450Step, + GLSLstd450SmoothStep, + GLSLstd450Fma, + GLSLstd450FrexpStruct, + GLSLstd450Ldexp, + GLSLstd450PackSnorm4x8, + GLSLstd450PackUnorm4x8, + GLSLstd450PackSnorm2x16, + GLSLstd450PackUnorm2x16, + GLSLstd450PackHalf2x16, + GLSLstd450PackDouble2x32, + GLSLstd450UnpackSnorm2x16, + GLSLstd450UnpackUnorm2x16, + GLSLstd450UnpackHalf2x16, + GLSLstd450UnpackSnorm4x8, + GLSLstd450UnpackUnorm4x8, + GLSLstd450UnpackDouble2x32, + GLSLstd450Length, + GLSLstd450Distance, + GLSLstd450Cross, + GLSLstd450Normalize, + GLSLstd450FaceForward, + GLSLstd450Reflect, + GLSLstd450Refract, + GLSLstd450FindILsb, + GLSLstd450FindSMsb, + GLSLstd450FindUMsb, + GLSLstd450InterpolateAtCentroid, + GLSLstd450InterpolateAtSample, + GLSLstd450InterpolateAtOffset, + GLSLstd450NMin, + GLSLstd450NMax, + GLSLstd450NClamp}; + } else { + // Map the result id to the empty set. + combinator_ops_[extension->result_id()]; + } +} + +void IRContext::InitializeCombinators() { + get_feature_mgr()->GetCapabilities()->ForEach( + [this](SpvCapability cap) { AddCombinatorsForCapability(cap); }); + + for (auto& extension : module()->ext_inst_imports()) { + AddCombinatorsForExtension(&extension); + } + + valid_analyses_ |= kAnalysisCombinators; +} + +void IRContext::RemoveFromIdToName(const Instruction* inst) { + if (id_to_name_ && + (inst->opcode() == SpvOpName || inst->opcode() == SpvOpMemberName)) { + auto range = id_to_name_->equal_range(inst->GetSingleWordInOperand(0)); + for (auto it = range.first; it != range.second; ++it) { + if (it->second == inst) { + id_to_name_->erase(it); + break; + } + } + } +} + +LoopDescriptor* IRContext::GetLoopDescriptor(const Function* f) { + if (!AreAnalysesValid(kAnalysisLoopAnalysis)) { + ResetLoopAnalysis(); + } + + std::unordered_map::iterator it = + loop_descriptors_.find(f); + if (it == loop_descriptors_.end()) { + return &loop_descriptors_ + .emplace(std::make_pair(f, LoopDescriptor(this, f))) + .first->second; + } + + return &it->second; +} + +uint32_t IRContext::FindBuiltinInputVar(uint32_t builtin) { + for (auto& a : module_->annotations()) { + if (a.opcode() != SpvOpDecorate) continue; + if (a.GetSingleWordInOperand(kSpvDecorateDecorationInIdx) != + SpvDecorationBuiltIn) + continue; + if (a.GetSingleWordInOperand(kSpvDecorateBuiltinInIdx) != builtin) continue; + uint32_t target_id = a.GetSingleWordInOperand(kSpvDecorateTargetIdInIdx); + Instruction* b_var = get_def_use_mgr()->GetDef(target_id); + if (b_var->opcode() != SpvOpVariable) continue; + if (b_var->GetSingleWordInOperand(0) != SpvStorageClassInput) continue; + return target_id; + } + return 0; +} + +void IRContext::AddVarToEntryPoints(uint32_t var_id) { + uint32_t ocnt = 0; + for (auto& e : module()->entry_points()) { + bool found = false; + e.ForEachInOperand([&ocnt, &found, &var_id](const uint32_t* idp) { + if (ocnt >= kEntryPointInterfaceInIdx) { + if (*idp == var_id) found = true; + } + ++ocnt; + }); + if (!found) { + e.AddOperand({SPV_OPERAND_TYPE_ID, {var_id}}); + get_def_use_mgr()->AnalyzeInstDefUse(&e); + } + } +} + +uint32_t IRContext::GetBuiltinInputVarId(uint32_t builtin) { + if (!AreAnalysesValid(kAnalysisBuiltinVarId)) ResetBuiltinAnalysis(); + // If cached, return it. + std::unordered_map::iterator it = + builtin_var_id_map_.find(builtin); + if (it != builtin_var_id_map_.end()) return it->second; + // Look for one in shader + uint32_t var_id = FindBuiltinInputVar(builtin); + if (var_id == 0) { + // If not found, create it + // TODO(greg-lunarg): Add support for all builtins + analysis::TypeManager* type_mgr = get_type_mgr(); + analysis::Type* reg_type; + switch (builtin) { + case SpvBuiltInFragCoord: { + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v4float_ty(reg_float_ty, 4); + reg_type = type_mgr->GetRegisteredType(&v4float_ty); + break; + } + case SpvBuiltInVertexIndex: + case SpvBuiltInInstanceIndex: + case SpvBuiltInPrimitiveId: + case SpvBuiltInInvocationId: + case SpvBuiltInSubgroupLocalInvocationId: { + analysis::Integer uint_ty(32, false); + reg_type = type_mgr->GetRegisteredType(&uint_ty); + break; + } + case SpvBuiltInGlobalInvocationId: + case SpvBuiltInLaunchIdNV: { + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Vector v3uint_ty(reg_uint_ty, 3); + reg_type = type_mgr->GetRegisteredType(&v3uint_ty); + break; + } + case SpvBuiltInTessCoord: { + analysis::Float float_ty(32); + analysis::Type* reg_float_ty = type_mgr->GetRegisteredType(&float_ty); + analysis::Vector v3float_ty(reg_float_ty, 3); + reg_type = type_mgr->GetRegisteredType(&v3float_ty); + break; + } + case SpvBuiltInSubgroupLtMask: { + analysis::Integer uint_ty(32, false); + analysis::Type* reg_uint_ty = type_mgr->GetRegisteredType(&uint_ty); + analysis::Vector v4uint_ty(reg_uint_ty, 4); + reg_type = type_mgr->GetRegisteredType(&v4uint_ty); + break; + } + default: { + assert(false && "unhandled builtin"); + return 0; + } + } + uint32_t type_id = type_mgr->GetTypeInstruction(reg_type); + uint32_t varTyPtrId = + type_mgr->FindPointerToType(type_id, SpvStorageClassInput); + // TODO(1841): Handle id overflow. + var_id = TakeNextId(); + std::unique_ptr newVarOp( + new Instruction(this, SpvOpVariable, varTyPtrId, var_id, + {{spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {SpvStorageClassInput}}})); + get_def_use_mgr()->AnalyzeInstDefUse(&*newVarOp); + module()->AddGlobalValue(std::move(newVarOp)); + get_decoration_mgr()->AddDecorationVal(var_id, SpvDecorationBuiltIn, + builtin); + AddVarToEntryPoints(var_id); + } + builtin_var_id_map_[builtin] = var_id; + return var_id; +} + +void IRContext::AddCalls(const Function* func, std::queue* todo) { + for (auto bi = func->begin(); bi != func->end(); ++bi) + for (auto ii = bi->begin(); ii != bi->end(); ++ii) + if (ii->opcode() == SpvOpFunctionCall) + todo->push(ii->GetSingleWordInOperand(0)); +} + +bool IRContext::ProcessEntryPointCallTree(ProcessFunction& pfn) { + // Collect all of the entry points as the roots. + std::queue roots; + for (auto& e : module()->entry_points()) { + roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + } + return ProcessCallTreeFromRoots(pfn, &roots); +} + +bool IRContext::ProcessReachableCallTree(ProcessFunction& pfn) { + std::queue roots; + + // Add all entry points since they can be reached from outside the module. + for (auto& e : module()->entry_points()) + roots.push(e.GetSingleWordInOperand(kEntryPointFunctionIdInIdx)); + + // Add all exported functions since they can be reached from outside the + // module. + for (auto& a : annotations()) { + // TODO: Handle group decorations as well. Currently not generate by any + // front-end, but could be coming. + if (a.opcode() == SpvOp::SpvOpDecorate) { + if (a.GetSingleWordOperand(1) == + SpvDecoration::SpvDecorationLinkageAttributes) { + uint32_t lastOperand = a.NumOperands() - 1; + if (a.GetSingleWordOperand(lastOperand) == + SpvLinkageType::SpvLinkageTypeExport) { + uint32_t id = a.GetSingleWordOperand(0); + if (GetFunction(id)) { + roots.push(id); + } + } + } + } + } + + return ProcessCallTreeFromRoots(pfn, &roots); +} + +bool IRContext::ProcessCallTreeFromRoots(ProcessFunction& pfn, + std::queue* roots) { + // Process call tree + bool modified = false; + std::unordered_set done; + + while (!roots->empty()) { + const uint32_t fi = roots->front(); + roots->pop(); + if (done.insert(fi).second) { + Function* fn = GetFunction(fi); + assert(fn && "Trying to process a function that does not exist."); + modified = pfn(fn) || modified; + AddCalls(fn, roots); + } + } + return modified; +} + +void IRContext::EmitErrorMessage(std::string message, Instruction* inst) { + if (!consumer()) { + return; + } + + Instruction* line_inst = inst; + while (line_inst != nullptr) { // Stop at the beginning of the basic block. + if (!line_inst->dbg_line_insts().empty()) { + line_inst = &line_inst->dbg_line_insts().back(); + if (line_inst->opcode() == SpvOpNoLine) { + line_inst = nullptr; + } + break; + } + line_inst = line_inst->PreviousNode(); + } + + uint32_t line_number = 0; + uint32_t col_number = 0; + char* source = nullptr; + if (line_inst != nullptr) { + Instruction* file_name = + get_def_use_mgr()->GetDef(line_inst->GetSingleWordInOperand(0)); + source = reinterpret_cast(&file_name->GetInOperand(0).words[0]); + + // Get the line number and column number. + line_number = line_inst->GetSingleWordInOperand(1); + col_number = line_inst->GetSingleWordInOperand(2); + } + + message += + "\n " + inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + consumer()(SPV_MSG_ERROR, source, {line_number, col_number, 0}, + message.c_str()); +} + +// Gets the dominator analysis for function |f|. +DominatorAnalysis* IRContext::GetDominatorAnalysis(const Function* f) { + if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) { + ResetDominatorAnalysis(); + } + + if (dominator_trees_.find(f) == dominator_trees_.end()) { + dominator_trees_[f].InitializeTree(*cfg(), f); + } + + return &dominator_trees_[f]; +} + +// Gets the postdominator analysis for function |f|. +PostDominatorAnalysis* IRContext::GetPostDominatorAnalysis(const Function* f) { + if (!AreAnalysesValid(kAnalysisDominatorAnalysis)) { + ResetDominatorAnalysis(); + } + + if (post_dominator_trees_.find(f) == post_dominator_trees_.end()) { + post_dominator_trees_[f].InitializeTree(*cfg(), f); + } + + return &post_dominator_trees_[f]; +} + +bool IRContext::CheckCFG() { + std::unordered_map> real_preds; + if (!AreAnalysesValid(kAnalysisCFG)) { + return true; + } + + for (Function& function : *module()) { + for (const auto& bb : function) { + bb.ForEachSuccessorLabel([&bb, &real_preds](const uint32_t lab_id) { + real_preds[lab_id].push_back(bb.id()); + }); + } + + for (auto& bb : function) { + std::vector preds = cfg()->preds(bb.id()); + std::vector real = real_preds[bb.id()]; + std::sort(preds.begin(), preds.end()); + std::sort(real.begin(), real.end()); + + bool same = true; + if (preds.size() != real.size()) { + same = false; + } + + for (size_t i = 0; i < real.size() && same; i++) { + if (preds[i] != real[i]) { + same = false; + } + } + + if (!same) { + std::cerr << "Predecessors for " << bb.id() << " are different:\n"; + + std::cerr << "Real:"; + for (uint32_t i : real) { + std::cerr << ' ' << i; + } + std::cerr << std::endl; + + std::cerr << "Recorded:"; + for (uint32_t i : preds) { + std::cerr << ' ' << i; + } + std::cerr << std::endl; + } + if (!same) return false; + } + } + + return true; +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/ir_context.h b/third_party/spirv-tools/source/opt/ir_context.h new file mode 100644 index 0000000..5aa25ac --- /dev/null +++ b/third_party/spirv-tools/source/opt/ir_context.h @@ -0,0 +1,1168 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_IR_CONTEXT_H_ +#define SOURCE_OPT_IR_CONTEXT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/opt/cfg.h" +#include "source/opt/constants.h" +#include "source/opt/debug_info_manager.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/feature_manager.h" +#include "source/opt/fold.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/module.h" +#include "source/opt/register_pressure.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/struct_cfg_analysis.h" +#include "source/opt/type_manager.h" +#include "source/opt/value_number_table.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +class IRContext { + public: + // Available analyses. + // + // When adding a new analysis: + // + // 1. Enum values should be powers of 2. These are cast into uint32_t + // bitmasks, so we can have at most 31 analyses represented. + // + // 2. Make sure it gets invalidated or preserved by IRContext methods that add + // or remove IR elements (e.g., KillDef, KillInst, ReplaceAllUsesWith). + // + // 3. Add handling code in BuildInvalidAnalyses and InvalidateAnalyses + enum Analysis { + kAnalysisNone = 0 << 0, + kAnalysisBegin = 1 << 0, + kAnalysisDefUse = kAnalysisBegin, + kAnalysisInstrToBlockMapping = 1 << 1, + kAnalysisDecorations = 1 << 2, + kAnalysisCombinators = 1 << 3, + kAnalysisCFG = 1 << 4, + kAnalysisDominatorAnalysis = 1 << 5, + kAnalysisLoopAnalysis = 1 << 6, + kAnalysisNameMap = 1 << 7, + kAnalysisScalarEvolution = 1 << 8, + kAnalysisRegisterPressure = 1 << 9, + kAnalysisValueNumberTable = 1 << 10, + kAnalysisStructuredCFG = 1 << 11, + kAnalysisBuiltinVarId = 1 << 12, + kAnalysisIdToFuncMapping = 1 << 13, + kAnalysisConstants = 1 << 14, + kAnalysisTypes = 1 << 15, + kAnalysisDebugInfo = 1 << 16, + kAnalysisEnd = 1 << 17 + }; + + using ProcessFunction = std::function; + + friend inline Analysis operator|(Analysis lhs, Analysis rhs); + friend inline Analysis& operator|=(Analysis& lhs, Analysis rhs); + friend inline Analysis operator<<(Analysis a, int shift); + friend inline Analysis& operator<<=(Analysis& a, int shift); + + // Creates an |IRContext| that contains an owned |Module| + IRContext(spv_target_env env, MessageConsumer c) + : syntax_context_(spvContextCreate(env)), + grammar_(syntax_context_), + unique_id_(0), + module_(new Module()), + consumer_(std::move(c)), + def_use_mgr_(nullptr), + valid_analyses_(kAnalysisNone), + constant_mgr_(nullptr), + type_mgr_(nullptr), + id_to_name_(nullptr), + max_id_bound_(kDefaultMaxIdBound), + preserve_bindings_(false), + preserve_spec_constants_(false) { + SetContextMessageConsumer(syntax_context_, consumer_); + module_->SetContext(this); + } + + IRContext(spv_target_env env, std::unique_ptr&& m, MessageConsumer c) + : syntax_context_(spvContextCreate(env)), + grammar_(syntax_context_), + unique_id_(0), + module_(std::move(m)), + consumer_(std::move(c)), + def_use_mgr_(nullptr), + valid_analyses_(kAnalysisNone), + type_mgr_(nullptr), + id_to_name_(nullptr), + max_id_bound_(kDefaultMaxIdBound), + preserve_bindings_(false), + preserve_spec_constants_(false) { + SetContextMessageConsumer(syntax_context_, consumer_); + module_->SetContext(this); + InitializeCombinators(); + } + + ~IRContext() { spvContextDestroy(syntax_context_); } + + Module* module() const { return module_.get(); } + + // Returns a vector of pointers to constant-creation instructions in this + // context. + inline std::vector GetConstants(); + inline std::vector GetConstants() const; + + // Iterators for annotation instructions contained in this context. + inline Module::inst_iterator annotation_begin(); + inline Module::inst_iterator annotation_end(); + inline IteratorRange annotations(); + inline IteratorRange annotations() const; + + // Iterators for capabilities instructions contained in this module. + inline Module::inst_iterator capability_begin(); + inline Module::inst_iterator capability_end(); + inline IteratorRange capabilities(); + inline IteratorRange capabilities() const; + + // Iterators for types, constants and global variables instructions. + inline Module::inst_iterator types_values_begin(); + inline Module::inst_iterator types_values_end(); + inline IteratorRange types_values(); + inline IteratorRange types_values() const; + + // Iterators for extension instructions contained in this module. + inline Module::inst_iterator ext_inst_import_begin(); + inline Module::inst_iterator ext_inst_import_end(); + inline IteratorRange ext_inst_imports(); + inline IteratorRange ext_inst_imports() const; + + // There are several kinds of debug instructions, according to where they can + // appear in the logical layout of a module: + // - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued + // - Section 7b: OpName, OpMemberName + // - Section 7c: OpModuleProcessed + // - Mostly anywhere: OpLine and OpNoLine + // + + // Iterators for debug 1 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7a. + inline Module::inst_iterator debug1_begin(); + inline Module::inst_iterator debug1_end(); + inline IteratorRange debugs1(); + inline IteratorRange debugs1() const; + + // Iterators for debug 2 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7b. + inline Module::inst_iterator debug2_begin(); + inline Module::inst_iterator debug2_end(); + inline IteratorRange debugs2(); + inline IteratorRange debugs2() const; + + // Iterators for debug 3 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7c. + inline Module::inst_iterator debug3_begin(); + inline Module::inst_iterator debug3_end(); + inline IteratorRange debugs3(); + inline IteratorRange debugs3() const; + + // Iterators for debug info instructions (excluding OpLine & OpNoLine) + // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100 + // or DebugInfo extension placed between section 9 and 10. + inline Module::inst_iterator ext_inst_debuginfo_begin(); + inline Module::inst_iterator ext_inst_debuginfo_end(); + inline IteratorRange ext_inst_debuginfo(); + inline IteratorRange ext_inst_debuginfo() const; + + // Add |capability| to the module, if it is not already enabled. + inline void AddCapability(SpvCapability capability); + + // Appends a capability instruction to this module. + inline void AddCapability(std::unique_ptr&& c); + // Appends an extension instruction to this module. + inline void AddExtension(const std::string& ext_name); + inline void AddExtension(std::unique_ptr&& e); + // Appends an extended instruction set instruction to this module. + inline void AddExtInstImport(const std::string& name); + inline void AddExtInstImport(std::unique_ptr&& e); + // Set the memory model for this module. + inline void SetMemoryModel(std::unique_ptr&& m); + // Appends an entry point instruction to this module. + inline void AddEntryPoint(std::unique_ptr&& e); + // Appends an execution mode instruction to this module. + inline void AddExecutionMode(std::unique_ptr&& e); + // Appends a debug 1 instruction (excluding OpLine & OpNoLine) to this module. + // "debug 1" instructions are the ones in layout section 7.a), see section + // 2.4 Logical Layout of a Module from the SPIR-V specification. + inline void AddDebug1Inst(std::unique_ptr&& d); + // Appends a debug 2 instruction (excluding OpLine & OpNoLine) to this module. + // "debug 2" instructions are the ones in layout section 7.b), see section + // 2.4 Logical Layout of a Module from the SPIR-V specification. + inline void AddDebug2Inst(std::unique_ptr&& d); + // Appends a debug 3 instruction (OpModuleProcessed) to this module. + // This is due to decision by the SPIR Working Group, pending publication. + inline void AddDebug3Inst(std::unique_ptr&& d); + // Appends a OpExtInst for DebugInfo to this module. + inline void AddExtInstDebugInfo(std::unique_ptr&& d); + // Appends an annotation instruction to this module. + inline void AddAnnotationInst(std::unique_ptr&& a); + // Appends a type-declaration instruction to this module. + inline void AddType(std::unique_ptr&& t); + // Appends a constant, global variable, or OpUndef instruction to this module. + inline void AddGlobalValue(std::unique_ptr&& v); + // Appends a function to this module. + inline void AddFunction(std::unique_ptr&& f); + + // Returns a pointer to a def-use manager. If the def-use manager is + // invalid, it is rebuilt first. + analysis::DefUseManager* get_def_use_mgr() { + if (!AreAnalysesValid(kAnalysisDefUse)) { + BuildDefUseManager(); + } + return def_use_mgr_.get(); + } + + // Returns a pointer to a value number table. If the liveness analysis is + // invalid, it is rebuilt first. + ValueNumberTable* GetValueNumberTable() { + if (!AreAnalysesValid(kAnalysisValueNumberTable)) { + BuildValueNumberTable(); + } + return vn_table_.get(); + } + + // Returns a pointer to a StructuredCFGAnalysis. If the analysis is invalid, + // it is rebuilt first. + StructuredCFGAnalysis* GetStructuredCFGAnalysis() { + if (!AreAnalysesValid(kAnalysisStructuredCFG)) { + BuildStructuredCFGAnalysis(); + } + return struct_cfg_analysis_.get(); + } + + // Returns a pointer to a liveness analysis. If the liveness analysis is + // invalid, it is rebuilt first. + LivenessAnalysis* GetLivenessAnalysis() { + if (!AreAnalysesValid(kAnalysisRegisterPressure)) { + BuildRegPressureAnalysis(); + } + return reg_pressure_.get(); + } + + // Returns the basic block for instruction |instr|. Re-builds the instruction + // block map, if needed. + BasicBlock* get_instr_block(Instruction* instr) { + if (!AreAnalysesValid(kAnalysisInstrToBlockMapping)) { + BuildInstrToBlockMapping(); + } + auto entry = instr_to_block_.find(instr); + return (entry != instr_to_block_.end()) ? entry->second : nullptr; + } + + // Returns the basic block for |id|. Re-builds the instruction block map, if + // needed. + // + // |id| must be a registered definition. + BasicBlock* get_instr_block(uint32_t id) { + Instruction* def = get_def_use_mgr()->GetDef(id); + return get_instr_block(def); + } + + // Sets the basic block for |inst|. Re-builds the mapping if it has become + // invalid. + void set_instr_block(Instruction* inst, BasicBlock* block) { + if (AreAnalysesValid(kAnalysisInstrToBlockMapping)) { + instr_to_block_[inst] = block; + } + } + + // Returns a pointer the decoration manager. If the decoration manger is + // invalid, it is rebuilt first. + analysis::DecorationManager* get_decoration_mgr() { + if (!AreAnalysesValid(kAnalysisDecorations)) { + BuildDecorationManager(); + } + return decoration_mgr_.get(); + } + + // Returns a pointer to the constant manager. If no constant manager has been + // created yet, it creates one. NOTE: Once created, the constant manager + // remains active and it is never re-built. + analysis::ConstantManager* get_constant_mgr() { + if (!AreAnalysesValid(kAnalysisConstants)) { + BuildConstantManager(); + } + return constant_mgr_.get(); + } + + // Returns a pointer to the type manager. If no type manager has been created + // yet, it creates one. NOTE: Once created, the type manager remains active it + // is never re-built. + analysis::TypeManager* get_type_mgr() { + if (!AreAnalysesValid(kAnalysisTypes)) { + BuildTypeManager(); + } + return type_mgr_.get(); + } + + // Returns a pointer to the debug information manager. If no debug + // information manager has been created yet, it creates one. + // NOTE: Once created, the debug information manager remains active + // it is never re-built. + analysis::DebugInfoManager* get_debug_info_mgr() { + if (!AreAnalysesValid(kAnalysisDebugInfo)) { + BuildDebugInfoManager(); + } + return debug_info_mgr_.get(); + } + + // Returns a pointer to the scalar evolution analysis. If it is invalid it + // will be rebuilt first. + ScalarEvolutionAnalysis* GetScalarEvolutionAnalysis() { + if (!AreAnalysesValid(kAnalysisScalarEvolution)) { + BuildScalarEvolutionAnalysis(); + } + return scalar_evolution_analysis_.get(); + } + + // Build the map from the ids to the OpName and OpMemberName instruction + // associated with it. + inline void BuildIdToNameMap(); + + // Returns a range of instrucions that contain all of the OpName and + // OpMemberNames associated with the given id. + inline IteratorRange::iterator> + GetNames(uint32_t id); + + // Returns an OpMemberName instruction that targets |struct_type_id| at + // index |index|. Returns nullptr if no such instruction exists. + // While the SPIR-V spec does not prohibit having multiple OpMemberName + // instructions for the same structure member, it is hard to imagine a member + // having more than one name. This method returns the first one it finds. + inline Instruction* GetMemberName(uint32_t struct_type_id, uint32_t index); + + // Sets the message consumer to the given |consumer|. |consumer| which will be + // invoked every time there is a message to be communicated to the outside. + void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); } + + // Returns the reference to the message consumer for this pass. + const MessageConsumer& consumer() const { return consumer_; } + + // Rebuilds the analyses in |set| that are invalid. + void BuildInvalidAnalyses(Analysis set); + + // Invalidates all of the analyses except for those in |preserved_analyses|. + void InvalidateAnalysesExceptFor(Analysis preserved_analyses); + + // Invalidates the analyses marked in |analyses_to_invalidate|. + void InvalidateAnalyses(Analysis analyses_to_invalidate); + + // Deletes the instruction defining the given |id|. Returns true on + // success, false if the given |id| is not defined at all. This method also + // erases the name, decorations, and defintion of |id|. + // + // Pointers and iterators pointing to the deleted instructions become invalid. + // However other pointers and iterators are still valid. + bool KillDef(uint32_t id); + + // Deletes the given instruction |inst|. This method erases the + // information of the given instruction's uses of its operands. If |inst| + // defines a result id, its name and decorations will also be deleted. + // + // Pointer and iterator pointing to the deleted instructions become invalid. + // However other pointers and iterators are still valid. + // + // Note that if an instruction is not in an instruction list, the memory may + // not be safe to delete, so the instruction is turned into a OpNop instead. + // This can happen with OpLabel. + // + // Returns a pointer to the instruction after |inst| or |nullptr| if no such + // instruction exists. + Instruction* KillInst(Instruction* inst); + + // Removes the non-semantic instruction tree that uses |inst|'s result id. + void KillNonSemanticInfo(Instruction* inst); + + // Returns true if all of the given analyses are valid. + bool AreAnalysesValid(Analysis set) { return (set & valid_analyses_) == set; } + + // Replaces all uses of |before| id with |after| id. Returns true if any + // replacement happens. This method does not kill the definition of the + // |before| id. If |after| is the same as |before|, does nothing and returns + // false. + // + // |before| and |after| must be registered definitions in the DefUseManager. + bool ReplaceAllUsesWith(uint32_t before, uint32_t after); + + // Replace all uses of |before| id with |after| id if those uses + // (instruction) return true for |predicate|. Returns true if + // any replacement happens. This method does not kill the definition of the + // |before| id. If |after| is the same as |before|, does nothing and return + // false. + bool ReplaceAllUsesWithPredicate( + uint32_t before, uint32_t after, + const std::function& predicate); + + // Returns true if all of the analyses that are suppose to be valid are + // actually valid. + bool IsConsistent(); + + // The IRContext will look at the def and uses of |inst| and update any valid + // analyses will be updated accordingly. + inline void AnalyzeDefUse(Instruction* inst); + + // Informs the IRContext that the uses of |inst| are going to change, and that + // is should forget everything it know about the current uses. Any valid + // analyses will be updated accordingly. + void ForgetUses(Instruction* inst); + + // The IRContext will look at the uses of |inst| and update any valid analyses + // will be updated accordingly. + void AnalyzeUses(Instruction* inst); + + // Kill all name and decorate ops targeting |id|. + void KillNamesAndDecorates(uint32_t id); + + // Kill all name and decorate ops targeting the result id of |inst|. + void KillNamesAndDecorates(Instruction* inst); + + // Change operands of debug instruction to DebugInfoNone. + void KillOperandFromDebugInstructions(Instruction* inst); + + // Returns the next unique id for use by an instruction. + inline uint32_t TakeNextUniqueId() { + assert(unique_id_ != std::numeric_limits::max()); + + // Skip zero. + return ++unique_id_; + } + + // Returns true if |inst| is a combinator in the current context. + // |combinator_ops_| is built if it has not been already. + inline bool IsCombinatorInstruction(const Instruction* inst) { + if (!AreAnalysesValid(kAnalysisCombinators)) { + InitializeCombinators(); + } + const uint32_t kExtInstSetIdInIndx = 0; + const uint32_t kExtInstInstructionInIndx = 1; + + if (inst->opcode() != SpvOpExtInst) { + return combinator_ops_[0].count(inst->opcode()) != 0; + } else { + uint32_t set = inst->GetSingleWordInOperand(kExtInstSetIdInIndx); + uint32_t op = inst->GetSingleWordInOperand(kExtInstInstructionInIndx); + return combinator_ops_[set].count(op) != 0; + } + } + + // Returns a pointer to the CFG for all the functions in |module_|. + CFG* cfg() { + if (!AreAnalysesValid(kAnalysisCFG)) { + BuildCFG(); + } + return cfg_.get(); + } + + // Gets the loop descriptor for function |f|. + LoopDescriptor* GetLoopDescriptor(const Function* f); + + // Gets the dominator analysis for function |f|. + DominatorAnalysis* GetDominatorAnalysis(const Function* f); + + // Gets the postdominator analysis for function |f|. + PostDominatorAnalysis* GetPostDominatorAnalysis(const Function* f); + + // Remove the dominator tree of |f| from the cache. + inline void RemoveDominatorAnalysis(const Function* f) { + dominator_trees_.erase(f); + } + + // Remove the postdominator tree of |f| from the cache. + inline void RemovePostDominatorAnalysis(const Function* f) { + post_dominator_trees_.erase(f); + } + + // Return the next available SSA id and increment it. Returns 0 if the + // maximum SSA id has been reached. + inline uint32_t TakeNextId() { + uint32_t next_id = module()->TakeNextIdBound(); + if (next_id == 0) { + if (consumer()) { + std::string message = "ID overflow. Try running compact-ids."; + consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str()); + } + } + return next_id; + } + + FeatureManager* get_feature_mgr() { + if (!feature_mgr_.get()) { + AnalyzeFeatures(); + } + return feature_mgr_.get(); + } + + void ResetFeatureManager() { feature_mgr_.reset(nullptr); } + + // Returns the grammar for this context. + const AssemblyGrammar& grammar() const { return grammar_; } + + // If |inst| has not yet been analysed by the def-use manager, then analyse + // its definitions and uses. + inline void UpdateDefUse(Instruction* inst); + + const InstructionFolder& get_instruction_folder() { + if (!inst_folder_) { + inst_folder_ = MakeUnique(this); + } + return *inst_folder_; + } + + uint32_t max_id_bound() const { return max_id_bound_; } + void set_max_id_bound(uint32_t new_bound) { max_id_bound_ = new_bound; } + + bool preserve_bindings() const { return preserve_bindings_; } + void set_preserve_bindings(bool should_preserve_bindings) { + preserve_bindings_ = should_preserve_bindings; + } + + bool preserve_spec_constants() const { return preserve_spec_constants_; } + void set_preserve_spec_constants(bool should_preserve_spec_constants) { + preserve_spec_constants_ = should_preserve_spec_constants; + } + + // Return id of input variable only decorated with |builtin|, if in module. + // Create variable and return its id otherwise. If builtin not currently + // supported, return 0. + uint32_t GetBuiltinInputVarId(uint32_t builtin); + + // Returns the function whose id is |id|, if one exists. Returns |nullptr| + // otherwise. + Function* GetFunction(uint32_t id) { + if (!AreAnalysesValid(kAnalysisIdToFuncMapping)) { + BuildIdToFuncMapping(); + } + auto entry = id_to_func_.find(id); + return (entry != id_to_func_.end()) ? entry->second : nullptr; + } + + Function* GetFunction(Instruction* inst) { + if (inst->opcode() != SpvOpFunction) { + return nullptr; + } + return GetFunction(inst->result_id()); + } + + // Add to |todo| all ids of functions called directly from |func|. + void AddCalls(const Function* func, std::queue* todo); + + // Applies |pfn| to every function in the call trees that are rooted at the + // entry points. Returns true if any call |pfn| returns true. By convention + // |pfn| should return true if it modified the module. + bool ProcessEntryPointCallTree(ProcessFunction& pfn); + + // Applies |pfn| to every function in the call trees rooted at the entry + // points and exported functions. Returns true if any call |pfn| returns + // true. By convention |pfn| should return true if it modified the module. + bool ProcessReachableCallTree(ProcessFunction& pfn); + + // Applies |pfn| to every function in the call trees rooted at the elements of + // |roots|. Returns true if any call to |pfn| returns true. By convention + // |pfn| should return true if it modified the module. After returning + // |roots| will be empty. + bool ProcessCallTreeFromRoots(ProcessFunction& pfn, + std::queue* roots); + + // Emmits a error message to the message consumer indicating the error + // described by |message| occurred in |inst|. + void EmitErrorMessage(std::string message, Instruction* inst); + + private: + // Builds the def-use manager from scratch, even if it was already valid. + void BuildDefUseManager() { + def_use_mgr_ = MakeUnique(module()); + valid_analyses_ = valid_analyses_ | kAnalysisDefUse; + } + + // Builds the instruction-block map for the whole module. + void BuildInstrToBlockMapping() { + instr_to_block_.clear(); + for (auto& fn : *module_) { + for (auto& block : fn) { + block.ForEachInst([this, &block](Instruction* inst) { + instr_to_block_[inst] = █ + }); + } + } + valid_analyses_ = valid_analyses_ | kAnalysisInstrToBlockMapping; + } + + // Builds the instruction-function map for the whole module. + void BuildIdToFuncMapping() { + id_to_func_.clear(); + for (auto& fn : *module_) { + id_to_func_[fn.result_id()] = &fn; + } + valid_analyses_ = valid_analyses_ | kAnalysisIdToFuncMapping; + } + + void BuildDecorationManager() { + decoration_mgr_ = MakeUnique(module()); + valid_analyses_ = valid_analyses_ | kAnalysisDecorations; + } + + void BuildCFG() { + cfg_ = MakeUnique(module()); + valid_analyses_ = valid_analyses_ | kAnalysisCFG; + } + + void BuildScalarEvolutionAnalysis() { + scalar_evolution_analysis_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisScalarEvolution; + } + + // Builds the liveness analysis from scratch, even if it was already valid. + void BuildRegPressureAnalysis() { + reg_pressure_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisRegisterPressure; + } + + // Builds the value number table analysis from scratch, even if it was already + // valid. + void BuildValueNumberTable() { + vn_table_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisValueNumberTable; + } + + // Builds the structured CFG analysis from scratch, even if it was already + // valid. + void BuildStructuredCFGAnalysis() { + struct_cfg_analysis_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisStructuredCFG; + } + + // Builds the constant manager from scratch, even if it was already + // valid. + void BuildConstantManager() { + constant_mgr_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisConstants; + } + + // Builds the type manager from scratch, even if it was already + // valid. + void BuildTypeManager() { + type_mgr_ = MakeUnique(consumer(), this); + valid_analyses_ = valid_analyses_ | kAnalysisTypes; + } + + // Builds the debug information manager from scratch, even if it was + // already valid. + void BuildDebugInfoManager() { + debug_info_mgr_ = MakeUnique(this); + valid_analyses_ = valid_analyses_ | kAnalysisDebugInfo; + } + + // Removes all computed dominator and post-dominator trees. This will force + // the context to rebuild the trees on demand. + void ResetDominatorAnalysis() { + // Clear the cache. + dominator_trees_.clear(); + post_dominator_trees_.clear(); + valid_analyses_ = valid_analyses_ | kAnalysisDominatorAnalysis; + } + + // Removes all computed loop descriptors. + void ResetLoopAnalysis() { + // Clear the cache. + loop_descriptors_.clear(); + valid_analyses_ = valid_analyses_ | kAnalysisLoopAnalysis; + } + + // Removes all computed loop descriptors. + void ResetBuiltinAnalysis() { + // Clear the cache. + builtin_var_id_map_.clear(); + valid_analyses_ = valid_analyses_ | kAnalysisBuiltinVarId; + } + + // Analyzes the features in the owned module. Builds the manager if required. + void AnalyzeFeatures() { + feature_mgr_ = MakeUnique(grammar_); + feature_mgr_->Analyze(module()); + } + + // Scans a module looking for it capabilities, and initializes combinator_ops_ + // accordingly. + void InitializeCombinators(); + + // Add the combinator opcode for the given capability to combinator_ops_. + void AddCombinatorsForCapability(uint32_t capability); + + // Add the combinator opcode for the given extension to combinator_ops_. + void AddCombinatorsForExtension(Instruction* extension); + + // Remove |inst| from |id_to_name_| if it is in map. + void RemoveFromIdToName(const Instruction* inst); + + // Returns true if it is suppose to be valid but it is incorrect. Returns + // true if the cfg is invalidated. + bool CheckCFG(); + + // Return id of input variable only decorated with |builtin|, if in module. + // Return 0 otherwise. + uint32_t FindBuiltinInputVar(uint32_t builtin); + + // Add |var_id| to all entry points in module. + void AddVarToEntryPoints(uint32_t var_id); + + // The SPIR-V syntax context containing grammar tables for opcodes and + // operands. + spv_context syntax_context_; + + // Auxiliary object for querying SPIR-V grammar facts. + AssemblyGrammar grammar_; + + // An unique identifier for instructions in |module_|. Can be used to order + // instructions in a container. + // + // This member is initialized to 0, but always issues this value plus one. + // Therefore, 0 is not a valid unique id for an instruction. + uint32_t unique_id_; + + // The module being processed within this IR context. + std::unique_ptr module_; + + // A message consumer for diagnostics. + MessageConsumer consumer_; + + // The def-use manager for |module_|. + std::unique_ptr def_use_mgr_; + + // The instruction decoration manager for |module_|. + std::unique_ptr decoration_mgr_; + std::unique_ptr feature_mgr_; + + // A map from instructions to the basic block they belong to. This mapping is + // built on-demand when get_instr_block() is called. + // + // NOTE: Do not traverse this map. Ever. Use the function and basic block + // iterators to traverse instructions. + std::unordered_map instr_to_block_; + + // A map from ids to the function they define. This mapping is + // built on-demand when GetFunction() is called. + // + // NOTE: Do not traverse this map. Ever. Use the function and basic block + // iterators to traverse instructions. + std::unordered_map id_to_func_; + + // A bitset indicating which analyes are currently valid. + Analysis valid_analyses_; + + // Opcodes of shader capability core executable instructions + // without side-effect. + std::unordered_map> combinator_ops_; + + // Opcodes of shader capability core executable instructions + // without side-effect. + std::unordered_map builtin_var_id_map_; + + // The CFG for all the functions in |module_|. + std::unique_ptr cfg_; + + // Each function in the module will create its own dominator tree. We cache + // the result so it doesn't need to be rebuilt each time. + std::map dominator_trees_; + std::map post_dominator_trees_; + + // Cache of loop descriptors for each function. + std::unordered_map loop_descriptors_; + + // Constant manager for |module_|. + std::unique_ptr constant_mgr_; + + // Type manager for |module_|. + std::unique_ptr type_mgr_; + + // Debug information manager for |module_|. + std::unique_ptr debug_info_mgr_; + + // A map from an id to its corresponding OpName and OpMemberName instructions. + std::unique_ptr> id_to_name_; + + // The cache scalar evolution analysis node. + std::unique_ptr scalar_evolution_analysis_; + + // The liveness analysis |module_|. + std::unique_ptr reg_pressure_; + + std::unique_ptr vn_table_; + + std::unique_ptr inst_folder_; + + std::unique_ptr struct_cfg_analysis_; + + // The maximum legal value for the id bound. + uint32_t max_id_bound_; + + // Whether all bindings within |module_| should be preserved. + bool preserve_bindings_; + + // Whether all specialization constants within |module_| + // should be preserved. + bool preserve_spec_constants_; +}; + +inline IRContext::Analysis operator|(IRContext::Analysis lhs, + IRContext::Analysis rhs) { + return static_cast(static_cast(lhs) | + static_cast(rhs)); +} + +inline IRContext::Analysis& operator|=(IRContext::Analysis& lhs, + IRContext::Analysis rhs) { + lhs = static_cast(static_cast(lhs) | + static_cast(rhs)); + return lhs; +} + +inline IRContext::Analysis operator<<(IRContext::Analysis a, int shift) { + return static_cast(static_cast(a) << shift); +} + +inline IRContext::Analysis& operator<<=(IRContext::Analysis& a, int shift) { + a = static_cast(static_cast(a) << shift); + return a; +} + +std::vector IRContext::GetConstants() { + return module()->GetConstants(); +} + +std::vector IRContext::GetConstants() const { + return ((const Module*)module())->GetConstants(); +} + +Module::inst_iterator IRContext::annotation_begin() { + return module()->annotation_begin(); +} + +Module::inst_iterator IRContext::annotation_end() { + return module()->annotation_end(); +} + +IteratorRange IRContext::annotations() { + return module_->annotations(); +} + +IteratorRange IRContext::annotations() const { + return ((const Module*)module_.get())->annotations(); +} + +Module::inst_iterator IRContext::capability_begin() { + return module()->capability_begin(); +} + +Module::inst_iterator IRContext::capability_end() { + return module()->capability_end(); +} + +IteratorRange IRContext::capabilities() { + return module()->capabilities(); +} + +IteratorRange IRContext::capabilities() const { + return ((const Module*)module())->capabilities(); +} + +Module::inst_iterator IRContext::types_values_begin() { + return module()->types_values_begin(); +} + +Module::inst_iterator IRContext::types_values_end() { + return module()->types_values_end(); +} + +IteratorRange IRContext::types_values() { + return module()->types_values(); +} + +IteratorRange IRContext::types_values() const { + return ((const Module*)module_.get())->types_values(); +} + +Module::inst_iterator IRContext::ext_inst_import_begin() { + return module()->ext_inst_import_begin(); +} + +Module::inst_iterator IRContext::ext_inst_import_end() { + return module()->ext_inst_import_end(); +} + +IteratorRange IRContext::ext_inst_imports() { + return module()->ext_inst_imports(); +} + +IteratorRange IRContext::ext_inst_imports() const { + return ((const Module*)module_.get())->ext_inst_imports(); +} + +Module::inst_iterator IRContext::debug1_begin() { + return module()->debug1_begin(); +} + +Module::inst_iterator IRContext::debug1_end() { return module()->debug1_end(); } + +IteratorRange IRContext::debugs1() { + return module()->debugs1(); +} + +IteratorRange IRContext::debugs1() const { + return ((const Module*)module_.get())->debugs1(); +} + +Module::inst_iterator IRContext::debug2_begin() { + return module()->debug2_begin(); +} +Module::inst_iterator IRContext::debug2_end() { return module()->debug2_end(); } + +IteratorRange IRContext::debugs2() { + return module()->debugs2(); +} + +IteratorRange IRContext::debugs2() const { + return ((const Module*)module_.get())->debugs2(); +} + +Module::inst_iterator IRContext::debug3_begin() { + return module()->debug3_begin(); +} + +Module::inst_iterator IRContext::debug3_end() { return module()->debug3_end(); } + +IteratorRange IRContext::debugs3() { + return module()->debugs3(); +} + +IteratorRange IRContext::debugs3() const { + return ((const Module*)module_.get())->debugs3(); +} + +Module::inst_iterator IRContext::ext_inst_debuginfo_begin() { + return module()->ext_inst_debuginfo_begin(); +} + +Module::inst_iterator IRContext::ext_inst_debuginfo_end() { + return module()->ext_inst_debuginfo_end(); +} + +IteratorRange IRContext::ext_inst_debuginfo() { + return module()->ext_inst_debuginfo(); +} + +IteratorRange IRContext::ext_inst_debuginfo() + const { + return ((const Module*)module_.get())->ext_inst_debuginfo(); +} + +void IRContext::AddCapability(SpvCapability capability) { + if (!get_feature_mgr()->HasCapability(capability)) { + std::unique_ptr capability_inst(new Instruction( + this, SpvOpCapability, 0, 0, + {{SPV_OPERAND_TYPE_CAPABILITY, {static_cast(capability)}}})); + AddCapability(std::move(capability_inst)); + } +} + +void IRContext::AddCapability(std::unique_ptr&& c) { + AddCombinatorsForCapability(c->GetSingleWordInOperand(0)); + if (feature_mgr_ != nullptr) { + feature_mgr_->AddCapability( + static_cast(c->GetSingleWordInOperand(0))); + } + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(c.get()); + } + module()->AddCapability(std::move(c)); +} + +void IRContext::AddExtension(const std::string& ext_name) { + const auto num_chars = ext_name.size(); + // Compute num words, accommodate the terminating null character. + const auto num_words = (num_chars + 1 + 3) / 4; + std::vector ext_words(num_words, 0u); + std::memcpy(ext_words.data(), ext_name.data(), num_chars); + AddExtension(std::unique_ptr( + new Instruction(this, SpvOpExtension, 0u, 0u, + {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); +} + +void IRContext::AddExtension(std::unique_ptr&& e) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(e.get()); + } + if (feature_mgr_ != nullptr) { + feature_mgr_->AddExtension(&*e); + } + module()->AddExtension(std::move(e)); +} + +void IRContext::AddExtInstImport(const std::string& name) { + const auto num_chars = name.size(); + // Compute num words, accommodate the terminating null character. + const auto num_words = (num_chars + 1 + 3) / 4; + std::vector ext_words(num_words, 0u); + std::memcpy(ext_words.data(), name.data(), num_chars); + AddExtInstImport(std::unique_ptr( + new Instruction(this, SpvOpExtInstImport, 0u, TakeNextId(), + {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}))); +} + +void IRContext::AddExtInstImport(std::unique_ptr&& e) { + AddCombinatorsForExtension(e.get()); + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(e.get()); + } + module()->AddExtInstImport(std::move(e)); + if (feature_mgr_ != nullptr) { + feature_mgr_->AddExtInstImportIds(module()); + } +} + +void IRContext::SetMemoryModel(std::unique_ptr&& m) { + module()->SetMemoryModel(std::move(m)); +} + +void IRContext::AddEntryPoint(std::unique_ptr&& e) { + module()->AddEntryPoint(std::move(e)); +} + +void IRContext::AddExecutionMode(std::unique_ptr&& e) { + module()->AddExecutionMode(std::move(e)); +} + +void IRContext::AddDebug1Inst(std::unique_ptr&& d) { + module()->AddDebug1Inst(std::move(d)); +} + +void IRContext::AddDebug2Inst(std::unique_ptr&& d) { + if (AreAnalysesValid(kAnalysisNameMap)) { + if (d->opcode() == SpvOpName || d->opcode() == SpvOpMemberName) { + // OpName and OpMemberName do not have result-ids. The target of the + // instruction is at InOperand index 0. + id_to_name_->insert({d->GetSingleWordInOperand(0), d.get()}); + } + } + module()->AddDebug2Inst(std::move(d)); +} + +void IRContext::AddDebug3Inst(std::unique_ptr&& d) { + module()->AddDebug3Inst(std::move(d)); +} + +void IRContext::AddExtInstDebugInfo(std::unique_ptr&& d) { + module()->AddExtInstDebugInfo(std::move(d)); +} + +void IRContext::AddAnnotationInst(std::unique_ptr&& a) { + if (AreAnalysesValid(kAnalysisDecorations)) { + get_decoration_mgr()->AddDecoration(a.get()); + } + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(a.get()); + } + module()->AddAnnotationInst(std::move(a)); +} + +void IRContext::AddType(std::unique_ptr&& t) { + module()->AddType(std::move(t)); + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(&*(--types_values_end())); + } +} + +void IRContext::AddGlobalValue(std::unique_ptr&& v) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(&*v); + } + module()->AddGlobalValue(std::move(v)); +} + +void IRContext::AddFunction(std::unique_ptr&& f) { + module()->AddFunction(std::move(f)); +} + +void IRContext::AnalyzeDefUse(Instruction* inst) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->AnalyzeInstDefUse(inst); + } +} + +void IRContext::UpdateDefUse(Instruction* inst) { + if (AreAnalysesValid(kAnalysisDefUse)) { + get_def_use_mgr()->UpdateDefUse(inst); + } +} + +void IRContext::BuildIdToNameMap() { + id_to_name_ = MakeUnique>(); + for (Instruction& debug_inst : debugs2()) { + if (debug_inst.opcode() == SpvOpMemberName || + debug_inst.opcode() == SpvOpName) { + id_to_name_->insert({debug_inst.GetSingleWordInOperand(0), &debug_inst}); + } + } + valid_analyses_ = valid_analyses_ | kAnalysisNameMap; +} + +IteratorRange::iterator> +IRContext::GetNames(uint32_t id) { + if (!AreAnalysesValid(kAnalysisNameMap)) { + BuildIdToNameMap(); + } + auto result = id_to_name_->equal_range(id); + return make_range(std::move(result.first), std::move(result.second)); +} + +Instruction* IRContext::GetMemberName(uint32_t struct_type_id, uint32_t index) { + if (!AreAnalysesValid(kAnalysisNameMap)) { + BuildIdToNameMap(); + } + auto result = id_to_name_->equal_range(struct_type_id); + for (auto i = result.first; i != result.second; ++i) { + auto* name_instr = i->second; + if (name_instr->opcode() == SpvOpMemberName && + name_instr->GetSingleWordInOperand(1) == index) { + return name_instr; + } + } + return nullptr; +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_IR_CONTEXT_H_ diff --git a/third_party/spirv-tools/source/opt/ir_loader.cpp b/third_party/spirv-tools/source/opt/ir_loader.cpp new file mode 100644 index 0000000..4a44309 --- /dev/null +++ b/third_party/spirv-tools/source/opt/ir_loader.cpp @@ -0,0 +1,313 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/ir_loader.h" + +#include + +#include "DebugInfo.h" +#include "OpenCLDebugInfo100.h" +#include "source/ext_inst.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" +#include "source/util/make_unique.h" + +static const uint32_t kExtInstSetIndex = 4; +static const uint32_t kLexicalScopeIndex = 5; +static const uint32_t kInlinedAtIndex = 6; + +namespace spvtools { +namespace opt { + +IrLoader::IrLoader(const MessageConsumer& consumer, Module* m) + : consumer_(consumer), + module_(m), + source_(""), + inst_index_(0), + last_dbg_scope_(kNoDebugScope, kNoInlinedAt) {} + +bool IrLoader::AddInstruction(const spv_parsed_instruction_t* inst) { + ++inst_index_; + const auto opcode = static_cast(inst->opcode); + if (IsDebugLineInst(opcode)) { + last_line_inst_.reset(); + dbg_line_info_.push_back( + Instruction(module()->context(), *inst, last_dbg_scope_)); + return true; + } + + // If it is a DebugScope or DebugNoScope of debug extension, we do not + // create a new instruction, but simply keep the information in + // struct DebugScope. + if (opcode == SpvOpExtInst && spvExtInstIsDebugInfo(inst->ext_inst_type)) { + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == OpenCLDebugInfo100DebugScope) { + uint32_t inlined_at = 0; + if (inst->num_words > kInlinedAtIndex) + inlined_at = inst->words[kInlinedAtIndex]; + last_dbg_scope_ = + DebugScope(inst->words[kLexicalScopeIndex], inlined_at); + module()->SetContainsDebugScope(); + return true; + } + if (ext_inst_key == OpenCLDebugInfo100DebugNoScope) { + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + module()->SetContainsDebugScope(); + return true; + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + if (ext_inst_key == DebugInfoDebugScope) { + uint32_t inlined_at = 0; + if (inst->num_words > kInlinedAtIndex) + inlined_at = inst->words[kInlinedAtIndex]; + last_dbg_scope_ = + DebugScope(inst->words[kLexicalScopeIndex], inlined_at); + module()->SetContainsDebugScope(); + return true; + } + if (ext_inst_key == DebugInfoDebugNoScope) { + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + module()->SetContainsDebugScope(); + return true; + } + } + } + + std::unique_ptr spv_inst( + new Instruction(module()->context(), *inst, std::move(dbg_line_info_))); + if (!spv_inst->dbg_line_insts().empty()) { + if (spv_inst->dbg_line_insts().back().opcode() != SpvOpNoLine) { + last_line_inst_ = std::unique_ptr( + spv_inst->dbg_line_insts().back().Clone(module()->context())); + } + dbg_line_info_.clear(); + } else if (last_line_inst_ != nullptr) { + last_line_inst_->SetDebugScope(last_dbg_scope_); + spv_inst->dbg_line_insts().push_back(*last_line_inst_); + } + + const char* src = source_.c_str(); + spv_position_t loc = {inst_index_, 0, 0}; + + // Handle function and basic block boundaries first, then normal + // instructions. + if (opcode == SpvOpFunction) { + if (function_ != nullptr) { + Error(consumer_, src, loc, "function inside function"); + return false; + } + function_ = MakeUnique(std::move(spv_inst)); + } else if (opcode == SpvOpFunctionEnd) { + if (function_ == nullptr) { + Error(consumer_, src, loc, + "OpFunctionEnd without corresponding OpFunction"); + return false; + } + if (block_ != nullptr) { + Error(consumer_, src, loc, "OpFunctionEnd inside basic block"); + return false; + } + function_->SetFunctionEnd(std::move(spv_inst)); + module_->AddFunction(std::move(function_)); + function_ = nullptr; + } else if (opcode == SpvOpLabel) { + if (function_ == nullptr) { + Error(consumer_, src, loc, "OpLabel outside function"); + return false; + } + if (block_ != nullptr) { + Error(consumer_, src, loc, "OpLabel inside basic block"); + return false; + } + block_ = MakeUnique(std::move(spv_inst)); + } else if (IsTerminatorInst(opcode)) { + if (function_ == nullptr) { + Error(consumer_, src, loc, "terminator instruction outside function"); + return false; + } + if (block_ == nullptr) { + Error(consumer_, src, loc, "terminator instruction outside basic block"); + return false; + } + if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) + spv_inst->SetDebugScope(last_dbg_scope_); + block_->AddInstruction(std::move(spv_inst)); + function_->AddBasicBlock(std::move(block_)); + block_ = nullptr; + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + last_line_inst_.reset(); + dbg_line_info_.clear(); + } else { + if (function_ == nullptr) { // Outside function definition + SPIRV_ASSERT(consumer_, block_ == nullptr); + if (opcode == SpvOpCapability) { + module_->AddCapability(std::move(spv_inst)); + } else if (opcode == SpvOpExtension) { + module_->AddExtension(std::move(spv_inst)); + } else if (opcode == SpvOpExtInstImport) { + module_->AddExtInstImport(std::move(spv_inst)); + } else if (opcode == SpvOpMemoryModel) { + module_->SetMemoryModel(std::move(spv_inst)); + } else if (opcode == SpvOpEntryPoint) { + module_->AddEntryPoint(std::move(spv_inst)); + } else if (opcode == SpvOpExecutionMode) { + module_->AddExecutionMode(std::move(spv_inst)); + } else if (IsDebug1Inst(opcode)) { + module_->AddDebug1Inst(std::move(spv_inst)); + } else if (IsDebug2Inst(opcode)) { + module_->AddDebug2Inst(std::move(spv_inst)); + } else if (IsDebug3Inst(opcode)) { + module_->AddDebug3Inst(std::move(spv_inst)); + } else if (IsAnnotationInst(opcode)) { + module_->AddAnnotationInst(std::move(spv_inst)); + } else if (IsTypeInst(opcode)) { + module_->AddType(std::move(spv_inst)); + } else if (IsConstantInst(opcode) || opcode == SpvOpVariable || + opcode == SpvOpUndef) { + module_->AddGlobalValue(std::move(spv_inst)); + } else if (opcode == SpvOpExtInst && + spvExtInstIsDebugInfo(inst->ext_inst_type)) { + module_->AddExtInstDebugInfo(std::move(spv_inst)); + } else if (opcode == SpvOpExtInst && + spvExtInstIsNonSemantic(inst->ext_inst_type)) { + // If there are no functions, add the non-semantic instructions to the + // global values. Otherwise append it to the list of the last function. + auto func_begin = module_->begin(); + auto func_end = module_->end(); + if (func_begin == func_end) { + module_->AddGlobalValue(std::move(spv_inst)); + } else { + (--func_end)->AddNonSemanticInstruction(std::move(spv_inst)); + } + } else { + Errorf(consumer_, src, loc, + "Unhandled inst type (opcode: %d) found outside function " + "definition.", + opcode); + return false; + } + } else { + if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) + last_dbg_scope_ = DebugScope(kNoDebugScope, kNoInlinedAt); + if (last_dbg_scope_.GetLexicalScope() != kNoDebugScope) + spv_inst->SetDebugScope(last_dbg_scope_); + if (opcode == SpvOpExtInst && + spvExtInstIsDebugInfo(inst->ext_inst_type)) { + const uint32_t ext_inst_index = inst->words[kExtInstSetIndex]; + if (inst->ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case OpenCLDebugInfo100DebugDeclare: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + case OpenCLDebugInfo100DebugValue: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + default: { + Errorf(consumer_, src, loc, + "Debug info extension instruction other than DebugScope, " + "DebugNoScope, DebugDeclare, and DebugValue found inside " + "function", + opcode); + return false; + } + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + switch (ext_inst_key) { + case DebugInfoDebugDeclare: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + case DebugInfoDebugValue: { + if (block_ == nullptr) // Inside function but outside blocks + function_->AddDebugInstructionInHeader(std::move(spv_inst)); + else + block_->AddInstruction(std::move(spv_inst)); + break; + } + default: { + Errorf(consumer_, src, loc, + "Debug info extension instruction other than DebugScope, " + "DebugNoScope, DebugDeclare, and DebugValue found inside " + "function", + opcode); + return false; + } + } + } + } else { + if (block_ == nullptr) { // Inside function but outside blocks + if (opcode != SpvOpFunctionParameter) { + Errorf(consumer_, src, loc, + "Non-OpFunctionParameter (opcode: %d) found inside " + "function but outside basic block", + opcode); + return false; + } + function_->AddParameter(std::move(spv_inst)); + } else { + block_->AddInstruction(std::move(spv_inst)); + } + } + } + } + return true; +} + +// Resolves internal references among the module, functions, basic blocks, etc. +// This function should be called after adding all instructions. +void IrLoader::EndModule() { + if (block_ && function_) { + // We're in the middle of a basic block, but the terminator is missing. + // Register the block anyway. This lets us write tests with less + // boilerplate. + function_->AddBasicBlock(std::move(block_)); + block_ = nullptr; + } + if (function_) { + // We're in the middle of a function, but the OpFunctionEnd is missing. + // Register the function anyway. This lets us write tests with less + // boilerplate. + module_->AddFunction(std::move(function_)); + function_ = nullptr; + } + for (auto& function : *module_) { + for (auto& bb : function) bb.SetParent(&function); + } + + // Copy any trailing Op*Line instruction into the module + module_->SetTrailingDbgLineInfo(std::move(dbg_line_info_)); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/ir_loader.h b/third_party/spirv-tools/source/opt/ir_loader.h new file mode 100644 index 0000000..d0610f1 --- /dev/null +++ b/third_party/spirv-tools/source/opt/ir_loader.h @@ -0,0 +1,91 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_IR_LOADER_H_ +#define SOURCE_OPT_IR_LOADER_H_ + +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { + +// Loader class for constructing SPIR-V in-memory IR representation. Methods in +// this class are designed to work with the interface for spvBinaryParse() in +// libspirv.h so that we can leverage the syntax checks implemented behind it. +// +// The user is expected to call SetModuleHeader() to fill in the module's +// header, and then AddInstruction() for each decoded instruction, and finally +// EndModule() to finalize the module. The instructions processed in sequence +// by AddInstruction() should comprise a valid SPIR-V module. +class IrLoader { + public: + // Instantiates a builder to construct the given |module| gradually. + // All internal messages will be communicated to the outside via the given + // message |consumer|. This instance only keeps a reference to the |consumer|, + // so the |consumer| should outlive this instance. + IrLoader(const MessageConsumer& consumer, Module* m); + + // Sets the source name of the module. + void SetSource(const std::string& src) { source_ = src; } + + Module* module() const { return module_; } + + // Sets the fields in the module's header to the given parameters. + void SetModuleHeader(uint32_t magic, uint32_t version, uint32_t generator, + uint32_t bound, uint32_t reserved) { + module_->SetHeader({magic, version, generator, bound, reserved}); + } + // Adds an instruction to the module. Returns true if no error occurs. This + // method will properly capture and store the data provided in |inst| so that + // |inst| is no longer needed after returning. + bool AddInstruction(const spv_parsed_instruction_t* inst); + // Finalizes the module construction. This must be called after the module + // header has been set and all instructions have been added. This is + // forgiving in the case of a missing terminator instruction on a basic block, + // or a missing OpFunctionEnd. Resolves internal bookkeeping. + void EndModule(); + + private: + // Consumer for communicating messages to outside. + const MessageConsumer& consumer_; + // The module to be built. + Module* module_; + // The source name of the module. + std::string source_; + // The last used instruction index. + uint32_t inst_index_; + // The current Function under construction. + std::unique_ptr function_; + // The current BasicBlock under construction. + std::unique_ptr block_; + // Line related debug instructions accumulated thus far. + std::vector dbg_line_info_; + // Line instruction that should be applied to the next instruction. + std::unique_ptr last_line_inst_; + + // The last DebugScope information that IrLoader::AddInstruction() handled. + DebugScope last_dbg_scope_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_IR_LOADER_H_ diff --git a/third_party/spirv-tools/source/opt/iterator.h b/third_party/spirv-tools/source/opt/iterator.h new file mode 100644 index 0000000..444d457 --- /dev/null +++ b/third_party/spirv-tools/source/opt/iterator.h @@ -0,0 +1,358 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_ITERATOR_H_ +#define SOURCE_OPT_ITERATOR_H_ + +#include // for ptrdiff_t +#include +#include +#include +#include +#include + +namespace spvtools { +namespace opt { + +// An ad hoc iterator class for std::vector>. The +// purpose of this iterator class is to provide transparent access to those +// std::unique_ptr managed elements in the vector, behaving like we are using +// std::vector<|ValueType|>. +template +class UptrVectorIterator + : public std::iterator::type> { + public: + using super = std::iterator< + std::random_access_iterator_tag, + typename std::conditional::type>; + + using pointer = typename super::pointer; + using reference = typename super::reference; + using difference_type = typename super::difference_type; + + // Type aliases. We need to apply constness properly if |IsConst| is true. + using Uptr = std::unique_ptr; + using UptrVector = typename std::conditional, + std::vector>::type; + using UnderlyingIterator = + typename std::conditional::type; + + // Creates a new iterator from the given |container| and its raw iterator + // |it|. + UptrVectorIterator(UptrVector* container, const UnderlyingIterator& it) + : container_(container), iterator_(it) {} + UptrVectorIterator(const UptrVectorIterator&) = default; + UptrVectorIterator& operator=(const UptrVectorIterator&) = default; + + inline UptrVectorIterator& operator++(); + inline UptrVectorIterator operator++(int); + inline UptrVectorIterator& operator--(); + inline UptrVectorIterator operator--(int); + + reference operator*() const { return **iterator_; } + pointer operator->() { return (*iterator_).get(); } + reference operator[](ptrdiff_t index) { return **(iterator_ + index); } + + inline bool operator==(const UptrVectorIterator& that) const; + inline bool operator!=(const UptrVectorIterator& that) const; + + inline ptrdiff_t operator-(const UptrVectorIterator& that) const; + inline bool operator<(const UptrVectorIterator& that) const; + + // Inserts the given |value| to the position pointed to by this iterator + // and returns an iterator to the newly iserted |value|. + // If the underlying vector changes capacity, all previous iterators will be + // invalidated. Otherwise, those previous iterators pointing to after the + // insertion point will be invalidated. + template + inline typename std::enable_if::type + InsertBefore(Uptr value); + + // Inserts the given |valueVector| to the position pointed to by this iterator + // and returns an iterator to the first newly inserted value. + // If the underlying vector changes capacity, all previous iterators will be + // invalidated. Otherwise, those previous iterators pointing to after the + // insertion point will be invalidated. + template + inline typename std::enable_if::type + InsertBefore(UptrVector* valueVector); + + // Erases the value at the position pointed to by this iterator + // and returns an iterator to the following value. + // If the underlying vector changes capacity, all previous iterators will be + // invalidated. Otherwise, those previous iterators pointing to after the + // erasure point will be invalidated. + template + inline typename std::enable_if::type + Erase(); + + // Returns the underlying iterator. + UnderlyingIterator Get() const { return iterator_; } + + // Returns a valid end iterator for the underlying container. + UptrVectorIterator End() const { + return UptrVectorIterator(container_, container_->end()); + } + + private: + UptrVector* container_; // The container we are manipulating. + UnderlyingIterator iterator_; // The raw iterator from the container. +}; + +// Handy class for a (begin, end) iterator pair. +template +class IteratorRange { + public: + IteratorRange(const IteratorType& b, const IteratorType& e) + : begin_(b), end_(e) {} + IteratorRange(IteratorType&& b, IteratorType&& e) + : begin_(std::move(b)), end_(std::move(e)) {} + + IteratorType begin() const { return begin_; } + IteratorType end() const { return end_; } + + bool empty() const { return begin_ == end_; } + size_t size() const { return end_ - begin_; } + + private: + IteratorType begin_; + IteratorType end_; +}; + +// Returns a (begin, end) iterator pair for the given iterators. +// The iterators must belong to the same container. +template +inline IteratorRange make_range(const IteratorType& begin, + const IteratorType& end) { + return {begin, end}; +} + +// Returns a (begin, end) iterator pair for the given iterators. +// The iterators must belong to the same container. +template +inline IteratorRange make_range(IteratorType&& begin, + IteratorType&& end) { + return {std::move(begin), std::move(end)}; +} + +// Returns a (begin, end) iterator pair for the given container. +template > +inline IteratorRange make_range( + std::vector>& container) { + return {IteratorType(&container, container.begin()), + IteratorType(&container, container.end())}; +} + +// Returns a const (begin, end) iterator pair for the given container. +template > +inline IteratorRange make_const_range( + const std::vector>& container) { + return {IteratorType(&container, container.cbegin()), + IteratorType(&container, container.cend())}; +} + +// Wrapping iterator class that only consider elements that satisfy the given +// predicate |Predicate|. When moving to the next element of the iterator, the +// FilterIterator will iterate over the range until it finds an element that +// satisfies |Predicate| or reaches the end of the iterator. +// +// Currently this iterator is always an input iterator. +template +class FilterIterator + : public std::iterator< + std::input_iterator_tag, typename SubIterator::value_type, + typename SubIterator::difference_type, typename SubIterator::pointer, + typename SubIterator::reference> { + public: + // Iterator interface. + using iterator_category = typename SubIterator::iterator_category; + using value_type = typename SubIterator::value_type; + using pointer = typename SubIterator::pointer; + using reference = typename SubIterator::reference; + using difference_type = typename SubIterator::difference_type; + + using Range = IteratorRange; + + FilterIterator(const IteratorRange& iteration_range, + Predicate predicate) + : cur_(iteration_range.begin()), + end_(iteration_range.end()), + predicate_(predicate) { + if (!IsPredicateSatisfied()) { + MoveToNextPosition(); + } + } + + FilterIterator(const SubIterator& end, Predicate predicate) + : FilterIterator({end, end}, predicate) {} + + inline FilterIterator& operator++() { + MoveToNextPosition(); + return *this; + } + inline FilterIterator operator++(int) { + FilterIterator old = *this; + MoveToNextPosition(); + return old; + } + + reference operator*() const { return *cur_; } + pointer operator->() { return &*cur_; } + + inline bool operator==(const FilterIterator& rhs) const { + return cur_ == rhs.cur_ && end_ == rhs.end_; + } + inline bool operator!=(const FilterIterator& rhs) const { + return !(*this == rhs); + } + + // Returns the underlying iterator. + SubIterator Get() const { return cur_; } + + // Returns the sentinel iterator. + FilterIterator GetEnd() const { return FilterIterator(end_, predicate_); } + + private: + // Returns true if the predicate is satisfied or the current iterator reached + // the end. + bool IsPredicateSatisfied() { return cur_ == end_ || predicate_(*cur_); } + + void MoveToNextPosition() { + if (cur_ == end_) return; + + do { + ++cur_; + } while (!IsPredicateSatisfied()); + } + + SubIterator cur_; + SubIterator end_; + Predicate predicate_; +}; + +template +FilterIterator MakeFilterIterator( + const IteratorRange& sub_iterator_range, Predicate predicate) { + return FilterIterator(sub_iterator_range, predicate); +} + +template +FilterIterator MakeFilterIterator( + const SubIterator& begin, const SubIterator& end, Predicate predicate) { + return MakeFilterIterator(make_range(begin, end), predicate); +} + +template +typename FilterIterator::Range MakeFilterIteratorRange( + const SubIterator& begin, const SubIterator& end, Predicate predicate) { + return typename FilterIterator::Range( + MakeFilterIterator(begin, end, predicate), + MakeFilterIterator(end, end, predicate)); +} + +template +inline UptrVectorIterator& UptrVectorIterator::operator++() { + ++iterator_; + return *this; +} + +template +inline UptrVectorIterator UptrVectorIterator::operator++(int) { + auto it = *this; + ++(*this); + return it; +} + +template +inline UptrVectorIterator& UptrVectorIterator::operator--() { + --iterator_; + return *this; +} + +template +inline UptrVectorIterator UptrVectorIterator::operator--(int) { + auto it = *this; + --(*this); + return it; +} + +template +inline bool UptrVectorIterator::operator==( + const UptrVectorIterator& that) const { + return container_ == that.container_ && iterator_ == that.iterator_; +} + +template +inline bool UptrVectorIterator::operator!=( + const UptrVectorIterator& that) const { + return !(*this == that); +} + +template +inline ptrdiff_t UptrVectorIterator::operator-( + const UptrVectorIterator& that) const { + assert(container_ == that.container_); + return iterator_ - that.iterator_; +} + +template +inline bool UptrVectorIterator::operator<( + const UptrVectorIterator& that) const { + assert(container_ == that.container_); + return iterator_ < that.iterator_; +} + +template +template +inline + typename std::enable_if>::type + UptrVectorIterator::InsertBefore(Uptr value) { + auto index = iterator_ - container_->begin(); + container_->insert(iterator_, std::move(value)); + return UptrVectorIterator(container_, container_->begin() + index); +} + +template +template +inline + typename std::enable_if>::type + UptrVectorIterator::InsertBefore(UptrVector* values) { + const auto pos = iterator_ - container_->begin(); + const auto origsz = container_->size(); + container_->resize(origsz + values->size()); + std::move_backward(container_->begin() + pos, container_->begin() + origsz, + container_->end()); + std::move(values->begin(), values->end(), container_->begin() + pos); + return UptrVectorIterator(container_, container_->begin() + pos); +} + +template +template +inline + typename std::enable_if>::type + UptrVectorIterator::Erase() { + auto index = iterator_ - container_->begin(); + (void)container_->erase(iterator_); + return UptrVectorIterator(container_, container_->begin() + index); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_ITERATOR_H_ diff --git a/third_party/spirv-tools/source/opt/legalize_vector_shuffle_pass.cpp b/third_party/spirv-tools/source/opt/legalize_vector_shuffle_pass.cpp new file mode 100644 index 0000000..b5d5d59 --- /dev/null +++ b/third_party/spirv-tools/source/opt/legalize_vector_shuffle_pass.cpp @@ -0,0 +1,39 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/legalize_vector_shuffle_pass.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status LegalizeVectorShufflePass::Process() { + bool changed = false; + context()->module()->ForEachInst([&changed](Instruction* inst) { + if (inst->opcode() != SpvOpVectorShuffle) return; + + for (uint32_t idx = 2; idx < inst->NumInOperands(); ++idx) { + auto literal = inst->GetSingleWordInOperand(idx); + if (literal != 0xFFFFFFFF) continue; + changed = true; + inst->SetInOperand(idx, {0}); + } + }); + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/legalize_vector_shuffle_pass.h b/third_party/spirv-tools/source/opt/legalize_vector_shuffle_pass.h new file mode 100644 index 0000000..ca6e1df --- /dev/null +++ b/third_party/spirv-tools/source/opt/legalize_vector_shuffle_pass.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LEGALIZE_VECTOR_SHUFFLE_PASS_H_ +#define SOURCE_OPT_LEGALIZE_VECTOR_SHUFFLE_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Converts any usages of 0xFFFFFFFF for the literals in OpVectorShuffle to a +// literal 0. This is needed because using OxFFFFFFFF is forbidden by the WebGPU +// spec. 0xFFFFFFFF in the main spec indicates that the result for this +// component has no source, thus is undefined. Since this is undefined +// behaviour we are free to use 0. +class LegalizeVectorShufflePass : public Pass { + public: + const char* name() const override { return "legalize-vector-shuffle"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LEGALIZE_VECTOR_SHUFFLE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/licm_pass.cpp b/third_party/spirv-tools/source/opt/licm_pass.cpp new file mode 100644 index 0000000..82851fd --- /dev/null +++ b/third_party/spirv-tools/source/opt/licm_pass.cpp @@ -0,0 +1,140 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/licm_pass.h" + +#include +#include + +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +Pass::Status LICMPass::Process() { return ProcessIRContext(); } + +Pass::Status LICMPass::ProcessIRContext() { + Status status = Status::SuccessWithoutChange; + Module* module = get_module(); + + // Process each function in the module + for (auto func = module->begin(); + func != module->end() && status != Status::Failure; ++func) { + status = CombineStatus(status, ProcessFunction(&*func)); + } + return status; +} + +Pass::Status LICMPass::ProcessFunction(Function* f) { + Status status = Status::SuccessWithoutChange; + LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f); + + // Process each loop in the function + for (auto it = loop_descriptor->begin(); + it != loop_descriptor->end() && status != Status::Failure; ++it) { + Loop& loop = *it; + // Ignore nested loops, as we will process them in order in ProcessLoop + if (loop.IsNested()) { + continue; + } + status = CombineStatus(status, ProcessLoop(&loop, f)); + } + return status; +} + +Pass::Status LICMPass::ProcessLoop(Loop* loop, Function* f) { + Status status = Status::SuccessWithoutChange; + + // Process all nested loops first + for (auto nl = loop->begin(); nl != loop->end() && status != Status::Failure; + ++nl) { + Loop* nested_loop = *nl; + status = CombineStatus(status, ProcessLoop(nested_loop, f)); + } + + std::vector loop_bbs{}; + status = CombineStatus( + status, + AnalyseAndHoistFromBB(loop, f, loop->GetHeaderBlock(), &loop_bbs)); + + for (size_t i = 0; i < loop_bbs.size() && status != Status::Failure; ++i) { + BasicBlock* bb = loop_bbs[i]; + // do not delete the element + status = + CombineStatus(status, AnalyseAndHoistFromBB(loop, f, bb, &loop_bbs)); + } + + return status; +} + +Pass::Status LICMPass::AnalyseAndHoistFromBB( + Loop* loop, Function* f, BasicBlock* bb, + std::vector* loop_bbs) { + bool modified = false; + std::function hoist_inst = + [this, &loop, &modified](Instruction* inst) { + if (loop->ShouldHoistInstruction(this->context(), inst)) { + if (!HoistInstruction(loop, inst)) { + return false; + } + modified = true; + } + return true; + }; + + if (IsImmediatelyContainedInLoop(loop, f, bb)) { + if (!bb->WhileEachInst(hoist_inst, false)) { + return Status::Failure; + } + } + + DominatorAnalysis* dom_analysis = context()->GetDominatorAnalysis(f); + DominatorTree& dom_tree = dom_analysis->GetDomTree(); + + for (DominatorTreeNode* child_dom_tree_node : *dom_tree.GetTreeNode(bb)) { + if (loop->IsInsideLoop(child_dom_tree_node->bb_)) { + loop_bbs->push_back(child_dom_tree_node->bb_); + } + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool LICMPass::IsImmediatelyContainedInLoop(Loop* loop, Function* f, + BasicBlock* bb) { + LoopDescriptor* loop_descriptor = context()->GetLoopDescriptor(f); + return loop == (*loop_descriptor)[bb->id()]; +} + +bool LICMPass::HoistInstruction(Loop* loop, Instruction* inst) { + // TODO(1841): Handle failure to create pre-header. + BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock(); + if (!pre_header_bb) { + return false; + } + Instruction* insertion_point = &*pre_header_bb->tail(); + Instruction* previous_node = insertion_point->PreviousNode(); + if (previous_node && (previous_node->opcode() == SpvOpLoopMerge || + previous_node->opcode() == SpvOpSelectionMerge)) { + insertion_point = previous_node; + } + + inst->InsertBefore(insertion_point); + context()->set_instr_block(inst, pre_header_bb); + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/licm_pass.h b/third_party/spirv-tools/source/opt/licm_pass.h new file mode 100644 index 0000000..597fe92 --- /dev/null +++ b/third_party/spirv-tools/source/opt/licm_pass.h @@ -0,0 +1,72 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LICM_PASS_H_ +#define SOURCE_OPT_LICM_PASS_H_ + +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class LICMPass : public Pass { + public: + LICMPass() {} + + const char* name() const override { return "loop-invariant-code-motion"; } + Status Process() override; + + private: + // Searches the IRContext for functions and processes each, moving invariants + // outside loops within the function where possible. + // Returns the status depending on whether or not there was a failure or + // change. + Pass::Status ProcessIRContext(); + + // Checks the function for loops, calling ProcessLoop on each one found. + // Returns the status depending on whether or not there was a failure or + // change. + Pass::Status ProcessFunction(Function* f); + + // Checks for invariants in the loop and attempts to move them to the loops + // preheader. Works from inner loop to outer when nested loops are found. + // Returns the status depending on whether or not there was a failure or + // change. + Pass::Status ProcessLoop(Loop* loop, Function* f); + + // Analyses each instruction in |bb|, hoisting invariants to |pre_header_bb|. + // Each child of |bb| wrt to |dom_tree| is pushed to |loop_bbs| + // Returns the status depending on whether or not there was a failure or + // change. + Pass::Status AnalyseAndHoistFromBB(Loop* loop, Function* f, BasicBlock* bb, + std::vector* loop_bbs); + + // Returns true if |bb| is immediately contained in |loop| + bool IsImmediatelyContainedInLoop(Loop* loop, Function* f, BasicBlock* bb); + + // Move the instruction to the preheader of |loop|. + // This method will update the instruction to block mapping for the context + bool HoistInstruction(Loop* loop, Instruction* inst); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LICM_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.cpp b/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.cpp new file mode 100644 index 0000000..205cd7a --- /dev/null +++ b/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.cpp @@ -0,0 +1,425 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/local_access_chain_convert_pass.h" + +#include "ir_builder.h" +#include "ir_context.h" +#include "iterator.h" + +namespace spvtools { +namespace opt { + +namespace { + +const uint32_t kStoreValIdInIdx = 1; +const uint32_t kAccessChainPtrIdInIdx = 0; +const uint32_t kConstantValueInIdx = 0; +const uint32_t kTypeIntWidthInIdx = 0; + +} // anonymous namespace + +void LocalAccessChainConvertPass::BuildAndAppendInst( + SpvOp opcode, uint32_t typeId, uint32_t resultId, + const std::vector& in_opnds, + std::vector>* newInsts) { + std::unique_ptr newInst( + new Instruction(context(), opcode, typeId, resultId, in_opnds)); + get_def_use_mgr()->AnalyzeInstDefUse(&*newInst); + newInsts->emplace_back(std::move(newInst)); +} + +uint32_t LocalAccessChainConvertPass::BuildAndAppendVarLoad( + const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId, + std::vector>* newInsts) { + const uint32_t ldResultId = TakeNextId(); + if (ldResultId == 0) { + return 0; + } + + *varId = ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx); + const Instruction* varInst = get_def_use_mgr()->GetDef(*varId); + assert(varInst->opcode() == SpvOpVariable); + *varPteTypeId = GetPointeeTypeId(varInst); + BuildAndAppendInst(SpvOpLoad, *varPteTypeId, ldResultId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {*varId}}}, + newInsts); + return ldResultId; +} + +void LocalAccessChainConvertPass::AppendConstantOperands( + const Instruction* ptrInst, std::vector* in_opnds) { + uint32_t iidIdx = 0; + ptrInst->ForEachInId([&iidIdx, &in_opnds, this](const uint32_t* iid) { + if (iidIdx > 0) { + const Instruction* cInst = get_def_use_mgr()->GetDef(*iid); + uint32_t val = cInst->GetSingleWordInOperand(kConstantValueInIdx); + in_opnds->push_back( + {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {val}}); + } + ++iidIdx; + }); +} + +bool LocalAccessChainConvertPass::ReplaceAccessChainLoad( + const Instruction* address_inst, Instruction* original_load) { + // Build and append load of variable in ptrInst + if (address_inst->NumInOperands() == 1) { + // An access chain with no indices is essentially a copy. All that is + // needed is to propagate the address. + context()->ReplaceAllUsesWith( + address_inst->result_id(), + address_inst->GetSingleWordInOperand(kAccessChainPtrIdInIdx)); + return true; + } + + std::vector> new_inst; + uint32_t varId; + uint32_t varPteTypeId; + const uint32_t ldResultId = + BuildAndAppendVarLoad(address_inst, &varId, &varPteTypeId, &new_inst); + if (ldResultId == 0) { + return false; + } + + new_inst[0]->UpdateDebugInfoFrom(original_load); + context()->get_decoration_mgr()->CloneDecorations( + original_load->result_id(), ldResultId, {SpvDecorationRelaxedPrecision}); + original_load->InsertBefore(std::move(new_inst)); + context()->get_debug_info_mgr()->AnalyzeDebugInst( + original_load->PreviousNode()); + + // Rewrite |original_load| into an extract. + Instruction::OperandList new_operands; + + // copy the result id and the type id to the new operand list. + new_operands.emplace_back(original_load->GetOperand(0)); + new_operands.emplace_back(original_load->GetOperand(1)); + + new_operands.emplace_back( + Operand({spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}})); + AppendConstantOperands(address_inst, &new_operands); + original_load->SetOpcode(SpvOpCompositeExtract); + original_load->ReplaceOperands(new_operands); + context()->UpdateDefUse(original_load); + return true; +} + +bool LocalAccessChainConvertPass::GenAccessChainStoreReplacement( + const Instruction* ptrInst, uint32_t valId, + std::vector>* newInsts) { + if (ptrInst->NumInOperands() == 1) { + // An access chain with no indices is essentially a copy. However, we still + // have to create a new store because the old ones will be deleted. + BuildAndAppendInst( + SpvOpStore, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {ptrInst->GetSingleWordInOperand(kAccessChainPtrIdInIdx)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}}, + newInsts); + return true; + } + + // Build and append load of variable in ptrInst + uint32_t varId; + uint32_t varPteTypeId; + const uint32_t ldResultId = + BuildAndAppendVarLoad(ptrInst, &varId, &varPteTypeId, newInsts); + if (ldResultId == 0) { + return false; + } + + context()->get_decoration_mgr()->CloneDecorations( + varId, ldResultId, {SpvDecorationRelaxedPrecision}); + + // Build and append Insert + const uint32_t insResultId = TakeNextId(); + if (insResultId == 0) { + return false; + } + std::vector ins_in_opnds = { + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {valId}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {ldResultId}}}; + AppendConstantOperands(ptrInst, &ins_in_opnds); + BuildAndAppendInst(SpvOpCompositeInsert, varPteTypeId, insResultId, + ins_in_opnds, newInsts); + + context()->get_decoration_mgr()->CloneDecorations( + varId, insResultId, {SpvDecorationRelaxedPrecision}); + + // Build and append Store + BuildAndAppendInst(SpvOpStore, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, {varId}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {insResultId}}}, + newInsts); + return true; +} + +bool LocalAccessChainConvertPass::IsConstantIndexAccessChain( + const Instruction* acp) const { + uint32_t inIdx = 0; + return acp->WhileEachInId([&inIdx, this](const uint32_t* tid) { + if (inIdx > 0) { + Instruction* opInst = get_def_use_mgr()->GetDef(*tid); + if (opInst->opcode() != SpvOpConstant) return false; + } + ++inIdx; + return true; + }); +} + +bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) { + if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; + if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { + if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue || + user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + return true; + } + SpvOp op = user->opcode(); + if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) { + if (!HasOnlySupportedRefs(user->result_id())) { + return false; + } + } else if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName && + !IsNonTypeDecorate(op)) { + return false; + } + return true; + })) { + supported_ref_ptrs_.insert(ptrId); + return true; + } + return false; +} + +void LocalAccessChainConvertPass::FindTargetVars(Function* func) { + for (auto bi = func->begin(); bi != func->end(); ++bi) { + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + switch (ii->opcode()) { + case SpvOpStore: + case SpvOpLoad: { + uint32_t varId; + Instruction* ptrInst = GetPtr(&*ii, &varId); + if (!IsTargetVar(varId)) break; + const SpvOp op = ptrInst->opcode(); + // Rule out variables with non-supported refs eg function calls + if (!HasOnlySupportedRefs(varId)) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } + // Rule out variables with nested access chains + // TODO(): Convert nested access chains + if (IsNonPtrAccessChain(op) && ptrInst->GetSingleWordInOperand( + kAccessChainPtrIdInIdx) != varId) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } + // Rule out variables accessed with non-constant indices + if (!IsConstantIndexAccessChain(ptrInst)) { + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + break; + } + } break; + default: + break; + } + } + } +} + +Pass::Status LocalAccessChainConvertPass::ConvertLocalAccessChains( + Function* func) { + FindTargetVars(func); + // Replace access chains of all targeted variables with equivalent + // extract and insert sequences + bool modified = false; + for (auto bi = func->begin(); bi != func->end(); ++bi) { + std::vector dead_instructions; + for (auto ii = bi->begin(); ii != bi->end(); ++ii) { + switch (ii->opcode()) { + case SpvOpLoad: { + uint32_t varId; + Instruction* ptrInst = GetPtr(&*ii, &varId); + if (!IsNonPtrAccessChain(ptrInst->opcode())) break; + if (!IsTargetVar(varId)) break; + if (!ReplaceAccessChainLoad(ptrInst, &*ii)) { + return Status::Failure; + } + modified = true; + } break; + case SpvOpStore: { + uint32_t varId; + Instruction* store = &*ii; + Instruction* ptrInst = GetPtr(store, &varId); + if (!IsNonPtrAccessChain(ptrInst->opcode())) break; + if (!IsTargetVar(varId)) break; + std::vector> newInsts; + uint32_t valId = store->GetSingleWordInOperand(kStoreValIdInIdx); + if (!GenAccessChainStoreReplacement(ptrInst, valId, &newInsts)) { + return Status::Failure; + } + size_t num_of_instructions_to_skip = newInsts.size() - 1; + dead_instructions.push_back(store); + ++ii; + ii = ii.InsertBefore(std::move(newInsts)); + for (size_t i = 0; i < num_of_instructions_to_skip; ++i) { + ii->UpdateDebugInfoFrom(store); + context()->get_debug_info_mgr()->AnalyzeDebugInst(&*ii); + ++ii; + } + ii->UpdateDebugInfoFrom(store); + context()->get_debug_info_mgr()->AnalyzeDebugInst(&*ii); + modified = true; + } break; + default: + break; + } + } + + while (!dead_instructions.empty()) { + Instruction* inst = dead_instructions.back(); + dead_instructions.pop_back(); + DCEInst(inst, [&dead_instructions](Instruction* other_inst) { + auto i = std::find(dead_instructions.begin(), dead_instructions.end(), + other_inst); + if (i != dead_instructions.end()) { + dead_instructions.erase(i); + } + }); + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void LocalAccessChainConvertPass::Initialize() { + // Initialize Target Variable Caches + seen_target_vars_.clear(); + seen_non_target_vars_.clear(); + + // Initialize collections + supported_ref_ptrs_.clear(); + + // Initialize extension allowlist + InitExtensions(); +} + +bool LocalAccessChainConvertPass::AllExtensionsSupported() const { + // This capability can now exist without the extension, so we have to check + // for the capability. This pass is only looking at function scope symbols, + // so we do not care if there are variable pointers on storage buffers. + if (context()->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers)) + return false; + // If any extension not in allowlist, return false + for (auto& ei : get_module()->extensions()) { + const char* extName = + reinterpret_cast(&ei.GetInOperand(0).words[0]); + if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) + return false; + } + return true; +} + +Pass::Status LocalAccessChainConvertPass::ProcessImpl() { + // If non-32-bit integer type in module, terminate processing + // TODO(): Handle non-32-bit integer constants in access chains + for (const Instruction& inst : get_module()->types_values()) + if (inst.opcode() == SpvOpTypeInt && + inst.GetSingleWordInOperand(kTypeIntWidthInIdx) != 32) + return Status::SuccessWithoutChange; + // Do not process if module contains OpGroupDecorate. Additional + // support required in KillNamesAndDecorates(). + // TODO(greg-lunarg): Add support for OpGroupDecorate + for (auto& ai : get_module()->annotations()) + if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange; + // Do not process if any disallowed extensions are enabled + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; + + // Process all functions in the module. + Status status = Status::SuccessWithoutChange; + for (Function& func : *get_module()) { + status = CombineStatus(status, ConvertLocalAccessChains(&func)); + if (status == Status::Failure) { + break; + } + } + return status; +} + +LocalAccessChainConvertPass::LocalAccessChainConvertPass() {} + +Pass::Status LocalAccessChainConvertPass::Process() { + Initialize(); + return ProcessImpl(); +} + +void LocalAccessChainConvertPass::InitExtensions() { + extensions_allowlist_.clear(); + extensions_allowlist_.insert({ + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + // SPV_KHR_variable_pointers + // Currently do not support extended pointer expressions + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", + "SPV_EXT_descriptor_indexing", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_KHR_terminate_invocation", + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.h b/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.h new file mode 100644 index 0000000..552062e --- /dev/null +++ b/third_party/spirv-tools/source/opt/local_access_chain_convert_pass.h @@ -0,0 +1,133 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ +#define SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class LocalAccessChainConvertPass : public MemPass { + public: + LocalAccessChainConvertPass(); + + const char* name() const override { return "convert-local-access-chains"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + using ProcessFunction = std::function; + + private: + // Return true if all refs through |ptrId| are only loads or stores and + // cache ptrId in supported_ref_ptrs_. TODO(dnovillo): This function is + // replicated in other passes and it's slightly different in every pass. Is it + // possible to make one common implementation? + bool HasOnlySupportedRefs(uint32_t ptrId); + + // Search |func| and cache function scope variables of target type that are + // not accessed with non-constant-index access chains. Also cache non-target + // variables. + void FindTargetVars(Function* func); + + // Build instruction from |opcode|, |typeId|, |resultId|, and |in_opnds|. + // Append to |newInsts|. + void BuildAndAppendInst(SpvOp opcode, uint32_t typeId, uint32_t resultId, + const std::vector& in_opnds, + std::vector>* newInsts); + + // Build load of variable in |ptrInst| and append to |newInsts|. + // Return var in |varId| and its pointee type in |varPteTypeId|. + uint32_t BuildAndAppendVarLoad( + const Instruction* ptrInst, uint32_t* varId, uint32_t* varPteTypeId, + std::vector>* newInsts); + + // Append literal integer operands to |in_opnds| corresponding to constant + // integer operands from access chain |ptrInst|. Assumes all indices in + // access chains are OpConstant. + void AppendConstantOperands(const Instruction* ptrInst, + std::vector* in_opnds); + + // Create a load/insert/store equivalent to a store of + // |valId| through (constant index) access chaing |ptrInst|. + // Append to |newInsts|. Returns true if successful. + bool GenAccessChainStoreReplacement( + const Instruction* ptrInst, uint32_t valId, + std::vector>* newInsts); + + // For the (constant index) access chain |address_inst|, create an + // equivalent load and extract that replaces |original_load|. The result id + // of the extract will be the same as the original result id of + // |original_load|. Returns true if successful. + bool ReplaceAccessChainLoad(const Instruction* address_inst, + Instruction* original_load); + + // Return true if all indices of access chain |acp| are OpConstant integers + bool IsConstantIndexAccessChain(const Instruction* acp) const; + + // Identify all function scope variables of target type which are + // accessed only with loads, stores and access chains with constant + // indices. Convert all loads and stores of such variables into equivalent + // loads, stores, extracts and inserts. This unifies access to these + // variables to a single mode and simplifies analysis and optimization. + // See IsTargetType() for targeted types. + // + // Nested access chains and pointer access chains are not currently + // converted. + // + // Returns a status to indicate success or failure, and change or no change. + Status ConvertLocalAccessChains(Function* func); + + // Initialize extensions allowlist + void InitExtensions(); + + // Return true if all extensions in this module are allowed by this pass. + bool AllExtensionsSupported() const; + + void Initialize(); + Pass::Status ProcessImpl(); + + // Variables with only supported references, ie. loads and stores using + // variable directly or through non-ptr access chains. + std::unordered_set supported_ref_ptrs_; + + // Extensions supported by this pass. + std::unordered_set extensions_allowlist_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOCAL_ACCESS_CHAIN_CONVERT_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/local_redundancy_elimination.cpp b/third_party/spirv-tools/source/opt/local_redundancy_elimination.cpp new file mode 100644 index 0000000..9539e65 --- /dev/null +++ b/third_party/spirv-tools/source/opt/local_redundancy_elimination.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/local_redundancy_elimination.h" + +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +Pass::Status LocalRedundancyEliminationPass::Process() { + bool modified = false; + ValueNumberTable vnTable(context()); + + for (auto& func : *get_module()) { + for (auto& bb : func) { + // Keeps track of all ids that contain a given value number. We keep + // track of multiple values because they could have the same value, but + // different decorations. + std::map value_to_ids; + if (EliminateRedundanciesInBB(&bb, vnTable, &value_to_ids)) + modified = true; + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool LocalRedundancyEliminationPass::EliminateRedundanciesInBB( + BasicBlock* block, const ValueNumberTable& vnTable, + std::map* value_to_ids) { + bool modified = false; + + auto func = [this, &vnTable, &modified, value_to_ids](Instruction* inst) { + if (inst->result_id() == 0) { + return; + } + + uint32_t value = vnTable.GetValueNumber(inst); + + if (value == 0) { + return; + } + + auto candidate = value_to_ids->insert({value, inst->result_id()}); + if (!candidate.second) { + context()->KillNamesAndDecorates(inst); + context()->ReplaceAllUsesWith(inst->result_id(), candidate.first->second); + context()->KillInst(inst); + modified = true; + } + }; + block->ForEachInst(func); + return modified; +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/local_redundancy_elimination.h b/third_party/spirv-tools/source/opt/local_redundancy_elimination.h new file mode 100644 index 0000000..770457a --- /dev/null +++ b/third_party/spirv-tools/source/opt/local_redundancy_elimination.h @@ -0,0 +1,68 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOCAL_REDUNDANCY_ELIMINATION_H_ +#define SOURCE_OPT_LOCAL_REDUNDANCY_ELIMINATION_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/pass.h" +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +// This pass implements local redundancy elimination. Its goal is to reduce the +// number of times the same value is computed. It works on each basic block +// independently, ie local. For each instruction in a basic block, it gets the +// value number for the result id, |id|, of the instruction. If that value +// number has already been computed in the basic block, it tries to replace the +// uses of |id| by the id that already contains the same value. Then the +// current instruction is deleted. +class LocalRedundancyEliminationPass : public Pass { + public: + const char* name() const override { return "local-redundancy-elimination"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + protected: + // Deletes instructions in |block| whose value is in |value_to_ids| or is + // computed earlier in |block|. + // + // |vnTable| must have computed a value number for every result id defined + // in |bb|. + // + // |value_to_ids| is a map from value number to ids. If {vn, id} is in + // |value_to_ids| then vn is the value number of id, and the definition of id + // dominates |bb|. + // + // Returns true if the module is changed. + bool EliminateRedundanciesInBB(BasicBlock* block, + const ValueNumberTable& vnTable, + std::map* value_to_ids); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOCAL_REDUNDANCY_ELIMINATION_H_ diff --git a/third_party/spirv-tools/source/opt/local_single_block_elim_pass.cpp b/third_party/spirv-tools/source/opt/local_single_block_elim_pass.cpp new file mode 100644 index 0000000..5f35ee1 --- /dev/null +++ b/third_party/spirv-tools/source/opt/local_single_block_elim_pass.cpp @@ -0,0 +1,277 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/local_single_block_elim_pass.h" + +#include + +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kStoreValIdInIdx = 1; + +} // anonymous namespace + +bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) { + if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true; + if (get_def_use_mgr()->WhileEachUser(ptrId, [this](Instruction* user) { + auto dbg_op = user->GetOpenCL100DebugOpcode(); + if (dbg_op == OpenCLDebugInfo100DebugDeclare || + dbg_op == OpenCLDebugInfo100DebugValue) { + return true; + } + SpvOp op = user->opcode(); + if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) { + if (!HasOnlySupportedRefs(user->result_id())) { + return false; + } + } else if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName && + !IsNonTypeDecorate(op)) { + return false; + } + return true; + })) { + supported_ref_ptrs_.insert(ptrId); + return true; + } + return false; +} + +bool LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElim( + Function* func) { + // Perform local store/load, load/load and store/store elimination + // on each block + bool modified = false; + std::vector instructions_to_kill; + std::unordered_set instructions_to_save; + for (auto bi = func->begin(); bi != func->end(); ++bi) { + var2store_.clear(); + var2load_.clear(); + auto next = bi->begin(); + for (auto ii = next; ii != bi->end(); ii = next) { + ++next; + switch (ii->opcode()) { + case SpvOpStore: { + // Verify store variable is target type + uint32_t varId; + Instruction* ptrInst = GetPtr(&*ii, &varId); + if (!IsTargetVar(varId)) continue; + if (!HasOnlySupportedRefs(varId)) continue; + // If a store to the whole variable, remember it for succeeding + // loads and stores. Otherwise forget any previous store to that + // variable. + if (ptrInst->opcode() == SpvOpVariable) { + // If a previous store to same variable, mark the store + // for deletion if not still used. Don't delete store + // if debugging; let ssa-rewrite and DCE handle it + auto prev_store = var2store_.find(varId); + if (prev_store != var2store_.end() && + instructions_to_save.count(prev_store->second) == 0 && + !context()->get_debug_info_mgr()->IsVariableDebugDeclared( + varId)) { + instructions_to_kill.push_back(prev_store->second); + modified = true; + } + + bool kill_store = false; + auto li = var2load_.find(varId); + if (li != var2load_.end()) { + if (ii->GetSingleWordInOperand(kStoreValIdInIdx) == + li->second->result_id()) { + // We are storing the same value that already exists in the + // memory location. The store does nothing. + kill_store = true; + } + } + + if (!kill_store) { + var2store_[varId] = &*ii; + var2load_.erase(varId); + } else { + instructions_to_kill.push_back(&*ii); + modified = true; + } + } else { + assert(IsNonPtrAccessChain(ptrInst->opcode())); + var2store_.erase(varId); + var2load_.erase(varId); + } + } break; + case SpvOpLoad: { + // Verify store variable is target type + uint32_t varId; + Instruction* ptrInst = GetPtr(&*ii, &varId); + if (!IsTargetVar(varId)) continue; + if (!HasOnlySupportedRefs(varId)) continue; + uint32_t replId = 0; + if (ptrInst->opcode() == SpvOpVariable) { + // If a load from a variable, look for a previous store or + // load from that variable and use its value. + auto si = var2store_.find(varId); + if (si != var2store_.end()) { + replId = si->second->GetSingleWordInOperand(kStoreValIdInIdx); + } else { + auto li = var2load_.find(varId); + if (li != var2load_.end()) { + replId = li->second->result_id(); + } + } + } else { + // If a partial load of a previously seen store, remember + // not to delete the store. + auto si = var2store_.find(varId); + if (si != var2store_.end()) instructions_to_save.insert(si->second); + } + if (replId != 0) { + // replace load's result id and delete load + context()->KillNamesAndDecorates(&*ii); + context()->ReplaceAllUsesWith(ii->result_id(), replId); + instructions_to_kill.push_back(&*ii); + modified = true; + } else { + if (ptrInst->opcode() == SpvOpVariable) + var2load_[varId] = &*ii; // register load + } + } break; + case SpvOpFunctionCall: { + // Conservatively assume all locals are redefined for now. + // TODO(): Handle more optimally + var2store_.clear(); + var2load_.clear(); + } break; + default: + break; + } + } + } + + for (Instruction* inst : instructions_to_kill) { + context()->KillInst(inst); + } + + return modified; +} + +void LocalSingleBlockLoadStoreElimPass::Initialize() { + // Initialize Target Type Caches + seen_target_vars_.clear(); + seen_non_target_vars_.clear(); + + // Clear collections + supported_ref_ptrs_.clear(); + + // Initialize extensions allowlist + InitExtensions(); +} + +bool LocalSingleBlockLoadStoreElimPass::AllExtensionsSupported() const { + // If any extension not in allowlist, return false + for (auto& ei : get_module()->extensions()) { + const char* extName = + reinterpret_cast(&ei.GetInOperand(0).words[0]); + if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) + return false; + } + return true; +} + +Pass::Status LocalSingleBlockLoadStoreElimPass::ProcessImpl() { + // Assumes relaxed logical addressing only (see instruction.h). + if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses)) + return Status::SuccessWithoutChange; + + // Do not process if module contains OpGroupDecorate. Additional + // support required in KillNamesAndDecorates(). + // TODO(greg-lunarg): Add support for OpGroupDecorate + for (auto& ai : get_module()->annotations()) + if (ai.opcode() == SpvOpGroupDecorate) return Status::SuccessWithoutChange; + // If any extensions in the module are not explicitly supported, + // return unmodified. + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; + // Process all entry point functions + ProcessFunction pfn = [this](Function* fp) { + return LocalSingleBlockLoadStoreElim(fp); + }; + + bool modified = context()->ProcessEntryPointCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +LocalSingleBlockLoadStoreElimPass::LocalSingleBlockLoadStoreElimPass() = + default; + +Pass::Status LocalSingleBlockLoadStoreElimPass::Process() { + Initialize(); + return ProcessImpl(); +} + +void LocalSingleBlockLoadStoreElimPass::InitExtensions() { + extensions_allowlist_.clear(); + extensions_allowlist_.insert({ + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers", + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_GOOGLE_user_type", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_demote_to_helper_invocation", + "SPV_EXT_descriptor_indexing", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_EXT_physical_storage_buffer", + "SPV_KHR_terminate_invocation", + }); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/local_single_block_elim_pass.h b/third_party/spirv-tools/source/opt/local_single_block_elim_pass.h new file mode 100644 index 0000000..ea72816 --- /dev/null +++ b/third_party/spirv-tools/source/opt/local_single_block_elim_pass.h @@ -0,0 +1,107 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_ +#define SOURCE_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class LocalSingleBlockLoadStoreElimPass : public MemPass { + public: + LocalSingleBlockLoadStoreElimPass(); + + const char* name() const override { return "eliminate-local-single-block"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Return true if all uses of |varId| are only through supported reference + // operations ie. loads and store. Also cache in supported_ref_ptrs_. + // TODO(dnovillo): This function is replicated in other passes and it's + // slightly different in every pass. Is it possible to make one common + // implementation? + bool HasOnlySupportedRefs(uint32_t varId); + + // On all entry point functions, within each basic block, eliminate + // loads and stores to function variables where possible. For + // loads, if previous load or store to same variable, replace + // load id with previous id and delete load. Finally, check if + // remaining stores are useless, and delete store and variable + // where possible. Assumes logical addressing. + bool LocalSingleBlockLoadStoreElim(Function* func); + + // Initialize extensions allowlist + void InitExtensions(); + + // Return true if all extensions in this module are supported by this pass. + bool AllExtensionsSupported() const; + + void Initialize(); + Pass::Status ProcessImpl(); + + // Map from function scope variable to a store of that variable in the + // current block whose value is currently valid. This map is cleared + // at the start of each block and incrementally updated as the block + // is scanned. The stores are candidates for elimination. The map is + // conservatively cleared when a function call is encountered. + std::unordered_map var2store_; + + // Map from function scope variable to a load of that variable in the + // current block whose value is currently valid. This map is cleared + // at the start of each block and incrementally updated as the block + // is scanned. The stores are candidates for elimination. The map is + // conservatively cleared when a function call is encountered. + std::unordered_map var2load_; + + // Set of variables whose most recent store in the current block cannot be + // deleted, for example, if there is a load of the variable which is + // dependent on the store and is not replaced and deleted by this pass, + // for example, a load through an access chain. A variable is removed + // from this set each time a new store of that variable is encountered. + std::unordered_set pinned_vars_; + + // Extensions supported by this pass. + std::unordered_set extensions_allowlist_; + + // Variables that are only referenced by supported operations for this + // pass ie. loads and stores. + std::unordered_set supported_ref_ptrs_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOCAL_SINGLE_BLOCK_ELIM_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/local_single_store_elim_pass.cpp b/third_party/spirv-tools/source/opt/local_single_store_elim_pass.cpp new file mode 100644 index 0000000..99c0fb2 --- /dev/null +++ b/third_party/spirv-tools/source/opt/local_single_store_elim_pass.cpp @@ -0,0 +1,311 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/local_single_store_elim_pass.h" + +#include "source/cfa.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +namespace { + +const uint32_t kStoreValIdInIdx = 1; +const uint32_t kVariableInitIdInIdx = 1; + +} // anonymous namespace + +bool LocalSingleStoreElimPass::LocalSingleStoreElim(Function* func) { + bool modified = false; + + // Check all function scope variables in |func|. + BasicBlock* entry_block = &*func->begin(); + for (Instruction& inst : *entry_block) { + if (inst.opcode() != SpvOpVariable) { + break; + } + + modified |= ProcessVariable(&inst); + } + return modified; +} + +bool LocalSingleStoreElimPass::AllExtensionsSupported() const { + // If any extension not in allowlist, return false + for (auto& ei : get_module()->extensions()) { + const char* extName = + reinterpret_cast(&ei.GetInOperand(0).words[0]); + if (extensions_allowlist_.find(extName) == extensions_allowlist_.end()) + return false; + } + return true; +} + +Pass::Status LocalSingleStoreElimPass::ProcessImpl() { + // Assumes relaxed logical addressing only (see instruction.h) + if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses)) + return Status::SuccessWithoutChange; + + // Do not process if any disallowed extensions are enabled + if (!AllExtensionsSupported()) return Status::SuccessWithoutChange; + // Process all entry point functions + ProcessFunction pfn = [this](Function* fp) { + return LocalSingleStoreElim(fp); + }; + bool modified = context()->ProcessEntryPointCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +LocalSingleStoreElimPass::LocalSingleStoreElimPass() = default; + +Pass::Status LocalSingleStoreElimPass::Process() { + InitExtensionAllowList(); + return ProcessImpl(); +} + +void LocalSingleStoreElimPass::InitExtensionAllowList() { + extensions_allowlist_.insert({ + "SPV_AMD_shader_explicit_vertex_parameter", + "SPV_AMD_shader_trinary_minmax", + "SPV_AMD_gcn_shader", + "SPV_KHR_shader_ballot", + "SPV_AMD_shader_ballot", + "SPV_AMD_gpu_shader_half_float", + "SPV_KHR_shader_draw_parameters", + "SPV_KHR_subgroup_vote", + "SPV_KHR_8bit_storage", + "SPV_KHR_16bit_storage", + "SPV_KHR_device_group", + "SPV_KHR_multiview", + "SPV_NVX_multiview_per_view_attributes", + "SPV_NV_viewport_array2", + "SPV_NV_stereo_view_rendering", + "SPV_NV_sample_mask_override_coverage", + "SPV_NV_geometry_shader_passthrough", + "SPV_AMD_texture_gather_bias_lod", + "SPV_KHR_storage_buffer_storage_class", + "SPV_KHR_variable_pointers", + "SPV_AMD_gpu_shader_int16", + "SPV_KHR_post_depth_coverage", + "SPV_KHR_shader_atomic_counter_ops", + "SPV_EXT_shader_stencil_export", + "SPV_EXT_shader_viewport_index_layer", + "SPV_AMD_shader_image_load_store_lod", + "SPV_AMD_shader_fragment_mask", + "SPV_EXT_fragment_fully_covered", + "SPV_AMD_gpu_shader_half_float_fetch", + "SPV_GOOGLE_decorate_string", + "SPV_GOOGLE_hlsl_functionality1", + "SPV_NV_shader_subgroup_partitioned", + "SPV_EXT_descriptor_indexing", + "SPV_NV_fragment_shader_barycentric", + "SPV_NV_compute_shader_derivatives", + "SPV_NV_shader_image_footprint", + "SPV_NV_shading_rate", + "SPV_NV_mesh_shader", + "SPV_NV_ray_tracing", + "SPV_KHR_ray_query", + "SPV_EXT_fragment_invocation_density", + "SPV_EXT_physical_storage_buffer", + "SPV_KHR_terminate_invocation", + }); +} +bool LocalSingleStoreElimPass::ProcessVariable(Instruction* var_inst) { + std::vector users; + FindUses(var_inst, &users); + + Instruction* store_inst = FindSingleStoreAndCheckUses(var_inst, users); + + if (store_inst == nullptr) { + return false; + } + + bool all_rewritten; + bool modified = RewriteLoads(store_inst, users, &all_rewritten); + + // If all uses are rewritten and the variable has a DebugDeclare and the + // variable is not an aggregate, add a DebugValue after the store and remove + // the DebugDeclare. + uint32_t var_id = var_inst->result_id(); + if (all_rewritten && + context()->get_debug_info_mgr()->IsVariableDebugDeclared(var_id)) { + const analysis::Type* var_type = + context()->get_type_mgr()->GetType(var_inst->type_id()); + const analysis::Type* store_type = var_type->AsPointer()->pointee_type(); + if (!(store_type->AsStruct() || store_type->AsArray())) { + modified |= RewriteDebugDeclares(store_inst, var_id); + } + } + + return modified; +} + +bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst, + uint32_t var_id) { + std::unordered_set invisible_decls; + uint32_t value_id = store_inst->GetSingleWordInOperand(1); + bool modified = + context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible( + store_inst, var_id, value_id, store_inst, &invisible_decls); + + // For cases like the argument passing for an inlined function, the value + // assignment is out of DebugDeclare's scope, but we have to preserve the + // value assignment information using DebugValue. Generally, we need + // ssa-rewrite analysis to decide a proper value assignment but at this point + // we confirm that |var_id| has a single store. We can safely add DebugValue. + if (!invisible_decls.empty()) { + BasicBlock* store_block = context()->get_instr_block(store_inst); + DominatorAnalysis* dominator_analysis = + context()->GetDominatorAnalysis(store_block->GetParent()); + for (auto* decl : invisible_decls) { + if (dominator_analysis->Dominates(store_inst, decl)) { + context()->get_debug_info_mgr()->AddDebugValueForDecl(decl, value_id); + modified = true; + } + } + } + modified |= context()->get_debug_info_mgr()->KillDebugDeclares(var_id); + return modified; +} + +Instruction* LocalSingleStoreElimPass::FindSingleStoreAndCheckUses( + Instruction* var_inst, const std::vector& users) const { + // Make sure there is exactly 1 store. + Instruction* store_inst = nullptr; + + // If |var_inst| has an initializer, then that will count as a store. + if (var_inst->NumInOperands() > 1) { + store_inst = var_inst; + } + + for (Instruction* user : users) { + switch (user->opcode()) { + case SpvOpStore: + // Since we are in the relaxed addressing mode, the use has to be the + // base address of the store, and not the value being store. Otherwise, + // we would have a pointer to a pointer to function scope memory, which + // is not allowed. + if (store_inst == nullptr) { + store_inst = user; + } else { + // More than 1 store. + return nullptr; + } + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + if (FeedsAStore(user)) { + // Has a partial store. Cannot propagate that. + return nullptr; + } + break; + case SpvOpLoad: + case SpvOpImageTexelPointer: + case SpvOpName: + case SpvOpCopyObject: + break; + case SpvOpExtInst: { + auto dbg_op = user->GetOpenCL100DebugOpcode(); + if (dbg_op == OpenCLDebugInfo100DebugDeclare || + dbg_op == OpenCLDebugInfo100DebugValue) { + break; + } + return nullptr; + } + default: + if (!user->IsDecoration()) { + // Don't know if this instruction modifies the variable. + // Conservatively assume it is a store. + return nullptr; + } + break; + } + } + return store_inst; +} + +void LocalSingleStoreElimPass::FindUses( + const Instruction* var_inst, std::vector* users) const { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + def_use_mgr->ForEachUser(var_inst, [users, this](Instruction* user) { + users->push_back(user); + if (user->opcode() == SpvOpCopyObject) { + FindUses(user, users); + } + }); +} + +bool LocalSingleStoreElimPass::FeedsAStore(Instruction* inst) const { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + return !def_use_mgr->WhileEachUser(inst, [this](Instruction* user) { + switch (user->opcode()) { + case SpvOpStore: + return false; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpCopyObject: + return !FeedsAStore(user); + case SpvOpLoad: + case SpvOpImageTexelPointer: + case SpvOpName: + return true; + default: + // Don't know if this instruction modifies the variable. + // Conservatively assume it is a store. + return user->IsDecoration(); + } + }); +} + +bool LocalSingleStoreElimPass::RewriteLoads( + Instruction* store_inst, const std::vector& uses, + bool* all_rewritten) { + BasicBlock* store_block = context()->get_instr_block(store_inst); + DominatorAnalysis* dominator_analysis = + context()->GetDominatorAnalysis(store_block->GetParent()); + + uint32_t stored_id; + if (store_inst->opcode() == SpvOpStore) + stored_id = store_inst->GetSingleWordInOperand(kStoreValIdInIdx); + else + stored_id = store_inst->GetSingleWordInOperand(kVariableInitIdInIdx); + + *all_rewritten = true; + bool modified = false; + for (Instruction* use : uses) { + if (use->opcode() == SpvOpStore) continue; + auto dbg_op = use->GetOpenCL100DebugOpcode(); + if (dbg_op == OpenCLDebugInfo100DebugDeclare || + dbg_op == OpenCLDebugInfo100DebugValue) + continue; + if (use->opcode() == SpvOpLoad && + dominator_analysis->Dominates(store_inst, use)) { + modified = true; + context()->KillNamesAndDecorates(use->result_id()); + context()->ReplaceAllUsesWith(use->result_id(), stored_id); + context()->KillInst(use); + } else { + *all_rewritten = false; + } + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/local_single_store_elim_pass.h b/third_party/spirv-tools/source/opt/local_single_store_elim_pass.h new file mode 100644 index 0000000..3aa0f02 --- /dev/null +++ b/third_party/spirv-tools/source/opt/local_single_store_elim_pass.h @@ -0,0 +1,108 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_ +#define SOURCE_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class LocalSingleStoreElimPass : public Pass { + using cbb_ptr = const BasicBlock*; + + public: + LocalSingleStoreElimPass(); + + const char* name() const override { return "eliminate-local-single-store"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Do "single-store" optimization of function variables defined only + // with a single non-access-chain store in |func|. Replace all their + // non-access-chain loads with the value that is stored and eliminate + // any resulting dead code. + bool LocalSingleStoreElim(Function* func); + + // Initialize extensions allowlist + void InitExtensionAllowList(); + + // Return true if all extensions in this module are allowed by this pass. + bool AllExtensionsSupported() const; + + Pass::Status ProcessImpl(); + + // If there is a single store to |var_inst|, and it covers the entire + // variable, then replace all of the loads of the entire variable that are + // dominated by the store by the value that was stored. Returns true if the + // module was changed. + bool ProcessVariable(Instruction* var_inst); + + // Collects all of the uses of |var_inst| into |uses|. This looks through + // OpObjectCopy's that copy the address of the variable, and collects those + // uses as well. + void FindUses(const Instruction* var_inst, + std::vector* uses) const; + + // Returns a store to |var_inst| if + // - it is a store to the entire variable, + // - and there are no other instructions that may modify |var_inst|. + Instruction* FindSingleStoreAndCheckUses( + Instruction* var_inst, const std::vector& users) const; + + // Returns true if the address that results from |inst| may be used as a base + // address in a store instruction or may be used to compute the base address + // of a store instruction. + bool FeedsAStore(Instruction* inst) const; + + // Replaces all of the loads in |uses| by the value stored in |store_inst|. + // The load instructions are then killed. |all_rewritten| is true iff all + // uses have been rewritten. + bool RewriteLoads(Instruction* store_inst, + const std::vector& uses, bool* all_rewritten); + + // Replaces DebugDeclares of |var_id| with DebugValues using the value + // assignment of |store_inst|. + bool RewriteDebugDeclares(Instruction* store_inst, uint32_t var_id); + + // Extensions supported by this pass. + std::unordered_set extensions_allowlist_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOCAL_SINGLE_STORE_ELIM_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/log.h b/third_party/spirv-tools/source/opt/log.h new file mode 100644 index 0000000..6805100 --- /dev/null +++ b/third_party/spirv-tools/source/opt/log.h @@ -0,0 +1,235 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOG_H_ +#define SOURCE_OPT_LOG_H_ + +#include +#include +#include +#include + +#include "spirv-tools/libspirv.hpp" + +// Asserts the given condition is true. Otherwise, sends a message to the +// consumer and exits the problem with failure code. Accepts the following +// formats: +// +// SPIRV_ASSERT(, ); +// SPIRV_ASSERT(, , ); +// SPIRV_ASSERT(, , +// , ); +// +// In the third format, the number of cannot exceed (5 - +// 2). If more arguments are wanted, grow PP_ARG_N and PP_NARGS in the below. +#if !defined(NDEBUG) +#define SPIRV_ASSERT(consumer, ...) SPIRV_ASSERT_IMPL(consumer, __VA_ARGS__) +#else +#define SPIRV_ASSERT(consumer, ...) +#endif + +// Logs a debug message to the consumer. Accepts the following formats: +// +// SPIRV_DEBUG(, ); +// SPIRV_DEBUG(, , ); +// +// In the second format, the number of cannot exceed (5 - +// 1). If more arguments are wanted, grow PP_ARG_N and PP_NARGS in the below. +#if !defined(NDEBUG) && defined(SPIRV_LOG_DEBUG) +#define SPIRV_DEBUG(consumer, ...) SPIRV_DEBUG_IMPL(consumer, __VA_ARGS__) +#else +#define SPIRV_DEBUG(consumer, ...) +#endif + +// Logs an error message to the consumer saying the given feature is +// unimplemented. +#define SPIRV_UNIMPLEMENTED(consumer, feature) \ + do { \ + spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ + {static_cast(__LINE__), 0, 0}, \ + "unimplemented: " feature); \ + } while (0) + +// Logs an error message to the consumer saying the code location +// should be unreachable. +#define SPIRV_UNREACHABLE(consumer) \ + do { \ + spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ + {static_cast(__LINE__), 0, 0}, "unreachable"); \ + } while (0) + +// Helper macros for concatenating arguments. +#define SPIRV_CONCATENATE(a, b) SPIRV_CONCATENATE_(a, b) +#define SPIRV_CONCATENATE_(a, b) a##b + +// Helper macro to force expanding __VA_ARGS__ to satisfy MSVC compiler. +#define PP_EXPAND(x) x + +namespace spvtools { + +// Calls the given |consumer| by supplying the |message|. The |message| is from +// the given |source| and |location| and of the given severity |level|. +inline void Log(const MessageConsumer& consumer, spv_message_level_t level, + const char* source, const spv_position_t& position, + const char* message) { + if (consumer != nullptr) consumer(level, source, position, message); +} + +// Calls the given |consumer| by supplying the message composed according to the +// given |format|. The |message| is from the given |source| and |location| and +// of the given severity |level|. +template +void Logf(const MessageConsumer& consumer, spv_message_level_t level, + const char* source, const spv_position_t& position, + const char* format, Args&&... args) { +#if defined(_MSC_VER) && _MSC_VER < 1900 +// Sadly, snprintf() is not supported until Visual Studio 2015! +#define snprintf _snprintf +#endif + + enum { kInitBufferSize = 256 }; + + char message[kInitBufferSize]; + const int size = + snprintf(message, kInitBufferSize, format, std::forward(args)...); + + if (size >= 0 && size < kInitBufferSize) { + Log(consumer, level, source, position, message); + return; + } + + if (size >= 0) { + // The initial buffer is insufficient. Allocate a buffer of a larger size, + // and write to it instead. Force the size to be unsigned to avoid a + // warning in GCC 7.1. + std::vector longer_message(size + 1u); + snprintf(longer_message.data(), longer_message.size(), format, + std::forward(args)...); + Log(consumer, level, source, position, longer_message.data()); + return; + } + + Log(consumer, level, source, position, "cannot compose log message"); + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#undef snprintf +#endif +} + +// Calls the given |consumer| by supplying the given error |message|. The +// |message| is from the given |source| and |location|. +inline void Error(const MessageConsumer& consumer, const char* source, + const spv_position_t& position, const char* message) { + Log(consumer, SPV_MSG_ERROR, source, position, message); +} + +// Calls the given |consumer| by supplying the error message composed according +// to the given |format|. The |message| is from the given |source| and +// |location|. +template +inline void Errorf(const MessageConsumer& consumer, const char* source, + const spv_position_t& position, const char* format, + Args&&... args) { + Logf(consumer, SPV_MSG_ERROR, source, position, format, + std::forward(args)...); +} + +} // namespace spvtools + +#define SPIRV_ASSERT_IMPL(consumer, ...) \ + PP_EXPAND(SPIRV_CONCATENATE(SPIRV_ASSERT_, PP_NARGS(__VA_ARGS__))( \ + consumer, __VA_ARGS__)) + +#define SPIRV_DEBUG_IMPL(consumer, ...) \ + PP_EXPAND(SPIRV_CONCATENATE(SPIRV_DEBUG_, PP_NARGS(__VA_ARGS__))( \ + consumer, __VA_ARGS__)) + +#define SPIRV_ASSERT_1(consumer, condition) \ + do { \ + if (!(condition)) { \ + spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ + {static_cast(__LINE__), 0, 0}, \ + "assertion failed: " #condition); \ + std::exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define SPIRV_ASSERT_2(consumer, condition, message) \ + do { \ + if (!(condition)) { \ + spvtools::Log(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ + {static_cast(__LINE__), 0, 0}, \ + "assertion failed: " message); \ + std::exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define SPIRV_ASSERT_more(consumer, condition, format, ...) \ + do { \ + if (!(condition)) { \ + spvtools::Logf(consumer, SPV_MSG_INTERNAL_ERROR, __FILE__, \ + {static_cast(__LINE__), 0, 0}, \ + "assertion failed: " format, __VA_ARGS__); \ + std::exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define SPIRV_ASSERT_3(consumer, condition, format, ...) \ + SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__) + +#define SPIRV_ASSERT_4(consumer, condition, format, ...) \ + SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__) + +#define SPIRV_ASSERT_5(consumer, condition, format, ...) \ + SPIRV_ASSERT_more(consumer, condition, format, __VA_ARGS__) + +#define SPIRV_DEBUG_1(consumer, message) \ + do { \ + spvtools::Log(consumer, SPV_MSG_DEBUG, __FILE__, \ + {static_cast(__LINE__), 0, 0}, message); \ + } while (0) + +#define SPIRV_DEBUG_more(consumer, format, ...) \ + do { \ + spvtools::Logf(consumer, SPV_MSG_DEBUG, __FILE__, \ + {static_cast(__LINE__), 0, 0}, format, \ + __VA_ARGS__); \ + } while (0) + +#define SPIRV_DEBUG_2(consumer, format, ...) \ + SPIRV_DEBUG_more(consumer, format, __VA_ARGS__) + +#define SPIRV_DEBUG_3(consumer, format, ...) \ + SPIRV_DEBUG_more(consumer, format, __VA_ARGS__) + +#define SPIRV_DEBUG_4(consumer, format, ...) \ + SPIRV_DEBUG_more(consumer, format, __VA_ARGS__) + +#define SPIRV_DEBUG_5(consumer, format, ...) \ + SPIRV_DEBUG_more(consumer, format, __VA_ARGS__) + +// Macros for counting the number of arguments passed in. +#define PP_NARGS(...) PP_EXPAND(PP_ARG_N(__VA_ARGS__, 5, 4, 3, 2, 1, 0)) +#define PP_ARG_N(_1, _2, _3, _4, _5, N, ...) N + +// Tests for making sure that PP_NARGS() behaves as expected. +static_assert(PP_NARGS(0) == 1, "PP_NARGS macro error"); +static_assert(PP_NARGS(0, 0) == 2, "PP_NARGS macro error"); +static_assert(PP_NARGS(0, 0, 0) == 3, "PP_NARGS macro error"); +static_assert(PP_NARGS(0, 0, 0, 0) == 4, "PP_NARGS macro error"); +static_assert(PP_NARGS(0, 0, 0, 0, 0) == 5, "PP_NARGS macro error"); +static_assert(PP_NARGS(1 + 1, 2, 3 / 3) == 3, "PP_NARGS macro error"); +static_assert(PP_NARGS((1, 1), 2, (3, 3)) == 3, "PP_NARGS macro error"); + +#endif // SOURCE_OPT_LOG_H_ diff --git a/third_party/spirv-tools/source/opt/loop_dependence.cpp b/third_party/spirv-tools/source/opt/loop_dependence.cpp new file mode 100644 index 0000000..d8de699 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_dependence.cpp @@ -0,0 +1,1675 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_dependence.h" + +#include +#include +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/scalar_analysis_nodes.h" + +namespace spvtools { +namespace opt { + +using SubscriptPair = std::pair; + +namespace { + +// Calculate the greatest common divisor of a & b using Stein's algorithm. +// https://en.wikipedia.org/wiki/Binary_GCD_algorithm +int64_t GreatestCommonDivisor(int64_t a, int64_t b) { + // Simple cases + if (a == b) { + return a; + } else if (a == 0) { + return b; + } else if (b == 0) { + return a; + } + + // Both even + if (a % 2 == 0 && b % 2 == 0) { + return 2 * GreatestCommonDivisor(a / 2, b / 2); + } + + // Even a, odd b + if (a % 2 == 0 && b % 2 == 1) { + return GreatestCommonDivisor(a / 2, b); + } + + // Odd a, even b + if (a % 2 == 1 && b % 2 == 0) { + return GreatestCommonDivisor(a, b / 2); + } + + // Both odd, reduce the larger argument + if (a > b) { + return GreatestCommonDivisor((a - b) / 2, b); + } else { + return GreatestCommonDivisor((b - a) / 2, a); + } +} + +// Check if node is affine, ie in the form: a0*i0 + a1*i1 + ... an*in + c +// and contains only the following types of nodes: SERecurrentNode, SEAddNode +// and SEConstantNode +bool IsInCorrectFormForGCDTest(SENode* node) { + bool children_ok = true; + + if (auto add_node = node->AsSEAddNode()) { + for (auto child : add_node->GetChildren()) { + children_ok &= IsInCorrectFormForGCDTest(child); + } + } + + bool this_ok = node->AsSERecurrentNode() || node->AsSEAddNode() || + node->AsSEConstantNode(); + + return children_ok && this_ok; +} + +// If |node| is an SERecurrentNode then returns |node| or if |node| is an +// SEAddNode returns a vector of SERecurrentNode that are its children. +std::vector GetAllTopLevelRecurrences(SENode* node) { + auto nodes = std::vector{}; + if (auto recurrent_node = node->AsSERecurrentNode()) { + nodes.push_back(recurrent_node); + } + + if (auto add_node = node->AsSEAddNode()) { + for (auto child : add_node->GetChildren()) { + auto child_nodes = GetAllTopLevelRecurrences(child); + nodes.insert(nodes.end(), child_nodes.begin(), child_nodes.end()); + } + } + + return nodes; +} + +// If |node| is an SEConstantNode then returns |node| or if |node| is an +// SEAddNode returns a vector of SEConstantNode that are its children. +std::vector GetAllTopLevelConstants(SENode* node) { + auto nodes = std::vector{}; + if (auto recurrent_node = node->AsSEConstantNode()) { + nodes.push_back(recurrent_node); + } + + if (auto add_node = node->AsSEAddNode()) { + for (auto child : add_node->GetChildren()) { + auto child_nodes = GetAllTopLevelConstants(child); + nodes.insert(nodes.end(), child_nodes.begin(), child_nodes.end()); + } + } + + return nodes; +} + +bool AreOffsetsAndCoefficientsConstant( + const std::vector& nodes) { + for (auto node : nodes) { + if (!node->GetOffset()->AsSEConstantNode() || + !node->GetOffset()->AsSEConstantNode()) { + return false; + } + } + return true; +} + +// Fold all SEConstantNode that appear in |recurrences| and |constants| into a +// single integer value. +int64_t CalculateConstantTerm(const std::vector& recurrences, + const std::vector& constants) { + int64_t constant_term = 0; + for (auto recurrence : recurrences) { + constant_term += + recurrence->GetOffset()->AsSEConstantNode()->FoldToSingleValue(); + } + + for (auto constant : constants) { + constant_term += constant->FoldToSingleValue(); + } + + return constant_term; +} + +int64_t CalculateGCDFromCoefficients( + const std::vector& recurrences, int64_t running_gcd) { + for (SERecurrentNode* recurrence : recurrences) { + auto coefficient = recurrence->GetCoefficient()->AsSEConstantNode(); + + running_gcd = GreatestCommonDivisor( + running_gcd, std::abs(coefficient->FoldToSingleValue())); + } + + return running_gcd; +} + +// Compare 2 fractions while first normalizing them, e.g. 2/4 and 4/8 will both +// be simplified to 1/2 and then determined to be equal. +bool NormalizeAndCompareFractions(int64_t numerator_0, int64_t denominator_0, + int64_t numerator_1, int64_t denominator_1) { + auto gcd_0 = + GreatestCommonDivisor(std::abs(numerator_0), std::abs(denominator_0)); + auto gcd_1 = + GreatestCommonDivisor(std::abs(numerator_1), std::abs(denominator_1)); + + auto normalized_numerator_0 = numerator_0 / gcd_0; + auto normalized_denominator_0 = denominator_0 / gcd_0; + auto normalized_numerator_1 = numerator_1 / gcd_1; + auto normalized_denominator_1 = denominator_1 / gcd_1; + + return normalized_numerator_0 == normalized_numerator_1 && + normalized_denominator_0 == normalized_denominator_1; +} + +} // namespace + +bool LoopDependenceAnalysis::GetDependence(const Instruction* source, + const Instruction* destination, + DistanceVector* distance_vector) { + // Start off by finding and marking all the loops in |loops_| that are + // irrelevant to the dependence analysis. + MarkUnsusedDistanceEntriesAsIrrelevant(source, destination, distance_vector); + + Instruction* source_access_chain = GetOperandDefinition(source, 0); + Instruction* destination_access_chain = GetOperandDefinition(destination, 0); + + auto num_access_chains = + (source_access_chain->opcode() == SpvOpAccessChain) + + (destination_access_chain->opcode() == SpvOpAccessChain); + + // If neither is an access chain, then they are load/store to a variable. + if (num_access_chains == 0) { + if (source_access_chain != destination_access_chain) { + // Not the same location, report independence + return true; + } else { + // Accessing the same variable + for (auto& entry : distance_vector->GetEntries()) { + entry = DistanceEntry(); + } + return false; + } + } + + // If only one is an access chain, it could be accessing a part of a struct + if (num_access_chains == 1) { + auto source_is_chain = source_access_chain->opcode() == SpvOpAccessChain; + auto access_chain = + source_is_chain ? source_access_chain : destination_access_chain; + auto variable = + source_is_chain ? destination_access_chain : source_access_chain; + + auto location_in_chain = GetOperandDefinition(access_chain, 0); + + if (variable != location_in_chain) { + // Not the same location, report independence + return true; + } else { + // Accessing the same variable + for (auto& entry : distance_vector->GetEntries()) { + entry = DistanceEntry(); + } + return false; + } + } + + // If the access chains aren't collecting from the same structure there is no + // dependence. + Instruction* source_array = GetOperandDefinition(source_access_chain, 0); + Instruction* destination_array = + GetOperandDefinition(destination_access_chain, 0); + + // Nested access chains are not supported yet, bail out. + if (source_array->opcode() == SpvOpAccessChain || + destination_array->opcode() == SpvOpAccessChain) { + for (auto& entry : distance_vector->GetEntries()) { + entry = DistanceEntry(); + } + return false; + } + + if (source_array != destination_array) { + PrintDebug("Proved independence through different arrays."); + return true; + } + + // To handle multiple subscripts we must get every operand in the access + // chains past the first. + std::vector source_subscripts = GetSubscripts(source); + std::vector destination_subscripts = GetSubscripts(destination); + + auto sets_of_subscripts = + PartitionSubscripts(source_subscripts, destination_subscripts); + + auto first_coupled = std::partition( + std::begin(sets_of_subscripts), std::end(sets_of_subscripts), + [](const std::set>& set) { + return set.size() == 1; + }); + + // Go through each subscript testing for independence. + // If any subscript results in independence, we prove independence between the + // load and store. + // If we can't prove independence we store what information we can gather in + // a DistanceVector. + for (auto it = std::begin(sets_of_subscripts); it < first_coupled; ++it) { + auto source_subscript = std::get<0>(*(*it).begin()); + auto destination_subscript = std::get<1>(*(*it).begin()); + + SENode* source_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(source_subscript)); + SENode* destination_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(destination_subscript)); + + // Check the loops are in a form we support. + auto subscript_pair = std::make_pair(source_node, destination_node); + + const Loop* loop = GetLoopForSubscriptPair(subscript_pair); + if (loop) { + if (!IsSupportedLoop(loop)) { + PrintDebug( + "GetDependence found an unsupported loop form. Assuming <=> for " + "loop."); + DistanceEntry* distance_entry = + GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); + if (distance_entry) { + distance_entry->direction = DistanceEntry::Directions::ALL; + } + continue; + } + } + + // If either node is simplified to a CanNotCompute we can't perform any + // analysis so must assume <=> dependence and return. + if (source_node->GetType() == SENode::CanNotCompute || + destination_node->GetType() == SENode::CanNotCompute) { + // Record the <=> dependence if we can get a DistanceEntry + PrintDebug( + "GetDependence found source_node || destination_node as " + "CanNotCompute. Abandoning evaluation for this subscript."); + DistanceEntry* distance_entry = + GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); + if (distance_entry) { + distance_entry->direction = DistanceEntry::Directions::ALL; + } + continue; + } + + // We have no induction variables so can apply a ZIV test. + if (IsZIV(subscript_pair)) { + PrintDebug("Found a ZIV subscript pair"); + if (ZIVTest(subscript_pair)) { + PrintDebug("Proved independence with ZIVTest."); + return true; + } + } + + // We have only one induction variable so should attempt an SIV test. + if (IsSIV(subscript_pair)) { + PrintDebug("Found a SIV subscript pair."); + if (SIVTest(subscript_pair, distance_vector)) { + PrintDebug("Proved independence with SIVTest."); + return true; + } + } + + // We have multiple induction variables so should attempt an MIV test. + if (IsMIV(subscript_pair)) { + PrintDebug("Found a MIV subscript pair."); + if (GCDMIVTest(subscript_pair)) { + PrintDebug("Proved independence with the GCD test."); + auto current_loops = CollectLoops(source_node, destination_node); + + for (auto current_loop : current_loops) { + auto distance_entry = + GetDistanceEntryForLoop(current_loop, distance_vector); + distance_entry->direction = DistanceEntry::Directions::NONE; + } + return true; + } + } + } + + for (auto it = first_coupled; it < std::end(sets_of_subscripts); ++it) { + auto coupled_instructions = *it; + std::vector coupled_subscripts{}; + + for (const auto& elem : coupled_instructions) { + auto source_subscript = std::get<0>(elem); + auto destination_subscript = std::get<1>(elem); + + SENode* source_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(source_subscript)); + SENode* destination_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(destination_subscript)); + + coupled_subscripts.push_back({source_node, destination_node}); + } + + auto supported = true; + + for (const auto& subscript : coupled_subscripts) { + auto loops = CollectLoops(std::get<0>(subscript), std::get<1>(subscript)); + + auto is_subscript_supported = + std::all_of(std::begin(loops), std::end(loops), + [this](const Loop* l) { return IsSupportedLoop(l); }); + + supported = supported && is_subscript_supported; + } + + if (DeltaTest(coupled_subscripts, distance_vector)) { + return true; + } + } + + // We were unable to prove independence so must gather all of the direction + // information we found. + PrintDebug( + "Couldn't prove independence.\n" + "All possible direction information has been collected in the input " + "DistanceVector."); + + return false; +} + +bool LoopDependenceAnalysis::ZIVTest( + const std::pair& subscript_pair) { + auto source = std::get<0>(subscript_pair); + auto destination = std::get<1>(subscript_pair); + + PrintDebug("Performing ZIVTest"); + // If source == destination, dependence with direction = and distance 0. + if (source == destination) { + PrintDebug("ZIVTest found EQ dependence."); + return false; + } else { + PrintDebug("ZIVTest found independence."); + // Otherwise we prove independence. + return true; + } +} + +bool LoopDependenceAnalysis::SIVTest( + const std::pair& subscript_pair, + DistanceVector* distance_vector) { + DistanceEntry* distance_entry = + GetDistanceEntryForSubscriptPair(subscript_pair, distance_vector); + if (!distance_entry) { + PrintDebug( + "SIVTest could not find a DistanceEntry for subscript_pair. Exiting"); + } + + SENode* source_node = std::get<0>(subscript_pair); + SENode* destination_node = std::get<1>(subscript_pair); + + int64_t source_induction_count = CountInductionVariables(source_node); + int64_t destination_induction_count = + CountInductionVariables(destination_node); + + // If the source node has no induction variables we can apply a + // WeakZeroSrcTest. + if (source_induction_count == 0) { + PrintDebug("Found source has no induction variable."); + if (WeakZeroSourceSIVTest( + source_node, destination_node->AsSERecurrentNode(), + destination_node->AsSERecurrentNode()->GetCoefficient(), + distance_entry)) { + PrintDebug("Proved independence with WeakZeroSourceSIVTest."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + } + + // If the destination has no induction variables we can apply a + // WeakZeroDestTest. + if (destination_induction_count == 0) { + PrintDebug("Found destination has no induction variable."); + if (WeakZeroDestinationSIVTest( + source_node->AsSERecurrentNode(), destination_node, + source_node->AsSERecurrentNode()->GetCoefficient(), + distance_entry)) { + PrintDebug("Proved independence with WeakZeroDestinationSIVTest."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + } + + // We now need to collect the SERecurrentExpr nodes from source and + // destination. We do not handle cases where source or destination have + // multiple SERecurrentExpr nodes. + std::vector source_recurrent_nodes = + source_node->CollectRecurrentNodes(); + std::vector destination_recurrent_nodes = + destination_node->CollectRecurrentNodes(); + + if (source_recurrent_nodes.size() == 1 && + destination_recurrent_nodes.size() == 1) { + PrintDebug("Found source and destination have 1 induction variable."); + SERecurrentNode* source_recurrent_expr = *source_recurrent_nodes.begin(); + SERecurrentNode* destination_recurrent_expr = + *destination_recurrent_nodes.begin(); + + // If the coefficients are identical we can apply a StrongSIVTest. + if (source_recurrent_expr->GetCoefficient() == + destination_recurrent_expr->GetCoefficient()) { + PrintDebug("Found source and destination share coefficient."); + if (StrongSIVTest(source_node, destination_node, + source_recurrent_expr->GetCoefficient(), + distance_entry)) { + PrintDebug("Proved independence with StrongSIVTest"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + } + + // If the coefficients are of equal magnitude and opposite sign we can + // apply a WeakCrossingSIVTest. + if (source_recurrent_expr->GetCoefficient() == + scalar_evolution_.CreateNegation( + destination_recurrent_expr->GetCoefficient())) { + PrintDebug("Found source coefficient = -destination coefficient."); + if (WeakCrossingSIVTest(source_node, destination_node, + source_recurrent_expr->GetCoefficient(), + distance_entry)) { + PrintDebug("Proved independence with WeakCrossingSIVTest"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + } + } + + return false; +} + +bool LoopDependenceAnalysis::StrongSIVTest(SENode* source, SENode* destination, + SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing StrongSIVTest."); + // If both source and destination are SERecurrentNodes we can perform tests + // based on distance. + // If either source or destination contain value unknown nodes or if one or + // both are not SERecurrentNodes we must attempt a symbolic test. + std::vector source_value_unknown_nodes = + source->CollectValueUnknownNodes(); + std::vector destination_value_unknown_nodes = + destination->CollectValueUnknownNodes(); + if (source_value_unknown_nodes.size() > 0 || + destination_value_unknown_nodes.size() > 0) { + PrintDebug( + "StrongSIVTest found symbolics. Will attempt SymbolicStrongSIVTest."); + return SymbolicStrongSIVTest(source, destination, coefficient, + distance_entry); + } + + if (!source->AsSERecurrentNode() || !destination->AsSERecurrentNode()) { + PrintDebug( + "StrongSIVTest could not simplify source and destination to " + "SERecurrentNodes so will exit."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; + } + + // Build an SENode for distance. + std::pair subscript_pair = + std::make_pair(source, destination); + const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); + SENode* source_constant_term = + GetConstantTerm(subscript_loop, source->AsSERecurrentNode()); + SENode* destination_constant_term = + GetConstantTerm(subscript_loop, destination->AsSERecurrentNode()); + if (!source_constant_term || !destination_constant_term) { + PrintDebug( + "StrongSIVTest could not collect the constant terms of either source " + "or destination so will exit."); + return false; + } + SENode* constant_term_delta = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( + destination_constant_term, source_constant_term)); + + // Scalar evolution doesn't perform division, so we must fold to constants and + // do it manually. + // We must check the offset delta and coefficient are constants. + int64_t distance = 0; + SEConstantNode* delta_constant = constant_term_delta->AsSEConstantNode(); + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (delta_constant && coefficient_constant) { + int64_t delta_value = delta_constant->FoldToSingleValue(); + int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); + PrintDebug( + "StrongSIVTest found delta value and coefficient value as constants " + "with values:\n" + "\tdelta value: " + + ToString(delta_value) + + "\n\tcoefficient value: " + ToString(coefficient_value) + "\n"); + // Check if the distance is not integral to try to prove independence. + if (delta_value % coefficient_value != 0) { + PrintDebug( + "StrongSIVTest proved independence through distance not being an " + "integer."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } else { + distance = delta_value / coefficient_value; + PrintDebug("StrongSIV test found distance as " + ToString(distance)); + } + } else { + // If we can't fold delta and coefficient to single values we can't produce + // distance. + // As a result we can't perform the rest of the pass and must assume + // dependence in all directions. + PrintDebug("StrongSIVTest could not produce a distance. Must exit."); + distance_entry->distance = DistanceEntry::Directions::ALL; + return false; + } + + // Next we gather the upper and lower bounds as constants if possible. If + // distance > upper_bound - lower_bound we prove independence. + SENode* lower_bound = GetLowerBound(subscript_loop); + SENode* upper_bound = GetUpperBound(subscript_loop); + if (lower_bound && upper_bound) { + PrintDebug("StrongSIVTest found bounds."); + SENode* bounds = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(upper_bound, lower_bound)); + + if (bounds->GetType() == SENode::SENodeType::Constant) { + int64_t bounds_value = bounds->AsSEConstantNode()->FoldToSingleValue(); + PrintDebug( + "StrongSIVTest found upper_bound - lower_bound as a constant with " + "value " + + ToString(bounds_value)); + + // If the absolute value of the distance is > upper bound - lower bound + // then we prove independence. + if (llabs(distance) > llabs(bounds_value)) { + PrintDebug( + "StrongSIVTest proved independence through distance escaping the " + "loop bounds."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::NONE; + distance_entry->distance = distance; + return true; + } + } + } else { + PrintDebug("StrongSIVTest was unable to gather lower and upper bounds."); + } + + // Otherwise we can get a direction as follows + // { < if distance > 0 + // direction = { = if distance == 0 + // { > if distance < 0 + PrintDebug( + "StrongSIVTest could not prove independence. Gathering direction " + "information."); + if (distance > 0) { + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::LT; + distance_entry->distance = distance; + return false; + } + if (distance == 0) { + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::EQ; + distance_entry->distance = 0; + return false; + } + if (distance < 0) { + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::GT; + distance_entry->distance = distance; + return false; + } + + // We were unable to prove independence or discern any additional information + // Must assume <=> direction. + PrintDebug( + "StrongSIVTest was unable to determine any dependence information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +bool LoopDependenceAnalysis::SymbolicStrongSIVTest( + SENode* source, SENode* destination, SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing SymbolicStrongSIVTest."); + SENode* source_destination_delta = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(source, destination)); + // By cancelling out the induction variables by subtracting the source and + // destination we can produce an expression of symbolics and constants. This + // expression can be compared to the loop bounds to find if the offset is + // outwith the bounds. + std::pair subscript_pair = + std::make_pair(source, destination); + const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); + if (IsProvablyOutsideOfLoopBounds(subscript_loop, source_destination_delta, + coefficient)) { + PrintDebug( + "SymbolicStrongSIVTest proved independence through loop bounds."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } + // We were unable to prove independence or discern any additional information. + // Must assume <=> direction. + PrintDebug( + "SymbolicStrongSIVTest was unable to determine any dependence " + "information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +bool LoopDependenceAnalysis::WeakZeroSourceSIVTest( + SENode* source, SERecurrentNode* destination, SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing WeakZeroSourceSIVTest."); + std::pair subscript_pair = + std::make_pair(source, destination); + const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); + // Build an SENode for distance. + SENode* destination_constant_term = + GetConstantTerm(subscript_loop, destination); + SENode* delta = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(source, destination_constant_term)); + + // Scalar evolution doesn't perform division, so we must fold to constants and + // do it manually. + int64_t distance = 0; + SEConstantNode* delta_constant = delta->AsSEConstantNode(); + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (delta_constant && coefficient_constant) { + PrintDebug( + "WeakZeroSourceSIVTest folding delta and coefficient to constants."); + int64_t delta_value = delta_constant->FoldToSingleValue(); + int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); + // Check if the distance is not integral. + if (delta_value % coefficient_value != 0) { + PrintDebug( + "WeakZeroSourceSIVTest proved independence through distance not " + "being an integer."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } else { + distance = delta_value / coefficient_value; + PrintDebug( + "WeakZeroSourceSIVTest calculated distance with the following " + "values\n" + "\tdelta value: " + + ToString(delta_value) + + "\n\tcoefficient value: " + ToString(coefficient_value) + + "\n\tdistance: " + ToString(distance) + "\n"); + } + } else { + PrintDebug( + "WeakZeroSourceSIVTest was unable to fold delta and coefficient to " + "constants."); + } + + // If we can prove the distance is outside the bounds we prove independence. + SEConstantNode* lower_bound = + GetLowerBound(subscript_loop)->AsSEConstantNode(); + SEConstantNode* upper_bound = + GetUpperBound(subscript_loop)->AsSEConstantNode(); + if (lower_bound && upper_bound) { + PrintDebug("WeakZeroSourceSIVTest found bounds as SEConstantNodes."); + int64_t lower_bound_value = lower_bound->FoldToSingleValue(); + int64_t upper_bound_value = upper_bound->FoldToSingleValue(); + if (!IsWithinBounds(llabs(distance), lower_bound_value, + upper_bound_value)) { + PrintDebug( + "WeakZeroSourceSIVTest proved independence through distance escaping " + "the loop bounds."); + PrintDebug( + "Bound values were as follow\n" + "\tlower bound value: " + + ToString(lower_bound_value) + + "\n\tupper bound value: " + ToString(upper_bound_value) + + "\n\tdistance value: " + ToString(distance) + "\n"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::NONE; + distance_entry->distance = distance; + return true; + } + } else { + PrintDebug( + "WeakZeroSourceSIVTest was unable to find lower and upper bound as " + "SEConstantNodes."); + } + + // Now we want to see if we can detect to peel the first or last iterations. + + // We get the FirstTripValue as GetFirstTripInductionNode() + + // GetConstantTerm(destination) + SENode* first_trip_SENode = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + GetFirstTripInductionNode(subscript_loop), + GetConstantTerm(subscript_loop, destination))); + + // If source == FirstTripValue, peel_first. + if (first_trip_SENode) { + PrintDebug("WeakZeroSourceSIVTest built first_trip_SENode."); + if (first_trip_SENode->AsSEConstantNode()) { + PrintDebug( + "WeakZeroSourceSIVTest has found first_trip_SENode as an " + "SEConstantNode with value: " + + ToString(first_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + + "\n"); + } + if (source == first_trip_SENode) { + // We have found that peeling the first iteration will break dependency. + PrintDebug( + "WeakZeroSourceSIVTest has found peeling first iteration will break " + "dependency"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::PEEL; + distance_entry->peel_first = true; + return false; + } + } else { + PrintDebug("WeakZeroSourceSIVTest was unable to build first_trip_SENode"); + } + + // We get the LastTripValue as GetFinalTripInductionNode(coefficient) + + // GetConstantTerm(destination) + SENode* final_trip_SENode = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + GetFinalTripInductionNode(subscript_loop, coefficient), + GetConstantTerm(subscript_loop, destination))); + + // If source == LastTripValue, peel_last. + if (final_trip_SENode) { + PrintDebug("WeakZeroSourceSIVTest built final_trip_SENode."); + if (first_trip_SENode->AsSEConstantNode()) { + PrintDebug( + "WeakZeroSourceSIVTest has found final_trip_SENode as an " + "SEConstantNode with value: " + + ToString(final_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + + "\n"); + } + if (source == final_trip_SENode) { + // We have found that peeling the last iteration will break dependency. + PrintDebug( + "WeakZeroSourceSIVTest has found peeling final iteration will break " + "dependency"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::PEEL; + distance_entry->peel_last = true; + return false; + } + } else { + PrintDebug("WeakZeroSourceSIVTest was unable to build final_trip_SENode"); + } + + // We were unable to prove independence or discern any additional information. + // Must assume <=> direction. + PrintDebug( + "WeakZeroSourceSIVTest was unable to determine any dependence " + "information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +bool LoopDependenceAnalysis::WeakZeroDestinationSIVTest( + SERecurrentNode* source, SENode* destination, SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing WeakZeroDestinationSIVTest."); + // Build an SENode for distance. + std::pair subscript_pair = + std::make_pair(source, destination); + const Loop* subscript_loop = GetLoopForSubscriptPair(subscript_pair); + SENode* source_constant_term = GetConstantTerm(subscript_loop, source); + SENode* delta = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(destination, source_constant_term)); + + // Scalar evolution doesn't perform division, so we must fold to constants and + // do it manually. + int64_t distance = 0; + SEConstantNode* delta_constant = delta->AsSEConstantNode(); + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (delta_constant && coefficient_constant) { + PrintDebug( + "WeakZeroDestinationSIVTest folding delta and coefficient to " + "constants."); + int64_t delta_value = delta_constant->FoldToSingleValue(); + int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); + // Check if the distance is not integral. + if (delta_value % coefficient_value != 0) { + PrintDebug( + "WeakZeroDestinationSIVTest proved independence through distance not " + "being an integer."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } else { + distance = delta_value / coefficient_value; + PrintDebug( + "WeakZeroDestinationSIVTest calculated distance with the following " + "values\n" + "\tdelta value: " + + ToString(delta_value) + + "\n\tcoefficient value: " + ToString(coefficient_value) + + "\n\tdistance: " + ToString(distance) + "\n"); + } + } else { + PrintDebug( + "WeakZeroDestinationSIVTest was unable to fold delta and coefficient " + "to constants."); + } + + // If we can prove the distance is outside the bounds we prove independence. + SEConstantNode* lower_bound = + GetLowerBound(subscript_loop)->AsSEConstantNode(); + SEConstantNode* upper_bound = + GetUpperBound(subscript_loop)->AsSEConstantNode(); + if (lower_bound && upper_bound) { + PrintDebug("WeakZeroDestinationSIVTest found bounds as SEConstantNodes."); + int64_t lower_bound_value = lower_bound->FoldToSingleValue(); + int64_t upper_bound_value = upper_bound->FoldToSingleValue(); + if (!IsWithinBounds(llabs(distance), lower_bound_value, + upper_bound_value)) { + PrintDebug( + "WeakZeroDestinationSIVTest proved independence through distance " + "escaping the loop bounds."); + PrintDebug( + "Bound values were as follows\n" + "\tlower bound value: " + + ToString(lower_bound_value) + + "\n\tupper bound value: " + ToString(upper_bound_value) + + "\n\tdistance value: " + ToString(distance)); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::NONE; + distance_entry->distance = distance; + return true; + } + } else { + PrintDebug( + "WeakZeroDestinationSIVTest was unable to find lower and upper bound " + "as SEConstantNodes."); + } + + // Now we want to see if we can detect to peel the first or last iterations. + + // We get the FirstTripValue as GetFirstTripInductionNode() + + // GetConstantTerm(source) + SENode* first_trip_SENode = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateAddNode(GetFirstTripInductionNode(subscript_loop), + GetConstantTerm(subscript_loop, source))); + + // If destination == FirstTripValue, peel_first. + if (first_trip_SENode) { + PrintDebug("WeakZeroDestinationSIVTest built first_trip_SENode."); + if (first_trip_SENode->AsSEConstantNode()) { + PrintDebug( + "WeakZeroDestinationSIVTest has found first_trip_SENode as an " + "SEConstantNode with value: " + + ToString(first_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + + "\n"); + } + if (destination == first_trip_SENode) { + // We have found that peeling the first iteration will break dependency. + PrintDebug( + "WeakZeroDestinationSIVTest has found peeling first iteration will " + "break dependency"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::PEEL; + distance_entry->peel_first = true; + return false; + } + } else { + PrintDebug( + "WeakZeroDestinationSIVTest was unable to build first_trip_SENode"); + } + + // We get the LastTripValue as GetFinalTripInductionNode(coefficient) + + // GetConstantTerm(source) + SENode* final_trip_SENode = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + GetFinalTripInductionNode(subscript_loop, coefficient), + GetConstantTerm(subscript_loop, source))); + + // If destination == LastTripValue, peel_last. + if (final_trip_SENode) { + PrintDebug("WeakZeroDestinationSIVTest built final_trip_SENode."); + if (final_trip_SENode->AsSEConstantNode()) { + PrintDebug( + "WeakZeroDestinationSIVTest has found final_trip_SENode as an " + "SEConstantNode with value: " + + ToString(final_trip_SENode->AsSEConstantNode()->FoldToSingleValue()) + + "\n"); + } + if (destination == final_trip_SENode) { + // We have found that peeling the last iteration will break dependency. + PrintDebug( + "WeakZeroDestinationSIVTest has found peeling final iteration will " + "break dependency"); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::PEEL; + distance_entry->peel_last = true; + return false; + } + } else { + PrintDebug( + "WeakZeroDestinationSIVTest was unable to build final_trip_SENode"); + } + + // We were unable to prove independence or discern any additional information. + // Must assume <=> direction. + PrintDebug( + "WeakZeroDestinationSIVTest was unable to determine any dependence " + "information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +bool LoopDependenceAnalysis::WeakCrossingSIVTest( + SENode* source, SENode* destination, SENode* coefficient, + DistanceEntry* distance_entry) { + PrintDebug("Performing WeakCrossingSIVTest."); + // We currently can't handle symbolic WeakCrossingSIVTests. If either source + // or destination are not SERecurrentNodes we must exit. + if (!source->AsSERecurrentNode() || !destination->AsSERecurrentNode()) { + PrintDebug( + "WeakCrossingSIVTest found source or destination != SERecurrentNode. " + "Exiting"); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; + } + + // Build an SENode for distance. + SENode* offset_delta = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( + destination->AsSERecurrentNode()->GetOffset(), + source->AsSERecurrentNode()->GetOffset())); + + // Scalar evolution doesn't perform division, so we must fold to constants and + // do it manually. + int64_t distance = 0; + SEConstantNode* delta_constant = offset_delta->AsSEConstantNode(); + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (delta_constant && coefficient_constant) { + PrintDebug( + "WeakCrossingSIVTest folding offset_delta and coefficient to " + "constants."); + int64_t delta_value = delta_constant->FoldToSingleValue(); + int64_t coefficient_value = coefficient_constant->FoldToSingleValue(); + // Check if the distance is not integral or if it has a non-integral part + // equal to 1/2. + if (delta_value % (2 * coefficient_value) != 0 && + static_cast(delta_value % (2 * coefficient_value)) / + static_cast(2 * coefficient_value) != + 0.5) { + PrintDebug( + "WeakCrossingSIVTest proved independence through distance escaping " + "the loop bounds."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DIRECTION; + distance_entry->direction = DistanceEntry::Directions::NONE; + return true; + } else { + distance = delta_value / (2 * coefficient_value); + } + + if (distance == 0) { + PrintDebug("WeakCrossingSIVTest found EQ dependence."); + distance_entry->dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + distance_entry->direction = DistanceEntry::Directions::EQ; + distance_entry->distance = 0; + return false; + } + } else { + PrintDebug( + "WeakCrossingSIVTest was unable to fold offset_delta and coefficient " + "to constants."); + } + + // We were unable to prove independence or discern any additional information. + // Must assume <=> direction. + PrintDebug( + "WeakCrossingSIVTest was unable to determine any dependence " + "information."); + distance_entry->direction = DistanceEntry::Directions::ALL; + return false; +} + +// Perform the GCD test if both, the source and the destination nodes, are in +// the form a0*i0 + a1*i1 + ... an*in + c. +bool LoopDependenceAnalysis::GCDMIVTest( + const std::pair& subscript_pair) { + auto source = std::get<0>(subscript_pair); + auto destination = std::get<1>(subscript_pair); + + // Bail out if source/destination is in an unexpected form. + if (!IsInCorrectFormForGCDTest(source) || + !IsInCorrectFormForGCDTest(destination)) { + return false; + } + + auto source_recurrences = GetAllTopLevelRecurrences(source); + auto dest_recurrences = GetAllTopLevelRecurrences(destination); + + // Bail out if all offsets and coefficients aren't constant. + if (!AreOffsetsAndCoefficientsConstant(source_recurrences) || + !AreOffsetsAndCoefficientsConstant(dest_recurrences)) { + return false; + } + + // Calculate the GCD of all coefficients. + auto source_constants = GetAllTopLevelConstants(source); + int64_t source_constant = + CalculateConstantTerm(source_recurrences, source_constants); + + auto dest_constants = GetAllTopLevelConstants(destination); + int64_t destination_constant = + CalculateConstantTerm(dest_recurrences, dest_constants); + + int64_t delta = std::abs(source_constant - destination_constant); + + int64_t running_gcd = 0; + + running_gcd = CalculateGCDFromCoefficients(source_recurrences, running_gcd); + running_gcd = CalculateGCDFromCoefficients(dest_recurrences, running_gcd); + + return delta % running_gcd != 0; +} + +using PartitionedSubscripts = + std::vector>>; +PartitionedSubscripts LoopDependenceAnalysis::PartitionSubscripts( + const std::vector& source_subscripts, + const std::vector& destination_subscripts) { + PartitionedSubscripts partitions{}; + + auto num_subscripts = source_subscripts.size(); + + // Create initial partitions with one subscript pair per partition. + for (size_t i = 0; i < num_subscripts; ++i) { + partitions.push_back({{source_subscripts[i], destination_subscripts[i]}}); + } + + // Iterate over the loops to create all partitions + for (auto loop : loops_) { + int64_t k = -1; + + for (size_t j = 0; j < partitions.size(); ++j) { + auto& current_partition = partitions[j]; + + // Does |loop| appear in |current_partition| + auto it = std::find_if( + current_partition.begin(), current_partition.end(), + [loop, + this](const std::pair& elem) -> bool { + auto source_recurrences = + scalar_evolution_.AnalyzeInstruction(std::get<0>(elem)) + ->CollectRecurrentNodes(); + auto destination_recurrences = + scalar_evolution_.AnalyzeInstruction(std::get<1>(elem)) + ->CollectRecurrentNodes(); + + source_recurrences.insert(source_recurrences.end(), + destination_recurrences.begin(), + destination_recurrences.end()); + + auto loops_in_pair = CollectLoops(source_recurrences); + auto end_it = loops_in_pair.end(); + + return std::find(loops_in_pair.begin(), end_it, loop) != end_it; + }); + + auto has_loop = it != current_partition.end(); + + if (has_loop) { + if (k == -1) { + k = j; + } else { + // Add |partitions[j]| to |partitions[k]| and discard |partitions[j]| + partitions[static_cast(k)].insert(current_partition.begin(), + current_partition.end()); + current_partition.clear(); + } + } + } + } + + // Remove empty (discarded) partitions + partitions.erase( + std::remove_if( + partitions.begin(), partitions.end(), + [](const std::set>& partition) { + return partition.empty(); + }), + partitions.end()); + + return partitions; +} + +Constraint* LoopDependenceAnalysis::IntersectConstraints( + Constraint* constraint_0, Constraint* constraint_1, + const SENode* lower_bound, const SENode* upper_bound) { + if (constraint_0->AsDependenceNone()) { + return constraint_1; + } else if (constraint_1->AsDependenceNone()) { + return constraint_0; + } + + // Both constraints are distances. Either the same distance or independent. + if (constraint_0->AsDependenceDistance() && + constraint_1->AsDependenceDistance()) { + auto dist_0 = constraint_0->AsDependenceDistance(); + auto dist_1 = constraint_1->AsDependenceDistance(); + + if (*dist_0->GetDistance() == *dist_1->GetDistance()) { + return constraint_0; + } else { + return make_constraint(); + } + } + + // Both constraints are points. Either the same point or independent. + if (constraint_0->AsDependencePoint() && constraint_1->AsDependencePoint()) { + auto point_0 = constraint_0->AsDependencePoint(); + auto point_1 = constraint_1->AsDependencePoint(); + + if (*point_0->GetSource() == *point_1->GetSource() && + *point_0->GetDestination() == *point_1->GetDestination()) { + return constraint_0; + } else { + return make_constraint(); + } + } + + // Both constraints are lines/distances. + if ((constraint_0->AsDependenceDistance() || + constraint_0->AsDependenceLine()) && + (constraint_1->AsDependenceDistance() || + constraint_1->AsDependenceLine())) { + auto is_distance_0 = constraint_0->AsDependenceDistance() != nullptr; + auto is_distance_1 = constraint_1->AsDependenceDistance() != nullptr; + + auto a0 = is_distance_0 ? scalar_evolution_.CreateConstant(1) + : constraint_0->AsDependenceLine()->GetA(); + auto b0 = is_distance_0 ? scalar_evolution_.CreateConstant(-1) + : constraint_0->AsDependenceLine()->GetB(); + auto c0 = + is_distance_0 + ? scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateNegation( + constraint_0->AsDependenceDistance()->GetDistance())) + : constraint_0->AsDependenceLine()->GetC(); + + auto a1 = is_distance_1 ? scalar_evolution_.CreateConstant(1) + : constraint_1->AsDependenceLine()->GetA(); + auto b1 = is_distance_1 ? scalar_evolution_.CreateConstant(-1) + : constraint_1->AsDependenceLine()->GetB(); + auto c1 = + is_distance_1 + ? scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateNegation( + constraint_1->AsDependenceDistance()->GetDistance())) + : constraint_1->AsDependenceLine()->GetC(); + + if (a0->AsSEConstantNode() && b0->AsSEConstantNode() && + c0->AsSEConstantNode() && a1->AsSEConstantNode() && + b1->AsSEConstantNode() && c1->AsSEConstantNode()) { + auto constant_a0 = a0->AsSEConstantNode()->FoldToSingleValue(); + auto constant_b0 = b0->AsSEConstantNode()->FoldToSingleValue(); + auto constant_c0 = c0->AsSEConstantNode()->FoldToSingleValue(); + + auto constant_a1 = a1->AsSEConstantNode()->FoldToSingleValue(); + auto constant_b1 = b1->AsSEConstantNode()->FoldToSingleValue(); + auto constant_c1 = c1->AsSEConstantNode()->FoldToSingleValue(); + + // a & b can't both be zero, otherwise it wouldn't be line. + if (NormalizeAndCompareFractions(constant_a0, constant_b0, constant_a1, + constant_b1)) { + // Slopes are equal, either parallel lines or the same line. + + if (constant_b0 == 0 && constant_b1 == 0) { + if (NormalizeAndCompareFractions(constant_c0, constant_a0, + constant_c1, constant_a1)) { + return constraint_0; + } + + return make_constraint(); + } else if (NormalizeAndCompareFractions(constant_c0, constant_b0, + constant_c1, constant_b1)) { + // Same line. + return constraint_0; + } else { + // Parallel lines can't intersect, report independence. + return make_constraint(); + } + + } else { + // Lines are not parallel, therefore, they must intersect. + + // Calculate intersection. + if (upper_bound->AsSEConstantNode() && + lower_bound->AsSEConstantNode()) { + auto constant_lower_bound = + lower_bound->AsSEConstantNode()->FoldToSingleValue(); + auto constant_upper_bound = + upper_bound->AsSEConstantNode()->FoldToSingleValue(); + + auto up = constant_b1 * constant_c0 - constant_b0 * constant_c1; + // Both b or both a can't be 0, so down is never 0 + // otherwise would have entered the parallel line section. + auto down = constant_b1 * constant_a0 - constant_b0 * constant_a1; + + auto x_coord = up / down; + + int64_t y_coord = 0; + int64_t arg1 = 0; + int64_t const_b_to_use = 0; + + if (constant_b1 != 0) { + arg1 = constant_c1 - constant_a1 * x_coord; + y_coord = arg1 / constant_b1; + const_b_to_use = constant_b1; + } else if (constant_b0 != 0) { + arg1 = constant_c0 - constant_a0 * x_coord; + y_coord = arg1 / constant_b0; + const_b_to_use = constant_b0; + } + + if (up % down == 0 && + arg1 % const_b_to_use == 0 && // Coordinates are integers. + constant_lower_bound <= + x_coord && // x_coord is within loop bounds. + x_coord <= constant_upper_bound && + constant_lower_bound <= + y_coord && // y_coord is within loop bounds. + y_coord <= constant_upper_bound) { + // Lines intersect at integer coordinates. + return make_constraint( + scalar_evolution_.CreateConstant(x_coord), + scalar_evolution_.CreateConstant(y_coord), + constraint_0->GetLoop()); + + } else { + return make_constraint(); + } + + } else { + // Not constants, bail out. + return make_constraint(); + } + } + + } else { + // Not constants, bail out. + return make_constraint(); + } + } + + // One constraint is a line/distance and the other is a point. + if ((constraint_0->AsDependencePoint() && + (constraint_1->AsDependenceLine() || + constraint_1->AsDependenceDistance())) || + (constraint_1->AsDependencePoint() && + (constraint_0->AsDependenceLine() || + constraint_0->AsDependenceDistance()))) { + auto point_0 = constraint_0->AsDependencePoint() != nullptr; + + auto point = point_0 ? constraint_0->AsDependencePoint() + : constraint_1->AsDependencePoint(); + + auto line_or_distance = point_0 ? constraint_1 : constraint_0; + + auto is_distance = line_or_distance->AsDependenceDistance() != nullptr; + + auto a = is_distance ? scalar_evolution_.CreateConstant(1) + : line_or_distance->AsDependenceLine()->GetA(); + auto b = is_distance ? scalar_evolution_.CreateConstant(-1) + : line_or_distance->AsDependenceLine()->GetB(); + auto c = + is_distance + ? scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateNegation( + line_or_distance->AsDependenceDistance()->GetDistance())) + : line_or_distance->AsDependenceLine()->GetC(); + + auto x = point->GetSource(); + auto y = point->GetDestination(); + + if (a->AsSEConstantNode() && b->AsSEConstantNode() && + c->AsSEConstantNode() && x->AsSEConstantNode() && + y->AsSEConstantNode()) { + auto constant_a = a->AsSEConstantNode()->FoldToSingleValue(); + auto constant_b = b->AsSEConstantNode()->FoldToSingleValue(); + auto constant_c = c->AsSEConstantNode()->FoldToSingleValue(); + + auto constant_x = x->AsSEConstantNode()->FoldToSingleValue(); + auto constant_y = y->AsSEConstantNode()->FoldToSingleValue(); + + auto left_hand_side = constant_a * constant_x + constant_b * constant_y; + + if (left_hand_side == constant_c) { + // Point is on line, return point + return point_0 ? constraint_0 : constraint_1; + } else { + // Point not on line, report independence (empty constraint). + return make_constraint(); + } + + } else { + // Not constants, bail out. + return make_constraint(); + } + } + + return nullptr; +} + +// Propagate constraints function as described in section 5 of Practical +// Dependence Testing, Goff, Kennedy, Tseng, 1991. +SubscriptPair LoopDependenceAnalysis::PropagateConstraints( + const SubscriptPair& subscript_pair, + const std::vector& constraints) { + SENode* new_first = subscript_pair.first; + SENode* new_second = subscript_pair.second; + + for (auto& constraint : constraints) { + // In the paper this is a[k]. We're extracting the coefficient ('a') of a + // recurrent expression with respect to the loop 'k'. + SENode* coefficient_of_recurrent = + scalar_evolution_.GetCoefficientFromRecurrentTerm( + new_first, constraint->GetLoop()); + + // In the paper this is a'[k]. + SENode* coefficient_of_recurrent_prime = + scalar_evolution_.GetCoefficientFromRecurrentTerm( + new_second, constraint->GetLoop()); + + if (constraint->GetType() == Constraint::Distance) { + DependenceDistance* as_distance = constraint->AsDependenceDistance(); + + // In the paper this is a[k]*d + SENode* rhs = scalar_evolution_.CreateMultiplyNode( + coefficient_of_recurrent, as_distance->GetDistance()); + + // In the paper this is a[k] <- 0 + SENode* zeroed_coefficient = + scalar_evolution_.BuildGraphWithoutRecurrentTerm( + new_first, constraint->GetLoop()); + + // In the paper this is e <- e - a[k]*d. + new_first = scalar_evolution_.CreateSubtraction(zeroed_coefficient, rhs); + new_first = scalar_evolution_.SimplifyExpression(new_first); + + // In the paper this is a'[k] - a[k]. + SENode* new_child = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(coefficient_of_recurrent_prime, + coefficient_of_recurrent)); + + // In the paper this is a'[k]'i[k]. + SERecurrentNode* prime_recurrent = + scalar_evolution_.GetRecurrentTerm(new_second, constraint->GetLoop()); + + if (!prime_recurrent) continue; + + // As we hash the nodes we need to create a new node when we update a + // child. + SENode* new_recurrent = scalar_evolution_.CreateRecurrentExpression( + constraint->GetLoop(), prime_recurrent->GetOffset(), new_child); + // In the paper this is a'[k] <- a'[k] - a[k]. + new_second = scalar_evolution_.UpdateChildNode( + new_second, prime_recurrent, new_recurrent); + } + } + + new_second = scalar_evolution_.SimplifyExpression(new_second); + return std::make_pair(new_first, new_second); +} + +bool LoopDependenceAnalysis::DeltaTest( + const std::vector& coupled_subscripts, + DistanceVector* dv_entry) { + std::vector constraints(loops_.size()); + + std::vector loop_appeared(loops_.size()); + + std::generate(std::begin(constraints), std::end(constraints), + [this]() { return make_constraint(); }); + + // Separate SIV and MIV subscripts + std::vector siv_subscripts{}; + std::vector miv_subscripts{}; + + for (const auto& subscript_pair : coupled_subscripts) { + if (IsSIV(subscript_pair)) { + siv_subscripts.push_back(subscript_pair); + } else { + miv_subscripts.push_back(subscript_pair); + } + } + + // Delta Test + while (!siv_subscripts.empty()) { + std::vector results(siv_subscripts.size()); + + std::vector current_distances( + siv_subscripts.size(), DistanceVector(loops_.size())); + + // Apply SIV test to all SIV subscripts, report independence if any of them + // is independent + std::transform( + std::begin(siv_subscripts), std::end(siv_subscripts), + std::begin(current_distances), std::begin(results), + [this](SubscriptPair& p, DistanceVector& d) { return SIVTest(p, &d); }); + + if (std::accumulate(std::begin(results), std::end(results), false, + std::logical_or{})) { + return true; + } + + // Derive new constraint vector. + std::vector> all_new_constrants{}; + + for (size_t i = 0; i < siv_subscripts.size(); ++i) { + auto loop = GetLoopForSubscriptPair(siv_subscripts[i]); + + auto loop_id = + std::distance(std::begin(loops_), + std::find(std::begin(loops_), std::end(loops_), loop)); + + loop_appeared[loop_id] = true; + auto distance_entry = current_distances[i].GetEntries()[loop_id]; + + if (distance_entry.dependence_information == + DistanceEntry::DependenceInformation::DISTANCE) { + // Construct a DependenceDistance. + auto node = scalar_evolution_.CreateConstant(distance_entry.distance); + + all_new_constrants.push_back( + {make_constraint(node, loop), loop_id}); + } else { + // Construct a DependenceLine. + const auto& subscript_pair = siv_subscripts[i]; + SENode* source_node = std::get<0>(subscript_pair); + SENode* destination_node = std::get<1>(subscript_pair); + + int64_t source_induction_count = CountInductionVariables(source_node); + int64_t destination_induction_count = + CountInductionVariables(destination_node); + + SENode* a = nullptr; + SENode* b = nullptr; + SENode* c = nullptr; + + if (destination_induction_count != 0) { + a = destination_node->AsSERecurrentNode()->GetCoefficient(); + c = scalar_evolution_.CreateNegation( + destination_node->AsSERecurrentNode()->GetOffset()); + } else { + a = scalar_evolution_.CreateConstant(0); + c = scalar_evolution_.CreateNegation(destination_node); + } + + if (source_induction_count != 0) { + b = scalar_evolution_.CreateNegation( + source_node->AsSERecurrentNode()->GetCoefficient()); + c = scalar_evolution_.CreateAddNode( + c, source_node->AsSERecurrentNode()->GetOffset()); + } else { + b = scalar_evolution_.CreateConstant(0); + c = scalar_evolution_.CreateAddNode(c, source_node); + } + + a = scalar_evolution_.SimplifyExpression(a); + b = scalar_evolution_.SimplifyExpression(b); + c = scalar_evolution_.SimplifyExpression(c); + + all_new_constrants.push_back( + {make_constraint(a, b, c, loop), loop_id}); + } + } + + // Calculate the intersection between the new and existing constraints. + std::vector intersection = constraints; + for (const auto& constraint_to_intersect : all_new_constrants) { + auto loop_id = std::get<1>(constraint_to_intersect); + auto loop = loops_[loop_id]; + intersection[loop_id] = IntersectConstraints( + intersection[loop_id], std::get<0>(constraint_to_intersect), + GetLowerBound(loop), GetUpperBound(loop)); + } + + // Report independence if an empty constraint (DependenceEmpty) is found. + auto first_empty = + std::find_if(std::begin(intersection), std::end(intersection), + [](Constraint* constraint) { + return constraint->AsDependenceEmpty() != nullptr; + }); + if (first_empty != std::end(intersection)) { + return true; + } + std::vector new_siv_subscripts{}; + std::vector new_miv_subscripts{}; + + auto equal = + std::equal(std::begin(constraints), std::end(constraints), + std::begin(intersection), + [](Constraint* a, Constraint* b) { return *a == *b; }); + + // If any constraints have changed, propagate them into the rest of the + // subscripts possibly creating new ZIV/SIV subscripts. + if (!equal) { + std::vector new_subscripts(miv_subscripts.size()); + + // Propagate constraints into MIV subscripts + std::transform(std::begin(miv_subscripts), std::end(miv_subscripts), + std::begin(new_subscripts), + [this, &intersection](SubscriptPair& subscript_pair) { + return PropagateConstraints(subscript_pair, + intersection); + }); + + // If a ZIV subscript is returned, apply test, otherwise, update untested + // subscripts. + for (auto& subscript : new_subscripts) { + if (IsZIV(subscript) && ZIVTest(subscript)) { + return true; + } else if (IsSIV(subscript)) { + new_siv_subscripts.push_back(subscript); + } else { + new_miv_subscripts.push_back(subscript); + } + } + } + + // Set new constraints and subscripts to test. + std::swap(siv_subscripts, new_siv_subscripts); + std::swap(miv_subscripts, new_miv_subscripts); + std::swap(constraints, intersection); + } + + // Create the dependence vector from the constraints. + for (size_t i = 0; i < loops_.size(); ++i) { + // Don't touch entries for loops that weren't tested. + if (loop_appeared[i]) { + auto current_constraint = constraints[i]; + auto& current_distance_entry = (*dv_entry).GetEntries()[i]; + + if (auto dependence_distance = + current_constraint->AsDependenceDistance()) { + if (auto constant_node = + dependence_distance->GetDistance()->AsSEConstantNode()) { + current_distance_entry.dependence_information = + DistanceEntry::DependenceInformation::DISTANCE; + + current_distance_entry.distance = constant_node->FoldToSingleValue(); + if (current_distance_entry.distance == 0) { + current_distance_entry.direction = DistanceEntry::Directions::EQ; + } else if (current_distance_entry.distance < 0) { + current_distance_entry.direction = DistanceEntry::Directions::GT; + } else { + current_distance_entry.direction = DistanceEntry::Directions::LT; + } + } + } else if (auto dependence_point = + current_constraint->AsDependencePoint()) { + auto source = dependence_point->GetSource(); + auto destination = dependence_point->GetDestination(); + + if (source->AsSEConstantNode() && destination->AsSEConstantNode()) { + current_distance_entry = DistanceEntry( + source->AsSEConstantNode()->FoldToSingleValue(), + destination->AsSEConstantNode()->FoldToSingleValue()); + } + } + } + } + + // Test any remaining MIV subscripts and report independence if found. + std::vector results(miv_subscripts.size()); + + std::transform(std::begin(miv_subscripts), std::end(miv_subscripts), + std::begin(results), + [this](const SubscriptPair& p) { return GCDMIVTest(p); }); + + return std::accumulate(std::begin(results), std::end(results), false, + std::logical_or{}); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_dependence.h b/third_party/spirv-tools/source/opt/loop_dependence.h new file mode 100644 index 0000000..03a9075 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_dependence.h @@ -0,0 +1,560 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_DEPENDENCE_H_ +#define SOURCE_OPT_LOOP_DEPENDENCE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/scalar_analysis.h" + +namespace spvtools { +namespace opt { + +// Stores information about dependence between a load and a store wrt a single +// loop in a loop nest. +// DependenceInformation +// * UNKNOWN if no dependence information can be gathered or is gathered +// for it. +// * DIRECTION if a dependence direction could be found, but not a +// distance. +// * DISTANCE if a dependence distance could be found. +// * PEEL if peeling either the first or last iteration will break +// dependence between the given load and store. +// * IRRELEVANT if it has no effect on the dependence between the given +// load and store. +// +// If peel_first == true, the analysis has found that peeling the first +// iteration of this loop will break dependence. +// +// If peel_last == true, the analysis has found that peeling the last iteration +// of this loop will break dependence. +class DistanceEntry { + public: + enum DependenceInformation { + UNKNOWN = 0, + DIRECTION = 1, + DISTANCE = 2, + PEEL = 3, + IRRELEVANT = 4, + POINT = 5 + }; + enum Directions { + NONE = 0, + LT = 1, + EQ = 2, + LE = 3, + GT = 4, + NE = 5, + GE = 6, + ALL = 7 + }; + DependenceInformation dependence_information; + Directions direction; + int64_t distance; + bool peel_first; + bool peel_last; + int64_t point_x; + int64_t point_y; + + DistanceEntry() + : dependence_information(DependenceInformation::UNKNOWN), + direction(Directions::ALL), + distance(0), + peel_first(false), + peel_last(false), + point_x(0), + point_y(0) {} + + explicit DistanceEntry(Directions direction_) + : dependence_information(DependenceInformation::DIRECTION), + direction(direction_), + distance(0), + peel_first(false), + peel_last(false), + point_x(0), + point_y(0) {} + + DistanceEntry(Directions direction_, int64_t distance_) + : dependence_information(DependenceInformation::DISTANCE), + direction(direction_), + distance(distance_), + peel_first(false), + peel_last(false), + point_x(0), + point_y(0) {} + + DistanceEntry(int64_t x, int64_t y) + : dependence_information(DependenceInformation::POINT), + direction(Directions::ALL), + distance(0), + peel_first(false), + peel_last(false), + point_x(x), + point_y(y) {} + + bool operator==(const DistanceEntry& rhs) const { + return direction == rhs.direction && peel_first == rhs.peel_first && + peel_last == rhs.peel_last && distance == rhs.distance && + point_x == rhs.point_x && point_y == rhs.point_y; + } + + bool operator!=(const DistanceEntry& rhs) const { return !(*this == rhs); } +}; + +// Stores a vector of DistanceEntrys, one per loop in the analysis. +// A DistanceVector holds all of the information gathered in a dependence +// analysis wrt the loops stored in the LoopDependenceAnalysis performing the +// analysis. +class DistanceVector { + public: + explicit DistanceVector(size_t size) : entries(size, DistanceEntry{}) {} + + explicit DistanceVector(std::vector entries_) + : entries(entries_) {} + + DistanceEntry& GetEntry(size_t index) { return entries[index]; } + const DistanceEntry& GetEntry(size_t index) const { return entries[index]; } + + std::vector& GetEntries() { return entries; } + const std::vector& GetEntries() const { return entries; } + + bool operator==(const DistanceVector& rhs) const { + if (entries.size() != rhs.entries.size()) { + return false; + } + for (size_t i = 0; i < entries.size(); ++i) { + if (entries[i] != rhs.entries[i]) { + return false; + } + } + return true; + } + bool operator!=(const DistanceVector& rhs) const { return !(*this == rhs); } + + private: + std::vector entries; +}; + +class DependenceLine; +class DependenceDistance; +class DependencePoint; +class DependenceNone; +class DependenceEmpty; + +class Constraint { + public: + explicit Constraint(const Loop* loop) : loop_(loop) {} + enum ConstraintType { Line, Distance, Point, None, Empty }; + + virtual ConstraintType GetType() const = 0; + + virtual ~Constraint() {} + + // Get the loop this constraint belongs to. + const Loop* GetLoop() const { return loop_; } + + bool operator==(const Constraint& other) const; + + bool operator!=(const Constraint& other) const; + +// clang-format off +#define DeclareCastMethod(target) \ + virtual target* As##target() { return nullptr; } \ + virtual const target* As##target() const { return nullptr; } + DeclareCastMethod(DependenceLine) + DeclareCastMethod(DependenceDistance) + DeclareCastMethod(DependencePoint) + DeclareCastMethod(DependenceNone) + DeclareCastMethod(DependenceEmpty) +#undef DeclareCastMethod + + protected: + const Loop* loop_; +}; +// clang-format on + +class DependenceLine : public Constraint { + public: + DependenceLine(SENode* a, SENode* b, SENode* c, const Loop* loop) + : Constraint(loop), a_(a), b_(b), c_(c) {} + + ConstraintType GetType() const final { return Line; } + + DependenceLine* AsDependenceLine() final { return this; } + const DependenceLine* AsDependenceLine() const final { return this; } + + SENode* GetA() const { return a_; } + SENode* GetB() const { return b_; } + SENode* GetC() const { return c_; } + + private: + SENode* a_; + SENode* b_; + SENode* c_; +}; + +class DependenceDistance : public Constraint { + public: + DependenceDistance(SENode* distance, const Loop* loop) + : Constraint(loop), distance_(distance) {} + + ConstraintType GetType() const final { return Distance; } + + DependenceDistance* AsDependenceDistance() final { return this; } + const DependenceDistance* AsDependenceDistance() const final { return this; } + + SENode* GetDistance() const { return distance_; } + + private: + SENode* distance_; +}; + +class DependencePoint : public Constraint { + public: + DependencePoint(SENode* source, SENode* destination, const Loop* loop) + : Constraint(loop), source_(source), destination_(destination) {} + + ConstraintType GetType() const final { return Point; } + + DependencePoint* AsDependencePoint() final { return this; } + const DependencePoint* AsDependencePoint() const final { return this; } + + SENode* GetSource() const { return source_; } + SENode* GetDestination() const { return destination_; } + + private: + SENode* source_; + SENode* destination_; +}; + +class DependenceNone : public Constraint { + public: + DependenceNone() : Constraint(nullptr) {} + ConstraintType GetType() const final { return None; } + + DependenceNone* AsDependenceNone() final { return this; } + const DependenceNone* AsDependenceNone() const final { return this; } +}; + +class DependenceEmpty : public Constraint { + public: + DependenceEmpty() : Constraint(nullptr) {} + ConstraintType GetType() const final { return Empty; } + + DependenceEmpty* AsDependenceEmpty() final { return this; } + const DependenceEmpty* AsDependenceEmpty() const final { return this; } +}; + +// Provides dependence information between a store instruction and a load +// instruction inside the same loop in a loop nest. +// +// The analysis can only check dependence between stores and loads with regard +// to the loop nest it is created with. +// +// The analysis can output debugging information to a stream. The output +// describes the control flow of the analysis and what information it can deduce +// at each step. +// SetDebugStream and ClearDebugStream are provided for this functionality. +// +// The dependency algorithm is based on the 1990 Paper +// Practical Dependence Testing +// Gina Goff, Ken Kennedy, Chau-Wen Tseng +// +// The algorithm first identifies subscript pairs between the load and store. +// Each pair is tested until all have been tested or independence is found. +// The number of induction variables in a pair determines which test to perform +// on it; +// Zero Index Variable (ZIV) is used when no induction variables are present +// in the pair. +// Single Index Variable (SIV) is used when only one induction variable is +// present, but may occur multiple times in the pair. +// Multiple Index Variable (MIV) is used when more than one induction variable +// is present in the pair. +class LoopDependenceAnalysis { + public: + LoopDependenceAnalysis(IRContext* context, std::vector loops) + : context_(context), + loops_(loops), + scalar_evolution_(context), + debug_stream_(nullptr), + constraints_{} {} + + // Finds the dependence between |source| and |destination|. + // |source| should be an OpLoad. + // |destination| should be an OpStore. + // Any direction and distance information found will be stored in + // |distance_vector|. + // Returns true if independence is found, false otherwise. + bool GetDependence(const Instruction* source, const Instruction* destination, + DistanceVector* distance_vector); + + // Returns true if |subscript_pair| represents a Zero Index Variable pair + // (ZIV) + bool IsZIV(const std::pair& subscript_pair); + + // Returns true if |subscript_pair| represents a Single Index Variable + // (SIV) pair + bool IsSIV(const std::pair& subscript_pair); + + // Returns true if |subscript_pair| represents a Multiple Index Variable + // (MIV) pair + bool IsMIV(const std::pair& subscript_pair); + + // Finds the lower bound of |loop| as an SENode* and returns the result. + // The lower bound is the starting value of the loops induction variable + SENode* GetLowerBound(const Loop* loop); + + // Finds the upper bound of |loop| as an SENode* and returns the result. + // The upper bound is the last value before the loop exit condition is met. + SENode* GetUpperBound(const Loop* loop); + + // Returns true if |value| is between |bound_one| and |bound_two| (inclusive). + bool IsWithinBounds(int64_t value, int64_t bound_one, int64_t bound_two); + + // Finds the bounds of |loop| as upper_bound - lower_bound and returns the + // resulting SENode. + // If the operations can not be completed a nullptr is returned. + SENode* GetTripCount(const Loop* loop); + + // Returns the SENode* produced by building an SENode from the result of + // calling GetInductionInitValue on |loop|. + // If the operation can not be completed a nullptr is returned. + SENode* GetFirstTripInductionNode(const Loop* loop); + + // Returns the SENode* produced by building an SENode from the result of + // GetFirstTripInductionNode + (GetTripCount - 1) * induction_coefficient. + // If the operation can not be completed a nullptr is returned. + SENode* GetFinalTripInductionNode(const Loop* loop, + SENode* induction_coefficient); + + // Returns all the distinct loops that appear in |nodes|. + std::set CollectLoops( + const std::vector& nodes); + + // Returns all the distinct loops that appear in |source| and |destination|. + std::set CollectLoops(SENode* source, SENode* destination); + + // Returns true if |distance| is provably outside the loop bounds. + // |coefficient| must be an SENode representing the coefficient of the + // induction variable of |loop|. + // This method is able to handle some symbolic cases which IsWithinBounds + // can't handle. + bool IsProvablyOutsideOfLoopBounds(const Loop* loop, SENode* distance, + SENode* coefficient); + + // Sets the ostream for debug information for the analysis. + void SetDebugStream(std::ostream& debug_stream) { + debug_stream_ = &debug_stream; + } + + // Clears the stored ostream to stop debug information printing. + void ClearDebugStream() { debug_stream_ = nullptr; } + + // Returns the ScalarEvolutionAnalysis used by this analysis. + ScalarEvolutionAnalysis* GetScalarEvolution() { return &scalar_evolution_; } + + // Creates a new constraint of type |T| and returns the pointer to it. + template + Constraint* make_constraint(Args&&... args) { + constraints_.push_back( + std::unique_ptr(new T(std::forward(args)...))); + + return constraints_.back().get(); + } + + // Subscript partitioning as described in Figure 1 of 'Practical Dependence + // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91. + // Partitions the subscripts into independent subscripts and minimally coupled + // sets of subscripts. + // Returns the partitioning of subscript pairs. Sets of size 1 indicates an + // independent subscript-pair and others indicate coupled sets. + using PartitionedSubscripts = + std::vector>>; + PartitionedSubscripts PartitionSubscripts( + const std::vector& source_subscripts, + const std::vector& destination_subscripts); + + // Returns the Loop* matching the loop for |subscript_pair|. + // |subscript_pair| must be an SIV pair. + const Loop* GetLoopForSubscriptPair( + const std::pair& subscript_pair); + + // Returns the DistanceEntry matching the loop for |subscript_pair|. + // |subscript_pair| must be an SIV pair. + DistanceEntry* GetDistanceEntryForSubscriptPair( + const std::pair& subscript_pair, + DistanceVector* distance_vector); + + // Returns the DistanceEntry matching |loop|. + DistanceEntry* GetDistanceEntryForLoop(const Loop* loop, + DistanceVector* distance_vector); + + // Returns a vector of Instruction* which form the subscripts of the array + // access defined by the access chain |instruction|. + std::vector GetSubscripts(const Instruction* instruction); + + // Delta test as described in Figure 3 of 'Practical Dependence + // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91. + bool DeltaTest( + const std::vector>& coupled_subscripts, + DistanceVector* dv_entry); + + // Constraint propagation as described in Figure 5 of 'Practical Dependence + // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91. + std::pair PropagateConstraints( + const std::pair& subscript_pair, + const std::vector& constraints); + + // Constraint intersection as described in Figure 4 of 'Practical Dependence + // Testing' by Gina Goff, Ken Kennedy, and Chau-Wen Tseng from PLDI '91. + Constraint* IntersectConstraints(Constraint* constraint_0, + Constraint* constraint_1, + const SENode* lower_bound, + const SENode* upper_bound); + + // Returns true if each loop in |loops| is in a form supported by this + // analysis. + // A loop is supported if it has a single induction variable and that + // induction variable has a step of +1 or -1 per loop iteration. + bool CheckSupportedLoops(std::vector loops); + + // Returns true if |loop| is in a form supported by this analysis. + // A loop is supported if it has a single induction variable and that + // induction variable has a step of +1 or -1 per loop iteration. + bool IsSupportedLoop(const Loop* loop); + + private: + IRContext* context_; + + // The loop nest we are analysing the dependence of. + std::vector loops_; + + // The ScalarEvolutionAnalysis used by this analysis to store and perform much + // of its logic. + ScalarEvolutionAnalysis scalar_evolution_; + + // The ostream debug information for the analysis to print to. + std::ostream* debug_stream_; + + // Stores all the constraints created by the analysis. + std::list> constraints_; + + // Returns true if independence can be proven and false if it can't be proven. + bool ZIVTest(const std::pair& subscript_pair); + + // Analyzes the subscript pair to find an applicable SIV test. + // Returns true if independence can be proven and false if it can't be proven. + bool SIVTest(const std::pair& subscript_pair, + DistanceVector* distance_vector); + + // Takes the form a*i + c1, a*i + c2 + // When c1 and c2 are loop invariant and a is constant + // distance = (c1 - c2)/a + // < if distance > 0 + // direction = = if distance = 0 + // > if distance < 0 + // Returns true if independence is proven and false if it can't be proven. + bool StrongSIVTest(SENode* source, SENode* destination, SENode* coeff, + DistanceEntry* distance_entry); + + // Takes for form a*i + c1, a*i + c2 + // where c1 and c2 are loop invariant and a is constant. + // c1 and/or c2 contain one or more SEValueUnknown nodes. + bool SymbolicStrongSIVTest(SENode* source, SENode* destination, + SENode* coefficient, + DistanceEntry* distance_entry); + + // Takes the form a1*i + c1, a2*i + c2 + // where a1 = 0 + // distance = (c1 - c2) / a2 + // Returns true if independence is proven and false if it can't be proven. + bool WeakZeroSourceSIVTest(SENode* source, SERecurrentNode* destination, + SENode* coefficient, + DistanceEntry* distance_entry); + + // Takes the form a1*i + c1, a2*i + c2 + // where a2 = 0 + // distance = (c2 - c1) / a1 + // Returns true if independence is proven and false if it can't be proven. + bool WeakZeroDestinationSIVTest(SERecurrentNode* source, SENode* destination, + SENode* coefficient, + DistanceEntry* distance_entry); + + // Takes the form a1*i + c1, a2*i + c2 + // where a1 = -a2 + // distance = (c2 - c1) / 2*a1 + // Returns true if independence is proven and false if it can't be proven. + bool WeakCrossingSIVTest(SENode* source, SENode* destination, + SENode* coefficient, DistanceEntry* distance_entry); + + // Uses the def_use_mgr to get the instruction referenced by + // SingleWordInOperand(|id|) when called on |instruction|. + Instruction* GetOperandDefinition(const Instruction* instruction, int id); + + // Perform the GCD test if both, the source and the destination nodes, are in + // the form a0*i0 + a1*i1 + ... an*in + c. + bool GCDMIVTest(const std::pair& subscript_pair); + + // Finds the number of induction variables in |node|. + // Returns -1 on failure. + int64_t CountInductionVariables(SENode* node); + + // Finds the number of induction variables shared between |source| and + // |destination|. + // Returns -1 on failure. + int64_t CountInductionVariables(SENode* source, SENode* destination); + + // Takes the offset from the induction variable and subtracts the lower bound + // from it to get the constant term added to the induction. + // Returns the resuting constant term, or nullptr if it could not be produced. + SENode* GetConstantTerm(const Loop* loop, SERecurrentNode* induction); + + // Marks all the distance entries in |distance_vector| that were relate to + // loops in |loops_| but were not used in any subscripts as irrelevant to the + // to the dependence test. + void MarkUnsusedDistanceEntriesAsIrrelevant(const Instruction* source, + const Instruction* destination, + DistanceVector* distance_vector); + + // Converts |value| to a std::string and returns the result. + // This is required because Android does not compile std::to_string. + template + std::string ToString(valueT value) { + std::ostringstream string_stream; + string_stream << value; + return string_stream.str(); + } + + // Prints |debug_msg| and "\n" to the ostream pointed to by |debug_stream_|. + // Won't print anything if |debug_stream_| is nullptr. + void PrintDebug(std::string debug_msg); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_DEPENDENCE_H_ diff --git a/third_party/spirv-tools/source/opt/loop_dependence_helpers.cpp b/third_party/spirv-tools/source/opt/loop_dependence_helpers.cpp new file mode 100644 index 0000000..de27a0a --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_dependence_helpers.cpp @@ -0,0 +1,541 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_dependence.h" + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/scalar_analysis_nodes.h" + +namespace spvtools { +namespace opt { + +bool LoopDependenceAnalysis::IsZIV( + const std::pair& subscript_pair) { + return CountInductionVariables(subscript_pair.first, subscript_pair.second) == + 0; +} + +bool LoopDependenceAnalysis::IsSIV( + const std::pair& subscript_pair) { + return CountInductionVariables(subscript_pair.first, subscript_pair.second) == + 1; +} + +bool LoopDependenceAnalysis::IsMIV( + const std::pair& subscript_pair) { + return CountInductionVariables(subscript_pair.first, subscript_pair.second) > + 1; +} + +SENode* LoopDependenceAnalysis::GetLowerBound(const Loop* loop) { + Instruction* cond_inst = loop->GetConditionInst(); + if (!cond_inst) { + return nullptr; + } + Instruction* lower_inst = GetOperandDefinition(cond_inst, 0); + switch (cond_inst->opcode()) { + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: { + // If we have a phi we are looking at the induction variable. We look + // through the phi to the initial value of the phi upon entering the loop. + if (lower_inst->opcode() == SpvOpPhi) { + lower_inst = GetOperandDefinition(lower_inst, 0); + // We don't handle looking through multiple phis. + if (lower_inst->opcode() == SpvOpPhi) { + return nullptr; + } + } + return scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(lower_inst)); + } + default: + return nullptr; + } +} + +SENode* LoopDependenceAnalysis::GetUpperBound(const Loop* loop) { + Instruction* cond_inst = loop->GetConditionInst(); + if (!cond_inst) { + return nullptr; + } + Instruction* upper_inst = GetOperandDefinition(cond_inst, 1); + switch (cond_inst->opcode()) { + case SpvOpULessThan: + case SpvOpSLessThan: { + // When we have a < condition we must subtract 1 from the analyzed upper + // instruction. + SENode* upper_bound = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction( + scalar_evolution_.AnalyzeInstruction(upper_inst), + scalar_evolution_.CreateConstant(1))); + return upper_bound; + } + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: { + // When we have a > condition we must add 1 to the analyzed upper + // instruction. + SENode* upper_bound = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + scalar_evolution_.AnalyzeInstruction(upper_inst), + scalar_evolution_.CreateConstant(1))); + return upper_bound; + } + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: { + // We don't need to modify the results of analyzing when we have <= or >=. + SENode* upper_bound = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(upper_inst)); + return upper_bound; + } + default: + return nullptr; + } +} + +bool LoopDependenceAnalysis::IsWithinBounds(int64_t value, int64_t bound_one, + int64_t bound_two) { + if (bound_one < bound_two) { + // If |bound_one| is the lower bound. + return (value >= bound_one && value <= bound_two); + } else if (bound_one > bound_two) { + // If |bound_two| is the lower bound. + return (value >= bound_two && value <= bound_one); + } else { + // Both bounds have the same value. + return value == bound_one; + } +} + +bool LoopDependenceAnalysis::IsProvablyOutsideOfLoopBounds( + const Loop* loop, SENode* distance, SENode* coefficient) { + // We test to see if we can reduce the coefficient to an integral constant. + SEConstantNode* coefficient_constant = coefficient->AsSEConstantNode(); + if (!coefficient_constant) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds could not reduce coefficient to a " + "SEConstantNode so must exit."); + return false; + } + + SENode* lower_bound = GetLowerBound(loop); + SENode* upper_bound = GetUpperBound(loop); + if (!lower_bound || !upper_bound) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds could not get both the lower and upper " + "bounds so must exit."); + return false; + } + // If the coefficient is positive we calculate bounds as upper - lower + // If the coefficient is negative we calculate bounds as lower - upper + SENode* bounds = nullptr; + if (coefficient_constant->FoldToSingleValue() >= 0) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds found coefficient >= 0.\n" + "Using bounds as upper - lower."); + bounds = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(upper_bound, lower_bound)); + } else { + PrintDebug( + "IsProvablyOutsideOfLoopBounds found coefficient < 0.\n" + "Using bounds as lower - upper."); + bounds = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(lower_bound, upper_bound)); + } + + // We can attempt to deal with symbolic cases by subtracting |distance| and + // the bound nodes. If we can subtract, simplify and produce a SEConstantNode + // we can produce some information. + SEConstantNode* distance_minus_bounds = + scalar_evolution_ + .SimplifyExpression( + scalar_evolution_.CreateSubtraction(distance, bounds)) + ->AsSEConstantNode(); + if (distance_minus_bounds) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds found distance - bounds as a " + "SEConstantNode with value " + + ToString(distance_minus_bounds->FoldToSingleValue())); + // If distance - bounds > 0 we prove the distance is outwith the loop + // bounds. + if (distance_minus_bounds->FoldToSingleValue() > 0) { + PrintDebug( + "IsProvablyOutsideOfLoopBounds found distance escaped the loop " + "bounds."); + return true; + } + } + + return false; +} + +const Loop* LoopDependenceAnalysis::GetLoopForSubscriptPair( + const std::pair& subscript_pair) { + // Collect all the SERecurrentNodes. + std::vector source_nodes = + std::get<0>(subscript_pair)->CollectRecurrentNodes(); + std::vector destination_nodes = + std::get<1>(subscript_pair)->CollectRecurrentNodes(); + + // Collect all the loops stored by the SERecurrentNodes. + std::unordered_set loops{}; + for (auto source_nodes_it = source_nodes.begin(); + source_nodes_it != source_nodes.end(); ++source_nodes_it) { + loops.insert((*source_nodes_it)->GetLoop()); + } + for (auto destination_nodes_it = destination_nodes.begin(); + destination_nodes_it != destination_nodes.end(); + ++destination_nodes_it) { + loops.insert((*destination_nodes_it)->GetLoop()); + } + + // If we didn't find 1 loop |subscript_pair| is a subscript over multiple or 0 + // loops. We don't handle this so return nullptr. + if (loops.size() != 1) { + PrintDebug("GetLoopForSubscriptPair found loops.size() != 1."); + return nullptr; + } + return *loops.begin(); +} + +DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForLoop( + const Loop* loop, DistanceVector* distance_vector) { + if (!loop) { + return nullptr; + } + + DistanceEntry* distance_entry = nullptr; + for (size_t loop_index = 0; loop_index < loops_.size(); ++loop_index) { + if (loop == loops_[loop_index]) { + distance_entry = &(distance_vector->GetEntries()[loop_index]); + break; + } + } + + return distance_entry; +} + +DistanceEntry* LoopDependenceAnalysis::GetDistanceEntryForSubscriptPair( + const std::pair& subscript_pair, + DistanceVector* distance_vector) { + const Loop* loop = GetLoopForSubscriptPair(subscript_pair); + + return GetDistanceEntryForLoop(loop, distance_vector); +} + +SENode* LoopDependenceAnalysis::GetTripCount(const Loop* loop) { + BasicBlock* condition_block = loop->FindConditionBlock(); + if (!condition_block) { + return nullptr; + } + Instruction* induction_instr = loop->FindConditionVariable(condition_block); + if (!induction_instr) { + return nullptr; + } + Instruction* cond_instr = loop->GetConditionInst(); + if (!cond_instr) { + return nullptr; + } + + size_t iteration_count = 0; + + // We have to check the instruction type here. If the condition instruction + // isn't a supported type we can't calculate the trip count. + if (loop->IsSupportedCondition(cond_instr->opcode())) { + if (loop->FindNumberOfIterations(induction_instr, &*condition_block->tail(), + &iteration_count)) { + return scalar_evolution_.CreateConstant( + static_cast(iteration_count)); + } + } + + return nullptr; +} + +SENode* LoopDependenceAnalysis::GetFirstTripInductionNode(const Loop* loop) { + BasicBlock* condition_block = loop->FindConditionBlock(); + if (!condition_block) { + return nullptr; + } + Instruction* induction_instr = loop->FindConditionVariable(condition_block); + if (!induction_instr) { + return nullptr; + } + int64_t induction_initial_value = 0; + if (!loop->GetInductionInitValue(induction_instr, &induction_initial_value)) { + return nullptr; + } + + SENode* induction_init_SENode = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateConstant(induction_initial_value)); + return induction_init_SENode; +} + +SENode* LoopDependenceAnalysis::GetFinalTripInductionNode( + const Loop* loop, SENode* induction_coefficient) { + SENode* first_trip_induction_node = GetFirstTripInductionNode(loop); + if (!first_trip_induction_node) { + return nullptr; + } + // Get trip_count as GetTripCount - 1 + // This is because the induction variable is not stepped on the first + // iteration of the loop + SENode* trip_count = + scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateSubtraction( + GetTripCount(loop), scalar_evolution_.CreateConstant(1))); + // Return first_trip_induction_node + trip_count * induction_coefficient + return scalar_evolution_.SimplifyExpression(scalar_evolution_.CreateAddNode( + first_trip_induction_node, + scalar_evolution_.CreateMultiplyNode(trip_count, induction_coefficient))); +} + +std::set LoopDependenceAnalysis::CollectLoops( + const std::vector& recurrent_nodes) { + // We don't handle loops with more than one induction variable. Therefore we + // can identify the number of induction variables by collecting all of the + // loops the collected recurrent nodes belong to. + std::set loops{}; + for (auto recurrent_nodes_it = recurrent_nodes.begin(); + recurrent_nodes_it != recurrent_nodes.end(); ++recurrent_nodes_it) { + loops.insert((*recurrent_nodes_it)->GetLoop()); + } + + return loops; +} + +int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* node) { + if (!node) { + return -1; + } + + std::vector recurrent_nodes = node->CollectRecurrentNodes(); + + // We don't handle loops with more than one induction variable. Therefore we + // can identify the number of induction variables by collecting all of the + // loops the collected recurrent nodes belong to. + std::set loops = CollectLoops(recurrent_nodes); + + return static_cast(loops.size()); +} + +std::set LoopDependenceAnalysis::CollectLoops( + SENode* source, SENode* destination) { + if (!source || !destination) { + return std::set{}; + } + + std::vector source_nodes = source->CollectRecurrentNodes(); + std::vector destination_nodes = + destination->CollectRecurrentNodes(); + + std::set loops = CollectLoops(source_nodes); + std::set destination_loops = CollectLoops(destination_nodes); + + loops.insert(std::begin(destination_loops), std::end(destination_loops)); + + return loops; +} + +int64_t LoopDependenceAnalysis::CountInductionVariables(SENode* source, + SENode* destination) { + if (!source || !destination) { + return -1; + } + + std::set loops = CollectLoops(source, destination); + + return static_cast(loops.size()); +} + +Instruction* LoopDependenceAnalysis::GetOperandDefinition( + const Instruction* instruction, int id) { + return context_->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(id)); +} + +std::vector LoopDependenceAnalysis::GetSubscripts( + const Instruction* instruction) { + Instruction* access_chain = GetOperandDefinition(instruction, 0); + + std::vector subscripts; + + for (auto i = 1u; i < access_chain->NumInOperandWords(); ++i) { + subscripts.push_back(GetOperandDefinition(access_chain, i)); + } + + return subscripts; +} + +SENode* LoopDependenceAnalysis::GetConstantTerm(const Loop* loop, + SERecurrentNode* induction) { + SENode* offset = induction->GetOffset(); + SENode* lower_bound = GetLowerBound(loop); + if (!offset || !lower_bound) { + return nullptr; + } + SENode* constant_term = scalar_evolution_.SimplifyExpression( + scalar_evolution_.CreateSubtraction(offset, lower_bound)); + return constant_term; +} + +bool LoopDependenceAnalysis::CheckSupportedLoops( + std::vector loops) { + for (auto loop : loops) { + if (!IsSupportedLoop(loop)) { + return false; + } + } + return true; +} + +void LoopDependenceAnalysis::MarkUnsusedDistanceEntriesAsIrrelevant( + const Instruction* source, const Instruction* destination, + DistanceVector* distance_vector) { + std::vector source_subscripts = GetSubscripts(source); + std::vector destination_subscripts = GetSubscripts(destination); + + std::set used_loops{}; + + for (Instruction* source_inst : source_subscripts) { + SENode* source_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(source_inst)); + std::vector recurrent_nodes = + source_node->CollectRecurrentNodes(); + for (SERecurrentNode* recurrent_node : recurrent_nodes) { + used_loops.insert(recurrent_node->GetLoop()); + } + } + + for (Instruction* destination_inst : destination_subscripts) { + SENode* destination_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(destination_inst)); + std::vector recurrent_nodes = + destination_node->CollectRecurrentNodes(); + for (SERecurrentNode* recurrent_node : recurrent_nodes) { + used_loops.insert(recurrent_node->GetLoop()); + } + } + + for (size_t i = 0; i < loops_.size(); ++i) { + if (used_loops.find(loops_[i]) == used_loops.end()) { + distance_vector->GetEntries()[i].dependence_information = + DistanceEntry::DependenceInformation::IRRELEVANT; + } + } +} + +bool LoopDependenceAnalysis::IsSupportedLoop(const Loop* loop) { + std::vector inductions{}; + loop->GetInductionVariables(inductions); + if (inductions.size() != 1) { + return false; + } + Instruction* induction = inductions[0]; + SENode* induction_node = scalar_evolution_.SimplifyExpression( + scalar_evolution_.AnalyzeInstruction(induction)); + if (!induction_node->AsSERecurrentNode()) { + return false; + } + SENode* induction_step = + induction_node->AsSERecurrentNode()->GetCoefficient(); + if (!induction_step->AsSEConstantNode()) { + return false; + } + if (!(induction_step->AsSEConstantNode()->FoldToSingleValue() == 1 || + induction_step->AsSEConstantNode()->FoldToSingleValue() == -1)) { + return false; + } + return true; +} + +void LoopDependenceAnalysis::PrintDebug(std::string debug_msg) { + if (debug_stream_) { + (*debug_stream_) << debug_msg << "\n"; + } +} + +bool Constraint::operator==(const Constraint& other) const { + // A distance of |d| is equivalent to a line |x - y = -d| + if ((GetType() == ConstraintType::Distance && + other.GetType() == ConstraintType::Line) || + (GetType() == ConstraintType::Line && + other.GetType() == ConstraintType::Distance)) { + auto is_distance = AsDependenceLine() != nullptr; + + auto as_distance = + is_distance ? AsDependenceDistance() : other.AsDependenceDistance(); + auto distance = as_distance->GetDistance(); + + auto line = other.AsDependenceLine(); + + auto scalar_evolution = distance->GetParentAnalysis(); + + auto neg_distance = scalar_evolution->SimplifyExpression( + scalar_evolution->CreateNegation(distance)); + + return *scalar_evolution->CreateConstant(1) == *line->GetA() && + *scalar_evolution->CreateConstant(-1) == *line->GetB() && + *neg_distance == *line->GetC(); + } + + if (GetType() != other.GetType()) { + return false; + } + + if (AsDependenceDistance()) { + return *AsDependenceDistance()->GetDistance() == + *other.AsDependenceDistance()->GetDistance(); + } + + if (AsDependenceLine()) { + auto this_line = AsDependenceLine(); + auto other_line = other.AsDependenceLine(); + return *this_line->GetA() == *other_line->GetA() && + *this_line->GetB() == *other_line->GetB() && + *this_line->GetC() == *other_line->GetC(); + } + + if (AsDependencePoint()) { + auto this_point = AsDependencePoint(); + auto other_point = other.AsDependencePoint(); + + return *this_point->GetSource() == *other_point->GetSource() && + *this_point->GetDestination() == *other_point->GetDestination(); + } + + return true; +} + +bool Constraint::operator!=(const Constraint& other) const { + return !(*this == other); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_descriptor.cpp b/third_party/spirv-tools/source/opt/loop_descriptor.cpp new file mode 100644 index 0000000..b5b5630 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_descriptor.cpp @@ -0,0 +1,1025 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_descriptor.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/constants.h" +#include "source/opt/dominator_tree.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" +#include "source/opt/tree_iterator.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +// Takes in a phi instruction |induction| and the loop |header| and returns the +// step operation of the loop. +Instruction* Loop::GetInductionStepOperation( + const Instruction* induction) const { + // Induction must be a phi instruction. + assert(induction->opcode() == SpvOpPhi); + + Instruction* step = nullptr; + + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + // Traverse the incoming operands of the phi instruction. + for (uint32_t operand_id = 1; operand_id < induction->NumInOperands(); + operand_id += 2) { + // Incoming edge. + BasicBlock* incoming_block = + context_->cfg()->block(induction->GetSingleWordInOperand(operand_id)); + + // Check if the block is dominated by header, and thus coming from within + // the loop. + if (IsInsideLoop(incoming_block)) { + step = def_use_manager->GetDef( + induction->GetSingleWordInOperand(operand_id - 1)); + break; + } + } + + if (!step || !IsSupportedStepOp(step->opcode())) { + return nullptr; + } + + // The induction variable which binds the loop must only be modified once. + uint32_t lhs = step->GetSingleWordInOperand(0); + uint32_t rhs = step->GetSingleWordInOperand(1); + + // One of the left hand side or right hand side of the step instruction must + // be the induction phi and the other must be an OpConstant. + if (lhs != induction->result_id() && rhs != induction->result_id()) { + return nullptr; + } + + if (def_use_manager->GetDef(lhs)->opcode() != SpvOp::SpvOpConstant && + def_use_manager->GetDef(rhs)->opcode() != SpvOp::SpvOpConstant) { + return nullptr; + } + + return step; +} + +// Returns true if the |step| operation is an induction variable step operation +// which is currently handled. +bool Loop::IsSupportedStepOp(SpvOp step) const { + switch (step) { + case SpvOp::SpvOpISub: + case SpvOp::SpvOpIAdd: + return true; + default: + return false; + } +} + +bool Loop::IsSupportedCondition(SpvOp condition) const { + switch (condition) { + // < + case SpvOp::SpvOpULessThan: + case SpvOp::SpvOpSLessThan: + // > + case SpvOp::SpvOpUGreaterThan: + case SpvOp::SpvOpSGreaterThan: + + // >= + case SpvOp::SpvOpSGreaterThanEqual: + case SpvOp::SpvOpUGreaterThanEqual: + // <= + case SpvOp::SpvOpSLessThanEqual: + case SpvOp::SpvOpULessThanEqual: + + return true; + default: + return false; + } +} + +int64_t Loop::GetResidualConditionValue(SpvOp condition, int64_t initial_value, + int64_t step_value, + size_t number_of_iterations, + size_t factor) { + int64_t remainder = + initial_value + (number_of_iterations % factor) * step_value; + + // We subtract or add one as the above formula calculates the remainder if the + // loop where just less than or greater than. Adding or subtracting one should + // give a functionally equivalent value. + switch (condition) { + case SpvOp::SpvOpSGreaterThanEqual: + case SpvOp::SpvOpUGreaterThanEqual: { + remainder -= 1; + break; + } + case SpvOp::SpvOpSLessThanEqual: + case SpvOp::SpvOpULessThanEqual: { + remainder += 1; + break; + } + + default: + break; + } + return remainder; +} + +Instruction* Loop::GetConditionInst() const { + BasicBlock* condition_block = FindConditionBlock(); + if (!condition_block) { + return nullptr; + } + Instruction* branch_conditional = &*condition_block->tail(); + if (!branch_conditional || + branch_conditional->opcode() != SpvOpBranchConditional) { + return nullptr; + } + Instruction* condition_inst = context_->get_def_use_mgr()->GetDef( + branch_conditional->GetSingleWordInOperand(0)); + if (IsSupportedCondition(condition_inst->opcode())) { + return condition_inst; + } + + return nullptr; +} + +// Extract the initial value from the |induction| OpPhi instruction and store it +// in |value|. If the function couldn't find the initial value of |induction| +// return false. +bool Loop::GetInductionInitValue(const Instruction* induction, + int64_t* value) const { + Instruction* constant_instruction = nullptr; + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + for (uint32_t operand_id = 0; operand_id < induction->NumInOperands(); + operand_id += 2) { + BasicBlock* bb = context_->cfg()->block( + induction->GetSingleWordInOperand(operand_id + 1)); + + if (!IsInsideLoop(bb)) { + constant_instruction = def_use_manager->GetDef( + induction->GetSingleWordInOperand(operand_id)); + } + } + + if (!constant_instruction) return false; + + const analysis::Constant* constant = + context_->get_constant_mgr()->FindDeclaredConstant( + constant_instruction->result_id()); + if (!constant) return false; + + if (value) { + const analysis::Integer* type = constant->type()->AsInteger(); + if (!type) { + return false; + } + + *value = type->IsSigned() ? constant->GetSignExtendedValue() + : constant->GetZeroExtendedValue(); + } + + return true; +} + +Loop::Loop(IRContext* context, DominatorAnalysis* dom_analysis, + BasicBlock* header, BasicBlock* continue_target, + BasicBlock* merge_target) + : context_(context), + loop_header_(header), + loop_continue_(continue_target), + loop_merge_(merge_target), + loop_preheader_(nullptr), + parent_(nullptr), + loop_is_marked_for_removal_(false) { + assert(context); + assert(dom_analysis); + loop_preheader_ = FindLoopPreheader(dom_analysis); + loop_latch_ = FindLatchBlock(); +} + +BasicBlock* Loop::FindLoopPreheader(DominatorAnalysis* dom_analysis) { + CFG* cfg = context_->cfg(); + DominatorTree& dom_tree = dom_analysis->GetDomTree(); + DominatorTreeNode* header_node = dom_tree.GetTreeNode(loop_header_); + + // The loop predecessor. + BasicBlock* loop_pred = nullptr; + + auto header_pred = cfg->preds(loop_header_->id()); + for (uint32_t p_id : header_pred) { + DominatorTreeNode* node = dom_tree.GetTreeNode(p_id); + if (node && !dom_tree.Dominates(header_node, node)) { + // The predecessor is not part of the loop, so potential loop preheader. + if (loop_pred && node->bb_ != loop_pred) { + // If we saw 2 distinct predecessors that are outside the loop, we don't + // have a loop preheader. + return nullptr; + } + loop_pred = node->bb_; + } + } + // Safe guard against invalid code, SPIR-V spec forbids loop with the entry + // node as header. + assert(loop_pred && "The header node is the entry block ?"); + + // So we have a unique basic block that can enter this loop. + // If this loop is the unique successor of this block, then it is a loop + // preheader. + bool is_preheader = true; + uint32_t loop_header_id = loop_header_->id(); + const auto* const_loop_pred = loop_pred; + const_loop_pred->ForEachSuccessorLabel( + [&is_preheader, loop_header_id](const uint32_t id) { + if (id != loop_header_id) is_preheader = false; + }); + if (is_preheader) return loop_pred; + return nullptr; +} + +bool Loop::IsInsideLoop(Instruction* inst) const { + const BasicBlock* parent_block = context_->get_instr_block(inst); + if (!parent_block) return false; + return IsInsideLoop(parent_block); +} + +bool Loop::IsBasicBlockInLoopSlow(const BasicBlock* bb) { + assert(bb->GetParent() && "The basic block does not belong to a function"); + DominatorAnalysis* dom_analysis = + context_->GetDominatorAnalysis(bb->GetParent()); + if (dom_analysis->IsReachable(bb) && + !dom_analysis->Dominates(GetHeaderBlock(), bb)) + return false; + + return true; +} + +BasicBlock* Loop::GetOrCreatePreHeaderBlock() { + if (loop_preheader_) return loop_preheader_; + + CFG* cfg = context_->cfg(); + loop_header_ = cfg->SplitLoopHeader(loop_header_); + return loop_preheader_; +} + +void Loop::SetContinueBlock(BasicBlock* continue_block) { + assert(IsInsideLoop(continue_block)); + loop_continue_ = continue_block; +} + +void Loop::SetLatchBlock(BasicBlock* latch) { +#ifndef NDEBUG + assert(latch->GetParent() && "The basic block does not belong to a function"); + + const auto* const_latch = latch; + const_latch->ForEachSuccessorLabel([this](uint32_t id) { + assert((!IsInsideLoop(id) || id == GetHeaderBlock()->id()) && + "A predecessor of the continue block does not belong to the loop"); + }); +#endif // NDEBUG + assert(IsInsideLoop(latch) && "The continue block is not in the loop"); + + SetLatchBlockImpl(latch); +} + +void Loop::SetMergeBlock(BasicBlock* merge) { +#ifndef NDEBUG + assert(merge->GetParent() && "The basic block does not belong to a function"); +#endif // NDEBUG + assert(!IsInsideLoop(merge) && "The merge block is in the loop"); + + SetMergeBlockImpl(merge); + if (GetHeaderBlock()->GetLoopMergeInst()) { + UpdateLoopMergeInst(); + } +} + +void Loop::SetPreHeaderBlock(BasicBlock* preheader) { + if (preheader) { + assert(!IsInsideLoop(preheader) && "The preheader block is in the loop"); + assert(preheader->tail()->opcode() == SpvOpBranch && + "The preheader block does not unconditionally branch to the header " + "block"); + assert(preheader->tail()->GetSingleWordOperand(0) == + GetHeaderBlock()->id() && + "The preheader block does not unconditionally branch to the header " + "block"); + } + loop_preheader_ = preheader; +} + +BasicBlock* Loop::FindLatchBlock() { + CFG* cfg = context_->cfg(); + + DominatorAnalysis* dominator_analysis = + context_->GetDominatorAnalysis(loop_header_->GetParent()); + + // Look at the predecessors of the loop header to find a predecessor block + // which is dominated by the loop continue target. There should only be one + // block which meets this criteria and this is the latch block, as per the + // SPIR-V spec. + for (uint32_t block_id : cfg->preds(loop_header_->id())) { + if (dominator_analysis->Dominates(loop_continue_->id(), block_id)) { + return cfg->block(block_id); + } + } + + assert( + false && + "Every loop should have a latch block dominated by the continue target"); + return nullptr; +} + +void Loop::GetExitBlocks(std::unordered_set* exit_blocks) const { + CFG* cfg = context_->cfg(); + exit_blocks->clear(); + + for (uint32_t bb_id : GetBlocks()) { + const BasicBlock* bb = cfg->block(bb_id); + bb->ForEachSuccessorLabel([exit_blocks, this](uint32_t succ) { + if (!IsInsideLoop(succ)) { + exit_blocks->insert(succ); + } + }); + } +} + +void Loop::GetMergingBlocks( + std::unordered_set* merging_blocks) const { + assert(GetMergeBlock() && "This loop is not structured"); + CFG* cfg = context_->cfg(); + merging_blocks->clear(); + + std::stack to_visit; + to_visit.push(GetMergeBlock()); + while (!to_visit.empty()) { + const BasicBlock* bb = to_visit.top(); + to_visit.pop(); + merging_blocks->insert(bb->id()); + for (uint32_t pred_id : cfg->preds(bb->id())) { + if (!IsInsideLoop(pred_id) && !merging_blocks->count(pred_id)) { + to_visit.push(cfg->block(pred_id)); + } + } + } +} + +namespace { + +static inline bool IsBasicBlockSafeToClone(IRContext* context, BasicBlock* bb) { + for (Instruction& inst : *bb) { + if (!inst.IsBranch() && !context->IsCombinatorInstruction(&inst)) + return false; + } + + return true; +} + +} // namespace + +bool Loop::IsSafeToClone() const { + CFG& cfg = *context_->cfg(); + + for (uint32_t bb_id : GetBlocks()) { + BasicBlock* bb = cfg.block(bb_id); + assert(bb); + if (!IsBasicBlockSafeToClone(context_, bb)) return false; + } + + // Look at the merge construct. + if (GetHeaderBlock()->GetLoopMergeInst()) { + std::unordered_set blocks; + GetMergingBlocks(&blocks); + blocks.erase(GetMergeBlock()->id()); + for (uint32_t bb_id : blocks) { + BasicBlock* bb = cfg.block(bb_id); + assert(bb); + if (!IsBasicBlockSafeToClone(context_, bb)) return false; + } + } + + return true; +} + +bool Loop::IsLCSSA() const { + CFG* cfg = context_->cfg(); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + std::unordered_set exit_blocks; + GetExitBlocks(&exit_blocks); + + // Declare ir_context so we can capture context_ in the below lambda + IRContext* ir_context = context_; + + for (uint32_t bb_id : GetBlocks()) { + for (Instruction& insn : *cfg->block(bb_id)) { + // All uses must be either: + // - In the loop; + // - In an exit block and in a phi instruction. + if (!def_use_mgr->WhileEachUser( + &insn, + [&exit_blocks, ir_context, this](Instruction* use) -> bool { + BasicBlock* parent = ir_context->get_instr_block(use); + assert(parent && "Invalid analysis"); + if (IsInsideLoop(parent)) return true; + if (use->opcode() != SpvOpPhi) return false; + return exit_blocks.count(parent->id()); + })) + return false; + } + } + return true; +} + +bool Loop::ShouldHoistInstruction(IRContext* context, Instruction* inst) { + return AreAllOperandsOutsideLoop(context, inst) && + inst->IsOpcodeCodeMotionSafe(); +} + +bool Loop::AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst) { + analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr(); + bool all_outside_loop = true; + + const std::function operand_outside_loop = + [this, &def_use_mgr, &all_outside_loop](uint32_t* id) { + if (this->IsInsideLoop(def_use_mgr->GetDef(*id))) { + all_outside_loop = false; + return; + } + }; + + inst->ForEachInId(operand_outside_loop); + return all_outside_loop; +} + +void Loop::ComputeLoopStructuredOrder( + std::vector* ordered_loop_blocks, bool include_pre_header, + bool include_merge) const { + CFG& cfg = *context_->cfg(); + + // Reserve the memory: all blocks in the loop + extra if needed. + ordered_loop_blocks->reserve(GetBlocks().size() + include_pre_header + + include_merge); + + if (include_pre_header && GetPreHeaderBlock()) + ordered_loop_blocks->push_back(loop_preheader_); + + bool is_shader = + context_->get_feature_mgr()->HasCapability(SpvCapabilityShader); + if (!is_shader) { + cfg.ForEachBlockInReversePostOrder( + loop_header_, [ordered_loop_blocks, this](BasicBlock* bb) { + if (IsInsideLoop(bb)) ordered_loop_blocks->push_back(bb); + }); + } else { + // If this is a shader, it is possible that there are unreachable merge and + // continue blocks that must be copied to retain the structured order. + // The structured order will include these. + std::list order; + cfg.ComputeStructuredOrder(loop_header_->GetParent(), loop_header_, &order); + for (BasicBlock* bb : order) { + if (bb == GetMergeBlock()) { + break; + } + ordered_loop_blocks->push_back(bb); + } + } + if (include_merge && GetMergeBlock()) + ordered_loop_blocks->push_back(loop_merge_); +} + +LoopDescriptor::LoopDescriptor(IRContext* context, const Function* f) + : loops_(), placeholder_top_loop_(nullptr) { + PopulateList(context, f); +} + +LoopDescriptor::~LoopDescriptor() { ClearLoops(); } + +void LoopDescriptor::PopulateList(IRContext* context, const Function* f) { + DominatorAnalysis* dom_analysis = context->GetDominatorAnalysis(f); + + ClearLoops(); + + // Post-order traversal of the dominator tree to find all the OpLoopMerge + // instructions. + DominatorTree& dom_tree = dom_analysis->GetDomTree(); + for (DominatorTreeNode& node : + make_range(dom_tree.post_begin(), dom_tree.post_end())) { + Instruction* merge_inst = node.bb_->GetLoopMergeInst(); + if (merge_inst) { + bool all_backedge_unreachable = true; + for (uint32_t pid : context->cfg()->preds(node.bb_->id())) { + if (dom_analysis->IsReachable(pid) && + dom_analysis->Dominates(node.bb_->id(), pid)) { + all_backedge_unreachable = false; + break; + } + } + if (all_backedge_unreachable) + continue; // ignore this one, we actually never branch back. + + // The id of the merge basic block of this loop. + uint32_t merge_bb_id = merge_inst->GetSingleWordOperand(0); + + // The id of the continue basic block of this loop. + uint32_t continue_bb_id = merge_inst->GetSingleWordOperand(1); + + // The merge target of this loop. + BasicBlock* merge_bb = context->cfg()->block(merge_bb_id); + + // The continue target of this loop. + BasicBlock* continue_bb = context->cfg()->block(continue_bb_id); + + // The basic block containing the merge instruction. + BasicBlock* header_bb = context->get_instr_block(merge_inst); + + // Add the loop to the list of all the loops in the function. + Loop* current_loop = + new Loop(context, dom_analysis, header_bb, continue_bb, merge_bb); + loops_.push_back(current_loop); + + // We have a bottom-up construction, so if this loop has nested-loops, + // they are by construction at the tail of the loop list. + for (auto itr = loops_.rbegin() + 1; itr != loops_.rend(); ++itr) { + Loop* previous_loop = *itr; + + // If the loop already has a parent, then it has been processed. + if (previous_loop->HasParent()) continue; + + // If the current loop does not dominates the previous loop then it is + // not nested loop. + if (!dom_analysis->Dominates(header_bb, + previous_loop->GetHeaderBlock())) + continue; + // If the current loop merge dominates the previous loop then it is + // not nested loop. + if (dom_analysis->Dominates(merge_bb, previous_loop->GetHeaderBlock())) + continue; + + current_loop->AddNestedLoop(previous_loop); + } + DominatorTreeNode* dom_merge_node = dom_tree.GetTreeNode(merge_bb); + for (DominatorTreeNode& loop_node : + make_range(node.df_begin(), node.df_end())) { + // Check if we are in the loop. + if (dom_tree.Dominates(dom_merge_node, &loop_node)) continue; + current_loop->AddBasicBlock(loop_node.bb_); + basic_block_to_loop_.insert( + std::make_pair(loop_node.bb_->id(), current_loop)); + } + } + } + for (Loop* loop : loops_) { + if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop); + } +} + +std::vector LoopDescriptor::GetLoopsInBinaryLayoutOrder() { + std::vector ids{}; + + for (size_t i = 0; i < NumLoops(); ++i) { + ids.push_back(GetLoopByIndex(i).GetHeaderBlock()->id()); + } + + std::vector loops{}; + if (!ids.empty()) { + auto function = GetLoopByIndex(0).GetHeaderBlock()->GetParent(); + for (const auto& block : *function) { + auto block_id = block.id(); + + auto element = std::find(std::begin(ids), std::end(ids), block_id); + if (element != std::end(ids)) { + loops.push_back(&GetLoopByIndex(element - std::begin(ids))); + } + } + } + + return loops; +} + +BasicBlock* Loop::FindConditionBlock() const { + if (!loop_merge_) { + return nullptr; + } + BasicBlock* condition_block = nullptr; + + uint32_t in_loop_pred = 0; + for (uint32_t p : context_->cfg()->preds(loop_merge_->id())) { + if (IsInsideLoop(p)) { + if (in_loop_pred) { + // 2 in-loop predecessors. + return nullptr; + } + in_loop_pred = p; + } + } + if (!in_loop_pred) { + // Merge block is unreachable. + return nullptr; + } + + BasicBlock* bb = context_->cfg()->block(in_loop_pred); + + if (!bb) return nullptr; + + const Instruction& branch = *bb->ctail(); + + // Make sure the branch is a conditional branch. + if (branch.opcode() != SpvOpBranchConditional) return nullptr; + + // Make sure one of the two possible branches is to the merge block. + if (branch.GetSingleWordInOperand(1) == loop_merge_->id() || + branch.GetSingleWordInOperand(2) == loop_merge_->id()) { + condition_block = bb; + } + + return condition_block; +} + +bool Loop::FindNumberOfIterations(const Instruction* induction, + const Instruction* branch_inst, + size_t* iterations_out, + int64_t* step_value_out, + int64_t* init_value_out) const { + // From the branch instruction find the branch condition. + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + // Condition instruction from the OpConditionalBranch. + Instruction* condition = + def_use_manager->GetDef(branch_inst->GetSingleWordOperand(0)); + + assert(IsSupportedCondition(condition->opcode())); + + // Get the constant manager from the ir context. + analysis::ConstantManager* const_manager = context_->get_constant_mgr(); + + // Find the constant value used by the condition variable. Exit out if it + // isn't a constant int. + const analysis::Constant* upper_bound = + const_manager->FindDeclaredConstant(condition->GetSingleWordOperand(3)); + if (!upper_bound) return false; + + // Must be integer because of the opcode on the condition. + const analysis::Integer* type = upper_bound->type()->AsInteger(); + + if (!type || type->width() > 64) { + return false; + } + + int64_t condition_value = type->IsSigned() + ? upper_bound->GetSignExtendedValue() + : upper_bound->GetZeroExtendedValue(); + + // Find the instruction which is stepping through the loop. + // + // GetInductionStepOperation returns nullptr if |step_inst| is OpConstantNull. + Instruction* step_inst = GetInductionStepOperation(induction); + if (!step_inst) return false; + + // Find the constant value used by the condition variable. + const analysis::Constant* step_constant = + const_manager->FindDeclaredConstant(step_inst->GetSingleWordOperand(3)); + if (!step_constant) return false; + + // Must be integer because of the opcode on the condition. + int64_t step_value = 0; + + const analysis::Integer* step_type = + step_constant->AsIntConstant()->type()->AsInteger(); + + if (step_type->IsSigned()) { + step_value = step_constant->AsIntConstant()->GetS32BitValue(); + } else { + step_value = step_constant->AsIntConstant()->GetU32BitValue(); + } + + // If this is a subtraction step we should negate the step value. + if (step_inst->opcode() == SpvOp::SpvOpISub) { + step_value = -step_value; + } + + // Find the inital value of the loop and make sure it is a constant integer. + int64_t init_value = 0; + if (!GetInductionInitValue(induction, &init_value)) return false; + + // If iterations is non null then store the value in that. + int64_t num_itrs = GetIterations(condition->opcode(), condition_value, + init_value, step_value); + + // If the loop body will not be reached return false. + if (num_itrs <= 0) { + return false; + } + + if (iterations_out) { + assert(static_cast(num_itrs) <= std::numeric_limits::max()); + *iterations_out = static_cast(num_itrs); + } + + if (step_value_out) { + *step_value_out = step_value; + } + + if (init_value_out) { + *init_value_out = init_value; + } + + return true; +} + +// We retrieve the number of iterations using the following formula, diff / +// |step_value| where diff is calculated differently according to the +// |condition| and uses the |condition_value| and |init_value|. If diff / +// |step_value| is NOT cleanly divisable then we add one to the sum. +int64_t Loop::GetIterations(SpvOp condition, int64_t condition_value, + int64_t init_value, int64_t step_value) const { + int64_t diff = 0; + + switch (condition) { + case SpvOp::SpvOpSLessThan: + case SpvOp::SpvOpULessThan: { + // If the condition is not met to begin with the loop will never iterate. + if (!(init_value < condition_value)) return 0; + + diff = condition_value - init_value; + + // If the operation is a less then operation then the diff and step must + // have the same sign otherwise the induction will never cross the + // condition (either never true or always true). + if ((diff < 0 && step_value > 0) || (diff > 0 && step_value < 0)) { + return 0; + } + + break; + } + case SpvOp::SpvOpSGreaterThan: + case SpvOp::SpvOpUGreaterThan: { + // If the condition is not met to begin with the loop will never iterate. + if (!(init_value > condition_value)) return 0; + + diff = init_value - condition_value; + + // If the operation is a greater than operation then the diff and step + // must have opposite signs. Otherwise the condition will always be true + // or will never be true. + if ((diff < 0 && step_value < 0) || (diff > 0 && step_value > 0)) { + return 0; + } + + break; + } + + case SpvOp::SpvOpSGreaterThanEqual: + case SpvOp::SpvOpUGreaterThanEqual: { + // If the condition is not met to begin with the loop will never iterate. + if (!(init_value >= condition_value)) return 0; + + // We subract one to make it the same as SpvOpGreaterThan as it is + // functionally equivalent. + diff = init_value - (condition_value - 1); + + // If the operation is a greater than operation then the diff and step + // must have opposite signs. Otherwise the condition will always be true + // or will never be true. + if ((diff > 0 && step_value > 0) || (diff < 0 && step_value < 0)) { + return 0; + } + + break; + } + + case SpvOp::SpvOpSLessThanEqual: + case SpvOp::SpvOpULessThanEqual: { + // If the condition is not met to begin with the loop will never iterate. + if (!(init_value <= condition_value)) return 0; + + // We add one to make it the same as SpvOpLessThan as it is functionally + // equivalent. + diff = (condition_value + 1) - init_value; + + // If the operation is a less than operation then the diff and step must + // have the same sign otherwise the induction will never cross the + // condition (either never true or always true). + if ((diff < 0 && step_value > 0) || (diff > 0 && step_value < 0)) { + return 0; + } + + break; + } + + default: + assert(false && + "Could not retrieve number of iterations from the loop condition. " + "Condition is not supported."); + } + + // Take the abs of - step values. + step_value = llabs(step_value); + diff = llabs(diff); + int64_t result = diff / step_value; + + if (diff % step_value != 0) { + result += 1; + } + return result; +} + +// Returns the list of induction variables within the loop. +void Loop::GetInductionVariables( + std::vector& induction_variables) const { + for (Instruction& inst : *loop_header_) { + if (inst.opcode() == SpvOp::SpvOpPhi) { + induction_variables.push_back(&inst); + } + } +} + +Instruction* Loop::FindConditionVariable( + const BasicBlock* condition_block) const { + // Find the branch instruction. + const Instruction& branch_inst = *condition_block->ctail(); + + Instruction* induction = nullptr; + // Verify that the branch instruction is a conditional branch. + if (branch_inst.opcode() == SpvOp::SpvOpBranchConditional) { + // From the branch instruction find the branch condition. + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + // Find the instruction representing the condition used in the conditional + // branch. + Instruction* condition = + def_use_manager->GetDef(branch_inst.GetSingleWordOperand(0)); + + // Ensure that the condition is a less than operation. + if (condition && IsSupportedCondition(condition->opcode())) { + // The left hand side operand of the operation. + Instruction* variable_inst = + def_use_manager->GetDef(condition->GetSingleWordOperand(2)); + + // Make sure the variable instruction used is a phi. + if (!variable_inst || variable_inst->opcode() != SpvOpPhi) return nullptr; + + // Make sure the phi instruction only has two incoming blocks. Each + // incoming block will be represented by two in operands in the phi + // instruction, the value and the block which that value came from. We + // assume the cannocalised phi will have two incoming values, one from the + // preheader and one from the continue block. + size_t max_supported_operands = 4; + if (variable_inst->NumInOperands() == max_supported_operands) { + // The operand index of the first incoming block label. + uint32_t operand_label_1 = 1; + + // The operand index of the second incoming block label. + uint32_t operand_label_2 = 3; + + // Make sure one of them is the preheader. + if (!IsInsideLoop( + variable_inst->GetSingleWordInOperand(operand_label_1)) && + !IsInsideLoop( + variable_inst->GetSingleWordInOperand(operand_label_2))) { + return nullptr; + } + + // And make sure that the other is the latch block. + if (variable_inst->GetSingleWordInOperand(operand_label_1) != + loop_latch_->id() && + variable_inst->GetSingleWordInOperand(operand_label_2) != + loop_latch_->id()) { + return nullptr; + } + } else { + return nullptr; + } + + if (!FindNumberOfIterations(variable_inst, &branch_inst, nullptr)) + return nullptr; + induction = variable_inst; + } + } + + return induction; +} + +bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() { + auto modified = false; + + for (auto& loop : *this) { + if (!loop.GetPreHeaderBlock()) { + modified = true; + // TODO(1841): Handle failure to create pre-header. + loop.GetOrCreatePreHeaderBlock(); + } + } + + return modified; +} + +// Add and remove loops which have been marked for addition and removal to +// maintain the state of the loop descriptor class. +void LoopDescriptor::PostModificationCleanup() { + LoopContainerType loops_to_remove_; + for (Loop* loop : loops_) { + if (loop->IsMarkedForRemoval()) { + loops_to_remove_.push_back(loop); + if (loop->HasParent()) { + loop->GetParent()->RemoveChildLoop(loop); + } + } + } + + for (Loop* loop : loops_to_remove_) { + loops_.erase(std::find(loops_.begin(), loops_.end(), loop)); + delete loop; + } + + for (auto& pair : loops_to_add_) { + Loop* parent = pair.first; + std::unique_ptr loop = std::move(pair.second); + + if (parent) { + loop->SetParent(nullptr); + parent->AddNestedLoop(loop.get()); + + for (uint32_t block_id : loop->GetBlocks()) { + parent->AddBasicBlock(block_id); + } + } + + loops_.emplace_back(loop.release()); + } + + loops_to_add_.clear(); +} + +void LoopDescriptor::ClearLoops() { + for (Loop* loop : loops_) { + delete loop; + } + loops_.clear(); +} + +// Adds a new loop nest to the descriptor set. +Loop* LoopDescriptor::AddLoopNest(std::unique_ptr new_loop) { + Loop* loop = new_loop.release(); + if (!loop->HasParent()) placeholder_top_loop_.nested_loops_.push_back(loop); + // Iterate from inner to outer most loop, adding basic block to loop mapping + // as we go. + for (Loop& current_loop : + make_range(iterator::begin(loop), iterator::end(nullptr))) { + loops_.push_back(¤t_loop); + for (uint32_t bb_id : current_loop.GetBlocks()) + basic_block_to_loop_.insert(std::make_pair(bb_id, ¤t_loop)); + } + + return loop; +} + +void LoopDescriptor::RemoveLoop(Loop* loop) { + Loop* parent = loop->GetParent() ? loop->GetParent() : &placeholder_top_loop_; + parent->nested_loops_.erase(std::find(parent->nested_loops_.begin(), + parent->nested_loops_.end(), loop)); + std::for_each( + loop->nested_loops_.begin(), loop->nested_loops_.end(), + [loop](Loop* sub_loop) { sub_loop->SetParent(loop->GetParent()); }); + parent->nested_loops_.insert(parent->nested_loops_.end(), + loop->nested_loops_.begin(), + loop->nested_loops_.end()); + for (uint32_t bb_id : loop->GetBlocks()) { + Loop* l = FindLoopForBasicBlock(bb_id); + if (l == loop) { + SetBasicBlockToLoop(bb_id, l->GetParent()); + } else { + ForgetBasicBlock(bb_id); + } + } + + LoopContainerType::iterator it = + std::find(loops_.begin(), loops_.end(), loop); + assert(it != loops_.end()); + delete loop; + loops_.erase(it); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_descriptor.h b/third_party/spirv-tools/source/opt/loop_descriptor.h new file mode 100644 index 0000000..4b4f8bc --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_descriptor.h @@ -0,0 +1,576 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_DESCRIPTOR_H_ +#define SOURCE_OPT_LOOP_DESCRIPTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/module.h" +#include "source/opt/tree_iterator.h" + +namespace spvtools { +namespace opt { + +class IRContext; +class CFG; +class LoopDescriptor; + +// A class to represent and manipulate a loop in structured control flow. +class Loop { + // The type used to represent nested child loops. + using ChildrenList = std::vector; + + public: + using iterator = ChildrenList::iterator; + using const_iterator = ChildrenList::const_iterator; + using BasicBlockListTy = std::unordered_set; + + explicit Loop(IRContext* context) + : context_(context), + loop_header_(nullptr), + loop_continue_(nullptr), + loop_merge_(nullptr), + loop_preheader_(nullptr), + loop_latch_(nullptr), + parent_(nullptr), + loop_is_marked_for_removal_(false) {} + + Loop(IRContext* context, DominatorAnalysis* analysis, BasicBlock* header, + BasicBlock* continue_target, BasicBlock* merge_target); + + // Iterators over the immediate sub-loops. + inline iterator begin() { return nested_loops_.begin(); } + inline iterator end() { return nested_loops_.end(); } + inline const_iterator begin() const { return cbegin(); } + inline const_iterator end() const { return cend(); } + inline const_iterator cbegin() const { return nested_loops_.begin(); } + inline const_iterator cend() const { return nested_loops_.end(); } + + // Returns the header (first basic block of the loop). This block contains the + // OpLoopMerge instruction. + inline BasicBlock* GetHeaderBlock() { return loop_header_; } + inline const BasicBlock* GetHeaderBlock() const { return loop_header_; } + inline void SetHeaderBlock(BasicBlock* header) { loop_header_ = header; } + + // Updates the OpLoopMerge instruction to reflect the current state of the + // loop. + inline void UpdateLoopMergeInst() { + assert(GetHeaderBlock()->GetLoopMergeInst() && + "The loop is not structured"); + Instruction* merge_inst = GetHeaderBlock()->GetLoopMergeInst(); + merge_inst->SetInOperand(0, {GetMergeBlock()->id()}); + } + + // Returns the continue target basic block. This is the block designated as + // the continue target by the OpLoopMerge instruction. + inline BasicBlock* GetContinueBlock() { return loop_continue_; } + inline const BasicBlock* GetContinueBlock() const { return loop_continue_; } + + // Returns the latch basic block (basic block that holds the back-edge). + // These functions return nullptr if the loop is not structured (i.e. if it + // has more than one backedge). + inline BasicBlock* GetLatchBlock() { return loop_latch_; } + inline const BasicBlock* GetLatchBlock() const { return loop_latch_; } + + // Sets |latch| as the loop unique block branching back to the header. + // A latch block must have the following properties: + // - |latch| must be in the loop; + // - must be the only block branching back to the header block. + void SetLatchBlock(BasicBlock* latch); + + // Sets |continue_block| as the continue block of the loop. This should be the + // continue target of the OpLoopMerge and should dominate the latch block. + void SetContinueBlock(BasicBlock* continue_block); + + // Returns the basic block which marks the end of the loop. + // These functions return nullptr if the loop is not structured. + inline BasicBlock* GetMergeBlock() { return loop_merge_; } + inline const BasicBlock* GetMergeBlock() const { return loop_merge_; } + // Sets |merge| as the loop merge block. A merge block must have the following + // properties: + // - |merge| must not be in the loop; + // - all its predecessors must be in the loop. + // - it must not be already used as merge block. + // If the loop has an OpLoopMerge in its header, this instruction is also + // updated. + void SetMergeBlock(BasicBlock* merge); + + // Returns the loop pre-header, nullptr means that the loop predecessor does + // not qualify as a preheader. + // The preheader is the unique predecessor that: + // - Dominates the loop header; + // - Has only the loop header as successor. + inline BasicBlock* GetPreHeaderBlock() { return loop_preheader_; } + + // Returns the loop pre-header. + inline const BasicBlock* GetPreHeaderBlock() const { return loop_preheader_; } + // Sets |preheader| as the loop preheader block. A preheader block must have + // the following properties: + // - |merge| must not be in the loop; + // - have an unconditional branch to the loop header. + void SetPreHeaderBlock(BasicBlock* preheader); + + // Returns the loop pre-header, if there is no suitable preheader it will be + // created. Returns |nullptr| if it fails to create the preheader. + BasicBlock* GetOrCreatePreHeaderBlock(); + + // Returns true if this loop contains any nested loops. + inline bool HasNestedLoops() const { return nested_loops_.size() != 0; } + + // Clears and fills |exit_blocks| with all basic blocks that are not in the + // loop and has at least one predecessor in the loop. + void GetExitBlocks(std::unordered_set* exit_blocks) const; + + // Clears and fills |merging_blocks| with all basic blocks that are + // post-dominated by the merge block. The merge block must exist. + // The set |merging_blocks| will only contain the merge block if it is + // unreachable. + void GetMergingBlocks(std::unordered_set* merging_blocks) const; + + // Returns true if the loop is in a Loop Closed SSA form. + // In LCSSA form, all in-loop definitions are used in the loop or in phi + // instructions in the loop exit blocks. + bool IsLCSSA() const; + + // Returns the depth of this loop in the loop nest. + // The outer-most loop has a depth of 1. + inline size_t GetDepth() const { + size_t lvl = 1; + for (const Loop* loop = GetParent(); loop; loop = loop->GetParent()) lvl++; + return lvl; + } + + inline size_t NumImmediateChildren() const { return nested_loops_.size(); } + + inline bool HasChildren() const { return !nested_loops_.empty(); } + // Adds |nested| as a nested loop of this loop. Automatically register |this| + // as the parent of |nested|. + inline void AddNestedLoop(Loop* nested) { + assert(!nested->GetParent() && "The loop has another parent."); + nested_loops_.push_back(nested); + nested->SetParent(this); + } + + inline Loop* GetParent() { return parent_; } + inline const Loop* GetParent() const { return parent_; } + + inline bool HasParent() const { return parent_; } + + // Returns true if this loop is itself nested within another loop. + inline bool IsNested() const { return parent_ != nullptr; } + + // Returns the set of all basic blocks contained within the loop. Will be all + // BasicBlocks dominated by the header which are not also dominated by the + // loop merge block. + inline const BasicBlockListTy& GetBlocks() const { + return loop_basic_blocks_; + } + + // Returns true if the basic block |bb| is inside this loop. + inline bool IsInsideLoop(const BasicBlock* bb) const { + return IsInsideLoop(bb->id()); + } + + // Returns true if the basic block id |bb_id| is inside this loop. + inline bool IsInsideLoop(uint32_t bb_id) const { + return loop_basic_blocks_.count(bb_id); + } + + // Returns true if the instruction |inst| is inside this loop. + bool IsInsideLoop(Instruction* inst) const; + + // Adds the Basic Block |bb| to this loop and its parents. + void AddBasicBlock(const BasicBlock* bb) { AddBasicBlock(bb->id()); } + + // Adds the Basic Block with |id| to this loop and its parents. + void AddBasicBlock(uint32_t id) { + for (Loop* loop = this; loop != nullptr; loop = loop->parent_) { + loop->loop_basic_blocks_.insert(id); + } + } + + // Removes the Basic Block id |bb_id| from this loop and its parents. + // It the user responsibility to make sure the removed block is not a merge, + // header or continue block. + void RemoveBasicBlock(uint32_t bb_id) { + for (Loop* loop = this; loop != nullptr; loop = loop->parent_) { + loop->loop_basic_blocks_.erase(bb_id); + } + } + + // Removes all the basic blocks from the set of basic blocks within the loop. + // This does not affect any of the stored pointers to the header, preheader, + // merge, or continue blocks. + void ClearBlocks() { loop_basic_blocks_.clear(); } + + // Adds the Basic Block |bb| this loop and its parents. + void AddBasicBlockToLoop(const BasicBlock* bb) { + assert(IsBasicBlockInLoopSlow(bb) && + "Basic block does not belong to the loop"); + + AddBasicBlock(bb); + } + + // Returns the list of induction variables within the loop. + void GetInductionVariables(std::vector& inductions) const; + + // This function uses the |condition| to find the induction variable which is + // used by the loop condition within the loop. This only works if the loop is + // bound by a single condition and single induction variable. + Instruction* FindConditionVariable(const BasicBlock* condition) const; + + // Returns the number of iterations within a loop when given the |induction| + // variable and the loop |condition| check. It stores the found number of + // iterations in the output parameter |iterations| and optionally, the step + // value in |step_value| and the initial value of the induction variable in + // |init_value|. + bool FindNumberOfIterations(const Instruction* induction, + const Instruction* condition, size_t* iterations, + int64_t* step_amount = nullptr, + int64_t* init_value = nullptr) const; + + // Returns the value of the OpLoopMerge control operand as a bool. Loop + // control can be None(0), Unroll(1), or DontUnroll(2). This function returns + // true if it is set to Unroll. + inline bool HasUnrollLoopControl() const { + assert(loop_header_); + if (!loop_header_->GetLoopMergeInst()) return false; + + return loop_header_->GetLoopMergeInst()->GetSingleWordOperand(2) == 1; + } + + // Finds the conditional block with a branch to the merge and continue blocks + // within the loop body. + BasicBlock* FindConditionBlock() const; + + // Remove the child loop form this loop. + inline void RemoveChildLoop(Loop* loop) { + nested_loops_.erase( + std::find(nested_loops_.begin(), nested_loops_.end(), loop)); + loop->SetParent(nullptr); + } + + // Mark this loop to be removed later by a call to + // LoopDescriptor::PostModificationCleanup. + inline void MarkLoopForRemoval() { loop_is_marked_for_removal_ = true; } + + // Returns whether or not this loop has been marked for removal. + inline bool IsMarkedForRemoval() const { return loop_is_marked_for_removal_; } + + // Returns true if all nested loops have been marked for removal. + inline bool AreAllChildrenMarkedForRemoval() const { + for (const Loop* child : nested_loops_) { + if (!child->IsMarkedForRemoval()) { + return false; + } + } + return true; + } + + // Checks if the loop contains any instruction that will prevent it from being + // cloned. If the loop is structured, the merge construct is also considered. + bool IsSafeToClone() const; + + // Sets the parent loop of this loop, that is, a loop which contains this loop + // as a nested child loop. + inline void SetParent(Loop* parent) { parent_ = parent; } + + // Returns true is the instruction is invariant and safe to move wrt loop + bool ShouldHoistInstruction(IRContext* context, Instruction* inst); + + // Returns true if all operands of inst are in basic blocks not contained in + // loop + bool AreAllOperandsOutsideLoop(IRContext* context, Instruction* inst); + + // Extract the initial value from the |induction| variable and store it in + // |value|. If the function couldn't find the initial value of |induction| + // return false. + bool GetInductionInitValue(const Instruction* induction, + int64_t* value) const; + + // Takes in a phi instruction |induction| and the loop |header| and returns + // the step operation of the loop. + Instruction* GetInductionStepOperation(const Instruction* induction) const; + + // Returns true if we can deduce the number of loop iterations in the step + // operation |step|. IsSupportedCondition must also be true for the condition + // instruction. + bool IsSupportedStepOp(SpvOp step) const; + + // Returns true if we can deduce the number of loop iterations in the + // condition operation |condition|. IsSupportedStepOp must also be true for + // the step instruction. + bool IsSupportedCondition(SpvOp condition) const; + + // Creates the list of the loop's basic block in structured order and store + // the result in |ordered_loop_blocks|. If |include_pre_header| is true, the + // pre-header block will also be included at the beginning of the list if it + // exist. If |include_merge| is true, the merge block will also be included at + // the end of the list if it exist. + void ComputeLoopStructuredOrder(std::vector* ordered_loop_blocks, + bool include_pre_header = false, + bool include_merge = false) const; + + // Given the loop |condition|, |initial_value|, |step_value|, the trip count + // |number_of_iterations|, and the |unroll_factor| requested, get the new + // condition value for the residual loop. + static int64_t GetResidualConditionValue(SpvOp condition, + int64_t initial_value, + int64_t step_value, + size_t number_of_iterations, + size_t unroll_factor); + + // Returns the condition instruction for entry into the loop + // Returns nullptr if it can't be found. + Instruction* GetConditionInst() const; + + // Returns the context associated this loop. + IRContext* GetContext() const { return context_; } + + // Looks at all the blocks with a branch to the header block to find one + // which is also dominated by the loop continue block. This block is the latch + // block. The specification mandates that this block should exist, therefore + // this function will assert if it is not found. + BasicBlock* FindLatchBlock(); + + private: + IRContext* context_; + // The block which marks the start of the loop. + BasicBlock* loop_header_; + + // The block which begins the body of the loop. + BasicBlock* loop_continue_; + + // The block which marks the end of the loop. + BasicBlock* loop_merge_; + + // The block immediately before the loop header. + BasicBlock* loop_preheader_; + + // The block containing the backedge to the loop header. + BasicBlock* loop_latch_; + + // A parent of a loop is the loop which contains it as a nested child loop. + Loop* parent_; + + // Nested child loops of this loop. + ChildrenList nested_loops_; + + // A set of all the basic blocks which comprise the loop structure. Will be + // computed only when needed on demand. + BasicBlockListTy loop_basic_blocks_; + + // Check that |bb| is inside the loop using domination property. + // Note: this is for assertion purposes only, IsInsideLoop should be used + // instead. + bool IsBasicBlockInLoopSlow(const BasicBlock* bb); + + // Returns the loop preheader if it exists, returns nullptr otherwise. + BasicBlock* FindLoopPreheader(DominatorAnalysis* dom_analysis); + + // Sets |latch| as the loop unique latch block. No checks are performed + // here. + inline void SetLatchBlockImpl(BasicBlock* latch) { loop_latch_ = latch; } + // Sets |merge| as the loop merge block. No checks are performed here. + inline void SetMergeBlockImpl(BasicBlock* merge) { loop_merge_ = merge; } + + // Each differnt loop |condition| affects how we calculate the number of + // iterations using the |condition_value|, |init_value|, and |step_values| of + // the induction variable. This method will return the number of iterations in + // a loop with those values for a given |condition|. + int64_t GetIterations(SpvOp condition, int64_t condition_value, + int64_t init_value, int64_t step_value) const; + + // This is to allow for loops to be removed mid iteration without invalidating + // the iterators. + bool loop_is_marked_for_removal_; + + // This is only to allow LoopDescriptor::placeholder_top_loop_ to add top + // level loops as child. + friend class LoopDescriptor; + friend class LoopUtils; +}; + +// Loop descriptions class for a given function. +// For a given function, the class builds loop nests information. +// The analysis expects a structured control flow. +class LoopDescriptor { + public: + // Iterator interface (depth first postorder traversal). + using iterator = PostOrderTreeDFIterator; + using const_iterator = PostOrderTreeDFIterator; + + using pre_iterator = TreeDFIterator; + using const_pre_iterator = TreeDFIterator; + + // Creates a loop object for all loops found in |f|. + LoopDescriptor(IRContext* context, const Function* f); + + // Disable copy constructor, to avoid double-free on destruction. + LoopDescriptor(const LoopDescriptor&) = delete; + // Move constructor. + LoopDescriptor(LoopDescriptor&& other) : placeholder_top_loop_(nullptr) { + // We need to take ownership of the Loop objects in the other + // LoopDescriptor, to avoid double-free. + loops_ = std::move(other.loops_); + other.loops_.clear(); + basic_block_to_loop_ = std::move(other.basic_block_to_loop_); + other.basic_block_to_loop_.clear(); + placeholder_top_loop_ = std::move(other.placeholder_top_loop_); + } + + // Destructor + ~LoopDescriptor(); + + // Returns the number of loops found in the function. + inline size_t NumLoops() const { return loops_.size(); } + + // Returns the loop at a particular |index|. The |index| must be in bounds, + // check with NumLoops before calling. + inline Loop& GetLoopByIndex(size_t index) const { + assert(loops_.size() > index && + "Index out of range (larger than loop count)"); + return *loops_[index]; + } + + // Returns the loops in |this| in the order their headers appear in the + // binary. + std::vector GetLoopsInBinaryLayoutOrder(); + + // Returns the inner most loop that contains the basic block id |block_id|. + inline Loop* operator[](uint32_t block_id) const { + return FindLoopForBasicBlock(block_id); + } + + // Returns the inner most loop that contains the basic block |bb|. + inline Loop* operator[](const BasicBlock* bb) const { + return (*this)[bb->id()]; + } + + // Iterators for post order depth first traversal of the loops. + // Inner most loops will be visited first. + inline iterator begin() { return iterator::begin(&placeholder_top_loop_); } + inline iterator end() { return iterator::end(&placeholder_top_loop_); } + inline const_iterator begin() const { return cbegin(); } + inline const_iterator end() const { return cend(); } + inline const_iterator cbegin() const { + return const_iterator::begin(&placeholder_top_loop_); + } + inline const_iterator cend() const { + return const_iterator::end(&placeholder_top_loop_); + } + + // Iterators for pre-order depth first traversal of the loops. + // Inner most loops will be visited first. + inline pre_iterator pre_begin() { + return ++pre_iterator(&placeholder_top_loop_); + } + inline pre_iterator pre_end() { return pre_iterator(); } + inline const_pre_iterator pre_begin() const { return pre_cbegin(); } + inline const_pre_iterator pre_end() const { return pre_cend(); } + inline const_pre_iterator pre_cbegin() const { + return ++const_pre_iterator(&placeholder_top_loop_); + } + inline const_pre_iterator pre_cend() const { return const_pre_iterator(); } + + // Returns the inner most loop that contains the basic block |bb|. + inline void SetBasicBlockToLoop(uint32_t bb_id, Loop* loop) { + basic_block_to_loop_[bb_id] = loop; + } + + // Mark the loop |loop_to_add| as needing to be added when the user calls + // PostModificationCleanup. |parent| may be null. + inline void AddLoop(std::unique_ptr&& loop_to_add, Loop* parent) { + loops_to_add_.emplace_back(std::make_pair(parent, std::move(loop_to_add))); + } + + // Checks all loops in |this| and will create pre-headers for all loops + // that don't have one. Returns |true| if any blocks were created. + bool CreatePreHeaderBlocksIfMissing(); + + // Should be called to preserve the LoopAnalysis after loops have been marked + // for addition with AddLoop or MarkLoopForRemoval. + void PostModificationCleanup(); + + // Removes the basic block id |bb_id| from the block to loop mapping. + inline void ForgetBasicBlock(uint32_t bb_id) { + basic_block_to_loop_.erase(bb_id); + } + + // Adds the loop |new_loop| and all its nested loops to the descriptor set. + // The object takes ownership of all the loops. + Loop* AddLoopNest(std::unique_ptr new_loop); + + // Remove the loop |loop|. + void RemoveLoop(Loop* loop); + + void SetAsTopLoop(Loop* loop) { + assert(std::find(placeholder_top_loop_.begin(), placeholder_top_loop_.end(), + loop) == placeholder_top_loop_.end() && + "already registered"); + placeholder_top_loop_.nested_loops_.push_back(loop); + } + + Loop* GetPlaceholderRootLoop() { return &placeholder_top_loop_; } + const Loop* GetPlaceholderRootLoop() const { return &placeholder_top_loop_; } + + private: + // TODO(dneto): This should be a vector of unique_ptr. But VisualStudio 2013 + // is unable to compile it. + using LoopContainerType = std::vector; + + using LoopsToAddContainerType = + std::vector>>; + + // Creates loop descriptors for the function |f|. + void PopulateList(IRContext* context, const Function* f); + + // Returns the inner most loop that contains the basic block id |block_id|. + inline Loop* FindLoopForBasicBlock(uint32_t block_id) const { + std::unordered_map::const_iterator it = + basic_block_to_loop_.find(block_id); + return it != basic_block_to_loop_.end() ? it->second : nullptr; + } + + // Erase all the loop information. + void ClearLoops(); + + // A list of all the loops in the function. This variable owns the Loop + // objects. + LoopContainerType loops_; + + // Placeholder root: this "loop" is only there to help iterators creation. + Loop placeholder_top_loop_; + + std::unordered_map basic_block_to_loop_; + + // List of the loops marked for addition when PostModificationCleanup is + // called. + LoopsToAddContainerType loops_to_add_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_DESCRIPTOR_H_ diff --git a/third_party/spirv-tools/source/opt/loop_fission.cpp b/third_party/spirv-tools/source/opt/loop_fission.cpp new file mode 100644 index 0000000..0678113 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_fission.cpp @@ -0,0 +1,513 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_fission.h" + +#include + +#include "source/opt/register_pressure.h" + +// Implement loop fission with an optional parameter to split only +// if the register pressure in a given loop meets a certain criteria. This is +// controlled via the constructors of LoopFissionPass. +// +// 1 - Build a list of loops to be split, these are top level loops (loops +// without child loops themselves) which meet the register pressure criteria, as +// determined by the ShouldSplitLoop method of LoopFissionPass. +// +// 2 - For each loop in the list, group each instruction into a set of related +// instructions by traversing each instructions users and operands recursively. +// We stop if we encounter an instruction we have seen before or an instruction +// which we don't consider relevent (i.e OpLoopMerge). We then group these +// groups into two different sets, one for the first loop and one for the +// second. +// +// 3 - We then run CanPerformSplit to check that it would be legal to split a +// loop using those two sets. We check that we haven't altered the relative +// order load/stores appear in the binary and that we aren't breaking any +// dependency between load/stores by splitting them into two loops. We also +// check that none of the OpBranch instructions are dependent on a load as we +// leave control flow structure intact and move only instructions in the body so +// we want to avoid any loads with side affects or aliasing. +// +// 4 - We then split the loop by calling SplitLoop. This function clones the +// loop and attaches it to the preheader and connects the new loops merge block +// to the current loop header block. We then use the two sets built in step 2 to +// remove instructions from each loop. If an instruction appears in the first +// set it is removed from the second loop and vice versa. +// +// 5 - If the multiple split passes flag is set we check if each of the loops +// still meet the register pressure criteria. If they do then we add them to the +// list of loops to be split (created in step one) to allow for loops to be +// split multiple times. +// + +namespace spvtools { +namespace opt { + +class LoopFissionImpl { + public: + LoopFissionImpl(IRContext* context, Loop* loop) + : context_(context), loop_(loop), load_used_in_condition_(false) {} + + // Group each instruction in the loop into sets of instructions related by + // their usedef chains. An instruction which uses another will appear in the + // same set. Then merge those sets into just two sets. Returns false if there + // was one or less sets created. + bool GroupInstructionsByUseDef(); + + // Check if the sets built by GroupInstructionsByUseDef violate any data + // dependence rules. + bool CanPerformSplit(); + + // Split the loop and return a pointer to the new loop. + Loop* SplitLoop(); + + // Checks if |inst| is safe to move. We can only move instructions which don't + // have any side effects and OpLoads and OpStores. + bool MovableInstruction(const Instruction& inst) const; + + private: + // Traverse the def use chain of |inst| and add the users and uses of |inst| + // which are in the same loop to the |returned_set|. + void TraverseUseDef(Instruction* inst, std::set* returned_set, + bool ignore_phi_users = false, bool report_loads = false); + + // We group the instructions in the block into two different groups, the + // instructions to be kept in the original loop and the ones to be cloned into + // the new loop. As the cloned loop is attached to the preheader it will be + // the first loop and the second loop will be the original. + std::set cloned_loop_instructions_; + std::set original_loop_instructions_; + + // We need a set of all the instructions to be seen so we can break any + // recursion and also so we can ignore certain instructions by preemptively + // adding them to this set. + std::set seen_instructions_; + + // A map of instructions to their relative position in the function. + std::map instruction_order_; + + IRContext* context_; + + Loop* loop_; + + // This is set to true by TraverseUseDef when traversing the instructions + // related to the loop condition and any if conditions should any of those + // instructions be a load. + bool load_used_in_condition_; +}; + +bool LoopFissionImpl::MovableInstruction(const Instruction& inst) const { + return inst.opcode() == SpvOp::SpvOpLoad || + inst.opcode() == SpvOp::SpvOpStore || + inst.opcode() == SpvOp::SpvOpSelectionMerge || + inst.opcode() == SpvOp::SpvOpPhi || inst.IsOpcodeCodeMotionSafe(); +} + +void LoopFissionImpl::TraverseUseDef(Instruction* inst, + std::set* returned_set, + bool ignore_phi_users, bool report_loads) { + assert(returned_set && "Set to be returned cannot be null."); + + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + std::set& inst_set = *returned_set; + + // We create this functor to traverse the use def chain to build the + // grouping of related instructions. The lambda captures the std::function + // to allow it to recurse. + std::function traverser_functor; + traverser_functor = [this, def_use, &inst_set, &traverser_functor, + ignore_phi_users, report_loads](Instruction* user) { + // If we've seen the instruction before or it is not inside the loop end the + // traversal. + if (!user || seen_instructions_.count(user) != 0 || + !context_->get_instr_block(user) || + !loop_->IsInsideLoop(context_->get_instr_block(user))) { + return; + } + + // Don't include labels or loop merge instructions in the instruction sets. + // Including them would mean we group instructions related only by using the + // same labels (i.e phis). We already preempt the inclusion of + // OpSelectionMerge by adding related instructions to the seen_instructions_ + // set. + if (user->opcode() == SpvOp::SpvOpLoopMerge || + user->opcode() == SpvOp::SpvOpLabel) + return; + + // If the |report_loads| flag is set, set the class field + // load_used_in_condition_ to false. This is used to check that none of the + // condition checks in the loop rely on loads. + if (user->opcode() == SpvOp::SpvOpLoad && report_loads) { + load_used_in_condition_ = true; + } + + // Add the instruction to the set of instructions already seen, this breaks + // recursion and allows us to ignore certain instructions. + seen_instructions_.insert(user); + + inst_set.insert(user); + + // Wrapper functor to traverse the operands of each instruction. + auto traverse_operand = [&traverser_functor, def_use](const uint32_t* id) { + traverser_functor(def_use->GetDef(*id)); + }; + user->ForEachInOperand(traverse_operand); + + // For the first traversal we want to ignore the users of the phi. + if (ignore_phi_users && user->opcode() == SpvOp::SpvOpPhi) return; + + // Traverse each user with this lambda. + def_use->ForEachUser(user, traverser_functor); + + // Wrapper functor for the use traversal. + auto traverse_use = [&traverser_functor](Instruction* use, uint32_t) { + traverser_functor(use); + }; + def_use->ForEachUse(user, traverse_use); + + }; + + // We start the traversal of the use def graph by invoking the above + // lambda with the |inst| parameter. + traverser_functor(inst); +} + +bool LoopFissionImpl::GroupInstructionsByUseDef() { + std::vector> sets{}; + + // We want to ignore all the instructions stemming from the loop condition + // instruction. + BasicBlock* condition_block = loop_->FindConditionBlock(); + + if (!condition_block) return false; + Instruction* condition = &*condition_block->tail(); + + // We iterate over the blocks via iterating over all the blocks in the + // function, we do this so we are iterating in the same order which the blocks + // appear in the binary. + Function& function = *loop_->GetHeaderBlock()->GetParent(); + + // Create a temporary set to ignore certain groups of instructions within the + // loop. We don't want any instructions related to control flow to be removed + // from either loop only instructions within the control flow bodies. + std::set instructions_to_ignore{}; + TraverseUseDef(condition, &instructions_to_ignore, true, true); + + // Traverse control flow instructions to ensure they are added to the + // seen_instructions_ set and will be ignored when it it called with actual + // sets. + for (BasicBlock& block : function) { + if (!loop_->IsInsideLoop(block.id())) continue; + + for (Instruction& inst : block) { + // Ignore all instructions related to control flow. + if (inst.opcode() == SpvOp::SpvOpSelectionMerge || inst.IsBranch()) { + TraverseUseDef(&inst, &instructions_to_ignore, true, true); + } + } + } + + // Traverse the instructions and generate the sets, automatically ignoring any + // instructions in instructions_to_ignore. + for (BasicBlock& block : function) { + if (!loop_->IsInsideLoop(block.id()) || + loop_->GetHeaderBlock()->id() == block.id()) + continue; + + for (Instruction& inst : block) { + // Record the order that each load/store is seen. + if (inst.opcode() == SpvOp::SpvOpLoad || + inst.opcode() == SpvOp::SpvOpStore) { + instruction_order_[&inst] = instruction_order_.size(); + } + + // Ignore instructions already seen in a traversal. + if (seen_instructions_.count(&inst) != 0) { + continue; + } + + // Build the set. + std::set inst_set{}; + TraverseUseDef(&inst, &inst_set); + if (!inst_set.empty()) sets.push_back(std::move(inst_set)); + } + } + + // If we have one or zero sets return false to indicate that due to + // insufficient instructions we couldn't split the loop into two groups and + // thus the loop can't be split any further. + if (sets.size() < 2) { + return false; + } + + // Merge the loop sets into two different sets. In CanPerformSplit we will + // validate that we don't break the relative ordering of loads/stores by doing + // this. + for (size_t index = 0; index < sets.size() / 2; ++index) { + cloned_loop_instructions_.insert(sets[index].begin(), sets[index].end()); + } + for (size_t index = sets.size() / 2; index < sets.size(); ++index) { + original_loop_instructions_.insert(sets[index].begin(), sets[index].end()); + } + + return true; +} + +bool LoopFissionImpl::CanPerformSplit() { + // Return false if any of the condition instructions in the loop depend on a + // load. + if (load_used_in_condition_) { + return false; + } + + // Build a list of all parent loops of this loop. Loop dependence analysis + // needs this structure. + std::vector loops; + Loop* parent_loop = loop_; + while (parent_loop) { + loops.push_back(parent_loop); + parent_loop = parent_loop->GetParent(); + } + + LoopDependenceAnalysis analysis{context_, loops}; + + // A list of all the stores in the cloned loop. + std::vector set_one_stores{}; + + // A list of all the loads in the cloned loop. + std::vector set_one_loads{}; + + // Populate the above lists. + for (Instruction* inst : cloned_loop_instructions_) { + if (inst->opcode() == SpvOp::SpvOpStore) { + set_one_stores.push_back(inst); + } else if (inst->opcode() == SpvOp::SpvOpLoad) { + set_one_loads.push_back(inst); + } + + // If we find any instruction which we can't move (such as a barrier), + // return false. + if (!MovableInstruction(*inst)) return false; + } + + // We need to calculate the depth of the loop to create the loop dependency + // distance vectors. + const size_t loop_depth = loop_->GetDepth(); + + // Check the dependencies between loads in the cloned loop and stores in the + // original and vice versa. + for (Instruction* inst : original_loop_instructions_) { + // If we find any instruction which we can't move (such as a barrier), + // return false. + if (!MovableInstruction(*inst)) return false; + + // Look at the dependency between the loads in the original and stores in + // the cloned loops. + if (inst->opcode() == SpvOp::SpvOpLoad) { + for (Instruction* store : set_one_stores) { + DistanceVector vec{loop_depth}; + + // If the store actually should appear after the load, return false. + // This means the store has been placed in the wrong grouping. + if (instruction_order_[store] > instruction_order_[inst]) { + return false; + } + // If not independent check the distance vector. + if (!analysis.GetDependence(store, inst, &vec)) { + for (DistanceEntry& entry : vec.GetEntries()) { + // A distance greater than zero means that the store in the cloned + // loop has a dependency on the load in the original loop. + if (entry.distance > 0) return false; + } + } + } + } else if (inst->opcode() == SpvOp::SpvOpStore) { + for (Instruction* load : set_one_loads) { + DistanceVector vec{loop_depth}; + + // If the load actually should appear after the store, return false. + if (instruction_order_[load] > instruction_order_[inst]) { + return false; + } + + // If not independent check the distance vector. + if (!analysis.GetDependence(inst, load, &vec)) { + for (DistanceEntry& entry : vec.GetEntries()) { + // A distance less than zero means the load in the cloned loop is + // dependent on the store instruction in the original loop. + if (entry.distance < 0) return false; + } + } + } + } + } + return true; +} + +Loop* LoopFissionImpl::SplitLoop() { + // Clone the loop. + LoopUtils util{context_, loop_}; + LoopUtils::LoopCloningResult clone_results; + Loop* cloned_loop = util.CloneAndAttachLoopToHeader(&clone_results); + + // Update the OpLoopMerge in the cloned loop. + cloned_loop->UpdateLoopMergeInst(); + + // Add the loop_ to the module. + // TODO(1841): Handle failure to create pre-header. + Function::iterator it = + util.GetFunction()->FindBlock(loop_->GetOrCreatePreHeaderBlock()->id()); + util.GetFunction()->AddBasicBlocks(clone_results.cloned_bb_.begin(), + clone_results.cloned_bb_.end(), ++it); + loop_->SetPreHeaderBlock(cloned_loop->GetMergeBlock()); + + std::vector instructions_to_kill{}; + + // Kill all the instructions which should appear in the cloned loop but not in + // the original loop. + for (uint32_t id : loop_->GetBlocks()) { + BasicBlock* block = context_->cfg()->block(id); + + for (Instruction& inst : *block) { + // If the instruction appears in the cloned loop instruction group, kill + // it. + if (cloned_loop_instructions_.count(&inst) == 1 && + original_loop_instructions_.count(&inst) == 0) { + instructions_to_kill.push_back(&inst); + if (inst.opcode() == SpvOp::SpvOpPhi) { + context_->ReplaceAllUsesWith( + inst.result_id(), clone_results.value_map_[inst.result_id()]); + } + } + } + } + + // Kill all instructions which should appear in the original loop and not in + // the cloned loop. + for (uint32_t id : cloned_loop->GetBlocks()) { + BasicBlock* block = context_->cfg()->block(id); + for (Instruction& inst : *block) { + Instruction* old_inst = clone_results.ptr_map_[&inst]; + // If the instruction belongs to the original loop instruction group, kill + // it. + if (cloned_loop_instructions_.count(old_inst) == 0 && + original_loop_instructions_.count(old_inst) == 1) { + instructions_to_kill.push_back(&inst); + } + } + } + + for (Instruction* i : instructions_to_kill) { + context_->KillInst(i); + } + + return cloned_loop; +} + +LoopFissionPass::LoopFissionPass(const size_t register_threshold_to_split, + bool split_multiple_times) + : split_multiple_times_(split_multiple_times) { + // Split if the number of registers in the loop exceeds + // |register_threshold_to_split|. + split_criteria_ = + [register_threshold_to_split]( + const RegisterLiveness::RegionRegisterLiveness& liveness) { + return liveness.used_registers_ > register_threshold_to_split; + }; +} + +LoopFissionPass::LoopFissionPass() : split_multiple_times_(false) { + // Split by default. + split_criteria_ = [](const RegisterLiveness::RegionRegisterLiveness&) { + return true; + }; +} + +bool LoopFissionPass::ShouldSplitLoop(const Loop& loop, IRContext* c) { + LivenessAnalysis* analysis = c->GetLivenessAnalysis(); + + RegisterLiveness::RegionRegisterLiveness liveness{}; + + Function* function = loop.GetHeaderBlock()->GetParent(); + analysis->Get(function)->ComputeLoopRegisterPressure(loop, &liveness); + + return split_criteria_(liveness); +} + +Pass::Status LoopFissionPass::Process() { + bool changed = false; + + for (Function& f : *context()->module()) { + // We collect all the inner most loops in the function and run the loop + // splitting util on each. The reason we do this is to allow us to iterate + // over each, as creating new loops will invalidate the the loop iterator. + std::vector inner_most_loops{}; + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(&f); + for (Loop& loop : loop_descriptor) { + if (!loop.HasChildren() && ShouldSplitLoop(loop, context())) { + inner_most_loops.push_back(&loop); + } + } + + // List of new loops which meet the criteria to be split again. + std::vector new_loops_to_split{}; + + while (!inner_most_loops.empty()) { + for (Loop* loop : inner_most_loops) { + LoopFissionImpl impl{context(), loop}; + + // Group the instructions in the loop into two different sets of related + // instructions. If we can't group the instructions into the two sets + // then we can't split the loop any further. + if (!impl.GroupInstructionsByUseDef()) { + continue; + } + + if (impl.CanPerformSplit()) { + Loop* second_loop = impl.SplitLoop(); + changed = true; + context()->InvalidateAnalysesExceptFor( + IRContext::kAnalysisLoopAnalysis); + + // If the newly created loop meets the criteria to be split, split it + // again. + if (ShouldSplitLoop(*second_loop, context())) + new_loops_to_split.push_back(second_loop); + + // If the original loop (now split) still meets the criteria to be + // split, split it again. + if (ShouldSplitLoop(*loop, context())) + new_loops_to_split.push_back(loop); + } + } + + // If the split multiple times flag has been set add the new loops which + // meet the splitting criteria into the list of loops to be split on the + // next iteration. + if (split_multiple_times_) { + inner_most_loops = std::move(new_loops_to_split); + } else { + break; + } + } + } + + return changed ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_fission.h b/third_party/spirv-tools/source/opt/loop_fission.h new file mode 100644 index 0000000..e7a59c1 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_fission.h @@ -0,0 +1,78 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_FISSION_H_ +#define SOURCE_OPT_LOOP_FISSION_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_utils.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/opt/tree_iterator.h" + +namespace spvtools { +namespace opt { + +class LoopFissionPass : public Pass { + public: + // Fuction used to determine if a given loop should be split. Takes register + // pressure region for that loop as a parameter and returns true if the loop + // should be split. + using FissionCriteriaFunction = + std::function; + + // Pass built with this constructor will split all loops regardless of + // register pressure. Will not split loops more than once. + LoopFissionPass(); + + // Split the loop if the number of registers used in the loop exceeds + // |register_threshold_to_split|. |split_multiple_times| flag determines + // whether or not the pass should split loops after already splitting them + // once. + LoopFissionPass(size_t register_threshold_to_split, + bool split_multiple_times = true); + + // Split loops whose register pressure meets the criteria of |functor|. + LoopFissionPass(FissionCriteriaFunction functor, + bool split_multiple_times = true) + : split_criteria_(functor), split_multiple_times_(split_multiple_times) {} + + const char* name() const override { return "loop-fission"; } + + Pass::Status Process() override; + + // Checks if |loop| meets the register pressure criteria to be split. + bool ShouldSplitLoop(const Loop& loop, IRContext* context); + + private: + // Functor to run in ShouldSplitLoop to determine if the register pressure + // criteria is met for splitting the loop. + FissionCriteriaFunction split_criteria_; + + // Flag designating whether or not we should also split the result of + // previously split loops if they meet the register presure criteria. + bool split_multiple_times_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_FISSION_H_ diff --git a/third_party/spirv-tools/source/opt/loop_fusion.cpp b/third_party/spirv-tools/source/opt/loop_fusion.cpp new file mode 100644 index 0000000..07d171a --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_fusion.cpp @@ -0,0 +1,730 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_fusion.h" + +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/loop_dependence.h" +#include "source/opt/loop_descriptor.h" + +namespace spvtools { +namespace opt { + +namespace { + +// Append all the loops nested in |loop| to |loops|. +void CollectChildren(Loop* loop, std::vector* loops) { + for (auto child : *loop) { + loops->push_back(child); + if (child->NumImmediateChildren() != 0) { + CollectChildren(child, loops); + } + } +} + +// Return the set of locations accessed by |stores| and |loads|. +std::set GetLocationsAccessed( + const std::map>& stores, + const std::map>& loads) { + std::set locations{}; + + for (const auto& kv : stores) { + locations.insert(std::get<0>(kv)); + } + + for (const auto& kv : loads) { + locations.insert(std::get<0>(kv)); + } + + return locations; +} + +// Append all dependences from |sources| to |destinations| to |dependences|. +void GetDependences(std::vector* dependences, + LoopDependenceAnalysis* analysis, + const std::vector& sources, + const std::vector& destinations, + size_t num_entries) { + for (auto source : sources) { + for (auto destination : destinations) { + DistanceVector dist(num_entries); + if (!analysis->GetDependence(source, destination, &dist)) { + dependences->push_back(dist); + } + } + } +} + +// Apped all instructions in |block| to |instructions|. +void AddInstructionsInBlock(std::vector* instructions, + BasicBlock* block) { + for (auto& inst : *block) { + instructions->push_back(&inst); + } + + instructions->push_back(block->GetLabelInst()); +} + +} // namespace + +bool LoopFusion::UsedInContinueOrConditionBlock(Instruction* phi_instruction, + Loop* loop) { + auto condition_block = loop->FindConditionBlock()->id(); + auto continue_block = loop->GetContinueBlock()->id(); + auto not_used = context_->get_def_use_mgr()->WhileEachUser( + phi_instruction, + [this, condition_block, continue_block](Instruction* instruction) { + auto block_id = context_->get_instr_block(instruction)->id(); + return block_id != condition_block && block_id != continue_block; + }); + + return !not_used; +} + +void LoopFusion::RemoveIfNotUsedContinueOrConditionBlock( + std::vector* instructions, Loop* loop) { + instructions->erase( + std::remove_if(std::begin(*instructions), std::end(*instructions), + [this, loop](Instruction* instruction) { + return !UsedInContinueOrConditionBlock(instruction, + loop); + }), + std::end(*instructions)); +} + +bool LoopFusion::AreCompatible() { + // Check that the loops are in the same function. + if (loop_0_->GetHeaderBlock()->GetParent() != + loop_1_->GetHeaderBlock()->GetParent()) { + return false; + } + + // Check that both loops have pre-header blocks. + if (!loop_0_->GetPreHeaderBlock() || !loop_1_->GetPreHeaderBlock()) { + return false; + } + + // Check there are no breaks. + if (context_->cfg()->preds(loop_0_->GetMergeBlock()->id()).size() != 1 || + context_->cfg()->preds(loop_1_->GetMergeBlock()->id()).size() != 1) { + return false; + } + + // Check there are no continues. + if (context_->cfg()->preds(loop_0_->GetContinueBlock()->id()).size() != 1 || + context_->cfg()->preds(loop_1_->GetContinueBlock()->id()).size() != 1) { + return false; + } + + // |GetInductionVariables| returns all OpPhi in the header. Check that both + // loops have exactly one that is used in the continue and condition blocks. + std::vector inductions_0{}, inductions_1{}; + loop_0_->GetInductionVariables(inductions_0); + RemoveIfNotUsedContinueOrConditionBlock(&inductions_0, loop_0_); + + if (inductions_0.size() != 1) { + return false; + } + + induction_0_ = inductions_0.front(); + + loop_1_->GetInductionVariables(inductions_1); + RemoveIfNotUsedContinueOrConditionBlock(&inductions_1, loop_1_); + + if (inductions_1.size() != 1) { + return false; + } + + induction_1_ = inductions_1.front(); + + if (!CheckInit()) { + return false; + } + + if (!CheckCondition()) { + return false; + } + + if (!CheckStep()) { + return false; + } + + // Check adjacency, |loop_0_| should come just before |loop_1_|. + // There is always at least one block between loops, even if it's empty. + // We'll check at most 2 preceeding blocks. + + auto pre_header_1 = loop_1_->GetPreHeaderBlock(); + + std::vector block_to_check{}; + block_to_check.push_back(pre_header_1); + + if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) { + // Follow CFG for one more block. + auto preds = context_->cfg()->preds(pre_header_1->id()); + if (preds.size() == 1) { + auto block = &*containing_function_->FindBlock(preds.front()); + if (block == loop_0_->GetMergeBlock()) { + block_to_check.push_back(block); + } else { + return false; + } + } else { + return false; + } + } + + // Check that the separating blocks are either empty or only contains a store + // to a local variable that is never read (left behind by + // '--eliminate-local-multi-store'). Also allow OpPhi, since the loop could be + // in LCSSA form. + for (auto block : block_to_check) { + for (auto& inst : *block) { + if (inst.opcode() == SpvOpStore) { + // Get the definition of the target to check it's function scope so + // there are no observable side effects. + auto variable = + context_->get_def_use_mgr()->GetDef(inst.GetSingleWordInOperand(0)); + + if (variable->opcode() != SpvOpVariable || + variable->GetSingleWordInOperand(0) != SpvStorageClassFunction) { + return false; + } + + // Check the target is never loaded. + auto is_used = false; + context_->get_def_use_mgr()->ForEachUse( + inst.GetSingleWordInOperand(0), + [&is_used](Instruction* use_inst, uint32_t) { + if (use_inst->opcode() == SpvOpLoad) { + is_used = true; + } + }); + + if (is_used) { + return false; + } + } else if (inst.opcode() == SpvOpPhi) { + if (inst.NumInOperands() != 2) { + return false; + } + } else if (inst.opcode() != SpvOpBranch) { + return false; + } + } + } + + return true; +} // namespace opt + +bool LoopFusion::ContainsBarriersOrFunctionCalls(Loop* loop) { + for (const auto& block : loop->GetBlocks()) { + for (const auto& inst : *containing_function_->FindBlock(block)) { + auto opcode = inst.opcode(); + if (opcode == SpvOpFunctionCall || opcode == SpvOpControlBarrier || + opcode == SpvOpMemoryBarrier || opcode == SpvOpTypeNamedBarrier || + opcode == SpvOpNamedBarrierInitialize || + opcode == SpvOpMemoryNamedBarrier) { + return true; + } + } + } + + return false; +} + +bool LoopFusion::CheckInit() { + int64_t loop_0_init; + if (!loop_0_->GetInductionInitValue(induction_0_, &loop_0_init)) { + return false; + } + + int64_t loop_1_init; + if (!loop_1_->GetInductionInitValue(induction_1_, &loop_1_init)) { + return false; + } + + if (loop_0_init != loop_1_init) { + return false; + } + + return true; +} + +bool LoopFusion::CheckCondition() { + auto condition_0 = loop_0_->GetConditionInst(); + auto condition_1 = loop_1_->GetConditionInst(); + + if (!loop_0_->IsSupportedCondition(condition_0->opcode()) || + !loop_1_->IsSupportedCondition(condition_1->opcode())) { + return false; + } + + if (condition_0->opcode() != condition_1->opcode()) { + return false; + } + + for (uint32_t i = 0; i < condition_0->NumInOperandWords(); ++i) { + auto arg_0 = context_->get_def_use_mgr()->GetDef( + condition_0->GetSingleWordInOperand(i)); + auto arg_1 = context_->get_def_use_mgr()->GetDef( + condition_1->GetSingleWordInOperand(i)); + + if (arg_0 == induction_0_ && arg_1 == induction_1_) { + continue; + } + + if (arg_0 == induction_0_ && arg_1 != induction_1_) { + return false; + } + + if (arg_1 == induction_1_ && arg_0 != induction_0_) { + return false; + } + + if (arg_0 != arg_1) { + return false; + } + } + + return true; +} + +bool LoopFusion::CheckStep() { + auto scalar_analysis = context_->GetScalarEvolutionAnalysis(); + SENode* induction_node_0 = scalar_analysis->SimplifyExpression( + scalar_analysis->AnalyzeInstruction(induction_0_)); + if (!induction_node_0->AsSERecurrentNode()) { + return false; + } + + SENode* induction_step_0 = + induction_node_0->AsSERecurrentNode()->GetCoefficient(); + if (!induction_step_0->AsSEConstantNode()) { + return false; + } + + SENode* induction_node_1 = scalar_analysis->SimplifyExpression( + scalar_analysis->AnalyzeInstruction(induction_1_)); + if (!induction_node_1->AsSERecurrentNode()) { + return false; + } + + SENode* induction_step_1 = + induction_node_1->AsSERecurrentNode()->GetCoefficient(); + if (!induction_step_1->AsSEConstantNode()) { + return false; + } + + if (*induction_step_0 != *induction_step_1) { + return false; + } + + return true; +} + +std::map> LoopFusion::LocationToMemOps( + const std::vector& mem_ops) { + std::map> location_map{}; + + for (auto instruction : mem_ops) { + auto access_location = context_->get_def_use_mgr()->GetDef( + instruction->GetSingleWordInOperand(0)); + + while (access_location->opcode() == SpvOpAccessChain) { + access_location = context_->get_def_use_mgr()->GetDef( + access_location->GetSingleWordInOperand(0)); + } + + location_map[access_location].push_back(instruction); + } + + return location_map; +} + +std::pair, std::vector> +LoopFusion::GetLoadsAndStoresInLoop(Loop* loop) { + std::vector loads{}; + std::vector stores{}; + + for (auto block_id : loop->GetBlocks()) { + if (block_id == loop->GetContinueBlock()->id()) { + continue; + } + + for (auto& instruction : *containing_function_->FindBlock(block_id)) { + if (instruction.opcode() == SpvOpLoad) { + loads.push_back(&instruction); + } else if (instruction.opcode() == SpvOpStore) { + stores.push_back(&instruction); + } + } + } + + return std::make_pair(loads, stores); +} + +bool LoopFusion::IsUsedInLoop(Instruction* instruction, Loop* loop) { + auto not_used = context_->get_def_use_mgr()->WhileEachUser( + instruction, [this, loop](Instruction* user) { + auto block_id = context_->get_instr_block(user)->id(); + return !loop->IsInsideLoop(block_id); + }); + + return !not_used; +} + +bool LoopFusion::IsLegal() { + assert(AreCompatible() && "Fusion can't be legal, loops are not compatible."); + + // Bail out if there are function calls as they could have side-effects that + // cause dependencies or if there are any barriers. + if (ContainsBarriersOrFunctionCalls(loop_0_) || + ContainsBarriersOrFunctionCalls(loop_1_)) { + return false; + } + + std::vector phi_instructions{}; + loop_0_->GetInductionVariables(phi_instructions); + + // Check no OpPhi in |loop_0_| is used in |loop_1_|. + for (auto phi_instruction : phi_instructions) { + if (IsUsedInLoop(phi_instruction, loop_1_)) { + return false; + } + } + + // Check no LCSSA OpPhi in merge block of |loop_0_| is used in |loop_1_|. + auto phi_used = false; + loop_0_->GetMergeBlock()->ForEachPhiInst( + [this, &phi_used](Instruction* phi_instruction) { + phi_used |= IsUsedInLoop(phi_instruction, loop_1_); + }); + + if (phi_used) { + return false; + } + + // Grab loads & stores from both loops. + auto loads_stores_0 = GetLoadsAndStoresInLoop(loop_0_); + auto loads_stores_1 = GetLoadsAndStoresInLoop(loop_1_); + + // Build memory location to operation maps. + auto load_locs_0 = LocationToMemOps(std::get<0>(loads_stores_0)); + auto store_locs_0 = LocationToMemOps(std::get<1>(loads_stores_0)); + + auto load_locs_1 = LocationToMemOps(std::get<0>(loads_stores_1)); + auto store_locs_1 = LocationToMemOps(std::get<1>(loads_stores_1)); + + // Get the locations accessed in both loops. + auto locations_0 = GetLocationsAccessed(store_locs_0, load_locs_0); + auto locations_1 = GetLocationsAccessed(store_locs_1, load_locs_1); + + std::vector potential_clashes{}; + + std::set_intersection(std::begin(locations_0), std::end(locations_0), + std::begin(locations_1), std::end(locations_1), + std::back_inserter(potential_clashes)); + + // If the loops don't access the same variables, the fusion is legal. + if (potential_clashes.empty()) { + return true; + } + + // Find variables that have at least one store. + std::vector potential_clashes_with_stores{}; + for (auto location : potential_clashes) { + if (store_locs_0.find(location) != std::end(store_locs_0) || + store_locs_1.find(location) != std::end(store_locs_1)) { + potential_clashes_with_stores.push_back(location); + } + } + + // If there are only loads to the same variables, the fusion is legal. + if (potential_clashes_with_stores.empty()) { + return true; + } + + // Else if loads and at least one store (across loops) to the same variable + // there is a potential dependence and we need to check the dependence + // distance. + + // Find all the loops in this loop nest for the dependency analysis. + std::vector loops{}; + + // Find the parents. + for (auto current_loop = loop_0_; current_loop != nullptr; + current_loop = current_loop->GetParent()) { + loops.push_back(current_loop); + } + + auto this_loop_position = loops.size() - 1; + std::reverse(std::begin(loops), std::end(loops)); + + // Find the children. + CollectChildren(loop_0_, &loops); + CollectChildren(loop_1_, &loops); + + // Check that any dependes created are legal. That means the fused loops do + // not have any dependencies with dependence distance greater than 0 that did + // not exist in the original loops. + + LoopDependenceAnalysis analysis(context_, loops); + + analysis.GetScalarEvolution()->AddLoopsToPretendAreTheSame( + {loop_0_, loop_1_}); + + for (auto location : potential_clashes_with_stores) { + // Analyse dependences from |loop_0_| to |loop_1_|. + std::vector dependences; + // Read-After-Write. + GetDependences(&dependences, &analysis, store_locs_0[location], + load_locs_1[location], loops.size()); + // Write-After-Read. + GetDependences(&dependences, &analysis, load_locs_0[location], + store_locs_1[location], loops.size()); + // Write-After-Write. + GetDependences(&dependences, &analysis, store_locs_0[location], + store_locs_1[location], loops.size()); + + // Check that the induction variables either don't appear in the subscripts + // or the dependence distance is negative. + for (const auto& dependence : dependences) { + const auto& entry = dependence.GetEntries()[this_loop_position]; + if ((entry.dependence_information == + DistanceEntry::DependenceInformation::DISTANCE && + entry.distance < 1) || + (entry.dependence_information == + DistanceEntry::DependenceInformation::IRRELEVANT)) { + continue; + } else { + return false; + } + } + } + + return true; +} + +void ReplacePhiParentWith(Instruction* inst, uint32_t orig_block, + uint32_t new_block) { + if (inst->GetSingleWordInOperand(1) == orig_block) { + inst->SetInOperand(1, {new_block}); + } else { + inst->SetInOperand(3, {new_block}); + } +} + +void LoopFusion::Fuse() { + assert(AreCompatible() && "Can't fuse, loops aren't compatible"); + assert(IsLegal() && "Can't fuse, illegal"); + + // Save the pointers/ids, won't be found in the middle of doing modifications. + auto header_1 = loop_1_->GetHeaderBlock()->id(); + auto condition_1 = loop_1_->FindConditionBlock()->id(); + auto continue_1 = loop_1_->GetContinueBlock()->id(); + auto continue_0 = loop_0_->GetContinueBlock()->id(); + auto condition_block_of_0 = loop_0_->FindConditionBlock(); + + // Find the blocks whose branches need updating. + auto first_block_of_1 = &*(++containing_function_->FindBlock(condition_1)); + auto last_block_of_1 = &*(--containing_function_->FindBlock(continue_1)); + auto last_block_of_0 = &*(--containing_function_->FindBlock(continue_0)); + + // Update the branch for |last_block_of_loop_0| to go to |first_block_of_1|. + last_block_of_0->ForEachSuccessorLabel( + [first_block_of_1](uint32_t* succ) { *succ = first_block_of_1->id(); }); + + // Update the branch for the |last_block_of_loop_1| to go to the continue + // block of |loop_0_|. + last_block_of_1->ForEachSuccessorLabel( + [this](uint32_t* succ) { *succ = loop_0_->GetContinueBlock()->id(); }); + + // Update merge block id in the header of |loop_0_| to the merge block of + // |loop_1_|. + loop_0_->GetHeaderBlock()->ForEachInst([this](Instruction* inst) { + if (inst->opcode() == SpvOpLoopMerge) { + inst->SetInOperand(0, {loop_1_->GetMergeBlock()->id()}); + } + }); + + // Update condition branch target in |loop_0_| to the merge block of + // |loop_1_|. + condition_block_of_0->ForEachInst([this](Instruction* inst) { + if (inst->opcode() == SpvOpBranchConditional) { + auto loop_0_merge_block_id = loop_0_->GetMergeBlock()->id(); + + if (inst->GetSingleWordInOperand(1) == loop_0_merge_block_id) { + inst->SetInOperand(1, {loop_1_->GetMergeBlock()->id()}); + } else { + inst->SetInOperand(2, {loop_1_->GetMergeBlock()->id()}); + } + } + }); + + // Move OpPhi instructions not corresponding to the induction variable from + // the header of |loop_1_| to the header of |loop_0_|. + std::vector instructions_to_move{}; + for (auto& instruction : *loop_1_->GetHeaderBlock()) { + if (instruction.opcode() == SpvOpPhi && &instruction != induction_1_) { + instructions_to_move.push_back(&instruction); + } + } + + for (auto& it : instructions_to_move) { + it->RemoveFromList(); + it->InsertBefore(induction_0_); + } + + // Update the OpPhi parents to the correct blocks in |loop_0_|. + loop_0_->GetHeaderBlock()->ForEachPhiInst([this](Instruction* i) { + ReplacePhiParentWith(i, loop_1_->GetPreHeaderBlock()->id(), + loop_0_->GetPreHeaderBlock()->id()); + + ReplacePhiParentWith(i, loop_1_->GetContinueBlock()->id(), + loop_0_->GetContinueBlock()->id()); + }); + + // Update instruction to block mapping & DefUseManager. + for (auto& phi_instruction : instructions_to_move) { + context_->set_instr_block(phi_instruction, loop_0_->GetHeaderBlock()); + context_->get_def_use_mgr()->AnalyzeInstUse(phi_instruction); + } + + // Replace the uses of the induction variable of |loop_1_| with that the + // induction variable of |loop_0_|. + context_->ReplaceAllUsesWith(induction_1_->result_id(), + induction_0_->result_id()); + + // Replace LCSSA OpPhi in merge block of |loop_0_|. + loop_0_->GetMergeBlock()->ForEachPhiInst([this](Instruction* instruction) { + context_->ReplaceAllUsesWith(instruction->result_id(), + instruction->GetSingleWordInOperand(0)); + }); + + // Update LCSSA OpPhi in merge block of |loop_1_|. + loop_1_->GetMergeBlock()->ForEachPhiInst( + [condition_block_of_0](Instruction* instruction) { + instruction->SetInOperand(1, {condition_block_of_0->id()}); + }); + + // Move the continue block of |loop_0_| after the last block of |loop_1_|. + containing_function_->MoveBasicBlockToAfter(continue_0, last_block_of_1); + + // Gather all instructions to be killed from |loop_1_| (induction variable + // initialisation, header, condition and continue blocks). + std::vector instr_to_delete{}; + AddInstructionsInBlock(&instr_to_delete, loop_1_->GetPreHeaderBlock()); + AddInstructionsInBlock(&instr_to_delete, loop_1_->GetHeaderBlock()); + AddInstructionsInBlock(&instr_to_delete, loop_1_->FindConditionBlock()); + AddInstructionsInBlock(&instr_to_delete, loop_1_->GetContinueBlock()); + + // There was an additional empty block between the loops, kill that too. + if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) { + AddInstructionsInBlock(&instr_to_delete, loop_0_->GetMergeBlock()); + } + + // Update the CFG, so it wouldn't need invalidating. + auto cfg = context_->cfg(); + + cfg->ForgetBlock(loop_1_->GetPreHeaderBlock()); + cfg->ForgetBlock(loop_1_->GetHeaderBlock()); + cfg->ForgetBlock(loop_1_->FindConditionBlock()); + cfg->ForgetBlock(loop_1_->GetContinueBlock()); + + if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) { + cfg->ForgetBlock(loop_0_->GetMergeBlock()); + } + + cfg->RemoveEdge(last_block_of_0->id(), loop_0_->GetContinueBlock()->id()); + cfg->AddEdge(last_block_of_0->id(), first_block_of_1->id()); + + cfg->AddEdge(last_block_of_1->id(), loop_0_->GetContinueBlock()->id()); + + cfg->AddEdge(loop_0_->GetContinueBlock()->id(), + loop_1_->GetHeaderBlock()->id()); + + cfg->AddEdge(condition_block_of_0->id(), loop_1_->GetMergeBlock()->id()); + + // Update DefUseManager. + auto def_use_mgr = context_->get_def_use_mgr(); + + // Uses of labels that are in updated branches need analysing. + def_use_mgr->AnalyzeInstUse(last_block_of_0->terminator()); + def_use_mgr->AnalyzeInstUse(last_block_of_1->terminator()); + def_use_mgr->AnalyzeInstUse(loop_0_->GetHeaderBlock()->GetLoopMergeInst()); + def_use_mgr->AnalyzeInstUse(condition_block_of_0->terminator()); + + // Update the LoopDescriptor, so it wouldn't need invalidating. + auto ld = context_->GetLoopDescriptor(containing_function_); + + // Create a copy, so the iterator wouldn't be invalidated. + std::vector loops_to_add_remove{}; + for (auto child_loop : *loop_1_) { + loops_to_add_remove.push_back(child_loop); + } + + for (auto child_loop : loops_to_add_remove) { + loop_1_->RemoveChildLoop(child_loop); + loop_0_->AddNestedLoop(child_loop); + } + + auto loop_1_blocks = loop_1_->GetBlocks(); + + for (auto block : loop_1_blocks) { + loop_1_->RemoveBasicBlock(block); + if (block != header_1 && block != condition_1 && block != continue_1) { + loop_0_->AddBasicBlock(block); + if ((*ld)[block] == loop_1_) { + ld->SetBasicBlockToLoop(block, loop_0_); + } + } + + if ((*ld)[block] == loop_1_) { + ld->ForgetBasicBlock(block); + } + } + + loop_1_->RemoveBasicBlock(loop_1_->GetPreHeaderBlock()->id()); + ld->ForgetBasicBlock(loop_1_->GetPreHeaderBlock()->id()); + + if (loop_0_->GetMergeBlock() != loop_1_->GetPreHeaderBlock()) { + loop_0_->RemoveBasicBlock(loop_0_->GetMergeBlock()->id()); + ld->ForgetBasicBlock(loop_0_->GetMergeBlock()->id()); + } + + loop_0_->SetMergeBlock(loop_1_->GetMergeBlock()); + + loop_1_->ClearBlocks(); + + ld->RemoveLoop(loop_1_); + + // Kill unnessecary instructions and remove all empty blocks. + for (auto inst : instr_to_delete) { + context_->KillInst(inst); + } + + containing_function_->RemoveEmptyBlocks(); + + // Invalidate analyses. + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisInstrToBlockMapping | + IRContext::Analysis::kAnalysisLoopAnalysis | + IRContext::Analysis::kAnalysisDefUse | IRContext::Analysis::kAnalysisCFG); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_fusion.h b/third_party/spirv-tools/source/opt/loop_fusion.h new file mode 100644 index 0000000..d61d678 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_fusion.h @@ -0,0 +1,114 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_FUSION_H_ +#define SOURCE_OPT_LOOP_FUSION_H_ + +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" +#include "source/opt/scalar_analysis.h" + +namespace spvtools { +namespace opt { + +class LoopFusion { + public: + LoopFusion(IRContext* context, Loop* loop_0, Loop* loop_1) + : context_(context), + loop_0_(loop_0), + loop_1_(loop_1), + containing_function_(loop_0->GetHeaderBlock()->GetParent()) {} + + // Checks if the |loop_0| and |loop_1| are compatible for fusion. + // That means: + // * they both have one induction variable + // * they have the same upper and lower bounds + // - same inital value + // - same condition + // * they have the same update step + // * they are adjacent, with |loop_0| appearing before |loop_1| + // * there are no break/continue in either of them + // * they both have pre-header blocks (required for ScalarEvolutionAnalysis + // and dependence checking). + bool AreCompatible(); + + // Checks if compatible |loop_0| and |loop_1| are legal to fuse. + // * fused loops do not have any dependencies with dependence distance greater + // than 0 that did not exist in the original loops. + // * there are no function calls in the loops (could have side-effects) + bool IsLegal(); + + // Perform the actual fusion of |loop_0_| and |loop_1_|. The loops have to be + // compatible and the fusion has to be legal. + void Fuse(); + + private: + // Check that the initial values are the same. + bool CheckInit(); + + // Check that the conditions are the same. + bool CheckCondition(); + + // Check that the steps are the same. + bool CheckStep(); + + // Returns |true| if |instruction| is used in the continue or condition block + // of |loop|. + bool UsedInContinueOrConditionBlock(Instruction* instruction, Loop* loop); + + // Remove entries in |instructions| that are not used in the continue or + // condition block of |loop|. + void RemoveIfNotUsedContinueOrConditionBlock( + std::vector* instructions, Loop* loop); + + // Returns |true| if |instruction| is used in |loop|. + bool IsUsedInLoop(Instruction* instruction, Loop* loop); + + // Returns |true| if |loop| has at least one barrier or function call. + bool ContainsBarriersOrFunctionCalls(Loop* loop); + + // Get all instructions in the |loop| (except in the latch block) that have + // the opcode |opcode|. + std::pair, std::vector> + GetLoadsAndStoresInLoop(Loop* loop); + + // Given a vector of memory operations (OpLoad/OpStore), constructs a map from + // variables to the loads/stores that those variables. + std::map> LocationToMemOps( + const std::vector& mem_ops); + + IRContext* context_; + + // The original loops to be fused. + Loop* loop_0_; + Loop* loop_1_; + + // The function that contains |loop_0_| and |loop_1_|. + Function* containing_function_ = nullptr; + + // The induction variables for |loop_0_| and |loop_1_|. + Instruction* induction_0_ = nullptr; + Instruction* induction_1_ = nullptr; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_FUSION_H_ diff --git a/third_party/spirv-tools/source/opt/loop_fusion_pass.cpp b/third_party/spirv-tools/source/opt/loop_fusion_pass.cpp new file mode 100644 index 0000000..bd8444a --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_fusion_pass.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_fusion_pass.h" + +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_fusion.h" +#include "source/opt/register_pressure.h" + +namespace spvtools { +namespace opt { + +Pass::Status LoopFusionPass::Process() { + bool modified = false; + Module* module = context()->module(); + + // Process each function in the module + for (Function& f : *module) { + modified |= ProcessFunction(&f); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool LoopFusionPass::ProcessFunction(Function* function) { + LoopDescriptor& ld = *context()->GetLoopDescriptor(function); + + // If a loop doesn't have a preheader needs then it needs to be created. Make + // sure to return Status::SuccessWithChange in that case. + auto modified = ld.CreatePreHeaderBlocksIfMissing(); + + // TODO(tremmelg): Could the only loop that |loop| could possibly be fused be + // picked out so don't have to check every loop + for (auto& loop_0 : ld) { + for (auto& loop_1 : ld) { + LoopFusion fusion(context(), &loop_0, &loop_1); + + if (fusion.AreCompatible() && fusion.IsLegal()) { + RegisterLiveness liveness(context(), function); + RegisterLiveness::RegionRegisterLiveness reg_pressure{}; + liveness.SimulateFusion(loop_0, loop_1, ®_pressure); + + if (reg_pressure.used_registers_ <= max_registers_per_loop_) { + fusion.Fuse(); + // Recurse, as the current iterators will have been invalidated. + ProcessFunction(function); + return true; + } + } + } + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_fusion_pass.h b/third_party/spirv-tools/source/opt/loop_fusion_pass.h new file mode 100644 index 0000000..3a0be60 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_fusion_pass.h @@ -0,0 +1,51 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_FUSION_PASS_H_ +#define SOURCE_OPT_LOOP_FUSION_PASS_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Implements a loop fusion pass. +// This pass will look for adjacent loops that are compatible and legal to be +// fused. It will fuse all such loops as long as the register usage for the +// fused loop stays under the threshold defined by |max_registers_per_loop|. +class LoopFusionPass : public Pass { + public: + explicit LoopFusionPass(size_t max_registers_per_loop) + : Pass(), max_registers_per_loop_(max_registers_per_loop) {} + + const char* name() const override { return "loop-fusion"; } + + // Processes the given |module|. Returns Status::Failure if errors occur when + // processing. Returns the corresponding Status::Success if processing is + // succesful to indicate whether changes have been made to the modue. + Status Process() override; + + private: + // Fuse loops in |function| if compatible, legal and the fused loop won't use + // too many registers. + bool ProcessFunction(Function* function); + + // The maximum number of registers a fused loop is allowed to use. + size_t max_registers_per_loop_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_FUSION_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/loop_peeling.cpp b/third_party/spirv-tools/source/opt/loop_peeling.cpp new file mode 100644 index 0000000..071c27c --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_peeling.cpp @@ -0,0 +1,1086 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_peeling.h" +#include "source/opt/loop_utils.h" +#include "source/opt/scalar_analysis.h" +#include "source/opt/scalar_analysis_nodes.h" + +namespace spvtools { +namespace opt { +size_t LoopPeelingPass::code_grow_threshold_ = 1000; + +void LoopPeeling::DuplicateAndConnectLoop( + LoopUtils::LoopCloningResult* clone_results) { + CFG& cfg = *context_->cfg(); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + assert(CanPeelLoop() && "Cannot peel loop!"); + + std::vector ordered_loop_blocks; + // TODO(1841): Handle failure to create pre-header. + BasicBlock* pre_header = loop_->GetOrCreatePreHeaderBlock(); + + loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks); + + cloned_loop_ = loop_utils_.CloneLoop(clone_results, ordered_loop_blocks); + + // Add the basic block to the function. + Function::iterator it = + loop_utils_.GetFunction()->FindBlock(pre_header->id()); + assert(it != loop_utils_.GetFunction()->end() && + "Pre-header not found in the function."); + loop_utils_.GetFunction()->AddBasicBlocks( + clone_results->cloned_bb_.begin(), clone_results->cloned_bb_.end(), ++it); + + // Make the |loop_|'s preheader the |cloned_loop_| one. + BasicBlock* cloned_header = cloned_loop_->GetHeaderBlock(); + pre_header->ForEachSuccessorLabel( + [cloned_header](uint32_t* succ) { *succ = cloned_header->id(); }); + + // Update cfg. + cfg.RemoveEdge(pre_header->id(), loop_->GetHeaderBlock()->id()); + cloned_loop_->SetPreHeaderBlock(pre_header); + loop_->SetPreHeaderBlock(nullptr); + + // When cloning the loop, we didn't cloned the merge block, so currently + // |cloned_loop_| shares the same block as |loop_|. + // We mutate all branches from |cloned_loop_| block to |loop_|'s merge into a + // branch to |loop_|'s header (so header will also be the merge of + // |cloned_loop_|). + uint32_t cloned_loop_exit = 0; + for (uint32_t pred_id : cfg.preds(loop_->GetMergeBlock()->id())) { + if (loop_->IsInsideLoop(pred_id)) continue; + BasicBlock* bb = cfg.block(pred_id); + assert(cloned_loop_exit == 0 && "The loop has multiple exits."); + cloned_loop_exit = bb->id(); + bb->ForEachSuccessorLabel([this](uint32_t* succ) { + if (*succ == loop_->GetMergeBlock()->id()) + *succ = loop_->GetHeaderBlock()->id(); + }); + } + + // Update cfg. + cfg.RemoveNonExistingEdges(loop_->GetMergeBlock()->id()); + cfg.AddEdge(cloned_loop_exit, loop_->GetHeaderBlock()->id()); + + // Patch the phi of the original loop header: + // - Set the loop entry branch to come from the cloned loop exit block; + // - Set the initial value of the phi using the corresponding cloned loop + // exit values. + // + // We patch the iterating value initializers of the original loop using the + // corresponding cloned loop exit values. Connects the cloned loop iterating + // values to the original loop. This make sure that the initial value of the + // second loop starts with the last value of the first loop. + // + // For example, loops like: + // + // int z = 0; + // for (int i = 0; i++ < M; i += cst1) { + // if (cond) + // z += cst2; + // } + // + // Will become: + // + // int z = 0; + // int i = 0; + // for (; i++ < M; i += cst1) { + // if (cond) + // z += cst2; + // } + // for (; i++ < M; i += cst1) { + // if (cond) + // z += cst2; + // } + loop_->GetHeaderBlock()->ForEachPhiInst([cloned_loop_exit, def_use_mgr, + clone_results, + this](Instruction* phi) { + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + if (!loop_->IsInsideLoop(phi->GetSingleWordInOperand(i + 1))) { + phi->SetInOperand(i, + {clone_results->value_map_.at( + exit_value_.at(phi->result_id())->result_id())}); + phi->SetInOperand(i + 1, {cloned_loop_exit}); + def_use_mgr->AnalyzeInstUse(phi); + return; + } + } + }); + + // Force the creation of a new preheader for the original loop and set it as + // the merge block for the cloned loop. + // TODO(1841): Handle failure to create pre-header. + cloned_loop_->SetMergeBlock(loop_->GetOrCreatePreHeaderBlock()); +} + +void LoopPeeling::InsertCanonicalInductionVariable( + LoopUtils::LoopCloningResult* clone_results) { + if (original_loop_canonical_induction_variable_) { + canonical_induction_variable_ = + context_->get_def_use_mgr()->GetDef(clone_results->value_map_.at( + original_loop_canonical_induction_variable_->result_id())); + return; + } + + BasicBlock::iterator insert_point = GetClonedLoop()->GetLatchBlock()->tail(); + if (GetClonedLoop()->GetLatchBlock()->GetMergeInst()) { + --insert_point; + } + InstructionBuilder builder( + context_, &*insert_point, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* uint_1_cst = + builder.GetIntConstant(1, int_type_->IsSigned()); + // Create the increment. + // Note that we do "1 + 1" here, one of the operand should the phi + // value but we don't have it yet. The operand will be set latter. + Instruction* iv_inc = builder.AddIAdd( + uint_1_cst->type_id(), uint_1_cst->result_id(), uint_1_cst->result_id()); + + builder.SetInsertPoint(&*GetClonedLoop()->GetHeaderBlock()->begin()); + + canonical_induction_variable_ = builder.AddPhi( + uint_1_cst->type_id(), + {builder.GetIntConstant(0, int_type_->IsSigned())->result_id(), + GetClonedLoop()->GetPreHeaderBlock()->id(), iv_inc->result_id(), + GetClonedLoop()->GetLatchBlock()->id()}); + // Connect everything. + iv_inc->SetInOperand(0, {canonical_induction_variable_->result_id()}); + + // Update def/use manager. + context_->get_def_use_mgr()->AnalyzeInstUse(iv_inc); + + // If do-while form, use the incremented value. + if (do_while_form_) { + canonical_induction_variable_ = iv_inc; + } +} + +void LoopPeeling::GetIteratorUpdateOperations( + const Loop* loop, Instruction* iterator, + std::unordered_set* operations) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + operations->insert(iterator); + iterator->ForEachInId([def_use_mgr, loop, operations, this](uint32_t* id) { + Instruction* insn = def_use_mgr->GetDef(*id); + if (insn->opcode() == SpvOpLabel) { + return; + } + if (operations->count(insn)) { + return; + } + if (!loop->IsInsideLoop(insn)) { + return; + } + GetIteratorUpdateOperations(loop, insn, operations); + }); +} + +// Gather the set of blocks for all the path from |entry| to |root|. +static void GetBlocksInPath(uint32_t block, uint32_t entry, + std::unordered_set* blocks_in_path, + const CFG& cfg) { + for (uint32_t pid : cfg.preds(block)) { + if (blocks_in_path->insert(pid).second) { + if (pid != entry) { + GetBlocksInPath(pid, entry, blocks_in_path, cfg); + } + } + } +} + +bool LoopPeeling::IsConditionCheckSideEffectFree() const { + CFG& cfg = *context_->cfg(); + + // The "do-while" form does not cause issues, the algorithm takes into account + // the first iteration. + if (!do_while_form_) { + uint32_t condition_block_id = cfg.preds(loop_->GetMergeBlock()->id())[0]; + + std::unordered_set blocks_in_path; + + blocks_in_path.insert(condition_block_id); + GetBlocksInPath(condition_block_id, loop_->GetHeaderBlock()->id(), + &blocks_in_path, cfg); + + for (uint32_t bb_id : blocks_in_path) { + BasicBlock* bb = cfg.block(bb_id); + if (!bb->WhileEachInst([this](Instruction* insn) { + if (insn->IsBranch()) return true; + switch (insn->opcode()) { + case SpvOpLabel: + case SpvOpSelectionMerge: + case SpvOpLoopMerge: + return true; + default: + break; + } + return context_->IsCombinatorInstruction(insn); + })) { + return false; + } + } + } + + return true; +} + +void LoopPeeling::GetIteratingExitValues() { + CFG& cfg = *context_->cfg(); + + loop_->GetHeaderBlock()->ForEachPhiInst( + [this](Instruction* phi) { exit_value_[phi->result_id()] = nullptr; }); + + if (!loop_->GetMergeBlock()) { + return; + } + if (cfg.preds(loop_->GetMergeBlock()->id()).size() != 1) { + return; + } + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + uint32_t condition_block_id = cfg.preds(loop_->GetMergeBlock()->id())[0]; + + auto& header_pred = cfg.preds(loop_->GetHeaderBlock()->id()); + do_while_form_ = std::find(header_pred.begin(), header_pred.end(), + condition_block_id) != header_pred.end(); + if (do_while_form_) { + loop_->GetHeaderBlock()->ForEachPhiInst( + [condition_block_id, def_use_mgr, this](Instruction* phi) { + std::unordered_set operations; + + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + if (condition_block_id == phi->GetSingleWordInOperand(i + 1)) { + exit_value_[phi->result_id()] = + def_use_mgr->GetDef(phi->GetSingleWordInOperand(i)); + } + } + }); + } else { + DominatorTree* dom_tree = + &context_->GetDominatorAnalysis(loop_utils_.GetFunction()) + ->GetDomTree(); + BasicBlock* condition_block = cfg.block(condition_block_id); + + loop_->GetHeaderBlock()->ForEachPhiInst( + [dom_tree, condition_block, this](Instruction* phi) { + std::unordered_set operations; + + // Not the back-edge value, check if the phi instruction is the only + // possible candidate. + GetIteratorUpdateOperations(loop_, phi, &operations); + + for (Instruction* insn : operations) { + if (insn == phi) { + continue; + } + if (dom_tree->Dominates(context_->get_instr_block(insn), + condition_block)) { + return; + } + } + exit_value_[phi->result_id()] = phi; + }); + } +} + +void LoopPeeling::FixExitCondition( + const std::function& condition_builder) { + CFG& cfg = *context_->cfg(); + + uint32_t condition_block_id = 0; + for (uint32_t id : cfg.preds(GetClonedLoop()->GetMergeBlock()->id())) { + if (GetClonedLoop()->IsInsideLoop(id)) { + condition_block_id = id; + break; + } + } + assert(condition_block_id != 0 && "2nd loop in improperly connected"); + + BasicBlock* condition_block = cfg.block(condition_block_id); + Instruction* exit_condition = condition_block->terminator(); + assert(exit_condition->opcode() == SpvOpBranchConditional); + BasicBlock::iterator insert_point = condition_block->tail(); + if (condition_block->GetMergeInst()) { + --insert_point; + } + + exit_condition->SetInOperand(0, {condition_builder(&*insert_point)}); + + uint32_t to_continue_block_idx = + GetClonedLoop()->IsInsideLoop(exit_condition->GetSingleWordInOperand(1)) + ? 1 + : 2; + exit_condition->SetInOperand( + 1, {exit_condition->GetSingleWordInOperand(to_continue_block_idx)}); + exit_condition->SetInOperand(2, {GetClonedLoop()->GetMergeBlock()->id()}); + + // Update def/use manager. + context_->get_def_use_mgr()->AnalyzeInstUse(exit_condition); +} + +BasicBlock* LoopPeeling::CreateBlockBefore(BasicBlock* bb) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + CFG& cfg = *context_->cfg(); + assert(cfg.preds(bb->id()).size() == 1 && "More than one predecessor"); + + // TODO(1841): Handle id overflow. + std::unique_ptr new_bb = + MakeUnique(std::unique_ptr(new Instruction( + context_, SpvOpLabel, 0, context_->TakeNextId(), {}))); + new_bb->SetParent(loop_utils_.GetFunction()); + // Update the loop descriptor. + Loop* in_loop = (*loop_utils_.GetLoopDescriptor())[bb]; + if (in_loop) { + in_loop->AddBasicBlock(new_bb.get()); + loop_utils_.GetLoopDescriptor()->SetBasicBlockToLoop(new_bb->id(), in_loop); + } + + context_->set_instr_block(new_bb->GetLabelInst(), new_bb.get()); + def_use_mgr->AnalyzeInstDefUse(new_bb->GetLabelInst()); + + BasicBlock* bb_pred = cfg.block(cfg.preds(bb->id())[0]); + bb_pred->tail()->ForEachInId([bb, &new_bb](uint32_t* id) { + if (*id == bb->id()) { + *id = new_bb->id(); + } + }); + cfg.RemoveEdge(bb_pred->id(), bb->id()); + cfg.AddEdge(bb_pred->id(), new_bb->id()); + def_use_mgr->AnalyzeInstUse(&*bb_pred->tail()); + + // Update the incoming branch. + bb->ForEachPhiInst([&new_bb, def_use_mgr](Instruction* phi) { + phi->SetInOperand(1, {new_bb->id()}); + def_use_mgr->AnalyzeInstUse(phi); + }); + InstructionBuilder( + context_, new_bb.get(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping) + .AddBranch(bb->id()); + cfg.RegisterBlock(new_bb.get()); + + // Add the basic block to the function. + Function::iterator it = loop_utils_.GetFunction()->FindBlock(bb->id()); + assert(it != loop_utils_.GetFunction()->end() && + "Basic block not found in the function."); + BasicBlock* ret = new_bb.get(); + loop_utils_.GetFunction()->AddBasicBlock(std::move(new_bb), it); + return ret; +} + +BasicBlock* LoopPeeling::ProtectLoop(Loop* loop, Instruction* condition, + BasicBlock* if_merge) { + // TODO(1841): Handle failure to create pre-header. + BasicBlock* if_block = loop->GetOrCreatePreHeaderBlock(); + // Will no longer be a pre-header because of the if. + loop->SetPreHeaderBlock(nullptr); + // Kill the branch to the header. + context_->KillInst(&*if_block->tail()); + + InstructionBuilder builder( + context_, if_block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + builder.AddConditionalBranch(condition->result_id(), + loop->GetHeaderBlock()->id(), if_merge->id(), + if_merge->id()); + + return if_block; +} + +void LoopPeeling::PeelBefore(uint32_t peel_factor) { + assert(CanPeelLoop() && "Cannot peel loop"); + LoopUtils::LoopCloningResult clone_results; + + // Clone the loop and insert the cloned one before the loop. + DuplicateAndConnectLoop(&clone_results); + + // Add a canonical induction variable "canonical_induction_variable_". + InsertCanonicalInductionVariable(&clone_results); + + InstructionBuilder builder( + context_, &*cloned_loop_->GetPreHeaderBlock()->tail(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* factor = + builder.GetIntConstant(peel_factor, int_type_->IsSigned()); + + Instruction* has_remaining_iteration = builder.AddLessThan( + factor->result_id(), loop_iteration_count_->result_id()); + Instruction* max_iteration = builder.AddSelect( + factor->type_id(), has_remaining_iteration->result_id(), + factor->result_id(), loop_iteration_count_->result_id()); + + // Change the exit condition of the cloned loop to be (exit when become + // false): + // "canonical_induction_variable_" < min("factor", "loop_iteration_count_") + FixExitCondition([max_iteration, this](Instruction* insert_before_point) { + return InstructionBuilder(context_, insert_before_point, + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping) + .AddLessThan(canonical_induction_variable_->result_id(), + max_iteration->result_id()) + ->result_id(); + }); + + // "Protect" the second loop: the second loop can only be executed if + // |has_remaining_iteration| is true (i.e. factor < loop_iteration_count_). + BasicBlock* if_merge_block = loop_->GetMergeBlock(); + loop_->SetMergeBlock(CreateBlockBefore(loop_->GetMergeBlock())); + // Prevent the second loop from being executed if we already executed all the + // required iterations. + BasicBlock* if_block = + ProtectLoop(loop_, has_remaining_iteration, if_merge_block); + // Patch the phi of the merge block. + if_merge_block->ForEachPhiInst( + [&clone_results, if_block, this](Instruction* phi) { + // if_merge_block had previously only 1 predecessor. + uint32_t incoming_value = phi->GetSingleWordInOperand(0); + auto def_in_loop = clone_results.value_map_.find(incoming_value); + if (def_in_loop != clone_results.value_map_.end()) + incoming_value = def_in_loop->second; + phi->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {incoming_value}}); + phi->AddOperand( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {if_block->id()}}); + context_->get_def_use_mgr()->AnalyzeInstUse(phi); + }); + + context_->InvalidateAnalysesExceptFor( + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisCFG); +} + +void LoopPeeling::PeelAfter(uint32_t peel_factor) { + assert(CanPeelLoop() && "Cannot peel loop"); + LoopUtils::LoopCloningResult clone_results; + + // Clone the loop and insert the cloned one before the loop. + DuplicateAndConnectLoop(&clone_results); + + // Add a canonical induction variable "canonical_induction_variable_". + InsertCanonicalInductionVariable(&clone_results); + + InstructionBuilder builder( + context_, &*cloned_loop_->GetPreHeaderBlock()->tail(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + Instruction* factor = + builder.GetIntConstant(peel_factor, int_type_->IsSigned()); + + Instruction* has_remaining_iteration = builder.AddLessThan( + factor->result_id(), loop_iteration_count_->result_id()); + + // Change the exit condition of the cloned loop to be (exit when become + // false): + // "canonical_induction_variable_" + "factor" < "loop_iteration_count_" + FixExitCondition([factor, this](Instruction* insert_before_point) { + InstructionBuilder cond_builder( + context_, insert_before_point, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // Build the following check: canonical_induction_variable_ + factor < + // iteration_count + return cond_builder + .AddLessThan(cond_builder + .AddIAdd(canonical_induction_variable_->type_id(), + canonical_induction_variable_->result_id(), + factor->result_id()) + ->result_id(), + loop_iteration_count_->result_id()) + ->result_id(); + }); + + // "Protect" the first loop: the first loop can only be executed if + // factor < loop_iteration_count_. + + // The original loop's pre-header was the cloned loop merge block. + GetClonedLoop()->SetMergeBlock( + CreateBlockBefore(GetOriginalLoop()->GetPreHeaderBlock())); + // Use the second loop preheader as if merge block. + + // Prevent the first loop if only the peeled loop needs it. + BasicBlock* if_block = ProtectLoop(cloned_loop_, has_remaining_iteration, + GetOriginalLoop()->GetPreHeaderBlock()); + + // Patch the phi of the header block. + // We added an if to enclose the first loop and because the phi node are + // connected to the exit value of the first loop, the definition no longer + // dominate the preheader. + // We had to the preheader (our if merge block) the required phi instruction + // and patch the header phi. + GetOriginalLoop()->GetHeaderBlock()->ForEachPhiInst( + [&clone_results, if_block, this](Instruction* phi) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + auto find_value_idx = [](Instruction* phi_inst, Loop* loop) { + uint32_t preheader_value_idx = + !loop->IsInsideLoop(phi_inst->GetSingleWordInOperand(1)) ? 0 : 2; + return preheader_value_idx; + }; + + Instruction* cloned_phi = + def_use_mgr->GetDef(clone_results.value_map_.at(phi->result_id())); + uint32_t cloned_preheader_value = cloned_phi->GetSingleWordInOperand( + find_value_idx(cloned_phi, GetClonedLoop())); + + Instruction* new_phi = + InstructionBuilder(context_, + &*GetOriginalLoop()->GetPreHeaderBlock()->tail(), + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping) + .AddPhi(phi->type_id(), + {phi->GetSingleWordInOperand( + find_value_idx(phi, GetOriginalLoop())), + GetClonedLoop()->GetMergeBlock()->id(), + cloned_preheader_value, if_block->id()}); + + phi->SetInOperand(find_value_idx(phi, GetOriginalLoop()), + {new_phi->result_id()}); + def_use_mgr->AnalyzeInstUse(phi); + }); + + context_->InvalidateAnalysesExceptFor( + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisCFG); +} + +Pass::Status LoopPeelingPass::Process() { + bool modified = false; + Module* module = context()->module(); + + // Process each function in the module + for (Function& f : *module) { + modified |= ProcessFunction(&f); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool LoopPeelingPass::ProcessFunction(Function* f) { + bool modified = false; + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(f); + + std::vector to_process_loop; + to_process_loop.reserve(loop_descriptor.NumLoops()); + for (Loop& l : loop_descriptor) { + to_process_loop.push_back(&l); + } + + ScalarEvolutionAnalysis scev_analysis(context()); + + for (Loop* loop : to_process_loop) { + CodeMetrics loop_size; + loop_size.Analyze(*loop); + + auto try_peel = [&loop_size, &modified, this](Loop* loop_to_peel) -> Loop* { + if (!loop_to_peel->IsLCSSA()) { + LoopUtils(context(), loop_to_peel).MakeLoopClosedSSA(); + } + + bool peeled_loop; + Loop* still_peelable_loop; + std::tie(peeled_loop, still_peelable_loop) = + ProcessLoop(loop_to_peel, &loop_size); + + if (peeled_loop) { + modified = true; + } + + return still_peelable_loop; + }; + + Loop* still_peelable_loop = try_peel(loop); + // The pass is working out the maximum factor by which a loop can be peeled. + // If the loop can potentially be peeled again, then there is only one + // possible direction, so only one call is still needed. + if (still_peelable_loop) { + try_peel(loop); + } + } + + return modified; +} + +std::pair LoopPeelingPass::ProcessLoop(Loop* loop, + CodeMetrics* loop_size) { + ScalarEvolutionAnalysis* scev_analysis = + context()->GetScalarEvolutionAnalysis(); + // Default values for bailing out. + std::pair bail_out{false, nullptr}; + + BasicBlock* exit_block = loop->FindConditionBlock(); + if (!exit_block) { + return bail_out; + } + + Instruction* exiting_iv = loop->FindConditionVariable(exit_block); + if (!exiting_iv) { + return bail_out; + } + size_t iterations = 0; + if (!loop->FindNumberOfIterations(exiting_iv, &*exit_block->tail(), + &iterations)) { + return bail_out; + } + if (!iterations) { + return bail_out; + } + + Instruction* canonical_induction_variable = nullptr; + + loop->GetHeaderBlock()->WhileEachPhiInst([&canonical_induction_variable, + scev_analysis, + this](Instruction* insn) { + if (const SERecurrentNode* iv = + scev_analysis->AnalyzeInstruction(insn)->AsSERecurrentNode()) { + const SEConstantNode* offset = iv->GetOffset()->AsSEConstantNode(); + const SEConstantNode* coeff = iv->GetCoefficient()->AsSEConstantNode(); + if (offset && coeff && offset->FoldToSingleValue() == 0 && + coeff->FoldToSingleValue() == 1) { + if (context()->get_type_mgr()->GetType(insn->type_id())->AsInteger()) { + canonical_induction_variable = insn; + return false; + } + } + } + return true; + }); + + bool is_signed = canonical_induction_variable + ? context() + ->get_type_mgr() + ->GetType(canonical_induction_variable->type_id()) + ->AsInteger() + ->IsSigned() + : false; + + LoopPeeling peeler( + loop, + InstructionBuilder( + context(), loop->GetHeaderBlock(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping) + .GetIntConstant(static_cast(iterations), + is_signed), + canonical_induction_variable); + + if (!peeler.CanPeelLoop()) { + return bail_out; + } + + // For each basic block in the loop, check if it can be peeled. If it + // can, get the direction (before/after) and by which factor. + LoopPeelingInfo peel_info(loop, iterations, scev_analysis); + + uint32_t peel_before_factor = 0; + uint32_t peel_after_factor = 0; + + for (uint32_t block : loop->GetBlocks()) { + if (block == exit_block->id()) { + continue; + } + BasicBlock* bb = cfg()->block(block); + PeelDirection direction; + uint32_t factor; + std::tie(direction, factor) = peel_info.GetPeelingInfo(bb); + + if (direction == PeelDirection::kNone) { + continue; + } + if (direction == PeelDirection::kBefore) { + peel_before_factor = std::max(peel_before_factor, factor); + } else { + assert(direction == PeelDirection::kAfter); + peel_after_factor = std::max(peel_after_factor, factor); + } + } + PeelDirection direction = PeelDirection::kNone; + uint32_t factor = 0; + + // Find which direction we should peel. + if (peel_before_factor) { + factor = peel_before_factor; + direction = PeelDirection::kBefore; + } + if (peel_after_factor) { + if (peel_before_factor < peel_after_factor) { + // Favor a peel after here and give the peel before another shot later. + factor = peel_after_factor; + direction = PeelDirection::kAfter; + } + } + + // Do the peel if we can. + if (direction == PeelDirection::kNone) return bail_out; + + // This does not take into account branch elimination opportunities and + // the unrolling. It assumes the peeled loop will be unrolled as well. + if (factor * loop_size->roi_size_ > code_grow_threshold_) { + return bail_out; + } + loop_size->roi_size_ *= factor; + + // Find if a loop should be peeled again. + Loop* extra_opportunity = nullptr; + + if (direction == PeelDirection::kBefore) { + peeler.PeelBefore(factor); + if (stats_) { + stats_->peeled_loops_.emplace_back(loop, PeelDirection::kBefore, factor); + } + if (peel_after_factor) { + // We could have peeled after, give it another try. + extra_opportunity = peeler.GetOriginalLoop(); + } + } else { + peeler.PeelAfter(factor); + if (stats_) { + stats_->peeled_loops_.emplace_back(loop, PeelDirection::kAfter, factor); + } + if (peel_before_factor) { + // We could have peeled before, give it another try. + extra_opportunity = peeler.GetClonedLoop(); + } + } + + return {true, extra_opportunity}; +} + +uint32_t LoopPeelingPass::LoopPeelingInfo::GetFirstLoopInvariantOperand( + Instruction* condition) const { + for (uint32_t i = 0; i < condition->NumInOperands(); i++) { + BasicBlock* bb = + context_->get_instr_block(condition->GetSingleWordInOperand(i)); + if (bb && loop_->IsInsideLoop(bb)) { + return condition->GetSingleWordInOperand(i); + } + } + + return 0; +} + +uint32_t LoopPeelingPass::LoopPeelingInfo::GetFirstNonLoopInvariantOperand( + Instruction* condition) const { + for (uint32_t i = 0; i < condition->NumInOperands(); i++) { + BasicBlock* bb = + context_->get_instr_block(condition->GetSingleWordInOperand(i)); + if (!bb || !loop_->IsInsideLoop(bb)) { + return condition->GetSingleWordInOperand(i); + } + } + + return 0; +} + +static bool IsHandledCondition(SpvOp opcode) { + switch (opcode) { + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: + case SpvOpULessThan: + case SpvOpSLessThan: + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: + return true; + default: + return false; + } +} + +LoopPeelingPass::LoopPeelingInfo::Direction +LoopPeelingPass::LoopPeelingInfo::GetPeelingInfo(BasicBlock* bb) const { + if (bb->terminator()->opcode() != SpvOpBranchConditional) { + return GetNoneDirection(); + } + + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + Instruction* condition = + def_use_mgr->GetDef(bb->terminator()->GetSingleWordInOperand(0)); + + if (!IsHandledCondition(condition->opcode())) { + return GetNoneDirection(); + } + + if (!GetFirstLoopInvariantOperand(condition)) { + // No loop invariant, it cannot be peeled by this pass. + return GetNoneDirection(); + } + if (!GetFirstNonLoopInvariantOperand(condition)) { + // Seems to be a job for the unswitch pass. + return GetNoneDirection(); + } + + // Left hand-side. + SExpression lhs = scev_analysis_->AnalyzeInstruction( + def_use_mgr->GetDef(condition->GetSingleWordInOperand(0))); + if (lhs->GetType() == SENode::CanNotCompute) { + // Can't make any conclusion. + return GetNoneDirection(); + } + + // Right hand-side. + SExpression rhs = scev_analysis_->AnalyzeInstruction( + def_use_mgr->GetDef(condition->GetSingleWordInOperand(1))); + if (rhs->GetType() == SENode::CanNotCompute) { + // Can't make any conclusion. + return GetNoneDirection(); + } + + // Only take into account recurrent expression over the current loop. + bool is_lhs_rec = !scev_analysis_->IsLoopInvariant(loop_, lhs); + bool is_rhs_rec = !scev_analysis_->IsLoopInvariant(loop_, rhs); + + if ((is_lhs_rec && is_rhs_rec) || (!is_lhs_rec && !is_rhs_rec)) { + return GetNoneDirection(); + } + + if (is_lhs_rec) { + if (!lhs->AsSERecurrentNode() || + lhs->AsSERecurrentNode()->GetLoop() != loop_) { + return GetNoneDirection(); + } + } + if (is_rhs_rec) { + if (!rhs->AsSERecurrentNode() || + rhs->AsSERecurrentNode()->GetLoop() != loop_) { + return GetNoneDirection(); + } + } + + // If the op code is ==, then we try a peel before or after. + // If opcode is not <, >, <= or >=, we bail out. + // + // For the remaining cases, we canonicalize the expression so that the + // constant expression is on the left hand side and the recurring expression + // is on the right hand side. If we swap hand side, then < becomes >, <= + // becomes >= etc. + // If the opcode is <=, then we add 1 to the right hand side and do the peel + // check on <. + // If the opcode is >=, then we add 1 to the left hand side and do the peel + // check on >. + + CmpOperator cmp_operator; + switch (condition->opcode()) { + default: + return GetNoneDirection(); + case SpvOpIEqual: + case SpvOpINotEqual: + return HandleEquality(lhs, rhs); + case SpvOpUGreaterThan: + case SpvOpSGreaterThan: { + cmp_operator = CmpOperator::kGT; + break; + } + case SpvOpULessThan: + case SpvOpSLessThan: { + cmp_operator = CmpOperator::kLT; + break; + } + // We add one to transform >= into > and <= into <. + case SpvOpUGreaterThanEqual: + case SpvOpSGreaterThanEqual: { + cmp_operator = CmpOperator::kGE; + break; + } + case SpvOpULessThanEqual: + case SpvOpSLessThanEqual: { + cmp_operator = CmpOperator::kLE; + break; + } + } + + // Force the left hand side to be the non recurring expression. + if (is_lhs_rec) { + std::swap(lhs, rhs); + switch (cmp_operator) { + case CmpOperator::kLT: { + cmp_operator = CmpOperator::kGT; + break; + } + case CmpOperator::kGT: { + cmp_operator = CmpOperator::kLT; + break; + } + case CmpOperator::kLE: { + cmp_operator = CmpOperator::kGE; + break; + } + case CmpOperator::kGE: { + cmp_operator = CmpOperator::kLE; + break; + } + } + } + return HandleInequality(cmp_operator, lhs, rhs->AsSERecurrentNode()); +} + +SExpression LoopPeelingPass::LoopPeelingInfo::GetValueAtFirstIteration( + SERecurrentNode* rec) const { + return rec->GetOffset(); +} + +SExpression LoopPeelingPass::LoopPeelingInfo::GetValueAtIteration( + SERecurrentNode* rec, int64_t iteration) const { + SExpression coeff = rec->GetCoefficient(); + SExpression offset = rec->GetOffset(); + + return (coeff * iteration) + offset; +} + +SExpression LoopPeelingPass::LoopPeelingInfo::GetValueAtLastIteration( + SERecurrentNode* rec) const { + return GetValueAtIteration(rec, loop_max_iterations_ - 1); +} + +bool LoopPeelingPass::LoopPeelingInfo::EvalOperator(CmpOperator cmp_op, + SExpression lhs, + SExpression rhs, + bool* result) const { + assert(scev_analysis_->IsLoopInvariant(loop_, lhs)); + assert(scev_analysis_->IsLoopInvariant(loop_, rhs)); + // We perform the test: 0 cmp_op rhs - lhs + // What is left is then to determine the sign of the expression. + switch (cmp_op) { + case CmpOperator::kLT: { + return scev_analysis_->IsAlwaysGreaterThanZero(rhs - lhs, result); + } + case CmpOperator::kGT: { + return scev_analysis_->IsAlwaysGreaterThanZero(lhs - rhs, result); + } + case CmpOperator::kLE: { + return scev_analysis_->IsAlwaysGreaterOrEqualToZero(rhs - lhs, result); + } + case CmpOperator::kGE: { + return scev_analysis_->IsAlwaysGreaterOrEqualToZero(lhs - rhs, result); + } + } + return false; +} + +LoopPeelingPass::LoopPeelingInfo::Direction +LoopPeelingPass::LoopPeelingInfo::HandleEquality(SExpression lhs, + SExpression rhs) const { + { + // Try peel before opportunity. + SExpression lhs_cst = lhs; + if (SERecurrentNode* rec_node = lhs->AsSERecurrentNode()) { + lhs_cst = rec_node->GetOffset(); + } + SExpression rhs_cst = rhs; + if (SERecurrentNode* rec_node = rhs->AsSERecurrentNode()) { + rhs_cst = rec_node->GetOffset(); + } + + if (lhs_cst == rhs_cst) { + return Direction{LoopPeelingPass::PeelDirection::kBefore, 1}; + } + } + + { + // Try peel after opportunity. + SExpression lhs_cst = lhs; + if (SERecurrentNode* rec_node = lhs->AsSERecurrentNode()) { + // rec_node(x) = a * x + b + // assign to lhs: a * (loop_max_iterations_ - 1) + b + lhs_cst = GetValueAtLastIteration(rec_node); + } + SExpression rhs_cst = rhs; + if (SERecurrentNode* rec_node = rhs->AsSERecurrentNode()) { + // rec_node(x) = a * x + b + // assign to lhs: a * (loop_max_iterations_ - 1) + b + rhs_cst = GetValueAtLastIteration(rec_node); + } + + if (lhs_cst == rhs_cst) { + return Direction{LoopPeelingPass::PeelDirection::kAfter, 1}; + } + } + + return GetNoneDirection(); +} + +LoopPeelingPass::LoopPeelingInfo::Direction +LoopPeelingPass::LoopPeelingInfo::HandleInequality(CmpOperator cmp_op, + SExpression lhs, + SERecurrentNode* rhs) const { + SExpression offset = rhs->GetOffset(); + SExpression coefficient = rhs->GetCoefficient(); + // Compute (cst - B) / A. + std::pair flip_iteration = (lhs - offset) / coefficient; + if (!flip_iteration.first->AsSEConstantNode()) { + return GetNoneDirection(); + } + // note: !!flip_iteration.second normalize to 0/1 (via bool cast). + int64_t iteration = + flip_iteration.first->AsSEConstantNode()->FoldToSingleValue() + + !!flip_iteration.second; + if (iteration <= 0 || + loop_max_iterations_ <= static_cast(iteration)) { + // Always true or false within the loop bounds. + return GetNoneDirection(); + } + // If this is a <= or >= operator and the iteration, make sure |iteration| is + // the one flipping the condition. + // If (cst - B) and A are not divisible, this equivalent to a < or > check, so + // we skip this test. + if (!flip_iteration.second && + (cmp_op == CmpOperator::kLE || cmp_op == CmpOperator::kGE)) { + bool first_iteration; + bool current_iteration; + if (!EvalOperator(cmp_op, lhs, offset, &first_iteration) || + !EvalOperator(cmp_op, lhs, GetValueAtIteration(rhs, iteration), + ¤t_iteration)) { + return GetNoneDirection(); + } + // If the condition did not flip the next will. + if (first_iteration == current_iteration) { + iteration++; + } + } + + uint32_t cast_iteration = 0; + // Integrity check: can we fit |iteration| in a uint32_t ? + if (static_cast(iteration) < std::numeric_limits::max()) { + cast_iteration = static_cast(iteration); + } + + if (cast_iteration) { + // Peel before if we are closer to the start, after if closer to the end. + if (loop_max_iterations_ / 2 > cast_iteration) { + return Direction{LoopPeelingPass::PeelDirection::kBefore, cast_iteration}; + } else { + return Direction{ + LoopPeelingPass::PeelDirection::kAfter, + static_cast(loop_max_iterations_ - cast_iteration)}; + } + } + + return GetNoneDirection(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_peeling.h b/third_party/spirv-tools/source/opt/loop_peeling.h new file mode 100644 index 0000000..413f896 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_peeling.h @@ -0,0 +1,336 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_PEELING_H_ +#define SOURCE_OPT_LOOP_PEELING_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" +#include "source/opt/pass.h" +#include "source/opt/scalar_analysis.h" + +namespace spvtools { +namespace opt { + +// Utility class to perform the peeling of a given loop. +// The loop peeling transformation make a certain amount of a loop iterations to +// be executed either before (peel before) or after (peel after) the transformed +// loop. +// +// For peeling cases the transformation does the following steps: +// - It clones the loop and inserts the cloned loop before the original loop; +// - It connects all iterating values of the cloned loop with the +// corresponding original loop values so that the second loop starts with +// the appropriate values. +// - It inserts a new induction variable "i" is inserted into the cloned that +// starts with the value 0 and increment by step of one. +// +// The last step is specific to each case: +// - Peel before: the transformation is to peel the "N" first iterations. +// The exit condition of the cloned loop is changed so that the loop +// exits when "i < N" becomes false. The original loop is then protected to +// only execute if there is any iteration left to do. +// - Peel after: the transformation is to peel the "N" last iterations, +// then the exit condition of the cloned loop is changed so that the loop +// exits when "i + N < max_iteration" becomes false, where "max_iteration" +// is the upper bound of the loop. The cloned loop is then protected to +// only execute if there is any iteration left to do no covered by the +// second. +// +// To be peelable: +// - The loop must be in LCSSA form; +// - The loop must not contain any breaks; +// - The loop must not have any ambiguous iterators updates (see +// "CanPeelLoop"). +// The method "CanPeelLoop" checks that those constrained are met. +class LoopPeeling { + public: + // LoopPeeling constructor. + // |loop| is the loop to peel. + // |loop_iteration_count| is the instruction holding the |loop| iteration + // count, must be invariant for |loop| and must be of an int 32 type (signed + // or unsigned). + // |canonical_induction_variable| is an induction variable that can be used to + // count the number of iterations, must be of the same type as + // |loop_iteration_count| and start at 0 and increase by step of one at each + // iteration. The value nullptr is interpreted as no suitable variable exists + // and one will be created. + LoopPeeling(Loop* loop, Instruction* loop_iteration_count, + Instruction* canonical_induction_variable = nullptr) + : context_(loop->GetContext()), + loop_utils_(loop->GetContext(), loop), + loop_(loop), + loop_iteration_count_(!loop->IsInsideLoop(loop_iteration_count) + ? loop_iteration_count + : nullptr), + int_type_(nullptr), + original_loop_canonical_induction_variable_( + canonical_induction_variable), + canonical_induction_variable_(nullptr) { + if (loop_iteration_count_) { + int_type_ = context_->get_type_mgr() + ->GetType(loop_iteration_count_->type_id()) + ->AsInteger(); + if (canonical_induction_variable_) { + assert(canonical_induction_variable_->type_id() == + loop_iteration_count_->type_id() && + "loop_iteration_count and canonical_induction_variable do not " + "have the same type"); + } + } + GetIteratingExitValues(); + } + + // Returns true if the loop can be peeled. + // To be peelable, all operation involved in the update of the loop iterators + // must not dominates the exit condition. This restriction is a work around to + // not miss compile code like: + // + // for (int i = 0; i + 1 < N; i++) {} + // for (int i = 0; ++i < N; i++) {} + // + // The increment will happen before the test on the exit condition leading to + // very look-a-like code. + // + // This restriction will not apply if a loop rotate is applied before (i.e. + // becomes a do-while loop). + bool CanPeelLoop() const { + CFG& cfg = *context_->cfg(); + + if (!loop_iteration_count_) { + return false; + } + if (!int_type_) { + return false; + } + if (int_type_->width() != 32) { + return false; + } + if (!loop_->IsLCSSA()) { + return false; + } + if (!loop_->GetMergeBlock()) { + return false; + } + if (cfg.preds(loop_->GetMergeBlock()->id()).size() != 1) { + return false; + } + if (!IsConditionCheckSideEffectFree()) { + return false; + } + + return !std::any_of(exit_value_.cbegin(), exit_value_.cend(), + [](std::pair it) { + return it.second == nullptr; + }); + } + + // Moves the execution of the |factor| first iterations of the loop into a + // dedicated loop. + void PeelBefore(uint32_t factor); + + // Moves the execution of the |factor| last iterations of the loop into a + // dedicated loop. + void PeelAfter(uint32_t factor); + + // Returns the cloned loop. + Loop* GetClonedLoop() { return cloned_loop_; } + // Returns the original loop. + Loop* GetOriginalLoop() { return loop_; } + + private: + IRContext* context_; + LoopUtils loop_utils_; + // The original loop. + Loop* loop_; + // The initial |loop_| upper bound. + Instruction* loop_iteration_count_; + // The int type to use for the canonical_induction_variable_. + analysis::Integer* int_type_; + // The cloned loop. + Loop* cloned_loop_; + // This is set to true when the exit and back-edge branch instruction is the + // same. + bool do_while_form_; + // The canonical induction variable from the original loop if it exists. + Instruction* original_loop_canonical_induction_variable_; + // The canonical induction variable of the cloned loop. The induction variable + // is initialized to 0 and incremented by step of 1. + Instruction* canonical_induction_variable_; + // Map between loop iterators and exit values. Loop iterators + std::unordered_map exit_value_; + + // Duplicate |loop_| and place the new loop before the cloned loop. Iterating + // values from the cloned loop are then connected to the original loop as + // initializer. + void DuplicateAndConnectLoop(LoopUtils::LoopCloningResult* clone_results); + + // Insert the canonical induction variable into the first loop as a simplified + // counter. + void InsertCanonicalInductionVariable( + LoopUtils::LoopCloningResult* clone_results); + + // Fixes the exit condition of the before loop. The function calls + // |condition_builder| to get the condition to use in the conditional branch + // of the loop exit. The loop will be exited if the condition evaluate to + // true. |condition_builder| takes an Instruction* that represent the + // insertion point. + void FixExitCondition( + const std::function& condition_builder); + + // Gathers all operations involved in the update of |iterator| into + // |operations|. + void GetIteratorUpdateOperations( + const Loop* loop, Instruction* iterator, + std::unordered_set* operations); + + // Gathers exiting iterator values. The function builds a map between each + // iterating value in the loop (a phi instruction in the loop header) and its + // SSA value when it exit the loop. If no exit value can be accurately found, + // it is map to nullptr (see comment on CanPeelLoop). + void GetIteratingExitValues(); + + // Returns true if a for-loop has no instruction with effects before the + // condition check. + bool IsConditionCheckSideEffectFree() const; + + // Creates a new basic block and insert it between |bb| and the predecessor of + // |bb|. + BasicBlock* CreateBlockBefore(BasicBlock* bb); + + // Inserts code to only execute |loop| only if the given |condition| is true. + // |if_merge| is a suitable basic block to be used by the if condition as + // merge block. + // The function returns the if block protecting the loop. + BasicBlock* ProtectLoop(Loop* loop, Instruction* condition, + BasicBlock* if_merge); +}; + +// Implements a loop peeling optimization. +// For each loop, the pass will try to peel it if there is conditions that +// are true for the "N" first or last iterations of the loop. +// To avoid code size explosion, too large loops will not be peeled. +class LoopPeelingPass : public Pass { + public: + // Describes the peeling direction. + enum class PeelDirection { + kNone, // Cannot peel + kBefore, // Can peel before + kAfter // Can peel last + }; + + // Holds some statistics about peeled function. + struct LoopPeelingStats { + std::vector> peeled_loops_; + }; + + LoopPeelingPass(LoopPeelingStats* stats = nullptr) : stats_(stats) {} + + // Sets the loop peeling growth threshold. If the code size increase is above + // |code_grow_threshold|, the loop will not be peeled. The code size is + // measured in terms of SPIR-V instructions. + static void SetLoopPeelingThreshold(size_t code_grow_threshold) { + code_grow_threshold_ = code_grow_threshold; + } + + // Returns the loop peeling code growth threshold. + static size_t GetLoopPeelingThreshold() { return code_grow_threshold_; } + + const char* name() const override { return "loop-peeling"; } + + // Processes the given |module|. Returns Status::Failure if errors occur when + // processing. Returns the corresponding Status::Success if processing is + // succesful to indicate whether changes have been made to the modue. + Pass::Status Process() override; + + private: + // Describes the peeling direction. + enum class CmpOperator { + kLT, // less than + kGT, // greater than + kLE, // less than or equal + kGE, // greater than or equal + }; + + class LoopPeelingInfo { + public: + using Direction = std::pair; + + LoopPeelingInfo(Loop* loop, size_t loop_max_iterations, + ScalarEvolutionAnalysis* scev_analysis) + : context_(loop->GetContext()), + loop_(loop), + scev_analysis_(scev_analysis), + loop_max_iterations_(loop_max_iterations) {} + + // Returns by how much and to which direction a loop should be peeled to + // make the conditional branch of the basic block |bb| an unconditional + // branch. If |bb|'s terminator is not a conditional branch or the condition + // is not workable then it returns PeelDirection::kNone and a 0 factor. + Direction GetPeelingInfo(BasicBlock* bb) const; + + private: + // Returns the id of the loop invariant operand of the conditional + // expression |condition|. It returns if no operand is invariant. + uint32_t GetFirstLoopInvariantOperand(Instruction* condition) const; + // Returns the id of the non loop invariant operand of the conditional + // expression |condition|. It returns if all operands are invariant. + uint32_t GetFirstNonLoopInvariantOperand(Instruction* condition) const; + + // Returns the value of |rec| at the first loop iteration. + SExpression GetValueAtFirstIteration(SERecurrentNode* rec) const; + // Returns the value of |rec| at the given |iteration|. + SExpression GetValueAtIteration(SERecurrentNode* rec, + int64_t iteration) const; + // Returns the value of |rec| at the last loop iteration. + SExpression GetValueAtLastIteration(SERecurrentNode* rec) const; + + bool EvalOperator(CmpOperator cmp_op, SExpression lhs, SExpression rhs, + bool* result) const; + + Direction HandleEquality(SExpression lhs, SExpression rhs) const; + Direction HandleInequality(CmpOperator cmp_op, SExpression lhs, + SERecurrentNode* rhs) const; + + static Direction GetNoneDirection() { + return Direction{LoopPeelingPass::PeelDirection::kNone, 0}; + } + IRContext* context_; + Loop* loop_; + ScalarEvolutionAnalysis* scev_analysis_; + size_t loop_max_iterations_; + }; + // Peel profitable loops in |f|. + bool ProcessFunction(Function* f); + // Peel |loop| if profitable. + std::pair ProcessLoop(Loop* loop, CodeMetrics* loop_size); + + static size_t code_grow_threshold_; + LoopPeelingStats* stats_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_PEELING_H_ diff --git a/third_party/spirv-tools/source/opt/loop_unroller.cpp b/third_party/spirv-tools/source/opt/loop_unroller.cpp new file mode 100644 index 0000000..6cdced4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_unroller.cpp @@ -0,0 +1,1119 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_unroller.h" + +#include +#include +#include +#include +#include +#include + +#include "source/opt/ir_builder.h" +#include "source/opt/loop_utils.h" + +// Implements loop util unrolling functionality for fully and partially +// unrolling loops. Given a factor it will duplicate the loop that many times, +// appending each one to the end of the old loop and removing backedges, to +// create a new unrolled loop. +// +// 1 - User calls LoopUtils::FullyUnroll or LoopUtils::PartiallyUnroll with a +// loop they wish to unroll. LoopUtils::CanPerformUnroll is used to +// validate that a given loop can be unrolled. That method (along with the +// constructor of loop) checks that the IR is in the expected canonicalised +// format. +// +// 2 - The LoopUtils methods create a LoopUnrollerUtilsImpl object to actually +// perform the unrolling. This implements helper methods to copy the loop basic +// blocks and remap the ids of instructions used inside them. +// +// 3 - The core of LoopUnrollerUtilsImpl is the Unroll method, this method +// actually performs the loop duplication. It does this by creating a +// LoopUnrollState object and then copying the loop as given by the factor +// parameter. The LoopUnrollState object retains the state of the unroller +// between the loop body copies as each iteration needs information on the last +// to adjust the phi induction variable, adjust the OpLoopMerge instruction in +// the main loop header, and change the previous continue block to point to the +// new header and the new continue block to the main loop header. +// +// 4 - If the loop is to be fully unrolled then it is simply closed after step +// 3, with the OpLoopMerge being deleted, the backedge removed, and the +// condition blocks folded. +// +// 5 - If it is being partially unrolled: if the unrolling factor leaves the +// loop with an even number of bodies with respect to the number of loop +// iterations then step 3 is all that is needed. If it is uneven then we need to +// duplicate the loop completely and unroll the duplicated loop to cover the +// residual part and adjust the first loop to cover only the "even" part. For +// instance if you request an unroll factor of 3 on a loop with 10 iterations +// then copying the body three times would leave you with three bodies in the +// loop +// where the loop still iterates over each 4 times. So we make two loops one +// iterating once then a second loop of three iterating 3 times. + +namespace spvtools { +namespace opt { +namespace { + +// Loop control constant value for DontUnroll flag. +static const uint32_t kLoopControlDontUnrollIndex = 2; + +// Operand index of the loop control parameter of the OpLoopMerge. +static const uint32_t kLoopControlIndex = 2; + +// This utility class encapsulates some of the state we need to maintain between +// loop unrolls. Specifically it maintains key blocks and the induction variable +// in the current loop duplication step and the blocks from the previous one. +// This is because each step of the unroll needs to use data from both the +// preceding step and the original loop. +struct LoopUnrollState { + LoopUnrollState() + : previous_phi_(nullptr), + previous_latch_block_(nullptr), + previous_condition_block_(nullptr), + new_phi(nullptr), + new_continue_block(nullptr), + new_condition_block(nullptr), + new_header_block(nullptr) {} + + // Initialize from the loop descriptor class. + LoopUnrollState(Instruction* induction, BasicBlock* latch_block, + BasicBlock* condition, std::vector&& phis) + : previous_phi_(induction), + previous_latch_block_(latch_block), + previous_condition_block_(condition), + new_phi(nullptr), + new_continue_block(nullptr), + new_condition_block(nullptr), + new_header_block(nullptr) { + previous_phis_ = std::move(phis); + } + + // Swap the state so that the new nodes are now the previous nodes. + void NextIterationState() { + previous_phi_ = new_phi; + previous_latch_block_ = new_latch_block; + previous_condition_block_ = new_condition_block; + previous_phis_ = std::move(new_phis_); + + // Clear new nodes. + new_phi = nullptr; + new_continue_block = nullptr; + new_condition_block = nullptr; + new_header_block = nullptr; + new_latch_block = nullptr; + + // Clear new block/instruction maps. + new_blocks.clear(); + new_inst.clear(); + ids_to_new_inst.clear(); + } + + // The induction variable from the immediately preceding loop body. + Instruction* previous_phi_; + + // All the phi nodes from the previous loop iteration. + std::vector previous_phis_; + + std::vector new_phis_; + + // The previous latch block. The backedge will be removed from this and + // added to the new latch block. + BasicBlock* previous_latch_block_; + + // The previous condition block. This may be folded to flatten the loop. + BasicBlock* previous_condition_block_; + + // The new induction variable. + Instruction* new_phi; + + // The new continue block. + BasicBlock* new_continue_block; + + // The new condition block. + BasicBlock* new_condition_block; + + // The new header block. + BasicBlock* new_header_block; + + // The new latch block. + BasicBlock* new_latch_block; + + // A mapping of new block ids to the original blocks which they were copied + // from. + std::unordered_map new_blocks; + + // A mapping of the original instruction ids to the instruction ids to their + // copies. + std::unordered_map new_inst; + + std::unordered_map ids_to_new_inst; +}; + +// This class implements the actual unrolling. It uses a LoopUnrollState to +// maintain the state of the unrolling inbetween steps. +class LoopUnrollerUtilsImpl { + public: + using BasicBlockListTy = std::vector>; + + LoopUnrollerUtilsImpl(IRContext* c, Function* function) + : context_(c), + function_(*function), + loop_condition_block_(nullptr), + loop_induction_variable_(nullptr), + number_of_loop_iterations_(0), + loop_step_value_(0), + loop_init_value_(0) {} + + // Unroll the |loop| by given |factor| by copying the whole body |factor| + // times. The resulting basicblock structure will remain a loop. + void PartiallyUnroll(Loop*, size_t factor); + + // If partially unrolling the |loop| would leave the loop with too many bodies + // for its number of iterations then this method should be used. This method + // will duplicate the |loop| completely, making the duplicated loop the + // successor of the original's merge block. The original loop will have its + // condition changed to loop over the residual part and the duplicate will be + // partially unrolled. The resulting structure will be two loops. + void PartiallyUnrollResidualFactor(Loop* loop, size_t factor); + + // Fully unroll the |loop| by copying the full body by the total number of + // loop iterations, folding all conditions, and removing the backedge from the + // continue block to the header. + void FullyUnroll(Loop* loop); + + // Get the ID of the variable in the |phi| paired with |label|. + uint32_t GetPhiDefID(const Instruction* phi, uint32_t label) const; + + // Close the loop by removing the OpLoopMerge from the |loop| header block and + // making the backedge point to the merge block. + void CloseUnrolledLoop(Loop* loop); + + // Remove the OpConditionalBranch instruction inside |conditional_block| used + // to branch to either exit or continue the loop and replace it with an + // unconditional OpBranch to block |new_target|. + void FoldConditionBlock(BasicBlock* condtion_block, uint32_t new_target); + + // Add all blocks_to_add_ to function_ at the |insert_point|. + void AddBlocksToFunction(const BasicBlock* insert_point); + + // Duplicates the |old_loop|, cloning each body and remaping the ids without + // removing instructions or changing relative structure. Result will be stored + // in |new_loop|. + void DuplicateLoop(Loop* old_loop, Loop* new_loop); + + inline size_t GetLoopIterationCount() const { + return number_of_loop_iterations_; + } + + // Extracts the initial state information from the |loop|. + void Init(Loop* loop); + + // Replace the uses of each induction variable outside the loop with the final + // value of the induction variable before the loop exit. To reflect the proper + // state of a fully unrolled loop. + void ReplaceInductionUseWithFinalValue(Loop* loop); + + // Remove all the instructions in the invalidated_instructions_ vector. + void RemoveDeadInstructions(); + + // Replace any use of induction variables outwith the loop with the final + // value of the induction variable in the unrolled loop. + void ReplaceOutsideLoopUseWithFinalValue(Loop* loop); + + // Set the LoopControl operand of the OpLoopMerge instruction to be + // DontUnroll. + void MarkLoopControlAsDontUnroll(Loop* loop) const; + + private: + // Remap all the in |basic_block| to new IDs and keep the mapping of new ids + // to old + // ids. |loop| is used to identify special loop blocks (header, continue, + // ect). + void AssignNewResultIds(BasicBlock* basic_block); + + // Using the map built by AssignNewResultIds, replace the uses in |inst| + // by the id that the use maps to. + void RemapOperands(Instruction* inst); + + // Using the map built by AssignNewResultIds, for each instruction in + // |basic_block| use + // that map to substitute the IDs used by instructions (in the operands) with + // the new ids. + void RemapOperands(BasicBlock* basic_block); + + // Copy the whole body of the loop, all blocks dominated by the |loop| header + // and not dominated by the |loop| merge. The copied body will be linked to by + // the old |loop| continue block and the new body will link to the |loop| + // header via the new continue block. |eliminate_conditions| is used to decide + // whether or not to fold all the condition blocks other than the last one. + void CopyBody(Loop* loop, bool eliminate_conditions); + + // Copy a given |block_to_copy| in the |loop| and record the mapping of the + // old/new ids. |preserve_instructions| determines whether or not the method + // will modify (other than result_id) instructions which are copied. + void CopyBasicBlock(Loop* loop, const BasicBlock* block_to_copy, + bool preserve_instructions); + + // The actual implementation of the unroll step. Unrolls |loop| by given + // |factor| by copying the body by |factor| times. Also propagates the + // induction variable value throughout the copies. + void Unroll(Loop* loop, size_t factor); + + // Fills the loop_blocks_inorder_ field with the ordered list of basic blocks + // as computed by the method ComputeLoopOrderedBlocks. + void ComputeLoopOrderedBlocks(Loop* loop); + + // Adds the blocks_to_add_ to both the |loop| and to the parent of |loop| if + // the parent exists. + void AddBlocksToLoop(Loop* loop) const; + + // After the partially unroll step the phi instructions in the header block + // will be in an illegal format. This function makes the phis legal by making + // the edge from the latch block come from the new latch block and the value + // to be the actual value of the phi at that point. + void LinkLastPhisToStart(Loop* loop) const; + + // Kill all debug declaration instructions from |bb|. + void KillDebugDeclares(BasicBlock* bb); + + // A pointer to the IRContext. Used to add/remove instructions and for usedef + // chains. + IRContext* context_; + + // A reference the function the loop is within. + Function& function_; + + // A list of basic blocks to be added to the loop at the end of an unroll + // step. + BasicBlockListTy blocks_to_add_; + + // List of instructions which are now dead and can be removed. + std::vector invalidated_instructions_; + + // Maintains the current state of the transform between calls to unroll. + LoopUnrollState state_; + + // An ordered list containing the loop basic blocks. + std::vector loop_blocks_inorder_; + + // The block containing the condition check which contains a conditional + // branch to the merge and continue block. + BasicBlock* loop_condition_block_; + + // The induction variable of the loop. + Instruction* loop_induction_variable_; + + // Phis used in the loop need to be remapped to use the actual result values + // and then be remapped at the end. + std::vector loop_phi_instructions_; + + // The number of loop iterations that the loop would preform pre-unroll. + size_t number_of_loop_iterations_; + + // The amount that the loop steps each iteration. + int64_t loop_step_value_; + + // The value the loop starts stepping from. + int64_t loop_init_value_; +}; + +/* + * Static helper functions. + */ + +// Retrieve the index of the OpPhi instruction |phi| which corresponds to the +// incoming |block| id. +static uint32_t GetPhiIndexFromLabel(const BasicBlock* block, + const Instruction* phi) { + for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) { + if (block->id() == phi->GetSingleWordInOperand(i)) { + return i; + } + } + assert(false && "Could not find operand in instruction."); + return 0; +} + +void LoopUnrollerUtilsImpl::Init(Loop* loop) { + loop_condition_block_ = loop->FindConditionBlock(); + + // When we reinit the second loop during PartiallyUnrollResidualFactor we need + // to use the cached value from the duplicate step as the dominator tree + // basded solution, loop->FindConditionBlock, requires all the nodes to be + // connected up with the correct branches. They won't be at this point. + if (!loop_condition_block_) { + loop_condition_block_ = state_.new_condition_block; + } + assert(loop_condition_block_); + + loop_induction_variable_ = loop->FindConditionVariable(loop_condition_block_); + assert(loop_induction_variable_); + + bool found = loop->FindNumberOfIterations( + loop_induction_variable_, &*loop_condition_block_->ctail(), + &number_of_loop_iterations_, &loop_step_value_, &loop_init_value_); + (void)found; // To silence unused variable warning on release builds. + assert(found); + + // Blocks are stored in an unordered set of ids in the loop class, we need to + // create the dominator ordered list. + ComputeLoopOrderedBlocks(loop); +} + +// This function is used to partially unroll the loop when the factor provided +// would normally lead to an illegal optimization. Instead of just unrolling the +// loop it creates two loops and unrolls one and adjusts the condition on the +// other. The end result being that the new loop pair iterates over the correct +// number of bodies. +void LoopUnrollerUtilsImpl::PartiallyUnrollResidualFactor(Loop* loop, + size_t factor) { + // TODO(1841): Handle id overflow. + std::unique_ptr new_label{new Instruction( + context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})}; + std::unique_ptr new_exit_bb{new BasicBlock(std::move(new_label))}; + + // Save the id of the block before we move it. + uint32_t new_merge_id = new_exit_bb->id(); + + // Add the block the list of blocks to add, we want this merge block to be + // right at the start of the new blocks. + blocks_to_add_.push_back(std::move(new_exit_bb)); + BasicBlock* new_exit_bb_raw = blocks_to_add_[0].get(); + Instruction& original_conditional_branch = *loop_condition_block_->tail(); + // Duplicate the loop, providing access to the blocks of both loops. + // This is a naked new due to the VS2013 requirement of not having unique + // pointers in vectors, as it will be inserted into a vector with + // loop_descriptor.AddLoop. + std::unique_ptr new_loop = MakeUnique(*loop); + + // Clear the basic blocks of the new loop. + new_loop->ClearBlocks(); + + DuplicateLoop(loop, new_loop.get()); + + // Add the blocks to the function. + AddBlocksToFunction(loop->GetMergeBlock()); + blocks_to_add_.clear(); + + // Create a new merge block for the first loop. + InstructionBuilder builder{context_, new_exit_bb_raw}; + // Make the first loop branch to the second. + builder.AddBranch(new_loop->GetHeaderBlock()->id()); + + loop_condition_block_ = state_.new_condition_block; + loop_induction_variable_ = state_.new_phi; + // Unroll the new loop by the factor with the usual -1 to account for the + // existing block iteration. + Unroll(new_loop.get(), factor); + + LinkLastPhisToStart(new_loop.get()); + AddBlocksToLoop(new_loop.get()); + + // Add the new merge block to the back of the list of blocks to be added. It + // needs to be the last block added to maintain dominator order in the binary. + blocks_to_add_.push_back( + std::unique_ptr(new_loop->GetMergeBlock())); + + // Add the blocks to the function. + AddBlocksToFunction(loop->GetMergeBlock()); + + // Reset the usedef analysis. + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis); + analysis::DefUseManager* def_use_manager = context_->get_def_use_mgr(); + + // The loop condition. + Instruction* condition_check = def_use_manager->GetDef( + original_conditional_branch.GetSingleWordOperand(0)); + + // This should have been checked by the LoopUtils::CanPerformUnroll function + // before entering this. + assert(loop->IsSupportedCondition(condition_check->opcode())); + + // We need to account for the initial body when calculating the remainder. + int64_t remainder = Loop::GetResidualConditionValue( + condition_check->opcode(), loop_init_value_, loop_step_value_, + number_of_loop_iterations_, factor); + + assert(remainder > std::numeric_limits::min() && + remainder < std::numeric_limits::max()); + + Instruction* new_constant = nullptr; + + // If the remainder is negative then we add a signed constant, otherwise just + // add an unsigned constant. + if (remainder < 0) { + new_constant = builder.GetSintConstant(static_cast(remainder)); + } else { + new_constant = builder.GetUintConstant(static_cast(remainder)); + } + + uint32_t constant_id = new_constant->result_id(); + + // Update the condition check. + condition_check->SetInOperand(1, {constant_id}); + + // Update the next phi node. The phi will have a constant value coming in from + // the preheader block. For the duplicated loop we need to update the constant + // to be the amount of iterations covered by the first loop and the incoming + // block to be the first loops new merge block. + std::vector new_inductions; + new_loop->GetInductionVariables(new_inductions); + + std::vector old_inductions; + loop->GetInductionVariables(old_inductions); + for (size_t index = 0; index < new_inductions.size(); ++index) { + Instruction* new_induction = new_inductions[index]; + Instruction* old_induction = old_inductions[index]; + // Get the index of the loop initalizer, the value coming in from the + // preheader. + uint32_t initalizer_index = + GetPhiIndexFromLabel(new_loop->GetPreHeaderBlock(), old_induction); + + // Replace the second loop initalizer with the phi from the first + new_induction->SetInOperand(initalizer_index - 1, + {old_induction->result_id()}); + new_induction->SetInOperand(initalizer_index, {new_merge_id}); + + // If the use of the first loop induction variable is outside of the loop + // then replace that use with the second loop induction variable. + uint32_t second_loop_induction = new_induction->result_id(); + auto replace_use_outside_of_loop = [loop, second_loop_induction]( + Instruction* user, + uint32_t operand_index) { + if (!loop->IsInsideLoop(user)) { + user->SetOperand(operand_index, {second_loop_induction}); + } + }; + + context_->get_def_use_mgr()->ForEachUse(old_induction, + replace_use_outside_of_loop); + } + + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis); + + context_->ReplaceAllUsesWith(loop->GetMergeBlock()->id(), new_merge_id); + + LoopDescriptor& loop_descriptor = *context_->GetLoopDescriptor(&function_); + + loop_descriptor.AddLoop(std::move(new_loop), loop->GetParent()); + + RemoveDeadInstructions(); +} + +// Mark this loop as DontUnroll as it will already be unrolled and it may not +// be safe to unroll a previously partially unrolled loop. +void LoopUnrollerUtilsImpl::MarkLoopControlAsDontUnroll(Loop* loop) const { + Instruction* loop_merge_inst = loop->GetHeaderBlock()->GetLoopMergeInst(); + assert(loop_merge_inst && + "Loop merge instruction could not be found after entering unroller " + "(should have exited before this)"); + loop_merge_inst->SetInOperand(kLoopControlIndex, + {kLoopControlDontUnrollIndex}); +} + +// Duplicate the |loop| body |factor| - 1 number of times while keeping the loop +// backedge intact. This will leave the loop with |factor| number of bodies +// after accounting for the initial body. +void LoopUnrollerUtilsImpl::Unroll(Loop* loop, size_t factor) { + // If we unroll a loop partially it will not be safe to unroll it further. + // This is due to the current method of calculating the number of loop + // iterations. + MarkLoopControlAsDontUnroll(loop); + + std::vector inductions; + loop->GetInductionVariables(inductions); + state_ = LoopUnrollState{loop_induction_variable_, loop->GetLatchBlock(), + loop_condition_block_, std::move(inductions)}; + for (size_t i = 0; i < factor - 1; ++i) { + CopyBody(loop, true); + } +} + +void LoopUnrollerUtilsImpl::RemoveDeadInstructions() { + // Remove the dead instructions. + for (Instruction* inst : invalidated_instructions_) { + context_->KillInst(inst); + } +} + +void LoopUnrollerUtilsImpl::ReplaceInductionUseWithFinalValue(Loop* loop) { + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis | + IRContext::Analysis::kAnalysisDefUse | + IRContext::Analysis::kAnalysisInstrToBlockMapping); + + std::vector inductions; + loop->GetInductionVariables(inductions); + + for (size_t index = 0; index < inductions.size(); ++index) { + uint32_t trip_step_id = GetPhiDefID(state_.previous_phis_[index], + state_.previous_latch_block_->id()); + context_->ReplaceAllUsesWith(inductions[index]->result_id(), trip_step_id); + invalidated_instructions_.push_back(inductions[index]); + } +} + +// Fully unroll the loop by partially unrolling it by the number of loop +// iterations minus one for the body already accounted for. +void LoopUnrollerUtilsImpl::FullyUnroll(Loop* loop) { + // We unroll the loop by number of iterations in the loop. + Unroll(loop, number_of_loop_iterations_); + + // The first condition block is preserved until now so it can be copied. + FoldConditionBlock(loop_condition_block_, 1); + + // Delete the OpLoopMerge and remove the backedge to the header. + CloseUnrolledLoop(loop); + + // Mark the loop for later deletion. This allows us to preserve the loop + // iterators but still disregard dead loops. + loop->MarkLoopForRemoval(); + + // If the loop has a parent add the new blocks to the parent. + if (loop->GetParent()) { + AddBlocksToLoop(loop->GetParent()); + } + + // Add the blocks to the function. + AddBlocksToFunction(loop->GetMergeBlock()); + + ReplaceInductionUseWithFinalValue(loop); + + RemoveDeadInstructions(); + // Invalidate all analyses. + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis | + IRContext::Analysis::kAnalysisDefUse); +} + +void LoopUnrollerUtilsImpl::KillDebugDeclares(BasicBlock* bb) { + // We cannot kill an instruction inside BasicBlock::ForEachInst() + // because it will generate dangling pointers. We use |to_be_killed| + // to kill them after the loop. + std::vector to_be_killed; + + bb->ForEachInst([&to_be_killed, this](Instruction* inst) { + if (context_->get_debug_info_mgr()->IsDebugDeclare(inst)) { + to_be_killed.push_back(inst); + } + }); + for (auto* inst : to_be_killed) context_->KillInst(inst); +} + +// Copy a given basic block, give it a new result_id, and store the new block +// and the id mapping in the state. |preserve_instructions| is used to determine +// whether or not this function should edit instructions other than the +// |result_id|. +void LoopUnrollerUtilsImpl::CopyBasicBlock(Loop* loop, const BasicBlock* itr, + bool preserve_instructions) { + // Clone the block exactly, including the IDs. + BasicBlock* basic_block = itr->Clone(context_); + basic_block->SetParent(itr->GetParent()); + + // We do not want to duplicate DebugDeclare. + KillDebugDeclares(basic_block); + + // Assign each result a new unique ID and keep a mapping of the old ids to + // the new ones. + AssignNewResultIds(basic_block); + + // If this is the continue block we are copying. + if (itr == loop->GetContinueBlock()) { + // Make the OpLoopMerge point to this block for the continue. + if (!preserve_instructions) { + Instruction* merge_inst = loop->GetHeaderBlock()->GetLoopMergeInst(); + merge_inst->SetInOperand(1, {basic_block->id()}); + context_->UpdateDefUse(merge_inst); + } + + state_.new_continue_block = basic_block; + } + + // If this is the header block we are copying. + if (itr == loop->GetHeaderBlock()) { + state_.new_header_block = basic_block; + + if (!preserve_instructions) { + // Remove the loop merge instruction if it exists. + Instruction* merge_inst = basic_block->GetLoopMergeInst(); + if (merge_inst) invalidated_instructions_.push_back(merge_inst); + } + } + + // If this is the latch block being copied, record it in the state. + if (itr == loop->GetLatchBlock()) state_.new_latch_block = basic_block; + + // If this is the condition block we are copying. + if (itr == loop_condition_block_) { + state_.new_condition_block = basic_block; + } + + // Add this block to the list of blocks to add to the function at the end of + // the unrolling process. + blocks_to_add_.push_back(std::unique_ptr(basic_block)); + + // Keep tracking the old block via a map. + state_.new_blocks[itr->id()] = basic_block; +} + +void LoopUnrollerUtilsImpl::CopyBody(Loop* loop, bool eliminate_conditions) { + // Copy each basic block in the loop, give them new ids, and save state + // information. + for (const BasicBlock* itr : loop_blocks_inorder_) { + CopyBasicBlock(loop, itr, false); + } + + // Set the previous latch block to point to the new header. + Instruction* latch_branch = state_.previous_latch_block_->terminator(); + latch_branch->SetInOperand(0, {state_.new_header_block->id()}); + context_->UpdateDefUse(latch_branch); + + // As the algorithm copies the original loop blocks exactly, the tail of the + // latch block on iterations after the first one will be a branch to the new + // header and not the actual loop header. The last continue block in the loop + // should always be a backedge to the global header. + Instruction* new_latch_branch = state_.new_latch_block->terminator(); + new_latch_branch->SetInOperand(0, {loop->GetHeaderBlock()->id()}); + context_->AnalyzeUses(new_latch_branch); + + std::vector inductions; + loop->GetInductionVariables(inductions); + for (size_t index = 0; index < inductions.size(); ++index) { + Instruction* primary_copy = inductions[index]; + + assert(primary_copy->result_id() != 0); + Instruction* induction_clone = + state_.ids_to_new_inst[state_.new_inst[primary_copy->result_id()]]; + + state_.new_phis_.push_back(induction_clone); + assert(induction_clone->result_id() != 0); + + if (!state_.previous_phis_.empty()) { + state_.new_inst[primary_copy->result_id()] = GetPhiDefID( + state_.previous_phis_[index], state_.previous_latch_block_->id()); + } else { + // Do not replace the first phi block ids. + state_.new_inst[primary_copy->result_id()] = primary_copy->result_id(); + } + } + + if (eliminate_conditions && + state_.new_condition_block != loop_condition_block_) { + FoldConditionBlock(state_.new_condition_block, 1); + } + + // Only reference to the header block is the backedge in the latch block, + // don't change this. + state_.new_inst[loop->GetHeaderBlock()->id()] = loop->GetHeaderBlock()->id(); + + for (auto& pair : state_.new_blocks) { + RemapOperands(pair.second); + } + + for (Instruction* dead_phi : state_.new_phis_) + invalidated_instructions_.push_back(dead_phi); + + // Swap the state so the new is now the previous. + state_.NextIterationState(); +} + +uint32_t LoopUnrollerUtilsImpl::GetPhiDefID(const Instruction* phi, + uint32_t label) const { + for (uint32_t operand = 3; operand < phi->NumOperands(); operand += 2) { + if (phi->GetSingleWordOperand(operand) == label) { + return phi->GetSingleWordOperand(operand - 1); + } + } + assert(false && "Could not find a phi index matching the provided label"); + return 0; +} + +void LoopUnrollerUtilsImpl::FoldConditionBlock(BasicBlock* condition_block, + uint32_t operand_label) { + // Remove the old conditional branch to the merge and continue blocks. + Instruction& old_branch = *condition_block->tail(); + uint32_t new_target = old_branch.GetSingleWordOperand(operand_label); + + DebugScope scope = old_branch.GetDebugScope(); + const std::vector lines = old_branch.dbg_line_insts(); + + context_->KillInst(&old_branch); + // Add the new unconditional branch to the merge block. + InstructionBuilder builder( + context_, condition_block, + IRContext::Analysis::kAnalysisDefUse | + IRContext::Analysis::kAnalysisInstrToBlockMapping); + Instruction* new_branch = builder.AddBranch(new_target); + + new_branch->set_dbg_line_insts(lines); + new_branch->SetDebugScope(scope); +} + +void LoopUnrollerUtilsImpl::CloseUnrolledLoop(Loop* loop) { + // Remove the OpLoopMerge instruction from the function. + Instruction* merge_inst = loop->GetHeaderBlock()->GetLoopMergeInst(); + invalidated_instructions_.push_back(merge_inst); + + // Remove the final backedge to the header and make it point instead to the + // merge block. + Instruction* latch_instruction = state_.previous_latch_block_->terminator(); + latch_instruction->SetInOperand(0, {loop->GetMergeBlock()->id()}); + context_->UpdateDefUse(latch_instruction); + + // Remove all induction variables as the phis will now be invalid. Replace all + // uses with the constant initializer value (all uses of phis will be in + // the first iteration with the subsequent phis already having been removed). + std::vector inductions; + loop->GetInductionVariables(inductions); + + // We can use the state instruction mechanism to replace all internal loop + // values within the first loop trip (as the subsequent ones will be updated + // by the copy function) with the value coming in from the preheader and then + // use context ReplaceAllUsesWith for the uses outside the loop with the final + // trip phi value. + state_.new_inst.clear(); + for (Instruction* induction : inductions) { + uint32_t initalizer_id = + GetPhiDefID(induction, loop->GetPreHeaderBlock()->id()); + + state_.new_inst[induction->result_id()] = initalizer_id; + } + + for (BasicBlock* block : loop_blocks_inorder_) { + RemapOperands(block); + } + + // Rewrite the last phis, since they may still reference the original phi. + for (Instruction* last_phi : state_.previous_phis_) { + RemapOperands(last_phi); + } +} + +// Uses the first loop to create a copy of the loop with new IDs. +void LoopUnrollerUtilsImpl::DuplicateLoop(Loop* old_loop, Loop* new_loop) { + std::vector new_block_order; + + // Copy every block in the old loop. + for (const BasicBlock* itr : loop_blocks_inorder_) { + CopyBasicBlock(old_loop, itr, true); + new_block_order.push_back(blocks_to_add_.back().get()); + } + + // Clone the merge block, give it a new id and record it in the state. + BasicBlock* new_merge = old_loop->GetMergeBlock()->Clone(context_); + new_merge->SetParent(old_loop->GetMergeBlock()->GetParent()); + AssignNewResultIds(new_merge); + state_.new_blocks[old_loop->GetMergeBlock()->id()] = new_merge; + + // Remap the operands of every instruction in the loop to point to the new + // copies. + for (auto& pair : state_.new_blocks) { + RemapOperands(pair.second); + } + + loop_blocks_inorder_ = std::move(new_block_order); + + AddBlocksToLoop(new_loop); + + new_loop->SetHeaderBlock(state_.new_header_block); + new_loop->SetContinueBlock(state_.new_continue_block); + new_loop->SetLatchBlock(state_.new_latch_block); + new_loop->SetMergeBlock(new_merge); +} + +// Whenever the utility copies a block it stores it in a tempory buffer, this +// function adds the buffer into the Function. The blocks will be inserted +// after the block |insert_point|. +void LoopUnrollerUtilsImpl::AddBlocksToFunction( + const BasicBlock* insert_point) { + for (auto basic_block_iterator = function_.begin(); + basic_block_iterator != function_.end(); ++basic_block_iterator) { + if (basic_block_iterator->id() == insert_point->id()) { + basic_block_iterator.InsertBefore(&blocks_to_add_); + return; + } + } + + assert( + false && + "Could not add basic blocks to function as insert point was not found."); +} + +// Assign all result_ids in |basic_block| instructions to new IDs and preserve +// the mapping of new ids to old ones. +void LoopUnrollerUtilsImpl::AssignNewResultIds(BasicBlock* basic_block) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + // Label instructions aren't covered by normal traversal of the + // instructions. + // TODO(1841): Handle id overflow. + uint32_t new_label_id = context_->TakeNextId(); + + // Assign a new id to the label. + state_.new_inst[basic_block->GetLabelInst()->result_id()] = new_label_id; + basic_block->GetLabelInst()->SetResultId(new_label_id); + def_use_mgr->AnalyzeInstDefUse(basic_block->GetLabelInst()); + + for (Instruction& inst : *basic_block) { + uint32_t old_id = inst.result_id(); + + // Ignore stores etc. + if (old_id == 0) { + continue; + } + + // Give the instruction a new id. + // TODO(1841): Handle id overflow. + inst.SetResultId(context_->TakeNextId()); + def_use_mgr->AnalyzeInstDef(&inst); + + // Save the mapping of old_id -> new_id. + state_.new_inst[old_id] = inst.result_id(); + // Check if this instruction is the induction variable. + if (loop_induction_variable_->result_id() == old_id) { + // Save a pointer to the new copy of it. + state_.new_phi = &inst; + } + state_.ids_to_new_inst[inst.result_id()] = &inst; + } +} + +void LoopUnrollerUtilsImpl::RemapOperands(Instruction* inst) { + auto remap_operands_to_new_ids = [this](uint32_t* id) { + auto itr = state_.new_inst.find(*id); + + if (itr != state_.new_inst.end()) { + *id = itr->second; + } + }; + + inst->ForEachInId(remap_operands_to_new_ids); + context_->AnalyzeUses(inst); +} + +void LoopUnrollerUtilsImpl::RemapOperands(BasicBlock* basic_block) { + for (Instruction& inst : *basic_block) { + RemapOperands(&inst); + } +} + +// Generate the ordered list of basic blocks in the |loop| and cache it for +// later use. +void LoopUnrollerUtilsImpl::ComputeLoopOrderedBlocks(Loop* loop) { + loop_blocks_inorder_.clear(); + loop->ComputeLoopStructuredOrder(&loop_blocks_inorder_); +} + +// Adds the blocks_to_add_ to both the loop and to the parent. +void LoopUnrollerUtilsImpl::AddBlocksToLoop(Loop* loop) const { + // Add the blocks to this loop. + for (auto& block_itr : blocks_to_add_) { + loop->AddBasicBlock(block_itr.get()); + } + + // Add the blocks to the parent as well. + if (loop->GetParent()) AddBlocksToLoop(loop->GetParent()); +} + +void LoopUnrollerUtilsImpl::LinkLastPhisToStart(Loop* loop) const { + std::vector inductions; + loop->GetInductionVariables(inductions); + + for (size_t i = 0; i < inductions.size(); ++i) { + Instruction* last_phi_in_block = state_.previous_phis_[i]; + + uint32_t phi_index = + GetPhiIndexFromLabel(state_.previous_latch_block_, last_phi_in_block); + uint32_t phi_variable = + last_phi_in_block->GetSingleWordInOperand(phi_index - 1); + uint32_t phi_label = last_phi_in_block->GetSingleWordInOperand(phi_index); + + Instruction* phi = inductions[i]; + phi->SetInOperand(phi_index - 1, {phi_variable}); + phi->SetInOperand(phi_index, {phi_label}); + } +} + +// Duplicate the |loop| body |factor| number of times while keeping the loop +// backedge intact. +void LoopUnrollerUtilsImpl::PartiallyUnroll(Loop* loop, size_t factor) { + Unroll(loop, factor); + LinkLastPhisToStart(loop); + AddBlocksToLoop(loop); + AddBlocksToFunction(loop->GetMergeBlock()); + RemoveDeadInstructions(); +} + +/* + * End LoopUtilsImpl. + */ + +} // namespace + +/* + * + * Begin Utils. + * + * */ + +bool LoopUtils::CanPerformUnroll() { + // The loop is expected to be in structured order. + if (!loop_->GetHeaderBlock()->GetMergeInst()) { + return false; + } + + // Find check the loop has a condition we can find and evaluate. + const BasicBlock* condition = loop_->FindConditionBlock(); + if (!condition) return false; + + // Check that we can find and process the induction variable. + const Instruction* induction = loop_->FindConditionVariable(condition); + if (!induction || induction->opcode() != SpvOpPhi) return false; + + // Check that we can find the number of loop iterations. + if (!loop_->FindNumberOfIterations(induction, &*condition->ctail(), nullptr)) + return false; + + // Make sure the latch block is a unconditional branch to the header + // block. + const Instruction& branch = *loop_->GetLatchBlock()->ctail(); + bool branching_assumption = + branch.opcode() == SpvOpBranch && + branch.GetSingleWordInOperand(0) == loop_->GetHeaderBlock()->id(); + if (!branching_assumption) { + return false; + } + + std::vector inductions; + loop_->GetInductionVariables(inductions); + + // Ban breaks within the loop. + const std::vector& merge_block_preds = + context_->cfg()->preds(loop_->GetMergeBlock()->id()); + if (merge_block_preds.size() != 1) { + return false; + } + + // Ban continues within the loop. + const std::vector& continue_block_preds = + context_->cfg()->preds(loop_->GetContinueBlock()->id()); + if (continue_block_preds.size() != 1) { + return false; + } + + // Ban returns in the loop. + // Iterate over all the blocks within the loop and check that none of them + // exit the loop. + for (uint32_t label_id : loop_->GetBlocks()) { + const BasicBlock* block = context_->cfg()->block(label_id); + if (block->ctail()->opcode() == SpvOp::SpvOpKill || + block->ctail()->opcode() == SpvOp::SpvOpReturn || + block->ctail()->opcode() == SpvOp::SpvOpReturnValue || + block->ctail()->opcode() == SpvOp::SpvOpTerminateInvocation) { + return false; + } + } + // Can only unroll inner loops. + if (!loop_->AreAllChildrenMarkedForRemoval()) { + return false; + } + + return true; +} + +bool LoopUtils::PartiallyUnroll(size_t factor) { + if (factor == 1 || !CanPerformUnroll()) return false; + + // Create the unroller utility. + LoopUnrollerUtilsImpl unroller{context_, + loop_->GetHeaderBlock()->GetParent()}; + unroller.Init(loop_); + + // If the unrolling factor is larger than or the same size as the loop just + // fully unroll the loop. + if (factor >= unroller.GetLoopIterationCount()) { + unroller.FullyUnroll(loop_); + return true; + } + + // If the loop unrolling factor is an residual number of iterations we need to + // let run the loop for the residual part then let it branch into the unrolled + // remaining part. We add one when calucating the remainder to take into + // account the one iteration already in the loop. + if (unroller.GetLoopIterationCount() % factor != 0) { + unroller.PartiallyUnrollResidualFactor(loop_, factor); + } else { + unroller.PartiallyUnroll(loop_, factor); + } + + return true; +} + +bool LoopUtils::FullyUnroll() { + if (!CanPerformUnroll()) return false; + + std::vector inductions; + loop_->GetInductionVariables(inductions); + + LoopUnrollerUtilsImpl unroller{context_, + loop_->GetHeaderBlock()->GetParent()}; + + unroller.Init(loop_); + unroller.FullyUnroll(loop_); + + return true; +} + +void LoopUtils::Finalize() { + // Clean up the loop descriptor to preserve the analysis. + + LoopDescriptor* LD = context_->GetLoopDescriptor(&function_); + LD->PostModificationCleanup(); +} + +/* + * + * Begin Pass. + * + */ + +Pass::Status LoopUnroller::Process() { + bool changed = false; + for (Function& f : *context()->module()) { + LoopDescriptor* LD = context()->GetLoopDescriptor(&f); + for (Loop& loop : *LD) { + LoopUtils loop_utils{context(), &loop}; + if (!loop.HasUnrollLoopControl() || !loop_utils.CanPerformUnroll()) { + continue; + } + + if (fully_unroll_) { + loop_utils.FullyUnroll(); + } else { + loop_utils.PartiallyUnroll(unroll_factor_); + } + changed = true; + } + LD->PostModificationCleanup(); + } + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_unroller.h b/third_party/spirv-tools/source/opt/loop_unroller.h new file mode 100644 index 0000000..71e7cca --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_unroller.h @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_UNROLLER_H_ +#define SOURCE_OPT_LOOP_UNROLLER_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class LoopUnroller : public Pass { + public: + LoopUnroller() : Pass(), fully_unroll_(true), unroll_factor_(0) {} + LoopUnroller(bool fully_unroll, int unroll_factor) + : Pass(), fully_unroll_(fully_unroll), unroll_factor_(unroll_factor) {} + + const char* name() const override { return "loop-unroll"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + bool fully_unroll_; + int unroll_factor_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_UNROLLER_H_ diff --git a/third_party/spirv-tools/source/opt/loop_unswitch_pass.cpp b/third_party/spirv-tools/source/opt/loop_unswitch_pass.cpp new file mode 100644 index 0000000..d805ecf --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_unswitch_pass.cpp @@ -0,0 +1,620 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/loop_unswitch_pass.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/dominator_tree.h" +#include "source/opt/fold.h" +#include "source/opt/function.h" +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" + +#include "source/opt/loop_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +static const uint32_t kTypePointerStorageClassInIdx = 0; + +} // anonymous namespace + +namespace { + +// This class handle the unswitch procedure for a given loop. +// The unswitch will not happen if: +// - The loop has any instruction that will prevent it; +// - The loop invariant condition is not uniform. +class LoopUnswitch { + public: + LoopUnswitch(IRContext* context, Function* function, Loop* loop, + LoopDescriptor* loop_desc) + : function_(function), + loop_(loop), + loop_desc_(*loop_desc), + context_(context), + switch_block_(nullptr) {} + + // Returns true if the loop can be unswitched. + // Can be unswitch if: + // - The loop has no instructions that prevents it (such as barrier); + // - The loop has one conditional branch or switch that do not depends on the + // loop; + // - The loop invariant condition is uniform; + bool CanUnswitchLoop() { + if (switch_block_) return true; + if (loop_->IsSafeToClone()) return false; + + CFG& cfg = *context_->cfg(); + + for (uint32_t bb_id : loop_->GetBlocks()) { + BasicBlock* bb = cfg.block(bb_id); + if (loop_->GetLatchBlock() == bb) { + continue; + } + + if (bb->terminator()->IsBranch() && + bb->terminator()->opcode() != SpvOpBranch) { + if (IsConditionNonConstantLoopInvariant(bb->terminator())) { + switch_block_ = bb; + break; + } + } + } + + return switch_block_; + } + + // Return the iterator to the basic block |bb|. + Function::iterator FindBasicBlockPosition(BasicBlock* bb_to_find) { + Function::iterator it = function_->FindBlock(bb_to_find->id()); + assert(it != function_->end() && "Basic Block not found"); + return it; + } + + // Creates a new basic block and insert it into the function |fn| at the + // position |ip|. This function preserves the def/use and instr to block + // managers. + BasicBlock* CreateBasicBlock(Function::iterator ip) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + // TODO(1841): Handle id overflow. + BasicBlock* bb = &*ip.InsertBefore(std::unique_ptr( + new BasicBlock(std::unique_ptr(new Instruction( + context_, SpvOpLabel, 0, context_->TakeNextId(), {}))))); + bb->SetParent(function_); + def_use_mgr->AnalyzeInstDef(bb->GetLabelInst()); + context_->set_instr_block(bb->GetLabelInst(), bb); + + return bb; + } + + Instruction* GetValueForDefaultPathForSwitch(Instruction* switch_inst) { + assert(switch_inst->opcode() == SpvOpSwitch && + "The given instructoin must be an OpSwitch."); + + // Find a value that can be used to select the default path. + // If none are possible, then it will just use 0. The value does not matter + // because this path will never be taken becaues the new switch outside of + // the loop cannot select this path either. + std::vector existing_values; + for (uint32_t i = 2; i < switch_inst->NumInOperands(); i += 2) { + existing_values.push_back(switch_inst->GetSingleWordInOperand(i)); + } + std::sort(existing_values.begin(), existing_values.end()); + uint32_t value_for_default_path = 0; + if (existing_values.size() < std::numeric_limits::max()) { + for (value_for_default_path = 0; + value_for_default_path < existing_values.size(); + value_for_default_path++) { + if (existing_values[value_for_default_path] != value_for_default_path) { + break; + } + } + } + InstructionBuilder builder( + context_, static_cast(nullptr), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + return builder.GetUintConstant(value_for_default_path); + } + + // Unswitches |loop_|. + void PerformUnswitch() { + assert(CanUnswitchLoop() && + "Cannot unswitch if there is not constant condition"); + assert(loop_->GetPreHeaderBlock() && "This loop has no pre-header block"); + assert(loop_->IsLCSSA() && "This loop is not in LCSSA form"); + + CFG& cfg = *context_->cfg(); + DominatorTree* dom_tree = + &context_->GetDominatorAnalysis(function_)->GetDomTree(); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + LoopUtils loop_utils(context_, loop_); + + ////////////////////////////////////////////////////////////////////////////// + // Step 1: Create the if merge block for structured modules. + // To do so, the |loop_| merge block will become the if's one and we + // create a merge for the loop. This will limit the amount of duplicated + // code the structured control flow imposes. + // For non structured program, the new loop will be connected to + // the old loop's exit blocks. + ////////////////////////////////////////////////////////////////////////////// + + // Get the merge block if it exists. + BasicBlock* if_merge_block = loop_->GetMergeBlock(); + // The merge block is only created if the loop has a unique exit block. We + // have this guarantee for structured loops, for compute loop it will + // trivially help maintain both a structured-like form and LCSAA. + BasicBlock* loop_merge_block = + if_merge_block + ? CreateBasicBlock(FindBasicBlockPosition(if_merge_block)) + : nullptr; + if (loop_merge_block) { + // Add the instruction and update managers. + InstructionBuilder builder( + context_, loop_merge_block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + builder.AddBranch(if_merge_block->id()); + builder.SetInsertPoint(&*loop_merge_block->begin()); + cfg.RegisterBlock(loop_merge_block); + def_use_mgr->AnalyzeInstDef(loop_merge_block->GetLabelInst()); + // Update CFG. + if_merge_block->ForEachPhiInst( + [loop_merge_block, &builder, this](Instruction* phi) { + Instruction* cloned = phi->Clone(context_); + cloned->SetResultId(TakeNextId()); + builder.AddInstruction(std::unique_ptr(cloned)); + phi->SetInOperand(0, {cloned->result_id()}); + phi->SetInOperand(1, {loop_merge_block->id()}); + for (uint32_t j = phi->NumInOperands() - 1; j > 1; j--) + phi->RemoveInOperand(j); + }); + // Copy the predecessor list (will get invalidated otherwise). + std::vector preds = cfg.preds(if_merge_block->id()); + for (uint32_t pid : preds) { + if (pid == loop_merge_block->id()) continue; + BasicBlock* p_bb = cfg.block(pid); + p_bb->ForEachSuccessorLabel( + [if_merge_block, loop_merge_block](uint32_t* id) { + if (*id == if_merge_block->id()) *id = loop_merge_block->id(); + }); + cfg.AddEdge(pid, loop_merge_block->id()); + } + cfg.RemoveNonExistingEdges(if_merge_block->id()); + // Update loop descriptor. + if (Loop* ploop = loop_->GetParent()) { + ploop->AddBasicBlock(loop_merge_block); + loop_desc_.SetBasicBlockToLoop(loop_merge_block->id(), ploop); + } + // Update the dominator tree. + DominatorTreeNode* loop_merge_dtn = + dom_tree->GetOrInsertNode(loop_merge_block); + DominatorTreeNode* if_merge_block_dtn = + dom_tree->GetOrInsertNode(if_merge_block); + loop_merge_dtn->parent_ = if_merge_block_dtn->parent_; + loop_merge_dtn->children_.push_back(if_merge_block_dtn); + loop_merge_dtn->parent_->children_.push_back(loop_merge_dtn); + if_merge_block_dtn->parent_->children_.erase(std::find( + if_merge_block_dtn->parent_->children_.begin(), + if_merge_block_dtn->parent_->children_.end(), if_merge_block_dtn)); + + loop_->SetMergeBlock(loop_merge_block); + } + + //////////////////////////////////////////////////////////////////////////// + // Step 2: Build a new preheader for |loop_|, use the old one + // for the invariant branch. + //////////////////////////////////////////////////////////////////////////// + + BasicBlock* if_block = loop_->GetPreHeaderBlock(); + // If this preheader is the parent loop header, + // we need to create a dedicated block for the if. + BasicBlock* loop_pre_header = + CreateBasicBlock(++FindBasicBlockPosition(if_block)); + InstructionBuilder( + context_, loop_pre_header, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping) + .AddBranch(loop_->GetHeaderBlock()->id()); + + if_block->tail()->SetInOperand(0, {loop_pre_header->id()}); + + // Update loop descriptor. + if (Loop* ploop = loop_desc_[if_block]) { + ploop->AddBasicBlock(loop_pre_header); + loop_desc_.SetBasicBlockToLoop(loop_pre_header->id(), ploop); + } + + // Update the CFG. + cfg.RegisterBlock(loop_pre_header); + def_use_mgr->AnalyzeInstDef(loop_pre_header->GetLabelInst()); + cfg.AddEdge(if_block->id(), loop_pre_header->id()); + cfg.RemoveNonExistingEdges(loop_->GetHeaderBlock()->id()); + + loop_->GetHeaderBlock()->ForEachPhiInst( + [loop_pre_header, if_block](Instruction* phi) { + phi->ForEachInId([loop_pre_header, if_block](uint32_t* id) { + if (*id == if_block->id()) { + *id = loop_pre_header->id(); + } + }); + }); + loop_->SetPreHeaderBlock(loop_pre_header); + + // Update the dominator tree. + DominatorTreeNode* loop_pre_header_dtn = + dom_tree->GetOrInsertNode(loop_pre_header); + DominatorTreeNode* if_block_dtn = dom_tree->GetTreeNode(if_block); + loop_pre_header_dtn->parent_ = if_block_dtn; + assert( + if_block_dtn->children_.size() == 1 && + "A loop preheader should only have the header block as a child in the " + "dominator tree"); + loop_pre_header_dtn->children_.push_back(if_block_dtn->children_[0]); + if_block_dtn->children_.clear(); + if_block_dtn->children_.push_back(loop_pre_header_dtn); + + // Make domination queries valid. + dom_tree->ResetDFNumbering(); + + // Compute an ordered list of basic block to clone: loop blocks + pre-header + // + merge block. + loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks_, true, true); + + ///////////////////////////// + // Do the actual unswitch: // + // - Clone the loop // + // - Connect exits // + // - Specialize the loop // + ///////////////////////////// + + Instruction* iv_condition = &*switch_block_->tail(); + SpvOp iv_opcode = iv_condition->opcode(); + Instruction* condition = + def_use_mgr->GetDef(iv_condition->GetOperand(0).words[0]); + + analysis::ConstantManager* cst_mgr = context_->get_constant_mgr(); + const analysis::Type* cond_type = + context_->get_type_mgr()->GetType(condition->type_id()); + + // Build the list of value for which we need to clone and specialize the + // loop. + std::vector> constant_branch; + // Special case for the original loop + Instruction* original_loop_constant_value; + if (iv_opcode == SpvOpBranchConditional) { + constant_branch.emplace_back( + cst_mgr->GetDefiningInstruction(cst_mgr->GetConstant(cond_type, {0})), + nullptr); + original_loop_constant_value = + cst_mgr->GetDefiningInstruction(cst_mgr->GetConstant(cond_type, {1})); + } else { + // We are looking to take the default branch, so we can't provide a + // specific value. + original_loop_constant_value = + GetValueForDefaultPathForSwitch(iv_condition); + + for (uint32_t i = 2; i < iv_condition->NumInOperands(); i += 2) { + constant_branch.emplace_back( + cst_mgr->GetDefiningInstruction(cst_mgr->GetConstant( + cond_type, iv_condition->GetInOperand(i).words)), + nullptr); + } + } + + // Get the loop landing pads. + std::unordered_set if_merging_blocks; + std::function is_from_original_loop; + if (loop_->GetHeaderBlock()->GetLoopMergeInst()) { + if_merging_blocks.insert(if_merge_block->id()); + is_from_original_loop = [this](uint32_t id) { + return loop_->IsInsideLoop(id) || loop_->GetMergeBlock()->id() == id; + }; + } else { + loop_->GetExitBlocks(&if_merging_blocks); + is_from_original_loop = [this](uint32_t id) { + return loop_->IsInsideLoop(id); + }; + } + + for (auto& specialisation_pair : constant_branch) { + Instruction* specialisation_value = specialisation_pair.first; + ////////////////////////////////////////////////////////// + // Step 3: Duplicate |loop_|. + ////////////////////////////////////////////////////////// + LoopUtils::LoopCloningResult clone_result; + + Loop* cloned_loop = + loop_utils.CloneLoop(&clone_result, ordered_loop_blocks_); + specialisation_pair.second = cloned_loop->GetPreHeaderBlock(); + + //////////////////////////////////// + // Step 4: Specialize the loop. // + //////////////////////////////////// + + { + SpecializeLoop(cloned_loop, condition, specialisation_value); + + /////////////////////////////////////////////////////////// + // Step 5: Connect convergent edges to the landing pads. // + /////////////////////////////////////////////////////////// + + for (uint32_t merge_bb_id : if_merging_blocks) { + BasicBlock* merge = context_->cfg()->block(merge_bb_id); + // We are in LCSSA so we only care about phi instructions. + merge->ForEachPhiInst( + [is_from_original_loop, &clone_result](Instruction* phi) { + uint32_t num_in_operands = phi->NumInOperands(); + for (uint32_t i = 0; i < num_in_operands; i += 2) { + uint32_t pred = phi->GetSingleWordInOperand(i + 1); + if (is_from_original_loop(pred)) { + pred = clone_result.value_map_.at(pred); + uint32_t incoming_value_id = phi->GetSingleWordInOperand(i); + // Not all the incoming values are coming from the loop. + ValueMapTy::iterator new_value = + clone_result.value_map_.find(incoming_value_id); + if (new_value != clone_result.value_map_.end()) { + incoming_value_id = new_value->second; + } + phi->AddOperand({SPV_OPERAND_TYPE_ID, {incoming_value_id}}); + phi->AddOperand({SPV_OPERAND_TYPE_ID, {pred}}); + } + } + }); + } + } + function_->AddBasicBlocks(clone_result.cloned_bb_.begin(), + clone_result.cloned_bb_.end(), + ++FindBasicBlockPosition(if_block)); + } + + // Specialize the existing loop. + SpecializeLoop(loop_, condition, original_loop_constant_value); + BasicBlock* original_loop_target = loop_->GetPreHeaderBlock(); + + ///////////////////////////////////// + // Finally: connect the new loops. // + ///////////////////////////////////// + + // Delete the old jump + context_->KillInst(&*if_block->tail()); + InstructionBuilder builder(context_, if_block); + if (iv_opcode == SpvOpBranchConditional) { + assert(constant_branch.size() == 1); + builder.AddConditionalBranch( + condition->result_id(), original_loop_target->id(), + constant_branch[0].second->id(), + if_merge_block ? if_merge_block->id() : kInvalidId); + } else { + std::vector> targets; + for (auto& t : constant_branch) { + targets.emplace_back(t.first->GetInOperand(0).words, t.second->id()); + } + + builder.AddSwitch(condition->result_id(), original_loop_target->id(), + targets, + if_merge_block ? if_merge_block->id() : kInvalidId); + } + + switch_block_ = nullptr; + ordered_loop_blocks_.clear(); + + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisLoopAnalysis); + } + + private: + using ValueMapTy = std::unordered_map; + using BlockMapTy = std::unordered_map; + + Function* function_; + Loop* loop_; + LoopDescriptor& loop_desc_; + IRContext* context_; + + BasicBlock* switch_block_; + // Map between instructions and if they are dynamically uniform. + std::unordered_map dynamically_uniform_; + // The loop basic blocks in structured order. + std::vector ordered_loop_blocks_; + + // Returns the next usable id for the context. + uint32_t TakeNextId() { + // TODO(1841): Handle id overflow. + return context_->TakeNextId(); + } + + // Simplifies |loop| assuming the instruction |to_version_insn| takes the + // value |cst_value|. |block_range| is an iterator range returning the loop + // basic blocks in a structured order (dominator first). + // The function will ignore basic blocks returned by |block_range| if they + // does not belong to the loop. + // The set |dead_blocks| will contain all the dead basic blocks. + // + // Requirements: + // - |loop| must be in the LCSSA form; + // - |cst_value| must be constant. + void SpecializeLoop(Loop* loop, Instruction* to_version_insn, + Instruction* cst_value) { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + std::function ignore_node; + ignore_node = [loop](uint32_t bb_id) { return !loop->IsInsideLoop(bb_id); }; + + std::vector> use_list; + def_use_mgr->ForEachUse(to_version_insn, + [&use_list, &ignore_node, this]( + Instruction* inst, uint32_t operand_index) { + BasicBlock* bb = context_->get_instr_block(inst); + + if (!bb || ignore_node(bb->id())) { + // Out of the loop, the specialization does not + // apply any more. + return; + } + use_list.emplace_back(inst, operand_index); + }); + + // First pass: inject the specialized value into the loop (and only the + // loop). + for (auto use : use_list) { + Instruction* inst = use.first; + uint32_t operand_index = use.second; + + // To also handle switch, cst_value can be nullptr: this case + // means that we are looking to branch to the default target of + // the switch. We don't actually know its value so we don't touch + // it if it not a switch. + assert(cst_value && "We do not have a value to use."); + inst->SetOperand(operand_index, {cst_value->result_id()}); + def_use_mgr->AnalyzeInstUse(inst); + } + } + + // Returns true if |var| is dynamically uniform. + // Note: this is currently approximated as uniform. + bool IsDynamicallyUniform(Instruction* var, const BasicBlock* entry, + const DominatorTree& post_dom_tree) { + assert(post_dom_tree.IsPostDominator()); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + auto it = dynamically_uniform_.find(var->result_id()); + + if (it != dynamically_uniform_.end()) return it->second; + + analysis::DecorationManager* dec_mgr = context_->get_decoration_mgr(); + + bool& is_uniform = dynamically_uniform_[var->result_id()]; + is_uniform = false; + + dec_mgr->WhileEachDecoration(var->result_id(), SpvDecorationUniform, + [&is_uniform](const Instruction&) { + is_uniform = true; + return false; + }); + if (is_uniform) { + return is_uniform; + } + + BasicBlock* parent = context_->get_instr_block(var); + if (!parent) { + return is_uniform = true; + } + + if (!post_dom_tree.Dominates(parent->id(), entry->id())) { + return is_uniform = false; + } + if (var->opcode() == SpvOpLoad) { + const uint32_t PtrTypeId = + def_use_mgr->GetDef(var->GetSingleWordInOperand(0))->type_id(); + const Instruction* PtrTypeInst = def_use_mgr->GetDef(PtrTypeId); + uint32_t storage_class = + PtrTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx); + if (storage_class != SpvStorageClassUniform && + storage_class != SpvStorageClassUniformConstant) { + return is_uniform = false; + } + } else { + if (!context_->IsCombinatorInstruction(var)) { + return is_uniform = false; + } + } + + return is_uniform = var->WhileEachInId([entry, &post_dom_tree, + this](const uint32_t* id) { + return IsDynamicallyUniform(context_->get_def_use_mgr()->GetDef(*id), + entry, post_dom_tree); + }); + } + + // Returns true if |insn| is not a constant, but is loop invariant and + // dynamically uniform. + bool IsConditionNonConstantLoopInvariant(Instruction* insn) { + assert(insn->IsBranch()); + assert(insn->opcode() != SpvOpBranch); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + Instruction* condition = def_use_mgr->GetDef(insn->GetOperand(0).words[0]); + if (condition->IsConstant()) { + return false; + } + + if (loop_->IsInsideLoop(condition)) { + return false; + } + + return IsDynamicallyUniform( + condition, function_->entry().get(), + context_->GetPostDominatorAnalysis(function_)->GetDomTree()); + } +}; + +} // namespace + +Pass::Status LoopUnswitchPass::Process() { + bool modified = false; + Module* module = context()->module(); + + // Process each function in the module + for (Function& f : *module) { + modified |= ProcessFunction(&f); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool LoopUnswitchPass::ProcessFunction(Function* f) { + bool modified = false; + std::unordered_set processed_loop; + + LoopDescriptor& loop_descriptor = *context()->GetLoopDescriptor(f); + + bool loop_changed = true; + while (loop_changed) { + loop_changed = false; + for (Loop& loop : make_range( + ++TreeDFIterator(loop_descriptor.GetPlaceholderRootLoop()), + TreeDFIterator())) { + if (processed_loop.count(&loop)) continue; + processed_loop.insert(&loop); + + LoopUnswitch unswitcher(context(), f, &loop, &loop_descriptor); + while (unswitcher.CanUnswitchLoop()) { + if (!loop.IsLCSSA()) { + LoopUtils(context(), &loop).MakeLoopClosedSSA(); + } + modified = true; + loop_changed = true; + unswitcher.PerformUnswitch(); + } + if (loop_changed) break; + } + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_unswitch_pass.h b/third_party/spirv-tools/source/opt/loop_unswitch_pass.h new file mode 100644 index 0000000..3ecdd61 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_unswitch_pass.h @@ -0,0 +1,43 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_UNSWITCH_PASS_H_ +#define SOURCE_OPT_LOOP_UNSWITCH_PASS_H_ + +#include "source/opt/loop_descriptor.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Implements the loop unswitch optimization. +// The loop unswitch hoists invariant "if" statements if the conditions are +// constant within the loop and clones the loop for each branch. +class LoopUnswitchPass : public Pass { + public: + const char* name() const override { return "loop-unswitch"; } + + // Processes the given |module|. Returns Status::Failure if errors occur when + // processing. Returns the corresponding Status::Success if processing is + // succesful to indicate whether changes have been made to the modue. + Pass::Status Process() override; + + private: + bool ProcessFunction(Function* f); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_UNSWITCH_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/loop_utils.cpp b/third_party/spirv-tools/source/opt/loop_utils.cpp new file mode 100644 index 0000000..8c6d355 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_utils.cpp @@ -0,0 +1,694 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include + +#include "source/cfa.h" +#include "source/opt/cfg.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" +#include "source/opt/loop_utils.h" + +namespace spvtools { +namespace opt { + +namespace { +// Return true if |bb| is dominated by at least one block in |exits| +static inline bool DominatesAnExit(BasicBlock* bb, + const std::unordered_set& exits, + const DominatorTree& dom_tree) { + for (BasicBlock* e_bb : exits) + if (dom_tree.Dominates(bb, e_bb)) return true; + return false; +} + +// Utility class to rewrite out-of-loop uses of an in-loop definition in terms +// of phi instructions to achieve a LCSSA form. +// For a given definition, the class user registers phi instructions using that +// definition in all loop exit blocks by which the definition escapes. +// Then, when rewriting a use of the definition, the rewriter walks the +// paths from the use the loop exits. At each step, it will insert a phi +// instruction to merge the incoming value according to exit blocks definition. +class LCSSARewriter { + public: + LCSSARewriter(IRContext* context, const DominatorTree& dom_tree, + const std::unordered_set& exit_bb, + BasicBlock* merge_block) + : context_(context), + cfg_(context_->cfg()), + dom_tree_(dom_tree), + exit_bb_(exit_bb), + merge_block_id_(merge_block ? merge_block->id() : 0) {} + + struct UseRewriter { + explicit UseRewriter(LCSSARewriter* base, const Instruction& def_insn) + : base_(base), def_insn_(def_insn) {} + // Rewrites the use of |def_insn_| by the instruction |user| at the index + // |operand_index| in terms of phi instruction. This recursively builds new + // phi instructions from |user| to the loop exit blocks' phis. The use of + // |def_insn_| in |user| is replaced by the relevant phi instruction at the + // end of the operation. + // It is assumed that |user| does not dominates any of the loop exit basic + // block. This operation does not update the def/use manager, instead it + // records what needs to be updated. The actual update is performed by + // UpdateManagers. + void RewriteUse(BasicBlock* bb, Instruction* user, uint32_t operand_index) { + assert( + (user->opcode() != SpvOpPhi || bb != GetParent(user)) && + "The root basic block must be the incoming edge if |user| is a phi " + "instruction"); + assert((user->opcode() == SpvOpPhi || bb == GetParent(user)) && + "The root basic block must be the instruction parent if |user| is " + "not " + "phi instruction"); + + Instruction* new_def = GetOrBuildIncoming(bb->id()); + + user->SetOperand(operand_index, {new_def->result_id()}); + rewritten_.insert(user); + } + + // In-place update of some managers (avoid full invalidation). + inline void UpdateManagers() { + analysis::DefUseManager* def_use_mgr = base_->context_->get_def_use_mgr(); + // Register all new definitions. + for (Instruction* insn : rewritten_) { + def_use_mgr->AnalyzeInstDef(insn); + } + // Register all new uses. + for (Instruction* insn : rewritten_) { + def_use_mgr->AnalyzeInstUse(insn); + } + } + + private: + // Return the basic block that |instr| belongs to. + BasicBlock* GetParent(Instruction* instr) { + return base_->context_->get_instr_block(instr); + } + + // Builds a phi instruction for the basic block |bb|. The function assumes + // that |defining_blocks| contains the list of basic block that define the + // usable value for each predecessor of |bb|. + inline Instruction* CreatePhiInstruction( + BasicBlock* bb, const std::vector& defining_blocks) { + std::vector incomings; + const std::vector& bb_preds = base_->cfg_->preds(bb->id()); + assert(bb_preds.size() == defining_blocks.size()); + for (size_t i = 0; i < bb_preds.size(); i++) { + incomings.push_back( + GetOrBuildIncoming(defining_blocks[i])->result_id()); + incomings.push_back(bb_preds[i]); + } + InstructionBuilder builder(base_->context_, &*bb->begin(), + IRContext::kAnalysisInstrToBlockMapping); + Instruction* incoming_phi = + builder.AddPhi(def_insn_.type_id(), incomings); + + rewritten_.insert(incoming_phi); + return incoming_phi; + } + + // Builds a phi instruction for the basic block |bb|, all incoming values + // will be |value|. + inline Instruction* CreatePhiInstruction(BasicBlock* bb, + const Instruction& value) { + std::vector incomings; + const std::vector& bb_preds = base_->cfg_->preds(bb->id()); + for (size_t i = 0; i < bb_preds.size(); i++) { + incomings.push_back(value.result_id()); + incomings.push_back(bb_preds[i]); + } + InstructionBuilder builder(base_->context_, &*bb->begin(), + IRContext::kAnalysisInstrToBlockMapping); + Instruction* incoming_phi = + builder.AddPhi(def_insn_.type_id(), incomings); + + rewritten_.insert(incoming_phi); + return incoming_phi; + } + + // Return the new def to use for the basic block |bb_id|. + // If |bb_id| does not have a suitable def to use then we: + // - return the common def used by all predecessors; + // - if there is no common def, then we build a new phi instr at the + // beginning of |bb_id| and return this new instruction. + Instruction* GetOrBuildIncoming(uint32_t bb_id) { + assert(base_->cfg_->block(bb_id) != nullptr && "Unknown basic block"); + + Instruction*& incoming_phi = bb_to_phi_[bb_id]; + if (incoming_phi) { + return incoming_phi; + } + + BasicBlock* bb = &*base_->cfg_->block(bb_id); + // If this is an exit basic block, look if there already is an eligible + // phi instruction. An eligible phi has |def_insn_| as all incoming + // values. + if (base_->exit_bb_.count(bb)) { + // Look if there is an eligible phi in this block. + if (!bb->WhileEachPhiInst([&incoming_phi, this](Instruction* phi) { + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + if (phi->GetSingleWordInOperand(i) != def_insn_.result_id()) + return true; + } + incoming_phi = phi; + rewritten_.insert(incoming_phi); + return false; + })) { + return incoming_phi; + } + incoming_phi = CreatePhiInstruction(bb, def_insn_); + return incoming_phi; + } + + // Get the block that defines the value to use for each predecessor. + // If the vector has 1 value, then it means that this block does not need + // to build a phi instruction unless |bb_id| is the loop merge block. + const std::vector& defining_blocks = + base_->GetDefiningBlocks(bb_id); + + // Special case for structured loops: merge block might be different from + // the exit block set. To maintain structured properties it will ease + // transformations if the merge block also holds a phi instruction like + // the exit ones. + if (defining_blocks.size() > 1 || bb_id == base_->merge_block_id_) { + if (defining_blocks.size() > 1) { + incoming_phi = CreatePhiInstruction(bb, defining_blocks); + } else { + assert(bb_id == base_->merge_block_id_); + incoming_phi = + CreatePhiInstruction(bb, *GetOrBuildIncoming(defining_blocks[0])); + } + } else { + incoming_phi = GetOrBuildIncoming(defining_blocks[0]); + } + + return incoming_phi; + } + + LCSSARewriter* base_; + const Instruction& def_insn_; + std::unordered_map bb_to_phi_; + std::unordered_set rewritten_; + }; + + private: + // Return the new def to use for the basic block |bb_id|. + // If |bb_id| does not have a suitable def to use then we: + // - return the common def used by all predecessors; + // - if there is no common def, then we build a new phi instr at the + // beginning of |bb_id| and return this new instruction. + const std::vector& GetDefiningBlocks(uint32_t bb_id) { + assert(cfg_->block(bb_id) != nullptr && "Unknown basic block"); + std::vector& defining_blocks = bb_to_defining_blocks_[bb_id]; + + if (defining_blocks.size()) return defining_blocks; + + // Check if one of the loop exit basic block dominates |bb_id|. + for (const BasicBlock* e_bb : exit_bb_) { + if (dom_tree_.Dominates(e_bb->id(), bb_id)) { + defining_blocks.push_back(e_bb->id()); + return defining_blocks; + } + } + + // Process parents, they will returns their suitable blocks. + // If they are all the same, this means this basic block is dominated by a + // common block, so we won't need to build a phi instruction. + for (uint32_t pred_id : cfg_->preds(bb_id)) { + const std::vector& pred_blocks = GetDefiningBlocks(pred_id); + if (pred_blocks.size() == 1) + defining_blocks.push_back(pred_blocks[0]); + else + defining_blocks.push_back(pred_id); + } + assert(defining_blocks.size()); + if (std::all_of(defining_blocks.begin(), defining_blocks.end(), + [&defining_blocks](uint32_t id) { + return id == defining_blocks[0]; + })) { + // No need for a phi. + defining_blocks.resize(1); + } + + return defining_blocks; + } + + IRContext* context_; + CFG* cfg_; + const DominatorTree& dom_tree_; + const std::unordered_set& exit_bb_; + uint32_t merge_block_id_; + // This map represent the set of known paths. For each key, the vector + // represent the set of blocks holding the definition to be used to build the + // phi instruction. + // If the vector has 0 value, then the path is unknown yet, and must be built. + // If the vector has 1 value, then the value defined by that basic block + // should be used. + // If the vector has more than 1 value, then a phi node must be created, the + // basic block ordering is the same as the predecessor ordering. + std::unordered_map> bb_to_defining_blocks_; +}; + +// Make the set |blocks| closed SSA. The set is closed SSA if all the uses +// outside the set are phi instructions in exiting basic block set (hold by +// |lcssa_rewriter|). +inline void MakeSetClosedSSA(IRContext* context, Function* function, + const std::unordered_set& blocks, + const std::unordered_set& exit_bb, + LCSSARewriter* lcssa_rewriter) { + CFG& cfg = *context->cfg(); + DominatorTree& dom_tree = + context->GetDominatorAnalysis(function)->GetDomTree(); + analysis::DefUseManager* def_use_manager = context->get_def_use_mgr(); + + for (uint32_t bb_id : blocks) { + BasicBlock* bb = cfg.block(bb_id); + // If bb does not dominate an exit block, then it cannot have escaping defs. + if (!DominatesAnExit(bb, exit_bb, dom_tree)) continue; + for (Instruction& inst : *bb) { + LCSSARewriter::UseRewriter rewriter(lcssa_rewriter, inst); + def_use_manager->ForEachUse( + &inst, [&blocks, &rewriter, &exit_bb, context]( + Instruction* use, uint32_t operand_index) { + BasicBlock* use_parent = context->get_instr_block(use); + assert(use_parent); + if (blocks.count(use_parent->id())) return; + + if (use->opcode() == SpvOpPhi) { + // If the use is a Phi instruction and the incoming block is + // coming from the loop, then that's consistent with LCSSA form. + if (exit_bb.count(use_parent)) { + return; + } else { + // That's not an exit block, but the user is a phi instruction. + // Consider the incoming branch only. + use_parent = context->get_instr_block( + use->GetSingleWordOperand(operand_index + 1)); + } + } + // Rewrite the use. Note that this call does not invalidate the + // def/use manager. So this operation is safe. + rewriter.RewriteUse(use_parent, use, operand_index); + }); + rewriter.UpdateManagers(); + } + } +} + +} // namespace + +void LoopUtils::CreateLoopDedicatedExits() { + Function* function = loop_->GetHeaderBlock()->GetParent(); + LoopDescriptor& loop_desc = *context_->GetLoopDescriptor(function); + CFG& cfg = *context_->cfg(); + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + const IRContext::Analysis PreservedAnalyses = + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + + // Gathers the set of basic block that are not in this loop and have at least + // one predecessor in the loop and one not in the loop. + std::unordered_set exit_bb_set; + loop_->GetExitBlocks(&exit_bb_set); + + std::unordered_set new_loop_exits; + bool made_change = false; + // For each block, we create a new one that gathers all branches from + // the loop and fall into the block. + for (uint32_t non_dedicate_id : exit_bb_set) { + BasicBlock* non_dedicate = cfg.block(non_dedicate_id); + const std::vector& bb_pred = cfg.preds(non_dedicate_id); + // Ignore the block if all the predecessors are in the loop. + if (std::all_of(bb_pred.begin(), bb_pred.end(), + [this](uint32_t id) { return loop_->IsInsideLoop(id); })) { + new_loop_exits.insert(non_dedicate); + continue; + } + + made_change = true; + Function::iterator insert_pt = function->begin(); + for (; insert_pt != function->end() && &*insert_pt != non_dedicate; + ++insert_pt) { + } + assert(insert_pt != function->end() && "Basic Block not found"); + + // Create the dedicate exit basic block. + // TODO(1841): Handle id overflow. + BasicBlock& exit = *insert_pt.InsertBefore(std::unique_ptr( + new BasicBlock(std::unique_ptr(new Instruction( + context_, SpvOpLabel, 0, context_->TakeNextId(), {}))))); + exit.SetParent(function); + + // Redirect in loop predecessors to |exit| block. + for (uint32_t exit_pred_id : bb_pred) { + if (loop_->IsInsideLoop(exit_pred_id)) { + BasicBlock* pred_block = cfg.block(exit_pred_id); + pred_block->ForEachSuccessorLabel([non_dedicate, &exit](uint32_t* id) { + if (*id == non_dedicate->id()) *id = exit.id(); + }); + // Update the CFG. + // |non_dedicate|'s predecessor list will be updated at the end of the + // loop. + cfg.RegisterBlock(pred_block); + } + } + + // Register the label to the def/use manager, requires for the phi patching. + def_use_mgr->AnalyzeInstDefUse(exit.GetLabelInst()); + context_->set_instr_block(exit.GetLabelInst(), &exit); + + InstructionBuilder builder(context_, &exit, PreservedAnalyses); + // Now jump from our dedicate basic block to the old exit. + // We also reset the insert point so all instructions are inserted before + // the branch. + builder.SetInsertPoint(builder.AddBranch(non_dedicate->id())); + non_dedicate->ForEachPhiInst( + [&builder, &exit, def_use_mgr, this](Instruction* phi) { + // New phi operands for this instruction. + std::vector new_phi_op; + // Phi operands for the dedicated exit block. + std::vector exit_phi_op; + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + uint32_t def_id = phi->GetSingleWordInOperand(i); + uint32_t incoming_id = phi->GetSingleWordInOperand(i + 1); + if (loop_->IsInsideLoop(incoming_id)) { + exit_phi_op.push_back(def_id); + exit_phi_op.push_back(incoming_id); + } else { + new_phi_op.push_back(def_id); + new_phi_op.push_back(incoming_id); + } + } + + // Build the new phi instruction dedicated exit block. + Instruction* exit_phi = builder.AddPhi(phi->type_id(), exit_phi_op); + // Build the new incoming branch. + new_phi_op.push_back(exit_phi->result_id()); + new_phi_op.push_back(exit.id()); + // Rewrite operands. + uint32_t idx = 0; + for (; idx < new_phi_op.size(); idx++) + phi->SetInOperand(idx, {new_phi_op[idx]}); + // Remove extra operands, from last to first (more efficient). + for (uint32_t j = phi->NumInOperands() - 1; j >= idx; j--) + phi->RemoveInOperand(j); + // Update the def/use manager for this |phi|. + def_use_mgr->AnalyzeInstUse(phi); + }); + // Update the CFG. + cfg.RegisterBlock(&exit); + cfg.RemoveNonExistingEdges(non_dedicate->id()); + new_loop_exits.insert(&exit); + // If non_dedicate is in a loop, add the new dedicated exit in that loop. + if (Loop* parent_loop = loop_desc[non_dedicate]) + parent_loop->AddBasicBlock(&exit); + } + + if (new_loop_exits.size() == 1) { + loop_->SetMergeBlock(*new_loop_exits.begin()); + } + + if (made_change) { + context_->InvalidateAnalysesExceptFor( + PreservedAnalyses | IRContext::kAnalysisCFG | + IRContext::Analysis::kAnalysisLoopAnalysis); + } +} + +void LoopUtils::MakeLoopClosedSSA() { + CreateLoopDedicatedExits(); + + Function* function = loop_->GetHeaderBlock()->GetParent(); + CFG& cfg = *context_->cfg(); + DominatorTree& dom_tree = + context_->GetDominatorAnalysis(function)->GetDomTree(); + + std::unordered_set exit_bb; + { + std::unordered_set exit_bb_id; + loop_->GetExitBlocks(&exit_bb_id); + for (uint32_t bb_id : exit_bb_id) { + exit_bb.insert(cfg.block(bb_id)); + } + } + + LCSSARewriter lcssa_rewriter(context_, dom_tree, exit_bb, + loop_->GetMergeBlock()); + MakeSetClosedSSA(context_, function, loop_->GetBlocks(), exit_bb, + &lcssa_rewriter); + + // Make sure all defs post-dominated by the merge block have their last use no + // further than the merge block. + if (loop_->GetMergeBlock()) { + std::unordered_set merging_bb_id; + loop_->GetMergingBlocks(&merging_bb_id); + merging_bb_id.erase(loop_->GetMergeBlock()->id()); + // Reset the exit set, now only the merge block is the exit. + exit_bb.clear(); + exit_bb.insert(loop_->GetMergeBlock()); + // LCSSARewriter is reusable here only because it forces the creation of a + // phi instruction in the merge block. + MakeSetClosedSSA(context_, function, merging_bb_id, exit_bb, + &lcssa_rewriter); + } + + context_->InvalidateAnalysesExceptFor( + IRContext::Analysis::kAnalysisCFG | + IRContext::Analysis::kAnalysisDominatorAnalysis | + IRContext::Analysis::kAnalysisLoopAnalysis); +} + +Loop* LoopUtils::CloneLoop(LoopCloningResult* cloning_result) const { + // Compute the structured order of the loop basic blocks and store it in the + // vector ordered_loop_blocks. + std::vector ordered_loop_blocks; + loop_->ComputeLoopStructuredOrder(&ordered_loop_blocks); + + // Clone the loop. + return CloneLoop(cloning_result, ordered_loop_blocks); +} + +Loop* LoopUtils::CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result) { + // Clone the loop. + Loop* new_loop = CloneLoop(cloning_result); + + // Create a new exit block/label for the new loop. + // TODO(1841): Handle id overflow. + std::unique_ptr new_label{new Instruction( + context_, SpvOp::SpvOpLabel, 0, context_->TakeNextId(), {})}; + std::unique_ptr new_exit_bb{new BasicBlock(std::move(new_label))}; + new_exit_bb->SetParent(loop_->GetMergeBlock()->GetParent()); + + // Create an unconditional branch to the header block. + InstructionBuilder builder{context_, new_exit_bb.get()}; + builder.AddBranch(loop_->GetHeaderBlock()->id()); + + // Save the ids of the new and old merge block. + const uint32_t old_merge_block = loop_->GetMergeBlock()->id(); + const uint32_t new_merge_block = new_exit_bb->id(); + + // Replace the uses of the old merge block in the new loop with the new merge + // block. + for (std::unique_ptr& basic_block : cloning_result->cloned_bb_) { + for (Instruction& inst : *basic_block) { + // For each operand in each instruction check if it is using the old merge + // block and change it to be the new merge block. + auto replace_merge_use = [old_merge_block, + new_merge_block](uint32_t* id) { + if (*id == old_merge_block) *id = new_merge_block; + }; + inst.ForEachInOperand(replace_merge_use); + } + } + + const uint32_t old_header = loop_->GetHeaderBlock()->id(); + const uint32_t new_header = new_loop->GetHeaderBlock()->id(); + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + + def_use->ForEachUse(old_header, + [new_header, this](Instruction* inst, uint32_t operand) { + if (!this->loop_->IsInsideLoop(inst)) + inst->SetOperand(operand, {new_header}); + }); + + // TODO(1841): Handle failure to create pre-header. + def_use->ForEachUse( + loop_->GetOrCreatePreHeaderBlock()->id(), + [new_merge_block, this](Instruction* inst, uint32_t operand) { + if (this->loop_->IsInsideLoop(inst)) + inst->SetOperand(operand, {new_merge_block}); + + }); + new_loop->SetMergeBlock(new_exit_bb.get()); + + new_loop->SetPreHeaderBlock(loop_->GetPreHeaderBlock()); + + // Add the new block into the cloned instructions. + cloning_result->cloned_bb_.push_back(std::move(new_exit_bb)); + + return new_loop; +} + +Loop* LoopUtils::CloneLoop( + LoopCloningResult* cloning_result, + const std::vector& ordered_loop_blocks) const { + analysis::DefUseManager* def_use_mgr = context_->get_def_use_mgr(); + + std::unique_ptr new_loop = MakeUnique(context_); + + CFG& cfg = *context_->cfg(); + + // Clone and place blocks in a SPIR-V compliant order (dominators first). + for (BasicBlock* old_bb : ordered_loop_blocks) { + // For each basic block in the loop, we clone it and register the mapping + // between old and new ids. + BasicBlock* new_bb = old_bb->Clone(context_); + new_bb->SetParent(&function_); + // TODO(1841): Handle id overflow. + new_bb->GetLabelInst()->SetResultId(context_->TakeNextId()); + def_use_mgr->AnalyzeInstDef(new_bb->GetLabelInst()); + context_->set_instr_block(new_bb->GetLabelInst(), new_bb); + cloning_result->cloned_bb_.emplace_back(new_bb); + + cloning_result->old_to_new_bb_[old_bb->id()] = new_bb; + cloning_result->new_to_old_bb_[new_bb->id()] = old_bb; + cloning_result->value_map_[old_bb->id()] = new_bb->id(); + + if (loop_->IsInsideLoop(old_bb)) new_loop->AddBasicBlock(new_bb); + + for (auto new_inst = new_bb->begin(), old_inst = old_bb->begin(); + new_inst != new_bb->end(); ++new_inst, ++old_inst) { + cloning_result->ptr_map_[&*new_inst] = &*old_inst; + if (new_inst->HasResultId()) { + // TODO(1841): Handle id overflow. + new_inst->SetResultId(context_->TakeNextId()); + cloning_result->value_map_[old_inst->result_id()] = + new_inst->result_id(); + + // Only look at the defs for now, uses are not updated yet. + def_use_mgr->AnalyzeInstDef(&*new_inst); + } + } + } + + // All instructions (including all labels) have been cloned, + // remap instruction operands id with the new ones. + for (std::unique_ptr& bb_ref : cloning_result->cloned_bb_) { + BasicBlock* bb = bb_ref.get(); + + for (Instruction& insn : *bb) { + insn.ForEachInId([cloning_result](uint32_t* old_id) { + // If the operand is defined in the loop, remap the id. + auto id_it = cloning_result->value_map_.find(*old_id); + if (id_it != cloning_result->value_map_.end()) { + *old_id = id_it->second; + } + }); + // Only look at what the instruction uses. All defs are register, so all + // should be fine now. + def_use_mgr->AnalyzeInstUse(&insn); + context_->set_instr_block(&insn, bb); + } + cfg.RegisterBlock(bb); + } + + PopulateLoopNest(new_loop.get(), *cloning_result); + + return new_loop.release(); +} + +void LoopUtils::PopulateLoopNest( + Loop* new_loop, const LoopCloningResult& cloning_result) const { + std::unordered_map loop_mapping; + loop_mapping[loop_] = new_loop; + + if (loop_->HasParent()) loop_->GetParent()->AddNestedLoop(new_loop); + PopulateLoopDesc(new_loop, loop_, cloning_result); + + for (Loop& sub_loop : + make_range(++TreeDFIterator(loop_), TreeDFIterator())) { + Loop* cloned = new Loop(context_); + if (Loop* parent = loop_mapping[sub_loop.GetParent()]) + parent->AddNestedLoop(cloned); + loop_mapping[&sub_loop] = cloned; + PopulateLoopDesc(cloned, &sub_loop, cloning_result); + } + + loop_desc_->AddLoopNest(std::unique_ptr(new_loop)); +} + +// Populates |new_loop| descriptor according to |old_loop|'s one. +void LoopUtils::PopulateLoopDesc( + Loop* new_loop, Loop* old_loop, + const LoopCloningResult& cloning_result) const { + for (uint32_t bb_id : old_loop->GetBlocks()) { + BasicBlock* bb = cloning_result.old_to_new_bb_.at(bb_id); + new_loop->AddBasicBlock(bb); + } + new_loop->SetHeaderBlock( + cloning_result.old_to_new_bb_.at(old_loop->GetHeaderBlock()->id())); + if (old_loop->GetLatchBlock()) + new_loop->SetLatchBlock( + cloning_result.old_to_new_bb_.at(old_loop->GetLatchBlock()->id())); + if (old_loop->GetContinueBlock()) + new_loop->SetContinueBlock( + cloning_result.old_to_new_bb_.at(old_loop->GetContinueBlock()->id())); + if (old_loop->GetMergeBlock()) { + auto it = + cloning_result.old_to_new_bb_.find(old_loop->GetMergeBlock()->id()); + BasicBlock* bb = it != cloning_result.old_to_new_bb_.end() + ? it->second + : old_loop->GetMergeBlock(); + new_loop->SetMergeBlock(bb); + } + if (old_loop->GetPreHeaderBlock()) { + auto it = + cloning_result.old_to_new_bb_.find(old_loop->GetPreHeaderBlock()->id()); + if (it != cloning_result.old_to_new_bb_.end()) { + new_loop->SetPreHeaderBlock(it->second); + } + } +} + +// Class to gather some metrics about a region of interest. +void CodeMetrics::Analyze(const Loop& loop) { + CFG& cfg = *loop.GetContext()->cfg(); + + roi_size_ = 0; + block_sizes_.clear(); + + for (uint32_t id : loop.GetBlocks()) { + const BasicBlock* bb = cfg.block(id); + size_t bb_size = 0; + bb->ForEachInst([&bb_size](const Instruction* insn) { + if (insn->opcode() == SpvOpLabel) return; + if (insn->IsNop()) return; + if (insn->opcode() == SpvOpPhi) return; + bb_size++; + }); + block_sizes_[bb->id()] = bb_size; + roi_size_ += bb_size; + } +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/loop_utils.h b/third_party/spirv-tools/source/opt/loop_utils.h new file mode 100644 index 0000000..a4e6190 --- /dev/null +++ b/third_party/spirv-tools/source/opt/loop_utils.h @@ -0,0 +1,182 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_LOOP_UTILS_H_ +#define SOURCE_OPT_LOOP_UTILS_H_ + +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/loop_descriptor.h" + +namespace spvtools { + +namespace opt { + +// Class to gather some metrics about a Region Of Interest (ROI). +// So far it counts the number of instructions in a ROI (excluding debug +// and label instructions) per basic block and in total. +struct CodeMetrics { + void Analyze(const Loop& loop); + + // The number of instructions per basic block in the ROI. + std::unordered_map block_sizes_; + + // Number of instruction in the ROI. + size_t roi_size_; +}; + +// LoopUtils is used to encapsulte loop optimizations and from the passes which +// use them. Any pass which needs a loop optimization should do it through this +// or through a pass which is using this. +class LoopUtils { + public: + // Holds a auxiliary results of the loop cloning procedure. + struct LoopCloningResult { + using ValueMapTy = std::unordered_map; + using BlockMapTy = std::unordered_map; + using PtrMap = std::unordered_map; + + PtrMap ptr_map_; + + // Mapping between the original loop ids and the new one. + ValueMapTy value_map_; + // Mapping between original loop blocks to the cloned one. + BlockMapTy old_to_new_bb_; + // Mapping between the cloned loop blocks to original one. + BlockMapTy new_to_old_bb_; + // List of cloned basic block. + std::vector> cloned_bb_; + }; + + LoopUtils(IRContext* context, Loop* loop) + : context_(context), + loop_desc_( + context->GetLoopDescriptor(loop->GetHeaderBlock()->GetParent())), + loop_(loop), + function_(*loop_->GetHeaderBlock()->GetParent()) {} + + // The converts the current loop to loop closed SSA form. + // In the loop closed SSA, all loop exiting values go through a dedicated Phi + // instruction. For instance: + // + // for (...) { + // A1 = ... + // if (...) + // A2 = ... + // A = phi A1, A2 + // } + // ... = op A ... + // + // Becomes + // + // for (...) { + // A1 = ... + // if (...) + // A2 = ... + // A = phi A1, A2 + // } + // C = phi A + // ... = op C ... + // + // This makes some loop transformations (such as loop unswitch) simpler + // (removes the needs to take care of exiting variables). + void MakeLoopClosedSSA(); + + // Create dedicate exit basic block. This ensure all exit basic blocks has the + // loop as sole predecessors. + // By construction, structured control flow already has a dedicated exit + // block. + // Preserves: CFG, def/use and instruction to block mapping. + void CreateLoopDedicatedExits(); + + // Clone |loop_| and remap its instructions. Newly created blocks + // will be added to the |cloning_result.cloned_bb_| list, correctly ordered to + // be inserted into a function. + // It is assumed that |ordered_loop_blocks| is compatible with the result of + // |Loop::ComputeLoopStructuredOrder|. If the preheader and merge block are in + // the list they will also be cloned. If not, the resulting loop will share + // them with the original loop. + // The function preserves the def/use, cfg and instr to block analyses. + // The cloned loop nest will be added to the loop descriptor and will have + // ownership. + Loop* CloneLoop(LoopCloningResult* cloning_result, + const std::vector& ordered_loop_blocks) const; + // Clone |loop_| and remap its instructions, as above. Overload to compute + // loop block ordering within method rather than taking in as parameter. + Loop* CloneLoop(LoopCloningResult* cloning_result) const; + + // Clone the |loop_| and make the new loop branch to the second loop on exit. + Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result); + + // Perfom a partial unroll of |loop| by given |factor|. This will copy the + // body of the loop |factor| times. So a |factor| of one would give a new loop + // with the original body plus one unrolled copy body. + bool PartiallyUnroll(size_t factor); + + // Fully unroll |loop|. + bool FullyUnroll(); + + // This function validates that |loop| meets the assumptions made by the + // implementation of the loop unroller. As the implementation accommodates + // more types of loops this function can reduce its checks. + // + // The conditions checked to ensure the loop can be unrolled are as follows: + // 1. That the loop is in structured order. + // 2. That the continue block is a branch to the header. + // 3. That the only phi used in the loop is the induction variable. + // TODO(stephen@codeplay.com): This is a temporary mesure, after the loop is + // converted into LCSAA form and has a single entry and exit we can rewrite + // the other phis. + // 4. That this is an inner most loop, or that loops contained within this + // loop have already been fully unrolled. + // 5. That each instruction in the loop is only used within the loop. + // (Related to the above phi condition). + bool CanPerformUnroll(); + + // Maintains the loop descriptor object after the unroll functions have been + // called, otherwise the analysis should be invalidated. + void Finalize(); + + // Returns the context associate to |loop_|. + IRContext* GetContext() { return context_; } + // Returns the loop descriptor owning |loop_|. + LoopDescriptor* GetLoopDescriptor() { return loop_desc_; } + // Returns the loop on which the object operates on. + Loop* GetLoop() const { return loop_; } + // Returns the function that |loop_| belong to. + Function* GetFunction() const { return &function_; } + + private: + IRContext* context_; + LoopDescriptor* loop_desc_; + Loop* loop_; + Function& function_; + + // Populates the loop nest of |new_loop| according to |loop_| nest. + void PopulateLoopNest(Loop* new_loop, + const LoopCloningResult& cloning_result) const; + + // Populates |new_loop| descriptor according to |old_loop|'s one. + void PopulateLoopDesc(Loop* new_loop, Loop* old_loop, + const LoopCloningResult& cloning_result) const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_LOOP_UTILS_H_ diff --git a/third_party/spirv-tools/source/opt/mem_pass.cpp b/third_party/spirv-tools/source/opt/mem_pass.cpp new file mode 100644 index 0000000..5738798 --- /dev/null +++ b/third_party/spirv-tools/source/opt/mem_pass.cpp @@ -0,0 +1,510 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/mem_pass.h" + +#include +#include +#include + +#include "OpenCLDebugInfo100.h" +#include "source/cfa.h" +#include "source/opt/basic_block.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +namespace { + +const uint32_t kCopyObjectOperandInIdx = 0; +const uint32_t kTypePointerStorageClassInIdx = 0; +const uint32_t kTypePointerTypeIdInIdx = 1; + +} // namespace + +bool MemPass::IsBaseTargetType(const Instruction* typeInst) const { + switch (typeInst->opcode()) { + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeBool: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeImage: + case SpvOpTypeSampler: + case SpvOpTypeSampledImage: + case SpvOpTypePointer: + return true; + default: + break; + } + return false; +} + +bool MemPass::IsTargetType(const Instruction* typeInst) const { + if (IsBaseTargetType(typeInst)) return true; + if (typeInst->opcode() == SpvOpTypeArray) { + if (!IsTargetType( + get_def_use_mgr()->GetDef(typeInst->GetSingleWordOperand(1)))) { + return false; + } + return true; + } + if (typeInst->opcode() != SpvOpTypeStruct) return false; + // All struct members must be math type + return typeInst->WhileEachInId([this](const uint32_t* tid) { + Instruction* compTypeInst = get_def_use_mgr()->GetDef(*tid); + if (!IsTargetType(compTypeInst)) return false; + return true; + }); +} + +bool MemPass::IsNonPtrAccessChain(const SpvOp opcode) const { + return opcode == SpvOpAccessChain || opcode == SpvOpInBoundsAccessChain; +} + +bool MemPass::IsPtr(uint32_t ptrId) { + uint32_t varId = ptrId; + Instruction* ptrInst = get_def_use_mgr()->GetDef(varId); + while (ptrInst->opcode() == SpvOpCopyObject) { + varId = ptrInst->GetSingleWordInOperand(kCopyObjectOperandInIdx); + ptrInst = get_def_use_mgr()->GetDef(varId); + } + const SpvOp op = ptrInst->opcode(); + if (op == SpvOpVariable || IsNonPtrAccessChain(op)) return true; + if (op != SpvOpFunctionParameter) return false; + const uint32_t varTypeId = ptrInst->type_id(); + const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); + return varTypeInst->opcode() == SpvOpTypePointer; +} + +Instruction* MemPass::GetPtr(uint32_t ptrId, uint32_t* varId) { + *varId = ptrId; + Instruction* ptrInst = get_def_use_mgr()->GetDef(*varId); + Instruction* varInst; + + if (ptrInst->opcode() == SpvOpConstantNull) { + *varId = 0; + return ptrInst; + } + + if (ptrInst->opcode() != SpvOpVariable && + ptrInst->opcode() != SpvOpFunctionParameter) { + varInst = ptrInst->GetBaseAddress(); + } else { + varInst = ptrInst; + } + if (varInst->opcode() == SpvOpVariable) { + *varId = varInst->result_id(); + } else { + *varId = 0; + } + + while (ptrInst->opcode() == SpvOpCopyObject) { + uint32_t temp = ptrInst->GetSingleWordInOperand(0); + ptrInst = get_def_use_mgr()->GetDef(temp); + } + + return ptrInst; +} + +Instruction* MemPass::GetPtr(Instruction* ip, uint32_t* varId) { + assert(ip->opcode() == SpvOpStore || ip->opcode() == SpvOpLoad || + ip->opcode() == SpvOpImageTexelPointer || ip->IsAtomicWithLoad()); + + // All of these opcode place the pointer in position 0. + const uint32_t ptrId = ip->GetSingleWordInOperand(0); + return GetPtr(ptrId, varId); +} + +bool MemPass::HasOnlyNamesAndDecorates(uint32_t id) const { + return get_def_use_mgr()->WhileEachUser(id, [this](Instruction* user) { + SpvOp op = user->opcode(); + if (op != SpvOpName && !IsNonTypeDecorate(op)) { + return false; + } + return true; + }); +} + +void MemPass::KillAllInsts(BasicBlock* bp, bool killLabel) { + bp->KillAllInsts(killLabel); +} + +bool MemPass::HasLoads(uint32_t varId) const { + return !get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) { + SpvOp op = user->opcode(); + // TODO(): The following is slightly conservative. Could be + // better handling of non-store/name. + if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) { + if (HasLoads(user->result_id())) { + return false; + } + } else if (op != SpvOpStore && op != SpvOpName && !IsNonTypeDecorate(op)) { + return false; + } + return true; + }); +} + +bool MemPass::IsLiveVar(uint32_t varId) const { + const Instruction* varInst = get_def_use_mgr()->GetDef(varId); + // assume live if not a variable eg. function parameter + if (varInst->opcode() != SpvOpVariable) return true; + // non-function scope vars are live + const uint32_t varTypeId = varInst->type_id(); + const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); + if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) != + SpvStorageClassFunction) + return true; + // test if variable is loaded from + return HasLoads(varId); +} + +void MemPass::AddStores(uint32_t ptr_id, std::queue* insts) { + get_def_use_mgr()->ForEachUser(ptr_id, [this, insts](Instruction* user) { + SpvOp op = user->opcode(); + if (IsNonPtrAccessChain(op)) { + AddStores(user->result_id(), insts); + } else if (op == SpvOpStore) { + insts->push(user); + } + }); +} + +void MemPass::DCEInst(Instruction* inst, + const std::function& call_back) { + std::queue deadInsts; + deadInsts.push(inst); + while (!deadInsts.empty()) { + Instruction* di = deadInsts.front(); + // Don't delete labels + if (di->opcode() == SpvOpLabel) { + deadInsts.pop(); + continue; + } + // Remember operands + std::set ids; + di->ForEachInId([&ids](uint32_t* iid) { ids.insert(*iid); }); + uint32_t varId = 0; + // Remember variable if dead load + if (di->opcode() == SpvOpLoad) (void)GetPtr(di, &varId); + if (call_back) { + call_back(di); + } + context()->KillInst(di); + // For all operands with no remaining uses, add their instruction + // to the dead instruction queue. + for (auto id : ids) + if (HasOnlyNamesAndDecorates(id)) { + Instruction* odi = get_def_use_mgr()->GetDef(id); + if (context()->IsCombinatorInstruction(odi)) deadInsts.push(odi); + } + // if a load was deleted and it was the variable's + // last load, add all its stores to dead queue + if (varId != 0 && !IsLiveVar(varId)) AddStores(varId, &deadInsts); + deadInsts.pop(); + } +} + +MemPass::MemPass() {} + +bool MemPass::HasOnlySupportedRefs(uint32_t varId) { + return get_def_use_mgr()->WhileEachUser(varId, [this](Instruction* user) { + auto dbg_op = user->GetOpenCL100DebugOpcode(); + if (dbg_op == OpenCLDebugInfo100DebugDeclare || + dbg_op == OpenCLDebugInfo100DebugValue) { + return true; + } + SpvOp op = user->opcode(); + if (op != SpvOpStore && op != SpvOpLoad && op != SpvOpName && + !IsNonTypeDecorate(op)) { + return false; + } + return true; + }); +} + +uint32_t MemPass::Type2Undef(uint32_t type_id) { + const auto uitr = type2undefs_.find(type_id); + if (uitr != type2undefs_.end()) return uitr->second; + const uint32_t undefId = TakeNextId(); + if (undefId == 0) { + return 0; + } + + std::unique_ptr undef_inst( + new Instruction(context(), SpvOpUndef, type_id, undefId, {})); + get_def_use_mgr()->AnalyzeInstDefUse(&*undef_inst); + get_module()->AddGlobalValue(std::move(undef_inst)); + type2undefs_[type_id] = undefId; + return undefId; +} + +bool MemPass::IsTargetVar(uint32_t varId) { + if (varId == 0) { + return false; + } + + if (seen_non_target_vars_.find(varId) != seen_non_target_vars_.end()) + return false; + if (seen_target_vars_.find(varId) != seen_target_vars_.end()) return true; + const Instruction* varInst = get_def_use_mgr()->GetDef(varId); + if (varInst->opcode() != SpvOpVariable) return false; + const uint32_t varTypeId = varInst->type_id(); + const Instruction* varTypeInst = get_def_use_mgr()->GetDef(varTypeId); + if (varTypeInst->GetSingleWordInOperand(kTypePointerStorageClassInIdx) != + SpvStorageClassFunction) { + seen_non_target_vars_.insert(varId); + return false; + } + const uint32_t varPteTypeId = + varTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx); + Instruction* varPteTypeInst = get_def_use_mgr()->GetDef(varPteTypeId); + if (!IsTargetType(varPteTypeInst)) { + seen_non_target_vars_.insert(varId); + return false; + } + seen_target_vars_.insert(varId); + return true; +} + +// Remove all |phi| operands coming from unreachable blocks (i.e., blocks not in +// |reachable_blocks|). There are two types of removal that this function can +// perform: +// +// 1- Any operand that comes directly from an unreachable block is completely +// removed. Since the block is unreachable, the edge between the unreachable +// block and the block holding |phi| has been removed. +// +// 2- Any operand that comes via a live block and was defined at an unreachable +// block gets its value replaced with an OpUndef value. Since the argument +// was generated in an unreachable block, it no longer exists, so it cannot +// be referenced. However, since the value does not reach |phi| directly +// from the unreachable block, the operand cannot be removed from |phi|. +// Therefore, we replace the argument value with OpUndef. +// +// For example, in the switch() below, assume that we want to remove the +// argument with value %11 coming from block %41. +// +// [ ... ] +// %41 = OpLabel <--- Unreachable block +// %11 = OpLoad %int %y +// [ ... ] +// OpSelectionMerge %16 None +// OpSwitch %12 %16 10 %13 13 %14 18 %15 +// %13 = OpLabel +// OpBranch %16 +// %14 = OpLabel +// OpStore %outparm %int_14 +// OpBranch %16 +// %15 = OpLabel +// OpStore %outparm %int_15 +// OpBranch %16 +// %16 = OpLabel +// %30 = OpPhi %int %11 %41 %int_42 %13 %11 %14 %11 %15 +// +// Since %41 is now an unreachable block, the first operand of |phi| needs to +// be removed completely. But the operands (%11 %14) and (%11 %15) cannot be +// removed because %14 and %15 are reachable blocks. Since %11 no longer exist, +// in those arguments, we replace all references to %11 with an OpUndef value. +// This results in |phi| looking like: +// +// %50 = OpUndef %int +// [ ... ] +// %30 = OpPhi %int %int_42 %13 %50 %14 %50 %15 +void MemPass::RemovePhiOperands( + Instruction* phi, const std::unordered_set& reachable_blocks) { + std::vector keep_operands; + uint32_t type_id = 0; + // The id of an undefined value we've generated. + uint32_t undef_id = 0; + + // Traverse all the operands in |phi|. Build the new operand vector by adding + // all the original operands from |phi| except the unwanted ones. + for (uint32_t i = 0; i < phi->NumOperands();) { + if (i < 2) { + // The first two arguments are always preserved. + keep_operands.push_back(phi->GetOperand(i)); + ++i; + continue; + } + + // The remaining Phi arguments come in pairs. Index 'i' contains the + // variable id, index 'i + 1' is the originating block id. + assert(i % 2 == 0 && i < phi->NumOperands() - 1 && + "malformed Phi arguments"); + + BasicBlock* in_block = cfg()->block(phi->GetSingleWordOperand(i + 1)); + if (reachable_blocks.find(in_block) == reachable_blocks.end()) { + // If the incoming block is unreachable, remove both operands as this + // means that the |phi| has lost an incoming edge. + i += 2; + continue; + } + + // In all other cases, the operand must be kept but may need to be changed. + uint32_t arg_id = phi->GetSingleWordOperand(i); + Instruction* arg_def_instr = get_def_use_mgr()->GetDef(arg_id); + BasicBlock* def_block = context()->get_instr_block(arg_def_instr); + if (def_block && + reachable_blocks.find(def_block) == reachable_blocks.end()) { + // If the current |phi| argument was defined in an unreachable block, it + // means that this |phi| argument is no longer defined. Replace it with + // |undef_id|. + if (!undef_id) { + type_id = arg_def_instr->type_id(); + undef_id = Type2Undef(type_id); + } + keep_operands.push_back( + Operand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, {undef_id})); + } else { + // Otherwise, the argument comes from a reachable block or from no block + // at all (meaning that it was defined in the global section of the + // program). In both cases, keep the argument intact. + keep_operands.push_back(phi->GetOperand(i)); + } + + keep_operands.push_back(phi->GetOperand(i + 1)); + + i += 2; + } + + context()->ForgetUses(phi); + phi->ReplaceOperands(keep_operands); + context()->AnalyzeUses(phi); +} + +void MemPass::RemoveBlock(Function::iterator* bi) { + auto& rm_block = **bi; + + // Remove instructions from the block. + rm_block.ForEachInst([&rm_block, this](Instruction* inst) { + // Note that we do not kill the block label instruction here. The label + // instruction is needed to identify the block, which is needed by the + // removal of phi operands. + if (inst != rm_block.GetLabelInst()) { + context()->KillInst(inst); + } + }); + + // Remove the label instruction last. + auto label = rm_block.GetLabelInst(); + context()->KillInst(label); + + *bi = bi->Erase(); +} + +bool MemPass::RemoveUnreachableBlocks(Function* func) { + bool modified = false; + + // Mark reachable all blocks reachable from the function's entry block. + std::unordered_set reachable_blocks; + std::unordered_set visited_blocks; + std::queue worklist; + reachable_blocks.insert(func->entry().get()); + + // Initially mark the function entry point as reachable. + worklist.push(func->entry().get()); + + auto mark_reachable = [&reachable_blocks, &visited_blocks, &worklist, + this](uint32_t label_id) { + auto successor = cfg()->block(label_id); + if (visited_blocks.count(successor) == 0) { + reachable_blocks.insert(successor); + worklist.push(successor); + visited_blocks.insert(successor); + } + }; + + // Transitively mark all blocks reachable from the entry as reachable. + while (!worklist.empty()) { + BasicBlock* block = worklist.front(); + worklist.pop(); + + // All the successors of a live block are also live. + static_cast(block)->ForEachSuccessorLabel( + mark_reachable); + + // All the Merge and ContinueTarget blocks of a live block are also live. + block->ForMergeAndContinueLabel(mark_reachable); + } + + // Update operands of Phi nodes that reference unreachable blocks. + for (auto& block : *func) { + // If the block is about to be removed, don't bother updating its + // Phi instructions. + if (reachable_blocks.count(&block) == 0) { + continue; + } + + // If the block is reachable and has Phi instructions, remove all + // operands from its Phi instructions that reference unreachable blocks. + // If the block has no Phi instructions, this is a no-op. + block.ForEachPhiInst([&reachable_blocks, this](Instruction* phi) { + RemovePhiOperands(phi, reachable_blocks); + }); + } + + // Erase unreachable blocks. + for (auto ebi = func->begin(); ebi != func->end();) { + if (reachable_blocks.count(&*ebi) == 0) { + RemoveBlock(&ebi); + modified = true; + } else { + ++ebi; + } + } + + return modified; +} + +bool MemPass::CFGCleanup(Function* func) { + bool modified = false; + modified |= RemoveUnreachableBlocks(func); + return modified; +} + +void MemPass::CollectTargetVars(Function* func) { + seen_target_vars_.clear(); + seen_non_target_vars_.clear(); + type2undefs_.clear(); + + // Collect target (and non-) variable sets. Remove variables with + // non-load/store refs from target variable set + for (auto& blk : *func) { + for (auto& inst : blk) { + switch (inst.opcode()) { + case SpvOpStore: + case SpvOpLoad: { + uint32_t varId; + (void)GetPtr(&inst, &varId); + if (!IsTargetVar(varId)) break; + if (HasOnlySupportedRefs(varId)) break; + seen_non_target_vars_.insert(varId); + seen_target_vars_.erase(varId); + } break; + default: + break; + } + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/mem_pass.h b/third_party/spirv-tools/source/opt/mem_pass.h new file mode 100644 index 0000000..dcc16b6 --- /dev/null +++ b/third_party/spirv-tools/source/opt/mem_pass.h @@ -0,0 +1,164 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_MEM_PASS_H_ +#define SOURCE_OPT_MEM_PASS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// A common base class for mem2reg-type passes. Provides common +// utility functions and supporting state. +class MemPass : public Pass { + public: + virtual ~MemPass() = default; + + // Returns an undef value for the given |var_id|'s type. + uint32_t GetUndefVal(uint32_t var_id) { + return Type2Undef(GetPointeeTypeId(get_def_use_mgr()->GetDef(var_id))); + } + + // Given a load or store |ip|, return the pointer instruction. + // Also return the base variable's id in |varId|. If no base variable is + // found, |varId| will be 0. + Instruction* GetPtr(Instruction* ip, uint32_t* varId); + + // Return true if |varId| is a previously identified target variable. + // Return false if |varId| is a previously identified non-target variable. + // + // Non-target variables are variable of function scope of a target type that + // are accessed with constant-index access chains. not accessed with + // non-constant-index access chains. Also cache non-target variables. + // + // If variable is not cached, return true if variable is a function scope + // variable of target type, false otherwise. Updates caches of target and + // non-target variables. + bool IsTargetVar(uint32_t varId); + + // Collect target SSA variables. This traverses all the loads and stores in + // function |func| looking for variables that can be replaced with SSA IDs. It + // populates the sets |seen_target_vars_| and |seen_non_target_vars_|. + void CollectTargetVars(Function* func); + + protected: + MemPass(); + + // Returns true if |typeInst| is a scalar type + // or a vector or matrix + bool IsBaseTargetType(const Instruction* typeInst) const; + + // Returns true if |typeInst| is a math type or a struct or array + // of a math type. + // TODO(): Add more complex types to convert + bool IsTargetType(const Instruction* typeInst) const; + + // Returns true if |opcode| is a non-ptr access chain op + bool IsNonPtrAccessChain(const SpvOp opcode) const; + + // Given the id |ptrId|, return true if the top-most non-CopyObj is + // a variable, a non-ptr access chain or a parameter of pointer type. + bool IsPtr(uint32_t ptrId); + + // Given the id of a pointer |ptrId|, return the top-most non-CopyObj. + // Also return the base variable's id in |varId|. If no base variable is + // found, |varId| will be 0. + Instruction* GetPtr(uint32_t ptrId, uint32_t* varId); + + // Return true if all uses of |id| are only name or decorate ops. + bool HasOnlyNamesAndDecorates(uint32_t id) const; + + // Kill all instructions in block |bp|. Whether or not to kill the label is + // indicated by |killLabel|. + void KillAllInsts(BasicBlock* bp, bool killLabel = true); + + // Return true if any instruction loads from |varId| + bool HasLoads(uint32_t varId) const; + + // Return true if |varId| is not a function variable or if it has + // a load + bool IsLiveVar(uint32_t varId) const; + + // Add stores using |ptr_id| to |insts| + void AddStores(uint32_t ptr_id, std::queue* insts); + + // Delete |inst| and iterate DCE on all its operands if they are now + // useless. If a load is deleted and its variable has no other loads, + // delete all its variable's stores. + void DCEInst(Instruction* inst, const std::function&); + + // Call all the cleanup helper functions on |func|. + bool CFGCleanup(Function* func); + + // Return true if |op| is supported decorate. + inline bool IsNonTypeDecorate(uint32_t op) const { + return (op == SpvOpDecorate || op == SpvOpDecorateId); + } + + // Return the id of an undef value with type |type_id|. Create and insert an + // undef after the first non-variable in the function if it doesn't already + // exist. Add undef to function undef map. Returns 0 of the value does not + // exist, and cannot be created. + uint32_t Type2Undef(uint32_t type_id); + + // Cache of verified target vars + std::unordered_set seen_target_vars_; + + // Cache of verified non-target vars + std::unordered_set seen_non_target_vars_; + + private: + // Return true if all uses of |varId| are only through supported reference + // operations ie. loads and store. Also cache in supported_ref_vars_. + // TODO(dnovillo): This function is replicated in other passes and it's + // slightly different in every pass. Is it possible to make one common + // implementation? + bool HasOnlySupportedRefs(uint32_t varId); + + // Remove all the unreachable basic blocks in |func|. + bool RemoveUnreachableBlocks(Function* func); + + // Remove the block pointed by the iterator |*bi|. This also removes + // all the instructions in the pointed-to block. + void RemoveBlock(Function::iterator* bi); + + // Remove Phi operands in |phi| that are coming from blocks not in + // |reachable_blocks|. + void RemovePhiOperands( + Instruction* phi, + const std::unordered_set& reachable_blocks); + + // Map from type to undef + std::unordered_map type2undefs_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_MEM_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/merge_return_pass.cpp b/third_party/spirv-tools/source/opt/merge_return_pass.cpp new file mode 100644 index 0000000..b43eb31 --- /dev/null +++ b/third_party/spirv-tools/source/opt/merge_return_pass.cpp @@ -0,0 +1,878 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/merge_return_pass.h" + +#include +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" +#include "source/util/bit_vector.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +Pass::Status MergeReturnPass::Process() { + bool is_shader = + context()->get_feature_mgr()->HasCapability(SpvCapabilityShader); + + bool failed = false; + ProcessFunction pfn = [&failed, is_shader, this](Function* function) { + std::vector return_blocks = CollectReturnBlocks(function); + if (return_blocks.size() <= 1) { + if (!is_shader || return_blocks.size() == 0) { + return false; + } + bool isInConstruct = + context()->GetStructuredCFGAnalysis()->ContainingConstruct( + return_blocks[0]->id()) != 0; + bool endsWithReturn = return_blocks[0] == function->tail(); + if (!isInConstruct && endsWithReturn) { + return false; + } + } + + function_ = function; + return_flag_ = nullptr; + return_value_ = nullptr; + final_return_block_ = nullptr; + + if (is_shader) { + if (!ProcessStructured(function, return_blocks)) { + failed = true; + } + } else { + MergeReturnBlocks(function, return_blocks); + } + return true; + }; + + bool modified = context()->ProcessReachableCallTree(pfn); + + if (failed) { + return Status::Failure; + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +void MergeReturnPass::GenerateState(BasicBlock* block) { + if (Instruction* mergeInst = block->GetMergeInst()) { + if (mergeInst->opcode() == SpvOpLoopMerge) { + // If new loop, break to this loop merge block + state_.emplace_back(mergeInst, mergeInst); + } else { + auto branchInst = mergeInst->NextNode(); + if (branchInst->opcode() == SpvOpSwitch) { + // If switch inside of loop, break to innermost loop merge block. + // Otherwise need to break to this switch merge block. + auto lastMergeInst = state_.back().BreakMergeInst(); + if (lastMergeInst && lastMergeInst->opcode() == SpvOpLoopMerge) + state_.emplace_back(lastMergeInst, mergeInst); + else + state_.emplace_back(mergeInst, mergeInst); + } else { + // If branch conditional inside loop, always break to innermost + // loop merge block. If branch conditional inside switch, break to + // innermost switch merge block. + auto lastMergeInst = state_.back().BreakMergeInst(); + state_.emplace_back(lastMergeInst, mergeInst); + } + } + } +} + +bool MergeReturnPass::ProcessStructured( + Function* function, const std::vector& return_blocks) { + if (HasNontrivialUnreachableBlocks(function)) { + if (consumer()) { + std::string message = + "Module contains unreachable blocks during merge return. Run dead " + "branch elimination before merge return."; + consumer()(SPV_MSG_ERROR, 0, {0, 0, 0}, message.c_str()); + } + return false; + } + + RecordImmediateDominators(function); + AddSingleCaseSwitchAroundFunction(); + + std::list order; + cfg()->ComputeStructuredOrder(function, &*function->begin(), &order); + + state_.clear(); + state_.emplace_back(nullptr, nullptr); + for (auto block : order) { + if (cfg()->IsPseudoEntryBlock(block) || cfg()->IsPseudoExitBlock(block) || + block == final_return_block_) { + continue; + } + + auto blockId = block->GetLabelInst()->result_id(); + if (blockId == CurrentState().CurrentMergeId()) { + // Pop the current state as we've hit the merge + state_.pop_back(); + } + + ProcessStructuredBlock(block); + + // Generate state for next block if warranted + GenerateState(block); + } + + state_.clear(); + state_.emplace_back(nullptr, nullptr); + std::unordered_set predicated; + for (auto block : order) { + if (cfg()->IsPseudoEntryBlock(block) || cfg()->IsPseudoExitBlock(block)) { + continue; + } + + auto blockId = block->id(); + if (blockId == CurrentState().CurrentMergeId()) { + // Pop the current state as we've hit the merge + state_.pop_back(); + } + + // Predicate successors of the original return blocks as necessary. + if (std::find(return_blocks.begin(), return_blocks.end(), block) != + return_blocks.end()) { + if (!PredicateBlocks(block, &predicated, &order)) { + return false; + } + } + + // Generate state for next block if warranted + GenerateState(block); + } + + // We have not kept the dominator tree up-to-date. + // Invalidate it at this point to make sure it will be rebuilt. + context()->RemoveDominatorAnalysis(function); + AddNewPhiNodes(); + return true; +} + +void MergeReturnPass::CreateReturnBlock() { + // Create a label for the new return block + std::unique_ptr return_label( + new Instruction(context(), SpvOpLabel, 0u, TakeNextId(), {})); + + // Create the new basic block + std::unique_ptr return_block( + new BasicBlock(std::move(return_label))); + function_->AddBasicBlock(std::move(return_block)); + final_return_block_ = &*(--function_->end()); + context()->AnalyzeDefUse(final_return_block_->GetLabelInst()); + context()->set_instr_block(final_return_block_->GetLabelInst(), + final_return_block_); + final_return_block_->SetParent(function_); +} + +void MergeReturnPass::CreateReturn(BasicBlock* block) { + AddReturnValue(); + + if (return_value_) { + // Load and return the final return value + uint32_t loadId = TakeNextId(); + block->AddInstruction(MakeUnique( + context(), SpvOpLoad, function_->type_id(), loadId, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {return_value_->result_id()}}})); + Instruction* var_inst = block->terminator(); + context()->AnalyzeDefUse(var_inst); + context()->set_instr_block(var_inst, block); + context()->get_decoration_mgr()->CloneDecorations( + return_value_->result_id(), loadId, {SpvDecorationRelaxedPrecision}); + + block->AddInstruction(MakeUnique( + context(), SpvOpReturnValue, 0, 0, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {loadId}}})); + context()->AnalyzeDefUse(block->terminator()); + context()->set_instr_block(block->terminator(), block); + } else { + block->AddInstruction(MakeUnique(context(), SpvOpReturn)); + context()->AnalyzeDefUse(block->terminator()); + context()->set_instr_block(block->terminator(), block); + } +} + +void MergeReturnPass::ProcessStructuredBlock(BasicBlock* block) { + SpvOp tail_opcode = block->tail()->opcode(); + if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue) { + if (!return_flag_) { + AddReturnFlag(); + } + } + + if (tail_opcode == SpvOpReturn || tail_opcode == SpvOpReturnValue || + tail_opcode == SpvOpUnreachable) { + assert(CurrentState().InBreakable() && + "Should be in the placeholder construct."); + BranchToBlock(block, CurrentState().BreakMergeId()); + return_blocks_.insert(block->id()); + } +} + +void MergeReturnPass::BranchToBlock(BasicBlock* block, uint32_t target) { + if (block->tail()->opcode() == SpvOpReturn || + block->tail()->opcode() == SpvOpReturnValue) { + RecordReturned(block); + RecordReturnValue(block); + } + + BasicBlock* target_block = context()->get_instr_block(target); + if (target_block->GetLoopMergeInst()) { + cfg()->SplitLoopHeader(target_block); + } + UpdatePhiNodes(block, target_block); + + Instruction* return_inst = block->terminator(); + return_inst->SetOpcode(SpvOpBranch); + return_inst->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {target}}}); + context()->get_def_use_mgr()->AnalyzeInstDefUse(return_inst); + new_edges_[target_block].insert(block->id()); + cfg()->AddEdge(block->id(), target); +} + +void MergeReturnPass::UpdatePhiNodes(BasicBlock* new_source, + BasicBlock* target) { + target->ForEachPhiInst([this, new_source](Instruction* inst) { + uint32_t undefId = Type2Undef(inst->type_id()); + inst->AddOperand({SPV_OPERAND_TYPE_ID, {undefId}}); + inst->AddOperand({SPV_OPERAND_TYPE_ID, {new_source->id()}}); + context()->UpdateDefUse(inst); + }); +} + +void MergeReturnPass::CreatePhiNodesForInst(BasicBlock* merge_block, + Instruction& inst) { + DominatorAnalysis* dom_tree = + context()->GetDominatorAnalysis(merge_block->GetParent()); + + if (inst.result_id() != 0) { + BasicBlock* inst_bb = context()->get_instr_block(&inst); + std::vector users_to_update; + context()->get_def_use_mgr()->ForEachUser( + &inst, + [&users_to_update, &dom_tree, &inst, inst_bb, this](Instruction* user) { + BasicBlock* user_bb = nullptr; + if (user->opcode() != SpvOpPhi) { + user_bb = context()->get_instr_block(user); + } else { + // For OpPhi, the use should be considered to be in the predecessor. + for (uint32_t i = 0; i < user->NumInOperands(); i += 2) { + if (user->GetSingleWordInOperand(i) == inst.result_id()) { + uint32_t user_bb_id = user->GetSingleWordInOperand(i + 1); + user_bb = context()->get_instr_block(user_bb_id); + break; + } + } + } + + // If |user_bb| is nullptr, then |user| is not in the function. It is + // something like an OpName or decoration, which should not be + // replaced with the result of the OpPhi. + if (user_bb && !dom_tree->Dominates(inst_bb, user_bb)) { + users_to_update.push_back(user); + } + }); + + if (users_to_update.empty()) { + return; + } + + // There is at least one values that needs to be replaced. + // First create the OpPhi instruction. + uint32_t undef_id = Type2Undef(inst.type_id()); + std::vector phi_operands; + const std::set& new_edges = new_edges_[merge_block]; + + // Add the OpPhi operands. If the predecessor is a return block use undef, + // otherwise use |inst|'s id. + std::vector preds = cfg()->preds(merge_block->id()); + for (uint32_t pred_id : preds) { + if (new_edges.count(pred_id)) { + phi_operands.push_back(undef_id); + } else { + phi_operands.push_back(inst.result_id()); + } + phi_operands.push_back(pred_id); + } + + Instruction* new_phi = nullptr; + // If the instruction is a pointer and variable pointers are not an option, + // then we have to regenerate the instruction instead of creating an OpPhi + // instruction. If not, the Spir-V will be invalid. + Instruction* inst_type = get_def_use_mgr()->GetDef(inst.type_id()); + bool regenerateInstruction = false; + if (inst_type->opcode() == SpvOpTypePointer) { + if (!context()->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointers)) { + regenerateInstruction = true; + } + + uint32_t storage_class = inst_type->GetSingleWordInOperand(0); + if (storage_class != SpvStorageClassWorkgroup && + storage_class != SpvStorageClassStorageBuffer) { + regenerateInstruction = true; + } + } + + if (regenerateInstruction) { + std::unique_ptr regen_inst(inst.Clone(context())); + uint32_t new_id = TakeNextId(); + regen_inst->SetResultId(new_id); + Instruction* insert_pos = &*merge_block->begin(); + while (insert_pos->opcode() == SpvOpPhi) { + insert_pos = insert_pos->NextNode(); + } + new_phi = insert_pos->InsertBefore(std::move(regen_inst)); + get_def_use_mgr()->AnalyzeInstDefUse(new_phi); + context()->set_instr_block(new_phi, merge_block); + + new_phi->ForEachInId([dom_tree, merge_block, this](uint32_t* use_id) { + Instruction* use = get_def_use_mgr()->GetDef(*use_id); + BasicBlock* use_bb = context()->get_instr_block(use); + if (use_bb != nullptr && !dom_tree->Dominates(use_bb, merge_block)) { + CreatePhiNodesForInst(merge_block, *use); + } + }); + } else { + InstructionBuilder builder( + context(), &*merge_block->begin(), + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + new_phi = builder.AddPhi(inst.type_id(), phi_operands); + } + uint32_t result_of_phi = new_phi->result_id(); + + // Update all of the users to use the result of the new OpPhi. + for (Instruction* user : users_to_update) { + user->ForEachInId([&inst, result_of_phi](uint32_t* id) { + if (*id == inst.result_id()) { + *id = result_of_phi; + } + }); + context()->AnalyzeUses(user); + } + } +} + +bool MergeReturnPass::PredicateBlocks( + BasicBlock* return_block, std::unordered_set* predicated, + std::list* order) { + // The CFG is being modified as the function proceeds so avoid caching + // successors. + + if (predicated->count(return_block)) { + return true; + } + + BasicBlock* block = nullptr; + const BasicBlock* const_block = const_cast(return_block); + const_block->ForEachSuccessorLabel([this, &block](const uint32_t idx) { + BasicBlock* succ_block = context()->get_instr_block(idx); + assert(block == nullptr); + block = succ_block; + }); + assert(block && + "Return blocks should have returns already replaced by a single " + "unconditional branch."); + + auto state = state_.rbegin(); + std::unordered_set seen; + if (block->id() == state->CurrentMergeId()) { + state++; + } else if (block->id() == state->BreakMergeId()) { + while (state->BreakMergeId() == block->id()) { + state++; + } + } + + while (block != nullptr && block != final_return_block_) { + if (!predicated->insert(block).second) break; + // Skip structured subgraphs. + assert(state->InBreakable() && + "Should be in the placeholder construct at the very least."); + Instruction* break_merge_inst = state->BreakMergeInst(); + uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0); + while (state->BreakMergeId() == merge_block_id) { + state++; + } + if (!BreakFromConstruct(block, predicated, order, break_merge_inst)) { + return false; + } + block = context()->get_instr_block(merge_block_id); + } + return true; +} + +bool MergeReturnPass::BreakFromConstruct( + BasicBlock* block, std::unordered_set* predicated, + std::list* order, Instruction* break_merge_inst) { + // Make sure the CFG is build here. If we don't then it becomes very hard + // to know which new blocks need to be updated. + context()->BuildInvalidAnalyses(IRContext::kAnalysisCFG); + + // When predicating, be aware of whether this block is a header block, a + // merge block or both. + // + // If this block is a merge block, ensure the appropriate header stays + // up-to-date with any changes (i.e. points to the pre-header). + // + // If this block is a header block, predicate the entire structured + // subgraph. This can act recursively. + + // If |block| is a loop header, then the back edge must jump to the original + // code, not the new header. + if (block->GetLoopMergeInst()) { + if (cfg()->SplitLoopHeader(block) == nullptr) { + return false; + } + } + + uint32_t merge_block_id = break_merge_inst->GetSingleWordInOperand(0); + BasicBlock* merge_block = context()->get_instr_block(merge_block_id); + if (merge_block->GetLoopMergeInst()) { + cfg()->SplitLoopHeader(merge_block); + } + + // Leave the phi instructions behind. + auto iter = block->begin(); + while (iter->opcode() == SpvOpPhi) { + ++iter; + } + + // Forget about the edges leaving block. They will be removed. + cfg()->RemoveSuccessorEdges(block); + + auto old_body_id = TakeNextId(); + BasicBlock* old_body = block->SplitBasicBlock(context(), old_body_id, iter); + predicated->insert(old_body); + + // If a return block is being split, mark the new body block also as a return + // block. + if (return_blocks_.count(block->id())) { + return_blocks_.insert(old_body_id); + } + + // If |block| was a continue target for a loop |old_body| is now the correct + // continue target. + if (break_merge_inst->opcode() == SpvOpLoopMerge && + break_merge_inst->GetSingleWordInOperand(1) == block->id()) { + break_merge_inst->SetInOperand(1, {old_body->id()}); + context()->UpdateDefUse(break_merge_inst); + } + + // Update |order| so old_block will be traversed. + InsertAfterElement(block, old_body, order); + + // Within the new header we need the following: + // 1. Load of the return status flag + // 2. Branch to |merge_block| (true) or old body (false) + // 3. Update OpPhi instructions in |merge_block|. + // 4. Update the CFG. + // + // Since we are branching to the merge block of the current construct, there + // is no need for an OpSelectionMerge. + + InstructionBuilder builder( + context(), block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + // 1. Load of the return status flag + analysis::Bool bool_type; + uint32_t bool_id = context()->get_type_mgr()->GetId(&bool_type); + assert(bool_id != 0); + uint32_t load_id = + builder.AddLoad(bool_id, return_flag_->result_id())->result_id(); + + // 2. Branch to |merge_block| (true) or |old_body| (false) + builder.AddConditionalBranch(load_id, merge_block->id(), old_body->id(), + old_body->id()); + + if (!new_edges_[merge_block].insert(block->id()).second) { + // It is possible that we already inserted a new edge to the merge block. + // If so, that edge now goes from |old_body| to |merge_block|. + new_edges_[merge_block].insert(old_body->id()); + } + + // 3. Update OpPhi instructions in |merge_block|. + UpdatePhiNodes(block, merge_block); + + // 4. Update the CFG. We do this after updating the OpPhi instructions + // because |UpdatePhiNodes| assumes the edge from |block| has not been added + // to the CFG yet. + cfg()->AddEdges(block); + cfg()->RegisterBlock(old_body); + + assert(old_body->begin() != old_body->end()); + assert(block->begin() != block->end()); + return true; +} + +void MergeReturnPass::RecordReturned(BasicBlock* block) { + if (block->tail()->opcode() != SpvOpReturn && + block->tail()->opcode() != SpvOpReturnValue) + return; + + assert(return_flag_ && "Did not generate the return flag variable."); + + if (!constant_true_) { + analysis::Bool temp; + const analysis::Bool* bool_type = + context()->get_type_mgr()->GetRegisteredType(&temp)->AsBool(); + + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Constant* true_const = + const_mgr->GetConstant(bool_type, {true}); + constant_true_ = const_mgr->GetDefiningInstruction(true_const); + context()->UpdateDefUse(constant_true_); + } + + std::unique_ptr return_store(new Instruction( + context(), SpvOpStore, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {return_flag_->result_id()}}, + {SPV_OPERAND_TYPE_ID, {constant_true_->result_id()}}})); + + Instruction* store_inst = + &*block->tail().InsertBefore(std::move(return_store)); + context()->set_instr_block(store_inst, block); + context()->AnalyzeDefUse(store_inst); +} + +void MergeReturnPass::RecordReturnValue(BasicBlock* block) { + auto terminator = *block->tail(); + if (terminator.opcode() != SpvOpReturnValue) { + return; + } + + assert(return_value_ && + "Did not generate the variable to hold the return value."); + + std::unique_ptr value_store(new Instruction( + context(), SpvOpStore, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {return_value_->result_id()}}, + {SPV_OPERAND_TYPE_ID, {terminator.GetSingleWordInOperand(0u)}}})); + + Instruction* store_inst = + &*block->tail().InsertBefore(std::move(value_store)); + context()->set_instr_block(store_inst, block); + context()->AnalyzeDefUse(store_inst); +} + +void MergeReturnPass::AddReturnValue() { + if (return_value_) return; + + uint32_t return_type_id = function_->type_id(); + if (get_def_use_mgr()->GetDef(return_type_id)->opcode() == SpvOpTypeVoid) + return; + + uint32_t return_ptr_type = context()->get_type_mgr()->FindPointerToType( + return_type_id, SpvStorageClassFunction); + + uint32_t var_id = TakeNextId(); + std::unique_ptr returnValue(new Instruction( + context(), SpvOpVariable, return_ptr_type, var_id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); + + auto insert_iter = function_->begin()->begin(); + insert_iter.InsertBefore(std::move(returnValue)); + BasicBlock* entry_block = &*function_->begin(); + return_value_ = &*entry_block->begin(); + context()->AnalyzeDefUse(return_value_); + context()->set_instr_block(return_value_, entry_block); + + context()->get_decoration_mgr()->CloneDecorations( + function_->result_id(), var_id, {SpvDecorationRelaxedPrecision}); +} + +void MergeReturnPass::AddReturnFlag() { + if (return_flag_) return; + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + analysis::Bool temp; + uint32_t bool_id = type_mgr->GetTypeInstruction(&temp); + analysis::Bool* bool_type = type_mgr->GetType(bool_id)->AsBool(); + + const analysis::Constant* false_const = + const_mgr->GetConstant(bool_type, {false}); + uint32_t const_false_id = + const_mgr->GetDefiningInstruction(false_const)->result_id(); + + uint32_t bool_ptr_id = + type_mgr->FindPointerToType(bool_id, SpvStorageClassFunction); + + uint32_t var_id = TakeNextId(); + std::unique_ptr returnFlag(new Instruction( + context(), SpvOpVariable, bool_ptr_id, var_id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}, + {SPV_OPERAND_TYPE_ID, {const_false_id}}})); + + auto insert_iter = function_->begin()->begin(); + + insert_iter.InsertBefore(std::move(returnFlag)); + BasicBlock* entry_block = &*function_->begin(); + return_flag_ = &*entry_block->begin(); + context()->AnalyzeDefUse(return_flag_); + context()->set_instr_block(return_flag_, entry_block); +} + +std::vector MergeReturnPass::CollectReturnBlocks( + Function* function) { + std::vector return_blocks; + for (auto& block : *function) { + Instruction& terminator = *block.tail(); + if (terminator.opcode() == SpvOpReturn || + terminator.opcode() == SpvOpReturnValue) { + return_blocks.push_back(&block); + } + } + return return_blocks; +} + +void MergeReturnPass::MergeReturnBlocks( + Function* function, const std::vector& return_blocks) { + if (return_blocks.size() <= 1) { + // No work to do. + return; + } + + CreateReturnBlock(); + uint32_t return_id = final_return_block_->id(); + auto ret_block_iter = --function->end(); + // Create the PHI for the merged block (if necessary). + // Create new return. + std::vector phi_ops; + for (auto block : return_blocks) { + if (block->tail()->opcode() == SpvOpReturnValue) { + phi_ops.push_back( + {SPV_OPERAND_TYPE_ID, {block->tail()->GetSingleWordInOperand(0u)}}); + phi_ops.push_back({SPV_OPERAND_TYPE_ID, {block->id()}}); + } + } + + if (!phi_ops.empty()) { + // Need a PHI node to select the correct return value. + uint32_t phi_result_id = TakeNextId(); + uint32_t phi_type_id = function->type_id(); + std::unique_ptr phi_inst(new Instruction( + context(), SpvOpPhi, phi_type_id, phi_result_id, phi_ops)); + ret_block_iter->AddInstruction(std::move(phi_inst)); + BasicBlock::iterator phiIter = ret_block_iter->tail(); + + std::unique_ptr return_inst( + new Instruction(context(), SpvOpReturnValue, 0u, 0u, + {{SPV_OPERAND_TYPE_ID, {phi_result_id}}})); + ret_block_iter->AddInstruction(std::move(return_inst)); + BasicBlock::iterator ret = ret_block_iter->tail(); + + // Register the phi def and mark instructions for use updates. + get_def_use_mgr()->AnalyzeInstDefUse(&*phiIter); + get_def_use_mgr()->AnalyzeInstDef(&*ret); + } else { + std::unique_ptr return_inst( + new Instruction(context(), SpvOpReturn)); + ret_block_iter->AddInstruction(std::move(return_inst)); + } + + // Replace returns with branches + for (auto block : return_blocks) { + context()->ForgetUses(block->terminator()); + block->tail()->SetOpcode(SpvOpBranch); + block->tail()->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {return_id}}}); + get_def_use_mgr()->AnalyzeInstUse(block->terminator()); + get_def_use_mgr()->AnalyzeInstUse(block->GetLabelInst()); + } + + get_def_use_mgr()->AnalyzeInstDefUse(ret_block_iter->GetLabelInst()); +} + +void MergeReturnPass::AddNewPhiNodes() { + std::list order; + cfg()->ComputeStructuredOrder(function_, &*function_->begin(), &order); + + for (BasicBlock* bb : order) { + AddNewPhiNodes(bb); + } +} + +void MergeReturnPass::AddNewPhiNodes(BasicBlock* bb) { + // New phi nodes are needed for any id whose definition used to dominate |bb|, + // but no longer dominates |bb|. These are found by walking the dominator + // tree starting at the original immediate dominator of |bb| and ending at its + // current dominator. + + // Because we are walking the updated dominator tree it is important that the + // new phi nodes for the original dominators of |bb| have already been added. + // Otherwise some ids might be missed. Consider the case where bb1 dominates + // bb2, and bb2 dominates bb3. Suppose there are changes such that bb1 no + // longer dominates bb2 and the same for bb2 and bb3. This algorithm will not + // look at the ids defined in bb1. However, calling |AddNewPhiNodes(bb2)| + // first will add a phi node in bb2 for that value. Then a call to + // |AddNewPhiNodes(bb3)| will process that value by processing the phi in bb2. + DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function_); + + BasicBlock* dominator = dom_tree->ImmediateDominator(bb); + if (dominator == nullptr) { + return; + } + + BasicBlock* current_bb = context()->get_instr_block(original_dominator_[bb]); + while (current_bb != nullptr && current_bb != dominator) { + for (Instruction& inst : *current_bb) { + CreatePhiNodesForInst(bb, inst); + } + current_bb = dom_tree->ImmediateDominator(current_bb); + } +} + +void MergeReturnPass::RecordImmediateDominators(Function* function) { + DominatorAnalysis* dom_tree = context()->GetDominatorAnalysis(function); + for (BasicBlock& bb : *function) { + BasicBlock* dominator_bb = dom_tree->ImmediateDominator(&bb); + if (dominator_bb && dominator_bb != cfg()->pseudo_entry_block()) { + original_dominator_[&bb] = dominator_bb->terminator(); + } else { + original_dominator_[&bb] = nullptr; + } + } +} + +void MergeReturnPass::InsertAfterElement(BasicBlock* element, + BasicBlock* new_element, + std::list* list) { + auto pos = std::find(list->begin(), list->end(), element); + assert(pos != list->end()); + ++pos; + list->insert(pos, new_element); +} + +void MergeReturnPass::AddSingleCaseSwitchAroundFunction() { + CreateReturnBlock(); + CreateReturn(final_return_block_); + + if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { + cfg()->RegisterBlock(final_return_block_); + } + + CreateSingleCaseSwitch(final_return_block_); +} + +BasicBlock* MergeReturnPass::CreateContinueTarget(uint32_t header_label_id) { + std::unique_ptr label( + new Instruction(context(), SpvOpLabel, 0u, TakeNextId(), {})); + + // Create the new basic block + std::unique_ptr block(new BasicBlock(std::move(label))); + + // Insert the new block just before the return block + auto pos = function_->end(); + assert(pos != function_->begin()); + pos--; + assert(pos != function_->begin()); + assert(&*pos == final_return_block_); + auto new_block = &*pos.InsertBefore(std::move(block)); + new_block->SetParent(function_); + + context()->AnalyzeDefUse(new_block->GetLabelInst()); + context()->set_instr_block(new_block->GetLabelInst(), new_block); + + InstructionBuilder builder( + context(), new_block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + builder.AddBranch(header_label_id); + + if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { + cfg()->RegisterBlock(new_block); + } + + return new_block; +} + +void MergeReturnPass::CreateSingleCaseSwitch(BasicBlock* merge_target) { + // Insert the switch before any code is run. We have to split the entry + // block to make sure the OpVariable instructions remain in the entry block. + BasicBlock* start_block = &*function_->begin(); + auto split_pos = start_block->begin(); + while (split_pos->opcode() == SpvOpVariable) { + ++split_pos; + } + + BasicBlock* old_block = + start_block->SplitBasicBlock(context(), TakeNextId(), split_pos); + + // Add the switch to the end of the entry block. + InstructionBuilder builder( + context(), start_block, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + + builder.AddSwitch(builder.GetUintConstantId(0u), old_block->id(), {}, + merge_target->id()); + + if (context()->AreAnalysesValid(IRContext::kAnalysisCFG)) { + cfg()->RegisterBlock(old_block); + cfg()->AddEdges(start_block); + } +} + +bool MergeReturnPass::HasNontrivialUnreachableBlocks(Function* function) { + utils::BitVector reachable_blocks; + cfg()->ForEachBlockInPostOrder( + function->entry().get(), + [&reachable_blocks](BasicBlock* bb) { reachable_blocks.Set(bb->id()); }); + + for (auto& bb : *function) { + if (reachable_blocks.Get(bb.id())) { + continue; + } + + StructuredCFGAnalysis* struct_cfg_analysis = + context()->GetStructuredCFGAnalysis(); + if (struct_cfg_analysis->IsContinueBlock(bb.id())) { + // |bb| must be an empty block ending with a branch to the header. + Instruction* inst = &*bb.begin(); + if (inst->opcode() != SpvOpBranch) { + return true; + } + + if (inst->GetSingleWordInOperand(0) != + struct_cfg_analysis->ContainingLoop(bb.id())) { + return true; + } + } else if (struct_cfg_analysis->IsMergeBlock(bb.id())) { + // |bb| must be an empty block ending with OpUnreachable. + if (bb.begin()->opcode() != SpvOpUnreachable) { + return true; + } + } else { + return true; + } + } + return false; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/merge_return_pass.h b/third_party/spirv-tools/source/opt/merge_return_pass.h new file mode 100644 index 0000000..06a3e7b --- /dev/null +++ b/third_party/spirv-tools/source/opt/merge_return_pass.h @@ -0,0 +1,337 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_MERGE_RETURN_PASS_H_ +#define SOURCE_OPT_MERGE_RETURN_PASS_H_ + +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/function.h" +#include "source/opt/mem_pass.h" + +namespace spvtools { +namespace opt { + +/******************************************************************************* + * + * Handling Structured Control Flow: + * + * Structured control flow guarantees that the CFG will converge at a given + * point (the merge block). Within structured control flow, all blocks must be + * post-dominated by the merge block, except return blocks and break blocks. + * A break block is a block that branches to a containing construct's merge + * block. + * + * Beyond this, we further assume that all unreachable blocks have been + * cleaned up. This means that the only unreachable blocks are those necessary + * for valid structured control flow. + * + * Algorithm: + * + * If a return is encountered, it should record that: i) the function has + * "returned" and ii) the value of the return. The return should be replaced + * with a branch. If current block is not within structured control flow, this + * is the final return. This block should branch to the new return block (its + * direct successor). If the current block is within structured control flow, + * the branch destination should be the innermost construct's merge. This + * merge will always exist because a single case switch is added around the + * entire function. If the merge block produces any live values it will need to + * be predicated. While the merge is nested in structured control flow, the + * predication path should branch to the merge block of the inner-most loop + * (or switch if no loop) it is contained in. Once structured control flow has + * been exited, it will be at the merge of the single case switch, which will + * simply return. + * + * In the final return block, the return value should be loaded and returned. + * Memory promotion passes should be able to promote the newly introduced + * variables ("has returned" and "return value"). + * + * Predicating the Final Merge: + * + * At each merge block predication needs to be introduced (optimization: only if + * that block produces value live beyond it). This needs to be done carefully. + * The merge block should be split into multiple blocks. + * + * 1 (loop header) + * / \ + * (ret) 2 3 (merge) + * + * || + * \/ + * + * 0 (single case switch header) + * | + * 1 (loop header) + * / \ + * 2 | (merge) + * \ / + * 3' (merge) + * / \ + * | 3 (original code in 3) + * \ / + * (ret) 4 (single case switch merge) + * + * In the above (simple) example, the return originally in |2| is passed through + * the loop merge. That merge is predicated such that the old body of the block + * is the else branch. The branch condition is based on the value of the "has + * returned" variable. + * + ******************************************************************************/ + +// Documented in optimizer.hpp +class MergeReturnPass : public MemPass { + public: + MergeReturnPass() + : function_(nullptr), + return_flag_(nullptr), + return_value_(nullptr), + constant_true_(nullptr), + final_return_block_(nullptr) {} + + const char* name() const override { return "merge-return"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // This class is used to store the a break merge instruction and a current + // merge instruction. The intended use is to keep track of the block to + // break to and the current innermost control flow construct merge block. + class StructuredControlState { + public: + StructuredControlState(Instruction* break_merge, Instruction* merge) + : break_merge_(break_merge), current_merge_(merge) {} + + StructuredControlState(const StructuredControlState&) = default; + + bool InBreakable() const { return break_merge_; } + bool InStructuredFlow() const { return CurrentMergeId() != 0; } + + uint32_t CurrentMergeId() const { + return current_merge_ ? current_merge_->GetSingleWordInOperand(0u) : 0u; + } + + uint32_t CurrentMergeHeader() const { + return current_merge_ ? current_merge_->context() + ->get_instr_block(current_merge_) + ->id() + : 0; + } + + uint32_t BreakMergeId() const { + return break_merge_ ? break_merge_->GetSingleWordInOperand(0u) : 0u; + } + + Instruction* BreakMergeInst() const { return break_merge_; } + + private: + Instruction* break_merge_; + Instruction* current_merge_; + }; + + // Returns all BasicBlocks terminated by OpReturn or OpReturnValue in + // |function|. + std::vector CollectReturnBlocks(Function* function); + + // Creates a new basic block with a single return. If |function| returns a + // value, a phi node is created to select the correct value to return. + // Replaces old returns with an unconditional branch to the new block. + void MergeReturnBlocks(Function* function, + const std::vector& returnBlocks); + + // Generate and push new control flow state if |block| contains a merge. + void GenerateState(BasicBlock* block); + + // Merges the return instruction in |function| so that it has a single return + // statement. It is assumed that |function| has structured control flow, and + // that |return_blocks| is a list of all of the basic blocks in |function| + // that have a return. + bool ProcessStructured(Function* function, + const std::vector& return_blocks); + + // Changes an OpReturn* or OpUnreachable instruction at the end of |block| + // into a store to |return_flag_|, a store to |return_value_| (if necessary), + // and a branch to the appropriate merge block. + // + // Is is assumed that |AddReturnValue| have already been called to created the + // variable to store a return value if there is one. + // + // Note this will break the semantics. To fix this, PredicateBlock will have + // to be called on the merge block the branch targets. + void ProcessStructuredBlock(BasicBlock* block); + + // Creates a variable used to store whether or not the control flow has + // traversed a block that used to have a return. A pointer to the instruction + // declaring the variable is stored in |return_flag_|. + void AddReturnFlag(); + + // Creates the variable used to store the return value when passing through + // a block that use to contain an OpReturnValue. + void AddReturnValue(); + + // Adds a store that stores true to |return_flag_| immediately before the + // terminator of |block|. It is assumed that |AddReturnFlag| has already been + // called. + void RecordReturned(BasicBlock* block); + + // Adds an instruction that stores the value being returned in the + // OpReturnValue in |block|. The value is stored to |return_value_|, and the + // store is placed before the OpReturnValue. + // + // If |block| does not contain an OpReturnValue, then this function has no + // effect. If |block| contains an OpReturnValue, then |AddReturnValue| must + // have already been called to create the variable to store to. + void RecordReturnValue(BasicBlock* block); + + // Adds an unconditional branch in |block| that branches to |target|. It also + // adds stores to |return_flag_| and |return_value_| as needed. + // |AddReturnFlag| and |AddReturnValue| must have already been called. + void BranchToBlock(BasicBlock* block, uint32_t target); + + // For every basic block that is reachable from |return_block|, extra code is + // added to jump around any code that should not be executed because the + // original code would have already returned. This involves adding new + // selections constructs to jump around these instructions. + // + // If new blocks that are created will be added to |order|. This way a call + // can traverse these new block in structured order. + // + // Returns true if successful. + bool PredicateBlocks(BasicBlock* return_block, + std::unordered_set* pSet, + std::list* order); + + // Add a conditional branch at the start of |block| that either jumps to + // the merge block of |break_merge_inst| or the original code in |block| + // depending on the value in |return_flag_|. The continue target in + // |break_merge_inst| will be updated if needed. + // + // If new blocks that are created will be added to |order|. This way a call + // can traverse these new block in structured order. + // + // Returns true if successful. + bool BreakFromConstruct(BasicBlock* block, + std::unordered_set* predicated, + std::list* order, + Instruction* break_merge_inst); + + // Add an |OpReturn| or |OpReturnValue| to the end of |block|. If an + // |OpReturnValue| is needed, the return value is loaded from |return_value_|. + void CreateReturn(BasicBlock* block); + + // Creates a block at the end of the function that will become the single + // return block at the end of the pass. + void CreateReturnBlock(); + + // Creates a Phi node in |merge_block| for the result of |inst|. + // Any uses of the result of |inst| that are no longer + // dominated by |inst|, are replaced with the result of the new |OpPhi| + // instruction. + void CreatePhiNodesForInst(BasicBlock* merge_block, Instruction& inst); + + // Add new phi nodes for any id that no longer dominate all of it uses. A phi + // node is added to a block |bb| for an id if the id is defined between the + // original immediate dominator of |bb| and its new immidiate dominator. It + // is assumed that at this point there are no unreachable blocks in the + // control flow graph. + void AddNewPhiNodes(); + + // Creates any new phi nodes that are needed in |bb|. |AddNewPhiNodes| must + // have already been called on the original dominators of |bb|. + void AddNewPhiNodes(BasicBlock* bb); + + // Records the terminator of immediate dominator for every basic block in + // |function|. + void RecordImmediateDominators(Function* function); + + // Modifies existing OpPhi instruction in |target| block to account for the + // new edge from |new_source|. The value for that edge will be an Undef. + // + // The CFG must not include the edge from |new_source| to |target| yet. + void UpdatePhiNodes(BasicBlock* new_source, BasicBlock* target); + + StructuredControlState& CurrentState() { return state_.back(); } + + // Inserts |new_element| into |list| after the first occurrence of |element|. + // |element| must be in |list| at least once. + void InsertAfterElement(BasicBlock* element, BasicBlock* new_element, + std::list* list); + + // Creates a single case switch around all of the exectuable code of the + // current function where the switch and case value are both zero and the + // default is the merge block. Returns after the switch is executed. Sets + // |final_return_block_|. + void AddSingleCaseSwitchAroundFunction(); + + // Creates a new basic block that branches to |header_label_id|. Returns the + // new basic block. The block will be the second last basic block in the + // function. + BasicBlock* CreateContinueTarget(uint32_t header_label_id); + + // Creates a one case switch around the executable code of the function with + // |merge_target| as the merge node. + void CreateSingleCaseSwitch(BasicBlock* merge_target); + + // Returns true if |function| has an unreachable block that is not a continue + // target that simply branches back to the header, or a merge block containing + // 1 instruction which is OpUnreachable. + bool HasNontrivialUnreachableBlocks(Function* function); + + // A stack used to keep track of the break and current control flow construct + // merge blocks. + std::vector state_; + + // The current function being transformed. + Function* function_; + + // The |OpVariable| instruction defining a boolean variable used to keep track + // of whether or not the function is trying to return. + Instruction* return_flag_; + + // The |OpVariable| instruction defining a variabled to used to keep track of + // the value that was returned when passing through a block that use to + // contain an |OpReturnValue|. + Instruction* return_value_; + + // The instruction defining the boolean constant true. + Instruction* constant_true_; + + // The basic block that is suppose to become the contain the only return value + // after processing the current function. + BasicBlock* final_return_block_; + + // This is a map from a node to its original immediate dominator identified by + // the terminator if that block. We use the terminator because the block we + // want may change if the block is split. + std::unordered_map original_dominator_; + + // A map from a basic block, bb, to the set of basic blocks which represent + // the new edges that reach |bb|. + std::unordered_map> new_edges_; + + // Contains all return blocks that are merged. This is set is populated while + // processing structured blocks and used to properly construct OpPhi + // instructions. + std::unordered_set return_blocks_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_MERGE_RETURN_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/module.cpp b/third_party/spirv-tools/source/opt/module.cpp new file mode 100644 index 0000000..9d3b0ed --- /dev/null +++ b/third_party/spirv-tools/source/opt/module.cpp @@ -0,0 +1,251 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/module.h" + +#include +#include +#include + +#include "source/operand.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { + +uint32_t Module::TakeNextIdBound() { + if (context()) { + if (id_bound() >= context()->max_id_bound()) { + return 0; + } + } else if (id_bound() >= kDefaultMaxIdBound) { + return 0; + } + + return header_.bound++; +} + +std::vector Module::GetTypes() { + std::vector type_insts; + for (auto& inst : types_values_) { + if (IsTypeInst(inst.opcode())) type_insts.push_back(&inst); + } + return type_insts; +} + +std::vector Module::GetTypes() const { + std::vector type_insts; + for (auto& inst : types_values_) { + if (IsTypeInst(inst.opcode())) type_insts.push_back(&inst); + } + return type_insts; +} + +std::vector Module::GetConstants() { + std::vector const_insts; + for (auto& inst : types_values_) { + if (IsConstantInst(inst.opcode())) const_insts.push_back(&inst); + } + return const_insts; +} + +std::vector Module::GetConstants() const { + std::vector const_insts; + for (auto& inst : types_values_) { + if (IsConstantInst(inst.opcode())) const_insts.push_back(&inst); + } + return const_insts; +} + +uint32_t Module::GetGlobalValue(SpvOp opcode) const { + for (auto& inst : types_values_) { + if (inst.opcode() == opcode) return inst.result_id(); + } + return 0; +} + +void Module::AddGlobalValue(SpvOp opcode, uint32_t result_id, + uint32_t type_id) { + std::unique_ptr newGlobal( + new Instruction(context(), opcode, type_id, result_id, {})); + AddGlobalValue(std::move(newGlobal)); +} + +void Module::ForEachInst(const std::function& f, + bool run_on_debug_line_insts) { +#define DELEGATE(list) list.ForEachInst(f, run_on_debug_line_insts) + DELEGATE(capabilities_); + DELEGATE(extensions_); + DELEGATE(ext_inst_imports_); + if (memory_model_) memory_model_->ForEachInst(f, run_on_debug_line_insts); + DELEGATE(entry_points_); + DELEGATE(execution_modes_); + DELEGATE(debugs1_); + DELEGATE(debugs2_); + DELEGATE(debugs3_); + DELEGATE(ext_inst_debuginfo_); + DELEGATE(annotations_); + DELEGATE(types_values_); + for (auto& i : functions_) { + i->ForEachInst(f, run_on_debug_line_insts, + /* run_on_non_semantic_insts = */ true); + } +#undef DELEGATE +} + +void Module::ForEachInst(const std::function& f, + bool run_on_debug_line_insts) const { +#define DELEGATE(i) i.ForEachInst(f, run_on_debug_line_insts) + for (auto& i : capabilities_) DELEGATE(i); + for (auto& i : extensions_) DELEGATE(i); + for (auto& i : ext_inst_imports_) DELEGATE(i); + if (memory_model_) + static_cast(memory_model_.get()) + ->ForEachInst(f, run_on_debug_line_insts); + for (auto& i : entry_points_) DELEGATE(i); + for (auto& i : execution_modes_) DELEGATE(i); + for (auto& i : debugs1_) DELEGATE(i); + for (auto& i : debugs2_) DELEGATE(i); + for (auto& i : debugs3_) DELEGATE(i); + for (auto& i : annotations_) DELEGATE(i); + for (auto& i : types_values_) DELEGATE(i); + for (auto& i : ext_inst_debuginfo_) DELEGATE(i); + for (auto& i : functions_) { + static_cast(i.get())->ForEachInst( + f, run_on_debug_line_insts, + /* run_on_non_semantic_insts = */ true); + } + if (run_on_debug_line_insts) { + for (auto& i : trailing_dbg_line_info_) DELEGATE(i); + } +#undef DELEGATE +} + +void Module::ToBinary(std::vector* binary, bool skip_nop) const { + binary->push_back(header_.magic_number); + binary->push_back(header_.version); + // TODO(antiagainst): should we change the generator number? + binary->push_back(header_.generator); + binary->push_back(header_.bound); + binary->push_back(header_.reserved); + + size_t bound_idx = binary->size() - 2; + DebugScope last_scope(kNoDebugScope, kNoInlinedAt); + const Instruction* last_line_inst = nullptr; + bool between_merge_and_branch = false; + auto write_inst = [binary, skip_nop, &last_scope, &last_line_inst, + &between_merge_and_branch, this](const Instruction* i) { + // Skip emitting line instructions between merge and branch instructions. + auto opcode = i->opcode(); + if (between_merge_and_branch && + (opcode == SpvOpLine || opcode == SpvOpNoLine)) { + return; + } + between_merge_and_branch = false; + if (last_line_inst != nullptr) { + // If the current instruction is OpLine and it is the same with + // the last line instruction that is still effective (can be applied + // to the next instruction), we skip writing the current instruction. + if (opcode == SpvOpLine) { + uint32_t operand_index = 0; + if (last_line_inst->WhileEachInOperand( + [&operand_index, i](const uint32_t* word) { + assert(i->NumInOperandWords() > operand_index); + return *word == i->GetSingleWordInOperand(operand_index++); + })) { + return; + } + } else if (opcode != SpvOpNoLine && i->dbg_line_insts().empty()) { + // If the current instruction does not have the line information, + // the last line information is not effective any more. Emit OpNoLine + // to specify it. + binary->push_back((1 << 16) | static_cast(SpvOpNoLine)); + last_line_inst = nullptr; + } + } + if (!(skip_nop && i->IsNop())) { + const auto& scope = i->GetDebugScope(); + if (scope != last_scope) { + // Emit DebugScope |scope| to |binary|. + auto dbg_inst = ext_inst_debuginfo_.begin(); + scope.ToBinary(dbg_inst->type_id(), context()->TakeNextId(), + dbg_inst->GetSingleWordOperand(2), binary); + last_scope = scope; + } + + i->ToBinaryWithoutAttachedDebugInsts(binary); + } + // Update the last line instruction. + if (IsTerminatorInst(opcode) || opcode == SpvOpNoLine) { + last_line_inst = nullptr; + } else if (opcode == SpvOpLoopMerge || opcode == SpvOpSelectionMerge) { + between_merge_and_branch = true; + last_line_inst = nullptr; + } else if (opcode == SpvOpLine) { + last_line_inst = i; + } + }; + ForEachInst(write_inst, true); + + // We create new instructions for DebugScope. The bound must be updated. + binary->data()[bound_idx] = header_.bound; +} + +uint32_t Module::ComputeIdBound() const { + uint32_t highest = 0; + + ForEachInst( + [&highest](const Instruction* inst) { + for (const auto& operand : *inst) { + if (spvIsIdType(operand.type)) { + highest = std::max(highest, operand.words[0]); + } + } + }, + true /* scan debug line insts as well */); + + return highest + 1; +} + +bool Module::HasExplicitCapability(uint32_t cap) { + for (auto& ci : capabilities_) { + uint32_t tcap = ci.GetSingleWordOperand(0); + if (tcap == cap) { + return true; + } + } + return false; +} + +uint32_t Module::GetExtInstImportId(const char* extstr) { + for (auto& ei : ext_inst_imports_) + if (!strcmp(extstr, + reinterpret_cast(&(ei.GetInOperand(0).words[0])))) + return ei.result_id(); + return 0; +} + +std::ostream& operator<<(std::ostream& str, const Module& module) { + module.ForEachInst([&str](const Instruction* inst) { + str << *inst; + if (inst->opcode() != SpvOpFunctionEnd) { + str << std::endl; + } + }); + return str; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/module.h b/third_party/spirv-tools/source/opt/module.h new file mode 100644 index 0000000..75da870 --- /dev/null +++ b/third_party/spirv-tools/source/opt/module.h @@ -0,0 +1,536 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_MODULE_H_ +#define SOURCE_OPT_MODULE_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/instruction.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +class IRContext; + +// A struct for containing the module header information. +struct ModuleHeader { + uint32_t magic_number; + uint32_t version; + uint32_t generator; + uint32_t bound; + uint32_t reserved; +}; + +// A SPIR-V module. It contains all the information for a SPIR-V module and +// serves as the backbone of optimization transformations. +class Module { + public: + using iterator = UptrVectorIterator; + using const_iterator = UptrVectorIterator; + using inst_iterator = InstructionList::iterator; + using const_inst_iterator = InstructionList::const_iterator; + + // Creates an empty module with zero'd header. + Module() : header_({}), contains_debug_scope_(false) {} + + // Sets the header to the given |header|. + void SetHeader(const ModuleHeader& header) { header_ = header; } + + // Sets the Id bound. The Id bound cannot be set to 0. + void SetIdBound(uint32_t bound) { + assert(bound != 0); + header_.bound = bound; + } + + // Returns the Id bound. + uint32_t IdBound() { return header_.bound; } + + // Returns the current Id bound and increases it to the next available value. + // If the id bound has already reached its maximum value, then 0 is returned. + // The maximum value for the id bound is obtained from the context. If there + // is none, then the minimum that limit can be according to the spir-v + // specification. + // TODO(1841): Update the uses to check for a 0 return value. + uint32_t TakeNextIdBound(); + + // Appends a capability instruction to this module. + inline void AddCapability(std::unique_ptr c); + + // Appends an extension instruction to this module. + inline void AddExtension(std::unique_ptr e); + + // Appends an extended instruction set instruction to this module. + inline void AddExtInstImport(std::unique_ptr e); + + // Set the memory model for this module. + inline void SetMemoryModel(std::unique_ptr m); + + // Appends an entry point instruction to this module. + inline void AddEntryPoint(std::unique_ptr e); + + // Appends an execution mode instruction to this module. + inline void AddExecutionMode(std::unique_ptr e); + + // Appends a debug 1 instruction (excluding OpLine & OpNoLine) to this module. + // "debug 1" instructions are the ones in layout section 7.a), see section + // 2.4 Logical Layout of a Module from the SPIR-V specification. + inline void AddDebug1Inst(std::unique_ptr d); + + // Appends a debug 2 instruction (excluding OpLine & OpNoLine) to this module. + // "debug 2" instructions are the ones in layout section 7.b), see section + // 2.4 Logical Layout of a Module from the SPIR-V specification. + inline void AddDebug2Inst(std::unique_ptr d); + + // Appends a debug 3 instruction (OpModuleProcessed) to this module. + // This is due to decision by the SPIR Working Group, pending publication. + inline void AddDebug3Inst(std::unique_ptr d); + + // Appends a debug info extension (OpenCL.DebugInfo.100 or DebugInfo) + // instruction to this module. + inline void AddExtInstDebugInfo(std::unique_ptr d); + + // Appends an annotation instruction to this module. + inline void AddAnnotationInst(std::unique_ptr a); + + // Appends a type-declaration instruction to this module. + inline void AddType(std::unique_ptr t); + + // Appends a constant, global variable, or OpUndef instruction to this module. + inline void AddGlobalValue(std::unique_ptr v); + + // Appends a function to this module. + inline void AddFunction(std::unique_ptr f); + + // Sets |contains_debug_scope_| as true. + inline void SetContainsDebugScope(); + inline bool ContainsDebugScope() { return contains_debug_scope_; } + + // Returns a vector of pointers to type-declaration instructions in this + // module. + std::vector GetTypes(); + std::vector GetTypes() const; + // Returns a vector of pointers to constant-creation instructions in this + // module. + std::vector GetConstants(); + std::vector GetConstants() const; + + // Return result id of global value with |opcode|, 0 if not present. + uint32_t GetGlobalValue(SpvOp opcode) const; + + // Add global value with |opcode|, |result_id| and |type_id| + void AddGlobalValue(SpvOp opcode, uint32_t result_id, uint32_t type_id); + + inline uint32_t id_bound() const { return header_.bound; } + + inline uint32_t version() const { return header_.version; } + + inline void set_version(uint32_t v) { header_.version = v; } + + // Iterators for capabilities instructions contained in this module. + inline inst_iterator capability_begin(); + inline inst_iterator capability_end(); + inline IteratorRange capabilities(); + inline IteratorRange capabilities() const; + + // Iterators for ext_inst_imports instructions contained in this module. + inline inst_iterator ext_inst_import_begin(); + inline inst_iterator ext_inst_import_end(); + inline IteratorRange ext_inst_imports(); + inline IteratorRange ext_inst_imports() const; + + // Return the memory model instruction contained inthis module. + inline Instruction* GetMemoryModel() { return memory_model_.get(); } + inline const Instruction* GetMemoryModel() const { + return memory_model_.get(); + } + + // There are several kinds of debug instructions, according to where they can + // appear in the logical layout of a module: + // - Section 7a: OpString, OpSourceExtension, OpSource, OpSourceContinued + // - Section 7b: OpName, OpMemberName + // - Section 7c: OpModuleProcessed + // - Mostly anywhere: OpLine and OpNoLine + // + + // Iterators for debug 1 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7a. + inline inst_iterator debug1_begin(); + inline inst_iterator debug1_end(); + inline IteratorRange debugs1(); + inline IteratorRange debugs1() const; + + // Iterators for debug 2 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7b. + inline inst_iterator debug2_begin(); + inline inst_iterator debug2_end(); + inline IteratorRange debugs2(); + inline IteratorRange debugs2() const; + + // Iterators for debug 3 instructions (excluding OpLine & OpNoLine) contained + // in this module. These are for layout section 7c. + inline inst_iterator debug3_begin(); + inline inst_iterator debug3_end(); + inline IteratorRange debugs3(); + inline IteratorRange debugs3() const; + + // Iterators for debug info instructions (excluding OpLine & OpNoLine) + // contained in this module. These are OpExtInst for OpenCL.DebugInfo.100 + // or DebugInfo extension placed between section 9 and 10. + inline inst_iterator ext_inst_debuginfo_begin(); + inline inst_iterator ext_inst_debuginfo_end(); + inline IteratorRange ext_inst_debuginfo(); + inline IteratorRange ext_inst_debuginfo() const; + + // Iterators for entry point instructions contained in this module + inline IteratorRange entry_points(); + inline IteratorRange entry_points() const; + + // Iterators for execution_modes instructions contained in this module. + inline inst_iterator execution_mode_begin(); + inline inst_iterator execution_mode_end(); + inline IteratorRange execution_modes(); + inline IteratorRange execution_modes() const; + + // Iterators for annotation instructions contained in this module. + inline inst_iterator annotation_begin(); + inline inst_iterator annotation_end(); + IteratorRange annotations(); + IteratorRange annotations() const; + + // Iterators for extension instructions contained in this module. + inline inst_iterator extension_begin(); + inline inst_iterator extension_end(); + IteratorRange extensions(); + IteratorRange extensions() const; + + // Iterators for types, constants and global variables instructions. + inline inst_iterator types_values_begin(); + inline inst_iterator types_values_end(); + inline IteratorRange types_values(); + inline IteratorRange types_values() const; + + // Iterators for functions contained in this module. + iterator begin() { return iterator(&functions_, functions_.begin()); } + iterator end() { return iterator(&functions_, functions_.end()); } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + inline const_iterator cbegin() const; + inline const_iterator cend() const; + + // Invokes function |f| on all instructions in this module, and optionally on + // the debug line instructions that precede them. + void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false); + void ForEachInst(const std::function& f, + bool run_on_debug_line_insts = false) const; + + // Pushes the binary segments for this instruction into the back of *|binary|. + // If |skip_nop| is true and this is a OpNop, do nothing. + void ToBinary(std::vector* binary, bool skip_nop) const; + + // Pushes the binary segments for this instruction into the back of *|binary| + // including all OpLine and OpNoLine even if we can skip emitting some line + // instructions. If |skip_nop| is true and this is a OpNop, do nothing. + void ToBinaryWithAllOpLines(std::vector* binary, + bool skip_nop) const; + + // Returns 1 more than the maximum Id value mentioned in the module. + uint32_t ComputeIdBound() const; + + // Returns true if module has capability |cap| + bool HasExplicitCapability(uint32_t cap); + + // Returns id for OpExtInst instruction for extension |extstr|. + // Returns 0 if not found. + uint32_t GetExtInstImportId(const char* extstr); + + // Sets the associated context for this module + void SetContext(IRContext* c) { context_ = c; } + + // Gets the associated context for this module + IRContext* context() const { return context_; } + + // Sets the trailing debug line info to |dbg_line_info|. + void SetTrailingDbgLineInfo(std::vector&& dbg_line_info) { + trailing_dbg_line_info_ = std::move(dbg_line_info); + } + + std::vector& trailing_dbg_line_info() { + return trailing_dbg_line_info_; + } + + const std::vector& trailing_dbg_line_info() const { + return trailing_dbg_line_info_; + } + + private: + ModuleHeader header_; // Module header + + // The following fields respect the "Logical Layout of a Module" in + // Section 2.4 of the SPIR-V specification. + IRContext* context_; + InstructionList capabilities_; + InstructionList extensions_; + InstructionList ext_inst_imports_; + // A module only has one memory model instruction. + std::unique_ptr memory_model_; + InstructionList entry_points_; + InstructionList execution_modes_; + InstructionList debugs1_; + InstructionList debugs2_; + InstructionList debugs3_; + InstructionList ext_inst_debuginfo_; + InstructionList annotations_; + // Type declarations, constants, and global variable declarations. + InstructionList types_values_; + std::vector> functions_; + + // If the module ends with Op*Line instruction, they will not be attached to + // any instruction. We record them here, so they will not be lost. + std::vector trailing_dbg_line_info_; + + // This module contains DebugScope or DebugNoScope. + bool contains_debug_scope_; +}; + +// Pretty-prints |module| to |str|. Returns |str|. +std::ostream& operator<<(std::ostream& str, const Module& module); + +inline void Module::AddCapability(std::unique_ptr c) { + capabilities_.push_back(std::move(c)); +} + +inline void Module::AddExtension(std::unique_ptr e) { + extensions_.push_back(std::move(e)); +} + +inline void Module::AddExtInstImport(std::unique_ptr e) { + ext_inst_imports_.push_back(std::move(e)); +} + +inline void Module::SetMemoryModel(std::unique_ptr m) { + memory_model_ = std::move(m); +} + +inline void Module::AddEntryPoint(std::unique_ptr e) { + entry_points_.push_back(std::move(e)); +} + +inline void Module::AddExecutionMode(std::unique_ptr e) { + execution_modes_.push_back(std::move(e)); +} + +inline void Module::AddDebug1Inst(std::unique_ptr d) { + debugs1_.push_back(std::move(d)); +} + +inline void Module::AddDebug2Inst(std::unique_ptr d) { + debugs2_.push_back(std::move(d)); +} + +inline void Module::AddDebug3Inst(std::unique_ptr d) { + debugs3_.push_back(std::move(d)); +} + +inline void Module::AddExtInstDebugInfo(std::unique_ptr d) { + ext_inst_debuginfo_.push_back(std::move(d)); +} + +inline void Module::AddAnnotationInst(std::unique_ptr a) { + annotations_.push_back(std::move(a)); +} + +inline void Module::AddType(std::unique_ptr t) { + types_values_.push_back(std::move(t)); +} + +inline void Module::AddGlobalValue(std::unique_ptr v) { + types_values_.push_back(std::move(v)); +} + +inline void Module::AddFunction(std::unique_ptr f) { + functions_.emplace_back(std::move(f)); +} + +inline void Module::SetContainsDebugScope() { contains_debug_scope_ = true; } + +inline Module::inst_iterator Module::capability_begin() { + return capabilities_.begin(); +} +inline Module::inst_iterator Module::capability_end() { + return capabilities_.end(); +} + +inline IteratorRange Module::capabilities() { + return make_range(capabilities_.begin(), capabilities_.end()); +} + +inline IteratorRange Module::capabilities() const { + return make_range(capabilities_.begin(), capabilities_.end()); +} + +inline Module::inst_iterator Module::ext_inst_import_begin() { + return ext_inst_imports_.begin(); +} +inline Module::inst_iterator Module::ext_inst_import_end() { + return ext_inst_imports_.end(); +} + +inline IteratorRange Module::ext_inst_imports() { + return make_range(ext_inst_imports_.begin(), ext_inst_imports_.end()); +} + +inline IteratorRange Module::ext_inst_imports() + const { + return make_range(ext_inst_imports_.begin(), ext_inst_imports_.end()); +} + +inline Module::inst_iterator Module::debug1_begin() { return debugs1_.begin(); } +inline Module::inst_iterator Module::debug1_end() { return debugs1_.end(); } + +inline IteratorRange Module::debugs1() { + return make_range(debugs1_.begin(), debugs1_.end()); +} + +inline IteratorRange Module::debugs1() const { + return make_range(debugs1_.begin(), debugs1_.end()); +} + +inline Module::inst_iterator Module::debug2_begin() { return debugs2_.begin(); } +inline Module::inst_iterator Module::debug2_end() { return debugs2_.end(); } + +inline IteratorRange Module::debugs2() { + return make_range(debugs2_.begin(), debugs2_.end()); +} + +inline IteratorRange Module::debugs2() const { + return make_range(debugs2_.begin(), debugs2_.end()); +} + +inline Module::inst_iterator Module::debug3_begin() { return debugs3_.begin(); } +inline Module::inst_iterator Module::debug3_end() { return debugs3_.end(); } + +inline IteratorRange Module::debugs3() { + return make_range(debugs3_.begin(), debugs3_.end()); +} + +inline IteratorRange Module::debugs3() const { + return make_range(debugs3_.begin(), debugs3_.end()); +} + +inline Module::inst_iterator Module::ext_inst_debuginfo_begin() { + return ext_inst_debuginfo_.begin(); +} +inline Module::inst_iterator Module::ext_inst_debuginfo_end() { + return ext_inst_debuginfo_.end(); +} + +inline IteratorRange Module::ext_inst_debuginfo() { + return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end()); +} + +inline IteratorRange Module::ext_inst_debuginfo() + const { + return make_range(ext_inst_debuginfo_.begin(), ext_inst_debuginfo_.end()); +} + +inline IteratorRange Module::entry_points() { + return make_range(entry_points_.begin(), entry_points_.end()); +} + +inline IteratorRange Module::entry_points() const { + return make_range(entry_points_.begin(), entry_points_.end()); +} + +inline Module::inst_iterator Module::execution_mode_begin() { + return execution_modes_.begin(); +} +inline Module::inst_iterator Module::execution_mode_end() { + return execution_modes_.end(); +} + +inline IteratorRange Module::execution_modes() { + return make_range(execution_modes_.begin(), execution_modes_.end()); +} + +inline IteratorRange Module::execution_modes() + const { + return make_range(execution_modes_.begin(), execution_modes_.end()); +} + +inline Module::inst_iterator Module::annotation_begin() { + return annotations_.begin(); +} +inline Module::inst_iterator Module::annotation_end() { + return annotations_.end(); +} + +inline IteratorRange Module::annotations() { + return make_range(annotations_.begin(), annotations_.end()); +} + +inline IteratorRange Module::annotations() const { + return make_range(annotations_.begin(), annotations_.end()); +} + +inline Module::inst_iterator Module::extension_begin() { + return extensions_.begin(); +} +inline Module::inst_iterator Module::extension_end() { + return extensions_.end(); +} + +inline IteratorRange Module::extensions() { + return make_range(extensions_.begin(), extensions_.end()); +} + +inline IteratorRange Module::extensions() const { + return make_range(extensions_.begin(), extensions_.end()); +} + +inline Module::inst_iterator Module::types_values_begin() { + return types_values_.begin(); +} + +inline Module::inst_iterator Module::types_values_end() { + return types_values_.end(); +} + +inline IteratorRange Module::types_values() { + return make_range(types_values_.begin(), types_values_.end()); +} + +inline IteratorRange Module::types_values() const { + return make_range(types_values_.begin(), types_values_.end()); +} + +inline Module::const_iterator Module::cbegin() const { + return const_iterator(&functions_, functions_.cbegin()); +} + +inline Module::const_iterator Module::cend() const { + return const_iterator(&functions_, functions_.cend()); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_MODULE_H_ diff --git a/third_party/spirv-tools/source/opt/null_pass.h b/third_party/spirv-tools/source/opt/null_pass.h new file mode 100644 index 0000000..2b5974f --- /dev/null +++ b/third_party/spirv-tools/source/opt/null_pass.h @@ -0,0 +1,34 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_NULL_PASS_H_ +#define SOURCE_OPT_NULL_PASS_H_ + +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class NullPass : public Pass { + public: + const char* name() const override { return "null"; } + Status Process() override { return Status::SuccessWithoutChange; } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_NULL_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/optimizer.cpp b/third_party/spirv-tools/source/opt/optimizer.cpp new file mode 100644 index 0000000..1ded2ee --- /dev/null +++ b/third_party/spirv-tools/source/opt/optimizer.cpp @@ -0,0 +1,977 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/optimizer.hpp" + +#include +#include +#include +#include +#include +#include + +#include "source/opt/build_module.h" +#include "source/opt/graphics_robust_access_pass.h" +#include "source/opt/log.h" +#include "source/opt/pass_manager.h" +#include "source/opt/passes.h" +#include "source/spirv_optimizer_options.h" +#include "source/util/make_unique.h" +#include "source/util/string_utils.h" + +namespace spvtools { + +struct Optimizer::PassToken::Impl { + Impl(std::unique_ptr p) : pass(std::move(p)) {} + + std::unique_ptr pass; // Internal implementation pass. +}; + +Optimizer::PassToken::PassToken( + std::unique_ptr impl) + : impl_(std::move(impl)) {} + +Optimizer::PassToken::PassToken(std::unique_ptr&& pass) + : impl_(MakeUnique(std::move(pass))) {} + +Optimizer::PassToken::PassToken(PassToken&& that) + : impl_(std::move(that.impl_)) {} + +Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) { + impl_ = std::move(that.impl_); + return *this; +} + +Optimizer::PassToken::~PassToken() {} + +struct Optimizer::Impl { + explicit Impl(spv_target_env env) : target_env(env), pass_manager() {} + + spv_target_env target_env; // Target environment. + opt::PassManager pass_manager; // Internal implementation pass manager. +}; + +Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {} + +Optimizer::~Optimizer() {} + +void Optimizer::SetMessageConsumer(MessageConsumer c) { + // All passes' message consumer needs to be updated. + for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) { + impl_->pass_manager.GetPass(i)->SetMessageConsumer(c); + } + impl_->pass_manager.SetMessageConsumer(std::move(c)); +} + +const MessageConsumer& Optimizer::consumer() const { + return impl_->pass_manager.consumer(); +} + +Optimizer& Optimizer::RegisterPass(PassToken&& p) { + // Change to use the pass manager's consumer. + p.impl_->pass->SetMessageConsumer(consumer()); + impl_->pass_manager.AddPass(std::move(p.impl_->pass)); + return *this; +} + +// The legalization passes take a spir-v shader generated by an HLSL front-end +// and turn it into a valid vulkan spir-v shader. There are two ways in which +// the code will be invalid at the start: +// +// 1) There will be opaque objects, like images, which will be passed around +// in intermediate objects. Valid spir-v will have to replace the use of +// the opaque object with an intermediate object that is the result of the +// load of the global opaque object. +// +// 2) There will be variables that contain pointers to structured or uniform +// buffers. It be legal, the variables must be eliminated, and the +// references to the structured buffers must use the result of OpVariable +// in the Uniform storage class. +// +// Optimization in this list must accept shaders with these relaxation of the +// rules. There is not guarantee that this list of optimizations is able to +// legalize all inputs, but it is on a best effort basis. +// +// The legalization problem is essentially a very general copy propagation +// problem. The optimization we use are all used to either do copy propagation +// or enable more copy propagation. +Optimizer& Optimizer::RegisterLegalizationPasses() { + return + // Wrap OpKill instructions so all other code can be inlined. + RegisterPass(CreateWrapOpKillPass()) + // Remove unreachable block so that merge return works. + .RegisterPass(CreateDeadBranchElimPass()) + // Merge the returns so we can inline. + .RegisterPass(CreateMergeReturnPass()) + // Make sure uses and definitions are in the same function. + .RegisterPass(CreateInlineExhaustivePass()) + // Make private variable function scope + .RegisterPass(CreateEliminateDeadFunctionsPass()) + .RegisterPass(CreatePrivateToLocalPass()) + // Fix up the storage classes that DXC may have purposely generated + // incorrectly. All functions are inlined, and a lot of dead code has + // been removed. + .RegisterPass(CreateFixStorageClassPass()) + // Propagate the value stored to the loads in very simple cases. + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + // Split up aggregates so they are easier to deal with. + .RegisterPass(CreateScalarReplacementPass(0)) + // Remove loads and stores so everything is in intermediate values. + // Takes care of copy propagation of non-members. + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + // Propagate constants to get as many constant conditions on branches + // as possible. + .RegisterPass(CreateCCPPass()) + .RegisterPass(CreateLoopUnrollPass(true)) + .RegisterPass(CreateDeadBranchElimPass()) + // Copy propagate members. Cleans up code sequences generated by + // scalar replacement. Also important for removing OpPhi nodes. + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateCopyPropagateArraysPass()) + // May need loop unrolling here see + // https://github.com/Microsoft/DirectXShaderCompiler/pull/930 + // Get rid of unused code that contain traces of illegal code + // or unused references to unbound external objects + .RegisterPass(CreateVectorDCEPass()) + .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateReduceLoadSizePass()) + .RegisterPass(CreateAggressiveDCEPass()); +} + +Optimizer& Optimizer::RegisterPerformancePasses() { + return RegisterPass(CreateWrapOpKillPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateMergeReturnPass()) + .RegisterPass(CreateInlineExhaustivePass()) + .RegisterPass(CreateEliminateDeadFunctionsPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreatePrivateToLocalPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateScalarReplacementPass()) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateCCPPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateLoopUnrollPass(true)) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateRedundancyEliminationPass()) + .RegisterPass(CreateCombineAccessChainsPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateScalarReplacementPass()) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateSSARewritePass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateVectorDCEPass()) + .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateIfConversionPass()) + .RegisterPass(CreateCopyPropagateArraysPass()) + .RegisterPass(CreateReduceLoadSizePass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateRedundancyEliminationPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateSimplificationPass()); +} + +Optimizer& Optimizer::RegisterSizePasses() { + return RegisterPass(CreateWrapOpKillPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateMergeReturnPass()) + .RegisterPass(CreateInlineExhaustivePass()) + .RegisterPass(CreateEliminateDeadFunctionsPass()) + .RegisterPass(CreatePrivateToLocalPass()) + .RegisterPass(CreateScalarReplacementPass(0)) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateCCPPass()) + .RegisterPass(CreateLoopUnrollPass(true)) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateScalarReplacementPass(0)) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateIfConversionPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateLocalAccessChainConvertPass()) + .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateCopyPropagateArraysPass()) + .RegisterPass(CreateVectorDCEPass()) + .RegisterPass(CreateDeadInsertElimPass()) + .RegisterPass(CreateEliminateDeadMembersPass()) + .RegisterPass(CreateLocalSingleStoreElimPass()) + .RegisterPass(CreateBlockMergePass()) + .RegisterPass(CreateLocalMultiStoreElimPass()) + .RegisterPass(CreateRedundancyEliminationPass()) + .RegisterPass(CreateSimplificationPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateCFGCleanupPass()); +} + +Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() { + return RegisterPass(CreateStripAtomicCounterMemoryPass()) + .RegisterPass(CreateGenerateWebGPUInitializersPass()) + .RegisterPass(CreateLegalizeVectorShufflePass()) + .RegisterPass(CreateSplitInvalidUnreachablePass()) + .RegisterPass(CreateEliminateDeadConstantPass()) + .RegisterPass(CreateFlattenDecorationPass()) + .RegisterPass(CreateAggressiveDCEPass()) + .RegisterPass(CreateDeadBranchElimPass()) + .RegisterPass(CreateCompactIdsPass()); +} + +Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() { + return RegisterPass(CreateDecomposeInitializedVariablesPass()) + .RegisterPass(CreateCompactIdsPass()); +} + +bool Optimizer::RegisterPassesFromFlags(const std::vector& flags) { + for (const auto& flag : flags) { + if (!RegisterPassFromFlag(flag)) { + return false; + } + } + + return true; +} + +bool Optimizer::FlagHasValidForm(const std::string& flag) const { + if (flag == "-O" || flag == "-Os") { + return true; + } else if (flag.size() > 2 && flag.substr(0, 2) == "--") { + return true; + } + + Errorf(consumer(), nullptr, {}, + "%s is not a valid flag. Flag passes should have the form " + "'--pass_name[=pass_args]'. Special flag names also accepted: -O " + "and -Os.", + flag.c_str()); + return false; +} + +bool Optimizer::RegisterPassFromFlag(const std::string& flag) { + if (!FlagHasValidForm(flag)) { + return false; + } + + // Split flags of the form --pass_name=pass_args. + auto p = utils::SplitFlagArgs(flag); + std::string pass_name = p.first; + std::string pass_args = p.second; + + // FIXME(dnovillo): This should be re-factored so that pass names can be + // automatically checked against Pass::name() and PassToken instances created + // via a template function. Additionally, class Pass should have a desc() + // method that describes the pass (so it can be used in --help). + // + // Both Pass::name() and Pass::desc() should be static class members so they + // can be invoked without creating a pass instance. + if (pass_name == "strip-atomic-counter-memory") { + RegisterPass(CreateStripAtomicCounterMemoryPass()); + } else if (pass_name == "strip-debug") { + RegisterPass(CreateStripDebugInfoPass()); + } else if (pass_name == "strip-reflect") { + RegisterPass(CreateStripReflectInfoPass()); + } else if (pass_name == "set-spec-const-default-value") { + if (pass_args.size() > 0) { + auto spec_ids_vals = + opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString( + pass_args.c_str()); + if (!spec_ids_vals) { + Errorf(consumer(), nullptr, {}, + "Invalid argument for --set-spec-const-default-value: %s", + pass_args.c_str()); + return false; + } + RegisterPass( + CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals))); + } else { + Errorf(consumer(), nullptr, {}, + "Invalid spec constant value string '%s'. Expected a string of " + ": pairs.", + pass_args.c_str()); + return false; + } + } else if (pass_name == "if-conversion") { + RegisterPass(CreateIfConversionPass()); + } else if (pass_name == "freeze-spec-const") { + RegisterPass(CreateFreezeSpecConstantValuePass()); + } else if (pass_name == "inline-entry-points-exhaustive") { + RegisterPass(CreateInlineExhaustivePass()); + } else if (pass_name == "inline-entry-points-opaque") { + RegisterPass(CreateInlineOpaquePass()); + } else if (pass_name == "combine-access-chains") { + RegisterPass(CreateCombineAccessChainsPass()); + } else if (pass_name == "convert-local-access-chains") { + RegisterPass(CreateLocalAccessChainConvertPass()); + } else if (pass_name == "descriptor-scalar-replacement") { + RegisterPass(CreateDescriptorScalarReplacementPass()); + } else if (pass_name == "eliminate-dead-code-aggressive") { + RegisterPass(CreateAggressiveDCEPass()); + } else if (pass_name == "eliminate-insert-extract") { + RegisterPass(CreateInsertExtractElimPass()); + } else if (pass_name == "eliminate-local-single-block") { + RegisterPass(CreateLocalSingleBlockLoadStoreElimPass()); + } else if (pass_name == "eliminate-local-single-store") { + RegisterPass(CreateLocalSingleStoreElimPass()); + } else if (pass_name == "merge-blocks") { + RegisterPass(CreateBlockMergePass()); + } else if (pass_name == "merge-return") { + RegisterPass(CreateMergeReturnPass()); + } else if (pass_name == "eliminate-dead-branches") { + RegisterPass(CreateDeadBranchElimPass()); + } else if (pass_name == "eliminate-dead-functions") { + RegisterPass(CreateEliminateDeadFunctionsPass()); + } else if (pass_name == "eliminate-local-multi-store") { + RegisterPass(CreateLocalMultiStoreElimPass()); + } else if (pass_name == "eliminate-dead-const") { + RegisterPass(CreateEliminateDeadConstantPass()); + } else if (pass_name == "eliminate-dead-inserts") { + RegisterPass(CreateDeadInsertElimPass()); + } else if (pass_name == "eliminate-dead-variables") { + RegisterPass(CreateDeadVariableEliminationPass()); + } else if (pass_name == "eliminate-dead-members") { + RegisterPass(CreateEliminateDeadMembersPass()); + } else if (pass_name == "fold-spec-const-op-composite") { + RegisterPass(CreateFoldSpecConstantOpAndCompositePass()); + } else if (pass_name == "loop-unswitch") { + RegisterPass(CreateLoopUnswitchPass()); + } else if (pass_name == "scalar-replacement") { + if (pass_args.size() == 0) { + RegisterPass(CreateScalarReplacementPass()); + } else { + int limit = -1; + if (pass_args.find_first_not_of("0123456789") == std::string::npos) { + limit = atoi(pass_args.c_str()); + } + + if (limit >= 0) { + RegisterPass(CreateScalarReplacementPass(limit)); + } else { + Error(consumer(), nullptr, {}, + "--scalar-replacement must have no arguments or a non-negative " + "integer argument"); + return false; + } + } + } else if (pass_name == "strength-reduction") { + RegisterPass(CreateStrengthReductionPass()); + } else if (pass_name == "unify-const") { + RegisterPass(CreateUnifyConstantPass()); + } else if (pass_name == "flatten-decorations") { + RegisterPass(CreateFlattenDecorationPass()); + } else if (pass_name == "compact-ids") { + RegisterPass(CreateCompactIdsPass()); + } else if (pass_name == "cfg-cleanup") { + RegisterPass(CreateCFGCleanupPass()); + } else if (pass_name == "local-redundancy-elimination") { + RegisterPass(CreateLocalRedundancyEliminationPass()); + } else if (pass_name == "loop-invariant-code-motion") { + RegisterPass(CreateLoopInvariantCodeMotionPass()); + } else if (pass_name == "reduce-load-size") { + RegisterPass(CreateReduceLoadSizePass()); + } else if (pass_name == "redundancy-elimination") { + RegisterPass(CreateRedundancyEliminationPass()); + } else if (pass_name == "private-to-local") { + RegisterPass(CreatePrivateToLocalPass()); + } else if (pass_name == "remove-duplicates") { + RegisterPass(CreateRemoveDuplicatesPass()); + } else if (pass_name == "workaround-1209") { + RegisterPass(CreateWorkaround1209Pass()); + } else if (pass_name == "replace-invalid-opcode") { + RegisterPass(CreateReplaceInvalidOpcodePass()); + } else if (pass_name == "inst-bindless-check") { + RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false)); + RegisterPass(CreateSimplificationPass()); + RegisterPass(CreateDeadBranchElimPass()); + RegisterPass(CreateBlockMergePass()); + RegisterPass(CreateAggressiveDCEPass()); + } else if (pass_name == "inst-desc-idx-check") { + RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true)); + RegisterPass(CreateSimplificationPass()); + RegisterPass(CreateDeadBranchElimPass()); + RegisterPass(CreateBlockMergePass()); + RegisterPass(CreateAggressiveDCEPass()); + } else if (pass_name == "inst-buff-oob-check") { + RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, true)); + RegisterPass(CreateSimplificationPass()); + RegisterPass(CreateDeadBranchElimPass()); + RegisterPass(CreateBlockMergePass()); + RegisterPass(CreateAggressiveDCEPass()); + } else if (pass_name == "inst-buff-addr-check") { + RegisterPass(CreateInstBuffAddrCheckPass(7, 23)); + RegisterPass(CreateAggressiveDCEPass()); + } else if (pass_name == "convert-relaxed-to-half") { + RegisterPass(CreateConvertRelaxedToHalfPass()); + } else if (pass_name == "relax-float-ops") { + RegisterPass(CreateRelaxFloatOpsPass()); + } else if (pass_name == "inst-debug-printf") { + RegisterPass(CreateInstDebugPrintfPass(7, 23)); + } else if (pass_name == "simplify-instructions") { + RegisterPass(CreateSimplificationPass()); + } else if (pass_name == "ssa-rewrite") { + RegisterPass(CreateSSARewritePass()); + } else if (pass_name == "copy-propagate-arrays") { + RegisterPass(CreateCopyPropagateArraysPass()); + } else if (pass_name == "loop-fission") { + int register_threshold_to_split = + (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1; + if (register_threshold_to_split > 0) { + RegisterPass(CreateLoopFissionPass( + static_cast(register_threshold_to_split))); + } else { + Error(consumer(), nullptr, {}, + "--loop-fission must have a positive integer argument"); + return false; + } + } else if (pass_name == "loop-fusion") { + int max_registers_per_loop = + (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1; + if (max_registers_per_loop > 0) { + RegisterPass( + CreateLoopFusionPass(static_cast(max_registers_per_loop))); + } else { + Error(consumer(), nullptr, {}, + "--loop-fusion must have a positive integer argument"); + return false; + } + } else if (pass_name == "loop-unroll") { + RegisterPass(CreateLoopUnrollPass(true)); + } else if (pass_name == "upgrade-memory-model") { + RegisterPass(CreateUpgradeMemoryModelPass()); + } else if (pass_name == "vector-dce") { + RegisterPass(CreateVectorDCEPass()); + } else if (pass_name == "loop-unroll-partial") { + int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0; + if (factor > 0) { + RegisterPass(CreateLoopUnrollPass(false, factor)); + } else { + Error(consumer(), nullptr, {}, + "--loop-unroll-partial must have a positive integer argument"); + return false; + } + } else if (pass_name == "loop-peeling") { + RegisterPass(CreateLoopPeelingPass()); + } else if (pass_name == "loop-peeling-threshold") { + int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0; + if (factor > 0) { + opt::LoopPeelingPass::SetLoopPeelingThreshold(factor); + } else { + Error(consumer(), nullptr, {}, + "--loop-peeling-threshold must have a positive integer argument"); + return false; + } + } else if (pass_name == "ccp") { + RegisterPass(CreateCCPPass()); + } else if (pass_name == "code-sink") { + RegisterPass(CreateCodeSinkingPass()); + } else if (pass_name == "fix-storage-class") { + RegisterPass(CreateFixStorageClassPass()); + } else if (pass_name == "O") { + RegisterPerformancePasses(); + } else if (pass_name == "Os") { + RegisterSizePasses(); + } else if (pass_name == "legalize-hlsl") { + RegisterLegalizationPasses(); + } else if (pass_name == "generate-webgpu-initializers") { + RegisterPass(CreateGenerateWebGPUInitializersPass()); + } else if (pass_name == "legalize-vector-shuffle") { + RegisterPass(CreateLegalizeVectorShufflePass()); + } else if (pass_name == "split-invalid-unreachable") { + RegisterPass(CreateSplitInvalidUnreachablePass()); + } else if (pass_name == "decompose-initialized-variables") { + RegisterPass(CreateDecomposeInitializedVariablesPass()); + } else if (pass_name == "graphics-robust-access") { + RegisterPass(CreateGraphicsRobustAccessPass()); + } else if (pass_name == "wrap-opkill") { + RegisterPass(CreateWrapOpKillPass()); + } else if (pass_name == "amd-ext-to-khr") { + RegisterPass(CreateAmdExtToKhrPass()); + } else { + Errorf(consumer(), nullptr, {}, + "Unknown flag '--%s'. Use --help for a list of valid flags", + pass_name.c_str()); + return false; + } + + return true; +} + +void Optimizer::SetTargetEnv(const spv_target_env env) { + impl_->target_env = env; +} + +bool Optimizer::Run(const uint32_t* original_binary, + const size_t original_binary_size, + std::vector* optimized_binary) const { + return Run(original_binary, original_binary_size, optimized_binary, + OptimizerOptions()); +} + +bool Optimizer::Run(const uint32_t* original_binary, + const size_t original_binary_size, + std::vector* optimized_binary, + const ValidatorOptions& validator_options, + bool skip_validation) const { + OptimizerOptions opt_options; + opt_options.set_run_validator(!skip_validation); + opt_options.set_validator_options(validator_options); + return Run(original_binary, original_binary_size, optimized_binary, + opt_options); +} + +bool Optimizer::Run(const uint32_t* original_binary, + const size_t original_binary_size, + std::vector* optimized_binary, + const spv_optimizer_options opt_options) const { + spvtools::SpirvTools tools(impl_->target_env); + tools.SetMessageConsumer(impl_->pass_manager.consumer()); + if (opt_options->run_validator_ && + !tools.Validate(original_binary, original_binary_size, + &opt_options->val_options_)) { + return false; + } + + std::unique_ptr context = BuildModule( + impl_->target_env, consumer(), original_binary, original_binary_size); + if (context == nullptr) return false; + + context->set_max_id_bound(opt_options->max_id_bound_); + context->set_preserve_bindings(opt_options->preserve_bindings_); + context->set_preserve_spec_constants(opt_options->preserve_spec_constants_); + + impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_); + impl_->pass_manager.SetTargetEnv(impl_->target_env); + auto status = impl_->pass_manager.Run(context.get()); + + if (status == opt::Pass::Status::Failure) { + return false; + } + +#ifndef NDEBUG + // We do not keep the result id of DebugScope in struct DebugScope. + // Instead, we assign random ids for them, which results in integrity + // check failures. We want to skip the integrity check when the module + // contains DebugScope instructions. + if (status == opt::Pass::Status::SuccessWithoutChange && + !context->module()->ContainsDebugScope()) { + std::vector optimized_binary_with_nop; + context->module()->ToBinary(&optimized_binary_with_nop, + /* skip_nop = */ false); + assert(optimized_binary_with_nop.size() == original_binary_size && + "Binary size unexpectedly changed despite the optimizer saying " + "there was no change"); + assert(memcmp(optimized_binary_with_nop.data(), original_binary, + original_binary_size) == 0 && + "Binary content unexpectedly changed despite the optimizer saying " + "there was no change"); + } +#endif // !NDEBUG + + // Note that |original_binary| and |optimized_binary| may share the same + // buffer and the below will invalidate |original_binary|. + optimized_binary->clear(); + context->module()->ToBinary(optimized_binary, /* skip_nop = */ true); + + return true; +} + +Optimizer& Optimizer::SetPrintAll(std::ostream* out) { + impl_->pass_manager.SetPrintAll(out); + return *this; +} + +Optimizer& Optimizer::SetTimeReport(std::ostream* out) { + impl_->pass_manager.SetTimeReport(out); + return *this; +} + +Optimizer& Optimizer::SetValidateAfterAll(bool validate) { + impl_->pass_manager.SetValidateAfterAll(validate); + return *this; +} + +Optimizer::PassToken CreateNullPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateStripAtomicCounterMemoryPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateStripDebugInfoPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateStripReflectInfoPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateEliminateDeadFunctionsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateEliminateDeadMembersPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map& id_value_map) { + return MakeUnique( + MakeUnique(id_value_map)); +} + +Optimizer::PassToken CreateSetSpecConstantDefaultValuePass( + const std::unordered_map>& id_value_map) { + return MakeUnique( + MakeUnique(id_value_map)); +} + +Optimizer::PassToken CreateFlattenDecorationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateFreezeSpecConstantValuePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateUnifyConstantPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateEliminateDeadConstantPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDeadVariableEliminationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateStrengthReductionPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateBlockMergePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateInlineExhaustivePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateInlineOpaquePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalAccessChainConvertPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalSingleStoreElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateInsertExtractElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDeadInsertElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDeadBranchElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalMultiStoreElimPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateAggressiveDCEPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreatePropagateLineInfoPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateRedundantLineInfoElimPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateCompactIdsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateMergeReturnPass() { + return MakeUnique( + MakeUnique()); +} + +std::vector Optimizer::GetPassNames() const { + std::vector v; + for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) { + v.push_back(impl_->pass_manager.GetPass(i)->name()); + } + return v; +} + +Optimizer::PassToken CreateCFGCleanupPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLocalRedundancyEliminationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLoopFissionPass(size_t threshold) { + return MakeUnique( + MakeUnique(threshold)); +} + +Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) { + return MakeUnique( + MakeUnique(max_registers_per_loop)); +} + +Optimizer::PassToken CreateLoopInvariantCodeMotionPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateLoopPeelingPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLoopUnswitchPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateRedundancyEliminationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateRemoveDuplicatesPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) { + return MakeUnique( + MakeUnique(size_limit)); +} + +Optimizer::PassToken CreatePrivateToLocalPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateCCPPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateWorkaround1209Pass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateIfConversionPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateReplaceInvalidOpcodePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateSimplificationPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) { + return MakeUnique( + MakeUnique(fully_unroll, factor)); +} + +Optimizer::PassToken CreateSSARewritePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateCopyPropagateArraysPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateVectorDCEPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateReduceLoadSizePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateCombineAccessChainsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateUpgradeMemoryModelPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set, + uint32_t shader_id, + bool input_length_enable, + bool input_init_enable, + bool input_buff_oob_enable) { + return MakeUnique( + MakeUnique( + desc_set, shader_id, input_length_enable, input_init_enable, + input_buff_oob_enable)); +} + +Optimizer::PassToken CreateInstDebugPrintfPass(uint32_t desc_set, + uint32_t shader_id) { + return MakeUnique( + MakeUnique(desc_set, shader_id)); +} + +Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set, + uint32_t shader_id) { + return MakeUnique( + MakeUnique(desc_set, shader_id)); +} + +Optimizer::PassToken CreateConvertRelaxedToHalfPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateRelaxFloatOpsPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateCodeSinkingPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateGenerateWebGPUInitializersPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateFixStorageClassPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateLegalizeVectorShufflePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDecomposeInitializedVariablesPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateSplitInvalidUnreachablePass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateGraphicsRobustAccessPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateDescriptorScalarReplacementPass() { + return MakeUnique( + MakeUnique()); +} + +Optimizer::PassToken CreateWrapOpKillPass() { + return MakeUnique(MakeUnique()); +} + +Optimizer::PassToken CreateAmdExtToKhrPass() { + return MakeUnique( + MakeUnique()); +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/pass.cpp b/third_party/spirv-tools/source/opt/pass.cpp new file mode 100644 index 0000000..09b78af --- /dev/null +++ b/third_party/spirv-tools/source/opt/pass.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2017 The Khronos Group Inc. +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/pass.h" + +#include "source/opt/ir_builder.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +namespace { + +const uint32_t kTypePointerTypeIdInIdx = 1; + +} // namespace + +Pass::Pass() : consumer_(nullptr), context_(nullptr), already_run_(false) {} + +Pass::Status Pass::Run(IRContext* ctx) { + if (already_run_) { + return Status::Failure; + } + already_run_ = true; + + context_ = ctx; + Pass::Status status = Process(); + context_ = nullptr; + + if (status == Status::SuccessWithChange) { + ctx->InvalidateAnalysesExceptFor(GetPreservedAnalyses()); + } + assert((status == Status::Failure || ctx->IsConsistent()) && + "An analysis in the context is out of date."); + return status; +} + +uint32_t Pass::GetPointeeTypeId(const Instruction* ptrInst) const { + const uint32_t ptrTypeId = ptrInst->type_id(); + const Instruction* ptrTypeInst = get_def_use_mgr()->GetDef(ptrTypeId); + return ptrTypeInst->GetSingleWordInOperand(kTypePointerTypeIdInIdx); +} + +Instruction* Pass::GetBaseType(uint32_t ty_id) { + Instruction* ty_inst = get_def_use_mgr()->GetDef(ty_id); + if (ty_inst->opcode() == SpvOpTypeMatrix) { + uint32_t vty_id = ty_inst->GetSingleWordInOperand(0); + ty_inst = get_def_use_mgr()->GetDef(vty_id); + } + if (ty_inst->opcode() == SpvOpTypeVector) { + uint32_t cty_id = ty_inst->GetSingleWordInOperand(0); + ty_inst = get_def_use_mgr()->GetDef(cty_id); + } + return ty_inst; +} + +bool Pass::IsFloat(uint32_t ty_id, uint32_t width) { + Instruction* ty_inst = GetBaseType(ty_id); + if (ty_inst->opcode() != SpvOpTypeFloat) return false; + return ty_inst->GetSingleWordInOperand(0) == width; +} + +uint32_t Pass::GetNullId(uint32_t type_id) { + if (IsFloat(type_id, 16)) context()->AddCapability(SpvCapabilityFloat16); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + const analysis::Type* type = type_mgr->GetType(type_id); + const analysis::Constant* null_const = const_mgr->GetConstant(type, {}); + Instruction* null_inst = + const_mgr->GetDefiningInstruction(null_const, type_id); + return null_inst->result_id(); +} + +uint32_t Pass::GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id, + Instruction* insertion_position) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + uint32_t original_type_id = object_to_copy->type_id(); + if (original_type_id == new_type_id) { + return object_to_copy->result_id(); + } + + InstructionBuilder ir_builder( + context(), insertion_position, + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse); + + analysis::Type* original_type = type_mgr->GetType(original_type_id); + analysis::Type* new_type = type_mgr->GetType(new_type_id); + + if (const analysis::Array* original_array_type = original_type->AsArray()) { + uint32_t original_element_type_id = + type_mgr->GetId(original_array_type->element_type()); + + analysis::Array* new_array_type = new_type->AsArray(); + assert(new_array_type != nullptr && "Can't copy an array to a non-array."); + uint32_t new_element_type_id = + type_mgr->GetId(new_array_type->element_type()); + + std::vector element_ids; + const analysis::Constant* length_const = + const_mgr->FindDeclaredConstant(original_array_type->LengthId()); + assert(length_const->AsIntConstant()); + uint32_t array_length = length_const->AsIntConstant()->GetU32(); + for (uint32_t i = 0; i < array_length; i++) { + Instruction* extract = ir_builder.AddCompositeExtract( + original_element_type_id, object_to_copy->result_id(), {i}); + element_ids.push_back( + GenerateCopy(extract, new_element_type_id, insertion_position)); + } + + return ir_builder.AddCompositeConstruct(new_type_id, element_ids) + ->result_id(); + } else if (const analysis::Struct* original_struct_type = + original_type->AsStruct()) { + analysis::Struct* new_struct_type = new_type->AsStruct(); + + const std::vector& original_types = + original_struct_type->element_types(); + const std::vector& new_types = + new_struct_type->element_types(); + std::vector element_ids; + for (uint32_t i = 0; i < original_types.size(); i++) { + Instruction* extract = ir_builder.AddCompositeExtract( + type_mgr->GetId(original_types[i]), object_to_copy->result_id(), {i}); + element_ids.push_back(GenerateCopy(extract, type_mgr->GetId(new_types[i]), + insertion_position)); + } + return ir_builder.AddCompositeConstruct(new_type_id, element_ids) + ->result_id(); + } else { + // If we do not have an aggregate type, then we have a problem. Either we + // found multiple instances of the same type, or we are copying to an + // incompatible type. Either way the code is illegal. + assert(false && + "Don't know how to copy this type. Code is likely illegal."); + } + return 0; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/pass.h b/third_party/spirv-tools/source/opt/pass.h new file mode 100644 index 0000000..a8c9c4b --- /dev/null +++ b/third_party/spirv-tools/source/opt/pass.h @@ -0,0 +1,164 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PASS_H_ +#define SOURCE_OPT_PASS_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "spirv-tools/libspirv.hpp" +#include "types.h" + +namespace spvtools { +namespace opt { + +// Abstract class of a pass. All passes should implement this abstract class +// and all analysis and transformation is done via the Process() method. +class Pass { + public: + // The status of processing a module using a pass. + // + // The numbers for the cases are assigned to make sure that Failure & anything + // is Failure, SuccessWithChange & any success is SuccessWithChange. + enum class Status { + Failure = 0x00, + SuccessWithChange = 0x10, + SuccessWithoutChange = 0x11, + }; + + using ProcessFunction = std::function; + + // Destructs the pass. + virtual ~Pass() = default; + + // Returns a descriptive name for this pass. + // + // NOTE: When deriving a new pass class, make sure you make the name + // compatible with the corresponding spirv-opt command-line flag. For example, + // if you add the flag --my-pass to spirv-opt, make this function return + // "my-pass" (no leading hyphens). + virtual const char* name() const = 0; + + // Sets the message consumer to the given |consumer|. |consumer| which will be + // invoked every time there is a message to be communicated to the outside. + void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); } + + // Returns the reference to the message consumer for this pass. + const MessageConsumer& consumer() const { return consumer_; } + + // Returns the def-use manager used for this pass. TODO(dnovillo): This should + // be handled by the pass manager. + analysis::DefUseManager* get_def_use_mgr() const { + return context()->get_def_use_mgr(); + } + + analysis::DecorationManager* get_decoration_mgr() const { + return context()->get_decoration_mgr(); + } + + FeatureManager* get_feature_mgr() const { + return context()->get_feature_mgr(); + } + + // Returns a pointer to the current module for this pass. + Module* get_module() const { return context_->module(); } + + // Sets the pointer to the current context for this pass. + void SetContextForTesting(IRContext* ctx) { context_ = ctx; } + + // Returns a pointer to the current context for this pass. + IRContext* context() const { return context_; } + + // Returns a pointer to the CFG for current module. + CFG* cfg() const { return context()->cfg(); } + + // Run the pass on the given |module|. Returns Status::Failure if errors occur + // when processing. Returns the corresponding Status::Success if processing is + // successful to indicate whether changes are made to the module. If there + // were any changes it will also invalidate the analyses in the IRContext + // that are not preserved. + // + // It is an error if |Run| is called twice with the same instance of the pass. + // If this happens the return value will be |Failure|. + Status Run(IRContext* ctx); + + // Returns the set of analyses that the pass is guaranteed to preserve. + virtual IRContext::Analysis GetPreservedAnalyses() { + return IRContext::kAnalysisNone; + } + + // Return type id for |ptrInst|'s pointee + uint32_t GetPointeeTypeId(const Instruction* ptrInst) const; + + // Return base type of |ty_id| type + Instruction* GetBaseType(uint32_t ty_id); + + // Return true if |inst| returns scalar, vector or matrix type with base + // float and |width| + bool IsFloat(uint32_t ty_id, uint32_t width); + + // Return the id of OpConstantNull of type |type_id|. Create if necessary. + uint32_t GetNullId(uint32_t type_id); + + protected: + // Constructs a new pass. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + Pass(); + + // Processes the given |module|. Returns Status::Failure if errors occur when + // processing. Returns the corresponding Status::Success if processing is + // succesful to indicate whether changes are made to the module. + virtual Status Process() = 0; + + // Return the next available SSA id and increment it. + // TODO(1841): Handle id overflow. + uint32_t TakeNextId() { return context_->TakeNextId(); } + + // Returns the id whose value is the same as |object_to_copy| except its type + // is |new_type_id|. Any instructions needed to generate this value will be + // inserted before |insertion_position|. + uint32_t GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id, + Instruction* insertion_position); + + private: + MessageConsumer consumer_; // Message consumer. + + // The context that this pass belongs to. + IRContext* context_; + + // An instance of a pass can only be run once because it is too hard to + // enforce proper resetting of internal state for each instance. This member + // is used to check that we do not run the same instance twice. + bool already_run_; +}; + +inline Pass::Status CombineStatus(Pass::Status a, Pass::Status b) { + return std::min(a, b); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/pass_manager.cpp b/third_party/spirv-tools/source/opt/pass_manager.cpp new file mode 100644 index 0000000..be53d34 --- /dev/null +++ b/third_party/spirv-tools/source/opt/pass_manager.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/pass_manager.h" + +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/util/timer.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { + +namespace opt { + +Pass::Status PassManager::Run(IRContext* context) { + auto status = Pass::Status::SuccessWithoutChange; + + // If print_all_stream_ is not null, prints the disassembly of the module + // to that stream, with the given preamble and optionally the pass name. + auto print_disassembly = [&context, this](const char* preamble, Pass* pass) { + if (print_all_stream_) { + std::vector binary; + context->module()->ToBinary(&binary, false); + SpirvTools t(SPV_ENV_UNIVERSAL_1_2); + std::string disassembly; + t.Disassemble(binary, &disassembly, 0); + *print_all_stream_ << preamble << (pass ? pass->name() : "") << "\n" + << disassembly << std::endl; + } + }; + + SPIRV_TIMER_DESCRIPTION(time_report_stream_, /* measure_mem_usage = */ true); + for (auto& pass : passes_) { + print_disassembly("; IR before pass ", pass.get()); + SPIRV_TIMER_SCOPED(time_report_stream_, (pass ? pass->name() : ""), true); + const auto one_status = pass->Run(context); + if (one_status == Pass::Status::Failure) return one_status; + if (one_status == Pass::Status::SuccessWithChange) status = one_status; + + if (validate_after_all_) { + spvtools::SpirvTools tools(target_env_); + tools.SetMessageConsumer(consumer()); + std::vector binary; + context->module()->ToBinary(&binary, true); + if (!tools.Validate(binary.data(), binary.size(), val_options_)) { + std::string msg = "Validation failed after pass "; + msg += pass->name(); + spv_position_t null_pos{0, 0, 0}; + consumer()(SPV_MSG_INTERNAL_ERROR, "", null_pos, msg.c_str()); + return Pass::Status::Failure; + } + } + + // Reset the pass to free any memory used by the pass. + pass.reset(nullptr); + } + print_disassembly("; IR after last pass", nullptr); + + // Set the Id bound in the header in case a pass forgot to do so. + // + // TODO(dnovillo): This should be unnecessary and automatically maintained by + // the IRContext. + if (status == Pass::Status::SuccessWithChange) { + context->module()->SetIdBound(context->module()->ComputeIdBound()); + } + passes_.clear(); + return status; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/pass_manager.h b/third_party/spirv-tools/source/opt/pass_manager.h new file mode 100644 index 0000000..9686ddd --- /dev/null +++ b/third_party/spirv-tools/source/opt/pass_manager.h @@ -0,0 +1,158 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PASS_MANAGER_H_ +#define SOURCE_OPT_PASS_MANAGER_H_ + +#include +#include +#include +#include + +#include "source/opt/log.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { + +// The pass manager, responsible for tracking and running passes. +// Clients should first call AddPass() to add passes and then call Run() +// to run on a module. Passes are executed in the exact order of addition. +class PassManager { + public: + // Constructs a pass manager. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + PassManager() + : consumer_(nullptr), + print_all_stream_(nullptr), + time_report_stream_(nullptr), + target_env_(SPV_ENV_UNIVERSAL_1_2), + val_options_(nullptr), + validate_after_all_(false) {} + + // Sets the message consumer to the given |consumer|. + void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); } + + // Adds an externally constructed pass. + void AddPass(std::unique_ptr pass); + // Uses the argument |args| to construct a pass instance of type |T|, and adds + // the pass instance to this pass manger. The pass added will use this pass + // manager's message consumer. + template + void AddPass(Args&&... args); + + // Returns the number of passes added. + uint32_t NumPasses() const; + // Returns a pointer to the |index|th pass added. + inline Pass* GetPass(uint32_t index) const; + + // Returns the message consumer. + inline const MessageConsumer& consumer() const; + + // Runs all passes on the given |module|. Returns Status::Failure if errors + // occur when processing using one of the registered passes. All passes + // registered after the error-reporting pass will be skipped. Returns the + // corresponding Status::Success if processing is succesful to indicate + // whether changes are made to the module. + // + // After running all the passes, they are removed from the list. + Pass::Status Run(IRContext* context); + + // Sets the option to print the disassembly before each pass and after the + // last pass. Output is written to |out| if that is not null. No output + // is generated if |out| is null. + PassManager& SetPrintAll(std::ostream* out) { + print_all_stream_ = out; + return *this; + } + + // Sets the option to print the resource utilization of each pass. Output is + // written to |out| if that is not null. No output is generated if |out| is + // null. + PassManager& SetTimeReport(std::ostream* out) { + time_report_stream_ = out; + return *this; + } + + // Sets the target environment for validation. + PassManager& SetTargetEnv(spv_target_env env) { + target_env_ = env; + return *this; + } + + // Sets the validation options. + PassManager& SetValidatorOptions(spv_validator_options options) { + val_options_ = options; + return *this; + } + + // Sets the option to validate after each pass. + PassManager& SetValidateAfterAll(bool validate) { + validate_after_all_ = validate; + return *this; + } + + private: + // Consumer for messages. + MessageConsumer consumer_; + // A vector of passes. Order matters. + std::vector> passes_; + // The output stream to write disassembly to before each pass, and after + // the last pass. If this is null, no output is generated. + std::ostream* print_all_stream_; + // The output stream to write the resource utilization of each pass. If this + // is null, no output is generated. + std::ostream* time_report_stream_; + // The target environment. + spv_target_env target_env_; + // The validator options (used when validating each pass). + spv_validator_options val_options_; + // Controls whether validation occurs after every pass. + bool validate_after_all_; +}; + +inline void PassManager::AddPass(std::unique_ptr pass) { + passes_.push_back(std::move(pass)); +} + +template +inline void PassManager::AddPass(Args&&... args) { + passes_.emplace_back(new T(std::forward(args)...)); + passes_.back()->SetMessageConsumer(consumer_); +} + +inline uint32_t PassManager::NumPasses() const { + return static_cast(passes_.size()); +} + +inline Pass* PassManager::GetPass(uint32_t index) const { + SPIRV_ASSERT(consumer_, index < passes_.size(), "index out of bound"); + return passes_[index].get(); +} + +inline const MessageConsumer& PassManager::consumer() const { + return consumer_; +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_PASS_MANAGER_H_ diff --git a/third_party/spirv-tools/source/opt/passes.h b/third_party/spirv-tools/source/opt/passes.h new file mode 100644 index 0000000..d47cc1c --- /dev/null +++ b/third_party/spirv-tools/source/opt/passes.h @@ -0,0 +1,85 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PASSES_H_ +#define SOURCE_OPT_PASSES_H_ + +// A single header to include all passes. + +#include "source/opt/aggressive_dead_code_elim_pass.h" +#include "source/opt/amd_ext_to_khr.h" +#include "source/opt/block_merge_pass.h" +#include "source/opt/ccp_pass.h" +#include "source/opt/cfg_cleanup_pass.h" +#include "source/opt/code_sink.h" +#include "source/opt/combine_access_chains.h" +#include "source/opt/compact_ids_pass.h" +#include "source/opt/convert_to_half_pass.h" +#include "source/opt/copy_prop_arrays.h" +#include "source/opt/dead_branch_elim_pass.h" +#include "source/opt/dead_insert_elim_pass.h" +#include "source/opt/dead_variable_elimination.h" +#include "source/opt/decompose_initialized_variables_pass.h" +#include "source/opt/desc_sroa.h" +#include "source/opt/eliminate_dead_constant_pass.h" +#include "source/opt/eliminate_dead_functions_pass.h" +#include "source/opt/eliminate_dead_members_pass.h" +#include "source/opt/empty_pass.h" +#include "source/opt/fix_storage_class.h" +#include "source/opt/flatten_decoration_pass.h" +#include "source/opt/fold_spec_constant_op_and_composite_pass.h" +#include "source/opt/freeze_spec_constant_value_pass.h" +#include "source/opt/generate_webgpu_initializers_pass.h" +#include "source/opt/graphics_robust_access_pass.h" +#include "source/opt/if_conversion.h" +#include "source/opt/inline_exhaustive_pass.h" +#include "source/opt/inline_opaque_pass.h" +#include "source/opt/inst_bindless_check_pass.h" +#include "source/opt/inst_buff_addr_check_pass.h" +#include "source/opt/inst_debug_printf_pass.h" +#include "source/opt/legalize_vector_shuffle_pass.h" +#include "source/opt/licm_pass.h" +#include "source/opt/local_access_chain_convert_pass.h" +#include "source/opt/local_redundancy_elimination.h" +#include "source/opt/local_single_block_elim_pass.h" +#include "source/opt/local_single_store_elim_pass.h" +#include "source/opt/loop_fission.h" +#include "source/opt/loop_fusion_pass.h" +#include "source/opt/loop_peeling.h" +#include "source/opt/loop_unroller.h" +#include "source/opt/loop_unswitch_pass.h" +#include "source/opt/merge_return_pass.h" +#include "source/opt/null_pass.h" +#include "source/opt/private_to_local_pass.h" +#include "source/opt/reduce_load_size.h" +#include "source/opt/redundancy_elimination.h" +#include "source/opt/relax_float_ops_pass.h" +#include "source/opt/remove_duplicates_pass.h" +#include "source/opt/replace_invalid_opc.h" +#include "source/opt/scalar_replacement_pass.h" +#include "source/opt/set_spec_constant_default_value_pass.h" +#include "source/opt/simplification_pass.h" +#include "source/opt/split_invalid_unreachable_pass.h" +#include "source/opt/ssa_rewrite_pass.h" +#include "source/opt/strength_reduction_pass.h" +#include "source/opt/strip_atomic_counter_memory_pass.h" +#include "source/opt/strip_debug_info_pass.h" +#include "source/opt/strip_reflect_info_pass.h" +#include "source/opt/unify_const_pass.h" +#include "source/opt/upgrade_memory_model.h" +#include "source/opt/vector_dce.h" +#include "source/opt/workaround1209.h" +#include "source/opt/wrap_opkill.h" + +#endif // SOURCE_OPT_PASSES_H_ diff --git a/third_party/spirv-tools/source/opt/pch_source_opt.cpp b/third_party/spirv-tools/source/opt/pch_source_opt.cpp new file mode 100644 index 0000000..f45448d --- /dev/null +++ b/third_party/spirv-tools/source/opt/pch_source_opt.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_source_opt.h" diff --git a/third_party/spirv-tools/source/opt/pch_source_opt.h b/third_party/spirv-tools/source/opt/pch_source_opt.h new file mode 100644 index 0000000..7356651 --- /dev/null +++ b/third_party/spirv-tools/source/opt/pch_source_opt.h @@ -0,0 +1,32 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "source/opt/basic_block.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/mem_pass.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "source/util/hex_float.h" +#include "source/util/make_unique.h" diff --git a/third_party/spirv-tools/source/opt/private_to_local_pass.cpp b/third_party/spirv-tools/source/opt/private_to_local_pass.cpp new file mode 100644 index 0000000..dd6cbbd --- /dev/null +++ b/third_party/spirv-tools/source/opt/private_to_local_pass.cpp @@ -0,0 +1,239 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/private_to_local_pass.h" + +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/spirv_constant.h" + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kVariableStorageClassInIdx = 0; +const uint32_t kSpvTypePointerTypeIdInIdx = 1; + +} // namespace + +Pass::Status PrivateToLocalPass::Process() { + bool modified = false; + + // Private variables require the shader capability. If this is not a shader, + // there is no work to do. + if (context()->get_feature_mgr()->HasCapability(SpvCapabilityAddresses)) + return Status::SuccessWithoutChange; + + std::vector> variables_to_move; + std::unordered_set localized_variables; + for (auto& inst : context()->types_values()) { + if (inst.opcode() != SpvOpVariable) { + continue; + } + + if (inst.GetSingleWordInOperand(kVariableStorageClassInIdx) != + SpvStorageClassPrivate) { + continue; + } + + Function* target_function = FindLocalFunction(inst); + if (target_function != nullptr) { + variables_to_move.push_back({&inst, target_function}); + } + } + + modified = !variables_to_move.empty(); + for (auto p : variables_to_move) { + if (!MoveVariable(p.first, p.second)) { + return Status::Failure; + } + localized_variables.insert(p.first->result_id()); + } + + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // In SPIR-V 1.4 and later entry points must list private storage class + // variables that are statically used by the entry point. Go through the + // entry points and remove any references to variables that were localized. + for (auto& entry : get_module()->entry_points()) { + std::vector new_operands; + for (uint32_t i = 0; i < entry.NumInOperands(); ++i) { + // Execution model, function id and name are always kept. + if (i < 3 || + !localized_variables.count(entry.GetSingleWordInOperand(i))) { + new_operands.push_back(entry.GetInOperand(i)); + } + } + if (new_operands.size() != entry.NumInOperands()) { + entry.SetInOperands(std::move(new_operands)); + context()->AnalyzeUses(&entry); + } + } + } + + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +Function* PrivateToLocalPass::FindLocalFunction(const Instruction& inst) const { + bool found_first_use = false; + Function* target_function = nullptr; + context()->get_def_use_mgr()->ForEachUser( + inst.result_id(), + [&target_function, &found_first_use, this](Instruction* use) { + BasicBlock* current_block = context()->get_instr_block(use); + if (current_block == nullptr) { + return; + } + + if (!IsValidUse(use)) { + found_first_use = true; + target_function = nullptr; + return; + } + Function* current_function = current_block->GetParent(); + if (!found_first_use) { + found_first_use = true; + target_function = current_function; + } else if (target_function != current_function) { + target_function = nullptr; + } + }); + return target_function; +} // namespace opt + +bool PrivateToLocalPass::MoveVariable(Instruction* variable, + Function* function) { + // The variable needs to be removed from the global section, and placed in the + // header of the function. First step remove from the global list. + variable->RemoveFromList(); + std::unique_ptr var(variable); // Take ownership. + context()->ForgetUses(variable); + + // Update the storage class of the variable. + variable->SetInOperand(kVariableStorageClassInIdx, {SpvStorageClassFunction}); + + // Update the type as well. + uint32_t new_type_id = GetNewType(variable->type_id()); + if (new_type_id == 0) { + return false; + } + variable->SetResultType(new_type_id); + + // Place the variable at the start of the first basic block. + context()->AnalyzeUses(variable); + context()->set_instr_block(variable, &*function->begin()); + function->begin()->begin()->InsertBefore(move(var)); + + // Update uses where the type may have changed. + return UpdateUses(variable); +} + +uint32_t PrivateToLocalPass::GetNewType(uint32_t old_type_id) { + auto type_mgr = context()->get_type_mgr(); + Instruction* old_type_inst = get_def_use_mgr()->GetDef(old_type_id); + uint32_t pointee_type_id = + old_type_inst->GetSingleWordInOperand(kSpvTypePointerTypeIdInIdx); + uint32_t new_type_id = + type_mgr->FindPointerToType(pointee_type_id, SpvStorageClassFunction); + if (new_type_id != 0) { + context()->UpdateDefUse(context()->get_def_use_mgr()->GetDef(new_type_id)); + } + return new_type_id; +} + +bool PrivateToLocalPass::IsValidUse(const Instruction* inst) const { + // The cases in this switch have to match the cases in |UpdateUse|. + // If we don't know how to update it, it is not valid. + if (inst->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugGlobalVariable) { + return true; + } + switch (inst->opcode()) { + case SpvOpLoad: + case SpvOpStore: + case SpvOpImageTexelPointer: // Treat like a load + return true; + case SpvOpAccessChain: + return context()->get_def_use_mgr()->WhileEachUser( + inst, [this](const Instruction* user) { + if (!IsValidUse(user)) return false; + return true; + }); + case SpvOpName: + return true; + default: + return spvOpcodeIsDecoration(inst->opcode()); + } +} + +bool PrivateToLocalPass::UpdateUse(Instruction* inst, Instruction* user) { + // The cases in this switch have to match the cases in |IsValidUse|. If we + // don't think it is valid, the optimization will not view the variable as a + // candidate, and therefore the use will not be updated. + if (inst->GetOpenCL100DebugOpcode() == + OpenCLDebugInfo100DebugGlobalVariable) { + context()->get_debug_info_mgr()->ConvertDebugGlobalToLocalVariable(inst, + user); + return true; + } + switch (inst->opcode()) { + case SpvOpLoad: + case SpvOpStore: + case SpvOpImageTexelPointer: // Treat like a load + // The type is fine because it is the type pointed to, and that does not + // change. + break; + case SpvOpAccessChain: { + context()->ForgetUses(inst); + uint32_t new_type_id = GetNewType(inst->type_id()); + if (new_type_id == 0) { + return false; + } + inst->SetResultType(new_type_id); + context()->AnalyzeUses(inst); + + // Update uses where the type may have changed. + if (!UpdateUses(inst)) { + return false; + } + } break; + case SpvOpName: + case SpvOpEntryPoint: // entry points will be updated separately. + break; + default: + assert(spvOpcodeIsDecoration(inst->opcode()) && + "Do not know how to update the type for this instruction."); + break; + } + return true; +} + +bool PrivateToLocalPass::UpdateUses(Instruction* inst) { + uint32_t id = inst->result_id(); + std::vector uses; + context()->get_def_use_mgr()->ForEachUser( + id, [&uses](Instruction* use) { uses.push_back(use); }); + + for (Instruction* use : uses) { + if (!UpdateUse(use, inst)) { + return false; + } + } + return true; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/private_to_local_pass.h b/third_party/spirv-tools/source/opt/private_to_local_pass.h new file mode 100644 index 0000000..c6127d6 --- /dev/null +++ b/third_party/spirv-tools/source/opt/private_to_local_pass.h @@ -0,0 +1,73 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PRIVATE_TO_LOCAL_PASS_H_ +#define SOURCE_OPT_PRIVATE_TO_LOCAL_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass implements total redundancy elimination. This is the same as +// local redundancy elimination except it looks across basic block boundaries. +// An instruction, inst, is totally redundant if there is another instruction +// that dominates inst, and also computes the same value. +class PrivateToLocalPass : public Pass { + public: + const char* name() const override { return "private-to-local"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Moves |variable| from the private storage class to the function storage + // class of |function|. Returns false if the variable could not be moved. + bool MoveVariable(Instruction* variable, Function* function); + + // |inst| is an instruction declaring a varible. If that variable is + // referenced in a single function and all of uses are valid as defined by + // |IsValidUse|, then that function is returned. Otherwise, the return + // value is |nullptr|. + Function* FindLocalFunction(const Instruction& inst) const; + + // Returns true is |inst| is a valid use of a pointer. In this case, a + // valid use is one where the transformation is able to rewrite the type to + // match a change in storage class of the original variable. + bool IsValidUse(const Instruction* inst) const; + + // Given the result id of a pointer type, |old_type_id|, this function + // returns the id of a the same pointer type except the storage class has + // been changed to function. If the type does not already exist, it will be + // created. Returns 0 if the new type could not be found or generated. + uint32_t GetNewType(uint32_t old_type_id); + + // Updates |inst|, and any instruction dependent on |inst|, to reflect the + // change of the base pointer now pointing to the function storage class. + bool UpdateUse(Instruction* inst, Instruction* user); + bool UpdateUses(Instruction* inst); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_PRIVATE_TO_LOCAL_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/propagator.cpp b/third_party/spirv-tools/source/opt/propagator.cpp new file mode 100644 index 0000000..6a1f1aa --- /dev/null +++ b/third_party/spirv-tools/source/opt/propagator.cpp @@ -0,0 +1,291 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/propagator.h" + +namespace spvtools { +namespace opt { + +void SSAPropagator::AddControlEdge(const Edge& edge) { + BasicBlock* dest_bb = edge.dest; + + // Refuse to add the exit block to the work list. + if (dest_bb == ctx_->cfg()->pseudo_exit_block()) { + return; + } + + // Try to mark the edge executable. If it was already in the set of + // executable edges, do nothing. + if (!MarkEdgeExecutable(edge)) { + return; + } + + // If the edge had not already been marked executable, add the destination + // basic block to the work list. + blocks_.push(dest_bb); +} + +void SSAPropagator::AddSSAEdges(Instruction* instr) { + // Ignore instructions that produce no result. + if (instr->result_id() == 0) { + return; + } + + get_def_use_mgr()->ForEachUser( + instr->result_id(), [this](Instruction* use_instr) { + // If the basic block for |use_instr| has not been simulated yet, do + // nothing. The instruction |use_instr| will be simulated next time the + // block is scheduled. + if (!BlockHasBeenSimulated(ctx_->get_instr_block(use_instr))) { + return; + } + + if (ShouldSimulateAgain(use_instr)) { + ssa_edge_uses_.push(use_instr); + } + }); +} + +bool SSAPropagator::IsPhiArgExecutable(Instruction* phi, uint32_t i) const { + BasicBlock* phi_bb = ctx_->get_instr_block(phi); + + uint32_t in_label_id = phi->GetSingleWordOperand(i + 1); + Instruction* in_label_instr = get_def_use_mgr()->GetDef(in_label_id); + BasicBlock* in_bb = ctx_->get_instr_block(in_label_instr); + + return IsEdgeExecutable(Edge(in_bb, phi_bb)); +} + +bool SSAPropagator::SetStatus(Instruction* inst, PropStatus status) { + bool has_old_status = false; + PropStatus old_status = kVarying; + if (HasStatus(inst)) { + has_old_status = true; + old_status = Status(inst); + } + + assert((!has_old_status || old_status <= status) && + "Invalid lattice transition"); + + bool status_changed = !has_old_status || (old_status != status); + if (status_changed) statuses_[inst] = status; + + return status_changed; +} + +bool SSAPropagator::Simulate(Instruction* instr) { + bool changed = false; + + // Don't bother visiting instructions that should not be simulated again. + if (!ShouldSimulateAgain(instr)) { + return changed; + } + + BasicBlock* dest_bb = nullptr; + PropStatus status = visit_fn_(instr, &dest_bb); + bool status_changed = SetStatus(instr, status); + + if (status == kVarying) { + // The statement produces a varying result, add it to the list of statements + // not to simulate anymore and add its SSA def-use edges for simulation. + DontSimulateAgain(instr); + if (status_changed) { + AddSSAEdges(instr); + } + + // If |instr| is a block terminator, add all the control edges out of its + // block. + if (instr->IsBlockTerminator()) { + BasicBlock* block = ctx_->get_instr_block(instr); + for (const auto& e : bb_succs_.at(block)) { + AddControlEdge(e); + } + } + return false; + } else if (status == kInteresting) { + // Add the SSA edges coming out of this instruction if the propagation + // status has changed. + if (status_changed) { + AddSSAEdges(instr); + } + + // If there are multiple outgoing control flow edges and we know which one + // will be taken, add the destination block to the CFG work list. + if (dest_bb) { + AddControlEdge(Edge(ctx_->get_instr_block(instr), dest_bb)); + } + changed = true; + } + + // At this point, we are dealing with instructions that are in status + // kInteresting or kNotInteresting. To decide whether this instruction should + // be simulated again, we examine its operands. If at least one operand O is + // defined at an instruction D that should be simulated again, then the output + // of D might affect |instr|, so we should simulate |instr| again. + bool has_operands_to_simulate = false; + if (instr->opcode() == SpvOpPhi) { + // For Phi instructions, an operand causes the Phi to be simulated again if + // the operand comes from an edge that has not yet been traversed or if its + // definition should be simulated again. + for (uint32_t i = 2; i < instr->NumOperands(); i += 2) { + // Phi arguments come in pairs. Index 'i' contains the + // variable id, index 'i + 1' is the originating block id. + assert(i % 2 == 0 && i < instr->NumOperands() - 1 && + "malformed Phi arguments"); + + uint32_t arg_id = instr->GetSingleWordOperand(i); + Instruction* arg_def_instr = get_def_use_mgr()->GetDef(arg_id); + if (!IsPhiArgExecutable(instr, i) || ShouldSimulateAgain(arg_def_instr)) { + has_operands_to_simulate = true; + break; + } + } + } else { + // For regular instructions, check if the defining instruction of each + // operand needs to be simulated again. If so, then this instruction should + // also be simulated again. + has_operands_to_simulate = + !instr->WhileEachInId([this](const uint32_t* use) { + Instruction* def_instr = get_def_use_mgr()->GetDef(*use); + if (ShouldSimulateAgain(def_instr)) { + return false; + } + return true; + }); + } + + if (!has_operands_to_simulate) { + DontSimulateAgain(instr); + } + + return changed; +} + +bool SSAPropagator::Simulate(BasicBlock* block) { + if (block == ctx_->cfg()->pseudo_exit_block()) { + return false; + } + + // Always simulate Phi instructions, even if we have simulated this block + // before. We do this because Phi instructions receive their inputs from + // incoming edges. When those edges are marked executable, the corresponding + // operand can be simulated. + bool changed = false; + block->ForEachPhiInst( + [&changed, this](Instruction* instr) { changed |= Simulate(instr); }); + + // If this is the first time this block is being simulated, simulate every + // statement in it. + if (!BlockHasBeenSimulated(block)) { + block->ForEachInst([this, &changed](Instruction* instr) { + if (instr->opcode() != SpvOpPhi) { + changed |= Simulate(instr); + } + }); + + MarkBlockSimulated(block); + + // If this block has exactly one successor, mark the edge to its successor + // as executable. + if (bb_succs_.at(block).size() == 1) { + AddControlEdge(bb_succs_.at(block).at(0)); + } + } + + return changed; +} + +void SSAPropagator::Initialize(Function* fn) { + // Compute predecessor and successor blocks for every block in |fn|'s CFG. + // TODO(dnovillo): Move this to CFG and always build them. Alternately, + // move it to IRContext and build CFG preds/succs on-demand. + bb_succs_[ctx_->cfg()->pseudo_entry_block()].push_back( + Edge(ctx_->cfg()->pseudo_entry_block(), fn->entry().get())); + + for (auto& block : *fn) { + const auto& const_block = block; + const_block.ForEachSuccessorLabel([this, &block](const uint32_t label_id) { + BasicBlock* succ_bb = + ctx_->get_instr_block(get_def_use_mgr()->GetDef(label_id)); + bb_succs_[&block].push_back(Edge(&block, succ_bb)); + bb_preds_[succ_bb].push_back(Edge(succ_bb, &block)); + }); + if (block.IsReturnOrAbort()) { + bb_succs_[&block].push_back( + Edge(&block, ctx_->cfg()->pseudo_exit_block())); + bb_preds_[ctx_->cfg()->pseudo_exit_block()].push_back( + Edge(ctx_->cfg()->pseudo_exit_block(), &block)); + } + } + + // Add the edges out of the entry block to seed the propagator. + const auto& entry_succs = bb_succs_[ctx_->cfg()->pseudo_entry_block()]; + for (const auto& e : entry_succs) { + AddControlEdge(e); + } +} + +bool SSAPropagator::Run(Function* fn) { + Initialize(fn); + + bool changed = false; + while (!blocks_.empty() || !ssa_edge_uses_.empty()) { + // Simulate all blocks first. Simulating blocks will add SSA edges to + // follow after all the blocks have been simulated. + if (!blocks_.empty()) { + auto block = blocks_.front(); + changed |= Simulate(block); + blocks_.pop(); + continue; + } + + // Simulate edges from the SSA queue. + if (!ssa_edge_uses_.empty()) { + Instruction* instr = ssa_edge_uses_.front(); + changed |= Simulate(instr); + ssa_edge_uses_.pop(); + } + } + +#ifndef NDEBUG + // Verify all visited values have settled. No value that has been simulated + // should end on not interesting. + fn->ForEachInst([this](Instruction* inst) { + assert( + (!HasStatus(inst) || Status(inst) != SSAPropagator::kNotInteresting) && + "Unsettled value"); + }); +#endif + + return changed; +} + +std::ostream& operator<<(std::ostream& str, + const SSAPropagator::PropStatus& status) { + switch (status) { + case SSAPropagator::kVarying: + str << "Varying"; + break; + case SSAPropagator::kInteresting: + str << "Interesting"; + break; + default: + str << "Not interesting"; + break; + } + return str; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/propagator.h b/third_party/spirv-tools/source/opt/propagator.h new file mode 100644 index 0000000..ac7c0e7 --- /dev/null +++ b/third_party/spirv-tools/source/opt/propagator.h @@ -0,0 +1,317 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_PROPAGATOR_H_ +#define SOURCE_OPT_PROPAGATOR_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" + +namespace spvtools { +namespace opt { + +// Represents a CFG control edge. +struct Edge { + Edge(BasicBlock* b1, BasicBlock* b2) : source(b1), dest(b2) { + assert(source && "CFG edges cannot have a null source block."); + assert(dest && "CFG edges cannot have a null destination block."); + } + BasicBlock* source; + BasicBlock* dest; + bool operator<(const Edge& o) const { + return std::make_pair(source->id(), dest->id()) < + std::make_pair(o.source->id(), o.dest->id()); + } +}; + +// This class implements a generic value propagation algorithm based on the +// conditional constant propagation algorithm proposed in +// +// Constant propagation with conditional branches, +// Wegman and Zadeck, ACM TOPLAS 13(2):181-210. +// +// A Propagation Engine for GCC +// Diego Novillo, GCC Summit 2005 +// http://ols.fedoraproject.org/GCC/Reprints-2005/novillo-Reprint.pdf +// +// The purpose of this implementation is to act as a common framework for any +// transformation that needs to propagate values from statements producing new +// values to statements using those values. Simulation proceeds as follows: +// +// 1- Initially, all edges of the CFG are marked not executable and the CFG +// worklist is seeded with all the statements in the entry basic block. +// +// 2- Every instruction I is simulated by calling a pass-provided function +// |visit_fn|. This function is responsible for three things: +// +// (a) Keep a value table of interesting values. This table maps SSA IDs to +// their values. For instance, when implementing constant propagation, +// given a store operation 'OpStore %f %int_3', |visit_fn| should assign +// the value 3 to the table slot for %f. +// +// In general, |visit_fn| will need to use the value table to replace its +// operands, fold the result and decide whether a new value needs to be +// stored in the table. |visit_fn| should only create a new mapping in +// the value table if all the operands in the instruction are known and +// present in the value table. +// +// (b) Return a status indicator to direct the propagator logic. Once the +// instruction is simulated, the propagator needs to know whether this +// instruction produced something interesting. This is indicated via +// |visit_fn|'s return value: +// +// SSAPropagator::kNotInteresting: Instruction I produces nothing of +// interest and does not affect any of the work lists. The +// propagator will visit the statement again if any of its operands +// produce an interesting value in the future. +// +// |visit_fn| should always return this value when it is not sure +// whether the instruction will produce an interesting value in the +// future or not. For instance, for constant propagation, an OpIAdd +// instruction may produce a constant if its two operands are +// constant, but the first time we visit the instruction, we still +// may not have its operands in the value table. +// +// SSAPropagator::kVarying: The value produced by I cannot be determined +// at compile time. Further simulation of I is not required. The +// propagator will not visit this instruction again. Additionally, +// the propagator will add all the instructions at the end of SSA +// def-use edges to be simulated again. +// +// If I is a basic block terminator, it will mark all outgoing edges +// as executable so they are traversed one more time. Eventually +// the kVarying attribute will be spread out to all the data and +// control dependents for I. +// +// It is important for propagation to use kVarying as a bottom value +// for the propagation lattice. It should never be possible for an +// instruction to return kVarying once and kInteresting on a second +// visit. Otherwise, propagation would not stabilize. +// +// SSAPropagator::kInteresting: Instruction I produces a value that can +// be computed at compile time. In this case, |visit_fn| should +// create a new mapping between I's result ID and the produced +// value. Much like the kNotInteresting case, the propagator will +// visit this instruction again if any of its operands changes. +// This is useful when the statement changes from one interesting +// state to another. +// +// (c) For conditional branches, |visit_fn| may decide which edge to take out +// of I's basic block. For example, if the operand for an OpSwitch is +// known to take a specific constant value, |visit_fn| should figure out +// the destination basic block and pass it back by setting the second +// argument to |visit_fn|. +// +// At the end of propagation, values in the value table are guaranteed to be +// stable and can be replaced in the IR. +// +// 3- The propagator keeps two work queues. Instructions are only added to +// these queues if they produce an interesting or varying value. None of this +// should be handled by |visit_fn|. The propagator keeps track of this +// automatically (see SSAPropagator::Simulate for implementation). +// +// CFG blocks: contains the queue of blocks to be simulated. +// Blocks are added to this queue if their incoming edges are +// executable. +// +// SSA Edges: An SSA edge is a def-use edge between a value-producing +// instruction and its use instruction. The SSA edges list +// contains the statements at the end of a def-use edge that need +// to be re-visited when an instruction produces a kVarying or +// kInteresting result. +// +// 4- Simulation terminates when all work queues are drained. +// +// +// EXAMPLE: Basic constant store propagator. +// +// Suppose we want to propagate all constant assignments of the form "OpStore +// %id %cst" where "%id" is some variable and "%cst" an OpConstant. The +// following code builds a table |values| where every id that was assigned a +// constant value is mapped to the constant value it was assigned. +// +// auto ctx = BuildModule(...); +// std::map values; +// const auto visit_fn = [&ctx, &values](Instruction* instr, +// BasicBlock** dest_bb) { +// if (instr->opcode() == SpvOpStore) { +// uint32_t rhs_id = instr->GetSingleWordOperand(1); +// Instruction* rhs_def = ctx->get_def_use_mgr()->GetDef(rhs_id); +// if (rhs_def->opcode() == SpvOpConstant) { +// uint32_t val = rhs_def->GetSingleWordOperand(2); +// values[rhs_id] = val; +// return SSAPropagator::kInteresting; +// } +// } +// return SSAPropagator::kVarying; +// }; +// SSAPropagator propagator(ctx.get(), &cfg, visit_fn); +// propagator.Run(&fn); +// +// Given the code: +// +// %int_4 = OpConstant %int 4 +// %int_3 = OpConstant %int 3 +// %int_1 = OpConstant %int 1 +// OpStore %x %int_4 +// OpStore %y %int_3 +// OpStore %z %int_1 +// +// After SSAPropagator::Run returns, the |values| map will contain the entries: +// values[%x] = 4, values[%y] = 3, and, values[%z] = 1. +class SSAPropagator { + public: + // Lattice values used for propagation. See class documentation for + // a description. + enum PropStatus { kNotInteresting, kInteresting, kVarying }; + + using VisitFunction = std::function; + + SSAPropagator(IRContext* context, const VisitFunction& visit_fn) + : ctx_(context), visit_fn_(visit_fn) {} + + // Runs the propagator on function |fn|. Returns true if changes were made to + // the function. Otherwise, it returns false. + bool Run(Function* fn); + + // Returns true if the |i|th argument for |phi| comes through a CFG edge that + // has been marked executable. |i| should be an index value accepted by + // Instruction::GetSingleWordOperand. + bool IsPhiArgExecutable(Instruction* phi, uint32_t i) const; + + // Returns true if |inst| has a recorded status. This will be true once |inst| + // has been simulated once. + bool HasStatus(Instruction* inst) const { return statuses_.count(inst); } + + // Returns the current propagation status of |inst|. Assumes + // |HasStatus(inst)| returns true. + PropStatus Status(Instruction* inst) const { + return statuses_.find(inst)->second; + } + + // Records the propagation status |status| for |inst|. Returns true if the + // status for |inst| has changed or set was set for the first time. + bool SetStatus(Instruction* inst, PropStatus status); + + private: + // Initialize processing. + void Initialize(Function* fn); + + // Simulate the execution |block| by calling |visit_fn_| on every instruction + // in it. + bool Simulate(BasicBlock* block); + + // Simulate the execution of |instr| by replacing all the known values in + // every operand and determining whether the result is interesting for + // propagation. This invokes the callback function |visit_fn_| to determine + // the value computed by |instr|. + bool Simulate(Instruction* instr); + + // Returns true if |instr| should be simulated again. + bool ShouldSimulateAgain(Instruction* instr) const { + return do_not_simulate_.find(instr) == do_not_simulate_.end(); + } + + // Add |instr| to the set of instructions not to simulate again. + void DontSimulateAgain(Instruction* instr) { do_not_simulate_.insert(instr); } + + // Returns true if |block| has been simulated already. + bool BlockHasBeenSimulated(BasicBlock* block) const { + return simulated_blocks_.find(block) != simulated_blocks_.end(); + } + + // Marks block |block| as simulated. + void MarkBlockSimulated(BasicBlock* block) { + simulated_blocks_.insert(block); + } + + // Marks |edge| as executable. Returns false if the edge was already marked + // as executable. + bool MarkEdgeExecutable(const Edge& edge) { + return executable_edges_.insert(edge).second; + } + + // Returns true if |edge| has been marked as executable. + bool IsEdgeExecutable(const Edge& edge) const { + return executable_edges_.find(edge) != executable_edges_.end(); + } + + // Returns a pointer to the def-use manager for |ctx_|. + analysis::DefUseManager* get_def_use_mgr() const { + return ctx_->get_def_use_mgr(); + } + + // If the CFG edge |e| has not been executed, this function adds |e|'s + // destination block to the work list. + void AddControlEdge(const Edge& e); + + // Adds all the instructions that use the result of |instr| to the SSA edges + // work list. If |instr| produces no result id, this does nothing. + void AddSSAEdges(Instruction* instr); + + // IR context to use. + IRContext* ctx_; + + // Function that visits instructions during simulation. The output of this + // function is used to determine if the simulated instruction produced a value + // interesting for propagation. The function is responsible for keeping + // track of interesting values by storing them in some user-provided map. + VisitFunction visit_fn_; + + // SSA def-use edges to traverse. Each entry is a destination statement for an + // SSA def-use edge as returned by |def_use_manager_|. + std::queue ssa_edge_uses_; + + // Blocks to simulate. + std::queue blocks_; + + // Blocks simulated during propagation. + std::unordered_set simulated_blocks_; + + // Set of instructions that should not be simulated again because they have + // been found to be in the kVarying state. + std::unordered_set do_not_simulate_; + + // Map between a basic block and its predecessor edges. + // TODO(dnovillo): Move this to CFG and always build them. Alternately, + // move it to IRContext and build CFG preds/succs on-demand. + std::unordered_map> bb_preds_; + + // Map between a basic block and its successor edges. + // TODO(dnovillo): Move this to CFG and always build them. Alternately, + // move it to IRContext and build CFG preds/succs on-demand. + std::unordered_map> bb_succs_; + + // Set of executable CFG edges. + std::set executable_edges_; + + // Tracks instruction propagation status. + std::unordered_map statuses_; +}; + +std::ostream& operator<<(std::ostream& str, + const SSAPropagator::PropStatus& status); + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_PROPAGATOR_H_ diff --git a/third_party/spirv-tools/source/opt/reduce_load_size.cpp b/third_party/spirv-tools/source/opt/reduce_load_size.cpp new file mode 100644 index 0000000..c58adf4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/reduce_load_size.cpp @@ -0,0 +1,183 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/reduce_load_size.h" + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/util/bit_vector.h" + +namespace { + +const uint32_t kExtractCompositeIdInIdx = 0; +const uint32_t kVariableStorageClassInIdx = 0; +const uint32_t kLoadPointerInIdx = 0; +const double kThreshold = 0.9; + +} // namespace + +namespace spvtools { +namespace opt { + +Pass::Status ReduceLoadSize::Process() { + bool modified = false; + + for (auto& func : *get_module()) { + func.ForEachInst([&modified, this](Instruction* inst) { + if (inst->opcode() == SpvOpCompositeExtract) { + if (ShouldReplaceExtract(inst)) { + modified |= ReplaceExtract(inst); + } + } + }); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool ReduceLoadSize::ReplaceExtract(Instruction* inst) { + assert(inst->opcode() == SpvOpCompositeExtract && + "Wrong opcode. Should be OpCompositeExtract."); + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + uint32_t composite_id = + inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* composite_inst = def_use_mgr->GetDef(composite_id); + + if (composite_inst->opcode() != SpvOpLoad) { + return false; + } + + analysis::Type* composite_type = type_mgr->GetType(composite_inst->type_id()); + if (composite_type->kind() == analysis::Type::kVector || + composite_type->kind() == analysis::Type::kMatrix) { + return false; + } + + Instruction* var = composite_inst->GetBaseAddress(); + if (var == nullptr || var->opcode() != SpvOpVariable) { + return false; + } + + SpvStorageClass storage_class = static_cast( + var->GetSingleWordInOperand(kVariableStorageClassInIdx)); + switch (storage_class) { + case SpvStorageClassUniform: + case SpvStorageClassUniformConstant: + case SpvStorageClassInput: + break; + default: + return false; + } + + // Create a new access chain and load just after the old load. + // We cannot create the new access chain load in the position of the extract + // because the storage may have been written to in between. + InstructionBuilder ir_builder( + inst->context(), composite_inst, + IRContext::kAnalysisInstrToBlockMapping | IRContext::kAnalysisDefUse); + + uint32_t pointer_to_result_type_id = + type_mgr->FindPointerToType(inst->type_id(), storage_class); + assert(pointer_to_result_type_id != 0 && + "We did not find the pointer type that we need."); + + analysis::Integer int_type(32, false); + const analysis::Type* uint32_type = type_mgr->GetRegisteredType(&int_type); + std::vector ids; + for (uint32_t i = 1; i < inst->NumInOperands(); ++i) { + uint32_t index = inst->GetSingleWordInOperand(i); + const analysis::Constant* index_const = + const_mgr->GetConstant(uint32_type, {index}); + ids.push_back(const_mgr->GetDefiningInstruction(index_const)->result_id()); + } + + Instruction* new_access_chain = ir_builder.AddAccessChain( + pointer_to_result_type_id, + composite_inst->GetSingleWordInOperand(kLoadPointerInIdx), ids); + Instruction* new_load = + ir_builder.AddLoad(inst->type_id(), new_access_chain->result_id()); + + context()->ReplaceAllUsesWith(inst->result_id(), new_load->result_id()); + context()->KillInst(inst); + return true; +} + +bool ReduceLoadSize::ShouldReplaceExtract(Instruction* inst) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + Instruction* op_inst = def_use_mgr->GetDef( + inst->GetSingleWordInOperand(kExtractCompositeIdInIdx)); + + if (op_inst->opcode() != SpvOpLoad) { + return false; + } + + auto cached_result = should_replace_cache_.find(op_inst->result_id()); + if (cached_result != should_replace_cache_.end()) { + return cached_result->second; + } + + bool all_elements_used = false; + std::set elements_used; + + all_elements_used = + !def_use_mgr->WhileEachUser(op_inst, [&elements_used](Instruction* use) { + if (use->IsOpenCL100DebugInstr()) return true; + if (use->opcode() != SpvOpCompositeExtract || + use->NumInOperands() == 1) { + return false; + } + elements_used.insert(use->GetSingleWordInOperand(1)); + return true; + }); + + bool should_replace = false; + if (all_elements_used) { + should_replace = false; + } else { + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Type* load_type = type_mgr->GetType(op_inst->type_id()); + uint32_t total_size = 1; + switch (load_type->kind()) { + case analysis::Type::kArray: { + const analysis::Constant* size_const = + const_mgr->FindDeclaredConstant(load_type->AsArray()->LengthId()); + assert(size_const->AsIntConstant()); + total_size = size_const->GetU32(); + } break; + case analysis::Type::kStruct: + total_size = static_cast( + load_type->AsStruct()->element_types().size()); + break; + default: + break; + } + double percent_used = static_cast(elements_used.size()) / + static_cast(total_size); + should_replace = (percent_used < kThreshold); + } + + should_replace_cache_[op_inst->result_id()] = should_replace; + return should_replace; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/reduce_load_size.h b/third_party/spirv-tools/source/opt/reduce_load_size.h new file mode 100644 index 0000000..ccac49b --- /dev/null +++ b/third_party/spirv-tools/source/opt/reduce_load_size.h @@ -0,0 +1,65 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REDUCE_LOAD_SIZE_H_ +#define SOURCE_OPT_REDUCE_LOAD_SIZE_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class ReduceLoadSize : public Pass { + public: + const char* name() const override { return "reduce-load-size"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces |inst|, which must be an OpCompositeExtract instruction, with + // an OpAccessChain and a load if possible. This happens only if it is a load + // feeding |inst|. Returns true if the substitution happened. The position + // of the new instructions will be in the same place as the load feeding the + // extract. + bool ReplaceExtract(Instruction* inst); + + // Returns true if the OpCompositeExtract instruction |inst| should be replace + // or not. This is determined by looking at the load that feeds |inst| if + // it is a load. |should_replace_cache_| is used to cache the results based + // on the load feeding |inst|. + bool ShouldReplaceExtract(Instruction* inst); + + // Maps the result id of an OpLoad instruction to the result of whether or + // not the OpCompositeExtract that use the id should be replaced. + std::unordered_map should_replace_cache_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REDUCE_LOAD_SIZE_H_ diff --git a/third_party/spirv-tools/source/opt/redundancy_elimination.cpp b/third_party/spirv-tools/source/opt/redundancy_elimination.cpp new file mode 100644 index 0000000..362e54d --- /dev/null +++ b/third_party/spirv-tools/source/opt/redundancy_elimination.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/redundancy_elimination.h" + +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +Pass::Status RedundancyEliminationPass::Process() { + bool modified = false; + ValueNumberTable vnTable(context()); + + for (auto& func : *get_module()) { + // Build the dominator tree for this function. It is how the code is + // traversed. + DominatorTree& dom_tree = + context()->GetDominatorAnalysis(&func)->GetDomTree(); + + // Keeps track of all ids that contain a given value number. We keep + // track of multiple values because they could have the same value, but + // different decorations. + std::map value_to_ids; + + if (EliminateRedundanciesFrom(dom_tree.GetRoot(), vnTable, value_to_ids)) { + modified = true; + } + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool RedundancyEliminationPass::EliminateRedundanciesFrom( + DominatorTreeNode* bb, const ValueNumberTable& vnTable, + std::map value_to_ids) { + bool modified = EliminateRedundanciesInBB(bb->bb_, vnTable, &value_to_ids); + + for (auto dominated_bb : bb->children_) { + modified |= EliminateRedundanciesFrom(dominated_bb, vnTable, value_to_ids); + } + + return modified; +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/redundancy_elimination.h b/third_party/spirv-tools/source/opt/redundancy_elimination.h new file mode 100644 index 0000000..91809b5 --- /dev/null +++ b/third_party/spirv-tools/source/opt/redundancy_elimination.h @@ -0,0 +1,56 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REDUNDANCY_ELIMINATION_H_ +#define SOURCE_OPT_REDUNDANCY_ELIMINATION_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/opt/local_redundancy_elimination.h" +#include "source/opt/pass.h" +#include "source/opt/value_number_table.h" + +namespace spvtools { +namespace opt { + +// This pass implements total redundancy elimination. This is the same as +// local redundancy elimination except it looks across basic block boundaries. +// An instruction, inst, is totally redundant if there is another instruction +// that dominates inst, and also computes the same value. +class RedundancyEliminationPass : public LocalRedundancyEliminationPass { + public: + const char* name() const override { return "redundancy-elimination"; } + Status Process() override; + + protected: + // Removes for all total redundancies in the function starting at |bb|. + // + // |vnTable| must have computed a value number for every result id defined + // in the function containing |bb|. + // + // |value_to_ids| is a map from value number to ids. If {vn, id} is in + // |value_to_ids| then vn is the value number of id, and the defintion of id + // dominates |bb|. + // + // Returns true if at least one instruction is deleted. + bool EliminateRedundanciesFrom(DominatorTreeNode* bb, + const ValueNumberTable& vnTable, + std::map value_to_ids); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REDUNDANCY_ELIMINATION_H_ diff --git a/third_party/spirv-tools/source/opt/reflect.h b/third_party/spirv-tools/source/opt/reflect.h new file mode 100644 index 0000000..2e253ad --- /dev/null +++ b/third_party/spirv-tools/source/opt/reflect.h @@ -0,0 +1,70 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REFLECT_H_ +#define SOURCE_OPT_REFLECT_H_ + +#include "source/latest_version_spirv_header.h" + +namespace spvtools { +namespace opt { + +// Note that as SPIR-V evolves over time, new opcodes may appear. So the +// following functions tend to be outdated and should be updated when SPIR-V +// version bumps. + +inline bool IsDebug1Inst(SpvOp opcode) { + return (opcode >= SpvOpSourceContinued && opcode <= SpvOpSourceExtension) || + opcode == SpvOpString; +} +inline bool IsDebug2Inst(SpvOp opcode) { + return opcode == SpvOpName || opcode == SpvOpMemberName; +} +inline bool IsDebug3Inst(SpvOp opcode) { + return opcode == SpvOpModuleProcessed; +} +inline bool IsDebugLineInst(SpvOp opcode) { + return opcode == SpvOpLine || opcode == SpvOpNoLine; +} +inline bool IsAnnotationInst(SpvOp opcode) { + return (opcode >= SpvOpDecorate && opcode <= SpvOpGroupMemberDecorate) || + opcode == SpvOpDecorateId || opcode == SpvOpDecorateStringGOOGLE || + opcode == SpvOpMemberDecorateStringGOOGLE; +} +inline bool IsTypeInst(SpvOp opcode) { + return (opcode >= SpvOpTypeVoid && opcode <= SpvOpTypeForwardPointer) || + opcode == SpvOpTypePipeStorage || opcode == SpvOpTypeNamedBarrier || + opcode == SpvOpTypeAccelerationStructureNV || + opcode == SpvOpTypeAccelerationStructureKHR || + opcode == SpvOpTypeRayQueryProvisionalKHR || + opcode == SpvOpTypeCooperativeMatrixNV; +} +inline bool IsConstantInst(SpvOp opcode) { + return opcode >= SpvOpConstantTrue && opcode <= SpvOpSpecConstantOp; +} +inline bool IsCompileTimeConstantInst(SpvOp opcode) { + return opcode >= SpvOpConstantTrue && opcode <= SpvOpConstantNull; +} +inline bool IsSpecConstantInst(SpvOp opcode) { + return opcode >= SpvOpSpecConstantTrue && opcode <= SpvOpSpecConstantOp; +} +inline bool IsTerminatorInst(SpvOp opcode) { + return (opcode >= SpvOpBranch && opcode <= SpvOpUnreachable) || + (opcode == SpvOpTerminateInvocation); +} + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REFLECT_H_ diff --git a/third_party/spirv-tools/source/opt/register_pressure.cpp b/third_party/spirv-tools/source/opt/register_pressure.cpp new file mode 100644 index 0000000..5750c6d --- /dev/null +++ b/third_party/spirv-tools/source/opt/register_pressure.cpp @@ -0,0 +1,583 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/register_pressure.h" + +#include +#include + +#include "source/opt/cfg.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_tree.h" +#include "source/opt/function.h" +#include "source/opt/ir_context.h" +#include "source/opt/iterator.h" + +namespace spvtools { +namespace opt { + +namespace { +// Predicate for the FilterIterator to only consider instructions that are not +// phi instructions defined in the basic block |bb|. +class ExcludePhiDefinedInBlock { + public: + ExcludePhiDefinedInBlock(IRContext* context, const BasicBlock* bb) + : context_(context), bb_(bb) {} + + bool operator()(Instruction* insn) const { + return !(insn->opcode() == SpvOpPhi && + context_->get_instr_block(insn) == bb_); + } + + private: + IRContext* context_; + const BasicBlock* bb_; +}; + +// Returns true if |insn| generates a SSA register that is likely to require a +// physical register. +bool CreatesRegisterUsage(Instruction* insn) { + if (!insn->HasResultId()) return false; + if (insn->opcode() == SpvOpUndef) return false; + if (IsConstantInst(insn->opcode())) return false; + if (insn->opcode() == SpvOpLabel) return false; + return true; +} + +// Compute the register liveness for each basic block of a function. This also +// fill-up some information about the pick register usage and a break down of +// register usage. This implements: "A non-iterative data-flow algorithm for +// computing liveness sets in strict ssa programs" from Boissinot et al. +class ComputeRegisterLiveness { + public: + ComputeRegisterLiveness(RegisterLiveness* reg_pressure, Function* f) + : reg_pressure_(reg_pressure), + context_(reg_pressure->GetContext()), + function_(f), + cfg_(*reg_pressure->GetContext()->cfg()), + def_use_manager_(*reg_pressure->GetContext()->get_def_use_mgr()), + dom_tree_( + reg_pressure->GetContext()->GetDominatorAnalysis(f)->GetDomTree()), + loop_desc_(*reg_pressure->GetContext()->GetLoopDescriptor(f)) {} + + // Computes the register liveness for |function_| and then estimate the + // register usage. The liveness algorithm works in 2 steps: + // - First, compute the liveness for each basic blocks, but will ignore any + // back-edge; + // - Second, walk loop forest to propagate registers crossing back-edges + // (add iterative values into the liveness set). + void Compute() { + for (BasicBlock& start_bb : *function_) { + if (reg_pressure_->Get(start_bb.id()) != nullptr) { + continue; + } + cfg_.ForEachBlockInPostOrder(&start_bb, [this](BasicBlock* bb) { + if (reg_pressure_->Get(bb->id()) == nullptr) { + ComputePartialLiveness(bb); + } + }); + } + DoLoopLivenessUnification(); + EvaluateRegisterRequirements(); + } + + private: + // Registers all SSA register used by successors of |bb| in their phi + // instructions. + void ComputePhiUses(const BasicBlock& bb, + RegisterLiveness::RegionRegisterLiveness::LiveSet* live) { + uint32_t bb_id = bb.id(); + bb.ForEachSuccessorLabel([live, bb_id, this](uint32_t sid) { + BasicBlock* succ_bb = cfg_.block(sid); + succ_bb->ForEachPhiInst([live, bb_id, this](const Instruction* phi) { + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + if (phi->GetSingleWordInOperand(i + 1) == bb_id) { + Instruction* insn_op = + def_use_manager_.GetDef(phi->GetSingleWordInOperand(i)); + if (CreatesRegisterUsage(insn_op)) { + live->insert(insn_op); + break; + } + } + } + }); + }); + } + + // Computes register liveness for each basic blocks but ignores all + // back-edges. + void ComputePartialLiveness(BasicBlock* bb) { + assert(reg_pressure_->Get(bb) == nullptr && + "Basic block already processed"); + + RegisterLiveness::RegionRegisterLiveness* live_inout = + reg_pressure_->GetOrInsert(bb->id()); + ComputePhiUses(*bb, &live_inout->live_out_); + + const BasicBlock* cbb = bb; + cbb->ForEachSuccessorLabel([&live_inout, bb, this](uint32_t sid) { + // Skip back edges. + if (dom_tree_.Dominates(sid, bb->id())) { + return; + } + + BasicBlock* succ_bb = cfg_.block(sid); + RegisterLiveness::RegionRegisterLiveness* succ_live_inout = + reg_pressure_->Get(succ_bb); + assert(succ_live_inout && + "Successor liveness analysis was not performed"); + + ExcludePhiDefinedInBlock predicate(context_, succ_bb); + auto filter = + MakeFilterIteratorRange(succ_live_inout->live_in_.begin(), + succ_live_inout->live_in_.end(), predicate); + live_inout->live_out_.insert(filter.begin(), filter.end()); + }); + + live_inout->live_in_ = live_inout->live_out_; + for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) { + if (insn.opcode() == SpvOpPhi) { + live_inout->live_in_.insert(&insn); + break; + } + live_inout->live_in_.erase(&insn); + insn.ForEachInId([live_inout, this](uint32_t* id) { + Instruction* insn_op = def_use_manager_.GetDef(*id); + if (CreatesRegisterUsage(insn_op)) { + live_inout->live_in_.insert(insn_op); + } + }); + } + } + + // Propagates the register liveness information of each loop iterators. + void DoLoopLivenessUnification() { + for (const Loop* loop : *loop_desc_.GetPlaceholderRootLoop()) { + DoLoopLivenessUnification(*loop); + } + } + + // Propagates the register liveness information of loop iterators trough-out + // the loop body. + void DoLoopLivenessUnification(const Loop& loop) { + auto blocks_in_loop = MakeFilterIteratorRange( + loop.GetBlocks().begin(), loop.GetBlocks().end(), + [&loop, this](uint32_t bb_id) { + return bb_id != loop.GetHeaderBlock()->id() && + loop_desc_[bb_id] == &loop; + }); + + RegisterLiveness::RegionRegisterLiveness* header_live_inout = + reg_pressure_->Get(loop.GetHeaderBlock()); + assert(header_live_inout && + "Liveness analysis was not performed for the current block"); + + ExcludePhiDefinedInBlock predicate(context_, loop.GetHeaderBlock()); + auto live_loop = + MakeFilterIteratorRange(header_live_inout->live_in_.begin(), + header_live_inout->live_in_.end(), predicate); + + for (uint32_t bb_id : blocks_in_loop) { + BasicBlock* bb = cfg_.block(bb_id); + + RegisterLiveness::RegionRegisterLiveness* live_inout = + reg_pressure_->Get(bb); + live_inout->live_in_.insert(live_loop.begin(), live_loop.end()); + live_inout->live_out_.insert(live_loop.begin(), live_loop.end()); + } + + for (const Loop* inner_loop : loop) { + RegisterLiveness::RegionRegisterLiveness* live_inout = + reg_pressure_->Get(inner_loop->GetHeaderBlock()); + live_inout->live_in_.insert(live_loop.begin(), live_loop.end()); + live_inout->live_out_.insert(live_loop.begin(), live_loop.end()); + + DoLoopLivenessUnification(*inner_loop); + } + } + + // Get the number of required registers for this each basic block. + void EvaluateRegisterRequirements() { + for (BasicBlock& bb : *function_) { + RegisterLiveness::RegionRegisterLiveness* live_inout = + reg_pressure_->Get(bb.id()); + assert(live_inout != nullptr && "Basic block not processed"); + + size_t reg_count = live_inout->live_out_.size(); + for (Instruction* insn : live_inout->live_out_) { + live_inout->AddRegisterClass(insn); + } + live_inout->used_registers_ = reg_count; + + std::unordered_set die_in_block; + for (Instruction& insn : make_range(bb.rbegin(), bb.rend())) { + // If it is a phi instruction, the register pressure will not change + // anymore. + if (insn.opcode() == SpvOpPhi) { + break; + } + + insn.ForEachInId( + [live_inout, &die_in_block, ®_count, this](uint32_t* id) { + Instruction* op_insn = def_use_manager_.GetDef(*id); + if (!CreatesRegisterUsage(op_insn) || + live_inout->live_out_.count(op_insn)) { + // already taken into account. + return; + } + if (!die_in_block.count(*id)) { + live_inout->AddRegisterClass(def_use_manager_.GetDef(*id)); + reg_count++; + die_in_block.insert(*id); + } + }); + live_inout->used_registers_ = + std::max(live_inout->used_registers_, reg_count); + if (CreatesRegisterUsage(&insn)) { + reg_count--; + } + } + } + } + + RegisterLiveness* reg_pressure_; + IRContext* context_; + Function* function_; + CFG& cfg_; + analysis::DefUseManager& def_use_manager_; + DominatorTree& dom_tree_; + LoopDescriptor& loop_desc_; +}; +} // namespace + +// Get the number of required registers for each basic block. +void RegisterLiveness::RegionRegisterLiveness::AddRegisterClass( + Instruction* insn) { + assert(CreatesRegisterUsage(insn) && "Instruction does not use a register"); + analysis::Type* type = + insn->context()->get_type_mgr()->GetType(insn->type_id()); + + RegisterLiveness::RegisterClass reg_class{type, false}; + + insn->context()->get_decoration_mgr()->WhileEachDecoration( + insn->result_id(), SpvDecorationUniform, + [®_class](const Instruction&) { + reg_class.is_uniform_ = true; + return false; + }); + + AddRegisterClass(reg_class); +} + +void RegisterLiveness::Analyze(Function* f) { + block_pressure_.clear(); + ComputeRegisterLiveness(this, f).Compute(); +} + +void RegisterLiveness::ComputeLoopRegisterPressure( + const Loop& loop, RegionRegisterLiveness* loop_reg_pressure) const { + loop_reg_pressure->Clear(); + + const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock()); + loop_reg_pressure->live_in_ = header_live_inout->live_in_; + + std::unordered_set exit_blocks; + loop.GetExitBlocks(&exit_blocks); + + for (uint32_t bb_id : exit_blocks) { + const RegionRegisterLiveness* live_inout = Get(bb_id); + loop_reg_pressure->live_out_.insert(live_inout->live_in_.begin(), + live_inout->live_in_.end()); + } + + std::unordered_set seen_insn; + for (Instruction* insn : loop_reg_pressure->live_out_) { + loop_reg_pressure->AddRegisterClass(insn); + seen_insn.insert(insn->result_id()); + } + for (Instruction* insn : loop_reg_pressure->live_in_) { + if (!seen_insn.count(insn->result_id())) { + continue; + } + loop_reg_pressure->AddRegisterClass(insn); + seen_insn.insert(insn->result_id()); + } + + loop_reg_pressure->used_registers_ = 0; + + for (uint32_t bb_id : loop.GetBlocks()) { + BasicBlock* bb = context_->cfg()->block(bb_id); + + const RegionRegisterLiveness* live_inout = Get(bb_id); + assert(live_inout != nullptr && "Basic block not processed"); + loop_reg_pressure->used_registers_ = std::max( + loop_reg_pressure->used_registers_, live_inout->used_registers_); + + for (Instruction& insn : *bb) { + if (insn.opcode() == SpvOpPhi || !CreatesRegisterUsage(&insn) || + seen_insn.count(insn.result_id())) { + continue; + } + loop_reg_pressure->AddRegisterClass(&insn); + } + } +} + +void RegisterLiveness::SimulateFusion( + const Loop& l1, const Loop& l2, RegionRegisterLiveness* sim_result) const { + sim_result->Clear(); + + // Compute the live-in state: + // sim_result.live_in = l1.live_in U l2.live_in + // This assumes that |l1| does not generated register that is live-out for + // |l1|. + const RegionRegisterLiveness* l1_header_live_inout = Get(l1.GetHeaderBlock()); + sim_result->live_in_ = l1_header_live_inout->live_in_; + + const RegionRegisterLiveness* l2_header_live_inout = Get(l2.GetHeaderBlock()); + sim_result->live_in_.insert(l2_header_live_inout->live_in_.begin(), + l2_header_live_inout->live_in_.end()); + + // The live-out set of the fused loop is the l2 live-out set. + std::unordered_set exit_blocks; + l2.GetExitBlocks(&exit_blocks); + + for (uint32_t bb_id : exit_blocks) { + const RegionRegisterLiveness* live_inout = Get(bb_id); + sim_result->live_out_.insert(live_inout->live_in_.begin(), + live_inout->live_in_.end()); + } + + // Compute the register usage information. + std::unordered_set seen_insn; + for (Instruction* insn : sim_result->live_out_) { + sim_result->AddRegisterClass(insn); + seen_insn.insert(insn->result_id()); + } + for (Instruction* insn : sim_result->live_in_) { + if (!seen_insn.count(insn->result_id())) { + continue; + } + sim_result->AddRegisterClass(insn); + seen_insn.insert(insn->result_id()); + } + + sim_result->used_registers_ = 0; + + // The loop fusion is injecting the l1 before the l2, the latch of l1 will be + // connected to the header of l2. + // To compute the register usage, we inject the loop live-in (union of l1 and + // l2 live-in header blocks) into the the live in/out of each basic block of + // l1 to get the peak register usage. We then repeat the operation to for l2 + // basic blocks but in this case we inject the live-out of the latch of l1. + auto live_loop = MakeFilterIteratorRange( + sim_result->live_in_.begin(), sim_result->live_in_.end(), + [&l1, &l2](Instruction* insn) { + BasicBlock* bb = insn->context()->get_instr_block(insn); + return insn->HasResultId() && + !(insn->opcode() == SpvOpPhi && + (bb == l1.GetHeaderBlock() || bb == l2.GetHeaderBlock())); + }); + + for (uint32_t bb_id : l1.GetBlocks()) { + BasicBlock* bb = context_->cfg()->block(bb_id); + + const RegionRegisterLiveness* live_inout_info = Get(bb_id); + assert(live_inout_info != nullptr && "Basic block not processed"); + RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_; + live_out.insert(live_loop.begin(), live_loop.end()); + sim_result->used_registers_ = + std::max(sim_result->used_registers_, + live_inout_info->used_registers_ + live_out.size() - + live_inout_info->live_out_.size()); + + for (Instruction& insn : *bb) { + if (insn.opcode() == SpvOpPhi || !CreatesRegisterUsage(&insn) || + seen_insn.count(insn.result_id())) { + continue; + } + sim_result->AddRegisterClass(&insn); + } + } + + const RegionRegisterLiveness* l1_latch_live_inout_info = + Get(l1.GetLatchBlock()->id()); + assert(l1_latch_live_inout_info != nullptr && "Basic block not processed"); + RegionRegisterLiveness::LiveSet l1_latch_live_out = + l1_latch_live_inout_info->live_out_; + l1_latch_live_out.insert(live_loop.begin(), live_loop.end()); + + auto live_loop_l2 = + make_range(l1_latch_live_out.begin(), l1_latch_live_out.end()); + + for (uint32_t bb_id : l2.GetBlocks()) { + BasicBlock* bb = context_->cfg()->block(bb_id); + + const RegionRegisterLiveness* live_inout_info = Get(bb_id); + assert(live_inout_info != nullptr && "Basic block not processed"); + RegionRegisterLiveness::LiveSet live_out = live_inout_info->live_out_; + live_out.insert(live_loop_l2.begin(), live_loop_l2.end()); + sim_result->used_registers_ = + std::max(sim_result->used_registers_, + live_inout_info->used_registers_ + live_out.size() - + live_inout_info->live_out_.size()); + + for (Instruction& insn : *bb) { + if (insn.opcode() == SpvOpPhi || !CreatesRegisterUsage(&insn) || + seen_insn.count(insn.result_id())) { + continue; + } + sim_result->AddRegisterClass(&insn); + } + } +} + +void RegisterLiveness::SimulateFission( + const Loop& loop, const std::unordered_set& moved_inst, + const std::unordered_set& copied_inst, + RegionRegisterLiveness* l1_sim_result, + RegionRegisterLiveness* l2_sim_result) const { + l1_sim_result->Clear(); + l2_sim_result->Clear(); + + // Filter predicates: consider instructions that only belong to the first and + // second loop. + auto belong_to_loop1 = [&moved_inst, &copied_inst, &loop](Instruction* insn) { + return moved_inst.count(insn) || copied_inst.count(insn) || + !loop.IsInsideLoop(insn); + }; + auto belong_to_loop2 = [&moved_inst](Instruction* insn) { + return !moved_inst.count(insn); + }; + + const RegionRegisterLiveness* header_live_inout = Get(loop.GetHeaderBlock()); + // l1 live-in + { + auto live_loop = MakeFilterIteratorRange( + header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(), + belong_to_loop1); + l1_sim_result->live_in_.insert(live_loop.begin(), live_loop.end()); + } + // l2 live-in + { + auto live_loop = MakeFilterIteratorRange( + header_live_inout->live_in_.begin(), header_live_inout->live_in_.end(), + belong_to_loop2); + l2_sim_result->live_in_.insert(live_loop.begin(), live_loop.end()); + } + + std::unordered_set exit_blocks; + loop.GetExitBlocks(&exit_blocks); + + // l2 live-out. + for (uint32_t bb_id : exit_blocks) { + const RegionRegisterLiveness* live_inout = Get(bb_id); + l2_sim_result->live_out_.insert(live_inout->live_in_.begin(), + live_inout->live_in_.end()); + } + // l1 live-out. + { + auto live_out = MakeFilterIteratorRange(l2_sim_result->live_out_.begin(), + l2_sim_result->live_out_.end(), + belong_to_loop1); + l1_sim_result->live_out_.insert(live_out.begin(), live_out.end()); + } + { + auto live_out = + MakeFilterIteratorRange(l2_sim_result->live_in_.begin(), + l2_sim_result->live_in_.end(), belong_to_loop1); + l1_sim_result->live_out_.insert(live_out.begin(), live_out.end()); + } + // Lives out of l1 are live out of l2 so are live in of l2 as well. + l2_sim_result->live_in_.insert(l1_sim_result->live_out_.begin(), + l1_sim_result->live_out_.end()); + + for (Instruction* insn : l1_sim_result->live_in_) { + l1_sim_result->AddRegisterClass(insn); + } + for (Instruction* insn : l2_sim_result->live_in_) { + l2_sim_result->AddRegisterClass(insn); + } + + l1_sim_result->used_registers_ = 0; + l2_sim_result->used_registers_ = 0; + + for (uint32_t bb_id : loop.GetBlocks()) { + BasicBlock* bb = context_->cfg()->block(bb_id); + + const RegisterLiveness::RegionRegisterLiveness* live_inout = Get(bb_id); + assert(live_inout != nullptr && "Basic block not processed"); + auto l1_block_live_out = + MakeFilterIteratorRange(live_inout->live_out_.begin(), + live_inout->live_out_.end(), belong_to_loop1); + auto l2_block_live_out = + MakeFilterIteratorRange(live_inout->live_out_.begin(), + live_inout->live_out_.end(), belong_to_loop2); + + size_t l1_reg_count = + std::distance(l1_block_live_out.begin(), l1_block_live_out.end()); + size_t l2_reg_count = + std::distance(l2_block_live_out.begin(), l2_block_live_out.end()); + + std::unordered_set die_in_block; + for (Instruction& insn : make_range(bb->rbegin(), bb->rend())) { + if (insn.opcode() == SpvOpPhi) { + break; + } + + bool does_belong_to_loop1 = belong_to_loop1(&insn); + bool does_belong_to_loop2 = belong_to_loop2(&insn); + insn.ForEachInId([live_inout, &die_in_block, &l1_reg_count, &l2_reg_count, + does_belong_to_loop1, does_belong_to_loop2, + this](uint32_t* id) { + Instruction* op_insn = context_->get_def_use_mgr()->GetDef(*id); + if (!CreatesRegisterUsage(op_insn) || + live_inout->live_out_.count(op_insn)) { + // already taken into account. + return; + } + if (!die_in_block.count(*id)) { + if (does_belong_to_loop1) { + l1_reg_count++; + } + if (does_belong_to_loop2) { + l2_reg_count++; + } + die_in_block.insert(*id); + } + }); + l1_sim_result->used_registers_ = + std::max(l1_sim_result->used_registers_, l1_reg_count); + l2_sim_result->used_registers_ = + std::max(l2_sim_result->used_registers_, l2_reg_count); + if (CreatesRegisterUsage(&insn)) { + if (does_belong_to_loop1) { + if (!l1_sim_result->live_in_.count(&insn)) { + l1_sim_result->AddRegisterClass(&insn); + } + l1_reg_count--; + } + if (does_belong_to_loop2) { + if (!l2_sim_result->live_in_.count(&insn)) { + l2_sim_result->AddRegisterClass(&insn); + } + l2_reg_count--; + } + } + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/register_pressure.h b/third_party/spirv-tools/source/opt/register_pressure.h new file mode 100644 index 0000000..cb3d2e2 --- /dev/null +++ b/third_party/spirv-tools/source/opt/register_pressure.h @@ -0,0 +1,196 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REGISTER_PRESSURE_H_ +#define SOURCE_OPT_REGISTER_PRESSURE_H_ + +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/types.h" + +namespace spvtools { +namespace opt { + +class IRContext; +class Loop; +class LoopDescriptor; + +// Handles the register pressure of a function for different regions (function, +// loop, basic block). It also contains some utilities to foresee the register +// pressure following code transformations. +class RegisterLiveness { + public: + // Classification of SSA registers. + struct RegisterClass { + analysis::Type* type_; + bool is_uniform_; + + bool operator==(const RegisterClass& rhs) const { + return std::tie(type_, is_uniform_) == + std::tie(rhs.type_, rhs.is_uniform_); + } + }; + + struct RegionRegisterLiveness { + using LiveSet = std::unordered_set; + using RegClassSetTy = std::vector>; + + // SSA register live when entering the basic block. + LiveSet live_in_; + // SSA register live when exiting the basic block. + LiveSet live_out_; + + // Maximum number of required registers. + size_t used_registers_; + // Break down of the number of required registers per class of register. + RegClassSetTy registers_classes_; + + void Clear() { + live_out_.clear(); + live_in_.clear(); + used_registers_ = 0; + registers_classes_.clear(); + } + + void AddRegisterClass(const RegisterClass& reg_class) { + auto it = std::find_if( + registers_classes_.begin(), registers_classes_.end(), + [®_class](const std::pair& class_count) { + return class_count.first == reg_class; + }); + if (it != registers_classes_.end()) { + it->second++; + } else { + registers_classes_.emplace_back(std::move(reg_class), + static_cast(1)); + } + } + + void AddRegisterClass(Instruction* insn); + }; + + RegisterLiveness(IRContext* context, Function* f) : context_(context) { + Analyze(f); + } + + // Returns liveness and register information for the basic block |bb|. If no + // entry exist for the basic block, the function returns null. + const RegionRegisterLiveness* Get(const BasicBlock* bb) const { + return Get(bb->id()); + } + + // Returns liveness and register information for the basic block id |bb_id|. + // If no entry exist for the basic block, the function returns null. + const RegionRegisterLiveness* Get(uint32_t bb_id) const { + RegionRegisterLivenessMap::const_iterator it = block_pressure_.find(bb_id); + if (it != block_pressure_.end()) { + return &it->second; + } + return nullptr; + } + + IRContext* GetContext() const { return context_; } + + // Returns liveness and register information for the basic block |bb|. If no + // entry exist for the basic block, the function returns null. + RegionRegisterLiveness* Get(const BasicBlock* bb) { return Get(bb->id()); } + + // Returns liveness and register information for the basic block id |bb_id|. + // If no entry exist for the basic block, the function returns null. + RegionRegisterLiveness* Get(uint32_t bb_id) { + RegionRegisterLivenessMap::iterator it = block_pressure_.find(bb_id); + if (it != block_pressure_.end()) { + return &it->second; + } + return nullptr; + } + + // Returns liveness and register information for the basic block id |bb_id| or + // create a new empty entry if no entry already existed. + RegionRegisterLiveness* GetOrInsert(uint32_t bb_id) { + return &block_pressure_[bb_id]; + } + + // Compute the register pressure for the |loop| and store the result into + // |reg_pressure|. The live-in set corresponds to the live-in set of the + // header block, the live-out set of the loop corresponds to the union of the + // live-in sets of each exit basic block. + void ComputeLoopRegisterPressure(const Loop& loop, + RegionRegisterLiveness* reg_pressure) const; + + // Estimate the register pressure for the |l1| and |l2| as if they were making + // one unique loop. The result is stored into |simulation_result|. + void SimulateFusion(const Loop& l1, const Loop& l2, + RegionRegisterLiveness* simulation_result) const; + + // Estimate the register pressure of |loop| after it has been fissioned + // according to |moved_instructions| and |copied_instructions|. The function + // assumes that the fission creates a new loop before |loop|, moves any + // instructions present inside |moved_instructions| and copies any + // instructions present inside |copied_instructions| into this new loop. + // The set |loop1_sim_result| store the simulation result of the loop with the + // moved instructions. The set |loop2_sim_result| store the simulation result + // of the loop with the removed instructions. + void SimulateFission( + const Loop& loop, + const std::unordered_set& moved_instructions, + const std::unordered_set& copied_instructions, + RegionRegisterLiveness* loop1_sim_result, + RegionRegisterLiveness* loop2_sim_result) const; + + private: + using RegionRegisterLivenessMap = + std::unordered_map; + + IRContext* context_; + RegionRegisterLivenessMap block_pressure_; + + void Analyze(Function* f); +}; + +// Handles the register pressure of a function for different regions (function, +// loop, basic block). It also contains some utilities to foresee the register +// pressure following code transformations. +class LivenessAnalysis { + using LivenessAnalysisMap = + std::unordered_map; + + public: + LivenessAnalysis(IRContext* context) : context_(context) {} + + // Computes the liveness analysis for the function |f| and cache the result. + // If the analysis was performed for this function, then the cached analysis + // is returned. + const RegisterLiveness* Get(Function* f) { + LivenessAnalysisMap::iterator it = analysis_cache_.find(f); + if (it != analysis_cache_.end()) { + return &it->second; + } + return &analysis_cache_.emplace(f, RegisterLiveness{context_, f}) + .first->second; + } + + private: + IRContext* context_; + LivenessAnalysisMap analysis_cache_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REGISTER_PRESSURE_H_ diff --git a/third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp b/third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp new file mode 100644 index 0000000..73f16dd --- /dev/null +++ b/third_party/spirv-tools/source/opt/relax_float_ops_pass.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "relax_float_ops_pass.h" + +#include "source/opt/ir_builder.h" + +namespace spvtools { +namespace opt { + +bool RelaxFloatOpsPass::IsRelaxable(Instruction* inst) { + return target_ops_core_f_rslt_.count(inst->opcode()) != 0 || + target_ops_core_f_opnd_.count(inst->opcode()) != 0 || + sample_ops_.count(inst->opcode()) != 0 || + (inst->opcode() == SpvOpExtInst && + inst->GetSingleWordInOperand(0) == + context()->get_feature_mgr()->GetExtInstImportId_GLSLstd450() && + target_ops_450_.count(inst->GetSingleWordInOperand(1)) != 0); +} + +bool RelaxFloatOpsPass::IsFloat32(Instruction* inst) { + uint32_t ty_id; + if (target_ops_core_f_opnd_.count(inst->opcode()) != 0) { + uint32_t opnd_id = inst->GetSingleWordInOperand(0); + Instruction* opnd_inst = get_def_use_mgr()->GetDef(opnd_id); + ty_id = opnd_inst->type_id(); + } else { + ty_id = inst->type_id(); + if (ty_id == 0) return false; + } + return IsFloat(ty_id, 32); +} + +bool RelaxFloatOpsPass::IsRelaxed(uint32_t r_id) { + for (auto r_inst : get_decoration_mgr()->GetDecorationsFor(r_id, false)) + if (r_inst->opcode() == SpvOpDecorate && + r_inst->GetSingleWordInOperand(1) == SpvDecorationRelaxedPrecision) + return true; + return false; +} + +bool RelaxFloatOpsPass::ProcessInst(Instruction* r_inst) { + uint32_t r_id = r_inst->result_id(); + if (r_id == 0) return false; + if (!IsFloat32(r_inst)) return false; + if (IsRelaxed(r_id)) return false; + if (!IsRelaxable(r_inst)) return false; + get_decoration_mgr()->AddDecoration(r_id, SpvDecorationRelaxedPrecision); + return true; +} + +bool RelaxFloatOpsPass::ProcessFunction(Function* func) { + bool modified = false; + cfg()->ForEachBlockInReversePostOrder( + func->entry().get(), [&modified, this](BasicBlock* bb) { + for (auto ii = bb->begin(); ii != bb->end(); ++ii) + modified |= ProcessInst(&*ii); + }); + return modified; +} + +Pass::Status RelaxFloatOpsPass::ProcessImpl() { + Pass::ProcessFunction pfn = [this](Function* fp) { + return ProcessFunction(fp); + }; + bool modified = context()->ProcessEntryPointCallTree(pfn); + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +Pass::Status RelaxFloatOpsPass::Process() { + Initialize(); + return ProcessImpl(); +} + +void RelaxFloatOpsPass::Initialize() { + target_ops_core_f_rslt_ = { + SpvOpLoad, + SpvOpPhi, + SpvOpVectorExtractDynamic, + SpvOpVectorInsertDynamic, + SpvOpVectorShuffle, + SpvOpCompositeExtract, + SpvOpCompositeConstruct, + SpvOpCompositeInsert, + SpvOpCopyObject, + SpvOpTranspose, + SpvOpConvertSToF, + SpvOpConvertUToF, + SpvOpFConvert, + // SpvOpQuantizeToF16, + SpvOpFNegate, + SpvOpFAdd, + SpvOpFSub, + SpvOpFMul, + SpvOpFDiv, + SpvOpFMod, + SpvOpVectorTimesScalar, + SpvOpMatrixTimesScalar, + SpvOpVectorTimesMatrix, + SpvOpMatrixTimesVector, + SpvOpMatrixTimesMatrix, + SpvOpOuterProduct, + SpvOpDot, + SpvOpSelect, + }; + target_ops_core_f_opnd_ = { + SpvOpFOrdEqual, + SpvOpFUnordEqual, + SpvOpFOrdNotEqual, + SpvOpFUnordNotEqual, + SpvOpFOrdLessThan, + SpvOpFUnordLessThan, + SpvOpFOrdGreaterThan, + SpvOpFUnordGreaterThan, + SpvOpFOrdLessThanEqual, + SpvOpFUnordLessThanEqual, + SpvOpFOrdGreaterThanEqual, + SpvOpFUnordGreaterThanEqual, + }; + target_ops_450_ = { + GLSLstd450Round, GLSLstd450RoundEven, GLSLstd450Trunc, GLSLstd450FAbs, + GLSLstd450FSign, GLSLstd450Floor, GLSLstd450Ceil, GLSLstd450Fract, + GLSLstd450Radians, GLSLstd450Degrees, GLSLstd450Sin, GLSLstd450Cos, + GLSLstd450Tan, GLSLstd450Asin, GLSLstd450Acos, GLSLstd450Atan, + GLSLstd450Sinh, GLSLstd450Cosh, GLSLstd450Tanh, GLSLstd450Asinh, + GLSLstd450Acosh, GLSLstd450Atanh, GLSLstd450Atan2, GLSLstd450Pow, + GLSLstd450Exp, GLSLstd450Log, GLSLstd450Exp2, GLSLstd450Log2, + GLSLstd450Sqrt, GLSLstd450InverseSqrt, GLSLstd450Determinant, + GLSLstd450MatrixInverse, + // TODO(greg-lunarg): GLSLstd450ModfStruct, + GLSLstd450FMin, GLSLstd450FMax, GLSLstd450FClamp, GLSLstd450FMix, + GLSLstd450Step, GLSLstd450SmoothStep, GLSLstd450Fma, + // TODO(greg-lunarg): GLSLstd450FrexpStruct, + GLSLstd450Ldexp, GLSLstd450Length, GLSLstd450Distance, GLSLstd450Cross, + GLSLstd450Normalize, GLSLstd450FaceForward, GLSLstd450Reflect, + GLSLstd450Refract, GLSLstd450NMin, GLSLstd450NMax, GLSLstd450NClamp}; + sample_ops_ = {SpvOpImageSampleImplicitLod, + SpvOpImageSampleExplicitLod, + SpvOpImageSampleDrefImplicitLod, + SpvOpImageSampleDrefExplicitLod, + SpvOpImageSampleProjImplicitLod, + SpvOpImageSampleProjExplicitLod, + SpvOpImageSampleProjDrefImplicitLod, + SpvOpImageSampleProjDrefExplicitLod, + SpvOpImageFetch, + SpvOpImageGather, + SpvOpImageDrefGather, + SpvOpImageRead, + SpvOpImageSparseSampleImplicitLod, + SpvOpImageSparseSampleExplicitLod, + SpvOpImageSparseSampleDrefImplicitLod, + SpvOpImageSparseSampleDrefExplicitLod, + SpvOpImageSparseSampleProjImplicitLod, + SpvOpImageSparseSampleProjExplicitLod, + SpvOpImageSparseSampleProjDrefImplicitLod, + SpvOpImageSparseSampleProjDrefExplicitLod, + SpvOpImageSparseFetch, + SpvOpImageSparseGather, + SpvOpImageSparseDrefGather, + SpvOpImageSparseTexelsResident, + SpvOpImageSparseRead}; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/relax_float_ops_pass.h b/third_party/spirv-tools/source/opt/relax_float_ops_pass.h new file mode 100644 index 0000000..5ee3d73 --- /dev/null +++ b/third_party/spirv-tools/source/opt/relax_float_ops_pass.h @@ -0,0 +1,80 @@ +// Copyright (c) 2019 Valve Corporation +// Copyright (c) 2019 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_ +#define LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_ + +#include "source/opt/ir_builder.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +class RelaxFloatOpsPass : public Pass { + public: + RelaxFloatOpsPass() : Pass() {} + + ~RelaxFloatOpsPass() override = default; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping; + } + + // See optimizer.hpp for pass user documentation. + Status Process() override; + + const char* name() const override { return "convert-to-half-pass"; } + + private: + // Return true if |inst| can have the RelaxedPrecision decoration applied + // to it. + bool IsRelaxable(Instruction* inst); + + // Return true if |inst| returns scalar, vector or matrix type with base + // float and width 32 + bool IsFloat32(Instruction* inst); + + // Return true if |r_id| is decorated with RelaxedPrecision + bool IsRelaxed(uint32_t r_id); + + // If |inst| is an instruction of float32-based type and is not decorated + // RelaxedPrecision, add such a decoration to the module. + bool ProcessInst(Instruction* inst); + + // Call ProcessInst on every instruction in |func|. + bool ProcessFunction(Function* func); + + Pass::Status ProcessImpl(); + + // Initialize state for converting to half + void Initialize(); + + // Set of float result core operations to be processed + std::unordered_set target_ops_core_f_rslt_; + + // Set of float operand core operations to be processed + std::unordered_set target_ops_core_f_opnd_; + + // Set of 450 extension operations to be processed + std::unordered_set target_ops_450_; + + // Set of sample operations + std::unordered_set sample_ops_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // LIBSPIRV_OPT_RELAX_FLOAT_OPS_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/remove_duplicates_pass.cpp b/third_party/spirv-tools/source/opt/remove_duplicates_pass.cpp new file mode 100644 index 0000000..0e65cc8 --- /dev/null +++ b/third_party/spirv-tools/source/opt/remove_duplicates_pass.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/remove_duplicates_pass.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opcode.h" +#include "source/opt/decoration_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/reflect.h" + +namespace spvtools { +namespace opt { + +Pass::Status RemoveDuplicatesPass::Process() { + bool modified = RemoveDuplicateCapabilities(); + modified |= RemoveDuplicatesExtInstImports(); + modified |= RemoveDuplicateTypes(); + modified |= RemoveDuplicateDecorations(); + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +bool RemoveDuplicatesPass::RemoveDuplicateCapabilities() const { + bool modified = false; + + if (context()->capabilities().empty()) { + return modified; + } + + std::unordered_set capabilities; + for (auto* i = &*context()->capability_begin(); i;) { + auto res = capabilities.insert(i->GetSingleWordOperand(0u)); + + if (res.second) { + // Never seen before, keep it. + i = i->NextNode(); + } else { + // It's a duplicate, remove it. + i = context()->KillInst(i); + modified = true; + } + } + + return modified; +} + +bool RemoveDuplicatesPass::RemoveDuplicatesExtInstImports() const { + bool modified = false; + + if (context()->ext_inst_imports().empty()) { + return modified; + } + + std::unordered_map ext_inst_imports; + for (auto* i = &*context()->ext_inst_import_begin(); i;) { + auto res = ext_inst_imports.emplace( + reinterpret_cast(i->GetInOperand(0u).words.data()), + i->result_id()); + if (res.second) { + // Never seen before, keep it. + i = i->NextNode(); + } else { + // It's a duplicate, remove it. + context()->ReplaceAllUsesWith(i->result_id(), res.first->second); + i = context()->KillInst(i); + modified = true; + } + } + + return modified; +} + +bool RemoveDuplicatesPass::RemoveDuplicateTypes() const { + bool modified = false; + + if (context()->types_values().empty()) { + return modified; + } + + analysis::TypeManager type_manager(context()->consumer(), context()); + + std::vector visited_types; + std::vector visited_forward_pointers; + std::vector to_delete; + for (auto* i = &*context()->types_values_begin(); i; i = i->NextNode()) { + const bool is_i_forward_pointer = i->opcode() == SpvOpTypeForwardPointer; + + // We only care about types. + if (!spvOpcodeGeneratesType(i->opcode()) && !is_i_forward_pointer) { + continue; + } + + if (!is_i_forward_pointer) { + // Is the current type equal to one of the types we have already visited? + SpvId id_to_keep = 0u; + analysis::Type* i_type = type_manager.GetType(i->result_id()); + assert(i_type); + // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the + // ResultIdTrie from unify_const_pass.cpp for this. + for (auto j : visited_types) { + analysis::Type* j_type = type_manager.GetType(j->result_id()); + assert(j_type); + if (*i_type == *j_type) { + id_to_keep = j->result_id(); + break; + } + } + + if (id_to_keep == 0u) { + // This is a never seen before type, keep it around. + visited_types.emplace_back(i); + } else { + // The same type has already been seen before, remove this one. + context()->KillNamesAndDecorates(i->result_id()); + context()->ReplaceAllUsesWith(i->result_id(), id_to_keep); + modified = true; + to_delete.emplace_back(i); + } + } else { + analysis::ForwardPointer i_type( + i->GetSingleWordInOperand(0u), + (SpvStorageClass)i->GetSingleWordInOperand(1u)); + i_type.SetTargetPointer( + type_manager.GetType(i_type.target_id())->AsPointer()); + + // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the + // ResultIdTrie from unify_const_pass.cpp for this. + const bool found_a_match = + std::find(std::begin(visited_forward_pointers), + std::end(visited_forward_pointers), + i_type) != std::end(visited_forward_pointers); + + if (!found_a_match) { + // This is a never seen before type, keep it around. + visited_forward_pointers.emplace_back(i_type); + } else { + // The same type has already been seen before, remove this one. + modified = true; + to_delete.emplace_back(i); + } + } + } + + for (auto i : to_delete) { + context()->KillInst(i); + } + + return modified; +} + +// TODO(pierremoreau): Duplicate decoration groups should be removed. For +// example, in +// OpDecorate %1 Constant +// %1 = OpDecorationGroup +// OpDecorate %2 Constant +// %2 = OpDecorationGroup +// OpGroupDecorate %1 %3 +// OpGroupDecorate %2 %4 +// group %2 could be removed. +bool RemoveDuplicatesPass::RemoveDuplicateDecorations() const { + bool modified = false; + + std::vector visited_decorations; + + analysis::DecorationManager decoration_manager(context()->module()); + for (auto* i = &*context()->annotation_begin(); i;) { + // Is the current decoration equal to one of the decorations we have + // already visited? + bool already_visited = false; + // TODO(dneto0): Use a trie to avoid quadratic behaviour? Extract the + // ResultIdTrie from unify_const_pass.cpp for this. + for (const Instruction* j : visited_decorations) { + if (decoration_manager.AreDecorationsTheSame(&*i, j, false)) { + already_visited = true; + break; + } + } + + if (!already_visited) { + // This is a never seen before decoration, keep it around. + visited_decorations.emplace_back(&*i); + i = i->NextNode(); + } else { + // The same decoration has already been seen before, remove this one. + modified = true; + i = context()->KillInst(i); + } + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/remove_duplicates_pass.h b/third_party/spirv-tools/source/opt/remove_duplicates_pass.h new file mode 100644 index 0000000..038caa8 --- /dev/null +++ b/third_party/spirv-tools/source/opt/remove_duplicates_pass.h @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REMOVE_DUPLICATES_PASS_H_ +#define SOURCE_OPT_REMOVE_DUPLICATES_PASS_H_ + +#include +#include + +#include "source/opt/decoration_manager.h" +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +using IdDecorationsList = + std::unordered_map>; + +// See optimizer.hpp for documentation. +class RemoveDuplicatesPass : public Pass { + public: + const char* name() const override { return "remove-duplicates"; } + Status Process() override; + + private: + // Remove duplicate capabilities from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicateCapabilities() const; + // Remove duplicate extended instruction imports from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicatesExtInstImports() const; + // Remove duplicate types from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicateTypes() const; + // Remove duplicate decorations from the module + // + // Returns true if the module was modified, false otherwise. + bool RemoveDuplicateDecorations() const; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REMOVE_DUPLICATES_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/replace_invalid_opc.cpp b/third_party/spirv-tools/source/opt/replace_invalid_opc.cpp new file mode 100644 index 0000000..38b7539 --- /dev/null +++ b/third_party/spirv-tools/source/opt/replace_invalid_opc.cpp @@ -0,0 +1,208 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/replace_invalid_opc.h" + +#include +#include + +namespace spvtools { +namespace opt { + +Pass::Status ReplaceInvalidOpcodePass::Process() { + bool modified = false; + + if (context()->get_feature_mgr()->HasCapability(SpvCapabilityLinkage)) { + return Status::SuccessWithoutChange; + } + + SpvExecutionModel execution_model = GetExecutionModel(); + if (execution_model == SpvExecutionModelKernel) { + // We do not handle kernels. + return Status::SuccessWithoutChange; + } + if (execution_model == SpvExecutionModelMax) { + // Mixed execution models for the entry points. This case is not currently + // handled. + return Status::SuccessWithoutChange; + } + + for (Function& func : *get_module()) { + modified |= RewriteFunction(&func, execution_model); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +SpvExecutionModel ReplaceInvalidOpcodePass::GetExecutionModel() { + SpvExecutionModel result = SpvExecutionModelMax; + bool first = true; + for (Instruction& entry_point : get_module()->entry_points()) { + if (first) { + result = + static_cast(entry_point.GetSingleWordInOperand(0)); + first = false; + } else { + SpvExecutionModel current_model = + static_cast(entry_point.GetSingleWordInOperand(0)); + if (current_model != result) { + result = SpvExecutionModelMax; + break; + } + } + } + return result; +} + +bool ReplaceInvalidOpcodePass::RewriteFunction(Function* function, + SpvExecutionModel model) { + bool modified = false; + Instruction* last_line_dbg_inst = nullptr; + function->ForEachInst( + [model, &modified, &last_line_dbg_inst, this](Instruction* inst) { + // Track the debug information so we can have a meaningful message. + if (inst->opcode() == SpvOpLabel || inst->opcode() == SpvOpNoLine) { + last_line_dbg_inst = nullptr; + return; + } else if (inst->opcode() == SpvOpLine) { + last_line_dbg_inst = inst; + return; + } + + bool replace = false; + if (model != SpvExecutionModelFragment && + IsFragmentShaderOnlyInstruction(inst)) { + replace = true; + } + + if (model != SpvExecutionModelTessellationControl && + model != SpvExecutionModelGLCompute) { + if (inst->opcode() == SpvOpControlBarrier) { + assert(model != SpvExecutionModelKernel && + "Expecting to be working on a shader module."); + replace = true; + } + } + + if (replace) { + modified = true; + if (last_line_dbg_inst == nullptr) { + ReplaceInstruction(inst, nullptr, 0, 0); + } else { + // Get the name of the source file. + Instruction* file_name = context()->get_def_use_mgr()->GetDef( + last_line_dbg_inst->GetSingleWordInOperand(0)); + const char* source = reinterpret_cast( + &file_name->GetInOperand(0).words[0]); + + // Get the line number and column number. + uint32_t line_number = + last_line_dbg_inst->GetSingleWordInOperand(1); + uint32_t col_number = last_line_dbg_inst->GetSingleWordInOperand(2); + + // Replace the instruction. + ReplaceInstruction(inst, source, line_number, col_number); + } + } + }, + /* run_on_debug_line_insts = */ true); + return modified; +} + +bool ReplaceInvalidOpcodePass::IsFragmentShaderOnlyInstruction( + Instruction* inst) { + switch (inst->opcode()) { + case SpvOpDPdx: + case SpvOpDPdy: + case SpvOpFwidth: + case SpvOpDPdxFine: + case SpvOpDPdyFine: + case SpvOpFwidthFine: + case SpvOpDPdxCoarse: + case SpvOpDPdyCoarse: + case SpvOpFwidthCoarse: + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageQueryLod: + // TODO: Teach |ReplaceInstruction| to handle block terminators. Then + // uncomment the OpKill case. + // case SpvOpKill: + // case SpvOpTerminateInstruction: + return true; + default: + return false; + } +} + +void ReplaceInvalidOpcodePass::ReplaceInstruction(Instruction* inst, + const char* source, + uint32_t line_number, + uint32_t column_number) { + if (inst->result_id() != 0) { + uint32_t const_id = GetSpecialConstant(inst->type_id()); + context()->KillNamesAndDecorates(inst); + context()->ReplaceAllUsesWith(inst->result_id(), const_id); + } + assert(!inst->IsBlockTerminator() && + "We cannot simply delete a block terminator. It must be replaced " + "with something."); + if (consumer()) { + std::string message = BuildWarningMessage(inst->opcode()); + consumer()(SPV_MSG_WARNING, source, {line_number, column_number, 0}, + message.c_str()); + } + context()->KillInst(inst); +} + +uint32_t ReplaceInvalidOpcodePass::GetSpecialConstant(uint32_t type_id) { + const analysis::Constant* special_const = nullptr; + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + + Instruction* type = context()->get_def_use_mgr()->GetDef(type_id); + if (type->opcode() == SpvOpTypeVector) { + uint32_t component_const = + GetSpecialConstant(type->GetSingleWordInOperand(0)); + std::vector ids; + for (uint32_t i = 0; i < type->GetSingleWordInOperand(1); ++i) { + ids.push_back(component_const); + } + special_const = const_mgr->GetConstant(type_mgr->GetType(type_id), ids); + } else { + assert(type->opcode() == SpvOpTypeInt || type->opcode() == SpvOpTypeFloat); + std::vector literal_words; + for (uint32_t i = 0; i < type->GetSingleWordInOperand(0); i += 32) { + literal_words.push_back(0xDEADBEEF); + } + special_const = + const_mgr->GetConstant(type_mgr->GetType(type_id), literal_words); + } + assert(special_const != nullptr); + return const_mgr->GetDefiningInstruction(special_const)->result_id(); +} + +std::string ReplaceInvalidOpcodePass::BuildWarningMessage(SpvOp opcode) { + spv_opcode_desc opcode_info; + context()->grammar().lookupOpcode(opcode, &opcode_info); + std::string message = "Removing "; + message += opcode_info->name; + message += " instruction because of incompatible execution model."; + return message; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/replace_invalid_opc.h b/third_party/spirv-tools/source/opt/replace_invalid_opc.h new file mode 100644 index 0000000..426bcac --- /dev/null +++ b/third_party/spirv-tools/source/opt/replace_invalid_opc.h @@ -0,0 +1,67 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_REPLACE_INVALID_OPC_H_ +#define SOURCE_OPT_REPLACE_INVALID_OPC_H_ + +#include + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// This pass will runs on shader modules only. It will replace the result of +// instructions that are valid for shader modules, but not the current shader +// stage, with a constant value. If the instruction does not have a return +// value, the instruction will simply be deleted. +class ReplaceInvalidOpcodePass : public Pass { + public: + const char* name() const override { return "replace-invalid-opcode"; } + Status Process() override; + + private: + // Returns the execution model that is used by every entry point in the + // module. If more than one execution model is used in the module, then the + // return value is SpvExecutionModelMax. + SpvExecutionModel GetExecutionModel(); + + // Replaces all instructions in |function| that are invalid with execution + // model |mode|, but valid for another shader model, with a special constant + // value. See |GetSpecialConstant|. + bool RewriteFunction(Function* function, SpvExecutionModel mode); + + // Returns true if |inst| is valid for fragment shaders only. + bool IsFragmentShaderOnlyInstruction(Instruction* inst); + + // Replaces all uses of the result of |inst|, if there is one, with the id of + // a special constant. Then |inst| is killed. |inst| cannot be a block + // terminator because the basic block will then become invalid. |inst| is no + // longer valid after calling this function. + void ReplaceInstruction(Instruction* inst, const char* source, + uint32_t line_number, uint32_t column_number); + + // Returns the id of a constant with type |type_id|. The type must be an + // integer, float, or vector. For scalar types, the hex representation of the + // constant will be the concatenation of 0xDEADBEEF with itself until the + // width of the type has been reached. For a vector, each element of the + // constant will be constructed the same way. + uint32_t GetSpecialConstant(uint32_t type_id); + std::string BuildWarningMessage(SpvOp opcode); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_REPLACE_INVALID_OPC_H_ diff --git a/third_party/spirv-tools/source/opt/scalar_analysis.cpp b/third_party/spirv-tools/source/opt/scalar_analysis.cpp new file mode 100644 index 0000000..38555e6 --- /dev/null +++ b/third_party/spirv-tools/source/opt/scalar_analysis.cpp @@ -0,0 +1,988 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/scalar_analysis.h" + +#include +#include +#include +#include + +#include "source/opt/ir_context.h" + +// Transforms a given scalar operation instruction into a DAG representation. +// +// 1. Take an instruction and traverse its operands until we reach a +// constant node or an instruction which we do not know how to compute the +// value, such as a load. +// +// 2. Create a new node for each instruction traversed and build the nodes for +// the in operands of that instruction as well. +// +// 3. Add the operand nodes as children of the first and hash the node. Use the +// hash to see if the node is already in the cache. We ensure the children are +// always in sorted order so that two nodes with the same children but inserted +// in a different order have the same hash and so that the overloaded operator== +// will return true. If the node is already in the cache return the cached +// version instead. +// +// 4. The created DAG can then be simplified by +// ScalarAnalysis::SimplifyExpression, implemented in +// scalar_analysis_simplification.cpp. See that file for further information on +// the simplification process. +// + +namespace spvtools { +namespace opt { + +uint32_t SENode::NumberOfNodes = 0; + +ScalarEvolutionAnalysis::ScalarEvolutionAnalysis(IRContext* context) + : context_(context), pretend_equal_{} { + // Create and cached the CantComputeNode. + cached_cant_compute_ = + GetCachedOrAdd(std::unique_ptr(new SECantCompute(this))); +} + +SENode* ScalarEvolutionAnalysis::CreateNegation(SENode* operand) { + // If operand is can't compute then the whole graph is can't compute. + if (operand->IsCantCompute()) return CreateCantComputeNode(); + + if (operand->GetType() == SENode::Constant) { + return CreateConstant(-operand->AsSEConstantNode()->FoldToSingleValue()); + } + std::unique_ptr negation_node{new SENegative(this)}; + negation_node->AddChild(operand); + return GetCachedOrAdd(std::move(negation_node)); +} + +SENode* ScalarEvolutionAnalysis::CreateConstant(int64_t integer) { + return GetCachedOrAdd( + std::unique_ptr(new SEConstantNode(this, integer))); +} + +SENode* ScalarEvolutionAnalysis::CreateRecurrentExpression( + const Loop* loop, SENode* offset, SENode* coefficient) { + assert(loop && "Recurrent add expressions must have a valid loop."); + + // If operands are can't compute then the whole graph is can't compute. + if (offset->IsCantCompute() || coefficient->IsCantCompute()) + return CreateCantComputeNode(); + + const Loop* loop_to_use = nullptr; + if (pretend_equal_[loop]) { + loop_to_use = pretend_equal_[loop]; + } else { + loop_to_use = loop; + } + + std::unique_ptr phi_node{ + new SERecurrentNode(this, loop_to_use)}; + phi_node->AddOffset(offset); + phi_node->AddCoefficient(coefficient); + + return GetCachedOrAdd(std::move(phi_node)); +} + +SENode* ScalarEvolutionAnalysis::AnalyzeMultiplyOp( + const Instruction* multiply) { + assert(multiply->opcode() == SpvOp::SpvOpIMul && + "Multiply node did not come from a multiply instruction"); + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + + SENode* op1 = + AnalyzeInstruction(def_use->GetDef(multiply->GetSingleWordInOperand(0))); + SENode* op2 = + AnalyzeInstruction(def_use->GetDef(multiply->GetSingleWordInOperand(1))); + + return CreateMultiplyNode(op1, op2); +} + +SENode* ScalarEvolutionAnalysis::CreateMultiplyNode(SENode* operand_1, + SENode* operand_2) { + // If operands are can't compute then the whole graph is can't compute. + if (operand_1->IsCantCompute() || operand_2->IsCantCompute()) + return CreateCantComputeNode(); + + if (operand_1->GetType() == SENode::Constant && + operand_2->GetType() == SENode::Constant) { + return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() * + operand_2->AsSEConstantNode()->FoldToSingleValue()); + } + + std::unique_ptr multiply_node{new SEMultiplyNode(this)}; + + multiply_node->AddChild(operand_1); + multiply_node->AddChild(operand_2); + + return GetCachedOrAdd(std::move(multiply_node)); +} + +SENode* ScalarEvolutionAnalysis::CreateSubtraction(SENode* operand_1, + SENode* operand_2) { + // Fold if both operands are constant. + if (operand_1->GetType() == SENode::Constant && + operand_2->GetType() == SENode::Constant) { + return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() - + operand_2->AsSEConstantNode()->FoldToSingleValue()); + } + + return CreateAddNode(operand_1, CreateNegation(operand_2)); +} + +SENode* ScalarEvolutionAnalysis::CreateAddNode(SENode* operand_1, + SENode* operand_2) { + // Fold if both operands are constant and the |simplify| flag is true. + if (operand_1->GetType() == SENode::Constant && + operand_2->GetType() == SENode::Constant) { + return CreateConstant(operand_1->AsSEConstantNode()->FoldToSingleValue() + + operand_2->AsSEConstantNode()->FoldToSingleValue()); + } + + // If operands are can't compute then the whole graph is can't compute. + if (operand_1->IsCantCompute() || operand_2->IsCantCompute()) + return CreateCantComputeNode(); + + std::unique_ptr add_node{new SEAddNode(this)}; + + add_node->AddChild(operand_1); + add_node->AddChild(operand_2); + + return GetCachedOrAdd(std::move(add_node)); +} + +SENode* ScalarEvolutionAnalysis::AnalyzeInstruction(const Instruction* inst) { + auto itr = recurrent_node_map_.find(inst); + if (itr != recurrent_node_map_.end()) return itr->second; + + SENode* output = nullptr; + switch (inst->opcode()) { + case SpvOp::SpvOpPhi: { + output = AnalyzePhiInstruction(inst); + break; + } + case SpvOp::SpvOpConstant: + case SpvOp::SpvOpConstantNull: { + output = AnalyzeConstant(inst); + break; + } + case SpvOp::SpvOpISub: + case SpvOp::SpvOpIAdd: { + output = AnalyzeAddOp(inst); + break; + } + case SpvOp::SpvOpIMul: { + output = AnalyzeMultiplyOp(inst); + break; + } + default: { + output = CreateValueUnknownNode(inst); + break; + } + } + + return output; +} + +SENode* ScalarEvolutionAnalysis::AnalyzeConstant(const Instruction* inst) { + if (inst->opcode() == SpvOp::SpvOpConstantNull) return CreateConstant(0); + + assert(inst->opcode() == SpvOp::SpvOpConstant); + assert(inst->NumInOperands() == 1); + int64_t value = 0; + + // Look up the instruction in the constant manager. + const analysis::Constant* constant = + context_->get_constant_mgr()->FindDeclaredConstant(inst->result_id()); + + if (!constant) return CreateCantComputeNode(); + + const analysis::IntConstant* int_constant = constant->AsIntConstant(); + + // Exit out if it is a 64 bit integer. + if (!int_constant || int_constant->words().size() != 1) + return CreateCantComputeNode(); + + if (int_constant->type()->AsInteger()->IsSigned()) { + value = int_constant->GetS32BitValue(); + } else { + value = int_constant->GetU32BitValue(); + } + + return CreateConstant(value); +} + +// Handles both addition and subtraction. If the |sub| flag is set then the +// addition will be op1+(-op2) otherwise op1+op2. +SENode* ScalarEvolutionAnalysis::AnalyzeAddOp(const Instruction* inst) { + assert((inst->opcode() == SpvOp::SpvOpIAdd || + inst->opcode() == SpvOp::SpvOpISub) && + "Add node must be created from a OpIAdd or OpISub instruction"); + + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + + SENode* op1 = + AnalyzeInstruction(def_use->GetDef(inst->GetSingleWordInOperand(0))); + + SENode* op2 = + AnalyzeInstruction(def_use->GetDef(inst->GetSingleWordInOperand(1))); + + // To handle subtraction we wrap the second operand in a unary negation node. + if (inst->opcode() == SpvOp::SpvOpISub) { + op2 = CreateNegation(op2); + } + + return CreateAddNode(op1, op2); +} + +SENode* ScalarEvolutionAnalysis::AnalyzePhiInstruction(const Instruction* phi) { + // The phi should only have two incoming value pairs. + if (phi->NumInOperands() != 4) { + return CreateCantComputeNode(); + } + + analysis::DefUseManager* def_use = context_->get_def_use_mgr(); + + // Get the basic block this instruction belongs to. + BasicBlock* basic_block = + context_->get_instr_block(const_cast(phi)); + + // And then the function that the basic blocks belongs to. + Function* function = basic_block->GetParent(); + + // Use the function to get the loop descriptor. + LoopDescriptor* loop_descriptor = context_->GetLoopDescriptor(function); + + // We only handle phis in loops at the moment. + if (!loop_descriptor) return CreateCantComputeNode(); + + // Get the innermost loop which this block belongs to. + Loop* loop = (*loop_descriptor)[basic_block->id()]; + + // If the loop doesn't exist or doesn't have a preheader or latch block, exit + // out. + if (!loop || !loop->GetLatchBlock() || !loop->GetPreHeaderBlock() || + loop->GetHeaderBlock() != basic_block) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + const Loop* loop_to_use = nullptr; + if (pretend_equal_[loop]) { + loop_to_use = pretend_equal_[loop]; + } else { + loop_to_use = loop; + } + std::unique_ptr phi_node{ + new SERecurrentNode(this, loop_to_use)}; + + // We add the node to this map to allow it to be returned before the node is + // fully built. This is needed as the subsequent call to AnalyzeInstruction + // could lead back to this |phi| instruction so we return the pointer + // immediately in AnalyzeInstruction to break the recursion. + recurrent_node_map_[phi] = phi_node.get(); + + // Traverse the operands of the instruction an create new nodes for each one. + for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) { + uint32_t value_id = phi->GetSingleWordInOperand(i); + uint32_t incoming_label_id = phi->GetSingleWordInOperand(i + 1); + + Instruction* value_inst = def_use->GetDef(value_id); + SENode* value_node = AnalyzeInstruction(value_inst); + + // If any operand is CantCompute then the whole graph is CantCompute. + if (value_node->IsCantCompute()) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + // If the value is coming from the preheader block then the value is the + // initial value of the phi. + if (incoming_label_id == loop->GetPreHeaderBlock()->id()) { + phi_node->AddOffset(value_node); + } else if (incoming_label_id == loop->GetLatchBlock()->id()) { + // Assumed to be in the form of step + phi. + if (value_node->GetType() != SENode::Add) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + SENode* step_node = nullptr; + SENode* phi_operand = nullptr; + SENode* operand_1 = value_node->GetChild(0); + SENode* operand_2 = value_node->GetChild(1); + + // Find which node is the step term. + if (!operand_1->AsSERecurrentNode()) + step_node = operand_1; + else if (!operand_2->AsSERecurrentNode()) + step_node = operand_2; + + // Find which node is the recurrent expression. + if (operand_1->AsSERecurrentNode()) + phi_operand = operand_1; + else if (operand_2->AsSERecurrentNode()) + phi_operand = operand_2; + + // If it is not in the form step + phi exit out. + if (!(step_node && phi_operand)) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + // If the phi operand is not the same phi node exit out. + if (phi_operand != phi_node.get()) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + if (!IsLoopInvariant(loop, step_node)) + return recurrent_node_map_[phi] = CreateCantComputeNode(); + + phi_node->AddCoefficient(step_node); + } + } + + // Once the node is fully built we update the map with the version from the + // cache (if it has already been added to the cache). + return recurrent_node_map_[phi] = GetCachedOrAdd(std::move(phi_node)); +} + +SENode* ScalarEvolutionAnalysis::CreateValueUnknownNode( + const Instruction* inst) { + std::unique_ptr load_node{ + new SEValueUnknown(this, inst->result_id())}; + return GetCachedOrAdd(std::move(load_node)); +} + +SENode* ScalarEvolutionAnalysis::CreateCantComputeNode() { + return cached_cant_compute_; +} + +// Add the created node into the cache of nodes. If it already exists return it. +SENode* ScalarEvolutionAnalysis::GetCachedOrAdd( + std::unique_ptr prospective_node) { + auto itr = node_cache_.find(prospective_node); + if (itr != node_cache_.end()) { + return (*itr).get(); + } + + SENode* raw_ptr_to_node = prospective_node.get(); + node_cache_.insert(std::move(prospective_node)); + return raw_ptr_to_node; +} + +bool ScalarEvolutionAnalysis::IsLoopInvariant(const Loop* loop, + const SENode* node) const { + for (auto itr = node->graph_cbegin(); itr != node->graph_cend(); ++itr) { + if (const SERecurrentNode* rec = itr->AsSERecurrentNode()) { + const BasicBlock* header = rec->GetLoop()->GetHeaderBlock(); + + // If the loop which the recurrent expression belongs to is either |loop + // or a nested loop inside |loop| then we assume it is variant. + if (loop->IsInsideLoop(header)) { + return false; + } + } else if (const SEValueUnknown* unknown = itr->AsSEValueUnknown()) { + // If the instruction is inside the loop we conservatively assume it is + // loop variant. + if (loop->IsInsideLoop(unknown->ResultId())) return false; + } + } + + return true; +} + +SENode* ScalarEvolutionAnalysis::GetCoefficientFromRecurrentTerm( + SENode* node, const Loop* loop) { + // Traverse the DAG to find the recurrent expression belonging to |loop|. + for (auto itr = node->graph_begin(); itr != node->graph_end(); ++itr) { + SERecurrentNode* rec = itr->AsSERecurrentNode(); + if (rec && rec->GetLoop() == loop) { + return rec->GetCoefficient(); + } + } + return CreateConstant(0); +} + +SENode* ScalarEvolutionAnalysis::UpdateChildNode(SENode* parent, + SENode* old_child, + SENode* new_child) { + // Only handles add. + if (parent->GetType() != SENode::Add) return parent; + + std::vector new_children; + for (SENode* child : *parent) { + if (child == old_child) { + new_children.push_back(new_child); + } else { + new_children.push_back(child); + } + } + + std::unique_ptr add_node{new SEAddNode(this)}; + for (SENode* child : new_children) { + add_node->AddChild(child); + } + + return SimplifyExpression(GetCachedOrAdd(std::move(add_node))); +} + +// Rebuild the |node| eliminating, if it exists, the recurrent term which +// belongs to the |loop|. +SENode* ScalarEvolutionAnalysis::BuildGraphWithoutRecurrentTerm( + SENode* node, const Loop* loop) { + // If the node is already a recurrent expression belonging to loop then just + // return the offset. + SERecurrentNode* recurrent = node->AsSERecurrentNode(); + if (recurrent) { + if (recurrent->GetLoop() == loop) { + return recurrent->GetOffset(); + } else { + return node; + } + } + + std::vector new_children; + // Otherwise find the recurrent node in the children of this node. + for (auto itr : *node) { + recurrent = itr->AsSERecurrentNode(); + if (recurrent && recurrent->GetLoop() == loop) { + new_children.push_back(recurrent->GetOffset()); + } else { + new_children.push_back(itr); + } + } + + std::unique_ptr add_node{new SEAddNode(this)}; + for (SENode* child : new_children) { + add_node->AddChild(child); + } + + return SimplifyExpression(GetCachedOrAdd(std::move(add_node))); +} + +// Return the recurrent term belonging to |loop| if it appears in the graph +// starting at |node| or null if it doesn't. +SERecurrentNode* ScalarEvolutionAnalysis::GetRecurrentTerm(SENode* node, + const Loop* loop) { + for (auto itr = node->graph_begin(); itr != node->graph_end(); ++itr) { + SERecurrentNode* rec = itr->AsSERecurrentNode(); + if (rec && rec->GetLoop() == loop) { + return rec; + } + } + return nullptr; +} +std::string SENode::AsString() const { + switch (GetType()) { + case Constant: + return "Constant"; + case RecurrentAddExpr: + return "RecurrentAddExpr"; + case Add: + return "Add"; + case Negative: + return "Negative"; + case Multiply: + return "Multiply"; + case ValueUnknown: + return "Value Unknown"; + case CanNotCompute: + return "Can not compute"; + } + return "NULL"; +} + +bool SENode::operator==(const SENode& other) const { + if (GetType() != other.GetType()) return false; + + if (other.GetChildren().size() != children_.size()) return false; + + const SERecurrentNode* this_as_recurrent = AsSERecurrentNode(); + + // Check the children are the same, for SERecurrentNodes we need to check the + // offset and coefficient manually as the child vector is sorted by ids so the + // offset/coefficient information is lost. + if (!this_as_recurrent) { + for (size_t index = 0; index < children_.size(); ++index) { + if (other.GetChildren()[index] != children_[index]) return false; + } + } else { + const SERecurrentNode* other_as_recurrent = other.AsSERecurrentNode(); + + // We've already checked the types are the same, this should not fail if + // this->AsSERecurrentNode() succeeded. + assert(other_as_recurrent); + + if (this_as_recurrent->GetCoefficient() != + other_as_recurrent->GetCoefficient()) + return false; + + if (this_as_recurrent->GetOffset() != other_as_recurrent->GetOffset()) + return false; + + if (this_as_recurrent->GetLoop() != other_as_recurrent->GetLoop()) + return false; + } + + // If we're dealing with a value unknown node check both nodes were created by + // the same instruction. + if (GetType() == SENode::ValueUnknown) { + if (AsSEValueUnknown()->ResultId() != + other.AsSEValueUnknown()->ResultId()) { + return false; + } + } + + if (AsSEConstantNode()) { + if (AsSEConstantNode()->FoldToSingleValue() != + other.AsSEConstantNode()->FoldToSingleValue()) + return false; + } + + return true; +} + +bool SENode::operator!=(const SENode& other) const { return !(*this == other); } + +namespace { +// Helper functions to insert 32/64 bit values into the 32 bit hash string. This +// allows us to add pointers to the string by reinterpreting the pointers as +// uintptr_t. PushToString will deduce the type, call sizeof on it and use +// that size to call into the correct PushToStringImpl functor depending on +// whether it is 32 or 64 bit. + +template +struct PushToStringImpl; + +template +struct PushToStringImpl { + void operator()(T id, std::u32string* str) { + str->push_back(static_cast(id >> 32)); + str->push_back(static_cast(id)); + } +}; + +template +struct PushToStringImpl { + void operator()(T id, std::u32string* str) { + str->push_back(static_cast(id)); + } +}; + +template +static void PushToString(T id, std::u32string* str) { + PushToStringImpl{}(id, str); +} + +} // namespace + +// Implements the hashing of SENodes. +size_t SENodeHash::operator()(const SENode* node) const { + // Concatinate the terms into a string which we can hash. + std::u32string hash_string{}; + + // Hashing the type as a string is safer than hashing the enum as the enum is + // very likely to collide with constants. + for (char ch : node->AsString()) { + hash_string.push_back(static_cast(ch)); + } + + // We just ignore the literal value unless it is a constant. + if (node->GetType() == SENode::Constant) + PushToString(node->AsSEConstantNode()->FoldToSingleValue(), &hash_string); + + const SERecurrentNode* recurrent = node->AsSERecurrentNode(); + + // If we're dealing with a recurrent expression hash the loop as well so that + // nested inductions like i=0,i++ and j=0,j++ correspond to different nodes. + if (recurrent) { + PushToString(reinterpret_cast(recurrent->GetLoop()), + &hash_string); + + // Recurrent expressions can't be hashed using the normal method as the + // order of coefficient and offset matters to the hash. + PushToString(reinterpret_cast(recurrent->GetCoefficient()), + &hash_string); + PushToString(reinterpret_cast(recurrent->GetOffset()), + &hash_string); + + return std::hash{}(hash_string); + } + + // Hash the result id of the original instruction which created this node if + // it is a value unknown node. + if (node->GetType() == SENode::ValueUnknown) { + PushToString(node->AsSEValueUnknown()->ResultId(), &hash_string); + } + + // Hash the pointers of the child nodes, each SENode has a unique pointer + // associated with it. + const std::vector& children = node->GetChildren(); + for (const SENode* child : children) { + PushToString(reinterpret_cast(child), &hash_string); + } + + return std::hash{}(hash_string); +} + +// This overload is the actual overload used by the node_cache_ set. +size_t SENodeHash::operator()(const std::unique_ptr& node) const { + return this->operator()(node.get()); +} + +void SENode::DumpDot(std::ostream& out, bool recurse) const { + size_t unique_id = std::hash{}(this); + out << unique_id << " [label=\"" << AsString() << " "; + if (GetType() == SENode::Constant) { + out << "\nwith value: " << this->AsSEConstantNode()->FoldToSingleValue(); + } + out << "\"]\n"; + for (const SENode* child : children_) { + size_t child_unique_id = std::hash{}(child); + out << unique_id << " -> " << child_unique_id << " \n"; + if (recurse) child->DumpDot(out, true); + } +} + +namespace { +class IsGreaterThanZero { + public: + explicit IsGreaterThanZero(IRContext* context) : context_(context) {} + + // Determine if the value of |node| is always strictly greater than zero if + // |or_equal_zero| is false or greater or equal to zero if |or_equal_zero| is + // true. It returns true is the evaluation was able to conclude something, in + // which case the result is stored in |result|. + // The algorithm work by going through all the nodes and determine the + // sign of each of them. + bool Eval(const SENode* node, bool or_equal_zero, bool* result) { + *result = false; + switch (Visit(node)) { + case Signedness::kPositiveOrNegative: { + return false; + } + case Signedness::kStrictlyNegative: { + *result = false; + break; + } + case Signedness::kNegative: { + if (!or_equal_zero) { + return false; + } + *result = false; + break; + } + case Signedness::kStrictlyPositive: { + *result = true; + break; + } + case Signedness::kPositive: { + if (!or_equal_zero) { + return false; + } + *result = true; + break; + } + } + return true; + } + + private: + enum class Signedness { + kPositiveOrNegative, // Yield a value positive or negative. + kStrictlyNegative, // Yield a value strictly less than 0. + kNegative, // Yield a value less or equal to 0. + kStrictlyPositive, // Yield a value strictly greater than 0. + kPositive // Yield a value greater or equal to 0. + }; + + // Combine the signedness according to arithmetic rules of a given operator. + using Combiner = std::function; + + // Returns a functor to interpret the signedness of 2 expressions as if they + // were added. + Combiner GetAddCombiner() const { + return [](Signedness lhs, Signedness rhs) { + switch (lhs) { + case Signedness::kPositiveOrNegative: + break; + case Signedness::kStrictlyNegative: + if (rhs == Signedness::kStrictlyNegative || + rhs == Signedness::kNegative) + return lhs; + break; + case Signedness::kNegative: { + if (rhs == Signedness::kStrictlyNegative) + return Signedness::kStrictlyNegative; + if (rhs == Signedness::kNegative) return Signedness::kNegative; + break; + } + case Signedness::kStrictlyPositive: { + if (rhs == Signedness::kStrictlyPositive || + rhs == Signedness::kPositive) { + return Signedness::kStrictlyPositive; + } + break; + } + case Signedness::kPositive: { + if (rhs == Signedness::kStrictlyPositive) + return Signedness::kStrictlyPositive; + if (rhs == Signedness::kPositive) return Signedness::kPositive; + break; + } + } + return Signedness::kPositiveOrNegative; + }; + } + + // Returns a functor to interpret the signedness of 2 expressions as if they + // were multiplied. + Combiner GetMulCombiner() const { + return [](Signedness lhs, Signedness rhs) { + switch (lhs) { + case Signedness::kPositiveOrNegative: + break; + case Signedness::kStrictlyNegative: { + switch (rhs) { + case Signedness::kPositiveOrNegative: { + break; + } + case Signedness::kStrictlyNegative: { + return Signedness::kStrictlyPositive; + } + case Signedness::kNegative: { + return Signedness::kPositive; + } + case Signedness::kStrictlyPositive: { + return Signedness::kStrictlyNegative; + } + case Signedness::kPositive: { + return Signedness::kNegative; + } + } + break; + } + case Signedness::kNegative: { + switch (rhs) { + case Signedness::kPositiveOrNegative: { + break; + } + case Signedness::kStrictlyNegative: + case Signedness::kNegative: { + return Signedness::kPositive; + } + case Signedness::kStrictlyPositive: + case Signedness::kPositive: { + return Signedness::kNegative; + } + } + break; + } + case Signedness::kStrictlyPositive: { + return rhs; + } + case Signedness::kPositive: { + switch (rhs) { + case Signedness::kPositiveOrNegative: { + break; + } + case Signedness::kStrictlyNegative: + case Signedness::kNegative: { + return Signedness::kNegative; + } + case Signedness::kStrictlyPositive: + case Signedness::kPositive: { + return Signedness::kPositive; + } + } + break; + } + } + return Signedness::kPositiveOrNegative; + }; + } + + Signedness Visit(const SENode* node) { + switch (node->GetType()) { + case SENode::Constant: + return Visit(node->AsSEConstantNode()); + break; + case SENode::RecurrentAddExpr: + return Visit(node->AsSERecurrentNode()); + break; + case SENode::Negative: + return Visit(node->AsSENegative()); + break; + case SENode::CanNotCompute: + return Visit(node->AsSECantCompute()); + break; + case SENode::ValueUnknown: + return Visit(node->AsSEValueUnknown()); + break; + case SENode::Add: + return VisitExpr(node, GetAddCombiner()); + break; + case SENode::Multiply: + return VisitExpr(node, GetMulCombiner()); + break; + } + return Signedness::kPositiveOrNegative; + } + + // Returns the signedness of a constant |node|. + Signedness Visit(const SEConstantNode* node) { + if (0 == node->FoldToSingleValue()) return Signedness::kPositive; + if (0 < node->FoldToSingleValue()) return Signedness::kStrictlyPositive; + if (0 > node->FoldToSingleValue()) return Signedness::kStrictlyNegative; + return Signedness::kPositiveOrNegative; + } + + // Returns the signedness of an unknown |node| based on its type. + Signedness Visit(const SEValueUnknown* node) { + Instruction* insn = context_->get_def_use_mgr()->GetDef(node->ResultId()); + analysis::Type* type = context_->get_type_mgr()->GetType(insn->type_id()); + assert(type && "Can't retrieve a type for the instruction"); + analysis::Integer* int_type = type->AsInteger(); + assert(type && "Can't retrieve an integer type for the instruction"); + return int_type->IsSigned() ? Signedness::kPositiveOrNegative + : Signedness::kPositive; + } + + // Returns the signedness of a recurring expression. + Signedness Visit(const SERecurrentNode* node) { + Signedness coeff_sign = Visit(node->GetCoefficient()); + // SERecurrentNode represent an affine expression in the range [0, + // loop_bound], so the result cannot be strictly positive or negative. + switch (coeff_sign) { + default: + break; + case Signedness::kStrictlyNegative: + coeff_sign = Signedness::kNegative; + break; + case Signedness::kStrictlyPositive: + coeff_sign = Signedness::kPositive; + break; + } + return GetAddCombiner()(coeff_sign, Visit(node->GetOffset())); + } + + // Returns the signedness of a negation |node|. + Signedness Visit(const SENegative* node) { + switch (Visit(*node->begin())) { + case Signedness::kPositiveOrNegative: { + return Signedness::kPositiveOrNegative; + } + case Signedness::kStrictlyNegative: { + return Signedness::kStrictlyPositive; + } + case Signedness::kNegative: { + return Signedness::kPositive; + } + case Signedness::kStrictlyPositive: { + return Signedness::kStrictlyNegative; + } + case Signedness::kPositive: { + return Signedness::kNegative; + } + } + return Signedness::kPositiveOrNegative; + } + + Signedness Visit(const SECantCompute*) { + return Signedness::kPositiveOrNegative; + } + + // Returns the signedness of a binary expression by using the combiner + // |reduce|. + Signedness VisitExpr( + const SENode* node, + std::function reduce) { + Signedness result = Visit(*node->begin()); + for (const SENode* operand : make_range(++node->begin(), node->end())) { + if (result == Signedness::kPositiveOrNegative) { + return Signedness::kPositiveOrNegative; + } + result = reduce(result, Visit(operand)); + } + return result; + } + + IRContext* context_; +}; +} // namespace + +bool ScalarEvolutionAnalysis::IsAlwaysGreaterThanZero(SENode* node, + bool* is_gt_zero) const { + return IsGreaterThanZero(context_).Eval(node, false, is_gt_zero); +} + +bool ScalarEvolutionAnalysis::IsAlwaysGreaterOrEqualToZero( + SENode* node, bool* is_ge_zero) const { + return IsGreaterThanZero(context_).Eval(node, true, is_ge_zero); +} + +namespace { + +// Remove |node| from the |mul| chain (of the form A * ... * |node| * ... * Z), +// if |node| is not in the chain, returns the original chain. +static SENode* RemoveOneNodeFromMultiplyChain(SEMultiplyNode* mul, + const SENode* node) { + SENode* lhs = mul->GetChildren()[0]; + SENode* rhs = mul->GetChildren()[1]; + if (lhs == node) { + return rhs; + } + if (rhs == node) { + return lhs; + } + if (lhs->AsSEMultiplyNode()) { + SENode* res = RemoveOneNodeFromMultiplyChain(lhs->AsSEMultiplyNode(), node); + if (res != lhs) + return mul->GetParentAnalysis()->CreateMultiplyNode(res, rhs); + } + if (rhs->AsSEMultiplyNode()) { + SENode* res = RemoveOneNodeFromMultiplyChain(rhs->AsSEMultiplyNode(), node); + if (res != rhs) + return mul->GetParentAnalysis()->CreateMultiplyNode(res, rhs); + } + + return mul; +} +} // namespace + +std::pair SExpression::operator/( + SExpression rhs_wrapper) const { + SENode* lhs = node_; + SENode* rhs = rhs_wrapper.node_; + // Check for division by 0. + if (rhs->AsSEConstantNode() && + !rhs->AsSEConstantNode()->FoldToSingleValue()) { + return {scev_->CreateCantComputeNode(), 0}; + } + + // Trivial case. + if (lhs->AsSEConstantNode() && rhs->AsSEConstantNode()) { + int64_t lhs_value = lhs->AsSEConstantNode()->FoldToSingleValue(); + int64_t rhs_value = rhs->AsSEConstantNode()->FoldToSingleValue(); + return {scev_->CreateConstant(lhs_value / rhs_value), + lhs_value % rhs_value}; + } + + // look for a "c U / U" pattern. + if (lhs->AsSEMultiplyNode()) { + assert(lhs->GetChildren().size() == 2 && + "More than 2 operand for a multiply node."); + SENode* res = RemoveOneNodeFromMultiplyChain(lhs->AsSEMultiplyNode(), rhs); + if (res != lhs) { + return {res, 0}; + } + } + + return {scev_->CreateCantComputeNode(), 0}; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/scalar_analysis.h b/third_party/spirv-tools/source/opt/scalar_analysis.h new file mode 100644 index 0000000..fb6d631 --- /dev/null +++ b/third_party/spirv-tools/source/opt/scalar_analysis.h @@ -0,0 +1,314 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SCALAR_ANALYSIS_H_ +#define SOURCE_OPT_SCALAR_ANALYSIS_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/instruction.h" +#include "source/opt/scalar_analysis_nodes.h" + +namespace spvtools { +namespace opt { + +class IRContext; +class Loop; + +// Manager for the Scalar Evolution analysis. Creates and maintains a DAG of +// scalar operations generated from analysing the use def graph from incoming +// instructions. Each node is hashed as it is added so like node (for instance, +// two induction variables i=0,i++ and j=0,j++) become the same node. After +// creating a DAG with AnalyzeInstruction it can the be simplified into a more +// usable form with SimplifyExpression. +class ScalarEvolutionAnalysis { + public: + explicit ScalarEvolutionAnalysis(IRContext* context); + + // Create a unary negative node on |operand|. + SENode* CreateNegation(SENode* operand); + + // Creates a subtraction between the two operands by adding |operand_1| to the + // negation of |operand_2|. + SENode* CreateSubtraction(SENode* operand_1, SENode* operand_2); + + // Create an addition node between two operands. The |simplify| when set will + // allow the function to return an SEConstant instead of an addition if the + // two input operands are also constant. + SENode* CreateAddNode(SENode* operand_1, SENode* operand_2); + + // Create a multiply node between two operands. + SENode* CreateMultiplyNode(SENode* operand_1, SENode* operand_2); + + // Create a node representing a constant integer. + SENode* CreateConstant(int64_t integer); + + // Create a value unknown node, such as a load. + SENode* CreateValueUnknownNode(const Instruction* inst); + + // Create a CantComputeNode. Used to exit out of analysis. + SENode* CreateCantComputeNode(); + + // Create a new recurrent node with |offset| and |coefficient|, with respect + // to |loop|. + SENode* CreateRecurrentExpression(const Loop* loop, SENode* offset, + SENode* coefficient); + + // Construct the DAG by traversing use def chain of |inst|. + SENode* AnalyzeInstruction(const Instruction* inst); + + // Simplify the |node| by grouping like terms or if contains a recurrent + // expression, rewrite the graph so the whole DAG (from |node| down) is in + // terms of that recurrent expression. + // + // For example. + // Induction variable i=0, i++ would produce Rec(0,1) so i+1 could be + // transformed into Rec(1,1). + // + // X+X*2+Y-Y+34-17 would be transformed into 3*X + 17, where X and Y are + // ValueUnknown nodes (such as a load instruction). + SENode* SimplifyExpression(SENode* node); + + // Add |prospective_node| into the cache and return a raw pointer to it. If + // |prospective_node| is already in the cache just return the raw pointer. + SENode* GetCachedOrAdd(std::unique_ptr prospective_node); + + // Checks that the graph starting from |node| is invariant to the |loop|. + bool IsLoopInvariant(const Loop* loop, const SENode* node) const; + + // Sets |is_gt_zero| to true if |node| represent a value always strictly + // greater than 0. The result of |is_gt_zero| is valid only if the function + // returns true. + bool IsAlwaysGreaterThanZero(SENode* node, bool* is_gt_zero) const; + + // Sets |is_ge_zero| to true if |node| represent a value greater or equals to + // 0. The result of |is_ge_zero| is valid only if the function returns true. + bool IsAlwaysGreaterOrEqualToZero(SENode* node, bool* is_ge_zero) const; + + // Find the recurrent term belonging to |loop| in the graph starting from + // |node| and return the coefficient of that recurrent term. Constant zero + // will be returned if no recurrent could be found. |node| should be in + // simplest form. + SENode* GetCoefficientFromRecurrentTerm(SENode* node, const Loop* loop); + + // Return a rebuilt graph starting from |node| with the recurrent expression + // belonging to |loop| being zeroed out. Returned node will be simplified. + SENode* BuildGraphWithoutRecurrentTerm(SENode* node, const Loop* loop); + + // Return the recurrent term belonging to |loop| if it appears in the graph + // starting at |node| or null if it doesn't. + SERecurrentNode* GetRecurrentTerm(SENode* node, const Loop* loop); + + SENode* UpdateChildNode(SENode* parent, SENode* child, SENode* new_child); + + // The loops in |loop_pair| will be considered the same when constructing + // SERecurrentNode objects. This enables analysing dependencies that will be + // created during loop fusion. + void AddLoopsToPretendAreTheSame( + const std::pair& loop_pair) { + pretend_equal_[std::get<1>(loop_pair)] = std::get<0>(loop_pair); + } + + private: + SENode* AnalyzeConstant(const Instruction* inst); + + // Handles both addition and subtraction. If the |instruction| is OpISub + // then the resulting node will be op1+(-op2) otherwise if it is OpIAdd then + // the result will be op1+op2. |instruction| must be OpIAdd or OpISub. + SENode* AnalyzeAddOp(const Instruction* instruction); + + SENode* AnalyzeMultiplyOp(const Instruction* multiply); + + SENode* AnalyzePhiInstruction(const Instruction* phi); + + IRContext* context_; + + // A map of instructions to SENodes. This is used to track recurrent + // expressions as they are added when analyzing instructions. Recurrent + // expressions come from phi nodes which by nature can include recursion so we + // check if nodes have already been built when analyzing instructions. + std::map recurrent_node_map_; + + // On creation we create and cache the CantCompute node so we not need to + // perform a needless create step. + SENode* cached_cant_compute_; + + // Helper functor to allow two unique_ptr to nodes to be compare. Only + // needed + // for the unordered_set implementation. + struct NodePointersEquality { + bool operator()(const std::unique_ptr& lhs, + const std::unique_ptr& rhs) const { + return *lhs == *rhs; + } + }; + + // Cache of nodes. All pointers to the nodes are references to the memory + // managed by they set. + std::unordered_set, SENodeHash, NodePointersEquality> + node_cache_; + + // Loops that should be considered the same for performing analysis for loop + // fusion. + std::map pretend_equal_; +}; + +// Wrapping class to manipulate SENode pointer using + - * / operators. +class SExpression { + public: + // Implicit on purpose ! + SExpression(SENode* node) + : node_(node->GetParentAnalysis()->SimplifyExpression(node)), + scev_(node->GetParentAnalysis()) {} + + inline operator SENode*() const { return node_; } + inline SENode* operator->() const { return node_; } + const SENode& operator*() const { return *node_; } + + inline ScalarEvolutionAnalysis* GetScalarEvolutionAnalysis() const { + return scev_; + } + + inline SExpression operator+(SENode* rhs) const; + template ::value, int>::type = 0> + inline SExpression operator+(T integer) const; + inline SExpression operator+(SExpression rhs) const; + + inline SExpression operator-() const; + inline SExpression operator-(SENode* rhs) const; + template ::value, int>::type = 0> + inline SExpression operator-(T integer) const; + inline SExpression operator-(SExpression rhs) const; + + inline SExpression operator*(SENode* rhs) const; + template ::value, int>::type = 0> + inline SExpression operator*(T integer) const; + inline SExpression operator*(SExpression rhs) const; + + template ::value, int>::type = 0> + inline std::pair operator/(T integer) const; + // Try to perform a division. Returns the pair . If it fails to simplify it, the function returns a + // CanNotCompute node. + std::pair operator/(SExpression rhs) const; + + private: + SENode* node_; + ScalarEvolutionAnalysis* scev_; +}; + +inline SExpression SExpression::operator+(SENode* rhs) const { + return scev_->CreateAddNode(node_, rhs); +} + +template ::value, int>::type> +inline SExpression SExpression::operator+(T integer) const { + return *this + scev_->CreateConstant(integer); +} + +inline SExpression SExpression::operator+(SExpression rhs) const { + return *this + rhs.node_; +} + +inline SExpression SExpression::operator-() const { + return scev_->CreateNegation(node_); +} + +inline SExpression SExpression::operator-(SENode* rhs) const { + return *this + scev_->CreateNegation(rhs); +} + +template ::value, int>::type> +inline SExpression SExpression::operator-(T integer) const { + return *this - scev_->CreateConstant(integer); +} + +inline SExpression SExpression::operator-(SExpression rhs) const { + return *this - rhs.node_; +} + +inline SExpression SExpression::operator*(SENode* rhs) const { + return scev_->CreateMultiplyNode(node_, rhs); +} + +template ::value, int>::type> +inline SExpression SExpression::operator*(T integer) const { + return *this * scev_->CreateConstant(integer); +} + +inline SExpression SExpression::operator*(SExpression rhs) const { + return *this * rhs.node_; +} + +template ::value, int>::type> +inline std::pair SExpression::operator/(T integer) const { + return *this / scev_->CreateConstant(integer); +} + +template ::value, int>::type> +inline SExpression operator+(T lhs, SExpression rhs) { + return rhs + lhs; +} +inline SExpression operator+(SENode* lhs, SExpression rhs) { return rhs + lhs; } + +template ::value, int>::type> +inline SExpression operator-(T lhs, SExpression rhs) { + // NOLINTNEXTLINE(whitespace/braces) + return SExpression{rhs.GetScalarEvolutionAnalysis()->CreateConstant(lhs)} - + rhs; +} +inline SExpression operator-(SENode* lhs, SExpression rhs) { + // NOLINTNEXTLINE(whitespace/braces) + return SExpression{lhs} - rhs; +} + +template ::value, int>::type> +inline SExpression operator*(T lhs, SExpression rhs) { + return rhs * lhs; +} +inline SExpression operator*(SENode* lhs, SExpression rhs) { return rhs * lhs; } + +template ::value, int>::type> +inline std::pair operator/(T lhs, SExpression rhs) { + // NOLINTNEXTLINE(whitespace/braces) + return SExpression{rhs.GetScalarEvolutionAnalysis()->CreateConstant(lhs)} / + rhs; +} +inline std::pair operator/(SENode* lhs, SExpression rhs) { + // NOLINTNEXTLINE(whitespace/braces) + return SExpression{lhs} / rhs; +} + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_SCALAR_ANALYSIS_H_ diff --git a/third_party/spirv-tools/source/opt/scalar_analysis_nodes.h b/third_party/spirv-tools/source/opt/scalar_analysis_nodes.h new file mode 100644 index 0000000..b0e3fef --- /dev/null +++ b/third_party/spirv-tools/source/opt/scalar_analysis_nodes.h @@ -0,0 +1,347 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASI, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SCALAR_ANALYSIS_NODES_H_ +#define SOURCE_OPT_SCALAR_ANALYSIS_NODES_H_ + +#include +#include +#include +#include + +#include "source/opt/tree_iterator.h" + +namespace spvtools { +namespace opt { + +class Loop; +class ScalarEvolutionAnalysis; +class SEConstantNode; +class SERecurrentNode; +class SEAddNode; +class SEMultiplyNode; +class SENegative; +class SEValueUnknown; +class SECantCompute; + +// Abstract class representing a node in the scalar evolution DAG. Each node +// contains a vector of pointers to its children and each subclass of SENode +// implements GetType and an As method to allow casting. SENodes can be hashed +// using the SENodeHash functor. The vector of children is sorted when a node is +// added. This is important as it allows the hash of X+Y to be the same as Y+X. +class SENode { + public: + enum SENodeType { + Constant, + RecurrentAddExpr, + Add, + Multiply, + Negative, + ValueUnknown, + CanNotCompute + }; + + using ChildContainerType = std::vector; + + explicit SENode(ScalarEvolutionAnalysis* parent_analysis) + : parent_analysis_(parent_analysis), unique_id_(++NumberOfNodes) {} + + virtual SENodeType GetType() const = 0; + + virtual ~SENode() {} + + virtual inline void AddChild(SENode* child) { + // If this is a constant node, assert. + if (AsSEConstantNode()) { + assert(false && "Trying to add a child node to a constant!"); + } + + // Find the first point in the vector where |child| is greater than the node + // currently in the vector. + auto find_first_less_than = [child](const SENode* node) { + return child->unique_id_ <= node->unique_id_; + }; + + auto position = std::find_if_not(children_.begin(), children_.end(), + find_first_less_than); + // Children are sorted so the hashing and equality operator will be the same + // for a node with the same children. X+Y should be the same as Y+X. + children_.insert(position, child); + } + + // Get the type as an std::string. This is used to represent the node in the + // dot output and is used to hash the type as well. + std::string AsString() const; + + // Dump the SENode and its immediate children, if |recurse| is true then it + // will recurse through all children to print the DAG starting from this node + // as a root. + void DumpDot(std::ostream& out, bool recurse = false) const; + + // Checks if two nodes are the same by hashing them. + bool operator==(const SENode& other) const; + + // Checks if two nodes are not the same by comparing the hashes. + bool operator!=(const SENode& other) const; + + // Return the child node at |index|. + inline SENode* GetChild(size_t index) { return children_[index]; } + inline const SENode* GetChild(size_t index) const { return children_[index]; } + + // Iterator to iterate over the child nodes. + using iterator = ChildContainerType::iterator; + using const_iterator = ChildContainerType::const_iterator; + + // Iterate over immediate child nodes. + iterator begin() { return children_.begin(); } + iterator end() { return children_.end(); } + + // Constant overloads for iterating over immediate child nodes. + const_iterator begin() const { return children_.cbegin(); } + const_iterator end() const { return children_.cend(); } + const_iterator cbegin() { return children_.cbegin(); } + const_iterator cend() { return children_.cend(); } + + // Collect all the recurrent nodes in this SENode + std::vector CollectRecurrentNodes() { + std::vector recurrent_nodes{}; + + if (auto recurrent_node = AsSERecurrentNode()) { + recurrent_nodes.push_back(recurrent_node); + } + + for (auto child : GetChildren()) { + auto child_recurrent_nodes = child->CollectRecurrentNodes(); + recurrent_nodes.insert(recurrent_nodes.end(), + child_recurrent_nodes.begin(), + child_recurrent_nodes.end()); + } + + return recurrent_nodes; + } + + // Collect all the value unknown nodes in this SENode + std::vector CollectValueUnknownNodes() { + std::vector value_unknown_nodes{}; + + if (auto value_unknown_node = AsSEValueUnknown()) { + value_unknown_nodes.push_back(value_unknown_node); + } + + for (auto child : GetChildren()) { + auto child_value_unknown_nodes = child->CollectValueUnknownNodes(); + value_unknown_nodes.insert(value_unknown_nodes.end(), + child_value_unknown_nodes.begin(), + child_value_unknown_nodes.end()); + } + + return value_unknown_nodes; + } + + // Iterator to iterate over the entire DAG. Even though we are using the tree + // iterator it should still be safe to iterate over. However, nodes with + // multiple parents will be visited multiple times, unlike in a tree. + using dag_iterator = TreeDFIterator; + using const_dag_iterator = TreeDFIterator; + + // Iterate over all child nodes in the graph. + dag_iterator graph_begin() { return dag_iterator(this); } + dag_iterator graph_end() { return dag_iterator(); } + const_dag_iterator graph_begin() const { return graph_cbegin(); } + const_dag_iterator graph_end() const { return graph_cend(); } + const_dag_iterator graph_cbegin() const { return const_dag_iterator(this); } + const_dag_iterator graph_cend() const { return const_dag_iterator(); } + + // Return the vector of immediate children. + const ChildContainerType& GetChildren() const { return children_; } + ChildContainerType& GetChildren() { return children_; } + + // Return true if this node is a cant compute node. + bool IsCantCompute() const { return GetType() == CanNotCompute; } + +// Implements a casting method for each type. +// clang-format off +#define DeclareCastMethod(target) \ + virtual target* As##target() { return nullptr; } \ + virtual const target* As##target() const { return nullptr; } + DeclareCastMethod(SEConstantNode) + DeclareCastMethod(SERecurrentNode) + DeclareCastMethod(SEAddNode) + DeclareCastMethod(SEMultiplyNode) + DeclareCastMethod(SENegative) + DeclareCastMethod(SEValueUnknown) + DeclareCastMethod(SECantCompute) +#undef DeclareCastMethod + + // Get the analysis which has this node in its cache. + inline ScalarEvolutionAnalysis* GetParentAnalysis() const { + return parent_analysis_; + } + + protected: + ChildContainerType children_; + + ScalarEvolutionAnalysis* parent_analysis_; + + // The unique id of this node, assigned on creation by incrementing the static + // node count. + uint32_t unique_id_; + + // The number of nodes created. + static uint32_t NumberOfNodes; +}; +// clang-format on + +// Function object to handle the hashing of SENodes. Hashing algorithm hashes +// the type (as a string), the literal value of any constants, and the child +// pointers which are assumed to be unique. +struct SENodeHash { + size_t operator()(const std::unique_ptr& node) const; + size_t operator()(const SENode* node) const; +}; + +// A node representing a constant integer. +class SEConstantNode : public SENode { + public: + SEConstantNode(ScalarEvolutionAnalysis* parent_analysis, int64_t value) + : SENode(parent_analysis), literal_value_(value) {} + + SENodeType GetType() const final { return Constant; } + + int64_t FoldToSingleValue() const { return literal_value_; } + + SEConstantNode* AsSEConstantNode() override { return this; } + const SEConstantNode* AsSEConstantNode() const override { return this; } + + inline void AddChild(SENode*) final { + assert(false && "Attempting to add a child to a constant node!"); + } + + protected: + int64_t literal_value_; +}; + +// A node representing a recurrent expression in the code. A recurrent +// expression is an expression whose value can be expressed as a linear +// expression of the loop iterations. Such as an induction variable. The actual +// value of a recurrent expression is coefficent_ * iteration + offset_, hence +// an induction variable i=0, i++ becomes a recurrent expression with an offset +// of zero and a coefficient of one. +class SERecurrentNode : public SENode { + public: + SERecurrentNode(ScalarEvolutionAnalysis* parent_analysis, const Loop* loop) + : SENode(parent_analysis), loop_(loop) {} + + SENodeType GetType() const final { return RecurrentAddExpr; } + + inline void AddCoefficient(SENode* child) { + coefficient_ = child; + SENode::AddChild(child); + } + + inline void AddOffset(SENode* child) { + offset_ = child; + SENode::AddChild(child); + } + + inline const SENode* GetCoefficient() const { return coefficient_; } + inline SENode* GetCoefficient() { return coefficient_; } + + inline const SENode* GetOffset() const { return offset_; } + inline SENode* GetOffset() { return offset_; } + + // Return the loop which this recurrent expression is recurring within. + const Loop* GetLoop() const { return loop_; } + + SERecurrentNode* AsSERecurrentNode() override { return this; } + const SERecurrentNode* AsSERecurrentNode() const override { return this; } + + private: + SENode* coefficient_; + SENode* offset_; + const Loop* loop_; +}; + +// A node representing an addition operation between child nodes. +class SEAddNode : public SENode { + public: + explicit SEAddNode(ScalarEvolutionAnalysis* parent_analysis) + : SENode(parent_analysis) {} + + SENodeType GetType() const final { return Add; } + + SEAddNode* AsSEAddNode() override { return this; } + const SEAddNode* AsSEAddNode() const override { return this; } +}; + +// A node representing a multiply operation between child nodes. +class SEMultiplyNode : public SENode { + public: + explicit SEMultiplyNode(ScalarEvolutionAnalysis* parent_analysis) + : SENode(parent_analysis) {} + + SENodeType GetType() const final { return Multiply; } + + SEMultiplyNode* AsSEMultiplyNode() override { return this; } + const SEMultiplyNode* AsSEMultiplyNode() const override { return this; } +}; + +// A node representing a unary negative operation. +class SENegative : public SENode { + public: + explicit SENegative(ScalarEvolutionAnalysis* parent_analysis) + : SENode(parent_analysis) {} + + SENodeType GetType() const final { return Negative; } + + SENegative* AsSENegative() override { return this; } + const SENegative* AsSENegative() const override { return this; } +}; + +// A node representing a value which we do not know the value of, such as a load +// instruction. +class SEValueUnknown : public SENode { + public: + // SEValueUnknowns must come from an instruction |unique_id| is the unique id + // of that instruction. This is so we cancompare value unknowns and have a + // unique value unknown for each instruction. + SEValueUnknown(ScalarEvolutionAnalysis* parent_analysis, uint32_t result_id) + : SENode(parent_analysis), result_id_(result_id) {} + + SENodeType GetType() const final { return ValueUnknown; } + + SEValueUnknown* AsSEValueUnknown() override { return this; } + const SEValueUnknown* AsSEValueUnknown() const override { return this; } + + inline uint32_t ResultId() const { return result_id_; } + + private: + uint32_t result_id_; +}; + +// A node which we cannot reason about at all. +class SECantCompute : public SENode { + public: + explicit SECantCompute(ScalarEvolutionAnalysis* parent_analysis) + : SENode(parent_analysis) {} + + SENodeType GetType() const final { return CanNotCompute; } + + SECantCompute* AsSECantCompute() override { return this; } + const SECantCompute* AsSECantCompute() const override { return this; } +}; + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_SCALAR_ANALYSIS_NODES_H_ diff --git a/third_party/spirv-tools/source/opt/scalar_analysis_simplification.cpp b/third_party/spirv-tools/source/opt/scalar_analysis_simplification.cpp new file mode 100644 index 0000000..52f2d6a --- /dev/null +++ b/third_party/spirv-tools/source/opt/scalar_analysis_simplification.cpp @@ -0,0 +1,539 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/scalar_analysis.h" + +#include +#include +#include +#include +#include +#include +#include + +// Simplifies scalar analysis DAGs. +// +// 1. Given a node passed to SimplifyExpression we first simplify the graph by +// calling SimplifyPolynomial. This groups like nodes following basic arithmetic +// rules, so multiple adds of the same load instruction could be grouped into a +// single multiply of that instruction. SimplifyPolynomial will traverse the DAG +// and build up an accumulator buffer for each class of instruction it finds. +// For example take the loop: +// for (i=0, i accumulators_; +}; + +// From a |multiply| build up the accumulator objects. +bool SENodeSimplifyImpl::AccumulatorsFromMultiply(SENode* multiply, + bool negation) { + if (multiply->GetChildren().size() != 2 || + multiply->GetType() != SENode::Multiply) + return false; + + SENode* operand_1 = multiply->GetChild(0); + SENode* operand_2 = multiply->GetChild(1); + + SENode* value_unknown = nullptr; + SENode* constant = nullptr; + + // Work out which operand is the unknown value. + if (operand_1->GetType() == SENode::ValueUnknown || + operand_1->GetType() == SENode::RecurrentAddExpr) + value_unknown = operand_1; + else if (operand_2->GetType() == SENode::ValueUnknown || + operand_2->GetType() == SENode::RecurrentAddExpr) + value_unknown = operand_2; + + // Work out which operand is the constant coefficient. + if (operand_1->GetType() == SENode::Constant) + constant = operand_1; + else if (operand_2->GetType() == SENode::Constant) + constant = operand_2; + + // If the expression is not a variable multiplied by a constant coefficient, + // exit out. + if (!(value_unknown && constant)) { + return false; + } + + int64_t sign = negation ? -1 : 1; + + auto iterator = accumulators_.find(value_unknown); + int64_t new_value = constant->AsSEConstantNode()->FoldToSingleValue() * sign; + // Add the result of the multiplication to the accumulators. + if (iterator != accumulators_.end()) { + (*iterator).second += new_value; + } else { + accumulators_.insert({value_unknown, new_value}); + } + + return true; +} + +SENode* SENodeSimplifyImpl::Simplify() { + // We only handle graphs with an addition, multiplication, or negation, at the + // root. + if (node_->GetType() != SENode::Add && node_->GetType() != SENode::Multiply && + node_->GetType() != SENode::Negative) + return node_; + + SENode* simplified_polynomial = SimplifyPolynomial(); + + SERecurrentNode* recurrent_expr = nullptr; + node_ = simplified_polynomial; + + // Fold recurrent expressions which are with respect to the same loop into a + // single recurrent expression. + simplified_polynomial = FoldRecurrentAddExpressions(simplified_polynomial); + + simplified_polynomial = + EliminateZeroCoefficientRecurrents(simplified_polynomial); + + // Traverse the immediate children of the new node to find the recurrent + // expression. If there is more than one there is nothing further we can do. + for (SENode* child : simplified_polynomial->GetChildren()) { + if (child->GetType() == SENode::RecurrentAddExpr) { + recurrent_expr = child->AsSERecurrentNode(); + } + } + + // We need to count the number of unique recurrent expressions in the DAG to + // ensure there is only one. + for (auto child_iterator = simplified_polynomial->graph_begin(); + child_iterator != simplified_polynomial->graph_end(); ++child_iterator) { + if (child_iterator->GetType() == SENode::RecurrentAddExpr && + recurrent_expr != child_iterator->AsSERecurrentNode()) { + return simplified_polynomial; + } + } + + if (recurrent_expr) { + return SimplifyRecurrentAddExpression(recurrent_expr); + } + + return simplified_polynomial; +} + +// Traverse the graph to build up the accumulator objects. +void SENodeSimplifyImpl::GatherAccumulatorsFromChildNodes(SENode* new_node, + SENode* child, + bool negation) { + int32_t sign = negation ? -1 : 1; + + if (child->GetType() == SENode::Constant) { + // Collect all the constants and add them together. + constant_accumulator_ += + child->AsSEConstantNode()->FoldToSingleValue() * sign; + + } else if (child->GetType() == SENode::ValueUnknown || + child->GetType() == SENode::RecurrentAddExpr) { + // To rebuild the graph of X+X+X*2 into 4*X we count the occurrences of X + // and create a new node of count*X after. X can either be a ValueUnknown or + // a RecurrentAddExpr. The count for each X is stored in the accumulators_ + // map. + + auto iterator = accumulators_.find(child); + // If we've encountered this term before add to the accumulator for it. + if (iterator == accumulators_.end()) + accumulators_.insert({child, sign}); + else + iterator->second += sign; + + } else if (child->GetType() == SENode::Multiply) { + if (!AccumulatorsFromMultiply(child, negation)) { + new_node->AddChild(child); + } + + } else if (child->GetType() == SENode::Add) { + for (SENode* next_child : *child) { + GatherAccumulatorsFromChildNodes(new_node, next_child, negation); + } + + } else if (child->GetType() == SENode::Negative) { + SENode* negated_node = child->GetChild(0); + GatherAccumulatorsFromChildNodes(new_node, negated_node, !negation); + } else { + // If we can't work out how to fold the expression just add it back into + // the graph. + new_node->AddChild(child); + } +} + +SERecurrentNode* SENodeSimplifyImpl::UpdateCoefficient( + SERecurrentNode* recurrent, int64_t coefficient_update) const { + std::unique_ptr new_recurrent_node{new SERecurrentNode( + recurrent->GetParentAnalysis(), recurrent->GetLoop())}; + + SENode* new_coefficient = analysis_.CreateMultiplyNode( + recurrent->GetCoefficient(), + analysis_.CreateConstant(coefficient_update)); + + // See if the node can be simplified. + SENode* simplified = analysis_.SimplifyExpression(new_coefficient); + if (simplified->GetType() != SENode::CanNotCompute) + new_coefficient = simplified; + + if (coefficient_update < 0) { + new_recurrent_node->AddOffset( + analysis_.CreateNegation(recurrent->GetOffset())); + } else { + new_recurrent_node->AddOffset(recurrent->GetOffset()); + } + + new_recurrent_node->AddCoefficient(new_coefficient); + + return analysis_.GetCachedOrAdd(std::move(new_recurrent_node)) + ->AsSERecurrentNode(); +} + +// Simplify all the terms in the polynomial function. +SENode* SENodeSimplifyImpl::SimplifyPolynomial() { + std::unique_ptr new_add{new SEAddNode(node_->GetParentAnalysis())}; + + // Traverse the graph and gather the accumulators from it. + GatherAccumulatorsFromChildNodes(new_add.get(), node_, false); + + // Fold all the constants into a single constant node. + if (constant_accumulator_ != 0) { + new_add->AddChild(analysis_.CreateConstant(constant_accumulator_)); + } + + for (auto& pair : accumulators_) { + SENode* term = pair.first; + int64_t count = pair.second; + + // We can eliminate the term completely. + if (count == 0) continue; + + if (count == 1) { + new_add->AddChild(term); + } else if (count == -1 && term->GetType() != SENode::RecurrentAddExpr) { + // If the count is -1 we can just add a negative version of that node, + // unless it is a recurrent expression as we would rather the negative + // goes on the recurrent expressions children. This makes it easier to + // work with in other places. + new_add->AddChild(analysis_.CreateNegation(term)); + } else { + // Output value unknown terms as count*term and output recurrent + // expression terms as rec(offset, coefficient + count) offset and + // coefficient are the same as in the original expression. + if (term->GetType() == SENode::ValueUnknown) { + SENode* count_as_constant = analysis_.CreateConstant(count); + new_add->AddChild( + analysis_.CreateMultiplyNode(count_as_constant, term)); + } else { + assert(term->GetType() == SENode::RecurrentAddExpr && + "We only handle value unknowns or recurrent expressions"); + + // Create a new recurrent expression by adding the count to the + // coefficient of the old one. + new_add->AddChild(UpdateCoefficient(term->AsSERecurrentNode(), count)); + } + } + } + + // If there is only one term in the addition left just return that term. + if (new_add->GetChildren().size() == 1) { + return new_add->GetChild(0); + } + + // If there are no terms left in the addition just return 0. + if (new_add->GetChildren().size() == 0) { + return analysis_.CreateConstant(0); + } + + return analysis_.GetCachedOrAdd(std::move(new_add)); +} + +SENode* SENodeSimplifyImpl::FoldRecurrentAddExpressions(SENode* root) { + std::unique_ptr new_node{new SEAddNode(&analysis_)}; + + // A mapping of loops to the list of recurrent expressions which are with + // respect to those loops. + std::map>> + loops_to_recurrent{}; + + bool has_multiple_same_loop_recurrent_terms = false; + + for (SENode* child : *root) { + bool negation = false; + + if (child->GetType() == SENode::Negative) { + child = child->GetChild(0); + negation = true; + } + + if (child->GetType() == SENode::RecurrentAddExpr) { + const Loop* loop = child->AsSERecurrentNode()->GetLoop(); + + SERecurrentNode* rec = child->AsSERecurrentNode(); + if (loops_to_recurrent.find(loop) == loops_to_recurrent.end()) { + loops_to_recurrent[loop] = {std::make_pair(rec, negation)}; + } else { + loops_to_recurrent[loop].push_back(std::make_pair(rec, negation)); + has_multiple_same_loop_recurrent_terms = true; + } + } else { + new_node->AddChild(child); + } + } + + if (!has_multiple_same_loop_recurrent_terms) return root; + + for (auto pair : loops_to_recurrent) { + std::vector>& recurrent_expressions = + pair.second; + const Loop* loop = pair.first; + + std::unique_ptr new_coefficient{new SEAddNode(&analysis_)}; + std::unique_ptr new_offset{new SEAddNode(&analysis_)}; + + for (auto node_pair : recurrent_expressions) { + SERecurrentNode* node = node_pair.first; + bool negative = node_pair.second; + + if (!negative) { + new_coefficient->AddChild(node->GetCoefficient()); + new_offset->AddChild(node->GetOffset()); + } else { + new_coefficient->AddChild( + analysis_.CreateNegation(node->GetCoefficient())); + new_offset->AddChild(analysis_.CreateNegation(node->GetOffset())); + } + } + + std::unique_ptr new_recurrent{ + new SERecurrentNode(&analysis_, loop)}; + + SENode* new_coefficient_simplified = + analysis_.SimplifyExpression(new_coefficient.get()); + + SENode* new_offset_simplified = + analysis_.SimplifyExpression(new_offset.get()); + + if (new_coefficient_simplified->GetType() == SENode::Constant && + new_coefficient_simplified->AsSEConstantNode()->FoldToSingleValue() == + 0) { + return new_offset_simplified; + } + + new_recurrent->AddCoefficient(new_coefficient_simplified); + new_recurrent->AddOffset(new_offset_simplified); + + new_node->AddChild(analysis_.GetCachedOrAdd(std::move(new_recurrent))); + } + + // If we only have one child in the add just return that. + if (new_node->GetChildren().size() == 1) { + return new_node->GetChild(0); + } + + return analysis_.GetCachedOrAdd(std::move(new_node)); +} + +SENode* SENodeSimplifyImpl::EliminateZeroCoefficientRecurrents(SENode* node) { + if (node->GetType() != SENode::Add) return node; + + bool has_change = false; + + std::vector new_children{}; + for (SENode* child : *node) { + if (child->GetType() == SENode::RecurrentAddExpr) { + SENode* coefficient = child->AsSERecurrentNode()->GetCoefficient(); + // If coefficient is zero then we can eliminate the recurrent expression + // entirely and just return the offset as the recurrent expression is + // representing the equation coefficient*iterations + offset. + if (coefficient->GetType() == SENode::Constant && + coefficient->AsSEConstantNode()->FoldToSingleValue() == 0) { + new_children.push_back(child->AsSERecurrentNode()->GetOffset()); + has_change = true; + } else { + new_children.push_back(child); + } + } else { + new_children.push_back(child); + } + } + + if (!has_change) return node; + + std::unique_ptr new_add{new SEAddNode(node_->GetParentAnalysis())}; + + for (SENode* child : new_children) { + new_add->AddChild(child); + } + + return analysis_.GetCachedOrAdd(std::move(new_add)); +} + +SENode* SENodeSimplifyImpl::SimplifyRecurrentAddExpression( + SERecurrentNode* recurrent_expr) { + const std::vector& children = node_->GetChildren(); + + std::unique_ptr recurrent_node{new SERecurrentNode( + recurrent_expr->GetParentAnalysis(), recurrent_expr->GetLoop())}; + + // Create and simplify the new offset node. + std::unique_ptr new_offset{ + new SEAddNode(recurrent_expr->GetParentAnalysis())}; + new_offset->AddChild(recurrent_expr->GetOffset()); + + for (SENode* child : children) { + if (child->GetType() != SENode::RecurrentAddExpr) { + new_offset->AddChild(child); + } + } + + // Simplify the new offset. + SENode* simplified_child = analysis_.SimplifyExpression(new_offset.get()); + + // If the child can be simplified, add the simplified form otherwise, add it + // via the usual caching mechanism. + if (simplified_child->GetType() != SENode::CanNotCompute) { + recurrent_node->AddOffset(simplified_child); + } else { + recurrent_expr->AddOffset(analysis_.GetCachedOrAdd(std::move(new_offset))); + } + + recurrent_node->AddCoefficient(recurrent_expr->GetCoefficient()); + + return analysis_.GetCachedOrAdd(std::move(recurrent_node)); +} + +/* + * Scalar Analysis simplification public methods. + */ + +SENode* ScalarEvolutionAnalysis::SimplifyExpression(SENode* node) { + SENodeSimplifyImpl impl{this, node}; + + return impl.Simplify(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/scalar_replacement_pass.cpp b/third_party/spirv-tools/source/opt/scalar_replacement_pass.cpp new file mode 100644 index 0000000..d71d605 --- /dev/null +++ b/third_party/spirv-tools/source/opt/scalar_replacement_pass.cpp @@ -0,0 +1,989 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/scalar_replacement_pass.h" + +#include +#include +#include +#include + +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/opt/reflect.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" + +static const uint32_t kDebugDeclareOperandLocalVariableIndex = 4; +static const uint32_t kDebugValueOperandValueIndex = 5; +static const uint32_t kDebugValueOperandExpressionIndex = 6; + +namespace spvtools { +namespace opt { + +Pass::Status ScalarReplacementPass::Process() { + Status status = Status::SuccessWithoutChange; + for (auto& f : *get_module()) { + Status functionStatus = ProcessFunction(&f); + if (functionStatus == Status::Failure) + return functionStatus; + else if (functionStatus == Status::SuccessWithChange) + status = functionStatus; + } + + return status; +} + +Pass::Status ScalarReplacementPass::ProcessFunction(Function* function) { + std::queue worklist; + BasicBlock& entry = *function->begin(); + for (auto iter = entry.begin(); iter != entry.end(); ++iter) { + // Function storage class OpVariables must appear as the first instructions + // of the entry block. + if (iter->opcode() != SpvOpVariable) break; + + Instruction* varInst = &*iter; + if (CanReplaceVariable(varInst)) { + worklist.push(varInst); + } + } + + Status status = Status::SuccessWithoutChange; + while (!worklist.empty()) { + Instruction* varInst = worklist.front(); + worklist.pop(); + + Status var_status = ReplaceVariable(varInst, &worklist); + if (var_status == Status::Failure) + return var_status; + else if (var_status == Status::SuccessWithChange) + status = var_status; + } + + return status; +} + +Pass::Status ScalarReplacementPass::ReplaceVariable( + Instruction* inst, std::queue* worklist) { + std::vector replacements; + if (!CreateReplacementVariables(inst, &replacements)) { + return Status::Failure; + } + + std::vector dead; + bool replaced_all_uses = get_def_use_mgr()->WhileEachUser( + inst, [this, &replacements, &dead](Instruction* user) { + if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare) { + if (ReplaceWholeDebugDeclare(user, replacements)) { + dead.push_back(user); + return true; + } + return false; + } + if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + if (ReplaceWholeDebugValue(user, replacements)) { + dead.push_back(user); + return true; + } + return false; + } + if (!IsAnnotationInst(user->opcode())) { + switch (user->opcode()) { + case SpvOpLoad: + if (ReplaceWholeLoad(user, replacements)) { + dead.push_back(user); + } else { + return false; + } + break; + case SpvOpStore: + if (ReplaceWholeStore(user, replacements)) { + dead.push_back(user); + } else { + return false; + } + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + if (ReplaceAccessChain(user, replacements)) + dead.push_back(user); + else + return false; + break; + case SpvOpName: + case SpvOpMemberName: + break; + default: + assert(false && "Unexpected opcode"); + break; + } + } + return true; + }); + + if (replaced_all_uses) { + dead.push_back(inst); + } else { + return Status::Failure; + } + + // If there are no dead instructions to clean up, return with no changes. + if (dead.empty()) return Status::SuccessWithoutChange; + + // Clean up some dead code. + while (!dead.empty()) { + Instruction* toKill = dead.back(); + dead.pop_back(); + context()->KillInst(toKill); + } + + // Attempt to further scalarize. + for (auto var : replacements) { + if (var->opcode() == SpvOpVariable) { + if (get_def_use_mgr()->NumUsers(var) == 0) { + context()->KillInst(var); + } else if (CanReplaceVariable(var)) { + worklist->push(var); + } + } + } + + return Status::SuccessWithChange; +} + +bool ScalarReplacementPass::ReplaceWholeDebugDeclare( + Instruction* dbg_decl, const std::vector& replacements) { + // Insert Deref operation to the front of the operation list of |dbg_decl|. + Instruction* dbg_expr = context()->get_def_use_mgr()->GetDef( + dbg_decl->GetSingleWordOperand(kDebugValueOperandExpressionIndex)); + auto* deref_expr = + context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr); + + // Add DebugValue instruction with Indexes operand and Deref operation. + int32_t idx = 0; + for (const auto* var : replacements) { + uint32_t dbg_local_variable = + dbg_decl->GetSingleWordOperand(kDebugDeclareOperandLocalVariableIndex); + uint32_t index_id = context()->get_constant_mgr()->GetSIntConst(idx); + + Instruction* added_dbg_value = + context()->get_debug_info_mgr()->AddDebugValueWithIndex( + dbg_local_variable, + /*value_id=*/var->result_id(), /*expr_id=*/deref_expr->result_id(), + index_id, /*insert_before=*/var->NextNode()); + if (added_dbg_value == nullptr) return false; + added_dbg_value->UpdateDebugInfoFrom(dbg_decl); + ++idx; + } + return true; +} + +bool ScalarReplacementPass::ReplaceWholeDebugValue( + Instruction* dbg_value, const std::vector& replacements) { + int32_t idx = 0; + BasicBlock* block = context()->get_instr_block(dbg_value); + for (auto var : replacements) { + // Clone the DebugValue. + std::unique_ptr new_dbg_value(dbg_value->Clone(context())); + uint32_t new_id = TakeNextId(); + if (new_id == 0) return false; + new_dbg_value->SetResultId(new_id); + // Update 'Value' operand to the |replacements|. + new_dbg_value->SetOperand(kDebugValueOperandValueIndex, {var->result_id()}); + // Append 'Indexes' operand. + new_dbg_value->AddOperand( + {SPV_OPERAND_TYPE_ID, + {context()->get_constant_mgr()->GetSIntConst(idx)}}); + // Insert the new DebugValue to the basic block. + auto* added_instr = dbg_value->InsertBefore(std::move(new_dbg_value)); + get_def_use_mgr()->AnalyzeInstDefUse(added_instr); + context()->set_instr_block(added_instr, block); + ++idx; + } + return true; +} + +bool ScalarReplacementPass::ReplaceWholeLoad( + Instruction* load, const std::vector& replacements) { + // Replaces the load of the entire composite with a load from each replacement + // variable followed by a composite construction. + BasicBlock* block = context()->get_instr_block(load); + std::vector loads; + loads.reserve(replacements.size()); + BasicBlock::iterator where(load); + for (auto var : replacements) { + // Create a load of each replacement variable. + if (var->opcode() != SpvOpVariable) { + loads.push_back(var); + continue; + } + + Instruction* type = GetStorageType(var); + uint32_t loadId = TakeNextId(); + if (loadId == 0) { + return false; + } + std::unique_ptr newLoad( + new Instruction(context(), SpvOpLoad, type->result_id(), loadId, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); + // Copy memory access attributes which start at index 1. Index 0 is the + // pointer to load. + for (uint32_t i = 1; i < load->NumInOperands(); ++i) { + Operand copy(load->GetInOperand(i)); + newLoad->AddOperand(std::move(copy)); + } + where = where.InsertBefore(std::move(newLoad)); + get_def_use_mgr()->AnalyzeInstDefUse(&*where); + context()->set_instr_block(&*where, block); + where->UpdateDebugInfoFrom(load); + loads.push_back(&*where); + } + + // Construct a new composite. + uint32_t compositeId = TakeNextId(); + if (compositeId == 0) { + return false; + } + where = load; + std::unique_ptr compositeConstruct(new Instruction( + context(), SpvOpCompositeConstruct, load->type_id(), compositeId, {})); + for (auto l : loads) { + Operand op(SPV_OPERAND_TYPE_ID, + std::initializer_list{l->result_id()}); + compositeConstruct->AddOperand(std::move(op)); + } + where = where.InsertBefore(std::move(compositeConstruct)); + get_def_use_mgr()->AnalyzeInstDefUse(&*where); + where->UpdateDebugInfoFrom(load); + context()->set_instr_block(&*where, block); + context()->ReplaceAllUsesWith(load->result_id(), compositeId); + return true; +} + +bool ScalarReplacementPass::ReplaceWholeStore( + Instruction* store, const std::vector& replacements) { + // Replaces a store to the whole composite with a series of extract and stores + // to each element. + uint32_t storeInput = store->GetSingleWordInOperand(1u); + BasicBlock* block = context()->get_instr_block(store); + BasicBlock::iterator where(store); + uint32_t elementIndex = 0; + for (auto var : replacements) { + // Create the extract. + if (var->opcode() != SpvOpVariable) { + elementIndex++; + continue; + } + + Instruction* type = GetStorageType(var); + uint32_t extractId = TakeNextId(); + if (extractId == 0) { + return false; + } + std::unique_ptr extract(new Instruction( + context(), SpvOpCompositeExtract, type->result_id(), extractId, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {storeInput}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {elementIndex++}}})); + auto iter = where.InsertBefore(std::move(extract)); + iter->UpdateDebugInfoFrom(store); + get_def_use_mgr()->AnalyzeInstDefUse(&*iter); + context()->set_instr_block(&*iter, block); + + // Create the store. + std::unique_ptr newStore( + new Instruction(context(), SpvOpStore, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}, + {SPV_OPERAND_TYPE_ID, {extractId}}})); + // Copy memory access attributes which start at index 2. Index 0 is the + // pointer and index 1 is the data. + for (uint32_t i = 2; i < store->NumInOperands(); ++i) { + Operand copy(store->GetInOperand(i)); + newStore->AddOperand(std::move(copy)); + } + iter = where.InsertBefore(std::move(newStore)); + iter->UpdateDebugInfoFrom(store); + get_def_use_mgr()->AnalyzeInstDefUse(&*iter); + context()->set_instr_block(&*iter, block); + } + return true; +} + +bool ScalarReplacementPass::ReplaceAccessChain( + Instruction* chain, const std::vector& replacements) { + // Replaces the access chain with either another access chain (with one fewer + // indexes) or a direct use of the replacement variable. + uint32_t indexId = chain->GetSingleWordInOperand(1u); + const Instruction* index = get_def_use_mgr()->GetDef(indexId); + int64_t indexValue = context() + ->get_constant_mgr() + ->GetConstantFromInst(index) + ->GetSignExtendedValue(); + if (indexValue < 0 || + indexValue >= static_cast(replacements.size())) { + // Out of bounds access, this is illegal IR. Notice that OpAccessChain + // indexing is 0-based, so we should also reject index == size-of-array. + return false; + } else { + const Instruction* var = replacements[static_cast(indexValue)]; + if (chain->NumInOperands() > 2) { + // Replace input access chain with another access chain. + BasicBlock::iterator chainIter(chain); + uint32_t replacementId = TakeNextId(); + if (replacementId == 0) { + return false; + } + std::unique_ptr replacementChain(new Instruction( + context(), chain->opcode(), chain->type_id(), replacementId, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}})); + // Add the remaining indexes. + for (uint32_t i = 2; i < chain->NumInOperands(); ++i) { + Operand copy(chain->GetInOperand(i)); + replacementChain->AddOperand(std::move(copy)); + } + replacementChain->UpdateDebugInfoFrom(chain); + auto iter = chainIter.InsertBefore(std::move(replacementChain)); + get_def_use_mgr()->AnalyzeInstDefUse(&*iter); + context()->set_instr_block(&*iter, context()->get_instr_block(chain)); + context()->ReplaceAllUsesWith(chain->result_id(), replacementId); + } else { + // Replace with a use of the variable. + context()->ReplaceAllUsesWith(chain->result_id(), var->result_id()); + } + } + + return true; +} + +bool ScalarReplacementPass::CreateReplacementVariables( + Instruction* inst, std::vector* replacements) { + Instruction* type = GetStorageType(inst); + + std::unique_ptr> components_used = + GetUsedComponents(inst); + + uint32_t elem = 0; + switch (type->opcode()) { + case SpvOpTypeStruct: + type->ForEachInOperand( + [this, inst, &elem, replacements, &components_used](uint32_t* id) { + if (!components_used || components_used->count(elem)) { + CreateVariable(*id, inst, elem, replacements); + } else { + replacements->push_back(CreateNullConstant(*id)); + } + elem++; + }); + break; + case SpvOpTypeArray: + for (uint32_t i = 0; i != GetArrayLength(type); ++i) { + if (!components_used || components_used->count(i)) { + CreateVariable(type->GetSingleWordInOperand(0u), inst, i, + replacements); + } else { + replacements->push_back( + CreateNullConstant(type->GetSingleWordInOperand(0u))); + } + } + break; + + case SpvOpTypeMatrix: + case SpvOpTypeVector: + for (uint32_t i = 0; i != GetNumElements(type); ++i) { + CreateVariable(type->GetSingleWordInOperand(0u), inst, i, replacements); + } + break; + + default: + assert(false && "Unexpected type."); + break; + } + + TransferAnnotations(inst, replacements); + return std::find(replacements->begin(), replacements->end(), nullptr) == + replacements->end(); +} + +void ScalarReplacementPass::TransferAnnotations( + const Instruction* source, std::vector* replacements) { + // Only transfer invariant and restrict decorations on the variable. There are + // no type or member decorations that are necessary to transfer. + for (auto inst : + get_decoration_mgr()->GetDecorationsFor(source->result_id(), false)) { + assert(inst->opcode() == SpvOpDecorate); + uint32_t decoration = inst->GetSingleWordInOperand(1u); + if (decoration == SpvDecorationInvariant || + decoration == SpvDecorationRestrict) { + for (auto var : *replacements) { + if (var == nullptr) { + continue; + } + + std::unique_ptr annotation( + new Instruction(context(), SpvOpDecorate, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {var->result_id()}}, + {SPV_OPERAND_TYPE_DECORATION, {decoration}}})); + for (uint32_t i = 2; i < inst->NumInOperands(); ++i) { + Operand copy(inst->GetInOperand(i)); + annotation->AddOperand(std::move(copy)); + } + context()->AddAnnotationInst(std::move(annotation)); + get_def_use_mgr()->AnalyzeInstUse(&*--context()->annotation_end()); + } + } + } +} + +void ScalarReplacementPass::CreateVariable( + uint32_t typeId, Instruction* varInst, uint32_t index, + std::vector* replacements) { + uint32_t ptrId = GetOrCreatePointerType(typeId); + uint32_t id = TakeNextId(); + + if (id == 0) { + replacements->push_back(nullptr); + } + + std::unique_ptr variable(new Instruction( + context(), SpvOpVariable, ptrId, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); + + BasicBlock* block = context()->get_instr_block(varInst); + block->begin().InsertBefore(std::move(variable)); + Instruction* inst = &*block->begin(); + + // If varInst was initialized, make sure to initialize its replacement. + GetOrCreateInitialValue(varInst, index, inst); + get_def_use_mgr()->AnalyzeInstDefUse(inst); + context()->set_instr_block(inst, block); + + // Copy decorations from the member to the new variable. + Instruction* typeInst = GetStorageType(varInst); + for (auto dec_inst : + get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) { + uint32_t decoration; + if (dec_inst->opcode() != SpvOpMemberDecorate) { + continue; + } + + if (dec_inst->GetSingleWordInOperand(1) != index) { + continue; + } + + decoration = dec_inst->GetSingleWordInOperand(2u); + switch (decoration) { + case SpvDecorationRelaxedPrecision: { + std::unique_ptr new_dec_inst( + new Instruction(context(), SpvOpDecorate, 0, 0, {})); + new_dec_inst->AddOperand(Operand(SPV_OPERAND_TYPE_ID, {id})); + for (uint32_t i = 2; i < dec_inst->NumInOperandWords(); ++i) { + new_dec_inst->AddOperand(Operand(dec_inst->GetInOperand(i))); + } + context()->AddAnnotationInst(std::move(new_dec_inst)); + } break; + default: + break; + } + } + + // Update the OpenCL.DebugInfo.100 debug information. + inst->UpdateDebugInfoFrom(varInst); + + replacements->push_back(inst); +} + +uint32_t ScalarReplacementPass::GetOrCreatePointerType(uint32_t id) { + auto iter = pointee_to_pointer_.find(id); + if (iter != pointee_to_pointer_.end()) return iter->second; + + analysis::Type* pointeeTy; + std::unique_ptr pointerTy; + std::tie(pointeeTy, pointerTy) = + context()->get_type_mgr()->GetTypeAndPointerType(id, + SpvStorageClassFunction); + uint32_t ptrId = 0; + if (pointeeTy->IsUniqueType()) { + // Non-ambiguous type, just ask the type manager for an id. + ptrId = context()->get_type_mgr()->GetTypeInstruction(pointerTy.get()); + pointee_to_pointer_[id] = ptrId; + return ptrId; + } + + // Ambiguous type. We must perform a linear search to try and find the right + // type. + for (auto global : context()->types_values()) { + if (global.opcode() == SpvOpTypePointer && + global.GetSingleWordInOperand(0u) == SpvStorageClassFunction && + global.GetSingleWordInOperand(1u) == id) { + if (get_decoration_mgr()->GetDecorationsFor(id, false).empty()) { + // Only reuse a decoration-less pointer of the correct type. + ptrId = global.result_id(); + break; + } + } + } + + if (ptrId != 0) { + pointee_to_pointer_[id] = ptrId; + return ptrId; + } + + ptrId = TakeNextId(); + context()->AddType(MakeUnique( + context(), SpvOpTypePointer, 0, ptrId, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}, + {SPV_OPERAND_TYPE_ID, {id}}})); + Instruction* ptr = &*--context()->types_values_end(); + get_def_use_mgr()->AnalyzeInstDefUse(ptr); + pointee_to_pointer_[id] = ptrId; + // Register with the type manager if necessary. + context()->get_type_mgr()->RegisterType(ptrId, *pointerTy); + + return ptrId; +} + +void ScalarReplacementPass::GetOrCreateInitialValue(Instruction* source, + uint32_t index, + Instruction* newVar) { + assert(source->opcode() == SpvOpVariable); + if (source->NumInOperands() < 2) return; + + uint32_t initId = source->GetSingleWordInOperand(1u); + uint32_t storageId = GetStorageType(newVar)->result_id(); + Instruction* init = get_def_use_mgr()->GetDef(initId); + uint32_t newInitId = 0; + // TODO(dnovillo): Refactor this with constant propagation. + if (init->opcode() == SpvOpConstantNull) { + // Initialize to appropriate NULL. + auto iter = type_to_null_.find(storageId); + if (iter == type_to_null_.end()) { + newInitId = TakeNextId(); + type_to_null_[storageId] = newInitId; + context()->AddGlobalValue( + MakeUnique(context(), SpvOpConstantNull, storageId, + newInitId, std::initializer_list{})); + Instruction* newNull = &*--context()->types_values_end(); + get_def_use_mgr()->AnalyzeInstDefUse(newNull); + } else { + newInitId = iter->second; + } + } else if (IsSpecConstantInst(init->opcode())) { + // Create a new constant extract. + newInitId = TakeNextId(); + context()->AddGlobalValue(MakeUnique( + context(), SpvOpSpecConstantOp, storageId, newInitId, + std::initializer_list{ + {SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER, {SpvOpCompositeExtract}}, + {SPV_OPERAND_TYPE_ID, {init->result_id()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {index}}})); + Instruction* newSpecConst = &*--context()->types_values_end(); + get_def_use_mgr()->AnalyzeInstDefUse(newSpecConst); + } else if (init->opcode() == SpvOpConstantComposite) { + // Get the appropriate index constant. + newInitId = init->GetSingleWordInOperand(index); + Instruction* element = get_def_use_mgr()->GetDef(newInitId); + if (element->opcode() == SpvOpUndef) { + // Undef is not a valid initializer for a variable. + newInitId = 0; + } + } else { + assert(false); + } + + if (newInitId != 0) { + newVar->AddOperand({SPV_OPERAND_TYPE_ID, {newInitId}}); + } +} + +uint64_t ScalarReplacementPass::GetArrayLength( + const Instruction* arrayType) const { + assert(arrayType->opcode() == SpvOpTypeArray); + const Instruction* length = + get_def_use_mgr()->GetDef(arrayType->GetSingleWordInOperand(1u)); + return context() + ->get_constant_mgr() + ->GetConstantFromInst(length) + ->GetZeroExtendedValue(); +} + +uint64_t ScalarReplacementPass::GetNumElements(const Instruction* type) const { + assert(type->opcode() == SpvOpTypeVector || + type->opcode() == SpvOpTypeMatrix); + const Operand& op = type->GetInOperand(1u); + assert(op.words.size() <= 2); + uint64_t len = 0; + for (size_t i = 0; i != op.words.size(); ++i) { + len |= (static_cast(op.words[i]) << (32ull * i)); + } + return len; +} + +bool ScalarReplacementPass::IsSpecConstant(uint32_t id) const { + const Instruction* inst = get_def_use_mgr()->GetDef(id); + assert(inst); + return spvOpcodeIsSpecConstant(inst->opcode()); +} + +Instruction* ScalarReplacementPass::GetStorageType( + const Instruction* inst) const { + assert(inst->opcode() == SpvOpVariable); + + uint32_t ptrTypeId = inst->type_id(); + uint32_t typeId = + get_def_use_mgr()->GetDef(ptrTypeId)->GetSingleWordInOperand(1u); + return get_def_use_mgr()->GetDef(typeId); +} + +bool ScalarReplacementPass::CanReplaceVariable( + const Instruction* varInst) const { + assert(varInst->opcode() == SpvOpVariable); + + // Can only replace function scope variables. + if (varInst->GetSingleWordInOperand(0u) != SpvStorageClassFunction) { + return false; + } + + if (!CheckTypeAnnotations(get_def_use_mgr()->GetDef(varInst->type_id()))) { + return false; + } + + const Instruction* typeInst = GetStorageType(varInst); + if (!CheckType(typeInst)) { + return false; + } + + if (!CheckAnnotations(varInst)) { + return false; + } + + if (!CheckUses(varInst)) { + return false; + } + + return true; +} + +bool ScalarReplacementPass::CheckType(const Instruction* typeInst) const { + if (!CheckTypeAnnotations(typeInst)) { + return false; + } + + switch (typeInst->opcode()) { + case SpvOpTypeStruct: + // Don't bother with empty structs or very large structs. + if (typeInst->NumInOperands() == 0 || + IsLargerThanSizeLimit(typeInst->NumInOperands())) { + return false; + } + return true; + case SpvOpTypeArray: + if (IsSpecConstant(typeInst->GetSingleWordInOperand(1u))) { + return false; + } + if (IsLargerThanSizeLimit(GetArrayLength(typeInst))) { + return false; + } + return true; + // TODO(alanbaker): Develop some heuristics for when this should be + // re-enabled. + //// Specifically including matrix and vector in an attempt to reduce the + //// number of vector registers required. + // case SpvOpTypeMatrix: + // case SpvOpTypeVector: + // if (IsLargerThanSizeLimit(GetNumElements(typeInst))) return false; + // return true; + + case SpvOpTypeRuntimeArray: + default: + return false; + } +} + +bool ScalarReplacementPass::CheckTypeAnnotations( + const Instruction* typeInst) const { + for (auto inst : + get_decoration_mgr()->GetDecorationsFor(typeInst->result_id(), false)) { + uint32_t decoration; + if (inst->opcode() == SpvOpDecorate) { + decoration = inst->GetSingleWordInOperand(1u); + } else { + assert(inst->opcode() == SpvOpMemberDecorate); + decoration = inst->GetSingleWordInOperand(2u); + } + + switch (decoration) { + case SpvDecorationRowMajor: + case SpvDecorationColMajor: + case SpvDecorationArrayStride: + case SpvDecorationMatrixStride: + case SpvDecorationCPacked: + case SpvDecorationInvariant: + case SpvDecorationRestrict: + case SpvDecorationOffset: + case SpvDecorationAlignment: + case SpvDecorationAlignmentId: + case SpvDecorationMaxByteOffset: + case SpvDecorationRelaxedPrecision: + break; + default: + return false; + } + } + + return true; +} + +bool ScalarReplacementPass::CheckAnnotations(const Instruction* varInst) const { + for (auto inst : + get_decoration_mgr()->GetDecorationsFor(varInst->result_id(), false)) { + assert(inst->opcode() == SpvOpDecorate); + uint32_t decoration = inst->GetSingleWordInOperand(1u); + switch (decoration) { + case SpvDecorationInvariant: + case SpvDecorationRestrict: + case SpvDecorationAlignment: + case SpvDecorationAlignmentId: + case SpvDecorationMaxByteOffset: + break; + default: + return false; + } + } + + return true; +} + +bool ScalarReplacementPass::CheckUses(const Instruction* inst) const { + VariableStats stats = {0, 0}; + bool ok = CheckUses(inst, &stats); + + // TODO(alanbaker/greg-lunarg): Add some meaningful heuristics about when + // SRoA is costly, such as when the structure has many (unaccessed?) + // members. + + return ok; +} + +bool ScalarReplacementPass::CheckUses(const Instruction* inst, + VariableStats* stats) const { + uint64_t max_legal_index = GetMaxLegalIndex(inst); + + bool ok = true; + get_def_use_mgr()->ForEachUse(inst, [this, max_legal_index, stats, &ok]( + const Instruction* user, + uint32_t index) { + if (user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugDeclare || + user->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) { + // TODO: include num_partial_accesses if it uses Fragment operation or + // DebugValue has Indexes operand. + stats->num_full_accesses++; + return; + } + + // Annotations are check as a group separately. + if (!IsAnnotationInst(user->opcode())) { + switch (user->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + if (index == 2u && user->NumInOperands() > 1) { + uint32_t id = user->GetSingleWordInOperand(1u); + const Instruction* opInst = get_def_use_mgr()->GetDef(id); + const auto* constant = + context()->get_constant_mgr()->GetConstantFromInst(opInst); + if (!constant) { + ok = false; + } else if (constant->GetZeroExtendedValue() >= max_legal_index) { + ok = false; + } else { + if (!CheckUsesRelaxed(user)) ok = false; + } + stats->num_partial_accesses++; + } else { + ok = false; + } + break; + case SpvOpLoad: + if (!CheckLoad(user, index)) ok = false; + stats->num_full_accesses++; + break; + case SpvOpStore: + if (!CheckStore(user, index)) ok = false; + stats->num_full_accesses++; + break; + case SpvOpName: + case SpvOpMemberName: + break; + default: + ok = false; + break; + } + } + }); + + return ok; +} + +bool ScalarReplacementPass::CheckUsesRelaxed(const Instruction* inst) const { + bool ok = true; + get_def_use_mgr()->ForEachUse( + inst, [this, &ok](const Instruction* user, uint32_t index) { + switch (user->opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + if (index != 2u) { + ok = false; + } else { + if (!CheckUsesRelaxed(user)) ok = false; + } + break; + case SpvOpLoad: + if (!CheckLoad(user, index)) ok = false; + break; + case SpvOpStore: + if (!CheckStore(user, index)) ok = false; + break; + default: + ok = false; + break; + } + }); + + return ok; +} + +bool ScalarReplacementPass::CheckLoad(const Instruction* inst, + uint32_t index) const { + if (index != 2u) return false; + if (inst->NumInOperands() >= 2 && + inst->GetSingleWordInOperand(1u) & SpvMemoryAccessVolatileMask) + return false; + return true; +} + +bool ScalarReplacementPass::CheckStore(const Instruction* inst, + uint32_t index) const { + if (index != 0u) return false; + if (inst->NumInOperands() >= 3 && + inst->GetSingleWordInOperand(2u) & SpvMemoryAccessVolatileMask) + return false; + return true; +} +bool ScalarReplacementPass::IsLargerThanSizeLimit(uint64_t length) const { + if (max_num_elements_ == 0) { + return false; + } + return length > max_num_elements_; +} + +std::unique_ptr> +ScalarReplacementPass::GetUsedComponents(Instruction* inst) { + std::unique_ptr> result( + new std::unordered_set()); + + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + def_use_mgr->WhileEachUser(inst, [&result, def_use_mgr, + this](Instruction* use) { + switch (use->opcode()) { + case SpvOpLoad: { + // Look for extract from the load. + std::vector t; + if (def_use_mgr->WhileEachUser(use, [&t](Instruction* use2) { + if (use2->opcode() != SpvOpCompositeExtract || + use2->NumInOperands() <= 1) { + return false; + } + t.push_back(use2->GetSingleWordInOperand(1)); + return true; + })) { + result->insert(t.begin(), t.end()); + return true; + } else { + result.reset(nullptr); + return false; + } + } + case SpvOpName: + case SpvOpMemberName: + case SpvOpStore: + // No components are used. + return true; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: { + // Add the first index it if is a constant. + // TODO: Could be improved by checking if the address is used in a load. + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + uint32_t index_id = use->GetSingleWordInOperand(1); + const analysis::Constant* index_const = + const_mgr->FindDeclaredConstant(index_id); + if (index_const) { + result->insert(index_const->GetSignExtendedValue()); + return true; + } else { + // Could be any element. Assuming all are used. + result.reset(nullptr); + return false; + } + } + default: + // We do not know what is happening. Have to assume the worst. + result.reset(nullptr); + return false; + } + }); + + return result; +} + +Instruction* ScalarReplacementPass::CreateNullConstant(uint32_t type_id) { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::ConstantManager* const_mgr = context()->get_constant_mgr(); + + const analysis::Type* type = type_mgr->GetType(type_id); + const analysis::Constant* null_const = const_mgr->GetConstant(type, {}); + Instruction* null_inst = + const_mgr->GetDefiningInstruction(null_const, type_id); + if (null_inst != nullptr) { + context()->UpdateDefUse(null_inst); + } + return null_inst; +} + +uint64_t ScalarReplacementPass::GetMaxLegalIndex( + const Instruction* var_inst) const { + assert(var_inst->opcode() == SpvOpVariable && + "|var_inst| must be a variable instruction."); + Instruction* type = GetStorageType(var_inst); + switch (type->opcode()) { + case SpvOpTypeStruct: + return type->NumInOperands(); + case SpvOpTypeArray: + return GetArrayLength(type); + case SpvOpTypeMatrix: + case SpvOpTypeVector: + return GetNumElements(type); + default: + return 0; + } + return 0; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/scalar_replacement_pass.h b/third_party/spirv-tools/source/opt/scalar_replacement_pass.h new file mode 100644 index 0000000..1f6c928 --- /dev/null +++ b/third_party/spirv-tools/source/opt/scalar_replacement_pass.h @@ -0,0 +1,257 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ +#define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/function.h" +#include "source/opt/pass.h" +#include "source/opt/type_manager.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class ScalarReplacementPass : public Pass { + private: + static const uint32_t kDefaultLimit = 100; + + public: + ScalarReplacementPass(uint32_t limit = kDefaultLimit) + : max_num_elements_(limit) { + name_[0] = '\0'; + strcat(name_, "scalar-replacement="); + sprintf(&name_[strlen(name_)], "%d", max_num_elements_); + } + + const char* name() const override { return name_; } + + // Attempts to scalarize all appropriate function scope variables. Returns + // SuccessWithChange if any change is made. + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Small container for tracking statistics about variables. + // + // TODO(alanbaker): Develop some useful heuristics to tune this pass. + struct VariableStats { + uint32_t num_partial_accesses; + uint32_t num_full_accesses; + }; + + // Attempts to scalarize all appropriate function scope variables in + // |function|. Returns SuccessWithChange if any changes are mode. + Status ProcessFunction(Function* function); + + // Returns true if |varInst| can be scalarized. + // + // Examines the use chain of |varInst| to verify all uses are valid for + // scalarization. + bool CanReplaceVariable(const Instruction* varInst) const; + + // Returns true if |typeInst| is an acceptable type to scalarize. + // + // Allows all aggregate types except runtime arrays. Additionally, checks the + // that the number of elements that would be scalarized is within bounds. + bool CheckType(const Instruction* typeInst) const; + + // Returns true if all the decorations for |varInst| are acceptable for + // scalarization. + bool CheckAnnotations(const Instruction* varInst) const; + + // Returns true if all the decorations for |typeInst| are acceptable for + // scalarization. + bool CheckTypeAnnotations(const Instruction* typeInst) const; + + // Returns true if the uses of |inst| are acceptable for scalarization. + // + // Recursively checks all the uses of |inst|. For |inst| specifically, only + // allows SpvOpAccessChain, SpvOpInBoundsAccessChain, SpvOpLoad and + // SpvOpStore. Access chains must have the first index be a compile-time + // constant. Subsequent uses of access chains (including other access chains) + // are checked in a more relaxed manner. + bool CheckUses(const Instruction* inst) const; + + // Helper function for the above |CheckUses|. + // + // This version tracks some stats about the current OpVariable. These stats + // are used to drive heuristics about when to scalarize. + bool CheckUses(const Instruction* inst, VariableStats* stats) const; + + // Relaxed helper function for |CheckUses|. + bool CheckUsesRelaxed(const Instruction* inst) const; + + // Transfers appropriate decorations from |source| to |replacements|. + void TransferAnnotations(const Instruction* source, + std::vector* replacements); + + // Scalarizes |inst| and updates its uses. + // + // |inst| must be an OpVariable. It is replaced with an OpVariable for each + // for element of the composite type. Uses of |inst| are updated as + // appropriate. If the replacement variables are themselves scalarizable, they + // get added to |worklist| for further processing. If any replacement + // variable ends up with no uses it is erased. Returns + // - Status::SuccessWithoutChange if the variable could not be replaced. + // - Status::SuccessWithChange if it made replacements. + // - Status::Failure if it couldn't create replacement variables. + Pass::Status ReplaceVariable(Instruction* inst, + std::queue* worklist); + + // Returns the underlying storage type for |inst|. + // + // |inst| must be an OpVariable. Returns the type that is pointed to by + // |inst|. + Instruction* GetStorageType(const Instruction* inst) const; + + // Returns true if the load can be scalarized. + // + // |inst| must be an OpLoad. Returns true if |index| is the pointer operand of + // |inst| and the load is not from volatile memory. + bool CheckLoad(const Instruction* inst, uint32_t index) const; + + // Returns true if the store can be scalarized. + // + // |inst| must be an OpStore. Returns true if |index| is the pointer operand + // of |inst| and the store is not to volatile memory. + bool CheckStore(const Instruction* inst, uint32_t index) const; + + // Creates a variable of type |typeId| from the |index|'th element of + // |varInst|. The new variable is added to |replacements|. If the variable + // could not be created, then |nullptr| is appended to |replacements|. + void CreateVariable(uint32_t typeId, Instruction* varInst, uint32_t index, + std::vector* replacements); + + // Populates |replacements| with a new OpVariable for each element of |inst|. + // Returns true if the replacement variables were successfully created. + // + // |inst| must be an OpVariable of a composite type. New variables are + // initialized the same as the corresponding index in |inst|. |replacements| + // will contain a variable for each element of the composite with matching + // indexes (i.e. the 0'th element of |inst| is the 0'th entry of + // |replacements|). + bool CreateReplacementVariables(Instruction* inst, + std::vector* replacements); + + // Returns the array length for |arrayInst|. + uint64_t GetArrayLength(const Instruction* arrayInst) const; + + // Returns the number of elements in |type|. + // + // |type| must be a vector or matrix type. + uint64_t GetNumElements(const Instruction* type) const; + + // Returns true if |id| is a specialization constant. + // + // |id| must be registered definition. + bool IsSpecConstant(uint32_t id) const; + + // Returns an id for a pointer to |id|. + uint32_t GetOrCreatePointerType(uint32_t id); + + // Creates the initial value for the |index| element of |source| in |newVar|. + // + // If there is an initial value for |source| for element |index|, it is + // appended as an operand on |newVar|. If the initial value is OpUndef, no + // initial value is added to |newVar|. + void GetOrCreateInitialValue(Instruction* source, uint32_t index, + Instruction* newVar); + + // Replaces the load to the entire composite. + // + // Generates a load for each replacement variable and then creates a new + // composite by combining all of the loads. + // + // |load| must be a load. Returns true if successful. + bool ReplaceWholeLoad(Instruction* load, + const std::vector& replacements); + + // Replaces the store to the entire composite. + // + // Generates a composite extract and store for each element in the scalarized + // variable from the original store data input. Returns true if successful. + bool ReplaceWholeStore(Instruction* store, + const std::vector& replacements); + + // Replaces the DebugDeclare to the entire composite. + // + // Generates a DebugValue with Deref operation for each element in the + // scalarized variable from the original DebugDeclare. Returns true if + // successful. + bool ReplaceWholeDebugDeclare(Instruction* dbg_decl, + const std::vector& replacements); + + // Replaces the DebugValue to the entire composite. + // + // Generates a DebugValue for each element in the scalarized variable from + // the original DebugValue. Returns true if successful. + bool ReplaceWholeDebugValue(Instruction* dbg_value, + const std::vector& replacements); + + // Replaces an access chain to the composite variable with either a direct use + // of the appropriate replacement variable or another access chain with the + // replacement variable as the base and one fewer indexes. Returns true if + // successful. + bool ReplaceAccessChain(Instruction* chain, + const std::vector& replacements); + + // Returns a set containing the which components of the result of |inst| are + // potentially used. If the return value is |nullptr|, then every components + // is possibly used. + std::unique_ptr> GetUsedComponents( + Instruction* inst); + + // Returns an instruction defining a null constant with type |type_id|. If + // one already exists, it is returned. Otherwise a new one is created. + // Returns |nullptr| if the new constant could not be created. + Instruction* CreateNullConstant(uint32_t type_id); + + // Maps storage type to a pointer type enclosing that type. + std::unordered_map pointee_to_pointer_; + + // Maps type id to OpConstantNull for that type. + std::unordered_map type_to_null_; + + // Returns the number of elements in the variable |var_inst|. + uint64_t GetMaxLegalIndex(const Instruction* var_inst) const; + + // Returns true if |length| is larger than limit on the size of the variable + // that we will be willing to split. + bool IsLargerThanSizeLimit(uint64_t length) const; + + // Limit on the number of members in an object that will be replaced. + // 0 means there is no limit. + uint32_t max_num_elements_; + char name_[55]; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp b/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp new file mode 100644 index 0000000..4c8d116 --- /dev/null +++ b/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.cpp @@ -0,0 +1,368 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/set_spec_constant_default_value_pass.h" + +#include +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/type_manager.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" +#include "source/util/parse_number.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace opt { + +namespace { +using utils::EncodeNumberStatus; +using utils::NumberType; +using utils::ParseAndEncodeNumber; +using utils::ParseNumber; + +// Given a numeric value in a null-terminated c string and the expected type of +// the value, parses the string and encodes it in a vector of words. If the +// value is a scalar integer or floating point value, encodes the value in +// SPIR-V encoding format. If the value is 'false' or 'true', returns a vector +// with single word with value 0 or 1 respectively. Returns the vector +// containing the encoded value on success. Otherwise returns an empty vector. +std::vector ParseDefaultValueStr(const char* text, + const analysis::Type* type) { + std::vector result; + if (!strcmp(text, "true") && type->AsBool()) { + result.push_back(1u); + } else if (!strcmp(text, "false") && type->AsBool()) { + result.push_back(0u); + } else { + NumberType number_type = {32, SPV_NUMBER_UNSIGNED_INT}; + if (const auto* IT = type->AsInteger()) { + number_type.bitwidth = IT->width(); + number_type.kind = + IT->IsSigned() ? SPV_NUMBER_SIGNED_INT : SPV_NUMBER_UNSIGNED_INT; + } else if (const auto* FT = type->AsFloat()) { + number_type.bitwidth = FT->width(); + number_type.kind = SPV_NUMBER_FLOATING; + } else { + // Does not handle types other then boolean, integer or float. Returns + // empty vector. + result.clear(); + return result; + } + EncodeNumberStatus rc = ParseAndEncodeNumber( + text, number_type, [&result](uint32_t word) { result.push_back(word); }, + nullptr); + // Clear the result vector on failure. + if (rc != EncodeNumberStatus::kSuccess) { + result.clear(); + } + } + return result; +} + +// Given a bit pattern and a type, checks if the bit pattern is compatible +// with the type. If so, returns the bit pattern, otherwise returns an empty +// bit pattern. If the given bit pattern is empty, returns an empty bit +// pattern. If the given type represents a SPIR-V Boolean type, the bit pattern +// to be returned is determined with the following standard: +// If any words in the input bit pattern are non zero, returns a bit pattern +// with 0x1, which represents a 'true'. +// If all words in the bit pattern are zero, returns a bit pattern with 0x0, +// which represents a 'false'. +std::vector ParseDefaultValueBitPattern( + const std::vector& input_bit_pattern, + const analysis::Type* type) { + std::vector result; + if (type->AsBool()) { + if (std::any_of(input_bit_pattern.begin(), input_bit_pattern.end(), + [](uint32_t i) { return i != 0; })) { + result.push_back(1u); + } else { + result.push_back(0u); + } + return result; + } else if (const auto* IT = type->AsInteger()) { + if (IT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + return std::vector(input_bit_pattern); + } + } else if (const auto* FT = type->AsFloat()) { + if (FT->width() == input_bit_pattern.size() * sizeof(uint32_t) * 8) { + return std::vector(input_bit_pattern); + } + } + result.clear(); + return result; +} + +// Returns true if the given instruction's result id could have a SpecId +// decoration. +bool CanHaveSpecIdDecoration(const Instruction& inst) { + switch (inst.opcode()) { + case SpvOp::SpvOpSpecConstant: + case SpvOp::SpvOpSpecConstantFalse: + case SpvOp::SpvOpSpecConstantTrue: + return true; + default: + return false; + } +} + +// Given a decoration group defining instruction that is decorated with SpecId +// decoration, finds the spec constant defining instruction which is the real +// target of the SpecId decoration. Returns the spec constant defining +// instruction if such an instruction is found, otherwise returns a nullptr. +Instruction* GetSpecIdTargetFromDecorationGroup( + const Instruction& decoration_group_defining_inst, + analysis::DefUseManager* def_use_mgr) { + // Find the OpGroupDecorate instruction which consumes the given decoration + // group. Note that the given decoration group has SpecId decoration, which + // is unique for different spec constants. So the decoration group cannot be + // consumed by different OpGroupDecorate instructions. Therefore we only need + // the first OpGroupDecoration instruction that uses the given decoration + // group. + Instruction* group_decorate_inst = nullptr; + if (def_use_mgr->WhileEachUser(&decoration_group_defining_inst, + [&group_decorate_inst](Instruction* user) { + if (user->opcode() == + SpvOp::SpvOpGroupDecorate) { + group_decorate_inst = user; + return false; + } + return true; + })) + return nullptr; + + // Scan through the target ids of the OpGroupDecorate instruction. There + // should be only one spec constant target consumes the SpecId decoration. + // If multiple target ids are presented in the OpGroupDecorate instruction, + // they must be the same one that defined by an eligible spec constant + // instruction. If the OpGroupDecorate instruction has different target ids + // or a target id is not defined by an eligible spec cosntant instruction, + // returns a nullptr. + Instruction* target_inst = nullptr; + for (uint32_t i = 1; i < group_decorate_inst->NumInOperands(); i++) { + // All the operands of a OpGroupDecorate instruction should be of type + // SPV_OPERAND_TYPE_ID. + uint32_t candidate_id = group_decorate_inst->GetSingleWordInOperand(i); + Instruction* candidate_inst = def_use_mgr->GetDef(candidate_id); + + if (!candidate_inst) { + continue; + } + + if (!target_inst) { + // If the spec constant target has not been found yet, check if the + // candidate instruction is the target. + if (CanHaveSpecIdDecoration(*candidate_inst)) { + target_inst = candidate_inst; + } else { + // Spec id decoration should not be applied on other instructions. + // TODO(qining): Emit an error message in the invalid case once the + // error handling is done. + return nullptr; + } + } else { + // If the spec constant target has been found, check if the candidate + // instruction is the same one as the target. The module is invalid if + // the candidate instruction is different with the found target. + // TODO(qining): Emit an error messaage in the invalid case once the + // error handling is done. + if (candidate_inst != target_inst) return nullptr; + } + } + return target_inst; +} +} // namespace + +Pass::Status SetSpecConstantDefaultValuePass::Process() { + // The operand index of decoration target in an OpDecorate instruction. + const uint32_t kTargetIdOperandIndex = 0; + // The operand index of the decoration literal in an OpDecorate instruction. + const uint32_t kDecorationOperandIndex = 1; + // The operand index of Spec id literal value in an OpDecorate SpecId + // instruction. + const uint32_t kSpecIdLiteralOperandIndex = 2; + // The number of operands in an OpDecorate SpecId instruction. + const uint32_t kOpDecorateSpecIdNumOperands = 3; + // The in-operand index of the default value in a OpSpecConstant instruction. + const uint32_t kOpSpecConstantLiteralInOperandIndex = 0; + + bool modified = false; + // Scan through all the annotation instructions to find 'OpDecorate SpecId' + // instructions. Then extract the decoration target of those instructions. + // The decoration targets should be spec constant defining instructions with + // opcode: OpSpecConstant{|True|False}. The spec id of those spec constants + // will be used to look up their new default values in the mapping from + // spec id to new default value strings. Once a new default value string + // is found for a spec id, the string will be parsed according to the target + // spec constant type. The parsed value will be used to replace the original + // default value of the target spec constant. + for (Instruction& inst : context()->annotations()) { + // Only process 'OpDecorate SpecId' instructions + if (inst.opcode() != SpvOp::SpvOpDecorate) continue; + if (inst.NumOperands() != kOpDecorateSpecIdNumOperands) continue; + if (inst.GetSingleWordInOperand(kDecorationOperandIndex) != + uint32_t(SpvDecoration::SpvDecorationSpecId)) { + continue; + } + + // 'inst' is an OpDecorate SpecId instruction. + uint32_t spec_id = inst.GetSingleWordOperand(kSpecIdLiteralOperandIndex); + uint32_t target_id = inst.GetSingleWordOperand(kTargetIdOperandIndex); + + // Find the spec constant defining instruction. Note that the + // target_id might be a decoration group id. + Instruction* spec_inst = nullptr; + if (Instruction* target_inst = get_def_use_mgr()->GetDef(target_id)) { + if (target_inst->opcode() == SpvOp::SpvOpDecorationGroup) { + spec_inst = + GetSpecIdTargetFromDecorationGroup(*target_inst, get_def_use_mgr()); + } else { + spec_inst = target_inst; + } + } else { + continue; + } + if (!spec_inst) continue; + + // Get the default value bit pattern for this spec id. + std::vector bit_pattern; + + if (spec_id_to_value_str_.size() != 0) { + // Search for the new string-form default value for this spec id. + auto iter = spec_id_to_value_str_.find(spec_id); + if (iter == spec_id_to_value_str_.end()) { + continue; + } + + // Gets the string of the default value and parses it to bit pattern + // with the type of the spec constant. + const std::string& default_value_str = iter->second; + bit_pattern = ParseDefaultValueStr( + default_value_str.c_str(), + context()->get_type_mgr()->GetType(spec_inst->type_id())); + + } else { + // Search for the new bit-pattern-form default value for this spec id. + auto iter = spec_id_to_value_bit_pattern_.find(spec_id); + if (iter == spec_id_to_value_bit_pattern_.end()) { + continue; + } + + // Gets the bit-pattern of the default value from the map directly. + bit_pattern = ParseDefaultValueBitPattern( + iter->second, + context()->get_type_mgr()->GetType(spec_inst->type_id())); + } + + if (bit_pattern.empty()) continue; + + // Update the operand bit patterns of the spec constant defining + // instruction. + switch (spec_inst->opcode()) { + case SpvOp::SpvOpSpecConstant: + // If the new value is the same with the original value, no + // need to do anything. Otherwise update the operand words. + if (spec_inst->GetInOperand(kOpSpecConstantLiteralInOperandIndex) + .words != bit_pattern) { + spec_inst->SetInOperand(kOpSpecConstantLiteralInOperandIndex, + std::move(bit_pattern)); + modified = true; + } + break; + case SpvOp::SpvOpSpecConstantTrue: + // If the new value is also 'true', no need to change anything. + // Otherwise, set the opcode to OpSpecConstantFalse; + if (!static_cast(bit_pattern.front())) { + spec_inst->SetOpcode(SpvOp::SpvOpSpecConstantFalse); + modified = true; + } + break; + case SpvOp::SpvOpSpecConstantFalse: + // If the new value is also 'false', no need to change anything. + // Otherwise, set the opcode to OpSpecConstantTrue; + if (static_cast(bit_pattern.front())) { + spec_inst->SetOpcode(SpvOp::SpvOpSpecConstantTrue); + modified = true; + } + break; + default: + break; + } + // No need to update the DefUse manager, as this pass does not change any + // ids. + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +// Returns true if the given char is ':', '\0' or considered as blank space +// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' '). +bool IsSeparator(char ch) { + return std::strchr(":\0", ch) || std::isspace(ch) != 0; +} + +std::unique_ptr +SetSpecConstantDefaultValuePass::ParseDefaultValuesString(const char* str) { + if (!str) return nullptr; + + auto spec_id_to_value = MakeUnique(); + + // The parsing loop, break when points to the end. + while (*str) { + // Find the spec id. + while (std::isspace(*str)) str++; // skip leading spaces. + const char* entry_begin = str; + while (!IsSeparator(*str)) str++; + const char* entry_end = str; + std::string spec_id_str(entry_begin, entry_end - entry_begin); + uint32_t spec_id = 0; + if (!ParseNumber(spec_id_str.c_str(), &spec_id)) { + // The spec id is not a valid uint32 number. + return nullptr; + } + auto iter = spec_id_to_value->find(spec_id); + if (iter != spec_id_to_value->end()) { + // Same spec id has been defined before + return nullptr; + } + // Find the ':', spaces between the spec id and the ':' are not allowed. + if (*str++ != ':') { + // ':' not found + return nullptr; + } + // Find the value string + const char* val_begin = str; + while (!IsSeparator(*str)) str++; + const char* val_end = str; + if (val_end == val_begin) { + // Value string is empty. + return nullptr; + } + // Update the mapping with spec id and value string. + (*spec_id_to_value)[spec_id] = std::string(val_begin, val_end - val_begin); + + // Skip trailing spaces. + while (std::isspace(*str)) str++; + } + + return spec_id_to_value; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.h b/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.h new file mode 100644 index 0000000..8bd1787 --- /dev/null +++ b/third_party/spirv-tools/source/opt/set_spec_constant_default_value_pass.h @@ -0,0 +1,114 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SET_SPEC_CONSTANT_DEFAULT_VALUE_PASS_H_ +#define SOURCE_OPT_SET_SPEC_CONSTANT_DEFAULT_VALUE_PASS_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SetSpecConstantDefaultValuePass : public Pass { + public: + using SpecIdToValueStrMap = std::unordered_map; + using SpecIdToValueBitPatternMap = + std::unordered_map>; + using SpecIdToInstMap = std::unordered_map; + + // Constructs a pass instance with a map from spec ids to default values + // in the form of string. + explicit SetSpecConstantDefaultValuePass( + const SpecIdToValueStrMap& default_values) + : spec_id_to_value_str_(default_values), + spec_id_to_value_bit_pattern_() {} + explicit SetSpecConstantDefaultValuePass(SpecIdToValueStrMap&& default_values) + : spec_id_to_value_str_(std::move(default_values)), + spec_id_to_value_bit_pattern_() {} + + // Constructs a pass instance with a map from spec ids to default values in + // the form of bit pattern. + explicit SetSpecConstantDefaultValuePass( + const SpecIdToValueBitPatternMap& default_values) + : spec_id_to_value_str_(), + spec_id_to_value_bit_pattern_(default_values) {} + explicit SetSpecConstantDefaultValuePass( + SpecIdToValueBitPatternMap&& default_values) + : spec_id_to_value_str_(), + spec_id_to_value_bit_pattern_(std::move(default_values)) {} + + const char* name() const override { return "set-spec-const-default-value"; } + Status Process() override; + + // Parses the given null-terminated C string to get a mapping from Spec Id to + // default value strings. Returns a unique pointer of the mapping from spec + // ids to spec constant default value strings built from the given |str| on + // success. Returns a nullptr if the given string is not valid for building + // the mapping. + // A valid string for building the mapping should follow the rule below: + // + // ": : ..." + // Example: + // "200:0x11 201:3.14 202:1.4728" + // + // Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t', + // '\f', '\v'). Each entry corresponds to a Spec Id and default value pair. + // Multiple spaces between, before or after entries are allowed. However, + // spaces are not allowed within spec id or the default value string because + // spaces are always considered as delimiter to separate entries. + // + // In each entry, the spec id and value string is separated by ':'. Missing + // ':' in any entry is invalid. And it is invalid to have blank spaces in + // between the spec id and ':' or the default value and ':'. + // + // : specifies the spec id value. + // The text must represent a valid uint32_t number. + // Hex format with '0x' prefix is allowed. + // Empty is not allowed. + // One spec id value can only be defined once, multiple default values + // defined for the same spec id is not allowed. Spec ids with same value + // but different formats (e.g. 0x100 and 256) are considered the same. + // + // : the default value string. + // Spaces before and after default value text is allowed. + // Spaces within the text is not allowed. + // Empty is not allowed. + static std::unique_ptr ParseDefaultValuesString( + const char* str); + + private: + // The mappings from spec ids to default values. Two maps are defined here, + // each to be used for one specific form of the default values. Only one of + // them will be populated in practice. + + // The mapping from spec ids to their string-form default values to be set. + const SpecIdToValueStrMap spec_id_to_value_str_; + // The mapping from spec ids to their bitpattern-form default values to be + // set. + const SpecIdToValueBitPatternMap spec_id_to_value_bit_pattern_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SET_SPEC_CONSTANT_DEFAULT_VALUE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/simplification_pass.cpp b/third_party/spirv-tools/source/opt/simplification_pass.cpp new file mode 100644 index 0000000..319ceec --- /dev/null +++ b/third_party/spirv-tools/source/opt/simplification_pass.cpp @@ -0,0 +1,165 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/simplification_pass.h" + +#include +#include +#include + +#include "source/opt/fold.h" + +namespace spvtools { +namespace opt { + +Pass::Status SimplificationPass::Process() { + bool modified = false; + + for (Function& function : *get_module()) { + modified |= SimplifyFunction(&function); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +void SimplificationPass::AddNewOperands( + Instruction* folded_inst, std::unordered_set* inst_seen, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = get_def_use_mgr(); + folded_inst->ForEachInId( + [&inst_seen, &def_use_mgr, &work_list](uint32_t* iid) { + Instruction* iid_inst = def_use_mgr->GetDef(*iid); + if (!inst_seen->insert(iid_inst).second) return; + work_list->push_back(iid_inst); + }); +} + +bool SimplificationPass::SimplifyFunction(Function* function) { + bool modified = false; + // Phase 1: Traverse all instructions in dominance order. + // The second phase will only be on the instructions whose inputs have changed + // after being processed during phase 1. Since OpPhi instructions are the + // only instructions whose inputs do not necessarily dominate the use, we keep + // track of the OpPhi instructions already seen, and add them to the work list + // for phase 2 when needed. + std::vector work_list; + std::unordered_set process_phis; + std::unordered_set inst_to_kill; + std::unordered_set in_work_list; + std::unordered_set inst_seen; + const InstructionFolder& folder = context()->get_instruction_folder(); + + cfg()->ForEachBlockInReversePostOrder( + function->entry().get(), + [&modified, &process_phis, &work_list, &in_work_list, &inst_to_kill, + &folder, &inst_seen, this](BasicBlock* bb) { + for (Instruction* inst = &*bb->begin(); inst; inst = inst->NextNode()) { + inst_seen.insert(inst); + if (inst->opcode() == SpvOpPhi) { + process_phis.insert(inst); + } + + bool is_foldable_copy = + inst->opcode() == SpvOpCopyObject && + context()->get_decoration_mgr()->HaveSubsetOfDecorations( + inst->result_id(), inst->GetSingleWordInOperand(0)); + + if (is_foldable_copy || folder.FoldInstruction(inst)) { + modified = true; + context()->AnalyzeUses(inst); + get_def_use_mgr()->ForEachUser(inst, [&work_list, &process_phis, + &in_work_list]( + Instruction* use) { + if (process_phis.count(use) && in_work_list.insert(use).second) { + work_list.push_back(use); + } + }); + + AddNewOperands(inst, &inst_seen, &work_list); + + if (inst->opcode() == SpvOpCopyObject) { + context()->ReplaceAllUsesWithPredicate( + inst->result_id(), inst->GetSingleWordInOperand(0), + [](Instruction* user) { + const auto opcode = user->opcode(); + if (!spvOpcodeIsDebug(opcode) && + !spvOpcodeIsDecoration(opcode)) { + return true; + } + return false; + }); + inst_to_kill.insert(inst); + in_work_list.insert(inst); + } else if (inst->opcode() == SpvOpNop) { + inst_to_kill.insert(inst); + in_work_list.insert(inst); + } + } + } + }); + + // Phase 2: process the instructions in the work list until all of the work is + // done. This time we add all users to the work list because phase 1 + // has already finished. + for (size_t i = 0; i < work_list.size(); ++i) { + Instruction* inst = work_list[i]; + in_work_list.erase(inst); + inst_seen.insert(inst); + + bool is_foldable_copy = + inst->opcode() == SpvOpCopyObject && + context()->get_decoration_mgr()->HaveSubsetOfDecorations( + inst->result_id(), inst->GetSingleWordInOperand(0)); + + if (is_foldable_copy || folder.FoldInstruction(inst)) { + modified = true; + context()->AnalyzeUses(inst); + get_def_use_mgr()->ForEachUser( + inst, [&work_list, &in_work_list](Instruction* use) { + if (!use->IsDecoration() && use->opcode() != SpvOpName && + in_work_list.insert(use).second) { + work_list.push_back(use); + } + }); + + AddNewOperands(inst, &inst_seen, &work_list); + + if (inst->opcode() == SpvOpCopyObject) { + context()->ReplaceAllUsesWithPredicate( + inst->result_id(), inst->GetSingleWordInOperand(0), + [](Instruction* user) { + const auto opcode = user->opcode(); + if (!spvOpcodeIsDebug(opcode) && !spvOpcodeIsDecoration(opcode)) { + return true; + } + return false; + }); + inst_to_kill.insert(inst); + in_work_list.insert(inst); + } else if (inst->opcode() == SpvOpNop) { + inst_to_kill.insert(inst); + in_work_list.insert(inst); + } + } + } + + // Phase 3: Kill instructions we know are no longer needed. + for (Instruction* inst : inst_to_kill) { + context()->KillInst(inst); + } + + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/simplification_pass.h b/third_party/spirv-tools/source/opt/simplification_pass.h new file mode 100644 index 0000000..149874b --- /dev/null +++ b/third_party/spirv-tools/source/opt/simplification_pass.h @@ -0,0 +1,58 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SIMPLIFICATION_PASS_H_ +#define SOURCE_OPT_SIMPLIFICATION_PASS_H_ + +#include "source/opt/function.h" +#include "source/opt/ir_context.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class SimplificationPass : public Pass { + public: + const char* name() const override { return "simplify-instructions"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisNameMap | IRContext::kAnalysisConstants | + IRContext::kAnalysisTypes; + } + + private: + // Returns true if the module was changed. The simplifier is called on every + // instruction in |function| until nothing else in the function can be + // simplified. + bool SimplifyFunction(Function* function); + + // FactorAddMul can create |folded_inst| Mul of new Add. If Mul, push any Add + // operand not in |seen_inst| into |worklist|. This is heavily restricted to + // improve compile time but can be expanded for future simplifications which + // simiarly create new operations. + void AddNewOperands(Instruction* folded_inst, + std::unordered_set* inst_seen, + std::vector* work_list); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SIMPLIFICATION_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/split_invalid_unreachable_pass.cpp b/third_party/spirv-tools/source/opt/split_invalid_unreachable_pass.cpp new file mode 100644 index 0000000..31cfbc3 --- /dev/null +++ b/third_party/spirv-tools/source/opt/split_invalid_unreachable_pass.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/split_invalid_unreachable_pass.h" + +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status SplitInvalidUnreachablePass::Process() { + bool changed = false; + std::unordered_set entry_points; + for (auto entry_point : context()->module()->entry_points()) { + entry_points.insert(entry_point.GetSingleWordOperand(1)); + } + + for (auto func = context()->module()->begin(); + func != context()->module()->end(); ++func) { + if (entry_points.find(func->result_id()) == entry_points.end()) continue; + std::unordered_set continue_targets; + std::unordered_set merge_blocks; + std::unordered_set unreachable_blocks; + for (auto block = func->begin(); block != func->end(); ++block) { + unreachable_blocks.insert(&*block); + uint32_t continue_target = block->ContinueBlockIdIfAny(); + if (continue_target != 0) continue_targets.insert(continue_target); + uint32_t merge_block = block->MergeBlockIdIfAny(); + if (merge_block != 0) merge_blocks.insert(merge_block); + } + + cfg()->ForEachBlockInPostOrder( + func->entry().get(), [&unreachable_blocks](BasicBlock* inner_block) { + unreachable_blocks.erase(inner_block); + }); + + for (auto unreachable : unreachable_blocks) { + uint32_t block_id = unreachable->id(); + if (continue_targets.find(block_id) == continue_targets.end() || + merge_blocks.find(block_id) == merge_blocks.end()) { + continue; + } + + std::vector> usages; + context()->get_def_use_mgr()->ForEachUse( + unreachable->GetLabelInst(), + [&usages](Instruction* use, uint32_t idx) { + if ((use->opcode() == SpvOpLoopMerge && idx == 0) || + use->opcode() == SpvOpSelectionMerge) { + usages.push_back(std::make_pair(use, idx)); + } + }); + + for (auto usage : usages) { + Instruction* use; + uint32_t idx; + std::tie(use, idx) = usage; + uint32_t new_id = context()->TakeNextId(); + std::unique_ptr new_label( + new Instruction(context(), SpvOpLabel, 0, new_id, {})); + get_def_use_mgr()->AnalyzeInstDefUse(new_label.get()); + std::unique_ptr new_block( + new BasicBlock(std::move(new_label))); + auto* block_ptr = new_block.get(); + InstructionBuilder builder(context(), new_block.get(), + IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping); + builder.AddUnreachable(); + cfg()->RegisterBlock(block_ptr); + (&*func)->InsertBasicBlockBefore(std::move(new_block), unreachable); + use->SetInOperand(0, {new_id}); + get_def_use_mgr()->UpdateDefUse(use); + cfg()->AddEdges(block_ptr); + changed = true; + } + } + } + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/split_invalid_unreachable_pass.h b/third_party/spirv-tools/source/opt/split_invalid_unreachable_pass.h new file mode 100644 index 0000000..a561344 --- /dev/null +++ b/third_party/spirv-tools/source/opt/split_invalid_unreachable_pass.h @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SPLIT_INVALID_UNREACHABLE_PASS_H_ +#define SOURCE_OPT_SPLIT_INVALID_UNREACHABLE_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Attempts to legalize for WebGPU by splitting up invalid unreachable blocks. +// Specifically, looking for cases of unreachable merge-blocks and +// continue-targets that are used more then once, which is illegal in WebGPU. +class SplitInvalidUnreachablePass : public Pass { + public: + const char* name() const override { return "split-invalid-unreachable"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SPLIT_INVALID_UNREACHABLE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp b/third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp new file mode 100644 index 0000000..3ff0361 --- /dev/null +++ b/third_party/spirv-tools/source/opt/ssa_rewrite_pass.cpp @@ -0,0 +1,769 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file implements the SSA rewriting algorithm proposed in +// +// Simple and Efficient Construction of Static Single Assignment Form. +// Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013) +// In: Jhala R., De Bosschere K. (eds) +// Compiler Construction. CC 2013. +// Lecture Notes in Computer Science, vol 7791. +// Springer, Berlin, Heidelberg +// +// https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6 +// +// In contrast to common eager algorithms based on dominance and dominance +// frontier information, this algorithm works backwards from load operations. +// +// When a target variable is loaded, it queries the variable's reaching +// definition. If the reaching definition is unknown at the current location, +// it searches backwards in the CFG, inserting Phi instructions at join points +// in the CFG along the way until it finds the desired store instruction. +// +// The algorithm avoids repeated lookups using memoization. +// +// For reducible CFGs, which are a superset of the structured CFGs in SPIRV, +// this algorithm is proven to produce minimal SSA. That is, it inserts the +// minimal number of Phi instructions required to ensure the SSA property, but +// some Phi instructions may be dead +// (https://en.wikipedia.org/wiki/Static_single_assignment_form). + +#include "source/opt/ssa_rewrite_pass.h" + +#include +#include + +#include "source/opcode.h" +#include "source/opt/cfg.h" +#include "source/opt/mem_pass.h" +#include "source/opt/types.h" +#include "source/util/make_unique.h" + +// Debug logging (0: Off, 1-N: Verbosity level). Replace this with the +// implementation done for +// https://github.com/KhronosGroup/SPIRV-Tools/issues/1351 +// #define SSA_REWRITE_DEBUGGING_LEVEL 3 + +#ifdef SSA_REWRITE_DEBUGGING_LEVEL +#include +#else +#define SSA_REWRITE_DEBUGGING_LEVEL 0 +#endif + +namespace spvtools { +namespace opt { + +namespace { +const uint32_t kStoreValIdInIdx = 1; +const uint32_t kVariableInitIdInIdx = 1; +const uint32_t kDebugDeclareOperandVariableIdx = 5; +} // namespace + +std::string SSARewriter::PhiCandidate::PrettyPrint(const CFG* cfg) const { + std::ostringstream str; + str << "%" << result_id_ << " = Phi[%" << var_id_ << ", BB %" << bb_->id() + << "]("; + if (phi_args_.size() > 0) { + uint32_t arg_ix = 0; + for (uint32_t pred_label : cfg->preds(bb_->id())) { + uint32_t arg_id = phi_args_[arg_ix++]; + str << "[%" << arg_id << ", bb(%" << pred_label << ")] "; + } + } + str << ")"; + if (copy_of_ != 0) { + str << " [COPY OF " << copy_of_ << "]"; + } + str << ((is_complete_) ? " [COMPLETE]" : " [INCOMPLETE]"); + + return str.str(); +} + +SSARewriter::PhiCandidate& SSARewriter::CreatePhiCandidate(uint32_t var_id, + BasicBlock* bb) { + // TODO(1841): Handle id overflow. + uint32_t phi_result_id = pass_->context()->TakeNextId(); + auto result = phi_candidates_.emplace( + phi_result_id, PhiCandidate(var_id, phi_result_id, bb)); + PhiCandidate& phi_candidate = result.first->second; + return phi_candidate; +} + +void SSARewriter::ReplacePhiUsersWith(const PhiCandidate& phi_to_remove, + uint32_t repl_id) { + for (uint32_t user_id : phi_to_remove.users()) { + PhiCandidate* user_phi = GetPhiCandidate(user_id); + BasicBlock* bb = pass_->context()->get_instr_block(user_id); + if (user_phi) { + // If the user is a Phi candidate, replace all arguments that refer to + // |phi_to_remove.result_id()| with |repl_id|. + for (uint32_t& arg : user_phi->phi_args()) { + if (arg == phi_to_remove.result_id()) { + arg = repl_id; + } + } + } else if (bb->id() == user_id) { + // The phi candidate is the definition of the variable at basic block + // |bb|. We must change this to the replacement. + WriteVariable(phi_to_remove.var_id(), bb, repl_id); + } else { + // For regular loads, traverse the |load_replacement_| table looking for + // instances of |phi_to_remove|. + for (auto& it : load_replacement_) { + if (it.second == phi_to_remove.result_id()) { + it.second = repl_id; + } + } + } + } +} + +uint32_t SSARewriter::TryRemoveTrivialPhi(PhiCandidate* phi_candidate) { + uint32_t same_id = 0; + for (uint32_t arg_id : phi_candidate->phi_args()) { + if (arg_id == same_id || arg_id == phi_candidate->result_id()) { + // This is a self-reference operand or a reference to the same value ID. + continue; + } + if (same_id != 0) { + // This Phi candidate merges at least two values. Therefore, it is not + // trivial. + assert(phi_candidate->copy_of() == 0 && + "Phi candidate transitioning from copy to non-copy."); + return phi_candidate->result_id(); + } + same_id = arg_id; + } + + // The previous logic has determined that this Phi candidate |phi_candidate| + // is trivial. It is essentially the copy operation phi_candidate->phi_result + // = Phi(same, same, same, ...). Since it is not necessary, we can re-route + // all the users of |phi_candidate->phi_result| to all its users, and remove + // |phi_candidate|. + + // Mark the Phi candidate as a trivial copy of |same_id|, so it won't be + // generated. + phi_candidate->MarkCopyOf(same_id); + + assert(same_id != 0 && "Completed Phis cannot have %0 in their arguments"); + + // Since |phi_candidate| always produces |same_id|, replace all the users of + // |phi_candidate| with |same_id|. + ReplacePhiUsersWith(*phi_candidate, same_id); + + return same_id; +} + +uint32_t SSARewriter::AddPhiOperands(PhiCandidate* phi_candidate) { + assert(phi_candidate->phi_args().size() == 0 && + "Phi candidate already has arguments"); + + bool found_0_arg = false; + for (uint32_t pred : pass_->cfg()->preds(phi_candidate->bb()->id())) { + BasicBlock* pred_bb = pass_->cfg()->block(pred); + + // If |pred_bb| is not sealed, use %0 to indicate that + // |phi_candidate| needs to be completed after the whole CFG has + // been processed. + // + // Note that we cannot call GetReachingDef() in these cases + // because this would generate an empty Phi candidate in + // |pred_bb|. When |pred_bb| is later processed, a new definition + // for |phi_candidate->var_id_| will be lost because + // |phi_candidate| will still be reached by the empty Phi. + // + // Consider: + // + // BB %23: + // %38 = Phi[%i](%int_0[%1], %39[%25]) + // + // ... + // + // BB %25: [Starts unsealed] + // %39 = Phi[%i]() + // %34 = ... + // OpStore %i %34 -> Currdef(%i) at %25 is %34 + // OpBranch %23 + // + // When we first create the Phi in %38, we add an operandless Phi in + // %39 to hold the unknown reaching def for %i. + // + // But then, when we go to complete %39 at the end. The reaching def + // for %i in %25's predecessor is %38 itself. So we miss the fact + // that %25 has a def for %i that should be used. + // + // By making the argument %0, we make |phi_candidate| incomplete, + // which will cause it to be completed after the whole CFG has + // been scanned. + uint32_t arg_id = IsBlockSealed(pred_bb) + ? GetReachingDef(phi_candidate->var_id(), pred_bb) + : 0; + phi_candidate->phi_args().push_back(arg_id); + + if (arg_id == 0) { + found_0_arg = true; + } else { + // If this argument is another Phi candidate, add |phi_candidate| to the + // list of users for the defining Phi. + PhiCandidate* defining_phi = GetPhiCandidate(arg_id); + if (defining_phi && defining_phi != phi_candidate) { + defining_phi->AddUser(phi_candidate->result_id()); + } + } + } + + // If we could not fill-in all the arguments of this Phi, mark it incomplete + // so it gets completed after the whole CFG has been processed. + if (found_0_arg) { + phi_candidate->MarkIncomplete(); + incomplete_phis_.push(phi_candidate); + return phi_candidate->result_id(); + } + + // Try to remove |phi_candidate|, if it's trivial. + uint32_t repl_id = TryRemoveTrivialPhi(phi_candidate); + if (repl_id == phi_candidate->result_id()) { + // |phi_candidate| is complete and not trivial. Add it to the + // list of Phi candidates to generate. + phi_candidate->MarkComplete(); + phis_to_generate_.push_back(phi_candidate); + } + + return repl_id; +} + +uint32_t SSARewriter::GetValueAtBlock(uint32_t var_id, BasicBlock* bb) { + assert(bb != nullptr); + const auto& bb_it = defs_at_block_.find(bb); + if (bb_it != defs_at_block_.end()) { + const auto& current_defs = bb_it->second; + const auto& var_it = current_defs.find(var_id); + if (var_it != current_defs.end()) { + return var_it->second; + } + } + return 0; +} + +uint32_t SSARewriter::GetReachingDef(uint32_t var_id, BasicBlock* bb) { + // If |var_id| has a definition in |bb|, return it. + uint32_t val_id = GetValueAtBlock(var_id, bb); + if (val_id != 0) return val_id; + + // Otherwise, look up the value for |var_id| in |bb|'s predecessors. + auto& predecessors = pass_->cfg()->preds(bb->id()); + if (predecessors.size() == 1) { + // If |bb| has exactly one predecessor, we look for |var_id|'s definition + // there. + val_id = GetReachingDef(var_id, pass_->cfg()->block(predecessors[0])); + } else if (predecessors.size() > 1) { + // If there is more than one predecessor, this is a join block which may + // require a Phi instruction. This will act as |var_id|'s current + // definition to break potential cycles. + PhiCandidate& phi_candidate = CreatePhiCandidate(var_id, bb); + + // Set the value for |bb| to avoid an infinite recursion. + WriteVariable(var_id, bb, phi_candidate.result_id()); + val_id = AddPhiOperands(&phi_candidate); + } + + // If we could not find a store for this variable in the path from the root + // of the CFG, the variable is not defined, so we use undef. + if (val_id == 0) { + val_id = pass_->GetUndefVal(var_id); + if (val_id == 0) { + return 0; + } + } + + WriteVariable(var_id, bb, val_id); + + return val_id; +} + +void SSARewriter::SealBlock(BasicBlock* bb) { + auto result = sealed_blocks_.insert(bb); + (void)result; + assert(result.second == true && + "Tried to seal the same basic block more than once."); +} + +void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) { + auto opcode = inst->opcode(); + assert((opcode == SpvOpStore || opcode == SpvOpVariable) && + "Expecting a store or a variable definition instruction."); + + uint32_t var_id = 0; + uint32_t val_id = 0; + if (opcode == SpvOpStore) { + (void)pass_->GetPtr(inst, &var_id); + val_id = inst->GetSingleWordInOperand(kStoreValIdInIdx); + } else if (inst->NumInOperands() >= 2) { + var_id = inst->result_id(); + val_id = inst->GetSingleWordInOperand(kVariableInitIdInIdx); + } + if (pass_->IsTargetVar(var_id)) { + WriteVariable(var_id, bb, val_id); + pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible( + inst, var_id, val_id, inst, &decls_invisible_to_value_assignment_); + +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': " + << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << "\n"; +#endif + } +} + +bool SSARewriter::ProcessLoad(Instruction* inst, BasicBlock* bb) { + // Get the pointer that we are using to load from. + uint32_t var_id = 0; + (void)pass_->GetPtr(inst, &var_id); + + // Get the immediate reaching definition for |var_id|. + // + // In the presence of variable pointers, the reaching definition may be + // another pointer. For example, the following fragment: + // + // %2 = OpVariable %_ptr_Input_float Input + // %11 = OpVariable %_ptr_Function__ptr_Input_float Function + // OpStore %11 %2 + // %12 = OpLoad %_ptr_Input_float %11 + // %13 = OpLoad %float %12 + // + // corresponds to the pseudo-code: + // + // layout(location = 0) in flat float *%2 + // float %13; + // float *%12; + // float **%11; + // *%11 = %2; + // %12 = *%11; + // %13 = *%12; + // + // which ultimately, should correspond to: + // + // %13 = *%2; + // + // During rewriting, the pointer %12 is found to be replaceable by %2 (i.e., + // load_replacement_[12] is 2). However, when processing the load + // %13 = *%12, the type of %12's reaching definition is another float + // pointer (%2), instead of a float value. + // + // When this happens, we need to continue looking up the reaching definition + // chain until we get to a float value or a non-target var (i.e. a variable + // that cannot be SSA replaced, like %2 in this case since it is a function + // argument). + analysis::DefUseManager* def_use_mgr = pass_->context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = pass_->context()->get_type_mgr(); + analysis::Type* load_type = type_mgr->GetType(inst->type_id()); + uint32_t val_id = 0; + bool found_reaching_def = false; + while (!found_reaching_def) { + if (!pass_->IsTargetVar(var_id)) { + // If the variable we are loading from is not an SSA target (globals, + // function parameters), do nothing. + return true; + } + + val_id = GetReachingDef(var_id, bb); + if (val_id == 0) { + return false; + } + + // If the reaching definition is a pointer type different than the type of + // the instruction we are analyzing, then it must be a reference to another + // pointer (otherwise, this would be invalid SPIRV). We continue + // de-referencing it by making |val_id| be |var_id|. + // + // NOTE: if there is no reaching definition instruction, it means |val_id| + // is an undef. + Instruction* reaching_def_inst = def_use_mgr->GetDef(val_id); + if (reaching_def_inst && + !type_mgr->GetType(reaching_def_inst->type_id())->IsSame(load_type)) { + var_id = val_id; + } else { + found_reaching_def = true; + } + } + + // Schedule a replacement for the result of this load instruction with + // |val_id|. After all the rewriting decisions are made, every use of + // this load will be replaced with |val_id|. + uint32_t load_id = inst->result_id(); + assert(load_replacement_.count(load_id) == 0); + load_replacement_[load_id] = val_id; + PhiCandidate* defining_phi = GetPhiCandidate(val_id); + if (defining_phi) { + defining_phi->AddUser(load_id); + } + +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "\tFound load: " + << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << " (replacement for %" << load_id << " is %" << val_id << ")\n"; +#endif + + return true; +} + +void SSARewriter::PrintPhiCandidates() const { + std::cerr << "\nPhi candidates:\n"; + for (const auto& phi_it : phi_candidates_) { + std::cerr << "\tBB %" << phi_it.second.bb()->id() << ": " + << phi_it.second.PrettyPrint(pass_->cfg()) << "\n"; + } + std::cerr << "\n"; +} + +void SSARewriter::PrintReplacementTable() const { + std::cerr << "\nLoad replacement table\n"; + for (const auto& it : load_replacement_) { + std::cerr << "\t%" << it.first << " -> %" << it.second << "\n"; + } + std::cerr << "\n"; +} + +bool SSARewriter::GenerateSSAReplacements(BasicBlock* bb) { +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "Generating SSA replacements for block: " << bb->id() << "\n"; + std::cerr << bb->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << "\n"; +#endif + + for (auto& inst : *bb) { + auto opcode = inst.opcode(); + if (opcode == SpvOpStore || opcode == SpvOpVariable) { + ProcessStore(&inst, bb); + } else if (inst.opcode() == SpvOpLoad) { + if (!ProcessLoad(&inst, bb)) { + return false; + } + } + } + + // Seal |bb|. This means that all the stores in it have been scanned and + // it's ready to feed them into its successors. + SealBlock(bb); + +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + PrintPhiCandidates(); + PrintReplacementTable(); + std::cerr << "\n\n"; +#endif + return true; +} + +uint32_t SSARewriter::GetReplacement(std::pair repl) { + uint32_t val_id = repl.second; + auto it = load_replacement_.find(val_id); + while (it != load_replacement_.end()) { + val_id = it->second; + it = load_replacement_.find(val_id); + } + return val_id; +} + +uint32_t SSARewriter::GetPhiArgument(const PhiCandidate* phi_candidate, + uint32_t ix) { + assert(phi_candidate->IsReady() && + "Tried to get the final argument from an incomplete/trivial Phi"); + + uint32_t arg_id = phi_candidate->phi_args()[ix]; + while (arg_id != 0) { + PhiCandidate* phi_user = GetPhiCandidate(arg_id); + if (phi_user == nullptr || phi_user->IsReady()) { + // If the argument is not a Phi or it's a Phi candidate ready to be + // emitted, return it. + return arg_id; + } + arg_id = phi_user->copy_of(); + } + + assert(false && + "No Phi candidates in the copy-of chain are ready to be generated"); + + return 0; +} + +bool SSARewriter::ApplyReplacements() { + bool modified = false; + +#if SSA_REWRITE_DEBUGGING_LEVEL > 2 + std::cerr << "\n\nApplying replacement decisions to IR\n\n"; + PrintPhiCandidates(); + PrintReplacementTable(); + std::cerr << "\n\n"; +#endif + + // Add Phi instructions from completed Phi candidates. + std::vector generated_phis; + for (const PhiCandidate* phi_candidate : phis_to_generate_) { +#if SSA_REWRITE_DEBUGGING_LEVEL > 2 + std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg()) + << "\n"; +#endif + + assert(phi_candidate->is_complete() && + "Tried to instantiate a Phi instruction from an incomplete Phi " + "candidate"); + + auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id()); + + // Build the vector of operands for the new OpPhi instruction. + uint32_t type_id = pass_->GetPointeeTypeId(local_var); + std::vector phi_operands; + uint32_t arg_ix = 0; + std::unordered_map already_seen; + for (uint32_t pred_label : pass_->cfg()->preds(phi_candidate->bb()->id())) { + uint32_t op_val_id = GetPhiArgument(phi_candidate, arg_ix++); + if (already_seen.count(pred_label) == 0) { + phi_operands.push_back( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {op_val_id}}); + phi_operands.push_back( + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {pred_label}}); + already_seen[pred_label] = op_val_id; + } else { + // It is possible that there are two edges from the same parent block. + // Since the OpPhi can have only one entry for each parent, we have to + // make sure the two edges are consistent with each other. + assert(already_seen[pred_label] == op_val_id && + "Inconsistent value for duplicate edges."); + } + } + + // Generate a new OpPhi instruction and insert it in its basic + // block. + std::unique_ptr phi_inst( + new Instruction(pass_->context(), SpvOpPhi, type_id, + phi_candidate->result_id(), phi_operands)); + generated_phis.push_back(phi_inst.get()); + pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst); + pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb()); + auto insert_it = phi_candidate->bb()->begin(); + insert_it = insert_it.InsertBefore(std::move(phi_inst)); + pass_->context()->get_decoration_mgr()->CloneDecorations( + phi_candidate->var_id(), phi_candidate->result_id(), + {SpvDecorationRelaxedPrecision}); + + // Add DebugValue for the new OpPhi instruction. + insert_it->SetDebugScope(local_var->GetDebugScope()); + pass_->context()->get_debug_info_mgr()->AddDebugValueIfVarDeclIsVisible( + &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(), + &*insert_it, &decls_invisible_to_value_assignment_); + + modified = true; + } + + // Scan uses for all inserted Phi instructions. Do this separately from the + // registration of the Phi instruction itself to avoid trying to analyze + // uses of Phi instructions that have not been registered yet. + for (Instruction* phi_inst : generated_phis) { + pass_->get_def_use_mgr()->AnalyzeInstUse(&*phi_inst); + } + +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "\n\nReplacing the result of load instructions with the " + "corresponding SSA id\n\n"; +#endif + + // Apply replacements from the load replacement table. + for (auto& repl : load_replacement_) { + uint32_t load_id = repl.first; + uint32_t val_id = GetReplacement(repl); + Instruction* load_inst = + pass_->context()->get_def_use_mgr()->GetDef(load_id); + +#if SSA_REWRITE_DEBUGGING_LEVEL > 2 + std::cerr << "\t" + << load_inst->PrettyPrint( + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES) + << " (%" << load_id << " -> %" << val_id << ")\n"; +#endif + + // Remove the load instruction and replace all the uses of this load's + // result with |val_id|. Kill any names or decorates using the load's + // result before replacing to prevent incorrect replacement in those + // instructions. + pass_->context()->KillNamesAndDecorates(load_id); + pass_->context()->ReplaceAllUsesWith(load_id, val_id); + pass_->context()->KillInst(load_inst); + modified = true; + } + + return modified; +} + +void SSARewriter::FinalizePhiCandidate(PhiCandidate* phi_candidate) { + assert(phi_candidate->phi_args().size() > 0 && + "Phi candidate should have arguments"); + + uint32_t ix = 0; + for (uint32_t pred : pass_->cfg()->preds(phi_candidate->bb()->id())) { + BasicBlock* pred_bb = pass_->cfg()->block(pred); + uint32_t& arg_id = phi_candidate->phi_args()[ix++]; + if (arg_id == 0) { + // If |pred_bb| is still not sealed, it means it's unreachable. In this + // case, we just use Undef as an argument. + arg_id = IsBlockSealed(pred_bb) + ? GetReachingDef(phi_candidate->var_id(), pred_bb) + : pass_->GetUndefVal(phi_candidate->var_id()); + } + } + + // This candidate is now completed. + phi_candidate->MarkComplete(); + + // If |phi_candidate| is not trivial, add it to the list of Phis to + // generate. + if (TryRemoveTrivialPhi(phi_candidate) == phi_candidate->result_id()) { + // If we could not remove |phi_candidate|, it means that it is complete + // and not trivial. Add it to the list of Phis to generate. + assert(!phi_candidate->copy_of() && "A completed Phi cannot be trivial."); + phis_to_generate_.push_back(phi_candidate); + } +} + +void SSARewriter::FinalizePhiCandidates() { +#if SSA_REWRITE_DEBUGGING_LEVEL > 1 + std::cerr << "Finalizing Phi candidates:\n\n"; + PrintPhiCandidates(); + std::cerr << "\n"; +#endif + + // Now, complete the collected candidates. + while (incomplete_phis_.size() > 0) { + PhiCandidate* phi_candidate = incomplete_phis_.front(); + incomplete_phis_.pop(); + FinalizePhiCandidate(phi_candidate); + } +} + +Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) { + // For the cases the value assignment is invisible to DebugDeclare e.g., + // the argument passing for an inlined function. + // + // Before inlining foo(int x): + // a = 3; + // foo(3); + // After inlining: + // a = 3; // we want to specify "DebugValue: %x = %int_3" + // foo and x disappeared! + // + // We want to specify the value for the variable using |defs_at_block_[bb]|, + // where |bb| is the basic block contains the decl. + DominatorAnalysis* dom_tree = pass_->context()->GetDominatorAnalysis(fp); + Pass::Status status = Pass::Status::SuccessWithoutChange; + for (auto* decl : decls_invisible_to_value_assignment_) { + uint32_t var_id = + decl->GetSingleWordOperand(kDebugDeclareOperandVariableIdx); + auto* var = pass_->get_def_use_mgr()->GetDef(var_id); + if (var->opcode() == SpvOpFunctionParameter) continue; + + auto* bb = pass_->context()->get_instr_block(decl); + uint32_t value_id = GetValueAtBlock(var_id, bb); + Instruction* value = nullptr; + if (value_id) value = pass_->get_def_use_mgr()->GetDef(value_id); + + // If |value| is defined before the function body, it dominates |decl|. + // If |value| dominates |decl|, we can set it as DebugValue. + if (value && (pass_->context()->get_instr_block(value) == nullptr || + dom_tree->Dominates(value, decl))) { + if (!pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl( + decl, value->result_id())) { + return Pass::Status::Failure; + } + } else { + // If |value| in the same basic block does not dominate |decl|, we can + // assign the value in the immediate dominator. + value_id = GetValueAtBlock(var_id, dom_tree->ImmediateDominator(bb)); + if (value_id && + !pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl( + decl, value_id)) { + return Pass::Status::Failure; + } + } + + // DebugDeclares of target variables will be removed by + // SSARewritePass::Process(). + if (!pass_->IsTargetVar(var_id)) { + pass_->context()->get_debug_info_mgr()->KillDebugDeclares(var_id); + } + status = Pass::Status::SuccessWithChange; + } + return status; +} + +Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) { +#if SSA_REWRITE_DEBUGGING_LEVEL > 0 + std::cerr << "Function before SSA rewrite:\n" + << fp->PrettyPrint(0) << "\n\n\n"; +#endif + + // Collect variables that can be converted into SSA IDs. + pass_->CollectTargetVars(fp); + + // Generate all the SSA replacements and Phi candidates. This will + // generate incomplete and trivial Phis. + bool succeeded = pass_->cfg()->WhileEachBlockInReversePostOrder( + fp->entry().get(), [this](BasicBlock* bb) { + if (!GenerateSSAReplacements(bb)) { + return false; + } + return true; + }); + + if (!succeeded) { + return Pass::Status::Failure; + } + + // Remove trivial Phis and add arguments to incomplete Phis. + FinalizePhiCandidates(); + + // Finally, apply all the replacements in the IR. + bool modified = ApplyReplacements(); + + auto status = AddDebugValuesForInvisibleDebugDecls(fp); + if (status == Pass::Status::SuccessWithChange || + status == Pass::Status::Failure) { + return status; + } + +#if SSA_REWRITE_DEBUGGING_LEVEL > 0 + std::cerr << "\n\n\nFunction after SSA rewrite:\n" + << fp->PrettyPrint(0) << "\n"; +#endif + + return modified ? Pass::Status::SuccessWithChange + : Pass::Status::SuccessWithoutChange; +} + +Pass::Status SSARewritePass::Process() { + Status status = Status::SuccessWithoutChange; + for (auto& fn : *get_module()) { + status = + CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn)); + // Kill DebugDeclares for target variables. + for (auto var_id : seen_target_vars_) { + context()->get_debug_info_mgr()->KillDebugDeclares(var_id); + } + if (status == Status::Failure) { + break; + } + } + return status; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/ssa_rewrite_pass.h b/third_party/spirv-tools/source/opt/ssa_rewrite_pass.h new file mode 100644 index 0000000..1f4cd24 --- /dev/null +++ b/third_party/spirv-tools/source/opt/ssa_rewrite_pass.h @@ -0,0 +1,315 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_SSA_REWRITE_PASS_H_ +#define SOURCE_OPT_SSA_REWRITE_PASS_H_ + +#include +#include +#include +#include +#include +#include + +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" +#include "source/opt/mem_pass.h" + +namespace spvtools { +namespace opt { + +// Utility class for passes that need to rewrite a function into SSA. This +// converts load/store operations on function-local variables into SSA IDs, +// which allows them to be the target of optimizing transformations. +// +// Store and load operations to these variables are converted into +// operations on SSA IDs. Phi instructions are added when needed. See the +// SSA construction paper for algorithmic details +// (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6) +class SSARewriter { + public: + SSARewriter(MemPass* pass) : pass_(pass) {} + + // Rewrites SSA-target variables in function |fp| into SSA. This is the + // entry point for the SSA rewrite algorithm. SSA-target variables are + // locally defined variables that meet the criteria set by IsSSATargetVar. + // + // Returns whether the function was modified or not, and whether or not the + // rewrite was successful. + Pass::Status RewriteFunctionIntoSSA(Function* fp); + + private: + class PhiCandidate { + public: + explicit PhiCandidate(uint32_t var, uint32_t result, BasicBlock* block) + : var_id_(var), + result_id_(result), + bb_(block), + phi_args_(), + copy_of_(0), + is_complete_(false), + users_() {} + + uint32_t var_id() const { return var_id_; } + uint32_t result_id() const { return result_id_; } + BasicBlock* bb() const { return bb_; } + std::vector& phi_args() { return phi_args_; } + const std::vector& phi_args() const { return phi_args_; } + uint32_t copy_of() const { return copy_of_; } + bool is_complete() const { return is_complete_; } + std::vector& users() { return users_; } + const std::vector& users() const { return users_; } + + // Marks this phi candidate as a trivial copy of |orig_id|. + void MarkCopyOf(uint32_t orig_id) { copy_of_ = orig_id; } + + // Marks this phi candidate as incomplete. + void MarkIncomplete() { is_complete_ = false; } + + // Marks this phi candidate as complete. + void MarkComplete() { is_complete_ = true; } + + // Returns true if this Phi candidate is ready to be emitted. + bool IsReady() const { return is_complete() && copy_of() == 0; } + + // Pretty prints this Phi candidate into a string and returns it. |cfg| is + // needed to lookup basic block predecessors. + std::string PrettyPrint(const CFG* cfg) const; + + // Registers |operand_id| as a user of this Phi candidate. + void AddUser(uint32_t operand_id) { users_.push_back(operand_id); } + + private: + // Variable ID that this Phi is merging. + uint32_t var_id_; + + // SSA ID generated by this Phi (i.e., this is the result ID of the eventual + // Phi instruction). + uint32_t result_id_; + + // Basic block to hold this Phi. + BasicBlock* bb_; + + // Vector of operands for every predecessor block of |bb|. This vector is + // organized so that the Ith slot contains the argument coming from the Ith + // predecessor of |bb|. + std::vector phi_args_; + + // If this Phi is a trivial copy of another Phi, this is the ID of the + // original. If this is 0, it means that this is not a trivial Phi. + uint32_t copy_of_; + + // False, if this Phi candidate has no arguments or at least one argument is + // %0. + bool is_complete_; + + // List of all users for this Phi instruction. Each element is the result ID + // of the load instruction replaced by this Phi, or the result ID of a Phi + // candidate that has this Phi in its list of operands. + std::vector users_; + }; + + // Type used to keep track of store operations in each basic block. + typedef std::unordered_map> + BlockDefsMap; + + // Generates all the SSA rewriting decisions for basic block |bb|. This + // populates the Phi candidate table (|phi_candidate_|) and the load + // replacement table (|load_replacement_). Returns true if successful. + bool GenerateSSAReplacements(BasicBlock* bb); + + // Seals block |bb|. Sealing a basic block means |bb| and all its + // predecessors of |bb| have been scanned for loads/stores. + void SealBlock(BasicBlock* bb); + + // Returns true if |bb| has been sealed. + bool IsBlockSealed(BasicBlock* bb) { return sealed_blocks_.count(bb) != 0; } + + // Returns the Phi candidate with result ID |id| if it exists in the table + // |phi_candidates_|. If no such Phi candidate exists, it returns nullptr. + PhiCandidate* GetPhiCandidate(uint32_t id) { + auto it = phi_candidates_.find(id); + return (it != phi_candidates_.end()) ? &it->second : nullptr; + } + + // Replaces all the users of Phi candidate |phi_cand| to be users of + // |repl_id|. + void ReplacePhiUsersWith(const PhiCandidate& phi_cand, uint32_t repl_id); + + // Returns the value ID that should replace the load ID in the given + // replacement pair |repl|. The replacement is a pair (|load_id|, |val_id|). + // If |val_id| is itself replaced by another value in the table, this function + // will look the replacement for |val_id| until it finds one that is not + // itself replaced. For instance, given: + // + // %34 = OpLoad %float %f1 + // OpStore %t %34 + // %36 = OpLoad %float %t + // + // Assume that %f1 is reached by a Phi candidate %42, the load + // replacement table will have the following entries: + // + // %34 -> %42 + // %36 -> %34 + // + // So, when looking for the replacement for %36, we should not use + // %34. Rather, we should use %42. To do this, the chain of + // replacements must be followed until we reach an element that has + // no replacement. + uint32_t GetReplacement(std::pair repl); + + // Returns the argument at index |ix| from |phi_candidate|. If argument |ix| + // comes from a trivial Phi, it follows the copy-of chain from that trivial + // Phi until it finds the original Phi candidate. + // + // This is only valid after all Phi candidates have been completed. It can + // only be called when generating the IR for these Phis. + uint32_t GetPhiArgument(const PhiCandidate* phi_candidate, uint32_t ix); + + // Applies all the SSA replacement decisions. This replaces loads/stores to + // SSA target variables with their corresponding SSA IDs, and inserts Phi + // instructions for them. + bool ApplyReplacements(); + + // Registers a definition for variable |var_id| in basic block |bb| with + // value |val_id|. + void WriteVariable(uint32_t var_id, BasicBlock* bb, uint32_t val_id) { + defs_at_block_[bb][var_id] = val_id; + if (auto* pc = GetPhiCandidate(val_id)) { + pc->AddUser(bb->id()); + } + } + + // Returns the value of |var_id| at |bb| if |defs_at_block_| contains it. + // Otherwise, returns 0. + uint32_t GetValueAtBlock(uint32_t var_id, BasicBlock* bb); + + // Processes the store operation |inst| in basic block |bb|. This extracts + // the variable ID being stored into, determines whether the variable is an + // SSA-target variable, and, if it is, it stores its value in the + // |defs_at_block_| map. + void ProcessStore(Instruction* inst, BasicBlock* bb); + + // Processes the load operation |inst| in basic block |bb|. This extracts + // the variable ID being stored into, determines whether the variable is an + // SSA-target variable, and, if it is, it reads its reaching definition by + // calling |GetReachingDef|. Returns true if successful. + bool ProcessLoad(Instruction* inst, BasicBlock* bb); + + // Reads the current definition for variable |var_id| in basic block |bb|. + // If |var_id| is not defined in block |bb| it walks up the predecessors of + // |bb|, creating new Phi candidates along the way, if needed. + // + // It returns the value for |var_id| from the RHS of the current reaching + // definition for |var_id|. + uint32_t GetReachingDef(uint32_t var_id, BasicBlock* bb); + + // Adds arguments to |phi_candidate| by getting the reaching definition of + // |phi_candidate|'s variable on each of the predecessors of its basic + // block. After populating the argument list, it determines whether all its + // arguments are the same. If so, it returns the ID of the argument that + // this Phi copies. + uint32_t AddPhiOperands(PhiCandidate* phi_candidate); + + // Creates a Phi candidate instruction for variable |var_id| in basic block + // |bb|. + // + // Since the rewriting algorithm may remove Phi candidates when it finds + // them to be trivial, we avoid the expense of creating actual Phi + // instructions by keeping a pool of Phi candidates (|phi_candidates_|) + // during rewriting. + // + // Once the candidate Phi is created, it returns its ID. + PhiCandidate& CreatePhiCandidate(uint32_t var_id, BasicBlock* bb); + + // Attempts to remove a trivial Phi candidate |phi_cand|. Trivial Phis are + // those that only reference themselves and one other value |val| any number + // of times. This will try to remove any other Phis that become trivial + // after |phi_cand| is removed. + // + // If |phi_cand| is trivial, it returns the SSA ID for the value that should + // replace it. Otherwise, it returns the SSA ID for |phi_cand|. + uint32_t TryRemoveTrivialPhi(PhiCandidate* phi_cand); + + // Finalizes |phi_candidate| by replacing every argument that is still %0 + // with its reaching definition. + void FinalizePhiCandidate(PhiCandidate* phi_candidate); + + // Finalizes processing of Phi candidates. Once the whole function has been + // scanned for loads and stores, the CFG will still have some incomplete and + // trivial Phis. This will add missing arguments and remove trivial Phi + // candidates. + void FinalizePhiCandidates(); + + // Adds DebugValues for DebugDeclares in + // |decls_invisible_to_value_assignment_|. Returns whether the function was + // modified or not, and whether or not the conversion was successful. + Pass::Status AddDebugValuesForInvisibleDebugDecls(Function* fp); + + // Prints the table of Phi candidates to std::cerr. + void PrintPhiCandidates() const; + + // Prints the load replacement table to std::cerr. + void PrintReplacementTable() const; + + // Map holding the value of every SSA-target variable at every basic block + // where the variable is stored. defs_at_block_[block][var_id] = val_id + // means that there is a store or Phi instruction for variable |var_id| at + // basic block |block| with value |val_id|. + BlockDefsMap defs_at_block_; + + // Map, indexed by Phi ID, holding all the Phi candidates created during SSA + // rewriting. |phi_candidates_[id]| returns the Phi candidate whose result + // is |id|. + std::unordered_map phi_candidates_; + + // Queue of incomplete Phi candidates. These are Phi candidates created at + // unsealed blocks. They need to be completed before they are instantiated + // in ApplyReplacements. + std::queue incomplete_phis_; + + // List of completed Phi candidates. These are the only candidates that + // will become real Phi instructions. + std::vector phis_to_generate_; + + // SSA replacement table. This maps variable IDs, resulting from a load + // operation, to the value IDs that will replace them after SSA rewriting. + // After all the rewriting decisions are made, a final scan through the IR + // is done to replace all uses of the original load ID with the value ID. + std::unordered_map load_replacement_; + + // Set of blocks that have been sealed already. + std::unordered_set sealed_blocks_; + + // Memory pass requesting the SSA rewriter. + MemPass* pass_; + + // Set of DebugDeclare instructions that are not added as DebugValue because + // they are invisible to the store or phi instructions. + std::unordered_set decls_invisible_to_value_assignment_; +}; + +class SSARewritePass : public MemPass { + public: + SSARewritePass() = default; + + const char* name() const override { return "ssa-rewrite"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_SSA_REWRITE_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/strength_reduction_pass.cpp b/third_party/spirv-tools/source/opt/strength_reduction_pass.cpp new file mode 100644 index 0000000..ab7c4eb --- /dev/null +++ b/third_party/spirv-tools/source/opt/strength_reduction_pass.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strength_reduction_pass.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" + +namespace { +// Count the number of trailing zeros in the binary representation of +// |constVal|. +uint32_t CountTrailingZeros(uint32_t constVal) { + // Faster if we use the hardware count trailing zeros instruction. + // If not available, we could create a table. + uint32_t shiftAmount = 0; + while ((constVal & 1) == 0) { + ++shiftAmount; + constVal = (constVal >> 1); + } + return shiftAmount; +} + +// Return true if |val| is a power of 2. +bool IsPowerOf2(uint32_t val) { + // The idea is that the & will clear out the least + // significant 1 bit. If it is a power of 2, then + // there is exactly 1 bit set, and the value becomes 0. + if (val == 0) return false; + return ((val - 1) & val) == 0; +} + +} // namespace + +namespace spvtools { +namespace opt { + +Pass::Status StrengthReductionPass::Process() { + // Initialize the member variables on a per module basis. + bool modified = false; + int32_type_id_ = 0; + uint32_type_id_ = 0; + std::memset(constant_ids_, 0, sizeof(constant_ids_)); + + FindIntTypesAndConstants(); + modified = ScanFunctions(); + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool StrengthReductionPass::ReplaceMultiplyByPowerOf2( + BasicBlock::iterator* inst) { + assert((*inst)->opcode() == SpvOp::SpvOpIMul && + "Only works for multiplication of integers."); + bool modified = false; + + // Currently only works on 32-bit integers. + if ((*inst)->type_id() != int32_type_id_ && + (*inst)->type_id() != uint32_type_id_) { + return modified; + } + + // Check the operands for a constant that is a power of 2. + for (int i = 0; i < 2; i++) { + uint32_t opId = (*inst)->GetSingleWordInOperand(i); + Instruction* opInst = get_def_use_mgr()->GetDef(opId); + if (opInst->opcode() == SpvOp::SpvOpConstant) { + // We found a constant operand. + uint32_t constVal = opInst->GetSingleWordOperand(2); + + if (IsPowerOf2(constVal)) { + modified = true; + uint32_t shiftAmount = CountTrailingZeros(constVal); + uint32_t shiftConstResultId = GetConstantId(shiftAmount); + + // Create the new instruction. + uint32_t newResultId = TakeNextId(); + std::vector newOperands; + newOperands.push_back((*inst)->GetInOperand(1 - i)); + Operand shiftOperand(spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {shiftConstResultId}); + newOperands.push_back(shiftOperand); + std::unique_ptr newInstruction( + new Instruction(context(), SpvOp::SpvOpShiftLeftLogical, + (*inst)->type_id(), newResultId, newOperands)); + + // Insert the new instruction and update the data structures. + (*inst) = (*inst).InsertBefore(std::move(newInstruction)); + get_def_use_mgr()->AnalyzeInstDefUse(&*(*inst)); + ++(*inst); + context()->ReplaceAllUsesWith((*inst)->result_id(), newResultId); + + // Remove the old instruction. + Instruction* inst_to_delete = &*(*inst); + --(*inst); + context()->KillInst(inst_to_delete); + + // We do not want to replace the instruction twice if both operands + // are constants that are a power of 2. So we break here. + break; + } + } + } + + return modified; +} + +void StrengthReductionPass::FindIntTypesAndConstants() { + analysis::Integer int32(32, true); + int32_type_id_ = context()->get_type_mgr()->GetId(&int32); + analysis::Integer uint32(32, false); + uint32_type_id_ = context()->get_type_mgr()->GetId(&uint32); + for (auto iter = get_module()->types_values_begin(); + iter != get_module()->types_values_end(); ++iter) { + switch (iter->opcode()) { + case SpvOp::SpvOpConstant: + if (iter->type_id() == uint32_type_id_) { + uint32_t value = iter->GetSingleWordOperand(2); + if (value <= 32) constant_ids_[value] = iter->result_id(); + } + break; + default: + break; + } + } +} + +uint32_t StrengthReductionPass::GetConstantId(uint32_t val) { + assert(val <= 32 && + "This function does not handle constants larger than 32."); + + if (constant_ids_[val] == 0) { + if (uint32_type_id_ == 0) { + analysis::Integer uint(32, false); + uint32_type_id_ = context()->get_type_mgr()->GetTypeInstruction(&uint); + } + + // Construct the constant. + uint32_t resultId = TakeNextId(); + Operand constant(spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, + {val}); + std::unique_ptr newConstant( + new Instruction(context(), SpvOp::SpvOpConstant, uint32_type_id_, + resultId, {constant})); + get_module()->AddGlobalValue(std::move(newConstant)); + + // Notify the DefUseManager about this constant. + auto constantIter = --get_module()->types_values_end(); + get_def_use_mgr()->AnalyzeInstDef(&*constantIter); + + // Store the result id for next time. + constant_ids_[val] = resultId; + } + + return constant_ids_[val]; +} + +bool StrengthReductionPass::ScanFunctions() { + // I did not use |ForEachInst| in the module because the function that acts on + // the instruction gets a pointer to the instruction. We cannot use that to + // insert a new instruction. I want an iterator. + bool modified = false; + for (auto& func : *get_module()) { + for (auto& bb : func) { + for (auto inst = bb.begin(); inst != bb.end(); ++inst) { + switch (inst->opcode()) { + case SpvOp::SpvOpIMul: + if (ReplaceMultiplyByPowerOf2(&inst)) modified = true; + break; + default: + break; + } + } + } + } + return modified; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/strength_reduction_pass.h b/third_party/spirv-tools/source/opt/strength_reduction_pass.h new file mode 100644 index 0000000..8dfeb30 --- /dev/null +++ b/third_party/spirv-tools/source/opt/strength_reduction_pass.h @@ -0,0 +1,65 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRENGTH_REDUCTION_PASS_H_ +#define SOURCE_OPT_STRENGTH_REDUCTION_PASS_H_ + +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class StrengthReductionPass : public Pass { + public: + const char* name() const override { return "strength-reduction"; } + Status Process() override; + + private: + // Replaces multiple by power of 2 with an equivalent bit shift. + // Returns true if something changed. + bool ReplaceMultiplyByPowerOf2(BasicBlock::iterator*); + + // Scan the types and constants in the module looking for the the integer + // types that we are + // interested in. The shift operation needs a small unsigned integer. We + // need to find + // them or create them. We do not want duplicates. + void FindIntTypesAndConstants(); + + // Get the id for the given constant. If it does not exist, it will be + // created. The parameter must be between 0 and 32 inclusive. + uint32_t GetConstantId(uint32_t); + + // Replaces certain instructions in function bodies with presumably cheaper + // ones. Returns true if something changed. + bool ScanFunctions(); + + // Type ids for the types of interest, or 0 if they do not exist. + uint32_t int32_type_id_; + uint32_t uint32_type_id_; + + // constant_ids[i] is the id for unsigned integer constant i. + // We set the limit at 32 because a bit shift of a 32-bit integer does not + // need a value larger than 32. + uint32_t constant_ids_[33]; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRENGTH_REDUCTION_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/strip_atomic_counter_memory_pass.cpp b/third_party/spirv-tools/source/opt/strip_atomic_counter_memory_pass.cpp new file mode 100644 index 0000000..47714b7 --- /dev/null +++ b/third_party/spirv-tools/source/opt/strip_atomic_counter_memory_pass.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strip_atomic_counter_memory_pass.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status StripAtomicCounterMemoryPass::Process() { + bool changed = false; + context()->module()->ForEachInst([this, &changed](Instruction* inst) { + auto indices = spvOpcodeMemorySemanticsOperandIndices(inst->opcode()); + if (indices.empty()) return; + + for (auto idx : indices) { + auto mem_sem_id = inst->GetSingleWordOperand(idx); + const auto& mem_sem_inst = + context()->get_def_use_mgr()->GetDef(mem_sem_id); + // The spec explicitly says that this id must be an OpConstant + auto mem_sem_val = mem_sem_inst->GetSingleWordOperand(2); + if (!(mem_sem_val & SpvMemorySemanticsAtomicCounterMemoryMask)) { + continue; + } + mem_sem_val &= ~SpvMemorySemanticsAtomicCounterMemoryMask; + + analysis::Integer int_type(32, false); + const analysis::Type* uint32_type = + context()->get_type_mgr()->GetRegisteredType(&int_type); + auto* new_const = context()->get_constant_mgr()->GetConstant( + uint32_type, {mem_sem_val}); + auto* new_const_inst = + context()->get_constant_mgr()->GetDefiningInstruction(new_const); + auto new_const_id = new_const_inst->result_id(); + + inst->SetOperand(idx, {new_const_id}); + context()->UpdateDefUse(inst); + changed = true; + } + }); + + return changed ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/strip_atomic_counter_memory_pass.h b/third_party/spirv-tools/source/opt/strip_atomic_counter_memory_pass.h new file mode 100644 index 0000000..62e274a --- /dev/null +++ b/third_party/spirv-tools/source/opt/strip_atomic_counter_memory_pass.h @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_ +#define SOURCE_OPT_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Removes the AtomicCounterMemory bit from the value being passed into memory +// semantics. This bit being set is ignored in Vulkan environments and +// forbidden WebGPU ones. +class StripAtomicCounterMemoryPass : public Pass { + public: + const char* name() const override { return "strip-atomic-counter-memory"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisCFG | IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisScalarEvolution | + IRContext::kAnalysisRegisterPressure | + IRContext::kAnalysisValueNumberTable | + IRContext::kAnalysisStructuredCFG | + IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisIdToFuncMapping | IRContext::kAnalysisTypes | + IRContext::kAnalysisDefUse | IRContext::kAnalysisConstants; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRIP_ATOMIC_COUNT_MEMORY_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/strip_debug_info_pass.cpp b/third_party/spirv-tools/source/opt/strip_debug_info_pass.cpp new file mode 100644 index 0000000..c86ce57 --- /dev/null +++ b/third_party/spirv-tools/source/opt/strip_debug_info_pass.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strip_debug_info_pass.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status StripDebugInfoPass::Process() { + bool uses_non_semantic_info = false; + for (auto& inst : context()->module()->extensions()) { + const char* ext_name = + reinterpret_cast(&inst.GetInOperand(0).words[0]); + if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + uses_non_semantic_info = true; + } + } + + std::vector to_kill; + + // if we use non-semantic info, it may reference OpString. Do a more + // expensive pass checking the uses of the OpString to see if any are + // OpExtInst on a non-semantic instruction set. If we're not using the + // extension then we can do a simpler pass and kill all debug1 instructions + if (uses_non_semantic_info) { + for (auto& inst : context()->module()->debugs1()) { + switch (inst.opcode()) { + case SpvOpString: { + analysis::DefUseManager* def_use = context()->get_def_use_mgr(); + + // see if this string is used anywhere by a non-semantic instruction + bool no_nonsemantic_use = + def_use->WhileEachUser(&inst, [def_use](Instruction* use) { + if (use->opcode() == SpvOpExtInst) { + auto ext_inst_set = + def_use->GetDef(use->GetSingleWordInOperand(0u)); + const char* extension_name = reinterpret_cast( + &ext_inst_set->GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + // found a non-semantic use, return false as we cannot + // remove this OpString + return false; + } + } + + // other instructions can't be a non-semantic use + return true; + }); + + if (no_nonsemantic_use) to_kill.push_back(&inst); + + break; + } + + default: + to_kill.push_back(&inst); + break; + } + } + } else { + for (auto& dbg : context()->debugs1()) to_kill.push_back(&dbg); + } + + for (auto& dbg : context()->debugs2()) to_kill.push_back(&dbg); + for (auto& dbg : context()->debugs3()) to_kill.push_back(&dbg); + for (auto& dbg : context()->ext_inst_debuginfo()) to_kill.push_back(&dbg); + + // OpName must come first, since they may refer to other debug instructions. + // If they are after the instructions that refer to, then they will be killed + // when that instruction is killed, which will lead to a double kill. + std::sort(to_kill.begin(), to_kill.end(), + [](Instruction* lhs, Instruction* rhs) -> bool { + if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName) + return true; + return false; + }); + + bool modified = !to_kill.empty(); + + for (auto* inst : to_kill) context()->KillInst(inst); + + // clear OpLine information + context()->module()->ForEachInst([&modified](Instruction* inst) { + modified |= !inst->dbg_line_insts().empty(); + inst->dbg_line_insts().clear(); + }); + + if (!get_module()->trailing_dbg_line_info().empty()) { + modified = true; + get_module()->trailing_dbg_line_info().clear(); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/strip_debug_info_pass.h b/third_party/spirv-tools/source/opt/strip_debug_info_pass.h new file mode 100644 index 0000000..47a2cd4 --- /dev/null +++ b/third_party/spirv-tools/source/opt/strip_debug_info_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRIP_DEBUG_INFO_PASS_H_ +#define SOURCE_OPT_STRIP_DEBUG_INFO_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class StripDebugInfoPass : public Pass { + public: + const char* name() const override { return "strip-debug"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRIP_DEBUG_INFO_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/strip_reflect_info_pass.cpp b/third_party/spirv-tools/source/opt/strip_reflect_info_pass.cpp new file mode 100644 index 0000000..8b0f2db --- /dev/null +++ b/third_party/spirv-tools/source/opt/strip_reflect_info_pass.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/strip_reflect_info_pass.h" + +#include +#include + +#include "source/opt/instruction.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +Pass::Status StripReflectInfoPass::Process() { + bool modified = false; + + std::vector to_remove; + + bool other_uses_for_decorate_string = false; + for (auto& inst : context()->module()->annotations()) { + switch (inst.opcode()) { + case SpvOpDecorateStringGOOGLE: + if (inst.GetSingleWordInOperand(1) == SpvDecorationHlslSemanticGOOGLE) { + to_remove.push_back(&inst); + } else { + other_uses_for_decorate_string = true; + } + break; + + case SpvOpMemberDecorateStringGOOGLE: + if (inst.GetSingleWordInOperand(2) == SpvDecorationHlslSemanticGOOGLE) { + to_remove.push_back(&inst); + } else { + other_uses_for_decorate_string = true; + } + break; + + case SpvOpDecorateId: + if (inst.GetSingleWordInOperand(1) == + SpvDecorationHlslCounterBufferGOOGLE) { + to_remove.push_back(&inst); + } + break; + + default: + break; + } + } + + for (auto& inst : context()->module()->extensions()) { + const char* ext_name = + reinterpret_cast(&inst.GetInOperand(0).words[0]); + if (0 == std::strcmp(ext_name, "SPV_GOOGLE_hlsl_functionality1")) { + to_remove.push_back(&inst); + } else if (!other_uses_for_decorate_string && + 0 == std::strcmp(ext_name, "SPV_GOOGLE_decorate_string")) { + to_remove.push_back(&inst); + } else if (0 == std::strcmp(ext_name, "SPV_KHR_non_semantic_info")) { + to_remove.push_back(&inst); + } + } + + // clear all debug data now if it hasn't been cleared already, to remove any + // remaining OpString that may have been referenced by non-semantic extinsts + for (auto& dbg : context()->debugs1()) to_remove.push_back(&dbg); + for (auto& dbg : context()->debugs2()) to_remove.push_back(&dbg); + for (auto& dbg : context()->debugs3()) to_remove.push_back(&dbg); + for (auto& dbg : context()->ext_inst_debuginfo()) to_remove.push_back(&dbg); + + // remove any extended inst imports that are non semantic + std::unordered_set non_semantic_sets; + for (auto& inst : context()->module()->ext_inst_imports()) { + assert(inst.opcode() == SpvOpExtInstImport && + "Expecting an import of an extension's instruction set."); + const char* extension_name = + reinterpret_cast(&inst.GetInOperand(0).words[0]); + if (0 == std::strncmp(extension_name, "NonSemantic.", 12)) { + non_semantic_sets.insert(inst.result_id()); + to_remove.push_back(&inst); + } + } + + // if we removed some non-semantic sets, then iterate over the instructions in + // the module to remove any OpExtInst that referenced those sets + if (!non_semantic_sets.empty()) { + context()->module()->ForEachInst( + [&non_semantic_sets, &to_remove](Instruction* inst) { + if (inst->opcode() == SpvOpExtInst) { + if (non_semantic_sets.find(inst->GetSingleWordInOperand(0)) != + non_semantic_sets.end()) { + to_remove.push_back(inst); + } + } + }); + } + + // OpName must come first, since they may refer to other debug instructions. + // If they are after the instructions that refer to, then they will be killed + // when that instruction is killed, which will lead to a double kill. + std::sort(to_remove.begin(), to_remove.end(), + [](Instruction* lhs, Instruction* rhs) -> bool { + if (lhs->opcode() == SpvOpName && rhs->opcode() != SpvOpName) + return true; + return false; + }); + + for (auto* inst : to_remove) { + modified = true; + context()->KillInst(inst); + } + + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/strip_reflect_info_pass.h b/third_party/spirv-tools/source/opt/strip_reflect_info_pass.h new file mode 100644 index 0000000..4e1999e --- /dev/null +++ b/third_party/spirv-tools/source/opt/strip_reflect_info_pass.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ +#define SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class StripReflectInfoPass : public Pass { + public: + const char* name() const override { return "strip-reflect"; } + Status Process() override; + + // Return the mask of preserved Analyses. + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisCombinators | IRContext::kAnalysisCFG | + IRContext::kAnalysisDominatorAnalysis | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_STRIP_REFLECT_INFO_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/struct_cfg_analysis.cpp b/third_party/spirv-tools/source/opt/struct_cfg_analysis.cpp new file mode 100644 index 0000000..203db87 --- /dev/null +++ b/third_party/spirv-tools/source/opt/struct_cfg_analysis.cpp @@ -0,0 +1,250 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/struct_cfg_analysis.h" + +#include "source/opt/ir_context.h" + +namespace { +const uint32_t kMergeNodeIndex = 0; +const uint32_t kContinueNodeIndex = 1; +} // namespace + +namespace spvtools { +namespace opt { + +StructuredCFGAnalysis::StructuredCFGAnalysis(IRContext* ctx) : context_(ctx) { + // If this is not a shader, there are no merge instructions, and not + // structured CFG to analyze. + if (!context_->get_feature_mgr()->HasCapability(SpvCapabilityShader)) { + return; + } + + for (auto& func : *context_->module()) { + AddBlocksInFunction(&func); + } +} + +void StructuredCFGAnalysis::AddBlocksInFunction(Function* func) { + if (func->begin() == func->end()) return; + + std::list order; + context_->cfg()->ComputeStructuredOrder(func, &*func->begin(), &order); + + struct TraversalInfo { + ConstructInfo cinfo; + uint32_t merge_node; + uint32_t continue_node; + }; + + // Set up a stack to keep track of currently active constructs. + std::vector state; + state.emplace_back(); + state[0].cinfo.containing_construct = 0; + state[0].cinfo.containing_loop = 0; + state[0].cinfo.containing_switch = 0; + state[0].cinfo.in_continue = false; + state[0].merge_node = 0; + state[0].continue_node = 0; + + for (BasicBlock* block : order) { + if (context_->cfg()->IsPseudoEntryBlock(block) || + context_->cfg()->IsPseudoExitBlock(block)) { + continue; + } + + if (block->id() == state.back().merge_node) { + state.pop_back(); + } + + // This works because the structured order is designed to keep the blocks in + // the continue construct between the continue header and the merge node. + if (block->id() == state.back().continue_node) { + state.back().cinfo.in_continue = true; + } + + bb_to_construct_.emplace(std::make_pair(block->id(), state.back().cinfo)); + + if (Instruction* merge_inst = block->GetMergeInst()) { + TraversalInfo new_state; + new_state.merge_node = + merge_inst->GetSingleWordInOperand(kMergeNodeIndex); + new_state.cinfo.containing_construct = block->id(); + + if (merge_inst->opcode() == SpvOpLoopMerge) { + new_state.cinfo.containing_loop = block->id(); + new_state.cinfo.containing_switch = 0; + new_state.continue_node = + merge_inst->GetSingleWordInOperand(kContinueNodeIndex); + if (block->id() == new_state.continue_node) { + new_state.cinfo.in_continue = true; + bb_to_construct_[block->id()].in_continue = true; + } else { + new_state.cinfo.in_continue = false; + } + } else { + new_state.cinfo.containing_loop = state.back().cinfo.containing_loop; + new_state.cinfo.in_continue = state.back().cinfo.in_continue; + new_state.continue_node = state.back().continue_node; + + if (merge_inst->NextNode()->opcode() == SpvOpSwitch) { + new_state.cinfo.containing_switch = block->id(); + } else { + new_state.cinfo.containing_switch = + state.back().cinfo.containing_switch; + } + } + + state.emplace_back(new_state); + merge_blocks_.Set(new_state.merge_node); + } + } +} + +uint32_t StructuredCFGAnalysis::ContainingConstruct(Instruction* inst) { + uint32_t bb = context_->get_instr_block(inst)->id(); + return ContainingConstruct(bb); +} + +uint32_t StructuredCFGAnalysis::MergeBlock(uint32_t bb_id) { + uint32_t header_id = ContainingConstruct(bb_id); + if (header_id == 0) { + return 0; + } + + BasicBlock* header = context_->cfg()->block(header_id); + Instruction* merge_inst = header->GetMergeInst(); + return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); +} + +uint32_t StructuredCFGAnalysis::NestingDepth(uint32_t bb_id) { + uint32_t result = 0; + + // Find the merge block of the current merge construct as long as the block is + // inside a merge construct, exiting one for each iteration. + for (uint32_t merge_block_id = MergeBlock(bb_id); merge_block_id != 0; + merge_block_id = MergeBlock(merge_block_id)) { + result++; + } + + return result; +} + +uint32_t StructuredCFGAnalysis::LoopMergeBlock(uint32_t bb_id) { + uint32_t header_id = ContainingLoop(bb_id); + if (header_id == 0) { + return 0; + } + + BasicBlock* header = context_->cfg()->block(header_id); + Instruction* merge_inst = header->GetMergeInst(); + return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); +} + +uint32_t StructuredCFGAnalysis::LoopContinueBlock(uint32_t bb_id) { + uint32_t header_id = ContainingLoop(bb_id); + if (header_id == 0) { + return 0; + } + + BasicBlock* header = context_->cfg()->block(header_id); + Instruction* merge_inst = header->GetMergeInst(); + return merge_inst->GetSingleWordInOperand(kContinueNodeIndex); +} + +uint32_t StructuredCFGAnalysis::LoopNestingDepth(uint32_t bb_id) { + uint32_t result = 0; + + // Find the merge block of the current loop as long as the block is inside a + // loop, exiting a loop for each iteration. + for (uint32_t merge_block_id = LoopMergeBlock(bb_id); merge_block_id != 0; + merge_block_id = LoopMergeBlock(merge_block_id)) { + result++; + } + + return result; +} + +uint32_t StructuredCFGAnalysis::SwitchMergeBlock(uint32_t bb_id) { + uint32_t header_id = ContainingSwitch(bb_id); + if (header_id == 0) { + return 0; + } + + BasicBlock* header = context_->cfg()->block(header_id); + Instruction* merge_inst = header->GetMergeInst(); + return merge_inst->GetSingleWordInOperand(kMergeNodeIndex); +} + +bool StructuredCFGAnalysis::IsContinueBlock(uint32_t bb_id) { + assert(bb_id != 0); + return LoopContinueBlock(bb_id) == bb_id; +} + +bool StructuredCFGAnalysis::IsInContainingLoopsContinueConstruct( + uint32_t bb_id) { + auto it = bb_to_construct_.find(bb_id); + if (it == bb_to_construct_.end()) { + return false; + } + return it->second.in_continue; +} + +bool StructuredCFGAnalysis::IsInContinueConstruct(uint32_t bb_id) { + while (bb_id != 0) { + if (IsInContainingLoopsContinueConstruct(bb_id)) { + return true; + } + bb_id = ContainingLoop(bb_id); + } + return false; +} + +bool StructuredCFGAnalysis::IsMergeBlock(uint32_t bb_id) { + return merge_blocks_.Get(bb_id); +} + +std::unordered_set +StructuredCFGAnalysis::FindFuncsCalledFromContinue() { + std::unordered_set called_from_continue; + std::queue funcs_to_process; + + // First collect the functions that are called directly from a continue + // construct. + for (Function& func : *context_->module()) { + for (auto& bb : func) { + if (IsInContainingLoopsContinueConstruct(bb.id())) { + for (const Instruction& inst : bb) { + if (inst.opcode() == SpvOpFunctionCall) { + funcs_to_process.push(inst.GetSingleWordInOperand(0)); + } + } + } + } + } + + // Now collect all of the functions that are indirectly called as well. + while (!funcs_to_process.empty()) { + uint32_t func_id = funcs_to_process.front(); + funcs_to_process.pop(); + Function* func = context_->GetFunction(func_id); + if (called_from_continue.insert(func_id).second) { + context_->AddCalls(func, &funcs_to_process); + } + } + return called_from_continue; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/struct_cfg_analysis.h b/third_party/spirv-tools/source/opt/struct_cfg_analysis.h new file mode 100644 index 0000000..9436b4f --- /dev/null +++ b/third_party/spirv-tools/source/opt/struct_cfg_analysis.h @@ -0,0 +1,160 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_ +#define SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_ + +#include +#include + +#include "source/opt/function.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +class IRContext; + +// An analysis that, for each basic block, finds the constructs in which it is +// contained, so we can easily get headers and merge nodes. +class StructuredCFGAnalysis { + public: + explicit StructuredCFGAnalysis(IRContext* ctx); + + // Returns the id of the header of the innermost merge construct + // that contains |bb_id|. Returns |0| if |bb_id| is not contained in any + // merge construct. + uint32_t ContainingConstruct(uint32_t bb_id) { + auto it = bb_to_construct_.find(bb_id); + if (it == bb_to_construct_.end()) { + return 0; + } + return it->second.containing_construct; + } + + // Returns the id of the header of the innermost merge construct + // that contains |inst|. Returns |0| if |inst| is not contained in any + // merge construct. + uint32_t ContainingConstruct(Instruction* inst); + + // Returns the id of the merge block of the innermost merge construct + // that contains |bb_id|. Returns |0| if |bb_id| is not contained in any + // merge construct. + uint32_t MergeBlock(uint32_t bb_id); + + // Returns the nesting depth of the given block, i.e. the number of merge + // constructs containing it. Headers and merge blocks are not considered part + // of the corresponding merge constructs. + uint32_t NestingDepth(uint32_t block_id); + + // Returns the id of the header of the innermost loop construct + // that contains |bb_id|. Return |0| if |bb_id| is not contained in any loop + // construct. + uint32_t ContainingLoop(uint32_t bb_id) { + auto it = bb_to_construct_.find(bb_id); + if (it == bb_to_construct_.end()) { + return 0; + } + return it->second.containing_loop; + } + + // Returns the id of the merge block of the innermost loop construct + // that contains |bb_id|. Return |0| if |bb_id| is not contained in any loop + // construct. + uint32_t LoopMergeBlock(uint32_t bb_id); + + // Returns the id of the continue block of the innermost loop construct + // that contains |bb_id|. Return |0| if |bb_id| is not contained in any loop + // construct. + uint32_t LoopContinueBlock(uint32_t bb_id); + + // Returns the loop nesting depth of |bb_id| within its function, i.e. the + // number of loop constructs in which |bb_id| is contained. As per other + // functions in StructuredCFGAnalysis, a loop header is not regarded as being + // part of the loop that it heads, so that e.g. the nesting depth of an + // outer-most loop header is 0. + uint32_t LoopNestingDepth(uint32_t bb_id); + + // Returns the id of the header of the innermost switch construct + // that contains |bb_id| as long as there is no intervening loop. Returns |0| + // if no such construct exists. + uint32_t ContainingSwitch(uint32_t bb_id) { + auto it = bb_to_construct_.find(bb_id); + if (it == bb_to_construct_.end()) { + return 0; + } + return it->second.containing_switch; + } + // Returns the id of the merge block of the innermost switch construct + // that contains |bb_id| as long as there is no intervening loop. Return |0| + // if no such block exists. + uint32_t SwitchMergeBlock(uint32_t bb_id); + + // Returns true if |bb_id| is the continue block for a loop. + bool IsContinueBlock(uint32_t bb_id); + + // Returns true if |bb_id| is in the continue construct for its inner most + // containing loop. + bool IsInContainingLoopsContinueConstruct(uint32_t bb_id); + + // Returns true if |bb_id| is in the continue construct for any loop in its + // function. + bool IsInContinueConstruct(uint32_t bb_id); + + // Return true if |bb_id| is the merge block for a construct. + bool IsMergeBlock(uint32_t bb_id); + + // Returns the set of function ids that are called directly or indirectly from + // a continue construct. + std::unordered_set FindFuncsCalledFromContinue(); + + private: + // Struct used to hold the information for a basic block. + // |containing_construct| is the header for the innermost containing + // construct, or 0 if no such construct exists. It could be a selection + // construct or a loop construct. + // + // |containing_loop| is the innermost containing loop construct, or 0 if the + // basic bloc is not in a loop. If the basic block is in a selection + // construct that is contained in a loop construct, then these two values will + // not be the same. + // + // |containing_switch| is the innermost contain selection construct with an + // |OpSwitch| for the branch, as long as there is not intervening loop. This + // is used to identify the selection construct from which it can break. + // + // |in_continue| is true of the block is in the continue construct for its + // innermost containing loop. + struct ConstructInfo { + uint32_t containing_construct; + uint32_t containing_loop; + uint32_t containing_switch; + bool in_continue; + }; + + // Populates |bb_to_construct_| with the innermost containing merge and loop + // constructs for each basic block in |func|. + void AddBlocksInFunction(Function* func); + + IRContext* context_; + + // A map from a basic block to the headers of its inner most containing + // constructs. + std::unordered_map bb_to_construct_; + utils::BitVector merge_blocks_; +}; + +} // namespace opt +} // namespace spvtools +#endif // SOURCE_OPT_STRUCT_CFG_ANALYSIS_H_ diff --git a/third_party/spirv-tools/source/opt/tree_iterator.h b/third_party/spirv-tools/source/opt/tree_iterator.h new file mode 100644 index 0000000..05f42bc --- /dev/null +++ b/third_party/spirv-tools/source/opt/tree_iterator.h @@ -0,0 +1,246 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_TREE_ITERATOR_H_ +#define SOURCE_OPT_TREE_ITERATOR_H_ + +#include +#include +#include + +namespace spvtools { +namespace opt { + +// Helper class to iterate over a tree in a depth first order. +// The class assumes the data structure is a tree, tree node type implements a +// forward iterator. +// At each step, the iterator holds the pointer to the current node and state of +// the walk. +// The state is recorded by stacking the iteration position of the node +// children. To move to the next node, the iterator: +// - Looks at the top of the stack; +// - Sets the node behind the iterator as the current node; +// - Increments the iterator if it has more children to visit, pops otherwise; +// - If the current node has children, the children iterator is pushed into the +// stack. +template +class TreeDFIterator { + static_assert(!std::is_pointer::value && + !std::is_reference::value, + "NodeTy should be a class"); + // Type alias to keep track of the const qualifier. + using NodeIterator = + typename std::conditional::value, + typename NodeTy::const_iterator, + typename NodeTy::iterator>::type; + + // Type alias to keep track of the const qualifier. + using NodePtr = NodeTy*; + + public: + // Standard iterator interface. + using reference = NodeTy&; + using value_type = NodeTy; + + explicit inline TreeDFIterator(NodePtr top_node) : current_(top_node) { + if (current_ && current_->begin() != current_->end()) + parent_iterators_.emplace(make_pair(current_, current_->begin())); + } + + // end() iterator. + inline TreeDFIterator() : TreeDFIterator(nullptr) {} + + bool operator==(const TreeDFIterator& x) const { + return current_ == x.current_; + } + + bool operator!=(const TreeDFIterator& x) const { return !(*this == x); } + + reference operator*() const { return *current_; } + + NodePtr operator->() const { return current_; } + + TreeDFIterator& operator++() { + MoveToNextNode(); + return *this; + } + + TreeDFIterator operator++(int) { + TreeDFIterator tmp = *this; + ++*this; + return tmp; + } + + private: + // Moves the iterator to the next node in the tree. + // If we are at the end, do nothing, otherwise + // if our current node has children, use the children iterator and push the + // current node into the stack. + // If we reach the end of the local iterator, pop it. + inline void MoveToNextNode() { + if (!current_) return; + if (parent_iterators_.empty()) { + current_ = nullptr; + return; + } + std::pair& next_it = parent_iterators_.top(); + // Set the new node. + current_ = *next_it.second; + // Update the iterator for the next child. + ++next_it.second; + // If we finished with node, pop it. + if (next_it.first->end() == next_it.second) parent_iterators_.pop(); + // If our current node is not a leaf, store the iteration state for later. + if (current_->begin() != current_->end()) + parent_iterators_.emplace(make_pair(current_, current_->begin())); + } + + // The current node of the tree. + NodePtr current_; + // State of the tree walk: each pair contains the parent node (which has been + // already visited) and the iterator of the next children to visit. + // When all the children has been visited, we pop the entry, get the next + // child and push back the pair if the children iterator is not end(). + std::stack> parent_iterators_; +}; + +// Helper class to iterate over a tree in a depth first post-order. +// The class assumes the data structure is a tree, tree node type implements a +// forward iterator. +// At each step, the iterator holds the pointer to the current node and state of +// the walk. +// The state is recorded by stacking the iteration position of the node +// children. To move to the next node, the iterator: +// - Looks at the top of the stack; +// - If the children iterator has reach the end, then the node become the +// current one and we pop the stack; +// - Otherwise, we save the child and increment the iterator; +// - We walk the child sub-tree until we find a leaf, stacking all non-leaves +// states (pair of node pointer and child iterator) as we walk it. +template +class PostOrderTreeDFIterator { + static_assert(!std::is_pointer::value && + !std::is_reference::value, + "NodeTy should be a class"); + // Type alias to keep track of the const qualifier. + using NodeIterator = + typename std::conditional::value, + typename NodeTy::const_iterator, + typename NodeTy::iterator>::type; + + // Type alias to keep track of the const qualifier. + using NodePtr = NodeTy*; + + public: + // Standard iterator interface. + using reference = NodeTy&; + using value_type = NodeTy; + + static inline PostOrderTreeDFIterator begin(NodePtr top_node) { + return PostOrderTreeDFIterator(top_node); + } + + static inline PostOrderTreeDFIterator end(NodePtr sentinel_node) { + return PostOrderTreeDFIterator(sentinel_node, false); + } + + bool operator==(const PostOrderTreeDFIterator& x) const { + return current_ == x.current_; + } + + bool operator!=(const PostOrderTreeDFIterator& x) const { + return !(*this == x); + } + + reference operator*() const { return *current_; } + + NodePtr operator->() const { return current_; } + + PostOrderTreeDFIterator& operator++() { + MoveToNextNode(); + return *this; + } + + PostOrderTreeDFIterator operator++(int) { + PostOrderTreeDFIterator tmp = *this; + ++*this; + return tmp; + } + + private: + explicit inline PostOrderTreeDFIterator(NodePtr top_node) + : current_(top_node) { + if (current_) WalkToLeaf(); + } + + // Constructor for the "end()" iterator. + // |end_sentinel| is the value that acts as end value (can be null). The bool + // parameters is to distinguish from the start() Ctor. + inline PostOrderTreeDFIterator(NodePtr sentinel_node, bool) + : current_(sentinel_node) {} + + // Moves the iterator to the next node in the tree. + // If we are at the end, do nothing, otherwise + // if our current node has children, use the children iterator and push the + // current node into the stack. + // If we reach the end of the local iterator, pop it. + inline void MoveToNextNode() { + if (!current_) return; + if (parent_iterators_.empty()) { + current_ = nullptr; + return; + } + std::pair& next_it = parent_iterators_.top(); + // If we visited all children, the current node is the top of the stack. + if (next_it.second == next_it.first->end()) { + // Set the new node. + current_ = next_it.first; + parent_iterators_.pop(); + return; + } + // We have more children to visit, set the current node to the first child + // and dive to leaf. + current_ = *next_it.second; + // Update the iterator for the next child (avoid unneeded pop). + ++next_it.second; + WalkToLeaf(); + } + + // Moves the iterator to the next node in the tree. + // If we are at the end, do nothing, otherwise + // if our current node has children, use the children iterator and push the + // current node into the stack. + // If we reach the end of the local iterator, pop it. + inline void WalkToLeaf() { + while (current_->begin() != current_->end()) { + NodeIterator next = ++current_->begin(); + parent_iterators_.emplace(make_pair(current_, next)); + // Set the first child as the new node. + current_ = *current_->begin(); + } + } + + // The current node of the tree. + NodePtr current_; + // State of the tree walk: each pair contains the parent node and the iterator + // of the next children to visit. + // When all the children has been visited, we pop the first entry and the + // parent node become the current node. + std::stack> parent_iterators_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_TREE_ITERATOR_H_ diff --git a/third_party/spirv-tools/source/opt/type_manager.cpp b/third_party/spirv-tools/source/opt/type_manager.cpp new file mode 100644 index 0000000..27c7199 --- /dev/null +++ b/third_party/spirv-tools/source/opt/type_manager.cpp @@ -0,0 +1,1053 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/type_manager.h" + +#include +#include +#include +#include + +#include "source/opt/ir_context.h" +#include "source/opt/log.h" +#include "source/opt/reflect.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { +namespace analysis { +namespace { + +const int kSpvTypePointerStorageClass = 1; +const int kSpvTypePointerTypeIdInIdx = 2; + +} // namespace + +TypeManager::TypeManager(const MessageConsumer& consumer, IRContext* c) + : consumer_(consumer), context_(c) { + AnalyzeTypes(*c->module()); +} + +Type* TypeManager::GetType(uint32_t id) const { + auto iter = id_to_type_.find(id); + if (iter != id_to_type_.end()) return (*iter).second; + iter = id_to_incomplete_type_.find(id); + if (iter != id_to_incomplete_type_.end()) return (*iter).second; + return nullptr; +} + +std::pair> TypeManager::GetTypeAndPointerType( + uint32_t id, SpvStorageClass sc) const { + Type* type = GetType(id); + if (type) { + return std::make_pair(type, MakeUnique(type, sc)); + } else { + return std::make_pair(type, std::unique_ptr()); + } +} + +uint32_t TypeManager::GetId(const Type* type) const { + auto iter = type_to_id_.find(type); + if (iter != type_to_id_.end()) { + return (*iter).second; + } + return 0; +} + +void TypeManager::AnalyzeTypes(const Module& module) { + // First pass through the constants, as some will be needed when traversing + // the types in the next pass. + for (const auto* inst : module.GetConstants()) { + id_to_constant_inst_[inst->result_id()] = inst; + } + + // Then pass through the types. Any types that reference a forward pointer + // (directly or indirectly) are incomplete, and are added to incomplete types. + for (const auto* inst : module.GetTypes()) { + RecordIfTypeDefinition(*inst); + } + + if (incomplete_types_.empty()) { + return; + } + + // Get the real pointer definition for all of the forward pointers. + for (auto& type : incomplete_types_) { + if (type.type()->kind() == Type::kForwardPointer) { + auto* t = GetType(type.id()); + assert(t); + auto* p = t->AsPointer(); + assert(p); + type.type()->AsForwardPointer()->SetTargetPointer(p); + } + } + + // Replaces the references to the forward pointers in the incomplete types. + for (auto& type : incomplete_types_) { + ReplaceForwardPointers(type.type()); + } + + // Delete the forward pointers now that they are not referenced anymore. + for (auto& type : incomplete_types_) { + if (type.type()->kind() == Type::kForwardPointer) { + type.ResetType(nullptr); + } + } + + // Compare the complete types looking for types that are the same. If there + // are two types that are the same, then replace one with the other. + // Continue until we reach a fixed point. + bool restart = true; + while (restart) { + restart = false; + for (auto it1 = incomplete_types_.begin(); it1 != incomplete_types_.end(); + ++it1) { + uint32_t id1 = it1->id(); + Type* type1 = it1->type(); + if (!type1) { + continue; + } + + for (auto it2 = it1 + 1; it2 != incomplete_types_.end(); ++it2) { + uint32_t id2 = it2->id(); + (void)(id2 + id1); + Type* type2 = it2->type(); + if (!type2) { + continue; + } + + if (type1->IsSame(type2)) { + ReplaceType(type1, type2); + it2->ResetType(nullptr); + id_to_incomplete_type_[it2->id()] = type1; + restart = true; + } + } + } + } + + // Add the remaining incomplete types to the type pool. + for (auto& type : incomplete_types_) { + if (type.type() && !type.type()->AsForwardPointer()) { + std::vector decorations = + context()->get_decoration_mgr()->GetDecorationsFor(type.id(), true); + for (auto dec : decorations) { + AttachDecoration(*dec, type.type()); + } + auto pair = type_pool_.insert(type.ReleaseType()); + id_to_type_[type.id()] = pair.first->get(); + type_to_id_[pair.first->get()] = type.id(); + id_to_incomplete_type_.erase(type.id()); + } + } + + // Add a mapping for any ids that whose original type was replaced by an + // equivalent type. + for (auto& type : id_to_incomplete_type_) { + id_to_type_[type.first] = type.second; + } + +#ifndef NDEBUG + // Check if the type pool contains two types that are the same. This + // is an indication that the hashing and comparison are wrong. It + // will cause a problem if the type pool gets resized and everything + // is rehashed. + for (auto& i : type_pool_) { + for (auto& j : type_pool_) { + Type* ti = i.get(); + Type* tj = j.get(); + assert((ti == tj || !ti->IsSame(tj)) && + "Type pool contains two types that are the same."); + } + } +#endif +} + +void TypeManager::RemoveId(uint32_t id) { + auto iter = id_to_type_.find(id); + if (iter == id_to_type_.end()) return; + + auto& type = iter->second; + if (!type->IsUniqueType(true)) { + auto tIter = type_to_id_.find(type); + if (tIter != type_to_id_.end() && tIter->second == id) { + // |type| currently maps to |id|. + // Search for an equivalent type to re-map. + bool found = false; + for (auto& pair : id_to_type_) { + if (pair.first != id && *pair.second == *type) { + // Equivalent ambiguous type, re-map type. + type_to_id_.erase(type); + type_to_id_[pair.second] = pair.first; + found = true; + break; + } + } + // No equivalent ambiguous type, remove mapping. + if (!found) type_to_id_.erase(tIter); + } + } else { + // Unique type, so just erase the entry. + type_to_id_.erase(type); + } + + // Erase the entry for |id|. + id_to_type_.erase(iter); +} + +uint32_t TypeManager::GetTypeInstruction(const Type* type) { + uint32_t id = GetId(type); + if (id != 0) return id; + + std::unique_ptr typeInst; + // TODO(1841): Handle id overflow. + id = context()->TakeNextId(); + if (id == 0) { + return 0; + } + + RegisterType(id, *type); + switch (type->kind()) { +#define DefineParameterlessCase(kind) \ + case Type::k##kind: \ + typeInst = MakeUnique(context(), SpvOpType##kind, 0, id, \ + std::initializer_list{}); \ + break; + DefineParameterlessCase(Void); + DefineParameterlessCase(Bool); + DefineParameterlessCase(Sampler); + DefineParameterlessCase(Event); + DefineParameterlessCase(DeviceEvent); + DefineParameterlessCase(ReserveId); + DefineParameterlessCase(Queue); + DefineParameterlessCase(PipeStorage); + DefineParameterlessCase(NamedBarrier); + DefineParameterlessCase(AccelerationStructureNV); +#undef DefineParameterlessCase + case Type::kInteger: + typeInst = MakeUnique( + context(), SpvOpTypeInt, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {type->AsInteger()->width()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {(type->AsInteger()->IsSigned() ? 1u : 0u)}}}); + break; + case Type::kFloat: + typeInst = MakeUnique( + context(), SpvOpTypeFloat, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {type->AsFloat()->width()}}}); + break; + case Type::kVector: { + uint32_t subtype = GetTypeInstruction(type->AsVector()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = + MakeUnique(context(), SpvOpTypeVector, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {subtype}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {type->AsVector()->element_count()}}}); + break; + } + case Type::kMatrix: { + uint32_t subtype = GetTypeInstruction(type->AsMatrix()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = + MakeUnique(context(), SpvOpTypeMatrix, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {subtype}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {type->AsMatrix()->element_count()}}}); + break; + } + case Type::kImage: { + const Image* image = type->AsImage(); + uint32_t subtype = GetTypeInstruction(image->sampled_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), SpvOpTypeImage, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {subtype}}, + {SPV_OPERAND_TYPE_DIMENSIONALITY, + {static_cast(image->dim())}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {image->depth()}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {(image->is_arrayed() ? 1u : 0u)}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {(image->is_multisampled() ? 1u : 0u)}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {image->sampled()}}, + {SPV_OPERAND_TYPE_SAMPLER_IMAGE_FORMAT, + {static_cast(image->format())}}, + {SPV_OPERAND_TYPE_ACCESS_QUALIFIER, + {static_cast(image->access_qualifier())}}}); + break; + } + case Type::kSampledImage: { + uint32_t subtype = + GetTypeInstruction(type->AsSampledImage()->image_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), SpvOpTypeSampledImage, 0, id, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {subtype}}}); + break; + } + case Type::kArray: { + uint32_t subtype = GetTypeInstruction(type->AsArray()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), SpvOpTypeArray, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {subtype}}, + {SPV_OPERAND_TYPE_ID, {type->AsArray()->LengthId()}}}); + break; + } + case Type::kRuntimeArray: { + uint32_t subtype = + GetTypeInstruction(type->AsRuntimeArray()->element_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), SpvOpTypeRuntimeArray, 0, id, + std::initializer_list{{SPV_OPERAND_TYPE_ID, {subtype}}}); + break; + } + case Type::kStruct: { + std::vector ops; + const Struct* structTy = type->AsStruct(); + for (auto ty : structTy->element_types()) { + uint32_t member_type_id = GetTypeInstruction(ty); + if (member_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {member_type_id})); + } + typeInst = + MakeUnique(context(), SpvOpTypeStruct, 0, id, ops); + break; + } + case Type::kOpaque: { + const Opaque* opaque = type->AsOpaque(); + size_t size = opaque->name().size(); + // Convert to null-terminated packed UTF-8 string. + std::vector words(size / 4 + 1, 0); + char* dst = reinterpret_cast(words.data()); + strncpy(dst, opaque->name().c_str(), size); + typeInst = MakeUnique( + context(), SpvOpTypeOpaque, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_LITERAL_STRING, words}}); + break; + } + case Type::kPointer: { + const Pointer* pointer = type->AsPointer(); + uint32_t subtype = GetTypeInstruction(pointer->pointee_type()); + if (subtype == 0) { + return 0; + } + typeInst = MakeUnique( + context(), SpvOpTypePointer, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(pointer->storage_class())}}, + {SPV_OPERAND_TYPE_ID, {subtype}}}); + break; + } + case Type::kFunction: { + std::vector ops; + const Function* function = type->AsFunction(); + uint32_t return_type_id = GetTypeInstruction(function->return_type()); + if (return_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {return_type_id})); + for (auto ty : function->param_types()) { + uint32_t paramater_type_id = GetTypeInstruction(ty); + if (paramater_type_id == 0) { + return 0; + } + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {paramater_type_id})); + } + typeInst = + MakeUnique(context(), SpvOpTypeFunction, 0, id, ops); + break; + } + case Type::kPipe: + typeInst = MakeUnique( + context(), SpvOpTypePipe, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ACCESS_QUALIFIER, + {static_cast(type->AsPipe()->access_qualifier())}}}); + break; + case Type::kForwardPointer: + typeInst = MakeUnique( + context(), SpvOpTypeForwardPointer, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {type->AsForwardPointer()->target_id()}}, + {SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast( + type->AsForwardPointer()->storage_class())}}}); + break; + case Type::kCooperativeMatrixNV: { + auto coop_mat = type->AsCooperativeMatrixNV(); + uint32_t const component_type = + GetTypeInstruction(coop_mat->component_type()); + if (component_type == 0) { + return 0; + } + typeInst = MakeUnique( + context(), SpvOpTypeCooperativeMatrixNV, 0, id, + std::initializer_list{ + {SPV_OPERAND_TYPE_ID, {component_type}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {coop_mat->scope_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->rows_id()}}, + {SPV_OPERAND_TYPE_ID, {coop_mat->columns_id()}}}); + break; + } + default: + assert(false && "Unexpected type"); + break; + } + context()->AddType(std::move(typeInst)); + context()->AnalyzeDefUse(&*--context()->types_values_end()); + AttachDecorations(id, type); + return id; +} + +uint32_t TypeManager::FindPointerToType(uint32_t type_id, + SpvStorageClass storage_class) { + Type* pointeeTy = GetType(type_id); + Pointer pointerTy(pointeeTy, storage_class); + if (pointeeTy->IsUniqueType(true)) { + // Non-ambiguous type. Get the pointer type through the type manager. + return GetTypeInstruction(&pointerTy); + } + + // Ambiguous type, do a linear search. + Module::inst_iterator type_itr = context()->module()->types_values_begin(); + for (; type_itr != context()->module()->types_values_end(); ++type_itr) { + const Instruction* type_inst = &*type_itr; + if (type_inst->opcode() == SpvOpTypePointer && + type_inst->GetSingleWordOperand(kSpvTypePointerTypeIdInIdx) == + type_id && + type_inst->GetSingleWordOperand(kSpvTypePointerStorageClass) == + storage_class) + return type_inst->result_id(); + } + + // Must create the pointer type. + // TODO(1841): Handle id overflow. + uint32_t resultId = context()->TakeNextId(); + std::unique_ptr type_inst( + new Instruction(context(), SpvOpTypePointer, 0, resultId, + {{spv_operand_type_t::SPV_OPERAND_TYPE_STORAGE_CLASS, + {uint32_t(storage_class)}}, + {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {type_id}}})); + context()->AddType(std::move(type_inst)); + context()->get_type_mgr()->RegisterType(resultId, pointerTy); + return resultId; +} + +void TypeManager::AttachDecorations(uint32_t id, const Type* type) { + for (auto vec : type->decorations()) { + CreateDecoration(id, vec); + } + if (const Struct* structTy = type->AsStruct()) { + for (auto pair : structTy->element_decorations()) { + uint32_t element = pair.first; + for (auto vec : pair.second) { + CreateDecoration(id, vec, element); + } + } + } +} + +void TypeManager::CreateDecoration(uint32_t target, + const std::vector& decoration, + uint32_t element) { + std::vector ops; + ops.push_back(Operand(SPV_OPERAND_TYPE_ID, {target})); + if (element != 0) { + ops.push_back(Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {element})); + } + ops.push_back(Operand(SPV_OPERAND_TYPE_DECORATION, {decoration[0]})); + for (size_t i = 1; i < decoration.size(); ++i) { + ops.push_back(Operand(SPV_OPERAND_TYPE_LITERAL_INTEGER, {decoration[i]})); + } + context()->AddAnnotationInst(MakeUnique( + context(), (element == 0 ? SpvOpDecorate : SpvOpMemberDecorate), 0, 0, + ops)); + Instruction* inst = &*--context()->annotation_end(); + context()->get_def_use_mgr()->AnalyzeInstUse(inst); +} + +Type* TypeManager::RebuildType(const Type& type) { + // The comparison and hash on the type pool will avoid inserting the rebuilt + // type if an equivalent type already exists. The rebuilt type will be deleted + // when it goes out of scope at the end of the function in that case. Repeated + // insertions of the same Type will, at most, keep one corresponding object in + // the type pool. + std::unique_ptr rebuilt_ty; + switch (type.kind()) { +#define DefineNoSubtypeCase(kind) \ + case Type::k##kind: \ + rebuilt_ty.reset(type.Clone().release()); \ + return type_pool_.insert(std::move(rebuilt_ty)).first->get(); + + DefineNoSubtypeCase(Void); + DefineNoSubtypeCase(Bool); + DefineNoSubtypeCase(Integer); + DefineNoSubtypeCase(Float); + DefineNoSubtypeCase(Sampler); + DefineNoSubtypeCase(Opaque); + DefineNoSubtypeCase(Event); + DefineNoSubtypeCase(DeviceEvent); + DefineNoSubtypeCase(ReserveId); + DefineNoSubtypeCase(Queue); + DefineNoSubtypeCase(Pipe); + DefineNoSubtypeCase(PipeStorage); + DefineNoSubtypeCase(NamedBarrier); + DefineNoSubtypeCase(AccelerationStructureNV); +#undef DefineNoSubtypeCase + case Type::kVector: { + const Vector* vec_ty = type.AsVector(); + const Type* ele_ty = vec_ty->element_type(); + rebuilt_ty = + MakeUnique(RebuildType(*ele_ty), vec_ty->element_count()); + break; + } + case Type::kMatrix: { + const Matrix* mat_ty = type.AsMatrix(); + const Type* ele_ty = mat_ty->element_type(); + rebuilt_ty = + MakeUnique(RebuildType(*ele_ty), mat_ty->element_count()); + break; + } + case Type::kImage: { + const Image* image_ty = type.AsImage(); + const Type* ele_ty = image_ty->sampled_type(); + rebuilt_ty = + MakeUnique(RebuildType(*ele_ty), image_ty->dim(), + image_ty->depth(), image_ty->is_arrayed(), + image_ty->is_multisampled(), image_ty->sampled(), + image_ty->format(), image_ty->access_qualifier()); + break; + } + case Type::kSampledImage: { + const SampledImage* image_ty = type.AsSampledImage(); + const Type* ele_ty = image_ty->image_type(); + rebuilt_ty = MakeUnique(RebuildType(*ele_ty)); + break; + } + case Type::kArray: { + const Array* array_ty = type.AsArray(); + rebuilt_ty = + MakeUnique(array_ty->element_type(), array_ty->length_info()); + break; + } + case Type::kRuntimeArray: { + const RuntimeArray* array_ty = type.AsRuntimeArray(); + const Type* ele_ty = array_ty->element_type(); + rebuilt_ty = MakeUnique(RebuildType(*ele_ty)); + break; + } + case Type::kStruct: { + const Struct* struct_ty = type.AsStruct(); + std::vector subtypes; + subtypes.reserve(struct_ty->element_types().size()); + for (const auto* ele_ty : struct_ty->element_types()) { + subtypes.push_back(RebuildType(*ele_ty)); + } + rebuilt_ty = MakeUnique(subtypes); + Struct* rebuilt_struct = rebuilt_ty->AsStruct(); + for (auto pair : struct_ty->element_decorations()) { + uint32_t index = pair.first; + for (const auto& dec : pair.second) { + // Explicit copy intended. + std::vector copy(dec); + rebuilt_struct->AddMemberDecoration(index, std::move(copy)); + } + } + break; + } + case Type::kPointer: { + const Pointer* pointer_ty = type.AsPointer(); + const Type* ele_ty = pointer_ty->pointee_type(); + rebuilt_ty = MakeUnique(RebuildType(*ele_ty), + pointer_ty->storage_class()); + break; + } + case Type::kFunction: { + const Function* function_ty = type.AsFunction(); + const Type* ret_ty = function_ty->return_type(); + std::vector param_types; + param_types.reserve(function_ty->param_types().size()); + for (const auto* param_ty : function_ty->param_types()) { + param_types.push_back(RebuildType(*param_ty)); + } + rebuilt_ty = MakeUnique(RebuildType(*ret_ty), param_types); + break; + } + case Type::kForwardPointer: { + const ForwardPointer* forward_ptr_ty = type.AsForwardPointer(); + rebuilt_ty = MakeUnique(forward_ptr_ty->target_id(), + forward_ptr_ty->storage_class()); + const Pointer* target_ptr = forward_ptr_ty->target_pointer(); + if (target_ptr) { + rebuilt_ty->AsForwardPointer()->SetTargetPointer( + RebuildType(*target_ptr)->AsPointer()); + } + break; + } + case Type::kCooperativeMatrixNV: { + const CooperativeMatrixNV* cm_type = type.AsCooperativeMatrixNV(); + const Type* component_type = cm_type->component_type(); + rebuilt_ty = MakeUnique( + RebuildType(*component_type), cm_type->scope_id(), cm_type->rows_id(), + cm_type->columns_id()); + break; + } + default: + assert(false && "Unhandled type"); + return nullptr; + } + for (const auto& dec : type.decorations()) { + // Explicit copy intended. + std::vector copy(dec); + rebuilt_ty->AddDecoration(std::move(copy)); + } + + return type_pool_.insert(std::move(rebuilt_ty)).first->get(); +} + +void TypeManager::RegisterType(uint32_t id, const Type& type) { + // Rebuild |type| so it and all its constituent types are owned by the type + // pool. + Type* rebuilt = RebuildType(type); + assert(rebuilt->IsSame(&type)); + id_to_type_[id] = rebuilt; + if (GetId(rebuilt) == 0) { + type_to_id_[rebuilt] = id; + } +} + +Type* TypeManager::GetRegisteredType(const Type* type) { + uint32_t id = GetTypeInstruction(type); + if (id == 0) { + return nullptr; + } + return GetType(id); +} + +Type* TypeManager::RecordIfTypeDefinition(const Instruction& inst) { + if (!IsTypeInst(inst.opcode())) return nullptr; + + Type* type = nullptr; + switch (inst.opcode()) { + case SpvOpTypeVoid: + type = new Void(); + break; + case SpvOpTypeBool: + type = new Bool(); + break; + case SpvOpTypeInt: + type = new Integer(inst.GetSingleWordInOperand(0), + inst.GetSingleWordInOperand(1)); + break; + case SpvOpTypeFloat: + type = new Float(inst.GetSingleWordInOperand(0)); + break; + case SpvOpTypeVector: + type = new Vector(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1)); + break; + case SpvOpTypeMatrix: + type = new Matrix(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1)); + break; + case SpvOpTypeImage: { + const SpvAccessQualifier access = + inst.NumInOperands() < 8 + ? SpvAccessQualifierReadOnly + : static_cast(inst.GetSingleWordInOperand(7)); + type = new Image( + GetType(inst.GetSingleWordInOperand(0)), + static_cast(inst.GetSingleWordInOperand(1)), + inst.GetSingleWordInOperand(2), inst.GetSingleWordInOperand(3) == 1, + inst.GetSingleWordInOperand(4) == 1, inst.GetSingleWordInOperand(5), + static_cast(inst.GetSingleWordInOperand(6)), access); + } break; + case SpvOpTypeSampler: + type = new Sampler(); + break; + case SpvOpTypeSampledImage: + type = new SampledImage(GetType(inst.GetSingleWordInOperand(0))); + break; + case SpvOpTypeArray: { + const uint32_t length_id = inst.GetSingleWordInOperand(1); + const Instruction* length_constant_inst = id_to_constant_inst_[length_id]; + assert(length_constant_inst); + + // How will we distinguish one length value from another? + // Determine extra words required to distinguish this array length + // from another. + std::vector extra_words{Array::LengthInfo::kDefiningId}; + // If it is a specialised constant, retrieve its SpecId. + // Only OpSpecConstant has a SpecId. + uint32_t spec_id = 0u; + bool has_spec_id = false; + if (length_constant_inst->opcode() == SpvOpSpecConstant) { + context()->get_decoration_mgr()->ForEachDecoration( + length_id, SpvDecorationSpecId, + [&spec_id, &has_spec_id](const Instruction& decoration) { + assert(decoration.opcode() == SpvOpDecorate); + spec_id = decoration.GetSingleWordOperand(2u); + has_spec_id = true; + }); + } + const auto opcode = length_constant_inst->opcode(); + if (has_spec_id) { + extra_words.push_back(spec_id); + } + if ((opcode == SpvOpConstant) || (opcode == SpvOpSpecConstant)) { + // Always include the literal constant words. In the spec constant + // case, the constant might not be overridden, so it's still + // significant. + extra_words.insert(extra_words.end(), + length_constant_inst->GetOperand(2).words.begin(), + length_constant_inst->GetOperand(2).words.end()); + extra_words[0] = has_spec_id ? Array::LengthInfo::kConstantWithSpecId + : Array::LengthInfo::kConstant; + } else { + assert(extra_words[0] == Array::LengthInfo::kDefiningId); + extra_words.push_back(length_id); + } + assert(extra_words.size() >= 2); + Array::LengthInfo length_info{length_id, extra_words}; + + type = new Array(GetType(inst.GetSingleWordInOperand(0)), length_info); + + if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + } break; + case SpvOpTypeRuntimeArray: + type = new RuntimeArray(GetType(inst.GetSingleWordInOperand(0))); + if (id_to_incomplete_type_.count(inst.GetSingleWordInOperand(0))) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + break; + case SpvOpTypeStruct: { + std::vector element_types; + bool incomplete_type = false; + for (uint32_t i = 0; i < inst.NumInOperands(); ++i) { + uint32_t type_id = inst.GetSingleWordInOperand(i); + element_types.push_back(GetType(type_id)); + if (id_to_incomplete_type_.count(type_id)) { + incomplete_type = true; + } + } + type = new Struct(element_types); + + if (incomplete_type) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + } break; + case SpvOpTypeOpaque: { + const uint32_t* data = inst.GetInOperand(0).words.data(); + type = new Opaque(reinterpret_cast(data)); + } break; + case SpvOpTypePointer: { + uint32_t pointee_type_id = inst.GetSingleWordInOperand(1); + type = new Pointer( + GetType(pointee_type_id), + static_cast(inst.GetSingleWordInOperand(0))); + + if (id_to_incomplete_type_.count(pointee_type_id)) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + id_to_incomplete_type_.erase(inst.result_id()); + + } break; + case SpvOpTypeFunction: { + bool incomplete_type = false; + uint32_t return_type_id = inst.GetSingleWordInOperand(0); + if (id_to_incomplete_type_.count(return_type_id)) { + incomplete_type = true; + } + Type* return_type = GetType(return_type_id); + std::vector param_types; + for (uint32_t i = 1; i < inst.NumInOperands(); ++i) { + uint32_t param_type_id = inst.GetSingleWordInOperand(i); + param_types.push_back(GetType(param_type_id)); + if (id_to_incomplete_type_.count(param_type_id)) { + incomplete_type = true; + } + } + + type = new Function(return_type, param_types); + + if (incomplete_type) { + incomplete_types_.emplace_back(inst.result_id(), type); + id_to_incomplete_type_[inst.result_id()] = type; + return type; + } + } break; + case SpvOpTypeEvent: + type = new Event(); + break; + case SpvOpTypeDeviceEvent: + type = new DeviceEvent(); + break; + case SpvOpTypeReserveId: + type = new ReserveId(); + break; + case SpvOpTypeQueue: + type = new Queue(); + break; + case SpvOpTypePipe: + type = new Pipe( + static_cast(inst.GetSingleWordInOperand(0))); + break; + case SpvOpTypeForwardPointer: { + // Handling of forward pointers is different from the other types. + uint32_t target_id = inst.GetSingleWordInOperand(0); + type = new ForwardPointer(target_id, static_cast( + inst.GetSingleWordInOperand(1))); + incomplete_types_.emplace_back(target_id, type); + id_to_incomplete_type_[target_id] = type; + return type; + } + case SpvOpTypePipeStorage: + type = new PipeStorage(); + break; + case SpvOpTypeNamedBarrier: + type = new NamedBarrier(); + break; + case SpvOpTypeAccelerationStructureNV: + type = new AccelerationStructureNV(); + break; + case SpvOpTypeCooperativeMatrixNV: + type = new CooperativeMatrixNV(GetType(inst.GetSingleWordInOperand(0)), + inst.GetSingleWordInOperand(1), + inst.GetSingleWordInOperand(2), + inst.GetSingleWordInOperand(3)); + break; + case SpvOpTypeRayQueryProvisionalKHR: + type = new RayQueryProvisionalKHR(); + break; + default: + SPIRV_UNIMPLEMENTED(consumer_, "unhandled type"); + break; + } + + uint32_t id = inst.result_id(); + SPIRV_ASSERT(consumer_, id != 0, "instruction without result id found"); + SPIRV_ASSERT(consumer_, type != nullptr, + "type should not be nullptr at this point"); + std::vector decorations = + context()->get_decoration_mgr()->GetDecorationsFor(id, true); + for (auto dec : decorations) { + AttachDecoration(*dec, type); + } + std::unique_ptr unique(type); + auto pair = type_pool_.insert(std::move(unique)); + id_to_type_[id] = pair.first->get(); + type_to_id_[pair.first->get()] = id; + return type; +} + +void TypeManager::AttachDecoration(const Instruction& inst, Type* type) { + const SpvOp opcode = inst.opcode(); + if (!IsAnnotationInst(opcode)) return; + + switch (opcode) { + case SpvOpDecorate: { + const auto count = inst.NumOperands(); + std::vector data; + for (uint32_t i = 1; i < count; ++i) { + data.push_back(inst.GetSingleWordOperand(i)); + } + type->AddDecoration(std::move(data)); + } break; + case SpvOpMemberDecorate: { + const auto count = inst.NumOperands(); + const uint32_t index = inst.GetSingleWordOperand(1); + std::vector data; + for (uint32_t i = 2; i < count; ++i) { + data.push_back(inst.GetSingleWordOperand(i)); + } + if (Struct* st = type->AsStruct()) { + st->AddMemberDecoration(index, std::move(data)); + } else { + SPIRV_UNIMPLEMENTED(consumer_, "OpMemberDecorate non-struct type"); + } + } break; + default: + SPIRV_UNREACHABLE(consumer_); + break; + } +} + +const Type* TypeManager::GetMemberType( + const Type* parent_type, const std::vector& access_chain) { + for (uint32_t element_index : access_chain) { + if (const Struct* struct_type = parent_type->AsStruct()) { + parent_type = struct_type->element_types()[element_index]; + } else if (const Array* array_type = parent_type->AsArray()) { + parent_type = array_type->element_type(); + } else if (const RuntimeArray* runtime_array_type = + parent_type->AsRuntimeArray()) { + parent_type = runtime_array_type->element_type(); + } else if (const Vector* vector_type = parent_type->AsVector()) { + parent_type = vector_type->element_type(); + } else if (const Matrix* matrix_type = parent_type->AsMatrix()) { + parent_type = matrix_type->element_type(); + } else { + assert(false && "Trying to get a member of a type without members."); + } + } + return parent_type; +} + +void TypeManager::ReplaceForwardPointers(Type* type) { + switch (type->kind()) { + case Type::kArray: { + const ForwardPointer* element_type = + type->AsArray()->element_type()->AsForwardPointer(); + if (element_type) { + type->AsArray()->ReplaceElementType(element_type->target_pointer()); + } + } break; + case Type::kRuntimeArray: { + const ForwardPointer* element_type = + type->AsRuntimeArray()->element_type()->AsForwardPointer(); + if (element_type) { + type->AsRuntimeArray()->ReplaceElementType( + element_type->target_pointer()); + } + } break; + case Type::kStruct: { + auto& member_types = type->AsStruct()->element_types(); + for (auto& member_type : member_types) { + if (member_type->AsForwardPointer()) { + member_type = member_type->AsForwardPointer()->target_pointer(); + assert(member_type); + } + } + } break; + case Type::kPointer: { + const ForwardPointer* pointee_type = + type->AsPointer()->pointee_type()->AsForwardPointer(); + if (pointee_type) { + type->AsPointer()->SetPointeeType(pointee_type->target_pointer()); + } + } break; + case Type::kFunction: { + Function* func_type = type->AsFunction(); + const ForwardPointer* return_type = + func_type->return_type()->AsForwardPointer(); + if (return_type) { + func_type->SetReturnType(return_type->target_pointer()); + } + + auto& param_types = func_type->param_types(); + for (auto& param_type : param_types) { + if (param_type->AsForwardPointer()) { + param_type = param_type->AsForwardPointer()->target_pointer(); + } + } + } break; + default: + break; + } +} + +void TypeManager::ReplaceType(Type* new_type, Type* original_type) { + assert(original_type->kind() == new_type->kind() && + "Types must be the same for replacement.\n"); + for (auto& p : incomplete_types_) { + Type* type = p.type(); + if (!type) { + continue; + } + + switch (type->kind()) { + case Type::kArray: { + const Type* element_type = type->AsArray()->element_type(); + if (element_type == original_type) { + type->AsArray()->ReplaceElementType(new_type); + } + } break; + case Type::kRuntimeArray: { + const Type* element_type = type->AsRuntimeArray()->element_type(); + if (element_type == original_type) { + type->AsRuntimeArray()->ReplaceElementType(new_type); + } + } break; + case Type::kStruct: { + auto& member_types = type->AsStruct()->element_types(); + for (auto& member_type : member_types) { + if (member_type == original_type) { + member_type = new_type; + } + } + } break; + case Type::kPointer: { + const Type* pointee_type = type->AsPointer()->pointee_type(); + if (pointee_type == original_type) { + type->AsPointer()->SetPointeeType(new_type); + } + } break; + case Type::kFunction: { + Function* func_type = type->AsFunction(); + const Type* return_type = func_type->return_type(); + if (return_type == original_type) { + func_type->SetReturnType(new_type); + } + + auto& param_types = func_type->param_types(); + for (auto& param_type : param_types) { + if (param_type == original_type) { + param_type = new_type; + } + } + } break; + default: + break; + } + } +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/type_manager.h b/third_party/spirv-tools/source/opt/type_manager.h new file mode 100644 index 0000000..ce9d83d --- /dev/null +++ b/third_party/spirv-tools/source/opt/type_manager.h @@ -0,0 +1,285 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_TYPE_MANAGER_H_ +#define SOURCE_OPT_TYPE_MANAGER_H_ + +#include +#include +#include +#include +#include + +#include "source/opt/module.h" +#include "source/opt/types.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace opt { + +class IRContext; + +namespace analysis { + +// Hashing functor. +// +// All type pointers must be non-null. +struct HashTypePointer { + size_t operator()(const Type* type) const { + assert(type); + return type->HashValue(); + } +}; +struct HashTypeUniquePointer { + size_t operator()(const std::unique_ptr& type) const { + assert(type); + return type->HashValue(); + } +}; + +// Equality functor. +// +// Checks if two types pointers are the same type. +// +// All type pointers must be non-null. +struct CompareTypePointers { + bool operator()(const Type* lhs, const Type* rhs) const { + assert(lhs && rhs); + return lhs->IsSame(rhs); + } +}; +struct CompareTypeUniquePointers { + bool operator()(const std::unique_ptr& lhs, + const std::unique_ptr& rhs) const { + assert(lhs && rhs); + return lhs->IsSame(rhs.get()); + } +}; + +// A class for managing the SPIR-V type hierarchy. +class TypeManager { + public: + using IdToTypeMap = std::unordered_map; + + // Constructs a type manager from the given |module|. All internal messages + // will be communicated to the outside via the given message |consumer|. + // This instance only keeps a reference to the |consumer|, so the |consumer| + // should outlive this instance. + TypeManager(const MessageConsumer& consumer, IRContext* c); + + TypeManager(const TypeManager&) = delete; + TypeManager(TypeManager&&) = delete; + TypeManager& operator=(const TypeManager&) = delete; + TypeManager& operator=(TypeManager&&) = delete; + + // Returns the type for the given type |id|. Returns nullptr if the given |id| + // does not define a type. + Type* GetType(uint32_t id) const; + // Returns the id for the given |type|. Returns 0 if can not find the given + // |type|. + uint32_t GetId(const Type* type) const; + // Returns the number of types hold in this manager. + size_t NumTypes() const { return id_to_type_.size(); } + // Iterators for all types contained in this manager. + IdToTypeMap::const_iterator begin() const { return id_to_type_.cbegin(); } + IdToTypeMap::const_iterator end() const { return id_to_type_.cend(); } + + // Returns a pair of the type and pointer to the type in |sc|. + // + // |id| must be a registered type. + std::pair> GetTypeAndPointerType( + uint32_t id, SpvStorageClass sc) const; + + // Returns an id for a declaration representing |type|. Returns 0 if the type + // does not exists, and could not be generated. + // + // If |type| is registered, then the registered id is returned. Otherwise, + // this function recursively adds type and annotation instructions as + // necessary to fully define |type|. + uint32_t GetTypeInstruction(const Type* type); + + // Find pointer to type and storage in module, return its resultId. If it is + // not found, a new type is created, and its id is returned. Returns 0 if the + // type could not be created. + uint32_t FindPointerToType(uint32_t type_id, SpvStorageClass storage_class); + + // Registers |id| to |type|. + // + // If GetId(|type|) already returns a non-zero id, that mapping will be + // unchanged. + void RegisterType(uint32_t id, const Type& type); + + // Return the registered type object that is the same as |type|. + Type* GetRegisteredType(const Type* type); + + // Removes knowledge of |id| from the manager. + // + // If |id| is an ambiguous type the multiple ids may be registered to |id|'s + // type (e.g. %struct1 and %struct1 might hash to the same type). In that + // case, calling GetId() with |id|'s type will return another suitable id + // defining that type. + void RemoveId(uint32_t id); + + // Returns the type of the member of |parent_type| that is identified by + // |access_chain|. The vector |access_chain| is a series of integers that are + // used to pick members as in the |OpCompositeExtract| instructions. If you + // want a member of an array, vector, or matrix that does not have a constant + // index, you can use 0 in that position. All elements have the same type. + const Type* GetMemberType(const Type* parent_type, + const std::vector& access_chain); + + Type* GetUIntType() { + Integer int_type(32, false); + return GetRegisteredType(&int_type); + } + + uint32_t GetUIntTypeId() { return GetTypeInstruction(GetUIntType()); } + + Type* GetSIntType() { + Integer int_type(32, true); + return GetRegisteredType(&int_type); + } + + uint32_t GetSIntTypeId() { return GetTypeInstruction(GetSIntType()); } + + Type* GetFloatType() { + Float float_type(32); + return GetRegisteredType(&float_type); + } + + uint32_t GetFloatTypeId() { return GetTypeInstruction(GetFloatType()); } + + Type* GetUIntVectorType(uint32_t size) { + Vector vec_type(GetUIntType(), size); + return GetRegisteredType(&vec_type); + } + + uint32_t GetUIntVectorTypeId(uint32_t size) { + return GetTypeInstruction(GetUIntVectorType(size)); + } + + Type* GetSIntVectorType(uint32_t size) { + Vector vec_type(GetSIntType(), size); + return GetRegisteredType(&vec_type); + } + + uint32_t GetSIntVectorTypeId(uint32_t size) { + return GetTypeInstruction(GetSIntVectorType(size)); + } + + Type* GetFloatVectorType(uint32_t size) { + Vector vec_type(GetFloatType(), size); + return GetRegisteredType(&vec_type); + } + + uint32_t GetFloatVectorTypeId(uint32_t size) { + return GetTypeInstruction(GetFloatVectorType(size)); + } + + Type* GetBoolType() { + Bool bool_type; + return GetRegisteredType(&bool_type); + } + + uint32_t GetBoolTypeId() { return GetTypeInstruction(GetBoolType()); } + + Type* GetVoidType() { + Void void_type; + return GetRegisteredType(&void_type); + } + + uint32_t GetVoidTypeId() { return GetTypeInstruction(GetVoidType()); } + + private: + using TypeToIdMap = std::unordered_map; + using TypePool = + std::unordered_set, HashTypeUniquePointer, + CompareTypeUniquePointers>; + + class UnresolvedType { + public: + UnresolvedType(uint32_t i, Type* t) : id_(i), type_(t) {} + UnresolvedType(const UnresolvedType&) = delete; + UnresolvedType(UnresolvedType&& that) + : id_(that.id_), type_(std::move(that.type_)) {} + + uint32_t id() { return id_; } + Type* type() { return type_.get(); } + std::unique_ptr&& ReleaseType() { return std::move(type_); } + void ResetType(Type* t) { type_.reset(t); } + + private: + uint32_t id_; + std::unique_ptr type_; + }; + using IdToUnresolvedType = std::vector; + + // Analyzes the types and decorations on types in the given |module|. + void AnalyzeTypes(const Module& module); + + IRContext* context() { return context_; } + + // Attaches the decorations on |type| to |id|. + void AttachDecorations(uint32_t id, const Type* type); + + // Create the annotation instruction. + // + // If |element| is zero, an OpDecorate is created, other an OpMemberDecorate + // is created. The annotation is registered with the DefUseManager and the + // DecorationManager. + void CreateDecoration(uint32_t id, const std::vector& decoration, + uint32_t element = 0); + + // Creates and returns a type from the given SPIR-V |inst|. Returns nullptr if + // the given instruction is not for defining a type. + Type* RecordIfTypeDefinition(const Instruction& inst); + // Attaches the decoration encoded in |inst| to |type|. Does nothing if the + // given instruction is not a decoration instruction. Assumes the target is + // |type| (e.g. should be called in loop of |type|'s decorations). + void AttachDecoration(const Instruction& inst, Type* type); + + // Returns an equivalent pointer to |type| built in terms of pointers owned by + // |type_pool_|. For example, if |type| is a vec3 of bool, it will be rebuilt + // replacing the bool subtype with one owned by |type_pool_|. + Type* RebuildType(const Type& type); + + // Completes the incomplete type |type|, by replaces all references to + // ForwardPointer by the defining Pointer. + void ReplaceForwardPointers(Type* type); + + // Replaces all references to |original_type| in |incomplete_types_| by + // |new_type|. + void ReplaceType(Type* new_type, Type* original_type); + + const MessageConsumer& consumer_; // Message consumer. + IRContext* context_; + IdToTypeMap id_to_type_; // Mapping from ids to their type representations. + TypeToIdMap type_to_id_; // Mapping from types to their defining ids. + TypePool type_pool_; // Memory owner of type pointers. + IdToUnresolvedType incomplete_types_; // All incomplete types. Stored in an + // std::vector to make traversals + // deterministic. + + IdToTypeMap id_to_incomplete_type_; // Maps ids to their type representations + // for incomplete types. + + std::unordered_map id_to_constant_inst_; +}; + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_TYPE_MANAGER_H_ diff --git a/third_party/spirv-tools/source/opt/types.cpp b/third_party/spirv-tools/source/opt/types.cpp new file mode 100644 index 0000000..426d3ea --- /dev/null +++ b/third_party/spirv-tools/source/opt/types.cpp @@ -0,0 +1,703 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/types.h" + +#include +#include +#include +#include +#include +#include + +#include "source/util/make_unique.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +using U32VecVec = std::vector>; + +namespace { + +// Returns true if the two vector of vectors are identical. +bool CompareTwoVectors(const U32VecVec a, const U32VecVec b) { + const auto size = a.size(); + if (size != b.size()) return false; + + if (size == 0) return true; + if (size == 1) return a.front() == b.front(); + + std::vector*> a_ptrs, b_ptrs; + a_ptrs.reserve(size); + a_ptrs.reserve(size); + for (uint32_t i = 0; i < size; ++i) { + a_ptrs.push_back(&a[i]); + b_ptrs.push_back(&b[i]); + } + + const auto cmp = [](const std::vector* m, + const std::vector* n) { + return m->front() < n->front(); + }; + + std::sort(a_ptrs.begin(), a_ptrs.end(), cmp); + std::sort(b_ptrs.begin(), b_ptrs.end(), cmp); + + for (uint32_t i = 0; i < size; ++i) { + if (*a_ptrs[i] != *b_ptrs[i]) return false; + } + return true; +} + +} // anonymous namespace + +std::string Type::GetDecorationStr() const { + std::ostringstream oss; + oss << "[["; + for (const auto& decoration : decorations_) { + oss << "("; + for (size_t i = 0; i < decoration.size(); ++i) { + oss << (i > 0 ? ", " : ""); + oss << decoration.at(i); + } + oss << ")"; + } + oss << "]]"; + return oss.str(); +} + +bool Type::HasSameDecorations(const Type* that) const { + return CompareTwoVectors(decorations_, that->decorations_); +} + +bool Type::IsUniqueType(bool allowVariablePointers) const { + switch (kind_) { + case kPointer: + return !allowVariablePointers; + case kStruct: + case kArray: + case kRuntimeArray: + return false; + default: + return true; + } +} + +std::unique_ptr Type::Clone() const { + std::unique_ptr type; + switch (kind_) { +#define DeclareKindCase(kind) \ + case k##kind: \ + type = MakeUnique(*this->As##kind()); \ + break + DeclareKindCase(Void); + DeclareKindCase(Bool); + DeclareKindCase(Integer); + DeclareKindCase(Float); + DeclareKindCase(Vector); + DeclareKindCase(Matrix); + DeclareKindCase(Image); + DeclareKindCase(Sampler); + DeclareKindCase(SampledImage); + DeclareKindCase(Array); + DeclareKindCase(RuntimeArray); + DeclareKindCase(Struct); + DeclareKindCase(Opaque); + DeclareKindCase(Pointer); + DeclareKindCase(Function); + DeclareKindCase(Event); + DeclareKindCase(DeviceEvent); + DeclareKindCase(ReserveId); + DeclareKindCase(Queue); + DeclareKindCase(Pipe); + DeclareKindCase(ForwardPointer); + DeclareKindCase(PipeStorage); + DeclareKindCase(NamedBarrier); + DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(RayQueryProvisionalKHR); +#undef DeclareKindCase + default: + assert(false && "Unhandled type"); + } + return type; +} + +std::unique_ptr Type::RemoveDecorations() const { + std::unique_ptr type(Clone()); + type->ClearDecorations(); + return type; +} + +bool Type::operator==(const Type& other) const { + if (kind_ != other.kind_) return false; + + switch (kind_) { +#define DeclareKindCase(kind) \ + case k##kind: \ + return As##kind()->IsSame(&other) + DeclareKindCase(Void); + DeclareKindCase(Bool); + DeclareKindCase(Integer); + DeclareKindCase(Float); + DeclareKindCase(Vector); + DeclareKindCase(Matrix); + DeclareKindCase(Image); + DeclareKindCase(Sampler); + DeclareKindCase(SampledImage); + DeclareKindCase(Array); + DeclareKindCase(RuntimeArray); + DeclareKindCase(Struct); + DeclareKindCase(Opaque); + DeclareKindCase(Pointer); + DeclareKindCase(Function); + DeclareKindCase(Event); + DeclareKindCase(DeviceEvent); + DeclareKindCase(ReserveId); + DeclareKindCase(Queue); + DeclareKindCase(Pipe); + DeclareKindCase(ForwardPointer); + DeclareKindCase(PipeStorage); + DeclareKindCase(NamedBarrier); + DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(RayQueryProvisionalKHR); +#undef DeclareKindCase + default: + assert(false && "Unhandled type"); + return false; + } +} + +void Type::GetHashWords(std::vector* words, + std::unordered_set* seen) const { + if (!seen->insert(this).second) { + return; + } + + words->push_back(kind_); + for (const auto& d : decorations_) { + for (auto w : d) { + words->push_back(w); + } + } + + switch (kind_) { +#define DeclareKindCase(type) \ + case k##type: \ + As##type()->GetExtraHashWords(words, seen); \ + break + DeclareKindCase(Void); + DeclareKindCase(Bool); + DeclareKindCase(Integer); + DeclareKindCase(Float); + DeclareKindCase(Vector); + DeclareKindCase(Matrix); + DeclareKindCase(Image); + DeclareKindCase(Sampler); + DeclareKindCase(SampledImage); + DeclareKindCase(Array); + DeclareKindCase(RuntimeArray); + DeclareKindCase(Struct); + DeclareKindCase(Opaque); + DeclareKindCase(Pointer); + DeclareKindCase(Function); + DeclareKindCase(Event); + DeclareKindCase(DeviceEvent); + DeclareKindCase(ReserveId); + DeclareKindCase(Queue); + DeclareKindCase(Pipe); + DeclareKindCase(ForwardPointer); + DeclareKindCase(PipeStorage); + DeclareKindCase(NamedBarrier); + DeclareKindCase(AccelerationStructureNV); + DeclareKindCase(CooperativeMatrixNV); + DeclareKindCase(RayQueryProvisionalKHR); +#undef DeclareKindCase + default: + assert(false && "Unhandled type"); + break; + } + + seen->erase(this); +} + +size_t Type::HashValue() const { + std::u32string h; + std::vector words; + GetHashWords(&words); + for (auto w : words) { + h.push_back(w); + } + + return std::hash()(h); +} + +bool Integer::IsSameImpl(const Type* that, IsSameCache*) const { + const Integer* it = that->AsInteger(); + return it && width_ == it->width_ && signed_ == it->signed_ && + HasSameDecorations(that); +} + +std::string Integer::str() const { + std::ostringstream oss; + oss << (signed_ ? "s" : "u") << "int" << width_; + return oss.str(); +} + +void Integer::GetExtraHashWords(std::vector* words, + std::unordered_set*) const { + words->push_back(width_); + words->push_back(signed_); +} + +bool Float::IsSameImpl(const Type* that, IsSameCache*) const { + const Float* ft = that->AsFloat(); + return ft && width_ == ft->width_ && HasSameDecorations(that); +} + +std::string Float::str() const { + std::ostringstream oss; + oss << "float" << width_; + return oss.str(); +} + +void Float::GetExtraHashWords(std::vector* words, + std::unordered_set*) const { + words->push_back(width_); +} + +Vector::Vector(const Type* type, uint32_t count) + : Type(kVector), element_type_(type), count_(count) { + assert(type->AsBool() || type->AsInteger() || type->AsFloat()); +} + +bool Vector::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Vector* vt = that->AsVector(); + if (!vt) return false; + return count_ == vt->count_ && + element_type_->IsSameImpl(vt->element_type_, seen) && + HasSameDecorations(that); +} + +std::string Vector::str() const { + std::ostringstream oss; + oss << "<" << element_type_->str() << ", " << count_ << ">"; + return oss.str(); +} + +void Vector::GetExtraHashWords(std::vector* words, + std::unordered_set* seen) const { + element_type_->GetHashWords(words, seen); + words->push_back(count_); +} + +Matrix::Matrix(const Type* type, uint32_t count) + : Type(kMatrix), element_type_(type), count_(count) { + assert(type->AsVector()); +} + +bool Matrix::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Matrix* mt = that->AsMatrix(); + if (!mt) return false; + return count_ == mt->count_ && + element_type_->IsSameImpl(mt->element_type_, seen) && + HasSameDecorations(that); +} + +std::string Matrix::str() const { + std::ostringstream oss; + oss << "<" << element_type_->str() << ", " << count_ << ">"; + return oss.str(); +} + +void Matrix::GetExtraHashWords(std::vector* words, + std::unordered_set* seen) const { + element_type_->GetHashWords(words, seen); + words->push_back(count_); +} + +Image::Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample, + uint32_t sampling, SpvImageFormat f, SpvAccessQualifier qualifier) + : Type(kImage), + sampled_type_(type), + dim_(dimen), + depth_(d), + arrayed_(array), + ms_(multisample), + sampled_(sampling), + format_(f), + access_qualifier_(qualifier) { + // TODO(antiagainst): check sampled_type +} + +bool Image::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Image* it = that->AsImage(); + if (!it) return false; + return dim_ == it->dim_ && depth_ == it->depth_ && arrayed_ == it->arrayed_ && + ms_ == it->ms_ && sampled_ == it->sampled_ && format_ == it->format_ && + access_qualifier_ == it->access_qualifier_ && + sampled_type_->IsSameImpl(it->sampled_type_, seen) && + HasSameDecorations(that); +} + +std::string Image::str() const { + std::ostringstream oss; + oss << "image(" << sampled_type_->str() << ", " << dim_ << ", " << depth_ + << ", " << arrayed_ << ", " << ms_ << ", " << sampled_ << ", " << format_ + << ", " << access_qualifier_ << ")"; + return oss.str(); +} + +void Image::GetExtraHashWords(std::vector* words, + std::unordered_set* seen) const { + sampled_type_->GetHashWords(words, seen); + words->push_back(dim_); + words->push_back(depth_); + words->push_back(arrayed_); + words->push_back(ms_); + words->push_back(sampled_); + words->push_back(format_); + words->push_back(access_qualifier_); +} + +bool SampledImage::IsSameImpl(const Type* that, IsSameCache* seen) const { + const SampledImage* sit = that->AsSampledImage(); + if (!sit) return false; + return image_type_->IsSameImpl(sit->image_type_, seen) && + HasSameDecorations(that); +} + +std::string SampledImage::str() const { + std::ostringstream oss; + oss << "sampled_image(" << image_type_->str() << ")"; + return oss.str(); +} + +void SampledImage::GetExtraHashWords( + std::vector* words, std::unordered_set* seen) const { + image_type_->GetHashWords(words, seen); +} + +Array::Array(const Type* type, const Array::LengthInfo& length_info_arg) + : Type(kArray), element_type_(type), length_info_(length_info_arg) { + assert(type != nullptr); + assert(!type->AsVoid()); + // We always have a word to say which case we're in, followed + // by at least one more word. + assert(length_info_arg.words.size() >= 2); +} + +bool Array::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Array* at = that->AsArray(); + if (!at) return false; + bool is_same = element_type_->IsSameImpl(at->element_type_, seen); + is_same = is_same && HasSameDecorations(that); + is_same = is_same && (length_info_.words == at->length_info_.words); + return is_same; +} + +std::string Array::str() const { + std::ostringstream oss; + oss << "[" << element_type_->str() << ", id(" << LengthId() << "), words("; + const char* spacer = ""; + for (auto w : length_info_.words) { + oss << spacer << w; + spacer = ","; + } + oss << ")]"; + return oss.str(); +} + +void Array::GetExtraHashWords(std::vector* words, + std::unordered_set* seen) const { + element_type_->GetHashWords(words, seen); + // This should mirror the logic in IsSameImpl + words->insert(words->end(), length_info_.words.begin(), + length_info_.words.end()); +} + +void Array::ReplaceElementType(const Type* type) { element_type_ = type; } + +RuntimeArray::RuntimeArray(const Type* type) + : Type(kRuntimeArray), element_type_(type) { + assert(!type->AsVoid()); +} + +bool RuntimeArray::IsSameImpl(const Type* that, IsSameCache* seen) const { + const RuntimeArray* rat = that->AsRuntimeArray(); + if (!rat) return false; + return element_type_->IsSameImpl(rat->element_type_, seen) && + HasSameDecorations(that); +} + +std::string RuntimeArray::str() const { + std::ostringstream oss; + oss << "[" << element_type_->str() << "]"; + return oss.str(); +} + +void RuntimeArray::GetExtraHashWords( + std::vector* words, std::unordered_set* seen) const { + element_type_->GetHashWords(words, seen); +} + +void RuntimeArray::ReplaceElementType(const Type* type) { + element_type_ = type; +} + +Struct::Struct(const std::vector& types) + : Type(kStruct), element_types_(types) { + for (const auto* t : types) { + (void)t; + assert(!t->AsVoid()); + } +} + +void Struct::AddMemberDecoration(uint32_t index, + std::vector&& decoration) { + if (index >= element_types_.size()) { + assert(0 && "index out of bound"); + return; + } + + element_decorations_[index].push_back(std::move(decoration)); +} + +bool Struct::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Struct* st = that->AsStruct(); + if (!st) return false; + if (element_types_.size() != st->element_types_.size()) return false; + const auto size = element_decorations_.size(); + if (size != st->element_decorations_.size()) return false; + if (!HasSameDecorations(that)) return false; + + for (size_t i = 0; i < element_types_.size(); ++i) { + if (!element_types_[i]->IsSameImpl(st->element_types_[i], seen)) + return false; + } + for (const auto& p : element_decorations_) { + if (st->element_decorations_.count(p.first) == 0) return false; + if (!CompareTwoVectors(p.second, st->element_decorations_.at(p.first))) + return false; + } + return true; +} + +std::string Struct::str() const { + std::ostringstream oss; + oss << "{"; + const size_t count = element_types_.size(); + for (size_t i = 0; i < count; ++i) { + oss << element_types_[i]->str(); + if (i + 1 != count) oss << ", "; + } + oss << "}"; + return oss.str(); +} + +void Struct::GetExtraHashWords(std::vector* words, + std::unordered_set* seen) const { + for (auto* t : element_types_) { + t->GetHashWords(words, seen); + } + for (const auto& pair : element_decorations_) { + words->push_back(pair.first); + for (const auto& d : pair.second) { + for (auto w : d) { + words->push_back(w); + } + } + } +} + +bool Opaque::IsSameImpl(const Type* that, IsSameCache*) const { + const Opaque* ot = that->AsOpaque(); + if (!ot) return false; + return name_ == ot->name_ && HasSameDecorations(that); +} + +std::string Opaque::str() const { + std::ostringstream oss; + oss << "opaque('" << name_ << "')"; + return oss.str(); +} + +void Opaque::GetExtraHashWords(std::vector* words, + std::unordered_set*) const { + for (auto c : name_) { + words->push_back(static_cast(c)); + } +} + +Pointer::Pointer(const Type* type, SpvStorageClass sc) + : Type(kPointer), pointee_type_(type), storage_class_(sc) {} + +bool Pointer::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Pointer* pt = that->AsPointer(); + if (!pt) return false; + if (storage_class_ != pt->storage_class_) return false; + auto p = seen->insert(std::make_pair(this, that->AsPointer())); + if (!p.second) { + return true; + } + bool same_pointee = pointee_type_->IsSameImpl(pt->pointee_type_, seen); + seen->erase(p.first); + if (!same_pointee) { + return false; + } + return HasSameDecorations(that); +} + +std::string Pointer::str() const { + std::ostringstream os; + os << pointee_type_->str() << " " << static_cast(storage_class_) + << "*"; + return os.str(); +} + +void Pointer::GetExtraHashWords(std::vector* words, + std::unordered_set* seen) const { + pointee_type_->GetHashWords(words, seen); + words->push_back(storage_class_); +} + +void Pointer::SetPointeeType(const Type* type) { pointee_type_ = type; } + +Function::Function(const Type* ret_type, const std::vector& params) + : Type(kFunction), return_type_(ret_type), param_types_(params) {} + +Function::Function(const Type* ret_type, std::vector& params) + : Type(kFunction), return_type_(ret_type), param_types_(params) {} + +bool Function::IsSameImpl(const Type* that, IsSameCache* seen) const { + const Function* ft = that->AsFunction(); + if (!ft) return false; + if (!return_type_->IsSameImpl(ft->return_type_, seen)) return false; + if (param_types_.size() != ft->param_types_.size()) return false; + for (size_t i = 0; i < param_types_.size(); ++i) { + if (!param_types_[i]->IsSameImpl(ft->param_types_[i], seen)) return false; + } + return HasSameDecorations(that); +} + +std::string Function::str() const { + std::ostringstream oss; + const size_t count = param_types_.size(); + oss << "("; + for (size_t i = 0; i < count; ++i) { + oss << param_types_[i]->str(); + if (i + 1 != count) oss << ", "; + } + oss << ") -> " << return_type_->str(); + return oss.str(); +} + +void Function::GetExtraHashWords(std::vector* words, + std::unordered_set* seen) const { + return_type_->GetHashWords(words, seen); + for (const auto* t : param_types_) { + t->GetHashWords(words, seen); + } +} + +void Function::SetReturnType(const Type* type) { return_type_ = type; } + +bool Pipe::IsSameImpl(const Type* that, IsSameCache*) const { + const Pipe* pt = that->AsPipe(); + if (!pt) return false; + return access_qualifier_ == pt->access_qualifier_ && HasSameDecorations(that); +} + +std::string Pipe::str() const { + std::ostringstream oss; + oss << "pipe(" << access_qualifier_ << ")"; + return oss.str(); +} + +void Pipe::GetExtraHashWords(std::vector* words, + std::unordered_set*) const { + words->push_back(access_qualifier_); +} + +bool ForwardPointer::IsSameImpl(const Type* that, IsSameCache*) const { + const ForwardPointer* fpt = that->AsForwardPointer(); + if (!fpt) return false; + return (pointer_ && fpt->pointer_ ? *pointer_ == *fpt->pointer_ + : target_id_ == fpt->target_id_) && + storage_class_ == fpt->storage_class_ && HasSameDecorations(that); +} + +std::string ForwardPointer::str() const { + std::ostringstream oss; + oss << "forward_pointer("; + if (pointer_ != nullptr) { + oss << pointer_->str(); + } else { + oss << target_id_; + } + oss << ")"; + return oss.str(); +} + +void ForwardPointer::GetExtraHashWords( + std::vector* words, std::unordered_set* seen) const { + words->push_back(target_id_); + words->push_back(storage_class_); + if (pointer_) pointer_->GetHashWords(words, seen); +} + +CooperativeMatrixNV::CooperativeMatrixNV(const Type* type, const uint32_t scope, + const uint32_t rows, + const uint32_t columns) + : Type(kCooperativeMatrixNV), + component_type_(type), + scope_id_(scope), + rows_id_(rows), + columns_id_(columns) { + assert(type != nullptr); + assert(scope != 0); + assert(rows != 0); + assert(columns != 0); +} + +std::string CooperativeMatrixNV::str() const { + std::ostringstream oss; + oss << "<" << component_type_->str() << ", " << scope_id_ << ", " << rows_id_ + << ", " << columns_id_ << ">"; + return oss.str(); +} + +void CooperativeMatrixNV::GetExtraHashWords( + std::vector* words, std::unordered_set* pSet) const { + component_type_->GetHashWords(words, pSet); + words->push_back(scope_id_); + words->push_back(rows_id_); + words->push_back(columns_id_); +} + +bool CooperativeMatrixNV::IsSameImpl(const Type* that, + IsSameCache* seen) const { + const CooperativeMatrixNV* mt = that->AsCooperativeMatrixNV(); + if (!mt) return false; + return component_type_->IsSameImpl(mt->component_type_, seen) && + scope_id_ == mt->scope_id_ && rows_id_ == mt->rows_id_ && + columns_id_ == mt->columns_id_ && HasSameDecorations(that); +} + +} // namespace analysis +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/types.h b/third_party/spirv-tools/source/opt/types.h new file mode 100644 index 0000000..ebeb476 --- /dev/null +++ b/third_party/spirv-tools/source/opt/types.h @@ -0,0 +1,672 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file provides a class hierarchy for representing SPIR-V types. + +#ifndef SOURCE_OPT_TYPES_H_ +#define SOURCE_OPT_TYPES_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "source/opt/instruction.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace opt { +namespace analysis { + +class Void; +class Bool; +class Integer; +class Float; +class Vector; +class Matrix; +class Image; +class Sampler; +class SampledImage; +class Array; +class RuntimeArray; +class Struct; +class Opaque; +class Pointer; +class Function; +class Event; +class DeviceEvent; +class ReserveId; +class Queue; +class Pipe; +class ForwardPointer; +class PipeStorage; +class NamedBarrier; +class AccelerationStructureNV; +class CooperativeMatrixNV; +class RayQueryProvisionalKHR; + +// Abstract class for a SPIR-V type. It has a bunch of As() methods, +// which is used as a way to probe the actual . +class Type { + public: + typedef std::set> IsSameCache; + + // Available subtypes. + // + // When adding a new derived class of Type, please add an entry to the enum. + enum Kind { + kVoid, + kBool, + kInteger, + kFloat, + kVector, + kMatrix, + kImage, + kSampler, + kSampledImage, + kArray, + kRuntimeArray, + kStruct, + kOpaque, + kPointer, + kFunction, + kEvent, + kDeviceEvent, + kReserveId, + kQueue, + kPipe, + kForwardPointer, + kPipeStorage, + kNamedBarrier, + kAccelerationStructureNV, + kCooperativeMatrixNV, + kRayQueryProvisionalKHR + }; + + Type(Kind k) : kind_(k) {} + + virtual ~Type() {} + + // Attaches a decoration directly on this type. + void AddDecoration(std::vector&& d) { + decorations_.push_back(std::move(d)); + } + // Returns the decorations on this type as a string. + std::string GetDecorationStr() const; + // Returns true if this type has exactly the same decorations as |that| type. + bool HasSameDecorations(const Type* that) const; + // Returns true if this type is exactly the same as |that| type, including + // decorations. + bool IsSame(const Type* that) const { + IsSameCache seen; + return IsSameImpl(that, &seen); + } + + // Returns true if this type is exactly the same as |that| type, including + // decorations. |seen| is the set of |Pointer*| pair that are currently being + // compared in a parent call to |IsSameImpl|. + virtual bool IsSameImpl(const Type* that, IsSameCache* seen) const = 0; + + // Returns a human-readable string to represent this type. + virtual std::string str() const = 0; + + Kind kind() const { return kind_; } + const std::vector>& decorations() const { + return decorations_; + } + + // Returns true if there is no decoration on this type. For struct types, + // returns true only when there is no decoration for both the struct type + // and the struct members. + virtual bool decoration_empty() const { return decorations_.empty(); } + + // Creates a clone of |this|. + std::unique_ptr Clone() const; + + // Returns a clone of |this| minus any decorations. + std::unique_ptr RemoveDecorations() const; + + // Returns true if this type must be unique. + // + // If variable pointers are allowed, then pointers are not required to be + // unique. + // TODO(alanbaker): Update this if variable pointers become a core feature. + bool IsUniqueType(bool allowVariablePointers = false) const; + + bool operator==(const Type& other) const; + + // Returns the hash value of this type. + size_t HashValue() const; + + // Adds the necessary words to compute a hash value of this type to |words|. + void GetHashWords(std::vector* words) const { + std::unordered_set seen; + GetHashWords(words, &seen); + } + + // Adds the necessary words to compute a hash value of this type to |words|. + void GetHashWords(std::vector* words, + std::unordered_set* seen) const; + + // Adds necessary extra words for a subtype to calculate a hash value into + // |words|. + virtual void GetExtraHashWords( + std::vector* words, + std::unordered_set* pSet) const = 0; + +// A bunch of methods for casting this type to a given type. Returns this if the +// cast can be done, nullptr otherwise. +// clang-format off +#define DeclareCastMethod(target) \ + virtual target* As##target() { return nullptr; } \ + virtual const target* As##target() const { return nullptr; } + DeclareCastMethod(Void) + DeclareCastMethod(Bool) + DeclareCastMethod(Integer) + DeclareCastMethod(Float) + DeclareCastMethod(Vector) + DeclareCastMethod(Matrix) + DeclareCastMethod(Image) + DeclareCastMethod(Sampler) + DeclareCastMethod(SampledImage) + DeclareCastMethod(Array) + DeclareCastMethod(RuntimeArray) + DeclareCastMethod(Struct) + DeclareCastMethod(Opaque) + DeclareCastMethod(Pointer) + DeclareCastMethod(Function) + DeclareCastMethod(Event) + DeclareCastMethod(DeviceEvent) + DeclareCastMethod(ReserveId) + DeclareCastMethod(Queue) + DeclareCastMethod(Pipe) + DeclareCastMethod(ForwardPointer) + DeclareCastMethod(PipeStorage) + DeclareCastMethod(NamedBarrier) + DeclareCastMethod(AccelerationStructureNV) + DeclareCastMethod(CooperativeMatrixNV) + DeclareCastMethod(RayQueryProvisionalKHR) +#undef DeclareCastMethod + + protected: + // Decorations attached to this type. Each decoration is encoded as a vector + // of uint32_t numbers. The first uint32_t number is the decoration value, + // and the rest are the parameters to the decoration (if exists). + std::vector> decorations_; + + private: + // Removes decorations on this type. For struct types, also removes element + // decorations. + virtual void ClearDecorations() { decorations_.clear(); } + + Kind kind_; +}; +// clang-format on + +class Integer : public Type { + public: + Integer(uint32_t w, bool is_signed) + : Type(kInteger), width_(w), signed_(is_signed) {} + Integer(const Integer&) = default; + + std::string str() const override; + + Integer* AsInteger() override { return this; } + const Integer* AsInteger() const override { return this; } + uint32_t width() const { return width_; } + bool IsSigned() const { return signed_; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + uint32_t width_; // bit width + bool signed_; // true if this integer is signed +}; + +class Float : public Type { + public: + Float(uint32_t w) : Type(kFloat), width_(w) {} + Float(const Float&) = default; + + std::string str() const override; + + Float* AsFloat() override { return this; } + const Float* AsFloat() const override { return this; } + uint32_t width() const { return width_; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + uint32_t width_; // bit width +}; + +class Vector : public Type { + public: + Vector(const Type* element_type, uint32_t count); + Vector(const Vector&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + uint32_t element_count() const { return count_; } + + Vector* AsVector() override { return this; } + const Vector* AsVector() const override { return this; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; + uint32_t count_; +}; + +class Matrix : public Type { + public: + Matrix(const Type* element_type, uint32_t count); + Matrix(const Matrix&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + uint32_t element_count() const { return count_; } + + Matrix* AsMatrix() override { return this; } + const Matrix* AsMatrix() const override { return this; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; + uint32_t count_; +}; + +class Image : public Type { + public: + Image(Type* type, SpvDim dimen, uint32_t d, bool array, bool multisample, + uint32_t sampling, SpvImageFormat f, + SpvAccessQualifier qualifier = SpvAccessQualifierReadOnly); + Image(const Image&) = default; + + std::string str() const override; + + Image* AsImage() override { return this; } + const Image* AsImage() const override { return this; } + + const Type* sampled_type() const { return sampled_type_; } + SpvDim dim() const { return dim_; } + uint32_t depth() const { return depth_; } + bool is_arrayed() const { return arrayed_; } + bool is_multisampled() const { return ms_; } + uint32_t sampled() const { return sampled_; } + SpvImageFormat format() const { return format_; } + SpvAccessQualifier access_qualifier() const { return access_qualifier_; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + Type* sampled_type_; + SpvDim dim_; + uint32_t depth_; + bool arrayed_; + bool ms_; + uint32_t sampled_; + SpvImageFormat format_; + SpvAccessQualifier access_qualifier_; +}; + +class SampledImage : public Type { + public: + SampledImage(Type* image) : Type(kSampledImage), image_type_(image) {} + SampledImage(const SampledImage&) = default; + + std::string str() const override; + + SampledImage* AsSampledImage() override { return this; } + const SampledImage* AsSampledImage() const override { return this; } + + const Type* image_type() const { return image_type_; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + Type* image_type_; +}; + +class Array : public Type { + public: + // Data about the length operand, that helps us distinguish between one + // array length and another. + struct LengthInfo { + // The result id of the instruction defining the length. + const uint32_t id; + enum Case : uint32_t { + kConstant = 0, + kConstantWithSpecId = 1, + kDefiningId = 2 + }; + // Extra words used to distinshish one array length and another. + // - if OpConstant, then it's 0, then the words in the literal constant + // value. + // - if OpSpecConstant, then it's 1, then the SpecID decoration if there + // is one, followed by the words in the literal constant value. + // The spec might not be overridden, in which case we'll end up using + // the literal value. + // - Otherwise, it's an OpSpecConsant, and this 2, then the ID (again). + const std::vector words; + }; + + // Constructs an array type with given element and length. If the length + // is an OpSpecConstant, then |spec_id| should be its SpecId decoration. + Array(const Type* element_type, const LengthInfo& length_info_arg); + Array(const Array&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + uint32_t LengthId() const { return length_info_.id; } + const LengthInfo& length_info() const { return length_info_; } + + Array* AsArray() override { return this; } + const Array* AsArray() const override { return this; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + void ReplaceElementType(const Type* element_type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; + const LengthInfo length_info_; +}; + +class RuntimeArray : public Type { + public: + RuntimeArray(const Type* element_type); + RuntimeArray(const RuntimeArray&) = default; + + std::string str() const override; + const Type* element_type() const { return element_type_; } + + RuntimeArray* AsRuntimeArray() override { return this; } + const RuntimeArray* AsRuntimeArray() const override { return this; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + void ReplaceElementType(const Type* element_type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* element_type_; +}; + +class Struct : public Type { + public: + Struct(const std::vector& element_types); + Struct(const Struct&) = default; + + // Adds a decoration to the member at the given index. The first word is the + // decoration enum, and the remaining words, if any, are its operands. + void AddMemberDecoration(uint32_t index, std::vector&& decoration); + + std::string str() const override; + const std::vector& element_types() const { + return element_types_; + } + std::vector& element_types() { return element_types_; } + bool decoration_empty() const override { + return decorations_.empty() && element_decorations_.empty(); + } + + const std::map>>& + element_decorations() const { + return element_decorations_; + } + + Struct* AsStruct() override { return this; } + const Struct* AsStruct() const override { return this; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + void ClearDecorations() override { + decorations_.clear(); + element_decorations_.clear(); + } + + std::vector element_types_; + // We can attach decorations to struct members and that should not affect the + // underlying element type. So we need an extra data structure here to keep + // track of element type decorations. They must be stored in an ordered map + // because |GetExtraHashWords| will traverse the structure. It must have a + // fixed order in order to hash to the same value every time. + std::map>> element_decorations_; +}; + +class Opaque : public Type { + public: + Opaque(std::string n) : Type(kOpaque), name_(std::move(n)) {} + Opaque(const Opaque&) = default; + + std::string str() const override; + + Opaque* AsOpaque() override { return this; } + const Opaque* AsOpaque() const override { return this; } + + const std::string& name() const { return name_; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + std::string name_; +}; + +class Pointer : public Type { + public: + Pointer(const Type* pointee, SpvStorageClass sc); + Pointer(const Pointer&) = default; + + std::string str() const override; + const Type* pointee_type() const { return pointee_type_; } + SpvStorageClass storage_class() const { return storage_class_; } + + Pointer* AsPointer() override { return this; } + const Pointer* AsPointer() const override { return this; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + void SetPointeeType(const Type* type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* pointee_type_; + SpvStorageClass storage_class_; +}; + +class Function : public Type { + public: + Function(const Type* ret_type, const std::vector& params); + Function(const Type* ret_type, std::vector& params); + Function(const Function&) = default; + + std::string str() const override; + + Function* AsFunction() override { return this; } + const Function* AsFunction() const override { return this; } + + const Type* return_type() const { return return_type_; } + const std::vector& param_types() const { return param_types_; } + std::vector& param_types() { return param_types_; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set*) const override; + + void SetReturnType(const Type* type); + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* return_type_; + std::vector param_types_; +}; + +class Pipe : public Type { + public: + Pipe(SpvAccessQualifier qualifier) + : Type(kPipe), access_qualifier_(qualifier) {} + Pipe(const Pipe&) = default; + + std::string str() const override; + + Pipe* AsPipe() override { return this; } + const Pipe* AsPipe() const override { return this; } + + SpvAccessQualifier access_qualifier() const { return access_qualifier_; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + SpvAccessQualifier access_qualifier_; +}; + +class ForwardPointer : public Type { + public: + ForwardPointer(uint32_t id, SpvStorageClass sc) + : Type(kForwardPointer), + target_id_(id), + storage_class_(sc), + pointer_(nullptr) {} + ForwardPointer(const ForwardPointer&) = default; + + uint32_t target_id() const { return target_id_; } + void SetTargetPointer(const Pointer* pointer) { pointer_ = pointer; } + SpvStorageClass storage_class() const { return storage_class_; } + const Pointer* target_pointer() const { return pointer_; } + + std::string str() const override; + + ForwardPointer* AsForwardPointer() override { return this; } + const ForwardPointer* AsForwardPointer() const override { return this; } + + void GetExtraHashWords(std::vector* words, + std::unordered_set* pSet) const override; + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + uint32_t target_id_; + SpvStorageClass storage_class_; + const Pointer* pointer_; +}; + +class CooperativeMatrixNV : public Type { + public: + CooperativeMatrixNV(const Type* type, const uint32_t scope, + const uint32_t rows, const uint32_t columns); + CooperativeMatrixNV(const CooperativeMatrixNV&) = default; + + std::string str() const override; + + CooperativeMatrixNV* AsCooperativeMatrixNV() override { return this; } + const CooperativeMatrixNV* AsCooperativeMatrixNV() const override { + return this; + } + + void GetExtraHashWords(std::vector*, + std::unordered_set*) const override; + + const Type* component_type() const { return component_type_; } + uint32_t scope_id() const { return scope_id_; } + uint32_t rows_id() const { return rows_id_; } + uint32_t columns_id() const { return columns_id_; } + + private: + bool IsSameImpl(const Type* that, IsSameCache*) const override; + + const Type* component_type_; + const uint32_t scope_id_; + const uint32_t rows_id_; + const uint32_t columns_id_; +}; + +#define DefineParameterlessType(type, name) \ + class type : public Type { \ + public: \ + type() : Type(k##type) {} \ + type(const type&) = default; \ + \ + std::string str() const override { return #name; } \ + \ + type* As##type() override { return this; } \ + const type* As##type() const override { return this; } \ + \ + void GetExtraHashWords(std::vector*, \ + std::unordered_set*) const override {} \ + \ + private: \ + bool IsSameImpl(const Type* that, IsSameCache*) const override { \ + return that->As##type() && HasSameDecorations(that); \ + } \ + } +DefineParameterlessType(Void, void); +DefineParameterlessType(Bool, bool); +DefineParameterlessType(Sampler, sampler); +DefineParameterlessType(Event, event); +DefineParameterlessType(DeviceEvent, device_event); +DefineParameterlessType(ReserveId, reserve_id); +DefineParameterlessType(Queue, queue); +DefineParameterlessType(PipeStorage, pipe_storage); +DefineParameterlessType(NamedBarrier, named_barrier); +DefineParameterlessType(AccelerationStructureNV, accelerationStructureNV); +DefineParameterlessType(RayQueryProvisionalKHR, rayQueryProvisionalKHR); +#undef DefineParameterlessType + +} // namespace analysis +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_TYPES_H_ diff --git a/third_party/spirv-tools/source/opt/unify_const_pass.cpp b/third_party/spirv-tools/source/opt/unify_const_pass.cpp new file mode 100644 index 0000000..227fd61 --- /dev/null +++ b/third_party/spirv-tools/source/opt/unify_const_pass.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/unify_const_pass.h" + +#include +#include +#include +#include + +#include "source/opt/def_use_manager.h" +#include "source/opt/ir_context.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +namespace { + +// The trie that stores a bunch of result ids and, for a given instruction, +// searches the result id that has been defined with the same opcode, type and +// operands. +class ResultIdTrie { + public: + ResultIdTrie() : root_(new Node) {} + + // For a given instruction, extracts its opcode, type id and operand words + // as an array of keys, looks up the trie to find a result id which is stored + // with the same opcode, type id and operand words. If none of such result id + // is found, creates a trie node with those keys, stores the instruction's + // result id and returns that result id. If an existing result id is found, + // returns the existing result id. + uint32_t LookupEquivalentResultFor(const Instruction& inst) { + auto keys = GetLookUpKeys(inst); + auto* node = root_.get(); + for (uint32_t key : keys) { + node = node->GetOrCreateTrieNodeFor(key); + } + if (node->result_id() == 0) { + node->SetResultId(inst.result_id()); + } + return node->result_id(); + } + + private: + // The trie node to store result ids. + class Node { + public: + using TrieNodeMap = std::unordered_map>; + + Node() : result_id_(0), next_() {} + uint32_t result_id() const { return result_id_; } + + // Sets the result id stored in this node. + void SetResultId(uint32_t id) { result_id_ = id; } + + // Searches for the child trie node with the given key. If the node is + // found, returns that node. Otherwise creates an empty child node with + // that key and returns that newly created node. + Node* GetOrCreateTrieNodeFor(uint32_t key) { + auto iter = next_.find(key); + if (iter == next_.end()) { + // insert a new node and return the node. + return next_.insert(std::make_pair(key, MakeUnique())) + .first->second.get(); + } + return iter->second.get(); + } + + private: + // The result id stored in this node. 0 means this node is empty. + uint32_t result_id_; + // The mapping from the keys to the child nodes of this node. + TrieNodeMap next_; + }; + + // Returns a vector of the opcode followed by the words in the raw SPIR-V + // instruction encoding but without the result id. + std::vector GetLookUpKeys(const Instruction& inst) { + std::vector keys; + // Need to use the opcode, otherwise there might be a conflict with the + // following case when 's binary value equals xx's id: + // OpSpecConstantOp tt yy zz + // OpSpecConstantComposite tt xx yy zz; + keys.push_back(static_cast(inst.opcode())); + for (const auto& operand : inst) { + if (operand.type == SPV_OPERAND_TYPE_RESULT_ID) continue; + keys.insert(keys.end(), operand.words.cbegin(), operand.words.cend()); + } + return keys; + } + + std::unique_ptr root_; // The root node of the trie. +}; +} // anonymous namespace + +Pass::Status UnifyConstantPass::Process() { + bool modified = false; + ResultIdTrie defined_constants; + + for (Instruction *next_instruction, + *inst = &*(context()->types_values_begin()); + inst; inst = next_instruction) { + next_instruction = inst->NextNode(); + + // Do not handle the instruction when there are decorations upon the result + // id. + if (get_def_use_mgr()->GetAnnotations(inst->result_id()).size() != 0) { + continue; + } + + // The overall algorithm is to store the result ids of all the eligible + // constants encountered so far in a trie. For a constant defining + // instruction under consideration, use its opcode, result type id and + // words in operands as an array of keys to lookup the trie. If a result id + // can be found for that array of keys, a constant with exactly the same + // value must has been defined before, the constant under processing + // should be replaced by the constant previously defined. If no such result + // id can be found for that array of keys, this must be the first time a + // constant with its value be defined, we then create a new trie node to + // store the result id with the keys. When replacing a duplicated constant + // with a previously defined constant, all the uses of the duplicated + // constant, which must be placed after the duplicated constant defining + // instruction, will be updated. This way, the descendants of the + // previously defined constant and the duplicated constant will both refer + // to the previously defined constant. So that the operand ids which are + // used in key arrays will be the ids of the unified constants, when + // processing is up to a descendant. This makes comparing the key array + // always valid for judging duplication. + switch (inst->opcode()) { + case SpvOp::SpvOpConstantTrue: + case SpvOp::SpvOpConstantFalse: + case SpvOp::SpvOpConstant: + case SpvOp::SpvOpConstantNull: + case SpvOp::SpvOpConstantSampler: + case SpvOp::SpvOpConstantComposite: + // Only spec constants defined with OpSpecConstantOp and + // OpSpecConstantComposite should be processed in this pass. Spec + // constants defined with OpSpecConstant{|True|False} are decorated with + // 'SpecId' decoration and all of them should be treated as unique. + // 'SpecId' is not applicable to SpecConstants defined with + // OpSpecConstant{Op|Composite}, their values are not necessary to be + // unique. When all the operands/compoents are the same between two + // OpSpecConstant{Op|Composite} results, their result values must be the + // same so are unifiable. + case SpvOp::SpvOpSpecConstantOp: + case SpvOp::SpvOpSpecConstantComposite: { + uint32_t id = defined_constants.LookupEquivalentResultFor(*inst); + if (id != inst->result_id()) { + // The constant is a duplicated one, use the cached constant to + // replace the uses of this duplicated one, then turn it to nop. + context()->ReplaceAllUsesWith(inst->result_id(), id); + context()->KillInst(inst); + modified = true; + } + break; + } + default: + break; + } + } + return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/unify_const_pass.h b/third_party/spirv-tools/source/opt/unify_const_pass.h new file mode 100644 index 0000000..f2b7897 --- /dev/null +++ b/third_party/spirv-tools/source/opt/unify_const_pass.h @@ -0,0 +1,35 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_UNIFY_CONST_PASS_H_ +#define SOURCE_OPT_UNIFY_CONST_PASS_H_ + +#include "source/opt/ir_context.h" +#include "source/opt/module.h" +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class UnifyConstantPass : public Pass { + public: + const char* name() const override { return "unify-const"; } + Status Process() override; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_UNIFY_CONST_PASS_H_ diff --git a/third_party/spirv-tools/source/opt/upgrade_memory_model.cpp b/third_party/spirv-tools/source/opt/upgrade_memory_model.cpp new file mode 100644 index 0000000..ab25205 --- /dev/null +++ b/third_party/spirv-tools/source/opt/upgrade_memory_model.cpp @@ -0,0 +1,770 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "upgrade_memory_model.h" + +#include + +#include "source/opt/ir_builder.h" +#include "source/opt/ir_context.h" +#include "source/spirv_constant.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace opt { + +Pass::Status UpgradeMemoryModel::Process() { + // TODO: This pass needs changes to support cooperative matrices. + if (context()->get_feature_mgr()->HasCapability( + SpvCapabilityCooperativeMatrixNV)) { + return Pass::Status::SuccessWithoutChange; + } + + // Only update Logical GLSL450 to Logical VulkanKHR. + Instruction* memory_model = get_module()->GetMemoryModel(); + if (memory_model->GetSingleWordInOperand(0u) != SpvAddressingModelLogical || + memory_model->GetSingleWordInOperand(1u) != SpvMemoryModelGLSL450) { + return Pass::Status::SuccessWithoutChange; + } + + UpgradeMemoryModelInstruction(); + UpgradeInstructions(); + CleanupDecorations(); + UpgradeBarriers(); + UpgradeMemoryScope(); + + return Pass::Status::SuccessWithChange; +} + +void UpgradeMemoryModel::UpgradeMemoryModelInstruction() { + // Overall changes necessary: + // 1. Add the OpExtension. + // 2. Add the OpCapability. + // 3. Modify the memory model. + Instruction* memory_model = get_module()->GetMemoryModel(); + context()->AddCapability(MakeUnique( + context(), SpvOpCapability, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_CAPABILITY, {SpvCapabilityVulkanMemoryModelKHR}}})); + const std::string extension = "SPV_KHR_vulkan_memory_model"; + std::vector words(extension.size() / 4 + 1, 0); + char* dst = reinterpret_cast(words.data()); + strncpy(dst, extension.c_str(), extension.size()); + context()->AddExtension( + MakeUnique(context(), SpvOpExtension, 0, 0, + std::initializer_list{ + {SPV_OPERAND_TYPE_LITERAL_STRING, words}})); + memory_model->SetInOperand(1u, {SpvMemoryModelVulkanKHR}); +} + +void UpgradeMemoryModel::UpgradeInstructions() { + // Coherent and Volatile decorations are deprecated. Remove them and replace + // with flags on the memory/image operations. The decorations can occur on + // OpVariable, OpFunctionParameter (of pointer type) and OpStructType (member + // decoration). Trace from the decoration target(s) to the final memory/image + // instructions. Additionally, Workgroup storage class variables and function + // parameters are implicitly coherent in GLSL450. + + // Upgrade modf and frexp first since they generate new stores. + // In SPIR-V 1.4 or later, normalize OpCopyMemory* access operands. + for (auto& func : *get_module()) { + func.ForEachInst([this](Instruction* inst) { + if (inst->opcode() == SpvOpExtInst) { + auto ext_inst = inst->GetSingleWordInOperand(1u); + if (ext_inst == GLSLstd450Modf || ext_inst == GLSLstd450Frexp) { + auto import = + get_def_use_mgr()->GetDef(inst->GetSingleWordInOperand(0u)); + if (reinterpret_cast(import->GetInOperand(0u).words.data()) == + std::string("GLSL.std.450")) { + UpgradeExtInst(inst); + } + } + } else if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + if (inst->opcode() == SpvOpCopyMemory || + inst->opcode() == SpvOpCopyMemorySized) { + uint32_t start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u; + if (inst->NumInOperands() > start_operand) { + auto num_access_words = MemoryAccessNumWords( + inst->GetSingleWordInOperand(start_operand)); + if ((num_access_words + start_operand) == inst->NumInOperands()) { + // There is a single memory access operand. Duplicate it to have a + // separate operand for both source and target. + for (uint32_t i = 0; i < num_access_words; ++i) { + auto operand = inst->GetInOperand(start_operand + i); + inst->AddOperand(std::move(operand)); + } + } + } else { + // Add two memory access operands. + inst->AddOperand( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}}); + inst->AddOperand( + {SPV_OPERAND_TYPE_MEMORY_ACCESS, {SpvMemoryAccessMaskNone}}); + } + } + } + }); + } + + UpgradeMemoryAndImages(); + UpgradeAtomics(); +} + +void UpgradeMemoryModel::UpgradeMemoryAndImages() { + for (auto& func : *get_module()) { + func.ForEachInst([this](Instruction* inst) { + bool is_coherent = false; + bool is_volatile = false; + bool src_coherent = false; + bool src_volatile = false; + bool dst_coherent = false; + bool dst_volatile = false; + uint32_t start_operand = 0u; + SpvScope scope = SpvScopeQueueFamilyKHR; + SpvScope src_scope = SpvScopeQueueFamilyKHR; + SpvScope dst_scope = SpvScopeQueueFamilyKHR; + switch (inst->opcode()) { + case SpvOpLoad: + case SpvOpStore: + std::tie(is_coherent, is_volatile, scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0u)); + break; + case SpvOpImageRead: + case SpvOpImageSparseRead: + case SpvOpImageWrite: + std::tie(is_coherent, is_volatile, scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0u)); + break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + std::tie(dst_coherent, dst_volatile, dst_scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0u)); + std::tie(src_coherent, src_volatile, src_scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(1u)); + break; + default: + break; + } + + switch (inst->opcode()) { + case SpvOpLoad: + UpgradeFlags(inst, 1u, is_coherent, is_volatile, kVisibility, + kMemory); + break; + case SpvOpStore: + UpgradeFlags(inst, 2u, is_coherent, is_volatile, kAvailability, + kMemory); + break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u; + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // There are guaranteed to be two memory access operands at this + // point so treat source and target separately. + uint32_t num_access_words = MemoryAccessNumWords( + inst->GetSingleWordInOperand(start_operand)); + UpgradeFlags(inst, start_operand, dst_coherent, dst_volatile, + kAvailability, kMemory); + UpgradeFlags(inst, start_operand + num_access_words, src_coherent, + src_volatile, kVisibility, kMemory); + } else { + UpgradeFlags(inst, start_operand, dst_coherent, dst_volatile, + kAvailability, kMemory); + UpgradeFlags(inst, start_operand, src_coherent, src_volatile, + kVisibility, kMemory); + } + break; + case SpvOpImageRead: + case SpvOpImageSparseRead: + UpgradeFlags(inst, 2u, is_coherent, is_volatile, kVisibility, kImage); + break; + case SpvOpImageWrite: + UpgradeFlags(inst, 3u, is_coherent, is_volatile, kAvailability, + kImage); + break; + default: + break; + } + + // |is_coherent| is never used for the same instructions as + // |src_coherent| and |dst_coherent|. + if (is_coherent) { + inst->AddOperand( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(scope)}}); + } + if (get_module()->version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // There are two memory access operands. The first is for the target and + // the second is for the source. + if (dst_coherent || src_coherent) { + start_operand = inst->opcode() == SpvOpCopyMemory ? 2u : 3u; + std::vector new_operands; + uint32_t num_access_words = + MemoryAccessNumWords(inst->GetSingleWordInOperand(start_operand)); + // The flags were already updated so subtract if we're adding a + // scope. + if (dst_coherent) --num_access_words; + for (uint32_t i = 0; i < start_operand + num_access_words; ++i) { + new_operands.push_back(inst->GetInOperand(i)); + } + // Add the target scope if necessary. + if (dst_coherent) { + new_operands.push_back( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}}); + } + // Copy the remaining current operands. + for (uint32_t i = start_operand + num_access_words; + i < inst->NumInOperands(); ++i) { + new_operands.push_back(inst->GetInOperand(i)); + } + // Add the source scope if necessary. + if (src_coherent) { + new_operands.push_back( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}}); + } + inst->SetInOperands(std::move(new_operands)); + } + } else { + // According to SPV_KHR_vulkan_memory_model, if both available and + // visible flags are used the first scope operand is for availability + // (writes) and the second is for visibility (reads). + if (dst_coherent) { + inst->AddOperand( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(dst_scope)}}); + } + if (src_coherent) { + inst->AddOperand( + {SPV_OPERAND_TYPE_SCOPE_ID, {GetScopeConstant(src_scope)}}); + } + } + }); + } +} + +void UpgradeMemoryModel::UpgradeAtomics() { + for (auto& func : *get_module()) { + func.ForEachInst([this](Instruction* inst) { + if (spvOpcodeIsAtomicOp(inst->opcode())) { + bool unused_coherent = false; + bool is_volatile = false; + SpvScope unused_scope = SpvScopeQueueFamilyKHR; + std::tie(unused_coherent, is_volatile, unused_scope) = + GetInstructionAttributes(inst->GetSingleWordInOperand(0)); + + UpgradeSemantics(inst, 2u, is_volatile); + if (inst->opcode() == SpvOpAtomicCompareExchange || + inst->opcode() == SpvOpAtomicCompareExchangeWeak) { + UpgradeSemantics(inst, 3u, is_volatile); + } + } + }); + } +} + +void UpgradeMemoryModel::UpgradeSemantics(Instruction* inst, + uint32_t in_operand, + bool is_volatile) { + if (!is_volatile) return; + + uint32_t semantics_id = inst->GetSingleWordInOperand(in_operand); + const analysis::Constant* constant = + context()->get_constant_mgr()->FindDeclaredConstant(semantics_id); + const analysis::Integer* type = constant->type()->AsInteger(); + assert(type && type->width() == 32); + uint32_t value = 0; + if (type->IsSigned()) { + value = static_cast(constant->GetS32()); + } else { + value = constant->GetU32(); + } + + value |= SpvMemorySemanticsVolatileMask; + auto new_constant = context()->get_constant_mgr()->GetConstant(type, {value}); + auto new_semantics = + context()->get_constant_mgr()->GetDefiningInstruction(new_constant); + inst->SetInOperand(in_operand, {new_semantics->result_id()}); +} + +std::tuple UpgradeMemoryModel::GetInstructionAttributes( + uint32_t id) { + // |id| is a pointer used in a memory/image instruction. Need to determine if + // that pointer points to volatile or coherent memory. Workgroup storage + // class is implicitly coherent and cannot be decorated with volatile, so + // short circuit that case. + Instruction* inst = context()->get_def_use_mgr()->GetDef(id); + analysis::Type* type = context()->get_type_mgr()->GetType(inst->type_id()); + if (type->AsPointer() && + type->AsPointer()->storage_class() == SpvStorageClassWorkgroup) { + return std::make_tuple(true, false, SpvScopeWorkgroup); + } + + bool is_coherent = false; + bool is_volatile = false; + std::unordered_set visited; + std::tie(is_coherent, is_volatile) = + TraceInstruction(context()->get_def_use_mgr()->GetDef(id), + std::vector(), &visited); + + return std::make_tuple(is_coherent, is_volatile, SpvScopeQueueFamilyKHR); +} + +std::pair UpgradeMemoryModel::TraceInstruction( + Instruction* inst, std::vector indices, + std::unordered_set* visited) { + auto iter = cache_.find(std::make_pair(inst->result_id(), indices)); + if (iter != cache_.end()) { + return iter->second; + } + + if (!visited->insert(inst->result_id()).second) { + return std::make_pair(false, false); + } + + // Initialize the cache before |indices| is (potentially) modified. + auto& cached_result = cache_[std::make_pair(inst->result_id(), indices)]; + cached_result.first = false; + cached_result.second = false; + + bool is_coherent = false; + bool is_volatile = false; + switch (inst->opcode()) { + case SpvOpVariable: + case SpvOpFunctionParameter: + is_coherent |= HasDecoration(inst, 0, SpvDecorationCoherent); + is_volatile |= HasDecoration(inst, 0, SpvDecorationVolatile); + if (!is_coherent || !is_volatile) { + bool type_coherent = false; + bool type_volatile = false; + std::tie(type_coherent, type_volatile) = + CheckType(inst->type_id(), indices); + is_coherent |= type_coherent; + is_volatile |= type_volatile; + } + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + // Store indices in reverse order. + for (uint32_t i = inst->NumInOperands() - 1; i > 0; --i) { + indices.push_back(inst->GetSingleWordInOperand(i)); + } + break; + case SpvOpPtrAccessChain: + // Store indices in reverse order. Skip the |Element| operand. + for (uint32_t i = inst->NumInOperands() - 1; i > 1; --i) { + indices.push_back(inst->GetSingleWordInOperand(i)); + } + break; + default: + break; + } + + // No point searching further. + if (is_coherent && is_volatile) { + cached_result.first = true; + cached_result.second = true; + return std::make_pair(true, true); + } + + // Variables and function parameters are sources. Continue searching until we + // reach them. + if (inst->opcode() != SpvOpVariable && + inst->opcode() != SpvOpFunctionParameter) { + inst->ForEachInId([this, &is_coherent, &is_volatile, &indices, + &visited](const uint32_t* id_ptr) { + Instruction* op_inst = context()->get_def_use_mgr()->GetDef(*id_ptr); + const analysis::Type* type = + context()->get_type_mgr()->GetType(op_inst->type_id()); + if (type && + (type->AsPointer() || type->AsImage() || type->AsSampledImage())) { + bool operand_coherent = false; + bool operand_volatile = false; + std::tie(operand_coherent, operand_volatile) = + TraceInstruction(op_inst, indices, visited); + is_coherent |= operand_coherent; + is_volatile |= operand_volatile; + } + }); + } + + cached_result.first = is_coherent; + cached_result.second = is_volatile; + return std::make_pair(is_coherent, is_volatile); +} + +std::pair UpgradeMemoryModel::CheckType( + uint32_t type_id, const std::vector& indices) { + bool is_coherent = false; + bool is_volatile = false; + Instruction* type_inst = context()->get_def_use_mgr()->GetDef(type_id); + assert(type_inst->opcode() == SpvOpTypePointer); + Instruction* element_inst = context()->get_def_use_mgr()->GetDef( + type_inst->GetSingleWordInOperand(1u)); + for (int i = (int)indices.size() - 1; i >= 0; --i) { + if (is_coherent && is_volatile) break; + + if (element_inst->opcode() == SpvOpTypePointer) { + element_inst = context()->get_def_use_mgr()->GetDef( + element_inst->GetSingleWordInOperand(1u)); + } else if (element_inst->opcode() == SpvOpTypeStruct) { + uint32_t index = indices.at(i); + Instruction* index_inst = context()->get_def_use_mgr()->GetDef(index); + assert(index_inst->opcode() == SpvOpConstant); + uint64_t value = GetIndexValue(index_inst); + is_coherent |= HasDecoration(element_inst, static_cast(value), + SpvDecorationCoherent); + is_volatile |= HasDecoration(element_inst, static_cast(value), + SpvDecorationVolatile); + element_inst = context()->get_def_use_mgr()->GetDef( + element_inst->GetSingleWordInOperand(static_cast(value))); + } else { + assert(spvOpcodeIsComposite(element_inst->opcode())); + element_inst = context()->get_def_use_mgr()->GetDef( + element_inst->GetSingleWordInOperand(0u)); + } + } + + if (!is_coherent || !is_volatile) { + bool remaining_coherent = false; + bool remaining_volatile = false; + std::tie(remaining_coherent, remaining_volatile) = + CheckAllTypes(element_inst); + is_coherent |= remaining_coherent; + is_volatile |= remaining_volatile; + } + + return std::make_pair(is_coherent, is_volatile); +} + +std::pair UpgradeMemoryModel::CheckAllTypes( + const Instruction* inst) { + std::unordered_set visited; + std::vector stack; + stack.push_back(inst); + + bool is_coherent = false; + bool is_volatile = false; + while (!stack.empty()) { + const Instruction* def = stack.back(); + stack.pop_back(); + + if (!visited.insert(def).second) continue; + + if (def->opcode() == SpvOpTypeStruct) { + // Any member decorated with coherent and/or volatile is enough to have + // the related operation be flagged as coherent and/or volatile. + is_coherent |= HasDecoration(def, std::numeric_limits::max(), + SpvDecorationCoherent); + is_volatile |= HasDecoration(def, std::numeric_limits::max(), + SpvDecorationVolatile); + if (is_coherent && is_volatile) + return std::make_pair(is_coherent, is_volatile); + + // Check the subtypes. + for (uint32_t i = 0; i < def->NumInOperands(); ++i) { + stack.push_back(context()->get_def_use_mgr()->GetDef( + def->GetSingleWordInOperand(i))); + } + } else if (spvOpcodeIsComposite(def->opcode())) { + stack.push_back(context()->get_def_use_mgr()->GetDef( + def->GetSingleWordInOperand(0u))); + } else if (def->opcode() == SpvOpTypePointer) { + stack.push_back(context()->get_def_use_mgr()->GetDef( + def->GetSingleWordInOperand(1u))); + } + } + + return std::make_pair(is_coherent, is_volatile); +} + +uint64_t UpgradeMemoryModel::GetIndexValue(Instruction* index_inst) { + const analysis::Constant* index_constant = + context()->get_constant_mgr()->GetConstantFromInst(index_inst); + assert(index_constant->AsIntConstant()); + if (index_constant->type()->AsInteger()->IsSigned()) { + if (index_constant->type()->AsInteger()->width() == 32) { + return index_constant->GetS32(); + } else { + return index_constant->GetS64(); + } + } else { + if (index_constant->type()->AsInteger()->width() == 32) { + return index_constant->GetU32(); + } else { + return index_constant->GetU64(); + } + } +} + +bool UpgradeMemoryModel::HasDecoration(const Instruction* inst, uint32_t value, + SpvDecoration decoration) { + // If the iteration was terminated early then an appropriate decoration was + // found. + return !context()->get_decoration_mgr()->WhileEachDecoration( + inst->result_id(), decoration, [value](const Instruction& i) { + if (i.opcode() == SpvOpDecorate || i.opcode() == SpvOpDecorateId) { + return false; + } else if (i.opcode() == SpvOpMemberDecorate) { + if (value == i.GetSingleWordInOperand(1u) || + value == std::numeric_limits::max()) + return false; + } + + return true; + }); +} + +void UpgradeMemoryModel::UpgradeFlags(Instruction* inst, uint32_t in_operand, + bool is_coherent, bool is_volatile, + OperationType operation_type, + InstructionType inst_type) { + if (!is_coherent && !is_volatile) return; + + uint32_t flags = 0; + if (inst->NumInOperands() > in_operand) { + flags |= inst->GetSingleWordInOperand(in_operand); + } + if (is_coherent) { + if (inst_type == kMemory) { + flags |= SpvMemoryAccessNonPrivatePointerKHRMask; + if (operation_type == kVisibility) { + flags |= SpvMemoryAccessMakePointerVisibleKHRMask; + } else { + flags |= SpvMemoryAccessMakePointerAvailableKHRMask; + } + } else { + flags |= SpvImageOperandsNonPrivateTexelKHRMask; + if (operation_type == kVisibility) { + flags |= SpvImageOperandsMakeTexelVisibleKHRMask; + } else { + flags |= SpvImageOperandsMakeTexelAvailableKHRMask; + } + } + } + + if (is_volatile) { + if (inst_type == kMemory) { + flags |= SpvMemoryAccessVolatileMask; + } else { + flags |= SpvImageOperandsVolatileTexelKHRMask; + } + } + + if (inst->NumInOperands() > in_operand) { + inst->SetInOperand(in_operand, {flags}); + } else if (inst_type == kMemory) { + inst->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, {flags}}); + } else { + inst->AddOperand({SPV_OPERAND_TYPE_OPTIONAL_IMAGE, {flags}}); + } +} + +uint32_t UpgradeMemoryModel::GetScopeConstant(SpvScope scope) { + analysis::Integer int_ty(32, false); + uint32_t int_id = context()->get_type_mgr()->GetTypeInstruction(&int_ty); + const analysis::Constant* constant = + context()->get_constant_mgr()->GetConstant( + context()->get_type_mgr()->GetType(int_id), + {static_cast(scope)}); + return context() + ->get_constant_mgr() + ->GetDefiningInstruction(constant) + ->result_id(); +} + +void UpgradeMemoryModel::CleanupDecorations() { + // All of the volatile and coherent decorations have been dealt with, so now + // we can just remove them. + get_module()->ForEachInst([this](Instruction* inst) { + if (inst->result_id() != 0) { + context()->get_decoration_mgr()->RemoveDecorationsFrom( + inst->result_id(), [](const Instruction& dec) { + switch (dec.opcode()) { + case SpvOpDecorate: + case SpvOpDecorateId: + if (dec.GetSingleWordInOperand(1u) == SpvDecorationCoherent || + dec.GetSingleWordInOperand(1u) == SpvDecorationVolatile) + return true; + break; + case SpvOpMemberDecorate: + if (dec.GetSingleWordInOperand(2u) == SpvDecorationCoherent || + dec.GetSingleWordInOperand(2u) == SpvDecorationVolatile) + return true; + break; + default: + break; + } + return false; + }); + } + }); +} + +void UpgradeMemoryModel::UpgradeBarriers() { + std::vector barriers; + // Collects all the control barriers in |function|. Returns true if the + // function operates on the Output storage class. + ProcessFunction CollectBarriers = [this, &barriers](Function* function) { + bool operates_on_output = false; + for (auto& block : *function) { + block.ForEachInst([this, &barriers, + &operates_on_output](Instruction* inst) { + if (inst->opcode() == SpvOpControlBarrier) { + barriers.push_back(inst); + } else if (!operates_on_output) { + // This instruction operates on output storage class if it is a + // pointer to output type or any input operand is a pointer to output + // type. + analysis::Type* type = + context()->get_type_mgr()->GetType(inst->type_id()); + if (type && type->AsPointer() && + type->AsPointer()->storage_class() == SpvStorageClassOutput) { + operates_on_output = true; + return; + } + inst->ForEachInId([this, &operates_on_output](uint32_t* id_ptr) { + Instruction* op_inst = + context()->get_def_use_mgr()->GetDef(*id_ptr); + analysis::Type* op_type = + context()->get_type_mgr()->GetType(op_inst->type_id()); + if (op_type && op_type->AsPointer() && + op_type->AsPointer()->storage_class() == SpvStorageClassOutput) + operates_on_output = true; + }); + } + }); + } + return operates_on_output; + }; + + std::queue roots; + for (auto& e : get_module()->entry_points()) + if (e.GetSingleWordInOperand(0u) == SpvExecutionModelTessellationControl) { + roots.push(e.GetSingleWordInOperand(1u)); + if (context()->ProcessCallTreeFromRoots(CollectBarriers, &roots)) { + for (auto barrier : barriers) { + // Add OutputMemoryKHR to the semantics of the barriers. + uint32_t semantics_id = barrier->GetSingleWordInOperand(2u); + Instruction* semantics_inst = + context()->get_def_use_mgr()->GetDef(semantics_id); + analysis::Type* semantics_type = + context()->get_type_mgr()->GetType(semantics_inst->type_id()); + uint64_t semantics_value = GetIndexValue(semantics_inst); + const analysis::Constant* constant = + context()->get_constant_mgr()->GetConstant( + semantics_type, {static_cast(semantics_value) | + SpvMemorySemanticsOutputMemoryKHRMask}); + barrier->SetInOperand(2u, {context() + ->get_constant_mgr() + ->GetDefiningInstruction(constant) + ->result_id()}); + } + } + barriers.clear(); + } +} + +void UpgradeMemoryModel::UpgradeMemoryScope() { + get_module()->ForEachInst([this](Instruction* inst) { + // Don't need to handle all the operations that take a scope. + // * Group operations can only be subgroup + // * Non-uniform can only be workgroup or subgroup + // * Named barriers are not supported by Vulkan + // * Workgroup ops (e.g. async_copy) have at most workgroup scope. + if (spvOpcodeIsAtomicOp(inst->opcode())) { + if (IsDeviceScope(inst->GetSingleWordInOperand(1))) { + inst->SetInOperand(1, {GetScopeConstant(SpvScopeQueueFamilyKHR)}); + } + } else if (inst->opcode() == SpvOpControlBarrier) { + if (IsDeviceScope(inst->GetSingleWordInOperand(1))) { + inst->SetInOperand(1, {GetScopeConstant(SpvScopeQueueFamilyKHR)}); + } + } else if (inst->opcode() == SpvOpMemoryBarrier) { + if (IsDeviceScope(inst->GetSingleWordInOperand(0))) { + inst->SetInOperand(0, {GetScopeConstant(SpvScopeQueueFamilyKHR)}); + } + } + }); +} + +bool UpgradeMemoryModel::IsDeviceScope(uint32_t scope_id) { + const analysis::Constant* constant = + context()->get_constant_mgr()->FindDeclaredConstant(scope_id); + assert(constant && "Memory scope must be a constant"); + + const analysis::Integer* type = constant->type()->AsInteger(); + assert(type); + assert(type->width() == 32 || type->width() == 64); + if (type->width() == 32) { + if (type->IsSigned()) + return static_cast(constant->GetS32()) == SpvScopeDevice; + else + return static_cast(constant->GetU32()) == SpvScopeDevice; + } else { + if (type->IsSigned()) + return static_cast(constant->GetS64()) == SpvScopeDevice; + else + return static_cast(constant->GetU64()) == SpvScopeDevice; + } + + assert(false); + return false; +} + +void UpgradeMemoryModel::UpgradeExtInst(Instruction* ext_inst) { + const bool is_modf = ext_inst->GetSingleWordInOperand(1u) == GLSLstd450Modf; + auto ptr_id = ext_inst->GetSingleWordInOperand(3u); + auto ptr_type_id = get_def_use_mgr()->GetDef(ptr_id)->type_id(); + auto pointee_type_id = + get_def_use_mgr()->GetDef(ptr_type_id)->GetSingleWordInOperand(1u); + auto element_type_id = ext_inst->type_id(); + std::vector element_types(2); + element_types[0] = context()->get_type_mgr()->GetType(element_type_id); + element_types[1] = context()->get_type_mgr()->GetType(pointee_type_id); + analysis::Struct struct_type(element_types); + uint32_t struct_id = + context()->get_type_mgr()->GetTypeInstruction(&struct_type); + // Change the operation + GLSLstd450 new_op = is_modf ? GLSLstd450ModfStruct : GLSLstd450FrexpStruct; + ext_inst->SetOperand(3u, {static_cast(new_op)}); + // Remove the pointer argument + ext_inst->RemoveOperand(5u); + // Set the type id to the new struct. + ext_inst->SetResultType(struct_id); + + // The result is now a struct of the original result. The zero'th element is + // old result and should replace the old result. The one'th element needs to + // be stored via a new instruction. + auto where = ext_inst->NextNode(); + InstructionBuilder builder( + context(), where, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + auto extract_0 = + builder.AddCompositeExtract(element_type_id, ext_inst->result_id(), {0}); + context()->ReplaceAllUsesWith(ext_inst->result_id(), extract_0->result_id()); + // The extract's input was just changed to itself, so fix that. + extract_0->SetInOperand(0u, {ext_inst->result_id()}); + auto extract_1 = + builder.AddCompositeExtract(pointee_type_id, ext_inst->result_id(), {1}); + builder.AddStore(ptr_id, extract_1->result_id()); +} + +uint32_t UpgradeMemoryModel::MemoryAccessNumWords(uint32_t mask) { + uint32_t result = 1; + if (mask & SpvMemoryAccessAlignedMask) ++result; + if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++result; + if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) ++result; + return result; +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/upgrade_memory_model.h b/third_party/spirv-tools/source/opt/upgrade_memory_model.h new file mode 100644 index 0000000..f75304e --- /dev/null +++ b/third_party/spirv-tools/source/opt/upgrade_memory_model.h @@ -0,0 +1,150 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ +#define LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ + +#include +#include + +#include "pass.h" + +namespace spvtools { +namespace opt { + +// Hashing functor for the memoized result store. +struct CacheHash { + size_t operator()( + const std::pair>& item) const { + std::u32string to_hash; + to_hash.push_back(item.first); + for (auto i : item.second) to_hash.push_back(i); + return std::hash()(to_hash); + } +}; + +// Upgrades the memory model from Logical GLSL450 to Logical VulkanKHR. +// +// This pass remove deprecated decorations (Volatile and Coherent) and replaces +// them with new flags on individual instructions. It adds the Output storage +// class semantic to control barriers in tessellation control shaders that have +// an access to Output memory. +class UpgradeMemoryModel : public Pass { + public: + const char* name() const override { return "upgrade-memory-model"; } + Status Process() override; + + private: + // Used to indicate whether the operation performs an availability or + // visibility operation. + enum OperationType { kVisibility, kAvailability }; + + // Used to indicate whether the instruction is a memory or image instruction. + enum InstructionType { kMemory, kImage }; + + // Modifies the OpMemoryModel to use VulkanKHR. Adds the Vulkan memory model + // capability and extension. + void UpgradeMemoryModelInstruction(); + + // Upgrades memory, image and atomic instructions. + // Memory and image instructions convert coherent and volatile decorations + // into flags on the instruction. + // Atomic memory semantics convert volatile decoration into flags on the + // instruction. + void UpgradeInstructions(); + + // Upgrades memory and image operands for instructions that have them. + void UpgradeMemoryAndImages(); + + // Adds the volatile memory semantic if necessary. + void UpgradeAtomics(); + + // Returns whether |id| is coherent and/or volatile. + std::tuple GetInstructionAttributes(uint32_t id); + + // Traces |inst| to determine if it is coherent and/or volatile. + // |indices| tracks the access chain indices seen so far. + std::pair TraceInstruction(Instruction* inst, + std::vector indices, + std::unordered_set* visited); + + // Return true if |inst| is decorated with |decoration|. + // If |inst| is decorated by member decorations then either |value| must + // match the index or |value| must be a maximum allowable value. The max + // value allows any element to match. + bool HasDecoration(const Instruction* inst, uint32_t value, + SpvDecoration decoration); + + // Returns whether |type_id| indexed via |indices| is coherent and/or + // volatile. + std::pair CheckType(uint32_t type_id, + const std::vector& indices); + + // Returns whether any type/element under |inst| is coherent and/or volatile. + std::pair CheckAllTypes(const Instruction* inst); + + // Modifies the flags of |inst| to include the new flags for the Vulkan + // memory model. |operation_type| indicates whether flags should use + // MakeVisible or MakeAvailable variants. |inst_type| indicates whether the + // Pointer or Texel variants of flags should be used. + void UpgradeFlags(Instruction* inst, uint32_t in_operand, bool is_coherent, + bool is_volatile, OperationType operation_type, + InstructionType inst_type); + + // Modifies the semantics at |in_operand| of |inst| to include the volatile + // bit if |is_volatile| is true. + void UpgradeSemantics(Instruction* inst, uint32_t in_operand, + bool is_volatile); + + // Returns the result id for a constant for |scope|. + uint32_t GetScopeConstant(SpvScope scope); + + // Returns the value of |index_inst|. |index_inst| must be an OpConstant of + // integer type.g + uint64_t GetIndexValue(Instruction* index_inst); + + // Removes coherent and volatile decorations. + void CleanupDecorations(); + + // For all tessellation control entry points, if there is an operation on + // Output storage class, then all barriers are modified to include the + // OutputMemoryKHR semantic. + void UpgradeBarriers(); + + // If the Vulkan memory model is specified, device scope actually means + // device scope. The memory scope must be modified to be QueueFamilyKHR + // scope. + void UpgradeMemoryScope(); + + // Returns true if |scope_id| is SpvScopeDevice. + bool IsDeviceScope(uint32_t scope_id); + + // Upgrades GLSL.std.450 modf and frexp. Both instructions are replaced with + // their struct versions. New extracts and a store are added in order to + // facilitate adding memory model flags. + void UpgradeExtInst(Instruction* modf); + + // Returns the number of words taken up by a memory access argument and its + // implied operands. + uint32_t MemoryAccessNumWords(uint32_t mask); + + // Caches the result of TraceInstruction. For a given result id and set of + // indices, stores whether that combination is coherent and/or volatile. + std::unordered_map>, + std::pair, CacheHash> + cache_; +}; +} // namespace opt +} // namespace spvtools +#endif // LIBSPIRV_OPT_UPGRADE_MEMORY_MODEL_H_ diff --git a/third_party/spirv-tools/source/opt/value_number_table.cpp b/third_party/spirv-tools/source/opt/value_number_table.cpp new file mode 100644 index 0000000..32d6de9 --- /dev/null +++ b/third_party/spirv-tools/source/opt/value_number_table.cpp @@ -0,0 +1,240 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/value_number_table.h" + +#include + +#include "source/opt/cfg.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace opt { + +uint32_t ValueNumberTable::GetValueNumber(Instruction* inst) const { + assert(inst->result_id() != 0 && + "inst must have a result id to get a value number."); + + // Check if this instruction already has a value. + auto result_id_to_val = id_to_value_.find(inst->result_id()); + if (result_id_to_val != id_to_value_.end()) { + return result_id_to_val->second; + } + return 0; +} + +uint32_t ValueNumberTable::GetValueNumber(uint32_t id) const { + return GetValueNumber(context()->get_def_use_mgr()->GetDef(id)); +} + +uint32_t ValueNumberTable::AssignValueNumber(Instruction* inst) { + // If it already has a value return that. + uint32_t value = GetValueNumber(inst); + if (value != 0) { + return value; + } + + // If the instruction has other side effects, then it must + // have its own value number. + // OpSampledImage and OpImage must remain in the same basic block in which + // they are used, because of this we will assign each one it own value number. + if (!context()->IsCombinatorInstruction(inst) && + !inst->IsOpenCL100DebugInstr()) { + value = TakeNextValueNumber(); + id_to_value_[inst->result_id()] = value; + return value; + } + + switch (inst->opcode()) { + case SpvOpSampledImage: + case SpvOpImage: + case SpvOpVariable: + value = TakeNextValueNumber(); + id_to_value_[inst->result_id()] = value; + return value; + default: + break; + } + + // If it is a load from memory that can be modified, we have to assume the + // memory has been modified, so we give it a new value number. + // + // Note that this test will also handle volatile loads because they are not + // read only. However, if this is ever relaxed because we analyze stores, we + // will have to add a new case for volatile loads. + if (inst->IsLoad() && !inst->IsReadOnlyLoad()) { + value = TakeNextValueNumber(); + id_to_value_[inst->result_id()] = value; + return value; + } + + analysis::DecorationManager* dec_mgr = context()->get_decoration_mgr(); + + // When we copy an object, the value numbers should be the same. + if (inst->opcode() == SpvOpCopyObject && + dec_mgr->HaveTheSameDecorations(inst->result_id(), + inst->GetSingleWordInOperand(0))) { + value = GetValueNumber(inst->GetSingleWordInOperand(0)); + if (value != 0) { + id_to_value_[inst->result_id()] = value; + return value; + } + } + + // Phi nodes are a type of copy. If all of the inputs have the same value + // number, then we can assign the result of the phi the same value number. + if (inst->opcode() == SpvOpPhi && inst->NumInOperands() > 0 && + dec_mgr->HaveTheSameDecorations(inst->result_id(), + inst->GetSingleWordInOperand(0))) { + value = GetValueNumber(inst->GetSingleWordInOperand(0)); + if (value != 0) { + for (uint32_t op = 2; op < inst->NumInOperands(); op += 2) { + if (value != GetValueNumber(inst->GetSingleWordInOperand(op))) { + value = 0; + break; + } + } + if (value != 0) { + id_to_value_[inst->result_id()] = value; + return value; + } + } + } + + // Replace all of the operands by their value number. The sign bit will be + // set to distinguish between an id and a value number. + Instruction value_ins(context(), inst->opcode(), inst->type_id(), + inst->result_id(), {}); + for (uint32_t o = 0; o < inst->NumInOperands(); ++o) { + const Operand& op = inst->GetInOperand(o); + if (spvIsIdType(op.type)) { + uint32_t id_value = op.words[0]; + auto use_id_to_val = id_to_value_.find(id_value); + if (use_id_to_val != id_to_value_.end()) { + id_value = (1 << 31) | use_id_to_val->second; + } + value_ins.AddOperand(Operand(op.type, {id_value})); + } else { + value_ins.AddOperand(Operand(op.type, op.words)); + } + } + + // TODO: Implement a normal form for opcodes that commute like integer + // addition. This will let us know that a+b is the same value as b+a. + + // Otherwise, we check if this value has been computed before. + auto value_iterator = instruction_to_value_.find(value_ins); + if (value_iterator != instruction_to_value_.end()) { + value = id_to_value_[value_iterator->first.result_id()]; + id_to_value_[inst->result_id()] = value; + return value; + } + + // If not, assign it a new value number. + value = TakeNextValueNumber(); + id_to_value_[inst->result_id()] = value; + instruction_to_value_[value_ins] = value; + return value; +} + +void ValueNumberTable::BuildDominatorTreeValueNumberTable() { + // First value number the headers. + for (auto& inst : context()->annotations()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (auto& inst : context()->capabilities()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (auto& inst : context()->types_values()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (auto& inst : context()->module()->ext_inst_imports()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (auto& inst : context()->module()->ext_inst_debuginfo()) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + + for (Function& func : *context()->module()) { + // For best results we want to traverse the code in reverse post order. + // This happens naturally because of the forward referencing rules. + for (BasicBlock& block : func) { + for (Instruction& inst : block) { + if (inst.result_id() != 0) { + AssignValueNumber(&inst); + } + } + } + } +} + +bool ComputeSameValue::operator()(const Instruction& lhs, + const Instruction& rhs) const { + if (lhs.result_id() == 0 || rhs.result_id() == 0) { + return false; + } + + if (lhs.opcode() != rhs.opcode()) { + return false; + } + + if (lhs.type_id() != rhs.type_id()) { + return false; + } + + if (lhs.NumInOperands() != rhs.NumInOperands()) { + return false; + } + + for (uint32_t i = 0; i < lhs.NumInOperands(); ++i) { + if (lhs.GetInOperand(i) != rhs.GetInOperand(i)) { + return false; + } + } + + return lhs.context()->get_decoration_mgr()->HaveTheSameDecorations( + lhs.result_id(), rhs.result_id()); +} + +std::size_t ValueTableHash::operator()(const Instruction& inst) const { + // We hash the opcode and in-operands, not the result, because we want + // instructions that are the same except for the result to hash to the + // same value. + std::u32string h; + h.push_back(inst.opcode()); + h.push_back(inst.type_id()); + for (uint32_t i = 0; i < inst.NumInOperands(); ++i) { + const auto& opnd = inst.GetInOperand(i); + for (uint32_t word : opnd.words) { + h.push_back(word); + } + } + return std::hash()(h); +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/value_number_table.h b/third_party/spirv-tools/source/opt/value_number_table.h new file mode 100644 index 0000000..39129ff --- /dev/null +++ b/third_party/spirv-tools/source/opt/value_number_table.h @@ -0,0 +1,91 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_VALUE_NUMBER_TABLE_H_ +#define SOURCE_OPT_VALUE_NUMBER_TABLE_H_ + +#include +#include + +#include "source/opt/instruction.h" + +namespace spvtools { +namespace opt { + +class IRContext; + +// Returns true if the two instructions compute the same value. Used by the +// value number table to compare two instructions. +class ComputeSameValue { + public: + bool operator()(const Instruction& lhs, const Instruction& rhs) const; +}; + +// The hash function used in the value number table. +class ValueTableHash { + public: + std::size_t operator()(const Instruction& inst) const; +}; + +// This class implements the value number analysis. It is using a hash-based +// approach to value numbering. It is essentially doing dominator-tree value +// numbering described in +// +// Preston Briggs, Keith D. Cooper, and L. Taylor Simpson. 1997. Value +// numbering. Softw. Pract. Exper. 27, 6 (June 1997), 701-724. +// https://www.cs.rice.edu/~keith/Promo/CRPC-TR94517.pdf.gz +// +// The main difference is that because we do not perform redundancy elimination +// as we build the value number table, we do not have to deal with cleaning up +// the scope. +class ValueNumberTable { + public: + ValueNumberTable(IRContext* ctx) : context_(ctx), next_value_number_(1) { + BuildDominatorTreeValueNumberTable(); + } + + // Returns the value number of the value computed by |inst|. |inst| must have + // a result id that will hold the computed value. If no value number has been + // assigned to the result id, then the return value is 0. + uint32_t GetValueNumber(Instruction* inst) const; + + // Returns the value number of the value contain in |id|. Returns 0 if it + // has not been assigned a value number. + uint32_t GetValueNumber(uint32_t id) const; + + IRContext* context() const { return context_; } + + private: + // Assigns a value number to every result id in the module. + void BuildDominatorTreeValueNumberTable(); + + // Returns the new value number. + uint32_t TakeNextValueNumber() { return next_value_number_++; } + + // Assigns a new value number to the result of |inst| if it does not already + // have one. Return the value number for |inst|. |inst| must have a result + // id. + uint32_t AssignValueNumber(Instruction* inst); + + std::unordered_map + instruction_to_value_; + std::unordered_map id_to_value_; + IRContext* context_; + uint32_t next_value_number_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_VALUE_NUMBER_TABLE_H_ diff --git a/third_party/spirv-tools/source/opt/vector_dce.cpp b/third_party/spirv-tools/source/opt/vector_dce.cpp new file mode 100644 index 0000000..14a55c1 --- /dev/null +++ b/third_party/spirv-tools/source/opt/vector_dce.cpp @@ -0,0 +1,419 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/vector_dce.h" + +#include + +namespace spvtools { +namespace opt { +namespace { + +const uint32_t kExtractCompositeIdInIdx = 0; +const uint32_t kInsertObjectIdInIdx = 0; +const uint32_t kInsertCompositeIdInIdx = 1; + +} // namespace + +Pass::Status VectorDCE::Process() { + bool modified = false; + for (Function& function : *get_module()) { + modified |= VectorDCEFunction(&function); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool VectorDCE::VectorDCEFunction(Function* function) { + LiveComponentMap live_components; + FindLiveComponents(function, &live_components); + return RewriteInstructions(function, live_components); +} + +void VectorDCE::FindLiveComponents(Function* function, + LiveComponentMap* live_components) { + std::vector work_list; + + // Prime the work list. We will assume that any instruction that does + // not result in a vector is live. + // + // Extending to structures and matrices is not as straight forward because of + // the nesting. We cannot simply us a bit vector to keep track of which + // components are live because of arbitrary nesting of structs. + function->ForEachInst( + [&work_list, this, live_components](Instruction* current_inst) { + if (current_inst->IsOpenCL100DebugInstr()) { + return; + } + if (!HasVectorOrScalarResult(current_inst) || + !context()->IsCombinatorInstruction(current_inst)) { + MarkUsesAsLive(current_inst, all_components_live_, live_components, + &work_list); + } + }); + + // Process the work list propagating liveness. + for (uint32_t i = 0; i < work_list.size(); i++) { + WorkListItem current_item = work_list[i]; + Instruction* current_inst = current_item.instruction; + + switch (current_inst->opcode()) { + case SpvOpCompositeExtract: + MarkExtractUseAsLive(current_inst, current_item.components, + live_components, &work_list); + break; + case SpvOpCompositeInsert: + MarkInsertUsesAsLive(current_item, live_components, &work_list); + break; + case SpvOpVectorShuffle: + MarkVectorShuffleUsesAsLive(current_item, live_components, &work_list); + break; + case SpvOpCompositeConstruct: + MarkCompositeContructUsesAsLive(current_item, live_components, + &work_list); + break; + default: + if (current_inst->IsScalarizable()) { + MarkUsesAsLive(current_inst, current_item.components, live_components, + &work_list); + } else { + MarkUsesAsLive(current_inst, all_components_live_, live_components, + &work_list); + } + break; + } + } +} + +void VectorDCE::MarkExtractUseAsLive(const Instruction* current_inst, + const utils::BitVector& live_elements, + LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + uint32_t operand_id = + current_inst->GetSingleWordInOperand(kExtractCompositeIdInIdx); + Instruction* operand_inst = def_use_mgr->GetDef(operand_id); + + if (HasVectorOrScalarResult(operand_inst)) { + WorkListItem new_item; + new_item.instruction = operand_inst; + if (current_inst->NumInOperands() < 2) { + new_item.components = live_elements; + } else { + new_item.components.Set(current_inst->GetSingleWordInOperand(1)); + } + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + } +} + +void VectorDCE::MarkInsertUsesAsLive( + const VectorDCE::WorkListItem& current_item, + LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + if (current_item.instruction->NumInOperands() > 2) { + uint32_t insert_position = + current_item.instruction->GetSingleWordInOperand(2); + + // Add the elements of the composite object that are used. + uint32_t operand_id = current_item.instruction->GetSingleWordInOperand( + kInsertCompositeIdInIdx); + Instruction* operand_inst = def_use_mgr->GetDef(operand_id); + + WorkListItem new_item; + new_item.instruction = operand_inst; + new_item.components = current_item.components; + new_item.components.Clear(insert_position); + + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + + // Add the element being inserted if it is used. + if (current_item.components.Get(insert_position)) { + uint32_t obj_operand_id = + current_item.instruction->GetSingleWordInOperand( + kInsertObjectIdInIdx); + Instruction* obj_operand_inst = def_use_mgr->GetDef(obj_operand_id); + WorkListItem new_item_for_obj; + new_item_for_obj.instruction = obj_operand_inst; + new_item_for_obj.components.Set(0); + AddItemToWorkListIfNeeded(new_item_for_obj, live_components, work_list); + } + } else { + // If there are no indices, then this is a copy of the object being + // inserted. + uint32_t object_id = + current_item.instruction->GetSingleWordInOperand(kInsertObjectIdInIdx); + Instruction* object_inst = def_use_mgr->GetDef(object_id); + + WorkListItem new_item; + new_item.instruction = object_inst; + new_item.components = current_item.components; + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + } +} + +void VectorDCE::MarkVectorShuffleUsesAsLive( + const WorkListItem& current_item, + VectorDCE::LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + WorkListItem first_operand; + first_operand.instruction = + def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(0)); + WorkListItem second_operand; + second_operand.instruction = + def_use_mgr->GetDef(current_item.instruction->GetSingleWordInOperand(1)); + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Vector* first_type = + type_mgr->GetType(first_operand.instruction->type_id())->AsVector(); + uint32_t size_of_first_operand = first_type->element_count(); + + for (uint32_t in_op = 2; in_op < current_item.instruction->NumInOperands(); + ++in_op) { + uint32_t index = current_item.instruction->GetSingleWordInOperand(in_op); + if (current_item.components.Get(in_op - 2)) { + if (index < size_of_first_operand) { + first_operand.components.Set(index); + } else { + second_operand.components.Set(index - size_of_first_operand); + } + } + } + + AddItemToWorkListIfNeeded(first_operand, live_components, work_list); + AddItemToWorkListIfNeeded(second_operand, live_components, work_list); +} + +void VectorDCE::MarkCompositeContructUsesAsLive( + VectorDCE::WorkListItem work_item, + VectorDCE::LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + + uint32_t current_component = 0; + Instruction* current_inst = work_item.instruction; + uint32_t num_in_operands = current_inst->NumInOperands(); + for (uint32_t i = 0; i < num_in_operands; ++i) { + uint32_t id = current_inst->GetSingleWordInOperand(i); + Instruction* op_inst = def_use_mgr->GetDef(id); + + if (HasScalarResult(op_inst)) { + WorkListItem new_work_item; + new_work_item.instruction = op_inst; + if (work_item.components.Get(current_component)) { + new_work_item.components.Set(0); + } + AddItemToWorkListIfNeeded(new_work_item, live_components, work_list); + current_component++; + } else { + assert(HasVectorResult(op_inst)); + WorkListItem new_work_item; + new_work_item.instruction = op_inst; + uint32_t op_vector_size = + type_mgr->GetType(op_inst->type_id())->AsVector()->element_count(); + + for (uint32_t op_vector_idx = 0; op_vector_idx < op_vector_size; + op_vector_idx++, current_component++) { + if (work_item.components.Get(current_component)) { + new_work_item.components.Set(op_vector_idx); + } + } + AddItemToWorkListIfNeeded(new_work_item, live_components, work_list); + } + } +} + +void VectorDCE::MarkUsesAsLive( + Instruction* current_inst, const utils::BitVector& live_elements, + LiveComponentMap* live_components, + std::vector* work_list) { + analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr(); + + current_inst->ForEachInId([&work_list, &live_elements, this, live_components, + def_use_mgr](uint32_t* operand_id) { + Instruction* operand_inst = def_use_mgr->GetDef(*operand_id); + + if (HasVectorResult(operand_inst)) { + WorkListItem new_item; + new_item.instruction = operand_inst; + new_item.components = live_elements; + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + } else if (HasScalarResult(operand_inst)) { + WorkListItem new_item; + new_item.instruction = operand_inst; + new_item.components.Set(0); + AddItemToWorkListIfNeeded(new_item, live_components, work_list); + } + }); +} + +bool VectorDCE::HasVectorOrScalarResult(const Instruction* inst) const { + return HasScalarResult(inst) || HasVectorResult(inst); +} + +bool VectorDCE::HasVectorResult(const Instruction* inst) const { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + if (inst->type_id() == 0) { + return false; + } + + const analysis::Type* current_type = type_mgr->GetType(inst->type_id()); + switch (current_type->kind()) { + case analysis::Type::kVector: + return true; + default: + return false; + } +} + +bool VectorDCE::HasScalarResult(const Instruction* inst) const { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + if (inst->type_id() == 0) { + return false; + } + + const analysis::Type* current_type = type_mgr->GetType(inst->type_id()); + switch (current_type->kind()) { + case analysis::Type::kBool: + case analysis::Type::kInteger: + case analysis::Type::kFloat: + return true; + default: + return false; + } +} + +bool VectorDCE::RewriteInstructions( + Function* function, const VectorDCE::LiveComponentMap& live_components) { + bool modified = false; + + // Kill DebugValue in the middle of the instruction iteration will result + // in accessing a dangling pointer. We keep dead DebugValue instructions + // in |dead_dbg_value| to kill them once after the iteration. + std::vector dead_dbg_value; + + function->ForEachInst([&modified, this, live_components, + &dead_dbg_value](Instruction* current_inst) { + if (!context()->IsCombinatorInstruction(current_inst)) { + return; + } + + auto live_component = live_components.find(current_inst->result_id()); + if (live_component == live_components.end()) { + // If this instruction is not in live_components then it does not + // produce a vector, or it is never referenced and ADCE will remove + // it. No point in trying to differentiate. + return; + } + + // If no element in the current instruction is used replace it with an + // OpUndef. + if (live_component->second.Empty()) { + modified = true; + MarkDebugValueUsesAsDead(current_inst, &dead_dbg_value); + uint32_t undef_id = this->Type2Undef(current_inst->type_id()); + context()->KillNamesAndDecorates(current_inst); + context()->ReplaceAllUsesWith(current_inst->result_id(), undef_id); + context()->KillInst(current_inst); + return; + } + + switch (current_inst->opcode()) { + case SpvOpCompositeInsert: + modified |= RewriteInsertInstruction( + current_inst, live_component->second, &dead_dbg_value); + break; + case SpvOpCompositeConstruct: + // TODO: The members that are not live can be replaced by an undef + // or constant. This will remove uses of those values, and possibly + // create opportunities for ADCE. + break; + default: + // Do nothing. + break; + } + }); + for (auto* i : dead_dbg_value) context()->KillInst(i); + return modified; +} + +bool VectorDCE::RewriteInsertInstruction( + Instruction* current_inst, const utils::BitVector& live_components, + std::vector* dead_dbg_value) { + // If the value being inserted is not live, then we can skip the insert. + + if (current_inst->NumInOperands() == 2) { + // If there are no indices, then this is the same as a copy. + context()->KillNamesAndDecorates(current_inst->result_id()); + uint32_t object_id = + current_inst->GetSingleWordInOperand(kInsertObjectIdInIdx); + context()->ReplaceAllUsesWith(current_inst->result_id(), object_id); + return true; + } + + uint32_t insert_index = current_inst->GetSingleWordInOperand(2); + if (!live_components.Get(insert_index)) { + MarkDebugValueUsesAsDead(current_inst, dead_dbg_value); + context()->KillNamesAndDecorates(current_inst->result_id()); + uint32_t composite_id = + current_inst->GetSingleWordInOperand(kInsertCompositeIdInIdx); + context()->ReplaceAllUsesWith(current_inst->result_id(), composite_id); + return true; + } + + // If the values already in the composite are not used, then replace it with + // an undef. + utils::BitVector temp = live_components; + temp.Clear(insert_index); + if (temp.Empty()) { + context()->ForgetUses(current_inst); + uint32_t undef_id = Type2Undef(current_inst->type_id()); + current_inst->SetInOperand(kInsertCompositeIdInIdx, {undef_id}); + context()->AnalyzeUses(current_inst); + return true; + } + + return false; +} + +void VectorDCE::MarkDebugValueUsesAsDead( + Instruction* composite, std::vector* dead_dbg_value) { + context()->get_def_use_mgr()->ForEachUser( + composite, [&dead_dbg_value](Instruction* use) { + if (use->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugValue) + dead_dbg_value->push_back(use); + }); +} + +void VectorDCE::AddItemToWorkListIfNeeded( + WorkListItem work_item, VectorDCE::LiveComponentMap* live_components, + std::vector* work_list) { + Instruction* current_inst = work_item.instruction; + auto it = live_components->find(current_inst->result_id()); + if (it == live_components->end()) { + live_components->emplace( + std::make_pair(current_inst->result_id(), work_item.components)); + work_list->emplace_back(work_item); + } else { + if (it->second.Or(work_item.components)) { + work_list->emplace_back(work_item); + } + } +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/vector_dce.h b/third_party/spirv-tools/source/opt/vector_dce.h new file mode 100644 index 0000000..0df9aee --- /dev/null +++ b/third_party/spirv-tools/source/opt/vector_dce.h @@ -0,0 +1,157 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_VECTOR_DCE_H_ +#define SOURCE_OPT_VECTOR_DCE_H_ + +#include +#include + +#include "source/opt/mem_pass.h" +#include "source/util/bit_vector.h" + +namespace spvtools { +namespace opt { + +class VectorDCE : public MemPass { + private: + using LiveComponentMap = std::unordered_map; + + // According to the SPEC the maximum size for a vector is 16. See the data + // rules in the universal validation rules (section 2.16.1). + enum { kMaxVectorSize = 16 }; + + struct WorkListItem { + WorkListItem() : instruction(nullptr), components(kMaxVectorSize) {} + + Instruction* instruction; + utils::BitVector components; + }; + + public: + VectorDCE() : all_components_live_(kMaxVectorSize) { + for (uint32_t i = 0; i < kMaxVectorSize; i++) { + all_components_live_.Set(i); + } + } + + const char* name() const override { return "vector-dce"; } + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | IRContext::kAnalysisCFG | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisDecorations | + IRContext::kAnalysisDominatorAnalysis | IRContext::kAnalysisNameMap | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Runs the vector dce pass on |function|. Returns true if |function| was + // modified. + bool VectorDCEFunction(Function* function); + + // Identifies the live components of the vectors that are results of + // instructions in |function|. The results are stored in |live_components|. + void FindLiveComponents(Function* function, + LiveComponentMap* live_components); + + // Rewrites instructions in |function| that are dead or partially dead. If an + // instruction does not have an entry in |live_components|, then it is not + // changed. Returns true if |function| was modified. + bool RewriteInstructions(Function* function, + const LiveComponentMap& live_components); + + // Makrs all DebugValue instructions that use |composite| for their values as + // dead instructions by putting them into |dead_dbg_value|. + void MarkDebugValueUsesAsDead(Instruction* composite, + std::vector* dead_dbg_value); + + // Rewrites the OpCompositeInsert instruction |current_inst| to avoid + // unnecessary computes given that the only components of the result that are + // live are |live_components|. + // + // If the value being inserted is not live, then the result of |current_inst| + // is replaced by the composite input to |current_inst|. + // + // If the composite input to |current_inst| is not live, then it is replaced + // by and OpUndef in |current_inst|. + bool RewriteInsertInstruction(Instruction* current_inst, + const utils::BitVector& live_components, + std::vector* dead_dbg_value); + + // Returns true if the result of |inst| is a vector or a scalar. + bool HasVectorOrScalarResult(const Instruction* inst) const; + + // Returns true if the result of |inst| is a scalar. + bool HasVectorResult(const Instruction* inst) const; + + // Returns true if the result of |inst| is a vector. + bool HasScalarResult(const Instruction* inst) const; + + // Adds |work_item| to |work_list| if it is not already live according to + // |live_components|. |live_components| is updated to indicate that + // |work_item| is now live. + void AddItemToWorkListIfNeeded(WorkListItem work_item, + LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the components |live_elements| of the uses in |current_inst| as live + // according to |live_components|. If they were not live before, then they are + // added to |work_list|. + void MarkUsesAsLive(Instruction* current_inst, + const utils::BitVector& live_elements, + LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the uses in the OpVectorShuffle instruction in |current_item| as live + // based on the live components in |current_item|. If anything becomes live + // they are added to |work_list| and |live_components| is updated + // accordingly. + void MarkVectorShuffleUsesAsLive(const WorkListItem& current_item, + VectorDCE::LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the uses in the OpCompositeInsert instruction in |current_item| as + // live based on the live components in |current_item|. If anything becomes + // live they are added to |work_list| and |live_components| is updated + // accordingly. + void MarkInsertUsesAsLive(const WorkListItem& current_item, + LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the uses in the OpCompositeExtract instruction |current_inst| as + // live. If anything becomes live they are added to |work_list| and + // |live_components| is updated accordingly. + void MarkExtractUseAsLive(const Instruction* current_inst, + const utils::BitVector& live_elements, + LiveComponentMap* live_components, + std::vector* work_list); + + // Marks the uses in the OpCompositeConstruct instruction |current_inst| as + // live. If anything becomes live they are added to |work_list| and + // |live_components| is updated accordingly. + void MarkCompositeContructUsesAsLive(WorkListItem work_item, + LiveComponentMap* live_components, + std::vector* work_list); + + // A BitVector that can always be used to say that all components of a vector + // are live. + utils::BitVector all_components_live_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_VECTOR_DCE_H_ diff --git a/third_party/spirv-tools/source/opt/workaround1209.cpp b/third_party/spirv-tools/source/opt/workaround1209.cpp new file mode 100644 index 0000000..d6e9d2c --- /dev/null +++ b/third_party/spirv-tools/source/opt/workaround1209.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/workaround1209.h" + +#include +#include +#include +#include + +namespace spvtools { +namespace opt { + +Pass::Status Workaround1209::Process() { + bool modified = false; + modified = RemoveOpUnreachableInLoops(); + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool Workaround1209::RemoveOpUnreachableInLoops() { + bool modified = false; + for (auto& func : *get_module()) { + std::list structured_order; + cfg()->ComputeStructuredOrder(&func, &*func.begin(), &structured_order); + + // Keep track of the loop merges. The top of the stack will always be the + // loop merge for the loop that immediately contains the basic block being + // processed. + std::stack loop_merges; + for (BasicBlock* bb : structured_order) { + if (!loop_merges.empty() && bb->id() == loop_merges.top()) { + loop_merges.pop(); + } + + if (bb->tail()->opcode() == SpvOpUnreachable) { + if (!loop_merges.empty()) { + // We found an OpUnreachable inside a loop. + // Replace it with an unconditional branch to the loop merge. + context()->KillInst(&*bb->tail()); + std::unique_ptr new_branch( + new Instruction(context(), SpvOpBranch, 0, 0, + {{spv_operand_type_t::SPV_OPERAND_TYPE_ID, + {loop_merges.top()}}})); + context()->AnalyzeDefUse(&*new_branch); + bb->AddInstruction(std::move(new_branch)); + modified = true; + } + } else { + if (bb->GetLoopMergeInst()) { + loop_merges.push(bb->MergeBlockIdIfAny()); + } + } + } + } + return modified; +} +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/workaround1209.h b/third_party/spirv-tools/source/opt/workaround1209.h new file mode 100644 index 0000000..9a1f88d --- /dev/null +++ b/third_party/spirv-tools/source/opt/workaround1209.h @@ -0,0 +1,41 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_WORKAROUND1209_H_ +#define SOURCE_OPT_WORKAROUND1209_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// See optimizer.hpp for documentation. +class Workaround1209 : public Pass { + public: + const char* name() const override { return "workaround-1209"; } + Status Process() override; + + private: + // There is at least one driver where an OpUnreachable found in a loop is not + // handled correctly. Workaround that by changing the OpUnreachable into a + // branch to the loop merge. + // + // Returns true if the code changed. + bool RemoveOpUnreachableInLoops(); +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_WORKAROUND1209_H_ diff --git a/third_party/spirv-tools/source/opt/wrap_opkill.cpp b/third_party/spirv-tools/source/opt/wrap_opkill.cpp new file mode 100644 index 0000000..ae1000c --- /dev/null +++ b/third_party/spirv-tools/source/opt/wrap_opkill.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/wrap_opkill.h" + +#include "ir_builder.h" + +namespace spvtools { +namespace opt { + +Pass::Status WrapOpKill::Process() { + bool modified = false; + + auto func_to_process = + context()->GetStructuredCFGAnalysis()->FindFuncsCalledFromContinue(); + for (uint32_t func_id : func_to_process) { + Function* func = context()->GetFunction(func_id); + bool successful = func->WhileEachInst([this, &modified](Instruction* inst) { + const auto opcode = inst->opcode(); + if ((opcode == SpvOpKill) || (opcode == SpvOpTerminateInvocation)) { + modified = true; + if (!ReplaceWithFunctionCall(inst)) { + return false; + } + } + return true; + }); + + if (!successful) { + return Status::Failure; + } + } + + if (opkill_function_ != nullptr) { + assert(modified && + "The function should only be generated if something was modified."); + context()->AddFunction(std::move(opkill_function_)); + } + if (opterminateinvocation_function_ != nullptr) { + assert(modified && + "The function should only be generated if something was modified."); + context()->AddFunction(std::move(opterminateinvocation_function_)); + } + return (modified ? Status::SuccessWithChange : Status::SuccessWithoutChange); +} + +bool WrapOpKill::ReplaceWithFunctionCall(Instruction* inst) { + assert((inst->opcode() == SpvOpKill || + inst->opcode() == SpvOpTerminateInvocation) && + "|inst| must be an OpKill or OpTerminateInvocation instruction."); + InstructionBuilder ir_builder( + context(), inst, + IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping); + uint32_t func_id = GetKillingFuncId(inst->opcode()); + if (func_id == 0) { + return false; + } + Instruction* call_inst = + ir_builder.AddFunctionCall(GetVoidTypeId(), func_id, {}); + if (call_inst == nullptr) { + return false; + } + call_inst->UpdateDebugInfoFrom(inst); + + Instruction* return_inst = nullptr; + uint32_t return_type_id = GetOwningFunctionsReturnType(inst); + if (return_type_id != GetVoidTypeId()) { + Instruction* undef = ir_builder.AddNullaryOp(return_type_id, SpvOpUndef); + if (undef == nullptr) { + return false; + } + return_inst = + ir_builder.AddUnaryOp(0, SpvOpReturnValue, undef->result_id()); + } else { + return_inst = ir_builder.AddNullaryOp(0, SpvOpReturn); + } + + if (return_inst == nullptr) { + return false; + } + + context()->KillInst(inst); + return true; +} + +uint32_t WrapOpKill::GetVoidTypeId() { + if (void_type_id_ != 0) { + return void_type_id_; + } + + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_type; + void_type_id_ = type_mgr->GetTypeInstruction(&void_type); + return void_type_id_; +} + +uint32_t WrapOpKill::GetVoidFunctionTypeId() { + analysis::TypeManager* type_mgr = context()->get_type_mgr(); + analysis::Void void_type; + const analysis::Type* registered_void_type = + type_mgr->GetRegisteredType(&void_type); + + analysis::Function func_type(registered_void_type, {}); + return type_mgr->GetTypeInstruction(&func_type); +} + +uint32_t WrapOpKill::GetKillingFuncId(SpvOp opcode) { + // Parameterize by opcode + assert(opcode == SpvOpKill || opcode == SpvOpTerminateInvocation); + + std::unique_ptr* const killing_func = + (opcode == SpvOpKill) ? &opkill_function_ + : &opterminateinvocation_function_; + + if (*killing_func != nullptr) { + return (*killing_func)->result_id(); + } + + uint32_t killing_func_id = TakeNextId(); + if (killing_func_id == 0) { + return 0; + } + + uint32_t void_type_id = GetVoidTypeId(); + if (void_type_id == 0) { + return 0; + } + + // Generate the function start instruction + std::unique_ptr func_start(new Instruction( + context(), SpvOpFunction, void_type_id, killing_func_id, {})); + func_start->AddOperand({SPV_OPERAND_TYPE_FUNCTION_CONTROL, {0}}); + func_start->AddOperand({SPV_OPERAND_TYPE_ID, {GetVoidFunctionTypeId()}}); + (*killing_func).reset(new Function(std::move(func_start))); + + // Generate the function end instruction + std::unique_ptr func_end( + new Instruction(context(), SpvOpFunctionEnd, 0, 0, {})); + (*killing_func)->SetFunctionEnd(std::move(func_end)); + + // Create the one basic block for the function. + uint32_t lab_id = TakeNextId(); + if (lab_id == 0) { + return 0; + } + std::unique_ptr label_inst( + new Instruction(context(), SpvOpLabel, 0, lab_id, {})); + std::unique_ptr bb(new BasicBlock(std::move(label_inst))); + + // Add the OpKill to the basic block + std::unique_ptr kill_inst( + new Instruction(context(), opcode, 0, 0, {})); + bb->AddInstruction(std::move(kill_inst)); + + // Add the bb to the function + bb->SetParent((*killing_func).get()); + (*killing_func)->AddBasicBlock(std::move(bb)); + + // Add the function to the module. + if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse)) { + (*killing_func)->ForEachInst([this](Instruction* inst) { + context()->AnalyzeDefUse(inst); + }); + } + + if (context()->AreAnalysesValid(IRContext::kAnalysisInstrToBlockMapping)) { + for (BasicBlock& basic_block : *(*killing_func)) { + context()->set_instr_block(basic_block.GetLabelInst(), &basic_block); + for (Instruction& inst : basic_block) { + context()->set_instr_block(&inst, &basic_block); + } + } + } + + return (*killing_func)->result_id(); +} + +uint32_t WrapOpKill::GetOwningFunctionsReturnType(Instruction* inst) { + BasicBlock* bb = context()->get_instr_block(inst); + if (bb == nullptr) { + return 0; + } + + Function* func = bb->GetParent(); + return func->type_id(); +} + +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/source/opt/wrap_opkill.h b/third_party/spirv-tools/source/opt/wrap_opkill.h new file mode 100644 index 0000000..7e43ca6 --- /dev/null +++ b/third_party/spirv-tools/source/opt/wrap_opkill.h @@ -0,0 +1,80 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_OPT_WRAP_OPKILL_H_ +#define SOURCE_OPT_WRAP_OPKILL_H_ + +#include "source/opt/pass.h" + +namespace spvtools { +namespace opt { + +// Documented in optimizer.hpp +class WrapOpKill : public Pass { + public: + WrapOpKill() : void_type_id_(0) {} + + const char* name() const override { return "wrap-opkill"; } + + Status Process() override; + + IRContext::Analysis GetPreservedAnalyses() override { + return IRContext::kAnalysisDefUse | + IRContext::kAnalysisInstrToBlockMapping | + IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators | + IRContext::kAnalysisNameMap | IRContext::kAnalysisBuiltinVarId | + IRContext::kAnalysisConstants | IRContext::kAnalysisTypes; + } + + private: + // Replaces the OpKill or OpTerminateInvocation instruction |inst| with a + // function call to a function that contains a single instruction, a clone of + // |inst|. An OpUnreachable instruction will be placed after the function + // call. Return true if successful. + bool ReplaceWithFunctionCall(Instruction* inst); + + // Returns the id of the void type. + uint32_t GetVoidTypeId(); + + // Returns the id of the function type for a void function with no parameters. + uint32_t GetVoidFunctionTypeId(); + + // Return the id of a function that has return type void, has no parameters, + // and contains a single instruction, which is |opcode|, either OpKill or + // OpTerminateInvocation. Returns 0 if the function could not be generated. + uint32_t GetKillingFuncId(SpvOp opcode); + + // Returns the id of the return type for the function that contains |inst|. + // Returns 0 if |inst| is not in a function. + uint32_t GetOwningFunctionsReturnType(Instruction* inst); + + // The id of the void type. If its value is 0, then the void type has not + // been found or created yet. + uint32_t void_type_id_; + + // The function that is a single instruction, which is an OpKill. The + // function has a void return type and takes no parameters. If the function is + // |nullptr|, then the function has not been generated. + std::unique_ptr opkill_function_; + // The function that is a single instruction, which is an + // OpTerminateInvocation. The function has a void return type and takes no + // parameters. If the function is |nullptr|, then the function has not been + // generated. + std::unique_ptr opterminateinvocation_function_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_WRAP_OPKILL_H_ diff --git a/third_party/spirv-tools/source/parsed_operand.cpp b/third_party/spirv-tools/source/parsed_operand.cpp new file mode 100644 index 0000000..7ad369c --- /dev/null +++ b/third_party/spirv-tools/source/parsed_operand.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains utility functions for spv_parsed_operand_t. + +#include "source/parsed_operand.h" + +#include +#include "source/util/hex_float.h" + +namespace spvtools { + +void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst, + const spv_parsed_operand_t& operand) { + if (operand.type != SPV_OPERAND_TYPE_LITERAL_INTEGER && + operand.type != SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER) + return; + if (operand.num_words < 1) return; + // TODO(dneto): Support more than 64-bits at a time. + if (operand.num_words > 2) return; + + const uint32_t word = inst.words[operand.offset]; + if (operand.num_words == 1) { + switch (operand.number_kind) { + case SPV_NUMBER_SIGNED_INT: + *out << int32_t(word); + break; + case SPV_NUMBER_UNSIGNED_INT: + *out << word; + break; + case SPV_NUMBER_FLOATING: + if (operand.number_bit_width == 16) { + *out << spvtools::utils::FloatProxy( + uint16_t(word & 0xFFFF)); + } else { + // Assume 32-bit floats. + *out << spvtools::utils::FloatProxy(word); + } + break; + default: + break; + } + } else if (operand.num_words == 2) { + // Multi-word numbers are presented with lower order words first. + uint64_t bits = + uint64_t(word) | (uint64_t(inst.words[operand.offset + 1]) << 32); + switch (operand.number_kind) { + case SPV_NUMBER_SIGNED_INT: + *out << int64_t(bits); + break; + case SPV_NUMBER_UNSIGNED_INT: + *out << bits; + break; + case SPV_NUMBER_FLOATING: + // Assume only 64-bit floats. + *out << spvtools::utils::FloatProxy(bits); + break; + default: + break; + } + } +} +} // namespace spvtools diff --git a/third_party/spirv-tools/source/parsed_operand.h b/third_party/spirv-tools/source/parsed_operand.h new file mode 100644 index 0000000..bab8611 --- /dev/null +++ b/third_party/spirv-tools/source/parsed_operand.h @@ -0,0 +1,33 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_PARSED_OPERAND_H_ +#define SOURCE_PARSED_OPERAND_H_ + +#include + +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Emits the numeric literal representation of the given instruction operand +// to the stream. The operand must be of numeric type. If integral it may +// be up to 64 bits wide. If floating point, then it must be 16, 32, or 64 +// bits wide. +void EmitNumericLiteral(std::ostream* out, const spv_parsed_instruction_t& inst, + const spv_parsed_operand_t& operand); + +} // namespace spvtools + +#endif // SOURCE_PARSED_OPERAND_H_ diff --git a/third_party/spirv-tools/source/pch_source.cpp b/third_party/spirv-tools/source/pch_source.cpp new file mode 100644 index 0000000..032e29e --- /dev/null +++ b/third_party/spirv-tools/source/pch_source.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_source.h" diff --git a/third_party/spirv-tools/source/pch_source.h b/third_party/spirv-tools/source/pch_source.h new file mode 100644 index 0000000..6695ba2 --- /dev/null +++ b/third_party/spirv-tools/source/pch_source.h @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validation_state.h" diff --git a/third_party/spirv-tools/source/print.cpp b/third_party/spirv-tools/source/print.cpp new file mode 100644 index 0000000..128587a --- /dev/null +++ b/third_party/spirv-tools/source/print.cpp @@ -0,0 +1,126 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/print.h" + +#if defined(SPIRV_ANDROID) || defined(SPIRV_LINUX) || defined(SPIRV_MAC) || \ + defined(SPIRV_IOS) || defined(SPIRV_FREEBSD) || \ + defined(SPIRV_EMSCRIPTEN) || defined(SPIRV_FUCHSIA) +namespace spvtools { + +clr::reset::operator const char*() { return "\x1b[0m"; } + +clr::grey::operator const char*() { return "\x1b[1;30m"; } + +clr::red::operator const char*() { return "\x1b[31m"; } + +clr::green::operator const char*() { return "\x1b[32m"; } + +clr::yellow::operator const char*() { return "\x1b[33m"; } + +clr::blue::operator const char*() { return "\x1b[34m"; } + +} // namespace spvtools +#elif defined(SPIRV_WINDOWS) +#include + +namespace spvtools { + +static void SetConsoleForegroundColorPrimary(HANDLE hConsole, WORD color) { + // Get screen buffer information from console handle + CONSOLE_SCREEN_BUFFER_INFO bufInfo; + GetConsoleScreenBufferInfo(hConsole, &bufInfo); + + // Get background color + color = WORD(color | (bufInfo.wAttributes & 0xfff0)); + + // Set foreground color + SetConsoleTextAttribute(hConsole, color); +} + +static void SetConsoleForegroundColor(WORD color) { + SetConsoleForegroundColorPrimary(GetStdHandle(STD_OUTPUT_HANDLE), color); + SetConsoleForegroundColorPrimary(GetStdHandle(STD_ERROR_HANDLE), color); +} + +clr::reset::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(0xf); + return ""; + } + return "\x1b[0m"; +} + +clr::grey::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_INTENSITY); + return ""; + } + return "\x1b[1;30m"; +} + +clr::red::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_RED); + return ""; + } + return "\x1b[31m"; +} + +clr::green::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_GREEN); + return ""; + } + return "\x1b[32m"; +} + +clr::yellow::operator const char*() { + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_RED | FOREGROUND_GREEN); + return ""; + } + return "\x1b[33m"; +} + +clr::blue::operator const char*() { + // Blue all by itself is hard to see against a black background (the + // default on command shell), or a medium blue background (the default + // on PowerShell). So increase its intensity. + + if (isPrint) { + SetConsoleForegroundColor(FOREGROUND_BLUE | FOREGROUND_INTENSITY); + return ""; + } + return "\x1b[94m"; +} + +} // namespace spvtools +#else +namespace spvtools { + +clr::reset::operator const char*() { return ""; } + +clr::grey::operator const char*() { return ""; } + +clr::red::operator const char*() { return ""; } + +clr::green::operator const char*() { return ""; } + +clr::yellow::operator const char*() { return ""; } + +clr::blue::operator const char*() { return ""; } + +} // namespace spvtools +#endif diff --git a/third_party/spirv-tools/source/print.h b/third_party/spirv-tools/source/print.h new file mode 100644 index 0000000..f31ba38 --- /dev/null +++ b/third_party/spirv-tools/source/print.h @@ -0,0 +1,75 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_PRINT_H_ +#define SOURCE_PRINT_H_ + +#include +#include + +namespace spvtools { + +// Wrapper for out stream selection. +class out_stream { + public: + out_stream() : pStream(nullptr) {} + explicit out_stream(std::stringstream& stream) : pStream(&stream) {} + + std::ostream& get() { + if (pStream) { + return *pStream; + } + return std::cout; + } + + private: + std::stringstream* pStream; +}; + +namespace clr { +// Resets console color. +struct reset { + operator const char*(); + bool isPrint; +}; +// Sets console color to grey. +struct grey { + operator const char*(); + bool isPrint; +}; +// Sets console color to red. +struct red { + operator const char*(); + bool isPrint; +}; +// Sets console color to green. +struct green { + operator const char*(); + bool isPrint; +}; +// Sets console color to yellow. +struct yellow { + operator const char*(); + bool isPrint; +}; +// Sets console color to blue. +struct blue { + operator const char*(); + bool isPrint; +}; +} // namespace clr + +} // namespace spvtools + +#endif // SOURCE_PRINT_H_ diff --git a/third_party/spirv-tools/source/reduce/CMakeLists.txt b/third_party/spirv-tools/source/reduce/CMakeLists.txt new file mode 100644 index 0000000..a3291c7 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/CMakeLists.txt @@ -0,0 +1,112 @@ +# Copyright (c) 2018 Google LLC + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set(SPIRV_TOOLS_REDUCE_SOURCES + change_operand_reduction_opportunity.h + change_operand_to_undef_reduction_opportunity.h + merge_blocks_reduction_opportunity.h + merge_blocks_reduction_opportunity_finder.h + operand_to_const_reduction_opportunity_finder.h + operand_to_undef_reduction_opportunity_finder.h + operand_to_dominating_id_reduction_opportunity_finder.h + reducer.h + reduction_opportunity.h + reduction_opportunity_finder.h + reduction_pass.h + reduction_util.h + remove_block_reduction_opportunity.h + remove_block_reduction_opportunity_finder.h + remove_function_reduction_opportunity.h + remove_function_reduction_opportunity_finder.h + remove_instruction_reduction_opportunity.h + remove_selection_reduction_opportunity.h + remove_selection_reduction_opportunity_finder.h + remove_struct_member_reduction_opportunity.h + remove_unused_instruction_reduction_opportunity_finder.h + remove_unused_struct_member_reduction_opportunity_finder.h + structured_loop_to_selection_reduction_opportunity.h + structured_loop_to_selection_reduction_opportunity_finder.h + conditional_branch_to_simple_conditional_branch_opportunity_finder.h + conditional_branch_to_simple_conditional_branch_reduction_opportunity.h + simple_conditional_branch_to_branch_opportunity_finder.h + simple_conditional_branch_to_branch_reduction_opportunity.h + + change_operand_reduction_opportunity.cpp + change_operand_to_undef_reduction_opportunity.cpp + merge_blocks_reduction_opportunity.cpp + merge_blocks_reduction_opportunity_finder.cpp + operand_to_const_reduction_opportunity_finder.cpp + operand_to_undef_reduction_opportunity_finder.cpp + operand_to_dominating_id_reduction_opportunity_finder.cpp + reducer.cpp + reduction_opportunity.cpp + reduction_opportunity_finder.cpp + reduction_pass.cpp + reduction_util.cpp + remove_block_reduction_opportunity.cpp + remove_block_reduction_opportunity_finder.cpp + remove_function_reduction_opportunity.cpp + remove_function_reduction_opportunity_finder.cpp + remove_instruction_reduction_opportunity.cpp + remove_selection_reduction_opportunity.cpp + remove_selection_reduction_opportunity_finder.cpp + remove_struct_member_reduction_opportunity.cpp + remove_unused_instruction_reduction_opportunity_finder.cpp + remove_unused_struct_member_reduction_opportunity_finder.cpp + structured_loop_to_selection_reduction_opportunity.cpp + structured_loop_to_selection_reduction_opportunity_finder.cpp + conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp + conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp + simple_conditional_branch_to_branch_opportunity_finder.cpp + simple_conditional_branch_to_branch_reduction_opportunity.cpp +) + +if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) + # Enable parallel builds across four cores for this lib + add_definitions(/MP4) +endif() + +spvtools_pch(SPIRV_TOOLS_REDUCE_SOURCES pch_source_reduce) + +add_library(SPIRV-Tools-reduce ${SPIRV_TOOLS_LIBRARY_TYPE} ${SPIRV_TOOLS_REDUCE_SOURCES}) + +spvtools_default_compile_options(SPIRV-Tools-reduce) +target_include_directories(SPIRV-Tools-reduce + PUBLIC + $ + $ + $ + PRIVATE ${spirv-tools_BINARY_DIR} +) +# The reducer reuses a lot of functionality from the SPIRV-Tools library. +target_link_libraries(SPIRV-Tools-reduce + PUBLIC ${SPIRV_TOOLS_FULL_VISIBILITY} + PUBLIC SPIRV-Tools-opt) + +set_property(TARGET SPIRV-Tools-reduce PROPERTY FOLDER "SPIRV-Tools libraries") +spvtools_check_symbol_exports(SPIRV-Tools-reduce) + +if(ENABLE_SPIRV_TOOLS_INSTALL) + install(TARGETS SPIRV-Tools-reduce EXPORT SPIRV-Tools-reduceTargets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + export(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake) + + spvtools_config_package_dir(SPIRV-Tools-reduce PACKAGE_DIR) + install(EXPORT SPIRV-Tools-reduceTargets FILE SPIRV-Tools-reduceTarget.cmake + DESTINATION ${PACKAGE_DIR}) + + spvtools_generate_config_file(SPIRV-Tools-reduce) + install(FILES ${CMAKE_BINARY_DIR}/SPIRV-Tools-reduceConfig.cmake DESTINATION ${PACKAGE_DIR}) +endif(ENABLE_SPIRV_TOOLS_INSTALL) diff --git a/third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.cpp new file mode 100644 index 0000000..c3f6fd7 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/change_operand_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +bool ChangeOperandReductionOpportunity::PreconditionHolds() { + // Check that the instruction still has the original operand. + return inst_->NumOperands() > operand_index_ && + inst_->GetOperand(operand_index_).words[0] == original_id_ && + inst_->GetOperand(operand_index_).type == original_type_; +} + +void ChangeOperandReductionOpportunity::Apply() { + inst_->SetOperand(operand_index_, {new_id_}); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.h new file mode 100644 index 0000000..18e6ca1 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/change_operand_reduction_opportunity.h @@ -0,0 +1,54 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace an id operand of an instruction with some other id. +class ChangeOperandReductionOpportunity : public ReductionOpportunity { + public: + // Constructs the opportunity to replace operand |operand_index| of |inst| + // with |new_id|. + ChangeOperandReductionOpportunity(opt::Instruction* inst, + uint32_t operand_index, uint32_t new_id) + : inst_(inst), + operand_index_(operand_index), + original_id_(inst->GetOperand(operand_index).words[0]), + original_type_(inst->GetOperand(operand_index).type), + new_id_(new_id) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Instruction* const inst_; + const uint32_t operand_index_; + const uint32_t original_id_; + const spv_operand_type_t original_type_; + const uint32_t new_id_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_CHANGE_OPERAND_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.cpp new file mode 100644 index 0000000..8e33da6 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/change_operand_to_undef_reduction_opportunity.h" + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +bool ChangeOperandToUndefReductionOpportunity::PreconditionHolds() { + // Check that the instruction still has the original operand. + return inst_->NumOperands() > operand_index_ && + inst_->GetOperand(operand_index_).words[0] == original_id_; +} + +void ChangeOperandToUndefReductionOpportunity::Apply() { + auto operand = inst_->GetOperand(operand_index_); + auto operand_id = operand.words[0]; + auto operand_id_def = context_->get_def_use_mgr()->GetDef(operand_id); + auto operand_type_id = operand_id_def->type_id(); + // The opportunity should not exist unless this holds. + assert(operand_type_id); + auto undef_id = FindOrCreateGlobalUndef(context_, operand_type_id); + inst_->SetOperand(operand_index_, {undef_id}); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.h new file mode 100644 index 0000000..ffd3155 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/change_operand_to_undef_reduction_opportunity.h @@ -0,0 +1,53 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace an id operand of an instruction with undef. +class ChangeOperandToUndefReductionOpportunity : public ReductionOpportunity { + public: + // Constructs the opportunity to replace operand |operand_index| of |inst| + // with undef. + ChangeOperandToUndefReductionOpportunity(opt::IRContext* context, + opt::Instruction* inst, + uint32_t operand_index) + : context_(context), + inst_(inst), + operand_index_(operand_index), + original_id_(inst->GetOperand(operand_index).words[0]) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Instruction* const inst_; + const uint32_t operand_index_; + const uint32_t original_id_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_CHANGE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp new file mode 100644 index 0000000..2cd779a --- /dev/null +++ b/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h" + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h" +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +std::vector> +ConditionalBranchToSimpleConditionalBranchOpportunityFinder:: + GetAvailableOpportunities(opt::IRContext* context, + uint32_t target_function) const { + std::vector> result; + + // Find the opportunities for redirecting all false targets before the + // opportunities for redirecting all true targets because the former + // opportunities disable the latter, and vice versa, and the efficiency of the + // reducer is improved by avoiding contiguous opportunities that disable one + // another. + for (bool redirect_to_true : {true, false}) { + // Consider every relevant function. + for (auto* function : GetTargetFunctions(context, target_function)) { + // Consider every block in the function. + for (auto& block : *function) { + // The terminator must be SpvOpBranchConditional. + opt::Instruction* terminator = block.terminator(); + if (terminator->opcode() != SpvOpBranchConditional) { + continue; + } + + uint32_t true_block_id = + terminator->GetSingleWordInOperand(kTrueBranchOperandIndex); + uint32_t false_block_id = + terminator->GetSingleWordInOperand(kFalseBranchOperandIndex); + + // The conditional branch must not already be simplified. + if (true_block_id == false_block_id) { + continue; + } + + // The redirected target must not be a back-edge to a structured loop + // header. + uint32_t redirected_block_id = + redirect_to_true ? false_block_id : true_block_id; + uint32_t containing_loop_header = + context->GetStructuredCFGAnalysis()->ContainingLoop(block.id()); + // The structured CFG analysis does not include a loop header as part + // of the loop construct, but we want to include it, so handle this + // special case: + if (block.GetLoopMergeInst() != nullptr) { + containing_loop_header = block.id(); + } + if (redirected_block_id == containing_loop_header) { + continue; + } + + result.push_back( + MakeUnique< + ConditionalBranchToSimpleConditionalBranchReductionOpportunity>( + context, block.terminator(), redirect_to_true)); + } + } + } + return result; +} + +std::string +ConditionalBranchToSimpleConditionalBranchOpportunityFinder::GetName() const { + return "ConditionalBranchToSimpleConditionalBranchOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h b/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h new file mode 100644 index 0000000..17af9b0 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to simplify conditional branches into simple +// conditional branches (conditional branches with one target). +class ConditionalBranchToSimpleConditionalBranchOpportunityFinder + : public ReductionOpportunityFinder { + public: + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const override; + + std::string GetName() const override; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLIFY_SELECTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp new file mode 100644 index 0000000..8304c30 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h" + +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +ConditionalBranchToSimpleConditionalBranchReductionOpportunity:: + ConditionalBranchToSimpleConditionalBranchReductionOpportunity( + opt::IRContext* context, + opt::Instruction* conditional_branch_instruction, bool redirect_to_true) + : context_(context), + conditional_branch_instruction_(conditional_branch_instruction), + redirect_to_true_(redirect_to_true) {} + +bool ConditionalBranchToSimpleConditionalBranchReductionOpportunity:: + PreconditionHolds() { + // Another opportunity may have already simplified this conditional branch, + // which should disable this opportunity. + return conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex) != + conditional_branch_instruction_->GetSingleWordInOperand( + kFalseBranchOperandIndex); +} + +void ConditionalBranchToSimpleConditionalBranchReductionOpportunity::Apply() { + uint32_t operand_to_modify = + redirect_to_true_ ? kFalseBranchOperandIndex : kTrueBranchOperandIndex; + uint32_t operand_to_copy = + redirect_to_true_ ? kTrueBranchOperandIndex : kFalseBranchOperandIndex; + + auto old_successor_block_id = + conditional_branch_instruction_->GetSingleWordInOperand( + operand_to_modify); + + // Do the branch redirection. + conditional_branch_instruction_->SetInOperand( + operand_to_modify, + {conditional_branch_instruction_->GetSingleWordInOperand( + operand_to_copy)}); + + // The old successor block may have phi instructions; these will need to + // respect the change in edges. + AdaptPhiInstructionsForRemovedEdge( + context_->get_instr_block(conditional_branch_instruction_)->id(), + context_->cfg()->block(old_successor_block_id)); + + // We have changed the CFG. + context_->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h new file mode 100644 index 0000000..1f9cb6d --- /dev/null +++ b/third_party/spirv-tools/source/reduce/conditional_branch_to_simple_conditional_branch_reduction_opportunity.h @@ -0,0 +1,54 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to simplify a conditional branch to a simple conditional +// branch (a conditional branch with one target). +class ConditionalBranchToSimpleConditionalBranchReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity to simplify |conditional_branch_instruction|. If + // |redirect_to_true| is true, the false target will be changed to also point + // to the true target; otherwise, the true target will be changed to also + // point to the false target. + explicit ConditionalBranchToSimpleConditionalBranchReductionOpportunity( + opt::IRContext* context, opt::Instruction* conditional_branch_instruction, + bool redirect_to_true); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Instruction* conditional_branch_instruction_; + + // If true, the false target will be changed to point to the true target; + // otherwise, the true target will be changed to point to the false target. + bool redirect_to_true_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLIFY_CONDITIONAL_BRANCH_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.cpp new file mode 100644 index 0000000..a2c3b40 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/merge_blocks_reduction_opportunity.h" + +#include "source/opt/block_merge_util.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +MergeBlocksReductionOpportunity::MergeBlocksReductionOpportunity( + opt::IRContext* context, opt::Function* function, opt::BasicBlock* block) { + // Precondition: the terminator has to be OpBranch. + assert(block->terminator()->opcode() == SpvOpBranch); + context_ = context; + function_ = function; + // Get the successor block associated with the OpBranch. + successor_block_ = + context->cfg()->block(block->terminator()->GetSingleWordInOperand(0)); +} + +bool MergeBlocksReductionOpportunity::PreconditionHolds() { + // Merge block opportunities can disable each other. + // Example: Given blocks: A->B->C. + // A is a loop header; B and C are blocks in the loop; C ends with OpReturn. + // There are two opportunities: B and C can be merged with their predecessors. + // Merge C. B now ends with OpReturn. We now just have: A->B. + // Merge B is now disabled, as this would lead to A, a loop header, ending + // with an OpReturn, which is invalid. + + const auto predecessors = context_->cfg()->preds(successor_block_->id()); + assert(1 == predecessors.size() && + "For a successor to be merged into its predecessor, exactly one " + "predecessor must be present."); + const uint32_t predecessor_id = predecessors[0]; + opt::BasicBlock* predecessor_block = + context_->get_instr_block(predecessor_id); + return opt::blockmergeutil::CanMergeWithSuccessor(context_, + predecessor_block); +} + +void MergeBlocksReductionOpportunity::Apply() { + // While the original block that targeted the successor may not exist anymore + // (it might have been merged with another block), some block must exist that + // targets the successor. Find it. + + const auto predecessors = context_->cfg()->preds(successor_block_->id()); + assert(1 == predecessors.size() && + "For a successor to be merged into its predecessor, exactly one " + "predecessor must be present."); + const uint32_t predecessor_id = predecessors[0]; + + // We need an iterator pointing to the predecessor, hence the loop. + for (auto bi = function_->begin(); bi != function_->end(); ++bi) { + if (bi->id() == predecessor_id) { + opt::blockmergeutil::MergeWithSuccessor(context_, function_, bi); + // Block merging changes the control flow graph, so invalidate it. + context_->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + return; + } + } + + assert(false && + "Unreachable: we should have found a block with the desired id."); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.h new file mode 100644 index 0000000..5c9180b --- /dev/null +++ b/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity.h @@ -0,0 +1,53 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to merge two blocks into one. +class MergeBlocksReductionOpportunity : public ReductionOpportunity { + public: + // Creates the opportunity to merge |block| with its successor, where |block| + // is inside |function|, and |context| is the enclosing IR context. + MergeBlocksReductionOpportunity(opt::IRContext* context, + opt::Function* function, + opt::BasicBlock* block); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::IRContext* context_; + opt::Function* function_; + + // Rather than holding on to the block that can be merged with its successor, + // we hold on to its successor. This is because the predecessor block might + // get merged with *its* predecessor, and so will no longer exist, while the + // successor will continue to exist until this opportunity gets applied. + opt::BasicBlock* successor_block_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..ea5e9da --- /dev/null +++ b/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" +#include "source/opt/block_merge_util.h" +#include "source/reduce/merge_blocks_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::string MergeBlocksReductionOpportunityFinder::GetName() const { + return "MergeBlocksReductionOpportunityFinder"; +} + +std::vector> +MergeBlocksReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every block in every function. + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + // See whether it is possible to merge this block with its successor. + if (opt::blockmergeutil::CanMergeWithSuccessor(context, &block)) { + // It is, so record an opportunity to do this. + result.push_back(spvtools::MakeUnique( + context, function, &block)); + } + } + } + return result; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.h new file mode 100644 index 0000000..df7a8bf --- /dev/null +++ b/third_party/spirv-tools/source/reduce/merge_blocks_reduction_opportunity_finder.h @@ -0,0 +1,42 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to merge blocks together. +class MergeBlocksReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + MergeBlocksReductionOpportunityFinder() = default; + + ~MergeBlocksReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_MERGE_BLOCKS_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..eb7498a --- /dev/null +++ b/third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" + +#include "source/opt/instruction.h" +#include "source/reduce/change_operand_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +OperandToConstReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + assert(result.empty()); + + // We first loop over all constants. This means that all the reduction + // opportunities to replace an operand with a particular constant will be + // contiguous, and in particular it means that multiple, incompatible + // reduction opportunities that try to replace the same operand with distinct + // constants are likely to be discontiguous. This is good because the + // reducer works in the spirit of delta debugging and tries applying large + // contiguous blocks of opportunities early on, and we want to avoid having a + // large block of incompatible opportunities if possible. + for (const auto& constant : context->GetConstants()) { + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + for (auto& inst : block) { + // We iterate through the operands using an explicit index (rather + // than using a lambda) so that we use said index in the construction + // of a ChangeOperandReductionOpportunity + for (uint32_t index = 0; index < inst.NumOperands(); index++) { + const auto& operand = inst.GetOperand(index); + if (spvIsInIdType(operand.type)) { + const auto id = operand.words[0]; + auto def = context->get_def_use_mgr()->GetDef(id); + if (spvOpcodeIsConstant(def->opcode())) { + // The argument is already a constant. + continue; + } + if (def->opcode() == SpvOpFunction) { + // The argument refers to a function, e.g. the function called + // by OpFunctionCall; avoid replacing this with a constant of + // the function's return type. + continue; + } + auto type_id = def->type_id(); + if (type_id) { + if (constant->type_id() == type_id) { + result.push_back( + MakeUnique( + &inst, index, constant->result_id())); + } + } + } + } + } + } + } + } + return result; +} + +std::string OperandToConstReductionOpportunityFinder::GetName() const { + return "OperandToConstReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.h new file mode 100644 index 0000000..6726746 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/operand_to_const_reduction_opportunity_finder.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to replace id operands of instructions with ids of +// constants. This reduces the extent to which ids of non-constants are used, +// paving the way for instructions that generate them to be eliminated. +class OperandToConstReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + OperandToConstReductionOpportunityFinder() = default; + + ~OperandToConstReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_OPERAND_TO_CONST_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..ca3a99e --- /dev/null +++ b/third_party/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.cpp @@ -0,0 +1,114 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" + +#include "source/opt/instruction.h" +#include "source/reduce/change_operand_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +OperandToDominatingIdReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Go through every instruction in every block, considering it as a potential + // dominator of other instructions. We choose this order for two reasons: + // + // (1) it is profitable for multiple opportunities to replace the same id x by + // different dominating ids y and z to be discontiguous, as they are + // incompatible. + // + // (2) We want to prioritise opportunities to replace an id with a more + // distant dominator. Intuitively, in a human-readable programming language + // if we have a complex expression e with many sub-expressions, we would like + // to prioritise replacing e with its smallest sub-expressions; generalising + // this idea to dominating ids this roughly corresponds to more distant + // dominators. + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto dominating_block = function->begin(); + dominating_block != function->end(); ++dominating_block) { + for (auto& dominating_inst : *dominating_block) { + if (dominating_inst.HasResultId() && dominating_inst.type_id()) { + // Consider replacing any operand with matching type in a dominated + // instruction with the id generated by this instruction. + GetOpportunitiesForDominatingInst( + &result, &dominating_inst, dominating_block, function, context); + } + } + } + } + return result; +} + +void OperandToDominatingIdReductionOpportunityFinder:: + GetOpportunitiesForDominatingInst( + std::vector>* opportunities, + opt::Instruction* candidate_dominator, + opt::Function::iterator candidate_dominator_block, + opt::Function* function, opt::IRContext* context) const { + assert(candidate_dominator->HasResultId()); + assert(candidate_dominator->type_id()); + auto dominator_analysis = context->GetDominatorAnalysis(function); + // SPIR-V requires a block to precede all blocks it dominates, so it suffices + // to search from the candidate dominator block onwards. + for (auto block = candidate_dominator_block; block != function->end(); + ++block) { + if (!dominator_analysis->Dominates(&*candidate_dominator_block, &*block)) { + // If the candidate dominator block doesn't dominate this block then there + // cannot be any of the desired reduction opportunities in this block. + continue; + } + for (auto& inst : *block) { + // We iterate through the operands using an explicit index (rather + // than using a lambda) so that we use said index in the construction + // of a ChangeOperandReductionOpportunity + for (uint32_t index = 0; index < inst.NumOperands(); index++) { + const auto& operand = inst.GetOperand(index); + if (spvIsInIdType(operand.type)) { + const auto id = operand.words[0]; + auto def = context->get_def_use_mgr()->GetDef(id); + assert(def); + if (!context->get_instr_block(def)) { + // The definition does not come from a block; e.g. it might be a + // constant. It is thus not relevant to this pass. + continue; + } + assert(!context->get_constant_mgr()->GetConstantFromInst(def) && + "We should not get here if the argument is a constant."); + if (def->type_id() != candidate_dominator->type_id()) { + // The types need to match. + continue; + } + if (candidate_dominator != def && + dominator_analysis->Dominates(candidate_dominator, def)) { + // A hit: the candidate dominator strictly dominates the definition. + opportunities->push_back( + MakeUnique( + &inst, index, candidate_dominator->result_id())); + } + } + } + } + } +} + +std::string OperandToDominatingIdReductionOpportunityFinder::GetName() const { + return "OperandToDominatingIdReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h new file mode 100644 index 0000000..5f33370 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h @@ -0,0 +1,56 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder that aims to bring to SPIR-V (and generalize) the idea from +// human-readable languages of e.g. finding opportunities to replace an +// expression with one of its arguments, (x + y) -> x, or with a reference to an +// identifier that was assigned to higher up in the program. The generalization +// of this is to replace an id with a different id of the same type defined in +// some dominating instruction. +// +// If id x is defined and then used several times, changing each use of x to +// some dominating definition may eventually allow the statement defining x +// to be eliminated by another pass. +class OperandToDominatingIdReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + OperandToDominatingIdReductionOpportunityFinder() = default; + + ~OperandToDominatingIdReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + void GetOpportunitiesForDominatingInst( + std::vector>* opportunities, + opt::Instruction* dominating_instruction, + opt::Function::iterator candidate_dominator_block, + opt::Function* function, opt::IRContext* context) const; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_OPERAND_TO_DOMINATING_ID_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..06bf955 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h" + +#include "source/opt/instruction.h" +#include "source/reduce/change_operand_to_undef_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +OperandToUndefReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + for (auto& inst : block) { + // Skip instructions that result in a pointer type. + auto type_id = inst.type_id(); + if (type_id) { + auto type_id_def = context->get_def_use_mgr()->GetDef(type_id); + if (type_id_def->opcode() == SpvOpTypePointer) { + continue; + } + } + + // We iterate through the operands using an explicit index (rather + // than using a lambda) so that we use said index in the construction + // of a ChangeOperandToUndefReductionOpportunity + for (uint32_t index = 0; index < inst.NumOperands(); index++) { + const auto& operand = inst.GetOperand(index); + + if (spvIsInIdType(operand.type)) { + const auto operand_id = operand.words[0]; + auto operand_id_def = + context->get_def_use_mgr()->GetDef(operand_id); + + // Skip constant and undef operands. + // We always want the reducer to make the module "smaller", which + // ensures termination. + // Therefore, we assume: id > undef id > constant id. + if (spvOpcodeIsConstantOrUndef(operand_id_def->opcode())) { + continue; + } + + // Don't replace function operands with undef. + if (operand_id_def->opcode() == SpvOpFunction) { + continue; + } + + // Only consider operands that have a type. + auto operand_type_id = operand_id_def->type_id(); + if (operand_type_id) { + auto operand_type_id_def = + context->get_def_use_mgr()->GetDef(operand_type_id); + + // Skip pointer operands. + if (operand_type_id_def->opcode() == SpvOpTypePointer) { + continue; + } + + result.push_back( + MakeUnique( + context, &inst, index)); + } + } + } + } + } + } + return result; +} + +std::string OperandToUndefReductionOpportunityFinder::GetName() const { + return "OperandToUndefReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.h new file mode 100644 index 0000000..a5c759e --- /dev/null +++ b/third_party/spirv-tools/source/reduce/operand_to_undef_reduction_opportunity_finder.h @@ -0,0 +1,43 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to replace id operands of instructions with ids of +// undef. +class OperandToUndefReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + OperandToUndefReductionOpportunityFinder() = default; + + ~OperandToUndefReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_OPERAND_TO_UNDEF_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/pch_source_reduce.cpp b/third_party/spirv-tools/source/reduce/pch_source_reduce.cpp new file mode 100644 index 0000000..61e7436 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/pch_source_reduce.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "pch_source_reduce.h" diff --git a/third_party/spirv-tools/source/reduce/pch_source_reduce.h b/third_party/spirv-tools/source/reduce/pch_source_reduce.h new file mode 100644 index 0000000..81bed20 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/pch_source_reduce.h @@ -0,0 +1,23 @@ +// Copyright (c) 2018 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include "source/reduce/change_operand_reduction_opportunity.h" +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" +#include "source/reduce/reduction_opportunity.h" +#include "source/reduce/reduction_pass.h" +#include "source/reduce/remove_instruction_reduction_opportunity.h" +#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" diff --git a/third_party/spirv-tools/source/reduce/reducer.cpp b/third_party/spirv-tools/source/reduce/reducer.cpp new file mode 100644 index 0000000..18eeaeb --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reducer.cpp @@ -0,0 +1,246 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reducer.h" + +#include +#include + +#include "source/reduce/conditional_branch_to_simple_conditional_branch_opportunity_finder.h" +#include "source/reduce/merge_blocks_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_const_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_dominating_id_reduction_opportunity_finder.h" +#include "source/reduce/operand_to_undef_reduction_opportunity_finder.h" +#include "source/reduce/remove_block_reduction_opportunity_finder.h" +#include "source/reduce/remove_function_reduction_opportunity_finder.h" +#include "source/reduce/remove_selection_reduction_opportunity_finder.h" +#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" +#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h" +#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" +#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" +#include "source/spirv_reducer_options.h" + +namespace spvtools { +namespace reduce { + +Reducer::Reducer(spv_target_env target_env) : target_env_(target_env) {} + +Reducer::~Reducer() = default; + +void Reducer::SetMessageConsumer(MessageConsumer c) { + for (auto& pass : passes_) { + pass->SetMessageConsumer(c); + } + for (auto& pass : cleanup_passes_) { + pass->SetMessageConsumer(c); + } + consumer_ = std::move(c); +} + +void Reducer::SetInterestingnessFunction( + Reducer::InterestingnessFunction interestingness_function) { + interestingness_function_ = std::move(interestingness_function); +} + +Reducer::ReductionResultStatus Reducer::Run( + std::vector&& binary_in, std::vector* binary_out, + spv_const_reducer_options options, + spv_validator_options validator_options) { + std::vector current_binary(std::move(binary_in)); + + spvtools::SpirvTools tools(target_env_); + assert(tools.IsValid() && "Failed to create SPIRV-Tools interface"); + + // Keeps track of how many reduction attempts have been tried. Reduction + // bails out if this reaches a given limit. + uint32_t reductions_applied = 0; + + // Initial state should be valid. + if (!tools.Validate(¤t_binary[0], current_binary.size(), + validator_options)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial binary is invalid; stopping."); + return Reducer::ReductionResultStatus::kInitialStateInvalid; + } + + // Initial state should be interesting. + if (!interestingness_function_(current_binary, reductions_applied)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Initial state was not interesting; stopping."); + return Reducer::ReductionResultStatus::kInitialStateNotInteresting; + } + + Reducer::ReductionResultStatus result = + RunPasses(&passes_, options, validator_options, tools, ¤t_binary, + &reductions_applied); + + if (result == Reducer::ReductionResultStatus::kComplete) { + // Cleanup passes. + result = RunPasses(&cleanup_passes_, options, validator_options, tools, + ¤t_binary, &reductions_applied); + } + + if (result == Reducer::ReductionResultStatus::kComplete) { + consumer_(SPV_MSG_INFO, nullptr, {}, "No more to reduce; stopping."); + } + + // Even if the reduction has failed by this point (e.g. due to producing an + // invalid binary), we still update the output binary for better debugging. + *binary_out = std::move(current_binary); + + return result; +} + +void Reducer::AddDefaultReductionPasses() { + AddReductionPass( + spvtools::MakeUnique( + false)); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass(spvtools::MakeUnique< + StructuredLoopToSelectionReductionOpportunityFinder>()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass( + spvtools::MakeUnique< + ConditionalBranchToSimpleConditionalBranchOpportunityFinder>()); + AddReductionPass( + spvtools::MakeUnique()); + AddReductionPass(spvtools::MakeUnique< + RemoveUnusedStructMemberReductionOpportunityFinder>()); + + // Cleanup passes. + + AddCleanupReductionPass( + spvtools::MakeUnique( + true)); +} + +void Reducer::AddReductionPass( + std::unique_ptr&& finder) { + passes_.push_back( + spvtools::MakeUnique(target_env_, std::move(finder))); +} + +void Reducer::AddCleanupReductionPass( + std::unique_ptr&& finder) { + cleanup_passes_.push_back( + spvtools::MakeUnique(target_env_, std::move(finder))); +} + +bool Reducer::ReachedStepLimit(uint32_t current_step, + spv_const_reducer_options options) { + return current_step >= options->step_limit; +} + +Reducer::ReductionResultStatus Reducer::RunPasses( + std::vector>* passes, + spv_const_reducer_options options, spv_validator_options validator_options, + const SpirvTools& tools, std::vector* current_binary, + uint32_t* const reductions_applied) { + // Determines whether, on completing one round of reduction passes, it is + // worthwhile trying a further round. + bool another_round_worthwhile = true; + + // Apply round after round of reduction passes until we hit the reduction + // step limit, or deem that another round is not going to be worthwhile. + while (!ReachedStepLimit(*reductions_applied, options) && + another_round_worthwhile) { + // At the start of a round of reduction passes, assume another round will + // not be worthwhile unless we find evidence to the contrary. + another_round_worthwhile = false; + + // Iterate through the available passes. + for (auto& pass : *passes) { + // If this pass hasn't reached its minimum granularity then it's + // worth eventually doing another round of reductions, in order to + // try this pass at a finer granularity. + another_round_worthwhile |= !pass->ReachedMinimumGranularity(); + + // Keep applying this pass at its current granularity until it stops + // working or we hit the reduction step limit. + consumer_(SPV_MSG_INFO, nullptr, {}, + ("Trying pass " + pass->GetName() + ".").c_str()); + do { + auto maybe_result = + pass->TryApplyReduction(*current_binary, options->target_function); + if (maybe_result.empty()) { + // For this round, the pass has no more opportunities (chunks) to + // apply, so move on to the next pass. + consumer_( + SPV_MSG_INFO, nullptr, {}, + ("Pass " + pass->GetName() + " did not make a reduction step.") + .c_str()); + break; + } + bool interesting = false; + std::stringstream stringstream; + (*reductions_applied)++; + stringstream << "Pass " << pass->GetName() << " made reduction step " + << *reductions_applied << "."; + consumer_(SPV_MSG_INFO, nullptr, {}, (stringstream.str().c_str())); + if (!tools.Validate(&maybe_result[0], maybe_result.size(), + validator_options)) { + // The reduction step went wrong and an invalid binary was produced. + // By design, this shouldn't happen; this is a safeguard to stop an + // invalid binary from being regarded as interesting. + consumer_(SPV_MSG_INFO, nullptr, {}, + "Reduction step produced an invalid binary."); + if (options->fail_on_validation_error) { + // In this mode, we fail, so we update the current binary so it is + // output for debugging. + *current_binary = std::move(maybe_result); + return Reducer::ReductionResultStatus::kStateInvalid; + } + } else if (interestingness_function_(maybe_result, + *reductions_applied)) { + // Success! The binary produced by this reduction step is + // interesting, so make it the binary of interest henceforth, and + // note that it's worth doing another round of reduction passes. + consumer_(SPV_MSG_INFO, nullptr, {}, "Reduction step succeeded."); + *current_binary = std::move(maybe_result); + interesting = true; + another_round_worthwhile = true; + } + // We must call this before the next call to TryApplyReduction. + pass->NotifyInteresting(interesting); + // Bail out if the reduction step limit has been reached. + } while (!ReachedStepLimit(*reductions_applied, options)); + } + } + + // Report whether reduction completed, or bailed out early due to reaching + // the step limit. + if (ReachedStepLimit(*reductions_applied, options)) { + consumer_(SPV_MSG_INFO, nullptr, {}, + "Reached reduction step limit; stopping."); + return Reducer::ReductionResultStatus::kReachedStepLimit; + } + + // The passes completed successfully, although we may still run more passes. + return Reducer::ReductionResultStatus::kComplete; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/reducer.h b/third_party/spirv-tools/source/reduce/reducer.h new file mode 100644 index 0000000..864ce75 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reducer.h @@ -0,0 +1,122 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCER_H_ +#define SOURCE_REDUCE_REDUCER_H_ + +#include +#include + +#include "source/reduce/reduction_pass.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace reduce { + +// This class manages the process of applying a reduction -- parameterized by a +// number of reduction passes and an interestingness test, to a SPIR-V binary. +class Reducer { + public: + // Possible statuses that can result from running a reduction. + enum ReductionResultStatus { + kInitialStateNotInteresting, + kReachedStepLimit, + kComplete, + kInitialStateInvalid, + + // Returned when the fail-on-validation-error option is set and a + // reduction step yields a state that fails validation. + kStateInvalid, + }; + + // The type for a function that will take a binary and return true if and + // only if the binary is deemed interesting. (The function also takes an + // integer argument that will be incremented each time the function is + // called; this is for debugging purposes). + // + // The notion of "interesting" depends on what properties of the binary or + // tools that process the binary we are trying to maintain during reduction. + using InterestingnessFunction = + std::function&, uint32_t)>; + + // Constructs an instance with the given target |target_env|, which is used to + // decode the binary to be reduced later. + // + // The constructed instance will have an empty message consumer, which just + // ignores all messages from the library. Use SetMessageConsumer() to supply + // one if messages are of concern. + // + // The constructed instance also needs to have an interestingness function + // set and some reduction passes added to it in order to be useful. + explicit Reducer(spv_target_env target_env); + + // Disables copy/move constructor/assignment operations. + Reducer(const Reducer&) = delete; + Reducer(Reducer&&) = delete; + Reducer& operator=(const Reducer&) = delete; + Reducer& operator=(Reducer&&) = delete; + + // Destructs this instance. + ~Reducer(); + + // Sets the message consumer to the given |consumer|. The |consumer| will be + // invoked once for each message communicated from the library. + void SetMessageConsumer(MessageConsumer consumer); + + // Sets the function that will be used to decide whether a reduced binary + // turned out to be interesting. + void SetInterestingnessFunction( + InterestingnessFunction interestingness_function); + + // Adds all default reduction passes. + void AddDefaultReductionPasses(); + + // Adds a reduction pass based on the given finder to the sequence of passes + // that will be iterated over. + void AddReductionPass(std::unique_ptr&& finder); + + // Adds a cleanup reduction pass based on the given finder to the sequence of + // passes that will run after other passes. + void AddCleanupReductionPass( + std::unique_ptr&& finder); + + // Reduces the given SPIR-V module |binary_out|. + // The reduced binary ends up in |binary_out|. + // A status is returned. + ReductionResultStatus Run(std::vector&& binary_in, + std::vector* binary_out, + spv_const_reducer_options options, + spv_validator_options validator_options); + + private: + static bool ReachedStepLimit(uint32_t current_step, + spv_const_reducer_options options); + + ReductionResultStatus RunPasses( + std::vector>* passes, + spv_const_reducer_options options, + spv_validator_options validator_options, const SpirvTools& tools, + std::vector* current_binary, uint32_t* reductions_applied); + + const spv_target_env target_env_; + MessageConsumer consumer_; + InterestingnessFunction interestingness_function_; + std::vector> passes_; + std::vector> cleanup_passes_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCER_H_ diff --git a/third_party/spirv-tools/source/reduce/reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/reduction_opportunity.cpp new file mode 100644 index 0000000..77be784 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reduction_opportunity.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +void ReductionOpportunity::TryToApply() { + if (PreconditionHolds()) { + Apply(); + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/reduction_opportunity.h b/third_party/spirv-tools/source/reduce/reduction_opportunity.h new file mode 100644 index 0000000..703a50a --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reduction_opportunity.h @@ -0,0 +1,47 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_ + +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace reduce { + +// Abstract class: an opportunity to apply a reducing transformation. +class ReductionOpportunity { + public: + ReductionOpportunity() = default; + virtual ~ReductionOpportunity() = default; + + // Returns true if this opportunity has not been disabled by the application + // of another conflicting opportunity. + virtual bool PreconditionHolds() = 0; + + // Applies the opportunity, mutating the module from which the opportunity was + // created. It is a no-op if PreconditionHolds() returns false. + void TryToApply(); + + protected: + // Applies the opportunity, mutating the module from which the opportunity was + // created. + // Precondition: PreconditionHolds() must return true. + virtual void Apply() = 0; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/reduction_opportunity_finder.cpp new file mode 100644 index 0000000..0bd253b --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reduction_opportunity_finder.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +std::vector ReductionOpportunityFinder::GetTargetFunctions( + opt::IRContext* ir_context, uint32_t target_function) { + std::vector result; + for (auto& function : *ir_context->module()) { + if (!target_function || function.result_id() == target_function) { + result.push_back(&function); + } + } + assert((!target_function || !result.empty()) && + "Requested target function must exist."); + return result; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/reduction_opportunity_finder.h new file mode 100644 index 0000000..d95c832 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reduction_opportunity_finder.h @@ -0,0 +1,58 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// Abstract class for finding opportunities for reducing a SPIR-V module. +class ReductionOpportunityFinder { + public: + ReductionOpportunityFinder() = default; + + virtual ~ReductionOpportunityFinder() = default; + + // Finds and returns the reduction opportunities relevant to this pass that + // could be applied to SPIR-V module |context|. + // + // If |target_function| is non-zero then the available opportunities will be + // restricted to only those opportunities that modify the function with result + // id |target_function|. + virtual std::vector> + GetAvailableOpportunities(opt::IRContext* context, + uint32_t target_function) const = 0; + + // Provides a name for the finder. + virtual std::string GetName() const = 0; + + protected: + // Requires that |target_function| is zero or the id of a function in + // |ir_context|. If |target_function| is zero, returns all the functions in + // |ir_context|. Otherwise, returns the function with id |target_function|. + // This allows fuzzer passes to restrict attention to a single function. + static std::vector GetTargetFunctions( + opt::IRContext* ir_context, uint32_t target_function); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/reduction_pass.cpp b/third_party/spirv-tools/source/reduce/reduction_pass.cpp new file mode 100644 index 0000000..c6d1ebf --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reduction_pass.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reduction_pass.h" + +#include + +#include "source/opt/build_module.h" + +namespace spvtools { +namespace reduce { + +std::vector ReductionPass::TryApplyReduction( + const std::vector& binary, uint32_t target_function) { + // We represent modules as binaries because (a) attempts at reduction need to + // end up in binary form to be passed on to SPIR-V-consuming tools, and (b) + // when we apply a reduction step we need to do it on a fresh version of the + // module as if the reduction step proves to be uninteresting we need to + // backtrack; re-parsing from binary provides a very clean way of cloning the + // module. + std::unique_ptr context = + BuildModule(target_env_, consumer_, binary.data(), binary.size()); + assert(context); + + std::vector> opportunities = + finder_->GetAvailableOpportunities(context.get(), target_function); + + // There is no point in having a granularity larger than the number of + // opportunities, so reduce the granularity in this case. + if (granularity_ > opportunities.size()) { + granularity_ = std::max((uint32_t)1, (uint32_t)opportunities.size()); + } + + assert(granularity_ > 0); + + if (index_ >= opportunities.size()) { + // We have reached the end of the available opportunities and, therefore, + // the end of the round for this pass, so reset the index and decrease the + // granularity for the next round. Return an empty vector to signal the end + // of the round. + index_ = 0; + granularity_ = std::max((uint32_t)1, granularity_ / 2); + return std::vector(); + } + + for (uint32_t i = index_; + i < std::min(index_ + granularity_, (uint32_t)opportunities.size()); + ++i) { + opportunities[i]->TryToApply(); + } + + std::vector result; + context->module()->ToBinary(&result, false); + return result; +} + +void ReductionPass::SetMessageConsumer(MessageConsumer consumer) { + consumer_ = std::move(consumer); +} + +bool ReductionPass::ReachedMinimumGranularity() const { + assert(granularity_ != 0); + return granularity_ == 1; +} + +std::string ReductionPass::GetName() const { return finder_->GetName(); } + +void ReductionPass::NotifyInteresting(bool interesting) { + if (!interesting) { + index_ += granularity_; + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/reduction_pass.h b/third_party/spirv-tools/source/reduce/reduction_pass.h new file mode 100644 index 0000000..1836182 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reduction_pass.h @@ -0,0 +1,86 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCTION_PASS_H_ +#define SOURCE_REDUCE_REDUCTION_PASS_H_ + +#include + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity_finder.h" +#include "spirv-tools/libspirv.hpp" + +namespace spvtools { +namespace reduce { + +// Abstract class representing a reduction pass, which can be repeatedly +// invoked to find and apply particular reduction opportunities to a SPIR-V +// binary. In the spirit of delta debugging, a pass initially tries to apply +// large chunks of reduction opportunities, iterating through available +// opportunities at a given granularity. When an iteration over available +// opportunities completes, the granularity is reduced and iteration starts +// again, until the minimum granularity is reached. +class ReductionPass { + public: + // Constructs a reduction pass with a given target environment, |target_env|, + // and a given finder of reduction opportunities, |finder|. + explicit ReductionPass(const spv_target_env target_env, + std::unique_ptr finder) + : target_env_(target_env), + finder_(std::move(finder)), + index_(0), + granularity_(std::numeric_limits::max()) {} + + // Applies the reduction pass to the given binary by applying a "chunk" of + // reduction opportunities. Returns the new binary if a chunk was applied; in + // this case, before the next call the caller must invoke + // NotifyInteresting(...) to indicate whether the new binary is interesting. + // Returns an empty vector if there are no more chunks left to apply; in this + // case, the index will be reset and the granularity lowered for the next + // round. + // + // If |target_function| is non-zero, only reduction opportunities that + // simplify the internals of the function with result id |target_function| + // will be applied. + std::vector TryApplyReduction(const std::vector& binary, + uint32_t target_function); + + // Notifies the reduction pass whether the binary returned from + // TryApplyReduction is interesting, so that the next call to + // TryApplyReduction will avoid applying the same chunk of opportunities. + void NotifyInteresting(bool interesting); + + // Sets a consumer to which relevant messages will be directed. + void SetMessageConsumer(MessageConsumer consumer); + + // Returns true if the granularity with which reduction opportunities are + // applied has reached a minimum. + bool ReachedMinimumGranularity() const; + + // Returns the name associated with this reduction pass (based on its + // associated finder). + std::string GetName() const; + + private: + const spv_target_env target_env_; + const std::unique_ptr finder_; + MessageConsumer consumer_; + uint32_t index_; + uint32_t granularity_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_PASS_H_ diff --git a/third_party/spirv-tools/source/reduce/reduction_util.cpp b/third_party/spirv-tools/source/reduce/reduction_util.cpp new file mode 100644 index 0000000..511f432 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reduction_util.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/reduction_util.h" + +#include "source/opt/ir_context.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace reduce { + +const uint32_t kTrueBranchOperandIndex = 1; +const uint32_t kFalseBranchOperandIndex = 2; + +uint32_t FindOrCreateGlobalVariable(opt::IRContext* context, + uint32_t pointer_type_id) { + for (auto& inst : context->module()->types_values()) { + if (inst.opcode() != SpvOpVariable) { + continue; + } + if (inst.type_id() == pointer_type_id) { + return inst.result_id(); + } + } + const uint32_t variable_id = context->TakeNextId(); + auto variable_inst = MakeUnique( + context, SpvOpVariable, pointer_type_id, variable_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_STORAGE_CLASS, + {static_cast(context->get_type_mgr() + ->GetType(pointer_type_id) + ->AsPointer() + ->storage_class())}}})); + context->module()->AddGlobalValue(std::move(variable_inst)); + return variable_id; +} + +uint32_t FindOrCreateFunctionVariable(opt::IRContext* context, + opt::Function* function, + uint32_t pointer_type_id) { + // The pointer type of a function variable must have Function storage class. + assert(context->get_type_mgr() + ->GetType(pointer_type_id) + ->AsPointer() + ->storage_class() == SpvStorageClassFunction); + + // Go through the instructions in the function's first block until we find a + // suitable variable, or go past all the variables. + opt::BasicBlock::iterator iter = function->begin()->begin(); + for (;; ++iter) { + // We will either find a suitable variable, or find a non-variable + // instruction; we won't exhaust all instructions. + assert(iter != function->begin()->end()); + if (iter->opcode() != SpvOpVariable) { + // If we see a non-variable, we have gone through all the variables. + break; + } + if (iter->type_id() == pointer_type_id) { + return iter->result_id(); + } + } + // At this point, iter refers to the first non-function instruction of the + // function's entry block. + const uint32_t variable_id = context->TakeNextId(); + auto variable_inst = MakeUnique( + context, SpvOpVariable, pointer_type_id, variable_id, + opt::Instruction::OperandList( + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); + iter->InsertBefore(std::move(variable_inst)); + return variable_id; +} + +uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id) { + for (auto& inst : context->module()->types_values()) { + if (inst.opcode() != SpvOpUndef) { + continue; + } + if (inst.type_id() == type_id) { + return inst.result_id(); + } + } + const uint32_t undef_id = context->TakeNextId(); + auto undef_inst = MakeUnique( + context, SpvOpUndef, type_id, undef_id, opt::Instruction::OperandList()); + assert(undef_id == undef_inst->result_id()); + context->module()->AddGlobalValue(std::move(undef_inst)); + return undef_id; +} + +void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, + opt::BasicBlock* to_block) { + to_block->ForEachPhiInst([&from_id](opt::Instruction* phi_inst) { + opt::Instruction::OperandList new_in_operands; + // Go through the OpPhi's input operands in (variable, parent) pairs. + for (uint32_t index = 0; index < phi_inst->NumInOperands(); index += 2) { + // Keep all pairs where the parent is not the block from which the edge + // is being removed. + if (phi_inst->GetInOperand(index + 1).words[0] != from_id) { + new_in_operands.push_back(phi_inst->GetInOperand(index)); + new_in_operands.push_back(phi_inst->GetInOperand(index + 1)); + } + } + phi_inst->SetInOperands(std::move(new_in_operands)); + }); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/reduction_util.h b/third_party/spirv-tools/source/reduce/reduction_util.h new file mode 100644 index 0000000..bcdb77c --- /dev/null +++ b/third_party/spirv-tools/source/reduce/reduction_util.h @@ -0,0 +1,51 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REDUCTION_UTIL_H_ +#define SOURCE_REDUCE_REDUCTION_UTIL_H_ + +#include "spirv-tools/libspirv.hpp" + +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +extern const uint32_t kTrueBranchOperandIndex; +extern const uint32_t kFalseBranchOperandIndex; + +// Returns a global OpVariable of type |pointer_type_id|, adding one if none +// exist. +uint32_t FindOrCreateGlobalVariable(opt::IRContext* context, + uint32_t pointer_type_id); + +// Returns an OpVariable of type |pointer_type_id| declared in |function|, +// adding one if none exist. +uint32_t FindOrCreateFunctionVariable(opt::IRContext* context, opt::Function*, + uint32_t pointer_type_id); + +// Returns an OpUndef id from the global value list that is of the given type, +// adding one if it does not exist. +uint32_t FindOrCreateGlobalUndef(opt::IRContext* context, uint32_t type_id); + +// Removes any components of |to_block|'s phi instructions relating to +// |from_id|. +void AdaptPhiInstructionsForRemovedEdge(uint32_t from_id, + opt::BasicBlock* to_block); + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REDUCTION_UTIL_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity.cpp new file mode 100644 index 0000000..aa48105 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_block_reduction_opportunity.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +RemoveBlockReductionOpportunity::RemoveBlockReductionOpportunity( + opt::Function* function, opt::BasicBlock* block) + : function_(function), block_(block) { + // precondition: + assert(block_->begin() != block_->end() && + block_->begin()->context()->get_def_use_mgr()->NumUsers( + block_->id()) == 0 && + "RemoveBlockReductionOpportunity block must have 0 references"); +} + +bool RemoveBlockReductionOpportunity::PreconditionHolds() { + // Removing other blocks cannot disable this opportunity. + return true; +} + +void RemoveBlockReductionOpportunity::Apply() { + // We need an iterator pointing to the block, hence the loop. + for (auto bi = function_->begin(); bi != function_->end(); ++bi) { + if (bi->id() == block_->id()) { + bi->KillAllInsts(true); + bi.Erase(); + // Block removal changes the function, but we don't use analyses, so no + // need to invalidate them. + return; + } + } + + assert(false && + "Unreachable: we should have found a block with the desired id."); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity.h new file mode 100644 index 0000000..4b358ab --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity.h @@ -0,0 +1,46 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to remove an unreferenced block. +// See RemoveBlockReductionOpportunityFinder. +class RemoveBlockReductionOpportunity : public ReductionOpportunity { + public: + // Creates the opportunity to remove |block| in |function| in |context|. + RemoveBlockReductionOpportunity(opt::Function* function, + opt::BasicBlock* block); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Function* function_; + opt::BasicBlock* block_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..27a4570 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.cpp @@ -0,0 +1,95 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_block_reduction_opportunity_finder.h" + +#include "source/reduce/remove_block_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::string RemoveBlockReductionOpportunityFinder::GetName() const { + return "RemoveBlockReductionOpportunityFinder"; +} + +std::vector> +RemoveBlockReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every block in every relevant function. + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto bi = function->begin(); bi != function->end(); ++bi) { + if (IsBlockValidOpportunity(context, function, &bi)) { + result.push_back( + MakeUnique(function, &*bi)); + } + } + } + return result; +} + +bool RemoveBlockReductionOpportunityFinder::IsBlockValidOpportunity( + opt::IRContext* context, opt::Function* function, + opt::Function::iterator* bi) { + assert(*bi != function->end() && "Block iterator was out of bounds"); + + // Don't remove first block; we don't want to end up with no blocks. + if (*bi == function->begin()) { + return false; + } + + // Don't remove blocks with references. + if (context->get_def_use_mgr()->NumUsers((*bi)->id()) > 0) { + return false; + } + + // Don't remove blocks whose instructions have outside references. + if (!BlockInstructionsHaveNoOutsideReferences(context, *bi)) { + return false; + } + + return true; +} + +bool RemoveBlockReductionOpportunityFinder:: + BlockInstructionsHaveNoOutsideReferences( + opt::IRContext* context, const opt::Function::iterator& bi) { + // Get all instructions in block. + std::unordered_set instructions_in_block; + for (const opt::Instruction& instruction : *bi) { + instructions_in_block.insert(instruction.unique_id()); + } + + // For each instruction... + for (const opt::Instruction& instruction : *bi) { + // For each use of the instruction... + bool no_uses_outside_block = context->get_def_use_mgr()->WhileEachUser( + &instruction, [&instructions_in_block](opt::Instruction* user) -> bool { + // If the use is in this block, continue (return true). Otherwise, we + // found an outside use; return false (and stop). + return instructions_in_block.find(user->unique_id()) != + instructions_in_block.end(); + }); + + if (!no_uses_outside_block) { + return false; + } + } + + return true; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.h new file mode 100644 index 0000000..d347bf9 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_block_reduction_opportunity_finder.h @@ -0,0 +1,55 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to remove a block. The optimizer can remove dead +// code. However, the reducer needs to be able to remove at a fine-grained +// level. +class RemoveBlockReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveBlockReductionOpportunityFinder() = default; + + ~RemoveBlockReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // Returns true if the block |bi| in function |function| is a valid + // opportunity according to various restrictions. + static bool IsBlockValidOpportunity(opt::IRContext* context, + opt::Function* function, + opt::Function::iterator* bi); + + // Returns true if the instructions (definitions) in block |bi| have no + // references, except for references from inside the block itself. + static bool BlockInstructionsHaveNoOutsideReferences( + opt::IRContext* context, const opt::Function::iterator& bi); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_BLOCK_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp new file mode 100644 index 0000000..ecad670 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_function_reduction_opportunity.h" + +#include "source/opt/eliminate_dead_functions_util.h" + +namespace spvtools { +namespace reduce { + +bool RemoveFunctionReductionOpportunity::PreconditionHolds() { + // Removing one function cannot influence whether another function can be + // removed. + return true; +} + +void RemoveFunctionReductionOpportunity::Apply() { + for (opt::Module::iterator function_it = context_->module()->begin(); + function_it != context_->module()->end(); ++function_it) { + if (&*function_it == function_) { + opt::eliminatedeadfunctionsutil::EliminateFunction(context_, + &function_it); + return; + } + } + assert(0 && "Function to be removed was not found."); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity.h new file mode 100644 index 0000000..d8c57db --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to remove an unreferenced function. +class RemoveFunctionReductionOpportunity : public ReductionOpportunity { + public: + // Creates an opportunity to remove |function| from the module represented by + // |context|. + RemoveFunctionReductionOpportunity(opt::IRContext* context, + opt::Function* function) + : context_(context), function_(function) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // The IR context for the module under analysis. + opt::IRContext* context_; + + // The function that can be removed. + opt::Function* function_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..1d8d972 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_function_reduction_opportunity_finder.h" + +#include "source/reduce/remove_function_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +RemoveFunctionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + if (target_function) { + // If we are targeting a specific function then we are only interested in + // opportunities that simplify the internals of that function; removing + // whole functions does not fit the bill. + return {}; + } + + std::vector> result; + // Consider each function. + for (auto& function : *context->module()) { + if (context->get_def_use_mgr()->NumUses(function.result_id()) > 0) { + // If the function is referenced, ignore it. + continue; + } + result.push_back( + MakeUnique(context, &function)); + } + return result; +} + +std::string RemoveFunctionReductionOpportunityFinder::GetName() const { + return "RemoveFunctionReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h new file mode 100644 index 0000000..6fcfb77 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_function_reduction_opportunity_finder.h @@ -0,0 +1,42 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder of opportunities to remove unreferenced functions. +class RemoveFunctionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveFunctionReductionOpportunityFinder() = default; + + ~RemoveFunctionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_FUNCTION_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.cpp new file mode 100644 index 0000000..8026204 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_instruction_reduction_opportunity.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +bool RemoveInstructionReductionOpportunity::PreconditionHolds() { return true; } + +void RemoveInstructionReductionOpportunity::Apply() { + const uint32_t kNumEntryPointInOperandsBeforeInterfaceIds = 3; + for (auto& entry_point : inst_->context()->module()->entry_points()) { + opt::Instruction::OperandList new_entry_point_in_operands; + for (uint32_t index = 0; index < entry_point.NumInOperands(); index++) { + if (index >= kNumEntryPointInOperandsBeforeInterfaceIds && + entry_point.GetSingleWordInOperand(index) == inst_->result_id()) { + continue; + } + new_entry_point_in_operands.push_back(entry_point.GetInOperand(index)); + } + entry_point.SetInOperands(std::move(new_entry_point_in_operands)); + } + inst_->context()->KillInst(inst_); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.h new file mode 100644 index 0000000..07bef50 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_instruction_reduction_opportunity.h @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to remove an instruction from the SPIR-V module. +class RemoveInstructionReductionOpportunity : public ReductionOpportunity { + public: + // Constructs the opportunity to remove |inst|. + explicit RemoveInstructionReductionOpportunity(opt::Instruction* inst) + : inst_(inst) {} + + // Always returns true, as this opportunity can always be applied. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Instruction* inst_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_INSTRUCTION_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity.cpp new file mode 100644 index 0000000..96f0147 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_selection_reduction_opportunity.h" + +#include "source/opt/basic_block.h" +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +bool RemoveSelectionReductionOpportunity::PreconditionHolds() { return true; } + +void RemoveSelectionReductionOpportunity::Apply() { + auto merge_instruction = header_block_->GetMergeInst(); + merge_instruction->context()->KillInst(merge_instruction); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity.h new file mode 100644 index 0000000..892618e --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity.h @@ -0,0 +1,47 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/basic_block.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity for removing a selection construct by simply removing the +// OpSelectionMerge instruction; thus, the selection must have already been +// simplified to a point where the instruction can be trivially removed. +class RemoveSelectionReductionOpportunity : public ReductionOpportunity { + public: + // Constructs a reduction opportunity from the selection header |block| in + // |function|. + RemoveSelectionReductionOpportunity(opt::BasicBlock* header_block) + : header_block_(header_block) {} + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // The header block of the selection. + opt::BasicBlock* header_block_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..74df1b8 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_selection_reduction_opportunity_finder.h" + +#include "source/reduce/remove_selection_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +namespace { +const uint32_t kMergeNodeIndex = 0; +const uint32_t kContinueNodeIndex = 1; +} // namespace + +std::string RemoveSelectionReductionOpportunityFinder::GetName() const { + return "RemoveSelectionReductionOpportunityFinder"; +} + +std::vector> +RemoveSelectionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + // Get all loop merge and continue blocks so we can check for these later. + std::unordered_set merge_and_continue_blocks_from_loops; + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + if (auto merge_instruction = block.GetMergeInst()) { + if (merge_instruction->opcode() == SpvOpLoopMerge) { + uint32_t merge_block_id = + merge_instruction->GetSingleWordOperand(kMergeNodeIndex); + uint32_t continue_block_id = + merge_instruction->GetSingleWordOperand(kContinueNodeIndex); + merge_and_continue_blocks_from_loops.insert(merge_block_id); + merge_and_continue_blocks_from_loops.insert(continue_block_id); + } + } + } + } + + // Return all selection headers where the OpSelectionMergeInstruction can be + // removed. + std::vector> result; + for (auto& function : *context->module()) { + for (auto& block : function) { + if (auto merge_instruction = block.GetMergeInst()) { + if (merge_instruction->opcode() == SpvOpSelectionMerge) { + if (CanOpSelectionMergeBeRemoved( + context, block, merge_instruction, + merge_and_continue_blocks_from_loops)) { + result.push_back( + MakeUnique(&block)); + } + } + } + } + } + return result; +} + +bool RemoveSelectionReductionOpportunityFinder::CanOpSelectionMergeBeRemoved( + opt::IRContext* context, const opt::BasicBlock& header_block, + opt::Instruction* merge_instruction, + std::unordered_set merge_and_continue_blocks_from_loops) { + assert(header_block.GetMergeInst() == merge_instruction && + "CanOpSelectionMergeBeRemoved(...): header block and merge " + "instruction mismatch"); + + // The OpSelectionMerge instruction is needed if either of the following are + // true. + // + // 1. The header block has at least two (unique) successors that are not + // merge or continue blocks of a loop. + // + // 2. The predecessors of the merge block are "using" the merge block to avoid + // divergence. In other words, there exists a predecessor of the merge block + // that has a successor that is not the merge block of this construct and not + // a merge or continue block of a loop. + + // 1. + { + uint32_t divergent_successor_count = 0; + + std::unordered_set seen_successors; + + header_block.ForEachSuccessorLabel( + [&seen_successors, &merge_and_continue_blocks_from_loops, + &divergent_successor_count](uint32_t successor) { + // Not already seen. + if (seen_successors.find(successor) == seen_successors.end()) { + seen_successors.insert(successor); + // Not a loop continue or merge. + if (merge_and_continue_blocks_from_loops.find(successor) == + merge_and_continue_blocks_from_loops.end()) { + ++divergent_successor_count; + } + } + }); + + if (divergent_successor_count > 1) { + return false; + } + } + + // 2. + { + uint32_t merge_block_id = + merge_instruction->GetSingleWordOperand(kMergeNodeIndex); + for (uint32_t predecessor_block_id : + context->cfg()->preds(merge_block_id)) { + const opt::BasicBlock* predecessor_block = + context->cfg()->block(predecessor_block_id); + assert(predecessor_block); + bool found_divergent_successor = false; + predecessor_block->ForEachSuccessorLabel( + [&found_divergent_successor, merge_block_id, + &merge_and_continue_blocks_from_loops](uint32_t successor_id) { + // The successor is not the merge block, nor a loop merge or + // continue. + if (successor_id != merge_block_id && + merge_and_continue_blocks_from_loops.find(successor_id) == + merge_and_continue_blocks_from_loops.end()) { + found_divergent_successor = true; + } + }); + if (found_divergent_successor) { + return false; + } + } + } + + return true; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.h new file mode 100644 index 0000000..1a17493 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_selection_reduction_opportunity_finder.h @@ -0,0 +1,49 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities for removing a selection construct by simply +// removing the OpSelectionMerge instruction; thus, the selections must have +// already been simplified to a point where they can be trivially removed. +class RemoveSelectionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveSelectionReductionOpportunityFinder() = default; + + ~RemoveSelectionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + // Returns true if the OpSelectionMerge instruction |merge_instruction| in + // block |header_block| can be removed. + static bool CanOpSelectionMergeBeRemoved( + opt::IRContext* context, const opt::BasicBlock& header_block, + opt::Instruction* merge_instruction, + std::unordered_set merge_and_continue_blocks_from_loops); +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.cpp new file mode 100644 index 0000000..787c629 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.cpp @@ -0,0 +1,208 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_struct_member_reduction_opportunity.h" + +#include "source/opt/ir_context.h" + +namespace spvtools { +namespace reduce { + +bool RemoveStructMemberReductionOpportunity::PreconditionHolds() { + return struct_type_->NumInOperands() == original_number_of_members_; +} + +void RemoveStructMemberReductionOpportunity::Apply() { + std::set decorations_to_kill; + + // We need to remove decorations that target the removed struct member, and + // adapt decorations that target later struct members by decrementing the + // member identifier. We also need to adapt composite construction + // instructions so that no id is provided for the member being removed. + // + // To do this, we consider every use of the struct type. + struct_type_->context()->get_def_use_mgr()->ForEachUse( + struct_type_, [this, &decorations_to_kill](opt::Instruction* user, + uint32_t /*operand_index*/) { + switch (user->opcode()) { + case SpvOpCompositeConstruct: + case SpvOpConstantComposite: + // This use is constructing a composite of the struct type, so we + // must remove the id that was provided for the member we are + // removing. + user->RemoveInOperand(member_index_); + break; + case SpvOpMemberDecorate: + // This use is decorating a member of the struct. + if (user->GetSingleWordInOperand(1) == member_index_) { + // The member we are removing is being decorated, so we record + // that we need to get rid of the decoration. + decorations_to_kill.insert(user); + } else if (user->GetSingleWordInOperand(1) > member_index_) { + // A member beyond the one we are removing is being decorated, so + // we adjust the index that identifies the member. + user->SetInOperand(1, {user->GetSingleWordInOperand(1) - 1}); + } + break; + default: + break; + } + }); + + // Get rid of all the decorations that were found to target the member being + // removed. + for (auto decoration_to_kill : decorations_to_kill) { + decoration_to_kill->context()->KillInst(decoration_to_kill); + } + + // We now look through all instructions that access composites via sequences + // of indices. Every time we find an index into the struct whose member is + // being removed, and if the member being accessed comes after the member + // being removed, we need to adjust the index accordingly. + // + // We go through every relevant instruction in every block of every function, + // and invoke a helper to adjust it. + auto context = struct_type_->context(); + for (auto& function : *context->module()) { + for (auto& block : function) { + for (auto& inst : block) { + switch (inst.opcode()) { + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: { + // These access chain instructions take sequences of ids for + // indexing, starting from input operand 1. + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(0)) + ->type_id()) + ->GetSingleWordInOperand(1); + AdjustAccessedIndices(composite_type_id, 1, false, context, &inst); + } break; + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: { + // These access chain instructions take sequences of ids for + // indexing, starting from input operand 2. + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(1)) + ->type_id()) + ->GetSingleWordInOperand(1); + AdjustAccessedIndices(composite_type_id, 2, false, context, &inst); + } break; + case SpvOpCompositeExtract: { + // OpCompositeExtract uses literals for indexing, starting at input + // operand 1. + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(0)) + ->type_id(); + AdjustAccessedIndices(composite_type_id, 1, true, context, &inst); + } break; + case SpvOpCompositeInsert: { + // OpCompositeInsert uses literals for indexing, starting at input + // operand 2. + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(1)) + ->type_id(); + AdjustAccessedIndices(composite_type_id, 2, true, context, &inst); + } break; + default: + break; + } + } + } + } + + // Remove the member from the struct type. + struct_type_->RemoveInOperand(member_index_); +} + +void RemoveStructMemberReductionOpportunity::AdjustAccessedIndices( + uint32_t composite_type_id, uint32_t first_index_input_operand, + bool literal_indices, opt::IRContext* context, + opt::Instruction* composite_access_instruction) const { + // Walk the series of types that are encountered by following the + // instruction's sequence of indices. For all types except structs, this is + // routine: the type of the composite dictates what the next type will be + // regardless of the specific index value. + uint32_t next_type = composite_type_id; + for (uint32_t i = first_index_input_operand; + i < composite_access_instruction->NumInOperands(); i++) { + auto type_inst = context->get_def_use_mgr()->GetDef(next_type); + switch (type_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeMatrix: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + next_type = type_inst->GetSingleWordInOperand(0); + break; + case SpvOpTypeStruct: { + // Struct types are special becuase (a) we may need to adjust the index + // being used, if the struct type is the one from which we are removing + // a member, and (b) the type encountered by following the current index + // is dependent on the value of the index. + + // Work out the member being accessed. If literal indexing is used this + // is simple; otherwise we need to look up the id of the constant + // instruction being used as an index and get the value of the constant. + uint32_t index_operand = + composite_access_instruction->GetSingleWordInOperand(i); + uint32_t member = literal_indices ? index_operand + : context->get_def_use_mgr() + ->GetDef(index_operand) + ->GetSingleWordInOperand(0); + + // The next type we will consider is obtained by looking up the struct + // type at |member|. + next_type = type_inst->GetSingleWordInOperand(member); + + if (type_inst == struct_type_ && member > member_index_) { + // The struct type is the struct from which we are removing a member, + // and the member being accessed is beyond the member we are removing. + // We thus need to decrement the index by 1. + uint32_t new_in_operand; + if (literal_indices) { + // With literal indexing this is straightforward. + new_in_operand = member - 1; + } else { + // With id-based indexing this is more tricky: we need to find or + // create a constant instruction whose value is one less than + // |member|, and use the id of this constant as the replacement + // input operand. + auto constant_inst = + context->get_def_use_mgr()->GetDef(index_operand); + auto int_type = context->get_type_mgr() + ->GetType(constant_inst->type_id()) + ->AsInteger(); + auto new_index_constant = + opt::analysis::IntConstant(int_type, {member - 1}); + new_in_operand = context->get_constant_mgr() + ->GetDefiningInstruction(&new_index_constant) + ->result_id(); + } + composite_access_instruction->SetInOperand(i, {new_in_operand}); + } + } break; + default: + assert(0 && "Unknown composite type."); + break; + } + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.h new file mode 100644 index 0000000..899e5ea --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_struct_member_reduction_opportunity.h @@ -0,0 +1,84 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_ + +#include "source/reduce/reduction_opportunity.h" + +#include "source/opt/instruction.h" + +namespace spvtools { +namespace reduce { + +// An opportunity for removing a member from a struct type, adjusting all uses +// of the struct accordingly. +class RemoveStructMemberReductionOpportunity : public ReductionOpportunity { + public: + // Constructs a reduction opportunity from the struct type |struct_type|, for + // removal of member |member_index|. + RemoveStructMemberReductionOpportunity(opt::Instruction* struct_type, + uint32_t member_index) + : struct_type_(struct_type), + member_index_(member_index), + original_number_of_members_(struct_type->NumInOperands()) {} + + // Opportunities to remove fields from a common struct type mutually + // invalidate each other. We guard against this by requiring that the struct + // still has the number of members it had when the opportunity was created. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // |composite_access_instruction| is an instruction that accesses a composite + // id using either a series of literal indices (e.g. in the case of + // OpCompositeInsert) or a series of index ids (e.g. in the case of + // OpAccessChain). + // + // This function adjusts the indices that are used by + // |composite_access_instruction| to that whenever an index is accessing a + // member of |struct_type_|, it is decremented if the member is beyond + // |member_index_|, to account for the removal of the |member_index_|-th + // member. + // + // |composite_type_id| is the id of the composite type that the series of + // indices is to be applied to. + // + // |first_index_input_operand| specifies the first input operand that is an + // index. + // + // |literal_indices| specifies whether indices are given as literals (true), + // or as ids (false). + // + // If id-based indexing is used, this function will add a constant for + // |member_index_| - 1 to the module if needed. + void AdjustAccessedIndices( + uint32_t composite_type_id, uint32_t first_index_input_operand, + bool literal_indices, opt::IRContext* context, + opt::Instruction* composite_access_instruction) const; + + // The struct type from which a member is to be removed. + opt::Instruction* struct_type_; + + uint32_t member_index_; + + uint32_t original_number_of_members_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..d7bb3a8 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.cpp @@ -0,0 +1,176 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_unused_instruction_reduction_opportunity_finder.h" + +#include "source/opcode.h" +#include "source/opt/instruction.h" +#include "source/reduce/remove_instruction_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +RemoveUnusedInstructionReductionOpportunityFinder:: + RemoveUnusedInstructionReductionOpportunityFinder( + bool remove_constants_and_undefs) + : remove_constants_and_undefs_(remove_constants_and_undefs) {} + +std::vector> +RemoveUnusedInstructionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + if (!target_function) { + // We are not restricting reduction to a specific function, so we consider + // unused instructions defined outside functions. + + for (auto& inst : context->module()->debugs1()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->debugs2()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->debugs3()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->ext_inst_debuginfo()) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->types_values()) { + if (!remove_constants_and_undefs_ && + spvOpcodeIsConstantOrUndef(inst.opcode())) { + continue; + } + if (!OnlyReferencedByIntimateDecorationOrEntryPointInterface(context, + inst)) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + + for (auto& inst : context->module()->annotations()) { + if (context->get_def_use_mgr()->NumUsers(&inst) > 0) { + continue; + } + if (!IsIndependentlyRemovableDecoration(inst)) { + continue; + } + result.push_back( + MakeUnique(&inst)); + } + } + + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + for (auto& inst : block) { + if (context->get_def_use_mgr()->NumUses(&inst) > 0) { + continue; + } + if (!remove_constants_and_undefs_ && + spvOpcodeIsConstantOrUndef(inst.opcode())) { + continue; + } + if (spvOpcodeIsBlockTerminator(inst.opcode()) || + inst.opcode() == SpvOpSelectionMerge || + inst.opcode() == SpvOpLoopMerge) { + // In this reduction pass we do not want to affect static + // control flow. + continue; + } + // Given that we're in a block, we should only get here if + // the instruction is not directly related to control flow; + // i.e., it's some straightforward instruction with an + // unused result, like an arithmetic operation or function + // call. + result.push_back( + MakeUnique(&inst)); + } + } + } + return result; +} + +std::string RemoveUnusedInstructionReductionOpportunityFinder::GetName() const { + return "RemoveUnusedInstructionReductionOpportunityFinder"; +} + +bool RemoveUnusedInstructionReductionOpportunityFinder:: + OnlyReferencedByIntimateDecorationOrEntryPointInterface( + opt::IRContext* context, const opt::Instruction& inst) const { + return context->get_def_use_mgr()->WhileEachUse( + &inst, [this](opt::Instruction* user, uint32_t use_index) -> bool { + return (user->IsDecoration() && + !IsIndependentlyRemovableDecoration(*user)) || + (user->opcode() == SpvOpEntryPoint && use_index > 2); + }); +} + +bool RemoveUnusedInstructionReductionOpportunityFinder:: + IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const { + uint32_t decoration; + switch (inst.opcode()) { + case SpvOpDecorate: + case SpvOpDecorateId: + case SpvOpDecorateString: + decoration = inst.GetSingleWordInOperand(1u); + break; + case SpvOpMemberDecorate: + case SpvOpMemberDecorateString: + decoration = inst.GetSingleWordInOperand(2u); + break; + default: + // The instruction is not a decoration. It is legitimate for this to be + // reached: it allows the method to be invoked on arbitrary instructions. + return false; + } + + // We conservatively only remove specific decorations that we believe will + // not change the shader interface, will not make the shader invalid, will + // actually be found in practice, etc. + + switch (decoration) { + case SpvDecorationRelaxedPrecision: + case SpvDecorationNoSignedWrap: + case SpvDecorationNoContraction: + case SpvDecorationNoUnsignedWrap: + case SpvDecorationUserSemantic: + return true; + default: + return false; + } +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h new file mode 100644 index 0000000..0323640 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_unused_instruction_reduction_opportunity_finder.h @@ -0,0 +1,61 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to remove non-control-flow instructions in blocks +// in cases where the instruction's id is either not referenced at all, or +// referenced only in a trivial manner (for example, we regard a struct type as +// unused if it is referenced only by struct layout decorations). As well as +// making the module smaller, removing an instruction that references particular +// ids may create opportunities for subsequently removing the instructions that +// generated those ids. +class RemoveUnusedInstructionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + explicit RemoveUnusedInstructionReductionOpportunityFinder( + bool remove_constants_and_undefs); + + ~RemoveUnusedInstructionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // Returns true if and only if the only uses of |inst| are by decorations that + // relate intimately to the instruction (as opposed to decorations that could + // be removed independently), or by interface ids in OpEntryPoint. + bool OnlyReferencedByIntimateDecorationOrEntryPointInterface( + opt::IRContext* context, const opt::Instruction& inst) const; + + // Returns true if and only if |inst| is a decoration instruction that can + // legitimately be removed on its own (rather than one that has to be removed + // simultaneously with other instructions). + bool IsIndependentlyRemovableDecoration(const opt::Instruction& inst) const; + + bool remove_constants_and_undefs_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_UNREFERENCED_INSTRUCTION_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..e72be62 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h" + +#include +#include + +#include "source/reduce/remove_struct_member_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +RemoveUnusedStructMemberReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + if (target_function) { + // Removing an unused struct member is a global change, as struct types are + // global. We thus do not consider such opportunities if we are targeting + // a specific function. + return {}; + } + + std::vector> result; + + // We track those struct members that are never accessed. We do this by + // associating a member index to all the structs that have this member index + // but do not use it. This representation is designed to allow reduction + // opportunities to be provided in a useful manner, so that opportunities + // associated with the same struct are unlikely to be adjacent. + std::map> unused_member_to_structs; + + // Consider every struct type in the module. + for (auto& type_or_value : context->types_values()) { + if (type_or_value.opcode() != SpvOpTypeStruct) { + continue; + } + + // Initially, we assume that *every* member of the struct is unused. We + // then refine this based on observed uses. + std::set unused_members; + for (uint32_t i = 0; i < type_or_value.NumInOperands(); i++) { + unused_members.insert(i); + } + + // A separate reduction pass deals with removal of names. If a struct + // member is still named, we treat it as being used. + context->get_def_use_mgr()->ForEachUse( + &type_or_value, + [&unused_members](opt::Instruction* user, uint32_t /*operand_index*/) { + switch (user->opcode()) { + case SpvOpMemberName: + unused_members.erase(user->GetSingleWordInOperand(1)); + break; + default: + break; + } + }); + + for (uint32_t member : unused_members) { + if (!unused_member_to_structs.count(member)) { + unused_member_to_structs.insert( + {member, std::set()}); + } + unused_member_to_structs.at(member).insert(&type_or_value); + } + } + + // We now go through every instruction that might index into a struct, and + // refine our tracking of which struct members are used based on the struct + // indexing we observe. We cannot just go through all uses of a struct type + // because the type is not necessarily even referenced, e.g. when walking + // arrays of structs. + for (auto& function : *context->module()) { + for (auto& block : function) { + for (auto& inst : block) { + switch (inst.opcode()) { + // For each indexing operation we observe, we invoke a helper to + // remove from our map those struct indices that are found to be used. + // The way the helper is invoked depends on whether the instruction + // uses literal or id indices, and the offset into the instruction's + // input operands from which index operands are provided. + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: { + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(0)) + ->type_id()) + ->GetSingleWordInOperand(1); + MarkAccessedMembersAsUsed(context, composite_type_id, 1, false, + inst, &unused_member_to_structs); + } break; + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: { + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(1)) + ->type_id()) + ->GetSingleWordInOperand(1); + MarkAccessedMembersAsUsed(context, composite_type_id, 2, false, + inst, &unused_member_to_structs); + } break; + case SpvOpCompositeExtract: { + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(0)) + ->type_id(); + MarkAccessedMembersAsUsed(context, composite_type_id, 1, true, inst, + &unused_member_to_structs); + } break; + case SpvOpCompositeInsert: { + auto composite_type_id = + context->get_def_use_mgr() + ->GetDef(inst.GetSingleWordInOperand(1)) + ->type_id(); + MarkAccessedMembersAsUsed(context, composite_type_id, 2, true, inst, + &unused_member_to_structs); + } break; + default: + break; + } + } + } + } + + // We now know those struct indices that are unsed, and we make a reduction + // opportunity for each of them. By mapping each relevant member index to the + // structs in which it is unsed, we will group all opportunities to remove + // member k of a struct (for some k) together. This reduces the likelihood + // that opportunities to remove members from the same struct will be adjacent, + // which is good because such opportunities mutually disable one another. + for (auto& entry : unused_member_to_structs) { + for (auto struct_type : entry.second) { + result.push_back(MakeUnique( + struct_type, entry.first)); + } + } + return result; +} + +void RemoveUnusedStructMemberReductionOpportunityFinder:: + MarkAccessedMembersAsUsed( + opt::IRContext* context, uint32_t composite_type_id, + uint32_t first_index_in_operand, bool literal_indices, + const opt::Instruction& composite_access_instruction, + std::map>* + unused_member_to_structs) const { + uint32_t next_type = composite_type_id; + for (uint32_t i = first_index_in_operand; + i < composite_access_instruction.NumInOperands(); i++) { + auto type_inst = context->get_def_use_mgr()->GetDef(next_type); + switch (type_inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeMatrix: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + next_type = type_inst->GetSingleWordInOperand(0); + break; + case SpvOpTypeStruct: { + uint32_t index_operand = + composite_access_instruction.GetSingleWordInOperand(i); + uint32_t member = literal_indices ? index_operand + : context->get_def_use_mgr() + ->GetDef(index_operand) + ->GetSingleWordInOperand(0); + // Remove the struct type from the struct types associated with this + // member index, but only if a set of struct types is known to be + // associated with this member index. + if (unused_member_to_structs->count(member)) { + unused_member_to_structs->at(member).erase(type_inst); + } + next_type = type_inst->GetSingleWordInOperand(member); + } break; + default: + assert(0 && "Unknown composite type."); + break; + } + } +} + +std::string RemoveUnusedStructMemberReductionOpportunityFinder::GetName() + const { + return "RemoveUnusedStructMemberReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h new file mode 100644 index 0000000..98f9c01 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/remove_unused_struct_member_reduction_opportunity_finder.h @@ -0,0 +1,61 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to remove struct members that are not explicitly +// used by extract, insert or access chain instructions. +class RemoveUnusedStructMemberReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + RemoveUnusedStructMemberReductionOpportunityFinder() = default; + + ~RemoveUnusedStructMemberReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: + // A helper method to update |unused_members_to_structs| by removing from it + // all struct member accesses that take place in + // |composite_access_instruction|. + // + // |composite_type_id| is the type of the root object indexed into by the + // instruction. + // + // |first_index_in_operand| provides indicates where in the input operands the + // sequence of indices begins. + // + // |literal_indices| indicates whether indices are literals (true) or ids + // (false). + void MarkAccessedMembersAsUsed( + opt::IRContext* context, uint32_t composite_type_id, + uint32_t first_index_in_operand, bool literal_indices, + const opt::Instruction& composite_access_instruction, + std::map>* unused_member_to_structs) + const; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_REMOVE_UNUSED_STRUCT_MEMBER_REDUCTION_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp new file mode 100644 index 0000000..d867c3a --- /dev/null +++ b/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h" + +#include "source/reduce/reduction_util.h" +#include "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +std::vector> +SimpleConditionalBranchToBranchOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + // Consider every function. + for (auto* function : GetTargetFunctions(context, target_function)) { + // Consider every block in the function. + for (auto& block : *function) { + // The terminator must be SpvOpBranchConditional. + opt::Instruction* terminator = block.terminator(); + if (terminator->opcode() != SpvOpBranchConditional) { + continue; + } + // It must not be a selection header, as these cannot be followed by + // OpBranch. + if (block.GetMergeInst() && + block.GetMergeInst()->opcode() == SpvOpSelectionMerge) { + continue; + } + // The conditional branch must be simplified. + if (terminator->GetSingleWordInOperand(kTrueBranchOperandIndex) != + terminator->GetSingleWordInOperand(kFalseBranchOperandIndex)) { + continue; + } + + result.push_back( + MakeUnique( + block.terminator())); + } + } + return result; +} + +std::string SimpleConditionalBranchToBranchOpportunityFinder::GetName() const { + return "SimpleConditionalBranchToBranchOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h b/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h new file mode 100644 index 0000000..8869908 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_opportunity_finder.h @@ -0,0 +1,37 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ +#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to change simple conditional branches (conditional +// branches with one target) to an OpBranch. +class SimpleConditionalBranchToBranchOpportunityFinder + : public ReductionOpportunityFinder { + public: + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const override; + + std::string GetName() const override; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_OPPORTUNITY_FINDER_H_ diff --git a/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp new file mode 100644 index 0000000..ca17f9e --- /dev/null +++ b/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h" + +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +SimpleConditionalBranchToBranchReductionOpportunity:: + SimpleConditionalBranchToBranchReductionOpportunity( + opt::Instruction* conditional_branch_instruction) + : conditional_branch_instruction_(conditional_branch_instruction) {} + +bool SimpleConditionalBranchToBranchReductionOpportunity::PreconditionHolds() { + // We find at most one opportunity per conditional branch and simplifying + // another branch cannot disable this opportunity. + return true; +} + +void SimpleConditionalBranchToBranchReductionOpportunity::Apply() { + assert(conditional_branch_instruction_->opcode() == SpvOpBranchConditional && + "SimpleConditionalBranchToBranchReductionOpportunity: branch was not " + "a conditional branch"); + + assert(conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex) == + conditional_branch_instruction_->GetSingleWordInOperand( + kFalseBranchOperandIndex) && + "SimpleConditionalBranchToBranchReductionOpportunity: branch was not " + "simple"); + + // OpBranchConditional %condition %block_id %block_id ... + // -> + // OpBranch %block_id + + conditional_branch_instruction_->SetOpcode(SpvOpBranch); + conditional_branch_instruction_->ReplaceOperands( + {{SPV_OPERAND_TYPE_ID, + {conditional_branch_instruction_->GetSingleWordInOperand( + kTrueBranchOperandIndex)}}}); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h new file mode 100644 index 0000000..eddb464 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/simple_conditional_branch_to_branch_reduction_opportunity.h @@ -0,0 +1,45 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/instruction.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to change simple conditional branches (conditional branches +// with one target) to an OpBranch. +class SimpleConditionalBranchToBranchReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity to simplify |conditional_branch_instruction|. + explicit SimpleConditionalBranchToBranchReductionOpportunity( + opt::Instruction* conditional_branch_instruction); + + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + opt::Instruction* conditional_branch_instruction_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_SIMPLE_CONDITIONAL_BRANCH_TO_BRANCH_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp b/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp new file mode 100644 index 0000000..0c00443 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.cpp @@ -0,0 +1,288 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_loop_to_selection_reduction_opportunity.h" + +#include "source/opt/aggressive_dead_code_elim_pass.h" +#include "source/opt/ir_context.h" +#include "source/reduce/reduction_util.h" + +namespace spvtools { +namespace reduce { + +namespace { +const uint32_t kMergeNodeIndex = 0; +} // namespace + +bool StructuredLoopToSelectionReductionOpportunity::PreconditionHolds() { + // Is the loop header reachable? + return loop_construct_header_->GetLabel() + ->context() + ->GetDominatorAnalysis(enclosing_function_) + ->IsReachable(loop_construct_header_); +} + +void StructuredLoopToSelectionReductionOpportunity::Apply() { + // Force computation of dominator analysis, CFG and structured CFG analysis + // before we start to mess with edges in the function. + context_->GetDominatorAnalysis(enclosing_function_); + context_->cfg(); + context_->GetStructuredCFGAnalysis(); + + // (1) Redirect edges that point to the loop's continue target to their + // closest merge block. + RedirectToClosestMergeBlock(loop_construct_header_->ContinueBlockId()); + + // (2) Redirect edges that point to the loop's merge block to their closest + // merge block (which might be that of an enclosing selection, for instance). + RedirectToClosestMergeBlock(loop_construct_header_->MergeBlockId()); + + // (3) Turn the loop construct header into a selection. + ChangeLoopToSelection(); + + // We have made control flow changes that do not preserve the analyses that + // were performed. + context_->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); + + // (4) By changing CFG edges we may have created scenarios where ids are used + // without being dominated; we fix instances of this. + FixNonDominatedIdUses(); + + // Invalidate the analyses we just used. + context_->InvalidateAnalysesExceptFor( + opt::IRContext::Analysis::kAnalysisNone); +} + +void StructuredLoopToSelectionReductionOpportunity::RedirectToClosestMergeBlock( + uint32_t original_target_id) { + // Consider every predecessor of the node with respect to which edges should + // be redirected. + std::set already_seen; + for (auto pred : context_->cfg()->preds(original_target_id)) { + if (already_seen.find(pred) != already_seen.end()) { + // We have already handled this predecessor (this scenario can arise if + // there are multiple edges from a block b to original_target_id). + continue; + } + already_seen.insert(pred); + + if (!context_->GetDominatorAnalysis(enclosing_function_) + ->IsReachable(pred)) { + // We do not care about unreachable predecessors (and dominance + // information, and thus the notion of structured control flow, makes + // little sense for unreachable blocks). + continue; + } + // Find the merge block of the structured control construct that most + // tightly encloses the predecessor. + uint32_t new_merge_target; + // The structured CFG analysis deliberately does not regard a header as + // belonging to the structure that it heads. We want it to, so handle this + // case specially. + if (context_->cfg()->block(pred)->MergeBlockIdIfAny()) { + new_merge_target = context_->cfg()->block(pred)->MergeBlockIdIfAny(); + } else { + new_merge_target = context_->GetStructuredCFGAnalysis()->MergeBlock(pred); + } + assert(new_merge_target != pred); + + if (!new_merge_target) { + // If the loop being transformed is outermost, and the predecessor is + // part of that loop's continue construct, there will be no such + // enclosing control construct. In this case, the continue construct + // will become unreachable anyway, so it is fine not to redirect the + // edge. + continue; + } + + if (new_merge_target != original_target_id) { + // Redirect the edge if it doesn't already point to the desired block. + RedirectEdge(pred, original_target_id, new_merge_target); + } + } +} + +void StructuredLoopToSelectionReductionOpportunity::RedirectEdge( + uint32_t source_id, uint32_t original_target_id, uint32_t new_target_id) { + // Redirect edge source_id->original_target_id to edge + // source_id->new_target_id, where the blocks involved are all different. + assert(source_id != original_target_id); + assert(source_id != new_target_id); + assert(original_target_id != new_target_id); + + // original_target_id must either be the merge target or continue construct + // for the loop being operated on. + assert(original_target_id == loop_construct_header_->MergeBlockId() || + original_target_id == loop_construct_header_->ContinueBlockId()); + + auto terminator = context_->cfg()->block(source_id)->terminator(); + + // Figure out which operands of the terminator need to be considered for + // redirection. + std::vector operand_indices; + if (terminator->opcode() == SpvOpBranch) { + operand_indices = {0}; + } else if (terminator->opcode() == SpvOpBranchConditional) { + operand_indices = {1, 2}; + } else { + assert(terminator->opcode() == SpvOpSwitch); + for (uint32_t label_index = 1; label_index < terminator->NumOperands(); + label_index += 2) { + operand_indices.push_back(label_index); + } + } + + // Redirect the relevant operands, asserting that at least one redirection is + // made. + bool redirected = false; + for (auto operand_index : operand_indices) { + if (terminator->GetSingleWordOperand(operand_index) == original_target_id) { + terminator->SetOperand(operand_index, {new_target_id}); + redirected = true; + } + } + (void)(redirected); + assert(redirected); + + // The old and new targets may have phi instructions; these will need to + // respect the change in edges. + AdaptPhiInstructionsForRemovedEdge( + source_id, context_->cfg()->block(original_target_id)); + AdaptPhiInstructionsForAddedEdge(source_id, + context_->cfg()->block(new_target_id)); +} + +void StructuredLoopToSelectionReductionOpportunity:: + AdaptPhiInstructionsForAddedEdge(uint32_t from_id, + opt::BasicBlock* to_block) { + to_block->ForEachPhiInst([this, &from_id](opt::Instruction* phi_inst) { + // Add to the phi operand an (undef, from_id) pair to reflect the added + // edge. + auto undef_id = FindOrCreateGlobalUndef(context_, phi_inst->type_id()); + phi_inst->AddOperand(opt::Operand(SPV_OPERAND_TYPE_ID, {undef_id})); + phi_inst->AddOperand(opt::Operand(SPV_OPERAND_TYPE_ID, {from_id})); + }); +} + +void StructuredLoopToSelectionReductionOpportunity::ChangeLoopToSelection() { + // Change the merge instruction from OpLoopMerge to OpSelectionMerge, with + // the same merge block. + auto loop_merge_inst = loop_construct_header_->GetLoopMergeInst(); + auto const loop_merge_block_id = + loop_merge_inst->GetSingleWordOperand(kMergeNodeIndex); + loop_merge_inst->SetOpcode(SpvOpSelectionMerge); + loop_merge_inst->ReplaceOperands( + {{loop_merge_inst->GetOperand(kMergeNodeIndex).type, + {loop_merge_block_id}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}}); + + // The loop header either finishes with OpBranch or OpBranchConditional. + // The latter is fine for a selection. In the former case we need to turn + // it into OpBranchConditional. We use "true" as the condition, and make + // the "else" branch be the merge block. + auto terminator = loop_construct_header_->terminator(); + if (terminator->opcode() == SpvOpBranch) { + opt::analysis::Bool temp; + const opt::analysis::Bool* bool_type = + context_->get_type_mgr()->GetRegisteredType(&temp)->AsBool(); + auto const_mgr = context_->get_constant_mgr(); + auto true_const = const_mgr->GetConstant(bool_type, {1}); + auto true_const_result_id = + const_mgr->GetDefiningInstruction(true_const)->result_id(); + auto original_branch_id = terminator->GetSingleWordOperand(0); + terminator->SetOpcode(SpvOpBranchConditional); + terminator->ReplaceOperands({{SPV_OPERAND_TYPE_ID, {true_const_result_id}}, + {SPV_OPERAND_TYPE_ID, {original_branch_id}}, + {SPV_OPERAND_TYPE_ID, {loop_merge_block_id}}}); + if (original_branch_id != loop_merge_block_id) { + AdaptPhiInstructionsForAddedEdge( + loop_construct_header_->id(), + context_->cfg()->block(loop_merge_block_id)); + } + } +} + +void StructuredLoopToSelectionReductionOpportunity::FixNonDominatedIdUses() { + // Consider each instruction in the function. + for (auto& block : *enclosing_function_) { + for (auto& def : block) { + if (def.opcode() == SpvOpVariable) { + // Variables are defined at the start of the function, and can be + // accessed by all blocks, even by unreachable blocks that have no + // dominators, so we do not need to worry about them. + continue; + } + context_->get_def_use_mgr()->ForEachUse(&def, [this, &block, &def]( + opt::Instruction* use, + uint32_t index) { + // Ignore uses outside of blocks, such as in OpDecorate. + if (context_->get_instr_block(use) == nullptr) { + return; + } + // If a use is not appropriately dominated by its definition, + // replace the use with an OpUndef, unless the definition is an + // access chain, in which case replace it with some (possibly fresh) + // variable (as we cannot load from / store to OpUndef). + if (!DefinitionSufficientlyDominatesUse(&def, use, index, block)) { + if (def.opcode() == SpvOpAccessChain) { + auto pointer_type = + context_->get_type_mgr()->GetType(def.type_id())->AsPointer(); + switch (pointer_type->storage_class()) { + case SpvStorageClassFunction: + use->SetOperand( + index, {FindOrCreateFunctionVariable( + context_, enclosing_function_, + context_->get_type_mgr()->GetId(pointer_type))}); + break; + default: + // TODO(2183) Need to think carefully about whether it makes + // sense to add new variables for all storage classes; it's + // fine for Private but might not be OK for input/output + // storage classes for example. + use->SetOperand( + index, {FindOrCreateGlobalVariable( + context_, + context_->get_type_mgr()->GetId(pointer_type))}); + break; + break; + } + } else { + use->SetOperand(index, + {FindOrCreateGlobalUndef(context_, def.type_id())}); + } + } + }); + } + } +} + +bool StructuredLoopToSelectionReductionOpportunity:: + DefinitionSufficientlyDominatesUse(opt::Instruction* def, + opt::Instruction* use, + uint32_t use_index, + opt::BasicBlock& def_block) { + if (use->opcode() == SpvOpPhi) { + // A use in a phi doesn't need to be dominated by its definition, but the + // associated parent block does need to be dominated by the definition. + return context_->GetDominatorAnalysis(enclosing_function_) + ->Dominates(def_block.id(), use->GetSingleWordOperand(use_index + 1)); + } + // In non-phi cases, a use needs to be dominated by its definition. + return context_->GetDominatorAnalysis(enclosing_function_) + ->Dominates(def, use); +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h b/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h new file mode 100644 index 0000000..4c57619 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity.h @@ -0,0 +1,97 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ +#define SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ + +#include "source/opt/def_use_manager.h" +#include "source/opt/dominator_analysis.h" +#include "source/opt/function.h" +#include "source/reduce/reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +// An opportunity to replace a structured loop with a selection. +class StructuredLoopToSelectionReductionOpportunity + : public ReductionOpportunity { + public: + // Constructs an opportunity from a loop header block and the function that + // encloses it. + explicit StructuredLoopToSelectionReductionOpportunity( + opt::IRContext* context, opt::BasicBlock* loop_construct_header, + opt::Function* enclosing_function) + : context_(context), + loop_construct_header_(loop_construct_header), + enclosing_function_(enclosing_function) {} + + // Returns true if the loop header is reachable. A structured loop might + // become unreachable as a result of turning another structured loop into + // a selection. + bool PreconditionHolds() override; + + protected: + void Apply() override; + + private: + // Parameter |original_target_id| is the id of the loop's merge block or + // continue target. This method considers each edge of the form + // b->original_target_id and transforms it into an edge of the form b->c, + // where c is the merge block of the structured control flow construct that + // most tightly contains b. + void RedirectToClosestMergeBlock(uint32_t original_target_id); + + // |source_id|, |original_target_id| and |new_target_id| are required to all + // be distinct, with a CFG edge existing from |source_id| to + // |original_target_id|, and |original_target_id| being either the merge block + // or continue target for the loop being operated on. + // The method removes this edge and adds an edge from + // |source_id| to |new_target_id|. It takes care of fixing up any OpPhi + // instructions associated with |original_target_id| and |new_target_id|. + void RedirectEdge(uint32_t source_id, uint32_t original_target_id, + uint32_t new_target_id); + + // Adds components to |to_block|'s phi instructions to account for a new + // incoming edge from |from_id|. + void AdaptPhiInstructionsForAddedEdge(uint32_t from_id, + opt::BasicBlock* to_block); + + // Turns the OpLoopMerge for the loop into OpSelectionMerge, and adapts the + // following branch instruction accordingly. + void ChangeLoopToSelection(); + + // Fixes any scenarios where, due to CFG changes, ids have uses not dominated + // by their definitions, by changing such uses to uses of OpUndef or of + // placeholder variables. + void FixNonDominatedIdUses(); + + // Returns true if and only if at least one of the following holds: + // 1) |def| dominates |use| + // 2) |def| is an OpVariable + // 3) |use| is part of an OpPhi, with associated incoming block b, and |def| + // dominates b. + bool DefinitionSufficientlyDominatesUse(opt::Instruction* def, + opt::Instruction* use, + uint32_t use_index, + opt::BasicBlock& def_block); + + opt::IRContext* context_; + opt::BasicBlock* loop_construct_header_; + opt::Function* enclosing_function_; +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_CUT_LOOP_REDUCTION_OPPORTUNITY_H_ diff --git a/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp b/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp new file mode 100644 index 0000000..fdf3ab0 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.cpp @@ -0,0 +1,102 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h" + +#include "source/reduce/structured_loop_to_selection_reduction_opportunity.h" + +namespace spvtools { +namespace reduce { + +namespace { +const uint32_t kMergeNodeIndex = 0; +const uint32_t kContinueNodeIndex = 1; +} // namespace + +std::vector> +StructuredLoopToSelectionReductionOpportunityFinder::GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const { + std::vector> result; + + std::set merge_block_ids; + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + auto merge_block_id = block.MergeBlockIdIfAny(); + if (merge_block_id) { + merge_block_ids.insert(merge_block_id); + } + } + } + + // Consider each loop construct header in the module. + for (auto* function : GetTargetFunctions(context, target_function)) { + for (auto& block : *function) { + auto loop_merge_inst = block.GetLoopMergeInst(); + if (!loop_merge_inst) { + // This is not a loop construct header. + continue; + } + + uint32_t continue_block_id = + loop_merge_inst->GetSingleWordOperand(kContinueNodeIndex); + + // Check whether the loop construct's continue target is the merge block + // of some structured control flow construct. If it is, we cautiously do + // not consider applying a transformation. + if (merge_block_ids.find(continue_block_id) != merge_block_ids.end()) { + continue; + } + + // Check whether the loop header block is also the continue target. If it + // is, we cautiously do not consider applying a transformation. + if (block.id() == continue_block_id) { + continue; + } + + // Check whether the loop construct header dominates its merge block. + // If not, the merge block must be unreachable in the control flow graph + // so we cautiously do not consider applying a transformation. + auto merge_block_id = + loop_merge_inst->GetSingleWordInOperand(kMergeNodeIndex); + if (!context->GetDominatorAnalysis(function)->Dominates(block.id(), + merge_block_id)) { + continue; + } + + // Check whether the loop construct merge block postdominates the loop + // construct header. If not (e.g. because the loop contains OpReturn, + // OpKill or OpUnreachable), we cautiously do not consider applying + // a transformation. + if (!context->GetPostDominatorAnalysis(function)->Dominates( + merge_block_id, block.id())) { + continue; + } + + // We can turn this structured loop into a selection, so add the + // opportunity to do so. + result.push_back( + MakeUnique( + context, &block, function)); + } + } + return result; +} + +std::string StructuredLoopToSelectionReductionOpportunityFinder::GetName() + const { + return "StructuredLoopToSelectionReductionOpportunityFinder"; +} + +} // namespace reduce +} // namespace spvtools diff --git a/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h b/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h new file mode 100644 index 0000000..6166af3 --- /dev/null +++ b/third_party/spirv-tools/source/reduce/structured_loop_to_selection_reduction_opportunity_finder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H +#define SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H + +#include "source/reduce/reduction_opportunity_finder.h" + +namespace spvtools { +namespace reduce { + +// A finder for opportunities to turn structured loops into selections, +// generalizing from a human-writable language the idea of turning a loop: +// +// while (c) { +// body; +// } +// +// into: +// +// if (c) { +// body; +// } +// +// Applying such opportunities results in continue constructs of transformed +// loops becoming unreachable, so that it may be possible to remove them +// subsequently. +class StructuredLoopToSelectionReductionOpportunityFinder + : public ReductionOpportunityFinder { + public: + StructuredLoopToSelectionReductionOpportunityFinder() = default; + + ~StructuredLoopToSelectionReductionOpportunityFinder() override = default; + + std::string GetName() const final; + + std::vector> GetAvailableOpportunities( + opt::IRContext* context, uint32_t target_function) const final; + + private: +}; + +} // namespace reduce +} // namespace spvtools + +#endif // SOURCE_REDUCE_STRUCTURED_LOOP_TO_SELECTION_REDUCTION_OPPORTUNITY_FINDER_H diff --git a/third_party/spirv-tools/source/software_version.cpp b/third_party/spirv-tools/source/software_version.cpp new file mode 100644 index 0000000..b258ebe --- /dev/null +++ b/third_party/spirv-tools/source/software_version.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2015-2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "spirv-tools/libspirv.h" + +namespace { + +const char* kBuildVersions[] = { +#include "build-version.inc" +}; + +} // anonymous namespace + +const char* spvSoftwareVersionString(void) { return kBuildVersions[0]; } + +const char* spvSoftwareVersionDetailsString(void) { return kBuildVersions[1]; } diff --git a/third_party/spirv-tools/source/spirv_constant.h b/third_party/spirv-tools/source/spirv_constant.h new file mode 100644 index 0000000..39771cc --- /dev/null +++ b/third_party/spirv-tools/source/spirv_constant.h @@ -0,0 +1,100 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_CONSTANT_H_ +#define SOURCE_SPIRV_CONSTANT_H_ + +#include "source/latest_version_spirv_header.h" +#include "spirv-tools/libspirv.h" + +// Version number macros. + +// Evaluates to a well-formed version header word, given valid +// SPIR-V version major and minor version numbers. +#define SPV_SPIRV_VERSION_WORD(MAJOR, MINOR) \ + ((uint32_t(uint8_t(MAJOR)) << 16) | (uint32_t(uint8_t(MINOR)) << 8)) +// Returns the major version extracted from a version header word. +#define SPV_SPIRV_VERSION_MAJOR_PART(WORD) ((uint32_t(WORD) >> 16) & 0xff) +// Returns the minor version extracted from a version header word. +#define SPV_SPIRV_VERSION_MINOR_PART(WORD) ((uint32_t(WORD) >> 8) & 0xff) + +// Header indices + +#define SPV_INDEX_MAGIC_NUMBER 0u +#define SPV_INDEX_VERSION_NUMBER 1u +#define SPV_INDEX_GENERATOR_NUMBER 2u +#define SPV_INDEX_BOUND 3u +#define SPV_INDEX_SCHEMA 4u +#define SPV_INDEX_INSTRUCTION 5u + +// Universal limits + +// SPIR-V 1.0 limits +#define SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX 0xffff +#define SPV_LIMIT_LITERAL_STRING_UTF8_CHARS_MAX 0xffff + +// A single Unicode character in UTF-8 encoding can take +// up 4 bytes. +#define SPV_LIMIT_LITERAL_STRING_BYTES_MAX \ + (SPV_LIMIT_LITERAL_STRING_UTF8_CHARS_MAX * 4) + +// NOTE: These are set to the minimum maximum values +// TODO(dneto): Check these. + +// libspirv limits. +#define SPV_LIMIT_RESULT_ID_BOUND 0x00400000 +#define SPV_LIMIT_CONTROL_FLOW_NEST_DEPTH 0x00000400 +#define SPV_LIMIT_GLOBAL_VARIABLES_MAX 0x00010000 +#define SPV_LIMIT_LOCAL_VARIABLES_MAX 0x00080000 +// TODO: Decorations per target ID max, depends on decoration table size +#define SPV_LIMIT_EXECUTION_MODE_PER_ENTRY_POINT_MAX 0x00000100 +#define SPV_LIMIT_INDICIES_MAX_ACCESS_CHAIN_COMPOSITE_MAX 0x00000100 +#define SPV_LIMIT_FUNCTION_PARAMETERS_PER_FUNCTION_DECL 0x00000100 +#define SPV_LIMIT_FUNCTION_CALL_ARGUMENTS_MAX 0x00000100 +#define SPV_LIMIT_EXT_FUNCTION_CALL_ARGUMENTS_MAX 0x00000100 +#define SPV_LIMIT_SWITCH_LITERAL_LABEL_PAIRS_MAX 0x00004000 +#define SPV_LIMIT_STRUCT_MEMBERS_MAX 0x0000400 +#define SPV_LIMIT_STRUCT_NESTING_DEPTH_MAX 0x00000100 + +// Enumerations + +// Values mapping to registered tools. See the registry at +// https://www.khronos.org/registry/spir-v/api/spir-v.xml +// These values occupy the higher order 16 bits of the generator magic word. +typedef enum spv_generator_t { + // TODO(dneto) Values 0 through 5 were registered only as vendor. + SPV_GENERATOR_KHRONOS = 0, + SPV_GENERATOR_LUNARG = 1, + SPV_GENERATOR_VALVE = 2, + SPV_GENERATOR_CODEPLAY = 3, + SPV_GENERATOR_NVIDIA = 4, + SPV_GENERATOR_ARM = 5, + // These are vendor and tool. + SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR = 6, + SPV_GENERATOR_KHRONOS_ASSEMBLER = 7, + SPV_GENERATOR_KHRONOS_GLSLANG = 8, + SPV_GENERATOR_NUM_ENTRIES, + SPV_FORCE_16_BIT_ENUM(spv_generator_t) +} spv_generator_t; + +// Evaluates to a well-formed generator magic word from a tool value and +// miscellaneous 16-bit value. +#define SPV_GENERATOR_WORD(TOOL, MISC) \ + ((uint32_t(uint16_t(TOOL)) << 16) | uint16_t(MISC)) +// Returns the tool component of the generator word. +#define SPV_GENERATOR_TOOL_PART(WORD) (uint32_t(WORD) >> 16) +// Returns the misc part of the generator word. +#define SPV_GENERATOR_MISC_PART(WORD) (uint32_t(WORD) & 0xFFFF) + +#endif // SOURCE_SPIRV_CONSTANT_H_ diff --git a/third_party/spirv-tools/source/spirv_definition.h b/third_party/spirv-tools/source/spirv_definition.h new file mode 100644 index 0000000..63a4ef0 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_definition.h @@ -0,0 +1,33 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_DEFINITION_H_ +#define SOURCE_SPIRV_DEFINITION_H_ + +#include + +#include "source/latest_version_spirv_header.h" + +#define spvIsInBitfield(value, bitfield) ((value) == ((value)&bitfield)) + +typedef struct spv_header_t { + uint32_t magic; + uint32_t version; + uint32_t generator; + uint32_t bound; + uint32_t schema; // NOTE: Reserved + const uint32_t* instructions; // NOTE: Unfixed pointer to instruciton stream +} spv_header_t; + +#endif // SOURCE_SPIRV_DEFINITION_H_ diff --git a/third_party/spirv-tools/source/spirv_endian.cpp b/third_party/spirv-tools/source/spirv_endian.cpp new file mode 100644 index 0000000..1d77091 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_endian.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_endian.h" + +#include + +enum { + I32_ENDIAN_LITTLE = 0x03020100ul, + I32_ENDIAN_BIG = 0x00010203ul, +}; + +// This constant value allows the detection of the host machine's endianness. +// Accessing it through the "value" member is valid due to C++11 section 3.10 +// paragraph 10. +static const union { + unsigned char bytes[4]; + uint32_t value; +} o32_host_order = {{0, 1, 2, 3}}; + +#define I32_ENDIAN_HOST (o32_host_order.value) + +uint32_t spvFixWord(const uint32_t word, const spv_endianness_t endian) { + if ((SPV_ENDIANNESS_LITTLE == endian && I32_ENDIAN_HOST == I32_ENDIAN_BIG) || + (SPV_ENDIANNESS_BIG == endian && I32_ENDIAN_HOST == I32_ENDIAN_LITTLE)) { + return (word & 0x000000ff) << 24 | (word & 0x0000ff00) << 8 | + (word & 0x00ff0000) >> 8 | (word & 0xff000000) >> 24; + } + + return word; +} + +uint64_t spvFixDoubleWord(const uint32_t low, const uint32_t high, + const spv_endianness_t endian) { + return (uint64_t(spvFixWord(high, endian)) << 32) | spvFixWord(low, endian); +} + +spv_result_t spvBinaryEndianness(spv_const_binary binary, + spv_endianness_t* pEndian) { + if (!binary->code || !binary->wordCount) return SPV_ERROR_INVALID_BINARY; + if (!pEndian) return SPV_ERROR_INVALID_POINTER; + + uint8_t bytes[4]; + memcpy(bytes, binary->code, sizeof(uint32_t)); + + if (0x03 == bytes[0] && 0x02 == bytes[1] && 0x23 == bytes[2] && + 0x07 == bytes[3]) { + *pEndian = SPV_ENDIANNESS_LITTLE; + return SPV_SUCCESS; + } + + if (0x07 == bytes[0] && 0x23 == bytes[1] && 0x02 == bytes[2] && + 0x03 == bytes[3]) { + *pEndian = SPV_ENDIANNESS_BIG; + return SPV_SUCCESS; + } + + return SPV_ERROR_INVALID_BINARY; +} + +bool spvIsHostEndian(spv_endianness_t endian) { + return ((SPV_ENDIANNESS_LITTLE == endian) && + (I32_ENDIAN_LITTLE == I32_ENDIAN_HOST)) || + ((SPV_ENDIANNESS_BIG == endian) && + (I32_ENDIAN_BIG == I32_ENDIAN_HOST)); +} diff --git a/third_party/spirv-tools/source/spirv_endian.h b/third_party/spirv-tools/source/spirv_endian.h new file mode 100644 index 0000000..c2540be --- /dev/null +++ b/third_party/spirv-tools/source/spirv_endian.h @@ -0,0 +1,37 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_ENDIAN_H_ +#define SOURCE_SPIRV_ENDIAN_H_ + +#include "spirv-tools/libspirv.h" + +// Converts a word in the specified endianness to the host native endianness. +uint32_t spvFixWord(const uint32_t word, const spv_endianness_t endianness); + +// Converts a pair of words in the specified endianness to the host native +// endianness. +uint64_t spvFixDoubleWord(const uint32_t low, const uint32_t high, + const spv_endianness_t endianness); + +// Gets the endianness of the SPIR-V module given in the binary parameter. +// Returns SPV_ENDIANNESS_UNKNOWN if the SPIR-V magic number is invalid, +// otherwise writes the determined endianness into *endian. +spv_result_t spvBinaryEndianness(const spv_const_binary binary, + spv_endianness_t* endian); + +// Returns true if the given endianness matches the host's native endiannes. +bool spvIsHostEndian(spv_endianness_t endian); + +#endif // SOURCE_SPIRV_ENDIAN_H_ diff --git a/third_party/spirv-tools/source/spirv_fuzzer_options.cpp b/third_party/spirv-tools/source/spirv_fuzzer_options.cpp new file mode 100644 index 0000000..3f62e0e --- /dev/null +++ b/third_party/spirv-tools/source/spirv_fuzzer_options.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_fuzzer_options.h" + +namespace { +// The default maximum number of steps for the reducer to run before giving up. +const uint32_t kDefaultStepLimit = 250; +} // namespace + +spv_fuzzer_options_t::spv_fuzzer_options_t() + : has_random_seed(false), + random_seed(0), + replay_range(0), + replay_validation_enabled(false), + shrinker_step_limit(kDefaultStepLimit), + fuzzer_pass_validation_enabled(false), + all_passes_enabled(false) {} + +SPIRV_TOOLS_EXPORT spv_fuzzer_options spvFuzzerOptionsCreate() { + return new spv_fuzzer_options_t(); +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsDestroy(spv_fuzzer_options options) { + delete options; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableReplayValidation( + spv_fuzzer_options options) { + options->replay_validation_enabled = true; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetRandomSeed( + spv_fuzzer_options options, uint32_t seed) { + options->has_random_seed = true; + options->random_seed = seed; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetReplayRange( + spv_fuzzer_options options, int32_t replay_range) { + options->replay_range = replay_range; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsSetShrinkerStepLimit( + spv_fuzzer_options options, uint32_t shrinker_step_limit) { + options->shrinker_step_limit = shrinker_step_limit; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableFuzzerPassValidation( + spv_fuzzer_options options) { + options->fuzzer_pass_validation_enabled = true; +} + +SPIRV_TOOLS_EXPORT void spvFuzzerOptionsEnableAllPasses( + spv_fuzzer_options options) { + options->all_passes_enabled = true; +} diff --git a/third_party/spirv-tools/source/spirv_fuzzer_options.h b/third_party/spirv-tools/source/spirv_fuzzer_options.h new file mode 100644 index 0000000..bb8d910 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_fuzzer_options.h @@ -0,0 +1,48 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_FUZZER_OPTIONS_H_ +#define SOURCE_SPIRV_FUZZER_OPTIONS_H_ + +#include "spirv-tools/libspirv.h" + +#include +#include + +// Manages command line options passed to the SPIR-V Fuzzer. New struct +// members may be added for any new option. +struct spv_fuzzer_options_t { + spv_fuzzer_options_t(); + + // See spvFuzzerOptionsSetRandomSeed. + bool has_random_seed; + uint32_t random_seed; + + // See spvFuzzerOptionsSetReplayRange. + int32_t replay_range; + + // See spvFuzzerOptionsEnableReplayValidation. + bool replay_validation_enabled; + + // See spvFuzzerOptionsSetShrinkerStepLimit. + uint32_t shrinker_step_limit; + + // See spvFuzzerOptionsValidateAfterEveryPass. + bool fuzzer_pass_validation_enabled; + + // See spvFuzzerOptionsEnableAllPasses. + bool all_passes_enabled; +}; + +#endif // SOURCE_SPIRV_FUZZER_OPTIONS_H_ diff --git a/third_party/spirv-tools/source/spirv_optimizer_options.cpp b/third_party/spirv-tools/source/spirv_optimizer_options.cpp new file mode 100644 index 0000000..e92ffc0 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_optimizer_options.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/spirv_optimizer_options.h" + +SPIRV_TOOLS_EXPORT spv_optimizer_options spvOptimizerOptionsCreate(void) { + return new spv_optimizer_options_t(); +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsDestroy( + spv_optimizer_options options) { + delete options; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetRunValidator( + spv_optimizer_options options, bool val) { + options->run_validator_ = val; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetValidatorOptions( + spv_optimizer_options options, spv_validator_options val) { + options->val_options_ = *val; +} +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetMaxIdBound( + spv_optimizer_options options, uint32_t val) { + options->max_id_bound_ = val; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveBindings( + spv_optimizer_options options, bool val) { + options->preserve_bindings_ = val; +} + +SPIRV_TOOLS_EXPORT void spvOptimizerOptionsSetPreserveSpecConstants( + spv_optimizer_options options, bool val) { + options->preserve_spec_constants_ = val; +} diff --git a/third_party/spirv-tools/source/spirv_optimizer_options.h b/third_party/spirv-tools/source/spirv_optimizer_options.h new file mode 100644 index 0000000..aa76d20 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_optimizer_options.h @@ -0,0 +1,49 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ +#define SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ + +#include "source/spirv_validator_options.h" +#include "spirv-tools/libspirv.h" + +// Manages command line options passed to the SPIR-V Validator. New struct +// members may be added for any new option. +struct spv_optimizer_options_t { + spv_optimizer_options_t() + : run_validator_(true), + val_options_(), + max_id_bound_(kDefaultMaxIdBound), + preserve_bindings_(false), + preserve_spec_constants_(false) {} + + // When true the validator will be run before optimizations are run. + bool run_validator_; + + // Options to pass to the validator if it is run. + spv_validator_options_t val_options_; + + // The maximum value the id bound for a module can have. The Spir-V spec says + // this value must be at least 0x3FFFFF, but implementations can allow for a + // higher value. + uint32_t max_id_bound_; + + // When true, all binding declarations within the module should be preserved. + bool preserve_bindings_; + + // When true, all specialization constants within the module should be + // preserved. + bool preserve_spec_constants_; +}; +#endif // SOURCE_SPIRV_OPTIMIZER_OPTIONS_H_ diff --git a/third_party/spirv-tools/source/spirv_reducer_options.cpp b/third_party/spirv-tools/source/spirv_reducer_options.cpp new file mode 100644 index 0000000..9086433 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_reducer_options.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/spirv_reducer_options.h" + +namespace { +// The default maximum number of steps the reducer will take before giving up. +const uint32_t kDefaultStepLimit = 2500; +} // namespace + +spv_reducer_options_t::spv_reducer_options_t() + : step_limit(kDefaultStepLimit), + fail_on_validation_error(false), + target_function(0) {} + +SPIRV_TOOLS_EXPORT spv_reducer_options spvReducerOptionsCreate() { + return new spv_reducer_options_t(); +} + +SPIRV_TOOLS_EXPORT void spvReducerOptionsDestroy(spv_reducer_options options) { + delete options; +} + +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetStepLimit( + spv_reducer_options options, uint32_t step_limit) { + options->step_limit = step_limit; +} + +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetFailOnValidationError( + spv_reducer_options options, bool fail_on_validation_error) { + options->fail_on_validation_error = fail_on_validation_error; +} + +SPIRV_TOOLS_EXPORT void spvReducerOptionsSetTargetFunction( + spv_reducer_options options, uint32_t target_function) { + options->target_function = target_function; +} diff --git a/third_party/spirv-tools/source/spirv_reducer_options.h b/third_party/spirv-tools/source/spirv_reducer_options.h new file mode 100644 index 0000000..911747d --- /dev/null +++ b/third_party/spirv-tools/source/spirv_reducer_options.h @@ -0,0 +1,38 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_REDUCER_OPTIONS_H_ +#define SOURCE_SPIRV_REDUCER_OPTIONS_H_ + +#include "spirv-tools/libspirv.h" + +#include +#include + +// Manages command line options passed to the SPIR-V Reducer. New struct +// members may be added for any new option. +struct spv_reducer_options_t { + spv_reducer_options_t(); + + // See spvReducerOptionsSetStepLimit. + uint32_t step_limit; + + // See spvReducerOptionsSetFailOnValidationError. + bool fail_on_validation_error; + + // See spvReducerOptionsSetTargetFunction. + uint32_t target_function; +}; + +#endif // SOURCE_SPIRV_REDUCER_OPTIONS_H_ diff --git a/third_party/spirv-tools/source/spirv_target_env.cpp b/third_party/spirv-tools/source/spirv_target_env.cpp new file mode 100644 index 0000000..e2ff99c --- /dev/null +++ b/third_party/spirv-tools/source/spirv_target_env.cpp @@ -0,0 +1,380 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_target_env.h" + +#include +#include + +#include "source/spirv_constant.h" +#include "spirv-tools/libspirv.h" + +const char* spvTargetEnvDescription(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + return "SPIR-V 1.0"; + case SPV_ENV_VULKAN_1_0: + return "SPIR-V 1.0 (under Vulkan 1.0 semantics)"; + case SPV_ENV_UNIVERSAL_1_1: + return "SPIR-V 1.1"; + case SPV_ENV_OPENCL_1_2: + return "SPIR-V 1.0 (under OpenCL 1.2 Full Profile semantics)"; + case SPV_ENV_OPENCL_EMBEDDED_1_2: + return "SPIR-V 1.0 (under OpenCL 1.2 Embedded Profile semantics)"; + case SPV_ENV_OPENCL_2_0: + return "SPIR-V 1.0 (under OpenCL 2.0 Full Profile semantics)"; + case SPV_ENV_OPENCL_EMBEDDED_2_0: + return "SPIR-V 1.0 (under OpenCL 2.0 Embedded Profile semantics)"; + case SPV_ENV_OPENCL_2_1: + return "SPIR-V 1.0 (under OpenCL 2.1 Full Profile semantics)"; + case SPV_ENV_OPENCL_EMBEDDED_2_1: + return "SPIR-V 1.0 (under OpenCL 2.1 Embedded Profile semantics)"; + case SPV_ENV_OPENCL_2_2: + return "SPIR-V 1.2 (under OpenCL 2.2 Full Profile semantics)"; + case SPV_ENV_OPENCL_EMBEDDED_2_2: + return "SPIR-V 1.2 (under OpenCL 2.2 Embedded Profile semantics)"; + case SPV_ENV_OPENGL_4_0: + return "SPIR-V 1.0 (under OpenGL 4.0 semantics)"; + case SPV_ENV_OPENGL_4_1: + return "SPIR-V 1.0 (under OpenGL 4.1 semantics)"; + case SPV_ENV_OPENGL_4_2: + return "SPIR-V 1.0 (under OpenGL 4.2 semantics)"; + case SPV_ENV_OPENGL_4_3: + return "SPIR-V 1.0 (under OpenGL 4.3 semantics)"; + case SPV_ENV_OPENGL_4_5: + return "SPIR-V 1.0 (under OpenGL 4.5 semantics)"; + case SPV_ENV_UNIVERSAL_1_2: + return "SPIR-V 1.2"; + case SPV_ENV_UNIVERSAL_1_3: + return "SPIR-V 1.3"; + case SPV_ENV_VULKAN_1_1: + return "SPIR-V 1.3 (under Vulkan 1.1 semantics)"; + case SPV_ENV_WEBGPU_0: + return "SPIR-V 1.3 (under WIP WebGPU semantics)"; + case SPV_ENV_UNIVERSAL_1_4: + return "SPIR-V 1.4"; + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + return "SPIR-V 1.4 (under Vulkan 1.1 semantics)"; + case SPV_ENV_UNIVERSAL_1_5: + return "SPIR-V 1.5"; + case SPV_ENV_VULKAN_1_2: + return "SPIR-V 1.5 (under Vulkan 1.2 semantics)"; + } + return ""; +} + +uint32_t spvVersionForTargetEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + return SPV_SPIRV_VERSION_WORD(1, 0); + case SPV_ENV_UNIVERSAL_1_1: + return SPV_SPIRV_VERSION_WORD(1, 1); + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + return SPV_SPIRV_VERSION_WORD(1, 2); + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_WEBGPU_0: + return SPV_SPIRV_VERSION_WORD(1, 3); + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + return SPV_SPIRV_VERSION_WORD(1, 4); + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + return SPV_SPIRV_VERSION_WORD(1, 5); + } + return SPV_SPIRV_VERSION_WORD(0, 0); +} + +static const std::pair spvTargetEnvNameMap[] = { + {"vulkan1.1spv1.4", SPV_ENV_VULKAN_1_1_SPIRV_1_4}, + {"vulkan1.0", SPV_ENV_VULKAN_1_0}, + {"vulkan1.1", SPV_ENV_VULKAN_1_1}, + {"vulkan1.2", SPV_ENV_VULKAN_1_2}, + {"spv1.0", SPV_ENV_UNIVERSAL_1_0}, + {"spv1.1", SPV_ENV_UNIVERSAL_1_1}, + {"spv1.2", SPV_ENV_UNIVERSAL_1_2}, + {"spv1.3", SPV_ENV_UNIVERSAL_1_3}, + {"spv1.4", SPV_ENV_UNIVERSAL_1_4}, + {"spv1.5", SPV_ENV_UNIVERSAL_1_5}, + {"opencl1.2embedded", SPV_ENV_OPENCL_EMBEDDED_1_2}, + {"opencl1.2", SPV_ENV_OPENCL_1_2}, + {"opencl2.0embedded", SPV_ENV_OPENCL_EMBEDDED_2_0}, + {"opencl2.0", SPV_ENV_OPENCL_2_0}, + {"opencl2.1embedded", SPV_ENV_OPENCL_EMBEDDED_2_1}, + {"opencl2.1", SPV_ENV_OPENCL_2_1}, + {"opencl2.2embedded", SPV_ENV_OPENCL_EMBEDDED_2_2}, + {"opencl2.2", SPV_ENV_OPENCL_2_2}, + {"opengl4.0", SPV_ENV_OPENGL_4_0}, + {"opengl4.1", SPV_ENV_OPENGL_4_1}, + {"opengl4.2", SPV_ENV_OPENGL_4_2}, + {"opengl4.3", SPV_ENV_OPENGL_4_3}, + {"opengl4.5", SPV_ENV_OPENGL_4_5}, + {"webgpu0", SPV_ENV_WEBGPU_0}, +}; + +bool spvParseTargetEnv(const char* s, spv_target_env* env) { + auto match = [s](const char* b) { + return s && (0 == strncmp(s, b, strlen(b))); + }; + for (auto& name_env : spvTargetEnvNameMap) { + if (match(name_env.first)) { + if (env) { + *env = name_env.second; + } + return true; + } + } + if (env) *env = SPV_ENV_UNIVERSAL_1_0; + return false; +} + +#define VULKAN_VER(MAJOR, MINOR) ((MAJOR << 22) | (MINOR << 12)) +#define SPIRV_VER(MAJOR, MINOR) ((MAJOR << 16) | (MINOR << 8)) + +struct VulkanEnv { + spv_target_env vulkan_env; + uint32_t vulkan_ver; + uint32_t spirv_ver; +}; +// Maps each Vulkan target environment enum to the Vulkan version, and the +// maximum supported SPIR-V version for that Vulkan environment. +// Keep this ordered from least capable to most capable. +static const VulkanEnv ordered_vulkan_envs[] = { + {SPV_ENV_VULKAN_1_0, VULKAN_VER(1, 0), SPIRV_VER(1, 0)}, + {SPV_ENV_VULKAN_1_1, VULKAN_VER(1, 1), SPIRV_VER(1, 3)}, + {SPV_ENV_VULKAN_1_1_SPIRV_1_4, VULKAN_VER(1, 1), SPIRV_VER(1, 4)}, + {SPV_ENV_VULKAN_1_2, VULKAN_VER(1, 2), SPIRV_VER(1, 5)}}; + +bool spvParseVulkanEnv(uint32_t vulkan_ver, uint32_t spirv_ver, + spv_target_env* env) { + for (auto triple : ordered_vulkan_envs) { + if (triple.vulkan_ver >= vulkan_ver && triple.spirv_ver >= spirv_ver) { + *env = triple.vulkan_env; + return true; + } + } + return false; +} + +bool spvIsVulkanEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_WEBGPU_0: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: + return false; + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_VULKAN_1_2: + return true; + } + return false; +} + +bool spvIsOpenCLEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_WEBGPU_0: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + return false; + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + return true; + } + return false; +} + +bool spvIsWebGPUEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + return false; + case SPV_ENV_WEBGPU_0: + return true; + } + return false; +} + +bool spvIsOpenGLEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_WEBGPU_0: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + return false; + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + return true; + } + return false; +} + +bool spvIsVulkanOrWebGPUEnv(spv_target_env env) { + return spvIsVulkanEnv(env) || spvIsWebGPUEnv(env); +} + +std::string spvLogStringForEnv(spv_target_env env) { + switch (env) { + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_2: { + return "OpenCL"; + } + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: { + return "OpenGL"; + } + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: { + case SPV_ENV_VULKAN_1_2: + return "Vulkan"; + } + case SPV_ENV_WEBGPU_0: { + return "WebGPU"; + } + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: { + return "Universal"; + } + } + return "Unknown"; +} + +std::string spvTargetEnvList(const int pad, const int wrap) { + std::string ret; + size_t max_line_len = wrap - pad; // The first line isn't padded + std::string line; + std::string sep = ""; + + for (auto& name_env : spvTargetEnvNameMap) { + std::string word = sep + name_env.first; + if (line.length() + word.length() > max_line_len) { + // Adding one word wouldn't fit, commit the line in progress and + // start a new one. + ret += line + "\n"; + line.assign(pad, ' '); + // The first line is done. The max length now comprises the + // padding. + max_line_len = wrap; + } + line += word; + sep = "|"; + } + + ret += line; + + return ret; +} diff --git a/third_party/spirv-tools/source/spirv_target_env.h b/third_party/spirv-tools/source/spirv_target_env.h new file mode 100644 index 0000000..1bdedf9 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_target_env.h @@ -0,0 +1,52 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_TARGET_ENV_H_ +#define SOURCE_SPIRV_TARGET_ENV_H_ + +#include + +#include "spirv-tools/libspirv.h" + +// Returns true if |env| is a VULKAN environment, false otherwise. +bool spvIsVulkanEnv(spv_target_env env); + +// Returns true if |env| is an OPENCL environment, false otherwise. +bool spvIsOpenCLEnv(spv_target_env env); + +// Returns true if |env| is an WEBGPU environment, false otherwise. +bool spvIsWebGPUEnv(spv_target_env env); + +// Returns true if |env| is an OPENGL environment, false otherwise. +bool spvIsOpenGLEnv(spv_target_env env); + +// Returns true if |env| is a VULKAN or WEBGPU environment, false otherwise. +bool spvIsVulkanOrWebGPUEnv(spv_target_env env); + +// Returns the version number for the given SPIR-V target environment. +uint32_t spvVersionForTargetEnv(spv_target_env env); + +// Returns a string to use in logging messages that indicates the class of +// environment, i.e. "Vulkan", "WebGPU", "OpenCL", etc. +std::string spvLogStringForEnv(spv_target_env env); + +// Returns a formatted list of all SPIR-V target environment names that +// can be parsed by spvParseTargetEnv. +// |pad| is the number of space characters that the begining of each line +// except the first one will be padded with. +// |wrap| is the max length of lines the user desires. Word-wrapping will +// occur to satisfy this limit. +std::string spvTargetEnvList(const int pad, const int wrap); + +#endif // SOURCE_SPIRV_TARGET_ENV_H_ diff --git a/third_party/spirv-tools/source/spirv_validator_options.cpp b/third_party/spirv-tools/source/spirv_validator_options.cpp new file mode 100644 index 0000000..01aa797 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_validator_options.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_validator_options.h" + +#include +#include + +bool spvParseUniversalLimitsOptions(const char* s, spv_validator_limit* type) { + auto match = [s](const char* b) { + return s && (0 == strncmp(s, b, strlen(b))); + }; + if (match("--max-struct-members")) { + *type = spv_validator_limit_max_struct_members; + } else if (match("--max-struct_depth")) { + *type = spv_validator_limit_max_struct_depth; + } else if (match("--max-local-variables")) { + *type = spv_validator_limit_max_local_variables; + } else if (match("--max-global-variables")) { + *type = spv_validator_limit_max_global_variables; + } else if (match("--max-switch-branches")) { + *type = spv_validator_limit_max_global_variables; + } else if (match("--max-function-args")) { + *type = spv_validator_limit_max_function_args; + } else if (match("--max-control-flow-nesting-depth")) { + *type = spv_validator_limit_max_control_flow_nesting_depth; + } else if (match("--max-access-chain-indexes")) { + *type = spv_validator_limit_max_access_chain_indexes; + } else if (match("--max-id-bound")) { + *type = spv_validator_limit_max_id_bound; + } else { + // The command line option for this validator limit has not been added. + // Therefore we return false. + return false; + } + + return true; +} + +spv_validator_options spvValidatorOptionsCreate(void) { + return new spv_validator_options_t; +} + +void spvValidatorOptionsDestroy(spv_validator_options options) { + delete options; +} + +void spvValidatorOptionsSetUniversalLimit(spv_validator_options options, + spv_validator_limit limit_type, + uint32_t limit) { + assert(options && "Validator options object may not be Null"); + switch (limit_type) { +#define LIMIT(TYPE, FIELD) \ + case TYPE: \ + options->universal_limits_.FIELD = limit; \ + break; + LIMIT(spv_validator_limit_max_struct_members, max_struct_members) + LIMIT(spv_validator_limit_max_struct_depth, max_struct_depth) + LIMIT(spv_validator_limit_max_local_variables, max_local_variables) + LIMIT(spv_validator_limit_max_global_variables, max_global_variables) + LIMIT(spv_validator_limit_max_switch_branches, max_switch_branches) + LIMIT(spv_validator_limit_max_function_args, max_function_args) + LIMIT(spv_validator_limit_max_control_flow_nesting_depth, + max_control_flow_nesting_depth) + LIMIT(spv_validator_limit_max_access_chain_indexes, + max_access_chain_indexes) + LIMIT(spv_validator_limit_max_id_bound, max_id_bound) +#undef LIMIT + } +} + +void spvValidatorOptionsSetRelaxStoreStruct(spv_validator_options options, + bool val) { + options->relax_struct_store = val; +} + +void spvValidatorOptionsSetRelaxLogicalPointer(spv_validator_options options, + bool val) { + options->relax_logical_pointer = val; +} + +void spvValidatorOptionsSetBeforeHlslLegalization(spv_validator_options options, + bool val) { + options->before_hlsl_legalization = val; + options->relax_logical_pointer = val; +} + +void spvValidatorOptionsSetRelaxBlockLayout(spv_validator_options options, + bool val) { + options->relax_block_layout = val; +} + +void spvValidatorOptionsSetUniformBufferStandardLayout( + spv_validator_options options, bool val) { + options->uniform_buffer_standard_layout = val; +} + +void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options, + bool val) { + options->scalar_block_layout = val; +} + +void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options, + bool val) { + options->skip_block_layout = val; +} diff --git a/third_party/spirv-tools/source/spirv_validator_options.h b/third_party/spirv-tools/source/spirv_validator_options.h new file mode 100644 index 0000000..b7da5d8 --- /dev/null +++ b/third_party/spirv-tools/source/spirv_validator_options.h @@ -0,0 +1,61 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_SPIRV_VALIDATOR_OPTIONS_H_ +#define SOURCE_SPIRV_VALIDATOR_OPTIONS_H_ + +#include "spirv-tools/libspirv.h" + +// Return true if the command line option for the validator limit is valid (Also +// returns the Enum for option in this case). Returns false otherwise. +bool spvParseUniversalLimitsOptions(const char* s, spv_validator_limit* limit); + +// Default initialization of this structure is to the default Universal Limits +// described in the SPIR-V Spec. +struct validator_universal_limits_t { + uint32_t max_struct_members{16383}; + uint32_t max_struct_depth{255}; + uint32_t max_local_variables{524287}; + uint32_t max_global_variables{65535}; + uint32_t max_switch_branches{16383}; + uint32_t max_function_args{255}; + uint32_t max_control_flow_nesting_depth{1023}; + uint32_t max_access_chain_indexes{255}; + uint32_t max_id_bound{0x3FFFFF}; +}; + +// Manages command line options passed to the SPIR-V Validator. New struct +// members may be added for any new option. +struct spv_validator_options_t { + spv_validator_options_t() + : universal_limits_(), + relax_struct_store(false), + relax_logical_pointer(false), + relax_block_layout(false), + uniform_buffer_standard_layout(false), + scalar_block_layout(false), + skip_block_layout(false), + before_hlsl_legalization(false) {} + + validator_universal_limits_t universal_limits_; + bool relax_struct_store; + bool relax_logical_pointer; + bool relax_block_layout; + bool uniform_buffer_standard_layout; + bool scalar_block_layout; + bool skip_block_layout; + bool before_hlsl_legalization; +}; + +#endif // SOURCE_SPIRV_VALIDATOR_OPTIONS_H_ diff --git a/third_party/spirv-tools/source/table.cpp b/third_party/spirv-tools/source/table.cpp new file mode 100644 index 0000000..8340e8e --- /dev/null +++ b/third_party/spirv-tools/source/table.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/table.h" + +#include + +spv_context spvContextCreate(spv_target_env env) { + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_VULKAN_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_OPENCL_1_2: + case SPV_ENV_OPENCL_EMBEDDED_1_2: + case SPV_ENV_OPENCL_2_0: + case SPV_ENV_OPENCL_EMBEDDED_2_0: + case SPV_ENV_OPENCL_2_1: + case SPV_ENV_OPENCL_EMBEDDED_2_1: + case SPV_ENV_OPENCL_2_2: + case SPV_ENV_OPENCL_EMBEDDED_2_2: + case SPV_ENV_OPENGL_4_0: + case SPV_ENV_OPENGL_4_1: + case SPV_ENV_OPENGL_4_2: + case SPV_ENV_OPENGL_4_3: + case SPV_ENV_OPENGL_4_5: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + case SPV_ENV_VULKAN_1_1: + case SPV_ENV_VULKAN_1_1_SPIRV_1_4: + case SPV_ENV_WEBGPU_0: + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: + case SPV_ENV_VULKAN_1_2: + break; + default: + return nullptr; + } + + spv_opcode_table opcode_table; + spv_operand_table operand_table; + spv_ext_inst_table ext_inst_table; + + spvOpcodeTableGet(&opcode_table, env); + spvOperandTableGet(&operand_table, env); + spvExtInstTableGet(&ext_inst_table, env); + + return new spv_context_t{env, opcode_table, operand_table, ext_inst_table, + nullptr /* a null default consumer */}; +} + +void spvContextDestroy(spv_context context) { delete context; } + +void spvtools::SetContextMessageConsumer(spv_context context, + spvtools::MessageConsumer consumer) { + context->consumer = std::move(consumer); +} diff --git a/third_party/spirv-tools/source/table.h b/third_party/spirv-tools/source/table.h new file mode 100644 index 0000000..5adf04a --- /dev/null +++ b/third_party/spirv-tools/source/table.h @@ -0,0 +1,133 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_TABLE_H_ +#define SOURCE_TABLE_H_ + +#include "source/extensions.h" +#include "source/latest_version_spirv_header.h" +#include "spirv-tools/libspirv.hpp" + +typedef struct spv_opcode_desc_t { + const char* name; + const SpvOp opcode; + const uint32_t numCapabilities; + const SpvCapability* capabilities; + // operandTypes[0..numTypes-1] describe logical operands for the instruction. + // The operand types include result id and result-type id, followed by + // the types of arguments. + const uint16_t numTypes; + spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? + const bool hasResult; // Does the instruction have a result ID operand? + const bool hasType; // Does the instruction have a type ID operand? + // A set of extensions that enable this feature. If empty then this operand + // value is in core and its availability is subject to minVersion. The + // assembler, binary parser, and disassembler ignore this rule, so you can + // freely process invalid modules. + const uint32_t numExtensions; + const spvtools::Extension* extensions; + // Minimal core SPIR-V version required for this feature, if without + // extensions. ~0u means reserved for future use. ~0u and non-empty extension + // lists means only available in extensions. + const uint32_t minVersion; + const uint32_t lastVersion; +} spv_opcode_desc_t; + +typedef struct spv_operand_desc_t { + const char* name; + const uint32_t value; + const uint32_t numCapabilities; + const SpvCapability* capabilities; + // A set of extensions that enable this feature. If empty then this operand + // value is in core and its availability is subject to minVersion. The + // assembler, binary parser, and disassembler ignore this rule, so you can + // freely process invalid modules. + const uint32_t numExtensions; + const spvtools::Extension* extensions; + const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? + // Minimal core SPIR-V version required for this feature, if without + // extensions. ~0u means reserved for future use. ~0u and non-empty extension + // lists means only available in extensions. + const uint32_t minVersion; + const uint32_t lastVersion; +} spv_operand_desc_t; + +typedef struct spv_operand_desc_group_t { + const spv_operand_type_t type; + const uint32_t count; + const spv_operand_desc_t* entries; +} spv_operand_desc_group_t; + +typedef struct spv_ext_inst_desc_t { + const char* name; + const uint32_t ext_inst; + const uint32_t numCapabilities; + const SpvCapability* capabilities; + const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? +} spv_ext_inst_desc_t; + +typedef struct spv_ext_inst_group_t { + const spv_ext_inst_type_t type; + const uint32_t count; + const spv_ext_inst_desc_t* entries; +} spv_ext_inst_group_t; + +typedef struct spv_opcode_table_t { + const uint32_t count; + const spv_opcode_desc_t* entries; +} spv_opcode_table_t; + +typedef struct spv_operand_table_t { + const uint32_t count; + const spv_operand_desc_group_t* types; +} spv_operand_table_t; + +typedef struct spv_ext_inst_table_t { + const uint32_t count; + const spv_ext_inst_group_t* groups; +} spv_ext_inst_table_t; + +typedef const spv_opcode_desc_t* spv_opcode_desc; +typedef const spv_operand_desc_t* spv_operand_desc; +typedef const spv_ext_inst_desc_t* spv_ext_inst_desc; + +typedef const spv_opcode_table_t* spv_opcode_table; +typedef const spv_operand_table_t* spv_operand_table; +typedef const spv_ext_inst_table_t* spv_ext_inst_table; + +struct spv_context_t { + const spv_target_env target_env; + const spv_opcode_table opcode_table; + const spv_operand_table operand_table; + const spv_ext_inst_table ext_inst_table; + spvtools::MessageConsumer consumer; +}; + +namespace spvtools { + +// Sets the message consumer to |consumer| in the given |context|. The original +// message consumer will be overwritten. +void SetContextMessageConsumer(spv_context context, MessageConsumer consumer); +} // namespace spvtools + +// Populates *table with entries for env. +spv_result_t spvOpcodeTableGet(spv_opcode_table* table, spv_target_env env); + +// Populates *table with entries for env. +spv_result_t spvOperandTableGet(spv_operand_table* table, spv_target_env env); + +// Populates *table with entries for env. +spv_result_t spvExtInstTableGet(spv_ext_inst_table* table, spv_target_env env); + +#endif // SOURCE_TABLE_H_ diff --git a/third_party/spirv-tools/source/text.cpp b/third_party/spirv-tools/source/text.cpp new file mode 100644 index 0000000..88a8e8f --- /dev/null +++ b/third_party/spirv-tools/source/text.cpp @@ -0,0 +1,841 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/text.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/ext_inst.h" +#include "source/instruction.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/table.h" +#include "source/text_handler.h" +#include "source/util/bitutils.h" +#include "source/util/parse_number.h" +#include "spirv-tools/libspirv.h" + +bool spvIsValidIDCharacter(const char value) { + return value == '_' || 0 != ::isalnum(value); +} + +// Returns true if the given string represents a valid ID name. +bool spvIsValidID(const char* textValue) { + const char* c = textValue; + for (; *c != '\0'; ++c) { + if (!spvIsValidIDCharacter(*c)) { + return false; + } + } + // If the string was empty, then the ID also is not valid. + return c != textValue; +} + +// Text API + +spv_result_t spvTextToLiteral(const char* textValue, spv_literal_t* pLiteral) { + bool isSigned = false; + int numPeriods = 0; + bool isString = false; + + const size_t len = strlen(textValue); + if (len == 0) return SPV_FAILED_MATCH; + + for (uint64_t index = 0; index < len; ++index) { + switch (textValue[index]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case '.': + numPeriods++; + break; + case '-': + if (index == 0) { + isSigned = true; + } else { + isString = true; + } + break; + default: + isString = true; + index = len; // break out of the loop too. + break; + } + } + + pLiteral->type = spv_literal_type_t(99); + + if (isString || numPeriods > 1 || (isSigned && len == 1)) { + if (len < 2 || textValue[0] != '"' || textValue[len - 1] != '"') + return SPV_FAILED_MATCH; + bool escaping = false; + for (const char* val = textValue + 1; val != textValue + len - 1; ++val) { + if ((*val == '\\') && (!escaping)) { + escaping = true; + } else { + // Have to save space for the null-terminator + if (pLiteral->str.size() >= SPV_LIMIT_LITERAL_STRING_BYTES_MAX) + return SPV_ERROR_OUT_OF_MEMORY; + pLiteral->str.push_back(*val); + escaping = false; + } + } + + pLiteral->type = SPV_LITERAL_TYPE_STRING; + } else if (numPeriods == 1) { + double d = std::strtod(textValue, nullptr); + float f = (float)d; + if (d == (double)f) { + pLiteral->type = SPV_LITERAL_TYPE_FLOAT_32; + pLiteral->value.f = f; + } else { + pLiteral->type = SPV_LITERAL_TYPE_FLOAT_64; + pLiteral->value.d = d; + } + } else if (isSigned) { + int64_t i64 = strtoll(textValue, nullptr, 10); + int32_t i32 = (int32_t)i64; + if (i64 == (int64_t)i32) { + pLiteral->type = SPV_LITERAL_TYPE_INT_32; + pLiteral->value.i32 = i32; + } else { + pLiteral->type = SPV_LITERAL_TYPE_INT_64; + pLiteral->value.i64 = i64; + } + } else { + uint64_t u64 = strtoull(textValue, nullptr, 10); + uint32_t u32 = (uint32_t)u64; + if (u64 == (uint64_t)u32) { + pLiteral->type = SPV_LITERAL_TYPE_UINT_32; + pLiteral->value.u32 = u32; + } else { + pLiteral->type = SPV_LITERAL_TYPE_UINT_64; + pLiteral->value.u64 = u64; + } + } + + return SPV_SUCCESS; +} + +namespace { + +/// Parses an immediate integer from text, guarding against overflow. If +/// successful, adds the parsed value to pInst, advances the context past it, +/// and returns SPV_SUCCESS. Otherwise, leaves pInst alone, emits diagnostics, +/// and returns SPV_ERROR_INVALID_TEXT. +spv_result_t encodeImmediate(spvtools::AssemblyContext* context, + const char* text, spv_instruction_t* pInst) { + assert(*text == '!'); + uint32_t parse_result; + if (!spvtools::utils::ParseNumber(text + 1, &parse_result)) { + return context->diagnostic(SPV_ERROR_INVALID_TEXT) + << "Invalid immediate integer: !" << text + 1; + } + context->binaryEncodeU32(parse_result, pInst); + context->seekForward(static_cast(strlen(text))); + return SPV_SUCCESS; +} + +} // anonymous namespace + +/// @brief Translate an Opcode operand to binary form +/// +/// @param[in] grammar the grammar to use for compilation +/// @param[in, out] context the dynamic compilation info +/// @param[in] type of the operand +/// @param[in] textValue word of text to be parsed +/// @param[out] pInst return binary Opcode +/// @param[in,out] pExpectedOperands the operand types expected +/// +/// @return result code +spv_result_t spvTextEncodeOperand(const spvtools::AssemblyGrammar& grammar, + spvtools::AssemblyContext* context, + const spv_operand_type_t type, + const char* textValue, + spv_instruction_t* pInst, + spv_operand_pattern_t* pExpectedOperands) { + // NOTE: Handle immediate int in the stream + if ('!' == textValue[0]) { + if (auto error = encodeImmediate(context, textValue, pInst)) { + return error; + } + *pExpectedOperands = + spvAlternatePatternFollowingImmediate(*pExpectedOperands); + return SPV_SUCCESS; + } + + // Optional literal operands can fail to parse. In that case use + // SPV_FAILED_MATCH to avoid emitting a diagostic. Use the following + // for those situations. + spv_result_t error_code_for_literals = + spvOperandIsOptional(type) ? SPV_FAILED_MATCH : SPV_ERROR_INVALID_TEXT; + + switch (type) { + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_TYPE_ID: + case SPV_OPERAND_TYPE_RESULT_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + case SPV_OPERAND_TYPE_OPTIONAL_ID: { + if ('%' == textValue[0]) { + textValue++; + } else { + return context->diagnostic() << "Expected id to start with %."; + } + if (!spvIsValidID(textValue)) { + return context->diagnostic() << "Invalid ID " << textValue; + } + const uint32_t id = context->spvNamedIdAssignOrGet(textValue); + if (type == SPV_OPERAND_TYPE_TYPE_ID) pInst->resultTypeId = id; + spvInstructionAddWord(pInst, id); + + // Set the extended instruction type. + // The import set id is the 3rd operand of OpExtInst. + if (pInst->opcode == SpvOpExtInst && pInst->words.size() == 4) { + auto ext_inst_type = context->getExtInstTypeForId(pInst->words[3]); + if (ext_inst_type == SPV_EXT_INST_TYPE_NONE) { + return context->diagnostic() + << "Invalid extended instruction import Id " + << pInst->words[2]; + } + pInst->extInstType = ext_inst_type; + } + } break; + + case SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER: { + // The assembler accepts the symbolic name for an extended instruction, + // and emits its corresponding number. + spv_ext_inst_desc extInst; + if (grammar.lookupExtInst(pInst->extInstType, textValue, &extInst) == + SPV_SUCCESS) { + // if we know about this extended instruction, push the numeric value + spvInstructionAddWord(pInst, extInst->ext_inst); + + // Prepare to parse the operands for the extended instructions. + spvPushOperandTypes(extInst->operandTypes, pExpectedOperands); + } else { + // if we don't know this extended instruction and the set isn't + // non-semantic, we cannot process further + if (!spvExtInstIsNonSemantic(pInst->extInstType)) { + return context->diagnostic() + << "Invalid extended instruction name '" << textValue << "'."; + } else { + // for non-semantic instruction sets, as long as the text name is an + // integer value we can encode it since we know the form of all such + // extended instructions + spv_literal_t extInstValue; + if (spvTextToLiteral(textValue, &extInstValue) || + extInstValue.type != SPV_LITERAL_TYPE_UINT_32) { + return context->diagnostic() + << "Couldn't translate unknown extended instruction name '" + << textValue << "' to unsigned integer."; + } + + spvInstructionAddWord(pInst, extInstValue.value.u32); + + // opcode contains an unknown number of IDs. + pExpectedOperands->push_back(SPV_OPERAND_TYPE_VARIABLE_ID); + } + } + } break; + + case SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER: { + // The assembler accepts the symbolic name for the opcode, but without + // the "Op" prefix. For example, "IAdd" is accepted. The number + // of the opcode is emitted. + SpvOp opcode; + if (grammar.lookupSpecConstantOpcode(textValue, &opcode)) { + return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) + << " '" << textValue << "'."; + } + spv_opcode_desc opcodeEntry = nullptr; + if (grammar.lookupOpcode(opcode, &opcodeEntry)) { + return context->diagnostic(SPV_ERROR_INTERNAL) + << "OpSpecConstant opcode table out of sync"; + } + spvInstructionAddWord(pInst, uint32_t(opcodeEntry->opcode)); + + // Prepare to parse the operands for the opcode. Except skip the + // type Id and result Id, since they've already been processed. + assert(opcodeEntry->hasType); + assert(opcodeEntry->hasResult); + assert(opcodeEntry->numTypes >= 2); + spvPushOperandTypes(opcodeEntry->operandTypes + 2, pExpectedOperands); + } break; + + case SPV_OPERAND_TYPE_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER: { + // The current operand is an *unsigned* 32-bit integer. + // That's just how the grammar works. + spvtools::IdType expected_type = { + 32, false, spvtools::IdTypeClass::kScalarIntegerType}; + if (auto error = context->binaryEncodeNumericLiteral( + textValue, error_code_for_literals, expected_type, pInst)) { + return error; + } + } break; + + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER: + // This is a context-independent literal number which can be a 32-bit + // number of floating point value. + if (auto error = context->binaryEncodeNumericLiteral( + textValue, error_code_for_literals, spvtools::kUnknownType, + pInst)) { + return error; + } + break; + + case SPV_OPERAND_TYPE_OPTIONAL_TYPED_LITERAL_INTEGER: + case SPV_OPERAND_TYPE_TYPED_LITERAL_NUMBER: { + spvtools::IdType expected_type = spvtools::kUnknownType; + // The encoding for OpConstant, OpSpecConstant and OpSwitch all + // depend on either their own result-id or the result-id of + // one of their parameters. + if (SpvOpConstant == pInst->opcode || + SpvOpSpecConstant == pInst->opcode) { + // The type of the literal is determined by the type Id of the + // instruction. + expected_type = + context->getTypeOfTypeGeneratingValue(pInst->resultTypeId); + if (!spvtools::isScalarFloating(expected_type) && + !spvtools::isScalarIntegral(expected_type)) { + spv_opcode_desc d; + const char* opcode_name = "opcode"; + if (SPV_SUCCESS == grammar.lookupOpcode(pInst->opcode, &d)) { + opcode_name = d->name; + } + return context->diagnostic() + << "Type for " << opcode_name + << " must be a scalar floating point or integer type"; + } + } else if (pInst->opcode == SpvOpSwitch) { + // The type of the literal is the same as the type of the selector. + expected_type = context->getTypeOfValueInstruction(pInst->words[1]); + if (!spvtools::isScalarIntegral(expected_type)) { + return context->diagnostic() + << "The selector operand for OpSwitch must be the result" + " of an instruction that generates an integer scalar"; + } + } + if (auto error = context->binaryEncodeNumericLiteral( + textValue, error_code_for_literals, expected_type, pInst)) { + return error; + } + } break; + + case SPV_OPERAND_TYPE_LITERAL_STRING: + case SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING: { + spv_literal_t literal = {}; + spv_result_t error = spvTextToLiteral(textValue, &literal); + if (error != SPV_SUCCESS) { + if (error == SPV_ERROR_OUT_OF_MEMORY) return error; + return context->diagnostic(error_code_for_literals) + << "Invalid literal string '" << textValue << "'."; + } + if (literal.type != SPV_LITERAL_TYPE_STRING) { + return context->diagnostic() + << "Expected literal string, found literal number '" << textValue + << "'."; + } + + // NOTE: Special case for extended instruction library import + if (SpvOpExtInstImport == pInst->opcode) { + const spv_ext_inst_type_t ext_inst_type = + spvExtInstImportTypeGet(literal.str.c_str()); + if (SPV_EXT_INST_TYPE_NONE == ext_inst_type) { + return context->diagnostic() + << "Invalid extended instruction import '" << literal.str + << "'"; + } + if ((error = context->recordIdAsExtInstImport(pInst->words[1], + ext_inst_type))) + return error; + } + + if (context->binaryEncodeString(literal.str.c_str(), pInst)) + return SPV_ERROR_INVALID_TEXT; + } break; + + // Masks. + case SPV_OPERAND_TYPE_FP_FAST_MATH_MODE: + case SPV_OPERAND_TYPE_FUNCTION_CONTROL: + case SPV_OPERAND_TYPE_LOOP_CONTROL: + case SPV_OPERAND_TYPE_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_IMAGE: + case SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS: + case SPV_OPERAND_TYPE_SELECTION_CONTROL: + case SPV_OPERAND_TYPE_DEBUG_INFO_FLAGS: + case SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_INFO_FLAGS: { + uint32_t value; + if (grammar.parseMaskOperand(type, textValue, &value)) { + return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) + << " operand '" << textValue << "'."; + } + if (auto error = context->binaryEncodeU32(value, pInst)) return error; + // Prepare to parse the operands for this logical operand. + grammar.pushOperandTypesForMask(type, value, pExpectedOperands); + } break; + case SPV_OPERAND_TYPE_OPTIONAL_CIV: { + auto error = spvTextEncodeOperand( + grammar, context, SPV_OPERAND_TYPE_OPTIONAL_LITERAL_NUMBER, textValue, + pInst, pExpectedOperands); + if (error == SPV_FAILED_MATCH) { + // It's not a literal number -- is it a literal string? + error = spvTextEncodeOperand(grammar, context, + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_STRING, + textValue, pInst, pExpectedOperands); + } + if (error == SPV_FAILED_MATCH) { + // It's not a literal -- is it an ID? + error = + spvTextEncodeOperand(grammar, context, SPV_OPERAND_TYPE_OPTIONAL_ID, + textValue, pInst, pExpectedOperands); + } + if (error) { + return context->diagnostic(error) + << "Invalid word following !: " << textValue; + } + if (pExpectedOperands->empty()) { + pExpectedOperands->push_back(SPV_OPERAND_TYPE_OPTIONAL_CIV); + } + } break; + default: { + // NOTE: All non literal operands are handled here using the operand + // table. + spv_operand_desc entry; + if (grammar.lookupOperand(type, textValue, strlen(textValue), &entry)) { + return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) + << " '" << textValue << "'."; + } + if (context->binaryEncodeU32(entry->value, pInst)) { + return context->diagnostic() << "Invalid " << spvOperandTypeStr(type) + << " '" << textValue << "'."; + } + + // Prepare to parse the operands for this logical operand. + spvPushOperandTypes(entry->operandTypes, pExpectedOperands); + } break; + } + return SPV_SUCCESS; +} + +namespace { + +/// Encodes an instruction started by ! at the given position in text. +/// +/// Puts the encoded words into *pInst. If successful, moves position past the +/// instruction and returns SPV_SUCCESS. Otherwise, returns an error code and +/// leaves position pointing to the error in text. +spv_result_t encodeInstructionStartingWithImmediate( + const spvtools::AssemblyGrammar& grammar, + spvtools::AssemblyContext* context, spv_instruction_t* pInst) { + std::string firstWord; + spv_position_t nextPosition = {}; + auto error = context->getWord(&firstWord, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + + if ((error = encodeImmediate(context, firstWord.c_str(), pInst))) { + return error; + } + while (context->advance() != SPV_END_OF_STREAM) { + // A beginning of a new instruction means we're done. + if (context->isStartOfNewInst()) return SPV_SUCCESS; + + // Otherwise, there must be an operand that's either a literal, an ID, or + // an immediate. + std::string operandValue; + if ((error = context->getWord(&operandValue, &nextPosition))) + return context->diagnostic(error) << "Internal Error"; + + if (operandValue == "=") + return context->diagnostic() << firstWord << " not allowed before =."; + + // Needed to pass to spvTextEncodeOpcode(), but it shouldn't ever be + // expanded. + spv_operand_pattern_t dummyExpectedOperands; + error = spvTextEncodeOperand( + grammar, context, SPV_OPERAND_TYPE_OPTIONAL_CIV, operandValue.c_str(), + pInst, &dummyExpectedOperands); + if (error) return error; + context->setPosition(nextPosition); + } + return SPV_SUCCESS; +} + +/// @brief Translate single Opcode and operands to binary form +/// +/// @param[in] grammar the grammar to use for compilation +/// @param[in, out] context the dynamic compilation info +/// @param[in] text stream to translate +/// @param[out] pInst returned binary Opcode +/// @param[in,out] pPosition in the text stream +/// +/// @return result code +spv_result_t spvTextEncodeOpcode(const spvtools::AssemblyGrammar& grammar, + spvtools::AssemblyContext* context, + spv_instruction_t* pInst) { + // Check for ! first. + if ('!' == context->peek()) { + return encodeInstructionStartingWithImmediate(grammar, context, pInst); + } + + std::string firstWord; + spv_position_t nextPosition = {}; + spv_result_t error = context->getWord(&firstWord, &nextPosition); + if (error) return context->diagnostic() << "Internal Error"; + + std::string opcodeName; + std::string result_id; + spv_position_t result_id_position = {}; + if (context->startsWithOp()) { + opcodeName = firstWord; + } else { + result_id = firstWord; + if ('%' != result_id.front()) { + return context->diagnostic() + << "Expected or at the beginning " + "of an instruction, found '" + << result_id << "'."; + } + result_id_position = context->position(); + + // The '=' sign. + context->setPosition(nextPosition); + if (context->advance()) + return context->diagnostic() << "Expected '=', found end of stream."; + std::string equal_sign; + error = context->getWord(&equal_sign, &nextPosition); + if ("=" != equal_sign) + return context->diagnostic() << "'=' expected after result id."; + + // The after the '=' sign. + context->setPosition(nextPosition); + if (context->advance()) + return context->diagnostic() << "Expected opcode, found end of stream."; + error = context->getWord(&opcodeName, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + if (!context->startsWithOp()) { + return context->diagnostic() + << "Invalid Opcode prefix '" << opcodeName << "'."; + } + } + + // NOTE: The table contains Opcode names without the "Op" prefix. + const char* pInstName = opcodeName.data() + 2; + + spv_opcode_desc opcodeEntry; + error = grammar.lookupOpcode(pInstName, &opcodeEntry); + if (error) { + return context->diagnostic(error) + << "Invalid Opcode name '" << opcodeName << "'"; + } + if (opcodeEntry->hasResult && result_id.empty()) { + return context->diagnostic() + << "Expected at the beginning of an instruction, found '" + << firstWord << "'."; + } + if (!opcodeEntry->hasResult && !result_id.empty()) { + return context->diagnostic() + << "Cannot set ID " << result_id << " because " << opcodeName + << " does not produce a result ID."; + } + pInst->opcode = opcodeEntry->opcode; + context->setPosition(nextPosition); + // Reserve the first word for the instruction. + spvInstructionAddWord(pInst, 0); + + // Maintains the ordered list of expected operand types. + // For many instructions we only need the {numTypes, operandTypes} + // entries in opcodeEntry. However, sometimes we need to modify + // the list as we parse the operands. This occurs when an operand + // has its own logical operands (such as the LocalSize operand for + // ExecutionMode), or for extended instructions that may have their + // own operands depending on the selected extended instruction. + spv_operand_pattern_t expectedOperands; + expectedOperands.reserve(opcodeEntry->numTypes); + for (auto i = 0; i < opcodeEntry->numTypes; i++) + expectedOperands.push_back( + opcodeEntry->operandTypes[opcodeEntry->numTypes - i - 1]); + + while (!expectedOperands.empty()) { + const spv_operand_type_t type = expectedOperands.back(); + expectedOperands.pop_back(); + + // Expand optional tuples lazily. + if (spvExpandOperandSequenceOnce(type, &expectedOperands)) continue; + + if (type == SPV_OPERAND_TYPE_RESULT_ID && !result_id.empty()) { + // Handle the for value generating instructions. + // We've already consumed it from the text stream. Here + // we inject its words into the instruction. + spv_position_t temp_pos = context->position(); + error = spvTextEncodeOperand(grammar, context, SPV_OPERAND_TYPE_RESULT_ID, + result_id.c_str(), pInst, nullptr); + result_id_position = context->position(); + // Because we are injecting we have to reset the position afterwards. + context->setPosition(temp_pos); + if (error) return error; + } else { + // Find the next word. + error = context->advance(); + if (error == SPV_END_OF_STREAM) { + if (spvOperandIsOptional(type)) { + // This would have been the last potential operand for the + // instruction, + // and we didn't find one. We're finished parsing this instruction. + break; + } else { + return context->diagnostic() + << "Expected operand, found end of stream."; + } + } + assert(error == SPV_SUCCESS && "Somebody added another way to fail"); + + if (context->isStartOfNewInst()) { + if (spvOperandIsOptional(type)) { + break; + } else { + return context->diagnostic() + << "Expected operand, found next instruction instead."; + } + } + + std::string operandValue; + error = context->getWord(&operandValue, &nextPosition); + if (error) return context->diagnostic(error) << "Internal Error"; + + error = spvTextEncodeOperand(grammar, context, type, operandValue.c_str(), + pInst, &expectedOperands); + + if (error == SPV_FAILED_MATCH && spvOperandIsOptional(type)) + return SPV_SUCCESS; + + if (error) return error; + + context->setPosition(nextPosition); + } + } + + if (spvOpcodeGeneratesType(pInst->opcode)) { + if (context->recordTypeDefinition(pInst) != SPV_SUCCESS) { + return SPV_ERROR_INVALID_TEXT; + } + } else if (opcodeEntry->hasType) { + // SPIR-V dictates that if an instruction has both a return value and a + // type ID then the type id is first, and the return value is second. + assert(opcodeEntry->hasResult && + "Unknown opcode: has a type but no result."); + context->recordTypeIdForValue(pInst->words[2], pInst->words[1]); + } + + if (pInst->words.size() > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { + return context->diagnostic() + << "Instruction too long: " << pInst->words.size() + << " words, but the limit is " + << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX; + } + + pInst->words[0] = + spvOpcodeMake(uint16_t(pInst->words.size()), opcodeEntry->opcode); + + return SPV_SUCCESS; +} + +enum { kAssemblerVersion = 0 }; + +// Populates a binary stream's |header|. The target environment is specified via +// |env| and Id bound is via |bound|. +spv_result_t SetHeader(spv_target_env env, const uint32_t bound, + uint32_t* header) { + if (!header) return SPV_ERROR_INVALID_BINARY; + + header[SPV_INDEX_MAGIC_NUMBER] = SpvMagicNumber; + header[SPV_INDEX_VERSION_NUMBER] = spvVersionForTargetEnv(env); + header[SPV_INDEX_GENERATOR_NUMBER] = + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, kAssemblerVersion); + header[SPV_INDEX_BOUND] = bound; + header[SPV_INDEX_SCHEMA] = 0; // NOTE: Reserved + + return SPV_SUCCESS; +} + +// Collects all numeric ids in the module source into |numeric_ids|. +// This function is essentially a dry-run of spvTextToBinary. +spv_result_t GetNumericIds(const spvtools::AssemblyGrammar& grammar, + const spvtools::MessageConsumer& consumer, + const spv_text text, + std::set* numeric_ids) { + spvtools::AssemblyContext context(text, consumer); + + if (!text->str) return context.diagnostic() << "Missing assembly text."; + + if (!grammar.isValid()) { + return SPV_ERROR_INVALID_TABLE; + } + + // Skip past whitespace and comments. + context.advance(); + + while (context.hasText()) { + spv_instruction_t inst; + + if (spvTextEncodeOpcode(grammar, &context, &inst)) { + return SPV_ERROR_INVALID_TEXT; + } + + if (context.advance()) break; + } + + *numeric_ids = context.GetNumericIds(); + return SPV_SUCCESS; +} + +// Translates a given assembly language module into binary form. +// If a diagnostic is generated, it is not yet marked as being +// for a text-based input. +spv_result_t spvTextToBinaryInternal(const spvtools::AssemblyGrammar& grammar, + const spvtools::MessageConsumer& consumer, + const spv_text text, + const uint32_t options, + spv_binary* pBinary) { + // The ids in this set will have the same values both in source and binary. + // All other ids will be generated by filling in the gaps. + std::set ids_to_preserve; + + if (options & SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS) { + // Collect all numeric ids from the source into ids_to_preserve. + const spv_result_t result = + GetNumericIds(grammar, consumer, text, &ids_to_preserve); + if (result != SPV_SUCCESS) return result; + } + + spvtools::AssemblyContext context(text, consumer, std::move(ids_to_preserve)); + + if (!text->str) return context.diagnostic() << "Missing assembly text."; + + if (!grammar.isValid()) { + return SPV_ERROR_INVALID_TABLE; + } + if (!pBinary) return SPV_ERROR_INVALID_POINTER; + + std::vector instructions; + + // Skip past whitespace and comments. + context.advance(); + + while (context.hasText()) { + instructions.push_back({}); + spv_instruction_t& inst = instructions.back(); + + if (spvTextEncodeOpcode(grammar, &context, &inst)) { + return SPV_ERROR_INVALID_TEXT; + } + + if (context.advance()) break; + } + + size_t totalSize = SPV_INDEX_INSTRUCTION; + for (auto& inst : instructions) { + totalSize += inst.words.size(); + } + + uint32_t* data = new uint32_t[totalSize]; + if (!data) return SPV_ERROR_OUT_OF_MEMORY; + uint64_t currentIndex = SPV_INDEX_INSTRUCTION; + for (auto& inst : instructions) { + memcpy(data + currentIndex, inst.words.data(), + sizeof(uint32_t) * inst.words.size()); + currentIndex += inst.words.size(); + } + + if (auto error = SetHeader(grammar.target_env(), context.getBound(), data)) + return error; + + spv_binary binary = new spv_binary_t(); + if (!binary) { + delete[] data; + return SPV_ERROR_OUT_OF_MEMORY; + } + binary->code = data; + binary->wordCount = totalSize; + + *pBinary = binary; + + return SPV_SUCCESS; +} + +} // anonymous namespace + +spv_result_t spvTextToBinary(const spv_const_context context, + const char* input_text, + const size_t input_text_size, spv_binary* pBinary, + spv_diagnostic* pDiagnostic) { + return spvTextToBinaryWithOptions(context, input_text, input_text_size, + SPV_TEXT_TO_BINARY_OPTION_NONE, pBinary, + pDiagnostic); +} + +spv_result_t spvTextToBinaryWithOptions(const spv_const_context context, + const char* input_text, + const size_t input_text_size, + const uint32_t options, + spv_binary* pBinary, + spv_diagnostic* pDiagnostic) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + spv_text_t text = {input_text, input_text_size}; + spvtools::AssemblyGrammar grammar(&hijack_context); + + spv_result_t result = spvTextToBinaryInternal( + grammar, hijack_context.consumer, &text, options, pBinary); + if (pDiagnostic && *pDiagnostic) (*pDiagnostic)->isTextSource = true; + + return result; +} + +void spvTextDestroy(spv_text text) { + if (text) { + if (text->str) delete[] text->str; + delete text; + } +} diff --git a/third_party/spirv-tools/source/text.h b/third_party/spirv-tools/source/text.h new file mode 100644 index 0000000..fa34ee1 --- /dev/null +++ b/third_party/spirv-tools/source/text.h @@ -0,0 +1,53 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_TEXT_H_ +#define SOURCE_TEXT_H_ + +#include + +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "spirv-tools/libspirv.h" + +typedef enum spv_literal_type_t { + SPV_LITERAL_TYPE_INT_32, + SPV_LITERAL_TYPE_INT_64, + SPV_LITERAL_TYPE_UINT_32, + SPV_LITERAL_TYPE_UINT_64, + SPV_LITERAL_TYPE_FLOAT_32, + SPV_LITERAL_TYPE_FLOAT_64, + SPV_LITERAL_TYPE_STRING, + SPV_FORCE_32_BIT_ENUM(spv_literal_type_t) +} spv_literal_type_t; + +typedef struct spv_literal_t { + spv_literal_type_t type; + union value_t { + int32_t i32; + int64_t i64; + uint32_t u32; + uint64_t u64; + float f; + double d; + } value; + std::string str; // Special field for literal string. +} spv_literal_t; + +// Converts the given text string to a number/string literal and writes the +// result to *literal. String literals must be surrounded by double-quotes ("), +// which are then stripped. +spv_result_t spvTextToLiteral(const char* text, spv_literal_t* literal); + +#endif // SOURCE_TEXT_H_ diff --git a/third_party/spirv-tools/source/text_handler.cpp b/third_party/spirv-tools/source/text_handler.cpp new file mode 100644 index 0000000..c31f34a --- /dev/null +++ b/third_party/spirv-tools/source/text_handler.cpp @@ -0,0 +1,397 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/text_handler.h" + +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/binary.h" +#include "source/ext_inst.h" +#include "source/instruction.h" +#include "source/opcode.h" +#include "source/text.h" +#include "source/util/bitutils.h" +#include "source/util/hex_float.h" +#include "source/util/parse_number.h" + +namespace spvtools { +namespace { + +// Advances |text| to the start of the next line and writes the new position to +// |position|. +spv_result_t advanceLine(spv_text text, spv_position position) { + while (true) { + if (position->index >= text->length) return SPV_END_OF_STREAM; + switch (text->str[position->index]) { + case '\0': + return SPV_END_OF_STREAM; + case '\n': + position->column = 0; + position->line++; + position->index++; + return SPV_SUCCESS; + default: + position->column++; + position->index++; + break; + } + } +} + +// Advances |text| to first non white space character and writes the new +// position to |position|. +// If a null terminator is found during the text advance, SPV_END_OF_STREAM is +// returned, SPV_SUCCESS otherwise. No error checking is performed on the +// parameters, its the users responsibility to ensure these are non null. +spv_result_t advance(spv_text text, spv_position position) { + // NOTE: Consume white space, otherwise don't advance. + if (position->index >= text->length) return SPV_END_OF_STREAM; + switch (text->str[position->index]) { + case '\0': + return SPV_END_OF_STREAM; + case ';': + if (spv_result_t error = advanceLine(text, position)) return error; + return advance(text, position); + case ' ': + case '\t': + case '\r': + position->column++; + position->index++; + return advance(text, position); + case '\n': + position->column = 0; + position->line++; + position->index++; + return advance(text, position); + default: + break; + } + return SPV_SUCCESS; +} + +// Fetches the next word from the given text stream starting from the given +// *position. On success, writes the decoded word into *word and updates +// *position to the location past the returned word. +// +// A word ends at the next comment or whitespace. However, double-quoted +// strings remain intact, and a backslash always escapes the next character. +spv_result_t getWord(spv_text text, spv_position position, std::string* word) { + if (!text->str || !text->length) return SPV_ERROR_INVALID_TEXT; + if (!position) return SPV_ERROR_INVALID_POINTER; + + const size_t start_index = position->index; + + bool quoting = false; + bool escaping = false; + + // NOTE: Assumes first character is not white space! + while (true) { + if (position->index >= text->length) { + word->assign(text->str + start_index, text->str + position->index); + return SPV_SUCCESS; + } + const char ch = text->str[position->index]; + if (ch == '\\') { + escaping = !escaping; + } else { + switch (ch) { + case '"': + if (!escaping) quoting = !quoting; + break; + case ' ': + case ';': + case '\t': + case '\n': + case '\r': + if (escaping || quoting) break; + // Fall through. + case '\0': { // NOTE: End of word found! + word->assign(text->str + start_index, text->str + position->index); + return SPV_SUCCESS; + } + default: + break; + } + escaping = false; + } + + position->column++; + position->index++; + } +} + +// Returns true if the characters in the text as position represent +// the start of an Opcode. +bool startsWithOp(spv_text text, spv_position position) { + if (text->length < position->index + 3) return false; + char ch0 = text->str[position->index]; + char ch1 = text->str[position->index + 1]; + char ch2 = text->str[position->index + 2]; + return ('O' == ch0 && 'p' == ch1 && ('A' <= ch2 && ch2 <= 'Z')); +} + +} // namespace + +const IdType kUnknownType = {0, false, IdTypeClass::kBottom}; + +// TODO(dneto): Reorder AssemblyContext definitions to match declaration order. + +// This represents all of the data that is only valid for the duration of +// a single compilation. +uint32_t AssemblyContext::spvNamedIdAssignOrGet(const char* textValue) { + if (!ids_to_preserve_.empty()) { + uint32_t id = 0; + if (spvtools::utils::ParseNumber(textValue, &id)) { + if (ids_to_preserve_.find(id) != ids_to_preserve_.end()) { + bound_ = std::max(bound_, id + 1); + return id; + } + } + } + + const auto it = named_ids_.find(textValue); + if (it == named_ids_.end()) { + uint32_t id = next_id_++; + if (!ids_to_preserve_.empty()) { + while (ids_to_preserve_.find(id) != ids_to_preserve_.end()) { + id = next_id_++; + } + } + + named_ids_.emplace(textValue, id); + bound_ = std::max(bound_, id + 1); + return id; + } + + return it->second; +} + +uint32_t AssemblyContext::getBound() const { return bound_; } + +spv_result_t AssemblyContext::advance() { + return spvtools::advance(text_, ¤t_position_); +} + +spv_result_t AssemblyContext::getWord(std::string* word, + spv_position next_position) { + *next_position = current_position_; + return spvtools::getWord(text_, next_position, word); +} + +bool AssemblyContext::startsWithOp() { + return spvtools::startsWithOp(text_, ¤t_position_); +} + +bool AssemblyContext::isStartOfNewInst() { + spv_position_t pos = current_position_; + if (spvtools::advance(text_, &pos)) return false; + if (spvtools::startsWithOp(text_, &pos)) return true; + + std::string word; + pos = current_position_; + if (spvtools::getWord(text_, &pos, &word)) return false; + if ('%' != word.front()) return false; + + if (spvtools::advance(text_, &pos)) return false; + if (spvtools::getWord(text_, &pos, &word)) return false; + if ("=" != word) return false; + + if (spvtools::advance(text_, &pos)) return false; + if (spvtools::startsWithOp(text_, &pos)) return true; + return false; +} + +char AssemblyContext::peek() const { + return text_->str[current_position_.index]; +} + +bool AssemblyContext::hasText() const { + return text_->length > current_position_.index; +} + +void AssemblyContext::seekForward(uint32_t size) { + current_position_.index += size; + current_position_.column += size; +} + +spv_result_t AssemblyContext::binaryEncodeU32(const uint32_t value, + spv_instruction_t* pInst) { + pInst->words.insert(pInst->words.end(), value); + return SPV_SUCCESS; +} + +spv_result_t AssemblyContext::binaryEncodeNumericLiteral( + const char* val, spv_result_t error_code, const IdType& type, + spv_instruction_t* pInst) { + using spvtools::utils::EncodeNumberStatus; + // Populate the NumberType from the IdType for parsing. + spvtools::utils::NumberType number_type; + switch (type.type_class) { + case IdTypeClass::kOtherType: + return diagnostic(SPV_ERROR_INTERNAL) + << "Unexpected numeric literal type"; + case IdTypeClass::kScalarIntegerType: + if (type.isSigned) { + number_type = {type.bitwidth, SPV_NUMBER_SIGNED_INT}; + } else { + number_type = {type.bitwidth, SPV_NUMBER_UNSIGNED_INT}; + } + break; + case IdTypeClass::kScalarFloatType: + number_type = {type.bitwidth, SPV_NUMBER_FLOATING}; + break; + case IdTypeClass::kBottom: + // kBottom means the type is unknown and we need to infer the type before + // parsing the number. The rule is: If there is a decimal point, treat + // the value as a floating point value, otherwise a integer value, then + // if the first char of the integer text is '-', treat the integer as a + // signed integer, otherwise an unsigned integer. + uint32_t bitwidth = static_cast(assumedBitWidth(type)); + if (strchr(val, '.')) { + number_type = {bitwidth, SPV_NUMBER_FLOATING}; + } else if (type.isSigned || val[0] == '-') { + number_type = {bitwidth, SPV_NUMBER_SIGNED_INT}; + } else { + number_type = {bitwidth, SPV_NUMBER_UNSIGNED_INT}; + } + break; + } + + std::string error_msg; + EncodeNumberStatus parse_status = ParseAndEncodeNumber( + val, number_type, + [this, pInst](uint32_t d) { this->binaryEncodeU32(d, pInst); }, + &error_msg); + switch (parse_status) { + case EncodeNumberStatus::kSuccess: + return SPV_SUCCESS; + case EncodeNumberStatus::kInvalidText: + return diagnostic(error_code) << error_msg; + case EncodeNumberStatus::kUnsupported: + return diagnostic(SPV_ERROR_INTERNAL) << error_msg; + case EncodeNumberStatus::kInvalidUsage: + return diagnostic(SPV_ERROR_INVALID_TEXT) << error_msg; + } + // This line is not reachable, only added to satisfy the compiler. + return diagnostic(SPV_ERROR_INTERNAL) + << "Unexpected result code from ParseAndEncodeNumber()"; +} + +spv_result_t AssemblyContext::binaryEncodeString(const char* value, + spv_instruction_t* pInst) { + const size_t length = strlen(value); + const size_t wordCount = (length / 4) + 1; + const size_t oldWordCount = pInst->words.size(); + const size_t newWordCount = oldWordCount + wordCount; + + // TODO(dneto): We can just defer this check until later. + if (newWordCount > SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX) { + return diagnostic() << "Instruction too long: more than " + << SPV_LIMIT_INSTRUCTION_WORD_COUNT_MAX << " words."; + } + + pInst->words.resize(newWordCount); + + // Make sure all the bytes in the last word are 0, in case we only + // write a partial word at the end. + pInst->words.back() = 0; + + char* dest = (char*)&pInst->words[oldWordCount]; + strncpy(dest, value, length + 1); + + return SPV_SUCCESS; +} + +spv_result_t AssemblyContext::recordTypeDefinition( + const spv_instruction_t* pInst) { + uint32_t value = pInst->words[1]; + if (types_.find(value) != types_.end()) { + return diagnostic() << "Value " << value + << " has already been used to generate a type"; + } + + if (pInst->opcode == SpvOpTypeInt) { + if (pInst->words.size() != 4) + return diagnostic() << "Invalid OpTypeInt instruction"; + types_[value] = {pInst->words[2], pInst->words[3] != 0, + IdTypeClass::kScalarIntegerType}; + } else if (pInst->opcode == SpvOpTypeFloat) { + if (pInst->words.size() != 3) + return diagnostic() << "Invalid OpTypeFloat instruction"; + types_[value] = {pInst->words[2], false, IdTypeClass::kScalarFloatType}; + } else { + types_[value] = {0, false, IdTypeClass::kOtherType}; + } + return SPV_SUCCESS; +} + +IdType AssemblyContext::getTypeOfTypeGeneratingValue(uint32_t value) const { + auto type = types_.find(value); + if (type == types_.end()) { + return kUnknownType; + } + return std::get<1>(*type); +} + +IdType AssemblyContext::getTypeOfValueInstruction(uint32_t value) const { + auto type_value = value_types_.find(value); + if (type_value == value_types_.end()) { + return {0, false, IdTypeClass::kBottom}; + } + return getTypeOfTypeGeneratingValue(std::get<1>(*type_value)); +} + +spv_result_t AssemblyContext::recordTypeIdForValue(uint32_t value, + uint32_t type) { + bool successfully_inserted = false; + std::tie(std::ignore, successfully_inserted) = + value_types_.insert(std::make_pair(value, type)); + if (!successfully_inserted) + return diagnostic() << "Value is being defined a second time"; + return SPV_SUCCESS; +} + +spv_result_t AssemblyContext::recordIdAsExtInstImport( + uint32_t id, spv_ext_inst_type_t type) { + bool successfully_inserted = false; + std::tie(std::ignore, successfully_inserted) = + import_id_to_ext_inst_type_.insert(std::make_pair(id, type)); + if (!successfully_inserted) + return diagnostic() << "Import Id is being defined a second time"; + return SPV_SUCCESS; +} + +spv_ext_inst_type_t AssemblyContext::getExtInstTypeForId(uint32_t id) const { + auto type = import_id_to_ext_inst_type_.find(id); + if (type == import_id_to_ext_inst_type_.end()) { + return SPV_EXT_INST_TYPE_NONE; + } + return std::get<1>(*type); +} + +std::set AssemblyContext::GetNumericIds() const { + std::set ids; + for (const auto& kv : named_ids_) { + uint32_t id; + if (spvtools::utils::ParseNumber(kv.first.c_str(), &id)) ids.insert(id); + } + return ids; +} + +} // namespace spvtools diff --git a/third_party/spirv-tools/source/text_handler.h b/third_party/spirv-tools/source/text_handler.h new file mode 100644 index 0000000..19972e9 --- /dev/null +++ b/third_party/spirv-tools/source/text_handler.h @@ -0,0 +1,264 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_TEXT_HANDLER_H_ +#define SOURCE_TEXT_HANDLER_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/instruction.h" +#include "source/text.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { + +// Structures + +// This is a lattice for tracking types. +enum class IdTypeClass { + kBottom = 0, // We have no information yet. + kScalarIntegerType, + kScalarFloatType, + kOtherType +}; + +// Contains ID type information that needs to be tracked across all Ids. +// Bitwidth is only valid when type_class is kScalarIntegerType or +// kScalarFloatType. +struct IdType { + uint32_t bitwidth; // Safe to assume that we will not have > 2^32 bits. + bool isSigned; // This is only significant if type_class is integral. + IdTypeClass type_class; +}; + +// Default equality operator for IdType. Tests if all members are the same. +inline bool operator==(const IdType& first, const IdType& second) { + return (first.bitwidth == second.bitwidth) && + (first.isSigned == second.isSigned) && + (first.type_class == second.type_class); +} + +// Tests whether any member of the IdTypes do not match. +inline bool operator!=(const IdType& first, const IdType& second) { + return !(first == second); +} + +// A value representing an unknown type. +extern const IdType kUnknownType; + +// Returns true if the type is a scalar integer type. +inline bool isScalarIntegral(const IdType& type) { + return type.type_class == IdTypeClass::kScalarIntegerType; +} + +// Returns true if the type is a scalar floating point type. +inline bool isScalarFloating(const IdType& type) { + return type.type_class == IdTypeClass::kScalarFloatType; +} + +// Returns the number of bits in the type. +// This is only valid for bottom, scalar integer, and scalar floating +// classes. For bottom, assume 32 bits. +inline int assumedBitWidth(const IdType& type) { + switch (type.type_class) { + case IdTypeClass::kBottom: + return 32; + case IdTypeClass::kScalarIntegerType: + case IdTypeClass::kScalarFloatType: + return type.bitwidth; + default: + break; + } + // We don't care about this case. + return 0; +} + +// A templated class with a static member function Clamp, where Clamp +// sets a referenced value of type T to 0 if T is an unsigned +// integer type, and returns true if it modified the referenced +// value. +template +class ClampToZeroIfUnsignedType { + public: + // The default specialization does not clamp the value. + static bool Clamp(T*) { return false; } +}; + +// The specialization of ClampToZeroIfUnsignedType for unsigned integer +// types. +template +class ClampToZeroIfUnsignedType< + T, typename std::enable_if::value>::type> { + public: + static bool Clamp(T* value_pointer) { + if (*value_pointer) { + *value_pointer = 0; + return true; + } + return false; + } +}; + +// Encapsulates the data used during the assembly of a SPIR-V module. +class AssemblyContext { + public: + AssemblyContext(spv_text text, const MessageConsumer& consumer, + std::set&& ids_to_preserve = std::set()) + : current_position_({}), + consumer_(consumer), + text_(text), + bound_(1), + next_id_(1), + ids_to_preserve_(std::move(ids_to_preserve)) {} + + // Assigns a new integer value to the given text ID, or returns the previously + // assigned integer value if the ID has been seen before. + uint32_t spvNamedIdAssignOrGet(const char* textValue); + + // Returns the largest largest numeric ID that has been assigned. + uint32_t getBound() const; + + // Advances position to point to the next word in the input stream. + // Returns SPV_SUCCESS on success. + spv_result_t advance(); + + // Sets word to the next word in the input text. Fills next_position with + // the next location past the end of the word. + spv_result_t getWord(std::string* word, spv_position next_position); + + // Returns true if the next word in the input is the start of a new Opcode. + bool startsWithOp(); + + // Returns true if the next word in the input is the start of a new + // instruction. + bool isStartOfNewInst(); + + // Returns a diagnostic object initialized with current position in the input + // stream, and for the given error code. Any data written to this object will + // show up in pDiagnsotic on destruction. + DiagnosticStream diagnostic(spv_result_t error) { + return DiagnosticStream(current_position_, consumer_, "", error); + } + + // Returns a diagnostic object with the default assembly error code. + DiagnosticStream diagnostic() { + // The default failure for assembly is invalid text. + return diagnostic(SPV_ERROR_INVALID_TEXT); + } + + // Returns then next character in the input stream. + char peek() const; + + // Returns true if there is more text in the input stream. + bool hasText() const; + + // Seeks the input stream forward by 'size' characters. + void seekForward(uint32_t size); + + // Sets the current position in the input stream to the given position. + void setPosition(const spv_position_t& newPosition) { + current_position_ = newPosition; + } + + // Returns the current position in the input stream. + const spv_position_t& position() const { return current_position_; } + + // Appends the given 32-bit value to the given instruction. + // Returns SPV_SUCCESS if the value could be correctly inserted in the + // instruction. + spv_result_t binaryEncodeU32(const uint32_t value, spv_instruction_t* pInst); + + // Appends the given string to the given instruction. + // Returns SPV_SUCCESS if the value could be correctly inserted in the + // instruction. + spv_result_t binaryEncodeString(const char* value, spv_instruction_t* pInst); + + // Appends the given numeric literal to the given instruction. + // Validates and respects the bitwidth supplied in the IdType argument. + // If the type is of class kBottom the value will be encoded as a + // 32-bit integer. + // Returns SPV_SUCCESS if the value could be correctly added to the + // instruction. Returns the given error code on failure, and emits + // a diagnostic if that error code is not SPV_FAILED_MATCH. + spv_result_t binaryEncodeNumericLiteral(const char* numeric_literal, + spv_result_t error_code, + const IdType& type, + spv_instruction_t* pInst); + + // Returns the IdType associated with this type-generating value. + // If the type has not been previously recorded with recordTypeDefinition, + // kUnknownType will be returned. + IdType getTypeOfTypeGeneratingValue(uint32_t value) const; + + // Returns the IdType that represents the return value of this Value + // generating instruction. + // If the value has not been recorded with recordTypeIdForValue, or the type + // could not be determined kUnknownType will be returned. + IdType getTypeOfValueInstruction(uint32_t value) const; + + // Tracks the type-defining instruction. The result of the tracking can + // later be queried using getValueType. + // pInst is expected to be completely filled in by the time this instruction + // is called. + // Returns SPV_SUCCESS on success, or SPV_ERROR_INVALID_VALUE on error. + spv_result_t recordTypeDefinition(const spv_instruction_t* pInst); + + // Tracks the relationship between the value and its type. + spv_result_t recordTypeIdForValue(uint32_t value, uint32_t type); + + // Records the given Id as being the import of the given extended instruction + // type. + spv_result_t recordIdAsExtInstImport(uint32_t id, spv_ext_inst_type_t type); + + // Returns the extended instruction type corresponding to the import with + // the given Id, if it exists. Returns SPV_EXT_INST_TYPE_NONE if the + // id is not the id for an extended instruction type. + spv_ext_inst_type_t getExtInstTypeForId(uint32_t id) const; + + // Returns a set consisting of each ID generated by spvNamedIdAssignOrGet from + // a numeric ID text representation. For example, generated from "%12" but not + // from "%foo". + std::set GetNumericIds() const; + + private: + // Maps ID names to their corresponding numerical ids. + using spv_named_id_table = std::unordered_map; + // Maps type-defining IDs to their IdType. + using spv_id_to_type_map = std::unordered_map; + // Maps Ids to the id of their type. + using spv_id_to_type_id = std::unordered_map; + + spv_named_id_table named_ids_; + spv_id_to_type_map types_; + spv_id_to_type_id value_types_; + // Maps an extended instruction import Id to the extended instruction type. + std::unordered_map import_id_to_ext_inst_type_; + spv_position_t current_position_; + MessageConsumer consumer_; + spv_text text_; + uint32_t bound_; + uint32_t next_id_; + std::set ids_to_preserve_; +}; + +} // namespace spvtools + +#endif // SOURCE_TEXT_HANDLER_H_ diff --git a/third_party/spirv-tools/source/util/bit_vector.cpp b/third_party/spirv-tools/source/util/bit_vector.cpp new file mode 100644 index 0000000..47e275b --- /dev/null +++ b/third_party/spirv-tools/source/util/bit_vector.cpp @@ -0,0 +1,82 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/util/bit_vector.h" + +#include +#include + +namespace spvtools { +namespace utils { + +void BitVector::ReportDensity(std::ostream& out) { + uint32_t count = 0; + + for (BitContainer e : bits_) { + while (e != 0) { + if ((e & 1) != 0) { + ++count; + } + e = e >> 1; + } + } + + out << "count=" << count + << ", total size (bytes)=" << bits_.size() * sizeof(BitContainer) + << ", bytes per element=" + << (double)(bits_.size() * sizeof(BitContainer)) / (double)(count); +} + +bool BitVector::Or(const BitVector& other) { + auto this_it = this->bits_.begin(); + auto other_it = other.bits_.begin(); + bool modified = false; + + while (this_it != this->bits_.end() && other_it != other.bits_.end()) { + auto temp = *this_it | *other_it; + if (temp != *this_it) { + modified = true; + *this_it = temp; + } + ++this_it; + ++other_it; + } + + if (other_it != other.bits_.end()) { + modified = true; + this->bits_.insert(this->bits_.end(), other_it, other.bits_.end()); + } + + return modified; +} + +std::ostream& operator<<(std::ostream& out, const BitVector& bv) { + out << "{"; + for (uint32_t i = 0; i < bv.bits_.size(); ++i) { + BitVector::BitContainer b = bv.bits_[i]; + uint32_t j = 0; + while (b != 0) { + if (b & 1) { + out << ' ' << i * BitVector::kBitContainerSize + j; + } + ++j; + b = b >> 1; + } + } + out << "}"; + return out; +} + +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/source/util/bit_vector.h b/third_party/spirv-tools/source/util/bit_vector.h new file mode 100644 index 0000000..3e189cb --- /dev/null +++ b/third_party/spirv-tools/source/util/bit_vector.h @@ -0,0 +1,119 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_BIT_VECTOR_H_ +#define SOURCE_UTIL_BIT_VECTOR_H_ + +#include +#include +#include + +namespace spvtools { +namespace utils { + +// Implements a bit vector class. +// +// All bits default to zero, and the upper bound is 2^32-1. +class BitVector { + private: + using BitContainer = uint64_t; + enum { kBitContainerSize = 64 }; + enum { kInitialNumBits = 1024 }; + + public: + // Creates a bit vector contianing 0s. + BitVector(uint32_t reserved_size = kInitialNumBits) + : bits_((reserved_size - 1) / kBitContainerSize + 1, 0) {} + + // Sets the |i|th bit to 1. Returns the |i|th bit before it was set. + bool Set(uint32_t i) { + uint32_t element_index = i / kBitContainerSize; + uint32_t bit_in_element = i % kBitContainerSize; + + if (element_index >= bits_.size()) { + bits_.resize(element_index + 1, 0); + } + + BitContainer original = bits_[element_index]; + BitContainer ith_bit = static_cast(1) << bit_in_element; + + if ((original & ith_bit) != 0) { + return true; + } else { + bits_[element_index] = original | ith_bit; + return false; + } + } + + // Sets the |i|th bit to 0. Return the |i|th bit before it was cleared. + bool Clear(uint32_t i) { + uint32_t element_index = i / kBitContainerSize; + uint32_t bit_in_element = i % kBitContainerSize; + + if (element_index >= bits_.size()) { + return false; + } + + BitContainer original = bits_[element_index]; + BitContainer ith_bit = static_cast(1) << bit_in_element; + + if ((original & ith_bit) == 0) { + return false; + } else { + bits_[element_index] = original & (~ith_bit); + return true; + } + } + + // Returns the |i|th bit. + bool Get(uint32_t i) const { + uint32_t element_index = i / kBitContainerSize; + uint32_t bit_in_element = i % kBitContainerSize; + + if (element_index >= bits_.size()) { + return false; + } + + return (bits_[element_index] & + (static_cast(1) << bit_in_element)) != 0; + } + + // Returns true if every bit is 0. + bool Empty() const { + for (BitContainer b : bits_) { + if (b != 0) { + return false; + } + } + return true; + } + + // Print a report on the densicy of the bit vector, number of 1 bits, number + // of bytes, and average bytes for 1 bit, to |out|. + void ReportDensity(std::ostream& out); + + friend std::ostream& operator<<(std::ostream&, const BitVector&); + + // Performs a bitwise-or operation on |this| and |that|, storing the result in + // |this|. Return true if |this| changed. + bool Or(const BitVector& that); + + private: + std::vector bits_; +}; + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_BIT_VECTOR_H_ diff --git a/third_party/spirv-tools/source/util/bitutils.h b/third_party/spirv-tools/source/util/bitutils.h new file mode 100644 index 0000000..9ced2f9 --- /dev/null +++ b/third_party/spirv-tools/source/util/bitutils.h @@ -0,0 +1,187 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_BITUTILS_H_ +#define SOURCE_UTIL_BITUTILS_H_ + +#include +#include +#include +#include + +namespace spvtools { +namespace utils { + +// Performs a bitwise copy of source to the destination type Dest. +template +Dest BitwiseCast(Src source) { + Dest dest; + static_assert(sizeof(source) == sizeof(dest), + "BitwiseCast: Source and destination must have the same size"); + std::memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// Calculates the bit width of the integer type |T|. +template +struct IntegerBitWidth { + static_assert(std::is_integral::value, "Integer type required"); + static const size_t kBitsPerByte = 8; + static const size_t get = sizeof(T) * kBitsPerByte; +}; + +// SetBits returns an integer of type with bits set +// for position through , counting from the least +// significant bit. In particular when Num == 0, no positions are set to 1. +// A static assert will be triggered if First + Num > sizeof(T) * 8, that is, +// a bit that will not fit in the underlying type is set. +template +struct SetBits { + static_assert(First < IntegerBitWidth::get, + "Tried to set a bit that is shifted too far."); + const static T get = (T(1) << First) | SetBits::get; +}; + +template +struct SetBits { + const static T get = T(0); +}; + +// This is all compile-time so we can put our tests right here. +static_assert(IntegerBitWidth::get == 32, "IntegerBitWidth mismatch"); +static_assert(IntegerBitWidth::get == 32, "IntegerBitWidth mismatch"); +static_assert(IntegerBitWidth::get == 64, "IntegerBitWidth mismatch"); +static_assert(IntegerBitWidth::get == 8, "IntegerBitWidth mismatch"); + +static_assert(SetBits::get == uint32_t(0x00000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000001), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x80000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x00000006), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xc0000000), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0x7FFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFFFFFF), + "SetBits failed"); +static_assert(SetBits::get == uint32_t(0xFFFF0000), + "SetBits failed"); + +static_assert(SetBits::get == uint64_t(0x0000000000000001LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x8000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0xc000000000000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x0000000080000000LL), + "SetBits failed"); +static_assert(SetBits::get == uint64_t(0x00000000FFFF0000LL), + "SetBits failed"); + +// Returns number of '1' bits in a word. +template +size_t CountSetBits(T word) { + static_assert(std::is_integral::value, + "CountSetBits requires integer type"); + size_t count = 0; + while (word) { + word &= word - 1; + ++count; + } + return count; +} + +// Checks if the bit at the |position| is set to '1'. +// Bits zero-indexed starting at the least significant bit. +// |position| must be within the bit width of |T|. +template +bool IsBitAtPositionSet(T word, size_t position) { + static_assert(std::is_integral::value, "Integer type required"); + static_assert(std::is_unsigned::value, "Unsigned type required"); + assert(position < IntegerBitWidth::get && + "position must be less than the bit width"); + return word & T(T(1) << position); +} + +// Returns a value obtained by setting a range of adjacent bits of |word| to +// |value|. Affected bits are within the range: +// [first_position, first_position + num_bits_to_mutate), +// assuming zero-based indexing starting at the least +// significant bit. Bits to mutate must be within the bit width of |T|. +template +T MutateBits(T word, size_t first_position, size_t num_bits_to_mutate, + bool value) { + static_assert(std::is_integral::value, "Integer type required"); + static_assert(std::is_unsigned::value, "Unsigned type required"); + static const size_t word_bit_width = IntegerBitWidth::get; + assert(first_position < word_bit_width && + "Mutated bits must be within bit width"); + assert(first_position + num_bits_to_mutate <= word_bit_width && + "Mutated bits must be within bit width"); + if (num_bits_to_mutate == 0) { + return word; + } + + const T all_ones = ~T(0); + const size_t num_unaffected_low_bits = first_position; + const T unaffected_low_mask = + T(T(all_ones >> num_unaffected_low_bits) << num_unaffected_low_bits); + + const size_t num_unaffected_high_bits = + word_bit_width - (first_position + num_bits_to_mutate); + const T unaffected_high_mask = + T(T(all_ones << num_unaffected_high_bits) >> num_unaffected_high_bits); + + const T mutation_mask = unaffected_low_mask & unaffected_high_mask; + if (value) { + return word | mutation_mask; + } + return word & T(~mutation_mask); +} + +// Returns a value obtained by setting the |num_bits_to_set| highest bits to +// '1'. |num_bits_to_set| must be not be greater than the bit width of |T|. +template +T SetHighBits(T word, size_t num_bits_to_set) { + if (num_bits_to_set == 0) { + return word; + } + const size_t word_bit_width = IntegerBitWidth::get; + assert(num_bits_to_set <= word_bit_width && + "Can't set more bits than bit width"); + return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set, + true); +} + +// Returns a value obtained by setting the |num_bits_to_set| highest bits to +// '0'. |num_bits_to_set| must be not be greater than the bit width of |T|. +template +T ClearHighBits(T word, size_t num_bits_to_set) { + if (num_bits_to_set == 0) { + return word; + } + const size_t word_bit_width = IntegerBitWidth::get; + assert(num_bits_to_set <= word_bit_width && + "Can't clear more bits than bit width"); + return MutateBits(word, word_bit_width - num_bits_to_set, num_bits_to_set, + false); +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_BITUTILS_H_ diff --git a/third_party/spirv-tools/source/util/hex_float.h b/third_party/spirv-tools/source/util/hex_float.h new file mode 100644 index 0000000..cfc40fa --- /dev/null +++ b/third_party/spirv-tools/source/util/hex_float.h @@ -0,0 +1,1150 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_HEX_FLOAT_H_ +#define SOURCE_UTIL_HEX_FLOAT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/util/bitutils.h" + +#ifndef __GNUC__ +#define GCC_VERSION 0 +#else +#define GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +namespace spvtools { +namespace utils { + +class Float16 { + public: + Float16(uint16_t v) : val(v) {} + Float16() = default; + static bool isNan(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) != 0); + } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(const Float16& val) { + return ((val.val & 0x7C00) == 0x7C00) && ((val.val & 0x3FF) == 0); + } + Float16(const Float16& other) { val = other.val; } + uint16_t get_value() const { return val; } + + // Returns the maximum normal value. + static Float16 max() { return Float16(0x7bff); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16(0xfbff); } + + private: + uint16_t val; +}; + +// To specialize this type, you must override uint_type to define +// an unsigned integer that can fit your floating point type. +// You must also add a isNan function that returns true if +// a value is Nan. +template +struct FloatProxyTraits { + using uint_type = void; +}; + +template <> +struct FloatProxyTraits { + using uint_type = uint32_t; + static bool isNan(float f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(float f) { return std::isinf(f); } + // Returns the maximum normal value. + static float max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static float lowest() { return std::numeric_limits::lowest(); } + // Returns the value as the native floating point format. + static float getAsFloat(const uint_type& t) { return BitwiseCast(t); } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const float& t) { + return BitwiseCast(t); + } + // Returns the bitwidth. + static uint32_t width() { return 32u; } +}; + +template <> +struct FloatProxyTraits { + using uint_type = uint64_t; + static bool isNan(double f) { return std::isnan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(double f) { return std::isinf(f); } + // Returns the maximum normal value. + static double max() { return std::numeric_limits::max(); } + // Returns the lowest normal value. + static double lowest() { return std::numeric_limits::lowest(); } + // Returns the value as the native floating point format. + static double getAsFloat(const uint_type& t) { + return BitwiseCast(t); + } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const double& t) { + return BitwiseCast(t); + } + // Returns the bitwidth. + static uint32_t width() { return 64u; } +}; + +template <> +struct FloatProxyTraits { + using uint_type = uint16_t; + static bool isNan(Float16 f) { return Float16::isNan(f); } + // Returns true if the given value is any kind of infinity. + static bool isInfinity(Float16 f) { return Float16::isInfinity(f); } + // Returns the maximum normal value. + static Float16 max() { return Float16::max(); } + // Returns the lowest normal value. + static Float16 lowest() { return Float16::lowest(); } + // Returns the value as the native floating point format. + static Float16 getAsFloat(const uint_type& t) { return Float16(t); } + // Returns the bits from the given floating pointer number. + static uint_type getBitsFromFloat(const Float16& t) { return t.get_value(); } + // Returns the bitwidth. + static uint32_t width() { return 16u; } +}; + +// Since copying a floating point number (especially if it is NaN) +// does not guarantee that bits are preserved, this class lets us +// store the type and use it as a float when necessary. +template +class FloatProxy { + public: + using uint_type = typename FloatProxyTraits::uint_type; + + // Since this is to act similar to the normal floats, + // do not initialize the data by default. + FloatProxy() = default; + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(T val) { data_ = FloatProxyTraits::getBitsFromFloat(val); } + + // Intentionally non-explicit. This is a proxy type so + // implicit conversions allow us to use it more transparently. + FloatProxy(uint_type val) { data_ = val; } + + // This is helpful to have and is guaranteed not to stomp bits. + FloatProxy operator-() const { + return static_cast(data_ ^ + (uint_type(0x1) << (sizeof(T) * 8 - 1))); + } + + // Returns the data as a floating point value. + T getAsFloat() const { return FloatProxyTraits::getAsFloat(data_); } + + // Returns the raw data. + uint_type data() const { return data_; } + + // Returns a vector of words suitable for use in an Operand. + std::vector GetWords() const { + std::vector words; + if (FloatProxyTraits::width() == 64) { + FloatProxyTraits::uint_type d = data(); + words.push_back(static_cast(d)); + words.push_back(static_cast(d >> 32)); + } else { + words.push_back(static_cast(data())); + } + return words; + } + + // Returns true if the value represents any type of NaN. + bool isNan() { return FloatProxyTraits::isNan(getAsFloat()); } + // Returns true if the value represents any type of infinity. + bool isInfinity() { return FloatProxyTraits::isInfinity(getAsFloat()); } + + // Returns the maximum normal value. + static FloatProxy max() { + return FloatProxy(FloatProxyTraits::max()); + } + // Returns the lowest normal value. + static FloatProxy lowest() { + return FloatProxy(FloatProxyTraits::lowest()); + } + + private: + uint_type data_; +}; + +template +bool operator==(const FloatProxy& first, const FloatProxy& second) { + return first.data() == second.data(); +} + +// Reads a FloatProxy value as a normal float from a stream. +template +std::istream& operator>>(std::istream& is, FloatProxy& value) { + T float_val; + is >> float_val; + value = FloatProxy(float_val); + return is; +} + +// This is an example traits. It is not meant to be used in practice, but will +// be the default for any non-specialized type. +template +struct HexFloatTraits { + // Integer type that can store this hex-float. + using uint_type = void; + // Signed integer type that can store this hex-float. + using int_type = void; + // The numerical type that this HexFloat represents. + using underlying_type = void; + // The type needed to construct the underlying type. + using native_type = void; + // The number of bits that are actually relevant in the uint_type. + // This allows us to deal with, for example, 24-bit values in a 32-bit + // integer. + static const uint32_t num_used_bits = 0; + // Number of bits that represent the exponent. + static const uint32_t num_exponent_bits = 0; + // Number of bits that represent the fractional part. + static const uint32_t num_fraction_bits = 0; + // The bias of the exponent. (How much we need to subtract from the stored + // value to get the correct value.) + static const uint32_t exponent_bias = 0; +}; + +// Traits for IEEE float. +// 1 sign bit, 8 exponent bits, 23 fractional bits. +template <> +struct HexFloatTraits> { + using uint_type = uint32_t; + using int_type = int32_t; + using underlying_type = FloatProxy; + using native_type = float; + static const uint_type num_used_bits = 32; + static const uint_type num_exponent_bits = 8; + static const uint_type num_fraction_bits = 23; + static const uint_type exponent_bias = 127; +}; + +// Traits for IEEE double. +// 1 sign bit, 11 exponent bits, 52 fractional bits. +template <> +struct HexFloatTraits> { + using uint_type = uint64_t; + using int_type = int64_t; + using underlying_type = FloatProxy; + using native_type = double; + static const uint_type num_used_bits = 64; + static const uint_type num_exponent_bits = 11; + static const uint_type num_fraction_bits = 52; + static const uint_type exponent_bias = 1023; +}; + +// Traits for IEEE half. +// 1 sign bit, 5 exponent bits, 10 fractional bits. +template <> +struct HexFloatTraits> { + using uint_type = uint16_t; + using int_type = int16_t; + using underlying_type = uint16_t; + using native_type = uint16_t; + static const uint_type num_used_bits = 16; + static const uint_type num_exponent_bits = 5; + static const uint_type num_fraction_bits = 10; + static const uint_type exponent_bias = 15; +}; + +enum class round_direction { + kToZero, + kToNearestEven, + kToPositiveInfinity, + kToNegativeInfinity, + max = kToNegativeInfinity +}; + +// Template class that houses a floating pointer number. +// It exposes a number of constants based on the provided traits to +// assist in interpreting the bits of the value. +template > +class HexFloat { + public: + using uint_type = typename Traits::uint_type; + using int_type = typename Traits::int_type; + using underlying_type = typename Traits::underlying_type; + using native_type = typename Traits::native_type; + + explicit HexFloat(T f) : value_(f) {} + + T value() const { return value_; } + void set_value(T f) { value_ = f; } + + // These are all written like this because it is convenient to have + // compile-time constants for all of these values. + + // Pass-through values to save typing. + static const uint32_t num_used_bits = Traits::num_used_bits; + static const uint32_t exponent_bias = Traits::exponent_bias; + static const uint32_t num_exponent_bits = Traits::num_exponent_bits; + static const uint32_t num_fraction_bits = Traits::num_fraction_bits; + + // Number of bits to shift left to set the highest relevant bit. + static const uint32_t top_bit_left_shift = num_used_bits - 1; + // How many nibbles (hex characters) the fractional part takes up. + static const uint32_t fraction_nibbles = (num_fraction_bits + 3) / 4; + // If the fractional part does not fit evenly into a hex character (4-bits) + // then we have to left-shift to get rid of leading 0s. This is the amount + // we have to shift (might be 0). + static const uint32_t num_overflow_bits = + fraction_nibbles * 4 - num_fraction_bits; + + // The representation of the fraction, not the actual bits. This + // includes the leading bit that is usually implicit. + static const uint_type fraction_represent_mask = + SetBits::get; + + // The topmost bit in the nibble-aligned fraction. + static const uint_type fraction_top_bit = + uint_type(1) << (num_fraction_bits + num_overflow_bits - 1); + + // The least significant bit in the exponent, which is also the bit + // immediately to the left of the significand. + static const uint_type first_exponent_bit = uint_type(1) + << (num_fraction_bits); + + // The mask for the encoded fraction. It does not include the + // implicit bit. + static const uint_type fraction_encode_mask = + SetBits::get; + + // The bit that is used as a sign. + static const uint_type sign_mask = uint_type(1) << top_bit_left_shift; + + // The bits that represent the exponent. + static const uint_type exponent_mask = + SetBits::get; + + // How far left the exponent is shifted. + static const uint32_t exponent_left_shift = num_fraction_bits; + + // How far from the right edge the fraction is shifted. + static const uint32_t fraction_right_shift = + static_cast(sizeof(uint_type) * 8) - num_fraction_bits; + + // The maximum representable unbiased exponent. + static const int_type max_exponent = + (exponent_mask >> num_fraction_bits) - exponent_bias; + // The minimum representable exponent for normalized numbers. + static const int_type min_exponent = -static_cast(exponent_bias); + + // Returns the bits associated with the value. + uint_type getBits() const { return value_.data(); } + + // Returns the bits associated with the value, without the leading sign bit. + uint_type getUnsignedBits() const { + return static_cast(value_.data() & ~sign_mask); + } + + // Returns the bits associated with the exponent, shifted to start at the + // lsb of the type. + const uint_type getExponentBits() const { + return static_cast((getBits() & exponent_mask) >> + num_fraction_bits); + } + + // Returns the exponent in unbiased form. This is the exponent in the + // human-friendly form. + const int_type getUnbiasedExponent() const { + return static_cast(getExponentBits() - exponent_bias); + } + + // Returns just the significand bits from the value. + const uint_type getSignificandBits() const { + return getBits() & fraction_encode_mask; + } + + // If the number was normalized, returns the unbiased exponent. + // If the number was denormal, normalize the exponent first. + const int_type getUnbiasedNormalizedExponent() const { + if ((getBits() & ~sign_mask) == 0) { // special case if everything is 0 + return 0; + } + int_type exp = getUnbiasedExponent(); + if (exp == min_exponent) { // We are in denorm land. + uint_type significand_bits = getSignificandBits(); + while ((significand_bits & (first_exponent_bit >> 1)) == 0) { + significand_bits = static_cast(significand_bits << 1); + exp = static_cast(exp - 1); + } + significand_bits &= fraction_encode_mask; + } + return exp; + } + + // Returns the signficand after it has been normalized. + const uint_type getNormalizedSignificand() const { + int_type unbiased_exponent = getUnbiasedNormalizedExponent(); + uint_type significand = getSignificandBits(); + for (int_type i = unbiased_exponent; i <= min_exponent; ++i) { + significand = static_cast(significand << 1); + } + significand &= fraction_encode_mask; + return significand; + } + + // Returns true if this number represents a negative value. + bool isNegative() const { return (getBits() & sign_mask) != 0; } + + // Sets this HexFloat from the individual components. + // Note this assumes EVERY significand is normalized, and has an implicit + // leading one. This means that the only way that this method will set 0, + // is if you set a number so denormalized that it underflows. + // Do not use this method with raw bits extracted from a subnormal number, + // since subnormals do not have an implicit leading 1 in the significand. + // The significand is also expected to be in the + // lowest-most num_fraction_bits of the uint_type. + // The exponent is expected to be unbiased, meaning an exponent of + // 0 actually means 0. + // If underflow_round_up is set, then on underflow, if a number is non-0 + // and would underflow, we round up to the smallest denorm. + void setFromSignUnbiasedExponentAndNormalizedSignificand( + bool negative, int_type exponent, uint_type significand, + bool round_denorm_up) { + bool significand_is_zero = significand == 0; + + if (exponent <= min_exponent) { + // If this was denormalized, then we have to shift the bit on, meaning + // the significand is not zero. + significand_is_zero = false; + significand |= first_exponent_bit; + significand = static_cast(significand >> 1); + } + + while (exponent < min_exponent) { + significand = static_cast(significand >> 1); + ++exponent; + } + + if (exponent == min_exponent) { + if (significand == 0 && !significand_is_zero && round_denorm_up) { + significand = static_cast(0x1); + } + } + + uint_type new_value = 0; + if (negative) { + new_value = static_cast(new_value | sign_mask); + } + exponent = static_cast(exponent + exponent_bias); + assert(exponent >= 0); + + // put it all together + exponent = static_cast((exponent << exponent_left_shift) & + exponent_mask); + significand = static_cast(significand & fraction_encode_mask); + new_value = static_cast(new_value | (exponent | significand)); + value_ = T(new_value); + } + + // Increments the significand of this number by the given amount. + // If this would spill the significand into the implicit bit, + // carry is set to true and the significand is shifted to fit into + // the correct location, otherwise carry is set to false. + // All significands and to_increment are assumed to be within the bounds + // for a valid significand. + static uint_type incrementSignificand(uint_type significand, + uint_type to_increment, bool* carry) { + significand = static_cast(significand + to_increment); + *carry = false; + if (significand & first_exponent_bit) { + *carry = true; + // The implicit 1-bit will have carried, so we should zero-out the + // top bit and shift back. + significand = static_cast(significand & ~first_exponent_bit); + significand = static_cast(significand >> 1); + } + return significand; + } + +#if GCC_VERSION == 40801 + // These exist because MSVC throws warnings on negative right-shifts + // even if they are not going to be executed. Eg: + // constant_number < 0? 0: constant_number + // These convert the negative left-shifts into right shifts. + template + struct negatable_left_shift { + static uint_type val(uint_type val) { + if (N > 0) { + return static_cast(val << N); + } else { + return static_cast(val >> N); + } + } + }; + + template + struct negatable_right_shift { + static uint_type val(uint_type val) { + if (N > 0) { + return static_cast(val >> N); + } else { + return static_cast(val << N); + } + } + }; + +#else + // These exist because MSVC throws warnings on negative right-shifts + // even if they are not going to be executed. Eg: + // constant_number < 0? 0: constant_number + // These convert the negative left-shifts into right shifts. + template + struct negatable_left_shift { + static uint_type val(uint_type val) { + return static_cast(val >> -N); + } + }; + + template + struct negatable_left_shift= 0>::type> { + static uint_type val(uint_type val) { + return static_cast(val << N); + } + }; + + template + struct negatable_right_shift { + static uint_type val(uint_type val) { + return static_cast(val << -N); + } + }; + + template + struct negatable_right_shift= 0>::type> { + static uint_type val(uint_type val) { + return static_cast(val >> N); + } + }; +#endif + + // Returns the significand, rounded to fit in a significand in + // other_T. This is shifted so that the most significant + // bit of the rounded number lines up with the most significant bit + // of the returned significand. + template + typename other_T::uint_type getRoundedNormalizedSignificand( + round_direction dir, bool* carry_bit) { + using other_uint_type = typename other_T::uint_type; + static const int_type num_throwaway_bits = + static_cast(num_fraction_bits) - + static_cast(other_T::num_fraction_bits); + + static const uint_type last_significant_bit = + (num_throwaway_bits < 0) + ? 0 + : negatable_left_shift::val(1u); + static const uint_type first_rounded_bit = + (num_throwaway_bits < 1) + ? 0 + : negatable_left_shift::val(1u); + + static const uint_type throwaway_mask_bits = + num_throwaway_bits > 0 ? num_throwaway_bits : 0; + static const uint_type throwaway_mask = + SetBits::get; + + *carry_bit = false; + other_uint_type out_val = 0; + uint_type significand = getNormalizedSignificand(); + // If we are up-casting, then we just have to shift to the right location. + if (num_throwaway_bits <= 0) { + out_val = static_cast(significand); + uint_type shift_amount = static_cast(-num_throwaway_bits); + out_val = static_cast(out_val << shift_amount); + return out_val; + } + + // If every non-representable bit is 0, then we don't have any casting to + // do. + if ((significand & throwaway_mask) == 0) { + return static_cast( + negatable_right_shift::val(significand)); + } + + bool round_away_from_zero = false; + // We actually have to narrow the significand here, so we have to follow the + // rounding rules. + switch (dir) { + case round_direction::kToZero: + break; + case round_direction::kToPositiveInfinity: + round_away_from_zero = !isNegative(); + break; + case round_direction::kToNegativeInfinity: + round_away_from_zero = isNegative(); + break; + case round_direction::kToNearestEven: + // Have to round down, round bit is 0 + if ((first_rounded_bit & significand) == 0) { + break; + } + if (((significand & throwaway_mask) & ~first_rounded_bit) != 0) { + // If any subsequent bit of the rounded portion is non-0 then we round + // up. + round_away_from_zero = true; + break; + } + // We are exactly half-way between 2 numbers, pick even. + if ((significand & last_significant_bit) != 0) { + // 1 for our last bit, round up. + round_away_from_zero = true; + break; + } + break; + } + + if (round_away_from_zero) { + return static_cast( + negatable_right_shift::val(incrementSignificand( + significand, last_significant_bit, carry_bit))); + } else { + return static_cast( + negatable_right_shift::val(significand)); + } + } + + // Casts this value to another HexFloat. If the cast is widening, + // then round_dir is ignored. If the cast is narrowing, then + // the result is rounded in the direction specified. + // This number will retain Nan and Inf values. + // It will also saturate to Inf if the number overflows, and + // underflow to (0 or min depending on rounding) if the number underflows. + template + void castTo(other_T& other, round_direction round_dir) { + other = other_T(static_cast(0)); + bool negate = isNegative(); + if (getUnsignedBits() == 0) { + if (negate) { + other.set_value(-other.value()); + } + return; + } + uint_type significand = getSignificandBits(); + bool carried = false; + typename other_T::uint_type rounded_significand = + getRoundedNormalizedSignificand(round_dir, &carried); + + int_type exponent = getUnbiasedExponent(); + if (exponent == min_exponent) { + // If we are denormal, normalize the exponent, so that we can encode + // easily. + exponent = static_cast(exponent + 1); + for (uint_type check_bit = first_exponent_bit >> 1; check_bit != 0; + check_bit = static_cast(check_bit >> 1)) { + exponent = static_cast(exponent - 1); + if (check_bit & significand) break; + } + } + + bool is_nan = + (getBits() & exponent_mask) == exponent_mask && significand != 0; + bool is_inf = + !is_nan && + ((exponent + carried) > static_cast(other_T::exponent_bias) || + (significand == 0 && (getBits() & exponent_mask) == exponent_mask)); + + // If we are Nan or Inf we should pass that through. + if (is_inf) { + other.set_value(typename other_T::underlying_type( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask))); + return; + } + if (is_nan) { + typename other_T::uint_type shifted_significand; + shifted_significand = static_cast( + negatable_left_shift< + static_cast(other_T::num_fraction_bits) - + static_cast(num_fraction_bits)>::val(significand)); + + // We are some sort of Nan. We try to keep the bit-pattern of the Nan + // as close as possible. If we had to shift off bits so we are 0, then we + // just set the last bit. + other.set_value(typename other_T::underlying_type( + static_cast( + (negate ? other_T::sign_mask : 0) | other_T::exponent_mask | + (shifted_significand == 0 ? 0x1 : shifted_significand)))); + return; + } + + bool round_underflow_up = + isNegative() ? round_dir == round_direction::kToNegativeInfinity + : round_dir == round_direction::kToPositiveInfinity; + using other_int_type = typename other_T::int_type; + // setFromSignUnbiasedExponentAndNormalizedSignificand will + // zero out any underflowing value (but retain the sign). + other.setFromSignUnbiasedExponentAndNormalizedSignificand( + negate, static_cast(exponent), rounded_significand, + round_underflow_up); + return; + } + + private: + T value_; + + static_assert(num_used_bits == + Traits::num_exponent_bits + Traits::num_fraction_bits + 1, + "The number of bits do not fit"); + static_assert(sizeof(T) == sizeof(uint_type), "The type sizes do not match"); +}; + +// Returns 4 bits represented by the hex character. +inline uint8_t get_nibble_from_character(int character) { + const char* dec = "0123456789"; + const char* lower = "abcdef"; + const char* upper = "ABCDEF"; + const char* p = nullptr; + if ((p = strchr(dec, character))) { + return static_cast(p - dec); + } else if ((p = strchr(lower, character))) { + return static_cast(p - lower + 0xa); + } else if ((p = strchr(upper, character))) { + return static_cast(p - upper + 0xa); + } + + assert(false && "This was called with a non-hex character"); + return 0; +} + +// Outputs the given HexFloat to the stream. +template +std::ostream& operator<<(std::ostream& os, const HexFloat& value) { + using HF = HexFloat; + using uint_type = typename HF::uint_type; + using int_type = typename HF::int_type; + + static_assert(HF::num_used_bits != 0, + "num_used_bits must be non-zero for a valid float"); + static_assert(HF::num_exponent_bits != 0, + "num_exponent_bits must be non-zero for a valid float"); + static_assert(HF::num_fraction_bits != 0, + "num_fractin_bits must be non-zero for a valid float"); + + const uint_type bits = value.value().data(); + const char* const sign = (bits & HF::sign_mask) ? "-" : ""; + const uint_type exponent = static_cast( + (bits & HF::exponent_mask) >> HF::num_fraction_bits); + + uint_type fraction = static_cast((bits & HF::fraction_encode_mask) + << HF::num_overflow_bits); + + const bool is_zero = exponent == 0 && fraction == 0; + const bool is_denorm = exponent == 0 && !is_zero; + + // exponent contains the biased exponent we have to convert it back into + // the normal range. + int_type int_exponent = static_cast(exponent - HF::exponent_bias); + // If the number is all zeros, then we actually have to NOT shift the + // exponent. + int_exponent = is_zero ? 0 : int_exponent; + + // If we are denorm, then start shifting, and decreasing the exponent until + // our leading bit is 1. + + if (is_denorm) { + while ((fraction & HF::fraction_top_bit) == 0) { + fraction = static_cast(fraction << 1); + int_exponent = static_cast(int_exponent - 1); + } + // Since this is denormalized, we have to consume the leading 1 since it + // will end up being implicit. + fraction = static_cast(fraction << 1); // eat the leading 1 + fraction &= HF::fraction_represent_mask; + } + + uint_type fraction_nibbles = HF::fraction_nibbles; + // We do not have to display any trailing 0s, since this represents the + // fractional part. + while (fraction_nibbles > 0 && (fraction & 0xF) == 0) { + // Shift off any trailing values; + fraction = static_cast(fraction >> 4); + --fraction_nibbles; + } + + const auto saved_flags = os.flags(); + const auto saved_fill = os.fill(); + + os << sign << "0x" << (is_zero ? '0' : '1'); + if (fraction_nibbles) { + // Make sure to keep the leading 0s in place, since this is the fractional + // part. + os << "." << std::setw(static_cast(fraction_nibbles)) + << std::setfill('0') << std::hex << fraction; + } + os << "p" << std::dec << (int_exponent >= 0 ? "+" : "") << int_exponent; + + os.flags(saved_flags); + os.fill(saved_fill); + + return os; +} + +// Returns true if negate_value is true and the next character on the +// input stream is a plus or minus sign. In that case we also set the fail bit +// on the stream and set the value to the zero value for its type. +template +inline bool RejectParseDueToLeadingSign(std::istream& is, bool negate_value, + HexFloat& value) { + if (negate_value) { + auto next_char = is.peek(); + if (next_char == '-' || next_char == '+') { + // Fail the parse. Emulate standard behaviour by setting the value to + // the zero value, and set the fail bit on the stream. + value = HexFloat(typename HexFloat::uint_type{0}); + is.setstate(std::ios_base::failbit); + return true; + } + } + return false; +} + +// Parses a floating point number from the given stream and stores it into the +// value parameter. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template +inline std::istream& ParseNormalFloat(std::istream& is, bool negate_value, + HexFloat& value) { + if (RejectParseDueToLeadingSign(is, negate_value, value)) { + return is; + } + T val; + is >> val; + if (negate_value) { + val = -val; + } + value.set_value(val); + // In the failure case, map -0.0 to 0.0. + if (is.fail() && value.getUnsignedBits() == 0u) { + value = HexFloat(typename HexFloat::uint_type{0}); + } + if (val.isInfinity()) { + // Fail the parse. Emulate standard behaviour by setting the value to + // the closest normal value, and set the fail bit on the stream. + value.set_value((value.isNegative() | negate_value) ? T::lowest() + : T::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Specialization of ParseNormalFloat for FloatProxy values. +// This will parse the float as it were a 32-bit floating point number, +// and then round it down to fit into a Float16 value. +// The number is rounded towards zero. +// If negate_value is true then the number may not have a leading minus or +// plus, and if it successfully parses, then the number is negated before +// being stored into the value parameter. +// If the value cannot be correctly parsed or overflows the target floating +// point type, then set the fail bit on the stream. +// TODO(dneto): Promise C++11 standard behavior in how the value is set in +// the error case, but only after all target platforms implement it correctly. +// In particular, the Microsoft C++ runtime appears to be out of spec. +template <> +inline std::istream& +ParseNormalFloat, HexFloatTraits>>( + std::istream& is, bool negate_value, + HexFloat, HexFloatTraits>>& value) { + // First parse as a 32-bit float. + HexFloat> float_val(0.0f); + ParseNormalFloat(is, negate_value, float_val); + + // Then convert to 16-bit float, saturating at infinities, and + // rounding toward zero. + float_val.castTo(value, round_direction::kToZero); + + // Overflow on 16-bit behaves the same as for 32- and 64-bit: set the + // fail bit and set the lowest or highest value. + if (Float16::isInfinity(value.value().getAsFloat())) { + value.set_value(value.isNegative() ? Float16::lowest() : Float16::max()); + is.setstate(std::ios_base::failbit); + } + return is; +} + +// Reads a HexFloat from the given stream. +// If the float is not encoded as a hex-float then it will be parsed +// as a regular float. +// This may fail if your stream does not support at least one unget. +// Nan values can be encoded with "0x1.p+exponent_bias". +// This would normally overflow a float and round to +// infinity but this special pattern is the exact representation for a NaN, +// and therefore is actually encoded as the correct NaN. To encode inf, +// either 0x0p+exponent_bias can be specified or any exponent greater than +// exponent_bias. +// Examples using IEEE 32-bit float encoding. +// 0x1.0p+128 (+inf) +// -0x1.0p-128 (-inf) +// +// 0x1.1p+128 (+Nan) +// -0x1.1p+128 (-Nan) +// +// 0x1p+129 (+inf) +// -0x1p+129 (-inf) +template +std::istream& operator>>(std::istream& is, HexFloat& value) { + using HF = HexFloat; + using uint_type = typename HF::uint_type; + using int_type = typename HF::int_type; + + value.set_value(static_cast(0.f)); + + if (is.flags() & std::ios::skipws) { + // If the user wants to skip whitespace , then we should obey that. + while (std::isspace(is.peek())) { + is.get(); + } + } + + auto next_char = is.peek(); + bool negate_value = false; + + if (next_char != '-' && next_char != '0') { + return ParseNormalFloat(is, negate_value, value); + } + + if (next_char == '-') { + negate_value = true; + is.get(); + next_char = is.peek(); + } + + if (next_char == '0') { + is.get(); // We may have to unget this. + auto maybe_hex_start = is.peek(); + if (maybe_hex_start != 'x' && maybe_hex_start != 'X') { + is.unget(); + return ParseNormalFloat(is, negate_value, value); + } else { + is.get(); // Throw away the 'x'; + } + } else { + return ParseNormalFloat(is, negate_value, value); + } + + // This "looks" like a hex-float so treat it as one. + bool seen_p = false; + bool seen_dot = false; + uint_type fraction_index = 0; + + uint_type fraction = 0; + int_type exponent = HF::exponent_bias; + + // Strip off leading zeros so we don't have to special-case them later. + while ((next_char = is.peek()) == '0') { + is.get(); + } + + bool is_denorm = + true; // Assume denorm "representation" until we hear otherwise. + // NB: This does not mean the value is actually denorm, + // it just means that it was written 0. + bool bits_written = false; // Stays false until we write a bit. + while (!seen_p && !seen_dot) { + // Handle characters that are left of the fractional part. + if (next_char == '.') { + seen_dot = true; + } else if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + // We know this is not denormalized since we have stripped all leading + // zeroes and we are not a ".". + is_denorm = false; + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x1 : 0x0; + if (bits_written) { + // If we are here the bits represented belong in the fractional + // part of the float, and we have to adjust the exponent accordingly. + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + exponent = static_cast(exponent + 1); + } + bits_written |= write_bit != 0; + } + } else { + // We have not found our exponent yet, so we have to fail. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + bits_written = false; + while (seen_dot && !seen_p) { + // Handle only fractional parts now. + if (next_char == 'p') { + seen_p = true; + } else if (::isxdigit(next_char)) { + int number = get_nibble_from_character(next_char); + for (int i = 0; i < 4; ++i, number <<= 1) { + uint_type write_bit = (number & 0x8) ? 0x01 : 0x00; + bits_written |= write_bit != 0; + if (is_denorm && !bits_written) { + // Handle modifying the exponent here this way we can handle + // an arbitrary number of hex values without overflowing our + // integer. + exponent = static_cast(exponent - 1); + } else { + fraction = static_cast( + fraction | + static_cast( + write_bit << (HF::top_bit_left_shift - fraction_index++))); + } + } + } else { + // We still have not found our 'p' exponent yet, so this is not a valid + // hex-float. + is.setstate(std::ios::failbit); + return is; + } + is.get(); + next_char = is.peek(); + } + + bool seen_sign = false; + int8_t exponent_sign = 1; + int_type written_exponent = 0; + while (true) { + if ((next_char == '-' || next_char == '+')) { + if (seen_sign) { + is.setstate(std::ios::failbit); + return is; + } + seen_sign = true; + exponent_sign = (next_char == '-') ? -1 : 1; + } else if (::isdigit(next_char)) { + // Hex-floats express their exponent as decimal. + written_exponent = static_cast(written_exponent * 10); + written_exponent = + static_cast(written_exponent + (next_char - '0')); + } else { + break; + } + is.get(); + next_char = is.peek(); + } + + written_exponent = static_cast(written_exponent * exponent_sign); + exponent = static_cast(exponent + written_exponent); + + bool is_zero = is_denorm && (fraction == 0); + if (is_denorm && !is_zero) { + fraction = static_cast(fraction << 1); + exponent = static_cast(exponent - 1); + } else if (is_zero) { + exponent = 0; + } + + if (exponent <= 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + fraction |= static_cast(1) << HF::top_bit_left_shift; + } + + fraction = (fraction >> HF::fraction_right_shift) & HF::fraction_encode_mask; + + const int_type max_exponent = + SetBits::get; + + // Handle actual denorm numbers + while (exponent < 0 && !is_zero) { + fraction = static_cast(fraction >> 1); + exponent = static_cast(exponent + 1); + + fraction &= HF::fraction_encode_mask; + if (fraction == 0) { + // We have underflowed our fraction. We should clamp to zero. + is_zero = true; + exponent = 0; + } + } + + // We have overflowed so we should be inf/-inf. + if (exponent > max_exponent) { + exponent = max_exponent; + fraction = 0; + } + + uint_type output_bits = static_cast( + static_cast(negate_value ? 1 : 0) << HF::top_bit_left_shift); + output_bits |= fraction; + + uint_type shifted_exponent = static_cast( + static_cast(exponent << HF::exponent_left_shift) & + HF::exponent_mask); + output_bits |= shifted_exponent; + + T output_float(output_bits); + value.set_value(output_float); + + return is; +} + +// Writes a FloatProxy value to a stream. +// Zero and normal numbers are printed in the usual notation, but with +// enough digits to fully reproduce the value. Other values (subnormal, +// NaN, and infinity) are printed as a hex float. +template +std::ostream& operator<<(std::ostream& os, const FloatProxy& value) { + auto float_val = value.getAsFloat(); + switch (std::fpclassify(float_val)) { + case FP_ZERO: + case FP_NORMAL: { + auto saved_precision = os.precision(); + os.precision(std::numeric_limits::max_digits10); + os << float_val; + os.precision(saved_precision); + } break; + default: + os << HexFloat>(value); + break; + } + return os; +} + +template <> +inline std::ostream& operator<<(std::ostream& os, + const FloatProxy& value) { + os << HexFloat>(value); + return os; +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_HEX_FLOAT_H_ diff --git a/third_party/spirv-tools/source/util/ilist.h b/third_party/spirv-tools/source/util/ilist.h new file mode 100644 index 0000000..9837b09 --- /dev/null +++ b/third_party/spirv-tools/source/util/ilist.h @@ -0,0 +1,365 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_ILIST_H_ +#define SOURCE_UTIL_ILIST_H_ + +#include +#include +#include +#include + +#include "source/util/ilist_node.h" + +namespace spvtools { +namespace utils { + +// An IntrusiveList is a generic implementation of a doubly-linked list. The +// intended convention for using this container is: +// +// class Node : public IntrusiveNodeBase { +// // Note that "Node", the class being defined is the template. +// // Must have a default constructor accessible to List. +// // Add whatever data is needed in the node +// }; +// +// using List = IntrusiveList; +// +// You can also inherit from IntrusiveList instead of a typedef if you want to +// add more functionality. +// +// The condition on the template for IntrusiveNodeBase is there to add some type +// checking to the container. The compiler will still allow inserting elements +// of type IntrusiveNodeBase, but that would be an error. This assumption +// allows NextNode and PreviousNode to return pointers to Node, and casting will +// not be required by the user. + +template +class IntrusiveList { + public: + static_assert( + std::is_base_of, NodeType>::value, + "The type from the node must be derived from IntrusiveNodeBase, with " + "itself in the template."); + + // Creates an empty list. + inline IntrusiveList(); + + // Moves the contents of the given list to the list being constructed. + IntrusiveList(IntrusiveList&&); + + // Destorys the list. Note that the elements of the list will not be deleted, + // but they will be removed from the list. + virtual ~IntrusiveList(); + + // Moves all of the elements in the list on the RHS to the list on the LHS. + IntrusiveList& operator=(IntrusiveList&&); + + // Basetype for iterators so an IntrusiveList can be traversed like STL + // containers. + template + class iterator_template { + public: + iterator_template(const iterator_template& i) : node_(i.node_) {} + + iterator_template& operator++() { + node_ = node_->next_node_; + return *this; + } + + iterator_template& operator--() { + node_ = node_->previous_node_; + return *this; + } + + iterator_template& operator=(const iterator_template& i) { + node_ = i.node_; + return *this; + } + + T& operator*() const { return *node_; } + T* operator->() const { return node_; } + + friend inline bool operator==(const iterator_template& lhs, + const iterator_template& rhs) { + return lhs.node_ == rhs.node_; + } + friend inline bool operator!=(const iterator_template& lhs, + const iterator_template& rhs) { + return !(lhs == rhs); + } + + // Moves the nodes in |list| to the list that |this| points to. The + // positions of the nodes will be immediately before the element pointed to + // by the iterator. The return value will be an iterator pointing to the + // first of the newly inserted elements. + iterator_template MoveBefore(IntrusiveList* list) { + if (list->empty()) return *this; + + NodeType* first_node = list->sentinel_.next_node_; + NodeType* last_node = list->sentinel_.previous_node_; + + this->node_->previous_node_->next_node_ = first_node; + first_node->previous_node_ = this->node_->previous_node_; + + last_node->next_node_ = this->node_; + this->node_->previous_node_ = last_node; + + list->sentinel_.next_node_ = &list->sentinel_; + list->sentinel_.previous_node_ = &list->sentinel_; + + return iterator(first_node); + } + + // Define standard iterator types needs so this class can be + // used with . + using iterator_category = std::bidirectional_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = T; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using size_type = size_t; + + protected: + iterator_template() = delete; + inline iterator_template(T* node) { node_ = node; } + T* node_; + + friend IntrusiveList; + }; + + using iterator = iterator_template; + using const_iterator = iterator_template; + + // Various types of iterators for the start (begin) and one past the end (end) + // of the list. + // + // Decrementing |end()| iterator will give and iterator pointing to the last + // element in the list, if one exists. + // + // Incrementing |end()| iterator will give |begin()|. + // + // Decrementing |begin()| will give |end()|. + // + // TODO: Not marking these functions as noexcept because Visual Studio 2013 + // does not support it. When we no longer care about that compiler, we should + // mark these as noexcept. + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + // Appends |node| to the end of the list. If |node| is already in a list, it + // will be removed from that list first. + void push_back(NodeType* node); + + // Returns true if the list is empty. + bool empty() const; + + // Makes the current list empty. + inline void clear(); + + // Returns references to the first or last element in the list. It is an + // error to call these functions on an empty list. + NodeType& front(); + NodeType& back(); + const NodeType& front() const; + const NodeType& back() const; + + // Transfers [|first|, |last|) from |other| into the list at |where|. + // + // If |other| is |this|, no change is made. + void Splice(iterator where, IntrusiveList* other, iterator first, + iterator last); + + protected: + // Doing a deep copy of the list does not make sense if the list does not own + // the data. It is not clear who will own the newly created data. Making + // copies illegal for that reason. + IntrusiveList(const IntrusiveList&) = delete; + IntrusiveList& operator=(const IntrusiveList&) = delete; + + // This function will assert if it finds the list containing |node| is not in + // a valid state. + static void Check(NodeType* node); + + // A special node used to represent both the start and end of the list, + // without being part of the list. + NodeType sentinel_; +}; + +// Implementation of IntrusiveList + +template +inline IntrusiveList::IntrusiveList() : sentinel_() { + sentinel_.next_node_ = &sentinel_; + sentinel_.previous_node_ = &sentinel_; + sentinel_.is_sentinel_ = true; +} + +template +IntrusiveList::IntrusiveList(IntrusiveList&& list) : sentinel_() { + sentinel_.next_node_ = &sentinel_; + sentinel_.previous_node_ = &sentinel_; + sentinel_.is_sentinel_ = true; + list.sentinel_.ReplaceWith(&sentinel_); +} + +template +IntrusiveList::~IntrusiveList() { + clear(); +} + +template +IntrusiveList& IntrusiveList::operator=( + IntrusiveList&& list) { + list.sentinel_.ReplaceWith(&sentinel_); + return *this; +} + +template +inline typename IntrusiveList::iterator +IntrusiveList::begin() { + return iterator(sentinel_.next_node_); +} + +template +inline typename IntrusiveList::iterator +IntrusiveList::end() { + return iterator(&sentinel_); +} + +template +inline typename IntrusiveList::const_iterator +IntrusiveList::begin() const { + return const_iterator(sentinel_.next_node_); +} + +template +inline typename IntrusiveList::const_iterator +IntrusiveList::end() const { + return const_iterator(&sentinel_); +} + +template +inline typename IntrusiveList::const_iterator +IntrusiveList::cbegin() const { + return const_iterator(sentinel_.next_node_); +} + +template +inline typename IntrusiveList::const_iterator +IntrusiveList::cend() const { + return const_iterator(&sentinel_); +} + +template +void IntrusiveList::push_back(NodeType* node) { + node->InsertBefore(&sentinel_); +} + +template +bool IntrusiveList::empty() const { + return sentinel_.NextNode() == nullptr; +} + +template +void IntrusiveList::clear() { + while (!empty()) { + front().RemoveFromList(); + } +} + +template +NodeType& IntrusiveList::front() { + NodeType* node = sentinel_.NextNode(); + assert(node != nullptr && "Can't get the front of an empty list."); + return *node; +} + +template +NodeType& IntrusiveList::back() { + NodeType* node = sentinel_.PreviousNode(); + assert(node != nullptr && "Can't get the back of an empty list."); + return *node; +} + +template +const NodeType& IntrusiveList::front() const { + NodeType* node = sentinel_.NextNode(); + assert(node != nullptr && "Can't get the front of an empty list."); + return *node; +} + +template +const NodeType& IntrusiveList::back() const { + NodeType* node = sentinel_.PreviousNode(); + assert(node != nullptr && "Can't get the back of an empty list."); + return *node; +} + +template +void IntrusiveList::Splice(iterator where, + IntrusiveList* other, + iterator first, iterator last) { + if (first == last) return; + if (other == this) return; + + NodeType* first_prev = first.node_->previous_node_; + NodeType* where_next = where.node_->next_node_; + + // Attach first. + where.node_->next_node_ = first.node_; + first.node_->previous_node_ = where.node_; + + // Attach last. + where_next->previous_node_ = last.node_->previous_node_; + last.node_->previous_node_->next_node_ = where_next; + + // Fixup other. + first_prev->next_node_ = last.node_; + last.node_->previous_node_ = first_prev; +} + +template +void IntrusiveList::Check(NodeType* start) { + int sentinel_count = 0; + NodeType* p = start; + do { + assert(p != nullptr); + assert(p->next_node_->previous_node_ == p); + assert(p->previous_node_->next_node_ == p); + if (p->is_sentinel_) sentinel_count++; + p = p->next_node_; + } while (p != start); + assert(sentinel_count == 1 && "List should have exactly 1 sentinel node."); + + p = start; + do { + assert(p != nullptr); + assert(p->previous_node_->next_node_ == p); + assert(p->next_node_->previous_node_ == p); + if (p->is_sentinel_) sentinel_count++; + p = p->previous_node_; + } while (p != start); +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_ILIST_H_ diff --git a/third_party/spirv-tools/source/util/ilist_node.h b/third_party/spirv-tools/source/util/ilist_node.h new file mode 100644 index 0000000..0579534 --- /dev/null +++ b/third_party/spirv-tools/source/util/ilist_node.h @@ -0,0 +1,265 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_ILIST_NODE_H_ +#define SOURCE_UTIL_ILIST_NODE_H_ + +#include + +namespace spvtools { +namespace utils { + +template +class IntrusiveList; + +// IntrusiveNodeBase is the base class for nodes in an IntrusiveList. +// See the comments in ilist.h on how to use the class. + +template +class IntrusiveNodeBase { + public: + // Creates a new node that is not in a list. + inline IntrusiveNodeBase(); + inline IntrusiveNodeBase(const IntrusiveNodeBase&); + inline IntrusiveNodeBase& operator=(const IntrusiveNodeBase&); + inline IntrusiveNodeBase(IntrusiveNodeBase&& that); + + // Destroys a node. It is an error to destroy a node that is part of a + // list, unless it is a sentinel. + virtual ~IntrusiveNodeBase(); + + IntrusiveNodeBase& operator=(IntrusiveNodeBase&& that); + + // Returns true if |this| is in a list. + inline bool IsInAList() const; + + // Returns the node that comes after the given node in the list, if one + // exists. If the given node is not in a list or is at the end of the list, + // the return value is nullptr. + inline NodeType* NextNode() const; + + // Returns the node that comes before the given node in the list, if one + // exists. If the given node is not in a list or is at the start of the + // list, the return value is nullptr. + inline NodeType* PreviousNode() const; + + // Inserts the given node immediately before |pos| in the list. + // If the given node is already in a list, it will first be removed + // from that list. + // + // It is assumed that the given node is of type NodeType. It is an error if + // |pos| is not already in a list. + inline void InsertBefore(NodeType* pos); + + // Inserts the given node immediately after |pos| in the list. + // If the given node is already in a list, it will first be removed + // from that list. + // + // It is assumed that the given node is of type NodeType. It is an error if + // |pos| is not already in a list, or if |pos| is equal to |this|. + inline void InsertAfter(NodeType* pos); + + // Removes the given node from the list. It is assumed that the node is + // in a list. Note that this does not free any storage related to the node, + // it becomes the caller's responsibility to free the storage. + inline void RemoveFromList(); + + protected: + // Replaces |this| with |target|. |this| is a sentinel if and only if + // |target| is also a sentinel. + // + // If neither node is a sentinel, |target| takes + // the place of |this|. It is assumed that |target| is not in a list. + // + // If both are sentinels, then it will cause all of the + // nodes in the list containing |this| to be moved to the list containing + // |target|. In this case, it is assumed that |target| is an empty list. + // + // No storage will be deleted. + void ReplaceWith(NodeType* target); + + // Returns true if |this| is the sentinel node of an empty list. + bool IsEmptyList(); + + // The pointers to the next and previous nodes in the list. + // If the current node is not part of a list, then |next_node_| and + // |previous_node_| are equal to |nullptr|. + NodeType* next_node_; + NodeType* previous_node_; + + // Only true for the sentinel node stored in the list itself. + bool is_sentinel_; + + friend IntrusiveList; +}; + +// Implementation of IntrusiveNodeBase + +template +inline IntrusiveNodeBase::IntrusiveNodeBase() + : next_node_(nullptr), previous_node_(nullptr), is_sentinel_(false) {} + +template +inline IntrusiveNodeBase::IntrusiveNodeBase( + const IntrusiveNodeBase&) { + next_node_ = nullptr; + previous_node_ = nullptr; + is_sentinel_ = false; +} + +template +inline IntrusiveNodeBase& IntrusiveNodeBase::operator=( + const IntrusiveNodeBase&) { + assert(!is_sentinel_); + if (IsInAList()) { + RemoveFromList(); + } + return *this; +} + +template +inline IntrusiveNodeBase::IntrusiveNodeBase(IntrusiveNodeBase&& that) + : next_node_(nullptr), + previous_node_(nullptr), + is_sentinel_(that.is_sentinel_) { + if (is_sentinel_) { + next_node_ = this; + previous_node_ = this; + } + that.ReplaceWith(this); +} + +template +IntrusiveNodeBase::~IntrusiveNodeBase() { + assert(is_sentinel_ || !IsInAList()); +} + +template +IntrusiveNodeBase& IntrusiveNodeBase::operator=( + IntrusiveNodeBase&& that) { + that.ReplaceWith(this); + return *this; +} + +template +inline bool IntrusiveNodeBase::IsInAList() const { + return next_node_ != nullptr; +} + +template +inline NodeType* IntrusiveNodeBase::NextNode() const { + if (!next_node_->is_sentinel_) return next_node_; + return nullptr; +} + +template +inline NodeType* IntrusiveNodeBase::PreviousNode() const { + if (!previous_node_->is_sentinel_) return previous_node_; + return nullptr; +} + +template +inline void IntrusiveNodeBase::InsertBefore(NodeType* pos) { + assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); + assert(pos->IsInAList() && "Pos should already be in a list."); + if (this->IsInAList()) this->RemoveFromList(); + + this->next_node_ = pos; + this->previous_node_ = pos->previous_node_; + pos->previous_node_ = static_cast(this); + this->previous_node_->next_node_ = static_cast(this); +} + +template +inline void IntrusiveNodeBase::InsertAfter(NodeType* pos) { + assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); + assert(pos->IsInAList() && "Pos should already be in a list."); + assert(this != pos && "Can't insert a node after itself."); + + if (this->IsInAList()) { + this->RemoveFromList(); + } + + this->previous_node_ = pos; + this->next_node_ = pos->next_node_; + pos->next_node_ = static_cast(this); + this->next_node_->previous_node_ = static_cast(this); +} + +template +inline void IntrusiveNodeBase::RemoveFromList() { + assert(!this->is_sentinel_ && "Sentinel nodes cannot be moved around."); + assert(this->IsInAList() && + "Cannot remove a node from a list if it is not in a list."); + + this->next_node_->previous_node_ = this->previous_node_; + this->previous_node_->next_node_ = this->next_node_; + this->next_node_ = nullptr; + this->previous_node_ = nullptr; +} + +template +void IntrusiveNodeBase::ReplaceWith(NodeType* target) { + if (this->is_sentinel_) { + assert(target->IsEmptyList() && + "If target is not an empty list, the nodes in that list would not " + "be linked to a sentinel."); + } else { + assert(IsInAList() && "The node being replaced must be in a list."); + assert(!target->is_sentinel_ && + "Cannot turn a sentinel node into one that is not."); + } + + if (!this->IsEmptyList()) { + // Link target into the same position that |this| was in. + target->next_node_ = this->next_node_; + target->previous_node_ = this->previous_node_; + target->next_node_->previous_node_ = target; + target->previous_node_->next_node_ = target; + + // Reset |this| to itself default value. + if (!this->is_sentinel_) { + // Reset |this| so that it is not in a list. + this->next_node_ = nullptr; + this->previous_node_ = nullptr; + } else { + // Set |this| so that it is the head of an empty list. + // We cannot treat sentinel nodes like others because it is invalid for + // a sentinel node to not be in a list. + this->next_node_ = static_cast(this); + this->previous_node_ = static_cast(this); + } + } else { + // If |this| points to itself, it must be a sentinel node with an empty + // list. Reset |this| so that it is the head of an empty list. We want + // |target| to be the same. The asserts above guarantee that. + } +} + +template +bool IntrusiveNodeBase::IsEmptyList() { + if (next_node_ == this) { + assert(is_sentinel_ && + "None sentinel nodes should never point to themselves."); + assert(previous_node_ == this && + "Inconsistency with the previous and next nodes."); + return true; + } + return false; +} + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_ILIST_NODE_H_ diff --git a/third_party/spirv-tools/source/util/make_unique.h b/third_party/spirv-tools/source/util/make_unique.h new file mode 100644 index 0000000..ad7976c --- /dev/null +++ b/third_party/spirv-tools/source/util/make_unique.h @@ -0,0 +1,30 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_MAKE_UNIQUE_H_ +#define SOURCE_UTIL_MAKE_UNIQUE_H_ + +#include +#include + +namespace spvtools { + +template +std::unique_ptr MakeUnique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); +} + +} // namespace spvtools + +#endif // SOURCE_UTIL_MAKE_UNIQUE_H_ diff --git a/third_party/spirv-tools/source/util/parse_number.cpp b/third_party/spirv-tools/source/util/parse_number.cpp new file mode 100644 index 0000000..c3351c2 --- /dev/null +++ b/third_party/spirv-tools/source/util/parse_number.cpp @@ -0,0 +1,217 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/util/parse_number.h" + +#include +#include +#include +#include +#include +#include + +#include "source/util/hex_float.h" +#include "source/util/make_unique.h" + +namespace spvtools { +namespace utils { +namespace { + +// A helper class that temporarily stores error messages and dump the messages +// to a string which given as as pointer when it is destructed. If the given +// pointer is a nullptr, this class does not store error message. +class ErrorMsgStream { + public: + explicit ErrorMsgStream(std::string* error_msg_sink) + : error_msg_sink_(error_msg_sink) { + if (error_msg_sink_) stream_ = MakeUnique(); + } + ~ErrorMsgStream() { + if (error_msg_sink_ && stream_) *error_msg_sink_ = stream_->str(); + } + template + ErrorMsgStream& operator<<(T val) { + if (stream_) *stream_ << val; + return *this; + } + + private: + std::unique_ptr stream_; + // The destination string to which this class dump the error message when + // destructor is called. + std::string* error_msg_sink_; +}; +} // namespace + +EncodeNumberStatus ParseAndEncodeIntegerNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg) { + if (!text) { + ErrorMsgStream(error_msg) << "The given text is a nullptr"; + return EncodeNumberStatus::kInvalidText; + } + + if (!IsIntegral(type)) { + ErrorMsgStream(error_msg) << "The expected type is not a integer type"; + return EncodeNumberStatus::kInvalidUsage; + } + + const uint32_t bit_width = AssumedBitWidth(type); + + if (bit_width > 64) { + ErrorMsgStream(error_msg) + << "Unsupported " << bit_width << "-bit integer literals"; + return EncodeNumberStatus::kUnsupported; + } + + // Either we are expecting anything or integer. + bool is_negative = text[0] == '-'; + bool can_be_signed = IsSigned(type); + + if (is_negative && !can_be_signed) { + ErrorMsgStream(error_msg) + << "Cannot put a negative number in an unsigned literal"; + return EncodeNumberStatus::kInvalidUsage; + } + + const bool is_hex = text[0] == '0' && (text[1] == 'x' || text[1] == 'X'); + + uint64_t decoded_bits; + if (is_negative) { + int64_t decoded_signed = 0; + + if (!ParseNumber(text, &decoded_signed)) { + ErrorMsgStream(error_msg) << "Invalid signed integer literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + + if (!CheckRangeAndIfHexThenSignExtend(decoded_signed, type, is_hex, + &decoded_signed)) { + ErrorMsgStream(error_msg) + << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase + << decoded_signed << " does not fit in a " << std::dec << bit_width + << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer"; + return EncodeNumberStatus::kInvalidText; + } + decoded_bits = decoded_signed; + } else { + // There's no leading minus sign, so parse it as an unsigned integer. + if (!ParseNumber(text, &decoded_bits)) { + ErrorMsgStream(error_msg) << "Invalid unsigned integer literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + if (!CheckRangeAndIfHexThenSignExtend(decoded_bits, type, is_hex, + &decoded_bits)) { + ErrorMsgStream(error_msg) + << "Integer " << (is_hex ? std::hex : std::dec) << std::showbase + << decoded_bits << " does not fit in a " << std::dec << bit_width + << "-bit " << (IsSigned(type) ? "signed" : "unsigned") << " integer"; + return EncodeNumberStatus::kInvalidText; + } + } + if (bit_width > 32) { + uint32_t low = uint32_t(0x00000000ffffffff & decoded_bits); + uint32_t high = uint32_t((0xffffffff00000000 & decoded_bits) >> 32); + emit(low); + emit(high); + } else { + emit(uint32_t(decoded_bits)); + } + return EncodeNumberStatus::kSuccess; +} + +EncodeNumberStatus ParseAndEncodeFloatingPointNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg) { + if (!text) { + ErrorMsgStream(error_msg) << "The given text is a nullptr"; + return EncodeNumberStatus::kInvalidText; + } + + if (!IsFloating(type)) { + ErrorMsgStream(error_msg) << "The expected type is not a float type"; + return EncodeNumberStatus::kInvalidUsage; + } + + const auto bit_width = AssumedBitWidth(type); + switch (bit_width) { + case 16: { + HexFloat> hVal(0); + if (!ParseNumber(text, &hVal)) { + ErrorMsgStream(error_msg) << "Invalid 16-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + // getAsFloat will return the Float16 value, and get_value + // will return a uint16_t representing the bits of the float. + // The encoding is therefore correct from the perspective of the SPIR-V + // spec since the top 16 bits will be 0. + emit(static_cast(hVal.value().getAsFloat().get_value())); + return EncodeNumberStatus::kSuccess; + } break; + case 32: { + HexFloat> fVal(0.0f); + if (!ParseNumber(text, &fVal)) { + ErrorMsgStream(error_msg) << "Invalid 32-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + emit(BitwiseCast(fVal)); + return EncodeNumberStatus::kSuccess; + } break; + case 64: { + HexFloat> dVal(0.0); + if (!ParseNumber(text, &dVal)) { + ErrorMsgStream(error_msg) << "Invalid 64-bit float literal: " << text; + return EncodeNumberStatus::kInvalidText; + } + uint64_t decoded_val = BitwiseCast(dVal); + uint32_t low = uint32_t(0x00000000ffffffff & decoded_val); + uint32_t high = uint32_t((0xffffffff00000000 & decoded_val) >> 32); + emit(low); + emit(high); + return EncodeNumberStatus::kSuccess; + } break; + default: + break; + } + ErrorMsgStream(error_msg) + << "Unsupported " << bit_width << "-bit float literals"; + return EncodeNumberStatus::kUnsupported; +} + +EncodeNumberStatus ParseAndEncodeNumber(const char* text, + const NumberType& type, + std::function emit, + std::string* error_msg) { + if (!text) { + ErrorMsgStream(error_msg) << "The given text is a nullptr"; + return EncodeNumberStatus::kInvalidText; + } + + if (IsUnknown(type)) { + ErrorMsgStream(error_msg) + << "The expected type is not a integer or float type"; + return EncodeNumberStatus::kInvalidUsage; + } + + // If we explicitly expect a floating-point number, we should handle that + // first. + if (IsFloating(type)) { + return ParseAndEncodeFloatingPointNumber(text, type, emit, error_msg); + } + + return ParseAndEncodeIntegerNumber(text, type, emit, error_msg); +} + +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/source/util/parse_number.h b/third_party/spirv-tools/source/util/parse_number.h new file mode 100644 index 0000000..729aac5 --- /dev/null +++ b/third_party/spirv-tools/source/util/parse_number.h @@ -0,0 +1,252 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_PARSE_NUMBER_H_ +#define SOURCE_UTIL_PARSE_NUMBER_H_ + +#include +#include +#include + +#include "source/util/hex_float.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace utils { + +// A struct to hold the expected type information for the number in text to be +// parsed. +struct NumberType { + uint32_t bitwidth; + // SPV_NUMBER_NONE means the type is unknown and is invalid to be used with + // ParseAndEncode{|Integer|Floating}Number(). + spv_number_kind_t kind; +}; + +// Returns true if the type is a scalar integer type. +inline bool IsIntegral(const NumberType& type) { + return type.kind == SPV_NUMBER_UNSIGNED_INT || + type.kind == SPV_NUMBER_SIGNED_INT; +} + +// Returns true if the type is a scalar floating point type. +inline bool IsFloating(const NumberType& type) { + return type.kind == SPV_NUMBER_FLOATING; +} + +// Returns true if the type is a signed value. +inline bool IsSigned(const NumberType& type) { + return type.kind == SPV_NUMBER_FLOATING || type.kind == SPV_NUMBER_SIGNED_INT; +} + +// Returns true if the type is unknown. +inline bool IsUnknown(const NumberType& type) { + return type.kind == SPV_NUMBER_NONE; +} + +// Returns the number of bits in the type. This is only valid for integer and +// floating types. +inline int AssumedBitWidth(const NumberType& type) { + switch (type.kind) { + case SPV_NUMBER_SIGNED_INT: + case SPV_NUMBER_UNSIGNED_INT: + case SPV_NUMBER_FLOATING: + return type.bitwidth; + default: + break; + } + // We don't care about this case. + return 0; +} + +// A templated class with a static member function Clamp, where Clamp sets a +// referenced value of type T to 0 if T is an unsigned integer type, and +// returns true if it modified the referenced value. +template +class ClampToZeroIfUnsignedType { + public: + // The default specialization does not clamp the value. + static bool Clamp(T*) { return false; } +}; + +// The specialization of ClampToZeroIfUnsignedType for unsigned integer types. +template +class ClampToZeroIfUnsignedType< + T, typename std::enable_if::value>::type> { + public: + static bool Clamp(T* value_pointer) { + if (*value_pointer) { + *value_pointer = 0; + return true; + } + return false; + } +}; + +// Returns true if the given value fits within the target scalar integral type. +// The target type may have an unusual bit width. If the value was originally +// specified as a hexadecimal number, then the overflow bits should be zero. +// If it was hex and the target type is signed, then return the sign-extended +// value through the updated_value_for_hex pointer argument. On failure, +// returns false. +template +bool CheckRangeAndIfHexThenSignExtend(T value, const NumberType& type, + bool is_hex, T* updated_value_for_hex) { + // The encoded result has three regions of bits that are of interest, from + // least to most significant: + // - magnitude bits, where the magnitude of the number would be stored if + // we were using a signed-magnitude representation. + // - an optional sign bit + // - overflow bits, up to bit 63 of a 64-bit number + // For example: + // Type Overflow Sign Magnitude + // --------------- -------- ---- --------- + // unsigned 8 bit 8-63 n/a 0-7 + // signed 8 bit 8-63 7 0-6 + // unsigned 16 bit 16-63 n/a 0-15 + // signed 16 bit 16-63 15 0-14 + + // We'll use masks to define the three regions. + // At first we'll assume the number is unsigned. + const uint32_t bit_width = AssumedBitWidth(type); + uint64_t magnitude_mask = + (bit_width == 64) ? -1 : ((uint64_t(1) << bit_width) - 1); + uint64_t sign_mask = 0; + uint64_t overflow_mask = ~magnitude_mask; + + if (value < 0 || IsSigned(type)) { + // Accommodate the sign bit. + magnitude_mask >>= 1; + sign_mask = magnitude_mask + 1; + } + + bool failed = false; + if (value < 0) { + // The top bits must all be 1 for a negative signed value. + failed = ((value & overflow_mask) != overflow_mask) || + ((value & sign_mask) != sign_mask); + } else { + if (is_hex) { + // Hex values are a bit special. They decode as unsigned values, but may + // represent a negative number. In this case, the overflow bits should + // be zero. + failed = (value & overflow_mask) != 0; + } else { + const uint64_t value_as_u64 = static_cast(value); + // Check overflow in the ordinary case. + failed = (value_as_u64 & magnitude_mask) != value_as_u64; + } + } + + if (failed) { + return false; + } + + // Sign extend hex the number. + if (is_hex && (value & sign_mask)) + *updated_value_for_hex = (value | overflow_mask); + + return true; +} + +// Parses a numeric value of a given type from the given text. The number +// should take up the entire string, and should be within bounds for the target +// type. On success, returns true and populates the object referenced by +// value_pointer. On failure, returns false. +template +bool ParseNumber(const char* text, T* value_pointer) { + // C++11 doesn't define std::istringstream(int8_t&), so calling this method + // with a single-byte type leads to implementation-defined behaviour. + // Similarly for uint8_t. + static_assert(sizeof(T) > 1, + "Single-byte types are not supported in this parse method"); + + if (!text) return false; + std::istringstream text_stream(text); + // Allow both decimal and hex input for integers. + // It also allows octal input, but we don't care about that case. + text_stream >> std::setbase(0); + text_stream >> *value_pointer; + + // We should have read something. + bool ok = (text[0] != 0) && !text_stream.bad(); + // It should have been all the text. + ok = ok && text_stream.eof(); + // It should have been in range. + ok = ok && !text_stream.fail(); + + // Work around a bug in the GNU C++11 library. It will happily parse + // "-1" for uint16_t as 65535. + if (ok && text[0] == '-') + ok = !ClampToZeroIfUnsignedType::Clamp(value_pointer); + + return ok; +} + +// Enum to indicate the parsing and encoding status. +enum class EncodeNumberStatus { + kSuccess = 0, + // Unsupported bit width etc. + kUnsupported, + // Expected type (NumberType) is not a scalar int or float, or putting a + // negative number in an unsigned literal. + kInvalidUsage, + // Number value does not fit the bit width of the expected type etc. + kInvalidText, +}; + +// Parses an integer value of a given |type| from the given |text| and encodes +// the number by the given |emit| function. On success, returns +// EncodeNumberStatus::kSuccess and the parsed number will be consumed by the +// given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Integers up to 64 bits are +// supported. +EncodeNumberStatus ParseAndEncodeIntegerNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg); + +// Parses a floating point value of a given |type| from the given |text| and +// encodes the number by the given |emit| funciton. On success, returns +// EncodeNumberStatus::kSuccess and the parsed number will be consumed by the +// given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Only 16, 32 and 64 bit +// floating point numbers are supported. +EncodeNumberStatus ParseAndEncodeFloatingPointNumber( + const char* text, const NumberType& type, + std::function emit, std::string* error_msg); + +// Parses an integer or floating point number of a given |type| from the given +// |text| and encodes the number by the given |emit| function. On success, +// returns EncodeNumberStatus::kSuccess and the parsed number will be consumed +// by the given |emit| function word by word (least significant word first). On +// failure, this function returns the error code of the encoding status and +// |emit| function will not be called. If the string pointer |error_msg| is not +// a nullptr, it will be overwritten with error messages in case of failure. In +// case of success, |error_msg| will not be touched. Integers up to 64 bits +// and 16/32/64 bit floating point values are supported. +EncodeNumberStatus ParseAndEncodeNumber(const char* text, + const NumberType& type, + std::function emit, + std::string* error_msg); + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_PARSE_NUMBER_H_ diff --git a/third_party/spirv-tools/source/util/small_vector.h b/third_party/spirv-tools/source/util/small_vector.h new file mode 100644 index 0000000..f2c1147 --- /dev/null +++ b/third_party/spirv-tools/source/util/small_vector.h @@ -0,0 +1,466 @@ +// Copyright (c) 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_SMALL_VECTOR_H_ +#define SOURCE_UTIL_SMALL_VECTOR_H_ + +#include +#include +#include +#include +#include + +#include "source/util/make_unique.h" + +namespace spvtools { +namespace utils { + +// The |SmallVector| class is intended to be a drop-in replacement for +// |std::vector|. The difference is in the implementation. A |SmallVector| is +// optimized for when the number of elements in the vector are small. Small is +// defined by the template parameter |small_size|. +// +// Note that |SmallVector| is not always faster than an |std::vector|, so you +// should experiment with different values for |small_size| and compare to +// using and |std::vector|. +// +// TODO: I have implemented the public member functions from |std::vector| that +// I needed. If others are needed they should be implemented. Do not implement +// public member functions that are not defined by std::vector. +template +class SmallVector { + public: + using iterator = T*; + using const_iterator = const T*; + + SmallVector() + : size_(0), + small_data_(reinterpret_cast(buffer)), + large_data_(nullptr) {} + + SmallVector(const SmallVector& that) : SmallVector() { *this = that; } + + SmallVector(SmallVector&& that) : SmallVector() { *this = std::move(that); } + + SmallVector(const std::vector& vec) : SmallVector() { + if (vec.size() > small_size) { + large_data_ = MakeUnique>(vec); + } else { + size_ = vec.size(); + for (uint32_t i = 0; i < size_; i++) { + new (small_data_ + i) T(vec[i]); + } + } + } + + SmallVector(std::vector&& vec) : SmallVector() { + if (vec.size() > small_size) { + large_data_ = MakeUnique>(std::move(vec)); + } else { + size_ = vec.size(); + for (uint32_t i = 0; i < size_; i++) { + new (small_data_ + i) T(std::move(vec[i])); + } + } + vec.clear(); + } + + SmallVector(std::initializer_list init_list) : SmallVector() { + if (init_list.size() < small_size) { + for (auto it = init_list.begin(); it != init_list.end(); ++it) { + new (small_data_ + (size_++)) T(std::move(*it)); + } + } else { + large_data_ = MakeUnique>(std::move(init_list)); + } + } + + SmallVector(size_t s, const T& v) : SmallVector() { resize(s, v); } + + virtual ~SmallVector() { + for (T* p = small_data_; p < small_data_ + size_; ++p) { + p->~T(); + } + } + + SmallVector& operator=(const SmallVector& that) { + assert(small_data_); + if (that.large_data_) { + if (large_data_) { + *large_data_ = *that.large_data_; + } else { + large_data_ = MakeUnique>(*that.large_data_); + } + } else { + large_data_.reset(nullptr); + size_t i = 0; + // Do a copy for any element in |this| that is already constructed. + for (; i < size_ && i < that.size_; ++i) { + small_data_[i] = that.small_data_[i]; + } + + if (i >= that.size_) { + // If the size of |this| becomes smaller after the assignment, then + // destroy any extra elements. + for (; i < size_; ++i) { + small_data_[i].~T(); + } + } else { + // If the size of |this| becomes larger after the assignement, copy + // construct the new elements that are needed. + for (; i < that.size_; ++i) { + new (small_data_ + i) T(that.small_data_[i]); + } + } + size_ = that.size_; + } + return *this; + } + + SmallVector& operator=(SmallVector&& that) { + if (that.large_data_) { + large_data_.reset(that.large_data_.release()); + } else { + large_data_.reset(nullptr); + size_t i = 0; + // Do a move for any element in |this| that is already constructed. + for (; i < size_ && i < that.size_; ++i) { + small_data_[i] = std::move(that.small_data_[i]); + } + + if (i >= that.size_) { + // If the size of |this| becomes smaller after the assignment, then + // destroy any extra elements. + for (; i < size_; ++i) { + small_data_[i].~T(); + } + } else { + // If the size of |this| becomes larger after the assignement, move + // construct the new elements that are needed. + for (; i < that.size_; ++i) { + new (small_data_ + i) T(std::move(that.small_data_[i])); + } + } + size_ = that.size_; + } + + // Reset |that| because all of the data has been moved to |this|. + that.DestructSmallData(); + return *this; + } + + template + friend bool operator==(const SmallVector& lhs, const OtherVector& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + auto rit = rhs.begin(); + for (auto lit = lhs.begin(); lit != lhs.end(); ++lit, ++rit) { + if (*lit != *rit) { + return false; + } + } + return true; + } + + friend bool operator==(const std::vector& lhs, const SmallVector& rhs) { + return rhs == lhs; + } + + friend bool operator!=(const SmallVector& lhs, const std::vector& rhs) { + return !(lhs == rhs); + } + + friend bool operator!=(const std::vector& lhs, const SmallVector& rhs) { + return rhs != lhs; + } + + T& operator[](size_t i) { + if (!large_data_) { + return small_data_[i]; + } else { + return (*large_data_)[i]; + } + } + + const T& operator[](size_t i) const { + if (!large_data_) { + return small_data_[i]; + } else { + return (*large_data_)[i]; + } + } + + size_t size() const { + if (!large_data_) { + return size_; + } else { + return large_data_->size(); + } + } + + iterator begin() { + if (large_data_) { + return large_data_->data(); + } else { + return small_data_; + } + } + + const_iterator begin() const { + if (large_data_) { + return large_data_->data(); + } else { + return small_data_; + } + } + + const_iterator cbegin() const { return begin(); } + + iterator end() { + if (large_data_) { + return large_data_->data() + large_data_->size(); + } else { + return small_data_ + size_; + } + } + + const_iterator end() const { + if (large_data_) { + return large_data_->data() + large_data_->size(); + } else { + return small_data_ + size_; + } + } + + const_iterator cend() const { return end(); } + + T* data() { return begin(); } + + const T* data() const { return cbegin(); } + + T& front() { return (*this)[0]; } + + const T& front() const { return (*this)[0]; } + + iterator erase(const_iterator pos) { return erase(pos, pos + 1); } + + iterator erase(const_iterator first, const_iterator last) { + if (large_data_) { + size_t start_index = first - large_data_->data(); + size_t end_index = last - large_data_->data(); + auto r = large_data_->erase(large_data_->begin() + start_index, + large_data_->begin() + end_index); + return large_data_->data() + (r - large_data_->begin()); + } + + // Since C++11, std::vector has |const_iterator| for the parameters, so I + // follow that. However, I need iterators to modify the current container, + // which is not const. This is why I cast away the const. + iterator f = const_cast(first); + iterator l = const_cast(last); + iterator e = end(); + + size_t num_of_del_elements = last - first; + iterator ret = f; + if (first == last) { + return ret; + } + + // Move |last| and any elements after it their earlier position. + while (l != e) { + *f = std::move(*l); + ++f; + ++l; + } + + // Destroy the elements that were supposed to be deleted. + while (f != l) { + f->~T(); + ++f; + } + + // Update the size. + size_ -= num_of_del_elements; + return ret; + } + + void push_back(const T& value) { + if (!large_data_ && size_ == small_size) { + MoveToLargeData(); + } + + if (large_data_) { + large_data_->push_back(value); + return; + } + + new (small_data_ + size_) T(value); + ++size_; + } + + void push_back(T&& value) { + if (!large_data_ && size_ == small_size) { + MoveToLargeData(); + } + + if (large_data_) { + large_data_->push_back(std::move(value)); + return; + } + + new (small_data_ + size_) T(std::move(value)); + ++size_; + } + + template + iterator insert(iterator pos, InputIt first, InputIt last) { + size_t element_idx = (pos - begin()); + size_t num_of_new_elements = std::distance(first, last); + size_t new_size = size_ + num_of_new_elements; + if (!large_data_ && new_size > small_size) { + MoveToLargeData(); + } + + if (large_data_) { + typename std::vector::iterator new_pos = + large_data_->begin() + element_idx; + large_data_->insert(new_pos, first, last); + return begin() + element_idx; + } + + // Move |pos| and all of the elements after it over |num_of_new_elements| + // places. We start at the end and work backwards, to make sure we do not + // overwrite data that we have not moved yet. + for (iterator i = begin() + new_size - 1, j = end() - 1; j >= pos; + --i, --j) { + if (i >= begin() + size_) { + new (i) T(std::move(*j)); + } else { + *i = std::move(*j); + } + } + + // Copy the new elements into position. + iterator p = pos; + for (; first != last; ++p, ++first) { + if (p >= small_data_ + size_) { + new (p) T(*first); + } else { + *p = *first; + } + } + + // Upate the size. + size_ += num_of_new_elements; + return pos; + } + + bool empty() const { + if (large_data_) { + return large_data_->empty(); + } + return size_ == 0; + } + + void clear() { + if (large_data_) { + large_data_->clear(); + } else { + DestructSmallData(); + } + } + + template + void emplace_back(Args&&... args) { + if (!large_data_ && size_ == small_size) { + MoveToLargeData(); + } + + if (large_data_) { + large_data_->emplace_back(std::forward(args)...); + } else { + new (small_data_ + size_) T(std::forward(args)...); + ++size_; + } + } + + void resize(size_t new_size, const T& v) { + if (!large_data_ && new_size > small_size) { + MoveToLargeData(); + } + + if (large_data_) { + large_data_->resize(new_size, v); + return; + } + + // If |new_size| < |size_|, then destroy the extra elements. + for (size_t i = new_size; i < size_; ++i) { + small_data_[i].~T(); + } + + // If |new_size| > |size_|, the copy construct the new elements. + for (size_t i = size_; i < new_size; ++i) { + new (small_data_ + i) T(v); + } + + // Update the size. + size_ = new_size; + } + + private: + // Moves all of the element from |small_data_| into a new std::vector that can + // be access through |large_data|. + void MoveToLargeData() { + assert(!large_data_); + large_data_ = MakeUnique>(); + for (size_t i = 0; i < size_; ++i) { + large_data_->emplace_back(std::move(small_data_[i])); + } + DestructSmallData(); + } + + // Destroys all of the elements in |small_data_| that have been constructed. + void DestructSmallData() { + for (size_t i = 0; i < size_; ++i) { + small_data_[i].~T(); + } + size_ = 0; + } + + // The number of elements in |small_data_| that have been constructed. + size_t size_; + + // The pointed used to access the array of elements when the number of + // elements is small. + T* small_data_; + + // The actual data used to store the array elements. It must never be used + // directly, but must only be accesed through |small_data_|. + typename std::aligned_storage::value>::type + buffer[small_size]; + + // A pointer to a vector that is used to store the elements of the vector when + // this size exceeds |small_size|. If |large_data_| is nullptr, then the data + // is stored in |small_data_|. Otherwise, the data is stored in + // |large_data_|. + std::unique_ptr> large_data_; +}; // namespace utils + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_SMALL_VECTOR_H_ diff --git a/third_party/spirv-tools/source/util/string_utils.cpp b/third_party/spirv-tools/source/util/string_utils.cpp new file mode 100644 index 0000000..b56c353 --- /dev/null +++ b/third_party/spirv-tools/source/util/string_utils.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/util/string_utils.h" + +namespace spvtools { +namespace utils { + +std::string CardinalToOrdinal(size_t cardinal) { + const size_t mod10 = cardinal % 10; + const size_t mod100 = cardinal % 100; + std::string suffix; + if (mod10 == 1 && mod100 != 11) + suffix = "st"; + else if (mod10 == 2 && mod100 != 12) + suffix = "nd"; + else if (mod10 == 3 && mod100 != 13) + suffix = "rd"; + else + suffix = "th"; + + return ToString(cardinal) + suffix; +} + +std::pair SplitFlagArgs(const std::string& flag) { + if (flag.size() < 2) return make_pair(flag, std::string()); + + // Detect the last dash before the pass name. Since we have to + // handle single dash options (-O and -Os), count up to two dashes. + size_t dash_ix = 0; + if (flag[0] == '-' && flag[1] == '-') + dash_ix = 2; + else if (flag[0] == '-') + dash_ix = 1; + + size_t ix = flag.find('='); + return (ix != std::string::npos) + ? make_pair(flag.substr(dash_ix, ix - 2), flag.substr(ix + 1)) + : make_pair(flag.substr(dash_ix), std::string()); +} + +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/source/util/string_utils.h b/third_party/spirv-tools/source/util/string_utils.h new file mode 100644 index 0000000..4282aa9 --- /dev/null +++ b/third_party/spirv-tools/source/util/string_utils.h @@ -0,0 +1,92 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_UTIL_STRING_UTILS_H_ +#define SOURCE_UTIL_STRING_UTILS_H_ + +#include +#include +#include +#include + +#include "source/util/string_utils.h" + +namespace spvtools { +namespace utils { + +// Converts arithmetic value |val| to its default string representation. +template +std::string ToString(T val) { + static_assert( + std::is_arithmetic::value, + "spvtools::utils::ToString is restricted to only arithmetic values"); + std::stringstream os; + os << val; + return os.str(); +} + +// Converts cardinal number to ordinal number string. +std::string CardinalToOrdinal(size_t cardinal); + +// Splits the string |flag|, of the form '--pass_name[=pass_args]' into two +// strings "pass_name" and "pass_args". If |flag| has no arguments, the second +// string will be empty. +std::pair SplitFlagArgs(const std::string& flag); + +// Encodes a string as a sequence of words, using the SPIR-V encoding. +inline std::vector MakeVector(std::string input) { + std::vector result; + uint32_t word = 0; + size_t num_bytes = input.size(); + // SPIR-V strings are null-terminated. The byte_index == num_bytes + // case is used to push the terminating null byte. + for (size_t byte_index = 0; byte_index <= num_bytes; byte_index++) { + const auto new_byte = + (byte_index < num_bytes ? uint8_t(input[byte_index]) : uint8_t(0)); + word |= (new_byte << (8 * (byte_index % sizeof(uint32_t)))); + if (3 == (byte_index % sizeof(uint32_t))) { + result.push_back(word); + word = 0; + } + } + // Emit a trailing partial word. + if ((num_bytes + 1) % sizeof(uint32_t)) { + result.push_back(word); + } + return result; +} + +// Decode a string from a sequence of words, using the SPIR-V encoding. +template +inline std::string MakeString(const VectorType& words) { + std::string result; + + for (uint32_t word : words) { + for (int byte_index = 0; byte_index < 4; byte_index++) { + uint32_t extracted_word = (word >> (8 * byte_index)) & 0xFF; + char c = static_cast(extracted_word); + if (c == 0) { + return result; + } + result += c; + } + } + assert(false && "Did not find terminating null for the string."); + return result; +} // namespace utils + +} // namespace utils +} // namespace spvtools + +#endif // SOURCE_UTIL_STRING_UTILS_H_ diff --git a/third_party/spirv-tools/source/util/timer.cpp b/third_party/spirv-tools/source/util/timer.cpp new file mode 100644 index 0000000..c8b8d5b --- /dev/null +++ b/third_party/spirv-tools/source/util/timer.cpp @@ -0,0 +1,102 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if defined(SPIRV_TIMER_ENABLED) + +#include "source/util/timer.h" + +#include +#include +#include +#include +#include + +namespace spvtools { +namespace utils { + +void PrintTimerDescription(std::ostream* out, bool measure_mem_usage) { + if (out) { + *out << std::setw(30) << "PASS name" << std::setw(12) << "CPU time" + << std::setw(12) << "WALL time" << std::setw(12) << "USR time" + << std::setw(12) << "SYS time"; + if (measure_mem_usage) { + *out << std::setw(12) << "RSS delta" << std::setw(16) << "PGFault delta"; + } + *out << std::endl; + } +} + +// Do not change the order of invoking system calls. We want to make CPU/Wall +// time correct as much as possible. Calling functions to get CPU/Wall time must +// closely surround the target code of measuring. +void Timer::Start() { + if (report_stream_) { + if (getrusage(RUSAGE_SELF, &usage_before_) == -1) + usage_status_ |= kGetrusageFailed; + if (clock_gettime(CLOCK_MONOTONIC, &wall_before_) == -1) + usage_status_ |= kClockGettimeWalltimeFailed; + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_before_) == -1) + usage_status_ |= kClockGettimeCPUtimeFailed; + } +} + +// The order of invoking system calls is important with the same reason as +// Timer::Start(). +void Timer::Stop() { + if (report_stream_ && usage_status_ == kSucceeded) { + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_after_) == -1) + usage_status_ |= kClockGettimeCPUtimeFailed; + if (clock_gettime(CLOCK_MONOTONIC, &wall_after_) == -1) + usage_status_ |= kClockGettimeWalltimeFailed; + if (getrusage(RUSAGE_SELF, &usage_after_) == -1) + usage_status_ = kGetrusageFailed; + } +} + +void Timer::Report(const char* tag) { + if (!report_stream_) return; + + report_stream_->precision(2); + *report_stream_ << std::fixed << std::setw(30) << tag; + + if (usage_status_ & kClockGettimeCPUtimeFailed) + *report_stream_ << std::setw(12) << "Failed"; + else + *report_stream_ << std::setw(12) << CPUTime(); + + if (usage_status_ & kClockGettimeWalltimeFailed) + *report_stream_ << std::setw(12) << "Failed"; + else + *report_stream_ << std::setw(12) << WallTime(); + + if (usage_status_ & kGetrusageFailed) { + *report_stream_ << std::setw(12) << "Failed" << std::setw(12) << "Failed"; + if (measure_mem_usage_) { + *report_stream_ << std::setw(12) << "Failed" << std::setw(12) << "Failed"; + } + } else { + *report_stream_ << std::setw(12) << UserTime() << std::setw(12) + << SystemTime(); + if (measure_mem_usage_) { + *report_stream_ << std::fixed << std::setw(12) << RSS() << std::setw(16) + << PageFault(); + } + } + *report_stream_ << std::endl; +} + +} // namespace utils +} // namespace spvtools + +#endif // defined(SPIRV_TIMER_ENABLED) diff --git a/third_party/spirv-tools/source/util/timer.h b/third_party/spirv-tools/source/util/timer.h new file mode 100644 index 0000000..fc4b747 --- /dev/null +++ b/third_party/spirv-tools/source/util/timer.h @@ -0,0 +1,392 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Contains utils for getting resource utilization + +#ifndef SOURCE_UTIL_TIMER_H_ +#define SOURCE_UTIL_TIMER_H_ + +#if defined(SPIRV_TIMER_ENABLED) + +#include +#include +#include + +// A macro to call spvtools::utils::PrintTimerDescription(std::ostream*, bool). +// The first argument must be given as std::ostream*. If it is NULL, the +// function does nothing. Otherwise, it prints resource types measured by Timer +// class. The second is optional and if it is true, the function also prints +// resource type fields related to memory. Otherwise, it does not print memory +// related fields. Its default is false. In usual, this must be placed before +// calling Timer::Report() to inform what those fields printed by +// Timer::Report() indicate (or spvtools::utils::PrintTimerDescription() must be +// used instead). +#define SPIRV_TIMER_DESCRIPTION(...) \ + spvtools::utils::PrintTimerDescription(__VA_ARGS__) + +// Creates an object of ScopedTimer to measure the resource utilization for the +// scope surrounding it as the following example: +// +// { // <-- beginning of this scope +// +// /* ... code out of interest ... */ +// +// SPIRV_TIMER_SCOPED(std::cout, tag); +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// } // <-- end of this scope. The destructor of ScopedTimer prints tag and +// the resource utilization to std::cout. +#define SPIRV_TIMER_SCOPED(...) \ + spvtools::utils::ScopedTimer timer##__LINE__( \ + __VA_ARGS__) + +namespace spvtools { +namespace utils { + +// Prints the description of resource types measured by Timer class. If |out| is +// NULL, it does nothing. Otherwise, it prints resource types. The second is +// optional and if it is true, the function also prints resource type fields +// related to memory. Its default is false. In usual, this must be placed before +// calling Timer::Report() to inform what those fields printed by +// Timer::Report() indicate. +void PrintTimerDescription(std::ostream*, bool = false); + +// Status of Timer. kGetrusageFailed means it failed in calling getrusage(). +// kClockGettimeWalltimeFailed means it failed in getting wall time when calling +// clock_gettime(). kClockGettimeCPUtimeFailed means it failed in getting CPU +// time when calling clock_gettime(). +enum UsageStatus { + kSucceeded = 0, + kGetrusageFailed = 1 << 0, + kClockGettimeWalltimeFailed = 1 << 1, + kClockGettimeCPUtimeFailed = 1 << 2, +}; + +// Timer measures the resource utilization for a range of code. The resource +// utilization consists of CPU time (i.e., process time), WALL time (elapsed +// time), USR time, SYS time, RSS delta, and the delta of the number of page +// faults. RSS delta and the delta of the number of page faults are measured +// only when |measure_mem_usage| given to the constructor is true. This class +// should be used as the following example: +// +// spvtools::utils::Timer timer(std::cout); +// timer.Start(); // <-- set |usage_before_|, |wall_before_|, +// and |cpu_before_| +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// timer.Stop(); // <-- set |cpu_after_|, |wall_after_|, and +// |usage_after_| +// timer.Report(tag); // <-- print tag and the resource utilization to +// std::cout. +class Timer { + public: + Timer(std::ostream* out, bool measure_mem_usage = false) + : report_stream_(out), + usage_status_(kSucceeded), + measure_mem_usage_(measure_mem_usage) {} + + // Sets |usage_before_|, |wall_before_|, and |cpu_before_| as results of + // getrusage(), clock_gettime() for the wall time, and clock_gettime() for the + // CPU time respectively. Note that this method erases all previous state of + // |usage_before_|, |wall_before_|, |cpu_before_|. + virtual void Start(); + + // Sets |cpu_after_|, |wall_after_|, and |usage_after_| as results of + // clock_gettime() for the wall time, and clock_gettime() for the CPU time, + // getrusage() respectively. Note that this method erases all previous state + // of |cpu_after_|, |wall_after_|, |usage_after_|. + virtual void Stop(); + + // If |report_stream_| is NULL, it does nothing. Otherwise, it prints the + // resource utilization (i.e., CPU/WALL/USR/SYS time, RSS delta) between the + // time of calling Timer::Start() and the time of calling Timer::Stop(). If we + // cannot get a resource usage because of failures, it prints "Failed" instead + // for the resource. + void Report(const char* tag); + + // Returns the measured CPU Time (i.e., process time) for a range of code + // execution. If kClockGettimeCPUtimeFailed is set by the failure of calling + // clock_gettime(), it returns -1. + virtual double CPUTime() { + if (usage_status_ & kClockGettimeCPUtimeFailed) return -1; + return TimeDifference(cpu_before_, cpu_after_); + } + + // Returns the measured Wall Time (i.e., elapsed time) for a range of code + // execution. If kClockGettimeWalltimeFailed is set by the failure of + // calling clock_gettime(), it returns -1. + virtual double WallTime() { + if (usage_status_ & kClockGettimeWalltimeFailed) return -1; + return TimeDifference(wall_before_, wall_after_); + } + + // Returns the measured USR Time for a range of code execution. If + // kGetrusageFailed is set because of the failure of calling getrusage(), it + // returns -1. + virtual double UserTime() { + if (usage_status_ & kGetrusageFailed) return -1; + return TimeDifference(usage_before_.ru_utime, usage_after_.ru_utime); + } + + // Returns the measured SYS Time for a range of code execution. If + // kGetrusageFailed is set because of the failure of calling getrusage(), it + // returns -1. + virtual double SystemTime() { + if (usage_status_ & kGetrusageFailed) return -1; + return TimeDifference(usage_before_.ru_stime, usage_after_.ru_stime); + } + + // Returns the measured RSS delta for a range of code execution. If + // kGetrusageFailed is set because of the failure of calling getrusage(), it + // returns -1. + virtual long RSS() const { + if (usage_status_ & kGetrusageFailed) return -1; + return usage_after_.ru_maxrss - usage_before_.ru_maxrss; + } + + // Returns the measured the delta of the number of page faults for a range of + // code execution. If kGetrusageFailed is set because of the failure of + // calling getrusage(), it returns -1. + virtual long PageFault() const { + if (usage_status_ & kGetrusageFailed) return -1; + return (usage_after_.ru_minflt - usage_before_.ru_minflt) + + (usage_after_.ru_majflt - usage_before_.ru_majflt); + } + + virtual ~Timer() {} + + private: + // Returns the time gap between |from| and |to| in seconds. + static double TimeDifference(const timeval& from, const timeval& to) { + assert((to.tv_sec > from.tv_sec) || + (to.tv_sec == from.tv_sec && to.tv_usec >= from.tv_usec)); + return static_cast(to.tv_sec - from.tv_sec) + + static_cast(to.tv_usec - from.tv_usec) * .000001; + } + + // Returns the time gap between |from| and |to| in seconds. + static double TimeDifference(const timespec& from, const timespec& to) { + assert((to.tv_sec > from.tv_sec) || + (to.tv_sec == from.tv_sec && to.tv_nsec >= from.tv_nsec)); + return static_cast(to.tv_sec - from.tv_sec) + + static_cast(to.tv_nsec - from.tv_nsec) * .000000001; + } + + // Output stream to print out the resource utilization. If it is NULL, + // Report() does nothing. + std::ostream* report_stream_; + + // Status to stop measurement if a system call returns an error. + unsigned usage_status_; + + // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when + // Timer::Start() is called. It is used as the base status of CPU time. + timespec cpu_before_; + + // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when + // Timer::Start() is called. It is used as the base status of WALL time. + timespec wall_before_; + + // Variable to save the result of getrusage() when Timer::Start() is called. + // It is used as the base status of USR time, SYS time, and RSS. + rusage usage_before_; + + // Variable to save the result of clock_gettime(CLOCK_PROCESS_CPUTIME_ID) when + // Timer::Stop() is called. It is used as the last status of CPU time. The + // resouce usage is measured by subtracting |cpu_before_| from it. + timespec cpu_after_; + + // Variable to save the result of clock_gettime(CLOCK_MONOTONIC) when + // Timer::Stop() is called. It is used as the last status of WALL time. The + // resouce usage is measured by subtracting |wall_before_| from it. + timespec wall_after_; + + // Variable to save the result of getrusage() when Timer::Stop() is called. It + // is used as the last status of USR time, SYS time, and RSS. Those resouce + // usages are measured by subtracting |usage_before_| from it. + rusage usage_after_; + + // If true, Timer reports the memory usage information too. Otherwise, Timer + // reports only USR time, WALL time, SYS time. + bool measure_mem_usage_; +}; + +// The purpose of ScopedTimer is to measure the resource utilization for a +// scope. Simply creating a local variable of ScopedTimer will call +// Timer::Start() and it calls Timer::Stop() and Timer::Report() at the end of +// the scope by its destructor. When we use this class, we must choose the +// proper Timer class (for class TimerType template) in advance. This class +// should be used as the following example: +// +// { // <-- beginning of this scope +// +// /* ... code out of interest ... */ +// +// spvtools::utils::ScopedTimer +// scopedtimer(std::cout, tag); +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// } // <-- end of this scope. The destructor of ScopedTimer prints tag and +// the resource utilization to std::cout. +// +// The template is used to choose a Timer class. Currently, +// only options for the Timer class are Timer and MockTimer in the unit test. +template +class ScopedTimer { + public: + ScopedTimer(std::ostream* out, const char* tag, + bool measure_mem_usage = false) + : timer(new TimerType(out, measure_mem_usage)), tag_(tag) { + timer->Start(); + } + + // At the end of the scope surrounding the instance of this class, this + // destructor saves the last status of resource usage and reports it. + virtual ~ScopedTimer() { + timer->Stop(); + timer->Report(tag_); + delete timer; + } + + private: + // Actual timer that measures the resource utilization. It must be an instance + // of Timer class if there is no special reason to use other class. + TimerType* timer; + + // A tag that will be printed in front of the trace reported by Timer class. + const char* tag_; +}; + +// CumulativeTimer is the same as Timer class, but it supports a cumulative +// measurement as the following example: +// +// CumulativeTimer *ctimer = new CumulativeTimer(std::cout); +// ctimer->Start(); +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// ctimer->Stop(); +// +// /* ... code out of interest ... */ +// +// ctimer->Start(); +// +// /* ... lines of code that we want to know its resource usage ... */ +// +// ctimer->Stop(); +// ctimer->Report(tag); +// delete ctimer; +// +class CumulativeTimer : public Timer { + public: + CumulativeTimer(std::ostream* out, bool measure_mem_usage = false) + : Timer(out, measure_mem_usage), + cpu_time_(0), + wall_time_(0), + usr_time_(0), + sys_time_(0), + rss_(0), + pgfaults_(0) {} + + // If we cannot get a resource usage because of failures, it sets -1 for the + // resource usage. + void Stop() override { + Timer::Stop(); + + if (cpu_time_ >= 0 && Timer::CPUTime() >= 0) + cpu_time_ += Timer::CPUTime(); + else + cpu_time_ = -1; + + if (wall_time_ >= 0 && Timer::WallTime() >= 0) + wall_time_ += Timer::WallTime(); + else + wall_time_ = -1; + + if (usr_time_ >= 0 && Timer::UserTime() >= 0) + usr_time_ += Timer::UserTime(); + else + usr_time_ = -1; + + if (sys_time_ >= 0 && Timer::SystemTime() >= 0) + sys_time_ += Timer::SystemTime(); + else + sys_time_ = -1; + + if (rss_ >= 0 && Timer::RSS() >= 0) + rss_ += Timer::RSS(); + else + rss_ = -1; + + if (pgfaults_ >= 0 && Timer::PageFault() >= 0) + pgfaults_ += Timer::PageFault(); + else + pgfaults_ = -1; + } + + // Returns the cumulative CPU Time (i.e., process time) for a range of code + // execution. + double CPUTime() override { return cpu_time_; } + + // Returns the cumulative Wall Time (i.e., elapsed time) for a range of code + // execution. + double WallTime() override { return wall_time_; } + + // Returns the cumulative USR Time for a range of code execution. + double UserTime() override { return usr_time_; } + + // Returns the cumulative SYS Time for a range of code execution. + double SystemTime() override { return sys_time_; } + + // Returns the cumulative RSS delta for a range of code execution. + long RSS() const override { return rss_; } + + // Returns the cumulative delta of number of page faults for a range of code + // execution. + long PageFault() const override { return pgfaults_; } + + private: + // Variable to save the cumulative CPU time (i.e., process time). + double cpu_time_; + + // Variable to save the cumulative wall time (i.e., elapsed time). + double wall_time_; + + // Variable to save the cumulative user time. + double usr_time_; + + // Variable to save the cumulative system time. + double sys_time_; + + // Variable to save the cumulative RSS delta. + long rss_; + + // Variable to save the cumulative delta of the number of page faults. + long pgfaults_; +}; + +} // namespace utils +} // namespace spvtools + +#else // defined(SPIRV_TIMER_ENABLED) + +#define SPIRV_TIMER_DESCRIPTION(...) +#define SPIRV_TIMER_SCOPED(...) + +#endif // defined(SPIRV_TIMER_ENABLED) + +#endif // SOURCE_UTIL_TIMER_H_ diff --git a/third_party/spirv-tools/source/val/basic_block.cpp b/third_party/spirv-tools/source/val/basic_block.cpp new file mode 100644 index 0000000..b2a8793 --- /dev/null +++ b/third_party/spirv-tools/source/val/basic_block.cpp @@ -0,0 +1,143 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/basic_block.h" + +#include +#include +#include + +namespace spvtools { +namespace val { + +BasicBlock::BasicBlock(uint32_t label_id) + : id_(label_id), + immediate_dominator_(nullptr), + immediate_post_dominator_(nullptr), + predecessors_(), + successors_(), + type_(0), + reachable_(false), + label_(nullptr), + terminator_(nullptr) {} + +void BasicBlock::SetImmediateDominator(BasicBlock* dom_block) { + immediate_dominator_ = dom_block; +} + +void BasicBlock::SetImmediatePostDominator(BasicBlock* pdom_block) { + immediate_post_dominator_ = pdom_block; +} + +const BasicBlock* BasicBlock::immediate_dominator() const { + return immediate_dominator_; +} + +const BasicBlock* BasicBlock::immediate_post_dominator() const { + return immediate_post_dominator_; +} + +BasicBlock* BasicBlock::immediate_dominator() { return immediate_dominator_; } +BasicBlock* BasicBlock::immediate_post_dominator() { + return immediate_post_dominator_; +} + +void BasicBlock::RegisterSuccessors( + const std::vector& next_blocks) { + for (auto& block : next_blocks) { + block->predecessors_.push_back(this); + successors_.push_back(block); + } +} + +bool BasicBlock::dominates(const BasicBlock& other) const { + return (this == &other) || + !(other.dom_end() == + std::find(other.dom_begin(), other.dom_end(), this)); +} + +bool BasicBlock::postdominates(const BasicBlock& other) const { + return (this == &other) || + !(other.pdom_end() == + std::find(other.pdom_begin(), other.pdom_end(), this)); +} + +BasicBlock::DominatorIterator::DominatorIterator() : current_(nullptr) {} + +BasicBlock::DominatorIterator::DominatorIterator( + const BasicBlock* block, + std::function dominator_func) + : current_(block), dom_func_(dominator_func) {} + +BasicBlock::DominatorIterator& BasicBlock::DominatorIterator::operator++() { + if (current_ == dom_func_(current_)) { + current_ = nullptr; + } else { + current_ = dom_func_(current_); + } + return *this; +} + +const BasicBlock::DominatorIterator BasicBlock::dom_begin() const { + return DominatorIterator( + this, [](const BasicBlock* b) { return b->immediate_dominator(); }); +} + +BasicBlock::DominatorIterator BasicBlock::dom_begin() { + return DominatorIterator( + this, [](const BasicBlock* b) { return b->immediate_dominator(); }); +} + +const BasicBlock::DominatorIterator BasicBlock::dom_end() const { + return DominatorIterator(); +} + +BasicBlock::DominatorIterator BasicBlock::dom_end() { + return DominatorIterator(); +} + +const BasicBlock::DominatorIterator BasicBlock::pdom_begin() const { + return DominatorIterator( + this, [](const BasicBlock* b) { return b->immediate_post_dominator(); }); +} + +BasicBlock::DominatorIterator BasicBlock::pdom_begin() { + return DominatorIterator( + this, [](const BasicBlock* b) { return b->immediate_post_dominator(); }); +} + +const BasicBlock::DominatorIterator BasicBlock::pdom_end() const { + return DominatorIterator(); +} + +BasicBlock::DominatorIterator BasicBlock::pdom_end() { + return DominatorIterator(); +} + +bool operator==(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs) { + return lhs.current_ == rhs.current_; +} + +bool operator!=(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs) { + return !(lhs == rhs); +} + +const BasicBlock*& BasicBlock::DominatorIterator::operator*() { + return current_; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/basic_block.h b/third_party/spirv-tools/source/val/basic_block.h new file mode 100644 index 0000000..5eea4f9 --- /dev/null +++ b/third_party/spirv-tools/source/val/basic_block.h @@ -0,0 +1,244 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_BASIC_BLOCK_H_ +#define SOURCE_VAL_BASIC_BLOCK_H_ + +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" + +namespace spvtools { +namespace val { + +enum BlockType : uint32_t { + kBlockTypeUndefined, + kBlockTypeSelection, + kBlockTypeLoop, + kBlockTypeMerge, + kBlockTypeBreak, + kBlockTypeContinue, + kBlockTypeReturn, + kBlockTypeCOUNT ///< Total number of block types. (must be the last element) +}; + +class Instruction; + +// This class represents a basic block in a SPIR-V module +class BasicBlock { + public: + /// Constructor for a BasicBlock + /// + /// @param[in] id The ID of the basic block + explicit BasicBlock(uint32_t id); + + /// Returns the id of the BasicBlock + uint32_t id() const { return id_; } + + /// Returns the predecessors of the BasicBlock + const std::vector* predecessors() const { + return &predecessors_; + } + + /// Returns the predecessors of the BasicBlock + std::vector* predecessors() { return &predecessors_; } + + /// Returns the successors of the BasicBlock + const std::vector* successors() const { return &successors_; } + + /// Returns the successors of the BasicBlock + std::vector* successors() { return &successors_; } + + /// Returns true if the block is reachable in the CFG + bool reachable() const { return reachable_; } + + /// Returns true if BasicBlock is of the given type + bool is_type(BlockType type) const { + if (type == kBlockTypeUndefined) return type_.none(); + return type_.test(type); + } + + /// Sets the reachability of the basic block in the CFG + void set_reachable(bool reachability) { reachable_ = reachability; } + + /// Sets the type of the BasicBlock + void set_type(BlockType type) { + if (type == kBlockTypeUndefined) + type_.reset(); + else + type_.set(type); + } + + /// Sets the immedate dominator of this basic block + /// + /// @param[in] dom_block The dominator block + void SetImmediateDominator(BasicBlock* dom_block); + + /// Sets the immedate post dominator of this basic block + /// + /// @param[in] pdom_block The post dominator block + void SetImmediatePostDominator(BasicBlock* pdom_block); + + /// Returns the immedate dominator of this basic block + BasicBlock* immediate_dominator(); + + /// Returns the immedate dominator of this basic block + const BasicBlock* immediate_dominator() const; + + /// Returns the immedate post dominator of this basic block + BasicBlock* immediate_post_dominator(); + + /// Returns the immedate post dominator of this basic block + const BasicBlock* immediate_post_dominator() const; + + /// Returns the label instruction for the block, or nullptr if not set. + const Instruction* label() const { return label_; } + + //// Registers the label instruction for the block. + void set_label(const Instruction* t) { label_ = t; } + + /// Registers the terminator instruction for the block. + void set_terminator(const Instruction* t) { terminator_ = t; } + + /// Returns the terminator instruction for the block. + const Instruction* terminator() const { return terminator_; } + + /// Adds @p next BasicBlocks as successors of this BasicBlock + void RegisterSuccessors( + const std::vector& next = std::vector()); + + /// Returns true if the id of the BasicBlock matches + bool operator==(const BasicBlock& other) const { return other.id_ == id_; } + + /// Returns true if the id of the BasicBlock matches + bool operator==(const uint32_t& other_id) const { return other_id == id_; } + + /// Returns true if this block dominates the other block. + /// Assumes dominators have been computed. + bool dominates(const BasicBlock& other) const; + + /// Returns true if this block postdominates the other block. + /// Assumes dominators have been computed. + bool postdominates(const BasicBlock& other) const; + + /// @brief A BasicBlock dominator iterator class + /// + /// This iterator will iterate over the (post)dominators of the block + class DominatorIterator + : public std::iterator { + public: + /// @brief Constructs the end of dominator iterator + /// + /// This will create an iterator which will represent the element + /// before the root node of the dominator tree + DominatorIterator(); + + /// @brief Constructs an iterator for the given block which points to + /// @p block + /// + /// @param block The block which is referenced by the iterator + /// @param dominator_func This function will be called to get the immediate + /// (post)dominator of the current block + DominatorIterator( + const BasicBlock* block, + std::function dominator_func); + + /// @brief Advances the iterator + DominatorIterator& operator++(); + + /// @brief Returns the current element + const BasicBlock*& operator*(); + + friend bool operator==(const DominatorIterator& lhs, + const DominatorIterator& rhs); + + private: + const BasicBlock* current_; + std::function dom_func_; + }; + + /// Returns a dominator iterator which points to the current block + const DominatorIterator dom_begin() const; + + /// Returns a dominator iterator which points to the current block + DominatorIterator dom_begin(); + + /// Returns a dominator iterator which points to one element past the first + /// block + const DominatorIterator dom_end() const; + + /// Returns a dominator iterator which points to one element past the first + /// block + DominatorIterator dom_end(); + + /// Returns a post dominator iterator which points to the current block + const DominatorIterator pdom_begin() const; + /// Returns a post dominator iterator which points to the current block + DominatorIterator pdom_begin(); + + /// Returns a post dominator iterator which points to one element past the + /// last block + const DominatorIterator pdom_end() const; + + /// Returns a post dominator iterator which points to one element past the + /// last block + DominatorIterator pdom_end(); + + private: + /// Id of the BasicBlock + const uint32_t id_; + + /// Pointer to the immediate dominator of the BasicBlock + BasicBlock* immediate_dominator_; + + /// Pointer to the immediate dominator of the BasicBlock + BasicBlock* immediate_post_dominator_; + + /// The set of predecessors of the BasicBlock + std::vector predecessors_; + + /// The set of successors of the BasicBlock + std::vector successors_; + + /// The type of the block + std::bitset type_; + + /// True if the block is reachable in the CFG + bool reachable_; + + /// label of this block, if any. + const Instruction* label_; + + /// Terminator of this block. + const Instruction* terminator_; +}; + +/// @brief Returns true if the iterators point to the same element or if both +/// iterators point to the @p dom_end block +bool operator==(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs); + +/// @brief Returns true if the iterators point to different elements and they +/// do not both point to the @p dom_end block +bool operator!=(const BasicBlock::DominatorIterator& lhs, + const BasicBlock::DominatorIterator& rhs); + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_BASIC_BLOCK_H_ diff --git a/third_party/spirv-tools/source/val/construct.cpp b/third_party/spirv-tools/source/val/construct.cpp new file mode 100644 index 0000000..733856c --- /dev/null +++ b/third_party/spirv-tools/source/val/construct.cpp @@ -0,0 +1,229 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/construct.h" + +#include +#include +#include + +#include "source/val/function.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +Construct::Construct(ConstructType construct_type, BasicBlock* entry, + BasicBlock* exit, std::vector constructs) + : type_(construct_type), + corresponding_constructs_(constructs), + entry_block_(entry), + exit_block_(exit) {} + +ConstructType Construct::type() const { return type_; } + +const std::vector& Construct::corresponding_constructs() const { + return corresponding_constructs_; +} +std::vector& Construct::corresponding_constructs() { + return corresponding_constructs_; +} + +bool ValidateConstructSize(ConstructType type, size_t size) { + switch (type) { + case ConstructType::kSelection: + return size == 0; + case ConstructType::kContinue: + return size == 1; + case ConstructType::kLoop: + return size == 1; + case ConstructType::kCase: + return size >= 1; + default: + assert(1 == 0 && "Type not defined"); + } + return false; +} + +void Construct::set_corresponding_constructs( + std::vector constructs) { + assert(ValidateConstructSize(type_, constructs.size())); + corresponding_constructs_ = constructs; +} + +const BasicBlock* Construct::entry_block() const { return entry_block_; } +BasicBlock* Construct::entry_block() { return entry_block_; } + +const BasicBlock* Construct::exit_block() const { return exit_block_; } +BasicBlock* Construct::exit_block() { return exit_block_; } + +void Construct::set_exit(BasicBlock* block) { exit_block_ = block; } + +Construct::ConstructBlockSet Construct::blocks(Function* function) const { + auto header = entry_block(); + auto merge = exit_block(); + assert(header); + int header_depth = function->GetBlockDepth(const_cast(header)); + ConstructBlockSet construct_blocks; + std::unordered_set corresponding_headers; + for (auto& other : corresponding_constructs()) { + corresponding_headers.insert(other->entry_block()); + } + std::vector stack; + stack.push_back(const_cast(header)); + while (!stack.empty()) { + BasicBlock* block = stack.back(); + stack.pop_back(); + + if (merge == block && ExitBlockIsMergeBlock()) { + // Merge block is not part of the construct. + continue; + } + + if (corresponding_headers.count(block)) { + // Entered a corresponding construct. + continue; + } + + int block_depth = function->GetBlockDepth(block); + if (block_depth < header_depth) { + // Broke to outer construct. + continue; + } + + // In a loop, the continue target is at a depth of the loop construct + 1. + // A selection construct nested directly within the loop construct is also + // at the same depth. It is valid, however, to branch directly to the + // continue target from within the selection construct. + if (block != header && block_depth == header_depth && + type() == ConstructType::kSelection && + block->is_type(kBlockTypeContinue)) { + // Continued to outer construct. + continue; + } + + if (!construct_blocks.insert(block).second) continue; + + if (merge != block) { + for (auto succ : *block->successors()) { + // All blocks in the construct must be dominated by the header. + if (header->dominates(*succ)) { + stack.push_back(succ); + } + } + } + } + + return construct_blocks; +} + +bool Construct::IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const { + // Structured Exits: + // - Selection: + // - branch to its merge + // - branch to nearest enclosing loop merge or continue + // - branch to nearest enclosing switch selection merge + // - Loop: + // - branch to its merge + // - branch to its continue + // - Continue: + // - branch to loop header + // - branch to loop merge + // + // Note: we will never see a case construct here. + assert(type() != ConstructType::kCase); + if (type() == ConstructType::kLoop) { + auto header = entry_block(); + auto terminator = header->terminator(); + auto index = terminator - &_.ordered_instructions()[0]; + auto merge_inst = &_.ordered_instructions()[index - 1]; + auto merge_block_id = merge_inst->GetOperandAs(0u); + auto continue_block_id = merge_inst->GetOperandAs(1u); + if (dest->id() == merge_block_id || dest->id() == continue_block_id) { + return true; + } + } else if (type() == ConstructType::kContinue) { + auto loop_construct = corresponding_constructs()[0]; + auto header = loop_construct->entry_block(); + auto terminator = header->terminator(); + auto index = terminator - &_.ordered_instructions()[0]; + auto merge_inst = &_.ordered_instructions()[index - 1]; + auto merge_block_id = merge_inst->GetOperandAs(0u); + if (dest == header || dest->id() == merge_block_id) { + return true; + } + } else { + assert(type() == ConstructType::kSelection); + if (dest == exit_block()) { + return true; + } + + // The next block in the traversal is either: + // i. The header block that declares |block| as its merge block. + // ii. The immediate dominator of |block|. + auto NextBlock = [](const BasicBlock* block) -> const BasicBlock* { + for (auto& use : block->label()->uses()) { + if ((use.first->opcode() == SpvOpLoopMerge || + use.first->opcode() == SpvOpSelectionMerge) && + use.second == 1) + return use.first->block(); + } + return block->immediate_dominator(); + }; + + bool seen_switch = false; + auto header = entry_block(); + auto block = NextBlock(header); + while (block) { + auto terminator = block->terminator(); + auto index = terminator - &_.ordered_instructions()[0]; + auto merge_inst = &_.ordered_instructions()[index - 1]; + if (merge_inst->opcode() == SpvOpLoopMerge || + (header->terminator()->opcode() != SpvOpSwitch && + merge_inst->opcode() == SpvOpSelectionMerge && + terminator->opcode() == SpvOpSwitch)) { + auto merge_target = merge_inst->GetOperandAs(0u); + auto merge_block = merge_inst->function()->GetBlock(merge_target).first; + if (merge_block->dominates(*header)) { + block = NextBlock(block); + continue; + } + + if ((!seen_switch || merge_inst->opcode() == SpvOpLoopMerge) && + dest->id() == merge_target) { + return true; + } else if (merge_inst->opcode() == SpvOpLoopMerge) { + auto continue_target = merge_inst->GetOperandAs(1u); + if (dest->id() == continue_target) { + return true; + } + } + + if (terminator->opcode() == SpvOpSwitch) { + seen_switch = true; + } + + // Hit an enclosing loop and didn't break or continue. + if (merge_inst->opcode() == SpvOpLoopMerge) return false; + } + + block = NextBlock(block); + } + } + + return false; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/construct.h b/third_party/spirv-tools/source/val/construct.h new file mode 100644 index 0000000..9476760 --- /dev/null +++ b/third_party/spirv-tools/source/val/construct.h @@ -0,0 +1,170 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_CONSTRUCT_H_ +#define SOURCE_VAL_CONSTRUCT_H_ + +#include +#include +#include + +#include "source/val/basic_block.h" + +namespace spvtools { +namespace val { +class ValidationState_t; + +/// Functor for ordering BasicBlocks. BasicBlock pointers must not be null. +struct less_than_id { + bool operator()(const BasicBlock* lhs, const BasicBlock* rhs) const { + return lhs->id() < rhs->id(); + } +}; + +enum class ConstructType : int { + kNone = 0, + /// The set of blocks dominated by a selection header, minus the set of blocks + /// dominated by the header's merge block + kSelection, + /// The set of blocks dominated by an OpLoopMerge's Continue Target and post + /// dominated by the corresponding back + kContinue, + /// The set of blocks dominated by a loop header, minus the set of blocks + /// dominated by the loop's merge block, minus the loop's corresponding + /// continue construct + kLoop, + /// The set of blocks dominated by an OpSwitch's Target or Default, minus the + /// set of blocks dominated by the OpSwitch's merge block (this construct is + /// only defined for those OpSwitch Target or Default that are not equal to + /// the OpSwitch's corresponding merge block) + kCase +}; + +class Function; + +/// @brief This class tracks the CFG constructs as defined in the SPIR-V spec +class Construct { + public: + Construct(ConstructType type, BasicBlock* dominator, + BasicBlock* exit = nullptr, + std::vector constructs = std::vector()); + + /// Returns the type of the construct + ConstructType type() const; + + const std::vector& corresponding_constructs() const; + std::vector& corresponding_constructs(); + void set_corresponding_constructs(std::vector constructs); + + /// Returns the dominator block of the construct. + /// + /// This is usually the header block or the first block of the construct. + const BasicBlock* entry_block() const; + + /// Returns the dominator block of the construct. + /// + /// This is usually the header block or the first block of the construct. + BasicBlock* entry_block(); + + /// Returns the exit block of the construct. + /// + /// For a continue construct it is the backedge block of the corresponding + /// loop construct. For the case construct it is the block that branches to + /// the OpSwitch merge block or other case blocks. Otherwise it is the merge + /// block of the corresponding header block + const BasicBlock* exit_block() const; + + /// Returns the exit block of the construct. + /// + /// For a continue construct it is the backedge block of the corresponding + /// loop construct. For the case construct it is the block that branches to + /// the OpSwitch merge block or other case blocks. Otherwise it is the merge + /// block of the corresponding header block + BasicBlock* exit_block(); + + /// Sets the exit block for this construct. This is useful for continue + /// constructs which do not know the back-edge block during construction + void set_exit(BasicBlock* exit_block); + + // Returns whether the exit block of this construct is the merge block + // for an OpLoopMerge or OpSelectionMerge + bool ExitBlockIsMergeBlock() const { + return type_ == ConstructType::kLoop || type_ == ConstructType::kSelection; + } + + using ConstructBlockSet = std::set; + + // Returns the basic blocks in this construct. This function should not + // be called before the exit block is set and dominators have been + // calculated. + ConstructBlockSet blocks(Function* function) const; + + // Returns true if |dest| is structured exit from the construct. Structured + // exits depend on the construct type. + // Selection: + // * branch to the associated merge + // * branch to the merge or continue of the innermost loop containing the + // selection + // * branch to the merge block of the innermost switch containing the + // selection + // Loop: + // * branch to the associated merge or continue + // Continue: + // * back-edge to the associated loop header + // * branch to the associated loop merge + // + // Note: the validator does not generate case constructs. Switches are + // checked separately from other constructs. + bool IsStructuredExit(ValidationState_t& _, BasicBlock* dest) const; + + private: + /// The type of the construct + ConstructType type_; + + /// These are the constructs that are related to this construct. These + /// constructs can be the continue construct, for the corresponding loop + /// construct, the case construct that are part of the same OpSwitch + /// instruction + /// + /// Here is a table that describes what constructs are included in + /// @p corresponding_constructs_ + /// | this construct | corresponding construct | + /// |----------------|----------------------------------| + /// | loop | continue | + /// | continue | loop | + /// | case | other cases in the same OpSwitch | + /// + /// kContinue and kLoop constructs will always have corresponding + /// constructs even if they are represented by the same block + std::vector corresponding_constructs_; + + /// @brief Dominator block for the construct + /// + /// The dominator block for the construct. Depending on the construct this may + /// be a selection header, a continue target of a loop, a loop header or a + /// Target or Default block of a switch + BasicBlock* entry_block_; + + /// @brief Exiting block for the construct + /// + /// The exit block for the construct. This can be a merge block for the loop + /// and selection constructs, a back-edge block for a continue construct, or + /// the branching block for the case construct + BasicBlock* exit_block_; +}; + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_CONSTRUCT_H_ diff --git a/third_party/spirv-tools/source/val/decoration.h b/third_party/spirv-tools/source/val/decoration.h new file mode 100644 index 0000000..ed3320f --- /dev/null +++ b/third_party/spirv-tools/source/val/decoration.h @@ -0,0 +1,89 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_DECORATION_H_ +#define SOURCE_VAL_DECORATION_H_ + +#include +#include +#include + +#include "source/latest_version_spirv_header.h" + +namespace spvtools { +namespace val { + +// An object of this class represents a specific decoration including its +// parameters (if any). Decorations are used by OpDecorate and OpMemberDecorate, +// and they describe certain properties that can be assigned to one or several +// s. +// +// A Decoration object contains the decoration type (an enum), associated +// literal parameters, and struct member index. If the decoration does not apply +// to a struct member, then the index is kInvalidIndex. A Decoration object does +// not store the target Id, i.e. the Id to which it applies. It is +// possible for the same decoration to be applied to several s (and they +// might be assigned using separate SPIR-V instructions, possibly using an +// assignment through GroupDecorate). +// +// Example 1: Decoration for an object with no parameters: +// OpDecorate %obj Flat +// dec_type_ = SpvDecorationFlat +// params_ = empty vector +// struct_member_index_ = kInvalidMember +// +// Example 2: Decoration for an object with two parameters: +// OpDecorate %obj LinkageAttributes "link" Import +// dec_type_ = SpvDecorationLinkageAttributes +// params_ = vector { link, Import } +// struct_member_index_ = kInvalidMember +// +// Example 3: Decoration for a member of a structure with one parameter: +// OpMemberDecorate %struct 2 Offset 2 +// dec_type_ = SpvDecorationOffset +// params_ = vector { 2 } +// struct_member_index_ = 2 +// +class Decoration { + public: + enum { kInvalidMember = -1 }; + Decoration(SpvDecoration t, + const std::vector& parameters = std::vector(), + uint32_t member_index = kInvalidMember) + : dec_type_(t), params_(parameters), struct_member_index_(member_index) {} + + void set_struct_member_index(uint32_t index) { struct_member_index_ = index; } + int struct_member_index() const { return struct_member_index_; } + SpvDecoration dec_type() const { return dec_type_; } + std::vector& params() { return params_; } + const std::vector& params() const { return params_; } + + inline bool operator==(const Decoration& rhs) const { + return (dec_type_ == rhs.dec_type_ && params_ == rhs.params_ && + struct_member_index_ == rhs.struct_member_index_); + } + + private: + SpvDecoration dec_type_; + std::vector params_; + + // If the decoration applies to a member of a structure type, then the index + // of the member is stored here. Otherwise, this is kInvalidIndex. + int struct_member_index_; +}; + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_DECORATION_H_ diff --git a/third_party/spirv-tools/source/val/function.cpp b/third_party/spirv-tools/source/val/function.cpp new file mode 100644 index 0000000..249c866 --- /dev/null +++ b/third_party/spirv-tools/source/val/function.cpp @@ -0,0 +1,416 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/function.h" + +#include +#include +#include +#include +#include +#include + +#include "source/cfa.h" +#include "source/val/basic_block.h" +#include "source/val/construct.h" +#include "source/val/validate.h" + +namespace spvtools { +namespace val { + +// Universal Limit of ResultID + 1 +static const uint32_t kInvalidId = 0x400000; + +Function::Function(uint32_t function_id, uint32_t result_type_id, + SpvFunctionControlMask function_control, + uint32_t function_type_id) + : id_(function_id), + function_type_id_(function_type_id), + result_type_id_(result_type_id), + function_control_(function_control), + declaration_type_(FunctionDecl::kFunctionDeclUnknown), + end_has_been_registered_(false), + blocks_(), + current_block_(nullptr), + pseudo_entry_block_(0), + pseudo_exit_block_(kInvalidId), + cfg_constructs_(), + variable_ids_(), + parameter_ids_() {} + +bool Function::IsFirstBlock(uint32_t block_id) const { + return !ordered_blocks_.empty() && *first_block() == block_id; +} + +spv_result_t Function::RegisterFunctionParameter(uint32_t parameter_id, + uint32_t type_id) { + assert(current_block_ == nullptr && + "RegisterFunctionParameter can only be called when parsing the binary " + "ouside of a block"); + // TODO(umar): Validate function parameter type order and count + // TODO(umar): Use these variables to validate parameter type + (void)parameter_id; + (void)type_id; + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterLoopMerge(uint32_t merge_id, + uint32_t continue_id) { + RegisterBlock(merge_id, false); + RegisterBlock(continue_id, false); + BasicBlock& merge_block = blocks_.at(merge_id); + BasicBlock& continue_target_block = blocks_.at(continue_id); + assert(current_block_ && + "RegisterLoopMerge must be called when called within a block"); + + current_block_->set_type(kBlockTypeLoop); + merge_block.set_type(kBlockTypeMerge); + continue_target_block.set_type(kBlockTypeContinue); + Construct& loop_construct = + AddConstruct({ConstructType::kLoop, current_block_, &merge_block}); + Construct& continue_construct = + AddConstruct({ConstructType::kContinue, &continue_target_block}); + + continue_construct.set_corresponding_constructs({&loop_construct}); + loop_construct.set_corresponding_constructs({&continue_construct}); + merge_block_header_[&merge_block] = current_block_; + if (continue_target_headers_.find(&continue_target_block) == + continue_target_headers_.end()) { + continue_target_headers_[&continue_target_block] = {current_block_}; + } else { + continue_target_headers_[&continue_target_block].push_back(current_block_); + } + + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterSelectionMerge(uint32_t merge_id) { + RegisterBlock(merge_id, false); + BasicBlock& merge_block = blocks_.at(merge_id); + current_block_->set_type(kBlockTypeSelection); + merge_block.set_type(kBlockTypeMerge); + merge_block_header_[&merge_block] = current_block_; + + AddConstruct({ConstructType::kSelection, current_block(), &merge_block}); + + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterSetFunctionDeclType(FunctionDecl type) { + assert(declaration_type_ == FunctionDecl::kFunctionDeclUnknown); + declaration_type_ = type; + return SPV_SUCCESS; +} + +spv_result_t Function::RegisterBlock(uint32_t block_id, bool is_definition) { + assert( + declaration_type_ == FunctionDecl::kFunctionDeclDefinition && + "RegisterBlocks can only be called after declaration_type_ is defined"); + + std::unordered_map::iterator inserted_block; + bool success = false; + tie(inserted_block, success) = + blocks_.insert({block_id, BasicBlock(block_id)}); + if (is_definition) { // new block definition + assert(current_block_ == nullptr && + "Register Block can only be called when parsing a binary outside of " + "a BasicBlock"); + + undefined_blocks_.erase(block_id); + current_block_ = &inserted_block->second; + ordered_blocks_.push_back(current_block_); + } else if (success) { // Block doesn't exsist but this is not a definition + undefined_blocks_.insert(block_id); + } + + return SPV_SUCCESS; +} + +void Function::RegisterBlockEnd(std::vector next_list) { + assert( + current_block_ && + "RegisterBlockEnd can only be called when parsing a binary in a block"); + std::vector next_blocks; + next_blocks.reserve(next_list.size()); + + std::unordered_map::iterator inserted_block; + bool success; + for (uint32_t successor_id : next_list) { + tie(inserted_block, success) = + blocks_.insert({successor_id, BasicBlock(successor_id)}); + if (success) { + undefined_blocks_.insert(successor_id); + } + next_blocks.push_back(&inserted_block->second); + } + + if (current_block_->is_type(kBlockTypeLoop)) { + // For each loop header, record the set of its successors, and include + // its continue target if the continue target is not the loop header + // itself. + std::vector& next_blocks_plus_continue_target = + loop_header_successors_plus_continue_target_map_[current_block_]; + next_blocks_plus_continue_target = next_blocks; + auto continue_target = + FindConstructForEntryBlock(current_block_, ConstructType::kLoop) + .corresponding_constructs() + .back() + ->entry_block(); + if (continue_target != current_block_) { + next_blocks_plus_continue_target.push_back(continue_target); + } + } + + current_block_->RegisterSuccessors(next_blocks); + current_block_ = nullptr; + return; +} + +void Function::RegisterFunctionEnd() { + if (!end_has_been_registered_) { + end_has_been_registered_ = true; + + ComputeAugmentedCFG(); + } +} + +size_t Function::block_count() const { return blocks_.size(); } + +size_t Function::undefined_block_count() const { + return undefined_blocks_.size(); +} + +const std::vector& Function::ordered_blocks() const { + return ordered_blocks_; +} +std::vector& Function::ordered_blocks() { return ordered_blocks_; } + +const BasicBlock* Function::current_block() const { return current_block_; } +BasicBlock* Function::current_block() { return current_block_; } + +const std::list& Function::constructs() const { + return cfg_constructs_; +} +std::list& Function::constructs() { return cfg_constructs_; } + +const BasicBlock* Function::first_block() const { + if (ordered_blocks_.empty()) return nullptr; + return ordered_blocks_[0]; +} +BasicBlock* Function::first_block() { + if (ordered_blocks_.empty()) return nullptr; + return ordered_blocks_[0]; +} + +bool Function::IsBlockType(uint32_t merge_block_id, BlockType type) const { + bool ret = false; + const BasicBlock* block; + std::tie(block, std::ignore) = GetBlock(merge_block_id); + if (block) { + ret = block->is_type(type); + } + return ret; +} + +std::pair Function::GetBlock(uint32_t block_id) const { + const auto b = blocks_.find(block_id); + if (b != end(blocks_)) { + const BasicBlock* block = &(b->second); + bool defined = + undefined_blocks_.find(block->id()) == std::end(undefined_blocks_); + return std::make_pair(block, defined); + } else { + return std::make_pair(nullptr, false); + } +} + +std::pair Function::GetBlock(uint32_t block_id) { + const BasicBlock* out; + bool defined; + std::tie(out, defined) = + const_cast(this)->GetBlock(block_id); + return std::make_pair(const_cast(out), defined); +} + +Function::GetBlocksFunction Function::AugmentedCFGSuccessorsFunction() const { + return [this](const BasicBlock* block) { + auto where = augmented_successors_map_.find(block); + return where == augmented_successors_map_.end() ? block->successors() + : &(*where).second; + }; +} + +Function::GetBlocksFunction +Function::AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const { + return [this](const BasicBlock* block) { + auto where = loop_header_successors_plus_continue_target_map_.find(block); + return where == loop_header_successors_plus_continue_target_map_.end() + ? AugmentedCFGSuccessorsFunction()(block) + : &(*where).second; + }; +} + +Function::GetBlocksFunction Function::AugmentedCFGPredecessorsFunction() const { + return [this](const BasicBlock* block) { + auto where = augmented_predecessors_map_.find(block); + return where == augmented_predecessors_map_.end() ? block->predecessors() + : &(*where).second; + }; +} + +void Function::ComputeAugmentedCFG() { + // Compute the successors of the pseudo-entry block, and + // the predecessors of the pseudo exit block. + auto succ_func = [](const BasicBlock* b) { return b->successors(); }; + auto pred_func = [](const BasicBlock* b) { return b->predecessors(); }; + CFA::ComputeAugmentedCFG( + ordered_blocks_, &pseudo_entry_block_, &pseudo_exit_block_, + &augmented_successors_map_, &augmented_predecessors_map_, succ_func, + pred_func); +} + +Construct& Function::AddConstruct(const Construct& new_construct) { + cfg_constructs_.push_back(new_construct); + auto& result = cfg_constructs_.back(); + entry_block_to_construct_[std::make_pair(new_construct.entry_block(), + new_construct.type())] = &result; + return result; +} + +Construct& Function::FindConstructForEntryBlock(const BasicBlock* entry_block, + ConstructType type) { + auto where = + entry_block_to_construct_.find(std::make_pair(entry_block, type)); + assert(where != entry_block_to_construct_.end()); + auto construct_ptr = (*where).second; + assert(construct_ptr); + return *construct_ptr; +} + +int Function::GetBlockDepth(BasicBlock* bb) { + // Guard against nullptr. + if (!bb) { + return 0; + } + // Only calculate the depth if it's not already calculated. + // This function uses memoization to avoid duplicate CFG depth calculations. + if (block_depth_.find(bb) != block_depth_.end()) { + return block_depth_[bb]; + } + + BasicBlock* bb_dom = bb->immediate_dominator(); + if (!bb_dom || bb == bb_dom) { + // This block has no dominator, so it's at depth 0. + block_depth_[bb] = 0; + } else if (bb->is_type(kBlockTypeContinue)) { + // This rule must precede the rule for merge blocks in order to set up + // depths correctly. If a block is both a merge and continue then the merge + // is nested within the continue's loop (or the graph is incorrect). + // The depth of the continue block entry point is 1 + loop header depth. + Construct* continue_construct = + entry_block_to_construct_[std::make_pair(bb, ConstructType::kContinue)]; + assert(continue_construct); + // Continue construct has only 1 corresponding construct (loop header). + Construct* loop_construct = + continue_construct->corresponding_constructs()[0]; + assert(loop_construct); + BasicBlock* loop_header = loop_construct->entry_block(); + // The continue target may be the loop itself (while 1). + // In such cases, the depth of the continue block is: 1 + depth of the + // loop's dominator block. + if (loop_header == bb) { + block_depth_[bb] = 1 + GetBlockDepth(bb_dom); + } else { + block_depth_[bb] = 1 + GetBlockDepth(loop_header); + } + } else if (bb->is_type(kBlockTypeMerge)) { + // If this is a merge block, its depth is equal to the block before + // branching. + BasicBlock* header = merge_block_header_[bb]; + assert(header); + block_depth_[bb] = GetBlockDepth(header); + } else if (bb_dom->is_type(kBlockTypeSelection) || + bb_dom->is_type(kBlockTypeLoop)) { + // The dominator of the given block is a header block. So, the nesting + // depth of this block is: 1 + nesting depth of the header. + block_depth_[bb] = 1 + GetBlockDepth(bb_dom); + } else { + block_depth_[bb] = GetBlockDepth(bb_dom); + } + return block_depth_[bb]; +} + +void Function::RegisterExecutionModelLimitation(SpvExecutionModel model, + const std::string& message) { + execution_model_limitations_.push_back( + [model, message](SpvExecutionModel in_model, std::string* out_message) { + if (model != in_model) { + if (out_message) { + *out_message = message; + } + return false; + } + return true; + }); +} + +bool Function::IsCompatibleWithExecutionModel(SpvExecutionModel model, + std::string* reason) const { + bool return_value = true; + std::stringstream ss_reason; + + for (const auto& is_compatible : execution_model_limitations_) { + std::string message; + if (!is_compatible(model, &message)) { + if (!reason) return false; + return_value = false; + if (!message.empty()) { + ss_reason << message << "\n"; + } + } + } + + if (!return_value && reason) { + *reason = ss_reason.str(); + } + + return return_value; +} + +bool Function::CheckLimitations(const ValidationState_t& _, + const Function* entry_point, + std::string* reason) const { + bool return_value = true; + std::stringstream ss_reason; + + for (const auto& is_compatible : limitations_) { + std::string message; + if (!is_compatible(_, entry_point, &message)) { + if (!reason) return false; + return_value = false; + if (!message.empty()) { + ss_reason << message << "\n"; + } + } + } + + if (!return_value && reason) { + *reason = ss_reason.str(); + } + + return return_value; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/function.h b/third_party/spirv-tools/source/val/function.h new file mode 100644 index 0000000..400bb63 --- /dev/null +++ b/third_party/spirv-tools/source/val/function.h @@ -0,0 +1,400 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_FUNCTION_H_ +#define SOURCE_VAL_FUNCTION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/latest_version_spirv_header.h" +#include "source/val/basic_block.h" +#include "source/val/construct.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +struct bb_constr_type_pair_hash { + std::size_t operator()( + const std::pair& p) const { + auto h1 = std::hash{}(p.first); + auto h2 = std::hash::type>{}( + static_cast::type>(p.second)); + return (h1 ^ h2); + } +}; + +enum class FunctionDecl { + kFunctionDeclUnknown, /// < Unknown function declaration + kFunctionDeclDeclaration, /// < Function declaration + kFunctionDeclDefinition /// < Function definition +}; + +/// This class manages all function declaration and definitions in a module. It +/// handles the state and id information while parsing a function in the SPIR-V +/// binary. +class Function { + public: + Function(uint32_t id, uint32_t result_type_id, + SpvFunctionControlMask function_control, uint32_t function_type_id); + + /// Registers a function parameter in the current function + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterFunctionParameter(uint32_t id, uint32_t type_id); + + /// Sets the declaration type of the current function + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterSetFunctionDeclType(FunctionDecl type); + + /// Registers a block in the current function. Subsequent block instructions + /// will target this block + /// @param id The ID of the label of the block + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterBlock(uint32_t id, bool is_definition = true); + + /// Registers a variable in the current block + /// + /// @param[in] type_id The type ID of the varaible + /// @param[in] id The ID of the varaible + /// @param[in] storage The storage of the variable + /// @param[in] init_id The initializer ID of the variable + /// + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterBlockVariable(uint32_t type_id, uint32_t id, + SpvStorageClass storage, uint32_t init_id); + + /// Registers a loop merge construct in the function + /// + /// @param[in] merge_id The merge block ID of the loop + /// @param[in] continue_id The continue block ID of the loop + /// + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterLoopMerge(uint32_t merge_id, uint32_t continue_id); + + /// Registers a selection merge construct in the function + /// @return Returns SPV_SUCCESS if the call was successful + spv_result_t RegisterSelectionMerge(uint32_t merge_id); + + /// Registers the end of the block + /// + /// @param[in] successors_list A list of ids to the block's successors + void RegisterBlockEnd(std::vector successors_list); + + /// Registers the end of the function. This is idempotent. + void RegisterFunctionEnd(); + + /// Returns true if the \p id block is the first block of this function + bool IsFirstBlock(uint32_t id) const; + + /// Returns true if the \p merge_block_id is a BlockType of \p type + bool IsBlockType(uint32_t merge_block_id, BlockType type) const; + + /// Returns a pair consisting of the BasicBlock with \p id and a bool + /// which is true if the block has been defined, and false if it is + /// declared but not defined. This function will return nullptr if the + /// \p id was not declared and not defined at the current point in the binary + std::pair GetBlock(uint32_t id) const; + std::pair GetBlock(uint32_t id); + + /// Returns the first block of the current function + const BasicBlock* first_block() const; + + /// Returns the first block of the current function + BasicBlock* first_block(); + + /// Returns a vector of all the blocks in the function + const std::vector& ordered_blocks() const; + + /// Returns a vector of all the blocks in the function + std::vector& ordered_blocks(); + + /// Returns a list of all the cfg constructs in the function + const std::list& constructs() const; + + /// Returns a list of all the cfg constructs in the function + std::list& constructs(); + + /// Returns the number of blocks in the current function being parsed + size_t block_count() const; + + /// Returns the id of the function + uint32_t id() const { return id_; } + + /// Returns return type id of the function + uint32_t GetResultTypeId() const { return result_type_id_; } + + /// Returns the number of blocks in the current function being parsed + size_t undefined_block_count() const; + const std::unordered_set& undefined_blocks() const { + return undefined_blocks_; + } + + /// Returns the block that is currently being parsed in the binary + BasicBlock* current_block(); + + /// Returns the block that is currently being parsed in the binary + const BasicBlock* current_block() const; + + // For dominance calculations, we want to analyze all the + // blocks in the function, even in degenerate control flow cases + // including unreachable blocks. We therefore make an "augmented CFG" + // which is the same as the ordinary CFG but adds: + // - A pseudo-entry node. + // - A pseudo-exit node. + // - A minimal set of edges so that a forward traversal from the + // pseudo-entry node will visit all nodes. + // - A minimal set of edges so that a backward traversal from the + // pseudo-exit node will visit all nodes. + // In particular, the pseudo-entry node is the unique source of the + // augmented CFG, and the psueo-exit node is the unique sink of the + // augmented CFG. + + /// Returns the pseudo exit block + BasicBlock* pseudo_entry_block() { return &pseudo_entry_block_; } + + /// Returns the pseudo exit block + const BasicBlock* pseudo_entry_block() const { return &pseudo_entry_block_; } + + /// Returns the pseudo exit block + BasicBlock* pseudo_exit_block() { return &pseudo_exit_block_; } + + /// Returns the pseudo exit block + const BasicBlock* pseudo_exit_block() const { return &pseudo_exit_block_; } + + using GetBlocksFunction = + std::function*(const BasicBlock*)>; + /// Returns the block successors function for the augmented CFG. + GetBlocksFunction AugmentedCFGSuccessorsFunction() const; + /// Like AugmentedCFGSuccessorsFunction, but also includes a forward edge from + /// a loop header block to its continue target, if they are different blocks. + GetBlocksFunction + AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge() const; + /// Returns the block predecessors function for the augmented CFG. + GetBlocksFunction AugmentedCFGPredecessorsFunction() const; + + /// Returns the control flow nesting depth of the given basic block. + /// This function only works when you have structured control flow. + /// This function should only be called after the control flow constructs have + /// been identified and dominators have been computed. + int GetBlockDepth(BasicBlock* bb); + + /// Prints a GraphViz digraph of the CFG of the current funciton + void PrintDotGraph() const; + + /// Prints a directed graph of the CFG of the current funciton + void PrintBlocks() const; + + /// Registers execution model limitation such as "Feature X is only available + /// with Execution Model Y". + void RegisterExecutionModelLimitation(SpvExecutionModel model, + const std::string& message); + + /// Registers execution model limitation with an |is_compatible| functor. + void RegisterExecutionModelLimitation( + std::function is_compatible) { + execution_model_limitations_.push_back(is_compatible); + } + + /// Registers limitation with an |is_compatible| functor. + void RegisterLimitation(std::function + is_compatible) { + limitations_.push_back(is_compatible); + } + + bool CheckLimitations(const ValidationState_t& _, const Function* entry_point, + std::string* reason) const; + + /// Returns true if the given execution model passes the limitations stored in + /// execution_model_limitations_. Returns false otherwise and fills optional + /// |reason| parameter. + bool IsCompatibleWithExecutionModel(SpvExecutionModel model, + std::string* reason = nullptr) const; + + // Inserts id to the set of functions called from this function. + void AddFunctionCallTarget(uint32_t call_target_id) { + function_call_targets_.insert(call_target_id); + } + + // Returns a set with ids of all functions called from this function. + const std::set function_call_targets() const { + return function_call_targets_; + } + + // Returns the block containing the OpSelectionMerge or OpLoopMerge that + // references |merge_block|. + // Values of |merge_block_header_| inserted by CFGPass, so do not call before + // the first iteration of ordered instructions in + // ValidateBinaryUsingContextAndValidationState has completed. + BasicBlock* GetMergeHeader(BasicBlock* merge_block) { + return merge_block_header_[merge_block]; + } + + // Returns vector of the blocks containing a OpLoopMerge that references + // |continue_target|. + // Values of |continue_target_headers_| inserted by CFGPass, so do not call + // before the first iteration of ordered instructions in + // ValidateBinaryUsingContextAndValidationState has completed. + std::vector GetContinueHeaders(BasicBlock* continue_target) { + if (continue_target_headers_.find(continue_target) == + continue_target_headers_.end()) { + return {}; + } + return continue_target_headers_[continue_target]; + } + + private: + // Computes the representation of the augmented CFG. + // Populates augmented_successors_map_ and augmented_predecessors_map_. + void ComputeAugmentedCFG(); + + // Adds a copy of the given Construct, and tracks it by its entry block. + // Returns a reference to the stored construct. + Construct& AddConstruct(const Construct& new_construct); + + // Returns a reference to the construct corresponding to the given entry + // block. + Construct& FindConstructForEntryBlock(const BasicBlock* entry_block, + ConstructType t); + + /// The result id of the OpLabel that defined this block + uint32_t id_; + + /// The type of the function + uint32_t function_type_id_; + + /// The type of the return value + uint32_t result_type_id_; + + /// The control fo the funciton + SpvFunctionControlMask function_control_; + + /// The type of declaration of each function + FunctionDecl declaration_type_; + + // Have we finished parsing this function? + bool end_has_been_registered_; + + /// The blocks in the function mapped by block ID + std::unordered_map blocks_; + + /// A list of blocks in the order they appeared in the binary + std::vector ordered_blocks_; + + /// Blocks which are forward referenced by blocks but not defined + std::unordered_set undefined_blocks_; + + /// The block that is currently being parsed + BasicBlock* current_block_; + + /// A pseudo entry node used in dominance analysis. + /// After the function end has been registered, the successor list of the + /// pseudo entry node is the minimal set of nodes such that all nodes in the + /// CFG can be reached by following successor lists. That is, the successors + /// will be: + /// - Any basic block without predecessors. This includes the entry + /// block to the function. + /// - A single node from each otherwise unreachable cycle in the CFG, if + /// such cycles exist. + /// The pseudo entry node does not appear in the predecessor or successor + /// list of any ordinary block. + /// It has no predecessors. + /// It has Id 0. + BasicBlock pseudo_entry_block_; + + /// A pseudo exit block used in dominance analysis. + /// After the function end has been registered, the predecessor list of the + /// pseudo exit node is the minimal set of nodes such that all nodes in the + /// CFG can be reached by following predecessor lists. That is, the + /// predecessors will be: + /// - Any basic block without successors. This includes any basic block + /// ending with an OpReturn, OpReturnValue or similar instructions. + /// - A single node from each otherwise unreachable cycle in the CFG, if + /// such cycles exist. + /// The pseudo exit node does not appear in the predecessor or successor + /// list of any ordinary block. + /// It has no successors. + BasicBlock pseudo_exit_block_; + + // Maps a block to its successors in the augmented CFG, if that set is + // different from its successors in the ordinary CFG. + std::unordered_map> + augmented_successors_map_; + // Maps a block to its predecessors in the augmented CFG, if that set is + // different from its predecessors in the ordinary CFG. + std::unordered_map> + augmented_predecessors_map_; + + // Maps a structured loop header to its CFG successors and also its + // continue target if that continue target is not the loop header + // itself. This might have duplicates. + std::unordered_map> + loop_header_successors_plus_continue_target_map_; + + /// The constructs that are available in this function + std::list cfg_constructs_; + + /// The variable IDs of the functions + std::vector variable_ids_; + + /// The function parameter ids of the functions + std::vector parameter_ids_; + + /// Maps a construct's entry block to the construct(s). + /// Since a basic block may be the entry block of different types of + /// constructs, the type of the construct should also be specified in order to + /// get the unique construct. + std::unordered_map, Construct*, + bb_constr_type_pair_hash> + entry_block_to_construct_; + + /// This map provides the header block for a given merge block. + std::unordered_map merge_block_header_; + + /// This map provides the header blocks for a given continue target. + std::unordered_map> + continue_target_headers_; + + /// Stores the control flow nesting depth of a given basic block + std::unordered_map block_depth_; + + /// Stores execution model limitations imposed by instructions used within the + /// function. The functor stored in the list return true if execution model + /// is compatible, false otherwise. If the functor returns false, it can also + /// optionally fill the string parameter with the reason for incompatibility. + std::list> + execution_model_limitations_; + + /// Stores limitations imposed by instructions used within the function. + /// Similar to execution_model_limitations_; + std::list> + limitations_; + + /// Stores ids of all functions called from this function. + std::set function_call_targets_; +}; + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_FUNCTION_H_ diff --git a/third_party/spirv-tools/source/val/instruction.cpp b/third_party/spirv-tools/source/val/instruction.cpp new file mode 100644 index 0000000..b915589 --- /dev/null +++ b/third_party/spirv-tools/source/val/instruction.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/instruction.h" + +#include + +namespace spvtools { +namespace val { + +Instruction::Instruction(const spv_parsed_instruction_t* inst) + : words_(inst->words, inst->words + inst->num_words), + operands_(inst->operands, inst->operands + inst->num_operands), + inst_({words_.data(), inst->num_words, inst->opcode, inst->ext_inst_type, + inst->type_id, inst->result_id, operands_.data(), + inst->num_operands}) {} + +void Instruction::RegisterUse(const Instruction* inst, uint32_t index) { + uses_.push_back(std::make_pair(inst, index)); +} + +bool operator<(const Instruction& lhs, const Instruction& rhs) { + return lhs.id() < rhs.id(); +} +bool operator<(const Instruction& lhs, uint32_t rhs) { return lhs.id() < rhs; } +bool operator==(const Instruction& lhs, const Instruction& rhs) { + return lhs.id() == rhs.id(); +} +bool operator==(const Instruction& lhs, uint32_t rhs) { + return lhs.id() == rhs; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/instruction.h b/third_party/spirv-tools/source/val/instruction.h new file mode 100644 index 0000000..617cb06 --- /dev/null +++ b/third_party/spirv-tools/source/val/instruction.h @@ -0,0 +1,152 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_INSTRUCTION_H_ +#define SOURCE_VAL_INSTRUCTION_H_ + +#include +#include +#include +#include +#include + +#include "source/ext_inst.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +class BasicBlock; +class Function; + +/// Wraps the spv_parsed_instruction struct along with use and definition of the +/// instruction's result id +class Instruction { + public: + explicit Instruction(const spv_parsed_instruction_t* inst); + + /// Registers the use of the Instruction in instruction \p inst at \p index + void RegisterUse(const Instruction* inst, uint32_t index); + + uint32_t id() const { return inst_.result_id; } + uint32_t type_id() const { return inst_.type_id; } + SpvOp opcode() const { return static_cast(inst_.opcode); } + + /// Returns the Function where the instruction was defined. nullptr if it was + /// defined outside of a Function + const Function* function() const { return function_; } + void set_function(Function* func) { function_ = func; } + + /// Returns the BasicBlock where the instruction was defined. nullptr if it + /// was defined outside of a BasicBlock + const BasicBlock* block() const { return block_; } + void set_block(BasicBlock* b) { block_ = b; } + + /// Returns a vector of pairs of all references to this instruction's result + /// id. The first element is the instruction in which this result id was + /// referenced and the second is the index of the word in that instruction + /// where this result id appeared + const std::vector>& uses() const { + return uses_; + } + + /// The word used to define the Instruction + uint32_t word(size_t index) const { return words_[index]; } + + /// The words used to define the Instruction + const std::vector& words() const { return words_; } + + /// Returns the operand at |idx|. + const spv_parsed_operand_t& operand(size_t idx) const { + return operands_[idx]; + } + + /// The operands of the Instruction + const std::vector& operands() const { + return operands_; + } + + /// Provides direct access to the stored C instruction object. + const spv_parsed_instruction_t& c_inst() const { return inst_; } + + /// Provides direct access to instructions spv_ext_inst_type_t object. + const spv_ext_inst_type_t& ext_inst_type() const { + return inst_.ext_inst_type; + } + + bool IsNonSemantic() const { + return opcode() == SpvOp::SpvOpExtInst && + spvExtInstIsNonSemantic(inst_.ext_inst_type); + } + + /// True if this is an OpExtInst for debug info extension. + bool IsDebugInfo() const { + return opcode() == SpvOp::SpvOpExtInst && + spvExtInstIsDebugInfo(inst_.ext_inst_type); + } + + // Casts the words belonging to the operand under |index| to |T| and returns. + template + T GetOperandAs(size_t index) const { + const spv_parsed_operand_t& o = operands_.at(index); + assert(o.num_words * 4 >= sizeof(T)); + assert(o.offset + o.num_words <= inst_.num_words); + return *reinterpret_cast(&words_[o.offset]); + } + + size_t LineNum() const { return line_num_; } + void SetLineNum(size_t pos) { line_num_ = pos; } + + private: + const std::vector words_; + const std::vector operands_; + spv_parsed_instruction_t inst_; + size_t line_num_ = 0; + + /// The function in which this instruction was declared + Function* function_ = nullptr; + + /// The basic block in which this instruction was declared + BasicBlock* block_ = nullptr; + + /// This is a vector of pairs of all references to this instruction's result + /// id. The first element is the instruction in which this result id was + /// referenced and the second is the index of the word in the referencing + /// instruction where this instruction appeared + std::vector> uses_; +}; + +bool operator<(const Instruction& lhs, const Instruction& rhs); +bool operator<(const Instruction& lhs, uint32_t rhs); +bool operator==(const Instruction& lhs, const Instruction& rhs); +bool operator==(const Instruction& lhs, uint32_t rhs); + +} // namespace val +} // namespace spvtools + +// custom specialization of std::hash for Instruction +namespace std { +template <> +struct hash { + typedef spvtools::val::Instruction argument_type; + typedef std::size_t result_type; + result_type operator()(const argument_type& inst) const { + return hash()(inst.id()); + } +}; + +} // namespace std + +#endif // SOURCE_VAL_INSTRUCTION_H_ diff --git a/third_party/spirv-tools/source/val/validate.cpp b/third_party/spirv-tools/source/val/validate.cpp new file mode 100644 index 0000000..d6e992b --- /dev/null +++ b/third_party/spirv-tools/source/val/validate.cpp @@ -0,0 +1,518 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/instruction.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "source/spirv_endian.h" +#include "source/spirv_target_env.h" +#include "source/spirv_validator_options.h" +#include "source/val/construct.h" +#include "source/val/function.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" +#include "spirv-tools/libspirv.h" + +namespace { +// TODO(issue 1950): The validator only returns a single message anyway, so no +// point in generating more than 1 warning. +static uint32_t kDefaultMaxNumOfWarnings = 1; +} // namespace + +namespace spvtools { +namespace val { +namespace { + +// Parses OpExtension instruction and registers extension. +void RegisterExtension(ValidationState_t& _, + const spv_parsed_instruction_t* inst) { + const std::string extension_str = spvtools::GetExtensionString(inst); + Extension extension; + if (!GetExtensionFromString(extension_str.c_str(), &extension)) { + // The error will be logged in the ProcessInstruction pass. + return; + } + + _.RegisterExtension(extension); +} + +// Parses the beginning of the module searching for OpExtension instructions. +// Registers extensions if recognized. Returns SPV_REQUESTED_TERMINATION +// once an instruction which is not SpvOpCapability and SpvOpExtension is +// encountered. According to the SPIR-V spec extensions are declared after +// capabilities and before everything else. +spv_result_t ProcessExtensions(void* user_data, + const spv_parsed_instruction_t* inst) { + const SpvOp opcode = static_cast(inst->opcode); + if (opcode == SpvOpCapability) return SPV_SUCCESS; + + if (opcode == SpvOpExtension) { + ValidationState_t& _ = *(reinterpret_cast(user_data)); + RegisterExtension(_, inst); + return SPV_SUCCESS; + } + + // OpExtension block is finished, requesting termination. + return SPV_REQUESTED_TERMINATION; +} + +spv_result_t ProcessInstruction(void* user_data, + const spv_parsed_instruction_t* inst) { + ValidationState_t& _ = *(reinterpret_cast(user_data)); + + auto* instruction = _.AddOrderedInstruction(inst); + _.RegisterDebugInstruction(instruction); + + return SPV_SUCCESS; +} + +spv_result_t ValidateForwardDecls(ValidationState_t& _) { + if (_.unresolved_forward_id_count() == 0) return SPV_SUCCESS; + + std::stringstream ss; + std::vector ids = _.UnresolvedForwardIds(); + + std::transform( + std::begin(ids), std::end(ids), + std::ostream_iterator(ss, " "), + bind(&ValidationState_t::getIdName, std::ref(_), std::placeholders::_1)); + + auto id_str = ss.str(); + return _.diag(SPV_ERROR_INVALID_ID, nullptr) + << "The following forward referenced IDs have not been defined:\n" + << id_str.substr(0, id_str.size() - 1); +} + +std::vector CalculateNamesForEntryPoint(ValidationState_t& _, + const uint32_t id) { + auto id_descriptions = _.entry_point_descriptions(id); + auto id_names = std::vector(); + id_names.reserve((id_descriptions.size())); + + for (auto description : id_descriptions) id_names.push_back(description.name); + + return id_names; +} + +spv_result_t ValidateEntryPointNameUnique(ValidationState_t& _, + const uint32_t id) { + auto id_names = CalculateNamesForEntryPoint(_, id); + const auto names = + std::unordered_set(id_names.begin(), id_names.end()); + + if (id_names.size() != names.size()) { + std::sort(id_names.begin(), id_names.end()); + for (size_t i = 0; i < id_names.size() - 1; i++) { + if (id_names[i] == id_names[i + 1]) { + return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id)) + << "Entry point name \"" << id_names[i] + << "\" is not unique, which is not allow in WebGPU env."; + } + } + } + + for (const auto other_id : _.entry_points()) { + if (other_id == id) continue; + const auto other_id_names = CalculateNamesForEntryPoint(_, other_id); + for (const auto& other_id_name : other_id_names) { + if (names.find(other_id_name) != names.end()) { + return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(id)) + << "Entry point name \"" << other_id_name + << "\" is not unique, which is not allow in WebGPU env."; + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateEntryPointNamesUnique(ValidationState_t& _) { + for (const auto id : _.entry_points()) { + auto result = ValidateEntryPointNameUnique(_, id); + if (result != SPV_SUCCESS) return result; + } + return SPV_SUCCESS; +} + +// Entry point validation. Based on 2.16.1 (Universal Validation Rules) of the +// SPIRV spec: +// * There is at least one OpEntryPoint instruction, unless the Linkage +// capability is being used. +// * No function can be targeted by both an OpEntryPoint instruction and an +// OpFunctionCall instruction. +// +// Additionally enforces that entry points for Vulkan and WebGPU should not have +// recursion. And that entry names should be unique for WebGPU. +spv_result_t ValidateEntryPoints(ValidationState_t& _) { + _.ComputeFunctionToEntryPointMapping(); + _.ComputeRecursiveEntryPoints(); + + if (_.entry_points().empty() && !_.HasCapability(SpvCapabilityLinkage)) { + return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) + << "No OpEntryPoint instruction was found. This is only allowed if " + "the Linkage capability is being used."; + } + + for (const auto& entry_point : _.entry_points()) { + if (_.IsFunctionCallTarget(entry_point)) { + return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(entry_point)) + << "A function (" << entry_point + << ") may not be targeted by both an OpEntryPoint instruction and " + "an OpFunctionCall instruction."; + } + + // For Vulkan and WebGPU, the static function-call graph for an entry point + // must not contain cycles. + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (_.recursive_entry_points().find(entry_point) != + _.recursive_entry_points().end()) { + return _.diag(SPV_ERROR_INVALID_BINARY, _.FindDef(entry_point)) + << "Entry points may not have a call graph with cycles."; + } + } + + // For WebGPU all entry point names must be unique. + if (spvIsWebGPUEnv(_.context()->target_env)) { + const auto result = ValidateEntryPointNamesUnique(_); + if (result != SPV_SUCCESS) return result; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateBinaryUsingContextAndValidationState( + const spv_context_t& context, const uint32_t* words, const size_t num_words, + spv_diagnostic* pDiagnostic, ValidationState_t* vstate) { + auto binary = std::unique_ptr( + new spv_const_binary_t{words, num_words}); + + spv_endianness_t endian; + spv_position_t position = {}; + if (spvBinaryEndianness(binary.get(), &endian)) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "Invalid SPIR-V magic number."; + } + + if (spvIsWebGPUEnv(context.target_env) && endian != SPV_ENDIANNESS_LITTLE) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "WebGPU requires SPIR-V to be little endian."; + } + + spv_header_t header; + if (spvBinaryHeaderGet(binary.get(), endian, &header)) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "Invalid SPIR-V header."; + } + + if (header.version > spvVersionForTargetEnv(context.target_env)) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_WRONG_VERSION) + << "Invalid SPIR-V binary version " + << SPV_SPIRV_VERSION_MAJOR_PART(header.version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(header.version) + << " for target environment " + << spvTargetEnvDescription(context.target_env) << "."; + } + + if (header.bound > vstate->options()->universal_limits_.max_id_bound) { + return DiagnosticStream(position, context.consumer, "", + SPV_ERROR_INVALID_BINARY) + << "Invalid SPIR-V. The id bound is larger than the max id bound " + << vstate->options()->universal_limits_.max_id_bound << "."; + } + + // Look for OpExtension instructions and register extensions. + // This parse should not produce any error messages. Hijack the context and + // replace the message consumer so that we do not pollute any state in input + // consumer. + spv_context_t hijacked_context = context; + hijacked_context.consumer = [](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}; + spvBinaryParse(&hijacked_context, vstate, words, num_words, + /* parsed_header = */ nullptr, ProcessExtensions, + /* diagnostic = */ nullptr); + + // Parse the module and perform inline validation checks. These checks do + // not require the the knowledge of the whole module. + if (auto error = spvBinaryParse(&context, vstate, words, num_words, + /*parsed_header =*/nullptr, + ProcessInstruction, pDiagnostic)) { + return error; + } + + std::vector visited_entry_points; + for (auto& instruction : vstate->ordered_instructions()) { + { + // In order to do this work outside of Process Instruction we need to be + // able to, briefly, de-const the instruction. + Instruction* inst = const_cast(&instruction); + + if (inst->opcode() == SpvOpEntryPoint) { + const auto entry_point = inst->GetOperandAs(1); + const auto execution_model = inst->GetOperandAs(0); + const char* str = reinterpret_cast( + inst->words().data() + inst->operand(2).offset); + const std::string desc_name(str); + + ValidationState_t::EntryPointDescription desc; + desc.name = desc_name; + + std::vector interfaces; + for (size_t j = 3; j < inst->operands().size(); ++j) + desc.interfaces.push_back(inst->word(inst->operand(j).offset)); + + vstate->RegisterEntryPoint(entry_point, execution_model, + std::move(desc)); + + if (visited_entry_points.size() > 0) { + for (const Instruction* check_inst : visited_entry_points) { + const auto check_execution_model = + check_inst->GetOperandAs(0); + const char* check_str = reinterpret_cast( + check_inst->words().data() + inst->operand(2).offset); + const std::string check_name(check_str); + + if (desc_name == check_name && + execution_model == check_execution_model) { + return vstate->diag(SPV_ERROR_INVALID_DATA, inst) + << "2 Entry points cannot share the same name and " + "ExecutionMode."; + } + } + } + visited_entry_points.push_back(inst); + } + if (inst->opcode() == SpvOpFunctionCall) { + if (!vstate->in_function_body()) { + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction) + << "A FunctionCall must happen within a function body."; + } + + const auto called_id = inst->GetOperandAs(2); + if (spvIsWebGPUEnv(context.target_env) && + !vstate->IsFunctionCallDefined(called_id)) { + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, &instruction) + << "For WebGPU, functions need to be defined before being " + "called."; + } + + vstate->AddFunctionCallTarget(called_id); + } + + if (vstate->in_function_body()) { + inst->set_function(&(vstate->current_function())); + inst->set_block(vstate->current_function().current_block()); + + if (vstate->in_block() && spvOpcodeIsBlockTerminator(inst->opcode())) { + vstate->current_function().current_block()->set_terminator(inst); + } + } + + if (auto error = IdPass(*vstate, inst)) return error; + } + + if (auto error = CapabilityPass(*vstate, &instruction)) return error; + if (auto error = ModuleLayoutPass(*vstate, &instruction)) return error; + if (auto error = CfgPass(*vstate, &instruction)) return error; + if (auto error = InstructionPass(*vstate, &instruction)) return error; + + // Now that all of the checks are done, update the state. + { + Instruction* inst = const_cast(&instruction); + vstate->RegisterInstruction(inst); + if (inst->opcode() == SpvOpTypeForwardPointer) { + vstate->RegisterForwardPointer(inst->GetOperandAs(0)); + } + } + } + + if (!vstate->has_memory_model_specified()) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << "Missing required OpMemoryModel instruction."; + + if (vstate->in_function_body()) + return vstate->diag(SPV_ERROR_INVALID_LAYOUT, nullptr) + << "Missing OpFunctionEnd at end of module."; + + // Catch undefined forward references before performing further checks. + if (auto error = ValidateForwardDecls(*vstate)) return error; + + // Calculate reachability after all the blocks are parsed, but early that it + // can be relied on in subsequent pases. + ReachabilityPass(*vstate); + + // ID usage needs be handled in its own iteration of the instructions, + // between the two others. It depends on the first loop to have been + // finished, so that all instructions have been registered. And the following + // loop depends on all of the usage data being populated. Thus it cannot live + // in either of those iterations. + // It should also live after the forward declaration check, since it will + // have problems with missing forward declarations, but give less useful error + // messages. + for (size_t i = 0; i < vstate->ordered_instructions().size(); ++i) { + auto& instruction = vstate->ordered_instructions()[i]; + if (auto error = UpdateIdUse(*vstate, &instruction)) return error; + } + + // Validate individual opcodes. + for (size_t i = 0; i < vstate->ordered_instructions().size(); ++i) { + auto& instruction = vstate->ordered_instructions()[i]; + + // Keep these passes in the order they appear in the SPIR-V specification + // sections to maintain test consistency. + if (auto error = MiscPass(*vstate, &instruction)) return error; + if (auto error = DebugPass(*vstate, &instruction)) return error; + if (auto error = AnnotationPass(*vstate, &instruction)) return error; + if (auto error = ExtensionPass(*vstate, &instruction)) return error; + if (auto error = ModeSettingPass(*vstate, &instruction)) return error; + if (auto error = TypePass(*vstate, &instruction)) return error; + if (auto error = ConstantPass(*vstate, &instruction)) return error; + if (auto error = MemoryPass(*vstate, &instruction)) return error; + if (auto error = FunctionPass(*vstate, &instruction)) return error; + if (auto error = ImagePass(*vstate, &instruction)) return error; + if (auto error = ConversionPass(*vstate, &instruction)) return error; + if (auto error = CompositesPass(*vstate, &instruction)) return error; + if (auto error = ArithmeticsPass(*vstate, &instruction)) return error; + if (auto error = BitwisePass(*vstate, &instruction)) return error; + if (auto error = LogicalsPass(*vstate, &instruction)) return error; + if (auto error = ControlFlowPass(*vstate, &instruction)) return error; + if (auto error = DerivativesPass(*vstate, &instruction)) return error; + if (auto error = AtomicsPass(*vstate, &instruction)) return error; + if (auto error = PrimitivesPass(*vstate, &instruction)) return error; + if (auto error = BarriersPass(*vstate, &instruction)) return error; + // Group + // Device-Side Enqueue + // Pipe + if (auto error = NonUniformPass(*vstate, &instruction)) return error; + + if (auto error = LiteralsPass(*vstate, &instruction)) return error; + } + + // Validate the preconditions involving adjacent instructions. e.g. SpvOpPhi + // must only be preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine. + if (auto error = ValidateAdjacency(*vstate)) return error; + + if (auto error = ValidateEntryPoints(*vstate)) return error; + // CFG checks are performed after the binary has been parsed + // and the CFGPass has collected information about the control flow + if (auto error = PerformCfgChecks(*vstate)) return error; + if (auto error = CheckIdDefinitionDominateUse(*vstate)) return error; + if (auto error = ValidateDecorations(*vstate)) return error; + if (auto error = ValidateInterfaces(*vstate)) return error; + // TODO(dsinclair): Restructure ValidateBuiltins so we can move into the + // for() above as it loops over all ordered_instructions internally. + if (auto error = ValidateBuiltIns(*vstate)) return error; + // These checks must be performed after individual opcode checks because + // those checks register the limitation checked here. + for (const auto& inst : vstate->ordered_instructions()) { + if (auto error = ValidateExecutionLimitations(*vstate, &inst)) return error; + if (auto error = ValidateSmallTypeUses(*vstate, &inst)) return error; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ValidateBinaryAndKeepValidationState( + const spv_const_context context, spv_const_validator_options options, + const uint32_t* words, const size_t num_words, spv_diagnostic* pDiagnostic, + std::unique_ptr* vstate) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + vstate->reset(new ValidationState_t(&hijack_context, options, words, + num_words, kDefaultMaxNumOfWarnings)); + + return ValidateBinaryUsingContextAndValidationState( + hijack_context, words, num_words, pDiagnostic, vstate->get()); +} + +} // namespace val +} // namespace spvtools + +spv_result_t spvValidate(const spv_const_context context, + const spv_const_binary binary, + spv_diagnostic* pDiagnostic) { + return spvValidateBinary(context, binary->code, binary->wordCount, + pDiagnostic); +} + +spv_result_t spvValidateBinary(const spv_const_context context, + const uint32_t* words, const size_t num_words, + spv_diagnostic* pDiagnostic) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + // This interface is used for default command line options. + spv_validator_options default_options = spvValidatorOptionsCreate(); + + // Create the ValidationState using the context and default options. + spvtools::val::ValidationState_t vstate(&hijack_context, default_options, + words, num_words, + kDefaultMaxNumOfWarnings); + + spv_result_t result = + spvtools::val::ValidateBinaryUsingContextAndValidationState( + hijack_context, words, num_words, pDiagnostic, &vstate); + + spvValidatorOptionsDestroy(default_options); + return result; +} + +spv_result_t spvValidateWithOptions(const spv_const_context context, + spv_const_validator_options options, + const spv_const_binary binary, + spv_diagnostic* pDiagnostic) { + spv_context_t hijack_context = *context; + if (pDiagnostic) { + *pDiagnostic = nullptr; + spvtools::UseDiagnosticAsMessageConsumer(&hijack_context, pDiagnostic); + } + + // Create the ValidationState using the context. + spvtools::val::ValidationState_t vstate(&hijack_context, options, + binary->code, binary->wordCount, + kDefaultMaxNumOfWarnings); + + return spvtools::val::ValidateBinaryUsingContextAndValidationState( + hijack_context, binary->code, binary->wordCount, pDiagnostic, &vstate); +} diff --git a/third_party/spirv-tools/source/val/validate.h b/third_party/spirv-tools/source/val/validate.h new file mode 100644 index 0000000..3fc183d --- /dev/null +++ b/third_party/spirv-tools/source/val/validate.h @@ -0,0 +1,242 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_VALIDATE_H_ +#define SOURCE_VAL_VALIDATE_H_ + +#include +#include +#include +#include + +#include "source/instruction.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +class ValidationState_t; +class BasicBlock; +class Instruction; + +/// A function that returns a vector of BasicBlocks given a BasicBlock. Used to +/// get the successor and predecessor nodes of a CFG block +using get_blocks_func = + std::function*(const BasicBlock*)>; + +/// @brief Performs the Control Flow Graph checks +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_CFG otherwise +spv_result_t PerformCfgChecks(ValidationState_t& _); + +/// @brief Updates the use vectors of all instructions that can be referenced +/// +/// This function will update the vector which define where an instruction was +/// referenced in the binary. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. +spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst); + +/// @brief This function checks all ID definitions dominate their use in the +/// CFG. +/// +/// This function will iterate over all ID definitions that are defined in the +/// functions of a module and make sure that the definitions appear in a +/// block that dominates their use. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_ID otherwise +spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _); + +/// @brief This function checks for preconditions involving the adjacent +/// instructions. +/// +/// This function will iterate over all instructions and check for any required +/// predecessor and/or successor instructions. e.g. SpvOpPhi must only be +/// preceeded by SpvOpLabel, SpvOpPhi, or SpvOpLine. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. SPV_ERROR_INVALID_DATA otherwise +spv_result_t ValidateAdjacency(ValidationState_t& _); + +/// @brief Validates static uses of input and output variables +/// +/// Checks that any entry point that uses a input or output variable lists that +/// variable in its interface. +/// +/// @param[in] _ the validation state of the module +/// +/// @return SPV_SUCCESS if no errors are found. +spv_result_t ValidateInterfaces(ValidationState_t& _); + +/// @brief Validates memory instructions +/// +/// @param[in] _ the validation state of the module +/// @return SPV_SUCCESS if no errors are found. +spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst); + +/// @brief Updates the immediate dominator for each of the block edges +/// +/// Updates the immediate dominator of the blocks for each of the edges +/// provided by the @p dom_edges parameter +/// +/// @param[in,out] dom_edges The edges of the dominator tree +/// @param[in] set_func This function will be called to updated the Immediate +/// dominator +void UpdateImmediateDominators( + const std::vector>& dom_edges, + std::function set_func); + +/// @brief Prints all of the dominators of a BasicBlock +/// +/// @param[in] block The dominators of this block will be printed +void printDominatorList(BasicBlock& block); + +/// Performs logical layout validation as described in section 2.4 of the SPIR-V +/// spec. +spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst); + +/// Performs Control Flow Graph validation and construction. +spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst); + +/// Validates Control Flow Graph instructions. +spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst); + +/// Performs Id and SSA validation of a module +spv_result_t IdPass(ValidationState_t& _, Instruction* inst); + +/// Performs instruction validation. +spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst); + +/// Performs decoration validation. Assumes each decoration on a group +/// has been propagated down to the group members. +spv_result_t ValidateDecorations(ValidationState_t& _); + +/// Performs validation of built-in variables. +spv_result_t ValidateBuiltIns(ValidationState_t& _); + +/// Validates type instructions. +spv_result_t TypePass(ValidationState_t& _, const Instruction* inst); + +/// Validates constant instructions. +spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of arithmetic instructions. +spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of composite instructions. +spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of conversion instructions. +spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of derivative instructions. +spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of logical instructions. +spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of bitwise instructions. +spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of image instructions. +spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of atomic instructions. +spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of barrier instructions. +spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of literal numbers. +spv_result_t LiteralsPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of extension instructions. +spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of annotation instructions. +spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of non-uniform group instructions. +spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of debug instructions. +spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst); + +// Validates that capability declarations use operands allowed in the current +// context. +spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of primitive instructions. +spv_result_t PrimitivesPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of mode setting instructions. +spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of function instructions. +spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst); + +/// Validates correctness of miscellaneous instructions. +spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst); + +/// Calculates the reachability of basic blocks. +void ReachabilityPass(ValidationState_t& _); + +/// Validates execution limitations. +/// +/// Verifies execution models are allowed for all functionality they contain. +spv_result_t ValidateExecutionLimitations(ValidationState_t& _, + const Instruction* inst); + +/// Validates restricted uses of 8- and 16-bit types. +/// +/// Validates shaders that uses 8- or 16-bit storage capabilities, but not full +/// capabilities only have appropriate uses of those types. +spv_result_t ValidateSmallTypeUses(ValidationState_t& _, + const Instruction* inst); + +/// @brief Validate the ID's within a SPIR-V binary +/// +/// @param[in] pInstructions array of instructions +/// @param[in] count number of elements in instruction array +/// @param[in] bound the binary header +/// @param[in,out] position current word in the binary +/// @param[in] consumer message consumer callback +/// +/// @return result code +spv_result_t spvValidateIDs(const spv_instruction_t* pInstructions, + const uint64_t count, const uint32_t bound, + spv_position position, + const MessageConsumer& consumer); + +// Performs validation for the SPIRV-V module binary. +// The main difference between this API and spvValidateBinary is that the +// "Validation State" is not destroyed upon function return; it lives on and is +// pointed to by the vstate unique_ptr. +spv_result_t ValidateBinaryAndKeepValidationState( + const spv_const_context context, spv_const_validator_options options, + const uint32_t* words, const size_t num_words, spv_diagnostic* pDiagnostic, + std::unique_ptr* vstate); + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_VALIDATE_H_ diff --git a/third_party/spirv-tools/source/val/validate_adjacency.cpp b/third_party/spirv-tools/source/val/validate_adjacency.cpp new file mode 100644 index 0000000..64655b0 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_adjacency.cpp @@ -0,0 +1,128 @@ +// Copyright (c) 2018 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of the intra-block preconditions of SPIR-V +// instructions. + +#include "source/val/validate.h" + +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +enum { + // Status right after meeting OpFunction. + IN_NEW_FUNCTION, + // Status right after meeting the entry block. + IN_ENTRY_BLOCK, + // Status right after meeting non-entry blocks. + PHI_VALID, + // Status right after meeting non-OpVariable instructions in the entry block + // or non-OpPhi instructions in non-entry blocks, except OpLine. + PHI_AND_VAR_INVALID, +}; + +spv_result_t ValidateAdjacency(ValidationState_t& _) { + const auto& instructions = _.ordered_instructions(); + int adjacency_status = PHI_AND_VAR_INVALID; + + for (size_t i = 0; i < instructions.size(); ++i) { + const auto& inst = instructions[i]; + switch (inst.opcode()) { + case SpvOpFunction: + case SpvOpFunctionParameter: + adjacency_status = IN_NEW_FUNCTION; + break; + case SpvOpLabel: + adjacency_status = + adjacency_status == IN_NEW_FUNCTION ? IN_ENTRY_BLOCK : PHI_VALID; + break; + case SpvOpExtInst: + // If it is a debug info instruction, we do not change the status to + // allow debug info instructions before OpVariable in a function. + // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need + // to discuss the location of DebugScope, DebugNoScope, DebugDeclare, + // and DebugValue. + if (!spvExtInstIsDebugInfo(inst.ext_inst_type())) { + adjacency_status = PHI_AND_VAR_INVALID; + } + break; + case SpvOpPhi: + if (adjacency_status != PHI_VALID) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "OpPhi must appear within a non-entry block before all " + << "non-OpPhi instructions " + << "(except for OpLine, which can be mixed with OpPhi)."; + } + break; + case SpvOpLine: + case SpvOpNoLine: + break; + case SpvOpLoopMerge: + adjacency_status = PHI_AND_VAR_INVALID; + if (i != (instructions.size() - 1)) { + switch (instructions[i + 1].opcode()) { + case SpvOpBranch: + case SpvOpBranchConditional: + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "OpLoopMerge must immediately precede either an " + << "OpBranch or OpBranchConditional instruction. " + << "OpLoopMerge must be the second-to-last instruction in " + << "its block."; + } + } + break; + case SpvOpSelectionMerge: + adjacency_status = PHI_AND_VAR_INVALID; + if (i != (instructions.size() - 1)) { + switch (instructions[i + 1].opcode()) { + case SpvOpBranchConditional: + case SpvOpSwitch: + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "OpSelectionMerge must immediately precede either an " + << "OpBranchConditional or OpSwitch instruction. " + << "OpSelectionMerge must be the second-to-last " + << "instruction in its block."; + } + } + break; + case SpvOpVariable: + if (inst.GetOperandAs(2) == SpvStorageClassFunction && + adjacency_status != IN_ENTRY_BLOCK) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "All OpVariable instructions in a function must be the " + "first instructions in the first block."; + } + break; + default: + adjacency_status = PHI_AND_VAR_INVALID; + break; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_annotation.cpp b/third_party/spirv-tools/source/val/validate_annotation.cpp new file mode 100644 index 0000000..df38f1b --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_annotation.cpp @@ -0,0 +1,480 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +bool IsValidWebGPUDecoration(uint32_t decoration) { + switch (decoration) { + case SpvDecorationSpecId: + case SpvDecorationBlock: + case SpvDecorationRowMajor: + case SpvDecorationColMajor: + case SpvDecorationArrayStride: + case SpvDecorationMatrixStride: + case SpvDecorationBuiltIn: + case SpvDecorationNoPerspective: + case SpvDecorationFlat: + case SpvDecorationCentroid: + case SpvDecorationRestrict: + case SpvDecorationAliased: + case SpvDecorationNonWritable: + case SpvDecorationNonReadable: + case SpvDecorationUniform: + case SpvDecorationLocation: + case SpvDecorationComponent: + case SpvDecorationIndex: + case SpvDecorationBinding: + case SpvDecorationDescriptorSet: + case SpvDecorationOffset: + case SpvDecorationNoContraction: + return true; + default: + return false; + } +} + +std::string LogStringForDecoration(uint32_t decoration) { + switch (decoration) { + case SpvDecorationRelaxedPrecision: + return "RelaxedPrecision"; + case SpvDecorationSpecId: + return "SpecId"; + case SpvDecorationBlock: + return "Block"; + case SpvDecorationBufferBlock: + return "BufferBlock"; + case SpvDecorationRowMajor: + return "RowMajor"; + case SpvDecorationColMajor: + return "ColMajor"; + case SpvDecorationArrayStride: + return "ArrayStride"; + case SpvDecorationMatrixStride: + return "MatrixStride"; + case SpvDecorationGLSLShared: + return "GLSLShared"; + case SpvDecorationGLSLPacked: + return "GLSLPacked"; + case SpvDecorationCPacked: + return "CPacked"; + case SpvDecorationBuiltIn: + return "BuiltIn"; + case SpvDecorationNoPerspective: + return "NoPerspective"; + case SpvDecorationFlat: + return "Flat"; + case SpvDecorationPatch: + return "Patch"; + case SpvDecorationCentroid: + return "Centroid"; + case SpvDecorationSample: + return "Sample"; + case SpvDecorationInvariant: + return "Invariant"; + case SpvDecorationRestrict: + return "Restrict"; + case SpvDecorationAliased: + return "Aliased"; + case SpvDecorationVolatile: + return "Volatile"; + case SpvDecorationConstant: + return "Constant"; + case SpvDecorationCoherent: + return "Coherent"; + case SpvDecorationNonWritable: + return "NonWritable"; + case SpvDecorationNonReadable: + return "NonReadable"; + case SpvDecorationUniform: + return "Uniform"; + case SpvDecorationSaturatedConversion: + return "SaturatedConversion"; + case SpvDecorationStream: + return "Stream"; + case SpvDecorationLocation: + return "Location"; + case SpvDecorationComponent: + return "Component"; + case SpvDecorationIndex: + return "Index"; + case SpvDecorationBinding: + return "Binding"; + case SpvDecorationDescriptorSet: + return "DescriptorSet"; + case SpvDecorationOffset: + return "Offset"; + case SpvDecorationXfbBuffer: + return "XfbBuffer"; + case SpvDecorationXfbStride: + return "XfbStride"; + case SpvDecorationFuncParamAttr: + return "FuncParamAttr"; + case SpvDecorationFPRoundingMode: + return "FPRoundingMode"; + case SpvDecorationFPFastMathMode: + return "FPFastMathMode"; + case SpvDecorationLinkageAttributes: + return "LinkageAttributes"; + case SpvDecorationNoContraction: + return "NoContraction"; + case SpvDecorationInputAttachmentIndex: + return "InputAttachmentIndex"; + case SpvDecorationAlignment: + return "Alignment"; + case SpvDecorationMaxByteOffset: + return "MaxByteOffset"; + case SpvDecorationAlignmentId: + return "AlignmentId"; + case SpvDecorationMaxByteOffsetId: + return "MaxByteOffsetId"; + case SpvDecorationNoSignedWrap: + return "NoSignedWrap"; + case SpvDecorationNoUnsignedWrap: + return "NoUnsignedWrap"; + case SpvDecorationExplicitInterpAMD: + return "ExplicitInterpAMD"; + case SpvDecorationOverrideCoverageNV: + return "OverrideCoverageNV"; + case SpvDecorationPassthroughNV: + return "PassthroughNV"; + case SpvDecorationViewportRelativeNV: + return "ViewportRelativeNV"; + case SpvDecorationSecondaryViewportRelativeNV: + return "SecondaryViewportRelativeNV"; + case SpvDecorationPerPrimitiveNV: + return "PerPrimitiveNV"; + case SpvDecorationPerViewNV: + return "PerViewNV"; + case SpvDecorationPerTaskNV: + return "PerTaskNV"; + case SpvDecorationPerVertexNV: + return "PerVertexNV"; + case SpvDecorationNonUniformEXT: + return "NonUniformEXT"; + case SpvDecorationRestrictPointerEXT: + return "RestrictPointerEXT"; + case SpvDecorationAliasedPointerEXT: + return "AliasedPointerEXT"; + case SpvDecorationHlslCounterBufferGOOGLE: + return "HlslCounterBufferGOOGLE"; + case SpvDecorationHlslSemanticGOOGLE: + return "HlslSemanticGOOGLE"; + default: + break; + } + return "Unknown"; +} + +// Returns true if the decoration takes ID parameters. +// TODO(dneto): This can be generated from the grammar. +bool DecorationTakesIdParameters(uint32_t type) { + switch (static_cast(type)) { + case SpvDecorationUniformId: + case SpvDecorationAlignmentId: + case SpvDecorationMaxByteOffsetId: + case SpvDecorationHlslCounterBufferGOOGLE: + return true; + default: + break; + } + return false; +} + +spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { + const auto decoration = inst->GetOperandAs(1); + if (decoration == SpvDecorationSpecId) { + const auto target_id = inst->GetOperandAs(0); + const auto target = _.FindDef(target_id); + if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpDecorate SpecId decoration target '" + << _.getIdName(target_id) + << "' is not a scalar specialization constant."; + } + } + + if (spvIsWebGPUEnv(_.context()->target_env) && + !IsValidWebGPUDecoration(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpDecorate decoration '" << LogStringForDecoration(decoration) + << "' is not valid for the WebGPU execution environment."; + } + + if (DecorationTakesIdParameters(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Decorations taking ID parameters may not be used with " + "OpDecorateId"; + } + // TODO: Add validations for all decorations. + return SPV_SUCCESS; +} + +spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) { + const auto decoration = inst->GetOperandAs(1); + if (!DecorationTakesIdParameters(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Decorations that don't take ID parameters may not be used with " + "OpDecorateId"; + } + // TODO: Add validations for these decorations. + // UniformId is covered elsewhere. + return SPV_SUCCESS; +} + +spv_result_t ValidateMemberDecorate(ValidationState_t& _, + const Instruction* inst) { + const auto struct_type_id = inst->GetOperandAs(0); + const auto struct_type = _.FindDef(struct_type_id); + if (!struct_type || SpvOpTypeStruct != struct_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpMemberDecorate Structure type '" + << _.getIdName(struct_type_id) << "' is not a struct type."; + } + const auto member = inst->GetOperandAs(1); + const auto member_count = + static_cast(struct_type->words().size() - 2); + if (member_count <= member) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Index " << member + << " provided in OpMemberDecorate for struct " + << _.getIdName(struct_type_id) + << " is out of bounds. The structure has " << member_count + << " members. Largest valid index is " << member_count - 1 << "."; + } + + const auto decoration = inst->GetOperandAs(2); + if (spvIsWebGPUEnv(_.context()->target_env) && + !IsValidWebGPUDecoration(decoration)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpMemberDecorate decoration '" << _.getIdName(decoration) + << "' is not valid for the WebGPU execution environment."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateDecorationGroup(ValidationState_t& _, + const Instruction* inst) { + if (spvIsWebGPUEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "OpDecorationGroup is not allowed in the WebGPU execution " + << "environment."; + } + + const auto decoration_group_id = inst->GetOperandAs(0); + const auto decoration_group = _.FindDef(decoration_group_id); + for (auto pair : decoration_group->uses()) { + auto use = pair.first; + if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate && + use->opcode() != SpvOpGroupMemberDecorate && + use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId && + !use->IsNonSemantic()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result id of OpDecorationGroup can only " + << "be targeted by OpName, OpGroupDecorate, " + << "OpDecorate, OpDecorateId, and OpGroupMemberDecorate"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupDecorate(ValidationState_t& _, + const Instruction* inst) { + if (spvIsWebGPUEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "OpGroupDecorate is not allowed in the WebGPU execution " + << "environment."; + } + + const auto decoration_group_id = inst->GetOperandAs(0); + auto decoration_group = _.FindDef(decoration_group_id); + if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpGroupDecorate Decoration group '" + << _.getIdName(decoration_group_id) + << "' is not a decoration group."; + } + for (unsigned i = 1; i < inst->operands().size(); ++i) { + auto target_id = inst->GetOperandAs(i); + auto target = _.FindDef(target_id); + if (!target || target->opcode() == SpvOpDecorationGroup) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpGroupDecorate may not target OpDecorationGroup '" + << _.getIdName(target_id) << "'"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _, + const Instruction* inst) { + if (spvIsWebGPUEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "OpGroupMemberDecorate is not allowed in the WebGPU execution " + << "environment."; + } + + const auto decoration_group_id = inst->GetOperandAs(0); + const auto decoration_group = _.FindDef(decoration_group_id); + if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpGroupMemberDecorate Decoration group '" + << _.getIdName(decoration_group_id) + << "' is not a decoration group."; + } + // Grammar checks ensures that the number of arguments to this instruction + // is an odd number: 1 decoration group + (id,literal) pairs. + for (size_t i = 1; i + 1 < inst->operands().size(); i += 2) { + const uint32_t struct_id = inst->GetOperandAs(i); + const uint32_t index = inst->GetOperandAs(i + 1); + auto struct_instr = _.FindDef(struct_id); + if (!struct_instr || SpvOpTypeStruct != struct_instr->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpGroupMemberDecorate Structure type '" + << _.getIdName(struct_id) << "' is not a struct type."; + } + const uint32_t num_struct_members = + static_cast(struct_instr->words().size() - 2); + if (index >= num_struct_members) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Index " << index + << " provided in OpGroupMemberDecorate for struct " + << _.getIdName(struct_id) + << " is out of bounds. The structure has " << num_struct_members + << " members. Largest valid index is " << num_struct_members - 1 + << "."; + } + } + return SPV_SUCCESS; +} + +// Registers necessary decoration(s) for the appropriate IDs based on the +// instruction. +spv_result_t RegisterDecorations(ValidationState_t& _, + const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpDecorate: + case SpvOpDecorateId: { + const uint32_t target_id = inst->word(1); + const SpvDecoration dec_type = static_cast(inst->word(2)); + std::vector dec_params; + if (inst->words().size() > 3) { + dec_params.insert(dec_params.end(), inst->words().begin() + 3, + inst->words().end()); + } + _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params)); + break; + } + case SpvOpMemberDecorate: { + const uint32_t struct_id = inst->word(1); + const uint32_t index = inst->word(2); + const SpvDecoration dec_type = static_cast(inst->word(3)); + std::vector dec_params; + if (inst->words().size() > 4) { + dec_params.insert(dec_params.end(), inst->words().begin() + 4, + inst->words().end()); + } + _.RegisterDecorationForId(struct_id, + Decoration(dec_type, dec_params, index)); + break; + } + case SpvOpDecorationGroup: { + // We don't need to do anything right now. Assigning decorations to groups + // will be taken care of via OpGroupDecorate. + break; + } + case SpvOpGroupDecorate: { + // Word 1 is the group . All subsequent words are target s that + // are going to be decorated with the decorations. + const uint32_t decoration_group_id = inst->word(1); + std::vector& group_decorations = + _.id_decorations(decoration_group_id); + for (size_t i = 2; i < inst->words().size(); ++i) { + const uint32_t target_id = inst->word(i); + _.RegisterDecorationsForId(target_id, group_decorations.begin(), + group_decorations.end()); + } + break; + } + case SpvOpGroupMemberDecorate: { + // Word 1 is the Decoration Group followed by (struct,literal) + // pairs. All decorations of the group should be applied to all the struct + // members that are specified in the instructions. + const uint32_t decoration_group_id = inst->word(1); + std::vector& group_decorations = + _.id_decorations(decoration_group_id); + // Grammar checks ensures that the number of arguments to this instruction + // is an odd number: 1 decoration group + (id,literal) pairs. + for (size_t i = 2; i + 1 < inst->words().size(); i = i + 2) { + const uint32_t struct_id = inst->word(i); + const uint32_t index = inst->word(i + 1); + // ID validation phase ensures this is in fact a struct instruction and + // that the index is not out of bound. + _.RegisterDecorationsForStructMember(struct_id, index, + group_decorations.begin(), + group_decorations.end()); + } + break; + } + default: + break; + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpDecorate: + if (auto error = ValidateDecorate(_, inst)) return error; + break; + case SpvOpDecorateId: + if (auto error = ValidateDecorateId(_, inst)) return error; + break; + // TODO(dneto): SpvOpDecorateStringGOOGLE + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253 + case SpvOpMemberDecorate: + if (auto error = ValidateMemberDecorate(_, inst)) return error; + break; + case SpvOpDecorationGroup: + if (auto error = ValidateDecorationGroup(_, inst)) return error; + break; + case SpvOpGroupDecorate: + if (auto error = ValidateGroupDecorate(_, inst)) return error; + break; + case SpvOpGroupMemberDecorate: + if (auto error = ValidateGroupMemberDecorate(_, inst)) return error; + break; + default: + break; + } + + // In order to validate decoration rules, we need to know all the decorations + // that are applied to any given . + RegisterDecorations(_, inst); + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_arithmetics.cpp b/third_party/spirv-tools/source/val/validate_arithmetics.cpp new file mode 100644 index 0000000..433330d --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_arithmetics.cpp @@ -0,0 +1,550 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Performs validation of arithmetic instructions. + +#include "source/val/validate.h" + +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of arithmetic instructions. +spv_result_t ArithmeticsPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case SpvOpFAdd: + case SpvOpFSub: + case SpvOpFMul: + case SpvOpFDiv: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpFNegate: { + bool supportsCoopMat = + (opcode != SpvOpFMul && opcode != SpvOpFRem && opcode != SpvOpFMod); + if (!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type) && + !(supportsCoopMat && _.IsFloatCooperativeMatrixType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected floating scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + if (_.GetOperandTypeId(inst, operand_index) != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + break; + } + + case SpvOpUDiv: + case SpvOpUMod: { + bool supportsCoopMat = (opcode == SpvOpUDiv); + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !(supportsCoopMat && + _.IsUnsignedIntCooperativeMatrixType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + if (_.GetOperandTypeId(inst, operand_index) != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to be of Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + } + break; + } + + case SpvOpISub: + case SpvOpIAdd: + case SpvOpIMul: + case SpvOpSDiv: + case SpvOpSMod: + case SpvOpSRem: + case SpvOpSNegate: { + bool supportsCoopMat = + (opcode != SpvOpIMul && opcode != SpvOpSRem && opcode != SpvOpSMod); + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t dimension = _.GetDimension(result_type); + const uint32_t bit_width = _.GetBitWidth(result_type); + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!type_id || + (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id) && + !(supportsCoopMat && _.IsIntCooperativeMatrixType(result_type)))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as operand: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + + if (_.GetDimension(type_id) != dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + + if (_.GetBitWidth(type_id) != bit_width) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected arithmetic operands to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + } + break; + } + + case SpvOpDot: { + if (!_.IsFloatScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar type as Result Type: " + << spvOpcodeString(opcode); + + uint32_t first_vector_num_components = 0; + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + + if (!type_id || !_.IsFloatVectorType(type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector as operand: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + + const uint32_t component_type = _.GetComponentType(type_id); + if (component_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component type to be equal to Result Type: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + + const uint32_t num_components = _.GetDimension(type_id); + if (operand_index == 2) { + first_vector_num_components = num_components; + } else if (num_components != first_vector_num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same number of componenets: " + << spvOpcodeString(opcode); + } + } + break; + } + + case SpvOpVectorTimesScalar: { + if (!_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); + if (result_type != vector_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector operand type to be equal to Result Type: " + << spvOpcodeString(opcode); + + const uint32_t component_type = _.GetComponentType(vector_type_id); + + const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); + if (component_type != scalar_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar operand type to be equal to the component " + << "type of the vector operand: " << spvOpcodeString(opcode); + + break; + } + + case SpvOpMatrixTimesScalar: { + if (!_.IsFloatMatrixType(result_type) && + !_.IsCooperativeMatrixType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); + if (result_type != matrix_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected matrix operand type to be equal to Result Type: " + << spvOpcodeString(opcode); + + const uint32_t component_type = _.GetComponentType(matrix_type_id); + + const uint32_t scalar_type_id = _.GetOperandTypeId(inst, 3); + if (component_type != scalar_type_id) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar operand type to be equal to the component " + << "type of the matrix operand: " << spvOpcodeString(opcode); + + break; + } + + case SpvOpVectorTimesMatrix: { + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 3); + + if (!_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t res_component_type = _.GetComponentType(result_type); + + if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as left operand: " + << spvOpcodeString(opcode); + + if (res_component_type != _.GetComponentType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and vector to be " + << "equal: " << spvOpcodeString(opcode); + + uint32_t matrix_num_rows = 0; + uint32_t matrix_num_cols = 0; + uint32_t matrix_col_type = 0; + uint32_t matrix_component_type = 0; + if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, + &matrix_num_cols, &matrix_col_type, + &matrix_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as right operand: " + << spvOpcodeString(opcode); + + if (res_component_type != matrix_component_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and matrix to be " + << "equal: " << spvOpcodeString(opcode); + + if (matrix_num_cols != _.GetDimension(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to " + << "Result Type vector size: " << spvOpcodeString(opcode); + + if (matrix_num_rows != _.GetDimension(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of rows of the matrix to be equal to the " + << "vector operand size: " << spvOpcodeString(opcode); + + break; + } + + case SpvOpMatrixTimesVector: { + const uint32_t matrix_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t vector_type_id = _.GetOperandTypeId(inst, 3); + + if (!_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as Result Type: " + << spvOpcodeString(opcode); + + uint32_t matrix_num_rows = 0; + uint32_t matrix_num_cols = 0; + uint32_t matrix_col_type = 0; + uint32_t matrix_component_type = 0; + if (!_.GetMatrixTypeInfo(matrix_type_id, &matrix_num_rows, + &matrix_num_cols, &matrix_col_type, + &matrix_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as left operand: " + << spvOpcodeString(opcode); + + if (result_type != matrix_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column type of the matrix to be equal to Result " + "Type: " + << spvOpcodeString(opcode); + + if (!vector_type_id || !_.IsFloatVectorType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as right operand: " + << spvOpcodeString(opcode); + + if (matrix_component_type != _.GetComponentType(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of the operands to be equal: " + << spvOpcodeString(opcode); + + if (matrix_num_cols != _.GetDimension(vector_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to the " + << "vector size: " << spvOpcodeString(opcode); + + break; + } + + case SpvOpMatrixTimesMatrix: { + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + uint32_t res_num_rows = 0; + uint32_t res_num_cols = 0; + uint32_t res_col_type = 0; + uint32_t res_component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, + &res_col_type, &res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + uint32_t left_num_rows = 0; + uint32_t left_num_cols = 0; + uint32_t left_col_type = 0; + uint32_t left_component_type = 0; + if (!_.GetMatrixTypeInfo(left_type_id, &left_num_rows, &left_num_cols, + &left_col_type, &left_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as left operand: " + << spvOpcodeString(opcode); + + uint32_t right_num_rows = 0; + uint32_t right_num_cols = 0; + uint32_t right_col_type = 0; + uint32_t right_component_type = 0; + if (!_.GetMatrixTypeInfo(right_type_id, &right_num_rows, &right_num_cols, + &right_col_type, &right_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as right operand: " + << spvOpcodeString(opcode); + + if (!_.IsFloatScalarType(res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + if (res_col_type != left_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column types of Result Type and left matrix to be " + << "equal: " << spvOpcodeString(opcode); + + if (res_component_type != right_component_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Result Type and right matrix to " + "be " + << "equal: " << spvOpcodeString(opcode); + + if (res_num_cols != right_num_cols) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of Result Type and right matrix " + "to " + << "be equal: " << spvOpcodeString(opcode); + + if (left_num_cols != right_num_rows) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of left matrix and number of " + "rows " + << "of right matrix to be equal: " << spvOpcodeString(opcode); + + assert(left_num_rows == res_num_rows); + break; + } + + case SpvOpOuterProduct: { + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + uint32_t res_num_rows = 0; + uint32_t res_num_cols = 0; + uint32_t res_col_type = 0; + uint32_t res_component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &res_num_rows, &res_num_cols, + &res_col_type, &res_component_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float matrix type as Result Type: " + << spvOpcodeString(opcode); + + if (left_type_id != res_col_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected column type of Result Type to be equal to the type " + << "of the left operand: " << spvOpcodeString(opcode); + + if (!right_type_id || !_.IsFloatVectorType(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float vector type as right operand: " + << spvOpcodeString(opcode); + + if (res_component_type != _.GetComponentType(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of the operands to be equal: " + << spvOpcodeString(opcode); + + if (res_num_cols != _.GetDimension(right_type_id)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns of the matrix to be equal to the " + << "vector size of the right operand: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpIAddCarry: + case SpvOpISubBorrow: + case SpvOpUMulExtended: + case SpvOpSMulExtended: { + std::vector result_types; + if (!_.GetStructMemberTypes(result_type, &result_types)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected a struct as Result Type: " + << spvOpcodeString(opcode); + + if (result_types.size() != 2) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct to have two members: " + << spvOpcodeString(opcode); + + if (opcode == SpvOpSMulExtended) { + if (!_.IsIntScalarType(result_types[0]) && + !_.IsIntVectorType(result_types[0])) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be integer " + "scalar " + << "or vector: " << spvOpcodeString(opcode); + } else { + if (!_.IsUnsignedIntScalarType(result_types[0]) && + !_.IsUnsignedIntVectorType(result_types[0])) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be unsigned " + << "integer scalar or vector: " << spvOpcodeString(opcode); + } + + if (result_types[0] != result_types[1]) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type struct member types to be identical: " + << spvOpcodeString(opcode); + + const uint32_t left_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t right_type_id = _.GetOperandTypeId(inst, 3); + + if (left_type_id != result_types[0] || right_type_id != result_types[0]) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to be of Result Type member type: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpCooperativeMatrixMulAddNV: { + const uint32_t D_type_id = _.GetOperandTypeId(inst, 1); + const uint32_t A_type_id = _.GetOperandTypeId(inst, 2); + const uint32_t B_type_id = _.GetOperandTypeId(inst, 3); + const uint32_t C_type_id = _.GetOperandTypeId(inst, 4); + + if (!_.IsCooperativeMatrixType(A_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as A Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixType(B_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as B Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixType(C_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as C Type: " + << spvOpcodeString(opcode); + } + if (!_.IsCooperativeMatrixType(D_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix type as Result Type: " + << spvOpcodeString(opcode); + } + + const auto A = _.FindDef(A_type_id); + const auto B = _.FindDef(B_type_id); + const auto C = _.FindDef(C_type_id); + const auto D = _.FindDef(D_type_id); + + std::tuple A_scope, B_scope, C_scope, D_scope, + A_rows, B_rows, C_rows, D_rows, A_cols, B_cols, C_cols, D_cols; + + A_scope = _.EvalInt32IfConst(A->GetOperandAs(2)); + B_scope = _.EvalInt32IfConst(B->GetOperandAs(2)); + C_scope = _.EvalInt32IfConst(C->GetOperandAs(2)); + D_scope = _.EvalInt32IfConst(D->GetOperandAs(2)); + + A_rows = _.EvalInt32IfConst(A->GetOperandAs(3)); + B_rows = _.EvalInt32IfConst(B->GetOperandAs(3)); + C_rows = _.EvalInt32IfConst(C->GetOperandAs(3)); + D_rows = _.EvalInt32IfConst(D->GetOperandAs(3)); + + A_cols = _.EvalInt32IfConst(A->GetOperandAs(4)); + B_cols = _.EvalInt32IfConst(B->GetOperandAs(4)); + C_cols = _.EvalInt32IfConst(C->GetOperandAs(4)); + D_cols = _.EvalInt32IfConst(D->GetOperandAs(4)); + + const auto notEqual = [](std::tuple X, + std::tuple Y) { + return (std::get<1>(X) && std::get<1>(Y) && + std::get<2>(X) != std::get<2>(Y)); + }; + + if (notEqual(A_scope, B_scope) || notEqual(A_scope, C_scope) || + notEqual(A_scope, D_scope) || notEqual(B_scope, C_scope) || + notEqual(B_scope, D_scope) || notEqual(C_scope, D_scope)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix scopes must match: " + << spvOpcodeString(opcode); + } + + if (notEqual(A_rows, C_rows) || notEqual(A_rows, D_rows) || + notEqual(C_rows, D_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'M' mismatch: " + << spvOpcodeString(opcode); + } + + if (notEqual(B_cols, C_cols) || notEqual(B_cols, D_cols) || + notEqual(C_cols, D_cols)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'N' mismatch: " + << spvOpcodeString(opcode); + } + + if (notEqual(A_cols, B_rows)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cooperative matrix 'K' mismatch: " + << spvOpcodeString(opcode); + } + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_atomics.cpp b/third_party/spirv-tools/source/val/validate_atomics.cpp new file mode 100644 index 0000000..3f1f561 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_atomics.cpp @@ -0,0 +1,348 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of atomic SPIR-V instructions. + +#include "source/val/validate.h" + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validate_memory_semantics.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace { + +bool IsStorageClassAllowedByUniversalRules(uint32_t storage_class) { + switch (storage_class) { + case SpvStorageClassUniform: + case SpvStorageClassStorageBuffer: + case SpvStorageClassWorkgroup: + case SpvStorageClassCrossWorkgroup: + case SpvStorageClassGeneric: + case SpvStorageClassAtomicCounter: + case SpvStorageClassImage: + case SpvStorageClassFunction: + case SpvStorageClassPhysicalStorageBufferEXT: + return true; + break; + default: + return false; + } +} + +} // namespace + +namespace spvtools { +namespace val { + +// Validates correctness of atomic instructions. +spv_result_t AtomicsPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + bool is_atomic_float_opcode = false; + if (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicStore || + opcode == SpvOpAtomicFAddEXT || opcode == SpvOpAtomicExchange) { + is_atomic_float_opcode = true; + } + switch (opcode) { + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicFAddEXT: + case SpvOpAtomicCompareExchange: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicFlagTestAndSet: + case SpvOpAtomicFlagClear: { + if (_.HasCapability(SpvCapabilityKernel) && + (opcode == SpvOpAtomicLoad || opcode == SpvOpAtomicExchange || + opcode == SpvOpAtomicCompareExchange)) { + if (!_.IsFloatScalarType(result_type) && + !_.IsIntScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be int or float scalar type"; + } + } else if (opcode == SpvOpAtomicFlagTestAndSet) { + if (!_.IsBoolScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be bool scalar type"; + } + } else if (opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore) { + assert(result_type == 0); + } else { + if (_.IsFloatScalarType(result_type)) { + if (is_atomic_float_opcode) { + if (opcode == SpvOpAtomicFAddEXT) { + if ((_.GetBitWidth(result_type) == 32) && + (!_.HasCapability(SpvCapabilityAtomicFloat32AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat32AddEXT " + "capability"; + } + if ((_.GetBitWidth(result_type) == 64) && + (!_.HasCapability(SpvCapabilityAtomicFloat64AddEXT))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": float add atomics require the AtomicFloat64AddEXT " + "capability"; + } + } + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be int scalar type"; + } + } else if (_.IsIntScalarType(result_type) && + opcode == SpvOpAtomicFAddEXT) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be float scalar type"; + } else if (!_.IsFloatScalarType(result_type) && + !_.IsIntScalarType(result_type)) { + switch (opcode) { + case SpvOpAtomicFAddEXT: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be float scalar type"; + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicSMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMin: + case SpvOpAtomicUMax: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be integer scalar type"; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be int or float scalar type"; + } + } + + if (spvIsVulkanEnv(_.context()->target_env) && + (_.GetBitWidth(result_type) != 32 && + (_.GetBitWidth(result_type) != 64 || + !_.HasCapability(SpvCapabilityInt64ImageEXT)))) { + switch (opcode) { + case SpvOpAtomicSMin: + case SpvOpAtomicUMin: + case SpvOpAtomicSMax: + case SpvOpAtomicUMax: + case SpvOpAtomicAnd: + case SpvOpAtomicOr: + case SpvOpAtomicXor: + case SpvOpAtomicIAdd: + case SpvOpAtomicISub: + case SpvOpAtomicFAddEXT: + case SpvOpAtomicLoad: + case SpvOpAtomicStore: + case SpvOpAtomicExchange: + case SpvOpAtomicIIncrement: + case SpvOpAtomicIDecrement: + case SpvOpAtomicCompareExchangeWeak: + case SpvOpAtomicCompareExchange: { + if (_.GetBitWidth(result_type) == 64 && + _.IsIntScalarType(result_type) && + !_.HasCapability(SpvCapabilityInt64Atomics)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": 64-bit atomics require the Int64Atomics " + "capability"; + } break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": according to the Vulkan spec atomic Result Type " + "needs " + "to be a 32-bit int scalar type"; + } + } + } + + uint32_t operand_index = + opcode == SpvOpAtomicFlagClear || opcode == SpvOpAtomicStore ? 0 : 2; + const uint32_t pointer_type = _.GetOperandTypeId(inst, operand_index++); + + uint32_t data_type = 0; + uint32_t storage_class = 0; + if (!_.GetPointerTypeInfo(pointer_type, &data_type, &storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to be of type OpTypePointer"; + } + + // Validate storage class against universal rules + if (!IsStorageClassAllowedByUniversalRules(storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": storage class forbidden by universal validation rules."; + } + + // Then Shader rules + if (_.HasCapability(SpvCapabilityShader)) { + if (storage_class == SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Function storage class forbidden when the Shader " + "capability is declared."; + } + } + + // And finally OpenCL environment rules + if (spvIsOpenCLEnv(_.context()->target_env)) { + if ((storage_class != SpvStorageClassFunction) && + (storage_class != SpvStorageClassWorkgroup) && + (storage_class != SpvStorageClassCrossWorkgroup) && + (storage_class != SpvStorageClassGeneric)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": storage class must be Function, Workgroup, " + "CrossWorkGroup or Generic in the OpenCL environment."; + } + + if (_.context()->target_env == SPV_ENV_OPENCL_1_2) { + if (storage_class == SpvStorageClassGeneric) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Storage class cannot be Generic in OpenCL 1.2 " + "environment"; + } + } + } + + if (opcode == SpvOpAtomicFlagTestAndSet || + opcode == SpvOpAtomicFlagClear) { + if (!_.IsIntScalarType(data_type) || _.GetBitWidth(data_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to point to a value of 32-bit int type"; + } + } else if (opcode == SpvOpAtomicStore) { + if (!_.IsFloatScalarType(data_type) && !_.IsIntScalarType(data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to be a pointer to int or float " + << "scalar type"; + } + } else { + if (data_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Pointer to point to a value of type Result " + "Type"; + } + } + + auto memory_scope = inst->GetOperandAs(operand_index++); + if (auto error = ValidateMemoryScope(_, inst, memory_scope)) { + return error; + } + + const auto equal_semantics_index = operand_index++; + if (auto error = ValidateMemorySemantics(_, inst, equal_semantics_index)) + return error; + + if (opcode == SpvOpAtomicCompareExchange || + opcode == SpvOpAtomicCompareExchangeWeak) { + const auto unequal_semantics_index = operand_index++; + if (auto error = + ValidateMemorySemantics(_, inst, unequal_semantics_index)) + return error; + + // Volatile bits must match for equal and unequal semantics. Previous + // checks guarantee they are 32-bit constants, but we need to recheck + // whether they are evaluatable constants. + bool is_int32 = false; + bool is_equal_const = false; + bool is_unequal_const = false; + uint32_t equal_value = 0; + uint32_t unequal_value = 0; + std::tie(is_int32, is_equal_const, equal_value) = _.EvalInt32IfConst( + inst->GetOperandAs(equal_semantics_index)); + std::tie(is_int32, is_unequal_const, unequal_value) = + _.EvalInt32IfConst( + inst->GetOperandAs(unequal_semantics_index)); + if (is_equal_const && is_unequal_const && + ((equal_value & SpvMemorySemanticsVolatileMask) ^ + (unequal_value & SpvMemorySemanticsVolatileMask))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Volatile mask setting must match for Equal and Unequal " + "memory semantics"; + } + } + + if (opcode == SpvOpAtomicStore) { + const uint32_t value_type = _.GetOperandTypeId(inst, 3); + if (value_type != data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Value type and the type pointed to by " + "Pointer to be the same"; + } + } else if (opcode != SpvOpAtomicLoad && opcode != SpvOpAtomicIIncrement && + opcode != SpvOpAtomicIDecrement && + opcode != SpvOpAtomicFlagTestAndSet && + opcode != SpvOpAtomicFlagClear) { + const uint32_t value_type = _.GetOperandTypeId(inst, operand_index++); + if (value_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Value to be of type Result Type"; + } + } + + if (opcode == SpvOpAtomicCompareExchange || + opcode == SpvOpAtomicCompareExchangeWeak) { + const uint32_t comparator_type = + _.GetOperandTypeId(inst, operand_index++); + if (comparator_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Comparator to be of type Result Type"; + } + } + + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_barriers.cpp b/third_party/spirv-tools/source/val/validate_barriers.cpp new file mode 100644 index 0000000..b499c8c --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_barriers.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of barrier SPIR-V instructions. + +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_memory_semantics.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of barrier instructions. +spv_result_t BarriersPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case SpvOpControlBarrier: { + if (_.version() < SPV_SPIRV_VERSION_WORD(1, 3)) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelTessellationControl && + model != SpvExecutionModelGLCompute && + model != SpvExecutionModelKernel && + model != SpvExecutionModelTaskNV && + model != SpvExecutionModelMeshNV) { + if (message) { + *message = + "OpControlBarrier requires one of the following " + "Execution " + "Models: TessellationControl, GLCompute or Kernel"; + } + return false; + } + return true; + }); + } + + const uint32_t execution_scope = inst->word(1); + const uint32_t memory_scope = inst->word(2); + + if (auto error = ValidateExecutionScope(_, inst, execution_scope)) { + return error; + } + + if (auto error = ValidateMemoryScope(_, inst, memory_scope)) { + return error; + } + + if (auto error = ValidateMemorySemantics(_, inst, 2)) { + return error; + } + break; + } + + case SpvOpMemoryBarrier: { + const uint32_t memory_scope = inst->word(1); + + if (auto error = ValidateMemoryScope(_, inst, memory_scope)) { + return error; + } + + if (auto error = ValidateMemorySemantics(_, inst, 1)) { + return error; + } + break; + } + + case SpvOpNamedBarrierInitialize: { + if (_.GetIdOpcode(result_type) != SpvOpTypeNamedBarrier) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Result Type to be OpTypeNamedBarrier"; + } + + const uint32_t subgroup_count_type = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(subgroup_count_type) || + _.GetBitWidth(subgroup_count_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Subgroup Count to be a 32-bit int"; + } + break; + } + + case SpvOpMemoryNamedBarrier: { + const uint32_t named_barrier_type = _.GetOperandTypeId(inst, 0); + if (_.GetIdOpcode(named_barrier_type) != SpvOpTypeNamedBarrier) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Named Barrier to be of type OpTypeNamedBarrier"; + } + + const uint32_t memory_scope = inst->word(2); + + if (auto error = ValidateMemoryScope(_, inst, memory_scope)) { + return error; + } + + if (auto error = ValidateMemorySemantics(_, inst, 2)) { + return error; + } + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_bitwise.cpp b/third_party/spirv-tools/source/val/validate_bitwise.cpp new file mode 100644 index 0000000..d46b3fc --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_bitwise.cpp @@ -0,0 +1,219 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of bitwise instructions. + +#include "source/val/validate.h" + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of bitwise instructions. +spv_result_t BitwisePass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case SpvOpShiftRightLogical: + case SpvOpShiftRightArithmetic: + case SpvOpShiftLeftLogical: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t result_dimension = _.GetDimension(result_type); + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + const uint32_t shift_type = _.GetOperandTypeId(inst, 3); + + if (!base_type || + (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.GetDimension(base_type) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode); + + if (_.GetBitWidth(base_type) != _.GetBitWidth(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode); + + if (!shift_type || + (!_.IsIntScalarType(shift_type) && !_.IsIntVectorType(shift_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Shift to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.GetDimension(shift_type) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Shift to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode); + break; + } + + case SpvOpBitwiseOr: + case SpvOpBitwiseXor: + case SpvOpBitwiseAnd: + case SpvOpNot: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t result_dimension = _.GetDimension(result_type); + const uint32_t result_bit_width = _.GetBitWidth(result_type); + + for (size_t operand_index = 2; operand_index < inst->operands().size(); + ++operand_index) { + const uint32_t type_id = _.GetOperandTypeId(inst, operand_index); + if (!type_id || + (!_.IsIntScalarType(type_id) && !_.IsIntVectorType(type_id))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector as operand: " + << spvOpcodeString(opcode) << " operand index " + << operand_index; + + if (_.GetDimension(type_id) != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same dimension " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + + if (_.GetBitWidth(type_id) != result_bit_width) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to have the same bit width " + << "as Result Type: " << spvOpcodeString(opcode) + << " operand index " << operand_index; + } + break; + } + + case SpvOpBitFieldInsert: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + const uint32_t insert_type = _.GetOperandTypeId(inst, 3); + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t count_type = _.GetOperandTypeId(inst, 5); + + if (base_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base Type to be equal to Result Type: " + << spvOpcodeString(opcode); + + if (insert_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Insert Type to be equal to Result Type: " + << spvOpcodeString(opcode); + + if (!offset_type || !_.IsIntScalarType(offset_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Offset Type to be int scalar: " + << spvOpcodeString(opcode); + + if (!count_type || !_.IsIntScalarType(count_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Count Type to be int scalar: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpBitFieldSExtract: + case SpvOpBitFieldUExtract: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + const uint32_t offset_type = _.GetOperandTypeId(inst, 3); + const uint32_t count_type = _.GetOperandTypeId(inst, 4); + + if (base_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base Type to be equal to Result Type: " + << spvOpcodeString(opcode); + + if (!offset_type || !_.IsIntScalarType(offset_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Offset Type to be int scalar: " + << spvOpcodeString(opcode); + + if (!count_type || !_.IsIntScalarType(count_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Count Type to be int scalar: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpBitReverse: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + + if (base_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base Type to be equal to Result Type: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpBitCount: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t base_type = _.GetOperandTypeId(inst, 2); + if (!base_type || + (!_.IsIntScalarType(base_type) && !_.IsIntVectorType(base_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base Type to be int scalar or vector: " + << spvOpcodeString(opcode); + + const uint32_t base_dimension = _.GetDimension(base_type); + const uint32_t result_dimension = _.GetDimension(result_type); + + if (base_dimension != result_dimension) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Base dimension to be equal to Result Type " + "dimension: " + << spvOpcodeString(opcode); + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_builtins.cpp b/third_party/spirv-tools/source/val/validate_builtins.cpp new file mode 100644 index 0000000..2c5604c --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_builtins.cpp @@ -0,0 +1,3785 @@ +// Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of built-in variables. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns a short textual description of the id defined by the given +// instruction. +std::string GetIdDesc(const Instruction& inst) { + std::ostringstream ss; + ss << "ID <" << inst.id() << "> (Op" << spvOpcodeString(inst.opcode()) << ")"; + return ss.str(); +} + +// Gets underlying data type which is +// - member type if instruction is OpTypeStruct +// (member index is taken from decoration). +// - data type if id creates a pointer. +// - type of the constant if instruction is OpConst or OpSpecConst. +// +// Fails in any other case. The function is based on built-ins allowed by +// the Vulkan spec. +// TODO: If non-Vulkan validation rules are added then it might need +// to be refactored. +spv_result_t GetUnderlyingType(ValidationState_t& _, + const Decoration& decoration, + const Instruction& inst, + uint32_t* underlying_type) { + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + if (inst.opcode() != SpvOpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << GetIdDesc(inst) + << "Attempted to get underlying data type via member index for " + "non-struct type."; + } + *underlying_type = inst.word(decoration.struct_member_index() + 2); + return SPV_SUCCESS; + } + + if (inst.opcode() == SpvOpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << GetIdDesc(inst) + << " did not find an member index to get underlying data type for " + "struct type."; + } + + if (spvOpcodeIsConstant(inst.opcode())) { + *underlying_type = inst.type_id(); + return SPV_SUCCESS; + } + + uint32_t storage_class = 0; + if (!_.GetPointerTypeInfo(inst.type_id(), underlying_type, &storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << GetIdDesc(inst) + << " is decorated with BuiltIn. BuiltIn decoration should only be " + "applied to struct types, variables and constants."; + } + return SPV_SUCCESS; +} + +// Returns Storage Class used by the instruction if applicable. +// Returns SpvStorageClassMax if not. +SpvStorageClass GetStorageClass(const Instruction& inst) { + switch (inst.opcode()) { + case SpvOpTypePointer: + case SpvOpTypeForwardPointer: { + return SpvStorageClass(inst.word(2)); + } + case SpvOpVariable: { + return SpvStorageClass(inst.word(3)); + } + case SpvOpGenericCastToPtrExplicit: { + return SpvStorageClass(inst.word(4)); + } + default: { break; } + } + return SpvStorageClassMax; +} + +bool IsBuiltInValidForWebGPU(SpvBuiltIn label) { + switch (label) { + case SpvBuiltInPosition: + case SpvBuiltInVertexIndex: + case SpvBuiltInInstanceIndex: + case SpvBuiltInFrontFacing: + case SpvBuiltInFragCoord: + case SpvBuiltInFragDepth: + case SpvBuiltInNumWorkgroups: + case SpvBuiltInWorkgroupSize: + case SpvBuiltInLocalInvocationId: + case SpvBuiltInGlobalInvocationId: + case SpvBuiltInLocalInvocationIndex: { + return true; + } + default: + break; + } + + return false; +} + +// Helper class managing validation of built-ins. +// TODO: Generic functionality of this class can be moved into +// ValidationState_t to be made available to other users. +class BuiltInsValidator { + public: + BuiltInsValidator(ValidationState_t& vstate) : _(vstate) {} + + // Run validation. + spv_result_t Run(); + + private: + // Goes through all decorations in the module, if decoration is BuiltIn + // calls ValidateSingleBuiltInAtDefinition(). + spv_result_t ValidateBuiltInsAtDefinition(); + + // Validates the instruction defining an id with built-in decoration. + // Can be called multiple times for the same id, if multiple built-ins are + // specified. Seeds id_to_at_reference_checks_ with decorated ids if needed. + spv_result_t ValidateSingleBuiltInAtDefinition(const Decoration& decoration, + const Instruction& inst); + + // The following section contains functions which are called when id defined + // by |inst| is decorated with BuiltIn |decoration|. + // Most functions are specific to a single built-in and have naming scheme: + // ValidateXYZAtDefinition. Some functions are common to multiple kinds of + // BuiltIn. + spv_result_t ValidateClipOrCullDistanceAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateFragCoordAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateFragDepthAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateFrontFacingAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateHelperInvocationAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateInvocationIdAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateInstanceIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateLayerOrViewportIndexAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidatePatchVerticesAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidatePointCoordAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidatePointSizeAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidatePositionAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidatePrimitiveIdAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateSampleIdAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateSampleMaskAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateSamplePositionAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateTessCoordAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateTessLevelOuterAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateTessLevelInnerAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateVertexIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateVertexIdOrInstanceIdAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateLocalInvocationIndexAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateWorkgroupSizeAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateBaseInstanceOrVertexAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateDrawIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateViewIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + spv_result_t ValidateDeviceIndexAtDefinition(const Decoration& decoration, + const Instruction& inst); + // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId. + spv_result_t ValidateComputeShaderI32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst); + spv_result_t ValidateSMBuiltinsAtDefinition(const Decoration& decoration, + const Instruction& inst); + + // Used for SubgroupEqMask, SubgroupGeMask, SubgroupGtMask, SubgroupLtMask, + // SubgroupLeMask. + spv_result_t ValidateI32Vec4InputAtDefinition(const Decoration& decoration, + const Instruction& inst); + // Used for SubgroupLocalInvocationId, SubgroupSize. + spv_result_t ValidateI32InputAtDefinition(const Decoration& decoration, + const Instruction& inst); + // Used for SubgroupId, NumSubgroups. + spv_result_t ValidateComputeI32InputAtDefinition(const Decoration& decoration, + const Instruction& inst); + + spv_result_t ValidatePrimitiveShadingRateAtDefinition( + const Decoration& decoration, const Instruction& inst); + + spv_result_t ValidateShadingRateAtDefinition(const Decoration& decoration, + const Instruction& inst); + + // The following section contains functions which are called when id defined + // by |referenced_inst| is + // 1. referenced by |referenced_from_inst| + // 2. dependent on |built_in_inst| which is decorated with BuiltIn + // |decoration|. Most functions are specific to a single built-in and have + // naming scheme: ValidateXYZAtReference. Some functions are common to + // multiple kinds of BuiltIn. + spv_result_t ValidateFragCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateFragDepthAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateFrontFacingAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateHelperInvocationAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateInvocationIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateInstanceIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateInstanceIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePatchVerticesAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePointCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePointSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePositionAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePrimitiveIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateSampleIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateSampleMaskAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateSamplePositionAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateTessCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateTessLevelAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateLocalInvocationIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateVertexIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateLayerOrViewportIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateWorkgroupSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateClipOrCullDistanceAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateBaseInstanceOrVertexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateDrawIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateViewIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateDeviceIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + // Used for GlobalInvocationId, LocalInvocationId, NumWorkgroups, WorkgroupId. + spv_result_t ValidateComputeShaderI32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + // Used for SubgroupId and NumSubgroups. + spv_result_t ValidateComputeI32InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateSMBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidatePrimitiveShadingRateAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + spv_result_t ValidateShadingRateAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + // Validates that |built_in_inst| is not (even indirectly) referenced from + // within a function which can be called with |execution_model|. + // + // |comment| - text explaining why the restriction was imposed. + // |decoration| - BuiltIn decoration which causes the restriction. + // |referenced_inst| - instruction which is dependent on |built_in_inst| and + // defines the id which was referenced. + // |referenced_from_inst| - instruction which references id defined by + // |referenced_inst| from within a function. + spv_result_t ValidateNotCalledWithExecutionModel( + std::string comment, SpvExecutionModel execution_model, + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst); + + // The following section contains functions which check that the decorated + // variable has the type specified in the function name. |diag| would be + // called with a corresponding error message, if validation is not successful. + spv_result_t ValidateBool( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateI32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateI32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateI32Arr( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateOptionalArrayedI32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateI32Helper( + const Decoration& decoration, const Instruction& inst, + const std::function& diag, + uint32_t underlying_type); + spv_result_t ValidateF32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateOptionalArrayedF32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag); + spv_result_t ValidateF32Helper( + const Decoration& decoration, const Instruction& inst, + const std::function& diag, + uint32_t underlying_type); + spv_result_t ValidateF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateOptionalArrayedF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateF32VecHelper( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag, + uint32_t underlying_type); + // If |num_components| is zero, the number of components is not checked. + spv_result_t ValidateF32Arr( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateOptionalArrayedF32Arr( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag); + spv_result_t ValidateF32ArrHelper( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag, + uint32_t underlying_type); + + // Generates strings like "Member #0 of struct ID <2>". + std::string GetDefinitionDesc(const Decoration& decoration, + const Instruction& inst) const; + + // Generates strings like "ID <51> (OpTypePointer) is referencing ID <2> + // (OpTypeStruct) which is decorated with BuiltIn Position". + std::string GetReferenceDesc( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst, + SpvExecutionModel execution_model = SpvExecutionModelMax) const; + + // Generates strings like "ID <51> (OpTypePointer) uses storage class + // UniformConstant". + std::string GetStorageClassDesc(const Instruction& inst) const; + + // Updates inner working of the class. Is called sequentially for every + // instruction. + void Update(const Instruction& inst); + + ValidationState_t& _; + + // Mapping id -> list of rules which validate instruction referencing the + // id. Rules can create new rules and add them to this container. + // Using std::map, and not std::unordered_map to avoid iterator invalidation + // during rehashing. + std::map>> + id_to_at_reference_checks_; + + // Id of the function we are currently inside. 0 if not inside a function. + uint32_t function_id_ = 0; + + // Entry points which can (indirectly) call the current function. + // The pointer either points to a vector inside to function_to_entry_points_ + // or to no_entry_points_. The pointer is guaranteed to never be null. + const std::vector no_entry_points; + const std::vector* entry_points_ = &no_entry_points; + + // Execution models with which the current function can be called. + std::set execution_models_; +}; + +void BuiltInsValidator::Update(const Instruction& inst) { + const SpvOp opcode = inst.opcode(); + if (opcode == SpvOpFunction) { + // Entering a function. + assert(function_id_ == 0); + function_id_ = inst.id(); + execution_models_.clear(); + entry_points_ = &_.FunctionEntryPoints(function_id_); + // Collect execution models from all entry points from which the current + // function can be called. + for (const uint32_t entry_point : *entry_points_) { + if (const auto* models = _.GetExecutionModels(entry_point)) { + execution_models_.insert(models->begin(), models->end()); + } + } + } + + if (opcode == SpvOpFunctionEnd) { + // Exiting a function. + assert(function_id_ != 0); + function_id_ = 0; + entry_points_ = &no_entry_points; + execution_models_.clear(); + } +} + +std::string BuiltInsValidator::GetDefinitionDesc( + const Decoration& decoration, const Instruction& inst) const { + std::ostringstream ss; + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + assert(inst.opcode() == SpvOpTypeStruct); + ss << "Member #" << decoration.struct_member_index(); + ss << " of struct ID <" << inst.id() << ">"; + } else { + ss << GetIdDesc(inst); + } + return ss.str(); +} + +std::string BuiltInsValidator::GetReferenceDesc( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, const Instruction& referenced_from_inst, + SpvExecutionModel execution_model) const { + std::ostringstream ss; + ss << GetIdDesc(referenced_from_inst) << " is referencing " + << GetIdDesc(referenced_inst); + if (built_in_inst.id() != referenced_inst.id()) { + ss << " which is dependent on " << GetIdDesc(built_in_inst); + } + + ss << " which is decorated with BuiltIn "; + ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]); + if (function_id_) { + ss << " in function <" << function_id_ << ">"; + if (execution_model != SpvExecutionModelMax) { + ss << " called with execution model "; + ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_EXECUTION_MODEL, + execution_model); + } + } + ss << "."; + return ss.str(); +} + +std::string BuiltInsValidator::GetStorageClassDesc( + const Instruction& inst) const { + std::ostringstream ss; + ss << GetIdDesc(inst) << " uses storage class "; + ss << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_STORAGE_CLASS, + GetStorageClass(inst)); + ss << "."; + return ss.str(); +} + +spv_result_t BuiltInsValidator::ValidateBool( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + if (!_.IsBoolScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not a bool scalar."); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + return ValidateI32Helper(decoration, inst, diag, underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateOptionalArrayedI32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + // Strip the array, if present. + if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) { + underlying_type = _.FindDef(underlying_type)->word(2u); + } + + return ValidateI32Helper(decoration, inst, diag, underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateI32Helper( + const Decoration& decoration, const Instruction& inst, + const std::function& diag, + uint32_t underlying_type) { + if (!_.IsIntScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an int scalar."); + } + + const uint32_t bit_width = _.GetBitWidth(underlying_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width + << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + // Strip the array, if present. + if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) { + underlying_type = _.FindDef(underlying_type)->word(2u); + } + + return ValidateF32Helper(decoration, inst, diag, underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + return ValidateF32Helper(decoration, inst, diag, underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32Helper( + const Decoration& decoration, const Instruction& inst, + const std::function& diag, + uint32_t underlying_type) { + if (!_.IsFloatScalarType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " is not a float scalar."); + } + + const uint32_t bit_width = _.GetBitWidth(underlying_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has bit width " << bit_width + << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + if (!_.IsIntVectorType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an int vector."); + } + + const uint32_t actual_num_components = _.GetDimension(underlying_type); + if (_.GetDimension(underlying_type) != num_components) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has " + << actual_num_components << " components."; + return diag(ss.str()); + } + + const uint32_t bit_width = _.GetBitWidth(underlying_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + // Strip the array, if present. + if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) { + underlying_type = _.FindDef(underlying_type)->word(2u); + } + + return ValidateF32VecHelper(decoration, inst, num_components, diag, + underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32Vec( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + return ValidateF32VecHelper(decoration, inst, num_components, diag, + underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32VecHelper( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag, + uint32_t underlying_type) { + if (!_.IsFloatVectorType(underlying_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " is not a float vector."); + } + + const uint32_t actual_num_components = _.GetDimension(underlying_type); + if (_.GetDimension(underlying_type) != num_components) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has " + << actual_num_components << " components."; + return diag(ss.str()); + } + + const uint32_t bit_width = _.GetBitWidth(underlying_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32Arr( + const Decoration& decoration, const Instruction& inst, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + const Instruction* const type_inst = _.FindDef(underlying_type); + if (type_inst->opcode() != SpvOpTypeArray) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); + } + + const uint32_t component_type = type_inst->word(2); + if (!_.IsIntScalarType(component_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " components are not int scalar."); + } + + const uint32_t bit_width = _.GetBitWidth(component_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateF32Arr( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + return ValidateF32ArrHelper(decoration, inst, num_components, diag, + underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateOptionalArrayedF32Arr( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag) { + uint32_t underlying_type = 0; + if (spv_result_t error = + GetUnderlyingType(_, decoration, inst, &underlying_type)) { + return error; + } + + // Strip an extra layer of arraying if present. + if (_.GetIdOpcode(underlying_type) == SpvOpTypeArray) { + uint32_t subtype = _.FindDef(underlying_type)->word(2u); + if (_.GetIdOpcode(subtype) == SpvOpTypeArray) { + underlying_type = subtype; + } + } + + return ValidateF32ArrHelper(decoration, inst, num_components, diag, + underlying_type); +} + +spv_result_t BuiltInsValidator::ValidateF32ArrHelper( + const Decoration& decoration, const Instruction& inst, + uint32_t num_components, + const std::function& diag, + uint32_t underlying_type) { + const Instruction* const type_inst = _.FindDef(underlying_type); + if (type_inst->opcode() != SpvOpTypeArray) { + return diag(GetDefinitionDesc(decoration, inst) + " is not an array."); + } + + const uint32_t component_type = type_inst->word(2); + if (!_.IsFloatScalarType(component_type)) { + return diag(GetDefinitionDesc(decoration, inst) + + " components are not float scalar."); + } + + const uint32_t bit_width = _.GetBitWidth(component_type); + if (bit_width != 32) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) + << " has components with bit width " << bit_width << "."; + return diag(ss.str()); + } + + if (num_components != 0) { + uint64_t actual_num_components = 0; + if (!_.GetConstantValUint64(type_inst->word(3), &actual_num_components)) { + assert(0 && "Array type definition is corrupt"); + } + if (actual_num_components != num_components) { + std::ostringstream ss; + ss << GetDefinitionDesc(decoration, inst) << " has " + << actual_num_components << " components."; + return diag(ss.str()); + } + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateNotCalledWithExecutionModel( + std::string comment, SpvExecutionModel execution_model, + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (function_id_) { + if (execution_models_.count(execution_model)) { + const char* execution_model_str = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_EXECUTION_MODEL, execution_model); + const char* built_in_str = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]); + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << comment << " " << GetIdDesc(referenced_inst) << " depends on " + << GetIdDesc(built_in_inst) << " which is decorated with BuiltIn " + << built_in_str << "." + << " Id <" << referenced_inst.id() << "> is later referenced by " + << GetIdDesc(referenced_from_inst) << " in function <" + << function_id_ << "> which is called with execution model " + << execution_model_str << "."; + } + } else { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + comment, execution_model, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtDefinition( + const Decoration& decoration, const Instruction& inst) { + // Seed at reference checks with this built-in. + return ValidateClipOrCullDistanceAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateClipOrCullDistanceAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = decoration.params()[0]; + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input or Output storage " + "class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == SpvStorageClassInput) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " + "used for variables with Input storage class if execution model is " + "Vertex.", + SpvExecutionModelVertex, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + if (storage_class == SpvStorageClassOutput) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + "Vulkan spec doesn't allow BuiltIn ClipDistance/CullDistance to be " + "used for variables with Output storage class if execution model is " + "Fragment.", + SpvExecutionModelFragment, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelFragment: + case SpvExecutionModelVertex: { + if (spv_result_t error = ValidateF32Arr( + decoration, built_in_inst, /* Any number of components */ 0, + [this, &decoration, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.params()[0] == SpvBuiltInClipDistance) + ? 4191 + : 4200; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit float array. " + << message; + })) { + return error; + } + break; + } + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: + case SpvExecutionModelGeometry: + case SpvExecutionModelMeshNV: { + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // The outer level of array is applied on the variable. + if (spv_result_t error = ValidateF32Arr( + decoration, built_in_inst, /* Any number of components */ 0, + [this, &decoration, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.params()[0] == SpvBuiltInClipDistance) + ? 4191 + : 4200; + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit float array. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedF32Arr( + decoration, built_in_inst, /* Any number of components */ 0, + [this, &decoration, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.params()[0] == SpvBuiltInClipDistance) + ? 4191 + : 4200; + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit float array. " + << message; + })) { + return error; + } + } + break; + } + + default: { + uint32_t vuid = + (decoration.params()[0] == SpvBuiltInClipDistance) ? 4187 : 4196; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with Fragment, Vertex, " + "TessellationControl, TessellationEvaluation or Geometry " + "execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateClipOrCullDistanceAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFragCoordAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 4, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4212) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn FragCoord " + "variable needs to be a 4-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFragCoordAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFragCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4211) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FragCoord to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4210) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FragCoord to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragCoordAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFragDepthAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4215) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn FragDepth " + "variable needs to be a 32-bit float scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFragDepthAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFragDepthAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4214) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FragDepth to be only used for " + "variables with Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4213) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FragDepth to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + + for (const uint32_t entry_point : *entry_points_) { + // Every entry point from which this function is called needs to have + // Execution Mode DepthReplacing. + const auto* modes = _.GetExecutionModes(entry_point); + if (!modes || !modes->count(SpvExecutionModeDepthReplacing)) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4216) + << spvLogStringForEnv(_.context()->target_env) + << " spec requires DepthReplacing execution mode to be " + "declared when using BuiltIn FragDepth. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFragDepthAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateFrontFacingAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateBool( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4231) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn FrontFacing " + "variable needs to be a bool scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateFrontFacingAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateFrontFacingAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4230) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FrontFacing to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4229) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn FrontFacing to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateFrontFacingAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateHelperInvocationAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateBool( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4241) + << "According to the Vulkan spec BuiltIn HelperInvocation " + "variable needs to be a bool scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateHelperInvocationAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateHelperInvocationAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4240) + << "Vulkan spec allows BuiltIn HelperInvocation to be only used " + "for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4239) + << "Vulkan spec allows BuiltIn HelperInvocation to be used only " + "with Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateHelperInvocationAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateInvocationIdAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4259) + << "According to the Vulkan spec BuiltIn InvocationId " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateInvocationIdAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateInvocationIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4258) + << "Vulkan spec allows BuiltIn InvocationId to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelTessellationControl && + execution_model != SpvExecutionModelGeometry) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4257) + << "Vulkan spec allows BuiltIn InvocationId to be used only " + "with TessellationControl or Geometry execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateInvocationIdAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateInstanceIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4265) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn InstanceIndex " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateInstanceIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateInstanceIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4264) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn InstanceIndex to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelVertex) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4263) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn InstanceIndex to be used only " + "with Vertex execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateInstanceIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePatchVerticesAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4310) + << "According to the Vulkan spec BuiltIn PatchVertices " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidatePatchVerticesAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePatchVerticesAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4309) + << "Vulkan spec allows BuiltIn PatchVertices to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelTessellationControl && + execution_model != SpvExecutionModelTessellationEvaluation) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4308) + << "Vulkan spec allows BuiltIn PatchVertices to be used only " + "with TessellationControl or TessellationEvaluation " + "execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePatchVerticesAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePointCoordAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 2, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4313) + << "According to the Vulkan spec BuiltIn PointCoord " + "variable needs to be a 2-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidatePointCoordAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePointCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4312) + << "Vulkan spec allows BuiltIn PointCoord to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4311) + << "Vulkan spec allows BuiltIn PointCoord to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePointCoordAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePointSizeAtDefinition( + const Decoration& decoration, const Instruction& inst) { + // Seed at reference checks with this built-in. + return ValidatePointSizeAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePointSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4316) + << "Vulkan spec allows BuiltIn PointSize to be only used for " + "variables with Input or Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == SpvStorageClassInput) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + std::string( + _.VkErrorID(4315) + + "Vulkan spec doesn't allow BuiltIn PointSize to be used for " + "variables with Input storage class if execution model is " + "Vertex."), + SpvExecutionModelVertex, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelVertex: { + if (spv_result_t error = ValidateF32( + decoration, built_in_inst, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4317) + << "According to the Vulkan spec BuiltIn PointSize " + "variable needs to be a 32-bit float scalar. " + << message; + })) { + return error; + } + break; + } + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: + case SpvExecutionModelGeometry: + case SpvExecutionModelMeshNV: { + // PointSize can be a per-vertex variable for tessellation control, + // tessellation evaluation and geometry shader stages. In such cases + // variables will have an array of 32-bit floats. + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // The array is on the variable, so this must be a 32-bit float. + if (spv_result_t error = ValidateF32( + decoration, built_in_inst, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(4317) + << "According to the Vulkan spec BuiltIn " + "PointSize variable needs to be a 32-bit " + "float scalar. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedF32( + decoration, built_in_inst, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(4317) + << "According to the Vulkan spec BuiltIn " + "PointSize variable needs to be a 32-bit " + "float scalar. " + << message; + })) { + return error; + } + } + break; + } + + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4314) + << "Vulkan spec allows BuiltIn PointSize to be used only with " + "Vertex, TessellationControl, TessellationEvaluation or " + "Geometry execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePointSizeAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePositionAtDefinition( + const Decoration& decoration, const Instruction& inst) { + // Seed at reference checks with this built-in. + return ValidatePositionAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePositionAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn Position to be only used for " + "variables with Input or Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == SpvStorageClassInput) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + std::string(_.VkErrorID(4320) + + "Vulkan spec doesn't allow BuiltIn Position to be used " + "for variables " + "with Input storage class if execution model is Vertex."), + SpvExecutionModelVertex, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelVertex: { + if (spv_result_t error = ValidateF32Vec( + decoration, built_in_inst, 4, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4321) + << "According to the Vulkan spec BuiltIn Position " + "variable needs to be a 4-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + break; + } + case SpvExecutionModelGeometry: + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: + case SpvExecutionModelMeshNV: { + // Position can be a per-vertex variable for tessellation control, + // tessellation evaluation, geometry and mesh shader stages. In such + // cases variables will have an array of 4-component 32-bit float + // vectors. + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // The array is on the variable, so this must be a 4-component + // 32-bit float vector. + if (spv_result_t error = ValidateF32Vec( + decoration, built_in_inst, 4, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(4321) + << "According to the Vulkan spec BuiltIn Position " + "variable needs to be a 4-component 32-bit " + "float vector. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedF32Vec( + decoration, built_in_inst, 4, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, + &referenced_from_inst) + << _.VkErrorID(4321) + << "According to the Vulkan spec BuiltIn Position " + "variable needs to be a 4-component 32-bit " + "float vector. " + << message; + })) { + return error; + } + } + break; + } + + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4318) + << "Vulkan spec allows BuiltIn Position to be used only " + "with Vertex, TessellationControl, TessellationEvaluation" + " or Geometry execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (spvIsWebGPUEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "WebGPU spec allows BuiltIn Position to be only used for " + "variables with Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelVertex: { + if (spv_result_t error = ValidateF32Vec( + decoration, built_in_inst, 4, + [this, &referenced_from_inst]( + const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "According to the WebGPU spec BuiltIn Position " + "variable needs to be a 4-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + break; + } + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "WebGPU spec allows BuiltIn Position to be used only " + "with the Vertex execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePositionAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + // PrimitiveId can be a per-primitive variable for mesh shader stage. + // In such cases variable will have an array of 32-bit integers. + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // This must be a 32-bit int scalar. + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4337) + << "According to the Vulkan spec BuiltIn PrimitiveId " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4337) + << "According to the Vulkan spec BuiltIn PrimitiveId " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + } + + // Seed at reference checks with this built-in. + return ValidatePrimitiveIdAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn PrimitiveId to be only used for " + "variables with Input or Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == SpvStorageClassOutput) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + std::string( + _.VkErrorID(4334) + + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "TessellationControl."), + SpvExecutionModelTessellationControl, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + std::string( + _.VkErrorID(4334) + + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "TessellationEvaluation."), + SpvExecutionModelTessellationEvaluation, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + std::string( + _.VkErrorID(4334) + + "Vulkan spec doesn't allow BuiltIn PrimitiveId to be used for " + "variables with Output storage class if execution model is " + "Fragment."), + SpvExecutionModelFragment, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelFragment: + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: + case SpvExecutionModelGeometry: + case SpvExecutionModelMeshNV: + case SpvExecutionModelRayGenerationNV: + case SpvExecutionModelIntersectionNV: + case SpvExecutionModelAnyHitNV: + case SpvExecutionModelClosestHitNV: + case SpvExecutionModelMissNV: + case SpvExecutionModelCallableNV: { + // Ok. + break; + } + + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4330) + << "Vulkan spec allows BuiltIn PrimitiveId to be used only " + "with Fragment, TessellationControl, " + "TessellationEvaluation or Geometry execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidatePrimitiveIdAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSampleIdAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4356) + << "According to the Vulkan spec BuiltIn SampleId " + "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateSampleIdAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateSampleIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4355) + << "Vulkan spec allows BuiltIn SampleId to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4354) + << "Vulkan spec allows BuiltIn SampleId to be used only with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateSampleIdAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSampleMaskAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32Arr( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4359) + << "According to the Vulkan spec BuiltIn SampleMask " + "variable needs to be a 32-bit int array. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateSampleMaskAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateSampleMaskAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4358) + << "Vulkan spec allows BuiltIn SampleMask to be only used for " + "variables with Input or Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4357) + << "Vulkan spec allows BuiltIn SampleMask to be used only " + "with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateSampleMaskAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSamplePositionAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 2, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4362) + << "According to the Vulkan spec BuiltIn SamplePosition " + "variable needs to be a 2-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateSamplePositionAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateSamplePositionAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4361) + << "Vulkan spec allows BuiltIn SamplePosition to be only used " + "for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4360) + << "Vulkan spec allows BuiltIn SamplePosition to be used only " + "with " + "Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateSamplePositionAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateTessCoordAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Vec( + decoration, inst, 3, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4389) + << "According to the Vulkan spec BuiltIn TessCoord " + "variable needs to be a 3-component 32-bit float " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateTessCoordAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateTessCoordAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4388) + << "Vulkan spec allows BuiltIn TessCoord to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelTessellationEvaluation) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4387) + << "Vulkan spec allows BuiltIn TessCoord to be used only with " + "TessellationEvaluation execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateTessCoordAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateTessLevelOuterAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Arr( + decoration, inst, 4, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4393) + << "According to the Vulkan spec BuiltIn TessLevelOuter " + "variable needs to be a 4-component 32-bit float " + "array. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateTessLevelAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateTessLevelInnerAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateF32Arr( + decoration, inst, 2, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4397) + << "According to the Vulkan spec BuiltIn TessLevelOuter " + "variable needs to be a 2-component 32-bit float " + "array. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateTessLevelAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateTessLevelAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = decoration.params()[0]; + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input or Output storage " + "class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == SpvStorageClassInput) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " + "used " + "for variables with Input storage class if execution model is " + "TessellationControl.", + SpvExecutionModelTessellationControl, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + if (storage_class == SpvStorageClassOutput) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + "Vulkan spec doesn't allow TessLevelOuter/TessLevelInner to be " + "used " + "for variables with Output storage class if execution model is " + "TessellationEvaluation.", + SpvExecutionModelTessellationEvaluation, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: { + // Ok. + break; + } + + default: { + uint32_t vuid = (operand == SpvBuiltInTessLevelOuter) ? 4390 : 4394; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with TessellationControl or " + "TessellationEvaluation execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateTessLevelAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateVertexIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4400) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn VertexIndex variable needs to be a " + "32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateVertexIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateVertexIdOrInstanceIdAtDefinition( + const Decoration& decoration, const Instruction& inst) { + const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]); + bool allow_instance_id = + (_.HasCapability(SpvCapabilityRayTracingNV) || + _.HasCapability(SpvCapabilityRayTracingProvisionalKHR)) && + label == SpvBuiltInInstanceId; + + if (spvIsVulkanEnv(_.context()->target_env) && !allow_instance_id) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "Vulkan spec doesn't allow BuiltIn VertexId/InstanceId " + "to be used."; + } + + if (label == SpvBuiltInInstanceId) { + return ValidateInstanceIdAtReference(decoration, inst, inst, inst); + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateInstanceIdAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelIntersectionNV: + case SpvExecutionModelClosestHitNV: + case SpvExecutionModelAnyHitNV: + // Do nothing, valid stages + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn InstanceId to be used " + "only with IntersectionNV, ClosestHitNV and AnyHitNV " + "execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst); + break; + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateInstanceIdAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsWebGPUEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "According to the WebGPU spec BuiltIn " + "LocalInvocationIndex variable needs to be a 32-bit " + "int." + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateLocalInvocationIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateLocalInvocationIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsWebGPUEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "WebGPU spec allows BuiltIn LocalInvocationIndex to be only " + "used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelGLCompute) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "WebGPU spec allows BuiltIn VertexIndex to be used only " + "with GLCompute execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateLocalInvocationIndexAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateVertexIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4399) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn VertexIndex to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelVertex) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4398) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn VertexIndex to be used only with " + "Vertex execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateVertexIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + // This can be a per-primitive variable for mesh shader stage. + // In such cases variable will have an array of 32-bit integers. + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + // This must be a 32-bit int scalar. + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &decoration, + &inst](const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.params()[0] == SpvBuiltInLayer) ? 4276 : 4408; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]) + << "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } else { + if (spv_result_t error = ValidateOptionalArrayedI32( + decoration, inst, + [this, &decoration, + &inst](const std::string& message) -> spv_result_t { + uint32_t vuid = + (decoration.params()[0] == SpvBuiltInLayer) ? 4276 : 4408; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_BUILT_IN, decoration.params()[0]) + << "variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + } + + // Seed at reference checks with this built-in. + return ValidateLayerOrViewportIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateLayerOrViewportIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = decoration.params()[0]; + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input or Output storage " + "class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + if (storage_class == SpvStorageClassInput) { + assert(function_id_ == 0); + for (const auto em : + {SpvExecutionModelVertex, SpvExecutionModelTessellationEvaluation, + SpvExecutionModelGeometry, SpvExecutionModelMeshNV}) { + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + std::string( + _.VkErrorID((operand == SpvBuiltInLayer) ? 4274 : 4406) + + "Vulkan spec doesn't allow BuiltIn Layer and " + "ViewportIndex to be " + "used for variables with Input storage class if " + "execution model is Vertex, TessellationEvaluation, " + "Geometry, or MeshNV."), + em, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + } + + if (storage_class == SpvStorageClassOutput) { + assert(function_id_ == 0); + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateNotCalledWithExecutionModel, this, + std::string(_.VkErrorID((operand == SpvBuiltInLayer) ? 4275 : 4407) + + "Vulkan spec doesn't allow BuiltIn Layer and " + "ViewportIndex to be " + "used for variables with Output storage class if " + "execution model is " + "Fragment."), + SpvExecutionModelFragment, decoration, built_in_inst, + referenced_from_inst, std::placeholders::_1)); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelGeometry: + case SpvExecutionModelFragment: + case SpvExecutionModelMeshNV: + // Ok. + break; + case SpvExecutionModelVertex: + case SpvExecutionModelTessellationEvaluation: { + if (!_.HasCapability(SpvCapabilityShaderViewportIndexLayerEXT)) { + if (operand == SpvBuiltInViewportIndex && + _.HasCapability(SpvCapabilityShaderViewportIndex)) + break; // Ok + if (operand == SpvBuiltInLayer && + _.HasCapability(SpvCapabilityShaderLayer)) + break; // Ok + + const char* capability = "ShaderViewportIndexLayerEXT"; + + if (operand == SpvBuiltInViewportIndex) + capability = "ShaderViewportIndexLayerEXT or ShaderViewportIndex"; + if (operand == SpvBuiltInLayer) + capability = "ShaderViewportIndexLayerEXT or ShaderLayer"; + + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << "Using BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " in Vertex or Tessellation execution model requires the " + << capability << " capability."; + } + break; + } + default: { + uint32_t vuid = (operand == SpvBuiltInLayer) ? 4272 : 4404; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with Vertex, TessellationEvaluation, " + "Geometry, or Fragment execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateLayerOrViewportIndexAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32Vec( + decoration, inst, 3, + [this, &decoration, + &inst](const std::string& message) -> spv_result_t { + uint32_t operand = decoration.params()[0]; + uint32_t vuid = 0; + switch (operand) { + case SpvBuiltInGlobalInvocationId: + vuid = 4238; + break; + case SpvBuiltInLocalInvocationId: + vuid = 4283; + break; + case SpvBuiltInNumWorkgroups: + vuid = 4298; + break; + case SpvBuiltInWorkgroupId: + vuid = 4424; + break; + }; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " variable needs to be a 3-component 32-bit int " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateComputeShaderI32Vec3InputAtReference(decoration, inst, inst, + inst); +} + +spv_result_t BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = decoration.params()[0]; + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + uint32_t vuid = 0; + switch (operand) { + case SpvBuiltInGlobalInvocationId: + vuid = 4237; + break; + case SpvBuiltInLocalInvocationId: + vuid = 4282; + break; + case SpvBuiltInNumWorkgroups: + vuid = 4297; + break; + case SpvBuiltInWorkgroupId: + vuid = 4423; + break; + }; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute || + execution_model == SpvExecutionModelTaskNV || + execution_model == SpvExecutionModelMeshNV; + bool has_webgpu_model = execution_model == SpvExecutionModelGLCompute; + if ((spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) || + (spvIsWebGPUEnv(_.context()->target_env) && !has_webgpu_model)) { + uint32_t vuid = 0; + switch (operand) { + case SpvBuiltInGlobalInvocationId: + vuid = 4236; + break; + case SpvBuiltInLocalInvocationId: + vuid = 4281; + break; + case SpvBuiltInNumWorkgroups: + vuid = 4296; + break; + case SpvBuiltInWorkgroupId: + vuid = 4422; + break; + }; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be used only with GLCompute execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateComputeShaderI32Vec3InputAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateComputeI32InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " cannot be used as a member decoration "; + } + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &decoration, + &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int " + "vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateComputeI32InputAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateComputeI32InputAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + bool has_vulkan_model = execution_model == SpvExecutionModelGLCompute || + execution_model == SpvExecutionModelTaskNV || + execution_model == SpvExecutionModelMeshNV; + if (spvIsVulkanEnv(_.context()->target_env) && !has_vulkan_model) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be used only with GLCompute execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateComputeI32InputAtReference, this, + decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " cannot be used as a member decoration "; + } + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &decoration, + &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int. " << message; + })) { + return error; + } + + const SpvStorageClass storage_class = GetStorageClass(inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, inst, inst, inst) << " " + << GetStorageClassDesc(inst); + } + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateI32Vec4InputAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (decoration.struct_member_index() != Decoration::kInvalidMember) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " cannot be used as a member decoration "; + } + if (spv_result_t error = ValidateI32Vec( + decoration, inst, 4, + [this, &decoration, + &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 4-component 32-bit int " + "vector. " + << message; + })) { + return error; + } + + const SpvStorageClass storage_class = GetStorageClass(inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, inst, inst, inst) << " " + << GetStorageClassDesc(inst); + } + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + if (spvIsVulkanEnv(_.context()->target_env) && + !spvOpcodeIsConstant(inst.opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4426) + << "Vulkan spec requires BuiltIn WorkgroupSize to be a " + "constant. " + << GetIdDesc(inst) << " is not a constant."; + } + + if (spv_result_t error = ValidateI32Vec( + decoration, inst, 3, + [this, &inst](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4427) << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn WorkgroupSize variable needs to be a " + "3-component 32-bit int vector. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateWorkgroupSizeAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateWorkgroupSizeAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelGLCompute) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4425) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be used only with GLCompute execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateWorkgroupSizeAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + uint32_t vuid = (decoration.params()[0] == SpvBuiltInBaseInstance) + ? 4183 + : 4186; + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(vuid) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateBaseInstanceOrVertexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = decoration.params()[0]; + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + uint32_t vuid = (operand == SpvBuiltInBaseInstance) ? 4182 : 4185; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelVertex) { + uint32_t vuid = (operand == SpvBuiltInBaseInstance) ? 4181 : 4184; + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(vuid) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with Vertex execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidateBaseInstanceOrVertexAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateDrawIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4209) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateDrawIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateDrawIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = decoration.params()[0]; + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4208) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelVertex && + execution_model != SpvExecutionModelMeshNV && + execution_model != SpvExecutionModelTaskNV) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4207) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be used only with Vertex, MeshNV, or TaskNV execution " + "model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateDrawIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateViewIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4403) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateViewIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateViewIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = decoration.params()[0]; + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4402) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model == SpvExecutionModelGLCompute) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4401) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be not be used with GLCompute execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateViewIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateDeviceIndexAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4206) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + return ValidateDeviceIndexAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateDeviceIndexAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + uint32_t operand = decoration.params()[0]; + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4205) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + operand) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateDeviceIndexAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "According to the " + << spvLogStringForEnv(_.context()->target_env) + << " spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateSMBuiltinsAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateSMBuiltinsAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << spvLogStringForEnv(_.context()->target_env) + << " spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be only used for " + "variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateSMBuiltinsAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4486) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidatePrimitiveShadingRateAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidatePrimitiveShadingRateAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassOutput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4485) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be only used for variables with Output storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + switch (execution_model) { + case SpvExecutionModelVertex: + case SpvExecutionModelGeometry: + case SpvExecutionModelMeshNV: + break; + default: { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4484) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be used only with Vertex, Geometry, or MeshNV " + "execution models. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back( + std::bind(&BuiltInsValidator::ValidatePrimitiveShadingRateAtReference, + this, decoration, built_in_inst, referenced_from_inst, + std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateShadingRateAtDefinition( + const Decoration& decoration, const Instruction& inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + if (spv_result_t error = ValidateI32( + decoration, inst, + [this, &inst, + &decoration](const std::string& message) -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << _.VkErrorID(4492) + << "According to the Vulkan spec BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " variable needs to be a 32-bit int scalar. " + << message; + })) { + return error; + } + } + + // Seed at reference checks with this built-in. + return ValidateShadingRateAtReference(decoration, inst, inst, inst); +} + +spv_result_t BuiltInsValidator::ValidateShadingRateAtReference( + const Decoration& decoration, const Instruction& built_in_inst, + const Instruction& referenced_inst, + const Instruction& referenced_from_inst) { + if (spvIsVulkanEnv(_.context()->target_env)) { + const SpvStorageClass storage_class = GetStorageClass(referenced_from_inst); + if (storage_class != SpvStorageClassMax && + storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4491) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be only used for variables with Input storage class. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst) + << " " << GetStorageClassDesc(referenced_from_inst); + } + + for (const SpvExecutionModel execution_model : execution_models_) { + if (execution_model != SpvExecutionModelFragment) { + return _.diag(SPV_ERROR_INVALID_DATA, &referenced_from_inst) + << _.VkErrorID(4490) << "Vulkan spec allows BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]) + << " to be used only with the Fragment execution model. " + << GetReferenceDesc(decoration, built_in_inst, referenced_inst, + referenced_from_inst, execution_model); + } + } + } + + if (function_id_ == 0) { + // Propagate this rule to all dependant ids in the global scope. + id_to_at_reference_checks_[referenced_from_inst.id()].push_back(std::bind( + &BuiltInsValidator::ValidateShadingRateAtReference, this, decoration, + built_in_inst, referenced_from_inst, std::placeholders::_1)); + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateSingleBuiltInAtDefinition( + const Decoration& decoration, const Instruction& inst) { + const SpvBuiltIn label = SpvBuiltIn(decoration.params()[0]); + + // Builtins can only be applied to variables, structures or constants. + auto target_opcode = inst.opcode(); + if (target_opcode != SpvOpTypeStruct && target_opcode != SpvOpVariable && + !spvOpcodeIsConstant(target_opcode)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "BuiltIns can only target variables, structs or constants"; + } + + if (!spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + // Early return. All currently implemented rules are based on Vulkan or + // WebGPU spec. + // + // TODO: If you are adding validation rules for environments other than + // Vulkan or WebGPU (or general rules which are not environment + // independent), then you need to modify or remove this condition. Consider + // also adding early returns into BuiltIn-specific rules, so that the system + // doesn't spawn new rules which don't do anything. + return SPV_SUCCESS; + } + + if (spvIsWebGPUEnv(_.context()->target_env) && + !IsBuiltInValidForWebGPU(label)) { + return _.diag(SPV_ERROR_INVALID_DATA, &inst) + << "WebGPU does not allow BuiltIn " + << _.grammar().lookupOperandName(SPV_OPERAND_TYPE_BUILT_IN, + decoration.params()[0]); + } + + // If you are adding a new BuiltIn enum, please register it here. + // If the newly added enum has validation rules associated with it + // consider leaving a TODO and/or creating an issue. + switch (label) { + case SpvBuiltInClipDistance: + case SpvBuiltInCullDistance: { + return ValidateClipOrCullDistanceAtDefinition(decoration, inst); + } + case SpvBuiltInFragCoord: { + return ValidateFragCoordAtDefinition(decoration, inst); + } + case SpvBuiltInFragDepth: { + return ValidateFragDepthAtDefinition(decoration, inst); + } + case SpvBuiltInFrontFacing: { + return ValidateFrontFacingAtDefinition(decoration, inst); + } + case SpvBuiltInGlobalInvocationId: + case SpvBuiltInLocalInvocationId: + case SpvBuiltInNumWorkgroups: + case SpvBuiltInWorkgroupId: { + return ValidateComputeShaderI32Vec3InputAtDefinition(decoration, inst); + } + case SpvBuiltInHelperInvocation: { + return ValidateHelperInvocationAtDefinition(decoration, inst); + } + case SpvBuiltInInvocationId: { + return ValidateInvocationIdAtDefinition(decoration, inst); + } + case SpvBuiltInInstanceIndex: { + return ValidateInstanceIndexAtDefinition(decoration, inst); + } + case SpvBuiltInLayer: + case SpvBuiltInViewportIndex: { + return ValidateLayerOrViewportIndexAtDefinition(decoration, inst); + } + case SpvBuiltInPatchVertices: { + return ValidatePatchVerticesAtDefinition(decoration, inst); + } + case SpvBuiltInPointCoord: { + return ValidatePointCoordAtDefinition(decoration, inst); + } + case SpvBuiltInPointSize: { + return ValidatePointSizeAtDefinition(decoration, inst); + } + case SpvBuiltInPosition: { + return ValidatePositionAtDefinition(decoration, inst); + } + case SpvBuiltInPrimitiveId: { + return ValidatePrimitiveIdAtDefinition(decoration, inst); + } + case SpvBuiltInSampleId: { + return ValidateSampleIdAtDefinition(decoration, inst); + } + case SpvBuiltInSampleMask: { + return ValidateSampleMaskAtDefinition(decoration, inst); + } + case SpvBuiltInSamplePosition: { + return ValidateSamplePositionAtDefinition(decoration, inst); + } + case SpvBuiltInSubgroupId: + case SpvBuiltInNumSubgroups: { + return ValidateComputeI32InputAtDefinition(decoration, inst); + } + case SpvBuiltInSubgroupLocalInvocationId: + case SpvBuiltInSubgroupSize: { + return ValidateI32InputAtDefinition(decoration, inst); + } + case SpvBuiltInSubgroupEqMask: + case SpvBuiltInSubgroupGeMask: + case SpvBuiltInSubgroupGtMask: + case SpvBuiltInSubgroupLeMask: + case SpvBuiltInSubgroupLtMask: { + return ValidateI32Vec4InputAtDefinition(decoration, inst); + } + case SpvBuiltInTessCoord: { + return ValidateTessCoordAtDefinition(decoration, inst); + } + case SpvBuiltInTessLevelOuter: { + return ValidateTessLevelOuterAtDefinition(decoration, inst); + } + case SpvBuiltInTessLevelInner: { + return ValidateTessLevelInnerAtDefinition(decoration, inst); + } + case SpvBuiltInVertexIndex: { + return ValidateVertexIndexAtDefinition(decoration, inst); + } + case SpvBuiltInWorkgroupSize: { + return ValidateWorkgroupSizeAtDefinition(decoration, inst); + } + case SpvBuiltInVertexId: + case SpvBuiltInInstanceId: { + return ValidateVertexIdOrInstanceIdAtDefinition(decoration, inst); + } + case SpvBuiltInLocalInvocationIndex: { + return ValidateLocalInvocationIndexAtDefinition(decoration, inst); + } + case SpvBuiltInWarpsPerSMNV: + case SpvBuiltInSMCountNV: + case SpvBuiltInWarpIDNV: + case SpvBuiltInSMIDNV: { + return ValidateSMBuiltinsAtDefinition(decoration, inst); + } + case SpvBuiltInBaseInstance: + case SpvBuiltInBaseVertex: { + return ValidateBaseInstanceOrVertexAtDefinition(decoration, inst); + } + case SpvBuiltInDrawIndex: { + return ValidateDrawIndexAtDefinition(decoration, inst); + } + case SpvBuiltInViewIndex: { + return ValidateViewIndexAtDefinition(decoration, inst); + } + case SpvBuiltInDeviceIndex: { + return ValidateDeviceIndexAtDefinition(decoration, inst); + } + case SpvBuiltInWorkDim: + case SpvBuiltInGlobalSize: + case SpvBuiltInEnqueuedWorkgroupSize: + case SpvBuiltInGlobalOffset: + case SpvBuiltInGlobalLinearId: + case SpvBuiltInSubgroupMaxSize: + case SpvBuiltInNumEnqueuedSubgroups: + case SpvBuiltInBaryCoordNoPerspAMD: + case SpvBuiltInBaryCoordNoPerspCentroidAMD: + case SpvBuiltInBaryCoordNoPerspSampleAMD: + case SpvBuiltInBaryCoordSmoothAMD: + case SpvBuiltInBaryCoordSmoothCentroidAMD: + case SpvBuiltInBaryCoordSmoothSampleAMD: + case SpvBuiltInBaryCoordPullModelAMD: + case SpvBuiltInFragStencilRefEXT: + case SpvBuiltInViewportMaskNV: + case SpvBuiltInSecondaryPositionNV: + case SpvBuiltInSecondaryViewportMaskNV: + case SpvBuiltInPositionPerViewNV: + case SpvBuiltInViewportMaskPerViewNV: + case SpvBuiltInFullyCoveredEXT: + case SpvBuiltInMax: + case SpvBuiltInTaskCountNV: + case SpvBuiltInPrimitiveCountNV: + case SpvBuiltInPrimitiveIndicesNV: + case SpvBuiltInClipDistancePerViewNV: + case SpvBuiltInCullDistancePerViewNV: + case SpvBuiltInLayerPerViewNV: + case SpvBuiltInMeshViewCountNV: + case SpvBuiltInMeshViewIndicesNV: + case SpvBuiltInBaryCoordNV: + case SpvBuiltInBaryCoordNoPerspNV: + case SpvBuiltInFragmentSizeNV: // alias SpvBuiltInFragSizeEXT + case SpvBuiltInInvocationsPerPixelNV: // alias + // SpvBuiltInFragInvocationCountEXT + case SpvBuiltInLaunchIdNV: + case SpvBuiltInLaunchSizeNV: + case SpvBuiltInWorldRayOriginNV: + case SpvBuiltInWorldRayDirectionNV: + case SpvBuiltInObjectRayOriginNV: + case SpvBuiltInObjectRayDirectionNV: + case SpvBuiltInRayTminNV: + case SpvBuiltInRayTmaxNV: + case SpvBuiltInInstanceCustomIndexNV: + case SpvBuiltInObjectToWorldNV: + case SpvBuiltInWorldToObjectNV: + case SpvBuiltInHitTNV: + case SpvBuiltInHitKindNV: + case SpvBuiltInIncomingRayFlagsNV: + case SpvBuiltInRayGeometryIndexKHR: { + // No validation rules (for the moment). + break; + + case SpvBuiltInPrimitiveShadingRateKHR: + return ValidatePrimitiveShadingRateAtDefinition(decoration, inst); + case SpvBuiltInShadingRateKHR: + return ValidateShadingRateAtDefinition(decoration, inst); + } + } + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::ValidateBuiltInsAtDefinition() { + for (const auto& kv : _.id_decorations()) { + const uint32_t id = kv.first; + const auto& decorations = kv.second; + if (decorations.empty()) { + continue; + } + + const Instruction* inst = _.FindDef(id); + assert(inst); + + for (const auto& decoration : kv.second) { + if (decoration.dec_type() != SpvDecorationBuiltIn) { + continue; + } + + if (spv_result_t error = + ValidateSingleBuiltInAtDefinition(decoration, *inst)) { + return error; + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t BuiltInsValidator::Run() { + // First pass: validate all built-ins at definition and seed + // id_to_at_reference_checks_ with built-ins. + if (auto error = ValidateBuiltInsAtDefinition()) { + return error; + } + + if (id_to_at_reference_checks_.empty()) { + // No validation tasks were seeded. Nothing else to do. + return SPV_SUCCESS; + } + + // Second pass: validate every id reference in the module using + // rules in id_to_at_reference_checks_. + for (const Instruction& inst : _.ordered_instructions()) { + Update(inst); + + std::set already_checked; + + for (const auto& operand : inst.operands()) { + if (!spvIsIdType(operand.type)) { + // Not id. + continue; + } + + const uint32_t id = inst.word(operand.offset); + if (id == inst.id()) { + // No need to check result id. + continue; + } + + if (!already_checked.insert(id).second) { + // The instruction has already referenced this id. + continue; + } + + // Instruction references the id. Run all checks associated with the id + // on the instruction. id_to_at_reference_checks_ can be modified in the + // process, iterators are safe because it's a tree-based map. + const auto it = id_to_at_reference_checks_.find(id); + if (it != id_to_at_reference_checks_.end()) { + for (const auto& check : it->second) { + if (spv_result_t error = check(inst)) { + return error; + } + } + } + } + } + + return SPV_SUCCESS; +} + +} // namespace + +// Validates correctness of built-in variables. +spv_result_t ValidateBuiltIns(ValidationState_t& _) { + BuiltInsValidator validator(_); + return validator.Run(); +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_capability.cpp b/third_party/spirv-tools/source/val/validate_capability.cpp new file mode 100644 index 0000000..4b98bc1 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_capability.cpp @@ -0,0 +1,382 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates OpCapability instruction. + +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +bool IsSupportGuaranteedVulkan_1_0(uint32_t capability) { + switch (capability) { + case SpvCapabilityMatrix: + case SpvCapabilityShader: + case SpvCapabilityInputAttachment: + case SpvCapabilitySampled1D: + case SpvCapabilityImage1D: + case SpvCapabilitySampledBuffer: + case SpvCapabilityImageBuffer: + case SpvCapabilityImageQuery: + case SpvCapabilityDerivativeControl: + return true; + } + return false; +} + +bool IsSupportGuaranteedVulkan_1_1(uint32_t capability) { + if (IsSupportGuaranteedVulkan_1_0(capability)) return true; + switch (capability) { + case SpvCapabilityDeviceGroup: + case SpvCapabilityMultiView: + return true; + } + return false; +} + +bool IsSupportGuaranteedVulkan_1_2(uint32_t capability) { + if (IsSupportGuaranteedVulkan_1_1(capability)) return true; + switch (capability) { + case SpvCapabilityShaderNonUniform: + return true; + } + return false; +} + +bool IsSupportOptionalVulkan_1_0(uint32_t capability) { + switch (capability) { + case SpvCapabilityGeometry: + case SpvCapabilityTessellation: + case SpvCapabilityFloat64: + case SpvCapabilityInt64: + case SpvCapabilityInt16: + case SpvCapabilityTessellationPointSize: + case SpvCapabilityGeometryPointSize: + case SpvCapabilityImageGatherExtended: + case SpvCapabilityStorageImageMultisample: + case SpvCapabilityUniformBufferArrayDynamicIndexing: + case SpvCapabilitySampledImageArrayDynamicIndexing: + case SpvCapabilityStorageBufferArrayDynamicIndexing: + case SpvCapabilityStorageImageArrayDynamicIndexing: + case SpvCapabilityClipDistance: + case SpvCapabilityCullDistance: + case SpvCapabilityImageCubeArray: + case SpvCapabilitySampleRateShading: + case SpvCapabilitySparseResidency: + case SpvCapabilityMinLod: + case SpvCapabilitySampledCubeArray: + case SpvCapabilityImageMSArray: + case SpvCapabilityStorageImageExtendedFormats: + case SpvCapabilityInterpolationFunction: + case SpvCapabilityStorageImageReadWithoutFormat: + case SpvCapabilityStorageImageWriteWithoutFormat: + case SpvCapabilityMultiViewport: + case SpvCapabilityInt64Atomics: + case SpvCapabilityTransformFeedback: + case SpvCapabilityGeometryStreams: + case SpvCapabilityFloat16: + case SpvCapabilityInt8: + return true; + } + return false; +} + +bool IsSupportOptionalVulkan_1_1(uint32_t capability) { + if (IsSupportOptionalVulkan_1_0(capability)) return true; + + switch (capability) { + case SpvCapabilityGroupNonUniform: + case SpvCapabilityGroupNonUniformVote: + case SpvCapabilityGroupNonUniformArithmetic: + case SpvCapabilityGroupNonUniformBallot: + case SpvCapabilityGroupNonUniformShuffle: + case SpvCapabilityGroupNonUniformShuffleRelative: + case SpvCapabilityGroupNonUniformClustered: + case SpvCapabilityGroupNonUniformQuad: + case SpvCapabilityDrawParameters: + // Alias SpvCapabilityStorageBuffer16BitAccess. + case SpvCapabilityStorageUniformBufferBlock16: + // Alias SpvCapabilityUniformAndStorageBuffer16BitAccess. + case SpvCapabilityStorageUniform16: + case SpvCapabilityStoragePushConstant16: + case SpvCapabilityStorageInputOutput16: + case SpvCapabilityDeviceGroup: + case SpvCapabilityMultiView: + case SpvCapabilityVariablePointersStorageBuffer: + case SpvCapabilityVariablePointers: + return true; + } + return false; +} + +bool IsSupportOptionalVulkan_1_2(uint32_t capability) { + if (IsSupportOptionalVulkan_1_1(capability)) return true; + + switch (capability) { + case SpvCapabilityDenormPreserve: + case SpvCapabilityDenormFlushToZero: + case SpvCapabilitySignedZeroInfNanPreserve: + case SpvCapabilityRoundingModeRTE: + case SpvCapabilityRoundingModeRTZ: + case SpvCapabilityVulkanMemoryModel: + case SpvCapabilityVulkanMemoryModelDeviceScope: + case SpvCapabilityStorageBuffer8BitAccess: + case SpvCapabilityUniformAndStorageBuffer8BitAccess: + case SpvCapabilityStoragePushConstant8: + case SpvCapabilityShaderViewportIndex: + case SpvCapabilityShaderLayer: + case SpvCapabilityPhysicalStorageBufferAddresses: + case SpvCapabilityRuntimeDescriptorArray: + case SpvCapabilityUniformTexelBufferArrayDynamicIndexing: + case SpvCapabilityStorageTexelBufferArrayDynamicIndexing: + case SpvCapabilityUniformBufferArrayNonUniformIndexing: + case SpvCapabilitySampledImageArrayNonUniformIndexing: + case SpvCapabilityStorageBufferArrayNonUniformIndexing: + case SpvCapabilityStorageImageArrayNonUniformIndexing: + case SpvCapabilityInputAttachmentArrayNonUniformIndexing: + case SpvCapabilityUniformTexelBufferArrayNonUniformIndexing: + case SpvCapabilityStorageTexelBufferArrayNonUniformIndexing: + return true; + } + return false; +} + +bool IsSupportGuaranteedOpenCL_1_2(uint32_t capability, bool embedded_profile) { + switch (capability) { + case SpvCapabilityAddresses: + case SpvCapabilityFloat16Buffer: + case SpvCapabilityInt16: + case SpvCapabilityInt8: + case SpvCapabilityKernel: + case SpvCapabilityLinkage: + case SpvCapabilityVector16: + return true; + case SpvCapabilityInt64: + return !embedded_profile; + } + return false; +} + +bool IsSupportGuaranteedOpenCL_2_0(uint32_t capability, bool embedded_profile) { + if (IsSupportGuaranteedOpenCL_1_2(capability, embedded_profile)) return true; + + switch (capability) { + case SpvCapabilityDeviceEnqueue: + case SpvCapabilityGenericPointer: + case SpvCapabilityGroups: + case SpvCapabilityPipes: + return true; + } + return false; +} + +bool IsSupportGuaranteedOpenCL_2_2(uint32_t capability, bool embedded_profile) { + if (IsSupportGuaranteedOpenCL_2_0(capability, embedded_profile)) return true; + + switch (capability) { + case SpvCapabilitySubgroupDispatch: + case SpvCapabilityPipeStorage: + return true; + } + return false; +} + +bool IsSupportOptionalOpenCL_1_2(uint32_t capability) { + switch (capability) { + case SpvCapabilityImageBasic: + case SpvCapabilityFloat64: + return true; + } + return false; +} + +// Checks if |capability| was enabled by extension. +bool IsEnabledByExtension(ValidationState_t& _, uint32_t capability) { + spv_operand_desc operand_desc = nullptr; + _.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, + &operand_desc); + + // operand_desc is expected to be not null, otherwise validator would have + // failed at an earlier stage. This 'assert' is 'just in case'. + assert(operand_desc); + + ExtensionSet operand_exts(operand_desc->numExtensions, + operand_desc->extensions); + if (operand_exts.IsEmpty()) return false; + + return _.HasAnyOfExtensions(operand_exts); +} + +bool IsEnabledByCapabilityOpenCL_1_2(ValidationState_t& _, + uint32_t capability) { + if (_.HasCapability(SpvCapabilityImageBasic)) { + switch (capability) { + case SpvCapabilityLiteralSampler: + case SpvCapabilitySampled1D: + case SpvCapabilityImage1D: + case SpvCapabilitySampledBuffer: + case SpvCapabilityImageBuffer: + return true; + } + return false; + } + return false; +} + +bool IsEnabledByCapabilityOpenCL_2_0(ValidationState_t& _, + uint32_t capability) { + if (_.HasCapability(SpvCapabilityImageBasic)) { + switch (capability) { + case SpvCapabilityImageReadWrite: + case SpvCapabilityLiteralSampler: + case SpvCapabilitySampled1D: + case SpvCapabilityImage1D: + case SpvCapabilitySampledBuffer: + case SpvCapabilityImageBuffer: + return true; + } + return false; + } + return false; +} + +bool IsSupportGuaranteedWebGPU(uint32_t capability) { + switch (capability) { + case SpvCapabilityMatrix: + case SpvCapabilityShader: + case SpvCapabilitySampled1D: + case SpvCapabilityImage1D: + case SpvCapabilityDerivativeControl: + case SpvCapabilityImageQuery: + return true; + } + return false; +} + +} // namespace + +// Validates that capability declarations use operands allowed in the current +// context. +spv_result_t CapabilityPass(ValidationState_t& _, const Instruction* inst) { + if (inst->opcode() != SpvOpCapability) return SPV_SUCCESS; + + assert(inst->operands().size() == 1); + + const spv_parsed_operand_t& operand = inst->operand(0); + + assert(operand.num_words == 1); + assert(operand.offset < inst->words().size()); + + const uint32_t capability = inst->word(operand.offset); + const auto capability_str = [&_, capability]() { + spv_operand_desc desc = nullptr; + if (_.grammar().lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, capability, + &desc) != SPV_SUCCESS || + !desc) { + return std::string("Unknown"); + } + return std::string(desc->name); + }; + + const auto env = _.context()->target_env; + const bool opencl_embedded = env == SPV_ENV_OPENCL_EMBEDDED_1_2 || + env == SPV_ENV_OPENCL_EMBEDDED_2_0 || + env == SPV_ENV_OPENCL_EMBEDDED_2_1 || + env == SPV_ENV_OPENCL_EMBEDDED_2_2; + const std::string opencl_profile = opencl_embedded ? "Embedded" : "Full"; + if (env == SPV_ENV_VULKAN_1_0) { + if (!IsSupportGuaranteedVulkan_1_0(capability) && + !IsSupportOptionalVulkan_1_0(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.0 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_VULKAN_1_1) { + if (!IsSupportGuaranteedVulkan_1_1(capability) && + !IsSupportOptionalVulkan_1_1(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.1 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_VULKAN_1_2) { + if (!IsSupportGuaranteedVulkan_1_2(capability) && + !IsSupportOptionalVulkan_1_2(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by Vulkan 1.2 specification" + << " (or requires extension)"; + } + } else if (env == SPV_ENV_OPENCL_1_2 || env == SPV_ENV_OPENCL_EMBEDDED_1_2) { + if (!IsSupportGuaranteedOpenCL_1_2(capability, opencl_embedded) && + !IsSupportOptionalOpenCL_1_2(capability) && + !IsEnabledByExtension(_, capability) && + !IsEnabledByCapabilityOpenCL_1_2(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by OpenCL 1.2 " << opencl_profile + << " Profile specification" + << " (or requires extension or capability)"; + } + } else if (env == SPV_ENV_OPENCL_2_0 || env == SPV_ENV_OPENCL_EMBEDDED_2_0 || + env == SPV_ENV_OPENCL_2_1 || env == SPV_ENV_OPENCL_EMBEDDED_2_1) { + if (!IsSupportGuaranteedOpenCL_2_0(capability, opencl_embedded) && + !IsSupportOptionalOpenCL_1_2(capability) && + !IsEnabledByExtension(_, capability) && + !IsEnabledByCapabilityOpenCL_2_0(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by OpenCL 2.0/2.1 " << opencl_profile + << " Profile specification" + << " (or requires extension or capability)"; + } + } else if (env == SPV_ENV_OPENCL_2_2 || env == SPV_ENV_OPENCL_EMBEDDED_2_2) { + if (!IsSupportGuaranteedOpenCL_2_2(capability, opencl_embedded) && + !IsSupportOptionalOpenCL_1_2(capability) && + !IsEnabledByExtension(_, capability) && + !IsEnabledByCapabilityOpenCL_2_0(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by OpenCL 2.2 " << opencl_profile + << " Profile specification" + << " (or requires extension or capability)"; + } + } else if (env == SPV_ENV_WEBGPU_0) { + if (!IsSupportGuaranteedWebGPU(capability) && + !IsEnabledByExtension(_, capability)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Capability " << capability_str() + << " is not allowed by WebGPU specification" + << " (or requires extension)"; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_cfg.cpp b/third_party/spirv-tools/source/val/validate_cfg.cpp new file mode 100644 index 0000000..8babd35 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_cfg.cpp @@ -0,0 +1,1180 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/cfa.h" +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/spirv_validator_options.h" +#include "source/val/basic_block.h" +#include "source/val/construct.h" +#include "source/val/function.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) { + auto block = inst->block(); + size_t num_in_ops = inst->words().size() - 3; + if (num_in_ops % 2 != 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi does not have an equal number of incoming values and " + "basic blocks."; + } + + const Instruction* type_inst = _.FindDef(inst->type_id()); + assert(type_inst); + + const SpvOp type_opcode = type_inst->opcode(); + if (type_opcode == SpvOpTypePointer && + _.addressing_model() == SpvAddressingModelLogical) { + if (!_.features().variable_pointers && + !_.features().variable_pointers_storage_buffer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using pointers with OpPhi requires capability " + << "VariablePointers or VariablePointersStorageBuffer"; + } + } + + if (!_.options()->before_hlsl_legalization) { + if (type_opcode == SpvOpTypeSampledImage || + (_.HasCapability(SpvCapabilityShader) && + (type_opcode == SpvOpTypeImage || type_opcode == SpvOpTypeSampler))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result type cannot be Op" << spvOpcodeString(type_opcode); + } + } + + // Create a uniqued vector of predecessor ids for comparison against + // incoming values. OpBranchConditional %cond %label %label produces two + // predecessors in the CFG. + std::vector pred_ids; + std::transform(block->predecessors()->begin(), block->predecessors()->end(), + std::back_inserter(pred_ids), + [](const BasicBlock* b) { return b->id(); }); + std::sort(pred_ids.begin(), pred_ids.end()); + pred_ids.erase(std::unique(pred_ids.begin(), pred_ids.end()), pred_ids.end()); + + size_t num_edges = num_in_ops / 2; + if (num_edges != pred_ids.size()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi's number of incoming blocks (" << num_edges + << ") does not match block's predecessor count (" + << block->predecessors()->size() << ")."; + } + + std::unordered_set observed_predecessors; + + for (size_t i = 3; i < inst->words().size(); ++i) { + auto inc_id = inst->word(i); + if (i % 2 == 1) { + // Incoming value type must match the phi result type. + auto inc_type_id = _.GetTypeId(inc_id); + if (inst->type_id() != inc_type_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi's result type " << _.getIdName(inst->type_id()) + << " does not match incoming value " << _.getIdName(inc_id) + << " type " << _.getIdName(inc_type_id) << "."; + } + } else { + if (_.GetIdOpcode(inc_id) != SpvOpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi's incoming basic block " << _.getIdName(inc_id) + << " is not an OpLabel."; + } + + // Incoming basic block must be an immediate predecessor of the phi's + // block. + if (!std::binary_search(pred_ids.begin(), pred_ids.end(), inc_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi's incoming basic block " << _.getIdName(inc_id) + << " is not a predecessor of " << _.getIdName(block->id()) + << "."; + } + + // We must not have already seen this predecessor as one of the phi's + // operands. + if (observed_predecessors.count(inc_id) != 0) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpPhi references incoming basic block " + << _.getIdName(inc_id) << " multiple times."; + } + + // Note the fact that we have now observed this predecessor. + observed_predecessors.insert(inc_id); + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateBranch(ValidationState_t& _, const Instruction* inst) { + // target operands must be OpLabel + const auto id = inst->GetOperandAs(0); + const auto target = _.FindDef(id); + if (!target || SpvOpLabel != target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "'Target Label' operands for OpBranch must be the ID " + "of an OpLabel instruction"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateBranchConditional(ValidationState_t& _, + const Instruction* inst) { + // num_operands is either 3 or 5 --- if 5, the last two need to be literal + // integers + const auto num_operands = inst->operands().size(); + if (num_operands != 3 && num_operands != 5) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpBranchConditional requires either 3 or 5 parameters"; + } + + // grab the condition operand and check that it is a bool + const auto cond_id = inst->GetOperandAs(0); + const auto cond_op = _.FindDef(cond_id); + if (!cond_op || !cond_op->type_id() || + !_.IsBoolScalarType(cond_op->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Condition operand for " + "OpBranchConditional must be " + "of boolean type"; + } + + // target operands must be OpLabel + // note that we don't need to check that the target labels are in the same + // function, + // PerformCfgChecks already checks for that + const auto true_id = inst->GetOperandAs(1); + const auto true_target = _.FindDef(true_id); + if (!true_target || SpvOpLabel != true_target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The 'True Label' operand for OpBranchConditional must be the " + "ID of an OpLabel instruction"; + } + + const auto false_id = inst->GetOperandAs(2); + const auto false_target = _.FindDef(false_id); + if (!false_target || SpvOpLabel != false_target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The 'False Label' operand for OpBranchConditional must be the " + "ID of an OpLabel instruction"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateSwitch(ValidationState_t& _, const Instruction* inst) { + const auto num_operands = inst->operands().size(); + // At least two operands (selector, default), any more than that are + // literal/target. + + // target operands must be OpLabel + for (size_t i = 2; i < num_operands; i += 2) { + // literal, id + const auto id = inst->GetOperandAs(i + 1); + const auto target = _.FindDef(id); + if (!target || SpvOpLabel != target->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "'Target Label' operands for OpSwitch must be IDs of an " + "OpLabel instruction"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateReturnValue(ValidationState_t& _, + const Instruction* inst) { + const auto value_id = inst->GetOperandAs(0); + const auto value = _.FindDef(value_id); + if (!value || !value->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpReturnValue Value '" << _.getIdName(value_id) + << "' does not represent a value."; + } + auto value_type = _.FindDef(value->type_id()); + if (!value_type || SpvOpTypeVoid == value_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpReturnValue value's type '" + << _.getIdName(value->type_id()) << "' is missing or void."; + } + + const bool uses_variable_pointer = + _.features().variable_pointers || + _.features().variable_pointers_storage_buffer; + + if (_.addressing_model() == SpvAddressingModelLogical && + SpvOpTypePointer == value_type->opcode() && !uses_variable_pointer && + !_.options()->relax_logical_pointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpReturnValue value's type '" + << _.getIdName(value->type_id()) + << "' is a pointer, which is invalid in the Logical addressing " + "model."; + } + + const auto function = inst->function(); + const auto return_type = _.FindDef(function->GetResultTypeId()); + if (!return_type || return_type->id() != value_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpReturnValue Value '" << _.getIdName(value_id) + << "'s type does not match OpFunction's return type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateLoopMerge(ValidationState_t& _, const Instruction* inst) { + const auto merge_id = inst->GetOperandAs(0); + const auto merge = _.FindDef(merge_id); + if (!merge || merge->opcode() != SpvOpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Merge Block " << _.getIdName(merge_id) << " must be an OpLabel"; + } + if (merge_id == inst->block()->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Merge Block may not be the block containing the OpLoopMerge\n"; + } + + const auto continue_id = inst->GetOperandAs(1); + const auto continue_target = _.FindDef(continue_id); + if (!continue_target || continue_target->opcode() != SpvOpLabel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Continue Target " << _.getIdName(continue_id) + << " must be an OpLabel"; + } + + if (merge_id == continue_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Merge Block and Continue Target must be different ids"; + } + + const auto loop_control = inst->GetOperandAs(2); + if ((loop_control >> SpvLoopControlUnrollShift) & 0x1 && + (loop_control >> SpvLoopControlDontUnrollShift) & 0x1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Unroll and DontUnroll loop controls must not both be specified"; + } + if ((loop_control >> SpvLoopControlDontUnrollShift) & 0x1 && + (loop_control >> SpvLoopControlPeelCountShift) & 0x1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "PeelCount and DontUnroll " + "loop controls must not " + "both be specified"; + } + if ((loop_control >> SpvLoopControlDontUnrollShift) & 0x1 && + (loop_control >> SpvLoopControlPartialCountShift) & 0x1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "PartialCount and " + "DontUnroll loop controls " + "must not both be specified"; + } + + uint32_t operand = 3; + if ((loop_control >> SpvLoopControlDependencyLengthShift) & 0x1) { + ++operand; + } + if ((loop_control >> SpvLoopControlMinIterationsShift) & 0x1) { + ++operand; + } + if ((loop_control >> SpvLoopControlMaxIterationsShift) & 0x1) { + ++operand; + } + if ((loop_control >> SpvLoopControlIterationMultipleShift) & 0x1) { + if (inst->operands().size() < operand || + inst->GetOperandAs(operand) == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "IterationMultiple loop " + "control operand must be " + "greater than zero"; + } + ++operand; + } + if ((loop_control >> SpvLoopControlPeelCountShift) & 0x1) { + ++operand; + } + if ((loop_control >> SpvLoopControlPartialCountShift) & 0x1) { + ++operand; + } + + // That the right number of operands is present is checked by the parser. The + // above code tracks operands for expanded validation checking in the future. + + return SPV_SUCCESS; +} + +} // namespace + +void printDominatorList(const BasicBlock& b) { + std::cout << b.id() << " is dominated by: "; + const BasicBlock* bb = &b; + while (bb->immediate_dominator() != bb) { + bb = bb->immediate_dominator(); + std::cout << bb->id() << " "; + } +} + +#define CFG_ASSERT(ASSERT_FUNC, TARGET) \ + if (spv_result_t rcode = ASSERT_FUNC(_, TARGET)) return rcode + +spv_result_t FirstBlockAssert(ValidationState_t& _, uint32_t target) { + if (_.current_function().IsFirstBlock(target)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(_.current_function().id())) + << "First block " << _.getIdName(target) << " of function " + << _.getIdName(_.current_function().id()) << " is targeted by block " + << _.getIdName(_.current_function().current_block()->id()); + } + return SPV_SUCCESS; +} + +spv_result_t MergeBlockAssert(ValidationState_t& _, uint32_t merge_block) { + if (_.current_function().IsBlockType(merge_block, kBlockTypeMerge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(_.current_function().id())) + << "Block " << _.getIdName(merge_block) + << " is already a merge block for another header"; + } + return SPV_SUCCESS; +} + +/// Update the continue construct's exit blocks once the backedge blocks are +/// identified in the CFG. +void UpdateContinueConstructExitBlocks( + Function& function, + const std::vector>& back_edges) { + auto& constructs = function.constructs(); + // TODO(umar): Think of a faster way to do this + for (auto& edge : back_edges) { + uint32_t back_edge_block_id; + uint32_t loop_header_block_id; + std::tie(back_edge_block_id, loop_header_block_id) = edge; + auto is_this_header = [=](Construct& c) { + return c.type() == ConstructType::kLoop && + c.entry_block()->id() == loop_header_block_id; + }; + + for (auto construct : constructs) { + if (is_this_header(construct)) { + Construct* continue_construct = + construct.corresponding_constructs().back(); + assert(continue_construct->type() == ConstructType::kContinue); + + BasicBlock* back_edge_block; + std::tie(back_edge_block, std::ignore) = + function.GetBlock(back_edge_block_id); + continue_construct->set_exit(back_edge_block); + } + } + } +} + +std::tuple ConstructNames( + ConstructType type) { + std::string construct_name, header_name, exit_name; + + switch (type) { + case ConstructType::kSelection: + construct_name = "selection"; + header_name = "selection header"; + exit_name = "merge block"; + break; + case ConstructType::kLoop: + construct_name = "loop"; + header_name = "loop header"; + exit_name = "merge block"; + break; + case ConstructType::kContinue: + construct_name = "continue"; + header_name = "continue target"; + exit_name = "back-edge block"; + break; + case ConstructType::kCase: + construct_name = "case"; + header_name = "case entry block"; + exit_name = "case exit block"; + break; + default: + assert(1 == 0 && "Not defined type"); + } + + return std::make_tuple(construct_name, header_name, exit_name); +} + +/// Constructs an error message for construct validation errors +std::string ConstructErrorString(const Construct& construct, + const std::string& header_string, + const std::string& exit_string, + const std::string& dominate_text) { + std::string construct_name, header_name, exit_name; + std::tie(construct_name, header_name, exit_name) = + ConstructNames(construct.type()); + + // TODO(umar): Add header block for continue constructs to error message + return "The " + construct_name + " construct with the " + header_name + " " + + header_string + " " + dominate_text + " the " + exit_name + " " + + exit_string; +} + +// Finds the fall through case construct of |target_block| and records it in +// |case_fall_through|. Returns SPV_ERROR_INVALID_CFG if the case construct +// headed by |target_block| branches to multiple case constructs. +spv_result_t FindCaseFallThrough( + ValidationState_t& _, BasicBlock* target_block, uint32_t* case_fall_through, + const BasicBlock* merge, const std::unordered_set& case_targets, + Function* function) { + std::vector stack; + stack.push_back(target_block); + std::unordered_set visited; + bool target_reachable = target_block->reachable(); + int target_depth = function->GetBlockDepth(target_block); + while (!stack.empty()) { + auto block = stack.back(); + stack.pop_back(); + + if (block == merge) continue; + + if (!visited.insert(block).second) continue; + + if (target_reachable && block->reachable() && + target_block->dominates(*block)) { + // Still in the case construct. + for (auto successor : *block->successors()) { + stack.push_back(successor); + } + } else { + // Exiting the case construct to non-merge block. + if (!case_targets.count(block->id())) { + int depth = function->GetBlockDepth(block); + if ((depth < target_depth) || + (depth == target_depth && block->is_type(kBlockTypeContinue))) { + continue; + } + + return _.diag(SPV_ERROR_INVALID_CFG, target_block->label()) + << "Case construct that targets " + << _.getIdName(target_block->id()) + << " has invalid branch to block " << _.getIdName(block->id()) + << " (not another case construct, corresponding merge, outer " + "loop merge or outer loop continue)"; + } + + if (*case_fall_through == 0u) { + if (target_block != block) { + *case_fall_through = block->id(); + } + } else if (*case_fall_through != block->id()) { + // Case construct has at most one branch to another case construct. + return _.diag(SPV_ERROR_INVALID_CFG, target_block->label()) + << "Case construct that targets " + << _.getIdName(target_block->id()) + << " has branches to multiple other case construct targets " + << _.getIdName(*case_fall_through) << " and " + << _.getIdName(block->id()); + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t StructuredSwitchChecks(ValidationState_t& _, Function* function, + const Instruction* switch_inst, + const BasicBlock* header, + const BasicBlock* merge) { + std::unordered_set case_targets; + for (uint32_t i = 1; i < switch_inst->operands().size(); i += 2) { + uint32_t target = switch_inst->GetOperandAs(i); + if (target != merge->id()) case_targets.insert(target); + } + // Tracks how many times each case construct is targeted by another case + // construct. + std::map num_fall_through_targeted; + uint32_t default_case_fall_through = 0u; + uint32_t default_target = switch_inst->GetOperandAs(1u); + bool default_appears_multiple_times = false; + for (uint32_t i = 3; i < switch_inst->operands().size(); i += 2) { + if (default_target == switch_inst->GetOperandAs(i)) { + default_appears_multiple_times = true; + break; + } + } + std::unordered_map seen_to_fall_through; + for (uint32_t i = 1; i < switch_inst->operands().size(); i += 2) { + uint32_t target = switch_inst->GetOperandAs(i); + if (target == merge->id()) continue; + + uint32_t case_fall_through = 0u; + auto seen_iter = seen_to_fall_through.find(target); + if (seen_iter == seen_to_fall_through.end()) { + const auto target_block = function->GetBlock(target).first; + // OpSwitch must dominate all its case constructs. + if (header->reachable() && target_block->reachable() && + !header->dominates(*target_block)) { + return _.diag(SPV_ERROR_INVALID_CFG, header->label()) + << "Selection header " << _.getIdName(header->id()) + << " does not dominate its case construct " + << _.getIdName(target); + } + + if (auto error = FindCaseFallThrough(_, target_block, &case_fall_through, + merge, case_targets, function)) { + return error; + } + + // Track how many time the fall through case has been targeted. + if (case_fall_through != 0u) { + auto where = num_fall_through_targeted.lower_bound(case_fall_through); + if (where == num_fall_through_targeted.end() || + where->first != case_fall_through) { + num_fall_through_targeted.insert( + where, std::make_pair(case_fall_through, 1)); + } else { + where->second++; + } + } + seen_to_fall_through.insert(std::make_pair(target, case_fall_through)); + } else { + case_fall_through = seen_iter->second; + } + + if (case_fall_through == default_target && + !default_appears_multiple_times) { + case_fall_through = default_case_fall_through; + } + if (case_fall_through != 0u) { + bool is_default = i == 1; + if (is_default) { + default_case_fall_through = case_fall_through; + } else { + // Allow code like: + // case x: + // case y: + // ... + // case z: + // + // Where x and y target the same block and fall through to z. + uint32_t j = i; + while ((j + 2 < switch_inst->operands().size()) && + target == switch_inst->GetOperandAs(j + 2)) { + j += 2; + } + // If Target T1 branches to Target T2, or if Target T1 branches to the + // Default target and the Default target branches to Target T2, then T1 + // must immediately precede T2 in the list of OpSwitch Target operands. + if ((switch_inst->operands().size() < j + 2) || + (case_fall_through != switch_inst->GetOperandAs(j + 2))) { + return _.diag(SPV_ERROR_INVALID_CFG, switch_inst) + << "Case construct that targets " << _.getIdName(target) + << " has branches to the case construct that targets " + << _.getIdName(case_fall_through) + << ", but does not immediately precede it in the " + "OpSwitch's target list"; + } + } + } + } + + // Each case construct must be branched to by at most one other case + // construct. + for (const auto& pair : num_fall_through_targeted) { + if (pair.second > 1) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pair.first)) + << "Multiple case constructs have branches to the case construct " + "that targets " + << _.getIdName(pair.first); + } + } + + return SPV_SUCCESS; +} + +// Validates that all CFG divergences (i.e. conditional branch or switch) are +// structured correctly. Either divergence is preceded by a merge instruction +// or the divergence introduces at most one unseen label. +spv_result_t ValidateStructuredSelections( + ValidationState_t& _, const std::vector& postorder) { + std::unordered_set seen; + for (auto iter = postorder.rbegin(); iter != postorder.rend(); ++iter) { + const auto* block = *iter; + const auto* terminator = block->terminator(); + if (!terminator) continue; + const auto index = terminator - &_.ordered_instructions()[0]; + auto* merge = &_.ordered_instructions()[index - 1]; + // Marks merges and continues as seen. + if (merge->opcode() == SpvOpSelectionMerge) { + seen.insert(merge->GetOperandAs(0)); + } else if (merge->opcode() == SpvOpLoopMerge) { + seen.insert(merge->GetOperandAs(0)); + seen.insert(merge->GetOperandAs(1)); + } else { + // Only track the pointer if it is a merge instruction. + merge = nullptr; + } + + // Skip unreachable blocks. + if (!block->reachable()) continue; + + if (terminator->opcode() == SpvOpBranchConditional) { + const auto true_label = terminator->GetOperandAs(1); + const auto false_label = terminator->GetOperandAs(2); + // Mark the upcoming blocks as seen now, but only error out if this block + // was missing a merge instruction and both labels hadn't been seen + // previously. + const bool both_unseen = + seen.insert(true_label).second && seen.insert(false_label).second; + if (!merge && both_unseen) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "Selection must be structured"; + } + } else if (terminator->opcode() == SpvOpSwitch) { + uint32_t count = 0; + // Mark the targets as seen now, but only error out if this block was + // missing a merge instruction and there were multiple unseen labels. + for (uint32_t i = 1; i < terminator->operands().size(); i += 2) { + const auto target = terminator->GetOperandAs(i); + if (seen.insert(target).second) { + count++; + } + } + if (!merge && count > 1) { + return _.diag(SPV_ERROR_INVALID_CFG, terminator) + << "Selection must be structured"; + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t StructuredControlFlowChecks( + ValidationState_t& _, Function* function, + const std::vector>& back_edges, + const std::vector& postorder) { + /// Check all backedges target only loop headers and have exactly one + /// back-edge branching to it + + // Map a loop header to blocks with back-edges to the loop header. + std::map> loop_latch_blocks; + for (auto back_edge : back_edges) { + uint32_t back_edge_block; + uint32_t header_block; + std::tie(back_edge_block, header_block) = back_edge; + if (!function->IsBlockType(header_block, kBlockTypeLoop)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(back_edge_block)) + << "Back-edges (" << _.getIdName(back_edge_block) << " -> " + << _.getIdName(header_block) + << ") can only be formed between a block and a loop header."; + } + loop_latch_blocks[header_block].insert(back_edge_block); + } + + // Check the loop headers have exactly one back-edge branching to it + for (BasicBlock* loop_header : function->ordered_blocks()) { + if (!loop_header->reachable()) continue; + if (!loop_header->is_type(kBlockTypeLoop)) continue; + auto loop_header_id = loop_header->id(); + auto num_latch_blocks = loop_latch_blocks[loop_header_id].size(); + if (num_latch_blocks != 1) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(loop_header_id)) + << "Loop header " << _.getIdName(loop_header_id) + << " is targeted by " << num_latch_blocks + << " back-edge blocks but the standard requires exactly one"; + } + } + + // Check construct rules + for (const Construct& construct : function->constructs()) { + auto header = construct.entry_block(); + auto merge = construct.exit_block(); + + if (header->reachable() && !merge) { + std::string construct_name, header_name, exit_name; + std::tie(construct_name, header_name, exit_name) = + ConstructNames(construct.type()); + return _.diag(SPV_ERROR_INTERNAL, _.FindDef(header->id())) + << "Construct " + construct_name + " with " + header_name + " " + + _.getIdName(header->id()) + " does not have a " + + exit_name + ". This may be a bug in the validator."; + } + + // If the exit block is reachable then it's dominated by the + // header. + if (merge && merge->reachable()) { + if (!header->dominates(*merge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "does not dominate"); + } + // If it's really a merge block for a selection or loop, then it must be + // *strictly* dominated by the header. + if (construct.ExitBlockIsMergeBlock() && (header == merge)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "does not strictly dominate"); + } + } + // Check post-dominance for continue constructs. But dominance and + // post-dominance only make sense when the construct is reachable. + if (header->reachable() && construct.type() == ConstructType::kContinue) { + if (!merge->postdominates(*header)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(merge->id())) + << ConstructErrorString(construct, _.getIdName(header->id()), + _.getIdName(merge->id()), + "is not post dominated by"); + } + } + + Construct::ConstructBlockSet construct_blocks = construct.blocks(function); + std::string construct_name, header_name, exit_name; + std::tie(construct_name, header_name, exit_name) = + ConstructNames(construct.type()); + for (auto block : construct_blocks) { + // Check that all exits from the construct are via structured exits. + for (auto succ : *block->successors()) { + if (block->reachable() && !construct_blocks.count(succ) && + !construct.IsStructuredExit(_, succ)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "block " << _.getIdName(block->id()) << " exits the " + << construct_name << " headed by " + << _.getIdName(header->id()) + << ", but not via a structured exit"; + } + } + if (block == header) continue; + // Check that for all non-header blocks, all predecessors are within this + // construct. + for (auto pred : *block->predecessors()) { + if (pred->reachable() && !construct_blocks.count(pred)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(pred->id())) + << "block " << pred->id() << " branches to the " + << construct_name << " construct, but not to the " + << header_name << " " << header->id(); + } + } + + if (block->is_type(BlockType::kBlockTypeSelection) || + block->is_type(BlockType::kBlockTypeLoop)) { + size_t index = (block->terminator() - &_.ordered_instructions()[0]) - 1; + const auto& merge_inst = _.ordered_instructions()[index]; + if (merge_inst.opcode() == SpvOpSelectionMerge || + merge_inst.opcode() == SpvOpLoopMerge) { + uint32_t merge_id = merge_inst.GetOperandAs(0); + auto merge_block = function->GetBlock(merge_id).first; + if (merge_block->reachable() && + !construct_blocks.count(merge_block)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "Header block " << _.getIdName(block->id()) + << " is contained in the " << construct_name + << " construct headed by " << _.getIdName(header->id()) + << ", but its merge block " << _.getIdName(merge_id) + << " is not"; + } + } + } + } + + // Checks rules for case constructs. + if (construct.type() == ConstructType::kSelection && + header->terminator()->opcode() == SpvOpSwitch) { + const auto terminator = header->terminator(); + if (auto error = + StructuredSwitchChecks(_, function, terminator, header, merge)) { + return error; + } + } + } + + if (auto error = ValidateStructuredSelections(_, postorder)) { + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t PerformWebGPUCfgChecks(ValidationState_t& _, Function* function) { + for (auto& block : function->ordered_blocks()) { + if (block->reachable()) continue; + if (block->is_type(kBlockTypeMerge)) { + // 1. Find the referencing merge and confirm that it is reachable. + BasicBlock* merge_header = function->GetMergeHeader(block); + assert(merge_header != nullptr); + if (!merge_header->reachable()) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable merge-blocks must be referenced by " + "a reachable merge instruction."; + } + + // 2. Check that the only instructions are OpLabel and OpUnreachable. + auto* label_inst = block->label(); + auto* terminator_inst = block->terminator(); + assert(label_inst != nullptr); + assert(terminator_inst != nullptr); + + if (terminator_inst->opcode() != SpvOpUnreachable) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable merge-blocks must terminate with " + "OpUnreachable."; + } + + auto label_idx = label_inst - &_.ordered_instructions()[0]; + auto terminator_idx = terminator_inst - &_.ordered_instructions()[0]; + if (label_idx + 1 != terminator_idx) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable merge-blocks must only contain an " + "OpLabel and OpUnreachable instruction."; + } + + // 3. Use label instruction to confirm there is no uses by branches. + for (auto use : label_inst->uses()) { + const auto* use_inst = use.first; + if (spvOpcodeIsBranch(use_inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable merge-blocks cannot be the target " + "of a branch."; + } + } + } else if (block->is_type(kBlockTypeContinue)) { + // 1. Find referencing loop and confirm that it is reachable. + std::vector continue_headers = + function->GetContinueHeaders(block); + if (continue_headers.empty()) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable continue-target must be referenced " + "by a loop instruction."; + } + + std::vector reachable_headers(continue_headers.size()); + auto iter = + std::copy_if(continue_headers.begin(), continue_headers.end(), + reachable_headers.begin(), + [](BasicBlock* header) { return header->reachable(); }); + reachable_headers.resize(std::distance(reachable_headers.begin(), iter)); + + if (reachable_headers.empty()) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable continue-target must be referenced " + "by a reachable loop instruction."; + } + + // 2. Check that the only instructions are OpLabel and OpBranch. + auto* label_inst = block->label(); + auto* terminator_inst = block->terminator(); + assert(label_inst != nullptr); + assert(terminator_inst != nullptr); + + if (terminator_inst->opcode() != SpvOpBranch) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable continue-target must terminate with " + "OpBranch."; + } + + auto label_idx = label_inst - &_.ordered_instructions()[0]; + auto terminator_idx = terminator_inst - &_.ordered_instructions()[0]; + if (label_idx + 1 != terminator_idx) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable continue-target must only contain " + "an OpLabel and an OpBranch instruction."; + } + + // 3. Use label instruction to confirm there is no uses by branches. + for (auto use : label_inst->uses()) { + const auto* use_inst = use.first; + if (spvOpcodeIsBranch(use_inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable continue-target cannot be the " + "target of a branch."; + } + } + + // 4. Confirm that continue-target has a back edge to a reachable loop + // header block. + auto branch_target = terminator_inst->GetOperandAs(0); + for (auto* continue_header : reachable_headers) { + if (branch_target != continue_header->id()) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, unreachable continue-target must only have a " + "back edge to a single reachable loop instruction."; + } + } + } else { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(block->id())) + << "For WebGPU, all blocks must be reachable, unless they are " + << "degenerate cases of merge-block or continue-target."; + } + } + return SPV_SUCCESS; +} + +spv_result_t PerformCfgChecks(ValidationState_t& _) { + for (auto& function : _.functions()) { + // Check all referenced blocks are defined within a function + if (function.undefined_block_count() != 0) { + std::string undef_blocks("{"); + bool first = true; + for (auto undefined_block : function.undefined_blocks()) { + undef_blocks += _.getIdName(undefined_block); + if (!first) { + undef_blocks += " "; + } + first = false; + } + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(function.id())) + << "Block(s) " << undef_blocks << "}" + << " are referenced but not defined in function " + << _.getIdName(function.id()); + } + + // Set each block's immediate dominator and immediate postdominator, + // and find all back-edges. + // + // We want to analyze all the blocks in the function, even in degenerate + // control flow cases including unreachable blocks. So use the augmented + // CFG to ensure we cover all the blocks. + std::vector postorder; + std::vector postdom_postorder; + std::vector> back_edges; + auto ignore_block = [](const BasicBlock*) {}; + auto ignore_edge = [](const BasicBlock*, const BasicBlock*) {}; + if (!function.ordered_blocks().empty()) { + /// calculate dominators + CFA::DepthFirstTraversal( + function.first_block(), function.AugmentedCFGSuccessorsFunction(), + ignore_block, [&](const BasicBlock* b) { postorder.push_back(b); }, + ignore_edge); + auto edges = CFA::CalculateDominators( + postorder, function.AugmentedCFGPredecessorsFunction()); + for (auto edge : edges) { + if (edge.first != edge.second) + edge.first->SetImmediateDominator(edge.second); + } + + /// calculate post dominators + CFA::DepthFirstTraversal( + function.pseudo_exit_block(), + function.AugmentedCFGPredecessorsFunction(), ignore_block, + [&](const BasicBlock* b) { postdom_postorder.push_back(b); }, + ignore_edge); + auto postdom_edges = CFA::CalculateDominators( + postdom_postorder, function.AugmentedCFGSuccessorsFunction()); + for (auto edge : postdom_edges) { + edge.first->SetImmediatePostDominator(edge.second); + } + /// calculate back edges. + CFA::DepthFirstTraversal( + function.pseudo_entry_block(), + function + .AugmentedCFGSuccessorsFunctionIncludingHeaderToContinueEdge(), + ignore_block, ignore_block, + [&](const BasicBlock* from, const BasicBlock* to) { + back_edges.emplace_back(from->id(), to->id()); + }); + } + UpdateContinueConstructExitBlocks(function, back_edges); + + auto& blocks = function.ordered_blocks(); + if (!blocks.empty()) { + // Check if the order of blocks in the binary appear before the blocks + // they dominate + for (auto block = begin(blocks) + 1; block != end(blocks); ++block) { + if (auto idom = (*block)->immediate_dominator()) { + if (idom != function.pseudo_entry_block() && + block == std::find(begin(blocks), block, idom)) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef(idom->id())) + << "Block " << _.getIdName((*block)->id()) + << " appears in the binary before its dominator " + << _.getIdName(idom->id()); + } + } + + // For WebGPU check that all unreachable blocks are degenerate cases for + // merge-block or continue-target. + if (spvIsWebGPUEnv(_.context()->target_env)) { + spv_result_t result = PerformWebGPUCfgChecks(_, &function); + if (result != SPV_SUCCESS) return result; + } + } + // If we have structed control flow, check that no block has a control + // flow nesting depth larger than the limit. + if (_.HasCapability(SpvCapabilityShader)) { + const int control_flow_nesting_depth_limit = + _.options()->universal_limits_.max_control_flow_nesting_depth; + for (auto block = begin(blocks); block != end(blocks); ++block) { + if (function.GetBlockDepth(*block) > + control_flow_nesting_depth_limit) { + return _.diag(SPV_ERROR_INVALID_CFG, _.FindDef((*block)->id())) + << "Maximum Control Flow nesting depth exceeded."; + } + } + } + } + + /// Structured control flow checks are only required for shader capabilities + if (_.HasCapability(SpvCapabilityShader)) { + if (auto error = + StructuredControlFlowChecks(_, &function, back_edges, postorder)) + return error; + } + } + return SPV_SUCCESS; +} + +spv_result_t CfgPass(ValidationState_t& _, const Instruction* inst) { + SpvOp opcode = inst->opcode(); + switch (opcode) { + case SpvOpLabel: + if (auto error = _.current_function().RegisterBlock(inst->id())) + return error; + + // TODO(github:1661) This should be done in the + // ValidationState::RegisterInstruction method but because of the order of + // passes the OpLabel ends up not being part of the basic block it starts. + _.current_function().current_block()->set_label(inst); + break; + case SpvOpLoopMerge: { + uint32_t merge_block = inst->GetOperandAs(0); + uint32_t continue_block = inst->GetOperandAs(1); + CFG_ASSERT(MergeBlockAssert, merge_block); + + if (auto error = _.current_function().RegisterLoopMerge(merge_block, + continue_block)) + return error; + } break; + case SpvOpSelectionMerge: { + uint32_t merge_block = inst->GetOperandAs(0); + CFG_ASSERT(MergeBlockAssert, merge_block); + + if (auto error = _.current_function().RegisterSelectionMerge(merge_block)) + return error; + } break; + case SpvOpBranch: { + uint32_t target = inst->GetOperandAs(0); + CFG_ASSERT(FirstBlockAssert, target); + + _.current_function().RegisterBlockEnd({target}); + } break; + case SpvOpBranchConditional: { + uint32_t tlabel = inst->GetOperandAs(1); + uint32_t flabel = inst->GetOperandAs(2); + CFG_ASSERT(FirstBlockAssert, tlabel); + CFG_ASSERT(FirstBlockAssert, flabel); + + _.current_function().RegisterBlockEnd({tlabel, flabel}); + } break; + + case SpvOpSwitch: { + std::vector cases; + for (size_t i = 1; i < inst->operands().size(); i += 2) { + uint32_t target = inst->GetOperandAs(i); + CFG_ASSERT(FirstBlockAssert, target); + cases.push_back(target); + } + _.current_function().RegisterBlockEnd({cases}); + } break; + case SpvOpReturn: { + const uint32_t return_type = _.current_function().GetResultTypeId(); + const Instruction* return_type_inst = _.FindDef(return_type); + assert(return_type_inst); + if (return_type_inst->opcode() != SpvOpTypeVoid) + return _.diag(SPV_ERROR_INVALID_CFG, inst) + << "OpReturn can only be called from a function with void " + << "return type."; + _.current_function().RegisterBlockEnd(std::vector()); + break; + } + case SpvOpKill: + case SpvOpReturnValue: + case SpvOpUnreachable: + case SpvOpTerminateInvocation: + _.current_function().RegisterBlockEnd(std::vector()); + if (opcode == SpvOpKill) { + _.current_function().RegisterExecutionModelLimitation( + SpvExecutionModelFragment, + "OpKill requires Fragment execution model"); + } + if (opcode == SpvOpTerminateInvocation) { + _.current_function().RegisterExecutionModelLimitation( + SpvExecutionModelFragment, + "OpTerminateInvocation requires Fragment execution model"); + } + break; + default: + break; + } + return SPV_SUCCESS; +} + +void ReachabilityPass(ValidationState_t& _) { + for (auto& f : _.functions()) { + std::vector stack; + auto entry = f.first_block(); + // Skip function declarations. + if (entry) stack.push_back(entry); + + while (!stack.empty()) { + auto block = stack.back(); + stack.pop_back(); + + if (block->reachable()) continue; + + block->set_reachable(true); + for (auto succ : *block->successors()) { + stack.push_back(succ); + } + } + } +} + +spv_result_t ControlFlowPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpPhi: + if (auto error = ValidatePhi(_, inst)) return error; + break; + case SpvOpBranch: + if (auto error = ValidateBranch(_, inst)) return error; + break; + case SpvOpBranchConditional: + if (auto error = ValidateBranchConditional(_, inst)) return error; + break; + case SpvOpReturnValue: + if (auto error = ValidateReturnValue(_, inst)) return error; + break; + case SpvOpSwitch: + if (auto error = ValidateSwitch(_, inst)) return error; + break; + case SpvOpLoopMerge: + if (auto error = ValidateLoopMerge(_, inst)) return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_composites.cpp b/third_party/spirv-tools/source/val/validate_composites.cpp new file mode 100644 index 0000000..eb8a324 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_composites.cpp @@ -0,0 +1,619 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of composite SPIR-V instructions. + +#include "source/val/validate.h" + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns the type of the value accessed by OpCompositeExtract or +// OpCompositeInsert instruction. The function traverses the hierarchy of +// nested data structures (structs, arrays, vectors, matrices) as directed by +// the sequence of indices in the instruction. May return error if traversal +// fails (encountered non-composite, out of bounds, no indices, nesting too +// deep). +spv_result_t GetExtractInsertValueType(ValidationState_t& _, + const Instruction* inst, + uint32_t* member_type) { + const SpvOp opcode = inst->opcode(); + assert(opcode == SpvOpCompositeExtract || opcode == SpvOpCompositeInsert); + uint32_t word_index = opcode == SpvOpCompositeExtract ? 4 : 5; + const uint32_t num_words = static_cast(inst->words().size()); + const uint32_t composite_id_index = word_index - 1; + const uint32_t num_indices = num_words - word_index; + const uint32_t kCompositeExtractInsertMaxNumIndices = 255; + + if (num_indices == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected at least one index to Op" + << spvOpcodeString(inst->opcode()) << ", zero found"; + + } else if (num_indices > kCompositeExtractInsertMaxNumIndices) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The number of indexes in Op" << spvOpcodeString(opcode) + << " may not exceed " << kCompositeExtractInsertMaxNumIndices + << ". Found " << num_indices << " indexes."; + } + + *member_type = _.GetTypeId(inst->word(composite_id_index)); + if (*member_type == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Composite to be an object of composite type"; + } + + for (; word_index < num_words; ++word_index) { + const uint32_t component_index = inst->word(word_index); + const Instruction* const type_inst = _.FindDef(*member_type); + assert(type_inst); + switch (type_inst->opcode()) { + case SpvOpTypeVector: { + *member_type = type_inst->word(2); + const uint32_t vector_size = type_inst->word(3); + if (component_index >= vector_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Vector access is out of bounds, vector size is " + << vector_size << ", but access index is " << component_index; + } + break; + } + case SpvOpTypeMatrix: { + *member_type = type_inst->word(2); + const uint32_t num_cols = type_inst->word(3); + if (component_index >= num_cols) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Matrix access is out of bounds, matrix has " << num_cols + << " columns, but access index is " << component_index; + } + break; + } + case SpvOpTypeArray: { + uint64_t array_size = 0; + auto size = _.FindDef(type_inst->word(3)); + *member_type = type_inst->word(2); + if (spvOpcodeIsSpecConstant(size->opcode())) { + // Cannot verify against the size of this array. + break; + } + + if (!_.GetConstantValUint64(type_inst->word(3), &array_size)) { + assert(0 && "Array type definition is corrupt"); + } + if (component_index >= array_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Array access is out of bounds, array size is " + << array_size << ", but access index is " << component_index; + } + break; + } + case SpvOpTypeRuntimeArray: { + *member_type = type_inst->word(2); + // Array size is unknown. + break; + } + case SpvOpTypeStruct: { + const size_t num_struct_members = type_inst->words().size() - 2; + if (component_index >= num_struct_members) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Index is out of bounds, can not find index " + << component_index << " in the structure '" + << type_inst->id() << "'. This structure has " + << num_struct_members << " members. Largest valid index is " + << num_struct_members - 1 << "."; + } + *member_type = type_inst->word(component_index + 2); + break; + } + case SpvOpTypeCooperativeMatrixNV: { + *member_type = type_inst->word(2); + break; + } + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Reached non-composite type while indexes still remain to " + "be traversed."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVectorExtractDynamic(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const SpvOp result_opcode = _.GetIdOpcode(result_type); + if (!spvOpcodeIsScalarType(result_opcode)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a scalar type"; + } + + const uint32_t vector_type = _.GetOperandTypeId(inst, 2); + const SpvOp vector_opcode = _.GetIdOpcode(vector_type); + if (vector_opcode != SpvOpTypeVector) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Vector type to be OpTypeVector"; + } + + if (_.GetComponentType(vector_type) != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Vector component type to be equal to Result Type"; + } + + const auto index = _.FindDef(inst->GetOperandAs(3)); + if (!index || index->type_id() == 0 || !_.IsIntScalarType(index->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Index to be int scalar"; + } + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot extract from a vector of 8- or 16-bit types"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateVectorInsertDyanmic(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const SpvOp result_opcode = _.GetIdOpcode(result_type); + if (result_opcode != SpvOpTypeVector) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypeVector"; + } + + const uint32_t vector_type = _.GetOperandTypeId(inst, 2); + if (vector_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Vector type to be equal to Result Type"; + } + + const uint32_t component_type = _.GetOperandTypeId(inst, 3); + if (_.GetComponentType(result_type) != component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Component type to be equal to Result Type " + << "component type"; + } + + const uint32_t index_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(index_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Index to be int scalar"; + } + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot insert into a vector of 8- or 16-bit types"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCompositeConstruct(ValidationState_t& _, + const Instruction* inst) { + const uint32_t num_operands = static_cast(inst->operands().size()); + const uint32_t result_type = inst->type_id(); + const SpvOp result_opcode = _.GetIdOpcode(result_type); + switch (result_opcode) { + case SpvOpTypeVector: { + const uint32_t num_result_components = _.GetDimension(result_type); + const uint32_t result_component_type = _.GetComponentType(result_type); + uint32_t given_component_count = 0; + + if (num_operands <= 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of constituents to be at least 2"; + } + + for (uint32_t operand_index = 2; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (operand_type == result_component_type) { + ++given_component_count; + } else { + if (_.GetIdOpcode(operand_type) != SpvOpTypeVector || + _.GetComponentType(operand_type) != result_component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituents to be scalars or vectors of" + << " the same type as Result Type components"; + } + + given_component_count += _.GetDimension(operand_type); + } + } + + if (num_result_components != given_component_count) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected total number of given components to be equal " + << "to the size of Result Type vector"; + } + + break; + } + case SpvOpTypeMatrix: { + uint32_t result_num_rows = 0; + uint32_t result_num_cols = 0; + uint32_t result_col_type = 0; + uint32_t result_component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &result_num_rows, &result_num_cols, + &result_col_type, &result_component_type)) { + assert(0); + } + + if (result_num_cols + 2 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected total number of Constituents to be equal " + << "to the number of columns of Result Type matrix"; + } + + for (uint32_t operand_index = 2; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (operand_type != result_col_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the column " + << "type Result Type matrix"; + } + } + + break; + } + case SpvOpTypeArray: { + const Instruction* const array_inst = _.FindDef(result_type); + assert(array_inst); + assert(array_inst->opcode() == SpvOpTypeArray); + + auto size = _.FindDef(array_inst->word(3)); + if (spvOpcodeIsSpecConstant(size->opcode())) { + // Cannot verify against the size of this array. + break; + } + + uint64_t array_size = 0; + if (!_.GetConstantValUint64(array_inst->word(3), &array_size)) { + assert(0 && "Array type definition is corrupt"); + } + + if (array_size + 2 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected total number of Constituents to be equal " + << "to the number of elements of Result Type array"; + } + + const uint32_t result_component_type = array_inst->word(2); + for (uint32_t operand_index = 2; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (operand_type != result_component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the column " + << "type Result Type array"; + } + } + + break; + } + case SpvOpTypeStruct: { + const Instruction* const struct_inst = _.FindDef(result_type); + assert(struct_inst); + assert(struct_inst->opcode() == SpvOpTypeStruct); + + if (struct_inst->operands().size() + 1 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected total number of Constituents to be equal " + << "to the number of members of Result Type struct"; + } + + for (uint32_t operand_index = 2; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + const uint32_t member_type = struct_inst->word(operand_index); + if (operand_type != member_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the " + << "corresponding member type of Result Type struct"; + } + } + + break; + } + case SpvOpTypeCooperativeMatrixNV: { + const auto result_type_inst = _.FindDef(result_type); + assert(result_type_inst); + const auto component_type_id = + result_type_inst->GetOperandAs(1); + + if (3 != num_operands) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected single constituent"; + } + + const uint32_t operand_type_id = _.GetOperandTypeId(inst, 2); + + if (operand_type_id != component_type_id) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Constituent type to be equal to the component type"; + } + + break; + } + default: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a composite type"; + } + } + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot create a composite containing 8- or 16-bit types"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCompositeExtract(ValidationState_t& _, + const Instruction* inst) { + uint32_t member_type = 0; + if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) { + return error; + } + + const uint32_t result_type = inst->type_id(); + if (result_type != member_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result type (Op" << spvOpcodeString(_.GetIdOpcode(result_type)) + << ") does not match the type that results from indexing into " + "the composite (Op" + << spvOpcodeString(_.GetIdOpcode(member_type)) << ")."; + } + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot extract from a composite of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCompositeInsert(ValidationState_t& _, + const Instruction* inst) { + const uint32_t object_type = _.GetOperandTypeId(inst, 2); + const uint32_t composite_type = _.GetOperandTypeId(inst, 3); + const uint32_t result_type = inst->type_id(); + if (result_type != composite_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Result Type must be the same as Composite type in Op" + << spvOpcodeString(inst->opcode()) << " yielding Result Id " + << result_type << "."; + } + + uint32_t member_type = 0; + if (spv_result_t error = GetExtractInsertValueType(_, inst, &member_type)) { + return error; + } + + if (object_type != member_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "The Object type (Op" + << spvOpcodeString(_.GetIdOpcode(object_type)) + << ") does not match the type that results from indexing into the " + "Composite (Op" + << spvOpcodeString(_.GetIdOpcode(member_type)) << ")."; + } + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot insert into a composite of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCopyObject(ValidationState_t& _, const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const uint32_t operand_type = _.GetOperandTypeId(inst, 2); + if (operand_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type and Operand type to be the same"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateTranspose(ValidationState_t& _, const Instruction* inst) { + uint32_t result_num_rows = 0; + uint32_t result_num_cols = 0; + uint32_t result_col_type = 0; + uint32_t result_component_type = 0; + const uint32_t result_type = inst->type_id(); + if (!_.GetMatrixTypeInfo(result_type, &result_num_rows, &result_num_cols, + &result_col_type, &result_component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a matrix type"; + } + + const uint32_t matrix_type = _.GetOperandTypeId(inst, 2); + uint32_t matrix_num_rows = 0; + uint32_t matrix_num_cols = 0; + uint32_t matrix_col_type = 0; + uint32_t matrix_component_type = 0; + if (!_.GetMatrixTypeInfo(matrix_type, &matrix_num_rows, &matrix_num_cols, + &matrix_col_type, &matrix_component_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Matrix to be of type OpTypeMatrix"; + } + + if (result_component_type != matrix_component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected component types of Matrix and Result Type to be " + << "identical"; + } + + if (result_num_rows != matrix_num_cols || + result_num_cols != matrix_num_rows) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected number of columns and the column size of Matrix " + << "to be the reverse of those of Result Type"; + } + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot transpose matrices of 16-bit floats"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateVectorShuffle(ValidationState_t& _, + const Instruction* inst) { + auto resultType = _.FindDef(inst->type_id()); + if (!resultType || resultType->opcode() != SpvOpTypeVector) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of OpVectorShuffle must be" + << " OpTypeVector. Found Op" + << spvOpcodeString(static_cast(resultType->opcode())) << "."; + } + + // The number of components in Result Type must be the same as the number of + // Component operands. + auto componentCount = inst->operands().size() - 4; + auto resultVectorDimension = resultType->GetOperandAs(2); + if (componentCount != resultVectorDimension) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVectorShuffle component literals count does not match " + "Result Type '" + << _.getIdName(resultType->id()) << "'s vector component count."; + } + + // Vector 1 and Vector 2 must both have vector types, with the same Component + // Type as Result Type. + auto vector1Object = _.FindDef(inst->GetOperandAs(2)); + auto vector1Type = _.FindDef(vector1Object->type_id()); + auto vector2Object = _.FindDef(inst->GetOperandAs(3)); + auto vector2Type = _.FindDef(vector2Object->type_id()); + if (!vector1Type || vector1Type->opcode() != SpvOpTypeVector) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The type of Vector 1 must be OpTypeVector."; + } + if (!vector2Type || vector2Type->opcode() != SpvOpTypeVector) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The type of Vector 2 must be OpTypeVector."; + } + + auto resultComponentType = resultType->GetOperandAs(1); + if (vector1Type->GetOperandAs(1) != resultComponentType) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Component Type of Vector 1 must be the same as ResultType."; + } + if (vector2Type->GetOperandAs(1) != resultComponentType) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Component Type of Vector 2 must be the same as ResultType."; + } + + // All Component literals must either be FFFFFFFF or in [0, N - 1]. + // For WebGPU specifically, Component literals cannot be FFFFFFFF. + auto vector1ComponentCount = vector1Type->GetOperandAs(2); + auto vector2ComponentCount = vector2Type->GetOperandAs(2); + auto N = vector1ComponentCount + vector2ComponentCount; + auto firstLiteralIndex = 4; + const auto is_webgpu_env = spvIsWebGPUEnv(_.context()->target_env); + for (size_t i = firstLiteralIndex; i < inst->operands().size(); ++i) { + auto literal = inst->GetOperandAs(i); + if (literal != 0xFFFFFFFF && literal >= N) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Component index " << literal << " is out of bounds for " + << "combined (Vector1 + Vector2) size of " << N << "."; + } + + if (is_webgpu_env && literal == 0xFFFFFFFF) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Component literal at operand " << i - firstLiteralIndex + << " cannot be 0xFFFFFFFF in WebGPU execution environment."; + } + } + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot shuffle a vector of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCopyLogical(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + const auto source = _.FindDef(inst->GetOperandAs(2u)); + const auto source_type = _.FindDef(source->type_id()); + if (!source_type || !result_type || source_type == result_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result Type must not equal the Operand type"; + } + + if (!_.LogicallyMatch(source_type, result_type, false)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result Type does not logically match the Operand type"; + } + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Cannot copy composites of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +} // anonymous namespace + +// Validates correctness of composite instructions. +spv_result_t CompositesPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpVectorExtractDynamic: + return ValidateVectorExtractDynamic(_, inst); + case SpvOpVectorInsertDynamic: + return ValidateVectorInsertDyanmic(_, inst); + case SpvOpVectorShuffle: + return ValidateVectorShuffle(_, inst); + case SpvOpCompositeConstruct: + return ValidateCompositeConstruct(_, inst); + case SpvOpCompositeExtract: + return ValidateCompositeExtract(_, inst); + case SpvOpCompositeInsert: + return ValidateCompositeInsert(_, inst); + case SpvOpCopyObject: + return ValidateCopyObject(_, inst); + case SpvOpTranspose: + return ValidateTranspose(_, inst); + case SpvOpCopyLogical: + return ValidateCopyLogical(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_constants.cpp b/third_party/spirv-tools/source/val/validate_constants.cpp new file mode 100644 index 0000000..dea95c8 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_constants.cpp @@ -0,0 +1,468 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateConstantBool(ValidationState_t& _, + const Instruction* inst) { + auto type = _.FindDef(inst->type_id()); + if (!type || type->opcode() != SpvOpTypeBool) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Op" << spvOpcodeString(inst->opcode()) << " Result Type '" + << _.getIdName(inst->type_id()) << "' is not a boolean type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateConstantComposite(ValidationState_t& _, + const Instruction* inst) { + std::string opcode_name = std::string("Op") + spvOpcodeString(inst->opcode()); + + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type || !spvOpcodeIsComposite(result_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Result Type '" + << _.getIdName(inst->type_id()) << "' is not a composite type."; + } + + const auto constituent_count = inst->words().size() - 3; + switch (result_type->opcode()) { + case SpvOpTypeVector: { + const auto component_count = result_type->GetOperandAs(2); + if (component_count != constituent_count) { + // TODO: Output ID's on diagnostic + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name + << " Constituent count does not match " + "Result Type '" + << _.getIdName(result_type->id()) + << "'s vector component count."; + } + const auto component_type = + _.FindDef(result_type->GetOperandAs(1)); + if (!component_type) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Component type is not defined."; + } + for (size_t constituent_index = 2; + constituent_index < inst->operands().size(); constituent_index++) { + const auto constituent_id = + inst->GetOperandAs(constituent_index); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' is not a constant or undef."; + } + const auto constituent_result_type = _.FindDef(constituent->type_id()); + if (!constituent_result_type || + component_type->opcode() != constituent_result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "'s type does not match Result Type '" + << _.getIdName(result_type->id()) << "'s vector element type."; + } + } + } break; + case SpvOpTypeMatrix: { + const auto column_count = result_type->GetOperandAs(2); + if (column_count != constituent_count) { + // TODO: Output ID's on diagnostic + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name + << " Constituent count does not match " + "Result Type '" + << _.getIdName(result_type->id()) << "'s matrix column count."; + } + + const auto column_type = _.FindDef(result_type->words()[2]); + if (!column_type) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Column type is not defined."; + } + const auto component_count = column_type->GetOperandAs(2); + const auto component_type = + _.FindDef(column_type->GetOperandAs(1)); + if (!component_type) { + return _.diag(SPV_ERROR_INVALID_ID, column_type) + << "Component type is not defined."; + } + + for (size_t constituent_index = 2; + constituent_index < inst->operands().size(); constituent_index++) { + const auto constituent_id = + inst->GetOperandAs(constituent_index); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + // The message says "... or undef" because the spec does not say + // undef is a constant. + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' is not a constant or undef."; + } + const auto vector = _.FindDef(constituent->type_id()); + if (!vector) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Result type is not defined."; + } + if (column_type->opcode() != vector->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' type does not match Result Type '" + << _.getIdName(result_type->id()) << "'s matrix column type."; + } + const auto vector_component_type = + _.FindDef(vector->GetOperandAs(1)); + if (component_type->id() != vector_component_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' component type does not match Result Type '" + << _.getIdName(result_type->id()) + << "'s matrix column component type."; + } + if (component_count != vector->words()[3]) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' vector component count does not match Result Type '" + << _.getIdName(result_type->id()) + << "'s vector component count."; + } + } + } break; + case SpvOpTypeArray: { + auto element_type = _.FindDef(result_type->GetOperandAs(1)); + if (!element_type) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Element type is not defined."; + } + const auto length = _.FindDef(result_type->GetOperandAs(2)); + if (!length) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "Length is not defined."; + } + bool is_int32; + bool is_const; + uint32_t value; + std::tie(is_int32, is_const, value) = _.EvalInt32IfConst(length->id()); + if (is_int32 && is_const && value != constituent_count) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name + << " Constituent count does not match " + "Result Type '" + << _.getIdName(result_type->id()) << "'s array length."; + } + for (size_t constituent_index = 2; + constituent_index < inst->operands().size(); constituent_index++) { + const auto constituent_id = + inst->GetOperandAs(constituent_index); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' is not a constant or undef."; + } + const auto constituent_type = _.FindDef(constituent->type_id()); + if (!constituent_type) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Result type is not defined."; + } + if (element_type->id() != constituent_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "'s type does not match Result Type '" + << _.getIdName(result_type->id()) << "'s array element type."; + } + } + } break; + case SpvOpTypeStruct: { + const auto member_count = result_type->words().size() - 2; + if (member_count != constituent_count) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(inst->type_id()) + << "' count does not match Result Type '" + << _.getIdName(result_type->id()) << "'s struct member count."; + } + for (uint32_t constituent_index = 2, member_index = 1; + constituent_index < inst->operands().size(); + constituent_index++, member_index++) { + const auto constituent_id = + inst->GetOperandAs(constituent_index); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || + !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' is not a constant or undef."; + } + const auto constituent_type = _.FindDef(constituent->type_id()); + if (!constituent_type) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Result type is not defined."; + } + + const auto member_type_id = + result_type->GetOperandAs(member_index); + const auto member_type = _.FindDef(member_type_id); + if (!member_type || member_type->id() != constituent_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' type does not match the Result Type '" + << _.getIdName(result_type->id()) << "'s member type."; + } + } + } break; + case SpvOpTypeCooperativeMatrixNV: { + if (1 != constituent_count) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(inst->type_id()) << "' count must be one."; + } + const auto constituent_id = inst->GetOperandAs(2); + const auto constituent = _.FindDef(constituent_id); + if (!constituent || !spvOpcodeIsConstantOrUndef(constituent->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' is not a constant or undef."; + } + const auto constituent_type = _.FindDef(constituent->type_id()); + if (!constituent_type) { + return _.diag(SPV_ERROR_INVALID_ID, constituent) + << "Result type is not defined."; + } + + const auto component_type_id = result_type->GetOperandAs(1); + const auto component_type = _.FindDef(component_type_id); + if (!component_type || component_type->id() != constituent_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opcode_name << " Constituent '" + << _.getIdName(constituent_id) + << "' type does not match the Result Type '" + << _.getIdName(result_type->id()) << "'s component type."; + } + } break; + default: + break; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateConstantSampler(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type || result_type->opcode() != SpvOpTypeSampler) { + return _.diag(SPV_ERROR_INVALID_ID, result_type) + << "OpConstantSampler Result Type '" + << _.getIdName(inst->type_id()) << "' is not a sampler type."; + } + + return SPV_SUCCESS; +} + +// True if instruction defines a type that can have a null value, as defined by +// the SPIR-V spec. Tracks composite-type components through module to check +// nullability transitively. +bool IsTypeNullable(const std::vector& instruction, + const ValidationState_t& _) { + uint16_t opcode; + uint16_t word_count; + spvOpcodeSplit(instruction[0], &word_count, &opcode); + switch (static_cast(opcode)) { + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: + case SpvOpTypeEvent: + case SpvOpTypeDeviceEvent: + case SpvOpTypeReserveId: + case SpvOpTypeQueue: + return true; + case SpvOpTypeArray: + case SpvOpTypeMatrix: + case SpvOpTypeCooperativeMatrixNV: + case SpvOpTypeVector: { + auto base_type = _.FindDef(instruction[2]); + return base_type && IsTypeNullable(base_type->words(), _); + } + case SpvOpTypeStruct: { + for (size_t elementIndex = 2; elementIndex < instruction.size(); + ++elementIndex) { + auto element = _.FindDef(instruction[elementIndex]); + if (!element || !IsTypeNullable(element->words(), _)) return false; + } + return true; + } + case SpvOpTypePointer: + if (instruction[2] == SpvStorageClassPhysicalStorageBuffer) { + return false; + } + return true; + default: + return false; + } +} + +spv_result_t ValidateConstantNull(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type || !IsTypeNullable(result_type->words(), _)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpConstantNull Result Type '" + << _.getIdName(inst->type_id()) << "' cannot have a null value."; + } + + return SPV_SUCCESS; +} + +// Validates that OpSpecConstant specializes to either int or float type. +spv_result_t ValidateSpecConstant(ValidationState_t& _, + const Instruction* inst) { + // Operand 0 is the of the type that we're specializing to. + auto type_id = inst->GetOperandAs(0); + auto type_instruction = _.FindDef(type_id); + auto type_opcode = type_instruction->opcode(); + if (type_opcode != SpvOpTypeInt && type_opcode != SpvOpTypeFloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Specialization constant " + "must be an integer or " + "floating-point number."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateSpecConstantOp(ValidationState_t& _, + const Instruction* inst) { + const auto op = inst->GetOperandAs(2); + + // The binary parser already ensures that the op is valid for *some* + // environment. Here we check restrictions. + switch (op) { + case SpvOpQuantizeToF16: + if (!_.HasCapability(SpvCapabilityShader)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Specialization constant operation " << spvOpcodeString(op) + << " requires Shader capability"; + } + break; + + case SpvOpUConvert: + if (!_.features().uconvert_spec_constant_op && + !_.HasCapability(SpvCapabilityKernel)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Prior to SPIR-V 1.4, specialization constant operation " + "UConvert requires Kernel capability or extension " + "SPV_AMD_gpu_shader_int16"; + } + break; + + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertFToU: + case SpvOpConvertUToF: + case SpvOpConvertPtrToU: + case SpvOpConvertUToPtr: + case SpvOpGenericCastToPtr: + case SpvOpPtrCastToGeneric: + case SpvOpBitcast: + case SpvOpFNegate: + case SpvOpFAdd: + case SpvOpFSub: + case SpvOpFMul: + case SpvOpFDiv: + case SpvOpFRem: + case SpvOpFMod: + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpPtrAccessChain: + case SpvOpInBoundsPtrAccessChain: + if (!_.HasCapability(SpvCapabilityKernel)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Specialization constant operation " << spvOpcodeString(op) + << " requires Kernel capability"; + } + break; + + default: + break; + } + + // TODO(dneto): Validate result type and arguments to the various operations. + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ConstantPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpConstantTrue: + case SpvOpConstantFalse: + case SpvOpSpecConstantTrue: + case SpvOpSpecConstantFalse: + if (auto error = ValidateConstantBool(_, inst)) return error; + break; + case SpvOpConstantComposite: + case SpvOpSpecConstantComposite: + if (auto error = ValidateConstantComposite(_, inst)) return error; + break; + case SpvOpConstantSampler: + if (auto error = ValidateConstantSampler(_, inst)) return error; + break; + case SpvOpConstantNull: + if (auto error = ValidateConstantNull(_, inst)) return error; + break; + case SpvOpSpecConstant: + if (auto error = ValidateSpecConstant(_, inst)) return error; + break; + case SpvOpSpecConstantOp: + if (auto error = ValidateSpecConstantOp(_, inst)) return error; + break; + default: + break; + } + + // Generally disallow creating 8- or 16-bit constants unless the full + // capabilities are present. + if (spvOpcodeIsConstant(inst->opcode()) && + _.HasCapability(SpvCapabilityShader) && + !_.IsPointerType(inst->type_id()) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot form constants of 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_conversion.cpp b/third_party/spirv-tools/source/val/validate_conversion.cpp new file mode 100644 index 0000000..0060d0b --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_conversion.cpp @@ -0,0 +1,546 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of conversion instructions. + +#include "source/val/validate.h" + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of conversion instructions. +spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case SpvOpConvertFToU: { + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !_.IsUnsignedIntCooperativeMatrixType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || (!_.IsFloatScalarType(input_type) && + !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + break; + } + + case SpvOpConvertFToS: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeMatrixType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || (!_.IsFloatScalarType(input_type) && + !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + break; + } + + case SpvOpConvertSToF: + case SpvOpConvertUToF: { + if (!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type) && + !_.IsFloatCooperativeMatrixType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + break; + } + + case SpvOpUConvert: { + if (!_.IsUnsignedIntScalarType(result_type) && + !_.IsUnsignedIntVectorType(result_type) && + !_.IsUnsignedIntCooperativeMatrixType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have different bit width from Result " + "Type: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpSConvert: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type) && + !_.IsIntCooperativeMatrixType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type) && + !_.IsIntCooperativeMatrixType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be int scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have different bit width from Result " + "Type: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpFConvert: { + if (!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type) && + !_.IsFloatCooperativeMatrixType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || (!_.IsFloatScalarType(input_type) && + !_.IsFloatVectorType(input_type) && + !_.IsFloatCooperativeMatrixType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be float scalar or vector: " + << spvOpcodeString(opcode); + + if (_.IsCooperativeMatrixType(result_type) || + _.IsCooperativeMatrixType(input_type)) { + spv_result_t ret = + _.CooperativeMatrixShapesMatch(inst, result_type, input_type); + if (ret != SPV_SUCCESS) return ret; + } else { + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + } + + if (_.GetBitWidth(result_type) == _.GetBitWidth(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have different bit width from Result " + "Type: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpQuantizeToF16: { + if ((!_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type)) || + _.GetBitWidth(result_type) != 32) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected 32-bit float scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (input_type != result_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input type to be equal to Result Type: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpConvertPtrToU: { + if (!_.IsUnsignedIntScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected unsigned int scalar type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!_.IsPointerType(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (_.addressing_model() == SpvAddressingModelLogical) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Logical addressing not supported: " + << spvOpcodeString(opcode); + + if (_.addressing_model() == + SpvAddressingModelPhysicalStorageBuffer64EXT) { + uint32_t input_storage_class = 0; + uint32_t input_data_type = 0; + _.GetPointerTypeInfo(input_type, &input_data_type, + &input_storage_class); + if (input_storage_class != SpvStorageClassPhysicalStorageBufferEXT) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer storage class must be PhysicalStorageBufferEXT: " + << spvOpcodeString(opcode); + } + break; + } + + case SpvOpSatConvertSToU: + case SpvOpSatConvertUToS: { + if (!_.IsIntScalarType(result_type) && !_.IsIntVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || + (!_.IsIntScalarType(input_type) && !_.IsIntVectorType(input_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar or vector as input: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same dimension as Result Type: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpConvertUToPtr: { + if (!_.IsPointerType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type || !_.IsIntScalarType(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected int scalar as input: " << spvOpcodeString(opcode); + + if (_.addressing_model() == SpvAddressingModelLogical) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Logical addressing not supported: " + << spvOpcodeString(opcode); + + if (_.addressing_model() == + SpvAddressingModelPhysicalStorageBuffer64EXT) { + uint32_t result_storage_class = 0; + uint32_t result_data_type = 0; + _.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class); + if (result_storage_class != SpvStorageClassPhysicalStorageBufferEXT) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer storage class must be PhysicalStorageBufferEXT: " + << spvOpcodeString(opcode); + } + break; + } + + case SpvOpPtrCastToGeneric: { + uint32_t result_storage_class = 0; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + if (result_storage_class != SpvStorageClassGeneric) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to have storage class Generic: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + uint32_t input_storage_class = 0; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, + &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != SpvStorageClassWorkgroup && + input_storage_class != SpvStorageClassCrossWorkgroup && + input_storage_class != SpvStorageClassFunction) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpGenericCastToPtr: { + uint32_t result_storage_class = 0; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + if (result_storage_class != SpvStorageClassWorkgroup && + result_storage_class != SpvStorageClassCrossWorkgroup && + result_storage_class != SpvStorageClassFunction) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to have storage class Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + uint32_t input_storage_class = 0; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, + &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != SpvStorageClassGeneric) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Generic: " + << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + break; + } + + case SpvOpGenericCastToPtrExplicit: { + uint32_t result_storage_class = 0; + uint32_t result_data_type = 0; + if (!_.GetPointerTypeInfo(result_type, &result_data_type, + &result_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer: " + << spvOpcodeString(opcode); + + const uint32_t target_storage_class = inst->word(4); + if (result_storage_class != target_storage_class) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be of target storage class: " + << spvOpcodeString(opcode); + + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + uint32_t input_storage_class = 0; + uint32_t input_data_type = 0; + if (!_.GetPointerTypeInfo(input_type, &input_data_type, + &input_storage_class)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer: " << spvOpcodeString(opcode); + + if (input_storage_class != SpvStorageClassGeneric) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have storage class Generic: " + << spvOpcodeString(opcode); + + if (result_data_type != input_data_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input and Result Type to point to the same type: " + << spvOpcodeString(opcode); + + if (target_storage_class != SpvStorageClassWorkgroup && + target_storage_class != SpvStorageClassCrossWorkgroup && + target_storage_class != SpvStorageClassFunction) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected target storage class to be Workgroup, " + << "CrossWorkgroup or Function: " << spvOpcodeString(opcode); + break; + } + + case SpvOpBitcast: { + const uint32_t input_type = _.GetOperandTypeId(inst, 2); + if (!input_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have a type: " << spvOpcodeString(opcode); + + const bool result_is_pointer = _.IsPointerType(result_type); + const bool result_is_int_scalar = _.IsIntScalarType(result_type); + const bool input_is_pointer = _.IsPointerType(input_type); + const bool input_is_int_scalar = _.IsIntScalarType(input_type); + + if (!result_is_pointer && !result_is_int_scalar && + !_.IsIntVectorType(result_type) && + !_.IsFloatScalarType(result_type) && + !_.IsFloatVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a pointer or int or float vector " + << "or scalar type: " << spvOpcodeString(opcode); + + if (!input_is_pointer && !input_is_int_scalar && + !_.IsIntVectorType(input_type) && !_.IsFloatScalarType(input_type) && + !_.IsFloatVectorType(input_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer or int or float vector " + << "or scalar: " << spvOpcodeString(opcode); + + if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 5) || + _.HasExtension(kSPV_KHR_physical_storage_buffer)) { + const bool result_is_int_vector = _.IsIntVectorType(result_type); + const bool result_has_int32 = + _.ContainsSizedIntOrFloatType(result_type, SpvOpTypeInt, 32); + const bool input_is_int_vector = _.IsIntVectorType(input_type); + const bool input_has_int32 = + _.ContainsSizedIntOrFloatType(input_type, SpvOpTypeInt, 32); + if (result_is_pointer && !input_is_pointer && !input_is_int_scalar && + !(input_is_int_vector && input_has_int32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer, int scalar or 32-bit int " + "vector if Result Type is pointer: " + << spvOpcodeString(opcode); + + if (input_is_pointer && !result_is_pointer && !result_is_int_scalar && + !(result_is_int_vector && result_has_int32)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer can only be converted to another pointer, int " + "scalar or 32-bit int vector: " + << spvOpcodeString(opcode); + } else { + if (result_is_pointer && !input_is_pointer && !input_is_int_scalar) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to be a pointer or int scalar if Result " + "Type is pointer: " + << spvOpcodeString(opcode); + + if (input_is_pointer && !result_is_pointer && !result_is_int_scalar) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Pointer can only be converted to another pointer or int " + "scalar: " + << spvOpcodeString(opcode); + } + + if (!result_is_pointer && !input_is_pointer) { + const uint32_t result_size = + _.GetBitWidth(result_type) * _.GetDimension(result_type); + const uint32_t input_size = + _.GetBitWidth(input_type) * _.GetDimension(input_type); + if (result_size != input_size) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected input to have the same total bit width as " + << "Result Type: " << spvOpcodeString(opcode); + } + break; + } + + default: + break; + } + + if (_.HasCapability(SpvCapabilityShader)) { + switch (inst->opcode()) { + case SpvOpConvertFToU: + case SpvOpConvertFToS: + case SpvOpConvertSToF: + case SpvOpConvertUToF: + case SpvOpBitcast: + if (_.ContainsLimitedUseIntOrFloatType(inst->type_id()) || + _.ContainsLimitedUseIntOrFloatType(_.GetOperandTypeId(inst, 2u))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "8- or 16-bit types can only be used with width-only " + "conversions"; + } + break; + default: + break; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_debug.cpp b/third_party/spirv-tools/source/val/validate_debug.cpp new file mode 100644 index 0000000..0a25d8a --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_debug.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateMemberName(ValidationState_t& _, const Instruction* inst) { + const auto type_id = inst->GetOperandAs(0); + const auto type = _.FindDef(type_id); + if (!type || SpvOpTypeStruct != type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpMemberName Type '" << _.getIdName(type_id) + << "' is not a struct type."; + } + const auto member_id = inst->GetOperandAs(1); + const auto member_count = (uint32_t)(type->words().size() - 2); + if (member_count <= member_id) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpMemberName Member '" << _.getIdName(member_id) + << "' index is larger than Type '" << _.getIdName(type->id()) + << "'s member count."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateLine(ValidationState_t& _, const Instruction* inst) { + const auto file_id = inst->GetOperandAs(0); + const auto file = _.FindDef(file_id); + if (!file || SpvOpString != file->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLine Target '" << _.getIdName(file_id) + << "' is not an OpString."; + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t DebugPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpMemberName: + if (auto error = ValidateMemberName(_, inst)) return error; + break; + case SpvOpLine: + if (auto error = ValidateLine(_, inst)) return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_decorations.cpp b/third_party/spirv-tools/source/val/validate_decorations.cpp new file mode 100644 index 0000000..d381276 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_decorations.cpp @@ -0,0 +1,1634 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/spirv_validator_options.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Distinguish between row and column major matrix layouts. +enum MatrixLayout { kRowMajor, kColumnMajor }; + +// A functor for hashing a pair of integers. +struct PairHash { + std::size_t operator()(const std::pair pair) const { + const uint32_t a = pair.first; + const uint32_t b = pair.second; + const uint32_t rotated_b = (b >> 2) | ((b & 3) << 30); + return a ^ rotated_b; + } +}; + +// A functor for hashing decoration types. +struct SpvDecorationHash { + std::size_t operator()(SpvDecoration dec) const { + return static_cast(dec); + } +}; + +// Struct member layout attributes that are inherited through arrays. +struct LayoutConstraints { + explicit LayoutConstraints( + MatrixLayout the_majorness = MatrixLayout::kColumnMajor, + uint32_t stride = 0) + : majorness(the_majorness), matrix_stride(stride) {} + MatrixLayout majorness; + uint32_t matrix_stride; +}; + +// A type for mapping (struct id, member id) to layout constraints. +using MemberConstraints = std::unordered_map, + LayoutConstraints, PairHash>; + +// Returns the array stride of the given array type. +uint32_t GetArrayStride(uint32_t array_id, ValidationState_t& vstate) { + for (auto& decoration : vstate.id_decorations(array_id)) { + if (SpvDecorationArrayStride == decoration.dec_type()) { + return decoration.params()[0]; + } + } + return 0; +} + +// Returns true if the given variable has a BuiltIn decoration. +bool isBuiltInVar(uint32_t var_id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(var_id); + return std::any_of( + decorations.begin(), decorations.end(), + [](const Decoration& d) { return SpvDecorationBuiltIn == d.dec_type(); }); +} + +// Returns true if the given structure type has any members with BuiltIn +// decoration. +bool isBuiltInStruct(uint32_t struct_id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(struct_id); + return std::any_of( + decorations.begin(), decorations.end(), [](const Decoration& d) { + return SpvDecorationBuiltIn == d.dec_type() && + Decoration::kInvalidMember != d.struct_member_index(); + }); +} + +// Returns true if the given ID has the Import LinkageAttributes decoration. +bool hasImportLinkageAttribute(uint32_t id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(id); + return std::any_of(decorations.begin(), decorations.end(), + [](const Decoration& d) { + return SpvDecorationLinkageAttributes == d.dec_type() && + d.params().size() >= 2u && + d.params().back() == SpvLinkageTypeImport; + }); +} + +// Returns a vector of all members of a structure. +std::vector getStructMembers(uint32_t struct_id, + ValidationState_t& vstate) { + const auto inst = vstate.FindDef(struct_id); + return std::vector(inst->words().begin() + 2, inst->words().end()); +} + +// Returns a vector of all members of a structure that have specific type. +std::vector getStructMembers(uint32_t struct_id, SpvOp type, + ValidationState_t& vstate) { + std::vector members; + for (auto id : getStructMembers(struct_id, vstate)) { + if (type == vstate.FindDef(id)->opcode()) { + members.push_back(id); + } + } + return members; +} + +// Returns whether the given structure is missing Offset decoration for any +// member. Handles also nested structures. +bool isMissingOffsetInStruct(uint32_t struct_id, ValidationState_t& vstate) { + std::vector hasOffset(getStructMembers(struct_id, vstate).size(), + false); + // Check offsets of member decorations + for (auto& decoration : vstate.id_decorations(struct_id)) { + if (SpvDecorationOffset == decoration.dec_type() && + Decoration::kInvalidMember != decoration.struct_member_index()) { + hasOffset[decoration.struct_member_index()] = true; + } + } + // Check also nested structures + bool nestedStructsMissingOffset = false; + for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) { + if (isMissingOffsetInStruct(id, vstate)) { + nestedStructsMissingOffset = true; + break; + } + } + return nestedStructsMissingOffset || + !std::all_of(hasOffset.begin(), hasOffset.end(), + [](const bool b) { return b; }); +} + +// Rounds x up to the next alignment. Assumes alignment is a power of two. +uint32_t align(uint32_t x, uint32_t alignment) { + return (x + alignment - 1) & ~(alignment - 1); +} + +// Returns base alignment of struct member. If |roundUp| is true, also +// ensure that structs and arrays are aligned at least to a multiple of 16 +// bytes. +uint32_t getBaseAlignment(uint32_t member_id, bool roundUp, + const LayoutConstraints& inherited, + MemberConstraints& constraints, + ValidationState_t& vstate) { + const auto inst = vstate.FindDef(member_id); + const auto& words = inst->words(); + // Minimal alignment is byte-aligned. + uint32_t baseAlignment = 1; + switch (inst->opcode()) { + case SpvOpTypeInt: + case SpvOpTypeFloat: + baseAlignment = words[2] / 8; + break; + case SpvOpTypeVector: { + const auto componentId = words[2]; + const auto numComponents = words[3]; + const auto componentAlignment = getBaseAlignment( + componentId, roundUp, inherited, constraints, vstate); + baseAlignment = + componentAlignment * (numComponents == 3 ? 4 : numComponents); + break; + } + case SpvOpTypeMatrix: { + const auto column_type = words[2]; + if (inherited.majorness == kColumnMajor) { + baseAlignment = getBaseAlignment(column_type, roundUp, inherited, + constraints, vstate); + } else { + // A row-major matrix of C columns has a base alignment equal to the + // base alignment of a vector of C matrix components. + const auto num_columns = words[3]; + const auto component_inst = vstate.FindDef(column_type); + const auto component_id = component_inst->words()[2]; + const auto componentAlignment = getBaseAlignment( + component_id, roundUp, inherited, constraints, vstate); + baseAlignment = + componentAlignment * (num_columns == 3 ? 4 : num_columns); + } + } break; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + baseAlignment = + getBaseAlignment(words[2], roundUp, inherited, constraints, vstate); + if (roundUp) baseAlignment = align(baseAlignment, 16u); + break; + case SpvOpTypeStruct: { + const auto members = getStructMembers(member_id, vstate); + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; ++memberIdx) { + const auto id = members[memberIdx]; + const auto& constraint = + constraints[std::make_pair(member_id, memberIdx)]; + baseAlignment = std::max( + baseAlignment, + getBaseAlignment(id, roundUp, constraint, constraints, vstate)); + } + if (roundUp) baseAlignment = align(baseAlignment, 16u); + break; + } + case SpvOpTypePointer: + baseAlignment = vstate.pointer_size_and_alignment(); + break; + default: + assert(0); + break; + } + + return baseAlignment; +} + +// Returns scalar alignment of a type. +uint32_t getScalarAlignment(uint32_t type_id, ValidationState_t& vstate) { + const auto inst = vstate.FindDef(type_id); + const auto& words = inst->words(); + switch (inst->opcode()) { + case SpvOpTypeInt: + case SpvOpTypeFloat: + return words[2] / 8; + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: { + const auto compositeMemberTypeId = words[2]; + return getScalarAlignment(compositeMemberTypeId, vstate); + } + case SpvOpTypeStruct: { + const auto members = getStructMembers(type_id, vstate); + uint32_t max_member_alignment = 1; + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; ++memberIdx) { + const auto id = members[memberIdx]; + uint32_t member_alignment = getScalarAlignment(id, vstate); + if (member_alignment > max_member_alignment) { + max_member_alignment = member_alignment; + } + } + return max_member_alignment; + } break; + case SpvOpTypePointer: + return vstate.pointer_size_and_alignment(); + default: + assert(0); + break; + } + + return 1; +} + +// Returns size of a struct member. Doesn't include padding at the end of struct +// or array. Assumes that in the struct case, all members have offsets. +uint32_t getSize(uint32_t member_id, const LayoutConstraints& inherited, + MemberConstraints& constraints, ValidationState_t& vstate) { + const auto inst = vstate.FindDef(member_id); + const auto& words = inst->words(); + switch (inst->opcode()) { + case SpvOpTypeInt: + case SpvOpTypeFloat: + return words[2] / 8; + case SpvOpTypeVector: { + const auto componentId = words[2]; + const auto numComponents = words[3]; + const auto componentSize = + getSize(componentId, inherited, constraints, vstate); + const auto size = componentSize * numComponents; + return size; + } + case SpvOpTypeArray: { + const auto sizeInst = vstate.FindDef(words[3]); + if (spvOpcodeIsSpecConstant(sizeInst->opcode())) return 0; + assert(SpvOpConstant == sizeInst->opcode()); + const uint32_t num_elem = sizeInst->words()[3]; + const uint32_t elem_type = words[2]; + const uint32_t elem_size = + getSize(elem_type, inherited, constraints, vstate); + // Account for gaps due to alignments in the first N-1 elements, + // then add the size of the last element. + const auto size = + (num_elem - 1) * GetArrayStride(member_id, vstate) + elem_size; + return size; + } + case SpvOpTypeRuntimeArray: + return 0; + case SpvOpTypeMatrix: { + const auto num_columns = words[3]; + if (inherited.majorness == kColumnMajor) { + return num_columns * inherited.matrix_stride; + } else { + // Row major case. + const auto column_type = words[2]; + const auto component_inst = vstate.FindDef(column_type); + const auto num_rows = component_inst->words()[3]; + const auto scalar_elem_type = component_inst->words()[2]; + const uint32_t scalar_elem_size = + getSize(scalar_elem_type, inherited, constraints, vstate); + return (num_rows - 1) * inherited.matrix_stride + + num_columns * scalar_elem_size; + } + } + case SpvOpTypeStruct: { + const auto& members = getStructMembers(member_id, vstate); + if (members.empty()) return 0; + const auto lastIdx = uint32_t(members.size() - 1); + const auto& lastMember = members.back(); + uint32_t offset = 0xffffffff; + // Find the offset of the last element and add the size. + for (auto& decoration : vstate.id_decorations(member_id)) { + if (SpvDecorationOffset == decoration.dec_type() && + decoration.struct_member_index() == (int)lastIdx) { + offset = decoration.params()[0]; + } + } + // This check depends on the fact that all members have offsets. This + // has been checked earlier in the flow. + assert(offset != 0xffffffff); + const auto& constraint = constraints[std::make_pair(lastMember, lastIdx)]; + return offset + getSize(lastMember, constraint, constraints, vstate); + } + case SpvOpTypePointer: + return vstate.pointer_size_and_alignment(); + default: + assert(0); + return 0; + } +} + +// A member is defined to improperly straddle if either of the following are +// true: +// - It is a vector with total size less than or equal to 16 bytes, and has +// Offset decorations placing its first byte at F and its last byte at L, where +// floor(F / 16) != floor(L / 16). +// - It is a vector with total size greater than 16 bytes and has its Offset +// decorations placing its first byte at a non-integer multiple of 16. +bool hasImproperStraddle(uint32_t id, uint32_t offset, + const LayoutConstraints& inherited, + MemberConstraints& constraints, + ValidationState_t& vstate) { + const auto size = getSize(id, inherited, constraints, vstate); + const auto F = offset; + const auto L = offset + size - 1; + if (size <= 16) { + if ((F >> 4) != (L >> 4)) return true; + } else { + if (F % 16 != 0) return true; + } + return false; +} + +// Returns true if |offset| satsifies an alignment to |alignment|. In the case +// of |alignment| of zero, the |offset| must also be zero. +bool IsAlignedTo(uint32_t offset, uint32_t alignment) { + if (alignment == 0) return offset == 0; + return 0 == (offset % alignment); +} + +// Returns SPV_SUCCESS if the given struct satisfies standard layout rules for +// Block or BufferBlocks in Vulkan. Otherwise emits a diagnostic and returns +// something other than SPV_SUCCESS. Matrices inherit the specified column +// or row major-ness. +spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, + const char* decoration_str, bool blockRules, + uint32_t incoming_offset, + MemberConstraints& constraints, + ValidationState_t& vstate) { + if (vstate.options()->skip_block_layout) return SPV_SUCCESS; + + // blockRules are the same as bufferBlock rules if the uniform buffer + // standard layout extension is being used. + if (vstate.options()->uniform_buffer_standard_layout) blockRules = false; + + // Relaxed layout and scalar layout can both be in effect at the same time. + // For example, relaxed layout is implied by Vulkan 1.1. But scalar layout + // is more permissive than relaxed layout. + const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout(); + const bool scalar_block_layout = vstate.options()->scalar_block_layout; + + auto fail = [&vstate, struct_id, storage_class_str, decoration_str, + blockRules, relaxed_block_layout, + scalar_block_layout](uint32_t member_idx) -> DiagnosticStream { + DiagnosticStream ds = + std::move(vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(struct_id)) + << "Structure id " << struct_id << " decorated as " + << decoration_str << " for variable in " << storage_class_str + << " storage class must follow " + << (scalar_block_layout + ? "scalar " + : (relaxed_block_layout ? "relaxed " : "standard ")) + << (blockRules ? "uniform buffer" : "storage buffer") + << " layout rules: member " << member_idx << " "); + return ds; + }; + + const auto& members = getStructMembers(struct_id, vstate); + + // To check for member overlaps, we want to traverse the members in + // offset order. + struct MemberOffsetPair { + uint32_t member; + uint32_t offset; + }; + std::vector member_offsets; + member_offsets.reserve(members.size()); + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; memberIdx++) { + uint32_t offset = 0xffffffff; + for (auto& decoration : vstate.id_decorations(struct_id)) { + if (decoration.struct_member_index() == (int)memberIdx) { + switch (decoration.dec_type()) { + case SpvDecorationOffset: + offset = decoration.params()[0]; + break; + default: + break; + } + } + } + member_offsets.push_back( + MemberOffsetPair{memberIdx, incoming_offset + offset}); + } + std::stable_sort( + member_offsets.begin(), member_offsets.end(), + [](const MemberOffsetPair& lhs, const MemberOffsetPair& rhs) { + return lhs.offset < rhs.offset; + }); + + // Now scan from lowest offest to highest offset. + uint32_t nextValidOffset = 0; + for (size_t ordered_member_idx = 0; + ordered_member_idx < member_offsets.size(); ordered_member_idx++) { + const auto& member_offset = member_offsets[ordered_member_idx]; + const auto memberIdx = member_offset.member; + const auto offset = member_offset.offset; + auto id = members[member_offset.member]; + const LayoutConstraints& constraint = + constraints[std::make_pair(struct_id, uint32_t(memberIdx))]; + // Scalar layout takes precedence because it's more permissive, and implying + // an alignment that divides evenly into the alignment that would otherwise + // be used. + const auto alignment = + scalar_block_layout + ? getScalarAlignment(id, vstate) + : getBaseAlignment(id, blockRules, constraint, constraints, vstate); + const auto inst = vstate.FindDef(id); + const auto opcode = inst->opcode(); + const auto size = getSize(id, constraint, constraints, vstate); + // Check offset. + if (offset == 0xffffffff) + return fail(memberIdx) << "is missing an Offset decoration"; + if (!scalar_block_layout && relaxed_block_layout && + opcode == SpvOpTypeVector) { + // In relaxed block layout, the vector offset must be aligned to the + // vector's scalar element type. + const auto componentId = inst->words()[2]; + const auto scalar_alignment = getScalarAlignment(componentId, vstate); + if (!IsAlignedTo(offset, scalar_alignment)) { + return fail(memberIdx) + << "at offset " << offset + << " is not aligned to scalar element size " << scalar_alignment; + } + } else { + // Without relaxed block layout, the offset must be divisible by the + // alignment requirement. + if (!IsAlignedTo(offset, alignment)) { + return fail(memberIdx) + << "at offset " << offset << " is not aligned to " << alignment; + } + } + if (offset < nextValidOffset) + return fail(memberIdx) << "at offset " << offset + << " overlaps previous member ending at offset " + << nextValidOffset - 1; + if (!scalar_block_layout && relaxed_block_layout) { + // Check improper straddle of vectors. + if (SpvOpTypeVector == opcode && + hasImproperStraddle(id, offset, constraint, constraints, vstate)) + return fail(memberIdx) + << "is an improperly straddling vector at offset " << offset; + } + // Check struct members recursively. + spv_result_t recursive_status = SPV_SUCCESS; + if (SpvOpTypeStruct == opcode && + SPV_SUCCESS != (recursive_status = checkLayout( + id, storage_class_str, decoration_str, blockRules, + offset, constraints, vstate))) + return recursive_status; + // Check matrix stride. + if (SpvOpTypeMatrix == opcode) { + for (auto& decoration : vstate.id_decorations(id)) { + if (SpvDecorationMatrixStride == decoration.dec_type() && + !IsAlignedTo(decoration.params()[0], alignment)) + return fail(memberIdx) + << "is a matrix with stride " << decoration.params()[0] + << " not satisfying alignment to " << alignment; + } + } + + // Check arrays and runtime arrays recursively. + auto array_inst = inst; + auto array_alignment = alignment; + while (array_inst->opcode() == SpvOpTypeArray || + array_inst->opcode() == SpvOpTypeRuntimeArray) { + const auto typeId = array_inst->word(2); + const auto element_inst = vstate.FindDef(typeId); + // Check array stride. + uint32_t array_stride = 0; + for (auto& decoration : vstate.id_decorations(array_inst->id())) { + if (SpvDecorationArrayStride == decoration.dec_type()) { + array_stride = decoration.params()[0]; + if (array_stride == 0) { + return fail(memberIdx) << "contains an array with stride 0"; + } + if (!IsAlignedTo(array_stride, array_alignment)) + return fail(memberIdx) + << "contains an array with stride " << decoration.params()[0] + << " not satisfying alignment to " << alignment; + } + } + + bool is_int32 = false; + bool is_const = false; + uint32_t num_elements = 0; + if (array_inst->opcode() == SpvOpTypeArray) { + std::tie(is_int32, is_const, num_elements) = + vstate.EvalInt32IfConst(array_inst->word(3)); + } + num_elements = std::max(1u, num_elements); + // Check each element recursively if it is a struct. There is a + // limitation to this check if the array size is a spec constant or is a + // runtime array then we will only check a single element. This means + // some improper straddles might be missed. + for (uint32_t i = 0; i < num_elements; ++i) { + uint32_t next_offset = i * array_stride + offset; + if (SpvOpTypeStruct == element_inst->opcode() && + SPV_SUCCESS != (recursive_status = checkLayout( + typeId, storage_class_str, decoration_str, + blockRules, next_offset, constraints, vstate))) + return recursive_status; + // If offsets accumulate up to a 16-byte multiple stop checking since + // it will just repeat. + if (i > 0 && (next_offset % 16 == 0)) break; + } + + // Proceed to the element in case it is an array. + array_inst = element_inst; + array_alignment = scalar_block_layout + ? getScalarAlignment(array_inst->id(), vstate) + : getBaseAlignment(array_inst->id(), blockRules, + constraint, constraints, vstate); + + const auto element_size = + getSize(element_inst->id(), constraint, constraints, vstate); + if (element_size > array_stride) { + return fail(memberIdx) + << "contains an array with stride " << array_stride + << ", but with an element size of " << element_size; + } + } + nextValidOffset = offset + size; + if (!scalar_block_layout && blockRules && + (SpvOpTypeArray == opcode || SpvOpTypeStruct == opcode)) { + // Uniform block rules don't permit anything in the padding of a struct + // or array. + nextValidOffset = align(nextValidOffset, alignment); + } + } + return SPV_SUCCESS; +} + +// Returns true if variable or structure id has given decoration. Handles also +// nested structures. +bool hasDecoration(uint32_t id, SpvDecoration decoration, + ValidationState_t& vstate) { + for (auto& dec : vstate.id_decorations(id)) { + if (decoration == dec.dec_type()) return true; + } + if (SpvOpTypeStruct != vstate.FindDef(id)->opcode()) { + return false; + } + for (auto member_id : getStructMembers(id, SpvOpTypeStruct, vstate)) { + if (hasDecoration(member_id, decoration, vstate)) { + return true; + } + } + return false; +} + +// Returns true if all ids of given type have a specified decoration. +bool checkForRequiredDecoration(uint32_t struct_id, SpvDecoration decoration, + SpvOp type, ValidationState_t& vstate) { + const auto& members = getStructMembers(struct_id, vstate); + for (size_t memberIdx = 0; memberIdx < members.size(); memberIdx++) { + const auto id = members[memberIdx]; + if (type != vstate.FindDef(id)->opcode()) continue; + bool found = false; + for (auto& dec : vstate.id_decorations(id)) { + if (decoration == dec.dec_type()) found = true; + } + for (auto& dec : vstate.id_decorations(struct_id)) { + if (decoration == dec.dec_type() && + (int)memberIdx == dec.struct_member_index()) { + found = true; + } + } + if (!found) { + return false; + } + } + for (auto id : getStructMembers(struct_id, SpvOpTypeStruct, vstate)) { + if (!checkForRequiredDecoration(id, decoration, type, vstate)) { + return false; + } + } + return true; +} + +spv_result_t CheckLinkageAttrOfFunctions(ValidationState_t& vstate) { + for (const auto& function : vstate.functions()) { + if (function.block_count() == 0u) { + // A function declaration (an OpFunction with no basic blocks), must have + // a Linkage Attributes Decoration with the Import Linkage Type. + if (!hasImportLinkageAttribute(function.id(), vstate)) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(function.id())) + << "Function declaration (id " << function.id() + << ") must have a LinkageAttributes decoration with the Import " + "Linkage type."; + } + } else { + if (hasImportLinkageAttribute(function.id(), vstate)) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(function.id())) + << "Function definition (id " << function.id() + << ") may not be decorated with Import Linkage type."; + } + } + } + return SPV_SUCCESS; +} + +// Checks whether an imported variable is initialized by this module. +spv_result_t CheckImportedVariableInitialization(ValidationState_t& vstate) { + // According the SPIR-V Spec 2.16.1, it is illegal to initialize an imported + // variable. This means that a module-scope OpVariable with initialization + // value cannot be marked with the Import Linkage Type (import type id = 1). + for (auto global_var_id : vstate.global_vars()) { + // Initializer is an optional argument for OpVariable. If initializer + // is present, the instruction will have 5 words. + auto variable_instr = vstate.FindDef(global_var_id); + if (variable_instr->words().size() == 5u && + hasImportLinkageAttribute(global_var_id, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, variable_instr) + << "A module-scope OpVariable with initialization value " + "cannot be marked with the Import Linkage Type."; + } + } + return SPV_SUCCESS; +} + +// Checks whether a builtin variable is valid. +spv_result_t CheckBuiltInVariable(uint32_t var_id, ValidationState_t& vstate) { + const auto& decorations = vstate.id_decorations(var_id); + for (const auto& d : decorations) { + if (spvIsVulkanEnv(vstate.context()->target_env)) { + if (d.dec_type() == SpvDecorationLocation || + d.dec_type() == SpvDecorationComponent) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << "A BuiltIn variable (id " << var_id + << ") cannot have any Location or Component decorations"; + } + } + } + return SPV_SUCCESS; +} + +// Checks whether proper decorations have been appied to the entry points. +spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { + for (uint32_t entry_point : vstate.entry_points()) { + const auto& descs = vstate.entry_point_descriptions(entry_point); + int num_builtin_inputs = 0; + int num_builtin_outputs = 0; + for (const auto& desc : descs) { + std::unordered_set seen_vars; + for (auto interface : desc.interfaces) { + Instruction* var_instr = vstate.FindDef(interface); + if (!var_instr || SpvOpVariable != var_instr->opcode()) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "Interfaces passed to OpEntryPoint must be of type " + "OpTypeVariable. Found Op" + << spvOpcodeString(var_instr->opcode()) << "."; + } + const SpvStorageClass storage_class = + var_instr->GetOperandAs(2); + if (vstate.version() >= SPV_SPIRV_VERSION_WORD(1, 4)) { + // Starting in 1.4, OpEntryPoint must list all global variables + // it statically uses and those interfaces must be unique. + if (storage_class == SpvStorageClassFunction) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "OpEntryPoint interfaces should only list global " + "variables"; + } + + if (!seen_vars.insert(var_instr).second) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "Non-unique OpEntryPoint interface " + << vstate.getIdName(interface) << " is disallowed"; + } + } else { + if (storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + return vstate.diag(SPV_ERROR_INVALID_ID, var_instr) + << "OpEntryPoint interfaces must be OpVariables with " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class " + << storage_class << " for Entry Point id " << entry_point + << "."; + } + } + + const uint32_t ptr_id = var_instr->word(1); + Instruction* ptr_instr = vstate.FindDef(ptr_id); + // It is guaranteed (by validator ID checks) that ptr_instr is + // OpTypePointer. Word 3 of this instruction is the type being pointed + // to. + const uint32_t type_id = ptr_instr->word(3); + Instruction* type_instr = vstate.FindDef(type_id); + if (type_instr && SpvOpTypeStruct == type_instr->opcode() && + isBuiltInStruct(type_id, vstate)) { + if (storage_class == SpvStorageClassInput) ++num_builtin_inputs; + if (storage_class == SpvStorageClassOutput) ++num_builtin_outputs; + if (num_builtin_inputs > 1 || num_builtin_outputs > 1) break; + if (auto error = CheckBuiltInVariable(interface, vstate)) + return error; + } else if (isBuiltInVar(interface, vstate)) { + if (auto error = CheckBuiltInVariable(interface, vstate)) + return error; + } + } + if (num_builtin_inputs > 1 || num_builtin_outputs > 1) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(entry_point)) + << "There must be at most one object per Storage Class that can " + "contain a structure type containing members decorated with " + "BuiltIn, consumed per entry-point. Entry Point id " + << entry_point << " does not meet this requirement."; + } + // The LinkageAttributes Decoration cannot be applied to functions + // targeted by an OpEntryPoint instruction + for (auto& decoration : vstate.id_decorations(entry_point)) { + if (SpvDecorationLinkageAttributes == decoration.dec_type()) { + const char* linkage_name = + reinterpret_cast(&decoration.params()[0]); + return vstate.diag(SPV_ERROR_INVALID_BINARY, + vstate.FindDef(entry_point)) + << "The LinkageAttributes Decoration (Linkage name: " + << linkage_name << ") cannot be applied to function id " + << entry_point + << " because it is targeted by an OpEntryPoint instruction."; + } + } + } + } + return SPV_SUCCESS; +} + +// Load |constraints| with all the member constraints for structs contained +// within the given array type. +void ComputeMemberConstraintsForArray(MemberConstraints* constraints, + uint32_t array_id, + const LayoutConstraints& inherited, + ValidationState_t& vstate); + +// Load |constraints| with all the member constraints for the given struct, +// and all its contained structs. +void ComputeMemberConstraintsForStruct(MemberConstraints* constraints, + uint32_t struct_id, + const LayoutConstraints& inherited, + ValidationState_t& vstate) { + assert(constraints); + const auto& members = getStructMembers(struct_id, vstate); + for (uint32_t memberIdx = 0, numMembers = uint32_t(members.size()); + memberIdx < numMembers; memberIdx++) { + LayoutConstraints& constraint = + (*constraints)[std::make_pair(struct_id, memberIdx)]; + constraint = inherited; + for (auto& decoration : vstate.id_decorations(struct_id)) { + if (decoration.struct_member_index() == (int)memberIdx) { + switch (decoration.dec_type()) { + case SpvDecorationRowMajor: + constraint.majorness = kRowMajor; + break; + case SpvDecorationColMajor: + constraint.majorness = kColumnMajor; + break; + case SpvDecorationMatrixStride: + constraint.matrix_stride = decoration.params()[0]; + break; + default: + break; + } + } + } + + // Now recurse + auto member_type_id = members[memberIdx]; + const auto member_type_inst = vstate.FindDef(member_type_id); + const auto opcode = member_type_inst->opcode(); + switch (opcode) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + ComputeMemberConstraintsForArray(constraints, member_type_id, inherited, + vstate); + break; + case SpvOpTypeStruct: + ComputeMemberConstraintsForStruct(constraints, member_type_id, + inherited, vstate); + break; + default: + break; + } + } +} + +void ComputeMemberConstraintsForArray(MemberConstraints* constraints, + uint32_t array_id, + const LayoutConstraints& inherited, + ValidationState_t& vstate) { + assert(constraints); + auto elem_type_id = vstate.FindDef(array_id)->words()[2]; + const auto elem_type_inst = vstate.FindDef(elem_type_id); + const auto opcode = elem_type_inst->opcode(); + switch (opcode) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + ComputeMemberConstraintsForArray(constraints, elem_type_id, inherited, + vstate); + break; + case SpvOpTypeStruct: + ComputeMemberConstraintsForStruct(constraints, elem_type_id, inherited, + vstate); + break; + default: + break; + } +} + +spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { + // Set of entry points that are known to use a push constant. + std::unordered_set uses_push_constant; + for (const auto& inst : vstate.ordered_instructions()) { + const auto& words = inst.words(); + if (SpvOpVariable == inst.opcode()) { + const auto var_id = inst.id(); + // For storage class / decoration combinations, see Vulkan 14.5.4 "Offset + // and Stride Assignment". + const auto storageClass = words[3]; + const bool uniform = storageClass == SpvStorageClassUniform; + const bool uniform_constant = + storageClass == SpvStorageClassUniformConstant; + const bool push_constant = storageClass == SpvStorageClassPushConstant; + const bool storage_buffer = storageClass == SpvStorageClassStorageBuffer; + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + // Vulkan 14.5.1: There must be no more than one PushConstant block + // per entry point. + if (push_constant) { + auto entry_points = vstate.EntryPointReferences(var_id); + for (auto ep_id : entry_points) { + const bool already_used = !uses_push_constant.insert(ep_id).second; + if (already_used) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << "Entry point id '" << ep_id + << "' uses more than one PushConstant interface.\n" + << "From Vulkan spec, section 14.5.1:\n" + << "There must be no more than one push constant block " + << "statically used per shader entry point."; + } + } + } + // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for + // UniformConstant which cannot be a struct. + if (uniform_constant) { + auto entry_points = vstate.EntryPointReferences(var_id); + if (!entry_points.empty() && + !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << "UniformConstant id '" << var_id + << "' is missing DescriptorSet decoration.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + if (!entry_points.empty() && + !hasDecoration(var_id, SpvDecorationBinding, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << "UniformConstant id '" << var_id + << "' is missing Binding decoration.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + } + } + + if (spvIsOpenGLEnv(vstate.context()->target_env)) { + bool has_block = hasDecoration(var_id, SpvDecorationBlock, vstate); + bool has_buffer_block = + hasDecoration(var_id, SpvDecorationBufferBlock, vstate); + if ((uniform && (has_block || has_buffer_block)) || + (storage_buffer && has_block)) { + auto entry_points = vstate.EntryPointReferences(var_id); + if (!entry_points.empty() && + !hasDecoration(var_id, SpvDecorationBinding, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << (uniform ? "Uniform" : "Storage Buffer") << " id '" + << var_id << "' is missing Binding decoration.\n" + << "From ARB_gl_spirv extension:\n" + << "Uniform and shader storage block variables must " + << "also be decorated with a *Binding*."; + } + } + } + + const bool phys_storage_buffer = + storageClass == SpvStorageClassPhysicalStorageBufferEXT; + if (uniform || push_constant || storage_buffer || phys_storage_buffer) { + const auto ptrInst = vstate.FindDef(words[1]); + assert(SpvOpTypePointer == ptrInst->opcode()); + auto id = ptrInst->words()[3]; + auto id_inst = vstate.FindDef(id); + // Jump through one level of arraying. + if (id_inst->opcode() == SpvOpTypeArray || + id_inst->opcode() == SpvOpTypeRuntimeArray) { + id = id_inst->GetOperandAs(1u); + id_inst = vstate.FindDef(id); + } + // Struct requirement is checked on variables so just move on here. + if (SpvOpTypeStruct != id_inst->opcode()) continue; + MemberConstraints constraints; + ComputeMemberConstraintsForStruct(&constraints, id, LayoutConstraints(), + vstate); + // Prepare for messages + const char* sc_str = + uniform ? "Uniform" + : (push_constant ? "PushConstant" : "StorageBuffer"); + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + const bool block = hasDecoration(id, SpvDecorationBlock, vstate); + const bool buffer_block = + hasDecoration(id, SpvDecorationBufferBlock, vstate); + if (storage_buffer && buffer_block) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << "Storage buffer id '" << var_id + << " In Vulkan, BufferBlock is disallowed on variables in " + "the StorageBuffer storage class"; + } + // Vulkan 14.5.1/2: Check Block decoration for PushConstant, Uniform + // and StorageBuffer variables. Uniform can also use BufferBlock. + if (push_constant && !block) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "PushConstant id '" << id + << "' is missing Block decoration.\n" + << "From Vulkan spec, section 14.5.1:\n" + << "Such variables must be identified with a Block " + "decoration"; + } + if (storage_buffer && !block) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "StorageBuffer id '" << id + << "' is missing Block decoration.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "Such variables must be identified with a Block " + "decoration"; + } + if (uniform && !block && !buffer_block) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Uniform id '" << id + << "' is missing Block or BufferBlock decoration.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "Such variables must be identified with a Block or " + "BufferBlock decoration"; + } + // Vulkan 14.5.2: Check DescriptorSet and Binding decoration for + // Uniform and StorageBuffer variables. + if (uniform || storage_buffer) { + auto entry_points = vstate.EntryPointReferences(var_id); + if (!entry_points.empty() && + !hasDecoration(var_id, SpvDecorationDescriptorSet, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << sc_str << " id '" << var_id + << "' is missing DescriptorSet decoration.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + if (!entry_points.empty() && + !hasDecoration(var_id, SpvDecorationBinding, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(var_id)) + << sc_str << " id '" << var_id + << "' is missing Binding decoration.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "These variables must have DescriptorSet and Binding " + "decorations specified"; + } + } + } + + for (const auto& dec : vstate.id_decorations(id)) { + const bool blockDeco = SpvDecorationBlock == dec.dec_type(); + const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type(); + const bool blockRules = uniform && blockDeco; + const bool bufferRules = + (uniform && bufferDeco) || (push_constant && blockDeco) || + ((storage_buffer || phys_storage_buffer) && blockDeco); + if (uniform && blockDeco) { + vstate.RegisterPointerToUniformBlock(ptrInst->id()); + vstate.RegisterStructForUniformBlock(id); + } + if ((uniform && bufferDeco) || + ((storage_buffer || phys_storage_buffer) && blockDeco)) { + vstate.RegisterPointerToStorageBuffer(ptrInst->id()); + vstate.RegisterStructForStorageBuffer(id); + } + + if (blockRules || bufferRules) { + const char* deco_str = blockDeco ? "Block" : "BufferBlock"; + spv_result_t recursive_status = SPV_SUCCESS; + if (isMissingOffsetInStruct(id, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must be explicitly laid out with Offset " + "decorations."; + } else if (hasDecoration(id, SpvDecorationGLSLShared, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must not use GLSLShared decoration."; + } else if (hasDecoration(id, SpvDecorationGLSLPacked, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must not use GLSLPacked decoration."; + } else if (!checkForRequiredDecoration(id, SpvDecorationArrayStride, + SpvOpTypeArray, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must be explicitly laid out with ArrayStride " + "decorations."; + } else if (!checkForRequiredDecoration(id, + SpvDecorationMatrixStride, + SpvOpTypeMatrix, vstate)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "Structure id " << id << " decorated as " << deco_str + << " must be explicitly laid out with MatrixStride " + "decorations."; + } else if (blockRules && + (SPV_SUCCESS != (recursive_status = checkLayout( + id, sc_str, deco_str, true, 0, + constraints, vstate)))) { + return recursive_status; + } else if (bufferRules && + (SPV_SUCCESS != (recursive_status = checkLayout( + id, sc_str, deco_str, false, 0, + constraints, vstate)))) { + return recursive_status; + } + } + } + } + } + } + return SPV_SUCCESS; +} + +// Returns true if |decoration| cannot be applied to the same id more than once. +bool AtMostOncePerId(SpvDecoration decoration) { + return decoration == SpvDecorationArrayStride; +} + +// Returns true if |decoration| cannot be applied to the same member more than +// once. +bool AtMostOncePerMember(SpvDecoration decoration) { + switch (decoration) { + case SpvDecorationOffset: + case SpvDecorationMatrixStride: + case SpvDecorationRowMajor: + case SpvDecorationColMajor: + return true; + default: + return false; + } +} + +// Returns the string name for |decoration|. +const char* GetDecorationName(SpvDecoration decoration) { + switch (decoration) { + case SpvDecorationAliased: + return "Aliased"; + case SpvDecorationRestrict: + return "Restrict"; + case SpvDecorationArrayStride: + return "ArrayStride"; + case SpvDecorationOffset: + return "Offset"; + case SpvDecorationMatrixStride: + return "MatrixStride"; + case SpvDecorationRowMajor: + return "RowMajor"; + case SpvDecorationColMajor: + return "ColMajor"; + case SpvDecorationBlock: + return "Block"; + case SpvDecorationBufferBlock: + return "BufferBlock"; + default: + return ""; + } +} + +spv_result_t CheckDecorationsCompatibility(ValidationState_t& vstate) { + using PerIDKey = std::tuple; + using PerMemberKey = std::tuple; + + // An Array of pairs where the decorations in the pair cannot both be applied + // to the same id. + static const SpvDecoration mutually_exclusive_per_id[][2] = { + {SpvDecorationBlock, SpvDecorationBufferBlock}, + {SpvDecorationRestrict, SpvDecorationAliased}}; + static const auto num_mutually_exclusive_per_id_pairs = + sizeof(mutually_exclusive_per_id) / (2 * sizeof(SpvDecoration)); + + // An Array of pairs where the decorations in the pair cannot both be applied + // to the same member. + static const SpvDecoration mutually_exclusive_per_member[][2] = { + {SpvDecorationRowMajor, SpvDecorationColMajor}}; + static const auto num_mutually_exclusive_per_mem_pairs = + sizeof(mutually_exclusive_per_member) / (2 * sizeof(SpvDecoration)); + + std::set seen_per_id; + std::set seen_per_member; + + for (const auto& inst : vstate.ordered_instructions()) { + const auto& words = inst.words(); + if (SpvOpDecorate == inst.opcode()) { + const auto id = words[1]; + const auto dec_type = static_cast(words[2]); + const auto k = PerIDKey(dec_type, id); + const auto already_used = !seen_per_id.insert(k).second; + if (already_used && AtMostOncePerId(dec_type)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "ID '" << id << "' decorated with " + << GetDecorationName(dec_type) + << " multiple times is not allowed."; + } + // Verify certain mutually exclusive decorations are not both applied on + // an ID. + for (uint32_t pair_idx = 0; + pair_idx < num_mutually_exclusive_per_id_pairs; ++pair_idx) { + SpvDecoration excl_dec_type = SpvDecorationMax; + if (mutually_exclusive_per_id[pair_idx][0] == dec_type) { + excl_dec_type = mutually_exclusive_per_id[pair_idx][1]; + } else if (mutually_exclusive_per_id[pair_idx][1] == dec_type) { + excl_dec_type = mutually_exclusive_per_id[pair_idx][0]; + } else { + continue; + } + + const auto excl_k = PerIDKey(excl_dec_type, id); + if (seen_per_id.find(excl_k) != seen_per_id.end()) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "ID '" << id << "' decorated with both " + << GetDecorationName(dec_type) << " and " + << GetDecorationName(excl_dec_type) << " is not allowed."; + } + } + } else if (SpvOpMemberDecorate == inst.opcode()) { + const auto id = words[1]; + const auto member_id = words[2]; + const auto dec_type = static_cast(words[3]); + const auto k = PerMemberKey(dec_type, id, member_id); + const auto already_used = !seen_per_member.insert(k).second; + if (already_used && AtMostOncePerMember(dec_type)) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "ID '" << id << "', member '" << member_id + << "' decorated with " << GetDecorationName(dec_type) + << " multiple times is not allowed."; + } + // Verify certain mutually exclusive decorations are not both applied on + // a (ID, member) tuple. + for (uint32_t pair_idx = 0; + pair_idx < num_mutually_exclusive_per_mem_pairs; ++pair_idx) { + SpvDecoration excl_dec_type = SpvDecorationMax; + if (mutually_exclusive_per_member[pair_idx][0] == dec_type) { + excl_dec_type = mutually_exclusive_per_member[pair_idx][1]; + } else if (mutually_exclusive_per_member[pair_idx][1] == dec_type) { + excl_dec_type = mutually_exclusive_per_member[pair_idx][0]; + } else { + continue; + } + + const auto excl_k = PerMemberKey(excl_dec_type, id, member_id); + if (seen_per_member.find(excl_k) != seen_per_member.end()) { + return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) + << "ID '" << id << "', member '" << member_id + << "' decorated with both " << GetDecorationName(dec_type) + << " and " << GetDecorationName(excl_dec_type) + << " is not allowed."; + } + } + } + } + return SPV_SUCCESS; +} + +spv_result_t CheckVulkanMemoryModelDeprecatedDecorations( + ValidationState_t& vstate) { + if (vstate.memory_model() != SpvMemoryModelVulkanKHR) return SPV_SUCCESS; + + std::string msg; + std::ostringstream str(msg); + for (const auto& def : vstate.all_definitions()) { + const auto inst = def.second; + const auto id = inst->id(); + for (const auto& dec : vstate.id_decorations(id)) { + const auto member = dec.struct_member_index(); + if (dec.dec_type() == SpvDecorationCoherent || + dec.dec_type() == SpvDecorationVolatile) { + str << (dec.dec_type() == SpvDecorationCoherent ? "Coherent" + : "Volatile"); + str << " decoration targeting " << vstate.getIdName(id); + if (member != Decoration::kInvalidMember) { + str << " (member index " << member << ")"; + } + str << " is banned when using the Vulkan memory model."; + return vstate.diag(SPV_ERROR_INVALID_ID, inst) << str.str(); + } + } + } + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for FPRoundingMode +// decorations. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckFPRoundingModeForShaders(ValidationState_t& vstate, + const Instruction& inst) { + // Validates width-only conversion instruction for floating-point object + // i.e., OpFConvert + if (inst.opcode() != SpvOpFConvert) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to a " + "width-only conversion instruction for floating-point " + "object."; + } + + // Validates Object operand of an OpStore + for (const auto& use : inst.uses()) { + const auto store = use.first; + if (store->opcode() == SpvOpFConvert) continue; + if (spvOpcodeIsDebug(store->opcode())) continue; + if (store->IsNonSemantic()) continue; + if (spvOpcodeIsDecoration(store->opcode())) continue; + if (store->opcode() != SpvOpStore) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore."; + } + + if (use.second != 2) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore."; + } + + const auto ptr_inst = vstate.FindDef(store->GetOperandAs(0)); + const auto ptr_type = vstate.FindDef(ptr_inst->GetOperandAs(0)); + + const auto half_float_id = ptr_type->GetOperandAs(2); + if (!vstate.IsFloatScalarOrVectorType(half_float_id) || + vstate.GetBitWidth(half_float_id) != 16) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore storing through a pointer " + "to " + "a 16-bit floating-point scalar or vector object."; + } + + // Validates storage class of the pointer to the OpStore + const auto storage = ptr_type->GetOperandAs(1); + if (storage != SpvStorageClassStorageBuffer && + storage != SpvStorageClassUniform && + storage != SpvStorageClassPushConstant && + storage != SpvStorageClassInput && storage != SpvStorageClassOutput && + storage != SpvStorageClassPhysicalStorageBufferEXT) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "FPRoundingMode decoration can be applied only to the " + "Object operand of an OpStore in the StorageBuffer, " + "PhysicalStorageBufferEXT, Uniform, PushConstant, Input, or " + "Output Storage Classes."; + } + } + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for the NonWritable +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. The |inst| parameter is the object being decorated. This must +// be called after TypePass and AnnotateCheckDecorationsOfBuffers are called. +spv_result_t CheckNonWritableDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + + if (decoration.struct_member_index() == Decoration::kInvalidMember) { + // The target must be a memory object declaration. + // First, it must be a variable or function parameter. + const auto opcode = inst.opcode(); + const auto type_id = inst.type_id(); + if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of NonWritable decoration must be a memory object " + "declaration (a variable or a function parameter)"; + } + const auto var_storage_class = opcode == SpvOpVariable + ? inst.GetOperandAs(2) + : SpvStorageClassMax; + if ((var_storage_class == SpvStorageClassFunction || + var_storage_class == SpvStorageClassPrivate) && + vstate.features().nonwritable_var_in_function_or_private) { + // New permitted feature in SPIR-V 1.4. + } else if ( + // It may point to a UBO, SSBO, or storage image. + vstate.IsPointerToUniformBlock(type_id) || + vstate.IsPointerToStorageBuffer(type_id) || + vstate.IsPointerToStorageImage(type_id)) { + } else { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of NonWritable decoration is invalid: must point to a " + "storage image, uniform block, " + << (vstate.features().nonwritable_var_in_function_or_private + ? "storage buffer, or variable in Private or Function " + "storage class" + : "or storage buffer"); + } + } + + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for Uniform or +// UniformId decorations. Otherwise emits a diagnostic and returns something +// other than SPV_SUCCESS. Assumes each decoration on a group has been +// propagated down to the group members. The |inst| parameter is the object +// being decorated. +spv_result_t CheckUniformDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + const char* const dec_name = + decoration.dec_type() == SpvDecorationUniform ? "Uniform" : "UniformId"; + + // Uniform or UniformId must decorate an "object" + // - has a result ID + // - is an instantiation of a non-void type. So it has a type ID, and that + // type is not void. + + // We already know the result ID is non-zero. + + if (inst.type_id() == 0) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration applied to a non-object"; + } + if (Instruction* type_inst = vstate.FindDef(inst.type_id())) { + if (type_inst->opcode() == SpvOpTypeVoid) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration applied to a value with void type"; + } + } else { + // We might never get here because this would have been rejected earlier in + // the flow. + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration applied to an object with invalid type"; + } + + // Use of Uniform with OpDecorate is checked elsewhere. + // Use of UniformId with OpDecorateId is checked elsewhere. + + if (decoration.dec_type() == SpvDecorationUniformId) { + assert(decoration.params().size() == 1 && + "Grammar ensures UniformId has one parameter"); + + // The scope id is an execution scope. + if (auto error = + ValidateExecutionScope(vstate, &inst, decoration.params()[0])) + return error; + } + + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for NoSignedWrap or +// NoUnsignedWrap decorations. Otherwise emits a diagnostic and returns +// something other than SPV_SUCCESS. Assumes each decoration on a group has been +// propagated down to the group members. +spv_result_t CheckIntegerWrapDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + switch (inst.opcode()) { + case SpvOpIAdd: + case SpvOpISub: + case SpvOpIMul: + case SpvOpShiftLeftLogical: + case SpvOpSNegate: + return SPV_SUCCESS; + case SpvOpExtInst: + // TODO(dneto): Only certain extended instructions allow these + // decorations. For now allow anything. + return SPV_SUCCESS; + default: + break; + } + + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << (decoration.dec_type() == SpvDecorationNoSignedWrap + ? "NoSignedWrap" + : "NoUnsignedWrap") + << " decoration may not be applied to " + << spvOpcodeString(inst.opcode()); +} + +// Returns SPV_SUCCESS if validation rules are satisfied for the Component +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckComponentDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + + uint32_t type_id; + if (decoration.struct_member_index() == Decoration::kInvalidMember) { + // The target must be a memory object declaration. + const auto opcode = inst.opcode(); + if (opcode != SpvOpVariable && opcode != SpvOpFunctionParameter) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of Component decoration must be a memory object " + "declaration (a variable or a function parameter)"; + } + + // Only valid for the Input and Output Storage Classes. + const auto storage_class = opcode == SpvOpVariable + ? inst.GetOperandAs(2) + : SpvStorageClassMax; + if (storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput && + storage_class != SpvStorageClassMax) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Target of Component decoration is invalid: must point to a " + "Storage Class of Input(1) or Output(3). Found Storage " + "Class " + << storage_class; + } + + type_id = inst.type_id(); + if (vstate.IsPointerType(type_id)) { + const auto pointer = vstate.FindDef(type_id); + type_id = pointer->GetOperandAs(2); + } + } else { + if (inst.opcode() != SpvOpTypeStruct) { + return vstate.diag(SPV_ERROR_INVALID_DATA, &inst) + << "Attempted to get underlying data type via member index for " + "non-struct type."; + } + type_id = inst.word(decoration.struct_member_index() + 2); + } + + if (spvIsVulkanEnv(vstate.context()->target_env)) { + // Strip the array, if present. + if (vstate.GetIdOpcode(type_id) == SpvOpTypeArray) { + type_id = vstate.FindDef(type_id)->word(2u); + } + + if (!vstate.IsIntScalarOrVectorType(type_id) && + !vstate.IsFloatScalarOrVectorType(type_id)) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Component decoration specified for type " + << vstate.getIdName(type_id) << " that is not a scalar or vector"; + } + + // For 16-, and 32-bit types, it is invalid if this sequence of components + // gets larger than 3. + const auto bit_width = vstate.GetBitWidth(type_id); + if (bit_width == 16 || bit_width == 32) { + assert(decoration.params().size() == 1 && + "Grammar ensures Component has one parameter"); + + const auto component = decoration.params()[0]; + const auto last_component = component + vstate.GetDimension(type_id) - 1; + if (last_component > 3) { + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Sequence of components starting with " << component + << " and ending with " << last_component + << " gets larger than 3"; + } + } + } + + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if validation rules are satisfied for the Block +// decoration. Otherwise emits a diagnostic and returns something other than +// SPV_SUCCESS. +spv_result_t CheckBlockDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + assert(inst.id() && "Parser ensures the target of the decoration has an ID"); + if (inst.opcode() != SpvOpTypeStruct) { + const char* const dec_name = + decoration.dec_type() == SpvDecorationBlock ? "Block" : "BufferBlock"; + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << dec_name << " decoration on a non-struct type."; + } + return SPV_SUCCESS; +} + +spv_result_t CheckLocationDecoration(ValidationState_t& vstate, + const Instruction& inst, + const Decoration& decoration) { + if (inst.opcode() == SpvOpVariable) return SPV_SUCCESS; + + if (decoration.struct_member_index() != Decoration::kInvalidMember && + inst.opcode() == SpvOpTypeStruct) { + return SPV_SUCCESS; + } + + return vstate.diag(SPV_ERROR_INVALID_ID, &inst) + << "Location decoration can only be applied to a variable or member " + "of a structure type"; +} + +#define PASS_OR_BAIL_AT_LINE(X, LINE) \ + { \ + spv_result_t e##LINE = (X); \ + if (e##LINE != SPV_SUCCESS) return e##LINE; \ + } +#define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__) + +// Check rules for decorations where we start from the decoration rather +// than the decorated object. Assumes each decoration on a group have been +// propagated down to the group members. +spv_result_t CheckDecorationsFromDecoration(ValidationState_t& vstate) { + // Some rules are only checked for shaders. + const bool is_shader = vstate.HasCapability(SpvCapabilityShader); + + for (const auto& kv : vstate.id_decorations()) { + const uint32_t id = kv.first; + const auto& decorations = kv.second; + if (decorations.empty()) continue; + + const Instruction* inst = vstate.FindDef(id); + assert(inst); + + // We assume the decorations applied to a decoration group have already + // been propagated down to the group members. + if (inst->opcode() == SpvOpDecorationGroup) continue; + + for (const auto& decoration : decorations) { + switch (decoration.dec_type()) { + case SpvDecorationComponent: + PASS_OR_BAIL(CheckComponentDecoration(vstate, *inst, decoration)); + break; + case SpvDecorationFPRoundingMode: + if (is_shader) + PASS_OR_BAIL(CheckFPRoundingModeForShaders(vstate, *inst)); + break; + case SpvDecorationNonWritable: + PASS_OR_BAIL(CheckNonWritableDecoration(vstate, *inst, decoration)); + break; + case SpvDecorationUniform: + case SpvDecorationUniformId: + PASS_OR_BAIL(CheckUniformDecoration(vstate, *inst, decoration)); + break; + case SpvDecorationNoSignedWrap: + case SpvDecorationNoUnsignedWrap: + PASS_OR_BAIL(CheckIntegerWrapDecoration(vstate, *inst, decoration)); + break; + case SpvDecorationBlock: + case SpvDecorationBufferBlock: + PASS_OR_BAIL(CheckBlockDecoration(vstate, *inst, decoration)); + break; + case SpvDecorationLocation: + PASS_OR_BAIL(CheckLocationDecoration(vstate, *inst, decoration)); + break; + default: + break; + } + } + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ValidateDecorations(ValidationState_t& vstate) { + if (auto error = CheckImportedVariableInitialization(vstate)) return error; + if (auto error = CheckDecorationsOfEntryPoints(vstate)) return error; + if (auto error = CheckDecorationsOfBuffers(vstate)) return error; + if (auto error = CheckDecorationsCompatibility(vstate)) return error; + if (auto error = CheckLinkageAttrOfFunctions(vstate)) return error; + if (auto error = CheckVulkanMemoryModelDeprecatedDecorations(vstate)) + return error; + if (auto error = CheckDecorationsFromDecoration(vstate)) return error; + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_derivatives.cpp b/third_party/spirv-tools/source/val/validate_derivatives.cpp new file mode 100644 index 0000000..067cc96 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_derivatives.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of derivative SPIR-V instructions. + +#include "source/val/validate.h" + +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of derivative instructions. +spv_result_t DerivativesPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case SpvOpDPdx: + case SpvOpDPdy: + case SpvOpFwidth: + case SpvOpDPdxFine: + case SpvOpDPdyFine: + case SpvOpFwidthFine: + case SpvOpDPdxCoarse: + case SpvOpDPdyCoarse: + case SpvOpFwidthCoarse: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be float scalar or vector type: " + << spvOpcodeString(opcode); + } + if (!_.ContainsSizedIntOrFloatType(result_type, SpvOpTypeFloat, 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result type component width must be 32 bits"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 2); + if (p_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected P type and Result Type to be the same: " + << spvOpcodeString(opcode); + } + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([opcode](SpvExecutionModel model, + std::string* message) { + if (model != SpvExecutionModelFragment && + model != SpvExecutionModelGLCompute) { + if (message) { + *message = + std::string( + "Derivative instructions require Fragment or GLCompute " + "execution model: ") + + spvOpcodeString(opcode); + } + return false; + } + return true; + }); + _.function(inst->function()->id()) + ->RegisterLimitation([opcode](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const auto* models = state.GetExecutionModels(entry_point->id()); + const auto* modes = state.GetExecutionModes(entry_point->id()); + if (models->find(SpvExecutionModelGLCompute) != models->end() && + modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()) { + if (message) { + *message = std::string( + "Derivative instructions require " + "DerivativeGroupQuadsNV " + "or DerivativeGroupLinearNV execution mode for " + "GLCompute execution model: ") + + spvOpcodeString(opcode); + } + return false; + } + return true; + }); + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_execution_limitations.cpp b/third_party/spirv-tools/source/val/validate_execution_limitations.cpp new file mode 100644 index 0000000..aac1c49 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_execution_limitations.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include "source/val/function.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateExecutionLimitations(ValidationState_t& _, + const Instruction* inst) { + if (inst->opcode() != SpvOpFunction) { + return SPV_SUCCESS; + } + + const auto func = _.function(inst->id()); + if (!func) { + return _.diag(SPV_ERROR_INTERNAL, inst) + << "Internal error: missing function id " << inst->id() << "."; + } + + for (uint32_t entry_id : _.FunctionEntryPoints(inst->id())) { + const auto* models = _.GetExecutionModels(entry_id); + if (models) { + if (models->empty()) { + return _.diag(SPV_ERROR_INTERNAL, inst) + << "Internal error: empty execution models for function id " + << entry_id << "."; + } + for (const auto model : *models) { + std::string reason; + if (!func->IsCompatibleWithExecutionModel(model, &reason)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpEntryPoint Entry Point '" << _.getIdName(entry_id) + << "'s callgraph contains function " + << _.getIdName(inst->id()) + << ", which cannot be used with the current execution " + "model:\n" + << reason; + } + } + } + + std::string reason; + if (!func->CheckLimitations(_, _.function(entry_id), &reason)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpEntryPoint Entry Point '" << _.getIdName(entry_id) + << "'s callgraph contains function " + << _.getIdName(inst->id()) + << ", which cannot be used with the current execution " + "modes:\n" + << reason; + } + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_extensions.cpp b/third_party/spirv-tools/source/val/validate_extensions.cpp new file mode 100644 index 0000000..17b0446 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_extensions.cpp @@ -0,0 +1,3160 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of extension SPIR-V instructions. +#include +#include +#include +#include + +#include "spirv/unified1/NonSemanticClspvReflection.h" + +#include "OpenCLDebugInfo100.h" +#include "source/diagnostic.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/latest_version_glsl_std_450_header.h" +#include "source/latest_version_opencl_std_header.h" +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +uint32_t GetSizeTBitWidth(const ValidationState_t& _) { + if (_.addressing_model() == SpvAddressingModelPhysical32) return 32; + + if (_.addressing_model() == SpvAddressingModelPhysical64) return 64; + + return 0; +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an instruction with |expected_opcode|. +spv_result_t ValidateOperandForDebugInfo( + ValidationState_t& _, const std::string& operand_name, + SpvOp expected_opcode, const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + auto* operand = _.FindDef(inst->word(word_index)); + if (operand->opcode() != expected_opcode) { + spv_opcode_desc desc = nullptr; + if (_.grammar().lookupOpcode(expected_opcode, &desc) != SPV_SUCCESS || + !desc) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << operand_name << " is invalid"; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << operand_name << " must be a result id of " + << "Op" << desc->name; + } + return SPV_SUCCESS; +} + +#define CHECK_OPERAND(NAME, opcode, index) \ + do { \ + auto result = ValidateOperandForDebugInfo(_, NAME, opcode, inst, index, \ + ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } while (0) + +// True if the operand of a debug info instruction |inst| at |word_index| +// satisifies |expectation| that is given as a function. Otherwise, +// returns false. +bool DoesDebugInfoOperandMatchExpectation( + const ValidationState_t& _, + const std::function& expectation, + const Instruction* inst, uint32_t word_index) { + if (inst->words().size() <= word_index) return false; + auto* debug_inst = _.FindDef(inst->word(word_index)); + if (debug_inst->opcode() != SpvOpExtInst || + debug_inst->ext_inst_type() != SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100 || + !expectation(OpenCLDebugInfo100Instructions(debug_inst->word(4)))) { + return false; + } + return true; +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an debug info instruction whose debug instruction type +// is |expected_debug_inst|. +spv_result_t ValidateDebugInfoOperand( + ValidationState_t& _, const std::string& debug_inst_name, + OpenCLDebugInfo100Instructions expected_debug_inst, const Instruction* inst, + uint32_t word_index, const std::function& ext_inst_name) { + std::function expectation = + [expected_debug_inst](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == expected_debug_inst; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + spv_ext_inst_desc desc = nullptr; + _.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + expected_debug_inst, &desc); + if (_.grammar().lookupExtInst(SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, + expected_debug_inst, &desc) != SPV_SUCCESS || + !desc) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name << " is invalid"; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name << " must be a result id of " + << desc->name; +} + +#define CHECK_DEBUG_OPERAND(NAME, debug_opcode, index) \ + do { \ + auto result = ValidateDebugInfoOperand(_, NAME, debug_opcode, inst, index, \ + ext_inst_name); \ + if (result != SPV_SUCCESS) return result; \ + } while (0) + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of an debug info instruction with DebugTypeBasic. +spv_result_t ValidateOperandBaseType( + ValidationState_t& _, const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + return ValidateDebugInfoOperand(_, "Base Type", + OpenCLDebugInfo100DebugTypeBasic, inst, + word_index, ext_inst_name); +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of a debug lexical scope instruction which is one of +// DebugCompilationUnit, DebugFunction, DebugLexicalBlock, or +// DebugTypeComposite. +spv_result_t ValidateOperandLexicalScope( + ValidationState_t& _, const std::string& debug_inst_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name) { + std::function expectation = + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugCompilationUnit || + dbg_inst == OpenCLDebugInfo100DebugFunction || + dbg_inst == OpenCLDebugInfo100DebugLexicalBlock || + dbg_inst == OpenCLDebugInfo100DebugTypeComposite; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name + << " must be a result id of a lexical scope"; +} + +// Check that the operand of a debug info instruction |inst| at |word_index| +// is a result id of a debug type instruction (See DebugTypeXXX in +// "4.3. Type instructions" section of OpenCL.DebugInfo.100 spec. +spv_result_t ValidateOperandDebugType( + ValidationState_t& _, const std::string& debug_inst_name, + const Instruction* inst, uint32_t word_index, + const std::function& ext_inst_name, + bool allow_template_param) { + std::function expectation = + [&allow_template_param](OpenCLDebugInfo100Instructions dbg_inst) { + if (allow_template_param && + (dbg_inst == OpenCLDebugInfo100DebugTypeTemplateParameter || + dbg_inst == + OpenCLDebugInfo100DebugTypeTemplateTemplateParameter)) { + return true; + } + return OpenCLDebugInfo100DebugTypeBasic <= dbg_inst && + dbg_inst <= OpenCLDebugInfo100DebugTypeTemplate; + }; + if (DoesDebugInfoOperandMatchExpectation(_, expectation, inst, word_index)) + return SPV_SUCCESS; + + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand " << debug_inst_name + << " is not a valid debug type"; +} + +bool IsUint32Constant(ValidationState_t& _, uint32_t id) { + auto inst = _.FindDef(id); + if (!inst || inst->opcode() != SpvOpConstant) { + return false; + } + + auto type = _.FindDef(inst->type_id()); + if (!type || type->opcode() != SpvOpTypeInt) { + return false; + } + + if (type->GetOperandAs(1) != 32) { + return false; + } + + if (type->GetOperandAs(2) != 0) { + return false; + } + + return true; +} + +spv_result_t ValidateClspvReflectionKernel(ValidationState_t& _, + const Instruction* inst) { + const auto kernel_id = inst->GetOperandAs(4); + const auto kernel = _.FindDef(kernel_id); + if (kernel->opcode() != SpvOpFunction) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel does not reference a function"; + } + + bool found_kernel = false; + for (auto entry_point : _.entry_points()) { + if (entry_point == kernel_id) { + found_kernel = true; + break; + } + } + if (!found_kernel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel does not reference an entry-point"; + } + + const auto* exec_models = _.GetExecutionModels(kernel_id); + if (!exec_models || exec_models->empty()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel does not reference an entry-point"; + } + for (auto exec_model : *exec_models) { + if (exec_model != SpvExecutionModelGLCompute) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel must refer only to GLCompute entry-points"; + } + } + + auto name = _.FindDef(inst->GetOperandAs(5)); + if (!name || name->opcode() != SpvOpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString"; + } + + const std::string name_str = reinterpret_cast( + name->words().data() + name->operands()[1].offset); + bool found = false; + for (auto& desc : _.entry_point_descriptions(kernel_id)) { + if (name_str == desc.name) { + found = true; + break; + } + } + if (!found) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Name must match an entry-point for Kernel"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentInfo(ValidationState_t& _, + const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (_.GetIdOpcode(inst->GetOperandAs(4)) != SpvOpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Name must be an OpString"; + } + if (num_operands > 5) { + if (_.GetIdOpcode(inst->GetOperandAs(5)) != SpvOpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "TypeName must be an OpString"; + } + } + if (num_operands > 6) { + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "AddressQualifier must be a 32-bit unsigned integer " + "OpConstant"; + } + } + if (num_operands > 7) { + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "AccessQualifier must be a 32-bit unsigned integer " + "OpConstant"; + } + } + if (num_operands > 8) { + if (!IsUint32Constant(_, inst->GetOperandAs(8))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "TypeQualifier must be a 32-bit unsigned integer " + "OpConstant"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateKernelDecl(ValidationState_t& _, const Instruction* inst) { + const auto decl_id = inst->GetOperandAs(4); + const auto decl = _.FindDef(decl_id); + if (!decl || decl->opcode() != SpvOpExtInst) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel must be a Kernel extended instruction"; + } + + if (decl->GetOperandAs(2) != inst->GetOperandAs(2)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel must be from the same extended instruction import"; + } + + const auto ext_inst = + decl->GetOperandAs(3); + if (ext_inst != NonSemanticClspvReflectionKernel) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Kernel must be a Kernel extended instruction"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateArgInfo(ValidationState_t& _, const Instruction* inst, + uint32_t info_index) { + auto info = _.FindDef(inst->GetOperandAs(info_index)); + if (!info || info->opcode() != SpvOpExtInst) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgInfo must be an ArgumentInfo extended instruction"; + } + + if (info->GetOperandAs(2) != inst->GetOperandAs(2)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgInfo must be from the same extended instruction import"; + } + + auto ext_inst = info->GetOperandAs(3); + if (ext_inst != NonSemanticClspvReflectionArgumentInfo) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ArgInfo must be an ArgumentInfo extended instruction"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentBuffer(ValidationState_t& _, + const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (num_operands == 9) { + if (auto error = ValidateArgInfo(_, inst, 8)) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentPodBuffer(ValidationState_t& _, + const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(8))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(9))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + if (num_operands == 11) { + if (auto error = ValidateArgInfo(_, inst, 10)) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentPodPushConstant( + ValidationState_t& _, const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + if (num_operands == 9) { + if (auto error = ValidateArgInfo(_, inst, 8)) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionArgumentWorkgroup(ValidationState_t& _, + const Instruction* inst) { + const auto num_operands = inst->operands().size(); + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Ordinal must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "SpecId must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "ElemSize must be a 32-bit unsigned integer OpConstant"; + } + + if (num_operands == 9) { + if (auto error = ValidateArgInfo(_, inst, 8)) { + return error; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionSpecConstantTriple( + ValidationState_t& _, const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "X must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Y must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Z must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionSpecConstantWorkDim( + ValidationState_t& _, const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Dim must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPushConstant(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Offset must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionConstantData(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (_.GetIdOpcode(inst->GetOperandAs(6)) != SpvOpString) { + return _.diag(SPV_ERROR_INVALID_ID, inst) << "Data must be an OpString"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionSampler(ValidationState_t& _, + const Instruction* inst) { + if (!IsUint32Constant(_, inst->GetOperandAs(4))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "DescriptorSet must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Binding must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Mask must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionPropertyRequiredWorkgroupSize( + ValidationState_t& _, const Instruction* inst) { + if (auto error = ValidateKernelDecl(_, inst)) { + return error; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(5))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "X must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(6))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Y must be a 32-bit unsigned integer OpConstant"; + } + + if (!IsUint32Constant(_, inst->GetOperandAs(7))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Z must be a 32-bit unsigned integer OpConstant"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateClspvReflectionInstruction(ValidationState_t& _, + const Instruction* inst, + uint32_t /*version*/) { + if (!_.IsVoidType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Return Type must be OpTypeVoid"; + } + + auto ext_inst = inst->GetOperandAs(3); + switch (ext_inst) { + case NonSemanticClspvReflectionKernel: + return ValidateClspvReflectionKernel(_, inst); + case NonSemanticClspvReflectionArgumentInfo: + return ValidateClspvReflectionArgumentInfo(_, inst); + case NonSemanticClspvReflectionArgumentStorageBuffer: + case NonSemanticClspvReflectionArgumentUniform: + case NonSemanticClspvReflectionArgumentSampledImage: + case NonSemanticClspvReflectionArgumentStorageImage: + case NonSemanticClspvReflectionArgumentSampler: + return ValidateClspvReflectionArgumentBuffer(_, inst); + case NonSemanticClspvReflectionArgumentPodStorageBuffer: + case NonSemanticClspvReflectionArgumentPodUniform: + return ValidateClspvReflectionArgumentPodBuffer(_, inst); + case NonSemanticClspvReflectionArgumentPodPushConstant: + return ValidateClspvReflectionArgumentPodPushConstant(_, inst); + case NonSemanticClspvReflectionArgumentWorkgroup: + return ValidateClspvReflectionArgumentWorkgroup(_, inst); + case NonSemanticClspvReflectionSpecConstantWorkgroupSize: + case NonSemanticClspvReflectionSpecConstantGlobalOffset: + return ValidateClspvReflectionSpecConstantTriple(_, inst); + case NonSemanticClspvReflectionSpecConstantWorkDim: + return ValidateClspvReflectionSpecConstantWorkDim(_, inst); + case NonSemanticClspvReflectionPushConstantGlobalOffset: + case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize: + case NonSemanticClspvReflectionPushConstantGlobalSize: + case NonSemanticClspvReflectionPushConstantRegionOffset: + case NonSemanticClspvReflectionPushConstantNumWorkgroups: + case NonSemanticClspvReflectionPushConstantRegionGroupOffset: + return ValidateClspvReflectionPushConstant(_, inst); + case NonSemanticClspvReflectionConstantDataStorageBuffer: + case NonSemanticClspvReflectionConstantDataUniform: + return ValidateClspvReflectionConstantData(_, inst); + case NonSemanticClspvReflectionLiteralSampler: + return ValidateClspvReflectionSampler(_, inst); + case NonSemanticClspvReflectionPropertyRequiredWorkgroupSize: + return ValidateClspvReflectionPropertyRequiredWorkgroupSize(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +bool IsConstIntScalarTypeWith32Or64Bits(ValidationState_t& _, + Instruction* instr) { + if (instr->opcode() != SpvOpConstant) return false; + if (!_.IsIntScalarType(instr->type_id())) return false; + uint32_t size_in_bits = _.GetBitWidth(instr->type_id()); + return size_in_bits == 32 || size_in_bits == 64; +} + +bool IsConstWithIntScalarType(ValidationState_t& _, const Instruction* inst, + uint32_t word_index) { + auto* int_scalar_const = _.FindDef(inst->word(word_index)); + if (int_scalar_const->opcode() == SpvOpConstant && + _.IsIntScalarType(int_scalar_const->type_id())) { + return true; + } + return false; +} + +bool IsDebugVariableWithIntScalarType(ValidationState_t& _, + const Instruction* inst, + uint32_t word_index) { + auto* dbg_int_scalar_var = _.FindDef(inst->word(word_index)); + if (OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) == + OpenCLDebugInfo100DebugLocalVariable || + OpenCLDebugInfo100Instructions(dbg_int_scalar_var->word(4)) == + OpenCLDebugInfo100DebugGlobalVariable) { + auto* dbg_type = _.FindDef(dbg_int_scalar_var->word(6)); + if (OpenCLDebugInfo100Instructions(dbg_type->word(4)) == + OpenCLDebugInfo100DebugTypeBasic && + (OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) == + OpenCLDebugInfo100Signed || + OpenCLDebugInfo100DebugBaseTypeAttributeEncoding(dbg_type->word(7)) == + OpenCLDebugInfo100Unsigned)) { + return true; + } + } + return false; +} + +} // anonymous namespace + +spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { + if (spvIsWebGPUEnv(_.context()->target_env)) { + std::string extension = GetExtensionString(&(inst->c_inst())); + + if (extension != ExtensionToString(kSPV_KHR_vulkan_memory_model)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For WebGPU, the only valid parameter to OpExtension is " + << "\"" << ExtensionToString(kSPV_KHR_vulkan_memory_model) + << "\"."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExtInstImport(ValidationState_t& _, + const Instruction* inst) { + const auto name_id = 1; + if (spvIsWebGPUEnv(_.context()->target_env)) { + const std::string name(reinterpret_cast( + inst->words().data() + inst->operands()[name_id].offset)); + if (name != "GLSL.std.450") { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For WebGPU, the only valid parameter to OpExtInstImport is " + "\"GLSL.std.450\"."; + } + } + + if (!_.HasExtension(kSPV_KHR_non_semantic_info)) { + const std::string name(reinterpret_cast( + inst->words().data() + inst->operands()[name_id].offset)); + if (name.find("NonSemantic.") == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "NonSemantic extended instruction sets cannot be declared " + "without SPV_KHR_non_semantic_info."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + const uint32_t num_operands = static_cast(inst->operands().size()); + + const uint32_t ext_inst_set = inst->word(3); + const uint32_t ext_inst_index = inst->word(4); + const spv_ext_inst_type_t ext_inst_type = + spv_ext_inst_type_t(inst->ext_inst_type()); + + auto ext_inst_name = [&_, ext_inst_set, ext_inst_type, ext_inst_index]() { + spv_ext_inst_desc desc = nullptr; + if (_.grammar().lookupExtInst(ext_inst_type, ext_inst_index, &desc) != + SPV_SUCCESS || + !desc) { + return std::string("Unknown ExtInst"); + } + + auto* import_inst = _.FindDef(ext_inst_set); + assert(import_inst); + + std::ostringstream ss; + ss << reinterpret_cast(import_inst->words().data() + 2); + ss << " "; + ss << desc->name; + + return ss.str(); + }; + + if (ext_inst_type == SPV_EXT_INST_TYPE_GLSL_STD_450) { + const GLSLstd450 ext_inst_key = GLSLstd450(ext_inst_index); + switch (ext_inst_key) { + case GLSLstd450Round: + case GLSLstd450RoundEven: + case GLSLstd450FAbs: + case GLSLstd450Trunc: + case GLSLstd450FSign: + case GLSLstd450Floor: + case GLSLstd450Ceil: + case GLSLstd450Fract: + case GLSLstd450Sqrt: + case GLSLstd450InverseSqrt: + case GLSLstd450FMin: + case GLSLstd450FMax: + case GLSLstd450FClamp: + case GLSLstd450FMix: + case GLSLstd450Step: + case GLSLstd450SmoothStep: + case GLSLstd450Fma: + case GLSLstd450Normalize: + case GLSLstd450FaceForward: + case GLSLstd450Reflect: + case GLSLstd450NMin: + case GLSLstd450NMax: + case GLSLstd450NClamp: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case GLSLstd450SAbs: + case GLSLstd450SSign: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FindILsb: + case GLSLstd450FindUMsb: + case GLSLstd450FindSMsb: { + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int scalar or vector type"; + } + + const uint32_t result_type_bit_width = _.GetBitWidth(result_type); + const uint32_t result_type_dimension = _.GetDimension(result_type); + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (!_.IsIntScalarOrVectorType(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected all operands to be int scalars or vectors"; + } + + if (result_type_dimension != _.GetDimension(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected all operands to have the same dimension as " + << "Result Type"; + } + + if (result_type_bit_width != _.GetBitWidth(operand_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected all operands to have the same bit width as " + << "Result Type"; + } + + if (ext_inst_key == GLSLstd450FindUMsb || + ext_inst_key == GLSLstd450FindSMsb) { + if (result_type_bit_width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "this instruction is currently limited to 32-bit width " + << "components"; + } + } + } + break; + } + + case GLSLstd450Radians: + case GLSLstd450Degrees: + case GLSLstd450Sin: + case GLSLstd450Cos: + case GLSLstd450Tan: + case GLSLstd450Asin: + case GLSLstd450Acos: + case GLSLstd450Atan: + case GLSLstd450Sinh: + case GLSLstd450Cosh: + case GLSLstd450Tanh: + case GLSLstd450Asinh: + case GLSLstd450Acosh: + case GLSLstd450Atanh: + case GLSLstd450Exp: + case GLSLstd450Exp2: + case GLSLstd450Log: + case GLSLstd450Log2: + case GLSLstd450Atan2: + case GLSLstd450Pow: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 16 or 32-bit scalar or " + "vector float type"; + } + + const uint32_t result_type_bit_width = _.GetBitWidth(result_type); + if (result_type_bit_width != 16 && result_type_bit_width != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 16 or 32-bit scalar or " + "vector float type"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case GLSLstd450Determinant: { + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (!_.GetMatrixTypeInfo(x_type, &num_rows, &num_cols, &col_type, + &component_type) || + num_rows != num_cols) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to be a square matrix"; + } + + if (result_type != component_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X component type to be equal to " + << "Result Type"; + } + break; + } + + case GLSLstd450MatrixInverse: { + uint32_t num_rows = 0; + uint32_t num_cols = 0; + uint32_t col_type = 0; + uint32_t component_type = 0; + if (!_.GetMatrixTypeInfo(result_type, &num_rows, &num_cols, &col_type, + &component_type) || + num_rows != num_cols) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a square matrix"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + break; + } + + case GLSLstd450Modf: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or vector float type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t i_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + uint32_t i_storage_class = 0; + uint32_t i_data_type = 0; + if (!_.GetPointerTypeInfo(i_type, &i_data_type, &i_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand I to be a pointer"; + } + + if (i_data_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand I data type to be equal to Result Type"; + } + + break; + } + + case GLSLstd450ModfStruct: { + std::vector result_types; + if (!_.GetStructMemberTypes(result_type, &result_types) || + result_types.size() != 2 || + !_.IsFloatScalarOrVectorType(result_types[0]) || + result_types[1] != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a struct with two identical " + << "scalar or vector float type members"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (x_type != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to members of " + << "Result Type struct"; + } + break; + } + + case GLSLstd450Frexp: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or vector float type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + uint32_t exp_storage_class = 0; + uint32_t exp_data_type = 0; + if (!_.GetPointerTypeInfo(exp_type, &exp_data_type, + &exp_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp to be a pointer"; + } + + if (!_.IsIntScalarOrVectorType(exp_data_type) || + (!_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(exp_data_type) != 32) || + (_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(exp_data_type) != 16 && + _.GetBitWidth(exp_data_type) != 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp data type to be a " + << (_.HasExtension(kSPV_AMD_gpu_shader_int16) + ? "16-bit or 32-bit " + : "32-bit ") + << "int scalar or vector type"; + } + + if (_.GetDimension(result_type) != _.GetDimension(exp_data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp data type to have the same component " + << "number as Result Type"; + } + + break; + } + + case GLSLstd450Ldexp: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or vector float type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + if (!_.IsIntScalarOrVectorType(exp_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp to be a 32-bit int scalar " + << "or vector type"; + } + + if (_.GetDimension(result_type) != _.GetDimension(exp_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Exp to have the same component " + << "number as Result Type"; + } + + break; + } + + case GLSLstd450FrexpStruct: { + std::vector result_types; + if (!_.GetStructMemberTypes(result_type, &result_types) || + result_types.size() != 2 || + !_.IsFloatScalarOrVectorType(result_types[0]) || + !_.IsIntScalarOrVectorType(result_types[1]) || + (!_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(result_types[1]) != 32) || + (_.HasExtension(kSPV_AMD_gpu_shader_int16) && + _.GetBitWidth(result_types[1]) != 16 && + _.GetBitWidth(result_types[1]) != 32) || + _.GetDimension(result_types[0]) != + _.GetDimension(result_types[1])) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a struct with two members, " + << "first member a float scalar or vector, second member a " + << (_.HasExtension(kSPV_AMD_gpu_shader_int16) + ? "16-bit or 32-bit " + : "32-bit ") + << "int scalar or vector with the same number of " + << "components as the first member"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (x_type != result_types[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to the first member " + << "of Result Type struct"; + } + break; + } + + case GLSLstd450PackSnorm4x8: + case GLSLstd450PackUnorm4x8: { + if (!_.IsIntScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be 32-bit int scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 4 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand V to be a 32-bit float vector of size 4"; + } + break; + } + + case GLSLstd450PackSnorm2x16: + case GLSLstd450PackUnorm2x16: + case GLSLstd450PackHalf2x16: { + if (!_.IsIntScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be 32-bit int scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatVectorType(v_type) || _.GetDimension(v_type) != 2 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand V to be a 32-bit float vector of size 2"; + } + break; + } + + case GLSLstd450PackDouble2x32: { + if (!_.IsFloatScalarType(result_type) || + _.GetBitWidth(result_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be 64-bit float scalar type"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntVectorType(v_type) || _.GetDimension(v_type) != 2 || + _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand V to be a 32-bit int vector of size 2"; + } + break; + } + + case GLSLstd450UnpackSnorm4x8: + case GLSLstd450UnpackUnorm4x8: { + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 4 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit float vector of size " + "4"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a 32-bit int scalar"; + } + break; + } + + case GLSLstd450UnpackSnorm2x16: + case GLSLstd450UnpackUnorm2x16: + case GLSLstd450UnpackHalf2x16: { + if (!_.IsFloatVectorType(result_type) || + _.GetDimension(result_type) != 2 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit float vector of size " + "2"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(v_type) || _.GetBitWidth(v_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a 32-bit int scalar"; + } + break; + } + + case GLSLstd450UnpackDouble2x32: { + if (!_.IsIntVectorType(result_type) || + _.GetDimension(result_type) != 2 || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit int vector of size " + "2"; + } + + const uint32_t v_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(v_type) || _.GetBitWidth(v_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand V to be a 64-bit float scalar"; + } + break; + } + + case GLSLstd450Length: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X component type to be equal to Result " + "Type"; + } + break; + } + + case GLSLstd450Distance: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p0_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 component type to be equal to " + << "Result Type"; + } + + const uint32_t p1_type = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatScalarOrVectorType(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P1 to be of float scalar or vector type"; + } + + if (result_type != _.GetComponentType(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P1 component type to be equal to " + << "Result Type"; + } + + if (_.GetDimension(p0_type) != _.GetDimension(p1_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operands P0 and P1 to have the same number of " + << "components"; + } + break; + } + + case GLSLstd450Cross: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float vector type"; + } + + if (_.GetDimension(result_type) != 3) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 3 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t y_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + if (y_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Y type to be equal to Result Type"; + } + break; + } + + case GLSLstd450Refract: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t i_type = _.GetOperandTypeId(inst, 4); + const uint32_t n_type = _.GetOperandTypeId(inst, 5); + const uint32_t eta_type = _.GetOperandTypeId(inst, 6); + + if (result_type != i_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand I to be of type equal to Result Type"; + } + + if (result_type != n_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand N to be of type equal to Result Type"; + } + + if (!_.IsFloatScalarType(eta_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Eta to be a float scalar"; + } + break; + } + + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: { + if (!_.HasCapability(SpvCapabilityInterpolationFunction)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << ext_inst_name() + << " requires capability InterpolationFunction"; + } + + if (!_.IsFloatScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit float scalar " + << "or vector type"; + } + + const uint32_t interpolant_type = _.GetOperandTypeId(inst, 4); + uint32_t interpolant_storage_class = 0; + uint32_t interpolant_data_type = 0; + if (!_.GetPointerTypeInfo(interpolant_type, &interpolant_data_type, + &interpolant_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Interpolant to be a pointer"; + } + + if (result_type != interpolant_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Interpolant data type to be equal to Result Type"; + } + + if (interpolant_storage_class != SpvStorageClassInput) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Interpolant storage class to be Input"; + } + + if (ext_inst_key == GLSLstd450InterpolateAtSample) { + const uint32_t sample_type = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarType(sample_type) || + _.GetBitWidth(sample_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Sample to be 32-bit integer"; + } + } + + if (ext_inst_key == GLSLstd450InterpolateAtOffset) { + const uint32_t offset_type = _.GetOperandTypeId(inst, 5); + if (!_.IsFloatVectorType(offset_type) || + _.GetDimension(offset_type) != 2 || + _.GetBitWidth(offset_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Offset to be a vector of 2 32-bit floats"; + } + } + + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + SpvExecutionModelFragment, + ext_inst_name() + + std::string(" requires Fragment execution model")); + break; + } + + case GLSLstd450IMix: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Extended instruction GLSLstd450IMix is not supported"; + } + + case GLSLstd450Bad: { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Encountered extended instruction GLSLstd450Bad"; + } + + case GLSLstd450Count: { + assert(0); + break; + } + } + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_STD) { + const OpenCLLIB::Entrypoints ext_inst_key = + OpenCLLIB::Entrypoints(ext_inst_index); + switch (ext_inst_key) { + case OpenCLLIB::Acos: + case OpenCLLIB::Acosh: + case OpenCLLIB::Acospi: + case OpenCLLIB::Asin: + case OpenCLLIB::Asinh: + case OpenCLLIB::Asinpi: + case OpenCLLIB::Atan: + case OpenCLLIB::Atan2: + case OpenCLLIB::Atanh: + case OpenCLLIB::Atanpi: + case OpenCLLIB::Atan2pi: + case OpenCLLIB::Cbrt: + case OpenCLLIB::Ceil: + case OpenCLLIB::Copysign: + case OpenCLLIB::Cos: + case OpenCLLIB::Cosh: + case OpenCLLIB::Cospi: + case OpenCLLIB::Erfc: + case OpenCLLIB::Erf: + case OpenCLLIB::Exp: + case OpenCLLIB::Exp2: + case OpenCLLIB::Exp10: + case OpenCLLIB::Expm1: + case OpenCLLIB::Fabs: + case OpenCLLIB::Fdim: + case OpenCLLIB::Floor: + case OpenCLLIB::Fma: + case OpenCLLIB::Fmax: + case OpenCLLIB::Fmin: + case OpenCLLIB::Fmod: + case OpenCLLIB::Hypot: + case OpenCLLIB::Lgamma: + case OpenCLLIB::Log: + case OpenCLLIB::Log2: + case OpenCLLIB::Log10: + case OpenCLLIB::Log1p: + case OpenCLLIB::Logb: + case OpenCLLIB::Mad: + case OpenCLLIB::Maxmag: + case OpenCLLIB::Minmag: + case OpenCLLIB::Nextafter: + case OpenCLLIB::Pow: + case OpenCLLIB::Powr: + case OpenCLLIB::Remainder: + case OpenCLLIB::Rint: + case OpenCLLIB::Round: + case OpenCLLIB::Rsqrt: + case OpenCLLIB::Sin: + case OpenCLLIB::Sinh: + case OpenCLLIB::Sinpi: + case OpenCLLIB::Sqrt: + case OpenCLLIB::Tan: + case OpenCLLIB::Tanh: + case OpenCLLIB::Tanpi: + case OpenCLLIB::Tgamma: + case OpenCLLIB::Trunc: + case OpenCLLIB::Half_cos: + case OpenCLLIB::Half_divide: + case OpenCLLIB::Half_exp: + case OpenCLLIB::Half_exp2: + case OpenCLLIB::Half_exp10: + case OpenCLLIB::Half_log: + case OpenCLLIB::Half_log2: + case OpenCLLIB::Half_log10: + case OpenCLLIB::Half_powr: + case OpenCLLIB::Half_recip: + case OpenCLLIB::Half_rsqrt: + case OpenCLLIB::Half_sin: + case OpenCLLIB::Half_sqrt: + case OpenCLLIB::Half_tan: + case OpenCLLIB::Native_cos: + case OpenCLLIB::Native_divide: + case OpenCLLIB::Native_exp: + case OpenCLLIB::Native_exp2: + case OpenCLLIB::Native_exp10: + case OpenCLLIB::Native_log: + case OpenCLLIB::Native_log2: + case OpenCLLIB::Native_log10: + case OpenCLLIB::Native_powr: + case OpenCLLIB::Native_recip: + case OpenCLLIB::Native_rsqrt: + case OpenCLLIB::Native_sin: + case OpenCLLIB::Native_sqrt: + case OpenCLLIB::Native_tan: + case OpenCLLIB::FClamp: + case OpenCLLIB::Degrees: + case OpenCLLIB::FMax_common: + case OpenCLLIB::FMin_common: + case OpenCLLIB::Mix: + case OpenCLLIB::Radians: + case OpenCLLIB::Step: + case OpenCLLIB::Smoothstep: + case OpenCLLIB::Sign: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Fract: + case OpenCLLIB::Modf: + case OpenCLLIB::Sincos: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand X to be equal to Result Type"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the last operand to be a pointer"; + } + + if (p_storage_class != SpvStorageClassGeneric && + p_storage_class != SpvStorageClassCrossWorkgroup && + p_storage_class != SpvStorageClassWorkgroup && + p_storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected storage class of the pointer to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (result_type != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected data type of the pointer to be equal to Result " + "Type"; + } + break; + } + + case OpenCLLIB::Frexp: + case OpenCLLIB::Lgamma_r: + case OpenCLLIB::Remquo: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + uint32_t operand_index = 4; + const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand X to be equal to Result Type"; + } + + if (ext_inst_key == OpenCLLIB::Remquo) { + const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++); + if (result_type != y_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand Y to be equal to Result Type"; + } + } + + const uint32_t p_type = _.GetOperandTypeId(inst, operand_index++); + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the last operand to be a pointer"; + } + + if (p_storage_class != SpvStorageClassGeneric && + p_storage_class != SpvStorageClassCrossWorkgroup && + p_storage_class != SpvStorageClassWorkgroup && + p_storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected storage class of the pointer to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (!_.IsIntScalarOrVectorType(p_data_type) || + _.GetBitWidth(p_data_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected data type of the pointer to be a 32-bit int " + "scalar or vector type"; + } + + if (_.GetDimension(p_data_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected data type of the pointer to have the same number " + "of components as Result Type"; + } + break; + } + + case OpenCLLIB::Ilogb: { + if (!_.IsIntScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit int scalar or vector " + "type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to be a float scalar or vector"; + } + + if (_.GetDimension(x_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to have the same number of components " + "as Result Type"; + } + break; + } + + case OpenCLLIB::Ldexp: + case OpenCLLIB::Pown: + case OpenCLLIB::Rootn: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + if (result_type != x_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected type of operand X to be equal to Result Type"; + } + + const uint32_t exp_type = _.GetOperandTypeId(inst, 5); + if (!_.IsIntScalarOrVectorType(exp_type) || + _.GetBitWidth(exp_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the exponent to be a 32-bit int scalar or vector"; + } + + if (_.GetDimension(exp_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected the exponent to have the same number of " + "components as Result Type"; + } + break; + } + + case OpenCLLIB::Nan: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t nancode_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarOrVectorType(nancode_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Nancode to be an int scalar or vector type"; + } + + if (_.GetDimension(nancode_type) != num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Nancode to have the same number of components as " + "Result Type"; + } + + if (_.GetBitWidth(result_type) != _.GetBitWidth(nancode_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Nancode to have the same bit width as Result " + "Type"; + } + break; + } + + case OpenCLLIB::SAbs: + case OpenCLLIB::SAbs_diff: + case OpenCLLIB::SAdd_sat: + case OpenCLLIB::UAdd_sat: + case OpenCLLIB::SHadd: + case OpenCLLIB::UHadd: + case OpenCLLIB::SRhadd: + case OpenCLLIB::URhadd: + case OpenCLLIB::SClamp: + case OpenCLLIB::UClamp: + case OpenCLLIB::Clz: + case OpenCLLIB::Ctz: + case OpenCLLIB::SMad_hi: + case OpenCLLIB::UMad_sat: + case OpenCLLIB::SMad_sat: + case OpenCLLIB::SMax: + case OpenCLLIB::UMax: + case OpenCLLIB::SMin: + case OpenCLLIB::UMin: + case OpenCLLIB::SMul_hi: + case OpenCLLIB::Rotate: + case OpenCLLIB::SSub_sat: + case OpenCLLIB::USub_sat: + case OpenCLLIB::Popcount: + case OpenCLLIB::UAbs: + case OpenCLLIB::UAbs_diff: + case OpenCLLIB::UMul_hi: + case OpenCLLIB::UMad_hi: { + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::U_Upsample: + case OpenCLLIB::S_Upsample: { + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int scalar or vector " + "type"; + } + + const uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components > 4 && result_num_components != 8 && + result_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t result_bit_width = _.GetBitWidth(result_type); + if (result_bit_width != 16 && result_bit_width != 32 && + result_bit_width != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected bit width of Result Type components to be 16, 32 " + "or 64"; + } + + const uint32_t hi_type = _.GetOperandTypeId(inst, 4); + const uint32_t lo_type = _.GetOperandTypeId(inst, 5); + + if (hi_type != lo_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Hi and Lo operands to have the same type"; + } + + if (result_num_components != _.GetDimension(hi_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Hi and Lo operands to have the same number of " + "components as Result Type"; + } + + if (result_bit_width != 2 * _.GetBitWidth(hi_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected bit width of components of Hi and Lo operands to " + "be half of the bit width of components of Result Type"; + } + break; + } + + case OpenCLLIB::SMad24: + case OpenCLLIB::UMad24: + case OpenCLLIB::SMul24: + case OpenCLLIB::UMul24: { + if (!_.IsIntScalarOrVectorType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit int scalar or vector " + "type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Cross: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components != 3 && num_components != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 3 or 4 components"; + } + + const uint32_t x_type = _.GetOperandTypeId(inst, 4); + const uint32_t y_type = _.GetOperandTypeId(inst, 5); + + if (x_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X type to be equal to Result Type"; + } + + if (y_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Y type to be equal to Result Type"; + } + break; + } + + case OpenCLLIB::Distance: + case OpenCLLIB::Fast_distance: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p0_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 to be of float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(p0_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 to have no more than 4 components"; + } + + if (result_type != _.GetComponentType(p0_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P0 component type to be equal to " + << "Result Type"; + } + + const uint32_t p1_type = _.GetOperandTypeId(inst, 5); + if (p0_type != p1_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operands P0 and P1 to be of the same type"; + } + break; + } + + case OpenCLLIB::Length: + case OpenCLLIB::Fast_length: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarOrVectorType(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a float scalar or vector"; + } + + const uint32_t num_components = _.GetDimension(p_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to have no more than 4 components"; + } + + if (result_type != _.GetComponentType(p_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P component type to be equal to Result " + "Type"; + } + break; + } + + case OpenCLLIB::Normalize: + case OpenCLLIB::Fast_normalize: { + if (!_.IsFloatScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar or vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have no more than 4 components"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + if (p_type != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P type to be equal to Result Type"; + } + break; + } + + case OpenCLLIB::Bitselect: { + if (!_.IsFloatScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int or float scalar or " + "vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + for (uint32_t operand_index = 4; operand_index < num_operands; + ++operand_index) { + const uint32_t operand_type = _.GetOperandTypeId(inst, operand_index); + if (result_type != operand_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected types of all operands to be equal to Result " + "Type"; + } + } + break; + } + + case OpenCLLIB::Select: { + if (!_.IsFloatScalarOrVectorType(result_type) && + !_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int or float scalar or " + "vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t a_type = _.GetOperandTypeId(inst, 4); + const uint32_t b_type = _.GetOperandTypeId(inst, 5); + const uint32_t c_type = _.GetOperandTypeId(inst, 6); + + if (result_type != a_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand A type to be equal to Result Type"; + } + + if (result_type != b_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand B type to be equal to Result Type"; + } + + if (!_.IsIntScalarOrVectorType(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand C to be an int scalar or vector"; + } + + if (num_components != _.GetDimension(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand C to have the same number of components " + "as Result Type"; + } + + if (_.GetBitWidth(result_type) != _.GetBitWidth(c_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand C to have the same bit width as Result " + "Type"; + } + break; + } + + case OpenCLLIB::Vloadn: { + if (!_.IsFloatVectorType(result_type) && + !_.IsIntVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int or float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != SpvStorageClassUniformConstant && + p_storage_class != SpvStorageClassGeneric && + p_storage_class != SpvStorageClassCrossWorkgroup && + p_storage_class != SpvStorageClassWorkgroup && + p_storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if (_.GetComponentType(result_type) != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be equal to component " + "type of Result Type"; + } + + const uint32_t n_value = inst->word(7); + if (num_components != n_value) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected literal N to be equal to the number of " + "components of Result Type"; + } + break; + } + + case OpenCLLIB::Vstoren: { + if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected Result Type to be void"; + } + + const uint32_t data_type = _.GetOperandTypeId(inst, 4); + const uint32_t offset_type = _.GetOperandTypeId(inst, 5); + const uint32_t p_type = _.GetOperandTypeId(inst, 6); + + if (!_.IsFloatVectorType(data_type) && !_.IsIntVectorType(data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to be an int or float vector"; + } + + const uint32_t num_components = _.GetDimension(data_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != SpvStorageClassGeneric && + p_storage_class != SpvStorageClassCrossWorkgroup && + p_storage_class != SpvStorageClassWorkgroup && + p_storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (_.GetComponentType(data_type) != p_data_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be equal to the type of " + "operand Data components"; + } + break; + } + + case OpenCLLIB::Vload_half: { + if (!_.IsFloatScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float scalar type"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != SpvStorageClassUniformConstant && + p_storage_class != SpvStorageClassGeneric && + p_storage_class != SpvStorageClassCrossWorkgroup && + p_storage_class != SpvStorageClassWorkgroup && + p_storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if (!_.IsFloatScalarType(p_data_type) || + _.GetBitWidth(p_data_type) != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be 16-bit float scalar"; + } + break; + } + + case OpenCLLIB::Vload_halfn: + case OpenCLLIB::Vloada_halfn: { + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a float vector type"; + } + + const uint32_t num_components = _.GetDimension(result_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 2, 3, 4, 8 or 16 components"; + } + + const uint32_t offset_type = _.GetOperandTypeId(inst, 4); + const uint32_t p_type = _.GetOperandTypeId(inst, 5); + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != SpvStorageClassUniformConstant && + p_storage_class != SpvStorageClassGeneric && + p_storage_class != SpvStorageClassCrossWorkgroup && + p_storage_class != SpvStorageClassWorkgroup && + p_storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be UniformConstant, " + "Generic, CrossWorkgroup, Workgroup or Function"; + } + + if (!_.IsFloatScalarType(p_data_type) || + _.GetBitWidth(p_data_type) != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be 16-bit float scalar"; + } + + const uint32_t n_value = inst->word(7); + if (num_components != n_value) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected literal N to be equal to the number of " + "components of Result Type"; + } + break; + } + + case OpenCLLIB::Vstore_half: + case OpenCLLIB::Vstore_half_r: + case OpenCLLIB::Vstore_halfn: + case OpenCLLIB::Vstore_halfn_r: + case OpenCLLIB::Vstorea_halfn: + case OpenCLLIB::Vstorea_halfn_r: { + if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected Result Type to be void"; + } + + const uint32_t data_type = _.GetOperandTypeId(inst, 4); + const uint32_t offset_type = _.GetOperandTypeId(inst, 5); + const uint32_t p_type = _.GetOperandTypeId(inst, 6); + const uint32_t data_type_bit_width = _.GetBitWidth(data_type); + + if (ext_inst_key == OpenCLLIB::Vstore_half || + ext_inst_key == OpenCLLIB::Vstore_half_r) { + if (!_.IsFloatScalarType(data_type) || + (data_type_bit_width != 32 && data_type_bit_width != 64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to be a 32 or 64-bit float scalar"; + } + } else { + if (!_.IsFloatVectorType(data_type) || + (data_type_bit_width != 32 && data_type_bit_width != 64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to be a 32 or 64-bit float vector"; + } + + const uint32_t num_components = _.GetDimension(data_type); + if (num_components > 4 && num_components != 8 && + num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Data to have 2, 3, 4, 8 or 16 components"; + } + } + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(offset_type) || + _.GetBitWidth(offset_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Offset to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P to be a pointer"; + } + + if (p_storage_class != SpvStorageClassGeneric && + p_storage_class != SpvStorageClassCrossWorkgroup && + p_storage_class != SpvStorageClassWorkgroup && + p_storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P storage class to be Generic, " + "CrossWorkgroup, Workgroup or Function"; + } + + if (!_.IsFloatScalarType(p_data_type) || + _.GetBitWidth(p_data_type) != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand P data type to be 16-bit float scalar"; + } + + // Rounding mode enum is checked by assembler. + break; + } + + case OpenCLLIB::Shuffle: + case OpenCLLIB::Shuffle2: { + if (!_.IsFloatVectorType(result_type) && + !_.IsIntVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be an int or float vector type"; + } + + const uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components != 2 && result_num_components != 4 && + result_num_components != 8 && result_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to have 2, 4, 8 or 16 components"; + } + + uint32_t operand_index = 4; + const uint32_t x_type = _.GetOperandTypeId(inst, operand_index++); + + if (ext_inst_key == OpenCLLIB::Shuffle2) { + const uint32_t y_type = _.GetOperandTypeId(inst, operand_index++); + if (x_type != y_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operands X and Y to be of the same type"; + } + } + + const uint32_t shuffle_mask_type = + _.GetOperandTypeId(inst, operand_index++); + + if (!_.IsFloatVectorType(x_type) && !_.IsIntVectorType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to be an int or float vector"; + } + + const uint32_t x_num_components = _.GetDimension(x_type); + if (x_num_components != 2 && x_num_components != 4 && + x_num_components != 8 && x_num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X to have 2, 4, 8 or 16 components"; + } + + const uint32_t result_component_type = _.GetComponentType(result_type); + + if (result_component_type != _.GetComponentType(x_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand X and Result Type to have equal " + "component types"; + } + + if (!_.IsIntVectorType(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Shuffle Mask to be an int vector"; + } + + if (result_num_components != _.GetDimension(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Shuffle Mask to have the same number of " + "components as Result Type"; + } + + if (_.GetBitWidth(result_component_type) != + _.GetBitWidth(shuffle_mask_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Shuffle Mask components to have the same " + "bit width as Result Type components"; + } + break; + } + + case OpenCLLIB::Printf: { + if (!_.IsIntScalarType(result_type) || + _.GetBitWidth(result_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a 32-bit int type"; + } + + const uint32_t format_type = _.GetOperandTypeId(inst, 4); + uint32_t format_storage_class = 0; + uint32_t format_data_type = 0; + if (!_.GetPointerTypeInfo(format_type, &format_data_type, + &format_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Format to be a pointer"; + } + + if (format_storage_class != SpvStorageClassUniformConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Format storage class to be UniformConstant"; + } + + if (!_.IsIntScalarType(format_data_type) || + _.GetBitWidth(format_data_type) != 8) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Format data type to be 8-bit int"; + } + break; + } + + case OpenCLLIB::Prefetch: { + if (_.GetIdOpcode(result_type) != SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected Result Type to be void"; + } + + const uint32_t p_type = _.GetOperandTypeId(inst, 4); + const uint32_t num_elements_type = _.GetOperandTypeId(inst, 5); + + uint32_t p_storage_class = 0; + uint32_t p_data_type = 0; + if (!_.GetPointerTypeInfo(p_type, &p_data_type, &p_storage_class)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Ptr to be a pointer"; + } + + if (p_storage_class != SpvStorageClassCrossWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Ptr storage class to be CrossWorkgroup"; + } + + if (!_.IsFloatScalarOrVectorType(p_data_type) && + !_.IsIntScalarOrVectorType(p_data_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Ptr data type to be int or float scalar or " + "vector"; + } + + const uint32_t num_components = _.GetDimension(p_data_type); + if (num_components > 4 && num_components != 8 && num_components != 16) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected Result Type to be a scalar or a vector with 2, " + "3, 4, 8 or 16 components"; + } + + const uint32_t size_t_bit_width = GetSizeTBitWidth(_); + if (!size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() + << " can only be used with physical addressing models"; + } + + if (!_.IsIntScalarType(num_elements_type) || + _.GetBitWidth(num_elements_type) != size_t_bit_width) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Num Elements to be of type size_t (" + << size_t_bit_width + << "-bit integer for the addressing model used in the module)"; + } + break; + } + } + } else if (ext_inst_type == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + if (!_.IsVoidType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected result type must be a result id of " + << "OpTypeVoid"; + } + + auto num_words = inst->words().size(); + + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + switch (ext_inst_key) { + case OpenCLDebugInfo100DebugInfoNone: + case OpenCLDebugInfo100DebugNoScope: + case OpenCLDebugInfo100DebugOperation: + // The binary parser validates the opcode for DebugInfoNone, + // DebugNoScope, DebugOperation, and the literal values don't need + // further checks. + break; + case OpenCLDebugInfo100DebugCompilationUnit: { + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + break; + } + case OpenCLDebugInfo100DebugSource: { + CHECK_OPERAND("File", SpvOpString, 5); + if (num_words == 7) CHECK_OPERAND("Text", SpvOpString, 6); + break; + } + case OpenCLDebugInfo100DebugTypeBasic: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_OPERAND("Size", SpvOpConstant, 6); + // "Encoding" param is already validated by the binary parsing stage. + break; + } + case OpenCLDebugInfo100DebugTypePointer: + case OpenCLDebugInfo100DebugTypeQualifier: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + break; + } + case OpenCLDebugInfo100DebugTypeVector: { + auto validate_base_type = + ValidateOperandBaseType(_, inst, 5, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + + uint32_t component_count = inst->word(6); + if (!component_count || component_count > 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be positive " + << "integer less than or equal to 4"; + } + break; + } + case OpenCLDebugInfo100DebugTypeArray: { + auto validate_base_type = ValidateOperandDebugType( + _, "Base Type", inst, 5, ext_inst_name, false); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + for (uint32_t i = 6; i < num_words; ++i) { + bool invalid = false; + auto* component_count = _.FindDef(inst->word(i)); + if (IsConstIntScalarTypeWith32Or64Bits(_, component_count)) { + // TODO: We need a spec discussion for the bindless array. + if (!component_count->word(3)) { + invalid = true; + } + } else if (component_count->words().size() > 6 && + (OpenCLDebugInfo100Instructions(component_count->word( + 4)) == OpenCLDebugInfo100DebugLocalVariable || + OpenCLDebugInfo100Instructions(component_count->word( + 4)) == OpenCLDebugInfo100DebugGlobalVariable)) { + auto* component_count_type = _.FindDef(component_count->word(6)); + if (component_count_type->words().size() > 7) { + if (OpenCLDebugInfo100Instructions(component_count_type->word( + 4)) != OpenCLDebugInfo100DebugTypeBasic || + OpenCLDebugInfo100DebugBaseTypeAttributeEncoding( + component_count_type->word(7)) != + OpenCLDebugInfo100Unsigned) { + invalid = true; + } else { + // DebugTypeBasic for DebugLocalVariable/DebugGlobalVariable + // must have Unsigned encoding and 32 or 64 as its size in bits. + Instruction* size_in_bits = + _.FindDef(component_count_type->word(6)); + if (!_.IsIntScalarType(size_in_bits->type_id()) || + (size_in_bits->word(3) != 32 && + size_in_bits->word(3) != 64)) { + invalid = true; + } + } + } else { + invalid = true; + } + } else { + invalid = true; + } + if (invalid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": Component Count must be " + << "OpConstant with a 32- or 64-bits integer scalar type or " + << "DebugGlobalVariable or DebugLocalVariable with a 32- or " + << "64-bits unsigned integer scalar type"; + } + } + break; + } + case OpenCLDebugInfo100DebugTypedef: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_base_type = + ValidateOperandBaseType(_, inst, 6, ext_inst_name); + if (validate_base_type != SPV_SUCCESS) return validate_base_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case OpenCLDebugInfo100DebugTypeFunction: { + auto* return_type = _.FindDef(inst->word(6)); + // TODO: We need a spec discussion that we have to allow return and + // parameter types of a DebugTypeFunction to have template parameter. + if (return_type->opcode() != SpvOpTypeVoid) { + auto validate_return = ValidateOperandDebugType( + _, "Return Type", inst, 6, ext_inst_name, true); + if (validate_return != SPV_SUCCESS) return validate_return; + } + for (uint32_t word_index = 7; word_index < num_words; ++word_index) { + auto validate_param = ValidateOperandDebugType( + _, "Parameter Types", inst, word_index, ext_inst_name, true); + if (validate_param != SPV_SUCCESS) return validate_param; + } + break; + } + case OpenCLDebugInfo100DebugTypeEnum: { + CHECK_OPERAND("Name", SpvOpString, 5); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugInfoNone; + }, + inst, 6)) { + auto validate_underlying_type = ValidateOperandDebugType( + _, "Underlying Types", inst, 6, ext_inst_name, false); + if (validate_underlying_type != SPV_SUCCESS) + return validate_underlying_type; + } + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Size", SpvOpConstant, 11); + auto* size = _.FindDef(inst->word(11)); + if (!_.IsIntScalarType(size->type_id()) || !size->word(3)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Size is a " + << "positive integer"; + } + for (uint32_t word_index = 13; word_index + 1 < num_words; + word_index += 2) { + CHECK_OPERAND("Value", SpvOpConstant, word_index); + CHECK_OPERAND("Name", SpvOpString, word_index + 1); + } + break; + } + case OpenCLDebugInfo100DebugTypeComposite: { + CHECK_OPERAND("Name", SpvOpString, 5); + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugInfoNone; + }, + inst, 12)) { + CHECK_OPERAND("Size", SpvOpConstant, 12); + } + for (uint32_t word_index = 14; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugTypeMember || + dbg_inst == OpenCLDebugInfo100DebugFunction || + dbg_inst == OpenCLDebugInfo100DebugTypeInheritance; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Members " + << "must be DebugTypeMember, DebugFunction, or " + "DebugTypeInheritance"; + } + } + break; + } + case OpenCLDebugInfo100DebugTypeMember: { + CHECK_OPERAND("Name", SpvOpString, 5); + // TODO: We need a spec discussion that we have to allow member types + // to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 10); + CHECK_OPERAND("Offset", SpvOpConstant, 11); + CHECK_OPERAND("Size", SpvOpConstant, 12); + if (num_words == 15) CHECK_OPERAND("Value", SpvOpConstant, 14); + break; + } + case OpenCLDebugInfo100DebugTypeInheritance: { + CHECK_DEBUG_OPERAND("Child", OpenCLDebugInfo100DebugTypeComposite, 5); + auto* debug_inst = _.FindDef(inst->word(5)); + auto composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Child must be class or struct debug type"; + } + CHECK_DEBUG_OPERAND("Parent", OpenCLDebugInfo100DebugTypeComposite, 6); + debug_inst = _.FindDef(inst->word(6)); + composite_type = + OpenCLDebugInfo100DebugCompositeType(debug_inst->word(6)); + if (composite_type != OpenCLDebugInfo100Class && + composite_type != OpenCLDebugInfo100Structure) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Parent must be class or struct debug " + "type"; + } + CHECK_OPERAND("Offset", SpvOpConstant, 7); + CHECK_OPERAND("Size", SpvOpConstant, 8); + break; + } + case OpenCLDebugInfo100DebugFunction: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugInfoNone; + }, + inst, 14)) { + CHECK_OPERAND("Function", SpvOpFunction, 14); + } + if (num_words == 16) { + CHECK_DEBUG_OPERAND("Declaration", + OpenCLDebugInfo100DebugFunctionDeclaration, 15); + } + break; + } + case OpenCLDebugInfo100DebugFunctionDeclaration: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + break; + } + case OpenCLDebugInfo100DebugLexicalBlock: { + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 5); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 8, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + if (num_words == 10) CHECK_OPERAND("Name", SpvOpString, 9); + break; + } + case OpenCLDebugInfo100DebugScope: { + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 5, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 7) { + CHECK_DEBUG_OPERAND("Inlined At", OpenCLDebugInfo100DebugInlinedAt, + 6); + } + break; + } + case OpenCLDebugInfo100DebugLocalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + // TODO: We need a spec discussion that we have to allow local variable + // types to have template parameter. + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, true); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_parent = + ValidateOperandLexicalScope(_, "Parent", inst, 10, ext_inst_name); + if (validate_parent != SPV_SUCCESS) return validate_parent; + break; + } + case OpenCLDebugInfo100DebugDeclare: { + CHECK_DEBUG_OPERAND("Local Variable", + OpenCLDebugInfo100DebugLocalVariable, 5); + auto* operand = _.FindDef(inst->word(6)); + if (operand->opcode() != SpvOpVariable && + operand->opcode() != SpvOpFunctionParameter) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpFunctionParameter"; + } + + CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7); + break; + } + case OpenCLDebugInfo100DebugExpression: { + for (uint32_t word_index = 5; word_index < num_words; ++word_index) { + CHECK_DEBUG_OPERAND("Operation", OpenCLDebugInfo100DebugOperation, + word_index); + } + break; + } + case OpenCLDebugInfo100DebugTypeTemplate: { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugTypeComposite || + dbg_inst == OpenCLDebugInfo100DebugFunction; + }, + inst, 5)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Target must be DebugTypeComposite " + << "or DebugFunction"; + } + for (uint32_t word_index = 6; word_index < num_words; ++word_index) { + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == + OpenCLDebugInfo100DebugTypeTemplateParameter || + dbg_inst == + OpenCLDebugInfo100DebugTypeTemplateTemplateParameter; + }, + inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Parameters must be " + << "DebugTypeTemplateParameter or " + << "DebugTypeTemplateTemplateParameter"; + } + } + break; + } + case OpenCLDebugInfo100DebugTypeTemplateParameter: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_actual_type = ValidateOperandDebugType( + _, "Actual Type", inst, 6, ext_inst_name, false); + if (validate_actual_type != SPV_SUCCESS) return validate_actual_type; + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugInfoNone; + }, + inst, 7)) { + CHECK_OPERAND("Value", SpvOpConstant, 7); + } + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 8); + break; + } + case OpenCLDebugInfo100DebugGlobalVariable: { + CHECK_OPERAND("Name", SpvOpString, 5); + auto validate_type = + ValidateOperandDebugType(_, "Type", inst, 6, ext_inst_name, false); + if (validate_type != SPV_SUCCESS) return validate_type; + CHECK_DEBUG_OPERAND("Source", OpenCLDebugInfo100DebugSource, 7); + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 10, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + CHECK_OPERAND("Linkage Name", SpvOpString, 11); + if (!DoesDebugInfoOperandMatchExpectation( + _, + [](OpenCLDebugInfo100Instructions dbg_inst) { + return dbg_inst == OpenCLDebugInfo100DebugInfoNone; + }, + inst, 12)) { + auto* operand = _.FindDef(inst->word(12)); + if (operand->opcode() != SpvOpVariable && + operand->opcode() != SpvOpConstant) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": " + << "expected operand Variable must be a result id of " + "OpVariable or OpConstant or DebugInfoNone"; + } + } + if (num_words == 15) { + CHECK_DEBUG_OPERAND("Static Member Declaration", + OpenCLDebugInfo100DebugTypeMember, 14); + } + break; + } + case OpenCLDebugInfo100DebugInlinedAt: { + auto validate_scope = + ValidateOperandLexicalScope(_, "Scope", inst, 6, ext_inst_name); + if (validate_scope != SPV_SUCCESS) return validate_scope; + if (num_words == 8) { + CHECK_DEBUG_OPERAND("Inlined", OpenCLDebugInfo100DebugInlinedAt, 7); + } + break; + } + case OpenCLDebugInfo100DebugValue: { + CHECK_DEBUG_OPERAND("Local Variable", + OpenCLDebugInfo100DebugLocalVariable, 5); + CHECK_DEBUG_OPERAND("Expression", OpenCLDebugInfo100DebugExpression, 7); + + for (uint32_t word_index = 8; word_index < num_words; ++word_index) { + // TODO: The following code simply checks if it is a const int scalar + // or a DebugLocalVariable or DebugGlobalVariable, but we have to + // check it using the same validation for Indexes of OpAccessChain. + if (!IsConstWithIntScalarType(_, inst, word_index) && + !IsDebugVariableWithIntScalarType(_, inst, word_index)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << ext_inst_name() << ": expected operand Indexes is " + << "OpConstant, DebugGlobalVariable, or " + << "type is OpConstant with an integer scalar type"; + } + } + break; + } + + // TODO: Add validation rules for remaining cases as well. + case OpenCLDebugInfo100DebugTypePtrToMember: + case OpenCLDebugInfo100DebugTypeTemplateTemplateParameter: + case OpenCLDebugInfo100DebugTypeTemplateParameterPack: + case OpenCLDebugInfo100DebugLexicalBlockDiscriminator: + case OpenCLDebugInfo100DebugInlinedVariable: + case OpenCLDebugInfo100DebugMacroDef: + case OpenCLDebugInfo100DebugMacroUndef: + case OpenCLDebugInfo100DebugImportedEntity: + break; + case OpenCLDebugInfo100InstructionsMax: + assert(0); + break; + } + } else if (ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { + auto import_inst = _.FindDef(inst->GetOperandAs(2)); + const std::string name(reinterpret_cast( + import_inst->words().data() + import_inst->operands()[1].offset)); + const std::string reflection = "NonSemantic.ClspvReflection."; + char* end_ptr; + auto version_string = name.substr(reflection.size()); + if (version_string.empty()) { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "Missing NonSemantic.ClspvReflection import version"; + } + uint32_t version = static_cast( + std::strtoul(version_string.c_str(), &end_ptr, 10)); + if (end_ptr && *end_ptr != '\0') { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "NonSemantic.ClspvReflection import does not encode the " + "version correctly"; + } + if (version == 0 || version > NonSemanticClspvReflectionRevision) { + return _.diag(SPV_ERROR_INVALID_DATA, import_inst) + << "Unknown NonSemantic.ClspvReflection import version"; + } + + return ValidateClspvReflectionInstruction(_, inst, version); + } + + return SPV_SUCCESS; +} + +spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + if (opcode == SpvOpExtension) return ValidateExtension(_, inst); + if (opcode == SpvOpExtInstImport) return ValidateExtInstImport(_, inst); + if (opcode == SpvOpExtInst) return ValidateExtInst(_, inst); + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_function.cpp b/third_party/spirv-tools/source/val/validate_function.cpp new file mode 100644 index 0000000..596186b --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_function.cpp @@ -0,0 +1,358 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns true if |a| and |b| are instructions defining pointers that point to +// types logically match and the decorations that apply to |b| are a subset +// of the decorations that apply to |a|. +bool DoPointeesLogicallyMatch(val::Instruction* a, val::Instruction* b, + ValidationState_t& _) { + if (a->opcode() != SpvOpTypePointer || b->opcode() != SpvOpTypePointer) { + return false; + } + + const auto& dec_a = _.id_decorations(a->id()); + const auto& dec_b = _.id_decorations(b->id()); + for (const auto& dec : dec_b) { + if (std::find(dec_a.begin(), dec_a.end(), dec) == dec_a.end()) { + return false; + } + } + + uint32_t a_type = a->GetOperandAs(2); + uint32_t b_type = b->GetOperandAs(2); + + if (a_type == b_type) { + return true; + } + + Instruction* a_type_inst = _.FindDef(a_type); + Instruction* b_type_inst = _.FindDef(b_type); + + return _.LogicallyMatch(a_type_inst, b_type_inst, true); +} + +spv_result_t ValidateFunction(ValidationState_t& _, const Instruction* inst) { + const auto function_type_id = inst->GetOperandAs(3); + const auto function_type = _.FindDef(function_type_id); + if (!function_type || SpvOpTypeFunction != function_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunction Function Type '" << _.getIdName(function_type_id) + << "' is not a function type."; + } + + const auto return_id = function_type->GetOperandAs(1); + if (return_id != inst->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunction Result Type '" << _.getIdName(inst->type_id()) + << "' does not match the Function Type's return type '" + << _.getIdName(return_id) << "'."; + } + + const std::vector acceptable = { + SpvOpGroupDecorate, + SpvOpDecorate, + SpvOpEnqueueKernel, + SpvOpEntryPoint, + SpvOpExecutionMode, + SpvOpExecutionModeId, + SpvOpFunctionCall, + SpvOpGetKernelNDrangeSubGroupCount, + SpvOpGetKernelNDrangeMaxSubGroupSize, + SpvOpGetKernelWorkGroupSize, + SpvOpGetKernelPreferredWorkGroupSizeMultiple, + SpvOpGetKernelLocalSizeForSubgroupCount, + SpvOpGetKernelMaxNumSubgroups, + SpvOpName}; + for (auto& pair : inst->uses()) { + const auto* use = pair.first; + if (std::find(acceptable.begin(), acceptable.end(), use->opcode()) == + acceptable.end() && + !use->IsNonSemantic() && !use->IsDebugInfo()) { + return _.diag(SPV_ERROR_INVALID_ID, use) + << "Invalid use of function result id " << _.getIdName(inst->id()) + << "."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateFunctionParameter(ValidationState_t& _, + const Instruction* inst) { + // NOTE: Find OpFunction & ensure OpFunctionParameter is not out of place. + size_t param_index = 0; + size_t inst_num = inst->LineNum() - 1; + if (inst_num == 0) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function parameter cannot be the first instruction."; + } + + auto func_inst = &_.ordered_instructions()[inst_num]; + while (--inst_num) { + func_inst = &_.ordered_instructions()[inst_num]; + if (func_inst->opcode() == SpvOpFunction) { + break; + } else if (func_inst->opcode() == SpvOpFunctionParameter) { + ++param_index; + } + } + + if (func_inst->opcode() != SpvOpFunction) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function parameter must be preceded by a function."; + } + + const auto function_type_id = func_inst->GetOperandAs(3); + const auto function_type = _.FindDef(function_type_id); + if (!function_type) { + return _.diag(SPV_ERROR_INVALID_ID, func_inst) + << "Missing function type definition."; + } + if (param_index >= function_type->words().size() - 3) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Too many OpFunctionParameters for " << func_inst->id() + << ": expected " << function_type->words().size() - 3 + << " based on the function's type"; + } + + const auto param_type = + _.FindDef(function_type->GetOperandAs(param_index + 2)); + if (!param_type || inst->type_id() != param_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionParameter Result Type '" + << _.getIdName(inst->type_id()) + << "' does not match the OpTypeFunction parameter " + "type of the same index."; + } + + // Validate that PhysicalStorageBufferEXT have one of Restrict, Aliased, + // RestrictPointerEXT, or AliasedPointerEXT. + auto param_nonarray_type_id = param_type->id(); + while (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypeArray) { + param_nonarray_type_id = + _.FindDef(param_nonarray_type_id)->GetOperandAs(1u); + } + if (_.GetIdOpcode(param_nonarray_type_id) == SpvOpTypePointer) { + auto param_nonarray_type = _.FindDef(param_nonarray_type_id); + if (param_nonarray_type->GetOperandAs(1u) == + SpvStorageClassPhysicalStorageBufferEXT) { + // check for Aliased or Restrict + const auto& decorations = _.id_decorations(inst->id()); + + bool foundAliased = std::any_of( + decorations.begin(), decorations.end(), [](const Decoration& d) { + return SpvDecorationAliased == d.dec_type(); + }); + + bool foundRestrict = std::any_of( + decorations.begin(), decorations.end(), [](const Decoration& d) { + return SpvDecorationRestrict == d.dec_type(); + }); + + if (!foundAliased && !foundRestrict) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionParameter " << inst->id() + << ": expected Aliased or Restrict for PhysicalStorageBufferEXT " + "pointer."; + } + if (foundAliased && foundRestrict) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionParameter " << inst->id() + << ": can't specify both Aliased and Restrict for " + "PhysicalStorageBufferEXT pointer."; + } + } else { + const auto pointee_type_id = + param_nonarray_type->GetOperandAs(2); + const auto pointee_type = _.FindDef(pointee_type_id); + if (SpvOpTypePointer == pointee_type->opcode() && + pointee_type->GetOperandAs(1u) == + SpvStorageClassPhysicalStorageBufferEXT) { + // check for AliasedPointerEXT/RestrictPointerEXT + const auto& decorations = _.id_decorations(inst->id()); + + bool foundAliased = std::any_of( + decorations.begin(), decorations.end(), [](const Decoration& d) { + return SpvDecorationAliasedPointerEXT == d.dec_type(); + }); + + bool foundRestrict = std::any_of( + decorations.begin(), decorations.end(), [](const Decoration& d) { + return SpvDecorationRestrictPointerEXT == d.dec_type(); + }); + + if (!foundAliased && !foundRestrict) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionParameter " << inst->id() + << ": expected AliasedPointerEXT or RestrictPointerEXT for " + "PhysicalStorageBufferEXT pointer."; + } + if (foundAliased && foundRestrict) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionParameter " << inst->id() + << ": can't specify both AliasedPointerEXT and " + "RestrictPointerEXT for PhysicalStorageBufferEXT pointer."; + } + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateFunctionCall(ValidationState_t& _, + const Instruction* inst) { + const auto function_id = inst->GetOperandAs(2); + const auto function = _.FindDef(function_id); + if (!function || SpvOpFunction != function->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionCall Function '" << _.getIdName(function_id) + << "' is not a function."; + } + + auto return_type = _.FindDef(function->type_id()); + if (!return_type || return_type->id() != inst->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionCall Result Type '" + << _.getIdName(inst->type_id()) + << "'s type does not match Function '" + << _.getIdName(return_type->id()) << "'s return type."; + } + + const auto function_type_id = function->GetOperandAs(3); + const auto function_type = _.FindDef(function_type_id); + if (!function_type || function_type->opcode() != SpvOpTypeFunction) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Missing function type definition."; + } + + const auto function_call_arg_count = inst->words().size() - 4; + const auto function_param_count = function_type->words().size() - 3; + if (function_param_count != function_call_arg_count) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionCall Function 's parameter count does not match " + "the argument count."; + } + + for (size_t argument_index = 3, param_index = 2; + argument_index < inst->operands().size(); + argument_index++, param_index++) { + const auto argument_id = inst->GetOperandAs(argument_index); + const auto argument = _.FindDef(argument_id); + if (!argument) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Missing argument " << argument_index - 3 << " definition."; + } + + const auto argument_type = _.FindDef(argument->type_id()); + if (!argument_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Missing argument " << argument_index - 3 + << " type definition."; + } + + const auto parameter_type_id = + function_type->GetOperandAs(param_index); + const auto parameter_type = _.FindDef(parameter_type_id); + if (!parameter_type || argument_type->id() != parameter_type->id()) { + if (!_.options()->before_hlsl_legalization || + !DoPointeesLogicallyMatch(argument_type, parameter_type, _)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpFunctionCall Argument '" << _.getIdName(argument_id) + << "'s type does not match Function '" + << _.getIdName(parameter_type_id) << "'s parameter type."; + } + } + + if (_.addressing_model() == SpvAddressingModelLogical) { + if (parameter_type->opcode() == SpvOpTypePointer && + !_.options()->relax_logical_pointer) { + SpvStorageClass sc = parameter_type->GetOperandAs(1u); + // Validate which storage classes can be pointer operands. + switch (sc) { + case SpvStorageClassUniformConstant: + case SpvStorageClassFunction: + case SpvStorageClassPrivate: + case SpvStorageClassWorkgroup: + case SpvStorageClassAtomicCounter: + // These are always allowed. + break; + case SpvStorageClassStorageBuffer: + if (!_.features().variable_pointers_storage_buffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "StorageBuffer pointer operand " + << _.getIdName(argument_id) + << " requires a variable pointers capability"; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Invalid storage class for pointer operand " + << _.getIdName(argument_id); + } + + // Validate memory object declaration requirements. + if (argument->opcode() != SpvOpVariable && + argument->opcode() != SpvOpFunctionParameter) { + const bool ssbo_vptr = + _.features().variable_pointers_storage_buffer && + sc == SpvStorageClassStorageBuffer; + const bool wg_vptr = + _.features().variable_pointers && sc == SpvStorageClassWorkgroup; + const bool uc_ptr = sc == SpvStorageClassUniformConstant; + if (!ssbo_vptr && !wg_vptr && !uc_ptr) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer operand " << _.getIdName(argument_id) + << " must be a memory object declaration"; + } + } + } + } + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t FunctionPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpFunction: + if (auto error = ValidateFunction(_, inst)) return error; + break; + case SpvOpFunctionParameter: + if (auto error = ValidateFunctionParameter(_, inst)) return error; + break; + case SpvOpFunctionCall: + if (auto error = ValidateFunctionCall(_, inst)) return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_id.cpp b/third_party/spirv-tools/source/val/validate_id.cpp new file mode 100644 index 0000000..e1a775a --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_id.cpp @@ -0,0 +1,245 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/diagnostic.h" +#include "source/instruction.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_validator_options.h" +#include "source/val/function.h" +#include "source/val/validation_state.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst) { + for (auto& operand : inst->operands()) { + const spv_operand_type_t& type = operand.type; + const uint32_t operand_id = inst->word(operand.offset); + if (spvIsIdType(type) && type != SPV_OPERAND_TYPE_RESULT_ID) { + if (auto def = _.FindDef(operand_id)) + def->RegisterUse(inst, operand.offset); + } + } + + return SPV_SUCCESS; +} + +/// This function checks all ID definitions dominate their use in the CFG. +/// +/// This function will iterate over all ID definitions that are defined in the +/// functions of a module and make sure that the definitions appear in a +/// block that dominates their use. +/// +/// NOTE: This function does NOT check module scoped functions which are +/// checked during the initial binary parse in the IdPass below +spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _) { + std::vector phi_instructions; + std::unordered_set phi_ids; + for (const auto& inst : _.ordered_instructions()) { + if (inst.id() == 0) continue; + if (const Function* func = inst.function()) { + if (const BasicBlock* block = inst.block()) { + // If the Id is defined within a block then make sure all references to + // that Id appear in a blocks that are dominated by the defining block + for (auto& use_index_pair : inst.uses()) { + const Instruction* use = use_index_pair.first; + if (const BasicBlock* use_block = use->block()) { + if (use_block->reachable() == false) continue; + if (use->opcode() == SpvOpPhi) { + if (phi_ids.insert(use->id()).second) { + phi_instructions.push_back(use); + } + } else if (!block->dominates(*use->block())) { + return _.diag(SPV_ERROR_INVALID_ID, use_block->label()) + << "ID " << _.getIdName(inst.id()) << " defined in block " + << _.getIdName(block->id()) + << " does not dominate its use in block " + << _.getIdName(use_block->id()); + } + } + } + } else { + // If the Ids defined within a function but not in a block(i.e. function + // parameters, block ids), then make sure all references to that Id + // appear within the same function + for (auto use : inst.uses()) { + const Instruction* user = use.first; + if (user->function() && user->function() != func) { + return _.diag(SPV_ERROR_INVALID_ID, _.FindDef(func->id())) + << "ID " << _.getIdName(inst.id()) << " used in function " + << _.getIdName(user->function()->id()) + << " is used outside of it's defining function " + << _.getIdName(func->id()); + } + } + } + } + // NOTE: Ids defined outside of functions must appear before they are used + // This check is being performed in the IdPass function + } + + // Check all OpPhi parent blocks are dominated by the variable's defining + // blocks + for (const Instruction* phi : phi_instructions) { + if (phi->block()->reachable() == false) continue; + for (size_t i = 3; i < phi->operands().size(); i += 2) { + const Instruction* variable = _.FindDef(phi->word(i)); + const BasicBlock* parent = + phi->function()->GetBlock(phi->word(i + 1)).first; + if (variable->block() && parent->reachable() && + !variable->block()->dominates(*parent)) { + return _.diag(SPV_ERROR_INVALID_ID, phi) + << "In OpPhi instruction " << _.getIdName(phi->id()) << ", ID " + << _.getIdName(variable->id()) + << " definition does not dominate its parent " + << _.getIdName(parent->id()); + } + } + } + + return SPV_SUCCESS; +} + +// Performs SSA validation on the IDs of an instruction. The +// can_have_forward_declared_ids functor should return true if the +// instruction operand's ID can be forward referenced. +spv_result_t IdPass(ValidationState_t& _, Instruction* inst) { + auto can_have_forward_declared_ids = + inst->opcode() == SpvOpExtInst && + spvExtInstIsDebugInfo(inst->ext_inst_type()) + ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction( + inst->ext_inst_type(), inst->word(4)) + : spvOperandCanBeForwardDeclaredFunction(inst->opcode()); + + // Keep track of a result id defined by this instruction. 0 means it + // does not define an id. + uint32_t result_id = 0; + + for (unsigned i = 0; i < inst->operands().size(); i++) { + const spv_parsed_operand_t& operand = inst->operand(i); + const spv_operand_type_t& type = operand.type; + // We only care about Id operands, which are a single word. + const uint32_t operand_word = inst->word(operand.offset); + + auto ret = SPV_ERROR_INTERNAL; + switch (type) { + case SPV_OPERAND_TYPE_RESULT_ID: + // NOTE: Multiple Id definitions are being checked by the binary parser. + // + // Defer undefined-forward-reference removal until after we've analyzed + // the remaining operands to this instruction. Deferral only matters + // for OpPhi since it's the only case where it defines its own forward + // reference. Other instructions that can have forward references + // either don't define a value or the forward reference is to a function + // Id (and hence defined outside of a function body). + result_id = operand_word; + // NOTE: The result Id is added (in RegisterInstruction) *after* all of + // the other Ids have been checked to avoid premature use in the same + // instruction. + ret = SPV_SUCCESS; + break; + case SPV_OPERAND_TYPE_ID: + case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID: + case SPV_OPERAND_TYPE_SCOPE_ID: + if (const auto def = _.FindDef(operand_word)) { + const auto opcode = inst->opcode(); + if (spvOpcodeGeneratesType(def->opcode()) && + !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) && + !inst->IsDebugInfo() && !inst->IsNonSemantic() && + !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction && + opcode != SpvOpCooperativeMatrixLengthNV && + !(opcode == SpvOpSpecConstantOp && + inst->word(3) == SpvOpCooperativeMatrixLengthNV)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " cannot be a type"; + } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) && + !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() && + !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) && + !spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi && + opcode != SpvOpExtInst && opcode != SpvOpExtInstImport && + opcode != SpvOpSelectionMerge && + opcode != SpvOpLoopMerge && opcode != SpvOpFunction && + opcode != SpvOpCooperativeMatrixLengthNV && + !(opcode == SpvOpSpecConstantOp && + inst->word(3) == SpvOpCooperativeMatrixLengthNV)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " requires a type"; + } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " in semantic instruction cannot be a non-semantic " + "instruction"; + } else { + ret = SPV_SUCCESS; + } + } else if (can_have_forward_declared_ids(i)) { + if (inst->opcode() == SpvOpTypeStruct && + !_.IsForwardPointer(operand_word)) { + ret = _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand " << _.getIdName(operand_word) + << " requires a previous definition"; + } else { + ret = _.ForwardDeclareId(operand_word); + } + } else { + ret = _.diag(SPV_ERROR_INVALID_ID, inst) + << "ID " << _.getIdName(operand_word) + << " has not been defined"; + } + break; + case SPV_OPERAND_TYPE_TYPE_ID: + if (_.IsDefinedId(operand_word)) { + auto* def = _.FindDef(operand_word); + if (!spvOpcodeGeneratesType(def->opcode())) { + ret = _.diag(SPV_ERROR_INVALID_ID, inst) + << "ID " << _.getIdName(operand_word) << " is not a type id"; + } else { + ret = SPV_SUCCESS; + } + } else { + ret = _.diag(SPV_ERROR_INVALID_ID, inst) + << "ID " << _.getIdName(operand_word) + << " has not been defined"; + } + break; + default: + ret = SPV_SUCCESS; + break; + } + if (SPV_SUCCESS != ret) return ret; + } + if (result_id) _.RemoveIfForwardDeclared(result_id); + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_image.cpp b/third_party/spirv-tools/source/val/validate_image.cpp new file mode 100644 index 0000000..299a3ef --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_image.cpp @@ -0,0 +1,2016 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of image instructions. + +#include "source/val/validate.h" + +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Performs compile time check that all SpvImageOperandsXXX cases are handled in +// this module. If SpvImageOperandsXXX list changes, this function will fail the +// build. +// For all other purposes this is a placeholder function. +bool CheckAllImageOperandsHandled() { + SpvImageOperandsMask enum_val = SpvImageOperandsBiasMask; + + // Some improvised code to prevent the compiler from considering enum_val + // constant and optimizing the switch away. + uint32_t stack_var = 0; + if (reinterpret_cast(&stack_var) % 256) + enum_val = SpvImageOperandsLodMask; + + switch (enum_val) { + // Please update the validation rules in this module if you are changing + // the list of image operands, and add new enum values to this switch. + case SpvImageOperandsMaskNone: + return false; + case SpvImageOperandsBiasMask: + case SpvImageOperandsLodMask: + case SpvImageOperandsGradMask: + case SpvImageOperandsConstOffsetMask: + case SpvImageOperandsOffsetMask: + case SpvImageOperandsConstOffsetsMask: + case SpvImageOperandsSampleMask: + case SpvImageOperandsMinLodMask: + + // TODO(dneto): Support image operands related to the Vulkan memory model. + // https://gitlab.khronos.org/spirv/spirv-tools/issues/32 + case SpvImageOperandsMakeTexelAvailableKHRMask: + case SpvImageOperandsMakeTexelVisibleKHRMask: + case SpvImageOperandsNonPrivateTexelKHRMask: + case SpvImageOperandsVolatileTexelKHRMask: + case SpvImageOperandsSignExtendMask: + case SpvImageOperandsZeroExtendMask: + return true; + } + return false; +} + +// Used by GetImageTypeInfo. See OpTypeImage spec for more information. +struct ImageTypeInfo { + uint32_t sampled_type = 0; + SpvDim dim = SpvDimMax; + uint32_t depth = 0; + uint32_t arrayed = 0; + uint32_t multisampled = 0; + uint32_t sampled = 0; + SpvImageFormat format = SpvImageFormatMax; + SpvAccessQualifier access_qualifier = SpvAccessQualifierMax; +}; + +// Provides information on image type. |id| should be object of either +// OpTypeImage or OpTypeSampledImage type. Returns false in case of failure +// (not a valid id, failed to parse the instruction, etc). +bool GetImageTypeInfo(const ValidationState_t& _, uint32_t id, + ImageTypeInfo* info) { + if (!id || !info) return false; + + const Instruction* inst = _.FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeSampledImage) { + inst = _.FindDef(inst->word(2)); + assert(inst); + } + + if (inst->opcode() != SpvOpTypeImage) return false; + + const size_t num_words = inst->words().size(); + if (num_words != 9 && num_words != 10) return false; + + info->sampled_type = inst->word(2); + info->dim = static_cast(inst->word(3)); + info->depth = inst->word(4); + info->arrayed = inst->word(5); + info->multisampled = inst->word(6); + info->sampled = inst->word(7); + info->format = static_cast(inst->word(8)); + info->access_qualifier = num_words < 10 + ? SpvAccessQualifierMax + : static_cast(inst->word(9)); + return true; +} + +bool IsImplicitLod(SpvOp opcode) { + switch (opcode) { + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + return true; + default: + break; + } + return false; +} + +bool IsExplicitLod(SpvOp opcode) { + switch (opcode) { + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + return true; + default: + break; + } + return false; +} + +bool IsValidLodOperand(const ValidationState_t& _, SpvOp opcode) { + switch (opcode) { + case SpvOpImageRead: + case SpvOpImageWrite: + case SpvOpImageSparseRead: + return _.HasCapability(SpvCapabilityImageReadWriteLodAMD); + default: + return IsExplicitLod(opcode); + } +} + +bool IsValidGatherLodBiasAMD(const ValidationState_t& _, SpvOp opcode) { + switch (opcode) { + case SpvOpImageGather: + case SpvOpImageSparseGather: + return _.HasCapability(SpvCapabilityImageGatherBiasLodAMD); + default: + break; + } + return false; +} + +// Returns true if the opcode is a Image instruction which applies +// homogenous projection to the coordinates. +bool IsProj(SpvOp opcode) { + switch (opcode) { + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + return true; + default: + break; + } + return false; +} + +// Returns the number of components in a coordinate used to access a texel in +// a single plane of an image with the given parameters. +uint32_t GetPlaneCoordSize(const ImageTypeInfo& info) { + uint32_t plane_size = 0; + // If this switch breaks your build, please add new values below. + switch (info.dim) { + case SpvDim1D: + case SpvDimBuffer: + plane_size = 1; + break; + case SpvDim2D: + case SpvDimRect: + case SpvDimSubpassData: + plane_size = 2; + break; + case SpvDim3D: + case SpvDimCube: + // For Cube direction vector is used instead of UV. + plane_size = 3; + break; + case SpvDimMax: + assert(0); + break; + } + + return plane_size; +} + +// Returns minimal number of coordinates based on image dim, arrayed and whether +// the instruction uses projection coordinates. +uint32_t GetMinCoordSize(SpvOp opcode, const ImageTypeInfo& info) { + if (info.dim == SpvDimCube && + (opcode == SpvOpImageRead || opcode == SpvOpImageWrite || + opcode == SpvOpImageSparseRead)) { + // These opcodes use UV for Cube, not direction vector. + return 3; + } + + return GetPlaneCoordSize(info) + info.arrayed + (IsProj(opcode) ? 1 : 0); +} + +// Checks ImageOperand bitfield and respective operands. +spv_result_t ValidateImageOperands(ValidationState_t& _, + const Instruction* inst, + const ImageTypeInfo& info, uint32_t mask, + uint32_t word_index) { + static const bool kAllImageOperandsHandled = CheckAllImageOperandsHandled(); + (void)kAllImageOperandsHandled; + + const SpvOp opcode = inst->opcode(); + const size_t num_words = inst->words().size(); + + // NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words. + const uint32_t mask_bits_having_operands = + mask & ~uint32_t(SpvImageOperandsNonPrivateTexelKHRMask | + SpvImageOperandsVolatileTexelKHRMask | + SpvImageOperandsSignExtendMask | + SpvImageOperandsZeroExtendMask); + size_t expected_num_image_operand_words = + spvtools::utils::CountSetBits(mask_bits_having_operands); + if (mask & SpvImageOperandsGradMask) { + // Grad uses two words. + ++expected_num_image_operand_words; + } + + if (expected_num_image_operand_words != num_words - word_index) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Number of image operand ids doesn't correspond to the bit mask"; + } + + if (spvtools::utils::CountSetBits( + mask & (SpvImageOperandsOffsetMask | SpvImageOperandsConstOffsetMask | + SpvImageOperandsConstOffsetsMask)) > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operands Offset, ConstOffset, ConstOffsets cannot be used " + << "together"; + } + + const bool is_implicit_lod = IsImplicitLod(opcode); + const bool is_explicit_lod = IsExplicitLod(opcode); + const bool is_valid_lod_operand = IsValidLodOperand(_, opcode); + const bool is_valid_gather_lod_bias_amd = IsValidGatherLodBiasAMD(_, opcode); + + // The checks should be done in the order of definition of OperandImage. + + if (mask & SpvImageOperandsBiasMask) { + if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Bias can only be used with ImplicitLod opcodes"; + } + + const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsFloatScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Bias to be float scalar"; + } + + if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && + info.dim != SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Bias requires 'Dim' parameter to be 1D, 2D, 3D " + "or Cube"; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Bias requires 'MS' parameter to be 0"; + } + } + + if (mask & SpvImageOperandsLodMask) { + if (!is_valid_lod_operand && opcode != SpvOpImageFetch && + opcode != SpvOpImageSparseFetch && !is_valid_gather_lod_bias_amd) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Lod can only be used with ExplicitLod opcodes " + << "and OpImageFetch"; + } + + if (mask & SpvImageOperandsGradMask) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand bits Lod and Grad cannot be set at the same " + "time"; + } + + const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + if (is_explicit_lod || is_valid_gather_lod_bias_amd) { + if (!_.IsFloatScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Lod to be float scalar when used " + << "with ExplicitLod"; + } + } else { + if (!_.IsIntScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Lod to be int scalar when used with " + << "OpImageFetch"; + } + } + + if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && + info.dim != SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Lod requires 'Dim' parameter to be 1D, 2D, 3D " + "or Cube"; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Lod requires 'MS' parameter to be 0"; + } + } + + if (mask & SpvImageOperandsGradMask) { + if (!is_explicit_lod) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Grad can only be used with ExplicitLod opcodes"; + } + + const uint32_t dx_type_id = _.GetTypeId(inst->word(word_index++)); + const uint32_t dy_type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsFloatScalarOrVectorType(dx_type_id) || + !_.IsFloatScalarOrVectorType(dy_type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both Image Operand Grad ids to be float scalars or " + << "vectors"; + } + + const uint32_t plane_size = GetPlaneCoordSize(info); + const uint32_t dx_size = _.GetDimension(dx_type_id); + const uint32_t dy_size = _.GetDimension(dy_type_id); + if (plane_size != dx_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Grad dx to have " << plane_size + << " components, but given " << dx_size; + } + + if (plane_size != dy_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Grad dy to have " << plane_size + << " components, but given " << dy_size; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Grad requires 'MS' parameter to be 0"; + } + } + + if (mask & SpvImageOperandsConstOffsetMask) { + if (info.dim == SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand ConstOffset cannot be used with Cube Image " + "'Dim'"; + } + + const uint32_t id = inst->word(word_index++); + const uint32_t type_id = _.GetTypeId(id); + if (!_.IsIntScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffset to be int scalar or " + << "vector"; + } + + if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffset to be a const object"; + } + + const uint32_t plane_size = GetPlaneCoordSize(info); + const uint32_t offset_size = _.GetDimension(type_id); + if (plane_size != offset_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffset to have " << plane_size + << " components, but given " << offset_size; + } + } + + if (mask & SpvImageOperandsOffsetMask) { + if (info.dim == SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Offset cannot be used with Cube Image 'Dim'"; + } + + const uint32_t id = inst->word(word_index++); + const uint32_t type_id = _.GetTypeId(id); + if (!_.IsIntScalarOrVectorType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Offset to be int scalar or " + << "vector"; + } + + const uint32_t plane_size = GetPlaneCoordSize(info); + const uint32_t offset_size = _.GetDimension(type_id); + if (plane_size != offset_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Offset to have " << plane_size + << " components, but given " << offset_size; + } + } + + if (mask & SpvImageOperandsConstOffsetsMask) { + if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather && + opcode != SpvOpImageSparseGather && + opcode != SpvOpImageSparseDrefGather) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand ConstOffsets can only be used with " + "OpImageGather and OpImageDrefGather"; + } + + if (info.dim == SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand ConstOffsets cannot be used with Cube Image " + "'Dim'"; + } + + const uint32_t id = inst->word(word_index++); + const uint32_t type_id = _.GetTypeId(id); + const Instruction* type_inst = _.FindDef(type_id); + assert(type_inst); + + if (type_inst->opcode() != SpvOpTypeArray) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffsets to be an array of size 4"; + } + + uint64_t array_size = 0; + if (!_.GetConstantValUint64(type_inst->word(3), &array_size)) { + assert(0 && "Array type definition is corrupt"); + } + + if (array_size != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffsets to be an array of size 4"; + } + + const uint32_t component_type = type_inst->word(2); + if (!_.IsIntVectorType(component_type) || + _.GetDimension(component_type) != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffsets array componenets to be " + "int vectors of size 2"; + } + + if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand ConstOffsets to be a const object"; + } + } + + if (mask & SpvImageOperandsSampleMask) { + if (opcode != SpvOpImageFetch && opcode != SpvOpImageRead && + opcode != SpvOpImageWrite && opcode != SpvOpImageSparseFetch && + opcode != SpvOpImageSparseRead) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Sample can only be used with OpImageFetch, " + << "OpImageRead, OpImageWrite, OpImageSparseFetch and " + << "OpImageSparseRead"; + } + + if (info.multisampled == 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand Sample requires non-zero 'MS' parameter"; + } + + const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsIntScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand Sample to be int scalar"; + } + } + + if (mask & SpvImageOperandsMinLodMask) { + if (!is_implicit_lod && !(mask & SpvImageOperandsGradMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MinLod can only be used with ImplicitLod " + << "opcodes or together with Image Operand Grad"; + } + + const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); + if (!_.IsFloatScalarType(type_id)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image Operand MinLod to be float scalar"; + } + + if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && + info.dim != SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MinLod requires 'Dim' parameter to be 1D, 2D, " + "3D or Cube"; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MinLod requires 'MS' parameter to be 0"; + } + } + + if (mask & SpvImageOperandsMakeTexelAvailableKHRMask) { + // Checked elsewhere: capability and memory model are correct. + if (opcode != SpvOpImageWrite) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MakeTexelAvailableKHR can only be used with Op" + << spvOpcodeString(SpvOpImageWrite) << ": Op" + << spvOpcodeString(opcode); + } + + if (!(mask & SpvImageOperandsNonPrivateTexelKHRMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MakeTexelAvailableKHR requires " + "NonPrivateTexelKHR is also specified: Op" + << spvOpcodeString(opcode); + } + + const auto available_scope = inst->word(word_index++); + if (auto error = ValidateMemoryScope(_, inst, available_scope)) + return error; + } + + if (mask & SpvImageOperandsMakeTexelVisibleKHRMask) { + // Checked elsewhere: capability and memory model are correct. + if (opcode != SpvOpImageRead && opcode != SpvOpImageSparseRead) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MakeTexelVisibleKHR can only be used with Op" + << spvOpcodeString(SpvOpImageRead) << " or Op" + << spvOpcodeString(SpvOpImageSparseRead) << ": Op" + << spvOpcodeString(opcode); + } + + if (!(mask & SpvImageOperandsNonPrivateTexelKHRMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Operand MakeTexelVisibleKHR requires NonPrivateTexelKHR " + "is also specified: Op" + << spvOpcodeString(opcode); + } + + const auto visible_scope = inst->word(word_index++); + if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error; + } + + if (mask & SpvImageOperandsSignExtendMask) { + // Checked elsewhere: SPIR-V 1.4 version or later. + + // "The texel value is converted to the target value via sign extension. + // Only valid when the texel type is a scalar or vector of integer type." + // + // We don't have enough information to know what the texel type is. + // In OpenCL, knowledge is deferred until runtime: the image SampledType is + // void, and the Format is Unknown. + // In Vulkan, the texel type is only known in all cases by the pipeline + // setup. + } + + if (mask & SpvImageOperandsZeroExtendMask) { + // Checked elsewhere: SPIR-V 1.4 version or later. + + // "The texel value is converted to the target value via zero extension. + // Only valid when the texel type is a scalar or vector of integer type." + // + // We don't have enough information to know what the texel type is. + // In OpenCL, knowledge is deferred until runtime: the image SampledType is + // void, and the Format is Unknown. + // In Vulkan, the texel type is only known in all cases by the pipeline + // setup. + } + + return SPV_SUCCESS; +} + +// Checks some of the validation rules which are common to multiple opcodes. +spv_result_t ValidateImageCommon(ValidationState_t& _, const Instruction* inst, + const ImageTypeInfo& info) { + const SpvOp opcode = inst->opcode(); + if (IsProj(opcode)) { + if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && + info.dim != SpvDimRect) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect"; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Image 'MS' parameter to be 0"; + } + + if (info.arrayed != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Image 'arrayed' parameter to be 0"; + } + } + + if (opcode == SpvOpImageRead || opcode == SpvOpImageSparseRead || + opcode == SpvOpImageWrite) { + if (info.sampled == 0) { + } else if (info.sampled == 2) { + if (info.dim == SpvDim1D && !_.HasCapability(SpvCapabilityImage1D)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability Image1D is required to access storage image"; + } else if (info.dim == SpvDimRect && + !_.HasCapability(SpvCapabilityImageRect)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageRect is required to access storage image"; + } else if (info.dim == SpvDimBuffer && + !_.HasCapability(SpvCapabilityImageBuffer)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageBuffer is required to access storage image"; + } else if (info.dim == SpvDimCube && info.arrayed == 1 && + !_.HasCapability(SpvCapabilityImageCubeArray)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageCubeArray is required to access " + << "storage image"; + } + + if (info.multisampled == 1 && + !_.HasCapability(SpvCapabilityImageMSArray)) { +#if 0 + // TODO(atgoo@github.com) The description of this rule in the spec + // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify + // and reenable. + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability ImageMSArray is required to access storage " + << "image"; +#endif + } + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 0 or 2"; + } + } + + return SPV_SUCCESS; +} + +// Returns true if opcode is *ImageSparse*, false otherwise. +bool IsSparse(SpvOp opcode) { + switch (opcode) { + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + case SpvOpImageSparseFetch: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + case SpvOpImageSparseTexelsResident: + case SpvOpImageSparseRead: { + return true; + } + + default: { return false; } + } + + return false; +} + +// Checks sparse image opcode result type and returns the second struct member. +// Returns inst.type_id for non-sparse image opcodes. +// Not valid for sparse image opcodes which do not return a struct. +spv_result_t GetActualResultType(ValidationState_t& _, const Instruction* inst, + uint32_t* actual_result_type) { + const SpvOp opcode = inst->opcode(); + + if (IsSparse(opcode)) { + const Instruction* const type_inst = _.FindDef(inst->type_id()); + assert(type_inst); + + if (!type_inst || type_inst->opcode() != SpvOpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypeStruct"; + } + + if (type_inst->words().size() != 4 || + !_.IsIntScalarType(type_inst->word(2))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be a struct containing an int " + "scalar and a texel"; + } + + *actual_result_type = type_inst->word(3); + } else { + *actual_result_type = inst->type_id(); + } + + return SPV_SUCCESS; +} + +// Returns a string describing actual result type of an opcode. +// Not valid for sparse image opcodes which do not return a struct. +const char* GetActualResultTypeStr(SpvOp opcode) { + if (IsSparse(opcode)) return "Result Type's second member"; + return "Result Type"; +} + +spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { + assert(inst->type_id() == 0); + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, inst->word(1), &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if ((!_.IsFloatScalarType(info.sampled_type) && + !_.IsIntScalarType(info.sampled_type)) || + (32 != _.GetBitWidth(info.sampled_type) && + (64 != _.GetBitWidth(info.sampled_type) || + !_.HasCapability(SpvCapabilityInt64ImageEXT)))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Type to be a 32-bit int or float " + "scalar type for Vulkan environment"; + } + } else if (spvIsOpenCLEnv(_.context()->target_env)) { + if (!_.IsVoidType(info.sampled_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Sampled Type must be OpTypeVoid in the OpenCL environment."; + } + } else { + const SpvOp sampled_type_opcode = _.GetIdOpcode(info.sampled_type); + if (sampled_type_opcode != SpvOpTypeVoid && + sampled_type_opcode != SpvOpTypeInt && + sampled_type_opcode != SpvOpTypeFloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Type to be either void or" + << " numerical scalar type"; + } + } + + // Dim is checked elsewhere. + + if (info.depth > 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid Depth " << info.depth << " (must be 0, 1 or 2)"; + } + + if (info.arrayed > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)"; + } + + if (spvIsOpenCLEnv(_.context()->target_env)) { + if ((info.arrayed == 1) && (info.dim != SpvDim1D) && + (info.dim != SpvDim2D)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the OpenCL environment, Arrayed may only be set to 1 " + << "when Dim is either 1D or 2D."; + } + } + + if (info.multisampled > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid MS " << info.multisampled << " (must be 0 or 1)"; + } + + if (spvIsOpenCLEnv(_.context()->target_env)) { + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "MS must be 0 in the OpenCL environement."; + } + } + + if (info.sampled > 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)"; + } + + if (spvIsOpenCLEnv(_.context()->target_env)) { + if (info.sampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Sampled must be 0 in the OpenCL environment."; + } + } + + if (info.dim == SpvDimSubpassData) { + if (info.sampled != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim SubpassData requires Sampled to be 2"; + } + + if (info.format != SpvImageFormatUnknown) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Dim SubpassData requires format Unknown"; + } + } + + // Format and Access Qualifier are also checked elsewhere. + + if (spvIsOpenCLEnv(_.context()->target_env)) { + if (info.access_qualifier == SpvAccessQualifierMax) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the OpenCL environment, the optional Access Qualifier" + << " must be present."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeSampledImage(ValidationState_t& _, + const Instruction* inst) { + const uint32_t image_type = inst->word(2); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + return SPV_SUCCESS; +} + +bool IsAllowedSampledImageOperand(SpvOp opcode) { + switch (opcode) { + case SpvOpSampledImage: + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImage: + case SpvOpImageQueryLod: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + case SpvOpCopyObject: + return true; + default: + return false; + } +} + +spv_result_t ValidateSampledImage(ValidationState_t& _, + const Instruction* inst) { + if (_.GetIdOpcode(inst->type_id()) != SpvOpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypeSampledImage."; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage."; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + // TODO(atgoo@github.com) Check compatibility of result type and received + // image. + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.sampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 1 " + << "for Vulkan environment."; + } + } else { + if (info.sampled != 0 && info.sampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 0 or 1"; + } + } + + if (info.dim == SpvDimSubpassData) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' parameter to be not SubpassData."; + } + + if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != SpvOpTypeSampler) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampler to be of type OpTypeSampler"; + } + + // We need to validate 2 things: + // * All OpSampledImage instructions must be in the same block in which their + // Result are consumed. + // * Result from OpSampledImage instructions must not appear as operands + // to OpPhi instructions or OpSelect instructions, or any instructions other + // than the image lookup and query instructions specified to take an operand + // whose type is OpTypeSampledImage. + std::vector consumers = _.getSampledImageConsumers(inst->id()); + if (!consumers.empty()) { + for (auto consumer_instr : consumers) { + const auto consumer_opcode = consumer_instr->opcode(); + if (consumer_instr->block() != inst->block()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "All OpSampledImage instructions must be in the same block " + "in " + "which their Result are consumed. OpSampledImage Result " + "Type '" + << _.getIdName(inst->id()) + << "' has a consumer in a different basic " + "block. The consumer instruction is '" + << _.getIdName(consumer_instr->id()) << "'."; + } + + if (consumer_opcode == SpvOpPhi || consumer_opcode == SpvOpSelect) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result from OpSampledImage instruction must not appear " + "as " + "operands of Op" + << spvOpcodeString(static_cast(consumer_opcode)) << "." + << " Found result '" << _.getIdName(inst->id()) + << "' as an operand of '" + << _.getIdName(consumer_instr->id()) << "'."; + } + + if (!IsAllowedSampledImageOperand(consumer_opcode)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result from OpSampledImage instruction must not appear " + "as operand for Op" + << spvOpcodeString(static_cast(consumer_opcode)) + << ", since it is not specificed as taking an " + << "OpTypeSampledImage." + << " Found result '" << _.getIdName(inst->id()) + << "' as an operand of '" + << _.getIdName(consumer_instr->id()) << "'."; + } + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageTexelPointer(ValidationState_t& _, + const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + if (result_type->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypePointer"; + } + + const auto storage_class = result_type->GetOperandAs(1); + if (storage_class != SpvStorageClassImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypePointer whose Storage Class " + "operand is Image"; + } + + const auto ptr_type = result_type->GetOperandAs(2); + const auto ptr_opcode = _.GetIdOpcode(ptr_type); + if (ptr_opcode != SpvOpTypeInt && ptr_opcode != SpvOpTypeFloat && + ptr_opcode != SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypePointer whose Type operand " + "must be a scalar numerical type or OpTypeVoid"; + } + + const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2)); + if (!image_ptr || image_ptr->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be OpTypePointer"; + } + + const auto image_type = image_ptr->GetOperandAs(2); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be OpTypePointer with Type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (info.sampled_type != ptr_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as the Type " + "pointed to by Result Type"; + } + + if (info.dim == SpvDimSubpassData) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Dim SubpassData cannot be used with OpImageTexelPointer"; + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be integer scalar or vector"; + } + + uint32_t expected_coord_size = 0; + if (info.arrayed == 0) { + expected_coord_size = GetPlaneCoordSize(info); + } else if (info.arrayed == 1) { + switch (info.dim) { + case SpvDim1D: + expected_coord_size = 2; + break; + case SpvDimCube: + case SpvDim2D: + expected_coord_size = 3; + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when " + "Arrayed is 1"; + break; + } + } + + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (expected_coord_size != actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have " << expected_coord_size + << " components, but given " << actual_coord_size; + } + + const uint32_t sample_type = _.GetOperandTypeId(inst, 4); + if (!sample_type || !_.IsIntScalarType(sample_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample to be integer scalar"; + } + + if (info.multisampled == 0) { + uint64_t ms = 0; + if (!_.GetConstantValUint64(inst->GetOperandAs(4), &ms) || + ms != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample for Image with MS 0 to be a valid for " + "the value 0"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { + return error; + } + + if (!_.IsIntVectorType(actual_result_type) && + !_.IsFloatVectorType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float vector type"; + } + + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to have 4 components"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Image to be of type OpTypeSampledImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + + if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) { + const uint32_t texel_component_type = + _.GetComponentType(actual_result_type); + if (texel_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if ((opcode == SpvOpImageSampleExplicitLod || + opcode == SpvOpImageSparseSampleExplicitLod) && + _.HasCapability(SpvCapabilityKernel)) { + if (!_.IsFloatScalarOrVectorType(coord_type) && + !_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int or float scalar or vector"; + } + } else { + if (!_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be float scalar or vector"; + } + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (inst->words().size() <= 5) { + assert(IsImplicitLod(opcode)); + return SPV_SUCCESS; + } + + const uint32_t mask = inst->word(5); + + if (spvIsOpenCLEnv(_.context()->target_env)) { + if (opcode == SpvOpImageSampleExplicitLod) { + if (mask & SpvImageOperandsConstOffsetMask) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ConstOffset image operand not allowed " + << "in the OpenCL environment."; + } + } + } + + if (spv_result_t result = + ValidateImageOperands(_, inst, info, mask, /* word_index = */ 6)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageDrefLod(ValidationState_t& _, + const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { + return error; + } + + if (!_.IsIntScalarType(actual_result_type) && + !_.IsFloatScalarType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float scalar type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Image to be of type OpTypeSampledImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + + if (actual_result_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode); + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be float scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + const uint32_t dref_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Dref to be of 32-bit float type"; + } + + if (inst->words().size() <= 6) { + assert(IsImplicitLod(opcode)); + return SPV_SUCCESS; + } + + const uint32_t mask = inst->word(6); + if (spv_result_t result = + ValidateImageOperands(_, inst, info, mask, /* word_index = */ 7)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) { + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { + return error; + } + + const SpvOp opcode = inst->opcode(); + if (!_.IsIntVectorType(actual_result_type) && + !_.IsFloatVectorType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float vector type"; + } + + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to have 4 components"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) { + const uint32_t result_component_type = + _.GetComponentType(actual_result_type); + if (result_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + if (info.dim == SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube"; + } + + if (info.sampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled' parameter to be 1"; + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (inst->words().size() <= 5) return SPV_SUCCESS; + + const uint32_t mask = inst->word(5); + if (spv_result_t result = + ValidateImageOperands(_, inst, info, mask, /* word_index = */ 6)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageGather(ValidationState_t& _, + const Instruction* inst) { + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) + return error; + + const SpvOp opcode = inst->opcode(); + if (!_.IsIntVectorType(actual_result_type) && + !_.IsFloatVectorType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float vector type"; + } + + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to have 4 components"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sampled Image to be of type OpTypeSampledImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (opcode == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather || + _.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) { + const uint32_t result_component_type = + _.GetComponentType(actual_result_type); + if (result_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + if (info.dim != SpvDim2D && info.dim != SpvDimCube && + info.dim != SpvDimRect) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Dim' cannot be Cube"; + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be float scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (opcode == SpvOpImageGather || opcode == SpvOpImageSparseGather) { + const uint32_t component_index_type = _.GetOperandTypeId(inst, 4); + if (!_.IsIntScalarType(component_index_type) || + _.GetBitWidth(component_index_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Component to be 32-bit int scalar"; + } + } else { + assert(opcode == SpvOpImageDrefGather || + opcode == SpvOpImageSparseDrefGather); + const uint32_t dref_type = _.GetOperandTypeId(inst, 4); + if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Dref to be of 32-bit float type"; + } + } + + if (inst->words().size() <= 6) return SPV_SUCCESS; + + const uint32_t mask = inst->word(6); + if (spv_result_t result = + ValidateImageOperands(_, inst, info, mask, /* word_index = */ 7)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + uint32_t actual_result_type = 0; + if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { + return error; + } + + if (!_.IsIntScalarOrVectorType(actual_result_type) && + !_.IsFloatScalarOrVectorType(actual_result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to be int or float scalar or vector type"; + } + +#if 0 + // TODO(atgoo@github.com) Disabled until the spec is clarified. + if (_.GetDimension(actual_result_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected " << GetActualResultTypeStr(opcode) + << " to have 4 components"; + } +#endif + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (info.dim == SpvDimSubpassData) { + if (opcode == SpvOpImageSparseRead) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image Dim SubpassData cannot be used with ImageSparseRead"; + } + + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + SpvExecutionModelFragment, + std::string("Dim SubpassData requires Fragment execution model: ") + + spvOpcodeString(opcode)); + } + + if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) { + const uint32_t result_component_type = + _.GetComponentType(actual_result_type); + if (result_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as " + << GetActualResultTypeStr(opcode) << " components"; + } + } + + if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(opcode, info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.format == SpvImageFormatUnknown && info.dim != SpvDimSubpassData && + !_.HasCapability(SpvCapabilityStorageImageReadWithoutFormat)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability StorageImageReadWithoutFormat is required to " + << "read storage image"; + } + } + + if (inst->words().size() <= 5) return SPV_SUCCESS; + + const uint32_t mask = inst->word(5); + + if (spvIsOpenCLEnv(_.context()->target_env)) { + if (mask & SpvImageOperandsConstOffsetMask) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "ConstOffset image operand not allowed " + << "in the OpenCL environment."; + } + } + + if (spv_result_t result = + ValidateImageOperands(_, inst, info, mask, /* word_index = */ 6)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { + const uint32_t image_type = _.GetOperandTypeId(inst, 0); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (info.dim == SpvDimSubpassData) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' cannot be SubpassData"; + } + + if (spv_result_t result = ValidateImageCommon(_, inst, info)) return result; + + const uint32_t coord_type = _.GetOperandTypeId(inst, 1); + if (!_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int scalar or vector"; + } + + const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + + // TODO(atgoo@github.com) The spec doesn't explicitely say what the type + // of texel should be. + const uint32_t texel_type = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarOrVectorType(texel_type) && + !_.IsFloatScalarOrVectorType(texel_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Texel to be int or float vector or scalar"; + } + +#if 0 + // TODO: See above. + if (_.GetDimension(texel_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Texel to have 4 components"; + } +#endif + + if (_.GetIdOpcode(info.sampled_type) != SpvOpTypeVoid) { + const uint32_t texel_component_type = _.GetComponentType(texel_type); + if (texel_component_type != info.sampled_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image 'Sampled Type' to be the same as Texel " + << "components"; + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (info.format == SpvImageFormatUnknown && info.dim != SpvDimSubpassData && + !_.HasCapability(SpvCapabilityStorageImageWriteWithoutFormat)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Capability StorageImageWriteWithoutFormat is required to " + "write " + << "to storage image"; + } + } + + if (inst->words().size() <= 4) { + return SPV_SUCCESS; + } else { + if (spvIsOpenCLEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Optional Image Operands are not allowed in the OpenCL " + << "environment."; + } + } + + const uint32_t mask = inst->word(4); + if (spv_result_t result = + ValidateImageOperands(_, inst, info, mask, /* word_index = */ 5)) + return result; + + return SPV_SUCCESS; +} + +spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (_.GetIdOpcode(result_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be OpTypeImage"; + } + + const uint32_t sampled_image_type = _.GetOperandTypeId(inst, 2); + const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type); + assert(sampled_image_type_inst); + + if (sampled_image_type_inst->opcode() != SpvOpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample Image to be of type OpTypeSampleImage"; + } + + if (sampled_image_type_inst->word(2) != result_type) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Sample Image image type to be equal to Result Type"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be int scalar or vector type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + uint32_t expected_num_components = info.arrayed; + switch (info.dim) { + case SpvDim1D: + expected_num_components += 1; + break; + case SpvDim2D: + case SpvDimCube: + expected_num_components += 2; + break; + case SpvDim3D: + expected_num_components += 3; + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be 1D, 2D, 3D or Cube"; + } + + if (info.multisampled != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0"; + } + + uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components != expected_num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type has " << result_num_components << " components, " + << "but " << expected_num_components << " expected"; + } + + const uint32_t lod_type = _.GetOperandTypeId(inst, 3); + if (!_.IsIntScalarType(lod_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Level of Detail to be int scalar"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageQuerySize(ValidationState_t& _, + const Instruction* inst) { + const uint32_t result_type = inst->type_id(); + if (!_.IsIntScalarOrVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be int scalar or vector type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + uint32_t expected_num_components = info.arrayed; + switch (info.dim) { + case SpvDim1D: + case SpvDimBuffer: + expected_num_components += 1; + break; + case SpvDim2D: + case SpvDimCube: + case SpvDimRect: + expected_num_components += 2; + break; + case SpvDim3D: + expected_num_components += 3; + break; + default: + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect"; + } + + if (info.dim == SpvDim1D || info.dim == SpvDim2D || info.dim == SpvDim3D || + info.dim == SpvDimCube) { + if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2"; + } + } + + uint32_t result_num_components = _.GetDimension(result_type); + if (result_num_components != expected_num_components) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Result Type has " << result_num_components << " components, " + << "but " << expected_num_components << " expected"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateImageQueryFormatOrOrder(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsIntScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be int scalar type"; + } + + if (_.GetIdOpcode(_.GetOperandTypeId(inst, 2)) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be of type OpTypeImage"; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageQueryLod(ValidationState_t& _, + const Instruction* inst) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [&](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelFragment && + model != SpvExecutionModelGLCompute) { + if (message) { + *message = std::string( + "OpImageQueryLod requires Fragment or GLCompute execution " + "model"); + } + return false; + } + return true; + }); + _.function(inst->function()->id()) + ->RegisterLimitation([](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const auto* models = state.GetExecutionModels(entry_point->id()); + const auto* modes = state.GetExecutionModes(entry_point->id()); + if (models->find(SpvExecutionModelGLCompute) != models->end() && + modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()) { + if (message) { + *message = std::string( + "OpImageQueryLod requires DerivativeGroupQuadsNV " + "or DerivativeGroupLinearNV execution mode for GLCompute " + "execution model"); + } + return false; + } + return true; + }); + + const uint32_t result_type = inst->type_id(); + if (!_.IsFloatVectorType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be float vector type"; + } + + if (_.GetDimension(result_type) != 2) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to have 2 components"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeSampledImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image operand to be of type OpTypeSampledImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && + info.dim != SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be 1D, 2D, 3D or Cube"; + } + + const uint32_t coord_type = _.GetOperandTypeId(inst, 3); + if (_.HasCapability(SpvCapabilityKernel)) { + if (!_.IsFloatScalarOrVectorType(coord_type) && + !_.IsIntScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be int or float scalar or vector"; + } + } else { + if (!_.IsFloatScalarOrVectorType(coord_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to be float scalar or vector"; + } + } + + const uint32_t min_coord_size = GetPlaneCoordSize(info); + const uint32_t actual_coord_size = _.GetDimension(coord_type); + if (min_coord_size > actual_coord_size) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Coordinate to have at least " << min_coord_size + << " components, but given only " << actual_coord_size; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageSparseLod(ValidationState_t& _, + const Instruction* inst) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Instruction reserved for future use, use of this instruction " + << "is invalid"; +} + +spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsIntScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be int scalar type"; + } + + const uint32_t image_type = _.GetOperandTypeId(inst, 2); + if (_.GetIdOpcode(image_type) != SpvOpTypeImage) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Image to be of type OpTypeImage"; + } + + ImageTypeInfo info; + if (!GetImageTypeInfo(_, image_type, &info)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Corrupt image type definition"; + } + + const SpvOp opcode = inst->opcode(); + if (opcode == SpvOpImageQueryLevels) { + if (info.dim != SpvDim1D && info.dim != SpvDim2D && info.dim != SpvDim3D && + info.dim != SpvDimCube) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Image 'Dim' must be 1D, 2D, 3D or Cube"; + } + } else { + assert(opcode == SpvOpImageQuerySamples); + if (info.dim != SpvDim2D) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D"; + } + + if (info.multisampled != 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 1"; + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateImageSparseTexelsResident(ValidationState_t& _, + const Instruction* inst) { + if (!_.IsBoolScalarType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be bool scalar type"; + } + + const uint32_t resident_code_type = _.GetOperandTypeId(inst, 2); + if (!_.IsIntScalarType(resident_code_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Resident Code to be int scalar"; + } + + return SPV_SUCCESS; +} + +} // namespace + +// Validates correctness of image instructions. +spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + if (IsImplicitLod(opcode)) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([opcode](SpvExecutionModel model, + std::string* message) { + if (model != SpvExecutionModelFragment && + model != SpvExecutionModelGLCompute) { + if (message) { + *message = + std::string( + "ImplicitLod instructions require Fragment or GLCompute " + "execution model: ") + + spvOpcodeString(opcode); + } + return false; + } + return true; + }); + _.function(inst->function()->id()) + ->RegisterLimitation([opcode](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const auto* models = state.GetExecutionModels(entry_point->id()); + const auto* modes = state.GetExecutionModes(entry_point->id()); + if (models->find(SpvExecutionModelGLCompute) != models->end() && + modes->find(SpvExecutionModeDerivativeGroupLinearNV) == + modes->end() && + modes->find(SpvExecutionModeDerivativeGroupQuadsNV) == + modes->end()) { + if (message) { + *message = + std::string( + "ImplicitLod instructions require DerivativeGroupQuadsNV " + "or DerivativeGroupLinearNV execution mode for GLCompute " + "execution model: ") + + spvOpcodeString(opcode); + } + return false; + } + return true; + }); + } + + switch (opcode) { + case SpvOpTypeImage: + return ValidateTypeImage(_, inst); + case SpvOpTypeSampledImage: + return ValidateTypeSampledImage(_, inst); + case SpvOpSampledImage: + return ValidateSampledImage(_, inst); + case SpvOpImageTexelPointer: + return ValidateImageTexelPointer(_, inst); + + case SpvOpImageSampleImplicitLod: + case SpvOpImageSampleExplicitLod: + case SpvOpImageSampleProjImplicitLod: + case SpvOpImageSampleProjExplicitLod: + case SpvOpImageSparseSampleImplicitLod: + case SpvOpImageSparseSampleExplicitLod: + return ValidateImageLod(_, inst); + + case SpvOpImageSampleDrefImplicitLod: + case SpvOpImageSampleDrefExplicitLod: + case SpvOpImageSampleProjDrefImplicitLod: + case SpvOpImageSampleProjDrefExplicitLod: + case SpvOpImageSparseSampleDrefImplicitLod: + case SpvOpImageSparseSampleDrefExplicitLod: + return ValidateImageDrefLod(_, inst); + + case SpvOpImageFetch: + case SpvOpImageSparseFetch: + return ValidateImageFetch(_, inst); + + case SpvOpImageGather: + case SpvOpImageDrefGather: + case SpvOpImageSparseGather: + case SpvOpImageSparseDrefGather: + return ValidateImageGather(_, inst); + + case SpvOpImageRead: + case SpvOpImageSparseRead: + return ValidateImageRead(_, inst); + + case SpvOpImageWrite: + return ValidateImageWrite(_, inst); + + case SpvOpImage: + return ValidateImage(_, inst); + + case SpvOpImageQueryFormat: + case SpvOpImageQueryOrder: + return ValidateImageQueryFormatOrOrder(_, inst); + + case SpvOpImageQuerySizeLod: + return ValidateImageQuerySizeLod(_, inst); + case SpvOpImageQuerySize: + return ValidateImageQuerySize(_, inst); + case SpvOpImageQueryLod: + return ValidateImageQueryLod(_, inst); + + case SpvOpImageQueryLevels: + case SpvOpImageQuerySamples: + return ValidateImageQueryLevelsOrSamples(_, inst); + + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: + return ValidateImageSparseLod(_, inst); + + case SpvOpImageSparseTexelsResident: + return ValidateImageSparseTexelsResident(_, inst); + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_instruction.cpp b/third_party/spirv-tools/source/val/validate_instruction.cpp new file mode 100644 index 0000000..9d395fb --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_instruction.cpp @@ -0,0 +1,501 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Performs validation on instructions that appear inside of a SPIR-V block. + +#include +#include +#include +#include +#include +#include + +#include "source/binary.h" +#include "source/diagnostic.h" +#include "source/enum_set.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/spirv_constant.h" +#include "source/spirv_definition.h" +#include "source/spirv_target_env.h" +#include "source/spirv_validator_options.h" +#include "source/util/string_utils.h" +#include "source/val/function.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +std::string ToString(const CapabilitySet& capabilities, + const AssemblyGrammar& grammar) { + std::stringstream ss; + capabilities.ForEach([&grammar, &ss](SpvCapability cap) { + spv_operand_desc desc; + if (SPV_SUCCESS == + grammar.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) + ss << desc->name << " "; + else + ss << cap << " "; + }); + return ss.str(); +} + +// Returns capabilities that enable an opcode. An empty result is interpreted +// as no prohibition of use of the opcode. If the result is non-empty, then +// the opcode may only be used if at least one of the capabilities is specified +// by the module. +CapabilitySet EnablingCapabilitiesForOp(const ValidationState_t& state, + SpvOp opcode) { + // Exceptions for SPV_AMD_shader_ballot + switch (opcode) { + // Normally these would require Group capability + case SpvOpGroupIAddNonUniformAMD: + case SpvOpGroupFAddNonUniformAMD: + case SpvOpGroupFMinNonUniformAMD: + case SpvOpGroupUMinNonUniformAMD: + case SpvOpGroupSMinNonUniformAMD: + case SpvOpGroupFMaxNonUniformAMD: + case SpvOpGroupUMaxNonUniformAMD: + case SpvOpGroupSMaxNonUniformAMD: + if (state.HasExtension(kSPV_AMD_shader_ballot)) return CapabilitySet(); + break; + default: + break; + } + // Look it up in the grammar + spv_opcode_desc opcode_desc = {}; + if (SPV_SUCCESS == state.grammar().lookupOpcode(opcode, &opcode_desc)) { + return state.grammar().filterCapsAgainstTargetEnv( + opcode_desc->capabilities, opcode_desc->numCapabilities); + } + return CapabilitySet(); +} + +// Returns SPV_SUCCESS if, for the given operand, the target environment +// satsifies minimum version requirements, or if the module declares an +// enabling extension for the operand. Otherwise emit a diagnostic and +// return an error code. +spv_result_t OperandVersionExtensionCheck( + ValidationState_t& _, const Instruction* inst, size_t which_operand, + const spv_operand_desc_t& operand_desc, uint32_t word) { + const uint32_t module_version = _.version(); + const uint32_t operand_min_version = operand_desc.minVersion; + const uint32_t operand_last_version = operand_desc.lastVersion; + const bool reserved = operand_min_version == 0xffffffffu; + const bool version_satisfied = !reserved && + (operand_min_version <= module_version) && + (module_version <= operand_last_version); + + if (version_satisfied) { + return SPV_SUCCESS; + } + + if (operand_last_version < module_version) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvtools::utils::CardinalToOrdinal(which_operand) + << " operand of " << spvOpcodeString(inst->opcode()) << ": operand " + << operand_desc.name << "(" << word << ") requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(operand_last_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(operand_last_version) + << " or earlier"; + } + + if (!reserved && operand_desc.numExtensions == 0) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvtools::utils::CardinalToOrdinal(which_operand) + << " operand of " << spvOpcodeString(inst->opcode()) << ": operand " + << operand_desc.name << "(" << word << ") requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(operand_min_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(operand_min_version) << " or later"; + } else { + ExtensionSet required_extensions(operand_desc.numExtensions, + operand_desc.extensions); + if (!_.HasAnyOfExtensions(required_extensions)) { + return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) + << spvtools::utils::CardinalToOrdinal(which_operand) + << " operand of " << spvOpcodeString(inst->opcode()) + << ": operand " << operand_desc.name << "(" << word + << ") requires one of these extensions: " + << ExtensionSetToString(required_extensions); + } + } + return SPV_SUCCESS; +} + +// Returns SPV_SUCCESS if the given operand is enabled by capabilities declared +// in the module. Otherwise issues an error message and returns +// SPV_ERROR_INVALID_CAPABILITY. +spv_result_t CheckRequiredCapabilities(ValidationState_t& state, + const Instruction* inst, + size_t which_operand, + const spv_parsed_operand_t& operand, + uint32_t word) { + // Mere mention of PointSize, ClipDistance, or CullDistance in a Builtin + // decoration does not require the associated capability. The use of such + // a variable value should trigger the capability requirement, but that's + // not implemented yet. This rule is independent of target environment. + // See https://github.com/KhronosGroup/SPIRV-Tools/issues/365 + if (operand.type == SPV_OPERAND_TYPE_BUILT_IN) { + switch (word) { + case SpvBuiltInPointSize: + case SpvBuiltInClipDistance: + case SpvBuiltInCullDistance: + return SPV_SUCCESS; + default: + break; + } + } else if (operand.type == SPV_OPERAND_TYPE_FP_ROUNDING_MODE) { + // Allow all FP rounding modes if requested + if (state.features().free_fp_rounding_mode) { + return SPV_SUCCESS; + } + } else if (operand.type == SPV_OPERAND_TYPE_GROUP_OPERATION && + state.features().group_ops_reduce_and_scans && + (word <= uint32_t(SpvGroupOperationExclusiveScan))) { + // Allow certain group operations if requested. + return SPV_SUCCESS; + } + + CapabilitySet enabling_capabilities; + spv_operand_desc operand_desc = nullptr; + const auto lookup_result = + state.grammar().lookupOperand(operand.type, word, &operand_desc); + if (lookup_result == SPV_SUCCESS) { + // Allow FPRoundingMode decoration if requested. + if (operand.type == SPV_OPERAND_TYPE_DECORATION && + operand_desc->value == SpvDecorationFPRoundingMode) { + if (state.features().free_fp_rounding_mode) return SPV_SUCCESS; + + // Vulkan API requires more capabilities on rounding mode. + if (spvIsVulkanEnv(state.context()->target_env)) { + enabling_capabilities.Add(SpvCapabilityStorageUniformBufferBlock16); + enabling_capabilities.Add(SpvCapabilityStorageUniform16); + enabling_capabilities.Add(SpvCapabilityStoragePushConstant16); + enabling_capabilities.Add(SpvCapabilityStorageInputOutput16); + } + } else { + enabling_capabilities = state.grammar().filterCapsAgainstTargetEnv( + operand_desc->capabilities, operand_desc->numCapabilities); + } + + // When encountering an OpCapability instruction, the instruction pass + // registers a capability with the module *before* checking capabilities. + // So in the case of an OpCapability instruction, don't bother checking + // enablement by another capability. + if (inst->opcode() != SpvOpCapability) { + const bool enabled_by_cap = + state.HasAnyOfCapabilities(enabling_capabilities); + if (!enabling_capabilities.IsEmpty() && !enabled_by_cap) { + return state.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Operand " << which_operand << " of " + << spvOpcodeString(inst->opcode()) + << " requires one of these capabilities: " + << ToString(enabling_capabilities, state.grammar()); + } + } + return OperandVersionExtensionCheck(state, inst, which_operand, + *operand_desc, word); + } + return SPV_SUCCESS; +} + +// Returns SPV_ERROR_INVALID_BINARY and emits a diagnostic if the instruction +// is explicitly reserved in the SPIR-V core spec. Otherwise return +// SPV_SUCCESS. +spv_result_t ReservedCheck(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + switch (opcode) { + // These instructions are enabled by a capability, but should never + // be used anyway. + case SpvOpImageSparseSampleProjImplicitLod: + case SpvOpImageSparseSampleProjExplicitLod: + case SpvOpImageSparseSampleProjDrefImplicitLod: + case SpvOpImageSparseSampleProjDrefExplicitLod: { + spv_opcode_desc inst_desc; + _.grammar().lookupOpcode(opcode, &inst_desc); + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Invalid Opcode name 'Op" << inst_desc->name << "'"; + } + default: + break; + } + return SPV_SUCCESS; +} + +// Returns SPV_ERROR_INVALID_CAPABILITY and emits a diagnostic if the +// instruction is invalid because the required capability isn't declared +// in the module. +spv_result_t CapabilityCheck(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + CapabilitySet opcode_caps = EnablingCapabilitiesForOp(_, opcode); + if (!_.HasAnyOfCapabilities(opcode_caps)) { + return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst) + << "Opcode " << spvOpcodeString(opcode) + << " requires one of these capabilities: " + << ToString(opcode_caps, _.grammar()); + } + for (size_t i = 0; i < inst->operands().size(); ++i) { + const auto& operand = inst->operand(i); + const auto word = inst->word(operand.offset); + if (spvOperandIsConcreteMask(operand.type)) { + // Check for required capabilities for each bit position of the mask. + for (uint32_t mask_bit = 0x80000000; mask_bit; mask_bit >>= 1) { + if (word & mask_bit) { + spv_result_t status = + CheckRequiredCapabilities(_, inst, i + 1, operand, mask_bit); + if (status != SPV_SUCCESS) return status; + } + } + } else if (spvIsIdType(operand.type)) { + // TODO(dneto): Check the value referenced by this Id, if we can compute + // it. For now, just punt, to fix issue 248: + // https://github.com/KhronosGroup/SPIRV-Tools/issues/248 + } else { + // Check the operand word as a whole. + spv_result_t status = + CheckRequiredCapabilities(_, inst, i + 1, operand, word); + if (status != SPV_SUCCESS) return status; + } + } + return SPV_SUCCESS; +} + +// Checks that the instruction can be used in this target environment's base +// version. Assumes that CapabilityCheck has checked direct capability +// dependencies for the opcode. +spv_result_t VersionCheck(ValidationState_t& _, const Instruction* inst) { + const auto opcode = inst->opcode(); + spv_opcode_desc inst_desc; + const spv_result_t r = _.grammar().lookupOpcode(opcode, &inst_desc); + assert(r == SPV_SUCCESS); + (void)r; + + const auto min_version = inst_desc->minVersion; + const auto last_version = inst_desc->lastVersion; + const auto module_version = _.version(); + + if (last_version < module_version) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvOpcodeString(opcode) << " requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(last_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(last_version) << " or earlier"; + } + + // OpTerminateInvocation is special because it is enabled by Shader + // capability, but also requries a extension and/or version check. + const bool capability_check_is_sufficient = + inst->opcode() != SpvOpTerminateInvocation; + + if (capability_check_is_sufficient && (inst_desc->numCapabilities > 0u)) { + // We already checked that the direct capability dependency has been + // satisfied. We don't need to check any further. + return SPV_SUCCESS; + } + + ExtensionSet exts(inst_desc->numExtensions, inst_desc->extensions); + if (exts.IsEmpty()) { + // If no extensions can enable this instruction, then emit error + // messages only concerning core SPIR-V versions if errors happen. + if (min_version == ~0u) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvOpcodeString(opcode) << " is reserved for future use."; + } + + if (module_version < min_version) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvOpcodeString(opcode) << " requires " + << spvTargetEnvDescription( + static_cast(min_version)) + << " at minimum."; + } + } else if (!_.HasAnyOfExtensions(exts)) { + // Otherwise, we only error out when no enabling extensions are + // registered. + if (min_version == ~0u) { + return _.diag(SPV_ERROR_MISSING_EXTENSION, inst) + << spvOpcodeString(opcode) + << " requires one of the following extensions: " + << ExtensionSetToString(exts); + } + + if (module_version < min_version) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << spvOpcodeString(opcode) << " requires SPIR-V version " + << SPV_SPIRV_VERSION_MAJOR_PART(min_version) << "." + << SPV_SPIRV_VERSION_MINOR_PART(min_version) + << " at minimum or one of the following extensions: " + << ExtensionSetToString(exts); + } + } + + return SPV_SUCCESS; +} + +// Checks that the Resuld is within the valid bound. +spv_result_t LimitCheckIdBound(ValidationState_t& _, const Instruction* inst) { + if (inst->id() >= _.getIdBound()) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Result '" << inst->id() + << "' must be less than the ID bound '" << _.getIdBound() << "'."; + } + return SPV_SUCCESS; +} + +// Checks that the number of OpTypeStruct members is within the limit. +spv_result_t LimitCheckStruct(ValidationState_t& _, const Instruction* inst) { + if (SpvOpTypeStruct != inst->opcode()) { + return SPV_SUCCESS; + } + + // Number of members is the number of operands of the instruction minus 1. + // One operand is the result ID. + const uint16_t limit = + static_cast(_.options()->universal_limits_.max_struct_members); + if (inst->operands().size() - 1 > limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Number of OpTypeStruct members (" << inst->operands().size() - 1 + << ") has exceeded the limit (" << limit << ")."; + } + + // Section 2.17 of SPIRV Spec specifies that the "Structure Nesting Depth" + // must be less than or equal to 255. + // This is interpreted as structures including other structures as + // members. The code does not follow pointers or look into arrays to see + // if we reach a structure downstream. The nesting depth of a struct is + // 1+(largest depth of any member). Scalars are at depth 0. + uint32_t max_member_depth = 0; + // Struct members start at word 2 of OpTypeStruct instruction. + for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) { + auto member = inst->word(word_i); + auto memberTypeInstr = _.FindDef(member); + if (memberTypeInstr && SpvOpTypeStruct == memberTypeInstr->opcode()) { + max_member_depth = std::max( + max_member_depth, _.struct_nesting_depth(memberTypeInstr->id())); + } + } + + const uint32_t depth_limit = _.options()->universal_limits_.max_struct_depth; + const uint32_t cur_depth = 1 + max_member_depth; + _.set_struct_nesting_depth(inst->id(), cur_depth); + if (cur_depth > depth_limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Structure Nesting Depth may not be larger than " << depth_limit + << ". Found " << cur_depth << "."; + } + return SPV_SUCCESS; +} + +// Checks that the number of (literal, label) pairs in OpSwitch is within +// the limit. +spv_result_t LimitCheckSwitch(ValidationState_t& _, const Instruction* inst) { + if (SpvOpSwitch == inst->opcode()) { + // The instruction syntax is as follows: + // OpSwitch literal label literal label ... + // literal,label pairs come after the first 2 operands. + // It is guaranteed at this point that num_operands is an even numner. + size_t num_pairs = (inst->operands().size() - 2) / 2; + const unsigned int num_pairs_limit = + _.options()->universal_limits_.max_switch_branches; + if (num_pairs > num_pairs_limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Number of (literal, label) pairs in OpSwitch (" << num_pairs + << ") exceeds the limit (" << num_pairs_limit << ")."; + } + } + return SPV_SUCCESS; +} + +// Ensure the number of variables of the given class does not exceed the +// limit. +spv_result_t LimitCheckNumVars(ValidationState_t& _, const uint32_t var_id, + const SpvStorageClass storage_class) { + if (SpvStorageClassFunction == storage_class) { + _.registerLocalVariable(var_id); + const uint32_t num_local_vars_limit = + _.options()->universal_limits_.max_local_variables; + if (_.num_local_vars() > num_local_vars_limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) + << "Number of local variables ('Function' Storage Class) " + "exceeded the valid limit (" + << num_local_vars_limit << ")."; + } + } else { + _.registerGlobalVariable(var_id); + const uint32_t num_global_vars_limit = + _.options()->universal_limits_.max_global_variables; + if (_.num_global_vars() > num_global_vars_limit) { + return _.diag(SPV_ERROR_INVALID_BINARY, nullptr) + << "Number of Global Variables (Storage Class other than " + "'Function') exceeded the valid limit (" + << num_global_vars_limit << ")."; + } + } + return SPV_SUCCESS; +} + +// Parses OpExtension instruction and logs warnings if unsuccessful. +spv_result_t CheckIfKnownExtension(ValidationState_t& _, + const Instruction* inst) { + const std::string extension_str = GetExtensionString(&(inst->c_inst())); + Extension extension; + if (!GetExtensionFromString(extension_str.c_str(), &extension)) { + return _.diag(SPV_WARNING, inst) + << "Found unrecognized extension " << extension_str; + } + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t InstructionPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + if (opcode == SpvOpExtension) { + CheckIfKnownExtension(_, inst); + } else if (opcode == SpvOpCapability) { + _.RegisterCapability(inst->GetOperandAs(0)); + } else if (opcode == SpvOpMemoryModel) { + if (_.has_memory_model_specified()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "OpMemoryModel should only be provided once."; + } + _.set_addressing_model(inst->GetOperandAs(0)); + _.set_memory_model(inst->GetOperandAs(1)); + } else if (opcode == SpvOpExecutionMode) { + const uint32_t entry_point = inst->word(1); + _.RegisterExecutionModeForEntryPoint(entry_point, + SpvExecutionMode(inst->word(2))); + } else if (opcode == SpvOpVariable) { + const auto storage_class = inst->GetOperandAs(2); + if (auto error = LimitCheckNumVars(_, inst->id(), storage_class)) { + return error; + } + } + + if (auto error = ReservedCheck(_, inst)) return error; + if (auto error = CapabilityCheck(_, inst)) return error; + if (auto error = LimitCheckIdBound(_, inst)) return error; + if (auto error = LimitCheckStruct(_, inst)) return error; + if (auto error = LimitCheckSwitch(_, inst)) return error; + if (auto error = VersionCheck(_, inst)) return error; + + // All instruction checks have passed. + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_interfaces.cpp b/third_party/spirv-tools/source/val/validate_interfaces.cpp new file mode 100644 index 0000000..d16d48e --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_interfaces.cpp @@ -0,0 +1,504 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "source/diagnostic.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/val/function.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns true if \c inst is an input or output variable. +bool is_interface_variable(const Instruction* inst, bool is_spv_1_4) { + if (is_spv_1_4) { + // Starting in SPIR-V 1.4, all global variables are interface variables. + return inst->opcode() == SpvOpVariable && + inst->word(3u) != SpvStorageClassFunction; + } else { + return inst->opcode() == SpvOpVariable && + (inst->word(3u) == SpvStorageClassInput || + inst->word(3u) == SpvStorageClassOutput); + } +} + +// Checks that \c var is listed as an interface in all the entry points that use +// it. +spv_result_t check_interface_variable(ValidationState_t& _, + const Instruction* var) { + std::vector functions; + std::vector uses; + for (auto use : var->uses()) { + uses.push_back(use.first); + } + for (uint32_t i = 0; i < uses.size(); ++i) { + const auto user = uses[i]; + if (const Function* func = user->function()) { + functions.push_back(func); + } else { + // In the rare case that the variable is used by another instruction in + // the global scope, continue searching for an instruction used in a + // function. + for (auto use : user->uses()) { + uses.push_back(use.first); + } + } + } + + std::sort(functions.begin(), functions.end(), + [](const Function* lhs, const Function* rhs) { + return lhs->id() < rhs->id(); + }); + functions.erase(std::unique(functions.begin(), functions.end()), + functions.end()); + + std::vector entry_points; + for (const auto func : functions) { + for (auto id : _.FunctionEntryPoints(func->id())) { + entry_points.push_back(id); + } + } + + std::sort(entry_points.begin(), entry_points.end()); + entry_points.erase(std::unique(entry_points.begin(), entry_points.end()), + entry_points.end()); + + for (auto id : entry_points) { + for (const auto& desc : _.entry_point_descriptions(id)) { + bool found = false; + for (auto interface : desc.interfaces) { + if (var->id() == interface) { + found = true; + break; + } + } + if (!found) { + return _.diag(SPV_ERROR_INVALID_ID, var) + << "Interface variable id <" << var->id() + << "> is used by entry point '" << desc.name << "' id <" << id + << ">, but is not listed as an interface"; + } + } + } + + return SPV_SUCCESS; +} + +// This function assumes a base location has been determined already. As such +// any further location decorations are invalid. +// TODO: if this code turns out to be slow, there is an opportunity to cache +// the result for a given type id. +spv_result_t NumConsumedLocations(ValidationState_t& _, const Instruction* type, + uint32_t* num_locations) { + *num_locations = 0; + switch (type->opcode()) { + case SpvOpTypeInt: + case SpvOpTypeFloat: + // Scalars always consume a single location. + *num_locations = 1; + break; + case SpvOpTypeVector: + // 3- and 4-component 64-bit vectors consume two locations. + if ((_.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeInt, 64) || + _.ContainsSizedIntOrFloatType(type->id(), SpvOpTypeFloat, 64)) && + (type->GetOperandAs(2) > 2)) { + *num_locations = 2; + } else { + *num_locations = 1; + } + break; + case SpvOpTypeMatrix: + // Matrices consume locations equal to the underlying vector type for + // each column. + NumConsumedLocations(_, _.FindDef(type->GetOperandAs(1)), + num_locations); + *num_locations *= type->GetOperandAs(2); + break; + case SpvOpTypeArray: { + // Arrays consume locations equal to the underlying type times the number + // of elements in the vector. + NumConsumedLocations(_, _.FindDef(type->GetOperandAs(1)), + num_locations); + bool is_int = false; + bool is_const = false; + uint32_t value = 0; + // Attempt to evaluate the number of array elements. + std::tie(is_int, is_const, value) = + _.EvalInt32IfConst(type->GetOperandAs(2)); + if (is_int && is_const) *num_locations *= value; + break; + } + case SpvOpTypeStruct: { + // Members cannot have location decorations at this point. + if (_.HasDecoration(type->id(), SpvDecorationLocation)) { + return _.diag(SPV_ERROR_INVALID_DATA, type) + << "Members cannot be assigned a location"; + } + + // Structs consume locations equal to the sum of the locations consumed + // by the members. + for (uint32_t i = 1; i < type->operands().size(); ++i) { + uint32_t member_locations = 0; + if (auto error = NumConsumedLocations( + _, _.FindDef(type->GetOperandAs(i)), + &member_locations)) { + return error; + } + *num_locations += member_locations; + } + break; + } + default: + break; + } + + return SPV_SUCCESS; +} + +// Returns the number of components consumed by types that support a component +// decoration. +uint32_t NumConsumedComponents(ValidationState_t& _, const Instruction* type) { + uint32_t num_components = 0; + switch (type->opcode()) { + case SpvOpTypeInt: + case SpvOpTypeFloat: + // 64-bit types consume two components. + if (type->GetOperandAs(1) == 64) { + num_components = 2; + } else { + num_components = 1; + } + break; + case SpvOpTypeVector: + // Vectors consume components equal to the underlying type's consumption + // times the number of elements in the vector. Note that 3- and 4-element + // vectors cannot have a component decoration (i.e. assumed to be zero). + num_components = + NumConsumedComponents(_, _.FindDef(type->GetOperandAs(1))); + num_components *= type->GetOperandAs(2); + break; + default: + // This is an error that is validated elsewhere. + break; + } + + return num_components; +} + +// Populates |locations| (and/or |output_index1_locations|) with the use +// location and component coordinates for |variable|. Indices are calculated as +// 4 * location + component. +spv_result_t GetLocationsForVariable( + ValidationState_t& _, const Instruction* entry_point, + const Instruction* variable, std::unordered_set* locations, + std::unordered_set* output_index1_locations) { + const bool is_fragment = entry_point->GetOperandAs(0) == + SpvExecutionModelFragment; + const bool is_output = + variable->GetOperandAs(2) == SpvStorageClassOutput; + auto ptr_type_id = variable->GetOperandAs(0); + auto ptr_type = _.FindDef(ptr_type_id); + auto type_id = ptr_type->GetOperandAs(2); + auto type = _.FindDef(type_id); + + // Check for Location, Component and Index decorations on the variable. The + // validator allows duplicate decorations if the location/component/index are + // equal. Also track Patch and PerTaskNV decorations. + bool has_location = false; + uint32_t location = 0; + bool has_component = false; + uint32_t component = 0; + bool has_index = false; + uint32_t index = 0; + bool has_patch = false; + bool has_per_task_nv = false; + bool has_per_vertex_nv = false; + for (auto& dec : _.id_decorations(variable->id())) { + if (dec.dec_type() == SpvDecorationLocation) { + if (has_location && dec.params()[0] != location) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << "Variable has conflicting location decorations"; + } + has_location = true; + location = dec.params()[0]; + } else if (dec.dec_type() == SpvDecorationComponent) { + if (has_component && dec.params()[0] != component) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << "Variable has conflicting component decorations"; + } + has_component = true; + component = dec.params()[0]; + } else if (dec.dec_type() == SpvDecorationIndex) { + if (!is_output || !is_fragment) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << "Index can only be applied to Fragment output variables"; + } + if (has_index && dec.params()[0] != index) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << "Variable has conflicting index decorations"; + } + has_index = true; + index = dec.params()[0]; + } else if (dec.dec_type() == SpvDecorationBuiltIn) { + // Don't check built-ins. + return SPV_SUCCESS; + } else if (dec.dec_type() == SpvDecorationPatch) { + has_patch = true; + } else if (dec.dec_type() == SpvDecorationPerTaskNV) { + has_per_task_nv = true; + } else if (dec.dec_type() == SpvDecorationPerVertexNV) { + has_per_vertex_nv = true; + } + } + + // Vulkan 14.1.3: Tessellation control and mesh per-vertex outputs and + // tessellation control, evaluation and geometry per-vertex inputs have a + // layer of arraying that is not included in interface matching. + bool is_arrayed = false; + switch (entry_point->GetOperandAs(0)) { + case SpvExecutionModelTessellationControl: + if (!has_patch) { + is_arrayed = true; + } + break; + case SpvExecutionModelTessellationEvaluation: + if (!is_output && !has_patch) { + is_arrayed = true; + } + break; + case SpvExecutionModelGeometry: + if (!is_output) { + is_arrayed = true; + } + break; + case SpvExecutionModelFragment: + if (!is_output && has_per_vertex_nv) { + is_arrayed = true; + } + break; + case SpvExecutionModelMeshNV: + if (is_output && !has_per_task_nv) { + is_arrayed = true; + } + break; + default: + break; + } + + // Unpack arrayness. + if (is_arrayed && (type->opcode() == SpvOpTypeArray || + type->opcode() == SpvOpTypeRuntimeArray)) { + type_id = type->GetOperandAs(1); + type = _.FindDef(type_id); + } + + if (type->opcode() == SpvOpTypeStruct) { + // Don't check built-ins. + if (_.HasDecoration(type_id, SpvDecorationBuiltIn)) return SPV_SUCCESS; + } + + // Only block-decorated structs don't need a location on the variable. + const bool is_block = _.HasDecoration(type_id, SpvDecorationBlock); + if (!has_location && !is_block) { + return _.diag(SPV_ERROR_INVALID_DATA, variable) + << "Variable must be decorated with a location"; + } + + const std::string storage_class = is_output ? "output" : "input"; + if (has_location) { + auto sub_type = type; + bool is_int = false; + bool is_const = false; + uint32_t array_size = 1; + // If the variable is still arrayed, mark the locations/components per + // index. + if (type->opcode() == SpvOpTypeArray) { + // Determine the array size if possible and get the element type. + std::tie(is_int, is_const, array_size) = + _.EvalInt32IfConst(type->GetOperandAs(2)); + if (!is_int || !is_const) array_size = 1; + auto sub_type_id = type->GetOperandAs(1); + sub_type = _.FindDef(sub_type_id); + } + + for (uint32_t array_idx = 0; array_idx < array_size; ++array_idx) { + uint32_t num_locations = 0; + if (auto error = NumConsumedLocations(_, sub_type, &num_locations)) + return error; + + uint32_t num_components = NumConsumedComponents(_, sub_type); + uint32_t array_location = location + (num_locations * array_idx); + uint32_t start = array_location * 4; + uint32_t end = (array_location + num_locations) * 4; + if (num_components != 0) { + start += component; + end = array_location * 4 + component + num_components; + } + + auto locs = locations; + if (has_index && index == 1) locs = output_index1_locations; + + for (uint32_t i = start; i < end; ++i) { + if (!locs->insert(i).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << i / 4 + << ", component " << i % 4; + } + } + } + } else { + // For Block-decorated structs with no location assigned to the variable, + // each member of the block must be assigned a location. Also record any + // member component assignments. The validator allows duplicate decorations + // if they agree on the location/component. + std::unordered_map member_locations; + std::unordered_map member_components; + for (auto& dec : _.id_decorations(type_id)) { + if (dec.dec_type() == SpvDecorationLocation) { + auto where = member_locations.find(dec.struct_member_index()); + if (where == member_locations.end()) { + member_locations[dec.struct_member_index()] = dec.params()[0]; + } else if (where->second != dec.params()[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, type) + << "Member index " << dec.struct_member_index() + << " has conflicting location assignments"; + } + } else if (dec.dec_type() == SpvDecorationComponent) { + auto where = member_components.find(dec.struct_member_index()); + if (where == member_components.end()) { + member_components[dec.struct_member_index()] = dec.params()[0]; + } else if (where->second != dec.params()[0]) { + return _.diag(SPV_ERROR_INVALID_DATA, type) + << "Member index " << dec.struct_member_index() + << " has conflicting component assignments"; + } + } + } + + for (uint32_t i = 1; i < type->operands().size(); ++i) { + auto where = member_locations.find(i - 1); + if (where == member_locations.end()) { + return _.diag(SPV_ERROR_INVALID_DATA, type) + << "Member index " << i - 1 + << " is missing a location assignment"; + } + + location = where->second; + auto member = _.FindDef(type->GetOperandAs(i)); + uint32_t num_locations = 0; + if (auto error = NumConsumedLocations(_, member, &num_locations)) + return error; + + // If the component is not specified, it is assumed to be zero. + uint32_t num_components = NumConsumedComponents(_, member); + component = 0; + if (member_components.count(i - 1)) { + component = member_components[i - 1]; + } + + uint32_t start = location * 4; + uint32_t end = (location + num_locations) * 4; + if (num_components != 0) { + start += component; + end = location * 4 + component + num_components; + } + for (uint32_t l = start; l < end; ++l) { + if (!locations->insert(l).second) { + return _.diag(SPV_ERROR_INVALID_DATA, entry_point) + << "Entry-point has conflicting " << storage_class + << " location assignment at location " << l / 4 + << ", component " << l % 4; + } + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateLocations(ValidationState_t& _, + const Instruction* entry_point) { + // According to Vulkan 14.1 only the following execution models have + // locations assigned. + switch (entry_point->GetOperandAs(0)) { + case SpvExecutionModelVertex: + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: + case SpvExecutionModelGeometry: + case SpvExecutionModelFragment: + break; + default: + return SPV_SUCCESS; + } + + // Locations are stored as a combined location and component values. + std::unordered_set input_locations; + std::unordered_set output_locations_index0; + std::unordered_set output_locations_index1; + for (uint32_t i = 3; i < entry_point->operands().size(); ++i) { + auto interface_id = entry_point->GetOperandAs(i); + auto interface_var = _.FindDef(interface_id); + auto storage_class = interface_var->GetOperandAs(2); + if (storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + continue; + } + + auto locations = (storage_class == SpvStorageClassInput) + ? &input_locations + : &output_locations_index0; + if (auto error = GetLocationsForVariable( + _, entry_point, interface_var, locations, &output_locations_index1)) + return error; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ValidateInterfaces(ValidationState_t& _) { + bool is_spv_1_4 = _.version() >= SPV_SPIRV_VERSION_WORD(1, 4); + for (auto& inst : _.ordered_instructions()) { + if (is_interface_variable(&inst, is_spv_1_4)) { + if (auto error = check_interface_variable(_, &inst)) { + return error; + } + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + for (auto& inst : _.ordered_instructions()) { + if (inst.opcode() == SpvOpEntryPoint) { + if (auto error = ValidateLocations(_, &inst)) { + return error; + } + } + if (inst.opcode() == SpvOpTypeVoid) break; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_layout.cpp b/third_party/spirv-tools/source/val/validate_layout.cpp new file mode 100644 index 0000000..b53f991 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_layout.cpp @@ -0,0 +1,357 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Source code for logical layout validation as described in section 2.4 + +#include + +#include "DebugInfo.h" +#include "OpenCLDebugInfo100.h" +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/operand.h" +#include "source/val/function.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Module scoped instructions are processed by determining if the opcode +// is part of the current layout section. If it is not then the next sections is +// checked. +spv_result_t ModuleScopedInstructions(ValidationState_t& _, + const Instruction* inst, SpvOp opcode) { + switch (opcode) { + case SpvOpExtInst: + if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but since they must name a return type they cannot be the + // first instruction in the types section. Therefore check that we are + // already in it. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } + } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + const uint32_t ext_inst_index = inst->word(4); + bool local_debug_info = false; + if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == OpenCLDebugInfo100DebugScope || + ext_inst_key == OpenCLDebugInfo100DebugNoScope || + ext_inst_key == OpenCLDebugInfo100DebugDeclare || + ext_inst_key == OpenCLDebugInfo100DebugValue) { + local_debug_info = true; + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + if (ext_inst_key == DebugInfoDebugScope || + ext_inst_key == DebugInfoDebugNoScope || + ext_inst_key == DebugInfoDebugDeclare || + ext_inst_key == DebugInfoDebugValue) { + local_debug_info = true; + } + } + + if (local_debug_info) { + if (_.in_function_body() == false) { + // DebugScope, DebugNoScope, DebugDeclare, DebugValue must + // appear in a function body. + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " + << "of debug info extension must appear in a function " + << "body"; + } + } else { + // Debug info extinst opcodes other than DebugScope, DebugNoScope, + // DebugDeclare, DebugValue must be placed between section 9 (types, + // constants, global variables) and section 10 (function + // declarations). + if (_.current_layout_section() < kLayoutTypes || + _.current_layout_section() >= kLayoutFunctionDeclarations) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Debug info extension instructions other than " + << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " + << "must appear between section 9 (types, constants, " + << "global variables) and section 10 (function " + << "declarations)"; + } + } + } else { + // otherwise they must be used in a block + if (_.current_layout_section() < kLayoutFunctionDefinitions) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " must appear in a block"; + } + } + break; + default: + break; + } + + while (_.IsOpcodeInCurrentLayoutSection(opcode) == false) { + if (_.IsOpcodeInPreviousLayoutSection(opcode)) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " is in an invalid layout section"; + } + + _.ProgressToNextLayoutSectionOrder(); + + switch (_.current_layout_section()) { + case kLayoutMemoryModel: + if (opcode != SpvOpMemoryModel) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) + << " cannot appear before the memory model instruction"; + } + break; + case kLayoutFunctionDeclarations: + // All module sections have been processed. Recursively call + // ModuleLayoutPass to process the next section of the module + return ModuleLayoutPass(_, inst); + default: + break; + } + } + return SPV_SUCCESS; +} + +// Function declaration validation is performed by making sure that the +// FunctionParameter and FunctionEnd instructions only appear inside of +// functions. It also ensures that the Function instruction does not appear +// inside of another function. This stage ends when the first label is +// encountered inside of a function. +spv_result_t FunctionScopedInstructions(ValidationState_t& _, + const Instruction* inst, SpvOp opcode) { + // Make sure we advance into the function definitions when we hit + // non-function declaration instructions. + if (_.current_layout_section() == kLayoutFunctionDeclarations && + !_.IsOpcodeInCurrentLayoutSection(opcode)) { + _.ProgressToNextLayoutSectionOrder(); + + if (_.in_function_body()) { + if (auto error = _.current_function().RegisterSetFunctionDeclType( + FunctionDecl::kFunctionDeclDefinition)) { + return error; + } + } + } + + if (_.IsOpcodeInCurrentLayoutSection(opcode)) { + switch (opcode) { + case SpvOpFunction: { + if (_.in_function_body()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Cannot declare a function in a function body"; + } + auto control_mask = inst->GetOperandAs(2); + if (auto error = + _.RegisterFunction(inst->id(), inst->type_id(), control_mask, + inst->GetOperandAs(3))) + return error; + if (_.current_layout_section() == kLayoutFunctionDefinitions) { + if (auto error = _.current_function().RegisterSetFunctionDeclType( + FunctionDecl::kFunctionDeclDefinition)) + return error; + } + } break; + + case SpvOpFunctionParameter: + if (_.in_function_body() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function parameter instructions must be in a " + "function body"; + } + if (_.current_function().block_count() != 0) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function parameters must only appear immediately after " + "the function definition"; + } + if (auto error = _.current_function().RegisterFunctionParameter( + inst->id(), inst->type_id())) + return error; + break; + + case SpvOpFunctionEnd: + if (_.in_function_body() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function end instructions must be in a function body"; + } + if (_.in_block()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function end cannot be called in blocks"; + } + if (_.current_function().block_count() == 0 && + _.current_layout_section() == kLayoutFunctionDefinitions) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Function declarations must appear before " + "function definitions."; + } + if (_.current_layout_section() == kLayoutFunctionDeclarations) { + if (auto error = _.current_function().RegisterSetFunctionDeclType( + FunctionDecl::kFunctionDeclDeclaration)) + return error; + } + if (auto error = _.RegisterFunctionEnd()) return error; + break; + + case SpvOpLine: + case SpvOpNoLine: + break; + case SpvOpLabel: + // If the label is encountered then the current function is a + // definition so set the function to a declaration and update the + // module section + if (_.in_function_body() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Label instructions must be in a function body"; + } + if (_.in_block()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "A block must end with a branch instruction."; + } + break; + + case SpvOpExtInst: + if (spvExtInstIsNonSemantic(inst->ext_inst_type())) { + // non-semantic extinst opcodes are allowed beginning in the types + // section, but must either be placed outside a function declaration, + // or inside a block. + if (_.current_layout_section() < kLayoutTypes) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst must not appear before types " + << "section"; + } else if (_.in_function_body() && _.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Non-semantic OpExtInst within function definition must " + "appear in a block"; + } + } else if (spvExtInstIsDebugInfo(inst->ext_inst_type())) { + const uint32_t ext_inst_index = inst->word(4); + bool local_debug_info = false; + if (inst->ext_inst_type() == SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100) { + const OpenCLDebugInfo100Instructions ext_inst_key = + OpenCLDebugInfo100Instructions(ext_inst_index); + if (ext_inst_key == OpenCLDebugInfo100DebugScope || + ext_inst_key == OpenCLDebugInfo100DebugNoScope || + ext_inst_key == OpenCLDebugInfo100DebugDeclare || + ext_inst_key == OpenCLDebugInfo100DebugValue) { + local_debug_info = true; + } + } else { + const DebugInfoInstructions ext_inst_key = + DebugInfoInstructions(ext_inst_index); + if (ext_inst_key == DebugInfoDebugScope || + ext_inst_key == DebugInfoDebugNoScope || + ext_inst_key == DebugInfoDebugDeclare || + ext_inst_key == DebugInfoDebugValue) { + local_debug_info = true; + } + } + + if (local_debug_info) { + if (_.in_function_body() == false) { + // DebugScope, DebugNoScope, DebugDeclare, DebugValue must + // appear in a function body. + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " + << "of debug info extension must appear in a function " + << "body"; + } + } else { + // Debug info extinst opcodes other than DebugScope, DebugNoScope, + // DebugDeclare, DebugValue must be placed between section 9 (types, + // constants, global variables) and section 10 (function + // declarations). + if (_.current_layout_section() < kLayoutTypes || + _.current_layout_section() >= kLayoutFunctionDeclarations) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Debug info extension instructions other than " + << "DebugScope, DebugNoScope, DebugDeclare, DebugValue " + << "must appear between section 9 (types, constants, " + << "global variables) and section 10 (function " + << "declarations)"; + } + } + } else { + // otherwise they must be used in a block + if (_.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " must appear in a block"; + } + } + break; + + default: + if (_.current_layout_section() == kLayoutFunctionDeclarations && + _.in_function_body()) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "A function must begin with a label"; + } else { + if (_.in_block() == false) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) << " must appear in a block"; + } + } + break; + } + } else { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << spvOpcodeString(opcode) + << " cannot appear in a function declaration"; + } + return SPV_SUCCESS; +} + +} // namespace + +// TODO(umar): Check linkage capabilities for function declarations +// TODO(umar): Better error messages +// NOTE: This function does not handle CFG related validation +// Performs logical layout validation. See Section 2.4 +spv_result_t ModuleLayoutPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + + switch (_.current_layout_section()) { + case kLayoutCapabilities: + case kLayoutExtensions: + case kLayoutExtInstImport: + case kLayoutMemoryModel: + case kLayoutEntryPoint: + case kLayoutExecutionMode: + case kLayoutDebug1: + case kLayoutDebug2: + case kLayoutDebug3: + case kLayoutAnnotations: + case kLayoutTypes: + if (auto error = ModuleScopedInstructions(_, inst, opcode)) return error; + break; + case kLayoutFunctionDeclarations: + case kLayoutFunctionDefinitions: + if (auto error = FunctionScopedInstructions(_, inst, opcode)) { + return error; + } + break; + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_literals.cpp b/third_party/spirv-tools/source/val/validate_literals.cpp new file mode 100644 index 0000000..53aae07 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_literals.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates literal numbers. + +#include "source/val/validate.h" + +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns true if the operand holds a literal number +bool IsLiteralNumber(const spv_parsed_operand_t& operand) { + switch (operand.number_kind) { + case SPV_NUMBER_SIGNED_INT: + case SPV_NUMBER_UNSIGNED_INT: + case SPV_NUMBER_FLOATING: + return true; + default: + return false; + } +} + +// Verifies that the upper bits of the given upper |word| with given +// lower |width| are zero- or sign-extended when |signed_int| is true +bool VerifyUpperBits(uint32_t word, uint32_t width, bool signed_int) { + assert(width < 32); + assert(0 < width); + const uint32_t upper_mask = 0xFFFFFFFFu << width; + const uint32_t upper_bits = word & upper_mask; + + bool result = false; + if (signed_int) { + const uint32_t sign_bit = word & (1u << (width - 1)); + if (sign_bit) { + result = upper_bits == upper_mask; + } else { + result = upper_bits == 0; + } + } else { + result = upper_bits == 0; + } + return result; +} + +} // namespace + +// Validates that literal numbers are represented according to the spec +spv_result_t LiteralsPass(ValidationState_t& _, const Instruction* inst) { + // For every operand that is a literal number + for (size_t i = 0; i < inst->operands().size(); i++) { + const spv_parsed_operand_t& operand = inst->operand(i); + if (!IsLiteralNumber(operand)) continue; + + // The upper bits are always in the last word (little-endian) + int last_index = operand.offset + operand.num_words - 1; + const uint32_t upper_word = inst->word(last_index); + + // TODO(jcaraban): is the |word size| defined in some header? + const uint32_t word_size = 32; + uint32_t bit_width = operand.number_bit_width; + + // Bit widths that are a multiple of the word size have no upper bits + const auto remaining_value_bits = bit_width % word_size; + if (remaining_value_bits == 0) continue; + + const bool signedness = operand.number_kind == SPV_NUMBER_SIGNED_INT; + + if (!VerifyUpperBits(upper_word, remaining_value_bits, signedness)) { + return _.diag(SPV_ERROR_INVALID_VALUE, inst) + << "The high-order bits of a literal number in instruction " + << inst->id() << " must be 0 for a floating-point type, " + << "or 0 for an integer type with Signedness of 0, " + << "or sign extended when Signedness is 1"; + } + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_logicals.cpp b/third_party/spirv-tools/source/val/validate_logicals.cpp new file mode 100644 index 0000000..5886dbf --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_logicals.cpp @@ -0,0 +1,286 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of logical SPIR-V instructions. + +#include "source/val/validate.h" + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of logical instructions. +spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + const uint32_t result_type = inst->type_id(); + + switch (opcode) { + case SpvOpAny: + case SpvOpAll: { + if (!_.IsBoolScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t vector_type = _.GetOperandTypeId(inst, 2); + if (!vector_type || !_.IsBoolVectorType(vector_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be vector bool: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpIsNan: + case SpvOpIsInf: + case SpvOpIsFinite: + case SpvOpIsNormal: + case SpvOpSignBitSet: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t operand_type = _.GetOperandTypeId(inst, 2); + if (!operand_type || (!_.IsFloatScalarType(operand_type) && + !_.IsFloatVectorType(operand_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be scalar or vector float: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(operand_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operand to be " + "equal: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpFOrdEqual: + case SpvOpFUnordEqual: + case SpvOpFOrdNotEqual: + case SpvOpFUnordNotEqual: + case SpvOpFOrdLessThan: + case SpvOpFUnordLessThan: + case SpvOpFOrdGreaterThan: + case SpvOpFUnordGreaterThan: + case SpvOpFOrdLessThanEqual: + case SpvOpFUnordLessThanEqual: + case SpvOpFOrdGreaterThanEqual: + case SpvOpFUnordGreaterThanEqual: + case SpvOpLessOrGreater: + case SpvOpOrdered: + case SpvOpUnordered: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2); + if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) && + !_.IsFloatVectorType(left_operand_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector float: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(left_operand_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be " + "equal: " + << spvOpcodeString(opcode); + + if (left_operand_type != _.GetOperandTypeId(inst, 3)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected left and right operands to have the same type: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpLogicalEqual: + case SpvOpLogicalNotEqual: + case SpvOpLogicalOr: + case SpvOpLogicalAnd: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + if (result_type != _.GetOperandTypeId(inst, 2) || + result_type != _.GetOperandTypeId(inst, 3)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpLogicalNot: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + if (result_type != _.GetOperandTypeId(inst, 2)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operand to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + + case SpvOpSelect: { + uint32_t dimension = 1; + { + const Instruction* type_inst = _.FindDef(result_type); + assert(type_inst); + + const auto composites = _.features().select_between_composites; + auto fail = [&_, composites, inst, opcode]() -> spv_result_t { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scalar or " + << (composites ? "composite" : "vector") + << " type as Result Type: " << spvOpcodeString(opcode); + }; + + const SpvOp type_opcode = type_inst->opcode(); + switch (type_opcode) { + case SpvOpTypePointer: { + if (_.addressing_model() == SpvAddressingModelLogical && + !_.features().variable_pointers && + !_.features().variable_pointers_storage_buffer) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using pointers with OpSelect requires capability " + << "VariablePointers or VariablePointersStorageBuffer"; + break; + } + + case SpvOpTypeVector: { + dimension = type_inst->word(3); + break; + } + + case SpvOpTypeBool: + case SpvOpTypeInt: + case SpvOpTypeFloat: { + break; + } + + // Not RuntimeArray because of other rules. + case SpvOpTypeArray: + case SpvOpTypeMatrix: + case SpvOpTypeStruct: { + if (!composites) return fail(); + break; + }; + + default: + return fail(); + } + + const uint32_t condition_type = _.GetOperandTypeId(inst, 2); + const uint32_t left_type = _.GetOperandTypeId(inst, 3); + const uint32_t right_type = _.GetOperandTypeId(inst, 4); + + if (!condition_type || (!_.IsBoolScalarType(condition_type) && + !_.IsBoolVectorType(condition_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as condition: " + << spvOpcodeString(opcode); + + if (_.GetDimension(condition_type) != dimension) { + // If the condition is a vector type, then the result must also be a + // vector with matching dimensions. In SPIR-V 1.4, a scalar condition + // can be used to select between vector types. |composites| is a + // proxy for SPIR-V 1.4 functionality. + if (!composites || _.IsBoolVectorType(condition_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the condition " + "to be equal: " + << spvOpcodeString(opcode); + } + } + + if (result_type != left_type || result_type != right_type) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both objects to be of Result Type: " + << spvOpcodeString(opcode); + + break; + } + } + + case SpvOpIEqual: + case SpvOpINotEqual: + case SpvOpUGreaterThan: + case SpvOpUGreaterThanEqual: + case SpvOpULessThan: + case SpvOpULessThanEqual: + case SpvOpSGreaterThan: + case SpvOpSGreaterThanEqual: + case SpvOpSLessThan: + case SpvOpSLessThanEqual: { + if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar or vector type as Result Type: " + << spvOpcodeString(opcode); + + const uint32_t left_type = _.GetOperandTypeId(inst, 2); + const uint32_t right_type = _.GetOperandTypeId(inst, 3); + + if (!left_type || + (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector int: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(left_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be" + << " equal: " << spvOpcodeString(opcode); + + if (!right_type || + (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type))) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected operands to be scalar or vector int: " + << spvOpcodeString(opcode); + + if (_.GetDimension(result_type) != _.GetDimension(right_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected vector sizes of Result Type and the operands to be" + << " equal: " << spvOpcodeString(opcode); + + if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected both operands to have the same component bit " + "width: " + << spvOpcodeString(opcode); + + break; + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_memory.cpp b/third_party/spirv-tools/source/val/validate_memory.cpp new file mode 100644 index 0000000..1e1a38d --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_memory.cpp @@ -0,0 +1,1669 @@ +// Copyright (c) 2018 Google LLC. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +bool AreLayoutCompatibleStructs(ValidationState_t&, const Instruction*, + const Instruction*); +bool HaveLayoutCompatibleMembers(ValidationState_t&, const Instruction*, + const Instruction*); +bool HaveSameLayoutDecorations(ValidationState_t&, const Instruction*, + const Instruction*); +bool HasConflictingMemberOffsets(const std::vector&, + const std::vector&); + +bool IsAllowedTypeOrArrayOfSame(ValidationState_t& _, const Instruction* type, + std::initializer_list allowed) { + if (std::find(allowed.begin(), allowed.end(), type->opcode()) != + allowed.end()) { + return true; + } + if (type->opcode() == SpvOpTypeArray || + type->opcode() == SpvOpTypeRuntimeArray) { + auto elem_type = _.FindDef(type->word(2)); + return std::find(allowed.begin(), allowed.end(), elem_type->opcode()) != + allowed.end(); + } + return false; +} + +// Returns true if the two instructions represent structs that, as far as the +// validator can tell, have the exact same data layout. +bool AreLayoutCompatibleStructs(ValidationState_t& _, const Instruction* type1, + const Instruction* type2) { + if (type1->opcode() != SpvOpTypeStruct) { + return false; + } + if (type2->opcode() != SpvOpTypeStruct) { + return false; + } + + if (!HaveLayoutCompatibleMembers(_, type1, type2)) return false; + + return HaveSameLayoutDecorations(_, type1, type2); +} + +// Returns true if the operands to the OpTypeStruct instruction defining the +// types are the same or are layout compatible types. |type1| and |type2| must +// be OpTypeStruct instructions. +bool HaveLayoutCompatibleMembers(ValidationState_t& _, const Instruction* type1, + const Instruction* type2) { + assert(type1->opcode() == SpvOpTypeStruct && + "type1 must be an OpTypeStruct instruction."); + assert(type2->opcode() == SpvOpTypeStruct && + "type2 must be an OpTypeStruct instruction."); + const auto& type1_operands = type1->operands(); + const auto& type2_operands = type2->operands(); + if (type1_operands.size() != type2_operands.size()) { + return false; + } + + for (size_t operand = 2; operand < type1_operands.size(); ++operand) { + if (type1->word(operand) != type2->word(operand)) { + auto def1 = _.FindDef(type1->word(operand)); + auto def2 = _.FindDef(type2->word(operand)); + if (!AreLayoutCompatibleStructs(_, def1, def2)) { + return false; + } + } + } + return true; +} + +// Returns true if all decorations that affect the data layout of the struct +// (like Offset), are the same for the two types. |type1| and |type2| must be +// OpTypeStruct instructions. +bool HaveSameLayoutDecorations(ValidationState_t& _, const Instruction* type1, + const Instruction* type2) { + assert(type1->opcode() == SpvOpTypeStruct && + "type1 must be an OpTypeStruct instruction."); + assert(type2->opcode() == SpvOpTypeStruct && + "type2 must be an OpTypeStruct instruction."); + const std::vector& type1_decorations = + _.id_decorations(type1->id()); + const std::vector& type2_decorations = + _.id_decorations(type2->id()); + + // TODO: Will have to add other check for arrays an matricies if we want to + // handle them. + if (HasConflictingMemberOffsets(type1_decorations, type2_decorations)) { + return false; + } + + return true; +} + +bool HasConflictingMemberOffsets( + const std::vector& type1_decorations, + const std::vector& type2_decorations) { + { + // We are interested in conflicting decoration. If a decoration is in one + // list but not the other, then we will assume the code is correct. We are + // looking for things we know to be wrong. + // + // We do not have to traverse type2_decoration because, after traversing + // type1_decorations, anything new will not be found in + // type1_decoration. Therefore, it cannot lead to a conflict. + for (const Decoration& decoration : type1_decorations) { + switch (decoration.dec_type()) { + case SpvDecorationOffset: { + // Since these affect the layout of the struct, they must be present + // in both structs. + auto compare = [&decoration](const Decoration& rhs) { + if (rhs.dec_type() != SpvDecorationOffset) return false; + return decoration.struct_member_index() == + rhs.struct_member_index(); + }; + auto i = std::find_if(type2_decorations.begin(), + type2_decorations.end(), compare); + if (i != type2_decorations.end() && + decoration.params().front() != i->params().front()) { + return true; + } + } break; + default: + // This decoration does not affect the layout of the structure, so + // just moving on. + break; + } + } + } + return false; +} + +// If |skip_builtin| is true, returns true if |storage| contains bool within +// it and no storage that contains the bool is builtin. +// If |skip_builtin| is false, returns true if |storage| contains bool within +// it. +bool ContainsInvalidBool(ValidationState_t& _, const Instruction* storage, + bool skip_builtin) { + if (skip_builtin) { + for (const Decoration& decoration : _.id_decorations(storage->id())) { + if (decoration.dec_type() == SpvDecorationBuiltIn) return false; + } + } + + const size_t elem_type_index = 1; + uint32_t elem_type_id; + Instruction* elem_type; + + switch (storage->opcode()) { + case SpvOpTypeBool: + return true; + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + elem_type_id = storage->GetOperandAs(elem_type_index); + elem_type = _.FindDef(elem_type_id); + return ContainsInvalidBool(_, elem_type, skip_builtin); + case SpvOpTypeStruct: + for (size_t member_type_index = 1; + member_type_index < storage->operands().size(); + ++member_type_index) { + auto member_type_id = + storage->GetOperandAs(member_type_index); + auto member_type = _.FindDef(member_type_id); + if (ContainsInvalidBool(_, member_type, skip_builtin)) return true; + } + default: + break; + } + return false; +} + +bool ContainsCooperativeMatrix(ValidationState_t& _, + const Instruction* storage) { + const size_t elem_type_index = 1; + uint32_t elem_type_id; + Instruction* elem_type; + + switch (storage->opcode()) { + case SpvOpTypeCooperativeMatrixNV: + return true; + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + elem_type_id = storage->GetOperandAs(elem_type_index); + elem_type = _.FindDef(elem_type_id); + return ContainsCooperativeMatrix(_, elem_type); + case SpvOpTypeStruct: + for (size_t member_type_index = 1; + member_type_index < storage->operands().size(); + ++member_type_index) { + auto member_type_id = + storage->GetOperandAs(member_type_index); + auto member_type = _.FindDef(member_type_id); + if (ContainsCooperativeMatrix(_, member_type)) return true; + } + break; + default: + break; + } + return false; +} + +std::pair GetStorageClass( + ValidationState_t& _, const Instruction* inst) { + SpvStorageClass dst_sc = SpvStorageClassMax; + SpvStorageClass src_sc = SpvStorageClassMax; + switch (inst->opcode()) { + case SpvOpCooperativeMatrixLoadNV: + case SpvOpLoad: { + auto load_pointer = _.FindDef(inst->GetOperandAs(2)); + auto load_pointer_type = _.FindDef(load_pointer->type_id()); + dst_sc = load_pointer_type->GetOperandAs(1); + break; + } + case SpvOpCooperativeMatrixStoreNV: + case SpvOpStore: { + auto store_pointer = _.FindDef(inst->GetOperandAs(0)); + auto store_pointer_type = _.FindDef(store_pointer->type_id()); + dst_sc = store_pointer_type->GetOperandAs(1); + break; + } + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: { + auto dst = _.FindDef(inst->GetOperandAs(0)); + auto dst_type = _.FindDef(dst->type_id()); + dst_sc = dst_type->GetOperandAs(1); + auto src = _.FindDef(inst->GetOperandAs(1)); + auto src_type = _.FindDef(src->type_id()); + src_sc = src_type->GetOperandAs(1); + break; + } + default: + break; + } + + return std::make_pair(dst_sc, src_sc); +} + +// Returns the number of instruction words taken up by a memory access +// argument and its implied operands. +int MemoryAccessNumWords(uint32_t mask) { + int result = 1; // Count the mask + if (mask & SpvMemoryAccessAlignedMask) ++result; + if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) ++result; + if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) ++result; + return result; +} + +// Returns the scope ID operand for MakeAvailable memory access with mask +// at the given operand index. +// This function is only called for OpLoad, OpStore, OpCopyMemory and +// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and +// OpCooperativeMatrixStoreNV. +uint32_t GetMakeAvailableScope(const Instruction* inst, uint32_t mask, + uint32_t mask_index) { + assert(mask & SpvMemoryAccessMakePointerAvailableKHRMask); + uint32_t this_bit = uint32_t(SpvMemoryAccessMakePointerAvailableKHRMask); + uint32_t index = + mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1))); + return inst->GetOperandAs(index); +} + +// This function is only called for OpLoad, OpStore, OpCopyMemory, +// OpCopyMemorySized, OpCooperativeMatrixLoadNV, and +// OpCooperativeMatrixStoreNV. +uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask, + uint32_t mask_index) { + assert(mask & SpvMemoryAccessMakePointerVisibleKHRMask); + uint32_t this_bit = uint32_t(SpvMemoryAccessMakePointerVisibleKHRMask); + uint32_t index = + mask_index - 1 + MemoryAccessNumWords(mask & (this_bit | (this_bit - 1))); + return inst->GetOperandAs(index); +} + +bool DoesStructContainRTA(const ValidationState_t& _, const Instruction* inst) { + for (size_t member_index = 1; member_index < inst->operands().size(); + ++member_index) { + const auto member_id = inst->GetOperandAs(member_index); + const auto member_type = _.FindDef(member_id); + if (member_type->opcode() == SpvOpTypeRuntimeArray) return true; + } + return false; +} + +spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst, + uint32_t index) { + SpvStorageClass dst_sc, src_sc; + std::tie(dst_sc, src_sc) = GetStorageClass(_, inst); + if (inst->operands().size() <= index) { + if (src_sc == SpvStorageClassPhysicalStorageBufferEXT || + dst_sc == SpvStorageClassPhysicalStorageBufferEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Memory accesses with PhysicalStorageBufferEXT must use " + "Aligned."; + } + return SPV_SUCCESS; + } + + const uint32_t mask = inst->GetOperandAs(index); + if (mask & SpvMemoryAccessMakePointerAvailableKHRMask) { + if (inst->opcode() == SpvOpLoad || + inst->opcode() == SpvOpCooperativeMatrixLoadNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MakePointerAvailableKHR cannot be used with OpLoad."; + } + + if (!(mask & SpvMemoryAccessNonPrivatePointerKHRMask)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NonPrivatePointerKHR must be specified if " + "MakePointerAvailableKHR is specified."; + } + + // Check the associated scope for MakeAvailableKHR. + const auto available_scope = GetMakeAvailableScope(inst, mask, index); + if (auto error = ValidateMemoryScope(_, inst, available_scope)) + return error; + } + + if (mask & SpvMemoryAccessMakePointerVisibleKHRMask) { + if (inst->opcode() == SpvOpStore || + inst->opcode() == SpvOpCooperativeMatrixStoreNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "MakePointerVisibleKHR cannot be used with OpStore."; + } + + if (!(mask & SpvMemoryAccessNonPrivatePointerKHRMask)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NonPrivatePointerKHR must be specified if " + << "MakePointerVisibleKHR is specified."; + } + + // Check the associated scope for MakeVisibleKHR. + const auto visible_scope = GetMakeVisibleScope(inst, mask, index); + if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error; + } + + if (mask & SpvMemoryAccessNonPrivatePointerKHRMask) { + if (dst_sc != SpvStorageClassUniform && + dst_sc != SpvStorageClassWorkgroup && + dst_sc != SpvStorageClassCrossWorkgroup && + dst_sc != SpvStorageClassGeneric && dst_sc != SpvStorageClassImage && + dst_sc != SpvStorageClassStorageBuffer && + dst_sc != SpvStorageClassPhysicalStorageBufferEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NonPrivatePointerKHR requires a pointer in Uniform, " + << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer " + << "storage classes."; + } + if (src_sc != SpvStorageClassMax && src_sc != SpvStorageClassUniform && + src_sc != SpvStorageClassWorkgroup && + src_sc != SpvStorageClassCrossWorkgroup && + src_sc != SpvStorageClassGeneric && src_sc != SpvStorageClassImage && + src_sc != SpvStorageClassStorageBuffer && + src_sc != SpvStorageClassPhysicalStorageBufferEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "NonPrivatePointerKHR requires a pointer in Uniform, " + << "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer " + << "storage classes."; + } + } + + if (!(mask & SpvMemoryAccessAlignedMask)) { + if (src_sc == SpvStorageClassPhysicalStorageBufferEXT || + dst_sc == SpvStorageClassPhysicalStorageBufferEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Memory accesses with PhysicalStorageBufferEXT must use " + "Aligned."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { + auto result_type = _.FindDef(inst->type_id()); + if (!result_type || result_type->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable Result Type '" << _.getIdName(inst->type_id()) + << "' is not a pointer type."; + } + + const auto initializer_index = 3; + const auto storage_class_index = 2; + if (initializer_index < inst->operands().size()) { + const auto initializer_id = inst->GetOperandAs(initializer_index); + const auto initializer = _.FindDef(initializer_id); + const auto is_module_scope_var = + initializer && (initializer->opcode() == SpvOpVariable) && + (initializer->GetOperandAs(storage_class_index) != + SpvStorageClassFunction); + const auto is_constant = + initializer && spvOpcodeIsConstant(initializer->opcode()); + if (!initializer || !(is_constant || is_module_scope_var)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable Initializer '" << _.getIdName(initializer_id) + << "' is not a constant or module-scope variable."; + } + if (initializer->type_id() != result_type->GetOperandAs(2u)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Initializer type must match the type pointed to by the Result " + "Type"; + } + } + + auto storage_class = inst->GetOperandAs(storage_class_index); + if (storage_class != SpvStorageClassWorkgroup && + storage_class != SpvStorageClassCrossWorkgroup && + storage_class != SpvStorageClassPrivate && + storage_class != SpvStorageClassFunction && + storage_class != SpvStorageClassRayPayloadNV && + storage_class != SpvStorageClassIncomingRayPayloadNV && + storage_class != SpvStorageClassHitAttributeNV && + storage_class != SpvStorageClassCallableDataNV && + storage_class != SpvStorageClassIncomingCallableDataNV) { + const auto storage_index = 2; + const auto storage_id = result_type->GetOperandAs(storage_index); + const auto storage = _.FindDef(storage_id); + bool storage_input_or_output = storage_class == SpvStorageClassInput || + storage_class == SpvStorageClassOutput; + bool builtin = false; + if (storage_input_or_output) { + for (const Decoration& decoration : _.id_decorations(inst->id())) { + if (decoration.dec_type() == SpvDecorationBuiltIn) { + builtin = true; + break; + } + } + } + if (!(storage_input_or_output && builtin) && + ContainsInvalidBool(_, storage, storage_input_or_output)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "If OpTypeBool is stored in conjunction with OpVariable, it " + << "can only be used with non-externally visible shader Storage " + << "Classes: Workgroup, CrossWorkgroup, Private, and Function"; + } + } + + if (!_.IsValidStorageClass(storage_class)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Invalid storage class for target environment"; + } + + if (storage_class == SpvStorageClassGeneric) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "OpVariable storage class cannot be Generic"; + } + + if (inst->function() && storage_class != SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Variables must have a function[7] storage class inside" + " of a function"; + } + + if (!inst->function() && storage_class == SpvStorageClassFunction) { + return _.diag(SPV_ERROR_INVALID_LAYOUT, inst) + << "Variables can not have a function[7] storage class " + "outside of a function"; + } + + // SPIR-V 3.32.8: Check that pointer type and variable type have the same + // storage class. + const auto result_storage_class_index = 1; + const auto result_storage_class = + result_type->GetOperandAs(result_storage_class_index); + if (storage_class != result_storage_class) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "From SPIR-V spec, section 3.32.8 on OpVariable:\n" + << "Its Storage Class operand must be the same as the Storage Class " + << "operand of the result type."; + } + + // Variable pointer related restrictions. + const auto pointee = _.FindDef(result_type->word(3)); + if (_.addressing_model() == SpvAddressingModelLogical && + !_.options()->relax_logical_pointer) { + // VariablePointersStorageBuffer is implied by VariablePointers. + if (pointee->opcode() == SpvOpTypePointer) { + if (!_.HasCapability(SpvCapabilityVariablePointersStorageBuffer)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing, variables may not allocate a pointer " + << "type"; + } else if (storage_class != SpvStorageClassFunction && + storage_class != SpvStorageClassPrivate) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In Logical addressing with variable pointers, variables " + << "that allocate pointers must be in Function or Private " + << "storage classes"; + } + } + } + + // Vulkan 14.5.1: Check type of PushConstant variables. + // Vulkan 14.5.2: Check type of UniformConstant and Uniform variables. + if (spvIsVulkanEnv(_.context()->target_env)) { + if (storage_class == SpvStorageClassPushConstant) { + if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "PushConstant OpVariable '" << _.getIdName(inst->id()) + << "' has illegal type.\n" + << "From Vulkan spec, section 14.5.1:\n" + << "Such variables must be typed as OpTypeStruct, " + << "or an array of this type"; + } + } + + if (storage_class == SpvStorageClassUniformConstant) { + if (!IsAllowedTypeOrArrayOfSame( + _, pointee, + {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage, + SpvOpTypeAccelerationStructureNV, + SpvOpTypeAccelerationStructureKHR, + SpvOpTypeRayQueryProvisionalKHR})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "UniformConstant OpVariable '" << _.getIdName(inst->id()) + << "' has illegal type.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "Variables identified with the UniformConstant storage class " + << "are used only as handles to refer to opaque resources. Such " + << "variables must be typed as OpTypeImage, OpTypeSampler, " + << "OpTypeSampledImage, OpTypeAccelerationStructureNV, " + "OpTypeAccelerationStructureKHR, " + "OpTypeRayQueryProvisionalKHR, " + << "or an array of one of these types."; + } + } + + if (storage_class == SpvStorageClassUniform) { + if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Uniform OpVariable '" << _.getIdName(inst->id()) + << "' has illegal type.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "Variables identified with the Uniform storage class are " + << "used to access transparent buffer backed resources. Such " + << "variables must be typed as OpTypeStruct, or an array of " + << "this type"; + } + } + + if (storage_class == SpvStorageClassStorageBuffer) { + if (!IsAllowedTypeOrArrayOfSame(_, pointee, {SpvOpTypeStruct})) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "StorageBuffer OpVariable '" << _.getIdName(inst->id()) + << "' has illegal type.\n" + << "From Vulkan spec, section 14.5.2:\n" + << "Variables identified with the StorageBuffer storage class " + "are used to access transparent buffer backed resources. " + "Such variables must be typed as OpTypeStruct, or an array " + "of this type"; + } + } + } + + // WebGPU & Vulkan Appendix A: Check that if contains initializer, then + // storage class is Output, Private, or Function. + if (inst->operands().size() > 3 && storage_class != SpvStorageClassOutput && + storage_class != SpvStorageClassPrivate && + storage_class != SpvStorageClassFunction) { + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, '" << _.getIdName(inst->id()) + << "', has a disallowed initializer & storage class " + << "combination.\n" + << "From " << spvLogStringForEnv(_.context()->target_env) + << " spec:\n" + << "Variable declarations that include initializers must have " + << "one of the following storage classes: Output, Private, or " + << "Function"; + } + } + + // WebGPU: All variables with storage class Output, Private, or Function MUST + // have an initializer. + if (spvIsWebGPUEnv(_.context()->target_env) && inst->operands().size() <= 3 && + (storage_class == SpvStorageClassOutput || + storage_class == SpvStorageClassPrivate || + storage_class == SpvStorageClassFunction)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, '" << _.getIdName(inst->id()) + << "', must have an initializer.\n" + << "From WebGPU execution environment spec:\n" + << "All variables in the following storage classes must have an " + << "initializer: Output, Private, or Function"; + } + + if (storage_class == SpvStorageClassPhysicalStorageBufferEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "PhysicalStorageBufferEXT must not be used with OpVariable."; + } + + auto pointee_base = pointee; + while (pointee_base->opcode() == SpvOpTypeArray) { + pointee_base = _.FindDef(pointee_base->GetOperandAs(1u)); + } + if (pointee_base->opcode() == SpvOpTypePointer) { + if (pointee_base->GetOperandAs(1u) == + SpvStorageClassPhysicalStorageBufferEXT) { + // check for AliasedPointerEXT/RestrictPointerEXT + bool foundAliased = + _.HasDecoration(inst->id(), SpvDecorationAliasedPointerEXT); + bool foundRestrict = + _.HasDecoration(inst->id(), SpvDecorationRestrictPointerEXT); + if (!foundAliased && !foundRestrict) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable " << inst->id() + << ": expected AliasedPointerEXT or RestrictPointerEXT for " + << "PhysicalStorageBufferEXT pointer."; + } + if (foundAliased && foundRestrict) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable " << inst->id() + << ": can't specify both AliasedPointerEXT and " + << "RestrictPointerEXT for PhysicalStorageBufferEXT pointer."; + } + } + } + + // Vulkan specific validation rules for OpTypeRuntimeArray + const auto type_index = 2; + const auto value_id = result_type->GetOperandAs(type_index); + auto value_type = _.FindDef(value_id); + if (spvIsVulkanEnv(_.context()->target_env)) { + // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct, + // so should never appear as a bare variable. + // Unless the module has the RuntimeDescriptorArrayEXT capability. + if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) { + if (!_.HasCapability(SpvCapabilityRuntimeDescriptorArrayEXT)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, '" << _.getIdName(inst->id()) + << "', is attempting to create memory for an illegal type, " + << "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only " + << "appear as the final member of an OpTypeStruct, thus cannot " + << "be instantiated via OpVariable"; + } else { + // A bare variable OpTypeRuntimeArray is allowed in this context, but + // still need to check the storage class. + if (storage_class != SpvStorageClassStorageBuffer && + storage_class != SpvStorageClassUniform && + storage_class != SpvStorageClassUniformConstant) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For Vulkan with RuntimeDescriptorArrayEXT, a variable " + << "containing OpTypeRuntimeArray must have storage class of " + << "StorageBuffer, Uniform, or UniformConstant."; + } + } + } + + // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it + // must either have the storage class StorageBuffer and be decorated + // with Block, or it must be in the Uniform storage class and be decorated + // as BufferBlock. + if (value_type && value_type->opcode() == SpvOpTypeStruct) { + if (DoesStructContainRTA(_, value_type)) { + if (storage_class == SpvStorageClassStorageBuffer) { + if (!_.HasDecoration(value_id, SpvDecorationBlock)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For Vulkan, an OpTypeStruct variable containing an " + << "OpTypeRuntimeArray must be decorated with Block if it " + << "has storage class StorageBuffer."; + } + } else if (storage_class == SpvStorageClassUniform) { + if (!_.HasDecoration(value_id, SpvDecorationBufferBlock)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For Vulkan, an OpTypeStruct variable containing an " + << "OpTypeRuntimeArray must be decorated with BufferBlock " + << "if it has storage class Uniform."; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For Vulkan, OpTypeStruct variables containing " + << "OpTypeRuntimeArray must have storage class of " + << "StorageBuffer or Uniform."; + } + } + } + } + + // WebGPU specific validation rules for OpTypeRuntimeArray + if (spvIsWebGPUEnv(_.context()->target_env)) { + // OpTypeRuntimeArray should only ever be in an OpTypeStruct, + // so should never appear as a bare variable. + if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpVariable, '" << _.getIdName(inst->id()) + << "', is attempting to create memory for an illegal type, " + << "OpTypeRuntimeArray.\nFor WebGPU OpTypeRuntimeArray can only " + << "appear as the final member of an OpTypeStruct, thus cannot " + << "be instantiated via OpVariable"; + } + + // If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it + // must have the storage class StorageBuffer and be decorated + // with Block. + if (value_type && value_type->opcode() == SpvOpTypeStruct) { + if (DoesStructContainRTA(_, value_type)) { + if (storage_class == SpvStorageClassStorageBuffer) { + if (!_.HasDecoration(value_id, SpvDecorationBlock)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For WebGPU, an OpTypeStruct variable containing an " + << "OpTypeRuntimeArray must be decorated with Block if it " + << "has storage class StorageBuffer."; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For WebGPU, OpTypeStruct variables containing " + << "OpTypeRuntimeArray must have storage class of " + << "StorageBuffer"; + } + } + } + } + + // Cooperative matrix types can only be allocated in Function or Private + if ((storage_class != SpvStorageClassFunction && + storage_class != SpvStorageClassPrivate) && + ContainsCooperativeMatrix(_, pointee)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cooperative matrix types (or types containing them) can only be " + "allocated " + << "in Function or Private storage classes or as function " + "parameters"; + } + + if (_.HasCapability(SpvCapabilityShader)) { + // Don't allow variables containing 16-bit elements without the appropriate + // capabilities. + if ((!_.HasCapability(SpvCapabilityInt16) && + _.ContainsSizedIntOrFloatType(value_id, SpvOpTypeInt, 16)) || + (!_.HasCapability(SpvCapabilityFloat16) && + _.ContainsSizedIntOrFloatType(value_id, SpvOpTypeFloat, 16))) { + auto underlying_type = value_type; + while (underlying_type->opcode() == SpvOpTypePointer) { + storage_class = underlying_type->GetOperandAs(1u); + underlying_type = + _.FindDef(underlying_type->GetOperandAs(2u)); + } + bool storage_class_ok = true; + std::string sc_name = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class); + switch (storage_class) { + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBufferEXT: + if (!_.HasCapability(SpvCapabilityStorageBuffer16BitAccess)) { + storage_class_ok = false; + } + break; + case SpvStorageClassUniform: + if (!_.HasCapability( + SpvCapabilityUniformAndStorageBuffer16BitAccess)) { + if (underlying_type->opcode() == SpvOpTypeArray || + underlying_type->opcode() == SpvOpTypeRuntimeArray) { + underlying_type = + _.FindDef(underlying_type->GetOperandAs(1u)); + } + if (!_.HasCapability(SpvCapabilityStorageBuffer16BitAccess) || + !_.HasDecoration(underlying_type->id(), + SpvDecorationBufferBlock)) { + storage_class_ok = false; + } + } + break; + case SpvStorageClassPushConstant: + if (!_.HasCapability(SpvCapabilityStoragePushConstant16)) { + storage_class_ok = false; + } + break; + case SpvStorageClassInput: + case SpvStorageClassOutput: + if (!_.HasCapability(SpvCapabilityStorageInputOutput16)) { + storage_class_ok = false; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot allocate a variable containing a 16-bit type in " + << sc_name << " storage class"; + } + if (!storage_class_ok) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Allocating a variable containing a 16-bit element in " + << sc_name << " storage class requires an additional capability"; + } + } + // Don't allow variables containing 8-bit elements without the appropriate + // capabilities. + if (!_.HasCapability(SpvCapabilityInt8) && + _.ContainsSizedIntOrFloatType(value_id, SpvOpTypeInt, 8)) { + auto underlying_type = value_type; + while (underlying_type->opcode() == SpvOpTypePointer) { + storage_class = underlying_type->GetOperandAs(1u); + underlying_type = + _.FindDef(underlying_type->GetOperandAs(2u)); + } + bool storage_class_ok = true; + std::string sc_name = _.grammar().lookupOperandName( + SPV_OPERAND_TYPE_STORAGE_CLASS, storage_class); + switch (storage_class) { + case SpvStorageClassStorageBuffer: + case SpvStorageClassPhysicalStorageBufferEXT: + if (!_.HasCapability(SpvCapabilityStorageBuffer8BitAccess)) { + storage_class_ok = false; + } + break; + case SpvStorageClassUniform: + if (!_.HasCapability( + SpvCapabilityUniformAndStorageBuffer8BitAccess)) { + if (underlying_type->opcode() == SpvOpTypeArray || + underlying_type->opcode() == SpvOpTypeRuntimeArray) { + underlying_type = + _.FindDef(underlying_type->GetOperandAs(1u)); + } + if (!_.HasCapability(SpvCapabilityStorageBuffer8BitAccess) || + !_.HasDecoration(underlying_type->id(), + SpvDecorationBufferBlock)) { + storage_class_ok = false; + } + } + break; + case SpvStorageClassPushConstant: + if (!_.HasCapability(SpvCapabilityStoragePushConstant8)) { + storage_class_ok = false; + } + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot allocate a variable containing a 8-bit type in " + << sc_name << " storage class"; + } + if (!storage_class_ok) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Allocating a variable containing a 8-bit element in " + << sc_name << " storage class requires an additional capability"; + } + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateLoad(ValidationState_t& _, const Instruction* inst) { + const auto result_type = _.FindDef(inst->type_id()); + if (!result_type) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLoad Result Type '" << _.getIdName(inst->type_id()) + << "' is not defined."; + } + + const bool uses_variable_pointers = + _.features().variable_pointers || + _.features().variable_pointers_storage_buffer; + const auto pointer_index = 2; + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + ((_.addressing_model() == SpvAddressingModelLogical) && + ((!uses_variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (uses_variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLoad Pointer '" << _.getIdName(pointer_id) + << "' is not a logical pointer."; + } + + const auto pointer_type = _.FindDef(pointer->type_id()); + if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLoad type for pointer '" << _.getIdName(pointer_id) + << "' is not a pointer type."; + } + + const auto pointee_type = _.FindDef(pointer_type->GetOperandAs(2)); + if (!pointee_type || result_type->id() != pointee_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpLoad Result Type '" << _.getIdName(inst->type_id()) + << "' does not match Pointer '" << _.getIdName(pointer->id()) + << "'s type."; + } + + if (auto error = CheckMemoryAccess(_, inst, 3)) return error; + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id()) && + result_type->opcode() != SpvOpTypePointer) { + if (result_type->opcode() != SpvOpTypeInt && + result_type->opcode() != SpvOpTypeFloat && + result_type->opcode() != SpvOpTypeVector && + result_type->opcode() != SpvOpTypeMatrix) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "8- or 16-bit loads must be a scalar, vector or matrix type"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateStore(ValidationState_t& _, const Instruction* inst) { + const bool uses_variable_pointer = + _.features().variable_pointers || + _.features().variable_pointers_storage_buffer; + const auto pointer_index = 0; + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + (_.addressing_model() == SpvAddressingModelLogical && + ((!uses_variable_pointer && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (uses_variable_pointer && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer '" << _.getIdName(pointer_id) + << "' is not a logical pointer."; + } + const auto pointer_type = _.FindDef(pointer->type_id()); + if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore type for pointer '" << _.getIdName(pointer_id) + << "' is not a pointer type."; + } + const auto type_id = pointer_type->GetOperandAs(2); + const auto type = _.FindDef(type_id); + if (!type || SpvOpTypeVoid == type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer '" << _.getIdName(pointer_id) + << "'s type is void."; + } + + // validate storage class + { + uint32_t data_type; + uint32_t storage_class; + if (!_.GetPointerTypeInfo(pointer_type->id(), &data_type, &storage_class)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer '" << _.getIdName(pointer_id) + << "' is not pointer type"; + } + + if (storage_class == SpvStorageClassUniformConstant || + storage_class == SpvStorageClassInput || + storage_class == SpvStorageClassPushConstant) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer '" << _.getIdName(pointer_id) + << "' storage class is read-only"; + } + + if (spvIsVulkanEnv(_.context()->target_env) && + storage_class == SpvStorageClassUniform) { + auto base_ptr = _.TracePointer(pointer); + if (base_ptr->opcode() == SpvOpVariable) { + // If it's not a variable a different check should catch the problem. + auto base_type = _.FindDef(base_ptr->GetOperandAs(0)); + // Get the pointed-to type. + base_type = _.FindDef(base_type->GetOperandAs(2u)); + if (base_type->opcode() == SpvOpTypeArray || + base_type->opcode() == SpvOpTypeRuntimeArray) { + base_type = _.FindDef(base_type->GetOperandAs(1u)); + } + if (_.HasDecoration(base_type->id(), SpvDecorationBlock)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In the Vulkan environment, cannot store to Uniform Blocks"; + } + } + } + } + + const auto object_index = 1; + const auto object_id = inst->GetOperandAs(object_index); + const auto object = _.FindDef(object_id); + if (!object || !object->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Object '" << _.getIdName(object_id) + << "' is not an object."; + } + const auto object_type = _.FindDef(object->type_id()); + if (!object_type || SpvOpTypeVoid == object_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Object '" << _.getIdName(object_id) + << "'s type is void."; + } + + if (type->id() != object_type->id()) { + if (!_.options()->relax_struct_store || type->opcode() != SpvOpTypeStruct || + object_type->opcode() != SpvOpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer '" << _.getIdName(pointer_id) + << "'s type does not match Object '" + << _.getIdName(object->id()) << "'s type."; + } + + // TODO: Check for layout compatible matricies and arrays as well. + if (!AreLayoutCompatibleStructs(_, type, object_type)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpStore Pointer '" << _.getIdName(pointer_id) + << "'s layout does not match Object '" + << _.getIdName(object->id()) << "'s layout."; + } + } + + if (auto error = CheckMemoryAccess(_, inst, 2)) return error; + + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id()) && + object_type->opcode() != SpvOpTypePointer) { + if (object_type->opcode() != SpvOpTypeInt && + object_type->opcode() != SpvOpTypeFloat && + object_type->opcode() != SpvOpTypeVector && + object_type->opcode() != SpvOpTypeMatrix) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "8- or 16-bit stores must be a scalar, vector or matrix type"; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateCopyMemoryMemoryAccess(ValidationState_t& _, + const Instruction* inst) { + assert(inst->opcode() == SpvOpCopyMemory || + inst->opcode() == SpvOpCopyMemorySized); + const uint32_t first_access_index = inst->opcode() == SpvOpCopyMemory ? 2 : 3; + if (inst->operands().size() > first_access_index) { + if (auto error = CheckMemoryAccess(_, inst, first_access_index)) + return error; + + const auto first_access = inst->GetOperandAs(first_access_index); + const uint32_t second_access_index = + first_access_index + MemoryAccessNumWords(first_access); + if (inst->operands().size() > second_access_index) { + if (_.features().copy_memory_permits_two_memory_accesses) { + if (auto error = CheckMemoryAccess(_, inst, second_access_index)) + return error; + + // In the two-access form in SPIR-V 1.4 and later: + // - the first is the target (write) access and it can't have + // make-visible. + // - the second is the source (read) access and it can't have + // make-available. + if (first_access & SpvMemoryAccessMakePointerVisibleKHRMask) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Target memory access must not include " + "MakePointerVisibleKHR"; + } + const auto second_access = + inst->GetOperandAs(second_access_index); + if (second_access & SpvMemoryAccessMakePointerAvailableKHRMask) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Source memory access must not include " + "MakePointerAvailableKHR"; + } + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(static_cast(inst->opcode())) + << " with two memory access operands requires SPIR-V 1.4 or " + "later"; + } + } + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCopyMemory(ValidationState_t& _, const Instruction* inst) { + const auto target_index = 0; + const auto target_id = inst->GetOperandAs(target_index); + const auto target = _.FindDef(target_id); + if (!target) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Target operand '" << _.getIdName(target_id) + << "' is not defined."; + } + + const auto source_index = 1; + const auto source_id = inst->GetOperandAs(source_index); + const auto source = _.FindDef(source_id); + if (!source) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Source operand '" << _.getIdName(source_id) + << "' is not defined."; + } + + const auto target_pointer_type = _.FindDef(target->type_id()); + if (!target_pointer_type || + target_pointer_type->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Target operand '" << _.getIdName(target_id) + << "' is not a pointer."; + } + + const auto source_pointer_type = _.FindDef(source->type_id()); + if (!source_pointer_type || + source_pointer_type->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Source operand '" << _.getIdName(source_id) + << "' is not a pointer."; + } + + if (inst->opcode() == SpvOpCopyMemory) { + const auto target_type = + _.FindDef(target_pointer_type->GetOperandAs(2)); + if (!target_type || target_type->opcode() == SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Target operand '" << _.getIdName(target_id) + << "' cannot be a void pointer."; + } + + const auto source_type = + _.FindDef(source_pointer_type->GetOperandAs(2)); + if (!source_type || source_type->opcode() == SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Source operand '" << _.getIdName(source_id) + << "' cannot be a void pointer."; + } + + if (target_type->id() != source_type->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Target '" << _.getIdName(source_id) + << "'s type does not match Source '" + << _.getIdName(source_type->id()) << "'s type."; + } + + if (auto error = CheckMemoryAccess(_, inst, 2)) return error; + } else { + const auto size_id = inst->GetOperandAs(2); + const auto size = _.FindDef(size_id); + if (!size) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand '" << _.getIdName(size_id) + << "' is not defined."; + } + + const auto size_type = _.FindDef(size->type_id()); + if (!_.IsIntScalarType(size_type->id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand '" << _.getIdName(size_id) + << "' must be a scalar integer type."; + } + + bool is_zero = true; + switch (size->opcode()) { + case SpvOpConstantNull: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand '" << _.getIdName(size_id) + << "' cannot be a constant zero."; + case SpvOpConstant: + if (size_type->word(3) == 1 && + size->word(size->words().size() - 1) & 0x80000000) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand '" << _.getIdName(size_id) + << "' cannot have the sign bit set to 1."; + } + for (size_t i = 3; is_zero && i < size->words().size(); ++i) { + is_zero &= (size->word(i) == 0); + } + if (is_zero) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Size operand '" << _.getIdName(size_id) + << "' cannot be a constant zero."; + } + break; + default: + // Cannot infer any other opcodes. + break; + } + + if (auto error = CheckMemoryAccess(_, inst, 3)) return error; + } + if (auto error = ValidateCopyMemoryMemoryAccess(_, inst)) return error; + + // Get past the pointers to avoid checking a pointer copy. + auto sub_type = _.FindDef(target_pointer_type->GetOperandAs(2)); + while (sub_type->opcode() == SpvOpTypePointer) { + sub_type = _.FindDef(sub_type->GetOperandAs(2)); + } + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(sub_type->id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot copy memory of objects containing 8- or 16-bit types"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateAccessChain(ValidationState_t& _, + const Instruction* inst) { + std::string instr_name = + "Op" + std::string(spvOpcodeString(static_cast(inst->opcode()))); + + // The result type must be OpTypePointer. + auto result_type = _.FindDef(inst->type_id()); + if (SpvOpTypePointer != result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of " << instr_name << " '" + << _.getIdName(inst->id()) << "' must be OpTypePointer. Found Op" + << spvOpcodeString(static_cast(result_type->opcode())) << "."; + } + + // Result type is a pointer. Find out what it's pointing to. + // This will be used to make sure the indexing results in the same type. + // OpTypePointer word 3 is the type being pointed to. + const auto result_type_pointee = _.FindDef(result_type->word(3)); + + // Base must be a pointer, pointing to the base of a composite object. + const auto base_index = 2; + const auto base_id = inst->GetOperandAs(base_index); + const auto base = _.FindDef(base_id); + const auto base_type = _.FindDef(base->type_id()); + if (!base_type || SpvOpTypePointer != base_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The Base '" << _.getIdName(base_id) << "' in " << instr_name + << " instruction must be a pointer."; + } + + // The result pointer storage class and base pointer storage class must match. + // Word 2 of OpTypePointer is the Storage Class. + auto result_type_storage_class = result_type->word(2); + auto base_type_storage_class = base_type->word(2); + if (result_type_storage_class != base_type_storage_class) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The result pointer storage class and base " + "pointer storage class in " + << instr_name << " do not match."; + } + + // The type pointed to by OpTypePointer (word 3) must be a composite type. + auto type_pointee = _.FindDef(base_type->word(3)); + + // Check Universal Limit (SPIR-V Spec. Section 2.17). + // The number of indexes passed to OpAccessChain may not exceed 255 + // The instruction includes 4 words + N words (for N indexes) + size_t num_indexes = inst->words().size() - 4; + if (inst->opcode() == SpvOpPtrAccessChain || + inst->opcode() == SpvOpInBoundsPtrAccessChain) { + // In pointer access chains, the element operand is required, but not + // counted as an index. + --num_indexes; + } + const size_t num_indexes_limit = + _.options()->universal_limits_.max_access_chain_indexes; + if (num_indexes > num_indexes_limit) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The number of indexes in " << instr_name << " may not exceed " + << num_indexes_limit << ". Found " << num_indexes << " indexes."; + } + // Indexes walk the type hierarchy to the desired depth, potentially down to + // scalar granularity. The first index in Indexes will select the top-level + // member/element/component/element of the base composite. All composite + // constituents use zero-based numbering, as described by their OpType... + // instruction. The second index will apply similarly to that result, and so + // on. Once any non-composite type is reached, there must be no remaining + // (unused) indexes. + auto starting_index = 4; + if (inst->opcode() == SpvOpPtrAccessChain || + inst->opcode() == SpvOpInBoundsPtrAccessChain) { + ++starting_index; + } + for (size_t i = starting_index; i < inst->words().size(); ++i) { + const uint32_t cur_word = inst->words()[i]; + // Earlier ID checks ensure that cur_word definition exists. + auto cur_word_instr = _.FindDef(cur_word); + // The index must be a scalar integer type (See OpAccessChain in the Spec.) + auto index_type = _.FindDef(cur_word_instr->type_id()); + if (!index_type || SpvOpTypeInt != index_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Indexes passed to " << instr_name + << " must be of type integer."; + } + switch (type_pointee->opcode()) { + case SpvOpTypeMatrix: + case SpvOpTypeVector: + case SpvOpTypeCooperativeMatrixNV: + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: { + // In OpTypeMatrix, OpTypeVector, SpvOpTypeCooperativeMatrixNV, + // OpTypeArray, and OpTypeRuntimeArray, word 2 is the Element Type. + type_pointee = _.FindDef(type_pointee->word(2)); + break; + } + case SpvOpTypeStruct: { + // In case of structures, there is an additional constraint on the + // index: the index must be an OpConstant. + if (SpvOpConstant != cur_word_instr->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr) + << "The passed to " << instr_name + << " to index into a " + "structure must be an OpConstant."; + } + // Get the index value from the OpConstant (word 3 of OpConstant). + // OpConstant could be a signed integer. But it's okay to treat it as + // unsigned because a negative constant int would never be seen as + // correct as a struct offset, since structs can't have more than 2 + // billion members. + const uint32_t cur_index = cur_word_instr->word(3); + // The index points to the struct member we want, therefore, the index + // should be less than the number of struct members. + const uint32_t num_struct_members = + static_cast(type_pointee->words().size() - 2); + if (cur_index >= num_struct_members) { + return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr) + << "Index is out of bounds: " << instr_name + << " can not find index " << cur_index + << " into the structure '" + << _.getIdName(type_pointee->id()) << "'. This structure has " + << num_struct_members << " members. Largest valid index is " + << num_struct_members - 1 << "."; + } + // Struct members IDs start at word 2 of OpTypeStruct. + auto structMemberId = type_pointee->word(cur_index + 2); + type_pointee = _.FindDef(structMemberId); + break; + } + default: { + // Give an error. reached non-composite type while indexes still remain. + return _.diag(SPV_ERROR_INVALID_ID, cur_word_instr) + << instr_name + << " reached non-composite type while indexes " + "still remain to be traversed."; + } + } + } + // At this point, we have fully walked down from the base using the indeces. + // The type being pointed to should be the same as the result type. + if (type_pointee->id() != result_type_pointee->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << instr_name << " result type (Op" + << spvOpcodeString(static_cast(result_type_pointee->opcode())) + << ") does not match the type that results from indexing into the " + "base " + " (Op" + << spvOpcodeString(static_cast(type_pointee->opcode())) + << ")."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidatePtrAccessChain(ValidationState_t& _, + const Instruction* inst) { + if (_.addressing_model() == SpvAddressingModelLogical) { + if (!_.features().variable_pointers && + !_.features().variable_pointers_storage_buffer) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Generating variable pointers requires capability " + << "VariablePointers or VariablePointersStorageBuffer"; + } + } + return ValidateAccessChain(_, inst); +} + +spv_result_t ValidateArrayLength(ValidationState_t& state, + const Instruction* inst) { + std::string instr_name = + "Op" + std::string(spvOpcodeString(static_cast(inst->opcode()))); + + // Result type must be a 32-bit unsigned int. + auto result_type = state.FindDef(inst->type_id()); + if (result_type->opcode() != SpvOpTypeInt || + result_type->GetOperandAs(1) != 32 || + result_type->GetOperandAs(2) != 0) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of " << instr_name << " '" + << state.getIdName(inst->id()) + << "' must be OpTypeInt with width 32 and signedness 0."; + } + + // The structure that is passed in must be an pointer to a structure, whose + // last element is a runtime array. + auto pointer = state.FindDef(inst->GetOperandAs(2)); + auto pointer_type = state.FindDef(pointer->type_id()); + if (pointer_type->opcode() != SpvOpTypePointer) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Struture's type in " << instr_name << " '" + << state.getIdName(inst->id()) + << "' must be a pointer to an OpTypeStruct."; + } + + auto structure_type = state.FindDef(pointer_type->GetOperandAs(2)); + if (structure_type->opcode() != SpvOpTypeStruct) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Struture's type in " << instr_name << " '" + << state.getIdName(inst->id()) + << "' must be a pointer to an OpTypeStruct."; + } + + auto num_of_members = structure_type->operands().size() - 1; + auto last_member = + state.FindDef(structure_type->GetOperandAs(num_of_members)); + if (last_member->opcode() != SpvOpTypeRuntimeArray) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Struture's last member in " << instr_name << " '" + << state.getIdName(inst->id()) << "' must be an OpTypeRuntimeArray."; + } + + // The array member must the the index of the last element (the run time + // array). + if (inst->GetOperandAs(3) != num_of_members - 1) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The array member in " << instr_name << " '" + << state.getIdName(inst->id()) + << "' must be an the last member of the struct."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixLengthNV(ValidationState_t& state, + const Instruction* inst) { + std::string instr_name = + "Op" + std::string(spvOpcodeString(static_cast(inst->opcode()))); + + // Result type must be a 32-bit unsigned int. + auto result_type = state.FindDef(inst->type_id()); + if (result_type->opcode() != SpvOpTypeInt || + result_type->GetOperandAs(1) != 32 || + result_type->GetOperandAs(2) != 0) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The Result Type of " << instr_name << " '" + << state.getIdName(inst->id()) + << "' must be OpTypeInt with width 32 and signedness 0."; + } + + auto type_id = inst->GetOperandAs(2); + auto type = state.FindDef(type_id); + if (type->opcode() != SpvOpTypeCooperativeMatrixNV) { + return state.diag(SPV_ERROR_INVALID_ID, inst) + << "The type in " << instr_name << " '" + << state.getIdName(type_id) + << "' must be OpTypeCooperativeMatrixNV."; + } + return SPV_SUCCESS; +} + +spv_result_t ValidateCooperativeMatrixLoadStoreNV(ValidationState_t& _, + const Instruction* inst) { + uint32_t type_id; + const char* opname; + if (inst->opcode() == SpvOpCooperativeMatrixLoadNV) { + type_id = inst->type_id(); + opname = "SpvOpCooperativeMatrixLoadNV"; + } else { + // get Object operand's type + type_id = _.FindDef(inst->GetOperandAs(1))->type_id(); + opname = "SpvOpCooperativeMatrixStoreNV"; + } + + auto matrix_type = _.FindDef(type_id); + + if (matrix_type->opcode() != SpvOpTypeCooperativeMatrixNV) { + if (inst->opcode() == SpvOpCooperativeMatrixLoadNV) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "SpvOpCooperativeMatrixLoadNV Result Type '" + << _.getIdName(type_id) << "' is not a cooperative matrix type."; + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "SpvOpCooperativeMatrixStoreNV Object type '" + << _.getIdName(type_id) << "' is not a cooperative matrix type."; + } + } + + const bool uses_variable_pointers = + _.features().variable_pointers || + _.features().variable_pointers_storage_buffer; + const auto pointer_index = + (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 2u : 0u; + const auto pointer_id = inst->GetOperandAs(pointer_index); + const auto pointer = _.FindDef(pointer_id); + if (!pointer || + ((_.addressing_model() == SpvAddressingModelLogical) && + ((!uses_variable_pointers && + !spvOpcodeReturnsLogicalPointer(pointer->opcode())) || + (uses_variable_pointers && + !spvOpcodeReturnsLogicalVariablePointer(pointer->opcode()))))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer '" << _.getIdName(pointer_id) + << "' is not a logical pointer."; + } + + const auto pointer_type_id = pointer->type_id(); + const auto pointer_type = _.FindDef(pointer_type_id); + if (!pointer_type || pointer_type->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " type for pointer '" << _.getIdName(pointer_id) + << "' is not a pointer type."; + } + + const auto storage_class_index = 1u; + const auto storage_class = + pointer_type->GetOperandAs(storage_class_index); + + if (storage_class != SpvStorageClassWorkgroup && + storage_class != SpvStorageClassStorageBuffer && + storage_class != SpvStorageClassPhysicalStorageBufferEXT) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " storage class for pointer type '" + << _.getIdName(pointer_type_id) + << "' is not Workgroup or StorageBuffer."; + } + + const auto pointee_id = pointer_type->GetOperandAs(2); + const auto pointee_type = _.FindDef(pointee_id); + if (!pointee_type || !(_.IsIntScalarOrVectorType(pointee_id) || + _.IsFloatScalarOrVectorType(pointee_id))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << opname << " Pointer '" << _.getIdName(pointer->id()) + << "'s Type must be a scalar or vector type."; + } + + const auto stride_index = + (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 3u : 2u; + const auto stride_id = inst->GetOperandAs(stride_index); + const auto stride = _.FindDef(stride_id); + if (!stride || !_.IsIntScalarType(stride->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Stride operand '" << _.getIdName(stride_id) + << "' must be a scalar integer type."; + } + + const auto colmajor_index = + (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 4u : 3u; + const auto colmajor_id = inst->GetOperandAs(colmajor_index); + const auto colmajor = _.FindDef(colmajor_id); + if (!colmajor || !_.IsBoolScalarType(colmajor->type_id()) || + !(spvOpcodeIsConstant(colmajor->opcode()) || + spvOpcodeIsSpecConstant(colmajor->opcode()))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Column Major operand '" << _.getIdName(colmajor_id) + << "' must be a boolean constant instruction."; + } + + const auto memory_access_index = + (inst->opcode() == SpvOpCooperativeMatrixLoadNV) ? 5u : 4u; + if (inst->operands().size() > memory_access_index) { + if (auto error = CheckMemoryAccess(_, inst, memory_access_index)) + return error; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidatePtrComparison(ValidationState_t& _, + const Instruction* inst) { + if (_.addressing_model() == SpvAddressingModelLogical && + !_.features().variable_pointers_storage_buffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Instruction cannot be used without a variable pointers " + "capability"; + } + + const auto result_type = _.FindDef(inst->type_id()); + if (inst->opcode() == SpvOpPtrDiff) { + if (!result_type || result_type->opcode() != SpvOpTypeInt) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result Type must be an integer scalar"; + } + } else { + if (!result_type || result_type->opcode() != SpvOpTypeBool) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Result Type must be OpTypeBool"; + } + } + + const auto op1 = _.FindDef(inst->GetOperandAs(2u)); + const auto op2 = _.FindDef(inst->GetOperandAs(3u)); + if (!op1 || !op2 || op1->type_id() != op2->type_id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "The types of Operand 1 and Operand 2 must match"; + } + const auto op1_type = _.FindDef(op1->type_id()); + if (!op1_type || op1_type->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Operand type must be a pointer"; + } + + SpvStorageClass sc = op1_type->GetOperandAs(1u); + if (_.addressing_model() == SpvAddressingModelLogical) { + if (sc != SpvStorageClassWorkgroup && sc != SpvStorageClassStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Invalid pointer storage class"; + } + + if (sc == SpvStorageClassWorkgroup && !_.features().variable_pointers) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Workgroup storage class pointer requires VariablePointers " + "capability to be specified"; + } + } else if (sc == SpvStorageClassPhysicalStorageBuffer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot use a pointer in the PhysicalStorageBuffer storage class"; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t MemoryPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpVariable: + if (auto error = ValidateVariable(_, inst)) return error; + break; + case SpvOpLoad: + if (auto error = ValidateLoad(_, inst)) return error; + break; + case SpvOpStore: + if (auto error = ValidateStore(_, inst)) return error; + break; + case SpvOpCopyMemory: + case SpvOpCopyMemorySized: + if (auto error = ValidateCopyMemory(_, inst)) return error; + break; + case SpvOpPtrAccessChain: + if (auto error = ValidatePtrAccessChain(_, inst)) return error; + break; + case SpvOpAccessChain: + case SpvOpInBoundsAccessChain: + case SpvOpInBoundsPtrAccessChain: + if (auto error = ValidateAccessChain(_, inst)) return error; + break; + case SpvOpArrayLength: + if (auto error = ValidateArrayLength(_, inst)) return error; + break; + case SpvOpCooperativeMatrixLoadNV: + case SpvOpCooperativeMatrixStoreNV: + if (auto error = ValidateCooperativeMatrixLoadStoreNV(_, inst)) + return error; + break; + case SpvOpCooperativeMatrixLengthNV: + if (auto error = ValidateCooperativeMatrixLengthNV(_, inst)) return error; + break; + case SpvOpPtrEqual: + case SpvOpPtrNotEqual: + case SpvOpPtrDiff: + if (auto error = ValidatePtrComparison(_, inst)) return error; + break; + case SpvOpImageTexelPointer: + case SpvOpGenericPtrMemSemantics: + default: + break; + } + + return SPV_SUCCESS; +} +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_memory_semantics.cpp b/third_party/spirv-tools/source/val/validate_memory_semantics.cpp new file mode 100644 index 0000000..4c582f0 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_memory_semantics.cpp @@ -0,0 +1,295 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate_memory_semantics.h" + +#include "source/diagnostic.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateMemorySemantics(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index) { + const SpvOp opcode = inst->opcode(); + const auto id = inst->GetOperandAs(operand_index); + bool is_int32 = false, is_const_int32 = false; + uint32_t value = 0; + std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(id); + + if (!is_int32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Memory Semantics to be a 32-bit int"; + } + + if (!is_const_int32) { + if (_.HasCapability(SpvCapabilityShader) && + !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics ids must be OpConstant when Shader " + "capability is present"; + } + + if (_.HasCapability(SpvCapabilityShader) && + _.HasCapability(SpvCapabilityCooperativeMatrixNV) && + !spvOpcodeIsConstant(_.GetIdOpcode(id))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics must be a constant instruction when " + "CooperativeMatrixNV capability is present"; + } + return SPV_SUCCESS; + } + + if (spvIsWebGPUEnv(_.context()->target_env)) { + uint32_t valid_bits; + switch (inst->opcode()) { + case SpvOpControlBarrier: + if (!(value & SpvMemorySemanticsAcquireReleaseMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For WebGPU, AcquireRelease must be set for Memory " + "Semantics of OpControlBarrier."; + } + + if (!(value & SpvMemorySemanticsWorkgroupMemoryMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For WebGPU, WorkgroupMemory must be set for Memory " + "Semantics of OpControlBarrier."; + } + + valid_bits = SpvMemorySemanticsAcquireReleaseMask | + SpvMemorySemanticsWorkgroupMemoryMask; + if (value & ~valid_bits) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For WebGPU only WorkgroupMemory and AcquireRelease may be " + "set for Memory Semantics of OpControlBarrier."; + } + break; + case SpvOpMemoryBarrier: + if (!(value & SpvMemorySemanticsImageMemoryMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For WebGPU, ImageMemory must be set for Memory Semantics " + "of OpMemoryBarrier."; + } + valid_bits = SpvMemorySemanticsImageMemoryMask; + if (value & ~valid_bits) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For WebGPU only ImageMemory may be set for Memory " + "Semantics of OpMemoryBarrier."; + } + break; + default: + if (spvOpcodeIsAtomicOp(inst->opcode())) { + if (value != 0) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "For WebGPU Memory no bits may be set for Memory " + "Semantics of OpAtomic* instructions."; + } + } + break; + } + } + + const size_t num_memory_order_set_bits = spvtools::utils::CountSetBits( + value & (SpvMemorySemanticsAcquireMask | SpvMemorySemanticsReleaseMask | + SpvMemorySemanticsAcquireReleaseMask | + SpvMemorySemanticsSequentiallyConsistentMask)); + + if (num_memory_order_set_bits > 1) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics can have at most one of the following " + "bits " + "set: Acquire, Release, AcquireRelease or " + "SequentiallyConsistent"; + } + + if (_.memory_model() == SpvMemoryModelVulkanKHR && + value & SpvMemorySemanticsSequentiallyConsistentMask) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "SequentiallyConsistent memory " + "semantics cannot be used with " + "the VulkanKHR memory model."; + } + + if (value & SpvMemorySemanticsMakeAvailableKHRMask && + !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics MakeAvailableKHR requires capability " + << "VulkanMemoryModelKHR"; + } + + if (value & SpvMemorySemanticsMakeVisibleKHRMask && + !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics MakeVisibleKHR requires capability " + << "VulkanMemoryModelKHR"; + } + + if (value & SpvMemorySemanticsOutputMemoryKHRMask && + !_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics OutputMemoryKHR requires capability " + << "VulkanMemoryModelKHR"; + } + + if (value & SpvMemorySemanticsVolatileMask) { + if (!_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics Volatile requires capability " + "VulkanMemoryModelKHR"; + } + + if (!spvOpcodeIsAtomicOp(inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics Volatile can only be used with atomic " + "instructions"; + } + } + + if (value & SpvMemorySemanticsUniformMemoryMask && + !_.HasCapability(SpvCapabilityShader)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics UniformMemory requires capability Shader"; + } + + // Checking for SpvCapabilityAtomicStorage is intentionally not done here. See + // https://github.com/KhronosGroup/glslang/issues/1618 for the reasoning why. + + if (value & (SpvMemorySemanticsMakeAvailableKHRMask | + SpvMemorySemanticsMakeVisibleKHRMask)) { + const bool includes_storage_class = + value & (SpvMemorySemanticsUniformMemoryMask | + SpvMemorySemanticsSubgroupMemoryMask | + SpvMemorySemanticsWorkgroupMemoryMask | + SpvMemorySemanticsCrossWorkgroupMemoryMask | + SpvMemorySemanticsAtomicCounterMemoryMask | + SpvMemorySemanticsImageMemoryMask | + SpvMemorySemanticsOutputMemoryKHRMask); + + if (!includes_storage_class) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Memory Semantics to include a storage class"; + } + } + + if (value & SpvMemorySemanticsMakeVisibleKHRMask && + !(value & (SpvMemorySemanticsAcquireMask | + SpvMemorySemanticsAcquireReleaseMask))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": MakeVisibleKHR Memory Semantics also requires either Acquire " + "or AcquireRelease Memory Semantics"; + } + + if (value & SpvMemorySemanticsMakeAvailableKHRMask && + !(value & (SpvMemorySemanticsReleaseMask | + SpvMemorySemanticsAcquireReleaseMask))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": MakeAvailableKHR Memory Semantics also requires either " + "Release or AcquireRelease Memory Semantics"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + const bool includes_storage_class = + value & (SpvMemorySemanticsUniformMemoryMask | + SpvMemorySemanticsWorkgroupMemoryMask | + SpvMemorySemanticsImageMemoryMask | + SpvMemorySemanticsOutputMemoryKHRMask); + + if (opcode == SpvOpMemoryBarrier && !num_memory_order_set_bits) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Vulkan specification requires Memory Semantics to have " + "one " + "of the following bits set: Acquire, Release, " + "AcquireRelease " + "or SequentiallyConsistent"; + } + + if (opcode == SpvOpMemoryBarrier && !includes_storage_class) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Memory Semantics to include a Vulkan-supported " + "storage class"; + } + +#if 0 + // TODO(atgoo@github.com): this check fails Vulkan CTS, reenable once fixed. + if (opcode == SpvOpControlBarrier && value && !includes_storage_class) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Memory Semantics to include a Vulkan-supported " + "storage class if Memory Semantics is not None"; + } +#endif + } + + if (opcode == SpvOpAtomicFlagClear && + (value & SpvMemorySemanticsAcquireMask || + value & SpvMemorySemanticsAcquireReleaseMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory Semantics Acquire and AcquireRelease cannot be used " + "with " + << spvOpcodeString(opcode); + } + + if (opcode == SpvOpAtomicCompareExchange && operand_index == 5 && + (value & SpvMemorySemanticsReleaseMask || + value & SpvMemorySemanticsAcquireReleaseMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Semantics Release and AcquireRelease cannot be " + "used " + "for operand Unequal"; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (opcode == SpvOpAtomicLoad && + (value & SpvMemorySemanticsReleaseMask || + value & SpvMemorySemanticsAcquireReleaseMask || + value & SpvMemorySemanticsSequentiallyConsistentMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Vulkan spec disallows OpAtomicLoad with Memory Semantics " + "Release, AcquireRelease and SequentiallyConsistent"; + } + + if (opcode == SpvOpAtomicStore && + (value & SpvMemorySemanticsAcquireMask || + value & SpvMemorySemanticsAcquireReleaseMask || + value & SpvMemorySemanticsSequentiallyConsistentMask)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Vulkan spec disallows OpAtomicStore with Memory Semantics " + "Acquire, AcquireRelease and SequentiallyConsistent"; + } + } + + // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_memory_semantics.h b/third_party/spirv-tools/source/val/validate_memory_semantics.h new file mode 100644 index 0000000..72a3e10 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_memory_semantics.h @@ -0,0 +1,28 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of memory semantics for SPIR-V instructions. + +#include "source/opcode.h" +#include "source/val/validate.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateMemorySemantics(ValidationState_t& _, + const Instruction* inst, + uint32_t operand_index); + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_misc.cpp b/third_party/spirv-tools/source/val/validate_misc.cpp new file mode 100644 index 0000000..f0deedf --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_misc.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2018 Google LLC. +// Copyright (c) 2019 NVIDIA Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateUndef(ValidationState_t& _, const Instruction* inst) { + if (_.HasCapability(SpvCapabilityShader) && + _.ContainsLimitedUseIntOrFloatType(inst->type_id()) && + !_.IsPointerType(inst->type_id())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Cannot create undefined values with 8- or 16-bit types"; + } + + if (spvIsWebGPUEnv(_.context()->target_env)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) << "OpUndef is disallowed"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateShaderClock(ValidationState_t& _, + const Instruction* inst) { + const uint32_t scope = inst->GetOperandAs(2); + if (auto error = ValidateScope(_, inst, scope)) { + return error; + } + + bool is_int32 = false, is_const_int32 = false; + uint32_t value = 0; + std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope); + if (is_const_int32 && value != SpvScopeSubgroup && value != SpvScopeDevice) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Scope must be Subgroup or Device"; + } + + // Result Type must be a 64 - bit unsigned integer type or + // a vector of two - components of 32 - + // bit unsigned integer type + const uint32_t result_type = inst->type_id(); + if (!(_.IsUnsignedIntScalarType(result_type) && + _.GetBitWidth(result_type) == 64) && + !(_.IsUnsignedIntVectorType(result_type) && + _.GetDimension(result_type) == 2 && _.GetBitWidth(result_type) == 32)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a " + "vector of two components" + " of unsigned integer" + " or 64bit unsigned integer"; + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t MiscPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpUndef: + if (auto error = ValidateUndef(_, inst)) return error; + break; + default: + break; + } + switch (inst->opcode()) { + case SpvOpBeginInvocationInterlockEXT: + case SpvOpEndInvocationInterlockEXT: + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + SpvExecutionModelFragment, + "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT " + "require Fragment execution model"); + + _.function(inst->function()->id()) + ->RegisterLimitation([](const ValidationState_t& state, + const Function* entry_point, + std::string* message) { + const auto* execution_modes = + state.GetExecutionModes(entry_point->id()); + + auto find_interlock = [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModePixelInterlockOrderedEXT: + case SpvExecutionModePixelInterlockUnorderedEXT: + case SpvExecutionModeSampleInterlockOrderedEXT: + case SpvExecutionModeSampleInterlockUnorderedEXT: + case SpvExecutionModeShadingRateInterlockOrderedEXT: + case SpvExecutionModeShadingRateInterlockUnorderedEXT: + return true; + default: + return false; + } + }; + + bool found = false; + if (execution_modes) { + auto i = std::find_if(execution_modes->begin(), + execution_modes->end(), find_interlock); + found = (i != execution_modes->end()); + } + + if (!found) { + *message = + "OpBeginInvocationInterlockEXT/OpEndInvocationInterlockEXT " + "require a fragment shader interlock execution mode."; + return false; + } + return true; + }); + break; + case SpvOpDemoteToHelperInvocationEXT: + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + SpvExecutionModelFragment, + "OpDemoteToHelperInvocationEXT requires Fragment execution " + "model"); + break; + case SpvOpIsHelperInvocationEXT: { + const uint32_t result_type = inst->type_id(); + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + SpvExecutionModelFragment, + "OpIsHelperInvocationEXT requires Fragment execution model"); + if (!_.IsBoolScalarType(result_type)) + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected bool scalar type as Result Type: " + << spvOpcodeString(inst->opcode()); + break; + } + case SpvOpReadClockKHR: + if (auto error = ValidateShaderClock(_, inst)) { + return error; + } + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_mode_setting.cpp b/third_party/spirv-tools/source/val/validate_mode_setting.cpp new file mode 100644 index 0000000..a7f8d33 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_mode_setting.cpp @@ -0,0 +1,543 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#include "source/val/validate.h" + +#include + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) { + const auto entry_point_id = inst->GetOperandAs(1); + auto entry_point = _.FindDef(entry_point_id); + if (!entry_point || SpvOpFunction != entry_point->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpEntryPoint Entry Point '" << _.getIdName(entry_point_id) + << "' is not a function."; + } + + // Only check the shader execution models + const SpvExecutionModel execution_model = + inst->GetOperandAs(0); + if (execution_model != SpvExecutionModelKernel) { + const auto entry_point_type_id = entry_point->GetOperandAs(3); + const auto entry_point_type = _.FindDef(entry_point_type_id); + if (!entry_point_type || 3 != entry_point_type->words().size()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpEntryPoint Entry Point '" << _.getIdName(entry_point_id) + << "'s function parameter count is not zero."; + } + } + + auto return_type = _.FindDef(entry_point->type_id()); + if (!return_type || SpvOpTypeVoid != return_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpEntryPoint Entry Point '" << _.getIdName(entry_point_id) + << "'s function return type is not void."; + } + + const auto* execution_modes = _.GetExecutionModes(entry_point_id); + if (_.HasCapability(SpvCapabilityShader)) { + switch (execution_model) { + case SpvExecutionModelFragment: + if (execution_modes && + execution_modes->count(SpvExecutionModeOriginUpperLeft) && + execution_modes->count(SpvExecutionModeOriginLowerLeft)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can only specify " + "one of OriginUpperLeft or OriginLowerLeft execution " + "modes."; + } + if (!execution_modes || + (!execution_modes->count(SpvExecutionModeOriginUpperLeft) && + !execution_modes->count(SpvExecutionModeOriginLowerLeft))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points require either an " + "OriginUpperLeft or OriginLowerLeft execution mode."; + } + if (execution_modes && + 1 < std::count_if(execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeDepthGreater: + case SpvExecutionModeDepthLess: + case SpvExecutionModeDepthUnchanged: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one of DepthGreater, DepthLess or DepthUnchanged " + "execution modes."; + } + if (execution_modes && + 1 < std::count_if( + execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModePixelInterlockOrderedEXT: + case SpvExecutionModePixelInterlockUnorderedEXT: + case SpvExecutionModeSampleInterlockOrderedEXT: + case SpvExecutionModeSampleInterlockUnorderedEXT: + case SpvExecutionModeShadingRateInterlockOrderedEXT: + case SpvExecutionModeShadingRateInterlockUnorderedEXT: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Fragment execution model entry points can specify at most " + "one fragment shader interlock execution mode."; + } + break; + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: + if (execution_modes && + 1 < std::count_if(execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeSpacingEqual: + case SpvExecutionModeSpacingFractionalEven: + case SpvExecutionModeSpacingFractionalOdd: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Tessellation execution model entry points can specify at " + "most one of SpacingEqual, SpacingFractionalOdd or " + "SpacingFractionalEven execution modes."; + } + if (execution_modes && + 1 < std::count_if(execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeTriangles: + case SpvExecutionModeQuads: + case SpvExecutionModeIsolines: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Tessellation execution model entry points can specify at " + "most one of Triangles, Quads or Isolines execution modes."; + } + if (execution_modes && + 1 < std::count_if(execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeVertexOrderCw: + case SpvExecutionModeVertexOrderCcw: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Tessellation execution model entry points can specify at " + "most one of VertexOrderCw or VertexOrderCcw execution " + "modes."; + } + break; + case SpvExecutionModelGeometry: + if (!execution_modes || + 1 != std::count_if(execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeInputPoints: + case SpvExecutionModeInputLines: + case SpvExecutionModeInputLinesAdjacency: + case SpvExecutionModeTriangles: + case SpvExecutionModeInputTrianglesAdjacency: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Geometry execution model entry points must specify " + "exactly one of InputPoints, InputLines, " + "InputLinesAdjacency, Triangles or InputTrianglesAdjacency " + "execution modes."; + } + if (!execution_modes || + 1 != std::count_if(execution_modes->begin(), execution_modes->end(), + [](const SpvExecutionMode& mode) { + switch (mode) { + case SpvExecutionModeOutputPoints: + case SpvExecutionModeOutputLineStrip: + case SpvExecutionModeOutputTriangleStrip: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Geometry execution model entry points must specify " + "exactly one of OutputPoints, OutputLineStrip or " + "OutputTriangleStrip execution modes."; + } + break; + default: + break; + } + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + switch (execution_model) { + case SpvExecutionModelGLCompute: + if (!execution_modes || + !execution_modes->count(SpvExecutionModeLocalSize)) { + bool ok = false; + for (auto& i : _.ordered_instructions()) { + if (i.opcode() == SpvOpDecorate) { + if (i.operands().size() > 2) { + if (i.GetOperandAs(1) == SpvDecorationBuiltIn && + i.GetOperandAs(2) == SpvBuiltInWorkgroupSize) { + ok = true; + break; + } + } + } + } + if (!ok) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the Vulkan environment, GLCompute execution model " + "entry points require either the LocalSize execution " + "mode or an object decorated with WorkgroupSize must be " + "specified."; + } + } + break; + default: + break; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExecutionMode(ValidationState_t& _, + const Instruction* inst) { + const auto entry_point_id = inst->GetOperandAs(0); + const auto found = std::find(_.entry_points().cbegin(), + _.entry_points().cend(), entry_point_id); + if (found == _.entry_points().cend()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpExecutionMode Entry Point '" + << _.getIdName(entry_point_id) + << "' is not the Entry Point " + "operand of an OpEntryPoint."; + } + + const auto mode = inst->GetOperandAs(1); + if (inst->opcode() == SpvOpExecutionModeId) { + size_t operand_count = inst->operands().size(); + for (size_t i = 2; i < operand_count; ++i) { + const auto operand_id = inst->GetOperandAs(2); + const auto* operand_inst = _.FindDef(operand_id); + if (mode == SpvExecutionModeSubgroupsPerWorkgroupId || + mode == SpvExecutionModeLocalSizeHintId || + mode == SpvExecutionModeLocalSizeId) { + if (!spvOpcodeIsConstant(operand_inst->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "For OpExecutionModeId all Extra Operand ids must be " + "constant " + "instructions."; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpExecutionModeId is only valid when the Mode operand is an " + "execution mode that takes Extra Operands that are id " + "operands."; + } + } + } else if (mode == SpvExecutionModeSubgroupsPerWorkgroupId || + mode == SpvExecutionModeLocalSizeHintId || + mode == SpvExecutionModeLocalSizeId) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "OpExecutionMode is only valid when the Mode operand is an " + "execution mode that takes no Extra Operands, or takes Extra " + "Operands that are not id operands."; + } + + const auto* models = _.GetExecutionModels(entry_point_id); + switch (mode) { + case SpvExecutionModeInvocations: + case SpvExecutionModeInputPoints: + case SpvExecutionModeInputLines: + case SpvExecutionModeInputLinesAdjacency: + case SpvExecutionModeInputTrianglesAdjacency: + case SpvExecutionModeOutputLineStrip: + case SpvExecutionModeOutputTriangleStrip: + if (!std::all_of(models->begin(), models->end(), + [](const SpvExecutionModel& model) { + return model == SpvExecutionModelGeometry; + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Geometry execution " + "model."; + } + break; + case SpvExecutionModeOutputPoints: + if (!std::all_of(models->begin(), models->end(), + [&_](const SpvExecutionModel& model) { + switch (model) { + case SpvExecutionModelGeometry: + return true; + case SpvExecutionModelMeshNV: + return _.HasCapability(SpvCapabilityMeshShadingNV); + default: + return false; + } + })) { + if (_.HasCapability(SpvCapabilityMeshShadingNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Geometry or " + "MeshNV execution model."; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Geometry " + "execution " + "model."; + } + } + break; + case SpvExecutionModeSpacingEqual: + case SpvExecutionModeSpacingFractionalEven: + case SpvExecutionModeSpacingFractionalOdd: + case SpvExecutionModeVertexOrderCw: + case SpvExecutionModeVertexOrderCcw: + case SpvExecutionModePointMode: + case SpvExecutionModeQuads: + case SpvExecutionModeIsolines: + if (!std::all_of( + models->begin(), models->end(), + [](const SpvExecutionModel& model) { + return (model == SpvExecutionModelTessellationControl) || + (model == SpvExecutionModelTessellationEvaluation); + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a tessellation " + "execution model."; + } + break; + case SpvExecutionModeTriangles: + if (!std::all_of(models->begin(), models->end(), + [](const SpvExecutionModel& model) { + switch (model) { + case SpvExecutionModelGeometry: + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: + return true; + default: + return false; + } + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Geometry or " + "tessellation execution model."; + } + break; + case SpvExecutionModeOutputVertices: + if (!std::all_of(models->begin(), models->end(), + [&_](const SpvExecutionModel& model) { + switch (model) { + case SpvExecutionModelGeometry: + case SpvExecutionModelTessellationControl: + case SpvExecutionModelTessellationEvaluation: + return true; + case SpvExecutionModelMeshNV: + return _.HasCapability(SpvCapabilityMeshShadingNV); + default: + return false; + } + })) { + if (_.HasCapability(SpvCapabilityMeshShadingNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Geometry, " + "tessellation or MeshNV execution model."; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Geometry or " + "tessellation execution model."; + } + } + break; + case SpvExecutionModePixelCenterInteger: + case SpvExecutionModeOriginUpperLeft: + case SpvExecutionModeOriginLowerLeft: + case SpvExecutionModeEarlyFragmentTests: + case SpvExecutionModeDepthReplacing: + case SpvExecutionModeDepthGreater: + case SpvExecutionModeDepthLess: + case SpvExecutionModeDepthUnchanged: + case SpvExecutionModePixelInterlockOrderedEXT: + case SpvExecutionModePixelInterlockUnorderedEXT: + case SpvExecutionModeSampleInterlockOrderedEXT: + case SpvExecutionModeSampleInterlockUnorderedEXT: + case SpvExecutionModeShadingRateInterlockOrderedEXT: + case SpvExecutionModeShadingRateInterlockUnorderedEXT: + if (!std::all_of(models->begin(), models->end(), + [](const SpvExecutionModel& model) { + return model == SpvExecutionModelFragment; + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Fragment execution " + "model."; + } + break; + case SpvExecutionModeLocalSizeHint: + case SpvExecutionModeVecTypeHint: + case SpvExecutionModeContractionOff: + case SpvExecutionModeLocalSizeHintId: + if (!std::all_of(models->begin(), models->end(), + [](const SpvExecutionModel& model) { + return model == SpvExecutionModelKernel; + })) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with the Kernel execution " + "model."; + } + break; + case SpvExecutionModeLocalSize: + case SpvExecutionModeLocalSizeId: + if (!std::all_of(models->begin(), models->end(), + [&_](const SpvExecutionModel& model) { + switch (model) { + case SpvExecutionModelKernel: + case SpvExecutionModelGLCompute: + return true; + case SpvExecutionModelTaskNV: + case SpvExecutionModelMeshNV: + return _.HasCapability(SpvCapabilityMeshShadingNV); + default: + return false; + } + })) { + if (_.HasCapability(SpvCapabilityMeshShadingNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Kernel, GLCompute, " + "MeshNV, or TaskNV execution model."; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode can only be used with a Kernel or " + "GLCompute " + "execution model."; + } + } + default: + break; + } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (mode == SpvExecutionModeOriginLowerLeft) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the Vulkan environment, the OriginLowerLeft execution mode " + "must not be used."; + } + if (mode == SpvExecutionModePixelCenterInteger) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "In the Vulkan environment, the PixelCenterInteger execution " + "mode must not be used."; + } + } + + if (spvIsWebGPUEnv(_.context()->target_env)) { + if (mode != SpvExecutionModeOriginUpperLeft && + mode != SpvExecutionModeDepthReplacing && + mode != SpvExecutionModeDepthGreater && + mode != SpvExecutionModeDepthLess && + mode != SpvExecutionModeDepthUnchanged && + mode != SpvExecutionModeLocalSize && + mode != SpvExecutionModeLocalSizeHint) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Execution mode must be one of OriginUpperLeft, " + "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, " + "LocalSize, or LocalSizeHint for WebGPU environment."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateMemoryModel(ValidationState_t& _, + const Instruction* inst) { + // Already produced an error if multiple memory model instructions are + // present. + if (_.memory_model() != SpvMemoryModelVulkanKHR && + _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "VulkanMemoryModelKHR capability must only be specified if " + "the VulkanKHR memory model is used."; + } + + if (spvIsWebGPUEnv(_.context()->target_env)) { + if (_.addressing_model() != SpvAddressingModelLogical) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Addressing model must be Logical for WebGPU environment."; + } + } + + if (spvIsOpenCLEnv(_.context()->target_env)) { + if ((_.addressing_model() != SpvAddressingModelPhysical32) && + (_.addressing_model() != SpvAddressingModelPhysical64)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Addressing model must be Physical32 or Physical64 " + << "in the OpenCL environment."; + } + if (_.memory_model() != SpvMemoryModelOpenCL) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Memory model must be OpenCL in the OpenCL environment."; + } + } + + return SPV_SUCCESS; +} + +} // namespace + +spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpEntryPoint: + if (auto error = ValidateEntryPoint(_, inst)) return error; + break; + case SpvOpExecutionMode: + case SpvOpExecutionModeId: + if (auto error = ValidateExecutionMode(_, inst)) return error; + break; + case SpvOpMemoryModel: + if (auto error = ValidateMemoryModel(_, inst)) return error; + break; + default: + break; + } + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_non_uniform.cpp b/third_party/spirv-tools/source/val/validate_non_uniform.cpp new file mode 100644 index 0000000..8dcf974 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_non_uniform.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of barrier SPIR-V instructions. + +#include "source/val/validate.h" + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/util/bitutils.h" +#include "source/val/instruction.h" +#include "source/val/validate_scopes.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { +namespace { + +spv_result_t ValidateGroupNonUniformBallotBitCount(ValidationState_t& _, + const Instruction* inst) { + // Scope is already checked by ValidateExecutionScope() above. + + const uint32_t result_type = inst->type_id(); + if (!_.IsUnsignedIntScalarType(result_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected Result Type to be an unsigned integer type scalar."; + } + + const auto value = inst->GetOperandAs(4); + const auto value_type = _.FindDef(value)->type_id(); + if (!_.IsUnsignedIntVectorType(value_type) || + _.GetDimension(value_type) != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Expected Value to be a " + "vector of four components " + "of integer type scalar"; + } + return SPV_SUCCESS; +} + +} // namespace + +// Validates correctness of non-uniform group instructions. +spv_result_t NonUniformPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + + if (spvOpcodeIsNonUniformGroupOperation(opcode)) { + const uint32_t execution_scope = inst->word(3); + if (auto error = ValidateExecutionScope(_, inst, execution_scope)) { + return error; + } + } + + switch (opcode) { + case SpvOpGroupNonUniformBallotBitCount: + return ValidateGroupNonUniformBallotBitCount(_, inst); + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_primitives.cpp b/third_party/spirv-tools/source/val/validate_primitives.cpp new file mode 100644 index 0000000..7d11f2e --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_primitives.cpp @@ -0,0 +1,75 @@ +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of primitive SPIR-V instructions. + +#include "source/val/validate.h" + +#include + +#include "source/diagnostic.h" +#include "source/opcode.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +// Validates correctness of primitive instructions. +spv_result_t PrimitivesPass(ValidationState_t& _, const Instruction* inst) { + const SpvOp opcode = inst->opcode(); + + switch (opcode) { + case SpvOpEmitVertex: + case SpvOpEndPrimitive: + case SpvOpEmitStreamVertex: + case SpvOpEndStreamPrimitive: + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + SpvExecutionModelGeometry, + std::string(spvOpcodeString(opcode)) + + " instructions require Geometry execution model"); + break; + default: + break; + } + + switch (opcode) { + case SpvOpEmitStreamVertex: + case SpvOpEndStreamPrimitive: { + const uint32_t stream_id = inst->word(1); + const uint32_t stream_type = _.GetTypeId(stream_id); + if (!_.IsIntScalarType(stream_type)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Stream to be int scalar"; + } + + const SpvOp stream_opcode = _.GetIdOpcode(stream_id); + if (!spvOpcodeIsConstant(stream_opcode)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": expected Stream to be constant instruction"; + } + } + + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_scopes.cpp b/third_party/spirv-tools/source/val/validate_scopes.cpp new file mode 100644 index 0000000..a6fb26d --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_scopes.cpp @@ -0,0 +1,325 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate_scopes.h" + +#include "source/diagnostic.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +bool IsValidScope(uint32_t scope) { + // Deliberately avoid a default case so we have to update the list when the + // scopes list changes. + switch (static_cast(scope)) { + case SpvScopeCrossDevice: + case SpvScopeDevice: + case SpvScopeWorkgroup: + case SpvScopeSubgroup: + case SpvScopeInvocation: + case SpvScopeQueueFamilyKHR: + case SpvScopeShaderCallKHR: + return true; + case SpvScopeMax: + break; + } + return false; +} + +spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst, + uint32_t scope) { + SpvOp opcode = inst->opcode(); + bool is_int32 = false, is_const_int32 = false; + uint32_t value = 0; + std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope); + + if (!is_int32) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) << ": expected scope to be a 32-bit int"; + } + + if (!is_const_int32) { + if (_.HasCapability(SpvCapabilityShader) && + !_.HasCapability(SpvCapabilityCooperativeMatrixNV)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Scope ids must be OpConstant when Shader capability is " + << "present"; + } + if (_.HasCapability(SpvCapabilityShader) && + _.HasCapability(SpvCapabilityCooperativeMatrixNV) && + !spvOpcodeIsConstant(_.GetIdOpcode(scope))) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Scope ids must be constant or specialization constant when " + << "CooperativeMatrixNV capability is present"; + } + } + + if (is_const_int32 && !IsValidScope(value)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid scope value:\n " << _.Disassemble(*_.FindDef(scope)); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateExecutionScope(ValidationState_t& _, + const Instruction* inst, uint32_t scope) { + SpvOp opcode = inst->opcode(); + bool is_int32 = false, is_const_int32 = false; + uint32_t value = 0; + std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope); + + if (auto error = ValidateScope(_, inst, scope)) { + return error; + } + + if (!is_const_int32) { + return SPV_SUCCESS; + } + + // Vulkan specific rules + if (spvIsVulkanEnv(_.context()->target_env)) { + // Vulkan 1.1 specific rules + if (_.context()->target_env != SPV_ENV_VULKAN_1_0) { + // Scope for Non Uniform Group Operations must be limited to Subgroup + if (spvOpcodeIsNonUniformGroupOperation(opcode) && + value != SpvScopeSubgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in Vulkan environment Execution scope is limited to " + << "Subgroup"; + } + } + + // If OpControlBarrier is used in fragment, vertex, tessellation evaluation, + // or geometry stages, the execution Scope must be Subgroup. + if (opcode == SpvOpControlBarrier && value != SpvScopeSubgroup) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation([](SpvExecutionModel model, + std::string* message) { + if (model == SpvExecutionModelFragment || + model == SpvExecutionModelVertex || + model == SpvExecutionModelGeometry || + model == SpvExecutionModelTessellationEvaluation) { + if (message) { + *message = + "in Vulkan evironment, OpControlBarrier execution scope " + "must be Subgroup for Fragment, Vertex, Geometry and " + "TessellationEvaluation execution models"; + } + return false; + } + return true; + }); + } + + // Vulkan generic rules + // Scope for execution must be limited to Workgroup or Subgroup + if (value != SpvScopeWorkgroup && value != SpvScopeSubgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in Vulkan environment Execution Scope is limited to " + << "Workgroup and Subgroup"; + } + } + + // WebGPU Specific rules + if (spvIsWebGPUEnv(_.context()->target_env)) { + if (value != SpvScopeWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in WebGPU environment Execution Scope is limited to " + << "Workgroup"; + } else { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelGLCompute) { + if (message) { + *message = + ": in WebGPU environment, Workgroup Execution Scope is " + "limited to GLCompute execution model"; + } + return false; + } + return true; + }); + } + } + + // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. + + // General SPIRV rules + // Scope for execution must be limited to Workgroup or Subgroup for + // non-uniform operations + if (spvOpcodeIsNonUniformGroupOperation(opcode) && + value != SpvScopeSubgroup && value != SpvScopeWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Execution scope is limited to Subgroup or Workgroup"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, + uint32_t scope) { + const SpvOp opcode = inst->opcode(); + bool is_int32 = false, is_const_int32 = false; + uint32_t value = 0; + std::tie(is_int32, is_const_int32, value) = _.EvalInt32IfConst(scope); + + if (auto error = ValidateScope(_, inst, scope)) { + return error; + } + + if (!is_const_int32) { + return SPV_SUCCESS; + } + + if (value == SpvScopeQueueFamilyKHR) { + if (_.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { + return SPV_SUCCESS; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": Memory Scope QueueFamilyKHR requires capability " + << "VulkanMemoryModelKHR"; + } + } + + if (value == SpvScopeDevice && + _.HasCapability(SpvCapabilityVulkanMemoryModelKHR) && + !_.HasCapability(SpvCapabilityVulkanMemoryModelDeviceScopeKHR)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Use of device scope with VulkanKHR memory model requires the " + << "VulkanMemoryModelDeviceScopeKHR capability"; + } + + // Vulkan Specific rules + if (spvIsVulkanEnv(_.context()->target_env)) { + if (value == SpvScopeCrossDevice) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in Vulkan environment, Memory Scope cannot be CrossDevice"; + } + // Vulkan 1.0 specifc rules + if (_.context()->target_env == SPV_ENV_VULKAN_1_0 && + value != SpvScopeDevice && value != SpvScopeWorkgroup && + value != SpvScopeInvocation) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in Vulkan 1.0 environment Memory Scope is limited to " + << "Device, Workgroup and Invocation"; + } + // Vulkan 1.1 specifc rules + if ((_.context()->target_env == SPV_ENV_VULKAN_1_1 || + _.context()->target_env == SPV_ENV_VULKAN_1_2) && + value != SpvScopeDevice && value != SpvScopeWorkgroup && + value != SpvScopeSubgroup && value != SpvScopeInvocation && + value != SpvScopeShaderCallKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in Vulkan 1.1 and 1.2 environment Memory Scope is limited " + << "to Device, Workgroup, Invocation, and ShaderCall"; + } + + if (value == SpvScopeShaderCallKHR) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelRayGenerationKHR && + model != SpvExecutionModelIntersectionKHR && + model != SpvExecutionModelAnyHitKHR && + model != SpvExecutionModelClosestHitKHR && + model != SpvExecutionModelMissKHR && + model != SpvExecutionModelCallableKHR) { + if (message) { + *message = + "ShaderCallKHR Memory Scope requires a ray tracing " + "execution model"; + } + return false; + } + return true; + }); + } + } + + // WebGPU specific rules + if (spvIsWebGPUEnv(_.context()->target_env)) { + switch (inst->opcode()) { + case SpvOpControlBarrier: + if (value != SpvScopeWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in WebGPU environment Memory Scope is limited to " + << "Workgroup for OpControlBarrier"; + } + break; + case SpvOpMemoryBarrier: + if (value != SpvScopeWorkgroup) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in WebGPU environment Memory Scope is limited to " + << "Workgroup for OpMemoryBarrier"; + } + break; + default: + if (spvOpcodeIsAtomicOp(inst->opcode())) { + if (value != SpvScopeQueueFamilyKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in WebGPU environment Memory Scope is limited to " + << "QueueFamilyKHR for OpAtomic* operations"; + } + } + + if (value != SpvScopeWorkgroup && value != SpvScopeInvocation && + value != SpvScopeQueueFamilyKHR) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << spvOpcodeString(opcode) + << ": in WebGPU environment Memory Scope is limited to " + << "Workgroup, Invocation, and QueueFamilyKHR"; + } + break; + } + + if (value == SpvScopeWorkgroup) { + _.function(inst->function()->id()) + ->RegisterExecutionModelLimitation( + [](SpvExecutionModel model, std::string* message) { + if (model != SpvExecutionModelGLCompute) { + if (message) { + *message = + ": in WebGPU environment, Workgroup Memory Scope is " + "limited to GLCompute execution model"; + } + return false; + } + return true; + }); + } + } + + // TODO(atgoo@github.com) Add checks for OpenCL and OpenGL environments. + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_scopes.h b/third_party/spirv-tools/source/val/validate_scopes.h new file mode 100644 index 0000000..ba8b301 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_scopes.h @@ -0,0 +1,33 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Validates correctness of scopes for SPIR-V instructions. + +#include "source/opcode.h" +#include "source/val/validate.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateScope(ValidationState_t& _, const Instruction* inst, + uint32_t scope); + +spv_result_t ValidateExecutionScope(ValidationState_t& _, + const Instruction* inst, uint32_t scope); + +spv_result_t ValidateMemoryScope(ValidationState_t& _, const Instruction* inst, + uint32_t scope); + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_small_type_uses.cpp b/third_party/spirv-tools/source/val/validate_small_type_uses.cpp new file mode 100644 index 0000000..9db82e7 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_small_type_uses.cpp @@ -0,0 +1,57 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validate.h" + +#include "source/val/instruction.h" +#include "source/val/validation_state.h" + +namespace spvtools { +namespace val { + +spv_result_t ValidateSmallTypeUses(ValidationState_t& _, + const Instruction* inst) { + if (!_.HasCapability(SpvCapabilityShader) || inst->type_id() == 0 || + !_.ContainsLimitedUseIntOrFloatType(inst->type_id())) { + return SPV_SUCCESS; + } + + if (_.IsPointerType(inst->type_id())) return SPV_SUCCESS; + + // The validator should previously have checked ways to generate 8- or 16-bit + // types. So we only need to considervalid paths from source to sink. + // When restricted, uses of 8- or 16-bit types can only be stores, + // width-only conversions, decorations and copy object. + for (auto use : inst->uses()) { + const auto* user = use.first; + switch (user->opcode()) { + case SpvOpDecorate: + case SpvOpDecorateId: + case SpvOpCopyObject: + case SpvOpStore: + case SpvOpFConvert: + case SpvOpUConvert: + case SpvOpSConvert: + break; + default: + return _.diag(SPV_ERROR_INVALID_ID, user) + << "Invalid use of 8- or 16-bit result"; + } + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validate_type.cpp b/third_party/spirv-tools/source/val/validate_type.cpp new file mode 100644 index 0000000..5924c69 --- /dev/null +++ b/third_party/spirv-tools/source/val/validate_type.cpp @@ -0,0 +1,673 @@ +// Copyright (c) 2018 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Ensures type declarations are unique unless allowed by the specification. + +#include "source/opcode.h" +#include "source/spirv_target_env.h" +#include "source/val/instruction.h" +#include "source/val/validate.h" +#include "source/val/validation_state.h" +#include "spirv/unified1/spirv.h" + +namespace spvtools { +namespace val { +namespace { + +// Returns, as an int64_t, the literal value from an OpConstant or the +// default value of an OpSpecConstant, assuming it is an integral type. +// For signed integers, relies the rule that literal value is sign extended +// to fill out to word granularity. Assumes that the constant value +// has +int64_t ConstantLiteralAsInt64(uint32_t width, + const std::vector& const_words) { + const uint32_t lo_word = const_words[3]; + if (width <= 32) return int32_t(lo_word); + assert(width <= 64); + assert(const_words.size() > 4); + const uint32_t hi_word = const_words[4]; // Must exist, per spec. + return static_cast(uint64_t(lo_word) | uint64_t(hi_word) << 32); +} + +// Returns, as an uint64_t, the literal value from an OpConstant or the +// default value of an OpSpecConstant, assuming it is an integral type. +// For signed integers, relies the rule that literal value is sign extended +// to fill out to word granularity. Assumes that the constant value +// has +int64_t ConstantLiteralAsUint64(uint32_t width, + const std::vector& const_words) { + const uint32_t lo_word = const_words[3]; + if (width <= 32) return lo_word; + assert(width <= 64); + assert(const_words.size() > 4); + const uint32_t hi_word = const_words[4]; // Must exist, per spec. + return (uint64_t(lo_word) | uint64_t(hi_word) << 32); +} + +// Validates that type declarations are unique, unless multiple declarations +// of the same data type are allowed by the specification. +// (see section 2.8 Types and Variables) +// Doesn't do anything if SPV_VAL_ignore_type_decl_unique was declared in the +// module. +spv_result_t ValidateUniqueness(ValidationState_t& _, const Instruction* inst) { + if (_.HasExtension(Extension::kSPV_VALIDATOR_ignore_type_decl_unique)) + return SPV_SUCCESS; + + const auto opcode = inst->opcode(); + if (opcode != SpvOpTypeArray && opcode != SpvOpTypeRuntimeArray && + opcode != SpvOpTypeStruct && opcode != SpvOpTypePointer && + !_.RegisterUniqueTypeDeclaration(inst)) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Duplicate non-aggregate type declarations are not allowed. " + "Opcode: " + << spvOpcodeString(opcode) << " id: " << inst->id(); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeInt(ValidationState_t& _, const Instruction* inst) { + // Validates that the number of bits specified for an Int type is valid. + // Scalar integer types can be parameterized only with 32-bits. + // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit + // integers, respectively. + auto num_bits = inst->GetOperandAs(1); + if (num_bits != 32) { + if (num_bits == 8) { + if (_.features().declare_int8_type) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using an 8-bit integer type requires the Int8 capability," + " or an extension that explicitly enables 8-bit integers."; + } else if (num_bits == 16) { + if (_.features().declare_int16_type) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 16-bit integer type requires the Int16 capability," + " or an extension that explicitly enables 16-bit integers."; + } else if (num_bits == 64) { + if (_.HasCapability(SpvCapabilityInt64)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 64-bit integer type requires the Int64 capability."; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid number of bits (" << num_bits + << ") used for OpTypeInt."; + } + } + + const auto signedness_index = 2; + const auto signedness = inst->GetOperandAs(signedness_index); + if (signedness != 0 && signedness != 1) { + return _.diag(SPV_ERROR_INVALID_VALUE, inst) + << "OpTypeInt has invalid signedness:"; + } + + // SPIR-V Spec 2.16.3: Validation Rules for Kernel Capabilities: The + // Signedness in OpTypeInt must always be 0. + if (SpvOpTypeInt == inst->opcode() && _.HasCapability(SpvCapabilityKernel) && + inst->GetOperandAs(2) != 0u) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "The Signedness in OpTypeInt " + "must always be 0 when Kernel " + "capability is used."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeFloat(ValidationState_t& _, const Instruction* inst) { + // Validates that the number of bits specified for an Int type is valid. + // Scalar integer types can be parameterized only with 32-bits. + // Int8, Int16, and Int64 capabilities allow using 8-bit, 16-bit, and 64-bit + // integers, respectively. + auto num_bits = inst->GetOperandAs(1); + if (num_bits == 32) { + return SPV_SUCCESS; + } + if (num_bits == 16) { + if (_.features().declare_float16_type) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 16-bit floating point " + << "type requires the Float16 or Float16Buffer capability," + " or an extension that explicitly enables 16-bit floating point."; + } + if (num_bits == 64) { + if (_.HasCapability(SpvCapabilityFloat64)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Using a 64-bit floating point " + << "type requires the Float64 capability."; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Invalid number of bits (" << num_bits << ") used for OpTypeFloat."; +} + +spv_result_t ValidateTypeVector(ValidationState_t& _, const Instruction* inst) { + const auto component_index = 1; + const auto component_id = inst->GetOperandAs(component_index); + const auto component_type = _.FindDef(component_id); + if (!component_type || !spvOpcodeIsScalarType(component_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeVector Component Type '" << _.getIdName(component_id) + << "' is not a scalar type."; + } + + // Validates that the number of components in the vector is valid. + // Vector types can only be parameterized as having 2, 3, or 4 components. + // If the Vector16 capability is added, 8 and 16 components are also allowed. + auto num_components = inst->GetOperandAs(2); + if (num_components == 2 || num_components == 3 || num_components == 4) { + return SPV_SUCCESS; + } else if (num_components == 8 || num_components == 16) { + if (_.HasCapability(SpvCapabilityVector16)) { + return SPV_SUCCESS; + } + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Having " << num_components << " components for " + << spvOpcodeString(inst->opcode()) + << " requires the Vector16 capability"; + } else { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << "Illegal number of components (" << num_components << ") for " + << spvOpcodeString(inst->opcode()); + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeMatrix(ValidationState_t& _, const Instruction* inst) { + const auto column_type_index = 1; + const auto column_type_id = inst->GetOperandAs(column_type_index); + const auto column_type = _.FindDef(column_type_id); + if (!column_type || SpvOpTypeVector != column_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Columns in a matrix must be of type vector."; + } + + // Trace back once more to find out the type of components in the vector. + // Operand 1 is the of the type of data in the vector. + const auto comp_type_id = column_type->GetOperandAs(1); + auto comp_type_instruction = _.FindDef(comp_type_id); + if (comp_type_instruction->opcode() != SpvOpTypeFloat) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be " + "parameterized with " + "floating-point types."; + } + + // Validates that the matrix has 2,3, or 4 columns. + auto num_cols = inst->GetOperandAs(2); + if (num_cols != 2 && num_cols != 3 && num_cols != 4) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Matrix types can only be " + "parameterized as having " + "only 2, 3, or 4 columns."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) { + const auto element_type_index = 1; + const auto element_type_id = inst->GetOperandAs(element_type_index); + const auto element_type = _.FindDef(element_type_id); + if (!element_type || !spvOpcodeGeneratesType(element_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Element Type '" << _.getIdName(element_type_id) + << "' is not a type."; + } + + if (element_type->opcode() == SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Element Type '" << _.getIdName(element_type_id) + << "' is a void type."; + } + + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) && + element_type->opcode() == SpvOpTypeRuntimeArray) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Element Type '" << _.getIdName(element_type_id) + << "' is not valid in " + << spvLogStringForEnv(_.context()->target_env) << " environments."; + } + + const auto length_index = 2; + const auto length_id = inst->GetOperandAs(length_index); + const auto length = _.FindDef(length_id); + if (!length || !spvOpcodeIsConstant(length->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length '" << _.getIdName(length_id) + << "' is not a scalar constant type."; + } + + // NOTE: Check the initialiser value of the constant + const auto const_inst = length->words(); + const auto const_result_type_index = 1; + const auto const_result_type = _.FindDef(const_inst[const_result_type_index]); + if (!const_result_type || SpvOpTypeInt != const_result_type->opcode()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length '" << _.getIdName(length_id) + << "' is not a constant integer type."; + } + + switch (length->opcode()) { + case SpvOpSpecConstant: + case SpvOpConstant: { + auto& type_words = const_result_type->words(); + const bool is_signed = type_words[3] > 0; + const uint32_t width = type_words[2]; + const int64_t ivalue = ConstantLiteralAsInt64(width, length->words()); + if (ivalue == 0 || (ivalue < 0 && is_signed)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length '" << _.getIdName(length_id) + << "' default value must be at least 1: found " << ivalue; + } + if (spvIsWebGPUEnv(_.context()->target_env)) { + // WebGPU has maximum integer width of 32 bits, and max array size + // is one more than the max signed integer representation. + const uint64_t max_permitted = (uint64_t(1) << 31); + const uint64_t uvalue = ConstantLiteralAsUint64(width, length->words()); + if (uvalue > max_permitted) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length '" << _.getIdName(length_id) + << "' size exceeds max value " << max_permitted + << " permitted by WebGPU: got " << uvalue; + } + } + } break; + case SpvOpConstantNull: + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeArray Length '" << _.getIdName(length_id) + << "' default value must be at least 1."; + case SpvOpSpecConstantOp: + // Assume it's OK, rather than try to evaluate the operation. + break; + default: + assert(0 && "bug in spvOpcodeIsConstant() or result type isn't int"); + } + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _, + const Instruction* inst) { + const auto element_type_index = 1; + const auto element_id = inst->GetOperandAs(element_type_index); + const auto element_type = _.FindDef(element_id); + if (!element_type || !spvOpcodeGeneratesType(element_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeRuntimeArray Element Type '" + << _.getIdName(element_id) << "' is not a type."; + } + + if (element_type->opcode() == SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeRuntimeArray Element Type '" + << _.getIdName(element_id) << "' is a void type."; + } + + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) && + element_type->opcode() == SpvOpTypeRuntimeArray) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeRuntimeArray Element Type '" + << _.getIdName(element_id) << "' is not valid in " + << spvLogStringForEnv(_.context()->target_env) << " environments."; + } + + return SPV_SUCCESS; +} + +bool ContainsOpaqueType(ValidationState_t& _, const Instruction* str) { + const size_t elem_type_index = 1; + uint32_t elem_type_id; + Instruction* elem_type; + + if (spvOpcodeIsBaseOpaqueType(str->opcode())) { + return true; + } + + switch (str->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + elem_type_id = str->GetOperandAs(elem_type_index); + elem_type = _.FindDef(elem_type_id); + return ContainsOpaqueType(_, elem_type); + case SpvOpTypeStruct: + for (size_t member_type_index = 1; + member_type_index < str->operands().size(); ++member_type_index) { + auto member_type_id = str->GetOperandAs(member_type_index); + auto member_type = _.FindDef(member_type_id); + if (ContainsOpaqueType(_, member_type)) return true; + } + break; + default: + break; + } + return false; +} + +spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) { + const uint32_t struct_id = inst->GetOperandAs(0); + for (size_t member_type_index = 1; + member_type_index < inst->operands().size(); ++member_type_index) { + auto member_type_id = inst->GetOperandAs(member_type_index); + if (member_type_id == inst->id()) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Structure members may not be self references"; + } + + auto member_type = _.FindDef(member_type_id); + if (!member_type || !spvOpcodeGeneratesType(member_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeStruct Member Type '" << _.getIdName(member_type_id) + << "' is not a type."; + } + if (member_type->opcode() == SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Structures cannot contain a void type."; + } + if (SpvOpTypeStruct == member_type->opcode() && + _.IsStructTypeWithBuiltInMember(member_type_id)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Structure " << _.getIdName(member_type_id) + << " contains members with BuiltIn decoration. Therefore this " + << "structure may not be contained as a member of another " + << "structure " + << "type. Structure " << _.getIdName(struct_id) + << " contains structure " << _.getIdName(member_type_id) + << "."; + } + + if (spvIsVulkanOrWebGPUEnv(_.context()->target_env) && + member_type->opcode() == SpvOpTypeRuntimeArray) { + const bool is_last_member = + member_type_index == inst->operands().size() - 1; + if (!is_last_member) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In " << spvLogStringForEnv(_.context()->target_env) + << ", OpTypeRuntimeArray must only be used for the last member " + "of an OpTypeStruct"; + } + } + } + + bool has_nested_blockOrBufferBlock_struct = false; + // Struct members start at word 2 of OpTypeStruct instruction. + for (size_t word_i = 2; word_i < inst->words().size(); ++word_i) { + auto member = inst->word(word_i); + auto memberTypeInstr = _.FindDef(member); + if (memberTypeInstr && SpvOpTypeStruct == memberTypeInstr->opcode()) { + if (_.HasDecoration(memberTypeInstr->id(), SpvDecorationBlock) || + _.HasDecoration(memberTypeInstr->id(), SpvDecorationBufferBlock) || + _.GetHasNestedBlockOrBufferBlockStruct(memberTypeInstr->id())) + has_nested_blockOrBufferBlock_struct = true; + } + } + + _.SetHasNestedBlockOrBufferBlockStruct(inst->id(), + has_nested_blockOrBufferBlock_struct); + if (_.GetHasNestedBlockOrBufferBlockStruct(inst->id()) && + (_.HasDecoration(inst->id(), SpvDecorationBufferBlock) || + _.HasDecoration(inst->id(), SpvDecorationBlock))) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "rules: A Block or BufferBlock cannot be nested within another " + "Block or BufferBlock. "; + } + + std::unordered_set built_in_members; + for (auto decoration : _.id_decorations(struct_id)) { + if (decoration.dec_type() == SpvDecorationBuiltIn && + decoration.struct_member_index() != Decoration::kInvalidMember) { + built_in_members.insert(decoration.struct_member_index()); + } + } + int num_struct_members = static_cast(inst->operands().size() - 1); + int num_builtin_members = static_cast(built_in_members.size()); + if (num_builtin_members > 0 && num_builtin_members != num_struct_members) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "When BuiltIn decoration is applied to a structure-type member, " + << "all members of that structure type must also be decorated with " + << "BuiltIn (No allowed mixing of built-in variables and " + << "non-built-in variables within a single structure). Structure id " + << struct_id << " does not meet this requirement."; + } + if (num_builtin_members > 0) { + _.RegisterStructTypeWithBuiltInMember(struct_id); + } + + if (spvIsVulkanEnv(_.context()->target_env) && + !_.options()->before_hlsl_legalization && ContainsOpaqueType(_, inst)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "In " << spvLogStringForEnv(_.context()->target_env) + << ", OpTypeStruct must not contain an opaque type."; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypePointer(ValidationState_t& _, + const Instruction* inst) { + auto type_id = inst->GetOperandAs(2); + auto type = _.FindDef(type_id); + if (!type || !spvOpcodeGeneratesType(type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypePointer Type '" << _.getIdName(type_id) + << "' is not a type."; + } + // See if this points to a storage image. + const auto storage_class = inst->GetOperandAs(1); + if (storage_class == SpvStorageClassUniformConstant) { + // Unpack an optional level of arraying. + if (type->opcode() == SpvOpTypeArray || + type->opcode() == SpvOpTypeRuntimeArray) { + type_id = type->GetOperandAs(1); + type = _.FindDef(type_id); + } + if (type->opcode() == SpvOpTypeImage) { + const auto sampled = type->GetOperandAs(6); + // 2 indicates this image is known to be be used without a sampler, i.e. + // a storage image. + if (sampled == 2) _.RegisterPointerToStorageImage(inst->id()); + } + } + + if (!_.IsValidStorageClass(storage_class)) { + return _.diag(SPV_ERROR_INVALID_BINARY, inst) + << "Invalid storage class for target environment"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeFunction(ValidationState_t& _, + const Instruction* inst) { + const auto return_type_id = inst->GetOperandAs(1); + const auto return_type = _.FindDef(return_type_id); + if (!return_type || !spvOpcodeGeneratesType(return_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeFunction Return Type '" << _.getIdName(return_type_id) + << "' is not a type."; + } + size_t num_args = 0; + for (size_t param_type_index = 2; param_type_index < inst->operands().size(); + ++param_type_index, ++num_args) { + const auto param_id = inst->GetOperandAs(param_type_index); + const auto param_type = _.FindDef(param_id); + if (!param_type || !spvOpcodeGeneratesType(param_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeFunction Parameter Type '" << _.getIdName(param_id) + << "' is not a type."; + } + + if (param_type->opcode() == SpvOpTypeVoid) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeFunction Parameter Type '" << _.getIdName(param_id) + << "' cannot be OpTypeVoid."; + } + } + const uint32_t num_function_args_limit = + _.options()->universal_limits_.max_function_args; + if (num_args > num_function_args_limit) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeFunction may not take more than " + << num_function_args_limit << " arguments. OpTypeFunction '" + << _.getIdName(inst->GetOperandAs(0)) << "' has " + << num_args << " arguments."; + } + + // The only valid uses of OpTypeFunction are in an OpFunction, debugging, or + // decoration instruction. + for (auto& pair : inst->uses()) { + const auto* use = pair.first; + if (use->opcode() != SpvOpFunction && !spvOpcodeIsDebug(use->opcode()) && + !use->IsNonSemantic() && !spvOpcodeIsDecoration(use->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, use) + << "Invalid use of function type result id " + << _.getIdName(inst->id()) << "."; + } + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeForwardPointer(ValidationState_t& _, + const Instruction* inst) { + const auto pointer_type_id = inst->GetOperandAs(0); + const auto pointer_type_inst = _.FindDef(pointer_type_id); + if (pointer_type_inst->opcode() != SpvOpTypePointer) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Pointer type in OpTypeForwardPointer is not a pointer type."; + } + + if (inst->GetOperandAs(1) != + pointer_type_inst->GetOperandAs(1)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Storage class in OpTypeForwardPointer does not match the " + << "pointer definition."; + } + + const auto pointee_type_id = pointer_type_inst->GetOperandAs(2); + const auto pointee_type = _.FindDef(pointee_type_id); + if (!pointee_type || pointee_type->opcode() != SpvOpTypeStruct) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Forward pointers must point to a structure"; + } + + return SPV_SUCCESS; +} + +spv_result_t ValidateTypeCooperativeMatrixNV(ValidationState_t& _, + const Instruction* inst) { + const auto component_type_index = 1; + const auto component_type_id = + inst->GetOperandAs(component_type_index); + const auto component_type = _.FindDef(component_type_id); + if (!component_type || (SpvOpTypeFloat != component_type->opcode() && + SpvOpTypeInt != component_type->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrixNV Component Type '" + << _.getIdName(component_type_id) + << "' is not a scalar numerical type."; + } + + const auto scope_index = 2; + const auto scope_id = inst->GetOperandAs(scope_index); + const auto scope = _.FindDef(scope_id); + if (!scope || !_.IsIntScalarType(scope->type_id()) || + !spvOpcodeIsConstant(scope->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrixNV Scope '" << _.getIdName(scope_id) + << "' is not a constant instruction with scalar integer type."; + } + + const auto rows_index = 3; + const auto rows_id = inst->GetOperandAs(rows_index); + const auto rows = _.FindDef(rows_id); + if (!rows || !_.IsIntScalarType(rows->type_id()) || + !spvOpcodeIsConstant(rows->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrixNV Rows '" << _.getIdName(rows_id) + << "' is not a constant instruction with scalar integer type."; + } + + const auto cols_index = 4; + const auto cols_id = inst->GetOperandAs(cols_index); + const auto cols = _.FindDef(cols_id); + if (!cols || !_.IsIntScalarType(cols->type_id()) || + !spvOpcodeIsConstant(cols->opcode())) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "OpTypeCooperativeMatrixNV Cols '" << _.getIdName(rows_id) + << "' is not a constant instruction with scalar integer type."; + } + + return SPV_SUCCESS; +} +} // namespace + +spv_result_t TypePass(ValidationState_t& _, const Instruction* inst) { + if (!spvOpcodeGeneratesType(inst->opcode()) && + inst->opcode() != SpvOpTypeForwardPointer) { + return SPV_SUCCESS; + } + + if (auto error = ValidateUniqueness(_, inst)) return error; + + switch (inst->opcode()) { + case SpvOpTypeInt: + if (auto error = ValidateTypeInt(_, inst)) return error; + break; + case SpvOpTypeFloat: + if (auto error = ValidateTypeFloat(_, inst)) return error; + break; + case SpvOpTypeVector: + if (auto error = ValidateTypeVector(_, inst)) return error; + break; + case SpvOpTypeMatrix: + if (auto error = ValidateTypeMatrix(_, inst)) return error; + break; + case SpvOpTypeArray: + if (auto error = ValidateTypeArray(_, inst)) return error; + break; + case SpvOpTypeRuntimeArray: + if (auto error = ValidateTypeRuntimeArray(_, inst)) return error; + break; + case SpvOpTypeStruct: + if (auto error = ValidateTypeStruct(_, inst)) return error; + break; + case SpvOpTypePointer: + if (auto error = ValidateTypePointer(_, inst)) return error; + break; + case SpvOpTypeFunction: + if (auto error = ValidateTypeFunction(_, inst)) return error; + break; + case SpvOpTypeForwardPointer: + if (auto error = ValidateTypeForwardPointer(_, inst)) return error; + break; + case SpvOpTypeCooperativeMatrixNV: + if (auto error = ValidateTypeCooperativeMatrixNV(_, inst)) return error; + break; + default: + break; + } + + return SPV_SUCCESS; +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validation_state.cpp b/third_party/spirv-tools/source/val/validation_state.cpp new file mode 100644 index 0000000..cb69dda --- /dev/null +++ b/third_party/spirv-tools/source/val/validation_state.cpp @@ -0,0 +1,1513 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/val/validation_state.h" + +#include +#include +#include + +#include "source/opcode.h" +#include "source/spirv_constant.h" +#include "source/spirv_target_env.h" +#include "source/val/basic_block.h" +#include "source/val/construct.h" +#include "source/val/function.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { +namespace { + +ModuleLayoutSection InstructionLayoutSection( + ModuleLayoutSection current_section, SpvOp op) { + // See Section 2.4 + if (spvOpcodeGeneratesType(op) || spvOpcodeIsConstant(op)) + return kLayoutTypes; + + switch (op) { + case SpvOpCapability: + return kLayoutCapabilities; + case SpvOpExtension: + return kLayoutExtensions; + case SpvOpExtInstImport: + return kLayoutExtInstImport; + case SpvOpMemoryModel: + return kLayoutMemoryModel; + case SpvOpEntryPoint: + return kLayoutEntryPoint; + case SpvOpExecutionMode: + case SpvOpExecutionModeId: + return kLayoutExecutionMode; + case SpvOpSourceContinued: + case SpvOpSource: + case SpvOpSourceExtension: + case SpvOpString: + return kLayoutDebug1; + case SpvOpName: + case SpvOpMemberName: + return kLayoutDebug2; + case SpvOpModuleProcessed: + return kLayoutDebug3; + case SpvOpDecorate: + case SpvOpMemberDecorate: + case SpvOpGroupDecorate: + case SpvOpGroupMemberDecorate: + case SpvOpDecorationGroup: + case SpvOpDecorateId: + case SpvOpDecorateStringGOOGLE: + case SpvOpMemberDecorateStringGOOGLE: + return kLayoutAnnotations; + case SpvOpTypeForwardPointer: + return kLayoutTypes; + case SpvOpVariable: + if (current_section == kLayoutTypes) return kLayoutTypes; + return kLayoutFunctionDefinitions; + case SpvOpExtInst: + // SpvOpExtInst is only allowed in types section for certain extended + // instruction sets. This will be checked separately. + if (current_section == kLayoutTypes) return kLayoutTypes; + return kLayoutFunctionDefinitions; + case SpvOpLine: + case SpvOpNoLine: + case SpvOpUndef: + if (current_section == kLayoutTypes) return kLayoutTypes; + return kLayoutFunctionDefinitions; + case SpvOpFunction: + case SpvOpFunctionParameter: + case SpvOpFunctionEnd: + if (current_section == kLayoutFunctionDeclarations) + return kLayoutFunctionDeclarations; + return kLayoutFunctionDefinitions; + default: + break; + } + return kLayoutFunctionDefinitions; +} + +bool IsInstructionInLayoutSection(ModuleLayoutSection layout, SpvOp op) { + return layout == InstructionLayoutSection(layout, op); +} + +// Counts the number of instructions and functions in the file. +spv_result_t CountInstructions(void* user_data, + const spv_parsed_instruction_t* inst) { + ValidationState_t& _ = *(reinterpret_cast(user_data)); + if (inst->opcode == SpvOpFunction) _.increment_total_functions(); + _.increment_total_instructions(); + + return SPV_SUCCESS; +} + +spv_result_t setHeader(void* user_data, spv_endianness_t, uint32_t, + uint32_t version, uint32_t generator, uint32_t id_bound, + uint32_t) { + ValidationState_t& vstate = + *(reinterpret_cast(user_data)); + vstate.setIdBound(id_bound); + vstate.setGenerator(generator); + vstate.setVersion(version); + + return SPV_SUCCESS; +} + +// Add features based on SPIR-V core version number. +void UpdateFeaturesBasedOnSpirvVersion(ValidationState_t::Feature* features, + uint32_t version) { + assert(features); + if (version >= SPV_SPIRV_VERSION_WORD(1, 4)) { + features->select_between_composites = true; + features->copy_memory_permits_two_memory_accesses = true; + features->uconvert_spec_constant_op = true; + features->nonwritable_var_in_function_or_private = true; + } +} + +} // namespace + +ValidationState_t::ValidationState_t(const spv_const_context ctx, + const spv_const_validator_options opt, + const uint32_t* words, + const size_t num_words, + const uint32_t max_warnings) + : context_(ctx), + options_(opt), + words_(words), + num_words_(num_words), + unresolved_forward_ids_{}, + operand_names_{}, + current_layout_section_(kLayoutCapabilities), + module_functions_(), + module_capabilities_(), + module_extensions_(), + ordered_instructions_(), + all_definitions_(), + global_vars_(), + local_vars_(), + struct_nesting_depth_(), + struct_has_nested_blockorbufferblock_struct_(), + grammar_(ctx), + addressing_model_(SpvAddressingModelMax), + memory_model_(SpvMemoryModelMax), + pointer_size_and_alignment_(0), + in_function_(false), + num_of_warnings_(0), + max_num_of_warnings_(max_warnings) { + assert(opt && "Validator options may not be Null."); + + const auto env = context_->target_env; + + if (spvIsVulkanEnv(env)) { + // Vulkan 1.1 includes VK_KHR_relaxed_block_layout in core. + if (env != SPV_ENV_VULKAN_1_0) { + features_.env_relaxed_block_layout = true; + } + } + + // Only attempt to count if we have words, otherwise let the other validation + // fail and generate an error. + if (num_words > 0) { + // Count the number of instructions in the binary. + // This parse should not produce any error messages. Hijack the context and + // replace the message consumer so that we do not pollute any state in input + // consumer. + spv_context_t hijacked_context = *ctx; + hijacked_context.consumer = [](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}; + spvBinaryParse(&hijacked_context, this, words, num_words, setHeader, + CountInstructions, + /* diagnostic = */ nullptr); + preallocateStorage(); + } + UpdateFeaturesBasedOnSpirvVersion(&features_, version_); + + friendly_mapper_ = spvtools::MakeUnique( + context_, words_, num_words_); + name_mapper_ = friendly_mapper_->GetNameMapper(); +} + +void ValidationState_t::preallocateStorage() { + ordered_instructions_.reserve(total_instructions_); + module_functions_.reserve(total_functions_); +} + +spv_result_t ValidationState_t::ForwardDeclareId(uint32_t id) { + unresolved_forward_ids_.insert(id); + return SPV_SUCCESS; +} + +spv_result_t ValidationState_t::RemoveIfForwardDeclared(uint32_t id) { + unresolved_forward_ids_.erase(id); + return SPV_SUCCESS; +} + +spv_result_t ValidationState_t::RegisterForwardPointer(uint32_t id) { + forward_pointer_ids_.insert(id); + return SPV_SUCCESS; +} + +bool ValidationState_t::IsForwardPointer(uint32_t id) const { + return (forward_pointer_ids_.find(id) != forward_pointer_ids_.end()); +} + +void ValidationState_t::AssignNameToId(uint32_t id, std::string name) { + operand_names_[id] = name; +} + +std::string ValidationState_t::getIdName(uint32_t id) const { + const std::string id_name = name_mapper_(id); + + std::stringstream out; + out << id << "[%" << id_name << "]"; + return out.str(); +} + +size_t ValidationState_t::unresolved_forward_id_count() const { + return unresolved_forward_ids_.size(); +} + +std::vector ValidationState_t::UnresolvedForwardIds() const { + std::vector out(std::begin(unresolved_forward_ids_), + std::end(unresolved_forward_ids_)); + return out; +} + +bool ValidationState_t::IsDefinedId(uint32_t id) const { + return all_definitions_.find(id) != std::end(all_definitions_); +} + +const Instruction* ValidationState_t::FindDef(uint32_t id) const { + auto it = all_definitions_.find(id); + if (it == all_definitions_.end()) return nullptr; + return it->second; +} + +Instruction* ValidationState_t::FindDef(uint32_t id) { + auto it = all_definitions_.find(id); + if (it == all_definitions_.end()) return nullptr; + return it->second; +} + +ModuleLayoutSection ValidationState_t::current_layout_section() const { + return current_layout_section_; +} + +void ValidationState_t::ProgressToNextLayoutSectionOrder() { + // Guard against going past the last element(kLayoutFunctionDefinitions) + if (current_layout_section_ <= kLayoutFunctionDefinitions) { + current_layout_section_ = + static_cast(current_layout_section_ + 1); + } +} + +bool ValidationState_t::IsOpcodeInPreviousLayoutSection(SpvOp op) { + ModuleLayoutSection section = + InstructionLayoutSection(current_layout_section_, op); + return section < current_layout_section_; +} + +bool ValidationState_t::IsOpcodeInCurrentLayoutSection(SpvOp op) { + return IsInstructionInLayoutSection(current_layout_section_, op); +} + +DiagnosticStream ValidationState_t::diag(spv_result_t error_code, + const Instruction* inst) { + if (error_code == SPV_WARNING) { + if (num_of_warnings_ == max_num_of_warnings_) { + DiagnosticStream({0, 0, 0}, context_->consumer, "", error_code) + << "Other warnings have been suppressed.\n"; + } + if (num_of_warnings_ >= max_num_of_warnings_) { + return DiagnosticStream({0, 0, 0}, nullptr, "", error_code); + } + ++num_of_warnings_; + } + + std::string disassembly; + if (inst) disassembly = Disassemble(*inst); + + return DiagnosticStream({0, 0, inst ? inst->LineNum() : 0}, + context_->consumer, disassembly, error_code); +} + +std::vector& ValidationState_t::functions() { + return module_functions_; +} + +Function& ValidationState_t::current_function() { + assert(in_function_body()); + return module_functions_.back(); +} + +const Function& ValidationState_t::current_function() const { + assert(in_function_body()); + return module_functions_.back(); +} + +const Function* ValidationState_t::function(uint32_t id) const { + const auto it = id_to_function_.find(id); + if (it == id_to_function_.end()) return nullptr; + return it->second; +} + +Function* ValidationState_t::function(uint32_t id) { + auto it = id_to_function_.find(id); + if (it == id_to_function_.end()) return nullptr; + return it->second; +} + +bool ValidationState_t::in_function_body() const { return in_function_; } + +bool ValidationState_t::in_block() const { + return module_functions_.empty() == false && + module_functions_.back().current_block() != nullptr; +} + +void ValidationState_t::RegisterCapability(SpvCapability cap) { + // Avoid redundant work. Otherwise the recursion could induce work + // quadrdatic in the capability dependency depth. (Ok, not much, but + // it's something.) + if (module_capabilities_.Contains(cap)) return; + + module_capabilities_.Add(cap); + spv_operand_desc desc; + if (SPV_SUCCESS == + grammar_.lookupOperand(SPV_OPERAND_TYPE_CAPABILITY, cap, &desc)) { + CapabilitySet(desc->numCapabilities, desc->capabilities) + .ForEach([this](SpvCapability c) { RegisterCapability(c); }); + } + + switch (cap) { + case SpvCapabilityKernel: + features_.group_ops_reduce_and_scans = true; + break; + case SpvCapabilityInt8: + features_.use_int8_type = true; + features_.declare_int8_type = true; + break; + case SpvCapabilityStorageBuffer8BitAccess: + case SpvCapabilityUniformAndStorageBuffer8BitAccess: + case SpvCapabilityStoragePushConstant8: + features_.declare_int8_type = true; + break; + case SpvCapabilityInt16: + features_.declare_int16_type = true; + break; + case SpvCapabilityFloat16: + case SpvCapabilityFloat16Buffer: + features_.declare_float16_type = true; + break; + case SpvCapabilityStorageUniformBufferBlock16: + case SpvCapabilityStorageUniform16: + case SpvCapabilityStoragePushConstant16: + case SpvCapabilityStorageInputOutput16: + features_.declare_int16_type = true; + features_.declare_float16_type = true; + features_.free_fp_rounding_mode = true; + break; + case SpvCapabilityVariablePointers: + features_.variable_pointers = true; + features_.variable_pointers_storage_buffer = true; + break; + case SpvCapabilityVariablePointersStorageBuffer: + features_.variable_pointers_storage_buffer = true; + break; + default: + break; + } +} + +void ValidationState_t::RegisterExtension(Extension ext) { + if (module_extensions_.Contains(ext)) return; + + module_extensions_.Add(ext); + + switch (ext) { + case kSPV_AMD_gpu_shader_half_float: + case kSPV_AMD_gpu_shader_half_float_fetch: + // SPV_AMD_gpu_shader_half_float enables float16 type. + // https://github.com/KhronosGroup/SPIRV-Tools/issues/1375 + features_.declare_float16_type = true; + break; + case kSPV_AMD_gpu_shader_int16: + // This is not yet in the extension, but it's recommended for it. + // See https://github.com/KhronosGroup/glslang/issues/848 + features_.uconvert_spec_constant_op = true; + break; + case kSPV_AMD_shader_ballot: + // The grammar doesn't encode the fact that SPV_AMD_shader_ballot + // enables the use of group operations Reduce, InclusiveScan, + // and ExclusiveScan. Enable it manually. + // https://github.com/KhronosGroup/SPIRV-Tools/issues/991 + features_.group_ops_reduce_and_scans = true; + break; + default: + break; + } +} + +bool ValidationState_t::HasAnyOfCapabilities( + const CapabilitySet& capabilities) const { + return module_capabilities_.HasAnyOf(capabilities); +} + +bool ValidationState_t::HasAnyOfExtensions( + const ExtensionSet& extensions) const { + return module_extensions_.HasAnyOf(extensions); +} + +void ValidationState_t::set_addressing_model(SpvAddressingModel am) { + addressing_model_ = am; + switch (am) { + case SpvAddressingModelPhysical32: + pointer_size_and_alignment_ = 4; + break; + default: + // fall through + case SpvAddressingModelPhysical64: + case SpvAddressingModelPhysicalStorageBuffer64EXT: + pointer_size_and_alignment_ = 8; + break; + } +} + +SpvAddressingModel ValidationState_t::addressing_model() const { + return addressing_model_; +} + +void ValidationState_t::set_memory_model(SpvMemoryModel mm) { + memory_model_ = mm; +} + +SpvMemoryModel ValidationState_t::memory_model() const { return memory_model_; } + +spv_result_t ValidationState_t::RegisterFunction( + uint32_t id, uint32_t ret_type_id, SpvFunctionControlMask function_control, + uint32_t function_type_id) { + assert(in_function_body() == false && + "RegisterFunction can only be called when parsing the binary outside " + "of another function"); + in_function_ = true; + module_functions_.emplace_back(id, ret_type_id, function_control, + function_type_id); + id_to_function_.emplace(id, ¤t_function()); + + // TODO(umar): validate function type and type_id + + return SPV_SUCCESS; +} + +spv_result_t ValidationState_t::RegisterFunctionEnd() { + assert(in_function_body() == true && + "RegisterFunctionEnd can only be called when parsing the binary " + "inside of another function"); + assert(in_block() == false && + "RegisterFunctionParameter can only be called when parsing the binary " + "ouside of a block"); + current_function().RegisterFunctionEnd(); + in_function_ = false; + return SPV_SUCCESS; +} + +Instruction* ValidationState_t::AddOrderedInstruction( + const spv_parsed_instruction_t* inst) { + ordered_instructions_.emplace_back(inst); + ordered_instructions_.back().SetLineNum(ordered_instructions_.size()); + return &ordered_instructions_.back(); +} + +// Improves diagnostic messages by collecting names of IDs +void ValidationState_t::RegisterDebugInstruction(const Instruction* inst) { + switch (inst->opcode()) { + case SpvOpName: { + const auto target = inst->GetOperandAs(0); + const auto* str = reinterpret_cast(inst->words().data() + + inst->operand(1).offset); + AssignNameToId(target, str); + break; + } + case SpvOpMemberName: { + const auto target = inst->GetOperandAs(0); + const auto* str = reinterpret_cast(inst->words().data() + + inst->operand(2).offset); + AssignNameToId(target, str); + break; + } + case SpvOpSourceContinued: + case SpvOpSource: + case SpvOpSourceExtension: + case SpvOpString: + case SpvOpLine: + case SpvOpNoLine: + default: + break; + } +} + +void ValidationState_t::RegisterInstruction(Instruction* inst) { + if (inst->id()) all_definitions_.insert(std::make_pair(inst->id(), inst)); + + // If the instruction is using an OpTypeSampledImage as an operand, it should + // be recorded. The validator will ensure that all usages of an + // OpTypeSampledImage and its definition are in the same basic block. + for (uint16_t i = 0; i < inst->operands().size(); ++i) { + const spv_parsed_operand_t& operand = inst->operand(i); + if (SPV_OPERAND_TYPE_ID == operand.type) { + const uint32_t operand_word = inst->word(operand.offset); + Instruction* operand_inst = FindDef(operand_word); + if (operand_inst && SpvOpSampledImage == operand_inst->opcode()) { + RegisterSampledImageConsumer(operand_word, inst); + } + } + } +} + +std::vector ValidationState_t::getSampledImageConsumers( + uint32_t sampled_image_id) const { + std::vector result; + auto iter = sampled_image_consumers_.find(sampled_image_id); + if (iter != sampled_image_consumers_.end()) { + result = iter->second; + } + return result; +} + +void ValidationState_t::RegisterSampledImageConsumer(uint32_t sampled_image_id, + Instruction* consumer) { + sampled_image_consumers_[sampled_image_id].push_back(consumer); +} + +uint32_t ValidationState_t::getIdBound() const { return id_bound_; } + +void ValidationState_t::setIdBound(const uint32_t bound) { id_bound_ = bound; } + +bool ValidationState_t::RegisterUniqueTypeDeclaration(const Instruction* inst) { + std::vector key; + key.push_back(static_cast(inst->opcode())); + for (size_t index = 0; index < inst->operands().size(); ++index) { + const spv_parsed_operand_t& operand = inst->operand(index); + + if (operand.type == SPV_OPERAND_TYPE_RESULT_ID) continue; + + const int words_begin = operand.offset; + const int words_end = words_begin + operand.num_words; + assert(words_end <= static_cast(inst->words().size())); + + key.insert(key.end(), inst->words().begin() + words_begin, + inst->words().begin() + words_end); + } + + return unique_type_declarations_.insert(std::move(key)).second; +} + +uint32_t ValidationState_t::GetTypeId(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst ? inst->type_id() : 0; +} + +SpvOp ValidationState_t::GetIdOpcode(uint32_t id) const { + const Instruction* inst = FindDef(id); + return inst ? inst->opcode() : SpvOpNop; +} + +uint32_t ValidationState_t::GetComponentType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + switch (inst->opcode()) { + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeBool: + return id; + + case SpvOpTypeVector: + return inst->word(2); + + case SpvOpTypeMatrix: + return GetComponentType(inst->word(2)); + + case SpvOpTypeCooperativeMatrixNV: + return inst->word(2); + + default: + break; + } + + if (inst->type_id()) return GetComponentType(inst->type_id()); + + assert(0); + return 0; +} + +uint32_t ValidationState_t::GetDimension(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + switch (inst->opcode()) { + case SpvOpTypeFloat: + case SpvOpTypeInt: + case SpvOpTypeBool: + return 1; + + case SpvOpTypeVector: + case SpvOpTypeMatrix: + return inst->word(3); + + case SpvOpTypeCooperativeMatrixNV: + // Actual dimension isn't known, return 0 + return 0; + + default: + break; + } + + if (inst->type_id()) return GetDimension(inst->type_id()); + + assert(0); + return 0; +} + +uint32_t ValidationState_t::GetBitWidth(uint32_t id) const { + const uint32_t component_type_id = GetComponentType(id); + const Instruction* inst = FindDef(component_type_id); + assert(inst); + + if (inst->opcode() == SpvOpTypeFloat || inst->opcode() == SpvOpTypeInt) + return inst->word(2); + + if (inst->opcode() == SpvOpTypeBool) return 1; + + assert(0); + return 0; +} + +bool ValidationState_t::IsVoidType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == SpvOpTypeVoid; +} + +bool ValidationState_t::IsFloatScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == SpvOpTypeFloat; +} + +bool ValidationState_t::IsFloatVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeVector) { + return IsFloatScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsFloatScalarOrVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeFloat) { + return true; + } + + if (inst->opcode() == SpvOpTypeVector) { + return IsFloatScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsIntScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == SpvOpTypeInt; +} + +bool ValidationState_t::IsIntVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeVector) { + return IsIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsIntScalarOrVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeInt) { + return true; + } + + if (inst->opcode() == SpvOpTypeVector) { + return IsIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsUnsignedIntScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == SpvOpTypeInt && inst->word(3) == 0; +} + +bool ValidationState_t::IsUnsignedIntVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeVector) { + return IsUnsignedIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsSignedIntScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == SpvOpTypeInt && inst->word(3) == 1; +} + +bool ValidationState_t::IsSignedIntVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeVector) { + return IsSignedIntScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsBoolScalarType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == SpvOpTypeBool; +} + +bool ValidationState_t::IsBoolVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeVector) { + return IsBoolScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsBoolScalarOrVectorType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeBool) { + return true; + } + + if (inst->opcode() == SpvOpTypeVector) { + return IsBoolScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::IsFloatMatrixType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + + if (inst->opcode() == SpvOpTypeMatrix) { + return IsFloatScalarType(GetComponentType(id)); + } + + return false; +} + +bool ValidationState_t::GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows, + uint32_t* num_cols, + uint32_t* column_type, + uint32_t* component_type) const { + if (!id) return false; + + const Instruction* mat_inst = FindDef(id); + assert(mat_inst); + if (mat_inst->opcode() != SpvOpTypeMatrix) return false; + + const uint32_t vec_type = mat_inst->word(2); + const Instruction* vec_inst = FindDef(vec_type); + assert(vec_inst); + + if (vec_inst->opcode() != SpvOpTypeVector) { + assert(0); + return false; + } + + *num_cols = mat_inst->word(3); + *num_rows = vec_inst->word(3); + *column_type = mat_inst->word(2); + *component_type = vec_inst->word(2); + + return true; +} + +bool ValidationState_t::GetStructMemberTypes( + uint32_t struct_type_id, std::vector* member_types) const { + member_types->clear(); + if (!struct_type_id) return false; + + const Instruction* inst = FindDef(struct_type_id); + assert(inst); + if (inst->opcode() != SpvOpTypeStruct) return false; + + *member_types = + std::vector(inst->words().cbegin() + 2, inst->words().cend()); + + if (member_types->empty()) return false; + + return true; +} + +bool ValidationState_t::IsPointerType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == SpvOpTypePointer; +} + +bool ValidationState_t::GetPointerTypeInfo(uint32_t id, uint32_t* data_type, + uint32_t* storage_class) const { + if (!id) return false; + + const Instruction* inst = FindDef(id); + assert(inst); + if (inst->opcode() != SpvOpTypePointer) return false; + + *storage_class = inst->word(2); + *data_type = inst->word(3); + return true; +} + +bool ValidationState_t::IsCooperativeMatrixType(uint32_t id) const { + const Instruction* inst = FindDef(id); + assert(inst); + return inst->opcode() == SpvOpTypeCooperativeMatrixNV; +} + +bool ValidationState_t::IsFloatCooperativeMatrixType(uint32_t id) const { + if (!IsCooperativeMatrixType(id)) return false; + return IsFloatScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsIntCooperativeMatrixType(uint32_t id) const { + if (!IsCooperativeMatrixType(id)) return false; + return IsIntScalarType(FindDef(id)->word(2)); +} + +bool ValidationState_t::IsUnsignedIntCooperativeMatrixType(uint32_t id) const { + if (!IsCooperativeMatrixType(id)) return false; + return IsUnsignedIntScalarType(FindDef(id)->word(2)); +} + +spv_result_t ValidationState_t::CooperativeMatrixShapesMatch( + const Instruction* inst, uint32_t m1, uint32_t m2) { + const auto m1_type = FindDef(m1); + const auto m2_type = FindDef(m2); + + if (m1_type->opcode() != SpvOpTypeCooperativeMatrixNV || + m2_type->opcode() != SpvOpTypeCooperativeMatrixNV) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected cooperative matrix types"; + } + + uint32_t m1_scope_id = m1_type->GetOperandAs(2); + uint32_t m1_rows_id = m1_type->GetOperandAs(3); + uint32_t m1_cols_id = m1_type->GetOperandAs(4); + + uint32_t m2_scope_id = m2_type->GetOperandAs(2); + uint32_t m2_rows_id = m2_type->GetOperandAs(3); + uint32_t m2_cols_id = m2_type->GetOperandAs(4); + + bool m1_is_int32 = false, m1_is_const_int32 = false, m2_is_int32 = false, + m2_is_const_int32 = false; + uint32_t m1_value = 0, m2_value = 0; + + std::tie(m1_is_int32, m1_is_const_int32, m1_value) = + EvalInt32IfConst(m1_scope_id); + std::tie(m2_is_int32, m2_is_const_int32, m2_value) = + EvalInt32IfConst(m2_scope_id); + + if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected scopes of Matrix and Result Type to be " + << "identical"; + } + + std::tie(m1_is_int32, m1_is_const_int32, m1_value) = + EvalInt32IfConst(m1_rows_id); + std::tie(m2_is_int32, m2_is_const_int32, m2_value) = + EvalInt32IfConst(m2_rows_id); + + if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected rows of Matrix type and Result Type to be " + << "identical"; + } + + std::tie(m1_is_int32, m1_is_const_int32, m1_value) = + EvalInt32IfConst(m1_cols_id); + std::tie(m2_is_int32, m2_is_const_int32, m2_value) = + EvalInt32IfConst(m2_cols_id); + + if (m1_is_const_int32 && m2_is_const_int32 && m1_value != m2_value) { + return diag(SPV_ERROR_INVALID_DATA, inst) + << "Expected columns of Matrix type and Result Type to be " + << "identical"; + } + + return SPV_SUCCESS; +} + +uint32_t ValidationState_t::GetOperandTypeId(const Instruction* inst, + size_t operand_index) const { + return GetTypeId(inst->GetOperandAs(operand_index)); +} + +bool ValidationState_t::GetConstantValUint64(uint32_t id, uint64_t* val) const { + const Instruction* inst = FindDef(id); + if (!inst) { + assert(0 && "Instruction not found"); + return false; + } + + if (inst->opcode() != SpvOpConstant && inst->opcode() != SpvOpSpecConstant) + return false; + + if (!IsIntScalarType(inst->type_id())) return false; + + if (inst->words().size() == 4) { + *val = inst->word(3); + } else { + assert(inst->words().size() == 5); + *val = inst->word(3); + *val |= uint64_t(inst->word(4)) << 32; + } + return true; +} + +std::tuple ValidationState_t::EvalInt32IfConst( + uint32_t id) const { + const Instruction* const inst = FindDef(id); + assert(inst); + const uint32_t type = inst->type_id(); + + if (type == 0 || !IsIntScalarType(type) || GetBitWidth(type) != 32) { + return std::make_tuple(false, false, 0); + } + + // Spec constant values cannot be evaluated so don't consider constant for + // the purpose of this method. + if (!spvOpcodeIsConstant(inst->opcode()) || + spvOpcodeIsSpecConstant(inst->opcode())) { + return std::make_tuple(true, false, 0); + } + + if (inst->opcode() == SpvOpConstantNull) { + return std::make_tuple(true, true, 0); + } + + assert(inst->words().size() == 4); + return std::make_tuple(true, true, inst->word(3)); +} + +void ValidationState_t::ComputeFunctionToEntryPointMapping() { + for (const uint32_t entry_point : entry_points()) { + std::stack call_stack; + std::set visited; + call_stack.push(entry_point); + while (!call_stack.empty()) { + const uint32_t called_func_id = call_stack.top(); + call_stack.pop(); + if (!visited.insert(called_func_id).second) continue; + + function_to_entry_points_[called_func_id].push_back(entry_point); + + const Function* called_func = function(called_func_id); + if (called_func) { + // Other checks should error out on this invalid SPIR-V. + for (const uint32_t new_call : called_func->function_call_targets()) { + call_stack.push(new_call); + } + } + } + } +} + +void ValidationState_t::ComputeRecursiveEntryPoints() { + for (const Function& func : functions()) { + std::stack call_stack; + std::set visited; + + for (const uint32_t new_call : func.function_call_targets()) { + call_stack.push(new_call); + } + + while (!call_stack.empty()) { + const uint32_t called_func_id = call_stack.top(); + call_stack.pop(); + + if (!visited.insert(called_func_id).second) continue; + + if (called_func_id == func.id()) { + for (const uint32_t entry_point : + function_to_entry_points_[called_func_id]) + recursive_entry_points_.insert(entry_point); + break; + } + + const Function* called_func = function(called_func_id); + if (called_func) { + // Other checks should error out on this invalid SPIR-V. + for (const uint32_t new_call : called_func->function_call_targets()) { + call_stack.push(new_call); + } + } + } + } +} + +const std::vector& ValidationState_t::FunctionEntryPoints( + uint32_t func) const { + auto iter = function_to_entry_points_.find(func); + if (iter == function_to_entry_points_.end()) { + return empty_ids_; + } else { + return iter->second; + } +} + +std::set ValidationState_t::EntryPointReferences(uint32_t id) const { + std::set referenced_entry_points; + const auto inst = FindDef(id); + if (!inst) return referenced_entry_points; + + std::vector stack; + stack.push_back(inst); + while (!stack.empty()) { + const auto current_inst = stack.back(); + stack.pop_back(); + + if (const auto func = current_inst->function()) { + // Instruction lives in a function, we can stop searching. + const auto function_entry_points = FunctionEntryPoints(func->id()); + referenced_entry_points.insert(function_entry_points.begin(), + function_entry_points.end()); + } else { + // Instruction is in the global scope, keep searching its uses. + for (auto pair : current_inst->uses()) { + const auto next_inst = pair.first; + stack.push_back(next_inst); + } + } + } + + return referenced_entry_points; +} + +std::string ValidationState_t::Disassemble(const Instruction& inst) const { + const spv_parsed_instruction_t& c_inst(inst.c_inst()); + return Disassemble(c_inst.words, c_inst.num_words); +} + +std::string ValidationState_t::Disassemble(const uint32_t* words, + uint16_t num_words) const { + uint32_t disassembly_options = SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES; + + return spvInstructionBinaryToText(context()->target_env, words, num_words, + words_, num_words_, disassembly_options); +} + +bool ValidationState_t::LogicallyMatch(const Instruction* lhs, + const Instruction* rhs, + bool check_decorations) { + if (lhs->opcode() != rhs->opcode()) { + return false; + } + + if (check_decorations) { + const auto& dec_a = id_decorations(lhs->id()); + const auto& dec_b = id_decorations(rhs->id()); + + for (const auto& dec : dec_b) { + if (std::find(dec_a.begin(), dec_a.end(), dec) == dec_a.end()) { + return false; + } + } + } + + if (lhs->opcode() == SpvOpTypeArray) { + // Size operands must match. + if (lhs->GetOperandAs(2u) != rhs->GetOperandAs(2u)) { + return false; + } + + // Elements must match or logically match. + const auto lhs_ele_id = lhs->GetOperandAs(1u); + const auto rhs_ele_id = rhs->GetOperandAs(1u); + if (lhs_ele_id == rhs_ele_id) { + return true; + } + + const auto lhs_ele = FindDef(lhs_ele_id); + const auto rhs_ele = FindDef(rhs_ele_id); + if (!lhs_ele || !rhs_ele) { + return false; + } + return LogicallyMatch(lhs_ele, rhs_ele, check_decorations); + } else if (lhs->opcode() == SpvOpTypeStruct) { + // Number of elements must match. + if (lhs->operands().size() != rhs->operands().size()) { + return false; + } + + for (size_t i = 1u; i < lhs->operands().size(); ++i) { + const auto lhs_ele_id = lhs->GetOperandAs(i); + const auto rhs_ele_id = rhs->GetOperandAs(i); + // Elements must match or logically match. + if (lhs_ele_id == rhs_ele_id) { + continue; + } + + const auto lhs_ele = FindDef(lhs_ele_id); + const auto rhs_ele = FindDef(rhs_ele_id); + if (!lhs_ele || !rhs_ele) { + return false; + } + + if (!LogicallyMatch(lhs_ele, rhs_ele, check_decorations)) { + return false; + } + } + + // All checks passed. + return true; + } + + // No other opcodes are acceptable at this point. Arrays and structs are + // caught above and if they're elements are not arrays or structs they are + // required to match exactly. + return false; +} + +const Instruction* ValidationState_t::TracePointer( + const Instruction* inst) const { + auto base_ptr = inst; + while (base_ptr->opcode() == SpvOpAccessChain || + base_ptr->opcode() == SpvOpInBoundsAccessChain || + base_ptr->opcode() == SpvOpPtrAccessChain || + base_ptr->opcode() == SpvOpInBoundsPtrAccessChain || + base_ptr->opcode() == SpvOpCopyObject) { + base_ptr = FindDef(base_ptr->GetOperandAs(2u)); + } + return base_ptr; +} + +bool ValidationState_t::ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, + uint32_t width) const { + if (type != SpvOpTypeInt && type != SpvOpTypeFloat) return false; + + const auto inst = FindDef(id); + if (!inst) return false; + + if (inst->opcode() == type) { + return inst->GetOperandAs(1u) == width; + } + + switch (inst->opcode()) { + case SpvOpTypeArray: + case SpvOpTypeRuntimeArray: + case SpvOpTypeVector: + case SpvOpTypeMatrix: + case SpvOpTypeImage: + case SpvOpTypeSampledImage: + case SpvOpTypeCooperativeMatrixNV: + return ContainsSizedIntOrFloatType(inst->GetOperandAs(1u), type, + width); + case SpvOpTypePointer: + if (IsForwardPointer(id)) return false; + return ContainsSizedIntOrFloatType(inst->GetOperandAs(2u), type, + width); + case SpvOpTypeFunction: + case SpvOpTypeStruct: { + for (uint32_t i = 1; i < inst->operands().size(); ++i) { + if (ContainsSizedIntOrFloatType(inst->GetOperandAs(i), type, + width)) + return true; + } + return false; + } + default: + return false; + } +} + +bool ValidationState_t::ContainsLimitedUseIntOrFloatType(uint32_t id) const { + if ((!HasCapability(SpvCapabilityInt16) && + ContainsSizedIntOrFloatType(id, SpvOpTypeInt, 16)) || + (!HasCapability(SpvCapabilityInt8) && + ContainsSizedIntOrFloatType(id, SpvOpTypeInt, 8)) || + (!HasCapability(SpvCapabilityFloat16) && + ContainsSizedIntOrFloatType(id, SpvOpTypeFloat, 16))) { + return true; + } + return false; +} + +bool ValidationState_t::IsValidStorageClass( + SpvStorageClass storage_class) const { + if (spvIsWebGPUEnv(context()->target_env)) { + switch (storage_class) { + case SpvStorageClassUniformConstant: + case SpvStorageClassUniform: + case SpvStorageClassStorageBuffer: + case SpvStorageClassInput: + case SpvStorageClassOutput: + case SpvStorageClassImage: + case SpvStorageClassWorkgroup: + case SpvStorageClassPrivate: + case SpvStorageClassFunction: + return true; + default: + return false; + } + } + + if (spvIsVulkanEnv(context()->target_env)) { + switch (storage_class) { + case SpvStorageClassUniformConstant: + case SpvStorageClassUniform: + case SpvStorageClassStorageBuffer: + case SpvStorageClassInput: + case SpvStorageClassOutput: + case SpvStorageClassImage: + case SpvStorageClassWorkgroup: + case SpvStorageClassPrivate: + case SpvStorageClassFunction: + case SpvStorageClassPushConstant: + case SpvStorageClassPhysicalStorageBuffer: + case SpvStorageClassRayPayloadNV: + case SpvStorageClassIncomingRayPayloadNV: + case SpvStorageClassHitAttributeNV: + case SpvStorageClassCallableDataNV: + case SpvStorageClassIncomingCallableDataNV: + case SpvStorageClassShaderRecordBufferNV: + return true; + default: + return false; + } + } + + return true; +} + +#define VUID_WRAP(vuid) "[" #vuid "] " + +// Currently no 2 VUID share the same id, so no need for |reference| +std::string ValidationState_t::VkErrorID(uint32_t id, + const char* /*reference*/) const { + if (!spvIsVulkanEnv(context_->target_env)) { + return ""; + } + + // This large switch case is only searched when an error has occured. + // If an id is changed, the old case must be modified or removed. Each string + // here is interpreted as being "implemented" + + // Clang format adds spaces between hyphens + // clang-format off + switch (id) { + case 4181: + return VUID_WRAP(VUID-BaseInstance-BaseInstance-04181); + case 4182: + return VUID_WRAP(VUID-BaseInstance-BaseInstance-04182); + case 4183: + return VUID_WRAP(VUID-BaseInstance-BaseInstance-04183); + case 4184: + return VUID_WRAP(VUID-BaseVertex-BaseVertex-04184); + case 4185: + return VUID_WRAP(VUID-BaseVertex-BaseVertex-04185); + case 4186: + return VUID_WRAP(VUID-BaseVertex-BaseVertex-04186); + case 4187: + return VUID_WRAP(VUID-ClipDistance-ClipDistance-04187); + case 4191: + return VUID_WRAP(VUID-ClipDistance-ClipDistance-04191); + case 4196: + return VUID_WRAP(VUID-CullDistance-CullDistance-04196); + case 4200: + return VUID_WRAP(VUID-CullDistance-CullDistance-04200); + case 4205: + return VUID_WRAP(VUID-DeviceIndex-DeviceIndex-04205); + case 4206: + return VUID_WRAP(VUID-DeviceIndex-DeviceIndex-04206); + case 4207: + return VUID_WRAP(VUID-DrawIndex-DrawIndex-04207); + case 4208: + return VUID_WRAP(VUID-DrawIndex-DrawIndex-04208); + case 4209: + return VUID_WRAP(VUID-DrawIndex-DrawIndex-04209); + case 4210: + return VUID_WRAP(VUID-FragCoord-FragCoord-04210); + case 4211: + return VUID_WRAP(VUID-FragCoord-FragCoord-04211); + case 4212: + return VUID_WRAP(VUID-FragCoord-FragCoord-04212); + case 4213: + return VUID_WRAP(VUID-FragDepth-FragDepth-04213); + case 4214: + return VUID_WRAP(VUID-FragDepth-FragDepth-04214); + case 4215: + return VUID_WRAP(VUID-FragDepth-FragDepth-04215); + case 4216: + return VUID_WRAP(VUID-FragDepth-FragDepth-04216); + case 4229: + return VUID_WRAP(VUID-FrontFacing-FrontFacing-04229); + case 4230: + return VUID_WRAP(VUID-FrontFacing-FrontFacing-04230); + case 4231: + return VUID_WRAP(VUID-FrontFacing-FrontFacing-04231); + case 4236: + return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04236); + case 4237: + return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04237); + case 4238: + return VUID_WRAP(VUID-GlobalInvocationId-GlobalInvocationId-04238); + case 4239: + return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04239); + case 4240: + return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04240); + case 4241: + return VUID_WRAP(VUID-HelperInvocation-HelperInvocation-04241); + case 4257: + return VUID_WRAP(VUID-InvocationId-InvocationId-04257); + case 4258: + return VUID_WRAP(VUID-InvocationId-InvocationId-04258); + case 4259: + return VUID_WRAP(VUID-InvocationId-InvocationId-04259); + case 4263: + return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04263); + case 4264: + return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04264); + case 4265: + return VUID_WRAP(VUID-InstanceIndex-InstanceIndex-04265); + case 4272: + return VUID_WRAP(VUID-Layer-Layer-04272); + case 4274: + return VUID_WRAP(VUID-Layer-Layer-04274); + case 4275: + return VUID_WRAP(VUID-Layer-Layer-04275); + case 4276: + return VUID_WRAP(VUID-Layer-Layer-04276); + case 4281: + return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04281); + case 4282: + return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04282); + case 4283: + return VUID_WRAP(VUID-LocalInvocationId-LocalInvocationId-04283); + case 4296: + return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04296); + case 4297: + return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04297); + case 4298: + return VUID_WRAP(VUID-NumWorkgroups-NumWorkgroups-04298); + case 4308: + return VUID_WRAP(VUID-PatchVertices-PatchVertices-04308); + case 4309: + return VUID_WRAP(VUID-PatchVertices-PatchVertices-04309); + case 4310: + return VUID_WRAP(VUID-PatchVertices-PatchVertices-04310); + case 4311: + return VUID_WRAP(VUID-PointCoord-PointCoord-04311); + case 4312: + return VUID_WRAP(VUID-PointCoord-PointCoord-04312); + case 4313: + return VUID_WRAP(VUID-PointCoord-PointCoord-04313); + case 4314: + return VUID_WRAP(VUID-PointSize-PointSize-04314); + case 4315: + return VUID_WRAP(VUID-PointSize-PointSize-04315); + case 4316: + return VUID_WRAP(VUID-PointSize-PointSize-04316); + case 4317: + return VUID_WRAP(VUID-PointSize-PointSize-04317); + case 4318: + return VUID_WRAP(VUID-Position-Position-04318); + case 4320: + return VUID_WRAP(VUID-Position-Position-04320); + case 4321: + return VUID_WRAP(VUID-Position-Position-04321); + case 4330: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04330); + case 4334: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04334); + case 4337: + return VUID_WRAP(VUID-PrimitiveId-PrimitiveId-04337); + case 4354: + return VUID_WRAP(VUID-SampleId-SampleId-04354); + case 4355: + return VUID_WRAP(VUID-SampleId-SampleId-04355); + case 4356: + return VUID_WRAP(VUID-SampleId-SampleId-04356); + case 4357: + return VUID_WRAP(VUID-SampleMask-SampleMask-04357); + case 4358: + return VUID_WRAP(VUID-SampleMask-SampleMask-04358); + case 4359: + return VUID_WRAP(VUID-SampleMask-SampleMask-04359); + case 4360: + return VUID_WRAP(VUID-SamplePosition-SamplePosition-04360); + case 4361: + return VUID_WRAP(VUID-SamplePosition-SamplePosition-04361); + case 4362: + return VUID_WRAP(VUID-SamplePosition-SamplePosition-04362); + case 4387: + return VUID_WRAP(VUID-TessCoord-TessCoord-04387); + case 4388: + return VUID_WRAP(VUID-TessCoord-TessCoord-04388); + case 4389: + return VUID_WRAP(VUID-TessCoord-TessCoord-04389); + case 4390: + return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04390); + case 4393: + return VUID_WRAP(VUID-TessLevelOuter-TessLevelOuter-04393); + case 4394: + return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04394); + case 4397: + return VUID_WRAP(VUID-TessLevelInner-TessLevelInner-04397); + case 4398: + return VUID_WRAP(VUID-VertexIndex-VertexIndex-04398); + case 4399: + return VUID_WRAP(VUID-VertexIndex-VertexIndex-04399); + case 4400: + return VUID_WRAP(VUID-VertexIndex-VertexIndex-04400); + case 4401: + return VUID_WRAP(VUID-ViewIndex-ViewIndex-04401); + case 4402: + return VUID_WRAP(VUID-ViewIndex-ViewIndex-04402); + case 4403: + return VUID_WRAP(VUID-ViewIndex-ViewIndex-04403); + case 4404: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04404); + case 4406: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04406); + case 4407: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04407); + case 4408: + return VUID_WRAP(VUID-ViewportIndex-ViewportIndex-04408); + case 4422: + return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04422); + case 4423: + return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04423); + case 4424: + return VUID_WRAP(VUID-WorkgroupId-WorkgroupId-04424); + case 4425: + return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04425); + case 4426: + return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04426); + case 4427: + return VUID_WRAP(VUID-WorkgroupSize-WorkgroupSize-04427); + case 4484: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04484); + case 4485: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04485); + case 4486: + return VUID_WRAP(VUID-PrimitiveShadingRateKHR-PrimitiveShadingRateKHR-04486); + case 4490: + return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04490); + case 4491: + return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04491); + case 4492: + return VUID_WRAP(VUID-ShadingRateKHR-ShadingRateKHR-04492); + default: + return ""; // unknown id + }; + // clang-format on +} + +} // namespace val +} // namespace spvtools diff --git a/third_party/spirv-tools/source/val/validation_state.h b/third_party/spirv-tools/source/val/validation_state.h new file mode 100644 index 0000000..aeb1ca8 --- /dev/null +++ b/third_party/spirv-tools/source/val/validation_state.h @@ -0,0 +1,890 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SOURCE_VAL_VALIDATION_STATE_H_ +#define SOURCE_VAL_VALIDATION_STATE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "source/assembly_grammar.h" +#include "source/diagnostic.h" +#include "source/disassemble.h" +#include "source/enum_set.h" +#include "source/latest_version_spirv_header.h" +#include "source/name_mapper.h" +#include "source/spirv_definition.h" +#include "source/spirv_validator_options.h" +#include "source/val/decoration.h" +#include "source/val/function.h" +#include "source/val/instruction.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace val { + +/// This enum represents the sections of a SPIRV module. See section 2.4 +/// of the SPIRV spec for additional details of the order. The enumerant values +/// are in the same order as the vector returned by GetModuleOrder +enum ModuleLayoutSection { + kLayoutCapabilities, /// < Section 2.4 #1 + kLayoutExtensions, /// < Section 2.4 #2 + kLayoutExtInstImport, /// < Section 2.4 #3 + kLayoutMemoryModel, /// < Section 2.4 #4 + kLayoutEntryPoint, /// < Section 2.4 #5 + kLayoutExecutionMode, /// < Section 2.4 #6 + kLayoutDebug1, /// < Section 2.4 #7 > 1 + kLayoutDebug2, /// < Section 2.4 #7 > 2 + kLayoutDebug3, /// < Section 2.4 #7 > 3 + kLayoutAnnotations, /// < Section 2.4 #8 + kLayoutTypes, /// < Section 2.4 #9 + kLayoutFunctionDeclarations, /// < Section 2.4 #10 + kLayoutFunctionDefinitions /// < Section 2.4 #11 +}; + +/// This class manages the state of the SPIR-V validation as it is being parsed. +class ValidationState_t { + public: + // Features that can optionally be turned on by a capability or environment. + struct Feature { + bool declare_int16_type = false; // Allow OpTypeInt with 16 bit width? + bool declare_float16_type = false; // Allow OpTypeFloat with 16 bit width? + bool free_fp_rounding_mode = false; // Allow the FPRoundingMode decoration + // and its vaules to be used without + // requiring any capability + + // Allow functionalities enabled by VariablePointers capability. + bool variable_pointers = false; + // Allow functionalities enabled by VariablePointersStorageBuffer + // capability. + bool variable_pointers_storage_buffer = false; + + // Permit group oerations Reduce, InclusiveScan, ExclusiveScan + bool group_ops_reduce_and_scans = false; + + // Allow OpTypeInt with 8 bit width? + bool declare_int8_type = false; + + // Target environment uses relaxed block layout. + // This is true for Vulkan 1.1 or later. + bool env_relaxed_block_layout = false; + + // Allow an OpTypeInt with 8 bit width to be used in more than just int + // conversion opcodes + bool use_int8_type = false; + + // Use scalar block layout. See VK_EXT_scalar_block_layout: + // Defines scalar alignment: + // - scalar alignment equals the scalar size in bytes + // - array alignment is same as its element alignment + // - array alignment is max alignment of any of its members + // - vector alignment is same as component alignment + // - matrix alignment is same as component alignment + // For struct in Uniform, StorageBuffer, PushConstant: + // - Offset of a member is multiple of scalar alignment of that member + // - ArrayStride and MatrixStride are multiples of scalar alignment + // Members need not be listed in offset order + bool scalar_block_layout = false; + + // SPIR-V 1.4 allows us to select between any two composite values + // of the same type. + bool select_between_composites = false; + + // SPIR-V 1.4 allows two memory access operands for OpCopyMemory and + // OpCopyMemorySized. + bool copy_memory_permits_two_memory_accesses = false; + + // SPIR-V 1.4 allows UConvert as a spec constant op in any environment. + // The Kernel capability already enables it, separately from this flag. + bool uconvert_spec_constant_op = false; + + // SPIR-V 1.4 allows Function and Private variables to be NonWritable + bool nonwritable_var_in_function_or_private = false; + }; + + ValidationState_t(const spv_const_context context, + const spv_const_validator_options opt, + const uint32_t* words, const size_t num_words, + const uint32_t max_warnings); + + /// Returns the context + spv_const_context context() const { return context_; } + + /// Returns the command line options + spv_const_validator_options options() const { return options_; } + + /// Sets the ID of the generator for this module. + void setGenerator(uint32_t gen) { generator_ = gen; } + + /// Returns the ID of the generator for this module. + uint32_t generator() const { return generator_; } + + /// Sets the SPIR-V version of this module. + void setVersion(uint32_t ver) { version_ = ver; } + + /// Gets the SPIR-V version of this module. + uint32_t version() const { return version_; } + + /// Forward declares the id in the module + spv_result_t ForwardDeclareId(uint32_t id); + + /// Removes a forward declared ID if it has been defined + spv_result_t RemoveIfForwardDeclared(uint32_t id); + + /// Registers an ID as a forward pointer + spv_result_t RegisterForwardPointer(uint32_t id); + + /// Returns whether or not an ID is a forward pointer + bool IsForwardPointer(uint32_t id) const; + + /// Assigns a name to an ID + void AssignNameToId(uint32_t id, std::string name); + + /// Returns a string representation of the ID in the format [Name] where + /// the is the numeric valid of the id and the Name is a name assigned by + /// the OpName instruction + std::string getIdName(uint32_t id) const; + + /// Accessor function for ID bound. + uint32_t getIdBound() const; + + /// Mutator function for ID bound. + void setIdBound(uint32_t bound); + + /// Returns the number of ID which have been forward referenced but not + /// defined + size_t unresolved_forward_id_count() const; + + /// Returns a vector of unresolved forward ids. + std::vector UnresolvedForwardIds() const; + + /// Returns true if the id has been defined + bool IsDefinedId(uint32_t id) const; + + /// Increments the total number of instructions in the file. + void increment_total_instructions() { total_instructions_++; } + + /// Increments the total number of functions in the file. + void increment_total_functions() { total_functions_++; } + + /// Allocates internal storage. Note, calling this will invalidate any + /// pointers to |ordered_instructions_| or |module_functions_| and, hence, + /// should only be called at the beginning of validation. + void preallocateStorage(); + + /// Returns the current layout section which is being processed + ModuleLayoutSection current_layout_section() const; + + /// Increments the module_layout_order_section_ + void ProgressToNextLayoutSectionOrder(); + + /// Determines if the op instruction is in a previous layout section + bool IsOpcodeInPreviousLayoutSection(SpvOp op); + + /// Determines if the op instruction is part of the current section + bool IsOpcodeInCurrentLayoutSection(SpvOp op); + + DiagnosticStream diag(spv_result_t error_code, const Instruction* inst); + + /// Returns the function states + std::vector& functions(); + + /// Returns the function states + Function& current_function(); + const Function& current_function() const; + + /// Returns function state with the given id, or nullptr if no such function. + const Function* function(uint32_t id) const; + Function* function(uint32_t id); + + /// Returns true if the called after a function instruction but before the + /// function end instruction + bool in_function_body() const; + + /// Returns true if called after a label instruction but before a branch + /// instruction + bool in_block() const; + + struct EntryPointDescription { + std::string name; + std::vector interfaces; + }; + + /// Registers |id| as an entry point with |execution_model| and |interfaces|. + void RegisterEntryPoint(const uint32_t id, SpvExecutionModel execution_model, + EntryPointDescription&& desc) { + entry_points_.push_back(id); + entry_point_to_execution_models_[id].insert(execution_model); + entry_point_descriptions_[id].emplace_back(desc); + } + + /// Returns a list of entry point function ids + const std::vector& entry_points() const { return entry_points_; } + + /// Returns the set of entry points that root call graphs that contain + /// recursion. + const std::set& recursive_entry_points() const { + return recursive_entry_points_; + } + + /// Registers execution mode for the given entry point. + void RegisterExecutionModeForEntryPoint(uint32_t entry_point, + SpvExecutionMode execution_mode) { + entry_point_to_execution_modes_[entry_point].insert(execution_mode); + } + + /// Returns the interface descriptions of a given entry point. + const std::vector& entry_point_descriptions( + uint32_t entry_point) { + return entry_point_descriptions_.at(entry_point); + } + + /// Returns Execution Models for the given Entry Point. + /// Returns nullptr if none found (would trigger assertion). + const std::set* GetExecutionModels( + uint32_t entry_point) const { + const auto it = entry_point_to_execution_models_.find(entry_point); + if (it == entry_point_to_execution_models_.end()) { + assert(0); + return nullptr; + } + return &it->second; + } + + /// Returns Execution Modes for the given Entry Point. + /// Returns nullptr if none found. + const std::set* GetExecutionModes( + uint32_t entry_point) const { + const auto it = entry_point_to_execution_modes_.find(entry_point); + if (it == entry_point_to_execution_modes_.end()) { + return nullptr; + } + return &it->second; + } + + /// Traverses call tree and computes function_to_entry_points_. + /// Note: called after fully parsing the binary. + void ComputeFunctionToEntryPointMapping(); + + /// Traverse call tree and computes recursive_entry_points_. + /// Note: called after fully parsing the binary and calling + /// ComputeFunctionToEntryPointMapping. + void ComputeRecursiveEntryPoints(); + + /// Returns all the entry points that can call |func|. + const std::vector& FunctionEntryPoints(uint32_t func) const; + + /// Returns all the entry points that statically use |id|. + /// + /// Note: requires ComputeFunctionToEntryPointMapping to have been called. + std::set EntryPointReferences(uint32_t id) const; + + /// Inserts an to the set of functions that are target of OpFunctionCall. + void AddFunctionCallTarget(const uint32_t id) { + function_call_targets_.insert(id); + current_function().AddFunctionCallTarget(id); + } + + /// Returns whether or not a function is the target of OpFunctionCall. + bool IsFunctionCallTarget(const uint32_t id) { + return (function_call_targets_.find(id) != function_call_targets_.end()); + } + + bool IsFunctionCallDefined(const uint32_t id) { + return (id_to_function_.find(id) != id_to_function_.end()); + } + /// Registers the capability and its dependent capabilities + void RegisterCapability(SpvCapability cap); + + /// Registers the extension. + void RegisterExtension(Extension ext); + + /// Registers the function in the module. Subsequent instructions will be + /// called against this function + spv_result_t RegisterFunction(uint32_t id, uint32_t ret_type_id, + SpvFunctionControlMask function_control, + uint32_t function_type_id); + + /// Register a function end instruction + spv_result_t RegisterFunctionEnd(); + + /// Returns true if the capability is enabled in the module. + bool HasCapability(SpvCapability cap) const { + return module_capabilities_.Contains(cap); + } + + /// Returns a reference to the set of capabilities in the module. + /// This is provided for debuggability. + const CapabilitySet& module_capabilities() const { + return module_capabilities_; + } + + /// Returns true if the extension is enabled in the module. + bool HasExtension(Extension ext) const { + return module_extensions_.Contains(ext); + } + + /// Returns true if any of the capabilities is enabled, or if |capabilities| + /// is an empty set. + bool HasAnyOfCapabilities(const CapabilitySet& capabilities) const; + + /// Returns true if any of the extensions is enabled, or if |extensions| + /// is an empty set. + bool HasAnyOfExtensions(const ExtensionSet& extensions) const; + + /// Sets the addressing model of this module (logical/physical). + void set_addressing_model(SpvAddressingModel am); + + /// Returns true if the OpMemoryModel was found. + bool has_memory_model_specified() const { + return addressing_model_ != SpvAddressingModelMax && + memory_model_ != SpvMemoryModelMax; + } + + /// Returns the addressing model of this module, or Logical if uninitialized. + SpvAddressingModel addressing_model() const; + + /// Returns the addressing model of this module, or Logical if uninitialized. + uint32_t pointer_size_and_alignment() const { + return pointer_size_and_alignment_; + } + + /// Sets the memory model of this module. + void set_memory_model(SpvMemoryModel mm); + + /// Returns the memory model of this module, or Simple if uninitialized. + SpvMemoryModel memory_model() const; + + const AssemblyGrammar& grammar() const { return grammar_; } + + /// Inserts the instruction into the list of ordered instructions in the file. + Instruction* AddOrderedInstruction(const spv_parsed_instruction_t* inst); + + /// Registers the instruction. This will add the instruction to the list of + /// definitions and register sampled image consumers. + void RegisterInstruction(Instruction* inst); + + /// Registers the debug instruction information. + void RegisterDebugInstruction(const Instruction* inst); + + /// Registers the decoration for the given + void RegisterDecorationForId(uint32_t id, const Decoration& dec) { + auto& dec_list = id_decorations_[id]; + auto lb = std::find(dec_list.begin(), dec_list.end(), dec); + if (lb == dec_list.end()) { + dec_list.push_back(dec); + } + } + + /// Registers the list of decorations for the given + template + void RegisterDecorationsForId(uint32_t id, InputIt begin, InputIt end) { + std::vector& cur_decs = id_decorations_[id]; + cur_decs.insert(cur_decs.end(), begin, end); + } + + /// Registers the list of decorations for the given member of the given + /// structure. + template + void RegisterDecorationsForStructMember(uint32_t struct_id, + uint32_t member_index, InputIt begin, + InputIt end) { + RegisterDecorationsForId(struct_id, begin, end); + for (auto& decoration : id_decorations_[struct_id]) { + decoration.set_struct_member_index(member_index); + } + } + + /// Returns all the decorations for the given . If no decorations exist + /// for the , it registers an empty vector for it in the map and + /// returns the empty vector. + std::vector& id_decorations(uint32_t id) { + return id_decorations_[id]; + } + + // Returns const pointer to the internal decoration container. + const std::map>& id_decorations() const { + return id_decorations_; + } + + /// Returns true if the given id has the given decoration , + /// otherwise returns false. + bool HasDecoration(uint32_t id, SpvDecoration dec) { + const auto& decorations = id_decorations_.find(id); + if (decorations == id_decorations_.end()) return false; + + return std::any_of( + decorations->second.begin(), decorations->second.end(), + [dec](const Decoration& d) { return dec == d.dec_type(); }); + } + + /// Finds id's def, if it exists. If found, returns the definition otherwise + /// nullptr + const Instruction* FindDef(uint32_t id) const; + + /// Finds id's def, if it exists. If found, returns the definition otherwise + /// nullptr + Instruction* FindDef(uint32_t id); + + /// Returns the instructions in the order they appear in the binary + const std::vector& ordered_instructions() const { + return ordered_instructions_; + } + + /// Returns a map of instructions mapped by their result id + const std::unordered_map& all_definitions() const { + return all_definitions_; + } + + /// Returns a vector containing the instructions that consume the given + /// SampledImage id. + std::vector getSampledImageConsumers(uint32_t id) const; + + /// Records cons_id as a consumer of sampled_image_id. + void RegisterSampledImageConsumer(uint32_t sampled_image_id, + Instruction* consumer); + + /// Returns the set of Global Variables. + std::unordered_set& global_vars() { return global_vars_; } + + /// Returns the set of Local Variables. + std::unordered_set& local_vars() { return local_vars_; } + + /// Returns the number of Global Variables. + size_t num_global_vars() { return global_vars_.size(); } + + /// Returns the number of Local Variables. + size_t num_local_vars() { return local_vars_.size(); } + + /// Inserts a new to the set of Global Variables. + void registerGlobalVariable(const uint32_t id) { global_vars_.insert(id); } + + /// Inserts a new to the set of Local Variables. + void registerLocalVariable(const uint32_t id) { local_vars_.insert(id); } + + // Returns true if using relaxed block layout, equivalent to + // VK_KHR_relaxed_block_layout. + bool IsRelaxedBlockLayout() const { + return features_.env_relaxed_block_layout || options()->relax_block_layout; + } + + /// Sets the struct nesting depth for a given struct ID + void set_struct_nesting_depth(uint32_t id, uint32_t depth) { + struct_nesting_depth_[id] = depth; + } + + /// Returns the nesting depth of a given structure ID + uint32_t struct_nesting_depth(uint32_t id) { + return struct_nesting_depth_[id]; + } + + /// Records the has a nested block/bufferblock decorated struct for a given + /// struct ID + void SetHasNestedBlockOrBufferBlockStruct(uint32_t id, bool has) { + struct_has_nested_blockorbufferblock_struct_[id] = has; + } + + /// For a given struct ID returns true if it has a nested block/bufferblock + /// decorated struct + bool GetHasNestedBlockOrBufferBlockStruct(uint32_t id) { + return struct_has_nested_blockorbufferblock_struct_[id]; + } + + /// Records that the structure type has a member decorated with a built-in. + void RegisterStructTypeWithBuiltInMember(uint32_t id) { + builtin_structs_.insert(id); + } + + /// Returns true if the struct type with the given Id has a BuiltIn member. + bool IsStructTypeWithBuiltInMember(uint32_t id) const { + return (builtin_structs_.find(id) != builtin_structs_.end()); + } + + // Returns the state of optional features. + const Feature& features() const { return features_; } + + /// Adds the instruction data to unique_type_declarations_. + /// Returns false if an identical type declaration already exists. + bool RegisterUniqueTypeDeclaration(const Instruction* inst); + + // Returns type_id of the scalar component of |id|. + // |id| can be either + // - scalar, vector or matrix type + // - object of either scalar, vector or matrix type + uint32_t GetComponentType(uint32_t id) const; + + // Returns + // - 1 for scalar types or objects + // - vector size for vector types or objects + // - num columns for matrix types or objects + // Should not be called with any other arguments (will return zero and invoke + // assertion). + uint32_t GetDimension(uint32_t id) const; + + // Returns bit width of scalar or component. + // |id| can be + // - scalar, vector or matrix type + // - object of either scalar, vector or matrix type + // Will invoke assertion and return 0 if |id| is none of the above. + uint32_t GetBitWidth(uint32_t id) const; + + // Provides detailed information on matrix type. + // Returns false iff |id| is not matrix type. + bool GetMatrixTypeInfo(uint32_t id, uint32_t* num_rows, uint32_t* num_cols, + uint32_t* column_type, uint32_t* component_type) const; + + // Collects struct member types into |member_types|. + // Returns false iff not struct type or has no members. + // Deletes prior contents of |member_types|. + bool GetStructMemberTypes(uint32_t struct_type_id, + std::vector* member_types) const; + + // Returns true iff |id| is a type corresponding to the name of the function. + // Only works for types not for objects. + bool IsVoidType(uint32_t id) const; + bool IsFloatScalarType(uint32_t id) const; + bool IsFloatVectorType(uint32_t id) const; + bool IsFloatScalarOrVectorType(uint32_t id) const; + bool IsFloatMatrixType(uint32_t id) const; + bool IsIntScalarType(uint32_t id) const; + bool IsIntVectorType(uint32_t id) const; + bool IsIntScalarOrVectorType(uint32_t id) const; + bool IsUnsignedIntScalarType(uint32_t id) const; + bool IsUnsignedIntVectorType(uint32_t id) const; + bool IsSignedIntScalarType(uint32_t id) const; + bool IsSignedIntVectorType(uint32_t id) const; + bool IsBoolScalarType(uint32_t id) const; + bool IsBoolVectorType(uint32_t id) const; + bool IsBoolScalarOrVectorType(uint32_t id) const; + bool IsPointerType(uint32_t id) const; + bool IsCooperativeMatrixType(uint32_t id) const; + bool IsFloatCooperativeMatrixType(uint32_t id) const; + bool IsIntCooperativeMatrixType(uint32_t id) const; + bool IsUnsignedIntCooperativeMatrixType(uint32_t id) const; + + // Returns true if |id| is a type id that contains |type| (or integer or + // floating point type) of |width| bits. + bool ContainsSizedIntOrFloatType(uint32_t id, SpvOp type, + uint32_t width) const; + // Returns true if |id| is a type id that contains a 8- or 16-bit int or + // 16-bit float that is not generally enabled for use. + bool ContainsLimitedUseIntOrFloatType(uint32_t id) const; + + // Gets value from OpConstant and OpSpecConstant as uint64. + // Returns false on failure (no instruction, wrong instruction, not int). + bool GetConstantValUint64(uint32_t id, uint64_t* val) const; + + // Returns type_id if id has type or zero otherwise. + uint32_t GetTypeId(uint32_t id) const; + + // Returns opcode of the instruction which issued the id or OpNop if the + // instruction is not registered. + SpvOp GetIdOpcode(uint32_t id) const; + + // Returns type_id for given id operand if it has a type or zero otherwise. + // |operand_index| is expected to be pointing towards an operand which is an + // id. + uint32_t GetOperandTypeId(const Instruction* inst, + size_t operand_index) const; + + // Provides information on pointer type. Returns false iff not pointer type. + bool GetPointerTypeInfo(uint32_t id, uint32_t* data_type, + uint32_t* storage_class) const; + + // Is the ID the type of a pointer to a uniform block: Block-decorated struct + // in uniform storage class? The result is only valid after internal method + // CheckDecorationsOfBuffers has been called. + bool IsPointerToUniformBlock(uint32_t type_id) const { + return pointer_to_uniform_block_.find(type_id) != + pointer_to_uniform_block_.cend(); + } + // Save the ID of a pointer to uniform block. + void RegisterPointerToUniformBlock(uint32_t type_id) { + pointer_to_uniform_block_.insert(type_id); + } + // Is the ID the type of a struct used as a uniform block? + // The result is only valid after internal method CheckDecorationsOfBuffers + // has been called. + bool IsStructForUniformBlock(uint32_t type_id) const { + return struct_for_uniform_block_.find(type_id) != + struct_for_uniform_block_.cend(); + } + // Save the ID of a struct of a uniform block. + void RegisterStructForUniformBlock(uint32_t type_id) { + struct_for_uniform_block_.insert(type_id); + } + // Is the ID the type of a pointer to a storage buffer: BufferBlock-decorated + // struct in uniform storage class, or Block-decorated struct in StorageBuffer + // storage class? The result is only valid after internal method + // CheckDecorationsOfBuffers has been called. + bool IsPointerToStorageBuffer(uint32_t type_id) const { + return pointer_to_storage_buffer_.find(type_id) != + pointer_to_storage_buffer_.cend(); + } + // Save the ID of a pointer to a storage buffer. + void RegisterPointerToStorageBuffer(uint32_t type_id) { + pointer_to_storage_buffer_.insert(type_id); + } + // Is the ID the type of a struct for storage buffer? + // The result is only valid after internal method CheckDecorationsOfBuffers + // has been called. + bool IsStructForStorageBuffer(uint32_t type_id) const { + return struct_for_storage_buffer_.find(type_id) != + struct_for_storage_buffer_.cend(); + } + // Save the ID of a struct of a storage buffer. + void RegisterStructForStorageBuffer(uint32_t type_id) { + struct_for_storage_buffer_.insert(type_id); + } + + // Is the ID the type of a pointer to a storage image? That is, the pointee + // type is an image type which is known to not use a sampler. + bool IsPointerToStorageImage(uint32_t type_id) const { + return pointer_to_storage_image_.find(type_id) != + pointer_to_storage_image_.cend(); + } + // Save the ID of a pointer to a storage image. + void RegisterPointerToStorageImage(uint32_t type_id) { + pointer_to_storage_image_.insert(type_id); + } + + // Tries to evaluate a 32-bit signed or unsigned scalar integer constant. + // Returns tuple . + // OpSpecConstant* return |is_const_int32| as false since their values cannot + // be relied upon during validation. + std::tuple EvalInt32IfConst(uint32_t id) const; + + // Returns the disassembly string for the given instruction. + std::string Disassemble(const Instruction& inst) const; + + // Returns the disassembly string for the given instruction. + std::string Disassemble(const uint32_t* words, uint16_t num_words) const; + + // Returns whether type m1 and type m2 are cooperative matrices with + // the same "shape" (matching scope, rows, cols). If any are specialization + // constants, we assume they can match because we can't prove they don't. + spv_result_t CooperativeMatrixShapesMatch(const Instruction* inst, + uint32_t m1, uint32_t m2); + + // Returns true if |lhs| and |rhs| logically match and, if the decorations of + // |rhs| are a subset of |lhs|. + // + // 1. Must both be either OpTypeArray or OpTypeStruct + // 2. If OpTypeArray, then + // * Length must be the same + // * Element type must match or logically match + // 3. If OpTypeStruct, then + // * Both have same number of elements + // * Element N for both structs must match or logically match + // + // If |check_decorations| is false, then the decorations are not checked. + bool LogicallyMatch(const Instruction* lhs, const Instruction* rhs, + bool check_decorations); + + // Traces |inst| to find a single base pointer. Returns the base pointer. + // Will trace through the following instructions: + // * OpAccessChain + // * OpInBoundsAccessChain + // * OpPtrAccessChain + // * OpInBoundsPtrAccessChain + // * OpCopyObject + const Instruction* TracePointer(const Instruction* inst) const; + + // Validates the storage class for the target environment. + bool IsValidStorageClass(SpvStorageClass storage_class) const; + + // Takes a Vulkan Valid Usage ID (VUID) as |id| and optional |reference| and + // will return a non-empty string only if ID is known and targeting Vulkan. + // VUIDs are found in the Vulkan-Docs repo in the form "[[VUID-ref-ref-id]]" + // where "id" is always an 5 char long number (with zeros padding) and matches + // to |id|. |reference| is used if there is a "common validity" and the VUID + // shares the same |id| value. + // + // More details about Vulkan validation can be found in Vulkan Guide: + // https://github.com/KhronosGroup/Vulkan-Guide/blob/master/chapters/validation_overview.md + std::string VkErrorID(uint32_t id, const char* reference = nullptr) const; + + // Testing method to allow setting the current layout section. + void SetCurrentLayoutSectionForTesting(ModuleLayoutSection section) { + current_layout_section_ = section; + } + + private: + ValidationState_t(const ValidationState_t&); + + const spv_const_context context_; + + /// Stores the Validator command line options. Must be a valid options object. + const spv_const_validator_options options_; + + /// The SPIR-V binary module we're validating. + const uint32_t* words_; + const size_t num_words_; + + /// The generator of the SPIR-V. + uint32_t generator_ = 0; + + /// The version of the SPIR-V. + uint32_t version_ = 0; + + /// The total number of instructions in the binary. + size_t total_instructions_ = 0; + /// The total number of functions in the binary. + size_t total_functions_ = 0; + + /// IDs which have been forward declared but have not been defined + std::unordered_set unresolved_forward_ids_; + + /// IDs that have been declared as forward pointers. + std::unordered_set forward_pointer_ids_; + + /// Stores a vector of instructions that use the result of a given + /// OpSampledImage instruction. + std::unordered_map> + sampled_image_consumers_; + + /// A map of operand IDs and their names defined by the OpName instruction + std::unordered_map operand_names_; + + /// The section of the code being processed + ModuleLayoutSection current_layout_section_; + + /// A list of functions in the module. + /// Pointers to objects in this container are guaranteed to be stable and + /// valid until the end of lifetime of the validation state. + std::vector module_functions_; + + /// Capabilities declared in the module + CapabilitySet module_capabilities_; + + /// Extensions declared in the module + ExtensionSet module_extensions_; + + /// List of all instructions in the order they appear in the binary + std::vector ordered_instructions_; + + /// Instructions that can be referenced by Ids + std::unordered_map all_definitions_; + + /// IDs that are entry points, ie, arguments to OpEntryPoint. + std::vector entry_points_; + + /// Maps an entry point id to its desciptions. + std::unordered_map> + entry_point_descriptions_; + + /// IDs that are entry points, ie, arguments to OpEntryPoint, and root a call + /// graph that recurses. + std::set recursive_entry_points_; + + /// Functions IDs that are target of OpFunctionCall. + std::unordered_set function_call_targets_; + + /// ID Bound from the Header + uint32_t id_bound_; + + /// Set of Global Variable IDs (Storage Class other than 'Function') + std::unordered_set global_vars_; + + /// Set of Local Variable IDs ('Function' Storage Class) + std::unordered_set local_vars_; + + /// Set of struct types that have members with a BuiltIn decoration. + std::unordered_set builtin_structs_; + + /// Structure Nesting Depth + std::unordered_map struct_nesting_depth_; + + /// Structure has nested blockorbufferblock struct + std::unordered_map + struct_has_nested_blockorbufferblock_struct_; + + /// Stores the list of decorations for a given + std::map> id_decorations_; + + /// Stores type declarations which need to be unique (i.e. non-aggregates), + /// in the form [opcode, operand words], result_id is not stored. + /// Using ordered set to avoid the need for a vector hash function. + /// The size of this container is expected not to exceed double-digits. + std::set> unique_type_declarations_; + + AssemblyGrammar grammar_; + + SpvAddressingModel addressing_model_; + SpvMemoryModel memory_model_; + // pointer size derived from addressing model. Assumes all storage classes + // have the same pointer size (for physical pointer types). + uint32_t pointer_size_and_alignment_; + + /// NOTE: See correspoding getter functions + bool in_function_; + + /// The state of optional features. These are determined by capabilities + /// declared by the module and the environment. + Feature features_; + + /// Maps function ids to function stat objects. + std::unordered_map id_to_function_; + + /// Mapping entry point -> execution models. It is presumed that the same + /// function could theoretically be used as 'main' by multiple OpEntryPoint + /// instructions. + std::unordered_map> + entry_point_to_execution_models_; + + /// Mapping entry point -> execution modes. + std::unordered_map> + entry_point_to_execution_modes_; + + /// Mapping function -> array of entry points inside this + /// module which can (indirectly) call the function. + std::unordered_map> function_to_entry_points_; + const std::vector empty_ids_; + + // The IDs of types of pointers to Block-decorated structs in Uniform storage + // class. This is populated at the start of ValidateDecorations. + std::unordered_set pointer_to_uniform_block_; + // The IDs of struct types for uniform blocks. + // This is populated at the start of ValidateDecorations. + std::unordered_set struct_for_uniform_block_; + // The IDs of types of pointers to BufferBlock-decorated structs in Uniform + // storage class, or Block-decorated structs in StorageBuffer storage class. + // This is populated at the start of ValidateDecorations. + std::unordered_set pointer_to_storage_buffer_; + // The IDs of struct types for storage buffers. + // This is populated at the start of ValidateDecorations. + std::unordered_set struct_for_storage_buffer_; + // The IDs of types of pointers to storage images. This is populated in the + // TypePass. + std::unordered_set pointer_to_storage_image_; + + /// Maps ids to friendly names. + std::unique_ptr friendly_mapper_; + spvtools::NameMapper name_mapper_; + + /// Variables used to reduce the number of diagnostic messages. + uint32_t num_of_warnings_; + uint32_t max_num_of_warnings_; +}; + +} // namespace val +} // namespace spvtools + +#endif // SOURCE_VAL_VALIDATION_STATE_H_ diff --git a/third_party/spirv-tools/test/CMakeLists.txt b/third_party/spirv-tools/test/CMakeLists.txt new file mode 100644 index 0000000..8ede58b --- /dev/null +++ b/third_party/spirv-tools/test/CMakeLists.txt @@ -0,0 +1,194 @@ +# Copyright (c) 2015-2016 The Khronos Group Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Add a SPIR-V Tools unit test. Signature: +# add_spvtools_unittest( +# TARGET target_name +# SRCS src_file.h src_file.cpp +# LIBS lib1 lib2 +# ) + +if (NOT "${SPIRV_SKIP_TESTS}") + if (TARGET gmock_main) + message(STATUS "Found Google Mock, building tests.") + else() + message(STATUS "Did not find googletest, tests will not be built. " + "To enable tests place googletest in '/external/googletest'.") + endif() +endif() + +function(add_spvtools_unittest) + if (NOT "${SPIRV_SKIP_TESTS}" AND TARGET gmock_main) + set(one_value_args TARGET PCH_FILE) + set(multi_value_args SRCS LIBS ENVIRONMENT) + cmake_parse_arguments( + ARG "" "${one_value_args}" "${multi_value_args}" ${ARGN}) + set(target test_${ARG_TARGET}) + set(SRC_COPY ${ARG_SRCS}) + if (DEFINED ARG_PCH_FILE) + spvtools_pch(SRC_COPY ${ARG_PCH_FILE}) + endif() + add_executable(${target} ${SRC_COPY}) + spvtools_default_compile_options(${target}) + if(${COMPILER_IS_LIKE_GNU}) + target_compile_options(${target} PRIVATE -Wno-undef) + # Effcee and RE2 headers exhibit shadowing. + target_compile_options(${target} PRIVATE -Wno-shadow) + endif() + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + # Disable C4503 "decorated name length exceeded" warning, + # triggered by some heavily templated types. + # We don't care much about that in test code. + # Important to do since we have warnings-as-errors. + target_compile_options(${target} PRIVATE /wd4503) + # Googletest accidentally turns off support for ::testing::Combine + # in VS 2017. See https://github.com/google/googletest/issues/1352 + # Forcibly turn it on again. + target_compile_options(${target} PRIVATE /DGTEST_HAS_COMBINE=1) + endif() + target_include_directories(${target} PRIVATE + ${SPIRV_HEADER_INCLUDE_DIR} + ${spirv-tools_SOURCE_DIR} + ${spirv-tools_SOURCE_DIR}/include + ${spirv-tools_SOURCE_DIR}/test + ${spirv-tools_BINARY_DIR} + ${gtest_SOURCE_DIR}/include + ${gmock_SOURCE_DIR}/include + ) + if (TARGET effcee) + # If using Effcee for testing, then add its include directory. + target_include_directories(${target} PRIVATE ${effcee_SOURCE_DIR}) + endif() + target_link_libraries(${target} PRIVATE ${ARG_LIBS}) + if (TARGET effcee) + target_link_libraries(${target} PRIVATE effcee) + endif() + target_link_libraries(${target} PRIVATE gmock_main) + add_test(NAME spirv-tools-${target} COMMAND ${target}) + if (DEFINED ARG_ENVIRONMENT) + set_tests_properties(spirv-tools-${target} PROPERTIES ENVIRONMENT ${ARG_ENVIRONMENT}) + endif() + set_property(TARGET ${target} PROPERTY FOLDER "SPIRV-Tools tests") + endif() +endfunction() + +set(TEST_SOURCES + test_fixture.h + unit_spirv.h + + assembly_context_test.cpp + assembly_format_test.cpp + binary_destroy_test.cpp + binary_endianness_test.cpp + binary_header_get_test.cpp + binary_parse_test.cpp + binary_strnlen_s_test.cpp + binary_to_text_test.cpp + binary_to_text.literal_test.cpp + comment_test.cpp + diagnostic_test.cpp + enum_string_mapping_test.cpp + enum_set_test.cpp + ext_inst.cldebug100_test.cpp + ext_inst.debuginfo_test.cpp + ext_inst.glsl_test.cpp + ext_inst.non_semantic_test.cpp + ext_inst.opencl_test.cpp + fix_word_test.cpp + generator_magic_number_test.cpp + hex_float_test.cpp + immediate_int_test.cpp + libspirv_macros_test.cpp + log_test.cpp + named_id_test.cpp + name_mapper_test.cpp + opcode_make_test.cpp + opcode_require_capabilities_test.cpp + opcode_split_test.cpp + opcode_table_get_test.cpp + operand_capabilities_test.cpp + operand_test.cpp + operand_pattern_test.cpp + parse_number_test.cpp + preserve_numeric_ids_test.cpp + software_version_test.cpp + string_utils_test.cpp + target_env_test.cpp + text_advance_test.cpp + text_destroy_test.cpp + text_literal_test.cpp + text_start_new_inst_test.cpp + text_to_binary.annotation_test.cpp + text_to_binary.barrier_test.cpp + text_to_binary.composite_test.cpp + text_to_binary.constant_test.cpp + text_to_binary.control_flow_test.cpp + text_to_binary_test.cpp + text_to_binary.debug_test.cpp + text_to_binary.device_side_enqueue_test.cpp + text_to_binary.extension_test.cpp + text_to_binary.function_test.cpp + text_to_binary.group_test.cpp + text_to_binary.image_test.cpp + text_to_binary.literal_test.cpp + text_to_binary.memory_test.cpp + text_to_binary.misc_test.cpp + text_to_binary.mode_setting_test.cpp + text_to_binary.pipe_storage_test.cpp + text_to_binary.type_declaration_test.cpp + text_to_binary.subgroup_dispatch_test.cpp + text_to_binary.reserved_sampling_test.cpp + text_word_get_test.cpp + + unit_spirv.cpp +) + +spvtools_pch(TEST_SOURCES pch_test) + +add_spvtools_unittest( + TARGET spirv_unit_tests + SRCS ${TEST_SOURCES} + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + +add_spvtools_unittest( + TARGET c_interface + SRCS c_interface_test.cpp + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) + +add_spvtools_unittest( + TARGET c_interface_shared + SRCS c_interface_test.cpp + LIBS ${SPIRV_TOOLS}-shared + ENVIRONMENT PATH=$) + +add_spvtools_unittest( + TARGET cpp_interface + SRCS cpp_interface_test.cpp + LIBS SPIRV-Tools-opt) + +if (${SPIRV_TIMER_ENABLED}) +add_spvtools_unittest( + TARGET timer + SRCS timer_test.cpp + LIBS ${SPIRV_TOOLS_FULL_VISIBILITY}) +endif() + + +add_subdirectory(link) +add_subdirectory(opt) +add_subdirectory(reduce) +add_subdirectory(fuzz) +add_subdirectory(tools) +add_subdirectory(util) +add_subdirectory(val) diff --git a/third_party/spirv-tools/test/assembly_context_test.cpp b/third_party/spirv-tools/test/assembly_context_test.cpp new file mode 100644 index 0000000..c8aa06b --- /dev/null +++ b/third_party/spirv-tools/test/assembly_context_test.cpp @@ -0,0 +1,77 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "source/instruction.h" +#include "source/util/string_utils.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::AutoText; +using spvtest::Concatenate; +using ::testing::Eq; + +struct EncodeStringCase { + std::string str; + std::vector initial_contents; +}; + +using EncodeStringTest = ::testing::TestWithParam; + +TEST_P(EncodeStringTest, Sample) { + AssemblyContext context(AutoText(""), nullptr); + spv_instruction_t inst; + inst.words = GetParam().initial_contents; + ASSERT_EQ(SPV_SUCCESS, + context.binaryEncodeString(GetParam().str.c_str(), &inst)); + // We already trust MakeVector + EXPECT_THAT(inst.words, Eq(Concatenate({GetParam().initial_contents, + utils::MakeVector(GetParam().str)}))); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + BinaryEncodeString, EncodeStringTest, + ::testing::ValuesIn(std::vector{ + // Use cases that exercise at least one to two words, + // and both empty and non-empty initial contents. + {"", {}}, + {"", {1,2,3}}, + {"a", {}}, + {"a", {4}}, + {"ab", {4}}, + {"abc", {}}, + {"abc", {18}}, + {"abcd", {}}, + {"abcd", {22}}, + {"abcde", {4}}, + {"abcdef", {}}, + {"abcdef", {99,42}}, + {"abcdefg", {}}, + {"abcdefg", {101}}, + {"abcdefgh", {}}, + {"abcdefgh", {102, 103, 104}}, + // A very long string, encoded after an initial word. + // SPIR-V limits strings to 65535 characters. + {std::string(65535, 'a'), {1}}, + })); +// clang-format on + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/assembly_format_test.cpp b/third_party/spirv-tools/test/assembly_format_test.cpp new file mode 100644 index 0000000..718e6d3 --- /dev/null +++ b/third_party/spirv-tools/test/assembly_format_test.cpp @@ -0,0 +1,50 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/test_fixture.h" + +namespace svptools { +namespace { + +using spvtest::ScopedContext; +using spvtest::TextToBinaryTest; + +TEST_F(TextToBinaryTest, InstOpcodeProducesResultIDButNoIDDefinedFails) { + SetText("OpTypeMatrix %1 %2 1000"); + EXPECT_EQ(SPV_ERROR_INVALID_TEXT, + spvTextToBinary(ScopedContext().context, text.str, text.length, + &binary, &diagnostic)); + ASSERT_NE(nullptr, diagnostic); + EXPECT_STREQ( + "Expected at the beginning of an instruction, found " + "'OpTypeMatrix'.", + diagnostic->error); + EXPECT_EQ(0u, diagnostic->position.line); +} + +TEST_F(TextToBinaryTest, + InstDefinesResultIDButOpcodeDoesNotProduceAResultFails) { + SetText("\n\n%foo = OpName %1 \"bar\""); + EXPECT_EQ(SPV_ERROR_INVALID_TEXT, + spvTextToBinary(ScopedContext().context, text.str, text.length, + &binary, &diagnostic)); + ASSERT_NE(nullptr, diagnostic); + EXPECT_STREQ( + "Cannot set ID %foo because OpName does not produce a result ID.", + diagnostic->error); + EXPECT_EQ(2u, diagnostic->position.line); +} + +} // namespace +} // namespace svptools diff --git a/third_party/spirv-tools/test/binary_destroy_test.cpp b/third_party/spirv-tools/test/binary_destroy_test.cpp new file mode 100644 index 0000000..e3870c9 --- /dev/null +++ b/third_party/spirv-tools/test/binary_destroy_test.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +#include "test/test_fixture.h" + +namespace spvtools { +namespace { + +using spvtest::ScopedContext; + +TEST(BinaryDestroy, Null) { + // There is no state or return value to check. Just check + // for the ability to call the API without abnormal termination. + spvBinaryDestroy(nullptr); +} + +using BinaryDestroySomething = spvtest::TextToBinaryTest; + +// Checks safety of destroying a validly constructed binary. +TEST_F(BinaryDestroySomething, Default) { + // Use a binary object constructed by the API instead of rolling our own. + SetText("OpSource OpenCL_C 120"); + spv_binary my_binary = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(ScopedContext().context, text.str, + text.length, &my_binary, &diagnostic)); + ASSERT_NE(nullptr, my_binary); + spvBinaryDestroy(my_binary); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/binary_endianness_test.cpp b/third_party/spirv-tools/test/binary_endianness_test.cpp new file mode 100644 index 0000000..3cd405d --- /dev/null +++ b/third_party/spirv-tools/test/binary_endianness_test.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +TEST(BinaryEndianness, InvalidCode) { + uint32_t invalidMagicNumber[] = {0}; + spv_const_binary_t binary = {invalidMagicNumber, 1}; + spv_endianness_t endian; + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, spvBinaryEndianness(&binary, &endian)); +} + +TEST(BinaryEndianness, Little) { + uint32_t magicNumber; + if (I32_ENDIAN_HOST == I32_ENDIAN_LITTLE) { + magicNumber = 0x07230203; + } else { + magicNumber = 0x03022307; + } + spv_const_binary_t binary = {&magicNumber, 1}; + spv_endianness_t endian; + ASSERT_EQ(SPV_SUCCESS, spvBinaryEndianness(&binary, &endian)); + ASSERT_EQ(SPV_ENDIANNESS_LITTLE, endian); +} + +TEST(BinaryEndianness, Big) { + uint32_t magicNumber; + if (I32_ENDIAN_HOST == I32_ENDIAN_BIG) { + magicNumber = 0x07230203; + } else { + magicNumber = 0x03022307; + } + spv_const_binary_t binary = {&magicNumber, 1}; + spv_endianness_t endian; + ASSERT_EQ(SPV_SUCCESS, spvBinaryEndianness(&binary, &endian)); + ASSERT_EQ(SPV_ENDIANNESS_BIG, endian); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/binary_header_get_test.cpp b/third_party/spirv-tools/test/binary_header_get_test.cpp new file mode 100644 index 0000000..3ce0b63 --- /dev/null +++ b/third_party/spirv-tools/test/binary_header_get_test.cpp @@ -0,0 +1,117 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/spirv_constant.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +class BinaryHeaderGet : public ::testing::Test { + public: + BinaryHeaderGet() { memset(code, 0, sizeof(code)); } + + virtual void SetUp() { + code[0] = SpvMagicNumber; + code[1] = SpvVersion; + code[2] = SPV_GENERATOR_CODEPLAY; + code[3] = 1; // NOTE: Bound + code[4] = 0; // NOTE: Schema; reserved + code[5] = 0; // NOTE: Instructions + + binary.code = code; + binary.wordCount = 6; + } + spv_const_binary_t get_const_binary() { + return spv_const_binary_t{binary.code, binary.wordCount}; + } + virtual void TearDown() {} + + uint32_t code[6]; + spv_binary_t binary; +}; + +TEST_F(BinaryHeaderGet, Default) { + spv_endianness_t endian; + spv_const_binary_t const_bin = get_const_binary(); + ASSERT_EQ(SPV_SUCCESS, spvBinaryEndianness(&const_bin, &endian)); + + spv_header_t header; + ASSERT_EQ(SPV_SUCCESS, spvBinaryHeaderGet(&const_bin, endian, &header)); + + ASSERT_EQ(static_cast(SpvMagicNumber), header.magic); + // Expect SPIRV-Headers updated to SPIR-V 1.5. + ASSERT_EQ(0x00010500u, header.version); + ASSERT_EQ(static_cast(SPV_GENERATOR_CODEPLAY), header.generator); + ASSERT_EQ(1u, header.bound); + ASSERT_EQ(0u, header.schema); + ASSERT_EQ(&code[5], header.instructions); +} + +TEST_F(BinaryHeaderGet, InvalidCode) { + spv_const_binary_t my_binary = {nullptr, 0}; + spv_header_t header; + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryHeaderGet(&my_binary, SPV_ENDIANNESS_LITTLE, &header)); +} + +TEST_F(BinaryHeaderGet, InvalidPointerHeader) { + spv_const_binary_t const_bin = get_const_binary(); + ASSERT_EQ(SPV_ERROR_INVALID_POINTER, + spvBinaryHeaderGet(&const_bin, SPV_ENDIANNESS_LITTLE, nullptr)); +} + +TEST_F(BinaryHeaderGet, TruncatedHeader) { + for (uint8_t i = 1; i < SPV_INDEX_INSTRUCTION; i++) { + binary.wordCount = i; + spv_const_binary_t const_bin = get_const_binary(); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryHeaderGet(&const_bin, SPV_ENDIANNESS_LITTLE, nullptr)); + } +} + +TEST_F(BinaryHeaderGet, VersionNonZeroHighByte) { + spv_header_t header; + code[1] = 0xFF010300; + spv_const_binary_t const_bin = get_const_binary(); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryHeaderGet(&const_bin, SPV_ENDIANNESS_LITTLE, &header)); +} + +TEST_F(BinaryHeaderGet, VersionNonZeroLowByte) { + spv_header_t header; + code[1] = 0x000103F0; + spv_const_binary_t const_bin = get_const_binary(); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryHeaderGet(&const_bin, SPV_ENDIANNESS_LITTLE, &header)); +} + +TEST_F(BinaryHeaderGet, VersionTooLow) { + spv_header_t header; + code[1] = 0x00000300; + spv_const_binary_t const_bin = get_const_binary(); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryHeaderGet(&const_bin, SPV_ENDIANNESS_LITTLE, &header)); +} + +TEST_F(BinaryHeaderGet, VersionTooHigh) { + spv_header_t header; + code[1] = 0x000F0300; + spv_const_binary_t const_bin = get_const_binary(); + ASSERT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryHeaderGet(&const_bin, SPV_ENDIANNESS_LITTLE, &header)); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/binary_parse_test.cpp b/third_party/spirv-tools/test/binary_parse_test.cpp new file mode 100644 index 0000000..93e87bd --- /dev/null +++ b/third_party/spirv-tools/test/binary_parse_test.cpp @@ -0,0 +1,893 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/latest_version_opencl_std_header.h" +#include "source/table.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +// Returns true if two spv_parsed_operand_t values are equal. +// To use this operator, this definition must appear in the same namespace +// as spv_parsed_operand_t. +static bool operator==(const spv_parsed_operand_t& a, + const spv_parsed_operand_t& b) { + return a.offset == b.offset && a.num_words == b.num_words && + a.type == b.type && a.number_kind == b.number_kind && + a.number_bit_width == b.number_bit_width; +} + +namespace spvtools { +namespace { + +using ::spvtest::Concatenate; +using ::spvtest::MakeInstruction; +using utils::MakeVector; +using ::spvtest::ScopedContext; +using ::testing::_; +using ::testing::AnyOf; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::Return; + +// An easily-constructible and comparable object for the contents of an +// spv_parsed_instruction_t. Unlike spv_parsed_instruction_t, owns the memory +// of its components. +struct ParsedInstruction { + explicit ParsedInstruction(const spv_parsed_instruction_t& inst) + : words(inst.words, inst.words + inst.num_words), + opcode(static_cast(inst.opcode)), + ext_inst_type(inst.ext_inst_type), + type_id(inst.type_id), + result_id(inst.result_id), + operands(inst.operands, inst.operands + inst.num_operands) {} + + std::vector words; + SpvOp opcode; + spv_ext_inst_type_t ext_inst_type; + uint32_t type_id; + uint32_t result_id; + std::vector operands; + + bool operator==(const ParsedInstruction& b) const { + return words == b.words && opcode == b.opcode && + ext_inst_type == b.ext_inst_type && type_id == b.type_id && + result_id == b.result_id && operands == b.operands; + } +}; + +// Prints a ParsedInstruction object to the given output stream, and returns +// the stream. +std::ostream& operator<<(std::ostream& os, const ParsedInstruction& inst) { + os << "\nParsedInstruction( {"; + spvtest::PrintTo(spvtest::WordVector(inst.words), &os); + os << "}, opcode: " << int(inst.opcode) + << " ext_inst_type: " << int(inst.ext_inst_type) + << " type_id: " << inst.type_id << " result_id: " << inst.result_id; + for (const auto& operand : inst.operands) { + os << " { offset: " << operand.offset << " num_words: " << operand.num_words + << " type: " << int(operand.type) + << " number_kind: " << int(operand.number_kind) + << " number_bit_width: " << int(operand.number_bit_width) << "}"; + } + os << ")"; + return os; +} + +// Basic check for the equality operator on ParsedInstruction. +TEST(ParsedInstruction, ZeroInitializedAreEqual) { + spv_parsed_instruction_t pi = {}; + ParsedInstruction a(pi); + ParsedInstruction b(pi); + EXPECT_THAT(a, ::testing::TypedEq(b)); +} + +// Googlemock class receiving Header/Instruction calls from spvBinaryParse(). +class MockParseClient { + public: + MOCK_METHOD6(Header, spv_result_t(spv_endianness_t endian, uint32_t magic, + uint32_t version, uint32_t generator, + uint32_t id_bound, uint32_t reserved)); + MOCK_METHOD1(Instruction, spv_result_t(const ParsedInstruction&)); +}; + +// Casts user_data as MockParseClient and invokes its Header(). +spv_result_t invoke_header(void* user_data, spv_endianness_t endian, + uint32_t magic, uint32_t version, uint32_t generator, + uint32_t id_bound, uint32_t reserved) { + return static_cast(user_data)->Header( + endian, magic, version, generator, id_bound, reserved); +} + +// Casts user_data as MockParseClient and invokes its Instruction(). +spv_result_t invoke_instruction( + void* user_data, const spv_parsed_instruction_t* parsed_instruction) { + return static_cast(user_data)->Instruction( + ParsedInstruction(*parsed_instruction)); +} + +// The SPIR-V module header words for the Khronos Assembler generator, +// for a module with an ID bound of 1. +const uint32_t kHeaderForBound1[] = { + SpvMagicNumber, SpvVersion, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), 1 /*bound*/, + 0 /*schema*/}; + +// Returns the expected SPIR-V module header words for the Khronos +// Assembler generator, and with a given Id bound. +std::vector ExpectedHeaderForBound(uint32_t bound) { + return {SpvMagicNumber, 0x10000, + SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), bound, 0}; +} + +// Returns a parsed operand for a non-number value at the given word offset +// within an instruction. +spv_parsed_operand_t MakeSimpleOperand(uint16_t offset, + spv_operand_type_t type) { + return {offset, 1, type, SPV_NUMBER_NONE, 0}; +} + +// Returns a parsed operand for a literal unsigned integer value at the given +// word offset within an instruction. +spv_parsed_operand_t MakeLiteralNumberOperand(uint16_t offset) { + return {offset, 1, SPV_OPERAND_TYPE_LITERAL_INTEGER, SPV_NUMBER_UNSIGNED_INT, + 32}; +} + +// Returns a parsed operand for a literal string value at the given +// word offset within an instruction. +spv_parsed_operand_t MakeLiteralStringOperand(uint16_t offset, + uint16_t length) { + return {offset, length, SPV_OPERAND_TYPE_LITERAL_STRING, SPV_NUMBER_NONE, 0}; +} + +// Returns a ParsedInstruction for an OpTypeVoid instruction that would +// generate the given result Id. +ParsedInstruction MakeParsedVoidTypeInstruction(uint32_t result_id) { + const auto void_inst = MakeInstruction(SpvOpTypeVoid, {result_id}); + const auto void_operands = std::vector{ + MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID)}; + const spv_parsed_instruction_t parsed_void_inst = { + void_inst.data(), + static_cast(void_inst.size()), + SpvOpTypeVoid, + SPV_EXT_INST_TYPE_NONE, + 0, // type id + result_id, + void_operands.data(), + static_cast(void_operands.size())}; + return ParsedInstruction(parsed_void_inst); +} + +// Returns a ParsedInstruction for an OpTypeInt instruction that generates +// the given result Id for a 32-bit signed integer scalar type. +ParsedInstruction MakeParsedInt32TypeInstruction(uint32_t result_id) { + const auto i32_inst = MakeInstruction(SpvOpTypeInt, {result_id, 32, 1}); + const auto i32_operands = std::vector{ + MakeSimpleOperand(1, SPV_OPERAND_TYPE_RESULT_ID), + MakeLiteralNumberOperand(2), MakeLiteralNumberOperand(3)}; + spv_parsed_instruction_t parsed_i32_inst = { + i32_inst.data(), + static_cast(i32_inst.size()), + SpvOpTypeInt, + SPV_EXT_INST_TYPE_NONE, + 0, // type id + result_id, + i32_operands.data(), + static_cast(i32_operands.size())}; + return ParsedInstruction(parsed_i32_inst); +} + +class BinaryParseTest : public spvtest::TextToBinaryTestBase<::testing::Test> { + protected: + ~BinaryParseTest() { spvDiagnosticDestroy(diagnostic_); } + + void Parse(const SpirvVector& words, spv_result_t expected_result, + bool flip_words = false) { + SpirvVector flipped_words(words); + SCOPED_TRACE(flip_words ? "Flipped Endianness" : "Normal Endianness"); + if (flip_words) { + std::transform(flipped_words.begin(), flipped_words.end(), + flipped_words.begin(), [](const uint32_t raw_word) { + return spvFixWord(raw_word, + I32_ENDIAN_HOST == I32_ENDIAN_BIG + ? SPV_ENDIANNESS_LITTLE + : SPV_ENDIANNESS_BIG); + }); + } + EXPECT_EQ(expected_result, + spvBinaryParse(ScopedContext().context, &client_, + flipped_words.data(), flipped_words.size(), + invoke_header, invoke_instruction, &diagnostic_)); + } + + spv_diagnostic diagnostic_ = nullptr; + MockParseClient client_; +}; + +// Adds an EXPECT_CALL to client_->Header() with appropriate parameters, +// including bound. Returns the EXPECT_CALL result. +#define EXPECT_HEADER(bound) \ + EXPECT_CALL( \ + client_, \ + Header(AnyOf(SPV_ENDIANNESS_LITTLE, SPV_ENDIANNESS_BIG), SpvMagicNumber, \ + 0x10000, SPV_GENERATOR_WORD(SPV_GENERATOR_KHRONOS_ASSEMBLER, 0), \ + bound, 0 /*reserved*/)) + +static const bool kSwapEndians[] = {false, true}; + +TEST_F(BinaryParseTest, EmptyModuleHasValidHeaderAndNoInstructionCallbacks) { + for (bool endian_swap : kSwapEndians) { + const auto words = CompileSuccessfully(""); + EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + Parse(words, SPV_SUCCESS, endian_swap); + EXPECT_EQ(nullptr, diagnostic_); + } +} + +TEST_F(BinaryParseTest, NullDiagnosticsIsOkForGoodParse) { + const auto words = CompileSuccessfully(""); + EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + EXPECT_EQ( + SPV_SUCCESS, + spvBinaryParse(ScopedContext().context, &client_, words.data(), + words.size(), invoke_header, invoke_instruction, nullptr)); +} + +TEST_F(BinaryParseTest, NullDiagnosticsIsOkForBadParse) { + auto words = CompileSuccessfully(""); + words.push_back(0xffffffff); // Certainly invalid instruction header. + EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + EXPECT_EQ( + SPV_ERROR_INVALID_BINARY, + spvBinaryParse(ScopedContext().context, &client_, words.data(), + words.size(), invoke_header, invoke_instruction, nullptr)); +} + +// Make sure that we don't blow up when both the consumer and the diagnostic are +// null. +TEST_F(BinaryParseTest, NullConsumerNullDiagnosticsForBadParse) { + auto words = CompileSuccessfully(""); + + auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1); + ctx.SetMessageConsumer(nullptr); + + words.push_back(0xffffffff); // Certainly invalid instruction header. + EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(), + invoke_header, invoke_instruction, nullptr)); +} + +TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForGoodParse) { + const auto words = CompileSuccessfully(""); + + auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*, + const spv_position_t&, + const char*) { ++invocation; }); + + EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + EXPECT_EQ(SPV_SUCCESS, + spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(), + invoke_header, invoke_instruction, nullptr)); + EXPECT_EQ(0, invocation); +} + +TEST_F(BinaryParseTest, SpecifyConsumerNullDiagnosticsForBadParse) { + auto words = CompileSuccessfully(""); + + auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + ctx.SetMessageConsumer( + [&invocation](spv_message_level_t level, const char* source, + const spv_position_t& position, const char* message) { + ++invocation; + EXPECT_EQ(SPV_MSG_ERROR, level); + EXPECT_STREQ("input", source); + EXPECT_EQ(0u, position.line); + EXPECT_EQ(0u, position.column); + EXPECT_EQ(1u, position.index); + EXPECT_STREQ("Invalid opcode: 65535", message); + }); + + words.push_back(0xffffffff); // Certainly invalid instruction header. + EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(), + invoke_header, invoke_instruction, nullptr)); + EXPECT_EQ(1, invocation); +} + +TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForGoodParse) { + const auto words = CompileSuccessfully(""); + + auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*, + const spv_position_t&, + const char*) { ++invocation; }); + + EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + EXPECT_EQ(SPV_SUCCESS, + spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(), + invoke_header, invoke_instruction, &diagnostic_)); + EXPECT_EQ(0, invocation); + EXPECT_EQ(nullptr, diagnostic_); +} + +TEST_F(BinaryParseTest, SpecifyConsumerSpecifyDiagnosticsForBadParse) { + auto words = CompileSuccessfully(""); + + auto ctx = spvtools::Context(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + ctx.SetMessageConsumer([&invocation](spv_message_level_t, const char*, + const spv_position_t&, + const char*) { ++invocation; }); + + words.push_back(0xffffffff); // Certainly invalid instruction header. + EXPECT_HEADER(1).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryParse(ctx.CContext(), &client_, words.data(), words.size(), + invoke_header, invoke_instruction, &diagnostic_)); + EXPECT_EQ(0, invocation); + EXPECT_STREQ("Invalid opcode: 65535", diagnostic_->error); +} + +TEST_F(BinaryParseTest, + ModuleWithSingleInstructionHasValidHeaderAndInstructionCallback) { + for (bool endian_swap : kSwapEndians) { + const auto words = CompileSuccessfully("%1 = OpTypeVoid"); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(2).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1))) + .WillOnce(Return(SPV_SUCCESS)); + Parse(words, SPV_SUCCESS, endian_swap); + EXPECT_EQ(nullptr, diagnostic_); + } +} + +TEST_F(BinaryParseTest, NullHeaderCallbackIsIgnored) { + const auto words = CompileSuccessfully("%1 = OpTypeVoid"); + EXPECT_CALL(client_, Header(_, _, _, _, _, _)) + .Times(0); // No header callback. + EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1))) + .WillOnce(Return(SPV_SUCCESS)); + EXPECT_EQ(SPV_SUCCESS, spvBinaryParse(ScopedContext().context, &client_, + words.data(), words.size(), nullptr, + invoke_instruction, &diagnostic_)); + EXPECT_EQ(nullptr, diagnostic_); +} + +TEST_F(BinaryParseTest, NullInstructionCallbackIsIgnored) { + const auto words = CompileSuccessfully("%1 = OpTypeVoid"); + EXPECT_HEADER((2)).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).Times(0); // No instruction callback. + EXPECT_EQ(SPV_SUCCESS, + spvBinaryParse(ScopedContext().context, &client_, words.data(), + words.size(), invoke_header, nullptr, &diagnostic_)); + EXPECT_EQ(nullptr, diagnostic_); +} + +// Check the result of multiple instruction callbacks. +// +// This test exercises non-default values for the following members of the +// spv_parsed_instruction_t struct: words, num_words, opcode, result_id, +// operands, num_operands. +TEST_F(BinaryParseTest, TwoScalarTypesGenerateTwoInstructionCallbacks) { + for (bool endian_swap : kSwapEndians) { + const auto words = CompileSuccessfully( + "%1 = OpTypeVoid " + "%2 = OpTypeInt 32 1"); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(3).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1))) + .WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2))) + .WillOnce(Return(SPV_SUCCESS)); + Parse(words, SPV_SUCCESS, endian_swap); + EXPECT_EQ(nullptr, diagnostic_); + } +} + +TEST_F(BinaryParseTest, EarlyReturnWithZeroPassingCallbacks) { + for (bool endian_swap : kSwapEndians) { + const auto words = CompileSuccessfully( + "%1 = OpTypeVoid " + "%2 = OpTypeInt 32 1"); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(3).WillOnce(Return(SPV_ERROR_INVALID_BINARY)); + // Early exit means no calls to Instruction(). + EXPECT_CALL(client_, Instruction(_)).Times(0); + Parse(words, SPV_ERROR_INVALID_BINARY, endian_swap); + // On error, the binary parser doesn't generate its own diagnostics. + EXPECT_EQ(nullptr, diagnostic_); + } +} + +TEST_F(BinaryParseTest, + EarlyReturnWithZeroPassingCallbacksAndSpecifiedResultCode) { + for (bool endian_swap : kSwapEndians) { + const auto words = CompileSuccessfully( + "%1 = OpTypeVoid " + "%2 = OpTypeInt 32 1"); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(3).WillOnce(Return(SPV_REQUESTED_TERMINATION)); + // Early exit means no calls to Instruction(). + EXPECT_CALL(client_, Instruction(_)).Times(0); + Parse(words, SPV_REQUESTED_TERMINATION, endian_swap); + // On early termination, the binary parser doesn't generate its own + // diagnostics. + EXPECT_EQ(nullptr, diagnostic_); + } +} + +TEST_F(BinaryParseTest, EarlyReturnWithOnePassingCallback) { + for (bool endian_swap : kSwapEndians) { + const auto words = CompileSuccessfully( + "%1 = OpTypeVoid " + "%2 = OpTypeInt 32 1 " + "%3 = OpTypeFloat 32"); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1))) + .WillOnce(Return(SPV_REQUESTED_TERMINATION)); + Parse(words, SPV_REQUESTED_TERMINATION, endian_swap); + // On early termination, the binary parser doesn't generate its own + // diagnostics. + EXPECT_EQ(nullptr, diagnostic_); + } +} + +TEST_F(BinaryParseTest, EarlyReturnWithTwoPassingCallbacks) { + for (bool endian_swap : kSwapEndians) { + const auto words = CompileSuccessfully( + "%1 = OpTypeVoid " + "%2 = OpTypeInt 32 1 " + "%3 = OpTypeFloat 32"); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(4).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(MakeParsedVoidTypeInstruction(1))) + .WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(MakeParsedInt32TypeInstruction(2))) + .WillOnce(Return(SPV_REQUESTED_TERMINATION)); + Parse(words, SPV_REQUESTED_TERMINATION, endian_swap); + // On early termination, the binary parser doesn't generate its own + // diagnostics. + EXPECT_EQ(nullptr, diagnostic_); + } +} + +TEST_F(BinaryParseTest, InstructionWithStringOperand) { + const std::string str = + "the future is already here, it's just not evenly distributed"; + const auto str_words = MakeVector(str); + const auto instruction = MakeInstruction(SpvOpName, {99}, str_words); + const auto words = Concatenate({ExpectedHeaderForBound(100), instruction}); + InSequence calls_expected_in_specific_order; + EXPECT_HEADER(100).WillOnce(Return(SPV_SUCCESS)); + const auto operands = std::vector{ + MakeSimpleOperand(1, SPV_OPERAND_TYPE_ID), + MakeLiteralStringOperand(2, static_cast(str_words.size()))}; + EXPECT_CALL(client_, + Instruction(ParsedInstruction(spv_parsed_instruction_t{ + instruction.data(), static_cast(instruction.size()), + SpvOpName, SPV_EXT_INST_TYPE_NONE, 0 /*type id*/, + 0 /* No result id for OpName*/, operands.data(), + static_cast(operands.size())}))) + .WillOnce(Return(SPV_SUCCESS)); + // Since we are actually checking the output, don't test the + // endian-swapped version. + Parse(words, SPV_SUCCESS, false); + EXPECT_EQ(nullptr, diagnostic_); +} + +// Checks for non-zero values for the result_id and ext_inst_type members +// spv_parsed_instruction_t. +TEST_F(BinaryParseTest, ExtendedInstruction) { + const auto words = CompileSuccessfully( + "%extcl = OpExtInstImport \"OpenCL.std\" " + "%result = OpExtInst %float %extcl sqrt %x"); + EXPECT_HEADER(5).WillOnce(Return(SPV_SUCCESS)); + EXPECT_CALL(client_, Instruction(_)).WillOnce(Return(SPV_SUCCESS)); + // We're only interested in the second call to Instruction(): + const auto operands = std::vector{ + MakeSimpleOperand(1, SPV_OPERAND_TYPE_TYPE_ID), + MakeSimpleOperand(2, SPV_OPERAND_TYPE_RESULT_ID), + MakeSimpleOperand(3, + SPV_OPERAND_TYPE_ID), // Extended instruction set Id + MakeSimpleOperand(4, SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER), + MakeSimpleOperand(5, SPV_OPERAND_TYPE_ID), // Id of the argument + }; + const auto instruction = MakeInstruction( + SpvOpExtInst, + {2, 3, 1, static_cast(OpenCLLIB::Entrypoints::Sqrt), 4}); + EXPECT_CALL(client_, + Instruction(ParsedInstruction(spv_parsed_instruction_t{ + instruction.data(), static_cast(instruction.size()), + SpvOpExtInst, SPV_EXT_INST_TYPE_OPENCL_STD, 2 /*type id*/, + 3 /*result id*/, operands.data(), + static_cast(operands.size())}))) + .WillOnce(Return(SPV_SUCCESS)); + // Since we are actually checking the output, don't test the + // endian-swapped version. + Parse(words, SPV_SUCCESS, false); + EXPECT_EQ(nullptr, diagnostic_); +} + +// A binary parser diagnostic test case where we provide the words array +// pointer and word count explicitly. +struct WordsAndCountDiagnosticCase { + const uint32_t* words; + size_t num_words; + std::string expected_diagnostic; +}; + +using BinaryParseWordsAndCountDiagnosticTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>; + +TEST_P(BinaryParseWordsAndCountDiagnosticTest, WordAndCountCases) { + EXPECT_EQ( + SPV_ERROR_INVALID_BINARY, + spvBinaryParse(ScopedContext().context, nullptr, GetParam().words, + GetParam().num_words, nullptr, nullptr, &diagnostic)); + ASSERT_NE(nullptr, diagnostic); + EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic)); +} + +INSTANTIATE_TEST_SUITE_P( + BinaryParseDiagnostic, BinaryParseWordsAndCountDiagnosticTest, + ::testing::ValuesIn(std::vector{ + {nullptr, 0, "Missing module."}, + {kHeaderForBound1, 0, + "Module has incomplete header: only 0 words instead of 5"}, + {kHeaderForBound1, 1, + "Module has incomplete header: only 1 words instead of 5"}, + {kHeaderForBound1, 2, + "Module has incomplete header: only 2 words instead of 5"}, + {kHeaderForBound1, 3, + "Module has incomplete header: only 3 words instead of 5"}, + {kHeaderForBound1, 4, + "Module has incomplete header: only 4 words instead of 5"}, + })); + +// A binary parser diagnostic test case where a vector of words is +// provided. We'll use this to express cases that can't be created +// via the assembler. Either we want to make a malformed instruction, +// or an invalid case the assembler would reject. +struct WordVectorDiagnosticCase { + std::vector words; + std::string expected_diagnostic; +}; + +using BinaryParseWordVectorDiagnosticTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>; + +TEST_P(BinaryParseWordVectorDiagnosticTest, WordVectorCases) { + const auto& words = GetParam().words; + EXPECT_THAT(spvBinaryParse(ScopedContext().context, nullptr, words.data(), + words.size(), nullptr, nullptr, &diagnostic), + AnyOf(SPV_ERROR_INVALID_BINARY, SPV_ERROR_INVALID_ID)); + ASSERT_NE(nullptr, diagnostic); + EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic)); +} + +INSTANTIATE_TEST_SUITE_P( + BinaryParseDiagnostic, BinaryParseWordVectorDiagnosticTest, + ::testing::ValuesIn(std::vector{ + {Concatenate({ExpectedHeaderForBound(1), {spvOpcodeMake(0, SpvOpNop)}}), + "Invalid instruction word count: 0"}, + {Concatenate( + {ExpectedHeaderForBound(1), + {spvOpcodeMake(1, static_cast( + std::numeric_limits::max()))}}), + "Invalid opcode: 65535"}, + {Concatenate({ExpectedHeaderForBound(1), + MakeInstruction(SpvOpNop, {42})}), + "Invalid instruction OpNop starting at word 5: expected " + "no more operands after 1 words, but stated word count is 2."}, + // Supply several more unexpectd words. + {Concatenate({ExpectedHeaderForBound(1), + MakeInstruction(SpvOpNop, {42, 43, 44, 45, 46, 47})}), + "Invalid instruction OpNop starting at word 5: expected " + "no more operands after 1 words, but stated word count is 7."}, + {Concatenate({ExpectedHeaderForBound(1), + MakeInstruction(SpvOpTypeVoid, {1, 2})}), + "Invalid instruction OpTypeVoid starting at word 5: expected " + "no more operands after 2 words, but stated word count is 3."}, + {Concatenate({ExpectedHeaderForBound(1), + MakeInstruction(SpvOpTypeVoid, {1, 2, 5, 9, 10})}), + "Invalid instruction OpTypeVoid starting at word 5: expected " + "no more operands after 2 words, but stated word count is 6."}, + {Concatenate({ExpectedHeaderForBound(1), + MakeInstruction(SpvOpTypeInt, {1, 32, 1, 9})}), + "Invalid instruction OpTypeInt starting at word 5: expected " + "no more operands after 4 words, but stated word count is 5."}, + {Concatenate({ExpectedHeaderForBound(1), + MakeInstruction(SpvOpTypeInt, {1})}), + "End of input reached while decoding OpTypeInt starting at word 5:" + " expected more operands after 2 words."}, + + // Check several cases for running off the end of input. + + // Detect a missing single word operand. + {Concatenate({ExpectedHeaderForBound(1), + {spvOpcodeMake(2, SpvOpTypeStruct)}}), + "End of input reached while decoding OpTypeStruct starting at word" + " 5: missing result ID operand at word offset 1."}, + // Detect this a missing a multi-word operand to OpConstant. + // We also lie and say the OpConstant instruction has 5 words when + // it only has 3. Corresponds to something like this: + // %1 = OpTypeInt 64 0 + // %2 = OpConstant %1 + {Concatenate({ExpectedHeaderForBound(3), + {MakeInstruction(SpvOpTypeInt, {1, 64, 0})}, + {spvOpcodeMake(5, SpvOpConstant), 1, 2}}), + "End of input reached while decoding OpConstant starting at word" + " 9: missing possibly multi-word literal number operand at word " + "offset 3."}, + // Detect when we provide only one word from the 64-bit literal, + // and again lie about the number of words in the instruction. + {Concatenate({ExpectedHeaderForBound(3), + {MakeInstruction(SpvOpTypeInt, {1, 64, 0})}, + {spvOpcodeMake(5, SpvOpConstant), 1, 2, 42}}), + "End of input reached while decoding OpConstant starting at word" + " 9: truncated possibly multi-word literal number operand at word " + "offset 3."}, + // Detect when a required string operand is missing. + // Also, lie about the length of the instruction. + {Concatenate({ExpectedHeaderForBound(3), + {spvOpcodeMake(3, SpvOpString), 1}}), + "End of input reached while decoding OpString starting at word" + " 5: missing literal string operand at word offset 2."}, + // Detect when a required string operand is truncated: it's missing + // a null terminator. Catching the error avoids a buffer overrun. + {Concatenate({ExpectedHeaderForBound(3), + {spvOpcodeMake(4, SpvOpString), 1, 0x41414141, + 0x41414141}}), + "End of input reached while decoding OpString starting at word" + " 5: truncated literal string operand at word offset 2."}, + // Detect when an optional string operand is truncated: it's missing + // a null terminator. Catching the error avoids a buffer overrun. + // (It is valid for an optional string operand to be absent.) + {Concatenate({ExpectedHeaderForBound(3), + {spvOpcodeMake(6, SpvOpSource), + static_cast(SpvSourceLanguageOpenCL_C), 210, + 1 /* file id */, + /*start of string*/ 0x41414141, 0x41414141}}), + "End of input reached while decoding OpSource starting at word" + " 5: truncated literal string operand at word offset 4."}, + + // (End of input exhaustion test cases.) + + // In this case the instruction word count is too small, where + // it would truncate a multi-word operand to OpConstant. + {Concatenate({ExpectedHeaderForBound(3), + {MakeInstruction(SpvOpTypeInt, {1, 64, 0})}, + {spvOpcodeMake(4, SpvOpConstant), 1, 2, 44, 44}}), + "Invalid word count: OpConstant starting at word 9 says it has 4" + " words, but found 5 words instead."}, + // Word count is to small, where it would truncate a literal string. + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(3, SpvOpString), 1, 0x41414141, 0}}), + "Invalid word count: OpString starting at word 5 says it has 3" + " words, but found 4 words instead."}, + // Word count is too large. The string terminates before the last + // word. + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(4, SpvOpString), 1 /* result id */}, + MakeVector("abc"), + {0 /* this word does not belong*/}}), + "Invalid instruction OpString starting at word 5: expected no more" + " operands after 3 words, but stated word count is 4."}, + // Word count is too large. There are too many words after the string + // literal. A linkage attribute decoration is the only case in SPIR-V + // where a string operand is followed by another operand. + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(6, SpvOpDecorate), 1 /* target id */, + static_cast(SpvDecorationLinkageAttributes)}, + MakeVector("abc"), + {static_cast(SpvLinkageTypeImport), + 0 /* does not belong */}}), + "Invalid instruction OpDecorate starting at word 5: expected no more" + " operands after 5 words, but stated word count is 6."}, + // Like the previous case, but with 5 extra words. + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(10, SpvOpDecorate), 1 /* target id */, + static_cast(SpvDecorationLinkageAttributes)}, + MakeVector("abc"), + {static_cast(SpvLinkageTypeImport), + /* don't belong */ 0, 1, 2, 3, 4}}), + "Invalid instruction OpDecorate starting at word 5: expected no more" + " operands after 5 words, but stated word count is 10."}, + // Like the previous two cases, but with OpMemberDecorate. + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(7, SpvOpMemberDecorate), 1 /* target id */, + 42 /* member index */, + static_cast(SpvDecorationLinkageAttributes)}, + MakeVector("abc"), + {static_cast(SpvLinkageTypeImport), + 0 /* does not belong */}}), + "Invalid instruction OpMemberDecorate starting at word 5: expected no" + " more operands after 6 words, but stated word count is 7."}, + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(11, SpvOpMemberDecorate), + 1 /* target id */, 42 /* member index */, + static_cast(SpvDecorationLinkageAttributes)}, + MakeVector("abc"), + {static_cast(SpvLinkageTypeImport), + /* don't belong */ 0, 1, 2, 3, 4}}), + "Invalid instruction OpMemberDecorate starting at word 5: expected no" + " more operands after 6 words, but stated word count is 11."}, + // Word count is too large. There should be no more words + // after the RelaxedPrecision decoration. + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(4, SpvOpDecorate), 1 /* target id */, + static_cast(SpvDecorationRelaxedPrecision), + 0 /* does not belong */}}), + "Invalid instruction OpDecorate starting at word 5: expected no" + " more operands after 3 words, but stated word count is 4."}, + // Word count is too large. There should be only one word after + // the SpecId decoration enum word. + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(5, SpvOpDecorate), 1 /* target id */, + static_cast(SpvDecorationSpecId), + 42 /* the spec id */, 0 /* does not belong */}}), + "Invalid instruction OpDecorate starting at word 5: expected no" + " more operands after 4 words, but stated word count is 5."}, + {Concatenate({ExpectedHeaderForBound(2), + {spvOpcodeMake(2, SpvOpTypeVoid), 0}}), + "Error: Result Id is 0"}, + {Concatenate({ + ExpectedHeaderForBound(2), + {spvOpcodeMake(2, SpvOpTypeVoid), 1}, + {spvOpcodeMake(2, SpvOpTypeBool), 1}, + }), + "Id 1 is defined more than once"}, + {Concatenate({ExpectedHeaderForBound(3), + MakeInstruction(SpvOpExtInst, {2, 3, 100, 4, 5})}), + "OpExtInst set Id 100 does not reference an OpExtInstImport result " + "Id"}, + {Concatenate({ExpectedHeaderForBound(101), + MakeInstruction(SpvOpExtInstImport, {100}, + MakeVector("OpenCL.std")), + // OpenCL cos is #14 + MakeInstruction(SpvOpExtInst, {2, 3, 100, 14, 5, 999})}), + "Invalid instruction OpExtInst starting at word 10: expected no " + "more operands after 6 words, but stated word count is 7."}, + // In this case, the OpSwitch selector refers to an invalid ID. + {Concatenate({ExpectedHeaderForBound(3), + MakeInstruction(SpvOpSwitch, {1, 2, 42, 3})}), + "Invalid OpSwitch: selector id 1 has no type"}, + // In this case, the OpSwitch selector refers to an ID that has + // no type. + {Concatenate({ExpectedHeaderForBound(3), + MakeInstruction(SpvOpLabel, {1}), + MakeInstruction(SpvOpSwitch, {1, 2, 42, 3})}), + "Invalid OpSwitch: selector id 1 has no type"}, + {Concatenate({ExpectedHeaderForBound(3), + MakeInstruction(SpvOpTypeInt, {1, 32, 0}), + MakeInstruction(SpvOpSwitch, {1, 3, 42, 3})}), + "Invalid OpSwitch: selector id 1 is a type, not a value"}, + {Concatenate({ExpectedHeaderForBound(3), + MakeInstruction(SpvOpTypeFloat, {1, 32}), + MakeInstruction(SpvOpConstant, {1, 2, 0x78f00000}), + MakeInstruction(SpvOpSwitch, {2, 3, 42, 3})}), + "Invalid OpSwitch: selector id 2 is not a scalar integer"}, + {Concatenate({ExpectedHeaderForBound(3), + MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("invalid-import"))}), + "Invalid extended instruction import 'invalid-import'"}, + {Concatenate({ + ExpectedHeaderForBound(3), + MakeInstruction(SpvOpTypeInt, {1, 32, 0}), + MakeInstruction(SpvOpConstant, {2, 2, 42}), + }), + "Type Id 2 is not a type"}, + {Concatenate({ + ExpectedHeaderForBound(3), + MakeInstruction(SpvOpTypeBool, {1}), + MakeInstruction(SpvOpConstant, {1, 2, 42}), + }), + "Type Id 1 is not a scalar numeric type"}, + })); + +// A binary parser diagnostic case generated from an assembly text input. +struct AssemblyDiagnosticCase { + std::string assembly; + std::string expected_diagnostic; +}; + +using BinaryParseAssemblyDiagnosticTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>; + +TEST_P(BinaryParseAssemblyDiagnosticTest, AssemblyCases) { + auto words = CompileSuccessfully(GetParam().assembly); + EXPECT_THAT(spvBinaryParse(ScopedContext().context, nullptr, words.data(), + words.size(), nullptr, nullptr, &diagnostic), + AnyOf(SPV_ERROR_INVALID_BINARY, SPV_ERROR_INVALID_ID)); + ASSERT_NE(nullptr, diagnostic); + EXPECT_THAT(diagnostic->error, Eq(GetParam().expected_diagnostic)); +} + +INSTANTIATE_TEST_SUITE_P( + BinaryParseDiagnostic, BinaryParseAssemblyDiagnosticTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpConstant !0 42", "Error: Type Id is 0"}, + // A required id is 0. + {"OpName !0 \"foo\"", "Id is 0"}, + // An optional id is 0, in this case the optional + // initializer. + {"%2 = OpVariable %1 CrossWorkgroup !0", "Id is 0"}, + {"OpControlBarrier !0 %1 %2", "scope ID is 0"}, + {"OpControlBarrier %1 !0 %2", "scope ID is 0"}, + {"OpControlBarrier %1 %2 !0", "memory semantics ID is 0"}, + {"%import = OpExtInstImport \"GLSL.std.450\" " + "%result = OpExtInst %type %import !999999 %x", + "Invalid extended instruction number: 999999"}, + {"%2 = OpSpecConstantOp %1 !1000 %2", + "Invalid OpSpecConstantOp opcode: 1000"}, + {"OpCapability !9999", "Invalid capability operand: 9999"}, + {"OpSource !9999 100", "Invalid source language operand: 9999"}, + {"OpEntryPoint !9999", "Invalid execution model operand: 9999"}, + {"OpMemoryModel !9999", "Invalid addressing model operand: 9999"}, + {"OpMemoryModel Logical !9999", "Invalid memory model operand: 9999"}, + {"OpExecutionMode %1 !9999", "Invalid execution mode operand: 9999"}, + {"OpTypeForwardPointer %1 !9999", + "Invalid storage class operand: 9999"}, + {"%2 = OpTypeImage %1 !9999", "Invalid dimensionality operand: 9999"}, + {"%2 = OpTypeImage %1 1D 0 0 0 0 !9999", + "Invalid image format operand: 9999"}, + {"OpDecorate %1 FPRoundingMode !9999", + "Invalid floating-point rounding mode operand: 9999"}, + {"OpDecorate %1 LinkageAttributes \"C\" !9999", + "Invalid linkage type operand: 9999"}, + {"%1 = OpTypePipe !9999", "Invalid access qualifier operand: 9999"}, + {"OpDecorate %1 FuncParamAttr !9999", + "Invalid function parameter attribute operand: 9999"}, + {"OpDecorate %1 !9999", "Invalid decoration operand: 9999"}, + {"OpDecorate %1 BuiltIn !9999", "Invalid built-in operand: 9999"}, + {"%2 = OpGroupIAdd %1 %3 !9999", + "Invalid group operation operand: 9999"}, + {"OpDecorate %1 FPFastMathMode !63", + "Invalid floating-point fast math mode operand: 63 has invalid mask " + "component 32"}, + {"%2 = OpFunction %2 !31", + "Invalid function control operand: 31 has invalid mask component 16"}, + {"OpLoopMerge %1 %2 !1027", + "Invalid loop control operand: 1027 has invalid mask component 1024"}, + {"%2 = OpImageFetch %1 %image %coord !32770", + "Invalid image operand: 32770 has invalid mask component 32768"}, + {"OpSelectionMerge %1 !7", + "Invalid selection control operand: 7 has invalid mask component 4"}, + })); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/binary_strnlen_s_test.cpp b/third_party/spirv-tools/test/binary_strnlen_s_test.cpp new file mode 100644 index 0000000..5f43bde --- /dev/null +++ b/third_party/spirv-tools/test/binary_strnlen_s_test.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +TEST(Strnlen, Samples) { + EXPECT_EQ(0u, spv_strnlen_s(nullptr, 0)); + EXPECT_EQ(0u, spv_strnlen_s(nullptr, 5)); + EXPECT_EQ(0u, spv_strnlen_s("abc", 0)); + EXPECT_EQ(1u, spv_strnlen_s("abc", 1)); + EXPECT_EQ(3u, spv_strnlen_s("abc", 3)); + EXPECT_EQ(3u, spv_strnlen_s("abc\0", 5)); + EXPECT_EQ(0u, spv_strnlen_s("\0", 5)); + EXPECT_EQ(1u, spv_strnlen_s("a\0c", 5)); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/binary_to_text.literal_test.cpp b/third_party/spirv-tools/test/binary_to_text.literal_test.cpp new file mode 100644 index 0000000..02daac7 --- /dev/null +++ b/third_party/spirv-tools/test/binary_to_text.literal_test.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::testing::Eq; +using RoundTripLiteralsTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(RoundTripLiteralsTest, Sample) { + EXPECT_THAT(EncodeAndDecodeSuccessfully(GetParam()), Eq(GetParam())); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + StringLiterals, RoundTripLiteralsTest, + ::testing::ValuesIn(std::vector{ + "OpName %1 \"\"\n", // empty + "OpName %1 \"foo\"\n", // normal + "OpName %1 \"foo bar\"\n", // string with spaces + "OpName %1 \"foo\tbar\"\n", // string with tab + "OpName %1 \"\tfoo\"\n", // starts with tab + "OpName %1 \" foo\"\n", // starts with space + "OpName %1 \"foo \"\n", // ends with space + "OpName %1 \"foo\t\"\n", // ends with tab + "OpName %1 \"foo\nbar\"\n", // contains newline + "OpName %1 \"\nfoo\nbar\"\n", // starts with newline + "OpName %1 \"\n\n\nfoo\nbar\"\n", // multiple newlines + "OpName %1 \"\\\"foo\nbar\\\"\"\n", // escaped quote + "OpName %1 \"\\\\foo\nbar\\\\\"\n", // escaped backslash + "OpName %1 \"\xE4\xBA\xB2\"\n", // UTF-8 + })); +// clang-format on + +using RoundTripSpecialCaseLiteralsTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +// Test case where the generated disassembly is not the same as the +// assembly passed in. +TEST_P(RoundTripSpecialCaseLiteralsTest, Sample) { + EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<0>(GetParam())), + Eq(std::get<1>(GetParam()))); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + StringLiterals, RoundTripSpecialCaseLiteralsTest, + ::testing::ValuesIn(std::vector>{ + {"OpName %1 \"\\foo\"\n", "OpName %1 \"foo\"\n"}, // Escape f + {"OpName %1 \"\\\nfoo\"\n", "OpName %1 \"\nfoo\"\n"}, // Escape newline + {"OpName %1 \"\\\xE4\xBA\xB2\"\n", "OpName %1 \"\xE4\xBA\xB2\"\n"}, // Escape utf-8 + })); +// clang-format on + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/binary_to_text_test.cpp b/third_party/spirv-tools/test/binary_to_text_test.cpp new file mode 100644 index 0000000..e8a02fd --- /dev/null +++ b/third_party/spirv-tools/test/binary_to_text_test.cpp @@ -0,0 +1,561 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/spirv_constant.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::AutoText; +using spvtest::ScopedContext; +using spvtest::TextToBinaryTest; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::HasSubstr; + +class BinaryToText : public ::testing::Test { + public: + BinaryToText() + : context(spvContextCreate(SPV_ENV_UNIVERSAL_1_0)), binary(nullptr) {} + ~BinaryToText() { + spvBinaryDestroy(binary); + spvContextDestroy(context); + } + + virtual void SetUp() { + const char* textStr = R"( + OpSource OpenCL_C 12 + OpMemoryModel Physical64 OpenCL + OpSourceExtension "PlaceholderExtensionName" + OpEntryPoint Kernel %1 "foo" + OpExecutionMode %1 LocalSizeHint 1 1 1 + %2 = OpTypeVoid + %3 = OpTypeBool + %4 = OpTypeInt 8 0 + %5 = OpTypeInt 8 1 + %6 = OpTypeInt 16 0 + %7 = OpTypeInt 16 1 + %8 = OpTypeInt 32 0 + %9 = OpTypeInt 32 1 +%10 = OpTypeInt 64 0 +%11 = OpTypeInt 64 1 +%12 = OpTypeFloat 16 +%13 = OpTypeFloat 32 +%14 = OpTypeFloat 64 +%15 = OpTypeVector %4 2 +)"; + spv_text_t text = {textStr, strlen(textStr)}; + spv_diagnostic diagnostic = nullptr; + spv_result_t error = + spvTextToBinary(context, text.str, text.length, &binary, &diagnostic); + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + ASSERT_EQ(SPV_SUCCESS, error); + } + + virtual void TearDown() { + spvBinaryDestroy(binary); + binary = nullptr; + } + + // Compiles the given assembly text, and saves it into 'binary'. + void CompileSuccessfully(std::string text) { + spvBinaryDestroy(binary); + binary = nullptr; + spv_diagnostic diagnostic = nullptr; + EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, text.c_str(), text.size(), + &binary, &diagnostic)); + } + + spv_context context; + spv_binary binary; +}; + +TEST_F(BinaryToText, Default) { + spv_text text = nullptr; + spv_diagnostic diagnostic = nullptr; + ASSERT_EQ( + SPV_SUCCESS, + spvBinaryToText(context, binary->code, binary->wordCount, + SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic)); + printf("%s", text->str); + spvTextDestroy(text); +} + +TEST_F(BinaryToText, MissingModule) { + spv_text text; + spv_diagnostic diagnostic = nullptr; + EXPECT_EQ( + SPV_ERROR_INVALID_BINARY, + spvBinaryToText(context, nullptr, 42, SPV_BINARY_TO_TEXT_OPTION_NONE, + &text, &diagnostic)); + EXPECT_THAT(diagnostic->error, Eq(std::string("Missing module."))); + if (diagnostic) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + } +} + +TEST_F(BinaryToText, TruncatedModule) { + // Make a valid module with zero instructions. + CompileSuccessfully(""); + EXPECT_EQ(SPV_INDEX_INSTRUCTION, binary->wordCount); + + for (size_t length = 0; length < SPV_INDEX_INSTRUCTION; length++) { + spv_text text = nullptr; + spv_diagnostic diagnostic = nullptr; + EXPECT_EQ( + SPV_ERROR_INVALID_BINARY, + spvBinaryToText(context, binary->code, length, + SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic)); + ASSERT_NE(nullptr, diagnostic); + std::stringstream expected; + expected << "Module has incomplete header: only " << length + << " words instead of " << SPV_INDEX_INSTRUCTION; + EXPECT_THAT(diagnostic->error, Eq(expected.str())); + spvDiagnosticDestroy(diagnostic); + } +} + +TEST_F(BinaryToText, InvalidMagicNumber) { + CompileSuccessfully(""); + std::vector damaged_binary(binary->code, + binary->code + binary->wordCount); + damaged_binary[SPV_INDEX_MAGIC_NUMBER] ^= 123; + + spv_diagnostic diagnostic = nullptr; + spv_text text; + EXPECT_EQ( + SPV_ERROR_INVALID_BINARY, + spvBinaryToText(context, damaged_binary.data(), damaged_binary.size(), + SPV_BINARY_TO_TEXT_OPTION_NONE, &text, &diagnostic)); + ASSERT_NE(nullptr, diagnostic); + std::stringstream expected; + expected << "Invalid SPIR-V magic number '" << std::hex + << damaged_binary[SPV_INDEX_MAGIC_NUMBER] << "'."; + EXPECT_THAT(diagnostic->error, Eq(expected.str())); + spvDiagnosticDestroy(diagnostic); +} + +struct FailedDecodeCase { + std::string source_text; + std::vector appended_instruction; + std::string expected_error_message; +}; + +using BinaryToTextFail = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(BinaryToTextFail, EncodeSuccessfullyDecodeFailed) { + EXPECT_THAT(EncodeSuccessfullyDecodeFailed(GetParam().source_text, + GetParam().appended_instruction), + Eq(GetParam().expected_error_message)); +} + +INSTANTIATE_TEST_SUITE_P( + InvalidIds, BinaryToTextFail, + ::testing::ValuesIn(std::vector{ + {"", spvtest::MakeInstruction(SpvOpTypeVoid, {0}), + "Error: Result Id is 0"}, + {"", spvtest::MakeInstruction(SpvOpConstant, {0, 1, 42}), + "Error: Type Id is 0"}, + {"%1 = OpTypeVoid", spvtest::MakeInstruction(SpvOpTypeVoid, {1}), + "Id 1 is defined more than once"}, + {"%1 = OpTypeVoid\n" + "%2 = OpNot %1 %foo", + spvtest::MakeInstruction(SpvOpNot, {1, 2, 3}), + "Id 2 is defined more than once"}, + {"%1 = OpTypeVoid\n" + "%2 = OpNot %1 %foo", + spvtest::MakeInstruction(SpvOpNot, {1, 1, 3}), + "Id 1 is defined more than once"}, + // The following are the two failure cases for + // Parser::setNumericTypeInfoForType. + {"", spvtest::MakeInstruction(SpvOpConstant, {500, 1, 42}), + "Type Id 500 is not a type"}, + {"%1 = OpTypeInt 32 0\n" + "%2 = OpTypeVector %1 4", + spvtest::MakeInstruction(SpvOpConstant, {2, 3, 999}), + "Type Id 2 is not a scalar numeric type"}, + })); + +INSTANTIATE_TEST_SUITE_P( + InvalidIdsCheckedDuringLiteralCaseParsing, BinaryToTextFail, + ::testing::ValuesIn(std::vector{ + {"", spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}), + "Invalid OpSwitch: selector id 1 has no type"}, + {"%1 = OpTypeVoid\n", + spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}), + "Invalid OpSwitch: selector id 1 is a type, not a value"}, + {"%1 = OpConstantTrue !500", + spvtest::MakeInstruction(SpvOpSwitch, {1, 2, 3, 4}), + "Type Id 500 is not a type"}, + {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 1.5", + spvtest::MakeInstruction(SpvOpSwitch, {2, 3, 4, 5}), + "Invalid OpSwitch: selector id 2 is not a scalar integer"}, + })); + +TEST_F(TextToBinaryTest, OneInstruction) { + const std::string input = "OpSource OpenCL_C 12\n"; + EXPECT_EQ(input, EncodeAndDecodeSuccessfully(input)); +} + +// Exercise the case where an operand itself has operands. +// This could detect problems in updating the expected-set-of-operands +// list. +TEST_F(TextToBinaryTest, OperandWithOperands) { + const std::string input = R"(OpEntryPoint Kernel %1 "foo" +OpExecutionMode %1 LocalSizeHint 100 200 300 +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%1 = OpFunction %1 None %3 +)"; + EXPECT_EQ(input, EncodeAndDecodeSuccessfully(input)); +} + +using RoundTripInstructionsTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>>; + +TEST_P(RoundTripInstructionsTest, Sample) { + EXPECT_THAT(EncodeAndDecodeSuccessfully(std::get<1>(GetParam()), + SPV_BINARY_TO_TEXT_OPTION_NONE, + std::get<0>(GetParam())), + Eq(std::get<1>(GetParam()))); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P( + NumericLiterals, RoundTripInstructionsTest, + // This test is independent of environment, so just test the one. + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3), + ::testing::ValuesIn(std::vector{ + "%1 = OpTypeInt 12 0\n%2 = OpConstant %1 1867\n", + "%1 = OpTypeInt 12 1\n%2 = OpConstant %1 1867\n", + "%1 = OpTypeInt 12 1\n%2 = OpConstant %1 -1867\n", + "%1 = OpTypeInt 32 0\n%2 = OpConstant %1 1867\n", + "%1 = OpTypeInt 32 1\n%2 = OpConstant %1 1867\n", + "%1 = OpTypeInt 32 1\n%2 = OpConstant %1 -1867\n", + "%1 = OpTypeInt 64 0\n%2 = OpConstant %1 18446744073709551615\n", + "%1 = OpTypeInt 64 1\n%2 = OpConstant %1 9223372036854775807\n", + "%1 = OpTypeInt 64 1\n%2 = OpConstant %1 -9223372036854775808\n", + // 16-bit floats print as hex floats. + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ff4p+16\n", + "%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.d2cp-10\n", + // 32-bit floats + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -3.125\n", + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128\n", // NaN + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128\n", // NaN + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128\n", // Inf + "%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128\n", // -Inf + // 64-bit floats + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -3.125\n", + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.ffffffffffffap-1023\n", // small normal + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.ffffffffffffap-1023\n", + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024\n", // NaN + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0002p+1024\n", // NaN + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024\n", // Inf + "%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024\n", // -Inf + }))); +// clang-format on + +INSTANTIATE_TEST_SUITE_P( + MemoryAccessMasks, RoundTripInstructionsTest, + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3), + ::testing::ValuesIn(std::vector{ + "OpStore %1 %2\n", // 3 words long. + "OpStore %1 %2 None\n", // 4 words long, explicit final 0. + "OpStore %1 %2 Volatile\n", + "OpStore %1 %2 Aligned 8\n", + "OpStore %1 %2 Nontemporal\n", + // Combinations show the names from LSB to MSB + "OpStore %1 %2 Volatile|Aligned 16\n", + "OpStore %1 %2 Volatile|Nontemporal\n", + "OpStore %1 %2 Volatile|Aligned|Nontemporal 32\n", + }))); + +INSTANTIATE_TEST_SUITE_P( + FPFastMathModeMasks, RoundTripInstructionsTest, + Combine( + ::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3), + ::testing::ValuesIn(std::vector{ + "OpDecorate %1 FPFastMathMode None\n", + "OpDecorate %1 FPFastMathMode NotNaN\n", + "OpDecorate %1 FPFastMathMode NotInf\n", + "OpDecorate %1 FPFastMathMode NSZ\n", + "OpDecorate %1 FPFastMathMode AllowRecip\n", + "OpDecorate %1 FPFastMathMode Fast\n", + // Combinations show the names from LSB to MSB + "OpDecorate %1 FPFastMathMode NotNaN|NotInf\n", + "OpDecorate %1 FPFastMathMode NSZ|AllowRecip\n", + "OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ|AllowRecip|Fast\n", + }))); + +INSTANTIATE_TEST_SUITE_P( + LoopControlMasks, RoundTripInstructionsTest, + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2), + ::testing::ValuesIn(std::vector{ + "OpLoopMerge %1 %2 None\n", + "OpLoopMerge %1 %2 Unroll\n", + "OpLoopMerge %1 %2 DontUnroll\n", + "OpLoopMerge %1 %2 Unroll|DontUnroll\n", + }))); + +INSTANTIATE_TEST_SUITE_P(LoopControlMasksV11, RoundTripInstructionsTest, + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, + SPV_ENV_UNIVERSAL_1_3), + ::testing::ValuesIn(std::vector{ + "OpLoopMerge %1 %2 DependencyInfinite\n", + "OpLoopMerge %1 %2 DependencyLength 8\n", + }))); + +INSTANTIATE_TEST_SUITE_P( + SelectionControlMasks, RoundTripInstructionsTest, + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_2), + ::testing::ValuesIn(std::vector{ + "OpSelectionMerge %1 None\n", + "OpSelectionMerge %1 Flatten\n", + "OpSelectionMerge %1 DontFlatten\n", + "OpSelectionMerge %1 Flatten|DontFlatten\n", + }))); + +INSTANTIATE_TEST_SUITE_P( + FunctionControlMasks, RoundTripInstructionsTest, + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3), + ::testing::ValuesIn(std::vector{ + "%2 = OpFunction %1 None %3\n", + "%2 = OpFunction %1 Inline %3\n", + "%2 = OpFunction %1 DontInline %3\n", + "%2 = OpFunction %1 Pure %3\n", + "%2 = OpFunction %1 Const %3\n", + "%2 = OpFunction %1 Inline|Pure|Const %3\n", + "%2 = OpFunction %1 DontInline|Const %3\n", + }))); + +INSTANTIATE_TEST_SUITE_P( + ImageMasks, RoundTripInstructionsTest, + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3), + ::testing::ValuesIn(std::vector{ + "%2 = OpImageFetch %1 %3 %4\n", + "%2 = OpImageFetch %1 %3 %4 None\n", + "%2 = OpImageFetch %1 %3 %4 Bias %5\n", + "%2 = OpImageFetch %1 %3 %4 Lod %5\n", + "%2 = OpImageFetch %1 %3 %4 Grad %5 %6\n", + "%2 = OpImageFetch %1 %3 %4 ConstOffset %5\n", + "%2 = OpImageFetch %1 %3 %4 Offset %5\n", + "%2 = OpImageFetch %1 %3 %4 ConstOffsets %5\n", + "%2 = OpImageFetch %1 %3 %4 Sample %5\n", + "%2 = OpImageFetch %1 %3 %4 MinLod %5\n", + "%2 = OpImageFetch %1 %3 %4 Bias|Lod|Grad %5 %6 %7 %8\n", + "%2 = OpImageFetch %1 %3 %4 ConstOffset|Offset|ConstOffsets" + " %5 %6 %7\n", + "%2 = OpImageFetch %1 %3 %4 Sample|MinLod %5 %6\n", + "%2 = OpImageFetch %1 %3 %4" + " Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod" + " %5 %6 %7 %8 %9 %10 %11 %12 %13\n"}))); + +INSTANTIATE_TEST_SUITE_P( + NewInstructionsInSPIRV1_2, RoundTripInstructionsTest, + Combine(::testing::Values(SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3), + ::testing::ValuesIn(std::vector{ + "OpExecutionModeId %1 SubgroupsPerWorkgroupId %2\n", + "OpExecutionModeId %1 LocalSizeId %2 %3 %4\n", + "OpExecutionModeId %1 LocalSizeHintId %2\n", + "OpDecorateId %1 AlignmentId %2\n", + "OpDecorateId %1 MaxByteOffsetId %2\n", + }))); + +using MaskSorting = TextToBinaryTest; + +TEST_F(MaskSorting, MasksAreSortedFromLSBToMSB) { + EXPECT_THAT(EncodeAndDecodeSuccessfully( + "OpStore %1 %2 Nontemporal|Aligned|Volatile 32"), + Eq("OpStore %1 %2 Volatile|Aligned|Nontemporal 32\n")); + EXPECT_THAT( + EncodeAndDecodeSuccessfully( + "OpDecorate %1 FPFastMathMode NotInf|Fast|AllowRecip|NotNaN|NSZ"), + Eq("OpDecorate %1 FPFastMathMode NotNaN|NotInf|NSZ|AllowRecip|Fast\n")); + EXPECT_THAT( + EncodeAndDecodeSuccessfully("OpLoopMerge %1 %2 DontUnroll|Unroll"), + Eq("OpLoopMerge %1 %2 Unroll|DontUnroll\n")); + EXPECT_THAT( + EncodeAndDecodeSuccessfully("OpSelectionMerge %1 DontFlatten|Flatten"), + Eq("OpSelectionMerge %1 Flatten|DontFlatten\n")); + EXPECT_THAT(EncodeAndDecodeSuccessfully( + "%2 = OpFunction %1 DontInline|Const|Pure|Inline %3"), + Eq("%2 = OpFunction %1 Inline|DontInline|Pure|Const %3\n")); + EXPECT_THAT(EncodeAndDecodeSuccessfully( + "%2 = OpImageFetch %1 %3 %4" + " MinLod|Sample|Offset|Lod|Grad|ConstOffsets|ConstOffset|Bias" + " %5 %6 %7 %8 %9 %10 %11 %12 %13\n"), + Eq("%2 = OpImageFetch %1 %3 %4" + " Bias|Lod|Grad|ConstOffset|Offset|ConstOffsets|Sample|MinLod" + " %5 %6 %7 %8 %9 %10 %11 %12 %13\n")); +} + +using OperandTypeTest = TextToBinaryTest; + +TEST_F(OperandTypeTest, OptionalTypedLiteralNumber) { + const std::string input = + "%1 = OpTypeInt 32 0\n" + "%2 = OpConstant %1 42\n" + "OpSwitch %2 %3 100 %4\n"; + EXPECT_EQ(input, EncodeAndDecodeSuccessfully(input)); +} + +using IndentTest = spvtest::TextToBinaryTest; + +TEST_F(IndentTest, Sample) { + const std::string input = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 %3 %4 %5 %6 %7 %8 %9 %10 ; force IDs into double digits +%11 = OpConstant %1 42 +OpStore %2 %3 Aligned|Volatile 4 ; bogus, but not indented +)"; + const std::string expected = + R"( OpCapability Shader + OpMemoryModel Logical GLSL450 + %1 = OpTypeInt 32 0 + %2 = OpTypeStruct %1 %3 %4 %5 %6 %7 %8 %9 %10 + %11 = OpConstant %1 42 + OpStore %2 %3 Volatile|Aligned 4 +)"; + EXPECT_THAT( + EncodeAndDecodeSuccessfully(input, SPV_BINARY_TO_TEXT_OPTION_INDENT), + expected); +} + +using FriendlyNameDisassemblyTest = spvtest::TextToBinaryTest; + +TEST_F(FriendlyNameDisassemblyTest, Sample) { + const std::string input = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeStruct %1 %3 %4 %5 %6 %7 %8 %9 %10 ; force IDs into double digits +%11 = OpConstant %1 42 +)"; + const std::string expected = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +%uint = OpTypeInt 32 0 +%_struct_2 = OpTypeStruct %uint %3 %4 %5 %6 %7 %8 %9 %10 +%uint_42 = OpConstant %uint 42 +)"; + EXPECT_THAT(EncodeAndDecodeSuccessfully( + input, SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES), + expected); +} + +TEST_F(TextToBinaryTest, ShowByteOffsetsWhenRequested) { + const std::string input = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +%1 = OpTypeInt 32 0 +%2 = OpTypeVoid +)"; + const std::string expected = + R"(OpCapability Shader ; 0x00000014 +OpMemoryModel Logical GLSL450 ; 0x0000001c +%1 = OpTypeInt 32 0 ; 0x00000028 +%2 = OpTypeVoid ; 0x00000038 +)"; + EXPECT_THAT(EncodeAndDecodeSuccessfully( + input, SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET), + expected); +} + +// Test version string. +TEST_F(TextToBinaryTest, VersionString) { + auto words = CompileSuccessfully(""); + spv_text decoded_text = nullptr; + EXPECT_THAT(spvBinaryToText(ScopedContext().context, words.data(), + words.size(), SPV_BINARY_TO_TEXT_OPTION_NONE, + &decoded_text, &diagnostic), + Eq(SPV_SUCCESS)); + EXPECT_EQ(nullptr, diagnostic); + + EXPECT_THAT(decoded_text->str, HasSubstr("Version: 1.0\n")) + << EncodeAndDecodeSuccessfully(""); + spvTextDestroy(decoded_text); +} + +// Test generator string. + +// A test case for the generator string. This allows us to +// test both of the 16-bit components of the generator word. +struct GeneratorStringCase { + uint16_t generator; + uint16_t misc; + std::string expected; +}; + +using GeneratorStringTest = spvtest::TextToBinaryTestBase< + ::testing::TestWithParam>; + +TEST_P(GeneratorStringTest, Sample) { + auto words = CompileSuccessfully(""); + EXPECT_EQ(2u, SPV_INDEX_GENERATOR_NUMBER); + words[SPV_INDEX_GENERATOR_NUMBER] = + SPV_GENERATOR_WORD(GetParam().generator, GetParam().misc); + + spv_text decoded_text = nullptr; + EXPECT_THAT(spvBinaryToText(ScopedContext().context, words.data(), + words.size(), SPV_BINARY_TO_TEXT_OPTION_NONE, + &decoded_text, &diagnostic), + Eq(SPV_SUCCESS)); + EXPECT_THAT(diagnostic, Eq(nullptr)); + EXPECT_THAT(std::string(decoded_text->str), HasSubstr(GetParam().expected)); + spvTextDestroy(decoded_text); +} + +INSTANTIATE_TEST_SUITE_P(GeneratorStrings, GeneratorStringTest, + ::testing::ValuesIn(std::vector{ + {SPV_GENERATOR_KHRONOS, 12, "Khronos; 12"}, + {SPV_GENERATOR_LUNARG, 99, "LunarG; 99"}, + {SPV_GENERATOR_VALVE, 1, "Valve; 1"}, + {SPV_GENERATOR_CODEPLAY, 65535, "Codeplay; 65535"}, + {SPV_GENERATOR_NVIDIA, 19, "NVIDIA; 19"}, + {SPV_GENERATOR_ARM, 1000, "ARM; 1000"}, + {SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR, 38, + "Khronos LLVM/SPIR-V Translator; 38"}, + {SPV_GENERATOR_KHRONOS_ASSEMBLER, 2, + "Khronos SPIR-V Tools Assembler; 2"}, + {SPV_GENERATOR_KHRONOS_GLSLANG, 1, + "Khronos Glslang Reference Front End; 1"}, + {1000, 18, "Unknown(1000); 18"}, + {65535, 32767, "Unknown(65535); 32767"}, + })); + +// TODO(dneto): Test new instructions and enums in SPIR-V 1.3 + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/c_interface_test.cpp b/third_party/spirv-tools/test/c_interface_test.cpp new file mode 100644 index 0000000..841bb2c --- /dev/null +++ b/third_party/spirv-tools/test/c_interface_test.cpp @@ -0,0 +1,299 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gtest/gtest.h" +#include "source/table.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace { + +// TODO(antiagainst): Use public C API for setting the consumer once exists. +#ifndef SPIRV_TOOLS_SHAREDLIB +void SetContextMessageConsumer(spv_context context, MessageConsumer consumer) { + spvtools::SetContextMessageConsumer(context, consumer); +} +#else +void SetContextMessageConsumer(spv_context, MessageConsumer) {} +#endif + +// The default consumer is a null std::function. +TEST(CInterface, DefaultConsumerNullDiagnosticForValidInput) { + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + const char input_text[] = + "OpCapability Shader\n" + "OpCapability Linkage\n" + "OpMemoryModel Logical GLSL450"; + + spv_binary binary = nullptr; + EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, + sizeof(input_text), &binary, nullptr)); + + { + // Sadly the compiler don't allow me to feed binary directly to + // spvValidate(). + spv_const_binary_t b{binary->code, binary->wordCount}; + EXPECT_EQ(SPV_SUCCESS, spvValidate(context, &b, nullptr)); + } + + spv_text text = nullptr; + EXPECT_EQ(SPV_SUCCESS, spvBinaryToText(context, binary->code, + binary->wordCount, 0, &text, nullptr)); + + spvTextDestroy(text); + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +// The default consumer is a null std::function. +TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidAssembling) { + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + const char input_text[] = "%1 = OpName"; + + spv_binary binary = nullptr; + EXPECT_EQ(SPV_ERROR_INVALID_TEXT, + spvTextToBinary(context, input_text, sizeof(input_text), &binary, + nullptr)); + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +// The default consumer is a null std::function. +TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidDiassembling) { + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + const char input_text[] = "OpNop"; + + spv_binary binary = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, + sizeof(input_text), &binary, nullptr)); + // Change OpNop to an invalid (wordcount|opcode) word. + binary->code[binary->wordCount - 1] = 0xffffffff; + + spv_text text = nullptr; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, + nullptr)); + + spvTextDestroy(text); + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +// The default consumer is a null std::function. +TEST(CInterface, DefaultConsumerNullDiagnosticForInvalidValidating) { + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + const char input_text[] = "OpNop"; + + spv_binary binary = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, + sizeof(input_text), &binary, nullptr)); + + spv_const_binary_t b{binary->code, binary->wordCount}; + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr)); + + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +TEST(CInterface, SpecifyConsumerNullDiagnosticForAssembling) { + const char input_text[] = " OpName\n"; + + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + SetContextMessageConsumer( + context, + [&invocation](spv_message_level_t level, const char* source, + const spv_position_t& position, const char* message) { + ++invocation; + EXPECT_EQ(SPV_MSG_ERROR, level); + // The error happens at scanning the begining of second line. + EXPECT_STREQ("input", source); + EXPECT_EQ(1u, position.line); + EXPECT_EQ(0u, position.column); + EXPECT_EQ(12u, position.index); + EXPECT_STREQ("Expected operand, found end of stream.", message); + }); + + spv_binary binary = nullptr; + EXPECT_EQ(SPV_ERROR_INVALID_TEXT, + spvTextToBinary(context, input_text, sizeof(input_text), &binary, + nullptr)); +#ifndef SPIRV_TOOLS_SHAREDLIB + EXPECT_EQ(1, invocation); +#endif + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +TEST(CInterface, SpecifyConsumerNullDiagnosticForDisassembling) { + const char input_text[] = "OpNop"; + + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + SetContextMessageConsumer( + context, + [&invocation](spv_message_level_t level, const char* source, + const spv_position_t& position, const char* message) { + ++invocation; + EXPECT_EQ(SPV_MSG_ERROR, level); + EXPECT_STREQ("input", source); + EXPECT_EQ(0u, position.line); + EXPECT_EQ(0u, position.column); + EXPECT_EQ(1u, position.index); + EXPECT_STREQ("Invalid opcode: 65535", message); + }); + + spv_binary binary = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, + sizeof(input_text), &binary, nullptr)); + // Change OpNop to an invalid (wordcount|opcode) word. + binary->code[binary->wordCount - 1] = 0xffffffff; + + spv_text text = nullptr; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, + nullptr)); +#ifndef SPIRV_TOOLS_SHAREDLIB + EXPECT_EQ(1, invocation); +#endif + + spvTextDestroy(text); + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +TEST(CInterface, SpecifyConsumerNullDiagnosticForValidating) { + const char input_text[] = "OpNop"; + + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + SetContextMessageConsumer( + context, + [&invocation](spv_message_level_t level, const char* source, + const spv_position_t& position, const char* message) { + ++invocation; + EXPECT_EQ(SPV_MSG_ERROR, level); + EXPECT_STREQ("input", source); + EXPECT_EQ(0u, position.line); + EXPECT_EQ(0u, position.column); + // TODO(antiagainst): what validation reports is not a word offset here. + // It is inconsistent with diassembler. Should be fixed. + EXPECT_EQ(1u, position.index); + EXPECT_STREQ( + "Nop cannot appear before the memory model instruction\n" + " OpNop\n", + message); + }); + + spv_binary binary = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, + sizeof(input_text), &binary, nullptr)); + + spv_const_binary_t b{binary->code, binary->wordCount}; + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, nullptr)); +#ifndef SPIRV_TOOLS_SHAREDLIB + EXPECT_EQ(1, invocation); +#endif + + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +// When having both a consumer and an diagnostic object, the diagnostic object +// should take priority. +TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForAssembling) { + const char input_text[] = " OpName"; + + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + SetContextMessageConsumer( + context, + [&invocation](spv_message_level_t, const char*, const spv_position_t&, + const char*) { ++invocation; }); + + spv_binary binary = nullptr; + spv_diagnostic diagnostic = nullptr; + EXPECT_EQ(SPV_ERROR_INVALID_TEXT, + spvTextToBinary(context, input_text, sizeof(input_text), &binary, + &diagnostic)); + EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. + EXPECT_STREQ("Expected operand, found end of stream.", diagnostic->error); + + spvDiagnosticDestroy(diagnostic); + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForDisassembling) { + const char input_text[] = "OpNop"; + + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + SetContextMessageConsumer( + context, + [&invocation](spv_message_level_t, const char*, const spv_position_t&, + const char*) { ++invocation; }); + + spv_binary binary = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, + sizeof(input_text), &binary, nullptr)); + // Change OpNop to an invalid (wordcount|opcode) word. + binary->code[binary->wordCount - 1] = 0xffffffff; + + spv_diagnostic diagnostic = nullptr; + spv_text text = nullptr; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + spvBinaryToText(context, binary->code, binary->wordCount, 0, &text, + &diagnostic)); + + EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. + EXPECT_STREQ("Invalid opcode: 65535", diagnostic->error); + + spvTextDestroy(text); + spvDiagnosticDestroy(diagnostic); + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +TEST(CInterface, SpecifyConsumerSpecifyDiagnosticForValidating) { + const char input_text[] = "OpNop"; + + auto context = spvContextCreate(SPV_ENV_UNIVERSAL_1_1); + int invocation = 0; + SetContextMessageConsumer( + context, + [&invocation](spv_message_level_t, const char*, const spv_position_t&, + const char*) { ++invocation; }); + + spv_binary binary = nullptr; + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(context, input_text, + sizeof(input_text), &binary, nullptr)); + + spv_diagnostic diagnostic = nullptr; + spv_const_binary_t b{binary->code, binary->wordCount}; + EXPECT_EQ(SPV_ERROR_INVALID_LAYOUT, spvValidate(context, &b, &diagnostic)); + + EXPECT_EQ(0, invocation); // Consumer should not be invoked at all. + EXPECT_STREQ( + "Nop cannot appear before the memory model instruction\n" + " OpNop\n", + diagnostic->error); + + spvDiagnosticDestroy(diagnostic); + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/comment_test.cpp b/third_party/spirv-tools/test/comment_test.cpp new file mode 100644 index 0000000..49f8df6 --- /dev/null +++ b/third_party/spirv-tools/test/comment_test.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using utils::MakeVector; +using spvtest::TextToBinaryTest; +using testing::Eq; + +TEST_F(TextToBinaryTest, Whitespace) { + std::string input = R"( +; I'm a proud comment at the beginning of the file +; I hide: OpCapability Shader + OpMemoryModel Logical Simple ; comment after instruction +;;;;;;;; many ;'s + %glsl450 = OpExtInstImport "GLSL.std.450" + ; comment indented +)"; + + EXPECT_THAT( + CompiledInstructions(input), + Eq(Concatenate({MakeInstruction(SpvOpMemoryModel, + {uint32_t(SpvAddressingModelLogical), + uint32_t(SpvMemoryModelSimple)}), + MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("GLSL.std.450"))}))); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/cpp_interface_test.cpp b/third_party/spirv-tools/test/cpp_interface_test.cpp new file mode 100644 index 0000000..538d40f --- /dev/null +++ b/third_party/spirv-tools/test/cpp_interface_test.cpp @@ -0,0 +1,328 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "spirv-tools/optimizer.hpp" +#include "spirv/1.1/spirv.h" + +namespace spvtools { +namespace { + +using ::testing::ContainerEq; +using ::testing::HasSubstr; + +// Return a string that contains the minimum instructions needed to form +// a valid module. Other instructions can be appended to this string. +std::string Header() { + return R"(OpCapability Shader +OpCapability Linkage +OpMemoryModel Logical GLSL450 +)"; +} + +// When we assemble with a target environment of SPIR-V 1.1, we expect +// the following in the module header version word. +const uint32_t kExpectedSpvVersion = 0x10100; + +TEST(CppInterface, SuccessfulRoundTrip) { + const std::string input_text = "%2 = OpSizeOf %1 %3\n"; + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + + std::vector binary; + EXPECT_TRUE(t.Assemble(input_text, &binary)); + EXPECT_TRUE(binary.size() > 5u); + EXPECT_EQ(SpvMagicNumber, binary[0]); + EXPECT_EQ(kExpectedSpvVersion, binary[1]); + + // This cannot pass validation since %1 is not defined. + t.SetMessageConsumer([](spv_message_level_t level, const char* source, + const spv_position_t& position, const char* message) { + EXPECT_EQ(SPV_MSG_ERROR, level); + EXPECT_STREQ("input", source); + EXPECT_EQ(0u, position.line); + EXPECT_EQ(0u, position.column); + EXPECT_EQ(1u, position.index); + EXPECT_STREQ("ID 1[%1] has not been defined\n %2 = OpSizeOf %1 %3\n", + message); + }); + EXPECT_FALSE(t.Validate(binary)); + + std::string output_text; + EXPECT_TRUE(t.Disassemble(binary, &output_text)); + EXPECT_EQ(input_text, output_text); +} + +TEST(CppInterface, AssembleEmptyModule) { + std::vector binary(10, 42); + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + EXPECT_TRUE(t.Assemble("", &binary)); + // We only have the header. + EXPECT_EQ(5u, binary.size()); + EXPECT_EQ(SpvMagicNumber, binary[0]); + EXPECT_EQ(kExpectedSpvVersion, binary[1]); +} + +TEST(CppInterface, AssembleOverloads) { + const std::string input_text = "%2 = OpSizeOf %1 %3\n"; + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + { + std::vector binary; + EXPECT_TRUE(t.Assemble(input_text, &binary)); + EXPECT_TRUE(binary.size() > 5u); + EXPECT_EQ(SpvMagicNumber, binary[0]); + EXPECT_EQ(kExpectedSpvVersion, binary[1]); + } + { + std::vector binary; + EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size(), &binary)); + EXPECT_TRUE(binary.size() > 5u); + EXPECT_EQ(SpvMagicNumber, binary[0]); + EXPECT_EQ(kExpectedSpvVersion, binary[1]); + } + { // Ignore the last newline. + std::vector binary; + EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size() - 1, &binary)); + EXPECT_TRUE(binary.size() > 5u); + EXPECT_EQ(SpvMagicNumber, binary[0]); + EXPECT_EQ(kExpectedSpvVersion, binary[1]); + } +} + +TEST(CppInterface, DisassembleEmptyModule) { + std::string text(10, 'x'); + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + int invocation_count = 0; + t.SetMessageConsumer( + [&invocation_count](spv_message_level_t level, const char* source, + const spv_position_t& position, const char* message) { + ++invocation_count; + EXPECT_EQ(SPV_MSG_ERROR, level); + EXPECT_STREQ("input", source); + EXPECT_EQ(0u, position.line); + EXPECT_EQ(0u, position.column); + EXPECT_EQ(0u, position.index); + EXPECT_STREQ("Missing module.", message); + }); + EXPECT_FALSE(t.Disassemble({}, &text)); + EXPECT_EQ("xxxxxxxxxx", text); // The original string is unmodified. + EXPECT_EQ(1, invocation_count); +} + +TEST(CppInterface, DisassembleOverloads) { + const std::string input_text = "%2 = OpSizeOf %1 %3\n"; + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + + std::vector binary; + EXPECT_TRUE(t.Assemble(input_text, &binary)); + + { + std::string output_text; + EXPECT_TRUE(t.Disassemble(binary, &output_text)); + EXPECT_EQ(input_text, output_text); + } + { + std::string output_text; + EXPECT_TRUE(t.Disassemble(binary.data(), binary.size(), &output_text)); + EXPECT_EQ(input_text, output_text); + } +} + +TEST(CppInterface, SuccessfulValidation) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + int invocation_count = 0; + t.SetMessageConsumer([&invocation_count](spv_message_level_t, const char*, + const spv_position_t&, const char*) { + ++invocation_count; + }); + + std::vector binary; + EXPECT_TRUE(t.Assemble(Header(), &binary)); + EXPECT_TRUE(t.Validate(binary)); + EXPECT_EQ(0, invocation_count); +} + +TEST(CppInterface, ValidateOverloads) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::vector binary; + EXPECT_TRUE(t.Assemble(Header(), &binary)); + + { EXPECT_TRUE(t.Validate(binary)); } + { EXPECT_TRUE(t.Validate(binary.data(), binary.size())); } +} + +TEST(CppInterface, ValidateEmptyModule) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + int invocation_count = 0; + t.SetMessageConsumer( + [&invocation_count](spv_message_level_t level, const char* source, + const spv_position_t& position, const char* message) { + ++invocation_count; + EXPECT_EQ(SPV_MSG_ERROR, level); + EXPECT_STREQ("input", source); + EXPECT_EQ(0u, position.line); + EXPECT_EQ(0u, position.column); + EXPECT_EQ(0u, position.index); + EXPECT_STREQ("Invalid SPIR-V magic number.", message); + }); + EXPECT_FALSE(t.Validate({})); + EXPECT_EQ(1, invocation_count); +} + +// Returns the assembly for a SPIR-V module with a struct declaration +// with the given number of members. +std::string MakeModuleHavingStruct(int num_members) { + std::stringstream os; + os << Header(); + os << R"(%1 = OpTypeInt 32 0 + %2 = OpTypeStruct)"; + for (int i = 0; i < num_members; i++) os << " %1"; + return os.str(); +} + +TEST(CppInterface, ValidateWithOptionsPass) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::vector binary; + EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary)); + const ValidatorOptions opts; + + EXPECT_TRUE(t.Validate(binary.data(), binary.size(), opts)); +} + +TEST(CppInterface, ValidateWithOptionsFail) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::vector binary; + EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary)); + ValidatorOptions opts; + opts.SetUniversalLimit(spv_validator_limit_max_struct_members, 9); + std::stringstream os; + t.SetMessageConsumer([&os](spv_message_level_t, const char*, + const spv_position_t&, + const char* message) { os << message; }); + + EXPECT_FALSE(t.Validate(binary.data(), binary.size(), opts)); + EXPECT_THAT( + os.str(), + HasSubstr( + "Number of OpTypeStruct members (10) has exceeded the limit (9)")); +} + +// Checks that after running the given optimizer |opt| on the given |original| +// source code, we can get the given |optimized| source code. +void CheckOptimization(const std::string& original, + const std::string& optimized, const Optimizer& opt) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::vector original_binary; + ASSERT_TRUE(t.Assemble(original, &original_binary)); + + std::vector optimized_binary; + EXPECT_TRUE(opt.Run(original_binary.data(), original_binary.size(), + &optimized_binary)); + + std::string optimized_text; + EXPECT_TRUE(t.Disassemble(optimized_binary, &optimized_text)); + EXPECT_EQ(optimized, optimized_text); +} + +TEST(CppInterface, OptimizeEmptyModule) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::vector binary; + EXPECT_TRUE(t.Assemble("", &binary)); + + Optimizer o(SPV_ENV_UNIVERSAL_1_1); + o.RegisterPass(CreateStripDebugInfoPass()); + + // Fails to validate. + EXPECT_FALSE(o.Run(binary.data(), binary.size(), &binary)); +} + +TEST(CppInterface, OptimizeModifiedModule) { + Optimizer o(SPV_ENV_UNIVERSAL_1_1); + o.RegisterPass(CreateStripDebugInfoPass()); + CheckOptimization(Header() + "OpSource GLSL 450", Header(), o); +} + +TEST(CppInterface, OptimizeMulitplePasses) { + std::string original_text = Header() + + "OpSource GLSL 450 " + "OpDecorate %true SpecId 1 " + "%bool = OpTypeBool " + "%true = OpSpecConstantTrue %bool"; + + Optimizer o(SPV_ENV_UNIVERSAL_1_1); + o.RegisterPass(CreateStripDebugInfoPass()) + .RegisterPass(CreateFreezeSpecConstantValuePass()); + + std::string expected_text = Header() + + "%bool = OpTypeBool\n" + "%true = OpConstantTrue %bool\n"; + + CheckOptimization(original_text, expected_text, o); +} + +TEST(CppInterface, OptimizeDoNothingWithPassToken) { + CreateFreezeSpecConstantValuePass(); + auto token = CreateUnifyConstantPass(); +} + +TEST(CppInterface, OptimizeReassignPassToken) { + auto token = CreateNullPass(); + token = CreateStripDebugInfoPass(); + + CheckOptimization( + Header() + "OpSource GLSL 450", Header(), + Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token))); +} + +TEST(CppInterface, OptimizeMoveConstructPassToken) { + auto token1 = CreateStripDebugInfoPass(); + Optimizer::PassToken token2(std::move(token1)); + + CheckOptimization( + Header() + "OpSource GLSL 450", Header(), + Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2))); +} + +TEST(CppInterface, OptimizeMoveAssignPassToken) { + auto token1 = CreateStripDebugInfoPass(); + auto token2 = CreateNullPass(); + token2 = std::move(token1); + + CheckOptimization( + Header() + "OpSource GLSL 450", Header(), + Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2))); +} + +TEST(CppInterface, OptimizeSameAddressForOriginalOptimizedBinary) { + SpirvTools t(SPV_ENV_UNIVERSAL_1_1); + std::vector binary; + ASSERT_TRUE(t.Assemble(Header() + "OpSource GLSL 450", &binary)); + + EXPECT_TRUE(Optimizer(SPV_ENV_UNIVERSAL_1_1) + .RegisterPass(CreateStripDebugInfoPass()) + .Run(binary.data(), binary.size(), &binary)); + + std::string optimized_text; + EXPECT_TRUE(t.Disassemble(binary, &optimized_text)); + EXPECT_EQ(Header(), optimized_text); +} + +// TODO(antiagainst): tests for SetMessageConsumer(). + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/diagnostic_test.cpp b/third_party/spirv-tools/test/diagnostic_test.cpp new file mode 100644 index 0000000..f86bae1 --- /dev/null +++ b/third_party/spirv-tools/test/diagnostic_test.cpp @@ -0,0 +1,150 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::testing::Eq; + +// Returns a newly created diagnostic value. +spv_diagnostic MakeValidDiagnostic() { + spv_position_t position = {}; + spv_diagnostic diagnostic = spvDiagnosticCreate(&position, ""); + EXPECT_NE(nullptr, diagnostic); + return diagnostic; +} + +TEST(Diagnostic, DestroyNull) { spvDiagnosticDestroy(nullptr); } + +TEST(Diagnostic, DestroyValidDiagnostic) { + spv_diagnostic diagnostic = MakeValidDiagnostic(); + spvDiagnosticDestroy(diagnostic); + // We aren't allowed to use the diagnostic pointer anymore. + // So we can't test its behaviour. +} + +TEST(Diagnostic, DestroyValidDiagnosticAfterReassignment) { + spv_diagnostic diagnostic = MakeValidDiagnostic(); + spv_diagnostic second_diagnostic = MakeValidDiagnostic(); + EXPECT_TRUE(diagnostic != second_diagnostic); + spvDiagnosticDestroy(diagnostic); + diagnostic = second_diagnostic; + spvDiagnosticDestroy(diagnostic); +} + +TEST(Diagnostic, PrintDefault) { + char message[] = "Test Diagnostic!"; + spv_diagnostic_t diagnostic = {{2, 3, 5}, message}; + // TODO: Redirect stderr + ASSERT_EQ(SPV_SUCCESS, spvDiagnosticPrint(&diagnostic)); + // TODO: Validate the output of spvDiagnosticPrint() + // TODO: Remove the redirection of stderr +} + +TEST(Diagnostic, PrintInvalidDiagnostic) { + ASSERT_EQ(SPV_ERROR_INVALID_DIAGNOSTIC, spvDiagnosticPrint(nullptr)); +} + +// TODO(dneto): We should be able to redirect the diagnostic printing. +// Once we do that, we can test diagnostic corner cases. + +TEST(DiagnosticStream, ConversionToResultType) { + // Check after the DiagnosticStream object is destroyed. + spv_result_t value; + { value = DiagnosticStream({}, nullptr, "", SPV_ERROR_INVALID_TEXT); } + EXPECT_EQ(SPV_ERROR_INVALID_TEXT, value); + + // Check implicit conversion via plain assignment. + value = DiagnosticStream({}, nullptr, "", SPV_SUCCESS); + EXPECT_EQ(SPV_SUCCESS, value); + + // Check conversion via constructor. + EXPECT_EQ(SPV_FAILED_MATCH, + spv_result_t(DiagnosticStream({}, nullptr, "", SPV_FAILED_MATCH))); +} + +TEST( + DiagnosticStream, + MoveConstructorPreservesPreviousMessagesAndPreventsOutputFromExpiringValue) { + std::ostringstream messages; + int message_count = 0; + auto consumer = [&messages, &message_count](spv_message_level_t, const char*, + const spv_position_t&, + const char* msg) { + message_count++; + messages << msg; + }; + + // Enclose the DiagnosticStream variables in a scope to force destruction. + { + DiagnosticStream ds0({}, consumer, "", SPV_ERROR_INVALID_BINARY); + ds0 << "First"; + DiagnosticStream ds1(std::move(ds0)); + ds1 << "Second"; + } + EXPECT_THAT(message_count, Eq(1)); + EXPECT_THAT(messages.str(), Eq("FirstSecond")); +} + +TEST(DiagnosticStream, MoveConstructorCanBeDirectlyShiftedTo) { + std::ostringstream messages; + int message_count = 0; + auto consumer = [&messages, &message_count](spv_message_level_t, const char*, + const spv_position_t&, + const char* msg) { + message_count++; + messages << msg; + }; + + // Enclose the DiagnosticStream variables in a scope to force destruction. + { + DiagnosticStream ds0({}, consumer, "", SPV_ERROR_INVALID_BINARY); + ds0 << "First"; + std::move(ds0) << "Second"; + } + EXPECT_THAT(message_count, Eq(1)); + EXPECT_THAT(messages.str(), Eq("FirstSecond")); +} + +TEST(DiagnosticStream, DiagnosticFromLambdaReturnCanStillBeUsed) { + std::ostringstream messages; + int message_count = 0; + auto consumer = [&messages, &message_count](spv_message_level_t, const char*, + const spv_position_t&, + const char* msg) { + message_count++; + messages << msg; + }; + + { + auto emitter = [&consumer]() -> DiagnosticStream { + DiagnosticStream ds0({}, consumer, "", SPV_ERROR_INVALID_BINARY); + ds0 << "First"; + return ds0; + }; + emitter() << "Second"; + } + EXPECT_THAT(message_count, Eq(1)); + EXPECT_THAT(messages.str(), Eq("FirstSecond")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/enum_set_test.cpp b/third_party/spirv-tools/test/enum_set_test.cpp new file mode 100644 index 0000000..047d642 --- /dev/null +++ b/third_party/spirv-tools/test/enum_set_test.cpp @@ -0,0 +1,290 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/enum_set.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::ElementsIn; +using ::testing::Eq; +using ::testing::ValuesIn; + +TEST(EnumSet, IsEmpty1) { + EnumSet set; + EXPECT_TRUE(set.IsEmpty()); + set.Add(0); + EXPECT_FALSE(set.IsEmpty()); +} + +TEST(EnumSet, IsEmpty2) { + EnumSet set; + EXPECT_TRUE(set.IsEmpty()); + set.Add(150); + EXPECT_FALSE(set.IsEmpty()); +} + +TEST(EnumSet, IsEmpty3) { + EnumSet set(4); + EXPECT_FALSE(set.IsEmpty()); +} + +TEST(EnumSet, IsEmpty4) { + EnumSet set(300); + EXPECT_FALSE(set.IsEmpty()); +} + +TEST(EnumSetHasAnyOf, EmptySetEmptyQuery) { + const EnumSet set; + const EnumSet empty; + EXPECT_TRUE(set.HasAnyOf(empty)); + EXPECT_TRUE(EnumSet().HasAnyOf(EnumSet())); +} + +TEST(EnumSetHasAnyOf, MaskSetEmptyQuery) { + EnumSet set; + const EnumSet empty; + set.Add(5); + set.Add(8); + EXPECT_TRUE(set.HasAnyOf(empty)); +} + +TEST(EnumSetHasAnyOf, OverflowSetEmptyQuery) { + EnumSet set; + const EnumSet empty; + set.Add(200); + set.Add(300); + EXPECT_TRUE(set.HasAnyOf(empty)); +} + +TEST(EnumSetHasAnyOf, EmptyQuery) { + EnumSet set; + const EnumSet empty; + set.Add(5); + set.Add(8); + set.Add(200); + set.Add(300); + EXPECT_TRUE(set.HasAnyOf(empty)); +} + +TEST(EnumSetHasAnyOf, EmptyQueryAlwaysTrue) { + EnumSet set; + const EnumSet empty; + EXPECT_TRUE(set.HasAnyOf(empty)); + set.Add(5); + EXPECT_TRUE(set.HasAnyOf(empty)); + + EXPECT_TRUE(EnumSet(100).HasAnyOf(EnumSet())); +} + +TEST(EnumSetHasAnyOf, ReflexiveMask) { + EnumSet set(3); + set.Add(24); + set.Add(30); + EXPECT_TRUE(set.HasAnyOf(set)); +} + +TEST(EnumSetHasAnyOf, ReflexiveOverflow) { + EnumSet set(200); + set.Add(300); + set.Add(400); + EXPECT_TRUE(set.HasAnyOf(set)); +} + +TEST(EnumSetHasAnyOf, Reflexive) { + EnumSet set(3); + set.Add(24); + set.Add(300); + set.Add(400); + EXPECT_TRUE(set.HasAnyOf(set)); +} + +TEST(EnumSetHasAnyOf, EmptySetHasNone) { + EnumSet set; + EnumSet items; + for (uint32_t i = 0; i < 200; ++i) { + items.Add(i); + EXPECT_FALSE(set.HasAnyOf(items)); + EXPECT_FALSE(set.HasAnyOf(EnumSet(i))); + } +} + +TEST(EnumSetHasAnyOf, MaskSetMaskQuery) { + EnumSet set(0); + EnumSet items(1); + EXPECT_FALSE(set.HasAnyOf(items)); + set.Add(2); + items.Add(3); + EXPECT_FALSE(set.HasAnyOf(items)); + set.Add(3); + EXPECT_TRUE(set.HasAnyOf(items)); + set.Add(4); + EXPECT_TRUE(set.HasAnyOf(items)); +} + +TEST(EnumSetHasAnyOf, OverflowSetOverflowQuery) { + EnumSet set(100); + EnumSet items(200); + EXPECT_FALSE(set.HasAnyOf(items)); + set.Add(300); + items.Add(400); + EXPECT_FALSE(set.HasAnyOf(items)); + set.Add(200); + EXPECT_TRUE(set.HasAnyOf(items)); + set.Add(500); + EXPECT_TRUE(set.HasAnyOf(items)); +} + +TEST(EnumSetHasAnyOf, GeneralCase) { + EnumSet set(0); + EnumSet items(100); + EXPECT_FALSE(set.HasAnyOf(items)); + set.Add(300); + items.Add(4); + EXPECT_FALSE(set.HasAnyOf(items)); + set.Add(5); + items.Add(500); + EXPECT_FALSE(set.HasAnyOf(items)); + set.Add(500); + EXPECT_TRUE(set.HasAnyOf(items)); + EXPECT_FALSE(set.HasAnyOf(EnumSet(20))); + EXPECT_FALSE(set.HasAnyOf(EnumSet(600))); + EXPECT_TRUE(set.HasAnyOf(EnumSet(5))); + EXPECT_TRUE(set.HasAnyOf(EnumSet(300))); + EXPECT_TRUE(set.HasAnyOf(EnumSet(0))); +} + +TEST(EnumSet, DefaultIsEmpty) { + EnumSet set; + for (uint32_t i = 0; i < 1000; ++i) { + EXPECT_FALSE(set.Contains(i)); + } +} + +TEST(CapabilitySet, ConstructSingleMemberMatrix) { + CapabilitySet s(SpvCapabilityMatrix); + EXPECT_TRUE(s.Contains(SpvCapabilityMatrix)); + EXPECT_FALSE(s.Contains(SpvCapabilityShader)); + EXPECT_FALSE(s.Contains(static_cast(1000))); +} + +TEST(CapabilitySet, ConstructSingleMemberMaxInMask) { + CapabilitySet s(static_cast(63)); + EXPECT_FALSE(s.Contains(SpvCapabilityMatrix)); + EXPECT_FALSE(s.Contains(SpvCapabilityShader)); + EXPECT_TRUE(s.Contains(static_cast(63))); + EXPECT_FALSE(s.Contains(static_cast(64))); + EXPECT_FALSE(s.Contains(static_cast(1000))); +} + +TEST(CapabilitySet, ConstructSingleMemberMinOverflow) { + // Check the first one that forces overflow beyond the mask. + CapabilitySet s(static_cast(64)); + EXPECT_FALSE(s.Contains(SpvCapabilityMatrix)); + EXPECT_FALSE(s.Contains(SpvCapabilityShader)); + EXPECT_FALSE(s.Contains(static_cast(63))); + EXPECT_TRUE(s.Contains(static_cast(64))); + EXPECT_FALSE(s.Contains(static_cast(1000))); +} + +TEST(CapabilitySet, ConstructSingleMemberMaxOverflow) { + // Check the max 32-bit signed int. + CapabilitySet s(static_cast(0x7fffffffu)); + EXPECT_FALSE(s.Contains(SpvCapabilityMatrix)); + EXPECT_FALSE(s.Contains(SpvCapabilityShader)); + EXPECT_FALSE(s.Contains(static_cast(1000))); + EXPECT_TRUE(s.Contains(static_cast(0x7fffffffu))); +} + +TEST(CapabilitySet, AddEnum) { + CapabilitySet s(SpvCapabilityShader); + s.Add(SpvCapabilityKernel); + s.Add(static_cast(42)); + EXPECT_FALSE(s.Contains(SpvCapabilityMatrix)); + EXPECT_TRUE(s.Contains(SpvCapabilityShader)); + EXPECT_TRUE(s.Contains(SpvCapabilityKernel)); + EXPECT_TRUE(s.Contains(static_cast(42))); +} + +TEST(CapabilitySet, InitializerListEmpty) { + CapabilitySet s{}; + for (uint32_t i = 0; i < 1000; i++) { + EXPECT_FALSE(s.Contains(static_cast(i))); + } +} + +struct ForEachCase { + CapabilitySet capabilities; + std::vector expected; +}; + +using CapabilitySetForEachTest = ::testing::TestWithParam; + +TEST_P(CapabilitySetForEachTest, CallsAsExpected) { + EXPECT_THAT(ElementsIn(GetParam().capabilities), Eq(GetParam().expected)); +} + +TEST_P(CapabilitySetForEachTest, CopyConstructor) { + CapabilitySet copy(GetParam().capabilities); + EXPECT_THAT(ElementsIn(copy), Eq(GetParam().expected)); +} + +TEST_P(CapabilitySetForEachTest, MoveConstructor) { + // We need a writable copy to move from. + CapabilitySet copy(GetParam().capabilities); + CapabilitySet moved(std::move(copy)); + EXPECT_THAT(ElementsIn(moved), Eq(GetParam().expected)); + + // The moved-from set is empty. + EXPECT_THAT(ElementsIn(copy), Eq(std::vector{})); +} + +TEST_P(CapabilitySetForEachTest, OperatorEquals) { + CapabilitySet assigned = GetParam().capabilities; + EXPECT_THAT(ElementsIn(assigned), Eq(GetParam().expected)); +} + +TEST_P(CapabilitySetForEachTest, OperatorEqualsSelfAssign) { + CapabilitySet assigned{GetParam().capabilities}; + assigned = assigned; + EXPECT_THAT(ElementsIn(assigned), Eq(GetParam().expected)); +} + +INSTANTIATE_TEST_SUITE_P(Samples, CapabilitySetForEachTest, + ValuesIn(std::vector{ + {{}, {}}, + {{SpvCapabilityMatrix}, {SpvCapabilityMatrix}}, + {{SpvCapabilityKernel, SpvCapabilityShader}, + {SpvCapabilityShader, SpvCapabilityKernel}}, + {{static_cast(999)}, + {static_cast(999)}}, + {{static_cast(0x7fffffff)}, + {static_cast(0x7fffffff)}}, + // Mixture and out of order + {{static_cast(0x7fffffff), + static_cast(100), + SpvCapabilityShader, SpvCapabilityMatrix}, + {SpvCapabilityMatrix, SpvCapabilityShader, + static_cast(100), + static_cast(0x7fffffff)}}, + })); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/enum_string_mapping_test.cpp b/third_party/spirv-tools/test/enum_string_mapping_test.cpp new file mode 100644 index 0000000..9bbd8ca --- /dev/null +++ b/third_party/spirv-tools/test/enum_string_mapping_test.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2017 Google Inc. +// Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights +// reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for OpExtension validator rules. + +#include +#include +#include + +#include "gtest/gtest.h" +#include "source/enum_string_mapping.h" +#include "source/extensions.h" + +namespace spvtools { +namespace { + +using ::testing::Values; +using ::testing::ValuesIn; + +using ExtensionTest = + ::testing::TestWithParam>; +using UnknownExtensionTest = ::testing::TestWithParam; +using CapabilityTest = + ::testing::TestWithParam>; + +TEST_P(ExtensionTest, TestExtensionFromString) { + const std::pair& param = GetParam(); + const Extension extension = param.first; + const std::string extension_str = param.second; + Extension result_extension; + ASSERT_TRUE(GetExtensionFromString(extension_str.c_str(), &result_extension)); + EXPECT_EQ(extension, result_extension); +} + +TEST_P(ExtensionTest, TestExtensionToString) { + const std::pair& param = GetParam(); + const Extension extension = param.first; + const std::string extension_str = param.second; + const std::string result_str = ExtensionToString(extension); + EXPECT_EQ(extension_str, result_str); +} + +TEST_P(UnknownExtensionTest, TestExtensionFromStringFails) { + Extension result_extension; + ASSERT_FALSE(GetExtensionFromString(GetParam().c_str(), &result_extension)); +} + +TEST_P(CapabilityTest, TestCapabilityToString) { + const std::pair& param = GetParam(); + const SpvCapability capability = param.first; + const std::string capability_str = param.second; + const std::string result_str = CapabilityToString(capability); + EXPECT_EQ(capability_str, result_str); +} + +INSTANTIATE_TEST_SUITE_P( + AllExtensions, ExtensionTest, + ValuesIn(std::vector>({ + {Extension::kSPV_KHR_16bit_storage, "SPV_KHR_16bit_storage"}, + {Extension::kSPV_KHR_device_group, "SPV_KHR_device_group"}, + {Extension::kSPV_KHR_multiview, "SPV_KHR_multiview"}, + {Extension::kSPV_KHR_shader_ballot, "SPV_KHR_shader_ballot"}, + {Extension::kSPV_KHR_shader_draw_parameters, + "SPV_KHR_shader_draw_parameters"}, + {Extension::kSPV_KHR_subgroup_vote, "SPV_KHR_subgroup_vote"}, + {Extension::kSPV_NVX_multiview_per_view_attributes, + "SPV_NVX_multiview_per_view_attributes"}, + {Extension::kSPV_NV_geometry_shader_passthrough, + "SPV_NV_geometry_shader_passthrough"}, + {Extension::kSPV_NV_sample_mask_override_coverage, + "SPV_NV_sample_mask_override_coverage"}, + {Extension::kSPV_NV_stereo_view_rendering, + "SPV_NV_stereo_view_rendering"}, + {Extension::kSPV_NV_viewport_array2, "SPV_NV_viewport_array2"}, + {Extension::kSPV_GOOGLE_decorate_string, "SPV_GOOGLE_decorate_string"}, + {Extension::kSPV_GOOGLE_hlsl_functionality1, + "SPV_GOOGLE_hlsl_functionality1"}, + {Extension::kSPV_KHR_8bit_storage, "SPV_KHR_8bit_storage"}, + }))); + +INSTANTIATE_TEST_SUITE_P(UnknownExtensions, UnknownExtensionTest, + Values("", "SPV_KHR_", "SPV_KHR_device_group_ERROR", + /*alphabetically before all extensions*/ "A", + /*alphabetically after all extensions*/ "Z", + "SPV_ERROR_random_string_hfsdklhlktherh")); + +INSTANTIATE_TEST_SUITE_P( + AllCapabilities, CapabilityTest, + ValuesIn(std::vector>( + {{SpvCapabilityMatrix, "Matrix"}, + {SpvCapabilityShader, "Shader"}, + {SpvCapabilityGeometry, "Geometry"}, + {SpvCapabilityTessellation, "Tessellation"}, + {SpvCapabilityAddresses, "Addresses"}, + {SpvCapabilityLinkage, "Linkage"}, + {SpvCapabilityKernel, "Kernel"}, + {SpvCapabilityVector16, "Vector16"}, + {SpvCapabilityFloat16Buffer, "Float16Buffer"}, + {SpvCapabilityFloat16, "Float16"}, + {SpvCapabilityFloat64, "Float64"}, + {SpvCapabilityInt64, "Int64"}, + {SpvCapabilityInt64Atomics, "Int64Atomics"}, + {SpvCapabilityImageBasic, "ImageBasic"}, + {SpvCapabilityImageReadWrite, "ImageReadWrite"}, + {SpvCapabilityImageMipmap, "ImageMipmap"}, + {SpvCapabilityPipes, "Pipes"}, + {SpvCapabilityGroups, "Groups"}, + {SpvCapabilityDeviceEnqueue, "DeviceEnqueue"}, + {SpvCapabilityLiteralSampler, "LiteralSampler"}, + {SpvCapabilityAtomicStorage, "AtomicStorage"}, + {SpvCapabilityInt16, "Int16"}, + {SpvCapabilityTessellationPointSize, "TessellationPointSize"}, + {SpvCapabilityGeometryPointSize, "GeometryPointSize"}, + {SpvCapabilityImageGatherExtended, "ImageGatherExtended"}, + {SpvCapabilityStorageImageMultisample, "StorageImageMultisample"}, + {SpvCapabilityUniformBufferArrayDynamicIndexing, + "UniformBufferArrayDynamicIndexing"}, + {SpvCapabilitySampledImageArrayDynamicIndexing, + "SampledImageArrayDynamicIndexing"}, + {SpvCapabilityStorageBufferArrayDynamicIndexing, + "StorageBufferArrayDynamicIndexing"}, + {SpvCapabilityStorageImageArrayDynamicIndexing, + "StorageImageArrayDynamicIndexing"}, + {SpvCapabilityClipDistance, "ClipDistance"}, + {SpvCapabilityCullDistance, "CullDistance"}, + {SpvCapabilityImageCubeArray, "ImageCubeArray"}, + {SpvCapabilitySampleRateShading, "SampleRateShading"}, + {SpvCapabilityImageRect, "ImageRect"}, + {SpvCapabilitySampledRect, "SampledRect"}, + {SpvCapabilityGenericPointer, "GenericPointer"}, + {SpvCapabilityInt8, "Int8"}, + {SpvCapabilityInputAttachment, "InputAttachment"}, + {SpvCapabilitySparseResidency, "SparseResidency"}, + {SpvCapabilityMinLod, "MinLod"}, + {SpvCapabilitySampled1D, "Sampled1D"}, + {SpvCapabilityImage1D, "Image1D"}, + {SpvCapabilitySampledCubeArray, "SampledCubeArray"}, + {SpvCapabilitySampledBuffer, "SampledBuffer"}, + {SpvCapabilityImageBuffer, "ImageBuffer"}, + {SpvCapabilityImageMSArray, "ImageMSArray"}, + {SpvCapabilityStorageImageExtendedFormats, + "StorageImageExtendedFormats"}, + {SpvCapabilityImageQuery, "ImageQuery"}, + {SpvCapabilityDerivativeControl, "DerivativeControl"}, + {SpvCapabilityInterpolationFunction, "InterpolationFunction"}, + {SpvCapabilityTransformFeedback, "TransformFeedback"}, + {SpvCapabilityGeometryStreams, "GeometryStreams"}, + {SpvCapabilityStorageImageReadWithoutFormat, + "StorageImageReadWithoutFormat"}, + {SpvCapabilityStorageImageWriteWithoutFormat, + "StorageImageWriteWithoutFormat"}, + {SpvCapabilityMultiViewport, "MultiViewport"}, + {SpvCapabilitySubgroupDispatch, "SubgroupDispatch"}, + {SpvCapabilityNamedBarrier, "NamedBarrier"}, + {SpvCapabilityPipeStorage, "PipeStorage"}, + {SpvCapabilitySubgroupBallotKHR, "SubgroupBallotKHR"}, + {SpvCapabilityDrawParameters, "DrawParameters"}, + {SpvCapabilitySubgroupVoteKHR, "SubgroupVoteKHR"}, + {SpvCapabilityStorageBuffer16BitAccess, "StorageBuffer16BitAccess"}, + {SpvCapabilityStorageUniformBufferBlock16, + "StorageBuffer16BitAccess"}, // Preferred name + {SpvCapabilityUniformAndStorageBuffer16BitAccess, + "UniformAndStorageBuffer16BitAccess"}, + {SpvCapabilityStorageUniform16, + "UniformAndStorageBuffer16BitAccess"}, // Preferred name + {SpvCapabilityStoragePushConstant16, "StoragePushConstant16"}, + {SpvCapabilityStorageInputOutput16, "StorageInputOutput16"}, + {SpvCapabilityDeviceGroup, "DeviceGroup"}, + {SpvCapabilityAtomicFloat32AddEXT, "AtomicFloat32AddEXT"}, + {SpvCapabilityAtomicFloat64AddEXT, "AtomicFloat64AddEXT"}, + {SpvCapabilityMultiView, "MultiView"}, + {SpvCapabilityInt64ImageEXT, "Int64ImageEXT"}, + {SpvCapabilitySampleMaskOverrideCoverageNV, + "SampleMaskOverrideCoverageNV"}, + {SpvCapabilityGeometryShaderPassthroughNV, + "GeometryShaderPassthroughNV"}, + // The next two are different names for the same token. + {SpvCapabilityShaderViewportIndexLayerNV, + "ShaderViewportIndexLayerEXT"}, + {SpvCapabilityShaderViewportIndexLayerEXT, + "ShaderViewportIndexLayerEXT"}, + {SpvCapabilityShaderViewportMaskNV, "ShaderViewportMaskNV"}, + {SpvCapabilityShaderStereoViewNV, "ShaderStereoViewNV"}, + {SpvCapabilityPerViewAttributesNV, "PerViewAttributesNV"}}))); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/ext_inst.cldebug100_test.cpp b/third_party/spirv-tools/test/ext_inst.cldebug100_test.cpp new file mode 100644 index 0000000..4f1e106 --- /dev/null +++ b/third_party/spirv-tools/test/ext_inst.cldebug100_test.cpp @@ -0,0 +1,1070 @@ +// Copyright (c) 2017-2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "OpenCLDebugInfo100.h" +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "spirv/unified1/spirv.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +// This file tests the correctness of encoding and decoding of instructions +// involving the OpenCL.DebugInfo.100 extended instruction set. +// Validation is not checked here. + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using testing::Eq; +using utils::MakeVector; + +// Test values of enums vs. what is written in the spec. + +TEST(ExtInstCLDebugInfo, InstructionValues) { + EXPECT_EQ(0, OpenCLDebugInfo100DebugInfoNone); + EXPECT_EQ(1, OpenCLDebugInfo100DebugCompilationUnit); + EXPECT_EQ(2, OpenCLDebugInfo100DebugTypeBasic); + EXPECT_EQ(3, OpenCLDebugInfo100DebugTypePointer); + EXPECT_EQ(4, OpenCLDebugInfo100DebugTypeQualifier); + EXPECT_EQ(5, OpenCLDebugInfo100DebugTypeArray); + EXPECT_EQ(6, OpenCLDebugInfo100DebugTypeVector); + EXPECT_EQ(7, OpenCLDebugInfo100DebugTypedef); + EXPECT_EQ(8, OpenCLDebugInfo100DebugTypeFunction); + EXPECT_EQ(9, OpenCLDebugInfo100DebugTypeEnum); + EXPECT_EQ(10, OpenCLDebugInfo100DebugTypeComposite); + EXPECT_EQ(11, OpenCLDebugInfo100DebugTypeMember); + EXPECT_EQ(12, OpenCLDebugInfo100DebugTypeInheritance); + EXPECT_EQ(13, OpenCLDebugInfo100DebugTypePtrToMember); + EXPECT_EQ(14, OpenCLDebugInfo100DebugTypeTemplate); + EXPECT_EQ(15, OpenCLDebugInfo100DebugTypeTemplateParameter); + EXPECT_EQ(16, OpenCLDebugInfo100DebugTypeTemplateTemplateParameter); + EXPECT_EQ(17, OpenCLDebugInfo100DebugTypeTemplateParameterPack); + EXPECT_EQ(18, OpenCLDebugInfo100DebugGlobalVariable); + EXPECT_EQ(19, OpenCLDebugInfo100DebugFunctionDeclaration); + EXPECT_EQ(20, OpenCLDebugInfo100DebugFunction); + EXPECT_EQ(21, OpenCLDebugInfo100DebugLexicalBlock); + EXPECT_EQ(22, OpenCLDebugInfo100DebugLexicalBlockDiscriminator); + EXPECT_EQ(23, OpenCLDebugInfo100DebugScope); + EXPECT_EQ(24, OpenCLDebugInfo100DebugNoScope); + EXPECT_EQ(25, OpenCLDebugInfo100DebugInlinedAt); + EXPECT_EQ(26, OpenCLDebugInfo100DebugLocalVariable); + EXPECT_EQ(27, OpenCLDebugInfo100DebugInlinedVariable); + EXPECT_EQ(28, OpenCLDebugInfo100DebugDeclare); + EXPECT_EQ(29, OpenCLDebugInfo100DebugValue); + EXPECT_EQ(30, OpenCLDebugInfo100DebugOperation); + EXPECT_EQ(31, OpenCLDebugInfo100DebugExpression); + EXPECT_EQ(32, OpenCLDebugInfo100DebugMacroDef); + EXPECT_EQ(33, OpenCLDebugInfo100DebugMacroUndef); + EXPECT_EQ(34, OpenCLDebugInfo100DebugImportedEntity); + EXPECT_EQ(35, OpenCLDebugInfo100DebugSource); +} + +TEST(ExtInstCLDebugInfo, InfoFlagValues) { + EXPECT_EQ(1 << 0, OpenCLDebugInfo100FlagIsProtected); + EXPECT_EQ(1 << 1, OpenCLDebugInfo100FlagIsPrivate); + EXPECT_EQ(((1 << 0) | (1 << 1)), OpenCLDebugInfo100FlagIsPublic); + EXPECT_EQ(1 << 2, OpenCLDebugInfo100FlagIsLocal); + EXPECT_EQ(1 << 3, OpenCLDebugInfo100FlagIsDefinition); + EXPECT_EQ(1 << 4, OpenCLDebugInfo100FlagFwdDecl); + EXPECT_EQ(1 << 5, OpenCLDebugInfo100FlagArtificial); + EXPECT_EQ(1 << 6, OpenCLDebugInfo100FlagExplicit); + EXPECT_EQ(1 << 7, OpenCLDebugInfo100FlagPrototyped); + EXPECT_EQ(1 << 8, OpenCLDebugInfo100FlagObjectPointer); + EXPECT_EQ(1 << 9, OpenCLDebugInfo100FlagStaticMember); + EXPECT_EQ(1 << 10, OpenCLDebugInfo100FlagIndirectVariable); + EXPECT_EQ(1 << 11, OpenCLDebugInfo100FlagLValueReference); + EXPECT_EQ(1 << 12, OpenCLDebugInfo100FlagRValueReference); + EXPECT_EQ(1 << 13, OpenCLDebugInfo100FlagIsOptimized); + EXPECT_EQ(1 << 14, OpenCLDebugInfo100FlagIsEnumClass); + EXPECT_EQ(1 << 15, OpenCLDebugInfo100FlagTypePassByValue); + EXPECT_EQ(1 << 16, OpenCLDebugInfo100FlagTypePassByReference); +} + +TEST(ExtInstCLDebugInfo, BaseTypeAttributeEndodingValues) { + EXPECT_EQ(0, OpenCLDebugInfo100Unspecified); + EXPECT_EQ(1, OpenCLDebugInfo100Address); + EXPECT_EQ(2, OpenCLDebugInfo100Boolean); + EXPECT_EQ(3, OpenCLDebugInfo100Float); + EXPECT_EQ(4, OpenCLDebugInfo100Signed); + EXPECT_EQ(5, OpenCLDebugInfo100SignedChar); + EXPECT_EQ(6, OpenCLDebugInfo100Unsigned); + EXPECT_EQ(7, OpenCLDebugInfo100UnsignedChar); +} + +TEST(ExtInstCLDebugInfo, CompositeTypeValues) { + EXPECT_EQ(0, OpenCLDebugInfo100Class); + EXPECT_EQ(1, OpenCLDebugInfo100Structure); + EXPECT_EQ(2, OpenCLDebugInfo100Union); +} + +TEST(ExtInstCLDebugInfo, TypeQualifierValues) { + EXPECT_EQ(0, OpenCLDebugInfo100ConstType); + EXPECT_EQ(1, OpenCLDebugInfo100VolatileType); + EXPECT_EQ(2, OpenCLDebugInfo100RestrictType); + EXPECT_EQ(3, OpenCLDebugInfo100AtomicType); +} + +TEST(ExtInstCLDebugInfo, DebugOperationValues) { + EXPECT_EQ(0, OpenCLDebugInfo100Deref); + EXPECT_EQ(1, OpenCLDebugInfo100Plus); + EXPECT_EQ(2, OpenCLDebugInfo100Minus); + EXPECT_EQ(3, OpenCLDebugInfo100PlusUconst); + EXPECT_EQ(4, OpenCLDebugInfo100BitPiece); + EXPECT_EQ(5, OpenCLDebugInfo100Swap); + EXPECT_EQ(6, OpenCLDebugInfo100Xderef); + EXPECT_EQ(7, OpenCLDebugInfo100StackValue); + EXPECT_EQ(8, OpenCLDebugInfo100Constu); + EXPECT_EQ(9, OpenCLDebugInfo100Fragment); +} + +TEST(ExtInstCLDebugInfo, ImportedEntityValues) { + EXPECT_EQ(0, OpenCLDebugInfo100ImportedModule); + EXPECT_EQ(1, OpenCLDebugInfo100ImportedDeclaration); +} + +// Test round trip through assembler and disassembler. + +struct InstructionCase { + uint32_t opcode; + std::string name; + std::string operands; + std::vector expected_operands; +}; + +using ExtInstCLDebugInfo100RoundTripTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; +using ExtInstCLDebugInfo100RoundTripTestExplicit = spvtest::TextToBinaryTest; + +TEST_P(ExtInstCLDebugInfo100RoundTripTest, ParameterizedExtInst) { + const std::string input = + "%1 = OpExtInstImport \"OpenCL.DebugInfo.100\"\n" + "%3 = OpExtInst %2 %1 " + + GetParam().name + GetParam().operands + "\n"; + // First make sure it assembles correctly. + std::cout << input << std::endl; + EXPECT_THAT(CompiledInstructions(input), + Eq(Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, + MakeVector("OpenCL.DebugInfo.100")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode}, + GetParam().expected_operands)}))) + << input; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input; +} + +#define EPREFIX "Debug" + +#define CASE_0(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, "", {} \ + } + +#define CASE_ILL(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #L0 " " #L1, { \ + 4, L0, L1 \ + } \ + } + +#define CASE_IL(Enum, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #L0, { \ + 4, L0 \ + } \ + } + +#define CASE_I(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4", { 4 } \ + } + +#define CASE_II(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5", { 4, 5 } \ + } + +#define CASE_III(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6", { \ + 4, 5, 6 \ + } \ + } + +#define CASE_IIII(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6 %7", { \ + 4, 5, 6, 7 \ + } \ + } + +#define CASE_IIIII(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 %6 %7 %8", \ + { \ + 4, 5, 6, 7, 8 \ + } \ + } + +#define CASE_IIIIII(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 %7 %8 %9", { \ + 4, 5, 6, 7, 8, 9 \ + } \ + } + +#define CASE_IIIIIII(Enum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 %7 %8 %9 %10", { \ + 4, 5, 6, 7, 8, 9, 10 \ + } \ + } + +#define CASE_IIILLI(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7", { \ + 4, 5, 6, L0, L1, 7 \ + } \ + } + +#define CASE_IIILLIF(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 " Fstr, { \ + 4, 5, 6, L0, L1, 7, Fnum \ + } \ + } + +#define CASE_IIILLIFL(Enum, L0, L1, Fstr, Fnum, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 " Fstr " " #L2, { \ + 4, 5, 6, L0, L1, 7, Fnum, L2 \ + } \ + } + +#define CASE_IIILLIL(Enum, L0, L1, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 " #L2, { \ + 4, 5, 6, L0, L1, 7, L2 \ + } \ + } + +#define CASE_IE(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #E0, { \ + 4, uint32_t(OpenCLDebugInfo100##E0) \ + } \ + } + +#define CASE_IEIILLI(Enum, E0, L1, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 %6 " #L1 " " #L2 " %7", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, 6, L1, L2, 7 \ + } \ + } + +#define CASE_IIE(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 %5 " #E0, { \ + 4, 5, uint32_t(OpenCLDebugInfo100##E0) \ + } \ + } + +#define CASE_ISF(Enum, S0, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #S0 " " Fstr, { \ + 4, uint32_t(SpvStorageClass##S0), Fnum \ + } \ + } + +#define CASE_LII(Enum, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #L0 " %4 %5", \ + { \ + L0, 4, 5 \ + } \ + } + +#define CASE_LLIe(Enum, L0, L1, RawEnumName, RawEnumValue) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #L0 " " #L1 " %4 " RawEnumName, { \ + L0, L1, 4, RawEnumValue \ + } \ + } + +#define CASE_ILI(Enum, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " %4 " #L0 " %5", \ + { \ + 4, L0, 5 \ + } \ + } + +#define CASE_ILII(Enum, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #L0 " %5 %6", { \ + 4, L0, 5, 6 \ + } \ + } + +#define CASE_ILLII(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #L0 " " #L1 " %5 %6", { \ + 4, L0, L1, 5, 6 \ + } \ + } + +#define CASE_IIILLIIF(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr, { \ + 4, 5, 6, L0, L1, 7, 8, Fnum \ + } \ + } + +#define CASE_IIILLIIFII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10 \ + } \ + } + +#define CASE_IIILLIIFIIII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12 \ + } \ + } + +#define CASE_IIILLIIFIIIIII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12 %13 %14", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12, 13, 14 \ + } \ + } + +#define CASE_IEILLIIIF(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr, { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum \ + } \ + } + +#define CASE_IEILLIIIFI(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9 \ + } \ + } + +#define CASE_IEILLIIIFII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10 \ + } \ + } + +#define CASE_IEILLIIIFIII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10 %11", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10, 11 \ + } \ + } + +#define CASE_IEILLIIIFIIII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 %8 " Fstr " %9 %10 %11 %12", { \ + 4, uint32_t(OpenCLDebugInfo100##E0), 5, L0, L1, 6, 7, 8, Fnum, 9, 10, \ + 11, 12 \ + } \ + } + +#define CASE_IIILLIIIF(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr, { \ + 4, 5, 6, L0, L1, 7, 8, 9, Fnum \ + } \ + } + +#define CASE_IIILLIIIFI(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr " %10", { \ + 4, 5, 6, L0, L1, 7, 8, 9, Fnum, 10 \ + } \ + } + +#define CASE_IIIIF(Enum, Fstr, Fnum) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 %7 " Fstr, { \ + 4, 5, 6, 7, Fnum \ + } \ + } + +#define CASE_IIILL(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1, { \ + 4, 5, 6, L0, L1 \ + } \ + } + +#define CASE_IIIILL(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 %7 " #L0 " " #L1, { \ + 4, 5, 6, 7, L0, L1 \ + } \ + } + +#define CASE_IILLI(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 " #L0 " " #L1 " %6", { \ + 4, 5, L0, L1, 6 \ + } \ + } + +#define CASE_IILLII(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7", { \ + 4, 5, L0, L1, 6, 7 \ + } \ + } + +#define CASE_IILLIII(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7 %8", { \ + 4, 5, L0, L1, 6, 7, 8 \ + } \ + } + +#define CASE_IILLIIII(Enum, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7 %8 %9", { \ + 4, 5, L0, L1, 6, 7, 8, 9 \ + } \ + } + +#define CASE_IIILLIIFLI(Enum, L0, L1, Fstr, Fnum, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9 \ + } \ + } + +#define CASE_IIILLIIFLII(Enum, L0, L1, Fstr, Fnum, L2) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9 %10", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9, 10 \ + } \ + } + +#define CASE_E(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0, { \ + uint32_t(OpenCLDebugInfo100##E0) \ + } \ + } + +#define CASE_EI(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " %4", { \ + uint32_t(OpenCLDebugInfo100##E0), 4 \ + } \ + } + +#define CASE_EII(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " %4 %5", \ + { \ + uint32_t(OpenCLDebugInfo100##E0), 4, 5 \ + } \ + } + +#define CASE_EIII(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #E0 " %4 %5 %6", { \ + uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6 \ + } \ + } + +#define CASE_EIIII(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #E0 " %4 %5 %6 %7", { \ + uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6, 7 \ + } \ + } + +#define CASE_EIIIII(Enum, E0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #E0 " %4 %5 %6 %7 %8", { \ + uint32_t(OpenCLDebugInfo100##E0), 4, 5, 6, 7, 8 \ + } \ + } + +#define CASE_EL(Enum, E0, L0) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, " " #E0 " " #L0, { \ + uint32_t(OpenCLDebugInfo100##E0), L0 \ + } \ + } + +#define CASE_ELL(Enum, E0, L0, L1) \ + { \ + uint32_t(OpenCLDebugInfo100Debug##Enum), EPREFIX #Enum, \ + " " #E0 " " #L0 " " #L1, { \ + uint32_t(OpenCLDebugInfo100##E0), L0, L1 \ + } \ + } + +// OpenCL.DebugInfo.100 4.1 Missing Debugging Information +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInfoNone, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_0(InfoNone), // enum value 0 + }))); + +// OpenCL.DebugInfo.100 4.2 Compilation Unit +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugCompilationUnit, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_LLIe(CompilationUnit, 100, 42, "HLSL", SpvSourceLanguageHLSL), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugSource, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + // TODO(dneto): Should this be a list of sourc texts, + // to accomodate length limits? + CASE_I(Source), + CASE_II(Source), + }))); + +// OpenCL.DebugInfo.100 4.3 Type instructions +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeBasic, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIE(TypeBasic, Unspecified), + CASE_IIE(TypeBasic, Address), + CASE_IIE(TypeBasic, Boolean), + CASE_IIE(TypeBasic, Float), + CASE_IIE(TypeBasic, Signed), + CASE_IIE(TypeBasic, SignedChar), + CASE_IIE(TypeBasic, Unsigned), + CASE_IIE(TypeBasic, UnsignedChar), + }))); + +// The FlagIsPublic is value is (1 << 0) | (1 << 2) which is the same +// as the bitwise-OR of FlagIsProtected and FlagIsPrivate. +// The disassembler will emit the compound expression instead. +// There is no simple fix for this. This enum is not really a mask +// for the bottom two bits. +TEST_F(ExtInstCLDebugInfo100RoundTripTestExplicit, FlagIsPublic) { + const std::string prefix = + "%1 = OpExtInstImport \"DebugInfo\"\n" + "%3 = OpExtInst %2 %1 DebugTypePointer %4 Private "; + const std::string input = prefix + "FlagIsPublic\n"; + const std::string expected = prefix + "FlagIsProtected|FlagIsPrivate\n"; + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(input), + Eq(Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")), + MakeInstruction(SpvOpExtInst, + {2, 3, 1, OpenCLDebugInfo100DebugTypePointer, 4, + uint32_t(SpvStorageClassPrivate), + OpenCLDebugInfo100FlagIsPublic})}))) + << input; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input; +} + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypePointer, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + + //// Use each flag independently. + CASE_ISF(TypePointer, Private, "FlagIsProtected", + uint32_t(OpenCLDebugInfo100FlagIsProtected)), + CASE_ISF(TypePointer, Private, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + + // FlagIsPublic is tested above. + + CASE_ISF(TypePointer, Private, "FlagIsLocal", + uint32_t(OpenCLDebugInfo100FlagIsLocal)), + CASE_ISF(TypePointer, Private, "FlagIsDefinition", + uint32_t(OpenCLDebugInfo100FlagIsDefinition)), + CASE_ISF(TypePointer, Private, "FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagFwdDecl)), + CASE_ISF(TypePointer, Private, "FlagArtificial", + uint32_t(OpenCLDebugInfo100FlagArtificial)), + CASE_ISF(TypePointer, Private, "FlagExplicit", + uint32_t(OpenCLDebugInfo100FlagExplicit)), + CASE_ISF(TypePointer, Private, "FlagPrototyped", + uint32_t(OpenCLDebugInfo100FlagPrototyped)), + CASE_ISF(TypePointer, Private, "FlagObjectPointer", + uint32_t(OpenCLDebugInfo100FlagObjectPointer)), + CASE_ISF(TypePointer, Private, "FlagStaticMember", + uint32_t(OpenCLDebugInfo100FlagStaticMember)), + CASE_ISF(TypePointer, Private, "FlagIndirectVariable", + uint32_t(OpenCLDebugInfo100FlagIndirectVariable)), + CASE_ISF(TypePointer, Private, "FlagLValueReference", + uint32_t(OpenCLDebugInfo100FlagLValueReference)), + CASE_ISF(TypePointer, Private, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_ISF(TypePointer, Private, "FlagIsEnumClass", + uint32_t(OpenCLDebugInfo100FlagIsEnumClass)), + CASE_ISF(TypePointer, Private, "FlagTypePassByValue", + uint32_t(OpenCLDebugInfo100FlagTypePassByValue)), + CASE_ISF(TypePointer, Private, "FlagTypePassByReference", + uint32_t(OpenCLDebugInfo100FlagTypePassByReference)), + + //// Use flags in combination, and try different storage classes. + CASE_ISF(TypePointer, Function, "FlagIsProtected|FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsProtected) | + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_ISF( + TypePointer, Workgroup, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl) | + uint32_t(OpenCLDebugInfo100FlagIndirectVariable) | + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeQualifier, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IE(TypeQualifier, ConstType), + CASE_IE(TypeQualifier, VolatileType), + CASE_IE(TypeQualifier, RestrictType), + CASE_IE(TypeQualifier, AtomicType), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeArray, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_II(TypeArray), + CASE_III(TypeArray), + CASE_IIII(TypeArray), + CASE_IIIII(TypeArray), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeVector, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IL(TypeVector, 2), + CASE_IL(TypeVector, 3), + CASE_IL(TypeVector, 4), + CASE_IL(TypeVector, 16), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypedef, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLI(Typedef, 12, 13), + CASE_IIILLI(Typedef, 14, 99), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeFunction, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_EI(TypeFunction, FlagIsProtected), + CASE_EII(TypeFunction, FlagIsDefinition), + CASE_EIII(TypeFunction, FlagArtificial), + CASE_EIIII(TypeFunction, FlagExplicit), + CASE_EIIIII(TypeFunction, FlagIsPrivate), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypeEnum, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIFII( + TypeEnum, 12, 13, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl) | + uint32_t(OpenCLDebugInfo100FlagIndirectVariable) | + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIFIIII(TypeEnum, 17, 18, "FlagStaticMember", + uint32_t(OpenCLDebugInfo100FlagStaticMember)), + CASE_IIILLIIFIIIIII(TypeEnum, 99, 1, "FlagStaticMember", + uint32_t(OpenCLDebugInfo100FlagStaticMember)), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypeComposite, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IEILLIIIF( + TypeComposite, Class, 12, 13, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl) | + uint32_t(OpenCLDebugInfo100FlagIndirectVariable) | + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + // Cover all tag values: Class, Structure, Union + CASE_IEILLIIIF(TypeComposite, Class, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIF(TypeComposite, Structure, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIF(TypeComposite, Union, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + // Now add members + CASE_IEILLIIIFI(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIFII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIFIII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IEILLIIIFIIII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypeMember, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIIF(TypeMember, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IIILLIIIF(TypeMember, 99, 100, "FlagIsPrivate|FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl)), + // Add the optional Id argument. + CASE_IIILLIIIFI(TypeMember, 12, 13, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugTypeInheritance, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIIIF(TypeInheritance, "FlagIsPrivate", + uint32_t(OpenCLDebugInfo100FlagIsPrivate)), + CASE_IIIIF(TypeInheritance, "FlagIsPrivate|FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagIsPrivate) | + uint32_t(OpenCLDebugInfo100FlagFwdDecl)), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypePtrToMember, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_II(TypePtrToMember), + }))); + +// OpenCL.DebugInfo.100 4.4 Templates + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplate, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_II(TypeTemplate), + CASE_III(TypeTemplate), + CASE_IIII(TypeTemplate), + CASE_IIIII(TypeTemplate), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateParameter, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIIILL(TypeTemplateParameter, 1, 2), + CASE_IIIILL(TypeTemplateParameter, 99, 102), + CASE_IIIILL(TypeTemplateParameter, 10, 7), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateTemplateParameter, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILL(TypeTemplateTemplateParameter, 1, 2), + CASE_IIILL(TypeTemplateTemplateParameter, 99, 102), + CASE_IIILL(TypeTemplateTemplateParameter, 10, 7), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugTypeTemplateParameterPack, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IILLI(TypeTemplateParameterPack, 1, 2), + CASE_IILLII(TypeTemplateParameterPack, 99, 102), + CASE_IILLIII(TypeTemplateParameterPack, 10, 7), + CASE_IILLIIII(TypeTemplateParameterPack, 10, 7), + }))); + +// OpenCL.DebugInfo.100 4.5 Global Variables + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugGlobalVariable, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIIF(GlobalVariable, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIIF(GlobalVariable, 42, 43, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIIFI(GlobalVariable, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIIFI(GlobalVariable, 42, 43, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + }))); + +// OpenCL.DebugInfo.100 4.6 Functions + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugFunctionDeclaration, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIF(FunctionDeclaration, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized)), + CASE_IIILLIIF(FunctionDeclaration, 42, 43, "FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagFwdDecl)), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugFunction, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIFLI(Function, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized), 3), + CASE_IIILLIIFLI(Function, 42, 43, "FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagFwdDecl), 44), + // Add the optional declaration Id. + CASE_IIILLIIFLII(Function, 1, 2, "FlagIsOptimized", + uint32_t(OpenCLDebugInfo100FlagIsOptimized), 3), + CASE_IIILLIIFLII(Function, 42, 43, "FlagFwdDecl", + uint32_t(OpenCLDebugInfo100FlagFwdDecl), 44), + }))); + +// OpenCL.DebugInfo.100 4.7 Local Information + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugLexicalBlock, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILLII(LexicalBlock, 1, 2), + CASE_ILLII(LexicalBlock, 42, 43), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugLexicalBlockDiscriminator, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILI(LexicalBlockDiscriminator, 1), + CASE_ILI(LexicalBlockDiscriminator, 42), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugScope, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_I(Scope), + CASE_II(Scope), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugNoScope, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_0(NoScope), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInlinedAt, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_LII(InlinedAt, 1), + CASE_LII(InlinedAt, 42), + }))); + +// OpenCL.DebugInfo.100 4.8 Local Variables + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugLocalVariable, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIF(LocalVariable, 1, 2, "FlagIsPrivate", + OpenCLDebugInfo100FlagIsPrivate), + CASE_IIILLIF(LocalVariable, 4, 5, "FlagIsProtected", + OpenCLDebugInfo100FlagIsProtected), + CASE_IIILLIFL(LocalVariable, 9, 99, "FlagIsProtected", + OpenCLDebugInfo100FlagIsProtected, 195), + CASE_IIILLIFL(LocalVariable, 19, 199, "FlagIsPrivate", + OpenCLDebugInfo100FlagIsPrivate, 195), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugInlinedVariable, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_II(InlinedVariable), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugDeclare, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_III(Declare), + }))); + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugDebugValue, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIII(Value), + CASE_IIIII(Value), + CASE_IIIIII(Value), + // Test up to 3 id parameters. We can always try more. + CASE_IIIIIII(Value), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugOperation, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_E(Operation, Deref), + CASE_E(Operation, Plus), + CASE_E(Operation, Minus), + CASE_EL(Operation, PlusUconst, 1), + CASE_EL(Operation, PlusUconst, 42), + CASE_ELL(Operation, BitPiece, 1, 2), + CASE_ELL(Operation, BitPiece, 4, 5), + CASE_E(Operation, Swap), + CASE_E(Operation, Xderef), + CASE_E(Operation, StackValue), + CASE_EL(Operation, Constu, 1), + CASE_EL(Operation, Constu, 42), + CASE_ELL(Operation, Fragment, 100, 200), + CASE_ELL(Operation, Fragment, 8, 9), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugDebugExpression, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_0(Expression), + CASE_I(Expression), + CASE_II(Expression), + CASE_III(Expression), + CASE_IIII(Expression), + CASE_IIIII(Expression), + CASE_IIIIII(Expression), + CASE_IIIIIII(Expression), + }))); + +// OpenCL.DebugInfo.100 4.9 Macros + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugMacroDef, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILI(MacroDef, 1), + CASE_ILI(MacroDef, 42), + CASE_ILII(MacroDef, 1), + CASE_ILII(MacroDef, 42), + }))); + +INSTANTIATE_TEST_SUITE_P(OpenCLDebugInfo100DebugMacroUndef, + ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILI(MacroUndef, 1), + CASE_ILI(MacroUndef, 42), + }))); + +// OpenCL.DebugInfo.100 4.10 Imported Entities + +INSTANTIATE_TEST_SUITE_P( + OpenCLDebugInfo100DebugImportedEntity, ExtInstCLDebugInfo100RoundTripTest, + ::testing::ValuesIn(std::vector({ + // ID Name + // Literal Tag + // ID Source + // ID Entity + // Literal Number Line + // Literal Number Column + // ID Parent + CASE_IEIILLI(ImportedEntity, ImportedModule, 67, 68), + CASE_IEIILLI(ImportedEntity, ImportedDeclaration, 42, 43), + }))); + +#undef EPREFIX +#undef CASE_0 +#undef CASE_ILL +#undef CASE_IL +#undef CASE_I +#undef CASE_II +#undef CASE_III +#undef CASE_IIII +#undef CASE_IIIII +#undef CASE_IIIIII +#undef CASE_IIIIIII +#undef CASE_IIILLI +#undef CASE_IIILLIL +#undef CASE_IE +#undef CASE_IEIILLI +#undef CASE_IIE +#undef CASE_ISF +#undef CASE_LII +#undef CASE_LLIe +#undef CASE_ILI +#undef CASE_ILII +#undef CASE_ILLII +#undef CASE_IIILLIF +#undef CASE_IIILLIFL +#undef CASE_IIILLIIF +#undef CASE_IIILLIIFII +#undef CASE_IIILLIIFIIII +#undef CASE_IIILLIIFIIIIII +#undef CASE_IEILLIIIF +#undef CASE_IEILLIIIFI +#undef CASE_IEILLIIIFII +#undef CASE_IEILLIIIFIII +#undef CASE_IEILLIIIFIIII +#undef CASE_IIILLIIIF +#undef CASE_IIILLIIIFI +#undef CASE_IIIIF +#undef CASE_IIILL +#undef CASE_IIIILL +#undef CASE_IILLI +#undef CASE_IILLII +#undef CASE_IILLIII +#undef CASE_IILLIIII +#undef CASE_IIILLIIFLI +#undef CASE_IIILLIIFLII +#undef CASE_E +#undef CASE_EI +#undef CASE_EII +#undef CASE_EIII +#undef CASE_EIIII +#undef CASE_EIIIII +#undef CASE_EL +#undef CASE_ELL + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/ext_inst.debuginfo_test.cpp b/third_party/spirv-tools/test/ext_inst.debuginfo_test.cpp new file mode 100644 index 0000000..9090c24 --- /dev/null +++ b/third_party/spirv-tools/test/ext_inst.debuginfo_test.cpp @@ -0,0 +1,815 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "DebugInfo.h" +#include "gmock/gmock.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +// This file tests the correctness of encoding and decoding of instructions +// involving the DebugInfo extended instruction set. +// Semantic correctness should be the responsibility of validator. +// +// See https://www.khronos.org/registry/spir-v/specs/1.0/DebugInfo.html + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using utils::MakeVector; +using testing::Eq; + +struct InstructionCase { + uint32_t opcode; + std::string name; + std::string operands; + std::vector expected_operands; +}; + +using ExtInstDebugInfoRoundTripTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; +using ExtInstDebugInfoRoundTripTestExplicit = spvtest::TextToBinaryTest; + +TEST_P(ExtInstDebugInfoRoundTripTest, ParameterizedExtInst) { + const std::string input = + "%1 = OpExtInstImport \"DebugInfo\"\n" + "%3 = OpExtInst %2 %1 " + + GetParam().name + GetParam().operands + "\n"; + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(input), + Eq(Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode}, + GetParam().expected_operands)}))) + << input; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input; +} + +#define CASE_0(Enum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, "", {} \ + } + +#define CASE_ILL(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " " #L1, { \ + 4, L0, L1 \ + } \ + } + +#define CASE_IL(Enum, L0) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0, { 4, L0 } \ + } + +#define CASE_I(Enum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4", { 4 } \ + } + +#define CASE_II(Enum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5", { 4, 5 } \ + } + +#define CASE_III(Enum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6", { 4, 5, 6 } \ + } + +#define CASE_IIII(Enum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7", { \ + 4, 5, 6, 7 \ + } \ + } + +#define CASE_IIIII(Enum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8", { \ + 4, 5, 6, 7, 8 \ + } \ + } + +#define CASE_IIIIII(Enum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8 %9", { \ + 4, 5, 6, 7, 8, 9 \ + } \ + } + +#define CASE_IIIIIII(Enum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 %8 %9 %10", { \ + 4, 5, 6, 7, 8, 9, 10 \ + } \ + } + +#define CASE_IIILLI(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7", { \ + 4, 5, 6, L0, L1, 7 \ + } \ + } + +#define CASE_IIILLIL(Enum, L0, L1, L2) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 " #L2, { \ + 4, 5, 6, L0, L1, 7, L2 \ + } \ + } + +#define CASE_IE(Enum, E0) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #E0, { \ + 4, uint32_t(DebugInfo##E0) \ + } \ + } + +#define CASE_IIE(Enum, E0) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 " #E0, { \ + 4, 5, uint32_t(DebugInfo##E0) \ + } \ + } + +#define CASE_ISF(Enum, S0, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #S0 " " Fstr, { \ + 4, uint32_t(SpvStorageClass##S0), Fnum \ + } \ + } + +#define CASE_LII(Enum, L0) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #L0 " %4 %5", { \ + L0, 4, 5 \ + } \ + } + +#define CASE_ILI(Enum, L0) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " %5", { \ + 4, L0, 5 \ + } \ + } + +#define CASE_ILII(Enum, L0) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 " #L0 " %5 %6", { \ + 4, L0, 5, 6 \ + } \ + } + +#define CASE_ILLII(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 " #L0 " " #L1 " %5 %6", { \ + 4, L0, L1, 5, 6 \ + } \ + } + +#define CASE_IIILLIIF(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr, { \ + 4, 5, 6, L0, L1, 7, 8, Fnum \ + } \ + } + +#define CASE_IIILLIIFII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10 \ + } \ + } + +#define CASE_IIILLIIFIIII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12 \ + } \ + } + +#define CASE_IIILLIIFIIIIII(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " %9 %10 %11 %12 %13 %14", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, 9, 10, 11, 12, 13, 14 \ + } \ + } + +#define CASE_IEILLIIF(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr, { \ + 4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum \ + } \ + } + +#define CASE_IEILLIIFI(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8", { \ + 4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8 \ + } \ + } + +#define CASE_IEILLIIFII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9", { \ + 4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9 \ + } \ + } + +#define CASE_IEILLIIFIII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9 %10", { \ + 4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9, 10 \ + } \ + } + +#define CASE_IEILLIIFIIII(Enum, E0, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 " #E0 " %5 " #L0 " " #L1 " %6 %7 " Fstr " %8 %9 %10 %11", { \ + 4, uint32_t(DebugInfo##E0), 5, L0, L1, 6, 7, Fnum, 8, 9, 10, 11 \ + } \ + } + +#define CASE_IIILLIIIF(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr, { \ + 4, 5, 6, L0, L1, 7, 8, 9, Fnum \ + } \ + } + +#define CASE_IIILLIIIFI(Enum, L0, L1, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 %9 " Fstr " %10", { \ + 4, 5, 6, L0, L1, 7, 8, 9, Fnum, 10 \ + } \ + } + +#define CASE_IIIIF(Enum, Fstr, Fnum) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 %7 " Fstr, { \ + 4, 5, 6, 7, Fnum \ + } \ + } + +#define CASE_IIILL(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " %4 %5 %6 " #L0 " " #L1, { \ + 4, 5, 6, L0, L1 \ + } \ + } + +#define CASE_IIIILL(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 %7 " #L0 " " #L1, { \ + 4, 5, 6, 7, L0, L1 \ + } \ + } + +#define CASE_IILLI(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 " #L0 " " #L1 " %6", { \ + 4, 5, L0, L1, 6 \ + } \ + } + +#define CASE_IILLII(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7", { \ + 4, 5, L0, L1, 6, 7 \ + } \ + } + +#define CASE_IILLIII(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7 %8", { \ + 4, 5, L0, L1, 6, 7, 8 \ + } \ + } + +#define CASE_IILLIIII(Enum, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 " #L0 " " #L1 " %6 %7 %8 %9", { \ + 4, 5, L0, L1, 6, 7, 8, 9 \ + } \ + } + +#define CASE_IIILLIIFLI(Enum, L0, L1, Fstr, Fnum, L2) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9 \ + } \ + } + +#define CASE_IIILLIIFLII(Enum, L0, L1, Fstr, Fnum, L2) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, \ + " %4 %5 %6 " #L0 " " #L1 " %7 %8 " Fstr " " #L2 " %9 %10", { \ + 4, 5, 6, L0, L1, 7, 8, Fnum, L2, 9, 10 \ + } \ + } + +#define CASE_E(Enum, E0) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0, { \ + uint32_t(DebugInfo##E0) \ + } \ + } + +#define CASE_EL(Enum, E0, L0) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0 " " #L0, { \ + uint32_t(DebugInfo##E0), L0 \ + } \ + } + +#define CASE_ELL(Enum, E0, L0, L1) \ + { \ + uint32_t(DebugInfoDebug##Enum), "Debug" #Enum, " " #E0 " " #L0 " " #L1, { \ + uint32_t(DebugInfo##E0), L0, L1 \ + } \ + } + +// DebugInfo 4.1 Absent Debugging Information +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugInfoNone, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_0(InfoNone), // enum value 0 + }))); + +// DebugInfo 4.2 Compilation Unit +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugCompilationUnit, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILL(CompilationUnit, 100, 42), + }))); + +// DebugInfo 4.3 Type instructions +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeBasic, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIE(TypeBasic, Unspecified), + CASE_IIE(TypeBasic, Address), + CASE_IIE(TypeBasic, Boolean), + CASE_IIE(TypeBasic, Float), + CASE_IIE(TypeBasic, Signed), + CASE_IIE(TypeBasic, SignedChar), + CASE_IIE(TypeBasic, Unsigned), + CASE_IIE(TypeBasic, UnsignedChar), + }))); + +// The FlagIsPublic is value is (1 << 0) | (1 << 2) which is the same +// as the bitwise-OR of FlagIsProtected and FlagIsPrivate. +// The disassembler will emit the compound expression instead. +// There is no simple fix for this. This enum is not really a mask +// for the bottom two bits. +TEST_F(ExtInstDebugInfoRoundTripTestExplicit, FlagIsPublic) { + const std::string prefix = + "%1 = OpExtInstImport \"DebugInfo\"\n" + "%3 = OpExtInst %2 %1 DebugTypePointer %4 Private "; + const std::string input = prefix + "FlagIsPublic\n"; + const std::string expected = prefix + "FlagIsProtected|FlagIsPrivate\n"; + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(input), + Eq(Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("DebugInfo")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, DebugInfoDebugTypePointer, 4, + uint32_t(SpvStorageClassPrivate), + DebugInfoFlagIsPublic})}))) + << input; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), Eq(expected)) << input; +} + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugTypePointer, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + + //// Use each flag independently. + CASE_ISF(TypePointer, Private, "FlagIsProtected", + uint32_t(DebugInfoFlagIsProtected)), + CASE_ISF(TypePointer, Private, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + + // FlagIsPublic is tested above. + + CASE_ISF(TypePointer, Private, "FlagIsLocal", + uint32_t(DebugInfoFlagIsLocal)), + CASE_ISF(TypePointer, Private, "FlagIsDefinition", + uint32_t(DebugInfoFlagIsDefinition)), + CASE_ISF(TypePointer, Private, "FlagFwdDecl", + uint32_t(DebugInfoFlagFwdDecl)), + CASE_ISF(TypePointer, Private, "FlagArtificial", + uint32_t(DebugInfoFlagArtificial)), + CASE_ISF(TypePointer, Private, "FlagExplicit", + uint32_t(DebugInfoFlagExplicit)), + CASE_ISF(TypePointer, Private, "FlagPrototyped", + uint32_t(DebugInfoFlagPrototyped)), + CASE_ISF(TypePointer, Private, "FlagObjectPointer", + uint32_t(DebugInfoFlagObjectPointer)), + CASE_ISF(TypePointer, Private, "FlagStaticMember", + uint32_t(DebugInfoFlagStaticMember)), + CASE_ISF(TypePointer, Private, "FlagIndirectVariable", + uint32_t(DebugInfoFlagIndirectVariable)), + CASE_ISF(TypePointer, Private, "FlagLValueReference", + uint32_t(DebugInfoFlagLValueReference)), + CASE_ISF(TypePointer, Private, "FlagIsOptimized", + uint32_t(DebugInfoFlagIsOptimized)), + + //// Use flags in combination, and try different storage classes. + CASE_ISF(TypePointer, Function, "FlagIsProtected|FlagIsPrivate", + uint32_t(DebugInfoFlagIsProtected) | + uint32_t(DebugInfoFlagIsPrivate)), + CASE_ISF( + TypePointer, Workgroup, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) | + uint32_t(DebugInfoFlagIndirectVariable) | + uint32_t(DebugInfoFlagIsOptimized)), + + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeQualifier, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IE(TypeQualifier, ConstType), + CASE_IE(TypeQualifier, VolatileType), + CASE_IE(TypeQualifier, RestrictType), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeArray, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_II(TypeArray), + CASE_III(TypeArray), + CASE_IIII(TypeArray), + CASE_IIIII(TypeArray), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeVector, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IL(TypeVector, 2), + CASE_IL(TypeVector, 3), + CASE_IL(TypeVector, 4), + CASE_IL(TypeVector, 16), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypedef, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLI(Typedef, 12, 13), + CASE_IIILLI(Typedef, 14, 99), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeFunction, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_I(TypeFunction), + CASE_II(TypeFunction), + CASE_III(TypeFunction), + CASE_IIII(TypeFunction), + CASE_IIIII(TypeFunction), + }))); + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugTypeEnum, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIFII( + TypeEnum, 12, 13, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) | + uint32_t(DebugInfoFlagIndirectVariable) | + uint32_t(DebugInfoFlagIsOptimized)), + CASE_IIILLIIFIIII(TypeEnum, 17, 18, "FlagStaticMember", + uint32_t(DebugInfoFlagStaticMember)), + CASE_IIILLIIFIIIIII(TypeEnum, 99, 1, "FlagStaticMember", + uint32_t(DebugInfoFlagStaticMember)), + }))); + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugTypeComposite, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IEILLIIF( + TypeComposite, Class, 12, 13, + "FlagIsPrivate|FlagFwdDecl|FlagIndirectVariable|FlagIsOptimized", + uint32_t(DebugInfoFlagIsPrivate) | uint32_t(DebugInfoFlagFwdDecl) | + uint32_t(DebugInfoFlagIndirectVariable) | + uint32_t(DebugInfoFlagIsOptimized)), + // Cover all tag values: Class, Structure, Union + CASE_IEILLIIF(TypeComposite, Class, 12, 13, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + CASE_IEILLIIF(TypeComposite, Structure, 12, 13, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + CASE_IEILLIIF(TypeComposite, Union, 12, 13, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + // Now add members + CASE_IEILLIIFI(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + CASE_IEILLIIFII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + CASE_IEILLIIFIII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + CASE_IEILLIIFIIII(TypeComposite, Class, 9, 10, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + }))); + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugTypeMember, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIIF(TypeMember, 12, 13, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + CASE_IIILLIIIF(TypeMember, 99, 100, "FlagIsPrivate|FlagFwdDecl", + uint32_t(DebugInfoFlagIsPrivate) | + uint32_t(DebugInfoFlagFwdDecl)), + // Add the optional Id argument. + CASE_IIILLIIIFI(TypeMember, 12, 13, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + }))); + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugTypeInheritance, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIIIF(TypeInheritance, "FlagIsPrivate", + uint32_t(DebugInfoFlagIsPrivate)), + CASE_IIIIF(TypeInheritance, "FlagIsPrivate|FlagFwdDecl", + uint32_t(DebugInfoFlagIsPrivate) | + uint32_t(DebugInfoFlagFwdDecl)), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypePtrToMember, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_II(TypePtrToMember), + }))); + +// DebugInfo 4.4 Templates + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeTemplate, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_II(TypeTemplate), + CASE_III(TypeTemplate), + CASE_IIII(TypeTemplate), + CASE_IIIII(TypeTemplate), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeTemplateParameter, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIIILL(TypeTemplateParameter, 1, 2), + CASE_IIIILL(TypeTemplateParameter, 99, 102), + CASE_IIIILL(TypeTemplateParameter, 10, 7), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeTemplateTemplateParameter, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILL(TypeTemplateTemplateParameter, 1, 2), + CASE_IIILL(TypeTemplateTemplateParameter, 99, 102), + CASE_IIILL(TypeTemplateTemplateParameter, 10, 7), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugTypeTemplateParameterPack, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IILLI(TypeTemplateParameterPack, 1, 2), + CASE_IILLII(TypeTemplateParameterPack, 99, 102), + CASE_IILLIII(TypeTemplateParameterPack, 10, 7), + CASE_IILLIIII(TypeTemplateParameterPack, 10, 7), + }))); + +// DebugInfo 4.5 Global Variables + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugGlobalVariable, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIIF(GlobalVariable, 1, 2, "FlagIsOptimized", + uint32_t(DebugInfoFlagIsOptimized)), + CASE_IIILLIIIF(GlobalVariable, 42, 43, "FlagIsOptimized", + uint32_t(DebugInfoFlagIsOptimized)), + CASE_IIILLIIIFI(GlobalVariable, 1, 2, "FlagIsOptimized", + uint32_t(DebugInfoFlagIsOptimized)), + CASE_IIILLIIIFI(GlobalVariable, 42, 43, "FlagIsOptimized", + uint32_t(DebugInfoFlagIsOptimized)), + }))); + +// DebugInfo 4.6 Functions + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugFunctionDeclaration, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIF(FunctionDeclaration, 1, 2, "FlagIsOptimized", + uint32_t(DebugInfoFlagIsOptimized)), + CASE_IIILLIIF(FunctionDeclaration, 42, 43, "FlagFwdDecl", + uint32_t(DebugInfoFlagFwdDecl)), + }))); + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugFunction, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLIIFLI(Function, 1, 2, "FlagIsOptimized", + uint32_t(DebugInfoFlagIsOptimized), 3), + CASE_IIILLIIFLI(Function, 42, 43, "FlagFwdDecl", + uint32_t(DebugInfoFlagFwdDecl), 44), + // Add the optional declaration Id. + CASE_IIILLIIFLII(Function, 1, 2, "FlagIsOptimized", + uint32_t(DebugInfoFlagIsOptimized), 3), + CASE_IIILLIIFLII(Function, 42, 43, "FlagFwdDecl", + uint32_t(DebugInfoFlagFwdDecl), 44), + }))); + +// DebugInfo 4.7 Local Information + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugLexicalBlock, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILLII(LexicalBlock, 1, 2), + CASE_ILLII(LexicalBlock, 42, 43), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugLexicalBlockDiscriminator, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILI(LexicalBlockDiscriminator, 1), + CASE_ILI(LexicalBlockDiscriminator, 42), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugScope, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_I(Scope), + CASE_II(Scope), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugNoScope, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_0(NoScope), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugInlinedAt, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_LII(InlinedAt, 1), + CASE_LII(InlinedAt, 42), + }))); + +// DebugInfo 4.8 Local Variables + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugLocalVariable, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_IIILLI(LocalVariable, 1, 2), + CASE_IIILLI(LocalVariable, 42, 43), + CASE_IIILLIL(LocalVariable, 1, 2, 3), + CASE_IIILLIL(LocalVariable, 42, 43, 44), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugInlinedVariable, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_II(InlinedVariable), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugDebugDeclare, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_III(Declare), + }))); + +INSTANTIATE_TEST_SUITE_P( + DebugInfoDebugDebugValue, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_III(Value), + CASE_IIII(Value), + CASE_IIIII(Value), + CASE_IIIIII(Value), + // Test up to 4 id parameters. We can always try more. + CASE_IIIIIII(Value), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugDebugOperation, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_E(Operation, Deref), + CASE_E(Operation, Plus), + CASE_E(Operation, Minus), + CASE_EL(Operation, PlusUconst, 1), + CASE_EL(Operation, PlusUconst, 42), + CASE_ELL(Operation, BitPiece, 1, 2), + CASE_ELL(Operation, BitPiece, 4, 5), + CASE_E(Operation, Swap), + CASE_E(Operation, Xderef), + CASE_E(Operation, StackValue), + CASE_EL(Operation, Constu, 1), + CASE_EL(Operation, Constu, 42), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugDebugExpression, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_0(Expression), + CASE_I(Expression), + CASE_II(Expression), + CASE_III(Expression), + CASE_IIII(Expression), + CASE_IIIII(Expression), + CASE_IIIIII(Expression), + CASE_IIIIIII(Expression), + }))); + +// DebugInfo 4.9 Macros + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugMacroDef, ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILI(MacroDef, 1), + CASE_ILI(MacroDef, 42), + CASE_ILII(MacroDef, 1), + CASE_ILII(MacroDef, 42), + }))); + +INSTANTIATE_TEST_SUITE_P(DebugInfoDebugMacroUndef, + ExtInstDebugInfoRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE_ILI(MacroUndef, 1), + CASE_ILI(MacroUndef, 42), + }))); + +#undef CASE_0 +#undef CASE_ILL +#undef CASE_IL +#undef CASE_I +#undef CASE_II +#undef CASE_III +#undef CASE_IIII +#undef CASE_IIIII +#undef CASE_IIIIII +#undef CASE_IIIIIII +#undef CASE_IIILLI +#undef CASE_IIILLIL +#undef CASE_IE +#undef CASE_IIE +#undef CASE_ISF +#undef CASE_LII +#undef CASE_ILI +#undef CASE_ILII +#undef CASE_ILLII +#undef CASE_IIILLIIF +#undef CASE_IIILLIIFII +#undef CASE_IIILLIIFIIII +#undef CASE_IIILLIIFIIIIII +#undef CASE_IEILLIIF +#undef CASE_IEILLIIFI +#undef CASE_IEILLIIFII +#undef CASE_IEILLIIFIII +#undef CASE_IEILLIIFIIII +#undef CASE_IIILLIIIF +#undef CASE_IIILLIIIFI +#undef CASE_IIIIF +#undef CASE_IIILL +#undef CASE_IIIILL +#undef CASE_IILLI +#undef CASE_IILLII +#undef CASE_IILLIII +#undef CASE_IILLIIII +#undef CASE_IIILLIIFLI +#undef CASE_IIILLIIFLII +#undef CASE_E +#undef CASE_EL +#undef CASE_ELL + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/ext_inst.glsl_test.cpp b/third_party/spirv-tools/test/ext_inst.glsl_test.cpp new file mode 100644 index 0000000..41d222f --- /dev/null +++ b/third_party/spirv-tools/test/ext_inst.glsl_test.cpp @@ -0,0 +1,204 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "source/latest_version_glsl_std_450_header.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +/// Context for an extended instruction. +/// +/// Information about a GLSL extended instruction (including its opname, return +/// type, etc.) and related instructions used to generate the return type and +/// constant as the operands. Used in generating extended instruction tests. +struct ExtInstContext { + const char* extInstOpName; + const char* extInstOperandVars; + /// The following fields are used to check the SPIR-V binary representation + /// of this instruction. + uint32_t extInstOpcode; ///< Opcode value for this extended instruction. + uint32_t extInstLength; ///< Wordcount of this extended instruction. + std::vector extInstOperandIds; ///< Ids for operands. +}; + +using ExtInstGLSLstd450RoundTripTest = ::testing::TestWithParam; + +TEST_P(ExtInstGLSLstd450RoundTripTest, ParameterizedExtInst) { + spv_context context = spvContextCreate(SPV_ENV_UNIVERSAL_1_0); + const std::string spirv = R"( +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical Simple +OpEntryPoint Vertex %2 "main" +%3 = OpTypeVoid +%4 = OpTypeFunction %3 +%2 = OpFunction %3 None %5 +%6 = OpLabel +%8 = OpExtInst %7 %1 )" + std::string(GetParam().extInstOpName) + + " " + GetParam().extInstOperandVars + R"( +OpReturn +OpFunctionEnd +)"; + const std::string spirv_header = + R"(; SPIR-V +; Version: 1.0 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 9 +; Schema: 0)"; + spv_binary binary = nullptr; + spv_diagnostic diagnostic; + spv_result_t error = spvTextToBinary(context, spirv.c_str(), spirv.size(), + &binary, &diagnostic); + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + ASSERT_EQ(SPV_SUCCESS, error) + << "Source was: " << std::endl + << spirv << std::endl + << "Test case for : " << GetParam().extInstOpName << std::endl; + } + + // Check we do have the extended instruction's corresponding binary code in + // the generated SPIR-V binary. + std::vector expected_contains( + {12 /*OpExtInst*/ | GetParam().extInstLength << 16, 7 /*return type*/, + 8 /*result id*/, 1 /*glsl450 import*/, GetParam().extInstOpcode}); + for (uint32_t operand : GetParam().extInstOperandIds) { + expected_contains.push_back(operand); + } + EXPECT_NE(binary->code + binary->wordCount, + std::search(binary->code, binary->code + binary->wordCount, + expected_contains.begin(), expected_contains.end())) + << "Cannot find\n" + << spvtest::WordVector(expected_contains).str() << "in\n" + << spvtest::WordVector(*binary).str(); + + // Check round trip gives the same text. + spv_text output_text = nullptr; + error = spvBinaryToText(context, binary->code, binary->wordCount, + SPV_BINARY_TO_TEXT_OPTION_NONE, &output_text, + &diagnostic); + + if (error) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + ASSERT_EQ(SPV_SUCCESS, error); + } + EXPECT_EQ(spirv_header + spirv, output_text->str); + spvTextDestroy(output_text); + spvBinaryDestroy(binary); + spvContextDestroy(context); +} + +INSTANTIATE_TEST_SUITE_P( + ExtInstParameters, ExtInstGLSLstd450RoundTripTest, + ::testing::ValuesIn(std::vector({ + // We are only testing the correctness of encoding and decoding here. + // Semantic correctness should be the responsibility of validator. So + // some of the instructions below have incorrect operand and/or return + // types, e.g, Modf, ModfStruct, etc. + {"Round", "%5", 1, 6, {5}}, + {"RoundEven", "%5", 2, 6, {5}}, + {"Trunc", "%5", 3, 6, {5}}, + {"FAbs", "%5", 4, 6, {5}}, + {"SAbs", "%5", 5, 6, {5}}, + {"FSign", "%5", 6, 6, {5}}, + {"SSign", "%5", 7, 6, {5}}, + {"Floor", "%5", 8, 6, {5}}, + {"Ceil", "%5", 9, 6, {5}}, + {"Fract", "%5", 10, 6, {5}}, + {"Radians", "%5", 11, 6, {5}}, + {"Degrees", "%5", 12, 6, {5}}, + {"Sin", "%5", 13, 6, {5}}, + {"Cos", "%5", 14, 6, {5}}, + {"Tan", "%5", 15, 6, {5}}, + {"Asin", "%5", 16, 6, {5}}, + {"Acos", "%5", 17, 6, {5}}, + {"Atan", "%5", 18, 6, {5}}, + {"Sinh", "%5", 19, 6, {5}}, + {"Cosh", "%5", 20, 6, {5}}, + {"Tanh", "%5", 21, 6, {5}}, + {"Asinh", "%5", 22, 6, {5}}, + {"Acosh", "%5", 23, 6, {5}}, + {"Atanh", "%5", 24, 6, {5}}, + {"Atan2", "%5 %5", 25, 7, {5, 5}}, + {"Pow", "%5 %5", 26, 7, {5, 5}}, + {"Exp", "%5", 27, 6, {5}}, + {"Log", "%5", 28, 6, {5}}, + {"Exp2", "%5", 29, 6, {5}}, + {"Log2", "%5", 30, 6, {5}}, + {"Sqrt", "%5", 31, 6, {5}}, + {"InverseSqrt", "%5", 32, 6, {5}}, + {"Determinant", "%5", 33, 6, {5}}, + {"MatrixInverse", "%5", 34, 6, {5}}, + {"Modf", "%5 %5", 35, 7, {5, 5}}, + {"ModfStruct", "%5", 36, 6, {5}}, + {"FMin", "%5 %5", 37, 7, {5, 5}}, + {"UMin", "%5 %5", 38, 7, {5, 5}}, + {"SMin", "%5 %5", 39, 7, {5, 5}}, + {"FMax", "%5 %5", 40, 7, {5, 5}}, + {"UMax", "%5 %5", 41, 7, {5, 5}}, + {"SMax", "%5 %5", 42, 7, {5, 5}}, + {"FClamp", "%5 %5 %5", 43, 8, {5, 5, 5}}, + {"UClamp", "%5 %5 %5", 44, 8, {5, 5, 5}}, + {"SClamp", "%5 %5 %5", 45, 8, {5, 5, 5}}, + {"FMix", "%5 %5 %5", 46, 8, {5, 5, 5}}, + {"IMix", "%5 %5 %5", 47, 8, {5, 5, 5}}, // Bug 15452. Reserved. + {"Step", "%5 %5", 48, 7, {5, 5}}, + {"SmoothStep", "%5 %5 %5", 49, 8, {5, 5, 5}}, + {"Fma", "%5 %5 %5", 50, 8, {5, 5, 5}}, + {"Frexp", "%5 %5", 51, 7, {5, 5}}, + {"FrexpStruct", "%5", 52, 6, {5}}, + {"Ldexp", "%5 %5", 53, 7, {5, 5}}, + {"PackSnorm4x8", "%5", 54, 6, {5}}, + {"PackUnorm4x8", "%5", 55, 6, {5}}, + {"PackSnorm2x16", "%5", 56, 6, {5}}, + {"PackUnorm2x16", "%5", 57, 6, {5}}, + {"PackHalf2x16", "%5", 58, 6, {5}}, + {"PackDouble2x32", "%5", 59, 6, {5}}, + {"UnpackSnorm2x16", "%5", 60, 6, {5}}, + {"UnpackUnorm2x16", "%5", 61, 6, {5}}, + {"UnpackHalf2x16", "%5", 62, 6, {5}}, + {"UnpackSnorm4x8", "%5", 63, 6, {5}}, + {"UnpackUnorm4x8", "%5", 64, 6, {5}}, + {"UnpackDouble2x32", "%5", 65, 6, {5}}, + {"Length", "%5", 66, 6, {5}}, + {"Distance", "%5 %5", 67, 7, {5, 5}}, + {"Cross", "%5 %5", 68, 7, {5, 5}}, + {"Normalize", "%5", 69, 6, {5}}, + // clang-format off + {"FaceForward", "%5 %5 %5", 70, 8, {5, 5, 5}}, + // clang-format on + {"Reflect", "%5 %5", 71, 7, {5, 5}}, + {"Refract", "%5 %5 %5", 72, 8, {5, 5, 5}}, + {"FindILsb", "%5", 73, 6, {5}}, + {"FindSMsb", "%5", 74, 6, {5}}, + {"FindUMsb", "%5", 75, 6, {5}}, + {"InterpolateAtCentroid", "%5", 76, 6, {5}}, + // clang-format off + {"InterpolateAtSample", "%5 %5", 77, 7, {5, 5}}, + {"InterpolateAtOffset", "%5 %5", 78, 7, {5, 5}}, + // clang-format on + {"NMin", "%5 %5", 79, 7, {5, 5}}, + {"NMax", "%5 %5", 80, 7, {5, 5}}, + {"NClamp", "%5 %5 %5", 81, 8, {5, 5, 5}}, + }))); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/ext_inst.non_semantic_test.cpp b/third_party/spirv-tools/test/ext_inst.non_semantic_test.cpp new file mode 100644 index 0000000..870684e --- /dev/null +++ b/third_party/spirv-tools/test/ext_inst.non_semantic_test.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Assembler tests for non-semantic extended instructions + +#include +#include + +#include "gmock/gmock.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +using ::testing::Eq; + +namespace spvtools { +namespace { + +using NonSemanticRoundTripTest = RoundTripTest; +using NonSemanticTextToBinaryTest = spvtest::TextToBinaryTest; + +TEST_F(NonSemanticRoundTripTest, NonSemanticInsts) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Testing.ExtInst" +%2 = OpTypeVoid +%3 = OpExtInst %2 %1 132384681 %2 +%4 = OpTypeInt 32 0 +%5 = OpConstant %4 123 +%6 = OpString "Test string" +%7 = OpExtInst %4 %1 82198732 %5 %6 +%8 = OpExtInstImport "NonSemantic.Testing.AnotherUnknownExtInstSet" +%9 = OpExtInst %4 %8 613874321 %7 %5 %6 +)"; + std::string disassembly = EncodeAndDecodeSuccessfully( + spirv, SPV_BINARY_TO_TEXT_OPTION_NONE, SPV_ENV_UNIVERSAL_1_0); + EXPECT_THAT(disassembly, Eq(spirv)); +} + +TEST_F(NonSemanticTextToBinaryTest, InvalidExtInstSetName) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic_Testing_ExtInst" +)"; + + EXPECT_THAT( + CompileFailure(spirv), + Eq("Invalid extended instruction import 'NonSemantic_Testing_ExtInst'")); +} + +TEST_F(NonSemanticTextToBinaryTest, NonSemanticIntParameter) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Testing.ExtInst" +%2 = OpTypeVoid +%3 = OpExtInst %2 %1 1 99999 +)"; + + EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %.")); +} + +TEST_F(NonSemanticTextToBinaryTest, NonSemanticFloatParameter) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Testing.ExtInst" +%2 = OpTypeVoid +%3 = OpExtInst %2 %1 1 3.141592 +)"; + + EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %.")); +} + +TEST_F(NonSemanticTextToBinaryTest, NonSemanticStringParameter) { + std::string spirv = R"(OpExtension "SPV_KHR_non_semantic_info" +%1 = OpExtInstImport "NonSemantic.Testing.ExtInst" +%2 = OpTypeVoid +%3 = OpExtInst %2 %1 1 "foobar" +)"; + + EXPECT_THAT(CompileFailure(spirv), Eq("Expected id to start with %.")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/ext_inst.opencl_test.cpp b/third_party/spirv-tools/test/ext_inst.opencl_test.cpp new file mode 100644 index 0000000..7547d92 --- /dev/null +++ b/third_party/spirv-tools/test/ext_inst.opencl_test.cpp @@ -0,0 +1,374 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "source/latest_version_opencl_std_header.h" +#include "source/util/string_utils.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using utils::MakeVector; +using spvtest::TextToBinaryTest; +using testing::Eq; + +struct InstructionCase { + uint32_t opcode; + std::string name; + std::string operands; + std::vector expected_operands; +}; + +using ExtInstOpenCLStdRoundTripTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(ExtInstOpenCLStdRoundTripTest, ParameterizedExtInst) { + // This example should not validate. + const std::string input = + "%1 = OpExtInstImport \"OpenCL.std\"\n" + "%3 = OpExtInst %2 %1 " + + GetParam().name + " " + GetParam().operands + "\n"; + // First make sure it assembles correctly. + EXPECT_THAT( + CompiledInstructions(input), + Eq(Concatenate( + {MakeInstruction(SpvOpExtInstImport, {1}, MakeVector("OpenCL.std")), + MakeInstruction(SpvOpExtInst, {2, 3, 1, GetParam().opcode}, + GetParam().expected_operands)}))) + << input; + // Now check the round trip through the disassembler. + EXPECT_THAT(EncodeAndDecodeSuccessfully(input), input) << input; +} + +#define CASE1(Enum, Name) \ + { \ + uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4", { 4 } \ + } +#define CASE2(Enum, Name) \ + { \ + uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5", { 4, 5 } \ + } +#define CASE3(Enum, Name) \ + { \ + uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6", { 4, 5, 6 } \ + } +#define CASE4(Enum, Name) \ + { \ + uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6 %7", { \ + 4, 5, 6, 7 \ + } \ + } +#define CASE2Lit(Enum, Name, LiteralNumber) \ + { \ + uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 " #LiteralNumber, { \ + 4, 5, LiteralNumber \ + } \ + } +#define CASE3Round(Enum, Name, Mode) \ + { \ + uint32_t(OpenCLLIB::Entrypoints::Enum), #Name, "%4 %5 %6 " #Mode, { \ + 4, 5, 6, uint32_t(SpvFPRoundingMode##Mode) \ + } \ + } + +// clang-format off +// OpenCL.std: 2.1 Math extended instructions +INSTANTIATE_TEST_SUITE_P( + OpenCLMath, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + // We are only testing the correctness of encoding and decoding here. + // Semantic correctness should be the responsibility of validator. + CASE1(Acos, acos), // enum value 0 + CASE1(Acosh, acosh), + CASE1(Acospi, acospi), + CASE1(Asin, asin), + CASE1(Asinh, asinh), + CASE1(Asinh, asinh), + CASE1(Asinpi, asinpi), + CASE1(Atan, atan), + CASE2(Atan2, atan2), + CASE1(Atanh, atanh), + CASE1(Atanpi, atanpi), + CASE2(Atan2pi, atan2pi), + CASE1(Cbrt, cbrt), + CASE1(Ceil, ceil), + CASE1(Ceil, ceil), + CASE2(Copysign, copysign), + CASE1(Cos, cos), + CASE1(Cosh, cosh), + CASE1(Cospi, cospi), + CASE1(Erfc, erfc), + CASE1(Erf, erf), + CASE1(Exp, exp), + CASE1(Exp2, exp2), + CASE1(Exp10, exp10), + CASE1(Expm1, expm1), + CASE1(Fabs, fabs), + CASE2(Fdim, fdim), + CASE1(Floor, floor), + CASE3(Fma, fma), + CASE2(Fmax, fmax), + CASE2(Fmin, fmin), + CASE2(Fmod, fmod), + CASE2(Fract, fract), + CASE2(Frexp, frexp), + CASE2(Hypot, hypot), + CASE1(Ilogb, ilogb), + CASE2(Ldexp, ldexp), + CASE1(Lgamma, lgamma), + CASE2(Lgamma_r, lgamma_r), + CASE1(Log, log), + CASE1(Log2, log2), + CASE1(Log10, log10), + CASE1(Log1p, log1p), + CASE3(Mad, mad), + CASE2(Maxmag, maxmag), + CASE2(Minmag, minmag), + CASE2(Modf, modf), + CASE1(Nan, nan), + CASE2(Nextafter, nextafter), + CASE2(Pow, pow), + CASE2(Pown, pown), + CASE2(Powr, powr), + CASE2(Remainder, remainder), + CASE3(Remquo, remquo), + CASE1(Rint, rint), + CASE2(Rootn, rootn), + CASE1(Round, round), + CASE1(Rsqrt, rsqrt), + CASE1(Sin, sin), + CASE2(Sincos, sincos), + CASE1(Sinh, sinh), + CASE1(Sinpi, sinpi), + CASE1(Sqrt, sqrt), + CASE1(Tan, tan), + CASE1(Tanh, tanh), + CASE1(Tanpi, tanpi), + CASE1(Tgamma, tgamma), + CASE1(Trunc, trunc), + CASE1(Half_cos, half_cos), + CASE2(Half_divide, half_divide), + CASE1(Half_exp, half_exp), + CASE1(Half_exp2, half_exp2), + CASE1(Half_exp10, half_exp10), + CASE1(Half_log, half_log), + CASE1(Half_log2, half_log2), + CASE1(Half_log10, half_log10), + CASE2(Half_powr, half_powr), + CASE1(Half_recip, half_recip), + CASE1(Half_rsqrt, half_rsqrt), + CASE1(Half_sin, half_sin), + CASE1(Half_sqrt, half_sqrt), + CASE1(Half_tan, half_tan), + CASE1(Native_cos, native_cos), + CASE2(Native_divide, native_divide), + CASE1(Native_exp, native_exp), + CASE1(Native_exp2, native_exp2), + CASE1(Native_exp10, native_exp10), + CASE1(Native_log, native_log), + CASE1(Native_log10, native_log10), + CASE2(Native_powr, native_powr), + CASE1(Native_recip, native_recip), + CASE1(Native_rsqrt, native_rsqrt), + CASE1(Native_sin, native_sin), + CASE1(Native_sqrt, native_sqrt), + CASE1(Native_tan, native_tan), // enum value 94 + }))); + +// OpenCL.std: 2.1 Integer instructions +INSTANTIATE_TEST_SUITE_P( + OpenCLInteger, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE1(SAbs, s_abs), // enum value 141 + CASE2(SAbs_diff, s_abs_diff), + CASE2(SAdd_sat, s_add_sat), + CASE2(UAdd_sat, u_add_sat), + CASE2(SHadd, s_hadd), + CASE2(UHadd, u_hadd), + CASE2(SRhadd, s_rhadd), + CASE2(SRhadd, s_rhadd), + CASE3(SClamp, s_clamp), + CASE3(UClamp, u_clamp), + CASE1(Clz, clz), + CASE1(Ctz, ctz), + CASE3(SMad_hi, s_mad_hi), + CASE3(UMad_sat, u_mad_sat), + CASE3(SMad_sat, s_mad_sat), + CASE2(SMax, s_max), + CASE2(UMax, u_max), + CASE2(SMin, s_min), + CASE2(UMin, u_min), + CASE2(SMul_hi, s_mul_hi), + CASE2(Rotate, rotate), + CASE2(SSub_sat, s_sub_sat), + CASE2(USub_sat, u_sub_sat), + CASE2(U_Upsample, u_upsample), + CASE2(S_Upsample, s_upsample), + CASE1(Popcount, popcount), + CASE3(SMad24, s_mad24), + CASE3(UMad24, u_mad24), + CASE2(SMul24, s_mul24), + CASE2(UMul24, u_mul24), // enum value 170 + CASE1(UAbs, u_abs), // enum value 201 + CASE2(UAbs_diff, u_abs_diff), + CASE2(UMul_hi, u_mul_hi), + CASE3(UMad_hi, u_mad_hi), // enum value 204 + }))); + +// OpenCL.std: 2.3 Common instrucitons +INSTANTIATE_TEST_SUITE_P( + OpenCLCommon, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE3(FClamp, fclamp), // enum value 95 + CASE1(Degrees, degrees), + CASE2(FMax_common, fmax_common), + CASE2(FMin_common, fmin_common), + CASE3(Mix, mix), + CASE1(Radians, radians), + CASE2(Step, step), + CASE3(Smoothstep, smoothstep), + CASE1(Sign, sign), // enum value 103 + }))); + +// OpenCL.std: 2.4 Geometric instructions +INSTANTIATE_TEST_SUITE_P( + OpenCLGeometric, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE2(Cross, cross), // enum value 104 + CASE2(Distance, distance), + CASE1(Length, length), + CASE1(Normalize, normalize), + CASE2(Fast_distance, fast_distance), + CASE1(Fast_length, fast_length), + CASE1(Fast_normalize, fast_normalize), // enum value 110 + }))); + +// OpenCL.std: 2.5 Relational instructions +INSTANTIATE_TEST_SUITE_P( + OpenCLRelational, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE3(Bitselect, bitselect), // enum value 186 + CASE3(Select, select), // enum value 187 + }))); + +// OpenCL.std: 2.6 Vector data load and store instructions +INSTANTIATE_TEST_SUITE_P( + OpenCLVectorLoadStore, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + // The last argument to Vloadn must be one of 2, 3, 4, 8, 16. + CASE2Lit(Vloadn, vloadn, 2), + CASE2Lit(Vloadn, vloadn, 3), + CASE2Lit(Vloadn, vloadn, 4), + CASE2Lit(Vloadn, vloadn, 8), + CASE2Lit(Vloadn, vloadn, 16), + CASE3(Vstoren, vstoren), + CASE2(Vload_half, vload_half), + CASE2Lit(Vload_halfn, vload_halfn, 2), + CASE2Lit(Vload_halfn, vload_halfn, 3), + CASE2Lit(Vload_halfn, vload_halfn, 4), + CASE2Lit(Vload_halfn, vload_halfn, 8), + CASE2Lit(Vload_halfn, vload_halfn, 16), + CASE3(Vstore_half, vstore_half), + // Try all the rounding modes. + CASE3Round(Vstore_half_r, vstore_half_r, RTE), + CASE3Round(Vstore_half_r, vstore_half_r, RTZ), + CASE3Round(Vstore_half_r, vstore_half_r, RTP), + CASE3Round(Vstore_half_r, vstore_half_r, RTN), + CASE3(Vstore_halfn, vstore_halfn), + CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTE), + CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTZ), + CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTP), + CASE3Round(Vstore_halfn_r, vstore_halfn_r, RTN), + CASE2Lit(Vloada_halfn, vloada_halfn, 2), + CASE2Lit(Vloada_halfn, vloada_halfn, 3), + CASE2Lit(Vloada_halfn, vloada_halfn, 4), + CASE2Lit(Vloada_halfn, vloada_halfn, 8), + CASE2Lit(Vloada_halfn, vloada_halfn, 16), + CASE3(Vstorea_halfn, vstorea_halfn), + CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTE), + CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTZ), + CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTP), + CASE3Round(Vstorea_halfn_r, vstorea_halfn_r, RTN), + }))); + +// OpenCL.std: 2.7 Miscellaneous vector instructions +INSTANTIATE_TEST_SUITE_P( + OpenCLMiscellaneousVector, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE2(Shuffle, shuffle), + CASE3(Shuffle2, shuffle2), + }))); + +// OpenCL.std: 2.8 Miscellaneous instructions + +#define PREFIX uint32_t(OpenCLLIB::Entrypoints::Printf), "printf" +INSTANTIATE_TEST_SUITE_P( + OpenCLMiscPrintf, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + // Printf is interesting because it takes a variable number of arguments. + // Start with zero optional arguments. + {PREFIX, "%4", {4}}, + {PREFIX, "%4 %5", {4, 5}}, + {PREFIX, "%4 %5 %6", {4, 5, 6}}, + {PREFIX, "%4 %5 %6 %7", {4, 5, 6, 7}}, + {PREFIX, "%4 %5 %6 %7 %8", {4, 5, 6, 7, 8}}, + {PREFIX, "%4 %5 %6 %7 %8 %9", {4, 5, 6, 7, 8, 9}}, + {PREFIX, "%4 %5 %6 %7 %8 %9 %10", {4, 5, 6, 7, 8, 9, 10}}, + {PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11", {4, 5, 6, 7, 8, 9, 10, 11}}, + {PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12", + {4, 5, 6, 7, 8, 9, 10, 11, 12}}, + {PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12 %13", + {4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {PREFIX, "%4 %5 %6 %7 %8 %9 %10 %11 %12 %13 %14", + {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}}, + }))); +#undef PREFIX + +INSTANTIATE_TEST_SUITE_P( + OpenCLMiscPrefetch, ExtInstOpenCLStdRoundTripTest, + ::testing::ValuesIn(std::vector({ + CASE2(Prefetch, prefetch), + }))); + +// OpenCL.std: 2.9.1 Image encoding +// No new instructions defined in this section. + +// OpenCL.std: 2.9.2 Sampler encoding +// No new instructions defined in this section. + +// OpenCL.std: 2.9.3 Image read +// No new instructions defined in this section. +// Use core instruction OpImageSampleExplicitLod instead. + +// OpenCL.std: 2.9.4 Image write +// No new instructions defined in this section. + +// clang-format on + +#undef CASE1 +#undef CASE2 +#undef CASE3 +#undef CASE4 +#undef CASE2Lit +#undef CASE3Round + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fix_word_test.cpp b/third_party/spirv-tools/test/fix_word_test.cpp new file mode 100644 index 0000000..b8c3a33 --- /dev/null +++ b/third_party/spirv-tools/test/fix_word_test.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +TEST(FixWord, Default) { + spv_endianness_t endian; + if (I32_ENDIAN_HOST == I32_ENDIAN_LITTLE) { + endian = SPV_ENDIANNESS_LITTLE; + } else { + endian = SPV_ENDIANNESS_BIG; + } + uint32_t word = 0x53780921; + ASSERT_EQ(word, spvFixWord(word, endian)); +} + +TEST(FixWord, Reorder) { + spv_endianness_t endian; + if (I32_ENDIAN_HOST == I32_ENDIAN_LITTLE) { + endian = SPV_ENDIANNESS_BIG; + } else { + endian = SPV_ENDIANNESS_LITTLE; + } + uint32_t word = 0x53780921; + uint32_t result = 0x21097853; + ASSERT_EQ(result, spvFixWord(word, endian)); +} + +TEST(FixDoubleWord, Default) { + spv_endianness_t endian = + (I32_ENDIAN_HOST == I32_ENDIAN_LITTLE ? SPV_ENDIANNESS_LITTLE + : SPV_ENDIANNESS_BIG); + uint32_t low = 0x53780921; + uint32_t high = 0xdeadbeef; + uint64_t result = 0xdeadbeef53780921; + ASSERT_EQ(result, spvFixDoubleWord(low, high, endian)); +} + +TEST(FixDoubleWord, Reorder) { + spv_endianness_t endian = + (I32_ENDIAN_HOST == I32_ENDIAN_LITTLE ? SPV_ENDIANNESS_BIG + : SPV_ENDIANNESS_LITTLE); + uint32_t low = 0x53780921; + uint32_t high = 0xdeadbeef; + uint64_t result = 0xefbeadde21097853; + ASSERT_EQ(result, spvFixDoubleWord(low, high, endian)); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/CMakeLists.txt b/third_party/spirv-tools/test/fuzz/CMakeLists.txt new file mode 100644 index 0000000..2e93293 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/CMakeLists.txt @@ -0,0 +1,138 @@ +# Copyright (c) 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if (${SPIRV_BUILD_FUZZER}) + + set(SOURCES + fuzz_test_util.h + + call_graph_test.cpp + comparator_deep_blocks_first_test.cpp + data_synonym_transformation_test.cpp + equivalence_relation_test.cpp + fact_manager/constant_uniform_facts_test.cpp + fact_manager/data_synonym_and_id_equation_facts_test.cpp + fact_manager/dead_block_facts_test.cpp + fact_manager/irrelevant_value_facts_test.cpp + fuzz_test_util.cpp + fuzzer_pass_add_opphi_synonyms_test.cpp + fuzzer_pass_construct_composites_test.cpp + fuzzer_pass_donate_modules_test.cpp + fuzzer_pass_outline_functions_test.cpp + instruction_descriptor_test.cpp + fuzzer_pass_test.cpp + replayer_test.cpp + shrinker_test.cpp + transformation_access_chain_test.cpp + transformation_add_bit_instruction_synonym_test.cpp + transformation_add_constant_boolean_test.cpp + transformation_add_constant_composite_test.cpp + transformation_add_constant_null_test.cpp + transformation_add_constant_scalar_test.cpp + transformation_add_copy_memory_test.cpp + transformation_add_dead_block_test.cpp + transformation_add_dead_break_test.cpp + transformation_add_dead_continue_test.cpp + transformation_add_early_terminator_wrapper_test.cpp + transformation_add_function_test.cpp + transformation_add_global_undef_test.cpp + transformation_add_global_variable_test.cpp + transformation_add_image_sample_unused_components_test.cpp + transformation_add_local_variable_test.cpp + transformation_add_loop_preheader_test.cpp + transformation_add_loop_to_create_int_constant_synonym_test.cpp + transformation_add_no_contraction_decoration_test.cpp + transformation_add_opphi_synonym_test.cpp + transformation_add_parameter_test.cpp + transformation_add_relaxed_decoration_test.cpp + transformation_add_synonym_test.cpp + transformation_add_type_array_test.cpp + transformation_add_type_boolean_test.cpp + transformation_add_type_float_test.cpp + transformation_add_type_function_test.cpp + transformation_add_type_int_test.cpp + transformation_add_type_matrix_test.cpp + transformation_add_type_pointer_test.cpp + transformation_add_type_struct_test.cpp + transformation_add_type_vector_test.cpp + transformation_adjust_branch_weights_test.cpp + transformation_composite_construct_test.cpp + transformation_composite_extract_test.cpp + transformation_composite_insert_test.cpp + transformation_compute_data_synonym_fact_closure_test.cpp + transformation_duplicate_region_with_selection_test.cpp + transformation_equation_instruction_test.cpp + transformation_expand_vector_reduction_test.cpp + transformation_flatten_conditional_branch_test.cpp + transformation_function_call_test.cpp + transformation_inline_function_test.cpp + transformation_invert_comparison_operator_test.cpp + transformation_load_test.cpp + transformation_make_vector_operation_dynamic_test.cpp + transformation_merge_blocks_test.cpp + transformation_merge_function_returns_test.cpp + transformation_move_block_down_test.cpp + transformation_move_instruction_down_test.cpp + transformation_mutate_pointer_test.cpp + transformation_outline_function_test.cpp + transformation_permute_function_parameters_test.cpp + transformation_permute_phi_operands_test.cpp + transformation_propagate_instruction_down_test.cpp + transformation_propagate_instruction_up_test.cpp + transformation_push_id_through_variable_test.cpp + transformation_replace_add_sub_mul_with_carrying_extended_test.cpp + transformation_replace_boolean_constant_with_constant_binary_test.cpp + transformation_replace_branch_from_dead_block_with_exit_test.cpp + transformation_replace_copy_object_with_store_load_test.cpp + transformation_replace_constant_with_uniform_test.cpp + transformation_replace_copy_memory_with_load_store_test.cpp + transformation_replace_id_with_synonym_test.cpp + transformation_replace_irrelevant_id_test.cpp + transformation_replace_linear_algebra_instruction_test.cpp + transformation_replace_load_store_with_copy_memory_test.cpp + transformation_replace_opphi_id_from_dead_predecessor_test.cpp + transformation_replace_opselect_with_conditional_branch_test.cpp + transformation_replace_parameter_with_global_test.cpp + transformation_replace_params_with_struct_test.cpp + transformation_set_function_control_test.cpp + transformation_set_loop_control_test.cpp + transformation_set_memory_operands_mask_test.cpp + transformation_set_selection_control_test.cpp + transformation_split_block_test.cpp + transformation_store_test.cpp + transformation_swap_commutable_operands_test.cpp + transformation_swap_conditional_branch_operands_test.cpp + transformation_toggle_access_chain_instruction_test.cpp + transformation_record_synonymous_constants_test.cpp + transformation_vector_shuffle_test.cpp + transformation_wrap_early_terminator_in_function_test.cpp + transformation_wrap_region_in_selection_test.cpp + uniform_buffer_element_descriptor_test.cpp) + + if (${SPIRV_ENABLE_LONG_FUZZER_TESTS}) + # These are long-running tests that depend on random seeds. We do not want + # to run them during regular whole-project CI because they may reveal + # spirv-fuzz bugs in changes that are totally unrelated to spirv-fuzz, + # which would be counfounding. Instead, they should be run regularly but + # separately. + set(SOURCES ${SOURCES} + fuzzer_replayer_test.cpp + fuzzer_shrinker_test.cpp) + endif() + + add_spvtools_unittest(TARGET fuzz + SRCS ${SOURCES} + LIBS SPIRV-Tools-fuzz + ) +endif() diff --git a/third_party/spirv-tools/test/fuzz/call_graph_test.cpp b/third_party/spirv-tools/test/fuzz/call_graph_test.cpp new file mode 100644 index 0000000..c9bd221 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/call_graph_test.cpp @@ -0,0 +1,378 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/call_graph.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +// The SPIR-V came from this GLSL, slightly modified +// (main is %2, A is %35, B is %48, C is %50, D is %61): +// +// #version 310 es +// +// int A (int x) { +// return x + 1; +// } +// +// void D() { +// } +// +// void C() { +// int x = 0; +// int y = 0; +// +// while (x < 10) { +// while (y < 10) { +// y = A(y); +// } +// x = A(x); +// } +// } +// +// void B () { +// int x = 0; +// int y = 0; +// +// while (x < 10) { +// D(); +// while (y < 10) { +// y = A(y); +// C(); +// } +// x++; +// } +// +// } +// +// void main() +// { +// int x = 0; +// int y = 0; +// int z = 0; +// +// while (x < 10) { +// while(y < 10) { +// y = A(x); +// while (z < 10) { +// z = A(z); +// } +// } +// x += 2; +// } +// +// B(); +// C(); +// } +std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypePointer Function %5 + %7 = OpTypeFunction %5 %6 + %8 = OpConstant %5 1 + %9 = OpConstant %5 0 + %10 = OpConstant %5 10 + %11 = OpTypeBool + %12 = OpConstant %5 2 + %2 = OpFunction %3 None %4 + %13 = OpLabel + %14 = OpVariable %6 Function + %15 = OpVariable %6 Function + %16 = OpVariable %6 Function + %17 = OpVariable %6 Function + %18 = OpVariable %6 Function + OpStore %14 %9 + OpStore %15 %9 + OpStore %16 %9 + OpBranch %19 + %19 = OpLabel + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + %23 = OpLoad %5 %14 + %24 = OpSLessThan %11 %23 %10 + OpBranchConditional %24 %25 %20 + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + %30 = OpLoad %5 %15 + %31 = OpSLessThan %11 %30 %10 + OpBranchConditional %31 %32 %27 + %32 = OpLabel + %33 = OpLoad %5 %14 + OpStore %17 %33 + %34 = OpFunctionCall %5 %35 %17 + OpStore %15 %34 + OpBranch %36 + %36 = OpLabel + OpLoopMerge %37 %38 None + OpBranch %39 + %39 = OpLabel + %40 = OpLoad %5 %16 + %41 = OpSLessThan %11 %40 %10 + OpBranchConditional %41 %42 %37 + %42 = OpLabel + %43 = OpLoad %5 %16 + OpStore %18 %43 + %44 = OpFunctionCall %5 %35 %18 + OpStore %16 %44 + OpBranch %38 + %38 = OpLabel + OpBranch %36 + %37 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranch %26 + %27 = OpLabel + %45 = OpLoad %5 %14 + %46 = OpIAdd %5 %45 %12 + OpStore %14 %46 + OpBranch %21 + %21 = OpLabel + OpBranch %19 + %20 = OpLabel + %47 = OpFunctionCall %3 %48 + %49 = OpFunctionCall %3 %50 + OpReturn + OpFunctionEnd + %35 = OpFunction %5 None %7 + %51 = OpFunctionParameter %6 + %52 = OpLabel + %53 = OpLoad %5 %51 + %54 = OpIAdd %5 %53 %8 + OpReturnValue %54 + OpFunctionEnd + %48 = OpFunction %3 None %4 + %55 = OpLabel + %56 = OpVariable %6 Function + %57 = OpVariable %6 Function + %58 = OpVariable %6 Function + OpStore %56 %9 + OpStore %57 %9 + OpBranch %59 + %59 = OpLabel + %60 = OpFunctionCall %3 %61 + OpLoopMerge %62 %63 None + OpBranch %64 + %64 = OpLabel + OpLoopMerge %65 %66 None + OpBranch %67 + %67 = OpLabel + %68 = OpLoad %5 %57 + %69 = OpSLessThan %11 %68 %10 + OpBranchConditional %69 %70 %65 + %70 = OpLabel + %71 = OpLoad %5 %57 + OpStore %58 %71 + %72 = OpFunctionCall %5 %35 %58 + OpStore %57 %72 + %73 = OpFunctionCall %3 %50 + OpBranch %66 + %66 = OpLabel + OpBranch %64 + %65 = OpLabel + %74 = OpLoad %5 %56 + %75 = OpIAdd %5 %74 %8 + OpStore %56 %75 + OpBranch %63 + %63 = OpLabel + %76 = OpLoad %5 %56 + %77 = OpSLessThan %11 %76 %10 + OpBranchConditional %77 %59 %62 + %62 = OpLabel + OpReturn + OpFunctionEnd + %50 = OpFunction %3 None %4 + %78 = OpLabel + %79 = OpVariable %6 Function + %80 = OpVariable %6 Function + %81 = OpVariable %6 Function + %82 = OpVariable %6 Function + OpStore %79 %9 + OpStore %80 %9 + OpBranch %83 + %83 = OpLabel + OpLoopMerge %84 %85 None + OpBranch %86 + %86 = OpLabel + %87 = OpLoad %5 %79 + %88 = OpSLessThan %11 %87 %10 + OpBranchConditional %88 %89 %84 + %89 = OpLabel + OpBranch %90 + %90 = OpLabel + OpLoopMerge %91 %92 None + OpBranch %93 + %93 = OpLabel + %94 = OpLoad %5 %80 + %95 = OpSLessThan %11 %94 %10 + OpBranchConditional %95 %96 %91 + %96 = OpLabel + %97 = OpLoad %5 %80 + OpStore %81 %97 + %98 = OpFunctionCall %5 %35 %81 + OpStore %80 %98 + OpBranch %92 + %92 = OpLabel + OpBranch %90 + %91 = OpLabel + %99 = OpLoad %5 %79 + OpStore %82 %99 + %100 = OpFunctionCall %5 %35 %82 + OpStore %79 %100 + OpBranch %85 + %85 = OpLabel + OpBranch %83 + %84 = OpLabel + OpReturn + OpFunctionEnd + %61 = OpFunction %3 None %4 + %101 = OpLabel + OpReturn + OpFunctionEnd +)"; + +// We have that: +// main calls: +// - A (maximum loop nesting depth of function call: 3) +// - B (0) +// - C (0) +// A calls nothing. +// B calls: +// - A (2) +// - C (2) +// - D (1) +// C calls: +// - A (2) +// D calls nothing. + +TEST(CallGraphTest, FunctionInDegree) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const auto graph = CallGraph(context.get()); + + const auto& function_in_degree = graph.GetFunctionInDegree(); + // Check the in-degrees of, in order: main, A, B, C, D. + ASSERT_EQ(function_in_degree.at(2), 0); + ASSERT_EQ(function_in_degree.at(35), 3); + ASSERT_EQ(function_in_degree.at(48), 1); + ASSERT_EQ(function_in_degree.at(50), 2); + ASSERT_EQ(function_in_degree.at(61), 1); +} + +TEST(CallGraphTest, DirectCallees) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const auto graph = CallGraph(context.get()); + + // Check the callee sets of, in order: main, A, B, C, D. + ASSERT_EQ(graph.GetDirectCallees(2), std::set({35, 48, 50})); + ASSERT_EQ(graph.GetDirectCallees(35), std::set({})); + ASSERT_EQ(graph.GetDirectCallees(48), std::set({35, 50, 61})); + ASSERT_EQ(graph.GetDirectCallees(50), std::set({35})); + ASSERT_EQ(graph.GetDirectCallees(61), std::set({})); +} + +TEST(CallGraphTest, IndirectCallees) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const auto graph = CallGraph(context.get()); + + // Check the callee sets of, in order: main, A, B, C, D. + ASSERT_EQ(graph.GetIndirectCallees(2), std::set({35, 48, 50, 61})); + ASSERT_EQ(graph.GetDirectCallees(35), std::set({})); + ASSERT_EQ(graph.GetDirectCallees(48), std::set({35, 50, 61})); + ASSERT_EQ(graph.GetDirectCallees(50), std::set({35})); + ASSERT_EQ(graph.GetDirectCallees(61), std::set({})); +} + +TEST(CallGraphTest, TopologicalOrder) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const auto graph = CallGraph(context.get()); + + const auto& topological_ordering = graph.GetFunctionsInTopologicalOrder(); + + // The possible topological orderings are: + // - main, B, D, C, A + // - main, B, C, D, A + // - main, B, C, A, D + ASSERT_TRUE( + topological_ordering == std::vector({2, 48, 61, 50, 35}) || + topological_ordering == std::vector({2, 48, 50, 61, 35}) || + topological_ordering == std::vector({2, 48, 50, 35, 61})); +} + +TEST(CallGraphTest, LoopNestingDepth) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const auto graph = CallGraph(context.get()); + + // Check the maximum loop nesting depth for function calls to, in order: + // main, A, B, C, D + ASSERT_EQ(graph.GetMaxCallNestingDepth(2), 0); + ASSERT_EQ(graph.GetMaxCallNestingDepth(35), 4); + ASSERT_EQ(graph.GetMaxCallNestingDepth(48), 0); + ASSERT_EQ(graph.GetMaxCallNestingDepth(50), 2); + ASSERT_EQ(graph.GetMaxCallNestingDepth(61), 1); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/comparator_deep_blocks_first_test.cpp b/third_party/spirv-tools/test/fuzz/comparator_deep_blocks_first_test.cpp new file mode 100644 index 0000000..d7dbde5 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/comparator_deep_blocks_first_test.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/comparator_deep_blocks_first.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/transformation_context.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 1 + %10 = OpConstant %7 10 + %11 = OpConstant %7 2 + %2 = OpFunction %3 None %4 + %12 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %6 %14 %15 + %14 = OpLabel + OpBranch %13 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpLoopMerge %17 %18 None + OpBranch %19 + %19 = OpLabel + OpBranchConditional %6 %20 %17 + %20 = OpLabel + OpSelectionMerge %21 None + OpBranchConditional %6 %22 %23 + %22 = OpLabel + OpBranch %21 + %23 = OpLabel + OpBranch %21 + %21 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %16 + %17 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + +TEST(ComparatorDeepBlocksFirstTest, Compare) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto is_deeper = ComparatorDeepBlocksFirst(context.get()); + + // The block ids and the corresponding depths are: + // 12, 13 -> depth 0 + // 14, 15, 16, 17 -> depth 1 + // 18, 19, 20, 21 -> depth 2 + // 22, 23 -> depth 3 + + // Perform some comparisons and check that they return true iff the first + // block is deeper than the second. + ASSERT_FALSE(is_deeper(12, 12)); + ASSERT_FALSE(is_deeper(12, 13)); + ASSERT_FALSE(is_deeper(12, 14)); + ASSERT_FALSE(is_deeper(12, 18)); + ASSERT_FALSE(is_deeper(12, 22)); + ASSERT_TRUE(is_deeper(14, 12)); + ASSERT_FALSE(is_deeper(14, 15)); + ASSERT_FALSE(is_deeper(15, 14)); + ASSERT_FALSE(is_deeper(14, 18)); + ASSERT_TRUE(is_deeper(18, 12)); + ASSERT_TRUE(is_deeper(18, 16)); +} + +TEST(ComparatorDeepBlocksFirstTest, Sort) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Check that, sorting using the comparator, the blocks are ordered from more + // deeply nested to less deeply nested. + // 17 has depth 1, 20 has depth 2, 13 has depth 0. + std::vector blocks = {context->get_instr_block(17), + context->get_instr_block(20), + context->get_instr_block(13)}; + + std::sort(blocks.begin(), blocks.end(), + ComparatorDeepBlocksFirst(context.get())); + + // Check that the blocks are in the correct order. + ASSERT_EQ(blocks[0]->id(), 20); + ASSERT_EQ(blocks[1]->id(), 17); + ASSERT_EQ(blocks[2]->id(), 13); +} +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/data_synonym_transformation_test.cpp b/third_party/spirv-tools/test/fuzz/data_synonym_transformation_test.cpp new file mode 100644 index 0000000..7e93f29 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/data_synonym_transformation_test.cpp @@ -0,0 +1,1250 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/data_descriptor.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_composite_extract.h" +#include "source/fuzz/transformation_replace_id_with_synonym.h" +#include "source/fuzz/transformation_vector_shuffle.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +// This file captures tests that check correctness of the collective use of a +// number of transformations that relate to data synonyms. + +protobufs::Fact MakeSynonymFact(uint32_t first_id, + const std::vector& first_indices, + uint32_t second_id, + const std::vector& second_indices) { + protobufs::FactDataSynonym data_synonym_fact; + *data_synonym_fact.mutable_data1() = + MakeDataDescriptor(first_id, first_indices); + *data_synonym_fact.mutable_data2() = + MakeDataDescriptor(second_id, second_indices); + protobufs::Fact result; + *result.mutable_data_synonym_fact() = data_synonym_fact; + return result; +} + +TEST(DataSynonymTransformationTest, ArrayCompositeSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "A" + OpName %20 "B" + OpName %31 "g" + OpName %35 "h" + OpDecorate %11 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 3 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 0 + %13 = OpConstant %6 3 + %14 = OpTypePointer Function %6 + %16 = OpTypeFloat 32 + %17 = OpConstant %7 4 + %18 = OpTypeArray %16 %17 + %19 = OpTypePointer Function %18 + %24 = OpTypePointer Function %16 + %28 = OpConstant %16 42 + %30 = OpConstant %6 2 + %34 = OpConstant %6 1 + %38 = OpConstant %6 42 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %20 = OpVariable %19 Function + %31 = OpVariable %24 Function + %35 = OpVariable %14 Function + %15 = OpAccessChain %14 %11 %12 + %21 = OpAccessChain %14 %11 %12 + %22 = OpLoad %6 %21 + %100 = OpCompositeConstruct %9 %12 %13 %22 + OpStore %15 %13 + %23 = OpConvertSToF %16 %22 + %25 = OpAccessChain %24 %20 %12 + OpStore %25 %23 + %26 = OpAccessChain %14 %11 %12 + %27 = OpLoad %6 %26 + %29 = OpAccessChain %24 %20 %27 + OpStore %29 %28 + %32 = OpLoad %16 %31 + %101 = OpCompositeConstruct %18 %28 %23 %32 %23 + %50 = OpCopyObject %16 %23 + %51 = OpCopyObject %16 %23 + %33 = OpAccessChain %24 %20 %30 + OpStore %33 %28 + OpStore %33 %32 + %36 = OpLoad %6 %35 + %37 = OpAccessChain %14 %11 %34 + OpStore %37 %36 + %39 = OpAccessChain %14 %11 %12 + %40 = OpLoad %6 %39 + %41 = OpIAdd %6 %38 %40 + %42 = OpAccessChain %14 %11 %30 + OpStore %42 %41 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(12, {}, 100, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(13, {}, 100, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(22, {}, 100, {2})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(28, {}, 101, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(23, {}, 101, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(32, {}, 101, {2})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(23, {}, 101, {3})); + + // Replace %12 with %100[0] in '%25 = OpAccessChain %24 %20 %12' + auto instruction_descriptor_1 = + MakeInstructionDescriptor(25, SpvOpAccessChain, 0); + auto good_extract_1 = + TransformationCompositeExtract(instruction_descriptor_1, 102, 100, {0}); + // Bad: id already in use + auto bad_extract_1 = TransformationCompositeExtract( + MakeInstructionDescriptor(25, SpvOpAccessChain, 0), 25, 100, {0}); + ASSERT_TRUE( + good_extract_1.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_extract_1.IsApplicable(context.get(), transformation_context)); + good_extract_1.Apply(context.get(), &transformation_context); + auto replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(12, instruction_descriptor_1, 1), 102); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %13 with %100[1] in 'OpStore %15 %13' + auto instruction_descriptor_2 = MakeInstructionDescriptor(100, SpvOpStore, 0); + auto good_extract_2 = + TransformationCompositeExtract(instruction_descriptor_2, 103, 100, {1}); + // No bad example provided here. + ASSERT_TRUE( + good_extract_2.IsApplicable(context.get(), transformation_context)); + good_extract_2.Apply(context.get(), &transformation_context); + auto replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(13, instruction_descriptor_2, 1), 103); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %22 with %100[2] in '%23 = OpConvertSToF %16 %22' + auto instruction_descriptor_3 = + MakeInstructionDescriptor(23, SpvOpConvertSToF, 0); + auto good_extract_3 = + TransformationCompositeExtract(instruction_descriptor_3, 104, 100, {2}); + ASSERT_TRUE( + good_extract_3.IsApplicable(context.get(), transformation_context)); + good_extract_3.Apply(context.get(), &transformation_context); + auto replacement_3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(22, instruction_descriptor_3, 0), 104); + // Bad: wrong input operand index + auto bad_replacement_3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(22, instruction_descriptor_3, 1), 104); + ASSERT_TRUE( + replacement_3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_replacement_3.IsApplicable(context.get(), transformation_context)); + replacement_3.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %28 with %101[0] in 'OpStore %33 %28' + auto instruction_descriptor_4 = MakeInstructionDescriptor(33, SpvOpStore, 0); + auto good_extract_4 = + TransformationCompositeExtract(instruction_descriptor_4, 105, 101, {0}); + // Bad: instruction descriptor does not identify an appropriate instruction + auto bad_extract_4 = TransformationCompositeExtract( + MakeInstructionDescriptor(33, SpvOpCopyObject, 0), 105, 101, {0}); + ASSERT_TRUE( + good_extract_4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_extract_4.IsApplicable(context.get(), transformation_context)); + good_extract_4.Apply(context.get(), &transformation_context); + auto replacement_4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(28, instruction_descriptor_4, 1), 105); + ASSERT_TRUE( + replacement_4.IsApplicable(context.get(), transformation_context)); + replacement_4.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %23 with %101[1] in '%50 = OpCopyObject %16 %23' + auto instruction_descriptor_5 = + MakeInstructionDescriptor(50, SpvOpCopyObject, 0); + auto good_extract_5 = + TransformationCompositeExtract(instruction_descriptor_5, 106, 101, {1}); + ASSERT_TRUE( + good_extract_5.IsApplicable(context.get(), transformation_context)); + good_extract_5.Apply(context.get(), &transformation_context); + auto replacement_5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(23, instruction_descriptor_5, 0), 106); + // Bad: wrong synonym fact being used + auto bad_replacement_5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(23, instruction_descriptor_5, 0), 105); + ASSERT_TRUE( + replacement_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_replacement_5.IsApplicable(context.get(), transformation_context)); + replacement_5.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %32 with %101[2] in 'OpStore %33 %32' + auto instruction_descriptor_6 = MakeInstructionDescriptor(33, SpvOpStore, 1); + auto good_extract_6 = + TransformationCompositeExtract(instruction_descriptor_6, 107, 101, {2}); + // Bad: id 1001 does not exist + auto bad_extract_6 = + TransformationCompositeExtract(instruction_descriptor_6, 107, 1001, {2}); + ASSERT_TRUE( + good_extract_6.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_extract_6.IsApplicable(context.get(), transformation_context)); + good_extract_6.Apply(context.get(), &transformation_context); + auto replacement_6 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(32, instruction_descriptor_6, 1), 107); + ASSERT_TRUE( + replacement_6.IsApplicable(context.get(), transformation_context)); + replacement_6.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %23 with %101[3] in '%51 = OpCopyObject %16 %23' + auto instruction_descriptor_7 = + MakeInstructionDescriptor(51, SpvOpCopyObject, 0); + auto good_extract_7 = + TransformationCompositeExtract(instruction_descriptor_7, 108, 101, {3}); + ASSERT_TRUE( + good_extract_7.IsApplicable(context.get(), transformation_context)); + good_extract_7.Apply(context.get(), &transformation_context); + auto replacement_7 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(23, instruction_descriptor_7, 0), 108); + // Bad: use id 0 is invalid + auto bad_replacement_7 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(0, instruction_descriptor_7, 0), 108); + ASSERT_TRUE( + replacement_7.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + bad_replacement_7.IsApplicable(context.get(), transformation_context)); + replacement_7.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "A" + OpName %20 "B" + OpName %31 "g" + OpName %35 "h" + OpDecorate %11 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %35 RelaxedPrecision + OpDecorate %36 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 3 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 0 + %13 = OpConstant %6 3 + %14 = OpTypePointer Function %6 + %16 = OpTypeFloat 32 + %17 = OpConstant %7 4 + %18 = OpTypeArray %16 %17 + %19 = OpTypePointer Function %18 + %24 = OpTypePointer Function %16 + %28 = OpConstant %16 42 + %30 = OpConstant %6 2 + %34 = OpConstant %6 1 + %38 = OpConstant %6 42 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %20 = OpVariable %19 Function + %31 = OpVariable %24 Function + %35 = OpVariable %14 Function + %15 = OpAccessChain %14 %11 %12 + %21 = OpAccessChain %14 %11 %12 + %22 = OpLoad %6 %21 + %100 = OpCompositeConstruct %9 %12 %13 %22 + %103 = OpCompositeExtract %6 %100 1 + OpStore %15 %103 + %104 = OpCompositeExtract %6 %100 2 + %23 = OpConvertSToF %16 %104 + %102 = OpCompositeExtract %6 %100 0 + %25 = OpAccessChain %24 %20 %102 + OpStore %25 %23 + %26 = OpAccessChain %14 %11 %12 + %27 = OpLoad %6 %26 + %29 = OpAccessChain %24 %20 %27 + OpStore %29 %28 + %32 = OpLoad %16 %31 + %101 = OpCompositeConstruct %18 %28 %23 %32 %23 + %106 = OpCompositeExtract %16 %101 1 + %50 = OpCopyObject %16 %106 + %108 = OpCompositeExtract %16 %101 3 + %51 = OpCopyObject %16 %108 + %33 = OpAccessChain %24 %20 %30 + %105 = OpCompositeExtract %16 %101 0 + OpStore %33 %105 + %107 = OpCompositeExtract %16 %101 2 + OpStore %33 %107 + %36 = OpLoad %6 %35 + %37 = OpAccessChain %14 %11 %34 + OpStore %37 %36 + %39 = OpAccessChain %14 %11 %12 + %40 = OpLoad %6 %39 + %41 = OpIAdd %6 %38 %40 + %42 = OpAccessChain %14 %11 %30 + OpStore %42 %41 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(DataSynonymTransformationTest, MatrixCompositeSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "m" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %50 = OpUndef %7 + %8 = OpTypeMatrix %7 3 + %9 = OpTypePointer Function %8 + %11 = OpTypeInt 32 1 + %12 = OpConstant %11 0 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %13 %13 %13 %13 + %15 = OpTypePointer Function %7 + %17 = OpConstant %11 1 + %18 = OpConstant %6 2 + %19 = OpConstantComposite %7 %18 %18 %18 %18 + %21 = OpConstant %11 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + %16 = OpAccessChain %15 %10 %12 + OpStore %16 %14 + %20 = OpAccessChain %15 %10 %17 + OpStore %20 %19 + %22 = OpAccessChain %15 %10 %12 + %23 = OpLoad %7 %22 + %24 = OpAccessChain %15 %10 %17 + %25 = OpLoad %7 %24 + %100 = OpCompositeConstruct %8 %23 %25 %50 + %26 = OpFAdd %7 %23 %25 + %27 = OpAccessChain %15 %10 %21 + OpStore %27 %26 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(23, {}, 100, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(25, {}, 100, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(50, {}, 100, {2})); + + // Replace %23 with %100[0] in '%26 = OpFAdd %7 %23 %25' + auto instruction_descriptor_1 = MakeInstructionDescriptor(26, SpvOpFAdd, 0); + auto extract_1 = + TransformationCompositeExtract(instruction_descriptor_1, 101, 100, {0}); + ASSERT_TRUE(extract_1.IsApplicable(context.get(), transformation_context)); + extract_1.Apply(context.get(), &transformation_context); + auto replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(23, instruction_descriptor_1, 0), 101); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %25 with %100[1] in '%26 = OpFAdd %7 %23 %25' + auto instruction_descriptor_2 = MakeInstructionDescriptor(26, SpvOpFAdd, 0); + auto extract_2 = + TransformationCompositeExtract(instruction_descriptor_2, 102, 100, {1}); + ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context)); + extract_2.Apply(context.get(), &transformation_context); + auto replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(25, instruction_descriptor_2, 1), 102); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "m" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %50 = OpUndef %7 + %8 = OpTypeMatrix %7 3 + %9 = OpTypePointer Function %8 + %11 = OpTypeInt 32 1 + %12 = OpConstant %11 0 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %13 %13 %13 %13 + %15 = OpTypePointer Function %7 + %17 = OpConstant %11 1 + %18 = OpConstant %6 2 + %19 = OpConstantComposite %7 %18 %18 %18 %18 + %21 = OpConstant %11 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + %16 = OpAccessChain %15 %10 %12 + OpStore %16 %14 + %20 = OpAccessChain %15 %10 %17 + OpStore %20 %19 + %22 = OpAccessChain %15 %10 %12 + %23 = OpLoad %7 %22 + %24 = OpAccessChain %15 %10 %17 + %25 = OpLoad %7 %24 + %100 = OpCompositeConstruct %8 %23 %25 %50 + %101 = OpCompositeExtract %7 %100 0 + %102 = OpCompositeExtract %7 %100 1 + %26 = OpFAdd %7 %101 %102 + %27 = OpAccessChain %15 %10 %21 + OpStore %27 %26 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(DataSynonymTransformationTest, StructCompositeSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "Inner" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpName %11 "i1" + OpName %17 "i2" + OpName %31 "Point" + OpMemberName %31 0 "x" + OpMemberName %31 1 "y" + OpMemberName %31 2 "z" + OpName %32 "Outer" + OpMemberName %32 0 "c" + OpMemberName %32 1 "d" + OpName %34 "o1" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeVector %7 2 + %9 = OpTypeStruct %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %7 2 + %14 = OpConstant %7 3 + %15 = OpConstantComposite %8 %13 %14 + %16 = OpConstantComposite %9 %12 %15 + %18 = OpConstant %6 0 + %19 = OpTypePointer Function %6 + %24 = OpTypePointer Function %8 + %27 = OpConstant %7 4 + %31 = OpTypeStruct %7 %7 %7 + %32 = OpTypeStruct %9 %31 + %33 = OpTypePointer Function %32 + %36 = OpConstant %7 10 + %37 = OpTypeInt 32 0 + %38 = OpConstant %37 0 + %39 = OpTypePointer Function %7 + %42 = OpConstant %37 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %17 = OpVariable %10 Function + %34 = OpVariable %33 Function + %101 = OpCompositeConstruct %31 %27 %36 %27 + OpStore %11 %16 + %20 = OpAccessChain %19 %11 %18 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %21 %12 + %102 = OpCompositeConstruct %9 %22 %15 + %23 = OpAccessChain %19 %17 %18 + OpStore %23 %22 + %25 = OpAccessChain %24 %17 %12 + %26 = OpLoad %8 %25 + %28 = OpCompositeConstruct %8 %27 %27 + %29 = OpFAdd %8 %26 %28 + %30 = OpAccessChain %24 %17 %12 + OpStore %30 %29 + %35 = OpLoad %9 %11 + %40 = OpAccessChain %39 %11 %12 %38 + %41 = OpLoad %7 %40 + %43 = OpAccessChain %39 %11 %12 %42 + %44 = OpLoad %7 %43 + %45 = OpCompositeConstruct %31 %36 %41 %44 + %100 = OpCompositeConstruct %32 %16 %45 + %46 = OpCompositeConstruct %32 %35 %45 + OpStore %34 %46 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(16, {}, 100, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(45, {}, 100, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(27, {}, 101, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(36, {}, 101, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(27, {}, 101, {2})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(22, {}, 102, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(15, {}, 102, {1})); + + // Replace %45 with %100[1] in '%46 = OpCompositeConstruct %32 %35 %45' + auto instruction_descriptor_1 = + MakeInstructionDescriptor(46, SpvOpCompositeConstruct, 0); + auto extract_1 = + TransformationCompositeExtract(instruction_descriptor_1, 201, 100, {1}); + ASSERT_TRUE(extract_1.IsApplicable(context.get(), transformation_context)); + extract_1.Apply(context.get(), &transformation_context); + auto replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(45, instruction_descriptor_1, 1), 201); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace second occurrence of %27 with %101[0] in '%28 = + // OpCompositeConstruct %8 %27 %27' + auto instruction_descriptor_2 = + MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0); + auto extract_2 = + TransformationCompositeExtract(instruction_descriptor_2, 202, 101, {0}); + ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context)); + extract_2.Apply(context.get(), &transformation_context); + auto replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(27, instruction_descriptor_2, 1), 202); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %36 with %101[1] in '%45 = OpCompositeConstruct %31 %36 %41 %44' + auto instruction_descriptor_3 = + MakeInstructionDescriptor(45, SpvOpCompositeConstruct, 0); + auto extract_3 = + TransformationCompositeExtract(instruction_descriptor_3, 203, 101, {1}); + ASSERT_TRUE(extract_3.IsApplicable(context.get(), transformation_context)); + extract_3.Apply(context.get(), &transformation_context); + auto replacement_3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(36, instruction_descriptor_3, 0), 203); + ASSERT_TRUE( + replacement_3.IsApplicable(context.get(), transformation_context)); + replacement_3.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace first occurrence of %27 with %101[2] in '%28 = OpCompositeConstruct + // %8 %27 %27' + auto instruction_descriptor_4 = + MakeInstructionDescriptor(28, SpvOpCompositeConstruct, 0); + auto extract_4 = + TransformationCompositeExtract(instruction_descriptor_4, 204, 101, {2}); + ASSERT_TRUE(extract_4.IsApplicable(context.get(), transformation_context)); + extract_4.Apply(context.get(), &transformation_context); + auto replacement_4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(27, instruction_descriptor_4, 0), 204); + ASSERT_TRUE( + replacement_4.IsApplicable(context.get(), transformation_context)); + replacement_4.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %22 with %102[0] in 'OpStore %23 %22' + auto instruction_descriptor_5 = MakeInstructionDescriptor(23, SpvOpStore, 0); + auto extract_5 = + TransformationCompositeExtract(instruction_descriptor_5, 205, 102, {0}); + ASSERT_TRUE(extract_5.IsApplicable(context.get(), transformation_context)); + extract_5.Apply(context.get(), &transformation_context); + auto replacement_5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(22, instruction_descriptor_5, 1), 205); + ASSERT_TRUE( + replacement_5.IsApplicable(context.get(), transformation_context)); + replacement_5.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "Inner" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpName %11 "i1" + OpName %17 "i2" + OpName %31 "Point" + OpMemberName %31 0 "x" + OpMemberName %31 1 "y" + OpMemberName %31 2 "z" + OpName %32 "Outer" + OpMemberName %32 0 "c" + OpMemberName %32 1 "d" + OpName %34 "o1" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeVector %7 2 + %9 = OpTypeStruct %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %7 2 + %14 = OpConstant %7 3 + %15 = OpConstantComposite %8 %13 %14 + %16 = OpConstantComposite %9 %12 %15 + %18 = OpConstant %6 0 + %19 = OpTypePointer Function %6 + %24 = OpTypePointer Function %8 + %27 = OpConstant %7 4 + %31 = OpTypeStruct %7 %7 %7 + %32 = OpTypeStruct %9 %31 + %33 = OpTypePointer Function %32 + %36 = OpConstant %7 10 + %37 = OpTypeInt 32 0 + %38 = OpConstant %37 0 + %39 = OpTypePointer Function %7 + %42 = OpConstant %37 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %17 = OpVariable %10 Function + %34 = OpVariable %33 Function + %101 = OpCompositeConstruct %31 %27 %36 %27 + OpStore %11 %16 + %20 = OpAccessChain %19 %11 %18 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %21 %12 + %102 = OpCompositeConstruct %9 %22 %15 + %23 = OpAccessChain %19 %17 %18 + %205 = OpCompositeExtract %6 %102 0 + OpStore %23 %205 + %25 = OpAccessChain %24 %17 %12 + %26 = OpLoad %8 %25 + %202 = OpCompositeExtract %7 %101 0 + %204 = OpCompositeExtract %7 %101 2 + %28 = OpCompositeConstruct %8 %204 %202 + %29 = OpFAdd %8 %26 %28 + %30 = OpAccessChain %24 %17 %12 + OpStore %30 %29 + %35 = OpLoad %9 %11 + %40 = OpAccessChain %39 %11 %12 %38 + %41 = OpLoad %7 %40 + %43 = OpAccessChain %39 %11 %12 %42 + %44 = OpLoad %7 %43 + %203 = OpCompositeExtract %7 %101 1 + %45 = OpCompositeConstruct %31 %203 %41 %44 + %100 = OpCompositeConstruct %32 %16 %45 + %201 = OpCompositeExtract %31 %100 1 + %46 = OpCompositeConstruct %32 %35 %201 + OpStore %34 %46 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(DataSynonymTransformationTest, VectorCompositeSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f" + OpName %12 "v2" + OpName %18 "v3" + OpName %23 "v4" + OpName %32 "b" + OpName %36 "bv2" + OpName %41 "bv3" + OpName %50 "bv4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 42 + %10 = OpTypeVector %6 2 + %11 = OpTypePointer Function %10 + %16 = OpTypeVector %6 3 + %17 = OpTypePointer Function %16 + %21 = OpTypeVector %6 4 + %22 = OpTypePointer Function %21 + %30 = OpTypeBool + %31 = OpTypePointer Function %30 + %33 = OpConstantFalse %30 + %34 = OpTypeVector %30 2 + %35 = OpTypePointer Function %34 + %37 = OpConstantTrue %30 + %38 = OpConstantComposite %34 %37 %37 + %39 = OpTypeVector %30 3 + %40 = OpTypePointer Function %39 + %48 = OpTypeVector %30 4 + %49 = OpTypePointer Function %48 + %51 = OpTypeInt 32 0 + %52 = OpConstant %51 2 + %55 = OpConstant %6 0 + %57 = OpConstant %51 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %18 = OpVariable %17 Function + %23 = OpVariable %22 Function + %32 = OpVariable %31 Function + %36 = OpVariable %35 Function + %41 = OpVariable %40 Function + %50 = OpVariable %49 Function + OpStore %8 %9 + %13 = OpLoad %6 %8 + %14 = OpLoad %6 %8 + %15 = OpCompositeConstruct %10 %13 %14 + OpStore %12 %15 + %19 = OpLoad %10 %12 + %20 = OpVectorShuffle %16 %19 %19 0 0 1 + OpStore %18 %20 + %24 = OpLoad %16 %18 + %25 = OpLoad %6 %8 + %26 = OpCompositeExtract %6 %24 0 + %27 = OpCompositeExtract %6 %24 1 + %28 = OpCompositeExtract %6 %24 2 + %29 = OpCompositeConstruct %21 %26 %27 %28 %25 + OpStore %23 %29 + OpStore %32 %33 + OpStore %36 %38 + %42 = OpLoad %30 %32 + %43 = OpLoad %34 %36 + %44 = OpVectorShuffle %34 %43 %43 0 0 + %45 = OpCompositeExtract %30 %44 0 + %46 = OpCompositeExtract %30 %44 1 + %47 = OpCompositeConstruct %39 %42 %45 %46 + OpStore %41 %47 + %53 = OpAccessChain %7 %23 %52 + %54 = OpLoad %6 %53 + + %100 = OpCompositeConstruct %21 %20 %54 + %101 = OpCompositeConstruct %21 %15 %19 + %102 = OpCompositeConstruct %16 %27 %15 + %103 = OpCompositeConstruct %48 %33 %47 + %104 = OpCompositeConstruct %34 %42 %45 + %105 = OpCompositeConstruct %39 %38 %46 + + %86 = OpCopyObject %30 %33 + %56 = OpFOrdNotEqual %30 %54 %55 + %80 = OpCopyObject %16 %20 + %58 = OpAccessChain %7 %18 %57 + %59 = OpLoad %6 %58 + %60 = OpFOrdNotEqual %30 %59 %55 + %61 = OpLoad %34 %36 + %62 = OpLogicalAnd %30 %45 %46 + %63 = OpLogicalOr %30 %45 %46 + %64 = OpCompositeConstruct %48 %56 %60 %62 %63 + OpStore %12 %15 + %81 = OpVectorShuffle %16 %19 %19 0 0 1 + %82 = OpCompositeConstruct %21 %26 %27 %28 %25 + %83 = OpCopyObject %10 %15 + %84 = OpCopyObject %39 %47 + OpStore %50 %64 + %85 = OpCopyObject %30 %42 + OpStore %36 %38 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(20, {0}, 100, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(20, {1}, 100, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(20, {2}, 100, {2})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(54, {}, 100, {3})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(15, {0}, 101, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(15, {1}, 101, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(19, {0}, 101, {2})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(19, {1}, 101, {3})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(27, {}, 102, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(15, {0}, 102, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(15, {1}, 102, {2})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(33, {}, 103, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(47, {0}, 103, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(47, {1}, 103, {2})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(47, {2}, 103, {3})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(42, {}, 104, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(45, {}, 104, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(38, {0}, 105, {0})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(38, {1}, 105, {1})); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(46, {}, 105, {2})); + + // Replace %20 with %100[0:2] in '%80 = OpCopyObject %16 %20' + auto instruction_descriptor_1 = + MakeInstructionDescriptor(80, SpvOpCopyObject, 0); + auto shuffle_1 = TransformationVectorShuffle(instruction_descriptor_1, 200, + 100, 100, {0, 1, 2}); + ASSERT_TRUE(shuffle_1.IsApplicable(context.get(), transformation_context)); + shuffle_1.Apply(context.get(), &transformation_context); + transformation_context.GetFactManager()->ComputeClosureOfFacts(100); + + auto replacement_1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(20, instruction_descriptor_1, 0), 200); + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + replacement_1.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %54 with %100[3] in '%56 = OpFOrdNotEqual %30 %54 %55' + auto instruction_descriptor_2 = + MakeInstructionDescriptor(56, SpvOpFOrdNotEqual, 0); + auto extract_2 = + TransformationCompositeExtract(instruction_descriptor_2, 201, 100, {3}); + + ASSERT_TRUE(extract_2.IsApplicable(context.get(), transformation_context)); + extract_2.Apply(context.get(), &transformation_context); + auto replacement_2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(54, instruction_descriptor_2, 0), 201); + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + replacement_2.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %15 with %101[0:1] in 'OpStore %12 %15' + auto instruction_descriptor_3 = MakeInstructionDescriptor(64, SpvOpStore, 0); + auto shuffle_3 = TransformationVectorShuffle(instruction_descriptor_3, 202, + 101, 101, {0, 1}); + ASSERT_TRUE(shuffle_3.IsApplicable(context.get(), transformation_context)); + shuffle_3.Apply(context.get(), &transformation_context); + transformation_context.GetFactManager()->ComputeClosureOfFacts(100); + + auto replacement_3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(15, instruction_descriptor_3, 1), 202); + ASSERT_TRUE( + replacement_3.IsApplicable(context.get(), transformation_context)); + replacement_3.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %19 with %101[2:3] in '%81 = OpVectorShuffle %16 %19 %19 0 0 1' + auto instruction_descriptor_4 = + MakeInstructionDescriptor(81, SpvOpVectorShuffle, 0); + auto shuffle_4 = TransformationVectorShuffle(instruction_descriptor_4, 203, + 101, 101, {2, 3}); + ASSERT_TRUE(shuffle_4.IsApplicable(context.get(), transformation_context)); + shuffle_4.Apply(context.get(), &transformation_context); + transformation_context.GetFactManager()->ComputeClosureOfFacts(100); + + auto replacement_4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(19, instruction_descriptor_4, 0), 203); + ASSERT_TRUE( + replacement_4.IsApplicable(context.get(), transformation_context)); + replacement_4.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %27 with %102[0] in '%82 = OpCompositeConstruct %21 %26 %27 %28 + // %25' + auto instruction_descriptor_5 = + MakeInstructionDescriptor(82, SpvOpCompositeConstruct, 0); + auto extract_5 = + TransformationCompositeExtract(instruction_descriptor_5, 204, 102, {0}); + + ASSERT_TRUE(extract_5.IsApplicable(context.get(), transformation_context)); + extract_5.Apply(context.get(), &transformation_context); + auto replacement_5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(27, instruction_descriptor_5, 1), 204); + ASSERT_TRUE( + replacement_5.IsApplicable(context.get(), transformation_context)); + replacement_5.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %15 with %102[1:2] in '%83 = OpCopyObject %10 %15' + auto instruction_descriptor_6 = + MakeInstructionDescriptor(83, SpvOpCopyObject, 0); + auto shuffle_6 = TransformationVectorShuffle(instruction_descriptor_6, 205, + 102, 102, {1, 2}); + ASSERT_TRUE(shuffle_6.IsApplicable(context.get(), transformation_context)); + shuffle_6.Apply(context.get(), &transformation_context); + transformation_context.GetFactManager()->ComputeClosureOfFacts(100); + + auto replacement_6 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(15, instruction_descriptor_6, 0), 205); + ASSERT_TRUE( + replacement_6.IsApplicable(context.get(), transformation_context)); + replacement_6.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %33 with %103[0] in '%86 = OpCopyObject %30 %33' + auto instruction_descriptor_7 = + MakeInstructionDescriptor(86, SpvOpCopyObject, 0); + auto extract_7 = + TransformationCompositeExtract(instruction_descriptor_7, 206, 103, {0}); + ASSERT_TRUE(extract_7.IsApplicable(context.get(), transformation_context)); + extract_7.Apply(context.get(), &transformation_context); + auto replacement_7 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(33, instruction_descriptor_7, 0), 206); + ASSERT_TRUE( + replacement_7.IsApplicable(context.get(), transformation_context)); + replacement_7.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %47 with %103[1:3] in '%84 = OpCopyObject %39 %47' + auto instruction_descriptor_8 = + MakeInstructionDescriptor(84, SpvOpCopyObject, 0); + auto shuffle_8 = TransformationVectorShuffle(instruction_descriptor_8, 207, + 103, 103, {1, 2, 3}); + ASSERT_TRUE(shuffle_8.IsApplicable(context.get(), transformation_context)); + shuffle_8.Apply(context.get(), &transformation_context); + transformation_context.GetFactManager()->ComputeClosureOfFacts(100); + + auto replacement_8 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(47, instruction_descriptor_8, 0), 207); + ASSERT_TRUE( + replacement_8.IsApplicable(context.get(), transformation_context)); + replacement_8.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %42 with %104[0] in '%85 = OpCopyObject %30 %42' + auto instruction_descriptor_9 = + MakeInstructionDescriptor(85, SpvOpCopyObject, 0); + auto extract_9 = + TransformationCompositeExtract(instruction_descriptor_9, 208, 104, {0}); + ASSERT_TRUE(extract_9.IsApplicable(context.get(), transformation_context)); + extract_9.Apply(context.get(), &transformation_context); + auto replacement_9 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(42, instruction_descriptor_9, 0), 208); + ASSERT_TRUE( + replacement_9.IsApplicable(context.get(), transformation_context)); + replacement_9.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %45 with %104[1] in '%63 = OpLogicalOr %30 %45 %46' + auto instruction_descriptor_10 = + MakeInstructionDescriptor(63, SpvOpLogicalOr, 0); + auto extract_10 = + TransformationCompositeExtract(instruction_descriptor_10, 209, 104, {1}); + ASSERT_TRUE(extract_10.IsApplicable(context.get(), transformation_context)); + extract_10.Apply(context.get(), &transformation_context); + auto replacement_10 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(45, instruction_descriptor_10, 0), 209); + ASSERT_TRUE( + replacement_10.IsApplicable(context.get(), transformation_context)); + replacement_10.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %38 with %105[0:1] in 'OpStore %36 %38' + auto instruction_descriptor_11 = MakeInstructionDescriptor(85, SpvOpStore, 0); + auto shuffle_11 = TransformationVectorShuffle(instruction_descriptor_11, 210, + 105, 105, {0, 1}); + ASSERT_TRUE(shuffle_11.IsApplicable(context.get(), transformation_context)); + shuffle_11.Apply(context.get(), &transformation_context); + transformation_context.GetFactManager()->ComputeClosureOfFacts(100); + + auto replacement_11 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(38, instruction_descriptor_11, 1), 210); + ASSERT_TRUE( + replacement_11.IsApplicable(context.get(), transformation_context)); + replacement_11.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %46 with %105[2] in '%62 = OpLogicalAnd %30 %45 %46' + auto instruction_descriptor_12 = + MakeInstructionDescriptor(62, SpvOpLogicalAnd, 0); + auto extract_12 = + TransformationCompositeExtract(instruction_descriptor_12, 211, 105, {2}); + ASSERT_TRUE(extract_12.IsApplicable(context.get(), transformation_context)); + extract_12.Apply(context.get(), &transformation_context); + auto replacement_12 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(46, instruction_descriptor_12, 1), 211); + ASSERT_TRUE( + replacement_12.IsApplicable(context.get(), transformation_context)); + replacement_12.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "f" + OpName %12 "v2" + OpName %18 "v3" + OpName %23 "v4" + OpName %32 "b" + OpName %36 "bv2" + OpName %41 "bv3" + OpName %50 "bv4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 42 + %10 = OpTypeVector %6 2 + %11 = OpTypePointer Function %10 + %16 = OpTypeVector %6 3 + %17 = OpTypePointer Function %16 + %21 = OpTypeVector %6 4 + %22 = OpTypePointer Function %21 + %30 = OpTypeBool + %31 = OpTypePointer Function %30 + %33 = OpConstantFalse %30 + %34 = OpTypeVector %30 2 + %35 = OpTypePointer Function %34 + %37 = OpConstantTrue %30 + %38 = OpConstantComposite %34 %37 %37 + %39 = OpTypeVector %30 3 + %40 = OpTypePointer Function %39 + %48 = OpTypeVector %30 4 + %49 = OpTypePointer Function %48 + %51 = OpTypeInt 32 0 + %52 = OpConstant %51 2 + %55 = OpConstant %6 0 + %57 = OpConstant %51 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %18 = OpVariable %17 Function + %23 = OpVariable %22 Function + %32 = OpVariable %31 Function + %36 = OpVariable %35 Function + %41 = OpVariable %40 Function + %50 = OpVariable %49 Function + OpStore %8 %9 + %13 = OpLoad %6 %8 + %14 = OpLoad %6 %8 + %15 = OpCompositeConstruct %10 %13 %14 + OpStore %12 %15 + %19 = OpLoad %10 %12 + %20 = OpVectorShuffle %16 %19 %19 0 0 1 + OpStore %18 %20 + %24 = OpLoad %16 %18 + %25 = OpLoad %6 %8 + %26 = OpCompositeExtract %6 %24 0 + %27 = OpCompositeExtract %6 %24 1 + %28 = OpCompositeExtract %6 %24 2 + %29 = OpCompositeConstruct %21 %26 %27 %28 %25 + OpStore %23 %29 + OpStore %32 %33 + OpStore %36 %38 + %42 = OpLoad %30 %32 + %43 = OpLoad %34 %36 + %44 = OpVectorShuffle %34 %43 %43 0 0 + %45 = OpCompositeExtract %30 %44 0 + %46 = OpCompositeExtract %30 %44 1 + %47 = OpCompositeConstruct %39 %42 %45 %46 + OpStore %41 %47 + %53 = OpAccessChain %7 %23 %52 + %54 = OpLoad %6 %53 + + %100 = OpCompositeConstruct %21 %20 %54 + %101 = OpCompositeConstruct %21 %15 %19 + %102 = OpCompositeConstruct %16 %27 %15 + %103 = OpCompositeConstruct %48 %33 %47 + %104 = OpCompositeConstruct %34 %42 %45 + %105 = OpCompositeConstruct %39 %38 %46 + + %206 = OpCompositeExtract %30 %103 0 + %86 = OpCopyObject %30 %206 + %201 = OpCompositeExtract %6 %100 3 + %56 = OpFOrdNotEqual %30 %201 %55 + %200 = OpVectorShuffle %16 %100 %100 0 1 2 + %80 = OpCopyObject %16 %200 + %58 = OpAccessChain %7 %18 %57 + %59 = OpLoad %6 %58 + %60 = OpFOrdNotEqual %30 %59 %55 + %61 = OpLoad %34 %36 + %211 = OpCompositeExtract %30 %105 2 + %62 = OpLogicalAnd %30 %45 %211 + %209 = OpCompositeExtract %30 %104 1 + %63 = OpLogicalOr %30 %209 %46 + %64 = OpCompositeConstruct %48 %56 %60 %62 %63 + %202 = OpVectorShuffle %10 %101 %101 0 1 + OpStore %12 %202 + %203 = OpVectorShuffle %10 %101 %101 2 3 + %81 = OpVectorShuffle %16 %203 %19 0 0 1 + %204 = OpCompositeExtract %6 %102 0 + %82 = OpCompositeConstruct %21 %26 %204 %28 %25 + %205 = OpVectorShuffle %10 %102 %102 1 2 + %83 = OpCopyObject %10 %205 + %207 = OpVectorShuffle %39 %103 %103 1 2 3 + %84 = OpCopyObject %39 %207 + OpStore %50 %64 + %208 = OpCompositeExtract %30 %104 0 + %85 = OpCopyObject %30 %208 + %210 = OpVectorShuffle %34 %105 %105 0 1 + OpStore %36 %210 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/equivalence_relation_test.cpp b/third_party/spirv-tools/test/fuzz/equivalence_relation_test.cpp new file mode 100644 index 0000000..431488e --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/equivalence_relation_test.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "source/fuzz/equivalence_relation.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace spvtools { +namespace fuzz { +namespace { + +struct UInt32Equals { + bool operator()(const uint32_t* first, const uint32_t* second) const { + return *first == *second; + } +}; + +struct UInt32Hash { + size_t operator()(const uint32_t* element) const { + return static_cast(*element); + } +}; + +std::vector ToUIntVector( + const std::vector& pointers) { + std::vector result; + for (auto pointer : pointers) { + result.push_back(*pointer); + } + return result; +} + +TEST(EquivalenceRelationTest, BasicTest) { + EquivalenceRelation relation; + ASSERT_TRUE(relation.GetAllKnownValues().empty()); + + for (uint32_t element = 0; element < 100; element++) { + relation.Register(element); + } + + for (uint32_t element = 2; element < 80; element += 2) { + relation.MakeEquivalent(0, element); + relation.MakeEquivalent(element - 1, element + 1); + } + + for (uint32_t element = 82; element < 100; element += 2) { + relation.MakeEquivalent(80, element); + relation.MakeEquivalent(element - 1, element + 1); + } + + relation.MakeEquivalent(78, 80); + + std::vector class1; + for (uint32_t element = 0; element < 98; element += 2) { + ASSERT_TRUE(relation.IsEquivalent(0, element)); + ASSERT_TRUE(relation.IsEquivalent(element, element + 2)); + class1.push_back(element); + } + class1.push_back(98); + + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(0)), + testing::WhenSorted(class1)); + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(4)), + testing::WhenSorted(class1)); + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(40)), + testing::WhenSorted(class1)); + + std::vector class2; + for (uint32_t element = 1; element < 79; element += 2) { + ASSERT_TRUE(relation.IsEquivalent(1, element)); + ASSERT_TRUE(relation.IsEquivalent(element, element + 2)); + class2.push_back(element); + } + class2.push_back(79); + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(1)), + testing::WhenSorted(class2)); + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(11)), + testing::WhenSorted(class2)); + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(31)), + testing::WhenSorted(class2)); + + std::vector class3; + for (uint32_t element = 81; element < 99; element += 2) { + ASSERT_TRUE(relation.IsEquivalent(81, element)); + ASSERT_TRUE(relation.IsEquivalent(element, element + 2)); + class3.push_back(element); + } + class3.push_back(99); + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(81)), + testing::WhenSorted(class3)); + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(91)), + testing::WhenSorted(class3)); + ASSERT_THAT(ToUIntVector(relation.GetEquivalenceClass(99)), + testing::WhenSorted(class3)); + + bool first = true; + std::vector previous_class; + for (auto representative : relation.GetEquivalenceClassRepresentatives()) { + std::vector current_class = + relation.GetEquivalenceClass(*representative); + ASSERT_TRUE(std::find(current_class.begin(), current_class.end(), + representative) != current_class.end()); + if (!first) { + ASSERT_TRUE(std::find(previous_class.begin(), previous_class.end(), + representative) == previous_class.end()); + } + previous_class = current_class; + first = false; + } +} + +TEST(EquivalenceRelationTest, DeterministicEquivalenceClassOrder) { + EquivalenceRelation relation1; + EquivalenceRelation relation2; + + for (uint32_t i = 0; i < 1000; ++i) { + relation1.Register(i); + relation2.Register(i); + } + + for (uint32_t i = 0; i < 1000; ++i) { + if (i >= 10) { + relation1.MakeEquivalent(i, i - 10); + relation2.MakeEquivalent(i, i - 10); + } + } + + // We constructed the equivalence relations in the same way, so we would like + // them to have identical representatives, and identically-ordered equivalence + // classes per representative. + ASSERT_THAT(ToUIntVector(relation1.GetEquivalenceClassRepresentatives()), + ToUIntVector(relation2.GetEquivalenceClassRepresentatives())); + for (auto representative : relation1.GetEquivalenceClassRepresentatives()) { + ASSERT_THAT(ToUIntVector(relation1.GetEquivalenceClass(*representative)), + ToUIntVector(relation2.GetEquivalenceClass(*representative))); + } +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fact_manager/constant_uniform_facts_test.cpp b/third_party/spirv-tools/test/fuzz/fact_manager/constant_uniform_facts_test.cpp new file mode 100644 index 0000000..4d80f0a --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fact_manager/constant_uniform_facts_test.cpp @@ -0,0 +1,797 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "source/fuzz/fact_manager/fact_manager.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +using opt::analysis::BoolConstant; +using opt::analysis::FloatConstant; +using opt::analysis::IntConstant; +using opt::analysis::ScalarConstant; + +using opt::analysis::Bool; +using opt::analysis::Float; +using opt::analysis::Integer; +using opt::analysis::Type; + +bool AddFactHelper( + FactManager* fact_manager, const std::vector& words, + const protobufs::UniformBufferElementDescriptor& descriptor) { + protobufs::FactConstantUniform constant_uniform_fact; + for (auto word : words) { + constant_uniform_fact.add_constant_word(word); + } + *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() = + descriptor; + protobufs::Fact fact; + *fact.mutable_constant_uniform_fact() = constant_uniform_fact; + return fact_manager->MaybeAddFact(fact); +} + +TEST(ConstantUniformFactsTest, ConstantsAvailableViaUniforms) { + std::string shader = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpDecorate %100 DescriptorSet 0 + OpDecorate %100 Binding 0 + OpDecorate %200 DescriptorSet 0 + OpDecorate %200 Binding 1 + OpDecorate %300 DescriptorSet 0 + OpDecorate %300 Binding 2 + OpDecorate %400 DescriptorSet 0 + OpDecorate %400 Binding 3 + OpDecorate %500 DescriptorSet 0 + OpDecorate %500 Binding 4 + OpDecorate %600 DescriptorSet 0 + OpDecorate %600 Binding 5 + OpDecorate %700 DescriptorSet 0 + OpDecorate %700 Binding 6 + OpDecorate %800 DescriptorSet 1 + OpDecorate %800 Binding 0 + OpDecorate %900 DescriptorSet 1 + OpDecorate %900 Binding 1 + OpDecorate %1000 DescriptorSet 1 + OpDecorate %1000 Binding 2 + OpDecorate %1100 DescriptorSet 1 + OpDecorate %1100 Binding 3 + OpDecorate %1200 DescriptorSet 1 + OpDecorate %1200 Binding 4 + OpDecorate %1300 DescriptorSet 1 + OpDecorate %1300 Binding 5 + OpDecorate %1400 DescriptorSet 1 + OpDecorate %1400 Binding 6 + OpDecorate %1500 DescriptorSet 2 + OpDecorate %1500 Binding 0 + OpDecorate %1600 DescriptorSet 2 + OpDecorate %1600 Binding 1 + OpDecorate %1700 DescriptorSet 2 + OpDecorate %1700 Binding 2 + OpDecorate %1800 DescriptorSet 2 + OpDecorate %1800 Binding 3 + OpDecorate %1900 DescriptorSet 2 + OpDecorate %1900 Binding 4 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpTypeInt 32 1 + %12 = OpTypeInt 64 0 + %13 = OpTypeInt 64 1 + %15 = OpTypeFloat 32 + %16 = OpTypeFloat 64 + %17 = OpConstant %11 5 + %18 = OpConstant %11 20 + %19 = OpTypeVector %10 4 + %20 = OpConstant %11 6 + %21 = OpTypeVector %12 4 + %22 = OpConstant %11 10 + %23 = OpTypeVector %11 4 + + %102 = OpTypeStruct %10 %10 %23 + %101 = OpTypePointer Uniform %102 + %100 = OpVariable %101 Uniform + + %203 = OpTypeArray %23 %17 + %202 = OpTypeArray %203 %18 + %201 = OpTypePointer Uniform %202 + %200 = OpVariable %201 Uniform + + %305 = OpTypeStruct %16 %16 %16 %11 %16 + %304 = OpTypeStruct %16 %16 %305 + %303 = OpTypeStruct %304 + %302 = OpTypeStruct %10 %303 + %301 = OpTypePointer Uniform %302 + %300 = OpVariable %301 Uniform + + %400 = OpVariable %101 Uniform + + %500 = OpVariable %201 Uniform + + %604 = OpTypeArray %13 %20 + %603 = OpTypeArray %604 %20 + %602 = OpTypeArray %603 %20 + %601 = OpTypePointer Uniform %602 + %600 = OpVariable %601 Uniform + + %703 = OpTypeArray %13 %20 + %702 = OpTypeArray %703 %20 + %701 = OpTypePointer Uniform %702 + %700 = OpVariable %701 Uniform + + %802 = OpTypeStruct %702 %602 %19 %202 %302 + %801 = OpTypePointer Uniform %802 + %800 = OpVariable %801 Uniform + + %902 = OpTypeStruct %702 %802 %19 %202 %302 + %901 = OpTypePointer Uniform %902 + %900 = OpVariable %901 Uniform + + %1003 = OpTypeStruct %802 + %1002 = OpTypeArray %1003 %20 + %1001 = OpTypePointer Uniform %1002 + %1000 = OpVariable %1001 Uniform + + %1101 = OpTypePointer Uniform %21 + %1100 = OpVariable %1101 Uniform + + %1202 = OpTypeArray %21 %20 + %1201 = OpTypePointer Uniform %1202 + %1200 = OpVariable %1201 Uniform + + %1302 = OpTypeArray %21 %20 + %1301 = OpTypePointer Uniform %1302 + %1300 = OpVariable %1301 Uniform + + %1402 = OpTypeArray %15 %22 + %1401 = OpTypePointer Uniform %1402 + %1400 = OpVariable %1401 Uniform + + %1501 = OpTypePointer Uniform %1402 + %1500 = OpVariable %1501 Uniform + + %1602 = OpTypeArray %1402 %22 + %1601 = OpTypePointer Uniform %1602 + %1600 = OpVariable %1601 Uniform + + %1704 = OpTypeStruct %16 %16 %16 + %1703 = OpTypeArray %1704 %22 + %1702 = OpTypeArray %1703 %22 + %1701 = OpTypePointer Uniform %1702 + %1700 = OpVariable %1701 Uniform + + %1800 = OpVariable %1701 Uniform + + %1906 = OpTypeStruct %16 + %1905 = OpTypeStruct %1906 + %1904 = OpTypeStruct %1905 + %1903 = OpTypeStruct %1904 + %1902 = OpTypeStruct %1903 + %1901 = OpTypePointer Uniform %1902 + %1900 = OpVariable %1901 Uniform + + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + uint32_t buffer_int32_min[1]; + uint32_t buffer_int64_1[2]; + uint32_t buffer_int64_max[2]; + uint32_t buffer_uint64_1[2]; + uint32_t buffer_uint64_max[2]; + uint32_t buffer_float_10[1]; + uint32_t buffer_double_10[2]; + uint32_t buffer_double_20[2]; + + { + int32_t temp = std::numeric_limits::min(); + std::memcpy(&buffer_int32_min, &temp, sizeof(temp)); + } + + { + int64_t temp = 1; + std::memcpy(&buffer_int64_1, &temp, sizeof(temp)); + } + + { + int64_t temp = std::numeric_limits::max(); + std::memcpy(&buffer_int64_max, &temp, sizeof(temp)); + } + + { + uint64_t temp = 1; + std::memcpy(&buffer_uint64_1, &temp, sizeof(temp)); + } + + { + uint64_t temp = std::numeric_limits::max(); + std::memcpy(&buffer_uint64_max, &temp, sizeof(temp)); + } + + { + float temp = 10.0f; + std::memcpy(&buffer_float_10, &temp, sizeof(float)); + } + + { + double temp = 10.0; + std::memcpy(&buffer_double_10, &temp, sizeof(temp)); + } + + { + double temp = 20.0; + std::memcpy(&buffer_double_20, &temp, sizeof(temp)); + } + + FactManager fact_manager(context.get()); + + uint32_t type_int32_id = 11; + uint32_t type_int64_id = 13; + uint32_t type_uint32_id = 10; + uint32_t type_uint64_id = 12; + uint32_t type_float_id = 15; + uint32_t type_double_id = 16; + + // Initially there should be no facts about uniforms. + ASSERT_TRUE( + fact_manager.GetConstantsAvailableFromUniformsForType(type_uint32_id) + .empty()); + + // In the comments that follow we write v[...][...] to refer to uniform + // variable v indexed with some given indices, when in practice v is + // identified via a (descriptor set, binding) pair. + + // 100[2][3] == int(1) + ASSERT_TRUE(AddFactHelper(&fact_manager, {1}, + MakeUniformBufferElementDescriptor(0, 0, {2, 3}))); + + // 200[1][2][3] == int(1) + ASSERT_TRUE(AddFactHelper( + &fact_manager, {1}, MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3}))); + + // 300[1][0][2][3] == int(1) + ASSERT_TRUE( + AddFactHelper(&fact_manager, {1}, + MakeUniformBufferElementDescriptor(0, 2, {1, 0, 2, 3}))); + + // 400[2][3] = int32_min + ASSERT_TRUE(AddFactHelper(&fact_manager, {buffer_int32_min[0]}, + MakeUniformBufferElementDescriptor(0, 3, {2, 3}))); + + // 500[1][2][3] = int32_min + ASSERT_TRUE( + AddFactHelper(&fact_manager, {buffer_int32_min[0]}, + MakeUniformBufferElementDescriptor(0, 4, {1, 2, 3}))); + + // 600[1][2][3] = int64_max + ASSERT_TRUE( + AddFactHelper(&fact_manager, {buffer_int64_max[0], buffer_int64_max[1]}, + MakeUniformBufferElementDescriptor(0, 5, {1, 2, 3}))); + + // 700[1][1] = int64_max + ASSERT_TRUE(AddFactHelper(&fact_manager, + {buffer_int64_max[0], buffer_int64_max[1]}, + MakeUniformBufferElementDescriptor(0, 6, {1, 1}))); + + // 800[2][3] = uint(1) + ASSERT_TRUE(AddFactHelper(&fact_manager, {1}, + MakeUniformBufferElementDescriptor(1, 0, {2, 3}))); + + // 900[1][2][3] = uint(1) + ASSERT_TRUE(AddFactHelper( + &fact_manager, {1}, MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3}))); + + // 1000[1][0][2][3] = uint(1) + ASSERT_TRUE( + AddFactHelper(&fact_manager, {1}, + MakeUniformBufferElementDescriptor(1, 2, {1, 0, 2, 3}))); + + // 1100[0] = uint64(1) + ASSERT_TRUE(AddFactHelper(&fact_manager, + {buffer_uint64_1[0], buffer_uint64_1[1]}, + MakeUniformBufferElementDescriptor(1, 3, {0}))); + + // 1200[0][0] = uint64_max + ASSERT_TRUE(AddFactHelper(&fact_manager, + {buffer_uint64_max[0], buffer_uint64_max[1]}, + MakeUniformBufferElementDescriptor(1, 4, {0, 0}))); + + // 1300[1][0] = uint64_max + ASSERT_TRUE(AddFactHelper(&fact_manager, + {buffer_uint64_max[0], buffer_uint64_max[1]}, + MakeUniformBufferElementDescriptor(1, 5, {1, 0}))); + + // 1400[6] = float(10.0) + ASSERT_TRUE(AddFactHelper(&fact_manager, {buffer_float_10[0]}, + MakeUniformBufferElementDescriptor(1, 6, {6}))); + + // 1500[7] = float(10.0) + ASSERT_TRUE(AddFactHelper(&fact_manager, {buffer_float_10[0]}, + MakeUniformBufferElementDescriptor(2, 0, {7}))); + + // 1600[9][9] = float(10.0) + ASSERT_TRUE(AddFactHelper(&fact_manager, {buffer_float_10[0]}, + MakeUniformBufferElementDescriptor(2, 1, {9, 9}))); + + // 1700[9][9][1] = double(10.0) + ASSERT_TRUE( + AddFactHelper(&fact_manager, {buffer_double_10[0], buffer_double_10[1]}, + MakeUniformBufferElementDescriptor(2, 2, {9, 9, 1}))); + + // 1800[9][9][2] = double(10.0) + ASSERT_TRUE( + AddFactHelper(&fact_manager, {buffer_double_10[0], buffer_double_10[1]}, + MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2}))); + + // 1900[0][0][0][0][0] = double(20.0) + ASSERT_TRUE( + AddFactHelper(&fact_manager, {buffer_double_20[0], buffer_double_20[1]}, + MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0}))); + + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_int32_id, 50, operands)); + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int32_min[0]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_int32_id, 51, operands)); + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int64_max[0]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_int64_max[1]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_int64_id, 52, operands)); + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_uint32_id, 53, operands)); + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_1[0]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_1[1]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_uint64_id, 54, operands)); + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_max[0]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_uint64_max[1]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_uint64_id, 55, operands)); + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_float_10[0]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_float_id, 56, operands)); + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_10[0]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_10[1]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_double_id, 57, operands)); + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_20[0]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {buffer_double_20[1]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_double_id, 58, operands)); + + // A duplicate of the constant with id 59. + operands = {{SPV_OPERAND_TYPE_LITERAL_INTEGER, {1}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, type_int32_id, 59, operands)); + + context->InvalidateAnalysesExceptFor(opt::IRContext::Analysis::kAnalysisNone); + + // Constants 1 and int32_min are available. + ASSERT_EQ(2, + fact_manager.GetConstantsAvailableFromUniformsForType(type_int32_id) + .size()); + // Constant int64_max is available. + ASSERT_EQ(1, + fact_manager.GetConstantsAvailableFromUniformsForType(type_int64_id) + .size()); + // Constant 1u is available. + ASSERT_EQ( + 1, fact_manager.GetConstantsAvailableFromUniformsForType(type_uint32_id) + .size()); + // Constants 1u and uint64_max are available. + ASSERT_EQ( + 2, fact_manager.GetConstantsAvailableFromUniformsForType(type_uint64_id) + .size()); + // Constant 10.0 is available. + ASSERT_EQ(1, + fact_manager.GetConstantsAvailableFromUniformsForType(type_float_id) + .size()); + // Constants 10.0 and 20.0 are available. + ASSERT_EQ( + 2, fact_manager.GetConstantsAvailableFromUniformsForType(type_double_id) + .size()); + + ASSERT_EQ(std::numeric_limits::max(), + context->get_constant_mgr() + ->FindDeclaredConstant( + fact_manager.GetConstantsAvailableFromUniformsForType( + type_int64_id)[0]) + ->AsIntConstant() + ->GetS64()); + ASSERT_EQ(1, context->get_constant_mgr() + ->FindDeclaredConstant( + fact_manager.GetConstantsAvailableFromUniformsForType( + type_uint32_id)[0]) + ->AsIntConstant() + ->GetU32()); + ASSERT_EQ(10.0f, + context->get_constant_mgr() + ->FindDeclaredConstant( + fact_manager.GetConstantsAvailableFromUniformsForType( + type_float_id)[0]) + ->AsFloatConstant() + ->GetFloat()); + const std::vector& double_constant_ids = + fact_manager.GetConstantsAvailableFromUniformsForType(type_double_id); + ASSERT_EQ(10.0, context->get_constant_mgr() + ->FindDeclaredConstant(double_constant_ids[0]) + ->AsFloatConstant() + ->GetDouble()); + ASSERT_EQ(20.0, context->get_constant_mgr() + ->FindDeclaredConstant(double_constant_ids[1]) + ->AsFloatConstant() + ->GetDouble()); + + const std::vector + descriptors_for_double_10 = + fact_manager.GetUniformDescriptorsForConstant(double_constant_ids[0]); + ASSERT_EQ(2, descriptors_for_double_10.size()); + { + auto temp = MakeUniformBufferElementDescriptor(2, 2, {9, 9, 1}); + ASSERT_TRUE(UniformBufferElementDescriptorEquals()( + &temp, &descriptors_for_double_10[0])); + } + { + auto temp = MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2}); + ASSERT_TRUE(UniformBufferElementDescriptorEquals()( + &temp, &descriptors_for_double_10[1])); + } + const std::vector + descriptors_for_double_20 = + fact_manager.GetUniformDescriptorsForConstant(double_constant_ids[1]); + ASSERT_EQ(1, descriptors_for_double_20.size()); + { + auto temp = MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0}); + ASSERT_TRUE(UniformBufferElementDescriptorEquals()( + &temp, &descriptors_for_double_20[0])); + } + + auto constant_1_id = fact_manager.GetConstantFromUniformDescriptor( + MakeUniformBufferElementDescriptor(2, 3, {9, 9, 2})); + ASSERT_TRUE(constant_1_id); + + auto constant_2_id = fact_manager.GetConstantFromUniformDescriptor( + MakeUniformBufferElementDescriptor(2, 4, {0, 0, 0, 0, 0})); + ASSERT_TRUE(constant_2_id); + + ASSERT_EQ(double_constant_ids[0], constant_1_id); + + ASSERT_EQ(double_constant_ids[1], constant_2_id); +} + +TEST(ConstantUniformFactsTest, TwoConstantsWithSameValue) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "buf" + OpMemberName %10 0 "a" + OpName %12 "" + OpDecorate %8 RelaxedPrecision + OpMemberDecorate %10 0 RelaxedPrecision + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %20 = OpConstant %6 1 + %10 = OpTypeStruct %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + auto uniform_buffer_element_descriptor = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + // (0, 0, [0]) = int(1) + ASSERT_TRUE( + AddFactHelper(&fact_manager, {1}, uniform_buffer_element_descriptor)); + auto constants = fact_manager.GetConstantsAvailableFromUniformsForType(6); + ASSERT_EQ(1, constants.size()); + ASSERT_TRUE(constants[0] == 9 || constants[0] == 20); + + auto constant = fact_manager.GetConstantFromUniformDescriptor( + uniform_buffer_element_descriptor); + ASSERT_TRUE(constant == 9 || constant == 20); + + // Because the constants with ids 9 and 20 are equal, we should get the same + // single uniform buffer element descriptor when we look up the descriptors + // for either one of them. + for (auto constant_id : {9u, 20u}) { + auto descriptors = + fact_manager.GetUniformDescriptorsForConstant(constant_id); + ASSERT_EQ(1, descriptors.size()); + ASSERT_TRUE(UniformBufferElementDescriptorEquals()( + &uniform_buffer_element_descriptor, &descriptors[0])); + } +} + +TEST(ConstantUniformFactsTest, NonFiniteFactsAreNotValid) { + std::string shader = R"( + OpCapability Shader + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "buf" + OpMemberName %7 0 "f" + OpMemberName %7 1 "d" + OpName %9 "" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 8 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %10 = OpTypeFloat 64 + %7 = OpTypeStruct %6 %10 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + auto uniform_buffer_element_descriptor_f = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + auto uniform_buffer_element_descriptor_d = + MakeUniformBufferElementDescriptor(0, 0, {1}); + + if (std::numeric_limits::has_infinity) { + // f == +inf + float positive_infinity_float = std::numeric_limits::infinity(); + uint32_t words[1]; + memcpy(words, &positive_infinity_float, sizeof(float)); + ASSERT_FALSE(AddFactHelper(&fact_manager, {words[0]}, + uniform_buffer_element_descriptor_f)); + // f == -inf + float negative_infinity_float = std::numeric_limits::infinity(); + memcpy(words, &negative_infinity_float, sizeof(float)); + ASSERT_FALSE(AddFactHelper(&fact_manager, {words[0]}, + uniform_buffer_element_descriptor_f)); + } + + if (std::numeric_limits::has_quiet_NaN) { + // f == NaN + float quiet_nan_float = std::numeric_limits::quiet_NaN(); + uint32_t words[1]; + memcpy(words, &quiet_nan_float, sizeof(float)); + ASSERT_FALSE(AddFactHelper(&fact_manager, {words[0]}, + uniform_buffer_element_descriptor_f)); + } + + if (std::numeric_limits::has_infinity) { + // d == +inf + double positive_infinity_double = std::numeric_limits::infinity(); + uint32_t words[2]; + memcpy(words, &positive_infinity_double, sizeof(double)); + ASSERT_FALSE(AddFactHelper(&fact_manager, {words[0], words[1]}, + uniform_buffer_element_descriptor_d)); + // d == -inf + double negative_infinity_double = -std::numeric_limits::infinity(); + memcpy(words, &negative_infinity_double, sizeof(double)); + ASSERT_FALSE(AddFactHelper(&fact_manager, {words[0], words[1]}, + uniform_buffer_element_descriptor_d)); + } + + if (std::numeric_limits::has_quiet_NaN) { + // d == NaN + double quiet_nan_double = std::numeric_limits::quiet_NaN(); + uint32_t words[2]; + memcpy(words, &quiet_nan_double, sizeof(double)); + ASSERT_FALSE(AddFactHelper(&fact_manager, {words[0], words[1]}, + uniform_buffer_element_descriptor_d)); + } +} + +TEST(ConstantUniformFactsTest, AmbiguousFact) { + // This test came from the following GLSL: + // + // #version 310 es + // + // precision highp float; + // + // layout(set = 0, binding = 0) uniform buf { + // float f; + // }; + // + // layout(set = 0, binding = 0) uniform buf2 { + // float g; + // }; + // + // void main() { + // + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "buf" + OpMemberName %7 0 "f" + OpName %9 "" + OpName %10 "buf2" + OpMemberName %10 0 "g" + OpName %12 "" + OpMemberDecorate %7 0 Offset 0 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %10 = OpTypeStruct %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + auto uniform_buffer_element_descriptor = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + // The fact cannot be added because it is ambiguous: there are two uniforms + // with descriptor set 0 and binding 0. + ASSERT_FALSE( + AddFactHelper(&fact_manager, {1}, uniform_buffer_element_descriptor)); +} + +TEST(ConstantUniformFactsTest, CheckingFactsDoesNotAddConstants) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %14 = OpAccessChain %13 %11 %12 + %15 = OpLoad %6 %14 + OpStore %8 %15 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + // 8[0] == int(1) + ASSERT_TRUE(AddFactHelper(&fact_manager, {1}, + MakeUniformBufferElementDescriptor(0, 0, {0}))); + + // Although 8[0] has the value 1, we do not have the constant 1 in the module. + // We thus should not find any constants available from uniforms for int type. + // Furthermore, the act of looking for appropriate constants should not change + // which constants are known to the constant manager. + auto int_type = context->get_type_mgr()->GetType(6)->AsInteger(); + opt::analysis::IntConstant constant_one(int_type, {1}); + ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one)); + auto available_constants = + fact_manager.GetConstantsAvailableFromUniformsForType(6); + ASSERT_EQ(0, available_constants.size()); + ASSERT_TRUE(IsEqual(env, shader, context.get())); + ASSERT_FALSE(context->get_constant_mgr()->FindConstant(&constant_one)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fact_manager/data_synonym_and_id_equation_facts_test.cpp b/third_party/spirv-tools/test/fuzz/fact_manager/data_synonym_and_id_equation_facts_test.cpp new file mode 100644 index 0000000..689d2e7 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fact_manager/data_synonym_and_id_equation_facts_test.cpp @@ -0,0 +1,691 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/fact_manager.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/transformation_merge_blocks.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +void CheckConsistencyOfSynonymFacts( + opt::IRContext* ir_context, + const TransformationContext& transformation_context) { + for (uint32_t id : transformation_context.GetFactManager() + ->GetIdsForWhichSynonymsAreKnown()) { + // Every id reported by the fact manager should exist in the module. + ASSERT_NE(ir_context->get_def_use_mgr()->GetDef(id), nullptr); + auto synonyms = + transformation_context.GetFactManager()->GetSynonymsForId(id); + for (auto& dd : synonyms) { + // Every reported synonym should have a base object that exists in the + // module. + ASSERT_NE(ir_context->get_def_use_mgr()->GetDef(dd->object()), nullptr); + } + } +} + +TEST(DataSynonymAndIdEquationFactsTest, RecursiveAdditionOfFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeMatrix %7 4 + %9 = OpConstant %6 0 + %10 = OpConstantComposite %7 %9 %9 %9 %9 + %11 = OpConstantComposite %8 %10 %10 %10 %10 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + fact_manager.AddFactDataSynonym(MakeDataDescriptor(10, {}), + MakeDataDescriptor(11, {2})); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {}), + MakeDataDescriptor(11, {2}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {0}), + MakeDataDescriptor(11, {2, 0}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {1}), + MakeDataDescriptor(11, {2, 1}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {2}), + MakeDataDescriptor(11, {2, 2}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(10, {3}), + MakeDataDescriptor(11, {2, 3}))); +} + +TEST(DataSynonymAndIdEquationFactsTest, CorollaryConversionFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %7 2 + %10 = OpTypeFloat 32 + %11 = OpTypeVector %10 2 + %15 = OpConstant %6 24 ; synonym of %16 + %16 = OpConstant %6 24 + %17 = OpConstant %7 24 ; synonym of %18 + %18 = OpConstant %7 24 + %19 = OpConstantComposite %8 %15 %15 ; synonym of %20 + %20 = OpConstantComposite %8 %16 %16 + %21 = OpConstantComposite %9 %17 %17 ; synonym of %22 + %22 = OpConstantComposite %9 %18 %18 + %23 = OpConstantComposite %8 %15 %15 ; not a synonym of %19 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %24 = OpConvertSToF %10 %15 ; synonym of %25 + %25 = OpConvertSToF %10 %16 + %26 = OpConvertUToF %10 %17 ; not a synonym of %27 (different opcode) + %27 = OpConvertSToF %10 %18 + %28 = OpConvertUToF %11 %19 ; synonym of %29 + %29 = OpConvertUToF %11 %20 + %30 = OpConvertSToF %11 %21 ; not a synonym of %31 (different opcode) + %31 = OpConvertUToF %11 %22 + %32 = OpConvertUToF %11 %23 ; not a synonym of %28 (operand is not synonymous) + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + // Add equation facts + fact_manager.AddFactIdEquation(24, SpvOpConvertSToF, {15}); + fact_manager.AddFactIdEquation(25, SpvOpConvertSToF, {16}); + fact_manager.AddFactIdEquation(26, SpvOpConvertUToF, {17}); + fact_manager.AddFactIdEquation(27, SpvOpConvertSToF, {18}); + fact_manager.AddFactIdEquation(28, SpvOpConvertUToF, {19}); + fact_manager.AddFactIdEquation(29, SpvOpConvertUToF, {20}); + fact_manager.AddFactIdEquation(30, SpvOpConvertSToF, {21}); + fact_manager.AddFactIdEquation(31, SpvOpConvertUToF, {22}); + fact_manager.AddFactIdEquation(32, SpvOpConvertUToF, {23}); + + fact_manager.AddFactDataSynonym(MakeDataDescriptor(15, {}), + MakeDataDescriptor(16, {})); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}), + MakeDataDescriptor(25, {}))); + + fact_manager.AddFactDataSynonym(MakeDataDescriptor(17, {}), + MakeDataDescriptor(18, {})); + ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(26, {}), + MakeDataDescriptor(27, {}))); + + fact_manager.AddFactDataSynonym(MakeDataDescriptor(19, {}), + MakeDataDescriptor(20, {})); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(28, {}), + MakeDataDescriptor(29, {}))); + + fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {}), + MakeDataDescriptor(22, {})); + ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(30, {}), + MakeDataDescriptor(31, {}))); + + ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}), + MakeDataDescriptor(28, {}))); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(23, {}), + MakeDataDescriptor(19, {})); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}), + MakeDataDescriptor(28, {}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}), + MakeDataDescriptor(29, {}))); +} + +TEST(DataSynonymAndIdEquationFactsTest, HandlesCorollariesWithInvalidIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %8 = OpTypeInt 32 1 + %9 = OpConstant %8 3 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpConvertSToF %6 %9 + OpBranch %16 + %16 = OpLabel + %17 = OpPhi %6 %14 %13 + %15 = OpConvertSToF %6 %9 + %18 = OpConvertSToF %6 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Add required facts. + transformation_context.GetFactManager()->AddFactIdEquation( + 14, SpvOpConvertSToF, {9}); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(14, {}), MakeDataDescriptor(17, {})); + + CheckConsistencyOfSynonymFacts(context.get(), transformation_context); + + // Apply TransformationMergeBlocks which will remove %17 from the module. + TransformationMergeBlocks transformation(16); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation.Apply(context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + CheckConsistencyOfSynonymFacts(context.get(), transformation_context); + + ASSERT_EQ(context->get_def_use_mgr()->GetDef(17), nullptr); + + // Add another equation. + transformation_context.GetFactManager()->AddFactIdEquation( + 15, SpvOpConvertSToF, {9}); + + // Check that two ids are synonymous even though one of them doesn't exist in + // the module (%17). + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(17, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(14, {}))); + + CheckConsistencyOfSynonymFacts(context.get(), transformation_context); + + // Remove some instructions from the module. At this point, the equivalence + // class of %14 has no valid members. + ASSERT_TRUE(context->KillDef(14)); + ASSERT_TRUE(context->KillDef(15)); + + transformation_context.GetFactManager()->AddFactIdEquation( + 18, SpvOpConvertSToF, {9}); + + CheckConsistencyOfSynonymFacts(context.get(), transformation_context); + + // We don't create synonyms if at least one of the equivalence classes has no + // valid members. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(14, {}), MakeDataDescriptor(18, {}))); +} + +TEST(DataSynonymAndIdEquationFactsTest, LogicalNotEquationFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpLogicalNot %6 %7 + %15 = OpCopyObject %6 %7 + %16 = OpCopyObject %6 %14 + %17 = OpLogicalNot %6 %16 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + fact_manager.AddFactDataSynonym(MakeDataDescriptor(15, {}), + MakeDataDescriptor(7, {})); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(16, {}), + MakeDataDescriptor(14, {})); + fact_manager.AddFactIdEquation(14, SpvOpLogicalNot, {7}); + fact_manager.AddFactIdEquation(17, SpvOpLogicalNot, {16}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(15, {}), + MakeDataDescriptor(7, {}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}), + MakeDataDescriptor(7, {}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(15, {}), + MakeDataDescriptor(17, {}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(16, {}), + MakeDataDescriptor(14, {}))); +} + +TEST(DataSynonymAndIdEquationFactsTest, SignedNegateEquationFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpSNegate %6 %7 + %15 = OpSNegate %6 %14 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + fact_manager.AddFactIdEquation(14, SpvOpSNegate, {7}); + fact_manager.AddFactIdEquation(15, SpvOpSNegate, {14}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(7, {}), + MakeDataDescriptor(15, {}))); +} + +TEST(DataSynonymAndIdEquationFactsTest, AddSubNegateFacts1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpIAdd %6 %15 %16 + %17 = OpCopyObject %6 %15 + %18 = OpCopyObject %6 %16 + %19 = OpISub %6 %14 %18 ; ==> synonymous(%19, %15) + %20 = OpISub %6 %14 %17 ; ==> synonymous(%20, %16) + %21 = OpCopyObject %6 %14 + %22 = OpISub %6 %16 %21 + %23 = OpCopyObject %6 %22 + %24 = OpSNegate %6 %23 ; ==> synonymous(%24, %15) + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + fact_manager.AddFactIdEquation(14, SpvOpIAdd, {15, 16}); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(17, {}), + MakeDataDescriptor(15, {})); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(18, {}), + MakeDataDescriptor(16, {})); + fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 18}); + fact_manager.AddFactIdEquation(20, SpvOpISub, {14, 17}); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(21, {}), + MakeDataDescriptor(14, {})); + fact_manager.AddFactIdEquation(22, SpvOpISub, {16, 21}); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(23, {}), + MakeDataDescriptor(22, {})); + fact_manager.AddFactIdEquation(24, SpvOpSNegate, {23}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(19, {}), + MakeDataDescriptor(15, {}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}), + MakeDataDescriptor(16, {}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(24, {}), + MakeDataDescriptor(15, {}))); +} + +TEST(DataSynonymAndIdEquationFactsTest, AddSubNegateFacts2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpISub %6 %15 %16 + %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15) + %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15) + %19 = OpISub %6 %14 %15 + %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16) + %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15) + %22 = OpISub %6 %14 %18 + %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16) + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}); + fact_manager.AddFactIdEquation(17, SpvOpIAdd, {14, 16}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}), + MakeDataDescriptor(15, {}))); + + fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 14}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(18, {}), + MakeDataDescriptor(15, {}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}), + MakeDataDescriptor(18, {}))); + + fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}); + fact_manager.AddFactIdEquation(20, SpvOpSNegate, {19}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}), + MakeDataDescriptor(16, {}))); + + fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}), + MakeDataDescriptor(15, {}))); + + fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}); + fact_manager.AddFactIdEquation(23, SpvOpSNegate, {22}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(23, {}), + MakeDataDescriptor(16, {}))); +} + +TEST(DataSynonymAndIdEquationFactsTest, ConversionEquations) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %5 = OpTypeInt 32 0 + %6 = OpTypeFloat 32 + %14 = OpTypeVector %4 2 + %15 = OpTypeVector %5 2 + %24 = OpTypeVector %6 2 + %16 = OpConstant %4 32 ; synonym of %17 + %17 = OpConstant %4 32 + %18 = OpConstant %5 32 ; synonym of %19 + %19 = OpConstant %5 32 + %20 = OpConstantComposite %14 %16 %16 ; synonym of %21 + %21 = OpConstantComposite %14 %17 %17 + %22 = OpConstantComposite %15 %18 %18 ; synonym of %23 + %23 = OpConstantComposite %15 %19 %19 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %25 = OpConvertUToF %6 %16 ; synonym of %26 + %26 = OpConvertUToF %6 %17 + %27 = OpConvertSToF %24 %20 ; not a synonym of %28 (wrong opcode) + %28 = OpConvertUToF %24 %21 + %29 = OpConvertSToF %6 %18 ; not a synonym of %30 (wrong opcode) + %30 = OpConvertUToF %6 %19 + %31 = OpConvertSToF %24 %22 ; synonym of %32 + %32 = OpConvertSToF %24 %23 + %33 = OpConvertUToF %6 %17 ; synonym of %26 + %34 = OpConvertSToF %24 %23 ; synonym of %32 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + fact_manager.AddFactDataSynonym(MakeDataDescriptor(16, {}), + MakeDataDescriptor(17, {})); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(18, {}), + MakeDataDescriptor(19, {})); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(20, {}), + MakeDataDescriptor(21, {})); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}), + MakeDataDescriptor(23, {})); + + fact_manager.AddFactIdEquation(25, SpvOpConvertUToF, {16}); + fact_manager.AddFactIdEquation(26, SpvOpConvertUToF, {17}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(25, {}), + MakeDataDescriptor(26, {}))); + + fact_manager.AddFactIdEquation(27, SpvOpConvertSToF, {20}); + fact_manager.AddFactIdEquation(28, SpvOpConvertUToF, {21}); + ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(27, {}), + MakeDataDescriptor(28, {}))); + + fact_manager.AddFactIdEquation(29, SpvOpConvertSToF, {18}); + fact_manager.AddFactIdEquation(30, SpvOpConvertUToF, {19}); + ASSERT_FALSE(fact_manager.IsSynonymous(MakeDataDescriptor(29, {}), + MakeDataDescriptor(30, {}))); + + fact_manager.AddFactIdEquation(31, SpvOpConvertSToF, {22}); + fact_manager.AddFactIdEquation(32, SpvOpConvertSToF, {23}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(31, {}), + MakeDataDescriptor(32, {}))); + + fact_manager.AddFactIdEquation(33, SpvOpConvertUToF, {17}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(33, {}), + MakeDataDescriptor(26, {}))); + + fact_manager.AddFactIdEquation(34, SpvOpConvertSToF, {23}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(32, {}), + MakeDataDescriptor(34, {}))); +} + +TEST(DataSynonymAndIdEquationFactsTest, BitcastEquationFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %5 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %4 2 + %10 = OpTypeVector %5 2 + %11 = OpTypeVector %8 2 + %6 = OpConstant %4 23 + %7 = OpConstant %5 23 + %19 = OpConstant %8 23 + %20 = OpConstantComposite %9 %6 %6 + %21 = OpConstantComposite %10 %7 %7 + %22 = OpConstantComposite %11 %19 %19 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %30 = OpBitcast %8 %6 + %31 = OpBitcast %5 %6 + %32 = OpBitcast %8 %7 + %33 = OpBitcast %4 %7 + %34 = OpBitcast %4 %19 + %35 = OpBitcast %5 %19 + %36 = OpBitcast %10 %20 + %37 = OpBitcast %11 %20 + %38 = OpBitcast %9 %21 + %39 = OpBitcast %11 %21 + %40 = OpBitcast %9 %22 + %41 = OpBitcast %10 %22 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + uint32_t lhs_id = 30; + for (uint32_t rhs_id : {6, 6, 7, 7, 19, 19, 20, 20, 21, 21, 22, 22}) { + fact_manager.AddFactIdEquation(lhs_id, SpvOpBitcast, {rhs_id}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(lhs_id, {}), + MakeDataDescriptor(rhs_id, {}))); + ++lhs_id; + } +} + +TEST(DataSynonymAndIdEquationFactsTest, EquationAndEquivalenceFacts) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpISub %6 %15 %16 + %114 = OpCopyObject %6 %14 + %17 = OpIAdd %6 %114 %16 ; ==> synonymous(%17, %15) + %18 = OpIAdd %6 %16 %114 ; ==> synonymous(%17, %18, %15) + %19 = OpISub %6 %114 %15 + %119 = OpCopyObject %6 %19 + %20 = OpSNegate %6 %119 ; ==> synonymous(%20, %16) + %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15) + %22 = OpISub %6 %14 %18 + %220 = OpCopyObject %6 %22 + %23 = OpSNegate %6 %220 ; ==> synonymous(%23, %16) + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + fact_manager.AddFactIdEquation(14, SpvOpISub, {15, 16}); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(114, {}), + MakeDataDescriptor(14, {})); + fact_manager.AddFactIdEquation(17, SpvOpIAdd, {114, 16}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}), + MakeDataDescriptor(15, {}))); + + fact_manager.AddFactIdEquation(18, SpvOpIAdd, {16, 114}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(18, {}), + MakeDataDescriptor(15, {}))); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(17, {}), + MakeDataDescriptor(18, {}))); + + fact_manager.AddFactIdEquation(19, SpvOpISub, {14, 15}); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(119, {}), + MakeDataDescriptor(19, {})); + fact_manager.AddFactIdEquation(20, SpvOpSNegate, {119}); + + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(20, {}), + MakeDataDescriptor(16, {}))); + + fact_manager.AddFactIdEquation(21, SpvOpISub, {14, 19}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(21, {}), + MakeDataDescriptor(15, {}))); + + fact_manager.AddFactIdEquation(22, SpvOpISub, {14, 18}); + fact_manager.AddFactDataSynonym(MakeDataDescriptor(22, {}), + MakeDataDescriptor(220, {})); + fact_manager.AddFactIdEquation(23, SpvOpSNegate, {220}); + ASSERT_TRUE(fact_manager.IsSynonymous(MakeDataDescriptor(23, {}), + MakeDataDescriptor(16, {}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fact_manager/dead_block_facts_test.cpp b/third_party/spirv-tools/test/fuzz/fact_manager/dead_block_facts_test.cpp new file mode 100644 index 0000000..7328fd2 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fact_manager/dead_block_facts_test.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/fact_manager.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(DeadBlockFactsTest, BlockIsDead) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %2 = OpFunction %3 None %4 + %9 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %6 %11 %12 + %11 = OpLabel + OpBranch %10 + %12 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + ASSERT_FALSE(fact_manager.BlockIsDead(9)); + ASSERT_FALSE(fact_manager.BlockIsDead(11)); + ASSERT_FALSE(fact_manager.BlockIsDead(12)); + + fact_manager.AddFactBlockIsDead(12); + + ASSERT_FALSE(fact_manager.BlockIsDead(9)); + ASSERT_FALSE(fact_manager.BlockIsDead(11)); + ASSERT_TRUE(fact_manager.BlockIsDead(12)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fact_manager/irrelevant_value_facts_test.cpp b/third_party/spirv-tools/test/fuzz/fact_manager/irrelevant_value_facts_test.cpp new file mode 100644 index 0000000..9fc0867 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fact_manager/irrelevant_value_facts_test.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fact_manager/fact_manager.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(IrrelevantValueFactsTest, IdIsIrrelevant) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %12 = OpConstant %6 0 + %13 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + ASSERT_FALSE(fact_manager.IdIsIrrelevant(12)); + ASSERT_FALSE(fact_manager.IdIsIrrelevant(13)); + + fact_manager.AddFactIdIsIrrelevant(12); + + ASSERT_TRUE(fact_manager.IdIsIrrelevant(12)); + ASSERT_FALSE(fact_manager.IdIsIrrelevant(13)); +} + +TEST(IrrelevantValueFactsTest, GetIrrelevantIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %12 = OpConstant %6 0 + %13 = OpConstant %6 1 + %14 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + ASSERT_EQ(fact_manager.GetIrrelevantIds(), std::unordered_set({})); + + fact_manager.AddFactIdIsIrrelevant(12); + + ASSERT_EQ(fact_manager.GetIrrelevantIds(), + std::unordered_set({12})); + + fact_manager.AddFactIdIsIrrelevant(13); + + ASSERT_EQ(fact_manager.GetIrrelevantIds(), + std::unordered_set({12, 13})); +} + +TEST(IrrelevantValueFactsTest, IdsFromDeadBlocksAreIrrelevant) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpTypePointer Function %7 + %9 = OpConstant %7 1 + %2 = OpFunction %3 None %4 + %10 = OpLabel + %11 = OpVariable %8 Function + OpSelectionMerge %12 None + OpBranchConditional %6 %13 %14 + %13 = OpLabel + OpBranch %12 + %14 = OpLabel + %15 = OpCopyObject %8 %11 + %16 = OpCopyObject %7 %9 + %17 = OpFunctionCall %3 %18 + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + %18 = OpFunction %3 None %4 + %19 = OpLabel + %20 = OpVariable %8 Function + %21 = OpCopyObject %7 %9 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + FactManager fact_manager(context.get()); + + ASSERT_FALSE(fact_manager.BlockIsDead(14)); + ASSERT_FALSE(fact_manager.BlockIsDead(19)); + + // Initially no id is irrelevant. + ASSERT_FALSE(fact_manager.IdIsIrrelevant(16)); + ASSERT_FALSE(fact_manager.IdIsIrrelevant(17)); + ASSERT_EQ(fact_manager.GetIrrelevantIds(), std::unordered_set({})); + + fact_manager.AddFactBlockIsDead(14); + + // %16 and %17 should now be considered irrelevant. + ASSERT_TRUE(fact_manager.IdIsIrrelevant(16)); + ASSERT_TRUE(fact_manager.IdIsIrrelevant(17)); + ASSERT_EQ(fact_manager.GetIrrelevantIds(), + std::unordered_set({16, 17})); + + // Similarly for %21. + ASSERT_FALSE(fact_manager.IdIsIrrelevant(21)); + + fact_manager.AddFactBlockIsDead(19); + + ASSERT_TRUE(fact_manager.IdIsIrrelevant(21)); + ASSERT_EQ(fact_manager.GetIrrelevantIds(), + std::unordered_set({16, 17, 21})); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fuzz_test_util.cpp b/third_party/spirv-tools/test/fuzz/fuzz_test_util.cpp new file mode 100644 index 0000000..28d3f89 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzz_test_util.cpp @@ -0,0 +1,188 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/fuzz/fuzz_test_util.h" + +#include "gtest/gtest.h" + +#include +#include + +#include "source/opt/def_use_manager.h" +#include "tools/io.h" + +namespace spvtools { +namespace fuzz { + +const spvtools::MessageConsumer kConsoleMessageConsumer = + [](spv_message_level_t level, const char*, const spv_position_t& position, + const char* message) -> void { + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + std::cerr << "error: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_WARNING: + std::cout << "warning: line " << position.index << ": " << message + << std::endl; + break; + case SPV_MSG_INFO: + std::cout << "info: line " << position.index << ": " << message + << std::endl; + break; + default: + break; + } +}; + +bool IsEqual(const spv_target_env env, + const std::vector& expected_binary, + const std::vector& actual_binary) { + if (expected_binary == actual_binary) { + return true; + } + SpirvTools t(env); + std::string expected_disassembled; + std::string actual_disassembled; + if (!t.Disassemble(expected_binary, &expected_disassembled, + kFuzzDisassembleOption)) { + return false; + } + if (!t.Disassemble(actual_binary, &actual_disassembled, + kFuzzDisassembleOption)) { + return false; + } + // Using expect gives us a string diff if the strings are not the same. + EXPECT_EQ(expected_disassembled, actual_disassembled); + // We then return the result of the equality comparison, to be used by an + // assertion in the test root function. + return expected_disassembled == actual_disassembled; +} + +bool IsEqual(const spv_target_env env, const std::string& expected_text, + const std::vector& actual_binary) { + std::vector expected_binary; + SpirvTools t(env); + if (!t.Assemble(expected_text, &expected_binary, kFuzzAssembleOption)) { + return false; + } + return IsEqual(env, expected_binary, actual_binary); +} + +bool IsEqual(const spv_target_env env, const std::string& expected_text, + const opt::IRContext* actual_ir) { + std::vector actual_binary; + actual_ir->module()->ToBinary(&actual_binary, false); + return IsEqual(env, expected_text, actual_binary); +} + +bool IsEqual(const spv_target_env env, const opt::IRContext* ir_1, + const opt::IRContext* ir_2) { + std::vector binary_1; + ir_1->module()->ToBinary(&binary_1, false); + std::vector binary_2; + ir_2->module()->ToBinary(&binary_2, false); + return IsEqual(env, binary_1, binary_2); +} + +bool IsEqual(const spv_target_env env, const std::vector& binary_1, + const opt::IRContext* ir_2) { + std::vector binary_2; + ir_2->module()->ToBinary(&binary_2, false); + return IsEqual(env, binary_1, binary_2); +} + +std::string ToString(spv_target_env env, const opt::IRContext* ir) { + std::vector binary; + ir->module()->ToBinary(&binary, false); + return ToString(env, binary); +} + +std::string ToString(spv_target_env env, const std::vector& binary) { + SpirvTools t(env); + std::string result; + t.Disassemble(binary, &result, kFuzzDisassembleOption); + return result; +} + +void DumpShader(opt::IRContext* context, const char* filename) { + std::vector binary; + context->module()->ToBinary(&binary, false); + DumpShader(binary, filename); +} + +void DumpShader(const std::vector& binary, const char* filename) { + auto write_file_succeeded = + WriteFile(filename, "wb", &binary[0], binary.size()); + if (!write_file_succeeded) { + std::cerr << "Failed to dump shader" << std::endl; + } +} + +void DumpTransformationsBinary( + const protobufs::TransformationSequence& transformations, + const char* filename) { + std::ofstream transformations_file; + transformations_file.open(filename, std::ios::out | std::ios::binary); + transformations.SerializeToOstream(&transformations_file); + transformations_file.close(); +} + +void DumpTransformationsJson( + const protobufs::TransformationSequence& transformations, + const char* filename) { + std::string json_string; + auto json_options = google::protobuf::util::JsonOptions(); + json_options.add_whitespace = true; + auto json_generation_status = google::protobuf::util::MessageToJsonString( + transformations, &json_string, json_options); + if (json_generation_status == google::protobuf::util::Status::OK) { + std::ofstream transformations_json_file(filename); + transformations_json_file << json_string; + transformations_json_file.close(); + } +} + +void ApplyAndCheckFreshIds( + const Transformation& transformation, opt::IRContext* ir_context, + TransformationContext* transformation_context, + const std::unordered_set& issued_overflow_ids) { + opt::analysis::DefUseManager::IdToDefMap before_transformation = + ir_context->get_def_use_mgr()->id_to_defs(); + transformation.Apply(ir_context, transformation_context); + opt::analysis::DefUseManager::IdToDefMap after_transformation = + ir_context->get_def_use_mgr()->id_to_defs(); + std::unordered_set fresh_ids_for_transformation = + transformation.GetFreshIds(); + for (auto& entry : after_transformation) { + uint32_t id = entry.first; + bool introduced_by_transformation_message = + fresh_ids_for_transformation.count(id); + bool introduced_by_overflow_ids = issued_overflow_ids.count(id); + ASSERT_FALSE(introduced_by_transformation_message && + introduced_by_overflow_ids); + if (before_transformation.count(entry.first)) { + ASSERT_FALSE(introduced_by_transformation_message || + introduced_by_overflow_ids); + } else { + ASSERT_TRUE(introduced_by_transformation_message || + introduced_by_overflow_ids); + } + } +} + +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fuzz_test_util.h b/third_party/spirv-tools/test/fuzz/fuzz_test_util.h new file mode 100644 index 0000000..1ee0875 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzz_test_util.h @@ -0,0 +1,103 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_FUZZ_FUZZ_TEST_UTIL_H_ +#define TEST_FUZZ_FUZZ_TEST_UTIL_H_ + +#include + +#include "source/fuzz/protobufs/spirvfuzz_protobufs.h" +#include "source/fuzz/transformation.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/build_module.h" +#include "source/opt/ir_context.h" +#include "spirv-tools/libspirv.h" + +namespace spvtools { +namespace fuzz { + +extern const spvtools::MessageConsumer kConsoleMessageConsumer; + +// Returns true if and only if the given binaries are bit-wise equal. +bool IsEqual(spv_target_env env, const std::vector& expected_binary, + const std::vector& actual_binary); + +// Assembles the given text and returns true if and only if the resulting binary +// is bit-wise equal to the given binary. +bool IsEqual(spv_target_env env, const std::string& expected_text, + const std::vector& actual_binary); + +// Assembles the given text and turns the given IR into binary, then returns +// true if and only if the resulting binaries are bit-wise equal. +bool IsEqual(spv_target_env env, const std::string& expected_text, + const opt::IRContext* actual_ir); + +// Turns the given IRs into binaries, then returns true if and only if the +// resulting binaries are bit-wise equal. +bool IsEqual(spv_target_env env, const opt::IRContext* ir_1, + const opt::IRContext* ir_2); + +// Turns |ir_2| into a binary, then returns true if and only if the resulting +// binary is bit-wise equal to |binary_1|. +bool IsEqual(spv_target_env env, const std::vector& binary_1, + const opt::IRContext* ir_2); + +// Assembles the given IR context, then returns its disassembly as a string. +// Useful for debugging. +std::string ToString(spv_target_env env, const opt::IRContext* ir); + +// Returns the disassembly of the given binary as a string. +// Useful for debugging. +std::string ToString(spv_target_env env, const std::vector& binary); + +// Assembly options for writing fuzzer tests. It simplifies matters if +// numeric ids do not change. +const uint32_t kFuzzAssembleOption = + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS; +// Disassembly options for writing fuzzer tests. +const uint32_t kFuzzDisassembleOption = + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | SPV_BINARY_TO_TEXT_OPTION_INDENT; + +// Dumps the SPIRV-V module in |context| to file |filename|. Useful for +// interactive debugging. +void DumpShader(opt::IRContext* context, const char* filename); + +// Dumps |binary| to file |filename|. Useful for interactive debugging. +void DumpShader(const std::vector& binary, const char* filename); + +// Dumps |transformations| to file |filename| in binary format. Useful for +// interactive debugging. +void DumpTransformationsBinary( + const protobufs::TransformationSequence& transformations, + const char* filename); + +// Dumps |transformations| to file |filename| in JSON format. Useful for +// interactive debugging. +void DumpTransformationsJson( + const protobufs::TransformationSequence& transformations, + const char* filename); + +// Applies |transformation| to |ir_context| and |transformation_context|, and +// asserts that any ids in |ir_context| that are only present post- +// transformation are either contained in |transformation.GetFreshIds()|, or +// in |issued_overflow_ids|. +void ApplyAndCheckFreshIds( + const Transformation& transformation, opt::IRContext* ir_context, + TransformationContext* transformation_context, + const std::unordered_set& issued_overflow_ids = {{}}); + +} // namespace fuzz +} // namespace spvtools + +#endif // TEST_FUZZ_FUZZ_TEST_UTIL_H_ diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp new file mode 100644 index 0000000..f7a0996 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_add_opphi_synonyms_test.cpp @@ -0,0 +1,176 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +protobufs::Fact MakeSynonymFact(uint32_t first, uint32_t second) { + protobufs::FactDataSynonym data_synonym_fact; + *data_synonym_fact.mutable_data1() = MakeDataDescriptor(first, {}); + *data_synonym_fact.mutable_data2() = MakeDataDescriptor(second, {}); + protobufs::Fact result; + *result.mutable_data_synonym_fact() = data_synonym_fact; + return result; +} + +// Adds synonym facts to the fact manager. +void SetUpIdSynonyms(FactManager* fact_manager) { + // Synonyms {9, 11, 15, 16, 21, 22} + fact_manager->MaybeAddFact(MakeSynonymFact(11, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(15, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(16, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(21, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(22, 9)); + + // Synonyms {10, 23} + fact_manager->MaybeAddFact(MakeSynonymFact(10, 23)); + + // Synonyms {14, 27} + fact_manager->MaybeAddFact(MakeSynonymFact(14, 27)); + + // Synonyms {24, 26, 30} + fact_manager->MaybeAddFact(MakeSynonymFact(26, 24)); + fact_manager->MaybeAddFact(MakeSynonymFact(30, 24)); +} + +// Returns true if the given lists have the same elements, regardless of their +// order. +template +bool ListsHaveTheSameElements(const std::vector& list1, + const std::vector& list2) { + auto sorted1 = list1; + std::sort(sorted1.begin(), sorted1.end()); + + auto sorted2 = list2; + std::sort(sorted2.begin(), sorted2.end()); + + return sorted1 == sorted2; +} + +std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %31 = OpTypeFunction %7 + %8 = OpTypeInt 32 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %8 1 + %12 = OpTypePointer Function %7 + %2 = OpFunction %3 None %4 + %13 = OpLabel + %14 = OpVariable %12 Function + %15 = OpCopyObject %7 %9 + %16 = OpCopyObject %8 %11 + OpBranch %17 + %17 = OpLabel + OpSelectionMerge %18 None + OpBranchConditional %6 %19 %20 + %19 = OpLabel + %21 = OpCopyObject %7 %15 + %22 = OpCopyObject %8 %16 + %23 = OpCopyObject %7 %10 + %24 = OpIAdd %7 %9 %10 + OpBranch %18 + %20 = OpLabel + OpBranch %18 + %18 = OpLabel + %26 = OpIAdd %7 %15 %10 + %27 = OpCopyObject %12 %14 + OpSelectionMerge %28 None + OpBranchConditional %6 %29 %28 + %29 = OpLabel + %30 = OpCopyObject %7 %26 + OpBranch %28 + %28 = OpLabel + OpReturn + OpFunctionEnd + %32 = OpFunction %7 None %31 + %33 = OpLabel + OpReturnValue %9 + OpFunctionEnd +)"; + +TEST(FuzzerPassAddOpPhiSynonymsTest, HelperFunctions) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassAddOpPhiSynonyms fuzzer_pass(context.get(), &transformation_context, + &fuzzer_context, + &transformation_sequence); + + SetUpIdSynonyms(transformation_context.GetFactManager()); + + std::vector> expected_equivalence_classes = { + {9, 15, 21}, {11, 16, 22}, {10, 23}, {6}, {24, 26, 30}}; + + ASSERT_TRUE(ListsHaveTheSameElements>( + fuzzer_pass.GetIdEquivalenceClasses(), expected_equivalence_classes)); + + // The set {24, 26, 30} is not suitable for 18 (none if the ids is available + // for predecessor 20). + ASSERT_FALSE( + fuzzer_pass.EquivalenceClassIsSuitableForBlock({24, 26, 30}, 18, 1)); + + // The set {6} is not suitable for 18 if we require at least 2 distinct + // available ids. + ASSERT_FALSE(fuzzer_pass.EquivalenceClassIsSuitableForBlock({6}, 18, 2)); + + // Only id 26 from the set {24, 26, 30} is available to use for the + // transformation at block 29, so the set is not suitable if we want at least + // 2 available ids. + ASSERT_FALSE( + fuzzer_pass.EquivalenceClassIsSuitableForBlock({24, 26, 30}, 29, 2)); + + ASSERT_TRUE( + fuzzer_pass.EquivalenceClassIsSuitableForBlock({24, 26, 30}, 29, 1)); + + // %21 is not available at the end of block 20. + ASSERT_TRUE(ListsHaveTheSameElements( + fuzzer_pass.GetSuitableIds({9, 15, 21}, 20), {9, 15})); + + // %24 and %30 are not available at the end of block 18. + ASSERT_TRUE(ListsHaveTheSameElements( + fuzzer_pass.GetSuitableIds({24, 26, 30}, 18), {26})); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp new file mode 100644 index 0000000..d49d1d6 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_construct_composites_test.cpp @@ -0,0 +1,188 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_construct_composites.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(FuzzerPassConstructCompositesTest, IsomorphicStructs) { + // This test declares various isomorphic structs, and a struct that is made up + // of these isomorphic structs. The pass to construct composites is then + // applied several times to check that no issues arise related to using a + // value of one struct type when a value of an isomorphic struct type is + // required. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpConstant %6 0 + %8 = OpTypeStruct %6 %6 %6 + %9 = OpTypeStruct %6 %6 %6 + %10 = OpTypeStruct %6 %6 %6 + %11 = OpTypeStruct %6 %6 %6 + %12 = OpTypeStruct %6 %6 %6 + %13 = OpTypeStruct %8 %9 %10 %11 %12 + %14 = OpConstantComposite %8 %7 %7 %7 + %15 = OpConstantComposite %9 %7 %7 %7 + %16 = OpConstantComposite %10 %7 %7 %7 + %17 = OpConstantComposite %11 %7 %7 %7 + %18 = OpConstantComposite %12 %7 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + + auto prng = MakeUnique(0); + + for (uint32_t i = 0; i < 10; i++) { + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + FuzzerContext fuzzer_context(prng.get(), 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassConstructComposites fuzzer_pass( + context.get(), &transformation_context, &fuzzer_context, + &transformation_sequence); + + fuzzer_pass.Apply(); + + // We just check that the result is valid. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } +} + +TEST(FuzzerPassConstructCompositesTest, IsomorphicArrays) { + // This test declares various isomorphic arrays, and a struct that is made up + // of these isomorphic arrays. The pass to construct composites is then + // applied several times to check that no issues arise related to using a + // value of one array type when a value of an isomorphic array type is + // required. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 3 + %7 = OpConstant %6 0 + %8 = OpTypeArray %6 %51 + %9 = OpTypeArray %6 %51 + %10 = OpTypeArray %6 %51 + %11 = OpTypeArray %6 %51 + %12 = OpTypeArray %6 %51 + %13 = OpTypeStruct %8 %9 %10 %11 %12 + %14 = OpConstantComposite %8 %7 %7 %7 + %15 = OpConstantComposite %9 %7 %7 %7 + %16 = OpConstantComposite %10 %7 %7 %7 + %17 = OpConstantComposite %11 %7 %7 %7 + %18 = OpConstantComposite %12 %7 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpNop + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + + auto prng = MakeUnique(0); + + for (uint32_t i = 0; i < 10; i++) { + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + FuzzerContext fuzzer_context(prng.get(), 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassConstructComposites fuzzer_pass( + context.get(), &transformation_context, &fuzzer_context, + &transformation_sequence); + + fuzzer_pass.Apply(); + + // We just check that the result is valid. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp new file mode 100644 index 0000000..1a7cd4a --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_donate_modules_test.cpp @@ -0,0 +1,2268 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_donate_modules.h" + +#include + +#include "gtest/gtest.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(FuzzerPassDonateModulesTest, BasicDonation) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "m" + OpName %16 "v" + OpDecorate %16 RelaxedPrecision + OpDecorate %20 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypeMatrix %7 2 + %9 = OpTypePointer Private %8 + %10 = OpVariable %9 Private + %11 = OpTypeInt 32 1 + %12 = OpConstant %11 0 + %13 = OpTypeInt 32 0 + %14 = OpTypeVector %13 4 + %15 = OpTypePointer Private %14 + %16 = OpVariable %15 Private + %17 = OpConstant %13 2 + %18 = OpTypePointer Private %13 + %22 = OpConstant %13 0 + %23 = OpTypePointer Private %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpAccessChain %18 %16 %17 + %20 = OpLoad %13 %19 + %21 = OpConvertUToF %6 %20 + %24 = OpAccessChain %23 %10 %12 %22 + OpStore %24 %21 + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "bar(mf24;" + OpName %11 "m" + OpName %20 "foo(vu4;" + OpName %19 "v" + OpName %23 "x" + OpName %26 "param" + OpName %29 "result" + OpName %31 "i" + OpName %81 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypeMatrix %7 2 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpTypeInt 32 0 + %15 = OpTypeVector %14 4 + %16 = OpTypePointer Function %15 + %17 = OpTypeInt 32 1 + %18 = OpTypeFunction %17 %16 + %22 = OpTypePointer Function %17 + %24 = OpConstant %14 2 + %25 = OpConstantComposite %15 %24 %24 %24 %24 + %28 = OpTypePointer Function %6 + %30 = OpConstant %6 0 + %32 = OpConstant %17 0 + %39 = OpConstant %17 10 + %40 = OpTypeBool + %43 = OpConstant %17 3 + %50 = OpConstant %17 1 + %55 = OpConstant %14 0 + %56 = OpTypePointer Function %14 + %59 = OpConstant %14 1 + %65 = OpConstant %17 2 + %68 = OpConstant %6 1 + %69 = OpConstant %6 2 + %70 = OpConstant %6 3 + %71 = OpConstant %6 4 + %72 = OpConstant %14 3 + %76 = OpConstant %6 6 + %77 = OpConstant %6 7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpVariable %22 Function + %26 = OpVariable %16 Function + OpStore %26 %25 + %27 = OpFunctionCall %17 %20 %26 + OpStore %23 %27 + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %29 = OpVariable %28 Function + %31 = OpVariable %22 Function + OpStore %29 %30 + OpStore %31 %32 + OpBranch %33 + %33 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %38 = OpLoad %17 %31 + %41 = OpSLessThan %40 %38 %39 + OpBranchConditional %41 %34 %35 + %34 = OpLabel + %42 = OpLoad %17 %31 + %44 = OpExtInst %17 %1 SClamp %42 %32 %43 + %45 = OpAccessChain %28 %11 %32 %44 + %46 = OpLoad %6 %45 + %47 = OpLoad %6 %29 + %48 = OpFAdd %6 %47 %46 + OpStore %29 %48 + OpBranch %36 + %36 = OpLabel + %49 = OpLoad %17 %31 + %51 = OpIAdd %17 %49 %50 + OpStore %31 %51 + OpBranch %33 + %35 = OpLabel + %52 = OpLoad %6 %29 + OpReturnValue %52 + OpFunctionEnd + %20 = OpFunction %17 None %18 + %19 = OpFunctionParameter %16 + %21 = OpLabel + %81 = OpVariable %9 Function + %57 = OpAccessChain %56 %19 %55 + %58 = OpLoad %14 %57 + %60 = OpAccessChain %56 %19 %59 + %61 = OpLoad %14 %60 + %62 = OpUGreaterThan %40 %58 %61 + OpSelectionMerge %64 None + OpBranchConditional %62 %63 %67 + %63 = OpLabel + OpReturnValue %65 + %67 = OpLabel + %73 = OpAccessChain %56 %19 %72 + %74 = OpLoad %14 %73 + %75 = OpConvertUToF %6 %74 + %78 = OpCompositeConstruct %7 %30 %68 %69 %70 + %79 = OpCompositeConstruct %7 %71 %75 %76 %77 + %80 = OpCompositeConstruct %8 %78 %79 + OpStore %81 %80 + %82 = OpFunctionCall %6 %12 %81 + %83 = OpConvertFToS %17 %82 + OpReturnValue %83 + %64 = OpLabel + %85 = OpUndef %17 + OpReturnValue %85 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonationWithUniforms) { + // This test checks that when donating a shader that contains uniforms, + // uniform variables and associated pointer types are demoted from having + // Uniform storage class to Private storage class. + std::string recipient_and_donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + OpMemberDecorate %19 0 Offset 0 + OpDecorate %19 Block + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpTypePointer Uniform %6 + %17 = OpTypePointer Function %12 + %19 = OpTypeStruct %12 + %20 = OpTypePointer Uniform %19 + %21 = OpVariable %20 Uniform + %22 = OpTypePointer Uniform %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %18 = OpVariable %17 Function + %15 = OpAccessChain %14 %11 %13 + %16 = OpLoad %6 %15 + OpStore %8 %16 + %23 = OpAccessChain %22 %21 %13 + %24 = OpLoad %12 %23 + OpStore %18 %24 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = BuildModule( + env, consumer, recipient_and_donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = BuildModule( + env, consumer, recipient_and_donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + OpMemberDecorate %19 0 Offset 0 + OpDecorate %19 Block + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpTypePointer Uniform %6 + %17 = OpTypePointer Function %12 + %19 = OpTypeStruct %12 + %20 = OpTypePointer Uniform %19 + %21 = OpVariable %20 Uniform + %22 = OpTypePointer Uniform %12 + %100 = OpTypePointer Function %6 + %101 = OpTypeStruct %6 + %102 = OpTypePointer Private %101 + %104 = OpConstant %6 0 + %105 = OpConstantComposite %101 %104 + %103 = OpVariable %102 Private %105 + %106 = OpConstant %12 0 + %107 = OpTypePointer Private %6 + %108 = OpTypePointer Function %12 + %109 = OpTypeStruct %12 + %110 = OpTypePointer Private %109 + %112 = OpConstantComposite %109 %13 + %111 = OpVariable %110 Private %112 + %113 = OpTypePointer Private %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %18 = OpVariable %17 Function + %15 = OpAccessChain %14 %11 %13 + %16 = OpLoad %6 %15 + OpStore %8 %16 + %23 = OpAccessChain %22 %21 %13 + %24 = OpLoad %12 %23 + OpStore %18 %24 + OpReturn + OpFunctionEnd + %114 = OpFunction %2 None %3 + %115 = OpLabel + %116 = OpVariable %100 Function %104 + %117 = OpVariable %108 Function %13 + %118 = OpAccessChain %107 %103 %106 + %119 = OpLoad %6 %118 + OpStore %116 %119 + %120 = OpAccessChain %113 %111 %106 + %121 = OpLoad %12 %120 + OpStore %117 %121 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonationWithInputAndOutputVariables) { + // This test checks that when donating a shader that contains input and output + // variables, such variables and associated pointer types are demoted to have + // the Private storage class. + std::string recipient_and_donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 Location 0 + OpDecorate %11 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %7 + %11 = OpVariable %10 Input + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %7 %11 + OpStore %9 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = BuildModule( + env, consumer, recipient_and_donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = BuildModule( + env, consumer, recipient_and_donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 %11 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %9 Location 0 + OpDecorate %11 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpTypePointer Input %7 + %11 = OpVariable %10 Input + %100 = OpTypePointer Private %7 + %102 = OpConstant %6 0 + %103 = OpConstantComposite %7 %102 %102 %102 %102 + %101 = OpVariable %100 Private %103 + %104 = OpTypePointer Private %7 + %105 = OpVariable %104 Private %103 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpLoad %7 %11 + OpStore %9 %12 + OpReturn + OpFunctionEnd + %106 = OpFunction %2 None %3 + %107 = OpLabel + %108 = OpLoad %7 %105 + OpStore %101 %108 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonateFunctionTypeWithDifferentPointers) { + std::string recipient_and_donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %7 Function + %10 = OpFunctionCall %2 %11 %9 + OpReturn + OpFunctionEnd + %11 = OpFunction %2 None %8 + %12 = OpFunctionParameter %7 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = BuildModule( + env, consumer, recipient_and_donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = BuildModule( + env, consumer, recipient_and_donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateOpConstantNull) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Private %6 + %8 = OpConstantNull %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImages) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + OpName %4 "main" + OpName %10 "mySampler" + OpName %21 "myTexture" + OpName %33 "v" + OpDecorate %10 RelaxedPrecision + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + OpDecorate %22 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypePointer UniformConstant %8 + %10 = OpVariable %9 UniformConstant + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 2 + %15 = OpTypeVector %12 2 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %20 = OpTypePointer UniformConstant %7 + %21 = OpVariable %20 UniformConstant + %23 = OpConstant %12 1 + %25 = OpConstant %17 1 + %27 = OpTypeBool + %31 = OpTypeVector %6 4 + %32 = OpTypePointer Function %31 + %35 = OpConstantComposite %15 %23 %23 + %36 = OpConstant %12 3 + %37 = OpConstant %12 4 + %38 = OpConstantComposite %15 %36 %37 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %32 Function + %11 = OpLoad %8 %10 + %14 = OpImage %7 %11 + %16 = OpImageQuerySizeLod %15 %14 %13 + %19 = OpCompositeExtract %12 %16 0 + %22 = OpLoad %7 %21 + %24 = OpImageQuerySizeLod %15 %22 %23 + %26 = OpCompositeExtract %12 %24 1 + %28 = OpSGreaterThan %27 %19 %26 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %41 + %29 = OpLabel + %34 = OpLoad %8 %10 + %39 = OpImage %7 %34 + %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38 + OpStore %33 %40 + OpBranch %30 + %41 = OpLabel + %42 = OpLoad %7 %21 + %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38 + OpStore %33 %43 + OpBranch %30 + %30 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesSampler) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %16 DescriptorSet 0 + OpDecorate %16 Binding 0 + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 64 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %23 = OpTypeFloat 32 + %6 = OpTypeImage %23 2D 2 0 0 1 Unknown + %47 = OpTypePointer UniformConstant %6 + %12 = OpVariable %47 UniformConstant + %15 = OpTypeSampler + %55 = OpTypePointer UniformConstant %15 + %17 = OpTypeSampledImage %6 + %16 = OpVariable %55 UniformConstant + %37 = OpTypeVector %23 4 + %109 = OpConstant %23 0 + %66 = OpConstantComposite %37 %109 %109 %109 %109 + %56 = OpTypeBool + %54 = OpConstantTrue %56 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %50 + %50 = OpLabel + %51 = OpPhi %37 %66 %5 %111 %53 + OpLoopMerge %52 %53 None + OpBranchConditional %54 %53 %52 + %53 = OpLabel + %106 = OpLoad %6 %12 + %107 = OpLoad %15 %16 + %110 = OpSampledImage %17 %106 %107 + %111 = OpImageSampleImplicitLod %37 %110 %66 Bias %109 + OpBranch %50 + %52 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageStructField) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + OpName %4 "main" + OpName %10 "mySampler" + OpName %21 "myTexture" + OpName %33 "v" + OpDecorate %10 RelaxedPrecision + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + OpDecorate %22 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypePointer UniformConstant %8 + %10 = OpVariable %9 UniformConstant + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 2 + %15 = OpTypeVector %12 2 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %20 = OpTypePointer UniformConstant %7 + %21 = OpVariable %20 UniformConstant + %23 = OpConstant %12 1 + %25 = OpConstant %17 1 + %27 = OpTypeBool + %31 = OpTypeVector %6 4 + %32 = OpTypePointer Function %31 + %35 = OpConstantComposite %15 %23 %23 + %36 = OpConstant %12 3 + %37 = OpConstant %12 4 + %38 = OpConstantComposite %15 %36 %37 + %201 = OpTypeStruct %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %32 Function + %11 = OpLoad %8 %10 + %14 = OpImage %7 %11 + %22 = OpLoad %7 %21 + %200 = OpCompositeConstruct %201 %14 %22 + %202 = OpCompositeExtract %7 %200 0 + %203 = OpCompositeExtract %7 %200 1 + %24 = OpImageQuerySizeLod %15 %203 %23 + %16 = OpImageQuerySizeLod %15 %202 %13 + %26 = OpCompositeExtract %12 %24 1 + %19 = OpCompositeExtract %12 %16 0 + %28 = OpSGreaterThan %27 %19 %26 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %41 + %29 = OpLabel + %34 = OpLoad %8 %10 + %39 = OpImage %7 %34 + %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38 + OpStore %33 %40 + OpBranch %30 + %41 = OpLabel + %42 = OpLoad %7 %21 + %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38 + OpStore %33 %43 + OpBranch %30 + %30 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateCodeThatUsesImageFunctionParameter) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + OpName %4 "main" + OpName %10 "mySampler" + OpName %21 "myTexture" + OpName %33 "v" + OpDecorate %10 RelaxedPrecision + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 0 + OpDecorate %11 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + OpDecorate %21 DescriptorSet 0 + OpDecorate %21 Binding 1 + OpDecorate %22 RelaxedPrecision + OpDecorate %34 RelaxedPrecision + OpDecorate %40 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypePointer UniformConstant %8 + %10 = OpVariable %9 UniformConstant + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 2 + %15 = OpTypeVector %12 2 + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 0 + %20 = OpTypePointer UniformConstant %7 + %21 = OpVariable %20 UniformConstant + %23 = OpConstant %12 1 + %25 = OpConstant %17 1 + %27 = OpTypeBool + %31 = OpTypeVector %6 4 + %32 = OpTypePointer Function %31 + %35 = OpConstantComposite %15 %23 %23 + %36 = OpConstant %12 3 + %37 = OpConstant %12 4 + %38 = OpConstantComposite %15 %36 %37 + %201 = OpTypeFunction %15 %7 %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %32 Function + %11 = OpLoad %8 %10 + %14 = OpImage %7 %11 + %16 = OpFunctionCall %15 %200 %14 %13 + %19 = OpCompositeExtract %12 %16 0 + %22 = OpLoad %7 %21 + %24 = OpImageQuerySizeLod %15 %22 %23 + %26 = OpCompositeExtract %12 %24 1 + %28 = OpSGreaterThan %27 %19 %26 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %41 + %29 = OpLabel + %34 = OpLoad %8 %10 + %39 = OpImage %7 %34 + %40 = OpImageFetch %31 %39 %35 Lod|ConstOffset %13 %38 + OpStore %33 %40 + OpBranch %30 + %41 = OpLabel + %42 = OpLoad %7 %21 + %43 = OpImageFetch %31 %42 %35 Lod|ConstOffset %13 %38 + OpStore %33 %43 + OpBranch %30 + %30 = OpLabel + OpReturn + OpFunctionEnd + %200 = OpFunction %15 None %201 + %202 = OpFunctionParameter %7 + %203 = OpFunctionParameter %12 + %204 = OpLabel + %205 = OpImageQuerySizeLod %15 %202 %203 + OpReturnValue %205 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateShaderWithImageStorageClass) { + std::string recipient_shader = R"( + OpCapability Shader + OpCapability ImageQuery + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpSourceExtension "GL_EXT_samplerless_texture_functions" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "MainPSPacked" + OpExecutionMode %2 OriginUpperLeft + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 128 + %49 = OpTypeInt 32 0 + %50 = OpTypeFloat 32 + %58 = OpConstant %50 1 + %66 = OpConstant %49 0 + %87 = OpTypeVector %50 2 + %88 = OpConstantComposite %87 %58 %58 + %17 = OpTypeImage %49 2D 2 0 0 2 R32ui + %118 = OpTypePointer UniformConstant %17 + %123 = OpTypeVector %49 2 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %142 = OpTypePointer Image %49 + %18 = OpVariable %118 UniformConstant + %2 = OpFunction %132 None %133 + %153 = OpLabel + %495 = OpConvertFToU %123 %88 + %501 = OpImageTexelPointer %142 %18 %495 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArray) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %9 ArrayStride 4 + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 BufferBlock + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeRuntimeArray %6 + %10 = OpTypeStruct %9 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %13 = OpTypeInt 32 0 + %16 = OpConstant %6 0 + %18 = OpConstant %6 1 + %20 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %14 = OpArrayLength %13 %12 0 + %15 = OpBitcast %6 %14 + OpStore %8 %15 + %17 = OpLoad %6 %8 + %19 = OpISub %6 %17 %18 + %21 = OpAccessChain %20 %12 %16 %19 + OpStore %21 %16 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithRuntimeArrayLivesafe) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpDecorate %16 ArrayStride 4 + OpMemberDecorate %17 0 Offset 0 + OpDecorate %17 BufferBlock + OpDecorate %19 DescriptorSet 0 + OpDecorate %19 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpTypeRuntimeArray %6 + %17 = OpTypeStruct %16 + %18 = OpTypePointer Uniform %17 + %19 = OpVariable %18 Uniform + %20 = OpTypeInt 32 0 + %23 = OpTypeBool + %26 = OpConstant %6 32 + %27 = OpTypePointer Uniform %6 + %30 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %21 = OpArrayLength %20 %19 0 + %22 = OpBitcast %6 %21 + %24 = OpSLessThan %23 %15 %22 + OpBranchConditional %24 %11 %12 + %11 = OpLabel + %25 = OpLoad %6 %8 + %28 = OpAccessChain %27 %19 %9 %25 + OpStore %28 %26 + OpBranch %13 + %13 = OpLabel + %29 = OpLoad %6 %8 + %31 = OpIAdd %6 %29 %30 + OpStore %8 %31 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithWorkgroupVariables) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Workgroup %6 + %8 = OpVariable %7 Workgroup + %9 = OpConstant %6 2 + %10 = OpVariable %7 Workgroup + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpStore %10 %11 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, DonateComputeShaderWithAtomics) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpTypePointer Uniform %6 + %16 = OpConstant %6 1 + %17 = OpConstant %6 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %15 = OpAccessChain %14 %11 %13 + %18 = OpAtomicIAdd %6 %15 %16 %17 %16 + OpStore %8 %18 + %19 = OpAccessChain %14 %11 %13 + %20 = OpLoad %6 %8 + %21 = OpAtomicUMin %6 %19 %16 %17 %20 + OpStore %8 %21 + %22 = OpAccessChain %14 %11 %13 + %23 = OpLoad %6 %8 + %24 = OpAtomicUMax %6 %22 %16 %17 %23 + OpStore %8 %24 + %25 = OpAccessChain %14 %11 %13 + %26 = OpLoad %6 %8 + %27 = OpAtomicAnd %6 %25 %16 %17 %26 + OpStore %8 %27 + %28 = OpAccessChain %14 %11 %13 + %29 = OpLoad %6 %8 + %30 = OpAtomicOr %6 %28 %16 %17 %29 + OpStore %8 %30 + %31 = OpAccessChain %14 %11 %13 + %32 = OpLoad %6 %8 + %33 = OpAtomicXor %6 %31 %16 %17 %32 + OpStore %8 %33 + %34 = OpAccessChain %14 %11 %13 + %35 = OpLoad %6 %8 + %36 = OpAtomicExchange %6 %34 %16 %17 %35 + OpStore %8 %36 + %37 = OpAccessChain %14 %11 %13 + %38 = OpLoad %6 %8 + %39 = OpAtomicCompareExchange %6 %37 %16 %17 %17 %16 %38 + OpStore %8 %39 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, Miscellaneous1) { + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "foo(" + OpName %10 "x" + OpName %12 "i" + OpName %33 "i" + OpName %42 "j" + OpDecorate %10 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %64 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 2 + %13 = OpConstant %8 0 + %20 = OpConstant %8 100 + %21 = OpTypeBool + %29 = OpConstant %8 1 + %40 = OpConstant %8 10 + %43 = OpConstant %8 20 + %61 = OpConstant %8 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %9 Function + %42 = OpVariable %9 Function + %32 = OpFunctionCall %2 %6 + OpStore %33 %13 + OpBranch %34 + %34 = OpLabel + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %39 = OpLoad %8 %33 + %41 = OpSLessThan %21 %39 %40 + OpBranchConditional %41 %35 %36 + %35 = OpLabel + OpStore %42 %43 + OpBranch %44 + %44 = OpLabel + OpLoopMerge %46 %47 None + OpBranch %48 + %48 = OpLabel + %49 = OpLoad %8 %42 + %50 = OpSGreaterThan %21 %49 %13 + OpBranchConditional %50 %45 %46 + %45 = OpLabel + %51 = OpFunctionCall %2 %6 + %52 = OpLoad %8 %42 + %53 = OpISub %8 %52 %29 + OpStore %42 %53 + OpBranch %47 + %47 = OpLabel + OpBranch %44 + %46 = OpLabel + OpBranch %54 + %54 = OpLabel + OpLoopMerge %56 %57 None + OpBranch %55 + %55 = OpLabel + %58 = OpLoad %8 %33 + %59 = OpIAdd %8 %58 %29 + OpStore %33 %59 + OpBranch %57 + %57 = OpLabel + %60 = OpLoad %8 %33 + %62 = OpSLessThan %21 %60 %61 + OpBranchConditional %62 %54 %56 + %56 = OpLabel + OpBranch %37 + %37 = OpLabel + %63 = OpLoad %8 %33 + %64 = OpIAdd %8 %63 %29 + OpStore %33 %64 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + %12 = OpVariable %9 Function + OpStore %10 %11 + OpStore %12 %13 + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + %19 = OpLoad %8 %12 + %22 = OpSLessThan %21 %19 %20 + OpBranchConditional %22 %15 %16 + %15 = OpLabel + %23 = OpLoad %8 %12 + %24 = OpLoad %8 %10 + %25 = OpIAdd %8 %24 %23 + OpStore %10 %25 + %26 = OpLoad %8 %10 + %27 = OpIMul %8 %26 %11 + OpStore %10 %27 + OpBranch %17 + %17 = OpLabel + %28 = OpLoad %8 %12 + %30 = OpIAdd %8 %28 %29 + OpStore %12 %30 + OpBranch %14 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator rng(0); + FuzzerContext fuzzer_context(&rng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, OpSpecConstantInstructions) { + std::string donor_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypeInt 32 1 + %8 = OpTypeStruct %6 %6 %7 + %9 = OpSpecConstantTrue %6 + %10 = OpSpecConstantFalse %6 + %11 = OpSpecConstant %7 2 + %12 = OpSpecConstantComposite %8 %9 %10 %11 + %13 = OpSpecConstantOp %6 LogicalEqual %9 %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // Check that the module is valid first. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %100 = OpTypeBool + %101 = OpTypeInt 32 1 + %102 = OpTypeStruct %100 %100 %101 + %103 = OpConstantTrue %100 + %104 = OpConstantFalse %100 + %105 = OpConstant %101 2 + %106 = OpConstantComposite %102 %103 %104 %105 + %107 = OpSpecConstantOp %100 LogicalEqual %103 %104 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %108 = OpFunction %2 None %3 + %109 = OpLabel + OpReturn + OpFunctionEnd + )"; + + // Now check that the transformation has produced the expected result. + ASSERT_TRUE(IsEqual(env, expected_shader, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, DonationSupportsOpTypeRuntimeArray) { + std::string donor_shader = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %29 "kernel_1" + OpEntryPoint GLCompute %37 "kernel_2" + OpSource OpenCL_C 120 + OpDecorate %2 ArrayStride 4 + OpMemberDecorate %3 0 Offset 0 + OpDecorate %3 Block + OpMemberDecorate %5 0 Offset 0 + OpMemberDecorate %6 0 Offset 0 + OpDecorate %6 Block + OpDecorate %21 BuiltIn WorkgroupSize + OpDecorate %23 DescriptorSet 0 + OpDecorate %23 Binding 0 + OpDecorate %25 SpecId 3 + OpDecorate %18 SpecId 0 + OpDecorate %19 SpecId 1 + OpDecorate %20 SpecId 2 + %1 = OpTypeInt 32 0 + %2 = OpTypeRuntimeArray %1 + %3 = OpTypeStruct %2 + %4 = OpTypePointer StorageBuffer %3 + %5 = OpTypeStruct %1 + %6 = OpTypeStruct %5 + %7 = OpTypePointer PushConstant %6 + %8 = OpTypeFloat 32 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %11 = OpTypePointer Workgroup %1 + %12 = OpTypePointer PushConstant %5 + %13 = OpTypePointer StorageBuffer %1 + %14 = OpTypeFunction %1 %1 + %15 = OpTypeVector %1 3 + %16 = OpTypePointer Private %15 + %17 = OpConstant %1 0 + %18 = OpSpecConstant %1 1 + %19 = OpSpecConstant %1 1 + %20 = OpSpecConstant %1 1 + %21 = OpSpecConstantComposite %15 %18 %19 %20 + %25 = OpSpecConstant %1 1 + %26 = OpTypeArray %1 %25 + %27 = OpTypePointer Workgroup %26 + %22 = OpVariable %16 Private %21 + %23 = OpVariable %4 StorageBuffer + %24 = OpVariable %7 PushConstant + %28 = OpVariable %27 Workgroup + %29 = OpFunction %9 None %10 + %30 = OpLabel + %31 = OpAccessChain %11 %28 %17 + %32 = OpAccessChain %12 %24 %17 + %33 = OpLoad %5 %32 + %34 = OpCompositeExtract %1 %33 0 + %35 = OpFunctionCall %1 %45 %34 + %36 = OpAccessChain %13 %23 %17 %34 + OpStore %36 %35 + OpReturn + OpFunctionEnd + %37 = OpFunction %9 None %10 + %38 = OpLabel + %39 = OpAccessChain %11 %28 %17 + %40 = OpAccessChain %12 %24 %17 + %41 = OpLoad %5 %40 + %42 = OpCompositeExtract %1 %41 0 + %43 = OpFunctionCall %1 %45 %42 + %44 = OpAccessChain %13 %23 %17 %42 + OpStore %44 %43 + OpReturn + OpFunctionEnd + %45 = OpFunction %1 Pure %14 + %46 = OpFunctionParameter %1 + %47 = OpLabel + %48 = OpAccessChain %11 %28 %46 + %49 = OpLoad %1 %48 + OpReturnValue %49 + OpFunctionEnd + )"; + + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_0; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator rng(0); + FuzzerContext fuzzer_context(&rng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +TEST(FuzzerPassDonateModulesTest, HandlesCapabilities) { + std::string donor_shader = R"( + OpCapability VariablePointersStorageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %11 = OpConstant %6 23 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %9 + + %9 = OpLabel + %10 = OpPhi %7 %8 %5 + OpStore %10 %11 + OpReturn + + OpFunctionEnd + )"; + + std::string recipient_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator rng(0); + FuzzerContext fuzzer_context(&rng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + ASSERT_TRUE(donor_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)); + ASSERT_FALSE(recipient_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // Check that recipient module hasn't changed. + ASSERT_TRUE(IsEqual(env, recipient_shader, recipient_context.get())); + + // Add the missing capability. + // + // We are adding VariablePointers to test the case when donor and recipient + // have different OpCapability instructions but the same capabilities. In our + // example, VariablePointers implicitly declares + // VariablePointersStorageBuffer. Thus, two modules must be compatible. + recipient_context->AddCapability(SpvCapabilityVariablePointers); + + ASSERT_TRUE(donor_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)); + ASSERT_TRUE(recipient_context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)); + + fuzzer_pass.DonateSingleModule(donor_context.get(), false); + + // Check that donation was successful. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %100 = OpTypeFloat 32 + %101 = OpConstant %100 23 + %102 = OpTypePointer Function %100 + %105 = OpConstant %100 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %103 = OpFunction %2 None %3 + %104 = OpLabel + %106 = OpVariable %102 Function %105 + OpBranch %107 + %107 = OpLabel + %108 = OpPhi %102 %106 %104 + OpStore %108 %101 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, recipient_context.get())); +} + +TEST(FuzzerPassDonateModulesTest, HandlesOpPhisInMergeBlock) { + std::string donor_shader = R"( + ; OpPhis don't support pointers without this capability + ; and we need pointers to test some of the functionality + OpCapability VariablePointers + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %14 = OpTypeBool + %15 = OpConstantTrue %14 + %42 = OpTypePointer Function %14 + + ; back-edge block is unreachable in the CFG + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %7 None + OpBranch %8 + %7 = OpLabel + OpBranch %6 + %8 = OpLabel + OpReturn + OpFunctionEnd + + ; back-edge block already has an edge to the merge block + %9 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpLoopMerge %13 %12 None + OpBranch %12 + %12 = OpLabel + OpBranchConditional %15 %11 %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + + ; merge block has no OpPhis + %16 = OpFunction %2 None %3 + %17 = OpLabel + OpBranch %18 + %18 = OpLabel + OpLoopMerge %20 %19 None + OpBranchConditional %15 %19 %20 + %19 = OpLabel + OpBranch %18 + %20 = OpLabel + OpReturn + OpFunctionEnd + + ; merge block has OpPhis and some of their operands are available at + ; the back-edge block + %21 = OpFunction %2 None %3 + %22 = OpLabel + OpBranch %23 + %23 = OpLabel + %24 = OpCopyObject %14 %15 + OpLoopMerge %28 %27 None + OpBranchConditional %15 %25 %28 + %25 = OpLabel + %26 = OpCopyObject %14 %15 + OpBranchConditional %15 %28 %27 + %27 = OpLabel + OpBranch %23 + %28 = OpLabel + %29 = OpPhi %14 %24 %23 %26 %25 + OpReturn + OpFunctionEnd + + ; none of the OpPhis' operands dominate the back-edge block but some of + ; them have basic type + %30 = OpFunction %2 None %3 + %31 = OpLabel + OpBranch %32 + %32 = OpLabel + OpLoopMerge %40 %39 None + OpBranch %33 + %33 = OpLabel + OpSelectionMerge %38 None + OpBranchConditional %15 %34 %36 + %34 = OpLabel + %35 = OpCopyObject %14 %15 + OpBranchConditional %35 %38 %40 + %36 = OpLabel + %37 = OpCopyObject %14 %15 + OpBranchConditional %37 %38 %40 + %38 = OpLabel + OpBranch %39 + %39 = OpLabel + OpBranch %32 + %40 = OpLabel + %41 = OpPhi %14 %35 %34 %37 %36 + OpReturn + OpFunctionEnd + + ; none of the OpPhis' operands dominate the back-edge block and none of + ; them have basic type + %43 = OpFunction %2 None %3 + %44 = OpLabel + %45 = OpVariable %42 Function + OpBranch %46 + %46 = OpLabel + OpLoopMerge %54 %53 None + OpBranch %47 + %47 = OpLabel + OpSelectionMerge %52 None + OpBranchConditional %15 %48 %50 + %48 = OpLabel + %49 = OpCopyObject %42 %45 + OpBranchConditional %15 %52 %54 + %50 = OpLabel + %51 = OpCopyObject %42 %45 + OpBranchConditional %15 %52 %54 + %52 = OpLabel + OpBranch %53 + %53 = OpLabel + OpBranch %46 + %54 = OpLabel + %55 = OpPhi %42 %49 %48 %51 %50 + OpReturn + OpFunctionEnd + )"; + + std::string recipient_shader = R"( + ; OpPhis don't support pointers without this capability + ; and we need pointers to test some of the functionality + OpCapability VariablePointers + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + const auto recipient_context = + BuildModule(env, consumer, recipient_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_context = + BuildModule(env, consumer, donor_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(recipient_context.get()), validator_options); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassDonateModules fuzzer_pass(recipient_context.get(), + &transformation_context, &fuzzer_context, + &transformation_sequence, {}); + + fuzzer_pass.DonateSingleModule(donor_context.get(), true); + + // We just check that the result is valid. Checking to what it should be + // exactly equal to would be very fragile. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + recipient_context.get(), validator_options, kConsoleMessageConsumer)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_outline_functions_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_outline_functions_test.cpp new file mode 100644 index 0000000..576962c --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_outline_functions_test.cpp @@ -0,0 +1,597 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer_pass_outline_functions.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpName %4 "b" + OpDecorate %3 RelaxedPrecision + OpDecorate %4 RelaxedPrecision + OpDecorate %5 RelaxedPrecision + OpDecorate %6 RelaxedPrecision + OpDecorate %7 RelaxedPrecision + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + %10 = OpTypeVoid + %11 = OpTypeFunction %10 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %14 = OpConstant %12 8 + %15 = OpConstant %12 23 + %16 = OpTypeBool + %17 = OpConstantTrue %16 + %18 = OpConstant %12 0 + %19 = OpConstant %12 1 + %2 = OpFunction %10 None %11 + %20 = OpLabel + %3 = OpVariable %13 Function + %4 = OpVariable %13 Function + OpStore %3 %14 + OpStore %4 %15 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %25 = OpPhi %12 %19 %21 %18 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %5 = OpLoad %12 %3 + %29 = OpSGreaterThan %16 %5 %18 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + %6 = OpLoad %12 %4 + %7 = OpISub %12 %6 %19 + OpStore %4 %7 + OpBranch %26 + %26 = OpLabel + %8 = OpLoad %12 %3 + %9 = OpISub %12 %8 %19 + OpStore %3 %9 + OpBranch %24 + %27 = OpLabel + OpBranch %23 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpBranch %31 + %31 = OpLabel + OpLoopMerge %32 %31 None + OpBranchConditional %17 %31 %32 + %32 = OpLabel + OpSelectionMerge %33 None + OpBranchConditional %17 %34 %35 + %34 = OpLabel + OpBranch %33 + %35 = OpLabel + OpBranch %33 + %33 = OpLabel + %42 = OpPhi %12 %19 %33 %18 %34 %18 %35 + OpLoopMerge %36 %33 None + OpBranchConditional %17 %36 %33 + %36 = OpLabel + %43 = OpPhi %12 %18 %33 %18 %41 + OpReturn + %37 = OpLabel + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + OpBranchConditional %17 %41 %38 + %41 = OpLabel + OpBranchConditional %17 %36 %39 + %39 = OpLabel + OpBranch %37 + %38 = OpLabel + OpReturn + OpFunctionEnd +)"; + +TEST(FuzzerPassOutlineFunctionsTest, EntryIsAlreadySuitable) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, + &fuzzer_context, + &transformation_sequence); + + // Block 28 + auto suitable_entry_block = + fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(28)); + + ASSERT_TRUE(suitable_entry_block); + ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 28); + + // Block 32 + suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(32)); + + ASSERT_TRUE(suitable_entry_block); + ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 32); + + // Block 41 + suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(41)); + + ASSERT_TRUE(suitable_entry_block); + ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 41); + + // The module should not have been changed. + ASSERT_TRUE(IsEqual(env, shader, context.get())); +} + +TEST(FuzzerPassOutlineFunctionsTest, EntryHasOpVariable) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, + &fuzzer_context, + &transformation_sequence); + + // Block 20 + auto suitable_entry_block = + fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(20)); + + // The block should have been split, the new entry block being the block + // generated by the splitting. + ASSERT_TRUE(suitable_entry_block); + ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 100); + + std::string after_adjustment = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpName %4 "b" + OpDecorate %3 RelaxedPrecision + OpDecorate %4 RelaxedPrecision + OpDecorate %5 RelaxedPrecision + OpDecorate %6 RelaxedPrecision + OpDecorate %7 RelaxedPrecision + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + %10 = OpTypeVoid + %11 = OpTypeFunction %10 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %14 = OpConstant %12 8 + %15 = OpConstant %12 23 + %16 = OpTypeBool + %17 = OpConstantTrue %16 + %18 = OpConstant %12 0 + %19 = OpConstant %12 1 + %2 = OpFunction %10 None %11 + %20 = OpLabel + %3 = OpVariable %13 Function + %4 = OpVariable %13 Function + OpBranch %100 + %100 = OpLabel + OpStore %3 %14 + OpStore %4 %15 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %25 = OpPhi %12 %19 %21 %18 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %5 = OpLoad %12 %3 + %29 = OpSGreaterThan %16 %5 %18 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + %6 = OpLoad %12 %4 + %7 = OpISub %12 %6 %19 + OpStore %4 %7 + OpBranch %26 + %26 = OpLabel + %8 = OpLoad %12 %3 + %9 = OpISub %12 %8 %19 + OpStore %3 %9 + OpBranch %24 + %27 = OpLabel + OpBranch %23 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpBranch %31 + %31 = OpLabel + OpLoopMerge %32 %31 None + OpBranchConditional %17 %31 %32 + %32 = OpLabel + OpSelectionMerge %33 None + OpBranchConditional %17 %34 %35 + %34 = OpLabel + OpBranch %33 + %35 = OpLabel + OpBranch %33 + %33 = OpLabel + %42 = OpPhi %12 %19 %33 %18 %34 %18 %35 + OpLoopMerge %36 %33 None + OpBranchConditional %17 %36 %33 + %36 = OpLabel + %43 = OpPhi %12 %18 %33 %18 %41 + OpReturn + %37 = OpLabel + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + OpBranchConditional %17 %41 %38 + %41 = OpLabel + OpBranchConditional %17 %36 %39 + %39 = OpLabel + OpBranch %37 + %38 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_adjustment, context.get())); +} + +TEST(FuzzerPassOutlineFunctionsTest, EntryBlockIsHeader) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, + &fuzzer_context, + &transformation_sequence); + + // Block 21 + auto suitable_entry_block = + fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(21)); + + // A suitable entry block should have been found by finding the preheader + // (%20) and then splitting it. + ASSERT_TRUE(suitable_entry_block); + ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 100); + + // Block 24 + suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(24)); + + // A preheader should have been created, because the current one is a loop + // header. + ASSERT_TRUE(suitable_entry_block); + ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 101); + + // Block 31 + suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(31)); + + // An existing suitable entry block should have been found by finding the + // preheader (%22), which is already suitable. + ASSERT_TRUE(suitable_entry_block); + ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 22); + + // Block 33 + suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(33)); + + // An existing suitable entry block should have been found by creating a new + // preheader (there is not one already), and then splitting it (as it contains + // OpPhi). + ASSERT_TRUE(suitable_entry_block); + ASSERT_TRUE(suitable_entry_block->GetLabel()->result_id() == 104); + + // Block 37 + suitable_entry_block = fuzzer_pass.MaybeGetEntryBlockSuitableForOutlining( + context->get_instr_block(37)); + + // No suitable entry block can be found for block 37, since it is a loop + // header with only one predecessor (the back-edge block). + ASSERT_FALSE(suitable_entry_block); + + std::string after_adjustments = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpName %4 "b" + OpDecorate %3 RelaxedPrecision + OpDecorate %4 RelaxedPrecision + OpDecorate %5 RelaxedPrecision + OpDecorate %6 RelaxedPrecision + OpDecorate %7 RelaxedPrecision + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + %10 = OpTypeVoid + %11 = OpTypeFunction %10 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %14 = OpConstant %12 8 + %15 = OpConstant %12 23 + %16 = OpTypeBool + %17 = OpConstantTrue %16 + %18 = OpConstant %12 0 + %19 = OpConstant %12 1 + %2 = OpFunction %10 None %11 + %20 = OpLabel + %3 = OpVariable %13 Function + %4 = OpVariable %13 Function + OpBranch %100 + %100 = OpLabel + OpStore %3 %14 + OpStore %4 %15 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %101 + %101 = OpLabel + OpBranch %24 + %24 = OpLabel + %25 = OpPhi %12 %19 %101 %18 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %5 = OpLoad %12 %3 + %29 = OpSGreaterThan %16 %5 %18 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + %6 = OpLoad %12 %4 + %7 = OpISub %12 %6 %19 + OpStore %4 %7 + OpBranch %26 + %26 = OpLabel + %8 = OpLoad %12 %3 + %9 = OpISub %12 %8 %19 + OpStore %3 %9 + OpBranch %24 + %27 = OpLabel + OpBranch %23 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpBranch %31 + %31 = OpLabel + OpLoopMerge %32 %31 None + OpBranchConditional %17 %31 %32 + %32 = OpLabel + OpSelectionMerge %102 None + OpBranchConditional %17 %34 %35 + %34 = OpLabel + OpBranch %102 + %35 = OpLabel + OpBranch %102 + %102 = OpLabel + %103 = OpPhi %12 %18 %34 %18 %35 + OpBranch %104 + %104 = OpLabel + OpBranch %33 + %33 = OpLabel + %42 = OpPhi %12 %103 %104 %19 %33 + OpLoopMerge %36 %33 None + OpBranchConditional %17 %36 %33 + %36 = OpLabel + %43 = OpPhi %12 %18 %33 %18 %41 + OpReturn + %37 = OpLabel + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + OpBranchConditional %17 %41 %38 + %41 = OpLabel + OpBranchConditional %17 %36 %39 + %39 = OpLabel + OpBranch %37 + %38 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_adjustments, context.get())); +} + +TEST(FuzzerPassOutlineFunctionsTest, ExitBlock) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformation_sequence; + + FuzzerPassOutlineFunctions fuzzer_pass(context.get(), &transformation_context, + &fuzzer_context, + &transformation_sequence); + + // Block 39 is not a merge block, so it is already suitable. + auto suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining( + context->get_instr_block(39)); + ASSERT_TRUE(suitable_exit_block); + ASSERT_TRUE(suitable_exit_block->GetLabel()->result_id() == 39); + + // The following are merge blocks and, thus, they will need to be split. + + // Block 22 + suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining( + context->get_instr_block(22)); + ASSERT_TRUE(suitable_exit_block); + ASSERT_TRUE(suitable_exit_block->GetLabel()->result_id() == 100); + + // Block 27 + suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining( + context->get_instr_block(27)); + ASSERT_TRUE(suitable_exit_block); + ASSERT_TRUE(suitable_exit_block->GetLabel()->result_id() == 101); + + // Block 36 + suitable_exit_block = fuzzer_pass.MaybeGetExitBlockSuitableForOutlining( + context->get_instr_block(36)); + ASSERT_TRUE(suitable_exit_block); + ASSERT_TRUE(suitable_exit_block->GetLabel()->result_id() == 102); + + std::string after_adjustments = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpName %4 "b" + OpDecorate %3 RelaxedPrecision + OpDecorate %4 RelaxedPrecision + OpDecorate %5 RelaxedPrecision + OpDecorate %6 RelaxedPrecision + OpDecorate %7 RelaxedPrecision + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + %10 = OpTypeVoid + %11 = OpTypeFunction %10 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %14 = OpConstant %12 8 + %15 = OpConstant %12 23 + %16 = OpTypeBool + %17 = OpConstantTrue %16 + %18 = OpConstant %12 0 + %19 = OpConstant %12 1 + %2 = OpFunction %10 None %11 + %20 = OpLabel + %3 = OpVariable %13 Function + %4 = OpVariable %13 Function + OpStore %3 %14 + OpStore %4 %15 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + %25 = OpPhi %12 %19 %21 %18 %26 + OpLoopMerge %27 %26 None + OpBranch %28 + %28 = OpLabel + %5 = OpLoad %12 %3 + %29 = OpSGreaterThan %16 %5 %18 + OpBranchConditional %29 %30 %27 + %30 = OpLabel + %6 = OpLoad %12 %4 + %7 = OpISub %12 %6 %19 + OpStore %4 %7 + OpBranch %26 + %26 = OpLabel + %8 = OpLoad %12 %3 + %9 = OpISub %12 %8 %19 + OpStore %3 %9 + OpBranch %24 + %27 = OpLabel + OpBranch %101 + %101 = OpLabel + OpBranch %23 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpBranch %100 + %100 = OpLabel + OpBranch %31 + %31 = OpLabel + OpLoopMerge %32 %31 None + OpBranchConditional %17 %31 %32 + %32 = OpLabel + OpSelectionMerge %33 None + OpBranchConditional %17 %34 %35 + %34 = OpLabel + OpBranch %33 + %35 = OpLabel + OpBranch %33 + %33 = OpLabel + %42 = OpPhi %12 %19 %33 %18 %34 %18 %35 + OpLoopMerge %36 %33 None + OpBranchConditional %17 %36 %33 + %36 = OpLabel + %43 = OpPhi %12 %18 %33 %18 %41 + OpBranch %102 + %102 = OpLabel + OpReturn + %37 = OpLabel + OpLoopMerge %38 %39 None + OpBranch %40 + %40 = OpLabel + OpBranchConditional %17 %41 %38 + %41 = OpLabel + OpBranchConditional %17 %36 %39 + %39 = OpLabel + OpBranch %37 + %38 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_adjustments, context.get())); +} +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_pass_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_pass_test.cpp new file mode 100644 index 0000000..283aa11 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzzer_pass_test.cpp @@ -0,0 +1,103 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_pass_add_opphi_synonyms.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +class FuzzerPassMock : public FuzzerPass { + public: + FuzzerPassMock(opt::IRContext* ir_context, + TransformationContext* transformation_context, + FuzzerContext* fuzzer_context, + protobufs::TransformationSequence* transformations) + : FuzzerPass(ir_context, transformation_context, fuzzer_context, + transformations) {} + + ~FuzzerPassMock() override = default; + + const std::unordered_set& GetReachedInstructions() const { + return reached_ids_; + } + + void Apply() override { + ForEachInstructionWithInstructionDescriptor( + [this](opt::Function* /*unused*/, opt::BasicBlock* /*unused*/, + opt::BasicBlock::iterator inst_it, + const protobufs::InstructionDescriptor& /*unused*/) { + if (inst_it->result_id()) { + reached_ids_.insert(inst_it->result_id()); + } + }); + } + + private: + std::unordered_set reached_ids_; +}; + +TEST(FuzzerPassTest, ForEachInstructionWithInstructionDescriptor) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %7 = OpUndef %6 + OpReturn + %8 = OpLabel + %9 = OpUndef %6 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Check that %5 is reachable and %8 is unreachable as expected. + const auto* dominator_analysis = + context->GetDominatorAnalysis(context->GetFunction(4)); + ASSERT_TRUE(dominator_analysis->IsReachable(5)); + ASSERT_FALSE(dominator_analysis->IsReachable(8)); + + PseudoRandomGenerator prng(0); + FuzzerContext fuzzer_context(&prng, 100); + protobufs::TransformationSequence transformations; + FuzzerPassMock fuzzer_pass_mock(context.get(), &transformation_context, + &fuzzer_context, &transformations); + fuzzer_pass_mock.Apply(); + + ASSERT_TRUE(fuzzer_pass_mock.GetReachedInstructions().count(7)); + ASSERT_FALSE(fuzzer_pass_mock.GetReachedInstructions().count(9)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp new file mode 100644 index 0000000..dc90574 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzzer_replayer_test.cpp @@ -0,0 +1,1750 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer.h" +#include "source/fuzz/replayer.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +const uint32_t kNumFuzzerRuns = 20; + +// The SPIR-V came from this GLSL: +// +// #version 310 es +// +// void foo() { +// int x; +// x = 2; +// for (int i = 0; i < 100; i++) { +// x += i; +// x = x * 2; +// } +// return; +// } +// +// void main() { +// foo(); +// for (int i = 0; i < 10; i++) { +// int j = 20; +// while(j > 0) { +// foo(); +// j--; +// } +// do { +// i++; +// } while(i < 4); +// } +// } + +const std::string kTestShader1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "foo(" + OpName %10 "x" + OpName %12 "i" + OpName %33 "i" + OpName %42 "j" + OpDecorate %10 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %64 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 2 + %13 = OpConstant %8 0 + %20 = OpConstant %8 100 + %21 = OpTypeBool + %29 = OpConstant %8 1 + %40 = OpConstant %8 10 + %43 = OpConstant %8 20 + %61 = OpConstant %8 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %9 Function + %42 = OpVariable %9 Function + %32 = OpFunctionCall %2 %6 + OpStore %33 %13 + OpBranch %34 + %34 = OpLabel + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %39 = OpLoad %8 %33 + %41 = OpSLessThan %21 %39 %40 + OpBranchConditional %41 %35 %36 + %35 = OpLabel + OpStore %42 %43 + OpBranch %44 + %44 = OpLabel + OpLoopMerge %46 %47 None + OpBranch %48 + %48 = OpLabel + %49 = OpLoad %8 %42 + %50 = OpSGreaterThan %21 %49 %13 + OpBranchConditional %50 %45 %46 + %45 = OpLabel + %51 = OpFunctionCall %2 %6 + %52 = OpLoad %8 %42 + %53 = OpISub %8 %52 %29 + OpStore %42 %53 + OpBranch %47 + %47 = OpLabel + OpBranch %44 + %46 = OpLabel + OpBranch %54 + %54 = OpLabel + OpLoopMerge %56 %57 None + OpBranch %55 + %55 = OpLabel + %58 = OpLoad %8 %33 + %59 = OpIAdd %8 %58 %29 + OpStore %33 %59 + OpBranch %57 + %57 = OpLabel + %60 = OpLoad %8 %33 + %62 = OpSLessThan %21 %60 %61 + OpBranchConditional %62 %54 %56 + %56 = OpLabel + OpBranch %37 + %37 = OpLabel + %63 = OpLoad %8 %33 + %64 = OpIAdd %8 %63 %29 + OpStore %33 %64 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + %12 = OpVariable %9 Function + OpStore %10 %11 + OpStore %12 %13 + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + %19 = OpLoad %8 %12 + %22 = OpSLessThan %21 %19 %20 + OpBranchConditional %22 %15 %16 + %15 = OpLabel + %23 = OpLoad %8 %12 + %24 = OpLoad %8 %10 + %25 = OpIAdd %8 %24 %23 + OpStore %10 %25 + %26 = OpLoad %8 %10 + %27 = OpIMul %8 %26 %11 + OpStore %10 %27 + OpBranch %17 + %17 = OpLabel + %28 = OpLoad %8 %12 + %30 = OpIAdd %8 %28 %29 + OpStore %12 %30 + OpBranch %14 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + +// The SPIR-V came from this GLSL, which was then optimized using spirv-opt +// with the -O argument: +// +// #version 310 es +// +// precision highp float; +// +// layout(location = 0) out vec4 _GLF_color; +// +// layout(set = 0, binding = 0) uniform buf0 { +// vec2 injectionSwitch; +// }; +// layout(set = 0, binding = 1) uniform buf1 { +// vec2 resolution; +// }; +// bool checkSwap(float a, float b) +// { +// return gl_FragCoord.y < resolution.y / 2.0 ? a > b : a < b; +// } +// void main() +// { +// float data[10]; +// for(int i = 0; i < 10; i++) +// { +// data[i] = float(10 - i) * injectionSwitch.y; +// } +// for(int i = 0; i < 9; i++) +// { +// for(int j = 0; j < 10; j++) +// { +// if(j < i + 1) +// { +// continue; +// } +// bool doSwap = checkSwap(data[i], data[j]); +// if(doSwap) +// { +// float temp = data[i]; +// data[i] = data[j]; +// data[j] = temp; +// } +// } +// } +// if(gl_FragCoord.x < resolution.x / 2.0) +// { +// _GLF_color = vec4(data[0] / 10.0, data[5] / 10.0, data[9] / 10.0, 1.0); +// } +// else +// { +// _GLF_color = vec4(data[5] / 10.0, data[9] / 10.0, data[0] / 10.0, 1.0); +// } +// } + +const std::string kTestShader2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %16 %139 %25 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %16 "gl_FragCoord" + OpName %23 "buf1" + OpMemberName %23 0 "resolution" + OpName %25 "" + OpName %61 "data" + OpName %66 "buf0" + OpMemberName %66 0 "injectionSwitch" + OpName %68 "" + OpName %139 "_GLF_color" + OpDecorate %16 BuiltIn FragCoord + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 Block + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + OpDecorate %64 RelaxedPrecision + OpMemberDecorate %66 0 Offset 0 + OpDecorate %66 Block + OpDecorate %68 DescriptorSet 0 + OpDecorate %68 Binding 0 + OpDecorate %75 RelaxedPrecision + OpDecorate %95 RelaxedPrecision + OpDecorate %126 RelaxedPrecision + OpDecorate %128 RelaxedPrecision + OpDecorate %139 Location 0 + OpDecorate %182 RelaxedPrecision + OpDecorate %183 RelaxedPrecision + OpDecorate %184 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeBool + %14 = OpTypeVector %6 4 + %15 = OpTypePointer Input %14 + %16 = OpVariable %15 Input + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 1 + %19 = OpTypePointer Input %6 + %22 = OpTypeVector %6 2 + %23 = OpTypeStruct %22 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %26 = OpTypeInt 32 1 + %27 = OpConstant %26 0 + %28 = OpTypePointer Uniform %6 + %56 = OpConstant %26 10 + %58 = OpConstant %17 10 + %59 = OpTypeArray %6 %58 + %60 = OpTypePointer Function %59 + %66 = OpTypeStruct %22 + %67 = OpTypePointer Uniform %66 + %68 = OpVariable %67 Uniform + %74 = OpConstant %26 1 + %83 = OpConstant %26 9 + %129 = OpConstant %17 0 + %138 = OpTypePointer Output %14 + %139 = OpVariable %138 Output + %144 = OpConstant %26 5 + %151 = OpConstant %6 1 + %194 = OpConstant %6 0.5 + %195 = OpConstant %6 0.100000001 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %61 = OpVariable %60 Function + OpBranch %50 + %50 = OpLabel + %182 = OpPhi %26 %27 %5 %75 %51 + %57 = OpSLessThan %8 %182 %56 + OpLoopMerge %52 %51 None + OpBranchConditional %57 %51 %52 + %51 = OpLabel + %64 = OpISub %26 %56 %182 + %65 = OpConvertSToF %6 %64 + %69 = OpAccessChain %28 %68 %27 %18 + %70 = OpLoad %6 %69 + %71 = OpFMul %6 %65 %70 + %72 = OpAccessChain %7 %61 %182 + OpStore %72 %71 + %75 = OpIAdd %26 %182 %74 + OpBranch %50 + %52 = OpLabel + OpBranch %77 + %77 = OpLabel + %183 = OpPhi %26 %27 %52 %128 %88 + %84 = OpSLessThan %8 %183 %83 + OpLoopMerge %79 %88 None + OpBranchConditional %84 %78 %79 + %78 = OpLabel + OpBranch %86 + %86 = OpLabel + %184 = OpPhi %26 %27 %78 %126 %89 + %92 = OpSLessThan %8 %184 %56 + OpLoopMerge %1000 %89 None + OpBranchConditional %92 %87 %1000 + %87 = OpLabel + %95 = OpIAdd %26 %183 %74 + %96 = OpSLessThan %8 %184 %95 + OpSelectionMerge %98 None + OpBranchConditional %96 %97 %98 + %97 = OpLabel + OpBranch %89 + %98 = OpLabel + %104 = OpAccessChain %7 %61 %183 + %105 = OpLoad %6 %104 + %107 = OpAccessChain %7 %61 %184 + %108 = OpLoad %6 %107 + %166 = OpAccessChain %19 %16 %18 + %167 = OpLoad %6 %166 + %168 = OpAccessChain %28 %25 %27 %18 + %169 = OpLoad %6 %168 + %170 = OpFMul %6 %169 %194 + %171 = OpFOrdLessThan %8 %167 %170 + OpSelectionMerge %172 None + OpBranchConditional %171 %173 %174 + %173 = OpLabel + %177 = OpFOrdGreaterThan %8 %105 %108 + OpBranch %172 + %174 = OpLabel + %180 = OpFOrdLessThan %8 %105 %108 + OpBranch %172 + %172 = OpLabel + %186 = OpPhi %8 %177 %173 %180 %174 + OpSelectionMerge %112 None + OpBranchConditional %186 %111 %112 + %111 = OpLabel + %116 = OpLoad %6 %104 + %120 = OpLoad %6 %107 + OpStore %104 %120 + OpStore %107 %116 + OpBranch %112 + %112 = OpLabel + OpBranch %89 + %89 = OpLabel + %126 = OpIAdd %26 %184 %74 + OpBranch %86 + %1000 = OpLabel + OpBranch %88 + %88 = OpLabel + %128 = OpIAdd %26 %183 %74 + OpBranch %77 + %79 = OpLabel + %130 = OpAccessChain %19 %16 %129 + %131 = OpLoad %6 %130 + %132 = OpAccessChain %28 %25 %27 %129 + %133 = OpLoad %6 %132 + %134 = OpFMul %6 %133 %194 + %135 = OpFOrdLessThan %8 %131 %134 + OpSelectionMerge %137 None + OpBranchConditional %135 %136 %153 + %136 = OpLabel + %140 = OpAccessChain %7 %61 %27 + %141 = OpLoad %6 %140 + %143 = OpFMul %6 %141 %195 + %145 = OpAccessChain %7 %61 %144 + %146 = OpLoad %6 %145 + %147 = OpFMul %6 %146 %195 + %148 = OpAccessChain %7 %61 %83 + %149 = OpLoad %6 %148 + %150 = OpFMul %6 %149 %195 + %152 = OpCompositeConstruct %14 %143 %147 %150 %151 + OpStore %139 %152 + OpBranch %137 + %153 = OpLabel + %154 = OpAccessChain %7 %61 %144 + %155 = OpLoad %6 %154 + %156 = OpFMul %6 %155 %195 + %157 = OpAccessChain %7 %61 %83 + %158 = OpLoad %6 %157 + %159 = OpFMul %6 %158 %195 + %160 = OpAccessChain %7 %61 %27 + %161 = OpLoad %6 %160 + %162 = OpFMul %6 %161 %195 + %163 = OpCompositeConstruct %14 %156 %159 %162 %151 + OpStore %139 %163 + OpBranch %137 + %137 = OpLabel + OpReturn + OpFunctionEnd + )"; + +// The SPIR-V came from this GLSL, which was then optimized using spirv-opt +// with the -O argument: +// +// #version 310 es +// +// precision highp float; +// +// layout(location = 0) out vec4 _GLF_color; +// +// layout(set = 0, binding = 0) uniform buf0 { +// vec2 resolution; +// }; +// void main(void) +// { +// float A[50]; +// for( +// int i = 0; +// i < 200; +// i ++ +// ) +// { +// if(i >= int(resolution.x)) +// { +// break; +// } +// if((4 * (i / 4)) == i) +// { +// A[i / 4] = float(i); +// } +// } +// for( +// int i = 0; +// i < 50; +// i ++ +// ) +// { +// if(i < int(gl_FragCoord.x)) +// { +// break; +// } +// if(i > 0) +// { +// A[i] += A[i - 1]; +// } +// } +// if(int(gl_FragCoord.x) < 20) +// { +// _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 40) +// { +// _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 60) +// { +// _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 80) +// { +// _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 100) +// { +// _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 120) +// { +// _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 140) +// { +// _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 160) +// { +// _GLF_color = vec4(A[35] / resolution.x, A[39] / +// resolution.y, 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 180) +// { +// _GLF_color = vec4(A[40] / resolution.x, A[44] / +// resolution.y, 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 180) +// { +// _GLF_color = vec4(A[45] / resolution.x, A[49] / +// resolution.y, 1.0, 1.0); +// } +// else +// { +// discard; +// } +// } + +const std::string kTestShader3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %68 %100 %24 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %22 "buf0" + OpMemberName %22 0 "resolution" + OpName %24 "" + OpName %46 "A" + OpName %68 "gl_FragCoord" + OpName %100 "_GLF_color" + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %68 BuiltIn FragCoord + OpDecorate %83 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %100 Location 0 + OpDecorate %302 RelaxedPrecision + OpDecorate %304 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpConstant %6 200 + %17 = OpTypeBool + %20 = OpTypeFloat 32 + %21 = OpTypeVector %20 2 + %22 = OpTypeStruct %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypeInt 32 0 + %26 = OpConstant %25 0 + %27 = OpTypePointer Uniform %20 + %35 = OpConstant %6 4 + %43 = OpConstant %25 50 + %44 = OpTypeArray %20 %43 + %45 = OpTypePointer Function %44 + %51 = OpTypePointer Function %20 + %54 = OpConstant %6 1 + %63 = OpConstant %6 50 + %66 = OpTypeVector %20 4 + %67 = OpTypePointer Input %66 + %68 = OpVariable %67 Input + %69 = OpTypePointer Input %20 + %95 = OpConstant %6 20 + %99 = OpTypePointer Output %66 + %100 = OpVariable %99 Output + %108 = OpConstant %25 1 + %112 = OpConstant %20 1 + %118 = OpConstant %6 40 + %122 = OpConstant %6 5 + %128 = OpConstant %6 9 + %139 = OpConstant %6 60 + %143 = OpConstant %6 10 + %149 = OpConstant %6 14 + %160 = OpConstant %6 80 + %164 = OpConstant %6 15 + %170 = OpConstant %6 19 + %181 = OpConstant %6 100 + %190 = OpConstant %6 24 + %201 = OpConstant %6 120 + %205 = OpConstant %6 25 + %211 = OpConstant %6 29 + %222 = OpConstant %6 140 + %226 = OpConstant %6 30 + %232 = OpConstant %6 34 + %243 = OpConstant %6 160 + %247 = OpConstant %6 35 + %253 = OpConstant %6 39 + %264 = OpConstant %6 180 + %273 = OpConstant %6 44 + %287 = OpConstant %6 45 + %293 = OpConstant %6 49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %46 = OpVariable %45 Function + OpBranch %10 + %10 = OpLabel + %302 = OpPhi %6 %9 %5 %55 %42 + %18 = OpSLessThan %17 %302 %16 + OpLoopMerge %12 %42 None + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %28 = OpAccessChain %27 %24 %9 %26 + %29 = OpLoad %20 %28 + %30 = OpConvertFToS %6 %29 + %31 = OpSGreaterThanEqual %17 %302 %30 + OpSelectionMerge %33 None + OpBranchConditional %31 %32 %33 + %32 = OpLabel + OpBranch %12 + %33 = OpLabel + %37 = OpSDiv %6 %302 %35 + %38 = OpIMul %6 %35 %37 + %40 = OpIEqual %17 %38 %302 + OpBranchConditional %40 %41 %42 + %41 = OpLabel + %50 = OpConvertSToF %20 %302 + %52 = OpAccessChain %51 %46 %37 + OpStore %52 %50 + OpBranch %42 + %42 = OpLabel + %55 = OpIAdd %6 %302 %54 + OpBranch %10 + %12 = OpLabel + OpBranch %57 + %57 = OpLabel + %304 = OpPhi %6 %9 %12 %91 %80 + %64 = OpSLessThan %17 %304 %63 + OpLoopMerge %59 %80 None + OpBranchConditional %64 %58 %59 + %58 = OpLabel + %70 = OpAccessChain %69 %68 %26 + %71 = OpLoad %20 %70 + %72 = OpConvertFToS %6 %71 + %73 = OpSLessThan %17 %304 %72 + OpSelectionMerge %75 None + OpBranchConditional %73 %74 %75 + %74 = OpLabel + OpBranch %59 + %75 = OpLabel + %78 = OpSGreaterThan %17 %304 %9 + OpBranchConditional %78 %79 %80 + %79 = OpLabel + %83 = OpISub %6 %304 %54 + %84 = OpAccessChain %51 %46 %83 + %85 = OpLoad %20 %84 + %86 = OpAccessChain %51 %46 %304 + %87 = OpLoad %20 %86 + %88 = OpFAdd %20 %87 %85 + OpStore %86 %88 + OpBranch %80 + %80 = OpLabel + %91 = OpIAdd %6 %304 %54 + OpBranch %57 + %59 = OpLabel + %92 = OpAccessChain %69 %68 %26 + %93 = OpLoad %20 %92 + %94 = OpConvertFToS %6 %93 + %96 = OpSLessThan %17 %94 %95 + OpSelectionMerge %98 None + OpBranchConditional %96 %97 %114 + %97 = OpLabel + %101 = OpAccessChain %51 %46 %9 + %102 = OpLoad %20 %101 + %103 = OpAccessChain %27 %24 %9 %26 + %104 = OpLoad %20 %103 + %105 = OpFDiv %20 %102 %104 + %106 = OpAccessChain %51 %46 %35 + %107 = OpLoad %20 %106 + %109 = OpAccessChain %27 %24 %9 %108 + %110 = OpLoad %20 %109 + %111 = OpFDiv %20 %107 %110 + %113 = OpCompositeConstruct %66 %105 %111 %112 %112 + OpStore %100 %113 + OpBranch %98 + %114 = OpLabel + %119 = OpSLessThan %17 %94 %118 + OpSelectionMerge %121 None + OpBranchConditional %119 %120 %135 + %120 = OpLabel + %123 = OpAccessChain %51 %46 %122 + %124 = OpLoad %20 %123 + %125 = OpAccessChain %27 %24 %9 %26 + %126 = OpLoad %20 %125 + %127 = OpFDiv %20 %124 %126 + %129 = OpAccessChain %51 %46 %128 + %130 = OpLoad %20 %129 + %131 = OpAccessChain %27 %24 %9 %108 + %132 = OpLoad %20 %131 + %133 = OpFDiv %20 %130 %132 + %134 = OpCompositeConstruct %66 %127 %133 %112 %112 + OpStore %100 %134 + OpBranch %121 + %135 = OpLabel + %140 = OpSLessThan %17 %94 %139 + OpSelectionMerge %142 None + OpBranchConditional %140 %141 %156 + %141 = OpLabel + %144 = OpAccessChain %51 %46 %143 + %145 = OpLoad %20 %144 + %146 = OpAccessChain %27 %24 %9 %26 + %147 = OpLoad %20 %146 + %148 = OpFDiv %20 %145 %147 + %150 = OpAccessChain %51 %46 %149 + %151 = OpLoad %20 %150 + %152 = OpAccessChain %27 %24 %9 %108 + %153 = OpLoad %20 %152 + %154 = OpFDiv %20 %151 %153 + %155 = OpCompositeConstruct %66 %148 %154 %112 %112 + OpStore %100 %155 + OpBranch %142 + %156 = OpLabel + %161 = OpSLessThan %17 %94 %160 + OpSelectionMerge %163 None + OpBranchConditional %161 %162 %177 + %162 = OpLabel + %165 = OpAccessChain %51 %46 %164 + %166 = OpLoad %20 %165 + %167 = OpAccessChain %27 %24 %9 %26 + %168 = OpLoad %20 %167 + %169 = OpFDiv %20 %166 %168 + %171 = OpAccessChain %51 %46 %170 + %172 = OpLoad %20 %171 + %173 = OpAccessChain %27 %24 %9 %108 + %174 = OpLoad %20 %173 + %175 = OpFDiv %20 %172 %174 + %176 = OpCompositeConstruct %66 %169 %175 %112 %112 + OpStore %100 %176 + OpBranch %163 + %177 = OpLabel + %182 = OpSLessThan %17 %94 %181 + OpSelectionMerge %184 None + OpBranchConditional %182 %183 %197 + %183 = OpLabel + %185 = OpAccessChain %51 %46 %95 + %186 = OpLoad %20 %185 + %187 = OpAccessChain %27 %24 %9 %26 + %188 = OpLoad %20 %187 + %189 = OpFDiv %20 %186 %188 + %191 = OpAccessChain %51 %46 %190 + %192 = OpLoad %20 %191 + %193 = OpAccessChain %27 %24 %9 %108 + %194 = OpLoad %20 %193 + %195 = OpFDiv %20 %192 %194 + %196 = OpCompositeConstruct %66 %189 %195 %112 %112 + OpStore %100 %196 + OpBranch %184 + %197 = OpLabel + %202 = OpSLessThan %17 %94 %201 + OpSelectionMerge %204 None + OpBranchConditional %202 %203 %218 + %203 = OpLabel + %206 = OpAccessChain %51 %46 %205 + %207 = OpLoad %20 %206 + %208 = OpAccessChain %27 %24 %9 %26 + %209 = OpLoad %20 %208 + %210 = OpFDiv %20 %207 %209 + %212 = OpAccessChain %51 %46 %211 + %213 = OpLoad %20 %212 + %214 = OpAccessChain %27 %24 %9 %108 + %215 = OpLoad %20 %214 + %216 = OpFDiv %20 %213 %215 + %217 = OpCompositeConstruct %66 %210 %216 %112 %112 + OpStore %100 %217 + OpBranch %204 + %218 = OpLabel + %223 = OpSLessThan %17 %94 %222 + OpSelectionMerge %225 None + OpBranchConditional %223 %224 %239 + %224 = OpLabel + %227 = OpAccessChain %51 %46 %226 + %228 = OpLoad %20 %227 + %229 = OpAccessChain %27 %24 %9 %26 + %230 = OpLoad %20 %229 + %231 = OpFDiv %20 %228 %230 + %233 = OpAccessChain %51 %46 %232 + %234 = OpLoad %20 %233 + %235 = OpAccessChain %27 %24 %9 %108 + %236 = OpLoad %20 %235 + %237 = OpFDiv %20 %234 %236 + %238 = OpCompositeConstruct %66 %231 %237 %112 %112 + OpStore %100 %238 + OpBranch %225 + %239 = OpLabel + %244 = OpSLessThan %17 %94 %243 + OpSelectionMerge %246 None + OpBranchConditional %244 %245 %260 + %245 = OpLabel + %248 = OpAccessChain %51 %46 %247 + %249 = OpLoad %20 %248 + %250 = OpAccessChain %27 %24 %9 %26 + %251 = OpLoad %20 %250 + %252 = OpFDiv %20 %249 %251 + %254 = OpAccessChain %51 %46 %253 + %255 = OpLoad %20 %254 + %256 = OpAccessChain %27 %24 %9 %108 + %257 = OpLoad %20 %256 + %258 = OpFDiv %20 %255 %257 + %259 = OpCompositeConstruct %66 %252 %258 %112 %112 + OpStore %100 %259 + OpBranch %246 + %260 = OpLabel + %265 = OpSLessThan %17 %94 %264 + OpSelectionMerge %267 None + OpBranchConditional %265 %266 %280 + %266 = OpLabel + %268 = OpAccessChain %51 %46 %118 + %269 = OpLoad %20 %268 + %270 = OpAccessChain %27 %24 %9 %26 + %271 = OpLoad %20 %270 + %272 = OpFDiv %20 %269 %271 + %274 = OpAccessChain %51 %46 %273 + %275 = OpLoad %20 %274 + %276 = OpAccessChain %27 %24 %9 %108 + %277 = OpLoad %20 %276 + %278 = OpFDiv %20 %275 %277 + %279 = OpCompositeConstruct %66 %272 %278 %112 %112 + OpStore %100 %279 + OpBranch %267 + %280 = OpLabel + OpSelectionMerge %285 None + OpBranchConditional %265 %285 %300 + %285 = OpLabel + %288 = OpAccessChain %51 %46 %287 + %289 = OpLoad %20 %288 + %290 = OpAccessChain %27 %24 %9 %26 + %291 = OpLoad %20 %290 + %292 = OpFDiv %20 %289 %291 + %294 = OpAccessChain %51 %46 %293 + %295 = OpLoad %20 %294 + %296 = OpAccessChain %27 %24 %9 %108 + %297 = OpLoad %20 %296 + %298 = OpFDiv %20 %295 %297 + %299 = OpCompositeConstruct %66 %292 %298 %112 %112 + OpStore %100 %299 + OpBranch %267 + %300 = OpLabel + OpKill + %267 = OpLabel + OpBranch %246 + %246 = OpLabel + OpBranch %225 + %225 = OpLabel + OpBranch %204 + %204 = OpLabel + OpBranch %184 + %184 = OpLabel + OpBranch %163 + %163 = OpLabel + OpBranch %142 + %142 = OpLabel + OpBranch %121 + %121 = OpLabel + OpBranch %98 + %98 = OpLabel + OpReturn + OpFunctionEnd + )"; + +// The SPIR-V comes from the 'matrices_smart_loops' GLSL shader that ships +// with GraphicsFuzz. + +const std::string kTestShader4 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %327 %363 %65 %70 %80 %90 %99 %108 %117 %126 %135 %144 %333 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "matrix_number" + OpName %12 "cols" + OpName %23 "rows" + OpName %31 "c" + OpName %41 "r" + OpName %65 "m22" + OpName %68 "buf0" + OpMemberName %68 0 "one" + OpName %70 "" + OpName %80 "m23" + OpName %90 "m24" + OpName %99 "m32" + OpName %108 "m33" + OpName %117 "m34" + OpName %126 "m42" + OpName %135 "m43" + OpName %144 "m44" + OpName %164 "sum_index" + OpName %165 "cols" + OpName %173 "rows" + OpName %184 "sums" + OpName %189 "c" + OpName %198 "r" + OpName %325 "region_x" + OpName %327 "gl_FragCoord" + OpName %331 "buf1" + OpMemberName %331 0 "resolution" + OpName %333 "" + OpName %340 "region_y" + OpName %348 "overall_region" + OpName %363 "_GLF_color" + OpDecorate %8 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %29 RelaxedPrecision + OpDecorate %31 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %41 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %48 RelaxedPrecision + OpDecorate %50 RelaxedPrecision + OpDecorate %66 RelaxedPrecision + OpDecorate %67 RelaxedPrecision + OpMemberDecorate %68 0 Offset 0 + OpDecorate %68 Block + OpDecorate %70 DescriptorSet 0 + OpDecorate %70 Binding 0 + OpDecorate %81 RelaxedPrecision + OpDecorate %82 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %92 RelaxedPrecision + OpDecorate %100 RelaxedPrecision + OpDecorate %101 RelaxedPrecision + OpDecorate %109 RelaxedPrecision + OpDecorate %110 RelaxedPrecision + OpDecorate %118 RelaxedPrecision + OpDecorate %119 RelaxedPrecision + OpDecorate %127 RelaxedPrecision + OpDecorate %128 RelaxedPrecision + OpDecorate %136 RelaxedPrecision + OpDecorate %137 RelaxedPrecision + OpDecorate %145 RelaxedPrecision + OpDecorate %146 RelaxedPrecision + OpDecorate %152 RelaxedPrecision + OpDecorate %154 RelaxedPrecision + OpDecorate %155 RelaxedPrecision + OpDecorate %156 RelaxedPrecision + OpDecorate %157 RelaxedPrecision + OpDecorate %159 RelaxedPrecision + OpDecorate %160 RelaxedPrecision + OpDecorate %161 RelaxedPrecision + OpDecorate %162 RelaxedPrecision + OpDecorate %163 RelaxedPrecision + OpDecorate %164 RelaxedPrecision + OpDecorate %165 RelaxedPrecision + OpDecorate %171 RelaxedPrecision + OpDecorate %173 RelaxedPrecision + OpDecorate %179 RelaxedPrecision + OpDecorate %185 RelaxedPrecision + OpDecorate %189 RelaxedPrecision + OpDecorate %195 RelaxedPrecision + OpDecorate %196 RelaxedPrecision + OpDecorate %198 RelaxedPrecision + OpDecorate %204 RelaxedPrecision + OpDecorate %205 RelaxedPrecision + OpDecorate %207 RelaxedPrecision + OpDecorate %218 RelaxedPrecision + OpDecorate %219 RelaxedPrecision + OpDecorate %220 RelaxedPrecision + OpDecorate %228 RelaxedPrecision + OpDecorate %229 RelaxedPrecision + OpDecorate %230 RelaxedPrecision + OpDecorate %238 RelaxedPrecision + OpDecorate %239 RelaxedPrecision + OpDecorate %240 RelaxedPrecision + OpDecorate %248 RelaxedPrecision + OpDecorate %249 RelaxedPrecision + OpDecorate %250 RelaxedPrecision + OpDecorate %258 RelaxedPrecision + OpDecorate %259 RelaxedPrecision + OpDecorate %260 RelaxedPrecision + OpDecorate %268 RelaxedPrecision + OpDecorate %269 RelaxedPrecision + OpDecorate %270 RelaxedPrecision + OpDecorate %278 RelaxedPrecision + OpDecorate %279 RelaxedPrecision + OpDecorate %280 RelaxedPrecision + OpDecorate %288 RelaxedPrecision + OpDecorate %289 RelaxedPrecision + OpDecorate %290 RelaxedPrecision + OpDecorate %298 RelaxedPrecision + OpDecorate %299 RelaxedPrecision + OpDecorate %300 RelaxedPrecision + OpDecorate %309 RelaxedPrecision + OpDecorate %310 RelaxedPrecision + OpDecorate %311 RelaxedPrecision + OpDecorate %312 RelaxedPrecision + OpDecorate %313 RelaxedPrecision + OpDecorate %319 RelaxedPrecision + OpDecorate %320 RelaxedPrecision + OpDecorate %321 RelaxedPrecision + OpDecorate %322 RelaxedPrecision + OpDecorate %323 RelaxedPrecision + OpDecorate %324 RelaxedPrecision + OpDecorate %325 RelaxedPrecision + OpDecorate %327 BuiltIn FragCoord + OpMemberDecorate %331 0 Offset 0 + OpDecorate %331 Block + OpDecorate %333 DescriptorSet 0 + OpDecorate %333 Binding 1 + OpDecorate %339 RelaxedPrecision + OpDecorate %340 RelaxedPrecision + OpDecorate %347 RelaxedPrecision + OpDecorate %348 RelaxedPrecision + OpDecorate %349 RelaxedPrecision + OpDecorate %351 RelaxedPrecision + OpDecorate %352 RelaxedPrecision + OpDecorate %353 RelaxedPrecision + OpDecorate %354 RelaxedPrecision + OpDecorate %356 RelaxedPrecision + OpDecorate %363 Location 0 + OpDecorate %364 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 2 + %20 = OpConstant %10 4 + %21 = OpTypeBool + %32 = OpConstant %10 0 + %61 = OpTypeFloat 32 + %62 = OpTypeVector %61 2 + %63 = OpTypeMatrix %62 2 + %64 = OpTypePointer Private %63 + %65 = OpVariable %64 Private + %68 = OpTypeStruct %61 + %69 = OpTypePointer Uniform %68 + %70 = OpVariable %69 Uniform + %71 = OpTypePointer Uniform %61 + %74 = OpTypePointer Private %61 + %77 = OpTypeVector %61 3 + %78 = OpTypeMatrix %77 2 + %79 = OpTypePointer Private %78 + %80 = OpVariable %79 Private + %87 = OpTypeVector %61 4 + %88 = OpTypeMatrix %87 2 + %89 = OpTypePointer Private %88 + %90 = OpVariable %89 Private + %97 = OpTypeMatrix %62 3 + %98 = OpTypePointer Private %97 + %99 = OpVariable %98 Private + %106 = OpTypeMatrix %77 3 + %107 = OpTypePointer Private %106 + %108 = OpVariable %107 Private + %115 = OpTypeMatrix %87 3 + %116 = OpTypePointer Private %115 + %117 = OpVariable %116 Private + %124 = OpTypeMatrix %62 4 + %125 = OpTypePointer Private %124 + %126 = OpVariable %125 Private + %133 = OpTypeMatrix %77 4 + %134 = OpTypePointer Private %133 + %135 = OpVariable %134 Private + %142 = OpTypeMatrix %87 4 + %143 = OpTypePointer Private %142 + %144 = OpVariable %143 Private + %153 = OpConstant %10 1 + %158 = OpConstant %6 1 + %181 = OpConstant %6 9 + %182 = OpTypeArray %61 %181 + %183 = OpTypePointer Function %182 + %186 = OpConstant %61 0 + %187 = OpTypePointer Function %61 + %314 = OpConstant %61 16 + %326 = OpTypePointer Input %87 + %327 = OpVariable %326 Input + %328 = OpTypePointer Input %61 + %331 = OpTypeStruct %62 + %332 = OpTypePointer Uniform %331 + %333 = OpVariable %332 Uniform + %336 = OpConstant %61 3 + %350 = OpConstant %10 3 + %357 = OpConstant %10 9 + %362 = OpTypePointer Output %87 + %363 = OpVariable %362 Output + %368 = OpConstant %61 1 + %374 = OpConstantComposite %87 %186 %186 %186 %368 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %23 = OpVariable %11 Function + %31 = OpVariable %11 Function + %41 = OpVariable %11 Function + %164 = OpVariable %11 Function + %165 = OpVariable %11 Function + %173 = OpVariable %11 Function + %184 = OpVariable %183 Function + %189 = OpVariable %11 Function + %198 = OpVariable %11 Function + %325 = OpVariable %11 Function + %340 = OpVariable %11 Function + %348 = OpVariable %11 Function + OpStore %8 %9 + OpStore %12 %13 + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + %19 = OpLoad %10 %12 + %22 = OpSLessThanEqual %21 %19 %20 + OpBranchConditional %22 %15 %16 + %15 = OpLabel + OpStore %23 %13 + OpBranch %24 + %24 = OpLabel + OpLoopMerge %26 %27 None + OpBranch %28 + %28 = OpLabel + %29 = OpLoad %10 %23 + %30 = OpSLessThanEqual %21 %29 %20 + OpBranchConditional %30 %25 %26 + %25 = OpLabel + OpStore %31 %32 + OpBranch %33 + %33 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %38 = OpLoad %10 %31 + %39 = OpLoad %10 %12 + %40 = OpSLessThan %21 %38 %39 + OpBranchConditional %40 %34 %35 + %34 = OpLabel + OpStore %41 %32 + OpBranch %42 + %42 = OpLabel + OpLoopMerge %44 %45 None + OpBranch %46 + %46 = OpLabel + %47 = OpLoad %10 %41 + %48 = OpLoad %10 %23 + %49 = OpSLessThan %21 %47 %48 + OpBranchConditional %49 %43 %44 + %43 = OpLabel + %50 = OpLoad %6 %8 + OpSelectionMerge %60 None + OpSwitch %50 %60 0 %51 1 %52 2 %53 3 %54 4 %55 5 %56 6 %57 7 %58 8 %59 + %51 = OpLabel + %66 = OpLoad %10 %31 + %67 = OpLoad %10 %41 + %72 = OpAccessChain %71 %70 %32 + %73 = OpLoad %61 %72 + %75 = OpAccessChain %74 %65 %66 %67 + OpStore %75 %73 + OpBranch %60 + %52 = OpLabel + %81 = OpLoad %10 %31 + %82 = OpLoad %10 %41 + %83 = OpAccessChain %71 %70 %32 + %84 = OpLoad %61 %83 + %85 = OpAccessChain %74 %80 %81 %82 + OpStore %85 %84 + OpBranch %60 + %53 = OpLabel + %91 = OpLoad %10 %31 + %92 = OpLoad %10 %41 + %93 = OpAccessChain %71 %70 %32 + %94 = OpLoad %61 %93 + %95 = OpAccessChain %74 %90 %91 %92 + OpStore %95 %94 + OpBranch %60 + %54 = OpLabel + %100 = OpLoad %10 %31 + %101 = OpLoad %10 %41 + %102 = OpAccessChain %71 %70 %32 + %103 = OpLoad %61 %102 + %104 = OpAccessChain %74 %99 %100 %101 + OpStore %104 %103 + OpBranch %60 + %55 = OpLabel + %109 = OpLoad %10 %31 + %110 = OpLoad %10 %41 + %111 = OpAccessChain %71 %70 %32 + %112 = OpLoad %61 %111 + %113 = OpAccessChain %74 %108 %109 %110 + OpStore %113 %112 + OpBranch %60 + %56 = OpLabel + %118 = OpLoad %10 %31 + %119 = OpLoad %10 %41 + %120 = OpAccessChain %71 %70 %32 + %121 = OpLoad %61 %120 + %122 = OpAccessChain %74 %117 %118 %119 + OpStore %122 %121 + OpBranch %60 + %57 = OpLabel + %127 = OpLoad %10 %31 + %128 = OpLoad %10 %41 + %129 = OpAccessChain %71 %70 %32 + %130 = OpLoad %61 %129 + %131 = OpAccessChain %74 %126 %127 %128 + OpStore %131 %130 + OpBranch %60 + %58 = OpLabel + %136 = OpLoad %10 %31 + %137 = OpLoad %10 %41 + %138 = OpAccessChain %71 %70 %32 + %139 = OpLoad %61 %138 + %140 = OpAccessChain %74 %135 %136 %137 + OpStore %140 %139 + OpBranch %60 + %59 = OpLabel + %145 = OpLoad %10 %31 + %146 = OpLoad %10 %41 + %147 = OpAccessChain %71 %70 %32 + %148 = OpLoad %61 %147 + %149 = OpAccessChain %74 %144 %145 %146 + OpStore %149 %148 + OpBranch %60 + %60 = OpLabel + OpBranch %45 + %45 = OpLabel + %152 = OpLoad %10 %41 + %154 = OpIAdd %10 %152 %153 + OpStore %41 %154 + OpBranch %42 + %44 = OpLabel + OpBranch %36 + %36 = OpLabel + %155 = OpLoad %10 %31 + %156 = OpIAdd %10 %155 %153 + OpStore %31 %156 + OpBranch %33 + %35 = OpLabel + %157 = OpLoad %6 %8 + %159 = OpIAdd %6 %157 %158 + OpStore %8 %159 + OpBranch %27 + %27 = OpLabel + %160 = OpLoad %10 %23 + %161 = OpIAdd %10 %160 %153 + OpStore %23 %161 + OpBranch %24 + %26 = OpLabel + OpBranch %17 + %17 = OpLabel + %162 = OpLoad %10 %12 + %163 = OpIAdd %10 %162 %153 + OpStore %12 %163 + OpBranch %14 + %16 = OpLabel + OpStore %164 %32 + OpStore %165 %13 + OpBranch %166 + %166 = OpLabel + OpLoopMerge %168 %169 None + OpBranch %170 + %170 = OpLabel + %171 = OpLoad %10 %165 + %172 = OpSLessThanEqual %21 %171 %20 + OpBranchConditional %172 %167 %168 + %167 = OpLabel + OpStore %173 %13 + OpBranch %174 + %174 = OpLabel + OpLoopMerge %176 %177 None + OpBranch %178 + %178 = OpLabel + %179 = OpLoad %10 %173 + %180 = OpSLessThanEqual %21 %179 %20 + OpBranchConditional %180 %175 %176 + %175 = OpLabel + %185 = OpLoad %10 %164 + %188 = OpAccessChain %187 %184 %185 + OpStore %188 %186 + OpStore %189 %32 + OpBranch %190 + %190 = OpLabel + OpLoopMerge %192 %193 None + OpBranch %194 + %194 = OpLabel + %195 = OpLoad %10 %189 + %196 = OpLoad %10 %165 + %197 = OpSLessThan %21 %195 %196 + OpBranchConditional %197 %191 %192 + %191 = OpLabel + OpStore %198 %32 + OpBranch %199 + %199 = OpLabel + OpLoopMerge %201 %202 None + OpBranch %203 + %203 = OpLabel + %204 = OpLoad %10 %198 + %205 = OpLoad %10 %173 + %206 = OpSLessThan %21 %204 %205 + OpBranchConditional %206 %200 %201 + %200 = OpLabel + %207 = OpLoad %10 %164 + OpSelectionMerge %217 None + OpSwitch %207 %217 0 %208 1 %209 2 %210 3 %211 4 %212 5 %213 6 %214 7 %215 8 %216 + %208 = OpLabel + %218 = OpLoad %10 %164 + %219 = OpLoad %10 %189 + %220 = OpLoad %10 %198 + %221 = OpAccessChain %74 %65 %219 %220 + %222 = OpLoad %61 %221 + %223 = OpAccessChain %187 %184 %218 + %224 = OpLoad %61 %223 + %225 = OpFAdd %61 %224 %222 + %226 = OpAccessChain %187 %184 %218 + OpStore %226 %225 + OpBranch %217 + %209 = OpLabel + %228 = OpLoad %10 %164 + %229 = OpLoad %10 %189 + %230 = OpLoad %10 %198 + %231 = OpAccessChain %74 %80 %229 %230 + %232 = OpLoad %61 %231 + %233 = OpAccessChain %187 %184 %228 + %234 = OpLoad %61 %233 + %235 = OpFAdd %61 %234 %232 + %236 = OpAccessChain %187 %184 %228 + OpStore %236 %235 + OpBranch %217 + %210 = OpLabel + %238 = OpLoad %10 %164 + %239 = OpLoad %10 %189 + %240 = OpLoad %10 %198 + %241 = OpAccessChain %74 %90 %239 %240 + %242 = OpLoad %61 %241 + %243 = OpAccessChain %187 %184 %238 + %244 = OpLoad %61 %243 + %245 = OpFAdd %61 %244 %242 + %246 = OpAccessChain %187 %184 %238 + OpStore %246 %245 + OpBranch %217 + %211 = OpLabel + %248 = OpLoad %10 %164 + %249 = OpLoad %10 %189 + %250 = OpLoad %10 %198 + %251 = OpAccessChain %74 %99 %249 %250 + %252 = OpLoad %61 %251 + %253 = OpAccessChain %187 %184 %248 + %254 = OpLoad %61 %253 + %255 = OpFAdd %61 %254 %252 + %256 = OpAccessChain %187 %184 %248 + OpStore %256 %255 + OpBranch %217 + %212 = OpLabel + %258 = OpLoad %10 %164 + %259 = OpLoad %10 %189 + %260 = OpLoad %10 %198 + %261 = OpAccessChain %74 %108 %259 %260 + %262 = OpLoad %61 %261 + %263 = OpAccessChain %187 %184 %258 + %264 = OpLoad %61 %263 + %265 = OpFAdd %61 %264 %262 + %266 = OpAccessChain %187 %184 %258 + OpStore %266 %265 + OpBranch %217 + %213 = OpLabel + %268 = OpLoad %10 %164 + %269 = OpLoad %10 %189 + %270 = OpLoad %10 %198 + %271 = OpAccessChain %74 %117 %269 %270 + %272 = OpLoad %61 %271 + %273 = OpAccessChain %187 %184 %268 + %274 = OpLoad %61 %273 + %275 = OpFAdd %61 %274 %272 + %276 = OpAccessChain %187 %184 %268 + OpStore %276 %275 + OpBranch %217 + %214 = OpLabel + %278 = OpLoad %10 %164 + %279 = OpLoad %10 %189 + %280 = OpLoad %10 %198 + %281 = OpAccessChain %74 %126 %279 %280 + %282 = OpLoad %61 %281 + %283 = OpAccessChain %187 %184 %278 + %284 = OpLoad %61 %283 + %285 = OpFAdd %61 %284 %282 + %286 = OpAccessChain %187 %184 %278 + OpStore %286 %285 + OpBranch %217 + %215 = OpLabel + %288 = OpLoad %10 %164 + %289 = OpLoad %10 %189 + %290 = OpLoad %10 %198 + %291 = OpAccessChain %74 %135 %289 %290 + %292 = OpLoad %61 %291 + %293 = OpAccessChain %187 %184 %288 + %294 = OpLoad %61 %293 + %295 = OpFAdd %61 %294 %292 + %296 = OpAccessChain %187 %184 %288 + OpStore %296 %295 + OpBranch %217 + %216 = OpLabel + %298 = OpLoad %10 %164 + %299 = OpLoad %10 %189 + %300 = OpLoad %10 %198 + %301 = OpAccessChain %74 %144 %299 %300 + %302 = OpLoad %61 %301 + %303 = OpAccessChain %187 %184 %298 + %304 = OpLoad %61 %303 + %305 = OpFAdd %61 %304 %302 + %306 = OpAccessChain %187 %184 %298 + OpStore %306 %305 + OpBranch %217 + %217 = OpLabel + OpBranch %202 + %202 = OpLabel + %309 = OpLoad %10 %198 + %310 = OpIAdd %10 %309 %153 + OpStore %198 %310 + OpBranch %199 + %201 = OpLabel + OpBranch %193 + %193 = OpLabel + %311 = OpLoad %10 %189 + %312 = OpIAdd %10 %311 %153 + OpStore %189 %312 + OpBranch %190 + %192 = OpLabel + %313 = OpLoad %10 %164 + %315 = OpAccessChain %187 %184 %313 + %316 = OpLoad %61 %315 + %317 = OpFDiv %61 %316 %314 + %318 = OpAccessChain %187 %184 %313 + OpStore %318 %317 + %319 = OpLoad %10 %164 + %320 = OpIAdd %10 %319 %153 + OpStore %164 %320 + OpBranch %177 + %177 = OpLabel + %321 = OpLoad %10 %173 + %322 = OpIAdd %10 %321 %153 + OpStore %173 %322 + OpBranch %174 + %176 = OpLabel + OpBranch %169 + %169 = OpLabel + %323 = OpLoad %10 %165 + %324 = OpIAdd %10 %323 %153 + OpStore %165 %324 + OpBranch %166 + %168 = OpLabel + %329 = OpAccessChain %328 %327 %9 + %330 = OpLoad %61 %329 + %334 = OpAccessChain %71 %333 %32 %9 + %335 = OpLoad %61 %334 + %337 = OpFDiv %61 %335 %336 + %338 = OpFDiv %61 %330 %337 + %339 = OpConvertFToS %10 %338 + OpStore %325 %339 + %341 = OpAccessChain %328 %327 %158 + %342 = OpLoad %61 %341 + %343 = OpAccessChain %71 %333 %32 %9 + %344 = OpLoad %61 %343 + %345 = OpFDiv %61 %344 %336 + %346 = OpFDiv %61 %342 %345 + %347 = OpConvertFToS %10 %346 + OpStore %340 %347 + %349 = OpLoad %10 %340 + %351 = OpIMul %10 %349 %350 + %352 = OpLoad %10 %325 + %353 = OpIAdd %10 %351 %352 + OpStore %348 %353 + %354 = OpLoad %10 %348 + %355 = OpSGreaterThan %21 %354 %32 + %356 = OpLoad %10 %348 + %358 = OpSLessThan %21 %356 %357 + %359 = OpLogicalAnd %21 %355 %358 + OpSelectionMerge %361 None + OpBranchConditional %359 %360 %373 + %360 = OpLabel + %364 = OpLoad %10 %348 + %365 = OpAccessChain %187 %184 %364 + %366 = OpLoad %61 %365 + %367 = OpCompositeConstruct %77 %366 %366 %366 + %369 = OpCompositeExtract %61 %367 0 + %370 = OpCompositeExtract %61 %367 1 + %371 = OpCompositeExtract %61 %367 2 + %372 = OpCompositeConstruct %87 %369 %370 %371 %368 + OpStore %363 %372 + OpBranch %361 + %373 = OpLabel + OpStore %363 %374 + OpBranch %361 + %361 = OpLabel + OpReturn + OpFunctionEnd + )"; + +// The SPIR-V comes from the following GLSL: +// +// #version 310 es +// precision highp float; +// +// layout(location = 0) out vec4 color; +// +// void main() +// { +// color = vec4(1.0, 0.0, 0.0, 1.0); +// } + +const std::string kTestShader5 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %9 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "color" + OpDecorate %9 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Output %7 + %9 = OpVariable %8 Output + %10 = OpConstant %6 1 + %11 = OpConstant %6 0 + %12 = OpConstantComposite %7 %10 %11 %11 %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpStore %9 %12 + OpReturn + OpFunctionEnd + )"; + +// Some miscellaneous SPIR-V. + +const std::string kTestShader6 = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + OpReturn + OpFunctionEnd + )"; + +void AddConstantUniformFact(protobufs::FactSequence* facts, + uint32_t descriptor_set, uint32_t binding, + std::vector&& indices, uint32_t value) { + protobufs::FactConstantUniform fact; + *fact.mutable_uniform_buffer_element_descriptor() = + MakeUniformBufferElementDescriptor(descriptor_set, binding, + std::move(indices)); + *fact.mutable_constant_word()->Add() = value; + protobufs::Fact temp; + *temp.mutable_constant_uniform_fact() = fact; + *facts->mutable_fact()->Add() = temp; +} + +// Reinterpret the bits of |value| as a 32-bit unsigned int +uint32_t FloatBitsAsUint(float value) { + uint32_t result; + memcpy(&result, &value, sizeof(float)); + return result; +} + +// Assembles the given |shader| text, and then runs the fuzzer |num_runs| +// times, using successive seeds starting from |initial_seed|. Checks that +// the binary produced after each fuzzer run is valid, and that replaying +// the transformations that were applied during fuzzing leads to an +// identical binary. +void RunFuzzerAndReplayer(const std::string& shader, + const protobufs::FactSequence& initial_facts, + uint32_t initial_seed, uint32_t num_runs) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + + std::vector binary_in; + SpirvTools t(env); + t.SetMessageConsumer(kConsoleMessageConsumer); + ASSERT_TRUE(t.Assemble(shader, &binary_in, kFuzzAssembleOption)); + ASSERT_TRUE(t.Validate(binary_in)); + + std::vector donor_suppliers; + for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3, &kTestShader4, + &kTestShader5, &kTestShader6}) { + donor_suppliers.emplace_back([donor]() { + return BuildModule(env, kConsoleMessageConsumer, *donor, + kFuzzAssembleOption); + }); + } + + std::vector strategies{ + Fuzzer::RepeatedPassStrategy::kSimple, + Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations, + Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations}; + uint32_t strategy_index = 0; + for (uint32_t seed = initial_seed; seed < initial_seed + num_runs; seed++) { + spvtools::ValidatorOptions validator_options; + // Every 4th time we run the fuzzer, enable all fuzzer passes. + bool enable_all_passes = (seed % 4) == 0; + auto fuzzer_result = + Fuzzer(env, kConsoleMessageConsumer, binary_in, initial_facts, + donor_suppliers, MakeUnique(seed), + enable_all_passes, strategies[strategy_index], true, + validator_options) + .Run(); + + // Cycle the repeated pass strategy so that we try a different one next time + // we run the fuzzer. + strategy_index = + (strategy_index + 1) % static_cast(strategies.size()); + + ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result.status); + ASSERT_TRUE(t.Validate(fuzzer_result.transformed_binary)); + + auto replayer_result = + Replayer( + env, kConsoleMessageConsumer, binary_in, initial_facts, + fuzzer_result.applied_transformations, + static_cast( + fuzzer_result.applied_transformations.transformation_size()), + false, validator_options) + .Run(); + ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, + replayer_result.status); + + // After replaying the transformations applied by the fuzzer, exactly those + // transformations should have been applied, and the binary resulting from + // replay should be identical to that which resulted from fuzzing. + std::string fuzzer_transformations_string; + std::string replayer_transformations_string; + fuzzer_result.applied_transformations.SerializeToString( + &fuzzer_transformations_string); + replayer_result.applied_transformations.SerializeToString( + &replayer_transformations_string); + ASSERT_EQ(fuzzer_transformations_string, replayer_transformations_string); + ASSERT_TRUE(IsEqual(env, fuzzer_result.transformed_binary, + replayer_result.transformed_module.get())); + } +} + +TEST(FuzzerReplayerTest, Miscellaneous1) { + // Do some fuzzer runs, starting from an initial seed of 0 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader1, protobufs::FactSequence(), 0, + kNumFuzzerRuns); +} + +TEST(FuzzerReplayerTest, Miscellaneous2) { + // Do some fuzzer runs, starting from an initial seed of 10 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader2, protobufs::FactSequence(), 10, + kNumFuzzerRuns); +} + +TEST(FuzzerReplayerTest, Miscellaneous3) { + // Add the facts "resolution.x == 250" and "resolution.y == 100". + protobufs::FactSequence facts; + AddConstantUniformFact(&facts, 0, 0, {0, 0}, 250); + AddConstantUniformFact(&facts, 0, 0, {0, 1}, 100); + + // Do some fuzzer runs, starting from an initial seed of 94 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader3, facts, 94, kNumFuzzerRuns); +} + +TEST(FuzzerReplayerTest, Miscellaneous4) { + // Add the facts: + // - "one == 1.0" + // - "resolution.y == 256.0", + protobufs::FactSequence facts; + AddConstantUniformFact(&facts, 0, 0, {0}, FloatBitsAsUint(1.0)); + AddConstantUniformFact(&facts, 0, 1, {0, 0}, FloatBitsAsUint(256.0)); + AddConstantUniformFact(&facts, 0, 1, {0, 1}, FloatBitsAsUint(256.0)); + + // Do some fuzzer runs, starting from an initial seed of 14 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader4, facts, 14, kNumFuzzerRuns); +} + +TEST(FuzzerReplayerTest, Miscellaneous5) { + // Do some fuzzer runs, starting from an initial seed of 29 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader5, protobufs::FactSequence(), 29, + kNumFuzzerRuns); +} + +TEST(FuzzerReplayerTest, Miscellaneous6) { + // Do some fuzzer runs, starting from an initial seed of 57 (seed value chosen + // arbitrarily). + RunFuzzerAndReplayer(kTestShader6, protobufs::FactSequence(), 57, + kNumFuzzerRuns); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp b/third_party/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp new file mode 100644 index 0000000..6d9dad3 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/fuzzer_shrinker_test.cpp @@ -0,0 +1,1149 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/fuzzer.h" +#include "source/fuzz/shrinker.h" + +#include +#include + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +// The following SPIR-V came from this GLSL: +// +// #version 310 es +// +// void foo() { +// int x; +// x = 2; +// for (int i = 0; i < 100; i++) { +// x += i; +// x = x * 2; +// } +// return; +// } +// +// void main() { +// foo(); +// for (int i = 0; i < 10; i++) { +// int j = 20; +// while(j > 0) { +// foo(); +// j--; +// } +// do { +// i++; +// } while(i < 4); +// } +// } + +const std::string kTestShader1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "foo(" + OpName %10 "x" + OpName %12 "i" + OpName %33 "i" + OpName %42 "j" + OpDecorate %10 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %23 RelaxedPrecision + OpDecorate %24 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + OpDecorate %28 RelaxedPrecision + OpDecorate %30 RelaxedPrecision + OpDecorate %33 RelaxedPrecision + OpDecorate %39 RelaxedPrecision + OpDecorate %42 RelaxedPrecision + OpDecorate %49 RelaxedPrecision + OpDecorate %52 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %59 RelaxedPrecision + OpDecorate %60 RelaxedPrecision + OpDecorate %63 RelaxedPrecision + OpDecorate %64 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 2 + %13 = OpConstant %8 0 + %20 = OpConstant %8 100 + %21 = OpTypeBool + %29 = OpConstant %8 1 + %40 = OpConstant %8 10 + %43 = OpConstant %8 20 + %61 = OpConstant %8 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %33 = OpVariable %9 Function + %42 = OpVariable %9 Function + %32 = OpFunctionCall %2 %6 + OpStore %33 %13 + OpBranch %34 + %34 = OpLabel + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %39 = OpLoad %8 %33 + %41 = OpSLessThan %21 %39 %40 + OpBranchConditional %41 %35 %36 + %35 = OpLabel + OpStore %42 %43 + OpBranch %44 + %44 = OpLabel + OpLoopMerge %46 %47 None + OpBranch %48 + %48 = OpLabel + %49 = OpLoad %8 %42 + %50 = OpSGreaterThan %21 %49 %13 + OpBranchConditional %50 %45 %46 + %45 = OpLabel + %51 = OpFunctionCall %2 %6 + %52 = OpLoad %8 %42 + %53 = OpISub %8 %52 %29 + OpStore %42 %53 + OpBranch %47 + %47 = OpLabel + OpBranch %44 + %46 = OpLabel + OpBranch %54 + %54 = OpLabel + OpLoopMerge %56 %57 None + OpBranch %55 + %55 = OpLabel + %58 = OpLoad %8 %33 + %59 = OpIAdd %8 %58 %29 + OpStore %33 %59 + OpBranch %57 + %57 = OpLabel + %60 = OpLoad %8 %33 + %62 = OpSLessThan %21 %60 %61 + OpBranchConditional %62 %54 %56 + %56 = OpLabel + OpBranch %37 + %37 = OpLabel + %63 = OpLoad %8 %33 + %64 = OpIAdd %8 %63 %29 + OpStore %33 %64 + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + %12 = OpVariable %9 Function + OpStore %10 %11 + OpStore %12 %13 + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + %19 = OpLoad %8 %12 + %22 = OpSLessThan %21 %19 %20 + OpBranchConditional %22 %15 %16 + %15 = OpLabel + %23 = OpLoad %8 %12 + %24 = OpLoad %8 %10 + %25 = OpIAdd %8 %24 %23 + OpStore %10 %25 + %26 = OpLoad %8 %10 + %27 = OpIMul %8 %26 %11 + OpStore %10 %27 + OpBranch %17 + %17 = OpLabel + %28 = OpLoad %8 %12 + %30 = OpIAdd %8 %28 %29 + OpStore %12 %30 + OpBranch %14 + %16 = OpLabel + OpReturn + OpFunctionEnd + + )"; + +// The following SPIR-V came from this GLSL, which was then optimized using +// spirv-opt with the -O argument: +// +// #version 310 es +// +// precision highp float; +// +// layout(location = 0) out vec4 _GLF_color; +// +// layout(set = 0, binding = 0) uniform buf0 { +// vec2 injectionSwitch; +// }; +// layout(set = 0, binding = 1) uniform buf1 { +// vec2 resolution; +// }; +// bool checkSwap(float a, float b) +// { +// return gl_FragCoord.y < resolution.y / 2.0 ? a > b : a < b; +// } +// void main() +// { +// float data[10]; +// for(int i = 0; i < 10; i++) +// { +// data[i] = float(10 - i) * injectionSwitch.y; +// } +// for(int i = 0; i < 9; i++) +// { +// for(int j = 0; j < 10; j++) +// { +// if(j < i + 1) +// { +// continue; +// } +// bool doSwap = checkSwap(data[i], data[j]); +// if(doSwap) +// { +// float temp = data[i]; +// data[i] = data[j]; +// data[j] = temp; +// } +// } +// } +// if(gl_FragCoord.x < resolution.x / 2.0) +// { +// _GLF_color = vec4(data[0] / 10.0, data[5] / 10.0, data[9] / 10.0, 1.0); +// } +// else +// { +// _GLF_color = vec4(data[5] / 10.0, data[9] / 10.0, data[0] / 10.0, 1.0); +// } +// } + +const std::string kTestShader2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %16 %139 %25 %68 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %16 "gl_FragCoord" + OpName %23 "buf1" + OpMemberName %23 0 "resolution" + OpName %25 "" + OpName %61 "data" + OpName %66 "buf0" + OpMemberName %66 0 "injectionSwitch" + OpName %68 "" + OpName %139 "_GLF_color" + OpDecorate %16 BuiltIn FragCoord + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 Block + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 1 + OpDecorate %64 RelaxedPrecision + OpMemberDecorate %66 0 Offset 0 + OpDecorate %66 Block + OpDecorate %68 DescriptorSet 0 + OpDecorate %68 Binding 0 + OpDecorate %75 RelaxedPrecision + OpDecorate %95 RelaxedPrecision + OpDecorate %126 RelaxedPrecision + OpDecorate %128 RelaxedPrecision + OpDecorate %139 Location 0 + OpDecorate %182 RelaxedPrecision + OpDecorate %183 RelaxedPrecision + OpDecorate %184 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeBool + %14 = OpTypeVector %6 4 + %15 = OpTypePointer Input %14 + %16 = OpVariable %15 Input + %17 = OpTypeInt 32 0 + %18 = OpConstant %17 1 + %19 = OpTypePointer Input %6 + %22 = OpTypeVector %6 2 + %23 = OpTypeStruct %22 + %24 = OpTypePointer Uniform %23 + %25 = OpVariable %24 Uniform + %26 = OpTypeInt 32 1 + %27 = OpConstant %26 0 + %28 = OpTypePointer Uniform %6 + %56 = OpConstant %26 10 + %58 = OpConstant %17 10 + %59 = OpTypeArray %6 %58 + %60 = OpTypePointer Function %59 + %66 = OpTypeStruct %22 + %67 = OpTypePointer Uniform %66 + %68 = OpVariable %67 Uniform + %74 = OpConstant %26 1 + %83 = OpConstant %26 9 + %129 = OpConstant %17 0 + %138 = OpTypePointer Output %14 + %139 = OpVariable %138 Output + %144 = OpConstant %26 5 + %151 = OpConstant %6 1 + %194 = OpConstant %6 0.5 + %195 = OpConstant %6 0.100000001 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %61 = OpVariable %60 Function + OpBranch %50 + %50 = OpLabel + %182 = OpPhi %26 %27 %5 %75 %51 + %57 = OpSLessThan %8 %182 %56 + OpLoopMerge %52 %51 None + OpBranchConditional %57 %51 %52 + %51 = OpLabel + %64 = OpISub %26 %56 %182 + %65 = OpConvertSToF %6 %64 + %69 = OpAccessChain %28 %68 %27 %18 + %70 = OpLoad %6 %69 + %71 = OpFMul %6 %65 %70 + %72 = OpAccessChain %7 %61 %182 + OpStore %72 %71 + %75 = OpIAdd %26 %182 %74 + OpBranch %50 + %52 = OpLabel + OpBranch %77 + %77 = OpLabel + %183 = OpPhi %26 %27 %52 %128 %88 + %84 = OpSLessThan %8 %183 %83 + OpLoopMerge %79 %88 None + OpBranchConditional %84 %78 %79 + %78 = OpLabel + OpBranch %86 + %86 = OpLabel + %184 = OpPhi %26 %27 %78 %126 %89 + %92 = OpSLessThan %8 %184 %56 + OpLoopMerge %1000 %89 None + OpBranchConditional %92 %87 %1000 + %87 = OpLabel + %95 = OpIAdd %26 %183 %74 + %96 = OpSLessThan %8 %184 %95 + OpSelectionMerge %98 None + OpBranchConditional %96 %97 %98 + %97 = OpLabel + OpBranch %89 + %98 = OpLabel + %104 = OpAccessChain %7 %61 %183 + %105 = OpLoad %6 %104 + %107 = OpAccessChain %7 %61 %184 + %108 = OpLoad %6 %107 + %166 = OpAccessChain %19 %16 %18 + %167 = OpLoad %6 %166 + %168 = OpAccessChain %28 %25 %27 %18 + %169 = OpLoad %6 %168 + %170 = OpFMul %6 %169 %194 + %171 = OpFOrdLessThan %8 %167 %170 + OpSelectionMerge %172 None + OpBranchConditional %171 %173 %174 + %173 = OpLabel + %177 = OpFOrdGreaterThan %8 %105 %108 + OpBranch %172 + %174 = OpLabel + %180 = OpFOrdLessThan %8 %105 %108 + OpBranch %172 + %172 = OpLabel + %186 = OpPhi %8 %177 %173 %180 %174 + OpSelectionMerge %112 None + OpBranchConditional %186 %111 %112 + %111 = OpLabel + %116 = OpLoad %6 %104 + %120 = OpLoad %6 %107 + OpStore %104 %120 + OpStore %107 %116 + OpBranch %112 + %112 = OpLabel + OpBranch %89 + %89 = OpLabel + %126 = OpIAdd %26 %184 %74 + OpBranch %86 + %1000 = OpLabel + OpBranch %88 + %88 = OpLabel + %128 = OpIAdd %26 %183 %74 + OpBranch %77 + %79 = OpLabel + %130 = OpAccessChain %19 %16 %129 + %131 = OpLoad %6 %130 + %132 = OpAccessChain %28 %25 %27 %129 + %133 = OpLoad %6 %132 + %134 = OpFMul %6 %133 %194 + %135 = OpFOrdLessThan %8 %131 %134 + OpSelectionMerge %137 None + OpBranchConditional %135 %136 %153 + %136 = OpLabel + %140 = OpAccessChain %7 %61 %27 + %141 = OpLoad %6 %140 + %143 = OpFMul %6 %141 %195 + %145 = OpAccessChain %7 %61 %144 + %146 = OpLoad %6 %145 + %147 = OpFMul %6 %146 %195 + %148 = OpAccessChain %7 %61 %83 + %149 = OpLoad %6 %148 + %150 = OpFMul %6 %149 %195 + %152 = OpCompositeConstruct %14 %143 %147 %150 %151 + OpStore %139 %152 + OpBranch %137 + %153 = OpLabel + %154 = OpAccessChain %7 %61 %144 + %155 = OpLoad %6 %154 + %156 = OpFMul %6 %155 %195 + %157 = OpAccessChain %7 %61 %83 + %158 = OpLoad %6 %157 + %159 = OpFMul %6 %158 %195 + %160 = OpAccessChain %7 %61 %27 + %161 = OpLoad %6 %160 + %162 = OpFMul %6 %161 %195 + %163 = OpCompositeConstruct %14 %156 %159 %162 %151 + OpStore %139 %163 + OpBranch %137 + %137 = OpLabel + OpReturn + OpFunctionEnd + )"; + +// The following SPIR-V came from this GLSL, which was then optimized using +// spirv-opt with the -O argument: +// +// #version 310 es +// +// precision highp float; +// +// layout(location = 0) out vec4 _GLF_color; +// +// layout(set = 0, binding = 0) uniform buf0 { +// vec2 resolution; +// }; +// void main(void) +// { +// float A[50]; +// for( +// int i = 0; +// i < 200; +// i ++ +// ) +// { +// if(i >= int(resolution.x)) +// { +// break; +// } +// if((4 * (i / 4)) == i) +// { +// A[i / 4] = float(i); +// } +// } +// for( +// int i = 0; +// i < 50; +// i ++ +// ) +// { +// if(i < int(gl_FragCoord.x)) +// { +// break; +// } +// if(i > 0) +// { +// A[i] += A[i - 1]; +// } +// } +// if(int(gl_FragCoord.x) < 20) +// { +// _GLF_color = vec4(A[0] / resolution.x, A[4] / resolution.y, 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 40) +// { +// _GLF_color = vec4(A[5] / resolution.x, A[9] / resolution.y, 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 60) +// { +// _GLF_color = vec4(A[10] / resolution.x, A[14] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 80) +// { +// _GLF_color = vec4(A[15] / resolution.x, A[19] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 100) +// { +// _GLF_color = vec4(A[20] / resolution.x, A[24] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 120) +// { +// _GLF_color = vec4(A[25] / resolution.x, A[29] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 140) +// { +// _GLF_color = vec4(A[30] / resolution.x, A[34] / resolution.y, +// 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 160) +// { +// _GLF_color = vec4(A[35] / resolution.x, A[39] / +// resolution.y, 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 180) +// { +// _GLF_color = vec4(A[40] / resolution.x, A[44] / +// resolution.y, 1.0, 1.0); +// } +// else +// if(int(gl_FragCoord.x) < 180) +// { +// _GLF_color = vec4(A[45] / resolution.x, A[49] / +// resolution.y, 1.0, 1.0); +// } +// else +// { +// discard; +// } +// } + +const std::string kTestShader3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %68 %100 %24 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %22 "buf0" + OpMemberName %22 0 "resolution" + OpName %24 "" + OpName %46 "A" + OpName %68 "gl_FragCoord" + OpName %100 "_GLF_color" + OpMemberDecorate %22 0 Offset 0 + OpDecorate %22 Block + OpDecorate %24 DescriptorSet 0 + OpDecorate %24 Binding 0 + OpDecorate %37 RelaxedPrecision + OpDecorate %38 RelaxedPrecision + OpDecorate %55 RelaxedPrecision + OpDecorate %68 BuiltIn FragCoord + OpDecorate %83 RelaxedPrecision + OpDecorate %91 RelaxedPrecision + OpDecorate %100 Location 0 + OpDecorate %302 RelaxedPrecision + OpDecorate %304 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpConstant %6 200 + %17 = OpTypeBool + %20 = OpTypeFloat 32 + %21 = OpTypeVector %20 2 + %22 = OpTypeStruct %21 + %23 = OpTypePointer Uniform %22 + %24 = OpVariable %23 Uniform + %25 = OpTypeInt 32 0 + %26 = OpConstant %25 0 + %27 = OpTypePointer Uniform %20 + %35 = OpConstant %6 4 + %43 = OpConstant %25 50 + %44 = OpTypeArray %20 %43 + %45 = OpTypePointer Function %44 + %51 = OpTypePointer Function %20 + %54 = OpConstant %6 1 + %63 = OpConstant %6 50 + %66 = OpTypeVector %20 4 + %67 = OpTypePointer Input %66 + %68 = OpVariable %67 Input + %69 = OpTypePointer Input %20 + %95 = OpConstant %6 20 + %99 = OpTypePointer Output %66 + %100 = OpVariable %99 Output + %108 = OpConstant %25 1 + %112 = OpConstant %20 1 + %118 = OpConstant %6 40 + %122 = OpConstant %6 5 + %128 = OpConstant %6 9 + %139 = OpConstant %6 60 + %143 = OpConstant %6 10 + %149 = OpConstant %6 14 + %160 = OpConstant %6 80 + %164 = OpConstant %6 15 + %170 = OpConstant %6 19 + %181 = OpConstant %6 100 + %190 = OpConstant %6 24 + %201 = OpConstant %6 120 + %205 = OpConstant %6 25 + %211 = OpConstant %6 29 + %222 = OpConstant %6 140 + %226 = OpConstant %6 30 + %232 = OpConstant %6 34 + %243 = OpConstant %6 160 + %247 = OpConstant %6 35 + %253 = OpConstant %6 39 + %264 = OpConstant %6 180 + %273 = OpConstant %6 44 + %287 = OpConstant %6 45 + %293 = OpConstant %6 49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %46 = OpVariable %45 Function + OpBranch %10 + %10 = OpLabel + %302 = OpPhi %6 %9 %5 %55 %42 + %18 = OpSLessThan %17 %302 %16 + OpLoopMerge %12 %42 None + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %28 = OpAccessChain %27 %24 %9 %26 + %29 = OpLoad %20 %28 + %30 = OpConvertFToS %6 %29 + %31 = OpSGreaterThanEqual %17 %302 %30 + OpSelectionMerge %33 None + OpBranchConditional %31 %32 %33 + %32 = OpLabel + OpBranch %12 + %33 = OpLabel + %37 = OpSDiv %6 %302 %35 + %38 = OpIMul %6 %35 %37 + %40 = OpIEqual %17 %38 %302 + OpBranchConditional %40 %41 %42 + %41 = OpLabel + %50 = OpConvertSToF %20 %302 + %52 = OpAccessChain %51 %46 %37 + OpStore %52 %50 + OpBranch %42 + %42 = OpLabel + %55 = OpIAdd %6 %302 %54 + OpBranch %10 + %12 = OpLabel + OpBranch %57 + %57 = OpLabel + %304 = OpPhi %6 %9 %12 %91 %80 + %64 = OpSLessThan %17 %304 %63 + OpLoopMerge %59 %80 None + OpBranchConditional %64 %58 %59 + %58 = OpLabel + %70 = OpAccessChain %69 %68 %26 + %71 = OpLoad %20 %70 + %72 = OpConvertFToS %6 %71 + %73 = OpSLessThan %17 %304 %72 + OpSelectionMerge %75 None + OpBranchConditional %73 %74 %75 + %74 = OpLabel + OpBranch %59 + %75 = OpLabel + %78 = OpSGreaterThan %17 %304 %9 + OpBranchConditional %78 %79 %80 + %79 = OpLabel + %83 = OpISub %6 %304 %54 + %84 = OpAccessChain %51 %46 %83 + %85 = OpLoad %20 %84 + %86 = OpAccessChain %51 %46 %304 + %87 = OpLoad %20 %86 + %88 = OpFAdd %20 %87 %85 + OpStore %86 %88 + OpBranch %80 + %80 = OpLabel + %91 = OpIAdd %6 %304 %54 + OpBranch %57 + %59 = OpLabel + %92 = OpAccessChain %69 %68 %26 + %93 = OpLoad %20 %92 + %94 = OpConvertFToS %6 %93 + %96 = OpSLessThan %17 %94 %95 + OpSelectionMerge %98 None + OpBranchConditional %96 %97 %114 + %97 = OpLabel + %101 = OpAccessChain %51 %46 %9 + %102 = OpLoad %20 %101 + %103 = OpAccessChain %27 %24 %9 %26 + %104 = OpLoad %20 %103 + %105 = OpFDiv %20 %102 %104 + %106 = OpAccessChain %51 %46 %35 + %107 = OpLoad %20 %106 + %109 = OpAccessChain %27 %24 %9 %108 + %110 = OpLoad %20 %109 + %111 = OpFDiv %20 %107 %110 + %113 = OpCompositeConstruct %66 %105 %111 %112 %112 + OpStore %100 %113 + OpBranch %98 + %114 = OpLabel + %119 = OpSLessThan %17 %94 %118 + OpSelectionMerge %121 None + OpBranchConditional %119 %120 %135 + %120 = OpLabel + %123 = OpAccessChain %51 %46 %122 + %124 = OpLoad %20 %123 + %125 = OpAccessChain %27 %24 %9 %26 + %126 = OpLoad %20 %125 + %127 = OpFDiv %20 %124 %126 + %129 = OpAccessChain %51 %46 %128 + %130 = OpLoad %20 %129 + %131 = OpAccessChain %27 %24 %9 %108 + %132 = OpLoad %20 %131 + %133 = OpFDiv %20 %130 %132 + %134 = OpCompositeConstruct %66 %127 %133 %112 %112 + OpStore %100 %134 + OpBranch %121 + %135 = OpLabel + %140 = OpSLessThan %17 %94 %139 + OpSelectionMerge %142 None + OpBranchConditional %140 %141 %156 + %141 = OpLabel + %144 = OpAccessChain %51 %46 %143 + %145 = OpLoad %20 %144 + %146 = OpAccessChain %27 %24 %9 %26 + %147 = OpLoad %20 %146 + %148 = OpFDiv %20 %145 %147 + %150 = OpAccessChain %51 %46 %149 + %151 = OpLoad %20 %150 + %152 = OpAccessChain %27 %24 %9 %108 + %153 = OpLoad %20 %152 + %154 = OpFDiv %20 %151 %153 + %155 = OpCompositeConstruct %66 %148 %154 %112 %112 + OpStore %100 %155 + OpBranch %142 + %156 = OpLabel + %161 = OpSLessThan %17 %94 %160 + OpSelectionMerge %163 None + OpBranchConditional %161 %162 %177 + %162 = OpLabel + %165 = OpAccessChain %51 %46 %164 + %166 = OpLoad %20 %165 + %167 = OpAccessChain %27 %24 %9 %26 + %168 = OpLoad %20 %167 + %169 = OpFDiv %20 %166 %168 + %171 = OpAccessChain %51 %46 %170 + %172 = OpLoad %20 %171 + %173 = OpAccessChain %27 %24 %9 %108 + %174 = OpLoad %20 %173 + %175 = OpFDiv %20 %172 %174 + %176 = OpCompositeConstruct %66 %169 %175 %112 %112 + OpStore %100 %176 + OpBranch %163 + %177 = OpLabel + %182 = OpSLessThan %17 %94 %181 + OpSelectionMerge %184 None + OpBranchConditional %182 %183 %197 + %183 = OpLabel + %185 = OpAccessChain %51 %46 %95 + %186 = OpLoad %20 %185 + %187 = OpAccessChain %27 %24 %9 %26 + %188 = OpLoad %20 %187 + %189 = OpFDiv %20 %186 %188 + %191 = OpAccessChain %51 %46 %190 + %192 = OpLoad %20 %191 + %193 = OpAccessChain %27 %24 %9 %108 + %194 = OpLoad %20 %193 + %195 = OpFDiv %20 %192 %194 + %196 = OpCompositeConstruct %66 %189 %195 %112 %112 + OpStore %100 %196 + OpBranch %184 + %197 = OpLabel + %202 = OpSLessThan %17 %94 %201 + OpSelectionMerge %204 None + OpBranchConditional %202 %203 %218 + %203 = OpLabel + %206 = OpAccessChain %51 %46 %205 + %207 = OpLoad %20 %206 + %208 = OpAccessChain %27 %24 %9 %26 + %209 = OpLoad %20 %208 + %210 = OpFDiv %20 %207 %209 + %212 = OpAccessChain %51 %46 %211 + %213 = OpLoad %20 %212 + %214 = OpAccessChain %27 %24 %9 %108 + %215 = OpLoad %20 %214 + %216 = OpFDiv %20 %213 %215 + %217 = OpCompositeConstruct %66 %210 %216 %112 %112 + OpStore %100 %217 + OpBranch %204 + %218 = OpLabel + %223 = OpSLessThan %17 %94 %222 + OpSelectionMerge %225 None + OpBranchConditional %223 %224 %239 + %224 = OpLabel + %227 = OpAccessChain %51 %46 %226 + %228 = OpLoad %20 %227 + %229 = OpAccessChain %27 %24 %9 %26 + %230 = OpLoad %20 %229 + %231 = OpFDiv %20 %228 %230 + %233 = OpAccessChain %51 %46 %232 + %234 = OpLoad %20 %233 + %235 = OpAccessChain %27 %24 %9 %108 + %236 = OpLoad %20 %235 + %237 = OpFDiv %20 %234 %236 + %238 = OpCompositeConstruct %66 %231 %237 %112 %112 + OpStore %100 %238 + OpBranch %225 + %239 = OpLabel + %244 = OpSLessThan %17 %94 %243 + OpSelectionMerge %246 None + OpBranchConditional %244 %245 %260 + %245 = OpLabel + %248 = OpAccessChain %51 %46 %247 + %249 = OpLoad %20 %248 + %250 = OpAccessChain %27 %24 %9 %26 + %251 = OpLoad %20 %250 + %252 = OpFDiv %20 %249 %251 + %254 = OpAccessChain %51 %46 %253 + %255 = OpLoad %20 %254 + %256 = OpAccessChain %27 %24 %9 %108 + %257 = OpLoad %20 %256 + %258 = OpFDiv %20 %255 %257 + %259 = OpCompositeConstruct %66 %252 %258 %112 %112 + OpStore %100 %259 + OpBranch %246 + %260 = OpLabel + %265 = OpSLessThan %17 %94 %264 + OpSelectionMerge %267 None + OpBranchConditional %265 %266 %280 + %266 = OpLabel + %268 = OpAccessChain %51 %46 %118 + %269 = OpLoad %20 %268 + %270 = OpAccessChain %27 %24 %9 %26 + %271 = OpLoad %20 %270 + %272 = OpFDiv %20 %269 %271 + %274 = OpAccessChain %51 %46 %273 + %275 = OpLoad %20 %274 + %276 = OpAccessChain %27 %24 %9 %108 + %277 = OpLoad %20 %276 + %278 = OpFDiv %20 %275 %277 + %279 = OpCompositeConstruct %66 %272 %278 %112 %112 + OpStore %100 %279 + OpBranch %267 + %280 = OpLabel + OpSelectionMerge %285 None + OpBranchConditional %265 %285 %300 + %285 = OpLabel + %288 = OpAccessChain %51 %46 %287 + %289 = OpLoad %20 %288 + %290 = OpAccessChain %27 %24 %9 %26 + %291 = OpLoad %20 %290 + %292 = OpFDiv %20 %289 %291 + %294 = OpAccessChain %51 %46 %293 + %295 = OpLoad %20 %294 + %296 = OpAccessChain %27 %24 %9 %108 + %297 = OpLoad %20 %296 + %298 = OpFDiv %20 %295 %297 + %299 = OpCompositeConstruct %66 %292 %298 %112 %112 + OpStore %100 %299 + OpBranch %267 + %300 = OpLabel + OpKill + %267 = OpLabel + OpBranch %246 + %246 = OpLabel + OpBranch %225 + %225 = OpLabel + OpBranch %204 + %204 = OpLabel + OpBranch %184 + %184 = OpLabel + OpBranch %163 + %163 = OpLabel + OpBranch %142 + %142 = OpLabel + OpBranch %121 + %121 = OpLabel + OpBranch %98 + %98 = OpLabel + OpReturn + OpFunctionEnd + )"; + +// Abstract class exposing an interestingness function as a virtual method. +class InterestingnessTest { + public: + virtual ~InterestingnessTest() = default; + + // Abstract method that subclasses should implement for specific notions of + // interestingness. Its signature matches Shrinker::InterestingnessFunction. + // Argument |binary| is the SPIR-V binary to be checked; |counter| is used for + // debugging purposes. + virtual bool Interesting(const std::vector& binary, + uint32_t counter) = 0; + + // Yields the Interesting instance method wrapped in a function object. + Shrinker::InterestingnessFunction AsFunction() { + return std::bind(&InterestingnessTest::Interesting, this, + std::placeholders::_1, std::placeholders::_2); + } +}; + +// A test that says all binaries are interesting. +class AlwaysInteresting : public InterestingnessTest { + public: + bool Interesting(const std::vector&, uint32_t) override { + return true; + } +}; + +// A test that says a binary is interesting first time round, and uninteresting +// thereafter. +class OnlyInterestingFirstTime : public InterestingnessTest { + public: + explicit OnlyInterestingFirstTime() : first_time_(true) {} + + bool Interesting(const std::vector&, uint32_t) override { + if (first_time_) { + first_time_ = false; + return true; + } + return false; + } + + private: + bool first_time_; +}; + +// A test that says a binary is interesting first time round, after which +// interestingness ping pongs between false and true. +class PingPong : public InterestingnessTest { + public: + explicit PingPong() : interesting_(false) {} + + bool Interesting(const std::vector&, uint32_t) override { + interesting_ = !interesting_; + return interesting_; + } + + private: + bool interesting_; +}; + +// A test that says a binary is interesting first time round, thereafter +// decides at random whether it is interesting. This allows the logic of the +// shrinker to be exercised quite a bit. +class InterestingThenRandom : public InterestingnessTest { + public: + InterestingThenRandom(const PseudoRandomGenerator& random_generator) + : first_time_(true), random_generator_(random_generator) {} + + bool Interesting(const std::vector&, uint32_t) override { + if (first_time_) { + first_time_ = false; + return true; + } + return random_generator_.RandomBool(); + } + + private: + bool first_time_; + PseudoRandomGenerator random_generator_; +}; + +// |binary_in| and |initial_facts| are a SPIR-V binary and sequence of facts to +// which |transformation_sequence_in| can be applied. Shrinking of +// |transformation_sequence_in| gets performed with respect to +// |interestingness_function|. If |expected_binary_out| is non-empty, it must +// match the binary obtained by applying the final shrunk set of +// transformations, in which case the number of such transformations should +// equal |expected_transformations_out_size|. +// +// The |step_limit| parameter restricts the number of steps that the shrinker +// will try; it can be set to something small for a faster (but less thorough) +// test. +// +// The |validator_options| parameter provides validator options that should be +// used during shrinking. +void RunAndCheckShrinker( + const spv_target_env& target_env, const std::vector& binary_in, + const protobufs::FactSequence& initial_facts, + const protobufs::TransformationSequence& transformation_sequence_in, + const Shrinker::InterestingnessFunction& interestingness_function, + const std::vector& expected_binary_out, + uint32_t expected_transformations_out_size, uint32_t step_limit, + spv_validator_options validator_options) { + // Run the shrinker. + auto shrinker_result = + Shrinker(target_env, kConsoleMessageConsumer, binary_in, initial_facts, + transformation_sequence_in, interestingness_function, step_limit, + false, validator_options) + .Run(); + + ASSERT_TRUE(Shrinker::ShrinkerResultStatus::kComplete == + shrinker_result.status || + Shrinker::ShrinkerResultStatus::kStepLimitReached == + shrinker_result.status); + + // If a non-empty expected binary was provided, check that it matches the + // result of shrinking and that the expected number of transformations remain. + if (!expected_binary_out.empty()) { + ASSERT_EQ(expected_binary_out, shrinker_result.transformed_binary); + ASSERT_EQ( + expected_transformations_out_size, + static_cast( + shrinker_result.applied_transformations.transformation_size())); + } +} + +// Assembles the given |shader| text, and then: +// - Runs the fuzzer with |seed| to yield a set of transformations +// - Shrinks the transformation with various interestingness functions, +// asserting some properties about the result each time +void RunFuzzerAndShrinker(const std::string& shader, + const protobufs::FactSequence& initial_facts, + uint32_t seed) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + + std::vector binary_in; + SpirvTools t(env); + t.SetMessageConsumer(kConsoleMessageConsumer); + ASSERT_TRUE(t.Assemble(shader, &binary_in, kFuzzAssembleOption)); + ASSERT_TRUE(t.Validate(binary_in)); + + std::vector donor_suppliers; + for (auto donor : {&kTestShader1, &kTestShader2, &kTestShader3}) { + donor_suppliers.emplace_back([donor]() { + return BuildModule(env, kConsoleMessageConsumer, *donor, + kFuzzAssembleOption); + }); + } + + // Run the fuzzer and check that it successfully yields a valid binary. + spvtools::ValidatorOptions validator_options; + + // Depending on the seed, decide whether to enable all passes and which + // repeated pass manager to use. + bool enable_all_passes = (seed % 4) == 0; + Fuzzer::RepeatedPassStrategy repeated_pass_strategy; + if ((seed % 3) == 0) { + repeated_pass_strategy = Fuzzer::RepeatedPassStrategy::kSimple; + } else if ((seed % 3) == 1) { + repeated_pass_strategy = + Fuzzer::RepeatedPassStrategy::kLoopedWithRecommendations; + } else { + repeated_pass_strategy = + Fuzzer::RepeatedPassStrategy::kRandomWithRecommendations; + } + + auto fuzzer_result = + Fuzzer(env, kConsoleMessageConsumer, binary_in, initial_facts, + donor_suppliers, MakeUnique(seed), + enable_all_passes, repeated_pass_strategy, true, validator_options) + .Run(); + ASSERT_EQ(Fuzzer::FuzzerResultStatus::kComplete, fuzzer_result.status); + ASSERT_TRUE(t.Validate(fuzzer_result.transformed_binary)); + + const uint32_t kReasonableStepLimit = 50; + const uint32_t kSmallStepLimit = 20; + + // With the AlwaysInteresting test, we should quickly shrink to the original + // binary with no transformations remaining. + RunAndCheckShrinker(env, binary_in, initial_facts, + fuzzer_result.applied_transformations, + AlwaysInteresting().AsFunction(), binary_in, 0, + kReasonableStepLimit, validator_options); + + // With the OnlyInterestingFirstTime test, no shrinking should be achieved. + RunAndCheckShrinker( + env, binary_in, initial_facts, fuzzer_result.applied_transformations, + OnlyInterestingFirstTime().AsFunction(), fuzzer_result.transformed_binary, + static_cast( + fuzzer_result.applied_transformations.transformation_size()), + kReasonableStepLimit, validator_options); + + // The PingPong test is unpredictable; passing an empty expected binary + // means that we don't check anything beyond that shrinking completes + // successfully. + RunAndCheckShrinker( + env, binary_in, initial_facts, fuzzer_result.applied_transformations, + PingPong().AsFunction(), {}, 0, kSmallStepLimit, validator_options); + + // The InterestingThenRandom test is unpredictable; passing an empty + // expected binary means that we do not check anything about shrinking + // results. + RunAndCheckShrinker( + env, binary_in, initial_facts, fuzzer_result.applied_transformations, + InterestingThenRandom(PseudoRandomGenerator(seed)).AsFunction(), {}, 0, + kSmallStepLimit, validator_options); +} + +TEST(FuzzerShrinkerTest, Miscellaneous1) { + RunFuzzerAndShrinker(kTestShader1, protobufs::FactSequence(), 2); +} + +TEST(FuzzerShrinkerTest, Miscellaneous2) { + RunFuzzerAndShrinker(kTestShader2, protobufs::FactSequence(), 19); +} + +TEST(FuzzerShrinkerTest, Miscellaneous3) { + // Add the facts "resolution.x == 250" and "resolution.y == 100". + protobufs::FactSequence facts; + { + protobufs::FactConstantUniform resolution_x_eq_250; + *resolution_x_eq_250.mutable_uniform_buffer_element_descriptor() = + MakeUniformBufferElementDescriptor(0, 0, {0, 0}); + *resolution_x_eq_250.mutable_constant_word()->Add() = 250; + protobufs::Fact temp; + *temp.mutable_constant_uniform_fact() = resolution_x_eq_250; + *facts.mutable_fact()->Add() = temp; + } + { + protobufs::FactConstantUniform resolution_y_eq_100; + *resolution_y_eq_100.mutable_uniform_buffer_element_descriptor() = + MakeUniformBufferElementDescriptor(0, 0, {0, 1}); + *resolution_y_eq_100.mutable_constant_word()->Add() = 100; + protobufs::Fact temp; + *temp.mutable_constant_uniform_fact() = resolution_y_eq_100; + *facts.mutable_fact()->Add() = temp; + } + // Also add an invalid fact, which should be ignored. + { + protobufs::FactConstantUniform bad_fact; + // The descriptor set, binding and indices used here deliberately make no + // sense. + *bad_fact.mutable_uniform_buffer_element_descriptor() = + MakeUniformBufferElementDescriptor(22, 33, {44, 55}); + *bad_fact.mutable_constant_word()->Add() = 100; + protobufs::Fact temp; + *temp.mutable_constant_uniform_fact() = bad_fact; + *facts.mutable_fact()->Add() = temp; + } + + // Do 2 fuzzer runs, starting from an initial seed of 194 (seed value chosen + // arbitrarily). + RunFuzzerAndShrinker(kTestShader3, facts, 194); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/instruction_descriptor_test.cpp b/third_party/spirv-tools/test/fuzz/instruction_descriptor_test.cpp new file mode 100644 index 0000000..3c04d7e --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/instruction_descriptor_test.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/instruction_descriptor.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(InstructionDescriptorTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 2 + %32 = OpConstant %10 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %164 = OpVariable %11 Function + %165 = OpVariable %11 Function + OpBranch %16 + %16 = OpLabel + OpStore %164 %32 + OpStore %165 %13 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + for (auto& function : *context->module()) { + for (auto& block : function) { + for (auto inst_it = block.cbegin(); inst_it != block.cend(); ++inst_it) { + ASSERT_EQ(&*inst_it, + FindInstruction(MakeInstructionDescriptor(block, inst_it), + context.get())); + } + } + } +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/replayer_test.cpp b/third_party/spirv-tools/test/fuzz/replayer_test.cpp new file mode 100644 index 0000000..151f263 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/replayer_test.cpp @@ -0,0 +1,475 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/replayer.h" + +#include "gtest/gtest.h" +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/transformation_add_constant_scalar.h" +#include "source/fuzz/transformation_add_global_variable.h" +#include "source/fuzz/transformation_add_parameter.h" +#include "source/fuzz/transformation_add_synonym.h" +#include "source/fuzz/transformation_flatten_conditional_branch.h" +#include "source/fuzz/transformation_split_block.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(ReplayerTest, PartialReplay) { + const std::string kTestShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "g" + OpName %11 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Private %6 + %8 = OpVariable %7 Private + %9 = OpConstant %6 10 + %10 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + OpStore %8 %9 + %12 = OpLoad %6 %8 + OpStore %11 %12 + %13 = OpLoad %6 %8 + OpStore %11 %13 + %14 = OpLoad %6 %8 + OpStore %11 %14 + %15 = OpLoad %6 %8 + OpStore %11 %15 + %16 = OpLoad %6 %8 + OpStore %11 %16 + %17 = OpLoad %6 %8 + OpStore %11 %17 + %18 = OpLoad %6 %8 + OpStore %11 %18 + %19 = OpLoad %6 %8 + OpStore %11 %19 + %20 = OpLoad %6 %8 + OpStore %11 %20 + %21 = OpLoad %6 %8 + OpStore %11 %21 + %22 = OpLoad %6 %8 + OpStore %11 %22 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + spvtools::ValidatorOptions validator_options; + + std::vector binary_in; + SpirvTools t(env); + t.SetMessageConsumer(kConsoleMessageConsumer); + ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption)); + ASSERT_TRUE(t.Validate(binary_in)); + + protobufs::TransformationSequence transformations; + for (uint32_t id = 12; id <= 22; id++) { + *transformations.add_transformation() = + TransformationSplitBlock(MakeInstructionDescriptor(id, SpvOpLoad, 0), + id + 100) + .ToMessage(); + } + + { + // Full replay + protobufs::FactSequence empty_facts; + auto replayer_result = + Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, + transformations, 11, true, validator_options) + .Run(); + // Replay should succeed. + ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, + replayer_result.status); + // All transformations should be applied. + ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals( + transformations, replayer_result.applied_transformations)); + + const std::string kFullySplitShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "g" + OpName %11 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Private %6 + %8 = OpVariable %7 Private + %9 = OpConstant %6 10 + %10 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + OpStore %8 %9 + OpBranch %112 + %112 = OpLabel + %12 = OpLoad %6 %8 + OpStore %11 %12 + OpBranch %113 + %113 = OpLabel + %13 = OpLoad %6 %8 + OpStore %11 %13 + OpBranch %114 + %114 = OpLabel + %14 = OpLoad %6 %8 + OpStore %11 %14 + OpBranch %115 + %115 = OpLabel + %15 = OpLoad %6 %8 + OpStore %11 %15 + OpBranch %116 + %116 = OpLabel + %16 = OpLoad %6 %8 + OpStore %11 %16 + OpBranch %117 + %117 = OpLabel + %17 = OpLoad %6 %8 + OpStore %11 %17 + OpBranch %118 + %118 = OpLabel + %18 = OpLoad %6 %8 + OpStore %11 %18 + OpBranch %119 + %119 = OpLabel + %19 = OpLoad %6 %8 + OpStore %11 %19 + OpBranch %120 + %120 = OpLabel + %20 = OpLoad %6 %8 + OpStore %11 %20 + OpBranch %121 + %121 = OpLabel + %21 = OpLoad %6 %8 + OpStore %11 %21 + OpBranch %122 + %122 = OpLabel + %22 = OpLoad %6 %8 + OpStore %11 %22 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, kFullySplitShader, + replayer_result.transformed_module.get())); + } + + { + // Half replay + protobufs::FactSequence empty_facts; + auto replayer_result = + Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, + transformations, 5, true, validator_options) + .Run(); + // Replay should succeed. + ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, + replayer_result.status); + // The first 5 transformations should be applied + ASSERT_EQ(5, replayer_result.applied_transformations.transformation_size()); + for (uint32_t i = 0; i < 5; i++) { + ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals( + transformations.transformation(i), + replayer_result.applied_transformations.transformation(i))); + } + + const std::string kHalfSplitShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "g" + OpName %11 "x" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Private %6 + %8 = OpVariable %7 Private + %9 = OpConstant %6 10 + %10 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + OpStore %8 %9 + OpBranch %112 + %112 = OpLabel + %12 = OpLoad %6 %8 + OpStore %11 %12 + OpBranch %113 + %113 = OpLabel + %13 = OpLoad %6 %8 + OpStore %11 %13 + OpBranch %114 + %114 = OpLabel + %14 = OpLoad %6 %8 + OpStore %11 %14 + OpBranch %115 + %115 = OpLabel + %15 = OpLoad %6 %8 + OpStore %11 %15 + OpBranch %116 + %116 = OpLabel + %16 = OpLoad %6 %8 + OpStore %11 %16 + %17 = OpLoad %6 %8 + OpStore %11 %17 + %18 = OpLoad %6 %8 + OpStore %11 %18 + %19 = OpLoad %6 %8 + OpStore %11 %19 + %20 = OpLoad %6 %8 + OpStore %11 %20 + %21 = OpLoad %6 %8 + OpStore %11 %21 + %22 = OpLoad %6 %8 + OpStore %11 %22 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, kHalfSplitShader, + replayer_result.transformed_module.get())); + } + + { + // Empty replay + protobufs::FactSequence empty_facts; + auto replayer_result = + Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, + transformations, 0, true, validator_options) + .Run(); + // Replay should succeed. + ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, + replayer_result.status); + // No transformations should be applied + ASSERT_EQ(0, replayer_result.applied_transformations.transformation_size()); + ASSERT_TRUE( + IsEqual(env, kTestShader, replayer_result.transformed_module.get())); + } + + { + // Invalid replay: too many transformations + protobufs::FactSequence empty_facts; + // The number of transformations requested to be applied exceeds the number + // of transformations + auto replayer_result = + Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, + transformations, 12, true, validator_options) + .Run(); + + // Replay should not succeed. + ASSERT_EQ(Replayer::ReplayerResultStatus::kTooManyTransformationsRequested, + replayer_result.status); + // No transformations should be applied + ASSERT_EQ(0, replayer_result.applied_transformations.transformation_size()); + // The output binary should be empty + ASSERT_EQ(nullptr, replayer_result.transformed_module); + } +} + +TEST(ReplayerTest, CheckFactsAfterReplay) { + const std::string kTestShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %50 = OpTypePointer Private %8 + %11 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + %12 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + spvtools::ValidatorOptions validator_options; + + std::vector binary_in; + SpirvTools t(env); + t.SetMessageConsumer(kConsoleMessageConsumer); + ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption)); + ASSERT_TRUE(t.Validate(binary_in)); + + protobufs::TransformationSequence transformations; + *transformations.add_transformation() = + TransformationAddConstantScalar(100, 8, {42}, true).ToMessage(); + *transformations.add_transformation() = + TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 100, + true) + .ToMessage(); + *transformations.add_transformation() = + TransformationAddParameter(6, 102, 8, {{12, 100}}, 103).ToMessage(); + *transformations.add_transformation() = + TransformationAddSynonym( + 11, + protobufs::TransformationAddSynonym::SynonymType:: + TransformationAddSynonym_SynonymType_COPY_OBJECT, + 104, MakeInstructionDescriptor(12, SpvOpFunctionCall, 0)) + .ToMessage(); + + // Full replay + protobufs::FactSequence empty_facts; + auto replayer_result = + Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, + transformations, transformations.transformation_size(), true, + validator_options) + .Run(); + // Replay should succeed. + ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status); + // All transformations should be applied. + ASSERT_TRUE(google::protobuf::util::MessageDifferencer::Equals( + transformations, replayer_result.applied_transformations)); + + const std::string kExpected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %50 = OpTypePointer Private %8 + %11 = OpConstant %8 1 + %100 = OpConstant %8 42 + %101 = OpVariable %50 Private %100 + %103 = OpTypeFunction %2 %8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + %104 = OpCopyObject %8 %11 + %12 = OpFunctionCall %2 %6 %100 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %103 + %102 = OpFunctionParameter %8 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE( + IsEqual(env, kExpected, replayer_result.transformed_module.get())); + + ASSERT_TRUE( + replayer_result.transformation_context->GetFactManager()->IdIsIrrelevant( + 100)); + ASSERT_TRUE(replayer_result.transformation_context->GetFactManager() + ->PointeeValueIsIrrelevant(101)); + ASSERT_TRUE( + replayer_result.transformation_context->GetFactManager()->IdIsIrrelevant( + 102)); + ASSERT_TRUE( + replayer_result.transformation_context->GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), MakeDataDescriptor(104, {}))); +} + +TEST(ReplayerTest, ReplayWithOverflowIds) { + const std::string kTestShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpTypePointer Private %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 0 + %12 = OpTypeBool + %17 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %13 = OpSGreaterThan %12 %10 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %15 + %14 = OpLabel + %16 = OpLoad %6 %8 + %18 = OpIAdd %6 %16 %17 + OpStore %8 %18 + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + spvtools::ValidatorOptions validator_options; + + std::vector binary_in; + SpirvTools t(env); + t.SetMessageConsumer(kConsoleMessageConsumer); + ASSERT_TRUE(t.Assemble(kTestShader, &binary_in, kFuzzAssembleOption)); + ASSERT_TRUE(t.Validate(binary_in)); + + protobufs::TransformationSequence transformations; + *transformations.add_transformation() = + TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}).ToMessage(); + *transformations.add_transformation() = + TransformationAddGlobalVariable(101, 50, SpvStorageClassPrivate, 11, true) + .ToMessage(); + + protobufs::FactSequence empty_facts; + auto replayer_result = + Replayer(env, kConsoleMessageConsumer, binary_in, empty_facts, + transformations, transformations.transformation_size(), true, + validator_options) + .Run(); + // Replay should succeed. + ASSERT_EQ(Replayer::ReplayerResultStatus::kComplete, replayer_result.status); + // All transformations should be applied. + ASSERT_EQ(2, replayer_result.applied_transformations.transformation_size()); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/shrinker_test.cpp b/third_party/spirv-tools/test/fuzz/shrinker_test.cpp new file mode 100644 index 0000000..42cd182 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/shrinker_test.cpp @@ -0,0 +1,383 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/shrinker.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fact_manager/fact_manager.h" +#include "source/fuzz/fuzzer_context.h" +#include "source/fuzz/fuzzer_pass_donate_modules.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/pseudo_random_generator.h" +#include "source/fuzz/transformation_context.h" +#include "source/opt/ir_context.h" +#include "source/util/make_unique.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(ShrinkerTest, ReduceAddedFunctions) { + const std::string kReferenceModule = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Private %6 + %8 = OpVariable %7 Private + %9 = OpConstant %6 2 + %10 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + OpStore %8 %9 + %12 = OpLoad %6 %8 + OpStore %11 %12 + OpReturn + OpFunctionEnd + )"; + + const std::string kDonorModule = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %12 = OpTypeFunction %2 %7 + %17 = OpConstant %6 0 + %26 = OpTypeBool + %32 = OpConstant %6 1 + %46 = OpTypePointer Private %6 + %47 = OpVariable %46 Private + %48 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %49 = OpVariable %7 Function + %50 = OpVariable %7 Function + %51 = OpLoad %6 %49 + OpStore %50 %51 + %52 = OpFunctionCall %2 %14 %50 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %16 = OpVariable %7 Function + %18 = OpVariable %7 Function + OpStore %16 %17 + OpStore %18 %17 + OpBranch %19 + %19 = OpLabel + OpLoopMerge %21 %22 None + OpBranch %23 + %23 = OpLabel + %24 = OpLoad %6 %18 + %25 = OpLoad %6 %9 + %27 = OpSLessThan %26 %24 %25 + OpBranchConditional %27 %20 %21 + %20 = OpLabel + %28 = OpLoad %6 %9 + %29 = OpLoad %6 %16 + %30 = OpIAdd %6 %29 %28 + OpStore %16 %30 + OpBranch %22 + %22 = OpLabel + %31 = OpLoad %6 %18 + %33 = OpIAdd %6 %31 %32 + OpStore %18 %33 + OpBranch %19 + %21 = OpLabel + %34 = OpLoad %6 %16 + %35 = OpNot %6 %34 + OpReturnValue %35 + OpFunctionEnd + %14 = OpFunction %2 None %12 + %13 = OpFunctionParameter %7 + %15 = OpLabel + %37 = OpVariable %7 Function + %38 = OpVariable %7 Function + %39 = OpLoad %6 %13 + OpStore %38 %39 + %40 = OpFunctionCall %6 %10 %38 + OpStore %37 %40 + %41 = OpLoad %6 %37 + %42 = OpLoad %6 %13 + %43 = OpSGreaterThan %26 %41 %42 + OpSelectionMerge %45 None + OpBranchConditional %43 %44 %45 + %44 = OpLabel + OpStore %47 %48 + OpBranch %45 + %45 = OpLabel + OpReturn + OpFunctionEnd + )"; + + // Note: |env| should ideally be declared const. However, due to a known + // issue with older versions of MSVC we would have to mark |env| as being + // captured due to its used in a lambda below, and other compilers would warn + // that such capturing is not necessary. Not declaring |env| as const means + // that it needs to be captured to be used in the lambda, and thus all + // compilers are kept happy. See: + // https://developercommunity.visualstudio.com/content/problem/367326/problems-with-capturing-constexpr-in-lambda.html + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = fuzzerutil::kSilentMessageConsumer; + + SpirvTools tools(env); + std::vector reference_binary; + ASSERT_TRUE( + tools.Assemble(kReferenceModule, &reference_binary, kFuzzAssembleOption)); + + spvtools::ValidatorOptions validator_options; + + const auto variant_ir_context = + BuildModule(env, consumer, kReferenceModule, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + variant_ir_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_ir_context = + BuildModule(env, consumer, kDonorModule, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_ir_context.get(), validator_options, kConsoleMessageConsumer)); + + PseudoRandomGenerator random_generator(0); + FuzzerContext fuzzer_context(&random_generator, 100); + TransformationContext transformation_context( + MakeUnique(variant_ir_context.get()), validator_options); + + protobufs::TransformationSequence transformations; + FuzzerPassDonateModules pass(variant_ir_context.get(), + &transformation_context, &fuzzer_context, + &transformations, {}); + pass.DonateSingleModule(donor_ir_context.get(), true); + + protobufs::FactSequence no_facts; + + Shrinker::InterestingnessFunction interestingness_function = + [consumer, env](const std::vector& binary, + uint32_t /*unused*/) -> bool { + bool found_op_not = false; + uint32_t op_call_count = 0; + auto temp_ir_context = + BuildModule(env, consumer, binary.data(), binary.size()); + for (auto& function : *temp_ir_context->module()) { + for (auto& block : function) { + for (auto& inst : block) { + if (inst.opcode() == SpvOpNot) { + found_op_not = true; + } else if (inst.opcode() == SpvOpFunctionCall) { + op_call_count++; + } + } + } + } + return found_op_not && op_call_count >= 2; + }; + + auto shrinker_result = + Shrinker(env, consumer, reference_binary, no_facts, transformations, + interestingness_function, 1000, true, validator_options) + .Run(); + ASSERT_EQ(Shrinker::ShrinkerResultStatus::kComplete, shrinker_result.status); + + // We now check that the module after shrinking looks right. + // The entry point should be identical to what it looked like in the + // reference, while the other functions should be absolutely minimal, + // containing only what is needed to satisfy the interestingness function. + auto ir_context_after_shrinking = + BuildModule(env, consumer, shrinker_result.transformed_binary.data(), + shrinker_result.transformed_binary.size()); + bool first_function = true; + for (auto& function : *ir_context_after_shrinking->module()) { + if (first_function) { + first_function = false; + bool first_block = true; + for (auto& block : function) { + ASSERT_TRUE(first_block); + uint32_t counter = 0; + for (auto& inst : block) { + switch (counter) { + case 0: + ASSERT_EQ(SpvOpVariable, inst.opcode()); + ASSERT_EQ(11, inst.result_id()); + break; + case 1: + ASSERT_EQ(SpvOpStore, inst.opcode()); + break; + case 2: + ASSERT_EQ(SpvOpLoad, inst.opcode()); + ASSERT_EQ(12, inst.result_id()); + break; + case 3: + ASSERT_EQ(SpvOpStore, inst.opcode()); + break; + case 4: + ASSERT_EQ(SpvOpReturn, inst.opcode()); + break; + default: + FAIL(); + } + counter++; + } + } + } else { + bool first_block = true; + for (auto& block : function) { + ASSERT_TRUE(first_block); + first_block = false; + for (auto& inst : block) { + switch (inst.opcode()) { + case SpvOpVariable: + case SpvOpNot: + case SpvOpReturn: + case SpvOpReturnValue: + case SpvOpFunctionCall: + // These are the only instructions we expect to see. + break; + default: + FAIL(); + } + } + } + } + } +} + +TEST(ShrinkerTest, HitStepLimitWhenReducingAddedFunctions) { + const std::string kReferenceModule = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Private %6 + %8 = OpVariable %7 Private + %9 = OpConstant %6 2 + %10 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + OpStore %8 %9 + %12 = OpLoad %6 %8 + OpStore %11 %12 + OpReturn + OpFunctionEnd + )"; + + const std::string kDonorModule = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %48 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %52 = OpCopyObject %6 %48 + %53 = OpCopyObject %6 %52 + %54 = OpCopyObject %6 %53 + %55 = OpCopyObject %6 %54 + %56 = OpCopyObject %6 %55 + %57 = OpCopyObject %6 %56 + %58 = OpCopyObject %6 %48 + %59 = OpCopyObject %6 %58 + %60 = OpCopyObject %6 %59 + %61 = OpCopyObject %6 %60 + %62 = OpCopyObject %6 %61 + %63 = OpCopyObject %6 %62 + %64 = OpCopyObject %6 %48 + OpReturn + OpFunctionEnd + )"; + + spv_target_env env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = fuzzerutil::kSilentMessageConsumer; + + SpirvTools tools(env); + std::vector reference_binary; + ASSERT_TRUE( + tools.Assemble(kReferenceModule, &reference_binary, kFuzzAssembleOption)); + + spvtools::ValidatorOptions validator_options; + + const auto variant_ir_context = + BuildModule(env, consumer, kReferenceModule, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + variant_ir_context.get(), validator_options, kConsoleMessageConsumer)); + + const auto donor_ir_context = + BuildModule(env, consumer, kDonorModule, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + donor_ir_context.get(), validator_options, kConsoleMessageConsumer)); + + PseudoRandomGenerator random_generator(0); + FuzzerContext fuzzer_context(&random_generator, 100); + TransformationContext transformation_context( + MakeUnique(variant_ir_context.get()), validator_options); + + protobufs::TransformationSequence transformations; + FuzzerPassDonateModules pass(variant_ir_context.get(), + &transformation_context, &fuzzer_context, + &transformations, {}); + pass.DonateSingleModule(donor_ir_context.get(), true); + + protobufs::FactSequence no_facts; + + Shrinker::InterestingnessFunction interestingness_function = + [consumer, env](const std::vector& binary, + uint32_t /*unused*/) -> bool { + auto temp_ir_context = + BuildModule(env, consumer, binary.data(), binary.size()); + uint32_t copy_object_count = 0; + temp_ir_context->module()->ForEachInst( + [©_object_count](opt::Instruction* inst) { + if (inst->opcode() == SpvOpCopyObject) { + copy_object_count++; + } + + }); + return copy_object_count >= 8; + }; + + auto shrinker_result = + Shrinker(env, consumer, reference_binary, no_facts, transformations, + interestingness_function, 30, true, validator_options) + .Run(); + ASSERT_EQ(Shrinker::ShrinkerResultStatus::kStepLimitReached, + shrinker_result.status); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_access_chain_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_access_chain_test.cpp new file mode 100644 index 0000000..5c43127 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_access_chain_test.cpp @@ -0,0 +1,713 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_access_chain.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAccessChainTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %48 %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %50 = OpTypeMatrix %7 2 + %70 = OpTypePointer Function %7 + %71 = OpTypePointer Function %50 + %8 = OpTypeStruct %7 %6 + %9 = OpTypePointer Function %8 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %12 = OpTypeFunction %10 %9 %11 + %17 = OpConstant %10 0 + %18 = OpTypeInt 32 0 + %19 = OpConstant %18 0 + %20 = OpTypePointer Function %6 + %99 = OpTypePointer Private %6 + %29 = OpConstant %6 0 + %30 = OpConstant %6 1 + %31 = OpConstantComposite %7 %29 %30 + %32 = OpConstant %6 2 + %33 = OpConstantComposite %8 %31 %32 + %35 = OpConstant %10 10 + %51 = OpConstant %18 10 + %80 = OpConstant %18 0 + %81 = OpConstant %10 1 + %82 = OpConstant %18 2 + %83 = OpConstant %10 3 + %84 = OpConstant %18 4 + %85 = OpConstant %10 5 + %52 = OpTypeArray %50 %51 + %53 = OpTypePointer Private %52 + %45 = OpUndef %9 + %46 = OpConstantNull %9 + %47 = OpTypePointer Private %8 + %48 = OpVariable %47 Private + %54 = OpVariable %53 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %28 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %9 Function + %38 = OpVariable %11 Function + %44 = OpCopyObject %9 %36 + OpStore %28 %33 + OpStore %34 %35 + %37 = OpLoad %8 %28 + OpStore %36 %37 + %39 = OpLoad %10 %34 + OpStore %38 %39 + %40 = OpFunctionCall %10 %15 %36 %38 + %41 = OpLoad %10 %34 + %42 = OpIAdd %10 %41 %40 + OpStore %34 %42 + OpReturn + OpFunctionEnd + %15 = OpFunction %10 None %12 + %13 = OpFunctionParameter %9 + %14 = OpFunctionParameter %11 + %16 = OpLabel + %21 = OpAccessChain %20 %13 %17 %19 + %43 = OpCopyObject %9 %13 + %22 = OpLoad %6 %21 + %23 = OpConvertFToS %10 %22 + %24 = OpLoad %10 %14 + %25 = OpIAdd %10 %23 %24 + OpReturnValue %25 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Types: + // Ptr | Pointee | Storage class | GLSL for pointee | Ids of this type + // ----+---------+---------------+---------------------+------------------ + // 9 | 8 | Function | struct(vec2, float) | 28, 36, 44, 13, 43 + // 11 | 10 | Function | int | 34, 38, 14 + // 20 | 6 | Function | float | - + // 99 | 6 | Private | float | - + // 53 | 52 | Private | mat2x2[10] | 54 + // 47 | 8 | Private | struct(vec2, float) | 48 + // 70 | 7 | Function | vec2 | - + // 71 | 59 | Function | mat2x2 | - + + // Indices 0-5 are in ids 80-85 + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 54); + + // Bad: id is not fresh + ASSERT_FALSE(TransformationAccessChain( + 43, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id does not exist + ASSERT_FALSE(TransformationAccessChain( + 100, 1000, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id is not a type + ASSERT_FALSE(TransformationAccessChain( + 100, 5, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id is not a pointer + ASSERT_FALSE(TransformationAccessChain( + 100, 23, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: index id does not exist + ASSERT_FALSE(TransformationAccessChain( + 100, 43, {1000}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: index id is not a constant and the pointer refers to a struct + ASSERT_FALSE(TransformationAccessChain( + 100, 43, {24}, MakeInstructionDescriptor(25, SpvOpIAdd, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: too many indices + ASSERT_FALSE( + TransformationAccessChain(100, 43, {80, 80, 80}, + MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: index id is out of bounds when accessing a struct + ASSERT_FALSE( + TransformationAccessChain(100, 43, {83, 80}, + MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to insert before variable + ASSERT_FALSE(TransformationAccessChain( + 100, 34, {}, MakeInstructionDescriptor(36, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: OpTypeBool must be present in the module to clamp an index + ASSERT_FALSE( + TransformationAccessChain(100, 36, {80, 81}, + MakeInstructionDescriptor(37, SpvOpStore, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer not available + ASSERT_FALSE( + TransformationAccessChain( + 100, 43, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: instruction descriptor does not identify anything + ASSERT_FALSE(TransformationAccessChain( + 100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 100)) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // Bad: pointer is null + ASSERT_DEATH( + TransformationAccessChain(100, 45, {80}, + MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context), + "Access chains should not be created from null/undefined pointers"); +#endif + +#ifndef NDEBUG + // Bad: pointer is undef + ASSERT_DEATH( + TransformationAccessChain(100, 46, {80}, + MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context), + "Access chains should not be created from null/undefined pointers"); +#endif + + // Bad: pointer to result type does not exist + ASSERT_FALSE(TransformationAccessChain( + 100, 52, {0}, MakeInstructionDescriptor(24, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationAccessChain transformation( + 100, 43, {80}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + } + + { + TransformationAccessChain transformation( + 101, 28, {81}, MakeInstructionDescriptor(42, SpvOpReturn, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); + } + + { + TransformationAccessChain transformation( + 102, 44, {}, MakeInstructionDescriptor(44, SpvOpStore, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103)); + } + + { + TransformationAccessChain transformation( + 103, 13, {80}, MakeInstructionDescriptor(21, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104)); + } + + { + TransformationAccessChain transformation( + 104, 34, {}, MakeInstructionDescriptor(44, SpvOpStore, 1)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105)); + } + + { + TransformationAccessChain transformation( + 105, 38, {}, MakeInstructionDescriptor(40, SpvOpFunctionCall, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(106)); + } + + { + TransformationAccessChain transformation( + 106, 14, {}, MakeInstructionDescriptor(24, SpvOpLoad, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(107)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %48 %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %50 = OpTypeMatrix %7 2 + %70 = OpTypePointer Function %7 + %71 = OpTypePointer Function %50 + %8 = OpTypeStruct %7 %6 + %9 = OpTypePointer Function %8 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %12 = OpTypeFunction %10 %9 %11 + %17 = OpConstant %10 0 + %18 = OpTypeInt 32 0 + %19 = OpConstant %18 0 + %20 = OpTypePointer Function %6 + %99 = OpTypePointer Private %6 + %29 = OpConstant %6 0 + %30 = OpConstant %6 1 + %31 = OpConstantComposite %7 %29 %30 + %32 = OpConstant %6 2 + %33 = OpConstantComposite %8 %31 %32 + %35 = OpConstant %10 10 + %51 = OpConstant %18 10 + %80 = OpConstant %18 0 + %81 = OpConstant %10 1 + %82 = OpConstant %18 2 + %83 = OpConstant %10 3 + %84 = OpConstant %18 4 + %85 = OpConstant %10 5 + %52 = OpTypeArray %50 %51 + %53 = OpTypePointer Private %52 + %45 = OpUndef %9 + %46 = OpConstantNull %9 + %47 = OpTypePointer Private %8 + %48 = OpVariable %47 Private + %54 = OpVariable %53 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %28 = OpVariable %9 Function + %34 = OpVariable %11 Function + %36 = OpVariable %9 Function + %38 = OpVariable %11 Function + %44 = OpCopyObject %9 %36 + %102 = OpAccessChain %9 %44 + OpStore %28 %33 + %104 = OpAccessChain %11 %34 + OpStore %34 %35 + %37 = OpLoad %8 %28 + OpStore %36 %37 + %39 = OpLoad %10 %34 + OpStore %38 %39 + %105 = OpAccessChain %11 %38 + %40 = OpFunctionCall %10 %15 %36 %38 + %41 = OpLoad %10 %34 + %42 = OpIAdd %10 %41 %40 + OpStore %34 %42 + %101 = OpAccessChain %20 %28 %81 + OpReturn + OpFunctionEnd + %15 = OpFunction %10 None %12 + %13 = OpFunctionParameter %9 + %14 = OpFunctionParameter %11 + %16 = OpLabel + %103 = OpAccessChain %70 %13 %80 + %21 = OpAccessChain %20 %13 %17 %19 + %43 = OpCopyObject %9 %13 + %22 = OpLoad %6 %21 + %23 = OpConvertFToS %10 %22 + %100 = OpAccessChain %70 %43 %80 + %106 = OpAccessChain %11 %14 + %24 = OpLoad %10 %14 + %25 = OpIAdd %10 %23 %24 + OpReturnValue %25 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAccessChainTest, IsomorphicStructs) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %11 %12 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Private %7 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Private %9 + %11 = OpVariable %8 Private + %12 = OpVariable %10 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + { + TransformationAccessChain transformation( + 100, 11, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationAccessChain transformation( + 101, 12, {}, MakeInstructionDescriptor(5, SpvOpReturn, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %11 %12 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 + %8 = OpTypePointer Private %7 + %9 = OpTypeStruct %6 + %10 = OpTypePointer Private %9 + %11 = OpVariable %8 Private + %12 = OpVariable %10 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpAccessChain %8 %11 + %101 = OpAccessChain %10 %12 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAccessChainTest, ClampingVariables) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %4 = OpTypeVoid + %5 = OpTypeBool + %6 = OpTypeFunction %4 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %7 4 + %9 = OpTypePointer Function %8 + %10 = OpConstant %7 0 + %11 = OpConstant %7 1 + %12 = OpConstant %7 3 + %13 = OpConstant %7 2 + %14 = OpConstantComposite %8 %10 %11 %12 %13 + %15 = OpTypePointer Function %7 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 1 + %18 = OpConstant %16 3 + %19 = OpTypeStruct %8 + %20 = OpTypePointer Function %19 + %21 = OpConstant %7 9 + %22 = OpConstant %16 10 + %23 = OpTypeArray %19 %22 + %24 = OpTypePointer Function %23 + %25 = OpTypeFloat 32 + %26 = OpTypeVector %25 4 + %27 = OpTypePointer Output %26 + %3 = OpVariable %27 Output + %2 = OpFunction %4 None %6 + %28 = OpLabel + %29 = OpVariable %9 Function + %30 = OpVariable %15 Function + %31 = OpVariable %15 Function + %32 = OpVariable %20 Function + %33 = OpVariable %15 Function + %34 = OpVariable %24 Function + OpStore %29 %14 + OpStore %30 %10 + %36 = OpLoad %7 %30 + %38 = OpLoad %8 %29 + %39 = OpCompositeConstruct %19 %38 + %40 = OpLoad %7 %30 + %42 = OpLoad %8 %29 + %43 = OpCompositeConstruct %19 %42 + %45 = OpLoad %7 %30 + %46 = OpLoad %7 %33 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: no ids given for clamping + ASSERT_FALSE(TransformationAccessChain( + 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: an id given for clamping is not fresh + ASSERT_FALSE(TransformationAccessChain( + 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0), + {{46, 201}}) + .IsApplicable(context.get(), transformation_context)); + + // Bad: an id given for clamping is not fresh + ASSERT_FALSE(TransformationAccessChain( + 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0), + {{200, 46}}) + .IsApplicable(context.get(), transformation_context)); + + // Bad: an id given for clamping is the same as the id for the access chain + ASSERT_FALSE(TransformationAccessChain( + 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0), + {{100, 201}}) + .IsApplicable(context.get(), transformation_context)); + + // Bad: the fresh ids given are not distinct + ASSERT_FALSE(TransformationAccessChain( + 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0), + {{200, 200}}) + .IsApplicable(context.get(), transformation_context)); + + // Bad: not enough ids given for clamping (2 pairs needed) + ASSERT_FALSE( + TransformationAccessChain(104, 34, {45, 10, 46}, + MakeInstructionDescriptor(46, SpvOpReturn, 0), + {{208, 209}, {209, 211}}) + .IsApplicable(context.get(), transformation_context)); + + // Bad: the fresh ids given are not distinct + ASSERT_FALSE( + TransformationAccessChain(104, 34, {45, 10, 46}, + MakeInstructionDescriptor(46, SpvOpReturn, 0), + {{208, 209}, {209, 211}}) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationAccessChain transformation( + 100, 29, {17}, MakeInstructionDescriptor(36, SpvOpLoad, 0), + {{200, 201}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + TransformationAccessChain transformation( + 101, 29, {36}, MakeInstructionDescriptor(38, SpvOpLoad, 0), + {{202, 203}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + TransformationAccessChain transformation( + 102, 32, {10, 40}, MakeInstructionDescriptor(42, SpvOpLoad, 0), + {{204, 205}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + TransformationAccessChain transformation( + 103, 34, {11}, MakeInstructionDescriptor(45, SpvOpLoad, 0), + {{206, 207}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + TransformationAccessChain transformation( + 104, 34, {45, 10, 46}, MakeInstructionDescriptor(46, SpvOpReturn, 0), + {{208, 209}, {210, 211}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %4 = OpTypeVoid + %5 = OpTypeBool + %6 = OpTypeFunction %4 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %7 4 + %9 = OpTypePointer Function %8 + %10 = OpConstant %7 0 + %11 = OpConstant %7 1 + %12 = OpConstant %7 3 + %13 = OpConstant %7 2 + %14 = OpConstantComposite %8 %10 %11 %12 %13 + %15 = OpTypePointer Function %7 + %16 = OpTypeInt 32 0 + %17 = OpConstant %16 1 + %18 = OpConstant %16 3 + %19 = OpTypeStruct %8 + %20 = OpTypePointer Function %19 + %21 = OpConstant %7 9 + %22 = OpConstant %16 10 + %23 = OpTypeArray %19 %22 + %24 = OpTypePointer Function %23 + %25 = OpTypeFloat 32 + %26 = OpTypeVector %25 4 + %27 = OpTypePointer Output %26 + %3 = OpVariable %27 Output + %2 = OpFunction %4 None %6 + %28 = OpLabel + %29 = OpVariable %9 Function + %30 = OpVariable %15 Function + %31 = OpVariable %15 Function + %32 = OpVariable %20 Function + %33 = OpVariable %15 Function + %34 = OpVariable %24 Function + OpStore %29 %14 + OpStore %30 %10 + %200 = OpULessThanEqual %5 %17 %18 + %201 = OpSelect %16 %200 %17 %18 + %100 = OpAccessChain %15 %29 %201 + %36 = OpLoad %7 %30 + %202 = OpULessThanEqual %5 %36 %12 + %203 = OpSelect %7 %202 %36 %12 + %101 = OpAccessChain %15 %29 %203 + %38 = OpLoad %8 %29 + %39 = OpCompositeConstruct %19 %38 + %40 = OpLoad %7 %30 + %204 = OpULessThanEqual %5 %40 %12 + %205 = OpSelect %7 %204 %40 %12 + %102 = OpAccessChain %15 %32 %10 %205 + %42 = OpLoad %8 %29 + %43 = OpCompositeConstruct %19 %42 + %206 = OpULessThanEqual %5 %11 %21 + %207 = OpSelect %7 %206 %11 %21 + %103 = OpAccessChain %20 %34 %207 + %45 = OpLoad %7 %30 + %46 = OpLoad %7 %33 + %208 = OpULessThanEqual %5 %45 %21 + %209 = OpSelect %7 %208 %45 %21 + %210 = OpULessThanEqual %5 %46 %12 + %211 = OpSelect %7 %210 %46 %12 + %104 = OpAccessChain %15 %34 %209 %10 %211 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp new file mode 100644 index 0000000..fa8f7bf --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_bit_instruction_synonym_test.cpp @@ -0,0 +1,942 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_bit_instruction_synonym.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddBitInstructionSynonymTest, IsApplicable) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %37 "main" + + ; Types + %2 = OpTypeInt 32 0 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + + ; Constants + %5 = OpConstant %2 0 + %6 = OpConstant %2 1 + %7 = OpConstant %2 2 + %8 = OpConstant %2 3 + %9 = OpConstant %2 4 + %10 = OpConstant %2 5 + %11 = OpConstant %2 6 + %12 = OpConstant %2 7 + %13 = OpConstant %2 8 + %14 = OpConstant %2 9 + %15 = OpConstant %2 10 + %16 = OpConstant %2 11 + %17 = OpConstant %2 12 + %18 = OpConstant %2 13 + %19 = OpConstant %2 14 + %20 = OpConstant %2 15 + %21 = OpConstant %2 16 + %22 = OpConstant %2 17 + %23 = OpConstant %2 18 + %24 = OpConstant %2 19 + %25 = OpConstant %2 20 + %26 = OpConstant %2 21 + %27 = OpConstant %2 22 + %28 = OpConstant %2 23 + %29 = OpConstant %2 24 + %30 = OpConstant %2 25 + %31 = OpConstant %2 26 + %32 = OpConstant %2 27 + %33 = OpConstant %2 28 + %34 = OpConstant %2 29 + %35 = OpConstant %2 30 + %36 = OpConstant %2 31 + + ; main function + %37 = OpFunction %3 None %4 + %38 = OpLabel + + ; Supported bit instructions + %39 = OpBitwiseOr %2 %5 %6 + %40 = OpBitwiseXor %2 %7 %8 + %41 = OpBitwiseAnd %2 %9 %10 + %42 = OpNot %2 %11 + + ; Not yet supported bit instructions + %43 = OpShiftRightLogical %2 %12 %13 + %44 = OpShiftRightArithmetic %2 %14 %15 + %45 = OpShiftLeftLogical %2 %16 %17 + %46 = OpBitReverse %2 %18 + %47 = OpBitCount %2 %19 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Tests undefined bit instruction. + auto transformation = TransformationAddBitInstructionSynonym( + 48, {49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, + 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests false bit instruction. + transformation = TransformationAddBitInstructionSynonym( + 38, {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests the number of fresh ids being different than the necessary. + transformation = TransformationAddBitInstructionSynonym( + 39, + {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, + 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests non-fresh ids. + transformation = TransformationAddBitInstructionSynonym( + 40, {47, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests unsupported transformation. + transformation = TransformationAddBitInstructionSynonym( + 43, {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests supported transformation. + transformation = TransformationAddBitInstructionSynonym( + 41, {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddBitInstructionSynonymTest, AddOpBitwiseOrSynonym) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %37 "main" + +; Types + %2 = OpTypeInt 32 0 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstant %2 0 + %6 = OpConstant %2 1 + %7 = OpConstant %2 2 + %8 = OpConstant %2 3 + %9 = OpConstant %2 4 + %10 = OpConstant %2 5 + %11 = OpConstant %2 6 + %12 = OpConstant %2 7 + %13 = OpConstant %2 8 + %14 = OpConstant %2 9 + %15 = OpConstant %2 10 + %16 = OpConstant %2 11 + %17 = OpConstant %2 12 + %18 = OpConstant %2 13 + %19 = OpConstant %2 14 + %20 = OpConstant %2 15 + %21 = OpConstant %2 16 + %22 = OpConstant %2 17 + %23 = OpConstant %2 18 + %24 = OpConstant %2 19 + %25 = OpConstant %2 20 + %26 = OpConstant %2 21 + %27 = OpConstant %2 22 + %28 = OpConstant %2 23 + %29 = OpConstant %2 24 + %30 = OpConstant %2 25 + %31 = OpConstant %2 26 + %32 = OpConstant %2 27 + %33 = OpConstant %2 28 + %34 = OpConstant %2 29 + %35 = OpConstant %2 30 + %36 = OpConstant %2 31 + +; main function + %37 = OpFunction %3 None %4 + %38 = OpLabel + %39 = OpBitwiseOr %2 %5 %6 ; bit instruction + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Adds OpBitwiseOr synonym. + auto transformation = TransformationAddBitInstructionSynonym( + 39, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {}))); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %37 "main" + +; Types + %2 = OpTypeInt 32 0 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstant %2 0 + %6 = OpConstant %2 1 + %7 = OpConstant %2 2 + %8 = OpConstant %2 3 + %9 = OpConstant %2 4 + %10 = OpConstant %2 5 + %11 = OpConstant %2 6 + %12 = OpConstant %2 7 + %13 = OpConstant %2 8 + %14 = OpConstant %2 9 + %15 = OpConstant %2 10 + %16 = OpConstant %2 11 + %17 = OpConstant %2 12 + %18 = OpConstant %2 13 + %19 = OpConstant %2 14 + %20 = OpConstant %2 15 + %21 = OpConstant %2 16 + %22 = OpConstant %2 17 + %23 = OpConstant %2 18 + %24 = OpConstant %2 19 + %25 = OpConstant %2 20 + %26 = OpConstant %2 21 + %27 = OpConstant %2 22 + %28 = OpConstant %2 23 + %29 = OpConstant %2 24 + %30 = OpConstant %2 25 + %31 = OpConstant %2 26 + %32 = OpConstant %2 27 + %33 = OpConstant %2 28 + %34 = OpConstant %2 29 + %35 = OpConstant %2 30 + %36 = OpConstant %2 31 + +; main function + %37 = OpFunction %3 None %4 + %38 = OpLabel + +; Add OpBitwiseOr synonym + %40 = OpBitFieldUExtract %2 %5 %5 %6 ; extracts bit 0 from %5 + %41 = OpBitFieldUExtract %2 %6 %5 %6 ; extracts bit 0 from %6 + %42 = OpBitwiseOr %2 %40 %41 + + %43 = OpBitFieldUExtract %2 %5 %6 %6 ; extracts bit 1 from %5 + %44 = OpBitFieldUExtract %2 %6 %6 %6 ; extracts bit 1 from %6 + %45 = OpBitwiseOr %2 %43 %44 + + %46 = OpBitFieldUExtract %2 %5 %7 %6 ; extracts bit 2 from %5 + %47 = OpBitFieldUExtract %2 %6 %7 %6 ; extracts bit 2 from %6 + %48 = OpBitwiseOr %2 %46 %47 + + %49 = OpBitFieldUExtract %2 %5 %8 %6 ; extracts bit 3 from %5 + %50 = OpBitFieldUExtract %2 %6 %8 %6 ; extracts bit 3 from %6 + %51 = OpBitwiseOr %2 %49 %50 + + %52 = OpBitFieldUExtract %2 %5 %9 %6 ; extracts bit 4 from %5 + %53 = OpBitFieldUExtract %2 %6 %9 %6 ; extracts bit 4 from %6 + %54 = OpBitwiseOr %2 %52 %53 + + %55 = OpBitFieldUExtract %2 %5 %10 %6 ; extracts bit 5 from %5 + %56 = OpBitFieldUExtract %2 %6 %10 %6 ; extracts bit 5 from %6 + %57 = OpBitwiseOr %2 %55 %56 + + %58 = OpBitFieldUExtract %2 %5 %11 %6 ; extracts bit 6 from %5 + %59 = OpBitFieldUExtract %2 %6 %11 %6 ; extracts bit 6 from %6 + %60 = OpBitwiseOr %2 %58 %59 + + %61 = OpBitFieldUExtract %2 %5 %12 %6 ; extracts bit 7 from %5 + %62 = OpBitFieldUExtract %2 %6 %12 %6 ; extracts bit 7 from %6 + %63 = OpBitwiseOr %2 %61 %62 + + %64 = OpBitFieldUExtract %2 %5 %13 %6 ; extracts bit 8 from %5 + %65 = OpBitFieldUExtract %2 %6 %13 %6 ; extracts bit 8 from %6 + %66 = OpBitwiseOr %2 %64 %65 + + %67 = OpBitFieldUExtract %2 %5 %14 %6 ; extracts bit 9 from %5 + %68 = OpBitFieldUExtract %2 %6 %14 %6 ; extracts bit 9 from %6 + %69 = OpBitwiseOr %2 %67 %68 + + %70 = OpBitFieldUExtract %2 %5 %15 %6 ; extracts bit 10 from %5 + %71 = OpBitFieldUExtract %2 %6 %15 %6 ; extracts bit 10 from %6 + %72 = OpBitwiseOr %2 %70 %71 + + %73 = OpBitFieldUExtract %2 %5 %16 %6 ; extracts bit 11 from %5 + %74 = OpBitFieldUExtract %2 %6 %16 %6 ; extracts bit 11 from %6 + %75 = OpBitwiseOr %2 %73 %74 + + %76 = OpBitFieldUExtract %2 %5 %17 %6 ; extracts bit 12 from %5 + %77 = OpBitFieldUExtract %2 %6 %17 %6 ; extracts bit 12 from %6 + %78 = OpBitwiseOr %2 %76 %77 + + %79 = OpBitFieldUExtract %2 %5 %18 %6 ; extracts bit 13 from %5 + %80 = OpBitFieldUExtract %2 %6 %18 %6 ; extracts bit 13 from %6 + %81 = OpBitwiseOr %2 %79 %80 + + %82 = OpBitFieldUExtract %2 %5 %19 %6 ; extracts bit 14 from %5 + %83 = OpBitFieldUExtract %2 %6 %19 %6 ; extracts bit 14 from %6 + %84 = OpBitwiseOr %2 %82 %83 + + %85 = OpBitFieldUExtract %2 %5 %20 %6 ; extracts bit 15 from %5 + %86 = OpBitFieldUExtract %2 %6 %20 %6 ; extracts bit 15 from %6 + %87 = OpBitwiseOr %2 %85 %86 + + %88 = OpBitFieldUExtract %2 %5 %21 %6 ; extracts bit 16 from %5 + %89 = OpBitFieldUExtract %2 %6 %21 %6 ; extracts bit 16 from %6 + %90 = OpBitwiseOr %2 %88 %89 + + %91 = OpBitFieldUExtract %2 %5 %22 %6 ; extracts bit 17 from %5 + %92 = OpBitFieldUExtract %2 %6 %22 %6 ; extracts bit 17 from %6 + %93 = OpBitwiseOr %2 %91 %92 + + %94 = OpBitFieldUExtract %2 %5 %23 %6 ; extracts bit 18 from %5 + %95 = OpBitFieldUExtract %2 %6 %23 %6 ; extracts bit 18 from %6 + %96 = OpBitwiseOr %2 %94 %95 + + %97 = OpBitFieldUExtract %2 %5 %24 %6 ; extracts bit 19 from %5 + %98 = OpBitFieldUExtract %2 %6 %24 %6 ; extracts bit 19 from %6 + %99 = OpBitwiseOr %2 %97 %98 + + %100 = OpBitFieldUExtract %2 %5 %25 %6 ; extracts bit 20 from %5 + %101 = OpBitFieldUExtract %2 %6 %25 %6 ; extracts bit 20 from %6 + %102 = OpBitwiseOr %2 %100 %101 + + %103 = OpBitFieldUExtract %2 %5 %26 %6 ; extracts bit 21 from %5 + %104 = OpBitFieldUExtract %2 %6 %26 %6 ; extracts bit 21 from %6 + %105 = OpBitwiseOr %2 %103 %104 + + %106 = OpBitFieldUExtract %2 %5 %27 %6 ; extracts bit 22 from %5 + %107 = OpBitFieldUExtract %2 %6 %27 %6 ; extracts bit 22 from %6 + %108 = OpBitwiseOr %2 %106 %107 + + %109 = OpBitFieldUExtract %2 %5 %28 %6 ; extracts bit 23 from %5 + %110 = OpBitFieldUExtract %2 %6 %28 %6 ; extracts bit 23 from %6 + %111 = OpBitwiseOr %2 %109 %110 + + %112 = OpBitFieldUExtract %2 %5 %29 %6 ; extracts bit 24 from %5 + %113 = OpBitFieldUExtract %2 %6 %29 %6 ; extracts bit 24 from %6 + %114 = OpBitwiseOr %2 %112 %113 + + %115 = OpBitFieldUExtract %2 %5 %30 %6 ; extracts bit 25 from %5 + %116 = OpBitFieldUExtract %2 %6 %30 %6 ; extracts bit 25 from %6 + %117 = OpBitwiseOr %2 %115 %116 + + %118 = OpBitFieldUExtract %2 %5 %31 %6 ; extracts bit 26 from %5 + %119 = OpBitFieldUExtract %2 %6 %31 %6 ; extracts bit 26 from %6 + %120 = OpBitwiseOr %2 %118 %119 + + %121 = OpBitFieldUExtract %2 %5 %32 %6 ; extracts bit 27 from %5 + %122 = OpBitFieldUExtract %2 %6 %32 %6 ; extracts bit 27 from %6 + %123 = OpBitwiseOr %2 %121 %122 + + %124 = OpBitFieldUExtract %2 %5 %33 %6 ; extracts bit 28 from %5 + %125 = OpBitFieldUExtract %2 %6 %33 %6 ; extracts bit 28 from %6 + %126 = OpBitwiseOr %2 %124 %125 + + %127 = OpBitFieldUExtract %2 %5 %34 %6 ; extracts bit 29 from %5 + %128 = OpBitFieldUExtract %2 %6 %34 %6 ; extracts bit 29 from %6 + %129 = OpBitwiseOr %2 %127 %128 + + %130 = OpBitFieldUExtract %2 %5 %35 %6 ; extracts bit 30 from %5 + %131 = OpBitFieldUExtract %2 %6 %35 %6 ; extracts bit 30 from %6 + %132 = OpBitwiseOr %2 %130 %131 + + %133 = OpBitFieldUExtract %2 %5 %36 %6 ; extracts bit 31 from %5 + %134 = OpBitFieldUExtract %2 %6 %36 %6 ; extracts bit 31 from %6 + %135 = OpBitwiseOr %2 %133 %134 + + %136 = OpBitFieldInsert %2 %42 %45 %6 %6 ; inserts bit 1 + %137 = OpBitFieldInsert %2 %136 %48 %7 %6 ; inserts bit 2 + %138 = OpBitFieldInsert %2 %137 %51 %8 %6 ; inserts bit 3 + %139 = OpBitFieldInsert %2 %138 %54 %9 %6 ; inserts bit 4 + %140 = OpBitFieldInsert %2 %139 %57 %10 %6 ; inserts bit 5 + %141 = OpBitFieldInsert %2 %140 %60 %11 %6 ; inserts bit 6 + %142 = OpBitFieldInsert %2 %141 %63 %12 %6 ; inserts bit 7 + %143 = OpBitFieldInsert %2 %142 %66 %13 %6 ; inserts bit 8 + %144 = OpBitFieldInsert %2 %143 %69 %14 %6 ; inserts bit 9 + %145 = OpBitFieldInsert %2 %144 %72 %15 %6 ; inserts bit 10 + %146 = OpBitFieldInsert %2 %145 %75 %16 %6 ; inserts bit 11 + %147 = OpBitFieldInsert %2 %146 %78 %17 %6 ; inserts bit 12 + %148 = OpBitFieldInsert %2 %147 %81 %18 %6 ; inserts bit 13 + %149 = OpBitFieldInsert %2 %148 %84 %19 %6 ; inserts bit 14 + %150 = OpBitFieldInsert %2 %149 %87 %20 %6 ; inserts bit 15 + %151 = OpBitFieldInsert %2 %150 %90 %21 %6 ; inserts bit 16 + %152 = OpBitFieldInsert %2 %151 %93 %22 %6 ; inserts bit 17 + %153 = OpBitFieldInsert %2 %152 %96 %23 %6 ; inserts bit 18 + %154 = OpBitFieldInsert %2 %153 %99 %24 %6 ; inserts bit 19 + %155 = OpBitFieldInsert %2 %154 %102 %25 %6 ; inserts bit 20 + %156 = OpBitFieldInsert %2 %155 %105 %26 %6 ; inserts bit 21 + %157 = OpBitFieldInsert %2 %156 %108 %27 %6 ; inserts bit 22 + %158 = OpBitFieldInsert %2 %157 %111 %28 %6 ; inserts bit 23 + %159 = OpBitFieldInsert %2 %158 %114 %29 %6 ; inserts bit 24 + %160 = OpBitFieldInsert %2 %159 %117 %30 %6 ; inserts bit 25 + %161 = OpBitFieldInsert %2 %160 %120 %31 %6 ; inserts bit 26 + %162 = OpBitFieldInsert %2 %161 %123 %32 %6 ; inserts bit 27 + %163 = OpBitFieldInsert %2 %162 %126 %33 %6 ; inserts bit 28 + %164 = OpBitFieldInsert %2 %163 %129 %34 %6 ; inserts bit 29 + %165 = OpBitFieldInsert %2 %164 %132 %35 %6 ; inserts bit 30 + %166 = OpBitFieldInsert %2 %165 %135 %36 %6 ; inserts bit 31 + %39 = OpBitwiseOr %2 %5 %6 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationAddBitInstructionSynonymTest, AddOpNotSynonym) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %37 "main" + +; Types + %2 = OpTypeInt 32 0 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstant %2 0 + %6 = OpConstant %2 1 + %7 = OpConstant %2 2 + %8 = OpConstant %2 3 + %9 = OpConstant %2 4 + %10 = OpConstant %2 5 + %11 = OpConstant %2 6 + %12 = OpConstant %2 7 + %13 = OpConstant %2 8 + %14 = OpConstant %2 9 + %15 = OpConstant %2 10 + %16 = OpConstant %2 11 + %17 = OpConstant %2 12 + %18 = OpConstant %2 13 + %19 = OpConstant %2 14 + %20 = OpConstant %2 15 + %21 = OpConstant %2 16 + %22 = OpConstant %2 17 + %23 = OpConstant %2 18 + %24 = OpConstant %2 19 + %25 = OpConstant %2 20 + %26 = OpConstant %2 21 + %27 = OpConstant %2 22 + %28 = OpConstant %2 23 + %29 = OpConstant %2 24 + %30 = OpConstant %2 25 + %31 = OpConstant %2 26 + %32 = OpConstant %2 27 + %33 = OpConstant %2 28 + %34 = OpConstant %2 29 + %35 = OpConstant %2 30 + %36 = OpConstant %2 31 + +; main function + %37 = OpFunction %3 None %4 + %38 = OpLabel + %39 = OpNot %2 %5 ; bit instruction + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Adds OpNot synonym. + auto transformation = TransformationAddBitInstructionSynonym( + 39, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(134, {}), MakeDataDescriptor(39, {}))); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %37 "main" + +; Types + %2 = OpTypeInt 32 0 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstant %2 0 + %6 = OpConstant %2 1 + %7 = OpConstant %2 2 + %8 = OpConstant %2 3 + %9 = OpConstant %2 4 + %10 = OpConstant %2 5 + %11 = OpConstant %2 6 + %12 = OpConstant %2 7 + %13 = OpConstant %2 8 + %14 = OpConstant %2 9 + %15 = OpConstant %2 10 + %16 = OpConstant %2 11 + %17 = OpConstant %2 12 + %18 = OpConstant %2 13 + %19 = OpConstant %2 14 + %20 = OpConstant %2 15 + %21 = OpConstant %2 16 + %22 = OpConstant %2 17 + %23 = OpConstant %2 18 + %24 = OpConstant %2 19 + %25 = OpConstant %2 20 + %26 = OpConstant %2 21 + %27 = OpConstant %2 22 + %28 = OpConstant %2 23 + %29 = OpConstant %2 24 + %30 = OpConstant %2 25 + %31 = OpConstant %2 26 + %32 = OpConstant %2 27 + %33 = OpConstant %2 28 + %34 = OpConstant %2 29 + %35 = OpConstant %2 30 + %36 = OpConstant %2 31 + +; main function + %37 = OpFunction %3 None %4 + %38 = OpLabel + +; Add OpNot synonym + %40 = OpBitFieldUExtract %2 %5 %5 %6 ; extracts bit 0 from %5 + %41 = OpNot %2 %40 + + %42 = OpBitFieldUExtract %2 %5 %6 %6 ; extracts bit 1 from %5 + %43 = OpNot %2 %42 + + %44 = OpBitFieldUExtract %2 %5 %7 %6 ; extracts bit 2 from %5 + %45 = OpNot %2 %44 + + %46 = OpBitFieldUExtract %2 %5 %8 %6 ; extracts bit 3 from %5 + %47 = OpNot %2 %46 + + %48 = OpBitFieldUExtract %2 %5 %9 %6 ; extracts bit 4 from %5 + %49 = OpNot %2 %48 + + %50 = OpBitFieldUExtract %2 %5 %10 %6 ; extracts bit 5 from %5 + %51 = OpNot %2 %50 + + %52 = OpBitFieldUExtract %2 %5 %11 %6 ; extracts bit 6 from %5 + %53 = OpNot %2 %52 + + %54 = OpBitFieldUExtract %2 %5 %12 %6 ; extracts bit 7 from %5 + %55 = OpNot %2 %54 + + %56 = OpBitFieldUExtract %2 %5 %13 %6 ; extracts bit 8 from %5 + %57 = OpNot %2 %56 + + %58 = OpBitFieldUExtract %2 %5 %14 %6 ; extracts bit 9 from %5 + %59 = OpNot %2 %58 + + %60 = OpBitFieldUExtract %2 %5 %15 %6 ; extracts bit 10 from %5 + %61 = OpNot %2 %60 + + %62 = OpBitFieldUExtract %2 %5 %16 %6 ; extracts bit 11 from %5 + %63 = OpNot %2 %62 + + %64 = OpBitFieldUExtract %2 %5 %17 %6 ; extracts bit 12 from %5 + %65 = OpNot %2 %64 + + %66 = OpBitFieldUExtract %2 %5 %18 %6 ; extracts bit 13 from %5 + %67 = OpNot %2 %66 + + %68 = OpBitFieldUExtract %2 %5 %19 %6 ; extracts bit 14 from %5 + %69 = OpNot %2 %68 + + %70 = OpBitFieldUExtract %2 %5 %20 %6 ; extracts bit 15 from %5 + %71 = OpNot %2 %70 + + %72 = OpBitFieldUExtract %2 %5 %21 %6 ; extracts bit 16 from %5 + %73 = OpNot %2 %72 + + %74 = OpBitFieldUExtract %2 %5 %22 %6 ; extracts bit 17 from %5 + %75 = OpNot %2 %74 + + %76 = OpBitFieldUExtract %2 %5 %23 %6 ; extracts bit 18 from %5 + %77 = OpNot %2 %76 + + %78 = OpBitFieldUExtract %2 %5 %24 %6 ; extracts bit 19 from %5 + %79 = OpNot %2 %78 + + %80 = OpBitFieldUExtract %2 %5 %25 %6 ; extracts bit 20 from %5 + %81 = OpNot %2 %80 + + %82 = OpBitFieldUExtract %2 %5 %26 %6 ; extracts bit 21 from %5 + %83 = OpNot %2 %82 + + %84 = OpBitFieldUExtract %2 %5 %27 %6 ; extracts bit 22 from %5 + %85 = OpNot %2 %84 + + %86 = OpBitFieldUExtract %2 %5 %28 %6 ; extracts bit 23 from %5 + %87 = OpNot %2 %86 + + %88 = OpBitFieldUExtract %2 %5 %29 %6 ; extracts bit 24 from %5 + %89 = OpNot %2 %88 + + %90 = OpBitFieldUExtract %2 %5 %30 %6 ; extracts bit 25 from %5 + %91 = OpNot %2 %90 + + %92 = OpBitFieldUExtract %2 %5 %31 %6 ; extracts bit 26 from %5 + %93 = OpNot %2 %92 + + %94 = OpBitFieldUExtract %2 %5 %32 %6 ; extracts bit 27 from %5 + %95 = OpNot %2 %94 + + %96 = OpBitFieldUExtract %2 %5 %33 %6 ; extracts bit 28 from %5 + %97 = OpNot %2 %96 + + %98 = OpBitFieldUExtract %2 %5 %34 %6 ; extracts bit 29 from %5 + %99 = OpNot %2 %98 + + %100 = OpBitFieldUExtract %2 %5 %35 %6 ; extracts bit 30 from %5 + %101 = OpNot %2 %100 + + %102 = OpBitFieldUExtract %2 %5 %36 %6 ; extracts bit 31 from %5 + %103 = OpNot %2 %102 + + %104 = OpBitFieldInsert %2 %41 %43 %6 %6 ; inserts bit 1 + %105 = OpBitFieldInsert %2 %104 %45 %7 %6 ; inserts bit 2 + %106 = OpBitFieldInsert %2 %105 %47 %8 %6 ; inserts bit 3 + %107 = OpBitFieldInsert %2 %106 %49 %9 %6 ; inserts bit 4 + %108 = OpBitFieldInsert %2 %107 %51 %10 %6 ; inserts bit 5 + %109 = OpBitFieldInsert %2 %108 %53 %11 %6 ; inserts bit 6 + %110 = OpBitFieldInsert %2 %109 %55 %12 %6 ; inserts bit 7 + %111 = OpBitFieldInsert %2 %110 %57 %13 %6 ; inserts bit 8 + %112 = OpBitFieldInsert %2 %111 %59 %14 %6 ; inserts bit 9 + %113 = OpBitFieldInsert %2 %112 %61 %15 %6 ; inserts bit 10 + %114 = OpBitFieldInsert %2 %113 %63 %16 %6 ; inserts bit 11 + %115 = OpBitFieldInsert %2 %114 %65 %17 %6 ; inserts bit 12 + %116 = OpBitFieldInsert %2 %115 %67 %18 %6 ; inserts bit 13 + %117 = OpBitFieldInsert %2 %116 %69 %19 %6 ; inserts bit 14 + %118 = OpBitFieldInsert %2 %117 %71 %20 %6 ; inserts bit 15 + %119 = OpBitFieldInsert %2 %118 %73 %21 %6 ; inserts bit 16 + %120 = OpBitFieldInsert %2 %119 %75 %22 %6 ; inserts bit 17 + %121 = OpBitFieldInsert %2 %120 %77 %23 %6 ; inserts bit 18 + %122 = OpBitFieldInsert %2 %121 %79 %24 %6 ; inserts bit 19 + %123 = OpBitFieldInsert %2 %122 %81 %25 %6 ; inserts bit 20 + %124 = OpBitFieldInsert %2 %123 %83 %26 %6 ; inserts bit 21 + %125 = OpBitFieldInsert %2 %124 %85 %27 %6 ; inserts bit 22 + %126 = OpBitFieldInsert %2 %125 %87 %28 %6 ; inserts bit 23 + %127 = OpBitFieldInsert %2 %126 %89 %29 %6 ; inserts bit 24 + %128 = OpBitFieldInsert %2 %127 %91 %30 %6 ; inserts bit 25 + %129 = OpBitFieldInsert %2 %128 %93 %31 %6 ; inserts bit 26 + %130 = OpBitFieldInsert %2 %129 %95 %32 %6 ; inserts bit 27 + %131 = OpBitFieldInsert %2 %130 %97 %33 %6 ; inserts bit 28 + %132 = OpBitFieldInsert %2 %131 %99 %34 %6 ; inserts bit 29 + %133 = OpBitFieldInsert %2 %132 %101 %35 %6 ; inserts bit 30 + %134 = OpBitFieldInsert %2 %133 %103 %36 %6 ; inserts bit 31 + %39 = OpNot %2 %5 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationAddBitInstructionSynonymTest, NoSynonymWhenIdIsIrrelevant) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %37 "main" + +; Types + %2 = OpTypeInt 32 0 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstant %2 0 + %6 = OpConstant %2 1 + %7 = OpConstant %2 2 + %8 = OpConstant %2 3 + %9 = OpConstant %2 4 + %10 = OpConstant %2 5 + %11 = OpConstant %2 6 + %12 = OpConstant %2 7 + %13 = OpConstant %2 8 + %14 = OpConstant %2 9 + %15 = OpConstant %2 10 + %16 = OpConstant %2 11 + %17 = OpConstant %2 12 + %18 = OpConstant %2 13 + %19 = OpConstant %2 14 + %20 = OpConstant %2 15 + %21 = OpConstant %2 16 + %22 = OpConstant %2 17 + %23 = OpConstant %2 18 + %24 = OpConstant %2 19 + %25 = OpConstant %2 20 + %26 = OpConstant %2 21 + %27 = OpConstant %2 22 + %28 = OpConstant %2 23 + %29 = OpConstant %2 24 + %30 = OpConstant %2 25 + %31 = OpConstant %2 26 + %32 = OpConstant %2 27 + %33 = OpConstant %2 28 + %34 = OpConstant %2 29 + %35 = OpConstant %2 30 + %36 = OpConstant %2 31 + +; main function + %37 = OpFunction %3 None %4 + %38 = OpLabel + %39 = OpBitwiseOr %2 %5 %6 ; bit instruction + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Mark the result id of the bit instruction as irrelevant. + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(39); + + // Adds OpBitwiseOr synonym. + auto transformation = TransformationAddBitInstructionSynonym( + 39, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + // No synonym should have been created, since the bit instruction is + // irrelevant. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {}))); +} + +TEST(TransformationAddBitInstructionSynonymTest, NoSynonymWhenBlockIsDead) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %37 "main" + +; Types + %2 = OpTypeInt 32 0 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstant %2 0 + %6 = OpConstant %2 1 + %7 = OpConstant %2 2 + %8 = OpConstant %2 3 + %9 = OpConstant %2 4 + %10 = OpConstant %2 5 + %11 = OpConstant %2 6 + %12 = OpConstant %2 7 + %13 = OpConstant %2 8 + %14 = OpConstant %2 9 + %15 = OpConstant %2 10 + %16 = OpConstant %2 11 + %17 = OpConstant %2 12 + %18 = OpConstant %2 13 + %19 = OpConstant %2 14 + %20 = OpConstant %2 15 + %21 = OpConstant %2 16 + %22 = OpConstant %2 17 + %23 = OpConstant %2 18 + %24 = OpConstant %2 19 + %25 = OpConstant %2 20 + %26 = OpConstant %2 21 + %27 = OpConstant %2 22 + %28 = OpConstant %2 23 + %29 = OpConstant %2 24 + %30 = OpConstant %2 25 + %31 = OpConstant %2 26 + %32 = OpConstant %2 27 + %33 = OpConstant %2 28 + %34 = OpConstant %2 29 + %35 = OpConstant %2 30 + %36 = OpConstant %2 31 + +; main function + %37 = OpFunction %3 None %4 + %38 = OpLabel + %39 = OpBitwiseOr %2 %5 %6 ; bit instruction + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Mark the block where we will try to create the synonym as dead. + transformation_context.GetFactManager()->AddFactBlockIsDead(38); + + // Adds OpBitwiseOr synonym. + auto transformation = TransformationAddBitInstructionSynonym( + 39, {40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 157, 158, 159, 160, 161, 162, 163, 164, 165, 166}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + // No synonym should have been created, since the bit instruction is + // irrelevant. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(166, {}), MakeDataDescriptor(39, {}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_constant_boolean_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_constant_boolean_test.cpp new file mode 100644 index 0000000..3506db6 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_constant_boolean_test.cpp @@ -0,0 +1,190 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_boolean.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddConstantBooleanTest, NeitherPresentInitiallyAddBoth) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %6 = OpTypeBool + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // True and false can both be added as neither is present. + ASSERT_TRUE(TransformationAddConstantBoolean(7, true, false) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddConstantBoolean(7, false, false) + .IsApplicable(context.get(), transformation_context)); + + // Irrelevant true and false can both be added as neither is present. + ASSERT_TRUE(TransformationAddConstantBoolean(7, true, true) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddConstantBoolean(7, false, true) + .IsApplicable(context.get(), transformation_context)); + + // Id 5 is already taken. + ASSERT_FALSE(TransformationAddConstantBoolean(5, true, false) + .IsApplicable(context.get(), transformation_context)); + + auto add_true = TransformationAddConstantBoolean(7, true, false); + auto add_false = TransformationAddConstantBoolean(8, false, false); + + ASSERT_TRUE(add_true.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(add_true, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Having added true, we cannot add it again with the same id. + ASSERT_FALSE(add_true.IsApplicable(context.get(), transformation_context)); + // But we can add it with a different id. + auto add_true_again = TransformationAddConstantBoolean(100, true, false); + ASSERT_TRUE( + add_true_again.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(add_true_again, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE(add_false.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(add_false, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Having added false, we cannot add it again with the same id. + ASSERT_FALSE(add_false.IsApplicable(context.get(), transformation_context)); + // But we can add it with a different id. + auto add_false_again = TransformationAddConstantBoolean(101, false, false); + ASSERT_TRUE( + add_false_again.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(add_false_again, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // We can create an irrelevant OpConstantTrue. + TransformationAddConstantBoolean irrelevant_true(102, true, true); + ASSERT_TRUE( + irrelevant_true.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(irrelevant_true, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // We can create an irrelevant OpConstantFalse. + TransformationAddConstantBoolean irrelevant_false(103, false, true); + ASSERT_TRUE( + irrelevant_false.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(irrelevant_false, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(100)); + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(101)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(102)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(103)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %6 = OpTypeBool + %3 = OpTypeFunction %2 + %7 = OpConstantTrue %6 + %100 = OpConstantTrue %6 + %8 = OpConstantFalse %6 + %101 = OpConstantFalse %6 + %102 = OpConstantTrue %6 + %103 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddConstantBooleanTest, NoOpTypeBoolPresent) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Neither true nor false can be added as OpTypeBool is not present. + ASSERT_FALSE(TransformationAddConstantBoolean(6, true, false) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddConstantBoolean(6, false, false) + .IsApplicable(context.get(), transformation_context)); + + // This does not depend on whether the constant is relevant or not. + ASSERT_FALSE(TransformationAddConstantBoolean(6, true, true) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddConstantBoolean(6, false, true) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_constant_composite_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_constant_composite_test.cpp new file mode 100644 index 0000000..2c296fb --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_constant_composite_test.cpp @@ -0,0 +1,289 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_composite.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddConstantCompositeTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeMatrix %7 3 + %11 = OpConstant %6 0 + %12 = OpConstant %6 1 + %14 = OpConstant %6 2 + %15 = OpConstant %6 3 + %17 = OpConstant %6 4 + %18 = OpConstant %6 5 + %21 = OpTypeInt 32 1 + %22 = OpTypeInt 32 0 + %23 = OpConstant %22 3 + %24 = OpTypeArray %21 %23 + %25 = OpTypeBool + %26 = OpTypeStruct %24 %25 + %29 = OpConstant %21 1 + %30 = OpConstant %21 2 + %31 = OpConstant %21 3 + %33 = OpConstantFalse %25 + %35 = OpTypeVector %6 3 + %38 = OpConstant %6 6 + %39 = OpConstant %6 7 + %40 = OpConstant %6 8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Too few ids + ASSERT_FALSE(TransformationAddConstantComposite(103, 8, {100, 101}, false) + .IsApplicable(context.get(), transformation_context)); + // Too many ids + ASSERT_FALSE(TransformationAddConstantComposite(101, 7, {14, 15, 14}, false) + .IsApplicable(context.get(), transformation_context)); + // Id already in use + ASSERT_FALSE(TransformationAddConstantComposite(40, 7, {11, 12}, false) + .IsApplicable(context.get(), transformation_context)); + // %39 is not a type + ASSERT_FALSE(TransformationAddConstantComposite(100, 39, {11, 12}, false) + .IsApplicable(context.get(), transformation_context)); + + TransformationAddConstantComposite transformations[] = { + // %100 = OpConstantComposite %7 %11 %12 + TransformationAddConstantComposite(100, 7, {11, 12}, false), + + // %101 = OpConstantComposite %7 %14 %15 + TransformationAddConstantComposite(101, 7, {14, 15}, false), + + // %102 = OpConstantComposite %7 %17 %18 + TransformationAddConstantComposite(102, 7, {17, 18}, false), + + // %103 = OpConstantComposite %8 %100 %101 %102 + TransformationAddConstantComposite(103, 8, {100, 101, 102}, false), + + // %104 = OpConstantComposite %24 %29 %30 %31 + TransformationAddConstantComposite(104, 24, {29, 30, 31}, false), + + // %105 = OpConstantComposite %26 %104 %33 + TransformationAddConstantComposite(105, 26, {104, 33}, false), + + // %106 = OpConstantComposite %35 %38 %39 %40 + TransformationAddConstantComposite(106, 35, {38, 39, 40}, false), + + // Same constants but with an irrelevant fact applied. + + // %107 = OpConstantComposite %7 %11 %12 + TransformationAddConstantComposite(107, 7, {11, 12}, true), + + // %108 = OpConstantComposite %7 %14 %15 + TransformationAddConstantComposite(108, 7, {14, 15}, true), + + // %109 = OpConstantComposite %7 %17 %18 + TransformationAddConstantComposite(109, 7, {17, 18}, true), + + // %110 = OpConstantComposite %8 %100 %101 %102 + TransformationAddConstantComposite(110, 8, {100, 101, 102}, true), + + // %111 = OpConstantComposite %24 %29 %30 %31 + TransformationAddConstantComposite(111, 24, {29, 30, 31}, true), + + // %112 = OpConstantComposite %26 %104 %33 + TransformationAddConstantComposite(112, 26, {104, 33}, true), + + // %113 = OpConstantComposite %35 %38 %39 %40 + TransformationAddConstantComposite(113, 35, {38, 39, 40}, true)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + for (uint32_t id = 100; id <= 106; ++id) { + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(id)); + } + + for (uint32_t id = 107; id <= 113; ++id) { + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(id)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeMatrix %7 3 + %11 = OpConstant %6 0 + %12 = OpConstant %6 1 + %14 = OpConstant %6 2 + %15 = OpConstant %6 3 + %17 = OpConstant %6 4 + %18 = OpConstant %6 5 + %21 = OpTypeInt 32 1 + %22 = OpTypeInt 32 0 + %23 = OpConstant %22 3 + %24 = OpTypeArray %21 %23 + %25 = OpTypeBool + %26 = OpTypeStruct %24 %25 + %29 = OpConstant %21 1 + %30 = OpConstant %21 2 + %31 = OpConstant %21 3 + %33 = OpConstantFalse %25 + %35 = OpTypeVector %6 3 + %38 = OpConstant %6 6 + %39 = OpConstant %6 7 + %40 = OpConstant %6 8 + %100 = OpConstantComposite %7 %11 %12 + %101 = OpConstantComposite %7 %14 %15 + %102 = OpConstantComposite %7 %17 %18 + %103 = OpConstantComposite %8 %100 %101 %102 + %104 = OpConstantComposite %24 %29 %30 %31 + %105 = OpConstantComposite %26 %104 %33 + %106 = OpConstantComposite %35 %38 %39 %40 + %107 = OpConstantComposite %7 %11 %12 + %108 = OpConstantComposite %7 %14 %15 + %109 = OpConstantComposite %7 %17 %18 + %110 = OpConstantComposite %8 %100 %101 %102 + %111 = OpConstantComposite %24 %29 %30 %31 + %112 = OpConstantComposite %26 %104 %33 + %113 = OpConstantComposite %35 %38 %39 %40 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddConstantCompositeTest, DisallowBufferBlockDecoration) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 320 + OpName %4 "main" + OpName %7 "buf" + OpMemberName %7 0 "a" + OpMemberName %7 1 "b" + OpName %9 "" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpDecorate %7 BufferBlock + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %10 = OpConstant %6 42 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_0; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationAddConstantComposite(100, 7, {10, 10}, false) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddConstantCompositeTest, DisallowBlockDecoration) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %9 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 320 + OpName %4 "main" + OpName %7 "buf" + OpMemberName %7 0 "a" + OpMemberName %7 1 "b" + OpName %9 "" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %10 = OpConstant %6 42 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer StorageBuffer %7 + %9 = OpVariable %8 StorageBuffer + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationAddConstantComposite(100, 7, {10, 10}, false) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_constant_null_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_constant_null_test.cpp new file mode 100644 index 0000000..ce20a67 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_constant_null_test.cpp @@ -0,0 +1,143 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_null.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddConstantNullTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %20 = OpTypeSampler + %21 = OpTypeImage %6 2D 0 0 0 0 Rgba32f + %22 = OpTypeSampledImage %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id already in use + ASSERT_FALSE(TransformationAddConstantNull(4, 11).IsApplicable( + context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE(TransformationAddConstantNull(100, 1).IsApplicable( + context.get(), transformation_context)); + + // %3 is a function type + ASSERT_FALSE(TransformationAddConstantNull(100, 3).IsApplicable( + context.get(), transformation_context)); + + // %20 is a sampler type + ASSERT_FALSE(TransformationAddConstantNull(100, 20).IsApplicable( + context.get(), transformation_context)); + + // %21 is an image type + ASSERT_FALSE(TransformationAddConstantNull(100, 21).IsApplicable( + context.get(), transformation_context)); + + // %22 is a sampled image type + ASSERT_FALSE(TransformationAddConstantNull(100, 22).IsApplicable( + context.get(), transformation_context)); + + TransformationAddConstantNull transformations[] = { + // %100 = OpConstantNull %6 + TransformationAddConstantNull(100, 6), + + // %101 = OpConstantNull %7 + TransformationAddConstantNull(101, 7), + + // %102 = OpConstantNull %8 + TransformationAddConstantNull(102, 8), + + // %103 = OpConstantNull %9 + TransformationAddConstantNull(103, 9), + + // %104 = OpConstantNull %10 + TransformationAddConstantNull(104, 10), + + // %105 = OpConstantNull %11 + TransformationAddConstantNull(105, 11)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %20 = OpTypeSampler + %21 = OpTypeImage %6 2D 0 0 0 0 Rgba32f + %22 = OpTypeSampledImage %21 + %100 = OpConstantNull %6 + %101 = OpConstantNull %7 + %102 = OpConstantNull %8 + %103 = OpConstantNull %9 + %104 = OpConstantNull %10 + %105 = OpConstantNull %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_constant_scalar_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_constant_scalar_test.cpp new file mode 100644 index 0000000..a153fb1 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_constant_scalar_test.cpp @@ -0,0 +1,356 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_constant_scalar.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddConstantScalarTest, IsApplicable) { + std::string reference_shader = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %17 "main" + +; Types + + ; 32-bit types + %2 = OpTypeInt 32 0 + %3 = OpTypeInt 32 1 + %4 = OpTypeFloat 32 + + ; 64-bit types + %5 = OpTypeInt 64 0 + %6 = OpTypeInt 64 1 + %7 = OpTypeFloat 64 + + %8 = OpTypePointer Private %2 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + +; Constants + + ; 32-bit constants + %11 = OpConstant %2 1 + %12 = OpConstant %3 2 + %13 = OpConstant %4 3 + + ; 64-bit constants + %14 = OpConstant %5 1 + %15 = OpConstant %6 2 + %16 = OpConstant %7 3 + +; main function + %17 = OpFunction %9 None %10 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests |fresh_id| being non-fresh. + auto transformation = TransformationAddConstantScalar(18, 2, {0}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests undefined |type_id|. + transformation = TransformationAddConstantScalar(19, 20, {0}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |type_id| not representing a type instruction. + transformation = TransformationAddConstantScalar(19, 11, {0}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |type_id| representing an OpTypePointer instruction. + transformation = TransformationAddConstantScalar(19, 8, {0}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |type_id| representing an OpTypeVoid instruction. + transformation = TransformationAddConstantScalar(19, 9, {0}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |words| having no words. + transformation = TransformationAddConstantScalar(19, 2, {}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |words| having 2 words for a 32-bit type. + transformation = TransformationAddConstantScalar(19, 2, {0, 1}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |words| having 3 words for a 64-bit type. + transformation = TransformationAddConstantScalar(19, 5, {0, 1, 2}, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddConstantScalarTest, Apply) { + std::string reference_shader = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %17 "main" + +; Types + + ; 32-bit types + %2 = OpTypeInt 32 0 + %3 = OpTypeInt 32 1 + %4 = OpTypeFloat 32 + + ; 64-bit types + %5 = OpTypeInt 64 0 + %6 = OpTypeInt 64 1 + %7 = OpTypeFloat 64 + + %8 = OpTypePointer Private %2 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + +; Constants + + ; 32-bit constants + %11 = OpConstant %2 1 + %12 = OpConstant %3 2 + %13 = OpConstant %4 3 + + ; 64-bit constants + %14 = OpConstant %5 1 + %15 = OpConstant %6 2 + %16 = OpConstant %7 3 + +; main function + %17 = OpFunction %9 None %10 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Adds 32-bit unsigned integer (1 logical operand with 1 word). + auto transformation = TransformationAddConstantScalar(19, 2, {4}, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + auto* constant_instruction = context->get_def_use_mgr()->GetDef(19); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds 32-bit signed integer (1 logical operand with 1 word). + transformation = TransformationAddConstantScalar(20, 3, {5}, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(20); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds 32-bit float (1 logical operand with 1 word). + transformation = TransformationAddConstantScalar( + 21, 4, {0b01000000110000000000000000000000}, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(21); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds 64-bit unsigned integer (1 logical operand with 2 words). + transformation = TransformationAddConstantScalar(22, 5, {7, 0}, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(22); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds 64-bit signed integer (1 logical operand with 2 words). + transformation = TransformationAddConstantScalar(23, 6, {8, 0}, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(23); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds 64-bit float (1 logical operand with 2 words). + transformation = TransformationAddConstantScalar( + 24, 7, {0, 0b01000000001000100000000000000000}, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(24); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds irrelevant 32-bit unsigned integer (1 logical operand with 1 word). + transformation = TransformationAddConstantScalar(25, 2, {10}, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(25); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds irrelevant 32-bit signed integer (1 logical operand with 1 word). + transformation = TransformationAddConstantScalar(26, 3, {11}, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(26); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds irrelevant 32-bit float (1 logical operand with 1 word). + transformation = TransformationAddConstantScalar( + 27, 4, {0b01000001010000000000000000000000}, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(27); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds irrelevant 64-bit unsigned integer (1 logical operand with 2 words). + transformation = TransformationAddConstantScalar(28, 5, {13, 0}, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(28); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds irrelevant 64-bit signed integer (1 logical operand with 2 words). + transformation = TransformationAddConstantScalar(29, 6, {14, 0}, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(29); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds irrelevant 64-bit float (1 logical operand with 2 words). + transformation = TransformationAddConstantScalar( + 30, 7, {0, 0b01000000001011100000000000000000}, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + constant_instruction = context->get_def_use_mgr()->GetDef(30); + EXPECT_EQ(constant_instruction->NumInOperands(), 1); + EXPECT_EQ(constant_instruction->NumInOperandWords(), 2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + for (uint32_t result_id = 19; result_id <= 24; ++result_id) { + ASSERT_FALSE( + transformation_context.GetFactManager()->IdIsIrrelevant(result_id)); + } + + for (uint32_t result_id = 25; result_id <= 30; ++result_id) { + ASSERT_TRUE( + transformation_context.GetFactManager()->IdIsIrrelevant(result_id)); + } + + std::string variant_shader = R"( + OpCapability Shader + OpCapability Int64 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %17 "main" + +; Types + + ; 32-bit types + %2 = OpTypeInt 32 0 + %3 = OpTypeInt 32 1 + %4 = OpTypeFloat 32 + + ; 64-bit types + %5 = OpTypeInt 64 0 + %6 = OpTypeInt 64 1 + %7 = OpTypeFloat 64 + + %8 = OpTypePointer Private %2 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + +; Constants + + ; 32-bit constants + %11 = OpConstant %2 1 + %12 = OpConstant %3 2 + %13 = OpConstant %4 3 + + ; 64-bit constants + %14 = OpConstant %5 1 + %15 = OpConstant %6 2 + %16 = OpConstant %7 3 + + ; added constants + %19 = OpConstant %2 4 + %20 = OpConstant %3 5 + %21 = OpConstant %4 6 + %22 = OpConstant %5 7 + %23 = OpConstant %6 8 + %24 = OpConstant %7 9 + %25 = OpConstant %2 10 + %26 = OpConstant %3 11 + %27 = OpConstant %4 12 + %28 = OpConstant %5 13 + %29 = OpConstant %6 14 + %30 = OpConstant %7 15 + +; main function + %17 = OpFunction %9 None %10 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_copy_memory_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_copy_memory_test.cpp new file mode 100644 index 0000000..642a556 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_copy_memory_test.cpp @@ -0,0 +1,483 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_copy_memory.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddCopyMemoryTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %19 RelaxedPrecision + OpMemberDecorate %66 0 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %78 = OpTypePointer Private %6 + %8 = OpTypeFunction %6 %7 + %17 = OpTypeInt 32 1 + %18 = OpTypePointer Function %17 + %79 = OpTypePointer Private %17 + %20 = OpConstant %17 0 + %21 = OpTypeFloat 32 + %22 = OpTypePointer Function %21 + %80 = OpTypePointer Private %21 + %24 = OpConstant %21 0 + %25 = OpConstantFalse %6 + %32 = OpConstantTrue %6 + %33 = OpTypeVector %21 4 + %34 = OpTypePointer Function %33 + %81 = OpTypePointer Private %33 + %36 = OpConstantComposite %33 %24 %24 %24 %24 + %37 = OpTypeMatrix %33 4 + %84 = OpConstantComposite %37 %36 %36 %36 %36 + %38 = OpTypePointer Function %37 + %82 = OpTypePointer Private %37 + %44 = OpConstant %21 1 + %66 = OpTypeStruct %17 %21 %6 %33 %37 + %85 = OpConstantComposite %66 %20 %24 %25 %36 %84 + %67 = OpTypePointer Function %66 + %83 = OpTypePointer Private %66 + %86 = OpVariable %79 Private %20 + %87 = OpUndef %79 + %88 = OpConstantNull %79 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpVariable %18 Function + %23 = OpVariable %22 Function + %26 = OpVariable %7 Function + %30 = OpVariable %7 Function + %35 = OpVariable %34 Function + %39 = OpVariable %38 Function + %68 = OpVariable %67 Function + OpStore %19 %20 + OpStore %23 %24 + OpStore %26 %25 + %27 = OpFunctionCall %6 %10 %26 + OpSelectionMerge %29 None + OpBranchConditional %27 %28 %31 + %28 = OpLabel + %89 = OpCopyObject %18 %19 + OpBranch %29 + %31 = OpLabel + OpBranch %29 + %76 = OpLabel + %77 = OpLogicalEqual %6 %25 %32 + OpBranch %29 + %29 = OpLabel + %75 = OpPhi %6 %25 %31 %32 %28 %77 %76 + OpStore %30 %75 + %40 = OpLoad %33 %35 + %41 = OpLoad %33 %35 + %42 = OpLoad %33 %35 + %43 = OpLoad %33 %35 + %45 = OpCompositeExtract %21 %40 0 + %46 = OpCompositeExtract %21 %40 1 + %47 = OpCompositeExtract %21 %40 2 + %48 = OpCompositeExtract %21 %40 3 + %49 = OpCompositeExtract %21 %41 0 + %50 = OpCompositeExtract %21 %41 1 + %51 = OpCompositeExtract %21 %41 2 + %52 = OpCompositeExtract %21 %41 3 + %53 = OpCompositeExtract %21 %42 0 + %54 = OpCompositeExtract %21 %42 1 + %55 = OpCompositeExtract %21 %42 2 + %56 = OpCompositeExtract %21 %42 3 + %57 = OpCompositeExtract %21 %43 0 + %58 = OpCompositeExtract %21 %43 1 + %59 = OpCompositeExtract %21 %43 2 + %60 = OpCompositeExtract %21 %43 3 + %61 = OpCompositeConstruct %33 %45 %46 %47 %48 + %62 = OpCompositeConstruct %33 %49 %50 %51 %52 + %63 = OpCompositeConstruct %33 %53 %54 %55 %56 + %64 = OpCompositeConstruct %33 %57 %58 %59 %60 + %65 = OpCompositeConstruct %37 %61 %62 %63 %64 + OpStore %39 %65 + %69 = OpLoad %17 %19 + %70 = OpLoad %21 %23 + %71 = OpLoad %6 %30 + %72 = OpLoad %33 %35 + %73 = OpLoad %37 %39 + %74 = OpCompositeConstruct %66 %69 %70 %71 %72 %73 + OpStore %68 %74 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %13 = OpLoad %6 %9 + OpStore %12 %13 + %14 = OpLoad %6 %12 + OpReturnValue %14 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Target id is not fresh (59). + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 59, 19, + SpvStorageClassPrivate, 20) + .IsApplicable(context.get(), transformation_context)); + + // Instruction descriptor is invalid (id 90 is undefined). + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(90, SpvOpVariable, 0), 90, 19, + SpvStorageClassPrivate, 20) + .IsApplicable(context.get(), transformation_context)); + + // Cannot insert OpCopyMemory before OpPhi. + ASSERT_FALSE( + TransformationAddCopyMemory(MakeInstructionDescriptor(75, SpvOpPhi, 0), + 90, 19, SpvStorageClassPrivate, 20) + .IsApplicable(context.get(), transformation_context)); + + // Source instruction is invalid. + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 76, + SpvStorageClassPrivate, 0) + .IsApplicable(context.get(), transformation_context)); + + // Source instruction's type doesn't exist. + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 5, + SpvStorageClassPrivate, 0) + .IsApplicable(context.get(), transformation_context)); + + // Source instruction's type is invalid. + ASSERT_FALSE( + TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), + 90, 40, SpvStorageClassPrivate, 0) + .IsApplicable(context.get(), transformation_context)); + + // Source instruction is OpUndef. + ASSERT_FALSE( + TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), + 90, 87, SpvStorageClassPrivate, 0) + .IsApplicable(context.get(), transformation_context)); + + // Source instruction is OpConstantNull. + ASSERT_FALSE( + TransformationAddCopyMemory(MakeInstructionDescriptor(41, SpvOpLoad, 0), + 90, 88, SpvStorageClassPrivate, 0) + .IsApplicable(context.get(), transformation_context)); + + // Storage class is invalid. + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 19, + SpvStorageClassWorkgroup, 20) + .IsApplicable(context.get(), transformation_context)); + + // Initializer is 0. + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 19, + SpvStorageClassPrivate, 0) + .IsApplicable(context.get(), transformation_context)); + + // Initializer has wrong type. + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(27, SpvOpFunctionCall, 0), 90, 19, + SpvStorageClassPrivate, 25) + .IsApplicable(context.get(), transformation_context)); + + // Source and target instructions are in different functions. + ASSERT_FALSE( + TransformationAddCopyMemory(MakeInstructionDescriptor(13, SpvOpLoad, 0), + 90, 19, SpvStorageClassPrivate, 20) + .IsApplicable(context.get(), transformation_context)); + + // Source instruction doesn't dominate the target instruction. + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(77, SpvOpLogicalEqual, 0), 90, 89, + SpvStorageClassPrivate, 20) + .IsApplicable(context.get(), transformation_context)); + + // Source and target instructions are the same. + ASSERT_FALSE(TransformationAddCopyMemory( + MakeInstructionDescriptor(19, SpvOpVariable, 0), 90, 19, + SpvStorageClassPrivate, 20) + .IsApplicable(context.get(), transformation_context)); + + // Correct transformations. + uint32_t fresh_id = 90; + auto descriptor = MakeInstructionDescriptor(27, SpvOpFunctionCall, 0); + std::vector source_ids = {19, 23, 26, 30, 35, 39, 68, 86}; + std::vector initializers = {20, 24, 25, 25, 36, 84, 85, 20}; + std::vector storage_classes = {SpvStorageClassPrivate, + SpvStorageClassFunction}; + for (size_t i = 0, n = source_ids.size(); i < n; ++i) { + TransformationAddCopyMemory transformation( + descriptor, fresh_id, source_ids[i], + storage_classes[i % storage_classes.size()], initializers[i]); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + fresh_id)); + fresh_id++; + } + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %19 RelaxedPrecision + OpMemberDecorate %66 0 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %78 = OpTypePointer Private %6 + %8 = OpTypeFunction %6 %7 + %17 = OpTypeInt 32 1 + %18 = OpTypePointer Function %17 + %79 = OpTypePointer Private %17 + %20 = OpConstant %17 0 + %21 = OpTypeFloat 32 + %22 = OpTypePointer Function %21 + %80 = OpTypePointer Private %21 + %24 = OpConstant %21 0 + %25 = OpConstantFalse %6 + %32 = OpConstantTrue %6 + %33 = OpTypeVector %21 4 + %34 = OpTypePointer Function %33 + %81 = OpTypePointer Private %33 + %36 = OpConstantComposite %33 %24 %24 %24 %24 + %37 = OpTypeMatrix %33 4 + %84 = OpConstantComposite %37 %36 %36 %36 %36 + %38 = OpTypePointer Function %37 + %82 = OpTypePointer Private %37 + %44 = OpConstant %21 1 + %66 = OpTypeStruct %17 %21 %6 %33 %37 + %85 = OpConstantComposite %66 %20 %24 %25 %36 %84 + %67 = OpTypePointer Function %66 + %83 = OpTypePointer Private %66 + %86 = OpVariable %79 Private %20 + %87 = OpUndef %79 + %88 = OpConstantNull %79 + %90 = OpVariable %79 Private %20 + %92 = OpVariable %78 Private %25 + %94 = OpVariable %81 Private %36 + %96 = OpVariable %83 Private %85 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %97 = OpVariable %18 Function %20 + %95 = OpVariable %38 Function %84 + %93 = OpVariable %7 Function %25 + %91 = OpVariable %22 Function %24 + %19 = OpVariable %18 Function + %23 = OpVariable %22 Function + %26 = OpVariable %7 Function + %30 = OpVariable %7 Function + %35 = OpVariable %34 Function + %39 = OpVariable %38 Function + %68 = OpVariable %67 Function + OpStore %19 %20 + OpStore %23 %24 + OpStore %26 %25 + OpCopyMemory %90 %19 + OpCopyMemory %91 %23 + OpCopyMemory %92 %26 + OpCopyMemory %93 %30 + OpCopyMemory %94 %35 + OpCopyMemory %95 %39 + OpCopyMemory %96 %68 + OpCopyMemory %97 %86 + %27 = OpFunctionCall %6 %10 %26 + OpSelectionMerge %29 None + OpBranchConditional %27 %28 %31 + %28 = OpLabel + %89 = OpCopyObject %18 %19 + OpBranch %29 + %31 = OpLabel + OpBranch %29 + %76 = OpLabel + %77 = OpLogicalEqual %6 %25 %32 + OpBranch %29 + %29 = OpLabel + %75 = OpPhi %6 %25 %31 %32 %28 %77 %76 + OpStore %30 %75 + %40 = OpLoad %33 %35 + %41 = OpLoad %33 %35 + %42 = OpLoad %33 %35 + %43 = OpLoad %33 %35 + %45 = OpCompositeExtract %21 %40 0 + %46 = OpCompositeExtract %21 %40 1 + %47 = OpCompositeExtract %21 %40 2 + %48 = OpCompositeExtract %21 %40 3 + %49 = OpCompositeExtract %21 %41 0 + %50 = OpCompositeExtract %21 %41 1 + %51 = OpCompositeExtract %21 %41 2 + %52 = OpCompositeExtract %21 %41 3 + %53 = OpCompositeExtract %21 %42 0 + %54 = OpCompositeExtract %21 %42 1 + %55 = OpCompositeExtract %21 %42 2 + %56 = OpCompositeExtract %21 %42 3 + %57 = OpCompositeExtract %21 %43 0 + %58 = OpCompositeExtract %21 %43 1 + %59 = OpCompositeExtract %21 %43 2 + %60 = OpCompositeExtract %21 %43 3 + %61 = OpCompositeConstruct %33 %45 %46 %47 %48 + %62 = OpCompositeConstruct %33 %49 %50 %51 %52 + %63 = OpCompositeConstruct %33 %53 %54 %55 %56 + %64 = OpCompositeConstruct %33 %57 %58 %59 %60 + %65 = OpCompositeConstruct %37 %61 %62 %63 %64 + OpStore %39 %65 + %69 = OpLoad %17 %19 + %70 = OpLoad %21 %23 + %71 = OpLoad %6 %30 + %72 = OpLoad %33 %35 + %73 = OpLoad %37 %39 + %74 = OpCompositeConstruct %66 %69 %70 %71 %72 %73 + OpStore %68 %74 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %13 = OpLoad %6 %9 + OpStore %12 %13 + %14 = OpLoad %6 %12 + OpReturnValue %14 + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddCopyMemoryTest, DisallowBufferBlockDecoration) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 320 + OpName %4 "main" + OpName %7 "buf" + OpMemberName %7 0 "a" + OpMemberName %7 1 "b" + OpName %9 "" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpDecorate %7 BufferBlock + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %10 = OpConstant %6 42 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Uniform %7 + %9 = OpVariable %8 Uniform + %50 = OpUndef %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_0; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE( + TransformationAddCopyMemory(MakeInstructionDescriptor(5, SpvOpReturn, 0), + 100, 9, SpvStorageClassPrivate, 50) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddCopyMemoryTest, DisallowBlockDecoration) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %9 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 320 + OpName %4 "main" + OpName %7 "buf" + OpMemberName %7 0 "a" + OpMemberName %7 1 "b" + OpName %9 "" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpDecorate %7 Block + OpDecorate %9 DescriptorSet 0 + OpDecorate %9 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %10 = OpConstant %6 42 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer StorageBuffer %7 + %9 = OpVariable %8 StorageBuffer + %50 = OpUndef %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE( + TransformationAddCopyMemory(MakeInstructionDescriptor(5, SpvOpReturn, 0), + 100, 9, SpvStorageClassPrivate, 50) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools \ No newline at end of file diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_dead_block_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_dead_block_test.cpp new file mode 100644 index 0000000..687f00c --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_dead_block_test.cpp @@ -0,0 +1,387 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_block.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddDeadBlockTest, BasicTest) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + +; Types + %2 = OpTypeBool + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstantTrue %2 + +; main function + %6 = OpFunction %3 None %4 + %7 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %5 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id 4 is already in use + auto transformation = TransformationAddDeadBlock(4, 11, true); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Id 5 is not a block + transformation = TransformationAddDeadBlock(14, 5, true); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests existing block not dominating its successor block. + transformation = TransformationAddDeadBlock(14, 8, true); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation = TransformationAddDeadBlock(14, 9, true); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests existing block being an unreachable block. + transformation = TransformationAddDeadBlock(14, 12, true); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests applicable case. + transformation = TransformationAddDeadBlock(14, 11, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(14)); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + +; Types + %2 = OpTypeBool + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstantTrue %2 + +; main function + %6 = OpFunction %3 None %4 + %7 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %5 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %5 %13 %14 + %14 = OpLabel + OpBranch %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeSelectionMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBlockTest, TargetBlockMustNotBeLoopMergeOrContinue) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + +; Types + %2 = OpTypeBool + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; Constants + %5 = OpConstantTrue %2 + +; main function + %6 = OpFunction %3 None %4 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %12 %11 None + OpBranchConditional %5 %9 %10 + %9 = OpLabel + OpBranch %11 + %10 = OpLabel + OpBranch %12 + %11 = OpLabel + OpBranch %8 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad because 9's successor is the loop continue target. + ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true) + .IsApplicable(context.get(), transformation_context)); + // Bad because 10's successor is the loop merge. + ASSERT_FALSE(TransformationAddDeadBlock(100, 10, true) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBlockTest, SourceBlockMustNotBeLoopHead) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %11 %12 None + OpBranch %9 + %9 = OpLabel + OpBranchConditional %7 %11 %12 + %12 = OpLabel + OpBranch %8 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad because 8 is a loop head. + ASSERT_FALSE(TransformationAddDeadBlock(100, 8, true) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBlockTest, OpPhiInTarget) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %9 = OpTypeInt 32 0 + %10 = OpConstant %9 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + %12 = OpPhi %6 %7 %5 + %13 = OpPhi %9 %10 %5 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationAddDeadBlock transformation(100, 5, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %9 = OpTypeInt 32 0 + %10 = OpConstant %9 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %7 %8 %100 + %100 = OpLabel + OpBranch %8 + %8 = OpLabel + %12 = OpPhi %6 %7 %5 %7 %100 + %13 = OpPhi %9 %10 %5 %10 %100 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBlockTest, BackEdge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + OpLoopMerge %10 %9 None + OpBranchConditional %7 %9 %10 + %9 = OpLabel + OpBranch %8 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // 9 is a back edge block, so it would not be OK to add a dead block here, + // as then both 9 and the dead block would branch to the loop header, 8. + ASSERT_FALSE(TransformationAddDeadBlock(100, 9, true) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_dead_break_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_dead_break_test.cpp new file mode 100644 index 0000000..5302d8a --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_dead_break_test.cpp @@ -0,0 +1,2874 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_break.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddDeadBreakTest, BreaksOutOfSimpleIf) { + // For a simple if-then-else, checks that some dead break scenarios are + // possible, and that some invalid scenarios are indeed not allowed. + + // The SPIR-V for this test is adapted from the following GLSL, by separating + // some assignments into their own basic blocks, and adding constants for true + // and false: + // + // void main() { + // int x; + // int y; + // x = 1; + // if (x < y) { + // x = 2; + // x = 3; + // } else { + // y = 2; + // y = 3; + // } + // x = y; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %11 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpTypeBool + %17 = OpConstant %6 2 + %18 = OpConstant %6 3 + %25 = OpConstantTrue %13 + %26 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %11 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLoad %6 %11 + %14 = OpSLessThan %13 %10 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %19 + %15 = OpLabel + OpStore %8 %17 + OpBranch %21 + %21 = OpLabel + OpStore %8 %18 + OpBranch %22 + %22 = OpLabel + OpBranch %16 + %19 = OpLabel + OpStore %11 %17 + OpBranch %23 + %23 = OpLabel + OpStore %11 %18 + OpBranch %24 + %24 = OpLabel + OpBranch %16 + %16 = OpLabel + %20 = OpLoad %6 %11 + OpStore %8 %20 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + const uint32_t merge_block = 16; + + // These are all possibilities. + ASSERT_TRUE(TransformationAddDeadBreak(15, merge_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(15, merge_block, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(21, merge_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(21, merge_block, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(22, merge_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(22, merge_block, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(19, merge_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(19, merge_block, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(23, merge_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(23, merge_block, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(24, merge_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(24, merge_block, false, {}) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable: 100 is not a block id. + ASSERT_FALSE(TransformationAddDeadBreak(100, merge_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(15, 100, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable: 24 is not a merge block. + ASSERT_FALSE(TransformationAddDeadBreak(15, 24, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // These are the transformations we will apply. + auto transformation1 = TransformationAddDeadBreak(15, merge_block, true, {}); + auto transformation2 = TransformationAddDeadBreak(21, merge_block, false, {}); + auto transformation3 = TransformationAddDeadBreak(22, merge_block, true, {}); + auto transformation4 = TransformationAddDeadBreak(19, merge_block, false, {}); + auto transformation5 = TransformationAddDeadBreak(23, merge_block, true, {}); + auto transformation6 = TransformationAddDeadBreak(24, merge_block, false, {}); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation6, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %11 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpTypeBool + %17 = OpConstant %6 2 + %18 = OpConstant %6 3 + %25 = OpConstantTrue %13 + %26 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %11 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLoad %6 %11 + %14 = OpSLessThan %13 %10 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %19 + %15 = OpLabel + OpStore %8 %17 + OpBranchConditional %25 %21 %16 + %21 = OpLabel + OpStore %8 %18 + OpBranchConditional %26 %16 %22 + %22 = OpLabel + OpBranchConditional %25 %16 %16 + %19 = OpLabel + OpStore %11 %17 + OpBranchConditional %26 %16 %23 + %23 = OpLabel + OpStore %11 %18 + OpBranchConditional %25 %24 %16 + %24 = OpLabel + OpBranchConditional %26 %16 %16 + %16 = OpLabel + %20 = OpLoad %6 %11 + OpStore %8 %20 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, BreakOutOfNestedIfs) { + // Checks some allowed and disallowed scenarios for nests of ifs. + + // The SPIR-V for this test is adapted from the following GLSL: + // + // void main() { + // int x; + // int y; + // x = 1; + // if (x < y) { + // x = 2; + // x = 3; + // if (x == y) { + // y = 3; + // } + // } else { + // y = 2; + // y = 3; + // } + // if (x == y) { + // x = 2; + // } + // x = y; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %11 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpTypeBool + %17 = OpConstant %6 2 + %18 = OpConstant %6 3 + %31 = OpConstantTrue %13 + %32 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %11 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLoad %6 %11 + %14 = OpSLessThan %13 %10 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %24 + %15 = OpLabel + OpStore %8 %17 + OpBranch %33 + %33 = OpLabel + OpStore %8 %18 + %19 = OpLoad %6 %8 + OpBranch %34 + %34 = OpLabel + %20 = OpLoad %6 %11 + %21 = OpIEqual %13 %19 %20 + OpSelectionMerge %23 None + OpBranchConditional %21 %22 %23 + %22 = OpLabel + OpStore %11 %18 + OpBranch %35 + %35 = OpLabel + OpBranch %23 + %23 = OpLabel + OpBranch %16 + %24 = OpLabel + OpStore %11 %17 + OpBranch %36 + %36 = OpLabel + OpStore %11 %18 + OpBranch %16 + %16 = OpLabel + %25 = OpLoad %6 %8 + OpBranch %37 + %37 = OpLabel + %26 = OpLoad %6 %11 + %27 = OpIEqual %13 %25 %26 + OpSelectionMerge %29 None + OpBranchConditional %27 %28 %29 + %28 = OpLabel + OpStore %8 %17 + OpBranch %38 + %38 = OpLabel + OpBranch %29 + %29 = OpLabel + %30 = OpLoad %6 %11 + OpStore %8 %30 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // The header and merge blocks + const uint32_t header_inner = 34; + const uint32_t merge_inner = 23; + const uint32_t header_outer = 5; + const uint32_t merge_outer = 16; + const uint32_t header_after = 37; + const uint32_t merge_after = 29; + + // The non-merge-nor-header blocks in each construct + const uint32_t inner_block_1 = 22; + const uint32_t inner_block_2 = 35; + const uint32_t outer_block_1 = 15; + const uint32_t outer_block_2 = 33; + const uint32_t outer_block_3 = 24; + const uint32_t outer_block_4 = 36; + const uint32_t after_block_1 = 28; + const uint32_t after_block_2 = 38; + + // Fine to break from a construct to its merge + ASSERT_TRUE(TransformationAddDeadBreak(inner_block_1, merge_inner, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(inner_block_2, merge_inner, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(outer_block_1, merge_outer, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(outer_block_2, merge_outer, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(outer_block_3, merge_outer, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(outer_block_4, merge_outer, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(after_block_1, merge_after, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(after_block_2, merge_after, false, {}) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to break to the wrong merge (whether enclosing or not) + ASSERT_FALSE(TransformationAddDeadBreak(inner_block_1, merge_outer, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(inner_block_2, merge_after, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(outer_block_1, merge_inner, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(outer_block_2, merge_after, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(after_block_1, merge_inner, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(after_block_2, merge_outer, false, {}) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to break from header (as it does not branch unconditionally) + ASSERT_FALSE(TransformationAddDeadBreak(header_inner, merge_inner, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(header_outer, merge_outer, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(header_after, merge_after, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to break to non-merge + ASSERT_FALSE( + TransformationAddDeadBreak(inner_block_1, inner_block_2, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddDeadBreak(outer_block_2, after_block_1, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(outer_block_1, header_after, true, {}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = + TransformationAddDeadBreak(inner_block_1, merge_inner, true, {}); + auto transformation2 = + TransformationAddDeadBreak(inner_block_2, merge_inner, false, {}); + auto transformation3 = + TransformationAddDeadBreak(outer_block_1, merge_outer, true, {}); + auto transformation4 = + TransformationAddDeadBreak(outer_block_2, merge_outer, false, {}); + auto transformation5 = + TransformationAddDeadBreak(outer_block_3, merge_outer, true, {}); + auto transformation6 = + TransformationAddDeadBreak(outer_block_4, merge_outer, false, {}); + auto transformation7 = + TransformationAddDeadBreak(after_block_1, merge_after, true, {}); + auto transformation8 = + TransformationAddDeadBreak(after_block_2, merge_after, false, {}); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation6, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation7, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation8.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation8, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %11 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpTypeBool + %17 = OpConstant %6 2 + %18 = OpConstant %6 3 + %31 = OpConstantTrue %13 + %32 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %11 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLoad %6 %11 + %14 = OpSLessThan %13 %10 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %24 + %15 = OpLabel + OpStore %8 %17 + OpBranchConditional %31 %33 %16 + %33 = OpLabel + OpStore %8 %18 + %19 = OpLoad %6 %8 + OpBranchConditional %32 %16 %34 + %34 = OpLabel + %20 = OpLoad %6 %11 + %21 = OpIEqual %13 %19 %20 + OpSelectionMerge %23 None + OpBranchConditional %21 %22 %23 + %22 = OpLabel + OpStore %11 %18 + OpBranchConditional %31 %35 %23 + %35 = OpLabel + OpBranchConditional %32 %23 %23 + %23 = OpLabel + OpBranch %16 + %24 = OpLabel + OpStore %11 %17 + OpBranchConditional %31 %36 %16 + %36 = OpLabel + OpStore %11 %18 + OpBranchConditional %32 %16 %16 + %16 = OpLabel + %25 = OpLoad %6 %8 + OpBranch %37 + %37 = OpLabel + %26 = OpLoad %6 %11 + %27 = OpIEqual %13 %25 %26 + OpSelectionMerge %29 None + OpBranchConditional %27 %28 %29 + %28 = OpLabel + OpStore %8 %17 + OpBranchConditional %31 %38 %29 + %38 = OpLabel + OpBranchConditional %32 %29 %29 + %29 = OpLabel + %30 = OpLoad %6 %11 + OpStore %8 %30 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, BreakOutOfNestedSwitches) { + // Checks some allowed and disallowed scenarios for nests of switches. + + // The SPIR-V for this test is adapted from the following GLSL: + // + // void main() { + // int x; + // int y; + // x = 1; + // if (x < y) { + // switch (x) { + // case 0: + // case 1: + // if (x == y) { + // } + // x = 2; + // break; + // case 3: + // if (y == 4) { + // y = 2; + // x = 3; + // } + // case 10: + // break; + // default: + // switch (y) { + // case 1: + // break; + // case 2: + // x = 4; + // y = 2; + // default: + // x = 3; + // break; + // } + // } + // } else { + // switch (y) { + // case 1: + // x = 4; + // case 2: + // y = 3; + // default: + // x = y; + // break; + // } + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %11 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpTypeBool + %29 = OpConstant %6 2 + %32 = OpConstant %6 4 + %36 = OpConstant %6 3 + %60 = OpConstantTrue %13 + %61 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %11 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLoad %6 %11 + %14 = OpSLessThan %13 %10 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %47 + %15 = OpLabel + %17 = OpLoad %6 %8 + OpSelectionMerge %22 None + OpSwitch %17 %21 0 %18 1 %18 3 %19 10 %20 + %21 = OpLabel + %38 = OpLoad %6 %11 + OpSelectionMerge %42 None + OpSwitch %38 %41 1 %39 2 %40 + %41 = OpLabel + OpStore %8 %36 + OpBranch %42 + %39 = OpLabel + OpBranch %42 + %40 = OpLabel + OpStore %8 %32 + OpStore %11 %29 + OpBranch %41 + %42 = OpLabel + OpBranch %22 + %18 = OpLabel + %23 = OpLoad %6 %8 + OpBranch %63 + %63 = OpLabel + %24 = OpLoad %6 %11 + %25 = OpIEqual %13 %23 %24 + OpSelectionMerge %27 None + OpBranchConditional %25 %26 %27 + %26 = OpLabel + OpBranch %27 + %27 = OpLabel + OpStore %8 %29 + OpBranch %22 + %19 = OpLabel + %31 = OpLoad %6 %11 + %33 = OpIEqual %13 %31 %32 + OpSelectionMerge %35 None + OpBranchConditional %33 %34 %35 + %34 = OpLabel + OpStore %11 %29 + OpBranch %62 + %62 = OpLabel + OpStore %8 %36 + OpBranch %35 + %35 = OpLabel + OpBranch %20 + %20 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %16 + %47 = OpLabel + %48 = OpLoad %6 %11 + OpSelectionMerge %52 None + OpSwitch %48 %51 1 %49 2 %50 + %51 = OpLabel + %53 = OpLoad %6 %11 + OpStore %8 %53 + OpBranch %52 + %49 = OpLabel + OpStore %8 %32 + OpBranch %50 + %50 = OpLabel + OpStore %11 %36 + OpBranch %51 + %52 = OpLabel + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // The header and merge blocks + const uint32_t header_outer_if = 5; + const uint32_t merge_outer_if = 16; + const uint32_t header_then_outer_switch = 15; + const uint32_t merge_then_outer_switch = 22; + const uint32_t header_then_inner_switch = 21; + const uint32_t merge_then_inner_switch = 42; + const uint32_t header_else_switch = 47; + const uint32_t merge_else_switch = 52; + const uint32_t header_inner_if_1 = 19; + const uint32_t merge_inner_if_1 = 35; + const uint32_t header_inner_if_2 = 63; + const uint32_t merge_inner_if_2 = 27; + + // The non-merge-nor-header blocks in each construct + const uint32_t then_outer_switch_block_1 = 18; + const uint32_t then_inner_switch_block_1 = 39; + const uint32_t then_inner_switch_block_2 = 40; + const uint32_t then_inner_switch_block_3 = 41; + const uint32_t else_switch_block_1 = 49; + const uint32_t else_switch_block_2 = 50; + const uint32_t else_switch_block_3 = 51; + const uint32_t inner_if_1_block_1 = 34; + const uint32_t inner_if_1_block_2 = 62; + const uint32_t inner_if_2_block_1 = 26; + + // Fine to branch straight to direct merge block for a construct + ASSERT_TRUE(TransformationAddDeadBreak(then_outer_switch_block_1, + merge_then_outer_switch, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_1, + merge_then_inner_switch, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_2, + merge_then_inner_switch, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(then_inner_switch_block_3, + merge_then_inner_switch, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_1, merge_else_switch, + false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_2, merge_else_switch, + true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(else_switch_block_3, merge_else_switch, + false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationAddDeadBreak(inner_if_1_block_1, merge_inner_if_1, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(inner_if_1_block_2, merge_inner_if_1, + false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationAddDeadBreak(inner_if_2_block_1, merge_inner_if_2, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to break out of a switch from a selection construct inside the + // switch. + ASSERT_FALSE(TransformationAddDeadBreak(inner_if_1_block_1, + merge_then_outer_switch, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(inner_if_1_block_2, + merge_then_outer_switch, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(inner_if_2_block_1, + merge_then_outer_switch, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Some miscellaneous inapplicable cases. + ASSERT_FALSE( + TransformationAddDeadBreak(header_outer_if, merge_outer_if, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(header_inner_if_1, inner_if_1_block_2, + false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(header_then_inner_switch, + header_then_outer_switch, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(header_else_switch, + then_inner_switch_block_3, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(header_inner_if_2, header_inner_if_2, + false, {}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = TransformationAddDeadBreak( + then_outer_switch_block_1, merge_then_outer_switch, true, {}); + auto transformation2 = TransformationAddDeadBreak( + then_inner_switch_block_1, merge_then_inner_switch, false, {}); + auto transformation3 = TransformationAddDeadBreak( + then_inner_switch_block_2, merge_then_inner_switch, true, {}); + auto transformation4 = TransformationAddDeadBreak( + then_inner_switch_block_3, merge_then_inner_switch, true, {}); + auto transformation5 = TransformationAddDeadBreak( + else_switch_block_1, merge_else_switch, false, {}); + auto transformation6 = TransformationAddDeadBreak( + else_switch_block_2, merge_else_switch, true, {}); + auto transformation7 = TransformationAddDeadBreak( + else_switch_block_3, merge_else_switch, false, {}); + auto transformation8 = TransformationAddDeadBreak(inner_if_1_block_1, + merge_inner_if_1, true, {}); + auto transformation9 = TransformationAddDeadBreak( + inner_if_1_block_2, merge_inner_if_1, false, {}); + auto transformation10 = TransformationAddDeadBreak( + inner_if_2_block_1, merge_inner_if_2, true, {}); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation6, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation7, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation8.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation8, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation9.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation9, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation10.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation10, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %11 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpTypeBool + %29 = OpConstant %6 2 + %32 = OpConstant %6 4 + %36 = OpConstant %6 3 + %60 = OpConstantTrue %13 + %61 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %11 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLoad %6 %11 + %14 = OpSLessThan %13 %10 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %47 + %15 = OpLabel + %17 = OpLoad %6 %8 + OpSelectionMerge %22 None + OpSwitch %17 %21 0 %18 1 %18 3 %19 10 %20 + %21 = OpLabel + %38 = OpLoad %6 %11 + OpSelectionMerge %42 None + OpSwitch %38 %41 1 %39 2 %40 + %41 = OpLabel + OpStore %8 %36 + OpBranchConditional %60 %42 %42 + %39 = OpLabel + OpBranchConditional %61 %42 %42 + %40 = OpLabel + OpStore %8 %32 + OpStore %11 %29 + OpBranchConditional %60 %41 %42 + %42 = OpLabel + OpBranch %22 + %18 = OpLabel + %23 = OpLoad %6 %8 + OpBranchConditional %60 %63 %22 + %63 = OpLabel + %24 = OpLoad %6 %11 + %25 = OpIEqual %13 %23 %24 + OpSelectionMerge %27 None + OpBranchConditional %25 %26 %27 + %26 = OpLabel + OpBranchConditional %60 %27 %27 + %27 = OpLabel + OpStore %8 %29 + OpBranch %22 + %19 = OpLabel + %31 = OpLoad %6 %11 + %33 = OpIEqual %13 %31 %32 + OpSelectionMerge %35 None + OpBranchConditional %33 %34 %35 + %34 = OpLabel + OpStore %11 %29 + OpBranchConditional %60 %62 %35 + %62 = OpLabel + OpStore %8 %36 + OpBranchConditional %61 %35 %35 + %35 = OpLabel + OpBranch %20 + %20 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %16 + %47 = OpLabel + %48 = OpLoad %6 %11 + OpSelectionMerge %52 None + OpSwitch %48 %51 1 %49 2 %50 + %51 = OpLabel + %53 = OpLoad %6 %11 + OpStore %8 %53 + OpBranchConditional %61 %52 %52 + %49 = OpLabel + OpStore %8 %32 + OpBranchConditional %61 %52 %50 + %50 = OpLabel + OpStore %11 %36 + OpBranchConditional %60 %51 %52 + %52 = OpLabel + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, BreakOutOfLoopNest) { + // Checks some allowed and disallowed scenarios for a nest of loops, including + // breaking from an if or switch right out of a loop. + + // The SPIR-V for this test is adapted from the following GLSL: + // + // void main() { + // int x, y; + // do { + // x++; + // for (int j = 0; j < 100; j++) { + // y++; + // if (x == y) { + // x++; + // if (x == 2) { + // y++; + // } + // switch (x) { + // case 0: + // x = 2; + // default: + // break; + // } + // } + // } + // } while (x > y); + // + // for (int i = 0; i < 100; i++) { + // x++; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "x" + OpName %16 "j" + OpName %27 "y" + OpName %55 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %14 = OpConstant %10 1 + %17 = OpConstant %10 0 + %24 = OpConstant %10 100 + %25 = OpTypeBool + %38 = OpConstant %10 2 + %67 = OpConstantTrue %25 + %68 = OpConstantFalse %25 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %11 Function + %16 = OpVariable %11 Function + %27 = OpVariable %11 Function + %55 = OpVariable %11 Function + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranch %7 + %7 = OpLabel + %13 = OpLoad %10 %12 + %15 = OpIAdd %10 %13 %14 + OpStore %12 %15 + OpStore %16 %17 + OpBranch %18 + %18 = OpLabel + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + %23 = OpLoad %10 %16 + %26 = OpSLessThan %25 %23 %24 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + %28 = OpLoad %10 %27 + %29 = OpIAdd %10 %28 %14 + OpStore %27 %29 + %30 = OpLoad %10 %12 + %31 = OpLoad %10 %27 + %32 = OpIEqual %25 %30 %31 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %34 + %33 = OpLabel + %35 = OpLoad %10 %12 + %36 = OpIAdd %10 %35 %14 + OpStore %12 %36 + %37 = OpLoad %10 %12 + %39 = OpIEqual %25 %37 %38 + OpSelectionMerge %41 None + OpBranchConditional %39 %40 %41 + %40 = OpLabel + %42 = OpLoad %10 %27 + %43 = OpIAdd %10 %42 %14 + OpStore %27 %43 + OpBranch %41 + %41 = OpLabel + %44 = OpLoad %10 %12 + OpSelectionMerge %47 None + OpSwitch %44 %46 0 %45 + %46 = OpLabel + OpBranch %47 + %45 = OpLabel + OpStore %12 %38 + OpBranch %46 + %47 = OpLabel + OpBranch %34 + %34 = OpLabel + OpBranch %21 + %21 = OpLabel + %50 = OpLoad %10 %16 + %51 = OpIAdd %10 %50 %14 + OpStore %16 %51 + OpBranch %18 + %20 = OpLabel + OpBranch %9 + %9 = OpLabel + %52 = OpLoad %10 %12 + %53 = OpLoad %10 %27 + %54 = OpSGreaterThan %25 %52 %53 + OpBranchConditional %54 %6 %8 + %8 = OpLabel + OpStore %55 %17 + OpBranch %56 + %56 = OpLabel + OpLoopMerge %58 %59 None + OpBranch %60 + %60 = OpLabel + %61 = OpLoad %10 %55 + %62 = OpSLessThan %25 %61 %24 + OpBranchConditional %62 %57 %58 + %57 = OpLabel + %63 = OpLoad %10 %12 + %64 = OpIAdd %10 %63 %14 + OpStore %12 %64 + OpBranch %59 + %59 = OpLabel + %65 = OpLoad %10 %55 + %66 = OpIAdd %10 %65 %14 + OpStore %55 %66 + OpBranch %56 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // The header and merge blocks + const uint32_t header_do_while = 6; + const uint32_t merge_do_while = 8; + const uint32_t header_for_j = 18; + const uint32_t merge_for_j = 20; + const uint32_t header_for_i = 56; + const uint32_t merge_for_i = 58; + const uint32_t header_switch = 41; + const uint32_t merge_switch = 47; + const uint32_t header_if_x_eq_y = 19; + const uint32_t merge_if_x_eq_y = 34; + const uint32_t header_if_x_eq_2 = 33; + const uint32_t merge_if_x_eq_2 = 41; + + // Loop continue targets + const uint32_t continue_do_while = 9; + const uint32_t continue_for_j = 21; + const uint32_t continue_for_i = 59; + + // Some blocks in these constructs + const uint32_t block_in_inner_if = 40; + const uint32_t block_switch_case = 46; + const uint32_t block_switch_default = 45; + const uint32_t block_in_for_i_loop = 57; + + // Fine to break from any loop header to its merge + ASSERT_TRUE( + TransformationAddDeadBreak(header_do_while, merge_do_while, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(header_for_i, merge_for_i, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(header_for_j, merge_for_j, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Fine to break from any of the blocks in constructs in the "for j" loop to + // that loop's merge + ASSERT_TRUE( + TransformationAddDeadBreak(block_in_inner_if, merge_for_j, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationAddDeadBreak(block_switch_case, merge_for_j, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationAddDeadBreak(block_switch_default, merge_for_j, false, {}) + .IsApplicable(context.get(), transformation_context)); + + // Fine to break from the body of the "for i" loop to that loop's merge + ASSERT_TRUE( + TransformationAddDeadBreak(block_in_for_i_loop, merge_for_i, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to break from multiple loops + ASSERT_FALSE( + TransformationAddDeadBreak(block_in_inner_if, merge_do_while, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddDeadBreak(block_switch_case, merge_do_while, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(block_switch_default, merge_do_while, + false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddDeadBreak(header_for_j, merge_do_while, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to break loop from its continue construct, except from the back-edge + // block. + ASSERT_FALSE( + TransformationAddDeadBreak(continue_do_while, merge_do_while, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(continue_for_j, merge_for_j, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(continue_for_i, merge_for_i, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to break out of multiple non-loop constructs if not breaking to a + // loop merge + ASSERT_FALSE( + TransformationAddDeadBreak(block_in_inner_if, merge_if_x_eq_y, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddDeadBreak(block_switch_case, merge_if_x_eq_y, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(block_switch_default, merge_if_x_eq_y, + false, {}) + .IsApplicable(context.get(), transformation_context)); + + // Some miscellaneous inapplicable transformations + ASSERT_FALSE( + TransformationAddDeadBreak(header_if_x_eq_2, header_if_x_eq_y, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddDeadBreak(merge_if_x_eq_2, merge_switch, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddDeadBreak(header_switch, header_switch, false, {}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = + TransformationAddDeadBreak(header_do_while, merge_do_while, true, {}); + auto transformation2 = + TransformationAddDeadBreak(header_for_i, merge_for_i, false, {}); + auto transformation3 = + TransformationAddDeadBreak(header_for_j, merge_for_j, true, {}); + auto transformation4 = + TransformationAddDeadBreak(block_in_inner_if, merge_for_j, false, {}); + auto transformation5 = + TransformationAddDeadBreak(block_switch_case, merge_for_j, true, {}); + auto transformation6 = + TransformationAddDeadBreak(block_switch_default, merge_for_j, false, {}); + auto transformation7 = + TransformationAddDeadBreak(block_in_for_i_loop, merge_for_i, true, {}); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation6, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation7, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "x" + OpName %16 "j" + OpName %27 "y" + OpName %55 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %14 = OpConstant %10 1 + %17 = OpConstant %10 0 + %24 = OpConstant %10 100 + %25 = OpTypeBool + %38 = OpConstant %10 2 + %67 = OpConstantTrue %25 + %68 = OpConstantFalse %25 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %11 Function + %16 = OpVariable %11 Function + %27 = OpVariable %11 Function + %55 = OpVariable %11 Function + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranchConditional %67 %7 %8 + %7 = OpLabel + %13 = OpLoad %10 %12 + %15 = OpIAdd %10 %13 %14 + OpStore %12 %15 + OpStore %16 %17 + OpBranch %18 + %18 = OpLabel + OpLoopMerge %20 %21 None + OpBranchConditional %67 %22 %20 + %22 = OpLabel + %23 = OpLoad %10 %16 + %26 = OpSLessThan %25 %23 %24 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + %28 = OpLoad %10 %27 + %29 = OpIAdd %10 %28 %14 + OpStore %27 %29 + %30 = OpLoad %10 %12 + %31 = OpLoad %10 %27 + %32 = OpIEqual %25 %30 %31 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %34 + %33 = OpLabel + %35 = OpLoad %10 %12 + %36 = OpIAdd %10 %35 %14 + OpStore %12 %36 + %37 = OpLoad %10 %12 + %39 = OpIEqual %25 %37 %38 + OpSelectionMerge %41 None + OpBranchConditional %39 %40 %41 + %40 = OpLabel + %42 = OpLoad %10 %27 + %43 = OpIAdd %10 %42 %14 + OpStore %27 %43 + OpBranchConditional %68 %20 %41 + %41 = OpLabel + %44 = OpLoad %10 %12 + OpSelectionMerge %47 None + OpSwitch %44 %46 0 %45 + %46 = OpLabel + OpBranchConditional %67 %47 %20 + %45 = OpLabel + OpStore %12 %38 + OpBranchConditional %68 %20 %46 + %47 = OpLabel + OpBranch %34 + %34 = OpLabel + OpBranch %21 + %21 = OpLabel + %50 = OpLoad %10 %16 + %51 = OpIAdd %10 %50 %14 + OpStore %16 %51 + OpBranch %18 + %20 = OpLabel + OpBranch %9 + %9 = OpLabel + %52 = OpLoad %10 %12 + %53 = OpLoad %10 %27 + %54 = OpSGreaterThan %25 %52 %53 + OpBranchConditional %54 %6 %8 + %8 = OpLabel + OpStore %55 %17 + OpBranch %56 + %56 = OpLabel + OpLoopMerge %58 %59 None + OpBranchConditional %68 %58 %60 + %60 = OpLabel + %61 = OpLoad %10 %55 + %62 = OpSLessThan %25 %61 %24 + OpBranchConditional %62 %57 %58 + %57 = OpLabel + %63 = OpLoad %10 %12 + %64 = OpIAdd %10 %63 %14 + OpStore %12 %64 + OpBranchConditional %67 %59 %58 + %59 = OpLabel + %65 = OpLoad %10 %55 + %66 = OpIAdd %10 %65 %14 + OpStore %55 %66 + OpBranch %56 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, NoBreakFromContinueConstruct) { + // Checks that it is illegal to break straight from a continue construct. + + // The SPIR-V for this test is adapted from the following GLSL: + // + // void main() { + // for (int i = 0; i < 100; i++) { + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + OpDecorate %8 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %21 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %22 = OpConstantTrue %17 + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + OpBranch %23 + %23 = OpLabel + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Not OK to break loop from its continue construct, except from the back-edge + // block. + ASSERT_FALSE(TransformationAddDeadBreak(13, 12, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadBreak(23, 12, true, {}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBreakTest, BreakFromBackEdgeBlock) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %10 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeBool + %6 = OpTypePointer Function %4 + +; Constants + %7 = OpConstant %4 0 + %8 = OpConstant %4 1 + %9 = OpConstantTrue %5 + +; main function + %10 = OpFunction %2 None %3 + %11 = OpLabel + %12 = OpVariable %6 Function + OpStore %12 %7 + OpBranch %13 + %13 = OpLabel + OpLoopMerge %21 %18 None ; structured loop + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %4 %12 + %16 = OpULessThan %5 %15 %8 ; i < 1 ? + OpBranchConditional %16 %17 %21 ; body or break + %17 = OpLabel ; body + OpBranch %18 + %18 = OpLabel ; continue target does not strictly dominates the back-edge block + %19 = OpLoad %4 %12 + %20 = OpIAdd %4 %19 %8 ; ++i + OpStore %12 %20 + OpBranch %13 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationAddDeadBreak(18, 21, true, {}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %10 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeBool + %6 = OpTypePointer Function %4 + +; Constants + %7 = OpConstant %4 0 + %8 = OpConstant %4 1 + %9 = OpConstantTrue %5 + +; main function + %10 = OpFunction %2 None %3 + %11 = OpLabel + %12 = OpVariable %6 Function + OpStore %12 %7 + OpBranch %13 + %13 = OpLabel + OpLoopMerge %21 %18 None ; structured loop + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %4 %12 + %16 = OpULessThan %5 %15 %8 ; i < 1 ? + OpBranchConditional %16 %17 %21 ; body or break + %17 = OpLabel ; body + OpBranch %18 + %18 = OpLabel ; continue target does not strictly dominates the back-edge block + %19 = OpLoad %4 %12 + %20 = OpIAdd %4 %19 %8 ; ++i + OpStore %12 %20 + OpBranchConditional %9 %13 %21 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationAddDeadBreakTest, SelectionInContinueConstruct) { + // Considers some scenarios where there is a selection construct in a loop's + // continue construct. + + // The SPIR-V for this test is adapted from the following GLSL: + // + // void main() { + // for (int i = 0; i < 100; i = (i < 50 ? i + 2 : i + 1)) { + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %99 = OpConstantTrue %17 + %20 = OpConstant %6 50 + %26 = OpConstant %6 2 + %30 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpSLessThan %17 %19 %20 + OpSelectionMerge %24 None + OpBranchConditional %21 %23 %28 + %23 = OpLabel + %25 = OpLoad %6 %8 + OpBranch %100 + %100 = OpLabel + %27 = OpIAdd %6 %25 %26 + OpStore %22 %27 + OpBranch %24 + %28 = OpLabel + %29 = OpLoad %6 %8 + OpBranch %101 + %101 = OpLabel + %31 = OpIAdd %6 %29 %30 + OpStore %22 %31 + OpBranch %24 + %24 = OpLabel + %32 = OpLoad %6 %22 + OpStore %8 %32 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + const uint32_t loop_merge = 12; + const uint32_t selection_merge = 24; + const uint32_t in_selection_1 = 23; + const uint32_t in_selection_2 = 100; + const uint32_t in_selection_3 = 28; + const uint32_t in_selection_4 = 101; + + // Not OK to jump from the selection to the loop merge, as this would break + // from the loop's continue construct. + ASSERT_FALSE(TransformationAddDeadBreak(in_selection_1, loop_merge, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(in_selection_2, loop_merge, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(in_selection_3, loop_merge, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddDeadBreak(in_selection_4, loop_merge, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // But fine to jump from the selection to its merge. + + auto transformation1 = + TransformationAddDeadBreak(in_selection_1, selection_merge, true, {}); + auto transformation2 = + TransformationAddDeadBreak(in_selection_2, selection_merge, true, {}); + auto transformation3 = + TransformationAddDeadBreak(in_selection_3, selection_merge, true, {}); + auto transformation4 = + TransformationAddDeadBreak(in_selection_4, selection_merge, true, {}); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %99 = OpConstantTrue %17 + %20 = OpConstant %6 50 + %26 = OpConstant %6 2 + %30 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpSLessThan %17 %19 %20 + OpSelectionMerge %24 None + OpBranchConditional %21 %23 %28 + %23 = OpLabel + %25 = OpLoad %6 %8 + OpBranchConditional %99 %100 %24 + %100 = OpLabel + %27 = OpIAdd %6 %25 %26 + OpStore %22 %27 + OpBranchConditional %99 %24 %24 + %28 = OpLabel + %29 = OpLoad %6 %8 + OpBranchConditional %99 %101 %24 + %101 = OpLabel + %31 = OpIAdd %6 %29 %30 + OpStore %22 %31 + OpBranchConditional %99 %24 %24 + %24 = OpLabel + %32 = OpLoad %6 %22 + OpStore %8 %32 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, LoopInContinueConstruct) { + // Considers some scenarios where there is a loop in a loop's continue + // construct. + + // The SPIR-V for this test is adapted from the following GLSL, with inlining + // applied so that the loop from foo is in the main loop's continue construct: + // + // int foo() { + // int result = 0; + // for (int j = 0; j < 10; j++) { + // result++; + // } + // return result; + // } + // + // void main() { + // for (int i = 0; i < 100; i += foo()) { + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %31 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %20 = OpConstant %6 10 + %21 = OpTypeBool + %100 = OpConstantTrue %21 + %24 = OpConstant %6 1 + %38 = OpConstant %6 100 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %43 = OpVariable %10 Function + %44 = OpVariable %10 Function + %45 = OpVariable %10 Function + %31 = OpVariable %10 Function + OpStore %31 %12 + OpBranch %32 + %32 = OpLabel + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel + %37 = OpLoad %6 %31 + %39 = OpSLessThan %21 %37 %38 + OpBranchConditional %39 %33 %34 + %33 = OpLabel + OpBranch %35 + %35 = OpLabel + OpStore %43 %12 + OpStore %44 %12 + OpBranch %46 + %46 = OpLabel + OpLoopMerge %47 %48 None + OpBranch %49 + %49 = OpLabel + %50 = OpLoad %6 %44 + %51 = OpSLessThan %21 %50 %20 + OpBranchConditional %51 %52 %47 + %52 = OpLabel + %53 = OpLoad %6 %43 + OpBranch %101 + %101 = OpLabel + %54 = OpIAdd %6 %53 %24 + OpStore %43 %54 + OpBranch %48 + %48 = OpLabel + %55 = OpLoad %6 %44 + %56 = OpIAdd %6 %55 %24 + OpStore %44 %56 + OpBranch %46 + %47 = OpLabel + %57 = OpLoad %6 %43 + OpStore %45 %57 + %40 = OpLoad %6 %45 + %41 = OpLoad %6 %31 + %42 = OpIAdd %6 %41 %40 + OpStore %31 %42 + OpBranch %32 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + const uint32_t outer_loop_merge = 34; + const uint32_t outer_loop_block = 33; + const uint32_t inner_loop_merge = 47; + const uint32_t inner_loop_block = 52; + + // Some inapplicable cases + ASSERT_FALSE( + TransformationAddDeadBreak(inner_loop_block, outer_loop_merge, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddDeadBreak(outer_loop_block, inner_loop_merge, true, {}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = + TransformationAddDeadBreak(inner_loop_block, inner_loop_merge, true, {}); + auto transformation2 = + TransformationAddDeadBreak(outer_loop_block, outer_loop_merge, true, {}); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %31 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %20 = OpConstant %6 10 + %21 = OpTypeBool + %100 = OpConstantTrue %21 + %24 = OpConstant %6 1 + %38 = OpConstant %6 100 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %43 = OpVariable %10 Function + %44 = OpVariable %10 Function + %45 = OpVariable %10 Function + %31 = OpVariable %10 Function + OpStore %31 %12 + OpBranch %32 + %32 = OpLabel + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel + %37 = OpLoad %6 %31 + %39 = OpSLessThan %21 %37 %38 + OpBranchConditional %39 %33 %34 + %33 = OpLabel + OpBranchConditional %100 %35 %34 + %35 = OpLabel + OpStore %43 %12 + OpStore %44 %12 + OpBranch %46 + %46 = OpLabel + OpLoopMerge %47 %48 None + OpBranch %49 + %49 = OpLabel + %50 = OpLoad %6 %44 + %51 = OpSLessThan %21 %50 %20 + OpBranchConditional %51 %52 %47 + %52 = OpLabel + %53 = OpLoad %6 %43 + OpBranchConditional %100 %101 %47 + %101 = OpLabel + %54 = OpIAdd %6 %53 %24 + OpStore %43 %54 + OpBranch %48 + %48 = OpLabel + %55 = OpLoad %6 %44 + %56 = OpIAdd %6 %55 %24 + OpStore %44 %56 + OpBranch %46 + %47 = OpLabel + %57 = OpLoad %6 %43 + OpStore %45 %57 + %40 = OpLoad %6 %45 + %41 = OpLoad %6 %31 + %42 = OpIAdd %6 %41 %40 + OpStore %31 %42 + OpBranch %32 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, PhiInstructions) { + // Checks that the transformation works in the presence of phi instructions. + + // The SPIR-V for this test is adapted from the following GLSL, with a bit of + // extra and artificial work to get some interesting uses of OpPhi: + // + // void main() { + // int x; int y; + // float f; + // x = 2; + // f = 3.0; + // if (x > y) { + // x = 3; + // f = 4.0; + // } else { + // x = x + 2; + // f = f + 10.0; + // } + // while (x < y) { + // x = x + 1; + // f = f + 1.0; + // } + // y = x; + // f = f + 3.0; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %12 "f" + OpName %15 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %10 = OpTypeFloat 32 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 3 + %17 = OpTypeBool + %80 = OpConstantTrue %17 + %21 = OpConstant %6 3 + %22 = OpConstant %10 4 + %27 = OpConstant %10 10 + %38 = OpConstant %6 1 + %41 = OpConstant %10 1 + %46 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %15 = OpVariable %7 Function + OpStore %8 %9 + OpStore %12 %13 + %18 = OpSGreaterThan %17 %9 %46 + OpSelectionMerge %20 None + OpBranchConditional %18 %19 %23 + %19 = OpLabel + OpStore %8 %21 + OpStore %12 %22 + OpBranch %20 + %23 = OpLabel + %25 = OpIAdd %6 %9 %9 + OpStore %8 %25 + OpBranch %70 + %70 = OpLabel + %28 = OpFAdd %10 %13 %27 + OpStore %12 %28 + OpBranch %20 + %20 = OpLabel + %52 = OpPhi %10 %22 %19 %28 %70 + %48 = OpPhi %6 %21 %19 %25 %70 + OpBranch %29 + %29 = OpLabel + %51 = OpPhi %10 %52 %20 %42 %32 + %47 = OpPhi %6 %48 %20 %39 %32 + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %36 = OpSLessThan %17 %47 %46 + OpBranchConditional %36 %30 %31 + %30 = OpLabel + %39 = OpIAdd %6 %47 %38 + OpStore %8 %39 + OpBranch %75 + %75 = OpLabel + %42 = OpFAdd %10 %51 %41 + OpStore %12 %42 + OpBranch %32 + %32 = OpLabel + OpBranch %29 + %31 = OpLabel + %71 = OpPhi %6 %47 %33 + %72 = OpPhi %10 %51 %33 + OpStore %15 %71 + %45 = OpFAdd %10 %72 %13 + OpStore %12 %45 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Some inapplicable transformations + // Not applicable because there is already an edge 19->20, so the OpPhis at 20 + // do not need to be updated + ASSERT_FALSE(TransformationAddDeadBreak(19, 20, true, {13, 21}) + .IsApplicable(context.get(), transformation_context)); + // Not applicable because two OpPhis (not zero) need to be updated at 20 + ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {}) + .IsApplicable(context.get(), transformation_context)); + // Not applicable because two OpPhis (not just one) need to be updated at 20 + ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {13}) + .IsApplicable(context.get(), transformation_context)); + // Not applicable because the given ids do not have types that match the + // OpPhis at 20, in order + ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 13}) + .IsApplicable(context.get(), transformation_context)); + // Not applicable because id 23 is a label + ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 23}) + .IsApplicable(context.get(), transformation_context)); + // Not applicable because 101 is not an id + ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {21, 101}) + .IsApplicable(context.get(), transformation_context)); + // Not applicable because ids 51 and 47 are not available at the end of block + // 23 + ASSERT_FALSE(TransformationAddDeadBreak(23, 20, true, {51, 47}) + .IsApplicable(context.get(), transformation_context)); + + // Not applicable because OpConstantFalse is not present in the module + ASSERT_FALSE(TransformationAddDeadBreak(19, 20, false, {}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = TransformationAddDeadBreak(19, 20, true, {}); + auto transformation2 = TransformationAddDeadBreak(23, 20, true, {13, 21}); + auto transformation3 = TransformationAddDeadBreak(70, 20, true, {}); + auto transformation4 = TransformationAddDeadBreak(30, 31, true, {21, 13}); + auto transformation5 = TransformationAddDeadBreak(75, 31, true, {47, 51}); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %12 "f" + OpName %15 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %10 = OpTypeFloat 32 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 3 + %17 = OpTypeBool + %80 = OpConstantTrue %17 + %21 = OpConstant %6 3 + %22 = OpConstant %10 4 + %27 = OpConstant %10 10 + %38 = OpConstant %6 1 + %41 = OpConstant %10 1 + %46 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %15 = OpVariable %7 Function + OpStore %8 %9 + OpStore %12 %13 + %18 = OpSGreaterThan %17 %9 %46 + OpSelectionMerge %20 None + OpBranchConditional %18 %19 %23 + %19 = OpLabel + OpStore %8 %21 + OpStore %12 %22 + OpBranchConditional %80 %20 %20 + %23 = OpLabel + %25 = OpIAdd %6 %9 %9 + OpStore %8 %25 + OpBranchConditional %80 %70 %20 + %70 = OpLabel + %28 = OpFAdd %10 %13 %27 + OpStore %12 %28 + OpBranchConditional %80 %20 %20 + %20 = OpLabel + %52 = OpPhi %10 %22 %19 %28 %70 %13 %23 + %48 = OpPhi %6 %21 %19 %25 %70 %21 %23 + OpBranch %29 + %29 = OpLabel + %51 = OpPhi %10 %52 %20 %42 %32 + %47 = OpPhi %6 %48 %20 %39 %32 + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %36 = OpSLessThan %17 %47 %46 + OpBranchConditional %36 %30 %31 + %30 = OpLabel + %39 = OpIAdd %6 %47 %38 + OpStore %8 %39 + OpBranchConditional %80 %75 %31 + %75 = OpLabel + %42 = OpFAdd %10 %51 %41 + OpStore %12 %42 + OpBranchConditional %80 %32 %31 + %32 = OpLabel + OpBranch %29 + %31 = OpLabel + %71 = OpPhi %6 %47 %33 %21 %30 %47 %75 + %72 = OpPhi %10 %51 %33 %13 %30 %51 %75 + OpStore %15 %71 + %45 = OpFAdd %10 %72 %13 + OpStore %12 %45 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, RespectDominanceRules1) { + // Right after the loop, an OpCopyObject defined by the loop is used. Adding + // a dead break would prevent that use from being dominated by its definition, + // so is not allowed. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %102 None + OpBranch %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %104 + %104 = OpLabel + OpBranch %102 + %102 = OpLabel + OpBranchConditional %11 %100 %101 + %101 = OpLabel + %201 = OpCopyObject %10 %200 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBreakTest, RespectDominanceRules2) { + // This example captures the following idiom: + // + // if { + // L1: + // } + // definition; + // L2: + // use; + // + // Adding a dead jump from L1 to L2 would lead to 'definition' no longer + // dominating 'use', and so is not allowed. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpSelectionMerge %101 None + OpBranchConditional %11 %102 %103 + %102 = OpLabel + OpBranch %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %101 + %101 = OpLabel + %201 = OpCopyObject %10 %200 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBreakTest, RespectDominanceRules3) { + // Right after the loop, an OpCopyObject defined by the loop is used in an + // OpPhi. Adding a dead break is OK in this case, due to the use being in an + // OpPhi. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %102 None + OpBranch %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %104 + %104 = OpLabel + OpBranch %102 + %102 = OpLabel + OpBranchConditional %11 %100 %101 + %101 = OpLabel + %201 = OpPhi %10 %200 %102 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto good_transformation = TransformationAddDeadBreak(100, 101, false, {11}); + ASSERT_TRUE( + good_transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(good_transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %102 None + OpBranchConditional %11 %101 %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %104 + %104 = OpLabel + OpBranch %102 + %102 = OpLabel + OpBranchConditional %11 %100 %101 + %101 = OpLabel + %201 = OpPhi %10 %200 %102 %11 %100 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, RespectDominanceRules4) { + // This example captures the following idiom: + // + // if { + // L1: + // } + // definition; + // L2: + // use in OpPhi; + // + // Adding a dead jump from L1 to L2 is OK, due to 'use' being in an OpPhi. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpSelectionMerge %101 None + OpBranchConditional %11 %102 %103 + %102 = OpLabel + OpBranch %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %101 + %101 = OpLabel + %201 = OpPhi %10 %200 %103 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto good_transformation = TransformationAddDeadBreak(102, 101, false, {11}); + ASSERT_TRUE( + good_transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(good_transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpSelectionMerge %101 None + OpBranchConditional %11 %102 %103 + %102 = OpLabel + OpBranchConditional %11 %101 %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %101 + %101 = OpLabel + %201 = OpPhi %10 %200 %103 %11 %102 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadBreakTest, RespectDominanceRules5) { + // After, but not right after, the loop, an OpCopyObject defined by the loop + // is used in an OpPhi. Adding a dead break is not OK in this case. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %102 None + OpBranch %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %104 + %104 = OpLabel + OpBranch %102 + %102 = OpLabel + OpBranchConditional %11 %100 %101 + %101 = OpLabel + OpBranch %105 + %105 = OpLabel + %201 = OpPhi %10 %200 %101 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadBreak(100, 101, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBreakTest, RespectDominanceRules6) { + // This example captures the following idiom: + // + // if { + // L1: + // } + // definition; + // L2: + // goto L3; + // L3: + // use in OpPhi; + // + // Adding a dead jump from L1 to L2 not OK, due to the use in an OpPhi being + // in L3. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpSelectionMerge %101 None + OpBranchConditional %11 %102 %103 + %102 = OpLabel + OpBranch %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %101 + %101 = OpLabel + OpBranch %150 + %150 = OpLabel + %201 = OpPhi %10 %200 %101 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBreakTest, RespectDominanceRules7) { + // This example - a variation on an earlier test - captures the following + // idiom: + // + // loop { + // L1: + // } + // definition; + // L2: + // use; + // + // Adding a dead jump from L1 to L2 would lead to 'definition' no longer + // dominating 'use', and so is not allowed. + // + // This version of the test captures the case where L1 appears after the + // loop merge (which SPIR-V dominance rules allow). + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %104 None + OpBranchConditional %11 %102 %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %101 + %101 = OpLabel + %201 = OpCopyObject %10 %200 + OpReturn + %102 = OpLabel + OpBranch %103 + %104 = OpLabel + OpBranch %100 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBreakTest, RespectDominanceRules8) { + // A variation of RespectDominanceRules8 where the defining block appears + // in the loop, but after the definition of interest. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %104 None + OpBranchConditional %11 %102 %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %101 + %102 = OpLabel + OpBranch %103 + %101 = OpLabel + %201 = OpCopyObject %10 %200 + OpReturn + %104 = OpLabel + OpBranch %100 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadBreak(102, 101, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadBreakTest, + BreakWouldDisobeyDominanceBlockOrderingRules) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %9 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %16 %15 None + OpBranch %11 + %11 = OpLabel + OpSelectionMerge %14 None + OpBranchConditional %9 %12 %13 + %14 = OpLabel + OpBranch %15 + %12 = OpLabel + OpBranch %16 + %13 = OpLabel + OpBranch %16 + %15 = OpLabel + OpBranch %10 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad because 14 comes before 12 in the module, and 14 has no predecessors. + // This means that an edge from 12 to 14 will lead to 12 dominating 14, which + // is illegal if 12 appears after 14. + auto bad_transformation = TransformationAddDeadBreak(12, 14, true, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp new file mode 100644 index 0000000..577f427 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_dead_continue_test.cpp @@ -0,0 +1,1616 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_dead_continue.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddDeadContinueTest, SimpleExample) { + // For a simple loop, checks that some dead continue scenarios are possible, + // checks that some invalid scenarios are indeed not allowed, and then applies + // a transformation. + + // The SPIR-V for this test is adapted from the following GLSL, by separating + // some assignments into their own basic blocks, and adding constants for true + // and false: + // + // void main() { + // int x = 0; + // for (int i = 0; i < 10; i++) { + // x = x + i; + // x = x + i; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %41 = OpConstantTrue %18 + %42 = OpConstantFalse %18 + %27 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %16 = OpLoad %6 %10 + %19 = OpSLessThan %18 %16 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %20 = OpLoad %6 %8 + %21 = OpLoad %6 %10 + %22 = OpIAdd %6 %20 %21 + OpStore %8 %22 + OpBranch %40 + %40 = OpLabel + %23 = OpLoad %6 %8 + %24 = OpLoad %6 %10 + %25 = OpIAdd %6 %23 %24 + OpStore %8 %25 + OpBranch %14 + %14 = OpLabel + %26 = OpLoad %6 %10 + %28 = OpIAdd %6 %26 %27 + OpStore %10 %28 + OpBranch %11 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // These are all possibilities. + ASSERT_TRUE(TransformationAddDeadContinue(11, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadContinue(11, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadContinue(12, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadContinue(12, false, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadContinue(40, true, {}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationAddDeadContinue(40, false, {}) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable: 100 is not a block id. + ASSERT_FALSE(TransformationAddDeadContinue(100, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable: 10 is not in a loop. + ASSERT_FALSE(TransformationAddDeadContinue(10, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable: 15 does not branch unconditionally to a single successor. + ASSERT_FALSE(TransformationAddDeadContinue(15, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable: 13 is not in a loop and has no successor. + ASSERT_FALSE(TransformationAddDeadContinue(13, true, {}) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable: 14 is the loop continue target, so it's not OK to jump to + // the loop continue from there. + ASSERT_FALSE(TransformationAddDeadContinue(14, false, {}) + .IsApplicable(context.get(), transformation_context)); + + // These are the transformations we will apply. + auto transformation1 = TransformationAddDeadContinue(11, true, {}); + auto transformation2 = TransformationAddDeadContinue(12, false, {}); + auto transformation3 = TransformationAddDeadContinue(40, true, {}); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %41 = OpConstantTrue %18 + %42 = OpConstantFalse %18 + %27 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + OpLoopMerge %13 %14 None + OpBranchConditional %41 %15 %14 + %15 = OpLabel + %16 = OpLoad %6 %10 + %19 = OpSLessThan %18 %16 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %20 = OpLoad %6 %8 + %21 = OpLoad %6 %10 + %22 = OpIAdd %6 %20 %21 + OpStore %8 %22 + OpBranchConditional %42 %14 %40 + %40 = OpLabel + %23 = OpLoad %6 %8 + %24 = OpLoad %6 %10 + %25 = OpIAdd %6 %23 %24 + OpStore %8 %25 + OpBranchConditional %41 %14 %14 + %14 = OpLabel + %26 = OpLoad %6 %10 + %28 = OpIAdd %6 %26 %27 + OpStore %10 %28 + OpBranch %11 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadContinueTest, LoopNest) { + // Checks some allowed and disallowed scenarios for a nest of loops, including + // continuing a loop from an if or switch. + + // The SPIR-V for this test is adapted from the following GLSL: + // + // void main() { + // int x, y; + // do { + // x++; + // for (int j = 0; j < 100; j++) { + // y++; + // if (x == y) { + // x++; + // if (x == 2) { + // y++; + // } + // switch (x) { + // case 0: + // x = 2; + // default: + // break; + // } + // } + // } + // } while (x > y); + // + // for (int i = 0; i < 100; i++) { + // x++; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "x" + OpName %16 "j" + OpName %27 "y" + OpName %55 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %14 = OpConstant %10 1 + %17 = OpConstant %10 0 + %24 = OpConstant %10 100 + %25 = OpTypeBool + %38 = OpConstant %10 2 + %67 = OpConstantTrue %25 + %68 = OpConstantFalse %25 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %11 Function + %16 = OpVariable %11 Function + %27 = OpVariable %11 Function + %55 = OpVariable %11 Function + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranch %7 + %7 = OpLabel + %13 = OpLoad %10 %12 + %15 = OpIAdd %10 %13 %14 + OpStore %12 %15 + OpStore %16 %17 + OpBranch %18 + %18 = OpLabel + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + %23 = OpLoad %10 %16 + %26 = OpSLessThan %25 %23 %24 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + %28 = OpLoad %10 %27 + %29 = OpIAdd %10 %28 %14 + OpStore %27 %29 + %30 = OpLoad %10 %12 + %31 = OpLoad %10 %27 + %32 = OpIEqual %25 %30 %31 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %34 + %33 = OpLabel + %35 = OpLoad %10 %12 + %36 = OpIAdd %10 %35 %14 + OpStore %12 %36 + %37 = OpLoad %10 %12 + %39 = OpIEqual %25 %37 %38 + OpSelectionMerge %41 None + OpBranchConditional %39 %40 %41 + %40 = OpLabel + %42 = OpLoad %10 %27 + %43 = OpIAdd %10 %42 %14 + OpStore %27 %43 + OpBranch %41 + %41 = OpLabel + %44 = OpLoad %10 %12 + OpSelectionMerge %47 None + OpSwitch %44 %46 0 %45 + %46 = OpLabel + OpBranch %47 + %45 = OpLabel + OpStore %12 %38 + OpBranch %46 + %47 = OpLabel + OpBranch %34 + %34 = OpLabel + OpBranch %21 + %21 = OpLabel + %50 = OpLoad %10 %16 + %51 = OpIAdd %10 %50 %14 + OpStore %16 %51 + OpBranch %18 + %20 = OpLabel + OpBranch %9 + %9 = OpLabel + %52 = OpLoad %10 %12 + %53 = OpLoad %10 %27 + %54 = OpSGreaterThan %25 %52 %53 + OpBranchConditional %54 %6 %8 + %8 = OpLabel + OpStore %55 %17 + OpBranch %56 + %56 = OpLabel + OpLoopMerge %58 %59 None + OpBranch %60 + %60 = OpLabel + %61 = OpLoad %10 %55 + %62 = OpSLessThan %25 %61 %24 + OpBranchConditional %62 %57 %58 + %57 = OpLabel + %63 = OpLoad %10 %12 + %64 = OpIAdd %10 %63 %14 + OpStore %12 %64 + OpBranch %59 + %59 = OpLabel + %65 = OpLoad %10 %55 + %66 = OpIAdd %10 %65 %14 + OpStore %55 %66 + OpBranch %56 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + std::vector good = {6, 7, 18, 20, 34, 40, 45, 46, 47, 56, 57}; + std::vector bad = {5, 8, 9, 19, 21, 22, 33, 41, 58, 59, 60}; + + for (uint32_t from_block : bad) { + ASSERT_FALSE(TransformationAddDeadContinue(from_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + } + for (uint32_t from_block : good) { + const TransformationAddDeadContinue transformation(from_block, true, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "x" + OpName %16 "j" + OpName %27 "y" + OpName %55 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %14 = OpConstant %10 1 + %17 = OpConstant %10 0 + %24 = OpConstant %10 100 + %25 = OpTypeBool + %38 = OpConstant %10 2 + %67 = OpConstantTrue %25 + %68 = OpConstantFalse %25 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %11 Function + %16 = OpVariable %11 Function + %27 = OpVariable %11 Function + %55 = OpVariable %11 Function + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranchConditional %67 %7 %9 + %7 = OpLabel + %13 = OpLoad %10 %12 + %15 = OpIAdd %10 %13 %14 + OpStore %12 %15 + OpStore %16 %17 + OpBranchConditional %67 %18 %9 + %18 = OpLabel + OpLoopMerge %20 %21 None + OpBranchConditional %67 %22 %21 + %22 = OpLabel + %23 = OpLoad %10 %16 + %26 = OpSLessThan %25 %23 %24 + OpBranchConditional %26 %19 %20 + %19 = OpLabel + %28 = OpLoad %10 %27 + %29 = OpIAdd %10 %28 %14 + OpStore %27 %29 + %30 = OpLoad %10 %12 + %31 = OpLoad %10 %27 + %32 = OpIEqual %25 %30 %31 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %34 + %33 = OpLabel + %35 = OpLoad %10 %12 + %36 = OpIAdd %10 %35 %14 + OpStore %12 %36 + %37 = OpLoad %10 %12 + %39 = OpIEqual %25 %37 %38 + OpSelectionMerge %41 None + OpBranchConditional %39 %40 %41 + %40 = OpLabel + %42 = OpLoad %10 %27 + %43 = OpIAdd %10 %42 %14 + OpStore %27 %43 + OpBranchConditional %67 %41 %21 + %41 = OpLabel + %44 = OpLoad %10 %12 + OpSelectionMerge %47 None + OpSwitch %44 %46 0 %45 + %46 = OpLabel + OpBranchConditional %67 %47 %21 + %45 = OpLabel + OpStore %12 %38 + OpBranchConditional %67 %46 %21 + %47 = OpLabel + OpBranchConditional %67 %34 %21 + %34 = OpLabel + OpBranchConditional %67 %21 %21 + %21 = OpLabel + %50 = OpLoad %10 %16 + %51 = OpIAdd %10 %50 %14 + OpStore %16 %51 + OpBranch %18 + %20 = OpLabel + OpBranchConditional %67 %9 %9 + %9 = OpLabel + %52 = OpLoad %10 %12 + %53 = OpLoad %10 %27 + %54 = OpSGreaterThan %25 %52 %53 + OpBranchConditional %54 %6 %8 + %8 = OpLabel + OpStore %55 %17 + OpBranch %56 + %56 = OpLabel + OpLoopMerge %58 %59 None + OpBranchConditional %67 %60 %59 + %60 = OpLabel + %61 = OpLoad %10 %55 + %62 = OpSLessThan %25 %61 %24 + OpBranchConditional %62 %57 %58 + %57 = OpLabel + %63 = OpLoad %10 %12 + %64 = OpIAdd %10 %63 %14 + OpStore %12 %64 + OpBranchConditional %67 %59 %59 + %59 = OpLabel + %65 = OpLoad %10 %55 + %66 = OpIAdd %10 %65 %14 + OpStore %55 %66 + OpBranch %56 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadConditionalTest, LoopInContinueConstruct) { + // Considers some scenarios where there is a loop in a loop's continue + // construct. + + // The SPIR-V for this test is adapted from the following GLSL, with inlining + // applied so that the loop from foo is in the main loop's continue construct: + // + // int foo() { + // int result = 0; + // for (int j = 0; j < 10; j++) { + // result++; + // } + // return result; + // } + // + // void main() { + // for (int i = 0; i < 100; i += foo()) { + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %31 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %20 = OpConstant %6 10 + %21 = OpTypeBool + %100 = OpConstantFalse %21 + %24 = OpConstant %6 1 + %38 = OpConstant %6 100 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %43 = OpVariable %10 Function + %44 = OpVariable %10 Function + %45 = OpVariable %10 Function + %31 = OpVariable %10 Function + OpStore %31 %12 + OpBranch %32 + %32 = OpLabel + OpLoopMerge %34 %35 None + OpBranch %36 + %36 = OpLabel + %37 = OpLoad %6 %31 + %39 = OpSLessThan %21 %37 %38 + OpBranchConditional %39 %33 %34 + %33 = OpLabel + OpBranch %35 + %35 = OpLabel + OpStore %43 %12 + OpStore %44 %12 + OpBranch %46 + %46 = OpLabel + OpLoopMerge %47 %48 None + OpBranch %49 + %49 = OpLabel + %50 = OpLoad %6 %44 + %51 = OpSLessThan %21 %50 %20 + OpBranchConditional %51 %52 %47 + %52 = OpLabel + %53 = OpLoad %6 %43 + OpBranch %101 + %101 = OpLabel + %54 = OpIAdd %6 %53 %24 + OpStore %43 %54 + OpBranch %48 + %48 = OpLabel + %55 = OpLoad %6 %44 + %56 = OpIAdd %6 %55 %24 + OpStore %44 %56 + OpBranch %46 + %47 = OpLabel + %57 = OpLoad %6 %43 + OpStore %45 %57 + %40 = OpLoad %6 %45 + %41 = OpLoad %6 %31 + %42 = OpIAdd %6 %41 %40 + OpStore %31 %42 + OpBranch %32 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + std::vector good = {32, 33, 46, 52, 101}; + std::vector bad = {5, 34, 36, 35, 47, 49, 48}; + + for (uint32_t from_block : bad) { + ASSERT_FALSE(TransformationAddDeadContinue(from_block, false, {}) + .IsApplicable(context.get(), transformation_context)); + } + for (uint32_t from_block : good) { + const TransformationAddDeadContinue transformation(from_block, false, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %31 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %20 = OpConstant %6 10 + %21 = OpTypeBool + %100 = OpConstantFalse %21 + %24 = OpConstant %6 1 + %38 = OpConstant %6 100 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %43 = OpVariable %10 Function + %44 = OpVariable %10 Function + %45 = OpVariable %10 Function + %31 = OpVariable %10 Function + OpStore %31 %12 + OpBranch %32 + %32 = OpLabel + OpLoopMerge %34 %35 None + OpBranchConditional %100 %35 %36 + %36 = OpLabel + %37 = OpLoad %6 %31 + %39 = OpSLessThan %21 %37 %38 + OpBranchConditional %39 %33 %34 + %33 = OpLabel + OpBranchConditional %100 %35 %35 + %35 = OpLabel + OpStore %43 %12 + OpStore %44 %12 + OpBranch %46 + %46 = OpLabel + OpLoopMerge %47 %48 None + OpBranchConditional %100 %48 %49 + %49 = OpLabel + %50 = OpLoad %6 %44 + %51 = OpSLessThan %21 %50 %20 + OpBranchConditional %51 %52 %47 + %52 = OpLabel + %53 = OpLoad %6 %43 + OpBranchConditional %100 %48 %101 + %101 = OpLabel + %54 = OpIAdd %6 %53 %24 + OpStore %43 %54 + OpBranchConditional %100 %48 %48 + %48 = OpLabel + %55 = OpLoad %6 %44 + %56 = OpIAdd %6 %55 %24 + OpStore %44 %56 + OpBranch %46 + %47 = OpLabel + %57 = OpLoad %6 %43 + OpStore %45 %57 + %40 = OpLoad %6 %45 + %41 = OpLoad %6 %31 + %42 = OpIAdd %6 %41 %40 + OpStore %31 %42 + OpBranch %32 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadContinueTest, PhiInstructions) { + // Checks that the transformation works in the presence of phi instructions. + + // The SPIR-V for this test is adapted from the following GLSL, with a bit of + // extra and artificial work to get some interesting uses of OpPhi: + // + // void main() { + // int x; int y; + // float f; + // x = 2; + // f = 3.0; + // if (x > y) { + // x = 3; + // f = 4.0; + // } else { + // x = x + 2; + // f = f + 10.0; + // } + // while (x < y) { + // x = x + 1; + // f = f + 1.0; + // } + // y = x; + // f = f + 3.0; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %12 "f" + OpName %15 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %10 = OpTypeFloat 32 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 3 + %17 = OpTypeBool + %80 = OpConstantTrue %17 + %21 = OpConstant %6 3 + %22 = OpConstant %10 4 + %27 = OpConstant %10 10 + %38 = OpConstant %6 1 + %41 = OpConstant %10 1 + %46 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %15 = OpVariable %7 Function + OpStore %8 %9 + OpStore %12 %13 + %18 = OpSGreaterThan %17 %9 %46 + OpSelectionMerge %20 None + OpBranchConditional %18 %19 %23 + %19 = OpLabel + OpStore %8 %21 + OpStore %12 %22 + OpBranch %20 + %23 = OpLabel + %25 = OpIAdd %6 %9 %9 + OpStore %8 %25 + OpBranch %70 + %70 = OpLabel + %28 = OpFAdd %10 %13 %27 + OpStore %12 %28 + OpBranch %20 + %20 = OpLabel + %52 = OpPhi %10 %22 %19 %28 %70 + %48 = OpPhi %6 %21 %19 %25 %70 + OpBranch %29 + %29 = OpLabel + %51 = OpPhi %10 %52 %20 %100 %32 + %47 = OpPhi %6 %48 %20 %101 %32 + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + %36 = OpSLessThan %17 %47 %46 + OpBranchConditional %36 %30 %31 + %30 = OpLabel + %39 = OpIAdd %6 %47 %38 + OpStore %8 %39 + OpBranch %75 + %75 = OpLabel + %42 = OpFAdd %10 %51 %41 + OpStore %12 %42 + OpBranch %32 + %32 = OpLabel + %100 = OpPhi %10 %42 %75 + %101 = OpPhi %6 %39 %75 + OpBranch %29 + %31 = OpLabel + %71 = OpPhi %6 %47 %33 + %72 = OpPhi %10 %51 %33 + OpStore %15 %71 + %45 = OpFAdd %10 %72 %13 + OpStore %12 %45 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + std::vector bad = {5, 19, 20, 23, 31, 32, 33, 70}; + + std::vector good = {29, 30, 75}; + + for (uint32_t from_block : bad) { + ASSERT_FALSE(TransformationAddDeadContinue(from_block, true, {}) + .IsApplicable(context.get(), transformation_context)); + } + auto transformation1 = TransformationAddDeadContinue(29, true, {13, 21}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + auto transformation2 = TransformationAddDeadContinue(30, true, {22, 46}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + // 75 already has the continue block as a successor, so we should not provide + // phi ids. + auto transformationBad = TransformationAddDeadContinue(75, true, {27, 46}); + ASSERT_FALSE( + transformationBad.IsApplicable(context.get(), transformation_context)); + + auto transformation3 = TransformationAddDeadContinue(75, true, {}); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %12 "f" + OpName %15 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %10 = OpTypeFloat 32 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 3 + %17 = OpTypeBool + %80 = OpConstantTrue %17 + %21 = OpConstant %6 3 + %22 = OpConstant %10 4 + %27 = OpConstant %10 10 + %38 = OpConstant %6 1 + %41 = OpConstant %10 1 + %46 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %15 = OpVariable %7 Function + OpStore %8 %9 + OpStore %12 %13 + %18 = OpSGreaterThan %17 %9 %46 + OpSelectionMerge %20 None + OpBranchConditional %18 %19 %23 + %19 = OpLabel + OpStore %8 %21 + OpStore %12 %22 + OpBranch %20 + %23 = OpLabel + %25 = OpIAdd %6 %9 %9 + OpStore %8 %25 + OpBranch %70 + %70 = OpLabel + %28 = OpFAdd %10 %13 %27 + OpStore %12 %28 + OpBranch %20 + %20 = OpLabel + %52 = OpPhi %10 %22 %19 %28 %70 + %48 = OpPhi %6 %21 %19 %25 %70 + OpBranch %29 + %29 = OpLabel + %51 = OpPhi %10 %52 %20 %100 %32 + %47 = OpPhi %6 %48 %20 %101 %32 + OpLoopMerge %31 %32 None + OpBranchConditional %80 %33 %32 + %33 = OpLabel + %36 = OpSLessThan %17 %47 %46 + OpBranchConditional %36 %30 %31 + %30 = OpLabel + %39 = OpIAdd %6 %47 %38 + OpStore %8 %39 + OpBranchConditional %80 %75 %32 + %75 = OpLabel + %42 = OpFAdd %10 %51 %41 + OpStore %12 %42 + OpBranchConditional %80 %32 %32 + %32 = OpLabel + %100 = OpPhi %10 %42 %75 %13 %29 %22 %30 + %101 = OpPhi %6 %39 %75 %21 %29 %46 %30 + OpBranch %29 + %31 = OpLabel + %71 = OpPhi %6 %47 %33 + %72 = OpPhi %10 %51 %33 + OpStore %15 %71 + %45 = OpFAdd %10 %72 %13 + OpStore %12 %45 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddDeadContinueTest, RespectDominanceRules1) { + // Checks that a dead continue cannot be added if it would prevent a block + // later in the loop from dominating the loop's continue construct, in the + // case where said block defines and id that is used in the loop's continue + // construct. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranch %7 + %7 = OpLabel + %21 = OpCopyObject %10 %11 + OpBranch %9 + %9 = OpLabel + %20 = OpPhi %10 %21 %7 + OpBranchConditional %11 %6 %8 + %8 = OpLabel + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %13 + %13 = OpLabel + OpBranch %22 + %22 = OpLabel + %23 = OpCopyObject %10 %11 + OpBranch %25 + %25 = OpLabel + OpBranch %15 + %15 = OpLabel + %26 = OpCopyObject %10 %23 + OpBranchConditional %11 %12 %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // This transformation is not applicable because the dead continue from the + // loop body prevents the definition of %23 later in the loop body from + // dominating its use in the loop's continue target. + auto bad_transformation = TransformationAddDeadContinue(13, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); + + auto good_transformation_1 = TransformationAddDeadContinue(7, false, {}); + ASSERT_TRUE(good_transformation_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(good_transformation_1, context.get(), + &transformation_context); + + auto good_transformation_2 = TransformationAddDeadContinue(22, false, {}); + ASSERT_TRUE(good_transformation_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(good_transformation_2, context.get(), + &transformation_context); + + // This transformation is OK, because the definition of %21 in the loop body + // is only used in an OpPhi in the loop's continue target. + auto good_transformation_3 = TransformationAddDeadContinue(6, false, {11}); + ASSERT_TRUE(good_transformation_3.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(good_transformation_3, context.get(), + &transformation_context); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %9 None + OpBranchConditional %11 %9 %7 + %7 = OpLabel + %21 = OpCopyObject %10 %11 + OpBranchConditional %11 %9 %9 + %9 = OpLabel + %20 = OpPhi %10 %21 %7 %11 %6 + OpBranchConditional %11 %6 %8 + %8 = OpLabel + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %13 + %13 = OpLabel + OpBranch %22 + %22 = OpLabel + %23 = OpCopyObject %10 %11 + OpBranchConditional %11 %15 %25 + %25 = OpLabel + OpBranch %15 + %15 = OpLabel + %26 = OpCopyObject %10 %23 + OpBranchConditional %11 %12 %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationAddDeadContinueTest, RespectDominanceRules2) { + // Checks that a dead continue cannot be added if it would lead to a use after + // the loop failing to be dominated by its definition. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %102 None + OpBranch %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %104 + %104 = OpLabel + OpBranch %102 + %102 = OpLabel + OpBranchConditional %11 %100 %101 + %101 = OpLabel + %201 = OpCopyObject %10 %200 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // This transformation would shortcut the part of the loop body that defines + // an id used after the loop. + auto bad_transformation = TransformationAddDeadContinue(100, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadContinueTest, RespectDominanceRules3) { + // Checks that a dead continue cannot be added if it would lead to a dominance + // problem with an id used in an OpPhi after the loop. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %102 None + OpBranch %103 + %103 = OpLabel + %200 = OpCopyObject %10 %11 + OpBranch %104 + %104 = OpLabel + OpBranch %102 + %102 = OpLabel + OpBranchConditional %11 %100 %101 + %101 = OpLabel + %201 = OpPhi %10 %200 %102 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // This transformation would shortcut the part of the loop body that defines + // an id used after the loop. + auto bad_transformation = TransformationAddDeadContinue(100, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadContinueTest, Miscellaneous1) { + // A miscellaneous test that exposed a bug in spirv-fuzz. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %586 %623 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %34 0 Offset 0 + OpDecorate %34 Block + OpDecorate %36 DescriptorSet 0 + OpDecorate %36 Binding 0 + OpDecorate %586 BuiltIn FragCoord + OpMemberDecorate %591 0 Offset 0 + OpDecorate %591 Block + OpDecorate %593 DescriptorSet 0 + OpDecorate %593 Binding 1 + OpDecorate %623 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 2 + %17 = OpTypeBool + %27 = OpTypeFloat 32 + %28 = OpTypeVector %27 2 + %29 = OpTypeMatrix %28 2 + %30 = OpTypePointer Private %29 + %31 = OpVariable %30 Private + %34 = OpTypeStruct %27 + %35 = OpTypePointer Uniform %34 + %36 = OpVariable %35 Uniform + %37 = OpTypePointer Uniform %27 + %40 = OpTypePointer Private %27 + %43 = OpConstant %6 1 + %62 = OpConstant %6 3 + %64 = OpTypeVector %27 3 + %65 = OpTypeMatrix %64 2 + %66 = OpTypePointer Private %65 + %67 = OpVariable %66 Private + %92 = OpConstant %6 4 + %94 = OpTypeVector %27 4 + %95 = OpTypeMatrix %94 2 + %96 = OpTypePointer Private %95 + %97 = OpVariable %96 Private + %123 = OpTypeMatrix %28 3 + %124 = OpTypePointer Private %123 + %125 = OpVariable %124 Private + %151 = OpTypeMatrix %64 3 + %152 = OpTypePointer Private %151 + %153 = OpVariable %152 Private + %179 = OpTypeMatrix %94 3 + %180 = OpTypePointer Private %179 + %181 = OpVariable %180 Private + %207 = OpTypeMatrix %28 4 + %208 = OpTypePointer Private %207 + %209 = OpVariable %208 Private + %235 = OpTypeMatrix %64 4 + %236 = OpTypePointer Private %235 + %237 = OpVariable %236 Private + %263 = OpTypeMatrix %94 4 + %264 = OpTypePointer Private %263 + %265 = OpVariable %264 Private + %275 = OpTypeInt 32 0 + %276 = OpConstant %275 9 + %277 = OpTypeArray %27 %276 + %278 = OpTypePointer Function %277 + %280 = OpConstant %27 0 + %281 = OpTypePointer Function %27 + %311 = OpConstant %27 16 + %448 = OpConstant %6 5 + %482 = OpConstant %6 6 + %516 = OpConstant %6 7 + %550 = OpConstant %6 8 + %585 = OpTypePointer Input %94 + %586 = OpVariable %585 Input + %587 = OpConstant %275 0 + %588 = OpTypePointer Input %27 + %591 = OpTypeStruct %28 + %592 = OpTypePointer Uniform %591 + %593 = OpVariable %592 Uniform + %596 = OpConstant %27 3 + %601 = OpConstant %275 1 + %617 = OpConstant %6 9 + %622 = OpTypePointer Output %94 + %623 = OpVariable %622 Output + %628 = OpConstant %27 1 + %634 = OpConstantComposite %94 %280 %280 %280 %628 + %635 = OpUndef %6 + %636 = OpUndef %17 + %637 = OpUndef %27 + %638 = OpUndef %64 + %639 = OpUndef %94 + %640 = OpConstantTrue %17 + %736 = OpConstantFalse %17 + %642 = OpVariable %37 Uniform + %643 = OpVariable %40 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %164 + %164 = OpLabel + OpLoopMerge %166 %167 None + OpBranch %165 + %165 = OpLabel + OpBranch %172 + %172 = OpLabel + OpSelectionMerge %174 None + OpBranchConditional %640 %174 %174 + %174 = OpLabel + %785 = OpCopyObject %6 %43 + OpBranch %167 + %167 = OpLabel + %190 = OpIAdd %6 %9 %785 + OpBranchConditional %640 %164 %166 + %166 = OpLabel + OpBranch %196 + %196 = OpLabel + OpBranch %194 + %194 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // This transformation would shortcut the part of the loop body that defines + // an id used in the continue target. + auto bad_transformation = TransformationAddDeadContinue(165, false, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadContinueTest, Miscellaneous2) { + // A miscellaneous test that exposed a bug in spirv-fuzz. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %51 = OpTypeBool + %395 = OpConstantTrue %51 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %389 + %389 = OpLabel + OpLoopMerge %388 %391 None + OpBranch %339 + %339 = OpLabel + OpSelectionMerge %396 None + OpBranchConditional %395 %388 %396 + %396 = OpLabel + OpBranch %1552 + %1552 = OpLabel + OpLoopMerge %1553 %1554 None + OpBranch %1556 + %1556 = OpLabel + OpLoopMerge %1557 %1570 None + OpBranchConditional %395 %1562 %1557 + %1562 = OpLabel + OpBranchConditional %395 %1571 %1570 + %1571 = OpLabel + OpBranch %1557 + %1570 = OpLabel + OpBranch %1556 + %1557 = OpLabel + OpSelectionMerge %1586 None + OpBranchConditional %395 %1553 %1586 + %1586 = OpLabel + OpBranch %1553 + %1554 = OpLabel + OpBranch %1552 + %1553 = OpLabel + OpBranch %388 + %391 = OpLabel + OpBranch %389 + %388 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // This transformation would introduce a branch from a continue target to + // itself. + auto bad_transformation = TransformationAddDeadContinue(1554, true, {}); + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadContinueTest, Miscellaneous3) { + // A miscellaneous test that exposed a bug in spirv-fuzz. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %85 = OpTypeBool + %434 = OpConstantFalse %85 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %234 + %234 = OpLabel + OpLoopMerge %235 %236 None + OpBranch %259 + %259 = OpLabel + OpLoopMerge %260 %274 None + OpBranchConditional %434 %265 %260 + %265 = OpLabel + OpBranch %275 + %275 = OpLabel + OpBranch %260 + %274 = OpLabel + OpBranch %259 + %260 = OpLabel + OpSelectionMerge %298 None + OpBranchConditional %434 %299 %300 + %300 = OpLabel + OpBranch %235 + %298 = OpLabel + OpUnreachable + %236 = OpLabel + OpBranch %234 + %299 = OpLabel + OpBranch %235 + %235 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadContinue(299, false, {}); + + // The continue edge would connect %299 to the previously-unreachable %236, + // making %299 dominate %236, and breaking the rule that block ordering must + // respect dominance. + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadContinueTest, Miscellaneous4) { + // A miscellaneous test that exposed a bug in spirv-fuzz. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %100 = OpConstantFalse %17 + %21 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %13 = OpLabel + %20 = OpLoad %6 %8 + %22 = OpIAdd %6 %20 %21 + OpStore %8 %22 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadContinue(10, false, {}); + + // The continue edge would connect %10 to the previously-unreachable %13, + // making %10 dominate %13, and breaking the rule that block ordering must + // respect dominance. + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadContinueTest, Miscellaneous5) { + // A miscellaneous test that exposed a bug in spirv-fuzz. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %98 + %98 = OpLabel + OpLoopMerge %100 %101 None + OpBranch %99 + %99 = OpLabel + OpSelectionMerge %111 None + OpBranchConditional %9 %110 %111 + %110 = OpLabel + OpBranch %100 + %111 = OpLabel + %200 = OpCopyObject %6 %9 + OpBranch %101 + %101 = OpLabel + %201 = OpCopyObject %6 %200 + OpBranchConditional %9 %98 %100 + %100 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadContinue(110, true, {}); + + // The continue edge would lead to the use of %200 in block %101 no longer + // being dominated by its definition in block %111. + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddDeadContinueTest, Miscellaneous6) { + // A miscellaneous test that exposed a bug in spirv-fuzz. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %9 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %13 %12 None + OpBranch %11 + %11 = OpLabel + %20 = OpCopyObject %6 %9 + OpBranch %12 + %12 = OpLabel + OpBranchConditional %9 %10 %13 + %13 = OpLabel + %21 = OpCopyObject %6 %20 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_transformation = TransformationAddDeadContinue(10, true, {}); + + ASSERT_FALSE( + bad_transformation.IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_early_terminator_wrapper_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_early_terminator_wrapper_test.cpp new file mode 100644 index 0000000..8239e21 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_early_terminator_wrapper_test.cpp @@ -0,0 +1,163 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_early_terminator_wrapper.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddEarlyTerminatorWrapperTest, NoVoidType) { + std::string shader = R"( + OpCapability Shader + OpCapability Linkage + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSource ESSL 320 + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 101, SpvOpKill) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddEarlyTerminatorWrapperTest, NoVoidFunctionType) { + std::string shader = R"( + OpCapability Shader + OpCapability Linkage + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpSource ESSL 320 + %2 = OpTypeVoid + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 101, SpvOpKill) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddEarlyTerminatorWrapperTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(2, 101, SpvOpKill) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 4, SpvOpKill) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddEarlyTerminatorWrapper(100, 100, SpvOpKill) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + ASSERT_DEATH(TransformationAddEarlyTerminatorWrapper(100, 101, SpvOpReturn) + .IsApplicable(context.get(), transformation_context), + "Invalid opcode."); +#endif + + auto transformation1 = + TransformationAddEarlyTerminatorWrapper(100, 101, SpvOpKill); + auto transformation2 = + TransformationAddEarlyTerminatorWrapper(102, 103, SpvOpUnreachable); + auto transformation3 = TransformationAddEarlyTerminatorWrapper( + 104, 105, SpvOpTerminateInvocation); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %100 = OpFunction %2 None %3 + %101 = OpLabel + OpKill + OpFunctionEnd + %102 = OpFunction %2 None %3 + %103 = OpLabel + OpUnreachable + OpFunctionEnd + %104 = OpFunction %2 None %3 + %105 = OpLabel + OpTerminateInvocation + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_function_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_function_test.cpp new file mode 100644 index 0000000..d55fb93 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_function_test.cpp @@ -0,0 +1,3018 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_function.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_message.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +protobufs::AccessChainClampingInfo MakeAccessClampingInfo( + uint32_t access_chain_id, + const std::vector>& compare_and_select_ids) { + protobufs::AccessChainClampingInfo result; + result.set_access_chain_id(access_chain_id); + for (auto& compare_and_select_id : compare_and_select_ids) { + auto pair = result.add_compare_and_select_ids(); + pair->set_first(compare_and_select_id.first); + pair->set_second(compare_and_select_id.second); + } + return result; +} + +std::vector GetInstructionsForFunction( + spv_target_env env, const MessageConsumer& consumer, + const std::string& donor, uint32_t function_id) { + std::vector result; + const auto donor_context = + BuildModule(env, consumer, donor, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + assert(fuzzerutil::IsValidAndWellFormed( + donor_context.get(), validator_options, kConsoleMessageConsumer) && + "The given donor must be valid."); + for (auto& function : *donor_context->module()) { + if (function.result_id() == function_id) { + function.ForEachInst([&result](opt::Instruction* inst) { + opt::Instruction::OperandList input_operands; + for (uint32_t i = 0; i < inst->NumInOperands(); i++) { + input_operands.push_back(inst->GetInOperand(i)); + } + result.push_back(MakeInstructionMessage(inst->opcode(), inst->type_id(), + inst->result_id(), + input_operands)); + }); + break; + } + } + assert(!result.empty() && "The required function should have been found."); + return result; +} + +// Returns true if and only if every pointer parameter and variable associated +// with |function_id| in |context| is known by |transformation_context| to be +// irrelevant, with the exception of |loop_limiter_id|, which must not be +// irrelevant. (It can be 0 if no loop limiter is expected, and 0 should not be +// deemed irrelevant). +bool AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + opt::IRContext* context, + const TransformationContext& transformation_context, uint32_t function_id, + uint32_t loop_limiter_id) { + // Look at all the functions until the function of interest is found. + for (auto& function : *context->module()) { + if (function.result_id() != function_id) { + continue; + } + // Check that the parameters are all irrelevant. + bool found_non_irrelevant_parameter = false; + function.ForEachParam([context, &transformation_context, + &found_non_irrelevant_parameter]( + opt::Instruction* inst) { + if (context->get_def_use_mgr()->GetDef(inst->type_id())->opcode() == + SpvOpTypePointer && + !transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + inst->result_id())) { + found_non_irrelevant_parameter = true; + } + }); + if (found_non_irrelevant_parameter) { + // A non-irrelevant parameter was found. + return false; + } + // Look through the instructions in the function's first block. + for (auto& inst : *function.begin()) { + if (inst.opcode() != SpvOpVariable) { + // We have found a non-variable instruction; this means we have gotten + // past all variables, so we are done. + return true; + } + // The variable should be irrelevant if and only if it is not the loop + // limiter. + if ((inst.result_id() == loop_limiter_id) == + transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + inst.result_id())) { + return false; + } + } + assert(false && + "We should have processed all variables and returned by " + "this point."); + } + assert(false && "We should have found the function of interest."); + return true; +} + +TEST(TransformationAddFunctionTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %7 %9 + %18 = OpConstant %8 0 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %37 = OpConstant %6 1 + %42 = OpTypePointer Private %8 + %43 = OpVariable %42 Private + %47 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationAddFunction transformation1(std::vector( + {MakeInstructionMessage( + SpvOpFunction, 8, 13, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {10}}}), + MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}), + MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}), + MakeInstructionMessage(SpvOpLabel, 0, 14, {}), + MakeInstructionMessage( + SpvOpVariable, 9, 17, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpVariable, 7, 19, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {18}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {20}}}), + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}), + MakeInstructionMessage(SpvOpLabel, 0, 21, {}), + MakeInstructionMessage( + SpvOpLoopMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {23}}, + {SPV_OPERAND_TYPE_ID, {24}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}}), + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}}), + MakeInstructionMessage(SpvOpLabel, 0, 25, {}), + MakeInstructionMessage(SpvOpLoad, 6, 26, {{SPV_OPERAND_TYPE_ID, {19}}}), + MakeInstructionMessage(SpvOpLoad, 6, 27, {{SPV_OPERAND_TYPE_ID, {11}}}), + MakeInstructionMessage( + SpvOpSLessThan, 28, 29, + {{SPV_OPERAND_TYPE_ID, {26}}, {SPV_OPERAND_TYPE_ID, {27}}}), + MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {29}}, + {SPV_OPERAND_TYPE_ID, {22}}, + {SPV_OPERAND_TYPE_ID, {23}}}), + MakeInstructionMessage(SpvOpLabel, 0, 22, {}), + MakeInstructionMessage(SpvOpLoad, 8, 30, {{SPV_OPERAND_TYPE_ID, {12}}}), + MakeInstructionMessage(SpvOpLoad, 6, 31, {{SPV_OPERAND_TYPE_ID, {19}}}), + MakeInstructionMessage(SpvOpConvertSToF, 8, 32, + {{SPV_OPERAND_TYPE_ID, {31}}}), + MakeInstructionMessage( + SpvOpFMul, 8, 33, + {{SPV_OPERAND_TYPE_ID, {30}}, {SPV_OPERAND_TYPE_ID, {32}}}), + MakeInstructionMessage(SpvOpLoad, 8, 34, {{SPV_OPERAND_TYPE_ID, {17}}}), + MakeInstructionMessage( + SpvOpFAdd, 8, 35, + {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {17}}, {SPV_OPERAND_TYPE_ID, {35}}}), + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {24}}}), + MakeInstructionMessage(SpvOpLabel, 0, 24, {}), + MakeInstructionMessage(SpvOpLoad, 6, 36, {{SPV_OPERAND_TYPE_ID, {19}}}), + MakeInstructionMessage( + SpvOpIAdd, 6, 38, + {{SPV_OPERAND_TYPE_ID, {36}}, {SPV_OPERAND_TYPE_ID, {37}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {19}}, {SPV_OPERAND_TYPE_ID, {38}}}), + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {21}}}), + MakeInstructionMessage(SpvOpLabel, 0, 23, {}), + MakeInstructionMessage(SpvOpLoad, 8, 39, {{SPV_OPERAND_TYPE_ID, {17}}}), + MakeInstructionMessage(SpvOpReturnValue, 0, 0, + {{SPV_OPERAND_TYPE_ID, {39}}}), + MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %7 %9 + %18 = OpConstant %8 0 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %37 = OpConstant %6 1 + %42 = OpTypePointer Private %8 + %43 = OpVariable %42 Private + %47 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %8 None %10 + %11 = OpFunctionParameter %7 + %12 = OpFunctionParameter %9 + %14 = OpLabel + %17 = OpVariable %9 Function + %19 = OpVariable %7 Function + OpStore %17 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %26 = OpLoad %6 %19 + %27 = OpLoad %6 %11 + %29 = OpSLessThan %28 %26 %27 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + %30 = OpLoad %8 %12 + %31 = OpLoad %6 %19 + %32 = OpConvertSToF %8 %31 + %33 = OpFMul %8 %30 %32 + %34 = OpLoad %8 %17 + %35 = OpFAdd %8 %34 %33 + OpStore %17 %35 + OpBranch %24 + %24 = OpLabel + %36 = OpLoad %6 %19 + %38 = OpIAdd %6 %36 %37 + OpStore %19 %38 + OpBranch %21 + %23 = OpLabel + %39 = OpLoad %8 %17 + OpReturnValue %39 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation1, context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(14)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(21)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(22)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(23)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(24)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(25)); + + TransformationAddFunction transformation2(std::vector( + {MakeInstructionMessage( + SpvOpFunction, 2, 15, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {3}}}), + MakeInstructionMessage(SpvOpLabel, 0, 16, {}), + MakeInstructionMessage( + SpvOpVariable, 7, 44, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpVariable, 9, 45, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpVariable, 7, 48, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpVariable, 9, 49, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {44}}, {SPV_OPERAND_TYPE_ID, {20}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {18}}}), + MakeInstructionMessage(SpvOpFunctionCall, 8, 46, + {{SPV_OPERAND_TYPE_ID, {13}}, + {SPV_OPERAND_TYPE_ID, {44}}, + {SPV_OPERAND_TYPE_ID, {45}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {48}}, {SPV_OPERAND_TYPE_ID, {37}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {49}}, {SPV_OPERAND_TYPE_ID, {47}}}), + MakeInstructionMessage(SpvOpFunctionCall, 8, 50, + {{SPV_OPERAND_TYPE_ID, {13}}, + {SPV_OPERAND_TYPE_ID, {48}}, + {SPV_OPERAND_TYPE_ID, {49}}}), + MakeInstructionMessage( + SpvOpFAdd, 8, 51, + {{SPV_OPERAND_TYPE_ID, {46}}, {SPV_OPERAND_TYPE_ID, {50}}}), + MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {43}}, {SPV_OPERAND_TYPE_ID, {51}}}), + MakeInstructionMessage(SpvOpReturn, 0, 0, {}), + MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})); + + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %7 %9 + %18 = OpConstant %8 0 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %37 = OpConstant %6 1 + %42 = OpTypePointer Private %8 + %43 = OpVariable %42 Private + %47 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %8 None %10 + %11 = OpFunctionParameter %7 + %12 = OpFunctionParameter %9 + %14 = OpLabel + %17 = OpVariable %9 Function + %19 = OpVariable %7 Function + OpStore %17 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %26 = OpLoad %6 %19 + %27 = OpLoad %6 %11 + %29 = OpSLessThan %28 %26 %27 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + %30 = OpLoad %8 %12 + %31 = OpLoad %6 %19 + %32 = OpConvertSToF %8 %31 + %33 = OpFMul %8 %30 %32 + %34 = OpLoad %8 %17 + %35 = OpFAdd %8 %34 %33 + OpStore %17 %35 + OpBranch %24 + %24 = OpLabel + %36 = OpLoad %6 %19 + %38 = OpIAdd %6 %36 %37 + OpStore %19 %38 + OpBranch %21 + %23 = OpLabel + %39 = OpLoad %8 %17 + OpReturnValue %39 + OpFunctionEnd + %15 = OpFunction %2 None %3 + %16 = OpLabel + %44 = OpVariable %7 Function + %45 = OpVariable %9 Function + %48 = OpVariable %7 Function + %49 = OpVariable %9 Function + OpStore %44 %20 + OpStore %45 %18 + %46 = OpFunctionCall %8 %13 %44 %45 + OpStore %48 %37 + OpStore %49 %47 + %50 = OpFunctionCall %8 %13 %48 %49 + %51 = OpFAdd %8 %46 %50 + OpStore %43 %51 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation2, context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(16)); +} + +TEST(TransformationAddFunctionTest, InapplicableTransformations) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %7 %9 + %18 = OpConstant %8 0 + %20 = OpConstant %6 0 + %28 = OpTypeBool + %37 = OpConstant %6 1 + %42 = OpTypePointer Private %8 + %43 = OpVariable %42 Private + %47 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %13 = OpFunction %8 None %10 + %11 = OpFunctionParameter %7 + %12 = OpFunctionParameter %9 + %14 = OpLabel + %17 = OpVariable %9 Function + %19 = OpVariable %7 Function + OpStore %17 %18 + OpStore %19 %20 + OpBranch %21 + %21 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + %26 = OpLoad %6 %19 + %27 = OpLoad %6 %11 + %29 = OpSLessThan %28 %26 %27 + OpBranchConditional %29 %22 %23 + %22 = OpLabel + %30 = OpLoad %8 %12 + %31 = OpLoad %6 %19 + %32 = OpConvertSToF %8 %31 + %33 = OpFMul %8 %30 %32 + %34 = OpLoad %8 %17 + %35 = OpFAdd %8 %34 %33 + OpStore %17 %35 + OpBranch %24 + %24 = OpLabel + %36 = OpLoad %6 %19 + %38 = OpIAdd %6 %36 %37 + OpStore %19 %38 + OpBranch %21 + %23 = OpLabel + %39 = OpLoad %8 %17 + OpReturnValue %39 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // No instructions + ASSERT_FALSE( + TransformationAddFunction(std::vector({})) + .IsApplicable(context.get(), transformation_context)); + + // No function begin + ASSERT_FALSE( + TransformationAddFunction( + std::vector( + {MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {}), + MakeInstructionMessage(SpvOpFunctionParameter, 9, 12, {}), + MakeInstructionMessage(SpvOpLabel, 0, 14, {})})) + .IsApplicable(context.get(), transformation_context)); + + // No OpLabel + ASSERT_FALSE( + TransformationAddFunction( + std::vector( + {MakeInstructionMessage(SpvOpFunction, 8, 13, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, + {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {10}}}), + MakeInstructionMessage(SpvOpReturnValue, 0, 0, + {{SPV_OPERAND_TYPE_ID, {39}}}), + MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})})) + .IsApplicable(context.get(), transformation_context)); + + // Abrupt end of instructions + ASSERT_FALSE(TransformationAddFunction( + std::vector({MakeInstructionMessage( + SpvOpFunction, 8, 13, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, + {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {10}}})})) + .IsApplicable(context.get(), transformation_context)); + + // No function end + ASSERT_FALSE( + TransformationAddFunction( + std::vector( + {MakeInstructionMessage(SpvOpFunction, 8, 13, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, + {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_ID, {10}}}), + MakeInstructionMessage(SpvOpLabel, 0, 14, {}), + MakeInstructionMessage(SpvOpReturnValue, 0, 0, + {{SPV_OPERAND_TYPE_ID, {39}}})})) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddFunctionTest, LoopLimiters) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 30, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 31, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 20, {})); + instructions.push_back(MakeInstructionMessage( + SpvOpLoopMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {21}}, + {SPV_OPERAND_TYPE_ID, {22}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {12}}, + {SPV_OPERAND_TYPE_ID, {23}}, + {SPV_OPERAND_TYPE_ID, {21}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 23, {})); + instructions.push_back(MakeInstructionMessage( + SpvOpLoopMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {25}}, + {SPV_OPERAND_TYPE_ID, {26}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {28}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 28, {})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {12}}, + {SPV_OPERAND_TYPE_ID, {26}}, + {SPV_OPERAND_TYPE_ID, {25}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 26, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {23}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 25, {})); + instructions.push_back(MakeInstructionMessage( + SpvOpLoopMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {24}}, + {SPV_OPERAND_TYPE_ID, {27}}, + {SPV_OPERAND_TYPE_LOOP_CONTROL, {SpvLoopControlMaskNone}}})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {12}}, + {SPV_OPERAND_TYPE_ID, {24}}, + {SPV_OPERAND_TYPE_ID, {27}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 27, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {25}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 24, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {22}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 22, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpBranch, 0, 0, {{SPV_OPERAND_TYPE_ID, {20}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 21, {})); + instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + spvtools::ValidatorOptions validator_options; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context1( + MakeUnique(context1.get()), validator_options); + TransformationContext transformation_context2( + MakeUnique(context2.get()), validator_options); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + ApplyAndCheckFreshIds(add_dead_function, context1.get(), + &transformation_context1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + // The added function should not be deemed livesafe. + ASSERT_FALSE( + transformation_context1.GetFactManager()->FunctionIsLivesafe(30)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context1.get(), transformation_context1, 30, 0)); + + std::string added_as_dead_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %3 + %31 = OpLabel + OpBranch %20 + %20 = OpLabel + OpLoopMerge %21 %22 None + OpBranchConditional %12 %23 %21 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %28 + %28 = OpLabel + OpBranchConditional %12 %26 %25 + %26 = OpLabel + OpBranch %23 + %25 = OpLabel + OpLoopMerge %24 %27 None + OpBranchConditional %12 %24 %27 + %27 = OpLabel + OpBranch %25 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %20 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + protobufs::LoopLimiterInfo loop_limiter1; + loop_limiter1.set_loop_header_id(20); + loop_limiter1.set_load_id(101); + loop_limiter1.set_increment_id(102); + loop_limiter1.set_compare_id(103); + loop_limiter1.set_logical_op_id(104); + + protobufs::LoopLimiterInfo loop_limiter2; + loop_limiter2.set_loop_header_id(23); + loop_limiter2.set_load_id(105); + loop_limiter2.set_increment_id(106); + loop_limiter2.set_compare_id(107); + loop_limiter2.set_logical_op_id(108); + + protobufs::LoopLimiterInfo loop_limiter3; + loop_limiter3.set_loop_header_id(25); + loop_limiter3.set_load_id(109); + loop_limiter3.set_increment_id(110); + loop_limiter3.set_compare_id(111); + loop_limiter3.set_logical_op_id(112); + + std::vector loop_limiters = { + loop_limiter1, loop_limiter2, loop_limiter3}; + + TransformationAddFunction add_livesafe_function(instructions, 100, 10, + loop_limiters, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), + &transformation_context2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context2.get(), validator_options, kConsoleMessageConsumer)); + // The added function should indeed be deemed livesafe. + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(30)); + // All variables/parameters in the function should be deemed irrelevant, + // except the loop limiter. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context2.get(), transformation_context2, 30, 100)); + std::string added_as_livesafe_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %3 + %31 = OpLabel + %100 = OpVariable %7 Function %8 + OpBranch %20 + %20 = OpLabel + OpLoopMerge %21 %22 None + OpBranchConditional %12 %23 %21 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %28 + %28 = OpLabel + OpBranchConditional %12 %26 %25 + %26 = OpLabel + %105 = OpLoad %6 %100 + %106 = OpIAdd %6 %105 %9 + OpStore %100 %106 + %107 = OpUGreaterThanEqual %11 %105 %10 + OpBranchConditional %107 %25 %23 + %25 = OpLabel + OpLoopMerge %24 %27 None + OpBranchConditional %12 %24 %27 + %27 = OpLabel + %109 = OpLoad %6 %100 + %110 = OpIAdd %6 %109 %9 + OpStore %100 %110 + %111 = OpUGreaterThanEqual %11 %109 %10 + OpBranchConditional %111 %24 %25 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + %101 = OpLoad %6 %100 + %102 = OpIAdd %6 %101 %9 + OpStore %100 %102 + %103 = OpUGreaterThanEqual %11 %101 %10 + OpBranchConditional %103 %21 %20 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, KillAndUnreachableInVoidFunction) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 10, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {8}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpIEqual, 14, 15, + {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpSelectionMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {17}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {15}}, + {SPV_OPERAND_TYPE_ID, {16}}, + {SPV_OPERAND_TYPE_ID, {17}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {})); + instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {})); + instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + spvtools::ValidatorOptions validator_options; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context1( + MakeUnique(context1.get()), validator_options); + TransformationContext transformation_context2( + MakeUnique(context2.get()), validator_options); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + ApplyAndCheckFreshIds(add_dead_function, context1.get(), + &transformation_context1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + // The added function should not be deemed livesafe. + ASSERT_FALSE( + transformation_context1.GetFactManager()->FunctionIsLivesafe(10)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context1.get(), transformation_context1, 10, 0)); + + std::string added_as_dead_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %15 = OpIEqual %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + OpUnreachable + %17 = OpLabel + OpKill + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, + {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), + &transformation_context2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context2.get(), validator_options, kConsoleMessageConsumer)); + // The added function should indeed be deemed livesafe. + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context2.get(), transformation_context2, 10, 0)); + std::string added_as_livesafe_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %15 = OpIEqual %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + OpReturn + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, KillAndUnreachableInNonVoidFunction) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpTypeFunction %6 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 6, 10, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {50}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 11, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 12, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpIEqual, 14, 15, + {{SPV_OPERAND_TYPE_ID, {12}}, {SPV_OPERAND_TYPE_ID, {13}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpSelectionMerge, 0, 0, + {{SPV_OPERAND_TYPE_ID, {17}}, + {SPV_OPERAND_TYPE_SELECTION_CONTROL, {SpvSelectionControlMaskNone}}})); + instructions.push_back(MakeInstructionMessage(SpvOpBranchConditional, 0, 0, + {{SPV_OPERAND_TYPE_ID, {15}}, + {SPV_OPERAND_TYPE_ID, {16}}, + {SPV_OPERAND_TYPE_ID, {17}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 16, {})); + instructions.push_back(MakeInstructionMessage(SpvOpUnreachable, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 17, {})); + instructions.push_back(MakeInstructionMessage(SpvOpKill, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + spvtools::ValidatorOptions validator_options; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context1( + MakeUnique(context1.get()), validator_options); + TransformationContext transformation_context2( + MakeUnique(context2.get()), validator_options); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + ApplyAndCheckFreshIds(add_dead_function, context1.get(), + &transformation_context1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + // The added function should not be deemed livesafe. + ASSERT_FALSE( + transformation_context1.GetFactManager()->FunctionIsLivesafe(10)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context1.get(), transformation_context1, 10, 0)); + + std::string added_as_dead_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpTypeFunction %6 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %50 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %15 = OpIEqual %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + OpUnreachable + %17 = OpLabel + OpKill + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, + {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), + &transformation_context2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context2.get(), validator_options, kConsoleMessageConsumer)); + // The added function should indeed be deemed livesafe. + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(10)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context2.get(), transformation_context2, 10, 0)); + std::string added_as_livesafe_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpTypeFunction %6 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %50 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %15 = OpIEqual %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + OpReturnValue %13 + %17 = OpLabel + OpReturnValue %13 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, ClampedAccessChains) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %100 = OpTypeBool + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %15 = OpTypeInt 32 0 + %102 = OpTypePointer Function %15 + %8 = OpTypeFunction %2 %7 %102 %7 + %16 = OpConstant %15 5 + %17 = OpTypeArray %6 %16 + %18 = OpTypeArray %17 %16 + %19 = OpTypePointer Private %18 + %20 = OpVariable %19 Private + %21 = OpConstant %6 0 + %23 = OpTypePointer Private %6 + %26 = OpTypePointer Function %17 + %29 = OpTypePointer Private %17 + %33 = OpConstant %6 4 + %200 = OpConstant %15 4 + %35 = OpConstant %15 10 + %36 = OpTypeArray %6 %35 + %37 = OpTypePointer Private %36 + %38 = OpVariable %37 Private + %54 = OpTypeFloat 32 + %55 = OpTypeVector %54 4 + %56 = OpTypePointer Private %55 + %57 = OpVariable %56 Private + %59 = OpTypeVector %54 3 + %60 = OpTypeMatrix %59 2 + %61 = OpTypePointer Private %60 + %62 = OpVariable %61 Private + %64 = OpTypePointer Private %54 + %69 = OpConstant %54 2 + %71 = OpConstant %6 1 + %72 = OpConstant %6 2 + %201 = OpConstant %15 2 + %73 = OpConstant %6 3 + %202 = OpConstant %15 3 + %203 = OpConstant %6 1 + %204 = OpConstant %6 9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 12, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {8}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 7, 9, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 102, 10, {})); + instructions.push_back( + MakeInstructionMessage(SpvOpFunctionParameter, 7, 11, {})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 13, {})); + + instructions.push_back(MakeInstructionMessage( + SpvOpVariable, 7, 14, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpVariable, 26, 27, + {{SPV_OPERAND_TYPE_STORAGE_CLASS, {SpvStorageClassFunction}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 22, {{SPV_OPERAND_TYPE_ID, {11}}})); + instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 24, + {{SPV_OPERAND_TYPE_ID, {20}}, + {SPV_OPERAND_TYPE_ID, {21}}, + {SPV_OPERAND_TYPE_ID, {22}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 25, {{SPV_OPERAND_TYPE_ID, {24}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {14}}, {SPV_OPERAND_TYPE_ID, {25}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 15, 28, {{SPV_OPERAND_TYPE_ID, {10}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpAccessChain, 29, 30, + {{SPV_OPERAND_TYPE_ID, {20}}, {SPV_OPERAND_TYPE_ID, {28}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 17, 31, {{SPV_OPERAND_TYPE_ID, {30}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {31}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 32, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpInBoundsAccessChain, 7, 34, + {{SPV_OPERAND_TYPE_ID, {27}}, {SPV_OPERAND_TYPE_ID, {32}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {34}}, {SPV_OPERAND_TYPE_ID, {33}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 39, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpAccessChain, 23, 40, + {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {33}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 41, {{SPV_OPERAND_TYPE_ID, {40}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpInBoundsAccessChain, 23, 42, + {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {39}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {42}}, {SPV_OPERAND_TYPE_ID, {41}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 15, 43, {{SPV_OPERAND_TYPE_ID, {10}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 44, {{SPV_OPERAND_TYPE_ID, {11}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 45, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 15, 46, {{SPV_OPERAND_TYPE_ID, {10}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpIAdd, 6, 47, + {{SPV_OPERAND_TYPE_ID, {45}}, {SPV_OPERAND_TYPE_ID, {46}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpAccessChain, 23, 48, + {{SPV_OPERAND_TYPE_ID, {38}}, {SPV_OPERAND_TYPE_ID, {47}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 49, {{SPV_OPERAND_TYPE_ID, {48}}})); + instructions.push_back(MakeInstructionMessage(SpvOpInBoundsAccessChain, 23, + 50, + {{SPV_OPERAND_TYPE_ID, {20}}, + {SPV_OPERAND_TYPE_ID, {43}}, + {SPV_OPERAND_TYPE_ID, {44}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 51, {{SPV_OPERAND_TYPE_ID, {50}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpIAdd, 6, 52, + {{SPV_OPERAND_TYPE_ID, {51}}, {SPV_OPERAND_TYPE_ID, {49}}})); + instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 23, 53, + {{SPV_OPERAND_TYPE_ID, {20}}, + {SPV_OPERAND_TYPE_ID, {43}}, + {SPV_OPERAND_TYPE_ID, {44}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {53}}, {SPV_OPERAND_TYPE_ID, {52}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 15, 58, {{SPV_OPERAND_TYPE_ID, {10}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 63, {{SPV_OPERAND_TYPE_ID, {11}}})); + instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 65, + {{SPV_OPERAND_TYPE_ID, {62}}, + {SPV_OPERAND_TYPE_ID, {21}}, + {SPV_OPERAND_TYPE_ID, {63}}})); + instructions.push_back(MakeInstructionMessage(SpvOpAccessChain, 64, 101, + {{SPV_OPERAND_TYPE_ID, {62}}, + {SPV_OPERAND_TYPE_ID, {45}}, + {SPV_OPERAND_TYPE_ID, {46}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 54, 66, {{SPV_OPERAND_TYPE_ID, {65}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpAccessChain, 64, 67, + {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {58}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {67}}, {SPV_OPERAND_TYPE_ID, {66}}})); + instructions.push_back( + MakeInstructionMessage(SpvOpLoad, 6, 68, {{SPV_OPERAND_TYPE_ID, {9}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpInBoundsAccessChain, 64, 70, + {{SPV_OPERAND_TYPE_ID, {57}}, {SPV_OPERAND_TYPE_ID, {68}}})); + instructions.push_back(MakeInstructionMessage( + SpvOpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {70}}, {SPV_OPERAND_TYPE_ID, {69}}})); + instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + spvtools::ValidatorOptions validator_options; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context1( + MakeUnique(context1.get()), validator_options); + TransformationContext transformation_context2( + MakeUnique(context2.get()), validator_options); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + ApplyAndCheckFreshIds(add_dead_function, context1.get(), + &transformation_context1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + // The function should not be deemed livesafe + ASSERT_FALSE( + transformation_context1.GetFactManager()->FunctionIsLivesafe(12)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context1.get(), transformation_context1, 12, 0)); + + std::string added_as_dead_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %100 = OpTypeBool + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %15 = OpTypeInt 32 0 + %102 = OpTypePointer Function %15 + %8 = OpTypeFunction %2 %7 %102 %7 + %16 = OpConstant %15 5 + %17 = OpTypeArray %6 %16 + %18 = OpTypeArray %17 %16 + %19 = OpTypePointer Private %18 + %20 = OpVariable %19 Private + %21 = OpConstant %6 0 + %23 = OpTypePointer Private %6 + %26 = OpTypePointer Function %17 + %29 = OpTypePointer Private %17 + %33 = OpConstant %6 4 + %200 = OpConstant %15 4 + %35 = OpConstant %15 10 + %36 = OpTypeArray %6 %35 + %37 = OpTypePointer Private %36 + %38 = OpVariable %37 Private + %54 = OpTypeFloat 32 + %55 = OpTypeVector %54 4 + %56 = OpTypePointer Private %55 + %57 = OpVariable %56 Private + %59 = OpTypeVector %54 3 + %60 = OpTypeMatrix %59 2 + %61 = OpTypePointer Private %60 + %62 = OpVariable %61 Private + %64 = OpTypePointer Private %54 + %69 = OpConstant %54 2 + %71 = OpConstant %6 1 + %72 = OpConstant %6 2 + %201 = OpConstant %15 2 + %73 = OpConstant %6 3 + %202 = OpConstant %15 3 + %203 = OpConstant %6 1 + %204 = OpConstant %6 9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %102 + %11 = OpFunctionParameter %7 + %13 = OpLabel + %14 = OpVariable %7 Function + %27 = OpVariable %26 Function + %22 = OpLoad %6 %11 + %24 = OpAccessChain %23 %20 %21 %22 + %25 = OpLoad %6 %24 + OpStore %14 %25 + %28 = OpLoad %15 %10 + %30 = OpAccessChain %29 %20 %28 + %31 = OpLoad %17 %30 + OpStore %27 %31 + %32 = OpLoad %6 %9 + %34 = OpInBoundsAccessChain %7 %27 %32 + OpStore %34 %33 + %39 = OpLoad %6 %9 + %40 = OpAccessChain %23 %38 %33 + %41 = OpLoad %6 %40 + %42 = OpInBoundsAccessChain %23 %38 %39 + OpStore %42 %41 + %43 = OpLoad %15 %10 + %44 = OpLoad %6 %11 + %45 = OpLoad %6 %9 + %46 = OpLoad %15 %10 + %47 = OpIAdd %6 %45 %46 + %48 = OpAccessChain %23 %38 %47 + %49 = OpLoad %6 %48 + %50 = OpInBoundsAccessChain %23 %20 %43 %44 + %51 = OpLoad %6 %50 + %52 = OpIAdd %6 %51 %49 + %53 = OpAccessChain %23 %20 %43 %44 + OpStore %53 %52 + %58 = OpLoad %15 %10 + %63 = OpLoad %6 %11 + %65 = OpAccessChain %64 %62 %21 %63 + %101 = OpAccessChain %64 %62 %45 %46 + %66 = OpLoad %54 %65 + %67 = OpAccessChain %64 %57 %58 + OpStore %67 %66 + %68 = OpLoad %6 %9 + %70 = OpInBoundsAccessChain %64 %57 %68 + OpStore %70 %69 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + std::vector access_chain_clamping_info; + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(24, {{1001, 2001}, {1002, 2002}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(30, {{1003, 2003}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(34, {{1004, 2004}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(40, {{1005, 2005}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(42, {{1006, 2006}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(48, {{1007, 2007}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(50, {{1008, 2008}, {1009, 2009}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(53, {{1010, 2010}, {1011, 2011}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(65, {{1012, 2012}, {1013, 2013}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(101, {{1014, 2014}, {1015, 2015}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(67, {{1016, 2016}})); + access_chain_clamping_info.push_back( + MakeAccessClampingInfo(70, {{1017, 2017}})); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 13, + access_chain_clamping_info); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), + &transformation_context2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context2.get(), validator_options, kConsoleMessageConsumer)); + // The function should be deemed livesafe + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(12)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context2.get(), transformation_context2, 12, 0)); + std::string added_as_livesafe_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %100 = OpTypeBool + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %15 = OpTypeInt 32 0 + %102 = OpTypePointer Function %15 + %8 = OpTypeFunction %2 %7 %102 %7 + %16 = OpConstant %15 5 + %17 = OpTypeArray %6 %16 + %18 = OpTypeArray %17 %16 + %19 = OpTypePointer Private %18 + %20 = OpVariable %19 Private + %21 = OpConstant %6 0 + %23 = OpTypePointer Private %6 + %26 = OpTypePointer Function %17 + %29 = OpTypePointer Private %17 + %33 = OpConstant %6 4 + %200 = OpConstant %15 4 + %35 = OpConstant %15 10 + %36 = OpTypeArray %6 %35 + %37 = OpTypePointer Private %36 + %38 = OpVariable %37 Private + %54 = OpTypeFloat 32 + %55 = OpTypeVector %54 4 + %56 = OpTypePointer Private %55 + %57 = OpVariable %56 Private + %59 = OpTypeVector %54 3 + %60 = OpTypeMatrix %59 2 + %61 = OpTypePointer Private %60 + %62 = OpVariable %61 Private + %64 = OpTypePointer Private %54 + %69 = OpConstant %54 2 + %71 = OpConstant %6 1 + %72 = OpConstant %6 2 + %201 = OpConstant %15 2 + %73 = OpConstant %6 3 + %202 = OpConstant %15 3 + %203 = OpConstant %6 1 + %204 = OpConstant %6 9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %102 + %11 = OpFunctionParameter %7 + %13 = OpLabel + %14 = OpVariable %7 Function + %27 = OpVariable %26 Function + %22 = OpLoad %6 %11 + %1002 = OpULessThanEqual %100 %22 %33 + %2002 = OpSelect %6 %1002 %22 %33 + %24 = OpAccessChain %23 %20 %21 %2002 + %25 = OpLoad %6 %24 + OpStore %14 %25 + %28 = OpLoad %15 %10 + %1003 = OpULessThanEqual %100 %28 %200 + %2003 = OpSelect %15 %1003 %28 %200 + %30 = OpAccessChain %29 %20 %2003 + %31 = OpLoad %17 %30 + OpStore %27 %31 + %32 = OpLoad %6 %9 + %1004 = OpULessThanEqual %100 %32 %33 + %2004 = OpSelect %6 %1004 %32 %33 + %34 = OpInBoundsAccessChain %7 %27 %2004 + OpStore %34 %33 + %39 = OpLoad %6 %9 + %40 = OpAccessChain %23 %38 %33 + %41 = OpLoad %6 %40 + %1006 = OpULessThanEqual %100 %39 %204 + %2006 = OpSelect %6 %1006 %39 %204 + %42 = OpInBoundsAccessChain %23 %38 %2006 + OpStore %42 %41 + %43 = OpLoad %15 %10 + %44 = OpLoad %6 %11 + %45 = OpLoad %6 %9 + %46 = OpLoad %15 %10 + %47 = OpIAdd %6 %45 %46 + %1007 = OpULessThanEqual %100 %47 %204 + %2007 = OpSelect %6 %1007 %47 %204 + %48 = OpAccessChain %23 %38 %2007 + %49 = OpLoad %6 %48 + %1008 = OpULessThanEqual %100 %43 %200 + %2008 = OpSelect %15 %1008 %43 %200 + %1009 = OpULessThanEqual %100 %44 %33 + %2009 = OpSelect %6 %1009 %44 %33 + %50 = OpInBoundsAccessChain %23 %20 %2008 %2009 + %51 = OpLoad %6 %50 + %52 = OpIAdd %6 %51 %49 + %1010 = OpULessThanEqual %100 %43 %200 + %2010 = OpSelect %15 %1010 %43 %200 + %1011 = OpULessThanEqual %100 %44 %33 + %2011 = OpSelect %6 %1011 %44 %33 + %53 = OpAccessChain %23 %20 %2010 %2011 + OpStore %53 %52 + %58 = OpLoad %15 %10 + %63 = OpLoad %6 %11 + %1013 = OpULessThanEqual %100 %63 %72 + %2013 = OpSelect %6 %1013 %63 %72 + %65 = OpAccessChain %64 %62 %21 %2013 + %1014 = OpULessThanEqual %100 %45 %71 + %2014 = OpSelect %6 %1014 %45 %71 + %1015 = OpULessThanEqual %100 %46 %201 + %2015 = OpSelect %15 %1015 %46 %201 + %101 = OpAccessChain %64 %62 %2014 %2015 + %66 = OpLoad %54 %65 + %1016 = OpULessThanEqual %100 %58 %202 + %2016 = OpSelect %15 %1016 %58 %202 + %67 = OpAccessChain %64 %57 %2016 + OpStore %67 %66 + %68 = OpLoad %6 %9 + %1017 = OpULessThanEqual %100 %68 %73 + %2017 = OpSelect %6 %1017 %68 %73 + %70 = OpInBoundsAccessChain %64 %57 %2017 + OpStore %70 %69 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_livesafe_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, LivesafeCanCallLivesafe) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 8, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11, + {{SPV_OPERAND_TYPE_ID, {6}}})); + instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + spvtools::ValidatorOptions validator_options; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context1( + MakeUnique(context1.get()), validator_options); + TransformationContext transformation_context2( + MakeUnique(context2.get()), validator_options); + + // Mark function 6 as livesafe. + transformation_context2.GetFactManager()->AddFactFunctionIsLivesafe(6); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + ApplyAndCheckFreshIds(add_dead_function, context1.get(), + &transformation_context1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + // The function should not be deemed livesafe + ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context1.get(), transformation_context1, 8, 0)); + + std::string added_as_live_or_dead_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %11 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context1.get())); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, + {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); + ApplyAndCheckFreshIds(add_livesafe_function, context2.get(), + &transformation_context2); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context2.get(), validator_options, kConsoleMessageConsumer)); + // The function should be deemed livesafe + ASSERT_TRUE(transformation_context2.GetFactManager()->FunctionIsLivesafe(8)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context2.get(), transformation_context2, 8, 0)); + ASSERT_TRUE(IsEqual(env, added_as_live_or_dead_code, context2.get())); +} + +TEST(TransformationAddFunctionTest, LivesafeOnlyCallsLivesafe) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpKill + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + + std::vector instructions; + + instructions.push_back(MakeInstructionMessage( + SpvOpFunction, 2, 8, + {{SPV_OPERAND_TYPE_FUNCTION_CONTROL, {SpvFunctionControlMaskNone}}, + {SPV_OPERAND_TYPE_TYPE_ID, {3}}})); + instructions.push_back(MakeInstructionMessage(SpvOpLabel, 0, 9, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionCall, 2, 11, + {{SPV_OPERAND_TYPE_ID, {6}}})); + instructions.push_back(MakeInstructionMessage(SpvOpReturn, 0, 0, {})); + instructions.push_back(MakeInstructionMessage(SpvOpFunctionEnd, 0, 0, {})); + + spvtools::ValidatorOptions validator_options; + + const auto context1 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + const auto context2 = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context1( + MakeUnique(context1.get()), validator_options); + TransformationContext transformation_context2( + MakeUnique(context2.get()), validator_options); + + TransformationAddFunction add_dead_function(instructions); + ASSERT_TRUE( + add_dead_function.IsApplicable(context1.get(), transformation_context1)); + ApplyAndCheckFreshIds(add_dead_function, context1.get(), + &transformation_context1); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context1.get(), validator_options, kConsoleMessageConsumer)); + // The function should not be deemed livesafe + ASSERT_FALSE(transformation_context1.GetFactManager()->FunctionIsLivesafe(8)); + // All variables/parameters in the function should be deemed irrelevant. + ASSERT_TRUE(AllVariablesAndParametersExceptLoopLimiterAreIrrelevant( + context1.get(), transformation_context1, 8, 0)); + + std::string added_as_dead_code = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpKill + OpFunctionEnd + %8 = OpFunction %2 None %3 + %9 = OpLabel + %11 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, added_as_dead_code, context1.get())); + + TransformationAddFunction add_livesafe_function(instructions, 0, 0, {}, 0, + {}); + ASSERT_FALSE(add_livesafe_function.IsApplicable(context2.get(), + transformation_context2)); +} + +TEST(TransformationAddFunctionTest, + LoopLimitersBackEdgeBlockEndsWithConditional1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + OpBranchConditional %20 %12 %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a sequence of instruction messages corresponding to function %6 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(add_livesafe_function, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %102 = OpLoad %28 %100 + %103 = OpIAdd %28 %102 %31 + OpStore %100 %103 + %104 = OpULessThan %19 %102 %32 + %105 = OpLogicalAnd %19 %20 %104 + OpBranchConditional %105 %12 %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, + LoopLimitersBackEdgeBlockEndsWithConditional2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %50 = OpLogicalNot %19 %20 + OpBranchConditional %50 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a sequence of instruction messages corresponding to function %6 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(add_livesafe_function, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %15 + %15 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %50 = OpLogicalNot %19 %20 + %102 = OpLoad %28 %100 + %103 = OpIAdd %28 %102 %31 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %19 %102 %32 + %105 = OpLogicalOr %19 %50 %104 + OpBranchConditional %105 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, LoopLimitersHeaderIsBackEdgeBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %50 = OpLogicalNot %19 %20 + OpLoopMerge %14 %12 None + OpBranchConditional %50 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a sequence of instruction messages corresponding to function %6 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(add_livesafe_function, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + %21 = OpLoad %8 %10 + %23 = OpIAdd %8 %21 %22 + OpStore %10 %23 + %50 = OpLogicalNot %19 %20 + %102 = OpLoad %28 %100 + %103 = OpIAdd %28 %102 %31 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %19 %102 %32 + %105 = OpLogicalOr %19 %50 %104 + OpLoopMerge %14 %12 None + OpBranchConditional %105 %14 %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, InfiniteLoop) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %22 = OpConstant %8 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %12 None + OpBranch %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a sequence of instruction messages corresponding to function %6 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + + // To make sure the loop's merge block is reachable, it must be dominated by + // the loop header. + ASSERT_FALSE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); +} + +TEST(TransformationAddFunctionTest, UnreachableContinueConstruct) { + // This captures the case where the loop's continue construct is statically + // unreachable. In this case the loop cannot iterate and so we do not add + // a loop limiter. (The reason we do not just add one anyway is that + // detecting which block would be the back-edge block is difficult in the + // absence of reliable dominance information.) + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %23 = OpConstant %8 1 + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %23 = OpConstant %8 1 + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + OpBranch %14 + %15 = OpLabel + %22 = OpLoad %8 %10 + %24 = OpIAdd %8 %22 %23 + OpStore %10 %24 + OpBranch %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a sequence of instruction messages corresponding to function %6 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(12); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + TransformationAddFunction add_livesafe_function(instructions, 100, 32, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(add_livesafe_function, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %18 = OpConstant %8 10 + %19 = OpTypeBool + %23 = OpConstant %8 1 + %26 = OpConstantTrue %19 + %27 = OpConstantFalse %19 + %28 = OpTypeInt 32 0 + %29 = OpTypePointer Function %28 + %30 = OpConstant %28 0 + %31 = OpConstant %28 1 + %32 = OpConstant %28 5 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpVariable %29 Function %30 + %10 = OpVariable %9 Function + OpStore %10 %11 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %15 None + OpBranch %16 + %16 = OpLabel + %17 = OpLoad %8 %10 + %20 = OpSLessThan %19 %17 %18 + OpBranchConditional %20 %13 %14 + %13 = OpLabel + OpBranch %14 + %15 = OpLabel + %22 = OpLoad %8 %10 + %24 = OpIAdd %8 %22 %23 + OpStore %10 %24 + OpBranch %12 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi1) { + // This captures the scenario where breaking a loop due to a loop limiter + // requires patching up OpPhi instructions occurring at the loop merge block. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %36 = OpFunctionCall %6 %8 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %11 = OpVariable %10 Function + OpStore %11 %12 + OpBranch %13 + %13 = OpLabel + %37 = OpPhi %6 %12 %9 %32 %16 + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %21 = OpSLessThan %20 %37 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %24 = OpSGreaterThan %20 %37 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + %29 = OpIAdd %6 %37 %28 + OpStore %11 %29 + OpBranch %15 + %26 = OpLabel + OpBranch %16 + %16 = OpLabel + %32 = OpIAdd %6 %37 %28 + OpStore %11 %32 + OpBranch %13 + %15 = OpLabel + %38 = OpPhi %6 %37 %17 %29 %25 + OpReturnValue %38 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 8); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(13); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + + TransformationAddFunction no_op_phi_data(instructions, 100, 28, + {loop_limiter_info}, 0, {}); + // The loop limiter info is not good enough; it does not include ids to patch + // up the OpPhi at the loop merge. + ASSERT_FALSE( + no_op_phi_data.IsApplicable(context.get(), transformation_context)); + + // Add a phi id for the new edge from the loop back edge block to the loop + // merge. + loop_limiter_info.add_phi_id(28); + TransformationAddFunction with_op_phi_data(instructions, 100, 28, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE( + with_op_phi_data.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(with_op_phi_data, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %100 = OpVariable %53 Function %51 + %11 = OpVariable %10 Function + OpStore %11 %12 + OpBranch %13 + %13 = OpLabel + %37 = OpPhi %6 %12 %9 %32 %16 + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %21 = OpSLessThan %20 %37 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %24 = OpSGreaterThan %20 %37 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + %29 = OpIAdd %6 %37 %28 + OpStore %11 %29 + OpBranch %15 + %26 = OpLabel + OpBranch %16 + %16 = OpLabel + %32 = OpIAdd %6 %37 %28 + OpStore %11 %32 + %102 = OpLoad %50 %100 + %103 = OpIAdd %50 %102 %52 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %20 %102 %28 + OpBranchConditional %104 %15 %13 + %15 = OpLabel + %38 = OpPhi %6 %37 %17 %29 %25 %28 %16 + OpReturnValue %38 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, LoopLimitersAndOpPhi2) { + // This captures the scenario where the loop merge block already has an OpPhi + // with the loop back edge block as a predecessor. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %60 = OpConstantTrue %20 + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %60 = OpConstantTrue %20 + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %11 = OpVariable %10 Function + OpStore %11 %12 + OpBranch %13 + %13 = OpLabel + %37 = OpPhi %6 %12 %9 %32 %16 + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %21 = OpSLessThan %20 %37 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %24 = OpSGreaterThan %20 %37 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + %29 = OpIAdd %6 %37 %28 + OpStore %11 %29 + OpBranch %15 + %26 = OpLabel + OpBranch %16 + %16 = OpLabel + %32 = OpIAdd %6 %37 %28 + OpStore %11 %32 + OpBranchConditional %60 %15 %13 + %15 = OpLabel + %38 = OpPhi %6 %37 %17 %29 %25 %23 %16 + OpReturnValue %38 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a sequence of instruction messages corresponding to function %8 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 8); + + protobufs::LoopLimiterInfo loop_limiter_info; + loop_limiter_info.set_loop_header_id(13); + loop_limiter_info.set_load_id(102); + loop_limiter_info.set_increment_id(103); + loop_limiter_info.set_compare_id(104); + loop_limiter_info.set_logical_op_id(105); + + TransformationAddFunction transformation(instructions, 100, 28, + {loop_limiter_info}, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %50 = OpTypeInt 32 0 + %51 = OpConstant %50 0 + %52 = OpConstant %50 1 + %53 = OpTypePointer Function %50 + %7 = OpTypeFunction %6 + %10 = OpTypePointer Function %6 + %12 = OpConstant %6 0 + %19 = OpConstant %6 100 + %20 = OpTypeBool + %60 = OpConstantTrue %20 + %23 = OpConstant %6 20 + %28 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + %100 = OpVariable %53 Function %51 + %11 = OpVariable %10 Function + OpStore %11 %12 + OpBranch %13 + %13 = OpLabel + %37 = OpPhi %6 %12 %9 %32 %16 + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %21 = OpSLessThan %20 %37 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %24 = OpSGreaterThan %20 %37 %23 + OpSelectionMerge %26 None + OpBranchConditional %24 %25 %26 + %25 = OpLabel + %29 = OpIAdd %6 %37 %28 + OpStore %11 %29 + OpBranch %15 + %26 = OpLabel + OpBranch %16 + %16 = OpLabel + %32 = OpIAdd %6 %37 %28 + OpStore %11 %32 + %102 = OpLoad %50 %100 + %103 = OpIAdd %50 %102 %52 + OpStore %100 %103 + %104 = OpUGreaterThanEqual %20 %102 %28 + %105 = OpLogicalOr %20 %60 %104 + OpBranchConditional %105 %15 %13 + %15 = OpLabel + %38 = OpPhi %6 %37 %17 %29 %25 %23 %16 + OpReturnValue %38 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationAddFunctionTest, StaticallyOutOfBoundsArrayAccess) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypeInt 32 0 + %10 = OpConstant %9 3 + %11 = OpTypeArray %8 %10 + %12 = OpTypePointer Private %11 + %13 = OpVariable %12 Private + %14 = OpConstant %8 3 + %20 = OpConstant %8 2 + %15 = OpConstant %8 1 + %21 = OpTypeBool + %16 = OpTypePointer Private %8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + std::string donor = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypeInt 32 0 + %10 = OpConstant %9 3 + %11 = OpTypeArray %8 %10 + %12 = OpTypePointer Private %11 + %13 = OpVariable %12 Private + %14 = OpConstant %8 3 + %15 = OpConstant %8 1 + %16 = OpTypePointer Private %8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %17 = OpAccessChain %16 %13 %14 + OpStore %17 %15 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a sequence of instruction messages corresponding to function %6 in + // |donor|. + std::vector instructions = + GetInstructionsForFunction(env, consumer, donor, 6); + + TransformationAddFunction add_livesafe_function( + instructions, 0, 0, {}, 0, {MakeAccessClampingInfo(17, {{100, 101}})}); + ASSERT_TRUE(add_livesafe_function.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(add_livesafe_function, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypeInt 32 0 + %10 = OpConstant %9 3 + %11 = OpTypeArray %8 %10 + %12 = OpTypePointer Private %11 + %13 = OpVariable %12 Private + %14 = OpConstant %8 3 + %20 = OpConstant %8 2 + %15 = OpConstant %8 1 + %21 = OpTypeBool + %16 = OpTypePointer Private %8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %100 = OpULessThanEqual %21 %14 %20 + %101 = OpSelect %8 %100 %14 %20 + %17 = OpAccessChain %16 %13 %101 + OpStore %17 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_global_undef_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_global_undef_test.cpp new file mode 100644 index 0000000..c3a49e4 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_global_undef_test.cpp @@ -0,0 +1,125 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_undef.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddGlobalUndefTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id already in use + ASSERT_FALSE(TransformationAddGlobalUndef(4, 11).IsApplicable( + context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE(TransformationAddGlobalUndef(100, 1).IsApplicable( + context.get(), transformation_context)); + + // %3 is a function type + ASSERT_FALSE(TransformationAddGlobalUndef(100, 3).IsApplicable( + context.get(), transformation_context)); + + TransformationAddGlobalUndef transformations[] = { + // %100 = OpUndef %6 + TransformationAddGlobalUndef(100, 6), + + // %101 = OpUndef %7 + TransformationAddGlobalUndef(101, 7), + + // %102 = OpUndef %8 + TransformationAddGlobalUndef(102, 8), + + // %103 = OpUndef %9 + TransformationAddGlobalUndef(103, 9), + + // %104 = OpUndef %10 + TransformationAddGlobalUndef(104, 10), + + // %105 = OpUndef %11 + TransformationAddGlobalUndef(105, 11)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %100 = OpUndef %6 + %101 = OpUndef %7 + %102 = OpUndef %8 + %103 = OpUndef %9 + %104 = OpUndef %10 + %105 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_global_variable_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_global_variable_test.cpp new file mode 100644 index 0000000..eb958a7 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_global_variable_test.cpp @@ -0,0 +1,407 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_global_variable.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddGlobalVariableTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %40 = OpConstant %6 0 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %41 = OpConstantComposite %8 %40 %40 + %9 = OpTypePointer Function %6 + %10 = OpTypePointer Private %6 + %20 = OpTypePointer Uniform %6 + %11 = OpTypePointer Function %7 + %12 = OpTypePointer Private %7 + %13 = OpTypePointer Private %8 + %14 = OpVariable %10 Private + %15 = OpVariable %20 Uniform + %16 = OpConstant %7 1 + %17 = OpTypePointer Private %10 + %18 = OpTypeBool + %19 = OpTypePointer Private %18 + %21 = OpConstantTrue %18 + %22 = OpConstantFalse %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id already in use + ASSERT_FALSE( + TransformationAddGlobalVariable(4, 10, SpvStorageClassPrivate, 0, true) + .IsApplicable(context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 1, SpvStorageClassPrivate, 0, false) + .IsApplicable(context.get(), transformation_context)); + + // %7 is not a pointer type + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 7, SpvStorageClassPrivate, 0, true) + .IsApplicable(context.get(), transformation_context)); + + // %9 does not have Private storage class + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 9, SpvStorageClassPrivate, 0, false) + .IsApplicable(context.get(), transformation_context)); + + // %15 does not have Private storage class + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 15, SpvStorageClassPrivate, 0, true) + .IsApplicable(context.get(), transformation_context)); + + // %10 is a pointer to float, while %16 is an int constant + ASSERT_FALSE(TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate, + 16, false) + .IsApplicable(context.get(), transformation_context)); + + // %10 is a Private pointer to float, while %15 is a variable with type + // Uniform float pointer + ASSERT_FALSE( + TransformationAddGlobalVariable(100, 10, SpvStorageClassPrivate, 15, true) + .IsApplicable(context.get(), transformation_context)); + + // %12 is a Private pointer to int, while %10 is a variable with type + // Private float pointer + ASSERT_FALSE(TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, + 10, false) + .IsApplicable(context.get(), transformation_context)); + + // %10 is pointer-to-float, and %14 has type pointer-to-float; that's not OK + // since the initializer's type should be the *pointee* type. + ASSERT_FALSE( + TransformationAddGlobalVariable(104, 10, SpvStorageClassPrivate, 14, true) + .IsApplicable(context.get(), transformation_context)); + + // This would work in principle, but logical addressing does not allow + // a pointer to a pointer. + ASSERT_FALSE(TransformationAddGlobalVariable(104, 17, SpvStorageClassPrivate, + 14, false) + .IsApplicable(context.get(), transformation_context)); + + TransformationAddGlobalVariable transformations[] = { + // %100 = OpVariable %12 Private + TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16, + true), + + // %101 = OpVariable %10 Private + TransformationAddGlobalVariable(101, 10, SpvStorageClassPrivate, 40, + false), + + // %102 = OpVariable %13 Private + TransformationAddGlobalVariable(102, 13, SpvStorageClassPrivate, 41, + true), + + // %103 = OpVariable %12 Private %16 + TransformationAddGlobalVariable(103, 12, SpvStorageClassPrivate, 16, + false), + + // %104 = OpVariable %19 Private %21 + TransformationAddGlobalVariable(104, 19, SpvStorageClassPrivate, 21, + true), + + // %105 = OpVariable %19 Private %22 + TransformationAddGlobalVariable(105, 19, SpvStorageClassPrivate, 22, + false)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105)); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %40 = OpConstant %6 0 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %41 = OpConstantComposite %8 %40 %40 + %9 = OpTypePointer Function %6 + %10 = OpTypePointer Private %6 + %20 = OpTypePointer Uniform %6 + %11 = OpTypePointer Function %7 + %12 = OpTypePointer Private %7 + %13 = OpTypePointer Private %8 + %14 = OpVariable %10 Private + %15 = OpVariable %20 Uniform + %16 = OpConstant %7 1 + %17 = OpTypePointer Private %10 + %18 = OpTypeBool + %19 = OpTypePointer Private %18 + %21 = OpConstantTrue %18 + %22 = OpConstantFalse %18 + %100 = OpVariable %12 Private %16 + %101 = OpVariable %10 Private %40 + %102 = OpVariable %13 Private %41 + %103 = OpVariable %12 Private %16 + %104 = OpVariable %19 Private %21 + %105 = OpVariable %19 Private %22 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddGlobalVariableTest, TestEntryPointInterfaceEnlargement) { + // This checks that when global variables are added to a SPIR-V 1.4+ module, + // they are also added to entry points of that module. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "m1" + OpEntryPoint Vertex %5 "m2" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypePointer Function %6 + %10 = OpTypePointer Private %6 + %20 = OpTypePointer Uniform %6 + %11 = OpTypePointer Function %7 + %12 = OpTypePointer Private %7 + %13 = OpTypePointer Private %8 + %14 = OpVariable %10 Private + %15 = OpVariable %20 Uniform + %16 = OpConstant %7 1 + %17 = OpTypePointer Private %10 + %18 = OpTypeBool + %19 = OpTypePointer Private %18 + %21 = OpConstantTrue %18 + %4 = OpFunction %2 None %3 + %30 = OpLabel + OpReturn + OpFunctionEnd + %5 = OpFunction %2 None %3 + %31 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationAddGlobalVariable transformations[] = { + // %100 = OpVariable %12 Private + TransformationAddGlobalVariable(100, 12, SpvStorageClassPrivate, 16, + true), + + // %101 = OpVariable %12 Private %16 + TransformationAddGlobalVariable(101, 12, SpvStorageClassPrivate, 16, + false), + + // %102 = OpVariable %19 Private %21 + TransformationAddGlobalVariable(102, 19, SpvStorageClassPrivate, 21, + true)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "m1" %100 %101 %102 + OpEntryPoint Vertex %5 "m2" %100 %101 %102 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypePointer Function %6 + %10 = OpTypePointer Private %6 + %20 = OpTypePointer Uniform %6 + %11 = OpTypePointer Function %7 + %12 = OpTypePointer Private %7 + %13 = OpTypePointer Private %8 + %14 = OpVariable %10 Private + %15 = OpVariable %20 Uniform + %16 = OpConstant %7 1 + %17 = OpTypePointer Private %10 + %18 = OpTypeBool + %19 = OpTypePointer Private %18 + %21 = OpConstantTrue %18 + %100 = OpVariable %12 Private %16 + %101 = OpVariable %12 Private %16 + %102 = OpVariable %19 Private %21 + %4 = OpFunction %2 None %3 + %30 = OpLabel + OpReturn + OpFunctionEnd + %5 = OpFunction %2 None %3 + %31 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddGlobalVariableTest, TestAddingWorkgroupGlobals) { + // This checks that workgroup globals can be added to a compute shader. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Workgroup %6 + %50 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); +#ifndef NDEBUG + ASSERT_DEATH( + TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 50, true) + .IsApplicable(context.get(), transformation_context), + "By construction this transformation should not have an.*initializer " + "when Workgroup storage class is used"); +#endif + + TransformationAddGlobalVariable transformations[] = { + // %8 = OpVariable %7 Workgroup + TransformationAddGlobalVariable(8, 7, SpvStorageClassWorkgroup, 0, true), + + // %10 = OpVariable %7 Workgroup + TransformationAddGlobalVariable(10, 7, SpvStorageClassWorkgroup, 0, + false)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(10)); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" %8 %10 + OpExecutionMode %4 LocalSize 1 1 1 + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Workgroup %6 + %50 = OpConstant %6 2 + %8 = OpVariable %7 Workgroup + %10 = OpVariable %7 Workgroup + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp new file mode 100644 index 0000000..072378c --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_image_sample_unused_components_test.cpp @@ -0,0 +1,255 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_image_sample_unused_components.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddImageSampleUnusedComponentsTest, IsApplicable) { + std::string shader = R"( + OpCapability Shader + OpCapability LiteralSampler + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %18 "main" %17 + OpExecutionMode %18 OriginUpperLeft + OpSource ESSL 310 + OpName %18 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f + %9 = OpTypePointer Image %8 + %10 = OpTypeSampledImage %8 + %11 = OpTypeSampler + %12 = OpConstant %4 1 + %13 = OpConstant %4 2 + %14 = OpConstant %4 3 + %15 = OpConstant %4 4 + %16 = OpConstantSampler %11 None 0 Linear + %17 = OpVariable %9 Image + %18 = OpFunction %2 None %3 + %19 = OpLabel + %20 = OpLoad %8 %17 + %21 = OpSampledImage %10 %20 %16 + %22 = OpCompositeConstruct %5 %12 %13 + %23 = OpCompositeConstruct %6 %22 %14 + %24 = OpCompositeConstruct %7 %23 %15 + %25 = OpImageSampleImplicitLod %7 %21 %22 + %26 = OpImageSampleExplicitLod %7 %21 %23 Lod %12 + %27 = OpImageSampleExplicitLod %7 %21 %24 Lod %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests applicable image instruction. + auto instruction_descriptor = + MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0); + auto transformation = + TransformationAddImageSampleUnusedComponents(23, instruction_descriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = + MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0); + transformation = + TransformationAddImageSampleUnusedComponents(24, instruction_descriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests undefined image instructions. + instruction_descriptor = + MakeInstructionDescriptor(27, SpvOpImageSampleImplicitLod, 0); + transformation = + TransformationAddImageSampleUnusedComponents(23, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = + MakeInstructionDescriptor(28, SpvOpImageSampleExplicitLod, 0); + transformation = + TransformationAddImageSampleUnusedComponents(23, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests non-image instructions. + instruction_descriptor = MakeInstructionDescriptor(19, SpvOpLabel, 0); + transformation = + TransformationAddImageSampleUnusedComponents(24, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLoad, 0); + transformation = + TransformationAddImageSampleUnusedComponents(24, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests coordinate operand being a vec4. + instruction_descriptor = + MakeInstructionDescriptor(27, SpvOpImageSampleExplicitLod, 0); + transformation = + TransformationAddImageSampleUnusedComponents(22, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests undefined coordinate with unused operands. + instruction_descriptor = + MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0); + transformation = + TransformationAddImageSampleUnusedComponents(27, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests coordinate with unused operands being a non-OpCompositeConstruct + // instruction. + instruction_descriptor = + MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0); + transformation = + TransformationAddImageSampleUnusedComponents(21, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests the first OpCompositeConstruct constituent not being the original + // coordinate. + instruction_descriptor = + MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0); + transformation = + TransformationAddImageSampleUnusedComponents(22, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddImageSampleUnusedComponentsTest, Apply) { + std::string reference_shader = R"( + OpCapability Shader + OpCapability LiteralSampler + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %18 "main" %17 + OpExecutionMode %18 OriginUpperLeft + OpSource ESSL 310 + OpName %18 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f + %9 = OpTypePointer Image %8 + %10 = OpTypeSampledImage %8 + %11 = OpTypeSampler + %12 = OpConstant %4 1 + %13 = OpConstant %4 2 + %14 = OpConstant %4 3 + %15 = OpConstant %4 4 + %16 = OpConstantSampler %11 None 0 Linear + %17 = OpVariable %9 Image + %18 = OpFunction %2 None %3 + %19 = OpLabel + %20 = OpLoad %8 %17 + %21 = OpSampledImage %10 %20 %16 + %22 = OpCompositeConstruct %5 %12 %13 + %23 = OpCompositeConstruct %6 %22 %14 + %24 = OpCompositeConstruct %7 %23 %15 + %25 = OpImageSampleImplicitLod %7 %21 %22 + %26 = OpImageSampleExplicitLod %7 %21 %23 Lod %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(25, SpvOpImageSampleImplicitLod, 0); + auto transformation = + TransformationAddImageSampleUnusedComponents(23, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(26, SpvOpImageSampleExplicitLod, 0); + transformation = + TransformationAddImageSampleUnusedComponents(24, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + OpCapability LiteralSampler + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %18 "main" %17 + OpExecutionMode %18 OriginUpperLeft + OpSource ESSL 310 + OpName %18 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeImage %4 2D 0 0 0 1 Rgba32f + %9 = OpTypePointer Image %8 + %10 = OpTypeSampledImage %8 + %11 = OpTypeSampler + %12 = OpConstant %4 1 + %13 = OpConstant %4 2 + %14 = OpConstant %4 3 + %15 = OpConstant %4 4 + %16 = OpConstantSampler %11 None 0 Linear + %17 = OpVariable %9 Image + %18 = OpFunction %2 None %3 + %19 = OpLabel + %20 = OpLoad %8 %17 + %21 = OpSampledImage %10 %20 %16 + %22 = OpCompositeConstruct %5 %12 %13 + %23 = OpCompositeConstruct %6 %22 %14 + %24 = OpCompositeConstruct %7 %23 %15 + %25 = OpImageSampleImplicitLod %7 %21 %23 + %26 = OpImageSampleExplicitLod %7 %21 %24 Lod %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_local_variable_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_local_variable_test.cpp new file mode 100644 index 0000000..ed57a28 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_local_variable_test.cpp @@ -0,0 +1,228 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_local_variable.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddLocalVariableTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 3 + %16 = OpTypeArray %13 %15 + %17 = OpTypeBool + %18 = OpTypeStruct %16 %7 %17 + %19 = OpTypePointer Function %18 + %21 = OpConstant %13 1 + %22 = OpConstant %13 2 + %23 = OpConstant %13 4 + %24 = OpConstantComposite %16 %21 %22 %23 + %25 = OpConstant %6 5 + %26 = OpConstant %6 6 + %27 = OpConstantComposite %7 %25 %26 + %28 = OpConstantFalse %17 + %29 = OpConstantComposite %18 %24 %27 %28 + %30 = OpTypeVector %13 2 + %31 = OpTypePointer Function %30 + %33 = OpConstantComposite %30 %21 %21 + %34 = OpTypeVector %17 3 + %35 = OpTypePointer Function %34 + %37 = OpConstantTrue %17 + %38 = OpConstantComposite %34 %37 %28 %28 + %39 = OpTypeVector %13 4 + %40 = OpTypeMatrix %39 3 + %41 = OpTypePointer Function %40 + %43 = OpConstantComposite %39 %21 %22 %23 %21 + %44 = OpConstantComposite %39 %22 %23 %21 %22 + %45 = OpConstantComposite %39 %23 %21 %22 %23 + %46 = OpConstantComposite %40 %43 %44 %45 + %50 = OpTypePointer Function %14 + %51 = OpConstantNull %14 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // A few cases of inapplicable transformations: + // Id 4 is already in use + ASSERT_FALSE(TransformationAddLocalVariable(4, 50, 4, 51, true) + .IsApplicable(context.get(), transformation_context)); + // Type mismatch between initializer and pointer + ASSERT_FALSE(TransformationAddLocalVariable(105, 46, 4, 51, true) + .IsApplicable(context.get(), transformation_context)); + // Id 5 is not a function + ASSERT_FALSE(TransformationAddLocalVariable(105, 50, 5, 51, true) + .IsApplicable(context.get(), transformation_context)); + + // %105 = OpVariable %50 Function %51 + { + TransformationAddLocalVariable transformation(105, 50, 4, 51, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + // %104 = OpVariable %41 Function %46 + { + TransformationAddLocalVariable transformation(104, 41, 4, 46, false); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + // %103 = OpVariable %35 Function %38 + { + TransformationAddLocalVariable transformation(103, 35, 4, 38, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + // %102 = OpVariable %31 Function %33 + { + TransformationAddLocalVariable transformation(102, 31, 4, 33, false); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + // %101 = OpVariable %19 Function %29 + { + TransformationAddLocalVariable transformation(101, 19, 4, 29, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + // %100 = OpVariable %8 Function %12 + { + TransformationAddLocalVariable transformation(100, 8, 4, 12, false); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(103)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(104)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(105)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeFloat 32 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 3 + %16 = OpTypeArray %13 %15 + %17 = OpTypeBool + %18 = OpTypeStruct %16 %7 %17 + %19 = OpTypePointer Function %18 + %21 = OpConstant %13 1 + %22 = OpConstant %13 2 + %23 = OpConstant %13 4 + %24 = OpConstantComposite %16 %21 %22 %23 + %25 = OpConstant %6 5 + %26 = OpConstant %6 6 + %27 = OpConstantComposite %7 %25 %26 + %28 = OpConstantFalse %17 + %29 = OpConstantComposite %18 %24 %27 %28 + %30 = OpTypeVector %13 2 + %31 = OpTypePointer Function %30 + %33 = OpConstantComposite %30 %21 %21 + %34 = OpTypeVector %17 3 + %35 = OpTypePointer Function %34 + %37 = OpConstantTrue %17 + %38 = OpConstantComposite %34 %37 %28 %28 + %39 = OpTypeVector %13 4 + %40 = OpTypeMatrix %39 3 + %41 = OpTypePointer Function %40 + %43 = OpConstantComposite %39 %21 %22 %23 %21 + %44 = OpConstantComposite %39 %22 %23 %21 %22 + %45 = OpConstantComposite %39 %23 %21 %22 %23 + %46 = OpConstantComposite %40 %43 %44 %45 + %50 = OpTypePointer Function %14 + %51 = OpConstantNull %14 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpVariable %8 Function %12 + %101 = OpVariable %19 Function %29 + %102 = OpVariable %31 Function %33 + %103 = OpVariable %35 Function %38 + %104 = OpVariable %41 Function %46 + %105 = OpVariable %50 Function %51 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_loop_preheader_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_loop_preheader_test.cpp new file mode 100644 index 0000000..1634494 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_loop_preheader_test.cpp @@ -0,0 +1,298 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_loop_preheader.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddLoopPreheaderTest, SimpleTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %11 None + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %10 %12 + %12 = OpLabel + OpLoopMerge %14 %13 None + OpBranch %13 + %13 = OpLabel + OpBranchConditional %7 %14 %12 + %15 = OpLabel + OpLoopMerge %17 %16 None + OpBranch %16 + %16 = OpLabel + OpBranchConditional %7 %15 %17 + %17 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %9 is not a loop header + ASSERT_FALSE(TransformationAddLoopPreheader(9, 15, {}).IsApplicable( + context.get(), transformation_context)); + + // The id %12 is not fresh + ASSERT_FALSE(TransformationAddLoopPreheader(10, 12, {}) + .IsApplicable(context.get(), transformation_context)); + + // Loop header %15 is not reachable (the only predecessor is the back-edge + // block) + ASSERT_FALSE(TransformationAddLoopPreheader(15, 100, {}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = TransformationAddLoopPreheader(10, 20, {}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + auto transformation2 = TransformationAddLoopPreheader(12, 21, {}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %20 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + OpBranch %10 + %10 = OpLabel + OpLoopMerge %21 %11 None + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %10 %21 + %21 = OpLabel + OpBranch %12 + %12 = OpLabel + OpLoopMerge %14 %13 None + OpBranch %13 + %13 = OpLabel + OpBranchConditional %7 %14 %12 + %15 = OpLabel + OpLoopMerge %17 %16 None + OpBranch %16 + %16 = OpLabel + OpBranchConditional %7 %15 %17 + %17 = OpLabel + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationAddLoopPreheaderTest, OpPhi) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpCopyObject %6 %7 + OpBranch %8 + %8 = OpLabel + %31 = OpPhi %6 %20 %5 %21 %9 + OpLoopMerge %10 %9 None + OpBranch %9 + %9 = OpLabel + %21 = OpCopyObject %6 %7 + OpBranchConditional %7 %8 %10 + %10 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %7 %11 %12 + %11 = OpLabel + %22 = OpCopyObject %6 %7 + OpBranch %13 + %12 = OpLabel + %23 = OpCopyObject %6 %7 + OpBranch %13 + %13 = OpLabel + %32 = OpPhi %6 %22 %11 %23 %12 %24 %14 + %33 = OpPhi %6 %7 %11 %7 %12 %24 %14 + OpLoopMerge %15 %14 None + OpBranch %14 + %14 = OpLabel + %24 = OpCopyObject %6 %7 + OpBranchConditional %7 %13 %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation1 = TransformationAddLoopPreheader(8, 40, {}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + // Not enough ids for the OpPhi instructions are given + ASSERT_FALSE(TransformationAddLoopPreheader(13, 41, {}) + .IsApplicable(context.get(), transformation_context)); + + // Not enough ids for the OpPhi instructions are given + ASSERT_FALSE(TransformationAddLoopPreheader(13, 41, {42}) + .IsApplicable(context.get(), transformation_context)); + + // One of the ids is not fresh + ASSERT_FALSE(TransformationAddLoopPreheader(13, 41, {31, 42}) + .IsApplicable(context.get(), transformation_context)); + + // One of the ids is repeated + ASSERT_FALSE(TransformationAddLoopPreheader(13, 41, {41, 42}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation2 = TransformationAddLoopPreheader(13, 41, {42, 43}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpCopyObject %6 %7 + OpBranch %40 + %40 = OpLabel + OpBranch %8 + %8 = OpLabel + %31 = OpPhi %6 %20 %40 %21 %9 + OpLoopMerge %10 %9 None + OpBranch %9 + %9 = OpLabel + %21 = OpCopyObject %6 %7 + OpBranchConditional %7 %8 %10 + %10 = OpLabel + OpSelectionMerge %41 None + OpBranchConditional %7 %11 %12 + %11 = OpLabel + %22 = OpCopyObject %6 %7 + OpBranch %41 + %12 = OpLabel + %23 = OpCopyObject %6 %7 + OpBranch %41 + %41 = OpLabel + %42 = OpPhi %6 %22 %11 %23 %12 + %43 = OpPhi %6 %7 %11 %7 %12 + OpBranch %13 + %13 = OpLabel + %32 = OpPhi %6 %42 %41 %24 %14 + %33 = OpPhi %6 %43 %41 %24 %14 + OpLoopMerge %15 %14 None + OpBranch %14 + %14 = OpLabel + %24 = OpCopyObject %6 %7 + OpBranchConditional %7 %13 %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_loop_to_create_int_constant_synonym_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_loop_to_create_int_constant_synonym_test.cpp new file mode 100644 index 0000000..d2d9b4d --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_loop_to_create_int_constant_synonym_test.cpp @@ -0,0 +1,1149 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_loop_to_create_int_constant_synonym.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddLoopToCreateIntConstantSynonymTest, + ConstantsNotSuitable) { + std::string shader = R"( + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %36 = OpTypeBool + %5 = OpTypeInt 32 1 + %6 = OpConstant %5 -1 + %7 = OpConstant %5 0 + %8 = OpConstant %5 1 + %9 = OpConstant %5 2 + %10 = OpConstant %5 5 + %11 = OpConstant %5 10 + %12 = OpConstant %5 20 + %13 = OpConstant %5 33 + %14 = OpTypeVector %5 2 + %15 = OpConstantComposite %14 %10 %11 + %16 = OpConstantComposite %14 %12 %12 + %17 = OpTypeVector %5 3 + %18 = OpConstantComposite %17 %11 %7 %11 + %19 = OpTypeInt 64 1 + %20 = OpConstant %19 0 + %21 = OpConstant %19 10 + %22 = OpTypeVector %19 2 + %23 = OpConstantComposite %22 %21 %20 + %24 = OpTypeFloat 32 + %25 = OpConstant %24 0 + %26 = OpConstant %24 5 + %27 = OpConstant %24 10 + %28 = OpConstant %24 20 + %29 = OpTypeVector %24 3 + %30 = OpConstantComposite %29 %26 %27 %26 + %31 = OpConstantComposite %29 %28 %28 %28 + %32 = OpConstantComposite %29 %27 %25 %27 + %2 = OpFunction %3 None %4 + %33 = OpLabel + %34 = OpCopyObject %5 %11 + OpBranch %35 + %35 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Reminder: the first four parameters of the constructor are the constants + // with values for C, I, S, N respectively. + + // %70 does not correspond to an id in the module. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 70, 12, 10, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %35 is not a constant. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 35, 12, 10, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %27, %28 and %26 are not integer constants, but scalar floats. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 27, 28, 26, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %32, %31 and %30 are not integer constants, but vector floats. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 32, 31, 30, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %18=(10, 0, 10) has 3 components, while %16=(20, 20) and %15=(5, 10) + // have 2. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 18, 16, 15, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %21 has bit width 64, while the width of %12 and %10 is 32. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 21, 12, 10, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %13 has component width 64, while the component width of %16 and %15 is 32. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 13, 16, 15, 9, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %21 (N) is a 64-bit integer, not 32-bit. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 7, 7, 7, 21, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %7 (N) has value 0, so N <= 0. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 7, 7, 7, 7, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %6 (N) has value -1, so N <= 1. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 7, 7, 7, 6, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // %13 (N) has value 33, so N > 32. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 7, 7, 7, 6, 13, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // C(%11)=10, I(%12)=20, S(%10)=5, N(%8)=1, so C=I-S*N does not hold, as + // 20-5*1=15. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 11, 12, 10, 8, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // C(%15)=(5, 10), I(%16)=(20, 20), S(%15)=(5, 10), N(%8)=1, so C=I-S*N does + // not hold, as (20, 20)-1*(5, 10) = (15, 10). + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 15, 16, 15, 8, 35, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddLoopToCreateIntConstantSynonymTest, + MissingConstantsOrBoolType) { + { + // The shader is missing the boolean type. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %20 = OpConstant %5 0 + %6 = OpConstant %5 1 + %7 = OpConstant %5 2 + %8 = OpConstant %5 5 + %9 = OpConstant %5 10 + %10 = OpConstant %5 20 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 9, 10, 8, 7, 12, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + } + { + // The shader is missing a 32-bit integer 0 constant. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %20 = OpTypeBool + %5 = OpTypeInt 32 1 + %6 = OpConstant %5 1 + %7 = OpConstant %5 2 + %8 = OpConstant %5 5 + %9 = OpConstant %5 10 + %10 = OpConstant %5 20 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 9, 10, 8, 7, 12, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + } + { + // The shader is missing a 32-bit integer 1 constant. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %20 = OpTypeBool + %5 = OpTypeInt 32 1 + %6 = OpConstant %5 0 + %7 = OpConstant %5 2 + %8 = OpConstant %5 5 + %9 = OpConstant %5 10 + %10 = OpConstant %5 20 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 9, 10, 8, 7, 12, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + } +} + +TEST(TransformationAddLoopToCreateIntConstantSynonymTest, Simple) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %7 5 + %12 = OpConstant %7 10 + %13 = OpConstant %7 20 + %2 = OpFunction %3 None %4 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + %22 = OpPhi %7 %12 %14 + OpSelectionMerge %16 None + OpBranchConditional %6 %17 %18 + %17 = OpLabel + %23 = OpPhi %7 %13 %15 + OpBranch %18 + %18 = OpLabel + OpBranch %16 + %16 = OpLabel + OpBranch %19 + %19 = OpLabel + OpLoopMerge %20 %19 None + OpBranchConditional %6 %20 %19 + %20 = OpLabel + OpBranch %21 + %21 = OpLabel + OpBranch %24 + %24 = OpLabel + OpLoopMerge %27 %25 None + OpBranch %25 + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpBranchConditional %6 %24 %27 + %27 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Block %14 has no predecessors. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 14, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // Block %18 has more than one predecessor. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 18, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // Block %16 is a merge block. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 16, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // Block %25 is a continue block. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 25, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // Block %19 has more than one predecessor. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 19, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // Block %20 is a merge block. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 20, 100, 101, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // Id %20 is supposed to be fresh, but it is not. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 15, 100, 20, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // Id %100 is used twice. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 15, 100, 100, 102, 103, 104, 105, 106, 107) + .IsApplicable(context.get(), transformation_context)); + + // Id %100 is used twice. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 15, 100, 101, 102, 103, 104, 105, 106, 100) + .IsApplicable(context.get(), transformation_context)); + + // Only the last id (for the additional block) is optional, so the other ones + // cannot be 0. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 15, 0, 101, 102, 103, 104, 105, 106, 100) + .IsApplicable(context.get(), transformation_context)); + + // This transformation will create a synonym of constant %12 from a 1-block + // loop. + auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 15, 100, 101, 102, 103, 104, 105, 106, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {}), MakeDataDescriptor(100, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // This transformation will create a synonym of constant %12 from a 2-block + // loop. + auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 17, 107, 108, 109, 110, 111, 112, 113, 114); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {}), MakeDataDescriptor(107, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // This transformation will create a synonym of constant %12 from a 2-block + // loop. + auto transformation3 = TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 26, 115, 116, 117, 118, 119, 120, 121, 0); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {}), MakeDataDescriptor(115, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %7 5 + %12 = OpConstant %7 10 + %13 = OpConstant %7 20 + %2 = OpFunction %3 None %4 + %14 = OpLabel + OpBranch %101 + %101 = OpLabel + %102 = OpPhi %7 %8 %14 %105 %101 + %103 = OpPhi %7 %13 %14 %104 %101 + %104 = OpISub %7 %103 %11 + %105 = OpIAdd %7 %102 %9 + %106 = OpSLessThan %5 %105 %10 + OpLoopMerge %15 %101 None + OpBranchConditional %106 %101 %15 + %15 = OpLabel + %100 = OpPhi %7 %104 %101 + %22 = OpPhi %7 %12 %101 + OpSelectionMerge %16 None + OpBranchConditional %6 %108 %18 + %108 = OpLabel + %109 = OpPhi %7 %8 %15 %112 %114 + %110 = OpPhi %7 %13 %15 %111 %114 + OpLoopMerge %17 %114 None + OpBranch %114 + %114 = OpLabel + %111 = OpISub %7 %110 %11 + %112 = OpIAdd %7 %109 %9 + %113 = OpSLessThan %5 %112 %10 + OpBranchConditional %113 %108 %17 + %17 = OpLabel + %107 = OpPhi %7 %111 %114 + %23 = OpPhi %7 %13 %114 + OpBranch %18 + %18 = OpLabel + OpBranch %16 + %16 = OpLabel + OpBranch %19 + %19 = OpLabel + OpLoopMerge %20 %19 None + OpBranchConditional %6 %20 %19 + %20 = OpLabel + OpBranch %21 + %21 = OpLabel + OpBranch %24 + %24 = OpLabel + OpLoopMerge %27 %25 None + OpBranch %25 + %25 = OpLabel + OpBranch %116 + %116 = OpLabel + %117 = OpPhi %7 %8 %25 %120 %116 + %118 = OpPhi %7 %13 %25 %119 %116 + %119 = OpISub %7 %118 %11 + %120 = OpIAdd %7 %117 %9 + %121 = OpSLessThan %5 %120 %10 + OpLoopMerge %26 %116 None + OpBranchConditional %121 %116 %26 + %26 = OpLabel + %115 = OpPhi %7 %119 %116 + OpBranchConditional %6 %24 %27 + %27 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationAddLoopToCreateIntConstantSynonymTest, + DifferentSignednessAndVectors) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %7 5 + %12 = OpConstant %7 10 + %13 = OpConstant %7 20 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpConstant %14 5 + %17 = OpConstant %14 10 + %18 = OpConstant %14 20 + %19 = OpTypeVector %7 2 + %20 = OpTypeVector %14 2 + %21 = OpConstantComposite %19 %12 %8 + %22 = OpConstantComposite %20 %17 %15 + %23 = OpConstantComposite %19 %13 %12 + %24 = OpConstantComposite %19 %11 %11 + %2 = OpFunction %3 None %4 + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpBranch %27 + %27 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranch %29 + %29 = OpLabel + OpBranch %30 + %30 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // These tests check that the transformation is applicable and is applied + // correctly with integers, scalar and vectors, of different signedness. + + // %12 is a signed integer, %18 and %16 are unsigned integers. + auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym( + 12, 18, 16, 10, 26, 100, 101, 102, 103, 104, 105, 106, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {}), MakeDataDescriptor(100, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %12 and %11 are signed integers, %18 is an unsigned integer. + auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym( + 12, 18, 11, 10, 27, 108, 109, 110, 111, 112, 113, 114, 0); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {}), MakeDataDescriptor(108, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %17, %18 and %16 are all signed integers. + auto transformation3 = TransformationAddLoopToCreateIntConstantSynonym( + 17, 18, 16, 10, 28, 115, 116, 117, 118, 119, 120, 121, 0); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(115, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %22 is an unsigned integer vector, %23 and %24 are signed integer vectors. + auto transformation4 = TransformationAddLoopToCreateIntConstantSynonym( + 22, 23, 24, 10, 29, 122, 123, 124, 125, 126, 127, 128, 0); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(22, {}), MakeDataDescriptor(122, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %21, %23 and %24 are all signed integer vectors. + auto transformation5 = TransformationAddLoopToCreateIntConstantSynonym( + 21, 23, 24, 10, 30, 129, 130, 131, 132, 133, 134, 135, 0); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(129, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %7 5 + %12 = OpConstant %7 10 + %13 = OpConstant %7 20 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpConstant %14 5 + %17 = OpConstant %14 10 + %18 = OpConstant %14 20 + %19 = OpTypeVector %7 2 + %20 = OpTypeVector %14 2 + %21 = OpConstantComposite %19 %12 %8 + %22 = OpConstantComposite %20 %17 %15 + %23 = OpConstantComposite %19 %13 %12 + %24 = OpConstantComposite %19 %11 %11 + %2 = OpFunction %3 None %4 + %25 = OpLabel + OpBranch %101 + %101 = OpLabel + %102 = OpPhi %7 %8 %25 %105 %101 + %103 = OpPhi %14 %18 %25 %104 %101 + %104 = OpISub %14 %103 %16 + %105 = OpIAdd %7 %102 %9 + %106 = OpSLessThan %5 %105 %10 + OpLoopMerge %26 %101 None + OpBranchConditional %106 %101 %26 + %26 = OpLabel + %100 = OpPhi %14 %104 %101 + OpBranch %109 + %109 = OpLabel + %110 = OpPhi %7 %8 %26 %113 %109 + %111 = OpPhi %14 %18 %26 %112 %109 + %112 = OpISub %14 %111 %11 + %113 = OpIAdd %7 %110 %9 + %114 = OpSLessThan %5 %113 %10 + OpLoopMerge %27 %109 None + OpBranchConditional %114 %109 %27 + %27 = OpLabel + %108 = OpPhi %14 %112 %109 + OpBranch %116 + %116 = OpLabel + %117 = OpPhi %7 %8 %27 %120 %116 + %118 = OpPhi %14 %18 %27 %119 %116 + %119 = OpISub %14 %118 %16 + %120 = OpIAdd %7 %117 %9 + %121 = OpSLessThan %5 %120 %10 + OpLoopMerge %28 %116 None + OpBranchConditional %121 %116 %28 + %28 = OpLabel + %115 = OpPhi %14 %119 %116 + OpBranch %123 + %123 = OpLabel + %124 = OpPhi %7 %8 %28 %127 %123 + %125 = OpPhi %19 %23 %28 %126 %123 + %126 = OpISub %19 %125 %24 + %127 = OpIAdd %7 %124 %9 + %128 = OpSLessThan %5 %127 %10 + OpLoopMerge %29 %123 None + OpBranchConditional %128 %123 %29 + %29 = OpLabel + %122 = OpPhi %19 %126 %123 + OpBranch %130 + %130 = OpLabel + %131 = OpPhi %7 %8 %29 %134 %130 + %132 = OpPhi %19 %23 %29 %133 %130 + %133 = OpISub %19 %132 %24 + %134 = OpIAdd %7 %131 %9 + %135 = OpSLessThan %5 %134 %10 + OpLoopMerge %30 %130 None + OpBranchConditional %135 %130 %30 + %30 = OpLabel + %129 = OpPhi %19 %133 %130 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationAddLoopToCreateIntConstantSynonymTest, 64BitConstants) { + std::string shader = R"( + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpTypeInt 64 1 + %12 = OpConstant %11 5 + %13 = OpConstant %11 10 + %14 = OpConstant %11 20 + %15 = OpTypeVector %11 2 + %16 = OpConstantComposite %15 %13 %13 + %17 = OpConstantComposite %15 %14 %14 + %18 = OpConstantComposite %15 %12 %12 + %2 = OpFunction %3 None %4 + %19 = OpLabel + OpBranch %20 + %20 = OpLabel + OpBranch %21 + %21 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // These tests check that the transformation can be applied, and is applied + // correctly, to 64-bit integer (scalar and vector) constants. + + // 64-bit scalar integers. + auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym( + 13, 14, 12, 10, 20, 100, 101, 102, 103, 104, 105, 106, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(13, {}), MakeDataDescriptor(100, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // 64-bit vector integers. + auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym( + 16, 17, 18, 10, 21, 107, 108, 109, 110, 111, 112, 113, 0); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(16, {}), MakeDataDescriptor(107, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpTypeInt 64 1 + %12 = OpConstant %11 5 + %13 = OpConstant %11 10 + %14 = OpConstant %11 20 + %15 = OpTypeVector %11 2 + %16 = OpConstantComposite %15 %13 %13 + %17 = OpConstantComposite %15 %14 %14 + %18 = OpConstantComposite %15 %12 %12 + %2 = OpFunction %3 None %4 + %19 = OpLabel + OpBranch %101 + %101 = OpLabel + %102 = OpPhi %7 %8 %19 %105 %101 + %103 = OpPhi %11 %14 %19 %104 %101 + %104 = OpISub %11 %103 %12 + %105 = OpIAdd %7 %102 %9 + %106 = OpSLessThan %5 %105 %10 + OpLoopMerge %20 %101 None + OpBranchConditional %106 %101 %20 + %20 = OpLabel + %100 = OpPhi %11 %104 %101 + OpBranch %108 + %108 = OpLabel + %109 = OpPhi %7 %8 %20 %112 %108 + %110 = OpPhi %15 %17 %20 %111 %108 + %111 = OpISub %15 %110 %18 + %112 = OpIAdd %7 %109 %9 + %113 = OpSLessThan %5 %112 %10 + OpLoopMerge %21 %108 None + OpBranchConditional %113 %108 %21 + %21 = OpLabel + %107 = OpPhi %15 %111 %108 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationAddLoopToCreateIntConstantSynonymTest, Underflow) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %7 20 + %12 = OpConstant %7 -4 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 214748365 + %15 = OpConstant %13 4294967256 + %2 = OpFunction %3 None %4 + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranch %18 + %18 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // These tests check that underflows are taken into consideration when + // deciding if transformation is applicable. + + // Subtracting 2147483648 20 times from 32-bit integer 0 underflows 2 times + // and the result is equivalent to -4. + auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym( + 12, 8, 14, 11, 17, 100, 101, 102, 103, 104, 105, 106, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {}), MakeDataDescriptor(100, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Subtracting 20 twice from 0 underflows and gives the unsigned integer + // 4294967256. + auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym( + 15, 8, 11, 10, 18, 107, 108, 109, 110, 111, 112, 113, 0); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(107, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %7 20 + %12 = OpConstant %7 -4 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 214748365 + %15 = OpConstant %13 4294967256 + %2 = OpFunction %3 None %4 + %16 = OpLabel + OpBranch %101 + %101 = OpLabel + %102 = OpPhi %7 %8 %16 %105 %101 + %103 = OpPhi %7 %8 %16 %104 %101 + %104 = OpISub %7 %103 %14 + %105 = OpIAdd %7 %102 %9 + %106 = OpSLessThan %5 %105 %11 + OpLoopMerge %17 %101 None + OpBranchConditional %106 %101 %17 + %17 = OpLabel + %100 = OpPhi %7 %104 %101 + OpBranch %108 + %108 = OpLabel + %109 = OpPhi %7 %8 %17 %112 %108 + %110 = OpPhi %7 %8 %17 %111 %108 + %111 = OpISub %7 %110 %11 + %112 = OpIAdd %7 %109 %9 + %113 = OpSLessThan %5 %112 %10 + OpLoopMerge %18 %108 None + OpBranchConditional %113 %108 %18 + %18 = OpLabel + %107 = OpPhi %7 %111 %108 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationAddLoopToCreateIntConstantSynonymTest, + InapplicableDueToDeadBlockOrIrrelevantId) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %7 5 + %12 = OpConstant %7 10 + %13 = OpConstant %7 20 + %1010 = OpConstant %7 2 + %1011 = OpConstant %7 5 + %1012 = OpConstant %7 10 + %1013 = OpConstant %7 20 + %2 = OpFunction %3 None %4 + %14 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %6 %16 %15 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactBlockIsDead(15); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(1010); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(1011); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(1012); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(1013); + // Bad because the block before which the loop would be inserted is dead. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 15, 100, 101, 102, 103, 104, 105, 106, 0) + .IsApplicable(context.get(), transformation_context)); + // OK + ASSERT_TRUE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 10, 17, 100, 101, 102, 103, 104, 105, 106, 0) + .IsApplicable(context.get(), transformation_context)); + // Bad because in each case one of the constants involved is irrelevant. + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 1012, 13, 11, 10, 17, 100, 101, 102, 103, 104, 105, 106, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 1013, 11, 10, 17, 100, 101, 102, 103, 104, 105, 106, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 1011, 10, 17, 100, 101, 102, 103, 104, 105, 106, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddLoopToCreateIntConstantSynonym( + 12, 13, 11, 1010, 17, 100, 101, 102, 103, 104, 105, 106, 0) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddLoopToCreateIntConstantSynonymTest, InserBeforeOpSwitch) { + // Checks that it is acceptable for a loop to be added before a target of an + // OpSwitch instruction. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + %20 = OpConstant %6 1 + %21 = OpConstant %6 2 + %22 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpSwitch %7 %9 0 %8 + %9 = OpLabel + OpBranch %10 + %8 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation1 = TransformationAddLoopToCreateIntConstantSynonym( + 20, 21, 20, 20, 9, 100, 101, 102, 103, 104, 105, 106, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(100, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation2 = TransformationAddLoopToCreateIntConstantSynonym( + 20, 21, 20, 20, 8, 200, 201, 202, 203, 204, 205, 206, 0); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(200, {}))); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + %20 = OpConstant %6 1 + %21 = OpConstant %6 2 + %22 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpSwitch %7 %101 0 %201 + %101 = OpLabel + %102 = OpPhi %6 %7 %5 %105 %101 + %103 = OpPhi %6 %21 %5 %104 %101 + %104 = OpISub %6 %103 %20 + %105 = OpIAdd %6 %102 %20 + %106 = OpSLessThan %22 %105 %20 + OpLoopMerge %9 %101 None + OpBranchConditional %106 %101 %9 + %9 = OpLabel + %100 = OpPhi %6 %104 %101 + OpBranch %10 + %201 = OpLabel + %202 = OpPhi %6 %7 %5 %205 %201 + %203 = OpPhi %6 %21 %5 %204 %201 + %204 = OpISub %6 %203 %20 + %205 = OpIAdd %6 %202 %20 + %206 = OpSLessThan %22 %205 %20 + OpLoopMerge %8 %201 None + OpBranchConditional %206 %201 %8 + %8 = OpLabel + %200 = OpPhi %6 %204 %201 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp new file mode 100644 index 0000000..4fc9d2d --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_no_contraction_decoration_test.cpp @@ -0,0 +1,202 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_no_contraction_decoration.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddNoContractionDecorationTest, BasicScenarios) { + // This is a simple transformation and this test handles the main cases. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpName %14 "i" + OpDecorate %32 NoContraction + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstant %6 2 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 0 + %22 = OpConstant %12 10 + %23 = OpTypeBool + %31 = OpConstant %6 3.5999999 + %38 = OpConstant %12 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %21 = OpLoad %12 %14 + %24 = OpSLessThan %23 %21 %22 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + %25 = OpLoad %6 %10 + %26 = OpLoad %6 %10 + %27 = OpFMul %6 %25 %26 + %28 = OpLoad %6 %8 + %29 = OpFAdd %6 %28 %27 + OpStore %8 %29 + %30 = OpLoad %6 %10 + %32 = OpFDiv %6 %30 %31 + OpStore %10 %32 + %33 = OpLoad %12 %14 + %34 = OpConvertSToF %6 %33 + %35 = OpLoad %6 %8 + %36 = OpFAdd %6 %35 %34 + OpStore %8 %36 + OpBranch %19 + %19 = OpLabel + %37 = OpLoad %12 %14 + %39 = OpIAdd %12 %37 %38 + OpStore %14 %39 + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Invalid: 200 is not an id + ASSERT_FALSE(TransformationAddNoContractionDecoration(200).IsApplicable( + context.get(), transformation_context)); + // Invalid: 17 is a block id + ASSERT_FALSE(TransformationAddNoContractionDecoration(17).IsApplicable( + context.get(), transformation_context)); + // Invalid: 24 is not arithmetic + ASSERT_FALSE(TransformationAddNoContractionDecoration(24).IsApplicable( + context.get(), transformation_context)); + + // It is valid to add NoContraction to each of these ids (and it's fine to + // have duplicates of the decoration, in the case of 32). + for (uint32_t result_id : {32u, 32u, 27u, 29u, 39u}) { + TransformationAddNoContractionDecoration transformation(result_id); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpName %14 "i" + OpDecorate %32 NoContraction + OpDecorate %32 NoContraction + OpDecorate %32 NoContraction + OpDecorate %27 NoContraction + OpDecorate %29 NoContraction + OpDecorate %39 NoContraction + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstant %6 2 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 0 + %22 = OpConstant %12 10 + %23 = OpTypeBool + %31 = OpConstant %6 3.5999999 + %38 = OpConstant %12 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + %21 = OpLoad %12 %14 + %24 = OpSLessThan %23 %21 %22 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + %25 = OpLoad %6 %10 + %26 = OpLoad %6 %10 + %27 = OpFMul %6 %25 %26 + %28 = OpLoad %6 %8 + %29 = OpFAdd %6 %28 %27 + OpStore %8 %29 + %30 = OpLoad %6 %10 + %32 = OpFDiv %6 %30 %31 + OpStore %10 %32 + %33 = OpLoad %12 %14 + %34 = OpConvertSToF %6 %33 + %35 = OpLoad %6 %8 + %36 = OpFAdd %6 %35 %34 + OpStore %8 %36 + OpBranch %19 + %19 = OpLabel + %37 = OpLoad %12 %14 + %39 = OpIAdd %12 %37 %38 + OpStore %14 %39 + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_opphi_synonym_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_opphi_synonym_test.cpp new file mode 100644 index 0000000..3501f8e --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_opphi_synonym_test.cpp @@ -0,0 +1,488 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_opphi_synonym.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +protobufs::Fact MakeSynonymFact(uint32_t first, uint32_t second) { + protobufs::FactDataSynonym data_synonym_fact; + *data_synonym_fact.mutable_data1() = MakeDataDescriptor(first, {}); + *data_synonym_fact.mutable_data2() = MakeDataDescriptor(second, {}); + protobufs::Fact result; + *result.mutable_data_synonym_fact() = data_synonym_fact; + return result; +} + +// Adds synonym facts to the fact manager. +void SetUpIdSynonyms(FactManager* fact_manager) { + fact_manager->MaybeAddFact(MakeSynonymFact(11, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(13, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(14, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(19, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(20, 9)); + fact_manager->MaybeAddFact(MakeSynonymFact(10, 21)); +} + +TEST(TransformationAddOpPhiSynonymTest, Inapplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %22 = OpTypePointer Function %7 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %8 1 + %2 = OpFunction %3 None %4 + %12 = OpLabel + %23 = OpVariable %22 Function + %13 = OpCopyObject %7 %9 + %14 = OpCopyObject %8 %11 + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %6 %17 %18 + %17 = OpLabel + %19 = OpCopyObject %7 %13 + %20 = OpCopyObject %8 %14 + %21 = OpCopyObject %7 %10 + OpBranch %16 + %18 = OpLabel + %24 = OpCopyObject %22 %23 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + SetUpIdSynonyms(transformation_context.GetFactManager()); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(23, 24)); + + // %13 is not a block label. + ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // Block %12 does not have a predecessor. + ASSERT_FALSE(TransformationAddOpPhiSynonym(12, {{}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // Not all predecessors of %16 (%17 and %18) are considered in the map. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // %30 does not exist in the module. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{30, 19}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // %20 is not a block label. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{20, 19}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // %15 is not the id of one of the predecessors of the block. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{15, 19}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // %30 does not exist in the module. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 30}, {18, 13}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // %19 and %10 are not synonymous. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 10}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // %19 and %14 do not have the same type. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 14}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // %19 is not available at the end of %18. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 9}, {18, 19}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // %21 is not a fresh id. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 9}, {18, 9}}}, 21) + .IsApplicable(context.get(), transformation_context)); + + // %23 and %24 have pointer id. + ASSERT_FALSE(TransformationAddOpPhiSynonym(16, {{{17, 23}, {18, 24}}}, 100) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddOpPhiSynonymTest, Apply) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %8 1 + %2 = OpFunction %3 None %4 + %12 = OpLabel + %13 = OpCopyObject %7 %9 + %14 = OpCopyObject %8 %11 + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %6 %17 %18 + %17 = OpLabel + %19 = OpCopyObject %7 %13 + %20 = OpCopyObject %8 %14 + %21 = OpCopyObject %7 %10 + OpBranch %16 + %18 = OpLabel + OpBranch %16 + %16 = OpLabel + OpBranch %22 + %22 = OpLabel + OpLoopMerge %23 %24 None + OpBranchConditional %6 %25 %23 + %25 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %6 %27 %26 + %27 = OpLabel + %28 = OpCopyObject %7 %13 + OpBranch %23 + %26 = OpLabel + OpSelectionMerge %29 None + OpBranchConditional %6 %29 %24 + %29 = OpLabel + %30 = OpCopyObject %7 %13 + OpBranch %23 + %24 = OpLabel + OpBranch %22 + %23 = OpLabel + OpSelectionMerge %31 None + OpBranchConditional %6 %31 %31 + %31 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + SetUpIdSynonyms(transformation_context.GetFactManager()); + + // Add some further synonym facts. + transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(28, 9)); + transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(30, 9)); + + auto transformation1 = TransformationAddOpPhiSynonym(17, {{{15, 13}}}, 100); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {}), MakeDataDescriptor(9, {}))); + + auto transformation2 = + TransformationAddOpPhiSynonym(16, {{{17, 19}, {18, 13}}}, 101); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(101, {}), MakeDataDescriptor(9, {}))); + + auto transformation3 = + TransformationAddOpPhiSynonym(23, {{{22, 13}, {27, 28}, {29, 30}}}, 102); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {}), MakeDataDescriptor(9, {}))); + + auto transformation4 = TransformationAddOpPhiSynonym(31, {{{23, 13}}}, 103); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(103, {}), MakeDataDescriptor(9, {}))); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpConstant %7 1 + %10 = OpConstant %7 2 + %11 = OpConstant %8 1 + %2 = OpFunction %3 None %4 + %12 = OpLabel + %13 = OpCopyObject %7 %9 + %14 = OpCopyObject %8 %11 + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %6 %17 %18 + %17 = OpLabel + %100 = OpPhi %7 %13 %15 + %19 = OpCopyObject %7 %13 + %20 = OpCopyObject %8 %14 + %21 = OpCopyObject %7 %10 + OpBranch %16 + %18 = OpLabel + OpBranch %16 + %16 = OpLabel + %101 = OpPhi %7 %19 %17 %13 %18 + OpBranch %22 + %22 = OpLabel + OpLoopMerge %23 %24 None + OpBranchConditional %6 %25 %23 + %25 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %6 %27 %26 + %27 = OpLabel + %28 = OpCopyObject %7 %13 + OpBranch %23 + %26 = OpLabel + OpSelectionMerge %29 None + OpBranchConditional %6 %29 %24 + %29 = OpLabel + %30 = OpCopyObject %7 %13 + OpBranch %23 + %24 = OpLabel + OpBranch %22 + %23 = OpLabel + %102 = OpPhi %7 %13 %22 %28 %27 %30 %29 + OpSelectionMerge %31 None + OpBranchConditional %6 %31 %31 + %31 = OpLabel + %103 = OpPhi %7 %13 %23 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationAddOpPhiSynonymTest, VariablePointers) { + std::string shader = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpTypePointer Workgroup %8 + %3 = OpVariable %10 Workgroup + %2 = OpFunction %4 None %5 + %11 = OpLabel + %12 = OpVariable %9 Function + OpSelectionMerge %13 None + OpBranchConditional %7 %14 %13 + %14 = OpLabel + %15 = OpCopyObject %10 %3 + %16 = OpCopyObject %9 %12 + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Declare synonyms + transformation_context.GetFactManager()->MaybeAddFact(MakeSynonymFact(3, 15)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(12, 16)); + + // Remove the VariablePointers capability. + context.get()->get_feature_mgr()->RemoveCapability( + SpvCapabilityVariablePointers); + + // The VariablePointers capability is required to add an OpPhi instruction of + // pointer type. + ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{{11, 3}, {14, 15}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + // Add the VariablePointers capability back. + context.get()->get_feature_mgr()->AddCapability( + SpvCapabilityVariablePointers); + + // If the ids have pointer type, the storage class must be Workgroup or + // StorageBuffer, but it is Function in this case. + ASSERT_FALSE(TransformationAddOpPhiSynonym(13, {{{11, 12}, {14, 16}}}, 100) + .IsApplicable(context.get(), transformation_context)); + + auto transformation = + TransformationAddOpPhiSynonym(13, {{{11, 3}, {14, 15}}}, 100); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string after_transformation = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpTypePointer Workgroup %8 + %3 = OpVariable %10 Workgroup + %2 = OpFunction %4 None %5 + %11 = OpLabel + %12 = OpVariable %9 Function + OpSelectionMerge %13 None + OpBranchConditional %7 %14 %13 + %14 = OpLabel + %15 = OpCopyObject %10 %3 + %16 = OpCopyObject %9 %12 + OpBranch %13 + %13 = OpLabel + %100 = OpPhi %10 %3 %11 %15 %14 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddOpPhiSynonymTest, DeadBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %15 = OpConstant %6 0 + %50 = OpConstant %6 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpSelectionMerge %13 None + OpBranchConditional %11 %12 %13 + %12 = OpLabel + %14 = OpLoad %6 %8 + %16 = OpIEqual %10 %14 %15 + OpSelectionMerge %18 None + OpBranchConditional %16 %17 %40 + %17 = OpLabel + OpBranch %18 + %40 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Dead blocks + transformation_context.GetFactManager()->AddFactBlockIsDead(12); + transformation_context.GetFactManager()->AddFactBlockIsDead(17); + transformation_context.GetFactManager()->AddFactBlockIsDead(18); + + // Declare synonym + ASSERT_TRUE(transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(15, 50))); + + // Bad because the block 18 is dead. + ASSERT_FALSE(TransformationAddOpPhiSynonym(18, {{{17, 15}, {40, 50}}}, 100) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_parameter_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_parameter_test.cpp new file mode 100644 index 0000000..7b2a15f --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_parameter_test.cpp @@ -0,0 +1,1106 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_parameter.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddParameterTest, NonPointerBasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %11 = OpTypeInt 32 1 + %16 = OpTypeFloat 32 + %3 = OpTypeFunction %2 + %6 = OpTypeFunction %7 %7 + %8 = OpConstant %11 23 + %12 = OpConstantTrue %7 + %15 = OpTypeFunction %2 %16 + %24 = OpTypeFunction %2 %16 %7 + %31 = OpTypeStruct %7 %11 + %32 = OpConstant %16 23 + %33 = OpConstantComposite %31 %12 %8 + %41 = OpTypeStruct %11 %16 + %42 = OpConstantComposite %41 %8 %32 + %43 = OpTypeFunction %2 %41 + %44 = OpTypeFunction %2 %41 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpFunctionCall %7 %9 %12 + OpReturn + OpFunctionEnd + + ; adjust type of the function in-place + %9 = OpFunction %7 None %6 + %14 = OpFunctionParameter %7 + %10 = OpLabel + OpReturnValue %12 + OpFunctionEnd + + ; reuse an existing function type + %17 = OpFunction %2 None %15 + %18 = OpFunctionParameter %16 + %19 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %15 + %21 = OpFunctionParameter %16 + %22 = OpLabel + OpReturn + OpFunctionEnd + %25 = OpFunction %2 None %24 + %26 = OpFunctionParameter %16 + %27 = OpFunctionParameter %7 + %28 = OpLabel + OpReturn + OpFunctionEnd + + ; create a new function type + %29 = OpFunction %2 None %3 + %30 = OpLabel + OpReturn + OpFunctionEnd + + ; don't adjust the type of the function if it creates a duplicate + %34 = OpFunction %2 None %43 + %35 = OpFunctionParameter %41 + %36 = OpLabel + OpReturn + OpFunctionEnd + %37 = OpFunction %2 None %44 + %38 = OpFunctionParameter %41 + %39 = OpFunctionParameter %7 + %40 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Can't modify entry point function. + ASSERT_FALSE(TransformationAddParameter(4, 60, 7, {{}}, 61) + .IsApplicable(context.get(), transformation_context)); + + // There is no function with result id 60. + ASSERT_FALSE(TransformationAddParameter(60, 60, 11, {{}}, 61) + .IsApplicable(context.get(), transformation_context)); + + // Parameter id is not fresh. + ASSERT_FALSE(TransformationAddParameter(9, 14, 11, {{{13, 8}}}, 61) + .IsApplicable(context.get(), transformation_context)); + + // Function type id is not fresh. + ASSERT_FALSE(TransformationAddParameter(9, 60, 11, {{{13, 8}}}, 14) + .IsApplicable(context.get(), transformation_context)); + + // Function type id and parameter type id are equal. + ASSERT_FALSE(TransformationAddParameter(9, 60, 11, {{{13, 8}}}, 60) + .IsApplicable(context.get(), transformation_context)); + + // Parameter's initializer doesn't exist. + ASSERT_FALSE(TransformationAddParameter(9, 60, 11, {{{13, 60}}}, 61) + .IsApplicable(context.get(), transformation_context)); + + // Correct transformations. + { + TransformationAddParameter correct(9, 60, 11, {{{13, 8}}}, 61); + ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(60)); + } + { + TransformationAddParameter correct(17, 62, 7, {{}}, 63); + ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(62)); + } + { + TransformationAddParameter correct(29, 64, 31, {{}}, 65); + ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(64)); + } + { + TransformationAddParameter correct(34, 66, 7, {{}}, 67); + ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(66)); + } + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %11 = OpTypeInt 32 1 + %16 = OpTypeFloat 32 + %3 = OpTypeFunction %2 + %8 = OpConstant %11 23 + %12 = OpConstantTrue %7 + %15 = OpTypeFunction %2 %16 + %24 = OpTypeFunction %2 %16 %7 + %31 = OpTypeStruct %7 %11 + %32 = OpConstant %16 23 + %33 = OpConstantComposite %31 %12 %8 + %41 = OpTypeStruct %11 %16 + %42 = OpConstantComposite %41 %8 %32 + %44 = OpTypeFunction %2 %41 %7 + %6 = OpTypeFunction %7 %7 %11 + %65 = OpTypeFunction %2 %31 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpFunctionCall %7 %9 %12 %8 + OpReturn + OpFunctionEnd + + ; adjust type of the function in-place + %9 = OpFunction %7 None %6 + %14 = OpFunctionParameter %7 + %60 = OpFunctionParameter %11 + %10 = OpLabel + OpReturnValue %12 + OpFunctionEnd + + ; reuse an existing function type + %17 = OpFunction %2 None %24 + %18 = OpFunctionParameter %16 + %62 = OpFunctionParameter %7 + %19 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %15 + %21 = OpFunctionParameter %16 + %22 = OpLabel + OpReturn + OpFunctionEnd + %25 = OpFunction %2 None %24 + %26 = OpFunctionParameter %16 + %27 = OpFunctionParameter %7 + %28 = OpLabel + OpReturn + OpFunctionEnd + + ; create a new function type + %29 = OpFunction %2 None %65 + %64 = OpFunctionParameter %31 + %30 = OpLabel + OpReturn + OpFunctionEnd + + ; don't adjust the type of the function if it creates a duplicate + %34 = OpFunction %2 None %44 + %35 = OpFunctionParameter %41 + %66 = OpFunctionParameter %7 + %36 = OpLabel + OpReturn + OpFunctionEnd + %37 = OpFunction %2 None %44 + %38 = OpFunctionParameter %41 + %39 = OpFunctionParameter %7 + %40 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationAddParameterTest, NonPointerNotApplicableTest) { + // This types handles case of adding a new parameter of a non-pointer type + // where the transformation is not applicable. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun1(" + OpName %12 "fun2(i1;" + OpName %11 "a" + OpName %14 "fun3(" + OpName %24 "f1" + OpName %27 "f2" + OpName %30 "i1" + OpName %31 "i2" + OpName %32 "param" + OpName %35 "i3" + OpName %36 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %9 + %18 = OpConstant %8 2 + %22 = OpTypeFloat 32 + %23 = OpTypePointer Private %22 + %24 = OpVariable %23 Private + %25 = OpConstant %22 1 + %26 = OpTypePointer Function %22 + %28 = OpConstant %22 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %27 = OpVariable %26 Function + %30 = OpVariable %9 Function + %31 = OpVariable %9 Function + %32 = OpVariable %9 Function + %35 = OpVariable %9 Function + %36 = OpVariable %9 Function + OpStore %24 %25 + OpStore %27 %28 + %29 = OpFunctionCall %2 %6 + OpStore %30 %18 + %33 = OpLoad %8 %30 + OpStore %32 %33 + %34 = OpFunctionCall %8 %12 %32 + OpStore %31 %34 + %37 = OpLoad %8 %31 + OpStore %36 %37 + %38 = OpFunctionCall %8 %12 %36 + OpStore %35 %38 + ; %39 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %8 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %17 = OpLoad %8 %11 + %19 = OpIAdd %8 %17 %18 + OpReturnValue %19 + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: Id 19 is not available in the caller that has id 34. + TransformationAddParameter transformation_bad_1(12, 50, 8, + {{{34, 19}, {38, 19}}}, 51); + + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: Id 8 does not have a type. + TransformationAddParameter transformation_bad_2(12, 50, 8, + {{{34, 8}, {38, 8}}}, 51); + + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: Types of id 25 and id 18 are different. + TransformationAddParameter transformation_bad_3(12, 50, 22, + {{{34, 25}, {38, 18}}}, 51); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); + + // Function with id 14 does not have any callers. + // Bad: Id 18 is not a vaild type. + TransformationAddParameter transformation_bad_4(14, 50, 18, {{}}, 51); + ASSERT_FALSE( + transformation_bad_4.IsApplicable(context.get(), transformation_context)); + + // Function with id 14 does not have any callers. + // Bad: Id 3 refers to OpTypeVoid, which is not supported. + TransformationAddParameter transformation_bad_6(14, 50, 3, {{}}, 51); + ASSERT_FALSE( + transformation_bad_6.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddParameterTest, PointerFunctionTest) { + // This types handles case of adding a new parameter of a pointer type with + // storage class Function. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun1(" + OpName %12 "fun2(i1;" + OpName %11 "a" + OpName %14 "fun3(" + OpName %17 "s" + OpName %24 "s" + OpName %28 "f1" + OpName %31 "f2" + OpName %34 "i1" + OpName %35 "i2" + OpName %36 "param" + OpName %39 "i3" + OpName %40 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %9 + %20 = OpConstant %8 2 + %25 = OpConstant %8 0 + %26 = OpTypeFloat 32 + %27 = OpTypePointer Private %26 + %28 = OpVariable %27 Private + %60 = OpTypePointer Output %26 + %61 = OpVariable %60 Output + %29 = OpConstant %26 1 + %30 = OpTypePointer Function %26 + %32 = OpConstant %26 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %31 = OpVariable %30 Function + %34 = OpVariable %9 Function + %35 = OpVariable %9 Function + %36 = OpVariable %9 Function + %39 = OpVariable %9 Function + %40 = OpVariable %9 Function + OpStore %28 %29 + OpStore %31 %32 + %33 = OpFunctionCall %2 %6 + OpStore %34 %20 + %37 = OpLoad %8 %34 + OpStore %36 %37 + %38 = OpFunctionCall %8 %12 %36 + OpStore %35 %38 + %41 = OpLoad %8 %35 + OpStore %40 %41 + %42 = OpFunctionCall %8 %12 %40 + OpStore %39 %42 + %43 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %8 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %17 = OpVariable %9 Function + %18 = OpLoad %8 %11 + OpStore %17 %18 + %19 = OpLoad %8 %17 + %21 = OpIAdd %8 %19 %20 + OpReturnValue %21 + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %24 = OpVariable %9 Function + OpStore %24 %25 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: Pointer of id 61 has storage class Output, which is not supported. + TransformationAddParameter transformation_bad_1(12, 50, 60, + {{{38, 61}, {42, 61}}}, 51); + + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Good: Local variable of id 31 is defined in the caller (main). + TransformationAddParameter transformation_good_1(12, 50, 30, + {{{38, 31}, {42, 31}}}, 51); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Good: Local variable of id 34 is defined in the caller (main). + TransformationAddParameter transformation_good_2(14, 52, 9, {{{43, 34}}}, 53); + ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Good: Local variable of id 39 is defined in the caller (main). + TransformationAddParameter transformation_good_3(6, 54, 9, {{{33, 39}}}, 55); + ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Good: This adds another pointer parameter to the function of id 6. + TransformationAddParameter transformation_good_4(6, 56, 30, {{{33, 31}}}, 57); + ASSERT_TRUE(transformation_good_4.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun1(" + OpName %12 "fun2(i1;" + OpName %11 "a" + OpName %14 "fun3(" + OpName %17 "s" + OpName %24 "s" + OpName %28 "f1" + OpName %31 "f2" + OpName %34 "i1" + OpName %35 "i2" + OpName %36 "param" + OpName %39 "i3" + OpName %40 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %20 = OpConstant %8 2 + %25 = OpConstant %8 0 + %26 = OpTypeFloat 32 + %27 = OpTypePointer Private %26 + %28 = OpVariable %27 Private + %60 = OpTypePointer Output %26 + %61 = OpVariable %60 Output + %29 = OpConstant %26 1 + %30 = OpTypePointer Function %26 + %32 = OpConstant %26 2 + %10 = OpTypeFunction %8 %9 %30 + %53 = OpTypeFunction %2 %9 + %57 = OpTypeFunction %2 %9 %30 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %31 = OpVariable %30 Function + %34 = OpVariable %9 Function + %35 = OpVariable %9 Function + %36 = OpVariable %9 Function + %39 = OpVariable %9 Function + %40 = OpVariable %9 Function + OpStore %28 %29 + OpStore %31 %32 + %33 = OpFunctionCall %2 %6 %39 %31 + OpStore %34 %20 + %37 = OpLoad %8 %34 + OpStore %36 %37 + %38 = OpFunctionCall %8 %12 %36 %31 + OpStore %35 %38 + %41 = OpLoad %8 %35 + OpStore %40 %41 + %42 = OpFunctionCall %8 %12 %40 %31 + OpStore %39 %42 + %43 = OpFunctionCall %2 %14 %34 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %57 + %54 = OpFunctionParameter %9 + %56 = OpFunctionParameter %30 + %7 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %8 None %10 + %11 = OpFunctionParameter %9 + %50 = OpFunctionParameter %30 + %13 = OpLabel + %17 = OpVariable %9 Function + %18 = OpLoad %8 %11 + OpStore %17 %18 + %19 = OpLoad %8 %17 + %21 = OpIAdd %8 %19 %20 + OpReturnValue %21 + OpFunctionEnd + %14 = OpFunction %2 None %53 + %52 = OpFunctionParameter %9 + %15 = OpLabel + %24 = OpVariable %9 Function + OpStore %24 %25 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationAddParameterTest, PointerPrivateWorkgroupTest) { + // This types handles case of adding a new parameter of a pointer type with + // storage class Private or Workgroup. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun1(" + OpName %12 "fun2(i1;" + OpName %11 "a" + OpName %14 "fun3(" + OpName %17 "s" + OpName %24 "s" + OpName %28 "f1" + OpName %31 "f2" + OpName %34 "i1" + OpName %35 "i2" + OpName %36 "param" + OpName %39 "i3" + OpName %40 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %8 %9 + %20 = OpConstant %8 2 + %25 = OpConstant %8 0 + %26 = OpTypeFloat 32 + %27 = OpTypePointer Private %26 + %28 = OpVariable %27 Private + %60 = OpTypePointer Workgroup %26 + %61 = OpVariable %60 Workgroup + %29 = OpConstant %26 1 + %30 = OpTypePointer Function %26 + %32 = OpConstant %26 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %31 = OpVariable %30 Function + %34 = OpVariable %9 Function + %35 = OpVariable %9 Function + %36 = OpVariable %9 Function + %39 = OpVariable %9 Function + %40 = OpVariable %9 Function + OpStore %28 %29 + OpStore %31 %32 + %33 = OpFunctionCall %2 %6 + OpStore %34 %20 + %37 = OpLoad %8 %34 + OpStore %36 %37 + %38 = OpFunctionCall %8 %12 %36 + OpStore %35 %38 + %41 = OpLoad %8 %35 + OpStore %40 %41 + %42 = OpFunctionCall %8 %12 %40 + OpStore %39 %42 + %43 = OpFunctionCall %2 %14 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %8 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %17 = OpVariable %9 Function + %18 = OpLoad %8 %11 + OpStore %17 %18 + %19 = OpLoad %8 %17 + %21 = OpIAdd %8 %19 %20 + OpReturnValue %21 + OpFunctionEnd + %14 = OpFunction %2 None %3 + %15 = OpLabel + %24 = OpVariable %9 Function + OpStore %24 %25 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Good: Global variable of id 28 (storage class Private) is defined in the + // caller (main). + TransformationAddParameter transformation_good_1(12, 70, 27, + {{{38, 28}, {42, 28}}}, 71); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Good: Global variable of id 61 is (storage class Workgroup) is defined in + // the caller (main). + TransformationAddParameter transformation_good_2(12, 72, 27, + {{{38, 28}, {42, 28}}}, 73); + ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_2, context.get(), + &transformation_context); + + // Good: Global variable of id 28 (storage class Private) is defined in the + // caller (main). + TransformationAddParameter transformation_good_3(6, 74, 27, {{{33, 28}}}, 75); + ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Good: Global variable of id 61 is (storage class Workgroup) is defined in + // the caller (main). + TransformationAddParameter transformation_good_4(6, 76, 60, {{{33, 61}}}, 77); + ASSERT_TRUE(transformation_good_4.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_4, context.get(), + &transformation_context); + + // Good: Global variable of id 28 (storage class Private) is defined in the + // caller (main). + TransformationAddParameter transformation_good_5(14, 78, 27, {{{43, 28}}}, + 79); + ASSERT_TRUE(transformation_good_5.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Good: Global variable of id 61 is (storage class Workgroup) is defined in + // the caller (main). + TransformationAddParameter transformation_good_6(14, 80, 60, {{{43, 61}}}, + 81); + ASSERT_TRUE(transformation_good_6.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_6, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun1(" + OpName %12 "fun2(i1;" + OpName %11 "a" + OpName %14 "fun3(" + OpName %17 "s" + OpName %24 "s" + OpName %28 "f1" + OpName %31 "f2" + OpName %34 "i1" + OpName %35 "i2" + OpName %36 "param" + OpName %39 "i3" + OpName %40 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %20 = OpConstant %8 2 + %25 = OpConstant %8 0 + %26 = OpTypeFloat 32 + %27 = OpTypePointer Private %26 + %28 = OpVariable %27 Private + %60 = OpTypePointer Workgroup %26 + %61 = OpVariable %60 Workgroup + %29 = OpConstant %26 1 + %30 = OpTypePointer Function %26 + %32 = OpConstant %26 2 + %10 = OpTypeFunction %8 %9 %27 %27 + %75 = OpTypeFunction %2 %27 %60 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %31 = OpVariable %30 Function + %34 = OpVariable %9 Function + %35 = OpVariable %9 Function + %36 = OpVariable %9 Function + %39 = OpVariable %9 Function + %40 = OpVariable %9 Function + OpStore %28 %29 + OpStore %31 %32 + %33 = OpFunctionCall %2 %6 %28 %61 + OpStore %34 %20 + %37 = OpLoad %8 %34 + OpStore %36 %37 + %38 = OpFunctionCall %8 %12 %36 %28 %28 + OpStore %35 %38 + %41 = OpLoad %8 %35 + OpStore %40 %41 + %42 = OpFunctionCall %8 %12 %40 %28 %28 + OpStore %39 %42 + %43 = OpFunctionCall %2 %14 %28 %61 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %75 + %74 = OpFunctionParameter %27 + %76 = OpFunctionParameter %60 + %7 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %8 None %10 + %11 = OpFunctionParameter %9 + %70 = OpFunctionParameter %27 + %72 = OpFunctionParameter %27 + %13 = OpLabel + %17 = OpVariable %9 Function + %18 = OpLoad %8 %11 + OpStore %17 %18 + %19 = OpLoad %8 %17 + %21 = OpIAdd %8 %19 %20 + OpReturnValue %21 + OpFunctionEnd + %14 = OpFunction %2 None %75 + %78 = OpFunctionParameter %27 + %80 = OpFunctionParameter %60 + %15 = OpLabel + %24 = OpVariable %9 Function + OpStore %24 %25 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationAddParameterTest, PointerMoreEntriesInMapTest) { + // This types handles case where call_parameter_id has an entry for at least + // every caller (there are more entries than it is necessary). + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %19 "i1" + OpName %21 "i2" + OpName %22 "i3" + OpName %24 "i4" + OpName %25 "param" + OpName %28 "i5" + OpName %29 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %15 = OpConstant %6 2 + %20 = OpConstant %6 1 + %23 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpVariable %7 Function + %21 = OpVariable %7 Function + %22 = OpVariable %7 Function + %24 = OpVariable %7 Function + %25 = OpVariable %7 Function + %28 = OpVariable %7 Function + %29 = OpVariable %7 Function + OpStore %19 %20 + OpStore %21 %15 + OpStore %22 %23 + %26 = OpLoad %6 %19 + OpStore %25 %26 + %27 = OpFunctionCall %6 %10 %25 + OpStore %24 %27 + %30 = OpLoad %6 %21 + OpStore %29 %30 + %31 = OpFunctionCall %6 %10 %29 + OpStore %28 %31 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %13 = OpLoad %6 %9 + OpStore %12 %13 + %14 = OpLoad %6 %12 + %16 = OpIAdd %6 %14 %15 + OpReturnValue %16 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Good: Local variable of id 21 is defined in every caller (id 27 and id 31). + TransformationAddParameter transformation_good_1( + 10, 70, 7, {{{27, 21}, {31, 21}, {30, 21}}}, 71); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Good: Local variable of id 28 is defined in every caller (id 27 and id 31). + TransformationAddParameter transformation_good_2( + 10, 72, 7, {{{27, 28}, {31, 28}, {14, 21}, {16, 14}}}, 73); + ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %19 "i1" + OpName %21 "i2" + OpName %22 "i3" + OpName %24 "i4" + OpName %25 "param" + OpName %28 "i5" + OpName %29 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %15 = OpConstant %6 2 + %20 = OpConstant %6 1 + %23 = OpConstant %6 3 + %8 = OpTypeFunction %6 %7 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpVariable %7 Function + %21 = OpVariable %7 Function + %22 = OpVariable %7 Function + %24 = OpVariable %7 Function + %25 = OpVariable %7 Function + %28 = OpVariable %7 Function + %29 = OpVariable %7 Function + OpStore %19 %20 + OpStore %21 %15 + OpStore %22 %23 + %26 = OpLoad %6 %19 + OpStore %25 %26 + %27 = OpFunctionCall %6 %10 %25 %21 %28 + OpStore %24 %27 + %30 = OpLoad %6 %21 + OpStore %29 %30 + %31 = OpFunctionCall %6 %10 %29 %21 %28 + OpStore %28 %31 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %70 = OpFunctionParameter %7 + %72 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %13 = OpLoad %6 %9 + OpStore %12 %13 + %14 = OpLoad %6 %12 + %16 = OpIAdd %6 %14 %15 + OpReturnValue %16 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationAddParameterTest, PointeeValueIsIrrelevantTest) { + // This test checks if the transformation has correctly applied the + // PointeeValueIsIrrelevant fact for new pointer parameters. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %20 "b" + OpName %22 "i1" + OpName %24 "i2" + OpName %25 "i3" + OpName %26 "param" + OpName %29 "i4" + OpName %30 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpTypePointer Workgroup %6 + %51 = OpVariable %50 Workgroup + %8 = OpTypeFunction %6 %7 + %15 = OpConstant %6 2 + %19 = OpTypePointer Private %6 + %20 = OpVariable %19 Private + %21 = OpConstant %6 0 + %23 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %22 = OpVariable %7 Function + %24 = OpVariable %7 Function + %25 = OpVariable %7 Function + %26 = OpVariable %7 Function + %29 = OpVariable %7 Function + %30 = OpVariable %7 Function + OpStore %20 %21 + OpStore %22 %23 + OpStore %24 %15 + %27 = OpLoad %6 %22 + OpStore %26 %27 + %28 = OpFunctionCall %6 %10 %26 + OpStore %25 %28 + %31 = OpLoad %6 %24 + OpStore %30 %31 + %32 = OpFunctionCall %6 %10 %30 + OpStore %29 %32 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %13 = OpLoad %6 %9 + OpStore %12 %13 + %14 = OpLoad %6 %12 + %16 = OpIAdd %6 %14 %15 + OpReturnValue %16 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationAddParameter transformation_good_1(10, 70, 7, + {{{28, 22}, {32, 22}}}, 71); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Check if the fact PointeeValueIsIrrelevant is set for the new parameter + // (storage class Function). + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(70)); + + TransformationAddParameter transformation_good_2(10, 72, 19, + {{{28, 20}, {32, 20}}}, 73); + ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Check if the fact PointeeValueIsIrrelevant is set for the new parameter + // (storage class Private). + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(72)); + + TransformationAddParameter transformation_good_3(10, 74, 50, + {{{28, 51}, {32, 51}}}, 75); + ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Check if the fact PointeeValueIsIrrelevant is set for the new parameter + // (storage class Workgroup). + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(74)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp new file mode 100644 index 0000000..c440882 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_relaxed_decoration_test.cpp @@ -0,0 +1,147 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_relaxed_decoration.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { +TEST(TransformationAddRelaxedDecorationTest, BasicScenarios) { + // This is a simple transformation and this test handles the main cases. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %14 "c" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 4 + %11 = OpConstant %6 6 + %12 = OpTypeBool + %13 = OpTypePointer Function %12 + %15 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpSelectionMerge %19 None + OpBranchConditional %15 %19 %100 + %100 = OpLabel + %25 = OpISub %6 %9 %11 + %28 = OpLogicalNot %12 %15 + OpBranch %19 + %19 = OpLabel + %27 = OpISub %6 %9 %11 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactBlockIsDead(100); + + // Invalid: 200 is not an id. + ASSERT_FALSE(TransformationAddRelaxedDecoration(200).IsApplicable( + context.get(), transformation_context)); + // Invalid: 1 is not in a block. + ASSERT_FALSE(TransformationAddRelaxedDecoration(1).IsApplicable( + context.get(), transformation_context)); + // Invalid: 27 is not in a dead block. + ASSERT_FALSE(TransformationAddRelaxedDecoration(27).IsApplicable( + context.get(), transformation_context)); + // Invalid: 28 is in a dead block, but returns bool (not numeric). + ASSERT_FALSE(TransformationAddRelaxedDecoration(28).IsApplicable( + context.get(), transformation_context)); + // It is valid to add RelaxedPrecision to 25 (and it's fine to + // have a duplicate). + for (uint32_t result_id : {25u, 25u}) { + TransformationAddRelaxedDecoration transformation(result_id); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %14 "c" + OpDecorate %25 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 4 + %11 = OpConstant %6 6 + %12 = OpTypeBool + %13 = OpTypePointer Function %12 + %15 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpSelectionMerge %19 None + OpBranchConditional %15 %19 %100 + %100 = OpLabel + %25 = OpISub %6 %9 %11 + %28 = OpLogicalNot %12 %15 + OpBranch %19 + %19 = OpLabel + %27 = OpISub %6 %9 %11 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_synonym_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_synonym_test.cpp new file mode 100644 index 0000000..3803fa3 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_synonym_test.cpp @@ -0,0 +1,1472 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_synonym.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddSynonymTest, NotApplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3 + %10 = OpTypeFloat 32 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 4.5 + %14 = OpTypeVector %10 2 + %15 = OpTypePointer Function %14 + %17 = OpConstant %10 3 + %18 = OpConstant %10 4 + %19 = OpConstantComposite %14 %17 %18 + %20 = OpTypeVector %6 2 + %21 = OpTypePointer Function %20 + %23 = OpConstant %6 4 + %24 = OpConstantComposite %20 %9 %23 + %26 = OpConstantNull %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %16 = OpVariable %15 Function + %22 = OpVariable %21 Function + OpStore %8 %9 + OpStore %12 %13 + OpStore %16 %19 + OpStore %22 %24 + %25 = OpUndef %6 + %27 = OpLoad %6 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24); + + auto insert_before = MakeInstructionDescriptor(22, SpvOpReturn, 0); + +#ifndef NDEBUG + ASSERT_DEATH( + TransformationAddSynonym( + 9, static_cast(-1), + 40, insert_before) + .IsApplicable(context.get(), transformation_context), + "Synonym type is invalid"); +#endif + + // These tests should succeed regardless of the synonym type. + for (int i = 0; + i < protobufs::TransformationAddSynonym::SynonymType_descriptor() + ->value_count(); + ++i) { + const auto* synonym_value = + protobufs::TransformationAddSynonym::SynonymType_descriptor()->value(i); + ASSERT_TRUE(protobufs::TransformationAddSynonym::SynonymType_IsValid( + synonym_value->number())); + auto synonym_type = + static_cast( + synonym_value->number()); + + // |synonym_fresh_id| is not fresh. + ASSERT_FALSE(TransformationAddSynonym(9, synonym_type, 9, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |result_id| is invalid. + ASSERT_FALSE(TransformationAddSynonym(40, synonym_type, 40, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Instruction with |result_id| has no type id. + ASSERT_FALSE(TransformationAddSynonym(5, synonym_type, 40, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Instruction with |result_id| is an OpUndef. + ASSERT_FALSE(TransformationAddSynonym(25, synonym_type, 40, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Instruction with |result_id| is an OpConstantNull. + ASSERT_FALSE(TransformationAddSynonym(26, synonym_type, 40, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |result_id| is irrelevant. + ASSERT_FALSE(TransformationAddSynonym(24, synonym_type, 40, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |insert_before| is invalid. + ASSERT_FALSE( + TransformationAddSynonym(9, synonym_type, 40, + MakeInstructionDescriptor(25, SpvOpStore, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Can't insert before |insert_before|. + ASSERT_FALSE( + TransformationAddSynonym(9, synonym_type, 40, + MakeInstructionDescriptor(5, SpvOpLabel, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddSynonym( + 9, synonym_type, 40, + MakeInstructionDescriptor(22, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddSynonym( + 9, synonym_type, 40, + MakeInstructionDescriptor(25, SpvOpFunctionEnd, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Domination rules are not satisfied. + ASSERT_FALSE( + TransformationAddSynonym(27, synonym_type, 40, + MakeInstructionDescriptor(27, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddSynonym(27, synonym_type, 40, + MakeInstructionDescriptor(22, SpvOpStore, 1)) + .IsApplicable(context.get(), transformation_context)); + } +} + +TEST(TransformationAddSynonymTest, AddZeroSubZeroMulOne) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + %8 = OpConstant %6 1 + %9 = OpConstant %6 34 + %10 = OpTypeInt 32 0 + %13 = OpConstant %10 34 + %14 = OpTypeFloat 32 + %15 = OpConstant %14 0 + %16 = OpConstant %14 1 + %17 = OpConstant %14 34 + %18 = OpTypeVector %14 2 + %19 = OpConstantComposite %18 %15 %15 + %20 = OpConstantComposite %18 %16 %16 + %21 = OpConstant %14 3 + %22 = OpConstant %14 4 + %23 = OpConstantComposite %18 %21 %22 + %24 = OpTypeVector %6 2 + %25 = OpConstantComposite %24 %7 %7 + %26 = OpConstantComposite %24 %8 %8 + %27 = OpConstant %6 3 + %28 = OpConstant %6 4 + %29 = OpConstantComposite %24 %27 %28 + %30 = OpTypeVector %10 2 + %33 = OpConstant %10 3 + %34 = OpConstant %10 4 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypeBool + %37 = OpTypeVector %36 2 + %38 = OpConstantTrue %36 + %39 = OpConstantComposite %37 %38 %38 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0); + + uint32_t fresh_id = 50; + for (auto synonym_type : {protobufs::TransformationAddSynonym::ADD_ZERO, + protobufs::TransformationAddSynonym::SUB_ZERO, + protobufs::TransformationAddSynonym::MUL_ONE}) { + ASSERT_TRUE( + TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type)); + + // Can't create a synonym of a scalar or a vector of a wrong (in this case - + // boolean) type. + ASSERT_FALSE( + TransformationAddSynonym(38, synonym_type, fresh_id, insert_before) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddSynonym(39, synonym_type, fresh_id, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Required constant is not present in the module. + ASSERT_FALSE( + TransformationAddSynonym(13, synonym_type, fresh_id, insert_before) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddSynonym(35, synonym_type, fresh_id, insert_before) + .IsApplicable(context.get(), transformation_context)); + + for (auto result_id : {9, 17, 23, 29}) { + TransformationAddSynonym transformation(result_id, synonym_type, fresh_id, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {}))); + ++fresh_id; + } + } + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + %8 = OpConstant %6 1 + %9 = OpConstant %6 34 + %10 = OpTypeInt 32 0 + %13 = OpConstant %10 34 + %14 = OpTypeFloat 32 + %15 = OpConstant %14 0 + %16 = OpConstant %14 1 + %17 = OpConstant %14 34 + %18 = OpTypeVector %14 2 + %19 = OpConstantComposite %18 %15 %15 + %20 = OpConstantComposite %18 %16 %16 + %21 = OpConstant %14 3 + %22 = OpConstant %14 4 + %23 = OpConstantComposite %18 %21 %22 + %24 = OpTypeVector %6 2 + %25 = OpConstantComposite %24 %7 %7 + %26 = OpConstantComposite %24 %8 %8 + %27 = OpConstant %6 3 + %28 = OpConstant %6 4 + %29 = OpConstantComposite %24 %27 %28 + %30 = OpTypeVector %10 2 + %33 = OpConstant %10 3 + %34 = OpConstant %10 4 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypeBool + %37 = OpTypeVector %36 2 + %38 = OpConstantTrue %36 + %39 = OpConstantComposite %37 %38 %38 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %50 = OpIAdd %6 %9 %7 + %51 = OpFAdd %14 %17 %15 + %52 = OpFAdd %18 %23 %19 + %53 = OpIAdd %24 %29 %25 + %54 = OpISub %6 %9 %7 + %55 = OpFSub %14 %17 %15 + %56 = OpFSub %18 %23 %19 + %57 = OpISub %24 %29 %25 + %58 = OpIMul %6 %9 %8 + %59 = OpFMul %14 %17 %16 + %60 = OpFMul %18 %23 %20 + %61 = OpIMul %24 %29 %26 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationAddSynonymTest, LogicalAndLogicalOr) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %9 = OpConstantTrue %6 + %10 = OpTypeVector %6 2 + %11 = OpConstantComposite %10 %7 %9 + %12 = OpConstantComposite %10 %7 %7 + %13 = OpConstantComposite %10 %9 %9 + %14 = OpTypeFloat 32 + %17 = OpConstant %14 35 + %18 = OpTypeVector %14 2 + %21 = OpConstant %14 3 + %22 = OpConstant %14 4 + %23 = OpConstantComposite %18 %21 %22 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0); + + uint32_t fresh_id = 50; + for (auto synonym_type : {protobufs::TransformationAddSynonym::LOGICAL_AND, + protobufs::TransformationAddSynonym::LOGICAL_OR}) { + ASSERT_TRUE( + TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type)); + + // Can't create a synonym of a scalar or a vector of a wrong (in this case - + // float) type. + ASSERT_FALSE( + TransformationAddSynonym(17, synonym_type, fresh_id, insert_before) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationAddSynonym(23, synonym_type, fresh_id, insert_before) + .IsApplicable(context.get(), transformation_context)); + + for (auto result_id : {9, 11}) { + TransformationAddSynonym transformation(result_id, synonym_type, fresh_id, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {}))); + ++fresh_id; + } + } + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %9 = OpConstantTrue %6 + %10 = OpTypeVector %6 2 + %11 = OpConstantComposite %10 %7 %9 + %12 = OpConstantComposite %10 %7 %7 + %13 = OpConstantComposite %10 %9 %9 + %14 = OpTypeFloat 32 + %17 = OpConstant %14 35 + %18 = OpTypeVector %14 2 + %21 = OpConstant %14 3 + %22 = OpConstant %14 4 + %23 = OpConstantComposite %18 %21 %22 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %50 = OpLogicalAnd %6 %9 %9 + %51 = OpLogicalAnd %10 %11 %13 + %52 = OpLogicalOr %6 %9 %7 + %53 = OpLogicalOr %10 %11 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationAddSynonymTest, LogicalAndConstantIsNotPresent) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %10 = OpTypeVector %6 2 + %12 = OpConstantComposite %10 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0); + const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_AND; + + // Required constant is not present in the module. + ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddSynonymTest, LogicalOrConstantIsNotPresent) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeVector %6 2 + %12 = OpConstantComposite %10 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0); + const auto synonym_type = protobufs::TransformationAddSynonym::LOGICAL_OR; + + // Required constant is not present in the module. + ASSERT_FALSE(TransformationAddSynonym(7, synonym_type, 50, insert_before) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddSynonym(12, synonym_type, 50, insert_before) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddSynonymTest, CopyObject) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 4 + %10 = OpTypeFloat 32 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 4 + %14 = OpTypeVector %10 2 + %15 = OpTypePointer Function %14 + %17 = OpConstant %10 3.4000001 + %18 = OpConstantComposite %14 %17 %17 + %19 = OpTypeBool + %20 = OpTypeStruct %19 + %21 = OpTypePointer Function %20 + %23 = OpConstantTrue %19 + %24 = OpConstantComposite %20 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %16 = OpVariable %15 Function + %22 = OpVariable %21 Function + OpStore %8 %9 + OpStore %12 %13 + OpStore %16 %18 + OpStore %22 %24 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(5, SpvOpReturn, 0); + const auto synonym_type = protobufs::TransformationAddSynonym::COPY_OBJECT; + + ASSERT_FALSE( + TransformationAddSynonym::IsAdditionalConstantRequired(synonym_type)); + + uint32_t fresh_id = 50; + for (auto result_id : {9, 13, 17, 18, 23, 24, 22}) { + TransformationAddSynonym transformation(result_id, synonym_type, fresh_id, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(result_id, {}), MakeDataDescriptor(fresh_id, {}))); + ++fresh_id; + } + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 4 + %10 = OpTypeFloat 32 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 4 + %14 = OpTypeVector %10 2 + %15 = OpTypePointer Function %14 + %17 = OpConstant %10 3.4000001 + %18 = OpConstantComposite %14 %17 %17 + %19 = OpTypeBool + %20 = OpTypeStruct %19 + %21 = OpTypePointer Function %20 + %23 = OpConstantTrue %19 + %24 = OpConstantComposite %20 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %16 = OpVariable %15 Function + %22 = OpVariable %21 Function + OpStore %8 %9 + OpStore %12 %13 + OpStore %16 %18 + OpStore %22 %24 + %50 = OpCopyObject %6 %9 + %51 = OpCopyObject %10 %13 + %52 = OpCopyObject %10 %17 + %53 = OpCopyObject %14 %18 + %54 = OpCopyObject %19 %23 + %55 = OpCopyObject %20 %24 + %56 = OpCopyObject %21 %22 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationAddSynonymTest, CopyBooleanConstants) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %8 = OpConstantFalse %6 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_EQ(0, transformation_context.GetFactManager() + ->GetIdsForWhichSynonymsAreKnown() + .size()); + + { + TransformationAddSynonym copy_true( + 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, + MakeInstructionDescriptor(5, SpvOpReturn, 0)); + ASSERT_TRUE(copy_true.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(copy_true, context.get(), &transformation_context); + + std::vector ids_for_which_synonyms_are_known = + transformation_context.GetFactManager() + ->GetIdsForWhichSynonymsAreKnown(); + ASSERT_EQ(2, ids_for_which_synonyms_are_known.size()); + ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), + ids_for_which_synonyms_are_known.end(), + 7) != ids_for_which_synonyms_are_known.end()); + ASSERT_EQ( + 2, transformation_context.GetFactManager()->GetSynonymsForId(7).size()); + protobufs::DataDescriptor descriptor_100 = MakeDataDescriptor(100, {}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(7, {}), descriptor_100)); + } + + { + TransformationAddSynonym copy_false( + 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 101, + MakeInstructionDescriptor(100, SpvOpReturn, 0)); + ASSERT_TRUE(copy_false.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(copy_false, context.get(), &transformation_context); + std::vector ids_for_which_synonyms_are_known = + transformation_context.GetFactManager() + ->GetIdsForWhichSynonymsAreKnown(); + ASSERT_EQ(4, ids_for_which_synonyms_are_known.size()); + ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), + ids_for_which_synonyms_are_known.end(), + 8) != ids_for_which_synonyms_are_known.end()); + ASSERT_EQ( + 2, transformation_context.GetFactManager()->GetSynonymsForId(8).size()); + protobufs::DataDescriptor descriptor_101 = MakeDataDescriptor(101, {}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(8, {}), descriptor_101)); + } + + { + TransformationAddSynonym copy_false_again( + 101, protobufs::TransformationAddSynonym::COPY_OBJECT, 102, + MakeInstructionDescriptor(5, SpvOpReturn, 0)); + ASSERT_TRUE( + copy_false_again.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(copy_false_again, context.get(), + &transformation_context); + std::vector ids_for_which_synonyms_are_known = + transformation_context.GetFactManager() + ->GetIdsForWhichSynonymsAreKnown(); + ASSERT_EQ(5, ids_for_which_synonyms_are_known.size()); + ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), + ids_for_which_synonyms_are_known.end(), + 101) != ids_for_which_synonyms_are_known.end()); + ASSERT_EQ( + 3, + transformation_context.GetFactManager()->GetSynonymsForId(101).size()); + protobufs::DataDescriptor descriptor_102 = MakeDataDescriptor(102, {}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(101, {}), descriptor_102)); + } + + { + TransformationAddSynonym copy_true_again( + 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 103, + MakeInstructionDescriptor(102, SpvOpReturn, 0)); + ASSERT_TRUE( + copy_true_again.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(copy_true_again, context.get(), + &transformation_context); + std::vector ids_for_which_synonyms_are_known = + transformation_context.GetFactManager() + ->GetIdsForWhichSynonymsAreKnown(); + ASSERT_EQ(6, ids_for_which_synonyms_are_known.size()); + ASSERT_TRUE(std::find(ids_for_which_synonyms_are_known.begin(), + ids_for_which_synonyms_are_known.end(), + 7) != ids_for_which_synonyms_are_known.end()); + ASSERT_EQ( + 3, transformation_context.GetFactManager()->GetSynonymsForId(7).size()); + protobufs::DataDescriptor descriptor_103 = MakeDataDescriptor(103, {}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(7, {}), descriptor_103)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %8 = OpConstantFalse %6 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %100 = OpCopyObject %6 %7 + %101 = OpCopyObject %6 %8 + %102 = OpCopyObject %6 %101 + %103 = OpCopyObject %6 %7 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddSynonymTest, CheckIllegalCases) { + // The following SPIR-V comes from this GLSL, pushed through spirv-opt + // and then doctored a bit. + // + // #version 310 es + // + // precision highp float; + // + // struct S { + // int a; + // float b; + // }; + // + // layout(set = 0, binding = 2) uniform block { + // S s; + // lowp float f; + // int ii; + // } ubuf; + // + // layout(location = 0) out vec4 color; + // + // void main() { + // float c = 0.0; + // lowp float d = 0.0; + // S localS = ubuf.s; + // for (int i = 0; i < ubuf.s.a; i++) { + // switch (ubuf.ii) { + // case 0: + // c += 0.1; + // d += 0.2; + // case 1: + // c += 0.1; + // if (c > d) { + // d += 0.2; + // } else { + // d += c; + // } + // break; + // default: + // i += 1; + // localS.b += d; + // } + // } + // color = vec4(c, d, localS.b, 1.0); + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %80 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "S" + OpMemberName %12 0 "a" + OpMemberName %12 1 "b" + OpName %15 "S" + OpMemberName %15 0 "a" + OpMemberName %15 1 "b" + OpName %16 "block" + OpMemberName %16 0 "s" + OpMemberName %16 1 "f" + OpMemberName %16 2 "ii" + OpName %18 "ubuf" + OpName %80 "color" + OpMemberDecorate %12 0 RelaxedPrecision + OpMemberDecorate %15 0 RelaxedPrecision + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 1 Offset 4 + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 RelaxedPrecision + OpMemberDecorate %16 1 Offset 16 + OpMemberDecorate %16 2 RelaxedPrecision + OpMemberDecorate %16 2 Offset 20 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 2 + OpDecorate %38 RelaxedPrecision + OpDecorate %43 RelaxedPrecision + OpDecorate %53 RelaxedPrecision + OpDecorate %62 RelaxedPrecision + OpDecorate %69 RelaxedPrecision + OpDecorate %77 RelaxedPrecision + OpDecorate %80 Location 0 + OpDecorate %101 RelaxedPrecision + OpDecorate %102 RelaxedPrecision + OpDecorate %96 RelaxedPrecision + OpDecorate %108 RelaxedPrecision + OpDecorate %107 RelaxedPrecision + OpDecorate %98 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %9 = OpConstant %6 0 + %11 = OpTypeInt 32 1 + %12 = OpTypeStruct %11 %6 + %15 = OpTypeStruct %11 %6 + %16 = OpTypeStruct %15 %6 %11 + %17 = OpTypePointer Uniform %16 + %18 = OpVariable %17 Uniform + %19 = OpConstant %11 0 + %20 = OpTypePointer Uniform %15 + %27 = OpConstant %11 1 + %36 = OpTypePointer Uniform %11 + %39 = OpTypeBool + %41 = OpConstant %11 2 + %48 = OpConstant %6 0.100000001 + %51 = OpConstant %6 0.200000003 + %78 = OpTypeVector %6 4 + %79 = OpTypePointer Output %78 + %80 = OpVariable %79 Output + %85 = OpConstant %6 1 + %95 = OpUndef %12 + %112 = OpTypePointer Uniform %6 + %113 = OpTypeInt 32 0 + %114 = OpConstant %113 1 + %179 = OpTypePointer Function %39 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %180 = OpVariable %179 Function + %181 = OpVariable %179 Function + %182 = OpVariable %179 Function + %21 = OpAccessChain %20 %18 %19 + %115 = OpAccessChain %112 %21 %114 + %116 = OpLoad %6 %115 + %90 = OpCompositeInsert %12 %116 %95 1 + OpBranch %30 + %30 = OpLabel + %99 = OpPhi %12 %90 %5 %109 %47 + %98 = OpPhi %6 %9 %5 %107 %47 + %97 = OpPhi %6 %9 %5 %105 %47 + %96 = OpPhi %11 %19 %5 %77 %47 + %37 = OpAccessChain %36 %18 %19 %19 + %38 = OpLoad %11 %37 + %40 = OpSLessThan %39 %96 %38 + OpLoopMerge %32 %47 None + OpBranchConditional %40 %31 %32 + %31 = OpLabel + %42 = OpAccessChain %36 %18 %41 + %43 = OpLoad %11 %42 + OpSelectionMerge %45 None + OpSwitch %43 %46 0 %44 1 %45 + %46 = OpLabel + %69 = OpIAdd %11 %96 %27 + %72 = OpCompositeExtract %6 %99 1 + %73 = OpFAdd %6 %72 %98 + %93 = OpCompositeInsert %12 %73 %99 1 + OpBranch %47 + %44 = OpLabel + %50 = OpFAdd %6 %97 %48 + %53 = OpFAdd %6 %98 %51 + OpBranch %45 + %45 = OpLabel + %101 = OpPhi %6 %98 %31 %53 %44 + %100 = OpPhi %6 %97 %31 %50 %44 + %55 = OpFAdd %6 %100 %48 + %58 = OpFOrdGreaterThan %39 %55 %101 + OpSelectionMerge %60 None + OpBranchConditional %58 %59 %63 + %59 = OpLabel + %62 = OpFAdd %6 %101 %51 + OpBranch %60 + %63 = OpLabel + %66 = OpFAdd %6 %101 %55 + OpBranch %60 + %60 = OpLabel + %108 = OpPhi %6 %62 %59 %66 %63 + OpBranch %47 + %47 = OpLabel + %109 = OpPhi %12 %93 %46 %99 %60 + %107 = OpPhi %6 %98 %46 %108 %60 + %105 = OpPhi %6 %97 %46 %55 %60 + %102 = OpPhi %11 %69 %46 %96 %60 + %77 = OpIAdd %11 %102 %27 + OpBranch %30 + %32 = OpLabel + %84 = OpCompositeExtract %6 %99 1 + %86 = OpCompositeConstruct %78 %97 %98 %84 %85 + OpStore %80 %86 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Inapplicable because %18 is decorated. + ASSERT_FALSE(TransformationAddSynonym( + 18, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(21, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because %77 is decorated. + ASSERT_FALSE(TransformationAddSynonym( + 77, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(77, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because %80 is decorated. + ASSERT_FALSE(TransformationAddSynonym( + 80, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(77, SpvOpIAdd, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because %84 is not available at the requested point + ASSERT_FALSE(TransformationAddSynonym( + 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(32, SpvOpCompositeExtract, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Fine because %84 is available at the requested point + ASSERT_TRUE(TransformationAddSynonym( + 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because id %9 is already in use + ASSERT_FALSE(TransformationAddSynonym( + 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 9, + MakeInstructionDescriptor(32, SpvOpCompositeConstruct, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because the requested point does not exist + ASSERT_FALSE(TransformationAddSynonym( + 84, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(86, SpvOpReturn, 2)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because %9 is not in a function + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(9, SpvOpTypeInt, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because the insert point is right before, or inside, a chunk + // of OpPhis + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(30, SpvOpPhi, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(99, SpvOpPhi, 1)) + .IsApplicable(context.get(), transformation_context)); + + // OK, because the insert point is just after a chunk of OpPhis. + ASSERT_TRUE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(96, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because the insert point is right after an OpSelectionMerge + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(58, SpvOpBranchConditional, 0)) + .IsApplicable(context.get(), transformation_context)); + + // OK, because the insert point is right before the OpSelectionMerge + ASSERT_TRUE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(58, SpvOpSelectionMerge, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because the insert point is right after an OpSelectionMerge + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(43, SpvOpSwitch, 0)) + .IsApplicable(context.get(), transformation_context)); + + // OK, because the insert point is right before the OpSelectionMerge + ASSERT_TRUE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(43, SpvOpSelectionMerge, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because the insert point is right after an OpLoopMerge + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(40, SpvOpBranchConditional, 0)) + .IsApplicable(context.get(), transformation_context)); + + // OK, because the insert point is right before the OpLoopMerge + ASSERT_TRUE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(40, SpvOpLoopMerge, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because id %300 does not exist + ASSERT_FALSE(TransformationAddSynonym( + 300, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(40, SpvOpLoopMerge, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Inapplicable because the following instruction is OpVariable + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(180, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(181, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(182, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + + // OK, because this is just past the group of OpVariable instructions. + ASSERT_TRUE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 200, + MakeInstructionDescriptor(182, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddSynonymTest, MiscellaneousCopies) { + // The following SPIR-V comes from this GLSL: + // + // #version 310 es + // + // precision highp float; + // + // float g; + // + // vec4 h; + // + // void main() { + // int a; + // int b; + // b = int(g); + // h.x = float(a); + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b" + OpName %11 "g" + OpName %16 "h" + OpName %17 "a" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeFloat 32 + %10 = OpTypePointer Private %9 + %11 = OpVariable %10 Private + %14 = OpTypeVector %9 4 + %15 = OpTypePointer Private %14 + %16 = OpVariable %15 Private + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %17 = OpVariable %7 Function + %12 = OpLoad %9 %11 + %13 = OpConvertFToS %6 %12 + OpStore %8 %13 + %18 = OpLoad %6 %17 + %19 = OpConvertSToF %9 %18 + %22 = OpAccessChain %10 %16 %21 + OpStore %22 %19 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + std::vector transformations = { + TransformationAddSynonym( + 19, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, + MakeInstructionDescriptor(22, SpvOpStore, 0)), + TransformationAddSynonym( + 22, protobufs::TransformationAddSynonym::COPY_OBJECT, 101, + MakeInstructionDescriptor(22, SpvOpCopyObject, 0)), + TransformationAddSynonym( + 12, protobufs::TransformationAddSynonym::COPY_OBJECT, 102, + MakeInstructionDescriptor(22, SpvOpCopyObject, 0)), + TransformationAddSynonym( + 11, protobufs::TransformationAddSynonym::COPY_OBJECT, 103, + MakeInstructionDescriptor(22, SpvOpCopyObject, 0)), + TransformationAddSynonym( + 16, protobufs::TransformationAddSynonym::COPY_OBJECT, 104, + MakeInstructionDescriptor(22, SpvOpCopyObject, 0)), + TransformationAddSynonym( + 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 105, + MakeInstructionDescriptor(22, SpvOpCopyObject, 0)), + TransformationAddSynonym( + 17, protobufs::TransformationAddSynonym::COPY_OBJECT, 106, + MakeInstructionDescriptor(22, SpvOpCopyObject, 0))}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b" + OpName %11 "g" + OpName %16 "h" + OpName %17 "a" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeFloat 32 + %10 = OpTypePointer Private %9 + %11 = OpVariable %10 Private + %14 = OpTypeVector %9 4 + %15 = OpTypePointer Private %14 + %16 = OpVariable %15 Private + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %17 = OpVariable %7 Function + %12 = OpLoad %9 %11 + %13 = OpConvertFToS %6 %12 + OpStore %8 %13 + %18 = OpLoad %6 %17 + %19 = OpConvertSToF %9 %18 + %22 = OpAccessChain %10 %16 %21 + %106 = OpCopyObject %7 %17 + %105 = OpCopyObject %7 %8 + %104 = OpCopyObject %15 %16 + %103 = OpCopyObject %10 %11 + %102 = OpCopyObject %9 %12 + %101 = OpCopyObject %10 %22 + %100 = OpCopyObject %9 %19 + OpStore %22 %19 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddSynonymTest, DoNotCopyNullOrUndefPointers) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpConstantNull %7 + %9 = OpUndef %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Illegal to copy null. + ASSERT_FALSE(TransformationAddSynonym( + 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, + MakeInstructionDescriptor(5, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Illegal to copy an OpUndef of pointer type. + ASSERT_FALSE(TransformationAddSynonym( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, + MakeInstructionDescriptor(5, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddSynonymTest, PropagateIrrelevantPointeeFact) { + // Checks that if a pointer is known to have an irrelevant value, the same + // holds after the pointer is copied. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %9 = OpVariable %7 Function + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(8); + + TransformationAddSynonym transformation1( + 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, + MakeInstructionDescriptor(9, SpvOpReturn, 0)); + TransformationAddSynonym transformation2( + 9, protobufs::TransformationAddSynonym::COPY_OBJECT, 101, + MakeInstructionDescriptor(9, SpvOpReturn, 0)); + TransformationAddSynonym transformation3( + 100, protobufs::TransformationAddSynonym::COPY_OBJECT, 102, + MakeInstructionDescriptor(9, SpvOpReturn, 0)); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(8)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(102)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(9)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(101)); +} + +TEST(TransformationAddSynonymTest, DoNotCopyOpSampledImage) { + // This checks that we do not try to copy the result id of an OpSampledImage + // instruction. + std::string shader = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE( + TransformationAddSynonym( + 216, protobufs::TransformationAddSynonym::COPY_OBJECT, 500, + MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0)) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddSynonymTest, DoNotCopyVoidRunctionResult) { + // This checks that we do not try to copy the result of a void function. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %6 "foo(" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationAddSynonym( + 8, protobufs::TransformationAddSynonym::COPY_OBJECT, 500, + MakeInstructionDescriptor(8, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddSynonymTest, HandlesDeadBlocks) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %11 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %11 Function + OpSelectionMerge %10 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(9); + + auto insert_before = MakeInstructionDescriptor(9, SpvOpBranch, 0); + + ASSERT_FALSE(TransformationAddSynonym( + 7, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, + insert_before) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_FALSE(TransformationAddSynonym( + 12, protobufs::TransformationAddSynonym::COPY_OBJECT, 100, + insert_before) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_array_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_array_test.cpp new file mode 100644 index 0000000..ab4ed9a --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_array_test.cpp @@ -0,0 +1,143 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_array.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeArrayTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %12 = OpConstant %7 3 + %13 = OpConstant %7 0 + %14 = OpConstant %7 -1 + %15 = OpTypeInt 32 0 + %16 = OpConstant %15 5 + %17 = OpConstant %15 0 + %18 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id already in use + ASSERT_FALSE(TransformationAddTypeArray(4, 10, 16).IsApplicable( + context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeArray(100, 1, 16) + .IsApplicable(context.get(), transformation_context)); + + // %3 is a function type + ASSERT_FALSE(TransformationAddTypeArray(100, 3, 16) + .IsApplicable(context.get(), transformation_context)); + + // %2 is not a constant + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 2) + .IsApplicable(context.get(), transformation_context)); + + // %18 is not an integer + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 18) + .IsApplicable(context.get(), transformation_context)); + + // %13 is signed 0 + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 13) + .IsApplicable(context.get(), transformation_context)); + + // %14 is negative + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 14) + .IsApplicable(context.get(), transformation_context)); + + // %17 is unsigned 0 + ASSERT_FALSE(TransformationAddTypeArray(100, 11, 17) + .IsApplicable(context.get(), transformation_context)); + + TransformationAddTypeArray transformations[] = { + // %100 = OpTypeArray %10 %16 + TransformationAddTypeArray(100, 10, 16), + + // %101 = OpTypeArray %7 %12 + TransformationAddTypeArray(101, 7, 12)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %12 = OpConstant %7 3 + %13 = OpConstant %7 0 + %14 = OpConstant %7 -1 + %15 = OpTypeInt 32 0 + %16 = OpConstant %15 5 + %17 = OpConstant %15 0 + %18 = OpConstant %6 1 + %100 = OpTypeArray %10 %16 + %101 = OpTypeArray %7 %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_boolean_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_boolean_test.cpp new file mode 100644 index 0000000..88d9f5b --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_boolean_test.cpp @@ -0,0 +1,86 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_boolean.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeBooleanTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Not applicable because id 1 is already in use. + ASSERT_FALSE(TransformationAddTypeBoolean(1).IsApplicable( + context.get(), transformation_context)); + + auto add_type_bool = TransformationAddTypeBoolean(100); + ASSERT_TRUE( + add_type_bool.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(add_type_bool, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Not applicable as we already have this type now. + ASSERT_FALSE(TransformationAddTypeBoolean(101).IsApplicable( + context.get(), transformation_context)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %100 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_float_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_float_test.cpp new file mode 100644 index 0000000..235d61b --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_float_test.cpp @@ -0,0 +1,145 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_float.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeFloatTest, IsApplicable) { + std::string reference_shader = R"( + OpCapability Shader + OpCapability Float16 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %5 "main" + +; Types + %2 = OpTypeFloat 16 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; main function + %5 = OpFunction %3 None %4 + %6 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests non-fresh id. + auto transformation = TransformationAddTypeFloat(1, 32); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests missing Float64 capability. + transformation = TransformationAddTypeFloat(7, 64); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests existing 16-bit float type. + transformation = TransformationAddTypeFloat(7, 16); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests adding 32-bit float type. + transformation = TransformationAddTypeFloat(7, 32); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddTypeFloatTest, Apply) { + std::string reference_shader = R"( + OpCapability Shader + OpCapability Float16 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + +; main function + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Adds 16-bit float type. + auto transformation = TransformationAddTypeFloat(6, 16); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds 32-bit float type. + transformation = TransformationAddTypeFloat(7, 32); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds 64-bit float type. + transformation = TransformationAddTypeFloat(8, 64); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + OpCapability Float16 + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 16 + %7 = OpTypeFloat 32 + %8 = OpTypeFloat 64 + +; main function + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_function_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_function_test.cpp new file mode 100644 index 0000000..5d9ea47 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_function_test.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_function.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeFunctionTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %12 = OpTypeFloat 32 + %13 = OpTypeStruct %6 %12 + %14 = OpTypePointer Function %13 + %200 = OpTypePointer Function %13 + %15 = OpTypeVector %12 3 + %16 = OpTypePointer Function %15 + %17 = OpTypeVector %12 2 + %18 = OpTypeFunction %17 %14 %16 + %23 = OpConstant %12 1 + %24 = OpConstantComposite %17 %23 %23 + %27 = OpConstant %6 3 + %30 = OpConstant %6 1 + %31 = OpConstant %12 2 + %32 = OpConstantComposite %13 %30 %31 + %33 = OpConstantComposite %15 %23 %23 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id already in use + ASSERT_FALSE(TransformationAddTypeFunction(4, 12, {12, 16, 14}) + .IsApplicable(context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeFunction(100, 1, {12, 16, 14}) + .IsApplicable(context.get(), transformation_context)); + + // %18 is a function type + ASSERT_FALSE(TransformationAddTypeFunction(100, 12, {18}) + .IsApplicable(context.get(), transformation_context)); + + // A function of this signature already exists + ASSERT_FALSE(TransformationAddTypeFunction(100, 17, {14, 16}) + .IsApplicable(context.get(), transformation_context)); + + TransformationAddTypeFunction transformations[] = { + // %100 = OpTypeFunction %12 %12 %16 %14 + TransformationAddTypeFunction(100, 12, {12, 16, 14}), + + // %101 = OpTypeFunction %12 + TransformationAddTypeFunction(101, 12, {}), + + // %102 = OpTypeFunction %17 %200 %16 + TransformationAddTypeFunction(102, 17, {200, 16})}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %12 = OpTypeFloat 32 + %13 = OpTypeStruct %6 %12 + %14 = OpTypePointer Function %13 + %200 = OpTypePointer Function %13 + %15 = OpTypeVector %12 3 + %16 = OpTypePointer Function %15 + %17 = OpTypeVector %12 2 + %18 = OpTypeFunction %17 %14 %16 + %23 = OpConstant %12 1 + %24 = OpConstantComposite %17 %23 %23 + %27 = OpConstant %6 3 + %30 = OpConstant %6 1 + %31 = OpConstant %12 2 + %32 = OpConstantComposite %13 %30 %31 + %33 = OpConstantComposite %15 %23 %23 %23 + %100 = OpTypeFunction %12 %12 %16 %14 + %101 = OpTypeFunction %12 + %102 = OpTypeFunction %17 %200 %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_int_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_int_test.cpp new file mode 100644 index 0000000..ee4e799 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_int_test.cpp @@ -0,0 +1,187 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_int.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeIntTest, IsApplicable) { + std::string reference_shader = R"( + OpCapability Shader + OpCapability Int8 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %5 "main" + +; Types + %2 = OpTypeInt 8 1 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + +; main function + %5 = OpFunction %3 None %4 + %6 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests non-fresh id. + auto transformation = TransformationAddTypeInt(1, 32, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests missing Int16 capability. + transformation = TransformationAddTypeInt(7, 16, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests missing Int64 capability. + transformation = TransformationAddTypeInt(7, 64, false); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests existing signed 8-bit integer type. + transformation = TransformationAddTypeInt(7, 8, true); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests adding unsigned 8-bit integer type. + transformation = TransformationAddTypeInt(7, 8, false); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests adding unsigned 32-bit integer type. + transformation = TransformationAddTypeInt(7, 32, false); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests adding signed 32-bit integer type. + transformation = TransformationAddTypeInt(7, 32, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAddTypeIntTest, Apply) { + std::string reference_shader = R"( + OpCapability Shader + OpCapability Int8 + OpCapability Int16 + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + +; main function + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Adds signed 8-bit integer type. + auto transformation = TransformationAddTypeInt(6, 8, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds signed 16-bit integer type. + transformation = TransformationAddTypeInt(7, 16, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds signed 32-bit integer type. + transformation = TransformationAddTypeInt(8, 32, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds signed 64-bit integer type. + transformation = TransformationAddTypeInt(9, 64, true); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds unsigned 8-bit integer type. + transformation = TransformationAddTypeInt(10, 8, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds unsigned 16-bit integer type. + transformation = TransformationAddTypeInt(11, 16, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds unsigned 32-bit integer type. + transformation = TransformationAddTypeInt(12, 32, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Adds unsigned 64-bit integer type. + transformation = TransformationAddTypeInt(13, 64, false); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + OpCapability Int8 + OpCapability Int16 + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 8 1 + %7 = OpTypeInt 16 1 + %8 = OpTypeInt 32 1 + %9 = OpTypeInt 64 1 + %10 = OpTypeInt 8 0 + %11 = OpTypeInt 16 0 + %12 = OpTypeInt 32 0 + %13 = OpTypeInt 64 0 + +; main function + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_matrix_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_matrix_test.cpp new file mode 100644 index 0000000..926e983 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_matrix_test.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_matrix.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeMatrixTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id already in use + ASSERT_FALSE(TransformationAddTypeMatrix(4, 9, 2).IsApplicable( + context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeMatrix(100, 1, 2).IsApplicable( + context.get(), transformation_context)); + + // %11 is not a floating-point vector + ASSERT_FALSE(TransformationAddTypeMatrix(100, 11, 2) + .IsApplicable(context.get(), transformation_context)); + + TransformationAddTypeMatrix transformations[] = { + // %100 = OpTypeMatrix %8 2 + TransformationAddTypeMatrix(100, 8, 2), + + // %101 = OpTypeMatrix %8 3 + TransformationAddTypeMatrix(101, 8, 3), + + // %102 = OpTypeMatrix %8 4 + TransformationAddTypeMatrix(102, 8, 4), + + // %103 = OpTypeMatrix %9 2 + TransformationAddTypeMatrix(103, 9, 2), + + // %104 = OpTypeMatrix %9 3 + TransformationAddTypeMatrix(104, 9, 3), + + // %105 = OpTypeMatrix %9 4 + TransformationAddTypeMatrix(105, 9, 4), + + // %106 = OpTypeMatrix %10 2 + TransformationAddTypeMatrix(106, 10, 2), + + // %107 = OpTypeMatrix %10 3 + TransformationAddTypeMatrix(107, 10, 3), + + // %108 = OpTypeMatrix %10 4 + TransformationAddTypeMatrix(108, 10, 4)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %100 = OpTypeMatrix %8 2 + %101 = OpTypeMatrix %8 3 + %102 = OpTypeMatrix %8 4 + %103 = OpTypeMatrix %9 2 + %104 = OpTypeMatrix %9 3 + %105 = OpTypeMatrix %9 4 + %106 = OpTypeMatrix %10 2 + %107 = OpTypeMatrix %10 3 + %108 = OpTypeMatrix %10 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp new file mode 100644 index 0000000..985e904 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_pointer_test.cpp @@ -0,0 +1,213 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_pointer.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypePointerTest, BasicTest) { + // The SPIR-V was obtained from this GLSL: + // + // #version 450 + // + // int x; + // float y; + // vec2 z; + // + // struct T { + // int a, b; + // }; + // + // struct S { + // T t; + // int u; + // }; + // + // void main() { + // S myS = S(T(1, 2), 3); + // myS.u = x; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %7 "T" + OpMemberName %7 0 "a" + OpMemberName %7 1 "b" + OpName %8 "S" + OpMemberName %8 0 "t" + OpMemberName %8 1 "u" + OpName %10 "myS" + OpName %17 "x" + OpName %23 "y" + OpName %26 "z" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypeStruct %7 %6 + %9 = OpTypePointer Function %8 + %11 = OpConstant %6 1 + %12 = OpConstant %6 2 + %13 = OpConstantComposite %7 %11 %12 + %14 = OpConstant %6 3 + %15 = OpConstantComposite %8 %13 %14 + %16 = OpTypePointer Private %6 + %17 = OpVariable %16 Private + %19 = OpTypePointer Function %6 + %21 = OpTypeFloat 32 + %22 = OpTypePointer Private %21 + %23 = OpVariable %22 Private + %24 = OpTypeVector %21 2 + %25 = OpTypePointer Private %24 + %26 = OpVariable %25 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %15 + %18 = OpLoad %6 %17 + %20 = OpAccessChain %19 %10 %11 + OpStore %20 %18 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto bad_type_id_does_not_exist = + TransformationAddTypePointer(100, SpvStorageClassFunction, 101); + auto bad_type_id_is_not_type = + TransformationAddTypePointer(100, SpvStorageClassFunction, 23); + auto bad_result_id_is_not_fresh = + TransformationAddTypePointer(17, SpvStorageClassFunction, 21); + + auto good_new_private_pointer_to_t = + TransformationAddTypePointer(101, SpvStorageClassPrivate, 7); + auto good_new_uniform_pointer_to_t = + TransformationAddTypePointer(102, SpvStorageClassUniform, 7); + auto good_another_function_pointer_to_s = + TransformationAddTypePointer(103, SpvStorageClassFunction, 8); + auto good_new_uniform_pointer_to_s = + TransformationAddTypePointer(104, SpvStorageClassUniform, 8); + auto good_another_private_pointer_to_float = + TransformationAddTypePointer(105, SpvStorageClassPrivate, 21); + auto good_new_private_pointer_to_private_pointer_to_float = + TransformationAddTypePointer(106, SpvStorageClassPrivate, 105); + auto good_new_uniform_pointer_to_vec2 = + TransformationAddTypePointer(107, SpvStorageClassUniform, 24); + auto good_new_private_pointer_to_uniform_pointer_to_vec2 = + TransformationAddTypePointer(108, SpvStorageClassPrivate, 107); + + ASSERT_FALSE(bad_type_id_does_not_exist.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(bad_type_id_is_not_type.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(bad_result_id_is_not_fresh.IsApplicable(context.get(), + transformation_context)); + + for (auto& transformation : + {good_new_private_pointer_to_t, good_new_uniform_pointer_to_t, + good_another_function_pointer_to_s, good_new_uniform_pointer_to_s, + good_another_private_pointer_to_float, + good_new_private_pointer_to_private_pointer_to_float, + good_new_uniform_pointer_to_vec2, + good_new_private_pointer_to_uniform_pointer_to_vec2}) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %7 "T" + OpMemberName %7 0 "a" + OpMemberName %7 1 "b" + OpName %8 "S" + OpMemberName %8 0 "t" + OpMemberName %8 1 "u" + OpName %10 "myS" + OpName %17 "x" + OpName %23 "y" + OpName %26 "z" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 + %8 = OpTypeStruct %7 %6 + %9 = OpTypePointer Function %8 + %11 = OpConstant %6 1 + %12 = OpConstant %6 2 + %13 = OpConstantComposite %7 %11 %12 + %14 = OpConstant %6 3 + %15 = OpConstantComposite %8 %13 %14 + %16 = OpTypePointer Private %6 + %17 = OpVariable %16 Private + %19 = OpTypePointer Function %6 + %21 = OpTypeFloat 32 + %22 = OpTypePointer Private %21 + %23 = OpVariable %22 Private + %24 = OpTypeVector %21 2 + %25 = OpTypePointer Private %24 + %26 = OpVariable %25 Private + %101 = OpTypePointer Private %7 + %102 = OpTypePointer Uniform %7 + %103 = OpTypePointer Function %8 + %104 = OpTypePointer Uniform %8 + %105 = OpTypePointer Private %21 + %106 = OpTypePointer Private %105 + %107 = OpTypePointer Uniform %24 + %108 = OpTypePointer Private %107 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %9 Function + OpStore %10 %15 + %18 = OpLoad %6 %17 + %20 = OpAccessChain %19 %10 %11 + OpStore %20 %18 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_struct_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_struct_test.cpp new file mode 100644 index 0000000..b57bab2 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_struct_test.cpp @@ -0,0 +1,158 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_struct.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeStructTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id already in use + ASSERT_FALSE(TransformationAddTypeStruct(4, {}).IsApplicable( + context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeStruct(100, {1}).IsApplicable( + context.get(), transformation_context)); + + // %3 is a function type + ASSERT_FALSE(TransformationAddTypeStruct(100, {3}).IsApplicable( + context.get(), transformation_context)); + + TransformationAddTypeStruct transformations[] = { + // %100 = OpTypeStruct %6 %7 %8 %9 %10 %11 + TransformationAddTypeStruct(100, {6, 7, 8, 9, 10, 11}), + + // %101 = OpTypeStruct + TransformationAddTypeStruct(101, {}), + + // %102 = OpTypeStruct %6 + TransformationAddTypeStruct(102, {6}), + + // %103 = OpTypeStruct %6 %6 + TransformationAddTypeStruct(103, {6, 6})}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 2 + %9 = OpTypeVector %6 3 + %10 = OpTypeVector %6 4 + %11 = OpTypeVector %7 2 + %100 = OpTypeStruct %6 %7 %8 %9 %10 %11 + %101 = OpTypeStruct + %102 = OpTypeStruct %6 + %103 = OpTypeStruct %6 %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationAddTypeStructTest, HandlesBuiltInMembers) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %2 "main" + OpMemberDecorate %4 0 BuiltIn Position + OpMemberDecorate %4 1 BuiltIn PointSize + OpMemberDecorate %4 2 BuiltIn ClipDistance + %6 = OpTypeFloat 32 + %5 = OpTypeVector %6 4 + %9 = OpTypeInt 32 1 + %8 = OpConstant %9 1 + %7 = OpTypeArray %6 %8 + %4 = OpTypeStruct %5 %6 %7 + %27 = OpTypeVoid + %28 = OpTypeFunction %27 + %2 = OpFunction %27 None %28 + %29 = OpLabel + OpReturn + OpFunctionEnd + + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // From the spec for the BuiltIn decoration: + // - When applied to a structure-type member, that structure type cannot + // be contained as a member of another structure type. + // + // OpTypeStruct with id %4 has BuiltIn members. + ASSERT_FALSE(TransformationAddTypeStruct(50, {6, 5, 4, 6, 7}) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_add_type_vector_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_add_type_vector_test.cpp new file mode 100644 index 0000000..a49ba6e --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_add_type_vector_test.cpp @@ -0,0 +1,109 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_add_type_vector.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAddTypeVectorTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Id already in use + ASSERT_FALSE(TransformationAddTypeVector(4, 6, 2).IsApplicable( + context.get(), transformation_context)); + // %1 is not a type + ASSERT_FALSE(TransformationAddTypeVector(100, 1, 2).IsApplicable( + context.get(), transformation_context)); + + TransformationAddTypeVector transformations[] = { + // %100 = OpTypeVector %6 2 + TransformationAddTypeVector(100, 6, 2), + + // %101 = OpTypeVector %7 3 + TransformationAddTypeVector(101, 7, 3), + + // %102 = OpTypeVector %8 4 + TransformationAddTypeVector(102, 8, 4), + + // %103 = OpTypeVector %9 2 + TransformationAddTypeVector(103, 9, 2)}; + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpTypeBool + %100 = OpTypeVector %6 2 + %101 = OpTypeVector %7 3 + %102 = OpTypeVector %8 4 + %103 = OpTypeVector %9 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_adjust_branch_weights_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_adjust_branch_weights_test.cpp new file mode 100644 index 0000000..1bf2c59 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_adjust_branch_weights_test.cpp @@ -0,0 +1,348 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_adjust_branch_weights.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationAdjustBranchWeightsTest, IsApplicableTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %51 %27 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %25 "buf" + OpMemberName %25 0 "value" + OpName %27 "" + OpName %51 "color" + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 0 + OpDecorate %51 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %150 = OpTypeVector %6 2 + %10 = OpConstant %6 0.300000012 + %11 = OpConstant %6 0.400000006 + %12 = OpConstant %6 0.5 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %25 = OpTypeStruct %6 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %6 + %32 = OpTypeBool + %103 = OpConstantTrue %32 + %34 = OpConstant %6 0.100000001 + %48 = OpConstant %15 1 + %50 = OpTypePointer Output %7 + %51 = OpVariable %50 Output + %100 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %101 = OpVariable %100 Function + %102 = OpVariable %100 Function + OpBranch %19 + %19 = OpLabel + %60 = OpPhi %7 %14 %5 %58 %20 + %59 = OpPhi %15 %18 %5 %49 %20 + %29 = OpAccessChain %28 %27 %18 + %30 = OpLoad %6 %29 + %31 = OpConvertFToS %15 %30 + %33 = OpSLessThan %32 %59 %31 + OpLoopMerge %21 %20 None + OpBranchConditional %33 %20 %21 1 2 + %20 = OpLabel + %39 = OpCompositeExtract %6 %60 0 + %40 = OpFAdd %6 %39 %34 + %55 = OpCompositeInsert %7 %40 %60 0 + %44 = OpCompositeExtract %6 %60 1 + %45 = OpFSub %6 %44 %34 + %58 = OpCompositeInsert %7 %45 %55 1 + %49 = OpIAdd %15 %59 %48 + OpBranch %19 + %21 = OpLabel + OpStore %51 %60 + OpSelectionMerge %105 None + OpBranchConditional %103 %104 %105 + %104 = OpLabel + OpBranch %105 + %105 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests OpBranchConditional instruction with weigths. + auto instruction_descriptor = + MakeInstructionDescriptor(33, SpvOpBranchConditional, 0); + auto transformation = + TransformationAdjustBranchWeights(instruction_descriptor, {0, 1}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests the two branch weights equal to 0. + instruction_descriptor = + MakeInstructionDescriptor(33, SpvOpBranchConditional, 0); + transformation = + TransformationAdjustBranchWeights(instruction_descriptor, {0, 0}); +#ifndef NDEBUG + ASSERT_DEATH( + transformation.IsApplicable(context.get(), transformation_context), + "At least one weight must be non-zero"); +#endif + + // Tests 32-bit unsigned integer overflow. + instruction_descriptor = + MakeInstructionDescriptor(33, SpvOpBranchConditional, 0); + transformation = TransformationAdjustBranchWeights(instruction_descriptor, + {UINT32_MAX, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = + MakeInstructionDescriptor(33, SpvOpBranchConditional, 0); + transformation = TransformationAdjustBranchWeights(instruction_descriptor, + {1, UINT32_MAX}); +#ifndef NDEBUG + ASSERT_DEATH( + transformation.IsApplicable(context.get(), transformation_context), + "The sum of the two weights must not be greater than UINT32_MAX"); +#endif + + // Tests OpBranchConditional instruction with no weights. + instruction_descriptor = + MakeInstructionDescriptor(21, SpvOpBranchConditional, 0); + transformation = + TransformationAdjustBranchWeights(instruction_descriptor, {0, 1}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests non-OpBranchConditional instructions. + instruction_descriptor = MakeInstructionDescriptor(2, SpvOpTypeVoid, 0); + transformation = + TransformationAdjustBranchWeights(instruction_descriptor, {5, 6}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = MakeInstructionDescriptor(20, SpvOpLabel, 0); + transformation = + TransformationAdjustBranchWeights(instruction_descriptor, {1, 2}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = MakeInstructionDescriptor(49, SpvOpIAdd, 0); + transformation = + TransformationAdjustBranchWeights(instruction_descriptor, {1, 2}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationAdjustBranchWeightsTest, ApplyTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %51 %27 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %25 "buf" + OpMemberName %25 0 "value" + OpName %27 "" + OpName %51 "color" + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 0 + OpDecorate %51 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %150 = OpTypeVector %6 2 + %10 = OpConstant %6 0.300000012 + %11 = OpConstant %6 0.400000006 + %12 = OpConstant %6 0.5 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %25 = OpTypeStruct %6 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %6 + %32 = OpTypeBool + %103 = OpConstantTrue %32 + %34 = OpConstant %6 0.100000001 + %48 = OpConstant %15 1 + %50 = OpTypePointer Output %7 + %51 = OpVariable %50 Output + %100 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %101 = OpVariable %100 Function + %102 = OpVariable %100 Function + OpBranch %19 + %19 = OpLabel + %60 = OpPhi %7 %14 %5 %58 %20 + %59 = OpPhi %15 %18 %5 %49 %20 + %29 = OpAccessChain %28 %27 %18 + %30 = OpLoad %6 %29 + %31 = OpConvertFToS %15 %30 + %33 = OpSLessThan %32 %59 %31 + OpLoopMerge %21 %20 None + OpBranchConditional %33 %20 %21 1 2 + %20 = OpLabel + %39 = OpCompositeExtract %6 %60 0 + %40 = OpFAdd %6 %39 %34 + %55 = OpCompositeInsert %7 %40 %60 0 + %44 = OpCompositeExtract %6 %60 1 + %45 = OpFSub %6 %44 %34 + %58 = OpCompositeInsert %7 %45 %55 1 + %49 = OpIAdd %15 %59 %48 + OpBranch %19 + %21 = OpLabel + OpStore %51 %60 + OpSelectionMerge %105 None + OpBranchConditional %103 %104 %105 + %104 = OpLabel + OpBranch %105 + %105 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(33, SpvOpBranchConditional, 0); + auto transformation = + TransformationAdjustBranchWeights(instruction_descriptor, {5, 6}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(21, SpvOpBranchConditional, 0); + transformation = + TransformationAdjustBranchWeights(instruction_descriptor, {7, 8}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %51 %27 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %25 "buf" + OpMemberName %25 0 "value" + OpName %27 "" + OpName %51 "color" + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 0 + OpDecorate %51 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %150 = OpTypeVector %6 2 + %10 = OpConstant %6 0.300000012 + %11 = OpConstant %6 0.400000006 + %12 = OpConstant %6 0.5 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %25 = OpTypeStruct %6 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %6 + %32 = OpTypeBool + %103 = OpConstantTrue %32 + %34 = OpConstant %6 0.100000001 + %48 = OpConstant %15 1 + %50 = OpTypePointer Output %7 + %51 = OpVariable %50 Output + %100 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %101 = OpVariable %100 Function + %102 = OpVariable %100 Function + OpBranch %19 + %19 = OpLabel + %60 = OpPhi %7 %14 %5 %58 %20 + %59 = OpPhi %15 %18 %5 %49 %20 + %29 = OpAccessChain %28 %27 %18 + %30 = OpLoad %6 %29 + %31 = OpConvertFToS %15 %30 + %33 = OpSLessThan %32 %59 %31 + OpLoopMerge %21 %20 None + OpBranchConditional %33 %20 %21 5 6 + %20 = OpLabel + %39 = OpCompositeExtract %6 %60 0 + %40 = OpFAdd %6 %39 %34 + %55 = OpCompositeInsert %7 %40 %60 0 + %44 = OpCompositeExtract %6 %60 1 + %45 = OpFSub %6 %44 %34 + %58 = OpCompositeInsert %7 %45 %55 1 + %49 = OpIAdd %15 %59 %48 + OpBranch %19 + %21 = OpLabel + OpStore %51 %60 + OpSelectionMerge %105 None + OpBranchConditional %103 %104 %105 7 8 + %104 = OpLabel + OpBranch %105 + %105 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_composite_construct_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_composite_construct_test.cpp new file mode 100644 index 0000000..d2a18b0 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_composite_construct_test.cpp @@ -0,0 +1,1648 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_composite_construct.h" + +#include "gtest/gtest.h" +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationCompositeConstructTest, ConstructArrays) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "floats" + OpName %22 "x" + OpName %39 "vecs" + OpName %49 "bools" + OpName %60 "many_uvec3s" + OpDecorate %60 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpConstant %6 1 + %15 = OpTypePointer Function %6 + %17 = OpConstant %12 1 + %18 = OpConstant %6 2 + %20 = OpTypeVector %6 2 + %21 = OpTypePointer Function %20 + %32 = OpTypeBool + %36 = OpConstant %7 3 + %37 = OpTypeArray %20 %36 + %38 = OpTypePointer Private %37 + %39 = OpVariable %38 Private + %40 = OpConstant %6 3 + %41 = OpConstantComposite %20 %40 %40 + %42 = OpTypePointer Private %20 + %44 = OpConstant %12 2 + %47 = OpTypeArray %32 %36 + %48 = OpTypePointer Function %47 + %50 = OpConstantTrue %32 + %51 = OpTypePointer Function %32 + %56 = OpTypeVector %7 3 + %57 = OpTypeArray %56 %8 + %58 = OpTypeArray %57 %8 + %59 = OpTypePointer Function %58 + %61 = OpConstant %7 4 + %62 = OpConstantComposite %56 %61 %61 %61 + %63 = OpTypePointer Function %56 + %65 = OpConstant %7 5 + %66 = OpConstantComposite %56 %65 %65 %65 + %67 = OpConstant %7 6 + %68 = OpConstantComposite %56 %67 %67 %67 + %69 = OpConstantComposite %57 %66 %68 + %100 = OpUndef %57 + %70 = OpTypePointer Function %57 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %22 = OpVariable %21 Function + %49 = OpVariable %48 Function + %60 = OpVariable %59 Function + %16 = OpAccessChain %15 %11 %13 + OpStore %16 %14 + %19 = OpAccessChain %15 %11 %17 + OpStore %19 %18 + %23 = OpAccessChain %15 %11 %13 + %24 = OpLoad %6 %23 + %25 = OpAccessChain %15 %11 %17 + %26 = OpLoad %6 %25 + %27 = OpCompositeConstruct %20 %24 %26 + OpStore %22 %27 + %28 = OpAccessChain %15 %11 %13 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %15 %11 %17 + %31 = OpLoad %6 %30 + %33 = OpFOrdGreaterThan %32 %29 %31 + OpSelectionMerge %35 None + OpBranchConditional %33 %34 %35 + %34 = OpLabel + %43 = OpAccessChain %42 %39 %17 + OpStore %43 %41 + %45 = OpLoad %20 %22 + %46 = OpAccessChain %42 %39 %44 + OpStore %46 %45 + OpBranch %35 + %35 = OpLabel + %52 = OpAccessChain %51 %49 %13 + OpStore %52 %50 + %53 = OpAccessChain %51 %49 %13 + %54 = OpLoad %32 %53 + %55 = OpAccessChain %51 %49 %17 + OpStore %55 %54 + %64 = OpAccessChain %63 %60 %13 %13 + OpStore %64 %62 + %71 = OpAccessChain %70 %60 %17 + OpStore %71 %69 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Make a vec2[3] + TransformationCompositeConstruct make_vec2_array_length_3( + 37, {41, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), + 200); + // Bad: there are too many components + TransformationCompositeConstruct make_vec2_array_length_3_bad( + 37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), + 200); + ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_vec2_array_length_3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2}))); + + // Make a float[2] + TransformationCompositeConstruct make_float_array_length_2( + 9, {24, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201); + // Bad: %41 does not have type float + TransformationCompositeConstruct make_float_array_length_2_bad( + 9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201); + ASSERT_TRUE(make_float_array_length_2.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(make_float_array_length_2_bad.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_float_array_length_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1}))); + + // Make a bool[3] + TransformationCompositeConstruct make_bool_array_length_3( + 47, {33, 50, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0), + 202); + // Bad: %54 is not available at the desired program point. + TransformationCompositeConstruct make_bool_array_length_3_bad( + 47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0), + 202); + ASSERT_TRUE(make_bool_array_length_3.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(make_bool_array_length_3_bad.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_bool_array_length_3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2}))); + + // make a uvec3[2][2] + TransformationCompositeConstruct make_uvec3_array_length_2_2( + 58, {69, 100}, MakeInstructionDescriptor(64, SpvOpStore, 0), 203); + // Bad: Skip count 100 is too large. + TransformationCompositeConstruct make_uvec3_array_length_2_2_bad( + 58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203); + ASSERT_TRUE(make_uvec3_array_length_2_2.IsApplicable(context.get(), + transformation_context)); + ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_uvec3_array_length_2_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(69, {}), MakeDataDescriptor(203, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {}), MakeDataDescriptor(203, {1}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "floats" + OpName %22 "x" + OpName %39 "vecs" + OpName %49 "bools" + OpName %60 "many_uvec3s" + OpDecorate %60 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpTypeInt 32 1 + %13 = OpConstant %12 0 + %14 = OpConstant %6 1 + %15 = OpTypePointer Function %6 + %17 = OpConstant %12 1 + %18 = OpConstant %6 2 + %20 = OpTypeVector %6 2 + %21 = OpTypePointer Function %20 + %32 = OpTypeBool + %36 = OpConstant %7 3 + %37 = OpTypeArray %20 %36 + %38 = OpTypePointer Private %37 + %39 = OpVariable %38 Private + %40 = OpConstant %6 3 + %41 = OpConstantComposite %20 %40 %40 + %42 = OpTypePointer Private %20 + %44 = OpConstant %12 2 + %47 = OpTypeArray %32 %36 + %48 = OpTypePointer Function %47 + %50 = OpConstantTrue %32 + %51 = OpTypePointer Function %32 + %56 = OpTypeVector %7 3 + %57 = OpTypeArray %56 %8 + %58 = OpTypeArray %57 %8 + %59 = OpTypePointer Function %58 + %61 = OpConstant %7 4 + %62 = OpConstantComposite %56 %61 %61 %61 + %63 = OpTypePointer Function %56 + %65 = OpConstant %7 5 + %66 = OpConstantComposite %56 %65 %65 %65 + %67 = OpConstant %7 6 + %68 = OpConstantComposite %56 %67 %67 %67 + %69 = OpConstantComposite %57 %66 %68 + %100 = OpUndef %57 + %70 = OpTypePointer Function %57 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %22 = OpVariable %21 Function + %49 = OpVariable %48 Function + %60 = OpVariable %59 Function + %16 = OpAccessChain %15 %11 %13 + OpStore %16 %14 + %19 = OpAccessChain %15 %11 %17 + OpStore %19 %18 + %23 = OpAccessChain %15 %11 %13 + %24 = OpLoad %6 %23 + %25 = OpAccessChain %15 %11 %17 + %26 = OpLoad %6 %25 + %27 = OpCompositeConstruct %20 %24 %26 + OpStore %22 %27 + %28 = OpAccessChain %15 %11 %13 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %15 %11 %17 + %31 = OpLoad %6 %30 + %33 = OpFOrdGreaterThan %32 %29 %31 + %202 = OpCompositeConstruct %47 %33 %50 %50 + OpSelectionMerge %35 None + OpBranchConditional %33 %34 %35 + %34 = OpLabel + %43 = OpAccessChain %42 %39 %17 + OpStore %43 %41 + %45 = OpLoad %20 %22 + %200 = OpCompositeConstruct %37 %41 %45 %27 + %46 = OpAccessChain %42 %39 %44 + OpStore %46 %45 + OpBranch %35 + %35 = OpLabel + %52 = OpAccessChain %51 %49 %13 + OpStore %52 %50 + %53 = OpAccessChain %51 %49 %13 + %54 = OpLoad %32 %53 + %55 = OpAccessChain %51 %49 %17 + OpStore %55 %54 + %64 = OpAccessChain %63 %60 %13 %13 + %203 = OpCompositeConstruct %58 %69 %100 + OpStore %64 %62 + %71 = OpAccessChain %70 %60 %17 + %201 = OpCompositeConstruct %9 %24 %40 + OpStore %71 %69 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationCompositeConstructTest, ConstructMatrices) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "v1" + OpName %12 "v2" + OpName %14 "v3" + OpName %19 "v4" + OpName %26 "v5" + OpName %29 "v6" + OpName %34 "m34" + OpName %37 "m43" + OpName %43 "vecs" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 + %17 = OpTypeVector %6 4 + %18 = OpTypePointer Function %17 + %21 = OpConstant %6 2 + %32 = OpTypeMatrix %17 3 + %33 = OpTypePointer Private %32 + %34 = OpVariable %33 Private + %35 = OpTypeMatrix %7 4 + %36 = OpTypePointer Private %35 + %37 = OpVariable %36 Private + %38 = OpTypeVector %6 2 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 3 + %41 = OpTypeArray %38 %40 + %42 = OpTypePointer Private %41 + %43 = OpVariable %42 Private + %100 = OpUndef %7 + %101 = OpUndef %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %12 = OpVariable %8 Function + %14 = OpVariable %8 Function + %19 = OpVariable %18 Function + %26 = OpVariable %18 Function + %29 = OpVariable %18 Function + OpStore %9 %11 + %13 = OpLoad %7 %9 + OpStore %12 %13 + %15 = OpLoad %7 %12 + %16 = OpVectorShuffle %7 %15 %15 2 1 0 + OpStore %14 %16 + %20 = OpLoad %7 %14 + %22 = OpCompositeExtract %6 %20 0 + %23 = OpCompositeExtract %6 %20 1 + %24 = OpCompositeExtract %6 %20 2 + %25 = OpCompositeConstruct %17 %22 %23 %24 %21 + OpStore %19 %25 + %27 = OpLoad %17 %19 + %28 = OpVectorShuffle %17 %27 %27 3 2 1 0 + OpStore %26 %28 + %30 = OpLoad %7 %9 + %31 = OpVectorShuffle %17 %30 %30 0 0 1 1 + OpStore %29 %31 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // make a mat3x4 + TransformationCompositeConstruct make_mat34( + 32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); + // Bad: %35 is mat4x3, not mat3x4. + TransformationCompositeConstruct make_mat34_bad( + 35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); + ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_mat34_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_mat34, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2}))); + + // make a mat4x3 + TransformationCompositeConstruct make_mat43( + 35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201); + // Bad: %25 does not match the matrix's column type. + TransformationCompositeConstruct make_mat43_bad( + 35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201); + ASSERT_TRUE(make_mat43.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_mat43_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_mat43, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(16, {}), MakeDataDescriptor(201, {2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {}), MakeDataDescriptor(201, {3}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "v1" + OpName %12 "v2" + OpName %14 "v3" + OpName %19 "v4" + OpName %26 "v5" + OpName %29 "v6" + OpName %34 "m34" + OpName %37 "m43" + OpName %43 "vecs" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 + %17 = OpTypeVector %6 4 + %18 = OpTypePointer Function %17 + %21 = OpConstant %6 2 + %32 = OpTypeMatrix %17 3 + %33 = OpTypePointer Private %32 + %34 = OpVariable %33 Private + %35 = OpTypeMatrix %7 4 + %36 = OpTypePointer Private %35 + %37 = OpVariable %36 Private + %38 = OpTypeVector %6 2 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 3 + %41 = OpTypeArray %38 %40 + %42 = OpTypePointer Private %41 + %43 = OpVariable %42 Private + %100 = OpUndef %7 + %101 = OpUndef %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %12 = OpVariable %8 Function + %14 = OpVariable %8 Function + %19 = OpVariable %18 Function + %26 = OpVariable %18 Function + %29 = OpVariable %18 Function + OpStore %9 %11 + %13 = OpLoad %7 %9 + OpStore %12 %13 + %15 = OpLoad %7 %12 + %16 = OpVectorShuffle %7 %15 %15 2 1 0 + OpStore %14 %16 + %20 = OpLoad %7 %14 + %22 = OpCompositeExtract %6 %20 0 + %23 = OpCompositeExtract %6 %20 1 + %24 = OpCompositeExtract %6 %20 2 + %25 = OpCompositeConstruct %17 %22 %23 %24 %21 + OpStore %19 %25 + %27 = OpLoad %17 %19 + %28 = OpVectorShuffle %17 %27 %27 3 2 1 0 + OpStore %26 %28 + %30 = OpLoad %7 %9 + %31 = OpVectorShuffle %17 %30 %30 0 0 1 1 + %201 = OpCompositeConstruct %35 %11 %13 %16 %100 + OpStore %29 %31 + %200 = OpCompositeConstruct %32 %25 %28 %31 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationCompositeConstructTest, ConstructStructs) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "Inner" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpName %11 "i1" + OpName %22 "i2" + OpName %33 "Outer" + OpMemberName %33 0 "c" + OpMemberName %33 1 "d" + OpMemberName %33 2 "e" + OpName %35 "o" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeInt 32 1 + %9 = OpTypeStruct %7 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %8 0 + %13 = OpConstant %6 2 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpTypePointer Function %6 + %18 = OpConstant %8 1 + %19 = OpConstant %8 3 + %20 = OpTypePointer Function %8 + %23 = OpTypePointer Function %7 + %31 = OpConstant %14 2 + %32 = OpTypeArray %9 %31 + %33 = OpTypeStruct %32 %9 %6 + %34 = OpTypePointer Function %33 + %36 = OpConstant %6 1 + %37 = OpConstantComposite %7 %36 %13 + %38 = OpConstant %8 2 + %39 = OpConstantComposite %9 %37 %38 + %40 = OpConstant %6 3 + %41 = OpConstant %6 4 + %42 = OpConstantComposite %7 %40 %41 + %56 = OpConstant %6 5 + %100 = OpUndef %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %22 = OpVariable %10 Function + %35 = OpVariable %34 Function + %17 = OpAccessChain %16 %11 %12 %15 + OpStore %17 %13 + %21 = OpAccessChain %20 %11 %18 + OpStore %21 %19 + %24 = OpAccessChain %23 %11 %12 + %25 = OpLoad %7 %24 + %26 = OpAccessChain %23 %22 %12 + OpStore %26 %25 + %27 = OpAccessChain %20 %11 %18 + %28 = OpLoad %8 %27 + %29 = OpIAdd %8 %28 %18 + %30 = OpAccessChain %20 %22 %18 + OpStore %30 %29 + %43 = OpAccessChain %20 %11 %18 + %44 = OpLoad %8 %43 + %45 = OpCompositeConstruct %9 %42 %44 + %46 = OpCompositeConstruct %32 %39 %45 + %47 = OpLoad %9 %22 + %48 = OpCompositeConstruct %33 %46 %47 %40 + OpStore %35 %48 + %49 = OpLoad %9 %11 + %50 = OpAccessChain %10 %35 %12 %12 + OpStore %50 %49 + %51 = OpLoad %9 %22 + %52 = OpAccessChain %10 %35 %12 %18 + OpStore %52 %51 + %53 = OpAccessChain %10 %35 %12 %12 + %54 = OpLoad %9 %53 + %55 = OpAccessChain %10 %35 %18 + OpStore %55 %54 + %57 = OpAccessChain %16 %35 %38 + OpStore %57 %56 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // make an Inner + TransformationCompositeConstruct make_inner( + 9, {25, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200); + // Bad: Too few fields to make the struct. + TransformationCompositeConstruct make_inner_bad( + 9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200); + ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_inner_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_inner, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1}))); + + // make an Outer + TransformationCompositeConstruct make_outer( + 33, {46, 200, 56}, MakeInstructionDescriptor(200, SpvOpAccessChain, 0), + 201); + // Bad: %200 is not available at the desired program point. + TransformationCompositeConstruct make_outer_bad( + 33, {46, 200, 56}, + MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201); + ASSERT_TRUE(make_outer.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_outer_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_outer, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(46, {}), MakeDataDescriptor(201, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(200, {}), MakeDataDescriptor(201, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "Inner" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpName %11 "i1" + OpName %22 "i2" + OpName %33 "Outer" + OpMemberName %33 0 "c" + OpMemberName %33 1 "d" + OpMemberName %33 2 "e" + OpName %35 "o" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypeInt 32 1 + %9 = OpTypeStruct %7 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %8 0 + %13 = OpConstant %6 2 + %14 = OpTypeInt 32 0 + %15 = OpConstant %14 0 + %16 = OpTypePointer Function %6 + %18 = OpConstant %8 1 + %19 = OpConstant %8 3 + %20 = OpTypePointer Function %8 + %23 = OpTypePointer Function %7 + %31 = OpConstant %14 2 + %32 = OpTypeArray %9 %31 + %33 = OpTypeStruct %32 %9 %6 + %34 = OpTypePointer Function %33 + %36 = OpConstant %6 1 + %37 = OpConstantComposite %7 %36 %13 + %38 = OpConstant %8 2 + %39 = OpConstantComposite %9 %37 %38 + %40 = OpConstant %6 3 + %41 = OpConstant %6 4 + %42 = OpConstantComposite %7 %40 %41 + %56 = OpConstant %6 5 + %100 = OpUndef %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %22 = OpVariable %10 Function + %35 = OpVariable %34 Function + %17 = OpAccessChain %16 %11 %12 %15 + OpStore %17 %13 + %21 = OpAccessChain %20 %11 %18 + OpStore %21 %19 + %24 = OpAccessChain %23 %11 %12 + %25 = OpLoad %7 %24 + %26 = OpAccessChain %23 %22 %12 + OpStore %26 %25 + %27 = OpAccessChain %20 %11 %18 + %28 = OpLoad %8 %27 + %29 = OpIAdd %8 %28 %18 + %30 = OpAccessChain %20 %22 %18 + OpStore %30 %29 + %43 = OpAccessChain %20 %11 %18 + %44 = OpLoad %8 %43 + %45 = OpCompositeConstruct %9 %42 %44 + %46 = OpCompositeConstruct %32 %39 %45 + %47 = OpLoad %9 %22 + %48 = OpCompositeConstruct %33 %46 %47 %40 + OpStore %35 %48 + %49 = OpLoad %9 %11 + %50 = OpAccessChain %10 %35 %12 %12 + OpStore %50 %49 + %51 = OpLoad %9 %22 + %52 = OpAccessChain %10 %35 %12 %18 + OpStore %52 %51 + %53 = OpAccessChain %10 %35 %12 %12 + %54 = OpLoad %9 %53 + %55 = OpAccessChain %10 %35 %18 + OpStore %55 %54 + %200 = OpCompositeConstruct %9 %25 %19 + %201 = OpCompositeConstruct %33 %46 %200 %56 + %57 = OpAccessChain %16 %35 %38 + OpStore %57 %56 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationCompositeConstructTest, ConstructVectors) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "v2" + OpName %27 "v3" + OpName %46 "v4" + OpName %53 "iv2" + OpName %61 "uv3" + OpName %72 "bv4" + OpName %88 "uv2" + OpName %95 "bv3" + OpName %104 "bv2" + OpName %116 "iv3" + OpName %124 "iv4" + OpName %133 "uv4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Function %6 + %18 = OpConstant %13 1 + %21 = OpTypeBool + %25 = OpTypeVector %6 3 + %26 = OpTypePointer Function %25 + %33 = OpConstant %6 3 + %34 = OpConstant %6 -0.756802499 + %38 = OpConstant %13 2 + %44 = OpTypeVector %6 4 + %45 = OpTypePointer Function %44 + %50 = OpTypeInt 32 1 + %51 = OpTypeVector %50 2 + %52 = OpTypePointer Function %51 + %57 = OpTypePointer Function %50 + %59 = OpTypeVector %13 3 + %60 = OpTypePointer Function %59 + %65 = OpConstant %13 3 + %67 = OpTypePointer Function %13 + %70 = OpTypeVector %21 4 + %71 = OpTypePointer Function %70 + %73 = OpConstantTrue %21 + %74 = OpTypePointer Function %21 + %86 = OpTypeVector %13 2 + %87 = OpTypePointer Function %86 + %93 = OpTypeVector %21 3 + %94 = OpTypePointer Function %93 + %102 = OpTypeVector %21 2 + %103 = OpTypePointer Function %102 + %111 = OpConstantFalse %21 + %114 = OpTypeVector %50 3 + %115 = OpTypePointer Function %114 + %117 = OpConstant %50 3 + %122 = OpTypeVector %50 4 + %123 = OpTypePointer Function %122 + %131 = OpTypeVector %13 4 + %132 = OpTypePointer Function %131 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %27 = OpVariable %26 Function + %46 = OpVariable %45 Function + %53 = OpVariable %52 Function + %61 = OpVariable %60 Function + %72 = OpVariable %71 Function + %88 = OpVariable %87 Function + %95 = OpVariable %94 Function + %104 = OpVariable %103 Function + %116 = OpVariable %115 Function + %124 = OpVariable %123 Function + %133 = OpVariable %132 Function + OpStore %9 %12 + %16 = OpAccessChain %15 %9 %14 + %17 = OpLoad %6 %16 + %19 = OpAccessChain %15 %9 %18 + %20 = OpLoad %6 %19 + %22 = OpFOrdGreaterThan %21 %17 %20 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %101 + %23 = OpLabel + %28 = OpAccessChain %15 %9 %14 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %15 %9 %18 + %31 = OpLoad %6 %30 + %32 = OpFAdd %6 %29 %31 + %35 = OpCompositeConstruct %25 %32 %33 %34 + OpStore %27 %35 + %36 = OpAccessChain %15 %27 %14 + %37 = OpLoad %6 %36 + %39 = OpAccessChain %15 %27 %38 + %40 = OpLoad %6 %39 + %41 = OpFOrdLessThan %21 %37 %40 + OpSelectionMerge %43 None + OpBranchConditional %41 %42 %69 + %42 = OpLabel + %47 = OpAccessChain %15 %9 %18 + %48 = OpLoad %6 %47 + %49 = OpAccessChain %15 %46 %14 + OpStore %49 %48 + %54 = OpAccessChain %15 %27 %38 + %55 = OpLoad %6 %54 + %56 = OpConvertFToS %50 %55 + %58 = OpAccessChain %57 %53 %14 + OpStore %58 %56 + %62 = OpAccessChain %15 %46 %14 + %63 = OpLoad %6 %62 + %64 = OpConvertFToU %13 %63 + %66 = OpIAdd %13 %64 %65 + %68 = OpAccessChain %67 %61 %14 + OpStore %68 %66 + OpBranch %43 + %69 = OpLabel + %75 = OpAccessChain %74 %72 %14 + OpStore %75 %73 + %76 = OpAccessChain %74 %72 %14 + %77 = OpLoad %21 %76 + %78 = OpLogicalNot %21 %77 + %79 = OpAccessChain %74 %72 %18 + OpStore %79 %78 + %80 = OpAccessChain %74 %72 %14 + %81 = OpLoad %21 %80 + %82 = OpAccessChain %74 %72 %18 + %83 = OpLoad %21 %82 + %84 = OpLogicalAnd %21 %81 %83 + %85 = OpAccessChain %74 %72 %38 + OpStore %85 %84 + %89 = OpAccessChain %67 %88 %14 + %90 = OpLoad %13 %89 + %91 = OpINotEqual %21 %90 %14 + %92 = OpAccessChain %74 %72 %65 + OpStore %92 %91 + OpBranch %43 + %43 = OpLabel + %96 = OpLoad %70 %72 + %97 = OpCompositeExtract %21 %96 0 + %98 = OpCompositeExtract %21 %96 1 + %99 = OpCompositeExtract %21 %96 2 + %100 = OpCompositeConstruct %93 %97 %98 %99 + OpStore %95 %100 + OpBranch %24 + %101 = OpLabel + %105 = OpAccessChain %67 %88 %14 + %106 = OpLoad %13 %105 + %107 = OpINotEqual %21 %106 %14 + %108 = OpCompositeConstruct %102 %107 %107 + OpStore %104 %108 + OpBranch %24 + %24 = OpLabel + %109 = OpAccessChain %74 %104 %18 + %110 = OpLoad %21 %109 + %112 = OpLogicalOr %21 %110 %111 + %113 = OpAccessChain %74 %104 %14 + OpStore %113 %112 + %118 = OpAccessChain %57 %116 %14 + OpStore %118 %117 + %119 = OpAccessChain %57 %116 %14 + %120 = OpLoad %50 %119 + %121 = OpAccessChain %57 %53 %18 + OpStore %121 %120 + %125 = OpAccessChain %57 %116 %14 + %126 = OpLoad %50 %125 + %127 = OpAccessChain %57 %53 %18 + %128 = OpLoad %50 %127 + %129 = OpIAdd %50 %126 %128 + %130 = OpAccessChain %57 %124 %65 + OpStore %130 %129 + %134 = OpAccessChain %57 %116 %14 + %135 = OpLoad %50 %134 + %136 = OpBitcast %13 %135 + %137 = OpAccessChain %67 %133 %14 + OpStore %137 %136 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationCompositeConstruct make_vec2( + 7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200); + // Bad: not enough data for a vec2 + TransformationCompositeConstruct make_vec2_bad( + 7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200); + ASSERT_TRUE(make_vec2.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_vec2_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_vec2, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1}))); + + TransformationCompositeConstruct make_vec3( + 25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), + 201); + // Bad: too much data for a vec3 + TransformationCompositeConstruct make_vec3_bad( + 25, {12, 32, 32}, + MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201); + ASSERT_TRUE(make_vec3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_vec3_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_vec3, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {0}), MakeDataDescriptor(201, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {1}), MakeDataDescriptor(201, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2}))); + + TransformationCompositeConstruct make_vec4( + 44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0), + 202); + // Bad: id 48 is not available at the insertion points + TransformationCompositeConstruct make_vec4_bad( + 44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0), + 202); + ASSERT_TRUE(make_vec4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_vec4_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_vec4, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3}))); + + TransformationCompositeConstruct make_ivec2( + 51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203); + // Bad: if 128 is not available at the instruction that defines 128 + TransformationCompositeConstruct make_ivec2_bad( + 51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203); + ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_ivec2_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_ivec2, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(126, {}), MakeDataDescriptor(203, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(120, {}), MakeDataDescriptor(203, {1}))); + + TransformationCompositeConstruct make_ivec3( + 114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0), + 204); + // Bad because 1300 is not an id + TransformationCompositeConstruct make_ivec3_bad( + 114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0), + 204); + ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_ivec3_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_ivec3, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {}), MakeDataDescriptor(204, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2}))); + + TransformationCompositeConstruct make_ivec4( + 122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0), + 205); + // Bad because 86 is the wrong type. + TransformationCompositeConstruct make_ivec4_bad( + 86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0), + 205); + ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_ivec4_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_ivec4, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(56, {}), MakeDataDescriptor(205, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {3}))); + + TransformationCompositeConstruct make_uvec2( + 86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206); + TransformationCompositeConstruct make_uvec2_bad( + 86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206); + ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_uvec2_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_uvec2, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1}))); + + TransformationCompositeConstruct make_uvec3( + 59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207); + // Bad because 1300 is not an id + TransformationCompositeConstruct make_uvec3_bad( + 59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207); + ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_uvec3_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_uvec3, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(18, {}), MakeDataDescriptor(207, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(136, {}), MakeDataDescriptor(207, {2}))); + + TransformationCompositeConstruct make_uvec4( + 131, {14, 18, 136, 136}, + MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208); + // Bad because 86 is the wrong type. + TransformationCompositeConstruct make_uvec4_bad( + 86, {14, 18, 136, 136}, + MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208); + ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_uvec4_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_uvec4, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(18, {}), MakeDataDescriptor(208, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {3}))); + + TransformationCompositeConstruct make_bvec2( + 102, + { + 111, + 41, + }, + MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 209); + // Bad because 0 is not a valid base instruction id + TransformationCompositeConstruct make_bvec2_bad( + 102, + { + 111, + 41, + }, + MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209); + ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_bvec2_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_bvec2, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(111, {}), MakeDataDescriptor(209, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1}))); + + TransformationCompositeConstruct make_bvec3( + 93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210); + // Bad because there are too many components for a bvec3 + TransformationCompositeConstruct make_bvec3_bad( + 93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210); + ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_bvec3_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_bvec3, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {0}), MakeDataDescriptor(210, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {1}), MakeDataDescriptor(210, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2}))); + + TransformationCompositeConstruct make_bvec4( + 70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211); + // Bad because 21 is a type, not a result id + TransformationCompositeConstruct make_bvec4_bad( + 70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211); + ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + make_bvec4_bad.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(make_bvec4, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {3}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "v2" + OpName %27 "v3" + OpName %46 "v4" + OpName %53 "iv2" + OpName %61 "uv3" + OpName %72 "bv4" + OpName %88 "uv2" + OpName %95 "bv3" + OpName %104 "bv2" + OpName %116 "iv3" + OpName %124 "iv4" + OpName %133 "uv4" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Function %6 + %18 = OpConstant %13 1 + %21 = OpTypeBool + %25 = OpTypeVector %6 3 + %26 = OpTypePointer Function %25 + %33 = OpConstant %6 3 + %34 = OpConstant %6 -0.756802499 + %38 = OpConstant %13 2 + %44 = OpTypeVector %6 4 + %45 = OpTypePointer Function %44 + %50 = OpTypeInt 32 1 + %51 = OpTypeVector %50 2 + %52 = OpTypePointer Function %51 + %57 = OpTypePointer Function %50 + %59 = OpTypeVector %13 3 + %60 = OpTypePointer Function %59 + %65 = OpConstant %13 3 + %67 = OpTypePointer Function %13 + %70 = OpTypeVector %21 4 + %71 = OpTypePointer Function %70 + %73 = OpConstantTrue %21 + %74 = OpTypePointer Function %21 + %86 = OpTypeVector %13 2 + %87 = OpTypePointer Function %86 + %93 = OpTypeVector %21 3 + %94 = OpTypePointer Function %93 + %102 = OpTypeVector %21 2 + %103 = OpTypePointer Function %102 + %111 = OpConstantFalse %21 + %114 = OpTypeVector %50 3 + %115 = OpTypePointer Function %114 + %117 = OpConstant %50 3 + %122 = OpTypeVector %50 4 + %123 = OpTypePointer Function %122 + %131 = OpTypeVector %13 4 + %132 = OpTypePointer Function %131 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %27 = OpVariable %26 Function + %46 = OpVariable %45 Function + %53 = OpVariable %52 Function + %61 = OpVariable %60 Function + %72 = OpVariable %71 Function + %88 = OpVariable %87 Function + %95 = OpVariable %94 Function + %104 = OpVariable %103 Function + %116 = OpVariable %115 Function + %124 = OpVariable %123 Function + %133 = OpVariable %132 Function + OpStore %9 %12 + %206 = OpCompositeConstruct %86 %18 %38 + %16 = OpAccessChain %15 %9 %14 + %17 = OpLoad %6 %16 + %19 = OpAccessChain %15 %9 %18 + %20 = OpLoad %6 %19 + %22 = OpFOrdGreaterThan %21 %17 %20 + OpSelectionMerge %24 None + OpBranchConditional %22 %23 %101 + %23 = OpLabel + %28 = OpAccessChain %15 %9 %14 + %29 = OpLoad %6 %28 + %30 = OpAccessChain %15 %9 %18 + %31 = OpLoad %6 %30 + %32 = OpFAdd %6 %29 %31 + %201 = OpCompositeConstruct %25 %12 %32 + %35 = OpCompositeConstruct %25 %32 %33 %34 + OpStore %27 %35 + %36 = OpAccessChain %15 %27 %14 + %37 = OpLoad %6 %36 + %39 = OpAccessChain %15 %27 %38 + %40 = OpLoad %6 %39 + %41 = OpFOrdLessThan %21 %37 %40 + OpSelectionMerge %43 None + OpBranchConditional %41 %42 %69 + %42 = OpLabel + %47 = OpAccessChain %15 %9 %18 + %48 = OpLoad %6 %47 + %49 = OpAccessChain %15 %46 %14 + OpStore %49 %48 + %54 = OpAccessChain %15 %27 %38 + %55 = OpLoad %6 %54 + %56 = OpConvertFToS %50 %55 + %58 = OpAccessChain %57 %53 %14 + OpStore %58 %56 + %62 = OpAccessChain %15 %46 %14 + %63 = OpLoad %6 %62 + %64 = OpConvertFToU %13 %63 + %205 = OpCompositeConstruct %122 %56 %117 %117 %117 + %66 = OpIAdd %13 %64 %65 + %204 = OpCompositeConstruct %114 %56 %117 %56 + %68 = OpAccessChain %67 %61 %14 + OpStore %68 %66 + OpBranch %43 + %69 = OpLabel + %202 = OpCompositeConstruct %44 %32 %32 %10 %11 + %209 = OpCompositeConstruct %102 %111 %41 + %75 = OpAccessChain %74 %72 %14 + OpStore %75 %73 + %76 = OpAccessChain %74 %72 %14 + %77 = OpLoad %21 %76 + %78 = OpLogicalNot %21 %77 + %79 = OpAccessChain %74 %72 %18 + OpStore %79 %78 + %80 = OpAccessChain %74 %72 %14 + %81 = OpLoad %21 %80 + %82 = OpAccessChain %74 %72 %18 + %83 = OpLoad %21 %82 + %84 = OpLogicalAnd %21 %81 %83 + %85 = OpAccessChain %74 %72 %38 + OpStore %85 %84 + %89 = OpAccessChain %67 %88 %14 + %90 = OpLoad %13 %89 + %91 = OpINotEqual %21 %90 %14 + %92 = OpAccessChain %74 %72 %65 + OpStore %92 %91 + OpBranch %43 + %43 = OpLabel + %96 = OpLoad %70 %72 + %97 = OpCompositeExtract %21 %96 0 + %98 = OpCompositeExtract %21 %96 1 + %99 = OpCompositeExtract %21 %96 2 + %100 = OpCompositeConstruct %93 %97 %98 %99 + %200 = OpCompositeConstruct %7 %17 %11 + OpStore %95 %100 + OpBranch %24 + %101 = OpLabel + %105 = OpAccessChain %67 %88 %14 + %106 = OpLoad %13 %105 + %107 = OpINotEqual %21 %106 %14 + %108 = OpCompositeConstruct %102 %107 %107 + %210 = OpCompositeConstruct %93 %108 %73 + OpStore %104 %108 + %211 = OpCompositeConstruct %70 %108 %108 + OpBranch %24 + %24 = OpLabel + %109 = OpAccessChain %74 %104 %18 + %110 = OpLoad %21 %109 + %112 = OpLogicalOr %21 %110 %111 + %113 = OpAccessChain %74 %104 %14 + OpStore %113 %112 + %118 = OpAccessChain %57 %116 %14 + OpStore %118 %117 + %119 = OpAccessChain %57 %116 %14 + %120 = OpLoad %50 %119 + %121 = OpAccessChain %57 %53 %18 + OpStore %121 %120 + %125 = OpAccessChain %57 %116 %14 + %126 = OpLoad %50 %125 + %127 = OpAccessChain %57 %53 %18 + %203 = OpCompositeConstruct %51 %126 %120 + %128 = OpLoad %50 %127 + %129 = OpIAdd %50 %126 %128 + %130 = OpAccessChain %57 %124 %65 + OpStore %130 %129 + %134 = OpAccessChain %57 %116 %14 + %135 = OpLoad %50 %134 + %136 = OpBitcast %13 %135 + %208 = OpCompositeConstruct %131 %14 %18 %136 %136 + %137 = OpAccessChain %67 %133 %14 + OpStore %137 %136 + %207 = OpCompositeConstruct %59 %14 %18 %136 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationCompositeConstructTest, AddSynonymsForRelevantIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 + %17 = OpTypeVector %6 4 + %18 = OpTypePointer Function %17 + %21 = OpConstant %6 2 + %32 = OpTypeMatrix %17 3 + %33 = OpTypePointer Private %32 + %34 = OpVariable %33 Private + %35 = OpTypeMatrix %7 4 + %36 = OpTypePointer Private %35 + %37 = OpVariable %36 Private + %38 = OpTypeVector %6 2 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 3 + %41 = OpTypeArray %38 %40 + %42 = OpTypePointer Private %41 + %43 = OpVariable %42 Private + %100 = OpUndef %7 + %101 = OpUndef %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %12 = OpVariable %8 Function + %14 = OpVariable %8 Function + %19 = OpVariable %18 Function + %26 = OpVariable %18 Function + %29 = OpVariable %18 Function + OpStore %9 %11 + %13 = OpLoad %7 %9 + OpStore %12 %13 + %15 = OpLoad %7 %12 + %16 = OpVectorShuffle %7 %15 %15 2 1 0 + OpStore %14 %16 + %20 = OpLoad %7 %14 + %22 = OpCompositeExtract %6 %20 0 + %23 = OpCompositeExtract %6 %20 1 + %24 = OpCompositeExtract %6 %20 2 + %25 = OpCompositeConstruct %17 %22 %23 %24 %21 + OpStore %19 %25 + %27 = OpLoad %17 %19 + %28 = OpVectorShuffle %17 %27 %27 3 2 1 0 + OpStore %26 %28 + %30 = OpLoad %7 %9 + %31 = OpVectorShuffle %17 %30 %30 0 0 1 1 + OpStore %29 %31 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationCompositeConstruct transformation( + 32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1}))); +} + +TEST(TransformationCompositeConstructTest, DontAddSynonymsForIrrelevantIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 1 + %11 = OpConstantComposite %7 %10 %10 %10 + %17 = OpTypeVector %6 4 + %18 = OpTypePointer Function %17 + %21 = OpConstant %6 2 + %32 = OpTypeMatrix %17 3 + %33 = OpTypePointer Private %32 + %34 = OpVariable %33 Private + %35 = OpTypeMatrix %7 4 + %36 = OpTypePointer Private %35 + %37 = OpVariable %36 Private + %38 = OpTypeVector %6 2 + %39 = OpTypeInt 32 0 + %40 = OpConstant %39 3 + %41 = OpTypeArray %38 %40 + %42 = OpTypePointer Private %41 + %43 = OpVariable %42 Private + %100 = OpUndef %7 + %101 = OpUndef %17 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %12 = OpVariable %8 Function + %14 = OpVariable %8 Function + %19 = OpVariable %18 Function + %26 = OpVariable %18 Function + %29 = OpVariable %18 Function + OpStore %9 %11 + %13 = OpLoad %7 %9 + OpStore %12 %13 + %15 = OpLoad %7 %12 + %16 = OpVectorShuffle %7 %15 %15 2 1 0 + OpStore %14 %16 + %20 = OpLoad %7 %14 + %22 = OpCompositeExtract %6 %20 0 + %23 = OpCompositeExtract %6 %20 1 + %24 = OpCompositeExtract %6 %20 2 + %25 = OpCompositeConstruct %17 %22 %23 %24 %21 + OpStore %19 %25 + %27 = OpLoad %17 %19 + %28 = OpVectorShuffle %17 %27 %27 3 2 1 0 + OpStore %26 %28 + %30 = OpLoad %7 %9 + %31 = OpVectorShuffle %17 %30 %30 0 0 1 1 + OpStore %29 %31 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(25); + + TransformationCompositeConstruct transformation( + 32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2}))); +} + +TEST(TransformationCompositeConstructTest, DontAddSynonymsInDeadBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstant %6 1 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeBool + %14 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %16 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactBlockIsDead(15); + + TransformationCompositeConstruct transformation( + 7, {10, 11}, MakeInstructionDescriptor(15, SpvOpBranch, 0), 100); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {0}), MakeDataDescriptor(10, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(11, {}))); +} + +TEST(TransformationCompositeConstructTest, OneIrrelevantComponent) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 %6 %6 + %8 = OpConstant %6 42 + %9 = OpConstant %6 50 + %10 = OpConstant %6 51 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(8); + + TransformationCompositeConstruct transformation( + 7, {8, 9, 10}, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {0}), MakeDataDescriptor(8, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(9, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {2}), MakeDataDescriptor(10, {}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_composite_extract_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_composite_extract_test.cpp new file mode 100644 index 0000000..383a4db --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_composite_extract_test.cpp @@ -0,0 +1,640 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_composite_extract.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationCompositeExtractTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %17 "FunnyPoint" + OpMemberName %17 0 "x" + OpMemberName %17 1 "y" + OpMemberName %17 2 "z" + OpName %19 "p" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %12 = OpTypeBool + %16 = OpTypeFloat 32 + %17 = OpTypeStruct %16 %16 %6 + %81 = OpTypeStruct %17 %16 + %18 = OpTypePointer Function %17 + %20 = OpConstant %6 0 + %23 = OpTypePointer Function %16 + %26 = OpConstant %6 1 + %30 = OpConstant %6 2 + %80 = OpUndef %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %19 = OpVariable %18 Function + %9 = OpLoad %6 %8 + %11 = OpLoad %6 %10 + %100 = OpCompositeConstruct %17 %80 %80 %26 + %104 = OpCompositeConstruct %81 %100 %80 + %13 = OpIEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %25 + %14 = OpLabel + %21 = OpLoad %6 %8 + %22 = OpConvertSToF %16 %21 + %101 = OpCompositeConstruct %17 %22 %80 %30 + %24 = OpAccessChain %23 %19 %20 + OpStore %24 %22 + OpBranch %15 + %25 = OpLabel + %27 = OpLoad %6 %10 + %28 = OpConvertSToF %16 %27 + %102 = OpCompositeConstruct %17 %80 %28 %27 + %29 = OpAccessChain %23 %19 %26 + OpStore %29 %28 + OpBranch %15 + %15 = OpLabel + %31 = OpAccessChain %23 %19 %20 + %32 = OpLoad %16 %31 + %33 = OpAccessChain %23 %19 %26 + %34 = OpLoad %16 %33 + %103 = OpCompositeConstruct %17 %34 %32 %9 + %35 = OpFAdd %16 %32 %34 + %36 = OpConvertFToS %6 %35 + %37 = OpAccessChain %7 %19 %30 + OpStore %37 %36 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Instruction does not exist. + ASSERT_FALSE(TransformationCompositeExtract( + MakeInstructionDescriptor(36, SpvOpIAdd, 0), 200, 101, {0}) + .IsApplicable(context.get(), transformation_context)); + + // Id for composite is not a composite. + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 32, {}) + .IsApplicable(context.get(), transformation_context)); + + // Composite does not dominate instruction being inserted before. + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 200, 101, {0}) + .IsApplicable(context.get(), transformation_context)); + + // Too many indices for extraction from struct composite. + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 200, 101, {0, 0}) + .IsApplicable(context.get(), transformation_context)); + + // Too many indices for extraction from struct composite. + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 0, 0}) + .IsApplicable(context.get(), transformation_context)); + + // Out of bounds index for extraction from struct composite. + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(13, SpvOpIEqual, 0), 200, 104, {0, 3}) + .IsApplicable(context.get(), transformation_context)); + + // Result id already used. + ASSERT_FALSE(TransformationCompositeExtract( + MakeInstructionDescriptor(35, SpvOpFAdd, 0), 80, 103, {0}) + .IsApplicable(context.get(), transformation_context)); + + TransformationCompositeExtract transformation_1( + MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2}); + ASSERT_TRUE( + transformation_1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationCompositeExtract transformation_2( + MakeInstructionDescriptor(37, SpvOpAccessChain, 0), 202, 104, {0, 2}); + ASSERT_TRUE( + transformation_2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationCompositeExtract transformation_3( + MakeInstructionDescriptor(29, SpvOpAccessChain, 0), 203, 104, {0}); + ASSERT_TRUE( + transformation_3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation_3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationCompositeExtract transformation_4( + MakeInstructionDescriptor(24, SpvOpStore, 0), 204, 101, {0}); + ASSERT_TRUE( + transformation_4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation_4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationCompositeExtract transformation_5( + MakeInstructionDescriptor(29, SpvOpBranch, 0), 205, 102, {2}); + ASSERT_TRUE( + transformation_5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation_5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationCompositeExtract transformation_6( + MakeInstructionDescriptor(37, SpvOpReturn, 0), 206, 103, {1}); + ASSERT_TRUE( + transformation_6.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation_6, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {}), MakeDataDescriptor(100, {2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(202, {}), MakeDataDescriptor(104, {0, 2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(203, {}), MakeDataDescriptor(104, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(204, {}), MakeDataDescriptor(101, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(205, {}), MakeDataDescriptor(102, {2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(206, {}), MakeDataDescriptor(103, {1}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %17 "FunnyPoint" + OpMemberName %17 0 "x" + OpMemberName %17 1 "y" + OpMemberName %17 2 "z" + OpName %19 "p" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %12 = OpTypeBool + %16 = OpTypeFloat 32 + %17 = OpTypeStruct %16 %16 %6 + %81 = OpTypeStruct %17 %16 + %18 = OpTypePointer Function %17 + %20 = OpConstant %6 0 + %23 = OpTypePointer Function %16 + %26 = OpConstant %6 1 + %30 = OpConstant %6 2 + %80 = OpUndef %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %19 = OpVariable %18 Function + %9 = OpLoad %6 %8 + %11 = OpLoad %6 %10 + %100 = OpCompositeConstruct %17 %80 %80 %26 + %104 = OpCompositeConstruct %81 %100 %80 + %13 = OpIEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %25 + %14 = OpLabel + %21 = OpLoad %6 %8 + %22 = OpConvertSToF %16 %21 + %101 = OpCompositeConstruct %17 %22 %80 %30 + %24 = OpAccessChain %23 %19 %20 + %204 = OpCompositeExtract %16 %101 0 + OpStore %24 %22 + OpBranch %15 + %25 = OpLabel + %27 = OpLoad %6 %10 + %28 = OpConvertSToF %16 %27 + %102 = OpCompositeConstruct %17 %80 %28 %27 + %203 = OpCompositeExtract %17 %104 0 + %29 = OpAccessChain %23 %19 %26 + OpStore %29 %28 + %205 = OpCompositeExtract %6 %102 2 + OpBranch %15 + %15 = OpLabel + %31 = OpAccessChain %23 %19 %20 + %32 = OpLoad %16 %31 + %33 = OpAccessChain %23 %19 %26 + %34 = OpLoad %16 %33 + %103 = OpCompositeConstruct %17 %34 %32 %9 + %35 = OpFAdd %16 %32 %34 + %201 = OpCompositeExtract %6 %100 2 + %36 = OpConvertFToS %6 %35 + %202 = OpCompositeExtract %6 %104 0 2 + %37 = OpAccessChain %7 %19 %30 + OpStore %37 %36 + %206 = OpCompositeExtract %16 %103 1 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationCompositeExtractTest, IllegalInsertionPoints) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %51 %27 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %25 "buf" + OpMemberName %25 0 "value" + OpName %27 "" + OpName %51 "color" + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 0 + OpDecorate %51 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %10 = OpConstant %6 0.300000012 + %11 = OpConstant %6 0.400000006 + %12 = OpConstant %6 0.5 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %25 = OpTypeStruct %6 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %6 + %32 = OpTypeBool + %103 = OpConstantTrue %32 + %34 = OpConstant %6 0.100000001 + %48 = OpConstant %15 1 + %50 = OpTypePointer Output %7 + %51 = OpVariable %50 Output + %100 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %101 = OpVariable %100 Function + %102 = OpVariable %100 Function + OpBranch %19 + %19 = OpLabel + %60 = OpPhi %7 %14 %5 %58 %20 + %59 = OpPhi %15 %18 %5 %49 %20 + %29 = OpAccessChain %28 %27 %18 + %30 = OpLoad %6 %29 + %31 = OpConvertFToS %15 %30 + %33 = OpSLessThan %32 %59 %31 + OpLoopMerge %21 %20 None + OpBranchConditional %33 %20 %21 + %20 = OpLabel + %39 = OpCompositeExtract %6 %60 0 + %40 = OpFAdd %6 %39 %34 + %55 = OpCompositeInsert %7 %40 %60 0 + %44 = OpCompositeExtract %6 %60 1 + %45 = OpFSub %6 %44 %34 + %58 = OpCompositeInsert %7 %45 %55 1 + %49 = OpIAdd %15 %59 %48 + OpBranch %19 + %21 = OpLabel + OpStore %51 %60 + OpSelectionMerge %105 None + OpBranchConditional %103 %104 %105 + %104 = OpLabel + OpBranch %105 + %105 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Cannot insert before the OpVariables of a function. + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, {0}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, {1}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationCompositeExtract( + MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, {1}) + .IsApplicable(context.get(), transformation_context)); + // OK to insert right after the OpVariables. + ASSERT_FALSE(TransformationCompositeExtract( + MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, {1}) + .IsApplicable(context.get(), transformation_context)); + + // Cannot insert before the OpPhis of a block. + ASSERT_FALSE(TransformationCompositeExtract( + MakeInstructionDescriptor(60, SpvOpPhi, 0), 200, 14, {2}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationCompositeExtract( + MakeInstructionDescriptor(59, SpvOpPhi, 0), 200, 14, {3}) + .IsApplicable(context.get(), transformation_context)); + // OK to insert after the OpPhis. + ASSERT_TRUE( + TransformationCompositeExtract( + MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, {3}) + .IsApplicable(context.get(), transformation_context)); + + // Cannot insert before OpLoopMerge + ASSERT_FALSE(TransformationCompositeExtract( + MakeInstructionDescriptor(33, SpvOpBranchConditional, 0), + 200, 14, {3}) + .IsApplicable(context.get(), transformation_context)); + + // Cannot insert before OpSelectionMerge + ASSERT_FALSE(TransformationCompositeExtract( + MakeInstructionDescriptor(21, SpvOpBranchConditional, 0), + 200, 14, {2}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationCompositeExtractTest, AddSynonymsForRelevantIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %17 "FunnyPoint" + OpMemberName %17 0 "x" + OpMemberName %17 1 "y" + OpMemberName %17 2 "z" + OpName %19 "p" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %12 = OpTypeBool + %16 = OpTypeFloat 32 + %17 = OpTypeStruct %16 %16 %6 + %81 = OpTypeStruct %17 %16 + %18 = OpTypePointer Function %17 + %20 = OpConstant %6 0 + %23 = OpTypePointer Function %16 + %26 = OpConstant %6 1 + %30 = OpConstant %6 2 + %80 = OpUndef %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %19 = OpVariable %18 Function + %9 = OpLoad %6 %8 + %11 = OpLoad %6 %10 + %100 = OpCompositeConstruct %17 %80 %80 %26 + %104 = OpCompositeConstruct %81 %100 %80 + %13 = OpIEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %25 + %14 = OpLabel + %21 = OpLoad %6 %8 + %22 = OpConvertSToF %16 %21 + %101 = OpCompositeConstruct %17 %22 %80 %30 + %24 = OpAccessChain %23 %19 %20 + OpStore %24 %22 + OpBranch %15 + %25 = OpLabel + %27 = OpLoad %6 %10 + %28 = OpConvertSToF %16 %27 + %102 = OpCompositeConstruct %17 %80 %28 %27 + %29 = OpAccessChain %23 %19 %26 + OpStore %29 %28 + OpBranch %15 + %15 = OpLabel + %31 = OpAccessChain %23 %19 %20 + %32 = OpLoad %16 %31 + %33 = OpAccessChain %23 %19 %26 + %34 = OpLoad %16 %33 + %103 = OpCompositeConstruct %17 %34 %32 %9 + %35 = OpFAdd %16 %32 %34 + %36 = OpConvertFToS %6 %35 + %37 = OpAccessChain %7 %19 %30 + OpStore %37 %36 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationCompositeExtract transformation( + MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {}), MakeDataDescriptor(100, {2}))); +} + +TEST(TransformationCompositeExtractTest, DontAddSynonymsForIrrelevantIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %17 "FunnyPoint" + OpMemberName %17 0 "x" + OpMemberName %17 1 "y" + OpMemberName %17 2 "z" + OpName %19 "p" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %12 = OpTypeBool + %16 = OpTypeFloat 32 + %17 = OpTypeStruct %16 %16 %6 + %81 = OpTypeStruct %17 %16 + %18 = OpTypePointer Function %17 + %20 = OpConstant %6 0 + %23 = OpTypePointer Function %16 + %26 = OpConstant %6 1 + %30 = OpConstant %6 2 + %80 = OpUndef %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %19 = OpVariable %18 Function + %9 = OpLoad %6 %8 + %11 = OpLoad %6 %10 + %100 = OpCompositeConstruct %17 %80 %80 %26 + %104 = OpCompositeConstruct %81 %100 %80 + %13 = OpIEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %25 + %14 = OpLabel + %21 = OpLoad %6 %8 + %22 = OpConvertSToF %16 %21 + %101 = OpCompositeConstruct %17 %22 %80 %30 + %24 = OpAccessChain %23 %19 %20 + OpStore %24 %22 + OpBranch %15 + %25 = OpLabel + %27 = OpLoad %6 %10 + %28 = OpConvertSToF %16 %27 + %102 = OpCompositeConstruct %17 %80 %28 %27 + %29 = OpAccessChain %23 %19 %26 + OpStore %29 %28 + OpBranch %15 + %15 = OpLabel + %31 = OpAccessChain %23 %19 %20 + %32 = OpLoad %16 %31 + %33 = OpAccessChain %23 %19 %26 + %34 = OpLoad %16 %33 + %103 = OpCompositeConstruct %17 %34 %32 %9 + %35 = OpFAdd %16 %32 %34 + %36 = OpConvertFToS %6 %35 + %37 = OpAccessChain %7 %19 %30 + OpStore %37 %36 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(100); + TransformationCompositeExtract transformation( + MakeInstructionDescriptor(36, SpvOpConvertFToS, 0), 201, 100, {2}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {}), MakeDataDescriptor(100, {2}))); +} + +TEST(TransformationCompositeExtractTest, DontAddSynonymInDeadBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstant %6 1 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeBool + %14 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %16 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactBlockIsDead(15); + TransformationCompositeExtract transformation( + MakeInstructionDescriptor(15, SpvOpBranch, 0), 100, 12, {0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {}), MakeDataDescriptor(12, {0}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_composite_insert_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_composite_insert_test.cpp new file mode 100644 index 0000000..3b6f34d --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_composite_insert_test.cpp @@ -0,0 +1,920 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_composite_insert.h" + +#include "gtest/gtest.h" +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationCompositeInsertTest, NotApplicableScenarios) { + // This test handles cases where IsApplicable() returns false. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "base" + OpMemberName %12 0 "a1" + OpMemberName %12 1 "a2" + OpName %14 "b" + OpName %18 "level_1" + OpMemberName %18 0 "b1" + OpMemberName %18 1 "b2" + OpName %20 "l1" + OpName %24 "level_2" + OpMemberName %24 0 "c1" + OpMemberName %24 1 "c2" + OpName %26 "l2" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeStruct %6 %6 + %13 = OpTypePointer Function %12 + %18 = OpTypeStruct %12 %12 + %19 = OpTypePointer Function %18 + %24 = OpTypeStruct %18 %18 + %25 = OpTypePointer Function %24 + %30 = OpTypeBool + %31 = OpConstantTrue %30 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %26 = OpVariable %25 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + %21 = OpLoad %12 %14 + %22 = OpLoad %12 %14 + %23 = OpCompositeConstruct %18 %21 %22 + OpStore %20 %23 + %27 = OpLoad %18 %20 + %28 = OpLoad %18 %20 + %29 = OpCompositeConstruct %24 %27 %28 + OpStore %26 %29 + OpSelectionMerge %33 None + OpBranchConditional %31 %32 %33 + %32 = OpLabel + OpBranch %33 + %33 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: |fresh_id| is not fresh. + auto transformation_bad_1 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpStore, 0), 20, 29, 11, {1, 0, 0}); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: |composite_id| does not refer to a existing instruction. + auto transformation_bad_2 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 40, 11, {1, 0, 0}); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: |composite_id| does not refer to a composite value. + auto transformation_bad_3 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 9, 11, {1, 0, 0}); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); + + // Bad: |object_id| does not refer to a defined instruction. + auto transformation_bad_4 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 40, {1, 0, 0}); + ASSERT_FALSE( + transformation_bad_4.IsApplicable(context.get(), transformation_context)); + + // Bad: |object_id| cannot refer to a pointer. + auto transformation_bad_5 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 8, {1, 0, 0}); + ASSERT_FALSE( + transformation_bad_5.IsApplicable(context.get(), transformation_context)); + + // Bad: |index| is not a correct index. + auto transformation_bad_6 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {2, 0, 0}); + ASSERT_FALSE( + transformation_bad_6.IsApplicable(context.get(), transformation_context)); + + // Bad: Type id of the object to be inserted and the type id of the + // component at |index| are not the same. + auto transformation_bad_7 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 29, 11, {1, 0}); + ASSERT_FALSE( + transformation_bad_7.IsApplicable(context.get(), transformation_context)); + + // Bad: |instruction_to_insert_before| does not refer to a defined + // instruction. + auto transformation_bad_8 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpIMul, 0), 50, 29, 11, {1, 0, 0}); + ASSERT_FALSE( + transformation_bad_8.IsApplicable(context.get(), transformation_context)); + + // Bad: OpCompositeInsert cannot be inserted before OpBranchConditional with + // OpSelectionMerge above it. + auto transformation_bad_9 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpBranchConditional, 0), 50, 29, 11, + {1, 0, 0}); + ASSERT_FALSE( + transformation_bad_9.IsApplicable(context.get(), transformation_context)); + + // Bad: |composite_id| does not have a type_id. + auto transformation_bad_10 = TransformationCompositeInsert( + MakeInstructionDescriptor(29, SpvOpStore, 0), 50, 1, 11, {1, 0, 0}); + ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(), + transformation_context)); +} + +TEST(TransformationCompositeInsertTest, EmptyCompositeScenarios) { + // This test handles cases where either the composite is empty or the + // composite contains an empty composite. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "base" + OpMemberName %12 0 "a1" + OpMemberName %12 1 "a2" + OpName %14 "b" + %2 = OpTypeVoid + %60 = OpTypeStruct + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %61 = OpConstantComposite %60 + %62 = OpConstantComposite %60 + %12 = OpTypeStruct %6 %6 + %63 = OpTypeStruct %6 %60 + %13 = OpTypePointer Function %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + %64 = OpCompositeConstruct %63 %15 %61 + OpStore %14 %17 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: The composite with |composite_id| cannot be empty. + auto transformation_bad_1 = TransformationCompositeInsert( + MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 61, 62, {1}); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Good: It is possible to insert into a composite an element which is an + // empty composite. + auto transformation_good_1 = TransformationCompositeInsert( + MakeInstructionDescriptor(64, SpvOpStore, 0), 50, 64, 62, {1}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "base" + OpMemberName %12 0 "a1" + OpMemberName %12 1 "a2" + OpName %14 "b" + %2 = OpTypeVoid + %60 = OpTypeStruct + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %61 = OpConstantComposite %60 + %62 = OpConstantComposite %60 + %12 = OpTypeStruct %6 %6 + %63 = OpTypeStruct %6 %60 + %13 = OpTypePointer Function %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + %64 = OpCompositeConstruct %63 %15 %61 + %50 = OpCompositeInsert %63 %62 %64 1 + OpStore %14 %17 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationCompositeInsertTest, IrrelevantCompositeNoSynonyms) { + // This test handles cases where either |composite| is irrelevant. + // The transformation shouldn't create any synonyms. + // The member composite has a different number of elements than the parent + // composite. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "base" + OpMemberName %12 0 "a1" + OpMemberName %12 1 "a2" + OpName %14 "b" + OpName %18 "level_1" + OpMemberName %18 0 "b1" + OpMemberName %18 1 "b2" + OpMemberName %18 2 "b3" + OpName %20 "l1" + OpName %25 "level_2" + OpMemberName %25 0 "c1" + OpMemberName %25 1 "c2" + OpName %27 "l2" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeStruct %6 %6 + %13 = OpTypePointer Function %12 + %18 = OpTypeStruct %12 %12 %12 + %19 = OpTypePointer Function %18 + %25 = OpTypeStruct %18 %18 + %26 = OpTypePointer Function %25 + %31 = OpTypeBool + %32 = OpConstantTrue %31 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %27 = OpVariable %26 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + %21 = OpLoad %12 %14 + %22 = OpLoad %12 %14 + %23 = OpLoad %12 %14 + %24 = OpCompositeConstruct %18 %21 %22 %23 + OpStore %20 %24 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %20 + %30 = OpCompositeConstruct %25 %28 %29 + OpStore %27 %30 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %34 + %33 = OpLabel + OpBranch %34 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Add fact that the composite is irrelevant. + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(30); + + auto transformation_good_1 = TransformationCompositeInsert( + MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // No synonyms that involve the original object - %30 - should have been + // added. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); + // We *should* have a synonym between %11 and the component of %50 into which + // it has been inserted. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); +} + +TEST(TransformationCompositeInsertTest, IrrelevantObjectNoSynonyms) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "base" + OpMemberName %12 0 "a1" + OpMemberName %12 1 "a2" + OpName %14 "b" + OpName %18 "level_1" + OpMemberName %18 0 "b1" + OpMemberName %18 1 "b2" + OpMemberName %18 2 "b3" + OpName %20 "l1" + OpName %25 "level_2" + OpMemberName %25 0 "c1" + OpMemberName %25 1 "c2" + OpName %27 "l2" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeStruct %6 %6 + %13 = OpTypePointer Function %12 + %18 = OpTypeStruct %12 %12 %12 + %19 = OpTypePointer Function %18 + %25 = OpTypeStruct %18 %18 + %26 = OpTypePointer Function %25 + %31 = OpTypeBool + %32 = OpConstantTrue %31 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %27 = OpVariable %26 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + %21 = OpLoad %12 %14 + %22 = OpLoad %12 %14 + %23 = OpLoad %12 %14 + %24 = OpCompositeConstruct %18 %21 %22 %23 + OpStore %20 %24 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %20 + %30 = OpCompositeConstruct %25 %28 %29 + OpStore %27 %30 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %34 + %33 = OpLabel + OpBranch %34 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Add fact that the object is irrelevant. + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11); + + auto transformation_good_1 = TransformationCompositeInsert( + MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Since %30 and %50 are not irrelevant, they should be synonymous at all + // indices unaffected by the insertion. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); + // Since %11 is irrelevant it should not be synonymous with the component into + // which it has been inserted. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); +} + +TEST(TransformationCompositeInsertTest, ApplicableCreatedSynonyms) { + // This test handles cases where neither |composite| nor |object| is + // irrelevant. The transformation should create synonyms. + // The member composite has a different number of elements than the parent + // composite. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "base" + OpMemberName %12 0 "a1" + OpMemberName %12 1 "a2" + OpName %14 "b" + OpName %18 "level_1" + OpMemberName %18 0 "b1" + OpMemberName %18 1 "b2" + OpMemberName %18 2 "b3" + OpName %20 "l1" + OpName %25 "level_2" + OpMemberName %25 0 "c1" + OpMemberName %25 1 "c2" + OpName %27 "l2" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeStruct %6 %6 + %13 = OpTypePointer Function %12 + %18 = OpTypeStruct %12 %12 %12 + %19 = OpTypePointer Function %18 + %25 = OpTypeStruct %18 %18 + %26 = OpTypePointer Function %25 + %31 = OpTypeBool + %32 = OpConstantTrue %31 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %27 = OpVariable %26 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + %21 = OpLoad %12 %14 + %22 = OpLoad %12 %14 + %23 = OpLoad %12 %14 + %24 = OpCompositeConstruct %18 %21 %22 %23 + OpStore %20 %24 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %20 + %30 = OpCompositeConstruct %25 %28 %29 + OpStore %27 %30 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %34 + %33 = OpLabel + OpBranch %34 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation_good_1 = TransformationCompositeInsert( + MakeInstructionDescriptor(30, SpvOpStore, 0), 50, 30, 11, {1, 0, 0}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // These synonyms should have been added. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {0}), MakeDataDescriptor(50, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 1}), MakeDataDescriptor(50, {1, 1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 2}), MakeDataDescriptor(50, {1, 2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 0, 1}), MakeDataDescriptor(50, {1, 0, 1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {1, 0, 0}), MakeDataDescriptor(11, {}))); + + // These synonyms should not have been added. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1}), MakeDataDescriptor(50, {1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 0}), MakeDataDescriptor(50, {1, 0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1, 0, 0}), MakeDataDescriptor(50, {1, 0, 0}))); + + auto transformation_good_2 = TransformationCompositeInsert( + MakeInstructionDescriptor(50, SpvOpStore, 0), 51, 50, 11, {0, 1, 1}); + ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // These synonyms should have been added. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {1}), MakeDataDescriptor(51, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {0, 0}), MakeDataDescriptor(51, {0, 0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {0, 2}), MakeDataDescriptor(51, {0, 2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {0, 1, 0}), MakeDataDescriptor(51, {0, 1, 0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(51, {0, 1, 1}), MakeDataDescriptor(11, {}))); + + // These synonyms should not have been added. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {0}), MakeDataDescriptor(51, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {0, 1}), MakeDataDescriptor(51, {0, 1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {0, 1, 1}), MakeDataDescriptor(51, {0, 1, 1}))); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "base" + OpMemberName %12 0 "a1" + OpMemberName %12 1 "a2" + OpName %14 "b" + OpName %18 "level_1" + OpMemberName %18 0 "b1" + OpMemberName %18 1 "b2" + OpMemberName %18 2 "b3" + OpName %20 "l1" + OpName %25 "level_2" + OpMemberName %25 0 "c1" + OpMemberName %25 1 "c2" + OpName %27 "l2" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeStruct %6 %6 + %13 = OpTypePointer Function %12 + %18 = OpTypeStruct %12 %12 %12 + %19 = OpTypePointer Function %18 + %25 = OpTypeStruct %18 %18 + %26 = OpTypePointer Function %25 + %31 = OpTypeBool + %32 = OpConstantTrue %31 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %20 = OpVariable %19 Function + %27 = OpVariable %26 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + %21 = OpLoad %12 %14 + %22 = OpLoad %12 %14 + %23 = OpLoad %12 %14 + %24 = OpCompositeConstruct %18 %21 %22 %23 + OpStore %20 %24 + %28 = OpLoad %18 %20 + %29 = OpLoad %18 %20 + %30 = OpCompositeConstruct %25 %28 %29 + %50 = OpCompositeInsert %25 %11 %30 1 0 0 + %51 = OpCompositeInsert %25 %11 %50 0 1 1 + OpStore %27 %30 + OpSelectionMerge %34 None + OpBranchConditional %32 %33 %34 + %33 = OpLabel + OpBranch %34 + %34 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationCompositeInsertTest, IdNotAvailableScenarios) { + // This test handles cases where either the composite or the object is not + // available before the |instruction_to_insert_before|. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "base" + OpMemberName %12 0 "a1" + OpMemberName %12 1 "a2" + OpName %14 "b1" + OpName %18 "b2" + OpName %22 "lvl1" + OpMemberName %22 0 "b1" + OpMemberName %22 1 "b2" + OpName %24 "l1" + OpName %28 "i3" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeStruct %6 %6 + %13 = OpTypePointer Function %12 + %22 = OpTypeStruct %12 %12 + %23 = OpTypePointer Function %22 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %18 = OpVariable %13 Function + %24 = OpVariable %23 Function + %28 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpCompositeConstruct %12 %15 %16 + OpStore %14 %17 + %19 = OpLoad %6 %10 + %20 = OpLoad %6 %8 + %21 = OpCompositeConstruct %12 %19 %20 + OpStore %18 %21 + %25 = OpLoad %12 %14 + %26 = OpLoad %12 %18 + %27 = OpCompositeConstruct %22 %25 %26 + OpStore %24 %27 + %29 = OpLoad %6 %8 + %30 = OpLoad %6 %10 + %31 = OpIMul %6 %29 %30 + OpStore %28 %31 + %60 = OpCompositeConstruct %12 %20 %19 + %61 = OpCompositeConstruct %22 %26 %25 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: The object with |object_id| is not available at + // |instruction_to_insert_before|. + auto transformation_bad_1 = TransformationCompositeInsert( + MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 27, 60, {1}); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: The composite with |composite_id| is not available at + // |instruction_to_insert_before|. + auto transformation_bad_2 = TransformationCompositeInsert( + MakeInstructionDescriptor(31, SpvOpIMul, 0), 50, 61, 21, {1}); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: The |instruction_to_insert_before| is the composite itself and is + // available. + auto transformation_bad_3 = TransformationCompositeInsert( + MakeInstructionDescriptor(61, SpvOpCompositeConstruct, 0), 50, 61, 21, + {1}); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); + + // Bad: The |instruction_to_insert_before| is the object itself and is not + // available. + auto transformation_bad_4 = TransformationCompositeInsert( + MakeInstructionDescriptor(60, SpvOpCompositeConstruct, 0), 50, 27, 60, + {1}); + ASSERT_FALSE( + transformation_bad_4.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationCompositeInsertTest, CompositeInsertionWithIrrelevantIds) { + // This checks that we do *not* get data synonym facts when we do composite + // insertion using irrelevant ids or in dead blocks. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpConstant %6 0 + %9 = OpConstantComposite %7 %8 %8 + %10 = OpTypeBool + %11 = OpConstantFalse %10 + %16 = OpConstant %6 0 + %17 = OpConstant %6 1 + %18 = OpConstantComposite %7 %8 %8 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %11 %14 %15 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(14); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(16); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(18); + + // Leads to synonyms - nothing is irrelevant. + auto transformation1 = TransformationCompositeInsert( + MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 100, 9, 17, {0}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {0}), MakeDataDescriptor(17, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {1}), MakeDataDescriptor(9, {1}))); + + // Because %16 is irrelevant, we don't get a synonym with the component to + // which it has been inserted (but we do for the other component). + auto transformation2 = TransformationCompositeInsert( + MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 101, 9, 16, {0}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(101, {0}), MakeDataDescriptor(16, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(101, {1}), MakeDataDescriptor(9, {1}))); + + // Because %18 is irrelevant we only get a synonym for the component into + // which insertion has taken place. + auto transformation3 = TransformationCompositeInsert( + MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0), 102, 18, 17, {0}); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {0}), MakeDataDescriptor(17, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(102, {1}), MakeDataDescriptor(18, {1}))); + + // Does not lead to synonyms as block %14 is dead. + auto transformation4 = TransformationCompositeInsert( + MakeInstructionDescriptor(14, SpvOpBranch, 0), 103, 9, 17, {0}); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(103, {0}), MakeDataDescriptor(17, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(103, {1}), MakeDataDescriptor(9, {1}))); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp new file mode 100644 index 0000000..995b260 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_compute_data_synonym_fact_closure_test.cpp @@ -0,0 +1,477 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_compute_data_synonym_fact_closure.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationComputeDataSynonymFactClosureTest, DataSynonymFacts) { + // The SPIR-V types and constants come from the following code. The body of + // the SPIR-V function then constructs a composite that is synonymous with + // myT. + // + // #version 310 es + // + // precision highp float; + // + // struct S { + // int a; + // uvec2 b; + // }; + // + // struct T { + // bool c[5]; + // mat4x2 d; + // S e; + // }; + // + // void main() { + // T myT = T(bool[5](true, false, true, false, true), + // mat4x2(vec2(1.0, 2.0), vec2(3.0, 4.0), + // vec2(5.0, 6.0), vec2(7.0, 8.0)), + // S(10, uvec2(100u, 200u))); + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %15 "S" + OpMemberName %15 0 "a" + OpMemberName %15 1 "b" + OpName %16 "T" + OpMemberName %16 0 "c" + OpMemberName %16 1 "d" + OpMemberName %16 2 "e" + OpName %18 "myT" + OpMemberDecorate %15 0 RelaxedPrecision + OpMemberDecorate %15 1 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 5 + %9 = OpTypeArray %6 %8 + %10 = OpTypeFloat 32 + %11 = OpTypeVector %10 2 + %12 = OpTypeMatrix %11 4 + %13 = OpTypeInt 32 1 + %14 = OpTypeVector %7 2 + %15 = OpTypeStruct %13 %14 + %16 = OpTypeStruct %9 %12 %15 + %17 = OpTypePointer Function %16 + %19 = OpConstantTrue %6 + %20 = OpConstantFalse %6 + %21 = OpConstantComposite %9 %19 %20 %19 %20 %19 + %22 = OpConstant %10 1 + %23 = OpConstant %10 2 + %24 = OpConstantComposite %11 %22 %23 + %25 = OpConstant %10 3 + %26 = OpConstant %10 4 + %27 = OpConstantComposite %11 %25 %26 + %28 = OpConstant %10 5 + %29 = OpConstant %10 6 + %30 = OpConstantComposite %11 %28 %29 + %31 = OpConstant %10 7 + %32 = OpConstant %10 8 + %33 = OpConstantComposite %11 %31 %32 + %34 = OpConstantComposite %12 %24 %27 %30 %33 + %35 = OpConstant %13 10 + %36 = OpConstant %7 100 + %37 = OpConstant %7 200 + %38 = OpConstantComposite %14 %36 %37 + %39 = OpConstantComposite %15 %35 %38 + %40 = OpConstantComposite %16 %21 %34 %39 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %17 Function + OpStore %18 %40 + %100 = OpCompositeConstruct %9 %19 %20 %19 %20 %19 + %101 = OpCompositeConstruct %11 %22 %23 + %102 = OpCompositeConstruct %11 %25 %26 + %103 = OpCompositeConstruct %11 %28 %29 + %104 = OpCompositeConstruct %11 %31 %32 + %105 = OpCompositeConstruct %12 %101 %102 %103 %104 + %106 = OpCompositeConstruct %14 %36 %37 + %107 = OpCompositeConstruct %15 %35 %106 + %108 = OpCompositeConstruct %16 %100 %105 %107 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(TransformationComputeDataSynonymFactClosure(100).IsApplicable( + context.get(), transformation_context)); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {0}), MakeDataDescriptor(101, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {1}), MakeDataDescriptor(101, {1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {0}), MakeDataDescriptor(101, {1}))); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {})); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {}), MakeDataDescriptor(101, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {0}), MakeDataDescriptor(101, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {1}), MakeDataDescriptor(101, {1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {0}), MakeDataDescriptor(101, {1}))); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {0}), MakeDataDescriptor(102, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {1}), MakeDataDescriptor(102, {1}))); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(27, {0}), MakeDataDescriptor(102, {0})); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {0}), MakeDataDescriptor(102, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {1}), MakeDataDescriptor(102, {1}))); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(27, {1}), MakeDataDescriptor(102, {1})); + + ApplyAndCheckFreshIds(TransformationComputeDataSynonymFactClosure(100), + context.get(), &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {}), MakeDataDescriptor(102, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {0}), MakeDataDescriptor(102, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(27, {1}), MakeDataDescriptor(102, {1}))); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {0}), MakeDataDescriptor(103, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1}), MakeDataDescriptor(103, {1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(33, {0}), MakeDataDescriptor(104, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(33, {1}), MakeDataDescriptor(104, {1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {}), MakeDataDescriptor(105, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {0}), MakeDataDescriptor(105, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {1}), MakeDataDescriptor(105, {1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {2}), MakeDataDescriptor(105, {2}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {3}), MakeDataDescriptor(105, {3}))); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(34, {0}), MakeDataDescriptor(105, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(34, {1}), MakeDataDescriptor(105, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(34, {2}), MakeDataDescriptor(105, {2})); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {}), MakeDataDescriptor(103, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {0}), MakeDataDescriptor(103, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(30, {1}), MakeDataDescriptor(103, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(33, {}), MakeDataDescriptor(104, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(33, {0}), MakeDataDescriptor(104, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(33, {1}), MakeDataDescriptor(104, {1}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {}), MakeDataDescriptor(105, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {0}), MakeDataDescriptor(105, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {1}), MakeDataDescriptor(105, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {2}), MakeDataDescriptor(105, {2}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {3}), MakeDataDescriptor(105, {3}))); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(34, {3}), MakeDataDescriptor(105, {3})); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(33, {0}), MakeDataDescriptor(104, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(34, {3}), MakeDataDescriptor(105, {3}))); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(100, {}))); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(21, {0}), MakeDataDescriptor(100, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(21, {1}), MakeDataDescriptor(100, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(21, {2}), MakeDataDescriptor(100, {2})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(21, {3}), MakeDataDescriptor(100, {3})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(21, {4}), MakeDataDescriptor(100, {4})); + + ApplyAndCheckFreshIds(TransformationComputeDataSynonymFactClosure(100), + context.get(), &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(100, {}))); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(39, {0}), MakeDataDescriptor(107, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(35, {}), MakeDataDescriptor(39, {0}))); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(39, {0}), MakeDataDescriptor(35, {})); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(39, {0}), MakeDataDescriptor(107, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(35, {}), MakeDataDescriptor(39, {0}))); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(38, {}), MakeDataDescriptor(106, {}))); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {})); + + ApplyAndCheckFreshIds(TransformationComputeDataSynonymFactClosure(100), + context.get(), &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(38, {0}), MakeDataDescriptor(36, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(38, {1}), MakeDataDescriptor(37, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(106, {0}), MakeDataDescriptor(36, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(106, {1}), MakeDataDescriptor(37, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(38, {}), MakeDataDescriptor(106, {}))); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), MakeDataDescriptor(108, {}))); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(107, {0}), MakeDataDescriptor(35, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {0}), MakeDataDescriptor(108, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {1}), MakeDataDescriptor(108, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {2}), MakeDataDescriptor(108, {2})); + + ApplyAndCheckFreshIds(TransformationComputeDataSynonymFactClosure(100), + context.get(), &transformation_context); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), MakeDataDescriptor(108, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {0}), MakeDataDescriptor(108, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1}), MakeDataDescriptor(108, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {2}), MakeDataDescriptor(108, {2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {0, 0}), MakeDataDescriptor(108, {0, 0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {0, 1}), MakeDataDescriptor(108, {0, 1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {0, 2}), MakeDataDescriptor(108, {0, 2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {0, 3}), MakeDataDescriptor(108, {0, 3}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {0, 4}), MakeDataDescriptor(108, {0, 4}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 0}), MakeDataDescriptor(108, {1, 0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 1}), MakeDataDescriptor(108, {1, 1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 2}), MakeDataDescriptor(108, {1, 2}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 3}), MakeDataDescriptor(108, {1, 3}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 0, 0}), MakeDataDescriptor(108, {1, 0, 0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 1, 0}), MakeDataDescriptor(108, {1, 1, 0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 2, 0}), MakeDataDescriptor(108, {1, 2, 0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 3, 0}), MakeDataDescriptor(108, {1, 3, 0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 0, 1}), MakeDataDescriptor(108, {1, 0, 1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 1, 1}), MakeDataDescriptor(108, {1, 1, 1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 2, 1}), MakeDataDescriptor(108, {1, 2, 1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {1, 3, 1}), MakeDataDescriptor(108, {1, 3, 1}))); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {2, 0}), MakeDataDescriptor(108, {2, 0}))); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {2, 1}), MakeDataDescriptor(108, {2, 1}))); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {2, 1, 0}), MakeDataDescriptor(108, {2, 1, 0}))); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {2, 1, 1}), MakeDataDescriptor(108, {2, 1, 1}))); +} + +TEST(TransformationComputeDataSynonymFactClosureTest, + ComputeClosureWithMissingIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 4 + %15 = OpConstant %6 24 + %16 = OpConstantComposite %7 %15 %15 %15 %15 + %17 = OpConstantComposite %7 %15 %15 %15 %15 + %18 = OpTypeStruct %7 + %19 = OpConstantComposite %18 %16 + %30 = OpConstantComposite %18 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpCopyObject %7 %16 + %51 = OpCopyObject %7 %17 + %20 = OpCopyObject %6 %15 + %21 = OpCopyObject %6 %15 + %22 = OpCopyObject %6 %15 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(20, {}), MakeDataDescriptor(15, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(22, {}), MakeDataDescriptor(15, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(17, {0}), MakeDataDescriptor(15, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(17, {1}), MakeDataDescriptor(15, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(17, {2}), MakeDataDescriptor(15, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(17, {3}), MakeDataDescriptor(15, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(16, {0}), MakeDataDescriptor(20, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(16, {1}), MakeDataDescriptor(21, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(16, {2}), MakeDataDescriptor(22, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(16, {3}), MakeDataDescriptor(15, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(51, {0}), MakeDataDescriptor(15, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(51, {1}), MakeDataDescriptor(15, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(51, {2}), MakeDataDescriptor(15, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(51, {3}), MakeDataDescriptor(15, {})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(50, {0}), MakeDataDescriptor(20, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(50, {1}), MakeDataDescriptor(21, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(50, {2}), MakeDataDescriptor(22, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(50, {3}), MakeDataDescriptor(15, {})); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(19, {}), MakeDataDescriptor(30, {}))); + + context->KillDef(20); + context->KillDef(21); + context->KillDef(22); + context->KillDef(50); + context->KillDef(51); + context->InvalidateAnalysesExceptFor(opt::IRContext::kAnalysisNone); + + ApplyAndCheckFreshIds(TransformationComputeDataSynonymFactClosure(100), + context.get(), &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(19, {}), MakeDataDescriptor(30, {}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_duplicate_region_with_selection_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_duplicate_region_with_selection_test.cpp new file mode 100644 index 0000000..f3738e7 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_duplicate_region_with_selection_test.cpp @@ -0,0 +1,2280 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_duplicate_region_with_selection.h" + +#include "gtest/gtest.h" +#include "source/fuzz/counter_overflow_id_source.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationDuplicateRegionWithSelectionTest, BasicUseTest) { + // This test handles a case where the ids from the original region are used in + // subsequent block. + + std::string shader = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "b" + OpName %18 "c" + OpName %20 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %14 = OpConstant %6 2 + %16 = OpTypeBool + %17 = OpTypePointer Function %16 + %19 = OpConstantTrue %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %17 Function + %20 = OpVariable %7 Function + OpStore %18 %19 + OpStore %20 %14 + %21 = OpFunctionCall %2 %10 %20 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + OpBranch %800 + %800 = OpLabel + %13 = OpLoad %6 %9 + %15 = OpIAdd %6 %13 %14 + OpStore %12 %15 + OpBranch %900 + %900 = OpLabel + %901 = OpIAdd %6 %15 %13 + %902 = OpISub %6 %13 %15 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string expected_shader = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "b" + OpName %18 "c" + OpName %20 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %14 = OpConstant %6 2 + %16 = OpTypeBool + %17 = OpTypePointer Function %16 + %19 = OpConstantTrue %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %17 Function + %20 = OpVariable %7 Function + OpStore %18 %19 + OpStore %20 %14 + %21 = OpFunctionCall %2 %10 %20 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + OpBranch %500 + %500 = OpLabel + OpSelectionMerge %501 None + OpBranchConditional %19 %800 %100 + %800 = OpLabel + %13 = OpLoad %6 %9 + %15 = OpIAdd %6 %13 %14 + OpStore %12 %15 + OpBranch %501 + %100 = OpLabel + %201 = OpLoad %6 %9 + %202 = OpIAdd %6 %201 %14 + OpStore %12 %202 + OpBranch %501 + %501 = OpLabel + %301 = OpPhi %6 %13 %800 %201 %100 + %302 = OpPhi %6 %15 %800 %202 %100 + OpBranch %900 + %900 = OpLabel + %901 = OpIAdd %6 %302 %301 + %902 = OpISub %6 %301 %302 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, BasicExitBlockTest) { + // This test handles a case where the exit block of the region is the exit + // block of the containing function. + + std::string shader = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "b" + OpName %18 "c" + OpName %20 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %14 = OpConstant %6 2 + %16 = OpTypeBool + %17 = OpTypePointer Function %16 + %19 = OpConstantTrue %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %17 Function + %20 = OpVariable %7 Function + OpStore %18 %19 + OpStore %20 %14 + %21 = OpFunctionCall %2 %10 %20 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + OpBranch %800 + %800 = OpLabel + %13 = OpLoad %6 %9 + %15 = OpIAdd %6 %13 %14 + OpStore %12 %15 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "b" + OpName %18 "c" + OpName %20 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %14 = OpConstant %6 2 + %16 = OpTypeBool + %17 = OpTypePointer Function %16 + %19 = OpConstantTrue %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %17 Function + %20 = OpVariable %7 Function + OpStore %18 %19 + OpStore %20 %14 + %21 = OpFunctionCall %2 %10 %20 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + OpBranch %500 + %500 = OpLabel + OpSelectionMerge %501 None + OpBranchConditional %19 %800 %100 + %800 = OpLabel + %13 = OpLoad %6 %9 + %15 = OpIAdd %6 %13 %14 + OpStore %12 %15 + OpBranch %501 + %100 = OpLabel + %201 = OpLoad %6 %9 + %202 = OpIAdd %6 %201 %14 + OpStore %12 %202 + OpBranch %501 + %501 = OpLabel + %301 = OpPhi %6 %13 %800 %201 %100 + %302 = OpPhi %6 %15 %800 %202 %100 + OpReturn + OpFunctionEnd + + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest) { + // This test handles few cases where the transformation is not applicable + // because of the control flow graph or layout of the blocks. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %18 "b" + OpName %25 "c" + OpName %27 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %24 = OpTypePointer Function %14 + %26 = OpConstantTrue %14 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %25 = OpVariable %24 Function + %27 = OpVariable %7 Function + OpStore %25 %26 + OpStore %27 %13 + %28 = OpFunctionCall %2 %10 %27 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %18 = OpVariable %7 Function + %12 = OpLoad %6 %9 + %15 = OpSLessThan %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %21 + %16 = OpLabel + %19 = OpLoad %6 %9 + %20 = OpIAdd %6 %19 %13 + OpStore %18 %20 + OpBranch %17 + %21 = OpLabel + %22 = OpLoad %6 %9 + %23 = OpISub %6 %22 %13 + OpStore %18 %23 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: |entry_block_id| refers to the entry block of the function (this + // transformation currently avoids such cases). + TransformationDuplicateRegionWithSelection transformation_bad_1 = + TransformationDuplicateRegionWithSelection( + 500, 26, 501, 11, 11, {{11, 100}}, {{18, 201}, {12, 202}, {15, 203}}, + {{18, 301}, {12, 302}, {15, 303}}); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: The block with id 16 does not dominate the block with id 21. + TransformationDuplicateRegionWithSelection transformation_bad_2 = + TransformationDuplicateRegionWithSelection( + 500, 26, 501, 16, 21, {{16, 100}, {21, 101}}, + {{19, 201}, {20, 202}, {22, 203}, {23, 204}}, + {{19, 301}, {20, 302}, {22, 303}, {23, 304}}); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: The block with id 21 does not post-dominate the block with id 11. + TransformationDuplicateRegionWithSelection transformation_bad_3 = + TransformationDuplicateRegionWithSelection( + 500, 26, 501, 11, 21, {{11, 100}, {21, 101}}, + {{18, 201}, {12, 202}, {15, 203}, {22, 204}, {23, 205}}, + {{18, 301}, {12, 302}, {15, 303}, {22, 304}, {23, 305}}); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); + + // Bad: The block with id 5 is contained in a different function than the + // block with id 11. + TransformationDuplicateRegionWithSelection transformation_bad_4 = + TransformationDuplicateRegionWithSelection( + 500, 26, 501, 5, 11, {{5, 100}, {11, 101}}, + {{25, 201}, {27, 202}, {28, 203}, {18, 204}, {12, 205}, {15, 206}}, + {{25, 301}, {27, 302}, {28, 303}, {18, 304}, {12, 305}, {15, 306}}); + ASSERT_FALSE( + transformation_bad_4.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableIdTest) { + // This test handles a case where the supplied ids are either not fresh, not + // distinct, not valid in their context or do not refer to the existing + // instructions. + + std::string shader = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "b" + OpName %18 "c" + OpName %20 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %14 = OpConstant %6 2 + %16 = OpTypeBool + %17 = OpTypePointer Function %16 + %19 = OpConstantTrue %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %17 Function + %20 = OpVariable %7 Function + OpStore %18 %19 + OpStore %20 %14 + %21 = OpFunctionCall %2 %10 %20 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + OpBranch %800 + %800 = OpLabel + %13 = OpLoad %6 %9 + %15 = OpIAdd %6 %13 %14 + OpStore %12 %15 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: A value in the |original_label_to_duplicate_label| is not a fresh id. + TransformationDuplicateRegionWithSelection transformation_bad_1 = + TransformationDuplicateRegionWithSelection( + 500, 19, 501, 800, 800, {{800, 21}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: Values in the |original_id_to_duplicate_id| are not distinct. + TransformationDuplicateRegionWithSelection transformation_bad_2 = + TransformationDuplicateRegionWithSelection( + 500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 201}}, + {{13, 301}, {15, 302}}); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: Values in the |original_id_to_phi_id| are not fresh and are not + // distinct with previous values. + TransformationDuplicateRegionWithSelection transformation_bad_3 = + TransformationDuplicateRegionWithSelection( + 500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 18}, {15, 202}}); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); + + // Bad: |entry_block_id| does not refer to an existing instruction. + TransformationDuplicateRegionWithSelection transformation_bad_4 = + TransformationDuplicateRegionWithSelection( + 500, 19, 501, 802, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + ASSERT_FALSE( + transformation_bad_4.IsApplicable(context.get(), transformation_context)); + + // Bad: |exit_block_id| does not refer to a block. + TransformationDuplicateRegionWithSelection transformation_bad_5 = + TransformationDuplicateRegionWithSelection( + 500, 19, 501, 800, 9, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + ASSERT_FALSE( + transformation_bad_5.IsApplicable(context.get(), transformation_context)); + + // Bad: |new_entry_fresh_id| is not fresh. + TransformationDuplicateRegionWithSelection transformation_bad_6 = + TransformationDuplicateRegionWithSelection( + 20, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + ASSERT_FALSE( + transformation_bad_6.IsApplicable(context.get(), transformation_context)); + + // Bad: |merge_label_fresh_id| is not fresh. + TransformationDuplicateRegionWithSelection transformation_bad_7 = + TransformationDuplicateRegionWithSelection( + 500, 19, 20, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + ASSERT_FALSE( + transformation_bad_7.IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // Bad: Instruction with id 15 is from the original region and is available + // at the end of the region but it is not present in the + // |original_id_to_phi_id|. + TransformationDuplicateRegionWithSelection transformation_bad_8 = + TransformationDuplicateRegionWithSelection( + 500, 19, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}}); + ASSERT_DEATH( + transformation_bad_8.IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); + + // Bad: Instruction with id 15 is from the original region but it is + // not present in the |original_id_to_duplicate_id|. + TransformationDuplicateRegionWithSelection transformation_bad_9 = + TransformationDuplicateRegionWithSelection(500, 19, 501, 800, 800, + {{800, 100}}, {{13, 201}}, + {{13, 301}, {15, 302}}); + ASSERT_DEATH( + transformation_bad_9.IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); +#endif + + // Bad: |condition_id| does not refer to the valid instruction. + TransformationDuplicateRegionWithSelection transformation_bad_10 = + TransformationDuplicateRegionWithSelection( + 500, 200, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + + ASSERT_FALSE(transformation_bad_10.IsApplicable(context.get(), + transformation_context)); + + // Bad: |condition_id| does not refer to the instruction of type OpTypeBool + TransformationDuplicateRegionWithSelection transformation_bad_11 = + TransformationDuplicateRegionWithSelection( + 500, 14, 501, 800, 800, {{800, 100}}, {{13, 201}, {15, 202}}, + {{13, 301}, {15, 302}}); + + ASSERT_FALSE(transformation_bad_11.IsApplicable(context.get(), + transformation_context)); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest2) { + // This test handles few cases where the transformation is not applicable + // because of the control flow graph or the layout of the blocks. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %12 "i" + OpName %29 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %19 = OpConstant %8 10 + %20 = OpTypeBool + %26 = OpConstant %8 1 + %28 = OpTypePointer Function %20 + %30 = OpConstantTrue %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %29 = OpVariable %28 Function + OpStore %29 %30 + %31 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + %12 = OpVariable %9 Function + OpStore %10 %11 + OpStore %12 %11 + OpBranch %13 + %13 = OpLabel + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %18 = OpLoad %8 %12 + %21 = OpSLessThan %20 %18 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %22 = OpLoad %8 %10 + %23 = OpLoad %8 %12 + %24 = OpIAdd %8 %22 %23 + OpStore %10 %24 + OpBranch %16 + %16 = OpLabel + OpBranch %50 + %50 = OpLabel + %25 = OpLoad %8 %12 + %27 = OpIAdd %8 %25 %26 + OpStore %12 %27 + OpBranch %13 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: The exit block cannot be a header of a loop, because the region won't + // be a single-entry, single-exit region. + TransformationDuplicateRegionWithSelection transformation_bad_1 = + TransformationDuplicateRegionWithSelection(500, 30, 501, 13, 13, + {{13, 100}}, {{}}, {{}}); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: The block with id 13, the loop header, is in the region. The block + // with id 15, the loop merge block, is not in the region. + TransformationDuplicateRegionWithSelection transformation_bad_2 = + TransformationDuplicateRegionWithSelection( + 500, 30, 501, 13, 17, {{13, 100}, {17, 101}}, {{18, 201}, {21, 202}}, + {{18, 301}, {21, 302}}); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: The block with id 13, the loop header, is not in the region. The block + // with id 16, the loop continue target, is in the region. + TransformationDuplicateRegionWithSelection transformation_bad_3 = + TransformationDuplicateRegionWithSelection( + 500, 30, 501, 16, 50, {{16, 100}, {50, 101}}, {{25, 201}, {27, 202}}, + {{25, 301}, {27, 302}}); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableCFGTest3) { + // This test handles a case where for the block which is not the exit block, + // not all successors are in the region. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %14 "a" + OpName %19 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeBool + %9 = OpConstantTrue %8 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 2 + %17 = OpConstant %12 3 + %18 = OpTypePointer Function %8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %19 = OpVariable %18 Function + OpStore %19 %9 + %20 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %14 = OpVariable %13 Function + OpSelectionMerge %11 None + OpBranchConditional %9 %10 %16 + %10 = OpLabel + OpStore %14 %15 + OpBranch %11 + %16 = OpLabel + OpStore %14 %17 + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: The block with id 7, which is not an exit block, has two successors: + // the block with id 10 and the block with id 16. The block with id 16 is not + // in the region. + TransformationDuplicateRegionWithSelection transformation_bad_1 = + TransformationDuplicateRegionWithSelection( + 500, 30, 501, 7, 10, {{13, 100}}, {{14, 201}}, {{14, 301}}); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, MultipleBlocksLoopTest) { + // This test handles a case where the region consists of multiple blocks + // (they form a loop). The transformation is applicable and the region is + // duplicated. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %12 "i" + OpName %29 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %19 = OpConstant %8 10 + %20 = OpTypeBool + %26 = OpConstant %8 1 + %28 = OpTypePointer Function %20 + %30 = OpConstantTrue %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %29 = OpVariable %28 Function + OpStore %29 %30 + %31 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + %12 = OpVariable %9 Function + OpStore %10 %11 + OpStore %12 %11 + OpBranch %50 + %50 = OpLabel + OpBranch %13 + %13 = OpLabel + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %18 = OpLoad %8 %12 + %21 = OpSLessThan %20 %18 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %22 = OpLoad %8 %10 + %23 = OpLoad %8 %12 + %24 = OpIAdd %8 %22 %23 + OpStore %10 %24 + OpBranch %16 + %16 = OpLabel + %25 = OpLoad %8 %12 + %27 = OpIAdd %8 %25 %26 + OpStore %12 %27 + OpBranch %13 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 500, 30, 501, 50, 15, + {{50, 100}, {13, 101}, {14, 102}, {15, 103}, {16, 104}, {17, 105}}, + {{22, 201}, + {23, 202}, + {24, 203}, + {25, 204}, + {27, 205}, + {18, 206}, + {21, 207}}, + {{22, 301}, + {23, 302}, + {24, 303}, + {25, 304}, + {27, 305}, + {18, 306}, + {21, 307}}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %12 "i" + OpName %29 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %19 = OpConstant %8 10 + %20 = OpTypeBool + %26 = OpConstant %8 1 + %28 = OpTypePointer Function %20 + %30 = OpConstantTrue %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %29 = OpVariable %28 Function + OpStore %29 %30 + %31 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + %12 = OpVariable %9 Function + OpStore %10 %11 + OpStore %12 %11 + OpBranch %500 + %500 = OpLabel + OpSelectionMerge %501 None + OpBranchConditional %30 %50 %100 + %50 = OpLabel + OpBranch %13 + %13 = OpLabel + OpLoopMerge %15 %16 None + OpBranch %17 + %17 = OpLabel + %18 = OpLoad %8 %12 + %21 = OpSLessThan %20 %18 %19 + OpBranchConditional %21 %14 %15 + %14 = OpLabel + %22 = OpLoad %8 %10 + %23 = OpLoad %8 %12 + %24 = OpIAdd %8 %22 %23 + OpStore %10 %24 + OpBranch %16 + %16 = OpLabel + %25 = OpLoad %8 %12 + %27 = OpIAdd %8 %25 %26 + OpStore %12 %27 + OpBranch %13 + %15 = OpLabel + OpBranch %501 + %100 = OpLabel + OpBranch %101 + %101 = OpLabel + OpLoopMerge %103 %104 None + OpBranch %105 + %105 = OpLabel + %206 = OpLoad %8 %12 + %207 = OpSLessThan %20 %206 %19 + OpBranchConditional %207 %102 %103 + %102 = OpLabel + %201 = OpLoad %8 %10 + %202 = OpLoad %8 %12 + %203 = OpIAdd %8 %201 %202 + OpStore %10 %203 + OpBranch %104 + %104 = OpLabel + %204 = OpLoad %8 %12 + %205 = OpIAdd %8 %204 %26 + OpStore %12 %205 + OpBranch %101 + %103 = OpLabel + OpBranch %501 + %501 = OpLabel + %306 = OpPhi %8 %18 %15 %206 %103 + %307 = OpPhi %20 %21 %15 %207 %103 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + ResolvingOpPhiExitBlockTest) { + // This test handles a case where the region under the transformation is + // referenced in OpPhi instructions. Since the new merge block becomes the + // exit of the region, these OpPhi instructions need to be updated. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %26 "b" + OpName %29 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 0 + %15 = OpConstant %6 2 + %16 = OpTypeBool + %25 = OpTypePointer Function %16 + %27 = OpConstantTrue %16 + %28 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpVariable %25 Function + %29 = OpVariable %7 Function + OpStore %26 %27 + OpStore %29 %28 + %30 = OpFunctionCall %2 %10 %29 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + OpStore %12 %13 + %14 = OpLoad %6 %9 + %17 = OpSLessThan %16 %14 %15 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %22 + %18 = OpLabel + %20 = OpLoad %6 %9 + %21 = OpIAdd %6 %20 %15 + OpStore %12 %21 + OpBranch %19 + %22 = OpLabel + %23 = OpLoad %6 %9 + %24 = OpIMul %6 %23 %15 + OpStore %12 %24 + OpBranch %19 + %19 = OpLabel + %40 = OpPhi %6 %21 %18 %24 %22 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 500, 27, 501, 22, 22, {{22, 100}}, {{23, 201}, {24, 202}}, + {{23, 301}, {24, 302}}); + + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %26 "b" + OpName %29 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 0 + %15 = OpConstant %6 2 + %16 = OpTypeBool + %25 = OpTypePointer Function %16 + %27 = OpConstantTrue %16 + %28 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %26 = OpVariable %25 Function + %29 = OpVariable %7 Function + OpStore %26 %27 + OpStore %29 %28 + %30 = OpFunctionCall %2 %10 %29 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + OpStore %12 %13 + %14 = OpLoad %6 %9 + %17 = OpSLessThan %16 %14 %15 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %500 + %18 = OpLabel + %20 = OpLoad %6 %9 + %21 = OpIAdd %6 %20 %15 + OpStore %12 %21 + OpBranch %19 + %500 = OpLabel + OpSelectionMerge %501 None + OpBranchConditional %27 %22 %100 + %22 = OpLabel + %23 = OpLoad %6 %9 + %24 = OpIMul %6 %23 %15 + OpStore %12 %24 + OpBranch %501 + %100 = OpLabel + %201 = OpLoad %6 %9 + %202 = OpIMul %6 %201 %15 + OpStore %12 %202 + OpBranch %501 + %501 = OpLabel + %301 = OpPhi %6 %23 %22 %201 %100 + %302 = OpPhi %6 %24 %22 %202 %100 + OpBranch %19 + %19 = OpLabel + %40 = OpPhi %6 %21 %18 %302 %501 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, NotApplicableEarlyReturn) { + // This test handles a case where one of the blocks has successor outside of + // the region, which has an early return from the function, so that the + // transformation is not applicable. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %27 "b" + OpName %30 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 0 + %15 = OpConstant %6 2 + %16 = OpTypeBool + %26 = OpTypePointer Function %16 + %28 = OpConstantTrue %16 + %29 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %27 = OpVariable %26 Function + %30 = OpVariable %7 Function + OpStore %27 %28 + OpStore %30 %29 + %31 = OpFunctionCall %2 %10 %30 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + OpBranch %50 + %50 = OpLabel + OpStore %12 %13 + %14 = OpLoad %6 %9 + %17 = OpSLessThan %16 %14 %15 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %22 + %18 = OpLabel + %20 = OpLoad %6 %9 + %21 = OpIAdd %6 %20 %15 + OpStore %12 %21 + OpBranch %19 + %22 = OpLabel + OpReturn + %19 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: The block with id 50, which is the entry block, has two successors: + // the block with id 18 and the block with id 22. The block 22 has an early + // return from the function, so that the entry block is not post-dominated by + // the exit block. + TransformationDuplicateRegionWithSelection transformation_bad_1 = + TransformationDuplicateRegionWithSelection( + 500, 28, 501, 50, 19, {{50, 100}, {18, 101}, {22, 102}, {19, 103}}, + {{14, 202}, {17, 203}, {20, 204}, {21, 205}}, + {{14, 302}, {17, 303}, {20, 304}, {21, 305}}); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + ResolvingOpPhiEntryBlockOnePredecessor) { + // This test handles a case where the entry block has an OpPhi instruction + // referring to its predecessor. After transformation, this instruction needs + // to be updated. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %14 "t" + OpName %20 "b" + OpName %23 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 0 + %15 = OpConstant %6 2 + %18 = OpTypeBool + %19 = OpTypePointer Function %18 + %21 = OpConstantTrue %18 + %22 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %19 Function + %23 = OpVariable %7 Function + OpStore %20 %21 + OpStore %23 %22 + %24 = OpFunctionCall %2 %10 %23 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %12 %13 + %16 = OpLoad %6 %12 + %17 = OpIMul %6 %15 %16 + OpStore %14 %17 + OpBranch %50 + %50 = OpLabel + %51 = OpPhi %6 %17 %11 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 500, 21, 501, 50, 50, {{50, 100}}, {{51, 201}}, {{51, 301}}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %14 "t" + OpName %20 "b" + OpName %23 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 0 + %15 = OpConstant %6 2 + %18 = OpTypeBool + %19 = OpTypePointer Function %18 + %21 = OpConstantTrue %18 + %22 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %19 Function + %23 = OpVariable %7 Function + OpStore %20 %21 + OpStore %23 %22 + %24 = OpFunctionCall %2 %10 %23 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %12 %13 + %16 = OpLoad %6 %12 + %17 = OpIMul %6 %15 %16 + OpStore %14 %17 + OpBranch %500 + %500 = OpLabel + OpSelectionMerge %501 None + OpBranchConditional %21 %50 %100 + %50 = OpLabel + %51 = OpPhi %6 %17 %500 + OpBranch %501 + %100 = OpLabel + %201 = OpPhi %6 %17 %500 + OpBranch %501 + %501 = OpLabel + %301 = OpPhi %6 %51 %50 %201 %100 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + NotApplicableNoVariablePointerCapability) { + // This test handles a case where the transformation would create an OpPhi + // instruction with pointer operands, however there is no cab + // CapabilityVariablePointers. Hence, the transformation is not applicable. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun(i1;" + OpName %9 "a" + OpName %12 "s" + OpName %14 "t" + OpName %20 "b" + OpName %23 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 0 + %15 = OpConstant %6 2 + %18 = OpTypeBool + %19 = OpTypePointer Function %18 + %21 = OpConstantTrue %18 + %22 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %19 Function + %23 = OpVariable %7 Function + OpStore %20 %21 + OpStore %23 %22 + %24 = OpFunctionCall %2 %10 %23 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %12 %13 + %16 = OpLoad %6 %12 + %17 = OpIMul %6 %15 %16 + OpStore %14 %17 + OpBranch %50 + %50 = OpLabel + %51 = OpCopyObject %7 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: There is no required capability CapabilityVariablePointers + TransformationDuplicateRegionWithSelection transformation_bad_1 = + TransformationDuplicateRegionWithSelection( + 500, 21, 501, 50, 50, {{50, 100}}, {{51, 201}}, {{51, 301}}); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + ExitBlockTerminatorOpUnreachable) { + // This test handles a case where the exit block ends with OpUnreachable. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %17 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %13 = OpConstant %8 2 + %15 = OpTypeBool + %16 = OpTypePointer Function %15 + %18 = OpConstantTrue %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %17 = OpVariable %16 Function + OpStore %17 %18 + %19 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpBranch %50 + %50 = OpLabel + OpStore %10 %11 + %12 = OpLoad %8 %10 + %14 = OpIAdd %8 %12 %13 + OpStore %10 %14 + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 500, 18, 501, 50, 50, {{50, 100}}, {{12, 201}, {14, 202}}, + {{12, 301}, {14, 302}}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %17 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %13 = OpConstant %8 2 + %15 = OpTypeBool + %16 = OpTypePointer Function %15 + %18 = OpConstantTrue %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %17 = OpVariable %16 Function + OpStore %17 %18 + %19 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpBranch %500 + %500 = OpLabel + OpSelectionMerge %501 None + OpBranchConditional %18 %50 %100 + %50 = OpLabel + OpStore %10 %11 + %12 = OpLoad %8 %10 + %14 = OpIAdd %8 %12 %13 + OpStore %10 %14 + OpBranch %501 + %100 = OpLabel + OpStore %10 %11 + %201 = OpLoad %8 %10 + %202 = OpIAdd %8 %201 %13 + OpStore %10 %202 + OpBranch %501 + %501 = OpLabel + %301 = OpPhi %8 %12 %50 %201 %100 + %302 = OpPhi %8 %14 %50 %202 %100 + OpUnreachable + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + ExitBlockTerminatorOpKill) { + // This test handles a case where the exit block ends with OpKill. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %17 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %13 = OpConstant %8 2 + %15 = OpTypeBool + %16 = OpTypePointer Function %15 + %18 = OpConstantTrue %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %17 = OpVariable %16 Function + OpStore %17 %18 + %19 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpBranch %50 + %50 = OpLabel + OpStore %10 %11 + %12 = OpLoad %8 %10 + %14 = OpIAdd %8 %12 %13 + OpStore %10 %14 + OpKill + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 500, 18, 501, 50, 50, {{50, 100}}, {{12, 201}, {14, 202}}, + {{12, 301}, {14, 302}}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %17 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %13 = OpConstant %8 2 + %15 = OpTypeBool + %16 = OpTypePointer Function %15 + %18 = OpConstantTrue %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %17 = OpVariable %16 Function + OpStore %17 %18 + %19 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpBranch %500 + %500 = OpLabel + OpSelectionMerge %501 None + OpBranchConditional %18 %50 %100 + %50 = OpLabel + OpStore %10 %11 + %12 = OpLoad %8 %10 + %14 = OpIAdd %8 %12 %13 + OpStore %10 %14 + OpBranch %501 + %100 = OpLabel + OpStore %10 %11 + %201 = OpLoad %8 %10 + %202 = OpIAdd %8 %201 %13 + OpStore %10 %202 + OpBranch %501 + %501 = OpLabel + %301 = OpPhi %8 %12 %50 %201 %100 + %302 = OpPhi %8 %14 %50 %202 %100 + OpKill + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + ContinueExitBlockNotApplicable) { + // This test handles a case where the exit block is the continue target and + // the transformation is not applicable. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "s" + OpName %10 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %17 = OpConstant %6 10 + %18 = OpTypeBool + %24 = OpConstant %6 5 + %30 = OpConstant %6 1 + %50 = OpConstantTrue %18 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %9 + OpBranch %11 + %11 = OpLabel + OpLoopMerge %13 %14 None + OpBranch %15 + %15 = OpLabel + %16 = OpLoad %6 %10 + %19 = OpSLessThan %18 %16 %17 + OpBranchConditional %19 %12 %13 + %12 = OpLabel + %20 = OpLoad %6 %10 + %21 = OpLoad %6 %8 + %22 = OpIAdd %6 %21 %20 + OpStore %8 %22 + %23 = OpLoad %6 %10 + %25 = OpIEqual %18 %23 %24 + OpSelectionMerge %27 None + OpBranchConditional %25 %26 %27 + %26 = OpLabel + OpBranch %13 + %27 = OpLabel + OpBranch %14 + %14 = OpLabel + %29 = OpLoad %6 %10 + %31 = OpIAdd %6 %29 %30 + OpStore %10 %31 + OpBranch %11 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationDuplicateRegionWithSelection transformation_bad = + TransformationDuplicateRegionWithSelection( + 500, 50, 501, 27, 14, {{27, 101}, {14, 102}}, {{29, 201}, {31, 202}}, + {{29, 301}, {31, 302}}); + + ASSERT_FALSE( + transformation_bad.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + MultiplePredecessorsNotApplicableTest) { + // This test handles a case where the entry block has multiple predecessors + // and the transformation is not applicable. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "fun1(i1;" + OpName %9 "a" + OpName %18 "b" + OpName %24 "b" + OpName %27 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %23 = OpTypePointer Function %14 + %25 = OpConstantTrue %14 + %26 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %24 = OpVariable %23 Function + %27 = OpVariable %7 Function + OpStore %24 %25 + OpStore %27 %26 + %28 = OpFunctionCall %2 %10 %27 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %18 = OpVariable %7 Function + %12 = OpLoad %6 %9 + %15 = OpSLessThan %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %20 + %16 = OpLabel + %19 = OpLoad %6 %9 + OpStore %18 %19 + OpBranch %60 + %20 = OpLabel + %21 = OpLoad %6 %9 + %22 = OpIAdd %6 %21 %13 + OpStore %18 %22 + OpBranch %60 + %60 = OpLabel + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation_bad = + TransformationDuplicateRegionWithSelection(500, 25, 501, 60, 60, + {{60, 101}}, {{}}, {{}}); + + ASSERT_FALSE( + transformation_bad.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, OverflowIds) { + // This test checks that the transformation correctly uses overflow ids, when + // they are both needed and provided. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %17 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %13 = OpConstant %8 2 + %15 = OpTypeBool + %16 = OpTypePointer Function %15 + %18 = OpConstantTrue %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %17 = OpVariable %16 Function + OpStore %17 %18 + %19 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpBranch %50 + %50 = OpLabel + OpStore %10 %11 + %12 = OpLoad %8 %10 + %14 = OpIAdd %8 %12 %13 + OpStore %10 %14 + OpKill + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + auto overflow_ids_unique_ptr = MakeUnique(1000); + auto overflow_ids_ptr = overflow_ids_unique_ptr.get(); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options, + std::move(overflow_ids_unique_ptr)); + + // The mappings do not provide sufficient ids, thus overflow ids are required. + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection(500, 18, 501, 50, 50, {}, + {{12, 201}}, {{14, 302}}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context, + overflow_ids_ptr->GetIssuedOverflowIds()); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %6 "fun(" + OpName %10 "s" + OpName %17 "b" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %11 = OpConstant %8 0 + %13 = OpConstant %8 2 + %15 = OpTypeBool + %16 = OpTypePointer Function %15 + %18 = OpConstantTrue %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %17 = OpVariable %16 Function + OpStore %17 %18 + %19 = OpFunctionCall %2 %6 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + %10 = OpVariable %9 Function + OpBranch %500 + %500 = OpLabel + OpSelectionMerge %501 None + OpBranchConditional %18 %50 %1000 + %50 = OpLabel + OpStore %10 %11 + %12 = OpLoad %8 %10 + %14 = OpIAdd %8 %12 %13 + OpStore %10 %14 + OpBranch %501 + %1000 = OpLabel + OpStore %10 %11 + %201 = OpLoad %8 %10 + %1002 = OpIAdd %8 %201 %13 + OpStore %10 %1002 + OpBranch %501 + %501 = OpLabel + %1001 = OpPhi %8 %12 %50 %201 %1000 + %302 = OpPhi %8 %14 %50 %1002 %1000 + OpKill + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + RegionExitIsOpBranchConditional) { + // Checks the case where the exit block of a region ends with + // OpBranchConditional (but is not a header). + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %50 = OpConstantTrue %17 + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + OpStore %8 %21 + OpBranchConditional %50 %13 %12 + %13 = OpLabel + %22 = OpLoad %6 %8 + %23 = OpIAdd %6 %22 %20 + OpStore %8 %23 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 600, 50, 601, 11, 11, {{11, 602}}, {{19, 603}, {21, 604}}, + {{19, 605}, {21, 606}}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %50 = OpConstantTrue %17 + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %600 %12 + %600 = OpLabel + OpSelectionMerge %601 None + OpBranchConditional %50 %11 %602 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + OpStore %8 %21 + OpBranch %601 + %602 = OpLabel + %603 = OpLoad %6 %8 + %604 = OpIAdd %6 %603 %20 + OpStore %8 %604 + OpBranch %601 + %601 = OpLabel + %605 = OpPhi %6 %19 %11 %603 %602 + %606 = OpPhi %6 %21 %11 %604 %602 + OpBranchConditional %50 %13 %12 + %13 = OpLabel + %22 = OpLoad %6 %8 + %23 = OpIAdd %6 %22 %20 + OpStore %8 %23 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + RegionExitIsOpBranchConditionalUsingBooleanDefinedInBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %50 = OpConstantTrue %17 + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + %70 = OpCopyObject %17 %50 + OpStore %8 %21 + OpBranchConditional %70 %13 %12 + %13 = OpLabel + %22 = OpLoad %6 %8 + %23 = OpIAdd %6 %22 %20 + OpStore %8 %23 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 600, 50, 601, 11, 11, {{11, 602}}, {{19, 603}, {21, 604}, {70, 608}}, + {{19, 605}, {21, 606}, {70, 607}}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %50 = OpConstantTrue %17 + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %600 %12 + %600 = OpLabel + OpSelectionMerge %601 None + OpBranchConditional %50 %11 %602 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + %70 = OpCopyObject %17 %50 + OpStore %8 %21 + OpBranch %601 + %602 = OpLabel + %603 = OpLoad %6 %8 + %604 = OpIAdd %6 %603 %20 + %608 = OpCopyObject %17 %50 + OpStore %8 %604 + OpBranch %601 + %601 = OpLabel + %605 = OpPhi %6 %19 %11 %603 %602 + %606 = OpPhi %6 %21 %11 %604 %602 + %607 = OpPhi %17 %70 %11 %608 %602 + OpBranchConditional %607 %13 %12 + %13 = OpLabel + %22 = OpLoad %6 %8 + %23 = OpIAdd %6 %22 %20 + OpStore %8 %23 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + RegionExitUsesOpReturnValueWithIdDefinedInRegion) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpConstant %6 2 + %30 = OpTypeBool + %31 = OpConstantTrue %30 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpFunctionCall %6 %8 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpCopyObject %6 %10 + OpReturnValue %21 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationDuplicateRegionWithSelection transformation_good_1 = + TransformationDuplicateRegionWithSelection( + 600, 31, 601, 20, 20, {{20, 602}}, {{21, 603}}, {{21, 605}}); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %10 = OpConstant %6 2 + %30 = OpTypeBool + %31 = OpConstantTrue %30 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpFunctionCall %6 %8 + OpReturn + OpFunctionEnd + %8 = OpFunction %6 None %7 + %9 = OpLabel + OpBranch %600 + %600 = OpLabel + OpSelectionMerge %601 None + OpBranchConditional %31 %20 %602 + %20 = OpLabel + %21 = OpCopyObject %6 %10 + OpBranch %601 + %602 = OpLabel + %603 = OpCopyObject %6 %10 + OpBranch %601 + %601 = OpLabel + %605 = OpPhi %6 %21 %20 %603 %602 + OpReturnValue %605 + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationDuplicateRegionWithSelectionTest, + InapplicableDueToOpTypeSampledImage) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %10 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %10 RelaxedPrecision + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypePointer UniformConstant %8 + %10 = OpVariable %9 UniformConstant + %12 = OpTypeVector %6 2 + %13 = OpConstant %6 0 + %14 = OpConstantComposite %12 %13 %13 + %15 = OpTypeVector %6 4 + %30 = OpTypeBool + %31 = OpConstantTrue %30 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %20 + %20 = OpLabel + %11 = OpLoad %8 %10 + OpBranch %21 + %21 = OpLabel + %16 = OpImageSampleImplicitLod %15 %11 %14 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationDuplicateRegionWithSelection( + 600, 31, 601, 20, 20, {{20, 602}}, {{11, 603}}, {{11, 605}}) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_equation_instruction_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_equation_instruction_test.cpp new file mode 100644 index 0000000..654fffc --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_equation_instruction_test.cpp @@ -0,0 +1,1680 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_equation_instruction.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationEquationInstructionTest, SignedNegate) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 24 + %40 = OpTypeBool + %41 = OpConstantTrue %40 + %20 = OpUndef %6 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %30 = OpCopyObject %6 %7 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Bad: id already in use. + ASSERT_FALSE(TransformationEquationInstruction(7, SpvOpSNegate, {7}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Bad: identified instruction does not exist. + ASSERT_FALSE( + TransformationEquationInstruction( + 14, SpvOpSNegate, {7}, MakeInstructionDescriptor(13, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: id 100 does not exist + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {100}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Bad: id 20 is an OpUndef + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {20}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Bad: id 30 is not available right before its definition + ASSERT_FALSE(TransformationEquationInstruction( + 14, SpvOpSNegate, {30}, + MakeInstructionDescriptor(30, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: too many arguments to OpSNegate. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {7, 7}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Bad: 40 is a type id. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {40}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Bad: wrong type of argument to OpSNegate. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpSNegate, {41}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = TransformationEquationInstruction( + 14, SpvOpSNegate, {7}, return_instruction); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation2 = TransformationEquationInstruction( + 15, SpvOpSNegate, {14}, return_instruction); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 24 + %40 = OpTypeBool + %41 = OpConstantTrue %40 + %20 = OpUndef %6 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %30 = OpCopyObject %6 %7 + %14 = OpSNegate %6 %7 + %15 = OpSNegate %6 %14 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, LogicalNot) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 5 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Bad: too few arguments to OpLogicalNot. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Bad: 6 is a type id. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {6}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Bad: wrong type of argument to OpLogicalNot. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpLogicalNot, {21}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = TransformationEquationInstruction( + 14, SpvOpLogicalNot, {7}, return_instruction); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation2 = TransformationEquationInstruction( + 15, SpvOpLogicalNot, {14}, return_instruction); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(7, {}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 5 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpLogicalNot %6 %7 + %15 = OpLogicalNot %6 %14 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, AddSubNegate1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %30 = OpTypeVector %6 3 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %31 = OpConstantComposite %30 %15 %16 %15 + %33 = OpTypeBool + %32 = OpConstantTrue %33 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Bad: too many arguments to OpIAdd. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 16, 16}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + // Bad: boolean argument to OpIAdd. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 32}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + // Bad: type as argument to OpIAdd. + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {33, 16}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + // Bad: arguments of mismatched widths + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {15, 31}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + // Bad: arguments of mismatched widths + ASSERT_FALSE(TransformationEquationInstruction(14, SpvOpIAdd, {31, 15}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = TransformationEquationInstruction( + 14, SpvOpIAdd, {15, 16}, return_instruction); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation2 = TransformationEquationInstruction( + 19, SpvOpISub, {14, 16}, return_instruction); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(15, {}), MakeDataDescriptor(19, {}))); + + auto transformation3 = TransformationEquationInstruction( + 20, SpvOpISub, {14, 15}, return_instruction); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}))); + + auto transformation4 = TransformationEquationInstruction( + 22, SpvOpISub, {16, 14}, return_instruction); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation5 = TransformationEquationInstruction( + 24, SpvOpSNegate, {22}, return_instruction); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(24, {}), MakeDataDescriptor(15, {}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %30 = OpTypeVector %6 3 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %31 = OpConstantComposite %30 %15 %16 %15 + %33 = OpTypeBool + %32 = OpConstantTrue %33 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpIAdd %6 %15 %16 + %19 = OpISub %6 %14 %16 ; ==> synonymous(%19, %15) + %20 = OpISub %6 %14 %15 ; ==> synonymous(%20, %16) + %22 = OpISub %6 %16 %14 + %24 = OpSNegate %6 %22 ; ==> synonymous(%24, %15) + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, AddSubNegate2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + auto transformation1 = TransformationEquationInstruction( + 14, SpvOpISub, {15, 16}, return_instruction); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation2 = TransformationEquationInstruction( + 17, SpvOpIAdd, {14, 16}, return_instruction); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(15, {}))); + + auto transformation3 = TransformationEquationInstruction( + 18, SpvOpIAdd, {16, 14}, return_instruction); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(17, {}), MakeDataDescriptor(18, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(18, {}), MakeDataDescriptor(15, {}))); + + auto transformation4 = TransformationEquationInstruction( + 19, SpvOpISub, {14, 15}, return_instruction); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation5 = TransformationEquationInstruction( + 20, SpvOpSNegate, {19}, return_instruction); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(20, {}), MakeDataDescriptor(16, {}))); + + auto transformation6 = TransformationEquationInstruction( + 21, SpvOpISub, {14, 19}, return_instruction); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation6, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(15, {}))); + + auto transformation7 = TransformationEquationInstruction( + 22, SpvOpISub, {14, 18}, return_instruction); + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation7, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation8 = TransformationEquationInstruction( + 23, SpvOpSNegate, {22}, return_instruction); + ASSERT_TRUE( + transformation8.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation8, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(23, {}), MakeDataDescriptor(16, {}))); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %14 = OpISub %6 %15 %16 + %17 = OpIAdd %6 %14 %16 ; ==> synonymous(%17, %15) + %18 = OpIAdd %6 %16 %14 ; ==> synonymous(%17, %18, %15) + %19 = OpISub %6 %14 %15 + %20 = OpSNegate %6 %19 ; ==> synonymous(%20, %16) + %21 = OpISub %6 %14 %19 ; ==> synonymous(%21, %15) + %22 = OpISub %6 %14 %18 + %23 = OpSNegate %6 %22 ; ==> synonymous(%23, %16) + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, Bitcast) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %6 2 + %10 = OpTypeVector %7 2 + %11 = OpTypeVector %8 2 + %21 = OpTypeBool + %22 = OpTypeVector %21 2 + %15 = OpConstant %6 24 + %16 = OpConstant %7 24 + %17 = OpConstant %8 24 + %18 = OpConstantComposite %9 %15 %15 + %19 = OpConstantComposite %10 %16 %16 + %20 = OpConstantComposite %11 %17 %17 + %23 = OpConstantTrue %21 + %24 = OpConstantComposite %22 %23 %23 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Too many operands. + ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpBitcast, {15, 16}, + insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Too few operands. + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {}, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Operand's id is invalid. + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {50}, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Operand's type is invalid + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {13}, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Operand must be a scalar or a vector of numerical type. +#ifndef NDEBUG + ASSERT_DEATH( + TransformationEquationInstruction(50, SpvOpBitcast, {23}, insert_before) + .IsApplicable(context.get(), transformation_context), + "Operand is not a scalar or a vector of numerical type"); + ASSERT_DEATH( + TransformationEquationInstruction(50, SpvOpBitcast, {24}, insert_before) + .IsApplicable(context.get(), transformation_context), + "Only vectors of numerical components are supported"); +#else + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {23}, insert_before) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {24}, insert_before) + .IsApplicable(context.get(), transformation_context)); +#endif + + for (uint32_t operand_id = 15, fresh_id = 50; operand_id <= 20; + ++operand_id, ++fresh_id) { + TransformationEquationInstruction transformation( + fresh_id, SpvOpBitcast, {operand_id}, insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %6 2 + %10 = OpTypeVector %7 2 + %11 = OpTypeVector %8 2 + %21 = OpTypeBool + %22 = OpTypeVector %21 2 + %15 = OpConstant %6 24 + %16 = OpConstant %7 24 + %17 = OpConstant %8 24 + %18 = OpConstantComposite %9 %15 %15 + %19 = OpConstantComposite %10 %16 %16 + %20 = OpConstantComposite %11 %17 %17 + %23 = OpConstantTrue %21 + %24 = OpConstantComposite %22 %23 %23 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpBitcast %8 %15 + %51 = OpBitcast %8 %16 + %52 = OpBitcast %6 %17 + %53 = OpBitcast %11 %18 + %54 = OpBitcast %11 %19 + %55 = OpBitcast %9 %20 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationEquationInstructionTest, + BitcastResultTypeFloatDoesNotExist) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %9 = OpTypeVector %6 2 + %10 = OpTypeVector %7 2 + %15 = OpConstant %6 24 + %16 = OpConstant %7 24 + %18 = OpConstantComposite %9 %15 %15 + %19 = OpConstantComposite %10 %16 %16 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Scalar floating-point type does not exist. + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {15}, insert_before) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {16}, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Vector of floating-point components does not exist. + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {18}, insert_before) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {19}, insert_before) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %8 = OpTypeFloat 32 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Scalar integral type does not exist. + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {17}, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // Vector of integral components does not exist. + ASSERT_FALSE( + TransformationEquationInstruction(50, SpvOpBitcast, {20}, insert_before) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %4 2 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + { + TransformationEquationInstruction transformation(50, SpvOpBitcast, {17}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(51, SpvOpBitcast, {20}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %4 2 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpBitcast %4 %17 + %51 = OpBitcast %9 %20 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %4 2 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + { + TransformationEquationInstruction transformation(50, SpvOpBitcast, {17}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(51, SpvOpBitcast, {20}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %4 2 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpBitcast %4 %17 + %51 = OpBitcast %9 %20 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist4) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %8 = OpTypeFloat 32 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + { + TransformationEquationInstruction transformation(50, SpvOpBitcast, {17}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_FALSE( + TransformationEquationInstruction(51, SpvOpBitcast, {20}, insert_before) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %8 = OpTypeFloat 32 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpBitcast %4 %17 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist5) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + { + TransformationEquationInstruction transformation(50, SpvOpBitcast, {17}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_FALSE( + TransformationEquationInstruction(51, SpvOpBitcast, {20}, insert_before) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpBitcast %4 %17 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist6) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %5 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %5 2 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + { + TransformationEquationInstruction transformation(50, SpvOpBitcast, {17}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(51, SpvOpBitcast, {20}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %5 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %5 2 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpBitcast %4 %17 + %51 = OpBitcast %9 %20 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationEquationInstructionTest, BitcastResultTypeIntDoesNotExist7) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %5 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %4 2 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto insert_before = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + { + TransformationEquationInstruction transformation(50, SpvOpBitcast, {17}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(51, SpvOpBitcast, {20}, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 1 + %5 = OpTypeInt 32 0 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %4 2 + %11 = OpTypeVector %8 2 + %17 = OpConstant %8 24 + %20 = OpConstantComposite %11 %17 %17 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpBitcast %4 %17 + %51 = OpBitcast %9 %20 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationEquationInstructionTest, Miscellaneous1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %113 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + auto transformation1 = TransformationEquationInstruction( + 522, SpvOpISub, {113, 113}, return_instruction); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation2 = TransformationEquationInstruction( + 570, SpvOpIAdd, {522, 113}, return_instruction); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %113 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %522 = OpISub %6 %113 %113 + %570 = OpIAdd %6 %522 %113 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {}))); + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, Miscellaneous2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %113 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + auto transformation1 = TransformationEquationInstruction( + 522, SpvOpISub, {113, 113}, return_instruction); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation2 = TransformationEquationInstruction( + 570, SpvOpIAdd, {522, 113}, return_instruction); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %113 = OpConstant %6 24 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %522 = OpISub %6 %113 %113 + %570 = OpIAdd %6 %522 %113 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(570, {}), MakeDataDescriptor(113, {}))); + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationEquationInstructionTest, ConversionInstructions) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %4 = OpTypeInt 32 0 + %5 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypeVector %4 3 + %9 = OpTypeVector %5 3 + %10 = OpConstant %6 12 + %20 = OpConstant %6 12 + %11 = OpConstant %4 12 + %21 = OpConstant %4 12 + %14 = OpConstant %5 12 + %15 = OpConstantComposite %7 %10 %10 %10 + %18 = OpConstantComposite %7 %10 %10 %10 + %16 = OpConstantComposite %8 %11 %11 %11 + %19 = OpConstantComposite %8 %11 %11 %11 + %17 = OpConstantComposite %9 %14 %14 %14 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Too few instruction operands. + ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Too many instruction operands. + ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {15, 16}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Operand has no type id. + ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {7}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // OpConvertSToF and OpConvertUToF require an operand to have scalar or vector + // of integral components type. + ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {17}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertSToF, {14}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertUToF, {17}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationEquationInstruction(50, SpvOpConvertUToF, {14}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationEquationInstruction transformation(50, SpvOpConvertSToF, {15}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(51, SpvOpConvertSToF, {10}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(52, SpvOpConvertUToF, {16}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(53, SpvOpConvertUToF, {11}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(58, SpvOpConvertSToF, {18}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(59, SpvOpConvertUToF, {19}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(60, SpvOpConvertSToF, {20}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationEquationInstruction transformation(61, SpvOpConvertUToF, {21}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %4 = OpTypeInt 32 0 + %5 = OpTypeFloat 32 + %7 = OpTypeVector %6 3 + %8 = OpTypeVector %4 3 + %9 = OpTypeVector %5 3 + %10 = OpConstant %6 12 + %20 = OpConstant %6 12 + %11 = OpConstant %4 12 + %21 = OpConstant %4 12 + %14 = OpConstant %5 12 + %15 = OpConstantComposite %7 %10 %10 %10 + %18 = OpConstantComposite %7 %10 %10 %10 + %16 = OpConstantComposite %8 %11 %11 %11 + %19 = OpConstantComposite %8 %11 %11 %11 + %17 = OpConstantComposite %9 %14 %14 %14 + %12 = OpFunction %2 None %3 + %13 = OpLabel + %50 = OpConvertSToF %9 %15 + %51 = OpConvertSToF %5 %10 + %52 = OpConvertUToF %9 %16 + %53 = OpConvertUToF %5 %11 + %58 = OpConvertSToF %9 %18 + %59 = OpConvertUToF %9 %19 + %60 = OpConvertSToF %5 %20 + %61 = OpConvertUToF %5 %21 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationEquationInstructionTest, FloatResultTypeDoesNotExist) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypeInt 32 1 + %8 = OpTypeVector %6 3 + %9 = OpTypeVector %7 3 + %10 = OpConstant %6 24 + %11 = OpConstant %7 25 + %14 = OpConstantComposite %8 %10 %10 %10 + %15 = OpConstantComposite %9 %11 %11 %11 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::InstructionDescriptor return_instruction = + MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Scalar float type doesn't exist. + ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertUToF, {10}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertSToF, {11}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + + // Vector float type doesn't exist. + ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertUToF, {14}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationEquationInstruction(16, SpvOpConvertSToF, {15}, + return_instruction) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationEquationInstructionTest, HandlesIrrelevantIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %30 = OpTypeVector %6 3 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %31 = OpConstantComposite %30 %15 %16 %15 + %33 = OpTypeBool + %32 = OpConstantTrue %33 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto return_instruction = MakeInstructionDescriptor(13, SpvOpReturn, 0); + + // Applicable. + TransformationEquationInstruction transformation(14, SpvOpIAdd, {15, 16}, + return_instruction); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Handles irrelevant ids. + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(16); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(15); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationEquationInstructionTest, HandlesDeadBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %12 "main" + OpExecutionMode %12 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %30 = OpTypeVector %6 3 + %15 = OpConstant %6 24 + %16 = OpConstant %6 37 + %31 = OpConstantComposite %30 %15 %16 %15 + %33 = OpTypeBool + %32 = OpConstantTrue %33 + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpSelectionMerge %40 None + OpBranchConditional %32 %40 %41 + %41 = OpLabel + OpBranch %40 + %40 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(41); + + TransformationEquationInstruction transformation1( + 14, SpvOpIAdd, {15, 16}, + MakeInstructionDescriptor(13, SpvOpSelectionMerge, 0)); + // No synonym is created since block is dead. + TransformationEquationInstruction transformation2( + 100, SpvOpISub, {14, 16}, MakeInstructionDescriptor(41, SpvOpBranch, 0)); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {}), MakeDataDescriptor(15, {}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_expand_vector_reduction_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_expand_vector_reduction_test.cpp new file mode 100644 index 0000000..ae5c4af --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_expand_vector_reduction_test.cpp @@ -0,0 +1,284 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_expand_vector_reduction.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationExpandVectorReductionTest, IsApplicable) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %9 "main" + + ; Types + %2 = OpTypeBool + %3 = OpTypeVector %2 2 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + + ; Constants + %6 = OpConstantTrue %2 + %7 = OpConstantFalse %2 + %8 = OpConstantComposite %3 %6 %7 + + ; main function + %9 = OpFunction %4 None %5 + %10 = OpLabel + %11 = OpAny %2 %8 + %12 = OpAll %2 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Tests undefined instruction. + auto transformation = TransformationExpandVectorReduction(13, {14, 15, 16}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests non OpAny or OpAll instruction. + transformation = TransformationExpandVectorReduction(10, {13, 14, 15}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests the number of fresh ids being different than the necessary. + transformation = TransformationExpandVectorReduction(11, {13, 14}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation = TransformationExpandVectorReduction(12, {13, 14, 15, 16}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests non-fresh ids. + transformation = TransformationExpandVectorReduction(11, {12, 13, 14}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests duplicated fresh ids. + transformation = TransformationExpandVectorReduction(11, {13, 13, 14}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests applicable transformations. + transformation = TransformationExpandVectorReduction(11, {13, 14, 15}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation = TransformationExpandVectorReduction(12, {13, 14, 15}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationExpandVectorReductionTest, Apply) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %13 "main" + + ; Types + %2 = OpTypeBool + %3 = OpTypeVector %2 2 + %4 = OpTypeVector %2 3 + %5 = OpTypeVector %2 4 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + + ; Constants + %8 = OpConstantTrue %2 + %9 = OpConstantFalse %2 + %10 = OpConstantComposite %3 %8 %9 + %11 = OpConstantComposite %4 %8 %9 %8 + %12 = OpConstantComposite %5 %8 %9 %8 %9 + + ; main function + %13 = OpFunction %6 None %7 + %14 = OpLabel + + ; OpAny for 2-dimensional vector + %15 = OpAny %2 %10 + + ; OpAny for 3-dimensional vector + %16 = OpAny %2 %11 + + ; OpAny for 4-dimensional vector + %17 = OpAny %2 %12 + + ; OpAll for 2-dimensional vector + %18 = OpAll %2 %10 + + ; OpAll for 3-dimensional vector + %19 = OpAll %2 %11 + + ; OpAll for 4-dimensional vector + %20 = OpAll %2 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Adds OpAny synonym for 2-dimensional vector. + auto transformation = TransformationExpandVectorReduction(15, {21, 22, 23}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(23, {}), MakeDataDescriptor(15, {}))); + + // Adds OpAny synonym for 3-dimensional vector. + transformation = + TransformationExpandVectorReduction(16, {24, 25, 26, 27, 28}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(28, {}), MakeDataDescriptor(16, {}))); + + // Adds OpAny synonym for 4-dimensional vector. + transformation = + TransformationExpandVectorReduction(17, {29, 30, 31, 32, 33, 34, 35}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(35, {}), MakeDataDescriptor(17, {}))); + + // Adds OpAll synonym for 2-dimensional vector. + transformation = TransformationExpandVectorReduction(18, {36, 37, 38}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(38, {}), MakeDataDescriptor(18, {}))); + + // Adds OpAll synonym for 3-dimensional vector. + transformation = + TransformationExpandVectorReduction(19, {39, 40, 41, 42, 43}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(43, {}), MakeDataDescriptor(19, {}))); + + // Adds OpAll synonym for 4-dimensional vector. + transformation = + TransformationExpandVectorReduction(20, {44, 45, 46, 47, 48, 49, 50}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(50, {}), MakeDataDescriptor(20, {}))); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %13 "main" + + ; Types + %2 = OpTypeBool + %3 = OpTypeVector %2 2 + %4 = OpTypeVector %2 3 + %5 = OpTypeVector %2 4 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + + ; Constants + %8 = OpConstantTrue %2 + %9 = OpConstantFalse %2 + %10 = OpConstantComposite %3 %8 %9 + %11 = OpConstantComposite %4 %8 %9 %8 + %12 = OpConstantComposite %5 %8 %9 %8 %9 + + ; main function + %13 = OpFunction %6 None %7 + %14 = OpLabel + + ; Add OpAny synonym for 2-dimensional vector + %21 = OpCompositeExtract %2 %10 0 + %22 = OpCompositeExtract %2 %10 1 + %23 = OpLogicalOr %2 %21 %22 + %15 = OpAny %2 %10 + + ; Add OpAny synonym for 3-dimensional vector + %24 = OpCompositeExtract %2 %11 0 + %25 = OpCompositeExtract %2 %11 1 + %26 = OpCompositeExtract %2 %11 2 + %27 = OpLogicalOr %2 %24 %25 + %28 = OpLogicalOr %2 %26 %27 + %16 = OpAny %2 %11 + + ; Add OpAny synonym for 4-dimensional vector + %29 = OpCompositeExtract %2 %12 0 + %30 = OpCompositeExtract %2 %12 1 + %31 = OpCompositeExtract %2 %12 2 + %32 = OpCompositeExtract %2 %12 3 + %33 = OpLogicalOr %2 %29 %30 + %34 = OpLogicalOr %2 %31 %33 + %35 = OpLogicalOr %2 %32 %34 + %17 = OpAny %2 %12 + + ; Add OpAll synonym for 2-dimensional vector + %36 = OpCompositeExtract %2 %10 0 + %37 = OpCompositeExtract %2 %10 1 + %38 = OpLogicalAnd %2 %36 %37 + %18 = OpAll %2 %10 + + ; Add OpAll synonym for 3-dimensional vector + %39 = OpCompositeExtract %2 %11 0 + %40 = OpCompositeExtract %2 %11 1 + %41 = OpCompositeExtract %2 %11 2 + %42 = OpLogicalAnd %2 %39 %40 + %43 = OpLogicalAnd %2 %41 %42 + %19 = OpAll %2 %11 + + ; Add OpAll synonym for 4-dimensional vector + %44 = OpCompositeExtract %2 %12 0 + %45 = OpCompositeExtract %2 %12 1 + %46 = OpCompositeExtract %2 %12 2 + %47 = OpCompositeExtract %2 %12 3 + %48 = OpLogicalAnd %2 %44 %45 + %49 = OpLogicalAnd %2 %46 %48 + %50 = OpLogicalAnd %2 %47 %49 + %20 = OpAll %2 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_flatten_conditional_branch_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_flatten_conditional_branch_test.cpp new file mode 100644 index 0000000..e0697d4 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_flatten_conditional_branch_test.cpp @@ -0,0 +1,2140 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_flatten_conditional_branch.h" + +#include "gtest/gtest.h" +#include "source/fuzz/counter_overflow_id_source.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +protobufs::SideEffectWrapperInfo MakeSideEffectWrapperInfo( + const protobufs::InstructionDescriptor& instruction, + uint32_t merge_block_id, uint32_t execute_block_id, + uint32_t actual_result_id, uint32_t alternative_block_id, + uint32_t placeholder_result_id, uint32_t value_to_copy_id) { + protobufs::SideEffectWrapperInfo result; + *result.mutable_instruction() = instruction; + result.set_merge_block_id(merge_block_id); + result.set_execute_block_id(execute_block_id); + result.set_actual_result_id(actual_result_id); + result.set_alternative_block_id(alternative_block_id); + result.set_placeholder_result_id(placeholder_result_id); + result.set_value_to_copy_id(value_to_copy_id); + return result; +} + +protobufs::SideEffectWrapperInfo MakeSideEffectWrapperInfo( + const protobufs::InstructionDescriptor& instruction, + uint32_t merge_block_id, uint32_t execute_block_id) { + return MakeSideEffectWrapperInfo(instruction, merge_block_id, + execute_block_id, 0, 0, 0, 0); +} + +TEST(TransformationFlattenConditionalBranchTest, Inapplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 0 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %11 = OpTypePointer Function %6 + %12 = OpTypePointer Workgroup %6 + %3 = OpVariable %12 Workgroup + %13 = OpConstant %6 2 + %2 = OpFunction %4 None %5 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %16 None + OpSwitch %13 %17 2 %18 + %17 = OpLabel + OpBranch %16 + %18 = OpLabel + OpBranch %16 + %16 = OpLabel + OpLoopMerge %19 %16 None + OpBranchConditional %10 %16 %19 + %19 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %10 %21 %20 + %21 = OpLabel + OpReturn + %20 = OpLabel + OpSelectionMerge %22 None + OpBranchConditional %10 %23 %22 + %23 = OpLabel + OpSelectionMerge %24 None + OpBranchConditional %10 %25 %24 + %25 = OpLabel + OpBranch %24 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %10 %26 %27 + %27 = OpLabel + OpBranch %28 + %28 = OpLabel + OpLoopMerge %29 %28 None + OpBranchConditional %10 %28 %29 + %29 = OpLabel + OpBranch %26 + %26 = OpLabel + OpSelectionMerge %30 None + OpBranchConditional %10 %30 %31 + %31 = OpLabel + OpBranch %32 + %32 = OpLabel + %33 = OpAtomicLoad %6 %3 %8 %8 + OpBranch %30 + %30 = OpLabel + OpSelectionMerge %34 None + OpBranchConditional %10 %35 %34 + %35 = OpLabel + OpMemoryBarrier %8 %8 + OpBranch %34 + %34 = OpLabel + OpLoopMerge %40 %39 None + OpBranchConditional %10 %36 %40 + %36 = OpLabel + OpSelectionMerge %38 None + OpBranchConditional %10 %37 %38 + %37 = OpLabel + OpBranch %40 + %38 = OpLabel + OpBranch %39 + %39 = OpLabel + OpBranch %34 + %40 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Block %15 does not end with OpBranchConditional. + ASSERT_FALSE(TransformationFlattenConditionalBranch(15, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // Block %17 is not a selection header. + ASSERT_FALSE(TransformationFlattenConditionalBranch(17, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // Block %16 is a loop header, not a selection header. + ASSERT_FALSE(TransformationFlattenConditionalBranch(16, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // Block %19 and the corresponding merge block do not describe a single-entry, + // single-exit region, because there is a return instruction in %21. + ASSERT_FALSE(TransformationFlattenConditionalBranch(19, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // Block %20 is the header of a construct containing an inner selection + // construct. + ASSERT_FALSE(TransformationFlattenConditionalBranch(20, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // Block %22 is the header of a construct containing an inner loop. + ASSERT_FALSE(TransformationFlattenConditionalBranch(22, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // Block %30 is the header of a construct containing a barrier instruction. + ASSERT_FALSE(TransformationFlattenConditionalBranch(30, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // %33 is not a block. + ASSERT_FALSE(TransformationFlattenConditionalBranch(33, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // Block %36 and the corresponding merge block do not describe a single-entry, + // single-exit region, because block %37 breaks out of the outer loop. + ASSERT_FALSE(TransformationFlattenConditionalBranch(36, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationFlattenConditionalBranchTest, Simple) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeBool + %4 = OpConstantTrue %3 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %2 = OpFunction %5 None %6 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %4 %9 %10 + %10 = OpLabel + %26 = OpPhi %3 %4 %7 + OpBranch %8 + %9 = OpLabel + %27 = OpPhi %3 %4 %7 + %11 = OpCopyObject %3 %4 + OpBranch %8 + %8 = OpLabel + %12 = OpPhi %3 %11 %9 %4 %10 + %23 = OpPhi %3 %4 %9 %4 %10 + OpBranch %13 + %13 = OpLabel + %14 = OpCopyObject %3 %4 + OpSelectionMerge %15 None + OpBranchConditional %4 %16 %17 + %16 = OpLabel + %28 = OpPhi %3 %4 %13 + OpBranch %18 + %18 = OpLabel + OpBranch %19 + %17 = OpLabel + %29 = OpPhi %3 %4 %13 + %20 = OpCopyObject %3 %4 + OpBranch %19 + %19 = OpLabel + %21 = OpPhi %3 %4 %18 %20 %17 + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %22 None + OpBranchConditional %4 %22 %22 + %22 = OpLabel + %30 = OpPhi %3 %4 %15 + OpSelectionMerge %25 None + OpBranchConditional %4 %24 %24 + %24 = OpLabel + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation1 = + TransformationFlattenConditionalBranch(7, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + auto transformation2 = + TransformationFlattenConditionalBranch(13, false, 0, 0, 0, {}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + auto transformation3 = + TransformationFlattenConditionalBranch(15, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + + auto transformation4 = + TransformationFlattenConditionalBranch(22, false, 0, 0, 0, {}); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeBool + %4 = OpConstantTrue %3 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %2 = OpFunction %5 None %6 + %7 = OpLabel + OpBranch %9 + %9 = OpLabel + %27 = OpPhi %3 %4 %7 + %11 = OpCopyObject %3 %4 + OpBranch %10 + %10 = OpLabel + %26 = OpPhi %3 %4 %9 + OpBranch %8 + %8 = OpLabel + %12 = OpSelect %3 %4 %11 %4 + %23 = OpSelect %3 %4 %4 %4 + OpBranch %13 + %13 = OpLabel + %14 = OpCopyObject %3 %4 + OpBranch %17 + %17 = OpLabel + %29 = OpPhi %3 %4 %13 + %20 = OpCopyObject %3 %4 + OpBranch %16 + %16 = OpLabel + %28 = OpPhi %3 %4 %17 + OpBranch %18 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + %21 = OpSelect %3 %4 %4 %20 + OpBranch %15 + %15 = OpLabel + OpBranch %22 + %22 = OpLabel + %30 = OpPhi %3 %4 %15 + OpBranch %24 + %24 = OpLabel + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, LoadStoreFunctionCall) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %11 = OpTypeInt 32 1 + %12 = OpTypeVector %11 4 + %13 = OpTypeFunction %11 + %70 = OpConstant %11 0 + %14 = OpConstant %11 1 + %15 = OpTypeFloat 32 + %16 = OpTypeVector %15 2 + %17 = OpConstant %15 1 + %18 = OpConstantComposite %16 %17 %17 + %19 = OpTypeBool + %20 = OpConstantTrue %19 + %21 = OpTypePointer Function %11 + %22 = OpTypeSampler + %23 = OpTypeImage %9 2D 2 0 0 1 Unknown + %24 = OpTypeSampledImage %23 + %25 = OpTypePointer Function %23 + %26 = OpTypePointer Function %22 + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 2 + %29 = OpTypeArray %11 %28 + %30 = OpTypePointer Function %29 + %2 = OpFunction %9 None %10 + %31 = OpLabel + %4 = OpVariable %21 Function + %5 = OpVariable %30 Function + %32 = OpVariable %25 Function + %33 = OpVariable %26 Function + %34 = OpLoad %23 %32 + %35 = OpLoad %22 %33 + OpSelectionMerge %36 None + OpBranchConditional %20 %37 %36 + %37 = OpLabel + %6 = OpLoad %11 %4 + %7 = OpIAdd %11 %6 %14 + OpStore %4 %7 + OpBranch %36 + %36 = OpLabel + %42 = OpPhi %11 %14 %37 %14 %31 + OpSelectionMerge %43 None + OpBranchConditional %20 %44 %45 + %44 = OpLabel + %8 = OpFunctionCall %11 %3 + OpStore %4 %8 + OpBranch %46 + %45 = OpLabel + %47 = OpAccessChain %21 %5 %14 + OpStore %47 %14 + OpBranch %46 + %46 = OpLabel + OpStore %4 %14 + OpBranch %43 + %43 = OpLabel + OpStore %4 %14 + OpSelectionMerge %48 None + OpBranchConditional %20 %49 %48 + %49 = OpLabel + OpBranch %48 + %48 = OpLabel + OpSelectionMerge %50 None + OpBranchConditional %20 %51 %50 + %51 = OpLabel + %52 = OpSampledImage %24 %34 %35 + %53 = OpLoad %11 %4 + %54 = OpImageSampleImplicitLod %12 %52 %18 + OpBranch %50 + %50 = OpLabel + OpReturn + OpFunctionEnd + %3 = OpFunction %11 None %13 + %55 = OpLabel + OpReturnValue %14 + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); +#ifndef NDEBUG + // The following checks lead to assertion failures, since some entries + // requiring fresh ids are not present in the map, and the transformation + // context does not have a source overflow ids. + + ASSERT_DEATH(TransformationFlattenConditionalBranch(31, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); + + ASSERT_DEATH(TransformationFlattenConditionalBranch( + 31, true, 0, 0, 0, + {{MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(6, SpvOpLoad, 0), 100, 101, + 102, 103, 104, 14)}}) + .IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); +#endif + + // The map maps from an instruction to a list with not enough fresh ids. + ASSERT_FALSE(TransformationFlattenConditionalBranch( + 31, true, 0, 0, 0, + {{MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(6, SpvOpLoad, 0), 100, 101, + 102, 103, 0, 0)}}) + .IsApplicable(context.get(), transformation_context)); + + // Not all fresh ids given are distinct. + ASSERT_FALSE(TransformationFlattenConditionalBranch( + 31, true, 0, 0, 0, + {{MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(6, SpvOpLoad, 0), 100, 100, + 102, 103, 104, 0)}}) + .IsApplicable(context.get(), transformation_context)); + + // %48 heads a construct containing an OpSampledImage instruction. + ASSERT_FALSE(TransformationFlattenConditionalBranch( + 48, true, 0, 0, 0, + {{MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(53, SpvOpLoad, 0), 100, 101, + 102, 103, 104, 0)}}) + .IsApplicable(context.get(), transformation_context)); + + // %0 is not a valid id. + ASSERT_FALSE( + TransformationFlattenConditionalBranch( + 31, true, 0, 0, 0, + {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(6, SpvOpLoad, 0), + 104, 100, 101, 102, 103, 0), + MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(6, SpvOpStore, 0), 106, 105)}) + .IsApplicable(context.get(), transformation_context)); + + // %17 is a float constant, while %6 has int type. + ASSERT_FALSE( + TransformationFlattenConditionalBranch( + 31, true, 0, 0, 0, + {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(6, SpvOpLoad, 0), + 104, 100, 101, 102, 103, 17), + MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(6, SpvOpStore, 0), 106, 105)}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = TransformationFlattenConditionalBranch( + 31, true, 0, 0, 0, + {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(6, SpvOpLoad, 0), + 104, 100, 101, 102, 103, 70), + MakeSideEffectWrapperInfo(MakeInstructionDescriptor(6, SpvOpStore, 0), + 106, 105)}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + // Check that the placeholder id was marked as irrelevant. + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(103)); + + // Make a new transformation context with a source of overflow ids. + auto overflow_ids_unique_ptr = MakeUnique(1000); + auto overflow_ids_ptr = overflow_ids_unique_ptr.get(); + TransformationContext new_transformation_context( + MakeUnique(context.get()), validator_options, + std::move(overflow_ids_unique_ptr)); + + auto transformation2 = TransformationFlattenConditionalBranch( + 36, false, 0, 0, 0, + {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(8, SpvOpStore, 0), + 114, 113)}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), new_transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &new_transformation_context, + overflow_ids_ptr->GetIssuedOverflowIds()); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %9 = OpTypeVoid + %10 = OpTypeFunction %9 + %11 = OpTypeInt 32 1 + %12 = OpTypeVector %11 4 + %13 = OpTypeFunction %11 + %70 = OpConstant %11 0 + %14 = OpConstant %11 1 + %15 = OpTypeFloat 32 + %16 = OpTypeVector %15 2 + %17 = OpConstant %15 1 + %18 = OpConstantComposite %16 %17 %17 + %19 = OpTypeBool + %20 = OpConstantTrue %19 + %21 = OpTypePointer Function %11 + %22 = OpTypeSampler + %23 = OpTypeImage %9 2D 2 0 0 1 Unknown + %24 = OpTypeSampledImage %23 + %25 = OpTypePointer Function %23 + %26 = OpTypePointer Function %22 + %27 = OpTypeInt 32 0 + %28 = OpConstant %27 2 + %29 = OpTypeArray %11 %28 + %30 = OpTypePointer Function %29 + %2 = OpFunction %9 None %10 + %31 = OpLabel + %4 = OpVariable %21 Function + %5 = OpVariable %30 Function + %32 = OpVariable %25 Function + %33 = OpVariable %26 Function + %34 = OpLoad %23 %32 + %35 = OpLoad %22 %33 + OpBranch %37 + %37 = OpLabel + OpSelectionMerge %104 None + OpBranchConditional %20 %100 %102 + %100 = OpLabel + %101 = OpLoad %11 %4 + OpBranch %104 + %102 = OpLabel + %103 = OpCopyObject %11 %70 + OpBranch %104 + %104 = OpLabel + %6 = OpPhi %11 %101 %100 %103 %102 + %7 = OpIAdd %11 %6 %14 + OpSelectionMerge %106 None + OpBranchConditional %20 %105 %106 + %105 = OpLabel + OpStore %4 %7 + OpBranch %106 + %106 = OpLabel + OpBranch %36 + %36 = OpLabel + %42 = OpSelect %11 %20 %14 %14 + OpBranch %45 + %45 = OpLabel + %47 = OpAccessChain %21 %5 %14 + OpSelectionMerge %1005 None + OpBranchConditional %20 %1005 %1006 + %1006 = OpLabel + OpStore %47 %14 + OpBranch %1005 + %1005 = OpLabel + OpBranch %44 + %44 = OpLabel + OpSelectionMerge %1000 None + OpBranchConditional %20 %1001 %1003 + %1001 = OpLabel + %1002 = OpFunctionCall %11 %3 + OpBranch %1000 + %1003 = OpLabel + %1004 = OpCopyObject %11 %70 + OpBranch %1000 + %1000 = OpLabel + %8 = OpPhi %11 %1002 %1001 %1004 %1003 + OpSelectionMerge %114 None + OpBranchConditional %20 %113 %114 + %113 = OpLabel + OpStore %4 %8 + OpBranch %114 + %114 = OpLabel + OpBranch %46 + %46 = OpLabel + OpStore %4 %14 + OpBranch %43 + %43 = OpLabel + OpStore %4 %14 + OpSelectionMerge %48 None + OpBranchConditional %20 %49 %48 + %49 = OpLabel + OpBranch %48 + %48 = OpLabel + OpSelectionMerge %50 None + OpBranchConditional %20 %51 %50 + %51 = OpLabel + %52 = OpSampledImage %24 %34 %35 + %53 = OpLoad %11 %4 + %54 = OpImageSampleImplicitLod %12 %52 %18 + OpBranch %50 + %50 = OpLabel + OpReturn + OpFunctionEnd + %3 = OpFunction %11 None %13 + %55 = OpLabel + OpReturnValue %14 + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} // namespace + +TEST(TransformationFlattenConditionalBranchTest, EdgeCases) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %5 %9 %8 + %9 = OpLabel + %10 = OpFunctionCall %3 %11 + OpBranch %8 + %8 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %5 %13 %12 + %13 = OpLabel + %14 = OpFunctionCall %3 %11 + %15 = OpCopyObject %3 %14 + OpBranch %12 + %12 = OpLabel + OpReturn + %16 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %5 %18 %17 + %18 = OpLabel + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + %11 = OpFunction %3 None %6 + %19 = OpLabel + OpBranch %20 + %20 = OpLabel + OpSelectionMerge %25 None + OpBranchConditional %5 %21 %22 + %21 = OpLabel + OpBranch %22 + %22 = OpLabel + OpSelectionMerge %24 None + OpBranchConditional %5 %24 %23 + %23 = OpLabel + OpBranch %24 + %24 = OpLabel + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); +#ifndef NDEBUG + // The selection construct headed by %7 requires fresh ids because it contains + // a function call. This causes an assertion failure because transformation + // context does not have a source of overflow ids. + ASSERT_DEATH(TransformationFlattenConditionalBranch(7, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); +#endif + + auto transformation1 = TransformationFlattenConditionalBranch( + 7, true, 0, 0, 0, + {{MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(10, SpvOpFunctionCall, 0), 100, 101)}}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + // The selection construct headed by %8 cannot be flattened because it + // contains a function call returning void, whose result id is used. + ASSERT_FALSE( + TransformationFlattenConditionalBranch( + 7, true, 0, 0, 0, + {{MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(14, SpvOpFunctionCall, 0), 102, 103)}}) + .IsApplicable(context.get(), transformation_context)); + + // Block %16 is unreachable. + ASSERT_FALSE(TransformationFlattenConditionalBranch(16, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation2 = + TransformationFlattenConditionalBranch(20, false, 0, 0, 0, {}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpBranch %9 + %9 = OpLabel + OpSelectionMerge %100 None + OpBranchConditional %5 %101 %100 + %101 = OpLabel + %10 = OpFunctionCall %3 %11 + OpBranch %100 + %100 = OpLabel + OpBranch %8 + %8 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %5 %13 %12 + %13 = OpLabel + %14 = OpFunctionCall %3 %11 + %15 = OpCopyObject %3 %14 + OpBranch %12 + %12 = OpLabel + OpReturn + %16 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %5 %18 %17 + %18 = OpLabel + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + %11 = OpFunction %3 None %6 + %19 = OpLabel + OpBranch %20 + %20 = OpLabel + OpBranch %21 + %21 = OpLabel + OpBranch %22 + %22 = OpLabel + OpSelectionMerge %24 None + OpBranchConditional %5 %24 %23 + %23 = OpLabel + OpBranch %24 + %24 = OpLabel + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, PhiToSelect1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %5 %9 %8 + %9 = OpLabel + OpBranch %8 + %8 = OpLabel + %11 = OpPhi %4 %5 %9 %10 %7 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(7, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %8 + %8 = OpLabel + %11 = OpSelect %4 %5 %5 %10 + OpReturn + OpFunctionEnd +)"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, PhiToSelect2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %5 %9 %8 + %9 = OpLabel + OpBranch %8 + %8 = OpLabel + %11 = OpPhi %4 %10 %7 %5 %9 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(7, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %8 + %8 = OpLabel + %11 = OpSelect %4 %5 %5 %10 + OpReturn + OpFunctionEnd +)"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, PhiToSelect3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %5 %9 %12 + %9 = OpLabel + OpBranch %8 + %12 = OpLabel + OpBranch %8 + %8 = OpLabel + %11 = OpPhi %4 %10 %12 %5 %9 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(7, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranch %8 + %8 = OpLabel + %11 = OpSelect %4 %5 %5 %10 + OpReturn + OpFunctionEnd +)"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, PhiToSelect4) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %5 %9 %12 + %9 = OpLabel + OpBranch %8 + %12 = OpLabel + OpBranch %8 + %8 = OpLabel + %11 = OpPhi %4 %5 %9 %10 %12 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(7, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %2 = OpFunction %3 None %6 + %7 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %12 + %12 = OpLabel + OpBranch %8 + %8 = OpLabel + %11 = OpSelect %4 %5 %5 %10 + OpReturn + OpFunctionEnd +)"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, PhiToSelect5) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %100 = OpTypePointer Function %4 + %2 = OpFunction %3 None %6 + %7 = OpLabel + %101 = OpVariable %100 Function + %102 = OpVariable %100 Function + OpSelectionMerge %470 None + OpBranchConditional %5 %454 %462 + %454 = OpLabel + %522 = OpLoad %4 %101 + OpBranch %470 + %462 = OpLabel + %466 = OpLoad %4 %102 + OpBranch %470 + %470 = OpLabel + %534 = OpPhi %4 %522 %454 %466 %462 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = TransformationFlattenConditionalBranch( + 7, true, 0, 0, 0, + {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(522, SpvOpLoad, 0), + 200, 201, 202, 203, 204, 5), + MakeSideEffectWrapperInfo(MakeInstructionDescriptor(466, SpvOpLoad, 0), + 300, 301, 302, 303, 304, 5)}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeBool + %5 = OpConstantTrue %4 + %10 = OpConstantFalse %4 + %6 = OpTypeFunction %3 + %100 = OpTypePointer Function %4 + %2 = OpFunction %3 None %6 + %7 = OpLabel + %101 = OpVariable %100 Function + %102 = OpVariable %100 Function + OpBranch %454 + %454 = OpLabel + OpSelectionMerge %200 None + OpBranchConditional %5 %201 %203 + %201 = OpLabel + %202 = OpLoad %4 %101 + OpBranch %200 + %203 = OpLabel + %204 = OpCopyObject %4 %5 + OpBranch %200 + %200 = OpLabel + %522 = OpPhi %4 %202 %201 %204 %203 + OpBranch %462 + %462 = OpLabel + OpSelectionMerge %300 None + OpBranchConditional %5 %303 %301 + %301 = OpLabel + %302 = OpLoad %4 %102 + OpBranch %300 + %303 = OpLabel + %304 = OpCopyObject %4 %5 + OpBranch %300 + %300 = OpLabel + %466 = OpPhi %4 %302 %301 %304 %303 + OpBranch %470 + %470 = OpLabel + %534 = OpSelect %4 %5 %522 %466 + OpReturn + OpFunctionEnd +)"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, + LoadFromBufferBlockDecoratedStruct) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpMemberDecorate %11 0 Offset 0 + OpDecorate %11 BufferBlock + OpDecorate %13 DescriptorSet 0 + OpDecorate %13 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeStruct %10 + %12 = OpTypePointer Uniform %11 + %13 = OpVariable %12 Uniform + %21 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + %20 = OpLoad %11 %13 + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = TransformationFlattenConditionalBranch( + 5, true, 0, 0, 0, + {MakeSideEffectWrapperInfo(MakeInstructionDescriptor(20, SpvOpLoad, 0), + 100, 101, 102, 103, 104, 21)}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); +} + +TEST(TransformationFlattenConditionalBranchTest, InapplicableSampledImageLoad) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 %96 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %12 BuiltIn FragCoord + OpDecorate %91 DescriptorSet 0 + OpDecorate %91 Binding 0 + OpDecorate %96 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 2 + %10 = OpTypeVector %6 4 + %11 = OpTypePointer Input %10 + %12 = OpVariable %11 Input + %21 = OpConstant %6 2 + %24 = OpTypeInt 32 1 + %33 = OpTypeBool + %35 = OpConstantTrue %33 + %88 = OpTypeImage %6 2D 0 0 0 1 Unknown + %89 = OpTypeSampledImage %88 + %90 = OpTypePointer UniformConstant %89 + %91 = OpVariable %90 UniformConstant + %95 = OpTypePointer Output %10 + %96 = OpVariable %95 Output + %200 = OpUndef %89 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %28 + %28 = OpLabel + OpSelectionMerge %38 None + OpBranchConditional %35 %32 %37 + %32 = OpLabel + %40 = OpLoad %89 %91 + OpBranch %38 + %37 = OpLabel + OpBranch %38 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationFlattenConditionalBranch( + 28, true, 0, 0, 0, + {MakeSideEffectWrapperInfo( + MakeInstructionDescriptor(40, SpvOpLoad, 0), 100, 101, + 102, 103, 104, 200)}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationFlattenConditionalBranchTest, + InapplicablePhiToSelectVector) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 3 + %12 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %20 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %11 %12 %8 %12 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationFlattenConditionalBranchTest, + InapplicablePhiToSelectVector2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %30 = OpTypeVector %6 3 + %31 = OpTypeVector %6 2 + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 3 + %40 = OpTypeFloat 32 + %41 = OpTypeVector %40 4 + %12 = OpUndef %11 + %60 = OpUndef %41 + %61 = OpConstantComposite %31 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %20 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %11 %12 %8 %12 %9 + %22 = OpPhi %11 %12 %8 %12 %9 + %23 = OpPhi %41 %60 %8 %60 %9 + %24 = OpPhi %31 %61 %8 %61 %9 + %25 = OpPhi %41 %60 %8 %60 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(5, true, 101, 102, 103, {}); + + // bvec4 is not present in the module. + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); +} + +TEST(TransformationFlattenConditionalBranchTest, + InapplicablePhiToSelectMatrix) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeFloat 32 + %30 = OpTypeVector %10 3 + %11 = OpTypeMatrix %30 3 + %12 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %20 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %11 %12 %8 %12 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationFlattenConditionalBranchTest, ApplicablePhiToSelectVector) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 3 + %12 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %20 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %11 %12 %8 %12 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 3 + %12 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpSelect %11 %7 %12 %12 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, ApplicablePhiToSelectVector2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %30 = OpTypeVector %6 3 + %31 = OpTypeVector %6 2 + %32 = OpTypeVector %6 4 + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 3 + %40 = OpTypeFloat 32 + %41 = OpTypeVector %40 4 + %12 = OpUndef %11 + %60 = OpUndef %41 + %61 = OpConstantComposite %31 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %20 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %11 %12 %8 %12 %9 + %22 = OpPhi %11 %12 %8 %12 %9 + %23 = OpPhi %41 %60 %8 %60 %9 + %24 = OpPhi %31 %61 %8 %61 %9 + %25 = OpPhi %41 %60 %8 %60 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // No id for the 2D vector case is provided. + ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 0, 102, 103, {}) + .IsApplicable(context.get(), transformation_context)); + + // No id for the 3D vector case is provided. + ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 101, 0, 103, {}) + .IsApplicable(context.get(), transformation_context)); + + // No id for the 4D vector case is provided. + ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 101, 102, 0, {}) + .IsApplicable(context.get(), transformation_context)); + + // %10 is not fresh + ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 10, 102, 103, {}) + .IsApplicable(context.get(), transformation_context)); + + // %10 is not fresh + ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 101, 10, 103, {}) + .IsApplicable(context.get(), transformation_context)); + + // %10 is not fresh + ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 101, 102, 10, {}) + .IsApplicable(context.get(), transformation_context)); + + // Duplicate "fresh" ids used for boolean vector constructors + ASSERT_FALSE( + TransformationFlattenConditionalBranch(5, true, 101, 102, 102, {}) + .IsApplicable(context.get(), transformation_context)); + + auto transformation = + TransformationFlattenConditionalBranch(5, true, 101, 102, 103, {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %30 = OpTypeVector %6 3 + %31 = OpTypeVector %6 2 + %32 = OpTypeVector %6 4 + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 3 + %40 = OpTypeFloat 32 + %41 = OpTypeVector %40 4 + %12 = OpUndef %11 + %60 = OpUndef %41 + %61 = OpConstantComposite %31 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %103 = OpCompositeConstruct %32 %7 %7 %7 %7 + %102 = OpCompositeConstruct %30 %7 %7 %7 + %101 = OpCompositeConstruct %31 %7 %7 + %21 = OpSelect %11 %102 %12 %12 + %22 = OpSelect %11 %102 %12 %12 + %23 = OpSelect %41 %103 %60 %60 + %24 = OpSelect %31 %101 %61 %61 + %25 = OpSelect %41 %103 %60 %60 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, ApplicablePhiToSelectVector3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %30 = OpTypeVector %6 3 + %31 = OpTypeVector %6 2 + %32 = OpTypeVector %6 4 + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 3 + %40 = OpTypeFloat 32 + %41 = OpTypeVector %40 4 + %12 = OpUndef %11 + %60 = OpUndef %41 + %61 = OpConstantComposite %31 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %20 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %11 %12 %8 %12 %9 + %22 = OpPhi %11 %12 %8 %12 %9 + %23 = OpPhi %41 %60 %8 %60 %9 + %24 = OpPhi %31 %61 %8 %61 %9 + %25 = OpPhi %41 %60 %8 %60 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(5, true, 101, 0, 103, {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Check that the in operands of any OpSelect instructions all have the + // appropriate operand type. + context->module()->ForEachInst([](opt::Instruction* inst) { + if (inst->opcode() == SpvOpSelect) { + ASSERT_EQ(SPV_OPERAND_TYPE_ID, inst->GetInOperand(0).type); + ASSERT_EQ(SPV_OPERAND_TYPE_ID, inst->GetInOperand(1).type); + ASSERT_EQ(SPV_OPERAND_TYPE_ID, inst->GetInOperand(2).type); + } + }); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %30 = OpTypeVector %6 3 + %31 = OpTypeVector %6 2 + %32 = OpTypeVector %6 4 + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 3 + %40 = OpTypeFloat 32 + %41 = OpTypeVector %40 4 + %12 = OpUndef %11 + %60 = OpUndef %41 + %61 = OpConstantComposite %31 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %103 = OpCompositeConstruct %32 %7 %7 %7 %7 + %101 = OpCompositeConstruct %31 %7 %7 + %21 = OpSelect %11 %7 %12 %12 + %22 = OpSelect %11 %7 %12 %12 + %23 = OpSelect %41 %103 %60 %60 + %24 = OpSelect %31 %101 %61 %61 + %25 = OpSelect %41 %103 %60 %60 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, ApplicablePhiToSelectMatrix) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeFloat 32 + %30 = OpTypeVector %10 3 + %11 = OpTypeMatrix %30 3 + %12 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %20 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %11 %12 %8 %12 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeFloat 32 + %30 = OpTypeVector %10 3 + %11 = OpTypeMatrix %30 3 + %12 = OpUndef %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %20 + %20 = OpLabel + %21 = OpSelect %11 %7 %12 %12 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, + InapplicableConditionIsIrrelevant) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(7); + + // Inapplicable because the branch condition, %7, is irrelevant. + ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationFlattenConditionalBranchTest, + OpPhiWhenTrueBranchIsConvergenceBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %9 %8 + %8 = OpLabel + %10 = OpCopyObject %6 %7 + OpBranch %9 + %9 = OpLabel + %11 = OpPhi %6 %10 %8 %7 %5 + %12 = OpPhi %6 %7 %5 %10 %8 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationFlattenConditionalBranch transformation(5, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + %10 = OpCopyObject %6 %7 + OpBranch %9 + %9 = OpLabel + %11 = OpSelect %6 %7 %7 %10 + %12 = OpSelect %6 %7 %7 %10 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, + OpPhiWhenFalseBranchIsConvergenceBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + %10 = OpCopyObject %6 %7 + OpBranch %9 + %9 = OpLabel + %11 = OpPhi %6 %10 %8 %7 %5 + %12 = OpPhi %6 %7 %5 %10 %8 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationFlattenConditionalBranch transformation(5, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %8 + %8 = OpLabel + %10 = OpCopyObject %6 %7 + OpBranch %9 + %9 = OpLabel + %11 = OpSelect %6 %7 %10 %7 + %12 = OpSelect %6 %7 %10 %7 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +TEST(TransformationFlattenConditionalBranchTest, ContainsDeadBlocksTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + %10 = OpCopyObject %6 %7 + OpBranch %9 + %9 = OpLabel + %11 = OpPhi %6 %10 %8 %7 %5 + %12 = OpPhi %6 %7 %5 %10 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationFlattenConditionalBranch transformation(5, true, 0, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation_context.GetFactManager()->AddFactBlockIsDead(8); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationFlattenConditionalBranchTest, ContainsContinueBlockTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpLoopMerge %15 %14 None + OpBranchConditional %7 %5 %15 + %5 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %7 %9 %10 + %9 = OpLabel + OpBranch %11 + %10 = OpLabel + OpBranch %14 + %11 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %13 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationFlattenConditionalBranch(5, true, 0, 0, 0, {}) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_function_call_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_function_call_test.cpp new file mode 100644 index 0000000..b27b3ca --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_function_call_test.cpp @@ -0,0 +1,475 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_function_call.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationFunctionCallTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %14 = OpTypeFunction %6 %7 %13 + %27 = OpConstant %6 1 + %50 = OpConstant %12 1 + %57 = OpTypeBool + %58 = OpConstantFalse %57 + %204 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %61 = OpVariable %7 Function + %62 = OpVariable %7 Function + %65 = OpVariable %13 Function + %66 = OpVariable %7 Function + %68 = OpVariable %13 Function + %71 = OpVariable %7 Function + %72 = OpVariable %13 Function + %73 = OpVariable %7 Function + %75 = OpVariable %13 Function + %78 = OpVariable %7 Function + %98 = OpAccessChain %7 %71 + %99 = OpCopyObject %7 %71 + OpSelectionMerge %60 None + OpBranchConditional %58 %59 %60 + %59 = OpLabel + OpBranch %60 + %60 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %26 = OpLoad %6 %9 + %28 = OpIAdd %6 %26 %27 + OpSelectionMerge %97 None + OpBranchConditional %58 %96 %97 + %96 = OpLabel + OpBranch %97 + %97 = OpLabel + OpReturnValue %28 + OpFunctionEnd + %17 = OpFunction %6 None %14 + %15 = OpFunctionParameter %7 + %16 = OpFunctionParameter %13 + %18 = OpLabel + %31 = OpVariable %7 Function + %32 = OpLoad %6 %15 + OpStore %31 %32 + %33 = OpFunctionCall %6 %10 %31 + OpReturnValue %33 + OpFunctionEnd + %21 = OpFunction %6 None %14 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %13 + %22 = OpLabel + %36 = OpLoad %6 %19 + %37 = OpLoad %12 %20 + %38 = OpConvertFToS %6 %37 + %39 = OpIAdd %6 %36 %38 + OpReturnValue %39 + OpFunctionEnd + %24 = OpFunction %6 None %8 + %23 = OpFunctionParameter %7 + %25 = OpLabel + %44 = OpVariable %7 Function + %46 = OpVariable %13 Function + %51 = OpVariable %7 Function + %52 = OpVariable %13 Function + %42 = OpLoad %6 %23 + %43 = OpConvertSToF %12 %42 + %45 = OpLoad %6 %23 + OpStore %44 %45 + OpStore %46 %43 + %47 = OpFunctionCall %6 %17 %44 %46 + %48 = OpLoad %6 %23 + %49 = OpIAdd %6 %48 %27 + OpStore %51 %49 + OpStore %52 %50 + %53 = OpFunctionCall %6 %17 %51 %52 + %54 = OpIAdd %6 %47 %53 + OpReturnValue %54 + OpFunctionEnd + %200 = OpFunction %6 None %14 + %201 = OpFunctionParameter %7 + %202 = OpFunctionParameter %13 + %203 = OpLabel + OpSelectionMerge %206 None + OpBranchConditional %58 %205 %206 + %205 = OpLabel + OpBranch %206 + %206 = OpLabel + OpReturnValue %204 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactBlockIsDead(59); + transformation_context.GetFactManager()->AddFactBlockIsDead(11); + transformation_context.GetFactManager()->AddFactBlockIsDead(18); + transformation_context.GetFactManager()->AddFactBlockIsDead(25); + transformation_context.GetFactManager()->AddFactBlockIsDead(96); + transformation_context.GetFactManager()->AddFactBlockIsDead(205); + transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(21); + transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(200); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 71); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 72); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 19); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 20); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 23); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 44); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 46); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 51); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 52); + + // Livesafe functions with argument types: 21(7, 13), 200(7, 13) + // Non-livesafe functions with argument types: 4(), 10(7), 17(7, 13), 24(7) + // Call graph edges: + // 17 -> 10 + // 24 -> 17 + + // Bad transformations + // Too many arguments + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {71, 72, 71}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // Too few arguments + ASSERT_FALSE(TransformationFunctionCall( + 100, 21, {71}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // Arguments are the wrong way around (types do not match) + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {72, 71}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // 21 is not an appropriate argument + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {21, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // 300 does not exist + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {300, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // 71 is not a function + ASSERT_FALSE( + TransformationFunctionCall(100, 71, {71, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // 500 does not exist + ASSERT_FALSE( + TransformationFunctionCall(100, 500, {71, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // Id is not fresh + ASSERT_FALSE( + TransformationFunctionCall(21, 21, {71, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // Access chain as pointer parameter + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {98, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // Copied object as pointer parameter + ASSERT_FALSE( + TransformationFunctionCall(100, 21, {99, 72}, + MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // Non-livesafe called from original live block + ASSERT_FALSE( + TransformationFunctionCall( + 100, 10, {71}, MakeInstructionDescriptor(99, SpvOpSelectionMerge, 0)) + .IsApplicable(context.get(), transformation_context)); + // Non-livesafe called from livesafe function + ASSERT_FALSE( + TransformationFunctionCall( + 100, 10, {19}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0)) + .IsApplicable(context.get(), transformation_context)); + // Livesafe function called with pointer to non-arbitrary local variable + ASSERT_FALSE( + TransformationFunctionCall( + 100, 21, {61, 72}, MakeInstructionDescriptor(38, SpvOpConvertFToS, 0)) + .IsApplicable(context.get(), transformation_context)); + // Direct recursion + ASSERT_FALSE(TransformationFunctionCall( + 100, 4, {}, MakeInstructionDescriptor(59, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // Indirect recursion + ASSERT_FALSE(TransformationFunctionCall( + 100, 24, {9}, MakeInstructionDescriptor(96, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + // Parameter 23 is not available at the call site + ASSERT_FALSE( + TransformationFunctionCall(104, 10, {23}, + MakeInstructionDescriptor(205, SpvOpBranch, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Good transformations + { + // Livesafe called from dead block: fine + TransformationFunctionCall transformation( + 100, 21, {71, 72}, MakeInstructionDescriptor(59, SpvOpBranch, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + // Livesafe called from original live block: fine + TransformationFunctionCall transformation( + 101, 21, {71, 72}, MakeInstructionDescriptor(98, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + // Livesafe called from livesafe function: fine + TransformationFunctionCall transformation( + 102, 200, {19, 20}, MakeInstructionDescriptor(36, SpvOpLoad, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + // Dead called from dead block in injected function: fine + TransformationFunctionCall transformation( + 103, 10, {23}, MakeInstructionDescriptor(45, SpvOpLoad, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + // Non-livesafe called from dead block in livesafe function: OK + TransformationFunctionCall transformation( + 104, 10, {201}, MakeInstructionDescriptor(205, SpvOpBranch, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + // Livesafe called from dead block with non-arbitrary parameter + TransformationFunctionCall transformation( + 105, 21, {62, 65}, MakeInstructionDescriptor(59, SpvOpBranch, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %14 = OpTypeFunction %6 %7 %13 + %27 = OpConstant %6 1 + %50 = OpConstant %12 1 + %57 = OpTypeBool + %58 = OpConstantFalse %57 + %204 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %61 = OpVariable %7 Function + %62 = OpVariable %7 Function + %65 = OpVariable %13 Function + %66 = OpVariable %7 Function + %68 = OpVariable %13 Function + %71 = OpVariable %7 Function + %72 = OpVariable %13 Function + %73 = OpVariable %7 Function + %75 = OpVariable %13 Function + %78 = OpVariable %7 Function + %101 = OpFunctionCall %6 %21 %71 %72 + %98 = OpAccessChain %7 %71 + %99 = OpCopyObject %7 %71 + OpSelectionMerge %60 None + OpBranchConditional %58 %59 %60 + %59 = OpLabel + %100 = OpFunctionCall %6 %21 %71 %72 + %105 = OpFunctionCall %6 %21 %62 %65 + OpBranch %60 + %60 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %26 = OpLoad %6 %9 + %28 = OpIAdd %6 %26 %27 + OpSelectionMerge %97 None + OpBranchConditional %58 %96 %97 + %96 = OpLabel + OpBranch %97 + %97 = OpLabel + OpReturnValue %28 + OpFunctionEnd + %17 = OpFunction %6 None %14 + %15 = OpFunctionParameter %7 + %16 = OpFunctionParameter %13 + %18 = OpLabel + %31 = OpVariable %7 Function + %32 = OpLoad %6 %15 + OpStore %31 %32 + %33 = OpFunctionCall %6 %10 %31 + OpReturnValue %33 + OpFunctionEnd + %21 = OpFunction %6 None %14 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %13 + %22 = OpLabel + %102 = OpFunctionCall %6 %200 %19 %20 + %36 = OpLoad %6 %19 + %37 = OpLoad %12 %20 + %38 = OpConvertFToS %6 %37 + %39 = OpIAdd %6 %36 %38 + OpReturnValue %39 + OpFunctionEnd + %24 = OpFunction %6 None %8 + %23 = OpFunctionParameter %7 + %25 = OpLabel + %44 = OpVariable %7 Function + %46 = OpVariable %13 Function + %51 = OpVariable %7 Function + %52 = OpVariable %13 Function + %42 = OpLoad %6 %23 + %43 = OpConvertSToF %12 %42 + %103 = OpFunctionCall %6 %10 %23 + %45 = OpLoad %6 %23 + OpStore %44 %45 + OpStore %46 %43 + %47 = OpFunctionCall %6 %17 %44 %46 + %48 = OpLoad %6 %23 + %49 = OpIAdd %6 %48 %27 + OpStore %51 %49 + OpStore %52 %50 + %53 = OpFunctionCall %6 %17 %51 %52 + %54 = OpIAdd %6 %47 %53 + OpReturnValue %54 + OpFunctionEnd + %200 = OpFunction %6 None %14 + %201 = OpFunctionParameter %7 + %202 = OpFunctionParameter %13 + %203 = OpLabel + OpSelectionMerge %206 None + OpBranchConditional %58 %205 %206 + %205 = OpLabel + %104 = OpFunctionCall %6 %10 %201 + OpBranch %206 + %206 = OpLabel + OpReturnValue %204 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationFunctionCallTest, DoNotInvokeEntryPoint) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %3 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactBlockIsDead(11); + + // 4 is an entry point, so it is not legal for it to be the target of a call. + ASSERT_FALSE(TransformationFunctionCall( + 100, 4, {}, MakeInstructionDescriptor(11, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_inline_function_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_inline_function_test.cpp new file mode 100644 index 0000000..4cd465f --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_inline_function_test.cpp @@ -0,0 +1,1116 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_inline_function.h" + +#include "gtest/gtest.h" +#include "source/fuzz/counter_overflow_id_source.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationInlineFunctionTest, IsApplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %52 "main" + OpExecutionMode %52 OriginUpperLeft + OpName %56 "function_with_void_return" + +; Types + %2 = OpTypeBool + %3 = OpTypeFloat 32 + %4 = OpTypeVector %3 4 + %5 = OpTypePointer Function %4 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeFunction %3 %5 %5 + +; Constant scalars + %9 = OpConstant %3 1 + %10 = OpConstant %3 2 + %11 = OpConstant %3 3 + %12 = OpConstant %3 4 + %13 = OpConstant %3 5 + %14 = OpConstant %3 6 + %15 = OpConstant %3 7 + %16 = OpConstant %3 8 + %17 = OpConstantTrue %2 + +; Constant vectors + %18 = OpConstantComposite %4 %9 %10 %11 %12 + %19 = OpConstantComposite %4 %13 %14 %15 %16 + +; function with void return + %20 = OpFunction %6 None %7 + %21 = OpLabel + OpReturn + OpFunctionEnd + +; function with early return + %22 = OpFunction %6 None %7 + %23 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %17 %24 %25 + %24 = OpLabel + OpReturn + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpReturn + OpFunctionEnd + +; function containing an OpKill instruction + %27 = OpFunction %6 None %7 + %28 = OpLabel + OpKill + OpFunctionEnd + +; function containing an OpUnreachable instruction + %29 = OpFunction %6 None %7 + %30 = OpLabel + OpUnreachable + OpFunctionEnd + +; dot product function + %31 = OpFunction %3 None %8 + %32 = OpFunctionParameter %5 + %33 = OpFunctionParameter %5 + %34 = OpLabel + %35 = OpLoad %4 %32 + %36 = OpLoad %4 %33 + %37 = OpCompositeExtract %3 %35 0 + %38 = OpCompositeExtract %3 %36 0 + %39 = OpFMul %3 %37 %38 + %40 = OpCompositeExtract %3 %35 1 + %41 = OpCompositeExtract %3 %36 1 + %42 = OpFMul %3 %40 %41 + %43 = OpCompositeExtract %3 %35 2 + %44 = OpCompositeExtract %3 %36 2 + %45 = OpFMul %3 %43 %44 + %46 = OpCompositeExtract %3 %35 3 + %47 = OpCompositeExtract %3 %36 3 + %48 = OpFMul %3 %46 %47 + %49 = OpFAdd %3 %39 %42 + %50 = OpFAdd %3 %45 %49 + %51 = OpFAdd %3 %48 %50 + OpReturnValue %51 + OpFunctionEnd + +; main function + %52 = OpFunction %6 None %7 + %53 = OpLabel + %54 = OpVariable %5 Function + %55 = OpVariable %5 Function + %56 = OpFunctionCall %6 %20 ; function with void return + OpBranch %57 + %57 = OpLabel + %59 = OpFunctionCall %6 %22 ; function with early return + OpBranch %60 + %60 = OpLabel + %61 = OpFunctionCall %6 %27 ; function containing OpKill + OpBranch %62 + %62 = OpLabel + %63 = OpFunctionCall %6 %29 ; function containing OpUnreachable + OpBranch %64 + %64 = OpLabel + OpStore %54 %18 + OpStore %55 %19 + %65 = OpFunctionCall %3 %31 %54 %55 ; dot product function + OpBranch %66 + %66 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests undefined OpFunctionCall instruction. + auto transformation = TransformationInlineFunction(67, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests false OpFunctionCall instruction. + transformation = TransformationInlineFunction(42, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests use of called function with void return. + transformation = TransformationInlineFunction(56, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests called function having an early return. + transformation = + TransformationInlineFunction(59, {{24, 67}, {25, 68}, {26, 69}}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests called function containing an OpKill instruction. + transformation = TransformationInlineFunction(61, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests called function containing an OpUnreachable instruction. + transformation = TransformationInlineFunction(63, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests applicable transformation. + transformation = TransformationInlineFunction(65, {{35, 67}, + {36, 68}, + {37, 69}, + {38, 70}, + {39, 71}, + {40, 72}, + {41, 73}, + {42, 74}, + {43, 75}, + {44, 76}, + {45, 77}, + {46, 78}, + {47, 79}, + {48, 80}, + {49, 81}, + {50, 82}, + {51, 83}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationInlineFunctionTest, Apply) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %39 "main" + +; Types + %2 = OpTypeFloat 32 + %3 = OpTypeVector %2 4 + %4 = OpTypePointer Function %3 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeFunction %2 %4 %4 + +; Constant scalars + %8 = OpConstant %2 1 + %9 = OpConstant %2 2 + %10 = OpConstant %2 3 + %11 = OpConstant %2 4 + %12 = OpConstant %2 5 + %13 = OpConstant %2 6 + %14 = OpConstant %2 7 + %15 = OpConstant %2 8 + +; Constant vectors + %16 = OpConstantComposite %3 %8 %9 %10 %11 + %17 = OpConstantComposite %3 %12 %13 %14 %15 + +; dot product function + %18 = OpFunction %2 None %7 + %19 = OpFunctionParameter %4 + %20 = OpFunctionParameter %4 + %21 = OpLabel + %22 = OpLoad %3 %19 + %23 = OpLoad %3 %20 + %24 = OpCompositeExtract %2 %22 0 + %25 = OpCompositeExtract %2 %23 0 + %26 = OpFMul %2 %24 %25 + %27 = OpCompositeExtract %2 %22 1 + %28 = OpCompositeExtract %2 %23 1 + %29 = OpFMul %2 %27 %28 + %30 = OpCompositeExtract %2 %22 2 + %31 = OpCompositeExtract %2 %23 2 + %32 = OpFMul %2 %30 %31 + %33 = OpCompositeExtract %2 %22 3 + %34 = OpCompositeExtract %2 %23 3 + %35 = OpFMul %2 %33 %34 + %36 = OpFAdd %2 %26 %29 + %37 = OpFAdd %2 %32 %36 + %38 = OpFAdd %2 %35 %37 + OpReturnValue %38 + OpFunctionEnd + +; main function + %39 = OpFunction %5 None %6 + %40 = OpLabel + %41 = OpVariable %4 Function + %42 = OpVariable %4 Function + OpStore %41 %16 + OpStore %42 %17 + %43 = OpFunctionCall %2 %18 %41 %42 ; dot product function call + OpBranch %44 + %44 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationInlineFunction(43, {{22, 45}, + {23, 46}, + {24, 47}, + {25, 48}, + {26, 49}, + {27, 50}, + {28, 51}, + {29, 52}, + {30, 53}, + {31, 54}, + {32, 55}, + {33, 56}, + {34, 57}, + {35, 58}, + {36, 59}, + {37, 60}, + {38, 61}}); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %39 "main" + +; Types + %2 = OpTypeFloat 32 + %3 = OpTypeVector %2 4 + %4 = OpTypePointer Function %3 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeFunction %2 %4 %4 + +; Constant scalars + %8 = OpConstant %2 1 + %9 = OpConstant %2 2 + %10 = OpConstant %2 3 + %11 = OpConstant %2 4 + %12 = OpConstant %2 5 + %13 = OpConstant %2 6 + %14 = OpConstant %2 7 + %15 = OpConstant %2 8 + +; Constant vectors + %16 = OpConstantComposite %3 %8 %9 %10 %11 + %17 = OpConstantComposite %3 %12 %13 %14 %15 + +; dot product function + %18 = OpFunction %2 None %7 + %19 = OpFunctionParameter %4 + %20 = OpFunctionParameter %4 + %21 = OpLabel + %22 = OpLoad %3 %19 + %23 = OpLoad %3 %20 + %24 = OpCompositeExtract %2 %22 0 + %25 = OpCompositeExtract %2 %23 0 + %26 = OpFMul %2 %24 %25 + %27 = OpCompositeExtract %2 %22 1 + %28 = OpCompositeExtract %2 %23 1 + %29 = OpFMul %2 %27 %28 + %30 = OpCompositeExtract %2 %22 2 + %31 = OpCompositeExtract %2 %23 2 + %32 = OpFMul %2 %30 %31 + %33 = OpCompositeExtract %2 %22 3 + %34 = OpCompositeExtract %2 %23 3 + %35 = OpFMul %2 %33 %34 + %36 = OpFAdd %2 %26 %29 + %37 = OpFAdd %2 %32 %36 + %38 = OpFAdd %2 %35 %37 + OpReturnValue %38 + OpFunctionEnd + +; main function + %39 = OpFunction %5 None %6 + %40 = OpLabel + %41 = OpVariable %4 Function + %42 = OpVariable %4 Function + OpStore %41 %16 + OpStore %42 %17 + %45 = OpLoad %3 %41 + %46 = OpLoad %3 %42 + %47 = OpCompositeExtract %2 %45 0 + %48 = OpCompositeExtract %2 %46 0 + %49 = OpFMul %2 %47 %48 + %50 = OpCompositeExtract %2 %45 1 + %51 = OpCompositeExtract %2 %46 1 + %52 = OpFMul %2 %50 %51 + %53 = OpCompositeExtract %2 %45 2 + %54 = OpCompositeExtract %2 %46 2 + %55 = OpFMul %2 %53 %54 + %56 = OpCompositeExtract %2 %45 3 + %57 = OpCompositeExtract %2 %46 3 + %58 = OpFMul %2 %56 %57 + %59 = OpFAdd %2 %49 %52 + %60 = OpFAdd %2 %55 %59 + %61 = OpFAdd %2 %58 %60 + %43 = OpCopyObject %2 %61 + OpBranch %44 + %44 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationInlineFunctionTest, ApplyToMultipleFunctions) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %15 "main" + +; Types + %2 = OpTypeInt 32 1 + %3 = OpTypeBool + %4 = OpTypePointer Private %2 + %5 = OpTypePointer Function %2 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeFunction %2 %5 + %9 = OpTypeFunction %2 %2 + +; Constants + %10 = OpConstant %2 0 + %11 = OpConstant %2 1 + %12 = OpConstant %2 2 + %13 = OpConstant %2 3 + +; Global variable + %14 = OpVariable %4 Private + +; main function + %15 = OpFunction %6 None %7 + %16 = OpLabel + %17 = OpVariable %5 Function + %18 = OpVariable %5 Function + %19 = OpVariable %5 Function + OpStore %17 %13 + %20 = OpLoad %2 %17 + OpStore %18 %20 + %21 = OpFunctionCall %2 %36 %18 + OpBranch %22 + %22 = OpLabel + %23 = OpFunctionCall %2 %36 %18 + OpStore %17 %21 + %24 = OpLoad %2 %17 + %25 = OpFunctionCall %2 %54 %24 + OpBranch %26 + %26 = OpLabel + %27 = OpFunctionCall %2 %54 %24 + %28 = OpLoad %2 %17 + %29 = OpIAdd %2 %28 %25 + OpStore %17 %29 + %30 = OpFunctionCall %6 %67 + OpBranch %31 + %31 = OpLabel + %32 = OpFunctionCall %6 %67 + %33 = OpLoad %2 %14 + %34 = OpLoad %2 %17 + %35 = OpIAdd %2 %34 %33 + OpStore %17 %35 + OpReturn + OpFunctionEnd + +; Function %36 + %36 = OpFunction %2 None %8 + %37 = OpFunctionParameter %5 + %38 = OpLabel + %39 = OpVariable %5 Function + %40 = OpVariable %5 Function + OpStore %39 %10 + OpBranch %41 + %41 = OpLabel + OpLoopMerge %52 %49 None + OpBranch %42 + %42 = OpLabel + %43 = OpLoad %2 %39 + %44 = OpLoad %2 %37 + %45 = OpSLessThan %3 %43 %44 + OpBranchConditional %45 %46 %52 + %46 = OpLabel + %47 = OpLoad %2 %40 + %48 = OpIAdd %2 %47 %11 + OpStore %40 %48 + OpBranch %49 + %49 = OpLabel + %50 = OpLoad %2 %39 + %51 = OpIAdd %2 %50 %12 + OpStore %39 %51 + OpBranch %41 + %52 = OpLabel + %53 = OpLoad %2 %40 + OpReturnValue %53 + OpFunctionEnd + +; Function %54 + %54 = OpFunction %2 None %9 + %55 = OpFunctionParameter %2 + %56 = OpLabel + %57 = OpVariable %5 Function + OpStore %57 %10 + %58 = OpSGreaterThan %3 %55 %10 + OpSelectionMerge %62 None + OpBranchConditional %58 %64 %59 + %59 = OpLabel + %60 = OpLoad %2 %57 + %61 = OpISub %2 %60 %12 + OpStore %57 %61 + OpBranch %62 + %62 = OpLabel + %63 = OpLoad %2 %57 + OpReturnValue %63 + %64 = OpLabel + %65 = OpLoad %2 %57 + %66 = OpIAdd %2 %65 %11 + OpStore %57 %66 + OpBranch %62 + OpFunctionEnd + +; Function %67 + %67 = OpFunction %6 None %7 + %68 = OpLabel + OpStore %14 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationInlineFunction(30, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + // Tests a parameter included in the id map. + transformation = TransformationInlineFunction(25, {{55, 69}, + {56, 70}, + {57, 71}, + {58, 72}, + {59, 73}, + {60, 74}, + {61, 75}, + {62, 76}, + {63, 77}, + {64, 78}, + {65, 79}, + {66, 80}}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // Tests the id of the returned value not included in the id map. + transformation = TransformationInlineFunction(25, {{56, 69}, + {57, 70}, + {58, 71}, + {59, 72}, + {60, 73}, + {61, 74}, + {62, 75}, + {64, 76}, + {65, 77}, + {66, 78}}); + ASSERT_DEATH( + transformation.IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); +#endif + + transformation = TransformationInlineFunction(25, {{57, 69}, + {58, 70}, + {59, 71}, + {60, 72}, + {61, 73}, + {62, 74}, + {63, 75}, + {64, 76}, + {65, 77}, + {66, 78}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationInlineFunction(21, {{39, 79}, + {40, 80}, + {41, 81}, + {42, 82}, + {43, 83}, + {44, 84}, + {45, 85}, + {46, 86}, + {47, 87}, + {48, 88}, + {49, 89}, + {50, 90}, + {51, 91}, + {52, 92}, + {53, 93}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %15 "main" + +; Types + %2 = OpTypeInt 32 1 + %3 = OpTypeBool + %4 = OpTypePointer Private %2 + %5 = OpTypePointer Function %2 + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeFunction %2 %5 + %9 = OpTypeFunction %2 %2 + +; Constants + %10 = OpConstant %2 0 + %11 = OpConstant %2 1 + %12 = OpConstant %2 2 + %13 = OpConstant %2 3 + +; Global variable + %14 = OpVariable %4 Private + +; main function + %15 = OpFunction %6 None %7 + %16 = OpLabel + %80 = OpVariable %5 Function + %79 = OpVariable %5 Function + %69 = OpVariable %5 Function + %17 = OpVariable %5 Function + %18 = OpVariable %5 Function + %19 = OpVariable %5 Function + OpStore %17 %13 + %20 = OpLoad %2 %17 + OpStore %18 %20 + OpStore %79 %10 + OpBranch %81 + %81 = OpLabel + OpLoopMerge %92 %89 None + OpBranch %82 + %82 = OpLabel + %83 = OpLoad %2 %79 + %84 = OpLoad %2 %18 + %85 = OpSLessThan %3 %83 %84 + OpBranchConditional %85 %86 %92 + %86 = OpLabel + %87 = OpLoad %2 %80 + %88 = OpIAdd %2 %87 %11 + OpStore %80 %88 + OpBranch %89 + %89 = OpLabel + %90 = OpLoad %2 %79 + %91 = OpIAdd %2 %90 %12 + OpStore %79 %91 + OpBranch %81 + %92 = OpLabel + %93 = OpLoad %2 %80 + %21 = OpCopyObject %2 %93 + OpBranch %22 + %22 = OpLabel + %23 = OpFunctionCall %2 %36 %18 + OpStore %17 %21 + %24 = OpLoad %2 %17 + OpStore %69 %10 + %70 = OpSGreaterThan %3 %24 %10 + OpSelectionMerge %74 None + OpBranchConditional %70 %76 %71 + %71 = OpLabel + %72 = OpLoad %2 %69 + %73 = OpISub %2 %72 %12 + OpStore %69 %73 + OpBranch %74 + %74 = OpLabel + %75 = OpLoad %2 %69 + %25 = OpCopyObject %2 %75 + OpBranch %26 + %76 = OpLabel + %77 = OpLoad %2 %69 + %78 = OpIAdd %2 %77 %11 + OpStore %69 %78 + OpBranch %74 + %26 = OpLabel + %27 = OpFunctionCall %2 %54 %24 + %28 = OpLoad %2 %17 + %29 = OpIAdd %2 %28 %25 + OpStore %17 %29 + OpStore %14 %12 + OpBranch %31 + %31 = OpLabel + %32 = OpFunctionCall %6 %67 + %33 = OpLoad %2 %14 + %34 = OpLoad %2 %17 + %35 = OpIAdd %2 %34 %33 + OpStore %17 %35 + OpReturn + OpFunctionEnd + +; Function %36 + %36 = OpFunction %2 None %8 + %37 = OpFunctionParameter %5 + %38 = OpLabel + %39 = OpVariable %5 Function + %40 = OpVariable %5 Function + OpStore %39 %10 + OpBranch %41 + %41 = OpLabel + OpLoopMerge %52 %49 None + OpBranch %42 + %42 = OpLabel + %43 = OpLoad %2 %39 + %44 = OpLoad %2 %37 + %45 = OpSLessThan %3 %43 %44 + OpBranchConditional %45 %46 %52 + %46 = OpLabel + %47 = OpLoad %2 %40 + %48 = OpIAdd %2 %47 %11 + OpStore %40 %48 + OpBranch %49 + %49 = OpLabel + %50 = OpLoad %2 %39 + %51 = OpIAdd %2 %50 %12 + OpStore %39 %51 + OpBranch %41 + %52 = OpLabel + %53 = OpLoad %2 %40 + OpReturnValue %53 + OpFunctionEnd + +; Function %54 + %54 = OpFunction %2 None %9 + %55 = OpFunctionParameter %2 + %56 = OpLabel + %57 = OpVariable %5 Function + OpStore %57 %10 + %58 = OpSGreaterThan %3 %55 %10 + OpSelectionMerge %62 None + OpBranchConditional %58 %64 %59 + %59 = OpLabel + %60 = OpLoad %2 %57 + %61 = OpISub %2 %60 %12 + OpStore %57 %61 + OpBranch %62 + %62 = OpLabel + %63 = OpLoad %2 %57 + OpReturnValue %63 + %64 = OpLabel + %65 = OpLoad %2 %57 + %66 = OpIAdd %2 %65 %11 + OpStore %57 %66 + OpBranch %62 + OpFunctionEnd + +; Function %67 + %67 = OpFunction %6 None %7 + %68 = OpLabel + OpStore %14 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationInlineFunctionTest, HandlesOpPhisInTheSecondBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpUndef %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpFunctionCall %2 %7 + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + %7 = OpFunction %2 None %3 + %8 = OpLabel + OpBranch %13 + %13 = OpLabel + %12 = OpPhi %10 %11 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationInlineFunction transformation(6, + {{{8, 20}, {13, 21}, {12, 22}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 0 + %11 = OpUndef %10 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %10 %11 %5 + OpBranch %14 + %14 = OpLabel + OpReturn + OpFunctionEnd + %7 = OpFunction %2 None %3 + %8 = OpLabel + OpBranch %13 + %13 = OpLabel + %12 = OpPhi %10 %11 %8 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationInlineFunctionTest, OverflowIds) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %39 "main" + +; Types + %2 = OpTypeFloat 32 + %3 = OpTypeVector %2 4 + %4 = OpTypePointer Function %3 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeFunction %2 %4 %4 + +; Constant scalars + %8 = OpConstant %2 1 + %9 = OpConstant %2 2 + %10 = OpConstant %2 3 + %11 = OpConstant %2 4 + %12 = OpConstant %2 5 + %13 = OpConstant %2 6 + %14 = OpConstant %2 7 + %15 = OpConstant %2 8 + +; Constant vectors + %16 = OpConstantComposite %3 %8 %9 %10 %11 + %17 = OpConstantComposite %3 %12 %13 %14 %15 + +; dot product function + %18 = OpFunction %2 None %7 + %19 = OpFunctionParameter %4 + %20 = OpFunctionParameter %4 + %21 = OpLabel + %22 = OpLoad %3 %19 + %23 = OpLoad %3 %20 + %24 = OpCompositeExtract %2 %22 0 + %25 = OpCompositeExtract %2 %23 0 + %26 = OpFMul %2 %24 %25 + %27 = OpCompositeExtract %2 %22 1 + %28 = OpCompositeExtract %2 %23 1 + %29 = OpFMul %2 %27 %28 + OpBranch %100 + %100 = OpLabel + %30 = OpCompositeExtract %2 %22 2 + %31 = OpCompositeExtract %2 %23 2 + %32 = OpFMul %2 %30 %31 + %33 = OpCompositeExtract %2 %22 3 + %34 = OpCompositeExtract %2 %23 3 + %35 = OpFMul %2 %33 %34 + %36 = OpFAdd %2 %26 %29 + %37 = OpFAdd %2 %32 %36 + %38 = OpFAdd %2 %35 %37 + OpReturnValue %38 + OpFunctionEnd + +; main function + %39 = OpFunction %5 None %6 + %40 = OpLabel + %41 = OpVariable %4 Function + %42 = OpVariable %4 Function + OpStore %41 %16 + OpStore %42 %17 + %43 = OpFunctionCall %2 %18 %41 %42 ; dot product function call + OpBranch %44 + %44 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + auto overflow_ids_unique_ptr = MakeUnique(1000); + auto overflow_ids_ptr = overflow_ids_unique_ptr.get(); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options, + std::move(overflow_ids_unique_ptr)); + auto transformation = TransformationInlineFunction(43, {{22, 45}, + {23, 46}, + {24, 47}, + {25, 48}, + {26, 49}, + {27, 50}, + {28, 51}, + {29, 52}}); + + // The following ids are left un-mapped; overflow ids will be required for + // them: 30, 31, 32, 33, 34, 35, 36, 37, 38, 100 + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context, + overflow_ids_ptr->GetIssuedOverflowIds()); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %39 "main" + +; Types + %2 = OpTypeFloat 32 + %3 = OpTypeVector %2 4 + %4 = OpTypePointer Function %3 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeFunction %2 %4 %4 + +; Constant scalars + %8 = OpConstant %2 1 + %9 = OpConstant %2 2 + %10 = OpConstant %2 3 + %11 = OpConstant %2 4 + %12 = OpConstant %2 5 + %13 = OpConstant %2 6 + %14 = OpConstant %2 7 + %15 = OpConstant %2 8 + +; Constant vectors + %16 = OpConstantComposite %3 %8 %9 %10 %11 + %17 = OpConstantComposite %3 %12 %13 %14 %15 + +; dot product function + %18 = OpFunction %2 None %7 + %19 = OpFunctionParameter %4 + %20 = OpFunctionParameter %4 + %21 = OpLabel + %22 = OpLoad %3 %19 + %23 = OpLoad %3 %20 + %24 = OpCompositeExtract %2 %22 0 + %25 = OpCompositeExtract %2 %23 0 + %26 = OpFMul %2 %24 %25 + %27 = OpCompositeExtract %2 %22 1 + %28 = OpCompositeExtract %2 %23 1 + %29 = OpFMul %2 %27 %28 + OpBranch %100 + %100 = OpLabel + %30 = OpCompositeExtract %2 %22 2 + %31 = OpCompositeExtract %2 %23 2 + %32 = OpFMul %2 %30 %31 + %33 = OpCompositeExtract %2 %22 3 + %34 = OpCompositeExtract %2 %23 3 + %35 = OpFMul %2 %33 %34 + %36 = OpFAdd %2 %26 %29 + %37 = OpFAdd %2 %32 %36 + %38 = OpFAdd %2 %35 %37 + OpReturnValue %38 + OpFunctionEnd + +; main function + %39 = OpFunction %5 None %6 + %40 = OpLabel + %41 = OpVariable %4 Function + %42 = OpVariable %4 Function + OpStore %41 %16 + OpStore %42 %17 + %45 = OpLoad %3 %41 + %46 = OpLoad %3 %42 + %47 = OpCompositeExtract %2 %45 0 + %48 = OpCompositeExtract %2 %46 0 + %49 = OpFMul %2 %47 %48 + %50 = OpCompositeExtract %2 %45 1 + %51 = OpCompositeExtract %2 %46 1 + %52 = OpFMul %2 %50 %51 + OpBranch %1000 + %1000 = OpLabel + %1001 = OpCompositeExtract %2 %45 2 + %1002 = OpCompositeExtract %2 %46 2 + %1003 = OpFMul %2 %1001 %1002 + %1004 = OpCompositeExtract %2 %45 3 + %1005 = OpCompositeExtract %2 %46 3 + %1006 = OpFMul %2 %1004 %1005 + %1007 = OpFAdd %2 %49 %52 + %1008 = OpFAdd %2 %1003 %1007 + %1009 = OpFAdd %2 %1006 %1008 + %43 = OpCopyObject %2 %1009 + OpBranch %44 + %44 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationInlineFunctionTest, OpPhiInBlockFollowingCall) { + // This test checks that if the block after the inlined function call has an + // OpPhi instruction and the called function contains multiple blocks then the + // OpPhi instruction gets updated to refer to the return block of the inlined + // function, since the block containing the call will no longer be a + // predecessor. + + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %13 = OpTypeBool + %14 = OpConstantTrue %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + %8 = OpFunctionCall %2 %6 + OpBranch %11 + %11 = OpLabel + %12 = OpPhi %13 %14 %10 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %20 + %20 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationInlineFunction(8, {{7, 100}, {20, 101}}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %13 = OpTypeBool + %14 = OpConstantTrue %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %101 + %101 = OpLabel + OpBranch %11 + %11 = OpLabel + %12 = OpPhi %13 %14 %101 + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %20 + %20 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_invert_comparison_operator_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_invert_comparison_operator_test.cpp new file mode 100644 index 0000000..3970f83 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_invert_comparison_operator_test.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_invert_comparison_operator.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationInvertComparisonOperatorTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpTypeBool + %9 = OpConstant %6 3 + %10 = OpConstant %6 4 + %11 = OpConstant %7 3 + %12 = OpConstant %7 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpSLessThan %8 %9 %10 + %14 = OpSLessThanEqual %8 %9 %10 + %15 = OpSGreaterThan %8 %9 %10 + %16 = OpSGreaterThanEqual %8 %9 %10 + %17 = OpULessThan %8 %11 %12 + %18 = OpULessThanEqual %8 %11 %12 + %19 = OpUGreaterThan %8 %11 %12 + %20 = OpUGreaterThanEqual %8 %11 %12 + %21 = OpIEqual %8 %9 %10 + %22 = OpINotEqual %8 %9 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Operator id is not valid. + ASSERT_FALSE(TransformationInvertComparisonOperator(23, 23).IsApplicable( + context.get(), transformation_context)); + + // Operator instruction is not supported. + ASSERT_FALSE(TransformationInvertComparisonOperator(5, 23).IsApplicable( + context.get(), transformation_context)); + + // Fresh id is not fresh. + ASSERT_FALSE(TransformationInvertComparisonOperator(13, 22).IsApplicable( + context.get(), transformation_context)); + + for (uint32_t fresh_id = 23, operator_id = 13; operator_id <= 22; + ++fresh_id, ++operator_id) { + TransformationInvertComparisonOperator transformation(operator_id, + fresh_id); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + std::string expected = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpTypeBool + %9 = OpConstant %6 3 + %10 = OpConstant %6 4 + %11 = OpConstant %7 3 + %12 = OpConstant %7 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %23 = OpSGreaterThanEqual %8 %9 %10 + %13 = OpLogicalNot %8 %23 + %24 = OpSGreaterThan %8 %9 %10 + %14 = OpLogicalNot %8 %24 + %25 = OpSLessThanEqual %8 %9 %10 + %15 = OpLogicalNot %8 %25 + %26 = OpSLessThan %8 %9 %10 + %16 = OpLogicalNot %8 %26 + %27 = OpUGreaterThanEqual %8 %11 %12 + %17 = OpLogicalNot %8 %27 + %28 = OpUGreaterThan %8 %11 %12 + %18 = OpLogicalNot %8 %28 + %29 = OpULessThanEqual %8 %11 %12 + %19 = OpLogicalNot %8 %29 + %30 = OpULessThan %8 %11 %12 + %20 = OpLogicalNot %8 %30 + %31 = OpINotEqual %8 %9 %10 + %21 = OpLogicalNot %8 %31 + %32 = OpIEqual %8 %9 %10 + %22 = OpLogicalNot %8 %32 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools \ No newline at end of file diff --git a/third_party/spirv-tools/test/fuzz/transformation_load_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_load_test.cpp new file mode 100644 index 0000000..a03ffdd --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_load_test.cpp @@ -0,0 +1,298 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_load.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationLoadTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function ; irrelevant + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 ; irrelevant + %13 = OpLabel + %46 = OpCopyObject %9 %11 ; irrelevant + %16 = OpAccessChain %15 %11 %14 ; irrelevant + OpReturnValue %21 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 27); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 11); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 46); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 16); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 52); + + transformation_context.GetFactManager()->AddFactBlockIsDead(36); + + // Variables with pointee types: + // 52 - ptr_to(7) + // 53 - ptr_to(6) + // 20 - ptr_to(8) + // 27 - ptr_to(8) - irrelevant + + // Access chains with pointee type: + // 22 - ptr_to(6) + // 26 - ptr_to(6) + // 30 - ptr_to(6) + // 33 - ptr_to(6) + // 38 - ptr_to(6) + // 40 - ptr_to(6) + // 43 - ptr_to(6) + // 16 - ptr_to(6) - irrelevant + + // Copied object with pointee type: + // 44 - ptr_to(8) + // 45 - ptr_to(6) + // 46 - ptr_to(8) - irrelevant + + // Function parameters with pointee type: + // 11 - ptr_to(8) - irrelevant + + // Pointers that cannot be used: + // 60 - null + // 61 - undefined + + // Bad: id is not fresh + ASSERT_FALSE(TransformationLoad( + 33, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: attempt to load from 11 from outside its function + ASSERT_FALSE(TransformationLoad( + 100, 11, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer is not available + ASSERT_FALSE(TransformationLoad( + 100, 33, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to insert before OpVariable + ASSERT_FALSE(TransformationLoad( + 100, 27, MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id does not exist + ASSERT_FALSE( + TransformationLoad(100, 1000, + MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id exists but does not have a type + ASSERT_FALSE(TransformationLoad( + 100, 5, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id exists and has a type, but is not a pointer + ASSERT_FALSE(TransformationLoad( + 100, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to load from null pointer + ASSERT_FALSE(TransformationLoad( + 100, 60, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to load from undefined pointer + ASSERT_FALSE(TransformationLoad( + 100, 61, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + // Bad: %40 is not available at the program point + ASSERT_FALSE( + TransformationLoad(100, 40, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: The described instruction does not exist + ASSERT_FALSE(TransformationLoad( + 100, 33, MakeInstructionDescriptor(1000, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationLoad transformation( + 100, 33, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + TransformationLoad transformation( + 101, 46, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + TransformationLoad transformation( + 102, 16, MakeInstructionDescriptor(16, SpvOpReturnValue, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + TransformationLoad transformation( + 103, 40, MakeInstructionDescriptor(43, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function ; irrelevant + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %100 = OpLoad %6 %33 + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %103 = OpLoad %6 %40 + %43 = OpAccessChain %15 %20 %14 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 ; irrelevant + %13 = OpLabel + %46 = OpCopyObject %9 %11 ; irrelevant + %16 = OpAccessChain %15 %11 %14 ; irrelevant + %101 = OpLoad %8 %46 + %102 = OpLoad %6 %16 + OpReturnValue %21 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_make_vector_operation_dynamic_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_make_vector_operation_dynamic_test.cpp new file mode 100644 index 0000000..064759a --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_make_vector_operation_dynamic_test.cpp @@ -0,0 +1,364 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_make_vector_operation_dynamic.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationMakeVectorOperationDynamicTest, IsApplicable) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %22 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeFloat 32 + %6 = OpTypeVector %5 2 + %7 = OpTypeVector %5 3 + %8 = OpTypeVector %5 4 + %9 = OpTypeMatrix %6 2 + +; Constant scalars + %10 = OpConstant %4 0 + %11 = OpConstant %4 1 + %12 = OpConstant %4 2 + %13 = OpConstant %5 0 + %14 = OpConstant %5 1 + %15 = OpConstant %5 2 + %16 = OpConstant %5 3 + +; Constant composites + %17 = OpConstantComposite %6 %13 %14 + %18 = OpConstantComposite %6 %15 %16 + %19 = OpConstantComposite %7 %13 %14 %15 + %20 = OpConstantComposite %8 %13 %14 %15 %16 + %21 = OpConstantComposite %9 %17 %18 + +; main function + %22 = OpFunction %2 None %3 + %23 = OpLabel + %24 = OpCompositeExtract %5 %17 0 + %25 = OpCompositeExtract %5 %17 1 + %26 = OpCompositeExtract %5 %18 0 + %27 = OpCompositeExtract %5 %18 1 + %28 = OpCompositeExtract %5 %19 0 + %29 = OpCompositeExtract %5 %19 1 + %30 = OpCompositeExtract %5 %19 2 + %31 = OpCompositeExtract %5 %20 0 + %32 = OpCompositeExtract %5 %20 1 + %33 = OpCompositeExtract %5 %20 2 + %34 = OpCompositeExtract %5 %20 3 + %35 = OpCompositeExtract %6 %21 0 + %36 = OpCompositeExtract %6 %21 1 + %37 = OpCompositeInsert %6 %15 %17 0 + %38 = OpCompositeInsert %6 %16 %17 1 + %39 = OpCompositeInsert %6 %13 %18 0 + %40 = OpCompositeInsert %6 %14 %18 1 + %41 = OpCompositeInsert %7 %13 %19 0 + %42 = OpCompositeInsert %7 %14 %19 1 + %43 = OpCompositeInsert %7 %15 %19 2 + %44 = OpCompositeInsert %8 %13 %20 0 + %45 = OpCompositeInsert %8 %14 %20 1 + %46 = OpCompositeInsert %8 %15 %20 2 + %47 = OpCompositeInsert %8 %16 %20 3 + %48 = OpCompositeInsert %9 %17 %21 0 + %49 = OpCompositeInsert %9 %18 %21 1 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests undefined instruction. + auto transformation = TransformationMakeVectorOperationDynamic(50, 10); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests non-composite instruction. + transformation = TransformationMakeVectorOperationDynamic(23, 11); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests composite being a matrix. + transformation = TransformationMakeVectorOperationDynamic(48, 12); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests literal not defined as constant. + transformation = TransformationMakeVectorOperationDynamic(34, 51); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests applicable instructions. + transformation = TransformationMakeVectorOperationDynamic(24, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation = TransformationMakeVectorOperationDynamic(25, 11); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation = TransformationMakeVectorOperationDynamic(26, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation = TransformationMakeVectorOperationDynamic(37, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation = TransformationMakeVectorOperationDynamic(38, 11); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + transformation = TransformationMakeVectorOperationDynamic(39, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMakeVectorOperationDynamicTest, Apply) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %20 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeFloat 32 + %6 = OpTypeVector %5 2 + %7 = OpTypeVector %5 3 + %8 = OpTypeVector %5 4 + +; Constant scalars + %9 = OpConstant %4 0 + %10 = OpConstant %4 1 + %11 = OpConstant %4 2 + %12 = OpConstant %4 3 + %13 = OpConstant %5 0 + %14 = OpConstant %5 1 + %15 = OpConstant %5 2 + %16 = OpConstant %5 3 + +; Constant vectors + %17 = OpConstantComposite %6 %13 %14 + %18 = OpConstantComposite %7 %13 %14 %15 + %19 = OpConstantComposite %8 %13 %14 %15 %16 + +; main function + %20 = OpFunction %2 None %3 + %21 = OpLabel + %22 = OpCompositeExtract %5 %17 0 + %23 = OpCompositeExtract %5 %17 1 + %24 = OpCompositeExtract %5 %18 0 + %25 = OpCompositeExtract %5 %18 1 + %26 = OpCompositeExtract %5 %18 2 + %27 = OpCompositeExtract %5 %19 0 + %28 = OpCompositeExtract %5 %19 1 + %29 = OpCompositeExtract %5 %19 2 + %30 = OpCompositeExtract %5 %19 3 + %31 = OpCompositeInsert %6 %13 %17 0 + %32 = OpCompositeInsert %6 %14 %17 1 + %33 = OpCompositeInsert %7 %13 %18 0 + %34 = OpCompositeInsert %7 %14 %18 1 + %35 = OpCompositeInsert %7 %15 %18 2 + %36 = OpCompositeInsert %8 %13 %19 0 + %37 = OpCompositeInsert %8 %14 %19 1 + %38 = OpCompositeInsert %8 %15 %19 2 + %39 = OpCompositeInsert %8 %16 %19 3 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationMakeVectorOperationDynamic(22, 9); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(23, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(24, 9); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(25, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(26, 11); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(27, 9); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(28, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(29, 11); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(30, 12); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(31, 9); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(32, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(33, 9); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(34, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(35, 11); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(36, 9); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(37, 10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(38, 11); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + transformation = TransformationMakeVectorOperationDynamic(39, 12); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %20 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeFloat 32 + %6 = OpTypeVector %5 2 + %7 = OpTypeVector %5 3 + %8 = OpTypeVector %5 4 + +; Constant scalars + %9 = OpConstant %4 0 + %10 = OpConstant %4 1 + %11 = OpConstant %4 2 + %12 = OpConstant %4 3 + %13 = OpConstant %5 0 + %14 = OpConstant %5 1 + %15 = OpConstant %5 2 + %16 = OpConstant %5 3 + +; Constant vectors + %17 = OpConstantComposite %6 %13 %14 + %18 = OpConstantComposite %7 %13 %14 %15 + %19 = OpConstantComposite %8 %13 %14 %15 %16 + +; main function + %20 = OpFunction %2 None %3 + %21 = OpLabel + %22 = OpVectorExtractDynamic %5 %17 %9 + %23 = OpVectorExtractDynamic %5 %17 %10 + %24 = OpVectorExtractDynamic %5 %18 %9 + %25 = OpVectorExtractDynamic %5 %18 %10 + %26 = OpVectorExtractDynamic %5 %18 %11 + %27 = OpVectorExtractDynamic %5 %19 %9 + %28 = OpVectorExtractDynamic %5 %19 %10 + %29 = OpVectorExtractDynamic %5 %19 %11 + %30 = OpVectorExtractDynamic %5 %19 %12 + %31 = OpVectorInsertDynamic %6 %17 %13 %9 + %32 = OpVectorInsertDynamic %6 %17 %14 %10 + %33 = OpVectorInsertDynamic %7 %18 %13 %9 + %34 = OpVectorInsertDynamic %7 %18 %14 %10 + %35 = OpVectorInsertDynamic %7 %18 %15 %11 + %36 = OpVectorInsertDynamic %8 %19 %13 %9 + %37 = OpVectorInsertDynamic %8 %19 %14 %10 + %38 = OpVectorInsertDynamic %8 %19 %15 %11 + %39 = OpVectorInsertDynamic %8 %19 %16 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_merge_blocks_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_merge_blocks_test.cpp new file mode 100644 index 0000000..d5289d3 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_merge_blocks_test.cpp @@ -0,0 +1,703 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_merge_blocks.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationMergeBlocksTest, BlockDoesNotExist) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationMergeBlocks(3).IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(TransformationMergeBlocks(7).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationMergeBlocksTest, DoNotMergeFirstBlockHasMultipleSuccessors) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %6 %9 + %6 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationMergeBlocks(6).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationMergeBlocksTest, + DoNotMergeSecondBlockHasMultiplePredecessors) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %6 %9 + %6 = OpLabel + OpBranch %10 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationMergeBlocks(10).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %8 %6 %9 + %6 = OpLabel + OpBranch %11 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationMergeBlocks transformation(10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %8 %6 %9 + %6 = OpLabel + OpBranch %11 + %9 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %6 + %6 = OpLabel + OpBranchConditional %8 %9 %11 + %9 = OpLabel + OpBranch %10 + %11 = OpLabel + OpBranch %5 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationMergeBlocks transformation(10); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %12 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %9 %11 None + OpBranch %6 + %6 = OpLabel + OpBranchConditional %8 %9 %11 + %9 = OpLabel + OpReturn + %11 = OpLabel + OpBranch %5 + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsLoopContinue) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %13 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %8 %9 %12 + %12 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %5 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationMergeBlocks transformation(11); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %13 = OpLabel + OpBranch %5 + %5 = OpLabel + OpLoopMerge %10 %12 None + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %8 %9 %12 + %12 = OpLabel + OpBranch %5 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockStartsWithOpPhi) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %7 = OpTypeBool + %8 = OpUndef %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %9 = OpPhi %7 %8 %5 + %10 = OpCopyObject %7 %9 + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %7 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationMergeBlocks transformation(6); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %7 = OpTypeBool + %8 = OpUndef %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpCopyObject %7 %8 + OpBranch %11 + %11 = OpLabel + %12 = OpCopyObject %7 %8 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, BasicMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %100 + %100 = OpLabel + OpStore %10 %11 + %12 = OpLoad %6 %10 + %13 = OpLoad %6 %8 + OpBranch %101 + %101 = OpLabel + %14 = OpIAdd %6 %13 %12 + OpStore %8 %14 + %15 = OpLoad %6 %8 + OpBranch %102 + %102 = OpLabel + %16 = OpLoad %6 %10 + %17 = OpIMul %6 %16 %15 + OpBranch %103 + %103 = OpLabel + OpStore %10 %17 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + for (auto& transformation : + {TransformationMergeBlocks(100), TransformationMergeBlocks(101), + TransformationMergeBlocks(102), TransformationMergeBlocks(103)}) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %12 = OpLoad %6 %10 + %13 = OpLoad %6 %8 + %14 = OpIAdd %6 %13 %12 + OpStore %8 %14 + %15 = OpLoad %6 %8 + %16 = OpLoad %6 %10 + %17 = OpIMul %6 %16 %15 + OpStore %10 %17 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, MergeWhenSecondBlockIsSelectionHeader) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %50 = OpTypeBool + %51 = OpConstantTrue %50 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %100 + %100 = OpLabel + OpStore %10 %11 + %12 = OpLoad %6 %10 + %13 = OpLoad %6 %8 + OpBranch %101 + %101 = OpLabel + OpSelectionMerge %103 None + OpBranchConditional %51 %102 %103 + %102 = OpLabel + %14 = OpIAdd %6 %13 %12 + OpStore %8 %14 + OpBranch %103 + %103 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + for (auto& transformation : + {TransformationMergeBlocks(101), TransformationMergeBlocks(100)}) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %50 = OpTypeBool + %51 = OpConstantTrue %50 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %12 = OpLoad %6 %10 + %13 = OpLoad %6 %8 + OpSelectionMerge %103 None + OpBranchConditional %51 %102 %103 + %102 = OpLabel + %14 = OpIAdd %6 %13 %12 + OpStore %8 %14 + OpBranch %103 + %103 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeBlocksTest, + MergeWhenFirstBlockIsLoopMergeFollowedByUnconditionalBranch) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %50 = OpTypeBool + %51 = OpConstantTrue %50 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %100 + %100 = OpLabel + OpLoopMerge %102 %103 None + OpBranch %101 + %101 = OpLabel + %200 = OpCopyObject %6 %9 + OpBranchConditional %51 %102 %103 + %103 = OpLabel + OpBranch %100 + %102 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationMergeBlocks transformation(101); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %50 = OpTypeBool + %51 = OpConstantTrue %50 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %100 + %100 = OpLabel + %200 = OpCopyObject %6 %9 + OpLoopMerge %102 %103 None + OpBranchConditional %51 %102 %103 + %103 = OpLabel + OpBranch %100 + %102 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_merge_function_returns_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_merge_function_returns_test.cpp new file mode 100644 index 0000000..e60d345 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_merge_function_returns_test.cpp @@ -0,0 +1,1886 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_merge_function_returns.h" + +#include "gtest/gtest.h" +#include "source/fuzz/counter_overflow_id_source.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +protobufs::ReturnMergingInfo MakeReturnMergingInfo( + uint32_t merge_block_id, uint32_t is_returning_id, + uint32_t maybe_return_val_id, + const std::map& opphi_to_suitable_id) { + protobufs::ReturnMergingInfo result; + result.set_merge_block_id(merge_block_id); + result.set_is_returning_id(is_returning_id); + result.set_maybe_return_val_id(maybe_return_val_id); + *result.mutable_opphi_to_suitable_id() = + fuzzerutil::MapToRepeatedUInt32Pair(opphi_to_suitable_id); + return result; +} + +TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeFunction %5 + %7 = OpTypeFloat 32 + %8 = OpTypeFunction %7 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %11 = OpConstantFalse %9 + %12 = OpConstant %5 0 + %13 = OpConstant %5 1 + %2 = OpFunction %3 None %4 + %14 = OpLabel + %15 = OpFunctionCall %3 %16 + %17 = OpFunctionCall %3 %18 + %19 = OpFunctionCall %3 %20 + %21 = OpFunctionCall %7 %22 + OpReturn + OpFunctionEnd + %16 = OpFunction %3 None %4 + %23 = OpLabel + OpSelectionMerge %24 None + OpBranchConditional %10 %25 %26 + %25 = OpLabel + OpReturn + %26 = OpLabel + OpReturn + %24 = OpLabel + OpUnreachable + OpFunctionEnd + %18 = OpFunction %3 None %4 + %27 = OpLabel + OpBranch %28 + %28 = OpLabel + OpLoopMerge %29 %30 None + OpBranch %31 + %31 = OpLabel + OpBranchConditional %10 %32 %29 + %32 = OpLabel + OpReturn + %30 = OpLabel + OpBranch %28 + %29 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %3 None %4 + %33 = OpLabel + OpBranch %34 + %34 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + OpBranchConditional %10 %38 %35 + %38 = OpLabel + OpReturn + %36 = OpLabel + OpBranch %34 + %35 = OpLabel + %39 = OpFunctionCall %3 %18 + OpReturn + OpFunctionEnd + %22 = OpFunction %7 None %8 + %40 = OpLabel + OpBranch %51 + %51 = OpLabel + OpLoopMerge %41 %53 None + OpBranchConditional %10 %42 %41 + %42 = OpLabel + %43 = OpConvertSToF %7 %12 + OpReturnValue %43 + %41 = OpLabel + %44 = OpConvertSToF %7 %13 + OpReturnValue %44 + %53 = OpLabel + OpBranch %51 + OpFunctionEnd + %45 = OpFunction %5 None %6 + %46 = OpLabel + OpBranch %52 + %52 = OpLabel + %47 = OpConvertSToF %7 %13 + OpLoopMerge %48 %54 None + OpBranchConditional %10 %49 %48 + %49 = OpLabel + OpReturnValue %12 + %48 = OpLabel + %50 = OpCopyObject %5 %12 + OpReturnValue %13 + %54 = OpLabel + OpBranch %52 + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Function %1 does not exist. + ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 101, 0, 0, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // The entry block (%22) of function %15 does not branch unconditionally to + // the following block. + ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 101, 0, 0, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // Block %28 is the merge block of a loop containing a return instruction, but + // it contains an OpReturn instruction (so, it contains instructions that are + // not OpLabel, OpPhi or OpBranch). + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 18, 100, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // Block %34 is the merge block of a loop containing a return instruction, but + // it contains an OpFunctionCall instruction (so, it contains instructions + // that are not OpLabel, OpPhi or OpBranch). + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 20, 100, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // Id %1000 cannot be found in the module and there is no id of the correct + // type (float) available at the end of the entry block of function %21. + ASSERT_FALSE(TransformationMergeFunctionReturns(22, 100, 101, 102, 1000, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // Id %47 is of type float, while function %45 has return type int. + ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 47, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // Id %50 is not available at the end of the entry block of function %45. + ASSERT_FALSE(TransformationMergeFunctionReturns(45, 100, 101, 102, 50, {{}}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) { + { + // OpConstantTrue is missing. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "A(" + OpDecorate %3 RelaxedPrecision + OpDecorate %4 RelaxedPrecision + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypeFunction %7 + %9 = OpTypeBool + %10 = OpConstantFalse %9 + %11 = OpConstant %7 1 + %12 = OpConstant %7 2 + %2 = OpFunction %5 None %6 + %13 = OpLabel + %4 = OpFunctionCall %7 %3 + OpReturn + OpFunctionEnd + %3 = OpFunction %7 None %8 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %10 %17 %16 + %17 = OpLabel + OpReturnValue %11 + %16 = OpLabel + OpReturnValue %12 + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}}) + .IsApplicable(context.get(), transformation_context)); + } + { + // OpConstantFalse is missing. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "A(" + OpDecorate %3 RelaxedPrecision + OpDecorate %4 RelaxedPrecision + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypeFunction %7 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %11 = OpConstant %7 1 + %12 = OpConstant %7 2 + %2 = OpFunction %5 None %6 + %13 = OpLabel + %4 = OpFunctionCall %7 %3 + OpReturn + OpFunctionEnd + %3 = OpFunction %7 None %8 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %10 %17 %16 + %17 = OpLabel + OpReturnValue %11 + %16 = OpLabel + OpReturnValue %12 + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationMergeFunctionReturns(3, 100, 101, 0, 0, {{}}) + .IsApplicable(context.get(), transformation_context)); + } +} + +TEST(TransformationMergeFunctionReturnsTest, InvalidIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeFunction %5 + %42 = OpTypeFloat 32 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpConstantFalse %7 + %10 = OpConstant %5 0 + %11 = OpConstant %5 1 + %2 = OpFunction %3 None %4 + %12 = OpLabel + %13 = OpFunctionCall %5 %14 + %15 = OpFunctionCall %3 %16 + OpReturn + OpFunctionEnd + %17 = OpFunction %3 None %4 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + OpLoopMerge %20 %21 None + OpBranch %22 + %22 = OpLabel + OpBranchConditional %8 %23 %20 + %23 = OpLabel + OpReturn + %21 = OpLabel + OpBranch %19 + %20 = OpLabel + OpBranch %24 + %24 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %5 None %6 + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + OpBranchConditional %8 %30 %27 + %30 = OpLabel + OpReturnValue %10 + %28 = OpLabel + OpBranch %26 + %27 = OpLabel + OpBranch %33 + %33 = OpLabel + OpReturnValue %11 + OpFunctionEnd + %16 = OpFunction %3 None %4 + %34 = OpLabel + OpBranch %35 + %35 = OpLabel + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %43 = OpConvertSToF %42 %10 + OpBranchConditional %8 %39 %36 + %39 = OpLabel + OpReturn + %37 = OpLabel + %44 = OpConvertSToF %42 %10 + OpBranch %35 + %36 = OpLabel + %31 = OpPhi %42 %43 %38 + %32 = OpPhi %5 %11 %38 + OpBranch %40 + %40 = OpLabel + %41 = OpFunctionCall %3 %17 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Fresh id %100 is used twice. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 17, 100, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // Fresh id %100 is used twice. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // %0 cannot be a fresh id for the new merge block. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 17, 100, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // %0 cannot be a fresh id for the new header block. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 17, 0, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // %0 cannot be a fresh id for the new |is_returning| instruction in an + // existing merge block. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 17, 100, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // %0 cannot be a fresh id for the new |return_val| instruction in the new + // return block. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 14, 100, 101, 0, 10, {{MakeReturnMergingInfo(27, 102, 103, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // %0 cannot be a fresh id for the new |maybe_return_val| instruction in an + // existing merge block, inside a non-void function. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 103, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // Fresh id %102 is repeated. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 14, 100, 101, 102, 10, {{MakeReturnMergingInfo(27, 102, 104, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // Id %11 (type int) does not have the correct type (float) for OpPhi + // instruction %31. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 16, 100, 101, 0, 0, + {{MakeReturnMergingInfo(36, 103, 104, {{{31, 11}, {32, 11}}})}}) + .IsApplicable(context.get(), transformation_context)); + + // Id %11 (type int) does not have the correct type (float) for OpPhi + // instruction %31. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 16, 100, 101, 0, 0, + {{MakeReturnMergingInfo(36, 102, 0, {{{31, 11}, {32, 11}}})}}) + .IsApplicable(context.get(), transformation_context)); + + // Id %43 is not available at the end of the entry block. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 16, 100, 101, 0, 0, + {{MakeReturnMergingInfo(36, 102, 0, {{{31, 44}, {32, 11}}})}}) + .IsApplicable(context.get(), transformation_context)); + + // There is not a mapping for id %31 (float OpPhi instruction in a loop merge + // block) and no suitable id is available at the end of the entry block. + ASSERT_FALSE(TransformationMergeFunctionReturns( + 16, 100, 101, 0, 0, + {{MakeReturnMergingInfo(36, 102, 0, {{{32, 11}}})}}) + .IsApplicable(context.get(), transformation_context)); + + // Id %1000 cannot be found in the module and no suitable id for OpPhi %31 is + // available at the end of the entry block. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 16, 100, 101, 0, 0, + {{MakeReturnMergingInfo(36, 102, 0, {{{31, 1000}, {32, 11}}})}}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMergeFunctionReturnsTest, Simple) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeFunction %5 + %7 = OpTypeFloat 32 + %8 = OpTypeFunction %7 %7 + %9 = OpTypeFunction %7 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %40 = OpConstantFalse %10 + %12 = OpConstant %5 1 + %2 = OpFunction %3 None %4 + %13 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %3 None %4 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %11 %18 %17 + %18 = OpLabel + OpReturn + %17 = OpLabel + OpReturn + OpFunctionEnd + %19 = OpFunction %5 None %6 + %20 = OpLabel + OpBranch %21 + %21 = OpLabel + OpSelectionMerge %22 None + OpBranchConditional %11 %23 %24 + %23 = OpLabel + OpReturnValue %12 + %24 = OpLabel + %25 = OpIAdd %5 %12 %12 + OpReturnValue %25 + %22 = OpLabel + OpUnreachable + OpFunctionEnd + %26 = OpFunction %7 None %8 + %27 = OpFunctionParameter %7 + %28 = OpLabel + OpBranch %29 + %29 = OpLabel + OpSelectionMerge %30 None + OpBranchConditional %11 %31 %30 + %31 = OpLabel + %32 = OpFAdd %7 %27 %27 + OpReturnValue %32 + %30 = OpLabel + OpReturnValue %27 + OpFunctionEnd + %33 = OpFunction %7 None %9 + %34 = OpLabel + %35 = OpConvertSToF %7 %12 + OpBranch %36 + %36 = OpLabel + OpSelectionMerge %37 None + OpBranchConditional %11 %38 %37 + %38 = OpLabel + %39 = OpFAdd %7 %35 %35 + OpReturnValue %39 + %37 = OpLabel + OpReturnValue %35 + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // The 0s are allowed because the function's return type is void. + auto transformation1 = + TransformationMergeFunctionReturns(14, 100, 101, 0, 0, {{}}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %12 is available at the end of the entry block of %19 (it is a global + // variable). + ASSERT_TRUE(TransformationMergeFunctionReturns(19, 110, 111, 112, 12, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // %1000 cannot be found in the module, but there is a suitable id available + // at the end of the entry block (%12). + auto transformation2 = + TransformationMergeFunctionReturns(19, 110, 111, 112, 1000, {{}}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %27 is available at the end of the entry block of %26 (it is a function + // parameter). + ASSERT_TRUE(TransformationMergeFunctionReturns(26, 120, 121, 122, 27, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // %1000 cannot be found in the module, but there is a suitable id available + // at the end of the entry block (%27). + auto transformation3 = + TransformationMergeFunctionReturns(26, 120, 121, 122, 1000, {{}}); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %35 is available at the end of the entry block of %33 (it is in the entry + // block). + ASSERT_TRUE(TransformationMergeFunctionReturns(26, 130, 131, 132, 27, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // %1000 cannot be found in the module, but there is a suitable id available + // at the end of the entry block (%35). + auto transformation4 = + TransformationMergeFunctionReturns(33, 130, 131, 132, 1000, {{}}); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeFunction %5 + %7 = OpTypeFloat 32 + %8 = OpTypeFunction %7 %7 + %9 = OpTypeFunction %7 + %10 = OpTypeBool + %11 = OpConstantTrue %10 + %40 = OpConstantFalse %10 + %12 = OpConstant %5 1 + %2 = OpFunction %3 None %4 + %13 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %3 None %4 + %15 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %100 None + OpBranchConditional %11 %16 %100 + %16 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %11 %18 %17 + %18 = OpLabel + OpBranch %101 + %17 = OpLabel + OpBranch %101 + %101 = OpLabel + OpReturn + OpFunctionEnd + %19 = OpFunction %5 None %6 + %20 = OpLabel + OpBranch %110 + %110 = OpLabel + OpLoopMerge %111 %110 None + OpBranchConditional %11 %21 %110 + %21 = OpLabel + OpSelectionMerge %22 None + OpBranchConditional %11 %23 %24 + %23 = OpLabel + OpBranch %111 + %24 = OpLabel + %25 = OpIAdd %5 %12 %12 + OpBranch %111 + %22 = OpLabel + OpUnreachable + %111 = OpLabel + %112 = OpPhi %5 %12 %23 %25 %24 + OpReturnValue %112 + OpFunctionEnd + %26 = OpFunction %7 None %8 + %27 = OpFunctionParameter %7 + %28 = OpLabel + OpBranch %120 + %120 = OpLabel + OpLoopMerge %121 %120 None + OpBranchConditional %11 %29 %120 + %29 = OpLabel + OpSelectionMerge %30 None + OpBranchConditional %11 %31 %30 + %31 = OpLabel + %32 = OpFAdd %7 %27 %27 + OpBranch %121 + %30 = OpLabel + OpBranch %121 + %121 = OpLabel + %122 = OpPhi %7 %27 %30 %32 %31 + OpReturnValue %122 + OpFunctionEnd + %33 = OpFunction %7 None %9 + %34 = OpLabel + %35 = OpConvertSToF %7 %12 + OpBranch %130 + %130 = OpLabel + OpLoopMerge %131 %130 None + OpBranchConditional %11 %36 %130 + %36 = OpLabel + OpSelectionMerge %37 None + OpBranchConditional %11 %38 %37 + %38 = OpLabel + %39 = OpFAdd %7 %35 %35 + OpBranch %131 + %37 = OpLabel + OpBranch %131 + %131 = OpLabel + %132 = OpPhi %7 %35 %37 %39 %38 + OpReturnValue %132 + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeFunction %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpConstantFalse %7 + %10 = OpConstant %5 2 + %11 = OpConstant %5 1 + %12 = OpConstant %5 3 + %2 = OpFunction %3 None %4 + %13 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %5 None %6 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpLoopMerge %17 %16 None + OpBranchConditional %8 %18 %16 + %18 = OpLabel + OpLoopMerge %19 %20 None + OpBranchConditional %8 %19 %21 + %19 = OpLabel + OpBranch %17 + %21 = OpLabel + OpReturnValue %12 + %17 = OpLabel + OpBranch %22 + %20 = OpLabel + OpBranch %18 + %22 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + OpBranchConditional %8 %26 %23 + %26 = OpLabel + OpSelectionMerge %27 None + OpBranchConditional %9 %28 %27 + %28 = OpLabel + OpBranch %29 + %29 = OpLabel + OpLoopMerge %30 %29 None + OpBranchConditional %8 %30 %29 + %30 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + OpBranchConditional %9 %34 %31 + %34 = OpLabel + OpReturnValue %10 + %32 = OpLabel + OpBranch %30 + %31 = OpLabel + %35 = OpPhi %5 %11 %33 + %36 = OpPhi %5 %10 %33 + OpBranch %37 + %37 = OpLabel + OpReturnValue %35 + %27 = OpLabel + OpBranch %24 + %24 = OpLabel + OpBranch %22 + %23 = OpLabel + OpBranch %38 + %38 = OpLabel + OpReturnValue %12 + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = TransformationMergeFunctionReturns( + 14, 100, 101, 102, 11, + {{MakeReturnMergingInfo(19, 103, 104, {{}}), + MakeReturnMergingInfo(17, 105, 106, {{}}), + MakeReturnMergingInfo(31, 107, 108, {{{35, 10}, {36, 12}}}), + MakeReturnMergingInfo(23, 109, 110, {})}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeFunction %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpConstantFalse %7 + %10 = OpConstant %5 2 + %11 = OpConstant %5 1 + %12 = OpConstant %5 3 + %2 = OpFunction %3 None %4 + %13 = OpLabel + OpReturn + OpFunctionEnd + %14 = OpFunction %5 None %6 + %15 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %100 None + OpBranchConditional %8 %16 %100 + %16 = OpLabel + OpLoopMerge %17 %16 None + OpBranchConditional %8 %18 %16 + %18 = OpLabel + OpLoopMerge %19 %20 None + OpBranchConditional %8 %19 %21 + %19 = OpLabel + %103 = OpPhi %7 %8 %21 %9 %18 + %104 = OpPhi %5 %12 %21 %11 %18 + OpBranch %17 + %21 = OpLabel + OpBranch %19 + %17 = OpLabel + %105 = OpPhi %7 %103 %19 + %106 = OpPhi %5 %104 %19 + OpBranchConditional %105 %101 %22 + %20 = OpLabel + OpBranch %18 + %22 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + OpBranchConditional %8 %26 %23 + %26 = OpLabel + OpSelectionMerge %27 None + OpBranchConditional %9 %28 %27 + %28 = OpLabel + OpBranch %29 + %29 = OpLabel + OpLoopMerge %30 %29 None + OpBranchConditional %8 %30 %29 + %30 = OpLabel + OpLoopMerge %31 %32 None + OpBranch %33 + %33 = OpLabel + OpBranchConditional %9 %34 %31 + %34 = OpLabel + OpBranch %31 + %32 = OpLabel + OpBranch %30 + %31 = OpLabel + %107 = OpPhi %7 %8 %34 %9 %33 + %108 = OpPhi %5 %10 %34 %11 %33 + %35 = OpPhi %5 %11 %33 %10 %34 + %36 = OpPhi %5 %10 %33 %12 %34 + OpBranchConditional %107 %23 %37 + %37 = OpLabel + OpBranch %23 + %27 = OpLabel + OpBranch %24 + %24 = OpLabel + OpBranch %22 + %23 = OpLabel + %109 = OpPhi %7 %107 %31 %8 %37 %9 %25 + %110 = OpPhi %5 %108 %31 %35 %37 %11 %25 + OpBranchConditional %109 %101 %38 + %38 = OpLabel + OpBranch %101 + %101 = OpLabel + %102 = OpPhi %5 %106 %17 %110 %23 %12 %38 + OpReturnValue %102 + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeFunction %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpConstantFalse %7 + %10 = OpConstant %5 1 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %5 None %6 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + %15 = OpIAdd %5 %10 %10 + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + OpBranchConditional %8 %19 %16 + %19 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %9 %21 %20 + %21 = OpLabel + OpReturnValue %10 + %20 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranchConditional %8 %14 %16 + %16 = OpLabel + %22 = OpPhi %5 %15 %17 %10 %18 + OpBranch %23 + %23 = OpLabel + OpReturnValue %22 + OpFunctionEnd + %24 = OpFunction %3 None %4 + %25 = OpLabel + OpBranch %26 + %26 = OpLabel + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + OpBranchConditional %8 %30 %27 + %30 = OpLabel + OpSelectionMerge %31 None + OpBranchConditional %9 %32 %31 + %32 = OpLabel + OpReturn + %31 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranch %26 + %27 = OpLabel + %33 = OpPhi %5 %10 %29 + OpBranch %34 + %34 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto overflow_ids_unique_ptr = MakeUnique(1000); + auto overflow_ids_ptr = overflow_ids_unique_ptr.get(); + TransformationContext transformation_context_with_overflow_ids( + MakeUnique(context.get()), validator_options, + std::move(overflow_ids_unique_ptr)); + + // No mapping from merge block %16 to fresh ids is given, so overflow ids are + // needed. + auto transformation1 = + TransformationMergeFunctionReturns(12, 100, 101, 102, 10, {{}}); + +#ifndef NDEBUG + ASSERT_DEATH( + transformation1.IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); +#endif + + ASSERT_TRUE(transformation1.IsApplicable( + context.get(), transformation_context_with_overflow_ids)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context_with_overflow_ids, + overflow_ids_ptr->GetIssuedOverflowIds()); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // No mapping from merge block %27 to fresh ids is given, so overflow ids are + // needed. + auto transformation2 = + TransformationMergeFunctionReturns(24, 110, 111, 0, 0, {{}}); + +#ifndef NDEBUG + ASSERT_DEATH( + transformation2.IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); +#endif + + ASSERT_TRUE(transformation2.IsApplicable( + context.get(), transformation_context_with_overflow_ids)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context_with_overflow_ids, {1002}); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpTypeFunction %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpConstantFalse %7 + %10 = OpConstant %5 1 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %5 None %6 + %13 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %100 None + OpBranchConditional %8 %14 %100 + %14 = OpLabel + %15 = OpIAdd %5 %10 %10 + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + OpBranchConditional %8 %19 %16 + %19 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %9 %21 %20 + %21 = OpLabel + OpBranch %16 + %20 = OpLabel + OpBranch %17 + %17 = OpLabel + OpBranchConditional %8 %14 %16 + %16 = OpLabel + %1000 = OpPhi %7 %8 %21 %9 %17 %9 %18 + %1001 = OpPhi %5 %10 %21 %10 %17 %10 %18 + %22 = OpPhi %5 %15 %17 %10 %18 %10 %21 + OpBranchConditional %1000 %101 %23 + %23 = OpLabel + OpBranch %101 + %101 = OpLabel + %102 = OpPhi %5 %1001 %16 %22 %23 + OpReturnValue %102 + OpFunctionEnd + %24 = OpFunction %3 None %4 + %25 = OpLabel + OpBranch %110 + %110 = OpLabel + OpLoopMerge %111 %110 None + OpBranchConditional %8 %26 %110 + %26 = OpLabel + OpLoopMerge %27 %28 None + OpBranch %29 + %29 = OpLabel + OpBranchConditional %8 %30 %27 + %30 = OpLabel + OpSelectionMerge %31 None + OpBranchConditional %9 %32 %31 + %32 = OpLabel + OpBranch %27 + %31 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranch %26 + %27 = OpLabel + %1002 = OpPhi %7 %8 %32 %9 %29 + %33 = OpPhi %5 %10 %29 %10 %32 + OpBranchConditional %1002 %111 %34 + %34 = OpLabel + OpBranch %111 + %111 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %8 = OpTypeInt 32 1 + %9 = OpTypeFunction %3 %8 + %10 = OpTypeFloat 32 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %3 None %9 + %13 = OpFunctionParameter %8 + %14 = OpLabel + %15 = OpConvertSToF %10 %13 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %17 %18 None + OpBranch %19 + %19 = OpLabel + OpBranchConditional %6 %20 %17 + %20 = OpLabel + OpSelectionMerge %21 None + OpBranchConditional %7 %22 %21 + %22 = OpLabel + OpReturn + %21 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %16 + %17 = OpLabel + %23 = OpPhi %8 %13 %19 + %24 = OpPhi %10 %15 %19 + %25 = OpPhi %5 %6 %19 + OpBranch %26 + %26 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // This test checks whether the transformation is able to find suitable ids + // to use in existing OpPhi instructions if they are not provided in the + // corresponding mapping. + + auto transformation = TransformationMergeFunctionReturns( + 12, 101, 102, 0, 0, + {{MakeReturnMergingInfo(17, 103, 0, {{{25, 7}, {35, 8}}})}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %8 = OpTypeInt 32 1 + %9 = OpTypeFunction %3 %8 + %10 = OpTypeFloat 32 + %2 = OpFunction %3 None %4 + %11 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %3 None %9 + %13 = OpFunctionParameter %8 + %14 = OpLabel + %15 = OpConvertSToF %10 %13 + OpBranch %101 + %101 = OpLabel + OpLoopMerge %102 %101 None + OpBranchConditional %6 %16 %101 + %16 = OpLabel + OpLoopMerge %17 %18 None + OpBranch %19 + %19 = OpLabel + OpBranchConditional %6 %20 %17 + %20 = OpLabel + OpSelectionMerge %21 None + OpBranchConditional %7 %22 %21 + %22 = OpLabel + OpBranch %17 + %21 = OpLabel + OpBranch %18 + %18 = OpLabel + OpBranch %16 + %17 = OpLabel + %103 = OpPhi %5 %6 %22 %7 %19 + %23 = OpPhi %8 %13 %19 %13 %22 + %24 = OpPhi %10 %15 %19 %15 %22 + %25 = OpPhi %5 %6 %19 %7 %22 + OpBranchConditional %103 %102 %26 + %26 = OpLabel + OpBranch %102 + %102 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) { + // An id defined in a loop is used in the corresponding merge block. After the + // transformation, the id will not dominate the merge block anymore. This is + // only OK if the use is inside an OpPhi instruction. (Note that there is also + // another condition for this transformation that forbids non-OpPhi + // instructions in relevant merge blocks, but that case is also considered + // here for completeness). + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %7 %13 %14 + %14 = OpLabel + OpReturn + %13 = OpLabel + %15 = OpCopyObject %5 %7 + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %9 %10 + %10 = OpLabel + %16 = OpCopyObject %5 %15 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + %18 = OpFunction %3 None %4 + %19 = OpLabel + OpBranch %20 + %20 = OpLabel + OpLoopMerge %21 %22 None + OpBranch %23 + %23 = OpLabel + OpSelectionMerge %24 None + OpBranchConditional %7 %24 %25 + %25 = OpLabel + OpReturn + %24 = OpLabel + %26 = OpCopyObject %5 %7 + OpBranch %22 + %22 = OpLabel + OpBranchConditional %7 %20 %21 + %21 = OpLabel + %27 = OpPhi %5 %26 %22 + OpBranch %28 + %28 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // In function %2, the definition of id %15 will not dominate its use in + // instruction %16 (inside merge block %10) after a new branch from return + // block %14 is added. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // In function %18, The definition of id %26 will still dominate its use in + // instruction %27 (inside merge block %21), because %27 is an OpPhi + // instruction. + auto transformation = TransformationMergeFunctionReturns( + 18, 100, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %7 %13 %14 + %14 = OpLabel + OpReturn + %13 = OpLabel + %15 = OpCopyObject %5 %7 + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %9 %10 + %10 = OpLabel + %16 = OpCopyObject %5 %15 + OpBranch %17 + %17 = OpLabel + OpReturn + OpFunctionEnd + %18 = OpFunction %3 None %4 + %19 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %100 None + OpBranchConditional %6 %20 %100 + %20 = OpLabel + OpLoopMerge %21 %22 None + OpBranch %23 + %23 = OpLabel + OpSelectionMerge %24 None + OpBranchConditional %7 %24 %25 + %25 = OpLabel + OpBranch %21 + %24 = OpLabel + %26 = OpCopyObject %5 %7 + OpBranch %22 + %22 = OpLabel + OpBranchConditional %7 %20 %21 + %21 = OpLabel + %102 = OpPhi %5 %6 %25 %7 %22 + %27 = OpPhi %5 %26 %22 %6 %25 + OpBranchConditional %102 %101 %28 + %28 = OpLabel + OpBranch %101 + %101 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules2) { + // An id defined in a loop is used after the corresponding merge block. After + // the transformation, the id will not dominate its use anymore, regardless of + // the kind of instruction in which it is used. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %7 %13 %14 + %14 = OpLabel + OpReturn + %13 = OpLabel + %15 = OpCopyObject %5 %7 + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %9 %10 + %10 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpCopyObject %5 %15 + OpReturn + OpFunctionEnd + %18 = OpFunction %3 None %4 + %19 = OpLabel + OpBranch %20 + %20 = OpLabel + OpLoopMerge %21 %22 None + OpBranch %23 + %23 = OpLabel + OpSelectionMerge %24 None + OpBranchConditional %7 %24 %25 + %25 = OpLabel + OpReturn + %24 = OpLabel + %26 = OpCopyObject %5 %7 + OpBranch %22 + %22 = OpLabel + OpBranchConditional %7 %20 %21 + %21 = OpLabel + OpBranch %27 + %27 = OpLabel + %28 = OpPhi %5 %26 %21 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // In function %2, the definition of id %15 will not dominate its use in + // instruction %17 (inside block %16) after a new branch from return + // block %14 to merge block %10 is added. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + // In function %18, the definition of id %26 will not dominate its use in + // instruction %28 (inside block %27) after a new branch from return + // block %25 to merge block %21 is added. + ASSERT_FALSE(TransformationMergeFunctionReturns( + 2, 100, 101, 0, 0, + {{MakeReturnMergingInfo(10, 102, 0, {{}}), + MakeReturnMergingInfo(21, 103, 0, {{}})}}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { + // An id defined in a loop is used inside the loop. + // Changes to the predecessors of the merge block do not affect the validity + // of the uses of such id. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %7 %13 %14 + %14 = OpLabel + OpReturn + %13 = OpLabel + %15 = OpCopyObject %5 %7 + OpSelectionMerge %16 None + OpBranchConditional %7 %16 %17 + %17 = OpLabel + %18 = OpPhi %5 %15 %13 + %19 = OpCopyObject %5 %15 + OpBranch %16 + %16 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %9 %10 + %10 = OpLabel + OpBranch %20 + %20 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // In function %2, the definition of id %15 will still dominate its use in + // instructions %18 and %19 after the transformation is applied, because the + // fact that the id definition dominates the uses does not depend on it + // dominating the merge block. + auto transformation = TransformationMergeFunctionReturns( + 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %100 None + OpBranchConditional %6 %9 %100 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %7 %13 %14 + %14 = OpLabel + OpBranch %10 + %13 = OpLabel + %15 = OpCopyObject %5 %7 + OpSelectionMerge %16 None + OpBranchConditional %7 %16 %17 + %17 = OpLabel + %18 = OpPhi %5 %15 %13 + %19 = OpCopyObject %5 %15 + OpBranch %16 + %16 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %9 %10 + %10 = OpLabel + %102 = OpPhi %5 %6 %14 %7 %11 + OpBranchConditional %102 %101 %20 + %20 = OpLabel + OpBranch %101 + %101 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { + // An id defined in a loop, which contain 2 return statements, is used after + // the loop. We can only apply the transformation if the id dominates all of + // the return blocks. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + %13 = OpCopyObject %5 %7 + OpSelectionMerge %14 None + OpBranchConditional %7 %14 %15 + %15 = OpLabel + OpReturn + %14 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %7 %16 %17 + %17 = OpLabel + OpReturn + %16 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %9 %10 + %10 = OpLabel + OpBranch %18 + %18 = OpLabel + %19 = OpCopyObject %5 %13 + OpReturn + OpFunctionEnd + %20 = OpFunction %3 None %4 + %21 = OpLabel + OpBranch %22 + %22 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %7 %26 %27 + %27 = OpLabel + OpReturn + %26 = OpLabel + %28 = OpCopyObject %5 %7 + OpSelectionMerge %29 None + OpBranchConditional %7 %29 %30 + %30 = OpLabel + OpReturn + %29 = OpLabel + OpBranch %24 + %24 = OpLabel + OpBranchConditional %7 %22 %23 + %23 = OpLabel + OpBranch %31 + %31 = OpLabel + %32 = OpCopyObject %5 %28 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // In function %2, the definition of id %13 will still dominate its use in + // instruction %19 after the transformation is applied, because %13 dominates + // all of the return blocks. + auto transformation = TransformationMergeFunctionReturns( + 2, 100, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // In function %20, the definition of id %28 will not dominate its use in + // instruction %32 after the transformation is applied, because %28 dominates + // only one of the return blocks. + ASSERT_FALSE( + TransformationMergeFunctionReturns( + 20, 100, 101, 0, 0, {{MakeReturnMergingInfo(23, 102, 103, {{}})}}) + .IsApplicable(context.get(), transformation_context)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %100 None + OpBranchConditional %6 %9 %100 + %9 = OpLabel + OpLoopMerge %10 %11 None + OpBranch %12 + %12 = OpLabel + %13 = OpCopyObject %5 %7 + OpSelectionMerge %14 None + OpBranchConditional %7 %14 %15 + %15 = OpLabel + OpBranch %10 + %14 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %7 %16 %17 + %17 = OpLabel + OpBranch %10 + %16 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranchConditional %7 %9 %10 + %10 = OpLabel + %102 = OpPhi %5 %6 %15 %6 %17 %7 %11 + OpBranchConditional %102 %101 %18 + %18 = OpLabel + %19 = OpCopyObject %5 %13 + OpBranch %101 + %101 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %3 None %4 + %21 = OpLabel + OpBranch %22 + %22 = OpLabel + OpLoopMerge %23 %24 None + OpBranch %25 + %25 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %7 %26 %27 + %27 = OpLabel + OpReturn + %26 = OpLabel + %28 = OpCopyObject %5 %7 + OpSelectionMerge %29 None + OpBranchConditional %7 %29 %30 + %30 = OpLabel + OpReturn + %29 = OpLabel + OpBranch %24 + %24 = OpLabel + OpBranchConditional %7 %22 %23 + %23 = OpLabel + OpBranch %31 + %31 = OpLabel + %32 = OpCopyObject %5 %28 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + %10 = OpPhi %5 %6 %8 + OpSelectionMerge %11 None + OpBranchConditional %6 %12 %11 + %12 = OpLabel + OpReturn + %11 = OpLabel + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + auto transformation = + TransformationMergeFunctionReturns(2, 100, 101, 0, 0, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Ensure that all input operands of OpBranchConditional instructions have + // the right operand type. + context->module()->ForEachInst([](opt::Instruction* inst) { + if (inst->opcode() == SpvOpBranchConditional) { + ASSERT_EQ(inst->GetInOperand(0).type, SPV_OPERAND_TYPE_ID); + ASSERT_EQ(inst->GetInOperand(1).type, SPV_OPERAND_TYPE_ID); + ASSERT_EQ(inst->GetInOperand(2).type, SPV_OPERAND_TYPE_ID); + } + }); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %2 = OpFunction %3 None %4 + %8 = OpLabel + OpBranch %100 + %100 = OpLabel + OpLoopMerge %101 %100 None + OpBranchConditional %6 %9 %100 + %9 = OpLabel + %10 = OpPhi %5 %6 %100 + OpSelectionMerge %11 None + OpBranchConditional %6 %12 %11 + %12 = OpLabel + OpBranch %101 + %11 = OpLabel + OpBranch %101 + %101 = OpLabel + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools \ No newline at end of file diff --git a/third_party/spirv-tools/test/fuzz/transformation_move_block_down_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_move_block_down_test.cpp new file mode 100644 index 0000000..e8d2588 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_move_block_down_test.cpp @@ -0,0 +1,726 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_move_block_down.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationMoveBlockDownTest, NoMovePossible1) { + // Block 11 cannot be moved down as it dominates block 12. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %11 + %11 = OpLabel + OpStore %8 %9 + OpBranch %12 + %12 = OpLabel + OpStore %8 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationMoveBlockDown(11); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMoveBlockDownTest, NoMovePossible2) { + // Block 5 cannot be moved down as it is the entry block. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpStore %8 %10 + OpReturn + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationMoveBlockDown(5); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMoveBlockDownTest, NoMovePossible3) { + // Block 100 does not exist, so cannot be moved down. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %11 + %11 = OpLabel + OpStore %8 %9 + OpBranch %12 + %12 = OpLabel + OpStore %8 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationMoveBlockDown(100); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMoveBlockDownTest, NoMovePossible4) { + // Block 12 is the last block in its function, so cannot be moved down. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %10 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %11 + %11 = OpLabel + OpStore %8 %9 + OpBranch %12 + %12 = OpLabel + OpStore %8 %10 + OpReturn + OpFunctionEnd + %13 = OpFunction %2 None %3 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationMoveBlockDown(12); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMoveBlockDownTest, ManyMovesPossible) { + // The SPIR-V arising from this shader has lots of opportunities for moving + // blocks around. + // + // void main() { + // int x; + // int y; + // if (x < y) { + // x = 1; + // if (y == x) { + // x = 3; + // } else { + // x = 4; + // } + // } else { + // if (y < x) { + // x = 5; + // } else { + // x = 6; + // } + // } + // } + + std::string before_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %12 = OpTypeBool + %16 = OpConstant %6 1 + %22 = OpConstant %6 3 + %24 = OpConstant %6 4 + %31 = OpConstant %6 5 + %33 = OpConstant %6 6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %9 = OpLoad %6 %8 + %11 = OpLoad %6 %10 + %13 = OpSLessThan %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %25 + %14 = OpLabel + OpStore %8 %16 + %17 = OpLoad %6 %10 + %18 = OpLoad %6 %8 + %19 = OpIEqual %12 %17 %18 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %23 + %20 = OpLabel + OpStore %8 %22 + OpBranch %21 + %23 = OpLabel + OpStore %8 %24 + OpBranch %21 + %21 = OpLabel + OpBranch %15 + %25 = OpLabel + %26 = OpLoad %6 %10 + %27 = OpLoad %6 %8 + %28 = OpSLessThan %12 %26 %27 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %32 + %29 = OpLabel + OpStore %8 %31 + OpBranch %30 + %32 = OpLabel + OpStore %8 %33 + OpBranch %30 + %30 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, before_transformation, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // The block ids are: 5 14 20 23 21 25 29 32 30 15 + // We make a transformation to move each of them down, plus a transformation + // to move a non-block, 27, down. + auto move_down_5 = TransformationMoveBlockDown(5); + auto move_down_14 = TransformationMoveBlockDown(14); + auto move_down_20 = TransformationMoveBlockDown(20); + auto move_down_23 = TransformationMoveBlockDown(23); + auto move_down_21 = TransformationMoveBlockDown(21); + auto move_down_25 = TransformationMoveBlockDown(25); + auto move_down_29 = TransformationMoveBlockDown(29); + auto move_down_32 = TransformationMoveBlockDown(32); + auto move_down_30 = TransformationMoveBlockDown(30); + auto move_down_15 = TransformationMoveBlockDown(15); + auto move_down_27 = TransformationMoveBlockDown(27); + + // Dominance is as follows: + // 5 dominates everything else + // 14 dominates 20, 23, 21 + // 20 dominates nothing + // 23 dominates nothing + // 21 dominates nothing + // 25 dominates 29, 32, 30 + // 29 dominates nothing + // 32 dominates nothing + // 30 dominates nothing + // 15 dominates nothing + + // Current ordering: 5 14 20 23 21 25 29 32 30 15 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + // Let's bubble 20 all the way down. + + ApplyAndCheckFreshIds(move_down_20, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 23 20 21 25 29 32 30 15 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_20, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 23 21 20 25 29 32 30 15 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_20, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 23 21 25 20 29 32 30 15 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_20, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 23 21 25 29 20 32 30 15 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_20, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 23 21 25 29 32 20 30 15 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_20, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 23 21 25 29 32 30 20 15 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_20.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_15.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_20, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_bubbling_20_down = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %12 = OpTypeBool + %16 = OpConstant %6 1 + %22 = OpConstant %6 3 + %24 = OpConstant %6 4 + %31 = OpConstant %6 5 + %33 = OpConstant %6 6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %9 = OpLoad %6 %8 + %11 = OpLoad %6 %10 + %13 = OpSLessThan %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %25 + %14 = OpLabel + OpStore %8 %16 + %17 = OpLoad %6 %10 + %18 = OpLoad %6 %8 + %19 = OpIEqual %12 %17 %18 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %23 + %23 = OpLabel + OpStore %8 %24 + OpBranch %21 + %21 = OpLabel + OpBranch %15 + %25 = OpLabel + %26 = OpLoad %6 %10 + %27 = OpLoad %6 %8 + %28 = OpSLessThan %12 %26 %27 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %32 + %29 = OpLabel + OpStore %8 %31 + OpBranch %30 + %32 = OpLabel + OpStore %8 %33 + OpBranch %30 + %30 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + %20 = OpLabel + OpStore %8 %22 + OpBranch %21 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_bubbling_20_down, context.get())); + + // Current ordering: 5 14 23 21 25 29 32 30 15 20 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_23, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 21 23 25 29 32 30 15 20 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_23, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 21 25 23 29 32 30 15 20 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_21, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Current ordering: 5 14 25 21 23 29 32 30 15 20 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(move_down_14, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_more_shuffling = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %17 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %12 = OpTypeBool + %16 = OpConstant %6 1 + %22 = OpConstant %6 3 + %24 = OpConstant %6 4 + %31 = OpConstant %6 5 + %33 = OpConstant %6 6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %9 = OpLoad %6 %8 + %11 = OpLoad %6 %10 + %13 = OpSLessThan %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %25 + %25 = OpLabel + %26 = OpLoad %6 %10 + %27 = OpLoad %6 %8 + %28 = OpSLessThan %12 %26 %27 + OpSelectionMerge %30 None + OpBranchConditional %28 %29 %32 + %14 = OpLabel + OpStore %8 %16 + %17 = OpLoad %6 %10 + %18 = OpLoad %6 %8 + %19 = OpIEqual %12 %17 %18 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %23 + %21 = OpLabel + OpBranch %15 + %23 = OpLabel + OpStore %8 %24 + OpBranch %21 + %29 = OpLabel + OpStore %8 %31 + OpBranch %30 + %32 = OpLabel + OpStore %8 %33 + OpBranch %30 + %30 = OpLabel + OpBranch %15 + %15 = OpLabel + OpReturn + %20 = OpLabel + OpStore %8 %22 + OpBranch %21 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_more_shuffling, context.get())); + + // Final ordering: 5 25 14 21 23 29 32 30 15 20 + ASSERT_FALSE(move_down_5.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_25.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_14.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_21.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_23.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_29.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_32.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_30.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(move_down_15.IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + move_down_20.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationMoveBlockDownTest, DoNotMoveUnreachable) { + // Block 6 is unreachable, so cannot be moved down. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %10 = OpTypeInt 32 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + %6 = OpLabel + %7 = OpUndef %10 + OpBranch %8 + %8 = OpLabel + %9 = OpCopyObject %10 %7 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = TransformationMoveBlockDown(6); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_move_instruction_down_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_move_instruction_down_test.cpp new file mode 100644 index 0000000..45dde7d --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_move_instruction_down_test.cpp @@ -0,0 +1,720 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_move_instruction_down.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationMoveInstructionDownTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpTypeBool + %17 = OpConstantFalse %16 + %20 = OpUndef %6 + %13 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %13 Function + %10 = OpIAdd %6 %9 %9 + %11 = OpISub %6 %9 %10 + OpStore %12 %10 + %14 = OpLoad %6 %12 + %15 = OpIMul %6 %9 %14 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %19 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + %42 = OpFunctionCall %2 %40 + %22 = OpIAdd %6 %15 %15 + %21 = OpIAdd %6 %15 %15 + OpReturn + OpFunctionEnd + %40 = OpFunction %2 None %3 + %41 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Instruction descriptor is invalid. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(30, SpvOpNop, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Opcode is not supported. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(5, SpvOpLabel, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(12, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(42, SpvOpFunctionCall, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Can't move the last instruction in the block. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(15, SpvOpBranchConditional, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Can't move the instruction if the next instruction is the last one in the + // block. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(21, SpvOpIAdd, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Can't insert instruction's opcode after its successor. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(15, SpvOpIMul, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Instruction's successor depends on the instruction. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(10, SpvOpIAdd, 0)) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(11, SpvOpISub, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(22, SpvOpIAdd, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpConstant %6 0 + %16 = OpTypeBool + %17 = OpConstantFalse %16 + %20 = OpUndef %6 + %13 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %13 Function + %10 = OpIAdd %6 %9 %9 + OpStore %12 %10 + %11 = OpISub %6 %9 %10 + %14 = OpLoad %6 %12 + %15 = OpIMul %6 %9 %14 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %19 + %18 = OpLabel + OpBranch %19 + %19 = OpLabel + %42 = OpFunctionCall %2 %40 + %21 = OpIAdd %6 %15 %15 + %22 = OpIAdd %6 %15 %15 + OpReturn + OpFunctionEnd + %40 = OpFunction %2 None %3 + %41 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMoveInstructionDownTest, HandlesUnsupportedInstructions) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 16 1 1 + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 2 + %20 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %21 = OpVariable %20 Function %7 + + ; can swap simple and not supported instructions + %8 = OpCopyObject %6 %7 + %9 = OpFunctionCall %2 %12 + + ; cannot swap memory and not supported instruction + %22 = OpLoad %6 %21 + %23 = OpFunctionCall %2 %12 + + ; cannot swap barrier and not supported instruction + OpMemoryBarrier %7 %7 + %24 = OpFunctionCall %2 %12 + + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Swap memory instruction with an unsupported one. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(22, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Swap memory barrier with an unsupported one. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(23, SpvOpMemoryBarrier, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Swap simple instruction with an unsupported one. + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(8, SpvOpCopyObject, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 16 1 1 + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 2 + %20 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %21 = OpVariable %20 Function %7 + + ; can swap simple and not supported instructions + %9 = OpFunctionCall %2 %12 + %8 = OpCopyObject %6 %7 + + ; cannot swap memory and not supported instruction + %22 = OpLoad %6 %21 + %23 = OpFunctionCall %2 %12 + + ; cannot swap barrier and not supported instruction + OpMemoryBarrier %7 %7 + %24 = OpFunctionCall %2 %12 + + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMoveInstructionDownTest, HandlesBarrierInstructions) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 16 1 1 + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 2 + %20 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %21 = OpVariable %20 Function %7 + + ; cannot swap two barrier instructions + OpMemoryBarrier %7 %7 + OpMemoryBarrier %7 %7 + + ; cannot swap barrier and memory instructions + OpMemoryBarrier %7 %7 + %22 = OpLoad %6 %21 + OpMemoryBarrier %7 %7 + + ; can swap barrier and simple instructions + %23 = OpCopyObject %6 %7 + OpMemoryBarrier %7 %7 + + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Swap two barrier instructions. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(21, SpvOpMemoryBarrier, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Swap barrier and memory instructions. + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(21, SpvOpMemoryBarrier, 2)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationMoveInstructionDown( + MakeInstructionDescriptor(22, SpvOpLoad, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Swap barrier and simple instructions. + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(23, SpvOpCopyObject, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(22, SpvOpMemoryBarrier, 1)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + ASSERT_TRUE(IsEqual(env, shader, context.get())); +} + +TEST(TransformationMoveInstructionDownTest, HandlesSimpleInstructions) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 16 1 1 + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 2 + %20 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %21 = OpVariable %20 Function %7 + + ; can swap simple and barrier instructions + %40 = OpCopyObject %6 %7 + OpMemoryBarrier %7 %7 + + ; can swap simple and memory instructions + %41 = OpCopyObject %6 %7 + %22 = OpLoad %6 %21 + + ; can swap two simple instructions + %23 = OpCopyObject %6 %7 + %42 = OpCopyObject %6 %7 + + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Swap simple and barrier instructions. + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(40, SpvOpCopyObject, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(21, SpvOpMemoryBarrier, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + // Swap simple and memory instructions. + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(41, SpvOpCopyObject, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(22, SpvOpLoad, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + // Swap two simple instructions. + { + TransformationMoveInstructionDown transformation( + MakeInstructionDescriptor(23, SpvOpCopyObject, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 16 1 1 + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 2 + %20 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %21 = OpVariable %20 Function %7 + + ; can swap simple and barrier instructions + %40 = OpCopyObject %6 %7 + OpMemoryBarrier %7 %7 + + ; can swap simple and memory instructions + %41 = OpCopyObject %6 %7 + %22 = OpLoad %6 %21 + + ; can swap two simple instructions + %42 = OpCopyObject %6 %7 + %23 = OpCopyObject %6 %7 + + OpReturn + OpFunctionEnd + %12 = OpFunction %2 None %3 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMoveInstructionDownTest, HandlesMemoryInstructions) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %4 "main" + OpExecutionMode %4 LocalSize 16 1 1 + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpConstant %6 2 + %20 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %21 = OpVariable %20 Function %7 + %22 = OpVariable %20 Function %7 + + ; swap R and R instructions + %23 = OpLoad %6 %21 + %24 = OpLoad %6 %22 + + ; swap R and RW instructions + + ; can't swap + %25 = OpLoad %6 %21 + OpCopyMemory %21 %22 + + ; can swap + %26 = OpLoad %6 %21 + OpCopyMemory %22 %21 + + %27 = OpLoad %6 %22 + OpCopyMemory %21 %22 + + %28 = OpLoad %6 %22 + OpCopyMemory %22 %21 + + ; swap R and W instructions + + ; can't swap + %29 = OpLoad %6 %21 + OpStore %21 %7 + + ; can swap + %30 = OpLoad %6 %22 + OpStore %21 %7 + + %31 = OpLoad %6 %21 + OpStore %22 %7 + + %32 = OpLoad %6 %22 + OpStore %22 %7 + + ; swap RW and RW instructions + + ; can't swap + OpCopyMemory %21 %21 + OpCopyMemory %21 %21 + + OpCopyMemory %21 %22 + OpCopyMemory %21 %21 + + OpCopyMemory %21 %21 + OpCopyMemory %21 %22 + + ; can swap + OpCopyMemory %22 %21 + OpCopyMemory %21 %22 + + OpCopyMemory %22 %21 + OpCopyMemory %22 %21 + + OpCopyMemory %21 %22 + OpCopyMemory %21 %22 + + ; swap RW and W instructions + + ; can't swap + OpCopyMemory %21 %21 + OpStore %21 %7 + + OpStore %21 %7 + OpCopyMemory %21 %21 + + ; can swap + OpCopyMemory %22 %21 + OpStore %21 %7 + + OpCopyMemory %21 %22 + OpStore %21 %7 + + OpCopyMemory %21 %21 + OpStore %22 %7 + + ; swap W and W instructions + + ; can't swap + OpStore %21 %7 + OpStore %21 %7 + + ; can swap + OpStore %22 %7 + OpStore %21 %7 + + OpStore %22 %7 + OpStore %22 %7 + + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 22); + + // Invalid swaps. + + protobufs::InstructionDescriptor invalid_swaps[] = { + // R and RW + MakeInstructionDescriptor(25, SpvOpLoad, 0), + + // R and W + MakeInstructionDescriptor(29, SpvOpLoad, 0), + + // RW and RW + MakeInstructionDescriptor(32, SpvOpCopyMemory, 0), + MakeInstructionDescriptor(32, SpvOpCopyMemory, 2), + MakeInstructionDescriptor(32, SpvOpCopyMemory, 4), + + // RW and W + MakeInstructionDescriptor(32, SpvOpCopyMemory, 12), + MakeInstructionDescriptor(32, SpvOpStore, 1), + + // W and W + MakeInstructionDescriptor(32, SpvOpStore, 6), + }; + + for (const auto& descriptor : invalid_swaps) { + ASSERT_FALSE(TransformationMoveInstructionDown(descriptor) + .IsApplicable(context.get(), transformation_context)); + } + + // Valid swaps. + protobufs::InstructionDescriptor valid_swaps[] = { + // R and R + MakeInstructionDescriptor(23, SpvOpLoad, 0), + MakeInstructionDescriptor(24, SpvOpLoad, 0), + + // R and RW + MakeInstructionDescriptor(26, SpvOpLoad, 0), + MakeInstructionDescriptor(25, SpvOpCopyMemory, 1), + + MakeInstructionDescriptor(27, SpvOpLoad, 0), + MakeInstructionDescriptor(26, SpvOpCopyMemory, 1), + + MakeInstructionDescriptor(28, SpvOpLoad, 0), + MakeInstructionDescriptor(27, SpvOpCopyMemory, 1), + + // R and W + MakeInstructionDescriptor(30, SpvOpLoad, 0), + MakeInstructionDescriptor(29, SpvOpStore, 1), + + MakeInstructionDescriptor(31, SpvOpLoad, 0), + MakeInstructionDescriptor(30, SpvOpStore, 1), + + MakeInstructionDescriptor(32, SpvOpLoad, 0), + MakeInstructionDescriptor(31, SpvOpStore, 1), + + // RW and RW + MakeInstructionDescriptor(32, SpvOpCopyMemory, 6), + MakeInstructionDescriptor(32, SpvOpCopyMemory, 6), + + MakeInstructionDescriptor(32, SpvOpCopyMemory, 8), + MakeInstructionDescriptor(32, SpvOpCopyMemory, 8), + + MakeInstructionDescriptor(32, SpvOpCopyMemory, 10), + MakeInstructionDescriptor(32, SpvOpCopyMemory, 10), + + // RW and W + MakeInstructionDescriptor(32, SpvOpCopyMemory, 14), + MakeInstructionDescriptor(32, SpvOpStore, 3), + + MakeInstructionDescriptor(32, SpvOpCopyMemory, 15), + MakeInstructionDescriptor(32, SpvOpStore, 4), + + MakeInstructionDescriptor(32, SpvOpCopyMemory, 16), + MakeInstructionDescriptor(32, SpvOpStore, 5), + + // W and W + MakeInstructionDescriptor(32, SpvOpStore, 8), + MakeInstructionDescriptor(32, SpvOpStore, 8), + + MakeInstructionDescriptor(32, SpvOpStore, 10), + MakeInstructionDescriptor(32, SpvOpStore, 10), + }; + + for (const auto& descriptor : valid_swaps) { + TransformationMoveInstructionDown transformation(descriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + ASSERT_TRUE(IsEqual(env, shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_mutate_pointer_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_mutate_pointer_test.cpp new file mode 100644 index 0000000..ae42310 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_mutate_pointer_test.cpp @@ -0,0 +1,322 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_mutate_pointer.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationMutatePointerTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %34 = OpConstant %7 0 + %36 = OpConstant %6 0 + %14 = OpTypeVector %7 3 + %35 = OpConstantComposite %14 %34 %34 %34 + %15 = OpTypeMatrix %14 2 + %8 = OpConstant %6 5 + %9 = OpTypeArray %7 %8 + %37 = OpConstantComposite %9 %34 %34 %34 %34 %34 + %11 = OpTypeStruct + %38 = OpConstantComposite %11 + %39 = OpConstantComposite %15 %35 %35 + %31 = OpTypePointer Function %14 + %10 = OpTypeStruct %7 %6 %9 %11 %15 %14 + %40 = OpConstantComposite %10 %34 %36 %37 %38 %39 %35 + %13 = OpTypePointer Function %10 + %16 = OpTypePointer Private %10 + %17 = OpTypePointer Workgroup %10 + %18 = OpTypeStruct %16 + %19 = OpTypePointer Private %18 + %20 = OpVariable %16 Private + %21 = OpVariable %17 Workgroup + %22 = OpVariable %19 Private + %23 = OpTypePointer Output %6 + %24 = OpVariable %23 Output + %27 = OpTypeFunction %2 %13 + %32 = OpUndef %16 + %33 = OpConstantNull %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %28 = OpFunction %2 None %27 + %29 = OpFunctionParameter %13 + %30 = OpLabel + %25 = OpVariable %13 Function + %26 = OpAccessChain %31 %25 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(35); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(39); + + const auto insert_before = MakeInstructionDescriptor(26, SpvOpReturn, 0); + + // 20 is not a fresh id. + ASSERT_FALSE(TransformationMutatePointer(20, 20, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |insert_before| instruction descriptor is invalid. + ASSERT_FALSE(TransformationMutatePointer( + 20, 70, MakeInstructionDescriptor(26, SpvOpStore, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Can't insert OpLoad before OpVariable. + ASSERT_FALSE(TransformationMutatePointer( + 20, 70, MakeInstructionDescriptor(26, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + + // |pointer_id| doesn't exist in the module. + ASSERT_FALSE(TransformationMutatePointer(70, 70, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |pointer_id| doesn't have a type id. + ASSERT_FALSE(TransformationMutatePointer(11, 70, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |pointer_id| is a result id of OpUndef. + ASSERT_FALSE(TransformationMutatePointer(32, 70, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |pointer_id| is a result id of OpConstantNull. + ASSERT_FALSE(TransformationMutatePointer(33, 70, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |pointer_id| is not a pointer instruction. + ASSERT_FALSE(TransformationMutatePointer(8, 70, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |pointer_id| has invalid storage class + ASSERT_FALSE(TransformationMutatePointer(24, 70, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |pointer_id|'s pointee contains non-scalar and non-composite constituents. + ASSERT_FALSE(TransformationMutatePointer(22, 70, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // There is no irrelevant zero constant to insert into the |pointer_id|. + ASSERT_FALSE(TransformationMutatePointer(20, 70, insert_before) + .IsApplicable(context.get(), transformation_context)); + + // |pointer_id| is not available before |insert_before|. + ASSERT_FALSE(TransformationMutatePointer( + 26, 70, MakeInstructionDescriptor(26, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(40); + + uint32_t fresh_id = 70; + uint32_t pointer_ids[] = { + 20, // Mutate Private variable. + 21, // Mutate Workgroup variable. + 25, // Mutate Function variable. + 29, // Mutate function parameter. + 26, // Mutate OpAccessChain. + }; + + for (auto pointer_id : pointer_ids) { + TransformationMutatePointer transformation(pointer_id, fresh_id++, + insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %34 = OpConstant %7 0 + %36 = OpConstant %6 0 + %14 = OpTypeVector %7 3 + %35 = OpConstantComposite %14 %34 %34 %34 + %15 = OpTypeMatrix %14 2 + %8 = OpConstant %6 5 + %9 = OpTypeArray %7 %8 + %37 = OpConstantComposite %9 %34 %34 %34 %34 %34 + %11 = OpTypeStruct + %38 = OpConstantComposite %11 + %39 = OpConstantComposite %15 %35 %35 + %31 = OpTypePointer Function %14 + %10 = OpTypeStruct %7 %6 %9 %11 %15 %14 + %40 = OpConstantComposite %10 %34 %36 %37 %38 %39 %35 + %13 = OpTypePointer Function %10 + %16 = OpTypePointer Private %10 + %17 = OpTypePointer Workgroup %10 + %18 = OpTypeStruct %16 + %19 = OpTypePointer Private %18 + %20 = OpVariable %16 Private + %21 = OpVariable %17 Workgroup + %22 = OpVariable %19 Private + %23 = OpTypePointer Output %6 + %24 = OpVariable %23 Output + %27 = OpTypeFunction %2 %13 + %32 = OpUndef %16 + %33 = OpConstantNull %16 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %28 = OpFunction %2 None %27 + %29 = OpFunctionParameter %13 + %30 = OpLabel + %25 = OpVariable %13 Function + %26 = OpAccessChain %31 %25 %8 + + ; modified Private variable + %70 = OpLoad %10 %20 + OpStore %20 %40 + OpStore %20 %70 + + ; modified Workgroup variable + %71 = OpLoad %10 %21 + OpStore %21 %40 + OpStore %21 %71 + + ; modified Function variable + %72 = OpLoad %10 %25 + OpStore %25 %40 + OpStore %25 %72 + + ; modified function parameter + %73 = OpLoad %10 %29 + OpStore %29 %40 + OpStore %29 %73 + + ; modified OpAccessChain + %74 = OpLoad %14 %26 + OpStore %26 %35 + OpStore %26 %74 + + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationMutatePointerTest, HandlesUnreachableBlocks) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + %8 = OpTypePointer Function %6 + %11 = OpTypePointer Private %6 + %12 = OpVariable %11 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpReturn + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(7); + + ASSERT_FALSE( + context->GetDominatorAnalysis(context->GetFunction(4))->IsReachable(10)); + + const auto insert_before = MakeInstructionDescriptor(10, SpvOpReturn, 0); + + // Can mutate a global variable in an unreachable block. + TransformationMutatePointer transformation(12, 50, insert_before); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + %8 = OpTypePointer Function %6 + %11 = OpTypePointer Private %6 + %12 = OpVariable %11 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpReturn + %10 = OpLabel + %50 = OpLoad %6 %12 + OpStore %12 %7 + OpStore %12 %50 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_outline_function_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_outline_function_test.cpp new file mode 100644 index 0000000..6c0bff4 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_outline_function_test.cpp @@ -0,0 +1,3304 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_outline_function.h" + +#include "gtest/gtest.h" +#include "source/fuzz/counter_overflow_id_source.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationOutlineFunctionTest, TrivialOutline) { + // This tests outlining of a single, empty basic block. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %103 = OpFunctionCall %2 %101 + OpReturn + OpFunctionEnd + %101 = OpFunction %2 None %3 + %102 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfRegionStartsWithOpVariable) { + // This checks that we do not outline the first block of a function if it + // contains OpVariable. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %7 = OpTypeBool + %8 = OpTypePointer Function %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpVariable %8 Function + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(5, 5, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, OutlineInterestingControlFlowNoState) { + // This tests outlining of some non-trivial control flow, but such that the + // basic blocks in the control flow do not actually do anything. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %12 %11 None + OpBranch %10 + %10 = OpLabel + OpBranchConditional %21 %11 %12 + %11 = OpLabel + OpBranch %9 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 13, /* not relevant */ + 200, 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %103 = OpFunctionCall %2 %101 + OpReturn + OpFunctionEnd + %101 = OpFunction %2 None %3 + %102 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpLoopMerge %12 %11 None + OpBranch %10 + %10 = OpLabel + OpBranchConditional %21 %11 %12 + %11 = OpLabel + OpBranch %9 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesUnusedIds) { + // This tests outlining of a single basic block that does some computation, + // but that does not use nor generate ids required outside of the outlined + // region. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %9 = OpIAdd %20 %7 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 6, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %103 = OpFunctionCall %2 %101 + OpReturn + OpFunctionEnd + %101 = OpFunction %2 None %3 + %102 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %9 = OpIAdd %20 %7 %8 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatGeneratesSingleUsedId) { + // This tests outlining of a block that generates an id that is used in a + // later block. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %9 = OpIAdd %20 %7 %8 + OpBranch %10 + %10 = OpLabel + %11 = OpCopyObject %20 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 6, 99, 100, 101, 102, 103, + 105, {}, {{9, 104}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %99 = OpTypeStruct %20 + %100 = OpTypeFunction %99 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %103 = OpFunctionCall %99 %101 + %9 = OpCompositeExtract %20 %103 0 + OpBranch %10 + %10 = OpLabel + %11 = OpCopyObject %20 %9 + OpReturn + OpFunctionEnd + %101 = OpFunction %99 None %100 + %102 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %104 = OpIAdd %20 %7 %8 + %105 = OpCompositeConstruct %99 %104 + OpReturnValue %105 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineDiamondThatGeneratesSeveralIds) { + // This tests outlining of several blocks that generate a number of ids that + // are used in later blocks. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %22 = OpTypeBool + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %7 = OpCopyObject %20 %21 + %8 = OpCopyObject %20 %21 + %9 = OpSLessThan %22 %7 %8 + OpSelectionMerge %12 None + OpBranchConditional %9 %10 %11 + %10 = OpLabel + %13 = OpIAdd %20 %7 %8 + OpBranch %12 + %11 = OpLabel + %14 = OpIAdd %20 %7 %7 + OpBranch %12 + %12 = OpLabel + %15 = OpPhi %20 %13 %10 %14 %11 + OpBranch %80 + %80 = OpLabel + OpBranch %16 + %16 = OpLabel + %17 = OpCopyObject %20 %15 + %18 = OpCopyObject %22 %9 + %19 = OpIAdd %20 %7 %8 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + 6, 80, 100, 101, 102, 103, 104, 105, {}, + {{15, 106}, {9, 107}, {7, 108}, {8, 109}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %22 = OpTypeBool + %3 = OpTypeFunction %2 + %100 = OpTypeStruct %20 %20 %22 %20 + %101 = OpTypeFunction %100 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %104 = OpFunctionCall %100 %102 + %7 = OpCompositeExtract %20 %104 0 + %8 = OpCompositeExtract %20 %104 1 + %9 = OpCompositeExtract %22 %104 2 + %15 = OpCompositeExtract %20 %104 3 + OpBranch %16 + %16 = OpLabel + %17 = OpCopyObject %20 %15 + %18 = OpCopyObject %22 %9 + %19 = OpIAdd %20 %7 %8 + OpReturn + OpFunctionEnd + %102 = OpFunction %100 None %101 + %103 = OpLabel + %108 = OpCopyObject %20 %21 + %109 = OpCopyObject %20 %21 + %107 = OpSLessThan %22 %108 %109 + OpSelectionMerge %12 None + OpBranchConditional %107 %10 %11 + %10 = OpLabel + %13 = OpIAdd %20 %108 %109 + OpBranch %12 + %11 = OpLabel + %14 = OpIAdd %20 %108 %108 + OpBranch %12 + %12 = OpLabel + %106 = OpPhi %20 %13 %10 %14 %11 + OpBranch %80 + %80 = OpLabel + %105 = OpCompositeConstruct %100 %108 %109 %107 %106 + OpReturnValue %105 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesASingleId) { + // This tests outlining of a block that uses an id defined earlier. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %7 = OpCopyObject %20 %21 + OpBranch %6 + %6 = OpLabel + %8 = OpCopyObject %20 %7 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104, + 105, {{7, 106}}, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %101 = OpTypeFunction %2 %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %7 = OpCopyObject %20 %21 + OpBranch %6 + %6 = OpLabel + %104 = OpFunctionCall %2 %102 %7 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + %102 = OpFunction %2 None %101 + %106 = OpFunctionParameter %20 + %103 = OpLabel + %8 = OpCopyObject %20 %106 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAVariable) { + // This tests outlining of a block that uses a variable. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %12 = OpTypePointer Function %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpVariable %12 Function + OpBranch %6 + %6 = OpLabel + %8 = OpLoad %20 %13 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 6, 100, 101, 102, 103, 104, + 105, {{13, 106}}, {}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeInt 32 1 + %21 = OpConstant %20 5 + %3 = OpTypeFunction %2 + %12 = OpTypePointer Function %20 + %101 = OpTypeFunction %2 %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %13 = OpVariable %12 Function + OpBranch %6 + %6 = OpLabel + %104 = OpFunctionCall %2 %102 %13 + OpBranch %10 + %10 = OpLabel + OpReturn + OpFunctionEnd + %102 = OpFunction %2 None %101 + %106 = OpFunctionParameter %12 + %103 = OpLabel + %8 = OpLoad %20 %106 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineCodeThatUsesAParameter) { + // This tests outlining of a block that uses a function parameter. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "x" + OpName %18 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %13 = OpConstant %6 1 + %17 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %7 Function + OpStore %18 %17 + %19 = OpFunctionCall %6 %10 %18 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %14 = OpIAdd %6 %12 %13 + OpReturnValue %14 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(11, 11, 100, 101, 102, 103, 104, + 105, {{9, 106}}, {{14, 107}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "x" + OpName %18 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 + %13 = OpConstant %6 1 + %17 = OpConstant %6 3 + %100 = OpTypeStruct %6 + %101 = OpTypeFunction %100 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpVariable %7 Function + OpStore %18 %17 + %19 = OpFunctionCall %6 %10 %18 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %104 = OpFunctionCall %100 %102 %9 + %14 = OpCompositeExtract %6 %104 0 + OpReturnValue %14 + OpFunctionEnd + %102 = OpFunction %100 None %101 + %106 = OpFunctionParameter %7 + %103 = OpLabel + %12 = OpLoad %6 %106 + %107 = OpIAdd %6 %12 %13 + %105 = OpCompositeConstruct %100 %107 + OpReturnValue %105 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfLoopMergeIsOutsideRegion) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %7 %8 None + OpBranch %8 + %8 = OpLabel + OpBranchConditional %10 %6 %7 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesReturn) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpReturn + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineIfRegionInvolvesKill) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpKill + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfRegionInvolvesUnreachable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpBranch %7 + %7 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %21 %8 %9 + %8 = OpLabel + OpBranch %10 + %9 = OpLabel + OpUnreachable + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpBranch %12 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 11, /* not relevant */ 200, + 100, 101, 102, 103, + /* not relevant */ 201, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfSelectionMergeIsOutsideRegion) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpSelectionMerge %7 None + OpBranchConditional %10 %8 %7 + %8 = OpLabel + OpBranch %7 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 8, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineIfLoopHeadIsOutsideRegion) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %8 %11 None + OpBranch %7 + %7 = OpLabel + OpBranchConditional %10 %11 %8 + %11 = OpLabel + OpBranch %6 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(7, 8, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineIfLoopContinueIsOutsideRegion) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + OpLoopMerge %7 %8 None + OpBranch %7 + %8 = OpLabel + OpBranch %6 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineWithLoopCarriedPhiDependence) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %9 = OpTypeBool + %10 = OpConstantTrue %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + %6 = OpLabel + %12 = OpPhi %9 %10 %5 %13 %8 + OpLoopMerge %7 %8 None + OpBranch %8 + %8 = OpLabel + %13 = OpCopyObject %9 %10 + OpBranchConditional %10 %6 %7 + %7 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(6, 7, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineSelectionHeaderNotInRegion) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %10 None + OpBranchConditional %7 %8 %8 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpBranch %10 + %10 = OpLabel + OpBranch %11 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation(8, 11, 100, 101, 102, 103, 104, + 105, {}, {}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnVoid) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %22 = OpCopyObject %20 %21 + OpBranch %54 + %54 = OpLabel + OpBranch %57 + %57 = OpLabel + %23 = OpCopyObject %20 %22 + OpBranch %58 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 54, + /*exit_block*/ 58, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{22, 206}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %201 = OpTypeFunction %2 %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %22 = OpCopyObject %20 %21 + OpBranch %54 + %54 = OpLabel + %204 = OpFunctionCall %2 %202 %22 + OpReturn + OpFunctionEnd + %202 = OpFunction %2 None %201 + %206 = OpFunctionParameter %20 + %203 = OpLabel + OpBranch %57 + %57 = OpLabel + %23 = OpCopyObject %20 %206 + OpBranch %58 + %58 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineRegionEndingWithReturnValue) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %30 = OpTypeFunction %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpFunctionCall %20 %100 + OpReturn + OpFunctionEnd + %100 = OpFunction %20 None %30 + %8 = OpLabel + %31 = OpCopyObject %20 %21 + OpBranch %9 + %9 = OpLabel + %32 = OpCopyObject %20 %31 + OpBranch %10 + %10 = OpLabel + OpReturnValue %32 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 9, + /*exit_block*/ 10, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{31, 206}}, + /*output_id_to_fresh_id*/ {{32, 207}}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeInt 32 0 + %21 = OpConstant %20 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %30 = OpTypeFunction %20 + %200 = OpTypeStruct %20 + %201 = OpTypeFunction %200 %20 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpFunctionCall %20 %100 + OpReturn + OpFunctionEnd + %100 = OpFunction %20 None %30 + %8 = OpLabel + %31 = OpCopyObject %20 %21 + OpBranch %9 + %9 = OpLabel + %204 = OpFunctionCall %200 %202 %31 + %32 = OpCompositeExtract %20 %204 0 + OpReturnValue %32 + OpFunctionEnd + %202 = OpFunction %200 None %201 + %206 = OpFunctionParameter %20 + %203 = OpLabel + %207 = OpCopyObject %20 %206 + OpBranch %10 + %10 = OpLabel + %205 = OpCompositeConstruct %200 %207 + OpReturnValue %205 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, + OutlineRegionEndingWithConditionalBranch) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %54 + %54 = OpLabel + %6 = OpCopyObject %20 %21 + OpSelectionMerge %8 None + OpBranchConditional %6 %7 %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 54, + /*exit_block*/ 54, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{}}, + /*output_id_to_fresh_id*/ {{6, 206}}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %200 = OpTypeStruct %20 + %201 = OpTypeFunction %200 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %54 + %54 = OpLabel + %204 = OpFunctionCall %200 %202 + %6 = OpCompositeExtract %20 %204 0 + OpSelectionMerge %8 None + OpBranchConditional %6 %7 %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + %202 = OpFunction %200 None %201 + %203 = OpLabel + %206 = OpCopyObject %20 %21 + %205 = OpCompositeConstruct %200 %206 + OpReturnValue %205 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, + OutlineRegionEndingWithConditionalBranch2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpCopyObject %20 %21 + OpBranch %54 + %54 = OpLabel + OpSelectionMerge %8 None + OpBranchConditional %6 %7 %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 54, + /*exit_block*/ 54, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %20 = OpTypeBool + %21 = OpConstantTrue %20 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %6 = OpCopyObject %20 %21 + OpBranch %54 + %54 = OpLabel + %204 = OpFunctionCall %2 %202 + OpSelectionMerge %8 None + OpBranchConditional %6 %7 %8 + %7 = OpLabel + OpBranch %8 + %8 = OpLabel + OpReturn + OpFunctionEnd + %202 = OpFunction %2 None %3 + %203 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatStartsWithOpPhi) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %21 + %21 = OpLabel + %22 = OpPhi %6 %7 %5 + %23 = OpCopyObject %6 %22 + OpBranch %24 + %24 = OpLabel + %25 = OpCopyObject %6 %23 + %26 = OpCopyObject %6 %22 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 21, + /*exit_block*/ 21, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {{22, 207}}, + /*output_id_to_fresh_id*/ {{23, 208}}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineRegionThatStartsWithLoopHeader) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + OpBranchConditional %7 %22 %23 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 21, + /*exit_block*/ 24, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineRegionThatEndsWithLoopMerge) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %21 + %21 = OpLabel + OpLoopMerge %22 %23 None + OpBranch %24 + %24 = OpLabel + OpBranchConditional %7 %22 %23 + %23 = OpLabel + OpBranch %21 + %22 = OpLabel + OpBranch %25 + %25 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 5, + /*exit_block*/ 22, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, DoNotOutlineRegionThatUsesAccessChain) { + // An access chain result is a pointer, but it cannot be passed as a function + // parameter, as it is not a memory object. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Function %7 + %9 = OpTypePointer Function %6 + %18 = OpTypeInt 32 0 + %19 = OpConstant %18 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %8 Function + OpBranch %11 + %11 = OpLabel + %12 = OpAccessChain %9 %10 %19 + OpBranch %13 + %13 = OpLabel + %14 = OpLoad %6 %12 + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 13, + /*exit_block*/ 15, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {{12, 207}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineRegionThatUsesCopiedObject) { + // Copying a variable leads to a pointer, but one that cannot be passed as a + // function parameter, as it is not a memory object. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %8 = OpTypePointer Function %7 + %9 = OpTypePointer Function %6 + %18 = OpTypeInt 32 0 + %19 = OpConstant %18 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %10 = OpVariable %8 Function + OpBranch %11 + %11 = OpLabel + %20 = OpCopyObject %8 %10 + OpBranch %13 + %13 = OpLabel + %12 = OpAccessChain %9 %20 %19 + %14 = OpLoad %6 %12 + OpBranch %15 + %15 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 13, + /*exit_block*/ 15, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {{20, 207}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, + DoOutlineRegionThatUsesPointerParameter) { + // The region being outlined reads from a function parameter of pointer type. + // This is OK: the function parameter can itself be passed on as a function + // parameter. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %15 = OpVariable %7 Function + %16 = OpVariable %7 Function + %17 = OpLoad %6 %15 + OpStore %16 %17 + %18 = OpFunctionCall %2 %10 %16 + %19 = OpLoad %6 %16 + OpStore %15 %19 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpLoad %6 %9 + %14 = OpIAdd %6 %12 %13 + OpBranch %20 + %20 = OpLabel + OpStore %9 %14 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 11, + /*exit_block*/ 11, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 204, + /*new_caller_result_id*/ 205, + /*new_callee_result_id*/ 206, + /*input_id_to_fresh_id*/ {{9, 207}}, + /*output_id_to_fresh_id*/ {{14, 208}}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %200 = OpTypeStruct %6 + %201 = OpTypeFunction %200 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %15 = OpVariable %7 Function + %16 = OpVariable %7 Function + %17 = OpLoad %6 %15 + OpStore %16 %17 + %18 = OpFunctionCall %2 %10 %16 + %19 = OpLoad %6 %16 + OpStore %15 %19 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %205 = OpFunctionCall %200 %202 %9 + %14 = OpCompositeExtract %6 %205 0 + OpBranch %20 + %20 = OpLabel + OpStore %9 %14 + OpReturn + OpFunctionEnd + %202 = OpFunction %200 None %201 + %207 = OpFunctionParameter %7 + %204 = OpLabel + %12 = OpLoad %6 %207 + %208 = OpIAdd %6 %12 %13 + %206 = OpCompositeConstruct %200 %208 + OpReturnValue %206 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineLivesafe) { + // In the following, %30 is a livesafe function, with irrelevant parameter + // %200 and irrelevant local variable %201. Variable %100 is a loop limiter, + // which is not irrelevant. The test checks that the outlined function is + // livesafe, and that the parameters corresponding to %200 and %201 have the + // irrelevant fact associated with them. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %199 = OpTypeFunction %2 %7 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %199 + %200 = OpFunctionParameter %7 + %31 = OpLabel + %100 = OpVariable %7 Function %8 + %201 = OpVariable %7 Function %8 + OpBranch %198 + %198 = OpLabel + OpBranch %20 + %20 = OpLabel + %101 = OpLoad %6 %100 + %102 = OpIAdd %6 %101 %9 + %202 = OpLoad %6 %200 + OpStore %201 %202 + OpStore %100 %102 + %103 = OpUGreaterThanEqual %11 %101 %10 + OpLoopMerge %21 %22 None + OpBranchConditional %103 %21 %104 + %104 = OpLabel + OpBranchConditional %12 %23 %21 + %23 = OpLabel + %105 = OpLoad %6 %100 + %106 = OpIAdd %6 %105 %9 + OpStore %100 %106 + %107 = OpUGreaterThanEqual %11 %105 %10 + OpLoopMerge %25 %26 None + OpBranchConditional %107 %25 %108 + %108 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranchConditional %12 %26 %25 + %26 = OpLabel + OpBranch %23 + %25 = OpLabel + %109 = OpLoad %6 %100 + %110 = OpIAdd %6 %109 %9 + OpStore %100 %110 + %111 = OpUGreaterThanEqual %11 %109 %10 + OpLoopMerge %24 %27 None + OpBranchConditional %111 %24 %112 + %112 = OpLabel + OpBranchConditional %12 %24 %27 + %27 = OpLabel + OpBranch %25 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %20 + %21 = OpLabel + OpBranch %197 + %197 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactFunctionIsLivesafe(30); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 200); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 201); + + TransformationOutlineFunction transformation( + /*entry_block*/ 198, + /*exit_block*/ 197, + /*new_function_struct_return_type_id*/ 400, + /*new_function_type_id*/ 401, + /*new_function_id*/ 402, + /*new_function_region_entry_block*/ 404, + /*new_caller_result_id*/ 405, + /*new_callee_result_id*/ 406, + /*input_id_to_fresh_id*/ {{100, 407}, {200, 408}, {201, 409}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // The original function should still be livesafe. + ASSERT_TRUE(transformation_context.GetFactManager()->FunctionIsLivesafe(30)); + // The outlined function should be livesafe. + ASSERT_TRUE(transformation_context.GetFactManager()->FunctionIsLivesafe(402)); + // The variable and parameter that were originally irrelevant should still be. + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(200)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(201)); + // The loop limiter should still be non-irrelevant. + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(100)); + // The parameters for the original irrelevant variables should be irrelevant. + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(408)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(409)); + // The parameter for the loop limiter should not be irrelevant. + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(407)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %199 = OpTypeFunction %2 %7 + %8 = OpConstant %6 0 + %9 = OpConstant %6 1 + %10 = OpConstant %6 5 + %11 = OpTypeBool + %12 = OpConstantTrue %11 + %401 = OpTypeFunction %2 %7 %7 %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %30 = OpFunction %2 None %199 + %200 = OpFunctionParameter %7 + %31 = OpLabel + %100 = OpVariable %7 Function %8 + %201 = OpVariable %7 Function %8 + OpBranch %198 + %198 = OpLabel + %405 = OpFunctionCall %2 %402 %200 %100 %201 + OpReturn + OpFunctionEnd + %402 = OpFunction %2 None %401 + %408 = OpFunctionParameter %7 + %407 = OpFunctionParameter %7 + %409 = OpFunctionParameter %7 + %404 = OpLabel + OpBranch %20 + %20 = OpLabel + %101 = OpLoad %6 %407 + %102 = OpIAdd %6 %101 %9 + %202 = OpLoad %6 %408 + OpStore %409 %202 + OpStore %407 %102 + %103 = OpUGreaterThanEqual %11 %101 %10 + OpLoopMerge %21 %22 None + OpBranchConditional %103 %21 %104 + %104 = OpLabel + OpBranchConditional %12 %23 %21 + %23 = OpLabel + %105 = OpLoad %6 %407 + %106 = OpIAdd %6 %105 %9 + OpStore %407 %106 + %107 = OpUGreaterThanEqual %11 %105 %10 + OpLoopMerge %25 %26 None + OpBranchConditional %107 %25 %108 + %108 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranchConditional %12 %26 %25 + %26 = OpLabel + OpBranch %23 + %25 = OpLabel + %109 = OpLoad %6 %407 + %110 = OpIAdd %6 %109 %9 + OpStore %407 %110 + %111 = OpUGreaterThanEqual %11 %109 %10 + OpLoopMerge %24 %27 None + OpBranchConditional %111 %24 %112 + %112 = OpLabel + OpBranchConditional %12 %24 %27 + %27 = OpLabel + OpBranch %25 + %24 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %20 + %21 = OpLabel + OpBranch %197 + %197 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks1) { + // This checks that if all blocks in the region being outlined were dead, all + // blocks in the outlined function will be dead. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "x" + OpName %12 "y" + OpName %21 "i" + OpName %46 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %14 = OpTypeBool + %15 = OpConstantFalse %14 + %22 = OpConstant %6 0 + %29 = OpConstant %6 10 + %41 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %46 = OpVariable %7 Function + OpStore %46 %13 + %47 = OpFunctionCall %2 %10 %46 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + %12 = OpVariable %7 Function + %21 = OpVariable %7 Function + OpStore %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %17 + %16 = OpLabel + %18 = OpLoad %6 %9 + OpStore %12 %18 + %19 = OpLoad %6 %9 + %20 = OpIAdd %6 %19 %13 + OpStore %9 %20 + OpStore %21 %22 + OpBranch %23 + %23 = OpLabel + OpLoopMerge %25 %26 None + OpBranch %27 + %27 = OpLabel + %28 = OpLoad %6 %21 + %30 = OpSLessThan %14 %28 %29 + OpBranchConditional %30 %24 %25 + %24 = OpLabel + %31 = OpLoad %6 %9 + %32 = OpLoad %6 %21 + %33 = OpSGreaterThan %14 %31 %32 + OpSelectionMerge %35 None + OpBranchConditional %33 %34 %35 + %34 = OpLabel + OpBranch %26 + %35 = OpLabel + %37 = OpLoad %6 %9 + %38 = OpLoad %6 %12 + %39 = OpIAdd %6 %38 %37 + OpStore %12 %39 + OpBranch %26 + %26 = OpLabel + %40 = OpLoad %6 %21 + %42 = OpIAdd %6 %40 %41 + OpStore %21 %42 + OpBranch %23 + %25 = OpLabel + OpBranch %50 + %50 = OpLabel + OpBranch %17 + %17 = OpLabel + %43 = OpLoad %6 %9 + %44 = OpLoad %6 %12 + %45 = OpIAdd %6 %44 %43 + OpStore %12 %45 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u}) { + transformation_context.GetFactManager()->AddFactBlockIsDead(block_id); + } + + TransformationOutlineFunction transformation( + /*entry_block*/ 16, + /*exit_block*/ 50, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{9, 206}, {12, 207}, {21, 208}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + // All the original blocks, plus the new function entry block, should be dead. + for (uint32_t block_id : {16u, 23u, 24u, 26u, 27u, 34u, 35u, 50u, 203u}) { + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(block_id)); + } +} + +TEST(TransformationOutlineFunctionTest, OutlineWithDeadBlocks2) { + // This checks that if some, but not all, blocks in the outlined region are + // dead, those (but not others) will be dead in the outlined function. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %8 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Private %6 + %8 = OpVariable %7 Private + %9 = OpConstantFalse %6 + %10 = OpTypePointer Function %6 + %12 = OpConstantTrue %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + OpBranch %30 + %30 = OpLabel + OpStore %8 %9 + OpBranch %31 + %31 = OpLabel + OpStore %11 %12 + OpSelectionMerge %36 None + OpBranchConditional %9 %32 %33 + %32 = OpLabel + OpBranch %34 + %33 = OpLabel + OpBranch %36 + %34 = OpLabel + OpBranch %35 + %35 = OpLabel + OpBranch %36 + %36 = OpLabel + OpBranch %37 + %37 = OpLabel + %13 = OpLoad %6 %8 + OpStore %11 %13 + %14 = OpLoad %6 %11 + OpStore %8 %14 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + for (uint32_t block_id : {32u, 34u, 35u}) { + transformation_context.GetFactManager()->AddFactBlockIsDead(block_id); + } + + TransformationOutlineFunction transformation( + /*entry_block*/ 30, + /*exit_block*/ 37, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{11, 206}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + // The blocks that were originally dead, but not others, should be dead. + for (uint32_t block_id : {32u, 34u, 35u}) { + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(block_id)); + } + for (uint32_t block_id : {5u, 30u, 31u, 33u, 36u, 37u, 203u}) { + ASSERT_FALSE( + transformation_context.GetFactManager()->BlockIsDead(block_id)); + } +} + +TEST(TransformationOutlineFunctionTest, + OutlineWithIrrelevantVariablesAndParameters) { + // This checks that if the outlined region uses a mixture of irrelevant and + // non-irrelevant variables and parameters, these properties are preserved + // during outlining. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 %7 + %13 = OpConstant %6 2 + %15 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %11 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %7 + %12 = OpLabel + %14 = OpVariable %7 Function + %20 = OpVariable %7 Function + OpBranch %50 + %50 = OpLabel + OpStore %9 %13 + OpStore %14 %15 + %16 = OpLoad %6 %14 + OpStore %10 %16 + %17 = OpLoad %6 %9 + %18 = OpLoad %6 %10 + %19 = OpIAdd %6 %17 %18 + OpStore %14 %19 + %21 = OpLoad %6 %9 + OpStore %20 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant(9); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 14); + + TransformationOutlineFunction transformation( + /*entry_block*/ 50, + /*exit_block*/ 50, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{9, 206}, {10, 207}, {14, 208}, {20, 209}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + // The variables that were originally irrelevant, plus input parameters + // corresponding to them, should be irrelevant. The rest should not be. + for (uint32_t variable_id : {9u, 14u, 206u, 208u}) { + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant( + variable_id)); + } + for (uint32_t variable_id : {10u, 20u, 207u, 209u}) { + ASSERT_FALSE( + transformation_context.GetFactManager()->BlockIsDead(variable_id)); + } +} + +TEST(TransformationOutlineFunctionTest, + DoNotOutlineCodeThatProducesUsedPointer) { + // This checks that we cannot outline a region of code if it produces a + // pointer result id that gets used outside the region. This avoids creating + // a struct with a pointer member. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %100 = OpTypeInt 32 0 + %99 = OpConstant %100 0 + %101 = OpTypeVector %100 2 + %102 = OpTypePointer Function %100 + %103 = OpTypePointer Function %101 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %104 = OpVariable %103 Function + OpBranch %80 + %80 = OpLabel + %105 = OpAccessChain %102 %104 %99 + OpBranch %106 + %106 = OpLabel + OpStore %105 %99 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 80, + /*exit_block*/ 80, + /*new_function_struct_return_type_id*/ 300, + /*new_function_type_id*/ 301, + /*new_function_id*/ 302, + /*new_function_region_entry_block*/ 304, + /*new_caller_result_id*/ 305, + /*new_callee_result_id*/ 306, + /*input_id_to_fresh_id*/ {{104, 307}}, + /*output_id_to_fresh_id*/ {{105, 308}}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, ExitBlockHeadsLoop) { + // This checks that it is not possible outline a region that ends in a loop + // head. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %15 = OpTypeInt 32 1 + %35 = OpTypeBool + %39 = OpConstant %15 1 + %40 = OpConstantTrue %35 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %22 + %22 = OpLabel + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %15 %39 %22 %39 %25 + OpLoopMerge %26 %25 None + OpBranchConditional %40 %25 %26 + %25 = OpLabel + OpBranch %23 + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 22, + /*exit_block*/ 23, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); +} + +TEST(TransformationOutlineFunctionTest, SkipVoidOutputId) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %81 = OpConstantTrue %21 + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %80 + %80 = OpLabel + %84 = OpFunctionCall %2 %87 + OpBranch %90 + %90 = OpLabel + %86 = OpPhi %2 %84 %80 + OpReturn + OpFunctionEnd + %87 = OpFunction %2 None %3 + %88 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 80, + /*exit_block*/ 80, + /*new_function_struct_return_type_id*/ 300, + /*new_function_type_id*/ 301, + /*new_function_id*/ 302, + /*new_function_region_entry_block*/ 304, + /*new_caller_result_id*/ 305, + /*new_callee_result_id*/ 306, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {{84, 307}}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %81 = OpConstantTrue %21 + %300 = OpTypeStruct + %301 = OpTypeFunction %300 + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %80 + %80 = OpLabel + %305 = OpFunctionCall %300 %302 + %84 = OpUndef %2 + OpBranch %90 + %90 = OpLabel + %86 = OpPhi %2 %84 %80 + OpReturn + OpFunctionEnd + %87 = OpFunction %2 None %3 + %88 = OpLabel + OpReturn + OpFunctionEnd + %302 = OpFunction %300 None %301 + %304 = OpLabel + %307 = OpFunctionCall %2 %87 + %306 = OpCompositeConstruct %300 + OpReturnValue %306 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, Miscellaneous1) { + // This tests outlining of some non-trivial code, and also tests the way + // overflow ids are used by the transformation. + + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %85 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %28 "buf" + OpMemberName %28 0 "u1" + OpMemberName %28 1 "u2" + OpName %30 "" + OpName %85 "color" + OpMemberDecorate %28 0 Offset 0 + OpMemberDecorate %28 1 Offset 4 + OpDecorate %28 Block + OpDecorate %30 DescriptorSet 0 + OpDecorate %30 Binding 0 + OpDecorate %85 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstant %6 3 + %13 = OpConstant %6 4 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %28 = OpTypeStruct %6 %6 + %29 = OpTypePointer Uniform %28 + %30 = OpVariable %29 Uniform + %31 = OpTypePointer Uniform %6 + %35 = OpTypeBool + %39 = OpConstant %15 1 + %84 = OpTypePointer Output %7 + %85 = OpVariable %84 Output + %114 = OpConstant %15 8 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %22 + %22 = OpLabel + %103 = OpPhi %15 %18 %5 %106 %43 + %102 = OpPhi %7 %14 %5 %107 %43 + %101 = OpPhi %15 %18 %5 %40 %43 + %32 = OpAccessChain %31 %30 %18 + %33 = OpLoad %6 %32 + %34 = OpConvertFToS %15 %33 + %36 = OpSLessThan %35 %101 %34 + OpLoopMerge %24 %43 None + OpBranchConditional %36 %23 %24 + %23 = OpLabel + %40 = OpIAdd %15 %101 %39 + OpBranch %150 + %150 = OpLabel + OpBranch %41 + %41 = OpLabel + %107 = OpPhi %7 %102 %150 %111 %65 + %106 = OpPhi %15 %103 %150 %110 %65 + %104 = OpPhi %15 %40 %150 %81 %65 + %47 = OpAccessChain %31 %30 %39 + %48 = OpLoad %6 %47 + %49 = OpConvertFToS %15 %48 + %50 = OpSLessThan %35 %104 %49 + OpLoopMerge %1000 %65 None + OpBranchConditional %50 %42 %1000 + %42 = OpLabel + %60 = OpIAdd %15 %106 %114 + %63 = OpSGreaterThan %35 %104 %60 + OpBranchConditional %63 %64 %65 + %64 = OpLabel + %71 = OpCompositeExtract %6 %107 0 + %72 = OpFAdd %6 %71 %11 + %97 = OpCompositeInsert %7 %72 %107 0 + %76 = OpCompositeExtract %6 %107 3 + %77 = OpConvertFToS %15 %76 + %79 = OpIAdd %15 %60 %77 + OpBranch %65 + %65 = OpLabel + %111 = OpPhi %7 %107 %42 %97 %64 + %110 = OpPhi %15 %60 %42 %79 %64 + %81 = OpIAdd %15 %104 %39 + OpBranch %41 + %1000 = OpLabel + OpBranch %1001 + %1001 = OpLabel + OpBranch %43 + %43 = OpLabel + OpBranch %22 + %24 = OpLabel + %87 = OpCompositeExtract %6 %102 0 + %91 = OpConvertSToF %6 %103 + %92 = OpCompositeConstruct %7 %87 %11 %91 %10 + OpStore %85 %92 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + spvtools::ValidatorOptions validator_options; + + TransformationOutlineFunction transformation( + /*entry_block*/ 150, + /*exit_block*/ 1001, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}}, + /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}}); + + TransformationOutlineFunction transformation_with_missing_input_id( + /*entry_block*/ 150, + /*exit_block*/ 1001, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{102, 300}, {40, 302}}, + /*output_id_to_fresh_id*/ {{106, 400}, {107, 401}}); + + TransformationOutlineFunction transformation_with_missing_output_id( + /*entry_block*/ 150, + /*exit_block*/ 1001, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{102, 300}, {103, 301}, {40, 302}}, + /*output_id_to_fresh_id*/ {{106, 400}}); + + TransformationOutlineFunction + transformation_with_missing_input_and_output_ids( + /*entry_block*/ 150, + /*exit_block*/ 1001, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {{102, 300}, {40, 302}}, + /*output_id_to_fresh_id*/ {{106, 400}}); + + { + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + +#ifndef NDEBUG + // We expect the following applicability checks to lead to assertion + // failures since the transformations are missing input or output ids, and + // the transformation context does not have a source of overflow ids. + ASSERT_DEATH(transformation_with_missing_input_id.IsApplicable( + context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); + ASSERT_DEATH(transformation_with_missing_output_id.IsApplicable( + context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); + ASSERT_DEATH(transformation_with_missing_input_and_output_ids.IsApplicable( + context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); +#endif + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %85 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %28 "buf" + OpMemberName %28 0 "u1" + OpMemberName %28 1 "u2" + OpName %30 "" + OpName %85 "color" + OpMemberDecorate %28 0 Offset 0 + OpMemberDecorate %28 1 Offset 4 + OpDecorate %28 Block + OpDecorate %30 DescriptorSet 0 + OpDecorate %30 Binding 0 + OpDecorate %85 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstant %6 3 + %13 = OpConstant %6 4 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %28 = OpTypeStruct %6 %6 + %29 = OpTypePointer Uniform %28 + %30 = OpVariable %29 Uniform + %31 = OpTypePointer Uniform %6 + %35 = OpTypeBool + %39 = OpConstant %15 1 + %84 = OpTypePointer Output %7 + %85 = OpVariable %84 Output + %114 = OpConstant %15 8 + %200 = OpTypeStruct %7 %15 + %201 = OpTypeFunction %200 %15 %7 %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %22 + %22 = OpLabel + %103 = OpPhi %15 %18 %5 %106 %43 + %102 = OpPhi %7 %14 %5 %107 %43 + %101 = OpPhi %15 %18 %5 %40 %43 + %32 = OpAccessChain %31 %30 %18 + %33 = OpLoad %6 %32 + %34 = OpConvertFToS %15 %33 + %36 = OpSLessThan %35 %101 %34 + OpLoopMerge %24 %43 None + OpBranchConditional %36 %23 %24 + %23 = OpLabel + %40 = OpIAdd %15 %101 %39 + OpBranch %150 + %150 = OpLabel + %204 = OpFunctionCall %200 %202 %103 %102 %40 + %107 = OpCompositeExtract %7 %204 0 + %106 = OpCompositeExtract %15 %204 1 + OpBranch %43 + %43 = OpLabel + OpBranch %22 + %24 = OpLabel + %87 = OpCompositeExtract %6 %102 0 + %91 = OpConvertSToF %6 %103 + %92 = OpCompositeConstruct %7 %87 %11 %91 %10 + OpStore %85 %92 + OpReturn + OpFunctionEnd + %202 = OpFunction %200 None %201 + %301 = OpFunctionParameter %15 + %300 = OpFunctionParameter %7 + %302 = OpFunctionParameter %15 + %203 = OpLabel + OpBranch %41 + %41 = OpLabel + %401 = OpPhi %7 %300 %203 %111 %65 + %400 = OpPhi %15 %301 %203 %110 %65 + %104 = OpPhi %15 %302 %203 %81 %65 + %47 = OpAccessChain %31 %30 %39 + %48 = OpLoad %6 %47 + %49 = OpConvertFToS %15 %48 + %50 = OpSLessThan %35 %104 %49 + OpLoopMerge %1000 %65 None + OpBranchConditional %50 %42 %1000 + %42 = OpLabel + %60 = OpIAdd %15 %400 %114 + %63 = OpSGreaterThan %35 %104 %60 + OpBranchConditional %63 %64 %65 + %64 = OpLabel + %71 = OpCompositeExtract %6 %401 0 + %72 = OpFAdd %6 %71 %11 + %97 = OpCompositeInsert %7 %72 %401 0 + %76 = OpCompositeExtract %6 %401 3 + %77 = OpConvertFToS %15 %76 + %79 = OpIAdd %15 %60 %77 + OpBranch %65 + %65 = OpLabel + %111 = OpPhi %7 %401 %42 %97 %64 + %110 = OpPhi %15 %60 %42 %79 %64 + %81 = OpIAdd %15 %104 %39 + OpBranch %41 + %1000 = OpLabel + OpBranch %1001 + %1001 = OpLabel + %205 = OpCompositeConstruct %200 %401 %400 + OpReturnValue %205 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); + } + + { + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + auto overflow_ids_unique_ptr = MakeUnique(2000); + auto overflow_ids_ptr = overflow_ids_unique_ptr.get(); + TransformationContext new_transformation_context( + MakeUnique(context.get()), validator_options, + std::move(overflow_ids_unique_ptr)); + ASSERT_TRUE(transformation_with_missing_input_id.IsApplicable( + context.get(), new_transformation_context)); + ASSERT_TRUE(transformation_with_missing_output_id.IsApplicable( + context.get(), new_transformation_context)); + ASSERT_TRUE(transformation_with_missing_input_and_output_ids.IsApplicable( + context.get(), new_transformation_context)); + ApplyAndCheckFreshIds(transformation_with_missing_input_and_output_ids, + context.get(), &new_transformation_context, + overflow_ids_ptr->GetIssuedOverflowIds()); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %85 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %28 "buf" + OpMemberName %28 0 "u1" + OpMemberName %28 1 "u2" + OpName %30 "" + OpName %85 "color" + OpMemberDecorate %28 0 Offset 0 + OpMemberDecorate %28 1 Offset 4 + OpDecorate %28 Block + OpDecorate %30 DescriptorSet 0 + OpDecorate %30 Binding 0 + OpDecorate %85 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %10 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpConstant %6 3 + %13 = OpConstant %6 4 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %28 = OpTypeStruct %6 %6 + %29 = OpTypePointer Uniform %28 + %30 = OpVariable %29 Uniform + %31 = OpTypePointer Uniform %6 + %35 = OpTypeBool + %39 = OpConstant %15 1 + %84 = OpTypePointer Output %7 + %85 = OpVariable %84 Output + %114 = OpConstant %15 8 + %200 = OpTypeStruct %7 %15 + %201 = OpTypeFunction %200 %15 %7 %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %22 + %22 = OpLabel + %103 = OpPhi %15 %18 %5 %106 %43 + %102 = OpPhi %7 %14 %5 %107 %43 + %101 = OpPhi %15 %18 %5 %40 %43 + %32 = OpAccessChain %31 %30 %18 + %33 = OpLoad %6 %32 + %34 = OpConvertFToS %15 %33 + %36 = OpSLessThan %35 %101 %34 + OpLoopMerge %24 %43 None + OpBranchConditional %36 %23 %24 + %23 = OpLabel + %40 = OpIAdd %15 %101 %39 + OpBranch %150 + %150 = OpLabel + %204 = OpFunctionCall %200 %202 %103 %102 %40 + %107 = OpCompositeExtract %7 %204 0 + %106 = OpCompositeExtract %15 %204 1 + OpBranch %43 + %43 = OpLabel + OpBranch %22 + %24 = OpLabel + %87 = OpCompositeExtract %6 %102 0 + %91 = OpConvertSToF %6 %103 + %92 = OpCompositeConstruct %7 %87 %11 %91 %10 + OpStore %85 %92 + OpReturn + OpFunctionEnd + %202 = OpFunction %200 None %201 + %2000 = OpFunctionParameter %15 + %300 = OpFunctionParameter %7 + %302 = OpFunctionParameter %15 + %203 = OpLabel + OpBranch %41 + %41 = OpLabel + %2001 = OpPhi %7 %300 %203 %111 %65 + %400 = OpPhi %15 %2000 %203 %110 %65 + %104 = OpPhi %15 %302 %203 %81 %65 + %47 = OpAccessChain %31 %30 %39 + %48 = OpLoad %6 %47 + %49 = OpConvertFToS %15 %48 + %50 = OpSLessThan %35 %104 %49 + OpLoopMerge %1000 %65 None + OpBranchConditional %50 %42 %1000 + %42 = OpLabel + %60 = OpIAdd %15 %400 %114 + %63 = OpSGreaterThan %35 %104 %60 + OpBranchConditional %63 %64 %65 + %64 = OpLabel + %71 = OpCompositeExtract %6 %2001 0 + %72 = OpFAdd %6 %71 %11 + %97 = OpCompositeInsert %7 %72 %2001 0 + %76 = OpCompositeExtract %6 %2001 3 + %77 = OpConvertFToS %15 %76 + %79 = OpIAdd %15 %60 %77 + OpBranch %65 + %65 = OpLabel + %111 = OpPhi %7 %2001 %42 %97 %64 + %110 = OpPhi %15 %60 %42 %79 %64 + %81 = OpIAdd %15 %104 %39 + OpBranch %41 + %1000 = OpLabel + OpBranch %1001 + %1001 = OpLabel + %205 = OpCompositeConstruct %200 %2001 %400 + OpReturnValue %205 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); + } +} + +TEST(TransformationOutlineFunctionTest, Miscellaneous2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %167 = OpConstantTrue %21 + %168 = OpConstantFalse %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %34 + %34 = OpLabel + OpLoopMerge %36 %37 None + OpBranchConditional %168 %37 %38 + %38 = OpLabel + OpBranchConditional %168 %37 %36 + %37 = OpLabel + OpBranch %34 + %36 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 38, + /*exit_block*/ 36, + /*new_function_struct_return_type_id*/ 200, + /*new_function_type_id*/ 201, + /*new_function_id*/ 202, + /*new_function_region_entry_block*/ 203, + /*new_caller_result_id*/ 204, + /*new_callee_result_id*/ 205, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationOutlineFunctionTest, Miscellaneous3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %167 = OpConstantTrue %21 + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %80 + %80 = OpLabel + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + OpBranchConditional %167 %15 %16 + %15 = OpLabel + OpBranch %17 + %16 = OpLabel + OpBranch %81 + %81 = OpLabel + OpReturn + %17 = OpLabel + OpBranch %14 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 80, + /*exit_block*/ 81, + /*new_function_struct_return_type_id*/ 300, + /*new_function_type_id*/ 301, + /*new_function_id*/ 302, + /*new_function_region_entry_block*/ 304, + /*new_caller_result_id*/ 305, + /*new_callee_result_id*/ 306, + /*input_id_to_fresh_id*/ {}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %167 = OpConstantTrue %21 + %6 = OpFunction %2 None %3 + %7 = OpLabel + OpBranch %80 + %80 = OpLabel + %305 = OpFunctionCall %2 %302 + OpReturn + OpFunctionEnd + %302 = OpFunction %2 None %3 + %304 = OpLabel + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + OpBranchConditional %167 %15 %16 + %15 = OpLabel + OpBranch %17 + %16 = OpLabel + OpBranch %81 + %81 = OpLabel + OpReturn + %17 = OpLabel + OpBranch %14 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationOutlineFunctionTest, Miscellaneous4) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %100 = OpTypeInt 32 0 + %101 = OpTypePointer Function %100 + %102 = OpTypePointer Function %100 + %103 = OpTypeFunction %2 %101 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %104 = OpVariable %102 Function + OpBranch %80 + %80 = OpLabel + %105 = OpLoad %100 %104 + OpBranch %106 + %106 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationOutlineFunction transformation( + /*entry_block*/ 80, + /*exit_block*/ 106, + /*new_function_struct_return_type_id*/ 300, + /*new_function_type_id*/ 301, + /*new_function_id*/ 302, + /*new_function_region_entry_block*/ 304, + /*new_caller_result_id*/ 305, + /*new_callee_result_id*/ 306, + /*input_id_to_fresh_id*/ {{104, 307}}, + /*output_id_to_fresh_id*/ {}); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %6 "main" + OpExecutionMode %6 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %21 = OpTypeBool + %100 = OpTypeInt 32 0 + %101 = OpTypePointer Function %100 + %102 = OpTypePointer Function %100 + %103 = OpTypeFunction %2 %101 + %301 = OpTypeFunction %2 %102 + %6 = OpFunction %2 None %3 + %7 = OpLabel + %104 = OpVariable %102 Function + OpBranch %80 + %80 = OpLabel + %305 = OpFunctionCall %2 %302 %104 + OpReturn + OpFunctionEnd + %302 = OpFunction %2 None %301 + %307 = OpFunctionParameter %102 + %304 = OpLabel + %105 = OpLoad %100 %307 + OpBranch %106 + %106 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_permute_function_parameters_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_permute_function_parameters_test.cpp new file mode 100644 index 0000000..c1eb125 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_permute_function_parameters_test.cpp @@ -0,0 +1,563 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_permute_function_parameters.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationPermuteFunctionParametersTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %72 %74 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "g(f1;f1;" + OpName %10 "x" + OpName %11 "y" + OpName %22 "f(f1;i1;vf2;" + OpName %19 "x" + OpName %20 "y" + OpName %21 "z" + OpName %28 "cond(i1;f1;" + OpName %26 "a" + OpName %27 "b" + OpName %53 "param" + OpName %54 "param" + OpName %66 "param" + OpName %67 "param" + OpName %72 "color" + OpName %74 "gl_FragCoord" + OpName %75 "param" + OpName %79 "param" + OpName %85 "param" + OpName %86 "param" + OpName %91 "param" + OpName %92 "param" + OpName %93 "param" + OpName %99 "param" + OpName %100 "param" + OpName %101 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %72 Location 0 + OpDecorate %74 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeVector %6 4 + %9 = OpTypeFunction %8 %7 %7 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %16 = OpTypeVector %6 2 + %17 = OpTypePointer Function %16 + %18 = OpTypeFunction %8 %7 %15 %17 + %24 = OpTypeBool + %25 = OpTypeFunction %24 %15 %7 + %31 = OpConstant %6 255 + %33 = OpConstant %6 0 + %34 = OpConstant %6 1 + %42 = OpTypeInt 32 0 + %43 = OpConstant %42 0 + %49 = OpConstant %42 1 + %64 = OpConstant %14 4 + %65 = OpConstant %6 5 + %71 = OpTypePointer Output %8 + %72 = OpVariable %71 Output + %73 = OpTypePointer Input %8 + %74 = OpVariable %73 Input + %76 = OpTypePointer Input %6 + %84 = OpConstant %14 5 + %90 = OpConstant %6 3 + %98 = OpConstant %6 4 + %206 = OpTypeFunction %2 %14 %16 + %223 = OpTypeFunction %2 %6 %8 + %224 = OpTypeFunction %2 %8 %6 + %233 = OpTypeFunction %2 %42 %24 + %234 = OpTypeFunction %2 %24 %42 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %66 = OpVariable %15 Function + %67 = OpVariable %7 Function + %75 = OpVariable %7 Function + %79 = OpVariable %7 Function + %85 = OpVariable %15 Function + %86 = OpVariable %7 Function + %91 = OpVariable %7 Function + %92 = OpVariable %15 Function + %93 = OpVariable %17 Function + %99 = OpVariable %7 Function + %100 = OpVariable %15 Function + %101 = OpVariable %17 Function + OpStore %66 %64 + OpStore %67 %65 + %68 = OpFunctionCall %24 %28 %66 %67 + OpSelectionMerge %70 None + OpBranchConditional %68 %69 %83 + %69 = OpLabel + %77 = OpAccessChain %76 %74 %43 + %78 = OpLoad %6 %77 + OpStore %75 %78 + %80 = OpAccessChain %76 %74 %49 + %81 = OpLoad %6 %80 + OpStore %79 %81 + %82 = OpFunctionCall %8 %12 %75 %79 + OpStore %72 %82 + OpBranch %70 + %83 = OpLabel + OpStore %85 %84 + OpStore %86 %65 + %87 = OpFunctionCall %24 %28 %85 %86 + OpSelectionMerge %89 None + OpBranchConditional %87 %88 %97 + %88 = OpLabel + OpStore %91 %90 + OpStore %92 %64 + %94 = OpLoad %8 %74 + %95 = OpVectorShuffle %16 %94 %94 0 1 + OpStore %93 %95 + %96 = OpFunctionCall %8 %22 %91 %92 %93 + OpStore %72 %96 + OpBranch %89 + %97 = OpLabel + OpStore %99 %98 + OpStore %100 %84 + %102 = OpLoad %8 %74 + %103 = OpVectorShuffle %16 %102 %102 0 1 + OpStore %101 %103 + %104 = OpFunctionCall %8 %22 %99 %100 %101 + OpStore %72 %104 + OpBranch %89 + %89 = OpLabel + OpBranch %70 + %70 = OpLabel + OpReturn + OpFunctionEnd + + ; adjust type of the function in-place + %12 = OpFunction %8 None %9 + %10 = OpFunctionParameter %7 + %11 = OpFunctionParameter %7 + %13 = OpLabel + %30 = OpLoad %6 %10 + %32 = OpFDiv %6 %30 %31 + %35 = OpLoad %6 %11 + %36 = OpFDiv %6 %35 %31 + %37 = OpFSub %6 %34 %36 + %38 = OpCompositeConstruct %8 %32 %33 %37 %34 + OpReturnValue %38 + OpFunctionEnd + %22 = OpFunction %8 None %18 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %15 + %21 = OpFunctionParameter %17 + %23 = OpLabel + %53 = OpVariable %7 Function + %54 = OpVariable %7 Function + %41 = OpLoad %6 %19 + %44 = OpAccessChain %7 %21 %43 + %45 = OpLoad %6 %44 + %46 = OpFAdd %6 %41 %45 + %47 = OpLoad %14 %20 + %48 = OpConvertSToF %6 %47 + %50 = OpAccessChain %7 %21 %49 + %51 = OpLoad %6 %50 + %52 = OpFAdd %6 %48 %51 + OpStore %53 %46 + OpStore %54 %52 + %55 = OpFunctionCall %8 %12 %53 %54 + OpReturnValue %55 + OpFunctionEnd + %28 = OpFunction %24 None %25 + %26 = OpFunctionParameter %15 + %27 = OpFunctionParameter %7 + %29 = OpLabel + %58 = OpLoad %14 %26 + %59 = OpConvertSToF %6 %58 + %60 = OpLoad %6 %27 + %61 = OpFOrdLessThan %24 %59 %60 + OpReturnValue %61 + OpFunctionEnd + + ; create a new function type + %200 = OpFunction %2 None %206 + %207 = OpFunctionParameter %14 + %208 = OpFunctionParameter %16 + %202 = OpLabel + OpReturn + OpFunctionEnd + %203 = OpFunction %2 None %206 + %209 = OpFunctionParameter %14 + %210 = OpFunctionParameter %16 + %205 = OpLabel + OpReturn + OpFunctionEnd + + ; reuse an existing function type + %211 = OpFunction %2 None %223 + %212 = OpFunctionParameter %6 + %213 = OpFunctionParameter %8 + %214 = OpLabel + OpReturn + OpFunctionEnd + %215 = OpFunction %2 None %224 + %216 = OpFunctionParameter %8 + %217 = OpFunctionParameter %6 + %218 = OpLabel + OpReturn + OpFunctionEnd + %219 = OpFunction %2 None %224 + %220 = OpFunctionParameter %8 + %221 = OpFunctionParameter %6 + %222 = OpLabel + OpReturn + OpFunctionEnd + + ; don't adjust the type of the function if it creates a duplicate + %225 = OpFunction %2 None %233 + %226 = OpFunctionParameter %42 + %227 = OpFunctionParameter %24 + %228 = OpLabel + OpReturn + OpFunctionEnd + %229 = OpFunction %2 None %234 + %230 = OpFunctionParameter %24 + %231 = OpFunctionParameter %42 + %232 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Can't permute main function + ASSERT_FALSE(TransformationPermuteFunctionParameters(4, 105, {}) + .IsApplicable(context.get(), transformation_context)); + + // Can't permute invalid instruction + ASSERT_FALSE(TransformationPermuteFunctionParameters(101, 105, {}) + .IsApplicable(context.get(), transformation_context)); + + // Permutation has too many values + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {2, 1, 0, 3}) + .IsApplicable(context.get(), transformation_context)); + + // Permutation has too few values + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {0, 1}) + .IsApplicable(context.get(), transformation_context)); + + // Permutation has invalid values 1 + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 105, {3, 1, 0}) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // Permutation has invalid values 2 + ASSERT_DEATH(TransformationPermuteFunctionParameters(22, 105, {2, 2, 1}) + .IsApplicable(context.get(), transformation_context), + "Permutation has duplicates"); +#endif + + // Result id for new function type is not fresh. + ASSERT_FALSE(TransformationPermuteFunctionParameters(22, 42, {2, 1, 0}) + .IsApplicable(context.get(), transformation_context)); + + // Successful transformations + { + TransformationPermuteFunctionParameters transformation(12, 105, {1, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationPermuteFunctionParameters transformation(28, 106, {1, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationPermuteFunctionParameters transformation(200, 107, {1, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationPermuteFunctionParameters transformation(219, 108, {1, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationPermuteFunctionParameters transformation(229, 109, {1, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %72 %74 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %12 "g(f1;f1;" + OpName %10 "x" + OpName %11 "y" + OpName %22 "f(f1;i1;vf2;" + OpName %19 "x" + OpName %20 "y" + OpName %21 "z" + OpName %28 "cond(i1;f1;" + OpName %26 "a" + OpName %27 "b" + OpName %53 "param" + OpName %54 "param" + OpName %66 "param" + OpName %67 "param" + OpName %72 "color" + OpName %74 "gl_FragCoord" + OpName %75 "param" + OpName %79 "param" + OpName %85 "param" + OpName %86 "param" + OpName %91 "param" + OpName %92 "param" + OpName %93 "param" + OpName %99 "param" + OpName %100 "param" + OpName %101 "param" + OpDecorate %20 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %47 RelaxedPrecision + OpDecorate %58 RelaxedPrecision + OpDecorate %72 Location 0 + OpDecorate %74 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %8 = OpTypeVector %6 4 + %9 = OpTypeFunction %8 %7 %7 + %14 = OpTypeInt 32 1 + %15 = OpTypePointer Function %14 + %16 = OpTypeVector %6 2 + %17 = OpTypePointer Function %16 + %18 = OpTypeFunction %8 %7 %15 %17 + %24 = OpTypeBool + %31 = OpConstant %6 255 + %33 = OpConstant %6 0 + %34 = OpConstant %6 1 + %42 = OpTypeInt 32 0 + %43 = OpConstant %42 0 + %49 = OpConstant %42 1 + %64 = OpConstant %14 4 + %65 = OpConstant %6 5 + %71 = OpTypePointer Output %8 + %72 = OpVariable %71 Output + %73 = OpTypePointer Input %8 + %74 = OpVariable %73 Input + %76 = OpTypePointer Input %6 + %84 = OpConstant %14 5 + %90 = OpConstant %6 3 + %98 = OpConstant %6 4 + %206 = OpTypeFunction %2 %14 %16 + %223 = OpTypeFunction %2 %6 %8 + %224 = OpTypeFunction %2 %8 %6 + %233 = OpTypeFunction %2 %42 %24 + %25 = OpTypeFunction %24 %7 %15 + %107 = OpTypeFunction %2 %16 %14 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %66 = OpVariable %15 Function + %67 = OpVariable %7 Function + %75 = OpVariable %7 Function + %79 = OpVariable %7 Function + %85 = OpVariable %15 Function + %86 = OpVariable %7 Function + %91 = OpVariable %7 Function + %92 = OpVariable %15 Function + %93 = OpVariable %17 Function + %99 = OpVariable %7 Function + %100 = OpVariable %15 Function + %101 = OpVariable %17 Function + OpStore %66 %64 + OpStore %67 %65 + %68 = OpFunctionCall %24 %28 %67 %66 + OpSelectionMerge %70 None + OpBranchConditional %68 %69 %83 + %69 = OpLabel + %77 = OpAccessChain %76 %74 %43 + %78 = OpLoad %6 %77 + OpStore %75 %78 + %80 = OpAccessChain %76 %74 %49 + %81 = OpLoad %6 %80 + OpStore %79 %81 + %82 = OpFunctionCall %8 %12 %79 %75 + OpStore %72 %82 + OpBranch %70 + %83 = OpLabel + OpStore %85 %84 + OpStore %86 %65 + %87 = OpFunctionCall %24 %28 %86 %85 + OpSelectionMerge %89 None + OpBranchConditional %87 %88 %97 + %88 = OpLabel + OpStore %91 %90 + OpStore %92 %64 + %94 = OpLoad %8 %74 + %95 = OpVectorShuffle %16 %94 %94 0 1 + OpStore %93 %95 + %96 = OpFunctionCall %8 %22 %91 %92 %93 + OpStore %72 %96 + OpBranch %89 + %97 = OpLabel + OpStore %99 %98 + OpStore %100 %84 + %102 = OpLoad %8 %74 + %103 = OpVectorShuffle %16 %102 %102 0 1 + OpStore %101 %103 + %104 = OpFunctionCall %8 %22 %99 %100 %101 + OpStore %72 %104 + OpBranch %89 + %89 = OpLabel + OpBranch %70 + %70 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %8 None %9 + %11 = OpFunctionParameter %7 + %10 = OpFunctionParameter %7 + %13 = OpLabel + %30 = OpLoad %6 %10 + %32 = OpFDiv %6 %30 %31 + %35 = OpLoad %6 %11 + %36 = OpFDiv %6 %35 %31 + %37 = OpFSub %6 %34 %36 + %38 = OpCompositeConstruct %8 %32 %33 %37 %34 + OpReturnValue %38 + OpFunctionEnd + %22 = OpFunction %8 None %18 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %15 + %21 = OpFunctionParameter %17 + %23 = OpLabel + %53 = OpVariable %7 Function + %54 = OpVariable %7 Function + %41 = OpLoad %6 %19 + %44 = OpAccessChain %7 %21 %43 + %45 = OpLoad %6 %44 + %46 = OpFAdd %6 %41 %45 + %47 = OpLoad %14 %20 + %48 = OpConvertSToF %6 %47 + %50 = OpAccessChain %7 %21 %49 + %51 = OpLoad %6 %50 + %52 = OpFAdd %6 %48 %51 + OpStore %53 %46 + OpStore %54 %52 + %55 = OpFunctionCall %8 %12 %54 %53 + OpReturnValue %55 + OpFunctionEnd + %28 = OpFunction %24 None %25 + %27 = OpFunctionParameter %7 + %26 = OpFunctionParameter %15 + %29 = OpLabel + %58 = OpLoad %14 %26 + %59 = OpConvertSToF %6 %58 + %60 = OpLoad %6 %27 + %61 = OpFOrdLessThan %24 %59 %60 + OpReturnValue %61 + OpFunctionEnd + %200 = OpFunction %2 None %107 + %208 = OpFunctionParameter %16 + %207 = OpFunctionParameter %14 + %202 = OpLabel + OpReturn + OpFunctionEnd + %203 = OpFunction %2 None %206 + %209 = OpFunctionParameter %14 + %210 = OpFunctionParameter %16 + %205 = OpLabel + OpReturn + OpFunctionEnd + %211 = OpFunction %2 None %223 + %212 = OpFunctionParameter %6 + %213 = OpFunctionParameter %8 + %214 = OpLabel + OpReturn + OpFunctionEnd + %215 = OpFunction %2 None %224 + %216 = OpFunctionParameter %8 + %217 = OpFunctionParameter %6 + %218 = OpLabel + OpReturn + OpFunctionEnd + %219 = OpFunction %2 None %223 + %221 = OpFunctionParameter %6 + %220 = OpFunctionParameter %8 + %222 = OpLabel + OpReturn + OpFunctionEnd + %225 = OpFunction %2 None %233 + %226 = OpFunctionParameter %42 + %227 = OpFunctionParameter %24 + %228 = OpLabel + OpReturn + OpFunctionEnd + %229 = OpFunction %2 None %233 + %231 = OpFunctionParameter %42 + %230 = OpFunctionParameter %24 + %232 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_permute_phi_operands_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_permute_phi_operands_test.cpp new file mode 100644 index 0000000..2843cfc --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_permute_phi_operands_test.cpp @@ -0,0 +1,155 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_permute_phi_operands.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationPermutePhiOperandsTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstant %6 1 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %12 = OpLoad %6 %8 + %13 = OpLoad %6 %10 + %15 = OpSLessThan %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %21 + %16 = OpLabel + %18 = OpLoad %6 %10 + %19 = OpLoad %6 %8 + %20 = OpIAdd %6 %19 %18 + OpBranch %17 + %21 = OpLabel + %22 = OpLoad %6 %10 + %23 = OpLoad %6 %8 + %24 = OpISub %6 %23 %22 + OpBranch %17 + %17 = OpLabel + %25 = OpPhi %6 %20 %16 %24 %21 + OpStore %8 %25 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Result id is invalid. + ASSERT_FALSE(TransformationPermutePhiOperands(26, {}).IsApplicable( + context.get(), transformation_context)); + + // Result id is not of an OpPhi instruction. + ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable( + context.get(), transformation_context)); + + // Result id is not of an OpPhi instruction. + ASSERT_FALSE(TransformationPermutePhiOperands(24, {}).IsApplicable( + context.get(), transformation_context)); + + // Permutation has invalid size. + ASSERT_FALSE(TransformationPermutePhiOperands(25, {0, 1, 2}) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // Permutation has duplicates. + ASSERT_DEATH(TransformationPermutePhiOperands(25, {0, 0}) + .IsApplicable(context.get(), transformation_context), + "Permutation has duplicates"); +#endif + + // Permutation's values are not in range. + ASSERT_FALSE(TransformationPermutePhiOperands(25, {1, 2}) + .IsApplicable(context.get(), transformation_context)); + + TransformationPermutePhiOperands transformation(25, {1, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstant %6 1 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %12 = OpLoad %6 %8 + %13 = OpLoad %6 %10 + %15 = OpSLessThan %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %21 + %16 = OpLabel + %18 = OpLoad %6 %10 + %19 = OpLoad %6 %8 + %20 = OpIAdd %6 %19 %18 + OpBranch %17 + %21 = OpLabel + %22 = OpLoad %6 %10 + %23 = OpLoad %6 %8 + %24 = OpISub %6 %23 %22 + OpBranch %17 + %17 = OpLabel + %25 = OpPhi %6 %24 %21 %20 %16 + OpStore %8 %25 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_propagate_instruction_down_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_propagate_instruction_down_test.cpp new file mode 100644 index 0000000..52974ca --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_propagate_instruction_down_test.cpp @@ -0,0 +1,1138 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_propagate_instruction_down.h" + +#include "gtest/gtest.h" +#include "source/fuzz/counter_overflow_id_source.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationPropagateInstructionDownTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %9 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + ; Has no instruction to propagate + %5 = OpLabel + %10 = OpVariable %9 Function + %8 = OpCopyObject %6 %7 + OpStore %10 %8 + OpBranch %11 + + ; Unreachable block + %100 = OpLabel + %101 = OpCopyObject %6 %7 + OpBranch %11 + + ; Selection header + ; + ; One of acceptable successors has an OpPhi that uses propagated + ; instruction's id + %11 = OpLabel + %19 = OpCopyObject %6 %7 + OpSelectionMerge %18 None + OpBranchConditional %13 %14 %18 + + ; %16 has no acceptable successors + %14 = OpLabel + %20 = OpPhi %6 %19 %11 + %15 = OpCopyObject %6 %7 ; dependency + OpBranch %16 + %16 = OpLabel + %17 = OpCopyObject %6 %15 + OpBranch %18 + + ; Can be applied + %18 = OpLabel + %21 = OpCopyObject %6 %7 + OpSelectionMerge %24 None + OpBranchConditional %13 %22 %23 + %22 = OpLabel + %29 = OpPhi %6 %7 %18 + OpStore %10 %21 + OpBranch %24 + %23 = OpLabel + OpStore %10 %21 + OpBranch %24 + %24 = OpLabel + OpStore %10 %21 + OpBranch %32 + + ; Can't replace all uses of the propagated instruction: %30 is + ; propagated into %27. + %32 = OpLabel + OpLoopMerge %28 %27 None + OpBranchConditional %13 %26 %28 + %26 = OpLabel + %25 = OpCopyObject %6 %7 + %30 = OpCopyObject %6 %25 + OpBranchConditional %13 %27 %28 + %27 = OpLabel + OpBranch %32 + %28 = OpLabel + %31 = OpPhi %6 %30 %26 %7 %32 ; Can't replace this use + OpReturn + + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Invalid block id. + ASSERT_FALSE(TransformationPropagateInstructionDown(200, 200, {{}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationPropagateInstructionDown(101, 200, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // The block is unreachable. + ASSERT_FALSE(TransformationPropagateInstructionDown(100, 200, {{}}) + .IsApplicable(context.get(), transformation_context)); + + // The block has no instruction to propagate. + ASSERT_FALSE(TransformationPropagateInstructionDown(5, 200, {{{11, 201}}}) + .IsApplicable(context.get(), transformation_context)); + + // The block has no acceptable successors. + ASSERT_FALSE(TransformationPropagateInstructionDown(16, 200, {{{18, 201}}}) + .IsApplicable(context.get(), transformation_context)); + + // One of acceptable successors has an OpPhi that uses propagated + // instruction's id. + ASSERT_FALSE( + TransformationPropagateInstructionDown(11, 200, {{{14, 201}, {18, 202}}}) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // Not all fresh ids are provided. + ASSERT_DEATH( + TransformationPropagateInstructionDown(18, 200, {{{22, 201}, {202, 203}}}) + .IsApplicable(context.get(), transformation_context), + "Bad attempt to query whether overflow ids are available."); +#endif + + // Not all fresh ids are fresh. + ASSERT_FALSE(TransformationPropagateInstructionDown( + 18, 18, {{{22, 201}, {23, 202}, {202, 203}}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationPropagateInstructionDown( + 18, 200, {{{22, 22}, {23, 202}, {202, 203}}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationPropagateInstructionDown( + 18, 18, {{{22, 22}, {23, 202}, {202, 203}}}) + .IsApplicable(context.get(), transformation_context)); + + // Not all fresh ids are unique. + ASSERT_FALSE(TransformationPropagateInstructionDown( + 18, 200, {{{22, 200}, {23, 202}, {202, 200}}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationPropagateInstructionDown( + 18, 200, {{{22, 201}, {23, 202}, {202, 200}}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationPropagateInstructionDown( + 18, 200, {{{22, 201}, {23, 201}, {202, 203}}}) + .IsApplicable(context.get(), transformation_context)); + + // Can't replace all uses of the propagated instruction: %30 is propagated + // into %27. + ASSERT_FALSE(TransformationPropagateInstructionDown(26, 200, {{{27, 201}}}) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationPropagateInstructionDown transformation( + 18, 200, {{{22, 201}, {23, 202}, {202, 203}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {}), MakeDataDescriptor(202, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {}), MakeDataDescriptor(200, {}))); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %9 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + ; Has no instruction to propagate + %5 = OpLabel + %10 = OpVariable %9 Function + %8 = OpCopyObject %6 %7 + OpStore %10 %8 + OpBranch %11 + + ; Unreachable block + %100 = OpLabel + %101 = OpCopyObject %6 %7 + OpBranch %11 + + ; Selection header + ; + ; One of acceptable successors has an OpPhi that uses propagated + ; instruction's id + %11 = OpLabel + %19 = OpCopyObject %6 %7 + OpSelectionMerge %18 None + OpBranchConditional %13 %14 %18 + + ; %16 has no acceptable successors + %14 = OpLabel + %20 = OpPhi %6 %19 %11 + %15 = OpCopyObject %6 %7 ; dependency + OpBranch %16 + %16 = OpLabel + %17 = OpCopyObject %6 %15 + OpBranch %18 + + ; Can be applied + %18 = OpLabel + OpSelectionMerge %24 None + OpBranchConditional %13 %22 %23 + %22 = OpLabel + %29 = OpPhi %6 %7 %18 + %201 = OpCopyObject %6 %7 + OpStore %10 %201 + OpBranch %24 + %23 = OpLabel + %202 = OpCopyObject %6 %7 + OpStore %10 %202 + OpBranch %24 + %24 = OpLabel + %200 = OpPhi %6 %201 %22 %202 %23 + OpStore %10 %200 + OpBranch %32 + + ; Can't replace all uses of the propagated instruction: %30 is + ; propagated into %27. + %32 = OpLabel + OpLoopMerge %28 %27 None + OpBranchConditional %13 %26 %28 + %26 = OpLabel + %25 = OpCopyObject %6 %7 + %30 = OpCopyObject %6 %25 + OpBranchConditional %13 %27 %28 + %27 = OpLabel + OpBranch %32 + %28 = OpLabel + %31 = OpPhi %6 %30 %26 %7 %32 ; Can't replace this use + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionDownTest, CantCreateOpPhiTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + + ; %5 doesn't belong to any construct + %5 = OpLabel + %15 = OpCopyObject %6 %7 + OpBranch %16 + + ; The merge block (%19) is unreachable + %16 = OpLabel + %17 = OpCopyObject %6 %7 + OpSelectionMerge %19 None + OpBranchConditional %13 %18 %18 + + ; %21 doesn't dominate the merge block - %20 + %18 = OpLabel + OpSelectionMerge %20 None + OpBranchConditional %13 %20 %21 + %21 = OpLabel + %22 = OpCopyObject %6 %7 + OpBranch %20 + + ; The merge block (%24) is an acceptable successor of the propagated + ; instruction's block + %20 = OpLabel + %23 = OpCopyObject %6 %7 + OpSelectionMerge %24 None + OpBranchConditional %13 %24 %30 + %30 = OpLabel + OpBranch %24 + + ; One of the predecessors of the merge block is not dominated by any + ; successor of the propagated instruction's block + %24 = OpLabel + %26 = OpCopyObject %6 %7 + OpLoopMerge %29 %25 None + OpBranch %25 + %25 = OpLabel + OpBranchConditional %13 %24 %29 + %28 = OpLabel ; unreachable predecessor of %29 + OpBranch %29 + %29 = OpLabel + OpReturn + + %19 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + TransformationPropagateInstructionDown transformations[] = { + // %5 doesn't belong to any construct. + {5, 200, {{{16, 201}}}}, + + // The merge block (%19) is unreachable. + {16, 200, {{{18, 202}}}}, + + // %21 doesn't dominate the merge block - %20. + {21, 200, {{{20, 203}}}}, + + // The merge block (%24) is an acceptable successor of the propagated + // instruction's block. + {20, 200, {{{24, 204}, {30, 205}}}}, + + // One of the predecessors of the merge block is not dominated by any + // successor of the propagated instruction's block. + {24, 200, {{{25, 206}}}}, + }; + + for (const auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + // No transformation has introduced an OpPhi instruction. + ASSERT_FALSE(context->get_def_use_mgr()->GetDef(200)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + + ; %5 doesn't belong to any construct + %5 = OpLabel + OpBranch %16 + + ; The merge block (%19) is unreachable + %16 = OpLabel + %201 = OpCopyObject %6 %7 + OpSelectionMerge %19 None + OpBranchConditional %13 %18 %18 + + ; %21 doesn't dominate the merge block - %20 + %18 = OpLabel + %202 = OpCopyObject %6 %7 + OpSelectionMerge %20 None + OpBranchConditional %13 %20 %21 + %21 = OpLabel + OpBranch %20 + + ; The merge block (%24) is an acceptable successor of the propagated + ; instruction's block + %20 = OpLabel + %203 = OpCopyObject %6 %7 + OpSelectionMerge %24 None + OpBranchConditional %13 %24 %30 + %30 = OpLabel + %205 = OpCopyObject %6 %7 + OpBranch %24 + + ; One of the predecessors of the merge block is not dominated by any + ; successor of the propagated instruction's block + %24 = OpLabel + %204 = OpCopyObject %6 %7 + OpLoopMerge %29 %25 None + OpBranch %25 + %25 = OpLabel + %206 = OpCopyObject %6 %7 + OpBranchConditional %13 %24 %29 + %28 = OpLabel ; unreachable predecessor of %29 + OpBranch %29 + %29 = OpLabel + OpReturn + + %19 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionDownTest, VariablePointersCapability) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %10 = OpTypePointer Workgroup %6 + %11 = OpVariable %10 Workgroup + %4 = OpFunction %2 None %3 + %5 = OpLabel + %18 = OpCopyObject %10 %11 + %14 = OpCopyObject %10 %11 + OpSelectionMerge %17 None + OpBranchConditional %13 %15 %16 + %15 = OpLabel + OpBranch %17 + %16 = OpLabel + OpBranch %17 + %17 = OpLabel + OpStore %18 %7 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + { + // Can propagate a pointer only if we don't have to create an OpPhi. + TransformationPropagateInstructionDown transformation( + 5, 200, {{{15, 201}, {16, 202}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + ASSERT_FALSE(context->get_def_use_mgr()->GetDef(200)); + } + { + // Can't propagate a pointer if there is no VariablePointersStorageBuffer + // capability and we need to create an OpPhi. + TransformationPropagateInstructionDown transformation( + 5, 200, {{{15, 203}, {16, 204}}}); + ASSERT_FALSE(context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + context->AddCapability(SpvCapabilityVariablePointers); + ASSERT_TRUE(context->get_feature_mgr()->HasCapability( + SpvCapabilityVariablePointersStorageBuffer)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %10 = OpTypePointer Workgroup %6 + %11 = OpVariable %10 Workgroup + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %17 None + OpBranchConditional %13 %15 %16 + %15 = OpLabel + %203 = OpCopyObject %10 %11 + %201 = OpCopyObject %10 %11 + OpBranch %17 + %16 = OpLabel + %204 = OpCopyObject %10 %11 + %202 = OpCopyObject %10 %11 + OpBranch %17 + %17 = OpLabel + %200 = OpPhi %10 %203 %15 %204 %16 + OpStore %200 %7 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionDownTest, UseOverflowIdsTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %10 = OpTypePointer Private %6 + %11 = OpVariable %10 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpCopyObject %6 %7 + OpSelectionMerge %23 None + OpBranchConditional %13 %21 %22 + %21 = OpLabel + OpStore %11 %20 + OpBranch %23 + %22 = OpLabel + OpStore %11 %20 + OpBranch %23 + %23 = OpLabel + OpStore %11 %20 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options, + MakeUnique(300)); + + TransformationPropagateInstructionDown transformation(5, 200, {{{21, 201}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context, + {300}); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %10 = OpTypePointer Private %6 + %11 = OpVariable %10 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %23 None + OpBranchConditional %13 %21 %22 + %21 = OpLabel + %201 = OpCopyObject %6 %7 + OpStore %11 %201 + OpBranch %23 + %22 = OpLabel + %300 = OpCopyObject %6 %7 + OpStore %11 %300 + OpBranch %23 + %23 = OpLabel + %200 = OpPhi %6 %201 %21 %300 %22 + OpStore %11 %200 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionDownTest, TestCreatedFacts) { + std::string shader = R"( + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %10 = OpTypePointer Private %6 + %11 = OpVariable %10 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpCopyObject %6 %7 + %24 = OpCopyObject %6 %7 ; Irrelevant id + %25 = OpCopyObject %10 %11 ; Pointee is irrelevant + OpSelectionMerge %23 None + OpBranchConditional %13 %21 %22 + %21 = OpLabel + OpStore %25 %20 + OpBranch %23 + %22 = OpLabel ; Dead block + OpStore %25 %20 + OpBranch %23 + %23 = OpLabel + OpStore %25 %20 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(22); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(24); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 25); + + { + // Propagate pointer with PointeeIsIrrelevant fact. + TransformationPropagateInstructionDown transformation( + 5, 200, {{{21, 201}, {22, 202}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(201)); + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(202)); + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(200)); + + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(201)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(202)); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(200)); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {}), MakeDataDescriptor(202, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {}), MakeDataDescriptor(200, {}))); + } + { + // Propagate an irrelevant id. + TransformationPropagateInstructionDown transformation( + 5, 203, {{{21, 204}, {22, 205}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(203)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(204)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(205)); + + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(203)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(204)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(205)); + + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(204, {}), MakeDataDescriptor(205, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(204, {}), MakeDataDescriptor(203, {}))); + } + { + // Propagate a regular id. + TransformationPropagateInstructionDown transformation( + 5, 206, {{{21, 207}, {22, 208}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(206)); + ASSERT_FALSE(transformation_context.GetFactManager()->IdIsIrrelevant(207)); + ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(208)); + + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(206)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(207)); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(208)); + + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(206, {}), MakeDataDescriptor(207, {}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(206, {}), MakeDataDescriptor(208, {}))); + } + + std::string after_transformation = R"( + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %10 = OpTypePointer Private %6 + %11 = OpVariable %10 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %23 None + OpBranchConditional %13 %21 %22 + %21 = OpLabel + %207 = OpCopyObject %6 %7 + %204 = OpCopyObject %6 %7 ; Irrelevant id + %201 = OpCopyObject %10 %11 ; Pointee is irrelevant + OpStore %201 %207 + OpBranch %23 + %22 = OpLabel ; Dead block + %208 = OpCopyObject %6 %7 + %205 = OpCopyObject %6 %7 ; Irrelevant id + %202 = OpCopyObject %10 %11 ; Pointee is irrelevant + OpStore %202 %208 + OpBranch %23 + %23 = OpLabel + %206 = OpPhi %6 %207 %21 %208 %22 + %203 = OpPhi %6 %204 %21 %205 %22 ; Irrelevant id + %200 = OpPhi %10 %201 %21 %202 %22 ; Pointee is irrelevant + OpStore %200 %206 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionDownTest, TestLoops1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %20 + + %20 = OpLabel + OpLoopMerge %26 %25 None + OpBranch %21 + + %21 = OpLabel + %22 = OpCopyObject %6 %7 + %31 = OpCopyObject %6 %7 + OpSelectionMerge %35 None + OpBranchConditional %13 %23 %24 + + %23 = OpLabel + %27 = OpCopyObject %6 %22 + %32 = OpCopyObject %6 %31 + OpBranch %26 + %24 = OpLabel + %28 = OpCopyObject %6 %22 + %33 = OpCopyObject %6 %31 + OpBranchConditional %13 %26 %25 + + %35 = OpLabel + OpBranch %25 + + %25 = OpLabel + %29 = OpCopyObject %6 %22 + %34 = OpCopyObject %6 %31 + OpBranch %20 + %26 = OpLabel + %30 = OpCopyObject %6 %22 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + { + TransformationPropagateInstructionDown transformation( + 21, 200, {{{23, 201}, {24, 202}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + // Can't replace usage of %22 in %26. + ASSERT_FALSE( + TransformationPropagateInstructionDown(21, 200, {{{23, 201}, {24, 202}}}) + .IsApplicable(context.get(), transformation_context)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %20 + + %20 = OpLabel + OpLoopMerge %26 %25 None + OpBranch %21 + + %21 = OpLabel + %22 = OpCopyObject %6 %7 + OpSelectionMerge %35 None + OpBranchConditional %13 %23 %24 + %23 = OpLabel + %201 = OpCopyObject %6 %7 + %27 = OpCopyObject %6 %22 + %32 = OpCopyObject %6 %201 + OpBranch %26 + %24 = OpLabel + %202 = OpCopyObject %6 %7 + %28 = OpCopyObject %6 %22 + %33 = OpCopyObject %6 %202 + OpBranchConditional %13 %26 %25 + + %35 = OpLabel + OpBranch %25 + + %25 = OpLabel + %29 = OpCopyObject %6 %22 + %34 = OpCopyObject %6 %202 + OpBranch %20 + %26 = OpLabel + %30 = OpCopyObject %6 %22 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionDownTest, TestLoops2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %20 + + %20 = OpLabel + %23 = OpPhi %6 %7 %5 %24 %21 + OpLoopMerge %22 %21 None + OpBranch %21 + + %21 = OpLabel + %24 = OpCopyObject %6 %23 + %25 = OpCopyObject %6 %7 + OpBranchConditional %13 %22 %20 + + %22 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + { + // Can propagate %25 from %21 into %20. + TransformationPropagateInstructionDown transformation( + 21, 200, {{{20, 201}, {22, 202}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + // Can propagate %201 from %20 into %21. + TransformationPropagateInstructionDown transformation(20, 200, + {{{21, 203}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + // Can't propagate %24 from %21 into %20. + ASSERT_FALSE( + TransformationPropagateInstructionDown(21, 200, {{{20, 204}, {22, 205}}}) + .IsApplicable(context.get(), transformation_context)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %20 + + %20 = OpLabel + %23 = OpPhi %6 %7 %5 %24 %21 + OpLoopMerge %22 %21 None + OpBranch %21 + + %21 = OpLabel + %203 = OpCopyObject %6 %7 + %24 = OpCopyObject %6 %23 + OpBranchConditional %13 %22 %20 + + %22 = OpLabel + %200 = OpPhi %6 %203 %21 + %202 = OpCopyObject %6 %7 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionDownTest, TestLoops3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %20 + + %20 = OpLabel + %27 = OpPhi %6 %7 %5 %26 %20 + %25 = OpCopyObject %6 %7 + %26 = OpCopyObject %6 %7 + OpLoopMerge %22 %20 None + OpBranchConditional %13 %20 %22 + + %22 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + { + // Propagate %25 into %20 and %22. Not that we are skipping %26 since not + // all of its users are in different blocks (%27).h + TransformationPropagateInstructionDown transformation( + 20, 200, {{{20, 201}, {22, 202}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 1 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %20 + + %20 = OpLabel + %27 = OpPhi %6 %7 %5 %26 %20 + %201 = OpCopyObject %6 %7 + %26 = OpCopyObject %6 %7 + OpLoopMerge %22 %20 None + OpBranchConditional %13 %20 %22 + + %22 = OpLabel + %202 = OpCopyObject %6 %7 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_propagate_instruction_up_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_propagate_instruction_up_test.cpp new file mode 100644 index 0000000..8a04270 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_propagate_instruction_up_test.cpp @@ -0,0 +1,952 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_propagate_instruction_up.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationPropagateInstructionUpTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %27 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %26 = OpVariable %27 Function + %13 = OpFOrdEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpFMod %6 %9 %17 + OpBranch %15 + + %19 = OpLabel + %22 = OpFAdd %6 %11 %20 + OpBranch %15 + + %15 = OpLabel + %21 = OpPhi %6 %18 %14 %22 %19 + %23 = OpFMul %6 %21 %21 + %24 = OpFDiv %6 %21 %23 + OpBranch %25 + + %25 = OpLabel + %28 = OpPhi %6 %20 %15 + OpStore %26 %28 + OpReturn + + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // |block_id| is invalid. + ASSERT_FALSE(TransformationPropagateInstructionUp(40, {{}}).IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(TransformationPropagateInstructionUp(26, {{}}).IsApplicable( + context.get(), transformation_context)); + + // |block_id| has no predecessors. + ASSERT_FALSE(TransformationPropagateInstructionUp(5, {{}}).IsApplicable( + context.get(), transformation_context)); + + // |block_id| has no valid instructions to propagate. + ASSERT_FALSE(TransformationPropagateInstructionUp(25, {{{15, 40}}}) + .IsApplicable(context.get(), transformation_context)); + + // Not all predecessors have fresh ids. + ASSERT_FALSE(TransformationPropagateInstructionUp(15, {{{19, 40}, {40, 41}}}) + .IsApplicable(context.get(), transformation_context)); + + // Not all ids are fresh. + ASSERT_FALSE( + TransformationPropagateInstructionUp(15, {{{19, 40}, {14, 14}, {40, 42}}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationPropagateInstructionUp(15, {{{19, 19}, {14, 40}, {40, 42}}}) + .IsApplicable(context.get(), transformation_context)); + + // Fresh ids have duplicates. + ASSERT_FALSE( + TransformationPropagateInstructionUp(15, {{{19, 40}, {14, 40}, {19, 41}}}) + .IsApplicable(context.get(), transformation_context)); + + // Valid transformations. + { + TransformationPropagateInstructionUp transformation(14, {{{5, 40}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + TransformationPropagateInstructionUp transformation(19, {{{5, 41}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %27 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %26 = OpVariable %27 Function + %13 = OpFOrdEqual %12 %9 %11 + %40 = OpFMod %6 %9 %17 ; propagated from %14 + %41 = OpFAdd %6 %11 %20 ; propagated from %19 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpPhi %6 %40 %5 ; propagated into %5 + OpBranch %15 + + %19 = OpLabel + %22 = OpPhi %6 %41 %5 ; propagated into %5 + OpBranch %15 + + %15 = OpLabel + %21 = OpPhi %6 %18 %14 %22 %19 + %23 = OpFMul %6 %21 %21 + %24 = OpFDiv %6 %21 %23 + OpBranch %25 + + %25 = OpLabel + %28 = OpPhi %6 %20 %15 + OpStore %26 %28 + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); + + { + TransformationPropagateInstructionUp transformation(15, + {{{14, 43}, {19, 44}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %27 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %26 = OpVariable %27 Function + %13 = OpFOrdEqual %12 %9 %11 + %40 = OpFMod %6 %9 %17 + %41 = OpFAdd %6 %11 %20 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpPhi %6 %40 %5 + %43 = OpFMul %6 %18 %18 ; propagated from %15 + OpBranch %15 + + %19 = OpLabel + %22 = OpPhi %6 %41 %5 + %44 = OpFMul %6 %22 %22 ; propagated from %15 + OpBranch %15 + + %15 = OpLabel + %23 = OpPhi %6 %43 %14 %44 %19 ; propagated into %14 and %19 + %21 = OpPhi %6 %18 %14 %22 %19 + %24 = OpFDiv %6 %21 %23 + OpBranch %25 + + %25 = OpLabel + %28 = OpPhi %6 %20 %15 + OpStore %26 %28 + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); + + { + TransformationPropagateInstructionUp transformation(15, + {{{14, 45}, {19, 46}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %27 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %26 = OpVariable %27 Function + %13 = OpFOrdEqual %12 %9 %11 + %40 = OpFMod %6 %9 %17 + %41 = OpFAdd %6 %11 %20 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpPhi %6 %40 %5 + %43 = OpFMul %6 %18 %18 + %45 = OpFDiv %6 %18 %43 ; propagated from %15 + OpBranch %15 + + %19 = OpLabel + %22 = OpPhi %6 %41 %5 + %44 = OpFMul %6 %22 %22 + %46 = OpFDiv %6 %22 %44 ; propagated from %15 + OpBranch %15 + + %15 = OpLabel + %24 = OpPhi %6 %45 %14 %46 %19 ; propagated into %14 and %19 + %23 = OpPhi %6 %43 %14 %44 %19 + %21 = OpPhi %6 %18 %14 %22 %19 + OpBranch %25 + + %25 = OpLabel + %28 = OpPhi %6 %20 %15 + OpStore %26 %28 + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionUpTest, BlockDominatesPredecessor1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %13 = OpFOrdEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpFMod %6 %9 %17 + OpBranch %15 + + %19 = OpLabel + %22 = OpFAdd %6 %11 %20 + OpBranch %15 + + %15 = OpLabel ; dominates %26 + %21 = OpPhi %6 %18 %14 %22 %19 %28 %26 + %23 = OpFMul %6 %21 %21 + %24 = OpFDiv %6 %21 %23 + OpLoopMerge %27 %26 None + OpBranch %26 + + %26 = OpLabel + %28 = OpFAdd %6 %24 %23 + OpBranch %15 + + %27 = OpLabel + OpReturn + + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationPropagateInstructionUp transformation( + 15, {{{14, 40}, {19, 41}, {26, 42}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %13 = OpFOrdEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpFMod %6 %9 %17 + %40 = OpFMul %6 %18 %18 ; propagated from %15 + OpBranch %15 + + %19 = OpLabel + %22 = OpFAdd %6 %11 %20 + %41 = OpFMul %6 %22 %22 ; propagated from %15 + OpBranch %15 + + %15 = OpLabel + %23 = OpPhi %6 %40 %14 %41 %19 %42 %26 ; propagated into %14, %19, %26 + %21 = OpPhi %6 %18 %14 %22 %19 %28 %26 + %24 = OpFDiv %6 %21 %23 + OpLoopMerge %27 %26 None + OpBranch %26 + + %26 = OpLabel + %28 = OpFAdd %6 %24 %23 + %42 = OpFMul %6 %28 %28 ; propagated from %15 + OpBranch %15 + + %27 = OpLabel + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionUpTest, BlockDominatesPredecessor2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %13 = OpFOrdEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpFMod %6 %9 %17 + OpBranch %15 + + %19 = OpLabel + %22 = OpFAdd %6 %11 %20 + OpBranch %15 + + %15 = OpLabel ; doesn't dominate %26 + %21 = OpPhi %6 %18 %14 %22 %19 %20 %26 + %23 = OpFMul %6 %21 %21 + %24 = OpFDiv %6 %21 %23 + OpLoopMerge %27 %26 None + OpBranch %27 + + %26 = OpLabel + OpBranch %15 + + %27 = OpLabel + OpReturn + + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationPropagateInstructionUp transformation( + 15, {{{14, 40}, {19, 41}, {26, 42}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %13 = OpFOrdEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpFMod %6 %9 %17 + %40 = OpFMul %6 %18 %18 ; propagated from %15 + OpBranch %15 + + %19 = OpLabel + %22 = OpFAdd %6 %11 %20 + %41 = OpFMul %6 %22 %22 ; propagated from %15 + OpBranch %15 + + %15 = OpLabel + %23 = OpPhi %6 %40 %14 %41 %19 %42 %26 ; propagated into %14, %19, %26 + %21 = OpPhi %6 %18 %14 %22 %19 %20 %26 + %24 = OpFDiv %6 %21 %23 + OpLoopMerge %27 %26 None + OpBranch %27 + + %26 = OpLabel + %42 = OpFMul %6 %20 %20 ; propagated from %15 + OpBranch %15 + + %27 = OpLabel + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionUpTest, BlockDominatesPredecessor3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %13 = OpFOrdEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpFMod %6 %9 %17 + OpBranch %15 + + %19 = OpLabel + %22 = OpFAdd %6 %11 %20 + OpBranch %15 + + %15 = OpLabel ; branches to itself + %21 = OpPhi %6 %18 %14 %22 %19 %24 %15 + %23 = OpFMul %6 %21 %21 + %24 = OpFDiv %6 %21 %23 + OpLoopMerge %27 %15 None + OpBranch %15 + + %27 = OpLabel + OpReturn + + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationPropagateInstructionUp transformation( + 15, {{{14, 40}, {19, 41}, {15, 42}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3.5 + %11 = OpConstant %6 3.4000001 + %12 = OpTypeBool + %17 = OpConstant %6 4 + %20 = OpConstant %6 45 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %13 = OpFOrdEqual %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %19 + + %14 = OpLabel + %18 = OpFMod %6 %9 %17 + %40 = OpFMul %6 %18 %18 ; propagated from %15 + OpBranch %15 + + %19 = OpLabel + %22 = OpFAdd %6 %11 %20 + %41 = OpFMul %6 %22 %22 ; propagated from %15 + OpBranch %15 + + %15 = OpLabel + %23 = OpPhi %6 %40 %14 %41 %19 %42 %15 ; propagated into %14, %19, %15 + %21 = OpPhi %6 %18 %14 %22 %19 %24 %15 + %24 = OpFDiv %6 %21 %23 + %42 = OpFMul %6 %24 %24 ; propagated from %15 + OpLoopMerge %27 %15 None + OpBranch %15 + + %27 = OpLabel + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionUpTest, + HandlesVariablePointersCapability) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %11 = OpConstant %6 23 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %9 + + %9 = OpLabel + %10 = OpCopyObject %7 %8 + OpStore %10 %11 + OpReturn + + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Required capabilities haven't yet been specified. + TransformationPropagateInstructionUp transformation(9, {{{5, 40}}}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + context->AddCapability(SpvCapabilityVariablePointers); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + OpCapability VariablePointers + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %11 = OpConstant %6 23 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %8 = OpVariable %7 Function + %40 = OpCopyObject %7 %8 ; propagated from %9 + OpBranch %9 + + %9 = OpLabel + %10 = OpPhi %7 %40 %5 ; propagated into %5 + OpStore %10 %11 + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionUpTest, + HandlesVariablePointersStorageBufferCapability) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %11 = OpConstant %6 23 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %8 = OpVariable %7 Function + OpBranch %9 + + %9 = OpLabel + %10 = OpCopyObject %7 %8 + OpStore %10 %11 + OpReturn + + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Required capabilities haven't yet been specified + TransformationPropagateInstructionUp transformation(9, {{{5, 40}}}); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + context->AddCapability(SpvCapabilityVariablePointersStorageBuffer); + + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + OpCapability VariablePointersStorageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %11 = OpConstant %6 23 + %7 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %8 = OpVariable %7 Function + %40 = OpCopyObject %7 %8 ; propagated from %9 + OpBranch %9 + + %9 = OpLabel + %10 = OpPhi %7 %40 %5 ; propagated into %5 + OpStore %10 %11 + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionUpTest, MultipleIdenticalPredecessors) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %11 = OpConstant %6 23 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %13 %9 %9 + + %9 = OpLabel + %14 = OpPhi %6 %11 %5 + %10 = OpCopyObject %6 %14 + OpReturn + + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationPropagateInstructionUp transformation(9, {{{5, 40}}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %11 = OpConstant %6 23 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %4 = OpFunction %2 None %3 + + %5 = OpLabel + %40 = OpCopyObject %6 %11 + OpSelectionMerge %9 None + OpBranchConditional %13 %9 %9 + + %9 = OpLabel + %10 = OpPhi %6 %40 %5 + %14 = OpPhi %6 %11 %5 + OpReturn + + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationPropagateInstructionUpTest, + InapplicableDueToOpTypeSampledImage) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %10 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %10 RelaxedPrecision + OpDecorate %10 DescriptorSet 0 + OpDecorate %10 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeImage %6 2D 0 0 0 1 Unknown + %8 = OpTypeSampledImage %7 + %9 = OpTypePointer UniformConstant %8 + %10 = OpVariable %9 UniformConstant + %12 = OpTypeVector %6 2 + %13 = OpConstant %6 0 + %14 = OpConstantComposite %12 %13 %13 + %15 = OpTypeVector %6 4 + %30 = OpTypeBool + %31 = OpConstantTrue %30 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpLoad %8 %10 + OpSelectionMerge %20 None + OpBranchConditional %31 %40 %41 + %40 = OpLabel + OpBranch %20 + %41 = OpLabel + OpBranch %20 + %20 = OpLabel + %50 = OpCopyObject %8 %11 + %16 = OpImageSampleImplicitLod %15 %50 %14 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE( + TransformationPropagateInstructionUp(20, {{{40, 100}, {41, 101}}}) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_push_id_through_variable_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_push_id_through_variable_test.cpp new file mode 100644 index 0000000..cd45c4c --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_push_id_through_variable_test.cpp @@ -0,0 +1,763 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_push_id_through_variable.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationPushIdThroughVariableTest, IsApplicable) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests the reference shader validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Tests |value_synonym_id| and |variable_id| are fresh ids. + uint32_t value_id = 21; + uint32_t value_synonym_id = 62; + uint32_t variable_id = 63; + uint32_t initializer_id = 23; + uint32_t variable_storage_class = SpvStorageClassPrivate; + auto instruction_descriptor = + MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + auto transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests |value_synonym_id| and |variable_id| are non-fresh ids. + value_id = 80; + value_synonym_id = 60; + variable_id = 61; + initializer_id = 80; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // The instruction to insert before is not defined. + value_id = 80; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 80; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(64, SpvOpAccessChain, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Attempting to insert the store and load instructions + // before an OpVariable instruction. + value_id = 24; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 24; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(27, SpvOpVariable, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // The block containing instruction descriptor must be reachable. + value_id = 80; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 80; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(100, SpvOpUnreachable, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests value instruction not available. + value_id = 64; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 23; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests pointer type not available. + value_id = 80; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 80; + variable_storage_class = SpvStorageClassPrivate; + instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests not a private nor function storage class. + value_id = 93; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 93; + variable_storage_class = SpvStorageClassInput; + instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); +#ifndef NDEBUG + ASSERT_DEATH( + transformation.IsApplicable(context.get(), transformation_context), + "The variable storage class must be private or function"); +#endif + + // Tests value instruction not available before instruction. + value_id = 95; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 80; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Variable initializer is not constant. + value_id = 95; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 95; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Variable initializer has wrong type. + value_id = 95; + value_synonym_id = 62; + variable_id = 63; + initializer_id = 93; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(40, SpvOpAccessChain, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationPushIdThroughVariableTest, Apply) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + OpStore %53 %21 + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + uint32_t value_id = 80; + uint32_t value_synonym_id = 100; + uint32_t variable_id = 101; + uint32_t initializer_id = 80; + uint32_t variable_storage_class = SpvStorageClassFunction; + auto instruction_descriptor = + MakeInstructionDescriptor(38, SpvOpAccessChain, 0); + auto transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + value_id = 21; + value_synonym_id = 102; + variable_id = 103; + initializer_id = 21; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + value_id = 95; + value_synonym_id = 104; + variable_id = 105; + initializer_id = 80; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + value_id = 80; + value_synonym_id = 106; + variable_id = 107; + initializer_id = 80; + variable_storage_class = SpvStorageClassFunction; + instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + value_id = 21; + value_synonym_id = 108; + variable_id = 109; + initializer_id = 21; + variable_storage_class = SpvStorageClassPrivate; + instruction_descriptor = MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + value_id = 23; + value_synonym_id = 110; + variable_id = 111; + initializer_id = 21; + variable_storage_class = SpvStorageClassPrivate; + instruction_descriptor = MakeInstructionDescriptor(27, SpvOpStore, 0); + transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 %109 %111 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %109 = OpVariable %51 Private %21 + %111 = OpVariable %51 Private %21 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %103 = OpVariable %15 Function %21 + %101 = OpVariable %9 Function %80 + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + OpStore %111 %23 + %110 = OpLoad %6 %111 + OpStore %53 %21 + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + OpStore %101 %80 + %100 = OpLoad %8 %101 + OpStore %103 %21 + %102 = OpLoad %6 %103 + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %107 = OpVariable %9 Function %80 + %105 = OpVariable %9 Function %80 + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpStore %105 %95 + %104 = OpLoad %8 %105 + OpStore %107 %80 + %106 = OpLoad %8 %107 + OpStore %109 %21 + %108 = OpLoad %6 %109 + OpReturnValue %21 + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(80, {}), MakeDataDescriptor(100, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(102, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(95, {}), MakeDataDescriptor(104, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(80, {}), MakeDataDescriptor(106, {}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(108, {}))); +} + +TEST(TransformationPushIdThroughVariableTest, AddSynonymsForRelevantIds) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests the reference shader validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + uint32_t value_id = 21; + uint32_t value_synonym_id = 62; + uint32_t variable_id = 63; + uint32_t initializer_id = 23; + uint32_t variable_storage_class = SpvStorageClassPrivate; + auto instruction_descriptor = + MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + auto transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(62, {}))); +} + +TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsForIrrelevantIds) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 + %13 = OpLabel + %46 = OpCopyObject %9 %11 + %16 = OpAccessChain %15 %11 %14 + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + %100 = OpLabel + OpUnreachable + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests the reference shader validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(21); + + uint32_t value_id = 21; + uint32_t value_synonym_id = 62; + uint32_t variable_id = 63; + uint32_t initializer_id = 23; + uint32_t variable_storage_class = SpvStorageClassPrivate; + auto instruction_descriptor = + MakeInstructionDescriptor(95, SpvOpReturnValue, 0); + auto transformation = TransformationPushIdThroughVariable( + value_id, value_synonym_id, variable_id, variable_storage_class, + initializer_id, instruction_descriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(21, {}), MakeDataDescriptor(62, {}))); +} + +TEST(TransformationPushIdThroughVariableTest, DontAddSynonymsInDeadBlocks) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstant %6 1 + %12 = OpConstantComposite %7 %10 %11 + %13 = OpTypeBool + %50 = OpTypePointer Function %13 + %14 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %16 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests the reference shader validity. + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + transformation_context.GetFactManager()->AddFactBlockIsDead(15); + auto transformation = TransformationPushIdThroughVariable( + 14, 100, 101, SpvStorageClassFunction, 14, + MakeInstructionDescriptor(15, SpvOpBranch, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(14, {}), MakeDataDescriptor(100, {}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_record_synonymous_constants_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_record_synonymous_constants_test.cpp new file mode 100644 index 0000000..010cb12 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_record_synonymous_constants_test.cpp @@ -0,0 +1,884 @@ +// Copyright (c) 2020 Stefano Milizia +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_record_synonymous_constants.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +// Apply the TransformationRecordSynonymousConstants defined by the given +// constant1_id and constant2_id and check that the fact that the two +// constants are synonym is recorded. +void ApplyTransformationAndCheckFactManager( + uint32_t constant1_id, uint32_t constant2_id, opt::IRContext* ir_context, + TransformationContext* transformation_context) { + ApplyAndCheckFreshIds( + TransformationRecordSynonymousConstants(constant1_id, constant2_id), + ir_context, transformation_context); + + ASSERT_TRUE(transformation_context->GetFactManager()->IsSynonymous( + MakeDataDescriptor(constant1_id, {}), + MakeDataDescriptor(constant2_id, {}))); +} + +TEST(TransformationRecordSynonymousConstantsTest, IntConstants) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %17 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %12 "c" + OpName %17 "color" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %17 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %19 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %18 = OpConstant %6 0 + %11 = OpConstantNull %6 + %13 = OpConstant %6 1 + %20 = OpConstant %19 1 + %21 = OpConstant %19 -1 + %22 = OpConstant %6 1 + %14 = OpTypeFloat 32 + %15 = OpTypeVector %14 4 + %16 = OpTypePointer Output %15 + %17 = OpVariable %16 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %13 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %3 is not a constant declaration + ASSERT_FALSE(TransformationRecordSynonymousConstants(3, 9).IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 3).IsApplicable( + context.get(), transformation_context)); + + // The two constants must be different + ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 9).IsApplicable( + context.get(), transformation_context)); + + // %9 and %13 are not equivalent + ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 13).IsApplicable( + context.get(), transformation_context)); + + // Swapping the ids gives the same result + ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 9).IsApplicable( + context.get(), transformation_context)); + + // %11 and %13 are not equivalent + ASSERT_FALSE(TransformationRecordSynonymousConstants(11, 13).IsApplicable( + context.get(), transformation_context)); + + // Swapping the ids gives the same result + ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 11).IsApplicable( + context.get(), transformation_context)); + + // %20 and %21 have different values + ASSERT_FALSE(TransformationRecordSynonymousConstants(20, 21).IsApplicable( + context.get(), transformation_context)); + + // %13 and %22 are equal and thus equivalent (having the same value and type) + ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 22).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(13, 22, context.get(), + &transformation_context); + + // %13 and %20 are equal even if %13 is signed and %20 is unsigned + ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 20).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(13, 20, context.get(), + &transformation_context); + + // %9 and %11 are equivalent (OpConstant with value 0 and OpConstantNull) + ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 11).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(9, 11, context.get(), + &transformation_context); + + // Swapping the ids gives the same result + ASSERT_TRUE(TransformationRecordSynonymousConstants(11, 9).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(11, 9, context.get(), + &transformation_context); +} + +TEST(TransformationRecordSynonymousConstantsTest, BoolConstants) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %19 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "b" + OpName %19 "color" + OpDecorate %19 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantFalse %6 + %20 = OpConstantNull %6 + %11 = OpConstantTrue %6 + %21 = OpConstantFalse %6 + %22 = OpConstantTrue %6 + %16 = OpTypeFloat 32 + %17 = OpTypeVector %16 4 + %18 = OpTypePointer Output %17 + %19 = OpVariable %18 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLogicalEqual %6 %10 %11 + OpSelectionMerge %14 None + OpBranchConditional %12 %13 %14 + %13 = OpLabel + OpReturn + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %9 and %11 are not equivalent + ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 11).IsApplicable( + context.get(), transformation_context)); + + // %20 and %11 are not equivalent + ASSERT_FALSE(TransformationRecordSynonymousConstants(20, 11).IsApplicable( + context.get(), transformation_context)); + + // %9 and %21 are equivalent (both OpConstantFalse) + ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 21).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(9, 21, context.get(), + &transformation_context); + + // %11 and %22 are equivalent (both OpConstantTrue) + ASSERT_TRUE(TransformationRecordSynonymousConstants(11, 22).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(11, 22, context.get(), + &transformation_context); + + // %9 and %20 are equivalent (OpConstantFalse and boolean OpConstantNull) + ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 20).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(9, 20, context.get(), + &transformation_context); +} + +TEST(TransformationRecordSynonymousConstantsTest, FloatConstants) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %22 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %12 "c" + OpName %22 "color" + OpDecorate %22 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstantNull %6 + %13 = OpConstant %6 2 + %26 = OpConstant %6 2 + %16 = OpTypeBool + %20 = OpTypeVector %6 4 + %21 = OpTypePointer Output %20 + %22 = OpVariable %21 Output + %23 = OpConstantComposite %20 %9 %11 %9 %11 + %25 = OpConstantComposite %20 %11 %9 %9 %11 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %13 + %14 = OpLoad %6 %8 + %15 = OpLoad %6 %10 + %17 = OpFOrdEqual %16 %14 %15 + OpSelectionMerge %19 None + OpBranchConditional %17 %18 %24 + %18 = OpLabel + OpStore %22 %23 + OpBranch %19 + %24 = OpLabel + OpStore %22 %25 + OpBranch %19 + %19 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %9 and %13 are not equivalent + ASSERT_FALSE(TransformationRecordSynonymousConstants(9, 13).IsApplicable( + context.get(), transformation_context)); + + // %11 and %13 are not equivalent + ASSERT_FALSE(TransformationRecordSynonymousConstants(11, 13).IsApplicable( + context.get(), transformation_context)); + + // %13 and %23 are not equivalent + ASSERT_FALSE(TransformationRecordSynonymousConstants(13, 23).IsApplicable( + context.get(), transformation_context)); + + // %13 and %26 are identical float constants + ASSERT_TRUE(TransformationRecordSynonymousConstants(13, 26).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(13, 26, context.get(), + &transformation_context); + + // %9 and %11 are equivalent () + ASSERT_TRUE(TransformationRecordSynonymousConstants(9, 11).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(9, 11, context.get(), + &transformation_context); +} + +TEST(TransformationRecordSynonymousConstantsTest, + VectorAndMatrixCompositeConstants) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %24 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %12 "d" + OpName %16 "e" + OpName %24 "color" + OpDecorate %12 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %24 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %28 = OpConstant %6 0 + %30 = OpConstant %6 1 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 0 + %14 = OpTypeBool + %15 = OpTypePointer Function %14 + %17 = OpConstantFalse %14 + %22 = OpTypeVector %6 4 + %37 = OpTypeVector %6 3 + %32 = OpTypeMatrix %22 2 + %39 = OpTypeMatrix %22 3 + %23 = OpTypePointer Output %22 + %24 = OpVariable %23 Output + %25 = OpConstantComposite %22 %9 %9 %9 %9 + %27 = OpConstantNull %22 + %29 = OpConstantComposite %22 %9 %28 %28 %9 + %31 = OpConstantComposite %22 %30 %9 %9 %9 + %38 = OpConstantComposite %37 %9 %9 %9 + %33 = OpConstantComposite %32 %25 %29 + %34 = OpConstantComposite %32 %27 %25 + %35 = OpConstantNull %32 + %36 = OpConstantComposite %32 %31 %25 + %40 = OpConstantComposite %39 %25 %25 %25 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %16 = OpVariable %15 Function + OpStore %8 %9 + OpStore %12 %13 + OpStore %16 %17 + %18 = OpLoad %10 %12 + %19 = OpIEqual %14 %18 %13 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %26 + %20 = OpLabel + OpStore %24 %25 + OpBranch %21 + %26 = OpLabel + OpStore %24 %25 + OpBranch %21 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %25 and %27 are equivalent (25 is zero-like, 27 is null) + ASSERT_TRUE(TransformationRecordSynonymousConstants(25, 27).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(25, 27, context.get(), + &transformation_context); + + // %25 and %29 are equivalent (same type and value) + ASSERT_TRUE(TransformationRecordSynonymousConstants(25, 29).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(25, 29, context.get(), + &transformation_context); + + // %27 and %29 are equivalent (27 is null, 29 is zero-like) + ASSERT_TRUE(TransformationRecordSynonymousConstants(27, 29).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(27, 29, context.get(), + &transformation_context); + + // %25 and %31 are not equivalent (they have different values) + ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 31).IsApplicable( + context.get(), transformation_context)); + + // %27 and %31 are not equivalent (27 is null, 31 is not zero-like) + ASSERT_FALSE(TransformationRecordSynonymousConstants(27, 31).IsApplicable( + context.get(), transformation_context)); + + // %25 and %38 are not equivalent (they have different sizes) + ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 38).IsApplicable( + context.get(), transformation_context)); + + // %35 and %36 are not equivalent (35 is null, 36 has non-zero components) + ASSERT_FALSE(TransformationRecordSynonymousConstants(35, 36).IsApplicable( + context.get(), transformation_context)); + + // %33 and %36 are not equivalent (not all components are equivalent) + ASSERT_FALSE(TransformationRecordSynonymousConstants(33, 36).IsApplicable( + context.get(), transformation_context)); + + // %33 and %40 are not equivalent (they have different sizes) + ASSERT_FALSE(TransformationRecordSynonymousConstants(33, 40).IsApplicable( + context.get(), transformation_context)); + + // %33 and %34 are equivalent (same type, equivalent components) + ASSERT_TRUE(TransformationRecordSynonymousConstants(33, 34).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(33, 34, context.get(), + &transformation_context); + + // %33 and %35 are equivalent (33 has zero-valued components, 35 is null) + ASSERT_TRUE(TransformationRecordSynonymousConstants(33, 35).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(33, 35, context.get(), + &transformation_context); +} + +TEST(TransformationRecordSynonymousConstantsTest, StructCompositeConstants) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %24 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %12 "d" + OpName %16 "e" + OpName %24 "color" + OpDecorate %12 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %24 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %28 = OpConstant %6 0 + %30 = OpConstant %6 1 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 0 + %33 = OpConstantNull %10 + %14 = OpTypeBool + %15 = OpTypePointer Function %14 + %17 = OpConstantFalse %14 + %34 = OpConstantNull %14 + %22 = OpTypeVector %6 4 + %32 = OpTypeStruct %22 %10 %14 %6 + %38 = OpTypeStruct %6 %6 %6 %6 + %23 = OpTypePointer Output %22 + %24 = OpVariable %23 Output + %25 = OpConstantComposite %22 %9 %9 %9 %9 + %27 = OpConstantNull %22 + %29 = OpConstantComposite %22 %9 %28 %28 %9 + %31 = OpConstantComposite %22 %30 %9 %9 %9 + %35 = OpConstantComposite %32 %25 %13 %17 %9 + %36 = OpConstantComposite %32 %27 %33 %34 %28 + %37 = OpConstantComposite %32 %31 %13 %17 %9 + %39 = OpConstantComposite %38 %9 %9 %9 %9 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %16 = OpVariable %15 Function + OpStore %8 %9 + OpStore %12 %13 + OpStore %16 %17 + %18 = OpLoad %10 %12 + %19 = OpIEqual %14 %18 %13 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %26 + %20 = OpLabel + OpStore %24 %25 + OpBranch %21 + %26 = OpLabel + OpStore %24 %25 + OpBranch %21 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %29 and %35 are not equivalent (they have different types) + ASSERT_FALSE(TransformationRecordSynonymousConstants(29, 35).IsApplicable( + context.get(), transformation_context)); + + // %35 and %37 are not equivalent (their first components are not equivalent) + ASSERT_FALSE(TransformationRecordSynonymousConstants(35, 37).IsApplicable( + context.get(), transformation_context)); + + // %35 and %36 are equivalent (all their components are equivalent) + ASSERT_TRUE(TransformationRecordSynonymousConstants(35, 36).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(35, 36, context.get(), + &transformation_context); + + // %25 and %39 are not equivalent (they have different types) + ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 39).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationRecordSynonymousConstantsTest, ArrayCompositeConstants) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %24 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %12 "d" + OpName %16 "e" + OpName %24 "color" + OpDecorate %12 RelaxedPrecision + OpDecorate %18 RelaxedPrecision + OpDecorate %24 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %38 = OpConstant %6 1 + %10 = OpTypeInt 32 1 + %11 = OpTypePointer Function %10 + %13 = OpConstant %10 0 + %27 = OpConstant %10 4 + %39 = OpConstant %10 2 + %14 = OpTypeBool + %15 = OpTypePointer Function %14 + %17 = OpConstantFalse %14 + %22 = OpTypeVector %6 4 + %28 = OpTypeArray %6 %27 + %29 = OpTypeArray %28 %27 + %40 = OpTypeArray %6 %39 + %23 = OpTypePointer Output %22 + %24 = OpVariable %23 Output + %25 = OpConstantComposite %22 %9 %9 %9 %9 + %31 = OpConstantComposite %28 %9 %9 %9 %9 + %41 = OpConstantComposite %40 %9 %9 + %32 = OpConstantComposite %28 %38 %9 %9 %9 + %33 = OpConstantNull %28 + %34 = OpConstantComposite %29 %31 %33 %31 %33 + %35 = OpConstantComposite %29 %33 %31 %33 %31 + %36 = OpConstantNull %29 + %37 = OpConstantComposite %29 %32 %33 %31 %33 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %12 = OpVariable %11 Function + %16 = OpVariable %15 Function + OpStore %8 %9 + OpStore %12 %13 + OpStore %16 %17 + %18 = OpLoad %10 %12 + %19 = OpIEqual %14 %18 %13 + OpSelectionMerge %21 None + OpBranchConditional %19 %20 %26 + %20 = OpLabel + OpStore %24 %25 + OpBranch %21 + %26 = OpLabel + OpStore %24 %25 + OpBranch %21 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %25 and %31 are not equivalent (they have different types) + ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 31).IsApplicable( + context.get(), transformation_context)); + + // %25 and %41 are not equivalent (they have different sizes) + ASSERT_FALSE(TransformationRecordSynonymousConstants(25, 41).IsApplicable( + context.get(), transformation_context)); + + // %31 and %32 are not equivalent (their components are not pairwise + // equivalent) + ASSERT_FALSE(TransformationRecordSynonymousConstants(31, 32).IsApplicable( + context.get(), transformation_context)); + + // %31 and %33 are equivalent (%31 has zero-valued components, 32 is null) + ASSERT_TRUE(TransformationRecordSynonymousConstants(31, 33).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(31, 33, context.get(), + &transformation_context); + + // %34 and %35 are equivalent (same type, equivalent components) + ASSERT_TRUE(TransformationRecordSynonymousConstants(34, 35).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(34, 35, context.get(), + &transformation_context); + + // %35 and %36 are equivalent (%36 is null, %35 has zero-valued components) + ASSERT_TRUE(TransformationRecordSynonymousConstants(35, 36).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(35, 36, context.get(), + &transformation_context); + + // %34 and %37 are not equivalent (they have non-equivalent components) + ASSERT_FALSE(TransformationRecordSynonymousConstants(34, 37).IsApplicable( + context.get(), transformation_context)); + + // %36 and %37 are not equivalent (36 is null, 37 does not have all-zero + // components) + ASSERT_FALSE(TransformationRecordSynonymousConstants(36, 37).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationRecordSynonymousConstantsTest, IntVectors) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %3 Location 0 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpTypeVector %6 4 + %9 = OpTypeVector %7 4 + %10 = OpTypePointer Function %8 + %11 = OpTypePointer Function %8 + %12 = OpConstant %6 0 + %13 = OpConstant %7 0 + %14 = OpConstant %6 1 + %25 = OpConstant %7 1 + %15 = OpConstantComposite %8 %12 %12 %12 %12 + %16 = OpConstantComposite %9 %13 %13 %13 %13 + %17 = OpConstantComposite %8 %14 %12 %12 %14 + %18 = OpConstantComposite %9 %25 %13 %13 %25 + %19 = OpConstantNull %8 + %20 = OpConstantNull %9 + %21 = OpTypeFloat 32 + %22 = OpTypeVector %21 4 + %23 = OpTypePointer Output %22 + %3 = OpVariable %23 Output + %2 = OpFunction %4 None %5 + %24 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %15 and %17 are not equivalent (having non-equivalent components) + ASSERT_FALSE(TransformationRecordSynonymousConstants(15, 17).IsApplicable( + context.get(), transformation_context)); + + // %17 and %19 are not equivalent (%19 is null, %17 is non-zero) + ASSERT_FALSE(TransformationRecordSynonymousConstants(17, 19).IsApplicable( + context.get(), transformation_context)); + + // %17 and %20 are not equivalent (%19 is null, %20 is non-zero) + ASSERT_FALSE(TransformationRecordSynonymousConstants(17, 20).IsApplicable( + context.get(), transformation_context)); + + // %15 and %16 are equivalent (having pairwise equivalent components) + ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 16).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(15, 16, context.get(), + &transformation_context); + + // %17 and %18 are equivalent (having pairwise equivalent components) + ASSERT_TRUE(TransformationRecordSynonymousConstants(17, 18).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(17, 18, context.get(), + &transformation_context); + + // %19 and %20 are equivalent (both null vectors with compatible types) + ASSERT_TRUE(TransformationRecordSynonymousConstants(19, 20).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(19, 20, context.get(), + &transformation_context); + + // %15 and %19 are equivalent (they have compatible types, %15 is zero-like + // and %19 is null) + ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 19).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(15, 19, context.get(), + &transformation_context); + + // %15 and %20 are equivalent (they have compatible types, %15 is zero-like + // and %20 is null) + ASSERT_TRUE(TransformationRecordSynonymousConstants(15, 20).IsApplicable( + context.get(), transformation_context)); + + ApplyTransformationAndCheckFactManager(15, 20, context.get(), + &transformation_context); +} + +TEST(TransformationRecordSynonymousConstantsTest, FirstIrrelevantConstant) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpConstant %6 23 + %8 = OpConstant %6 23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(TransformationRecordSynonymousConstants(7, 8).IsApplicable( + context.get(), transformation_context)); + + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(7); + ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationRecordSynonymousConstantsTest, SecondIrrelevantConstant) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpConstant %6 23 + %8 = OpConstant %6 23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(TransformationRecordSynonymousConstants(7, 8).IsApplicable( + context.get(), transformation_context)); + + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(8); + ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable( + context.get(), transformation_context)); +} + +TEST(TransformationRecordSynonymousConstantsTest, InvalidIds) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpConstant %6 23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationRecordSynonymousConstants(7, 8).IsApplicable( + context.get(), transformation_context)); + + ASSERT_FALSE(TransformationRecordSynonymousConstants(8, 7).IsApplicable( + context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_add_sub_mul_with_carrying_extended_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_add_sub_mul_with_carrying_extended_test.cpp new file mode 100644 index 0000000..5bc2a8e --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_add_sub_mul_with_carrying_extended_test.cpp @@ -0,0 +1,618 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_add_sub_mul_with_carrying_extended.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest, + NotApplicableBasicChecks) { + // First conditions in IsApplicable() are checked. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %12 "i3" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %13 = OpLoad %6 %10 + %14 = OpLoad %6 %8 + %15 = OpSDiv %6 %13 %14 + OpStore %12 %15 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: |struct_fresh_id| must be fresh. + auto transformation_bad_1 = + TransformationReplaceAddSubMulWithCarryingExtended(14, 15); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: The transformation cannot be applied to an instruction OpSDiv. + auto transformation_bad_2 = + TransformationReplaceAddSubMulWithCarryingExtended(20, 15); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: The transformation cannot be applied to an nonexistent instruction. + auto transformation_bad_3 = + TransformationReplaceAddSubMulWithCarryingExtended(20, 21); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest, + NotApplicableDifferingSignedTypes) { + // Operand types and result types do not match. Not applicable to an operation + // on vectors with signed integers and operation on signed integers. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i1" + OpName %10 "i2" + OpName %16 "v1" + OpName %20 "v2" + OpName %25 "v3" + OpName %31 "u1" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %14 = OpTypeVector %6 3 + %15 = OpTypePointer Function %14 + %17 = OpConstant %6 0 + %18 = OpConstant %6 2 + %19 = OpConstantComposite %14 %17 %9 %18 + %21 = OpConstant %6 3 + %22 = OpConstant %6 4 + %23 = OpConstant %6 5 + %24 = OpConstantComposite %14 %21 %22 %23 + %29 = OpTypeInt 32 0 + %30 = OpTypePointer Function %29 + %32 = OpConstant %29 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %16 = OpVariable %15 Function + %20 = OpVariable %15 Function + %25 = OpVariable %15 Function + %31 = OpVariable %30 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + %12 = OpLoad %6 %8 + %13 = OpISub %6 %11 %12 + OpStore %10 %13 + OpStore %16 %19 + OpStore %20 %24 + %26 = OpLoad %14 %16 + %27 = OpLoad %14 %20 + %28 = OpIAdd %14 %26 %27 + OpStore %25 %28 + OpStore %31 %32 + %40 = OpIMul %6 %32 %18 + %41 = OpIAdd %6 %32 %32 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Bad: The transformation cannot be applied to an instruction OpIMul that has + // different signedness of the types of operands. + auto transformation_bad_1 = + TransformationReplaceAddSubMulWithCarryingExtended(50, 40); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: The transformation cannot be applied to an instruction OpIAdd that has + // different signedness of the result type than the signedness of the types of + // the operands. + auto transformation_bad_2 = + TransformationReplaceAddSubMulWithCarryingExtended(50, 41); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: The transformation cannot be applied to the instruction OpIAdd of two + // vectors that have signed components. + auto transformation_bad_3 = + TransformationReplaceAddSubMulWithCarryingExtended(50, 28); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); + + // Bad: The transformation cannot be applied to the instruction OpISub of two + // signed integers + auto transformation_bad_4 = + TransformationReplaceAddSubMulWithCarryingExtended(50, 13); + ASSERT_FALSE( + transformation_bad_4.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest, + NotApplicableMissingStructTypes) { + // In all cases the required struct types are missing. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "u1" + OpName %10 "u2" + OpName %12 "u3" + OpName %24 "i1" + OpName %26 "i2" + OpName %28 "i3" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %22 = OpTypeInt 32 1 + %23 = OpTypePointer Function %22 + %25 = OpConstant %22 1 + %27 = OpConstant %22 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %24 = OpVariable %23 Function + %26 = OpVariable %23 Function + %28 = OpVariable %23 Function + OpStore %8 %9 + OpStore %10 %11 + %13 = OpLoad %6 %8 + %14 = OpLoad %6 %10 + %15 = OpIAdd %6 %13 %14 + OpStore %12 %15 + %16 = OpLoad %6 %8 + %17 = OpLoad %6 %10 + %18 = OpISub %6 %16 %17 + OpStore %12 %18 + %19 = OpLoad %6 %8 + %20 = OpLoad %6 %10 + %21 = OpIMul %6 %19 %20 + OpStore %12 %21 + OpStore %24 %25 + OpStore %26 %27 + %29 = OpLoad %22 %24 + %30 = OpLoad %22 %26 + %31 = OpIMul %22 %29 %30 + OpStore %28 %31 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation_bad_1 = + TransformationReplaceAddSubMulWithCarryingExtended(50, 15); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + auto transformation_bad_2 = + TransformationReplaceAddSubMulWithCarryingExtended(50, 18); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: The transformation cannot be applied to the instruction OpIAdd of two + // vectors that have signed components. + auto transformation_bad_3 = + TransformationReplaceAddSubMulWithCarryingExtended(50, 21); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); + + // Bad: The transformation cannot be applied to the instruction OpISub of two + // signed integers + auto transformation_bad_4 = + TransformationReplaceAddSubMulWithCarryingExtended(50, 31); + ASSERT_FALSE( + transformation_bad_4.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceAddSubMulWithCarryingExtendedTest, + ApplicableScenarios) { + // In this test all of the transformations can be applied. The required struct + // types are provided. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "u1" + OpName %10 "u2" + OpName %12 "u3" + OpName %24 "i1" + OpName %26 "i2" + OpName %28 "i3" + OpName %34 "uv1" + OpName %36 "uv2" + OpName %39 "uv3" + OpName %51 "v1" + OpName %53 "v2" + OpName %56 "v3" + OpName %60 "pair_uint" + OpMemberName %60 0 "u_1" + OpMemberName %60 1 "u_2" + OpName %62 "p_uint" + OpName %63 "pair_uvec2" + OpMemberName %63 0 "uv_1" + OpMemberName %63 1 "uv_2" + OpName %65 "p_uvec2" + OpName %66 "pair_ivec2" + OpMemberName %66 0 "v_1" + OpMemberName %66 1 "v_2" + OpName %68 "p_ivec2" + OpName %69 "pair_int" + OpMemberName %69 0 "i_1" + OpMemberName %69 1 "i_2" + OpName %71 "p_int" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %22 = OpTypeInt 32 1 + %23 = OpTypePointer Function %22 + %25 = OpConstant %22 1 + %27 = OpConstant %22 2 + %32 = OpTypeVector %6 2 + %33 = OpTypePointer Function %32 + %35 = OpConstantComposite %32 %9 %11 + %37 = OpConstant %6 3 + %38 = OpConstantComposite %32 %11 %37 + %49 = OpTypeVector %22 2 + %50 = OpTypePointer Function %49 + %52 = OpConstantComposite %49 %25 %27 + %54 = OpConstant %22 3 + %55 = OpConstantComposite %49 %27 %54 + %60 = OpTypeStruct %6 %6 + %61 = OpTypePointer Private %60 + %62 = OpVariable %61 Private + %63 = OpTypeStruct %32 %32 + %64 = OpTypePointer Private %63 + %65 = OpVariable %64 Private + %66 = OpTypeStruct %49 %49 + %67 = OpTypePointer Private %66 + %68 = OpVariable %67 Private + %69 = OpTypeStruct %22 %22 + %70 = OpTypePointer Private %69 + %71 = OpVariable %70 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %24 = OpVariable %23 Function + %26 = OpVariable %23 Function + %28 = OpVariable %23 Function + %34 = OpVariable %33 Function + %36 = OpVariable %33 Function + %39 = OpVariable %33 Function + %51 = OpVariable %50 Function + %53 = OpVariable %50 Function + %56 = OpVariable %50 Function + OpStore %8 %9 + OpStore %10 %11 + %13 = OpLoad %6 %8 + %14 = OpLoad %6 %10 + %15 = OpIAdd %6 %13 %14 + OpStore %12 %15 + %16 = OpLoad %6 %8 + %17 = OpLoad %6 %10 + %18 = OpISub %6 %16 %17 + OpStore %12 %18 + %19 = OpLoad %6 %8 + %20 = OpLoad %6 %10 + %21 = OpIMul %6 %19 %20 + OpStore %12 %21 + OpStore %24 %25 + OpStore %26 %27 + %29 = OpLoad %22 %24 + %30 = OpLoad %22 %26 + %31 = OpIMul %22 %29 %30 + OpStore %28 %31 + OpStore %34 %35 + OpStore %36 %38 + %40 = OpLoad %32 %34 + %41 = OpLoad %32 %36 + %42 = OpIAdd %32 %40 %41 + OpStore %39 %42 + %43 = OpLoad %32 %34 + %44 = OpLoad %32 %36 + %45 = OpISub %32 %43 %44 + OpStore %39 %45 + %46 = OpLoad %32 %34 + %47 = OpLoad %32 %36 + %48 = OpIMul %32 %46 %47 + OpStore %39 %48 + OpStore %51 %52 + OpStore %53 %55 + %57 = OpLoad %49 %51 + %58 = OpLoad %49 %53 + %59 = OpIMul %49 %57 %58 + OpStore %56 %59 + OpReturn + OpFunctionEnd + )"; + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation_good_1 = + TransformationReplaceAddSubMulWithCarryingExtended(80, 15); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_good_2 = + TransformationReplaceAddSubMulWithCarryingExtended(81, 18); + ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_good_3 = + TransformationReplaceAddSubMulWithCarryingExtended(82, 21); + ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_3, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_good_4 = + TransformationReplaceAddSubMulWithCarryingExtended(83, 31); + ASSERT_TRUE(transformation_good_4.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_4, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_good_5 = + TransformationReplaceAddSubMulWithCarryingExtended(84, 42); + ASSERT_TRUE(transformation_good_5.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_5, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_good_6 = + TransformationReplaceAddSubMulWithCarryingExtended(85, 45); + ASSERT_TRUE(transformation_good_6.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_6, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_good_7 = + TransformationReplaceAddSubMulWithCarryingExtended(86, 48); + ASSERT_TRUE(transformation_good_7.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_7, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_good_8 = + TransformationReplaceAddSubMulWithCarryingExtended(87, 59); + ASSERT_TRUE(transformation_good_8.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_8, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "u1" + OpName %10 "u2" + OpName %12 "u3" + OpName %24 "i1" + OpName %26 "i2" + OpName %28 "i3" + OpName %34 "uv1" + OpName %36 "uv2" + OpName %39 "uv3" + OpName %51 "v1" + OpName %53 "v2" + OpName %56 "v3" + OpName %60 "pair_uint" + OpMemberName %60 0 "u_1" + OpMemberName %60 1 "u_2" + OpName %62 "p_uint" + OpName %63 "pair_uvec2" + OpMemberName %63 0 "uv_1" + OpMemberName %63 1 "uv_2" + OpName %65 "p_uvec2" + OpName %66 "pair_ivec2" + OpMemberName %66 0 "v_1" + OpMemberName %66 1 "v_2" + OpName %68 "p_ivec2" + OpName %69 "pair_int" + OpMemberName %69 0 "i_1" + OpMemberName %69 1 "i_2" + OpName %71 "p_int" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 0 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %22 = OpTypeInt 32 1 + %23 = OpTypePointer Function %22 + %25 = OpConstant %22 1 + %27 = OpConstant %22 2 + %32 = OpTypeVector %6 2 + %33 = OpTypePointer Function %32 + %35 = OpConstantComposite %32 %9 %11 + %37 = OpConstant %6 3 + %38 = OpConstantComposite %32 %11 %37 + %49 = OpTypeVector %22 2 + %50 = OpTypePointer Function %49 + %52 = OpConstantComposite %49 %25 %27 + %54 = OpConstant %22 3 + %55 = OpConstantComposite %49 %27 %54 + %60 = OpTypeStruct %6 %6 + %61 = OpTypePointer Private %60 + %62 = OpVariable %61 Private + %63 = OpTypeStruct %32 %32 + %64 = OpTypePointer Private %63 + %65 = OpVariable %64 Private + %66 = OpTypeStruct %49 %49 + %67 = OpTypePointer Private %66 + %68 = OpVariable %67 Private + %69 = OpTypeStruct %22 %22 + %70 = OpTypePointer Private %69 + %71 = OpVariable %70 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %24 = OpVariable %23 Function + %26 = OpVariable %23 Function + %28 = OpVariable %23 Function + %34 = OpVariable %33 Function + %36 = OpVariable %33 Function + %39 = OpVariable %33 Function + %51 = OpVariable %50 Function + %53 = OpVariable %50 Function + %56 = OpVariable %50 Function + OpStore %8 %9 + OpStore %10 %11 + %13 = OpLoad %6 %8 + %14 = OpLoad %6 %10 + %80 = OpIAddCarry %60 %13 %14 + %15 = OpCompositeExtract %6 %80 0 + OpStore %12 %15 + %16 = OpLoad %6 %8 + %17 = OpLoad %6 %10 + %81 = OpISubBorrow %60 %16 %17 + %18 = OpCompositeExtract %6 %81 0 + OpStore %12 %18 + %19 = OpLoad %6 %8 + %20 = OpLoad %6 %10 + %82 = OpUMulExtended %60 %19 %20 + %21 = OpCompositeExtract %6 %82 0 + OpStore %12 %21 + OpStore %24 %25 + OpStore %26 %27 + %29 = OpLoad %22 %24 + %30 = OpLoad %22 %26 + %83 = OpSMulExtended %69 %29 %30 + %31 = OpCompositeExtract %22 %83 0 + OpStore %28 %31 + OpStore %34 %35 + OpStore %36 %38 + %40 = OpLoad %32 %34 + %41 = OpLoad %32 %36 + %84 = OpIAddCarry %63 %40 %41 + %42 = OpCompositeExtract %32 %84 0 + OpStore %39 %42 + %43 = OpLoad %32 %34 + %44 = OpLoad %32 %36 + %85 = OpISubBorrow %63 %43 %44 + %45 = OpCompositeExtract %32 %85 0 + OpStore %39 %45 + %46 = OpLoad %32 %34 + %47 = OpLoad %32 %36 + %86 = OpUMulExtended %63 %46 %47 + %48 = OpCompositeExtract %32 %86 0 + OpStore %39 %48 + OpStore %51 %52 + OpStore %53 %55 + %57 = OpLoad %49 %51 + %58 = OpLoad %49 %53 + %87 = OpSMulExtended %66 %57 %58 + %59 = OpCompositeExtract %49 %87 0 + OpStore %56 %59 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp new file mode 100644 index 0000000..b8c2a8a --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_boolean_constant_with_constant_binary_test.cpp @@ -0,0 +1,752 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_boolean_constant_with_constant_binary.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, + BasicReplacements) { + // The test came from the following pseudo-GLSL, where int64 and uint64 denote + // 64-bit integer types (they were replaced with int and uint during + // translation to SPIR-V, and the generated SPIR-V has been doctored to + // accommodate them). + // + // #version 450 + // + // void main() { + // double d1, d2; + // d1 = 1.0; + // d2 = 2.0; + // float f1, f2; + // f1 = 4.0; + // f2 = 8.0; + // int i1, i2; + // i1 = 100; + // i2 = 200; + // + // uint u1, u2; + // u1 = 300u; + // u2 = 400u; + // + // int64 i64_1, i64_2; + // i64_1 = 500; + // i64_2 = 600; + // + // uint64 u64_1, u64_2; + // u64_1 = 700u; + // u64_2 = 800u; + // + // bool b, c, d, e; + // b = true; + // c = false; + // d = true || c; + // c = c && false; + // } + std::string shader = R"( + OpCapability Shader + OpCapability Float64 + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "d1" + OpName %10 "d2" + OpName %14 "f1" + OpName %16 "f2" + OpName %20 "i1" + OpName %22 "i2" + OpName %26 "u1" + OpName %28 "u2" + OpName %30 "i64_1" + OpName %32 "i64_2" + OpName %34 "u64_1" + OpName %36 "u64_2" + OpName %40 "b" + OpName %42 "c" + OpName %44 "d" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 64 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 4 + %17 = OpConstant %12 8 + %18 = OpTypeInt 32 1 + %60 = OpTypeInt 64 1 + %61 = OpTypePointer Function %60 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 -100 + %23 = OpConstant %18 200 + %24 = OpTypeInt 32 0 + %62 = OpTypeInt 64 0 + %63 = OpTypePointer Function %62 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 300 + %29 = OpConstant %24 400 + %31 = OpConstant %60 -600 + %33 = OpConstant %60 -500 + %35 = OpConstant %62 700 + %37 = OpConstant %62 800 + %38 = OpTypeBool + %39 = OpTypePointer Function %38 + %41 = OpConstantTrue %38 + %43 = OpConstantFalse %38 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %16 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %28 = OpVariable %25 Function + %30 = OpVariable %61 Function + %32 = OpVariable %61 Function + %34 = OpVariable %63 Function + %36 = OpVariable %63 Function + %40 = OpVariable %39 Function + %42 = OpVariable %39 Function + %44 = OpVariable %39 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpStore %16 %17 + OpStore %20 %21 + OpStore %22 %23 + OpStore %26 %27 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpStore %34 %35 + OpStore %36 %37 + OpStore %40 %41 + OpStore %42 %43 + %45 = OpLoad %38 %42 + %46 = OpLogicalOr %38 %41 %45 + OpStore %44 %46 + %47 = OpLoad %38 %42 + %48 = OpLogicalAnd %38 %47 %43 + OpStore %42 %48 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + std::vector uses_of_true = { + MakeIdUseDescriptor(41, MakeInstructionDescriptor(44, SpvOpStore, 12), 1), + MakeIdUseDescriptor(41, MakeInstructionDescriptor(46, SpvOpLogicalOr, 0), + 0)}; + + std::vector uses_of_false = { + MakeIdUseDescriptor(43, MakeInstructionDescriptor(44, SpvOpStore, 13), 1), + MakeIdUseDescriptor(43, MakeInstructionDescriptor(48, SpvOpLogicalAnd, 0), + 1)}; + + const uint32_t fresh_id = 100; + + std::vector fp_gt_opcodes = { + SpvOpFOrdGreaterThan, SpvOpFOrdGreaterThanEqual, SpvOpFUnordGreaterThan, + SpvOpFUnordGreaterThanEqual}; + + std::vector fp_lt_opcodes = {SpvOpFOrdLessThan, SpvOpFOrdLessThanEqual, + SpvOpFUnordLessThan, + SpvOpFUnordLessThanEqual}; + + std::vector int_gt_opcodes = {SpvOpSGreaterThan, + SpvOpSGreaterThanEqual}; + + std::vector int_lt_opcodes = {SpvOpSLessThan, SpvOpSLessThanEqual}; + + std::vector uint_gt_opcodes = {SpvOpUGreaterThan, + SpvOpUGreaterThanEqual}; + + std::vector uint_lt_opcodes = {SpvOpULessThan, SpvOpULessThanEqual}; + +#define CHECK_OPERATOR(USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \ + ASSERT_TRUE(TransformationReplaceBooleanConstantWithConstantBinary( \ + USE_DESCRIPTOR, LHS_ID, RHS_ID, OPCODE, FRESH_ID) \ + .IsApplicable(context.get(), transformation_context)); \ + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( \ + USE_DESCRIPTOR, RHS_ID, LHS_ID, OPCODE, FRESH_ID) \ + .IsApplicable(context.get(), transformation_context)); + +#define CHECK_TRANSFORMATION_APPLICABILITY(GT_OPCODES, LT_OPCODES, SMALL_ID, \ + LARGE_ID) \ + for (auto gt_opcode : GT_OPCODES) { \ + for (auto& true_use : uses_of_true) { \ + CHECK_OPERATOR(true_use, LARGE_ID, SMALL_ID, gt_opcode, fresh_id); \ + } \ + for (auto& false_use : uses_of_false) { \ + CHECK_OPERATOR(false_use, SMALL_ID, LARGE_ID, gt_opcode, fresh_id); \ + } \ + } \ + for (auto lt_opcode : LT_OPCODES) { \ + for (auto& true_use : uses_of_true) { \ + CHECK_OPERATOR(true_use, SMALL_ID, LARGE_ID, lt_opcode, fresh_id); \ + } \ + for (auto& false_use : uses_of_false) { \ + CHECK_OPERATOR(false_use, LARGE_ID, SMALL_ID, lt_opcode, fresh_id); \ + } \ + } + + // Float + { CHECK_TRANSFORMATION_APPLICABILITY(fp_gt_opcodes, fp_lt_opcodes, 15, 17); } + + // Double + { CHECK_TRANSFORMATION_APPLICABILITY(fp_gt_opcodes, fp_lt_opcodes, 9, 11); } + + // Int32 + { + CHECK_TRANSFORMATION_APPLICABILITY(int_gt_opcodes, int_lt_opcodes, 21, 23); + } + + // Int64 + { + CHECK_TRANSFORMATION_APPLICABILITY(int_gt_opcodes, int_lt_opcodes, 31, 33); + } + + // Uint32 + { + CHECK_TRANSFORMATION_APPLICABILITY(uint_gt_opcodes, uint_lt_opcodes, 27, + 29); + } + + // Uint64 + { + CHECK_TRANSFORMATION_APPLICABILITY(uint_gt_opcodes, uint_lt_opcodes, 35, + 37); + } + + // Target id is not fresh + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 15, 17, SpvOpFOrdLessThan, 15) + .IsApplicable(context.get(), transformation_context)); + + // LHS id does not exist + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 300, 17, SpvOpFOrdLessThan, 200) + .IsApplicable(context.get(), transformation_context)); + + // RHS id does not exist + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 15, 300, SpvOpFOrdLessThan, 200) + .IsApplicable(context.get(), transformation_context)); + + // LHS and RHS ids do not match type + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 11, 17, SpvOpFOrdLessThan, 200) + .IsApplicable(context.get(), transformation_context)); + + // Opcode not appropriate + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 15, 17, SpvOpFDiv, 200) + .IsApplicable(context.get(), transformation_context)); + + auto replace_true_with_double_comparison = + TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 11, 9, SpvOpFUnordGreaterThan, 100); + auto replace_true_with_uint32_comparison = + TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[1], 27, 29, SpvOpULessThanEqual, 101); + auto replace_false_with_float_comparison = + TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_false[0], 17, 15, SpvOpFOrdLessThan, 102); + auto replace_false_with_sint64_comparison = + TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_false[1], 33, 31, SpvOpSLessThan, 103); + + ASSERT_TRUE(replace_true_with_double_comparison.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_true_with_double_comparison, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(replace_true_with_uint32_comparison.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_true_with_uint32_comparison, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(replace_false_with_float_comparison.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_false_with_float_comparison, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(replace_false_with_sint64_comparison.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_false_with_sint64_comparison, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after = R"( + OpCapability Shader + OpCapability Float64 + OpCapability Int64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "d1" + OpName %10 "d2" + OpName %14 "f1" + OpName %16 "f2" + OpName %20 "i1" + OpName %22 "i2" + OpName %26 "u1" + OpName %28 "u2" + OpName %30 "i64_1" + OpName %32 "i64_2" + OpName %34 "u64_1" + OpName %36 "u64_2" + OpName %40 "b" + OpName %42 "c" + OpName %44 "d" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 64 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 4 + %17 = OpConstant %12 8 + %18 = OpTypeInt 32 1 + %60 = OpTypeInt 64 1 + %61 = OpTypePointer Function %60 + %19 = OpTypePointer Function %18 + %21 = OpConstant %18 -100 + %23 = OpConstant %18 200 + %24 = OpTypeInt 32 0 + %62 = OpTypeInt 64 0 + %63 = OpTypePointer Function %62 + %25 = OpTypePointer Function %24 + %27 = OpConstant %24 300 + %29 = OpConstant %24 400 + %31 = OpConstant %60 -600 + %33 = OpConstant %60 -500 + %35 = OpConstant %62 700 + %37 = OpConstant %62 800 + %38 = OpTypeBool + %39 = OpTypePointer Function %38 + %41 = OpConstantTrue %38 + %43 = OpConstantFalse %38 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %16 = OpVariable %13 Function + %20 = OpVariable %19 Function + %22 = OpVariable %19 Function + %26 = OpVariable %25 Function + %28 = OpVariable %25 Function + %30 = OpVariable %61 Function + %32 = OpVariable %61 Function + %34 = OpVariable %63 Function + %36 = OpVariable %63 Function + %40 = OpVariable %39 Function + %42 = OpVariable %39 Function + %44 = OpVariable %39 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpStore %16 %17 + OpStore %20 %21 + OpStore %22 %23 + OpStore %26 %27 + OpStore %28 %29 + OpStore %30 %31 + OpStore %32 %33 + OpStore %34 %35 + OpStore %36 %37 + %100 = OpFUnordGreaterThan %38 %11 %9 + OpStore %40 %100 + %102 = OpFOrdLessThan %38 %17 %15 + OpStore %42 %102 + %45 = OpLoad %38 %42 + %101 = OpULessThanEqual %38 %27 %29 + %46 = OpLogicalOr %38 %101 %45 + OpStore %44 %46 + %47 = OpLoad %38 %42 + %103 = OpSLessThan %38 %33 %31 + %48 = OpLogicalAnd %38 %47 %103 + OpStore %42 %48 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after, context.get())); + + if (std::numeric_limits::has_quiet_NaN) { + double quiet_nan_double = std::numeric_limits::quiet_NaN(); + uint32_t words[2]; + memcpy(words, &quiet_nan_double, sizeof(double)); + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, 6, 200, operands)); + fuzzerutil::UpdateModuleIdBound(context.get(), 200); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + // The transformation is not applicable because %200 is NaN. + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 11, 200, SpvOpFOrdLessThan, 300) + .IsApplicable(context.get(), transformation_context)); + } + if (std::numeric_limits::has_infinity) { + double positive_infinity_double = std::numeric_limits::infinity(); + uint32_t words[2]; + memcpy(words, &positive_infinity_double, sizeof(double)); + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[0]}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words[1]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, 6, 201, operands)); + fuzzerutil::UpdateModuleIdBound(context.get(), 201); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + // Even though the double constant %11 is less than the infinity %201, the + // transformation is restricted to only apply to finite values. + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 11, 201, SpvOpFOrdLessThan, 300) + .IsApplicable(context.get(), transformation_context)); + } + if (std::numeric_limits::has_infinity) { + float positive_infinity_float = std::numeric_limits::infinity(); + float negative_infinity_float = -1 * positive_infinity_float; + uint32_t words_positive_infinity[1]; + uint32_t words_negative_infinity[1]; + memcpy(words_positive_infinity, &positive_infinity_float, sizeof(float)); + memcpy(words_negative_infinity, &negative_infinity_float, sizeof(float)); + opt::Instruction::OperandList operands_positive_infinity = { + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_positive_infinity[0]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, 12, 202, operands_positive_infinity)); + fuzzerutil::UpdateModuleIdBound(context.get(), 202); + opt::Instruction::OperandList operands = { + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {words_negative_infinity[0]}}}; + context->module()->AddGlobalValue(MakeUnique( + context.get(), SpvOpConstant, 12, 203, operands)); + fuzzerutil::UpdateModuleIdBound(context.get(), 203); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + // Even though the negative infinity at %203 is less than the positive + // infinity %202, the transformation is restricted to only apply to finite + // values. + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + uses_of_true[0], 203, 202, SpvOpFOrdLessThan, 300) + .IsApplicable(context.get(), transformation_context)); + } +} + +TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, + MergeInstructions) { + // The test came from the following GLSL: + // + // void main() { + // int x = 1; + // int y = 2; + // if (true) { + // x = 2; + // } + // while(false) { + // y = 2; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %21 = OpConstantFalse %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + OpSelectionMerge %15 None + OpBranchConditional %13 %14 %15 + %14 = OpLabel + OpStore %8 %11 + OpBranch %15 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranchConditional %21 %17 %18 + %17 = OpLabel + OpStore %10 %11 + OpBranch %19 + %19 = OpLabel + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto use_of_true_in_if = MakeIdUseDescriptor( + 13, MakeInstructionDescriptor(10, SpvOpBranchConditional, 0), 0); + auto use_of_false_in_while = MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(16, SpvOpBranchConditional, 0), 0); + + auto replacement_1 = TransformationReplaceBooleanConstantWithConstantBinary( + use_of_true_in_if, 9, 11, SpvOpSLessThan, 100); + auto replacement_2 = TransformationReplaceBooleanConstantWithConstantBinary( + use_of_false_in_while, 9, 11, SpvOpSGreaterThanEqual, 101); + + ASSERT_TRUE( + replacement_1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement_1, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE( + replacement_2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement_2, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %12 = OpTypeBool + %13 = OpConstantTrue %12 + %21 = OpConstantFalse %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %100 = OpSLessThan %12 %9 %11 + OpSelectionMerge %15 None + OpBranchConditional %100 %14 %15 + %14 = OpLabel + OpStore %8 %11 + OpBranch %15 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + %101 = OpSGreaterThanEqual %12 %9 %11 + OpLoopMerge %18 %19 None + OpBranchConditional %101 %17 %18 + %17 = OpLabel + OpStore %10 %11 + OpBranch %19 + %19 = OpLabel + OpBranch %16 + %18 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after, context.get())); +} + +TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, OpPhi) { + // Hand-written SPIR-V to check applicability of the transformation on an + // OpPhi argument. + + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %10 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeBool + +; Constants + %6 = OpConstant %4 0 + %7 = OpConstant %4 1 + %8 = OpConstantTrue %5 + %9 = OpConstantFalse %5 + +; main function + %10 = OpFunction %2 None %3 + %11 = OpLabel + OpSelectionMerge %13 None + OpBranchConditional %8 %12 %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + %14 = OpPhi %5 %8 %11 %9 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = MakeInstructionDescriptor(14, SpvOpPhi, 0); + auto id_use_descriptor = MakeIdUseDescriptor(8, instruction_descriptor, 0); + auto transformation = TransformationReplaceBooleanConstantWithConstantBinary( + id_use_descriptor, 6, 7, SpvOpULessThan, 15); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %10 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeInt 32 0 + %5 = OpTypeBool + +; Constants + %6 = OpConstant %4 0 + %7 = OpConstant %4 1 + %8 = OpConstantTrue %5 + %9 = OpConstantFalse %5 + +; main function + %10 = OpFunction %2 None %3 + %11 = OpLabel + %15 = OpULessThan %5 %6 %7 + OpSelectionMerge %13 None + OpBranchConditional %8 %12 %13 + %12 = OpLabel + OpBranch %13 + %13 = OpLabel + %14 = OpPhi %5 %15 %11 %9 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationReplaceBooleanConstantWithConstantBinaryTest, + DoNotReplaceVariableInitializer) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypePointer Function %6 + %9 = OpConstantTrue %6 + %10 = OpTypeInt 32 1 + %13 = OpConstant %10 0 + %15 = OpConstant %10 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %50 = OpVariable %7 Function %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_FALSE(TransformationReplaceBooleanConstantWithConstantBinary( + MakeIdUseDescriptor( + 9, MakeInstructionDescriptor(50, SpvOpVariable, 0), 1), + 13, 15, SpvOpSLessThan, 100) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp new file mode 100644 index 0000000..4532503 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_branch_from_dead_block_with_exit_test.cpp @@ -0,0 +1,571 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_branch_from_dead_block_with_exit.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceBranchFromDeadBlockWithExitTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 1 + %17 = OpConstant %12 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpVariable %13 Function + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %21 + %8 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %7 %10 %16 + %10 = OpLabel + OpStore %14 %15 + OpBranch %20 + %20 = OpLabel + OpBranch %11 + %16 = OpLabel + OpStore %14 %17 + OpBranch %11 + %11 = OpLabel + OpBranch %9 + %21 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(8); + transformation_context.GetFactManager()->AddFactBlockIsDead(10); + transformation_context.GetFactManager()->AddFactBlockIsDead(11); + transformation_context.GetFactManager()->AddFactBlockIsDead(16); + transformation_context.GetFactManager()->AddFactBlockIsDead(20); + + // Bad: 4 is not a block + ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(4, SpvOpKill, 0) + .IsApplicable(context.get(), transformation_context)); + // Bad: 200 does not exist + ASSERT_FALSE( + TransformationReplaceBranchFromDeadBlockWithExit(200, SpvOpKill, 0) + .IsApplicable(context.get(), transformation_context)); + // Bad: 21 is not a dead block + ASSERT_FALSE( + TransformationReplaceBranchFromDeadBlockWithExit(21, SpvOpKill, 0) + .IsApplicable(context.get(), transformation_context)); + // Bad: terminator of 8 is not OpBranch + ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit(8, SpvOpKill, 0) + .IsApplicable(context.get(), transformation_context)); + // Bad: 10's successor only has 10 as a predecessor + ASSERT_FALSE( + TransformationReplaceBranchFromDeadBlockWithExit(10, SpvOpKill, 0) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + ASSERT_DEATH( + TransformationReplaceBranchFromDeadBlockWithExit(20, SpvOpSwitch, 0) + .IsApplicable(context.get(), transformation_context), + "Invalid early exit opcode."); +#endif + + auto transformation1 = + TransformationReplaceBranchFromDeadBlockWithExit(20, SpvOpKill, 0); + auto transformation2 = + TransformationReplaceBranchFromDeadBlockWithExit(16, SpvOpKill, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + // Applying transformation 1 should disable transformation 2 + ASSERT_FALSE( + transformation2.IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 1 + %17 = OpConstant %12 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpVariable %13 Function + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %21 + %8 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %7 %10 %16 + %10 = OpLabel + OpStore %14 %15 + OpBranch %20 + %20 = OpLabel + OpKill + %16 = OpLabel + OpStore %14 %17 + OpBranch %11 + %11 = OpLabel + OpBranch %9 + %21 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceBranchFromDeadBlockWithExitTest, + VertexShaderWithLoopInContinueConstruct) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %22 = OpConstant %6 10 + %23 = OpTypeBool + %26 = OpConstant %6 1 + %40 = OpConstant %6 100 + %48 = OpConstantFalse %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %50 None + OpBranchConditional %48 %49 %50 + %49 = OpLabel + %51 = OpFunctionCall %6 %10 + OpBranch %50 + %50 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %13 = OpVariable %12 Function + %15 = OpVariable %12 Function + %33 = OpVariable %12 Function + OpStore %33 %14 + OpBranch %34 + %34 = OpLabel + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %39 = OpLoad %6 %33 + %41 = OpSLessThan %23 %39 %40 + OpBranchConditional %41 %35 %36 + %35 = OpLabel + OpSelectionMerge %202 None + OpBranchConditional %48 %200 %201 + %200 = OpLabel + OpBranch %202 + %201 = OpLabel + %400 = OpCopyObject %6 %14 + OpBranch %202 + %202 = OpLabel + OpBranch %37 + %37 = OpLabel + OpStore %13 %14 + OpStore %15 %14 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + OpSelectionMerge %102 None + OpBranchConditional %48 %100 %101 + %100 = OpLabel + OpBranch %102 + %101 = OpLabel + OpBranch %102 + %102 = OpLabel + %21 = OpLoad %6 %15 + %24 = OpSLessThan %23 %21 %22 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + OpSelectionMerge %302 None + OpBranchConditional %48 %300 %301 + %300 = OpLabel + OpBranch %302 + %301 = OpLabel + OpBranch %302 + %302 = OpLabel + %25 = OpLoad %6 %13 + %27 = OpIAdd %6 %25 %26 + OpStore %13 %27 + OpBranch %19 + %19 = OpLabel + %28 = OpLoad %6 %15 + %29 = OpIAdd %6 %28 %26 + OpStore %15 %29 + OpBranch %16 + %18 = OpLabel + %30 = OpLoad %6 %13 + %42 = OpCopyObject %6 %30 + %43 = OpLoad %6 %33 + %44 = OpIAdd %6 %43 %42 + OpStore %33 %44 + OpBranch %34 + %36 = OpLabel + %45 = OpLoad %6 %33 + OpReturnValue %45 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + for (auto block : {16, 17, 18, 19, 20, 34, 35, 36, 37, 38, + 49, 100, 101, 102, 200, 201, 202, 300, 301, 302}) { + transformation_context.GetFactManager()->AddFactBlockIsDead(block); + } + + // Bad: OpKill not allowed in vertex shader + ASSERT_FALSE( + TransformationReplaceBranchFromDeadBlockWithExit(201, SpvOpKill, 0) + .IsApplicable(context.get(), transformation_context)); + + // Bad: OpReturn is not allowed in function that expects a returned value. + ASSERT_FALSE( + TransformationReplaceBranchFromDeadBlockWithExit(200, SpvOpReturn, 0) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Return value id does not exist + ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit( + 201, SpvOpReturnValue, 1000) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Return value id does not have a type + ASSERT_FALSE( + TransformationReplaceBranchFromDeadBlockWithExit(200, SpvOpReturnValue, 6) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Return value id does not have the right type + ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit( + 201, SpvOpReturnValue, 48) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Return value id is not available + ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit( + 200, SpvOpReturnValue, 400) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Early exit now allowed in continue construct + ASSERT_FALSE( + TransformationReplaceBranchFromDeadBlockWithExit(101, SpvOpUnreachable, 0) + .IsApplicable(context.get(), transformation_context)); + + // Bad: Early exit now allowed in continue construct (again) + ASSERT_FALSE(TransformationReplaceBranchFromDeadBlockWithExit( + 300, SpvOpReturnValue, 14) + .IsApplicable(context.get(), transformation_context)); + + auto transformation1 = TransformationReplaceBranchFromDeadBlockWithExit( + 200, SpvOpUnreachable, 0); + auto transformation2 = TransformationReplaceBranchFromDeadBlockWithExit( + 201, SpvOpReturnValue, 400); + + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_FALSE( + transformation1.IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + opt::Instruction* return_value_inst = + context->get_instr_block(201)->terminator(); + ASSERT_EQ(SpvOpReturnValue, return_value_inst->opcode()); + ASSERT_EQ(SPV_OPERAND_TYPE_ID, return_value_inst->GetInOperand(0).type); + ASSERT_EQ(400, return_value_inst->GetSingleWordInOperand(0)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %4 "main" + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %12 = OpTypePointer Function %6 + %14 = OpConstant %6 0 + %22 = OpConstant %6 10 + %23 = OpTypeBool + %26 = OpConstant %6 1 + %40 = OpConstant %6 100 + %48 = OpConstantFalse %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %50 None + OpBranchConditional %48 %49 %50 + %49 = OpLabel + %51 = OpFunctionCall %6 %10 + OpBranch %50 + %50 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + %13 = OpVariable %12 Function + %15 = OpVariable %12 Function + %33 = OpVariable %12 Function + OpStore %33 %14 + OpBranch %34 + %34 = OpLabel + OpLoopMerge %36 %37 None + OpBranch %38 + %38 = OpLabel + %39 = OpLoad %6 %33 + %41 = OpSLessThan %23 %39 %40 + OpBranchConditional %41 %35 %36 + %35 = OpLabel + OpSelectionMerge %202 None + OpBranchConditional %48 %200 %201 + %200 = OpLabel + OpBranch %202 + %201 = OpLabel + %400 = OpCopyObject %6 %14 + OpReturnValue %400 + %202 = OpLabel + OpBranch %37 + %37 = OpLabel + OpStore %13 %14 + OpStore %15 %14 + OpBranch %16 + %16 = OpLabel + OpLoopMerge %18 %19 None + OpBranch %20 + %20 = OpLabel + OpSelectionMerge %102 None + OpBranchConditional %48 %100 %101 + %100 = OpLabel + OpBranch %102 + %101 = OpLabel + OpBranch %102 + %102 = OpLabel + %21 = OpLoad %6 %15 + %24 = OpSLessThan %23 %21 %22 + OpBranchConditional %24 %17 %18 + %17 = OpLabel + OpSelectionMerge %302 None + OpBranchConditional %48 %300 %301 + %300 = OpLabel + OpBranch %302 + %301 = OpLabel + OpBranch %302 + %302 = OpLabel + %25 = OpLoad %6 %13 + %27 = OpIAdd %6 %25 %26 + OpStore %13 %27 + OpBranch %19 + %19 = OpLabel + %28 = OpLoad %6 %15 + %29 = OpIAdd %6 %28 %26 + OpStore %15 %29 + OpBranch %16 + %18 = OpLabel + %30 = OpLoad %6 %13 + %42 = OpCopyObject %6 %30 + %43 = OpLoad %6 %33 + %44 = OpIAdd %6 %43 %42 + OpStore %33 %44 + OpBranch %34 + %36 = OpLabel + %45 = OpLoad %6 %33 + OpReturnValue %45 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceBranchFromDeadBlockWithExitTest, OpPhi) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 1 + %17 = OpConstant %12 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpVariable %13 Function + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %21 + %8 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %7 %10 %16 + %10 = OpLabel + OpStore %14 %15 + OpBranch %20 + %20 = OpLabel + %48 = OpCopyObject %12 %15 + OpBranch %11 + %16 = OpLabel + OpStore %14 %17 + %49 = OpCopyObject %12 %17 + OpBranch %11 + %11 = OpLabel + %50 = OpPhi %12 %48 %20 %49 %16 + OpBranch %9 + %21 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + transformation_context.GetFactManager()->AddFactBlockIsDead(8); + transformation_context.GetFactManager()->AddFactBlockIsDead(10); + transformation_context.GetFactManager()->AddFactBlockIsDead(11); + transformation_context.GetFactManager()->AddFactBlockIsDead(16); + transformation_context.GetFactManager()->AddFactBlockIsDead(20); + + auto transformation1 = + TransformationReplaceBranchFromDeadBlockWithExit(20, SpvOpKill, 0); + auto transformation2 = + TransformationReplaceBranchFromDeadBlockWithExit(16, SpvOpKill, 0); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + // Applying transformation 1 should disable transformation 2 + ASSERT_FALSE( + transformation2.IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %12 = OpTypeInt 32 1 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 1 + %17 = OpConstant %12 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %14 = OpVariable %13 Function + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %21 + %8 = OpLabel + OpSelectionMerge %11 None + OpBranchConditional %7 %10 %16 + %10 = OpLabel + OpStore %14 %15 + OpBranch %20 + %20 = OpLabel + %48 = OpCopyObject %12 %15 + OpKill + %16 = OpLabel + OpStore %14 %17 + %49 = OpCopyObject %12 %17 + OpBranch %11 + %11 = OpLabel + %50 = OpPhi %12 %49 %16 + OpBranch %9 + %21 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp new file mode 100644 index 0000000..fe76068 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_constant_with_uniform_test.cpp @@ -0,0 +1,1636 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_constant_with_uniform.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "source/fuzz/uniform_buffer_element_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +bool AddFactHelper( + TransformationContext* transformation_context, uint32_t word, + const protobufs::UniformBufferElementDescriptor& descriptor) { + protobufs::FactConstantUniform constant_uniform_fact; + constant_uniform_fact.add_constant_word(word); + *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() = + descriptor; + protobufs::Fact fact; + *fact.mutable_constant_uniform_fact() = constant_uniform_fact; + return transformation_context->GetFactManager()->MaybeAddFact(fact); +} + +TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // int a; + // int b; + // int c; + // }; + // + // void main() + // { + // int x; + // x = 1; + // x = x + 2; + // x = 3 + x; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %16 "blockname" + OpMemberName %16 0 "a" + OpMemberName %16 1 "b" + OpMemberName %16 2 "c" + OpName %18 "" + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %14 = OpConstant %6 3 + %16 = OpTypeStruct %6 %6 %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpIAdd %6 %10 %11 + OpStore %8 %12 + %13 = OpLoad %6 %8 + %15 = OpIAdd %6 %14 %13 + OpStore %8 %15 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::UniformBufferElementDescriptor blockname_a = + MakeUniformBufferElementDescriptor(0, 0, {0}); + protobufs::UniformBufferElementDescriptor blockname_b = + MakeUniformBufferElementDescriptor(0, 0, {1}); + protobufs::UniformBufferElementDescriptor blockname_c = + MakeUniformBufferElementDescriptor(0, 0, {2}); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 1, blockname_a)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 2, blockname_b)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 3, blockname_c)); + + // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively. + protobufs::IdUseDescriptor use_of_9_in_store = + MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1); + protobufs::IdUseDescriptor use_of_11_in_add = + MakeIdUseDescriptor(11, MakeInstructionDescriptor(12, SpvOpIAdd, 0), 1); + protobufs::IdUseDescriptor use_of_14_in_add = + MakeIdUseDescriptor(14, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0); + + // These transformations work: they match the facts. + auto transformation_use_of_9_in_store = + TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_a, + 100, 101); + ASSERT_TRUE(transformation_use_of_9_in_store.IsApplicable( + context.get(), transformation_context)); + auto transformation_use_of_11_in_add = + TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b, + 102, 103); + ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable( + context.get(), transformation_context)); + auto transformation_use_of_14_in_add = + TransformationReplaceConstantWithUniform(use_of_14_in_add, blockname_c, + 104, 105); + ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable( + context.get(), transformation_context)); + + // The transformations are not applicable if we change which uniforms are + // applied to which constants. + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, + blockname_b, 101, 102) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, + blockname_c, 101, 102) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_14_in_add, + blockname_a, 101, 102) + .IsApplicable(context.get(), transformation_context)); + + // The following transformations do not apply because the uniform descriptors + // are not sensible. + protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor1 = + MakeUniformBufferElementDescriptor(1, 2, {0}); + protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor2 = + MakeUniformBufferElementDescriptor(0, 0, {5}); + ASSERT_FALSE(TransformationReplaceConstantWithUniform( + use_of_9_in_store, nonsense_uniform_descriptor1, 101, 102) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceConstantWithUniform( + use_of_9_in_store, nonsense_uniform_descriptor2, 101, 102) + .IsApplicable(context.get(), transformation_context)); + + // The following transformation does not apply because the id descriptor is + // not sensible. + protobufs::IdUseDescriptor nonsense_id_use_descriptor = + MakeIdUseDescriptor(9, MakeInstructionDescriptor(15, SpvOpIAdd, 0), 0); + ASSERT_FALSE(TransformationReplaceConstantWithUniform( + nonsense_id_use_descriptor, blockname_a, 101, 102) + .IsApplicable(context.get(), transformation_context)); + + // The following transformations do not apply because the ids are not fresh. + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, + blockname_b, 15, 103) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, + blockname_b, 102, 15) + .IsApplicable(context.get(), transformation_context)); + + // Apply the use of 9 in a store. + ApplyAndCheckFreshIds(transformation_use_of_9_in_store, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string after_replacing_use_of_9_in_store = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %16 "blockname" + OpMemberName %16 0 "a" + OpMemberName %16 1 "b" + OpMemberName %16 2 "c" + OpName %18 "" + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %14 = OpConstant %6 3 + %16 = OpTypeStruct %6 %6 %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpAccessChain %51 %18 %50 + %101 = OpLoad %6 %100 + OpStore %8 %101 + %10 = OpLoad %6 %8 + %12 = OpIAdd %6 %10 %11 + OpStore %8 %12 + %13 = OpLoad %6 %8 + %15 = OpIAdd %6 %14 %13 + OpStore %8 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_replacing_use_of_9_in_store, context.get())); + + ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable( + context.get(), transformation_context)); + // Apply the use of 11 in an add. + ApplyAndCheckFreshIds(transformation_use_of_11_in_add, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string after_replacing_use_of_11_in_add = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %16 "blockname" + OpMemberName %16 0 "a" + OpMemberName %16 1 "b" + OpMemberName %16 2 "c" + OpName %18 "" + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %14 = OpConstant %6 3 + %16 = OpTypeStruct %6 %6 %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpAccessChain %51 %18 %50 + %101 = OpLoad %6 %100 + OpStore %8 %101 + %10 = OpLoad %6 %8 + %102 = OpAccessChain %51 %18 %9 + %103 = OpLoad %6 %102 + %12 = OpIAdd %6 %10 %103 + OpStore %8 %12 + %13 = OpLoad %6 %8 + %15 = OpIAdd %6 %14 %13 + OpStore %8 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_replacing_use_of_11_in_add, context.get())); + + ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable( + context.get(), transformation_context)); + // Apply the use of 15 in an add. + ApplyAndCheckFreshIds(transformation_use_of_14_in_add, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + std::string after_replacing_use_of_14_in_add = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "x" + OpName %16 "blockname" + OpMemberName %16 0 "a" + OpMemberName %16 1 "b" + OpMemberName %16 2 "c" + OpName %18 "" + OpMemberDecorate %16 0 Offset 0 + OpMemberDecorate %16 1 Offset 4 + OpMemberDecorate %16 2 Offset 8 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %9 = OpConstant %6 1 + %11 = OpConstant %6 2 + %14 = OpConstant %6 3 + %16 = OpTypeStruct %6 %6 %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpAccessChain %51 %18 %50 + %101 = OpLoad %6 %100 + OpStore %8 %101 + %10 = OpLoad %6 %8 + %102 = OpAccessChain %51 %18 %9 + %103 = OpLoad %6 %102 + %12 = OpIAdd %6 %10 %103 + OpStore %8 %12 + %13 = OpLoad %6 %8 + %104 = OpAccessChain %51 %18 %11 + %105 = OpLoad %6 %104 + %15 = OpIAdd %6 %105 %13 + OpStore %8 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_replacing_use_of_14_in_add, context.get())); +} + +TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) { + // This test came from the following GLSL: + // + // #version 450 + // + // struct U { + // int x; // == 4 + // }; + // + // struct T { + // int x; // == 3 + // U y; + // }; + // + // struct S { + // T x; + // int y; // == 2 + // }; + // + // uniform blockname { + // int x; // == 1 + // S y; + // }; + // + // void foo(int a) { } + // + // void main() + // { + // int x; + // x = 1; + // x = x + 2; + // x = 3 + x; + // foo(4); + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "a" + OpName %12 "x" + OpName %21 "param" + OpName %23 "U" + OpMemberName %23 0 "x" + OpName %24 "T" + OpMemberName %24 0 "x" + OpMemberName %24 1 "y" + OpName %25 "S" + OpMemberName %25 0 "x" + OpMemberName %25 1 "y" + OpName %26 "blockname" + OpMemberName %26 0 "x" + OpMemberName %26 1 "y" + OpName %28 "" + OpMemberDecorate %23 0 Offset 0 + OpMemberDecorate %24 0 Offset 0 + OpMemberDecorate %24 1 Offset 16 + OpMemberDecorate %25 0 Offset 0 + OpMemberDecorate %25 1 Offset 32 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 16 + OpDecorate %26 Block + OpDecorate %28 DescriptorSet 0 + OpDecorate %28 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpConstant %6 0 + %13 = OpConstant %6 1 + %15 = OpConstant %6 2 + %17 = OpConstant %6 3 + %20 = OpConstant %6 4 + %23 = OpTypeStruct %6 + %24 = OpTypeStruct %6 %23 + %25 = OpTypeStruct %24 %6 + %26 = OpTypeStruct %6 %25 + %27 = OpTypePointer Uniform %26 + %51 = OpTypePointer Uniform %6 + %28 = OpVariable %27 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %7 Function + %21 = OpVariable %7 Function + OpStore %12 %13 + %14 = OpLoad %6 %12 + %16 = OpIAdd %6 %14 %15 + OpStore %12 %16 + %18 = OpLoad %6 %12 + %19 = OpIAdd %6 %17 %18 + OpStore %12 %19 + OpStore %21 %20 + %22 = OpFunctionCall %2 %10 %21 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::UniformBufferElementDescriptor blockname_1 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + protobufs::UniformBufferElementDescriptor blockname_2 = + MakeUniformBufferElementDescriptor(0, 0, {1, 1}); + protobufs::UniformBufferElementDescriptor blockname_3 = + MakeUniformBufferElementDescriptor(0, 0, {1, 0, 0}); + protobufs::UniformBufferElementDescriptor blockname_4 = + MakeUniformBufferElementDescriptor(0, 0, {1, 0, 1, 0}); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 1, blockname_1)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 2, blockname_2)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 3, blockname_3)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 4, blockname_4)); + + // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively. + protobufs::IdUseDescriptor use_of_13_in_store = + MakeIdUseDescriptor(13, MakeInstructionDescriptor(21, SpvOpStore, 0), 1); + protobufs::IdUseDescriptor use_of_15_in_add = + MakeIdUseDescriptor(15, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 1); + protobufs::IdUseDescriptor use_of_17_in_add = + MakeIdUseDescriptor(17, MakeInstructionDescriptor(19, SpvOpIAdd, 0), 0); + protobufs::IdUseDescriptor use_of_20_in_store = + MakeIdUseDescriptor(20, MakeInstructionDescriptor(19, SpvOpStore, 1), 1); + + // These transformations work: they match the facts. + auto transformation_use_of_13_in_store = + TransformationReplaceConstantWithUniform(use_of_13_in_store, blockname_1, + 100, 101); + ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + auto transformation_use_of_15_in_add = + TransformationReplaceConstantWithUniform(use_of_15_in_add, blockname_2, + 102, 103); + ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + auto transformation_use_of_17_in_add = + TransformationReplaceConstantWithUniform(use_of_17_in_add, blockname_3, + 104, 105); + ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + auto transformation_use_of_20_in_store = + TransformationReplaceConstantWithUniform(use_of_20_in_store, blockname_4, + 106, 107); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation_use_of_13_in_store, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation_use_of_15_in_add, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation_use_of_17_in_add, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + ApplyAndCheckFreshIds(transformation_use_of_20_in_store, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable( + context.get(), transformation_context)); + ASSERT_FALSE(transformation_use_of_20_in_store.IsApplicable( + context.get(), transformation_context)); + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "a" + OpName %12 "x" + OpName %21 "param" + OpName %23 "U" + OpMemberName %23 0 "x" + OpName %24 "T" + OpMemberName %24 0 "x" + OpMemberName %24 1 "y" + OpName %25 "S" + OpMemberName %25 0 "x" + OpMemberName %25 1 "y" + OpName %26 "blockname" + OpMemberName %26 0 "x" + OpMemberName %26 1 "y" + OpName %28 "" + OpMemberDecorate %23 0 Offset 0 + OpMemberDecorate %24 0 Offset 0 + OpMemberDecorate %24 1 Offset 16 + OpMemberDecorate %25 0 Offset 0 + OpMemberDecorate %25 1 Offset 32 + OpMemberDecorate %26 0 Offset 0 + OpMemberDecorate %26 1 Offset 16 + OpDecorate %26 Block + OpDecorate %28 DescriptorSet 0 + OpDecorate %28 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %50 = OpConstant %6 0 + %13 = OpConstant %6 1 + %15 = OpConstant %6 2 + %17 = OpConstant %6 3 + %20 = OpConstant %6 4 + %23 = OpTypeStruct %6 + %24 = OpTypeStruct %6 %23 + %25 = OpTypeStruct %24 %6 + %26 = OpTypeStruct %6 %25 + %27 = OpTypePointer Uniform %26 + %51 = OpTypePointer Uniform %6 + %28 = OpVariable %27 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %7 Function + %21 = OpVariable %7 Function + %100 = OpAccessChain %51 %28 %50 + %101 = OpLoad %6 %100 + OpStore %12 %101 + %14 = OpLoad %6 %12 + %102 = OpAccessChain %51 %28 %13 %13 + %103 = OpLoad %6 %102 + %16 = OpIAdd %6 %14 %103 + OpStore %12 %16 + %18 = OpLoad %6 %12 + %104 = OpAccessChain %51 %28 %13 %50 %50 + %105 = OpLoad %6 %104 + %19 = OpIAdd %6 %105 %18 + OpStore %12 %19 + %106 = OpAccessChain %51 %28 %13 %50 %13 %50 + %107 = OpLoad %6 %106 + OpStore %21 %107 + %22 = OpFunctionCall %2 %10 %21 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after, context.get())); +} + +TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // int x; // == 0 + // }; + // + // void main() + // { + // int a; + // a = 0; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "a" + OpName %10 "blockname" + OpMemberName %10 0 "x" + OpName %12 "" + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %10 = OpTypeStruct %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::UniformBufferElementDescriptor blockname_0 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 0, blockname_0)); + + // The constant id is 9 for 0. + protobufs::IdUseDescriptor use_of_9_in_store = + MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1); + + // This transformation is not available because no uniform pointer to integer + // type is present: + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, + blockname_0, 100, 101) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // int x; // == 0 + // int y; // == 9 + // }; + // + // void main() + // { + // int a; + // a = 9; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "a" + OpName %10 "blockname" + OpMemberName %10 0 "x" + OpMemberName %10 1 "y" + OpName %12 "" + OpMemberDecorate %10 0 Offset 0 + OpMemberDecorate %10 1 Offset 4 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 9 + %10 = OpTypeStruct %6 %6 + %11 = OpTypePointer Uniform %10 + %50 = OpTypePointer Uniform %6 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::UniformBufferElementDescriptor blockname_0 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + protobufs::UniformBufferElementDescriptor blockname_9 = + MakeUniformBufferElementDescriptor(0, 0, {1}); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 9, blockname_9)); + + // The constant id is 9 for 9. + protobufs::IdUseDescriptor use_of_9_in_store = + MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1); + + // This transformation is not available because no constant is present for the + // index 1 required to index into the uniform buffer: + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, + blockname_9, 100, 101) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceConstantWithUniformTest, + NoIntTypePresentToEnableIndexing) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // float f; // == 9 + // }; + // + // void main() + // { + // float a; + // a = 3.0; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "a" + OpName %10 "blockname" + OpMemberName %10 0 "f" + OpName %12 "" + OpMemberDecorate %10 0 Offset 0 + OpDecorate %10 Block + OpDecorate %12 DescriptorSet 0 + OpDecorate %12 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 3 + %10 = OpTypeStruct %6 + %11 = OpTypePointer Uniform %10 + %12 = OpVariable %11 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::UniformBufferElementDescriptor blockname_3 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + uint32_t float_data[1]; + float temp = 3.0; + memcpy(&float_data[0], &temp, sizeof(float)); + ASSERT_TRUE( + AddFactHelper(&transformation_context, float_data[0], blockname_3)); + + // The constant id is 9 for 3.0. + protobufs::IdUseDescriptor use_of_9_in_store = + MakeIdUseDescriptor(9, MakeInstructionDescriptor(8, SpvOpStore, 0), 1); + + // This transformation is not available because no integer type is present to + // allow a constant index to be expressed: + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, + blockname_3, 100, 101) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceConstantWithUniformTest, + UniformFactsDoNotMatchConstants) { + // This test came from the following GLSL: + // + // #version 450 + // + // uniform blockname { + // int x; // == 9 + // int y; // == 10 + // }; + // + // void main() + // { + // int a; + // int b; + // a = 9; + // b = 10; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %12 "blockname" + OpMemberName %12 0 "x" + OpMemberName %12 1 "y" + OpName %14 "" + OpMemberDecorate %12 0 Offset 0 + OpMemberDecorate %12 1 Offset 4 + OpDecorate %12 Block + OpDecorate %14 DescriptorSet 0 + OpDecorate %14 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 9 + %11 = OpConstant %6 10 + %50 = OpConstant %6 0 + %51 = OpConstant %6 1 + %12 = OpTypeStruct %6 %6 + %13 = OpTypePointer Uniform %12 + %52 = OpTypePointer Uniform %6 + %14 = OpVariable %13 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::UniformBufferElementDescriptor blockname_9 = + MakeUniformBufferElementDescriptor(0, 0, {0}); + protobufs::UniformBufferElementDescriptor blockname_10 = + MakeUniformBufferElementDescriptor(0, 0, {1}); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 9, blockname_9)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 10, blockname_10)); + + // The constant ids for 9 and 10 are 9 and 11 respectively + protobufs::IdUseDescriptor use_of_9_in_store = + MakeIdUseDescriptor(9, MakeInstructionDescriptor(10, SpvOpStore, 0), 1); + protobufs::IdUseDescriptor use_of_11_in_store = + MakeIdUseDescriptor(11, MakeInstructionDescriptor(10, SpvOpStore, 1), 1); + + // These are right: + ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store, + blockname_9, 100, 101) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_11_in_store, + blockname_10, 102, 103) + .IsApplicable(context.get(), transformation_context)); + + // These are wrong because the constants do not match the facts about + // uniforms. + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_store, + blockname_9, 100, 101) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, + blockname_10, 102, 103) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { + // The following GLSL was the basis for this test: + + // #version 450 + // + // struct T { + // float a[5]; // [1.0, 1.5, 1.75, 1.875, 1.9375] + // ivec4 b; // (1, 2, 3, 4) + // vec3 c; // (2.0, 2.5, 2.75) + // uint d; // 42u + // bool e; // Not used in test + // }; + // + // uniform block { + // T f; + // int g; // 22 + // uvec2 h; // (100u, 200u) + // }; + // + // void main() + // { + // T myT; + // + // myT.a[0] = 1.9375; + // myT.a[1] = 1.875; + // myT.a[2] = 1.75; + // myT.a[3] = 1.5; + // myT.a[4] = 1.0; + // + // myT.b.x = 4; + // myT.b.y = 3; + // myT.b.z = 2; + // myT.b.w = 1; + // + // myT.b.r = 22; + // + // myT.c[0] = 2.75; + // myT.c[0] = 2.5; + // myT.c[0] = 2.0; + // + // myT.d = 42u; + // myT.d = 100u; + // myT.d = 200u; + // + // myT.e = true; // No attempt to replace 'true' by a uniform value + // + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %14 "T" + OpMemberName %14 0 "a" + OpMemberName %14 1 "b" + OpMemberName %14 2 "c" + OpMemberName %14 3 "d" + OpMemberName %14 4 "e" + OpName %16 "myT" + OpName %61 "T" + OpMemberName %61 0 "a" + OpMemberName %61 1 "b" + OpMemberName %61 2 "c" + OpMemberName %61 3 "d" + OpMemberName %61 4 "e" + OpName %63 "block" + OpMemberName %63 0 "f" + OpMemberName %63 1 "g" + OpMemberName %63 2 "h" + OpName %65 "" + OpDecorate %60 ArrayStride 16 + OpMemberDecorate %61 0 Offset 0 + OpMemberDecorate %61 1 Offset 80 + OpMemberDecorate %61 2 Offset 96 + OpMemberDecorate %61 3 Offset 108 + OpMemberDecorate %61 4 Offset 112 + OpMemberDecorate %63 0 Offset 0 + OpMemberDecorate %63 1 Offset 128 + OpMemberDecorate %63 2 Offset 136 + OpDecorate %63 Block + OpDecorate %65 DescriptorSet 0 + OpDecorate %65 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 5 + %9 = OpTypeArray %6 %8 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 4 + %12 = OpTypeVector %6 3 + %13 = OpTypeBool + %14 = OpTypeStruct %9 %11 %12 %7 %13 + %15 = OpTypePointer Function %14 + %17 = OpConstant %10 0 + %18 = OpConstant %6 1.9375 + %19 = OpTypePointer Function %6 + %21 = OpConstant %10 1 + %22 = OpConstant %6 1.875 + %24 = OpConstant %10 2 + %25 = OpConstant %6 1.75 + %27 = OpConstant %10 3 + %28 = OpConstant %6 1.5 + %30 = OpConstant %10 4 + %31 = OpConstant %6 1 + %33 = OpConstant %7 0 + %34 = OpTypePointer Function %10 + %36 = OpConstant %7 1 + %38 = OpConstant %7 2 + %40 = OpConstant %7 3 + %42 = OpConstant %10 22 + %44 = OpConstant %6 2.75 + %46 = OpConstant %6 2.5 + %48 = OpConstant %6 2 + %50 = OpConstant %7 42 + %51 = OpTypePointer Function %7 + %53 = OpConstant %7 100 + %55 = OpConstant %7 200 + %57 = OpConstantTrue %13 + %58 = OpTypePointer Function %13 + %60 = OpTypeArray %6 %8 + %61 = OpTypeStruct %60 %11 %12 %7 %7 + %62 = OpTypeVector %7 2 + %63 = OpTypeStruct %61 %10 %62 + %64 = OpTypePointer Uniform %63 + %100 = OpTypePointer Uniform %10 + %101 = OpTypePointer Uniform %7 + %102 = OpTypePointer Uniform %6 + %103 = OpTypePointer Uniform %13 + %65 = OpVariable %64 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %16 = OpVariable %15 Function + %20 = OpAccessChain %19 %16 %17 %17 + OpStore %20 %18 + %23 = OpAccessChain %19 %16 %17 %21 + OpStore %23 %22 + %26 = OpAccessChain %19 %16 %17 %24 + OpStore %26 %25 + %29 = OpAccessChain %19 %16 %17 %27 + OpStore %29 %28 + %32 = OpAccessChain %19 %16 %17 %30 + OpStore %32 %31 + %35 = OpAccessChain %34 %16 %21 %33 + OpStore %35 %30 + %37 = OpAccessChain %34 %16 %21 %36 + OpStore %37 %27 + %39 = OpAccessChain %34 %16 %21 %38 + OpStore %39 %24 + %41 = OpAccessChain %34 %16 %21 %40 + OpStore %41 %21 + %43 = OpAccessChain %34 %16 %21 %33 + OpStore %43 %42 + %45 = OpAccessChain %19 %16 %24 %33 + OpStore %45 %44 + %47 = OpAccessChain %19 %16 %24 %33 + OpStore %47 %46 + %49 = OpAccessChain %19 %16 %24 %33 + OpStore %49 %48 + %52 = OpAccessChain %51 %16 %27 + OpStore %52 %50 + %54 = OpAccessChain %51 %16 %27 + OpStore %54 %53 + %56 = OpAccessChain %51 %16 %27 + OpStore %56 %55 + %59 = OpAccessChain %58 %16 %30 + OpStore %59 %57 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + const float float_array_values[5] = {1.0, 1.5, 1.75, 1.875, 1.9375}; + uint32_t float_array_data[5]; + memcpy(&float_array_data, &float_array_values, sizeof(float_array_values)); + + const float float_vector_values[3] = {2.0, 2.5, 2.75}; + uint32_t float_vector_data[3]; + memcpy(&float_vector_data, &float_vector_values, sizeof(float_vector_values)); + + protobufs::UniformBufferElementDescriptor uniform_f_a_0 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 0}); + protobufs::UniformBufferElementDescriptor uniform_f_a_1 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 1}); + protobufs::UniformBufferElementDescriptor uniform_f_a_2 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 2}); + protobufs::UniformBufferElementDescriptor uniform_f_a_3 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 3}); + protobufs::UniformBufferElementDescriptor uniform_f_a_4 = + MakeUniformBufferElementDescriptor(0, 0, {0, 0, 4}); + + protobufs::UniformBufferElementDescriptor uniform_f_b_x = + MakeUniformBufferElementDescriptor(0, 0, {0, 1, 0}); + protobufs::UniformBufferElementDescriptor uniform_f_b_y = + MakeUniformBufferElementDescriptor(0, 0, {0, 1, 1}); + protobufs::UniformBufferElementDescriptor uniform_f_b_z = + MakeUniformBufferElementDescriptor(0, 0, {0, 1, 2}); + protobufs::UniformBufferElementDescriptor uniform_f_b_w = + MakeUniformBufferElementDescriptor(0, 0, {0, 1, 3}); + + protobufs::UniformBufferElementDescriptor uniform_f_c_x = + MakeUniformBufferElementDescriptor(0, 0, {0, 2, 0}); + protobufs::UniformBufferElementDescriptor uniform_f_c_y = + MakeUniformBufferElementDescriptor(0, 0, {0, 2, 1}); + protobufs::UniformBufferElementDescriptor uniform_f_c_z = + MakeUniformBufferElementDescriptor(0, 0, {0, 2, 2}); + + protobufs::UniformBufferElementDescriptor uniform_f_d = + MakeUniformBufferElementDescriptor(0, 0, {0, 3}); + + protobufs::UniformBufferElementDescriptor uniform_g = + MakeUniformBufferElementDescriptor(0, 0, {1}); + + protobufs::UniformBufferElementDescriptor uniform_h_x = + MakeUniformBufferElementDescriptor(0, 0, {2, 0}); + protobufs::UniformBufferElementDescriptor uniform_h_y = + MakeUniformBufferElementDescriptor(0, 0, {2, 1}); + + ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[0], + uniform_f_a_0)); + ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[1], + uniform_f_a_1)); + ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[2], + uniform_f_a_2)); + ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[3], + uniform_f_a_3)); + ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[4], + uniform_f_a_4)); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 1, uniform_f_b_x)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 2, uniform_f_b_y)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 3, uniform_f_b_z)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 4, uniform_f_b_w)); + + ASSERT_TRUE(AddFactHelper(&transformation_context, float_vector_data[0], + uniform_f_c_x)); + ASSERT_TRUE(AddFactHelper(&transformation_context, float_vector_data[1], + uniform_f_c_y)); + ASSERT_TRUE(AddFactHelper(&transformation_context, float_vector_data[2], + uniform_f_c_z)); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 42, uniform_f_d)); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 22, uniform_g)); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 100, uniform_h_x)); + ASSERT_TRUE(AddFactHelper(&transformation_context, 200, uniform_h_y)); + + std::vector transformations; + + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(18, MakeInstructionDescriptor(20, SpvOpStore, 0), 1), + uniform_f_a_4, 200, 201)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(22, MakeInstructionDescriptor(23, SpvOpStore, 0), 1), + uniform_f_a_3, 202, 203)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(25, MakeInstructionDescriptor(26, SpvOpStore, 0), 1), + uniform_f_a_2, 204, 205)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(28, MakeInstructionDescriptor(29, SpvOpStore, 0), 1), + uniform_f_a_1, 206, 207)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(31, MakeInstructionDescriptor(32, SpvOpStore, 0), 1), + uniform_f_a_0, 208, 209)); + + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(30, MakeInstructionDescriptor(35, SpvOpStore, 0), 1), + uniform_f_b_w, 210, 211)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(27, MakeInstructionDescriptor(37, SpvOpStore, 0), 1), + uniform_f_b_z, 212, 213)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(24, MakeInstructionDescriptor(39, SpvOpStore, 0), 1), + uniform_f_b_y, 214, 215)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(21, MakeInstructionDescriptor(41, SpvOpStore, 0), 1), + uniform_f_b_x, 216, 217)); + + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(44, MakeInstructionDescriptor(45, SpvOpStore, 0), 1), + uniform_f_c_z, 220, 221)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(46, MakeInstructionDescriptor(47, SpvOpStore, 0), 1), + uniform_f_c_y, 222, 223)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(48, MakeInstructionDescriptor(49, SpvOpStore, 0), 1), + uniform_f_c_x, 224, 225)); + + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(50, MakeInstructionDescriptor(52, SpvOpStore, 0), 1), + uniform_f_d, 226, 227)); + + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(53, MakeInstructionDescriptor(54, SpvOpStore, 0), 1), + uniform_h_x, 228, 229)); + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(55, MakeInstructionDescriptor(56, SpvOpStore, 0), 1), + uniform_h_y, 230, 231)); + + transformations.emplace_back(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor(42, MakeInstructionDescriptor(43, SpvOpStore, 0), 1), + uniform_g, 218, 219)); + + for (auto& transformation : transformations) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpName %4 "main" + OpName %14 "T" + OpMemberName %14 0 "a" + OpMemberName %14 1 "b" + OpMemberName %14 2 "c" + OpMemberName %14 3 "d" + OpMemberName %14 4 "e" + OpName %16 "myT" + OpName %61 "T" + OpMemberName %61 0 "a" + OpMemberName %61 1 "b" + OpMemberName %61 2 "c" + OpMemberName %61 3 "d" + OpMemberName %61 4 "e" + OpName %63 "block" + OpMemberName %63 0 "f" + OpMemberName %63 1 "g" + OpMemberName %63 2 "h" + OpName %65 "" + OpDecorate %60 ArrayStride 16 + OpMemberDecorate %61 0 Offset 0 + OpMemberDecorate %61 1 Offset 80 + OpMemberDecorate %61 2 Offset 96 + OpMemberDecorate %61 3 Offset 108 + OpMemberDecorate %61 4 Offset 112 + OpMemberDecorate %63 0 Offset 0 + OpMemberDecorate %63 1 Offset 128 + OpMemberDecorate %63 2 Offset 136 + OpDecorate %63 Block + OpDecorate %65 DescriptorSet 0 + OpDecorate %65 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 5 + %9 = OpTypeArray %6 %8 + %10 = OpTypeInt 32 1 + %11 = OpTypeVector %10 4 + %12 = OpTypeVector %6 3 + %13 = OpTypeBool + %14 = OpTypeStruct %9 %11 %12 %7 %13 + %15 = OpTypePointer Function %14 + %17 = OpConstant %10 0 + %18 = OpConstant %6 1.9375 + %19 = OpTypePointer Function %6 + %21 = OpConstant %10 1 + %22 = OpConstant %6 1.875 + %24 = OpConstant %10 2 + %25 = OpConstant %6 1.75 + %27 = OpConstant %10 3 + %28 = OpConstant %6 1.5 + %30 = OpConstant %10 4 + %31 = OpConstant %6 1 + %33 = OpConstant %7 0 + %34 = OpTypePointer Function %10 + %36 = OpConstant %7 1 + %38 = OpConstant %7 2 + %40 = OpConstant %7 3 + %42 = OpConstant %10 22 + %44 = OpConstant %6 2.75 + %46 = OpConstant %6 2.5 + %48 = OpConstant %6 2 + %50 = OpConstant %7 42 + %51 = OpTypePointer Function %7 + %53 = OpConstant %7 100 + %55 = OpConstant %7 200 + %57 = OpConstantTrue %13 + %58 = OpTypePointer Function %13 + %60 = OpTypeArray %6 %8 + %61 = OpTypeStruct %60 %11 %12 %7 %7 + %62 = OpTypeVector %7 2 + %63 = OpTypeStruct %61 %10 %62 + %64 = OpTypePointer Uniform %63 + %100 = OpTypePointer Uniform %10 + %101 = OpTypePointer Uniform %7 + %102 = OpTypePointer Uniform %6 + %103 = OpTypePointer Uniform %13 + %65 = OpVariable %64 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %16 = OpVariable %15 Function + %20 = OpAccessChain %19 %16 %17 %17 + %200 = OpAccessChain %102 %65 %17 %17 %30 + %201 = OpLoad %6 %200 + OpStore %20 %201 + %23 = OpAccessChain %19 %16 %17 %21 + %202 = OpAccessChain %102 %65 %17 %17 %27 + %203 = OpLoad %6 %202 + OpStore %23 %203 + %26 = OpAccessChain %19 %16 %17 %24 + %204 = OpAccessChain %102 %65 %17 %17 %24 + %205 = OpLoad %6 %204 + OpStore %26 %205 + %29 = OpAccessChain %19 %16 %17 %27 + %206 = OpAccessChain %102 %65 %17 %17 %21 + %207 = OpLoad %6 %206 + OpStore %29 %207 + %32 = OpAccessChain %19 %16 %17 %30 + %208 = OpAccessChain %102 %65 %17 %17 %17 + %209 = OpLoad %6 %208 + OpStore %32 %209 + %35 = OpAccessChain %34 %16 %21 %33 + %210 = OpAccessChain %100 %65 %17 %21 %27 + %211 = OpLoad %10 %210 + OpStore %35 %211 + %37 = OpAccessChain %34 %16 %21 %36 + %212 = OpAccessChain %100 %65 %17 %21 %24 + %213 = OpLoad %10 %212 + OpStore %37 %213 + %39 = OpAccessChain %34 %16 %21 %38 + %214 = OpAccessChain %100 %65 %17 %21 %21 + %215 = OpLoad %10 %214 + OpStore %39 %215 + %41 = OpAccessChain %34 %16 %21 %40 + %216 = OpAccessChain %100 %65 %17 %21 %17 + %217 = OpLoad %10 %216 + OpStore %41 %217 + %43 = OpAccessChain %34 %16 %21 %33 + %218 = OpAccessChain %100 %65 %21 + %219 = OpLoad %10 %218 + OpStore %43 %219 + %45 = OpAccessChain %19 %16 %24 %33 + %220 = OpAccessChain %102 %65 %17 %24 %24 + %221 = OpLoad %6 %220 + OpStore %45 %221 + %47 = OpAccessChain %19 %16 %24 %33 + %222 = OpAccessChain %102 %65 %17 %24 %21 + %223 = OpLoad %6 %222 + OpStore %47 %223 + %49 = OpAccessChain %19 %16 %24 %33 + %224 = OpAccessChain %102 %65 %17 %24 %17 + %225 = OpLoad %6 %224 + OpStore %49 %225 + %52 = OpAccessChain %51 %16 %27 + %226 = OpAccessChain %101 %65 %17 %27 + %227 = OpLoad %7 %226 + OpStore %52 %227 + %54 = OpAccessChain %51 %16 %27 + %228 = OpAccessChain %101 %65 %24 %17 + %229 = OpLoad %7 %228 + OpStore %54 %229 + %56 = OpAccessChain %51 %16 %27 + %230 = OpAccessChain %101 %65 %24 %21 + %231 = OpLoad %7 %230 + OpStore %56 %231 + %59 = OpAccessChain %58 %16 %30 + OpStore %59 %57 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after, context.get())); +} + +TEST(TransformationReplaceConstantWithUniformTest, + DoNotReplaceVariableInitializer) { + // If a local variable has a constant initializer, this cannot be replaced + // by a uniform. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource GLSL 450 + OpMemberDecorate %16 0 Offset 0 + OpDecorate %16 Block + OpDecorate %18 DescriptorSet 0 + OpDecorate %18 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %50 = OpConstant %6 0 + %16 = OpTypeStruct %6 + %17 = OpTypePointer Uniform %16 + %51 = OpTypePointer Uniform %6 + %18 = OpVariable %17 Uniform + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function %50 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + protobufs::UniformBufferElementDescriptor blockname_a = + MakeUniformBufferElementDescriptor(0, 0, {0}); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 0, blockname_a)); + + ASSERT_FALSE(TransformationReplaceConstantWithUniform( + MakeIdUseDescriptor( + 50, MakeInstructionDescriptor(8, SpvOpVariable, 0), 1), + blockname_a, 100, 101) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceConstantWithUniformTest, ReplaceOpPhiOperand) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %32 DescriptorSet 0 + OpDecorate %32 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 2 + %13 = OpConstant %6 4 + %21 = OpConstant %6 1 + %34 = OpConstant %6 0 + %10 = OpTypeBool + %30 = OpTypeStruct %6 + %31 = OpTypePointer Uniform %30 + %32 = OpVariable %31 Uniform + %33 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %11 = OpLabel + OpBranch %5 + %5 = OpLabel + %23 = OpPhi %6 %7 %11 %20 %15 + %9 = OpSLessThan %10 %23 %13 + OpLoopMerge %8 %15 None + OpBranchConditional %9 %15 %8 + %15 = OpLabel + %20 = OpIAdd %6 %23 %21 + OpBranch %5 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto int_descriptor = MakeUniformBufferElementDescriptor(0, 0, {0}); + + ASSERT_TRUE(AddFactHelper(&transformation_context, 2, int_descriptor)); + + { + TransformationReplaceConstantWithUniform transformation( + MakeIdUseDescriptor(7, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0), + int_descriptor, 50, 51); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpDecorate %32 DescriptorSet 0 + OpDecorate %32 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 2 + %13 = OpConstant %6 4 + %21 = OpConstant %6 1 + %34 = OpConstant %6 0 + %10 = OpTypeBool + %30 = OpTypeStruct %6 + %31 = OpTypePointer Uniform %30 + %32 = OpVariable %31 Uniform + %33 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %11 = OpLabel + %50 = OpAccessChain %33 %32 %34 + %51 = OpLoad %6 %50 + OpBranch %5 + %5 = OpLabel + %23 = OpPhi %6 %51 %11 %20 %15 + %9 = OpSLessThan %10 %23 %13 + OpLoopMerge %8 %15 None + OpBranchConditional %9 %15 %8 + %15 = OpLabel + %20 = OpIAdd %6 %23 %21 + OpBranch %5 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp new file mode 100644 index 0000000..bcd04af --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_copy_memory_with_load_store_test.cpp @@ -0,0 +1,158 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_copy_memory_with_load_store.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceCopyMemoryWithLoadStoreTest, BasicScenarios) { + // This is a simple transformation and this test handles the main cases. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %14 "c" + OpName %16 "d" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 2 + %17 = OpConstant %12 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %16 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpStore %16 %17 + OpCopyMemory %8 %10 + OpCopyMemory %16 %14 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto instruction_descriptor_invalid_1 = + MakeInstructionDescriptor(5, SpvOpStore, 0); + auto instruction_descriptor_valid_1 = + MakeInstructionDescriptor(5, SpvOpCopyMemory, 0); + auto instruction_descriptor_valid_2 = + MakeInstructionDescriptor(5, SpvOpCopyMemory, 0); + + // Invalid: |source_id| is not a fresh id. + auto transformation_invalid_1 = TransformationReplaceCopyMemoryWithLoadStore( + 15, instruction_descriptor_valid_1); + ASSERT_FALSE(transformation_invalid_1.IsApplicable(context.get(), + transformation_context)); + + // Invalid: |instruction_descriptor_invalid| refers to an instruction OpStore. + auto transformation_invalid_2 = TransformationReplaceCopyMemoryWithLoadStore( + 20, instruction_descriptor_invalid_1); + ASSERT_FALSE(transformation_invalid_2.IsApplicable(context.get(), + transformation_context)); + + auto transformation_valid_1 = TransformationReplaceCopyMemoryWithLoadStore( + 20, instruction_descriptor_valid_1); + ASSERT_TRUE(transformation_valid_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_valid_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_valid_2 = TransformationReplaceCopyMemoryWithLoadStore( + 21, instruction_descriptor_valid_2); + ASSERT_TRUE(transformation_valid_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_valid_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %14 "c" + OpName %16 "d" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 2 + %17 = OpConstant %12 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %16 = OpVariable %13 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpStore %16 %17 + %20 = OpLoad %6 %10 + OpStore %8 %20 + %21 = OpLoad %12 %14 + OpStore %16 %21 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp new file mode 100644 index 0000000..fa8c068 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_copy_object_with_store_load_test.cpp @@ -0,0 +1,267 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_copy_object_with_store_load.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceCopyObjectWithStoreLoad, BasicScenarios) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %23 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %14 "c" + OpName %16 "d" + OpName %18 "e" + OpName %23 "f" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 2 + %17 = OpConstant %12 3 + %22 = OpTypePointer Private %12 + %23 = OpVariable %22 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %16 = OpVariable %13 Function + %18 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpStore %16 %17 + %19 = OpLoad %6 %8 + %20 = OpLoad %6 %10 + %21 = OpIAdd %6 %19 %20 + OpStore %18 %21 + %24 = OpLoad %12 %14 + %25 = OpLoad %12 %16 + %26 = OpFMul %12 %24 %25 + OpStore %23 %26 + %27 = OpCopyObject %6 %21 + %28 = OpCopyObject %12 %26 + %40 = OpCopyObject %13 %14 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Invalid: fresh_variable_id=10 is not fresh. + auto transformation_invalid_1 = TransformationReplaceCopyObjectWithStoreLoad( + 27, 10, SpvStorageClassFunction, 9); + ASSERT_FALSE(transformation_invalid_1.IsApplicable(context.get(), + transformation_context)); + + // Invalid: copy_object_result_id=26 is not a CopyObject instruction. + auto transformation_invalid_2 = TransformationReplaceCopyObjectWithStoreLoad( + 26, 30, SpvStorageClassFunction, 9); + ASSERT_FALSE(transformation_invalid_2.IsApplicable(context.get(), + transformation_context)); + + // Invalid: copy_object_result_id=40 is of type pointer. + auto transformation_invalid_3 = TransformationReplaceCopyObjectWithStoreLoad( + 40, 30, SpvStorageClassFunction, 9); + ASSERT_FALSE(transformation_invalid_3.IsApplicable(context.get(), + transformation_context)); + + // Invalid: Pointer type instruction in this storage class pointing to the + // value type is not defined. + auto transformation_invalid_4 = TransformationReplaceCopyObjectWithStoreLoad( + 40, 30, SpvStorageClassPrivate, 9); + ASSERT_FALSE(transformation_invalid_4.IsApplicable(context.get(), + transformation_context)); + + // Invalid: initializer_id=15 has the wrong type relative to the OpCopyObject + // instruction. + auto transformation_invalid_5 = TransformationReplaceCopyObjectWithStoreLoad( + 27, 30, SpvStorageClassFunction, 15); + ASSERT_FALSE(transformation_invalid_5.IsApplicable(context.get(), + transformation_context)); + + // Invalid: SpvStorageClassUniform is not applicable to the transformation. + auto transformation_invalid_6 = TransformationReplaceCopyObjectWithStoreLoad( + 27, 30, SpvStorageClassUniform, 9); + ASSERT_FALSE(transformation_invalid_6.IsApplicable(context.get(), + transformation_context)); + + auto transformation_valid_1 = TransformationReplaceCopyObjectWithStoreLoad( + 27, 30, SpvStorageClassFunction, 9); + ASSERT_TRUE(transformation_valid_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_valid_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_valid_2 = TransformationReplaceCopyObjectWithStoreLoad( + 28, 32, SpvStorageClassPrivate, 15); + ASSERT_TRUE(transformation_valid_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_valid_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %23 %32 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %14 "c" + OpName %16 "d" + OpName %18 "e" + OpName %23 "f" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %12 = OpTypeFloat 32 + %13 = OpTypePointer Function %12 + %15 = OpConstant %12 2 + %17 = OpConstant %12 3 + %22 = OpTypePointer Private %12 + %23 = OpVariable %22 Private + %32 = OpVariable %22 Private %15 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %30 = OpVariable %7 Function %9 + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %14 = OpVariable %13 Function + %16 = OpVariable %13 Function + %18 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %14 %15 + OpStore %16 %17 + %19 = OpLoad %6 %8 + %20 = OpLoad %6 %10 + %21 = OpIAdd %6 %19 %20 + OpStore %18 %21 + %24 = OpLoad %12 %14 + %25 = OpLoad %12 %16 + %26 = OpFMul %12 %24 %25 + OpStore %23 %26 + OpStore %30 %21 + %27 = OpLoad %6 %30 + OpStore %32 %26 + %28 = OpLoad %12 %32 + %40 = OpCopyObject %13 %14 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceCopyObjectWithStoreLoad, IrrelevantIdsAndDeadBlocks) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %30 = OpTypePointer Function %6 + %10 = OpConstant %6 0 + %11 = OpConstant %6 1 + %13 = OpTypeBool + %14 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %16 + %15 = OpLabel + %50 = OpCopyObject %6 %10 + OpBranch %16 + %16 = OpLabel + %51 = OpCopyObject %6 %11 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + transformation_context.GetFactManager()->AddFactBlockIsDead(15); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(11); + + auto transformation_1 = TransformationReplaceCopyObjectWithStoreLoad( + 50, 100, SpvStorageClassFunction, 10); + ASSERT_TRUE( + transformation_1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation_1, context.get(), + &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(100, {}), MakeDataDescriptor(50, {}))); + + auto transformation_2 = TransformationReplaceCopyObjectWithStoreLoad( + 51, 101, SpvStorageClassFunction, 10); + ASSERT_TRUE( + transformation_2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation_2, context.get(), + &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(101, {}), MakeDataDescriptor(51, {}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp new file mode 100644 index 0000000..5c10fc5 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_id_with_synonym_test.cpp @@ -0,0 +1,1793 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_id_with_synonym.h" + +#include "gtest/gtest.h" +#include "source/fuzz/data_descriptor.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +// The following shader was obtained from this GLSL, which was then optimized +// with spirv-opt -O and manually edited to include some uses of OpCopyObject +// (to introduce id synonyms). +// +// #version 310 es +// +// precision highp int; +// precision highp float; +// +// layout(set = 0, binding = 0) uniform buf { +// int a; +// int b; +// int c; +// }; +// +// layout(location = 0) out vec4 color; +// +// void main() { +// int x = a; +// float f = 0.0; +// while (x < b) { +// switch(x % 4) { +// case 0: +// color[0] = f; +// break; +// case 1: +// color[1] = f; +// break; +// case 2: +// color[2] = f; +// break; +// case 3: +// color[3] = f; +// break; +// default: +// break; +// } +// if (x > c) { +// x++; +// } else { +// x += 2; +// } +// } +// color[0] += color[1] + float(x); +// } +const std::string kComplexShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %42 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "buf" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpMemberName %9 2 "c" + OpName %11 "" + OpName %42 "color" + OpMemberDecorate %9 0 Offset 0 + OpMemberDecorate %9 1 Offset 4 + OpMemberDecorate %9 2 Offset 8 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + OpDecorate %42 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeStruct %6 %6 %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypePointer Uniform %6 + %16 = OpTypeFloat 32 + %19 = OpConstant %16 0 + %26 = OpConstant %6 1 + %29 = OpTypeBool + %32 = OpConstant %6 4 + %40 = OpTypeVector %16 4 + %41 = OpTypePointer Output %40 + %42 = OpVariable %41 Output + %44 = OpTypeInt 32 0 + %45 = OpConstant %44 0 + %46 = OpTypePointer Output %16 + %50 = OpConstant %44 1 + %54 = OpConstant %44 2 + %58 = OpConstant %44 3 + %64 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %209 = OpCopyObject %6 %12 + %14 = OpAccessChain %13 %11 %12 + %15 = OpLoad %6 %14 + %200 = OpCopyObject %6 %15 + OpBranch %20 + %20 = OpLabel + %84 = OpPhi %6 %15 %5 %86 %69 + %27 = OpAccessChain %13 %11 %26 + %28 = OpLoad %6 %27 + %207 = OpCopyObject %6 %84 + %201 = OpCopyObject %6 %15 + %30 = OpSLessThan %29 %84 %28 + OpLoopMerge %22 %69 None + OpBranchConditional %30 %21 %22 + %21 = OpLabel + %33 = OpSMod %6 %84 %32 + %208 = OpCopyObject %6 %33 + OpSelectionMerge %39 None + OpSwitch %33 %38 0 %34 1 %35 2 %36 3 %37 + %38 = OpLabel + %202 = OpCopyObject %6 %15 + OpBranch %39 + %34 = OpLabel + %210 = OpCopyObject %16 %19 + %47 = OpAccessChain %46 %42 %45 + OpStore %47 %19 + OpBranch %39 + %35 = OpLabel + %51 = OpAccessChain %46 %42 %50 + OpStore %51 %19 + OpBranch %39 + %36 = OpLabel + %204 = OpCopyObject %44 %54 + %55 = OpAccessChain %46 %42 %54 + %203 = OpCopyObject %46 %55 + OpStore %55 %19 + OpBranch %39 + %37 = OpLabel + %59 = OpAccessChain %46 %42 %58 + OpStore %59 %19 + OpBranch %39 + %39 = OpLabel + %300 = OpIAdd %6 %15 %15 + %65 = OpAccessChain %13 %11 %64 + %66 = OpLoad %6 %65 + %67 = OpSGreaterThan %29 %84 %66 + OpSelectionMerge %1000 None + OpBranchConditional %67 %68 %72 + %68 = OpLabel + %71 = OpIAdd %6 %84 %26 + OpBranch %1000 + %72 = OpLabel + %74 = OpIAdd %6 %84 %64 + %205 = OpCopyObject %6 %74 + OpBranch %1000 + %1000 = OpLabel + %86 = OpPhi %6 %71 %68 %74 %72 + %301 = OpPhi %6 %71 %68 %15 %72 + OpBranch %69 + %69 = OpLabel + OpBranch %20 + %22 = OpLabel + %75 = OpAccessChain %46 %42 %50 + %76 = OpLoad %16 %75 + %78 = OpConvertSToF %16 %84 + %80 = OpAccessChain %46 %42 %45 + %206 = OpCopyObject %16 %78 + %81 = OpLoad %16 %80 + %79 = OpFAdd %16 %76 %78 + %82 = OpFAdd %16 %81 %79 + OpStore %80 %82 + OpReturn + OpFunctionEnd +)"; + +protobufs::Fact MakeSynonymFact(uint32_t first, uint32_t second) { + protobufs::FactDataSynonym data_synonym_fact; + *data_synonym_fact.mutable_data1() = MakeDataDescriptor(first, {}); + *data_synonym_fact.mutable_data2() = MakeDataDescriptor(second, {}); + protobufs::Fact result; + *result.mutable_data_synonym_fact() = data_synonym_fact; + return result; +} + +// Equips the fact manager with synonym facts for the above shader. +void SetUpIdSynonyms(FactManager* fact_manager) { + fact_manager->MaybeAddFact(MakeSynonymFact(15, 200)); + fact_manager->MaybeAddFact(MakeSynonymFact(15, 201)); + fact_manager->MaybeAddFact(MakeSynonymFact(15, 202)); + fact_manager->MaybeAddFact(MakeSynonymFact(55, 203)); + fact_manager->MaybeAddFact(MakeSynonymFact(54, 204)); + fact_manager->MaybeAddFact(MakeSynonymFact(74, 205)); + fact_manager->MaybeAddFact(MakeSynonymFact(78, 206)); + fact_manager->MaybeAddFact(MakeSynonymFact(84, 207)); + fact_manager->MaybeAddFact(MakeSynonymFact(33, 208)); + fact_manager->MaybeAddFact(MakeSynonymFact(12, 209)); + fact_manager->MaybeAddFact(MakeSynonymFact(19, 210)); +} + +TEST(TransformationReplaceIdWithSynonymTest, IllegalTransformations) { + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, kComplexShader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + SetUpIdSynonyms(transformation_context.GetFactManager()); + + // %202 cannot replace %15 as in-operand 0 of %300, since %202 does not + // dominate %300. + auto synonym_does_not_dominate_use = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(15, MakeInstructionDescriptor(300, SpvOpIAdd, 0), 0), + 202); + ASSERT_FALSE(synonym_does_not_dominate_use.IsApplicable( + context.get(), transformation_context)); + + // %202 cannot replace %15 as in-operand 2 of %301, since this is the OpPhi's + // incoming value for block %72, and %202 does not dominate %72. + auto synonym_does_not_dominate_use_op_phi = + TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(15, MakeInstructionDescriptor(301, SpvOpPhi, 0), + 2), + 202); + ASSERT_FALSE(synonym_does_not_dominate_use_op_phi.IsApplicable( + context.get(), transformation_context)); + + // %200 is not a synonym for %84 + auto id_in_use_is_not_synonymous = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 84, MakeInstructionDescriptor(67, SpvOpSGreaterThan, 0), 0), + 200); + ASSERT_FALSE(id_in_use_is_not_synonymous.IsApplicable( + context.get(), transformation_context)); + + // %86 is not a synonym for anything (and in particular not for %74) + auto id_has_no_synonyms = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(86, MakeInstructionDescriptor(84, SpvOpPhi, 0), 2), + 74); + ASSERT_FALSE( + id_has_no_synonyms.IsApplicable(context.get(), transformation_context)); + + // This would lead to %207 = 'OpCopyObject %type %207' if it were allowed + auto synonym_use_is_in_synonym_definition = + TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 84, MakeInstructionDescriptor(207, SpvOpCopyObject, 0), 0), + 207); + ASSERT_FALSE(synonym_use_is_in_synonym_definition.IsApplicable( + context.get(), transformation_context)); + + // The id use descriptor does not lead to a use (%84 is not used in the + // definition of %207) + auto bad_id_use_descriptor = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 84, MakeInstructionDescriptor(200, SpvOpCopyObject, 0), 0), + 207); + ASSERT_FALSE(bad_id_use_descriptor.IsApplicable(context.get(), + transformation_context)); + + // This replacement would lead to an access chain into a struct using a + // non-constant index. + auto bad_access_chain = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 12, MakeInstructionDescriptor(14, SpvOpAccessChain, 0), 1), + 209); + ASSERT_FALSE( + bad_access_chain.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceIdWithSynonymTest, LegalTransformations) { + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, kComplexShader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + SetUpIdSynonyms(transformation_context.GetFactManager()); + + auto global_constant_synonym = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(19, MakeInstructionDescriptor(47, SpvOpStore, 0), 1), + 210); + ASSERT_TRUE(global_constant_synonym.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(global_constant_synonym, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto replace_vector_access_chain_index = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 54, MakeInstructionDescriptor(55, SpvOpAccessChain, 0), 1), + 204); + ASSERT_TRUE(replace_vector_access_chain_index.IsApplicable( + context.get(), transformation_context)); + ApplyAndCheckFreshIds(replace_vector_access_chain_index, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // This is an interesting case because it replaces something that is being + // copied with something that is already a synonym. + auto regular_replacement = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 15, MakeInstructionDescriptor(202, SpvOpCopyObject, 0), 0), + 201); + ASSERT_TRUE( + regular_replacement.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(regular_replacement, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto regular_replacement2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(55, MakeInstructionDescriptor(203, SpvOpStore, 0), 0), + 203); + ASSERT_TRUE( + regular_replacement2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(regular_replacement2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto good_op_phi = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(74, MakeInstructionDescriptor(86, SpvOpPhi, 0), 2), + 205); + ASSERT_TRUE(good_op_phi.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(good_op_phi, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %42 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %9 "buf" + OpMemberName %9 0 "a" + OpMemberName %9 1 "b" + OpMemberName %9 2 "c" + OpName %11 "" + OpName %42 "color" + OpMemberDecorate %9 0 Offset 0 + OpMemberDecorate %9 1 Offset 4 + OpMemberDecorate %9 2 Offset 8 + OpDecorate %9 Block + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + OpDecorate %42 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %9 = OpTypeStruct %6 %6 %6 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypePointer Uniform %6 + %16 = OpTypeFloat 32 + %19 = OpConstant %16 0 + %26 = OpConstant %6 1 + %29 = OpTypeBool + %32 = OpConstant %6 4 + %40 = OpTypeVector %16 4 + %41 = OpTypePointer Output %40 + %42 = OpVariable %41 Output + %44 = OpTypeInt 32 0 + %45 = OpConstant %44 0 + %46 = OpTypePointer Output %16 + %50 = OpConstant %44 1 + %54 = OpConstant %44 2 + %58 = OpConstant %44 3 + %64 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %209 = OpCopyObject %6 %12 + %14 = OpAccessChain %13 %11 %12 + %15 = OpLoad %6 %14 + %200 = OpCopyObject %6 %15 + OpBranch %20 + %20 = OpLabel + %84 = OpPhi %6 %15 %5 %86 %69 + %27 = OpAccessChain %13 %11 %26 + %28 = OpLoad %6 %27 + %207 = OpCopyObject %6 %84 + %201 = OpCopyObject %6 %15 + %30 = OpSLessThan %29 %84 %28 + OpLoopMerge %22 %69 None + OpBranchConditional %30 %21 %22 + %21 = OpLabel + %33 = OpSMod %6 %84 %32 + %208 = OpCopyObject %6 %33 + OpSelectionMerge %39 None + OpSwitch %33 %38 0 %34 1 %35 2 %36 3 %37 + %38 = OpLabel + %202 = OpCopyObject %6 %201 + OpBranch %39 + %34 = OpLabel + %210 = OpCopyObject %16 %19 + %47 = OpAccessChain %46 %42 %45 + OpStore %47 %210 + OpBranch %39 + %35 = OpLabel + %51 = OpAccessChain %46 %42 %50 + OpStore %51 %19 + OpBranch %39 + %36 = OpLabel + %204 = OpCopyObject %44 %54 + %55 = OpAccessChain %46 %42 %204 + %203 = OpCopyObject %46 %55 + OpStore %203 %19 + OpBranch %39 + %37 = OpLabel + %59 = OpAccessChain %46 %42 %58 + OpStore %59 %19 + OpBranch %39 + %39 = OpLabel + %300 = OpIAdd %6 %15 %15 + %65 = OpAccessChain %13 %11 %64 + %66 = OpLoad %6 %65 + %67 = OpSGreaterThan %29 %84 %66 + OpSelectionMerge %1000 None + OpBranchConditional %67 %68 %72 + %68 = OpLabel + %71 = OpIAdd %6 %84 %26 + OpBranch %1000 + %72 = OpLabel + %74 = OpIAdd %6 %84 %64 + %205 = OpCopyObject %6 %74 + OpBranch %1000 + %1000 = OpLabel + %86 = OpPhi %6 %71 %68 %205 %72 + %301 = OpPhi %6 %71 %68 %15 %72 + OpBranch %69 + %69 = OpLabel + OpBranch %20 + %22 = OpLabel + %75 = OpAccessChain %46 %42 %50 + %76 = OpLoad %16 %75 + %78 = OpConvertSToF %16 %84 + %80 = OpAccessChain %46 %42 %45 + %206 = OpCopyObject %16 %78 + %81 = OpLoad %16 %80 + %79 = OpFAdd %16 %76 %78 + %82 = OpFAdd %16 %81 %79 + OpStore %80 %82 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfVariables) { + // The following SPIR-V comes from this GLSL, with object copies added: + // + // #version 310 es + // + // precision highp int; + // + // int g; + // + // void main() { + // int l; + // l = g; + // g = l; + // } + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "l" + OpName %10 "g" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypePointer Private %6 + %10 = OpVariable %9 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpCopyObject %9 %10 + %101 = OpCopyObject %7 %8 + %11 = OpLoad %6 %10 + OpStore %8 %11 + %12 = OpLoad %6 %8 + OpStore %10 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(10, 100)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(8, 101)); + + // Replace %10 with %100 in: + // %11 = OpLoad %6 %10 + auto replacement1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(10, MakeInstructionDescriptor(11, SpvOpLoad, 0), 0), + 100); + ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %8 with %101 in: + // OpStore %8 %11 + auto replacement2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(8, MakeInstructionDescriptor(11, SpvOpStore, 0), 0), + 101); + ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %8 with %101 in: + // %12 = OpLoad %6 %8 + auto replacement3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(8, MakeInstructionDescriptor(12, SpvOpLoad, 0), 0), + 101); + ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement3, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replace %10 with %100 in: + // OpStore %10 %12 + auto replacement4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(10, MakeInstructionDescriptor(12, SpvOpStore, 0), 0), + 100); + ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "l" + OpName %10 "g" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypePointer Private %6 + %10 = OpVariable %9 Private + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %100 = OpCopyObject %9 %10 + %101 = OpCopyObject %7 %8 + %11 = OpLoad %6 %100 + OpStore %101 %11 + %12 = OpLoad %6 %101 + OpStore %100 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, + SynonymOfVariableNoGoodInFunctionCall) { + // The following SPIR-V comes from this GLSL, with an object copy added: + // + // #version 310 es + // + // precision highp int; + // + // void foo(int x) { } + // + // void main() { + // int a; + // a = 2; + // foo(a); + // } + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %10 "foo(i1;" + OpName %9 "x" + OpName %12 "a" + OpName %14 "param" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %7 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %12 = OpVariable %7 Function + %14 = OpVariable %7 Function + OpStore %12 %13 + %15 = OpLoad %6 %12 + OpStore %14 %15 + %100 = OpCopyObject %7 %14 + %16 = OpFunctionCall %2 %10 %14 + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %7 + %11 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(14, 100)); + + // Replace %14 with %100 in: + // %16 = OpFunctionCall %2 %10 %14 + auto replacement = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 14, MakeInstructionDescriptor(16, SpvOpFunctionCall, 0), 1), + 100); + ASSERT_FALSE(replacement.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceIdWithSynonymTest, SynonymsOfAccessChainIndices) { + // The following SPIR-V comes from this GLSL, with object copies added: + // + // #version 310 es + // + // precision highp float; + // precision highp int; + // + // struct S { + // int[3] a; + // vec4 b; + // bool c; + // } d; + // + // float[20] e; + // + // struct T { + // float f; + // S g; + // } h; + // + // T[4] i; + // + // void main() { + // d.a[2] = 10; + // d.b[3] = 11.0; + // d.c = false; + // e[17] = 12.0; + // h.f = 13.0; + // h.g.a[1] = 14; + // h.g.b[0] = 15.0; + // h.g.c = true; + // i[0].f = 16.0; + // i[1].g.a[0] = 17; + // i[2].g.b[1] = 18.0; + // i[3].g.c = true; + // } + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "S" + OpMemberName %13 0 "a" + OpMemberName %13 1 "b" + OpMemberName %13 2 "c" + OpName %15 "d" + OpName %31 "e" + OpName %35 "T" + OpMemberName %35 0 "f" + OpMemberName %35 1 "g" + OpName %37 "h" + OpName %50 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 3 + %9 = OpTypeArray %6 %8 + %10 = OpTypeFloat 32 + %11 = OpTypeVector %10 4 + %12 = OpTypeBool + %13 = OpTypeStruct %9 %11 %12 + %14 = OpTypePointer Private %13 + %15 = OpVariable %14 Private + %16 = OpConstant %6 0 + %17 = OpConstant %6 2 + %18 = OpConstant %6 10 + %19 = OpTypePointer Private %6 + %21 = OpConstant %6 1 + %22 = OpConstant %10 11 + %23 = OpTypePointer Private %10 + %25 = OpConstantFalse %12 + %26 = OpTypePointer Private %12 + %28 = OpConstant %7 20 + %29 = OpTypeArray %10 %28 + %30 = OpTypePointer Private %29 + %31 = OpVariable %30 Private + %32 = OpConstant %6 17 + %33 = OpConstant %10 12 + %35 = OpTypeStruct %10 %13 + %36 = OpTypePointer Private %35 + %37 = OpVariable %36 Private + %38 = OpConstant %10 13 + %40 = OpConstant %6 14 + %42 = OpConstant %10 15 + %43 = OpConstant %7 0 + %45 = OpConstantTrue %12 + %47 = OpConstant %7 4 + %48 = OpTypeArray %35 %47 + %49 = OpTypePointer Private %48 + %50 = OpVariable %49 Private + %51 = OpConstant %10 16 + %54 = OpConstant %10 18 + %55 = OpConstant %7 1 + %57 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + + %100 = OpCopyObject %6 %16 ; 0 + %101 = OpCopyObject %6 %21 ; 1 + %102 = OpCopyObject %6 %17 ; 2 + %103 = OpCopyObject %6 %57 ; 3 + %104 = OpCopyObject %6 %18 ; 10 + %105 = OpCopyObject %6 %40 ; 14 + %106 = OpCopyObject %6 %32 ; 17 + %107 = OpCopyObject %7 %43 ; 0 + %108 = OpCopyObject %7 %55 ; 1 + %109 = OpCopyObject %7 %8 ; 3 + %110 = OpCopyObject %7 %47 ; 4 + %111 = OpCopyObject %7 %28 ; 20 + %112 = OpCopyObject %12 %45 ; true + + %20 = OpAccessChain %19 %15 %16 %17 + OpStore %20 %18 + %24 = OpAccessChain %23 %15 %21 %8 + OpStore %24 %22 + %27 = OpAccessChain %26 %15 %17 + OpStore %27 %25 + %34 = OpAccessChain %23 %31 %32 + OpStore %34 %33 + %39 = OpAccessChain %23 %37 %16 + OpStore %39 %38 + %41 = OpAccessChain %19 %37 %21 %16 %21 + OpStore %41 %40 + %44 = OpAccessChain %23 %37 %21 %21 %43 + OpStore %44 %42 + %46 = OpAccessChain %26 %37 %21 %17 + OpStore %46 %45 + %52 = OpAccessChain %23 %50 %16 %16 + OpStore %52 %51 + %53 = OpAccessChain %19 %50 %21 %21 %16 %16 + OpStore %53 %32 + %56 = OpAccessChain %23 %50 %17 %21 %21 %55 + OpStore %56 %54 + %58 = OpInBoundsAccessChain %26 %50 %57 %21 %17 + OpStore %58 %45 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Add synonym facts corresponding to the OpCopyObject operations that have + // been applied to all constants in the module. + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(16, 100)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(21, 101)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(17, 102)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(57, 103)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(18, 104)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(40, 105)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(32, 106)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(43, 107)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(55, 108)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(8, 109)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(47, 110)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(28, 111)); + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(45, 112)); + + // Replacements of the form %16 -> %100 + + // %20 = OpAccessChain %19 %15 *%16* %17 + // Corresponds to d.*a*[2] + // The index %16 used for a cannot be replaced + auto replacement1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 1), + 100); + ASSERT_FALSE( + replacement1.IsApplicable(context.get(), transformation_context)); + + // %39 = OpAccessChain %23 %37 *%16* + // Corresponds to h.*f* + // The index %16 used for f cannot be replaced + auto replacement2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(39, SpvOpAccessChain, 0), 1), + 100); + ASSERT_FALSE( + replacement2.IsApplicable(context.get(), transformation_context)); + + // %41 = OpAccessChain %19 %37 %21 *%16* %21 + // Corresponds to h.g.*a*[1] + // The index %16 used for a cannot be replaced + auto replacement3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 2), + 100); + ASSERT_FALSE( + replacement3.IsApplicable(context.get(), transformation_context)); + + // %52 = OpAccessChain %23 %50 *%16* %16 + // Corresponds to i[*0*].f + // The index %16 used for 0 *can* be replaced + auto replacement4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 1), + 100); + ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %52 = OpAccessChain %23 %50 %16 *%16* + // Corresponds to i[0].*f* + // The index %16 used for f cannot be replaced + auto replacement5 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(52, SpvOpAccessChain, 0), 2), + 100); + ASSERT_FALSE( + replacement5.IsApplicable(context.get(), transformation_context)); + + // %53 = OpAccessChain %19 %50 %21 %21 *%16* %16 + // Corresponds to i[1].g.*a*[0] + // The index %16 used for a cannot be replaced + auto replacement6 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 3), + 100); + ASSERT_FALSE( + replacement6.IsApplicable(context.get(), transformation_context)); + + // %53 = OpAccessChain %19 %50 %21 %21 %16 *%16* + // Corresponds to i[1].g.a[*0*] + // The index %16 used for 0 *can* be replaced + auto replacement7 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 16, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 4), + 100); + ASSERT_TRUE(replacement7.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement7, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replacements of the form %21 -> %101 + + // %24 = OpAccessChain %23 %15 *%21* %8 + // Corresponds to d.*b*[3] + // The index %24 used for b cannot be replaced + auto replacement8 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), 1), + 101); + ASSERT_FALSE( + replacement8.IsApplicable(context.get(), transformation_context)); + + // %41 = OpAccessChain %19 %37 *%21* %16 %21 + // Corresponds to h.*g*.a[1] + // The index %24 used for g cannot be replaced + auto replacement9 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 1), + 101); + ASSERT_FALSE( + replacement9.IsApplicable(context.get(), transformation_context)); + + // %41 = OpAccessChain %19 %37 %21 %16 *%21* + // Corresponds to h.g.a[*1*] + // The index %24 used for 1 *can* be replaced + auto replacement10 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(41, SpvOpAccessChain, 0), 3), + 101); + ASSERT_TRUE( + replacement10.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement10, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %44 = OpAccessChain %23 %37 *%21* %21 %43 + // Corresponds to h.*g*.b[0] + // The index %24 used for g cannot be replaced + auto replacement11 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 1), + 101); + ASSERT_FALSE( + replacement11.IsApplicable(context.get(), transformation_context)); + + // %44 = OpAccessChain %23 %37 %21 *%21* %43 + // Corresponds to h.g.*b*[0] + // The index %24 used for b cannot be replaced + auto replacement12 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 2), + 101); + ASSERT_FALSE( + replacement12.IsApplicable(context.get(), transformation_context)); + + // %46 = OpAccessChain %26 %37 *%21* %17 + // Corresponds to h.*g*.c + // The index %24 used for g cannot be replaced + auto replacement13 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 1), + 101); + ASSERT_FALSE( + replacement13.IsApplicable(context.get(), transformation_context)); + + // %53 = OpAccessChain %19 %50 *%21* %21 %16 %16 + // Corresponds to i[*1*].g.a[0] + // The index %24 used for 1 *can* be replaced + auto replacement14 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 1), + 101); + ASSERT_TRUE( + replacement14.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement14, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %53 = OpAccessChain %19 %50 %21 *%21* %16 %16 + // Corresponds to i[1].*g*.a[0] + // The index %24 used for g cannot be replaced + auto replacement15 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(53, SpvOpAccessChain, 0), 2), + 101); + ASSERT_FALSE( + replacement15.IsApplicable(context.get(), transformation_context)); + + // %56 = OpAccessChain %23 %50 %17 *%21* %21 %55 + // Corresponds to i[2].*g*.b[1] + // The index %24 used for g cannot be replaced + auto replacement16 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 2), + 101); + ASSERT_FALSE( + replacement16.IsApplicable(context.get(), transformation_context)); + + // %56 = OpAccessChain %23 %50 %17 %21 *%21* %55 + // Corresponds to i[2].g.*b*[1] + // The index %24 used for b cannot be replaced + auto replacement17 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 3), + 101); + ASSERT_FALSE( + replacement17.IsApplicable(context.get(), transformation_context)); + + // %58 = OpInBoundsAccessChain %26 %50 %57 *%21* %17 + // Corresponds to i[3].*g*.c + // The index %24 used for g cannot be replaced + auto replacement18 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 21, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 2), + 101); + ASSERT_FALSE( + replacement18.IsApplicable(context.get(), transformation_context)); + + // Replacements of the form %17 -> %102 + + // %20 = OpAccessChain %19 %15 %16 %17 + // Corresponds to d.a[*2*] + // The index %17 used for 2 *can* be replaced + auto replacement19 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(20, SpvOpAccessChain, 0), 2), + 102); + ASSERT_TRUE( + replacement19.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement19, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %27 = OpAccessChain %26 %15 %17 + // Corresponds to d.c + // The index %17 used for c cannot be replaced + auto replacement20 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(27, SpvOpAccessChain, 0), 1), + 102); + ASSERT_FALSE( + replacement20.IsApplicable(context.get(), transformation_context)); + + // %46 = OpAccessChain %26 %37 %21 %17 + // Corresponds to h.g.*c* + // The index %17 used for c cannot be replaced + auto replacement21 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(46, SpvOpAccessChain, 0), 2), + 102); + ASSERT_FALSE( + replacement21.IsApplicable(context.get(), transformation_context)); + + // %56 = OpAccessChain %23 %50 %17 %21 %21 %55 + // Corresponds to i[*2*].g.b[1] + // The index %17 used for 2 *can* be replaced + auto replacement22 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 1), + 102); + ASSERT_TRUE( + replacement22.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement22, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // %58 = OpInBoundsAccessChain %26 %50 %57 %21 %17 + // Corresponds to i[3].g.*c* + // The index %17 used for c cannot be replaced + auto replacement23 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 17, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 3), + 102); + ASSERT_FALSE( + replacement23.IsApplicable(context.get(), transformation_context)); + + // Replacements of the form %57 -> %103 + + // %58 = OpInBoundsAccessChain %26 %50 *%57* %21 %17 + // Corresponds to i[*3*].g.c + // The index %57 used for 3 *can* be replaced + auto replacement24 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 57, MakeInstructionDescriptor(58, SpvOpInBoundsAccessChain, 0), 1), + 103); + ASSERT_TRUE( + replacement24.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement24, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replacements of the form %32 -> %106 + + // %34 = OpAccessChain %23 %31 *%32* + // Corresponds to e[*17*] + // The index %32 used for 17 *can* be replaced + auto replacement25 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 32, MakeInstructionDescriptor(34, SpvOpAccessChain, 0), 1), + 106); + ASSERT_TRUE( + replacement25.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement25, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replacements of the form %43 -> %107 + + // %44 = OpAccessChain %23 %37 %21 %21 *%43* + // Corresponds to h.g.b[*0*] + // The index %43 used for 0 *can* be replaced + auto replacement26 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 43, MakeInstructionDescriptor(44, SpvOpAccessChain, 0), 3), + 107); + ASSERT_TRUE( + replacement26.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement26, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replacements of the form %55 -> %108 + + // %56 = OpAccessChain %23 %50 %17 %21 %21 *%55* + // Corresponds to i[2].g.b[*1*] + // The index %55 used for 1 *can* be replaced + auto replacement27 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 55, MakeInstructionDescriptor(56, SpvOpAccessChain, 0), 4), + 108); + ASSERT_TRUE( + replacement27.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement27, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Replacements of the form %8 -> %109 + + // %24 = OpAccessChain %23 %15 %21 *%8* + // Corresponds to d.b[*3*] + // The index %8 used for 3 *can* be replaced + auto replacement28 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(8, MakeInstructionDescriptor(24, SpvOpAccessChain, 0), + 2), + 109); + ASSERT_TRUE( + replacement28.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement28, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %13 "S" + OpMemberName %13 0 "a" + OpMemberName %13 1 "b" + OpMemberName %13 2 "c" + OpName %15 "d" + OpName %31 "e" + OpName %35 "T" + OpMemberName %35 0 "f" + OpMemberName %35 1 "g" + OpName %37 "h" + OpName %50 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 3 + %9 = OpTypeArray %6 %8 + %10 = OpTypeFloat 32 + %11 = OpTypeVector %10 4 + %12 = OpTypeBool + %13 = OpTypeStruct %9 %11 %12 + %14 = OpTypePointer Private %13 + %15 = OpVariable %14 Private + %16 = OpConstant %6 0 + %17 = OpConstant %6 2 + %18 = OpConstant %6 10 + %19 = OpTypePointer Private %6 + %21 = OpConstant %6 1 + %22 = OpConstant %10 11 + %23 = OpTypePointer Private %10 + %25 = OpConstantFalse %12 + %26 = OpTypePointer Private %12 + %28 = OpConstant %7 20 + %29 = OpTypeArray %10 %28 + %30 = OpTypePointer Private %29 + %31 = OpVariable %30 Private + %32 = OpConstant %6 17 + %33 = OpConstant %10 12 + %35 = OpTypeStruct %10 %13 + %36 = OpTypePointer Private %35 + %37 = OpVariable %36 Private + %38 = OpConstant %10 13 + %40 = OpConstant %6 14 + %42 = OpConstant %10 15 + %43 = OpConstant %7 0 + %45 = OpConstantTrue %12 + %47 = OpConstant %7 4 + %48 = OpTypeArray %35 %47 + %49 = OpTypePointer Private %48 + %50 = OpVariable %49 Private + %51 = OpConstant %10 16 + %54 = OpConstant %10 18 + %55 = OpConstant %7 1 + %57 = OpConstant %6 3 + %4 = OpFunction %2 None %3 + %5 = OpLabel + + %100 = OpCopyObject %6 %16 ; 0 + %101 = OpCopyObject %6 %21 ; 1 + %102 = OpCopyObject %6 %17 ; 2 + %103 = OpCopyObject %6 %57 ; 3 + %104 = OpCopyObject %6 %18 ; 10 + %105 = OpCopyObject %6 %40 ; 14 + %106 = OpCopyObject %6 %32 ; 17 + %107 = OpCopyObject %7 %43 ; 0 + %108 = OpCopyObject %7 %55 ; 1 + %109 = OpCopyObject %7 %8 ; 3 + %110 = OpCopyObject %7 %47 ; 4 + %111 = OpCopyObject %7 %28 ; 20 + %112 = OpCopyObject %12 %45 ; true + + %20 = OpAccessChain %19 %15 %16 %102 + OpStore %20 %18 + %24 = OpAccessChain %23 %15 %21 %109 + OpStore %24 %22 + %27 = OpAccessChain %26 %15 %17 + OpStore %27 %25 + %34 = OpAccessChain %23 %31 %106 + OpStore %34 %33 + %39 = OpAccessChain %23 %37 %16 + OpStore %39 %38 + %41 = OpAccessChain %19 %37 %21 %16 %101 + OpStore %41 %40 + %44 = OpAccessChain %23 %37 %21 %21 %107 + OpStore %44 %42 + %46 = OpAccessChain %26 %37 %21 %17 + OpStore %46 %45 + %52 = OpAccessChain %23 %50 %100 %16 + OpStore %52 %51 + %53 = OpAccessChain %19 %50 %101 %21 %16 %100 + OpStore %53 %32 + %56 = OpAccessChain %23 %50 %102 %21 %21 %108 + OpStore %56 %54 + %58 = OpInBoundsAccessChain %26 %50 %103 %21 %17 + OpStore %58 %45 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, RuntimeArrayTest) { + // This checks that OpRuntimeArray is correctly handled. + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 ArrayStride 8 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypeRuntimeArray %7 + %9 = OpTypeStruct %8 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %50 = OpCopyObject %6 %12 + %51 = OpCopyObject %13 %14 + %16 = OpAccessChain %15 %11 %12 %12 %14 + OpStore %16 %12 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Add synonym fact relating %50 and %12. + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(50, 12)); + // Add synonym fact relating %51 and %14. + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(51, 14)); + + // Not legal because the index being replaced is a struct index. + ASSERT_FALSE( + TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 1), + 50) + .IsApplicable(context.get(), transformation_context)); + + // Fine to replace an index into a runtime array. + auto replacement1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 12, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 2), + 50); + ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context); + + // Fine to replace an index into a vector inside the runtime array. + auto replacement2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 14, MakeInstructionDescriptor(16, SpvOpAccessChain, 0), 3), + 51); + ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %8 ArrayStride 8 + OpMemberDecorate %9 0 Offset 0 + OpDecorate %9 BufferBlock + OpDecorate %11 DescriptorSet 0 + OpDecorate %11 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypeRuntimeArray %7 + %9 = OpTypeStruct %8 + %10 = OpTypePointer Uniform %9 + %11 = OpVariable %10 Uniform + %12 = OpConstant %6 0 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 0 + %15 = OpTypePointer Uniform %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %50 = OpCopyObject %6 %12 + %51 = OpCopyObject %13 %14 + %16 = OpAccessChain %15 %11 %12 %50 %51 + OpStore %16 %12 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, + DoNotReplaceSampleParameterOfOpImageTexelPointer) { + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %3 + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpConstant %6 2 + %9 = OpConstant %6 0 + %10 = OpConstant %6 10 + %11 = OpTypeBool + %12 = OpConstant %6 1 + %13 = OpTypeFloat 32 + %14 = OpTypePointer Image %13 + %15 = OpTypeImage %13 2D 0 0 0 0 Rgba8 + %16 = OpTypePointer Private %15 + %3 = OpVariable %16 Private + %17 = OpTypeVector %6 2 + %18 = OpConstantComposite %17 %9 %9 + %2 = OpFunction %4 None %5 + %19 = OpLabel + %100 = OpCopyObject %6 %9 + %20 = OpImageTexelPointer %14 %3 %18 %9 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Add synonym fact relating %100 and %9. + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(100, 9)); + + // Not legal the Sample argument of OpImageTexelPointer needs to be a zero + // constant. + ASSERT_FALSE( + TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 9, MakeInstructionDescriptor(20, SpvOpImageTexelPointer, 0), 2), + 100) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceIdWithSynonymTest, EquivalentIntegerConstants) { + // This checks that replacing an integer constant with an equivalent one with + // different signedness is allowed only when valid. + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpDecorate %3 RelaxedPrecision + %4 = OpTypeVoid + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeFunction %4 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 1 + %11 = OpTypeInt 32 0 + %12 = OpTypePointer Function %11 + %13 = OpConstant %11 1 + %2 = OpFunction %4 None %7 + %14 = OpLabel + %3 = OpVariable %9 Function + %15 = OpSNegate %8 %10 + %16 = OpIAdd %8 %10 %10 + %17 = OpSDiv %8 %10 %10 + %18 = OpUDiv %11 %13 %13 + %19 = OpBitwiseAnd %8 %10 %10 + %20 = OpSelect %8 %6 %10 %17 + %21 = OpIEqual %5 %10 %10 + OpStore %3 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Add synonym fact relating %10 and %13 (equivalent integer constant with + // different signedness). + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(10, 13)); + + // Legal because OpSNegate always considers the integer as signed + auto replacement1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(10, MakeInstructionDescriptor(15, SpvOpSNegate, 0), + 0), + 13); + ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context); + + // Legal because OpIAdd does not care about the signedness of the operands + auto replacement2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(10, MakeInstructionDescriptor(16, SpvOpIAdd, 0), 0), + 13); + ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context); + + // Legal because OpSDiv does not care about the signedness of the operands + auto replacement3 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(10, MakeInstructionDescriptor(17, SpvOpSDiv, 0), 0), + 13); + ASSERT_TRUE(replacement3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement3, context.get(), &transformation_context); + + // Not legal because OpUDiv requires unsigned integers + ASSERT_FALSE(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 13, MakeInstructionDescriptor(18, SpvOpUDiv, 0), 0), + 10) + .IsApplicable(context.get(), transformation_context)); + + // Legal because OpSDiv does not care about the signedness of the operands + auto replacement4 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(10, MakeInstructionDescriptor(19, SpvOpBitwiseAnd, 0), + 0), + 13); + ASSERT_TRUE(replacement4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement4, context.get(), &transformation_context); + + // Not legal because OpSelect requires both operands to have the same type as + // the result type + ASSERT_FALSE(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 10, MakeInstructionDescriptor(20, SpvOpUDiv, 0), 1), + 13) + .IsApplicable(context.get(), transformation_context)); + + // Not legal because OpStore requires the object to match the type pointed + // to by the pointer. + ASSERT_FALSE(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 10, MakeInstructionDescriptor(21, SpvOpStore, 0), 1), + 13) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpDecorate %3 RelaxedPrecision + %4 = OpTypeVoid + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpTypeFunction %4 + %8 = OpTypeInt 32 1 + %9 = OpTypePointer Function %8 + %10 = OpConstant %8 1 + %11 = OpTypeInt 32 0 + %12 = OpTypePointer Function %11 + %13 = OpConstant %11 1 + %2 = OpFunction %4 None %7 + %14 = OpLabel + %3 = OpVariable %9 Function + %15 = OpSNegate %8 %13 + %16 = OpIAdd %8 %13 %10 + %17 = OpSDiv %8 %13 %10 + %18 = OpUDiv %11 %13 %13 + %19 = OpBitwiseAnd %8 %13 %10 + %20 = OpSelect %8 %6 %10 %17 + %21 = OpIEqual %5 %10 %10 + OpStore %3 %10 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, EquivalentIntegerVectorConstants) { + // This checks that replacing an integer constant with an equivalent one with + // different signedness is allowed only when valid. + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpDecorate %3 RelaxedPrecision + OpDecorate %4 RelaxedPrecision + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpTypeVector %7 4 + %10 = OpTypeVector %8 4 + %11 = OpTypePointer Function %9 + %12 = OpConstant %7 1 + %13 = OpConstant %8 1 + %14 = OpConstantComposite %9 %12 %12 %12 %12 + %15 = OpConstantComposite %10 %13 %13 %13 %13 + %16 = OpTypePointer Function %7 + %2 = OpFunction %5 None %6 + %17 = OpLabel + %3 = OpVariable %11 Function + %18 = OpIAdd %9 %14 %14 + OpStore %3 %14 + %19 = OpAccessChain %16 %3 %13 + %4 = OpLoad %7 %19 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Add synonym fact relating %10 and %13 (equivalent integer vectors with + // different signedness). + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(14, 15)); + + // Legal because OpIAdd does not consider the signedness of the operands + auto replacement1 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor(14, MakeInstructionDescriptor(18, SpvOpIAdd, 0), 0), + 15); + ASSERT_TRUE(replacement1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement1, context.get(), &transformation_context); + + // Not legal because OpStore requires the object to match the type pointed + // to by the pointer. + ASSERT_FALSE(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 14, MakeInstructionDescriptor(18, SpvOpStore, 0), 1), + 15) + .IsApplicable(context.get(), transformation_context)); + + // Add synonym fact relating %12 and %13 (equivalent integer constants with + // different signedness). + transformation_context.GetFactManager()->MaybeAddFact( + MakeSynonymFact(12, 13)); + + // Legal because the indices of OpAccessChain are always treated as signed + auto replacement2 = TransformationReplaceIdWithSynonym( + MakeIdUseDescriptor( + 13, MakeInstructionDescriptor(19, SpvOpAccessChain, 0), 1), + 12); + ASSERT_TRUE(replacement2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(replacement2, context.get(), &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + const std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpDecorate %3 RelaxedPrecision + OpDecorate %4 RelaxedPrecision + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpTypeVector %7 4 + %10 = OpTypeVector %8 4 + %11 = OpTypePointer Function %9 + %12 = OpConstant %7 1 + %13 = OpConstant %8 1 + %14 = OpConstantComposite %9 %12 %12 %12 %12 + %15 = OpConstantComposite %10 %13 %13 %13 %13 + %16 = OpTypePointer Function %7 + %2 = OpFunction %5 None %6 + %17 = OpLabel + %3 = OpVariable %11 Function + %18 = OpIAdd %9 %15 %14 + OpStore %3 %14 + %19 = OpAccessChain %16 %3 %12 + %4 = OpLoad %7 %19 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIdWithSynonymTest, IncompatibleTypes) { + const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeInt 32 1 + %8 = OpTypeInt 32 0 + %9 = OpTypeFloat 32 + %12 = OpConstant %7 1 + %13 = OpConstant %8 1 + %10 = OpConstant %9 1 + %2 = OpFunction %5 None %6 + %17 = OpLabel + %18 = OpIAdd %7 %12 %13 + %19 = OpFAdd %9 %10 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto* op_i_add = context->get_def_use_mgr()->GetDef(18); + ASSERT_TRUE(op_i_add); + + auto* op_f_add = context->get_def_use_mgr()->GetDef(19); + ASSERT_TRUE(op_f_add); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(12, {}), MakeDataDescriptor(13, {})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(12, {}), MakeDataDescriptor(10, {})); + + // Synonym differs only in signedness for OpIAdd. + ASSERT_TRUE(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptorFromUse(context.get(), op_i_add, 0), 13) + .IsApplicable(context.get(), transformation_context)); + + // Synonym has wrong type for OpIAdd. + ASSERT_FALSE(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptorFromUse(context.get(), op_i_add, 0), 10) + .IsApplicable(context.get(), transformation_context)); + + // Synonym has wrong type for OpFAdd. + ASSERT_FALSE(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptorFromUse(context.get(), op_f_add, 0), 12) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceIdWithSynonym( + MakeIdUseDescriptorFromUse(context.get(), op_f_add, 0), 13) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_irrelevant_id_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_irrelevant_id_test.cpp new file mode 100644 index 0000000..c04a091 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_irrelevant_id_test.cpp @@ -0,0 +1,336 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_irrelevant_id.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/id_use_descriptor.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { +const std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpName %4 "b" + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpTypeInt 32 1 + %10 = OpTypePointer Function %9 + %11 = OpConstant %9 2 + %12 = OpTypeStruct %9 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 3 + %15 = OpTypeArray %12 %14 + %16 = OpTypePointer Function %15 + %17 = OpConstant %9 0 + %2 = OpFunction %5 None %6 + %18 = OpLabel + %3 = OpVariable %10 Function + %4 = OpVariable %10 Function + %19 = OpVariable %16 Function + OpStore %3 %11 + %20 = OpLoad %9 %3 + %21 = OpAccessChain %10 %19 %20 %17 + %22 = OpLoad %9 %21 + OpStore %4 %22 + %23 = OpLoad %9 %4 + %24 = OpIAdd %9 %20 %23 + %25 = OpISub %9 %23 %20 + OpReturn + OpFunctionEnd +)"; + +void SetUpIrrelevantIdFacts(FactManager* fact_manager) { + fact_manager->AddFactIdIsIrrelevant(17); + fact_manager->AddFactIdIsIrrelevant(23); + fact_manager->AddFactIdIsIrrelevant(24); + fact_manager->AddFactIdIsIrrelevant(25); +} + +TEST(TransformationReplaceIrrelevantIdTest, Inapplicable) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + SetUpIrrelevantIdFacts(transformation_context.GetFactManager()); + + auto instruction_21_descriptor = + MakeInstructionDescriptor(21, SpvOpAccessChain, 0); + auto instruction_24_descriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0); + + // %20 has not been declared as irrelevant. + ASSERT_FALSE(TransformationReplaceIrrelevantId( + MakeIdUseDescriptor(20, instruction_24_descriptor, 0), 23) + .IsApplicable(context.get(), transformation_context)); + + // %22 is not used in %24. + ASSERT_FALSE(TransformationReplaceIrrelevantId( + MakeIdUseDescriptor(22, instruction_24_descriptor, 1), 20) + .IsApplicable(context.get(), transformation_context)); + + // Replacement id %50 does not exist. + ASSERT_FALSE(TransformationReplaceIrrelevantId( + MakeIdUseDescriptor(23, instruction_24_descriptor, 1), 50) + .IsApplicable(context.get(), transformation_context)); + + // %25 is not available to use at %24. + ASSERT_FALSE(TransformationReplaceIrrelevantId( + MakeIdUseDescriptor(23, instruction_24_descriptor, 1), 25) + .IsApplicable(context.get(), transformation_context)); + + // %24 is not available to use at %24. + ASSERT_FALSE(TransformationReplaceIrrelevantId( + MakeIdUseDescriptor(23, instruction_24_descriptor, 1), 24) + .IsApplicable(context.get(), transformation_context)); + + // %8 has not the same type as %23. + ASSERT_FALSE(TransformationReplaceIrrelevantId( + MakeIdUseDescriptor(23, instruction_24_descriptor, 1), 8) + .IsApplicable(context.get(), transformation_context)); + + // %17 is an index to a struct in an access chain, so it can't be replaced. + ASSERT_FALSE(TransformationReplaceIrrelevantId( + MakeIdUseDescriptor(17, instruction_21_descriptor, 2), 20) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceIrrelevantIdTest, Apply) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + SetUpIrrelevantIdFacts(transformation_context.GetFactManager()); + + auto instruction_24_descriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0); + + // Replace the use of %23 in %24 with %22. + auto transformation = TransformationReplaceIrrelevantId( + MakeIdUseDescriptor(23, instruction_24_descriptor, 1), 22); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + OpName %3 "a" + OpName %4 "b" + %5 = OpTypeVoid + %6 = OpTypeFunction %5 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + %9 = OpTypeInt 32 1 + %10 = OpTypePointer Function %9 + %11 = OpConstant %9 2 + %12 = OpTypeStruct %9 + %13 = OpTypeInt 32 0 + %14 = OpConstant %13 3 + %15 = OpTypeArray %12 %14 + %16 = OpTypePointer Function %15 + %17 = OpConstant %9 0 + %2 = OpFunction %5 None %6 + %18 = OpLabel + %3 = OpVariable %10 Function + %4 = OpVariable %10 Function + %19 = OpVariable %16 Function + OpStore %3 %11 + %20 = OpLoad %9 %3 + %21 = OpAccessChain %10 %19 %20 %17 + %22 = OpLoad %9 %21 + OpStore %4 %22 + %23 = OpLoad %9 %4 + %24 = OpIAdd %9 %20 %22 + %25 = OpISub %9 %23 %20 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceIrrelevantIdTest, + DoNotReplaceVariableInitializerWithNonConstant) { + // Checks that it is not possible to replace the initializer of a variable + // with a non-constant id (such as a function parameter). + const std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %2 %6 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %10 = OpFunction %2 None %8 + %9 = OpFunctionParameter %6 + %11 = OpLabel + %12 = OpVariable %7 Function %13 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(13); + + // We cannot replace the use of %13 in the initializer of %12 with %9 because + // %9 is not a constant. + ASSERT_FALSE(TransformationReplaceIrrelevantId( + MakeIdUseDescriptor( + 13, MakeInstructionDescriptor(12, SpvOpVariable, 0), 1), + 9) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceIrrelevantIdTest, + DoNotReplaceIrrelevantIdWithOpFunction) { + // Checks that an OpFunction result id is not allowed to be used to replace an + // irrelevant id. + const std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFunction %6 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpCopyObject %6 %13 + %21 = OpCopyObject %6 %20 + OpReturn + OpFunctionEnd + %10 = OpFunction %6 None %7 + %11 = OpLabel + OpReturnValue %13 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(20); + + // We cannot replace the use of %20 in by %21 with %10 because %10 is an + // OpFunction instruction. + ASSERT_FALSE( + TransformationReplaceIrrelevantId( + MakeIdUseDescriptor( + 20, MakeInstructionDescriptor(21, SpvOpCopyObject, 0), 0), + 10) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceIrrelevantIdTest, OpAccessChainIrrelevantIndex) { + // Checks that we can't replace irrelevant index operands in OpAccessChain. + const std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstant %6 2 + %13 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + %12 = OpAccessChain %13 %9 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(10); + + // We cannot replace the use of %10 in %12 with %11 because %10 is an + // irrelevant id. + ASSERT_FALSE( + TransformationReplaceIrrelevantId( + MakeIdUseDescriptor( + 10, MakeInstructionDescriptor(12, SpvOpAccessChain, 0), 1), + 11) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp new file mode 100644 index 0000000..8ec5552 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_linear_algebra_instruction_test.cpp @@ -0,0 +1,2486 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_linear_algebra_instruction.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceLinearAlgebraInstructionTest, IsApplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %22 "main" + OpExecutionMode %22 OriginUpperLeft + OpSource ESSL 310 + OpName %22 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpConstant %4 1 + %9 = OpConstant %4 2 + %10 = OpConstant %4 3 + %11 = OpConstant %4 4 + %12 = OpConstant %4 5 + %13 = OpConstant %4 6 + %14 = OpConstant %4 7 + %15 = OpConstant %4 8 + %16 = OpConstantComposite %5 %8 %9 + %17 = OpConstantComposite %5 %10 %11 + %18 = OpConstantComposite %6 %8 %9 %10 + %19 = OpConstantComposite %6 %11 %12 %13 + %20 = OpConstantComposite %7 %8 %9 %10 %11 + %21 = OpConstantComposite %7 %12 %13 %14 %15 + %22 = OpFunction %2 None %3 + %23 = OpLabel + %24 = OpDot %4 %16 %17 + %25 = OpDot %4 %18 %19 + %26 = OpDot %4 %20 %21 + %27 = OpVectorTimesScalar %5 %16 %8 + %28 = OpVectorTimesScalar %6 %18 %9 + %29 = OpVectorTimesScalar %7 %20 %10 + %30 = OpCopyObject %4 %24 + %31 = OpFAdd %4 %8 %9 + %32 = OpFMul %4 %10 %11 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests linear algebra instructions. + auto instruction_descriptor = MakeInstructionDescriptor(24, SpvOpDot, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36, 37, 38}, instruction_descriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = + MakeInstructionDescriptor(27, SpvOpVectorTimesScalar, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36}, instruction_descriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests non-linear algebra instructions. + instruction_descriptor = MakeInstructionDescriptor(30, SpvOpCopyObject, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36, 37, 38}, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = MakeInstructionDescriptor(31, SpvOpFAdd, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36, 37}, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = MakeInstructionDescriptor(32, SpvOpFMul, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36}, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests number of fresh ids is different than necessary. + instruction_descriptor = MakeInstructionDescriptor(25, SpvOpDot, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36}, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = + MakeInstructionDescriptor(28, SpvOpVectorTimesScalar, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36, 37, 38, 39}, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests non-fresh ids. + instruction_descriptor = MakeInstructionDescriptor(26, SpvOpDot, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 5, 36, 37, 8, 39, 40, 1, 42, 3, 44, 45, 46}, + instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instruction_descriptor = + MakeInstructionDescriptor(29, SpvOpVectorTimesScalar, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36, 7, 38, 9, 40}, instruction_descriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceLinearAlgebraInstructionTest, ReplaceOpTranspose) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %54 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Transposing a 2x2 matrix + %56 = OpTranspose %8 %45 + +; Transposing a 2x3 matrix + %57 = OpTranspose %11 %46 + +; Transposing a 2x4 matrix + %58 = OpTranspose %14 %47 + +; Transposing a 3x2 matrix + %59 = OpTranspose %9 %48 + +; Transposing a 3x3 matrix + %60 = OpTranspose %12 %49 + +; Transposing a 3x4 matrix + %61 = OpTranspose %15 %50 + +; Transposing a 4x2 matrix + %62 = OpTranspose %10 %51 + +; Transposing a 4x3 matrix + %63 = OpTranspose %13 %52 + +; Transposing a 4x4 matrix + %64 = OpTranspose %16 %53 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(56, SpvOpTranspose, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {65, 66, 67, 68, 69, 70, 71, 72, 73, 74}, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(57, SpvOpTranspose, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(58, SpvOpTranspose, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(59, SpvOpTranspose, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(60, SpvOpTranspose, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %54 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Transposing a 2x2 matrix + %65 = OpCompositeExtract %5 %45 0 + %66 = OpCompositeExtract %4 %65 0 + %67 = OpCompositeExtract %5 %45 1 + %68 = OpCompositeExtract %4 %67 0 + %69 = OpCompositeConstruct %5 %66 %68 + %70 = OpCompositeExtract %5 %45 0 + %71 = OpCompositeExtract %4 %70 1 + %72 = OpCompositeExtract %5 %45 1 + %73 = OpCompositeExtract %4 %72 1 + %74 = OpCompositeConstruct %5 %71 %73 + %56 = OpCompositeConstruct %8 %69 %74 + +; Transposing a 2x3 matrix + %75 = OpCompositeExtract %5 %46 0 + %76 = OpCompositeExtract %4 %75 0 + %77 = OpCompositeExtract %5 %46 1 + %78 = OpCompositeExtract %4 %77 0 + %79 = OpCompositeExtract %5 %46 2 + %80 = OpCompositeExtract %4 %79 0 + %81 = OpCompositeConstruct %6 %76 %78 %80 + %82 = OpCompositeExtract %5 %46 0 + %83 = OpCompositeExtract %4 %82 1 + %84 = OpCompositeExtract %5 %46 1 + %85 = OpCompositeExtract %4 %84 1 + %86 = OpCompositeExtract %5 %46 2 + %87 = OpCompositeExtract %4 %86 1 + %88 = OpCompositeConstruct %6 %83 %85 %87 + %57 = OpCompositeConstruct %11 %81 %88 + +; Transposing a 2x4 matrix + %89 = OpCompositeExtract %5 %47 0 + %90 = OpCompositeExtract %4 %89 0 + %91 = OpCompositeExtract %5 %47 1 + %92 = OpCompositeExtract %4 %91 0 + %93 = OpCompositeExtract %5 %47 2 + %94 = OpCompositeExtract %4 %93 0 + %95 = OpCompositeExtract %5 %47 3 + %96 = OpCompositeExtract %4 %95 0 + %97 = OpCompositeConstruct %7 %90 %92 %94 %96 + %98 = OpCompositeExtract %5 %47 0 + %99 = OpCompositeExtract %4 %98 1 + %100 = OpCompositeExtract %5 %47 1 + %101 = OpCompositeExtract %4 %100 1 + %102 = OpCompositeExtract %5 %47 2 + %103 = OpCompositeExtract %4 %102 1 + %104 = OpCompositeExtract %5 %47 3 + %105 = OpCompositeExtract %4 %104 1 + %106 = OpCompositeConstruct %7 %99 %101 %103 %105 + %58 = OpCompositeConstruct %14 %97 %106 + +; Transposing a 3x2 matrix + %107 = OpCompositeExtract %6 %48 0 + %108 = OpCompositeExtract %4 %107 0 + %109 = OpCompositeExtract %6 %48 1 + %110 = OpCompositeExtract %4 %109 0 + %111 = OpCompositeConstruct %5 %108 %110 + %112 = OpCompositeExtract %6 %48 0 + %113 = OpCompositeExtract %4 %112 1 + %114 = OpCompositeExtract %6 %48 1 + %115 = OpCompositeExtract %4 %114 1 + %116 = OpCompositeConstruct %5 %113 %115 + %117 = OpCompositeExtract %6 %48 0 + %118 = OpCompositeExtract %4 %117 2 + %119 = OpCompositeExtract %6 %48 1 + %120 = OpCompositeExtract %4 %119 2 + %121 = OpCompositeConstruct %5 %118 %120 + %59 = OpCompositeConstruct %9 %111 %116 %121 + +; Transposing a 3x3 matrix + %122 = OpCompositeExtract %6 %49 0 + %123 = OpCompositeExtract %4 %122 0 + %124 = OpCompositeExtract %6 %49 1 + %125 = OpCompositeExtract %4 %124 0 + %126 = OpCompositeExtract %6 %49 2 + %127 = OpCompositeExtract %4 %126 0 + %128 = OpCompositeConstruct %6 %123 %125 %127 + %129 = OpCompositeExtract %6 %49 0 + %130 = OpCompositeExtract %4 %129 1 + %131 = OpCompositeExtract %6 %49 1 + %132 = OpCompositeExtract %4 %131 1 + %133 = OpCompositeExtract %6 %49 2 + %134 = OpCompositeExtract %4 %133 1 + %135 = OpCompositeConstruct %6 %130 %132 %134 + %136 = OpCompositeExtract %6 %49 0 + %137 = OpCompositeExtract %4 %136 2 + %138 = OpCompositeExtract %6 %49 1 + %139 = OpCompositeExtract %4 %138 2 + %140 = OpCompositeExtract %6 %49 2 + %141 = OpCompositeExtract %4 %140 2 + %142 = OpCompositeConstruct %6 %137 %139 %141 + %60 = OpCompositeConstruct %12 %128 %135 %142 + +; Transposing a 3x4 matrix + %61 = OpTranspose %15 %50 + +; Transposing a 4x2 matrix + %62 = OpTranspose %10 %51 + +; Transposing a 4x3 matrix + %63 = OpTranspose %13 %52 + +; Transposing a 4x4 matrix + %64 = OpTranspose %16 %53 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationReplaceLinearAlgebraInstructionTest, + ReplaceOpVectorTimesScalar) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %15 "main" + OpExecutionMode %15 OriginUpperLeft + OpSource ESSL 310 + OpName %15 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpConstant %4 1 + %9 = OpConstant %4 2 + %10 = OpConstant %4 3 + %11 = OpConstant %4 4 + %12 = OpConstantComposite %5 %8 %9 + %13 = OpConstantComposite %6 %8 %9 %10 + %14 = OpConstantComposite %7 %8 %9 %10 %11 + %15 = OpFunction %2 None %3 + %16 = OpLabel + %17 = OpVectorTimesScalar %5 %12 %8 + %18 = OpVectorTimesScalar %6 %13 %9 + %19 = OpVectorTimesScalar %7 %14 %10 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(17, SpvOpVectorTimesScalar, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {20, 21, 22, 23}, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(18, SpvOpVectorTimesScalar, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {24, 25, 26, 27, 28, 29}, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(19, SpvOpVectorTimesScalar, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {30, 31, 32, 33, 34, 35, 36, 37}, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %15 "main" + OpExecutionMode %15 OriginUpperLeft + OpSource ESSL 310 + OpName %15 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpConstant %4 1 + %9 = OpConstant %4 2 + %10 = OpConstant %4 3 + %11 = OpConstant %4 4 + %12 = OpConstantComposite %5 %8 %9 + %13 = OpConstantComposite %6 %8 %9 %10 + %14 = OpConstantComposite %7 %8 %9 %10 %11 + %15 = OpFunction %2 None %3 + %16 = OpLabel + %20 = OpCompositeExtract %4 %12 0 + %21 = OpFMul %4 %20 %8 + %22 = OpCompositeExtract %4 %12 1 + %23 = OpFMul %4 %22 %8 + %17 = OpCompositeConstruct %5 %21 %23 + %24 = OpCompositeExtract %4 %13 0 + %25 = OpFMul %4 %24 %9 + %26 = OpCompositeExtract %4 %13 1 + %27 = OpFMul %4 %26 %9 + %28 = OpCompositeExtract %4 %13 2 + %29 = OpFMul %4 %28 %9 + %18 = OpCompositeConstruct %6 %25 %27 %29 + %30 = OpCompositeExtract %4 %14 0 + %31 = OpFMul %4 %30 %10 + %32 = OpCompositeExtract %4 %14 1 + %33 = OpFMul %4 %32 %10 + %34 = OpCompositeExtract %4 %14 2 + %35 = OpFMul %4 %34 %10 + %36 = OpCompositeExtract %4 %14 3 + %37 = OpFMul %4 %36 %10 + %19 = OpCompositeConstruct %7 %31 %33 %35 %37 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationReplaceLinearAlgebraInstructionTest, + ReplaceOpMatrixTimesScalar) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %54 "main" + OpExecutionMode %54 OriginUpperLeft + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Multiplying 2-row matrices by scalar + %56 = OpMatrixTimesScalar %8 %45 %17 + %57 = OpMatrixTimesScalar %9 %46 %18 + %58 = OpMatrixTimesScalar %10 %47 %19 + +; Multiplying 3-row matrices by scalar + %59 = OpMatrixTimesScalar %11 %48 %21 + %60 = OpMatrixTimesScalar %12 %49 %22 + %61 = OpMatrixTimesScalar %13 %50 %23 + +; Multiplying 4-row matrices by scalar + %62 = OpMatrixTimesScalar %14 %51 %24 + %63 = OpMatrixTimesScalar %15 %52 %25 + %64 = OpMatrixTimesScalar %16 %53 %26 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(56, SpvOpMatrixTimesScalar, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76}, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(57, SpvOpMatrixTimesScalar, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(58, SpvOpMatrixTimesScalar, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %54 "main" + OpExecutionMode %54 OriginUpperLeft + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Multiplying 2x2 matrix by scalar + %65 = OpCompositeExtract %5 %45 0 + %66 = OpCompositeExtract %4 %65 0 + %67 = OpFMul %4 %66 %17 + %68 = OpCompositeExtract %4 %65 1 + %69 = OpFMul %4 %68 %17 + %70 = OpCompositeConstruct %5 %67 %69 + %71 = OpCompositeExtract %5 %45 1 + %72 = OpCompositeExtract %4 %71 0 + %73 = OpFMul %4 %72 %17 + %74 = OpCompositeExtract %4 %71 1 + %75 = OpFMul %4 %74 %17 + %76 = OpCompositeConstruct %5 %73 %75 + %56 = OpCompositeConstruct %8 %70 %76 + +; Multiplying 2x3 matrix by scalar + %77 = OpCompositeExtract %5 %46 0 + %78 = OpCompositeExtract %4 %77 0 + %79 = OpFMul %4 %78 %18 + %80 = OpCompositeExtract %4 %77 1 + %81 = OpFMul %4 %80 %18 + %82 = OpCompositeConstruct %5 %79 %81 + %83 = OpCompositeExtract %5 %46 1 + %84 = OpCompositeExtract %4 %83 0 + %85 = OpFMul %4 %84 %18 + %86 = OpCompositeExtract %4 %83 1 + %87 = OpFMul %4 %86 %18 + %88 = OpCompositeConstruct %5 %85 %87 + %89 = OpCompositeExtract %5 %46 2 + %90 = OpCompositeExtract %4 %89 0 + %91 = OpFMul %4 %90 %18 + %92 = OpCompositeExtract %4 %89 1 + %93 = OpFMul %4 %92 %18 + %94 = OpCompositeConstruct %5 %91 %93 + %57 = OpCompositeConstruct %9 %82 %88 %94 + +; Multiplying 2x4 matrix by scalar + %95 = OpCompositeExtract %5 %47 0 + %96 = OpCompositeExtract %4 %95 0 + %97 = OpFMul %4 %96 %19 + %98 = OpCompositeExtract %4 %95 1 + %99 = OpFMul %4 %98 %19 + %100 = OpCompositeConstruct %5 %97 %99 + %101 = OpCompositeExtract %5 %47 1 + %102 = OpCompositeExtract %4 %101 0 + %103 = OpFMul %4 %102 %19 + %104 = OpCompositeExtract %4 %101 1 + %105 = OpFMul %4 %104 %19 + %106 = OpCompositeConstruct %5 %103 %105 + %107 = OpCompositeExtract %5 %47 2 + %108 = OpCompositeExtract %4 %107 0 + %109 = OpFMul %4 %108 %19 + %110 = OpCompositeExtract %4 %107 1 + %111 = OpFMul %4 %110 %19 + %112 = OpCompositeConstruct %5 %109 %111 + %113 = OpCompositeExtract %5 %47 3 + %114 = OpCompositeExtract %4 %113 0 + %115 = OpFMul %4 %114 %19 + %116 = OpCompositeExtract %4 %113 1 + %117 = OpFMul %4 %116 %19 + %118 = OpCompositeConstruct %5 %115 %117 + %58 = OpCompositeConstruct %10 %100 %106 %112 %118 + +; Multiplying 3-row matrices by scalar + %59 = OpMatrixTimesScalar %11 %48 %21 + %60 = OpMatrixTimesScalar %12 %49 %22 + %61 = OpMatrixTimesScalar %13 %50 %23 + +; Multiplying 4-row matrices by scalar + %62 = OpMatrixTimesScalar %14 %51 %24 + %63 = OpMatrixTimesScalar %15 %52 %25 + %64 = OpMatrixTimesScalar %16 %53 %26 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationReplaceLinearAlgebraInstructionTest, + ReplaceOpVectorTimesMatrix) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %54 "main" + OpExecutionMode %54 OriginUpperLeft + OpSource ESSL 310 + OpName %54 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Multiplying 2-dimensional vector by 2x2 matrix + %56 = OpVectorTimesMatrix %5 %33 %45 + +; Multiplying 2-dimensional vector by 2x3 matrix + %57 = OpVectorTimesMatrix %6 %34 %46 + +; Multiplying 2-dimensional vector by 2x4 matrix + %58 = OpVectorTimesMatrix %7 %35 %47 + +; Multiplying 3-dimensional vector by 3x2 matrix + %59 = OpVectorTimesMatrix %5 %37 %48 + +; Multiplying 3-dimensional vector by 3x3 matrix + %60 = OpVectorTimesMatrix %6 %38 %49 + +; Multiplying 3-dimensional vector by 3x4 matrix + %61 = OpVectorTimesMatrix %7 %39 %50 + +; Multiplying 4-dimensional vector by 4x2 matrix + %62 = OpVectorTimesMatrix %5 %41 %51 + +; Multiplying 4-dimensional vector by 4x3 matrix + %63 = OpVectorTimesMatrix %6 %42 %52 + +; Multiplying 4-dimensional vector by 4x4 matrix + %64 = OpVectorTimesMatrix %7 %43 %53 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(56, SpvOpVectorTimesMatrix, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(57, SpvOpVectorTimesMatrix, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, 95, 96, 97, 98}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(58, SpvOpVectorTimesMatrix, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(59, SpvOpVectorTimesMatrix, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %54 "main" + OpExecutionMode %54 OriginUpperLeft + OpSource ESSL 310 + OpName %54 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Multiplying 2-dimensional vector by 2x2 matrix + %65 = OpCompositeExtract %4 %33 0 + %66 = OpCompositeExtract %4 %33 1 + %67 = OpCompositeExtract %5 %45 0 + %68 = OpCompositeExtract %4 %67 0 + %69 = OpFMul %4 %65 %68 + %70 = OpCompositeExtract %4 %67 1 + %71 = OpFMul %4 %66 %70 + %72 = OpFAdd %4 %69 %71 + %73 = OpCompositeExtract %5 %45 1 + %74 = OpCompositeExtract %4 %73 0 + %75 = OpFMul %4 %65 %74 + %76 = OpCompositeExtract %4 %73 1 + %77 = OpFMul %4 %66 %76 + %78 = OpFAdd %4 %75 %77 + %56 = OpCompositeConstruct %5 %72 %78 + +; Multiplying 2-dimensional vector by 2x3 matrix + %79 = OpCompositeExtract %4 %34 0 + %80 = OpCompositeExtract %4 %34 1 + %81 = OpCompositeExtract %5 %46 0 + %82 = OpCompositeExtract %4 %81 0 + %83 = OpFMul %4 %79 %82 + %84 = OpCompositeExtract %4 %81 1 + %85 = OpFMul %4 %80 %84 + %86 = OpFAdd %4 %83 %85 + %87 = OpCompositeExtract %5 %46 1 + %88 = OpCompositeExtract %4 %87 0 + %89 = OpFMul %4 %79 %88 + %90 = OpCompositeExtract %4 %87 1 + %91 = OpFMul %4 %80 %90 + %92 = OpFAdd %4 %89 %91 + %93 = OpCompositeExtract %5 %46 2 + %94 = OpCompositeExtract %4 %93 0 + %95 = OpFMul %4 %79 %94 + %96 = OpCompositeExtract %4 %93 1 + %97 = OpFMul %4 %80 %96 + %98 = OpFAdd %4 %95 %97 + %57 = OpCompositeConstruct %6 %86 %92 %98 + +; Multiplying 2-dimensional vector by 2x4 matrix + %99 = OpCompositeExtract %4 %35 0 + %100 = OpCompositeExtract %4 %35 1 + %101 = OpCompositeExtract %5 %47 0 + %102 = OpCompositeExtract %4 %101 0 + %103 = OpFMul %4 %99 %102 + %104 = OpCompositeExtract %4 %101 1 + %105 = OpFMul %4 %100 %104 + %106 = OpFAdd %4 %103 %105 + %107 = OpCompositeExtract %5 %47 1 + %108 = OpCompositeExtract %4 %107 0 + %109 = OpFMul %4 %99 %108 + %110 = OpCompositeExtract %4 %107 1 + %111 = OpFMul %4 %100 %110 + %112 = OpFAdd %4 %109 %111 + %113 = OpCompositeExtract %5 %47 2 + %114 = OpCompositeExtract %4 %113 0 + %115 = OpFMul %4 %99 %114 + %116 = OpCompositeExtract %4 %113 1 + %117 = OpFMul %4 %100 %116 + %118 = OpFAdd %4 %115 %117 + %119 = OpCompositeExtract %5 %47 3 + %120 = OpCompositeExtract %4 %119 0 + %121 = OpFMul %4 %99 %120 + %122 = OpCompositeExtract %4 %119 1 + %123 = OpFMul %4 %100 %122 + %124 = OpFAdd %4 %121 %123 + %58 = OpCompositeConstruct %7 %106 %112 %118 %124 + +; Multiplying 3-dimensional vector by 3x2 matrix + %125 = OpCompositeExtract %4 %37 0 + %126 = OpCompositeExtract %4 %37 1 + %127 = OpCompositeExtract %4 %37 2 + %128 = OpCompositeExtract %6 %48 0 + %129 = OpCompositeExtract %4 %128 0 + %130 = OpFMul %4 %125 %129 + %131 = OpCompositeExtract %4 %128 1 + %132 = OpFMul %4 %126 %131 + %133 = OpCompositeExtract %4 %128 2 + %134 = OpFMul %4 %127 %133 + %135 = OpFAdd %4 %130 %132 + %136 = OpFAdd %4 %134 %135 + %137 = OpCompositeExtract %6 %48 1 + %138 = OpCompositeExtract %4 %137 0 + %139 = OpFMul %4 %125 %138 + %140 = OpCompositeExtract %4 %137 1 + %141 = OpFMul %4 %126 %140 + %142 = OpCompositeExtract %4 %137 2 + %143 = OpFMul %4 %127 %142 + %144 = OpFAdd %4 %139 %141 + %145 = OpFAdd %4 %143 %144 + %59 = OpCompositeConstruct %5 %136 %145 + +; Multiplying 3-dimensional vector by 3x3 matrix + %60 = OpVectorTimesMatrix %6 %38 %49 + +; Multiplying 3-dimensional vector by 3x4 matrix + %61 = OpVectorTimesMatrix %7 %39 %50 + +; Multiplying 4-dimensional vector by 4x2 matrix + %62 = OpVectorTimesMatrix %5 %41 %51 + +; Multiplying 4-dimensional vector by 4x3 matrix + %63 = OpVectorTimesMatrix %6 %42 %52 + +; Multiplying 4-dimensional vector by 4x4 matrix + %64 = OpVectorTimesMatrix %7 %43 %53 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationReplaceLinearAlgebraInstructionTest, + ReplaceOpMatrixTimesVector) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %54 "main" + OpExecutionMode %54 OriginUpperLeft + OpSource ESSL 310 + OpName %54 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Multiplying 2x2 matrix by 2-dimensional vector + %56 = OpMatrixTimesVector %5 %45 %33 + +; Multiplying 3x2 matrix by 2-dimensional vector + %57 = OpMatrixTimesVector %6 %48 %34 + +; Multiplying 4x2 matrix by 2-dimensional vector + %58 = OpMatrixTimesVector %7 %51 %35 + +; Multiplying 2x3 matrix by 3-dimensional vector + %59 = OpMatrixTimesVector %5 %46 %37 + +; Multiplying 3x3 matrix by 3-dimensional vector + %60 = OpMatrixTimesVector %6 %49 %38 + +; Multiplying 4x3 matrix by 3-dimensional vector + %61 = OpMatrixTimesVector %7 %52 %39 + +; Multiplying 2x4 matrix by 4-dimensional vector + %62 = OpMatrixTimesVector %5 %47 %41 + +; Multiplying 3x4 matrix by 4-dimensional vector + %63 = OpMatrixTimesVector %6 %50 %42 + +; Multiplying 4x4 matrix by 4-dimensional vector + %64 = OpMatrixTimesVector %7 %53 %43 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(56, SpvOpMatrixTimesVector, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(57, SpvOpMatrixTimesVector, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(58, SpvOpMatrixTimesVector, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(59, SpvOpMatrixTimesVector, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %54 "main" + OpExecutionMode %54 OriginUpperLeft + OpSource ESSL 310 + OpName %54 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Multiplying 2x2 matrix by 2-dimensional vector + %65 = OpCompositeExtract %5 %45 0 + %66 = OpCompositeExtract %5 %45 1 + %67 = OpCompositeExtract %4 %33 0 + %68 = OpCompositeExtract %4 %33 1 + %69 = OpCompositeExtract %4 %65 0 + %70 = OpFMul %4 %69 %67 + %71 = OpCompositeExtract %4 %66 0 + %72 = OpFMul %4 %71 %68 + %73 = OpFAdd %4 %70 %72 + %74 = OpCompositeExtract %4 %65 1 + %75 = OpFMul %4 %74 %67 + %76 = OpCompositeExtract %4 %66 1 + %77 = OpFMul %4 %76 %68 + %78 = OpFAdd %4 %75 %77 + %56 = OpCompositeConstruct %5 %73 %78 + +; Multiplying 3x2 matrix by 2-dimensional vector + %79 = OpCompositeExtract %6 %48 0 + %80 = OpCompositeExtract %6 %48 1 + %81 = OpCompositeExtract %4 %34 0 + %82 = OpCompositeExtract %4 %34 1 + %83 = OpCompositeExtract %4 %79 0 + %84 = OpFMul %4 %83 %81 + %85 = OpCompositeExtract %4 %80 0 + %86 = OpFMul %4 %85 %82 + %87 = OpFAdd %4 %84 %86 + %88 = OpCompositeExtract %4 %79 1 + %89 = OpFMul %4 %88 %81 + %90 = OpCompositeExtract %4 %80 1 + %91 = OpFMul %4 %90 %82 + %92 = OpFAdd %4 %89 %91 + %93 = OpCompositeExtract %4 %79 2 + %94 = OpFMul %4 %93 %81 + %95 = OpCompositeExtract %4 %80 2 + %96 = OpFMul %4 %95 %82 + %97 = OpFAdd %4 %94 %96 + %57 = OpCompositeConstruct %6 %87 %92 %97 + +; Multiplying 4x2 matrix by 2-dimensional vector + %98 = OpCompositeExtract %7 %51 0 + %99 = OpCompositeExtract %7 %51 1 + %100 = OpCompositeExtract %4 %35 0 + %101 = OpCompositeExtract %4 %35 1 + %102 = OpCompositeExtract %4 %98 0 + %103 = OpFMul %4 %102 %100 + %104 = OpCompositeExtract %4 %99 0 + %105 = OpFMul %4 %104 %101 + %106 = OpFAdd %4 %103 %105 + %107 = OpCompositeExtract %4 %98 1 + %108 = OpFMul %4 %107 %100 + %109 = OpCompositeExtract %4 %99 1 + %110 = OpFMul %4 %109 %101 + %111 = OpFAdd %4 %108 %110 + %112 = OpCompositeExtract %4 %98 2 + %113 = OpFMul %4 %112 %100 + %114 = OpCompositeExtract %4 %99 2 + %115 = OpFMul %4 %114 %101 + %116 = OpFAdd %4 %113 %115 + %117 = OpCompositeExtract %4 %98 3 + %118 = OpFMul %4 %117 %100 + %119 = OpCompositeExtract %4 %99 3 + %120 = OpFMul %4 %119 %101 + %121 = OpFAdd %4 %118 %120 + %58 = OpCompositeConstruct %7 %106 %111 %116 %121 + +; Multiplying 2x3 matrix by 3-dimensional vector + %122 = OpCompositeExtract %5 %46 0 + %123 = OpCompositeExtract %5 %46 1 + %124 = OpCompositeExtract %5 %46 2 + %125 = OpCompositeExtract %4 %37 0 + %126 = OpCompositeExtract %4 %37 1 + %127 = OpCompositeExtract %4 %37 2 + %128 = OpCompositeExtract %4 %122 0 + %129 = OpFMul %4 %128 %125 + %130 = OpCompositeExtract %4 %123 0 + %131 = OpFMul %4 %130 %126 + %132 = OpCompositeExtract %4 %124 0 + %133 = OpFMul %4 %132 %127 + %134 = OpFAdd %4 %129 %131 + %135 = OpFAdd %4 %133 %134 + %136 = OpCompositeExtract %4 %122 1 + %137 = OpFMul %4 %136 %125 + %138 = OpCompositeExtract %4 %123 1 + %139 = OpFMul %4 %138 %126 + %140 = OpCompositeExtract %4 %124 1 + %141 = OpFMul %4 %140 %127 + %142 = OpFAdd %4 %137 %139 + %143 = OpFAdd %4 %141 %142 + %59 = OpCompositeConstruct %5 %135 %143 + +; Multiplying 3x3 matrix by 3-dimensional vector + %60 = OpMatrixTimesVector %6 %49 %38 + +; Multiplying 4x3 matrix by 3-dimensional vector + %61 = OpMatrixTimesVector %7 %52 %39 + +; Multiplying 2x4 matrix by 4-dimensional vector + %62 = OpMatrixTimesVector %5 %47 %41 + +; Multiplying 3x4 matrix by 4-dimensional vector + %63 = OpMatrixTimesVector %6 %50 %42 + +; Multiplying 4x4 matrix by 4-dimensional vector + %64 = OpMatrixTimesVector %7 %53 %43 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationReplaceLinearAlgebraInstructionTest, + ReplaceOpMatrixTimesMatrix) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %54 "main" + OpExecutionMode %54 OriginUpperLeft + OpSource ESSL 310 + OpName %54 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Multiplying 2x2 matrix by 2x2 matrix + %56 = OpMatrixTimesMatrix %8 %45 %45 + +; Multiplying 2x2 matrix by 2x3 matrix + %57 = OpMatrixTimesMatrix %9 %45 %46 + +; Multiplying 2x2 matrix by 2x4 matrix + %58 = OpMatrixTimesMatrix %10 %45 %47 + +; Multiplying 2x3 matrix by 3x2 matrix + %59 = OpMatrixTimesMatrix %8 %46 %48 + +; Multiplying 2x3 matrix by 3x3 matrix + %60 = OpMatrixTimesMatrix %9 %46 %49 + +; Multiplying 2x3 matrix by 3x4 matrix + %61 = OpMatrixTimesMatrix %10 %46 %50 + +; Multiplying 2x4 matrix by 4x2 matrix + %62 = OpMatrixTimesMatrix %8 %47 %51 + +; Multiplying 2x4 matrix by 4x3 matrix + %63 = OpMatrixTimesMatrix %9 %47 %52 + +; Multiplying 2x4 matrix by 4x4 matrix + %64 = OpMatrixTimesMatrix %10 %47 %53 + +; Multiplying 3x2 matrix by 2x2 matrix + %65 = OpMatrixTimesMatrix %11 %48 %45 + +; Multiplying 3x2 matrix by 2x3 matrix + %66 = OpMatrixTimesMatrix %12 %48 %46 + +; Multiplying 3x2 matrix by 2x4 matrix + %67 = OpMatrixTimesMatrix %13 %48 %47 + +; Multiplying 3x3 matrix by 3x2 matrix + %68 = OpMatrixTimesMatrix %11 %49 %48 + +; Multiplying 3x3 matrix by 3x3 matrix + %69 = OpMatrixTimesMatrix %12 %49 %49 + +; Multiplying 3x3 matrix by 3x4 matrix + %70 = OpMatrixTimesMatrix %13 %49 %50 + +; Multiplying 3x4 matrix by 4x2 matrix + %71 = OpMatrixTimesMatrix %11 %50 %51 + +; Multiplying 3x4 matrix by 4x3 matrix + %72 = OpMatrixTimesMatrix %12 %50 %52 + +; Multiplying 3x4 matrix by 4x4 matrix + %73 = OpMatrixTimesMatrix %13 %50 %53 + +; Multiplying 4x2 matrix by 2x2 matrix + %74 = OpMatrixTimesMatrix %14 %51 %45 + +; Multiplying 4x2 matrix by 2x3 matrix + %75 = OpMatrixTimesMatrix %15 %51 %46 + +; Multiplying 4x2 matrix by 2x4 matrix + %76 = OpMatrixTimesMatrix %16 %51 %47 + +; Multiplying 4x3 matrix by 3x2 matrix + %77 = OpMatrixTimesMatrix %14 %52 %48 + +; Multiplying 4x3 matrix by 3x3 matrix + %78 = OpMatrixTimesMatrix %15 %52 %49 + +; Multiplying 4x3 matrix by 3x4 matrix + %79 = OpMatrixTimesMatrix %16 %52 %50 + +; Multiplying 4x4 matrix by 4x2 matrix + %80 = OpMatrixTimesMatrix %14 %53 %51 + +; Multiplying 4x4 matrix by 4x3 matrix + %81 = OpMatrixTimesMatrix %15 %53 %52 + +; Multiplying 4x4 matrix by 4x4 matrix + %82 = OpMatrixTimesMatrix %16 %53 %53 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(56, SpvOpMatrixTimesMatrix, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(57, SpvOpMatrixTimesMatrix, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, + 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = + MakeInstructionDescriptor(58, SpvOpMatrixTimesMatrix, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %54 "main" + OpExecutionMode %54 OriginUpperLeft + OpSource ESSL 310 + OpName %54 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; Constant matrices + %45 = OpConstantComposite %8 %33 %34 + %46 = OpConstantComposite %9 %33 %34 %35 + %47 = OpConstantComposite %10 %33 %34 %35 %36 + %48 = OpConstantComposite %11 %37 %38 + %49 = OpConstantComposite %12 %37 %38 %39 + %50 = OpConstantComposite %13 %37 %38 %39 %40 + %51 = OpConstantComposite %14 %41 %42 + %52 = OpConstantComposite %15 %41 %42 %43 + %53 = OpConstantComposite %16 %41 %42 %43 %44 + +; main function + %54 = OpFunction %2 None %3 + %55 = OpLabel + +; Multiplying 2x2 matrix by 2x2 matrix + %83 = OpCompositeExtract %5 %45 0 ; matrix 2 column 0 + %84 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %85 = OpCompositeExtract %4 %84 0 ; matrix 1 row 0 column 0 + %86 = OpCompositeExtract %4 %83 0 ; matrix 2 row 0 column 0 + %87 = OpFMul %4 %85 %86 + %88 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %89 = OpCompositeExtract %4 %88 0 ; matrix 1 row 0 column 1 + %90 = OpCompositeExtract %4 %83 1 ; matrix 2 row 1 column 0 + %91 = OpFMul %4 %89 %90 + %92 = OpFAdd %4 %87 %91 + %93 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %94 = OpCompositeExtract %4 %93 1 ; matrix 1 row 1 column 0 + %95 = OpCompositeExtract %4 %83 0 ; matrix 2 row 0 column 0 + %96 = OpFMul %4 %94 %95 + %97 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %98 = OpCompositeExtract %4 %97 1 ; matrix 1 row 1 column 1 + %99 = OpCompositeExtract %4 %83 1 ; matrix 2 row 1 column 0 + %100 = OpFMul %4 %98 %99 + %101 = OpFAdd %4 %96 %100 + %102 = OpCompositeConstruct %5 %92 %101 ; resulting matrix column 0 + %103 = OpCompositeExtract %5 %45 1 ; matrix 2 column 1 + %104 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %105 = OpCompositeExtract %4 %104 0 ; matrix 1 row 0 column 0 + %106 = OpCompositeExtract %4 %103 0 ; matrix 2 row 0 column 1 + %107 = OpFMul %4 %105 %106 + %108 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %109 = OpCompositeExtract %4 %108 0 ; matrix 1 row 0 column 1 + %110 = OpCompositeExtract %4 %103 1 ; matrix 2 row 1 column 1 + %111 = OpFMul %4 %109 %110 + %112 = OpFAdd %4 %107 %111 + %113 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %114 = OpCompositeExtract %4 %113 1 ; matrix 1 row 1 column 0 + %115 = OpCompositeExtract %4 %103 0 ; matrix 2 row 0 column 1 + %116 = OpFMul %4 %114 %115 + %117 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %118 = OpCompositeExtract %4 %117 1 ; matrix 1 row 1 column 1 + %119 = OpCompositeExtract %4 %103 1 ; matrix 2 row 1 column 1 + %120 = OpFMul %4 %118 %119 + %121 = OpFAdd %4 %116 %120 + %122 = OpCompositeConstruct %5 %112 %121 ; resulting matrix column 1 + %56 = OpCompositeConstruct %8 %102 %122 ; resulting matrix + +; Multiplying 2x2 matrix by 2x3 matrix + %123 = OpCompositeExtract %5 %46 0 ; matrix 2 column 0 + %124 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %125 = OpCompositeExtract %4 %124 0 ; matrix 1 row 0 column 0 + %126 = OpCompositeExtract %4 %123 0 ; matrix 2 row 0 column 0 + %127 = OpFMul %4 %125 %126 + %128 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %129 = OpCompositeExtract %4 %128 0 ; matrix 1 row 0 column 1 + %130 = OpCompositeExtract %4 %123 1 ; matrix 2 row 1 column 0 + %131 = OpFMul %4 %129 %130 + %132 = OpFAdd %4 %127 %131 + %133 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %134 = OpCompositeExtract %4 %133 1 ; matrix 1 row 1 column 0 + %135 = OpCompositeExtract %4 %123 0 ; matrix 2 row 0 column 0 + %136 = OpFMul %4 %134 %135 + %137 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %138 = OpCompositeExtract %4 %137 1 ; matrix 1 row 1 column 1 + %139 = OpCompositeExtract %4 %123 1 ; matrix 2 row 1 column 0 + %140 = OpFMul %4 %138 %139 + %141 = OpFAdd %4 %136 %140 + %142 = OpCompositeConstruct %5 %132 %141 ; resulting matrix column 0 + %143 = OpCompositeExtract %5 %46 1 ; matrix 2 column 1 + %144 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %145 = OpCompositeExtract %4 %144 0 ; matrix 1 row 0 column 0 + %146 = OpCompositeExtract %4 %143 0 ; matrix 2 row 0 column 1 + %147 = OpFMul %4 %145 %146 + %148 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %149 = OpCompositeExtract %4 %148 0 ; matrix 1 row 0 column 1 + %150 = OpCompositeExtract %4 %143 1 ; matrix 2 row 1 column 1 + %151 = OpFMul %4 %149 %150 + %152 = OpFAdd %4 %147 %151 + %153 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %154 = OpCompositeExtract %4 %153 1 ; matrix 1 row 1 column 0 + %155 = OpCompositeExtract %4 %143 0 ; matrix 2 row 0 column 1 + %156 = OpFMul %4 %154 %155 + %157 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %158 = OpCompositeExtract %4 %157 1 ; matrix 1 row 1 column 1 + %159 = OpCompositeExtract %4 %143 1 ; matrix 2 row 1 column 1 + %160 = OpFMul %4 %158 %159 + %161 = OpFAdd %4 %156 %160 + %162 = OpCompositeConstruct %5 %152 %161 ; resulting matrix column 1 + %163 = OpCompositeExtract %5 %46 2 ; matrix 2 column 2 + %164 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %165 = OpCompositeExtract %4 %164 0 ; matrix 1 row 0 column 0 + %166 = OpCompositeExtract %4 %163 0 ; matrix 2 row 0 column 2 + %167 = OpFMul %4 %165 %166 + %168 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %169 = OpCompositeExtract %4 %168 0 ; matrix 1 row 0 column 1 + %170 = OpCompositeExtract %4 %163 1 ; matrix 2 row 1 column 2 + %171 = OpFMul %4 %169 %170 + %172 = OpFAdd %4 %167 %171 + %173 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %174 = OpCompositeExtract %4 %173 1 ; matrix 1 row 1 column 0 + %175 = OpCompositeExtract %4 %163 0 ; matrix 2 row 0 column 2 + %176 = OpFMul %4 %174 %175 + %177 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %178 = OpCompositeExtract %4 %177 1 ; matrix 1 row 1 column 1 + %179 = OpCompositeExtract %4 %163 1 ; matrix 2 row 1 column 2 + %180 = OpFMul %4 %178 %179 + %181 = OpFAdd %4 %176 %180 + %182 = OpCompositeConstruct %5 %172 %181 ; resulting matrix column 2 + %57 = OpCompositeConstruct %9 %142 %162 %182 + +; Multiplying 2x2 matrix by 2x4 matrix + %183 = OpCompositeExtract %5 %47 0 ; matrix 2 column 0 + %184 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %185 = OpCompositeExtract %4 %184 0 ; matrix 1 row 0 column 0 + %186 = OpCompositeExtract %4 %183 0 ; matrix 2 row 0 column 0 + %187 = OpFMul %4 %185 %186 + %188 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %189 = OpCompositeExtract %4 %188 0 ; matrix 1 row 0 column 1 + %190 = OpCompositeExtract %4 %183 1 ; matrix 2 row 1 column 0 + %191 = OpFMul %4 %189 %190 + %192 = OpFAdd %4 %187 %191 + %193 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %194 = OpCompositeExtract %4 %193 1 ; matrix 1 row 1 column 0 + %195 = OpCompositeExtract %4 %183 0 ; matrix 2 row 0 column 0 + %196 = OpFMul %4 %194 %195 + %197 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %198 = OpCompositeExtract %4 %197 1 ; matrix 1 row 1 column 1 + %199 = OpCompositeExtract %4 %183 1 ; matrix 2 row 1 column 0 + %200 = OpFMul %4 %198 %199 + %201 = OpFAdd %4 %196 %200 + %202 = OpCompositeConstruct %5 %192 %201 ; resulting matrix column 0 + %203 = OpCompositeExtract %5 %47 1 ; matrix 2 column 1 + %204 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %205 = OpCompositeExtract %4 %204 0 ; matrix 1 row 0 column 0 + %206 = OpCompositeExtract %4 %203 0 ; matrix 2 row 0 column 1 + %207 = OpFMul %4 %205 %206 + %208 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %209 = OpCompositeExtract %4 %208 0 ; matrix 1 row 0 column 1 + %210 = OpCompositeExtract %4 %203 1 ; matrix 2 row 1 column 1 + %211 = OpFMul %4 %209 %210 + %212 = OpFAdd %4 %207 %211 + %213 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %214 = OpCompositeExtract %4 %213 1 ; matrix 1 row 1 column 0 + %215 = OpCompositeExtract %4 %203 0 ; matrix 2 row 0 column 1 + %216 = OpFMul %4 %214 %215 + %217 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %218 = OpCompositeExtract %4 %217 1 ; matrix 1 row 1 column 1 + %219 = OpCompositeExtract %4 %203 1 ; matrix 2 row 1 column 1 + %220 = OpFMul %4 %218 %219 + %221 = OpFAdd %4 %216 %220 + %222 = OpCompositeConstruct %5 %212 %221 ; resulting matrix column 1 + %223 = OpCompositeExtract %5 %47 2 ; matrix 2 column 2 + %224 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %225 = OpCompositeExtract %4 %224 0 ; matrix 1 row 0 column 0 + %226 = OpCompositeExtract %4 %223 0 ; matrix 2 row 0 column 2 + %227 = OpFMul %4 %225 %226 + %228 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %229 = OpCompositeExtract %4 %228 0 ; matrix 1 row 0 column 1 + %230 = OpCompositeExtract %4 %223 1 ; matrix 2 row 1 column 2 + %231 = OpFMul %4 %229 %230 + %232 = OpFAdd %4 %227 %231 + %233 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %234 = OpCompositeExtract %4 %233 1 ; matrix 1 row 1 column 0 + %235 = OpCompositeExtract %4 %223 0 ; matrix 2 row 0 column 2 + %236 = OpFMul %4 %234 %235 + %237 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %238 = OpCompositeExtract %4 %237 1 ; matrix 1 row 1 column 1 + %239 = OpCompositeExtract %4 %223 1 ; matrix 2 row 1 column 2 + %240 = OpFMul %4 %238 %239 + %241 = OpFAdd %4 %236 %240 + %242 = OpCompositeConstruct %5 %232 %241 ; resulting matrix column 2 + %243 = OpCompositeExtract %5 %47 3 ; matrix 2 column 3 + %244 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %245 = OpCompositeExtract %4 %244 0 ; matrix 1 row 0 column 0 + %246 = OpCompositeExtract %4 %243 0 ; matrix 2 row 0 column 3 + %247 = OpFMul %4 %245 %246 + %248 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %249 = OpCompositeExtract %4 %248 0 ; matrix 1 row 0 column 1 + %250 = OpCompositeExtract %4 %243 1 ; matrix 2 row 1 column 3 + %251 = OpFMul %4 %249 %250 + %252 = OpFAdd %4 %247 %251 + %253 = OpCompositeExtract %5 %45 0 ; matrix 1 column 0 + %254 = OpCompositeExtract %4 %253 1 ; matrix 1 row 1 column 0 + %255 = OpCompositeExtract %4 %243 0 ; matrix 2 row 0 column 3 + %256 = OpFMul %4 %254 %255 + %257 = OpCompositeExtract %5 %45 1 ; matrix 1 column 1 + %258 = OpCompositeExtract %4 %257 1 ; matrix 1 row 1 column 1 + %259 = OpCompositeExtract %4 %243 1 ; matrix 2 row 1 column 3 + %260 = OpFMul %4 %258 %259 + %261 = OpFAdd %4 %256 %260 + %262 = OpCompositeConstruct %5 %252 %261 ; resulting matrix column 3 + %58 = OpCompositeConstruct %10 %202 %222 %242 %262 + +; Multiplying 2x3 matrix by 3x2 matrix + %59 = OpMatrixTimesMatrix %8 %46 %48 + +; Multiplying 2x3 matrix by 3x3 matrix + %60 = OpMatrixTimesMatrix %9 %46 %49 + +; Multiplying 2x3 matrix by 3x4 matrix + %61 = OpMatrixTimesMatrix %10 %46 %50 + +; Multiplying 2x4 matrix by 4x2 matrix + %62 = OpMatrixTimesMatrix %8 %47 %51 + +; Multiplying 2x4 matrix by 4x3 matrix + %63 = OpMatrixTimesMatrix %9 %47 %52 + +; Multiplying 2x4 matrix by 4x4 matrix + %64 = OpMatrixTimesMatrix %10 %47 %53 + +; Multiplying 3x2 matrix by 2x2 matrix + %65 = OpMatrixTimesMatrix %11 %48 %45 + +; Multiplying 3x2 matrix by 2x3 matrix + %66 = OpMatrixTimesMatrix %12 %48 %46 + +; Multiplying 3x2 matrix by 2x4 matrix + %67 = OpMatrixTimesMatrix %13 %48 %47 + +; Multiplying 3x3 matrix by 3x2 matrix + %68 = OpMatrixTimesMatrix %11 %49 %48 + +; Multiplying 3x3 matrix by 3x3 matrix + %69 = OpMatrixTimesMatrix %12 %49 %49 + +; Multiplying 3x3 matrix by 3x4 matrix + %70 = OpMatrixTimesMatrix %13 %49 %50 + +; Multiplying 3x4 matrix by 4x2 matrix + %71 = OpMatrixTimesMatrix %11 %50 %51 + +; Multiplying 3x4 matrix by 4x3 matrix + %72 = OpMatrixTimesMatrix %12 %50 %52 + +; Multiplying 3x4 matrix by 4x4 matrix + %73 = OpMatrixTimesMatrix %13 %50 %53 + +; Multiplying 4x2 matrix by 2x2 matrix + %74 = OpMatrixTimesMatrix %14 %51 %45 + +; Multiplying 4x2 matrix by 2x3 matrix + %75 = OpMatrixTimesMatrix %15 %51 %46 + +; Multiplying 4x2 matrix by 2x4 matrix + %76 = OpMatrixTimesMatrix %16 %51 %47 + +; Multiplying 4x3 matrix by 3x2 matrix + %77 = OpMatrixTimesMatrix %14 %52 %48 + +; Multiplying 4x3 matrix by 3x3 matrix + %78 = OpMatrixTimesMatrix %15 %52 %49 + +; Multiplying 4x3 matrix by 3x4 matrix + %79 = OpMatrixTimesMatrix %16 %52 %50 + +; Multiplying 4x4 matrix by 4x2 matrix + %80 = OpMatrixTimesMatrix %14 %53 %51 + +; Multiplying 4x4 matrix by 4x3 matrix + %81 = OpMatrixTimesMatrix %15 %53 %52 + +; Multiplying 4x4 matrix by 4x4 matrix + %82 = OpMatrixTimesMatrix %16 %53 %53 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationReplaceLinearAlgebraInstructionTest, ReplaceOpOuterProduct) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %45 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; main function + %45 = OpFunction %2 None %3 + %46 = OpLabel + +; Multiplying 2-dimensional vector by 2-dimensional vector + %47 = OpOuterProduct %8 %33 %34 + +; Multiplying 2-dimensional vector by 3-dimensional vector + %48 = OpOuterProduct %9 %35 %37 + +; Multiplying 2-dimensional vector by 4-dimensional vector + %49 = OpOuterProduct %10 %36 %41 + +; Multiplying 3-dimensional vector by 2-dimensional vector + %50 = OpOuterProduct %11 %37 %33 + +; Multiplying 3-dimensional vector by 3-dimensional vector + %51 = OpOuterProduct %12 %38 %39 + +; Multiplying 3-dimensional vector by 4-dimensional vector + %52 = OpOuterProduct %13 %40 %41 + +; Multiplying 4-dimensional vector by 2-dimensional vector + %53 = OpOuterProduct %14 %41 %33 + +; Multiplying 4-dimensional vector by 3-dimensional vector + %54 = OpOuterProduct %15 %42 %37 + +; Multiplying 4-dimensional vector by 4-dimensional vector + %55 = OpOuterProduct %16 %43 %44 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = + MakeInstructionDescriptor(47, SpvOpOuterProduct, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67}, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(48, SpvOpOuterProduct, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(49, SpvOpOuterProduct, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(50, SpvOpOuterProduct, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, + 124, 125}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %45 "main" + +; Types + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpTypeMatrix %5 2 + %9 = OpTypeMatrix %5 3 + %10 = OpTypeMatrix %5 4 + %11 = OpTypeMatrix %6 2 + %12 = OpTypeMatrix %6 3 + %13 = OpTypeMatrix %6 4 + %14 = OpTypeMatrix %7 2 + %15 = OpTypeMatrix %7 3 + %16 = OpTypeMatrix %7 4 + +; Constant scalars + %17 = OpConstant %4 1 + %18 = OpConstant %4 2 + %19 = OpConstant %4 3 + %20 = OpConstant %4 4 + %21 = OpConstant %4 5 + %22 = OpConstant %4 6 + %23 = OpConstant %4 7 + %24 = OpConstant %4 8 + %25 = OpConstant %4 9 + %26 = OpConstant %4 10 + %27 = OpConstant %4 11 + %28 = OpConstant %4 12 + %29 = OpConstant %4 13 + %30 = OpConstant %4 14 + %31 = OpConstant %4 15 + %32 = OpConstant %4 16 + +; Constant vectors + %33 = OpConstantComposite %5 %17 %18 + %34 = OpConstantComposite %5 %19 %20 + %35 = OpConstantComposite %5 %21 %22 + %36 = OpConstantComposite %5 %23 %24 + %37 = OpConstantComposite %6 %17 %18 %19 + %38 = OpConstantComposite %6 %20 %21 %22 + %39 = OpConstantComposite %6 %23 %24 %25 + %40 = OpConstantComposite %6 %26 %27 %28 + %41 = OpConstantComposite %7 %17 %18 %19 %20 + %42 = OpConstantComposite %7 %21 %22 %23 %24 + %43 = OpConstantComposite %7 %25 %26 %27 %28 + %44 = OpConstantComposite %7 %29 %30 %31 %32 + +; main function + %45 = OpFunction %2 None %3 + %46 = OpLabel + +; Multiplying 2-dimensional vector by 2-dimensional vector + %56 = OpCompositeExtract %4 %34 0 + %57 = OpCompositeExtract %4 %33 0 + %58 = OpFMul %4 %56 %57 + %59 = OpCompositeExtract %4 %33 1 + %60 = OpFMul %4 %56 %59 + %61 = OpCompositeConstruct %5 %58 %60 + %62 = OpCompositeExtract %4 %34 1 + %63 = OpCompositeExtract %4 %33 0 + %64 = OpFMul %4 %62 %63 + %65 = OpCompositeExtract %4 %33 1 + %66 = OpFMul %4 %62 %65 + %67 = OpCompositeConstruct %5 %64 %66 + %47 = OpCompositeConstruct %8 %61 %67 + +; Multiplying 2-dimensional vector by 3-dimensional vector + %68 = OpCompositeExtract %4 %37 0 + %69 = OpCompositeExtract %4 %35 0 + %70 = OpFMul %4 %68 %69 + %71 = OpCompositeExtract %4 %35 1 + %72 = OpFMul %4 %68 %71 + %73 = OpCompositeConstruct %5 %70 %72 + %74 = OpCompositeExtract %4 %37 1 + %75 = OpCompositeExtract %4 %35 0 + %76 = OpFMul %4 %74 %75 + %77 = OpCompositeExtract %4 %35 1 + %78 = OpFMul %4 %74 %77 + %79 = OpCompositeConstruct %5 %76 %78 + %80 = OpCompositeExtract %4 %37 2 + %81 = OpCompositeExtract %4 %35 0 + %82 = OpFMul %4 %80 %81 + %83 = OpCompositeExtract %4 %35 1 + %84 = OpFMul %4 %80 %83 + %85 = OpCompositeConstruct %5 %82 %84 + %48 = OpCompositeConstruct %9 %73 %79 %85 + +; Multiplying 2-dimensional vector by 4-dimensional vector + %86 = OpCompositeExtract %4 %41 0 + %87 = OpCompositeExtract %4 %36 0 + %88 = OpFMul %4 %86 %87 + %89 = OpCompositeExtract %4 %36 1 + %90 = OpFMul %4 %86 %89 + %91 = OpCompositeConstruct %5 %88 %90 + %92 = OpCompositeExtract %4 %41 1 + %93 = OpCompositeExtract %4 %36 0 + %94 = OpFMul %4 %92 %93 + %95 = OpCompositeExtract %4 %36 1 + %96 = OpFMul %4 %92 %95 + %97 = OpCompositeConstruct %5 %94 %96 + %98 = OpCompositeExtract %4 %41 2 + %99 = OpCompositeExtract %4 %36 0 + %100 = OpFMul %4 %98 %99 + %101 = OpCompositeExtract %4 %36 1 + %102 = OpFMul %4 %98 %101 + %103 = OpCompositeConstruct %5 %100 %102 + %104 = OpCompositeExtract %4 %41 3 + %105 = OpCompositeExtract %4 %36 0 + %106 = OpFMul %4 %104 %105 + %107 = OpCompositeExtract %4 %36 1 + %108 = OpFMul %4 %104 %107 + %109 = OpCompositeConstruct %5 %106 %108 + %49 = OpCompositeConstruct %10 %91 %97 %103 %109 + +; Multiplying 3-dimensional vector by 2-dimensional vector + %110 = OpCompositeExtract %4 %33 0 + %111 = OpCompositeExtract %4 %37 0 + %112 = OpFMul %4 %110 %111 + %113 = OpCompositeExtract %4 %37 1 + %114 = OpFMul %4 %110 %113 + %115 = OpCompositeExtract %4 %37 2 + %116 = OpFMul %4 %110 %115 + %117 = OpCompositeConstruct %6 %112 %114 %116 + %118 = OpCompositeExtract %4 %33 1 + %119 = OpCompositeExtract %4 %37 0 + %120 = OpFMul %4 %118 %119 + %121 = OpCompositeExtract %4 %37 1 + %122 = OpFMul %4 %118 %121 + %123 = OpCompositeExtract %4 %37 2 + %124 = OpFMul %4 %118 %123 + %125 = OpCompositeConstruct %6 %120 %122 %124 + %50 = OpCompositeConstruct %11 %117 %125 + +; Multiplying 3-dimensional vector by 3-dimensional vector + %51 = OpOuterProduct %12 %38 %39 + +; Multiplying 3-dimensional vector by 4-dimensional vector + %52 = OpOuterProduct %13 %40 %41 + +; Multiplying 4-dimensional vector by 2-dimensional vector + %53 = OpOuterProduct %14 %41 %33 + +; Multiplying 4-dimensional vector by 3-dimensional vector + %54 = OpOuterProduct %15 %42 %37 + +; Multiplying 4-dimensional vector by 4-dimensional vector + %55 = OpOuterProduct %16 %43 %44 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +TEST(TransformationReplaceLinearAlgebraInstructionTest, ReplaceOpDot) { + std::string reference_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %22 "main" + OpExecutionMode %22 OriginUpperLeft + OpSource ESSL 310 + OpName %22 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpConstant %4 1 + %9 = OpConstant %4 2 + %10 = OpConstant %4 3 + %11 = OpConstant %4 4 + %12 = OpConstant %4 5 + %13 = OpConstant %4 6 + %14 = OpConstant %4 7 + %15 = OpConstant %4 8 + %16 = OpConstantComposite %5 %8 %9 + %17 = OpConstantComposite %5 %10 %11 + %18 = OpConstantComposite %6 %8 %9 %10 + %19 = OpConstantComposite %6 %11 %12 %13 + %20 = OpConstantComposite %7 %8 %9 %10 %11 + %21 = OpConstantComposite %7 %12 %13 %14 %15 + %22 = OpFunction %2 None %3 + %23 = OpLabel + %24 = OpDot %4 %16 %17 + %25 = OpDot %4 %18 %19 + %26 = OpDot %4 %20 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instruction_descriptor = MakeInstructionDescriptor(24, SpvOpDot, 0); + auto transformation = TransformationReplaceLinearAlgebraInstruction( + {27, 28, 29, 30, 31, 32}, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(25, SpvOpDot, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {33, 34, 35, 36, 37, 38, 39, 40, 41, 42}, instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instruction_descriptor = MakeInstructionDescriptor(26, SpvOpDot, 0); + transformation = TransformationReplaceLinearAlgebraInstruction( + {43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56}, + instruction_descriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variant_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %22 "main" + OpExecutionMode %22 OriginUpperLeft + OpSource ESSL 310 + OpName %22 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %4 = OpTypeFloat 32 + %5 = OpTypeVector %4 2 + %6 = OpTypeVector %4 3 + %7 = OpTypeVector %4 4 + %8 = OpConstant %4 1 + %9 = OpConstant %4 2 + %10 = OpConstant %4 3 + %11 = OpConstant %4 4 + %12 = OpConstant %4 5 + %13 = OpConstant %4 6 + %14 = OpConstant %4 7 + %15 = OpConstant %4 8 + %16 = OpConstantComposite %5 %8 %9 + %17 = OpConstantComposite %5 %10 %11 + %18 = OpConstantComposite %6 %8 %9 %10 + %19 = OpConstantComposite %6 %11 %12 %13 + %20 = OpConstantComposite %7 %8 %9 %10 %11 + %21 = OpConstantComposite %7 %12 %13 %14 %15 + %22 = OpFunction %2 None %3 + %23 = OpLabel + %27 = OpCompositeExtract %4 %16 0 + %28 = OpCompositeExtract %4 %17 0 + %29 = OpFMul %4 %27 %28 + %30 = OpCompositeExtract %4 %16 1 + %31 = OpCompositeExtract %4 %17 1 + %32 = OpFMul %4 %30 %31 + %24 = OpFAdd %4 %29 %32 + %33 = OpCompositeExtract %4 %18 0 + %34 = OpCompositeExtract %4 %19 0 + %35 = OpFMul %4 %33 %34 + %36 = OpCompositeExtract %4 %18 1 + %37 = OpCompositeExtract %4 %19 1 + %38 = OpFMul %4 %36 %37 + %39 = OpCompositeExtract %4 %18 2 + %40 = OpCompositeExtract %4 %19 2 + %41 = OpFMul %4 %39 %40 + %42 = OpFAdd %4 %35 %38 + %25 = OpFAdd %4 %41 %42 + %43 = OpCompositeExtract %4 %20 0 + %44 = OpCompositeExtract %4 %21 0 + %45 = OpFMul %4 %43 %44 + %46 = OpCompositeExtract %4 %20 1 + %47 = OpCompositeExtract %4 %21 1 + %48 = OpFMul %4 %46 %47 + %49 = OpCompositeExtract %4 %20 2 + %50 = OpCompositeExtract %4 %21 2 + %51 = OpFMul %4 %49 %50 + %52 = OpCompositeExtract %4 %20 3 + %53 = OpCompositeExtract %4 %21 3 + %54 = OpFMul %4 %52 %53 + %55 = OpFAdd %4 %45 %48 + %56 = OpFAdd %4 %51 %55 + %26 = OpFAdd %4 %54 %56 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp new file mode 100644 index 0000000..bc10099 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_load_store_with_copy_memory_test.cpp @@ -0,0 +1,287 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_load_store_with_copy_memory.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceLoadStoreWithCopyMemoryTest, BasicScenarios) { + // This is a simple transformation and this test handles the main cases. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %26 %28 %31 %33 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %12 "c" + OpName %14 "d" + OpName %18 "e" + OpName %20 "f" + OpName %26 "i1" + OpName %28 "i2" + OpName %31 "g1" + OpName %33 "g2" + OpDecorate %26 Location 0 + OpDecorate %28 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %13 = OpConstant %6 4 + %15 = OpConstant %6 5 + %16 = OpTypeFloat 32 + %17 = OpTypePointer Function %16 + %19 = OpConstant %16 2 + %21 = OpConstant %16 3 + %25 = OpTypePointer Output %6 + %26 = OpVariable %25 Output + %27 = OpConstant %6 1 + %28 = OpVariable %25 Output + %30 = OpTypePointer Private %6 + %31 = OpVariable %30 Private + %32 = OpConstant %6 0 + %33 = OpVariable %30 Private + %35 = OpTypeBool + %36 = OpConstantTrue %35 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %14 = OpVariable %7 Function + %18 = OpVariable %17 Function + %20 = OpVariable %17 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %13 + OpStore %14 %15 + OpStore %18 %19 + OpStore %20 %21 + %22 = OpLoad %6 %8 + OpCopyMemory %10 %8 + OpStore %10 %22 + %23 = OpLoad %6 %12 + OpStore %14 %23 + %24 = OpLoad %16 %18 + OpStore %20 %24 + OpStore %26 %27 + OpStore %28 %27 + %29 = OpLoad %6 %26 + OpMemoryBarrier %32 %32 + OpStore %28 %29 + OpStore %31 %32 + OpStore %33 %32 + %34 = OpLoad %6 %33 + OpSelectionMerge %38 None + OpBranchConditional %36 %37 %38 + %37 = OpLabel + OpStore %31 %34 + OpBranch %38 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto bad_instruction_descriptor_1 = + MakeInstructionDescriptor(11, SpvOpConstant, 0); + + auto load_instruction_descriptor_1 = + MakeInstructionDescriptor(22, SpvOpLoad, 0); + auto load_instruction_descriptor_2 = + MakeInstructionDescriptor(23, SpvOpLoad, 0); + auto load_instruction_descriptor_3 = + MakeInstructionDescriptor(24, SpvOpLoad, 0); + auto load_instruction_descriptor_other_block = + MakeInstructionDescriptor(34, SpvOpLoad, 0); + auto load_instruction_descriptor_unsafe = + MakeInstructionDescriptor(29, SpvOpLoad, 0); + + auto store_instruction_descriptor_1 = + MakeInstructionDescriptor(22, SpvOpStore, 0); + auto store_instruction_descriptor_2 = + MakeInstructionDescriptor(23, SpvOpStore, 0); + auto store_instruction_descriptor_3 = + MakeInstructionDescriptor(24, SpvOpStore, 0); + auto store_instruction_descriptor_other_block = + MakeInstructionDescriptor(37, SpvOpStore, 0); + auto store_instruction_descriptor_unsafe = + MakeInstructionDescriptor(29, SpvOpStore, 0); + + // Bad: |load_instruction_descriptor| is incorrect. + auto transformation_bad_1 = TransformationReplaceLoadStoreWithCopyMemory( + bad_instruction_descriptor_1, store_instruction_descriptor_1); + ASSERT_FALSE( + transformation_bad_1.IsApplicable(context.get(), transformation_context)); + + // Bad: |store_instruction_descriptor| is incorrect. + auto transformation_bad_2 = TransformationReplaceLoadStoreWithCopyMemory( + load_instruction_descriptor_1, bad_instruction_descriptor_1); + ASSERT_FALSE( + transformation_bad_2.IsApplicable(context.get(), transformation_context)); + + // Bad: Intermediate values of the OpLoad and the OpStore don't match. + auto transformation_bad_3 = TransformationReplaceLoadStoreWithCopyMemory( + load_instruction_descriptor_1, store_instruction_descriptor_2); + ASSERT_FALSE( + transformation_bad_3.IsApplicable(context.get(), transformation_context)); + + // Bad: There is an interfering OpCopyMemory instruction between the OpLoad + // and the OpStore. + auto transformation_bad_4 = TransformationReplaceLoadStoreWithCopyMemory( + load_instruction_descriptor_1, store_instruction_descriptor_1); + ASSERT_FALSE( + transformation_bad_4.IsApplicable(context.get(), transformation_context)); + + // Bad: There is an interfering OpMemoryBarrier instruction between the OpLoad + // and the OpStore. + auto transformation_bad_5 = TransformationReplaceLoadStoreWithCopyMemory( + load_instruction_descriptor_unsafe, store_instruction_descriptor_unsafe); + ASSERT_FALSE( + transformation_bad_5.IsApplicable(context.get(), transformation_context)); + + // Bad: OpLoad and OpStore instructions are in different blocks. + auto transformation_bad_6 = TransformationReplaceLoadStoreWithCopyMemory( + load_instruction_descriptor_other_block, + store_instruction_descriptor_other_block); + ASSERT_FALSE( + transformation_bad_6.IsApplicable(context.get(), transformation_context)); + + auto transformation_good_1 = TransformationReplaceLoadStoreWithCopyMemory( + load_instruction_descriptor_2, store_instruction_descriptor_2); + ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_1, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + auto transformation_good_2 = TransformationReplaceLoadStoreWithCopyMemory( + load_instruction_descriptor_3, store_instruction_descriptor_3); + ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), + transformation_context)); + ApplyAndCheckFreshIds(transformation_good_2, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %26 %28 %31 %33 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpName %12 "c" + OpName %14 "d" + OpName %18 "e" + OpName %20 "f" + OpName %26 "i1" + OpName %28 "i2" + OpName %31 "g1" + OpName %33 "g2" + OpDecorate %26 Location 0 + OpDecorate %28 Location 1 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %11 = OpConstant %6 3 + %13 = OpConstant %6 4 + %15 = OpConstant %6 5 + %16 = OpTypeFloat 32 + %17 = OpTypePointer Function %16 + %19 = OpConstant %16 2 + %21 = OpConstant %16 3 + %25 = OpTypePointer Output %6 + %26 = OpVariable %25 Output + %27 = OpConstant %6 1 + %28 = OpVariable %25 Output + %30 = OpTypePointer Private %6 + %31 = OpVariable %30 Private + %32 = OpConstant %6 0 + %33 = OpVariable %30 Private + %35 = OpTypeBool + %36 = OpConstantTrue %35 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + %12 = OpVariable %7 Function + %14 = OpVariable %7 Function + %18 = OpVariable %17 Function + %20 = OpVariable %17 Function + OpStore %8 %9 + OpStore %10 %11 + OpStore %12 %13 + OpStore %14 %15 + OpStore %18 %19 + OpStore %20 %21 + %22 = OpLoad %6 %8 + OpCopyMemory %10 %8 + OpStore %10 %22 + %23 = OpLoad %6 %12 + OpCopyMemory %14 %12 + %24 = OpLoad %16 %18 + OpCopyMemory %20 %18 + OpStore %26 %27 + OpStore %28 %27 + %29 = OpLoad %6 %26 + OpMemoryBarrier %32 %32 + OpStore %28 %29 + OpStore %31 %32 + OpStore %33 %32 + %34 = OpLoad %6 %33 + OpSelectionMerge %38 None + OpBranchConditional %36 %37 %38 + %37 = OpLabel + OpStore %31 %34 + OpBranch %38 + %38 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_opphi_id_from_dead_predecessor_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_opphi_id_from_dead_predecessor_test.cpp new file mode 100644 index 0000000..875d79e --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_opphi_id_from_dead_predecessor_test.cpp @@ -0,0 +1,212 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_opphi_id_from_dead_predecessor.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %8 = OpTypeInt 32 1 + %9 = OpConstant %8 2 + %10 = OpConstant %8 3 + %11 = OpConstant %8 4 + %12 = OpConstant %8 5 + %13 = OpConstant %8 6 + %2 = OpFunction %3 None %4 + %14 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %6 %16 %17 + %16 = OpLabel + %18 = OpCopyObject %8 %9 + OpSelectionMerge %19 None + OpBranchConditional %7 %20 %21 + %20 = OpLabel + %22 = OpCopyObject %8 %10 + %23 = OpCopyObject %8 %12 + OpBranch %19 + %21 = OpLabel + %24 = OpCopyObject %8 %9 + OpBranch %19 + %19 = OpLabel + %25 = OpPhi %8 %22 %20 %24 %21 + OpBranch %15 + %17 = OpLabel + %26 = OpCopyObject %8 %12 + %27 = OpCopyObject %8 %13 + OpBranch %28 + %28 = OpLabel + %29 = OpPhi %8 %27 %17 + OpBranch %15 + %15 = OpLabel + %30 = OpPhi %8 %25 %19 %26 %28 + OpReturn + OpFunctionEnd +)"; + +TEST(TransformationReplaceOpPhiIdFromDeadPredecessorTest, Inapplicable) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Record the fact that blocks 20, 17, 28 are dead. + transformation_context.GetFactManager()->AddFactBlockIsDead(20); + transformation_context.GetFactManager()->AddFactBlockIsDead(17); + transformation_context.GetFactManager()->AddFactBlockIsDead(28); + + // %26 is not an OpPhi instruction. + ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(26, 14, 10) + .IsApplicable(context.get(), transformation_context)); + + // %25 is not a block label. + ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(30, 25, 10) + .IsApplicable(context.get(), transformation_context)); + + // %14 is not a predecessor of %28 (which contains %29). + ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(29, 14, 10) + .IsApplicable(context.get(), transformation_context)); + + // %19 is not a dead block. + ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(30, 19, 10) + .IsApplicable(context.get(), transformation_context)); + + // %7 does not have the same type id as %25. + ASSERT_FALSE( + TransformationReplaceOpPhiIdFromDeadPredecessor(25, 20, 7).IsApplicable( + context.get(), transformation_context)); + + // %29 is not available at the end of %20. + ASSERT_FALSE(TransformationReplaceOpPhiIdFromDeadPredecessor(25, 20, 29) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceOpPhiIdFromDeadPredecessorTest, Apply) { + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + // Record the fact that blocks 20, 17, 28 are dead. + transformation_context.GetFactManager()->AddFactBlockIsDead(20); + transformation_context.GetFactManager()->AddFactBlockIsDead(17); + transformation_context.GetFactManager()->AddFactBlockIsDead(28); + + auto transformation1 = + TransformationReplaceOpPhiIdFromDeadPredecessor(25, 20, 18); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + auto transformation2 = + TransformationReplaceOpPhiIdFromDeadPredecessor(30, 28, 29); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + auto transformation3 = + TransformationReplaceOpPhiIdFromDeadPredecessor(29, 17, 10); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformations = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeBool + %6 = OpConstantTrue %5 + %7 = OpConstantFalse %5 + %8 = OpTypeInt 32 1 + %9 = OpConstant %8 2 + %10 = OpConstant %8 3 + %11 = OpConstant %8 4 + %12 = OpConstant %8 5 + %13 = OpConstant %8 6 + %2 = OpFunction %3 None %4 + %14 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %6 %16 %17 + %16 = OpLabel + %18 = OpCopyObject %8 %9 + OpSelectionMerge %19 None + OpBranchConditional %7 %20 %21 + %20 = OpLabel + %22 = OpCopyObject %8 %10 + %23 = OpCopyObject %8 %12 + OpBranch %19 + %21 = OpLabel + %24 = OpCopyObject %8 %9 + OpBranch %19 + %19 = OpLabel + %25 = OpPhi %8 %18 %20 %24 %21 + OpBranch %15 + %17 = OpLabel + %26 = OpCopyObject %8 %12 + %27 = OpCopyObject %8 %13 + OpBranch %28 + %28 = OpLabel + %29 = OpPhi %8 %10 %17 + OpBranch %15 + %15 = OpLabel + %30 = OpPhi %8 %25 %19 %29 %28 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_opselect_with_conditional_branch_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_opselect_with_conditional_branch_test.cpp new file mode 100644 index 0000000..1712855 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_opselect_with_conditional_branch_test.cpp @@ -0,0 +1,276 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_opselect_with_conditional_branch.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceOpSelectWithConditionalBranchTest, Inapplicable) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpConstant %5 1 + %7 = OpConstant %5 2 + %8 = OpTypeVector %5 4 + %9 = OpConstantNull %8 + %10 = OpConstantComposite %8 %6 %6 %7 %7 + %11 = OpTypeBool + %12 = OpTypeVector %11 4 + %13 = OpConstantTrue %11 + %14 = OpConstantFalse %11 + %15 = OpConstantComposite %12 %13 %14 %14 %13 + %2 = OpFunction %3 None %4 + %16 = OpLabel + %17 = OpCopyObject %5 %6 + %18 = OpCopyObject %5 %7 + OpBranch %19 + %19 = OpLabel + %20 = OpCopyObject %5 %17 + %21 = OpSelect %5 %13 %17 %18 + OpBranch %22 + %22 = OpLabel + %23 = OpSelect %8 %15 %9 %10 + OpBranch %24 + %24 = OpLabel + OpSelectionMerge %25 None + OpBranchConditional %13 %26 %27 + %26 = OpLabel + %28 = OpSelect %5 %13 %17 %18 + OpBranch %27 + %27 = OpLabel + %29 = OpSelect %5 %13 %17 %18 + OpBranch %25 + %25 = OpLabel + %30 = OpSelect %5 %13 %17 %18 + OpBranch %31 + %31 = OpLabel + OpLoopMerge %32 %33 None + OpBranch %33 + %33 = OpLabel + %34 = OpSelect %5 %13 %17 %18 + OpBranchConditional %13 %31 %32 + %32 = OpLabel + %35 = OpSelect %5 %13 %17 %18 + OpBranch %36 + %36 = OpLabel + %37 = OpSelect %5 %13 %17 %18 + OpReturn + OpFunctionEnd +)"; + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // %20 is not an OpSelect instruction. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(20, 100, 101) + .IsApplicable(context.get(), transformation_context)); + + // %21 is not the first instruction in its block. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(21, 100, 101) + .IsApplicable(context.get(), transformation_context)); + + // The condition for %23 is not a scalar, but a vector of booleans. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(23, 100, 101) + .IsApplicable(context.get(), transformation_context)); + + // The predecessor (%24) of the block containing %28 is the header of a + // selection construct and does not branch unconditionally. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(24, 100, 101) + .IsApplicable(context.get(), transformation_context)); + + // The block containing %29 has two predecessors (%24 and %26). + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(29, 100, 101) + .IsApplicable(context.get(), transformation_context)); + + // The block containing %30 is the merge block for a selection construct. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(30, 100, 101) + .IsApplicable(context.get(), transformation_context)); + + // The predecessor (%31) of the block containing %34 is a loop header. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(31, 100, 101) + .IsApplicable(context.get(), transformation_context)); + + // The block containing %35 is the merge block for a loop construct. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(35, 100, 101) + .IsApplicable(context.get(), transformation_context)); + +#ifndef NDEBUG + // |true_block_id| and |false_block_id| are both 0. + ASSERT_DEATH( + TransformationReplaceOpSelectWithConditionalBranch(37, 0, 0).IsApplicable( + context.get(), transformation_context), + "At least one of the ids must be non-zero."); +#endif + + // The fresh ids are not distinct. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(37, 100, 100) + .IsApplicable(context.get(), transformation_context)); + + // One of the ids is not fresh. + ASSERT_FALSE(TransformationReplaceOpSelectWithConditionalBranch(37, 100, 10) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationReplaceOpSelectWithConditionalBranchTest, Simple) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpConstant %5 1 + %7 = OpConstant %5 2 + %8 = OpTypeVector %5 4 + %9 = OpConstantNull %8 + %10 = OpConstantComposite %8 %6 %6 %7 %7 + %11 = OpTypeBool + %12 = OpTypeVector %11 4 + %13 = OpConstantTrue %11 + %14 = OpConstantFalse %11 + %15 = OpConstantComposite %12 %13 %14 %14 %13 + %2 = OpFunction %3 None %4 + %16 = OpLabel + %17 = OpCopyObject %5 %6 + %18 = OpCopyObject %5 %7 + OpBranch %19 + %19 = OpLabel + %20 = OpSelect %5 %13 %17 %18 + OpSelectionMerge %21 None + OpBranchConditional %13 %22 %21 + %22 = OpLabel + OpBranch %23 + %23 = OpLabel + %24 = OpSelect %8 %13 %9 %10 + OpBranch %21 + %21 = OpLabel + OpBranch %25 + %25 = OpLabel + %26 = OpSelect %5 %13 %17 %18 + OpReturn + OpFunctionEnd +)"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto transformation = + TransformationReplaceOpSelectWithConditionalBranch(20, 100, 101); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + auto transformation2 = + TransformationReplaceOpSelectWithConditionalBranch(24, 0, 102); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + auto transformation3 = + TransformationReplaceOpSelectWithConditionalBranch(26, 103, 0); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + OpSource ESSL 310 + OpName %2 "main" + %3 = OpTypeVoid + %4 = OpTypeFunction %3 + %5 = OpTypeInt 32 1 + %6 = OpConstant %5 1 + %7 = OpConstant %5 2 + %8 = OpTypeVector %5 4 + %9 = OpConstantNull %8 + %10 = OpConstantComposite %8 %6 %6 %7 %7 + %11 = OpTypeBool + %12 = OpTypeVector %11 4 + %13 = OpConstantTrue %11 + %14 = OpConstantFalse %11 + %15 = OpConstantComposite %12 %13 %14 %14 %13 + %2 = OpFunction %3 None %4 + %16 = OpLabel + %17 = OpCopyObject %5 %6 + %18 = OpCopyObject %5 %7 + OpSelectionMerge %19 None + OpBranchConditional %13 %100 %101 + %100 = OpLabel + OpBranch %19 + %101 = OpLabel + OpBranch %19 + %19 = OpLabel + %20 = OpPhi %5 %17 %100 %18 %101 + OpSelectionMerge %21 None + OpBranchConditional %13 %22 %21 + %22 = OpLabel + OpSelectionMerge %23 None + OpBranchConditional %13 %23 %102 + %102 = OpLabel + OpBranch %23 + %23 = OpLabel + %24 = OpPhi %8 %9 %22 %10 %102 + OpBranch %21 + %21 = OpLabel + OpSelectionMerge %25 None + OpBranchConditional %13 %103 %25 + %103 = OpLabel + OpBranch %25 + %25 = OpLabel + %26 = OpPhi %5 %17 %103 %18 %21 + OpReturn + OpFunctionEnd +)"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp new file mode 100644 index 0000000..478d809 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_parameter_with_global_test.cpp @@ -0,0 +1,364 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_parameter_with_global.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceParameterWithGlobalTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %13 0 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Private %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Private %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Private %10 + %12 = OpTypeBool + %71 = OpTypeFunction %2 %6 + %83 = OpTypeFunction %2 %6 %12 + %93 = OpTypeFunction %2 %10 + %94 = OpTypeFunction %2 %8 %10 + %40 = OpTypePointer Function %12 + %13 = OpTypeStruct %6 %8 + %14 = OpTypePointer Private %13 + %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12 + %22 = OpConstant %6 0 + %23 = OpConstant %8 0 + %26 = OpConstantComposite %10 %23 %23 + %27 = OpConstantTrue %12 + %28 = OpConstantComposite %13 %22 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %41 = OpVariable %40 Function %27 + %33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27 + OpReturn + OpFunctionEnd + + ; adjust type of the function in-place + %20 = OpFunction %2 None %15 + %16 = OpFunctionParameter %6 + %17 = OpFunctionParameter %8 + %18 = OpFunctionParameter %10 + %19 = OpFunctionParameter %13 + %42 = OpFunctionParameter %40 + %43 = OpFunctionParameter %12 + %21 = OpLabel + OpReturn + OpFunctionEnd + + ; reuse an existing function type + %70 = OpFunction %2 None %71 + %72 = OpFunctionParameter %6 + %73 = OpLabel + OpReturn + OpFunctionEnd + %74 = OpFunction %2 None %71 + %75 = OpFunctionParameter %6 + %76 = OpLabel + OpReturn + OpFunctionEnd + + ; create a new function type + %77 = OpFunction %2 None %83 + %78 = OpFunctionParameter %6 + %84 = OpFunctionParameter %12 + %79 = OpLabel + OpReturn + OpFunctionEnd + %80 = OpFunction %2 None %83 + %81 = OpFunctionParameter %6 + %85 = OpFunctionParameter %12 + %82 = OpLabel + OpReturn + OpFunctionEnd + + ; don't adjust the type of the function if it creates a duplicate + %86 = OpFunction %2 None %93 + %87 = OpFunctionParameter %10 + %89 = OpLabel + OpReturn + OpFunctionEnd + %90 = OpFunction %2 None %94 + %91 = OpFunctionParameter %8 + %95 = OpFunctionParameter %10 + %92 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Parameter id is invalid. + ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 50, 51) + .IsApplicable(context.get(), transformation_context)); + + // Parameter id is not a result id of an OpFunctionParameter instruction. + ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 21, 51) + .IsApplicable(context.get(), transformation_context)); + + // Parameter has unsupported type. + ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 42, 51) + .IsApplicable(context.get(), transformation_context)); + + // Initializer for a global variable doesn't exist in the module. + ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 43, 51) + .IsApplicable(context.get(), transformation_context)); + + // Pointer type for a global variable doesn't exist in the module. + ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 43, 51) + .IsApplicable(context.get(), transformation_context)); + + // Function type id is not fresh. + ASSERT_FALSE(TransformationReplaceParameterWithGlobal(16, 16, 51) + .IsApplicable(context.get(), transformation_context)); + + // Global variable id is not fresh. + ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 16, 16) + .IsApplicable(context.get(), transformation_context)); + + // Function type fresh id and global variable fresh id are equal. + ASSERT_FALSE(TransformationReplaceParameterWithGlobal(50, 16, 50) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationReplaceParameterWithGlobal transformation(50, 16, 51); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParameterWithGlobal transformation(52, 17, 53); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParameterWithGlobal transformation(54, 18, 55); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParameterWithGlobal transformation(56, 19, 57); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParameterWithGlobal transformation(58, 75, 59); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParameterWithGlobal transformation(60, 81, 61); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParameterWithGlobal transformation(62, 91, 63); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %13 0 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Private %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Private %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Private %10 + %12 = OpTypeBool + %71 = OpTypeFunction %2 %6 + %83 = OpTypeFunction %2 %6 %12 + %93 = OpTypeFunction %2 %10 + %40 = OpTypePointer Function %12 + %13 = OpTypeStruct %6 %8 + %14 = OpTypePointer Private %13 + %22 = OpConstant %6 0 + %23 = OpConstant %8 0 + %26 = OpConstantComposite %10 %23 %23 + %27 = OpConstantTrue %12 + %28 = OpConstantComposite %13 %22 %23 + %51 = OpVariable %7 Private %22 + %53 = OpVariable %9 Private %23 + %55 = OpVariable %11 Private %26 + %57 = OpVariable %14 Private %28 + %15 = OpTypeFunction %2 %40 %12 + %59 = OpVariable %7 Private %22 + %61 = OpVariable %7 Private %22 + %60 = OpTypeFunction %2 %12 + %63 = OpVariable %9 Private %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %41 = OpVariable %40 Function %27 + OpStore %51 %22 + OpStore %53 %23 + OpStore %55 %26 + OpStore %57 %28 + %33 = OpFunctionCall %2 %20 %41 %27 + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %15 + %42 = OpFunctionParameter %40 + %43 = OpFunctionParameter %12 + %21 = OpLabel + %19 = OpLoad %13 %57 + %18 = OpLoad %10 %55 + %17 = OpLoad %8 %53 + %16 = OpLoad %6 %51 + OpReturn + OpFunctionEnd + %70 = OpFunction %2 None %71 + %72 = OpFunctionParameter %6 + %73 = OpLabel + OpReturn + OpFunctionEnd + %74 = OpFunction %2 None %3 + %76 = OpLabel + %75 = OpLoad %6 %59 + OpReturn + OpFunctionEnd + %77 = OpFunction %2 None %83 + %78 = OpFunctionParameter %6 + %84 = OpFunctionParameter %12 + %79 = OpLabel + OpReturn + OpFunctionEnd + %80 = OpFunction %2 None %60 + %85 = OpFunctionParameter %12 + %82 = OpLabel + %81 = OpLoad %6 %61 + OpReturn + OpFunctionEnd + %86 = OpFunction %2 None %93 + %87 = OpFunctionParameter %10 + %89 = OpLabel + OpReturn + OpFunctionEnd + %90 = OpFunction %2 None %93 + %95 = OpFunctionParameter %10 + %92 = OpLabel + %91 = OpLoad %8 %63 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationReplaceParameterWithGlobalTest, + HandlesIrrelevantParameters) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %9 = OpTypeInt 32 1 + %3 = OpTypeFunction %2 + %7 = OpTypeFunction %2 %9 %9 + %12 = OpTypePointer Private %9 + %13 = OpConstant %9 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %6 = OpFunction %2 None %7 + %10 = OpFunctionParameter %9 + %11 = OpFunctionParameter %9 + %8 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(10); + + { + TransformationReplaceParameterWithGlobal transformation(20, 10, 21); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(21)); + } + { + TransformationReplaceParameterWithGlobal transformation(22, 11, 23); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_FALSE( + transformation_context.GetFactManager()->PointeeValueIsIrrelevant(23)); + } +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_replace_params_with_struct_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_replace_params_with_struct_test.cpp new file mode 100644 index 0000000..afa782e --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_replace_params_with_struct_test.cpp @@ -0,0 +1,527 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_replace_params_with_struct.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationReplaceParamsWithStructTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %13 0 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %2 = OpTypeVoid + %6 = OpTypeInt 32 1 + %3 = OpTypeFunction %2 + %7 = OpTypePointer Private %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Private %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Private %10 + %12 = OpTypeBool + %51 = OpTypeFunction %2 %12 + %64 = OpTypeStruct %6 + %63 = OpTypeFunction %2 %64 + %65 = OpTypeFunction %2 %6 + %75 = OpTypeStruct %8 + %76 = OpTypeFunction %2 %75 + %77 = OpTypeFunction %2 %8 + %40 = OpTypePointer Function %12 + %13 = OpTypeStruct %6 %8 + %45 = OpTypeStruct %6 %10 %13 + %46 = OpTypeStruct %12 + %47 = OpTypeStruct %8 %45 %46 + %14 = OpTypePointer Private %13 + %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12 + %22 = OpConstant %6 0 + %23 = OpConstant %8 0 + %26 = OpConstantComposite %10 %23 %23 + %27 = OpConstantTrue %12 + %28 = OpConstantComposite %13 %22 %23 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %41 = OpVariable %40 Function %27 + %33 = OpFunctionCall %2 %20 %22 %23 %26 %28 %41 %27 + OpReturn + OpFunctionEnd + + ; adjust type of the function in-place + %20 = OpFunction %2 None %15 + %16 = OpFunctionParameter %6 + %17 = OpFunctionParameter %8 + %18 = OpFunctionParameter %10 + %19 = OpFunctionParameter %13 + %42 = OpFunctionParameter %40 + %43 = OpFunctionParameter %12 + %21 = OpLabel + OpReturn + OpFunctionEnd + + ; create a new function type + %50 = OpFunction %2 None %51 + %52 = OpFunctionParameter %12 + %53 = OpLabel + OpReturn + OpFunctionEnd + %54 = OpFunction %2 None %51 + %55 = OpFunctionParameter %12 + %56 = OpLabel + OpReturn + OpFunctionEnd + + ; reuse an existing function type + %57 = OpFunction %2 None %63 + %58 = OpFunctionParameter %64 + %59 = OpLabel + OpReturn + OpFunctionEnd + %60 = OpFunction %2 None %65 + %61 = OpFunctionParameter %6 + %62 = OpLabel + OpReturn + OpFunctionEnd + %66 = OpFunction %2 None %65 + %67 = OpFunctionParameter %6 + %68 = OpLabel + OpReturn + OpFunctionEnd + + ; don't adjust the type of the function if it creates a duplicate + %69 = OpFunction %2 None %76 + %70 = OpFunctionParameter %75 + %71 = OpLabel + OpReturn + OpFunctionEnd + %72 = OpFunction %2 None %77 + %73 = OpFunctionParameter %8 + %74 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // |parameter_id| is empty. + ASSERT_FALSE( + TransformationReplaceParamsWithStruct({}, 90, 91, {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + + // |parameter_id| has duplicates. + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 16, 17}, 90, 91, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + + // |parameter_id| has invalid values. + ASSERT_FALSE(TransformationReplaceParamsWithStruct({21, 16, 17}, 90, 91, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 90, 17}, 90, 91, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + + // Parameter's belong to different functions. + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 52}, 90, 91, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + + // Parameter has unsupported type. + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17, 42, 43}, 90, 91, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + + // OpTypeStruct does not exist in the module. + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 43}, 90, 91, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + + // |caller_id_to_fresh_composite_id| misses values. + ASSERT_FALSE( + TransformationReplaceParamsWithStruct({16, 17}, 90, 91, {{90, 93}}) + .IsApplicable(context.get(), transformation_context)); + + // All fresh ids must be unique. + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 90, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91, + {{33, 90}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91, + {{33, 92}, {90, 92}}) + .IsApplicable(context.get(), transformation_context)); + + // All 'fresh' ids must be fresh. + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 91, + {{33, 33}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 33, 91, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationReplaceParamsWithStruct({16, 17}, 90, 33, + {{33, 92}, {90, 93}}) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationReplaceParamsWithStruct transformation({16, 18, 19}, 90, 91, + {{33, 92}, {90, 93}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParamsWithStruct transformation({43}, 93, 94, + {{33, 95}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParamsWithStruct transformation({17, 91, 94}, 96, 97, + {{33, 98}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParamsWithStruct transformation({55}, 99, 100, {{}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParamsWithStruct transformation({61}, 101, 102, {{}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + { + TransformationReplaceParamsWithStruct transformation({73}, 103, 104, {{}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string expected_shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpMemberDecorate %13 0 RelaxedPrecision + OpDecorate %16 RelaxedPrecision + %2 = OpTypeVoid + %6 = OpTypeInt 32 1 + %3 = OpTypeFunction %2 + %7 = OpTypePointer Private %6 + %8 = OpTypeFloat 32 + %9 = OpTypePointer Private %8 + %10 = OpTypeVector %8 2 + %11 = OpTypePointer Private %10 + %12 = OpTypeBool + %51 = OpTypeFunction %2 %12 + %64 = OpTypeStruct %6 + %63 = OpTypeFunction %2 %64 + %65 = OpTypeFunction %2 %6 + %75 = OpTypeStruct %8 + %76 = OpTypeFunction %2 %75 + %40 = OpTypePointer Function %12 + %13 = OpTypeStruct %6 %8 + %45 = OpTypeStruct %6 %10 %13 + %46 = OpTypeStruct %12 + %47 = OpTypeStruct %8 %45 %46 + %14 = OpTypePointer Private %13 + %22 = OpConstant %6 0 + %23 = OpConstant %8 0 + %26 = OpConstantComposite %10 %23 %23 + %27 = OpConstantTrue %12 + %28 = OpConstantComposite %13 %22 %23 + %15 = OpTypeFunction %2 %40 %47 + %99 = OpTypeFunction %2 %46 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %41 = OpVariable %40 Function %27 + %92 = OpCompositeConstruct %45 %22 %26 %28 + %95 = OpCompositeConstruct %46 %27 + %98 = OpCompositeConstruct %47 %23 %92 %95 + %33 = OpFunctionCall %2 %20 %41 %98 + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %15 + %42 = OpFunctionParameter %40 + %97 = OpFunctionParameter %47 + %21 = OpLabel + %94 = OpCompositeExtract %46 %97 2 + %91 = OpCompositeExtract %45 %97 1 + %17 = OpCompositeExtract %8 %97 0 + %43 = OpCompositeExtract %12 %94 0 + %19 = OpCompositeExtract %13 %91 2 + %18 = OpCompositeExtract %10 %91 1 + %16 = OpCompositeExtract %6 %91 0 + OpReturn + OpFunctionEnd + %50 = OpFunction %2 None %51 + %52 = OpFunctionParameter %12 + %53 = OpLabel + OpReturn + OpFunctionEnd + %54 = OpFunction %2 None %99 + %100 = OpFunctionParameter %46 + %56 = OpLabel + %55 = OpCompositeExtract %12 %100 0 + OpReturn + OpFunctionEnd + %57 = OpFunction %2 None %63 + %58 = OpFunctionParameter %64 + %59 = OpLabel + OpReturn + OpFunctionEnd + %60 = OpFunction %2 None %63 + %102 = OpFunctionParameter %64 + %62 = OpLabel + %61 = OpCompositeExtract %6 %102 0 + OpReturn + OpFunctionEnd + %66 = OpFunction %2 None %65 + %67 = OpFunctionParameter %6 + %68 = OpLabel + OpReturn + OpFunctionEnd + %69 = OpFunction %2 None %76 + %70 = OpFunctionParameter %75 + %71 = OpLabel + OpReturn + OpFunctionEnd + %72 = OpFunction %2 None %76 + %104 = OpFunctionParameter %75 + %74 = OpLabel + %73 = OpCompositeExtract %8 %104 0 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); +} + +TEST(TransformationReplaceParamsWithStructTest, ParametersRemainValid) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %6 = OpTypeInt 32 1 + %3 = OpTypeFunction %2 + %8 = OpTypeFloat 32 + %10 = OpTypeVector %8 2 + %12 = OpTypeBool + %40 = OpTypePointer Function %12 + %13 = OpTypeStruct %6 %8 + %45 = OpTypeStruct %6 %8 %13 + %47 = OpTypeStruct %45 %12 %10 + %15 = OpTypeFunction %2 %6 %8 %10 %13 %40 %12 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %15 + %16 = OpFunctionParameter %6 + %17 = OpFunctionParameter %8 + %18 = OpFunctionParameter %10 + %19 = OpFunctionParameter %13 + %42 = OpFunctionParameter %40 + %43 = OpFunctionParameter %12 + %21 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + { + // Try to replace parameters in "increasing" order of their declaration. + TransformationReplaceParamsWithStruct transformation({16, 17, 19}, 70, 71, + {{}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %6 = OpTypeInt 32 1 + %3 = OpTypeFunction %2 + %8 = OpTypeFloat 32 + %10 = OpTypeVector %8 2 + %12 = OpTypeBool + %40 = OpTypePointer Function %12 + %13 = OpTypeStruct %6 %8 + %45 = OpTypeStruct %6 %8 %13 + %47 = OpTypeStruct %45 %12 %10 + %15 = OpTypeFunction %2 %10 %40 %12 %45 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %15 + %18 = OpFunctionParameter %10 + %42 = OpFunctionParameter %40 + %43 = OpFunctionParameter %12 + %71 = OpFunctionParameter %45 + %21 = OpLabel + %19 = OpCompositeExtract %13 %71 2 + %17 = OpCompositeExtract %8 %71 1 + %16 = OpCompositeExtract %6 %71 0 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); + + { + // Try to replace parameters in "decreasing" order of their declaration. + TransformationReplaceParamsWithStruct transformation({71, 43, 18}, 72, 73, + {{}}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %6 = OpTypeInt 32 1 + %3 = OpTypeFunction %2 + %8 = OpTypeFloat 32 + %10 = OpTypeVector %8 2 + %12 = OpTypeBool + %40 = OpTypePointer Function %12 + %13 = OpTypeStruct %6 %8 + %45 = OpTypeStruct %6 %8 %13 + %47 = OpTypeStruct %45 %12 %10 + %15 = OpTypeFunction %2 %40 %47 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpReturn + OpFunctionEnd + %20 = OpFunction %2 None %15 + %42 = OpFunctionParameter %40 + %73 = OpFunctionParameter %47 + %21 = OpLabel + %18 = OpCompositeExtract %10 %73 2 + %43 = OpCompositeExtract %12 %73 1 + %71 = OpCompositeExtract %45 %73 0 + %19 = OpCompositeExtract %13 %71 2 + %17 = OpCompositeExtract %8 %71 1 + %16 = OpCompositeExtract %6 %71 0 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationReplaceParamsWithStructTest, IsomorphicStructs) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %16 "main" + OpExecutionMode %16 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %6 = OpTypeInt 32 1 + %7 = OpTypeStruct %6 + %8 = OpTypeStruct %6 + %9 = OpTypeStruct %8 + %10 = OpTypeFunction %2 %7 + %15 = OpTypeFunction %2 + %16 = OpFunction %2 None %15 + %17 = OpLabel + OpReturn + OpFunctionEnd + %11 = OpFunction %2 None %10 + %12 = OpFunctionParameter %7 + %13 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + ASSERT_FALSE(TransformationReplaceParamsWithStruct({12}, 100, 101, {{}}) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp new file mode 100644 index 0000000..85402e1 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_set_function_control_test.cpp @@ -0,0 +1,263 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_function_control.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSetFunctionControlTest, VariousScenarios) { + // This is a simple transformation; this test captures the important things + // to check for. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "foo(i1;i1;" + OpName %9 "a" + OpName %10 "b" + OpName %13 "bar(" + OpName %17 "baz(i1;" + OpName %16 "x" + OpName %21 "boo(i1;i1;" + OpName %19 "a" + OpName %20 "b" + OpName %29 "g" + OpName %42 "param" + OpName %44 "param" + OpName %45 "param" + OpName %48 "param" + OpName %49 "param" + OpName %54 "color" + OpDecorate %54 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 %7 + %15 = OpTypeFunction %6 %7 + %28 = OpTypePointer Private %6 + %29 = OpVariable %28 Private + %30 = OpConstant %6 2 + %31 = OpConstant %6 5 + %51 = OpTypeFloat 32 + %52 = OpTypeVector %51 4 + %53 = OpTypePointer Output %52 + %54 = OpVariable %53 Output + %4 = OpFunction %2 None %3 + %5 = OpLabel + %42 = OpVariable %7 Function + %44 = OpVariable %7 Function + %45 = OpVariable %7 Function + %48 = OpVariable %7 Function + %49 = OpVariable %7 Function + %41 = OpFunctionCall %2 %13 + OpStore %42 %30 + %43 = OpFunctionCall %6 %17 %42 + OpStore %44 %31 + %46 = OpLoad %6 %29 + OpStore %45 %46 + %47 = OpFunctionCall %6 %21 %44 %45 + OpStore %48 %43 + OpStore %49 %47 + %50 = OpFunctionCall %6 %11 %48 %49 + OpReturn + OpFunctionEnd + %11 = OpFunction %6 Const %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %7 + %12 = OpLabel + %23 = OpLoad %6 %9 + %24 = OpLoad %6 %10 + %25 = OpIAdd %6 %23 %24 + OpReturnValue %25 + OpFunctionEnd + %13 = OpFunction %2 Inline %3 + %14 = OpLabel + OpStore %29 %30 + OpReturn + OpFunctionEnd + %17 = OpFunction %6 Pure|DontInline %15 + %16 = OpFunctionParameter %7 + %18 = OpLabel + %32 = OpLoad %6 %16 + %33 = OpIAdd %6 %31 %32 + OpReturnValue %33 + OpFunctionEnd + %21 = OpFunction %6 DontInline %8 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %7 + %22 = OpLabel + %36 = OpLoad %6 %19 + %37 = OpLoad %6 %20 + %38 = OpIMul %6 %36 %37 + OpReturnValue %38 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // %36 is not a function + ASSERT_FALSE(TransformationSetFunctionControl(36, SpvFunctionControlMaskNone) + .IsApplicable(context.get(), transformation_context)); + // Cannot add the Pure function control to %4 as it did not already have it + ASSERT_FALSE(TransformationSetFunctionControl(4, SpvFunctionControlPureMask) + .IsApplicable(context.get(), transformation_context)); + // Cannot add the Const function control to %21 as it did not already + // have it + ASSERT_FALSE(TransformationSetFunctionControl(21, SpvFunctionControlConstMask) + .IsApplicable(context.get(), transformation_context)); + + // Set to None, removing Const + TransformationSetFunctionControl transformation1(11, + SpvFunctionControlMaskNone); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + // Set to Inline; silly to do it on an entry point, but it is allowed + TransformationSetFunctionControl transformation2( + 4, SpvFunctionControlInlineMask); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + // Set to Pure, removing DontInline + TransformationSetFunctionControl transformation3(17, + SpvFunctionControlPureMask); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + + // Change from Inline to DontInline + TransformationSetFunctionControl transformation4( + 13, SpvFunctionControlDontInlineMask); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %54 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %11 "foo(i1;i1;" + OpName %9 "a" + OpName %10 "b" + OpName %13 "bar(" + OpName %17 "baz(i1;" + OpName %16 "x" + OpName %21 "boo(i1;i1;" + OpName %19 "a" + OpName %20 "b" + OpName %29 "g" + OpName %42 "param" + OpName %44 "param" + OpName %45 "param" + OpName %48 "param" + OpName %49 "param" + OpName %54 "color" + OpDecorate %54 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %8 = OpTypeFunction %6 %7 %7 + %15 = OpTypeFunction %6 %7 + %28 = OpTypePointer Private %6 + %29 = OpVariable %28 Private + %30 = OpConstant %6 2 + %31 = OpConstant %6 5 + %51 = OpTypeFloat 32 + %52 = OpTypeVector %51 4 + %53 = OpTypePointer Output %52 + %54 = OpVariable %53 Output + %4 = OpFunction %2 Inline %3 + %5 = OpLabel + %42 = OpVariable %7 Function + %44 = OpVariable %7 Function + %45 = OpVariable %7 Function + %48 = OpVariable %7 Function + %49 = OpVariable %7 Function + %41 = OpFunctionCall %2 %13 + OpStore %42 %30 + %43 = OpFunctionCall %6 %17 %42 + OpStore %44 %31 + %46 = OpLoad %6 %29 + OpStore %45 %46 + %47 = OpFunctionCall %6 %21 %44 %45 + OpStore %48 %43 + OpStore %49 %47 + %50 = OpFunctionCall %6 %11 %48 %49 + OpReturn + OpFunctionEnd + %11 = OpFunction %6 None %8 + %9 = OpFunctionParameter %7 + %10 = OpFunctionParameter %7 + %12 = OpLabel + %23 = OpLoad %6 %9 + %24 = OpLoad %6 %10 + %25 = OpIAdd %6 %23 %24 + OpReturnValue %25 + OpFunctionEnd + %13 = OpFunction %2 DontInline %3 + %14 = OpLabel + OpStore %29 %30 + OpReturn + OpFunctionEnd + %17 = OpFunction %6 Pure %15 + %16 = OpFunctionParameter %7 + %18 = OpLabel + %32 = OpLoad %6 %16 + %33 = OpIAdd %6 %31 %32 + OpReturnValue %33 + OpFunctionEnd + %21 = OpFunction %6 DontInline %8 + %19 = OpFunctionParameter %7 + %20 = OpFunctionParameter %7 + %22 = OpLabel + %36 = OpLoad %6 %19 + %37 = OpLoad %6 %20 + %38 = OpIMul %6 %36 %37 + OpReturnValue %38 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp new file mode 100644 index 0000000..3312a67 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_set_loop_control_test.cpp @@ -0,0 +1,986 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_loop_control.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSetLoopControlTest, VariousScenarios) { + // This test features loops with various different controls, and goes through + // a number of acceptable and unacceptable transformations to those controls. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + %32 = OpVariable %7 Function + %42 = OpVariable %7 Function + %52 = OpVariable %7 Function + %62 = OpVariable %7 Function + %72 = OpVariable %7 Function + %82 = OpVariable %7 Function + %92 = OpVariable %7 Function + %102 = OpVariable %7 Function + %112 = OpVariable %7 Function + %122 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %132 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %132 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %132 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %133 = OpPhi %6 %9 %12 %31 %26 + OpLoopMerge %25 %26 Unroll + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %133 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %31 = OpIAdd %6 %133 %20 + OpStore %22 %31 + OpBranch %23 + %25 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + %134 = OpPhi %6 %9 %25 %41 %36 + OpLoopMerge %35 %36 DontUnroll + OpBranch %37 + %37 = OpLabel + %39 = OpSLessThan %17 %134 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpBranch %36 + %36 = OpLabel + %41 = OpIAdd %6 %134 %20 + OpStore %32 %41 + OpBranch %33 + %35 = OpLabel + OpStore %42 %9 + OpBranch %43 + %43 = OpLabel + %135 = OpPhi %6 %9 %35 %51 %46 + OpLoopMerge %45 %46 DependencyInfinite + OpBranch %47 + %47 = OpLabel + %49 = OpSLessThan %17 %135 %16 + OpBranchConditional %49 %44 %45 + %44 = OpLabel + OpBranch %46 + %46 = OpLabel + %51 = OpIAdd %6 %135 %20 + OpStore %42 %51 + OpBranch %43 + %45 = OpLabel + OpStore %52 %9 + OpBranch %53 + %53 = OpLabel + %136 = OpPhi %6 %9 %45 %61 %56 + OpLoopMerge %55 %56 DependencyLength 3 + OpBranch %57 + %57 = OpLabel + %59 = OpSLessThan %17 %136 %16 + OpBranchConditional %59 %54 %55 + %54 = OpLabel + OpBranch %56 + %56 = OpLabel + %61 = OpIAdd %6 %136 %20 + OpStore %52 %61 + OpBranch %53 + %55 = OpLabel + OpStore %62 %9 + OpBranch %63 + %63 = OpLabel + %137 = OpPhi %6 %9 %55 %71 %66 + OpLoopMerge %65 %66 MinIterations 10 + OpBranch %67 + %67 = OpLabel + %69 = OpSLessThan %17 %137 %16 + OpBranchConditional %69 %64 %65 + %64 = OpLabel + OpBranch %66 + %66 = OpLabel + %71 = OpIAdd %6 %137 %20 + OpStore %62 %71 + OpBranch %63 + %65 = OpLabel + OpStore %72 %9 + OpBranch %73 + %73 = OpLabel + %138 = OpPhi %6 %9 %65 %81 %76 + OpLoopMerge %75 %76 MaxIterations 50 + OpBranch %77 + %77 = OpLabel + %79 = OpSLessThan %17 %138 %16 + OpBranchConditional %79 %74 %75 + %74 = OpLabel + OpBranch %76 + %76 = OpLabel + %81 = OpIAdd %6 %138 %20 + OpStore %72 %81 + OpBranch %73 + %75 = OpLabel + OpStore %82 %9 + OpBranch %83 + %83 = OpLabel + %139 = OpPhi %6 %9 %75 %91 %86 + OpLoopMerge %85 %86 IterationMultiple 4 + OpBranch %87 + %87 = OpLabel + %89 = OpSLessThan %17 %139 %16 + OpBranchConditional %89 %84 %85 + %84 = OpLabel + OpBranch %86 + %86 = OpLabel + %91 = OpIAdd %6 %139 %20 + OpStore %82 %91 + OpBranch %83 + %85 = OpLabel + OpStore %92 %9 + OpBranch %93 + %93 = OpLabel + %140 = OpPhi %6 %9 %85 %101 %96 + OpLoopMerge %95 %96 PeelCount 2 + OpBranch %97 + %97 = OpLabel + %99 = OpSLessThan %17 %140 %16 + OpBranchConditional %99 %94 %95 + %94 = OpLabel + OpBranch %96 + %96 = OpLabel + %101 = OpIAdd %6 %140 %20 + OpStore %92 %101 + OpBranch %93 + %95 = OpLabel + OpStore %102 %9 + OpBranch %103 + %103 = OpLabel + %141 = OpPhi %6 %9 %95 %111 %106 + OpLoopMerge %105 %106 PartialCount 3 + OpBranch %107 + %107 = OpLabel + %109 = OpSLessThan %17 %141 %16 + OpBranchConditional %109 %104 %105 + %104 = OpLabel + OpBranch %106 + %106 = OpLabel + %111 = OpIAdd %6 %141 %20 + OpStore %102 %111 + OpBranch %103 + %105 = OpLabel + OpStore %112 %9 + OpBranch %113 + %113 = OpLabel + %142 = OpPhi %6 %9 %105 %121 %116 + OpLoopMerge %115 %116 Unroll|PeelCount|PartialCount 3 4 + OpBranch %117 + %117 = OpLabel + %119 = OpSLessThan %17 %142 %16 + OpBranchConditional %119 %114 %115 + %114 = OpLabel + OpBranch %116 + %116 = OpLabel + %121 = OpIAdd %6 %142 %20 + OpStore %112 %121 + OpBranch %113 + %115 = OpLabel + OpStore %122 %9 + OpBranch %123 + %123 = OpLabel + %143 = OpPhi %6 %9 %115 %131 %126 + OpLoopMerge %125 %126 DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount 2 5 90 4 7 14 + OpBranch %127 + %127 = OpLabel + %129 = OpSLessThan %17 %143 %16 + OpBranchConditional %129 %124 %125 + %124 = OpLabel + OpBranch %126 + %126 = OpLabel + %131 = OpIAdd %6 %143 %20 + OpStore %122 %131 + OpBranch %123 + %125 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // These are the loop headers together with the selection controls of their + // merge instructions: + // %10 None + // %23 Unroll + // %33 DontUnroll + // %43 DependencyInfinite + // %53 DependencyLength 3 + // %63 MinIterations 10 + // %73 MaxIterations 50 + // %83 IterationMultiple 4 + // %93 PeelCount 2 + // %103 PartialCount 3 + // %113 Unroll|PeelCount|PartialCount 3 4 + // %123 + // DependencyLength|MinIterations|MaxIterations|IterationMultiple|PeelCount|PartialCount + // 2 5 90 4 7 14 + + ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(10, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(10, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl( + 10, SpvLoopControlDependencyInfiniteMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlDependencyLengthMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlMinIterationsMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlMaxIterationsMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl( + 10, SpvLoopControlIterationMultipleMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlPeelCountMask, 3, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 0, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(10, SpvLoopControlPartialCountMask, 3, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl( + 10, + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 3, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(10, + SpvLoopControlUnrollMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 3, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl(10, + SpvLoopControlDontUnrollMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 3, 3) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(23, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl( + 23, + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 3, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(23, SpvLoopControlMaxIterationsMask, 2, 3) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(33, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(33, SpvLoopControlMinIterationsMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl( + 33, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl(33, + SpvLoopControlDontUnrollMask | + SpvLoopControlPartialCountMask, + 0, 10) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(43, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(43, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl( + 43, + SpvLoopControlMaskNone | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl( + 43, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl( + 43, + SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(43, + SpvLoopControlDependencyInfiniteMask | + SpvLoopControlDependencyLengthMask, + 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl( + 43, SpvLoopControlUnrollMask | SpvLoopControlPeelCountMask, 5, 0) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(53, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(53, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(53, SpvLoopControlMaxIterationsMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl( + 53, SpvLoopControlMaskNone | SpvLoopControlDependencyLengthMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl( + 53, SpvLoopControlUnrollMask | SpvLoopControlDependencyInfiniteMask, + 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl( + 53, SpvLoopControlDontUnrollMask | SpvLoopControlDependencyLengthMask, + 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(53, + SpvLoopControlDependencyInfiniteMask | + SpvLoopControlDependencyLengthMask, + 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl( + 53, + SpvLoopControlUnrollMask | SpvLoopControlDependencyLengthMask | + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 5, 3) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(63, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(63, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(63, + SpvLoopControlUnrollMask | + SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 5, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(63, + SpvLoopControlUnrollMask | + SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask, + 23, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl( + 63, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask, + 2, 23) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(73, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(73, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl( + 73, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 5, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(73, + SpvLoopControlUnrollMask | + SpvLoopControlMaxIterationsMask | + SpvLoopControlPeelCountMask, + 23, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl( + 73, + SpvLoopControlUnrollMask | SpvLoopControlMaxIterationsMask | + SpvLoopControlPeelCountMask, + 2, 23) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(83, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl( + 83, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 5, 3) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(83, + SpvLoopControlUnrollMask | + SpvLoopControlIterationMultipleMask | + SpvLoopControlPeelCountMask, + 23, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(83, + SpvLoopControlUnrollMask | + SpvLoopControlIterationMultipleMask | + SpvLoopControlPeelCountMask, + 2, 23) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(93, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(93, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl(93, SpvLoopControlPeelCountMask, 8, 8) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(93, SpvLoopControlPartialCountMask, 0, 8) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl( + 93, + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 16, 8) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(103, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(103, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl(103, + SpvLoopControlDontUnrollMask | + SpvLoopControlPartialCountMask, + 0, 60) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(113, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(113, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl( + 113, + SpvLoopControlIterationMultipleMask | SpvLoopControlPeelCountMask, 12, + 0) + .IsApplicable(context.get(), transformation_context)); + + ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlMaskNone, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(123, SpvLoopControlUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl(123, SpvLoopControlDontUnrollMask, 0, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + TransformationSetLoopControl( + 123, + SpvLoopControlMinIterationsMask | SpvLoopControlMaxIterationsMask | + SpvLoopControlIterationMultipleMask | + SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, + 7, 8) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE(TransformationSetLoopControl(123, + SpvLoopControlUnrollMask | + SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | + SpvLoopControlPartialCountMask, + 0, 9) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSetLoopControl( + 123, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | + SpvLoopControlPartialCountMask, + 7, 9) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSetLoopControl( + 123, + SpvLoopControlDontUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask, + 7, 9) + .IsApplicable(context.get(), transformation_context)); + + ApplyAndCheckFreshIds( + TransformationSetLoopControl(10, + SpvLoopControlUnrollMask | + SpvLoopControlPeelCountMask | + SpvLoopControlPartialCountMask, + 3, 3), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl(23, SpvLoopControlDontUnrollMask, 0, 0), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl(33, SpvLoopControlUnrollMask, 0, 0), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl( + 43, + SpvLoopControlDontUnrollMask | SpvLoopControlDependencyInfiniteMask, + 0, 0), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl(53, SpvLoopControlMaskNone, 0, 0), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl(63, + SpvLoopControlUnrollMask | + SpvLoopControlMinIterationsMask | + SpvLoopControlPeelCountMask, + 23, 0), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl(73, + SpvLoopControlUnrollMask | + SpvLoopControlMaxIterationsMask | + SpvLoopControlPeelCountMask, + 23, 0), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl(83, SpvLoopControlDontUnrollMask, 0, 0), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl( + 93, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 16, + 8), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl(103, SpvLoopControlPartialCountMask, 0, 60), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl(113, SpvLoopControlPeelCountMask, 12, 0), + context.get(), &transformation_context); + ApplyAndCheckFreshIds( + TransformationSetLoopControl( + 123, + SpvLoopControlUnrollMask | SpvLoopControlMinIterationsMask | + SpvLoopControlMaxIterationsMask | SpvLoopControlPartialCountMask, + 0, 9), + context.get(), &transformation_context); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %22 = OpVariable %7 Function + %32 = OpVariable %7 Function + %42 = OpVariable %7 Function + %52 = OpVariable %7 Function + %62 = OpVariable %7 Function + %72 = OpVariable %7 Function + %82 = OpVariable %7 Function + %92 = OpVariable %7 Function + %102 = OpVariable %7 Function + %112 = OpVariable %7 Function + %122 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %132 = OpPhi %6 %9 %5 %21 %13 + OpLoopMerge %12 %13 Unroll|PeelCount|PartialCount 3 3 + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %132 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %21 = OpIAdd %6 %132 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpStore %22 %9 + OpBranch %23 + %23 = OpLabel + %133 = OpPhi %6 %9 %12 %31 %26 + OpLoopMerge %25 %26 DontUnroll + OpBranch %27 + %27 = OpLabel + %29 = OpSLessThan %17 %133 %16 + OpBranchConditional %29 %24 %25 + %24 = OpLabel + OpBranch %26 + %26 = OpLabel + %31 = OpIAdd %6 %133 %20 + OpStore %22 %31 + OpBranch %23 + %25 = OpLabel + OpStore %32 %9 + OpBranch %33 + %33 = OpLabel + %134 = OpPhi %6 %9 %25 %41 %36 + OpLoopMerge %35 %36 Unroll + OpBranch %37 + %37 = OpLabel + %39 = OpSLessThan %17 %134 %16 + OpBranchConditional %39 %34 %35 + %34 = OpLabel + OpBranch %36 + %36 = OpLabel + %41 = OpIAdd %6 %134 %20 + OpStore %32 %41 + OpBranch %33 + %35 = OpLabel + OpStore %42 %9 + OpBranch %43 + %43 = OpLabel + %135 = OpPhi %6 %9 %35 %51 %46 + OpLoopMerge %45 %46 DontUnroll|DependencyInfinite + OpBranch %47 + %47 = OpLabel + %49 = OpSLessThan %17 %135 %16 + OpBranchConditional %49 %44 %45 + %44 = OpLabel + OpBranch %46 + %46 = OpLabel + %51 = OpIAdd %6 %135 %20 + OpStore %42 %51 + OpBranch %43 + %45 = OpLabel + OpStore %52 %9 + OpBranch %53 + %53 = OpLabel + %136 = OpPhi %6 %9 %45 %61 %56 + OpLoopMerge %55 %56 None + OpBranch %57 + %57 = OpLabel + %59 = OpSLessThan %17 %136 %16 + OpBranchConditional %59 %54 %55 + %54 = OpLabel + OpBranch %56 + %56 = OpLabel + %61 = OpIAdd %6 %136 %20 + OpStore %52 %61 + OpBranch %53 + %55 = OpLabel + OpStore %62 %9 + OpBranch %63 + %63 = OpLabel + %137 = OpPhi %6 %9 %55 %71 %66 + OpLoopMerge %65 %66 Unroll|MinIterations|PeelCount 10 23 + OpBranch %67 + %67 = OpLabel + %69 = OpSLessThan %17 %137 %16 + OpBranchConditional %69 %64 %65 + %64 = OpLabel + OpBranch %66 + %66 = OpLabel + %71 = OpIAdd %6 %137 %20 + OpStore %62 %71 + OpBranch %63 + %65 = OpLabel + OpStore %72 %9 + OpBranch %73 + %73 = OpLabel + %138 = OpPhi %6 %9 %65 %81 %76 + OpLoopMerge %75 %76 Unroll|MaxIterations|PeelCount 50 23 + OpBranch %77 + %77 = OpLabel + %79 = OpSLessThan %17 %138 %16 + OpBranchConditional %79 %74 %75 + %74 = OpLabel + OpBranch %76 + %76 = OpLabel + %81 = OpIAdd %6 %138 %20 + OpStore %72 %81 + OpBranch %73 + %75 = OpLabel + OpStore %82 %9 + OpBranch %83 + %83 = OpLabel + %139 = OpPhi %6 %9 %75 %91 %86 + OpLoopMerge %85 %86 DontUnroll + OpBranch %87 + %87 = OpLabel + %89 = OpSLessThan %17 %139 %16 + OpBranchConditional %89 %84 %85 + %84 = OpLabel + OpBranch %86 + %86 = OpLabel + %91 = OpIAdd %6 %139 %20 + OpStore %82 %91 + OpBranch %83 + %85 = OpLabel + OpStore %92 %9 + OpBranch %93 + %93 = OpLabel + %140 = OpPhi %6 %9 %85 %101 %96 + OpLoopMerge %95 %96 PeelCount|PartialCount 16 8 + OpBranch %97 + %97 = OpLabel + %99 = OpSLessThan %17 %140 %16 + OpBranchConditional %99 %94 %95 + %94 = OpLabel + OpBranch %96 + %96 = OpLabel + %101 = OpIAdd %6 %140 %20 + OpStore %92 %101 + OpBranch %93 + %95 = OpLabel + OpStore %102 %9 + OpBranch %103 + %103 = OpLabel + %141 = OpPhi %6 %9 %95 %111 %106 + OpLoopMerge %105 %106 PartialCount 60 + OpBranch %107 + %107 = OpLabel + %109 = OpSLessThan %17 %141 %16 + OpBranchConditional %109 %104 %105 + %104 = OpLabel + OpBranch %106 + %106 = OpLabel + %111 = OpIAdd %6 %141 %20 + OpStore %102 %111 + OpBranch %103 + %105 = OpLabel + OpStore %112 %9 + OpBranch %113 + %113 = OpLabel + %142 = OpPhi %6 %9 %105 %121 %116 + OpLoopMerge %115 %116 PeelCount 12 + OpBranch %117 + %117 = OpLabel + %119 = OpSLessThan %17 %142 %16 + OpBranchConditional %119 %114 %115 + %114 = OpLabel + OpBranch %116 + %116 = OpLabel + %121 = OpIAdd %6 %142 %20 + OpStore %112 %121 + OpBranch %113 + %115 = OpLabel + OpStore %122 %9 + OpBranch %123 + %123 = OpLabel + %143 = OpPhi %6 %9 %115 %131 %126 + OpLoopMerge %125 %126 Unroll|MinIterations|MaxIterations|PartialCount 5 90 9 + OpBranch %127 + %127 = OpLabel + %129 = OpSLessThan %17 %143 %16 + OpBranchConditional %129 %124 %125 + %124 = OpLabel + OpBranch %126 + %126 = OpLabel + %131 = OpIAdd %6 %143 %20 + OpStore %122 %131 + OpBranch %123 + %125 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationSetLoopControlTest, CheckSPIRVVersionsRespected) { + // This test checks that we do not allow introducing PeelCount and + // PartialCount loop controls if the SPIR-V version being used does not + // support them. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + OpBranch %13 + %13 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpIAdd %6 %19 %20 + OpStore %8 %21 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + for (auto env : + {SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, SPV_ENV_UNIVERSAL_1_2, + SPV_ENV_UNIVERSAL_1_3, SPV_ENV_UNIVERSAL_1_4, SPV_ENV_UNIVERSAL_1_5}) { + const auto consumer = nullptr; + const auto context = + BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationSetLoopControl transformation( + 10, SpvLoopControlPeelCountMask | SpvLoopControlPartialCountMask, 4, 4); + + switch (env) { + case SPV_ENV_UNIVERSAL_1_0: + case SPV_ENV_UNIVERSAL_1_1: + case SPV_ENV_UNIVERSAL_1_2: + case SPV_ENV_UNIVERSAL_1_3: + // PeelCount and PartialCount were introduced in SPIRV 1.4, so are not + // valid in the context of older versions. + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + break; + case SPV_ENV_UNIVERSAL_1_4: + case SPV_ENV_UNIVERSAL_1_5: + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + break; + default: + assert(false && "Unhandled environment"); + break; + } + } +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp new file mode 100644 index 0000000..4763d7a --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_set_memory_operands_mask_test.cpp @@ -0,0 +1,520 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_memory_operands_mask.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSetMemoryOperandsMaskTest, PreSpirv14) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "Point3D" + OpMemberName %7 0 "x" + OpMemberName %7 1 "y" + OpMemberName %7 2 "z" + OpName %12 "global_points" + OpName %15 "block" + OpMemberName %15 0 "in_points" + OpMemberName %15 1 "in_point" + OpName %17 "" + OpName %133 "local_points" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpMemberDecorate %7 2 Offset 8 + OpDecorate %10 ArrayStride 16 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 1 Offset 192 + OpDecorate %15 Block + OpDecorate %17 DescriptorSet 0 + OpDecorate %17 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 %6 %6 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 12 + %10 = OpTypeArray %7 %9 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private + %15 = OpTypeStruct %10 %7 + %16 = OpTypePointer Uniform %15 + %17 = OpVariable %16 Uniform + %18 = OpTypeInt 32 1 + %19 = OpConstant %18 0 + %20 = OpTypePointer Uniform %10 + %24 = OpTypePointer Private %7 + %27 = OpTypePointer Private %6 + %30 = OpConstant %18 1 + %132 = OpTypePointer Function %10 + %135 = OpTypePointer Uniform %7 + %145 = OpTypePointer Function %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %133 = OpVariable %132 Function + %21 = OpAccessChain %20 %17 %19 + OpCopyMemory %12 %21 Aligned 16 + OpCopyMemory %133 %12 Volatile + OpCopyMemory %133 %12 + %136 = OpAccessChain %135 %17 %30 + %138 = OpAccessChain %24 %12 %19 + OpCopyMemory %138 %136 None + %146 = OpAccessChain %145 %133 %30 + %147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16 + %148 = OpAccessChain %24 %12 %19 + OpStore %148 %147 Nontemporal + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Not OK: the instruction is not a memory access. + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpAccessChain, 0), + SpvMemoryAccessMaskNone, 0) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to remove Aligned + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(147, SpvOpLoad, 0), + SpvMemoryAccessVolatileMask | SpvMemoryAccessNontemporalMask, + 0) + .IsApplicable(context.get(), transformation_context)); + + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(147, SpvOpLoad, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + // Not OK to remove Aligned + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessMaskNone, 0) + .IsApplicable(context.get(), transformation_context)); + + // OK: leaves the mask as is + ASSERT_TRUE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask, 0) + .IsApplicable(context.get(), transformation_context)); + + { + // OK: adds Nontemporal and Volatile + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask | + SpvMemoryAccessVolatileMask, + 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + // Not OK to remove Volatile + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessNontemporalMask, 0) + .IsApplicable(context.get(), transformation_context)); + + // Not OK to add Aligned + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 0) + .IsApplicable(context.get(), transformation_context)); + + { + // OK: adds Nontemporal + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + // OK: adds Nontemporal (creates new operand) + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 2), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + // OK: adds Nontemporal and Volatile + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + // OK: removes Nontemporal, adds Volatile + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(148, SpvOpStore, 0), + SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "Point3D" + OpMemberName %7 0 "x" + OpMemberName %7 1 "y" + OpMemberName %7 2 "z" + OpName %12 "global_points" + OpName %15 "block" + OpMemberName %15 0 "in_points" + OpMemberName %15 1 "in_point" + OpName %17 "" + OpName %133 "local_points" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpMemberDecorate %7 2 Offset 8 + OpDecorate %10 ArrayStride 16 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 1 Offset 192 + OpDecorate %15 Block + OpDecorate %17 DescriptorSet 0 + OpDecorate %17 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 %6 %6 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 12 + %10 = OpTypeArray %7 %9 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private + %15 = OpTypeStruct %10 %7 + %16 = OpTypePointer Uniform %15 + %17 = OpVariable %16 Uniform + %18 = OpTypeInt 32 1 + %19 = OpConstant %18 0 + %20 = OpTypePointer Uniform %10 + %24 = OpTypePointer Private %7 + %27 = OpTypePointer Private %6 + %30 = OpConstant %18 1 + %132 = OpTypePointer Function %10 + %135 = OpTypePointer Uniform %7 + %145 = OpTypePointer Function %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %133 = OpVariable %132 Function + %21 = OpAccessChain %20 %17 %19 + OpCopyMemory %12 %21 Aligned|Nontemporal|Volatile 16 + OpCopyMemory %133 %12 Nontemporal|Volatile + OpCopyMemory %133 %12 Nontemporal|Volatile + %136 = OpAccessChain %135 %17 %30 + %138 = OpAccessChain %24 %12 %19 + OpCopyMemory %138 %136 Nontemporal|Volatile + %146 = OpAccessChain %145 %133 %30 + %147 = OpLoad %7 %146 Aligned|Volatile 16 + %148 = OpAccessChain %24 %12 %19 + OpStore %148 %147 Volatile + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationSetMemoryOperandsMaskTest, Spirv14) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 %17 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "Point3D" + OpMemberName %7 0 "x" + OpMemberName %7 1 "y" + OpMemberName %7 2 "z" + OpName %12 "global_points" + OpName %15 "block" + OpMemberName %15 0 "in_points" + OpMemberName %15 1 "in_point" + OpName %17 "" + OpName %133 "local_points" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpMemberDecorate %7 2 Offset 8 + OpDecorate %10 ArrayStride 16 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 1 Offset 192 + OpDecorate %15 Block + OpDecorate %17 DescriptorSet 0 + OpDecorate %17 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 %6 %6 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 12 + %10 = OpTypeArray %7 %9 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private + %15 = OpTypeStruct %10 %7 + %16 = OpTypePointer Uniform %15 + %17 = OpVariable %16 Uniform + %18 = OpTypeInt 32 1 + %19 = OpConstant %18 0 + %20 = OpTypePointer Uniform %10 + %24 = OpTypePointer Private %7 + %27 = OpTypePointer Private %6 + %30 = OpConstant %18 1 + %132 = OpTypePointer Function %10 + %135 = OpTypePointer Uniform %7 + %145 = OpTypePointer Function %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %133 = OpVariable %132 Function + %21 = OpAccessChain %20 %17 %19 + OpCopyMemory %12 %21 Aligned 16 Nontemporal|Aligned 16 + OpCopyMemory %133 %12 Volatile + OpCopyMemory %133 %12 + OpCopyMemory %133 %12 + %136 = OpAccessChain %135 %17 %30 + %138 = OpAccessChain %24 %12 %19 + OpCopyMemory %138 %136 None Aligned 16 + OpCopyMemory %138 %136 Aligned 16 + %146 = OpAccessChain %145 %133 %30 + %147 = OpLoad %7 %146 Volatile|Nontemporal|Aligned 16 + %148 = OpAccessChain %24 %12 %19 + OpStore %148 %147 Nontemporal + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessVolatileMask, 1); + // Bad: cannot remove aligned + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 0), + SpvMemoryAccessVolatileMask, 1) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1); + // Bad: cannot remove volatile + ASSERT_FALSE(TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 1), + SpvMemoryAccessNontemporalMask, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + // Creates the first operand. + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 2), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + // Creates both operands. + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(21, SpvOpCopyMemory, 3), + SpvMemoryAccessNontemporalMask | SpvMemoryAccessVolatileMask, 1); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 1); + // Bad: the first mask is None, so Aligned cannot be added to it. + ASSERT_FALSE( + TransformationSetMemoryOperandsMask( + MakeInstructionDescriptor(138, SpvOpCopyMemory, 0), + SpvMemoryAccessAlignedMask | SpvMemoryAccessNontemporalMask, 0) + .IsApplicable(context.get(), transformation_context)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(138, SpvOpCopyMemory, 1), + SpvMemoryAccessVolatileMask, 1); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(147, SpvOpLoad, 0), + SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + { + TransformationSetMemoryOperandsMask transformation( + MakeInstructionDescriptor(148, SpvOpStore, 0), SpvMemoryAccessMaskNone, + 0); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %12 %17 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %7 "Point3D" + OpMemberName %7 0 "x" + OpMemberName %7 1 "y" + OpMemberName %7 2 "z" + OpName %12 "global_points" + OpName %15 "block" + OpMemberName %15 0 "in_points" + OpMemberName %15 1 "in_point" + OpName %17 "" + OpName %133 "local_points" + OpMemberDecorate %7 0 Offset 0 + OpMemberDecorate %7 1 Offset 4 + OpMemberDecorate %7 2 Offset 8 + OpDecorate %10 ArrayStride 16 + OpMemberDecorate %15 0 Offset 0 + OpMemberDecorate %15 1 Offset 192 + OpDecorate %15 Block + OpDecorate %17 DescriptorSet 0 + OpDecorate %17 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeStruct %6 %6 %6 + %8 = OpTypeInt 32 0 + %9 = OpConstant %8 12 + %10 = OpTypeArray %7 %9 + %11 = OpTypePointer Private %10 + %12 = OpVariable %11 Private + %15 = OpTypeStruct %10 %7 + %16 = OpTypePointer Uniform %15 + %17 = OpVariable %16 Uniform + %18 = OpTypeInt 32 1 + %19 = OpConstant %18 0 + %20 = OpTypePointer Uniform %10 + %24 = OpTypePointer Private %7 + %27 = OpTypePointer Private %6 + %30 = OpConstant %18 1 + %132 = OpTypePointer Function %10 + %135 = OpTypePointer Uniform %7 + %145 = OpTypePointer Function %7 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %133 = OpVariable %132 Function + %21 = OpAccessChain %20 %17 %19 + OpCopyMemory %12 %21 Aligned 16 Aligned|Volatile 16 + OpCopyMemory %133 %12 Volatile Nontemporal|Volatile + OpCopyMemory %133 %12 Nontemporal|Volatile + OpCopyMemory %133 %12 None Nontemporal|Volatile + %136 = OpAccessChain %135 %17 %30 + %138 = OpAccessChain %24 %12 %19 + OpCopyMemory %138 %136 None Aligned|Nontemporal 16 + OpCopyMemory %138 %136 Aligned 16 Volatile + %146 = OpAccessChain %145 %133 %30 + %147 = OpLoad %7 %146 Volatile|Aligned 16 + %148 = OpAccessChain %24 %12 %19 + OpStore %148 %147 None + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp new file mode 100644 index 0000000..c584ff1 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_set_selection_control_test.cpp @@ -0,0 +1,231 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_set_selection_control.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSetSelectionControlTest, VariousScenarios) { + // This is a simple transformation; this test captures the important things + // to check for. + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 3 + %25 = OpConstant %6 1 + %28 = OpConstant %6 2 + %38 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpSGreaterThan %17 %19 %20 + OpSelectionMerge %23 Flatten + OpBranchConditional %21 %22 %23 + %22 = OpLabel + %24 = OpLoad %6 %8 + %26 = OpIAdd %6 %24 %25 + OpStore %8 %26 + OpBranch %23 + %23 = OpLabel + %27 = OpLoad %6 %8 + %29 = OpSLessThan %17 %27 %28 + OpSelectionMerge %31 DontFlatten + OpBranchConditional %29 %30 %31 + %30 = OpLabel + %32 = OpLoad %6 %8 + %33 = OpISub %6 %32 %25 + OpStore %8 %33 + OpBranch %31 + %31 = OpLabel + %34 = OpLoad %6 %8 + OpSelectionMerge %37 None + OpSwitch %34 %36 0 %35 + %36 = OpLabel + OpBranch %37 + %35 = OpLabel + %39 = OpLoad %6 %8 + %40 = OpIAdd %6 %39 %38 + OpStore %8 %40 + OpBranch %36 + %37 = OpLabel + OpBranch %13 + %13 = OpLabel + %43 = OpLoad %6 %8 + %44 = OpIAdd %6 %43 %25 + OpStore %8 %44 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // %44 is not a block + ASSERT_FALSE( + TransformationSetSelectionControl(44, SpvSelectionControlFlattenMask) + .IsApplicable(context.get(), transformation_context)); + // %13 does not end with OpSelectionMerge + ASSERT_FALSE( + TransformationSetSelectionControl(13, SpvSelectionControlMaskNone) + .IsApplicable(context.get(), transformation_context)); + // %10 ends in OpLoopMerge, not OpSelectionMerge + ASSERT_FALSE( + TransformationSetSelectionControl(10, SpvSelectionControlMaskNone) + .IsApplicable(context.get(), transformation_context)); + + TransformationSetSelectionControl transformation1( + 11, SpvSelectionControlDontFlattenMask); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + + TransformationSetSelectionControl transformation2( + 23, SpvSelectionControlFlattenMask); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + + TransformationSetSelectionControl transformation3( + 31, SpvSelectionControlMaskNone); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + + TransformationSetSelectionControl transformation4( + 31, SpvSelectionControlFlattenMask); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 10 + %17 = OpTypeBool + %20 = OpConstant %6 3 + %25 = OpConstant %6 1 + %28 = OpConstant %6 2 + %38 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %15 = OpLoad %6 %8 + %18 = OpSLessThan %17 %15 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %19 = OpLoad %6 %8 + %21 = OpSGreaterThan %17 %19 %20 + OpSelectionMerge %23 DontFlatten + OpBranchConditional %21 %22 %23 + %22 = OpLabel + %24 = OpLoad %6 %8 + %26 = OpIAdd %6 %24 %25 + OpStore %8 %26 + OpBranch %23 + %23 = OpLabel + %27 = OpLoad %6 %8 + %29 = OpSLessThan %17 %27 %28 + OpSelectionMerge %31 Flatten + OpBranchConditional %29 %30 %31 + %30 = OpLabel + %32 = OpLoad %6 %8 + %33 = OpISub %6 %32 %25 + OpStore %8 %33 + OpBranch %31 + %31 = OpLabel + %34 = OpLoad %6 %8 + OpSelectionMerge %37 Flatten + OpSwitch %34 %36 0 %35 + %36 = OpLabel + OpBranch %37 + %35 = OpLabel + %39 = OpLoad %6 %8 + %40 = OpIAdd %6 %39 %38 + OpStore %8 %40 + OpBranch %36 + %37 = OpLabel + OpBranch %13 + %13 = OpLabel + %43 = OpLoad %6 %8 + %44 = OpIAdd %6 %43 %25 + OpStore %8 %44 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_split_block_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_split_block_test.cpp new file mode 100644 index 0000000..55091de --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_split_block_test.cpp @@ -0,0 +1,921 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_split_block.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSplitBlockTest, NotApplicable) { + // The SPIR-V in this test came from the following fragment shader, with + // local store elimination applied to get some OpPhi instructions. + // + // void main() { + // int x; + // int i; + // for (i = 0; i < 100; i++) { + // x += i; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + OpName %19 "x" + OpDecorate %8 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %24 = OpConstant %6 1 + %28 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %27 = OpPhi %6 %28 %5 %22 %13 + %26 = OpPhi %6 %9 %5 %25 %13 + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %26 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %22 = OpIAdd %6 %27 %26 + OpStore %19 %22 + OpBranch %13 + %13 = OpLabel + %25 = OpIAdd %6 %26 %24 + OpStore %8 %25 + OpBranch %10 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // No split before OpVariable + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(8, SpvOpVariable, 0), 100) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(8, SpvOpVariable, 1), 100) + .IsApplicable(context.get(), transformation_context)); + + // No split before OpLabel + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(14, SpvOpLabel, 0), 100) + .IsApplicable(context.get(), transformation_context)); + + // No split if base instruction is outside a function + ASSERT_FALSE( + TransformationSplitBlock(MakeInstructionDescriptor(1, SpvOpLabel, 0), 100) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(1, SpvOpExecutionMode, 0), 100) + .IsApplicable(context.get(), transformation_context)); + + // No split if block is loop header + ASSERT_FALSE( + TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100) + .IsApplicable(context.get(), transformation_context)); + + // No split if base instruction does not exist + ASSERT_FALSE( + TransformationSplitBlock(MakeInstructionDescriptor(88, SpvOpIAdd, 0), 100) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(88, SpvOpIMul, 22), 100) + .IsApplicable(context.get(), transformation_context)); + + // No split if too many instructions with the desired opcode are skipped + ASSERT_FALSE( + TransformationSplitBlock( + MakeInstructionDescriptor(18, SpvOpBranchConditional, 1), 100) + .IsApplicable(context.get(), transformation_context)); + + // No split if id in use + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 27) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(18, SpvOpSLessThan, 0), 14) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationSplitBlockTest, SplitBlockSeveralTimes) { + // The SPIR-V in this test came from the following fragment shader: + // + // void main() { + // int a; + // int b; + // a = 1; + // b = a; + // a = b; + // b = 2; + // b++; + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpStore %10 %11 + %12 = OpLoad %6 %10 + OpStore %8 %12 + OpStore %10 %13 + %14 = OpLoad %6 %10 + %15 = OpIAdd %6 %14 %9 + OpStore %10 %15 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto split_1 = TransformationSplitBlock( + MakeInstructionDescriptor(5, SpvOpStore, 0), 100); + ASSERT_TRUE(split_1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(split_1, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_split_1 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpBranch %100 + %100 = OpLabel + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpStore %10 %11 + %12 = OpLoad %6 %10 + OpStore %8 %12 + OpStore %10 %13 + %14 = OpLoad %6 %10 + %15 = OpIAdd %6 %14 %9 + OpStore %10 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_split_1, context.get())); + + auto split_2 = TransformationSplitBlock( + MakeInstructionDescriptor(11, SpvOpStore, 0), 101); + ASSERT_TRUE(split_2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(split_2, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_split_2 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpBranch %100 + %100 = OpLabel + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpBranch %101 + %101 = OpLabel + OpStore %10 %11 + %12 = OpLoad %6 %10 + OpStore %8 %12 + OpStore %10 %13 + %14 = OpLoad %6 %10 + %15 = OpIAdd %6 %14 %9 + OpStore %10 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_split_2, context.get())); + + auto split_3 = TransformationSplitBlock( + MakeInstructionDescriptor(14, SpvOpLoad, 0), 102); + ASSERT_TRUE(split_3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(split_3, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_split_3 = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "a" + OpName %10 "b" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + OpDecorate %14 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %13 = OpConstant %6 2 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpBranch %100 + %100 = OpLabel + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpBranch %101 + %101 = OpLabel + OpStore %10 %11 + %12 = OpLoad %6 %10 + OpStore %8 %12 + OpStore %10 %13 + OpBranch %102 + %102 = OpLabel + %14 = OpLoad %6 %10 + %15 = OpIAdd %6 %14 %9 + OpStore %10 %15 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_split_3, context.get())); +} + +TEST(TransformationSplitBlockTest, SplitBlockBeforeSelectBranch) { + // The SPIR-V in this test came from the following fragment shader: + // + // void main() { + // int x, y; + // x = 2; + // if (x < y) { + // y = 3; + // } else { + // y = 4; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %11 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %13 = OpTypeBool + %17 = OpConstant %6 3 + %19 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %11 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLoad %6 %11 + %14 = OpSLessThan %13 %10 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %18 + %15 = OpLabel + OpStore %11 %17 + OpBranch %16 + %18 = OpLabel + OpStore %11 %19 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Illegal to split between the merge and the conditional branch. + ASSERT_FALSE( + TransformationSplitBlock( + MakeInstructionDescriptor(14, SpvOpBranchConditional, 0), 100) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSplitBlock( + MakeInstructionDescriptor(12, SpvOpBranchConditional, 0), 100) + .IsApplicable(context.get(), transformation_context)); + + auto split = TransformationSplitBlock( + MakeInstructionDescriptor(14, SpvOpSelectionMerge, 0), 100); + ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(split, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_split = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %11 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + OpDecorate %12 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 2 + %13 = OpTypeBool + %17 = OpConstant %6 3 + %19 = OpConstant %6 4 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %11 = OpVariable %7 Function + OpStore %8 %9 + %10 = OpLoad %6 %8 + %12 = OpLoad %6 %11 + %14 = OpSLessThan %13 %10 %12 + OpBranch %100 + %100 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %18 + %15 = OpLabel + OpStore %11 %17 + OpBranch %16 + %18 = OpLabel + OpStore %11 %19 + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_split, context.get())); +} + +TEST(TransformationSplitBlockTest, SplitBlockBeforeSwitchBranch) { + // The SPIR-V in this test came from the following fragment shader: + // + // void main() { + // int x, y; + // switch (y) { + // case 1: + // x = 2; + // case 2: + // break; + // case 3: + // x = 4; + // default: + // x = 6; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "y" + OpName %15 "x" + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %16 = OpConstant %6 2 + %18 = OpConstant %6 4 + %19 = OpConstant %6 6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %15 = OpVariable %7 Function + %9 = OpLoad %6 %8 + OpSelectionMerge %14 None + OpSwitch %9 %13 1 %10 2 %11 3 %12 + %13 = OpLabel + OpStore %15 %19 + OpBranch %14 + %10 = OpLabel + OpStore %15 %16 + OpBranch %11 + %11 = OpLabel + OpBranch %14 + %12 = OpLabel + OpStore %15 %18 + OpBranch %13 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Illegal to split between the merge and the conditional branch. + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(9, SpvOpSwitch, 0), 100) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationSplitBlock( + MakeInstructionDescriptor(15, SpvOpSwitch, 0), 100) + .IsApplicable(context.get(), transformation_context)); + + auto split = TransformationSplitBlock( + MakeInstructionDescriptor(9, SpvOpSelectionMerge, 0), 100); + ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(split, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_split = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "y" + OpName %15 "x" + OpDecorate %8 RelaxedPrecision + OpDecorate %9 RelaxedPrecision + OpDecorate %15 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %16 = OpConstant %6 2 + %18 = OpConstant %6 4 + %19 = OpConstant %6 6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %15 = OpVariable %7 Function + %9 = OpLoad %6 %8 + OpBranch %100 + %100 = OpLabel + OpSelectionMerge %14 None + OpSwitch %9 %13 1 %10 2 %11 3 %12 + %13 = OpLabel + OpStore %15 %19 + OpBranch %14 + %10 = OpLabel + OpStore %15 %16 + OpBranch %11 + %11 = OpLabel + OpBranch %14 + %12 = OpLabel + OpStore %15 %18 + OpBranch %13 + %14 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_split, context.get())); +} + +TEST(TransformationSplitBlockTest, NoSplitDuringOpPhis) { + // The SPIR-V in this test came from the following fragment shader, with + // local store elimination applied to get some OpPhi instructions. + // + // void main() { + // int x; + // int i; + // for (i = 0; i < 100; i++) { + // x += i; + // } + // } + + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "i" + OpName %19 "x" + OpDecorate %8 RelaxedPrecision + OpDecorate %19 RelaxedPrecision + OpDecorate %22 RelaxedPrecision + OpDecorate %25 RelaxedPrecision + OpDecorate %26 RelaxedPrecision + OpDecorate %27 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %16 = OpConstant %6 100 + %17 = OpTypeBool + %24 = OpConstant %6 1 + %28 = OpUndef %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %19 = OpVariable %7 Function + OpStore %8 %9 + OpBranch %10 + %10 = OpLabel + %27 = OpPhi %6 %28 %5 %22 %13 + %26 = OpPhi %6 %9 %5 %25 %13 + OpBranch %50 + %50 = OpLabel + OpLoopMerge %12 %13 None + OpBranch %14 + %14 = OpLabel + %18 = OpSLessThan %17 %26 %16 + OpBranchConditional %18 %11 %12 + %11 = OpLabel + %22 = OpIAdd %6 %27 %26 + OpStore %19 %22 + OpBranch %13 + %13 = OpLabel + %25 = OpIAdd %6 %26 %24 + OpStore %8 %25 + OpBranch %50 + %12 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // We cannot split before OpPhi instructions, since the number of incoming + // blocks may not appropriately match after splitting. + ASSERT_FALSE( + TransformationSplitBlock(MakeInstructionDescriptor(26, SpvOpPhi, 0), 100) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 0), 100) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationSplitBlock(MakeInstructionDescriptor(27, SpvOpPhi, 1), 100) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationSplitBlockTest, SplitOpPhiWithSinglePredecessor) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpBranch %20 + %20 = OpLabel + %21 = OpPhi %6 %11 %5 + OpStore %10 %21 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + ASSERT_TRUE( + TransformationSplitBlock(MakeInstructionDescriptor(21, SpvOpPhi, 0), 100) + .IsApplicable(context.get(), transformation_context)); + // An equivalent transformation to the above, just described with respect to a + // different base instruction. + auto split = + TransformationSplitBlock(MakeInstructionDescriptor(20, SpvOpPhi, 0), 100); + ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(split, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_split = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %8 "x" + OpName %10 "y" + OpDecorate %8 RelaxedPrecision + OpDecorate %10 RelaxedPrecision + OpDecorate %11 RelaxedPrecision + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 1 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + %11 = OpLoad %6 %8 + OpBranch %20 + %20 = OpLabel + OpBranch %100 + %100 = OpLabel + %21 = OpPhi %6 %11 %20 + OpStore %10 %21 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_split, context.get())); +} + +TEST(TransformationSplitBlockTest, DeadBlockShouldSplitToTwoDeadBlocks) { + // This checks that if a block B is marked as dead, it should split into a + // pair of dead blocks. + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Record the fact that block 8 is dead. + transformation_context.GetFactManager()->AddFactBlockIsDead(8); + + auto split = TransformationSplitBlock( + MakeInstructionDescriptor(8, SpvOpBranch, 0), 100); + ASSERT_TRUE(split.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(split, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(8)); + ASSERT_TRUE(transformation_context.GetFactManager()->BlockIsDead(100)); + + std::string after_split = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpConstantFalse %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %9 None + OpBranchConditional %7 %8 %9 + %8 = OpLabel + OpBranch %100 + %100 = OpLabel + OpBranch %9 + %9 = OpLabel + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_split, context.get())); +} + +TEST(TransformationSplitBlockTest, DoNotSplitUseOfOpSampledImage) { + // This checks that we cannot split the definition of an OpSampledImage + // from its use. + std::string shader = R"( + OpCapability Shader + OpCapability SampledBuffer + OpCapability ImageBuffer + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" %40 %41 + OpExecutionMode %2 OriginUpperLeft + OpSource GLSL 450 + OpDecorate %40 DescriptorSet 0 + OpDecorate %40 Binding 69 + OpDecorate %41 DescriptorSet 0 + OpDecorate %41 Binding 1 + %54 = OpTypeFloat 32 + %76 = OpTypeVector %54 4 + %55 = OpConstant %54 0 + %56 = OpTypeVector %54 3 + %94 = OpTypeVector %54 2 + %112 = OpConstantComposite %94 %55 %55 + %57 = OpConstantComposite %56 %55 %55 %55 + %15 = OpTypeImage %54 2D 2 0 0 1 Unknown + %114 = OpTypePointer UniformConstant %15 + %38 = OpTypeSampler + %125 = OpTypePointer UniformConstant %38 + %132 = OpTypeVoid + %133 = OpTypeFunction %132 + %45 = OpTypeSampledImage %15 + %40 = OpVariable %114 UniformConstant + %41 = OpVariable %125 UniformConstant + %2 = OpFunction %132 None %133 + %164 = OpLabel + %184 = OpLoad %15 %40 + %213 = OpLoad %38 %41 + %216 = OpSampledImage %45 %184 %213 + %217 = OpImageSampleImplicitLod %76 %216 %112 Bias %55 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + + spvtools::ValidatorOptions validator_options; + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto split = TransformationSplitBlock( + MakeInstructionDescriptor(217, SpvOpImageSampleImplicitLod, 0), 500); + ASSERT_FALSE(split.IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_store_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_store_test.cpp new file mode 100644 index 0000000..93257d0 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_store_test.cpp @@ -0,0 +1,429 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_store.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationStoreTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function ; irrelevant + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 ; irrelevant + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 ; irrelevant + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 ; irrelevant + %13 = OpLabel + %46 = OpCopyObject %9 %11 ; irrelevant + %16 = OpAccessChain %15 %11 %14 ; irrelevant + %95 = OpCopyObject %8 %80 + OpReturnValue %21 + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 27); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 11); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 46); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 16); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 52); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 81); + transformation_context.GetFactManager()->AddFactValueOfPointeeIsIrrelevant( + 82); + + transformation_context.GetFactManager()->AddFactBlockIsDead(36); + + // Variables with pointee types: + // 52 - ptr_to(7) + // 53 - ptr_to(6) + // 20 - ptr_to(8) + // 27 - ptr_to(8) - irrelevant + // 92 - ptr_to(90) - read only + + // Access chains with pointee type: + // 22 - ptr_to(6) + // 26 - ptr_to(6) + // 30 - ptr_to(6) + // 33 - ptr_to(6) + // 38 - ptr_to(6) + // 40 - ptr_to(6) + // 43 - ptr_to(6) + // 16 - ptr_to(6) - irrelevant + + // Copied object with pointee type: + // 44 - ptr_to(8) + // 45 - ptr_to(6) + // 46 - ptr_to(8) - irrelevant + // 81 - ptr_to(8) - irrelevant + // 82 - ptr_to(8) - irrelevant + + // Function parameters with pointee type: + // 11 - ptr_to(8) - irrelevant + + // Pointers that cannot be used: + // 60 - null + // 61 - undefined + + // Bad: attempt to store to 11 from outside its function + ASSERT_FALSE(TransformationStore( + 11, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer is not available + ASSERT_FALSE(TransformationStore( + 81, 80, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to insert before OpVariable + ASSERT_FALSE(TransformationStore( + 52, 24, MakeInstructionDescriptor(27, SpvOpVariable, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id does not exist + ASSERT_FALSE(TransformationStore( + 1000, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id exists but does not have a type + ASSERT_FALSE(TransformationStore( + 5, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: pointer id exists and has a type, but is not a pointer + ASSERT_FALSE(TransformationStore( + 24, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to store to a null pointer + ASSERT_FALSE(TransformationStore( + 60, 24, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to store to an undefined pointer + ASSERT_FALSE(TransformationStore( + 61, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: %82 is not available at the program point + ASSERT_FALSE( + TransformationStore(82, 80, MakeInstructionDescriptor(37, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: value id does not exist + ASSERT_FALSE(TransformationStore( + 27, 1000, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: value id exists but does not have a type + ASSERT_FALSE(TransformationStore( + 27, 15, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: value id exists but has the wrong type + ASSERT_FALSE(TransformationStore( + 27, 14, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: attempt to store to read-only variable + ASSERT_FALSE(TransformationStore( + 92, 93, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: value is not available + ASSERT_FALSE(TransformationStore( + 27, 95, MakeInstructionDescriptor(40, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + // Bad: variable being stored to does not have an irrelevant pointee value, + // and the store is not in a dead block. + ASSERT_FALSE(TransformationStore( + 20, 95, MakeInstructionDescriptor(45, SpvOpCopyObject, 0)) + .IsApplicable(context.get(), transformation_context)); + + // The described instruction does not exist. + ASSERT_FALSE(TransformationStore( + 27, 80, MakeInstructionDescriptor(1000, SpvOpAccessChain, 0)) + .IsApplicable(context.get(), transformation_context)); + + { + // Store to irrelevant variable from dead block. + TransformationStore transformation( + 27, 80, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + // Store to irrelevant variable from live block. + TransformationStore transformation( + 11, 95, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + // Store to irrelevant variable from live block. + TransformationStore transformation( + 46, 80, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + // Store to irrelevant variable from live block. + TransformationStore transformation( + 16, 21, MakeInstructionDescriptor(95, SpvOpReturnValue, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + { + // Store to non-irrelevant variable from dead block. + TransformationStore transformation( + 53, 21, MakeInstructionDescriptor(38, SpvOpAccessChain, 0)); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %92 %52 %53 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpDecorate %92 BuiltIn FragCoord + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeFloat 32 + %8 = OpTypeStruct %6 %7 + %9 = OpTypePointer Function %8 + %10 = OpTypeFunction %6 %9 + %14 = OpConstant %6 0 + %15 = OpTypePointer Function %6 + %51 = OpTypePointer Private %6 + %21 = OpConstant %6 2 + %23 = OpConstant %6 1 + %24 = OpConstant %7 1 + %25 = OpTypePointer Function %7 + %50 = OpTypePointer Private %7 + %34 = OpTypeBool + %35 = OpConstantFalse %34 + %60 = OpConstantNull %50 + %61 = OpUndef %51 + %52 = OpVariable %50 Private + %53 = OpVariable %51 Private + %80 = OpConstantComposite %8 %21 %24 + %90 = OpTypeVector %7 4 + %91 = OpTypePointer Input %90 + %92 = OpVariable %91 Input + %93 = OpConstantComposite %90 %24 %24 %24 %24 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %20 = OpVariable %9 Function + %27 = OpVariable %9 Function ; irrelevant + %22 = OpAccessChain %15 %20 %14 + %44 = OpCopyObject %9 %20 + %26 = OpAccessChain %25 %20 %23 + %29 = OpFunctionCall %6 %12 %27 + %30 = OpAccessChain %15 %20 %14 + %45 = OpCopyObject %15 %30 + %81 = OpCopyObject %9 %27 ; irrelevant + %33 = OpAccessChain %15 %20 %14 + OpSelectionMerge %37 None + OpBranchConditional %35 %36 %37 + %36 = OpLabel + OpStore %27 %80 + OpStore %53 %21 + %38 = OpAccessChain %15 %20 %14 + %40 = OpAccessChain %15 %20 %14 + %43 = OpAccessChain %15 %20 %14 + %82 = OpCopyObject %9 %27 ; irrelevant + OpBranch %37 + %37 = OpLabel + OpReturn + OpFunctionEnd + %12 = OpFunction %6 None %10 + %11 = OpFunctionParameter %9 ; irrelevant + %13 = OpLabel + %46 = OpCopyObject %9 %11 ; irrelevant + %16 = OpAccessChain %15 %11 %14 ; irrelevant + %95 = OpCopyObject %8 %80 + OpStore %11 %95 + OpStore %46 %80 + OpStore %16 %21 + OpReturnValue %21 + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationStoreTest, DoNotAllowStoresToReadOnlyMemory) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpMemberDecorate %10 0 Offset 0 + OpMemberDecorate %10 1 Offset 4 + OpDecorate %10 Block + OpMemberDecorate %23 0 Offset 0 + OpDecorate %23 Block + OpDecorate %25 DescriptorSet 0 + OpDecorate %25 Binding 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpTypeFloat 32 + %10 = OpTypeStruct %6 %9 + %11 = OpTypePointer PushConstant %10 + %12 = OpVariable %11 PushConstant + %13 = OpConstant %6 0 + %14 = OpTypePointer PushConstant %6 + %17 = OpConstant %6 1 + %18 = OpTypePointer PushConstant %9 + %23 = OpTypeStruct %9 + %24 = OpTypePointer UniformConstant %23 + %25 = OpVariable %24 UniformConstant + %26 = OpTypePointer UniformConstant %9 + %50 = OpConstant %9 0 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %15 = OpAccessChain %14 %12 %13 + %19 = OpAccessChain %18 %12 %17 + %27 = OpAccessChain %26 %25 %13 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactBlockIsDead(5); + + ASSERT_FALSE( + TransformationStore(15, 13, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(19, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationStore(27, 50, MakeInstructionDescriptor(27, SpvOpReturn, 0)) + .IsApplicable(context.get(), transformation_context)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_swap_commutable_operands_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_swap_commutable_operands_test.cpp new file mode 100644 index 0000000..0731529 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_swap_commutable_operands_test.cpp @@ -0,0 +1,456 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_commutable_operands.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSwapCommutableOperandsTest, IsApplicableTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests existing commutative instructions + auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0); + auto transformation = + TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests existing non-commutative instructions + instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(8, SpvOpConstant, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(11, SpvOpVariable, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = + MakeInstructionDescriptor(14, SpvOpConstantComposite, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests the base instruction id not existing + instructionDescriptor = MakeInstructionDescriptor(67, SpvOpIAddCarry, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(68, SpvOpIEqual, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(69, SpvOpINotEqual, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(70, SpvOpFOrdEqual, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(71, SpvOpPtrEqual, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests there being no instruction with the desired opcode after the base + // instruction id + instructionDescriptor = MakeInstructionDescriptor(24, SpvOpIAdd, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(38, SpvOpIMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(45, SpvOpFAdd, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(66, SpvOpFMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests there being an instruction with the desired opcode after the base + // instruction id, but the skip count associated with the instruction + // descriptor being so high. + instructionDescriptor = MakeInstructionDescriptor(11, SpvOpIAdd, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(16, SpvOpIMul, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(23, SpvOpFAdd, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(32, SpvOpFMul, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(37, SpvOpDot, 100); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationSwapCommutableOperandsTest, ApplyTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instructionDescriptor = MakeInstructionDescriptor(22, SpvOpIAdd, 0); + auto transformation = + TransformationSwapCommutableOperands(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instructionDescriptor = MakeInstructionDescriptor(28, SpvOpIMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instructionDescriptor = MakeInstructionDescriptor(42, SpvOpFAdd, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instructionDescriptor = MakeInstructionDescriptor(48, SpvOpFMul, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instructionDescriptor = MakeInstructionDescriptor(66, SpvOpDot, 0); + transformation = TransformationSwapCommutableOperands(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variantShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %21 %19 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %27 %25 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %41 %39 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %47 %45 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %65 %63 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, variantShader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp new file mode 100644 index 0000000..e7a8732 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_swap_conditional_branch_operands_test.cpp @@ -0,0 +1,146 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_swap_conditional_branch_operands.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationSwapConditionalBranchOperandsTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstant %6 1 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %12 = OpLoad %6 %8 + %13 = OpLoad %6 %10 + %15 = OpSLessThan %14 %12 %13 + OpSelectionMerge %17 None + OpBranchConditional %15 %16 %21 10 20 + %16 = OpLabel + %18 = OpLoad %6 %10 + %19 = OpLoad %6 %8 + %20 = OpIAdd %6 %19 %18 + OpBranch %17 + %21 = OpLabel + %22 = OpLoad %6 %10 + %23 = OpLoad %6 %8 + %24 = OpISub %6 %23 %22 + OpBranch %17 + %17 = OpLabel + %25 = OpPhi %6 %20 %16 %24 %21 + OpStore %8 %25 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Invalid instruction descriptor. + ASSERT_FALSE(TransformationSwapConditionalBranchOperands( + MakeInstructionDescriptor(26, SpvOpPhi, 0), 26) + .IsApplicable(context.get(), transformation_context)); + + // Descriptor for a wrong instruction. + ASSERT_FALSE(TransformationSwapConditionalBranchOperands( + MakeInstructionDescriptor(25, SpvOpPhi, 0), 26) + .IsApplicable(context.get(), transformation_context)); + + // Fresh id is not fresh. + ASSERT_FALSE(TransformationSwapConditionalBranchOperands( + MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 25) + .IsApplicable(context.get(), transformation_context)); + + TransformationSwapConditionalBranchOperands transformation( + MakeInstructionDescriptor(15, SpvOpBranchConditional, 0), 26); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypePointer Function %6 + %9 = OpConstant %6 0 + %11 = OpConstant %6 1 + %14 = OpTypeBool + %4 = OpFunction %2 None %3 + %5 = OpLabel + %8 = OpVariable %7 Function + %10 = OpVariable %7 Function + OpStore %8 %9 + OpStore %10 %11 + %12 = OpLoad %6 %8 + %13 = OpLoad %6 %10 + %15 = OpSLessThan %14 %12 %13 + %26 = OpLogicalNot %14 %15 + OpSelectionMerge %17 None + OpBranchConditional %26 %21 %16 20 10 + %16 = OpLabel + %18 = OpLoad %6 %10 + %19 = OpLoad %6 %8 + %20 = OpIAdd %6 %19 %18 + OpBranch %17 + %21 = OpLabel + %22 = OpLoad %6 %10 + %23 = OpLoad %6 %8 + %24 = OpISub %6 %23 %22 + OpBranch %17 + %17 = OpLabel + %25 = OpPhi %6 %20 %16 %24 %21 + OpStore %8 %25 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp new file mode 100644 index 0000000..84ed20d --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_toggle_access_chain_instruction_test.cpp @@ -0,0 +1,432 @@ +// Copyright (c) 2020 André Perez Maselco +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_toggle_access_chain_instruction.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationToggleAccessChainInstructionTest, IsApplicableTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpInBoundsAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpInBoundsAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Tests existing access chain instructions + auto instructionDescriptor = + MakeInstructionDescriptor(18, SpvOpAccessChain, 0); + auto transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = + MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = + MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests existing non-access chain instructions + instructionDescriptor = MakeInstructionDescriptor(1, SpvOpExtInstImport, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(5, SpvOpLabel, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = + MakeInstructionDescriptor(14, SpvOpConstantComposite, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests the base instruction id not existing + instructionDescriptor = MakeInstructionDescriptor(67, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = MakeInstructionDescriptor(68, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = + MakeInstructionDescriptor(69, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests there being no instruction with the desired opcode after the base + // instruction id + instructionDescriptor = MakeInstructionDescriptor(65, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = + MakeInstructionDescriptor(66, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + // Tests there being an instruction with the desired opcode after the base + // instruction id, but the skip count associated with the instruction + // descriptor being so high. + instructionDescriptor = MakeInstructionDescriptor(11, SpvOpAccessChain, 100); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); + + instructionDescriptor = + MakeInstructionDescriptor(16, SpvOpInBoundsAccessChain, 100); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ASSERT_FALSE( + transformation.IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationToggleAccessChainInstructionTest, ApplyTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpInBoundsAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpInBoundsAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_5; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + auto instructionDescriptor = + MakeInstructionDescriptor(18, SpvOpAccessChain, 0); + auto transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instructionDescriptor = + MakeInstructionDescriptor(20, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instructionDescriptor = MakeInstructionDescriptor(24, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instructionDescriptor = + MakeInstructionDescriptor(26, SpvOpInBoundsAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + instructionDescriptor = MakeInstructionDescriptor(38, SpvOpAccessChain, 0); + transformation = + TransformationToggleAccessChainInstruction(instructionDescriptor); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + + std::string variantShader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeInt 32 0 + %8 = OpConstant %7 2 + %9 = OpTypeArray %6 %8 + %10 = OpTypePointer Function %9 + %12 = OpConstant %6 1 + %13 = OpConstant %6 2 + %14 = OpConstantComposite %9 %12 %13 + %15 = OpTypePointer Function %6 + %17 = OpConstant %6 0 + %29 = OpTypeFloat 32 + %30 = OpTypeArray %29 %8 + %31 = OpTypePointer Function %30 + %33 = OpConstant %29 1 + %34 = OpConstant %29 2 + %35 = OpConstantComposite %30 %33 %34 + %36 = OpTypePointer Function %29 + %49 = OpTypeVector %29 3 + %50 = OpTypeArray %49 %8 + %51 = OpTypePointer Function %50 + %53 = OpConstant %29 3 + %54 = OpConstantComposite %49 %33 %34 %53 + %55 = OpConstant %29 4 + %56 = OpConstant %29 5 + %57 = OpConstant %29 6 + %58 = OpConstantComposite %49 %55 %56 %57 + %59 = OpConstantComposite %50 %54 %58 + %61 = OpTypePointer Function %49 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %11 = OpVariable %10 Function + %16 = OpVariable %15 Function + %23 = OpVariable %15 Function + %32 = OpVariable %31 Function + %37 = OpVariable %36 Function + %43 = OpVariable %36 Function + %52 = OpVariable %51 Function + %60 = OpVariable %36 Function + OpStore %11 %14 + %18 = OpInBoundsAccessChain %15 %11 %17 + %19 = OpLoad %6 %18 + %20 = OpAccessChain %15 %11 %12 + %21 = OpLoad %6 %20 + %22 = OpIAdd %6 %19 %21 + OpStore %16 %22 + %24 = OpInBoundsAccessChain %15 %11 %17 + %25 = OpLoad %6 %24 + %26 = OpAccessChain %15 %11 %12 + %27 = OpLoad %6 %26 + %28 = OpIMul %6 %25 %27 + OpStore %23 %28 + OpStore %32 %35 + %38 = OpInBoundsAccessChain %36 %32 %17 + %39 = OpLoad %29 %38 + %40 = OpAccessChain %36 %32 %12 + %41 = OpLoad %29 %40 + %42 = OpFAdd %29 %39 %41 + OpStore %37 %42 + %44 = OpAccessChain %36 %32 %17 + %45 = OpLoad %29 %44 + %46 = OpAccessChain %36 %32 %12 + %47 = OpLoad %29 %46 + %48 = OpFMul %29 %45 %47 + OpStore %43 %48 + OpStore %52 %59 + %62 = OpAccessChain %61 %52 %17 + %63 = OpLoad %49 %62 + %64 = OpAccessChain %61 %52 %12 + %65 = OpLoad %49 %64 + %66 = OpDot %29 %63 %65 + OpStore %60 %66 + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, variantShader, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_vector_shuffle_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_vector_shuffle_test.cpp new file mode 100644 index 0000000..e3dc0a7 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_vector_shuffle_test.cpp @@ -0,0 +1,806 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_vector_shuffle.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationVectorShuffleTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypeVector %6 2 + %10 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %12 = OpConstantComposite %7 %10 %11 + %112 = OpUndef %7 + %13 = OpTypeVector %6 3 + %16 = OpConstantComposite %13 %10 %11 %10 + %17 = OpTypeVector %6 4 + %20 = OpConstantComposite %17 %10 %11 %10 %11 + %21 = OpTypeInt 32 1 + %22 = OpTypeVector %21 2 + %25 = OpConstant %21 1 + %26 = OpConstant %21 0 + %27 = OpConstantComposite %22 %25 %26 + %28 = OpTypeVector %21 3 + %31 = OpConstantComposite %28 %25 %26 %25 + %32 = OpTypeVector %21 4 + %33 = OpTypePointer Function %32 + %35 = OpConstantComposite %32 %25 %26 %25 %26 + %36 = OpTypeInt 32 0 + %37 = OpTypeVector %36 2 + %40 = OpConstant %36 1 + %41 = OpConstant %36 0 + %42 = OpConstantComposite %37 %40 %41 + %43 = OpTypeVector %36 3 + %46 = OpConstantComposite %43 %40 %41 %40 + %47 = OpTypeVector %36 4 + %50 = OpConstantComposite %47 %40 %41 %40 %41 + %51 = OpTypeFloat 32 + %55 = OpConstant %51 1 + %56 = OpConstant %51 0 + %58 = OpTypeVector %51 3 + %61 = OpConstantComposite %58 %55 %56 %55 + %62 = OpTypeVector %51 4 + %65 = OpConstantComposite %62 %55 %56 %55 %56 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %100 None + OpBranchConditional %10 %101 %102 + %101 = OpLabel + %103 = OpCompositeConstruct %62 %55 %55 %55 %56 + OpBranch %100 + %102 = OpLabel + OpBranch %100 + %100 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(12, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(12, {1})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(16, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(16, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(16, {2})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(20, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(20, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(10, {}), MakeDataDescriptor(20, {2})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(11, {}), MakeDataDescriptor(20, {3})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(27, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(26, {}), MakeDataDescriptor(27, {1})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(31, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(26, {}), MakeDataDescriptor(31, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(31, {2})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(35, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(26, {}), MakeDataDescriptor(35, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(25, {}), MakeDataDescriptor(35, {2})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(26, {}), MakeDataDescriptor(35, {3})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(42, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(41, {}), MakeDataDescriptor(42, {1})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(46, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(41, {}), MakeDataDescriptor(46, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(46, {2})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(50, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(41, {}), MakeDataDescriptor(50, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(40, {}), MakeDataDescriptor(50, {2})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(41, {}), MakeDataDescriptor(50, {3})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(55, {}), MakeDataDescriptor(61, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(56, {}), MakeDataDescriptor(61, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(55, {}), MakeDataDescriptor(61, {2})); + + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(55, {}), MakeDataDescriptor(65, {0})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(56, {}), MakeDataDescriptor(65, {1})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(55, {}), MakeDataDescriptor(65, {2})); + transformation_context.GetFactManager()->AddFactDataSynonym( + MakeDataDescriptor(56, {}), MakeDataDescriptor(65, {3})); + + // %103 does not dominate the return instruction. + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 103, 65, + {3, 5, 7}) + .IsApplicable(context.get(), transformation_context)); + + // Illegal to shuffle a bvec2 and a vec3 + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 112, 61, + {0, 2, 4}) + .IsApplicable(context.get(), transformation_context)); + + // Illegal to shuffle an ivec2 and a uvec4 + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 50, + {1, 3, 5}) + .IsApplicable(context.get(), transformation_context)); + + // Vector 1 does not exist + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 300, 50, + {1, 3, 5}) + .IsApplicable(context.get(), transformation_context)); + + // Vector 2 does not exist + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 27, 300, + {1, 3, 5}) + .IsApplicable(context.get(), transformation_context)); + + // Index out of range + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0, 20}) + .IsApplicable(context.get(), transformation_context)); + + // Too many indices + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, + {0, 1, 0, 1, 0, 1, 0, 1}) + .IsApplicable(context.get(), transformation_context)); + + // Too few indices + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {}) + .IsApplicable(context.get(), transformation_context)); + + // Too few indices again + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {0}) + .IsApplicable(context.get(), transformation_context)); + + // Indices define unknown type: we do not have vec2 + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 65, 65, {0, 1}) + .IsApplicable(context.get(), transformation_context)); + + // The instruction to insert before does not exist + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpCompositeConstruct, 1), + 201, 20, 12, {0xFFFFFFFF, 3, 5}) + .IsApplicable(context.get(), transformation_context)); + + // The 'fresh' id is already in use + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 12, 12, 112, {}) + .IsApplicable(context.get(), transformation_context)); + + protobufs::DataDescriptor temp_dd; + + TransformationVectorShuffle transformation1( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {1, 0}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + temp_dd = MakeDataDescriptor(200, {0}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), temp_dd)); + temp_dd = MakeDataDescriptor(200, {1}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(10, {}), temp_dd)); + + TransformationVectorShuffle transformation2( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 201, 20, 12, + {0xFFFFFFFF, 3, 5}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + temp_dd = MakeDataDescriptor(201, {1}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), temp_dd)); + temp_dd = MakeDataDescriptor(201, {2}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(11, {}), temp_dd)); + + TransformationVectorShuffle transformation3( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 202, 27, 35, {5, 4, 1}); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + temp_dd = MakeDataDescriptor(202, {0}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(26, {}), temp_dd)); + temp_dd = MakeDataDescriptor(202, {1}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(25, {}), temp_dd)); + temp_dd = MakeDataDescriptor(202, {2}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(26, {}), temp_dd)); + + TransformationVectorShuffle transformation4( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 203, 42, 46, {0, 1}); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + temp_dd = MakeDataDescriptor(203, {0}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd)); + temp_dd = MakeDataDescriptor(203, {1}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), temp_dd)); + + TransformationVectorShuffle transformation5( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 204, 42, 46, {2, 3, 4}); + ASSERT_TRUE( + transformation5.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation5, context.get(), + &transformation_context); + temp_dd = MakeDataDescriptor(204, {0}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd)); + temp_dd = MakeDataDescriptor(204, {1}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), temp_dd)); + temp_dd = MakeDataDescriptor(204, {2}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd)); + + TransformationVectorShuffle transformation6( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 205, 42, 42, + {0, 1, 2, 3}); + ASSERT_TRUE( + transformation6.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation6, context.get(), + &transformation_context); + temp_dd = MakeDataDescriptor(205, {0}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd)); + temp_dd = MakeDataDescriptor(205, {1}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), temp_dd)); + temp_dd = MakeDataDescriptor(205, {2}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(40, {}), temp_dd)); + temp_dd = MakeDataDescriptor(205, {3}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(41, {}), temp_dd)); + + // swizzle vec4 from vec4 and vec4 using some undefs + TransformationVectorShuffle transformation7( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 206, 65, 65, + {0xFFFFFFFF, 3, 6, 0xFFFFFFFF}); + ASSERT_TRUE( + transformation7.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation7, context.get(), + &transformation_context); + temp_dd = MakeDataDescriptor(206, {1}); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(56, {}), temp_dd)); + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypeVector %6 2 + %10 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %12 = OpConstantComposite %7 %10 %11 + %112 = OpUndef %7 + %13 = OpTypeVector %6 3 + %16 = OpConstantComposite %13 %10 %11 %10 + %17 = OpTypeVector %6 4 + %20 = OpConstantComposite %17 %10 %11 %10 %11 + %21 = OpTypeInt 32 1 + %22 = OpTypeVector %21 2 + %25 = OpConstant %21 1 + %26 = OpConstant %21 0 + %27 = OpConstantComposite %22 %25 %26 + %28 = OpTypeVector %21 3 + %31 = OpConstantComposite %28 %25 %26 %25 + %32 = OpTypeVector %21 4 + %33 = OpTypePointer Function %32 + %35 = OpConstantComposite %32 %25 %26 %25 %26 + %36 = OpTypeInt 32 0 + %37 = OpTypeVector %36 2 + %40 = OpConstant %36 1 + %41 = OpConstant %36 0 + %42 = OpConstantComposite %37 %40 %41 + %43 = OpTypeVector %36 3 + %46 = OpConstantComposite %43 %40 %41 %40 + %47 = OpTypeVector %36 4 + %50 = OpConstantComposite %47 %40 %41 %40 %41 + %51 = OpTypeFloat 32 + %55 = OpConstant %51 1 + %56 = OpConstant %51 0 + %58 = OpTypeVector %51 3 + %61 = OpConstantComposite %58 %55 %56 %55 + %62 = OpTypeVector %51 4 + %65 = OpConstantComposite %62 %55 %56 %55 %56 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %100 None + OpBranchConditional %10 %101 %102 + %101 = OpLabel + %103 = OpCompositeConstruct %62 %55 %55 %55 %56 + OpBranch %100 + %102 = OpLabel + OpBranch %100 + %100 = OpLabel + %200 = OpVectorShuffle %7 %12 %112 1 0 + %201 = OpVectorShuffle %13 %20 %12 0xFFFFFFFF 3 5 + %202 = OpVectorShuffle %28 %27 %35 5 4 1 + %203 = OpVectorShuffle %37 %42 %46 0 1 + %204 = OpVectorShuffle %43 %42 %46 2 3 4 + %205 = OpVectorShuffle %47 %42 %42 0 1 2 3 + %206 = OpVectorShuffle %62 %65 %65 0xFFFFFFFF 3 6 0xFFFFFFFF + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +TEST(TransformationVectorShuffleTest, IllegalInsertionPoints) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" %51 %27 + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + OpName %4 "main" + OpName %25 "buf" + OpMemberName %25 0 "value" + OpName %27 "" + OpName %51 "color" + OpMemberDecorate %25 0 Offset 0 + OpDecorate %25 Block + OpDecorate %27 DescriptorSet 0 + OpDecorate %27 Binding 0 + OpDecorate %51 Location 0 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeFloat 32 + %7 = OpTypeVector %6 4 + %150 = OpTypeVector %6 2 + %10 = OpConstant %6 0.300000012 + %11 = OpConstant %6 0.400000006 + %12 = OpConstant %6 0.5 + %13 = OpConstant %6 1 + %14 = OpConstantComposite %7 %10 %11 %12 %13 + %15 = OpTypeInt 32 1 + %18 = OpConstant %15 0 + %25 = OpTypeStruct %6 + %26 = OpTypePointer Uniform %25 + %27 = OpVariable %26 Uniform + %28 = OpTypePointer Uniform %6 + %32 = OpTypeBool + %103 = OpConstantTrue %32 + %34 = OpConstant %6 0.100000001 + %48 = OpConstant %15 1 + %50 = OpTypePointer Output %7 + %51 = OpVariable %50 Output + %100 = OpTypePointer Function %6 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %101 = OpVariable %100 Function + %102 = OpVariable %100 Function + OpBranch %19 + %19 = OpLabel + %60 = OpPhi %7 %14 %5 %58 %20 + %59 = OpPhi %15 %18 %5 %49 %20 + %29 = OpAccessChain %28 %27 %18 + %30 = OpLoad %6 %29 + %31 = OpConvertFToS %15 %30 + %33 = OpSLessThan %32 %59 %31 + OpLoopMerge %21 %20 None + OpBranchConditional %33 %20 %21 + %20 = OpLabel + %39 = OpCompositeExtract %6 %60 0 + %40 = OpFAdd %6 %39 %34 + %55 = OpCompositeInsert %7 %40 %60 0 + %44 = OpCompositeExtract %6 %60 1 + %45 = OpFSub %6 %44 %34 + %58 = OpCompositeInsert %7 %45 %55 1 + %49 = OpIAdd %15 %59 %48 + OpBranch %19 + %21 = OpLabel + OpStore %51 %60 + OpSelectionMerge %105 None + OpBranchConditional %103 %104 %105 + %104 = OpLabel + OpBranch %105 + %105 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + // Cannot insert before the OpVariables of a function. + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(101, SpvOpVariable, 0), 200, 14, 14, {0, 1}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(101, SpvOpVariable, 1), 200, 14, 14, {1, 2}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(102, SpvOpVariable, 0), 200, 14, 14, {1, 2}) + .IsApplicable(context.get(), transformation_context)); + // OK to insert right after the OpVariables. + ASSERT_FALSE( + TransformationVectorShuffle( + MakeInstructionDescriptor(102, SpvOpBranch, 1), 200, 14, 14, {1, 1}) + .IsApplicable(context.get(), transformation_context)); + + // Cannot insert before the OpPhis of a block. + ASSERT_FALSE( + TransformationVectorShuffle(MakeInstructionDescriptor(60, SpvOpPhi, 0), + 200, 14, 14, {2, 0}) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE( + TransformationVectorShuffle(MakeInstructionDescriptor(59, SpvOpPhi, 0), + 200, 14, 14, {3, 0}) + .IsApplicable(context.get(), transformation_context)); + // OK to insert after the OpPhis. + ASSERT_TRUE(TransformationVectorShuffle( + MakeInstructionDescriptor(59, SpvOpAccessChain, 0), 200, 14, + 14, {3, 4}) + .IsApplicable(context.get(), transformation_context)); + + // Cannot insert before OpLoopMerge + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(33, SpvOpBranchConditional, 0), + 200, 14, 14, {3}) + .IsApplicable(context.get(), transformation_context)); + + // Cannot insert before OpSelectionMerge + ASSERT_FALSE(TransformationVectorShuffle( + MakeInstructionDescriptor(21, SpvOpBranchConditional, 0), + 200, 14, 14, {2}) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationVectorShuffleTest, HandlesIrrelevantIds1) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypeVector %6 2 + %10 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %12 = OpConstantComposite %7 %10 %11 + %112 = OpConstantComposite %7 %11 %10 + %13 = OpTypeVector %6 3 + %16 = OpConstantComposite %13 %10 %11 %10 + %17 = OpTypeVector %6 4 + %20 = OpConstantComposite %17 %10 %11 %10 %11 + %21 = OpTypeInt 32 1 + %22 = OpTypeVector %21 2 + %25 = OpConstant %21 1 + %26 = OpConstant %21 0 + %27 = OpConstantComposite %22 %25 %26 + %28 = OpTypeVector %21 3 + %31 = OpConstantComposite %28 %25 %26 %25 + %32 = OpTypeVector %21 4 + %33 = OpTypePointer Function %32 + %35 = OpConstantComposite %32 %25 %26 %25 %26 + %36 = OpTypeInt 32 0 + %37 = OpTypeVector %36 2 + %40 = OpConstant %36 1 + %41 = OpConstant %36 0 + %42 = OpConstantComposite %37 %40 %41 + %43 = OpTypeVector %36 3 + %46 = OpConstantComposite %43 %40 %41 %40 + %47 = OpTypeVector %36 4 + %50 = OpConstantComposite %47 %40 %41 %40 %41 + %51 = OpTypeFloat 32 + %55 = OpConstant %51 1 + %56 = OpConstant %51 0 + %58 = OpTypeVector %51 3 + %61 = OpConstantComposite %58 %55 %56 %55 + %62 = OpTypeVector %51 4 + %65 = OpConstantComposite %62 %55 %56 %55 %56 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %100 None + OpBranchConditional %10 %101 %102 + %101 = OpLabel + %103 = OpCompositeConstruct %62 %55 %55 %55 %56 + OpBranch %100 + %102 = OpLabel + OpBranch %100 + %100 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + TransformationVectorShuffle transformation( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {2, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {0}), MakeDataDescriptor(200, {1}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(112, {0}), MakeDataDescriptor(200, {0}))); +} + +TEST(TransformationVectorShuffleTest, HandlesIrrelevantIds2) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeBool + %7 = OpTypeVector %6 2 + %10 = OpConstantTrue %6 + %11 = OpConstantFalse %6 + %12 = OpConstantComposite %7 %10 %11 + %112 = OpConstantComposite %7 %11 %10 + %13 = OpTypeVector %6 3 + %16 = OpConstantComposite %13 %10 %11 %10 + %17 = OpTypeVector %6 4 + %20 = OpConstantComposite %17 %10 %11 %10 %11 + %21 = OpTypeInt 32 1 + %22 = OpTypeVector %21 2 + %25 = OpConstant %21 1 + %26 = OpConstant %21 0 + %27 = OpConstantComposite %22 %25 %26 + %28 = OpTypeVector %21 3 + %31 = OpConstantComposite %28 %25 %26 %25 + %32 = OpTypeVector %21 4 + %33 = OpTypePointer Function %32 + %35 = OpConstantComposite %32 %25 %26 %25 %26 + %36 = OpTypeInt 32 0 + %37 = OpTypeVector %36 2 + %40 = OpConstant %36 1 + %41 = OpConstant %36 0 + %42 = OpConstantComposite %37 %40 %41 + %43 = OpTypeVector %36 3 + %46 = OpConstantComposite %43 %40 %41 %40 + %47 = OpTypeVector %36 4 + %50 = OpConstantComposite %47 %40 %41 %40 %41 + %51 = OpTypeFloat 32 + %55 = OpConstant %51 1 + %56 = OpConstant %51 0 + %58 = OpTypeVector %51 3 + %61 = OpConstantComposite %58 %55 %56 %55 + %62 = OpTypeVector %51 4 + %65 = OpConstantComposite %62 %55 %56 %55 %56 + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %100 None + OpBranchConditional %10 %101 %102 + %101 = OpLabel + %103 = OpCompositeConstruct %62 %55 %55 %55 %56 + OpBranch %100 + %102 = OpLabel + OpBranch %100 + %100 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(112); + TransformationVectorShuffle transformation( + MakeInstructionDescriptor(100, SpvOpReturn, 0), 200, 12, 112, {2, 0}); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + // Because %12 is not irrelevant, we get a synonym between it and %200[1]. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(12, {0}), MakeDataDescriptor(200, {1}))); + // Because %112 is irrelevant, we do not get a synonym between it and %200[0]. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(112, {0}), MakeDataDescriptor(200, {0}))); +} + +TEST(TransformationVectorShuffleTest, HandlesIrrelevantIds3) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpTypeVector %6 2 + %8 = OpTypePointer Function %7 + %10 = OpConstant %6 0 + %11 = OpConstant %6 1 + %12 = OpConstantComposite %7 %10 %11 + %40 = OpConstantComposite %7 %10 %11 + %13 = OpTypeBool + %14 = OpConstantFalse %13 + %4 = OpFunction %2 None %3 + %5 = OpLabel + %9 = OpVariable %8 Function + OpStore %9 %12 + OpSelectionMerge %16 None + OpBranchConditional %14 %15 %16 + %15 = OpLabel + OpBranch %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(40); + transformation_context.GetFactManager()->AddFactBlockIsDead(15); + + TransformationVectorShuffle transformation1( + MakeInstructionDescriptor(15, SpvOpBranch, 0), 200, 12, 12, {0, 3}); + ASSERT_TRUE( + transformation1.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation1, context.get(), + &transformation_context); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(200, {0}), MakeDataDescriptor(12, {0}))); + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(200, {1}), MakeDataDescriptor(12, {1}))); + + TransformationVectorShuffle transformation2( + MakeInstructionDescriptor(16, SpvOpReturn, 0), 201, 12, 40, {0, 1}); + ASSERT_TRUE( + transformation2.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation2, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {0}), MakeDataDescriptor(12, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(201, {1}), MakeDataDescriptor(12, {1}))); + + TransformationVectorShuffle transformation3( + MakeInstructionDescriptor(16, SpvOpReturn, 0), 202, 40, 12, {2, 3}); + ASSERT_TRUE( + transformation3.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation3, context.get(), + &transformation_context); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(202, {0}), MakeDataDescriptor(12, {0}))); + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(202, {1}), MakeDataDescriptor(12, {1}))); + + TransformationVectorShuffle transformation4( + MakeInstructionDescriptor(16, SpvOpReturn, 0), 203, 40, 12, {0, 3}); + ASSERT_TRUE( + transformation4.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation4, context.get(), + &transformation_context); + // Because %40 is irrelevant we do not get a synonym between it and %203[0]. + ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(203, {0}), MakeDataDescriptor(40, {0}))); + // Because %12 is *not* irrelevant we do get a synonym between it and %203[1]. + ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous( + MakeDataDescriptor(203, {1}), MakeDataDescriptor(12, {1}))); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_wrap_early_terminator_in_function_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_wrap_early_terminator_in_function_test.cpp new file mode 100644 index 0000000..7b4e487 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_wrap_early_terminator_in_function_test.cpp @@ -0,0 +1,318 @@ +// Copyright (c) 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_early_terminator_in_function.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationWrapEarlyTerminatorInFunctionTest, IsApplicable) { + std::string shader = R"( + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + %90 = OpTypeBool + %91 = OpConstantFalse %90 + + %20 = OpTypeFunction %2 %6 + %21 = OpTypeFunction %6 + + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %11 None + OpSwitch %7 %11 0 %8 1 %9 2 %10 + %8 = OpLabel + OpKill + %9 = OpLabel + OpUnreachable + %10 = OpLabel + OpTerminateInvocation + %11 = OpLabel + OpReturn + OpFunctionEnd + + %30 = OpFunction %2 None %3 + %31 = OpLabel + OpKill + OpFunctionEnd + + %50 = OpFunction %2 None %3 + %51 = OpLabel + OpTerminateInvocation + OpFunctionEnd + + %60 = OpFunction %6 None %21 + %61 = OpLabel + OpBranch %62 + %62 = OpLabel + OpKill + OpFunctionEnd + + %70 = OpFunction %6 None %21 + %71 = OpLabel + OpUnreachable + OpFunctionEnd + + %80 = OpFunction %2 None %20 + %81 = OpFunctionParameter %6 + %82 = OpLabel + OpTerminateInvocation + OpFunctionEnd + + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Bad: id is not fresh + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 61, MakeInstructionDescriptor(8, SpvOpKill, 0), 0) + .IsApplicable(context.get(), transformation_context)); + + // Bad: early terminator instruction descriptor does not exist + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(82, SpvOpKill, 0), 0) + .IsApplicable(context.get(), transformation_context)); + + // Bad: early terminator instruction does not identify an early terminator + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(5, SpvOpSelectionMerge, 0), 0) + .IsApplicable(context.get(), transformation_context)); + + // Bad: no wrapper function is available + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(9, SpvOpUnreachable, 0), 0) + .IsApplicable(context.get(), transformation_context)); + + // Bad: returned value does not exist + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(62, SpvOpKill, 0), 1000) + .IsApplicable(context.get(), transformation_context)); + + // Bad: returned value does not have a type + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(62, SpvOpKill, 0), 61) + .IsApplicable(context.get(), transformation_context)); + + // Bad: returned value type does not match + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(62, SpvOpKill, 0), 91) + .IsApplicable(context.get(), transformation_context)); + + // Bad: returned value is not available + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(62, SpvOpKill, 0), 81) + .IsApplicable(context.get(), transformation_context)); + + // Bad: the OpKill being targeted is in the only available wrapper; we cannot + // have the wrapper call itself. + ASSERT_FALSE(TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(31, SpvOpKill, 0), 0) + .IsApplicable(context.get(), transformation_context)); +} + +TEST(TransformationWrapEarlyTerminatorInFunctionTest, Apply) { + std::string shader = R"( + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + + %20 = OpTypeFunction %2 %6 + %21 = OpTypeFunction %6 + + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %11 None + OpSwitch %7 %11 0 %8 1 %9 2 %10 + %8 = OpLabel + OpKill + %9 = OpLabel + OpUnreachable + %10 = OpLabel + OpTerminateInvocation + %11 = OpLabel + OpReturn + OpFunctionEnd + + %30 = OpFunction %2 None %3 + %31 = OpLabel + OpKill + OpFunctionEnd + + %40 = OpFunction %2 None %3 + %41 = OpLabel + OpUnreachable + OpFunctionEnd + + %50 = OpFunction %2 None %3 + %51 = OpLabel + OpTerminateInvocation + OpFunctionEnd + + %60 = OpFunction %2 None %3 + %61 = OpLabel + OpBranch %62 + %62 = OpLabel + OpKill + OpFunctionEnd + + %70 = OpFunction %6 None %21 + %71 = OpLabel + OpUnreachable + OpFunctionEnd + + %80 = OpFunction %2 None %20 + %81 = OpFunctionParameter %6 + %82 = OpLabel + OpTerminateInvocation + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_4; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + for (auto& transformation : + {TransformationWrapEarlyTerminatorInFunction( + 100, MakeInstructionDescriptor(8, SpvOpKill, 0), 0), + TransformationWrapEarlyTerminatorInFunction( + 101, MakeInstructionDescriptor(9, SpvOpUnreachable, 0), 0), + TransformationWrapEarlyTerminatorInFunction( + 102, MakeInstructionDescriptor(10, SpvOpTerminateInvocation, 0), 0), + TransformationWrapEarlyTerminatorInFunction( + 103, MakeInstructionDescriptor(62, SpvOpKill, 0), 0), + TransformationWrapEarlyTerminatorInFunction( + 104, MakeInstructionDescriptor(71, SpvOpUnreachable, 0), 7), + TransformationWrapEarlyTerminatorInFunction( + 105, MakeInstructionDescriptor(82, SpvOpTerminateInvocation, 0), + 0)}) { + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + } + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + + std::string after_transformation = R"( + OpCapability Shader + OpExtension "SPV_KHR_terminate_invocation" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 320 + OpName %4 "main" + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %6 = OpTypeInt 32 1 + %7 = OpConstant %6 0 + + %20 = OpTypeFunction %2 %6 + %21 = OpTypeFunction %6 + + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %11 None + OpSwitch %7 %11 0 %8 1 %9 2 %10 + %8 = OpLabel + %100 = OpFunctionCall %2 %30 + OpReturn + %9 = OpLabel + %101 = OpFunctionCall %2 %40 + OpReturn + %10 = OpLabel + %102 = OpFunctionCall %2 %50 + OpReturn + %11 = OpLabel + OpReturn + OpFunctionEnd + + %30 = OpFunction %2 None %3 + %31 = OpLabel + OpKill + OpFunctionEnd + + %40 = OpFunction %2 None %3 + %41 = OpLabel + OpUnreachable + OpFunctionEnd + + %50 = OpFunction %2 None %3 + %51 = OpLabel + OpTerminateInvocation + OpFunctionEnd + + %60 = OpFunction %2 None %3 + %61 = OpLabel + OpBranch %62 + %62 = OpLabel + %103 = OpFunctionCall %2 %30 + OpReturn + OpFunctionEnd + + %70 = OpFunction %6 None %21 + %71 = OpLabel + %104 = OpFunctionCall %2 %40 + OpReturnValue %7 + OpFunctionEnd + + %80 = OpFunction %2 None %20 + %81 = OpFunctionParameter %6 + %82 = OpLabel + %105 = OpFunctionCall %2 %50 + OpReturn + OpFunctionEnd + )"; + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/transformation_wrap_region_in_selection_test.cpp b/third_party/spirv-tools/test/fuzz/transformation_wrap_region_in_selection_test.cpp new file mode 100644 index 0000000..9669c41 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/transformation_wrap_region_in_selection_test.cpp @@ -0,0 +1,266 @@ +// Copyright (c) 2020 Vasyl Teliman +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/transformation_wrap_region_in_selection.h" + +#include "gtest/gtest.h" +#include "source/fuzz/fuzzer_util.h" +#include "source/fuzz/instruction_descriptor.h" +#include "test/fuzz/fuzz_test_util.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(TransformationWrapRegionInSelectionTest, BasicTest) { + std::string shader = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpBranch %6 + + %6 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %8 %11 %12 + %11 = OpLabel + OpReturn + + %12 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %8 %13 %14 + %13 = OpLabel + OpBranch %15 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + OpBranch %16 + + %16 = OpLabel + OpReturn + OpFunctionEnd + + %9 = OpFunction %2 None %3 + %10 = OpLabel + OpBranch %20 + + %20 = OpLabel + OpLoopMerge %23 %22 None + OpBranch %21 + %21 = OpLabel + OpBranchConditional %8 %24 %23 + %24 = OpLabel + OpBranch %22 + + ; continue target + %22 = OpLabel + OpLoopMerge %25 %28 None + OpBranchConditional %8 %27 %25 + %27 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranch %22 + %25 = OpLabel + OpBranch %20 + + ; merge block + %23 = OpLabel + OpBranch %26 + + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + const auto env = SPV_ENV_UNIVERSAL_1_3; + const auto consumer = nullptr; + const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); + spvtools::ValidatorOptions validator_options; + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, + kConsoleMessageConsumer)); + TransformationContext transformation_context( + MakeUnique(context.get()), validator_options); + + // Boolean constant does not exist. + ASSERT_FALSE(TransformationWrapRegionInSelection(5, 6, false) + .IsApplicable(context.get(), transformation_context)); + + // Irrelevant constant does not exist. + ASSERT_FALSE(TransformationWrapRegionInSelection(5, 6, true) + .IsApplicable(context.get(), transformation_context)); + + transformation_context.GetFactManager()->AddFactIdIsIrrelevant(8); + + // Block ids are invalid. + ASSERT_FALSE(TransformationWrapRegionInSelection(100, 6, true) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationWrapRegionInSelection(5, 100, true) + .IsApplicable(context.get(), transformation_context)); + + // Blocks are from different functions. + ASSERT_FALSE(TransformationWrapRegionInSelection(5, 10, true) + .IsApplicable(context.get(), transformation_context)); + + // Header block candidate does not dominate merge block candidate. + ASSERT_FALSE(TransformationWrapRegionInSelection(13, 16, true) + .IsApplicable(context.get(), transformation_context)); + + // Header block candidate does not *strictly* dominate merge block candidate. + ASSERT_FALSE(TransformationWrapRegionInSelection(5, 5, true) + .IsApplicable(context.get(), transformation_context)); + + // Merge block candidate does not postdominate header block candidate. + ASSERT_FALSE(TransformationWrapRegionInSelection(5, 16, true) + .IsApplicable(context.get(), transformation_context)); + + // Header block candidate is already a header block of some other construct. + ASSERT_FALSE(TransformationWrapRegionInSelection(12, 16, true) + .IsApplicable(context.get(), transformation_context)); + + // Header block's terminator is not an OpBranch. + ASSERT_FALSE(TransformationWrapRegionInSelection(21, 24, true) + .IsApplicable(context.get(), transformation_context)); + + // Merge block candidate is already a merge block of some other construct. + ASSERT_FALSE(TransformationWrapRegionInSelection(5, 15, true) + .IsApplicable(context.get(), transformation_context)); + + // Header block candidate and merge block candidate are in different + // constructs. + ASSERT_FALSE(TransformationWrapRegionInSelection(10, 21, true) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationWrapRegionInSelection(24, 25, true) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationWrapRegionInSelection(24, 22, true) + .IsApplicable(context.get(), transformation_context)); + ASSERT_FALSE(TransformationWrapRegionInSelection(24, 27, true) + .IsApplicable(context.get(), transformation_context)); + + { + // Header block candidate can be a merge block of some existing construct. + TransformationWrapRegionInSelection transformation(15, 16, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + // Merge block candidate can be a header block of some existing construct. + TransformationWrapRegionInSelection transformation(5, 6, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + { + // Wrap a loop construct. + TransformationWrapRegionInSelection transformation(10, 26, true); + ASSERT_TRUE( + transformation.IsApplicable(context.get(), transformation_context)); + ApplyAndCheckFreshIds(transformation, context.get(), + &transformation_context); + ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( + context.get(), validator_options, kConsoleMessageConsumer)); + } + + std::string after_transformation = R"( + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %4 "main" + OpExecutionMode %4 OriginUpperLeft + OpSource ESSL 310 + %2 = OpTypeVoid + %3 = OpTypeFunction %2 + %7 = OpTypeBool + %8 = OpConstantTrue %7 + + %4 = OpFunction %2 None %3 + %5 = OpLabel + OpSelectionMerge %6 None + OpBranchConditional %8 %6 %6 + + %6 = OpLabel + OpSelectionMerge %12 None + OpBranchConditional %8 %11 %12 + %11 = OpLabel + OpReturn + + %12 = OpLabel + OpSelectionMerge %15 None + OpBranchConditional %8 %13 %14 + %13 = OpLabel + OpBranch %15 + %14 = OpLabel + OpBranch %15 + + %15 = OpLabel + OpSelectionMerge %16 None + OpBranchConditional %8 %16 %16 + %16 = OpLabel + OpReturn + OpFunctionEnd + + %9 = OpFunction %2 None %3 + %10 = OpLabel + OpSelectionMerge %26 None + OpBranchConditional %8 %20 %20 + + %20 = OpLabel + OpLoopMerge %23 %22 None + OpBranch %21 + %21 = OpLabel + OpBranchConditional %8 %24 %23 + %24 = OpLabel + OpBranch %22 + + ; continue target + %22 = OpLabel + OpLoopMerge %25 %28 None + OpBranchConditional %8 %27 %25 + %27 = OpLabel + OpBranch %28 + %28 = OpLabel + OpBranch %22 + %25 = OpLabel + OpBranch %20 + + ; merge block + %23 = OpLabel + OpBranch %26 + + %26 = OpLabel + OpReturn + OpFunctionEnd + )"; + + ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp b/third_party/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp new file mode 100644 index 0000000..dd24794 --- /dev/null +++ b/third_party/spirv-tools/test/fuzz/uniform_buffer_element_descriptor_test.cpp @@ -0,0 +1,85 @@ +// Copyright (c) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/fuzz/uniform_buffer_element_descriptor.h" + +#include "gtest/gtest.h" + +namespace spvtools { +namespace fuzz { +namespace { + +TEST(UniformBufferElementDescriptorTest, TestEquality) { + // Test that equality works as expected for various buffer element + // descriptors. + + protobufs::UniformBufferElementDescriptor descriptor1 = + MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor2 = + MakeUniformBufferElementDescriptor(0, 0, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor3 = + MakeUniformBufferElementDescriptor(0, 1, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor4 = + MakeUniformBufferElementDescriptor(1, 0, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor5 = + MakeUniformBufferElementDescriptor(1, 1, {1, 2, 3}); + protobufs::UniformBufferElementDescriptor descriptor6 = + MakeUniformBufferElementDescriptor(0, 0, {1, 2, 4}); + protobufs::UniformBufferElementDescriptor descriptor7 = + MakeUniformBufferElementDescriptor(0, 0, {1, 2}); + + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor1)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor2)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor3)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor4)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor5)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor6)); + ASSERT_TRUE( + UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor7)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor3)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor3, &descriptor1)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor4)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor4, &descriptor1)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor5)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor5, &descriptor1)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor6)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor6, &descriptor1)); + + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor1, &descriptor7)); + ASSERT_FALSE( + UniformBufferElementDescriptorEquals()(&descriptor7, &descriptor1)); +} + +} // namespace +} // namespace fuzz +} // namespace spvtools diff --git a/third_party/spirv-tools/test/fuzzers/BUILD.gn b/third_party/spirv-tools/test/fuzzers/BUILD.gn new file mode 100644 index 0000000..be1258a --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/BUILD.gn @@ -0,0 +1,215 @@ +# Copyright 2018 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//testing/libfuzzer/fuzzer_test.gni") +import("//testing/test.gni") + +config("fuzzer_config") { + configs = [ "../..:spvtools_internal_config" ] +} + +group("fuzzers") { + testonly = true + deps = [] + + if (!build_with_chromium || use_fuzzing_engine) { + deps += [ ":fuzzers_bin" ] + } +} + +if (!build_with_chromium || use_fuzzing_engine) { + group("fuzzers_bin") { + testonly = true + + deps = [ + ":spvtools_as_fuzzer", + ":spvtools_binary_parser_fuzzer", + ":spvtools_dis_fuzzer", + ":spvtools_opt_legalization_fuzzer", + ":spvtools_opt_performance_fuzzer", + ":spvtools_opt_size_fuzzer", + ":spvtools_opt_webgputovulkan_fuzzer", + ":spvtools_opt_vulkantowebgpu_fuzzer", + ":spvtools_val_fuzzer", + ":spvtools_val_webgpu_fuzzer", + ] + } +} + +template("spvtools_fuzzer") { + source_set(target_name) { + testonly = true + sources = invoker.sources + deps = [ + "../..:spvtools", + "../..:spvtools_opt", + "../..:spvtools_val", + ] + if (defined(invoker.deps)) { + deps += invoker.deps + } + + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ + "//build/config/compiler:no_chromium_code", + ":fuzzer_config", + ] + } +} + +spvtools_fuzzer("spvtools_as_fuzzer_src") { + sources = [ + "spvtools_as_fuzzer.cpp", + ] +} + +spvtools_fuzzer("spvtools_binary_parser_fuzzer_src") { + sources = [ + "spvtools_binary_parser_fuzzer.cpp", + ] +} + +spvtools_fuzzer("spvtools_dis_fuzzer_src") { + sources = [ + "spvtools_dis_fuzzer.cpp", + ] +} + +spvtools_fuzzer("spvtools_opt_performance_fuzzer_src") { + sources = [ + "spvtools_opt_performance_fuzzer.cpp", + ] +} + +spvtools_fuzzer("spvtools_opt_legalization_fuzzer_src") { + sources = [ + "spvtools_opt_legalization_fuzzer.cpp", + ] +} + +spvtools_fuzzer("spvtools_opt_size_fuzzer_src") { + sources = [ + "spvtools_opt_size_fuzzer.cpp", + ] +} + + +spvtools_fuzzer("spvtools_opt_webgputovulkan_fuzzer_src") { + sources = [ + "spvtools_opt_webgputovulkan_fuzzer.cpp", + ] +} + +spvtools_fuzzer("spvtools_opt_vulkantowebgpu_fuzzer_src") { + sources = [ + "spvtools_opt_vulkantowebgpu_fuzzer.cpp", + ] +} + +spvtools_fuzzer("spvtools_val_fuzzer_src") { + sources = [ + "spvtools_val_fuzzer.cpp", + ] +} + +spvtools_fuzzer("spvtools_val_webgpu_fuzzer_src") { + sources = [ + "spvtools_val_webgpu_fuzzer.cpp", + ] +} + +if (!build_with_chromium || use_fuzzing_engine) { + fuzzer_test("spvtools_as_fuzzer") { + sources = [] + deps = [ + ":spvtools_as_fuzzer_src", + ] + # Intentionally doesn't use the seed corpus, because it consumes + # part of the input as not part of the file. + } + + fuzzer_test("spvtools_binary_parser_fuzzer") { + sources = [] + deps = [ + ":spvtools_binary_parser_fuzzer_src", + ] + # Intentionally doesn't use the seed corpus, because it consumes + # part of the input as not part of the file. + } + + fuzzer_test("spvtools_dis_fuzzer") { + sources = [] + deps = [ + ":spvtools_dis_fuzzer_src", + ] + # Intentionally doesn't use the seed corpus, because it consumes + # part of the input as not part of the file. + } + + fuzzer_test("spvtools_opt_performance_fuzzer") { + sources = [] + deps = [ + ":spvtools_opt_performance_fuzzer_src", + ] + seed_corpus = "corpora/spv" + } + + fuzzer_test("spvtools_opt_legalization_fuzzer") { + sources = [] + deps = [ + ":spvtools_opt_legalization_fuzzer_src", + ] + seed_corpus = "corpora/spv" + } + + fuzzer_test("spvtools_opt_size_fuzzer") { + sources = [] + deps = [ + ":spvtools_opt_size_fuzzer_src", + ] + seed_corpus = "corpora/spv" + } + + fuzzer_test("spvtools_opt_webgputovulkan_fuzzer") { + sources = [] + deps = [ + ":spvtools_opt_webgputovulkan_fuzzer_src", + ] + seed_corpus = "corpora/spv" + } + + fuzzer_test("spvtools_opt_vulkantowebgpu_fuzzer") { + sources = [] + deps = [ + ":spvtools_opt_vulkantowebgpu_fuzzer_src", + ] + seed_corpus = "corpora/spv" + } + + fuzzer_test("spvtools_val_fuzzer") { + sources = [] + deps = [ + ":spvtools_val_fuzzer_src", + ] + seed_corpus = "corpora/spv" + } + + fuzzer_test("spvtools_val_webgpu_fuzzer") { + sources = [] + deps = [ + ":spvtools_val_webgpu_fuzzer_src", + ] + seed_corpus = "corpora/spv" + } +} diff --git a/third_party/spirv-tools/test/fuzzers/corpora/spv/simple.spv b/third_party/spirv-tools/test/fuzzers/corpora/spv/simple.spv new file mode 100644 index 0000000000000000000000000000000000000000..f972a56fd96468e5973c1d7943ff28be523effe3 GIT binary patch literal 728 zcmYk4+e-pb5XL9h)vl(QT0Mx;W3Z21hysa_pioqzw^*eIp>}1{p#Qv=qVIP)3mq6| zzHjcc28H9Y*_xSE>`1@7niV_~IE;7K%2t#{hU15Lo|tXRu1TsEI9`Qh1r|y_6-+v` zec97#^pY&IPnnLR-g`ES@;afi|JMbtD`q&pOBSo8m6QeM&C=I2lMcmhN-yFpev3Zh zESkGmc;=cXj=UM%4@QEDFS}XO)zUe}kI$3dsDC?gzM|D#4&BW9>Q9Zfga>BN9M^Rp zt!G`gtvS0XyCkk7X(^stfZ3}pAM?X?#B6vQ4!kAJO>%!#HkSu?<=^u*c@5!)bVowY ziN{UZJnFTi$we-Osn0&dgQ?Fx^f=XJ*o8THa9?8hhQ!j7J5%dQ7`-E*-cXnwDxSMm zHnHFfcX9qVe|_Qc(--br)0|7_PfcdyvFn-Q=*u@@7XCv$#j)3c=h-)(=g{*%ip8ex EFITED5dZ)H literal 0 HcmV?d00001 diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_as_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_as_fuzzer.cpp new file mode 100644 index 0000000..8cecb05 --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_as_fuzzer.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include // memcpy +#include + +#include "source/spirv_target_env.h" +#include "spirv-tools/libspirv.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < sizeof(spv_target_env) + 1) return 0; + + const spv_context context = + spvContextCreate(*reinterpret_cast(data)); + if (context == nullptr) return 0; + + data += sizeof(spv_target_env); + size -= sizeof(spv_target_env); + + std::vector input; + + std::vector input_str; + size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char); + input_str.resize(char_count); + memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t)); + + spv_binary binary = nullptr; + spv_diagnostic diagnostic = nullptr; + spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), + SPV_TEXT_TO_BINARY_OPTION_NONE, &binary, + &diagnostic); + if (diagnostic) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + diagnostic = nullptr; + } + + if (binary) { + spvBinaryDestroy(binary); + binary = nullptr; + } + + spvTextToBinaryWithOptions(context, input_str.data(), input_str.size(), + SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS, + &binary, &diagnostic); + if (diagnostic) { + spvDiagnosticPrint(diagnostic); + spvDiagnosticDestroy(diagnostic); + diagnostic = nullptr; + } + + if (binary) { + spvBinaryDestroy(binary); + binary = nullptr; + } + + spvContextDestroy(context); + + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_binary_parser_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_binary_parser_fuzzer.cpp new file mode 100644 index 0000000..76ba4d9 --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_binary_parser_fuzzer.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "spirv-tools/libspirv.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < sizeof(spv_target_env) + 1) return 0; + + const spv_context context = + spvContextCreate(*reinterpret_cast(data)); + if (context == nullptr) return 0; + + data += sizeof(spv_target_env); + size -= sizeof(spv_target_env); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + spvBinaryParse(context, nullptr, input.data(), input.size(), nullptr, nullptr, + nullptr); + + spvContextDestroy(context); + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp new file mode 100644 index 0000000..ca9a52d --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_dis_fuzzer.cpp @@ -0,0 +1,71 @@ +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include // memcpy +#include + +#include "source/spirv_target_env.h" +#include "spirv-tools/libspirv.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < sizeof(spv_target_env) + 1) return 0; + + const spv_context context = + spvContextCreate(*reinterpret_cast(data)); + if (context == nullptr) return 0; + + data += sizeof(spv_target_env); + size -= sizeof(spv_target_env); + + std::vector input; + input.resize(size >> 2); + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + std::vector input_str; + size_t char_count = input.size() * sizeof(uint32_t) / sizeof(char); + input_str.resize(char_count); + memcpy(input_str.data(), input.data(), input.size() * sizeof(uint32_t)); + + spv_text text = nullptr; + spv_diagnostic diagnostic = nullptr; + + for (uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE; + options < + (SPV_BINARY_TO_TEXT_OPTION_PRINT | SPV_BINARY_TO_TEXT_OPTION_COLOR | + SPV_BINARY_TO_TEXT_OPTION_INDENT | + SPV_BINARY_TO_TEXT_OPTION_SHOW_BYTE_OFFSET | + SPV_BINARY_TO_TEXT_OPTION_NO_HEADER | + SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES); + options++) { + spvBinaryToText(context, input.data(), input.size(), options, &text, + &diagnostic); + if (diagnostic) { + spvDiagnosticDestroy(diagnostic); + diagnostic = nullptr; + } + + if (text) { + spvTextDestroy(text); + text = nullptr; + } + } + + spvContextDestroy(context); + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp new file mode 100644 index 0000000..b45a98c --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_opt_legalization_fuzzer.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "spirv-tools/optimizer.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + optimizer.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + optimizer.RegisterLegalizationPasses(); + optimizer.Run(input.data(), input.size(), &input); + + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_opt_performance_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_opt_performance_fuzzer.cpp new file mode 100644 index 0000000..6c3bd6a --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_opt_performance_fuzzer.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "spirv-tools/optimizer.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + optimizer.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + optimizer.RegisterPerformancePasses(); + optimizer.Run(input.data(), input.size(), &input); + + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_opt_size_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_opt_size_fuzzer.cpp new file mode 100644 index 0000000..68c7974 --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_opt_size_fuzzer.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "spirv-tools/optimizer.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + spvtools::Optimizer optimizer(SPV_ENV_UNIVERSAL_1_3); + optimizer.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + optimizer.RegisterSizePasses(); + optimizer.Run(input.data(), input.size(), &input); + + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp new file mode 100644 index 0000000..9371c0d --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_opt_vulkantowebgpu_fuzzer.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "spirv-tools/optimizer.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + spvtools::Optimizer optimizer(SPV_ENV_VULKAN_1_1); + optimizer.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + optimizer.RegisterVulkanToWebGPUPasses(); + optimizer.Run(input.data(), input.size(), &input); + + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp new file mode 100644 index 0000000..78ddbb7 --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_opt_webgputovulkan_fuzzer.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "spirv-tools/optimizer.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + spvtools::Optimizer optimizer(SPV_ENV_WEBGPU_0); + optimizer.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + optimizer.RegisterWebGPUToVulkanPasses(); + optimizer.Run(input.data(), input.size(), &input); + + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_val_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_val_fuzzer.cpp new file mode 100644 index 0000000..5dc4303 --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_val_fuzzer.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2018 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "spirv-tools/libspirv.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_3); + tools.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + tools.Validate(input); + return 0; +} diff --git a/third_party/spirv-tools/test/fuzzers/spvtools_val_webgpu_fuzzer.cpp b/third_party/spirv-tools/test/fuzzers/spvtools_val_webgpu_fuzzer.cpp new file mode 100644 index 0000000..bed6e1a --- /dev/null +++ b/third_party/spirv-tools/test/fuzzers/spvtools_val_webgpu_fuzzer.cpp @@ -0,0 +1,36 @@ +// Copyright (c) 2019 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "spirv-tools/libspirv.hpp" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + spvtools::SpirvTools tools(SPV_ENV_WEBGPU_0); + tools.SetMessageConsumer([](spv_message_level_t, const char*, + const spv_position_t&, const char*) {}); + + std::vector input; + input.resize(size >> 2); + + size_t count = 0; + for (size_t i = 0; (i + 3) < size; i += 4) { + input[count++] = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | + (data[i + 3]) << 24; + } + + tools.Validate(input); + return 0; +} diff --git a/third_party/spirv-tools/test/generator_magic_number_test.cpp b/third_party/spirv-tools/test/generator_magic_number_test.cpp new file mode 100644 index 0000000..7131ac4 --- /dev/null +++ b/third_party/spirv-tools/test/generator_magic_number_test.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/opcode.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::spvtest::EnumCase; +using ::testing::Eq; +using GeneratorMagicNumberTest = + ::testing::TestWithParam>; + +TEST_P(GeneratorMagicNumberTest, Single) { + EXPECT_THAT(std::string(spvGeneratorStr(GetParam().value())), + GetParam().name()); +} + +INSTANTIATE_TEST_SUITE_P( + Registered, GeneratorMagicNumberTest, + ::testing::ValuesIn(std::vector>{ + {SPV_GENERATOR_KHRONOS, "Khronos"}, + {SPV_GENERATOR_LUNARG, "LunarG"}, + {SPV_GENERATOR_VALVE, "Valve"}, + {SPV_GENERATOR_CODEPLAY, "Codeplay"}, + {SPV_GENERATOR_NVIDIA, "NVIDIA"}, + {SPV_GENERATOR_ARM, "ARM"}, + {SPV_GENERATOR_KHRONOS_LLVM_TRANSLATOR, + "Khronos LLVM/SPIR-V Translator"}, + {SPV_GENERATOR_KHRONOS_ASSEMBLER, "Khronos SPIR-V Tools Assembler"}, + {SPV_GENERATOR_KHRONOS_GLSLANG, "Khronos Glslang Reference Front End"}, + })); + +INSTANTIATE_TEST_SUITE_P( + Unregistered, GeneratorMagicNumberTest, + ::testing::ValuesIn(std::vector>{ + // We read registered entries from the SPIR-V XML Registry file + // which can change over time. + {spv_generator_t(1000), "Unknown"}, + {spv_generator_t(9999), "Unknown"}, + })); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/hex_float_test.cpp b/third_party/spirv-tools/test/hex_float_test.cpp new file mode 100644 index 0000000..c422f75 --- /dev/null +++ b/third_party/spirv-tools/test/hex_float_test.cpp @@ -0,0 +1,1331 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/util/hex_float.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace utils { +namespace { + +using ::testing::Eq; + +// In this file "encode" means converting a number into a string, +// and "decode" means converting a string into a number. + +using HexFloatTest = + ::testing::TestWithParam, std::string>>; +using DecodeHexFloatTest = + ::testing::TestWithParam>>; +using HexDoubleTest = + ::testing::TestWithParam, std::string>>; +using DecodeHexDoubleTest = + ::testing::TestWithParam>>; +using RoundTripFloatTest = ::testing::TestWithParam; +using RoundTripDoubleTest = ::testing::TestWithParam; + +// Hex-encodes a float value. +template +std::string EncodeViaHexFloat(const T& value) { + std::stringstream ss; + ss << HexFloat(value); + return ss.str(); +} + +// The following two tests can't be DRY because they take different parameter +// types. + +TEST_P(HexFloatTest, EncodeCorrectly) { + EXPECT_THAT(EncodeViaHexFloat(GetParam().first), Eq(GetParam().second)); +} + +TEST_P(HexDoubleTest, EncodeCorrectly) { + EXPECT_THAT(EncodeViaHexFloat(GetParam().first), Eq(GetParam().second)); +} + +// Decodes a hex-float string. +template +FloatProxy Decode(const std::string& str) { + HexFloat> decoded(0.f); + EXPECT_TRUE((std::stringstream(str) >> decoded).eof()); + return decoded.value(); +} + +TEST_P(HexFloatTest, DecodeCorrectly) { + EXPECT_THAT(Decode(GetParam().second), Eq(GetParam().first)); +} + +TEST_P(HexDoubleTest, DecodeCorrectly) { + EXPECT_THAT(Decode(GetParam().second), Eq(GetParam().first)); +} + +INSTANTIATE_TEST_SUITE_P( + Float32Tests, HexFloatTest, + ::testing::ValuesIn(std::vector, std::string>>({ + {0.f, "0x0p+0"}, + {1.f, "0x1p+0"}, + {2.f, "0x1p+1"}, + {3.f, "0x1.8p+1"}, + {0.5f, "0x1p-1"}, + {0.25f, "0x1p-2"}, + {0.75f, "0x1.8p-1"}, + {-0.f, "-0x0p+0"}, + {-1.f, "-0x1p+0"}, + {-0.5f, "-0x1p-1"}, + {-0.25f, "-0x1p-2"}, + {-0.75f, "-0x1.8p-1"}, + + // Larger numbers + {512.f, "0x1p+9"}, + {-512.f, "-0x1p+9"}, + {1024.f, "0x1p+10"}, + {-1024.f, "-0x1p+10"}, + {1024.f + 8.f, "0x1.02p+10"}, + {-1024.f - 8.f, "-0x1.02p+10"}, + + // Small numbers + {1.0f / 512.f, "0x1p-9"}, + {1.0f / -512.f, "-0x1p-9"}, + {1.0f / 1024.f, "0x1p-10"}, + {1.0f / -1024.f, "-0x1p-10"}, + {1.0f / 1024.f + 1.0f / 8.f, "0x1.02p-3"}, + {1.0f / -1024.f - 1.0f / 8.f, "-0x1.02p-3"}, + + // lowest non-denorm + {float(ldexp(1.0f, -126)), "0x1p-126"}, + {float(ldexp(-1.0f, -126)), "-0x1p-126"}, + + // Denormalized values + {float(ldexp(1.0f, -127)), "0x1p-127"}, + {float(ldexp(1.0f, -127) / 2.0f), "0x1p-128"}, + {float(ldexp(1.0f, -127) / 4.0f), "0x1p-129"}, + {float(ldexp(1.0f, -127) / 8.0f), "0x1p-130"}, + {float(ldexp(-1.0f, -127)), "-0x1p-127"}, + {float(ldexp(-1.0f, -127) / 2.0f), "-0x1p-128"}, + {float(ldexp(-1.0f, -127) / 4.0f), "-0x1p-129"}, + {float(ldexp(-1.0f, -127) / 8.0f), "-0x1p-130"}, + + {float(ldexp(1.0, -127) + (ldexp(1.0, -127) / 2.0f)), "0x1.8p-127"}, + {float(ldexp(1.0, -127) / 2.0 + (ldexp(1.0, -127) / 4.0f)), + "0x1.8p-128"}, + + }))); + +INSTANTIATE_TEST_SUITE_P( + Float32NanTests, HexFloatTest, + ::testing::ValuesIn(std::vector, std::string>>({ + // Various NAN and INF cases + {uint32_t(0xFF800000), "-0x1p+128"}, // -inf + {uint32_t(0x7F800000), "0x1p+128"}, // inf + {uint32_t(0xFFC00000), "-0x1.8p+128"}, // -nan + {uint32_t(0xFF800100), "-0x1.0002p+128"}, // -nan + {uint32_t(0xFF800c00), "-0x1.0018p+128"}, // -nan + {uint32_t(0xFF80F000), "-0x1.01ep+128"}, // -nan + {uint32_t(0xFFFFFFFF), "-0x1.fffffep+128"}, // -nan + {uint32_t(0x7FC00000), "0x1.8p+128"}, // +nan + {uint32_t(0x7F800100), "0x1.0002p+128"}, // +nan + {uint32_t(0x7f800c00), "0x1.0018p+128"}, // +nan + {uint32_t(0x7F80F000), "0x1.01ep+128"}, // +nan + {uint32_t(0x7FFFFFFF), "0x1.fffffep+128"}, // +nan + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64Tests, HexDoubleTest, + ::testing::ValuesIn( + std::vector, std::string>>({ + {0., "0x0p+0"}, + {1., "0x1p+0"}, + {2., "0x1p+1"}, + {3., "0x1.8p+1"}, + {0.5, "0x1p-1"}, + {0.25, "0x1p-2"}, + {0.75, "0x1.8p-1"}, + {-0., "-0x0p+0"}, + {-1., "-0x1p+0"}, + {-0.5, "-0x1p-1"}, + {-0.25, "-0x1p-2"}, + {-0.75, "-0x1.8p-1"}, + + // Larger numbers + {512., "0x1p+9"}, + {-512., "-0x1p+9"}, + {1024., "0x1p+10"}, + {-1024., "-0x1p+10"}, + {1024. + 8., "0x1.02p+10"}, + {-1024. - 8., "-0x1.02p+10"}, + + // Large outside the range of normal floats + {ldexp(1.0, 128), "0x1p+128"}, + {ldexp(1.0, 129), "0x1p+129"}, + {ldexp(-1.0, 128), "-0x1p+128"}, + {ldexp(-1.0, 129), "-0x1p+129"}, + {ldexp(1.0, 128) + ldexp(1.0, 90), "0x1.0000000004p+128"}, + {ldexp(1.0, 129) + ldexp(1.0, 120), "0x1.008p+129"}, + {ldexp(-1.0, 128) + ldexp(1.0, 90), "-0x1.fffffffff8p+127"}, + {ldexp(-1.0, 129) + ldexp(1.0, 120), "-0x1.ffp+128"}, + + // Small numbers + {1.0 / 512., "0x1p-9"}, + {1.0 / -512., "-0x1p-9"}, + {1.0 / 1024., "0x1p-10"}, + {1.0 / -1024., "-0x1p-10"}, + {1.0 / 1024. + 1.0 / 8., "0x1.02p-3"}, + {1.0 / -1024. - 1.0 / 8., "-0x1.02p-3"}, + + // Small outside the range of normal floats + {ldexp(1.0, -128), "0x1p-128"}, + {ldexp(1.0, -129), "0x1p-129"}, + {ldexp(-1.0, -128), "-0x1p-128"}, + {ldexp(-1.0, -129), "-0x1p-129"}, + {ldexp(1.0, -128) + ldexp(1.0, -90), "0x1.0000000004p-90"}, + {ldexp(1.0, -129) + ldexp(1.0, -120), "0x1.008p-120"}, + {ldexp(-1.0, -128) + ldexp(1.0, -90), "0x1.fffffffff8p-91"}, + {ldexp(-1.0, -129) + ldexp(1.0, -120), "0x1.ffp-121"}, + + // lowest non-denorm + {ldexp(1.0, -1022), "0x1p-1022"}, + {ldexp(-1.0, -1022), "-0x1p-1022"}, + + // Denormalized values + {ldexp(1.0, -1023), "0x1p-1023"}, + {ldexp(1.0, -1023) / 2.0, "0x1p-1024"}, + {ldexp(1.0, -1023) / 4.0, "0x1p-1025"}, + {ldexp(1.0, -1023) / 8.0, "0x1p-1026"}, + {ldexp(-1.0, -1024), "-0x1p-1024"}, + {ldexp(-1.0, -1024) / 2.0, "-0x1p-1025"}, + {ldexp(-1.0, -1024) / 4.0, "-0x1p-1026"}, + {ldexp(-1.0, -1024) / 8.0, "-0x1p-1027"}, + + {ldexp(1.0, -1023) + (ldexp(1.0, -1023) / 2.0), "0x1.8p-1023"}, + {ldexp(1.0, -1023) / 2.0 + (ldexp(1.0, -1023) / 4.0), + "0x1.8p-1024"}, + + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64NanTests, HexDoubleTest, + ::testing::ValuesIn(std::vector< + std::pair, std::string>>({ + // Various NAN and INF cases + {uint64_t(0xFFF0000000000000LL), "-0x1p+1024"}, // -inf + {uint64_t(0x7FF0000000000000LL), "0x1p+1024"}, // +inf + {uint64_t(0xFFF8000000000000LL), "-0x1.8p+1024"}, // -nan + {uint64_t(0xFFF0F00000000000LL), "-0x1.0fp+1024"}, // -nan + {uint64_t(0xFFF0000000000001LL), "-0x1.0000000000001p+1024"}, // -nan + {uint64_t(0xFFF0000300000000LL), "-0x1.00003p+1024"}, // -nan + {uint64_t(0xFFFFFFFFFFFFFFFFLL), "-0x1.fffffffffffffp+1024"}, // -nan + {uint64_t(0x7FF8000000000000LL), "0x1.8p+1024"}, // +nan + {uint64_t(0x7FF0F00000000000LL), "0x1.0fp+1024"}, // +nan + {uint64_t(0x7FF0000000000001LL), "0x1.0000000000001p+1024"}, // -nan + {uint64_t(0x7FF0000300000000LL), "0x1.00003p+1024"}, // -nan + {uint64_t(0x7FFFFFFFFFFFFFFFLL), "0x1.fffffffffffffp+1024"}, // -nan + }))); + +// Tests that encoding a value and decoding it again restores +// the same value. +TEST_P(RoundTripFloatTest, CanStoreAccurately) { + std::stringstream ss; + ss << FloatProxy(GetParam()); + ss.seekg(0); + FloatProxy res; + ss >> res; + EXPECT_THAT(GetParam(), Eq(res.getAsFloat())); +} + +TEST_P(RoundTripDoubleTest, CanStoreAccurately) { + std::stringstream ss; + ss << FloatProxy(GetParam()); + ss.seekg(0); + FloatProxy res; + ss >> res; + EXPECT_THAT(GetParam(), Eq(res.getAsFloat())); +} + +INSTANTIATE_TEST_SUITE_P( + Float32StoreTests, RoundTripFloatTest, + ::testing::ValuesIn(std::vector( + {// Value requiring more than 6 digits of precision to be + // represented accurately. + 3.0000002f}))); + +INSTANTIATE_TEST_SUITE_P( + Float64StoreTests, RoundTripDoubleTest, + ::testing::ValuesIn(std::vector( + {// Value requiring more than 15 digits of precision to be + // represented accurately. + 1.5000000000000002}))); + +TEST(HexFloatStreamTest, OperatorLeftShiftPreservesFloatAndFill) { + std::stringstream s; + s << std::setw(4) << std::oct << std::setfill('x') << 8 << " " + << FloatProxy(uint32_t(0xFF800100)) << " " << std::setw(4) << 9; + EXPECT_THAT(s.str(), Eq(std::string("xx10 -0x1.0002p+128 xx11"))); +} + +TEST(HexDoubleStreamTest, OperatorLeftShiftPreservesFloatAndFill) { + std::stringstream s; + s << std::setw(4) << std::oct << std::setfill('x') << 8 << " " + << FloatProxy(uint64_t(0x7FF0F00000000000LL)) << " " << std::setw(4) + << 9; + EXPECT_THAT(s.str(), Eq(std::string("xx10 0x1.0fp+1024 xx11"))); +} + +TEST_P(DecodeHexFloatTest, DecodeCorrectly) { + EXPECT_THAT(Decode(GetParam().first), Eq(GetParam().second)); +} + +TEST_P(DecodeHexDoubleTest, DecodeCorrectly) { + EXPECT_THAT(Decode(GetParam().first), Eq(GetParam().second)); +} + +INSTANTIATE_TEST_SUITE_P( + Float32DecodeTests, DecodeHexFloatTest, + ::testing::ValuesIn(std::vector>>({ + {"0x0p+000", 0.f}, + {"0x0p0", 0.f}, + {"0x0p-0", 0.f}, + + // flush to zero cases + {"0x1p-500", 0.f}, // Exponent underflows. + {"-0x1p-500", -0.f}, + {"0x0.00000000001p-126", 0.f}, // Fraction causes underflow. + {"-0x0.0000000001p-127", -0.f}, + {"-0x0.01p-142", -0.f}, // Fraction causes additional underflow. + {"0x0.01p-142", 0.f}, + + // Some floats that do not encode the same way as they decode. + {"0x2p+0", 2.f}, + {"0xFFp+0", 255.f}, + {"0x0.8p+0", 0.5f}, + {"0x0.4p+0", 0.25f}, + }))); + +INSTANTIATE_TEST_SUITE_P( + Float32DecodeInfTests, DecodeHexFloatTest, + ::testing::ValuesIn(std::vector>>({ + // inf cases + {"-0x1p+128", uint32_t(0xFF800000)}, // -inf + {"0x32p+127", uint32_t(0x7F800000)}, // inf + {"0x32p+500", uint32_t(0x7F800000)}, // inf + {"-0x32p+127", uint32_t(0xFF800000)}, // -inf + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64DecodeTests, DecodeHexDoubleTest, + ::testing::ValuesIn( + std::vector>>({ + {"0x0p+000", 0.}, + {"0x0p0", 0.}, + {"0x0p-0", 0.}, + + // flush to zero cases + {"0x1p-5000", 0.}, // Exponent underflows. + {"-0x1p-5000", -0.}, + {"0x0.0000000000000001p-1023", 0.}, // Fraction causes underflow. + {"-0x0.000000000000001p-1024", -0.}, + {"-0x0.01p-1090", -0.f}, // Fraction causes additional underflow. + {"0x0.01p-1090", 0.}, + + // Some floats that do not encode the same way as they decode. + {"0x2p+0", 2.}, + {"0xFFp+0", 255.}, + {"0x0.8p+0", 0.5}, + {"0x0.4p+0", 0.25}, + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64DecodeInfTests, DecodeHexDoubleTest, + ::testing::ValuesIn( + std::vector>>({ + // inf cases + {"-0x1p+1024", uint64_t(0xFFF0000000000000)}, // -inf + {"0x32p+1023", uint64_t(0x7FF0000000000000)}, // inf + {"0x32p+5000", uint64_t(0x7FF0000000000000)}, // inf + {"-0x32p+1023", uint64_t(0xFFF0000000000000)}, // -inf + }))); + +TEST(FloatProxy, ValidConversion) { + EXPECT_THAT(FloatProxy(1.f).getAsFloat(), Eq(1.0f)); + EXPECT_THAT(FloatProxy(32.f).getAsFloat(), Eq(32.0f)); + EXPECT_THAT(FloatProxy(-1.f).getAsFloat(), Eq(-1.0f)); + EXPECT_THAT(FloatProxy(0.f).getAsFloat(), Eq(0.0f)); + EXPECT_THAT(FloatProxy(-0.f).getAsFloat(), Eq(-0.0f)); + EXPECT_THAT(FloatProxy(1.2e32f).getAsFloat(), Eq(1.2e32f)); + + EXPECT_TRUE(std::isinf(FloatProxy(uint32_t(0xFF800000)).getAsFloat())); + EXPECT_TRUE(std::isinf(FloatProxy(uint32_t(0x7F800000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFFC00000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFF800100)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFF800c00)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFF80F000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0xFFFFFFFF)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7FC00000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7F800100)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7f800c00)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7F80F000)).getAsFloat())); + EXPECT_TRUE(std::isnan(FloatProxy(uint32_t(0x7FFFFFFF)).getAsFloat())); + + EXPECT_THAT(FloatProxy(uint32_t(0xFF800000)).data(), Eq(0xFF800000u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F800000)).data(), Eq(0x7F800000u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFFC00000)).data(), Eq(0xFFC00000u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF800100)).data(), Eq(0xFF800100u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF800c00)).data(), Eq(0xFF800c00u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFF80F000)).data(), Eq(0xFF80F000u)); + EXPECT_THAT(FloatProxy(uint32_t(0xFFFFFFFF)).data(), Eq(0xFFFFFFFFu)); + EXPECT_THAT(FloatProxy(uint32_t(0x7FC00000)).data(), Eq(0x7FC00000u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F800100)).data(), Eq(0x7F800100u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7f800c00)).data(), Eq(0x7f800c00u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7F80F000)).data(), Eq(0x7F80F000u)); + EXPECT_THAT(FloatProxy(uint32_t(0x7FFFFFFF)).data(), Eq(0x7FFFFFFFu)); +} + +TEST(FloatProxy, Nan) { + EXPECT_TRUE(FloatProxy(uint32_t(0xFFC00000)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0xFF800100)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0xFF800c00)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0xFF80F000)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0xFFFFFFFF)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7FC00000)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7F800100)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7f800c00)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7F80F000)).isNan()); + EXPECT_TRUE(FloatProxy(uint32_t(0x7FFFFFFF)).isNan()); +} + +TEST(FloatProxy, Negation) { + EXPECT_THAT((-FloatProxy(1.f)).getAsFloat(), Eq(-1.0f)); + EXPECT_THAT((-FloatProxy(0.f)).getAsFloat(), Eq(-0.0f)); + + EXPECT_THAT((-FloatProxy(-1.f)).getAsFloat(), Eq(1.0f)); + EXPECT_THAT((-FloatProxy(-0.f)).getAsFloat(), Eq(0.0f)); + + EXPECT_THAT((-FloatProxy(32.f)).getAsFloat(), Eq(-32.0f)); + EXPECT_THAT((-FloatProxy(-32.f)).getAsFloat(), Eq(32.0f)); + + EXPECT_THAT((-FloatProxy(1.2e32f)).getAsFloat(), Eq(-1.2e32f)); + EXPECT_THAT((-FloatProxy(-1.2e32f)).getAsFloat(), Eq(1.2e32f)); + + EXPECT_THAT( + (-FloatProxy(std::numeric_limits::infinity())).getAsFloat(), + Eq(-std::numeric_limits::infinity())); + EXPECT_THAT((-FloatProxy(-std::numeric_limits::infinity())) + .getAsFloat(), + Eq(std::numeric_limits::infinity())); +} + +// Test conversion of FloatProxy values to strings. +// +// In previous cases, we always wrapped the FloatProxy value in a HexFloat +// before conversion to a string. In the following cases, the FloatProxy +// decides for itself whether to print as a regular number or as a hex float. + +using FloatProxyFloatTest = + ::testing::TestWithParam, std::string>>; +using FloatProxyDoubleTest = + ::testing::TestWithParam, std::string>>; + +// Converts a float value to a string via a FloatProxy. +template +std::string EncodeViaFloatProxy(const T& value) { + std::stringstream ss; + ss << value; + return ss.str(); +} + +// Converts a floating point string so that the exponent prefix +// is 'e', and the exponent value does not have leading zeros. +// The Microsoft runtime library likes to write things like "2.5E+010". +// Convert that to "2.5e+10". +// We don't care what happens to strings that are not floating point +// strings. +std::string NormalizeExponentInFloatString(std::string in) { + std::string result; + // Reserve one spot for the terminating null, even when the sscanf fails. + std::vector prefix(in.size() + 1); + char e; + char plus_or_minus; + int exponent; // in base 10 + if ((4 == std::sscanf(in.c_str(), "%[-+.0123456789]%c%c%d", prefix.data(), &e, + &plus_or_minus, &exponent)) && + (e == 'e' || e == 'E') && + (plus_or_minus == '-' || plus_or_minus == '+')) { + // It looks like a floating point value with exponent. + std::stringstream out; + out << prefix.data() << 'e' << plus_or_minus << exponent; + result = out.str(); + } else { + result = in; + } + return result; +} + +TEST(NormalizeFloat, Sample) { + EXPECT_THAT(NormalizeExponentInFloatString(""), Eq("")); + EXPECT_THAT(NormalizeExponentInFloatString("1e-12"), Eq("1e-12")); + EXPECT_THAT(NormalizeExponentInFloatString("1E+14"), Eq("1e+14")); + EXPECT_THAT(NormalizeExponentInFloatString("1e-0012"), Eq("1e-12")); + EXPECT_THAT(NormalizeExponentInFloatString("1.263E+014"), Eq("1.263e+14")); +} + +// The following two tests can't be DRY because they take different parameter +// types. +TEST_P(FloatProxyFloatTest, EncodeCorrectly) { + EXPECT_THAT( + NormalizeExponentInFloatString(EncodeViaFloatProxy(GetParam().first)), + Eq(GetParam().second)); +} + +TEST_P(FloatProxyDoubleTest, EncodeCorrectly) { + EXPECT_THAT( + NormalizeExponentInFloatString(EncodeViaFloatProxy(GetParam().first)), + Eq(GetParam().second)); +} + +INSTANTIATE_TEST_SUITE_P( + Float32Tests, FloatProxyFloatTest, + ::testing::ValuesIn(std::vector, std::string>>({ + // Zero + {0.f, "0"}, + // Normal numbers + {1.f, "1"}, + {-0.25f, "-0.25"}, + {1000.0f, "1000"}, + + // Still normal numbers, but with large magnitude exponents. + {float(ldexp(1.f, 126)), "8.50705917e+37"}, + {float(ldexp(-1.f, -126)), "-1.17549435e-38"}, + + // denormalized values are printed as hex floats. + {float(ldexp(1.0f, -127)), "0x1p-127"}, + {float(ldexp(1.5f, -128)), "0x1.8p-128"}, + {float(ldexp(1.25, -129)), "0x1.4p-129"}, + {float(ldexp(1.125, -130)), "0x1.2p-130"}, + {float(ldexp(-1.0f, -127)), "-0x1p-127"}, + {float(ldexp(-1.0f, -128)), "-0x1p-128"}, + {float(ldexp(-1.0f, -129)), "-0x1p-129"}, + {float(ldexp(-1.5f, -130)), "-0x1.8p-130"}, + + // NaNs + {FloatProxy(uint32_t(0xFFC00000)), "-0x1.8p+128"}, + {FloatProxy(uint32_t(0xFF800100)), "-0x1.0002p+128"}, + + {std::numeric_limits::infinity(), "0x1p+128"}, + {-std::numeric_limits::infinity(), "-0x1p+128"}, + }))); + +INSTANTIATE_TEST_SUITE_P( + Float64Tests, FloatProxyDoubleTest, + ::testing::ValuesIn( + std::vector, std::string>>({ + {0., "0"}, + {1., "1"}, + {-0.25, "-0.25"}, + {1000.0, "1000"}, + + // Large outside the range of normal floats + {ldexp(1.0, 128), "3.4028236692093846e+38"}, + {ldexp(1.5, 129), "1.0208471007628154e+39"}, + {ldexp(-1.0, 128), "-3.4028236692093846e+38"}, + {ldexp(-1.5, 129), "-1.0208471007628154e+39"}, + + // Small outside the range of normal floats + {ldexp(1.5, -129), "2.2040519077917891e-39"}, + {ldexp(-1.5, -129), "-2.2040519077917891e-39"}, + + // lowest non-denorm + {ldexp(1.0, -1022), "2.2250738585072014e-308"}, + {ldexp(-1.0, -1022), "-2.2250738585072014e-308"}, + + // Denormalized values + {ldexp(1.125, -1023), "0x1.2p-1023"}, + {ldexp(-1.375, -1024), "-0x1.6p-1024"}, + + // NaNs + {uint64_t(0x7FF8000000000000LL), "0x1.8p+1024"}, + {uint64_t(0xFFF0F00000000000LL), "-0x1.0fp+1024"}, + + // Infinity + {std::numeric_limits::infinity(), "0x1p+1024"}, + {-std::numeric_limits::infinity(), "-0x1p+1024"}, + + }))); + +// double is used so that unbiased_exponent can be used with the output +// of ldexp directly. +int32_t unbiased_exponent(double f) { + return HexFloat>(static_cast(f)) + .getUnbiasedNormalizedExponent(); +} + +int16_t unbiased_half_exponent(uint16_t f) { + return HexFloat>(f).getUnbiasedNormalizedExponent(); +} + +TEST(HexFloatOperationTest, UnbiasedExponent) { + // Float cases + EXPECT_EQ(0, unbiased_exponent(ldexp(1.0f, 0))); + EXPECT_EQ(-32, unbiased_exponent(ldexp(1.0f, -32))); + EXPECT_EQ(42, unbiased_exponent(ldexp(1.0f, 42))); + EXPECT_EQ(125, unbiased_exponent(ldexp(1.0f, 125))); + + EXPECT_EQ(128, + HexFloat>(std::numeric_limits::infinity()) + .getUnbiasedNormalizedExponent()); + + EXPECT_EQ(-100, unbiased_exponent(ldexp(1.0f, -100))); + EXPECT_EQ(-127, unbiased_exponent(ldexp(1.0f, -127))); // First denorm + EXPECT_EQ(-128, unbiased_exponent(ldexp(1.0f, -128))); + EXPECT_EQ(-129, unbiased_exponent(ldexp(1.0f, -129))); + EXPECT_EQ(-140, unbiased_exponent(ldexp(1.0f, -140))); + // Smallest representable number + EXPECT_EQ(-126 - 23, unbiased_exponent(ldexp(1.0f, -126 - 23))); + // Should get rounded to 0 first. + EXPECT_EQ(0, unbiased_exponent(ldexp(1.0f, -127 - 23))); + + // Float16 cases + // The exponent is represented in the bits 0x7C00 + // The offset is -15 + EXPECT_EQ(0, unbiased_half_exponent(0x3C00)); + EXPECT_EQ(3, unbiased_half_exponent(0x4800)); + EXPECT_EQ(-1, unbiased_half_exponent(0x3800)); + EXPECT_EQ(-14, unbiased_half_exponent(0x0400)); + EXPECT_EQ(16, unbiased_half_exponent(0x7C00)); + EXPECT_EQ(10, unbiased_half_exponent(0x6400)); + + // Smallest representable number + EXPECT_EQ(-24, unbiased_half_exponent(0x0001)); +} + +// Creates a float that is the sum of 1/(2 ^ fractions[i]) for i in factions +float float_fractions(const std::vector& fractions) { + float f = 0; + for (int32_t i : fractions) { + f += std::ldexp(1.0f, -i); + } + return f; +} + +// Returns the normalized significand of a HexFloat> +// that was created by calling float_fractions with the input fractions, +// raised to the power of exp. +uint32_t normalized_significand(const std::vector& fractions, + uint32_t exp) { + return HexFloat>( + static_cast(ldexp(float_fractions(fractions), exp))) + .getNormalizedSignificand(); +} + +// Sets the bits from MSB to LSB of the significand part of a float. +// For example 0 would set the bit 23 (counting from LSB to MSB), +// and 1 would set the 22nd bit. +uint32_t bits_set(const std::vector& bits) { + const uint32_t top_bit = 1u << 22u; + uint32_t val = 0; + for (uint32_t i : bits) { + val |= top_bit >> i; + } + return val; +} + +// The same as bits_set but for a Float16 value instead of 32-bit floating +// point. +uint16_t half_bits_set(const std::vector& bits) { + const uint32_t top_bit = 1u << 9u; + uint32_t val = 0; + for (uint32_t i : bits) { + val |= top_bit >> i; + } + return static_cast(val); +} + +TEST(HexFloatOperationTest, NormalizedSignificand) { + // For normalized numbers (the following) it should be a simple matter + // of getting rid of the top implicit bit + EXPECT_EQ(bits_set({}), normalized_significand({0}, 0)); + EXPECT_EQ(bits_set({0}), normalized_significand({0, 1}, 0)); + EXPECT_EQ(bits_set({0, 1}), normalized_significand({0, 1, 2}, 0)); + EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 0)); + EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 32)); + EXPECT_EQ(bits_set({1}), normalized_significand({0, 2}, 126)); + + // For denormalized numbers we expect the normalized significand to + // shift as if it were normalized. This means, in practice that the + // top_most set bit will be cut off. Looks very similar to above (on purpose) + EXPECT_EQ(bits_set({}), + normalized_significand({0}, static_cast(-127))); + EXPECT_EQ(bits_set({3}), + normalized_significand({0, 4}, static_cast(-128))); + EXPECT_EQ(bits_set({3}), + normalized_significand({0, 4}, static_cast(-127))); + EXPECT_EQ(bits_set({}), + normalized_significand({22}, static_cast(-127))); + EXPECT_EQ(bits_set({0}), + normalized_significand({21, 22}, static_cast(-127))); +} + +// Returns the 32-bit floating point value created by +// calling setFromSignUnbiasedExponentAndNormalizedSignificand +// on a HexFloat> +float set_from_sign(bool negative, int32_t unbiased_exponent, + uint32_t significand, bool round_denorm_up) { + HexFloat> f(0.f); + f.setFromSignUnbiasedExponentAndNormalizedSignificand( + negative, unbiased_exponent, significand, round_denorm_up); + return f.value().getAsFloat(); +} + +TEST(HexFloatOperationTests, + SetFromSignUnbiasedExponentAndNormalizedSignificand) { + EXPECT_EQ(1.f, set_from_sign(false, 0, 0, false)); + + // Tests insertion of various denormalized numbers with and without round up. + EXPECT_EQ(static_cast(ldexp(1.f, -149)), + set_from_sign(false, -149, 0, false)); + EXPECT_EQ(static_cast(ldexp(1.f, -149)), + set_from_sign(false, -149, 0, true)); + EXPECT_EQ(0.f, set_from_sign(false, -150, 1, false)); + EXPECT_EQ(static_cast(ldexp(1.f, -149)), + set_from_sign(false, -150, 1, true)); + + EXPECT_EQ(ldexp(1.0f, -127), set_from_sign(false, -127, 0, false)); + EXPECT_EQ(ldexp(1.0f, -128), set_from_sign(false, -128, 0, false)); + EXPECT_EQ(float_fractions({0, 1, 2, 5}), + set_from_sign(false, 0, bits_set({0, 1, 4}), false)); + EXPECT_EQ(ldexp(float_fractions({0, 1, 2, 5}), -32), + set_from_sign(false, -32, bits_set({0, 1, 4}), false)); + EXPECT_EQ(ldexp(float_fractions({0, 1, 2, 5}), -128), + set_from_sign(false, -128, bits_set({0, 1, 4}), false)); + + // The negative cases from above. + EXPECT_EQ(-1.f, set_from_sign(true, 0, 0, false)); + EXPECT_EQ(-ldexp(1.0, -127), set_from_sign(true, -127, 0, false)); + EXPECT_EQ(-ldexp(1.0, -128), set_from_sign(true, -128, 0, false)); + EXPECT_EQ(-float_fractions({0, 1, 2, 5}), + set_from_sign(true, 0, bits_set({0, 1, 4}), false)); + EXPECT_EQ(-ldexp(float_fractions({0, 1, 2, 5}), -32), + set_from_sign(true, -32, bits_set({0, 1, 4}), false)); + EXPECT_EQ(-ldexp(float_fractions({0, 1, 2, 5}), -128), + set_from_sign(true, -128, bits_set({0, 1, 4}), false)); +} + +TEST(HexFloatOperationTests, NonRounding) { + // Rounding from 32-bit hex-float to 32-bit hex-float should be trivial, + // except in the denorm case which is a bit more complex. + using HF = HexFloat>; + bool carry_bit = false; + + round_direction rounding[] = {round_direction::kToZero, + round_direction::kToNearestEven, + round_direction::kToPositiveInfinity, + round_direction::kToNegativeInfinity}; + + // Everything fits, so this should be straight-forward + for (round_direction round : rounding) { + EXPECT_EQ(bits_set({}), + HF(0.f).getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + + EXPECT_EQ(bits_set({0}), + HF(float_fractions({0, 1})) + .getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + + EXPECT_EQ(bits_set({1, 3}), + HF(float_fractions({0, 2, 4})) + .getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + + EXPECT_EQ( + bits_set({0, 1, 4}), + HF(static_cast(-ldexp(float_fractions({0, 1, 2, 5}), -128))) + .getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + + EXPECT_EQ(bits_set({0, 1, 4, 22}), + HF(static_cast(float_fractions({0, 1, 2, 5, 23}))) + .getRoundedNormalizedSignificand(round, &carry_bit)); + EXPECT_FALSE(carry_bit); + } +} + +using RD = round_direction; +struct RoundSignificandCase { + float source_float; + std::pair expected_results; + round_direction round; +}; + +using HexFloatRoundTest = ::testing::TestWithParam; + +TEST_P(HexFloatRoundTest, RoundDownToFP16) { + using HF = HexFloat>; + using HF16 = HexFloat>; + + HF input_value(GetParam().source_float); + bool carry_bit = false; + EXPECT_EQ(GetParam().expected_results.first, + input_value.getRoundedNormalizedSignificand(GetParam().round, + &carry_bit)); + EXPECT_EQ(carry_bit, GetParam().expected_results.second); +} + +// clang-format off +INSTANTIATE_TEST_SUITE_P(F32ToF16, HexFloatRoundTest, + ::testing::ValuesIn(std::vector( + { + {float_fractions({0}), std::make_pair(half_bits_set({}), false), RD::kToZero}, + {float_fractions({0}), std::make_pair(half_bits_set({}), false), RD::kToNearestEven}, + {float_fractions({0}), std::make_pair(half_bits_set({}), false), RD::kToPositiveInfinity}, + {float_fractions({0}), std::make_pair(half_bits_set({}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1}), std::make_pair(half_bits_set({0}), false), RD::kToZero}, + + {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), RD::kToZero}, + {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0, 9}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 11}), std::make_pair(half_bits_set({0}), false), RD::kToNearestEven}, + + {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 9}), false), RD::kToZero}, + {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 8}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 10, 11}), std::make_pair(half_bits_set({0, 8}), false), RD::kToNearestEven}, + + {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), RD::kToZero}, + {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven}, + + {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), RD::kToZero}, + {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0}), false), RD::kToPositiveInfinity}, + {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNegativeInfinity}, + {-float_fractions({0, 1, 11, 12}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven}, + + {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0}), false), RD::kToZero}, + {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0, 9}), false), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 11, 22}), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven}, + + // Carries + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), false), RD::kToZero}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({}), true), RD::kToPositiveInfinity}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}), false), RD::kToNegativeInfinity}, + {float_fractions({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}), std::make_pair(half_bits_set({}), true), RD::kToNearestEven}, + + // Cases where original number was denorm. Note: this should have no effect + // the number is pre-normalized. + {static_cast(ldexp(float_fractions({0, 1, 11, 13}), -128)), std::make_pair(half_bits_set({0}), false), RD::kToZero}, + {static_cast(ldexp(float_fractions({0, 1, 11, 13}), -129)), std::make_pair(half_bits_set({0, 9}), false), RD::kToPositiveInfinity}, + {static_cast(ldexp(float_fractions({0, 1, 11, 13}), -131)), std::make_pair(half_bits_set({0}), false), RD::kToNegativeInfinity}, + {static_cast(ldexp(float_fractions({0, 1, 11, 13}), -130)), std::make_pair(half_bits_set({0, 9}), false), RD::kToNearestEven}, + }))); +// clang-format on + +struct UpCastSignificandCase { + uint16_t source_half; + uint32_t expected_result; +}; + +using HexFloatRoundUpSignificandTest = + ::testing::TestWithParam; +TEST_P(HexFloatRoundUpSignificandTest, Widening) { + using HF = HexFloat>; + using HF16 = HexFloat>; + bool carry_bit = false; + + round_direction rounding[] = {round_direction::kToZero, + round_direction::kToNearestEven, + round_direction::kToPositiveInfinity, + round_direction::kToNegativeInfinity}; + + // Everything fits, so everything should just be bit-shifts. + for (round_direction round : rounding) { + carry_bit = false; + HF16 input_value(GetParam().source_half); + EXPECT_EQ( + GetParam().expected_result, + input_value.getRoundedNormalizedSignificand(round, &carry_bit)) + << std::hex << "0x" + << input_value.getRoundedNormalizedSignificand(round, &carry_bit) + << " 0x" << GetParam().expected_result; + EXPECT_FALSE(carry_bit); + } +} + +INSTANTIATE_TEST_SUITE_P( + F16toF32, HexFloatRoundUpSignificandTest, + // 0xFC00 of the source 16-bit hex value cover the sign and the exponent. + // They are ignored for this test. + ::testing::ValuesIn(std::vector({ + {0x3F00, 0x600000}, + {0x0F00, 0x600000}, + {0x0F01, 0x602000}, + {0x0FFF, 0x7FE000}, + }))); + +struct DownCastTest { + float source_float; + uint16_t expected_half; + std::vector directions; +}; + +std::string get_round_text(round_direction direction) { +#define CASE(round_direction) \ + case round_direction: \ + return #round_direction + + switch (direction) { + CASE(round_direction::kToZero); + CASE(round_direction::kToPositiveInfinity); + CASE(round_direction::kToNegativeInfinity); + CASE(round_direction::kToNearestEven); + } +#undef CASE + return ""; +} + +using HexFloatFP32To16Tests = ::testing::TestWithParam; + +TEST_P(HexFloatFP32To16Tests, NarrowingCasts) { + using HF = HexFloat>; + using HF16 = HexFloat>; + HF f(GetParam().source_float); + for (auto round : GetParam().directions) { + HF16 half(0); + f.castTo(half, round); + EXPECT_EQ(GetParam().expected_half, half.value().getAsFloat().get_value()) + << get_round_text(round) << " " << std::hex + << BitwiseCast(GetParam().source_float) + << " cast to: " << half.value().getAsFloat().get_value(); + } +} + +const uint16_t positive_infinity = 0x7C00; +const uint16_t negative_infinity = 0xFC00; + +INSTANTIATE_TEST_SUITE_P( + F32ToF16, HexFloatFP32To16Tests, + ::testing::ValuesIn(std::vector({ + // Exactly representable as half. + {0.f, + 0x0, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-0.f, + 0x8000, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {1.0f, + 0x3C00, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-1.0f, + 0xBC00, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + {float_fractions({0, 1, 10}), + 0x3E01, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-float_fractions({0, 1, 10}), + 0xBE01, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast(ldexp(float_fractions({0, 1, 10}), 3)), + 0x4A01, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast(-ldexp(float_fractions({0, 1, 10}), 3)), + 0xCA01, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Underflow + {static_cast(ldexp(1.0f, -25)), + 0x0, + {RD::kToZero, RD::kToNegativeInfinity, RD::kToNearestEven}}, + {static_cast(ldexp(1.0f, -25)), 0x1, {RD::kToPositiveInfinity}}, + {static_cast(-ldexp(1.0f, -25)), + 0x8000, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNearestEven}}, + {static_cast(-ldexp(1.0f, -25)), + 0x8001, + {RD::kToNegativeInfinity}}, + {static_cast(ldexp(1.0f, -24)), + 0x1, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Overflow + {static_cast(ldexp(1.0f, 16)), + positive_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast(ldexp(1.0f, 18)), + positive_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast(ldexp(1.3f, 16)), + positive_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast(-ldexp(1.0f, 16)), + negative_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast(-ldexp(1.0f, 18)), + negative_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {static_cast(-ldexp(1.3f, 16)), + negative_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Transfer of Infinities + {std::numeric_limits::infinity(), + positive_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + {-std::numeric_limits::infinity(), + negative_infinity, + {RD::kToZero, RD::kToPositiveInfinity, RD::kToNegativeInfinity, + RD::kToNearestEven}}, + + // Nans are below because we cannot test for equality. + }))); + +struct UpCastCase { + uint16_t source_half; + float expected_float; +}; + +using HexFloatFP16To32Tests = ::testing::TestWithParam; +TEST_P(HexFloatFP16To32Tests, WideningCasts) { + using HF = HexFloat>; + using HF16 = HexFloat>; + HF16 f(GetParam().source_half); + + round_direction rounding[] = {round_direction::kToZero, + round_direction::kToNearestEven, + round_direction::kToPositiveInfinity, + round_direction::kToNegativeInfinity}; + + // Everything fits, so everything should just be bit-shifts. + for (round_direction round : rounding) { + HF flt(0.f); + f.castTo(flt, round); + EXPECT_EQ(GetParam().expected_float, flt.value().getAsFloat()) + << get_round_text(round) << " " << std::hex + << BitwiseCast(GetParam().source_half) + << " cast to: " << flt.value().getAsFloat(); + } +} + +INSTANTIATE_TEST_SUITE_P( + F16ToF32, HexFloatFP16To32Tests, + ::testing::ValuesIn(std::vector({ + {0x0000, 0.f}, + {0x8000, -0.f}, + {0x3C00, 1.0f}, + {0xBC00, -1.0f}, + {0x3F00, float_fractions({0, 1, 2})}, + {0xBF00, -float_fractions({0, 1, 2})}, + {0x3F01, float_fractions({0, 1, 2, 10})}, + {0xBF01, -float_fractions({0, 1, 2, 10})}, + + // denorm + {0x0001, static_cast(ldexp(1.0, -24))}, + {0x0002, static_cast(ldexp(1.0, -23))}, + {0x8001, static_cast(-ldexp(1.0, -24))}, + {0x8011, static_cast(-ldexp(1.0, -20) + -ldexp(1.0, -24))}, + + // inf + {0x7C00, std::numeric_limits::infinity()}, + {0xFC00, -std::numeric_limits::infinity()}, + }))); + +TEST(HexFloatOperationTests, NanTests) { + using HF = HexFloat>; + using HF16 = HexFloat>; + round_direction rounding[] = {round_direction::kToZero, + round_direction::kToNearestEven, + round_direction::kToPositiveInfinity, + round_direction::kToNegativeInfinity}; + + // Everything fits, so everything should just be bit-shifts. + for (round_direction round : rounding) { + HF16 f16(0); + HF f(0.f); + HF(std::numeric_limits::quiet_NaN()).castTo(f16, round); + EXPECT_TRUE(f16.value().isNan()); + HF(std::numeric_limits::signaling_NaN()).castTo(f16, round); + EXPECT_TRUE(f16.value().isNan()); + + HF16(0x7C01).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + HF16(0x7C11).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + HF16(0xFC01).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + HF16(0x7C10).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + HF16(0xFF00).castTo(f, round); + EXPECT_TRUE(f.value().isNan()); + } +} + +// A test case for parsing good and bad HexFloat> literals. +template +struct FloatParseCase { + std::string literal; + bool negate_value; + bool expect_success; + HexFloat> expected_value; +}; + +using ParseNormalFloatTest = ::testing::TestWithParam>; + +TEST_P(ParseNormalFloatTest, Samples) { + std::stringstream input(GetParam().literal); + HexFloat> parsed_value(0.0f); + ParseNormalFloat(input, GetParam().negate_value, parsed_value); + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().literal + << " negate: " << GetParam().negate_value; + if (GetParam().expect_success) { + EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value())) + << " literal: " << GetParam().literal + << " negate: " << GetParam().negate_value; + } +} + +// Returns a FloatParseCase with expected failure. +template +FloatParseCase BadFloatParseCase(std::string literal, bool negate_value, + T expected_value) { + HexFloat> proxy_expected_value(expected_value); + return FloatParseCase{literal, negate_value, false, proxy_expected_value}; +} + +// Returns a FloatParseCase that should successfully parse to a given value. +template +FloatParseCase GoodFloatParseCase(std::string literal, bool negate_value, + T expected_value) { + HexFloat> proxy_expected_value(expected_value); + return FloatParseCase{literal, negate_value, true, proxy_expected_value}; +} + +INSTANTIATE_TEST_SUITE_P( + FloatParse, ParseNormalFloatTest, + ::testing::ValuesIn(std::vector>{ + // Failing cases due to trivially incorrect syntax. + BadFloatParseCase("abc", false, 0.0f), + BadFloatParseCase("abc", true, 0.0f), + + // Valid cases. + GoodFloatParseCase("0", false, 0.0f), + GoodFloatParseCase("0.0", false, 0.0f), + GoodFloatParseCase("-0.0", false, -0.0f), + GoodFloatParseCase("2.0", false, 2.0f), + GoodFloatParseCase("-2.0", false, -2.0f), + GoodFloatParseCase("+2.0", false, 2.0f), + // Cases with negate_value being true. + GoodFloatParseCase("0.0", true, -0.0f), + GoodFloatParseCase("2.0", true, -2.0f), + + // When negate_value is true, we should not accept a + // leading minus or plus. + BadFloatParseCase("-0.0", true, 0.0f), + BadFloatParseCase("-2.0", true, 0.0f), + BadFloatParseCase("+0.0", true, 0.0f), + BadFloatParseCase("+2.0", true, 0.0f), + + // Overflow is an error for 32-bit float parsing. + BadFloatParseCase("1e40", false, FLT_MAX), + BadFloatParseCase("1e40", true, -FLT_MAX), + BadFloatParseCase("-1e40", false, -FLT_MAX), + // We can't have -1e40 and negate_value == true since + // that represents an original case of "--1e40" which + // is invalid. + })); + +using ParseNormalFloat16Test = + ::testing::TestWithParam>; + +TEST_P(ParseNormalFloat16Test, Samples) { + std::stringstream input(GetParam().literal); + HexFloat> parsed_value(0); + ParseNormalFloat(input, GetParam().negate_value, parsed_value); + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().literal + << " negate: " << GetParam().negate_value; + if (GetParam().expect_success) { + EXPECT_THAT(parsed_value.value(), Eq(GetParam().expected_value.value())) + << " literal: " << GetParam().literal + << " negate: " << GetParam().negate_value; + } +} + +INSTANTIATE_TEST_SUITE_P( + Float16Parse, ParseNormalFloat16Test, + ::testing::ValuesIn(std::vector>{ + // Failing cases due to trivially incorrect syntax. + BadFloatParseCase("abc", false, uint16_t{0}), + BadFloatParseCase("abc", true, uint16_t{0}), + + // Valid cases. + GoodFloatParseCase("0", false, uint16_t{0}), + GoodFloatParseCase("0.0", false, uint16_t{0}), + GoodFloatParseCase("-0.0", false, uint16_t{0x8000}), + GoodFloatParseCase("2.0", false, uint16_t{0x4000}), + GoodFloatParseCase("-2.0", false, uint16_t{0xc000}), + GoodFloatParseCase("+2.0", false, uint16_t{0x4000}), + // Cases with negate_value being true. + GoodFloatParseCase("0.0", true, uint16_t{0x8000}), + GoodFloatParseCase("2.0", true, uint16_t{0xc000}), + + // When negate_value is true, we should not accept a leading minus or + // plus. + BadFloatParseCase("-0.0", true, uint16_t{0}), + BadFloatParseCase("-2.0", true, uint16_t{0}), + BadFloatParseCase("+0.0", true, uint16_t{0}), + BadFloatParseCase("+2.0", true, uint16_t{0}), + })); + +// A test case for detecting infinities. +template +struct OverflowParseCase { + std::string input; + bool expect_success; + T expected_value; +}; + +using FloatProxyParseOverflowFloatTest = + ::testing::TestWithParam>; + +TEST_P(FloatProxyParseOverflowFloatTest, Sample) { + std::istringstream input(GetParam().input); + HexFloat> value(0.0f); + input >> value; + EXPECT_NE(GetParam().expect_success, input.fail()); + if (GetParam().expect_success) { + EXPECT_THAT(value.value().getAsFloat(), GetParam().expected_value); + } +} + +INSTANTIATE_TEST_SUITE_P( + FloatOverflow, FloatProxyParseOverflowFloatTest, + ::testing::ValuesIn(std::vector>({ + {"0", true, 0.0f}, + {"0.0", true, 0.0f}, + {"1.0", true, 1.0f}, + {"1e38", true, 1e38f}, + {"-1e38", true, -1e38f}, + {"1e40", false, FLT_MAX}, + {"-1e40", false, -FLT_MAX}, + {"1e400", false, FLT_MAX}, + {"-1e400", false, -FLT_MAX}, + }))); + +using FloatProxyParseOverflowDoubleTest = + ::testing::TestWithParam>; + +TEST_P(FloatProxyParseOverflowDoubleTest, Sample) { + std::istringstream input(GetParam().input); + HexFloat> value(0.0); + input >> value; + EXPECT_NE(GetParam().expect_success, input.fail()); + if (GetParam().expect_success) { + EXPECT_THAT(value.value().getAsFloat(), Eq(GetParam().expected_value)); + } +} + +INSTANTIATE_TEST_SUITE_P( + DoubleOverflow, FloatProxyParseOverflowDoubleTest, + ::testing::ValuesIn(std::vector>({ + {"0", true, 0.0}, + {"0.0", true, 0.0}, + {"1.0", true, 1.0}, + {"1e38", true, 1e38}, + {"-1e38", true, -1e38}, + {"1e40", true, 1e40}, + {"-1e40", true, -1e40}, + {"1e400", false, DBL_MAX}, + {"-1e400", false, -DBL_MAX}, + }))); + +using FloatProxyParseOverflowFloat16Test = + ::testing::TestWithParam>; + +TEST_P(FloatProxyParseOverflowFloat16Test, Sample) { + std::istringstream input(GetParam().input); + HexFloat> value(0); + input >> value; + EXPECT_NE(GetParam().expect_success, input.fail()) + << " literal: " << GetParam().input; + if (GetParam().expect_success) { + EXPECT_THAT(value.value().data(), Eq(GetParam().expected_value)) + << " literal: " << GetParam().input; + } +} + +INSTANTIATE_TEST_SUITE_P( + Float16Overflow, FloatProxyParseOverflowFloat16Test, + ::testing::ValuesIn(std::vector>({ + {"0", true, uint16_t{0}}, + {"0.0", true, uint16_t{0}}, + {"1.0", true, uint16_t{0x3c00}}, + // Overflow for 16-bit float is an error, and returns max or + // lowest value. + {"1e38", false, uint16_t{0x7bff}}, + {"1e40", false, uint16_t{0x7bff}}, + {"1e400", false, uint16_t{0x7bff}}, + {"-1e38", false, uint16_t{0xfbff}}, + {"-1e40", false, uint16_t{0xfbff}}, + {"-1e400", false, uint16_t{0xfbff}}, + }))); + +TEST(FloatProxy, Max) { + EXPECT_THAT(FloatProxy::max().getAsFloat().get_value(), + Eq(uint16_t{0x7bff})); + EXPECT_THAT(FloatProxy::max().getAsFloat(), + Eq(std::numeric_limits::max())); + EXPECT_THAT(FloatProxy::max().getAsFloat(), + Eq(std::numeric_limits::max())); +} + +TEST(FloatProxy, Lowest) { + EXPECT_THAT(FloatProxy::lowest().getAsFloat().get_value(), + Eq(uint16_t{0xfbff})); + EXPECT_THAT(FloatProxy::lowest().getAsFloat(), + Eq(std::numeric_limits::lowest())); + EXPECT_THAT(FloatProxy::lowest().getAsFloat(), + Eq(std::numeric_limits::lowest())); +} + +// TODO(awoloszyn): Add fp16 tests and HexFloatTraits. +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/immediate_int_test.cpp b/third_party/spirv-tools/test/immediate_int_test.cpp new file mode 100644 index 0000000..393075a --- /dev/null +++ b/third_party/spirv-tools/test/immediate_int_test.cpp @@ -0,0 +1,291 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "gmock/gmock.h" +#include "source/util/bitutils.h" +#include "test/test_fixture.h" + +namespace spvtools { +namespace utils { +namespace { + +using spvtest::Concatenate; +using spvtest::MakeInstruction; +using spvtest::ScopedContext; +using spvtest::TextToBinaryTest; +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::StrEq; + +TEST_F(TextToBinaryTest, ImmediateIntOpCode) { + SetText("!0x00FF00FF"); + ASSERT_EQ(SPV_SUCCESS, spvTextToBinary(ScopedContext().context, text.str, + text.length, &binary, &diagnostic)); + EXPECT_EQ(0x00FF00FFu, binary->code[5]); + if (diagnostic) { + spvDiagnosticPrint(diagnostic); + } +} + +TEST_F(TextToBinaryTest, ImmediateIntOperand) { + SetText("OpCapability !0x00FF00FF"); + EXPECT_EQ(SPV_SUCCESS, spvTextToBinary(ScopedContext().context, text.str, + text.length, &binary, &diagnostic)); + EXPECT_EQ(0x00FF00FFu, binary->code[6]); + if (diagnostic) { + spvDiagnosticPrint(diagnostic); + } +} + +using ImmediateIntTest = TextToBinaryTest; + +TEST_F(ImmediateIntTest, AnyWordInSimpleStatement) { + EXPECT_THAT(CompiledInstructions("!0x00040018 %a %b %123"), + Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 3}))); + EXPECT_THAT(CompiledInstructions("!0x00040018 !1 %b %123"), + Eq(MakeInstruction(SpvOpTypeMatrix, {1, 1, 2}))); + EXPECT_THAT(CompiledInstructions("%a = OpTypeMatrix !2 %123"), + Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 2}))); + EXPECT_THAT(CompiledInstructions("%a = OpTypeMatrix %b !123"), + Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 123}))); + EXPECT_THAT(CompiledInstructions("!0x00040018 %a !2 %123"), + Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 2}))); + EXPECT_THAT(CompiledInstructions("!0x00040018 !1 %b !123"), + Eq(MakeInstruction(SpvOpTypeMatrix, {1, 1, 123}))); + EXPECT_THAT(CompiledInstructions("!0x00040018 !1 !2 !123"), + Eq(MakeInstruction(SpvOpTypeMatrix, {1, 2, 123}))); +} + +TEST_F(ImmediateIntTest, AnyWordAfterEqualsAndOpCode) { + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 %c 123"), + Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 2, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 123"), + Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b %c !123"), + Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength %b !3 !123"), + Eq(MakeInstruction(SpvOpArrayLength, {1, 2, 3, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 123"), + Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 3, 123}))); + EXPECT_THAT(CompiledInstructions("%a = OpArrayLength !2 !3 !123"), + Eq(MakeInstruction(SpvOpArrayLength, {2, 1, 3, 123}))); +} + +TEST_F(ImmediateIntTest, ResultIdInAssignment) { + EXPECT_EQ("!2 not allowed before =.", + CompileFailure("!2 = OpArrayLength %12 %1 123")); + EXPECT_EQ("!2 not allowed before =.", + CompileFailure("!2 = !0x00040044 %12 %1 123")); +} + +TEST_F(ImmediateIntTest, OpCodeInAssignment) { + EXPECT_EQ("Invalid Opcode prefix '!0x00040044'.", + CompileFailure("%2 = !0x00040044 %12 %1 123")); +} + +// Literal integers after ! are handled correctly. +TEST_F(ImmediateIntTest, IntegerFollowingImmediate) { + const SpirvVector original = CompiledInstructions("%1 = OpTypeInt 8 1"); + EXPECT_EQ(original, CompiledInstructions("!0x00040015 1 8 1")); + EXPECT_EQ(original, CompiledInstructions("!0x00040015 !1 8 1")); + + // With !, we can (and can only) accept 32-bit number literals, + // even when we declare the return type is 64-bit. + EXPECT_EQ(Concatenate({ + MakeInstruction(SpvOpTypeInt, {1, 64, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 4294967295}), + }), + CompiledInstructions("%i64 = OpTypeInt 64 0\n" + "!0x0004002b %i64 !2 4294967295")); + // 64-bit integer literal. + EXPECT_EQ("Invalid word following !: 5000000000", + CompileFailure("%2 = OpConstant !1 5000000000")); + EXPECT_EQ("Invalid word following !: 5000000000", + CompileFailure("%i64 = OpTypeInt 64 0\n" + "!0x0005002b %i64 !2 5000000000")); + + // Negative integer. + EXPECT_EQ(CompiledInstructions("%i64 = OpTypeInt 32 1\n" + "%2 = OpConstant %i64 -123"), + CompiledInstructions("%i64 = OpTypeInt 32 1\n" + "!0x0004002b %i64 !2 -123")); + + // TODO(deki): uncomment assertions below and make them pass. + // Hex value(s). + // EXPECT_EQ(CompileSuccessfully("%1 = OpConstant %10 0x12345678"), + // CompileSuccessfully("OpConstant %10 !1 0x12345678", kCAF)); + // EXPECT_EQ( + // CompileSuccessfully("%1 = OpConstant %10 0x12345678 0x87654321"), + // CompileSuccessfully("OpConstant %10 !1 0x12345678 0x87654321", kCAF)); +} + +// Literal floats after ! are handled correctly. +TEST_F(ImmediateIntTest, FloatFollowingImmediate) { + EXPECT_EQ( + CompiledInstructions("%1 = OpTypeFloat 32\n%2 = OpConstant %1 0.123"), + CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 !2 0.123")); + EXPECT_EQ( + CompiledInstructions("%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0.5"), + CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 !2 -0.5")); + EXPECT_EQ( + CompiledInstructions("%1 = OpTypeFloat 32\n%2 = OpConstant %1 0.123"), + CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 %2 0.123")); + EXPECT_EQ( + CompiledInstructions("%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0.5"), + CompiledInstructions("%1 = OpTypeFloat 32\n!0x0004002b %1 %2 -0.5")); + + EXPECT_EQ(Concatenate({ + MakeInstruction(SpvOpTypeInt, {1, 64, 0}), + MakeInstruction(SpvOpConstant, {1, 2, 0xb, 0xa}), + MakeInstruction(SpvOpSwitch, + {2, 1234, BitwiseCast(2.5f), 3}), + }), + CompiledInstructions("%i64 = OpTypeInt 64 0\n" + "%big = OpConstant %i64 0xa0000000b\n" + "OpSwitch %big !1234 2.5 %target\n")); +} + +// Literal strings after ! are handled correctly. +TEST_F(ImmediateIntTest, StringFollowingImmediate) { + // Try a variety of strings, including empty and single-character. + for (std::string name : {"", "s", "longish", "really looooooooooooooooong"}) { + const SpirvVector original = + CompiledInstructions("OpMemberName %10 4 \"" + name + "\""); + EXPECT_EQ(original, + CompiledInstructions("OpMemberName %10 !4 \"" + name + "\"")) + << name; + EXPECT_EQ(original, + CompiledInstructions("OpMemberName !1 !4 \"" + name + "\"")) + << name; + const uint16_t wordCount = static_cast(4 + name.size() / 4); + const uint32_t firstWord = spvOpcodeMake(wordCount, SpvOpMemberName); + EXPECT_EQ(original, CompiledInstructions("!" + std::to_string(firstWord) + + " %10 !4 \"" + name + "\"")) + << name; + } +} + +// IDs after ! are handled correctly. +TEST_F(ImmediateIntTest, IdFollowingImmediate) { + EXPECT_EQ(CompileSuccessfully("%123 = OpDecorationGroup"), + CompileSuccessfully("!0x00020049 %123")); + EXPECT_EQ(CompileSuccessfully("%group = OpDecorationGroup"), + CompileSuccessfully("!0x00020049 %group")); +} + +// ! after ! is handled correctly. +TEST_F(ImmediateIntTest, ImmediateFollowingImmediate) { + const SpirvVector original = CompiledInstructions("%a = OpTypeMatrix %b 7"); + EXPECT_EQ(original, CompiledInstructions("%a = OpTypeMatrix !2 !7")); + EXPECT_EQ(original, CompiledInstructions("!0x00040018 %a !2 !7")); +} + +TEST_F(ImmediateIntTest, InvalidStatement) { + EXPECT_THAT(Subvector(CompileSuccessfully("!4 !3 !2 !1"), kFirstInstruction), + ElementsAre(4, 3, 2, 1)); +} + +TEST_F(ImmediateIntTest, InvalidStatementBetweenValidOnes) { + EXPECT_THAT(Subvector(CompileSuccessfully( + "%10 = OpTypeFloat 32 !5 !6 !7 OpEmitVertex"), + kFirstInstruction), + ElementsAre(spvOpcodeMake(3, SpvOpTypeFloat), 1, 32, 5, 6, 7, + spvOpcodeMake(1, SpvOpEmitVertex))); +} + +TEST_F(ImmediateIntTest, NextOpcodeRecognized) { + const SpirvVector original = CompileSuccessfully(R"( +%1 = OpLoad %10 %2 Volatile +%4 = OpCompositeInsert %11 %1 %3 0 1 2 +)"); + const SpirvVector alternate = CompileSuccessfully(R"( +%1 = OpLoad %10 %2 !1 +%4 = OpCompositeInsert %11 %1 %3 0 1 2 +)"); + EXPECT_EQ(original, alternate); +} + +TEST_F(ImmediateIntTest, WrongLengthButNextOpcodeStillRecognized) { + const SpirvVector original = CompileSuccessfully(R"( +%1 = OpLoad %10 %2 Volatile +OpCopyMemorySized %3 %4 %1 +)"); + const SpirvVector alternate = CompileSuccessfully(R"( +!0x0002003D %10 %1 %2 !1 +OpCopyMemorySized %3 %4 %1 +)"); + EXPECT_EQ(0x0002003Du, alternate[kFirstInstruction]); + EXPECT_EQ(Subvector(original, kFirstInstruction + 1), + Subvector(alternate, kFirstInstruction + 1)); +} + +// Like NextOpcodeRecognized, but next statement is in assignment form. +TEST_F(ImmediateIntTest, NextAssignmentRecognized) { + const SpirvVector original = CompileSuccessfully(R"( +%1 = OpLoad %10 %2 None +%4 = OpFunctionCall %10 %3 %123 +)"); + const SpirvVector alternate = CompileSuccessfully(R"( +%1 = OpLoad %10 %2 !0 +%4 = OpFunctionCall %10 %3 %123 +)"); + EXPECT_EQ(original, alternate); +} + +// Two instructions in a row each have ! opcode. +TEST_F(ImmediateIntTest, ConsecutiveImmediateOpcodes) { + const SpirvVector original = CompileSuccessfully(R"( +%1 = OpConstantSampler %10 Clamp 78 Linear +%4 = OpFRem %11 %3 %2 +%5 = OpIsValidEvent %12 %2 +)"); + const SpirvVector alternate = CompileSuccessfully(R"( +!0x0006002D %10 %1 !2 78 !1 +!0x0005008C %11 %4 %3 %2 +%5 = OpIsValidEvent %12 %2 +)"); + EXPECT_EQ(original, alternate); +} + +// ! followed by, eg, an enum or '=' or a random bareword. +TEST_F(ImmediateIntTest, ForbiddenOperands) { + EXPECT_THAT(CompileFailure("OpMemoryModel !0 OpenCL"), HasSubstr("OpenCL")); + EXPECT_THAT(CompileFailure("!1 %0 = !2"), HasSubstr("=")); + EXPECT_THAT(CompileFailure("OpMemoryModel !0 random_bareword"), + HasSubstr("random_bareword")); + // Immediate integers longer than one 32-bit word. + EXPECT_THAT(CompileFailure("!5000000000"), HasSubstr("5000000000")); + EXPECT_THAT(CompileFailure("!999999999999999999"), + HasSubstr("999999999999999999")); + EXPECT_THAT(CompileFailure("!0x00020049 !5000000000"), + HasSubstr("5000000000")); + // Negative numbers. + EXPECT_THAT(CompileFailure("!0x00020049 !-123"), HasSubstr("-123")); +} + +TEST_F(ImmediateIntTest, NotInteger) { + EXPECT_THAT(CompileFailure("!abc"), StrEq("Invalid immediate integer: !abc")); + EXPECT_THAT(CompileFailure("!12.3"), + StrEq("Invalid immediate integer: !12.3")); + EXPECT_THAT(CompileFailure("!12K"), StrEq("Invalid immediate integer: !12K")); +} + +} // namespace +} // namespace utils +} // namespace spvtools diff --git a/third_party/spirv-tools/test/libspirv_macros_test.cpp b/third_party/spirv-tools/test/libspirv_macros_test.cpp new file mode 100644 index 0000000..bf5add6 --- /dev/null +++ b/third_party/spirv-tools/test/libspirv_macros_test.cpp @@ -0,0 +1,25 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +TEST(Macros, BitShiftInnerParens) { ASSERT_EQ(65536, SPV_BIT(2 << 3)); } + +TEST(Macros, BitShiftOuterParens) { ASSERT_EQ(15, SPV_BIT(4) - 1); } + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/CMakeLists.txt b/third_party/spirv-tools/test/link/CMakeLists.txt new file mode 100644 index 0000000..ee41b91 --- /dev/null +++ b/third_party/spirv-tools/test/link/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2017 Pierre Moreau +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +add_spvtools_unittest(TARGET link + SRCS + binary_version_test.cpp + entry_points_test.cpp + global_values_amount_test.cpp + ids_limit_test.cpp + matching_imports_to_exports_test.cpp + memory_model_test.cpp + partial_linkage_test.cpp + unique_ids_test.cpp + type_match_test.cpp + LIBS SPIRV-Tools-opt SPIRV-Tools-link +) diff --git a/third_party/spirv-tools/test/link/binary_version_test.cpp b/third_party/spirv-tools/test/link/binary_version_test.cpp new file mode 100644 index 0000000..80aab0f --- /dev/null +++ b/third_party/spirv-tools/test/link/binary_version_test.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using BinaryVersion = spvtest::LinkerTest; + +TEST_F(BinaryVersion, LinkerChoosesMaxSpirvVersion) { + // clang-format off + spvtest::Binaries binaries = { + { + SpvMagicNumber, + 0x00010300u, + SPV_GENERATOR_CODEPLAY, + 1u, // NOTE: Bound + 0u // NOTE: Schema; reserved + }, + { + SpvMagicNumber, + 0x00010500u, + SPV_GENERATOR_CODEPLAY, + 1u, // NOTE: Bound + 0u // NOTE: Schema; reserved + }, + { + SpvMagicNumber, + 0x00010100u, + SPV_GENERATOR_CODEPLAY, + 1u, // NOTE: Bound + 0u // NOTE: Schema; reserved + } + }; + // clang-format on + spvtest::Binary linked_binary; + + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), std::string()); + + EXPECT_EQ(0x00010500u, linked_binary[1]); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/entry_points_test.cpp b/third_party/spirv-tools/test/link/entry_points_test.cpp new file mode 100644 index 0000000..bac8e02 --- /dev/null +++ b/third_party/spirv-tools/test/link/entry_points_test.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using ::testing::HasSubstr; + +class EntryPoints : public spvtest::LinkerTest {}; + +TEST_F(EntryPoints, SameModelDifferentName) { + const std::string body1 = R"( +OpEntryPoint GLCompute %3 "foo" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +OpFunctionEnd +)"; + const std::string body2 = R"( +OpEntryPoint GLCompute %3 "bar" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +OpFunctionEnd +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), std::string()); +} + +TEST_F(EntryPoints, DifferentModelSameName) { + const std::string body1 = R"( +OpEntryPoint GLCompute %3 "foo" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +OpFunctionEnd +)"; + const std::string body2 = R"( +OpEntryPoint Vertex %3 "foo" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +OpFunctionEnd +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), std::string()); +} + +TEST_F(EntryPoints, SameModelAndName) { + const std::string body1 = R"( +OpEntryPoint GLCompute %3 "foo" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +OpFunctionEnd +)"; + const std::string body2 = R"( +OpEntryPoint GLCompute %3 "foo" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpFunction %1 None %2 +OpFunctionEnd +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INTERNAL, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("The entry point \"foo\", with execution model " + "GLCompute, was already defined.")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/global_values_amount_test.cpp b/third_party/spirv-tools/test/link/global_values_amount_test.cpp new file mode 100644 index 0000000..2c4ee1f --- /dev/null +++ b/third_party/spirv-tools/test/link/global_values_amount_test.cpp @@ -0,0 +1,153 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using ::testing::HasSubstr; + +class EntryPointsAmountTest : public spvtest::LinkerTest { + public: + EntryPointsAmountTest() { binaries.reserve(0xFFFF); } + + void SetUp() override { + binaries.push_back({SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_CODEPLAY, + 10u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + 3u << SpvWordCountShift | SpvOpTypeFloat, + 1u, // NOTE: Result ID + 32u, // NOTE: Width + + 4u << SpvWordCountShift | SpvOpTypePointer, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u, // NOTE: Type ID + + 2u << SpvWordCountShift | SpvOpTypeVoid, + 3u, // NOTE: Result ID + + 3u << SpvWordCountShift | SpvOpTypeFunction, + 4u, // NOTE: Result ID + 3u, // NOTE: Return type + + 5u << SpvWordCountShift | SpvOpFunction, + 3u, // NOTE: Result type + 5u, // NOTE: Result ID + SpvFunctionControlMaskNone, + 4u, // NOTE: Function type + + 2u << SpvWordCountShift | SpvOpLabel, + 6u, // NOTE: Result ID + + 4u << SpvWordCountShift | SpvOpVariable, + 2u, // NOTE: Type ID + 7u, // NOTE: Result ID + SpvStorageClassFunction, + + 4u << SpvWordCountShift | SpvOpVariable, + 2u, // NOTE: Type ID + 8u, // NOTE: Result ID + SpvStorageClassFunction, + + 4u << SpvWordCountShift | SpvOpVariable, + 2u, // NOTE: Type ID + 9u, // NOTE: Result ID + SpvStorageClassFunction, + + 1u << SpvWordCountShift | SpvOpReturn, + + 1u << SpvWordCountShift | SpvOpFunctionEnd}); + for (size_t i = 0u; i < 2u; ++i) { + spvtest::Binary binary = { + SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_CODEPLAY, + 103u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + 3u << SpvWordCountShift | SpvOpTypeFloat, + 1u, // NOTE: Result ID + 32u, // NOTE: Width + + 4u << SpvWordCountShift | SpvOpTypePointer, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u // NOTE: Type ID + }; + + for (uint32_t j = 0u; j < 0xFFFFu / 2u; ++j) { + binary.push_back(4u << SpvWordCountShift | SpvOpVariable); + binary.push_back(2u); // NOTE: Type ID + binary.push_back(j + 3u); // NOTE: Result ID + binary.push_back(SpvStorageClassInput); + } + binaries.push_back(binary); + } + } + void TearDown() override { binaries.clear(); } + + spvtest::Binaries binaries; +}; + +TEST_F(EntryPointsAmountTest, UnderLimit) { + spvtest::Binary linked_binary; + + EXPECT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), std::string()); +} + +TEST_F(EntryPointsAmountTest, OverLimit) { + binaries.push_back({SpvMagicNumber, + SpvVersion, + SPV_GENERATOR_CODEPLAY, + 5u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + + 3u << SpvWordCountShift | SpvOpTypeFloat, + 1u, // NOTE: Result ID + 32u, // NOTE: Width + + 4u << SpvWordCountShift | SpvOpTypePointer, + 2u, // NOTE: Result ID + SpvStorageClassInput, + 1u, // NOTE: Type ID + + 4u << SpvWordCountShift | SpvOpVariable, + 2u, // NOTE: Type ID + 3u, // NOTE: Result ID + SpvStorageClassInput, + + 4u << SpvWordCountShift | SpvOpVariable, + 2u, // NOTE: Type ID + 4u, // NOTE: Result ID + SpvStorageClassInput}); + + spvtest::Binary linked_binary; + + EXPECT_EQ(SPV_ERROR_INTERNAL, Link(binaries, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("The limit of global values, 65535, was exceeded; " + "65536 global values were found.")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/ids_limit_test.cpp b/third_party/spirv-tools/test/link/ids_limit_test.cpp new file mode 100644 index 0000000..6d7815a --- /dev/null +++ b/third_party/spirv-tools/test/link/ids_limit_test.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using ::testing::HasSubstr; +using IdsLimit = spvtest::LinkerTest; + +TEST_F(IdsLimit, UnderLimit) { + spvtest::Binaries binaries = { + { + SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, + 0x2FFFFFu, // NOTE: Bound + 0u, // NOTE: Schema; reserved + }, + { + SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, + 0x100000u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + }}; + spvtest::Binary linked_binary; + + ASSERT_EQ(SPV_SUCCESS, Link(binaries, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), std::string()); + EXPECT_EQ(0x3FFFFEu, linked_binary[3]); +} + +TEST_F(IdsLimit, OverLimit) { + spvtest::Binaries binaries = { + { + SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, + 0x2FFFFFu, // NOTE: Bound + 0u, // NOTE: Schema; reserved + }, + { + SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, + 0x100000u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + }, + { + SpvMagicNumber, SpvVersion, SPV_GENERATOR_CODEPLAY, + 3u, // NOTE: Bound + 0u, // NOTE: Schema; reserved + }}; + + spvtest::Binary linked_binary; + + EXPECT_EQ(SPV_ERROR_INVALID_ID, Link(binaries, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("The limit of IDs, 4194303, was exceeded: 4194304 is " + "the current ID bound.")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/linker_fixture.h b/third_party/spirv-tools/test/link/linker_fixture.h new file mode 100644 index 0000000..7bb1223 --- /dev/null +++ b/third_party/spirv-tools/test/link/linker_fixture.h @@ -0,0 +1,222 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_LINK_LINKER_FIXTURE_H_ +#define TEST_LINK_LINKER_FIXTURE_H_ + +#include +#include +#include + +#include "effcee/effcee.h" +#include "re2/re2.h" +#include "source/spirv_constant.h" +#include "spirv-tools/linker.hpp" +#include "test/unit_spirv.h" + +namespace spvtest { + +using Binary = std::vector; +using Binaries = std::vector; + +class LinkerTest : public ::testing::Test { + public: + LinkerTest() + : context_(SPV_ENV_UNIVERSAL_1_2), + tools_(SPV_ENV_UNIVERSAL_1_2), + assemble_options_(spvtools::SpirvTools::kDefaultAssembleOption), + disassemble_options_(spvtools::SpirvTools::kDefaultDisassembleOption) { + const auto consumer = [this](spv_message_level_t level, const char*, + const spv_position_t& position, + const char* message) { + if (!error_message_.empty()) error_message_ += "\n"; + switch (level) { + case SPV_MSG_FATAL: + case SPV_MSG_INTERNAL_ERROR: + case SPV_MSG_ERROR: + error_message_ += "ERROR"; + break; + case SPV_MSG_WARNING: + error_message_ += "WARNING"; + break; + case SPV_MSG_INFO: + error_message_ += "INFO"; + break; + case SPV_MSG_DEBUG: + error_message_ += "DEBUG"; + break; + } + error_message_ += ": " + std::to_string(position.index) + ": " + message; + }; + context_.SetMessageConsumer(consumer); + tools_.SetMessageConsumer(consumer); + } + + void TearDown() override { error_message_.clear(); } + + // Assembles each of the given strings into SPIR-V binaries before linking + // them together. SPV_ERROR_INVALID_TEXT is returned if the assembling failed + // for any of the input strings, and SPV_ERROR_INVALID_POINTER if + // |linked_binary| is a null pointer. + spv_result_t AssembleAndLink( + const std::vector& bodies, spvtest::Binary* linked_binary, + spvtools::LinkerOptions options = spvtools::LinkerOptions()) { + if (!linked_binary) return SPV_ERROR_INVALID_POINTER; + + spvtest::Binaries binaries(bodies.size()); + for (size_t i = 0u; i < bodies.size(); ++i) + if (!tools_.Assemble(bodies[i], binaries.data() + i, assemble_options_)) + return SPV_ERROR_INVALID_TEXT; + + return spvtools::Link(context_, binaries, linked_binary, options); + } + + // Assembles and links a vector of SPIR-V bodies based on the |templateBody|. + // Template arguments to be replaced are written as {a,b,...}. + // SPV_ERROR_INVALID_TEXT is returned if the assembling failed for any of the + // resulting bodies (or errors in the template), and SPV_ERROR_INVALID_POINTER + // if |linked_binary| is a null pointer. + spv_result_t ExpandAndLink( + const std::string& templateBody, spvtest::Binary* linked_binary, + spvtools::LinkerOptions options = spvtools::LinkerOptions()) { + if (!linked_binary) return SPV_ERROR_INVALID_POINTER; + + // Find out how many template arguments there are, we assume they all have + // the same number. We'll error later if they don't. + re2::StringPiece temp(templateBody); + re2::StringPiece x; + int cnt = 0; + if (!RE2::FindAndConsume(&temp, "{")) return SPV_ERROR_INVALID_TEXT; + while (RE2::FindAndConsume(&temp, "([,}])", &x) && x[0] == ',') cnt++; + cnt++; + if (cnt <= 1) return SPV_ERROR_INVALID_TEXT; + + // Construct a regex for a single common strip and template expansion. + std::string regex("([^{]*){"); + for (int i = 0; i < cnt; i++) regex += (i > 0) ? ",([^,]*)" : "([^,]*)"; + regex += "}"; + RE2 pattern(regex); + + // Prepare the RE2::Args for processing. + re2::StringPiece common; + std::vector variants(cnt); + std::vector args(cnt + 1); + args[0] = RE2::Arg(&common); + std::vector pargs(cnt + 1); + pargs[0] = &args[0]; + for (int i = 0; i < cnt; i++) { + args[i + 1] = RE2::Arg(&variants[i]); + pargs[i + 1] = &args[i + 1]; + } + + // Reset and construct the bodies bit by bit. + std::vector bodies(cnt); + re2::StringPiece temp2(templateBody); + while (RE2::ConsumeN(&temp2, pattern, pargs.data(), cnt + 1)) { + for (int i = 0; i < cnt; i++) { + bodies[i].append(common.begin(), common.end()); + bodies[i].append(variants[i].begin(), variants[i].end()); + } + } + RE2::Consume(&temp2, "([^{]*)", &common); + for (int i = 0; i < cnt; i++) + bodies[i].append(common.begin(), common.end()); + + // Run through the assemble and link stages of the process. + return AssembleAndLink(bodies, linked_binary, options); + } + + // Expand the |templateBody| and link the results as with ExpandAndLink, + // then disassemble and test that the result matches the |expected|. + void ExpandAndCheck( + const std::string& templateBody, const std::string& expected, + const spvtools::LinkerOptions options = spvtools::LinkerOptions()) { + spvtest::Binary linked_binary; + spv_result_t res = ExpandAndLink(templateBody, &linked_binary, options); + EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage() << "\nExpanded from:\n" + << templateBody; + if (res == SPV_SUCCESS) { + std::string result; + EXPECT_TRUE( + tools_.Disassemble(linked_binary, &result, disassemble_options_)) + << GetErrorMessage(); + EXPECT_EQ(expected, result); + } + } + + // An alternative to ExpandAndCheck, which uses the |templateBody| as the + // match pattern for the disassembled linked result. + void ExpandAndMatch( + const std::string& templateBody, + const spvtools::LinkerOptions options = spvtools::LinkerOptions()) { + spvtest::Binary linked_binary; + spv_result_t res = ExpandAndLink(templateBody, &linked_binary, options); + EXPECT_EQ(SPV_SUCCESS, res) << GetErrorMessage() << "\nExpanded from:\n" + << templateBody; + if (res == SPV_SUCCESS) { + std::string result; + EXPECT_TRUE( + tools_.Disassemble(linked_binary, &result, disassemble_options_)) + << GetErrorMessage(); + auto match_res = effcee::Match(result, templateBody); + EXPECT_EQ(effcee::Result::Status::Ok, match_res.status()) + << match_res.message() << "\nExpanded from:\n" + << templateBody << "\nChecking result:\n" + << result; + } + } + + // Links the given SPIR-V binaries together; SPV_ERROR_INVALID_POINTER is + // returned if |linked_binary| is a null pointer. + spv_result_t Link( + const spvtest::Binaries& binaries, spvtest::Binary* linked_binary, + spvtools::LinkerOptions options = spvtools::LinkerOptions()) { + if (!linked_binary) return SPV_ERROR_INVALID_POINTER; + return spvtools::Link(context_, binaries, linked_binary, options); + } + + // Disassembles |binary| and outputs the result in |text|. If |text| is a + // null pointer, SPV_ERROR_INVALID_POINTER is returned. + spv_result_t Disassemble(const spvtest::Binary& binary, std::string* text) { + if (!text) return SPV_ERROR_INVALID_POINTER; + return tools_.Disassemble(binary, text, disassemble_options_) + ? SPV_SUCCESS + : SPV_ERROR_INVALID_BINARY; + } + + // Sets the options for the assembler. + void SetAssembleOptions(uint32_t assemble_options) { + assemble_options_ = assemble_options; + } + + // Sets the options used by the disassembler. + void SetDisassembleOptions(uint32_t disassemble_options) { + disassemble_options_ = disassemble_options; + } + + // Returns the accumulated error messages for the test. + std::string GetErrorMessage() const { return error_message_; } + + private: + spvtools::Context context_; + spvtools::SpirvTools + tools_; // An instance for calling SPIRV-Tools functionalities. + uint32_t assemble_options_; + uint32_t disassemble_options_; + std::string error_message_; +}; + +} // namespace spvtest + +#endif // TEST_LINK_LINKER_FIXTURE_H_ diff --git a/third_party/spirv-tools/test/link/matching_imports_to_exports_test.cpp b/third_party/spirv-tools/test/link/matching_imports_to_exports_test.cpp new file mode 100644 index 0000000..e76c69f --- /dev/null +++ b/third_party/spirv-tools/test/link/matching_imports_to_exports_test.cpp @@ -0,0 +1,476 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using ::testing::HasSubstr; +using MatchingImportsToExports = spvtest::LinkerTest; + +TEST_F(MatchingImportsToExports, Default) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +%3 = OpVariable %2 Input +)"; + const std::string body2 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%3 = OpConstant %2 42 +%1 = OpVariable %2 Uniform %3 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); + + const std::string expected_res = + R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" +%1 = OpTypeFloat 32 +%2 = OpVariable %1 Input +%3 = OpConstant %1 42 +%4 = OpVariable %1 Uniform %3 +)"; + std::string res_body; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + << GetErrorMessage(); + EXPECT_EQ(expected_res, res_body); +} + +TEST_F(MatchingImportsToExports, NotALibraryExtraExports) { + const std::string body = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary)) + << GetErrorMessage(); + + const std::string expected_res = + R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" +%1 = OpTypeFloat 32 +%2 = OpVariable %1 Uniform +)"; + std::string res_body; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + << GetErrorMessage(); + EXPECT_EQ(expected_res, res_body); +} + +TEST_F(MatchingImportsToExports, LibraryExtraExports) { + const std::string body = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +)"; + + spvtest::Binary linked_binary; + LinkerOptions options; + options.SetCreateLibrary(true); + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options)) + << GetErrorMessage(); + + const std::string expected_res = R"(OpCapability Linkage +OpModuleProcessed "Linked by SPIR-V Tools Linker" +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +)"; + std::string res_body; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + << GetErrorMessage(); + EXPECT_EQ(expected_res, res_body); +} + +TEST_F(MatchingImportsToExports, UnresolvedImports) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +)"; + const std::string body2 = R"()"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Unresolved external reference to \"foo\".")); +} + +TEST_F(MatchingImportsToExports, TypeMismatch) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +%3 = OpVariable %2 Input +)"; + const std::string body2 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 42 +%1 = OpVariable %2 Uniform %3 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Type mismatch on symbol \"foo\" between imported " + "variable/function %1 and exported variable/function %4")); +} + +TEST_F(MatchingImportsToExports, MultipleDefinitions) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +%3 = OpVariable %2 Input +)"; + const std::string body2 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%3 = OpConstant %2 42 +%1 = OpVariable %2 Uniform %3 +)"; + const std::string body3 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%3 = OpConstant %2 -1 +%1 = OpVariable %2 Uniform %3 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2, body3}, &linked_binary)) + << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Too many external references, 2, were found " + "for \"foo\".")); +} + +TEST_F(MatchingImportsToExports, SameNameDifferentTypes) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +%3 = OpVariable %2 Input +)"; + const std::string body2 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeInt 32 0 +%3 = OpConstant %2 42 +%1 = OpVariable %2 Uniform %3 +)"; + const std::string body3 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%3 = OpConstant %2 12 +%1 = OpVariable %2 Uniform %3 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2, body3}, &linked_binary)) + << GetErrorMessage(); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Too many external references, 2, were found " + "for \"foo\".")); +} + +TEST_F(MatchingImportsToExports, DecorationMismatch) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +OpDecorate %2 Constant +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +%3 = OpVariable %2 Input +)"; + const std::string body2 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeFloat 32 +%3 = OpConstant %2 42 +%1 = OpVariable %2 Uniform %3 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Type mismatch on symbol \"foo\" between imported " + "variable/function %1 and exported variable/function %4")); +} + +TEST_F(MatchingImportsToExports, + FuncParamAttrDifferButStillMatchExportToImport) { + const std::string body1 = R"( +OpCapability Kernel +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +OpDecorate %2 FuncParamAttr Zext +%3 = OpTypeVoid +%4 = OpTypeInt 32 0 +%5 = OpTypeFunction %3 %4 +%1 = OpFunction %3 None %5 +%2 = OpFunctionParameter %4 +OpFunctionEnd +)"; + const std::string body2 = R"( +OpCapability Kernel +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +OpDecorate %2 FuncParamAttr Sext +%3 = OpTypeVoid +%4 = OpTypeInt 32 0 +%5 = OpTypeFunction %3 %4 +%1 = OpFunction %3 None %5 +%2 = OpFunctionParameter %4 +%6 = OpLabel +OpReturn +OpFunctionEnd +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); + + const std::string expected_res = R"(OpCapability Kernel +OpModuleProcessed "Linked by SPIR-V Tools Linker" +OpDecorate %1 FuncParamAttr Sext +%2 = OpTypeVoid +%3 = OpTypeInt 32 0 +%4 = OpTypeFunction %2 %3 +%5 = OpFunction %2 None %4 +%1 = OpFunctionParameter %3 +%6 = OpLabel +OpReturn +OpFunctionEnd +)"; + std::string res_body; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + << GetErrorMessage(); + EXPECT_EQ(expected_res, res_body); +} + +TEST_F(MatchingImportsToExports, FunctionCtrl) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%4 = OpTypeFloat 32 +%5 = OpVariable %4 Uniform +%1 = OpFunction %2 None %3 +OpFunctionEnd +)"; + const std::string body2 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +%2 = OpTypeVoid +%3 = OpTypeFunction %2 +%1 = OpFunction %2 Inline %3 +%4 = OpLabel +OpReturn +OpFunctionEnd +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); + + const std::string expected_res = + R"(OpModuleProcessed "Linked by SPIR-V Tools Linker" +%1 = OpTypeVoid +%2 = OpTypeFunction %1 +%3 = OpTypeFloat 32 +%4 = OpVariable %3 Uniform +%5 = OpFunction %1 Inline %2 +%6 = OpLabel +OpReturn +OpFunctionEnd +)"; + std::string res_body; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + << GetErrorMessage(); + EXPECT_EQ(expected_res, res_body); +} + +TEST_F(MatchingImportsToExports, UseExportedFuncParamAttr) { + const std::string body1 = R"( +OpCapability Kernel +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +OpDecorate %2 FuncParamAttr Zext +%2 = OpDecorationGroup +OpGroupDecorate %2 %3 %4 +%5 = OpTypeVoid +%6 = OpTypeInt 32 0 +%7 = OpTypeFunction %5 %6 +%1 = OpFunction %5 None %7 +%3 = OpFunctionParameter %6 +OpFunctionEnd +%8 = OpFunction %5 None %7 +%4 = OpFunctionParameter %6 +OpFunctionEnd +)"; + const std::string body2 = R"( +OpCapability Kernel +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Export +OpDecorate %2 FuncParamAttr Sext +%3 = OpTypeVoid +%4 = OpTypeInt 32 0 +%5 = OpTypeFunction %3 %4 +%1 = OpFunction %3 None %5 +%2 = OpFunctionParameter %4 +%6 = OpLabel +OpReturn +OpFunctionEnd +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); + + const std::string expected_res = R"(OpCapability Kernel +OpModuleProcessed "Linked by SPIR-V Tools Linker" +OpDecorate %1 FuncParamAttr Zext +%1 = OpDecorationGroup +OpGroupDecorate %1 %2 +OpDecorate %3 FuncParamAttr Sext +%4 = OpTypeVoid +%5 = OpTypeInt 32 0 +%6 = OpTypeFunction %4 %5 +%7 = OpFunction %4 None %6 +%2 = OpFunctionParameter %5 +OpFunctionEnd +%8 = OpFunction %4 None %6 +%3 = OpFunctionParameter %5 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + std::string res_body; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + << GetErrorMessage(); + EXPECT_EQ(expected_res, res_body); +} + +TEST_F(MatchingImportsToExports, NamesAndDecorations) { + const std::string body1 = R"( +OpCapability Kernel +OpCapability Linkage +OpName %1 "foo" +OpName %3 "param" +OpDecorate %1 LinkageAttributes "foo" Import +OpDecorate %2 Restrict +OpDecorate %4 NonWritable +%2 = OpDecorationGroup +OpGroupDecorate %2 %3 %4 +%5 = OpTypeVoid +%6 = OpTypeInt 32 0 +%9 = OpTypePointer Function %6 +%7 = OpTypeFunction %5 %9 +%1 = OpFunction %5 None %7 +%3 = OpFunctionParameter %9 +OpFunctionEnd +%8 = OpFunction %5 None %7 +%4 = OpFunctionParameter %9 +OpFunctionEnd +)"; + const std::string body2 = R"( +OpCapability Kernel +OpCapability Linkage +OpName %1 "foo" +OpName %2 "param" +OpDecorate %1 LinkageAttributes "foo" Export +OpDecorate %2 Restrict +%3 = OpTypeVoid +%4 = OpTypeInt 32 0 +%7 = OpTypePointer Function %4 +%5 = OpTypeFunction %3 %7 +%1 = OpFunction %3 None %5 +%2 = OpFunctionParameter %7 +%6 = OpLabel +OpReturn +OpFunctionEnd +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)) + << GetErrorMessage(); + + const std::string expected_res = R"(OpCapability Kernel +OpName %1 "foo" +OpName %2 "param" +OpModuleProcessed "Linked by SPIR-V Tools Linker" +OpDecorate %3 Restrict +OpDecorate %4 NonWritable +%3 = OpDecorationGroup +OpGroupDecorate %3 %4 +OpDecorate %2 Restrict +%5 = OpTypeVoid +%6 = OpTypeInt 32 0 +%7 = OpTypePointer Function %6 +%8 = OpTypeFunction %5 %7 +%9 = OpFunction %5 None %8 +%4 = OpFunctionParameter %7 +OpFunctionEnd +%1 = OpFunction %5 None %8 +%2 = OpFunctionParameter %7 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + std::string res_body; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + << GetErrorMessage(); + EXPECT_EQ(expected_res, res_body); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/memory_model_test.cpp b/third_party/spirv-tools/test/link/memory_model_test.cpp new file mode 100644 index 0000000..2add504 --- /dev/null +++ b/third_party/spirv-tools/test/link/memory_model_test.cpp @@ -0,0 +1,74 @@ +// Copyright (c) 2017 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using ::testing::HasSubstr; +using MemoryModel = spvtest::LinkerTest; + +TEST_F(MemoryModel, Default) { + const std::string body1 = R"( +OpMemoryModel Logical Simple +)"; + const std::string body2 = R"( +OpMemoryModel Logical Simple +)"; + + spvtest::Binary linked_binary; + ASSERT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), std::string()); + + EXPECT_EQ(SpvAddressingModelLogical, linked_binary[6]); + EXPECT_EQ(SpvMemoryModelSimple, linked_binary[7]); +} + +TEST_F(MemoryModel, AddressingMismatch) { + const std::string body1 = R"( +OpMemoryModel Logical Simple +)"; + const std::string body2 = R"( +OpMemoryModel Physical32 Simple +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INTERNAL, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT( + GetErrorMessage(), + HasSubstr("Conflicting addressing models: Logical vs Physical32.")); +} + +TEST_F(MemoryModel, MemoryMismatch) { + const std::string body1 = R"( +OpMemoryModel Logical Simple +)"; + const std::string body2 = R"( +OpMemoryModel Logical GLSL450 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INTERNAL, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Conflicting memory models: Simple vs GLSL450.")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/partial_linkage_test.cpp b/third_party/spirv-tools/test/link/partial_linkage_test.cpp new file mode 100644 index 0000000..c43b06e --- /dev/null +++ b/third_party/spirv-tools/test/link/partial_linkage_test.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2018 Pierre Moreau +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using ::testing::HasSubstr; +using PartialLinkage = spvtest::LinkerTest; + +TEST_F(PartialLinkage, Allowed) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +OpDecorate %2 LinkageAttributes "bar" Import +%3 = OpTypeFloat 32 +%1 = OpVariable %3 Uniform +%2 = OpVariable %3 Uniform +)"; + const std::string body2 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "bar" Export +%2 = OpTypeFloat 32 +%3 = OpConstant %2 3.1415 +%1 = OpVariable %2 Uniform %3 +)"; + + spvtest::Binary linked_binary; + LinkerOptions linker_options; + linker_options.SetAllowPartialLinkage(true); + ASSERT_EQ(SPV_SUCCESS, + AssembleAndLink({body1, body2}, &linked_binary, linker_options)); + + const std::string expected_res = R"(OpCapability Linkage +OpModuleProcessed "Linked by SPIR-V Tools Linker" +OpDecorate %1 LinkageAttributes "foo" Import +%2 = OpTypeFloat 32 +%1 = OpVariable %2 Uniform +%3 = OpConstant %2 3.1415 +%4 = OpVariable %2 Uniform %3 +)"; + std::string res_body; + SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER); + ASSERT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body)) + << GetErrorMessage(); + EXPECT_EQ(expected_res, res_body); +} + +TEST_F(PartialLinkage, Disallowed) { + const std::string body1 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "foo" Import +OpDecorate %2 LinkageAttributes "bar" Import +%3 = OpTypeFloat 32 +%1 = OpVariable %3 Uniform +%2 = OpVariable %3 Uniform +)"; + const std::string body2 = R"( +OpCapability Linkage +OpDecorate %1 LinkageAttributes "bar" Export +%2 = OpTypeFloat 32 +%3 = OpConstant %2 3.1415 +%1 = OpVariable %2 Uniform %3 +)"; + + spvtest::Binary linked_binary; + EXPECT_EQ(SPV_ERROR_INVALID_BINARY, + AssembleAndLink({body1, body2}, &linked_binary)); + EXPECT_THAT(GetErrorMessage(), + HasSubstr("Unresolved external reference to \"foo\".")); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/type_match_test.cpp b/third_party/spirv-tools/test/link/type_match_test.cpp new file mode 100644 index 0000000..dae70c1 --- /dev/null +++ b/third_party/spirv-tools/test/link/type_match_test.cpp @@ -0,0 +1,148 @@ +// Copyright (c) 2019 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using TypeMatch = spvtest::LinkerTest; + +// Basic types +#define PartInt(D, N) D(N) " = OpTypeInt 32 0" +#define PartFloat(D, N) D(N) " = OpTypeFloat 32" +#define PartOpaque(D, N) D(N) " = OpTypeOpaque \"bar\"" +#define PartSampler(D, N) D(N) " = OpTypeSampler" +#define PartEvent(D, N) D(N) " = OpTypeEvent" +#define PartDeviceEvent(D, N) D(N) " = OpTypeDeviceEvent" +#define PartReserveId(D, N) D(N) " = OpTypeReserveId" +#define PartQueue(D, N) D(N) " = OpTypeQueue" +#define PartPipe(D, N) D(N) " = OpTypePipe ReadWrite" +#define PartPipeStorage(D, N) D(N) " = OpTypePipeStorage" +#define PartNamedBarrier(D, N) D(N) " = OpTypeNamedBarrier" + +// Compound types +#define PartVector(DR, DA, N, T) DR(N) " = OpTypeVector " DA(T) " 3" +#define PartMatrix(DR, DA, N, T) DR(N) " = OpTypeMatrix " DA(T) " 4" +#define PartImage(DR, DA, N, T) \ + DR(N) " = OpTypeImage " DA(T) " 2D 0 0 0 0 Rgba32f" +#define PartSampledImage(DR, DA, N, T) DR(N) " = OpTypeSampledImage " DA(T) +#define PartArray(DR, DA, N, T) DR(N) " = OpTypeArray " DA(T) " " DA(const) +#define PartRuntimeArray(DR, DA, N, T) DR(N) " = OpTypeRuntimeArray " DA(T) +#define PartStruct(DR, DA, N, T) DR(N) " = OpTypeStruct " DA(T) " " DA(T) +#define PartPointer(DR, DA, N, T) DR(N) " = OpTypePointer Workgroup " DA(T) +#define PartFunction(DR, DA, N, T) DR(N) " = OpTypeFunction " DA(T) " " DA(T) + +#define CheckDecoRes(S) "[[" #S ":%\\w+]]" +#define CheckDecoArg(S) "[[" #S "]]" +#define InstDeco(S) "%" #S + +#define MatchPart1(F, N) \ + "; CHECK: " Part##F(CheckDecoRes, N) "\n" Part##F(InstDeco, N) "\n" +#define MatchPart2(F, N, T) \ + "; CHECK: " Part##F(CheckDecoRes, CheckDecoArg, N, T) "\n" Part##F( \ + InstDeco, InstDeco, N, T) "\n" + +#define MatchF(N, CODE) \ + TEST_F(TypeMatch, N) { \ + const std::string base = \ + "OpCapability Linkage\n" \ + "OpCapability NamedBarrier\n" \ + "OpCapability PipeStorage\n" \ + "OpCapability Pipes\n" \ + "OpCapability DeviceEnqueue\n" \ + "OpCapability Kernel\n" \ + "OpCapability Shader\n" \ + "OpCapability Addresses\n" \ + "OpDecorate %var LinkageAttributes \"foo\" " \ + "{Import,Export}\n" \ + "; CHECK: [[baseint:%\\w+]] = OpTypeInt 32 1\n" \ + "%baseint = OpTypeInt 32 1\n" \ + "; CHECK: [[const:%\\w+]] = OpConstant [[baseint]] 3\n" \ + "%const = OpConstant %baseint 3\n" CODE \ + "; CHECK: OpVariable [[type]] Uniform\n" \ + "%var = OpVariable %type Uniform"; \ + ExpandAndMatch(base); \ + } + +#define Match1(T) MatchF(Type##T, MatchPart1(T, type)) +#define Match2(T, A) \ + MatchF(T##OfType##A, MatchPart1(A, a) MatchPart2(T, type, a)) +#define Match3(T, A, B) \ + MatchF(T##Of##A##Of##B, \ + MatchPart1(B, b) MatchPart2(A, a, b) MatchPart2(T, type, a)) + +// clang-format off +// Basic types +Match1(Int) +Match1(Float) +Match1(Opaque) +Match1(Sampler) +Match1(Event) +Match1(DeviceEvent) +Match1(ReserveId) +Match1(Queue) +Match1(Pipe) +Match1(PipeStorage) +Match1(NamedBarrier) + +// Simpler (restricted) compound types +Match2(Vector, Float) +Match3(Matrix, Vector, Float) +Match2(Image, Float) + +// Unrestricted compound types +#define MatchCompounds1(A) \ + Match2(RuntimeArray, A) \ + Match2(Struct, A) \ + Match2(Pointer, A) \ + Match2(Function, A) \ + Match2(Array, A) +#define MatchCompounds2(A, B) \ + Match3(RuntimeArray, A, B) \ + Match3(Struct, A, B) \ + Match3(Pointer, A, B) \ + Match3(Function, A, B) \ + Match3(Array, A, B) + +MatchCompounds1(Float) +MatchCompounds2(Array, Float) +MatchCompounds2(RuntimeArray, Float) +MatchCompounds2(Struct, Float) +MatchCompounds2(Pointer, Float) +MatchCompounds2(Function, Float) +// clang-format on + +// ForwardPointer tests, which don't fit into the previous mold +#define MatchFpF(N, CODE) \ + MatchF(N, \ + "; CHECK: OpTypeForwardPointer [[type:%\\w+]] Workgroup\n" \ + "OpTypeForwardPointer %type Workgroup\n" CODE \ + "; CHECK: [[type]] = OpTypePointer Workgroup [[realtype]]\n" \ + "%type = OpTypePointer Workgroup %realtype\n") +#define MatchFp1(T) MatchFpF(ForwardPointerOf##T, MatchPart1(T, realtype)) +#define MatchFp2(T, A) \ + MatchFpF(ForwardPointerOf##T, MatchPart1(A, a) MatchPart2(T, realtype, a)) + + // clang-format off +MatchFp1(Float) +MatchFp2(Array, Float) +MatchFp2(RuntimeArray, Float) +MatchFp2(Struct, Float) +MatchFp2(Function, Float) +// clang-format on + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/link/unique_ids_test.cpp b/third_party/spirv-tools/test/link/unique_ids_test.cpp new file mode 100644 index 0000000..55c70ea --- /dev/null +++ b/third_party/spirv-tools/test/link/unique_ids_test.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2017 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "test/link/linker_fixture.h" + +namespace spvtools { +namespace { + +using UniqueIds = spvtest::LinkerTest; + +TEST_F(UniqueIds, UniquelyMerged) { + std::vector bodies(2); + bodies[0] = + // clang-format off + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpEntryPoint Vertex %main \"main\"\n" + "OpSource ESSL 310\n" + "OpName %main \"main\"\n" + "OpName %f_ \"f(\"\n" + "OpName %gv1 \"gv1\"\n" + "OpName %gv2 \"gv2\"\n" + "OpName %lv1 \"lv1\"\n" + "OpName %lv2 \"lv2\"\n" + "OpName %lv1_0 \"lv1\"\n" + "%void = OpTypeVoid\n" + "%10 = OpTypeFunction %void\n" + "%float = OpTypeFloat 32\n" + "%12 = OpTypeFunction %float\n" + "%_ptr_Private_float = OpTypePointer Private %float\n" + "%gv1 = OpVariable %_ptr_Private_float Private\n" + "%float_10 = OpConstant %float 10\n" + "%gv2 = OpVariable %_ptr_Private_float Private\n" + "%float_100 = OpConstant %float 100\n" + "%_ptr_Function_float = OpTypePointer Function %float\n" + "%main = OpFunction %void None %10\n" + "%17 = OpLabel\n" + "%lv1_0 = OpVariable %_ptr_Function_float Function\n" + "OpStore %gv1 %float_10\n" + "OpStore %gv2 %float_100\n" + "%18 = OpLoad %float %gv1\n" + "%19 = OpLoad %float %gv2\n" + "%20 = OpFSub %float %18 %19\n" + "OpStore %lv1_0 %20\n" + "OpReturn\n" + "OpFunctionEnd\n" + "%f_ = OpFunction %float None %12\n" + "%21 = OpLabel\n" + "%lv1 = OpVariable %_ptr_Function_float Function\n" + "%lv2 = OpVariable %_ptr_Function_float Function\n" + "%22 = OpLoad %float %gv1\n" + "%23 = OpLoad %float %gv2\n" + "%24 = OpFAdd %float %22 %23\n" + "OpStore %lv1 %24\n" + "%25 = OpLoad %float %gv1\n" + "%26 = OpLoad %float %gv2\n" + "%27 = OpFMul %float %25 %26\n" + "OpStore %lv2 %27\n" + "%28 = OpLoad %float %lv1\n" + "%29 = OpLoad %float %lv2\n" + "%30 = OpFDiv %float %28 %29\n" + "OpReturnValue %30\n" + "OpFunctionEnd\n"; + // clang-format on + bodies[1] = + // clang-format off + "OpCapability Shader\n" + "%1 = OpExtInstImport \"GLSL.std.450\"\n" + "OpMemoryModel Logical GLSL450\n" + "OpSource ESSL 310\n" + "OpName %main \"main2\"\n" + "OpName %f_ \"f(\"\n" + "OpName %gv1 \"gv12\"\n" + "OpName %gv2 \"gv22\"\n" + "OpName %lv1 \"lv12\"\n" + "OpName %lv2 \"lv22\"\n" + "OpName %lv1_0 \"lv12\"\n" + "%void = OpTypeVoid\n" + "%10 = OpTypeFunction %void\n" + "%float = OpTypeFloat 32\n" + "%12 = OpTypeFunction %float\n" + "%_ptr_Private_float = OpTypePointer Private %float\n" + "%gv1 = OpVariable %_ptr_Private_float Private\n" + "%float_10 = OpConstant %float 10\n" + "%gv2 = OpVariable %_ptr_Private_float Private\n" + "%float_100 = OpConstant %float 100\n" + "%_ptr_Function_float = OpTypePointer Function %float\n" + "%main = OpFunction %void None %10\n" + "%17 = OpLabel\n" + "%lv1_0 = OpVariable %_ptr_Function_float Function\n" + "OpStore %gv1 %float_10\n" + "OpStore %gv2 %float_100\n" + "%18 = OpLoad %float %gv1\n" + "%19 = OpLoad %float %gv2\n" + "%20 = OpFSub %float %18 %19\n" + "OpStore %lv1_0 %20\n" + "OpReturn\n" + "OpFunctionEnd\n" + "%f_ = OpFunction %float None %12\n" + "%21 = OpLabel\n" + "%lv1 = OpVariable %_ptr_Function_float Function\n" + "%lv2 = OpVariable %_ptr_Function_float Function\n" + "%22 = OpLoad %float %gv1\n" + "%23 = OpLoad %float %gv2\n" + "%24 = OpFAdd %float %22 %23\n" + "OpStore %lv1 %24\n" + "%25 = OpLoad %float %gv1\n" + "%26 = OpLoad %float %gv2\n" + "%27 = OpFMul %float %25 %26\n" + "OpStore %lv2 %27\n" + "%28 = OpLoad %float %lv1\n" + "%29 = OpLoad %float %lv2\n" + "%30 = OpFDiv %float %28 %29\n" + "OpReturnValue %30\n" + "OpFunctionEnd\n"; + // clang-format on + + spvtest::Binary linked_binary; + LinkerOptions options; + options.SetVerifyIds(true); + spv_result_t res = AssembleAndLink(bodies, &linked_binary, options); + EXPECT_EQ(SPV_SUCCESS, res); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/log_test.cpp b/third_party/spirv-tools/test/log_test.cpp new file mode 100644 index 0000000..ec66aa1 --- /dev/null +++ b/third_party/spirv-tools/test/log_test.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "source/opt/log.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace spvtools { +namespace { + +using ::testing::MatchesRegex; + +TEST(Log, Unimplemented) { + int invocation = 0; + auto consumer = [&invocation](spv_message_level_t level, const char* source, + const spv_position_t&, const char* message) { + ++invocation; + EXPECT_EQ(SPV_MSG_INTERNAL_ERROR, level); + EXPECT_THAT(source, MatchesRegex(".*log_test.cpp$")); + EXPECT_STREQ("unimplemented: the-ultimite-feature", message); + }; + + SPIRV_UNIMPLEMENTED(consumer, "the-ultimite-feature"); + EXPECT_EQ(1, invocation); +} + +TEST(Log, Unreachable) { + int invocation = 0; + auto consumer = [&invocation](spv_message_level_t level, const char* source, + const spv_position_t&, const char* message) { + ++invocation; + EXPECT_EQ(SPV_MSG_INTERNAL_ERROR, level); + EXPECT_THAT(source, MatchesRegex(".*log_test.cpp$")); + EXPECT_STREQ("unreachable", message); + }; + + SPIRV_UNREACHABLE(consumer); + EXPECT_EQ(1, invocation); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/name_mapper_test.cpp b/third_party/spirv-tools/test/name_mapper_test.cpp new file mode 100644 index 0000000..759c705 --- /dev/null +++ b/third_party/spirv-tools/test/name_mapper_test.cpp @@ -0,0 +1,348 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "gmock/gmock.h" +#include "source/name_mapper.h" +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::ScopedContext; +using ::testing::Eq; + +TEST(TrivialNameTest, Samples) { + auto mapper = GetTrivialNameMapper(); + EXPECT_EQ(mapper(1), "1"); + EXPECT_EQ(mapper(1999), "1999"); + EXPECT_EQ(mapper(1024), "1024"); +} + +// A test case for the name mappers that actually look at an assembled module. +struct NameIdCase { + std::string assembly; // Input assembly text + uint32_t id; + std::string expected_name; +}; + +using FriendlyNameTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(FriendlyNameTest, SingleMapping) { + ScopedContext context(SPV_ENV_UNIVERSAL_1_1); + auto words = CompileSuccessfully(GetParam().assembly, SPV_ENV_UNIVERSAL_1_1); + auto friendly_mapper = + FriendlyNameMapper(context.context, words.data(), words.size()); + NameMapper mapper = friendly_mapper.GetNameMapper(); + EXPECT_THAT(mapper(GetParam().id), Eq(GetParam().expected_name)) + << GetParam().assembly << std::endl + << " for id " << GetParam().id; +} + +INSTANTIATE_TEST_SUITE_P(ScalarType, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeVoid", 1, "void"}, + {"%1 = OpTypeBool", 1, "bool"}, + {"%1 = OpTypeInt 8 0", 1, "uchar"}, + {"%1 = OpTypeInt 8 1", 1, "char"}, + {"%1 = OpTypeInt 16 0", 1, "ushort"}, + {"%1 = OpTypeInt 16 1", 1, "short"}, + {"%1 = OpTypeInt 32 0", 1, "uint"}, + {"%1 = OpTypeInt 32 1", 1, "int"}, + {"%1 = OpTypeInt 64 0", 1, "ulong"}, + {"%1 = OpTypeInt 64 1", 1, "long"}, + {"%1 = OpTypeInt 1 0", 1, "u1"}, + {"%1 = OpTypeInt 1 1", 1, "i1"}, + {"%1 = OpTypeInt 33 0", 1, "u33"}, + {"%1 = OpTypeInt 33 1", 1, "i33"}, + + {"%1 = OpTypeFloat 16", 1, "half"}, + {"%1 = OpTypeFloat 32", 1, "float"}, + {"%1 = OpTypeFloat 64", 1, "double"}, + {"%1 = OpTypeFloat 10", 1, "fp10"}, + {"%1 = OpTypeFloat 55", 1, "fp55"}, + })); + +INSTANTIATE_TEST_SUITE_P( + VectorType, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeBool %2 = OpTypeVector %1 1", 2, "v1bool"}, + {"%1 = OpTypeBool %2 = OpTypeVector %1 2", 2, "v2bool"}, + {"%1 = OpTypeBool %2 = OpTypeVector %1 3", 2, "v3bool"}, + {"%1 = OpTypeBool %2 = OpTypeVector %1 4", 2, "v4bool"}, + + {"%1 = OpTypeInt 8 0 %2 = OpTypeVector %1 2", 2, "v2uchar"}, + {"%1 = OpTypeInt 16 1 %2 = OpTypeVector %1 3", 2, "v3short"}, + {"%1 = OpTypeInt 32 0 %2 = OpTypeVector %1 4", 2, "v4uint"}, + {"%1 = OpTypeInt 64 1 %2 = OpTypeVector %1 3", 2, "v3long"}, + {"%1 = OpTypeInt 20 0 %2 = OpTypeVector %1 4", 2, "v4u20"}, + {"%1 = OpTypeInt 21 1 %2 = OpTypeVector %1 3", 2, "v3i21"}, + + {"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2", 2, "v2float"}, + // OpName overrides the element name. + {"OpName %1 \"time\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2", 2, + "v2time"}, + })); + +INSTANTIATE_TEST_SUITE_P( + MatrixType, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeBool %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 2", 3, + "mat2v2bool"}, + {"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 3", 3, + "mat3v2float"}, + {"%1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = OpTypeMatrix %2 4", 3, + "mat4v2float"}, + {"OpName %1 \"time\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 = " + "OpTypeMatrix %2 4", + 3, "mat4v2time"}, + {"OpName %2 \"lat_long\" %1 = OpTypeFloat 32 %2 = OpTypeVector %1 2 %3 " + "= OpTypeMatrix %2 4", + 3, "mat4lat_long"}, + })); + +INSTANTIATE_TEST_SUITE_P( + OpName, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"OpName %1 \"abcdefg\"", 1, "abcdefg"}, + {"OpName %1 \"Hello world!\"", 1, "Hello_world_"}, + {"OpName %1 \"0123456789\"", 1, "0123456789"}, + {"OpName %1 \"_\"", 1, "_"}, + // An empty string is not valid for SPIR-V assembly IDs. + {"OpName %1 \"\"", 1, "_"}, + // Test uniqueness when presented with things mapping to "_" + {"OpName %1 \"\" OpName %2 \"\"", 1, "_"}, + {"OpName %1 \"\" OpName %2 \"\"", 2, "__0"}, + {"OpName %1 \"\" OpName %2 \"\" OpName %3 \"_\"", 3, "__1"}, + // Test uniqueness of names that are forced to be + // numbers. + {"OpName %1 \"2\" OpName %2 \"2\"", 1, "2"}, + {"OpName %1 \"2\" OpName %2 \"2\"", 2, "2_0"}, + // Test uniqueness in the face of forward references + // for Ids that don't already have friendly names. + // In particular, the first OpDecorate assigns the name, and + // the second one can't override it. + {"OpDecorate %1 Volatile OpDecorate %1 Restrict", 1, "1"}, + // But a forced name can override the name that + // would have been assigned via the OpDecorate + // forward reference. + {"OpName %1 \"mememe\" OpDecorate %1 Volatile OpDecorate %1 Restrict", + 1, "mememe"}, + // OpName can override other inferences. We assume valid instruction + // ordering, where OpName precedes type definitions. + {"OpName %1 \"myfloat\" %1 = OpTypeFloat 32", 1, "myfloat"}, + })); + +INSTANTIATE_TEST_SUITE_P( + UniquenessHeuristic, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 1, "void"}, + {"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 2, "void_0"}, + {"%1 = OpTypeVoid %2 = OpTypeVoid %3 = OpTypeVoid", 3, "void_1"}, + })); + +INSTANTIATE_TEST_SUITE_P(Arrays, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"OpName %2 \"FortyTwo\" %1 = OpTypeFloat 32 " + "%2 = OpConstant %1 42 %3 = OpTypeArray %1 %2", + 3, "_arr_float_FortyTwo"}, + {"%1 = OpTypeInt 32 0 " + "%2 = OpTypeRuntimeArray %1", + 2, "_runtimearr_uint"}, + })); + +INSTANTIATE_TEST_SUITE_P(Structs, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeBool " + "%2 = OpTypeStruct %1 %1 %1", + 2, "_struct_2"}, + {"%1 = OpTypeBool " + "%2 = OpTypeStruct %1 %1 %1 " + "%3 = OpTypeStruct %2 %2", + 3, "_struct_3"}, + })); + +INSTANTIATE_TEST_SUITE_P( + Pointer, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeFloat 32 %2 = OpTypePointer Workgroup %1", 2, + "_ptr_Workgroup_float"}, + {"%1 = OpTypeBool %2 = OpTypePointer Private %1", 2, + "_ptr_Private_bool"}, + // OpTypeForwardPointer doesn't force generation of the name for its + // target type. + {"%1 = OpTypeBool OpTypeForwardPointer %2 Private %2 = OpTypePointer " + "Private %1", + 2, "_ptr_Private_bool"}, + })); + +INSTANTIATE_TEST_SUITE_P(ExoticTypes, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeEvent", 1, "Event"}, + {"%1 = OpTypeDeviceEvent", 1, "DeviceEvent"}, + {"%1 = OpTypeReserveId", 1, "ReserveId"}, + {"%1 = OpTypeQueue", 1, "Queue"}, + {"%1 = OpTypeOpaque \"hello world!\"", 1, + "Opaque_hello_world_"}, + {"%1 = OpTypePipe ReadOnly", 1, "PipeReadOnly"}, + {"%1 = OpTypePipe WriteOnly", 1, "PipeWriteOnly"}, + {"%1 = OpTypePipe ReadWrite", 1, "PipeReadWrite"}, + {"%1 = OpTypePipeStorage", 1, "PipeStorage"}, + {"%1 = OpTypeNamedBarrier", 1, "NamedBarrier"}, + })); + +// Makes a test case for a BuiltIn variable declaration. +NameIdCase BuiltInCase(std::string assembly_name, std::string expected) { + return NameIdCase{std::string("OpDecorate %1 BuiltIn ") + assembly_name + + " %1 = OpVariable %2 Input", + 1, expected}; +} + +// Makes a test case for a BuiltIn variable declaration. In this overload, +// the expected result is the same as the assembly name. +NameIdCase BuiltInCase(std::string assembly_name) { + return BuiltInCase(assembly_name, assembly_name); +} + +// Makes a test case for a BuiltIn variable declaration. In this overload, +// the expected result is the same as the assembly name, but with a "gl_" +// prefix. +NameIdCase BuiltInGLCase(std::string assembly_name) { + return BuiltInCase(assembly_name, std::string("gl_") + assembly_name); +} + +INSTANTIATE_TEST_SUITE_P( + BuiltIns, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + BuiltInGLCase("Position"), + BuiltInGLCase("PointSize"), + BuiltInGLCase("ClipDistance"), + BuiltInGLCase("CullDistance"), + BuiltInCase("VertexId", "gl_VertexID"), + BuiltInCase("InstanceId", "gl_InstanceID"), + BuiltInCase("PrimitiveId", "gl_PrimitiveID"), + BuiltInCase("InvocationId", "gl_InvocationID"), + BuiltInGLCase("Layer"), + BuiltInGLCase("ViewportIndex"), + BuiltInGLCase("TessLevelOuter"), + BuiltInGLCase("TessLevelInner"), + BuiltInGLCase("TessCoord"), + BuiltInGLCase("PatchVertices"), + BuiltInGLCase("FragCoord"), + BuiltInGLCase("PointCoord"), + BuiltInGLCase("FrontFacing"), + BuiltInCase("SampleId", "gl_SampleID"), + BuiltInGLCase("SamplePosition"), + BuiltInGLCase("SampleMask"), + BuiltInGLCase("FragDepth"), + BuiltInGLCase("HelperInvocation"), + BuiltInCase("NumWorkgroups", "gl_NumWorkGroups"), + BuiltInCase("WorkgroupSize", "gl_WorkGroupSize"), + BuiltInCase("WorkgroupId", "gl_WorkGroupID"), + BuiltInCase("LocalInvocationId", "gl_LocalInvocationID"), + BuiltInCase("GlobalInvocationId", "gl_GlobalInvocationID"), + BuiltInGLCase("LocalInvocationIndex"), + BuiltInCase("WorkDim"), + BuiltInCase("GlobalSize"), + BuiltInCase("EnqueuedWorkgroupSize"), + BuiltInCase("GlobalOffset"), + BuiltInCase("GlobalLinearId"), + BuiltInCase("SubgroupSize"), + BuiltInCase("SubgroupMaxSize"), + BuiltInCase("NumSubgroups"), + BuiltInCase("NumEnqueuedSubgroups"), + BuiltInCase("SubgroupId"), + BuiltInCase("SubgroupLocalInvocationId"), + BuiltInGLCase("VertexIndex"), + BuiltInGLCase("InstanceIndex"), + BuiltInGLCase("BaseInstance"), + BuiltInCase("SubgroupEqMaskKHR"), + BuiltInCase("SubgroupGeMaskKHR"), + BuiltInCase("SubgroupGtMaskKHR"), + BuiltInCase("SubgroupLeMaskKHR"), + BuiltInCase("SubgroupLtMaskKHR"), + })); + +INSTANTIATE_TEST_SUITE_P(DebugNameOverridesBuiltin, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"OpName %1 \"foo\" OpDecorate %1 BuiltIn WorkDim " + "%1 = OpVariable %2 Input", + 1, "foo"}})); + +INSTANTIATE_TEST_SUITE_P( + SimpleIntegralConstants, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 0", 2, "uint_0"}, + {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 1", 2, "uint_1"}, + {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 2", 2, "uint_2"}, + {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 9", 2, "uint_9"}, + {"%1 = OpTypeInt 32 0 %2 = OpConstant %1 42", 2, "uint_42"}, + {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 0", 2, "int_0"}, + {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 1", 2, "int_1"}, + {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 2", 2, "int_2"}, + {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 9", 2, "int_9"}, + {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 42", 2, "int_42"}, + {"%1 = OpTypeInt 32 1 %2 = OpConstant %1 -42", 2, "int_n42"}, + // Exotic bit widths + {"%1 = OpTypeInt 33 0 %2 = OpConstant %1 0", 2, "u33_0"}, + {"%1 = OpTypeInt 33 1 %2 = OpConstant %1 10", 2, "i33_10"}, + {"%1 = OpTypeInt 33 1 %2 = OpConstant %1 -19", 2, "i33_n19"}, + })); + +INSTANTIATE_TEST_SUITE_P( + SimpleFloatConstants, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeFloat 16\n%2 = OpConstant %1 0x1.ff4p+16", 2, + "half_0x1_ff4p_16"}, + {"%1 = OpTypeFloat 16\n%2 = OpConstant %1 -0x1.d2cp-10", 2, + "half_n0x1_d2cpn10"}, + // 32-bit floats + {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -3.125", 2, "float_n3_125"}, + {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1.8p+128", 2, + "float_0x1_8p_128"}, // NaN + {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1.0002p+128", 2, + "float_n0x1_0002p_128"}, // NaN + {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 0x1p+128", 2, + "float_0x1p_128"}, // Inf + {"%1 = OpTypeFloat 32\n%2 = OpConstant %1 -0x1p+128", 2, + "float_n0x1p_128"}, // -Inf + // 64-bit floats + {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -3.125", 2, "double_n3_125"}, + {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.ffffffffffffap-1023", 2, + "double_0x1_ffffffffffffapn1023"}, // small normal + {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.ffffffffffffap-1023", 2, + "double_n0x1_ffffffffffffapn1023"}, + {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1.8p+1024", 2, + "double_0x1_8p_1024"}, // NaN + {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1.0002p+1024", 2, + "double_n0x1_0002p_1024"}, // NaN + {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 0x1p+1024", 2, + "double_0x1p_1024"}, // Inf + {"%1 = OpTypeFloat 64\n%2 = OpConstant %1 -0x1p+1024", 2, + "double_n0x1p_1024"}, // -Inf + })); + +INSTANTIATE_TEST_SUITE_P( + BooleanConstants, FriendlyNameTest, + ::testing::ValuesIn(std::vector{ + {"%1 = OpTypeBool\n%2 = OpConstantTrue %1", 2, "true"}, + {"%1 = OpTypeBool\n%2 = OpConstantFalse %1", 2, "false"}, + })); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/named_id_test.cpp b/third_party/spirv-tools/test/named_id_test.cpp new file mode 100644 index 0000000..01f09be --- /dev/null +++ b/third_party/spirv-tools/test/named_id_test.cpp @@ -0,0 +1,87 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "test/test_fixture.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using NamedIdTest = spvtest::TextToBinaryTest; + +TEST_F(NamedIdTest, Default) { + const std::string input = R"( + OpCapability Shader + OpMemoryModel Logical Simple + OpEntryPoint Vertex %main "foo" + %void = OpTypeVoid +%fnMain = OpTypeFunction %void + %main = OpFunction %void None %fnMain +%lbMain = OpLabel + OpReturn + OpFunctionEnd)"; + const std::string output = + "OpCapability Shader\n" + "OpMemoryModel Logical Simple\n" + "OpEntryPoint Vertex %1 \"foo\"\n" + "%2 = OpTypeVoid\n" + "%3 = OpTypeFunction %2\n" + "%1 = OpFunction %2 None %3\n" + "%4 = OpLabel\n" + "OpReturn\n" + "OpFunctionEnd\n"; + EXPECT_EQ(output, EncodeAndDecodeSuccessfully(input)); +} + +struct IdCheckCase { + std::string id; + bool valid; +}; + +using IdValidityTest = + spvtest::TextToBinaryTestBase<::testing::TestWithParam>; + +TEST_P(IdValidityTest, IdTypes) { + const std::string input = GetParam().id + " = OpTypeVoid"; + SetText(input); + if (GetParam().valid) { + CompileSuccessfully(input); + } else { + CompileFailure(input); + } +} + +INSTANTIATE_TEST_SUITE_P( + ValidAndInvalidIds, IdValidityTest, + ::testing::ValuesIn(std::vector( + {{"%1", true}, {"%2abc", true}, {"%3Def", true}, + {"%4GHI", true}, {"%5_j_k", true}, {"%6J_M", true}, + {"%n", true}, {"%O", true}, {"%p7", true}, + {"%Q8", true}, {"%R_S", true}, {"%T_10_U", true}, + {"%V_11", true}, {"%W_X_13", true}, {"%_A", true}, + {"%_", true}, {"%__", true}, {"%A_", true}, + {"%_A_", true}, + + {"%@", false}, {"%!", false}, {"%ABC!", false}, + {"%__A__@", false}, {"%%", false}, {"%-", false}, + {"%foo_@_bar", false}, {"%", false}, + + {"5", false}, {"32", false}, {"foo", false}, + {"a%bar", false}}))); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opcode_make_test.cpp b/third_party/spirv-tools/test/opcode_make_test.cpp new file mode 100644 index 0000000..6481ef3 --- /dev/null +++ b/third_party/spirv-tools/test/opcode_make_test.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +// A sampling of word counts. Covers extreme points well, and all bit +// positions, and some combinations of bit positions. +const uint16_t kSampleWordCounts[] = { + 0, 1, 2, 3, 4, 8, 16, 32, 64, 127, 128, + 256, 511, 512, 1024, 2048, 4096, 8192, 16384, 32768, 0xfffe, 0xffff}; + +// A sampling of opcode values. Covers the lower values well, a few samples +// around the number of core instructions (as of this writing), and some +// higher values. +const uint16_t kSampleOpcodes[] = {0, 1, 2, 3, 4, 100, + 300, 305, 1023, 0xfffe, 0xffff}; + +TEST(OpcodeMake, Samples) { + for (auto wordCount : kSampleWordCounts) { + for (auto opcode : kSampleOpcodes) { + uint32_t word = 0; + word |= uint32_t(opcode); + word |= uint32_t(wordCount) << 16; + EXPECT_EQ(word, spvOpcodeMake(wordCount, SpvOp(opcode))); + } + } +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opcode_require_capabilities_test.cpp b/third_party/spirv-tools/test/opcode_require_capabilities_test.cpp new file mode 100644 index 0000000..07e86f8 --- /dev/null +++ b/third_party/spirv-tools/test/opcode_require_capabilities_test.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +#include "source/enum_set.h" + +namespace spvtools { +namespace { + +using spvtest::ElementsIn; + +// Capabilities required by an Opcode. +struct ExpectedOpCodeCapabilities { + SpvOp opcode; + CapabilitySet capabilities; +}; + +using OpcodeTableCapabilitiesTest = + ::testing::TestWithParam; + +TEST_P(OpcodeTableCapabilitiesTest, TableEntryMatchesExpectedCapabilities) { + auto env = SPV_ENV_UNIVERSAL_1_1; + spv_opcode_table opcodeTable; + ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableGet(&opcodeTable, env)); + spv_opcode_desc entry; + ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableValueLookup(env, opcodeTable, + GetParam().opcode, &entry)); + EXPECT_EQ( + ElementsIn(GetParam().capabilities), + ElementsIn(CapabilitySet(entry->numCapabilities, entry->capabilities))); +} + +INSTANTIATE_TEST_SUITE_P( + TableRowTest, OpcodeTableCapabilitiesTest, + // Spot-check a few opcodes. + ::testing::Values( + ExpectedOpCodeCapabilities{ + SpvOpImageQuerySize, + CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}}, + ExpectedOpCodeCapabilities{ + SpvOpImageQuerySizeLod, + CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}}, + ExpectedOpCodeCapabilities{ + SpvOpImageQueryLevels, + CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}}, + ExpectedOpCodeCapabilities{ + SpvOpImageQuerySamples, + CapabilitySet{SpvCapabilityKernel, SpvCapabilityImageQuery}}, + ExpectedOpCodeCapabilities{SpvOpImageSparseSampleImplicitLod, + CapabilitySet{SpvCapabilitySparseResidency}}, + ExpectedOpCodeCapabilities{SpvOpCopyMemorySized, + CapabilitySet{SpvCapabilityAddresses}}, + ExpectedOpCodeCapabilities{SpvOpArrayLength, + CapabilitySet{SpvCapabilityShader}}, + ExpectedOpCodeCapabilities{SpvOpFunction, CapabilitySet()}, + ExpectedOpCodeCapabilities{SpvOpConvertFToS, CapabilitySet()}, + ExpectedOpCodeCapabilities{SpvOpEmitStreamVertex, + CapabilitySet{SpvCapabilityGeometryStreams}}, + ExpectedOpCodeCapabilities{SpvOpTypeNamedBarrier, + CapabilitySet{SpvCapabilityNamedBarrier}}, + ExpectedOpCodeCapabilities{ + SpvOpGetKernelMaxNumSubgroups, + CapabilitySet{SpvCapabilitySubgroupDispatch}})); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opcode_split_test.cpp b/third_party/spirv-tools/test/opcode_split_test.cpp new file mode 100644 index 0000000..43fedb3 --- /dev/null +++ b/third_party/spirv-tools/test/opcode_split_test.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +TEST(OpcodeSplit, Default) { + uint32_t word = spvOpcodeMake(42, (SpvOp)23); + uint16_t wordCount = 0; + uint16_t opcode; + spvOpcodeSplit(word, &wordCount, &opcode); + ASSERT_EQ(42, wordCount); + ASSERT_EQ(23, opcode); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opcode_table_get_test.cpp b/third_party/spirv-tools/test/opcode_table_get_test.cpp new file mode 100644 index 0000000..4ff67d9 --- /dev/null +++ b/third_party/spirv-tools/test/opcode_table_get_test.cpp @@ -0,0 +1,39 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gmock/gmock.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using GetTargetOpcodeTableGetTest = ::testing::TestWithParam; +using ::testing::ValuesIn; + +TEST_P(GetTargetOpcodeTableGetTest, IntegrityCheck) { + spv_opcode_table table; + ASSERT_EQ(SPV_SUCCESS, spvOpcodeTableGet(&table, GetParam())); + ASSERT_NE(0u, table->count); + ASSERT_NE(nullptr, table->entries); +} + +TEST_P(GetTargetOpcodeTableGetTest, InvalidPointerTable) { + ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOpcodeTableGet(nullptr, GetParam())); +} + +INSTANTIATE_TEST_SUITE_P(OpcodeTableGet, GetTargetOpcodeTableGetTest, + ValuesIn(spvtest::AllTargetEnvironments())); + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/operand-class-test-coverage.csv b/third_party/spirv-tools/test/operand-class-test-coverage.csv new file mode 100644 index 0000000..116fdfe --- /dev/null +++ b/third_party/spirv-tools/test/operand-class-test-coverage.csv @@ -0,0 +1,43 @@ +Operand class,Example instruction,Notes,example unit test,negative-enum coverage location +" OperandNone,",UNUSED,not in grammar,,not enum +" OperandId,",many,ID,too many to count,not enum +" OperandOptionalId,","Source, Variable",OPTIONAL_ID,OpSourceAcceptsOptionalFileId,not enum +" OperandOptionalImage,",ImageFetch,,ImageOperandsTest,"TEST_F(ImageOperandsTest, WrongOperand)" +" OperandVariableIds,",ExtInst,,,not enum +" OperandOptionalLiteral,",ExecutionMode,,AnyExecutionMode,not enum +" OperandOptionalLiteralString,",Source,,OpSourceAcceptsOptionalSourceText,not enum +" OperandVariableLiterals,",Decorate,,OpDecorateSimpleTest,not enum +" OperandVariableIdLiteral,",GroupMemberDecorate,,GroupMemberDecorate*,not enum +" OperandVariableLiteralId,",Switch,,Switch*,not enum +" OperandLiteralNumber,","Source, Switch, ...",,Switch*,not enum +" OperandLiteralString,",SourceContinued,,OpSourceContinued,not enum +" OperandSource,",Source,,OpSource,not enum +" OperandExecutionModel,",EntryPoint,,OpEntryPointTest,"TEST_F(OpEntryPointTest, WrongModel)" +" OperandAddressing,",OpMemoryModel,,OpMemoryModelTest,"TEST_F(OpMemoryModelTest, WrongModel)" +" OperandMemory,",OpMemoryModel,,OpMemoryModelTest,"TEST_F(OpMemoryModelTest, WrongModel)" +" OperandExecutionMode,",OpExecutionMode,,OpExecutionModeTest,"TEST_F(OpExecutionModeTest, WrongMode)" +" OperandStorage,","TypePointer, TypeForwardPointer, Variable",,StorageClassTest,"TEST_F(OpTypeForwardPointerTest, WrongClass)" +" OperandDimensionality,",TypeImage,,DimTest/AnyDim,"TEST_F(DimTest, WrongDim)" +" OperandSamplerAddressingMode,",ConstantSampler,,SamplerAddressingModeTest,"TEST_F(SamplerAddressingModeTest, WrongMode)" +" OperandSamplerFilterMode,",ConstantSampler,,AnySamplerFilterMode,"TEST_F(SamplerFilterModeTest, WrongMode)" +" OperandSamplerImageFormat,",TypeImage,SAMPLER_IMAGE_FORMAT,ImageFormatTest,"TEST_F(ImageFormatTest, WrongFormat)" +" OperandImageChannelOrder,",UNUSED,returned as result value only,, +" OperandImageChannelDataType,",UNUSED,returned as result value only,, +" OperandImageOperands,",UNUSED,used to make a spec section,,see OperandOptionalImage +" OperandFPFastMath,",OpDecorate,,CombinedFPFastMathMask,"TEST_F(OpDecorateEnumTest, WrongFPFastMathMode)" +" OperandFPRoundingMode,",OpDecorate,,,"TEST_F(OpDecorateEnumTest, WrongFPRoundingMode)" +" OperandLinkageType,",OpDecorate,,OpDecorateLinkageTest,"TEST_F(OpDecorateLinkageTest, WrongType)" +" OperandAccessQualifier,",OpTypePipe,,AnyAccessQualifier,"TEST_F(OpTypePipeTest, WrongAccessQualifier)" +" OperandFuncParamAttr,",OpDecorate,,TextToBinaryDecorateFuncParamAttr,"TEST_F(OpDecorateEnumTest, WrongFuncParamAttr)" +" OperandDecoration,",OpDecorate,,AnyAccessQualifier,"TEST_F(OpTypePipeTest, WrongAccessQualifier)" +" OperandBuiltIn,",OpDecorate,,TextToBinaryDecorateBultIn,"TEST_F(OpDecorateEnumTest, WrongBuiltIn)" +" OperandSelect,",SelectionMerge,,TextToBinarySelectionMerge,"TEST_F(OpSelectionMergeTest, WrongSelectionControl)" +" OperandLoop,",LoopMerge,,CombinedLoopControlMask,"TEST_F(OpLoopMergeTest, WrongLoopControl)" +" OperandFunction,",Function,,AnySingleFunctionControlMask,"TEST_F(OpFunctionControlTest, WrongFunctionControl)" +" OperandMemorySemantics,",OpMemoryBarrier,"it's an ID, not in grammar",OpMemoryBarrier*,not enum +" OperandMemoryAccess,",UNUSED,"should be on opstore, but hacked in opcode.cpp",,not enum +" OperandScope,",MemoryBarrier,"it's an ID, not in grammar",OpMemoryBarrier*,not enum +" OperandGroupOperation,",GroupIAdd,,GroupOperationTest,"TEST_F(GroupOperationTest, WrongGroupOperation)" +" OperandKernelEnqueueFlags,",OpEnqueueKernel,"it's an ID, not in grammar",should not have one,not enum +" OperandKernelProfilingInfo,",OpCaptureEventProfilingInfo,"it's an ID, not in grammar",should not have one,not enum +" OperandCapability,",Capability,,OpCapabilityTest,"TEST_F(TextToBinaryCapability, BadInvalidCapability)" diff --git a/third_party/spirv-tools/test/operand_capabilities_test.cpp b/third_party/spirv-tools/test/operand_capabilities_test.cpp new file mode 100644 index 0000000..addb08a --- /dev/null +++ b/third_party/spirv-tools/test/operand_capabilities_test.cpp @@ -0,0 +1,755 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Test capability dependencies for enums. + +#include +#include + +#include "gmock/gmock.h" +#include "source/enum_set.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using spvtest::ElementsIn; +using ::testing::Combine; +using ::testing::Eq; +using ::testing::TestWithParam; +using ::testing::Values; +using ::testing::ValuesIn; + +// A test case for mapping an enum to a capability mask. +struct EnumCapabilityCase { + spv_operand_type_t type; + uint32_t value; + CapabilitySet expected_capabilities; +}; + +// Test fixture for testing EnumCapabilityCases. +using EnumCapabilityTest = + TestWithParam>; + +TEST_P(EnumCapabilityTest, Sample) { + const auto env = std::get<0>(GetParam()); + const auto context = spvContextCreate(env); + const AssemblyGrammar grammar(context); + spv_operand_desc entry; + + ASSERT_EQ(SPV_SUCCESS, + grammar.lookupOperand(std::get<1>(GetParam()).type, + std::get<1>(GetParam()).value, &entry)); + const auto cap_set = grammar.filterCapsAgainstTargetEnv( + entry->capabilities, entry->numCapabilities); + + EXPECT_THAT(ElementsIn(cap_set), + Eq(ElementsIn(std::get<1>(GetParam()).expected_capabilities))) + << " capability value " << std::get<1>(GetParam()).value; + spvContextDestroy(context); +} + +#define CASE0(TYPE, VALUE) \ + { \ + SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), {} \ + } +#define CASE1(TYPE, VALUE, CAP) \ + { \ + SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \ + SpvCapability##CAP \ + } \ + } +#define CASE2(TYPE, VALUE, CAP1, CAP2) \ + { \ + SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \ + SpvCapability##CAP1, SpvCapability##CAP2 \ + } \ + } +#define CASE3(TYPE, VALUE, CAP1, CAP2, CAP3) \ + { \ + SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \ + SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3 \ + } \ + } +#define CASE4(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4) \ + { \ + SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \ + SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \ + SpvCapability##CAP4 \ + } \ + } +#define CASE5(TYPE, VALUE, CAP1, CAP2, CAP3, CAP4, CAP5) \ + { \ + SPV_OPERAND_TYPE_##TYPE, uint32_t(Spv##VALUE), CapabilitySet { \ + SpvCapability##CAP1, SpvCapability##CAP2, SpvCapability##CAP3, \ + SpvCapability##CAP4, SpvCapability##CAP5 \ + } \ + } + +// See SPIR-V Section 3.3 Execution Model +INSTANTIATE_TEST_SUITE_P( + ExecutionModel, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(EXECUTION_MODEL, ExecutionModelVertex, Shader), + CASE1(EXECUTION_MODEL, ExecutionModelTessellationControl, + Tessellation), + CASE1(EXECUTION_MODEL, ExecutionModelTessellationEvaluation, + Tessellation), + CASE1(EXECUTION_MODEL, ExecutionModelGeometry, Geometry), + CASE1(EXECUTION_MODEL, ExecutionModelFragment, Shader), + CASE1(EXECUTION_MODEL, ExecutionModelGLCompute, Shader), + CASE1(EXECUTION_MODEL, ExecutionModelKernel, Kernel), + }))); + +// See SPIR-V Section 3.4 Addressing Model +INSTANTIATE_TEST_SUITE_P( + AddressingModel, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(ADDRESSING_MODEL, AddressingModelLogical), + CASE1(ADDRESSING_MODEL, AddressingModelPhysical32, Addresses), + CASE1(ADDRESSING_MODEL, AddressingModelPhysical64, Addresses), + }))); + +// See SPIR-V Section 3.5 Memory Model +INSTANTIATE_TEST_SUITE_P( + MemoryModel, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(MEMORY_MODEL, MemoryModelSimple, Shader), + CASE1(MEMORY_MODEL, MemoryModelGLSL450, Shader), + CASE1(MEMORY_MODEL, MemoryModelOpenCL, Kernel), + }))); + +// See SPIR-V Section 3.6 Execution Mode +INSTANTIATE_TEST_SUITE_P( + ExecutionMode, EnumCapabilityTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(EXECUTION_MODE, ExecutionModeInvocations, Geometry), + CASE1(EXECUTION_MODE, ExecutionModeSpacingEqual, Tessellation), + CASE1(EXECUTION_MODE, ExecutionModeSpacingFractionalEven, + Tessellation), + CASE1(EXECUTION_MODE, ExecutionModeSpacingFractionalOdd, + Tessellation), + CASE1(EXECUTION_MODE, ExecutionModeVertexOrderCw, Tessellation), + CASE1(EXECUTION_MODE, ExecutionModeVertexOrderCcw, Tessellation), + CASE1(EXECUTION_MODE, ExecutionModePixelCenterInteger, Shader), + CASE1(EXECUTION_MODE, ExecutionModeOriginUpperLeft, Shader), + CASE1(EXECUTION_MODE, ExecutionModeOriginLowerLeft, Shader), + CASE1(EXECUTION_MODE, ExecutionModeEarlyFragmentTests, Shader), + CASE1(EXECUTION_MODE, ExecutionModePointMode, Tessellation), + CASE1(EXECUTION_MODE, ExecutionModeXfb, TransformFeedback), + CASE1(EXECUTION_MODE, ExecutionModeDepthReplacing, Shader), + CASE1(EXECUTION_MODE, ExecutionModeDepthGreater, Shader), + CASE1(EXECUTION_MODE, ExecutionModeDepthLess, Shader), + CASE1(EXECUTION_MODE, ExecutionModeDepthUnchanged, Shader), + CASE0(EXECUTION_MODE, ExecutionModeLocalSize), + CASE1(EXECUTION_MODE, ExecutionModeLocalSizeHint, Kernel), + CASE1(EXECUTION_MODE, ExecutionModeInputPoints, Geometry), + CASE1(EXECUTION_MODE, ExecutionModeInputLines, Geometry), + CASE1(EXECUTION_MODE, ExecutionModeInputLinesAdjacency, Geometry), + CASE2(EXECUTION_MODE, ExecutionModeTriangles, Geometry, + Tessellation), + CASE1(EXECUTION_MODE, ExecutionModeInputTrianglesAdjacency, + Geometry), + CASE1(EXECUTION_MODE, ExecutionModeQuads, Tessellation), + CASE1(EXECUTION_MODE, ExecutionModeIsolines, Tessellation), + CASE3(EXECUTION_MODE, ExecutionModeOutputVertices, Geometry, + Tessellation, MeshShadingNV), + CASE2(EXECUTION_MODE, ExecutionModeOutputPoints, Geometry, + MeshShadingNV), + CASE1(EXECUTION_MODE, ExecutionModeOutputLineStrip, Geometry), + CASE1(EXECUTION_MODE, ExecutionModeOutputTriangleStrip, Geometry), + CASE1(EXECUTION_MODE, ExecutionModeVecTypeHint, Kernel), + CASE1(EXECUTION_MODE, ExecutionModeContractionOff, Kernel), + }))); + +INSTANTIATE_TEST_SUITE_P( + ExecutionModeV11, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(EXECUTION_MODE, ExecutionModeInitializer, Kernel), + CASE1(EXECUTION_MODE, ExecutionModeFinalizer, Kernel), + CASE1(EXECUTION_MODE, ExecutionModeSubgroupSize, + SubgroupDispatch), + CASE1(EXECUTION_MODE, ExecutionModeSubgroupsPerWorkgroup, + SubgroupDispatch)}))); + +// See SPIR-V Section 3.7 Storage Class +INSTANTIATE_TEST_SUITE_P( + StorageClass, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(STORAGE_CLASS, StorageClassUniformConstant), + CASE1(STORAGE_CLASS, StorageClassUniform, Shader), + CASE1(STORAGE_CLASS, StorageClassOutput, Shader), + CASE0(STORAGE_CLASS, StorageClassWorkgroup), + CASE0(STORAGE_CLASS, StorageClassCrossWorkgroup), + CASE1(STORAGE_CLASS, StorageClassPrivate, Shader), + CASE0(STORAGE_CLASS, StorageClassFunction), + CASE1(STORAGE_CLASS, StorageClassGeneric, + GenericPointer), // Bug 14287 + CASE1(STORAGE_CLASS, StorageClassPushConstant, Shader), + CASE1(STORAGE_CLASS, StorageClassAtomicCounter, AtomicStorage), + CASE0(STORAGE_CLASS, StorageClassImage), + }))); + +// See SPIR-V Section 3.8 Dim +INSTANTIATE_TEST_SUITE_P( + Dim, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE2(DIMENSIONALITY, Dim1D, Sampled1D, Image1D), + CASE3(DIMENSIONALITY, Dim2D, Kernel, Shader, ImageMSArray), + CASE0(DIMENSIONALITY, Dim3D), + CASE2(DIMENSIONALITY, DimCube, Shader, ImageCubeArray), + CASE2(DIMENSIONALITY, DimRect, SampledRect, ImageRect), + CASE2(DIMENSIONALITY, DimBuffer, SampledBuffer, ImageBuffer), + CASE1(DIMENSIONALITY, DimSubpassData, InputAttachment), + }))); + +// See SPIR-V Section 3.9 Sampler Addressing Mode +INSTANTIATE_TEST_SUITE_P( + SamplerAddressingMode, EnumCapabilityTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeNone, Kernel), + CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeClampToEdge, + Kernel), + CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeClamp, Kernel), + CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeRepeat, Kernel), + CASE1(SAMPLER_ADDRESSING_MODE, SamplerAddressingModeRepeatMirrored, + Kernel), + }))); + +// See SPIR-V Section 3.10 Sampler Filter Mode +INSTANTIATE_TEST_SUITE_P( + SamplerFilterMode, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(SAMPLER_FILTER_MODE, SamplerFilterModeNearest, Kernel), + CASE1(SAMPLER_FILTER_MODE, SamplerFilterModeLinear, Kernel), + }))); + +// See SPIR-V Section 3.11 Image Format +INSTANTIATE_TEST_SUITE_P( + ImageFormat, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + // clang-format off + CASE0(SAMPLER_IMAGE_FORMAT, ImageFormatUnknown), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32f, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16f, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR32f, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8Snorm, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32f, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16f, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR11fG11fB10f, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16f, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgb10A2, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16Snorm, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16Snorm, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8Snorm, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16Snorm, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8Snorm, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32i, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16i, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8i, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR32i, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32i, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16i, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8i, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16i, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8i, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba32ui, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba16ui, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8ui, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgba8ui, Shader), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRgb10a2ui, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg32ui, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg16ui, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatRg8ui, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR16ui, StorageImageExtendedFormats), + CASE1(SAMPLER_IMAGE_FORMAT, ImageFormatR8ui, StorageImageExtendedFormats), + // clang-format on + }))); + +// See SPIR-V Section 3.12 Image Channel Order +INSTANTIATE_TEST_SUITE_P( + ImageChannelOrder, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderR, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRG, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGB, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderBGRA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderARGB, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderIntensity, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderLuminance, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRx, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGx, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderRGBx, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepth, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderDepthStencil, + Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGB, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBx, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersRGBA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrdersBGRA, Kernel), + CASE1(IMAGE_CHANNEL_ORDER, ImageChannelOrderABGR, Kernel), + }))); + +// See SPIR-V Section 3.13 Image Channel Data Type +INSTANTIATE_TEST_SUITE_P( + ImageChannelDataType, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + // clang-format off + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSnormInt8, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSnormInt16, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt8, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt16, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormShort565, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormShort555, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt101010, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt8, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt16, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeSignedInt32, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt8, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt16, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnsignedInt32, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeHalfFloat, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeFloat, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt24, Kernel), + CASE1(IMAGE_CHANNEL_DATA_TYPE, ImageChannelDataTypeUnormInt101010_2, Kernel), + // clang-format on + }))); + +// See SPIR-V Section 3.14 Image Operands +INSTANTIATE_TEST_SUITE_P( + ImageOperands, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + // clang-format off + CASE0(OPTIONAL_IMAGE, ImageOperandsMaskNone), + CASE1(OPTIONAL_IMAGE, ImageOperandsBiasMask, Shader), + CASE0(OPTIONAL_IMAGE, ImageOperandsLodMask), + CASE0(OPTIONAL_IMAGE, ImageOperandsGradMask), + CASE0(OPTIONAL_IMAGE, ImageOperandsConstOffsetMask), + CASE1(OPTIONAL_IMAGE, ImageOperandsOffsetMask, ImageGatherExtended), + CASE1(OPTIONAL_IMAGE, ImageOperandsConstOffsetsMask, ImageGatherExtended), + CASE0(OPTIONAL_IMAGE, ImageOperandsSampleMask), + CASE1(OPTIONAL_IMAGE, ImageOperandsMinLodMask, MinLod), + // clang-format on + }))); + +// See SPIR-V Section 3.15 FP Fast Math Mode +INSTANTIATE_TEST_SUITE_P( + FPFastMathMode, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(FP_FAST_MATH_MODE, FPFastMathModeMaskNone), + CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotNaNMask, Kernel), + CASE1(FP_FAST_MATH_MODE, FPFastMathModeNotInfMask, Kernel), + CASE1(FP_FAST_MATH_MODE, FPFastMathModeNSZMask, Kernel), + CASE1(FP_FAST_MATH_MODE, FPFastMathModeAllowRecipMask, Kernel), + CASE1(FP_FAST_MATH_MODE, FPFastMathModeFastMask, Kernel), + }))); + +// See SPIR-V Section 3.17 Linkage Type +INSTANTIATE_TEST_SUITE_P( + LinkageType, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(LINKAGE_TYPE, LinkageTypeExport, Linkage), + CASE1(LINKAGE_TYPE, LinkageTypeImport, Linkage), + }))); + +// See SPIR-V Section 3.18 Access Qualifier +INSTANTIATE_TEST_SUITE_P( + AccessQualifier, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(ACCESS_QUALIFIER, AccessQualifierReadOnly, Kernel), + CASE1(ACCESS_QUALIFIER, AccessQualifierWriteOnly, Kernel), + CASE1(ACCESS_QUALIFIER, AccessQualifierReadWrite, Kernel), + }))); + +// See SPIR-V Section 3.19 Function Parameter Attribute +INSTANTIATE_TEST_SUITE_P( + FunctionParameterAttribute, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + // clang-format off + CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeZext, Kernel), + CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeSext, Kernel), + CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeByVal, Kernel), + CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeSret, Kernel), + CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoAlias, Kernel), + CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoCapture, Kernel), + CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoWrite, Kernel), + CASE1(FUNCTION_PARAMETER_ATTRIBUTE, FunctionParameterAttributeNoReadWrite, Kernel), + // clang-format on + }))); + +// See SPIR-V Section 3.20 Decoration +INSTANTIATE_TEST_SUITE_P( + Decoration, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(DECORATION, DecorationRelaxedPrecision, Shader), + // DecorationSpecId handled below. + CASE1(DECORATION, DecorationBlock, Shader), + CASE1(DECORATION, DecorationBufferBlock, Shader), + CASE1(DECORATION, DecorationRowMajor, Matrix), + CASE1(DECORATION, DecorationColMajor, Matrix), + CASE1(DECORATION, DecorationArrayStride, Shader), + CASE1(DECORATION, DecorationMatrixStride, Matrix), // Bug 15234 + CASE1(DECORATION, DecorationGLSLShared, Shader), + CASE1(DECORATION, DecorationGLSLPacked, Shader), + CASE1(DECORATION, DecorationCPacked, Kernel), + CASE0(DECORATION, DecorationBuiltIn), // Bug 15248 + // Value 12 placeholder + CASE1(DECORATION, DecorationNoPerspective, Shader), + CASE1(DECORATION, DecorationFlat, Shader), + CASE1(DECORATION, DecorationPatch, Tessellation), + CASE1(DECORATION, DecorationCentroid, Shader), + CASE1(DECORATION, DecorationSample, + SampleRateShading), // Bug 15234 + CASE1(DECORATION, DecorationInvariant, Shader), + CASE0(DECORATION, DecorationRestrict), + CASE0(DECORATION, DecorationAliased), + CASE0(DECORATION, DecorationVolatile), + CASE1(DECORATION, DecorationConstant, Kernel), + CASE0(DECORATION, DecorationCoherent), + CASE0(DECORATION, DecorationNonWritable), + CASE0(DECORATION, DecorationNonReadable), + CASE1(DECORATION, DecorationUniform, Shader), + // Value 27 is an intentional gap in the spec numbering. + CASE1(DECORATION, DecorationSaturatedConversion, Kernel), + CASE1(DECORATION, DecorationStream, GeometryStreams), + CASE1(DECORATION, DecorationLocation, Shader), + CASE1(DECORATION, DecorationComponent, Shader), + CASE1(DECORATION, DecorationIndex, Shader), + CASE1(DECORATION, DecorationBinding, Shader), + CASE1(DECORATION, DecorationDescriptorSet, Shader), + CASE1(DECORATION, DecorationOffset, Shader), // Bug 15268 + CASE1(DECORATION, DecorationXfbBuffer, TransformFeedback), + CASE1(DECORATION, DecorationXfbStride, TransformFeedback), + CASE1(DECORATION, DecorationFuncParamAttr, Kernel), + CASE1(DECORATION, DecorationFPFastMathMode, Kernel), + CASE1(DECORATION, DecorationLinkageAttributes, Linkage), + CASE1(DECORATION, DecorationNoContraction, Shader), + CASE1(DECORATION, DecorationInputAttachmentIndex, + InputAttachment), + CASE1(DECORATION, DecorationAlignment, Kernel), + }))); + +#if 0 +// SpecId has different requirements in v1.0 and v1.1: +INSTANTIATE_TEST_SUITE_P(DecorationSpecIdV10, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0), + ValuesIn(std::vector{CASE1( + DECORATION, DecorationSpecId, Shader)}))); +#endif + +INSTANTIATE_TEST_SUITE_P( + DecorationV11, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE2(DECORATION, DecorationSpecId, Shader, Kernel), + CASE1(DECORATION, DecorationMaxByteOffset, Addresses)}))); + +// See SPIR-V Section 3.21 BuiltIn +INSTANTIATE_TEST_SUITE_P( + BuiltIn, EnumCapabilityTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + // clang-format off + CASE1(BUILT_IN, BuiltInPosition, Shader), + CASE1(BUILT_IN, BuiltInPointSize, Shader), + // 2 is an intentional gap in the spec numbering. + CASE1(BUILT_IN, BuiltInClipDistance, ClipDistance), // Bug 1407, 15234 + CASE1(BUILT_IN, BuiltInCullDistance, CullDistance), // Bug 1407, 15234 + CASE1(BUILT_IN, BuiltInVertexId, Shader), + CASE1(BUILT_IN, BuiltInInstanceId, Shader), + CASE4(BUILT_IN, BuiltInPrimitiveId, Geometry, Tessellation, + RayTracingNV, RayTracingProvisionalKHR), + CASE2(BUILT_IN, BuiltInInvocationId, Geometry, Tessellation), + CASE2(BUILT_IN, BuiltInLayer, Geometry, ShaderViewportIndexLayerEXT), + CASE2(BUILT_IN, BuiltInViewportIndex, MultiViewport, ShaderViewportIndexLayerEXT), // Bug 15234 + CASE1(BUILT_IN, BuiltInTessLevelOuter, Tessellation), + CASE1(BUILT_IN, BuiltInTessLevelInner, Tessellation), + CASE1(BUILT_IN, BuiltInTessCoord, Tessellation), + CASE1(BUILT_IN, BuiltInPatchVertices, Tessellation), + CASE1(BUILT_IN, BuiltInFragCoord, Shader), + CASE1(BUILT_IN, BuiltInPointCoord, Shader), + CASE1(BUILT_IN, BuiltInFrontFacing, Shader), + CASE1(BUILT_IN, BuiltInSampleId, SampleRateShading), // Bug 15234 + CASE1(BUILT_IN, BuiltInSamplePosition, SampleRateShading), // Bug 15234 + CASE1(BUILT_IN, BuiltInSampleMask, Shader), // Bug 15234, Issue 182 + // Value 21 intentionally missing + CASE1(BUILT_IN, BuiltInFragDepth, Shader), + CASE1(BUILT_IN, BuiltInHelperInvocation, Shader), + CASE0(BUILT_IN, BuiltInNumWorkgroups), + CASE0(BUILT_IN, BuiltInWorkgroupSize), + CASE0(BUILT_IN, BuiltInWorkgroupId), + CASE0(BUILT_IN, BuiltInLocalInvocationId), + CASE0(BUILT_IN, BuiltInGlobalInvocationId), + CASE0(BUILT_IN, BuiltInLocalInvocationIndex), + CASE1(BUILT_IN, BuiltInWorkDim, Kernel), + CASE1(BUILT_IN, BuiltInGlobalSize, Kernel), + CASE1(BUILT_IN, BuiltInEnqueuedWorkgroupSize, Kernel), + CASE1(BUILT_IN, BuiltInGlobalOffset, Kernel), + CASE1(BUILT_IN, BuiltInGlobalLinearId, Kernel), + // Value 35 intentionally missing + CASE2(BUILT_IN, BuiltInSubgroupSize, Kernel, SubgroupBallotKHR), + CASE1(BUILT_IN, BuiltInSubgroupMaxSize, Kernel), + CASE1(BUILT_IN, BuiltInNumSubgroups, Kernel), + CASE1(BUILT_IN, BuiltInNumEnqueuedSubgroups, Kernel), + CASE1(BUILT_IN, BuiltInSubgroupId, Kernel), + CASE2(BUILT_IN, BuiltInSubgroupLocalInvocationId, Kernel, SubgroupBallotKHR), + CASE1(BUILT_IN, BuiltInVertexIndex, Shader), + CASE1(BUILT_IN, BuiltInInstanceIndex, Shader), + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + BuiltInV1_5, EnumCapabilityTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_5), + ValuesIn(std::vector{ + // SPIR-V 1.5 adds new capabilities to enable these two builtins. + CASE3(BUILT_IN, BuiltInLayer, Geometry, ShaderLayer, + ShaderViewportIndexLayerEXT), + CASE3(BUILT_IN, BuiltInViewportIndex, MultiViewport, + ShaderViewportIndex, ShaderViewportIndexLayerEXT), + }))); + +// See SPIR-V Section 3.22 Selection Control +INSTANTIATE_TEST_SUITE_P( + SelectionControl, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(SELECTION_CONTROL, SelectionControlMaskNone), + CASE0(SELECTION_CONTROL, SelectionControlFlattenMask), + CASE0(SELECTION_CONTROL, SelectionControlDontFlattenMask), + }))); + +// See SPIR-V Section 3.23 Loop Control +INSTANTIATE_TEST_SUITE_P( + LoopControl, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(LOOP_CONTROL, LoopControlMaskNone), + CASE0(LOOP_CONTROL, LoopControlUnrollMask), + CASE0(LOOP_CONTROL, LoopControlDontUnrollMask), + }))); + +INSTANTIATE_TEST_SUITE_P( + LoopControlV11, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(LOOP_CONTROL, LoopControlDependencyInfiniteMask), + CASE0(LOOP_CONTROL, LoopControlDependencyLengthMask), + }))); + +// See SPIR-V Section 3.24 Function Control +INSTANTIATE_TEST_SUITE_P( + FunctionControl, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(FUNCTION_CONTROL, FunctionControlMaskNone), + CASE0(FUNCTION_CONTROL, FunctionControlInlineMask), + CASE0(FUNCTION_CONTROL, FunctionControlDontInlineMask), + CASE0(FUNCTION_CONTROL, FunctionControlPureMask), + CASE0(FUNCTION_CONTROL, FunctionControlConstMask), + }))); + +// See SPIR-V Section 3.25 Memory Semantics +INSTANTIATE_TEST_SUITE_P( + MemorySemantics, EnumCapabilityTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsMaskNone), + CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsAcquireMask), + CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsReleaseMask), + CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsAcquireReleaseMask), + CASE0(MEMORY_SEMANTICS_ID, + MemorySemanticsSequentiallyConsistentMask), + CASE1(MEMORY_SEMANTICS_ID, MemorySemanticsUniformMemoryMask, + Shader), + CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsSubgroupMemoryMask), + CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsWorkgroupMemoryMask), + CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsCrossWorkgroupMemoryMask), + CASE1(MEMORY_SEMANTICS_ID, MemorySemanticsAtomicCounterMemoryMask, + AtomicStorage), // Bug 15234 + CASE0(MEMORY_SEMANTICS_ID, MemorySemanticsImageMemoryMask), + }))); + +// See SPIR-V Section 3.26 Memory Access +INSTANTIATE_TEST_SUITE_P( + MemoryAccess, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessMaskNone), + CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessVolatileMask), + CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessAlignedMask), + CASE0(OPTIONAL_MEMORY_ACCESS, MemoryAccessNontemporalMask), + }))); + +// See SPIR-V Section 3.27 Scope +INSTANTIATE_TEST_SUITE_P( + Scope, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_UNIVERSAL_1_2, SPV_ENV_UNIVERSAL_1_3), + ValuesIn(std::vector{ + CASE0(SCOPE_ID, ScopeCrossDevice), + CASE0(SCOPE_ID, ScopeDevice), + CASE0(SCOPE_ID, ScopeWorkgroup), + CASE0(SCOPE_ID, ScopeSubgroup), + CASE0(SCOPE_ID, ScopeInvocation), + CASE1(SCOPE_ID, ScopeQueueFamilyKHR, VulkanMemoryModelKHR), + }))); + +// See SPIR-V Section 3.28 Group Operation +INSTANTIATE_TEST_SUITE_P( + GroupOperation, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE3(GROUP_OPERATION, GroupOperationReduce, Kernel, + GroupNonUniformArithmetic, GroupNonUniformBallot), + CASE3(GROUP_OPERATION, GroupOperationInclusiveScan, Kernel, + GroupNonUniformArithmetic, GroupNonUniformBallot), + CASE3(GROUP_OPERATION, GroupOperationExclusiveScan, Kernel, + GroupNonUniformArithmetic, GroupNonUniformBallot), + }))); + +// See SPIR-V Section 3.29 Kernel Enqueue Flags +INSTANTIATE_TEST_SUITE_P( + KernelEnqueueFlags, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsNoWait, Kernel), + CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsWaitKernel, Kernel), + CASE1(KERNEL_ENQ_FLAGS, KernelEnqueueFlagsWaitWorkGroup, + Kernel), + }))); + +// See SPIR-V Section 3.30 Kernel Profiling Info +INSTANTIATE_TEST_SUITE_P( + KernelProfilingInfo, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE0(KERNEL_PROFILING_INFO, KernelProfilingInfoMaskNone), + CASE1(KERNEL_PROFILING_INFO, KernelProfilingInfoCmdExecTimeMask, + Kernel), + }))); + +// See SPIR-V Section 3.31 Capability +INSTANTIATE_TEST_SUITE_P( + CapabilityDependsOn, EnumCapabilityTest, + Combine( + Values(SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + // clang-format off + CASE0(CAPABILITY, CapabilityMatrix), + CASE1(CAPABILITY, CapabilityShader, Matrix), + CASE1(CAPABILITY, CapabilityGeometry, Shader), + CASE1(CAPABILITY, CapabilityTessellation, Shader), + CASE0(CAPABILITY, CapabilityAddresses), + CASE0(CAPABILITY, CapabilityLinkage), + CASE0(CAPABILITY, CapabilityKernel), + CASE1(CAPABILITY, CapabilityVector16, Kernel), + CASE1(CAPABILITY, CapabilityFloat16Buffer, Kernel), + CASE0(CAPABILITY, CapabilityFloat16), // Bug 15234 + CASE0(CAPABILITY, CapabilityFloat64), + CASE0(CAPABILITY, CapabilityInt64), + CASE1(CAPABILITY, CapabilityInt64Atomics, Int64), + CASE1(CAPABILITY, CapabilityImageBasic, Kernel), + CASE1(CAPABILITY, CapabilityImageReadWrite, ImageBasic), + CASE1(CAPABILITY, CapabilityImageMipmap, ImageBasic), + // Value 16 intentionally missing. + CASE1(CAPABILITY, CapabilityPipes, Kernel), + CASE0(CAPABILITY, CapabilityGroups), + CASE1(CAPABILITY, CapabilityDeviceEnqueue, Kernel), + CASE1(CAPABILITY, CapabilityLiteralSampler, Kernel), + CASE1(CAPABILITY, CapabilityAtomicStorage, Shader), + CASE0(CAPABILITY, CapabilityInt16), + CASE1(CAPABILITY, CapabilityTessellationPointSize, Tessellation), + CASE1(CAPABILITY, CapabilityGeometryPointSize, Geometry), + CASE1(CAPABILITY, CapabilityImageGatherExtended, Shader), + // Value 26 intentionally missing. + CASE1(CAPABILITY, CapabilityStorageImageMultisample, Shader), + CASE1(CAPABILITY, CapabilityUniformBufferArrayDynamicIndexing, Shader), + CASE1(CAPABILITY, CapabilitySampledImageArrayDynamicIndexing, Shader), + CASE1(CAPABILITY, CapabilityStorageBufferArrayDynamicIndexing, Shader), + CASE1(CAPABILITY, CapabilityStorageImageArrayDynamicIndexing, Shader), + CASE1(CAPABILITY, CapabilityClipDistance, Shader), + CASE1(CAPABILITY, CapabilityCullDistance, Shader), + CASE1(CAPABILITY, CapabilityImageCubeArray, SampledCubeArray), + CASE1(CAPABILITY, CapabilitySampleRateShading, Shader), + CASE1(CAPABILITY, CapabilityImageRect, SampledRect), + CASE1(CAPABILITY, CapabilitySampledRect, Shader), + CASE1(CAPABILITY, CapabilityGenericPointer, Addresses), + CASE0(CAPABILITY, CapabilityInt8), + CASE1(CAPABILITY, CapabilityInputAttachment, Shader), + CASE1(CAPABILITY, CapabilitySparseResidency, Shader), + CASE1(CAPABILITY, CapabilityMinLod, Shader), + CASE1(CAPABILITY, CapabilityImage1D, Sampled1D), + CASE1(CAPABILITY, CapabilitySampledCubeArray, Shader), + CASE1(CAPABILITY, CapabilityImageBuffer, SampledBuffer), + CASE1(CAPABILITY, CapabilityImageMSArray, Shader), + CASE1(CAPABILITY, CapabilityStorageImageExtendedFormats, Shader), + CASE1(CAPABILITY, CapabilityImageQuery, Shader), + CASE1(CAPABILITY, CapabilityDerivativeControl, Shader), + CASE1(CAPABILITY, CapabilityInterpolationFunction, Shader), + CASE1(CAPABILITY, CapabilityTransformFeedback, Shader), + CASE1(CAPABILITY, CapabilityGeometryStreams, Geometry), + CASE1(CAPABILITY, CapabilityStorageImageReadWithoutFormat, Shader), + CASE1(CAPABILITY, CapabilityStorageImageWriteWithoutFormat, Shader), + CASE1(CAPABILITY, CapabilityMultiViewport, Geometry), + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + CapabilityDependsOnV11, EnumCapabilityTest, + Combine(Values(SPV_ENV_UNIVERSAL_1_1), + ValuesIn(std::vector{ + CASE1(CAPABILITY, CapabilitySubgroupDispatch, DeviceEnqueue), + CASE1(CAPABILITY, CapabilityNamedBarrier, Kernel), + CASE1(CAPABILITY, CapabilityPipeStorage, Pipes), + }))); + +#undef CASE0 +#undef CASE1 +#undef CASE2 + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/operand_pattern_test.cpp b/third_party/spirv-tools/test/operand_pattern_test.cpp new file mode 100644 index 0000000..1caf008 --- /dev/null +++ b/third_party/spirv-tools/test/operand_pattern_test.cpp @@ -0,0 +1,270 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "source/operand.h" +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using ::testing::Eq; + +TEST(OperandPattern, InitiallyEmpty) { + spv_operand_pattern_t empty; + EXPECT_THAT(empty, Eq(spv_operand_pattern_t{})); + EXPECT_EQ(0u, empty.size()); + EXPECT_TRUE(empty.empty()); +} + +TEST(OperandPattern, PushBacksAreOnTheRight) { + spv_operand_pattern_t pattern; + + pattern.push_back(SPV_OPERAND_TYPE_ID); + EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID})); + EXPECT_EQ(1u, pattern.size()); + EXPECT_TRUE(!pattern.empty()); + EXPECT_EQ(SPV_OPERAND_TYPE_ID, pattern.back()); + + pattern.push_back(SPV_OPERAND_TYPE_NONE); + EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_NONE})); + EXPECT_EQ(2u, pattern.size()); + EXPECT_TRUE(!pattern.empty()); + EXPECT_EQ(SPV_OPERAND_TYPE_NONE, pattern.back()); +} + +TEST(OperandPattern, PopBacksAreOnTheRight) { + spv_operand_pattern_t pattern{SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_LITERAL_INTEGER}; + + pattern.pop_back(); + EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_ID})); + + pattern.pop_back(); + EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{})); +} + +// A test case for typed mask expansion +struct MaskExpansionCase { + spv_operand_type_t type; + uint32_t mask; + spv_operand_pattern_t initial; + spv_operand_pattern_t expected; +}; + +using MaskExpansionTest = ::testing::TestWithParam; + +TEST_P(MaskExpansionTest, Sample) { + spv_operand_table operandTable = nullptr; + auto env = SPV_ENV_UNIVERSAL_1_0; + ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&operandTable, env)); + + spv_operand_pattern_t pattern(GetParam().initial); + spvPushOperandTypesForMask(env, operandTable, GetParam().type, + GetParam().mask, &pattern); + EXPECT_THAT(pattern, Eq(GetParam().expected)); +} + +// These macros let us write non-trivial examples without too much text. +#define PREFIX0 SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_NONE +#define PREFIX1 \ + SPV_OPERAND_TYPE_STORAGE_CLASS, SPV_OPERAND_TYPE_SAMPLER_FILTER_MODE, \ + SPV_OPERAND_TYPE_ID +INSTANTIATE_TEST_SUITE_P( + OperandPattern, MaskExpansionTest, + ::testing::ValuesIn(std::vector{ + // No bits means no change. + {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, 0, {PREFIX0}, {PREFIX0}}, + // Unknown bits means no change. Use all bits that aren't in the + // grammar. + // The last mask enum is 0x20 + {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, + 0xffffffc0, + {PREFIX1}, + {PREFIX1}}, + // Volatile has no operands. + {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, + SpvMemoryAccessVolatileMask, + {PREFIX0}, + {PREFIX0}}, + // Aligned has one literal number operand. + {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, + SpvMemoryAccessAlignedMask, + {PREFIX1}, + {PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}}, + // Volatile with Aligned still has just one literal number operand. + {SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS, + SpvMemoryAccessVolatileMask | SpvMemoryAccessAlignedMask, + {PREFIX1}, + {PREFIX1, SPV_OPERAND_TYPE_LITERAL_INTEGER}}, + })); +#undef PREFIX0 +#undef PREFIX1 + +// Returns a vector of all operand types that can be used in a pattern. +std::vector allOperandTypes() { + std::vector result; + for (int i = 0; i < SPV_OPERAND_TYPE_NUM_OPERAND_TYPES; i++) { + result.push_back(spv_operand_type_t(i)); + } + return result; +} + +using MatchableOperandExpansionTest = + ::testing::TestWithParam; + +TEST_P(MatchableOperandExpansionTest, MatchableOperandsDontExpand) { + const spv_operand_type_t type = GetParam(); + if (!spvOperandIsVariable(type)) { + spv_operand_pattern_t pattern; + const bool did_expand = spvExpandOperandSequenceOnce(type, &pattern); + EXPECT_FALSE(did_expand); + EXPECT_THAT(pattern, Eq(spv_operand_pattern_t{})); + } +} + +INSTANTIATE_TEST_SUITE_P(MatchableOperandExpansion, + MatchableOperandExpansionTest, + ::testing::ValuesIn(allOperandTypes())); + +using VariableOperandExpansionTest = + ::testing::TestWithParam; + +TEST_P(VariableOperandExpansionTest, NonMatchableOperandsExpand) { + const spv_operand_type_t type = GetParam(); + if (spvOperandIsVariable(type)) { + spv_operand_pattern_t pattern; + const bool did_expand = spvExpandOperandSequenceOnce(type, &pattern); + EXPECT_TRUE(did_expand); + EXPECT_FALSE(pattern.empty()); + // For the existing rules, the first expansion of a zero-or-more operand + // type yields a matchable operand type. This isn't strictly necessary. + EXPECT_FALSE(spvOperandIsVariable(pattern.back())); + } +} + +INSTANTIATE_TEST_SUITE_P(NonMatchableOperandExpansion, + VariableOperandExpansionTest, + ::testing::ValuesIn(allOperandTypes())); + +TEST(AlternatePatternFollowingImmediate, Empty) { + EXPECT_THAT(spvAlternatePatternFollowingImmediate({}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV})); +} + +TEST(AlternatePatternFollowingImmediate, SingleElement) { + // Spot-check a random selection of types. + EXPECT_THAT(spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV})); + EXPECT_THAT( + spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_CAPABILITY}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV})); + EXPECT_THAT( + spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_LOOP_CONTROL}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV})); + EXPECT_THAT(spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV})); + EXPECT_THAT(spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_ID}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV})); +} + +TEST(AlternatePatternFollowingImmediate, SingleResultId) { + EXPECT_THAT( + spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_RESULT_ID}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_RESULT_ID})); +} + +TEST(AlternatePatternFollowingImmediate, MultipleNonResultIds) { + EXPECT_THAT( + spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_VARIABLE_ID_LITERAL_INTEGER, + SPV_OPERAND_TYPE_CAPABILITY, SPV_OPERAND_TYPE_LOOP_CONTROL, + SPV_OPERAND_TYPE_OPTIONAL_LITERAL_INTEGER, SPV_OPERAND_TYPE_ID}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV})); +} + +TEST(AlternatePatternFollowingImmediate, ResultIdFront) { + EXPECT_THAT(spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_OPTIONAL_CIV})); + EXPECT_THAT( + spvAlternatePatternFollowingImmediate({SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_FP_ROUNDING_MODE, + SPV_OPERAND_TYPE_ID}), + Eq(spv_operand_pattern_t{ + SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV})); + EXPECT_THAT( + spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_DIMENSIONALITY, + SPV_OPERAND_TYPE_LINKAGE_TYPE, + SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE, + SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_VARIABLE_ID}), + Eq(spv_operand_pattern_t{ + SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV})); +} + +TEST(AlternatePatternFollowingImmediate, ResultIdMiddle) { + EXPECT_THAT(spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_FP_ROUNDING_MODE, + SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_ID}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_OPTIONAL_CIV})); + EXPECT_THAT( + spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_DIMENSIONALITY, SPV_OPERAND_TYPE_LINKAGE_TYPE, + SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE, + SPV_OPERAND_TYPE_RESULT_ID, SPV_OPERAND_TYPE_FP_ROUNDING_MODE, + SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_VARIABLE_ID}), + Eq(spv_operand_pattern_t{ + SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_RESULT_ID, + SPV_OPERAND_TYPE_OPTIONAL_CIV, SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_OPTIONAL_CIV})); +} + +TEST(AlternatePatternFollowingImmediate, ResultIdBack) { + EXPECT_THAT(spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_ID, SPV_OPERAND_TYPE_RESULT_ID}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_RESULT_ID})); + EXPECT_THAT(spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_RESULT_ID}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_RESULT_ID})); + EXPECT_THAT( + spvAlternatePatternFollowingImmediate( + {SPV_OPERAND_TYPE_DIMENSIONALITY, SPV_OPERAND_TYPE_LINKAGE_TYPE, + SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE, + SPV_OPERAND_TYPE_FP_ROUNDING_MODE, SPV_OPERAND_TYPE_ID, + SPV_OPERAND_TYPE_VARIABLE_ID, SPV_OPERAND_TYPE_RESULT_ID}), + Eq(spv_operand_pattern_t{SPV_OPERAND_TYPE_OPTIONAL_CIV, + SPV_OPERAND_TYPE_RESULT_ID})); +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/operand_test.cpp b/third_party/spirv-tools/test/operand_test.cpp new file mode 100644 index 0000000..ec45da5 --- /dev/null +++ b/third_party/spirv-tools/test/operand_test.cpp @@ -0,0 +1,123 @@ +// Copyright (c) 2015-2016 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "test/unit_spirv.h" + +namespace spvtools { +namespace { + +using GetTargetTest = ::testing::TestWithParam; +using ::testing::ValuesIn; + +TEST_P(GetTargetTest, Default) { + spv_operand_table table; + ASSERT_EQ(SPV_SUCCESS, spvOperandTableGet(&table, GetParam())); + ASSERT_NE(0u, table->count); + ASSERT_NE(nullptr, table->types); +} + +TEST_P(GetTargetTest, InvalidPointerTable) { + ASSERT_EQ(SPV_ERROR_INVALID_POINTER, spvOperandTableGet(nullptr, GetParam())); +} + +INSTANTIATE_TEST_SUITE_P(OperandTableGet, GetTargetTest, + ValuesIn(std::vector{ + SPV_ENV_UNIVERSAL_1_0, SPV_ENV_UNIVERSAL_1_1, + SPV_ENV_VULKAN_1_0})); + +TEST(OperandString, AllAreDefinedExceptVariable) { + // None has no string, so don't test it. + EXPECT_EQ(0u, SPV_OPERAND_TYPE_NONE); + // Start testing at enum with value 1, skipping None. + for (int i = 1; i < int(SPV_OPERAND_TYPE_NUM_OPERAND_TYPES); i++) { + const auto type = static_cast(i); + if (spvOperandIsVariable(type)) { + EXPECT_STREQ("unknown", spvOperandTypeStr(type)) + << " variable type " << i << " has a name '" + << spvOperandTypeStr(type) << "'when it should not"; + } else { + EXPECT_STRNE("unknown", spvOperandTypeStr(type)) + << " operand type " << i << " has no name when it should"; + } + } +} + +TEST(OperandIsConcreteMask, Sample) { + // Check a few operand types preceding the concrete mask types. + EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_NONE)); + EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_ID)); + EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_LITERAL_INTEGER)); + EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_CAPABILITY)); + + // Check all the concrete mask operand types. + EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_IMAGE)); + EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_FP_FAST_MATH_MODE)); + EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_SELECTION_CONTROL)); + EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_LOOP_CONTROL)); + EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_FUNCTION_CONTROL)); + EXPECT_TRUE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_MEMORY_ACCESS)); + + // Check a few operand types after the concrete mask types, including the + // optional forms for Image and MemoryAccess. + EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_OPTIONAL_ID)); + EXPECT_FALSE(spvOperandIsConcreteMask(SPV_OPERAND_TYPE_OPTIONAL_IMAGE)); + EXPECT_FALSE( + spvOperandIsConcreteMask(SPV_OPERAND_TYPE_OPTIONAL_MEMORY_ACCESS)); +} + +TEST(OperandType, NoneTypeClassification) { + EXPECT_FALSE(spvOperandIsConcrete(SPV_OPERAND_TYPE_NONE)); + EXPECT_FALSE(spvOperandIsOptional(SPV_OPERAND_TYPE_NONE)); + EXPECT_FALSE(spvOperandIsVariable(SPV_OPERAND_TYPE_NONE)); +} + +TEST(OperandType, EndSentinelTypeClassification) { + EXPECT_FALSE(spvOperandIsConcrete(SPV_OPERAND_TYPE_NUM_OPERAND_TYPES)); + EXPECT_FALSE(spvOperandIsOptional(SPV_OPERAND_TYPE_NUM_OPERAND_TYPES)); + EXPECT_FALSE(spvOperandIsVariable(SPV_OPERAND_TYPE_NUM_OPERAND_TYPES)); +} + +TEST(OperandType, WidthForcingTypeClassification) { + EXPECT_FALSE(spvOperandIsConcrete(SPV_FORCE_32BIT_spv_operand_type_t)); + EXPECT_FALSE(spvOperandIsOptional(SPV_FORCE_32BIT_spv_operand_type_t)); + EXPECT_FALSE(spvOperandIsVariable(SPV_FORCE_32BIT_spv_operand_type_t)); +} + +TEST(OperandType, EachTypeIsEitherConcreteOrOptionalNotBoth) { + EXPECT_EQ(0u, SPV_OPERAND_TYPE_NONE); + // Start testing at enum with value 1, skipping None. + for (int i = 1; i < int(SPV_OPERAND_TYPE_NUM_OPERAND_TYPES); i++) { + const auto type = static_cast(i); + EXPECT_NE(spvOperandIsConcrete(type), spvOperandIsOptional(type)) + << " operand type " << int(type) << " concrete? " + << int(spvOperandIsConcrete(type)) << " optional? " + << int(spvOperandIsOptional(type)); + } +} + +TEST(OperandType, EachVariableTypeIsOptional) { + EXPECT_EQ(0u, SPV_OPERAND_TYPE_NONE); + // Start testing at enum with value 1, skipping None. + for (int i = 1; i < int(SPV_OPERAND_TYPE_NUM_OPERAND_TYPES); i++) { + const auto type = static_cast(i); + if (spvOperandIsVariable(type)) { + EXPECT_TRUE(spvOperandIsOptional(type)) << " variable type " << int(type); + } + } +} + +} // namespace +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/CMakeLists.txt b/third_party/spirv-tools/test/opt/CMakeLists.txt new file mode 100644 index 0000000..3426958 --- /dev/null +++ b/third_party/spirv-tools/test/opt/CMakeLists.txt @@ -0,0 +1,108 @@ +# Copyright (c) 2016 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_subdirectory(dominator_tree) +add_subdirectory(loop_optimizations) + +add_spvtools_unittest(TARGET opt + SRCS aggressive_dead_code_elim_test.cpp + amd_ext_to_khr.cpp + assembly_builder_test.cpp + block_merge_test.cpp + ccp_test.cpp + cfg_cleanup_test.cpp + cfg_test.cpp + code_sink_test.cpp + combine_access_chains_test.cpp + compact_ids_test.cpp + constants_test.cpp + constant_manager_test.cpp + convert_relaxed_to_half_test.cpp + copy_prop_array_test.cpp + dead_branch_elim_test.cpp + dead_insert_elim_test.cpp + dead_variable_elim_test.cpp + debug_info_manager_test.cpp + decompose_initialized_variables_test.cpp + decoration_manager_test.cpp + def_use_test.cpp + desc_sroa_test.cpp + eliminate_dead_const_test.cpp + eliminate_dead_functions_test.cpp + eliminate_dead_member_test.cpp + feature_manager_test.cpp + fix_storage_class_test.cpp + flatten_decoration_test.cpp + fold_spec_const_op_composite_test.cpp + fold_test.cpp + freeze_spec_const_test.cpp + function_test.cpp + generate_webgpu_initializers_test.cpp + graphics_robust_access_test.cpp + if_conversion_test.cpp + inline_opaque_test.cpp + inline_test.cpp + insert_extract_elim_test.cpp + inst_bindless_check_test.cpp + inst_buff_addr_check_test.cpp + inst_debug_printf_test.cpp + instruction_list_test.cpp + instruction_test.cpp + ir_builder.cpp + ir_context_test.cpp + ir_loader_test.cpp + iterator_test.cpp + legalize_vector_shuffle_test.cpp + line_debug_info_test.cpp + local_access_chain_convert_test.cpp + local_redundancy_elimination_test.cpp + local_single_block_elim.cpp + local_single_store_elim_test.cpp + local_ssa_elim_test.cpp + module_test.cpp + module_utils.h + optimizer_test.cpp + pass_manager_test.cpp + pass_merge_return_test.cpp + pass_remove_duplicates_test.cpp + pass_utils.cpp + private_to_local_test.cpp + propagator_test.cpp + reduce_load_size_test.cpp + redundancy_elimination_test.cpp + register_liveness.cpp + relax_float_ops_test.cpp + replace_invalid_opc_test.cpp + scalar_analysis.cpp + scalar_replacement_test.cpp + set_spec_const_default_value_test.cpp + simplification_test.cpp + split_invalid_unreachable_test.cpp + strength_reduction_test.cpp + strip_atomic_counter_memory_test.cpp + strip_debug_info_test.cpp + strip_reflect_info_test.cpp + struct_cfg_analysis_test.cpp + type_manager_test.cpp + types_test.cpp + unify_const_test.cpp + upgrade_memory_model_test.cpp + utils_test.cpp pass_utils.cpp + value_table_test.cpp + vector_dce_test.cpp + workaround1209_test.cpp + wrap_opkill_test.cpp + LIBS SPIRV-Tools-opt + PCH_FILE pch_test_opt +) diff --git a/third_party/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp b/third_party/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp new file mode 100644 index 0000000..972e6e5 --- /dev/null +++ b/third_party/spirv-tools/test/opt/aggressive_dead_code_elim_test.cpp @@ -0,0 +1,7592 @@ +// Copyright (c) 2017 Valve Corporation +// Copyright (c) 2017 LunarG Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "test/opt/assembly_builder.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using AggressiveDCETest = PassTest<::testing::Test>; + +TEST_F(AggressiveDCETest, EliminateExtendedInst) { + // #version 140 + // + // in vec4 BaseColor; + // in vec4 Dead; + // + // void main() + // { + // vec4 v = BaseColor; + // vec4 dv = sqrt(Dead); + // gl_FragColor = v; + // } + + const std::string predefs1 = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +)"; + + const std::string names_before = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %dv "dv" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string names_after = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string predefs2 = + R"(%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %9 +%15 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%dv = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +OpStore %v %16 +%17 = OpLoad %v4float %Dead +%18 = OpExtInst %v4float %1 Sqrt %17 +OpStore %dv %18 +%19 = OpLoad %v4float %v +OpStore %gl_FragColor %19 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %9 +%15 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +OpStore %v %16 +%19 = OpLoad %v4float %v +OpStore %gl_FragColor %19 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs1 + names_before + predefs2 + func_before, + predefs1 + names_after + predefs2 + func_after, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateFrexp) { + // Note: SPIR-V hand-edited to utilize Frexp + // + // #version 450 + // + // in vec4 BaseColor; + // in vec4 Dead; + // out vec4 Color; + // out ivec4 iv2; + // + // void main() + // { + // vec4 v = BaseColor; + // vec4 dv = frexp(Dead, iv2); + // Color = v; + // } + + const std::string predefs1 = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %iv2 %Color +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +)"; + + const std::string names_before = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %dv "dv" +OpName %Dead "Dead" +OpName %iv2 "iv2" +OpName %ResType "ResType" +OpName %Color "Color" +)"; + + const std::string names_after = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %Dead "Dead" +OpName %iv2 "iv2" +OpName %Color "Color" +)"; + + const std::string predefs2_before = + R"(%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%int = OpTypeInt 32 1 +%v4int = OpTypeVector %int 4 +%_ptr_Output_v4int = OpTypePointer Output %v4int +%iv2 = OpVariable %_ptr_Output_v4int Output +%ResType = OpTypeStruct %v4float %v4int +%_ptr_Output_v4float = OpTypePointer Output %v4float +%Color = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string predefs2_after = + R"(%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%int = OpTypeInt 32 1 +%v4int = OpTypeVector %int 4 +%_ptr_Output_v4int = OpTypePointer Output %v4int +%iv2 = OpVariable %_ptr_Output_v4int Output +%_ptr_Output_v4float = OpTypePointer Output %v4float +%Color = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %11 +%20 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%dv = OpVariable %_ptr_Function_v4float Function +%21 = OpLoad %v4float %BaseColor +OpStore %v %21 +%22 = OpLoad %v4float %Dead +%23 = OpExtInst %v4float %1 Frexp %22 %iv2 +OpStore %dv %23 +%24 = OpLoad %v4float %v +OpStore %Color %24 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %11 +%20 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%21 = OpLoad %v4float %BaseColor +OpStore %v %21 +%22 = OpLoad %v4float %Dead +%23 = OpExtInst %v4float %1 Frexp %22 %iv2 +%24 = OpLoad %v4float %v +OpStore %Color %24 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs1 + names_before + predefs2_before + func_before, + predefs1 + names_after + predefs2_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, EliminateDecorate) { + // Note: The SPIR-V was hand-edited to add the OpDecorate + // + // #version 140 + // + // in vec4 BaseColor; + // in vec4 Dead; + // + // void main() + // { + // vec4 v = BaseColor; + // vec4 dv = Dead * 0.5; + // gl_FragColor = v; + // } + + const std::string predefs1 = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +)"; + + const std::string names_before = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %dv "dv" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +OpDecorate %8 RelaxedPrecision +)"; + + const std::string names_after = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string predefs2_before = + R"(%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%float_0_5 = OpConstant %float 0.5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string predefs2_after = + R"(%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %10 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%dv = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +%19 = OpLoad %v4float %Dead +%8 = OpVectorTimesScalar %v4float %19 %float_0_5 +OpStore %dv %8 +%20 = OpLoad %v4float %v +OpStore %gl_FragColor %20 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %10 +%17 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%18 = OpLoad %v4float %BaseColor +OpStore %v %18 +%20 = OpLoad %v4float %v +OpStore %gl_FragColor %20 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs1 + names_before + predefs2_before + func_before, + predefs1 + names_after + predefs2_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, Simple) { + // #version 140 + // + // in vec4 BaseColor; + // in vec4 Dead; + // + // void main() + // { + // vec4 v = BaseColor; + // vec4 dv = Dead; + // gl_FragColor = v; + // } + + const std::string predefs1 = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +)"; + + const std::string names_before = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %dv "dv" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string names_after = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string predefs2 = + R"(%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %9 +%15 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%dv = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +OpStore %v %16 +%17 = OpLoad %v4float %Dead +OpStore %dv %17 +%18 = OpLoad %v4float %v +OpStore %gl_FragColor %18 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %9 +%15 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +OpStore %v %16 +%18 = OpLoad %v4float %v +OpStore %gl_FragColor %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs1 + names_before + predefs2 + func_before, + predefs1 + names_after + predefs2 + func_after, true, true); +} + +TEST_F(AggressiveDCETest, OptAllowListExtension) { + // #version 140 + // + // in vec4 BaseColor; + // in vec4 Dead; + // + // void main() + // { + // vec4 v = BaseColor; + // vec4 dv = Dead; + // gl_FragColor = v; + // } + + const std::string predefs1 = + R"(OpCapability Shader +OpExtension "SPV_AMD_gpu_shader_int16" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +)"; + + const std::string names_before = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %dv "dv" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string names_after = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string predefs2 = + R"(%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %9 +%15 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%dv = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +OpStore %v %16 +%17 = OpLoad %v4float %Dead +OpStore %dv %17 +%18 = OpLoad %v4float %v +OpStore %gl_FragColor %18 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %9 +%15 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +OpStore %v %16 +%18 = OpLoad %v4float %v +OpStore %gl_FragColor %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs1 + names_before + predefs2 + func_before, + predefs1 + names_after + predefs2 + func_after, true, true); +} + +TEST_F(AggressiveDCETest, NoOptDenyListExtension) { + // #version 140 + // + // in vec4 BaseColor; + // in vec4 Dead; + // + // void main() + // { + // vec4 v = BaseColor; + // vec4 dv = Dead; + // gl_FragColor = v; + // } + + const std::string assembly = + R"(OpCapability Shader +OpExtension "SPV_KHR_variable_pointers" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %dv "dv" +OpName %Dead "Dead" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %9 +%15 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%dv = OpVariable %_ptr_Function_v4float Function +%16 = OpLoad %v4float %BaseColor +OpStore %v %16 +%17 = OpLoad %v4float %Dead +OpStore %dv %17 +%18 = OpLoad %v4float %v +OpStore %gl_FragColor %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, ElimWithCall) { + // This demonstrates that "dead" function calls are not eliminated. + // Also demonstrates that DCE will happen in presence of function call. + // #version 140 + // in vec4 i1; + // in vec4 i2; + // + // void nothing(vec4 v) + // { + // } + // + // void main() + // { + // vec4 v1 = i1; + // vec4 v2 = i2; + // nothing(v1); + // gl_FragColor = vec4(0.0); + // } + + const std::string defs_before = + R"( OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i1 %i2 %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %nothing_vf4_ "nothing(vf4;" +OpName %v "v" +OpName %v1 "v1" +OpName %i1 "i1" +OpName %v2 "v2" +OpName %i2 "i2" +OpName %param "param" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%16 = OpTypeFunction %void %_ptr_Function_v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%i1 = OpVariable %_ptr_Input_v4float Input +%i2 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +)"; + + const std::string defs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %i1 %i2 %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %nothing_vf4_ "nothing(vf4;" +OpName %v "v" +OpName %v1 "v1" +OpName %i1 "i1" +OpName %i2 "i2" +OpName %param "param" +OpName %gl_FragColor "gl_FragColor" +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%16 = OpTypeFunction %void %_ptr_Function_v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%i1 = OpVariable %_ptr_Input_v4float Input +%i2 = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +%20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %12 +%21 = OpLabel +%v1 = OpVariable %_ptr_Function_v4float Function +%v2 = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %i1 +OpStore %v1 %22 +%23 = OpLoad %v4float %i2 +OpStore %v2 %23 +%24 = OpLoad %v4float %v1 +OpStore %param %24 +%25 = OpFunctionCall %void %nothing_vf4_ %param +OpStore %gl_FragColor %20 +OpReturn +OpFunctionEnd +%nothing_vf4_ = OpFunction %void None %16 +%v = OpFunctionParameter %_ptr_Function_v4float +%26 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %12 +%21 = OpLabel +%v1 = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%22 = OpLoad %v4float %i1 +OpStore %v1 %22 +%24 = OpLoad %v4float %v1 +OpStore %param %24 +%25 = OpFunctionCall %void %nothing_vf4_ %param +OpStore %gl_FragColor %20 +OpReturn +OpFunctionEnd +%nothing_vf4_ = OpFunction %void None %16 +%v = OpFunctionParameter %_ptr_Function_v4float +%26 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, NoParamElim) { + // This demonstrates that unused parameters are not eliminated, but + // dead uses of them are. + // #version 140 + // + // in vec4 BaseColor; + // + // vec4 foo(vec4 v1, vec4 v2) + // { + // vec4 t = -v1; + // return v2; + // } + // + // void main() + // { + // vec4 dead; + // gl_FragColor = foo(dead, BaseColor); + // } + + const std::string defs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_vf4_vf4_ "foo(vf4;vf4;" +OpName %v1 "v1" +OpName %v2 "v2" +OpName %t "t" +OpName %gl_FragColor "gl_FragColor" +OpName %dead "dead" +OpName %BaseColor "BaseColor" +OpName %param "param" +OpName %param_0 "param" +%void = OpTypeVoid +%13 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%17 = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%main = OpFunction %void None %13 +%20 = OpLabel +%dead = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%param_0 = OpVariable %_ptr_Function_v4float Function +%21 = OpLoad %v4float %dead +OpStore %param %21 +%22 = OpLoad %v4float %BaseColor +OpStore %param_0 %22 +%23 = OpFunctionCall %v4float %foo_vf4_vf4_ %param %param_0 +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string defs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %gl_FragColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %foo_vf4_vf4_ "foo(vf4;vf4;" +OpName %v1 "v1" +OpName %v2 "v2" +OpName %gl_FragColor "gl_FragColor" +OpName %dead "dead" +OpName %BaseColor "BaseColor" +OpName %param "param" +OpName %param_0 "param" +%void = OpTypeVoid +%13 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%17 = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%main = OpFunction %void None %13 +%20 = OpLabel +%dead = OpVariable %_ptr_Function_v4float Function +%param = OpVariable %_ptr_Function_v4float Function +%param_0 = OpVariable %_ptr_Function_v4float Function +%21 = OpLoad %v4float %dead +OpStore %param %21 +%22 = OpLoad %v4float %BaseColor +OpStore %param_0 %22 +%23 = OpFunctionCall %v4float %foo_vf4_vf4_ %param %param_0 +OpStore %gl_FragColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string func_before = + R"(%foo_vf4_vf4_ = OpFunction %v4float None %17 +%v1 = OpFunctionParameter %_ptr_Function_v4float +%v2 = OpFunctionParameter %_ptr_Function_v4float +%24 = OpLabel +%t = OpVariable %_ptr_Function_v4float Function +%25 = OpLoad %v4float %v1 +%26 = OpFNegate %v4float %25 +OpStore %t %26 +%27 = OpLoad %v4float %v2 +OpReturnValue %27 +OpFunctionEnd +)"; + + const std::string func_after = + R"(%foo_vf4_vf4_ = OpFunction %v4float None %17 +%v1 = OpFunctionParameter %_ptr_Function_v4float +%v2 = OpFunctionParameter %_ptr_Function_v4float +%24 = OpLabel +%27 = OpLoad %v4float %v2 +OpReturnValue %27 +OpFunctionEnd +)"; + + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, ElimOpaque) { + // SPIR-V not representable from GLSL; not generatable from HLSL + // for the moment. + + const std::string defs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %outColor %texCoords +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %S_t "S_t" +OpMemberName %S_t 0 "v0" +OpMemberName %S_t 1 "v1" +OpMemberName %S_t 2 "smp" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %s0 "s0" +OpName %texCoords "texCoords" +OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%14 = OpTypeImage %float 2D 0 0 0 1 Unknown +%15 = OpTypeSampledImage %14 +%S_t = OpTypeStruct %v2float %v2float %15 +%_ptr_Function_S_t = OpTypePointer Function %S_t +%17 = OpTypeFunction %void %_ptr_Function_S_t +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 +%_ptr_Function_15 = OpTypePointer Function %15 +%sampler15 = OpVariable %_ptr_UniformConstant_15 UniformConstant +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_2 = OpConstant %int 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string defs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %outColor %texCoords +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpName %main "main" +OpName %outColor "outColor" +OpName %sampler15 "sampler15" +OpName %texCoords "texCoords" +OpDecorate %sampler15 DescriptorSet 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%outColor = OpVariable %_ptr_Output_v4float Output +%14 = OpTypeImage %float 2D 0 0 0 1 Unknown +%15 = OpTypeSampledImage %14 +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 +%sampler15 = OpVariable %_ptr_UniformConstant_15 UniformConstant +%_ptr_Input_v2float = OpTypePointer Input %v2float +%texCoords = OpVariable %_ptr_Input_v2float Input +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %9 +%25 = OpLabel +%s0 = OpVariable %_ptr_Function_S_t Function +%26 = OpLoad %v2float %texCoords +%27 = OpLoad %S_t %s0 +%28 = OpCompositeInsert %S_t %26 %27 0 +%29 = OpLoad %15 %sampler15 +%30 = OpCompositeInsert %S_t %29 %28 2 +OpStore %s0 %30 +%31 = OpImageSampleImplicitLod %v4float %29 %26 +OpStore %outColor %31 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %9 +%25 = OpLabel +%26 = OpLoad %v2float %texCoords +%29 = OpLoad %15 %sampler15 +%31 = OpImageSampleImplicitLod %v4float %29 %26 +OpStore %outColor %31 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(defs_before + func_before, + defs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, NoParamStoreElim) { + // Should not eliminate stores to params + // + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void foo(in vec4 v1, out vec4 v2) + // { + // v2 = -v1; + // } + // + // void main() + // { + // foo(BaseColor, OutColor); + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %foo_vf4_vf4_ "foo(vf4;vf4;" +OpName %v1 "v1" +OpName %v2 "v2" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpName %param "param" +OpName %param_0 "param" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%15 = OpTypeFunction %void %_ptr_Function_v4float %_ptr_Function_v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %11 +%18 = OpLabel +%param = OpVariable %_ptr_Function_v4float Function +%param_0 = OpVariable %_ptr_Function_v4float Function +%19 = OpLoad %v4float %BaseColor +OpStore %param %19 +%20 = OpFunctionCall %void %foo_vf4_vf4_ %param %param_0 +%21 = OpLoad %v4float %param_0 +OpStore %OutColor %21 +OpReturn +OpFunctionEnd +%foo_vf4_vf4_ = OpFunction %void None %15 +%v1 = OpFunctionParameter %_ptr_Function_v4float +%v2 = OpFunctionParameter %_ptr_Function_v4float +%22 = OpLabel +%23 = OpLoad %v4float %v1 +%24 = OpFNegate %v4float %23 +OpStore %v2 %24 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, PrivateStoreElimInEntryNoCalls) { + // Eliminate stores to private in entry point with no calls + // Note: Not legal GLSL + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 1) in vec4 Dead; + // layout(location = 0) out vec4 OutColor; + // + // private vec4 dv; + // + // void main() + // { + // vec4 v = BaseColor; + // dv = Dead; + // OutColor = v; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %dv "dv" +OpName %Dead "Dead" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %Dead Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Private_v4float = OpTypePointer Private %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%dv = OpVariable %_ptr_Private_v4float Private +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %Dead "Dead" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %Dead Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string main_before = + R"(%main = OpFunction %void None %9 +%16 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%17 = OpLoad %v4float %BaseColor +OpStore %v %17 +%18 = OpLoad %v4float %Dead +OpStore %dv %18 +%19 = OpLoad %v4float %v +%20 = OpFNegate %v4float %19 +OpStore %OutColor %20 +OpReturn +OpFunctionEnd +)"; + + const std::string main_after = + R"(%main = OpFunction %void None %9 +%16 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%17 = OpLoad %v4float %BaseColor +OpStore %v %17 +%19 = OpLoad %v4float %v +%20 = OpFNegate %v4float %19 +OpStore %OutColor %20 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + main_before, predefs_after + main_after, true, true); +} + +TEST_F(AggressiveDCETest, NoPrivateStoreElimIfLoad) { + // Should not eliminate stores to private when there is a load + // Note: Not legal GLSL + // + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // private vec4 pv; + // + // void main() + // { + // pv = BaseColor; + // OutColor = pv; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %pv "pv" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Private_v4float = OpTypePointer Private %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%pv = OpVariable %_ptr_Private_v4float Private +%main = OpFunction %void None %7 +%13 = OpLabel +%14 = OpLoad %v4float %BaseColor +OpStore %pv %14 +%15 = OpLoad %v4float %pv +%16 = OpFNegate %v4float %15 +OpStore %OutColor %16 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoPrivateStoreElimWithCall) { + // Should not eliminate stores to private when function contains call + // Note: Not legal GLSL + // + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // private vec4 v1; + // + // void foo() + // { + // OutColor = -v1; + // } + // + // void main() + // { + // v1 = BaseColor; + // foo(); + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %OutColor %BaseColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %foo_ "foo(" +OpName %OutColor "OutColor" +OpName %v1 "v1" +OpName %BaseColor "BaseColor" +OpDecorate %OutColor Location 0 +OpDecorate %BaseColor Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Private_v4float = OpTypePointer Private %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%v1 = OpVariable %_ptr_Private_v4float Private +%BaseColor = OpVariable %_ptr_Input_v4float Input +%main = OpFunction %void None %8 +%14 = OpLabel +%15 = OpLoad %v4float %BaseColor +OpStore %v1 %15 +%16 = OpFunctionCall %void %foo_ +OpReturn +OpFunctionEnd +%foo_ = OpFunction %void None %8 +%17 = OpLabel +%18 = OpLoad %v4float %v1 +%19 = OpFNegate %v4float %18 +OpStore %OutColor %19 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoPrivateStoreElimInNonEntry) { + // Should not eliminate stores to private when function is not entry point + // Note: Not legal GLSL + // + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // private vec4 v1; + // + // void foo() + // { + // v1 = BaseColor; + // } + // + // void main() + // { + // foo(); + // OutColor = -v1; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %foo_ "foo(" +OpName %v1 "v1" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Private_v4float = OpTypePointer Private %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%v1 = OpVariable %_ptr_Private_v4float Private +%OutColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %8 +%14 = OpLabel +%15 = OpFunctionCall %void %foo_ +%16 = OpLoad %v4float %v1 +%17 = OpFNegate %v4float %16 +OpStore %OutColor %17 +OpReturn +OpFunctionEnd +%foo_ = OpFunction %void None %8 +%18 = OpLabel +%19 = OpLoad %v4float %BaseColor +OpStore %v1 %19 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, WorkgroupStoreElimInEntryNoCalls) { + // Eliminate stores to private in entry point with no calls + // Note: Not legal GLSL + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 1) in vec4 Dead; + // layout(location = 0) out vec4 OutColor; + // + // workgroup vec4 dv; + // + // void main() + // { + // vec4 v = BaseColor; + // dv = Dead; + // OutColor = v; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %dv "dv" +OpName %Dead "Dead" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %Dead Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Workgroup_v4float = OpTypePointer Workgroup %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%dv = OpVariable %_ptr_Workgroup_v4float Workgroup +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %Dead %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %Dead "Dead" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %Dead Location 1 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%Dead = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string main_before = + R"(%main = OpFunction %void None %9 +%16 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%17 = OpLoad %v4float %BaseColor +OpStore %v %17 +%18 = OpLoad %v4float %Dead +OpStore %dv %18 +%19 = OpLoad %v4float %v +%20 = OpFNegate %v4float %19 +OpStore %OutColor %20 +OpReturn +OpFunctionEnd +)"; + + const std::string main_after = + R"(%main = OpFunction %void None %9 +%16 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%17 = OpLoad %v4float %BaseColor +OpStore %v %17 +%19 = OpLoad %v4float %v +%20 = OpFNegate %v4float %19 +OpStore %OutColor %20 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + main_before, predefs_after + main_after, true, true); +} + +TEST_F(AggressiveDCETest, EliminateDeadIfThenElse) { + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // float d; + // if (BaseColor.x == 0) + // d = BaseColor.y; + // else + // d = BaseColor.z; + // OutColor = vec4(1.0,1.0,1.0,1.0); + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %d "d" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_1 = OpConstant %float 1 +%21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_1 = OpConstant %float 1 +%21 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %7 +%22 = OpLabel +%d = OpVariable %_ptr_Function_float Function +%23 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 +%24 = OpLoad %float %23 +%25 = OpFOrdEqual %bool %24 %float_0 +OpSelectionMerge %26 None +OpBranchConditional %25 %27 %28 +%27 = OpLabel +%29 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 +%30 = OpLoad %float %29 +OpStore %d %30 +OpBranch %26 +%28 = OpLabel +%31 = OpAccessChain %_ptr_Input_float %BaseColor %uint_2 +%32 = OpLoad %float %31 +OpStore %d %32 +OpBranch %26 +%26 = OpLabel +OpStore %OutColor %21 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %7 +%22 = OpLabel +OpBranch %26 +%26 = OpLabel +OpStore %OutColor %21 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + func_before, predefs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, EliminateDeadIfThen) { + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // float d; + // if (BaseColor.x == 0) + // d = BaseColor.y; + // OutColor = vec4(1.0,1.0,1.0,1.0); + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %d "d" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_1 = OpConstant %float 1 +%20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_1 = OpConstant %float 1 +%20 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %7 +%21 = OpLabel +%d = OpVariable %_ptr_Function_float Function +%22 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 +%23 = OpLoad %float %22 +%24 = OpFOrdEqual %bool %23 %float_0 +OpSelectionMerge %25 None +OpBranchConditional %24 %26 %25 +%26 = OpLabel +%27 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 +%28 = OpLoad %float %27 +OpStore %d %28 +OpBranch %25 +%25 = OpLabel +OpStore %OutColor %20 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %7 +%21 = OpLabel +OpBranch %25 +%25 = OpLabel +OpStore %OutColor %20 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + func_before, predefs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, EliminateDeadSwitch) { + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 1) in flat int x; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // float d; + // switch (x) { + // case 0: + // d = BaseColor.y; + // } + // OutColor = vec4(1.0,1.0,1.0,1.0); + // } + const std::string before = + R"(OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %x %BaseColor %OutColor + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpName %main "main" + OpName %x "x" + OpName %d "d" + OpName %BaseColor "BaseColor" + OpName %OutColor "OutColor" + OpDecorate %x Flat + OpDecorate %x Location 1 + OpDecorate %BaseColor Location 0 + OpDecorate %OutColor Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int + %x = OpVariable %_ptr_Input_int Input + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float + %BaseColor = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_ptr_Input_float = OpTypePointer Input %float +%_ptr_Output_v4float = OpTypePointer Output %v4float + %OutColor = OpVariable %_ptr_Output_v4float Output + %float_1 = OpConstant %float 1 + %27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 + %main = OpFunction %void None %3 + %5 = OpLabel + %d = OpVariable %_ptr_Function_float Function + %9 = OpLoad %int %x + OpSelectionMerge %11 None + OpSwitch %9 %11 0 %10 + %10 = OpLabel + %21 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 + %22 = OpLoad %float %21 + OpStore %d %22 + OpBranch %11 + %11 = OpLabel + OpStore %OutColor %27 + OpReturn + OpFunctionEnd)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %x %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %x "x" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %x Flat +OpDecorate %x Location 1 +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Input_int = OpTypePointer Input %int +%x = OpVariable %_ptr_Input_int Input +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_1 = OpConstant %float 1 +%27 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +%main = OpFunction %void None %3 +%5 = OpLabel +OpBranch %11 +%11 = OpLabel +OpStore %OutColor %27 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(AggressiveDCETest, EliminateDeadIfThenElseNested) { + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // float d; + // if (BaseColor.x == 0) + // if (BaseColor.y == 0) + // d = 0.0; + // else + // d = 0.25; + // else + // if (BaseColor.y == 0) + // d = 0.5; + // else + // d = 0.75; + // OutColor = vec4(1.0,1.0,1.0,1.0); + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %d "d" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%uint_1 = OpConstant %uint 1 +%_ptr_Function_float = OpTypePointer Function %float +%float_0_25 = OpConstant %float 0.25 +%float_0_5 = OpConstant %float 0.5 +%float_0_75 = OpConstant %float 0.75 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_1 = OpConstant %float 1 +%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%float_1 = OpConstant %float 1 +%23 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %7 +%24 = OpLabel +%d = OpVariable %_ptr_Function_float Function +%25 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 +%26 = OpLoad %float %25 +%27 = OpFOrdEqual %bool %26 %float_0 +OpSelectionMerge %28 None +OpBranchConditional %27 %29 %30 +%29 = OpLabel +%31 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 +%32 = OpLoad %float %31 +%33 = OpFOrdEqual %bool %32 %float_0 +OpSelectionMerge %34 None +OpBranchConditional %33 %35 %36 +%35 = OpLabel +OpStore %d %float_0 +OpBranch %34 +%36 = OpLabel +OpStore %d %float_0_25 +OpBranch %34 +%34 = OpLabel +OpBranch %28 +%30 = OpLabel +%37 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 +%38 = OpLoad %float %37 +%39 = OpFOrdEqual %bool %38 %float_0 +OpSelectionMerge %40 None +OpBranchConditional %39 %41 %42 +%41 = OpLabel +OpStore %d %float_0_5 +OpBranch %40 +%42 = OpLabel +OpStore %d %float_0_75 +OpBranch %40 +%40 = OpLabel +OpBranch %28 +%28 = OpLabel +OpStore %OutColor %23 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %7 +%24 = OpLabel +OpBranch %28 +%28 = OpLabel +OpStore %OutColor %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + func_before, predefs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateLiveIfThenElse) { + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // float t; + // if (BaseColor.x == 0) + // t = BaseColor.y; + // else + // t = BaseColor.z; + // OutColor = vec4(t); + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %t "t" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %7 +%20 = OpLabel +%t = OpVariable %_ptr_Function_float Function +%21 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 +%22 = OpLoad %float %21 +%23 = OpFOrdEqual %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %26 +%25 = OpLabel +%27 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 +%28 = OpLoad %float %27 +OpStore %t %28 +OpBranch %24 +%26 = OpLabel +%29 = OpAccessChain %_ptr_Input_float %BaseColor %uint_2 +%30 = OpLoad %float %29 +OpStore %t %30 +OpBranch %24 +%24 = OpLabel +%31 = OpLoad %float %t +%32 = OpCompositeConstruct %v4float %31 %31 %31 %31 +OpStore %OutColor %32 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateLiveIfThenElseNested) { + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // float t; + // if (BaseColor.x == 0) + // if (BaseColor.y == 0) + // t = 0.0; + // else + // t = 0.25; + // else + // if (BaseColor.y == 0) + // t = 0.5; + // else + // t = 0.75; + // OutColor = vec4(t); + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %t "t" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%uint_1 = OpConstant %uint 1 +%_ptr_Function_float = OpTypePointer Function %float +%float_0_25 = OpConstant %float 0.25 +%float_0_5 = OpConstant %float 0.5 +%float_0_75 = OpConstant %float 0.75 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %7 +%22 = OpLabel +%t = OpVariable %_ptr_Function_float Function +%23 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 +%24 = OpLoad %float %23 +%25 = OpFOrdEqual %bool %24 %float_0 +OpSelectionMerge %26 None +OpBranchConditional %25 %27 %28 +%27 = OpLabel +%29 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 +%30 = OpLoad %float %29 +%31 = OpFOrdEqual %bool %30 %float_0 +OpSelectionMerge %32 None +OpBranchConditional %31 %33 %34 +%33 = OpLabel +OpStore %t %float_0 +OpBranch %32 +%34 = OpLabel +OpStore %t %float_0_25 +OpBranch %32 +%32 = OpLabel +OpBranch %26 +%28 = OpLabel +%35 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 +%36 = OpLoad %float %35 +%37 = OpFOrdEqual %bool %36 %float_0 +OpSelectionMerge %38 None +OpBranchConditional %37 %39 %40 +%39 = OpLabel +OpStore %t %float_0_5 +OpBranch %38 +%40 = OpLabel +OpStore %t %float_0_75 +OpBranch %38 +%38 = OpLabel +OpBranch %26 +%26 = OpLabel +%41 = OpLoad %float %t +%42 = OpCompositeConstruct %v4float %41 %41 %41 %41 +OpStore %OutColor %42 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateIfWithPhi) { + // Note: Assembly hand-optimized from GLSL + // + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // float t; + // if (BaseColor.x == 0) + // t = 0.0; + // else + // t = 1.0; + // OutColor = vec4(t); + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%float_1 = OpConstant %float 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %6 +%17 = OpLabel +%18 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 +%19 = OpLoad %float %18 +%20 = OpFOrdEqual %bool %19 %float_0 +OpSelectionMerge %21 None +OpBranchConditional %20 %22 %23 +%22 = OpLabel +OpBranch %21 +%23 = OpLabel +OpBranch %21 +%21 = OpLabel +%24 = OpPhi %float %float_0 %22 %float_1 %23 +%25 = OpCompositeConstruct %v4float %24 %24 %24 %24 +OpStore %OutColor %25 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateIfBreak) { + // Note: Assembly optimized from GLSL + // + // #version 450 + // + // layout(location=0) in vec4 InColor; + // layout(location=0) out vec4 OutColor; + // + // void main() + // { + // float f = 0.0; + // for (;;) { + // f += 2.0; + // if (f > 20.0) + // break; + // } + // + // OutColor = InColor / f; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %OutColor %InColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %f "f" +OpName %OutColor "OutColor" +OpName %InColor "InColor" +OpDecorate %OutColor Location 0 +OpDecorate %InColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%float_2 = OpConstant %float 2 +%float_20 = OpConstant %float 20 +%bool = OpTypeBool +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +%_ptr_Input_v4float = OpTypePointer Input %v4float +%InColor = OpVariable %_ptr_Input_v4float Input +%main = OpFunction %void None %7 +%17 = OpLabel +%f = OpVariable %_ptr_Function_float Function +OpStore %f %float_0 +OpBranch %18 +%18 = OpLabel +OpLoopMerge %19 %20 None +OpBranch %21 +%21 = OpLabel +%22 = OpLoad %float %f +%23 = OpFAdd %float %22 %float_2 +OpStore %f %23 +%24 = OpLoad %float %f +%25 = OpFOrdGreaterThan %bool %24 %float_20 +OpSelectionMerge %26 None +OpBranchConditional %25 %27 %26 +%27 = OpLabel +OpBranch %19 +%26 = OpLabel +OpBranch %20 +%20 = OpLabel +OpBranch %18 +%19 = OpLabel +%28 = OpLoad %v4float %InColor +%29 = OpLoad %float %f +%30 = OpCompositeConstruct %v4float %29 %29 %29 %29 +%31 = OpFDiv %v4float %28 %30 +OpStore %OutColor %31 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateIfBreak2) { + // Do not eliminate break as conditional branch with merge instruction + // Note: SPIR-V edited to add merge instruction before break. + // + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10]; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) + // s += g_F[i]; + // o = s; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %s "s" +OpName %i "i" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %10 +%25 = OpLabel +%s = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %s %float_0 +OpStore %i %int_0 +OpBranch %26 +%26 = OpLabel +OpLoopMerge %27 %28 None +OpBranch %29 +%29 = OpLabel +%30 = OpLoad %int %i +%31 = OpSLessThan %bool %30 %int_10 +OpSelectionMerge %32 None +OpBranchConditional %31 %32 %27 +%32 = OpLabel +%33 = OpLoad %int %i +%34 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %33 +%35 = OpLoad %float %34 +%36 = OpLoad %float %s +%37 = OpFAdd %float %36 %35 +OpStore %s %37 +OpBranch %28 +%28 = OpLabel +%38 = OpLoad %int %i +%39 = OpIAdd %int %38 %int_1 +OpStore %i %39 +OpBranch %26 +%27 = OpLabel +%40 = OpLoad %float %s +OpStore %o %40 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, EliminateEntireUselessLoop) { + // #version 140 + // in vec4 BaseColor; + // + // layout(std140) uniform U_t + // { + // int g_I ; + // } ; + // + // void main() + // { + // vec4 v = BaseColor; + // float df = 0.0; + // int i = 0; + // while (i < g_I) { + // df = df * 0.5; + // i = i + 1; + // } + // gl_FragColor = v; + // } + + const std::string predefs1 = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +)"; + + const std::string names_before = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %df "df" +OpName %i "i" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_I" +OpName %_ "" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string names_after = + R"(OpName %main "main" +OpName %v "v" +OpName %BaseColor "BaseColor" +OpName %gl_FragColor "gl_FragColor" +)"; + + const std::string predefs2_before = + R"(OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t Block +OpDecorate %_ DescriptorSet 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%U_t = OpTypeStruct %int +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_int = OpTypePointer Uniform %int +%bool = OpTypeBool +%float_0_5 = OpConstant %float 0.5 +%int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string predefs2_after = + R"(%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%gl_FragColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %11 +%27 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%df = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%28 = OpLoad %v4float %BaseColor +OpStore %v %28 +OpStore %df %float_0 +OpStore %i %int_0 +OpBranch %29 +%29 = OpLabel +OpLoopMerge %30 %31 None +OpBranch %32 +%32 = OpLabel +%33 = OpLoad %int %i +%34 = OpAccessChain %_ptr_Uniform_int %_ %int_0 +%35 = OpLoad %int %34 +%36 = OpSLessThan %bool %33 %35 +OpBranchConditional %36 %37 %30 +%37 = OpLabel +%38 = OpLoad %float %df +%39 = OpFMul %float %38 %float_0_5 +OpStore %df %39 +%40 = OpLoad %int %i +%41 = OpIAdd %int %40 %int_1 +OpStore %i %41 +OpBranch %31 +%31 = OpLabel +OpBranch %29 +%30 = OpLabel +%42 = OpLoad %v4float %v +OpStore %gl_FragColor %42 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %11 +%27 = OpLabel +%v = OpVariable %_ptr_Function_v4float Function +%28 = OpLoad %v4float %BaseColor +OpStore %v %28 +OpBranch %29 +%29 = OpLabel +OpBranch %30 +%30 = OpLabel +%42 = OpLoad %v4float %v +OpStore %gl_FragColor %42 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs1 + names_before + predefs2_before + func_before, + predefs1 + names_after + predefs2_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateBusyLoop) { + // Note: SPIR-V edited to replace AtomicAdd(i,0) with AtomicLoad(i) + // + // #version 450 + // + // layout(std430) buffer I_t + // { + // int g_I; + // int g_I2; + // }; + // + // layout(location = 0) out int o; + // + // void main(void) + // { + // while (atomicAdd(g_I, 0) == 0) {} + // o = g_I2; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %I_t "I_t" +OpMemberName %I_t 0 "g_I" +OpMemberName %I_t 1 "g_I2" +OpName %_ "" +OpName %o "o" +OpMemberDecorate %I_t 0 Offset 0 +OpMemberDecorate %I_t 1 Offset 4 +OpDecorate %I_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%I_t = OpTypeStruct %int %int +%_ptr_Uniform_I_t = OpTypePointer Uniform %I_t +%_ = OpVariable %_ptr_Uniform_I_t Uniform +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%_ptr_Uniform_int = OpTypePointer Uniform %int +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%uint_0 = OpConstant %uint 0 +%bool = OpTypeBool +%_ptr_Output_int = OpTypePointer Output %int +%o = OpVariable %_ptr_Output_int Output +%main = OpFunction %void None %7 +%18 = OpLabel +OpBranch %19 +%19 = OpLabel +OpLoopMerge %20 %21 None +OpBranch %22 +%22 = OpLabel +%23 = OpAccessChain %_ptr_Uniform_int %_ %int_0 +%24 = OpAtomicLoad %int %23 %uint_1 %uint_0 +%25 = OpIEqual %bool %24 %int_0 +OpBranchConditional %25 %26 %20 +%26 = OpLabel +OpBranch %21 +%21 = OpLabel +OpBranch %19 +%20 = OpLabel +%27 = OpAccessChain %_ptr_Uniform_int %_ %int_1 +%28 = OpLoad %int %27 +OpStore %o %28 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateLiveLoop) { + // Note: SPIR-V optimized + // + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10]; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) + // s += g_F[i]; + // o = s; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %8 +%21 = OpLabel +OpBranch %22 +%22 = OpLabel +%23 = OpPhi %float %float_0 %21 %24 %25 +%26 = OpPhi %int %int_0 %21 %27 %25 +OpLoopMerge %28 %25 None +OpBranch %29 +%29 = OpLabel +%30 = OpSLessThan %bool %26 %int_10 +OpBranchConditional %30 %31 %28 +%31 = OpLabel +%32 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %26 +%33 = OpLoad %float %32 +%24 = OpFAdd %float %23 %33 +OpBranch %25 +%25 = OpLabel +%27 = OpIAdd %int %26 %int_1 +OpBranch %22 +%28 = OpLabel +OpStore %o %23 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, EliminateEntireFunctionBody) { + // #version 450 + // + // layout(location = 0) in vec4 BaseColor; + // layout(location = 0) out vec4 OutColor; + // + // void main() + // { + // float d; + // if (BaseColor.x == 0) + // d = BaseColor.y; + // else + // d = BaseColor.z; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %d "d" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%_ptr_Input_float = OpTypePointer Input %float +%float_0 = OpConstant %float 0 +%bool = OpTypeBool +%_ptr_Function_float = OpTypePointer Function %float +%uint_1 = OpConstant %uint 1 +%uint_2 = OpConstant %uint 2 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %BaseColor %OutColor +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 450 +OpName %main "main" +OpName %BaseColor "BaseColor" +OpName %OutColor "OutColor" +OpDecorate %BaseColor Location 0 +OpDecorate %OutColor Location 0 +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%BaseColor = OpVariable %_ptr_Input_v4float Input +%_ptr_Output_v4float = OpTypePointer Output %v4float +%OutColor = OpVariable %_ptr_Output_v4float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %7 +%20 = OpLabel +%d = OpVariable %_ptr_Function_float Function +%21 = OpAccessChain %_ptr_Input_float %BaseColor %uint_0 +%22 = OpLoad %float %21 +%23 = OpFOrdEqual %bool %22 %float_0 +OpSelectionMerge %24 None +OpBranchConditional %23 %25 %26 +%25 = OpLabel +%27 = OpAccessChain %_ptr_Input_float %BaseColor %uint_1 +%28 = OpLoad %float %27 +OpStore %d %28 +OpBranch %24 +%26 = OpLabel +%29 = OpAccessChain %_ptr_Input_float %BaseColor %uint_2 +%30 = OpLoad %float %29 +OpStore %d %30 +OpBranch %24 +%24 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %7 +%20 = OpLabel +OpBranch %24 +%24 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + func_before, predefs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, EliminateUselessInnerLoop) { + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10]; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) { + // for (int j=0; j<10; j++) { + // } + // s += g_F[i]; + // } + // o = s; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %s "s" +OpName %i "i" +OpName %j "j" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %s "s" +OpName %i "i" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%11 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_1 = OpConstant %int 1 +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %11 +%26 = OpLabel +%s = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%j = OpVariable %_ptr_Function_int Function +OpStore %s %float_0 +OpStore %i %int_0 +OpBranch %27 +%27 = OpLabel +OpLoopMerge %28 %29 None +OpBranch %30 +%30 = OpLabel +%31 = OpLoad %int %i +%32 = OpSLessThan %bool %31 %int_10 +OpBranchConditional %32 %33 %28 +%33 = OpLabel +OpStore %j %int_0 +OpBranch %34 +%34 = OpLabel +OpLoopMerge %35 %36 None +OpBranch %37 +%37 = OpLabel +%38 = OpLoad %int %j +%39 = OpSLessThan %bool %38 %int_10 +OpBranchConditional %39 %40 %35 +%40 = OpLabel +OpBranch %36 +%36 = OpLabel +%41 = OpLoad %int %j +%42 = OpIAdd %int %41 %int_1 +OpStore %j %42 +OpBranch %34 +%35 = OpLabel +%43 = OpLoad %int %i +%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %43 +%45 = OpLoad %float %44 +%46 = OpLoad %float %s +%47 = OpFAdd %float %46 %45 +OpStore %s %47 +OpBranch %29 +%29 = OpLabel +%48 = OpLoad %int %i +%49 = OpIAdd %int %48 %int_1 +OpStore %i %49 +OpBranch %27 +%28 = OpLabel +%50 = OpLoad %float %s +OpStore %o %50 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %11 +%26 = OpLabel +%s = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %s %float_0 +OpStore %i %int_0 +OpBranch %27 +%27 = OpLabel +OpLoopMerge %28 %29 None +OpBranch %30 +%30 = OpLabel +%31 = OpLoad %int %i +%32 = OpSLessThan %bool %31 %int_10 +OpBranchConditional %32 %33 %28 +%33 = OpLabel +OpBranch %34 +%34 = OpLabel +OpBranch %35 +%35 = OpLabel +%43 = OpLoad %int %i +%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %43 +%45 = OpLoad %float %44 +%46 = OpLoad %float %s +%47 = OpFAdd %float %46 %45 +OpStore %s %47 +OpBranch %29 +%29 = OpLabel +%48 = OpLoad %int %i +%49 = OpIAdd %int %48 %int_1 +OpStore %i %49 +OpBranch %27 +%28 = OpLabel +%50 = OpLoad %float %s +OpStore %o %50 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + func_before, predefs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, EliminateUselessNestedLoopWithIf) { + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10][10]; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) { + // for (int j=0; j<10; j++) { + // float t = g_F[i][j]; + // if (t > 0.0) + // s += t; + // } + // } + // o = 0.0; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %s "s" +OpName %i "i" +OpName %j "j" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpDecorate %_arr__arr_float_uint_10_uint_10 ArrayStride 40 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 +%U_t = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %o "o" +OpDecorate %o Location 0 +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %12 +%27 = OpLabel +%s = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%j = OpVariable %_ptr_Function_int Function +OpStore %s %float_0 +OpStore %i %int_0 +OpBranch %28 +%28 = OpLabel +OpLoopMerge %29 %30 None +OpBranch %31 +%31 = OpLabel +%32 = OpLoad %int %i +%33 = OpSLessThan %bool %32 %int_10 +OpBranchConditional %33 %34 %29 +%34 = OpLabel +OpStore %j %int_0 +OpBranch %35 +%35 = OpLabel +OpLoopMerge %36 %37 None +OpBranch %38 +%38 = OpLabel +%39 = OpLoad %int %j +%40 = OpSLessThan %bool %39 %int_10 +OpBranchConditional %40 %41 %36 +%41 = OpLabel +%42 = OpLoad %int %i +%43 = OpLoad %int %j +%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %42 %43 +%45 = OpLoad %float %44 +%46 = OpFOrdGreaterThan %bool %45 %float_0 +OpSelectionMerge %47 None +OpBranchConditional %46 %48 %47 +%48 = OpLabel +%49 = OpLoad %float %s +%50 = OpFAdd %float %49 %45 +OpStore %s %50 +OpBranch %47 +%47 = OpLabel +OpBranch %37 +%37 = OpLabel +%51 = OpLoad %int %j +%52 = OpIAdd %int %51 %int_1 +OpStore %j %52 +OpBranch %35 +%36 = OpLabel +OpBranch %30 +%30 = OpLabel +%53 = OpLoad %int %i +%54 = OpIAdd %int %53 %int_1 +OpStore %i %54 +OpBranch %28 +%29 = OpLabel +OpStore %o %float_0 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %12 +%27 = OpLabel +OpBranch %28 +%28 = OpLabel +OpBranch %29 +%29 = OpLabel +OpStore %o %float_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + func_before, predefs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, EliminateEmptyIfBeforeContinue) { + // #version 430 + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) { + // s += 1.0; + // if (i > s) {} + // } + // o = s; + // } + + const std::string predefs_before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %3 +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpName %main "main" +OpDecorate %3 Location 0 +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%3 = OpVariable %_ptr_Output_float Output +)"; + + const std::string predefs_after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %3 +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpName %main "main" +OpDecorate %3 Location 0 +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%float_1 = OpConstant %float 1 +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%3 = OpVariable %_ptr_Output_float Output +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %5 +%16 = OpLabel +OpBranch %17 +%17 = OpLabel +%18 = OpPhi %float %float_0 %16 %19 %20 +%21 = OpPhi %int %int_0 %16 %22 %20 +OpLoopMerge %23 %20 None +OpBranch %24 +%24 = OpLabel +%25 = OpSLessThan %bool %21 %int_10 +OpBranchConditional %25 %26 %23 +%26 = OpLabel +%19 = OpFAdd %float %18 %float_1 +%27 = OpConvertFToS %int %19 +%28 = OpSGreaterThan %bool %21 %27 +OpSelectionMerge %20 None +OpBranchConditional %28 %29 %20 +%29 = OpLabel +OpBranch %20 +%20 = OpLabel +%22 = OpIAdd %int %21 %int_1 +OpBranch %17 +%23 = OpLabel +OpStore %3 %18 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %5 +%16 = OpLabel +OpBranch %17 +%17 = OpLabel +%18 = OpPhi %float %float_0 %16 %19 %20 +%21 = OpPhi %int %int_0 %16 %22 %20 +OpLoopMerge %23 %20 None +OpBranch %24 +%24 = OpLabel +%25 = OpSLessThan %bool %21 %int_10 +OpBranchConditional %25 %26 %23 +%26 = OpLabel +%19 = OpFAdd %float %18 %float_1 +OpBranch %20 +%20 = OpLabel +%22 = OpIAdd %int %21 %int_1 +OpBranch %17 +%23 = OpLabel +OpStore %3 %18 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs_before + func_before, predefs_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateLiveNestedLoopWithIf) { + // Note: SPIR-V optimized + // + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10][10]; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) { + // for (int j=0; j<10; j++) { + // float t = g_F[i][j]; + // if (t > 0.0) + // s += t; + // } + // } + // o = s; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %s "s" +OpName %i "i" +OpName %j "j" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpDecorate %_arr__arr_float_uint_10_uint_10 ArrayStride 40 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%12 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_arr__arr_float_uint_10_uint_10 = OpTypeArray %_arr_float_uint_10 %uint_10 +%U_t = OpTypeStruct %_arr__arr_float_uint_10_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %12 +%27 = OpLabel +%s = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +%j = OpVariable %_ptr_Function_int Function +OpStore %s %float_0 +OpStore %i %int_0 +OpBranch %28 +%28 = OpLabel +OpLoopMerge %29 %30 None +OpBranch %31 +%31 = OpLabel +%32 = OpLoad %int %i +%33 = OpSLessThan %bool %32 %int_10 +OpBranchConditional %33 %34 %29 +%34 = OpLabel +OpStore %j %int_0 +OpBranch %35 +%35 = OpLabel +OpLoopMerge %36 %37 None +OpBranch %38 +%38 = OpLabel +%39 = OpLoad %int %j +%40 = OpSLessThan %bool %39 %int_10 +OpBranchConditional %40 %41 %36 +%41 = OpLabel +%42 = OpLoad %int %i +%43 = OpLoad %int %j +%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %42 %43 +%45 = OpLoad %float %44 +%46 = OpFOrdGreaterThan %bool %45 %float_0 +OpSelectionMerge %47 None +OpBranchConditional %46 %48 %47 +%48 = OpLabel +%49 = OpLoad %float %s +%50 = OpFAdd %float %49 %45 +OpStore %s %50 +OpBranch %47 +%47 = OpLabel +OpBranch %37 +%37 = OpLabel +%51 = OpLoad %int %j +%52 = OpIAdd %int %51 %int_1 +OpStore %j %52 +OpBranch %35 +%36 = OpLabel +OpBranch %30 +%30 = OpLabel +%53 = OpLoad %int %i +%54 = OpIAdd %int %53 %int_1 +OpStore %i %54 +OpBranch %28 +%29 = OpLabel +%55 = OpLoad %float %s +OpStore %o %55 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateIfContinue) { + // Do not eliminate continue embedded in if construct + // + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10]; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) { + // if (i % 2 == 0) continue; + // s += g_F[i]; + // } + // o = s; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %s "s" +OpName %i "i" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_2 = OpConstant %int 2 +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %10 +%26 = OpLabel +%s = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %s %float_0 +OpStore %i %int_0 +OpBranch %27 +%27 = OpLabel +OpLoopMerge %28 %29 None +OpBranch %30 +%30 = OpLabel +%31 = OpLoad %int %i +%32 = OpSLessThan %bool %31 %int_10 +OpBranchConditional %32 %33 %28 +%33 = OpLabel +%34 = OpLoad %int %i +%35 = OpSMod %int %34 %int_2 +%36 = OpIEqual %bool %35 %int_0 +OpSelectionMerge %37 None +OpBranchConditional %36 %38 %37 +%38 = OpLabel +OpBranch %29 +%37 = OpLabel +%39 = OpLoad %int %i +%40 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %39 +%41 = OpLoad %float %40 +%42 = OpLoad %float %s +%43 = OpFAdd %float %42 %41 +OpStore %s %43 +OpBranch %29 +%29 = OpLabel +%44 = OpLoad %int %i +%45 = OpIAdd %int %44 %int_1 +OpStore %i %45 +OpBranch %27 +%28 = OpLabel +%46 = OpLoad %float %s +OpStore %o %46 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateIfContinue2) { + // Do not eliminate continue not embedded in if construct + // + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10]; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) { + // if (i % 2 == 0) continue; + // s += g_F[i]; + // } + // o = s; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %s "s" +OpName %i "i" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_2 = OpConstant %int 2 +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %10 +%26 = OpLabel +%s = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %s %float_0 +OpStore %i %int_0 +OpBranch %27 +%27 = OpLabel +OpLoopMerge %28 %29 None +OpBranch %30 +%30 = OpLabel +%31 = OpLoad %int %i +%32 = OpSLessThan %bool %31 %int_10 +OpBranchConditional %32 %33 %28 +%33 = OpLabel +%34 = OpLoad %int %i +%35 = OpSMod %int %34 %int_2 +%36 = OpIEqual %bool %35 %int_0 +OpBranchConditional %36 %29 %37 +%37 = OpLabel +%38 = OpLoad %int %i +%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %38 +%40 = OpLoad %float %39 +%41 = OpLoad %float %s +%42 = OpFAdd %float %41 %40 +OpStore %s %42 +OpBranch %29 +%29 = OpLabel +%43 = OpLoad %int %i +%44 = OpIAdd %int %43 %int_1 +OpStore %i %44 +OpBranch %27 +%28 = OpLabel +%45 = OpLoad %float %s +OpStore %o %45 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +TEST_F(AggressiveDCETest, NoEliminateIfContinue3) { + // Do not eliminate continue as conditional branch with merge instruction + // Note: SPIR-V edited to add merge instruction before continue. + // + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10]; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // float s = 0.0; + // for (int i=0; i<10; i++) { + // if (i % 2 == 0) continue; + // s += g_F[i]; + // } + // o = s; + // } + + const std::string assembly = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %s "s" +OpName %i "i" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float +%float_0 = OpConstant %float 0 +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%int_2 = OpConstant %int 2 +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %10 +%26 = OpLabel +%s = OpVariable %_ptr_Function_float Function +%i = OpVariable %_ptr_Function_int Function +OpStore %s %float_0 +OpStore %i %int_0 +OpBranch %27 +%27 = OpLabel +OpLoopMerge %28 %29 None +OpBranch %30 +%30 = OpLabel +%31 = OpLoad %int %i +%32 = OpSLessThan %bool %31 %int_10 +OpBranchConditional %32 %33 %28 +%33 = OpLabel +%34 = OpLoad %int %i +%35 = OpSMod %int %34 %int_2 +%36 = OpIEqual %bool %35 %int_0 +OpSelectionMerge %37 None +OpBranchConditional %36 %29 %37 +%37 = OpLabel +%38 = OpLoad %int %i +%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %38 +%40 = OpLoad %float %39 +%41 = OpLoad %float %s +%42 = OpFAdd %float %41 %40 +OpStore %s %42 +OpBranch %29 +%29 = OpLabel +%43 = OpLoad %int %i +%44 = OpIAdd %int %43 %int_1 +OpStore %i %44 +OpBranch %27 +%28 = OpLabel +%45 = OpLoad %float %s +OpStore %o %45 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(assembly, assembly, true, true); +} + +// This is not valid input and ADCE does not support variable pointers and only +// supports shaders. +TEST_F(AggressiveDCETest, PointerVariable) { + // ADCE is able to handle code that contains a load whose base address + // comes from a load and not an OpVariable. I want to see an instruction + // removed to be sure that ADCE is not exiting early. + + const std::string before = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_runtimearr__struct_3 ArrayStride 16 +OpMemberDecorate %_struct_5 0 Offset 0 +OpDecorate %_struct_5 BufferBlock +OpMemberDecorate %_struct_6 0 Offset 0 +OpDecorate %_struct_6 BufferBlock +OpDecorate %2 Location 0 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +OpDecorate %8 DescriptorSet 0 +OpDecorate %8 Binding 1 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_struct_3 = OpTypeStruct %v4float +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 +%_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 +%_struct_6 = OpTypeStruct %int +%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6 +%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5 +%_ptr_Function__ptr_Uniform__struct_6 = OpTypePointer Function %_ptr_Uniform__struct_6 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%2 = OpVariable %_ptr_Output_v4float Output +%7 = OpVariable %_ptr_Uniform__struct_5 Uniform +%8 = OpVariable %_ptr_Uniform__struct_6 Uniform +%1 = OpFunction %void None %10 +%23 = OpLabel +%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function +OpStore %24 %7 +%26 = OpLoad %_ptr_Uniform__struct_5 %24 +%27 = OpAccessChain %_ptr_Uniform_v4float %26 %int_0 %uint_0 %int_0 +%28 = OpLoad %v4float %27 +%29 = OpCopyObject %v4float %28 +OpStore %2 %28 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" %2 +OpExecutionMode %1 OriginUpperLeft +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_runtimearr__struct_3 ArrayStride 16 +OpMemberDecorate %_struct_5 0 Offset 0 +OpDecorate %_struct_5 BufferBlock +OpDecorate %2 Location 0 +OpDecorate %7 DescriptorSet 0 +OpDecorate %7 Binding 0 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%uint = OpTypeInt 32 0 +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float +%_struct_3 = OpTypeStruct %v4float +%_runtimearr__struct_3 = OpTypeRuntimeArray %_struct_3 +%_struct_5 = OpTypeStruct %_runtimearr__struct_3 +%_ptr_Uniform__struct_5 = OpTypePointer Uniform %_struct_5 +%_ptr_Function__ptr_Uniform__struct_5 = OpTypePointer Function %_ptr_Uniform__struct_5 +%int_0 = OpConstant %int 0 +%uint_0 = OpConstant %uint 0 +%2 = OpVariable %_ptr_Output_v4float Output +%7 = OpVariable %_ptr_Uniform__struct_5 Uniform +%1 = OpFunction %void None %10 +%23 = OpLabel +%24 = OpVariable %_ptr_Function__ptr_Uniform__struct_5 Function +OpStore %24 %7 +%25 = OpLoad %_ptr_Uniform__struct_5 %24 +%26 = OpAccessChain %_ptr_Uniform_v4float %25 %int_0 %uint_0 %int_0 +%27 = OpLoad %v4float %26 +OpStore %2 %27 +OpReturn +OpFunctionEnd +)"; + + // The input is not valid and ADCE only supports shaders, but not variable + // pointers. Workaround this by enabling relaxed logical pointers in the + // validator. + ValidatorOptions()->relax_logical_pointer = true; + SinglePassRunAndCheck(before, after, true, true); +} + +// %dead is unused. Make sure we remove it along with its name. +TEST_F(AggressiveDCETest, RemoveUnreferenced) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %dead "dead" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%dead = OpVariable %_ptr_Private_float Private +%main = OpFunction %void None %5 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%main = OpFunction %void None %5 +%8 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +// Delete %dead because it is unreferenced. Then %initializer becomes +// unreferenced, so remove it as well. +TEST_F(AggressiveDCETest, RemoveUnreferencedWithInit1) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %dead "dead" +OpName %initializer "initializer" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%initializer = OpVariable %_ptr_Private_float Private +%dead = OpVariable %_ptr_Private_float Private %initializer +%main = OpFunction %void None %6 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%main = OpFunction %void None %6 +%9 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +// Keep %live because it is used, and its initializer. +TEST_F(AggressiveDCETest, KeepReferenced) { + const std::string before = + R"(OpCapability Shader +OpCapability Linkage +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %output +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 150 +OpName %main "main" +OpName %live "live" +OpName %initializer "initializer" +OpName %output "output" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%float = OpTypeFloat 32 +%_ptr_Private_float = OpTypePointer Private %float +%initializer = OpConstant %float 0 +%live = OpVariable %_ptr_Private_float Private %initializer +%_ptr_Output_float = OpTypePointer Output %float +%output = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %6 +%9 = OpLabel +%10 = OpLoad %float %live +OpStore %output %10 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, before, true, true); +} + +// This test that the decoration associated with a variable are removed when the +// variable is removed. +TEST_F(AggressiveDCETest, RemoveVariableAndDecorations) { + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpSource GLSL 450 +OpName %main "main" +OpName %B "B" +OpMemberName %B 0 "a" +OpName %Bdat "Bdat" +OpMemberDecorate %B 0 Offset 0 +OpDecorate %B BufferBlock +OpDecorate %Bdat DescriptorSet 0 +OpDecorate %Bdat Binding 0 +%void = OpTypeVoid +%6 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%B = OpTypeStruct %uint +%_ptr_Uniform_B = OpTypePointer Uniform %B +%Bdat = OpVariable %_ptr_Uniform_B Uniform +%int = OpTypeInt 32 1 +%int_0 = OpConstant %int 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Uniform_uint = OpTypePointer Uniform %uint +%main = OpFunction %void None %6 +%13 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpSource GLSL 450 +OpName %main "main" +%void = OpTypeVoid +%6 = OpTypeFunction %void +%main = OpFunction %void None %6 +%13 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(AggressiveDCETest, DeadNestedSwitch) { + const std::string text = R"( +; CHECK: OpLabel +; CHECK: OpBranch [[block:%\w+]] +; CHECK-NOT: OpSwitch +; CHECK-NEXT: [[block]] = OpLabel +; CHECK: OpBranch [[block:%\w+]] +; CHECK-NOT: OpSwitch +; CHECK-NEXT: [[block]] = OpLabel +; CHECK-NEXT: OpStore +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" %x +OpExecutionMode %func OriginUpperLeft +OpName %func "func" +%void = OpTypeVoid +%1 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_ptr_Output = OpTypePointer Output %uint +%uint_ptr_Input = OpTypePointer Input %uint +%x = OpVariable %uint_ptr_Output Output +%a = OpVariable %uint_ptr_Input Input +%func = OpFunction %void None %1 +%entry = OpLabel +OpBranch %header +%header = OpLabel +%ld = OpLoad %uint %a +OpLoopMerge %merge %continue None +OpBranch %postheader +%postheader = OpLabel +; This switch doesn't require an OpSelectionMerge and is nested in the dead loop. +OpSwitch %ld %merge 0 %extra 1 %continue +%extra = OpLabel +OpBranch %continue +%continue = OpLabel +OpBranch %header +%merge = OpLabel +OpStore %x %uint_0 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, LiveNestedSwitch) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" %3 %10 +OpExecutionMode %func OriginUpperLeft +OpName %func "func" +%void = OpTypeVoid +%1 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%_ptr_Output_uint = OpTypePointer Output %uint +%_ptr_Input_uint = OpTypePointer Input %uint +%3 = OpVariable %_ptr_Output_uint Output +%10 = OpVariable %_ptr_Input_uint Input +%func = OpFunction %void None %1 +%11 = OpLabel +OpBranch %12 +%12 = OpLabel +%13 = OpLoad %uint %10 +OpLoopMerge %14 %15 None +OpBranch %16 +%16 = OpLabel +OpSwitch %13 %14 0 %17 1 %15 +%17 = OpLabel +OpStore %3 %uint_1 +OpBranch %15 +%15 = OpLabel +OpBranch %12 +%14 = OpLabel +OpStore %3 %uint_0 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, text, false, true); +} + +TEST_F(AggressiveDCETest, BasicDeleteDeadFunction) { + // The function Dead should be removed because it is never called. + const std::vector common_code = { + // clang-format off + "OpCapability Shader", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\"", + "OpName %main \"main\"", + "OpName %Live \"Live\"", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%main = OpFunction %void None %7", + "%15 = OpLabel", + "%16 = OpFunctionCall %void %Live", + "%17 = OpFunctionCall %void %Live", + "OpReturn", + "OpFunctionEnd", + "%Live = OpFunction %void None %7", + "%20 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + const std::vector dead_function = { + // clang-format off + "%Dead = OpFunction %void None %7", + "%19 = OpLabel", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck( + JoinAllInsts(Concat(common_code, dead_function)), + JoinAllInsts(common_code), /* skip_nop = */ true); +} + +TEST_F(AggressiveDCETest, BasicKeepLiveFunction) { + // Everything is reachable from an entry point, so no functions should be + // deleted. + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Fragment %main \"main\"", + "OpName %main \"main\"", + "OpName %Live1 \"Live1\"", + "OpName %Live2 \"Live2\"", + "%void = OpTypeVoid", + "%7 = OpTypeFunction %void", + "%main = OpFunction %void None %7", + "%15 = OpLabel", + "%16 = OpFunctionCall %void %Live2", + "%17 = OpFunctionCall %void %Live1", + "OpReturn", + "OpFunctionEnd", + "%Live1 = OpFunction %void None %7", + "%19 = OpLabel", + "OpReturn", + "OpFunctionEnd", + "%Live2 = OpFunction %void None %7", + "%20 = OpLabel", + "OpReturn", + "OpFunctionEnd" + // clang-format on + }; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + std::string assembly = JoinAllInsts(text); + auto result = SinglePassRunAndDisassemble( + assembly, /* skip_nop = */ true, /* do_validation = */ false); + EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result)); + EXPECT_EQ(assembly, std::get<0>(result)); +} + +TEST_F(AggressiveDCETest, BasicRemoveDecorationsAndNames) { + // We want to remove the names and decorations associated with results that + // are removed. This test will check for that. + const std::string text = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpName %main "main" + OpName %Dead "Dead" + OpName %x "x" + OpName %y "y" + OpName %z "z" + OpDecorate %x RelaxedPrecision + OpDecorate %y RelaxedPrecision + OpDecorate %z RelaxedPrecision + OpDecorate %6 RelaxedPrecision + OpDecorate %7 RelaxedPrecision + OpDecorate %8 RelaxedPrecision + %void = OpTypeVoid + %10 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %10 + %14 = OpLabel + OpReturn + OpFunctionEnd + %Dead = OpFunction %void None %10 + %15 = OpLabel + %x = OpVariable %_ptr_Function_float Function + %y = OpVariable %_ptr_Function_float Function + %z = OpVariable %_ptr_Function_float Function + OpStore %x %float_1 + OpStore %y %float_1 + %6 = OpLoad %float %x + %7 = OpLoad %float %y + %8 = OpFAdd %float %6 %7 + OpStore %z %8 + OpReturn + OpFunctionEnd)"; + + const std::string expected_output = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %main "main" +OpName %main "main" +%void = OpTypeVoid +%10 = OpTypeFunction %void +%main = OpFunction %void None %10 +%14 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, expected_output, + /* skip_nop = */ true); +} + +TEST_F(AggressiveDCETest, BasicAllDeadConstants) { + const std::string text = R"( + ; CHECK-NOT: OpConstant + OpCapability Shader + OpCapability Float64 + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %main "main" + OpName %main "main" + %void = OpTypeVoid + %4 = OpTypeFunction %void + %bool = OpTypeBool + %true = OpConstantTrue %bool + %false = OpConstantFalse %bool + %int = OpTypeInt 32 1 + %9 = OpConstant %int 1 + %uint = OpTypeInt 32 0 + %11 = OpConstant %uint 2 + %float = OpTypeFloat 32 + %13 = OpConstant %float 3.1415 + %double = OpTypeFloat 64 + %15 = OpConstant %double 3.14159265358979 + %main = OpFunction %void None %4 + %16 = OpLabel + OpReturn + OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, BasicNoneDeadConstants) { + const std::vector text = { + // clang-format off + "OpCapability Shader", + "OpCapability Float64", + "%1 = OpExtInstImport \"GLSL.std.450\"", + "OpMemoryModel Logical GLSL450", + "OpEntryPoint Vertex %main \"main\" %btv %bfv %iv %uv %fv %dv", + "OpName %main \"main\"", + "OpName %btv \"btv\"", + "OpName %bfv \"bfv\"", + "OpName %iv \"iv\"", + "OpName %uv \"uv\"", + "OpName %fv \"fv\"", + "OpName %dv \"dv\"", + "%void = OpTypeVoid", + "%10 = OpTypeFunction %void", + "%bool = OpTypeBool", + "%_ptr_Output_bool = OpTypePointer Output %bool", + "%true = OpConstantTrue %bool", + "%false = OpConstantFalse %bool", + "%int = OpTypeInt 32 1", + "%_ptr_Output_int = OpTypePointer Output %int", + "%int_1 = OpConstant %int 1", + "%uint = OpTypeInt 32 0", + "%_ptr_Output_uint = OpTypePointer Output %uint", + "%uint_2 = OpConstant %uint 2", + "%float = OpTypeFloat 32", + "%_ptr_Output_float = OpTypePointer Output %float", + "%float_3_1415 = OpConstant %float 3.1415", + "%double = OpTypeFloat 64", + "%_ptr_Output_double = OpTypePointer Output %double", + "%double_3_14159265358979 = OpConstant %double 3.14159265358979", + "%btv = OpVariable %_ptr_Output_bool Output", + "%bfv = OpVariable %_ptr_Output_bool Output", + "%iv = OpVariable %_ptr_Output_int Output", + "%uv = OpVariable %_ptr_Output_uint Output", + "%fv = OpVariable %_ptr_Output_float Output", + "%dv = OpVariable %_ptr_Output_double Output", + "%main = OpFunction %void None %10", + "%27 = OpLabel", + "OpStore %btv %true", + "OpStore %bfv %false", + "OpStore %iv %int_1", + "OpStore %uv %uint_2", + "OpStore %fv %float_3_1415", + "OpStore %dv %double_3_14159265358979", + "OpReturn", + "OpFunctionEnd", + // clang-format on + }; + // All constants are used, so none of them should be eliminated. + SinglePassRunAndCheck( + JoinAllInsts(text), JoinAllInsts(text), /* skip_nop = */ true); +} + +struct AggressiveEliminateDeadConstantTestCase { + // Type declarations and constants that should be kept. + std::vector used_consts; + // Instructions that refer to constants, this is added to create uses for + // some constants so they won't be treated as dead constants. + std::vector main_insts; + // Dead constants that should be removed. + std::vector dead_consts; + // Expectations + std::vector checks; +}; + +// All types that are potentially required in +// AggressiveEliminateDeadConstantTest. +const std::vector CommonTypes = { + // clang-format off + // scalar types + "%bool = OpTypeBool", + "%uint = OpTypeInt 32 0", + "%int = OpTypeInt 32 1", + "%float = OpTypeFloat 32", + "%double = OpTypeFloat 64", + // vector types + "%v2bool = OpTypeVector %bool 2", + "%v2uint = OpTypeVector %uint 2", + "%v2int = OpTypeVector %int 2", + "%v3int = OpTypeVector %int 3", + "%v4int = OpTypeVector %int 4", + "%v2float = OpTypeVector %float 2", + "%v3float = OpTypeVector %float 3", + "%v2double = OpTypeVector %double 2", + // variable pointer types + "%_pf_bool = OpTypePointer Output %bool", + "%_pf_uint = OpTypePointer Output %uint", + "%_pf_int = OpTypePointer Output %int", + "%_pf_float = OpTypePointer Output %float", + "%_pf_double = OpTypePointer Output %double", + "%_pf_v2int = OpTypePointer Output %v2int", + "%_pf_v3int = OpTypePointer Output %v3int", + "%_pf_v2float = OpTypePointer Output %v2float", + "%_pf_v3float = OpTypePointer Output %v3float", + "%_pf_v2double = OpTypePointer Output %v2double", + // struct types + "%inner_struct = OpTypeStruct %bool %int %float %double", + "%outer_struct = OpTypeStruct %inner_struct %int %double", + "%flat_struct = OpTypeStruct %bool %int %float %double", + // clang-format on +}; + +using AggressiveEliminateDeadConstantTest = + PassTest<::testing::TestWithParam>; + +TEST_P(AggressiveEliminateDeadConstantTest, Custom) { + auto& tc = GetParam(); + AssemblyBuilder builder; + builder.AppendTypesConstantsGlobals(CommonTypes) + .AppendTypesConstantsGlobals(tc.used_consts) + .AppendInMain(tc.main_insts); + const std::string expected = builder.GetCode(); + builder.AppendTypesConstantsGlobals(tc.dead_consts); + builder.PrependPreamble(tc.checks); + const std::string assembly_with_dead_const = builder.GetCode(); + + // Do not enable validation. As the input code is invalid from the base + // tests (ported from other passes). + SinglePassRunAndMatch(assembly_with_dead_const, false); +} + +INSTANTIATE_TEST_SUITE_P( + ScalarTypeConstants, AggressiveEliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Scalar type constants, one dead constant and one used constant. + { + /* .used_consts = */ + { + "%used_const_int = OpConstant %int 1", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Output", + "OpStore %int_var %used_const_int", + }, + /* .dead_consts = */ + { + "%dead_const_int = OpConstant %int 1", + }, + /* .checks = */ + { + "; CHECK: [[const:%\\w+]] = OpConstant %int 1", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[const]]", + }, + }, + { + /* .used_consts = */ + { + "%used_const_uint = OpConstant %uint 1", + }, + /* .main_insts = */ + { + "%uint_var = OpVariable %_pf_uint Output", + "OpStore %uint_var %used_const_uint", + }, + /* .dead_consts = */ + { + "%dead_const_uint = OpConstant %uint 1", + }, + /* .checks = */ + { + "; CHECK: [[const:%\\w+]] = OpConstant %uint 1", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[const]]", + }, + }, + { + /* .used_consts = */ + { + "%used_const_float = OpConstant %float 3.1415", + }, + /* .main_insts = */ + { + "%float_var = OpVariable %_pf_float Output", + "OpStore %float_var %used_const_float", + }, + /* .dead_consts = */ + { + "%dead_const_float = OpConstant %float 3.1415", + }, + /* .checks = */ + { + "; CHECK: [[const:%\\w+]] = OpConstant %float 3.1415", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[const]]", + }, + }, + { + /* .used_consts = */ + { + "%used_const_double = OpConstant %double 3.14", + }, + /* .main_insts = */ + { + "%double_var = OpVariable %_pf_double Output", + "OpStore %double_var %used_const_double", + }, + /* .dead_consts = */ + { + "%dead_const_double = OpConstant %double 3.14", + }, + /* .checks = */ + { + "; CHECK: [[const:%\\w+]] = OpConstant %double 3.14", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[const]]", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + VectorTypeConstants, AggressiveEliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Tests eliminating dead constant type ivec2. One dead constant vector + // and one used constant vector, each built from its own group of + // scalar constants. + { + /* .used_consts = */ + { + "%used_int_x = OpConstant %int 1", + "%used_int_y = OpConstant %int 2", + "%used_v2int = OpConstantComposite %v2int %used_int_x %used_int_y", + }, + /* .main_insts = */ + { + "%v2int_var = OpVariable %_pf_v2int Output", + "OpStore %v2int_var %used_v2int", + }, + /* .dead_consts = */ + { + "%dead_int_x = OpConstant %int 1", + "%dead_int_y = OpConstant %int 2", + "%dead_v2int = OpConstantComposite %v2int %dead_int_x %dead_int_y", + }, + /* .checks = */ + { + "; CHECK: [[constx:%\\w+]] = OpConstant %int 1", + "; CHECK: [[consty:%\\w+]] = OpConstant %int 2", + "; CHECK: [[const:%\\w+]] = OpConstantComposite %v2int [[constx]] [[consty]]", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[const]]", + }, + }, + // Tests eliminating dead constant ivec3. One dead constant vector and + // one used constant vector. But both built from a same group of + // scalar constants. + { + /* .used_consts = */ + { + "%used_int_x = OpConstant %int 1", + "%used_int_y = OpConstant %int 2", + "%used_int_z = OpConstant %int 3", + "%used_v3int = OpConstantComposite %v3int %used_int_x %used_int_y %used_int_z", + }, + /* .main_insts = */ + { + "%v3int_var = OpVariable %_pf_v3int Output", + "OpStore %v3int_var %used_v3int", + }, + /* .dead_consts = */ + { + "%dead_v3int = OpConstantComposite %v3int %used_int_x %used_int_y %used_int_z", + }, + /* .checks = */ + { + "; CHECK: [[constx:%\\w+]] = OpConstant %int 1", + "; CHECK: [[consty:%\\w+]] = OpConstant %int 2", + "; CHECK: [[constz:%\\w+]] = OpConstant %int 3", + "; CHECK: [[const:%\\w+]] = OpConstantComposite %v3int [[constx]] [[consty]] [[constz]]", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[const]]", + }, + }, + // Tests eliminating dead constant vec2. One dead constant vector and + // one used constant vector. Each built from its own group of scalar + // constants. + { + /* .used_consts = */ + { + "%used_float_x = OpConstant %float 3.1415", + "%used_float_y = OpConstant %float 4.13", + "%used_v2float = OpConstantComposite %v2float %used_float_x %used_float_y", + }, + /* .main_insts = */ + { + "%v2float_var = OpVariable %_pf_v2float Output", + "OpStore %v2float_var %used_v2float", + }, + /* .dead_consts = */ + { + "%dead_float_x = OpConstant %float 3.1415", + "%dead_float_y = OpConstant %float 4.13", + "%dead_v2float = OpConstantComposite %v2float %dead_float_x %dead_float_y", + }, + /* .checks = */ + { + "; CHECK: [[constx:%\\w+]] = OpConstant %float 3.1415", + "; CHECK: [[consty:%\\w+]] = OpConstant %float 4.13", + "; CHECK: [[const:%\\w+]] = OpConstantComposite %v2float [[constx]] [[consty]]", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[const]]", + }, + }, + // Tests eliminating dead constant vec3. One dead constant vector and + // one used constant vector. Both built from a same group of scalar + // constants. + { + /* .used_consts = */ + { + "%used_float_x = OpConstant %float 3.1415", + "%used_float_y = OpConstant %float 4.25", + "%used_float_z = OpConstant %float 4.75", + "%used_v3float = OpConstantComposite %v3float %used_float_x %used_float_y %used_float_z", + }, + /* .main_insts = */ + { + "%v3float_var = OpVariable %_pf_v3float Output", + "OpStore %v3float_var %used_v3float", + }, + /* .dead_consts = */ + { + "%dead_v3float = OpConstantComposite %v3float %used_float_x %used_float_y %used_float_z", + }, + /* .checks = */ + { + "; CHECK: [[constx:%\\w+]] = OpConstant %float 3.1415", + "; CHECK: [[consty:%\\w+]] = OpConstant %float 4.25", + "; CHECK: [[constz:%\\w+]] = OpConstant %float 4.75", + "; CHECK: [[const:%\\w+]] = OpConstantComposite %v3float [[constx]] [[consty]]", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[const]]", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + StructTypeConstants, AggressiveEliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // A plain struct type dead constants. All of its components are dead + // constants too. + { + /* .used_consts = */ {}, + /* .main_insts = */ {}, + /* .dead_consts = */ + { + "%dead_bool = OpConstantTrue %bool", + "%dead_int = OpConstant %int 1", + "%dead_float = OpConstant %float 2.5", + "%dead_double = OpConstant %double 3.14159265358979", + "%dead_struct = OpConstantComposite %flat_struct %dead_bool %dead_int %dead_float %dead_double", + }, + /* .checks = */ + { + "; CHECK-NOT: OpConstant", + }, + }, + // A plain struct type dead constants. Some of its components are dead + // constants while others are not. + { + /* .used_consts = */ + { + "%used_int = OpConstant %int 1", + "%used_double = OpConstant %double 3.14159265358979", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Output", + "OpStore %int_var %used_int", + "%double_var = OpVariable %_pf_double Output", + "OpStore %double_var %used_double", + }, + /* .dead_consts = */ + { + "%dead_bool = OpConstantTrue %bool", + "%dead_float = OpConstant %float 2.5", + "%dead_struct = OpConstantComposite %flat_struct %dead_bool %used_int %dead_float %used_double", + }, + /* .checks = */ + { + "; CHECK: [[int:%\\w+]] = OpConstant %int 1", + "; CHECK: [[double:%\\w+]] = OpConstant %double 3.14159265358979", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[int]]", + "; CHECK: OpStore {{%\\w+}} [[double]]", + }, + }, + // A nesting struct type dead constants. All components of both outer + // and inner structs are dead and should be removed after dead constant + // elimination. + { + /* .used_consts = */ {}, + /* .main_insts = */ {}, + /* .dead_consts = */ + { + "%dead_bool = OpConstantTrue %bool", + "%dead_int = OpConstant %int 1", + "%dead_float = OpConstant %float 2.5", + "%dead_double = OpConstant %double 3.1415926535", + "%dead_inner_struct = OpConstantComposite %inner_struct %dead_bool %dead_int %dead_float %dead_double", + "%dead_int2 = OpConstant %int 2", + "%dead_double2 = OpConstant %double 1.428571428514", + "%dead_outer_struct = OpConstantComposite %outer_struct %dead_inner_struct %dead_int2 %dead_double2", + }, + /* .checks = */ + { + "; CHECK-NOT: OpConstant", + }, + }, + // A nesting struct type dead constants. Some of its components are + // dead constants while others are not. + { + /* .used_consts = */ + { + "%used_int = OpConstant %int 1", + "%used_double = OpConstant %double 3.14159265358979", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Output", + "OpStore %int_var %used_int", + "%double_var = OpVariable %_pf_double Output", + "OpStore %double_var %used_double", + }, + /* .dead_consts = */ + { + "%dead_bool = OpConstantTrue %bool", + "%dead_float = OpConstant %float 2.5", + "%dead_inner_struct = OpConstantComposite %inner_struct %dead_bool %used_int %dead_float %used_double", + "%dead_int = OpConstant %int 2", + "%dead_outer_struct = OpConstantComposite %outer_struct %dead_inner_struct %dead_int %used_double", + }, + /* .checks = */ + { + "; CHECK: [[int:%\\w+]] = OpConstant %int 1", + "; CHECK: [[double:%\\w+]] = OpConstant %double 3.14159265358979", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[int]]", + "; CHECK: OpStore {{%\\w+}} [[double]]", + }, + }, + // A nesting struct case. The inner struct is used while the outer struct is not + { + /* .used_const = */ + { + "%used_bool = OpConstantTrue %bool", + "%used_int = OpConstant %int 1", + "%used_float = OpConstant %float 1.23", + "%used_double = OpConstant %double 1.2345678901234", + "%used_inner_struct = OpConstantComposite %inner_struct %used_bool %used_int %used_float %used_double", + }, + /* .main_insts = */ + { + "%bool_var = OpVariable %_pf_bool Output", + "%bool_from_inner_struct = OpCompositeExtract %bool %used_inner_struct 0", + "OpStore %bool_var %bool_from_inner_struct", + }, + /* .dead_consts = */ + { + "%dead_int = OpConstant %int 2", + "%dead_outer_struct = OpConstantComposite %outer_struct %used_inner_struct %dead_int %used_double" + }, + /* .checks = */ + { + "; CHECK: [[bool:%\\w+]] = OpConstantTrue", + "; CHECK: [[int:%\\w+]] = OpConstant %int 1", + "; CHECK: [[float:%\\w+]] = OpConstant %float 1.23", + "; CHECK: [[double:%\\w+]] = OpConstant %double 1.2345678901234", + "; CHECK: [[struct:%\\w+]] = OpConstantComposite %inner_struct [[bool]] [[int]] [[float]] [[double]]", + "; CHECK-NOT: OpConstant", + "; CHECK: OpCompositeExtract %bool [[struct]]", + } + }, + // A nesting struct case. The outer struct is used, so the inner struct should not + // be removed even though it is not used anywhere. + { + /* .used_const = */ + { + "%used_bool = OpConstantTrue %bool", + "%used_int = OpConstant %int 1", + "%used_float = OpConstant %float 1.23", + "%used_double = OpConstant %double 1.2345678901234", + "%used_inner_struct = OpConstantComposite %inner_struct %used_bool %used_int %used_float %used_double", + "%used_outer_struct = OpConstantComposite %outer_struct %used_inner_struct %used_int %used_double" + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Output", + "%int_from_outer_struct = OpCompositeExtract %int %used_outer_struct 1", + "OpStore %int_var %int_from_outer_struct", + }, + /* .dead_consts = */ {}, + /* .checks = */ + { + "; CHECK: [[bool:%\\w+]] = OpConstantTrue %bool", + "; CHECK: [[int:%\\w+]] = OpConstant %int 1", + "; CHECK: [[float:%\\w+]] = OpConstant %float 1.23", + "; CHECK: [[double:%\\w+]] = OpConstant %double 1.2345678901234", + "; CHECK: [[inner_struct:%\\w+]] = OpConstantComposite %inner_struct %used_bool %used_int %used_float %used_double", + "; CHECK: [[outer_struct:%\\w+]] = OpConstantComposite %outer_struct %used_inner_struct %used_int %used_double", + "; CHECK: OpCompositeExtract %int [[outer_struct]]", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + ScalarTypeSpecConstants, AggressiveEliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // All scalar type spec constants. + { + /* .used_consts = */ + { + "%used_bool = OpSpecConstantTrue %bool", + "%used_uint = OpSpecConstant %uint 2", + "%used_int = OpSpecConstant %int 2", + "%used_float = OpSpecConstant %float 2.5", + "%used_double = OpSpecConstant %double 1.428571428514", + }, + /* .main_insts = */ + { + "%bool_var = OpVariable %_pf_bool Output", + "%uint_var = OpVariable %_pf_uint Output", + "%int_var = OpVariable %_pf_int Output", + "%float_var = OpVariable %_pf_float Output", + "%double_var = OpVariable %_pf_double Output", + "OpStore %bool_var %used_bool", + "OpStore %uint_var %used_uint", + "OpStore %int_var %used_int", + "OpStore %float_var %used_float", + "OpStore %double_var %used_double", + }, + /* .dead_consts = */ + { + "%dead_bool = OpSpecConstantTrue %bool", + "%dead_uint = OpSpecConstant %uint 2", + "%dead_int = OpSpecConstant %int 2", + "%dead_float = OpSpecConstant %float 2.5", + "%dead_double = OpSpecConstant %double 1.428571428514", + }, + /* .checks = */ + { + "; CHECK: [[bool:%\\w+]] = OpSpecConstantTrue %bool", + "; CHECK: [[uint:%\\w+]] = OpSpecConstant %uint 2", + "; CHECK: [[int:%\\w+]] = OpSpecConstant %int 2", + "; CHECK: [[float:%\\w+]] = OpSpecConstant %float 2.5", + "; CHECK: [[double:%\\w+]] = OpSpecConstant %double 1.428571428514", + "; CHECK-NOT: OpSpecConstant", + "; CHECK: OpStore {{%\\w+}} [[bool]]", + "; CHECK: OpStore {{%\\w+}} [[uint]]", + "; CHECK: OpStore {{%\\w+}} [[int]]", + "; CHECK: OpStore {{%\\w+}} [[float]]", + "; CHECK: OpStore {{%\\w+}} [[double]]", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + VectorTypeSpecConstants, AggressiveEliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Bool vector type spec constants. One vector has all component dead, + // another vector has one dead boolean and one used boolean. + { + /* .used_consts = */ + { + "%used_bool = OpSpecConstantTrue %bool", + }, + /* .main_insts = */ + { + "%bool_var = OpVariable %_pf_bool Output", + "OpStore %bool_var %used_bool", + }, + /* .dead_consts = */ + { + "%dead_bool = OpSpecConstantFalse %bool", + "%dead_bool_vec1 = OpSpecConstantComposite %v2bool %dead_bool %dead_bool", + "%dead_bool_vec2 = OpSpecConstantComposite %v2bool %dead_bool %used_bool", + }, + /* .checks = */ + { + "; CHECK: [[bool:%\\w+]] = OpSpecConstantTrue %bool", + "; CHECK-NOT: OpSpecConstant", + "; CHECK: OpStore {{%\\w+}} [[bool]]", + }, + }, + + // Uint vector type spec constants. One vector has all component dead, + // another vector has one dead unsigend integer and one used unsigned + // integer. + { + /* .used_consts = */ + { + "%used_uint = OpSpecConstant %uint 3", + }, + /* .main_insts = */ + { + "%uint_var = OpVariable %_pf_uint Output", + "OpStore %uint_var %used_uint", + }, + /* .dead_consts = */ + { + "%dead_uint = OpSpecConstant %uint 1", + "%dead_uint_vec1 = OpSpecConstantComposite %v2uint %dead_uint %dead_uint", + "%dead_uint_vec2 = OpSpecConstantComposite %v2uint %dead_uint %used_uint", + }, + /* .checks = */ + { + "; CHECK: [[uint:%\\w+]] = OpSpecConstant %uint 3", + "; CHECK-NOT: OpSpecConstant", + "; CHECK: OpStore {{%\\w+}} [[uint]]", + }, + }, + + // Int vector type spec constants. One vector has all component dead, + // another vector has one dead integer and one used integer. + { + /* .used_consts = */ + { + "%used_int = OpSpecConstant %int 3", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Output", + "OpStore %int_var %used_int", + }, + /* .dead_consts = */ + { + "%dead_int = OpSpecConstant %int 1", + "%dead_int_vec1 = OpSpecConstantComposite %v2int %dead_int %dead_int", + "%dead_int_vec2 = OpSpecConstantComposite %v2int %dead_int %used_int", + }, + /* .checks = */ + { + "; CHECK: [[int:%\\w+]] = OpSpecConstant %int 3", + "; CHECK-NOT: OpSpecConstant", + "; CHECK: OpStore {{%\\w+}} [[int]]", + }, + }, + + // Int vector type spec constants built with both spec constants and + // front-end constants. + { + /* .used_consts = */ + { + "%used_spec_int = OpSpecConstant %int 3", + "%used_front_end_int = OpConstant %int 3", + }, + /* .main_insts = */ + { + "%int_var1 = OpVariable %_pf_int Output", + "OpStore %int_var1 %used_spec_int", + "%int_var2 = OpVariable %_pf_int Output", + "OpStore %int_var2 %used_front_end_int", + }, + /* .dead_consts = */ + { + "%dead_spec_int = OpSpecConstant %int 1", + "%dead_front_end_int = OpConstant %int 1", + // Dead front-end and dead spec constants + "%dead_int_vec1 = OpSpecConstantComposite %v2int %dead_spec_int %dead_front_end_int", + // Used front-end and dead spec constants + "%dead_int_vec2 = OpSpecConstantComposite %v2int %dead_spec_int %used_front_end_int", + // Dead front-end and used spec constants + "%dead_int_vec3 = OpSpecConstantComposite %v2int %dead_front_end_int %used_spec_int", + }, + /* .checks = */ + { + "; CHECK: [[int1:%\\w+]] = OpSpecConstant %int 3", + "; CHECK: [[int2:%\\w+]] = OpConstant %int 3", + "; CHECK-NOT: OpSpecConstant", + "; CHECK-NOT: OpConstant", + "; CHECK: OpStore {{%\\w+}} [[int1]]", + "; CHECK: OpStore {{%\\w+}} [[int2]]", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + SpecConstantOp, AggressiveEliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Cast operations: uint <-> int <-> bool + { + /* .used_consts = */ {}, + /* .main_insts = */ {}, + /* .dead_consts = */ + { + // Assistant constants, only used in dead spec constant + // operations. + "%signed_zero = OpConstant %int 0", + "%signed_zero_vec = OpConstantComposite %v2int %signed_zero %signed_zero", + "%unsigned_zero = OpConstant %uint 0", + "%unsigned_zero_vec = OpConstantComposite %v2uint %unsigned_zero %unsigned_zero", + "%signed_one = OpConstant %int 1", + "%signed_one_vec = OpConstantComposite %v2int %signed_one %signed_one", + "%unsigned_one = OpConstant %uint 1", + "%unsigned_one_vec = OpConstantComposite %v2uint %unsigned_one %unsigned_one", + + // Spec constants that support casting to each other. + "%dead_bool = OpSpecConstantTrue %bool", + "%dead_uint = OpSpecConstant %uint 1", + "%dead_int = OpSpecConstant %int 2", + "%dead_bool_vec = OpSpecConstantComposite %v2bool %dead_bool %dead_bool", + "%dead_uint_vec = OpSpecConstantComposite %v2uint %dead_uint %dead_uint", + "%dead_int_vec = OpSpecConstantComposite %v2int %dead_int %dead_int", + + // Scalar cast to boolean spec constant. + "%int_to_bool = OpSpecConstantOp %bool INotEqual %dead_int %signed_zero", + "%uint_to_bool = OpSpecConstantOp %bool INotEqual %dead_uint %unsigned_zero", + + // Vector cast to boolean spec constant. + "%int_to_bool_vec = OpSpecConstantOp %v2bool INotEqual %dead_int_vec %signed_zero_vec", + "%uint_to_bool_vec = OpSpecConstantOp %v2bool INotEqual %dead_uint_vec %unsigned_zero_vec", + + // Scalar cast to int spec constant. + "%bool_to_int = OpSpecConstantOp %int Select %dead_bool %signed_one %signed_zero", + "%uint_to_int = OpSpecConstantOp %uint IAdd %dead_uint %unsigned_zero", + + // Vector cast to int spec constant. + "%bool_to_int_vec = OpSpecConstantOp %v2int Select %dead_bool_vec %signed_one_vec %signed_zero_vec", + "%uint_to_int_vec = OpSpecConstantOp %v2uint IAdd %dead_uint_vec %unsigned_zero_vec", + + // Scalar cast to uint spec constant. + "%bool_to_uint = OpSpecConstantOp %uint Select %dead_bool %unsigned_one %unsigned_zero", + "%int_to_uint_vec = OpSpecConstantOp %uint IAdd %dead_int %signed_zero", + + // Vector cast to uint spec constant. + "%bool_to_uint_vec = OpSpecConstantOp %v2uint Select %dead_bool_vec %unsigned_one_vec %unsigned_zero_vec", + "%int_to_uint = OpSpecConstantOp %v2uint IAdd %dead_int_vec %signed_zero_vec", + }, + /* .checks = */ + { + "; CHECK-NOT: OpConstant", + "; CHECK-NOT: OpSpecConstant", + }, + }, + + // Add, sub, mul, div, rem. + { + /* .used_consts = */ {}, + /* .main_insts = */ {}, + /* .dead_consts = */ + { + "%dead_spec_int_a = OpSpecConstant %int 1", + "%dead_spec_int_a_vec = OpSpecConstantComposite %v2int %dead_spec_int_a %dead_spec_int_a", + + "%dead_spec_int_b = OpSpecConstant %int 2", + "%dead_spec_int_b_vec = OpSpecConstantComposite %v2int %dead_spec_int_b %dead_spec_int_b", + + "%dead_const_int_c = OpConstant %int 3", + "%dead_const_int_c_vec = OpConstantComposite %v2int %dead_const_int_c %dead_const_int_c", + + // Add + "%add_a_b = OpSpecConstantOp %int IAdd %dead_spec_int_a %dead_spec_int_b", + "%add_a_b_vec = OpSpecConstantOp %v2int IAdd %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Sub + "%sub_a_b = OpSpecConstantOp %int ISub %dead_spec_int_a %dead_spec_int_b", + "%sub_a_b_vec = OpSpecConstantOp %v2int ISub %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Mul + "%mul_a_b = OpSpecConstantOp %int IMul %dead_spec_int_a %dead_spec_int_b", + "%mul_a_b_vec = OpSpecConstantOp %v2int IMul %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Div + "%div_a_b = OpSpecConstantOp %int SDiv %dead_spec_int_a %dead_spec_int_b", + "%div_a_b_vec = OpSpecConstantOp %v2int SDiv %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Bitwise Xor + "%xor_a_b = OpSpecConstantOp %int BitwiseXor %dead_spec_int_a %dead_spec_int_b", + "%xor_a_b_vec = OpSpecConstantOp %v2int BitwiseXor %dead_spec_int_a_vec %dead_spec_int_b_vec", + + // Scalar Comparison + "%less_a_b = OpSpecConstantOp %bool SLessThan %dead_spec_int_a %dead_spec_int_b", + }, + /* .checks = */ + { + "; CHECK-NOT: OpConstant", + "; CHECK-NOT: OpSpecConstant", + }, + }, + + // Vectors without used swizzles should be removed. + { + /* .used_consts = */ + { + "%used_int = OpConstant %int 3", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Output", + "OpStore %int_var %used_int", + }, + /* .dead_consts = */ + { + "%dead_int = OpConstant %int 3", + + "%dead_spec_int_a = OpSpecConstant %int 1", + "%vec_a = OpSpecConstantComposite %v4int %dead_spec_int_a %dead_spec_int_a %dead_int %dead_int", + + "%dead_spec_int_b = OpSpecConstant %int 2", + "%vec_b = OpSpecConstantComposite %v4int %dead_spec_int_b %dead_spec_int_b %used_int %used_int", + + // Extract scalar + "%a_x = OpSpecConstantOp %int CompositeExtract %vec_a 0", + "%b_x = OpSpecConstantOp %int CompositeExtract %vec_b 0", + + // Extract vector + "%a_xy = OpSpecConstantOp %v2int VectorShuffle %vec_a %vec_a 0 1", + "%b_xy = OpSpecConstantOp %v2int VectorShuffle %vec_b %vec_b 0 1", + }, + /* .checks = */ + { + "; CHECK: [[int:%\\w+]] = OpConstant %int 3", + "; CHECK-NOT: OpConstant", + "; CHECK-NOT: OpSpecConstant", + "; CHECK: OpStore {{%\\w+}} [[int]]", + }, + }, + // Vectors with used swizzles should not be removed. + { + /* .used_consts = */ + { + "%used_int = OpConstant %int 3", + "%used_spec_int_a = OpSpecConstant %int 1", + "%used_spec_int_b = OpSpecConstant %int 2", + // Create vectors + "%vec_a = OpSpecConstantComposite %v4int %used_spec_int_a %used_spec_int_a %used_int %used_int", + "%vec_b = OpSpecConstantComposite %v4int %used_spec_int_b %used_spec_int_b %used_int %used_int", + // Extract vector + "%a_xy = OpSpecConstantOp %v2int VectorShuffle %vec_a %vec_a 0 1", + "%b_xy = OpSpecConstantOp %v2int VectorShuffle %vec_b %vec_b 0 1", + }, + /* .main_insts = */ + { + "%v2int_var_a = OpVariable %_pf_v2int Output", + "%v2int_var_b = OpVariable %_pf_v2int Output", + "OpStore %v2int_var_a %a_xy", + "OpStore %v2int_var_b %b_xy", + }, + /* .dead_consts = */ {}, + /* .checks = */ + { + "; CHECK: [[int:%\\w+]] = OpConstant %int 3", + "; CHECK: [[a:%\\w+]] = OpSpecConstant %int 1", + "; CHECK: [[b:%\\w+]] = OpSpecConstant %int 2", + "; CHECK: [[veca:%\\w+]] = OpSpecConstantComposite %v4int [[a]] [[a]] [[int]] [[int]]", + "; CHECK: [[vecb:%\\w+]] = OpSpecConstantComposite %v4int [[b]] [[b]] [[int]] [[int]]", + "; CHECK: [[exa:%\\w+]] = OpSpecConstantOp %v2int VectorShuffle [[veca]] [[veca]] 0 1", + "; CHECK: [[exb:%\\w+]] = OpSpecConstantOp %v2int VectorShuffle [[vecb]] [[vecb]] 0 1", + "; CHECK-NOT: OpConstant", + "; CHECK-NOT: OpSpecConstant", + "; CHECK: OpStore {{%\\w+}} [[exa]]", + "; CHECK: OpStore {{%\\w+}} [[exb]]", + }, + }, + // clang-format on + }))); + +INSTANTIATE_TEST_SUITE_P( + LongDefUseChain, AggressiveEliminateDeadConstantTest, + ::testing::ValuesIn(std::vector({ + // clang-format off + // Long Def-Use chain with binary operations. + { + /* .used_consts = */ + { + "%array_size = OpConstant %int 4", + "%type_arr_int_4 = OpTypeArray %int %array_size", + "%used_int_0 = OpConstant %int 100", + "%used_int_1 = OpConstant %int 1", + "%used_int_2 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_1", + "%used_int_3 = OpSpecConstantOp %int ISub %used_int_0 %used_int_2", + "%used_int_4 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_3", + "%used_int_5 = OpSpecConstantOp %int ISub %used_int_0 %used_int_4", + "%used_int_6 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_5", + "%used_int_7 = OpSpecConstantOp %int ISub %used_int_0 %used_int_6", + "%used_int_8 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_7", + "%used_int_9 = OpSpecConstantOp %int ISub %used_int_0 %used_int_8", + "%used_int_10 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_9", + "%used_int_11 = OpSpecConstantOp %int ISub %used_int_0 %used_int_10", + "%used_int_12 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_11", + "%used_int_13 = OpSpecConstantOp %int ISub %used_int_0 %used_int_12", + "%used_int_14 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_13", + "%used_int_15 = OpSpecConstantOp %int ISub %used_int_0 %used_int_14", + "%used_int_16 = OpSpecConstantOp %int ISub %used_int_0 %used_int_15", + "%used_int_17 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_16", + "%used_int_18 = OpSpecConstantOp %int ISub %used_int_0 %used_int_17", + "%used_int_19 = OpSpecConstantOp %int IAdd %used_int_0 %used_int_18", + "%used_int_20 = OpSpecConstantOp %int ISub %used_int_0 %used_int_19", + "%used_vec_a = OpSpecConstantComposite %v2int %used_int_18 %used_int_19", + "%used_vec_b = OpSpecConstantOp %v2int IMul %used_vec_a %used_vec_a", + "%used_int_21 = OpSpecConstantOp %int CompositeExtract %used_vec_b 0", + "%used_array = OpConstantComposite %type_arr_int_4 %used_int_20 %used_int_20 %used_int_21 %used_int_21", + }, + /* .main_insts = */ + { + "%int_var = OpVariable %_pf_int Output", + "%used_array_2 = OpCompositeExtract %int %used_array 2", + "OpStore %int_var %used_array_2", + }, + /* .dead_consts = */ + { + "%dead_int_1 = OpConstant %int 2", + "%dead_int_2 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_1", + "%dead_int_3 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_2", + "%dead_int_4 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_3", + "%dead_int_5 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_4", + "%dead_int_6 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_5", + "%dead_int_7 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_6", + "%dead_int_8 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_7", + "%dead_int_9 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_8", + "%dead_int_10 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_9", + "%dead_int_11 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_10", + "%dead_int_12 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_11", + "%dead_int_13 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_12", + "%dead_int_14 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_13", + "%dead_int_15 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_14", + "%dead_int_16 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_15", + "%dead_int_17 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_16", + "%dead_int_18 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_17", + "%dead_int_19 = OpSpecConstantOp %int IAdd %used_int_0 %dead_int_18", + "%dead_int_20 = OpSpecConstantOp %int ISub %used_int_0 %dead_int_19", + "%dead_vec_a = OpSpecConstantComposite %v2int %dead_int_18 %dead_int_19", + "%dead_vec_b = OpSpecConstantOp %v2int IMul %dead_vec_a %dead_vec_a", + "%dead_int_21 = OpSpecConstantOp %int CompositeExtract %dead_vec_b 0", + "%dead_array = OpConstantComposite %type_arr_int_4 %dead_int_20 %used_int_20 %dead_int_19 %used_int_19", + }, + /* .checks = */ + { + "; CHECK: OpConstant %int 4", + "; CHECK: [[array:%\\w+]] = OpConstantComposite %type_arr_int_4 %used_int_20 %used_int_20 %used_int_21 %used_int_21", + "; CHECK-NOT: OpConstant", + "; CHECK-NOT: OpSpecConstant", + "; CHECK: OpStore {{%\\w+}} [[array]]", + }, + }, + // Long Def-Use chain with swizzle + // clang-format on + }))); + +TEST_F(AggressiveDCETest, DeadDecorationGroup) { + // The decoration group should be eliminated because the target of group + // decorate is dead. + const std::string text = R"( +; CHECK-NOT: OpDecorat +; CHECK-NOT: OpGroupDecorate +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %1 Restrict +OpDecorate %1 Aliased +%1 = OpDecorationGroup +OpGroupDecorate %1 %var +%void = OpTypeVoid +%func = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_ptr = OpTypePointer Function %uint +%main = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %uint_ptr Function +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, DeadDecorationGroupAndValidDecorationMgr) { + // The decoration group should be eliminated because the target of group + // decorate is dead. + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %1 Restrict +OpDecorate %1 Aliased +%1 = OpDecorationGroup +OpGroupDecorate %1 %var +%void = OpTypeVoid +%func = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_ptr = OpTypePointer Function %uint +%main = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %uint_ptr Function +OpReturn +OpFunctionEnd + )"; + + auto pass = MakeUnique(); + auto consumer = [](spv_message_level_t, const char*, const spv_position_t&, + const char* message) { + std::cerr << message << std::endl; + }; + auto context = BuildModule(SPV_ENV_UNIVERSAL_1_1, consumer, text); + + // Build the decoration manager before the pass. + context->get_decoration_mgr(); + + const auto status = pass->Run(context.get()); + EXPECT_EQ(status, Pass::Status::SuccessWithChange); +} + +TEST_F(AggressiveDCETest, ParitallyDeadDecorationGroup) { + const std::string text = R"( +; CHECK: OpDecorate [[grp:%\w+]] Restrict +; CHECK: [[grp]] = OpDecorationGroup +; CHECK: OpGroupDecorate [[grp]] [[output:%\w+]] +; CHECK: [[output]] = OpVariable {{%\w+}} Output +; CHECK-NOT: OpVariable {{%\w+}} Function +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %output +OpExecutionMode %main OriginUpperLeft +OpDecorate %1 Restrict +%1 = OpDecorationGroup +OpGroupDecorate %1 %var %output +%void = OpTypeVoid +%func = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_ptr_Function = OpTypePointer Function %uint +%uint_ptr_Output = OpTypePointer Output %uint +%uint_0 = OpConstant %uint 0 +%output = OpVariable %uint_ptr_Output Output +%main = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %uint_ptr_Function Function +OpStore %output %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, ParitallyDeadDecorationGroupDifferentGroupDecorate) { + const std::string text = R"( +; CHECK: OpDecorate [[grp:%\w+]] Restrict +; CHECK: [[grp]] = OpDecorationGroup +; CHECK: OpGroupDecorate [[grp]] [[output:%\w+]] +; CHECK-NOT: OpGroupDecorate +; CHECK: [[output]] = OpVariable {{%\w+}} Output +; CHECK-NOT: OpVariable {{%\w+}} Function +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %output +OpExecutionMode %main OriginUpperLeft +OpDecorate %1 Restrict +%1 = OpDecorationGroup +OpGroupDecorate %1 %output +OpGroupDecorate %1 %var +%void = OpTypeVoid +%func = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%uint_ptr_Function = OpTypePointer Function %uint +%uint_ptr_Output = OpTypePointer Output %uint +%uint_0 = OpConstant %uint 0 +%output = OpVariable %uint_ptr_Output Output +%main = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %uint_ptr_Function Function +OpStore %output %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, DeadGroupMemberDecorate) { + const std::string text = R"( +; CHECK-NOT: OpDec +; CHECK-NOT: OpGroup +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpDecorate %1 Offset 0 +OpDecorate %1 Uniform +%1 = OpDecorationGroup +OpGroupMemberDecorate %1 %var 0 +%void = OpTypeVoid +%func = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%struct = OpTypeStruct %uint %uint +%struct_ptr = OpTypePointer Function %struct +%main = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct_ptr Function +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, PartiallyDeadGroupMemberDecorate) { + const std::string text = R"( +; CHECK: OpDecorate [[grp:%\w+]] Offset 0 +; CHECK: OpDecorate [[grp]] RelaxedPrecision +; CHECK: [[grp]] = OpDecorationGroup +; CHECK: OpGroupMemberDecorate [[grp]] [[output:%\w+]] 1 +; CHECK: [[output]] = OpTypeStruct +; CHECK-NOT: OpTypeStruct +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %output +OpExecutionMode %main OriginUpperLeft +OpDecorate %1 Offset 0 +OpDecorate %1 RelaxedPrecision +%1 = OpDecorationGroup +OpGroupMemberDecorate %1 %var_struct 0 %output_struct 1 +%void = OpTypeVoid +%func = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%var_struct = OpTypeStruct %uint %uint +%output_struct = OpTypeStruct %uint %uint +%struct_ptr_Function = OpTypePointer Function %var_struct +%struct_ptr_Output = OpTypePointer Output %output_struct +%uint_ptr_Output = OpTypePointer Output %uint +%output = OpVariable %struct_ptr_Output Output +%uint_0 = OpConstant %uint 0 +%main = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct_ptr_Function Function +%3 = OpAccessChain %uint_ptr_Output %output %uint_0 +OpStore %3 %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, + PartiallyDeadGroupMemberDecorateDifferentGroupDecorate) { + const std::string text = R"( +; CHECK: OpDecorate [[grp:%\w+]] Offset 0 +; CHECK: OpDecorate [[grp]] RelaxedPrecision +; CHECK: [[grp]] = OpDecorationGroup +; CHECK: OpGroupMemberDecorate [[grp]] [[output:%\w+]] 1 +; CHECK-NOT: OpGroupMemberDecorate +; CHECK: [[output]] = OpTypeStruct +; CHECK-NOT: OpTypeStruct +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %output +OpExecutionMode %main OriginUpperLeft +OpDecorate %1 Offset 0 +OpDecorate %1 RelaxedPrecision +%1 = OpDecorationGroup +OpGroupMemberDecorate %1 %var_struct 0 +OpGroupMemberDecorate %1 %output_struct 1 +%void = OpTypeVoid +%func = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%var_struct = OpTypeStruct %uint %uint +%output_struct = OpTypeStruct %uint %uint +%struct_ptr_Function = OpTypePointer Function %var_struct +%struct_ptr_Output = OpTypePointer Output %output_struct +%uint_ptr_Output = OpTypePointer Output %uint +%output = OpVariable %struct_ptr_Output Output +%uint_0 = OpConstant %uint 0 +%main = OpFunction %void None %func +%2 = OpLabel +%var = OpVariable %struct_ptr_Function Function +%3 = OpAccessChain %uint_ptr_Output %output %uint_0 +OpStore %3 %uint_0 +OpReturn +OpFunctionEnd + )"; + + SinglePassRunAndMatch(text, true); +} + +// Test for #1404 +TEST_F(AggressiveDCETest, DontRemoveWorkgroupSize) { + const std::string text = R"( +; CHECK: OpDecorate [[wgs:%\w+]] BuiltIn WorkgroupSize +; CHECK: [[wgs]] = OpSpecConstantComposite +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %func "func" +OpExecutionMode %func LocalSize 1 1 1 +OpDecorate %1 BuiltIn WorkgroupSize +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%functy = OpTypeFunction %void +%v3int = OpTypeVector %int 3 +%2 = OpSpecConstant %int 1 +%1 = OpSpecConstantComposite %v3int %2 %2 %2 +%func = OpFunction %void None %functy +%3 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +// Test for #1214 +TEST_F(AggressiveDCETest, LoopHeaderIsAlsoAnotherLoopMerge) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "func" %2 +OpExecutionMode %1 OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%uint = OpTypeInt 32 0 +%_ptr_Output_uint = OpTypePointer Output %uint +%2 = OpVariable %_ptr_Output_uint Output +%uint_0 = OpConstant %uint 0 +%9 = OpTypeFunction %void +%1 = OpFunction %void None %9 +%10 = OpLabel +OpBranch %11 +%11 = OpLabel +OpLoopMerge %12 %13 None +OpBranchConditional %true %14 %13 +%14 = OpLabel +OpStore %2 %uint_0 +OpLoopMerge %15 %16 None +OpBranchConditional %true %15 %16 +%16 = OpLabel +OpBranch %14 +%15 = OpLabel +OpBranchConditional %true %12 %13 +%13 = OpLabel +OpBranch %11 +%12 = OpLabel +%17 = OpPhi %uint %uint_0 %15 %uint_0 %18 +OpStore %2 %17 +OpLoopMerge %19 %18 None +OpBranchConditional %true %19 %18 +%18 = OpLabel +OpBranch %12 +%19 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(AggressiveDCETest, BreaksDontVisitPhis) { + const std::string text = R"( +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %func "func" %var +OpExecutionMode %func OriginUpperLeft +%void = OpTypeVoid +%bool = OpTypeBool +%true = OpConstantTrue %bool +%int = OpTypeInt 32 0 +%int_ptr_Output = OpTypePointer Output %int +%var = OpVariable %int_ptr_Output Output +%int0 = OpConstant %int 0 +%functy = OpTypeFunction %void +%func = OpFunction %void None %functy +%entry = OpLabel +OpBranch %outer_header +%outer_header = OpLabel +OpLoopMerge %outer_merge %outer_continue None +OpBranchConditional %true %inner_header %outer_continue +%inner_header = OpLabel +%phi = OpPhi %int %int0 %outer_header %int0 %inner_continue +OpStore %var %phi +OpLoopMerge %inner_merge %inner_continue None +OpBranchConditional %true %inner_merge %inner_continue +%inner_continue = OpLabel +OpBranch %inner_header +%inner_merge = OpLabel +OpBranch %outer_continue +%outer_continue = OpLabel +%p = OpPhi %int %int0 %outer_header %int0 %inner_merge +OpStore %var %p +OpBranch %outer_header +%outer_merge = OpLabel +OpReturn +OpFunctionEnd +)"; + + EXPECT_EQ(Pass::Status::SuccessWithoutChange, + std::get<1>(SinglePassRunAndDisassemble( + text, false, true))); +} + +// Test for #1212 +TEST_F(AggressiveDCETest, ConstStoreInnerLoop) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "main" %2 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%_ptr_Output_float = OpTypePointer Output %float +%2 = OpVariable %_ptr_Output_float Output +%float_3 = OpConstant %float 3 +%1 = OpFunction %void None %4 +%13 = OpLabel +OpBranch %14 +%14 = OpLabel +OpLoopMerge %15 %16 None +OpBranchConditional %true %17 %15 +%17 = OpLabel +OpStore %2 %float_3 +OpLoopMerge %18 %17 None +OpBranchConditional %true %18 %17 +%18 = OpLabel +OpBranch %15 +%16 = OpLabel +OpBranch %14 +%15 = OpLabel +OpBranch %20 +%20 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, text, true, true); +} + +// Test for #1212 +TEST_F(AggressiveDCETest, InnerLoopCopy) { + const std::string text = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint Vertex %1 "main" %2 %3 +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%bool = OpTypeBool +%true = OpConstantTrue %bool +%_ptr_Output_float = OpTypePointer Output %float +%_ptr_Input_float = OpTypePointer Input %float +%2 = OpVariable %_ptr_Output_float Output +%3 = OpVariable %_ptr_Input_float Input +%1 = OpFunction %void None %5 +%14 = OpLabel +OpBranch %15 +%15 = OpLabel +OpLoopMerge %16 %17 None +OpBranchConditional %true %18 %16 +%18 = OpLabel +%19 = OpLoad %float %3 +OpStore %2 %19 +OpLoopMerge %20 %18 None +OpBranchConditional %true %20 %18 +%20 = OpLabel +OpBranch %16 +%17 = OpLabel +OpBranch %15 +%16 = OpLabel +OpBranch %22 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(AggressiveDCETest, AtomicAdd) { + const std::string text = R"(OpCapability SampledBuffer +OpCapability StorageImageExtendedFormats +OpCapability ImageBuffer +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "min" %gl_GlobalInvocationID +OpExecutionMode %2 LocalSize 64 1 1 +OpSource HLSL 600 +OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId +OpDecorate %4 DescriptorSet 4 +OpDecorate %4 Binding 70 +%uint = OpTypeInt 32 0 +%6 = OpTypeImage %uint Buffer 0 0 0 2 R32ui +%_ptr_UniformConstant_6 = OpTypePointer UniformConstant %6 +%_ptr_Private_6 = OpTypePointer Private %6 +%void = OpTypeVoid +%10 = OpTypeFunction %void +%uint_0 = OpConstant %uint 0 +%uint_1 = OpConstant %uint 1 +%v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Image_uint = OpTypePointer Image %uint +%4 = OpVariable %_ptr_UniformConstant_6 UniformConstant +%16 = OpVariable %_ptr_Private_6 Private +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%2 = OpFunction %void None %10 +%17 = OpLabel +%18 = OpLoad %6 %4 +OpStore %16 %18 +%19 = OpImageTexelPointer %_ptr_Image_uint %16 %uint_0 %uint_0 +%20 = OpAtomicIAdd %uint %19 %uint_1 %uint_0 %uint_1 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(text, text, true, true); +} + +TEST_F(AggressiveDCETest, SafelyRemoveDecorateString) { + const std::string preamble = R"(OpCapability Shader +OpExtension "SPV_GOOGLE_hlsl_functionality1" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %1 "main" +OpExecutionMode %1 OriginUpperLeft +)"; + + const std::string body_before = + R"(OpDecorateStringGOOGLE %2 HlslSemanticGOOGLE "FOOBAR" +%void = OpTypeVoid +%4 = OpTypeFunction %void +%uint = OpTypeInt 32 0 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%2 = OpVariable %_ptr_StorageBuffer_uint StorageBuffer +%1 = OpFunction %void None %4 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + const std::string body_after = R"(%void = OpTypeVoid +%4 = OpTypeFunction %void +%1 = OpFunction %void None %4 +%7 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(preamble + body_before, + preamble + body_after, true, true); +} + +TEST_F(AggressiveDCETest, CopyMemoryToGlobal) { + // |local| is loaded in an OpCopyMemory instruction. So the store must be + // kept alive. + const std::string test = + R"(OpCapability Geometry +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %global +OpExecutionMode %main Triangles +OpExecutionMode %main Invocations 1 +OpExecutionMode %main OutputTriangleStrip +OpExecutionMode %main OutputVertices 5 +OpSource GLSL 440 +OpName %main "main" +OpName %local "local" +OpName %global "global" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpConstantNull %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%global = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %7 +%19 = OpLabel +%local = OpVariable %_ptr_Function_v4float Function +OpStore %local %12 +OpCopyMemory %global %local +OpEndPrimitive +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, test, true, true); +} + +TEST_F(AggressiveDCETest, CopyMemoryToLocal) { + // Make sure the store to |local2| using OpCopyMemory is kept and keeps + // |local1| alive. + const std::string test = + R"(OpCapability Geometry +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %global +OpExecutionMode %main Triangles +OpExecutionMode %main Invocations 1 +OpExecutionMode %main OutputTriangleStrip +OpExecutionMode %main OutputVertices 5 +OpSource GLSL 440 +OpName %main "main" +OpName %local1 "local1" +OpName %local2 "local2" +OpName %global "global" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpConstantNull %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%global = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %7 +%19 = OpLabel +%local1 = OpVariable %_ptr_Function_v4float Function +%local2 = OpVariable %_ptr_Function_v4float Function +OpStore %local1 %12 +OpCopyMemory %local2 %local1 +OpCopyMemory %global %local2 +OpEndPrimitive +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, test, true, true); +} + +TEST_F(AggressiveDCETest, RemoveCopyMemoryToLocal) { + // Test that we remove function scope variables that are stored to using + // OpCopyMemory, but are never loaded. We can remove both |local1| and + // |local2|. + const std::string test = + R"(OpCapability Geometry +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %global +OpExecutionMode %main Triangles +OpExecutionMode %main Invocations 1 +OpExecutionMode %main OutputTriangleStrip +OpExecutionMode %main OutputVertices 5 +OpSource GLSL 440 +OpName %main "main" +OpName %local1 "local1" +OpName %local2 "local2" +OpName %global "global" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpConstantNull %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%global = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %7 +%19 = OpLabel +%local1 = OpVariable %_ptr_Function_v4float Function +%local2 = OpVariable %_ptr_Function_v4float Function +OpStore %local1 %12 +OpCopyMemory %local2 %local1 +OpEndPrimitive +OpReturn +OpFunctionEnd +)"; + + const std::string result = + R"(OpCapability Geometry +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %global +OpExecutionMode %main Triangles +OpExecutionMode %main Invocations 1 +OpExecutionMode %main OutputTriangleStrip +OpExecutionMode %main OutputVertices 5 +OpSource GLSL 440 +OpName %main "main" +OpName %global "global" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%global = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %7 +%19 = OpLabel +OpEndPrimitive +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, result, true, true); +} + +TEST_F(AggressiveDCETest, RemoveCopyMemoryToLocal2) { + // We are able to remove "local2" because it is not loaded, but have to keep + // the stores to "local1". + const std::string test = + R"(OpCapability Geometry +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %global +OpExecutionMode %main Triangles +OpExecutionMode %main Invocations 1 +OpExecutionMode %main OutputTriangleStrip +OpExecutionMode %main OutputVertices 5 +OpSource GLSL 440 +OpName %main "main" +OpName %local1 "local1" +OpName %local2 "local2" +OpName %global "global" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpConstantNull %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%global = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %7 +%19 = OpLabel +%local1 = OpVariable %_ptr_Function_v4float Function +%local2 = OpVariable %_ptr_Function_v4float Function +OpStore %local1 %12 +OpCopyMemory %local2 %local1 +OpCopyMemory %global %local1 +OpEndPrimitive +OpReturn +OpFunctionEnd +)"; + + const std::string result = + R"(OpCapability Geometry +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Geometry %main "main" %global +OpExecutionMode %main Triangles +OpExecutionMode %main Invocations 1 +OpExecutionMode %main OutputTriangleStrip +OpExecutionMode %main OutputVertices 5 +OpSource GLSL 440 +OpName %main "main" +OpName %local1 "local1" +OpName %global "global" +%void = OpTypeVoid +%7 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v4float = OpTypeVector %float 4 +%12 = OpConstantNull %v4float +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Output_v4float = OpTypePointer Output %v4float +%global = OpVariable %_ptr_Output_v4float Output +%main = OpFunction %void None %7 +%19 = OpLabel +%local1 = OpVariable %_ptr_Function_v4float Function +OpStore %local1 %12 +OpCopyMemory %global %local1 +OpEndPrimitive +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, result, true, true); +} + +TEST_F(AggressiveDCETest, StructuredIfWithConditionalExit) { + // We are able to remove "local2" because it is not loaded, but have to keep + // the stores to "local1". + const std::string test = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 140 +OpSourceExtension "GL_GOOGLE_cpp_style_line_directive" +OpSourceExtension "GL_GOOGLE_include_directive" +OpName %main "main" +OpName %a "a" +%void = OpTypeVoid +%5 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Uniform_int = OpTypePointer Uniform %int +%int_0 = OpConstant %int 0 +%bool = OpTypeBool +%int_100 = OpConstant %int 100 +%int_1 = OpConstant %int 1 +%a = OpVariable %_ptr_Uniform_int Uniform +%main = OpFunction %void None %5 +%12 = OpLabel +%13 = OpLoad %int %a +%14 = OpSGreaterThan %bool %13 %int_0 +OpSelectionMerge %15 None +OpBranchConditional %14 %16 %15 +%16 = OpLabel +%17 = OpLoad %int %a +%18 = OpSLessThan %bool %17 %int_100 +OpBranchConditional %18 %19 %15 +%19 = OpLabel +OpStore %a %int_1 +OpBranch %15 +%15 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, test, true, true); +} + +TEST_F(AggressiveDCETest, CountingLoopNotEliminated) { + // #version 310 es + // + // precision highp float; + // precision highp int; + // + // layout(location = 0) out vec4 _GLF_color; + // + // void main() + // { + // float data[1]; + // for (int c = 0; c < 1; c++) { + // if (true) { + // do { + // for (int i = 0; i < 1; i++) { + // data[i] = 1.0; + // } + // } while (false); + // } + // } + // _GLF_color = vec4(data[0], 0.0, 0.0, 1.0); + // } + const std::string test = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %_GLF_color +OpExecutionMode %main OriginUpperLeft +OpSource ESSL 310 +OpName %main "main" +OpName %c "c" +OpName %i "i" +OpName %data "data" +OpName %_GLF_color "_GLF_color" +OpDecorate %_GLF_color Location 0 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%bool = OpTypeBool +%float = OpTypeFloat 32 +%uint = OpTypeInt 32 0 +%uint_1 = OpConstant %uint 1 +%_arr_float_uint_1 = OpTypeArray %float %uint_1 +%_ptr_Function__arr_float_uint_1 = OpTypePointer Function %_arr_float_uint_1 +%float_1 = OpConstant %float 1 +%_ptr_Function_float = OpTypePointer Function %float +%false = OpConstantFalse %bool +%v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%_GLF_color = OpVariable %_ptr_Output_v4float Output +%float_0 = OpConstant %float 0 +%main = OpFunction %void None %8 +%26 = OpLabel +%c = OpVariable %_ptr_Function_int Function +%i = OpVariable %_ptr_Function_int Function +%data = OpVariable %_ptr_Function__arr_float_uint_1 Function +OpStore %c %int_0 +OpBranch %27 +%27 = OpLabel +OpLoopMerge %28 %29 None +OpBranch %30 +%30 = OpLabel +%31 = OpLoad %int %c +%32 = OpSLessThan %bool %31 %int_1 +OpBranchConditional %32 %33 %28 +%33 = OpLabel +OpBranch %34 +%34 = OpLabel +OpBranch %35 +%35 = OpLabel +OpLoopMerge %36 %37 None +OpBranch %38 +%38 = OpLabel +OpStore %i %int_0 +OpBranch %39 +%39 = OpLabel +OpLoopMerge %40 %41 None +OpBranch %42 +%42 = OpLabel +%43 = OpLoad %int %i +%44 = OpSLessThan %bool %43 %int_1 +OpBranchConditional %44 %46 %40 +%46 = OpLabel +%47 = OpLoad %int %i +%48 = OpAccessChain %_ptr_Function_float %data %47 +OpStore %48 %float_1 +OpBranch %41 +%41 = OpLabel +%49 = OpLoad %int %i +%50 = OpIAdd %int %49 %int_1 +OpStore %i %50 +OpBranch %39 +%40 = OpLabel +OpBranch %37 +%37 = OpLabel +OpBranchConditional %false %35 %36 +%36 = OpLabel +OpBranch %45 +%45 = OpLabel +OpBranch %29 +%29 = OpLabel +%51 = OpLoad %int %c +%52 = OpIAdd %int %51 %int_1 +OpStore %c %52 +OpBranch %27 +%28 = OpLabel +%53 = OpAccessChain %_ptr_Function_float %data %int_0 +%54 = OpLoad %float %53 +%55 = OpCompositeConstruct %v4float %54 %float_0 %float_0 %float_1 +OpStore %_GLF_color %55 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(test, test, true, true); +} + +TEST_F(AggressiveDCETest, EliminateLoopWithUnreachable) { + // #version 430 + // + // layout(std430) buffer U_t + // { + // float g_F[10]; + // float g_S; + // }; + // + // layout(location = 0)out float o; + // + // void main(void) + // { + // // Useless loop + // for (int i = 0; i<10; i++) { + // if (g_F[i] == 0.0) + // break; + // else + // break; + // // Unreachable merge block created here. + // // Need to edit SPIR-V to change to OpUnreachable + // } + // o = g_S; + // } + + const std::string before = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %i "i" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpMemberName %U_t 1 "g_S" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpMemberDecorate %U_t 1 Offset 40 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int +%int_0 = OpConstant %int 0 +%int_10 = OpConstant %int 10 +%bool = OpTypeBool +%float = OpTypeFloat 32 +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 %float +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%float_0 = OpConstant %float 0 +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %9 +%23 = OpLabel +%i = OpVariable %_ptr_Function_int Function +OpStore %i %int_0 +OpBranch %24 +%24 = OpLabel +OpLoopMerge %25 %26 None +OpBranch %27 +%27 = OpLabel +%28 = OpLoad %int %i +%29 = OpSLessThan %bool %28 %int_10 +OpBranchConditional %29 %30 %25 +%30 = OpLabel +%31 = OpLoad %int %i +%32 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %31 +%33 = OpLoad %float %32 +%34 = OpFOrdEqual %bool %33 %float_0 +OpSelectionMerge %35 None +OpBranchConditional %34 %36 %37 +%36 = OpLabel +OpBranch %25 +%37 = OpLabel +OpBranch %25 +%35 = OpLabel +OpUnreachable +%26 = OpLabel +%38 = OpLoad %int %i +%39 = OpIAdd %int %38 %int_1 +OpStore %i %39 +OpBranch %24 +%25 = OpLabel +%40 = OpAccessChain %_ptr_Uniform_float %_ %int_1 +%41 = OpLoad %float %40 +OpStore %o %41 +OpReturn +OpFunctionEnd +)"; + + const std::string after = + R"(OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" %o +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %U_t "U_t" +OpMemberName %U_t 0 "g_F" +OpMemberName %U_t 1 "g_S" +OpName %_ "" +OpName %o "o" +OpDecorate %_arr_float_uint_10 ArrayStride 4 +OpMemberDecorate %U_t 0 Offset 0 +OpMemberDecorate %U_t 1 Offset 40 +OpDecorate %U_t BufferBlock +OpDecorate %_ DescriptorSet 0 +OpDecorate %o Location 0 +%void = OpTypeVoid +%9 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%float = OpTypeFloat 32 +%uint = OpTypeInt 32 0 +%uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%U_t = OpTypeStruct %_arr_float_uint_10 %float +%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t +%_ = OpVariable %_ptr_Uniform_U_t Uniform +%_ptr_Uniform_float = OpTypePointer Uniform %float +%int_1 = OpConstant %int 1 +%_ptr_Output_float = OpTypePointer Output %float +%o = OpVariable %_ptr_Output_float Output +%main = OpFunction %void None %9 +%23 = OpLabel +OpBranch %24 +%24 = OpLabel +OpBranch %25 +%25 = OpLabel +%40 = OpAccessChain %_ptr_Uniform_float %_ %int_1 +%41 = OpLoad %float %40 +OpStore %o %41 +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, after, true, true); +} + +TEST_F(AggressiveDCETest, DeadHlslCounterBufferGOOGLE) { + // We are able to remove "local2" because it is not loaded, but have to keep + // the stores to "local1". + const std::string test = + R"( +; CHECK-NOT: OpDecorateId +; CHECK: [[var:%\w+]] = OpVariable +; CHECK-NOT: OpVariable +; CHECK: [[ac:%\w+]] = OpAccessChain {{%\w+}} [[var]] +; CHECK: OpStore [[ac]] + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %1 "main" + OpExecutionMode %1 LocalSize 32 1 1 + OpSource HLSL 600 + OpDecorate %_runtimearr_v2float ArrayStride 8 + OpMemberDecorate %_struct_3 0 Offset 0 + OpDecorate %_struct_3 BufferBlock + OpMemberDecorate %_struct_4 0 Offset 0 + OpDecorate %_struct_4 BufferBlock + OpDecorateId %5 HlslCounterBufferGOOGLE %6 + OpDecorate %5 DescriptorSet 0 + OpDecorate %5 Binding 0 + OpDecorate %6 DescriptorSet 0 + OpDecorate %6 Binding 1 + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_runtimearr_v2float = OpTypeRuntimeArray %v2float + %_struct_3 = OpTypeStruct %_runtimearr_v2float +%_ptr_Uniform__struct_3 = OpTypePointer Uniform %_struct_3 + %int = OpTypeInt 32 1 + %_struct_4 = OpTypeStruct %int +%_ptr_Uniform__struct_4 = OpTypePointer Uniform %_struct_4 + %void = OpTypeVoid + %13 = OpTypeFunction %void + %19 = OpConstantNull %v2float + %int_0 = OpConstant %int 0 +%_ptr_Uniform_v2float = OpTypePointer Uniform %v2float + %5 = OpVariable %_ptr_Uniform__struct_3 Uniform + %6 = OpVariable %_ptr_Uniform__struct_4 Uniform + %1 = OpFunction %void None %13 + %22 = OpLabel + %23 = OpAccessChain %_ptr_Uniform_v2float %5 %int_0 %int_0 + OpStore %23 %19 + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(test, true); +} + +TEST_F(AggressiveDCETest, Dead) { + // We are able to remove "local2" because it is not loaded, but have to keep + // the stores to "local1". + const std::string test = + R"( +; CHECK: OpCapability +; CHECK-NOT: OpMemberDecorateStringGOOGLE +; CHECK: OpFunctionEnd + OpCapability Shader + OpExtension "SPV_GOOGLE_hlsl_functionality1" + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %VSMain "VSMain" + OpSource HLSL 500 + OpName %VSMain "VSMain" + OpName %PSInput "PSInput" + OpMemberName %PSInput 0 "Pos" + OpMemberName %PSInput 1 "uv" + OpMemberDecorateStringGOOGLE %PSInput 0 HlslSemanticGOOGLE "SV_POSITION" + OpMemberDecorateStringGOOGLE %PSInput 1 HlslSemanticGOOGLE "TEX_COORD" + %void = OpTypeVoid + %5 = OpTypeFunction %void + %float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v4float = OpTypeVector %float 4 +%PSInput = OpTypeStruct %v4float %v2float + %VSMain = OpFunction %void None %5 + %9 = OpLabel + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(test, true); +} + +TEST_F(AggressiveDCETest, DeadInfiniteLoop) { + const std::string test = R"( +; CHECK: OpSwitch {{%\w+}} {{%\w+}} {{\w+}} {{%\w+}} {{\w+}} [[block:%\w+]] +; CHECK: [[block]] = OpLabel +; CHECK-NEXT: OpBranch [[block:%\w+]] +; CHECK: [[block]] = OpLabel +; CHECK-NEXT: OpBranch [[block:%\w+]] +; CHECK: [[block]] = OpLabel +; CHECK-NEXT: OpReturn + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %8 3 + %10 = OpTypeFunction %9 + %11 = OpConstant %8 1 + %12 = OpConstantComposite %9 %11 %11 %11 + %13 = OpTypeInt 32 1 + %32 = OpUndef %13 + %2 = OpFunction %6 None %7 + %33 = OpLabel + OpBranch %34 + %34 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %38 = OpFunctionCall %9 %39 + OpSelectionMerge %40 None + OpSwitch %32 %40 14 %41 58 %42 + %42 = OpLabel + OpBranch %43 + %43 = OpLabel + OpLoopMerge %44 %45 None + OpBranch %45 + %45 = OpLabel + OpBranch %43 + %44 = OpLabel + OpUnreachable + %41 = OpLabel + OpBranch %36 + %40 = OpLabel + OpBranch %36 + %36 = OpLabel + OpBranch %34 + %35 = OpLabel + OpReturn + OpFunctionEnd + %39 = OpFunction %9 None %10 + %46 = OpLabel + OpReturnValue %12 + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(test, true); +} + +TEST_F(AggressiveDCETest, DeadInfiniteLoopReturnValue) { + const std::string test = R"( +; CHECK: [[vec3:%\w+]] = OpTypeVector +; CHECK: [[undef:%\w+]] = OpUndef [[vec3]] +; CHECK: OpSwitch {{%\w+}} {{%\w+}} {{\w+}} {{%\w+}} {{\w+}} [[block:%\w+]] +; CHECK: [[block]] = OpLabel +; CHECK-NEXT: OpBranch [[block:%\w+]] +; CHECK: [[block]] = OpLabel +; CHECK-NEXT: OpBranch [[block:%\w+]] +; CHECK: [[block]] = OpLabel +; CHECK-NEXT: OpReturnValue [[undef]] + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "main" + OpExecutionMode %2 OriginUpperLeft + %6 = OpTypeVoid + %7 = OpTypeFunction %6 + %8 = OpTypeFloat 32 + %9 = OpTypeVector %8 3 + %10 = OpTypeFunction %9 + %11 = OpConstant %8 1 + %12 = OpConstantComposite %9 %11 %11 %11 + %13 = OpTypeInt 32 1 + %32 = OpUndef %13 + %2 = OpFunction %6 None %7 + %entry = OpLabel + %call = OpFunctionCall %9 %func + OpReturn + OpFunctionEnd + %func = OpFunction %9 None %10 + %33 = OpLabel + OpBranch %34 + %34 = OpLabel + OpLoopMerge %35 %36 None + OpBranch %37 + %37 = OpLabel + %38 = OpFunctionCall %9 %39 + OpSelectionMerge %40 None + OpSwitch %32 %40 14 %41 58 %42 + %42 = OpLabel + OpBranch %43 + %43 = OpLabel + OpLoopMerge %44 %45 None + OpBranch %45 + %45 = OpLabel + OpBranch %43 + %44 = OpLabel + OpUnreachable + %41 = OpLabel + OpBranch %36 + %40 = OpLabel + OpBranch %36 + %36 = OpLabel + OpBranch %34 + %35 = OpLabel + OpReturnValue %12 + OpFunctionEnd + %39 = OpFunction %9 None %10 + %46 = OpLabel + OpReturnValue %12 + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(test, true); +} + +TEST_F(AggressiveDCETest, TestVariablePointer) { + const std::string before = + R"(OpCapability Shader +OpCapability VariablePointers +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "main" +OpExecutionMode %2 LocalSize 1 1 1 +OpSource GLSL 450 +OpMemberDecorate %_struct_3 0 Offset 0 +OpDecorate %_struct_3 Block +OpDecorate %4 DescriptorSet 0 +OpDecorate %4 Binding 0 +OpDecorate %_ptr_StorageBuffer_int ArrayStride 4 +OpDecorate %_arr_int_int_128 ArrayStride 4 +%void = OpTypeVoid +%8 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%int_128 = OpConstant %int 128 +%_arr_int_int_128 = OpTypeArray %int %int_128 +%_struct_3 = OpTypeStruct %_arr_int_int_128 +%_ptr_StorageBuffer__struct_3 = OpTypePointer StorageBuffer %_struct_3 +%4 = OpVariable %_ptr_StorageBuffer__struct_3 StorageBuffer +%bool = OpTypeBool +%true = OpConstantTrue %bool +%int_0 = OpConstant %int 0 +%int_1 = OpConstant %int 1 +%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int +%2 = OpFunction %void None %8 +%16 = OpLabel +%17 = OpAccessChain %_ptr_StorageBuffer_int %4 %int_0 %int_0 +OpBranch %18 +%18 = OpLabel +%19 = OpPhi %_ptr_StorageBuffer_int %17 %16 %20 %21 +OpLoopMerge %22 %21 None +OpBranchConditional %true %23 %22 +%23 = OpLabel +OpStore %19 %int_0 +OpBranch %21 +%21 = OpLabel +%20 = OpPtrAccessChain %_ptr_StorageBuffer_int %19 %int_1 +OpBranch %18 +%22 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndCheck(before, before, true, true); +} + +TEST_F(AggressiveDCETest, DeadInputInterfaceV13) { + const std::string spirv = R"( +; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]] +; CHECK: [[var]] = OpVariable +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %dead +OpExecutionMode %main LocalSize 1 1 1 +OpName %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_input_int = OpTypePointer Input %int +%dead = OpVariable %ptr_input_int Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_3); + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(AggressiveDCETest, DeadInputInterfaceV14) { + const std::string spirv = R"( +; CHECK: OpEntryPoint GLCompute %main "main" [[var:%\w+]] +; CHECK: [[var]] = OpVariable +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %dead +OpExecutionMode %main LocalSize 1 1 1 +OpName %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_input_int = OpTypePointer Input %int +%dead = OpVariable %ptr_input_int Input +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(AggressiveDCETest, DeadInterfaceV14) { + const std::string spirv = R"( +; CHECK-NOT: OpEntryPoint GLCompute %main "main" % +; CHECK: OpEntryPoint GLCompute %main "main" +; CHECK-NOT: OpVariable +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %dead +OpExecutionMode %main LocalSize 1 1 1 +OpName %main "main" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%ptr_private_int = OpTypePointer Private %int +%dead = OpVariable %ptr_private_int Private +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(AggressiveDCETest, DeadInterfacesV14) { + const std::string spirv = R"( +; CHECK: OpEntryPoint GLCompute %main "main" %live1 %live2 +; CHECK-NOT: %dead +OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %main "main" %live1 %dead1 %dead2 %live2 +OpExecutionMode %main LocalSize 1 1 1 +OpName %main "main" +OpName %live1 "live1" +OpName %live2 "live2" +OpName %dead1 "dead1" +OpName %dead2 "dead2" +%void = OpTypeVoid +%int = OpTypeInt 32 0 +%int0 = OpConstant %int 0 +%ptr_ssbo_int = OpTypePointer StorageBuffer %int +%live1 = OpVariable %ptr_ssbo_int StorageBuffer +%live2 = OpVariable %ptr_ssbo_int StorageBuffer +%dead1 = OpVariable %ptr_ssbo_int StorageBuffer +%dead2 = OpVariable %ptr_ssbo_int StorageBuffer +%void_fn = OpTypeFunction %void +%main = OpFunction %void None %void_fn +%entry = OpLabel +OpStore %live1 %int0 +OpStore %live2 %int0 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(AggressiveDCETest, PreserveBindings) { + const std::string spirv = R"( +; CHECK: OpDecorate %unusedSampler DescriptorSet 0 +; CHECK: OpDecorate %unusedSampler Binding 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %unusedSampler "unusedSampler" +OpDecorate %unusedSampler DescriptorSet 0 +OpDecorate %unusedSampler Binding 0 +%void = OpTypeVoid +%5 = OpTypeFunction %void +%float = OpTypeFloat 32 +%7 = OpTypeImage %float 2D 0 0 0 1 Unknown +%8 = OpTypeSampledImage %7 +%_ptr_UniformConstant_8 = OpTypePointer UniformConstant %8 +%unusedSampler = OpVariable %_ptr_UniformConstant_8 UniformConstant +%main = OpFunction %void None %5 +%10 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + + OptimizerOptions()->preserve_bindings_ = true; + + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(AggressiveDCETest, PreserveSpecConstants) { + const std::string spirv = R"( +; CHECK: OpName %specConstant "specConstant" +; CHECK: %specConstant = OpSpecConstant %int 0 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %main "main" +OpExecutionMode %main OriginUpperLeft +OpSource GLSL 430 +OpName %main "main" +OpName %specConstant "specConstant" +OpDecorate %specConstant SpecId 0 +%void = OpTypeVoid +%3 = OpTypeFunction %void +%int = OpTypeInt 32 1 +%specConstant = OpSpecConstant %int 0 +%main = OpFunction %void None %3 +%5 = OpLabel +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + + OptimizerOptions()->preserve_spec_constants_ = true; + + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(AggressiveDCETest, LiveDecorateId) { + const std::string spirv = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" %2 +OpExecutionMode %1 LocalSize 8 1 1 +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +OpDecorateId %3 UniformId %uint_2 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%2 = OpVariable %_ptr_StorageBuffer_uint StorageBuffer +%8 = OpTypeFunction %void +%1 = OpFunction %void None %8 +%9 = OpLabel +%3 = OpLoad %uint %2 +OpStore %2 %3 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + OptimizerOptions()->preserve_spec_constants_ = true; + SinglePassRunAndCheck(spirv, spirv, true); +} + +TEST_F(AggressiveDCETest, LiveDecorateIdOnGroup) { + const std::string spirv = R"(OpCapability Shader +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %1 "main" %2 +OpExecutionMode %1 LocalSize 8 1 1 +OpDecorate %2 DescriptorSet 0 +OpDecorate %2 Binding 0 +OpDecorateId %3 UniformId %uint_2 +%3 = OpDecorationGroup +OpGroupDecorate %3 %5 +%void = OpTypeVoid +%uint = OpTypeInt 32 0 +%uint_2 = OpConstant %uint 2 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint +%2 = OpVariable %_ptr_StorageBuffer_uint StorageBuffer +%9 = OpTypeFunction %void +%1 = OpFunction %void None %9 +%10 = OpLabel +%5 = OpLoad %uint %2 +OpStore %2 %5 +OpReturn +OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + OptimizerOptions()->preserve_spec_constants_ = true; + SinglePassRunAndCheck(spirv, spirv, true); +} + +TEST_F(AggressiveDCETest, NoEliminateForwardPointer) { + // clang-format off + // + // #version 450 + // #extension GL_EXT_buffer_reference : enable + // + // // forward reference + // layout(buffer_reference) buffer blockType; + // + // layout(buffer_reference, std430, buffer_reference_align = 16) buffer blockType { + // int x; + // blockType next; + // }; + // + // layout(std430) buffer rootBlock { + // blockType root; + // } r; + // + // void main() + // { + // blockType b = r.root; + // b = b.next; + // b.x = 531; + // } + // + // clang-format on + + const std::string predefs1 = + R"(OpCapability Shader +OpCapability PhysicalStorageBufferAddresses +OpExtension "SPV_EXT_physical_storage_buffer" +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel PhysicalStorageBuffer64 GLSL450 +OpEntryPoint GLCompute %main "main" +OpExecutionMode %main LocalSize 1 1 1 +OpSource GLSL 450 +OpSourceExtension "GL_EXT_buffer_reference" +)"; + + const std::string names_before = + R"(OpName %main "main" +OpName %blockType "blockType" +OpMemberName %blockType 0 "x" +OpMemberName %blockType 1 "next" +OpName %b "b" +OpName %rootBlock "rootBlock" +OpMemberName %rootBlock 0 "root" +OpName %r "r" +OpMemberDecorate %blockType 0 Offset 0 +OpMemberDecorate %blockType 1 Offset 8 +OpDecorate %blockType Block +OpDecorate %b AliasedPointer +OpMemberDecorate %rootBlock 0 Offset 0 +OpDecorate %rootBlock Block +OpDecorate %r DescriptorSet 0 +OpDecorate %r Binding 0 +)"; + + const std::string names_after = + R"(OpName %main "main" +OpName %blockType "blockType" +OpMemberName %blockType 0 "x" +OpMemberName %blockType 1 "next" +OpName %rootBlock "rootBlock" +OpMemberName %rootBlock 0 "root" +OpName %r "r" +OpMemberDecorate %blockType 0 Offset 0 +OpMemberDecorate %blockType 1 Offset 8 +OpDecorate %blockType Block +OpMemberDecorate %rootBlock 0 Offset 0 +OpDecorate %rootBlock Block +OpDecorate %r DescriptorSet 0 +OpDecorate %r Binding 0 +)"; + + const std::string predefs2_before = + R"(%void = OpTypeVoid +%3 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer +%int = OpTypeInt 32 1 +%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType +%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType +%_ptr_Function__ptr_PhysicalStorageBuffer_blockType = OpTypePointer Function %_ptr_PhysicalStorageBuffer_blockType +%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType +%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock +%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType +%int_1 = OpConstant %int 1 +%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType +%int_531 = OpConstant %int 531 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +)"; + + const std::string predefs2_after = + R"(%void = OpTypeVoid +%8 = OpTypeFunction %void +OpTypeForwardPointer %_ptr_PhysicalStorageBuffer_blockType PhysicalStorageBuffer +%int = OpTypeInt 32 1 +%blockType = OpTypeStruct %int %_ptr_PhysicalStorageBuffer_blockType +%_ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %blockType +%rootBlock = OpTypeStruct %_ptr_PhysicalStorageBuffer_blockType +%_ptr_StorageBuffer_rootBlock = OpTypePointer StorageBuffer %rootBlock +%r = OpVariable %_ptr_StorageBuffer_rootBlock StorageBuffer +%int_0 = OpConstant %int 0 +%_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer StorageBuffer %_ptr_PhysicalStorageBuffer_blockType +%int_1 = OpConstant %int 1 +%_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType = OpTypePointer PhysicalStorageBuffer %_ptr_PhysicalStorageBuffer_blockType +%int_531 = OpConstant %int 531 +%_ptr_PhysicalStorageBuffer_int = OpTypePointer PhysicalStorageBuffer %int +)"; + + const std::string func_before = + R"(%main = OpFunction %void None %3 +%5 = OpLabel +%b = OpVariable %_ptr_Function__ptr_PhysicalStorageBuffer_blockType Function +%16 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0 +%17 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %16 +%21 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %17 %int_1 +%22 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %21 Aligned 8 +OpStore %b %22 +%26 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %22 %int_0 +OpStore %26 %int_531 Aligned 16 +OpReturn +OpFunctionEnd +)"; + + const std::string func_after = + R"(%main = OpFunction %void None %8 +%19 = OpLabel +%20 = OpAccessChain %_ptr_StorageBuffer__ptr_PhysicalStorageBuffer_blockType %r %int_0 +%21 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %20 +%22 = OpAccessChain %_ptr_PhysicalStorageBuffer__ptr_PhysicalStorageBuffer_blockType %21 %int_1 +%23 = OpLoad %_ptr_PhysicalStorageBuffer_blockType %22 Aligned 8 +%24 = OpAccessChain %_ptr_PhysicalStorageBuffer_int %23 %int_0 +OpStore %24 %int_531 Aligned 16 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck( + predefs1 + names_before + predefs2_before + func_before, + predefs1 + names_after + predefs2_after + func_after, true, true); +} + +TEST_F(AggressiveDCETest, MultipleFunctionProcessIndependently) { + const std::string spirv = R"( + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %entryHistogram "entryHistogram" %gl_GlobalInvocationID %gl_LocalInvocationIndex + OpEntryPoint GLCompute %entryAverage "entryAverage" %gl_GlobalInvocationID %gl_LocalInvocationIndex + OpExecutionMode %entryHistogram LocalSize 16 16 1 + OpExecutionMode %entryAverage LocalSize 256 1 1 + OpSource HLSL 640 + OpName %type_RWStructuredBuffer_uint "type.RWStructuredBuffer.uint" + OpName %uHistogram "uHistogram" + OpName %type_ACSBuffer_counter "type.ACSBuffer.counter" + OpMemberName %type_ACSBuffer_counter 0 "counter" + OpName %counter_var_uHistogram "counter.var.uHistogram" + OpName %sharedHistogram "sharedHistogram" + OpName %entryHistogram "entryHistogram" + OpName %param_var_id "param.var.id" + OpName %param_var_idx "param.var.idx" + OpName %entryAverage "entryAverage" + OpName %param_var_id_0 "param.var.id" + OpName %param_var_idx_0 "param.var.idx" + OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId + OpDecorate %gl_LocalInvocationIndex BuiltIn LocalInvocationIndex + OpDecorate %uHistogram DescriptorSet 0 + OpDecorate %uHistogram Binding 0 + OpDecorate %counter_var_uHistogram DescriptorSet 0 + OpDecorate %counter_var_uHistogram Binding 1 + OpDecorate %_runtimearr_uint ArrayStride 4 + OpMemberDecorate %type_RWStructuredBuffer_uint 0 Offset 0 + OpDecorate %type_RWStructuredBuffer_uint BufferBlock + OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0 + OpDecorate %type_ACSBuffer_counter BufferBlock + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_4 = OpConstant %uint 4 + %uint_8 = OpConstant %uint 8 + %uint_16 = OpConstant %uint 16 + %uint_32 = OpConstant %uint 32 + %uint_64 = OpConstant %uint 64 + %uint_128 = OpConstant %uint 128 + %uint_256 = OpConstant %uint 256 + %uint_512 = OpConstant %uint 512 + %uint_254 = OpConstant %uint 254 + %uint_255 = OpConstant %uint 255 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_runtimearr_uint = OpTypeRuntimeArray %uint +%type_RWStructuredBuffer_uint = OpTypeStruct %_runtimearr_uint +%_ptr_Uniform_type_RWStructuredBuffer_uint = OpTypePointer Uniform %type_RWStructuredBuffer_uint +%type_ACSBuffer_counter = OpTypeStruct %int +%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter +%_arr_uint_uint_256 = OpTypeArray %uint %uint_256 +%_ptr_Workgroup__arr_uint_uint_256 = OpTypePointer Workgroup %_arr_uint_uint_256 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint +%_ptr_Input_uint = OpTypePointer Input %uint + %void = OpTypeVoid + %49 = OpTypeFunction %void +%_ptr_Function_v3uint = OpTypePointer Function %v3uint +%_ptr_Function_uint = OpTypePointer Function %uint + %52 = OpTypeFunction %void %_ptr_Function_v3uint %_ptr_Function_uint +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint + %uint_264 = OpConstant %uint 264 + %bool = OpTypeBool +%_ptr_Uniform_uint = OpTypePointer Uniform %uint + %uHistogram = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_uint Uniform +%counter_var_uHistogram = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform +%sharedHistogram = OpVariable %_ptr_Workgroup__arr_uint_uint_256 Workgroup +%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input +%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input +%entryHistogram = OpFunction %void None %49 + %57 = OpLabel +%param_var_id = OpVariable %_ptr_Function_v3uint Function +%param_var_idx = OpVariable %_ptr_Function_uint Function + %58 = OpLoad %v3uint %gl_GlobalInvocationID + %59 = OpLoad %uint %gl_LocalInvocationIndex + %79 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %int_0 + %80 = OpAtomicIAdd %uint %79 %uint_1 %uint_0 %uint_1 + OpReturn + OpFunctionEnd +%entryAverage = OpFunction %void None %49 + %63 = OpLabel +%param_var_id_0 = OpVariable %_ptr_Function_v3uint Function +%param_var_idx_0 = OpVariable %_ptr_Function_uint Function + %64 = OpLoad %v3uint %gl_GlobalInvocationID + %65 = OpLoad %uint %gl_LocalInvocationIndex + OpStore %param_var_idx_0 %65 + %83 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65 + OpStore %83 %uint_0 + +; CHECK: [[ieq:%\w+]] = OpIEqual +; CHECK-NEXT: OpSelectionMerge [[merge:%\w+]] +; CHECK-NEXT: OpBranchConditional [[ieq]] [[not_elim:%\w+]] [[merge]] +; CHECK-NEXT: [[not_elim]] = OpLabel +; CHECK: [[merge]] = OpLabel + + OpControlBarrier %uint_2 %uint_2 %uint_264 + %85 = OpIEqual %bool %65 %uint_0 + OpSelectionMerge %89 None + OpBranchConditional %85 %86 %89 + %86 = OpLabel + %88 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65 + OpStore %88 %uint_1 + OpBranch %89 + %89 = OpLabel + OpControlBarrier %uint_2 %uint_2 %uint_264 + %91 = OpAccessChain %_ptr_Workgroup_uint %sharedHistogram %65 + %92 = OpLoad %uint %91 + %94 = OpAccessChain %_ptr_Uniform_uint %uHistogram %int_0 %65 + OpStore %94 %92 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_UNIVERSAL_1_3); + + SinglePassRunAndMatch(spirv, true); +} + +TEST_F(AggressiveDCETest, DebugInfoKeepInFunctionElimStoreVar) { + // Verify that dead local variable tc and store eliminated but all + // in-function debuginfo kept. + // + // The SPIR-V has been inlined and local single store eliminated + // + // Texture2D g_tColor; + // SamplerState g_sAniso; + // + // struct PS_INPUT { + // float2 vTextureCoords : TEXCOORD2; + // }; + // + // struct PS_OUTPUT { + // float4 vColor : SV_Target0; + // }; + // + // PS_OUTPUT MainPs(PS_INPUT i) { + // PS_OUTPUT ps_output; + // float2 tc = i.vTextureCoords.xy; + // ps_output.vColor = g_tColor.Sample(g_sAniso, tc); + // return ps_output; + // } + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %7 = OpString "foo.frag" + %8 = OpString "PS_OUTPUT" + %9 = OpString "float" + %10 = OpString "vColor" + %11 = OpString "PS_INPUT" + %12 = OpString "vTextureCoords" + %13 = OpString "@type.2d.image" + %14 = OpString "type.2d.image" + %15 = OpString "Texture2D.TemplateParam" + %16 = OpString "src.MainPs" + %17 = OpString "tc" + %18 = OpString "ps_output" + %19 = OpString "i" + %20 = OpString "@type.sampler" + %21 = OpString "type.sampler" + %22 = OpString "g_sAniso" + %23 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_64 = OpConstant %uint 64 + %45 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%_ptr_Function_v4float = OpTypePointer Function %v4float + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %51 = OpExtInst %void %1 DebugInfoNone + %52 = OpExtInst %void %1 DebugExpression + %53 = OpExtInst %void %1 DebugOperation Deref + %54 = OpExtInst %void %1 DebugExpression %53 + %55 = OpExtInst %void %1 DebugSource %7 + %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL + %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58 + %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float + %60 = OpExtInst %void %1 DebugTypeVector %59 4 + %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62 + %63 = OpExtInst %void %1 DebugTypeVector %59 2 + %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate + %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate + %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0 + %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65 + %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61 + %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51 + %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68 + %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal + %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal + %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1 + %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate + %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition + %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition + %MainPs = OpFunction %void None %45 + %76 = OpLabel + %107 = OpExtInst %void %1 DebugScope %69 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69 + %78 = OpVariable %_ptr_Function_PS_OUTPUT Function + %79 = OpVariable %_ptr_Function_v2float Function + %108 = OpExtInst %void %1 DebugNoScope +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope + %81 = OpVariable %_ptr_Function_PS_OUTPUT Function +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %82 = OpLoad %v2float %in_var_TEXCOORD2 + %83 = OpCompositeConstruct %PS_INPUT %82 + OpStore %param_var_i %83 + %109 = OpExtInst %void %1 DebugScope %68 + %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 + %110 = OpExtInst %void %1 DebugScope %69 + %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %68 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugScope %69 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugDeclare %71 %78 %52 + OpLine %7 19 17 + %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %89 = OpLoad %v2float %88 + OpLine %7 19 12 + OpStore %79 %89 +;CHECK-NOT: OpStore %79 %89 + OpLine %7 19 12 + %106 = OpExtInst %void %1 DebugValue %70 %89 %52 +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugValue %70 %89 %52 + OpLine %7 20 26 + %91 = OpLoad %type_2d_image %g_tColor + OpLine %7 20 46 + %92 = OpLoad %type_sampler %g_sAniso + OpLine %7 20 26 + %94 = OpSampledImage %type_sampled_image %91 %92 + %95 = OpImageSampleImplicitLod %v4float %94 %89 None + OpLine %7 20 5 + %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0 + OpStore %96 %95 + OpLine %7 21 12 + %97 = OpLoad %PS_OUTPUT %78 + OpLine %7 21 5 + OpStore %81 %97 + %111 = OpExtInst %void %1 DebugNoScope +;CHECK: {{%\w+}} = OpExtInst %void %1 DebugNoScope + %100 = OpCompositeExtract %v4float %97 0 + OpStore %out_var_SV_Target0 %100 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, DebugInfoDeclareKeepsStore) { + // Verify that local variable tc and its store are kept by DebugDeclare. + // + // Same shader source as DebugInfoInFunctionKeepStoreVarElim. The SPIR-V + // has just been inlined. + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %20 = OpString "foo.frag" + %24 = OpString "PS_OUTPUT" + %28 = OpString "float" + %31 = OpString "vColor" + %33 = OpString "PS_INPUT" + %38 = OpString "vTextureCoords" + %40 = OpString "@type.2d.image" + %41 = OpString "type.2d.image" + %43 = OpString "Texture2D.TemplateParam" + %47 = OpString "src.MainPs" + %51 = OpString "tc" + %53 = OpString "ps_output" + %56 = OpString "i" + %58 = OpString "@type.sampler" + %59 = OpString "type.sampler" + %61 = OpString "g_sAniso" + %63 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_64 = OpConstant %uint 64 + %65 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %75 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%_ptr_Function_v4float = OpTypePointer Function %v4float + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %39 = OpExtInst %void %1 DebugInfoNone + %55 = OpExtInst %void %1 DebugExpression + %22 = OpExtInst %void %1 DebugSource %20 + %23 = OpExtInst %void %1 DebugCompilationUnit 1 4 %22 HLSL + %26 = OpExtInst %void %1 DebugTypeComposite %24 Structure %22 10 1 %23 %24 %uint_128 FlagIsProtected|FlagIsPrivate %27 + %29 = OpExtInst %void %1 DebugTypeBasic %28 %uint_32 Float + %30 = OpExtInst %void %1 DebugTypeVector %29 4 + %27 = OpExtInst %void %1 DebugTypeMember %31 %30 %22 12 5 %26 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %35 = OpExtInst %void %1 DebugTypeComposite %33 Structure %22 5 1 %23 %33 %uint_64 FlagIsProtected|FlagIsPrivate %36 + %37 = OpExtInst %void %1 DebugTypeVector %29 2 + %36 = OpExtInst %void %1 DebugTypeMember %38 %37 %22 7 5 %35 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate + %42 = OpExtInst %void %1 DebugTypeComposite %40 Class %22 0 0 %23 %41 %39 FlagIsProtected|FlagIsPrivate + %44 = OpExtInst %void %1 DebugTypeTemplateParameter %43 %29 %39 %22 0 0 + %45 = OpExtInst %void %1 DebugTypeTemplate %42 %44 + %46 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %26 %35 + %48 = OpExtInst %void %1 DebugFunction %47 %46 %22 15 1 %23 %47 FlagIsProtected|FlagIsPrivate 16 %39 + %50 = OpExtInst %void %1 DebugLexicalBlock %22 16 1 %48 + %52 = OpExtInst %void %1 DebugLocalVariable %51 %37 %22 19 12 %50 FlagIsLocal + %54 = OpExtInst %void %1 DebugLocalVariable %53 %26 %22 17 15 %50 FlagIsLocal + %57 = OpExtInst %void %1 DebugLocalVariable %56 %35 %22 15 29 %48 FlagIsLocal 1 + %60 = OpExtInst %void %1 DebugTypeComposite %58 Structure %22 0 0 %23 %59 %39 FlagIsProtected|FlagIsPrivate + %62 = OpExtInst %void %1 DebugGlobalVariable %61 %60 %22 3 14 %23 %61 %g_sAniso FlagIsDefinition + %64 = OpExtInst %void %1 DebugGlobalVariable %63 %42 %22 1 11 %23 %63 %g_tColor FlagIsDefinition + %MainPs = OpFunction %void None %65 + %66 = OpLabel + %114 = OpExtInst %void %1 DebugScope %50 + %98 = OpVariable %_ptr_Function_PS_OUTPUT Function + %99 = OpVariable %_ptr_Function_v2float Function + %115 = OpExtInst %void %1 DebugNoScope + %100 = OpVariable %_ptr_Function_PS_OUTPUT Function +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %70 = OpLoad %v2float %in_var_TEXCOORD2 + %71 = OpCompositeConstruct %PS_INPUT %70 + OpStore %param_var_i %71 + %116 = OpExtInst %void %1 DebugScope %48 + %102 = OpExtInst %void %1 DebugDeclare %57 %param_var_i %55 + %117 = OpExtInst %void %1 DebugScope %50 + %103 = OpExtInst %void %1 DebugDeclare %54 %98 %55 + OpLine %20 19 17 + %104 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %105 = OpLoad %v2float %104 + OpLine %20 19 12 + OpStore %99 %105 +;CHECK: OpStore %99 %105 + %106 = OpExtInst %void %1 DebugDeclare %52 %99 %55 + OpLine %20 20 26 + %107 = OpLoad %type_2d_image %g_tColor + OpLine %20 20 46 + %108 = OpLoad %type_sampler %g_sAniso + OpLine %20 20 26 + %110 = OpSampledImage %type_sampled_image %107 %108 + %111 = OpImageSampleImplicitLod %v4float %110 %105 None + OpLine %20 20 5 + %112 = OpAccessChain %_ptr_Function_v4float %98 %int_0 + OpStore %112 %111 + OpLine %20 21 12 + %113 = OpLoad %PS_OUTPUT %98 + OpLine %20 21 5 + OpStore %100 %113 + %118 = OpExtInst %void %1 DebugNoScope + %73 = OpLoad %PS_OUTPUT %100 + %74 = OpCompositeExtract %v4float %73 0 + OpStore %out_var_SV_Target0 %74 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, DebugInfoValueDerefKeepsStore) { + // Verify that local variable tc and its store are kept by DebugValue with + // Deref. + // + // Same shader source as DebugInfoInFunctionKeepStoreVarElim. The SPIR-V + // has just been inlined and edited to replace the DebugDeclare with the + // DebugValue/Deref. + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %7 = OpString "foo.frag" + %8 = OpString "PS_OUTPUT" + %9 = OpString "float" + %10 = OpString "vColor" + %11 = OpString "PS_INPUT" + %12 = OpString "vTextureCoords" + %13 = OpString "@type.2d.image" + %14 = OpString "type.2d.image" + %15 = OpString "Texture2D.TemplateParam" + %16 = OpString "src.MainPs" + %17 = OpString "tc" + %18 = OpString "ps_output" + %19 = OpString "i" + %20 = OpString "@type.sampler" + %21 = OpString "type.sampler" + %22 = OpString "g_sAniso" + %23 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 1 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_64 = OpConstant %uint 64 + %45 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %47 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%_ptr_Function_v4float = OpTypePointer Function %v4float + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %51 = OpExtInst %void %1 DebugInfoNone + %52 = OpExtInst %void %1 DebugExpression + %53 = OpExtInst %void %1 DebugOperation Deref + %54 = OpExtInst %void %1 DebugExpression %53 + %55 = OpExtInst %void %1 DebugSource %7 + %56 = OpExtInst %void %1 DebugCompilationUnit 1 4 %55 HLSL + %57 = OpExtInst %void %1 DebugTypeComposite %8 Structure %55 10 1 %56 %8 %uint_128 FlagIsProtected|FlagIsPrivate %58 + %59 = OpExtInst %void %1 DebugTypeBasic %9 %uint_32 Float + %60 = OpExtInst %void %1 DebugTypeVector %59 4 + %58 = OpExtInst %void %1 DebugTypeMember %10 %60 %55 12 5 %57 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %61 = OpExtInst %void %1 DebugTypeComposite %11 Structure %55 5 1 %56 %11 %uint_64 FlagIsProtected|FlagIsPrivate %62 + %63 = OpExtInst %void %1 DebugTypeVector %59 2 + %62 = OpExtInst %void %1 DebugTypeMember %12 %63 %55 7 5 %61 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate + %64 = OpExtInst %void %1 DebugTypeComposite %13 Class %55 0 0 %56 %14 %51 FlagIsProtected|FlagIsPrivate + %65 = OpExtInst %void %1 DebugTypeTemplateParameter %15 %59 %51 %55 0 0 + %66 = OpExtInst %void %1 DebugTypeTemplate %64 %65 + %67 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %57 %61 + %68 = OpExtInst %void %1 DebugFunction %16 %67 %55 15 1 %56 %16 FlagIsProtected|FlagIsPrivate 16 %51 + %69 = OpExtInst %void %1 DebugLexicalBlock %55 16 1 %68 + %70 = OpExtInst %void %1 DebugLocalVariable %17 %63 %55 19 12 %69 FlagIsLocal + %71 = OpExtInst %void %1 DebugLocalVariable %18 %57 %55 17 15 %69 FlagIsLocal + %72 = OpExtInst %void %1 DebugLocalVariable %19 %61 %55 15 29 %68 FlagIsLocal 1 + %73 = OpExtInst %void %1 DebugTypeComposite %20 Structure %55 0 0 %56 %21 %51 FlagIsProtected|FlagIsPrivate + %74 = OpExtInst %void %1 DebugGlobalVariable %22 %73 %55 3 14 %56 %22 %g_sAniso FlagIsDefinition + %75 = OpExtInst %void %1 DebugGlobalVariable %23 %64 %55 1 11 %56 %23 %g_tColor FlagIsDefinition + %MainPs = OpFunction %void None %45 + %76 = OpLabel + %101 = OpExtInst %void %1 DebugScope %69 + %78 = OpVariable %_ptr_Function_PS_OUTPUT Function + %79 = OpVariable %_ptr_Function_v2float Function + %102 = OpExtInst %void %1 DebugNoScope + %81 = OpVariable %_ptr_Function_PS_OUTPUT Function +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %82 = OpLoad %v2float %in_var_TEXCOORD2 + %83 = OpCompositeConstruct %PS_INPUT %82 + OpStore %param_var_i %83 + %103 = OpExtInst %void %1 DebugScope %68 + %85 = OpExtInst %void %1 DebugDeclare %72 %param_var_i %52 + %104 = OpExtInst %void %1 DebugScope %69 + %87 = OpExtInst %void %1 DebugDeclare %71 %78 %52 + OpLine %7 19 17 + %88 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %89 = OpLoad %v2float %88 + OpLine %7 19 12 + OpStore %79 %89 +;CHECK: OpStore %79 %89 + %90 = OpExtInst %void %1 DebugValue %70 %79 %54 + OpLine %7 20 26 + %91 = OpLoad %type_2d_image %g_tColor + OpLine %7 20 46 + %92 = OpLoad %type_sampler %g_sAniso + OpLine %7 20 26 + %94 = OpSampledImage %type_sampled_image %91 %92 + %95 = OpImageSampleImplicitLod %v4float %94 %89 None + OpLine %7 20 5 + %96 = OpAccessChain %_ptr_Function_v4float %78 %int_0 + OpStore %96 %95 + OpLine %7 21 12 + %97 = OpLoad %PS_OUTPUT %78 + OpLine %7 21 5 + OpStore %81 %97 + %105 = OpExtInst %void %1 DebugNoScope + %99 = OpLoad %PS_OUTPUT %81 + %100 = OpCompositeExtract %v4float %99 0 + OpStore %out_var_SV_Target0 %100 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, DebugInfoElimUnusedTextureKeepGlobalVariable) { + // Verify that unused texture g_tColor2 is eliminated but its + // DebugGlobalVariable is retained but with DebugInfoNone for its Variable. + // + // Same shader source as DebugInfoInFunctionKeepStoreVarElim but with unused + // g_tColor2 added. + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %MainPs "MainPs" %g_tColor %g_tColor2 %g_sAniso %in_var_TEXCOORD2 %out_var_SV_Target0 + OpExecutionMode %MainPs OriginUpperLeft + %21 = OpString "foo6.frag" + %25 = OpString "PS_OUTPUT" + %29 = OpString "float" + %32 = OpString "vColor" + %34 = OpString "PS_INPUT" + %39 = OpString "vTextureCoords" + %41 = OpString "@type.2d.image" + %42 = OpString "type.2d.image" + %44 = OpString "Texture2D.TemplateParam" + %48 = OpString "src.MainPs" + %52 = OpString "tc" + %54 = OpString "ps_output" + %57 = OpString "i" + %59 = OpString "@type.sampler" + %60 = OpString "type.sampler" + %62 = OpString "g_sAniso" + %64 = OpString "g_tColor2" + %66 = OpString "g_tColor" + OpName %type_2d_image "type.2d.image" + OpName %g_tColor "g_tColor" + OpName %g_tColor2 "g_tColor2" + OpName %type_sampler "type.sampler" + OpName %g_sAniso "g_sAniso" + OpName %in_var_TEXCOORD2 "in.var.TEXCOORD2" + OpName %out_var_SV_Target0 "out.var.SV_Target0" + OpName %MainPs "MainPs" + OpName %PS_INPUT "PS_INPUT" + OpMemberName %PS_INPUT 0 "vTextureCoords" + OpName %param_var_i "param.var.i" + OpName %PS_OUTPUT "PS_OUTPUT" + OpMemberName %PS_OUTPUT 0 "vColor" + OpName %type_sampled_image "type.sampled.image" + OpDecorate %in_var_TEXCOORD2 Location 0 + OpDecorate %out_var_SV_Target0 Location 0 + OpDecorate %g_tColor DescriptorSet 0 + OpDecorate %g_tColor Binding 0 + OpDecorate %g_tColor2 DescriptorSet 0 + OpDecorate %g_tColor2 Binding 1 + OpDecorate %g_sAniso DescriptorSet 0 + OpDecorate %g_sAniso Binding 2 + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 + %float = OpTypeFloat 32 +%type_2d_image = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_type_2d_image = OpTypePointer UniformConstant %type_2d_image +%type_sampler = OpTypeSampler +%_ptr_UniformConstant_type_sampler = OpTypePointer UniformConstant %type_sampler + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %uint_128 = OpConstant %uint 128 + %uint_0 = OpConstant %uint 0 + %uint_64 = OpConstant %uint 64 + %68 = OpTypeFunction %void + %PS_INPUT = OpTypeStruct %v2float +%_ptr_Function_PS_INPUT = OpTypePointer Function %PS_INPUT + %PS_OUTPUT = OpTypeStruct %v4float + %78 = OpTypeFunction %PS_OUTPUT %_ptr_Function_PS_INPUT +%_ptr_Function_PS_OUTPUT = OpTypePointer Function %PS_OUTPUT +%_ptr_Function_v2float = OpTypePointer Function %v2float +%type_sampled_image = OpTypeSampledImage %type_2d_image +%_ptr_Function_v4float = OpTypePointer Function %v4float + %g_tColor = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_tColor2 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant +;CHECK-NOT: %g_tColor2 = OpVariable %_ptr_UniformConstant_type_2d_image UniformConstant + %g_sAniso = OpVariable %_ptr_UniformConstant_type_sampler UniformConstant +%in_var_TEXCOORD2 = OpVariable %_ptr_Input_v2float Input +%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output + %40 = OpExtInst %void %1 DebugInfoNone + %56 = OpExtInst %void %1 DebugExpression + %23 = OpExtInst %void %1 DebugSource %21 + %24 = OpExtInst %void %1 DebugCompilationUnit 1 4 %23 HLSL + %27 = OpExtInst %void %1 DebugTypeComposite %25 Structure %23 11 1 %24 %25 %uint_128 FlagIsProtected|FlagIsPrivate %28 + %30 = OpExtInst %void %1 DebugTypeBasic %29 %uint_32 Float + %31 = OpExtInst %void %1 DebugTypeVector %30 4 + %28 = OpExtInst %void %1 DebugTypeMember %32 %31 %23 13 5 %27 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate + %36 = OpExtInst %void %1 DebugTypeComposite %34 Structure %23 6 1 %24 %34 %uint_64 FlagIsProtected|FlagIsPrivate %37 + %38 = OpExtInst %void %1 DebugTypeVector %30 2 + %37 = OpExtInst %void %1 DebugTypeMember %39 %38 %23 8 5 %36 %uint_0 %uint_64 FlagIsProtected|FlagIsPrivate + %43 = OpExtInst %void %1 DebugTypeComposite %41 Class %23 0 0 %24 %42 %40 FlagIsProtected|FlagIsPrivate + %45 = OpExtInst %void %1 DebugTypeTemplateParameter %44 %30 %40 %23 0 0 + %46 = OpExtInst %void %1 DebugTypeTemplate %43 %45 + %47 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %27 %36 + %49 = OpExtInst %void %1 DebugFunction %48 %47 %23 16 1 %24 %48 FlagIsProtected|FlagIsPrivate 17 %40 + %51 = OpExtInst %void %1 DebugLexicalBlock %23 17 1 %49 + %53 = OpExtInst %void %1 DebugLocalVariable %52 %38 %23 20 12 %51 FlagIsLocal + %55 = OpExtInst %void %1 DebugLocalVariable %54 %27 %23 18 15 %51 FlagIsLocal + %58 = OpExtInst %void %1 DebugLocalVariable %57 %36 %23 16 29 %49 FlagIsLocal 1 + %61 = OpExtInst %void %1 DebugTypeComposite %59 Structure %23 0 0 %24 %60 %40 FlagIsProtected|FlagIsPrivate + %63 = OpExtInst %void %1 DebugGlobalVariable %62 %61 %23 4 14 %24 %62 %g_sAniso FlagIsDefinition + %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %g_tColor2 FlagIsDefinition +;CHECK-NOT: %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %g_tColor2 FlagIsDefinition +;CHECK: %65 = OpExtInst %void %1 DebugGlobalVariable %64 %43 %23 2 11 %24 %64 %40 FlagIsDefinition + %67 = OpExtInst %void %1 DebugGlobalVariable %66 %43 %23 1 11 %24 %66 %g_tColor FlagIsDefinition + %MainPs = OpFunction %void None %68 + %69 = OpLabel + %117 = OpExtInst %void %1 DebugScope %51 + %101 = OpVariable %_ptr_Function_PS_OUTPUT Function + %102 = OpVariable %_ptr_Function_v2float Function + %118 = OpExtInst %void %1 DebugNoScope + %103 = OpVariable %_ptr_Function_PS_OUTPUT Function +%param_var_i = OpVariable %_ptr_Function_PS_INPUT Function + %73 = OpLoad %v2float %in_var_TEXCOORD2 + %74 = OpCompositeConstruct %PS_INPUT %73 + OpStore %param_var_i %74 + %119 = OpExtInst %void %1 DebugScope %49 + %105 = OpExtInst %void %1 DebugDeclare %58 %param_var_i %56 + %120 = OpExtInst %void %1 DebugScope %51 + %106 = OpExtInst %void %1 DebugDeclare %55 %101 %56 + OpLine %21 20 17 + %107 = OpAccessChain %_ptr_Function_v2float %param_var_i %int_0 + %108 = OpLoad %v2float %107 + OpLine %21 20 12 + OpStore %102 %108 + %109 = OpExtInst %void %1 DebugDeclare %53 %102 %56 + OpLine %21 21 26 + %110 = OpLoad %type_2d_image %g_tColor + OpLine %21 21 46 + %111 = OpLoad %type_sampler %g_sAniso + OpLine %21 21 57 + %112 = OpLoad %v2float %102 + OpLine %21 21 26 + %113 = OpSampledImage %type_sampled_image %110 %111 + %114 = OpImageSampleImplicitLod %v4float %113 %112 None + OpLine %21 21 5 + %115 = OpAccessChain %_ptr_Function_v4float %101 %int_0 + OpStore %115 %114 + OpLine %21 22 12 + %116 = OpLoad %PS_OUTPUT %101 + OpLine %21 22 5 + OpStore %103 %116 + %121 = OpExtInst %void %1 DebugNoScope + %76 = OpLoad %PS_OUTPUT %103 + %77 = OpCompositeExtract %v4float %76 0 + OpStore %out_var_SV_Target0 %77 + OpReturn + OpFunctionEnd +)"; + + SetTargetEnv(SPV_ENV_VULKAN_1_2); + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +TEST_F(AggressiveDCETest, KeepDebugScopeParent) { + // Verify that local variable tc and its store are kept by DebugDeclare. + // + // Same shader source as DebugInfoInFunctionKeepStoreVarElim. The SPIR-V + // has just been inlined. + + const std::string text = R"( + OpCapability Shader + %1 = OpExtInstImport "OpenCL.DebugInfo.100" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %out_var_SV_TARGET0 + OpExecutionMode %main OriginUpperLeft + %11 = OpString "float" + %16 = OpString "t.hlsl" + %19 = OpString "src.main" + OpName %out_var_SV_TARGET0 "out.var.SV_TARGET0" + OpName %main "main" + OpDecorate %out_var_SV_TARGET0 Location 0 + %float = OpTypeFloat 32 + %float_0 = OpConstant %float 0 + %v4float = OpTypeVector %float 4 + %7 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 + %uint = OpTypeInt 32 0 + %uint_32 = OpConstant %uint 32 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %void = OpTypeVoid + %23 = OpTypeFunction %void + %26 = OpTypeFunction %v4float +%out_var_SV_TARGET0 = OpVariable %_ptr_Output_v4float Output +%_ptr_Function_v4float = OpTypePointer Function %v4float + %33 = OpExtInst %void %1 DebugInfoNone + %13 = OpExtInst %void %1 DebugTypeBasic %11 %uint_32 Float + %14 = OpExtInst %void %1 DebugTypeVector %13 4 + %15 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %14 + %17 = OpExtInst %void %1 DebugSource %16 + %18 = OpExtInst %void %1 DebugCompilationUnit 1 4 %17 HLSL + %20 = OpExtInst %void %1 DebugFunction %19 %15 %17 1 1 %18 %19 FlagIsProtected|FlagIsPrivate 2 %33 + %22 = OpExtInst %void %1 DebugLexicalBlock %17 2 1 %20 + %main = OpFunction %void None %23 + %24 = OpLabel + %31 = OpVariable %_ptr_Function_v4float Function +; CHECK: [[block:%\w+]] = OpExtInst %void %1 DebugLexicalBlock +; CHECK: DebugScope [[block]] + %34 = OpExtInst %void %1 DebugScope %22 + OpLine %16 3 5 + OpStore %31 %7 + OpStore %out_var_SV_TARGET0 %7 + %35 = OpExtInst %void %1 DebugNoScope + OpReturn + OpFunctionEnd +)"; + + SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS); + SinglePassRunAndMatch(text, true); +} + +// TODO(greg-lunarg): Add tests to verify handling of these cases: +// +// Check that logical addressing required +// Check that function calls inhibit optimization +// Others? + +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/amd_ext_to_khr.cpp b/third_party/spirv-tools/test/opt/amd_ext_to_khr.cpp new file mode 100644 index 0000000..3340e89 --- /dev/null +++ b/third_party/spirv-tools/test/opt/amd_ext_to_khr.cpp @@ -0,0 +1,953 @@ +// Copyright (c) 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "gmock/gmock.h" +#include "test/opt/pass_fixture.h" +#include "test/opt/pass_utils.h" + +namespace spvtools { +namespace opt { +namespace { + +using AmdExtToKhrTest = PassTest<::testing::Test>; + +using ::testing::HasSubstr; + +std::string GetTest(std::string op_code, std::string new_op_code) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot" +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[undef:%\w+]] = OpUndef %uint +; CHECK-NEXT: )" + new_op_code + + R"( %uint %uint_3 Reduce [[undef]] + OpCapability Shader + OpCapability Groups + OpExtension "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %uint + %8 = )" + op_code + + R"( %uint %uint_3 Reduce %7 + OpReturn + OpFunctionEnd + +)"; + return text; +} + +TEST_F(AmdExtToKhrTest, ReplaceGroupIAddNonUniformAMD) { + std::string text = + GetTest("OpGroupIAddNonUniformAMD", "OpGroupNonUniformIAdd"); + SinglePassRunAndMatch(text, true); +} +TEST_F(AmdExtToKhrTest, ReplaceGroupFAddNonUniformAMD) { + std::string text = + GetTest("OpGroupFAddNonUniformAMD", "OpGroupNonUniformFAdd"); + SinglePassRunAndMatch(text, true); +} +TEST_F(AmdExtToKhrTest, ReplaceGroupUMinNonUniformAMD) { + std::string text = + GetTest("OpGroupUMinNonUniformAMD", "OpGroupNonUniformUMin"); + SinglePassRunAndMatch(text, true); +} +TEST_F(AmdExtToKhrTest, ReplaceGroupSMinNonUniformAMD) { + std::string text = + GetTest("OpGroupSMinNonUniformAMD", "OpGroupNonUniformSMin"); + SinglePassRunAndMatch(text, true); +} +TEST_F(AmdExtToKhrTest, ReplaceGroupFMinNonUniformAMD) { + std::string text = + GetTest("OpGroupFMinNonUniformAMD", "OpGroupNonUniformFMin"); + SinglePassRunAndMatch(text, true); +} +TEST_F(AmdExtToKhrTest, ReplaceGroupUMaxNonUniformAMD) { + std::string text = + GetTest("OpGroupUMaxNonUniformAMD", "OpGroupNonUniformUMax"); + SinglePassRunAndMatch(text, true); +} +TEST_F(AmdExtToKhrTest, ReplaceGroupSMaxNonUniformAMD) { + std::string text = + GetTest("OpGroupSMaxNonUniformAMD", "OpGroupNonUniformSMax"); + SinglePassRunAndMatch(text, true); +} +TEST_F(AmdExtToKhrTest, ReplaceGroupFMaxNonUniformAMD) { + std::string text = + GetTest("OpGroupFMaxNonUniformAMD", "OpGroupNonUniformFMax"); + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceMbcntAMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_ballot" +; CHECK: OpDecorate [[var:%\w+]] BuiltIn SubgroupLtMask +; CHECK: [[var]] = OpVariable %_ptr_Input_v4uint Input +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[ld:%\w+]] = OpLoad %v4uint [[var]] +; CHECK-NEXT: [[shuffle:%\w+]] = OpVectorShuffle %v2uint [[ld]] [[ld]] 0 1 +; CHECK-NEXT: [[bitcast:%\w+]] = OpBitcast %ulong [[shuffle]] +; CHECK-NEXT: [[and:%\w+]] = OpBitwiseAnd %ulong [[bitcast]] %ulong_0 +; CHECK-NEXT: [[result:%\w+]] = OpBitCount %uint [[and]] + OpCapability Shader + OpCapability Int64 + OpExtension "SPV_AMD_shader_ballot" + %1 = OpExtInstImport "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "func" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %ulong = OpTypeInt 64 0 + %ulong_0 = OpConstant %ulong 0 + %2 = OpFunction %void None %4 + %8 = OpLabel + %9 = OpExtInst %uint %1 MbcntAMD %ulong_0 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceSwizzleInvocationsAMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_ballot" +; CHECK: OpDecorate [[var:%\w+]] BuiltIn SubgroupLocalInvocationId +; CHECK: [[subgroup:%\w+]] = OpConstant %uint 3 +; CHECK: [[offset:%\w+]] = OpConstantComposite %v4uint +; CHECK: [[var]] = OpVariable %_ptr_Input_uint Input +; CHECK: [[uint_max:%\w+]] = OpConstant %uint 4294967295 +; CHECK: [[ballot_value:%\w+]] = OpConstantComposite %v4uint [[uint_max]] [[uint_max]] [[uint_max]] [[uint_max]] +; CHECK: [[null:%\w+]] = OpConstantNull [[type:%\w+]] +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[data:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[id:%\w+]] = OpLoad %uint [[var]] +; CHECK-NEXT: [[quad_idx:%\w+]] = OpBitwiseAnd %uint [[id]] %uint_3 +; CHECK-NEXT: [[quad_ldr:%\w+]] = OpBitwiseXor %uint [[id]] [[quad_idx]] +; CHECK-NEXT: [[my_offset:%\w+]] = OpVectorExtractDynamic %uint [[offset]] [[quad_idx]] +; CHECK-NEXT: [[target_inv:%\w+]] = OpIAdd %uint [[quad_ldr]] [[my_offset]] +; CHECK-NEXT: [[is_active:%\w+]] = OpGroupNonUniformBallotBitExtract %bool [[subgroup]] [[ballot_value]] [[target_inv]] +; CHECK-NEXT: [[shuffle:%\w+]] = OpGroupNonUniformShuffle [[type]] [[subgroup]] [[data]] [[target_inv]] +; CHECK-NEXT: [[result:%\w+]] = OpSelect [[type]] [[is_active]] [[shuffle]] [[null]] + OpCapability Shader + OpExtension "SPV_AMD_shader_ballot" + %ext = OpExtInstImport "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_x = OpConstant %uint 1 + %uint_y = OpConstant %uint 2 + %uint_z = OpConstant %uint 3 + %uint_w = OpConstant %uint 0 + %v4uint = OpTypeVector %uint 4 + %offset = OpConstantComposite %v4uint %uint_x %uint_y %uint_z %uint_x + %1 = OpFunction %void None %3 + %6 = OpLabel + %data = OpUndef %uint + %9 = OpExtInst %uint %ext SwizzleInvocationsAMD %data %offset + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} +TEST_F(AmdExtToKhrTest, ReplaceSwizzleInvocationsMaskedAMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_ballot" +; CHECK: OpDecorate [[var:%\w+]] BuiltIn SubgroupLocalInvocationId +; CHECK: [[x:%\w+]] = OpConstant %uint 19 +; CHECK: [[y:%\w+]] = OpConstant %uint 12 +; CHECK: [[z:%\w+]] = OpConstant %uint 16 +; CHECK: [[var]] = OpVariable %_ptr_Input_uint Input +; CHECK: [[mask_extend:%\w+]] = OpConstant %uint 4294967264 +; CHECK: [[uint_max:%\w+]] = OpConstant %uint 4294967295 +; CHECK: [[subgroup:%\w+]] = OpConstant %uint 3 +; CHECK: [[ballot_value:%\w+]] = OpConstantComposite %v4uint [[uint_max]] [[uint_max]] [[uint_max]] [[uint_max]] +; CHECK: [[null:%\w+]] = OpConstantNull [[type:%\w+]] +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[data:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[id:%\w+]] = OpLoad %uint [[var]] +; CHECK-NEXT: [[and_mask:%\w+]] = OpBitwiseOr %uint [[x]] [[mask_extend]] +; CHECK-NEXT: [[and:%\w+]] = OpBitwiseAnd %uint [[id]] [[and_mask]] +; CHECK-NEXT: [[or:%\w+]] = OpBitwiseOr %uint [[and]] [[y]] +; CHECK-NEXT: [[target_inv:%\w+]] = OpBitwiseXor %uint [[or]] [[z]] +; CHECK-NEXT: [[is_active:%\w+]] = OpGroupNonUniformBallotBitExtract %bool [[subgroup]] [[ballot_value]] [[target_inv]] +; CHECK-NEXT: [[shuffle:%\w+]] = OpGroupNonUniformShuffle [[type]] [[subgroup]] [[data]] [[target_inv]] +; CHECK-NEXT: [[result:%\w+]] = OpSelect [[type]] [[is_active]] [[shuffle]] [[null]] + OpCapability Shader + OpExtension "SPV_AMD_shader_ballot" + %ext = OpExtInstImport "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_x = OpConstant %uint 19 + %uint_y = OpConstant %uint 12 + %uint_z = OpConstant %uint 16 + %v3uint = OpTypeVector %uint 3 + %mask = OpConstantComposite %v3uint %uint_x %uint_y %uint_z + %1 = OpFunction %void None %3 + %6 = OpLabel + %data = OpUndef %uint + %9 = OpExtInst %uint %ext SwizzleInvocationsMaskedAMD %data %mask + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceWriteInvocationAMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_ballot" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_ballot" +; CHECK: OpDecorate [[var:%\w+]] BuiltIn SubgroupLocalInvocationId +; CHECK: [[var]] = OpVariable %_ptr_Input_uint Input +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[input_val:%\w+]] = OpUndef %uint +; CHECK-NEXT: [[write_val:%\w+]] = OpUndef %uint +; CHECK-NEXT: [[ld:%\w+]] = OpLoad %uint [[var]] +; CHECK-NEXT: [[cmp:%\w+]] = OpIEqual %bool [[ld]] %uint_3 +; CHECK-NEXT: [[result:%\w+]] = OpSelect %uint [[cmp]] [[write_val]] [[input_val]] + OpCapability Shader + OpExtension "SPV_AMD_shader_ballot" + %ext = OpExtInstImport "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %uint + %8 = OpUndef %uint + %9 = OpExtInst %uint %ext WriteInvocationAMD %7 %8 %uint_3 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceFMin3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeFloat 32 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[x]] [[y]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[temp]] [[z]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %float + %8 = OpUndef %float + %9 = OpUndef %float + %10 = OpExtInst %float %ext FMin3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceSMin3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeInt 32 1 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[x]] [[y]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[temp]] [[z]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %int + %8 = OpUndef %int + %9 = OpUndef %int + %10 = OpExtInst %int %ext SMin3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceUMin3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeInt 32 0 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[x]] [[y]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[temp]] [[z]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %uint + %8 = OpUndef %uint + %9 = OpUndef %uint + %10 = OpExtInst %uint %ext UMin3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceFMax3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeFloat 32 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[x]] [[y]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[temp]] [[z]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %float + %8 = OpUndef %float + %9 = OpUndef %float + %10 = OpExtInst %float %ext FMax3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceSMax3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeInt 32 1 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[x]] [[y]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[temp]] [[z]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %int + %8 = OpUndef %int + %9 = OpUndef %int + %10 = OpExtInst %int %ext SMax3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceUMax3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeInt 32 0 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[x]] [[y]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[temp]] [[z]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %uint + %8 = OpUndef %uint + %9 = OpUndef %uint + %10 = OpExtInst %uint %ext UMax3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceVecUMax3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeVector +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[temp:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[x]] [[y]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[temp]] [[z]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %vec = OpTypeVector %uint 4 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %vec + %8 = OpUndef %vec + %9 = OpUndef %vec + %10 = OpExtInst %vec %ext UMax3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceFMid3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeFloat 32 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] FMin [[y]] [[z]] +; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] FMax [[y]] [[z]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] FClamp [[x]] [[min]] [[max]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %float + %8 = OpUndef %float + %9 = OpUndef %float + %10 = OpExtInst %float %ext FMid3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceSMid3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeInt 32 1 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] SMin [[y]] [[z]] +; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] SMax [[y]] [[z]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] SClamp [[x]] [[min]] [[max]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %int + %8 = OpUndef %int + %9 = OpUndef %int + %10 = OpExtInst %int %ext SMid3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceUMid3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeInt 32 0 +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[y]] [[z]] +; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[y]] [[z]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UClamp [[x]] [[min]] [[max]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %uint + %8 = OpUndef %uint + %9 = OpUndef %uint + %10 = OpExtInst %uint %ext UMid3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceVecUMid3AMD) { + const std::string text = R"( +; CHECK: OpCapability Shader +; CHECK-NOT: OpExtension "SPV_AMD_shader_trinary_minmax" +; CHECK-NOT: OpExtInstImport "SPV_AMD_shader_trinary_minmax" +; CHECK: [[ext:%\w+]] = OpExtInstImport "GLSL.std.450" +; CHECK: [[type:%\w+]] = OpTypeVector +; CHECK: OpFunction +; CHECK-NEXT: OpLabel +; CHECK-NEXT: [[x:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[y:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[z:%\w+]] = OpUndef [[type]] +; CHECK-NEXT: [[min:%\w+]] = OpExtInst [[type]] [[ext]] UMin [[y]] [[z]] +; CHECK-NEXT: [[max:%\w+]] = OpExtInst [[type]] [[ext]] UMax [[y]] [[z]] +; CHECK-NEXT: [[result:%\w+]] = OpExtInst [[type]] [[ext]] UClamp [[x]] [[min]] [[max]] + OpCapability Shader + OpExtension "SPV_AMD_shader_trinary_minmax" + %ext = OpExtInstImport "SPV_AMD_shader_trinary_minmax" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %1 "func" + OpExecutionMode %1 OriginUpperLeft + %void = OpTypeVoid + %3 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %vec = OpTypeVector %uint 3 + %int = OpTypeInt 32 1 + %float = OpTypeFloat 32 + %uint_3 = OpConstant %uint 3 + %1 = OpFunction %void None %3 + %6 = OpLabel + %7 = OpUndef %vec + %8 = OpUndef %vec + %9 = OpUndef %vec + %10 = OpExtInst %vec %ext UMid3AMD %7 %8 %9 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceCubeFaceCoordAMD) { + // Sorry for the Check test. The code sequence is so long, I do not think + // that a match test would be anymore legible. This tests the replacement of + // the CubeFaceCoordAMD instruction. + const std::string before = R"( + OpCapability Shader + OpExtension "SPV_KHR_storage_buffer_storage_class" + OpExtension "SPV_AMD_gcn_shader" + %1 = OpExtInstImport "SPV_AMD_gcn_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint GLCompute %2 "main" + OpExecutionMode %2 LocalSize 1 1 1 + %void = OpTypeVoid + %4 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %2 = OpFunction %void None %4 + %8 = OpLabel + %9 = OpUndef %v3float + %10 = OpExtInst %v2float %1 CubeFaceCoordAMD %9 + OpReturn + OpFunctionEnd +)"; + + const std::string after = R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%12 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "main" +OpExecutionMode %2 LocalSize 1 1 1 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v2float = OpTypeVector %float 2 +%v3float = OpTypeVector %float 3 +%bool = OpTypeBool +%float_0 = OpConstant %float 0 +%float_2 = OpConstant %float 2 +%float_0_5 = OpConstant %float 0.5 +%16 = OpConstantComposite %v2float %float_0_5 %float_0_5 +%2 = OpFunction %void None %4 +%8 = OpLabel +%9 = OpUndef %v3float +%17 = OpCompositeExtract %float %9 0 +%18 = OpCompositeExtract %float %9 1 +%19 = OpCompositeExtract %float %9 2 +%20 = OpFNegate %float %17 +%21 = OpFNegate %float %18 +%22 = OpFNegate %float %19 +%23 = OpExtInst %float %12 FAbs %17 +%24 = OpExtInst %float %12 FAbs %18 +%25 = OpExtInst %float %12 FAbs %19 +%26 = OpFOrdLessThan %bool %19 %float_0 +%27 = OpFOrdLessThan %bool %18 %float_0 +%28 = OpFOrdLessThan %bool %17 %float_0 +%29 = OpExtInst %float %12 FMax %23 %24 +%30 = OpExtInst %float %12 FMax %25 %29 +%31 = OpFMul %float %float_2 %30 +%32 = OpFOrdGreaterThanEqual %bool %25 %29 +%33 = OpLogicalNot %bool %32 +%34 = OpFOrdGreaterThanEqual %bool %24 %23 +%35 = OpLogicalAnd %bool %33 %34 +%36 = OpSelect %float %26 %20 %17 +%37 = OpSelect %float %28 %19 %22 +%38 = OpSelect %float %35 %17 %37 +%39 = OpSelect %float %32 %36 %38 +%40 = OpSelect %float %27 %22 %19 +%41 = OpSelect %float %35 %40 %21 +%42 = OpCompositeConstruct %v2float %39 %41 +%43 = OpCompositeConstruct %v2float %31 %31 +%44 = OpFDiv %v2float %42 %43 +%10 = OpFAdd %v2float %44 %16 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true); +} + +TEST_F(AmdExtToKhrTest, ReplaceCubeFaceIndexAMD) { + // Sorry for the Check test. The code sequence is so long, I do not think + // that a match test would be anymore legible. This tests the replacement of + // the CubeFaceIndexAMD instruction. + const std::string before = R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +OpExtension "SPV_AMD_gcn_shader" +%1 = OpExtInstImport "SPV_AMD_gcn_shader" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "main" +OpExecutionMode %2 LocalSize 1 1 1 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%2 = OpFunction %void None %4 +%7 = OpLabel +%8 = OpUndef %v3float +%9 = OpExtInst %float %1 CubeFaceIndexAMD %8 +OpReturn +OpFunctionEnd +)"; + + const std::string after = R"(OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%11 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %2 "main" +OpExecutionMode %2 LocalSize 1 1 1 +%void = OpTypeVoid +%4 = OpTypeFunction %void +%float = OpTypeFloat 32 +%v3float = OpTypeVector %float 3 +%bool = OpTypeBool +%float_0 = OpConstant %float 0 +%float_1 = OpConstant %float 1 +%float_2 = OpConstant %float 2 +%float_3 = OpConstant %float 3 +%float_4 = OpConstant %float 4 +%float_5 = OpConstant %float 5 +%2 = OpFunction %void None %4 +%7 = OpLabel +%8 = OpUndef %v3float +%18 = OpCompositeExtract %float %8 0 +%19 = OpCompositeExtract %float %8 1 +%20 = OpCompositeExtract %float %8 2 +%21 = OpExtInst %float %11 FAbs %18 +%22 = OpExtInst %float %11 FAbs %19 +%23 = OpExtInst %float %11 FAbs %20 +%24 = OpFOrdLessThan %bool %20 %float_0 +%25 = OpFOrdLessThan %bool %19 %float_0 +%26 = OpFOrdLessThan %bool %18 %float_0 +%27 = OpExtInst %float %11 FMax %21 %22 +%28 = OpFOrdGreaterThanEqual %bool %23 %27 +%29 = OpFOrdGreaterThanEqual %bool %22 %21 +%30 = OpSelect %float %24 %float_5 %float_4 +%31 = OpSelect %float %25 %float_3 %float_2 +%32 = OpSelect %float %26 %float_1 %float_0 +%33 = OpSelect %float %29 %31 %32 +%9 = OpSelect %float %28 %30 %33 +OpReturn +OpFunctionEnd +)"; + + SinglePassRunAndCheck(before, after, true); +} + +TEST_F(AmdExtToKhrTest, SetVersion) { + const std::string text = R"( + OpCapability Shader + OpCapability Int64 + OpExtension "SPV_AMD_shader_ballot" + %1 = OpExtInstImport "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "func" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %ulong = OpTypeInt 64 0 + %ulong_0 = OpConstant %ulong 0 + %2 = OpFunction %void None %4 + %8 = OpLabel + %9 = OpExtInst %uint %1 MbcntAMD %ulong_0 + OpReturn + OpFunctionEnd +)"; + + // Set the version to 1.1 and make sure it is upgraded to 1.3. + SetTargetEnv(SPV_ENV_UNIVERSAL_1_1); + SetDisassembleOptions(0); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* skip_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); + const std::string& output = std::get<0>(result); + EXPECT_THAT(output, HasSubstr("Version: 1.3")); +} + +TEST_F(AmdExtToKhrTest, SetVersion1) { + const std::string text = R"( + OpCapability Shader + OpCapability Int64 + OpExtension "SPV_AMD_shader_ballot" + %1 = OpExtInstImport "SPV_AMD_shader_ballot" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %2 "func" + OpExecutionMode %2 OriginUpperLeft + %void = OpTypeVoid + %4 = OpTypeFunction %void + %uint = OpTypeInt 32 0 + %ulong = OpTypeInt 64 0 + %ulong_0 = OpConstant %ulong 0 + %2 = OpFunction %void None %4 + %8 = OpLabel + %9 = OpExtInst %uint %1 MbcntAMD %ulong_0 + OpReturn + OpFunctionEnd +)"; + + // Set the version to 1.4 and make sure it is stays the same. + SetTargetEnv(SPV_ENV_UNIVERSAL_1_4); + SetDisassembleOptions(0); + auto result = SinglePassRunAndDisassemble( + text, /* skip_nop = */ true, /* skip_validation = */ false); + + EXPECT_EQ(Pass::Status::SuccessWithChange, std::get<1>(result)); + const std::string& output = std::get<0>(result); + EXPECT_THAT(output, HasSubstr("Version: 1.4")); +} + +TEST_F(AmdExtToKhrTest, TimeAMD) { + const std::string text = R"( + OpCapability Shader + OpCapability Int64 + OpExtension "SPV_AMD_gcn_shader" +; CHECK-NOT: OpExtension "SPV_AMD_gcn_shader" +; CHECK: OpExtension "SPV_KHR_shader_clock" + %1 = OpExtInstImport "GLSL.std.450" + %2 = OpExtInstImport "SPV_AMD_gcn_shader" +; CHECK-NOT: OpExtInstImport "SPV_AMD_gcn_shader" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" + OpExecutionMode %main OriginUpperLeft + OpSource GLSL 450 + OpSourceExtension "GL_AMD_gcn_shader" + OpSourceExtension "GL_ARB_gpu_shader_int64" + OpName %main "main" + OpName %time "time" + %void = OpTypeVoid + %6 = OpTypeFunction %void + %ulong = OpTypeInt 64 0 +%_ptr_Function_ulong = OpTypePointer Function %ulong + %main = OpFunction %void None %6 + %9 = OpLabel + %time = OpVariable %_ptr_Function_ulong Function +; CHECK: [[uint:%\w+]] = OpTypeInt 32 0 +; CHECK: [[uint_3:%\w+]] = OpConstant [[uint]] 3 + %10 = OpExtInst %ulong %2 TimeAMD +; CHECK: %10 = OpReadClockKHR %ulong [[uint_3]] + OpStore %time %10 + OpReturn + OpFunctionEnd +)"; + + SinglePassRunAndMatch(text, true); +} +} // namespace +} // namespace opt +} // namespace spvtools diff --git a/third_party/spirv-tools/test/opt/assembly_builder.h b/third_party/spirv-tools/test/opt/assembly_builder.h new file mode 100644 index 0000000..1673c09 --- /dev/null +++ b/third_party/spirv-tools/test/opt/assembly_builder.h @@ -0,0 +1,266 @@ +// Copyright (c) 2016 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEST_OPT_ASSEMBLY_BUILDER_H_ +#define TEST_OPT_ASSEMBLY_BUILDER_H_ + +#include +#include +#include +#include +#include +#include + +namespace spvtools { +namespace opt { + +// A simple SPIR-V assembly code builder for test uses. It builds an SPIR-V +// assembly module from vectors of assembly strings. It allows users to add +// instructions to the main function and the type-constants-globals section +// directly. It relies on OpName instructions and friendly-name disassembling +// to keep the ID names unchanged after assembling. +// +// An assembly module is divided into several sections, matching with the +// SPIR-V Logical Layout: +// Global Preamble: +// OpCapability instructions; +// OpExtension instructions and OpExtInstImport instructions; +// OpMemoryModel instruction; +// OpEntryPoint and OpExecutionMode instruction; +// OpString, OpSourceExtension, OpSource and OpSourceContinued instructions. +// Names: +// OpName instructions. +// Annotations: +// OpDecorate, OpMemberDecorate, OpGroupDecorate, OpGroupMemberDecorate and +// OpDecorationGroup. +// Types, Constants and Global variables: +// Types, constants and global variables declaration instructions. +// Main Function: +// Main function instructions. +// Main Function Postamble: +// The return and function end instructions. +// +// The assembly code is built by concatenating all the strings in the above +// sections. +// +// Users define the contents in section +// and